[
  {
    "path": ".forgejo/workflows/bin-linux64-release.yaml",
    "content": "name: bin-linux64-release\n\non:\n  push:\n    branches: [ master ]\n\njobs:\n  build:\n    runs-on: docker\n\n    steps:\n    - name: clone src\n      uses: actions/checkout@v3\n      with:\n        fetch-depth: 1\n\n    - name: apt-get dependencies\n      run: |\n        apt-get -qq update\n        apt-get -qq install --no-install-recommends \\\n        zip \\\n        gettext \\\n        libasound2-dev \\\n        libgnutls28-dev \\\n        libopenxr-dev \\\n        libqscintilla2-qt5-dev \\\n        libsdl2-dev \\\n        libvulkan-dev \\\n        libwayland-dev \\\n        libxcursor-dev \\\n        libxkbcommon-dev \\\n        libxrandr-dev \\\n        qtbase5-dev\n\n    - name: makelibs\n      run: cd engine && make makelibs FTE_TARGET=linux64\n\n    - name: build fteqw\n      run: cd engine && make m-rel FTE_TARGET=linux64 BRANDFLAGS=\"-DLINK_EZHUD=1\"\n\n    - name: build fteqcc\n      run: cd engine && make qcc-rel FTE_TARGET=linux64\n\n    - name: build dedicated-server\n      run: cd engine && make sv-rel FTE_TARGET=linux64\n\n    - name: build plugins\n      run: cd engine && make plugins-rel FTE_TARGET=linux64 NATIVE_PLUGINS=\"ode cod ezhud hl2 irc models xmpp openxr openssl quake3 qi\"\n\n    - name: build imgtool\n      run: cd engine && make imgtool FTE_TARGET=linux64\n\n    - name: build iqmtool\n      run: cd engine && make iqmtool FTE_TARGET=linux64\n\n    - name: upload release\n      uses: actions/forgejo-release@4d26949b75e208a9d85204fdd1d6685af0f876a8\n      with:\n        direction: upload\n        release-dir: ./engine/release\n        token: ${{ secrets.TOKEN }}\n        override: true\n"
  },
  {
    "path": ".github/workflows/main.yml",
    "content": "name: main\n\non: [ push, pull_request, workflow_dispatch ]\n\njobs:\n  cmake:\n    strategy:\n      matrix:\n        os: [ ubuntu-latest ]\n        profile: [ \"Debug\", \"Release\" ]\n\n    runs-on: ${{ matrix.os }}\n\n    steps:\n      - name: Get number of CPU cores\n        uses: SimenB/github-actions-cpu-cores@v2\n        id: cpu-cores\n\n      - uses: actions/checkout@v4\n\n      - name: \"Install Dependencies\"\n        run: |\n          sudo apt-get -qq update\n          sudo apt-get -qq install --no-install-recommends              \\\n            gettext                                                     \\\n            libasound2-dev                                              \\\n            libbullet-dev                                               \\\n            libgnutls28-dev                                             \\\n            libopenxr-dev                                               \\\n            libqscintilla2-qt5-dev                                      \\\n            libsdl2-dev                                                 \\\n            libpng-dev                                                  \\\n            libvorbis-dev                                               \\\n            libvulkan-dev                                               \\\n            libwayland-dev                                              \\\n            libxcursor-dev                                              \\\n            libxkbcommon-dev                                            \\\n            libxrandr-dev                                               \\\n            qtbase5-dev\n\n            # Disabled due to warnings that break the debug build with -Werror\n            # libode-dev\n            # libavcodec-dev libavformat-dev libavutil-dev libswscale-dev\n\n      - name: \"Configure: ${{ matrix.profile }}\"\n        run: |\n          cmake -B build_${{ matrix.profile }} -DCMAKE_BUILD_TYPE=${{ matrix.profile }}\n\n      - name: \"Build: ${{ matrix.profile }}\"\n        run: |\n          cmake --build build_${{ matrix.profile }} --parallel ${{ steps.cpu-cores.outputs.count }}\n\n  make:\n    strategy:\n      matrix:\n        include:\n          - name: web\n            fte_target: web\n            make_targets: \"gl-rel\"\n            os: ubuntu-latest\n          - name: linux64\n            fte_target: linux64\n            make_targets: \"m-rel sv-rel qtv-rel qcc-rel\"\n            os: ubuntu-latest\n            packages: \"libpng-dev libasound2-dev libgl-dev libegl1-mesa-dev libwayland-dev libxcursor-dev libxi-dev libxkbcommon-dev libxrandr-dev libxss-dev\"\n          - name: win32\n            fte_target: win32\n            make_targets: \"m-rel sv-rel qcc-rel\"\n            os: ubuntu-latest\n            packages: \"binutils-mingw-w64-i686 gcc-mingw-w64-i686 g++-mingw-w64-i686\"\n          - name: win64\n            fte_target: win64\n            make_targets: \"m-rel sv-rel qcc-rel\"\n            os: ubuntu-latest\n            packages: \"binutils-mingw-w64-x86-64 gcc-mingw-w64-x86-64 g++-mingw-w64-x86-64\"\n          - name: macos-arm64\n            fte_target: SDL2\n            make_targets: \"gl-rel sv-rel qcc-rel\"\n            args: \"ARCH=arm STRIPFLAGS=\"\n            packages: \"sdl2\"\n            os: macos-latest\n\n    name: make-${{ matrix.name }}\n    runs-on: ${{ matrix.os }}\n    timeout-minutes: 30\n\n    steps:\n      - name: Get number of CPU cores\n        uses: SimenB/github-actions-cpu-cores@v2\n        id: cpu-cores\n\n      - uses: actions/checkout@v4\n\n      - name: Extract versions\n        run: grep 'VER=' engine/Makefile | tee makelibs_versions\n\n      - uses: actions/cache@v4\n        id: cache\n        with:\n          path: engine/libs-*\n          key: ${{ runner.os }}-${{ matrix.fte_target }}-${{ hashFiles('makelibs_versions') }}\n\n      - uses: mymindstorm/setup-emsdk@v14\n        with:\n          version: \"2.0.12\"\n          actions-cache-folder: \"emsdk-cache-2.0.12\"\n          cache-key: \"emsdk-2.0.12\"\n        if: matrix.fte_target == 'web'\n\n      - name: Verify emscripten\n        run: emcc -v\n        if: matrix.fte_target == 'web'\n\n      - name: Install dependencies (linux)\n        run: sudo apt-get -qq update && sudo apt-get -qq install --no-install-recommends ${{ matrix.packages }}\n        if: matrix.packages != '' && matrix.os == 'ubuntu-latest'\n\n      - name: Install dependencies (macos)\n        run: brew install ${{ matrix.packages }}\n        if: matrix.packages != '' && matrix.os == 'macos-latest'\n\n      - name: Build dependencies\n        working-directory: engine\n        run: make FTE_TARGET=${{ matrix.fte_target }} makelibs ${{ matrix.args }}\n        if: steps.cache.outputs.cache-hit != 'true'\n\n      - uses: ammaraskar/gcc-problem-matcher@0.3.0\n\n      - name: Build ${{ matrix.name }}\n        working-directory: engine\n        run: |\n          make -j ${{ steps.cpu-cores.outputs.count }} FTE_TARGET=${{ matrix.fte_target }} ${{ matrix.make_targets }} ${{ matrix.args }} LINK_EZHUD=1 LINK_OPENSSL=1\n\n      - name: Attach macOS docs\n        run: |\n          cat <<EOF > engine/release/fte_macos.txt\n          To allow executables to run issue for example:\n          chmod +x fteqw-glsdl2\n          xattr -d com.apple.quarantine fteqw-glsdl2\n          \n          If you don't have SDL2 installed, run:\n          brew install sdl2\n          EOF\n        if: matrix.os == 'macos-latest'\n\n      - name: Get version\n        id: version\n        run: echo \"short_sha=$(git rev-parse --short HEAD)\" >> $GITHUB_OUTPUT\n\n      - name: Upload artifacts\n        uses: actions/upload-artifact@v4\n        with:\n          name: fteqw-${{ matrix.name }}-${{ steps.version.outputs.short_sha }}\n          path: |\n            engine/release/fte*\n            !engine/release/*.db\n          compression-level: 9\n\n  flatpak:\n    name: make-flatpak-${{ matrix.arch.name }}\n    timeout-minutes: 30\n    strategy:\n      matrix:\n        arch:\n          - name: x86_64\n            runner: ubuntu-latest\n          - name: aarch64\n            runner: ubuntu-24.04-arm\n      fail-fast: false\n    runs-on: ${{ matrix.arch.runner }}\n    container:\n      image: ghcr.io/flathub-infra/flatpak-github-actions:kde-5.15-23.08\n      options: --privileged\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v4\n\n      - name: Lint metadata and build manifest\n        run: |\n          echo \"::group::Lint metainfo\"\n          flatpak-builder-lint \\\n            --user-exceptions=dist/linux/linter-exceptions.json --exceptions \\\n            appstream ./dist/linux/org.fteqw.fteqw.metainfo.xml \\\n            || export FLATPAK_LINT_FAILED=1\n          echo \"::endgroup::\"\n          echo \"::group::Lint Flatpak manifest\"\n          flatpak-builder-lint \\\n            --user-exceptions=dist/linux/linter-exceptions.json --exceptions \\\n            manifest ./dist/linux/org.fteqw.fteqw.yml \\\n            || export FLATPAK_LINT_FAILED=1\n          echo \"::endgroup::\"\n          if [[ ! -z \"$FLATPAK_LINT_FAILED\" ]]; then\n            echo \"::error::Metadata linting failed\"\n            exit 1\n          fi\n          git config --global --add safe.directory $(pwd)\n          echo \"SHORT_SHA=`git rev-parse --short HEAD`\" >> $GITHUB_ENV\n\n      - name: Build flatpak\n        uses: flatpak/flatpak-github-actions/flatpak-builder@v6\n        with:\n          bundle: org.fteqw.fteqw.${{ env.SHORT_SHA }}.flatpak\n          manifest-path: dist/linux/org.fteqw.fteqw.yml\n          cache-key: flatpak-builder-${{ matrix.arch }}-${{ env.SHORT_SHA }}\n          arch: ${{ matrix.arch.name }}\n\n      - name: Lint build directory\n        run: |\n          flatpak-builder-lint \\\n            --user-exceptions=dist/linux/linter-exceptions.json --exceptions \\\n            builddir flatpak_app\n"
  },
  {
    "path": ".gitignore",
    "content": "#misc build artifacts\nbuild\n*.o\n*.o.d\nengine/debug/\nengine/release/\nengine/libs-*/\nengine/bullet3-*/\nengine/*.tar.gz\nengine/shaders/generatebuiltinsl\nengine/shaders/makevulkanblob\nengine/shaders/vulkanblobs/\nplugins/cef/cef_linux64/\nplugins/cef/cef_windows64/\n\n#quakec build artifacts...\nquakec/csaddon/csaddon.dat\nquakec/csaddon/csaddon.lno\nquakec/csqctest/csprogs.dat\nquakec/csqctest/csprogs.lno\nquakec/csqctest/progs.dat\nquakec/csqctest/progs.lno\nquakec/menu.dat\nquakec/menu.lno\n\n# clangd artifacts\ncompile_commands.json\n.cache/\n\n#for qtcreator users.\nCMakeLists.txt.user\n\n# android target has a load of debris\nengine/droid/bin/\nengine/droid/gen/\nengine/droid/libs/\nengine/droid/default.properties\nengine/droid/local.properties\nengine/droid/proguard.cfg\nengine/droid/build.xml\nengine/droid/ftekeystore\nengine/droid/proguard-project.txt\nengine/droid/project.properties\n\n# Flatpak build artifacts\n.flatpak-builder\n"
  },
  {
    "path": "CMakeLists.txt",
    "content": "#Note: this file was made primarily to support msvc and its project file incompatibilities nightmare.\r\n#Its also useful for various other IDEs like QtCreator etc.\r\n#It uses system libraries, so it will have dependancy issues with public releases where those dependancies are distro/version-specific.\r\n#Public builds are still built using the (overcomplicated) traditional (g)makefile.\r\n\r\nCMAKE_MINIMUM_REQUIRED(VERSION 3.0...3.10)\r\ncmake_policy(SET CMP0063 NEW)\r\n\r\nPROJECT(FTEQuake)\r\n\r\nINCLUDE_DIRECTORIES(\r\n\tengine/common\r\n\tengine/client\r\n\tengine/qclib\r\n\tengine/gl\r\n\tengine/server\r\n\tengine\r\n)\r\n\r\nIF (EXISTS ${CMAKE_SOURCE_DIR}/.svn AND NOT DEFINED FTE_REVISON)\r\n\tEXECUTE_PROCESS(COMMAND\r\n\t\t\"svnversion\"\r\n\t\tWORKING_DIRECTORY \"${CMAKE_SOURCE_DIR}\"\r\n\t\tOUTPUT_VARIABLE FTE_REVISON\r\n\t\tERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE\r\n\t)\r\n\r\n\tIF (DEFINED FTE_REVISON)\r\n\t\tMESSAGE(STATUS \"FTE SVN Revision ${FTE_REVISON}\")\r\n\t\tIF(FTE_REVISON MATCHES \"M\")\r\n\t\t\tMESSAGE(STATUS \"--- PRIVATE CHANGES DETECTED ---\")\r\n\t\t\tSET(FTE_REVISON SVNREVISION=${FTE_REVISON})\r\n\t\tELSE()\r\n\t\t\tMESSAGE(STATUS \"No local changes\")\r\n\t\t\tEXECUTE_PROCESS(COMMAND\r\n\t\t\t\tsvn info --show-item last-changed-date --no-newline\r\n\t\t\t\tWORKING_DIRECTORY \"${CMAKE_SOURCE_DIR}\"\r\n\t\t\t\tOUTPUT_VARIABLE FTE_DATE\r\n\t\t\t)\r\n\r\n\t\t\tSET(FTE_REVISON SVNREVISION=${FTE_REVISON} SVNDATE=${FTE_DATE})\r\n\t\tENDIF()\r\n\tENDIF()\r\nENDIF()\r\nIF (EXISTS ${CMAKE_SOURCE_DIR}/.git AND NOT DEFINED FTE_REVISON)\r\n\tEXECUTE_PROCESS(COMMAND\r\n\t\tgit describe --always --long --dirty\r\n\t\tWORKING_DIRECTORY \"${CMAKE_SOURCE_DIR}\"\r\n\t\tOUTPUT_VARIABLE FTE_REVISON_GIT\r\n\t\tERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE\r\n\t)\r\n\tEXECUTE_PROCESS(COMMAND\r\n\t\tgit log -1 --format=%cs\r\n\t\tWORKING_DIRECTORY \"${CMAKE_SOURCE_DIR}\"\r\n\t\tOUTPUT_VARIABLE FTE_DATE\r\n\t\tERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE\r\n\t)\r\n\tEXECUTE_PROCESS(COMMAND\r\n\t\tgit branch --show-current\r\n\t\tWORKING_DIRECTORY \"${CMAKE_SOURCE_DIR}\"\r\n\t\tOUTPUT_VARIABLE FTE_BRANCH\r\n\t\tERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE\r\n\t)\r\n\tEXECUTE_PROCESS(COMMAND\r\n\t\tgit rev-parse --is-shallow-repository\r\n\t\tWORKING_DIRECTORY \"${CMAKE_SOURCE_DIR}\"\r\n\t\tOUTPUT_VARIABLE FTE_GIT_IS_SHALLOW\r\n\t\tERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE\r\n\t)\r\n\tIF(FTE_GIT_IS_SHALLOW STREQUAL true)\r\n\t\tMESSAGE(STATUS \"shallow clone prevents calculation of revision number.\")\r\n\t\tSET(SVNREVISION \"git-${FTE_REVISON_GIT}\")\t#if its a shallow clone then we can't count commits properly so don't know what revision we actually are.\r\n\tELSE()\r\n\t\tEXECUTE_PROCESS(COMMAND\r\n\t\t\tgit rev-list HEAD --count\r\n\t\t\tWORKING_DIRECTORY \"${CMAKE_SOURCE_DIR}\"\r\n\t\t\tOUTPUT_VARIABLE SVNREVISION\r\n\t\t\tERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE\r\n\t\t)\r\n\t\tMATH(EXPR SVNREVISION \"${SVNREVISION} + 29\") #not all svn commits managed to appear im the git repo, so we have a small bias to keep things consistent.\r\n\t\tIF (FTE_BRANCH STREQUAL \"master\" OR FTE_BRANCH STREQUAL \"\")\r\n\t\t\tSET(SVNREVISION \"${SVNREVISION}-git-${FTE_REVISON_GIT}\")\r\n\t\tELSE()\r\n\t\t\tSET(SVNREVISION \"${FTE_BRANCH}-${SVNREVISION}-git-${FTE_REVISON_GIT}\") #weird branches get a different form of revision, to reduce confusion.\r\n\t\tENDIF()\r\n\tENDIF()\r\n\tMESSAGE(STATUS \"FTE GIT ${FTE_BRANCH} Revision ${SVNREVISION}, ${FTE_DATE}\")\r\n\tSET(FTE_REVISON SVNREVISION=${SVNREVISION} SVNDATE=${FTE_DATE} FTE_BRANCH=${FTE_BRANCH})\r\nENDIF()\r\n\r\n#plugins need visibility hidden in order to avoid conflicts with function names that match the engine.\r\n#this is consistent with how windows works so no great loss.\r\n#plus it means that gcc can inline more (with LTO), including optimising args.\r\nset(CMAKE_CXX_VISIBILITY_PRESET hidden)\r\nset(CMAKE_C_VISIBILITY_PRESET hidden)\r\n\r\nIF(${CMAKE_VERSION} VERSION_LESS \"3.9.0\")\r\n\tMESSAGE(STATUS \"no LTO - old cmake.\")\r\nELSE()\r\n\tcmake_policy(SET CMP0069 NEW)\r\n\tIF(NOT CMAKE_BUILD_TYPE MATCHES \"Debug\")\r\n\t\t#use LTO where possible. reportedly requires cmake 3.9 to actually work\r\n\t\tINCLUDE(CheckIPOSupported)\r\n\t\tcheck_ipo_supported(RESULT result)\r\n\t\tIF(result)\r\n\t\t\tSET(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE)\r\n\t\t\tMESSAGE(STATUS \"Using LTO.\")\r\n\t\tELSE()\r\n\t\t\tMESSAGE(STATUS \"no LTO - not supported.\")\r\n\t\tENDIF()\r\n\tELSE()\r\n\t\tMESSAGE(STATUS \"no LTO - debug.\")\r\n\tENDIF()\r\nENDIF()\r\n\r\n# Added these to solve some build issues I ran into - Brad\r\nIF(FTE_BIG_ENDIAN)\r\n\tADD_DEFINITIONS(-DFTE_BIG_ENDIAN)\r\nENDIF()\r\n\r\nIF(FTE_LITTLE_ENDIAN)\r\n\tADD_DEFINITIONS(-DFTE_LITTLE_ENDIAN)\r\nENDIF()\r\n\r\n# libepoll-shim needs to be installed on the BSDs and Mac OSX to get \r\n# some of the server code to compile and work correctly on those platforms - Brad\r\nIF(CMAKE_SYSTEM_NAME MATCHES \"BSD\" OR CMAKE_SYSTEM_NAME MATCHES \"Darwin\")\r\n\tINCLUDE(FetchContent)\r\n\tFetchContent_Declare(\r\n\t\tepoll-shim\r\n\t\tGIT_REPOSITORY https://github.com/jiixyj/epoll-shim.git\r\n\t\tGIT_TAG master\r\n\t)\r\n\tSET(epoll-shim BUILD_TESTS OFF CACHE INTERNAL \"\")\r\n\tSET(epoll-shim BUILD_SHARED_LIBS OFF CACHE INTERNAL \"\")\r\n\tFetchContent_MakeAvailable(epoll-shim)\r\n\r\n\tSET(EPOLL_INC_DIR \"${epoll-shim_SOURCE_DIR}/include\")\r\nENDIF()\r\n\r\nSET(FTE_BUILD_CONFIG ${PROJECT_SOURCE_DIR}/engine/common/config_fteqw.h CACHE FILEPATH \"Which build config file to use to control supported features.\")\r\nSET(FTE_LIB_DEFINES ${FTE_LIB_DEFINES};CONFIG_FILE_NAME=${FTE_BUILD_CONFIG})\r\nSET(FTE_USE_SDL false CACHE BOOL \"Force the use of SDL2 instead of using native builds.\")\r\n\r\nINCLUDE(GNUInstallDirs)\r\nSET(FTE_INSTALL_BINDIR games CACHE STRING \"Binary dir to install to.\")\r\nSET(FTE_INSTALL_LIBDIR fteqw CACHE STRING \"Binary dir to install to.\")\r\n\r\nIF(NOT WIN32)\r\n\tSET(SYS_LIBS ${SYS_LIBS} m)\r\nELSE()\r\n\tSET(SYS_LIBS ${SYS_LIBS})\r\nENDIF()\r\n\r\nSET(FTE_DEP_ZLIB true CACHE BOOL \"Link against zlib.\")\r\nIF(FTE_DEP_ZLIB)\r\n\tFIND_PACKAGE(ZLIB)\r\nENDIF()\r\nIF(ZLIB_FOUND)\r\n\tSET(FTE_LIB_DEFINES ${FTE_LIB_DEFINES};ZLIB_STATIC)\r\n\tSET(FTE_LIBS ${FTE_LIBS} ${ZLIB_LIBRARIES})\r\n\tSET(FTESV_LIBS ${FTESV_LIBS} ${ZLIB_LIBRARIES})\r\n\tSET(FTEQCC_LIBS ${FTEQCC_LIBS} ${ZLIB_LIBRARIES})\r\n\tSET(FTEQTV_LIBS ${FTEQTV_LIBS} ${ZLIB_LIBRARIES})\r\nELSE()\r\n\tMESSAGE(WARNING \"libz library NOT available. compressed pk3, ICE, Q2E, etc etc, yada yada, blah blah will not be available.\")\r\n\tSET(FTE_LIB_DEFINES ${FTE_LIB_DEFINES};NO_ZLIB)\r\nENDIF()\r\n\r\nSET(FTE_DEP_BZIP2 true CACHE BOOL \"Link against libbzip2.\")\r\nIF(FTE_DEP_BZIP2)\r\n\tFIND_PACKAGE(BZip2)\r\nENDIF()\r\nIF(BZIP2_FOUND)\r\n\tSET(FTE_LIB_DEFINES ${FTE_LIB_DEFINES};AVAIL_BZLIB;BZLIB_STATIC)\r\n\tSET(FTE_LIBS ${FTE_LIBS} bz2)\r\n\tSET(FTESV_LIBS ${FTESV_LIBS} bz2)\r\n#\tMESSAGE(STATUS \"bzip2 library found. bz2-compressed pk3s will work for the price of extra bloat! yay!\")\r\nELSE()\r\n\tMESSAGE(WARNING \"bzip2 library NOT available. bz2-compressed pk3s will not be available, as if anyone cares.\")\r\nENDIF()\r\n\r\nSET(OpenGL_GL_PREFERENCE LEGACY)\r\nFIND_PACKAGE(OpenGL)\r\nIF(OpenGL_FOUND)\r\n\tSET(FTE_LIB_DEFINES ${FTE_LIB_DEFINES};GLQUAKE)\r\nELSE()\r\n\tMESSAGE(WARNING \"opengl library NOT available. Will depend upon vulkan.\")\r\n\tSET(FTE_LIB_DEFINES ${FTE_LIB_DEFINES};NO_OPENGL)\r\nENDIF()\r\n\r\nSET(FTE_DEP_JPEG true CACHE BOOL \"Link against libjpeg.\")\r\nIF(FTE_DEP_JPEG)\r\n\tFIND_PACKAGE(JPEG)\r\nENDIF()\r\nIF(JPEG_FOUND)\r\n\tINCLUDE_DIRECTORIES( ${JPEG_INCLUDE_DIRS} )\r\n\tSET(FTE_LIB_DEFINES ${FTE_LIB_DEFINES};LIBJPEG_STATIC)\r\n\tSET(FTE_LIBS ${FTE_LIBS} ${JPEG_LIBRARIES})\r\nELSE()\r\n\tMESSAGE(WARNING \"libjpeg library NOT available. Who cares?\")\r\n\tSET(FTE_LIB_DEFINES ${FTE_LIB_DEFINES};NO_JPEG)\r\n\tSET(JPEG_LIBRARIES)\r\nENDIF()\r\n\r\nIF(NOT ${WIN32})\r\n\tSET(FTE_DEP_DBUS true CACHE BOOL \"Link against libdbus.\")\r\n\tIF(FTE_DEP_DBUS)\r\n\t\tFIND_PACKAGE(DBus1)\r\n\tENDIF()\r\n\tIF(DBUS1_FOUND)\r\n\t\tINCLUDE_DIRECTORIES( ${DBus1_INCLUDE_DIRS} )\r\n\t\tSET(FTE_LIB_DEFINES ${FTE_LIB_DEFINES};HAVE_DBUS)\r\n\t\tSET(FTE_LIBS ${FTE_LIBS} ${DBus1_LIBRARIES})\r\n\tELSE()\r\n\t\tMESSAGE(WARNING \"libdbus-1 library NOT available. Who cares?\")\r\n\tENDIF()\r\nENDIF()\r\n\r\nSET(FTE_DEP_PNG true CACHE BOOL \"Link against libpng.\")\r\nIF(FTE_DEP_PNG)\r\n\tFIND_PACKAGE(PNG)\r\nENDIF()\r\nIF(PNG_FOUND)\r\n\tINCLUDE_DIRECTORIES( ${PNG_INCLUDE_DIRS} )\r\n\tSET(FTE_LIB_DEFINES ${FTE_LIB_DEFINES};LIBPNG_STATIC)\r\n\tSET(FTE_LIBS ${FTE_LIBS} ${PNG_LIBRARIES})\r\nELSE()\r\n\tMESSAGE(WARNING \"libpng library NOT available. Good luck with screenshots.\")\r\n\tSET(FTE_LIB_DEFINES ${FTE_LIB_DEFINES};NO_PNG)\r\n\tSET(PNG_LIBRARIES)\r\nENDIF()\r\n\r\nSET(FTE_DEP_FREETYPE true CACHE BOOL \"Link against libfreetype.\")\r\nIF(FTE_DEP_FREETYPE)\r\n\tFIND_PACKAGE(Freetype)\r\nENDIF()\r\nIF(FREETYPE_FOUND)\r\n\tINCLUDE_DIRECTORIES( ${FREETYPE_INCLUDE_DIRS} )\r\n\tSET(FTE_LIB_DEFINES ${FTE_LIB_DEFINES};LIBFREETYPE_STATIC)\r\n\tSET(FTE_LIBS ${FTE_LIBS} ${FREETYPE_LIBRARIES})\r\n\r\n\tFIND_PACKAGE(Fontconfig)\r\n\tIF(Fontconfig_FOUND)\r\n\t\tINCLUDE_DIRECTORIES( ${Fontconfig_INCLUDE_DIRS} )\r\n\t\tSET(FTE_LIB_DEFINES ${FTE_LIB_DEFINES};LIBFONTCONFIG_STATIC)\r\n\t\tSET(FTE_LIBS ${FTE_LIBS} ${Fontconfig_LIBRARIES})\r\n\tELSE()\r\n\t\tMESSAGE(WARNING \"fontconfig library NOT available. I hope you're not using any system fonts.\")\r\n\tENDIF()\r\nELSE()\r\n\tMESSAGE(WARNING \"freetype library NOT available. I hope you're okay with ascii.\")\r\n\tSET(FTE_LIB_DEFINES ${FTE_LIB_DEFINES};NO_FREETYPE)\r\nENDIF()\r\n\r\n#this is just for headers.\r\nFIND_PATH(VULKAN_INCLUDE_DIR vulkan/vulkan.h)\r\nIF(VULKAN_INCLUDE_DIR)\r\n\tINCLUDE_DIRECTORIES( ${VULKAN_INCLUDE_DIR} )\r\n\tSET(FTE_DEFINES ${FTE_DEFINES};VKQUAKE) #no libs required, thankfully\r\nELSE()\r\n\tMESSAGE(WARNING \"Vulkan headers NOT available.\")\r\nENDIF()\r\n\r\nSET(FTE_DEP_VORBISFILE true CACHE BOOL \"Link against libvorbisfile.\")\r\nIF(FTE_DEP_VORBISFILE)\r\n\tFIND_LIBRARY(VORBISFILE_LIBRARY NAMES vorbisfile)\r\nENDIF()\r\nIF(NOT VORBISFILE_LIBRARY)\r\n\tINCLUDE_DIRECTORIES( ${VORBISFILE_INCLUDE_DIRS} )\r\n\tMESSAGE(WARNING \"libvorbisfile library NOT available. Who listens to the bgm anyway?\")\r\n\tSET(FTE_LIB_DEFINES ${FTE_LIB_DEFINES};NO_OGG)\r\nENDIF()\r\n\r\nIF(CMAKE_BUILD_TYPE MATCHES \"Debug\")\r\n\tSET(FTE_WERROR true CACHE BOOL \"Warnings as errors.\")\r\nELSE()\r\n\tSET(FTE_WERROR false CACHE BOOL \"Warnings as errors.\")\r\nENDIF()\r\nIF(FTE_WERROR)\r\n\tSET(FTE_WERROR_ARG \"-Werror\")\r\nELSE()\r\n\tSET(FTE_WERROR_ARG \"\")\r\nENDIF()\r\n\r\nIF(CMAKE_C_COMPILER_ID MATCHES \"Clang\")\r\n\tset(CMAKE_C_FLAGS \"${CMAKE_C_FLAGS} -Wno-pointer-sign\")\r\n\r\n\tIF(CMAKE_BUILD_TYPE MATCHES \"Debug\")\r\n\t\tset(CMAKE_C_FLAGS \"${CMAKE_C_FLAGS} -Wall ${FTE_WERROR_ARG} -Wno-pointer-sign -Wno-unknown-pragmas -Wno-format-zero-length -Wno-strict-aliasing -Wno-error=cpp\")\r\n\tELSE()\r\n\t\tset(CMAKE_C_FLAGS \"${CMAKE_C_FLAGS} -O3 ${FTE_WERROR_ARG}\")\r\n\tENDIF()\r\nendif()\r\nIF(CMAKE_C_COMPILER_ID MATCHES \"GNU\")\r\n\tSET(CMAKE_C_FLAGS \"${CMAKE_C_FLAGS} -Wstrict-prototypes\")\t\t\t#\r\n\tSET(CMAKE_C_FLAGS \"${CMAKE_C_FLAGS} -Wold-style-definition\")\t\t#k&r c is weird and can't cope with 64bit types.\r\n\tSET(CMAKE_C_FLAGS \"${CMAKE_C_FLAGS} -Wmissing-parameter-type\")\t\t#k&r c is weird and can't cope with 64bit types.\r\n\tSET(CMAKE_C_FLAGS \"${CMAKE_C_FLAGS} -Wold-style-declaration\")\t\t#\r\n\tSET(CMAKE_C_FLAGS \"${CMAKE_C_FLAGS} -Wpointer-arith\")\t\t\t\t#void* stuff\r\n\tSET(CMAKE_C_FLAGS \"${CMAKE_C_FLAGS} -Wvla\")\t\t\t\t\t\t\t#msvc doesn't support vla\r\n\tSET(CMAKE_C_FLAGS \"${CMAKE_C_FLAGS} -Wdeclaration-after-statement\")\t#msvc doesn't allow defs after statements, and they're so very tempting...\r\n\tset(CMAKE_C_FLAGS \"${CMAKE_C_FLAGS} -Wformat-truncation=1\")\r\n\t#TODO SET(CMAKE_C_FLAGS \"${CMAKE_C_FLAGS} -Wmissing-prototypes\")\t\t\t#for finding missing statics.\r\n\t#SET(CMAKE_C_FLAGS \"${CMAKE_C_FLAGS} -Wno-unused-function\")\t\t\t#\r\n#\tset(CMAKE_C_FLAGS \"${CMAKE_C_FLAGS} -Wswitch-enum\")\t\t\t\t\t#to warn about omitted enums despite default.\r\n\r\n\t#might as well do this, public builds use the regular Makefile.\r\n\tIF(CMAKE_SYSTEM_PROCESSOR MATCHES \"ppc64le\" OR CMAKE_SYSTEM_PROCESSOR MATCHES \"ppc64\")\r\n\t\tSET(CMAKE_C_FLAGS \"${CMAKE_C_FLAGS} -mcpu=native\")\r\n\tELSE()\r\n\t\tSET(CMAKE_C_FLAGS \"${CMAKE_C_FLAGS} -march=native\")\r\n\tENDIF()\r\n\tIF(CMAKE_BUILD_TYPE MATCHES \"Debug\")\r\n\t\tset(CMAKE_C_FLAGS \"${CMAKE_C_FLAGS} -Wall ${FTE_WERROR_} -Wno-pointer-sign -Wno-unknown-pragmas -Wno-format-zero-length -Wno-strict-aliasing -Wno-error=cpp\")\r\n\tELSE()\r\n\t\tset(CMAKE_C_FLAGS \"${CMAKE_C_FLAGS} -O3 ${FTE_WERROR_}\")\r\n\tENDIF()\r\n\tIF (NOT FTE_USE_SDL)\r\n\t\tset(CMAKE_C_FLAGS \"${CMAKE_C_FLAGS} -Wl,--warn-common\")\r\n\tENDIF()\r\n\t#SET(CMAKE_C_FLAGS \"${CMAKE_C_FLAGS} -Wc++-compat\")\t#lul, thousands of errors!\r\nENDIF()\r\nIF(CMAKE_BUILD_TYPE MATCHES \"Debug\")\r\n\tIF(NOT ${WIN32})\r\n\t\tset(CMAKE_C_FLAGS \"${CMAKE_C_FLAGS} -fstack-protector-strong\")\r\n\t\tset(CMAKE_C_FLAGS \"${CMAKE_C_FLAGS} -std=gnu89\")\r\n\tENDIF()\r\n\tset(CMAKE_C_FLAGS \"${CMAKE_C_FLAGS} -D_DEBUG\")\r\nENDIF()\r\nset(CMAKE_C_FLAGS \"${CMAKE_C_FLAGS} -D_FILE_OFFSET_BITS=64\")\r\n\r\nset(CMAKE_C_FLAGS \"${CMAKE_C_FLAGS} -DFTE_LIBRARY_PATH=${CMAKE_INSTALL_FULL_LIBDIR}/${FTE_INSTALL_LIBDIR} -DFTE_DATA_DIR=${CMAKE_INSTALL_FULL_DATAROOTDIR}\")\r\n\r\nFUNCTION(EMBED_PLUGIN_META PLUGNAME PLUGTITLE PLUGDESC)\r\n\tSET_TARGET_PROPERTIES(plug_${PLUGNAME} PROPERTIES OUTPUT_NAME \"${PLUGNAME}\")\r\n\tSET_TARGET_PROPERTIES(plug_${PLUGNAME} PROPERTIES PREFIX \"fteplug_\")\r\n\tSET_TARGET_PROPERTIES(plug_${PLUGNAME} PROPERTIES LINK_FLAGS \"-Wl,--no-undefined\")\r\n\tSET(INSTALLTARGS ${INSTALLTARGS} \"plug_${PLUGNAME}\" PARENT_SCOPE)\r\n\t#sadly we need to use a temp zip file, because otherwise zip insists on using zip64 extensions which breaks zip -A (as well as any attempts to read any files).\r\n\tADD_CUSTOM_COMMAND(\r\n\t\tTARGET plug_${PLUGNAME} POST_BUILD\r\n\t\tCOMMAND /bin/echo -e \"{\\\\n\tpackage fteplug_${PLUGNAME}\\\\n\tver \\\"${SVNREVISION}\\\"\\\\n\tcategory Plugins\\\\n\ttitle \\\"${PLUGTITLE}\\\"\\\\n\tgamedir \\\"\\\"\\\\n\tdesc \\\"${PLUGDESC}\\\"\\\\n}\" | zip -q -9 -fz- $<TARGET_FILE:plug_${PLUGNAME}>.zip -\r\n\t\tCOMMAND cmake -E cat $<TARGET_FILE:plug_${PLUGNAME}>.zip >> \"$<TARGET_FILE:plug_${PLUGNAME}>\"\r\n\t\tCOMMAND zip -A \"$<TARGET_FILE:plug_${PLUGNAME}>\"\r\n\t\tCOMMAND cmake -E rm $<TARGET_FILE:plug_${PLUGNAME}>.zip\r\n\t\tVERBATIM)\r\nENDFUNCTION()\r\n\r\nSET(FTE_DEP_GNUTLS true CACHE BOOL \"Link against gnutls\")\r\nIF(FTE_DEP_GNUTLS)\r\n\tFIND_PACKAGE(GnuTLS)\r\n\tIF(NOT GNUTLS_FOUND)\r\n\t\tMESSAGE(WARNING \"gnutls library NOT available. HTTPS/DTLS will not be available.\")\r\n\t\tSET(FTE_LIB_DEFINES ${FTE_LIB_DEFINES};NO_GNUTLS)\r\n\tELSE()\r\n\t\tIF(WIN32)\r\n\t\t\tSET(GNUTLS_STATIC true CACHE BOOL \"Link gnutls statically.\") #usually as an .so though. :/\r\n\t\tELSE()\r\n\t\t\tSET(GNUTLS_STATIC false CACHE BOOL \"Link gnutls statically.\") #usually as an .so though. :/\r\n\t\tENDIF()\r\n\t\tIF(GNUTLS_STATIC)\r\n\t\t\tSET(FTE_LIB_DEFINES ${FTE_LIB_DEFINES};GNUTLS_STATIC)\r\n\t\t\tSET(FTE_LIBS ${FTE_LIBS} ${GNUTLS_LIBRARY})\r\n\t\t\tSET(FTESV_LIBS ${FTESV_LIBS} ${GNUTLS_LIBRARY})\r\n\t\tENDIF()\r\n\tENDIF()\r\nENDIF()\r\nIF(WIN32)\r\n\tSET(FTE_DEP_WINSSPI true CACHE BOOL \"Link against winsspi(schannel)\")\r\n\tIF(NOT FTE_DEP_WINSSPI)\r\n\t\tSET(FTE_DEFINES ${FTE_DEFINES};NO_WINSSPI)\r\n\tENDIF()\r\nENDIF()\r\n\r\nSET(FTE_DEP_SDL3 false CACHE BOOL \"Use SDL3 if available (disabling many platform-specific behaviours).\")\r\nIF(FTE_DEP_SDL3)\r\n\tFIND_PACKAGE(SDL3 QUIET)\r\nENDIF()\r\nIF(SDL3_FOUND)\t#SDL3\r\n\tMESSAGE(STATUS \"SDL3 library found, woo, modern, for now.\")\r\n\r\n#\tFIND_PACKAGE(PkgConfig REQUIRED)\r\n#\tPKG_SEARCH_MODULE(sdl3 REQUIRED sdl3)\r\n\r\n\tFIND_PACKAGE(SDL3 REQUIRED)\r\n\r\n\tINCLUDE_DIRECTORIES(${FREETYPE_INCLUDE_DIRS} ${SDL3_INCLUDE_DIRS})\r\n\r\n\tSET(FTE_DEFINES ${FTE_DEFINES};FTE_SDL3;MULTITHREAD)\r\n\tSET(FTE_LIBS ${FTE_LIBS} ${SYS_LIBS} ${CMAKE_DL_LIBS} ${SDL3_LIBRARIES})\r\n\tSET(FTE_ARCH_FILES\r\n\t\tengine/client/sys_sdl.c\r\n\t\tengine/client/snd_al.c\r\n\t\tengine/client/snd_sdl.c\r\n\t\tengine/client/in_sdl.c\r\n\t\tengine/client/cd_sdl.c\r\n\t\tengine/gl/gl_vidsdl.c\r\n\t)\r\n\r\n\tSET(FTESV_DEFINES ${FTESV_DEFINES};MULTITHREAD)\r\n\tSET(FTESV_LIBS ${FTESV_LIBS} ${SYS_LIBS} ${CMAKE_DL_LIBS} ${SDL3_LIBRARIES})\r\n\r\n\tIF(WIN32)\r\n\t\tSET(FTE_LIBS ${FTE_LIBS} wsock32 gdi32 ole32)\r\n\t\tSET(FTE_DEFINES ${FTE_DEFINES})\r\n\t\tSET(FTE_ARCH_FILES ${FTE_ARCH_FILES}\r\n\t\t\tengine/client/winquake.rc\r\n\t\t\tengine/common/net_ssl_winsspi.c\r\n\t\t\tengine/common/fs_win32.c\r\n\t\t)\r\n\t\tSET(FTESV_ARCH_FILES ${FTESV_ARCH_FILES}\r\n\t\t\tengine/client/winquake.rc\r\n\t\t\tengine/common/net_ssl_winsspi.c\r\n\t\t\tengine/common/fs_win32.c\r\n\t\t\tengine/server/sv_sys_win.c\r\n\t\t)\r\n\tELSE()\r\n\t\tSET(FTE_ARCH_FILES ${FTE_ARCH_FILES}\r\n\t\t\tengine/common/net_ssl_gnutls.c\r\n\t\t)\r\n\t\tSET(FTESV_ARCH_FILES ${FTESV_ARCH_FILES}\r\n\t\t\tengine/common/net_ssl_gnutls.c\r\n\t\t\tengine/common/sys_linux_threads.c\r\n\t\t\tengine/server/sv_sys_unix.c\r\n\t\t)\r\n\t\tSET(FTESV_LIBS ${FTESV_LIBS} pthread)\r\n\tENDIF()\r\nELSEIF(${ANDROID})\r\n#\tFIND_PACKAGE(Freetype REQUIRED)\r\n\r\n#\tINCLUDE_DIRECTORIES(\t${FREETYPE_INCLUDE_DIRS} )\r\n\r\n\tSET(FTE_DEFINES ${FTE_DEFINES};ANDROID;VKQUAKE;MULTITHREAD)\r\n\tSET(FTE_LIBS ${FTE_LIBS} android log EGL ${SYS_LIBS} ${CMAKE_DL_LIBS})\r\n\tSET(FTE_ARCH_FILES\r\n\t\tengine/client/sys_droid.c\r\n\t\tengine/common/sys_linux_threads.c\r\n\t\tengine/client/snd_droid.c\r\n\t\tengine/client/cd_null.c\r\n\t\tengine/gl/gl_viddroid.c\r\n\t)\r\nELSEIF(WIN32 AND NOT FTE_USE_SDL)\r\n\tINCLUDE_DIRECTORIES(engine/libs engine/libs/freetype2/include)\r\n#\tLINK_DIRECTORIES(engine/libs/mingw64-libs)\r\n\t\r\n\t#\tengine/server/sv_sys_win.c\r\n\t\r\n\tSET(FTE_LIBS ${FTE_LIBS} ole32 gdi32 wsock32 winmm dxguid)\r\n\tSET(FTE_DEFINES ${FTE_DEFINES};D3D9QUAKE;D3D11QUAKE)\r\n\tSET(FTE_ARCH_FILES \r\n\t\tengine/client/winquake.rc\r\n\t\tengine/common/sys_win_threads.c\r\n\t\tengine/common/net_ssl_winsspi.c\r\n\t\tengine/common/net_ssl_gnutls.c\r\n\t\tengine/common/fs_win32.c\r\n\t\tengine/client/cd_win.c\r\n\t\tengine/client/in_win.c\r\n\t\tengine/client/snd_al.c\r\n\t\tengine/client/snd_directx.c\r\n\t\tengine/client/snd_wasapi.c\r\n\t\tengine/client/snd_win.c\r\n\t\tengine/client/snd_xaudio.c\r\n\t\tengine/client/sys_win.c\r\n\t\t\r\n\t\tengine/gl/gl_vidnt.c\r\n\r\n\t\tengine/d3d/d3d_backend.c\r\n\t\tengine/d3d/d3d_image.c\r\n\t\tengine/d3d/d3d_shader.c\r\n\t\tengine/d3d/d3d11_backend.c\r\n\t\tengine/d3d/d3d11_image.c\r\n\t\tengine/d3d/d3d11_shader.c\r\n\t\tengine/d3d/d3d8_backend.c\r\n\t\tengine/d3d/d3d8_image.c\r\n\t\tengine/d3d/vid_d3d.c\r\n\t\tengine/d3d/vid_d3d11.c\r\n\t\tengine/d3d/vid_d3d8.c\r\n\t)\r\n\t\r\n\tSET(FTESV_LIBS ${FTESV_LIBS} wsock32 winmm)\r\n\tSET(FTESV_ARCH_FILES \r\n\t\tengine/client/winquake.rc\r\n\t\tengine/common/sys_win_threads.c\r\n\t\tengine/common/net_ssl_winsspi.c\r\n\t\tengine/common/net_ssl_gnutls.c\r\n\t\tengine/common/fs_win32.c\r\n\t\tengine/server/sv_sys_win.c\r\n\t)\r\nELSEIF(UNIX AND NOT FTE_USE_SDL)\t#linux(ish)\r\n\t\t\t\t#openbsd will have issues with snd_linux.c\r\n\r\n\t#linux-only packages\r\n\tFIND_PACKAGE(ALSA)\r\n\tIF(ALSA_FOUND)\r\n\t\tSET(FTE_LIB_DEFINES ${FTE_LIB_DEFINES};AUDIO_ALSA;AUDIO_PULSE)\r\n\tELSE()\r\n\t\tMESSAGE(WARNING \"asound (alsa) library NOT available.\")\r\n\tENDIF()\r\n\r\n\tFIND_PACKAGE(X11)\r\n\tIF(X11_FOUND)\r\n\t\tIF (NOT X11_Xcursor_FOUND)\r\n\t\t\tSET(FTE_LIB_DEFINES ${FTE_LIB_DEFINES};NO_X11_CURSOR)\r\n\t\t\tMESSAGE(WARNING \"Xcursor library NOT available.\")\r\n\t\tENDIF()\r\n\t\tIF (NOT X11_Xrandr_FOUND)\r\n\t\t\tSET(FTE_LIB_DEFINES ${FTE_LIB_DEFINES};NO_X11_RANDR)\r\n\t\t\tMESSAGE(WARNING \"Xrandr library NOT available.\")\r\n\t\tENDIF()\r\n\t\tIF (NOT X11_Xscreensaver_FOUND)\r\n\t\t\tSET(FTE_LIB_DEFINES ${FTE_LIB_DEFINES};NO_X11_XSS)\r\n\t\t\tMESSAGE(WARNING \"Xss library NOT available.\")\r\n\t\tENDIF()\r\n\tELSE()\r\n\t\tMESSAGE(WARNING \"x11 library NOT available.\")\r\n\t\tSET(FTE_LIB_DEFINES ${FTE_LIB_DEFINES};NO_X11)\r\n\tENDIF()\r\n\r\n\tSET(FTE_DEFINES ${FTE_DEFINES};DYNAMIC_SDL;MULTITHREAD)\r\n\tSET(FTE_LIBS ${FTE_LIBS} ${SYS_LIBS} ${CMAKE_DL_LIBS} pthread ${SDL2_LIBRARIES})\r\n\tSET(FTE_ARCH_FILES ${FTE_ARCH_FILES}\r\n\t\tengine/client/sys_linux.c\r\n\t\tengine/common/sys_linux_threads.c\r\n\t\tengine/common/net_ssl_gnutls.c\r\n\r\n\t\tengine/client/snd_al.c\r\n\t\tengine/client/snd_alsa.c\r\n\t\tengine/client/snd_linux.c\r\n\t\tengine/client/snd_pulse.c\r\n\t\tengine/client/snd_sdl.c\t#we use SDL audio even without sys_sdl, because of pulseaudio fucking over alsa, alsa fucking over oss3, and oss4 not being used. Either way, openal should be the default anyway.\r\n\r\n\t\tengine/client/cd_linux.c\r\n\t\tengine/gl/gl_vidlinuxglx.c\r\n\t\tengine/gl/gl_videgl.c\r\n\r\n#\t\tengine/gl/gl_vidrpi.c\r\n#\t\tengine/gl/gl_vidwayland.c\r\n\t)\r\n\r\n\t#openbsd uses a libossaudio library for all the oss stuff, use that to ensure that we still get sound\r\n\tFIND_LIBRARY(\r\n\t\tOSSAUDIO_LIBRARY\r\n\t\tNAMES ossaudio\r\n\t)\r\n\tIF(OSSAUDIO_LIBRARY)\r\n\t\tSET(FTE_LIBS ${FTE_LIBS} ${OSSAUDIO_LIBRARY})\r\n\tENDIF()\r\n\r\n\t#on linux, use wayland (we normally dynamically link, but we still need headers).\r\n\tFIND_LIBRARY(\r\n\t\tWAYLAND_CLIENT_LIBRARY\r\n\t\tNAMES wayland-client libwayland-client\r\n\t)\r\n\tFIND_LIBRARY(\r\n\t\tWAYLAND_EGL\r\n\t\tNAMES wayland-egl\r\n\t)\r\n\tFIND_LIBRARY(\r\n\t\tHAVE_XKBCOMMON\r\n\t\tNAMES xkbcommon\r\n\t)\r\n\tIF(NOT HAVE_XKBCOMMON)\r\n\t\tMESSAGE(WARNING \"xkbcommon library not found, needed for wayland to be usable.\")\r\n\t\tUNSET(WAYLAND_CLIENT_LIBRARY)\r\n\tENDIF()\r\n\tIF(WAYLAND_CLIENT_LIBRARY AND WAYLAND_EGL)\r\n#\t\tSET(FTE_LIBS ${FTE_LIBS} ${WAYLAND_CLIENT_LIBRARY} ${HAVE_XKBCOMMON} ${WAYLAND_EGL})\r\n#\t\tSET(FTE_DEFINES ${FTE_DEFINES};STATIC_WAYLAND)\r\n\r\n\t\tSET(FTE_DEFINES ${FTE_DEFINES};WAYLANDQUAKE;USE_EGL)\r\n\t\tSET(FTE_ARCH_FILES ${FTE_ARCH_FILES}\r\n\t\t\tengine/gl/gl_vidwayland.c\r\n\t\t)\r\n\tELSE()\r\n\t\tMESSAGE(WARNING \"Wayland library NOT available. X11 will live forever anyway.\")\r\n\t\tIF(NOT X11_FOUND)\r\n\t\t\tMESSAGE(WARNING \"No renderers supported!\")\r\n\t\t\tSET(FTE_NO_RENDERERS 1)\r\n\t\tENDIF()\r\n\tENDIF()\r\n\r\n\tIF(CMAKE_SYSTEM_NAME MATCHES \"BSD\" OR CMAKE_SYSTEM_NAME MATCHES \"Darwin\")\r\n\t\tFIND_LIBRARY(epoll-shim REQUIRED)\r\n\t\tINCLUDE_DIRECTORIES(${EPOLL_INC_DIR})\r\n\t\tSET(FTESV_LIBS ${FTESV_LIBS} epoll-shim)\r\n\t\tSET(FTESV_DEFINES ${FTESV_DEFINES};HAVE_EPOLL)\r\n\tENDIF()\r\n\r\n\tSET(FTESV_DEFINES ${FTESV_DEFINES};MULTITHREAD)\r\n\tSET(FTESV_ARCH_FILES ${FTESV_ARCH_FILES}\r\n\t\tengine/server/sv_sys_unix.c\r\n\t\tengine/common/sys_linux_threads.c\r\n\t\tengine/common/net_ssl_gnutls.c\r\n\t)\r\n\tSET(FTESV_LIBS ${FTESV_LIBS} ${SYS_LIBS} ${CMAKE_DL_LIBS} pthread)\r\n\r\nELSEIF(1)\t#SDL2\r\n#\tFIND_PACKAGE(Freetype REQUIRED)\r\n#\tINCLUDE_DIRECTORIES(engine/libs engine/libs/freetype2/include)\r\n\r\n\tFIND_PACKAGE(PkgConfig REQUIRED)\r\n\tPKG_SEARCH_MODULE(sdl2 REQUIRED sdl2)\r\n\r\n\tFIND_PACKAGE(SDL2 REQUIRED)\r\n\r\n\tINCLUDE_DIRECTORIES(${FREETYPE_INCLUDE_DIRS} ${SDL2_INCLUDE_DIRS})\r\n\t\r\n\t#SDL2.0.7 supports vulkan, so lets use it.\r\n\tSET(FTE_DEFINES ${FTE_DEFINES};FTE_SDL;MULTITHREAD)\r\n\tSET(FTE_LIBS ${FTE_LIBS} ${SYS_LIBS} ${CMAKE_DL_LIBS} ${SDL2_LIBRARIES})\r\n\tSET(FTE_ARCH_FILES\r\n\t\tengine/client/sys_sdl.c\r\n\t\tengine/client/snd_al.c\r\n\t\tengine/client/snd_sdl.c\r\n\t\tengine/client/in_sdl.c\r\n\t\tengine/client/cd_sdl.c\r\n\t\tengine/gl/gl_vidsdl.c\r\n\t)\r\n\r\n\tSET(FTESV_DEFINES ${FTESV_DEFINES};MULTITHREAD)\r\n\tSET(FTESV_LIBS ${FTESV_LIBS} ${SYS_LIBS} ${CMAKE_DL_LIBS} ${SDL2_LIBRARIES})\r\n\r\n\tIF(WIN32)\r\n\t\tSET(FTE_LIBS ${FTE_LIBS} wsock32 gdi32 ole32)\r\n\t\tSET(FTE_DEFINES ${FTE_DEFINES};NO_DIRECTX)\r\n\t\tSET(FTE_ARCH_FILES ${FTE_ARCH_FILES}\r\n\t\t\tengine/client/winquake.rc\r\n\t\t\tengine/common/net_ssl_winsspi.c\r\n\t\t\tengine/common/fs_win32.c\r\n\t\t)\r\n\t\tSET(FTESV_ARCH_FILES ${FTESV_ARCH_FILES}\r\n\t\t\tengine/client/winquake.rc\r\n\t\t\tengine/common/net_ssl_winsspi.c\r\n\t\t\tengine/common/fs_win32.c\r\n\t\t\tengine/server/sv_sys_win.c\r\n\t\t)\r\n\tELSE()\r\n\t\tSET(FTE_ARCH_FILES ${FTE_ARCH_FILES}\r\n\t\t\tengine/common/net_ssl_gnutls.c\r\n\t\t)\r\n\t\tSET(FTESV_ARCH_FILES ${FTESV_ARCH_FILES}\r\n\t\t\tengine/common/net_ssl_gnutls.c\r\n\t\t\tengine/common/sys_linux_threads.c\r\n\t\t\tengine/server/sv_sys_unix.c\r\n\t\t)\r\n\t\tSET(FTESV_LIBS ${FTESV_LIBS} pthread)\r\n\tENDIF()\r\nELSE()\r\n#\tengine/common/sys_linux_threads.c\r\n#\tengine/common/net_ssl_gnutls.c\r\n#\tengine/server/sv_sys_unix.c\r\n\r\n#\tengine/client/snd_alsa.c\r\n#\tengine/client/snd_droid.c\r\n#\tengine/client/snd_linux.c\r\n#\tengine/client/snd_macos.c\r\n#\tengine/client/snd_morphos.c\r\n#\tengine/client/snd_sblaster.c\r\n#\tengine/client/snd_sdl.c\r\n#\tengine/client/snd_sndio.c\r\n\r\n#\tengine/client/sys_dos.c\r\n#\tengine/client/sys_droid.c\r\n#\tengine/client/sys_linux.c\r\n#\tengine/client/sys_morphos.c\r\n#\tengine/client/sys_plugfte.c\r\n#\tengine/client/sys_sdl.c\r\n#\tengine/client/sys_xdk.c\r\n\r\n#\tengine/client/cd_linux.c\r\n#\tengine/client/cd_null.c\r\n#\tengine/client/cd_sdl.c\r\n#\tengine/client/in_morphos.c\r\n#\tengine/client/in_sdl.c\r\n\t\r\n#\tengine/gl/gl_viddroid.c\r\n#\tengine/gl/gl_videgl.c\r\n#\tengine/gl/gl_vidlinuxglx.c\r\n#\tengine/gl/gl_vidmacos.c\r\n#\tengine/gl/gl_vidmorphos.c\r\n#\tengine/gl/gl_vidnull.c\r\n#\tengine/gl/gl_vidrpi.c\r\n#\tengine/gl/gl_vidsdl.c\r\n#\tengine/gl/gl_vidtinyglstubs.c\r\n#\tengine/gl/gl_vidwayland.c\r\nENDIF()\r\n\r\nSET(FTE_GL_FILES\r\n#These are GL-specific, but can be left even if no gl is supported.\r\n\tengine/gl/gl_backend.c\r\n\tengine/gl/gl_bloom.c\r\n\tengine/gl/gl_draw.c\r\n\tengine/gl/gl_rmain.c\r\n\tengine/gl/gl_rmisc.c\r\n\tengine/gl/gl_rsurf.c\r\n\tengine/gl/gl_screen.c\r\n\tengine/gl/gl_vidcommon.c\r\n\tengine/gl/glmod_doom.c\r\n)\r\nSET(FTE_VK_FILES\r\n\tengine/vk/vk_backend.c\r\n\tengine/vk/vk_init.c\r\n)\r\nSET(FTE_QCVM_FILES\r\n\tengine/qclib/comprout.c\r\n\tengine/qclib/initlib.c\r\n\tengine/qclib/pr_edict.c\r\n\tengine/qclib/pr_exec.c\r\n\tengine/qclib/pr_multi.c\r\n\tengine/qclib/qcc_cmdlib.c\r\n\tengine/qclib/qcc_pr_comp.c\r\n\tengine/qclib/qcc_pr_lex.c\r\n#\tengine/qclib/decomp.c\r\n#\tengine/qclib/packager.c\r\n#\tengine/qclib/pr_x86.c\r\n#\tengine/qclib/qccgui.c\r\n#\tengine/qclib/qccguistuff.c\r\n#\tengine/qclib/qcctui.c\r\n\tengine/qclib/qccmain.c\r\n\tengine/qclib/qcd_main.c\r\n\tengine/qclib/qcdecomp.c\r\n)\r\nSET(FTE_COMMON_FILES\r\n\t#these files are common to both server-only and client+server builds.\r\n\tengine/common/cmd.c\r\n\tengine/common/com_mesh.c\r\n\tengine/common/com_bih.c\r\n\tengine/common/common.c\r\n\tengine/common/json.c\r\n\tengine/common/crc.c\r\n\tengine/common/cvar.c\r\n\tengine/common/fs.c\r\n\tengine/common/fs_dzip.c\r\n\tengine/common/fs_pak.c\r\n\tengine/common/fs_stdio.c\r\n\tengine/common/fs_xz.c\r\n\tengine/common/fs_zip.c\r\n\tengine/common/gl_q2bsp.c\r\n\tengine/common/huff.c\r\n\tengine/common/log.c\r\n\tengine/common/mathlib.c\r\n\tengine/common/md4.c\r\n\tengine/common/md5.c\r\n\tengine/common/net_chan.c\r\n\tengine/common/net_ice.c\r\n\tengine/common/net_wins.c\r\n\tengine/common/plugin.c\r\n\tengine/common/pmove.c\r\n\tengine/common/pmovetst.c\r\n\tengine/common/pr_bgcmd.c\r\n\tengine/common/q1bsp.c\r\n\tengine/common/q2pmove.c\r\n\tengine/common/qvm.c\r\n\tengine/common/sha1.c\r\n\tengine/common/sha2.c\r\n\tengine/common/translate.c\r\n\tengine/common/zone.c\r\n\r\n\t#important headers\r\n\tengine/common/bothdefs.h\r\n\tengine/common/config_fteqw.h\r\n\tengine/common/config_minimal.h\r\n\tengine/common/config_nocompat.h\r\n\tengine/common/config_wastes.h\r\n\tengine/common/config_freecs.h\r\n\tengine/common/config_fteqw_noweb.h\r\n\r\n\t#useless headers that I'll never search for\r\n\tengine/client/api_menu.h\r\n\tengine/client/cdaudio.h\r\n\tengine/client/client.h\r\n\tengine/client/cl_ignore.h\r\n\tengine/client/cl_master.h\r\n\tengine/client/input.h\r\n\tengine/client/keys.h\r\n\tengine/client/menu.h\r\n\tengine/client/merged.h\r\n\tengine/client/modelgen.h\r\n\tengine/client/quakedef.h\r\n\tengine/client/render.h\r\n\tengine/client/sbar.h\r\n\tengine/client/screen.h\r\n\tengine/client/sound.h\r\n\tengine/client/spritegn.h\r\n#\tengine/client/sys_plugfte.h\r\n\tengine/client/vid.h\r\n\tengine/client/view.h\r\n\tengine/client/wad.h\r\n#\tengine/client/winquake.h\r\n\tengine/common/bothdefs.h\r\n\tengine/common/bspfile.h\r\n\tengine/common/cmd.h\r\n\tengine/common/com_mesh.h\r\n\tengine/common/common.h\r\n\tengine/common/console.h\r\n\tengine/common/cvar.h\r\n\tengine/common/fs.h\r\n\tengine/common/mathlib.h\r\n\tengine/common/net.h\r\n\tengine/common/netinc.h\r\n\tengine/common/particles.h\r\n\tengine/common/pmove.h\r\n\tengine/common/pr_common.h\r\n\tengine/common/protocol.h\r\n\tengine/common/sys.h\r\n\tengine/common/translate.h\r\n\tengine/common/ui_public.h\r\n\tengine/common/vm.h\r\n\tengine/common/world.h\r\n\tengine/common/zone.h\r\n\tengine/gl/gl_draw.h\r\n\tengine/gl/gl_model.h\r\n\tengine/gl/glquake.h\r\n\tengine/gl/glsupp.h\r\n\tengine/gl/gl_terrain.h\r\n\tengine/gl/gl_videgl.h\r\n\tengine/gl/model_hl.h\r\n\tengine/gl/shader.h\r\n\tengine/http/iweb.h\r\n\tengine/qclib/cmdlib.h\r\n\tengine/qclib/execloop.h\r\n\tengine/qclib/gui.h\r\n\tengine/qclib/hash.h\r\n\tengine/qclib/pr_comp.h\r\n\tengine/qclib/progsint.h\r\n\tengine/qclib/progslib.h\r\n\tengine/qclib/progtype.h\r\n\tengine/qclib/qcc.h\r\n\tengine/qclib/qcd.h\r\n\tengine/server/progdefs.h\r\n\tengine/server/progs.h\r\n\tengine/server/q2game.h\r\n\tengine/server/server.h\r\n\t#engine/server/svhl_gcapi.h\r\n\tengine/server/sv_sql.h\r\n\t#engine/sw/sw.h\r\n\t#engine/sw/sw_spans.h\r\n\tengine/vk/vkrenderer.h\r\n\tengine/web/ftejslib.h\r\n\r\n\r\n\t#sigh\r\n\tengine/client/pr_skelobj.c\r\n\tengine/client/m_download.c\r\n\tengine/client/net_master.c\r\n\tengine/client/r_d3.c\r\n\t\r\n\t#these are here because of hitmodel etc\r\n\tengine/gl/gl_heightmap.c\r\n\tengine/gl/gl_hlmdl.c\r\n\tengine/gl/gl_model.c\r\n\t\r\n\tengine/server/sv_move.c\r\n\tengine/server/sv_phys.c\r\n\tengine/server/world.c\r\n\t\r\n\t${FTE_QCVM_FILES}\r\n\tengine/qclib/hash.c\r\n\t\r\n\tengine/http/httpclient.c\r\n)\r\n\r\nSET(FTE_SERVER_FILES\r\n\tengine/server/net_preparse.c\r\n\tengine/server/pr_cmds.c\r\n\tengine/server/pr_lua.c\r\n\tengine/server/pr_q1qvm.c\r\n\tengine/server/savegame.c\r\n\tengine/server/sv_ccmds.c\r\n\tengine/server/sv_chat.c\r\n\tengine/server/sv_cluster.c\r\n\tengine/server/sv_demo.c\r\n\tengine/server/sv_ents.c\r\n\tengine/server/sv_init.c\r\n\tengine/server/sv_main.c\r\n\tengine/server/sv_master.c\r\n\tengine/server/sv_mvd.c\r\n\tengine/server/sv_nchan.c\r\n\tengine/server/sv_rankin.c\r\n\tengine/server/sv_send.c\r\n\tengine/server/sv_sql.c\r\n\tengine/server/sv_user.c\r\n#\tengine/server/svhl_game.c\r\n#\tengine/server/svhl_phys.c\r\n#\tengine/server/svhl_world.c\r\n\tengine/server/svq2_ents.c\r\n\tengine/server/svq2_game.c\r\n)\r\n\r\n#these files are only in the client\r\nSET(FTE_CLIENT_FILES\r\n\tengine/client/cl_cam.c\r\n\tengine/client/cl_demo.c\r\n\tengine/client/cl_ents.c\r\n\tengine/client/cl_ignore.c\r\n\tengine/client/cl_input.c\r\n\tengine/client/cl_main.c\r\n\tengine/client/cl_parse.c\r\n\tengine/client/cl_pred.c\r\n\tengine/client/cl_screen.c\r\n\tengine/client/cl_tent.c\r\n#\tengine/client/clhl_game.c\r\n\tengine/client/clq2_cin.c\r\n\tengine/client/clq2_ents.c\r\n\tengine/client/console.c\r\n\tengine/client/fragstats.c\r\n\tengine/client/image.c\r\n\tengine/client/in_generic.c\r\n\tengine/client/keys.c\r\n\tengine/client/m_items.c\r\n\tengine/client/m_master.c\r\n\tengine/client/m_mp3.c\r\n\tengine/client/m_multi.c\r\n\tengine/client/m_options.c\r\n\tengine/client/m_script.c\r\n\tengine/client/m_native.c\r\n\tengine/client/m_single.c\r\n\tengine/client/menu.c\r\n\tengine/client/p_classic.c\r\n\tengine/client/p_null.c\r\n\tengine/client/p_script.c\r\n\tengine/client/pr_clcmd.c\r\n\tengine/client/pr_csqc.c\r\n\tengine/client/pr_menu.c\r\n\tengine/client/r_2d.c\r\n\tengine/client/r_d3.c\r\n\tengine/client/r_part.c\r\n\tengine/client/r_partset.c\r\n\tengine/client/r_surf.c\r\n\tengine/client/renderer.c\r\n\tengine/client/renderque.c\r\n\tengine/client/roq_read.c\r\n\tengine/client/sbar.c\r\n\tengine/client/skin.c\r\n\tengine/client/snd_dma.c\r\n\tengine/client/snd_mem.c\r\n\tengine/client/snd_mix.c\r\n\tengine/client/snd_mp3.c\r\n\tengine/client/snd_ov.c\r\n\tengine/client/textedit.c\r\n\tengine/client/valid.c\r\n\tengine/client/view.c\r\n\tengine/client/wad.c\r\n\tengine/client/zqtp.c\r\n\r\n\r\n#These are generic renderer files and no longer gl-specific (for the most part)\r\n\tengine/gl/gl_alias.c\r\n\tengine/gl/gl_font.c\r\n\tengine/gl/gl_ngraph.c\r\n\tengine/gl/gl_rlight.c\r\n\tengine/gl/gl_shader.c\r\n\tengine/gl/gl_shadow.c\r\n\tengine/gl/gl_warp.c\r\n\tengine/gl/ltface.c\r\n\r\n#these are renderer-specific\r\n\tengine/client/vid_headless.c\r\n\t${FTE_GL_FILES}\r\n\t${FTE_VK_FILES}\r\n)\r\n\r\nSET(FTE_Q3_FILES\r\n\tplugins/quake3/botlib/be_aas_bspq3.c\r\n\tplugins/quake3/botlib/be_aas_entity.c\r\n\tplugins/quake3/botlib/be_aas_move.c\r\n\tplugins/quake3/botlib/be_aas_routealt.c\r\n\tplugins/quake3/botlib/be_ai_char.c\r\n\tplugins/quake3/botlib/be_ai_goal.c\r\n\tplugins/quake3/botlib/be_ai_weight.c\r\n\tplugins/quake3/botlib/l_crc.c\r\n\tplugins/quake3/botlib/l_memory.c\r\n\tplugins/quake3/botlib/l_struct.c\r\n\tplugins/quake3/botlib/be_aas_cluster.c\r\n\tplugins/quake3/botlib/be_aas_file.c\r\n\tplugins/quake3/botlib/be_aas_optimize.c\r\n\tplugins/quake3/botlib/be_aas_route.c\r\n\tplugins/quake3/botlib/be_ai_chat.c\r\n\tplugins/quake3/botlib/be_ai_move.c\r\n\tplugins/quake3/botlib/be_ea.c\r\n\tplugins/quake3/botlib/l_libvar.c\r\n\tplugins/quake3/botlib/l_precomp.c\r\n\tplugins/quake3/botlib/be_aas_debug.c\r\n\tplugins/quake3/botlib/be_aas_main.c\r\n\tplugins/quake3/botlib/be_aas_reach.c\r\n\tplugins/quake3/botlib/be_aas_sample.c\r\n\tplugins/quake3/botlib/be_ai_gen.c\r\n\tplugins/quake3/botlib/be_ai_weap.c\r\n\tplugins/quake3/botlib/be_interface.c\r\n\tplugins/quake3/botlib/l_log.c\r\n\tplugins/quake3/botlib/l_script.c\r\n\tplugins/quake3/botlib/standalone.c\r\n\r\n\tplugins/quake3/clq3_cg.c\r\n\tplugins/quake3/clq3_ui.c\r\n\tplugins/quake3/clq3_parse.c\r\n\tplugins/quake3/svq3_game.c\r\n\tplugins/quake3/q3common.c\r\n\tplugins/quake3/q3common.h\r\n\r\n\tplugins/quake3/clq3defs.h\r\n\tplugins/quake3/q3g_public.h\r\n)\r\n\r\n#For annoying compressed gltf2 files.\r\nSET(FTE_DEP_DRACO false CACHE BOOL \"Link against libdraco (apache2).\")\r\nIF(FTE_DEP_DRACO)\r\n\tFIND_LIBRARY(\r\n\t\tDRACO_LIBRARY\r\n\t\tNAMES draco\r\n\t)\r\n\tIF(DRACO_LIBRARY)\r\n\t\tSET(DRACO_FILES plugins/models/draco.cpp)\r\n\t\tSET(DRACO_CFLAGS HAVE_DRACO)\r\n\r\n\t\tSET(FTE_COMMON_FILES ${FTE_COMMON_FILES} ${DRACO_FILES})\r\n\t\tSET(FTE_LIB_DEFINES ${FTE_LIB_DEFINES};${DRACO_CFLAGS})\r\n\t\tSET(FTE_LIBS ${FTE_LIBS} ${DRACO_LIBRARY})\r\n\t\tSET(FTESV_LIBS ${FTESV_LIBS} ${DRACO_LIBRARY})\r\n\tELSE()\r\n\t\tMESSAGE(WARNING \"draco library not found, needed for GLTF's KHR_draco_mesh_compression to be usable.\")\r\n\tENDIF()\r\nENDIF()\r\n\r\n\r\nSET(FTE_PLUG_QUAKE3 true CACHE BOOL \"Compile Quake3 plugin.\")\r\nIF(FTE_PLUG_QUAKE3)\r\n\tIF (0)\r\n\t\tSET(FTE_DEFINES ${FTE_DEFINES};${Q3_DEFINES})\r\n\t\tSET(FTE_LIBS ${FTE_LIBS} quake3)\r\n\r\n\t\t#define the modules and make sure they're linked (one generic, one for server-only builds.\r\n\t\tADD_LIBRARY(quake3 STATIC ${FTE_Q3_FILES})\r\n\t\tSET_TARGET_PROPERTIES(quake3 PROPERTIES COMPILE_DEFINITIONS \"${FTE_LIB_DEFINES};${FTE_REVISON};BOTLIB;BOTLIB_STATIC;FTEPLUGIN;STATIC_Q3\")\r\n\t\tTARGET_LINK_LIBRARIES(quake3 ${SYS_LIBS})\r\n\r\n\t\t#ADD_LIBRARY(q3sv STATIC EXCLUDE_FROM_ALL ${FTE_Q3_FILES})\r\n\t\t#SET_TARGET_PROPERTIES(q3sv PROPERTIES COMPILE_DEFINITIONS \"${FTE_LIB_DEFINES};${FTE_REVISON};BOTLIB;BOTLIB_STATIC;SERVERONLY\")\r\n\t\t#SET_TARGET_PROPERTIES(q3sv PROPERTIES LINK_FLAGS \"-Wl,--no-undefined\")\r\n\t\t#TARGET_LINK_LIBRARIES(q3sv ${SYS_LIBS})\r\n\t\t#SET(FTESV_LIBS ${FTESV_LIBS} q3sv)\r\n\tELSE()\r\n\t\t#define the modules and make sure they're linked (one generic, one for server-only builds.\r\n\t\tADD_LIBRARY(plug_quake3 MODULE ${FTE_Q3_FILES} plugins/plugin.c)\r\n\t\tSET_TARGET_PROPERTIES(plug_quake3 PROPERTIES COMPILE_DEFINITIONS \"${FTE_LIB_DEFINES};${FTE_REVISON};BOTLIB;BOTLIB_STATIC;FTEPLUGIN\")\r\n\t\tTARGET_LINK_LIBRARIES(plug_quake3 ${SYS_LIBS})\r\n\t\tEMBED_PLUGIN_META(quake3 \"Quake3 Compat\" \"Provides compatability with Quake3's gamecode.\")\r\n\tENDIF()\r\nENDIF()\r\n\r\n#still a wip, so disabled by default\r\nSET(FTE_PLUG_COD true CACHE BOOL \"Compile Call of Duty plugin.\")\r\nIF(FTE_PLUG_COD)\r\n\tADD_LIBRARY(plug_cod MODULE\r\n\t\tplugins/cod/codmod.c\r\n\t\tplugins/cod/codbsp.c\r\n\t\tplugins/cod/codmat.c\r\n\t\tplugins/cod/codiwi.c\r\n\t\t#plugins/cod/codff.c\r\n\t\tplugins/plugin.c)\r\n\tSET_TARGET_PROPERTIES(plug_cod PROPERTIES COMPILE_DEFINITIONS \"${FTE_LIB_DEFINES};${FTE_REVISON};FTEPLUGIN\")\r\n\tTARGET_LINK_LIBRARIES(plug_cod ${SYS_LIBS}\r\n\t\t#${ZLIB_LIBRARIES}\r\n\t\t)\r\n\tEMBED_PLUGIN_META(cod \"CoD Formats\" \"Provides compatability with Call Of Duty's file formats.\")\r\nENDIF()\r\n\r\nFILE(STRINGS \"${FTE_BUILD_CONFIG}\" BULLET_INTERNAL REGEX \"^#define[\\t ]+USE_INTERNAL_BULLET\")\r\nIF(BULLET_INTERNAL)\r\n\t#Built-in bullet physics plugin...\r\n\tFIND_PACKAGE(Bullet REQUIRED)\r\n\tSET(FTE_COMMON_FILES ${FTE_COMMON_FILES} plugins/bullet/bulletplug.cpp)\r\n\tINCLUDE_DIRECTORIES( ${BULLET_INCLUDE_DIRS} )\r\n\tSET(FTE_LIBS ${FTE_LIBS} ${BULLET_LIBRARIES})\r\n\tSET(FTESV_LIBS ${FTESV_LIBS} ${BULLET_LIBRARIES})\r\nELSE()\r\n\t#Bullet Physics library plugin\r\n\tSET(FTE_PLUG_BULLET true CACHE BOOL \"Compile bullet rigid body physics plugin.\")\r\n\tIF(FTE_PLUG_BULLET)\r\n\t\tFIND_PACKAGE(Bullet)\r\n\t\tIF (BULLET_FOUND)\r\n\t\t\tADD_LIBRARY(plug_bullet MODULE\r\n\t\t\t\tplugins/plugin.c\r\n\t\t\t\tplugins/bullet/bulletplug.cpp\r\n\t\t\t)\r\n\t\t\tTARGET_INCLUDE_DIRECTORIES(plug_bullet PUBLIC ${BULLET_INCLUDE_DIRS})\r\n\t\t\tSET_TARGET_PROPERTIES(plug_bullet PROPERTIES COMPILE_DEFINITIONS \"FTEPLUGIN;${FTE_LIB_DEFINES}\")\r\n\t\t\tTARGET_LINK_LIBRARIES(plug_bullet ${SYS_LIBS} ${BULLET_LIBRARIES})\r\n\r\n\t\t\tEMBED_PLUGIN_META(bullet \"Bullet Physics Plugin\" \"Provides Rigid Body Physics.\")\r\n\t\tELSE()\r\n\t\t\tMESSAGE(WARNING \"bullet library not detected, skipping plugin\")\r\n\t\tENDIF()\r\n\tENDIF()\r\nENDIF()\r\n\r\n#ODE Physics library plugin\r\nSET(FTE_PLUG_ODE true CACHE BOOL \"Compile ODE rigid body physics plugin.\")\r\nSET_PROPERTY(CACHE FTE_PLUG_ODE PROPERTY STRINGS false true static)\r\nIF(FTE_PLUG_ODE)\r\n\tFIND_PATH(LIBODE_INCLUDE_DIR ode/ode.h)\r\n\tIF (LIBODE_INCLUDE_DIR)\r\n\t\tFIND_LIBRARY(LIBODE_LIBRARY ode)\r\n\tENDIF()\r\n\tIF (LIBODE_LIBRARY)\r\n\t\tIF (FTE_PLUG_ODE STREQUAL \"static\")\r\n\t\t\t#SET (FTE_COMMON_FILES ${FTE_COMMON_FILES} engine/common/com_phys_ode.c)\r\n\t\t\tSET(FTE_LIB_DEFINES \"${FTE_LIB_DEFINES};USE_INTERNAL_ODE;ODE_STATIC\")\r\n\t\t\tSET(FTE_LIBS ${FTE_LIBS} ${LIBODE_LIBRARY})\r\n\t\t\tSET(FTESV_LIBS ${FTESV_LIBS} ${LIBODE_LIBRARY})\r\n\t\t\tSET(FTE_INCLUDES ${FTE_INCLUDES} ${ODE_INCLUDE_DIRS})\r\n\t\tELSE()\r\n\t\t\tADD_LIBRARY(plug_ode MODULE\r\n\t\t\t\tplugins/plugin.c\r\n\t\t\t\tengine/common/com_phys_ode.c\r\n\t\t\t\tengine/common/mathlib.c\r\n\t\t\t)\r\n\t\t\tTARGET_INCLUDE_DIRECTORIES(plug_ode PUBLIC ${ODE_INCLUDE_DIRS})\r\n\t\t\tSET_TARGET_PROPERTIES(plug_ode PROPERTIES COMPILE_DEFINITIONS \"FTEPLUGIN;ODE_STATIC\")\r\n\t\t\tTARGET_LINK_LIBRARIES(plug_ode ${SYS_LIBS} ${LIBODE_LIBRARY})\r\n\r\n\t\t\tEMBED_PLUGIN_META(ode \"ODE Physics\" \"Provides Rigid Body Physics behaviours.\")\r\n\t\tENDIF()\r\n\tELSE()\r\n\t\tMESSAGE(WARNING \"ODE library not found, no ode plugin for you\")\r\n\tENDIF()\r\nENDIF()\r\n\r\nIF(ANDROID)\r\n\t#android sucks. everything is a library. so we build the engine as a shared library and completely ignore dedicated servers+tools\r\n\tSET(FTE_ENGINE_FTEDROID true CACHE BOOL \"Compile ftedroid engine shared library.\")\r\n\tIF(FTE_ENGINE_FTEDROID)\r\n\t\tADD_LIBRARY(ftedroid MODULE\r\n\t\t\t${FTE_ARCH_FILES}\r\n\t\t\t${FTE_COMMON_FILES}\r\n\t\t\t${FTE_CLIENT_FILES}\r\n\t\t)\r\n\t\tSET_TARGET_PROPERTIES(ftedroid PROPERTIES COMPILE_DEFINITIONS \"${FTE_LIB_DEFINES};${FTE_DEFINES};${FTE_REVISON}\")\r\n\t\tTARGET_INCLUDE_DIRECTORIES(ftedroid PUBLIC ${FTE_INCLUDES})\r\n\t\tTARGET_LINK_LIBRARIES(ftedroid ${FTE_LIBS} )\r\n\t\tSET(INSTALLTARGS ${INSTALLTARGS} ftedroid)\r\n\tENDIF()\r\nELSE()\r\n\t#systems that actually have executables...\r\n\tSET(FTE_ENGINE true CACHE BOOL \"Compile fteqw engine binary.\")\r\n\tIF(FTE_ENGINE)\r\n\t\tADD_EXECUTABLE(fteqw WIN32\r\n\t\t\t${FTE_ARCH_FILES}\r\n\t\t\t${FTE_COMMON_FILES}\r\n\t\t\t${FTE_CLIENT_FILES}\r\n\t\t\t${FTE_SERVER_FILES}\r\n\t\t)\r\n\t\tIF(CMAKE_SYSTEM_NAME MATCHES \"BSD\" OR CMAKE_SYSTEM_NAME MATCHES \"Darwin\")\r\n\t\t\tFIND_LIBRARY(epoll-shim REQUIRED)\r\n\t\t\tSET(FTE_INCLUDES ${FTE_INCLUDES} \"${EPOLL_INC_DIR}\")\r\n\t\t\tSET(FTE_LIBS ${FTE_LIBS} epoll-shim)\r\n\t\t\tSET(FTE_DEFINES ${FTE_DEFINES};HAVE_EPOLL)\r\n\t\tENDIF()\r\n\t\tSET_TARGET_PROPERTIES(fteqw PROPERTIES COMPILE_DEFINITIONS \"${FTE_LIB_DEFINES};${FTE_DEFINES};${FTE_REVISON}\")\r\n\t\tTARGET_INCLUDE_DIRECTORIES(fteqw PUBLIC ${FTE_INCLUDES})\r\n\t\tTARGET_LINK_LIBRARIES(fteqw ${FTE_LIBS})\r\n\t\tSET(INSTALLTARGS ${INSTALLTARGS} fteqw)\r\n\r\n\t\tADD_CUSTOM_TARGET(fteqw-i18n ALL\r\n\t\t\tVERBATIM\r\n\t\t\tWORKING_DIRECTORY \"${CMAKE_CURRENT_SOURCE_DIR}\"\r\n\t\t\tCOMMAND xgettext --output=${CMAKE_CURRENT_BINARY_DIR}/fteqw.pot -k -kCon_TPrintf -kCon_SafeTPrintf -kNetchan_OutOfBandTPrintf:4 -kSV_OutOfBandTPrintf:4 -klangtext -kSV_TPrintToClient:3 -kSV_ClientTPrintf:3 -kSV_BroadcastTPrintf:2 -kCVARAFCD:6 -kCVARAFD:5 -kCVARFCD:5 -kCVARAD:4 -kCVARFD:4 -kCVARCD:4 -kCVARD:3 -kCmd_AddCommandD:3 -kCmd_AddCommandAD:4 -kMenu_Prompt:4:5:6 -kHost_EndGame\t${FTE_ARCH_FILES} ${FTE_COMMON_FILES} ${FTE_CLIENT_FILES} ${FTE_SERVER_FILES}\r\n\t\t\tBYPRODUCTS \"${CMAKE_CURRENT_BINARY_DIR}/fteqw.pot\"\r\n\t\t\tSOURCES\r\n\t\t\t\t${FTE_ARCH_FILES} ${FTE_COMMON_FILES} ${FTE_CLIENT_FILES} ${FTE_SERVER_FILES}\r\n\t\t)\r\n\tENDIF()\r\n\r\n\tSET(FTE_ENGINE_SERVER_ONLY true CACHE BOOL \"Compile fteqw-sv (server only) engine binary.\")\r\n\tIF(FTE_ENGINE_SERVER_ONLY)\r\n\t\tADD_EXECUTABLE(fteqw-sv\r\n\t\t\t${FTESV_ARCH_FILES}\r\n\t\t\t${FTE_COMMON_FILES}\r\n\t\t\t${FTE_SERVER_FILES}\r\n\t\t)\r\n\t\tSET_TARGET_PROPERTIES(fteqw-sv PROPERTIES COMPILE_DEFINITIONS \"SERVERONLY;${FTE_LIB_DEFINES};${FTESV_DEFINES};${FTE_REVISON}\")\r\n\t\tTARGET_INCLUDE_DIRECTORIES(fteqw-sv PUBLIC ${FTE_INCLUDES})\r\n\t\tTARGET_LINK_LIBRARIES(fteqw-sv ${FTESV_LIBS})\r\n\t\tSET(INSTALLTARGS ${INSTALLTARGS} fteqw-sv)\r\n\tENDIF()\r\n\r\n\tSET(FTE_ENGINE_CLIENT_ONLY false CACHE BOOL \"Compile ftedw-cl (client-only) engine binary.\")\r\n\tIF(FTE_ENGINE_CLIENT_ONLY)\r\n\t\tADD_EXECUTABLE(fteqw-cl\r\n\t\t\t${FTE_ARCH_FILES}\r\n\t\t\t${FTE_COMMON_FILES}\r\n\t\t\t${FTE_CLIENT_FILES}\r\n\t\t)\r\n\t\tSET_TARGET_PROPERTIES(fteqw-cl PROPERTIES COMPILE_DEFINITIONS \"CLIENTONLY;${FTE_LIB_DEFINES};${FTE_DEFINES};${FTE_REVISON}\")\r\n\t\tTARGET_INCLUDE_DIRECTORIES(fteqw-cl PUBLIC ${FTE_INCLUDES})\r\n\t\tTARGET_LINK_LIBRARIES(fteqw-cl ${FTE_LIBS})\r\n\t\tSET(INSTALLTARGS ${INSTALLTARGS} fteqw-cl)\r\n\tENDIF()\r\n\r\n\tIF(FTE_ENGINE OR FTE_ENGINE_CLIENT)\r\n\t\tFIND_PACKAGE(SDL2)\r\n\t\tIF(SDL2_FOUND)\r\n\t\t\tSET(FTE_SDL2 FTE_SDL)\r\n\t\t\tSET(FTE_SDL2_INCLUDES ${SDL2_INCLUDE_DIRS})\r\n\t\tENDIF()\r\n\tENDIF()\r\n\r\n\tSET(FTE_TOOL_IQM true CACHE BOOL \"Compile IQM Tool.\")\r\n\tIF(FTE_TOOL_IQM)\r\n\t\tADD_EXECUTABLE(iqmtool\r\n\t\t\tiqm/iqm.cpp\r\n\t\t\tplugins/models/gltf.c\r\n\t\t\t${DRACO_FILES}\r\n\t\t\tengine/common/json.c\r\n\t\t\tengine/client/image.c\r\n\t\t\timgtool.c\r\n\t\t\tiqm/iqm.h\r\n\t\t)\r\n\t\tSET_TARGET_PROPERTIES(iqmtool PROPERTIES COMPILE_DEFINITIONS \"IQMTOOL;${DRACO_CFLAGS};${FTE_LIB_DEFINES};${FTE_REVISON}\")\r\n\t\tTARGET_LINK_LIBRARIES(iqmtool ${CMAKE_DL_LIBS} ${DRACO_LIBRARY} ${JPEG_LIBRARIES} ${PNG_LIBRARIES})\r\n\t\tSET(INSTALLTARGS ${INSTALLTARGS} iqmtool)\r\n\tENDIF()\r\n\r\n\tSET(FTE_TOOL_IMAGE true CACHE BOOL \"Compile Image Tool.\")\r\n\tIF(FTE_TOOL_IMAGE)\r\n\t\tADD_EXECUTABLE(imgtool\r\n\t\t\tengine/client/image.c\r\n\t\t\timgtool.c\r\n\t\t)\r\n\t\tTARGET_INCLUDE_DIRECTORIES(imgtool PUBLIC ${FTE_SDL2_INCLUDES})\r\n\t\tSET_TARGET_PROPERTIES(imgtool PROPERTIES COMPILE_DEFINITIONS \"IMGTOOL;${FTE_LIB_DEFINES};${FTE_DEFINES};${FTE_REVISON};${FTE_SDL2}\")\r\n\t\tTARGET_LINK_LIBRARIES(imgtool ${FTE_LIBS} )\r\n\t\tSET(INSTALLTARGS ${INSTALLTARGS} imgtool)\r\n\tENDIF()\r\n\r\n\tSET(FTE_TOOL_QTV true CACHE BOOL \"Compile qtv server.\")\r\n\tIF(FTE_TOOL_QTV)\r\n\t\tADD_EXECUTABLE(qtv\r\n\t\t\tfteqtv/netchan.c\r\n\t\t\tfteqtv/parse.c\r\n\t\t\tfteqtv/msg.c\r\n\t\t\tfteqtv/qw.c\r\n\t\t\tfteqtv/source.c\r\n\t\t\tfteqtv/bsp.c\r\n\t\t\tfteqtv/rcon.c\r\n\t\t\tfteqtv/relay.c\r\n\t\t\tfteqtv/mdfour.c\r\n\t\t\tengine/common/md5.c\r\n\t\t\tfteqtv/crc.c\r\n\t\t\tfteqtv/control.c\r\n\t\t\tfteqtv/forward.c\r\n\t\t\tfteqtv/pmove.c\r\n\t\t\tfteqtv/menu.c\r\n\t\t\tfteqtv/httpsv.c\r\n\t\t\tfteqtv/libqtvc/glibc_sucks.c\r\n\t\t\tengine/common/sha1.c\r\n\t\t)\r\n\t\tSET_TARGET_PROPERTIES(qtv PROPERTIES COMPILE_DEFINITIONS \"${FTE_REVISON};${FTE_LIB_DEFINES}\")\r\n\t\tIF(WIN32)\r\n\t\t\tTARGET_LINK_LIBRARIES(qtv ws2_32 winmm ${SYS_LIBS} ${FTEQTV_LIBS})\r\n\t\tELSEIF(CMAKE_SYSTEM_NAME MATCHES \"BSD\" OR CMAKE_SYSTEM_NAME MATCHES \"Darwin\")\r\n\t\t\t# Add Epoll-shim for the kqueue Unixes here - Brad\r\n\t\t\tFIND_LIBRARY(epoll-shim REQUIRED)\r\n\t\t\tTARGET_INCLUDE_DIRECTORIES(qtv PUBLIC \"${EPOLL_INC_DIR}\")\r\n\t\t\tTARGET_LINK_LIBRARIES(qtv epoll-shim ${SYS_LIBS} ${FTEQTV_LIBS})\r\n\t\t\tSET_TARGET_PROPERTIES(qtv PROPERTIES COMPILE_DEFINITIONS \"${FTE_REVISON};${FTE_LIB_DEFINES};HAVE_EPOLL\")\r\n\t\tELSE()\r\n\t\t\tTARGET_LINK_LIBRARIES(qtv ${SYS_LIBS} ${FTEQTV_LIBS})\r\n\t\tENDIF()\r\n\t\tSET(INSTALLTARGS ${INSTALLTARGS} qtv)\r\n\tENDIF()\r\n\r\n\tIF(WIN32)\r\n\t\tSET(FTE_TOOL_MASTER false CACHE BOOL \"Compile master server.\")\r\n\tELSE()\r\n\t\tSET(FTE_TOOL_MASTER true CACHE BOOL \"Compile master server.\")\r\n\tENDIF()\r\n\tIF(FTE_TOOL_MASTER)\r\n\t\tADD_EXECUTABLE(ftemaster\r\n\t\t\t${FTESV_ARCH_FILES}\r\n\t\t\tengine/server/sv_master.c\r\n\t\t\tengine/common/net_ice.c\t\t#for the stun responses.\r\n\t\t\tengine/common/net_wins.c\r\n\t\t\tengine/common/cvar.c\r\n\t\t\tengine/common/cmd.c\r\n\t\t\tengine/common/sha1.c\t\t#for websockets\r\n\t\t\tengine/common/sha2.c\t\t#for fingerprints\r\n\t\t\tengine/http/httpclient.c\t#for the pipe stuff\r\n\t\t\tengine/common/log.c\r\n\t\t\tengine/common/fs.c\r\n\t\t\tengine/common/fs_stdio.c\r\n\t\t\tengine/common/common.c\r\n\t\t\tengine/common/translate.c\r\n\t\t\tengine/common/zone.c\r\n\t\t\tengine/qclib/hash.c\r\n\t\t)\r\n\t\tSET_TARGET_PROPERTIES(ftemaster PROPERTIES COMPILE_DEFINITIONS \"MASTERONLY;${FTE_LIB_DEFINES};${FTESV_DEFINES};${FTE_REVISON}\")\r\n\t\tTARGET_LINK_LIBRARIES(ftemaster ${FTESV_LIBS})\r\n\t\tSET(INSTALLTARGS ${INSTALLTARGS} ftemaster)\r\n\tENDIF()\r\n\r\n\tSET(FTE_TOOL_HTTPSV true CACHE BOOL \"Compile small http server.\")\r\n\tIF(FTE_TOOL_HTTPSV)\r\n\t\tADD_EXECUTABLE(httpserver\r\n\t\t\tengine/common/fs_stdio.c\r\n\t\t\tengine/http/httpserver.c\r\n\t\t\tengine/http/iwebiface.c\r\n\t\t\tengine/http/ftpserver.c\r\n\t\t)\r\n\t\tSET_TARGET_PROPERTIES(httpserver PROPERTIES COMPILE_DEFINITIONS \"WEBSERVER;WEBSVONLY;${FTE_REVISON}\")\r\n\t\tIF(WIN32)\r\n\t\t\tTARGET_LINK_LIBRARIES(httpserver ws2_32)\r\n\t\tELSEIF(CMAKE_SYSTEM_NAME MATCHES \"BSD\" OR CMAKE_SYSTEM_NAME MATCHES \"Darwin\")\r\n\t\t\tFIND_LIBRARY(epoll-shim REQUIRED)\r\n\t\t\tTARGET_INCLUDE_DIRECTORIES(httpserver PUBLIC \"${EPOLL_INC_DIR}\")\r\n\t\t\tTARGET_LINK_LIBRARIES(httpserver epoll-shim)\r\n\t\t\tSET_TARGET_PROPERTIES(httpserver PROPERTIES COMPILE_DEFINITIONS \"WEBSERVER;WEBSVONLY;${FTE_REVISON};HAVE_EPOLL\")\r\n\t\tENDIF()\r\n\t\t#SET(INSTALLTARGS ${INSTALLTARGS} httpserver)\r\n\tENDIF()\r\n\r\n\tSET(FTE_TOOL_QCVM false CACHE BOOL \"Compile standalone qcvm.\")\r\n\tIF(FTE_TOOL_QCVM)\r\n\t\tADD_EXECUTABLE(qcvm\r\n\t\t\tengine/qclib/test.c\r\n\r\n\t\t\tengine/qclib/hash.c\r\n\t\t\t${FTE_QCVM_FILES}\r\n\t\t)\r\n\t\tSET_TARGET_PROPERTIES(qcvm PROPERTIES COMPILE_DEFINITIONS \"${FTE_LIB_DEFINES};${FTE_REVISON}\")\r\n\t\tTARGET_LINK_LIBRARIES(qcvm ${FTEQCC_LIBS} ${SYS_LIBS})\r\n\t\tSET(INSTALLTARGS ${INSTALLTARGS} qcvm)\r\n\tENDIF()\r\n\r\n\tSET(FTE_TOOL_QCC true CACHE BOOL \"Compile commandline qc compiler.\")\r\n\tIF(FTE_TOOL_QCC)\r\n\t\tADD_EXECUTABLE(fteqcc\r\n\t\t\tengine/qclib/qcctui.c\r\n\t\t\tengine/qclib/comprout.c\r\n\t\t\tengine/qclib/hash.c\r\n\t\t\tengine/qclib/qcc_cmdlib.c\r\n\t\t\tengine/qclib/qcc_pr_comp.c\r\n\t\t\tengine/qclib/qcc_pr_lex.c\r\n\t\t\tengine/qclib/qccmain.c\r\n\t\t\tengine/qclib/qcd_main.c\r\n\t\t\tengine/qclib/decomp.c\r\n\t\t\tengine/qclib/packager.c\r\n\t\t)\r\n\t\tSET_TARGET_PROPERTIES(fteqcc PROPERTIES COMPILE_DEFINITIONS \"${FTE_LIB_DEFINES};${FTE_REVISON}\")\r\n\t\tTARGET_LINK_LIBRARIES(fteqcc ${FTEQCC_LIBS} ${SYS_LIBS})\r\n\t\tSET(INSTALLTARGS ${INSTALLTARGS} fteqcc)\r\n\tENDIF()\r\n\r\n\tSET(FTE_TOOL_QCCGUI true CACHE BOOL \"Compile gui qc compiler.\")\r\n\tIF(FTE_TOOL_QCCGUI)\r\n\t\tIF(${WIN32})\r\n\t\t\tADD_EXECUTABLE(fteqccgui WIN32\r\n\t\t\t\tengine/qclib/qccgui.c\r\n\t\t\t\tengine/qclib/qccguistuff.c\r\n\t\t\t\tengine/qclib/comprout.c\r\n\t\t\t\tengine/qclib/hash.c\r\n\t\t\t\tengine/qclib/qcc_cmdlib.c\r\n\t\t\t\tengine/qclib/qcc_pr_comp.c\r\n\t\t\t\tengine/qclib/qcc_pr_lex.c\r\n\t\t\t\tengine/qclib/qccmain.c\r\n\t\t\t\tengine/qclib/decomp.c\r\n\t\t\t\tengine/qclib/packager.c\r\n\t\t\t\tengine/qclib/qcd_main.c\r\n\t\t\t)\r\n\t\t\tSET_TARGET_PROPERTIES(fteqccgui PROPERTIES COMPILE_DEFINITIONS \"${FTE_LIB_DEFINES};${FTE_REVISON}\")\r\n\t\t\tTARGET_LINK_LIBRARIES(fteqccgui ${FTEQCC_LIBS} shlwapi ole32 comctl32 comdlg32)\r\n\t\t\tSET(INSTALLTARGS ${INSTALLTARGS} fteqccgui)\r\n\t\tELSE()\r\n\t\t\tFIND_PACKAGE(Qt5Widgets)\r\n\r\n\t\t\tFIND_PATH(QSCINTILLA_INCLUDE_DIR\r\n\t\t\t\tNAMES Qsci/qsciglobal.h\r\n\t\t\t\tPATHS ${Qt5Widgets_INCLUDE_DIRS}\r\n\t\t\t\tPATH_SUFFIXES Qsci\r\n\t\t\t)\r\n\t\t\tFIND_LIBRARY(QSCINTILLA_LIBRARY\r\n\t\t\t\tNAMES qscintilla2_qt5\r\n\t\t\t\tPATHS\r\n\t\t\t\t\t${QT_LIBRARY_DIR}\r\n\t\t\t\t\t/usr/local/lib\r\n\t\t\t\t\t/usr/local/lib/qt5\r\n\t\t\t\t\t/usr/lib\r\n\t\t\t)\r\n\r\n\t\t\tIF (QSCINTILLA_INCLUDE_DIR AND QSCINTILLA_LIBRARY AND Qt5Widgets_FOUND)\r\n\t\t\t\tADD_EXECUTABLE(fteqccgui\r\n\t\t\t\t\tengine/qclib/qccguiqt.cpp\r\n\t\t\t\t\tengine/qclib/qccguistuff.c\r\n\t\t\t\t\tengine/qclib/comprout.c\r\n\t\t\t\t\tengine/qclib/hash.c\r\n\t\t\t\t\tengine/qclib/qcc_cmdlib.c\r\n\t\t\t\t\tengine/qclib/qcc_pr_comp.c\r\n\t\t\t\t\tengine/qclib/qcc_pr_lex.c\r\n\t\t\t\t\tengine/qclib/qccmain.c\r\n\t\t\t\t\tengine/qclib/decomp.c\r\n#\t\t\t\t\tengine/qclib/packager.c\r\n\t\t\t\t\tengine/qclib/qcd_main.c\r\n\t\t\t\t)\r\n\t\t\t\tTARGET_INCLUDE_DIRECTORIES(fteqccgui PUBLIC ${Qt5Widgets_INCLUDE_DIRS} ${QSCINTILLA_INCLUDE_DIR})\r\n\t\t\t\tSET_TARGET_PROPERTIES(fteqccgui PROPERTIES COMPILE_DEFINITIONS \"${FTE_LIB_DEFINES};${FTE_REVISON};${Qt5Widgets_COMPILE_DEFINITIONS}\")\r\n\t\t\t\tSET_PROPERTY(TARGET fteqccgui PROPERTY POSITION_INDEPENDENT_CODE TRUE)\r\n\t\t\t\tTARGET_LINK_LIBRARIES(fteqccgui ${FTEQCC_LIBS} ${Qt5Widgets_LIBRARIES} ${QSCINTILLA_LIBRARY})\r\n\t\t\t\tSET(INSTALLTARGS ${INSTALLTARGS} fteqccgui)\r\n\t\t\tELSE()\r\n\t\t\t\tMESSAGE(WARNING \"qscintilla/qt5widgets library not detected, no fteqccgui for you\")\r\n\t\t\tENDIF()\r\n\t\tENDIF()\r\n\tENDIF()\r\nENDIF()\r\n\r\nIF(0)\r\n\t#software renderer plugin\r\n\t#not stable enough, and probably won't ever be\r\n\tADD_LIBRARY(sw MODULE\r\n\t\tplugins/plugin.c\r\n\t\tengine/sw/sw_backend.c\r\n\t\tengine/sw/sw_image.c\r\n\t\tengine/sw/sw_rast.c\r\n\t\t#engine/sw/sw_viddos.c\r\n#\t\tengine/sw/sw_vidwin.c\r\n\t\tengine/common/mathlib.c\r\n#\t\tengine/client/in_win.c\r\n\r\n\t\tengine/sw/sw.h\r\n\t\tengine/sw/sw_spans.h\r\n\t)\r\n\tSET_TARGET_PROPERTIES(sw PROPERTIES COMPILE_DEFINITIONS \"FTEPLUGIN;${FTE_LIB_DEFINES};SWQUAKE\")\r\n\tTARGET_LINK_LIBRARIES(sw ${SYS_LIBS})\r\n\r\n\tEMBED_PLUGIN_META(sw \"Software Renderer\" \"Provides software rendering. Slow.\")\r\nENDIF()\r\n\r\n#Quake Injector Alike plugin\r\nSET(FTE_PLUG_QI true CACHE BOOL \"Compile Quake-Injnector plugin.\")\r\nIF(FTE_PLUG_QI)\r\n\tADD_LIBRARY(plug_qi MODULE\r\n\t\tplugins/plugin.c\r\n\t\tplugins/qi/qi.c\r\n\t\tplugins/jabber/xml.c\r\n\t)\r\n\tSET_TARGET_PROPERTIES(plug_qi PROPERTIES COMPILE_DEFINITIONS \"FTEPLUGIN;${FTE_LIB_DEFINES}\")\r\n\tTARGET_LINK_LIBRARIES(plug_qi ${SYS_LIBS})\r\n\r\n\tEMBED_PLUGIN_META(qi \"Quaddicted Map Database\" \"Provides easy access to the quaddicted map database. Once installed you can use eg 'map qi_dopa:start' to begin playing dopa, or load it via the menus.\")\r\nENDIF()\r\n\r\nSET(FTE_PLUG_OPENSSL false CACHE BOOL \"Compile OpenSSL.\")\r\nIF(FTE_PLUG_OPENSSL)\r\n\t#the openssl license is incompatible with the GPL, so while we have code to use it distributing the binaries built with it is not a (legal) option.\r\n\t#note that openssl 3.0.0 upwards are apache-2 licensed, which IS gpl-3 compatible (though not gpl-2). debian has not caught up with that yet, however.\r\n\t#Crosscompile linux->win64: sudo ln -s ${pwd}/engine/libs-x86_64-w64-mingw32/openssl-openssl-3.0.1/ /usr/x86_64-w64-mingw32/OpenSSL\r\n\tSET(OPENSSL_USE_STATIC_LIBS true CACHE BOOL \"Link openssl statically.\") #usually as an .so though. :/)\r\n\tFIND_PACKAGE(OpenSSL)\r\n\tIF(OPENSSL_VERSION_MAJOR LESS 3)\r\n\t\tSET(FTE_PRIVATE_USE_ONLY false CACHE BOOL \"Ignore license violations.\")\r\n\tENDIF()\r\n\tIF(NOT OPENSSL_FOUND)\r\n\t\tMESSAGE(WARNING \"openssl library NOT available. you'll have to use some other library.\")\r\n\tELSEIF(OPENSSL_VERSION_MAJOR LESS 3 AND NOT FTE_PRIVATE_USE_ONLY)\r\n\t\tMESSAGE(WARNING \"openssl v3 required for GPL compliance. Enable FTE_PRIVATE_USE_ONLY to compile openssl plugin.\")\r\n\tELSE()\r\n\t\tIF(OPENSSL_VERSION_MAJOR LESS 3)\r\n\t\t\tMESSAGE(WARNING \"openssl library version is not 3 or above. You may not distribute plugin binaries due to license conflict.\")\r\n\t\tELSE()\r\n\t\t\tMESSAGE(WARNING \"Using openssl. Resulting plugin must be licensed as GPLv3.\")\r\n\t\tENDIF()\r\n\t\tSET(FTE_LIB_DEFINES ${FTE_LIB_DEFINES})\r\n\t\tif (WIN32)\r\n\t\t\tSET(OPENSSL_LIBRARIES ${OPENSSL_LIBRARIES} ws2_32)\r\n\t\tENDIF()\r\n\r\n\t\tADD_LIBRARY(plug_openssl MODULE\r\n\t\t\tplugins/plugin.c\r\n\t\t\tplugins/net_ssl_openssl.c\r\n\t\t)\r\n\t\tTARGET_INCLUDE_DIRECTORIES(plug_openssl PRIVATE ${OPENSSL_INCLUDE_DIR})\r\n\t\tSET_TARGET_PROPERTIES(plug_openssl PROPERTIES COMPILE_DEFINITIONS \"FTEPLUGIN;${FTE_LIB_DEFINES}\")\r\n\t\tTARGET_LINK_LIBRARIES(plug_openssl ${SYS_LIBS} ${OPENSSL_LIBRARIES})\r\n\r\n\t\tEMBED_PLUGIN_META(openssl \"OpenSSL\" \"Provides OpenSSL support for dtls/tls/https support. The crypto library that is actually used is controlled via the tls_provider cvar.\")\r\n\tENDIF()\r\nENDIF()\r\n\r\n#SET(FTE_PLUG_GNUTLS true CACHE BOOL \"Compile GnuTLS Library.\")\r\n#IF(FTE_PLUG_GNUTLS)\r\n#\tFIND_PACKAGE(GnuTLS)\r\n#\tIF(NOT GNUTLS_FOUND)\r\n#\t\tMESSAGE(WARNING \"gnutls library NOT available. you'll have to use some other library.\")\r\n#\tELSE()\r\n#\t\tSET(FTE_LIB_DEFINES ${FTE_LIB_DEFINES})\r\n#\r\n#\t\tADD_LIBRARY(plug_gnutls MODULE\r\n#\t\t\tplugins/plugin.c\r\n#\t\t\tengine/common/net_ssl_gnutls.c\r\n#\t\t)\r\n#\t\tSET_TARGET_PROPERTIES(plug_gnutls PROPERTIES COMPILE_DEFINITIONS \"FTEPLUGIN;${FTE_LIB_DEFINES}\")\r\n#\t\tTARGET_LINK_LIBRARIES(plug_gnutls ${SYS_LIBS} ${GNUTLS_LIBRARIES})\r\n#\r\n#\t\tEMBED_PLUGIN_META(gnutls \"GnuTLS\" \"Provides GnuTLS support for dtls/tls/https support. The crypto library that is actually used is controlled via the tls_provider cvar.\")\r\n#\tENDIF()\r\n#ENDIF()\r\n\r\n#EzQuake Hud port plugin\r\nSET(FTE_PLUG_EZHUD true CACHE BOOL \"Compile MoreQuakeWorld Hud plugin .\")\r\nIF(FTE_PLUG_EZHUD)\r\n\tADD_LIBRARY(plug_ezhud MODULE\r\n\t\tplugins/plugin.c\r\n\t\tplugins/ezhud/ezquakeisms.c\r\n\t\tplugins/ezhud/hud.c\r\n\t\tplugins/ezhud/hud_common.c\r\n\t\tplugins/ezhud/hud_editor.c\r\n\t)\r\n\tSET_TARGET_PROPERTIES(plug_ezhud PROPERTIES COMPILE_DEFINITIONS \"FTEPLUGIN;${FTE_LIB_DEFINES}\")\r\n\tTARGET_LINK_LIBRARIES(plug_ezhud ${SYS_LIBS})\r\n\r\n\tEMBED_PLUGIN_META(ezhud \"EzHud Plugin\" \"Provides compat with ezquake's hud scripts.\")\r\nENDIF()\r\n\r\n#NameMaker string generation plugin\r\nSET(FTE_PLUG_NAMEMAKER false CACHE BOOL \"Compile namemaker plugin.\")\r\nIF(FTE_PLUG_NAMEMAKER)\r\n\tADD_LIBRARY(plug_namemaker MODULE\r\n\t\tplugins/plugin.c\r\n\t\tplugins/namemaker/namemaker.c\r\n\t)\r\n\tSET_TARGET_PROPERTIES(plug_namemaker PROPERTIES COMPILE_DEFINITIONS \"${FTE_LIB_DEFINES}\")\r\n\tTARGET_LINK_LIBRARIES(plug_namemaker ${SYS_LIBS})\r\n\r\n\tEMBED_PLUGIN_META(namemaker \"Name Maker Plugin\" \"Provides a lame UI for selecting arbitrary non-ascii glyphs as part of your nickname.\")\r\nENDIF()\r\n\r\n#Terrain Generation plugin\r\nSET(FTE_PLUG_TERRAINGEN false CACHE BOOL \"Compile sample terrain generation plugin.\")\r\nIF(FTE_PLUG_TERRAINGEN)\r\n\tADD_LIBRARY(plug_terraingen MODULE\r\n\t\tplugins/plugin.c\r\n\t\tplugins/terrorgen/terragen.c\r\n\t)\r\n\tSET_TARGET_PROPERTIES(plug_terraingen PROPERTIES COMPILE_DEFINITIONS \"FTEPLUGIN;${FTE_LIB_DEFINES}\")\r\n\tTARGET_LINK_LIBRARIES(plug_terraingen ${SYS_LIBS})\r\n\r\n\tEMBED_PLUGIN_META(terraingen \"TerrainGen Plugin\" \"A lame example plugin for randomised terrain generation.\")\r\nENDIF()\r\n\r\n#IRC client plugin\r\nSET(FTE_PLUG_IRC true CACHE BOOL \"Compile irc plugin.\")\r\nIF(FTE_PLUG_IRC)\r\n\tADD_LIBRARY(plug_irc MODULE\r\n\t\tplugins/plugin.c\r\n\t\tplugins/irc/ircclient.c\r\n\t)\r\n\tSET_TARGET_PROPERTIES(plug_irc PROPERTIES COMPILE_DEFINITIONS \"FTEPLUGIN;${FTE_REVISON};${FTE_LIB_DEFINES}\")\r\n\tTARGET_LINK_LIBRARIES(plug_irc ${SYS_LIBS})\r\n\r\n\tEMBED_PLUGIN_META(irc \"IRC Plugin\" \"Allows you to chat on IRC without tabbing out.\")\r\nENDIF()\r\n\r\nIF(ZLIB_FOUND)\r\n#mpq package format plugin (blizzard games)\r\nSET(FTE_PLUG_MPQ false CACHE BOOL \"Compile mpq junk.\")\r\nIF(FTE_PLUG_MPQ)\r\n\tADD_LIBRARY(plug_mpq MODULE\r\n\t\tplugins/plugin.c\r\n\t\tplugins/mpq/blast.c\r\n\t\tplugins/mpq/fs_mpq.c\r\n\t)\r\n\tSET_TARGET_PROPERTIES(plug_mpq PROPERTIES COMPILE_DEFINITIONS \"FTEPLUGIN;${FTE_LIB_DEFINES}\")\r\n\tTARGET_LINK_LIBRARIES(plug_mpq ${SYS_LIBS} ${ZLIB_LIBRARIES})\r\n\r\n\tEMBED_PLUGIN_META(irc \"MPQ Archive Plugin\" \"Adds support for reading .mpq files. Not very useful...\")\r\nENDIF()\r\nENDIF()\r\n\r\n#vpk package format plugin (halflife2)\r\nSET(FTE_PLUG_HL2 true CACHE BOOL \"Compile support for hl2 file formats.\")\r\nIF(FTE_PLUG_HL2)\r\n\tADD_LIBRARY(plug_hl2 MODULE\r\n\t\tplugins/plugin.c\r\n\t\tplugins/hl2/hl2.c\r\n\t\tplugins/hl2/fs_vpk.c\r\n\t\tplugins/hl2/fs_vpk_vtmb.c\r\n\t\tplugins/hl2/fs_gma.c\r\n\t\tplugins/hl2/img_tth.c\r\n\t\tplugins/hl2/img_vtf.c\r\n\t\tplugins/hl2/mod_hl2.c\r\n\t\tplugins/hl2/mod_vbsp.c\r\n\t\tplugins/hl2/mat_vmt.c\r\n\t)\r\n\tSET_TARGET_PROPERTIES(plug_hl2 PROPERTIES COMPILE_DEFINITIONS \"FTEPLUGIN;MULTITHREAD;${FTE_LIB_DEFINES}\")\r\n\tTARGET_LINK_LIBRARIES(plug_hl2 ${SYS_LIBS} ${ZLIB_LIBRARIES})\r\n\r\n\tEMBED_PLUGIN_META(hl2 \"HalfLife2 Formats Plugin\" \"Adds support for reading various file formats used by HalfLife2. Requires mod support to be useful.\")\r\nENDIF()\r\n\r\n#model formats plugin\r\nSET(FTE_PLUG_MODELS true CACHE BOOL \"Compile models formats plugin.\")\r\nIF(FTE_PLUG_MODELS)\r\n\tADD_LIBRARY(plug_models MODULE\r\n\t\tplugins/plugin.c\r\n\t\tplugins/models/models.c\r\n\t\tplugins/models/gltf.c\r\n\t\t${DRACO_FILES}\r\n\t\tengine/common/json.c\r\n\t\tplugins/models/exportiqm.c\r\n\t)\r\n\tSET_TARGET_PROPERTIES(plug_models PROPERTIES COMPILE_DEFINITIONS \"FTEPLUGIN;${DRACO_CFLAGS};${FTE_LIB_DEFINES}\")\r\n\tTARGET_LINK_LIBRARIES(plug_models ${SYS_LIBS} ${DRACO_LIBRARY})\r\n\r\n\tEMBED_PLUGIN_META(models \"Models Plugin\" \"Kinda redundant now that the engine has gltf2 loading.\")\r\nENDIF()\r\n\r\nSET(FTE_PLUG_X11SV false CACHE BOOL \"Compile x11 server plugin.\")\r\nIF(FTE_PLUG_X11SV)\r\n\t#x11 server plugin (note: for displaying other programs)\r\n\t#not stable enough, and probably won't ever be\r\n\tADD_LIBRARY(plug_x11sv MODULE\r\n\t\tplugins/plugin.c\r\n\t\tplugins/xsv/m_x.c\r\n\t\tplugins/xsv/x_reqs.c\r\n\t\tplugins/xsv/x_res.c\r\n\t\tengine/qclib/hash.c\r\n\t)\r\n\tSET_TARGET_PROPERTIES(plug_x11sv PROPERTIES COMPILE_DEFINITIONS \"FTEPLUGIN;${FTE_LIB_DEFINES}\")\r\n\tTARGET_LINK_LIBRARIES(plug_x11sv ${SYS_LIBS})\r\n\r\n\tEMBED_PLUGIN_META(x11sv \"X11 Server Plugin\" \"Provides a primitive X11 server in the form of a video decoder plugin.\")\r\nENDIF()\r\n\r\n#ffmpeg client plugin. no proper way to detect dependancies right now, so I've gotta try the manual way.\r\nSET(FTE_PLUG_FFMPEG true CACHE BOOL \"Compile ffmpeg media plugin.\")\r\nIF(FTE_PLUG_FFMPEG)\r\n\tFIND_PATH(AVCODEC_INCLUDE_DIR libavcodec/avcodec.h)\r\n\tFIND_PATH(AVFORMAT_INCLUDE_DIR libavformat/avformat.h)\r\n\tFIND_PATH(AVUTIL_INCLUDE_DIR libavutil/avutil.h)\r\n\tFIND_PATH(AVSWSCALE_INCLUDE_DIR libswscale/swscale.h)\r\n\tIF((AVFORMAT_INCLUDE_DIR) AND (AVSWSCALE_INCLUDE_DIR))\r\n\t\tFIND_LIBRARY(AVCODEC_LIBRARY avcodec)\r\n\t\tFIND_LIBRARY(AVFORMAT_LIBRARY avformat)\r\n\t\tFIND_LIBRARY(AVUTIL_LIBRARY avutil)\r\n\t\tFIND_LIBRARY(AVSWSCALE_LIBRARY swscale)\r\n\r\n\t\tADD_LIBRARY(plug_ffmpeg MODULE\r\n\t\t\tplugins/plugin.c\r\n\t\t\tplugins/avplug/avaudio.c\r\n\t\t\tplugins/avplug/avdecode.c\r\n\t\t\tplugins/avplug/avencode.c\r\n\t\t)\r\n\t\tTARGET_INCLUDE_DIRECTORIES(plug_ffmpeg PUBLIC ${AVCODEC_INCLUDE_DIR} ${AVFORMAT_INCLUDE_DIR} ${AVUTIL_INCLUDE_DIR} ${AVSWSCALE_INCLUDE_DIR})\r\n\t\tTARGET_LINK_LIBRARIES(plug_ffmpeg ${SYS_LIBS} ${AVFORMAT_LIBRARY} ${AVCODEC_LIBRARY} ${AVUTIL_LIBRARY} ${AVSWSCALE_LIBRARY})\r\n\t\tSET_TARGET_PROPERTIES(plug_ffmpeg PROPERTIES COMPILE_DEFINITIONS \"FTEPLUGIN;${FTE_LIB_DEFINES}\")\r\n\r\n\t\tEMBED_PLUGIN_META(ffmpeg \"FFMPEG Video Decoding Plugin\" \"Provides support for more audio formats, as well as video playback and better capture support.\")\r\n\tELSE()\r\n\t\tMESSAGE(WARNING \"ffmpeg library NOT available. Quake shouldn't be playing fmv anyway.\")\r\n\tENDIF()\r\nENDIF()\r\n\r\nSET(FTE_PLUG_TIMIDITY false CACHE BOOL \"Compile timidity audio plugin.\")\r\nIF(FTE_PLUG_TIMIDITY)\r\n\t#timidity\r\n\tFIND_PATH(TIMIDITY_INCLUDE_DIR timidity/timidity.h)\r\n\tIF(TIMIDITY_INCLUDE_DIR)\r\n\t\tFIND_LIBRARY(TIMIDITY_LIBRARY timidity)\r\n\r\n\t\tADD_LIBRARY(plug_timidity MODULE\r\n\t\t\tplugins/plugin.c\r\n\t\t\tplugins/timidity.c\r\n\t\t)\r\n\t\tTARGET_INCLUDE_DIRECTORIES(plug_timidity PUBLIC ${TIMIDITY_INCLUDE_DIR})\r\n\t\tTARGET_LINK_LIBRARIES(plug_timidity ${SYS_LIBS} ${TIMIDITY_LIBRARY})\r\n\t\tSET_TARGET_PROPERTIES(plug_timidity PROPERTIES COMPILE_DEFINITIONS \"FTEPLUGIN;${FTE_LIB_DEFINES}\")\r\n\r\n\t\tEMBED_PLUGIN_META(timidity \"Timidity Plugin\" \"Provides support for playback of midi files.\")\r\n\tELSE()\r\n\t\tMESSAGE(WARNING \"timidity library NOT available. We'll just stick to fake-cd music for hexen2.\")\r\n\tENDIF()\r\nENDIF()\r\n\r\n#openxr plugin\r\nSET(FTE_PLUG_OPENXR true CACHE BOOL \"Compile openxr plugin (for vr support).\")\r\nIF(FTE_PLUG_OPENXR)\r\n\tFIND_PACKAGE(PkgConfig)\r\n\tIF (PKGCONFIG_FOUND)\r\n\t\tIF (NOT CMAKE_CROSSCOMPILING)\t#its picking up the linux headers then complaining that they're missing in mingw. also almost entirely untested so no great loss.\r\n\t\t\tPKG_SEARCH_MODULE(OPENXR openxr)\r\n\t\tENDIF()\r\n\t\tIF (OPENXR_FOUND)\r\n\t\t\tADD_LIBRARY(plug_openxr MODULE\r\n\t\t\t\tplugins/plugin.c\r\n\t\t\t\tplugins/openxr.c\r\n\t\t\t)\r\n\t\t\tTARGET_INCLUDE_DIRECTORIES(plug_openxr PRIVATE ${OPENXR_INCLUDE_DIRS} )\r\n\r\n\t\t\tIF (1)\t#dynamically link\r\n\t\t\t\tSET_TARGET_PROPERTIES(plug_openxr PROPERTIES COMPILE_DEFINITIONS \"FTEPLUGIN;${FTE_LIB_DEFINES};${FTE_DEFINES};XR_NO_PROTOTYPES\")\r\n\t\t\t\tTARGET_LINK_LIBRARIES(plug_openxr ${SYS_LIBS})\r\n\t\t\tELSE()\t#statically link\r\n\t\t\t\tSET_TARGET_PROPERTIES(plug_openxr PROPERTIES COMPILE_DEFINITIONS \"FTEPLUGIN;${FTE_LIB_DEFINES};${FTE_DEFINES}\")\r\n\t\t\t\tTARGET_LINK_LIBRARIES(plug_openxr ${SYS_LIBS} ${OPENXR_LIBRARIES})\r\n\t\t\tENDIF()\r\n\r\n\t\t\tEMBED_PLUGIN_META(openxr \"OpenXR Plugin\" \"Provides support for Virtual Reality headsets and input devices.\")\r\n\t\tELSE()\r\n\t\t\tMESSAGE(WARNING \"openxr library NOT available. Quake is already a reality anyway.\")\r\n\t\tENDIF()\r\n\tENDIF()\r\nENDIF()\r\n\r\n##cef plugin\r\n##libcef itself can be obtained from https://cef-builds.spotifycdn.com/index.html#linux64 (minimal builds, which still ends up with a 1,162,752,744 byte libcef.so - yes, actual size)\r\n##(be sure to manually strip the binary of its debug info)\r\n##to get this cmake stuff to recognise the headers etc:\r\n##\tcd $FTEQW-REPO && ln -s $FOO/cef_binary_$FOO+chromium-$FOO_linux64_minimal plugins/cef/cef_linux64\r\n##(note that other systems use other subdir names)\r\nSET(FTE_PLUG_CEF true CACHE BOOL \"Compile libcef (webbrowser) plugin.\")\r\nSET(CEF_PATH ${CEF_PATH} CACHE PATH \"Base location of libcef for target platform.\")\r\nIF(FTE_PLUG_CEF)\r\n\tIF(CEF_PATH MATCHES \"\")\r\n\t\tUNSET(CEF_PATH CACHE)\r\n\t\tIF(WIN32)\r\n\t\t\tIF(CMAKE_HOST_SYSTEM_PROCESSOR MATCHES \"AMD64\")\r\n\t\t\t\tFIND_PATH (CEF_PATH include/cef_version.h plugins/cef/cef_windows64)\r\n\t\t\tELSEIF(CMAKE_HOST_SYSTEM_PROCESSOR MATCHES \"x86\")\r\n\t\t\t\tFIND_PATH (CEF_PATH include/cef_version.h plugins/cef/cef_windows32)\r\n\t\t\tELSEIF(CMAKE_HOST_SYSTEM_PROCESSOR MATCHES \"ARM64\")\r\n\t\t\t\tFIND_PATH (CEF_PATH include/cef_version.h plugins/cef/cef_windowsarm64)\r\n\t\t\tENDIF()\r\n\t\tELSEIF(\"${CMAKE_SYSTEM}\" MATCHES \"Linux\")\r\n\t\t\tIF(CMAKE_HOST_SYSTEM_PROCESSOR MATCHES \"x86_64\")\r\n\t\t\t\tFIND_PATH (CEF_PATH include/cef_version.h plugins/cef/cef_linux64)\r\n\t\t\tELSEIF(CMAKE_HOST_SYSTEM_PROCESSOR MATCHES \"i686\")\r\n\t\t\t\tFIND_PATH (CEF_PATH include/cef_version.h plugins/cef/cef_linux32)\r\n\t\t\tENDIF()\r\n\t\tENDIF()\r\n\tENDIF()\r\n\t#FIND_LIBRARY(CEF_LIBRARIES cef ${CEF_PATH}/Release)\r\n\tIF (CEF_PATH)\r\n\t\t##statically link only for release builds. debug builds probably don't want to have to wait for ages for the debugger to finish loading debug info unless we're actually using this stuff.\r\n\t\tIF(CMAKE_BUILD_TYPE MATCHES \"Release\")\r\n\t\t\tSET(CEF_LIBRARIES \"${CMAKE_BINARY_DIR}/libcef.so\")\r\n\t\tENDIF()\r\n\t\tADD_LIBRARY(plug_cef MODULE\r\n\t\t\tplugins/plugin.c\r\n\t\t\tplugins/cef/cef.c\r\n\t\t)\r\n\t\tTARGET_INCLUDE_DIRECTORIES(plug_cef PRIVATE ${CEF_PATH})\r\n\t\tSET_TARGET_PROPERTIES(plug_cef PROPERTIES BUILD_RPATH_USE_ORIGIN true)\r\n\t\tif (CEF_LIBRARIES)\r\n\t\t\tSET_TARGET_PROPERTIES(plug_cef PROPERTIES COMPILE_DEFINITIONS \"FTEPLUGIN;${FTE_LIB_DEFINES};${FTE_DEFINES};LIBCEF_STATIC\")\r\n\t\t\tTARGET_LINK_LIBRARIES(plug_cef ${SYS_LIBS} ${CEF_LIBRARIES} ${CMAKE_DL_LIBS})\r\n\t\tELSE()\r\n\t\t\tSET_TARGET_PROPERTIES(plug_cef PROPERTIES COMPILE_DEFINITIONS \"FTEPLUGIN;${FTE_LIB_DEFINES};${FTE_DEFINES};LIBCEF_DYNAMIC\")\r\n\t\t\tTARGET_LINK_LIBRARIES(plug_cef ${SYS_LIBS} ${CMAKE_DL_LIBS})\r\n\t\tENDIF()\r\n\r\n\t\tIF(NOT ${UNIX})\r\n\t\t\tADD_CUSTOM_COMMAND(\r\n\t\t\t\tTARGET plug_cef PRE_LINK\r\n\t\t\t\tCOMMAND cp ${CEF_PATH}/Release/* ${CMAKE_BINARY_DIR}\r\n\t\t\t\tCOMMAND cp ${CEF_PATH}/Resources/* ${CMAKE_BINARY_DIR}\r\n\t\t\t\t)\r\n\t\tELSE()\r\n\t\t\tIF(CMAKE_BUILD_TYPE MATCHES \"Release\")\r\n\t\t\t\t#sigh, cef ain't stripped properly on linux.\r\n\t\t\t\tADD_CUSTOM_COMMAND(\r\n\t\t\t\t\tTARGET plug_cef PRE_LINK\r\n\t\t\t\t\tCOMMAND ln -f -s ${CEF_PATH}/Release/* ${CMAKE_BINARY_DIR}\r\n\t\t\t\t\tCOMMAND strip ${CMAKE_BINARY_DIR}/libcef.so -o libcef.so\r\n\t\t\t\t\tCOMMAND strip ${CMAKE_BINARY_DIR}/libEGL.so -o libEGL.so\r\n\t\t\t\t\tCOMMAND strip ${CMAKE_BINARY_DIR}/libGLESv2.so -o libGLESv2.so\r\n\t\t\t\t\tCOMMAND strip ${CMAKE_BINARY_DIR}/chrome-sandbox -o chrome-sandbox\r\n\t\t\t\t\tCOMMAND ln -f -s ${CEF_PATH}/Resources/* ${CMAKE_BINARY_DIR}\r\n\t\t\t\t\t)\r\n\t\t\tELSE()\r\n\t\t\t\tADD_CUSTOM_COMMAND(\r\n\t\t\t\t\tTARGET plug_cef PRE_LINK\r\n\t\t\t\t\tCOMMAND ln -f -s ${CEF_PATH}/Release/* ${CMAKE_BINARY_DIR}\r\n\t\t\t\t\tCOMMAND ln -f -s ${CEF_PATH}/Resources/* ${CMAKE_BINARY_DIR}\r\n\t\t\t\t\t)\r\n\t\t\tENDIF()\r\n\t\tENDIF()\r\n\r\n\t\tEMBED_PLUGIN_META(cef \"libcef(Browser) Plugin\" \"This plugin provides support for an in-game web browser.\")\r\n\tELSE()\r\n\t\tMESSAGE(WARNING \"libcef library NOT available. no web browser support on walls.\")\r\n\tENDIF()\r\nENDIF()\r\n\r\nSET(FTE_PLUG_XMPP true CACHE BOOL \"Compile xmpp/jabber instant-messenger plugin.\")\r\nIF(FTE_PLUG_XMPP)\r\n\t#XMPP/jabber client plugin\r\n\tADD_LIBRARY(plug_xmpp MODULE\r\n\t\tplugins/plugin.c\r\n\t\tplugins/jabber/jabberclient.c\r\n\t\tplugins/jabber/xml.c\r\n\t\tplugins/jabber/jingle.c\r\n\t\tplugins/jabber/sift.c\r\n\t\tengine/common/sha1.c\r\n\t\tengine/common/sha2.c\r\n\t\tplugins/emailnot/md5.c\r\n\t)\r\n\tSET_TARGET_PROPERTIES(plug_xmpp PROPERTIES COMPILE_DEFINITIONS \"FTEPLUGIN;${FTE_LIB_DEFINES}\")\r\n\r\n\t#for SRV lookups, so we actually get the right server from account names/etc.\r\n\tIF(ANDROID)\t#libresolv issues.\r\n\tELSEIF(${WIN32}) #softlinks a dll\r\n\tELSE()\r\n\t\tTARGET_LINK_LIBRARIES(plug_xmpp ${SYS_LIBS} resolv)\r\n\tENDIF()\r\n\r\n\tEMBED_PLUGIN_META(xmpp \"XMPP Plugin\" \"XMPP/Jabber instant messenger plugin for chatting without tabbing out.\")\r\nENDIF()\r\n\r\nINSTALL(TARGETS ${INSTALLTARGS}\r\n\tRUNTIME DESTINATION \"${CMAKE_INSTALL_PREFIX}/${FTE_INSTALL_BINDIR}\"\r\n\tLIBRARY DESTINATION \"${CMAKE_INSTALL_FULL_LIBDIR}/${FTE_INSTALL_LIBDIR}\"\r\n)\r\n\r\nIF(UNIX AND NOT APPLE)\r\n\tINSTALL(FILES\r\n\t\t${CMAKE_SOURCE_DIR}/dist/linux/org.fteqw.fteqw.desktop\r\n\t\tDESTINATION \"${CMAKE_INSTALL_DATAROOTDIR}/applications/\")\r\n\tINSTALL(FILES\r\n\t\t${CMAKE_SOURCE_DIR}/dist/org.fteqw.fteqw.svg\r\n\t\tDESTINATION \"${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/scalable/apps/\")\r\n\tINSTALL(FILES\r\n\t\t${CMAKE_SOURCE_DIR}/dist/linux/org.fteqw.fteqw.metainfo.xml\r\n\t\tDESTINATION \"${CMAKE_INSTALL_DATAROOTDIR}/metainfo/\")\r\n\tINSTALL(DIRECTORY ${CMAKE_SOURCE_DIR}/games\r\n\t\tDESTINATION \"${CMAKE_INSTALL_PREFIX}/etc/xdg/fteqw\"\r\n\t\tFILES_MATCHING PATTERN \"*.fmf\")\r\nENDIF()\r\n\r\nSET(FTE_MENU_SYS true CACHE BOOL \"Compile System Menu.\")\r\nIF(FTE_MENU_SYS)\r\n\tADD_CUSTOM_TARGET(menusys ALL\r\n\t\tVERBATIM\r\n\t\tDEPENDS fteqcc\r\n\t\tWORKING_DIRECTORY \"${CMAKE_CURRENT_SOURCE_DIR}/quakec/menusys/\"\r\n\t\tCOMMAND fteqcc -srcfile \"menu.src\" -o \"${CMAKE_CURRENT_BINARY_DIR}/menu.dat\" -DREVISION=\"${SVNREVISION}\" -DDATE=\"${FTE_DATE}\" -DBRANCH=\"${FTE_BRANCH}\"\r\n\t\tCOMMAND /bin/echo -e \"{\\\\n\tpackage fte_menusys\\\\n\tver \\\"${SVNREVISION}\\\"\\\\n\tcategory Plugins\\\\n\ttitle \\\"Replacement Menus\\\"\\\\n\tgamedir \\\"id1\\\"\\\\n\tdesc \\\"Modern menus to replace the ancient quake ones\\\"\\\\n}\" | zip -j -q -9 -fz- \"${CMAKE_CURRENT_BINARY_DIR}/menusys.pk3\" - \"${CMAKE_CURRENT_BINARY_DIR}/menu.dat\"\r\n\t\tBYPRODUCTS \"${CMAKE_CURRENT_BINARY_DIR}/menu.dat\" \"${CMAKE_CURRENT_BINARY_DIR}/menu.lno\" \"${CMAKE_CURRENT_BINARY_DIR}/menusys.pk3\"\r\n\t\tSOURCES\r\n\t\t\tquakec/menusys/menu.src\r\n\t\t\tquakec/menusys/fteextensions.qc\r\n\t\t\tquakec/menusys/menusys/mitems.qc\r\n\t\t\tquakec/menusys/menusys/mitems_common.qc\r\n\t\t\tquakec/menusys/menusys/mitem_frame.qc\r\n\t\t\tquakec/menusys/menusys/mitem_desktop.qc\r\n\t\t\tquakec/menusys/menusys/mitem_exmenu.qc\r\n\t\t\tquakec/menusys/menusys/mitem_edittext.qc\r\n\t\t\tquakec/menusys/menusys/mitem_tabs.qc\r\n\t\t\tquakec/menusys/menusys/mitem_colours.qc\r\n\t\t\tquakec/menusys/menusys/mitem_checkbox.qc\r\n\t\t\tquakec/menusys/menusys/mitem_slider.qc\r\n\t\t\tquakec/menusys/menusys/mitem_combo.qc\r\n\t\t\tquakec/menusys/menusys/mitem_bind.qc\r\n\t\t\tquakec/menusys/menusys/mitem_spinnymodel.qc\r\n\t\t\tquakec/menusys/menu/loadsave.qc\r\n\t\t\tquakec/menusys/menu/newgame.qc\r\n\t\t\tquakec/menusys/menu/options_basic.qc\r\n\t\t\tquakec/menusys/menu/options_effects.qc\r\n\t\t\tquakec/menusys/menu/options_keys.qc\r\n\t\t\tquakec/menusys/menu/options.qc\r\n\t\t\tquakec/menusys/menu/presets.qc\r\n\t\t\tquakec/menusys/menu/servers.qc\r\n\t\t\tquakec/menusys/menu/main.qc\r\n\t\t\tquakec/menusys/menu/mods.qc\r\n\t\t\tquakec/menusys/menu/cvars.qc\r\n\t\t\tquakec/menusys/menu/updates.qc\r\n\t\t\tquakec/menusys/menu/options_audio.qc\r\n\t\t\tquakec/menusys/menu/options_configs.qc\r\n\t\t\tquakec/menusys/menu/options_hud.qc\r\n\t\t\tquakec/menusys/menu/options_particles.qc\r\n\t\t\tquakec/menusys/menu/options_video.qc\r\n\t\t\tquakec/menusys/menu/quit.qc\r\n\t)\r\n\r\n\tINSTALL(FILES\r\n\t\t${CMAKE_CURRENT_BINARY_DIR}/menusys.pk3\r\n\t\tDESTINATION \"${CMAKE_INSTALL_DATAROOTDIR}/games/quake/id1/\")\r\nENDIF()\r\n\r\nSET(FTE_CSADDON true CACHE BOOL \"CS Addon.\")\r\nIF(FTE_CSADDON)\r\n\tADD_CUSTOM_TARGET(csaddon ALL\r\n\t\tVERBATIM\r\n\t\tDEPENDS fteqcc\r\n\t\tWORKING_DIRECTORY \"${CMAKE_CURRENT_SOURCE_DIR}/quakec/csaddon/src/\"\r\n\t\tCOMMAND fteqcc -srcfile \"csaddon.src\" -o \"${CMAKE_CURRENT_BINARY_DIR}/csaddon.dat\"\r\n\t\tCOMMAND /bin/echo -e \"{\\\\n\tpackage fte_csaddon\\\\n\tver \\\"${SVNREVISION}\\\"\\\\n\tcategory Plugins\\\\n\ttitle \\\"${PLUGTITLE}\\\"\\\\n\tgamedir \\\"id1\\\"\\\\n\tdesc \\\"${PLUGDESC}\\\"\\\\n}\" | zip -j -q -9 -fz- \"${CMAKE_CURRENT_BINARY_DIR}/csaddon.pk3\" - \"${CMAKE_CURRENT_BINARY_DIR}/csaddon.dat\"\r\n\t\tBYPRODUCTS \"${CMAKE_CURRENT_BINARY_DIR}/csaddon.dat\" \"${CMAKE_CURRENT_BINARY_DIR}/csaddon.lno\" \"${CMAKE_CURRENT_BINARY_DIR}/csaddon.pk3\"\r\n\t\tSOURCES\r\n\t\t\tquakec/csaddon/src/csaddon.src\r\n\r\n\t\t\tquakec/csaddon/src/csplat.qc\r\n\t\t\tquakec/csaddon/src/csfixups.qc\r\n\r\n\t\t\tquakec/csaddon/src/editor_lights.qc\r\n\t\t\tquakec/csaddon/src/editor_terrain.qc\r\n\t\t\tquakec/csaddon/src/brush_selection.qc\r\n\t\t\tquakec/csaddon/src/brush_history.qc\r\n\t\t\tquakec/csaddon/src/brush_manip.qc\r\n\t\t\tquakec/csaddon/src/brush_draw.qc\r\n\t\t\tquakec/csaddon/src/brush_vertedit.qc\r\n\t\t\tquakec/csaddon/src/editor_brushes.qc\r\n\t\t\tquakec/csaddon/src/editor_ents.qc\r\n\t\t\tquakec/csaddon/src/textfield.qc\r\n\t\t\tquakec/csaddon/src/editor_particles.qc\r\n\t\t\tquakec/csaddon/src/menu.qc\r\n\t\t\tquakec/csaddon/src/cam.qc\r\n\t\t\tquakec/csaddon/src/csaddon.qc\r\n\t)\r\n\r\n\tINSTALL(FILES\r\n\t\t${CMAKE_CURRENT_BINARY_DIR}/csaddon.pk3\r\n\t\tDESTINATION \"${CMAKE_INSTALL_DATAROOTDIR}/games/quake/id1/\")\r\nENDIF()\r\n"
  },
  {
    "path": "LICENSE",
    "content": "GNU GENERAL PUBLIC LICENSE\n\nVersion 2, June 1991\n\nCopyright (C) 1989, 1991 Free Software Foundation, Inc.\n<https://fsf.org/>\nEveryone is permitted to copy and distribute verbatim copies\nof this license document, but changing it is not allowed.\n\nPreamble\n\nThe licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too.\n\nWhen we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things.\n\nTo protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it.\n\nFor example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights.\n\nWe protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software.\n\nAlso, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations.\n\nFinally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all.\n\nThe precise terms and conditions for copying, distribution and modification follow.\nTERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION\n\n0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The \"Program\", below, refers to any such program or work, and a \"work based on the Program\" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term \"modification\".) Each licensee is addressed as \"you\".\n\nActivities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does.\n\n1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program.\n\nYou may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee.\n\n2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions:\n\n    a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change.\n    b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License.\n    c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this 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.)\n\nThese 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 themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it.\n\nThus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program.\n\nIn addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License.\n\n3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following:\n\n    a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or,\n    b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or,\n    c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.)\n\nThe source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable.\n\nIf distribution of executable or object code is made by offering 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.\n\n4. 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 void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance.\n\n5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it.\n\n6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License.\n\n7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program.\n\nIf any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances.\n\nIt is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice.\n\nThis section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License.\n\n8. 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 may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License.\n\n9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns.\n\nEach version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and \"any later version\", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation.\n\n10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally.\n\nNO WARRANTY\n\n11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.\n\n12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.\nEND OF TERMS AND CONDITIONS\nHow to Apply These Terms to Your New Programs\n\nIf you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms.\n\nTo do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the \"copyright\" line and a pointer to where the full notice is found.\n\none line to give the program's name and an idea of what it does.\nCopyright (C) yyyy  name of author\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, see\n<https://www.gnu.org/licenses/>.\n\nAlso add information on how to contact you by electronic and paper mail.\n\nIf the program is interactive, make it output a short notice like this when it starts in an interactive mode:\n\nGnomovision version 69, Copyright (C) year name of author\nGnomovision comes with ABSOLUTELY NO WARRANTY; for details\ntype `show w'.  This is free software, and you are welcome\nto redistribute it under certain conditions; type `show c'\nfor details.\n\nThe hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program.\n\nYou should also get your employer (if you work as a programmer) or your school, if any, to sign a \"copyright disclaimer\" for the program, if necessary. Here is a sample; alter the names:\n\nYoyodyne, Inc., hereby disclaims all copyright\ninterest in the program `Gnomovision'\n(which makes passes at compilers) written\nby James Hacker.\n\nsignature of Moe Ghoul, 1 April 1989\nMoe Ghoul, President of Vice\n\nThis General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License.\n"
  },
  {
    "path": "README.md",
    "content": "# [FTEQW](https://fteqw.org)\n\n![FTEQW Logo](engine/client/fte_eukara.ico)\n\nPowerful engine for playing and modding idTech based games.\n\n# What is FTEQW?\n\nFTEQW is an advanced and portable Quake engine. It supports multiple games running on idTech, plus its own set of games that developers have created.\n\nDue to the vast amount of supported formats, features, and innovations inside the engine and its very own QuakeC compiler (FTEQCC), it's very much considered the swiss-army knife of Quake engines.\n\n### Highlights:\n\n- Single & multi-player support\n- Supports multiple games\n- Vast amount of map, model, & image formats are supported\n- Advanced console, with descriptions & autocompletion\n- Plugin support, enabling use of FFMPEG, Bullet/ODE physics & more\n- Extensive suite of QuakeC/entity debugging features\n- Deep integration with FTEQCC (fork of QuakeC created for FTEQW), which can even be executed in-game\n- Support for split-screen local multiplayer\n- Voice-chat via Opus & Speex\n- Support for hundreds of players on a single server\n- Works on Windows, Linux, OpenBSD... & more\n- New features are added all the time in cooperation with modders\n\n# Contributions\n\nContributions and help is always welcomed.\n\n### Guidelines:\n\n- Be kind and respectful\n- GPL2 licensed contributions are preferred, but plugins can be different but GPL-compatbile licenses\n- This codebase follows USA/EU/UK copyright laws\n- Always give credit from other codebases and make sure licenses are compatible\n- Test your changes and ensure nothing else has been broken (games, plugins, formats, etc)\n\n# Reporting Issues\n\nBug reports are welcomed! :)\n\n### Required Information:\n\n- Your system information such as your **Operating System** and **Hardware** (GPU/CPU)\n- If the binary is pre-built (e.g. from fteqw.org) or if it was built manually\n- What version of FTEQW you're using (type `version` in console)\n- If it is a supported game/mod/plugin/etc you're having issues with, then provide the version info for it, and tell us how it should be behaving\n- Make sure you have read the included documentation and ensure you have done everything right\n- Remember to double check the problem hasn't already been reported\n- Screenshots and/or video are generally desired if it is a visual malfunction\n\n**Windows Users**\n\nPlease make sure you have not renamed your executable, `fteqw.exe`, to be `winquake.exe` or `glquake.exe` as Windows attempts compatability fixes that are not required for FTEQW and will cause problems.\n\n# Documentation\n\nPlease see the `documentation` folder inside the repo for building, using the engine, tools, and more.\n\nThe `specs` folder is for more advanced users seeking QuakeC and idTech file format related information or examples.\n\n# Contact\n\n### Matrix\n\nhttps://matrix.to/#/#fte:matrix.org\n\n### IRC\n\n**Server:** irc.quakenet.org\n\n**Channel:** #fte\n\n### Forums\n\n**[Spike](https://forums.insideqc.com/memberlist.php?mode=viewprofile&u=26)** and **[eukara](https://forums.insideqc.com/memberlist.php?mode=viewprofile&u=949)** can be found on [insideqc.com](https://forums.insideqc.com/)\n\n### Discord\n\nhttps://discord.gg/p2ag7x6Ca6\n\n# Credits\n\nPlease see the `Credits.md` file.\n\n# License\n\nCopyright (c) 2004-2025 FTE's team and its contributors\nQuake source (c) 1999 id Software\n\nFTEQW is supplied to you under the terms of the same license as the\noriginal Quake sources, the GNU General Public License Version 2.\nPlease read the `LICENSE` file for details.\n\n# Download\n\nThe latest source & binaries are always available at:\n\n[fteqw.org](https://fteqw.org)\n\n[fteqcc.org](https://fteqcc.org)\n"
  },
  {
    "path": "build_qc.sh",
    "content": "#!/bin/bash\n#FTEQCC and FTEQW must be defined.\n#QSS should be defined...\n\nBUILDFOLDER=~/htdocs\nBUILDLOGFOLDER=$BUILDFOLDER/build_logs\n\n\nif [ \"$FTEQCC\" == \"\" ]; then\n\tQSS=~/htdocs/qss/quakespasm-spiked-linux64\n\tFTEQW=~/htdocs/linux_amd64/fteqw64\n\tFTEQCC=~/htdocs/linux_amd64/fteqcc64\nfi\n\n#this really should use the native cpu type... until then we use 32bit in case anyone's still using a 32bit kernel.\nif [ \"$FTEQW\" != \"\" ]; then\n    echo \"--- QC builds ---\"\n\techo \"Making fteextensions.qc\"\n\n\tBASEDIR=~/.fte\n\tGAMEDIR=fte\n\tmkdir -p $BASEDIR/$GAMEDIR/src\n\n\t#generate fte's extensions\n\tCFG=$BASEDIR/fte/minusargsaresilly.cfg\n\techo \"pr_dumpplatform -o fteextensions\" > $CFG\n\techo \"pr_dumpplatform -o csqcsysdefs -Tcs\" >> $CFG\n\techo \"pr_dumpplatform -o menusysdefs -Tmenu\" >> $CFG\n\t$FTEQW -basedir $BASEDIR -nohome -quake -game $GAMEDIR +set snd_device none -nosound +set vid_renderer sv +exec minusargsaresilly.cfg +quit >> /dev/null\n\n\t#if we have fteqcc available then try to generate some symbol lists for our generic defs\n\tif [ \"$FTEQCC\" != \"\" ]; then\n\t\t#get QSS to spit out its defs for completeness.\n\t\tif [ \"$QSS\" != \"\" ] && [ -e \"$BASEDIR/id1/pak1.pak\" ] && [ -e \"$QSS\" ]; then\n\t\t\tCFG=$BASEDIR/$GAMEDIR/gen.cfg\n\t\t\techo \"pr_dumpplatform -Oqscsextensions -Tcs\" > $CFG\n\t\t\techo \"pr_dumpplatform -Oqsextensions -Tss\" >> $CFG\n\t\t\techo \"pr_dumpplatform -Oqsmenuextensions -Tmenu\" >> $CFG\n\t\t\t$QSS -dedicated -nohome -game $GAMEDIR -basedir $BASEDIR +exec gen.cfg +quit >>/dev/null\n\t\telse\n\t\t\techo \"QSS not available?\"\n\t\tfi\n\t\t\n\t\tif [ ! -e \"$BASEDIR/fte/src/dpsymbols.src\" ]; then\n\t\t\tln -sr quakec/dpsymbols.src $BASEDIR/$GAMEDIR/src/\n\t\tfi\n\t\tif [ ! -e \"$BASEDIR/fte/src/dpdefs/\" ]; then\n\t\t\techo \"no dpdefs subdir found in $BASEDIR/fte/src/ ... manual intervention required\"\n\t\tfi\n\n\t\t#generate symbol tables from the various engines's defs\n\t\t(\tcd $BASEDIR/$GAMEDIR/src\n\t\t\t$FTEQCC -Fdumpsymbols -oqsscs.dat        qscsextensions.qc   && sort -o qss_cs.sym   qsscs.dat.sym\n\t\t\t$FTEQCC -Fdumpsymbols -oqssss.dat        qsextensions.qc     && sort -o qss_ss.sym   qssss.dat.sym\n\t\t\t$FTEQCC -Fdumpsymbols -oqssmn.dat        qsmenuextensions.qc && sort -o qss_menu.sym qssmn.dat.sym\n\n\t\t\t$FTEQCC -Fdumpsymbols -oftecs.dat -DCSQC fteextensions.qc    && sort -o fte_cs.sym   ftecs.dat.sym\n\t\t\t$FTEQCC -Fdumpsymbols -oftess.dat -DSSQC fteextensions.qc    && sort -o fte_ss.sym   ftess.dat.sym\n\t\t\t$FTEQCC -Fdumpsymbols -oftemn.dat -DMENU fteextensions.qc    && sort -o fte_menu.sym ftemn.dat.sym\n\n\t\t\t$FTEQCC -Fdumpsymbols -odpcs.dat  -DCSQC dpsymbols.src       && sort -o dp_cs.sym    dpcs.dat.sym\n\t\t\t$FTEQCC -Fdumpsymbols -odpss.dat  -DSSQC dpsymbols.src       && sort -o dp_ss.sym    dpss.dat.sym\n\t\t\t$FTEQCC -Fdumpsymbols -odpmn.dat  -DMENU dpsymbols.src       && sort -o dp_menu.sym  dpmn.dat.sym\n\t\t) >>/dev/null\n\n\t\t#generate generic extensions\n\t\tCFG=$BASEDIR/fte/minusargsaresilly.cfg\n\t\techo \"pr_dumpplatform     -Ocsqc_api -Fdepfilter -Tsimplecs\" > $CFG\n\t\techo \"pr_dumpplatform -Ofte_csqc_api -Fdepfilter -Tcs\" >> $CFG\n\t\techo \"pr_dumpplatform  -Odp_csqc_api -Fdepfilter -Tdpcs\" >> $CFG\n\t\techo \"pr_dumpplatform     -Ossqc_api -Fdepfilter -Tnq\" >> $CFG\n\t\techo \"pr_dumpplatform     -Omenu_api -Fdepfilter -Tmenu\" >> $CFG\n\t\t$FTEQW -basedir $BASEDIR -nohome -quake -game $GAMEDIR +set snd_device none -nosound +set vid_renderer sv +exec minusargsaresilly.cfg +quit >> /dev/null\n\tfi\n\n\t#fix up and copy the results somewhere useful\n\trm $CFG\n\n\tmkdir -p $BUILDFOLDER/ftedefs $BUILDFOLDER/genericdefs\n\tmv $BASEDIR/$GAMEDIR/src/fteextensions.qc $BUILDFOLDER/ftedefs\n\tmv $BASEDIR/$GAMEDIR/src/csqcsysdefs.qc $BUILDFOLDER/ftedefs\n\tmv $BASEDIR/$GAMEDIR/src/menusysdefs.qc $BUILDFOLDER/ftedefs\n\tmv $BASEDIR/$GAMEDIR/src/*_api.qc $BUILDFOLDER/genericdefs\nfi\n\n\nif [ \"$FTEQCC\" != \"\" ]; then\n\tmkdir -p $BUILDFOLDER/csaddon/\n\n\t(\tcd quakec/csaddon/src\n\t\techo -n \"Making csaddon... \"\n\t\t$FTEQCC -srcfile csaddon.src > $BUILDLOGFOLDER/csaddon.txt 2>&1\n\t\tif [ $? -eq 0 ]; then\n\t\t\techo \"done\"\n\t\t\tcp ../csaddon.dat $BUILDFOLDER/csaddon/\n\t\t\tcd ..\n\t\t\tzip -q9 $BUILDFOLDER/csaddon/csaddon.pk3 csaddon.dat\n\t\telse\n\t\t\techo \"failed\"\n\t\tfi\n\t)\n\n\t(\tcd quakec/menusys\n\t\techo -n \"Making menusys... \"\n\t\t$FTEQCC -srcfile menu.src > $BUILDLOGFOLDER/menu.txt 2>&1\n\t\tif [ $? -eq 0 ]; then\n\t\t\techo \"done\"\n\t\t\tzip -q -q9 -o -r $BUILDFOLDER/csaddon/menusys_src.zip .\n\t\t\tcp ../menu.dat $BUILDFOLDER/csaddon/\n\t\t\tcd ..\n\t\t\tzip -q9 $BUILDFOLDER/csaddon/menusys.pk3 menu.dat\n\t\telse\n\t\t\techo \"failed\"\n\t\tfi\n\t)\nelse\n\techo \"Skiping csaddon + qcmenu, no compiler build\"\nfi\n\n"
  },
  {
    "path": "build_setup.sh",
    "content": "#!/bin/bash\n#sets up dependancies for debian-jessie (8.7)\n#this script must be run twice. first time as root, which installs system packages\n#second time as a regular user (probably not your normal one), which installs 3rd-party stuff\n\nSVNROOT=$(cd \"$(dirname \"$BASH_SOURCE\")\" && pwd)\nFTEROOT=$(realpath $SVNROOT/..)\nFTEROOT=${FTEROOT:-~}\nFTECONFIG=$SVNROOT/build.cfg\n\nBUILDFOLDER=`echo ~`/htdocs\nBUILDLOGFOLDER=$BUILDFOLDER/build_logs\n\n#mac defaults\nOSXCROSSROOT=$FTEROOT/osxcross\n\n#emscripten defaults\nEMSCRIPTENROOT=$FTEROOT/emsdk-portable\n\n#android defaults\nANDROIDROOT=$FTEROOT/android\nif [ ! -z \"$(uname -o 2>&1 | grep Cygwin)\" ]; then\n\tANDROID_HOSTSYSTEM=windows-x86_64\nelse\n\tANDROID_HOSTSYSTEM=linux-$(uname -m)\nfi\nANDROIDBUILDTOOLS=25.0.0\nANDROID_ZIPALIGN=$ANDROIDROOT/build-tools/$ANDROIDBUILDTOOLS/zipalign\t#relative to ndk tools\n\nTHREADS=\"-j 4\"\n\nTARGETS_LINUX=\"qcc-rel rel dbg plugins-rel plugins-dbg\" #gl-rel vk-rel \nTARGETS_WINDOWS=\"sv-rel m-rel qcc-rel qccgui-scintilla qccgui-dbg m-dbg sv-dbg plugins-dbg plugins-rel\" #gl-rel vk-rel mingl-rel d3d-rel \n\n\nPLUGINS_DROID=\"qi ezhud irc hl2\"\nPLUGINS_LINUXx86=\"openxr ode qi ezhud xmpp irc hl2\"\nPLUGINS_LINUXx64=\"openxr ode qi ezhud xmpp irc hl2\"\nPLUGINS_LINUXx32=\"qi ezhud xmpp irc hl2\"\nPLUGINS_LINUXarmhf=\"qi ezhud xmpp irc hl2\"\nPLUGINS_LINUXaarch64=\"qi ezhud xmpp irc hl2\"\nif [ \"$(uname -m)\" != \"x86_64\" ]; then\n\tPLUGINS_LINUXx86=\"openxr ode qi ezhud xmpp irc hl2\"\nfi\nif [ \"$(uname -m)\" == \"x86_64\" ]; then\n\tPLUGINS_LINUX64=\"openxr ode qi ezhud xmpp irc hl2\"\nfi\n#windows is always cross compiled, so we don't have issues with non-native ffmpeg\n#windows doesn't cross compile, so no system dependancy issues\n#skip some dependancies if we're running on cygwin, ode is buggy.\nif [ \"$(uname -s)\" == \"Linux\" ]; then\n\tPLUGINS_WIN32=\"ode qi ezhud xmpp irc hl2\"\n\tPLUGINS_WIN64=\"ode qi ezhud xmpp irc hl2\"\nelse\n\tPLUGINS_WIN32=\"qi ezhud xmpp irc hl2\"\n\tPLUGINS_WIN64=\"qi ezhud xmpp irc hl2\"\nfi\n\necho\necho \"This is Spike's script to set up various cross compilers and dependancies.\"\necho \"This script will check dependancies. If something isn't installed you can either rerun the script as root (which will ONLY install system packages), or manually apt-get or whatever. You can then re-run the script as a regular user to finish configuring 3rd party dependancies.\"\necho\necho \"You can change your choices later by just re-running this script\"\necho \"(Your settings will be autosaved in $FTECONFIG)\"\necho\necho \"If you just want to compile a native build, just use the following command:\"\necho \"cd $SVNROOT/engine && make gl-rel\"\necho \"(if you're in cygwin, add FTE_TARGET=win32 to compile for native windows)\"\necho \"(add plugins-rel qcc-rel qccgui-rel sv-rel vk-rel etc for additional targets)\"\necho \"(or use -dbg if you want debug builds for whatever reason)\"\necho\n\n#always execute it if it exists, so that we preserve custom paths etc that are not prompted for here\nif [ -e $FTECONFIG ]; then\n\t. $FTECONFIG\n\n\tif [ $UID -eq 0 ]; then\n\t\tREUSE_CONFIG=\"y\"\t#root shouldn't be writing/owning the config file.\n\telse\n\t\tREUSE_CONFIG=\"u\"\n\tfi\nelse\n\tif [ $UID -eq 0 ]; then\n\t\texit\t#root can't create the output, as that would take ownership.\n\telse\n\t\tREUSE_CONFIG=\"n\"\n\tfi\nfi\n\nif [ \"$BUILD_CLEAN\" == \"n\" ]; then\n\tNOUPDATE=\"y\"\nfi\n\n#check args (and override config as desired)\nwhile [[ $# -gt 0 ]]\ndo\n\tcase $1 in\n\t-r)\t\t\t\t#for people that want to build a specific revision for some reason.\n\t\tSVN_REV_ARG=\"-r $2\"\n\t\tNOUPDATE=\n\t\tshift\n\t\t;;\n\t-j)\n\t\tTHREADS=\"-j $2\"\n\t\tshift\n\t\t;;\n\t-help|--help)\n\t\techo \"  -r VER       Specifies the SVN revision to update to\"\n\t\techo \"  -j THREADS   Specifies how many jobs to make with\"\n\t\techo \"  --help       This text\"\n\t\texit 0\n\t\t;;\n\t-build|--build)\t#for custom build settings\n\t\tTARGET=\"FTE_CONFIG=$2\"\n\t\tshift\n\t\t;;\n\t--fast)\t\t\t#for people that want to live dangerously.\n\t\tBUILD_CLEAN=\"n\"\n\t\t;;\n\t--noupdate)\t\t#for people living privately or building old revisions...\n\t\tNOUPDATE=\"y\"\n\t\t;;\n\t--unattended)\t#don't prompt, use various defaults.\n\t\tUNATTENDED=\"y\"\n\t\tREUSE_CONFIG=\"y\"\n\t\t;;\n\t*)\n\t\techo \"Unknown option $1\"\n\t\t;;\n\tesac\n\tshift\ndone\n\nif [ \"$REUSE_CONFIG\" == \"u\" ]; then\n\tread -n 1 -p \"Reuse previous build config? [y/N] \" REUSE_CONFIG && echo\n\tREUSE_CONFIG=${REUSE_CONFIG:-n}\nfi\n\nif [ \"$REUSE_CONFIG\" != \"y\" ]; then\n\t#linux compiles are native-only, so don't bug out on cygwin which lacks a cross compiler.\n\tBUILD_LINUXx86=n\n\tBUILD_LINUXx64=n\n\tBUILD_LINUXx32=n\n\tBUILD_LINUXarmhf=n\n\tif [ \"$(uname -s)\" == \"Linux\" ]; then\n\t\tread -n 1 -p \"Build for Linux x86? [Y/n] \" BUILD_LINUXx86 && echo\n\t\tread -n 1 -p \"Build for Linux x86_64? [Y/n] \" BUILD_LINUXx64 && echo\n\t\tread -n 1 -p \"Build for Linux x32? [y/N] \" BUILD_LINUXx32 && echo\n\t\t#ubuntu's gcc-multilib-arm-foo package conflicts with gcc-multilib...\n\t\t#the whole point of multilib was to avoid conflicts... someone fucked up.\n\t\t#read -n 1 -p \"Build for Linux armhf [y/N] \" BUILD_LINUXarmhf && echo\n\telse\n\t\techo \"Skipping Linux options.\"\n\tfi\n\tBUILD_CYGWIN=n\n\tBUILD_MSVC=n\n\tif [ \"$(uname -o)\" == \"Cygwin\" ]; then\n\t\tread -n 1 -p \"Build for Cygwin? [y/N] \" BUILD_CYGWIN && echo\n\t\tread -n 1 -p \"Build with MSVC? (requires windows7 sdk) [y/N] \" BUILD_MSVC && echo\n\telse\n\t\techo \"Skipping Cygwin options.\"\n\tfi\n\tread -n 1 -p \"Build for Windows x86? [Y/n] \" BUILD_WIN32 && echo\n\tread -n 1 -p \"Build for Windows x86_64? [Y/n] \" BUILD_WIN64 && echo\n\tBUILD_DOS=n\n\tif [ \"$(uname -o)\" == \"Cygwin\" ]; then\n\t\tread -n 1 -p \"Build for Dos? [y/N] \" BUILD_DOS && echo\n\tfi\n\tBUILD_SDL_LINUXx86=n\n\tBUILD_SDL_LINUXx64=n\n\tBUILD_SDL_WIN32=n\n\tBUILD_SDL_WIN64=n\n\tif [ \"$(uname -sm)\" == \"Linux i686\" ]; then\n\t\tread -n 1 -p \"Build for Linux x86 SDL? [y/N] \" BUILD_SDL_LINUXx32 && echo\n\tfi\n\tif [ \"$(uname -sm)\" == \"Linux x86_64\" ]; then\n\t\tread -n 1 -p \"Build for Linux x86_64 SDL? [y/N] \" BUILD_SDL_LINUXx64 && echo\n\tfi\n\tread -n 1 -p \"Build for Android? [y/N] \" BUILD_ANDROID && echo\n\tread -n 1 -p \"Build for Emscripten? [y/N] \" BUILD_WEB && echo\n\tif [ 0 -ne 0 ]; then\n\t\tread -n 1 -p \"Build for MacOSX? [y/N] \" BUILD_MAC && echo\n\telse\n\t\techo \"Skipping mac option.\"\n\tfi\nfi\n\nBUILD_CLEAN=${BUILD_CLEAN:-y}\nBUILD_LINUXx86=${BUILD_LINUXx86:-y}\nBUILD_LINUXx64=${BUILD_LINUXx64:-y}\nBUILD_LINUXx32=${BUILD_LINUXx32:-n}\nBUILD_LINUXarmhf=${BUILD_LINUXarmhf:-n}\nBUILD_LINUXaarch64=${BUILD_LINUXaarch64:-n}\nBUILD_CYGWIN=${BUILD_CYGWIN:-n}\nBUILD_WIN32=${BUILD_WIN32:-y}\nBUILD_WIN64=${BUILD_WIN64:-y}\nBUILD_DOS=${BUILD_DOS:-n}\nBUILD_MSVC=${BUILD_MSVC:-n}\nBUILD_SDL=${BUILD_SDL:-n}\nBUILD_ANDROID=${BUILD_ANDROID:-n}\nBUILD_WEB=${BUILD_WEB:-n}\nBUILD_MAC=${BUILD_MAC:-n}\n\nif [ \"$UID\" != \"0\" ]; then\n\techo \"#path config for fte build scripts\"\t\t>$FTECONFIG\n\techo \"THREADS=\\\"$THREADS\\\"\"\t\t\t\t>>$FTECONFIG\n\techo \"BUILDFOLDER=\\\"$BUILDFOLDER\\\"\"\t\t\t>>$FTECONFIG\n\techo \"BUILDLOGFOLDER=\\\"$BUILDLOGFOLDER\\\"\"\t\t>>$FTECONFIG\n\techo \"SVNROOT=\\\"$SVNROOT\\\"\"\t\t\t\t>>$FTECONFIG\n\techo \"ANDROIDROOT=\\\"$ANDROIDROOT\\\"\"\t\t\t>>$FTECONFIG\n\techo \"export ANDROID_HOSTSYSTEM=\\\"$ANDROID_HOSTSYSTEM\\\"\"\t>>$FTECONFIG\n\techo \"export ANDROID_ZIPALIGN=\\\"$ANDROID_ZIPALIGN\\\"\"\t>>$FTECONFIG\n\techo \"EMSCRIPTENROOT=\\\"$EMSCRIPTENROOT\\\"\"\t\t>>$FTECONFIG\n\techo \"OSXCROSSROOT=\\\"$OSXCROSSROOT\\\"\"\t\t\t>>$FTECONFIG\n\n\techo \"BUILD_CLEAN=\\\"$BUILD_CLEAN\\\"\"\t\t>>$FTECONFIG\n\n\techo \"BUILD_LINUXx86=\\\"$BUILD_LINUXx86\\\"\"\t\t>>$FTECONFIG\n\techo \"BUILD_LINUXx64=\\\"$BUILD_LINUXx64\\\"\"\t\t>>$FTECONFIG\n\techo \"BUILD_LINUXx32=\\\"$BUILD_LINUXx32\\\"\"\t\t>>$FTECONFIG\n\techo \"BUILD_LINUXarmhf=\\\"$BUILD_LINUXarmhf\\\"\"\t\t>>$FTECONFIG\n\techo \"BUILD_LINUXaarch64=\\\"$BUILD_LINUXaarch64\\\"\"\t\t>>$FTECONFIG\n\techo \"BUILD_CYGWIN=\\\"$BUILD_CYGWIN\\\"\"\t\t\t>>$FTECONFIG\n\techo \"BUILD_WIN32=\\\"$BUILD_WIN32\\\"\"\t\t\t>>$FTECONFIG\n\techo \"BUILD_WIN64=\\\"$BUILD_WIN64\\\"\"\t\t\t>>$FTECONFIG\n\techo \"BUILD_DOS=\\\"$BUILD_DOS\\\"\"\t\t\t\t>>$FTECONFIG\n\techo \"BUILD_MSVC=\\\"$BUILD_MSVC\\\"\"\t\t\t>>$FTECONFIG\n\techo \"BUILD_ANDROID=\\\"$BUILD_ANDROID\\\"\"\t\t\t>>$FTECONFIG\n\techo \"BUILD_SDL=\\\"$BUILD_SDL\\\"\"\t\t\t\t>>$FTECONFIG\n\techo \"BUILD_WEB=\\\"$BUILD_WEB\\\"\"\t\t\t\t>>$FTECONFIG\n\techo \"BUILD_MAC=\\\"$BUILD_MAC\\\"\"\t\t\t\t>>$FTECONFIG\n\n\techo \"TARGETS_WINDOWS=\\\"$TARGETS_WINDOWS\\\"\"\t\t>>$FTECONFIG\n\techo \"TARGETS_LINUX=\\\"$TARGETS_LINUX\\\"\"\t\t\t>>$FTECONFIG\n\techo \"PLUGINS_WIN32=\\\"$PLUGINS_WIN32\\\"\"\t\t>>$FTECONFIG\n\techo \"PLUGINS_WIN64=\\\"$PLUGINS_WIN64\\\"\"\t\t>>$FTECONFIG\n\techo \"PLUGINS_LINUXx86=\\\"$PLUGINS_LINUXx86\\\"\"\t\t>>$FTECONFIG\n\techo \"PLUGINS_LINUXx64=\\\"$PLUGINS_LINUXx64\\\"\"\t\t>>$FTECONFIG\n\techo \"PLUGINS_LINUXx32=\\\"$PLUGINS_LINUXx32\\\"\"\t\t>>$FTECONFIG\n\techo \"PLUGINS_LINUXarmhf=\\\"$PLUGINS_LINUXarmhf\\\"\"\t>>$FTECONFIG\n\techo \"PLUGINS_LINUXaarch64=\\\"$PLUGINS_LINUXaarch64\\\"\"\t>>$FTECONFIG\n\techo \"PLUGINS_DROID=\\\"$PLUGINS_DROID\\\"\"\t>>$FTECONFIG\nfi\n\ntrue\ntrue=$?\nfalse\nfalse=$?\n\nif [ \"$(uname -s)\" == \"Linux\" ]; then\n\t. /etc/os-release\nfi\nfunction debianpackages {\n\t#make sure apt-get is installed\n\tif [ -z `which apt-get 2>>/dev/null` ]; then\n\t\treturn $false\n\tfi\n\tlocal ret=$true\n\tfor i in \"$@\"\n\tdo\n\t\tdpkg -s $i 2>&1 >> /dev/null\n\t\tif [ $? -eq 1 ]; then\n\t\t\techo \"Package missing: $i\"\n\t\t\tret=$false\n\t\tfi\n\tdone\n\n\tif [ $ret == $false ]; then\n\t\techo \"Packages are not installed. Press enter to continue (or ctrl+c and install).\"\n\t\tif [ \"$UNATTENDED\" != \"y\" ]; then\n\t\t\tread\n\t\tfi\n\t\tret=$true\n\tfi\n\treturn $ret\n}\nfunction jessiepackages {\n\tif [ \"$PRETTY_NAME\" != \"Debian GNU/Linux 8 (jessie)\" ]; then\n\t\treturn $false\n\tfi\n\n\tdebianpackages $@\n\treturn $?\n}\n\n#we don't really know what system we're on. assume they have any system dependancies.\n#fixme: args are programs findable with which\nfunction otherpackages {\n\tif [ -z \"$PRETTY_NAME\" ]; then\n\t\treturn $true\n\tfi\n\treturn $false\n}\n\n\n#Note: only the native linux-sdl target can be compiled, as libSDL[2]-dev doesn't support multiarch properly, and we depend upon it instead of building from source (thus ensuring it has whatever distro stuff needed... though frankly that should be inside the .so instead of the headers).\n\n#if [ $UID -eq 0 ] && [ ! -z `which apt-get` ]; then\n\t#because multiarch requires separate packages for some things, we'll need to set that up now (in case noone did that yet)\n#\tdpkg --add-architecture i386\n#\tapt-get update\n#fi\n\n#generic crap. much of this is needed to set up and decompress dependancies and stuff.\ndebianpackages git make automake libtool p7zip-full zip ca-certificates || otherpackages z7 make git || exit\n\nif [ \"$BUILD_LINUXx86\" == \"y\" ]; then\n\t#for building linux targets\n\tdebianpackages gcc-multilib g++-multilib mesa-common-dev libasound2-dev libxcursor-dev || otherpackages gcc || exit\n\tjessiepackages libgnutls28-dev || debianpackages libgnutls28-dev || otherpackages gcc || exit\n\tif [[ \"$PLUGINS_LINUXx86\" =~ \"ffmpeg\" ]]; then\n\t\tdebianpackages libswscale-dev libavcodec-dev || otherpackages || exit\n\tfi\n\tif [[ \"$PLUGINS_LINUXx86\" =~ \"openxr\" ]]; then\n\t\tdebianpackages libopenxr-dev || otherpackages || exit\n\tfi\nfi\nif [ \"$BUILD_LINUXx64\" == \"y\" ]; then\n\t#for building linux targets\n\tdebianpackages gcc-multilib g++-multilib mesa-common-dev libasound2-dev libxcursor-dev || otherpackages gcc || exit\n\tjessiepackages libgnutls28-dev || debianpackages libgnutls28-dev || otherpackages gcc || exit\n\tif [[ \"$PLUGINS_LINUXx64\" =~ \"ffmpeg\" ]]; then\n\t\tdebianpackages libswscale-dev libavcodec-dev || otherpackages || exit\n\tfi\n\tif [[ \"$PLUGINS_LINUXx64\" =~ \"openxr\" ]]; then\n\t\tdebianpackages libopenxr-dev || otherpackages || exit\n\tfi\nfi\nif [ \"$BUILD_LINUXx32\" == \"y\" ]; then\n\t#for building linux targets\n\tdebianpackages gcc-multilib g++-multilib mesa-common-dev libasound2-dev libxcursor-dev || otherpackages gcc || exit\n\tjessiepackages libgnutls28-dev || debianpackages libgnutls28-dev || otherpackages gcc || exit\nfi\nif [ \"$BUILD_LINUXarmhf\" == \"y\" ]; then\n\t#for building linux targets\n\tdebianpackages gcc-multilib-arm-linux-gnueabihf g++-multilib-arm-linux-gnueabihf mesa-common-dev libasound2-dev libxcursor-dev || otherpackages gcc || exit\n\tjessiepackages libgnutls28-dev || debianpackages libgnutls28-dev || otherpackages gcc || exit\nfi\nif [ \"$BUILD_SDL\" == \"y\" ]; then\n\t#for building SDL targets\n\tdebianpackages libSDL1.2-dev libSDL2-dev libspeex-dev libspeexdsp-dev || otherpackages || exit\nfi\n\nif [ \"$BUILD_WIN32\" == \"y\" ] || [ \"$BUILD_WIN64\" == \"y\" ]; then\n\t#for building windows targets\n\t#tools package provides pkg-config\n\t#python is needed to configure scintilla properly.\n\tdebianpackages mingw-w64 mingw-w64-tools python || otherpackages x86_64-w64-mingw32-gcc python || exit\nfi\n\n\nif [ \"$BUILD_ANDROID\" == \"y\" ]; then\n\t( (jessiepackages openjdk-8-jdk-headless || debianpackages openjdk-8-jdk-headless ) && debianpackages ant) || otherpackages || exit\nfi\n\nif [ \"$BUILD_WEB\" == \"y\" ]; then\n\t( (jessiepackages cmake || debianpackages cmake) && debianpackages git build-essential) || exit\nfi\n\nif [ \"$BUILD_MAC\" == \"y\" ]; then\n\tdebianpackages git cmake libxml2-dev fuse || otherpackages || exit\nfi\ndebianpackages subversion make build-essential || otherpackages svn make || exit\n\necho \"System Package checks complete.\"\n\nif [ \"$UID\" == \"0\" ]; then\n\t#avoid root taking ownership of anything.\n\techo \"Refusing to update/rebuild toolchains as root.\"\n\techo \"Please continue running this script as a regular user.\"\n\texit\nfi\n\nif [ \"$UNATTENDED\" != \"y\" ]; then\n\techo\n\techo \"(Any new toolchains will be installed to $FTEROOT)\"\n\techo \"(Say no if you're certain you already set up everything)\"\n\tread -n 1 -p \"Rebuild/update any toolchains now? [y/N] \" REBUILD_TOOLCHAINS && echo\nelse\n\tREBUILD_TOOLCHAINS=\"y\"\nfi\nREBUILD_TOOLCHAINS=${REBUILD_TOOLCHAINS:-n}\nmkdir -p $FTEROOT\n\n#dos shit\nif [ \"$BUILD_DOS\" == \"y\" ] && [ $UID -ne 0 ] && [ $REBUILD_TOOLCHAINS == \"y\" ]; then\n\techo \"You'll need to manually install djgpp for DOS builds.\"\nfi\n\n#android shit. WARNING: should come first as it spits out some EULAs that need confirming.\nif [ \"$BUILD_ANDROID\" == \"y\" ] && [ $UID -ne 0 ] && [ $REBUILD_TOOLCHAINS == \"y\" ]; then\n\tmkdir -p $ANDROIDROOT\n\tcd $ANDROIDROOT\n\twget -N https://dl.google.com/android/repository/tools_r25.2.3-linux.zip\n\tunzip -qn tools_r25.2.3-linux.zip\n\tcd tools/bin\n\t#yes, android-8 is fucking old now. newer versions won't work on older devices.\n\techo \"downloading android build tools\"\n\t./sdkmanager \"build-tools;$ANDROID_BUILDTOOLS\"\n\techo \"downloading android platform tools\"\n\t./sdkmanager \"platform-tools\"\n\techo \"downloading android-9\"\n\t./sdkmanager \"platforms;android-9\"\n\techo \"downloading android ndk\"\n\t./sdkmanager \"ndk-bundle\"\n\tcd ~\nfi\n\n#emscripten/web shit\nif [ \"$BUILD_WEB\" == \"y\" ] && [ $UID -ne 0 ] && [ $REBUILD_TOOLCHAINS == \"y\" ]; then\n\tmkdir -p $EMSCRIPTENROOT\n\tcd $EMSCRIPTENROOT/..\n\twget -N https://s3.amazonaws.com/mozilla-games/emscripten/releases/emsdk-portable.tar.gz\n\tcd $EMSCRIPTENROOT\n\ttar xzf ../emsdk-portable.tar.gz --strip-components=1\n\t./emsdk install latest\n\t./emsdk activate latest\n\tcd ~\nfi\n\n\n#osxcross, for mac crap\nif [ \"$BUILD_MAC\" == \"y\" ] && [ $UID -ne 0 ] && [ $REBUILD_TOOLCHAINS == \"y\" ] && [ \"$UNATTENDED\" != \"y\" ]; then\n\techo \"Setting up OSXCross... THIS IS TOTALLY UNTESTED\"\n\tread -p \"You need to download xcode first. Where did you download the .dmg file to?\" XCODE\n\tgit clone https://github.com/tpoechtrager/osxcross.git $OSXCROSSROOT\n\tcd $OSXCROSSROOT\n\ttools/gen_sdk_package_darling_dmg.sh $XCODE\n\tcp *.tar.xz\n\tSDK_VERSION=10.10 UNATTENDED=0 ./build.sh\n\tcd ~\nfi\n\n\nif [ $UID -ne 0 ] && [ $REBUILD_TOOLCHAINS == \"y\" ]; then\n\t#initial checkout of fte's svn\n\tif [ \"$NOUPDATE\"!=\"n\" ]; then\n\t\tif [ ! -d $SVNROOT ]; then\n\t\t\tsvn checkout https://svn.code.sf.net/p/fteqw/code/trunk $SVNROOT $SVN_REV_ARG\n\t\telse\n\t\t\tcd $SVNROOT\n\t\t\tsvn up $SVN_REV_ARG\n\t\tfi\n\tfi\n\n\t#FIXME: there may be race conditions when compiling.\n\t#so make sure we've pre-built certain targets without using -j\n\t#linux distros vary too much with various dependancies and versions and such, so we might as well pre-build our own copies of certain libraries. this really only needs to be done once, but its safe to retry anyway.\n\tcd $SVNROOT/engine\n\tif [ \"$BUILD_LINUXx86\" == \"y\" ]; then\n\t\techo \"Making libraries (linux x86)...\"\n\t\tmake FTE_TARGET=linux32 makelibs CPUOPTIMISATIONS=-fno-finite-math-only 2>&1 >>/dev/null\n\tfi\n\tif [ \"$BUILD_LINUXx64\" == \"y\" ]; then\n\t\techo \"Making libraries (linux x86_64)...\"\n\t\tmake FTE_TARGET=linux64 makelibs CPUOPTIMISATIONS=-fno-finite-math-only 2>&1 >>/dev/null\n\tfi\n\tif [ \"$BUILD_LINUXx32\" == \"y\" ]; then\n\t\techo \"Making libraries (linux x32)...\"\n\t\tmake FTE_TARGET=linuxx32 makelibs CPUOPTIMISATIONS=-fno-finite-math-only 2>&1 >>/dev/null\n\tfi\n\tif [ \"$BUILD_LINUXarmhf\" == \"y\" ]; then\n\t\techo \"Making libraries (linux armhf)...\"\n\t\tmake FTE_TARGET=linuxarmhf makelibs CPUOPTIMISATIONS=-fno-finite-math-only 2>&1 >>/dev/null\n\tfi\n\tif [ \"$BUILD_LINUXaarch64\" == \"y\" ]; then\n\t\techo \"Making libraries (linux aarch64)...\"\n\t\tmake FTE_TARGET=linuxaarch64 makelibs CPUOPTIMISATIONS=-fno-finite-math-only 2>&1 >>/dev/null\n\tfi\n\tif [ \"$BUILD_WIN32\" == \"y\" ]; then\n\t\techo \"Making libraries (win32)...\"\n\t\tmake FTE_TARGET=win32 makelibs CPUOPTIMISATIONS=-fno-finite-math-only 2>&1 >>/dev/null\n\tfi\n\tif [ \"$BUILD_WIN64\" == \"y\" ]; then\n\t\techo \"Making libraries (win64)...\"\n\t\tmake FTE_TARGET=win64 makelibs CPUOPTIMISATIONS=-fno-finite-math-only 2>&1 >>/dev/null\n\tfi\n\n\t#These plugins have external 3rd-party dependancies that are downloaded as part of building.\n\tif [ \"$BUILD_WIN32\" == \"y\" ] && [[ \"$PLUGINS_WIN32\" =~ \"ode\" ]]; then\n\t\techo \"Prebuilding ODE library (win32)...\"\n\t\tmake FTE_TARGET=win32 plugins-rel NATIVE_PLUGINS=ode 2>&1 >>/dev/null\n\tfi\n\tif [ \"$BUILD_WIN64\" == \"y\" ] && [[ \"$PLUGINS_WIN64\" =~ \"ode\" ]]; then\n\t\techo \"Prebuilding ODE library (win64)...\"\n\t\tmake FTE_TARGET=win64 plugins-rel NATIVE_PLUGINS=ode 2>&1 >>/dev/null\n\tfi\n\tif [ \"$BUILD_LINUXx86\" == \"y\" ] && [[ \"$PLUGINS_LINUXx86\" =~ \"ode\" ]]; then\n\t\techo \"Prebuilding ODE library (linux x86)...\"\n\t\tmake FTE_TARGET=linux32 plugins-rel NATIVE_PLUGINS=ode CPUOPTIMISATIONS=-fno-finite-math-only 2>&1 >>/dev/null\n\tfi\n\tif [ \"$BUILD_LINUXx64\" == \"y\" ] && [[ \"$PLUGINS_LINUXx64\" =~ \"ode\" ]]; then\n\t\techo \"Prebuilding ODE library (linux x86_64)...\"\n\t\tmake FTE_TARGET=linux64 plugins-rel NATIVE_PLUGINS=ode CPUOPTIMISATIONS=-fno-finite-math-only 2>&1 >>/dev/null\n\tfi\n\tif [ \"$BUILD_WIN32\" == \"y\" ] && [[ \"$PLUGINS_WIN32\" =~ \"ffmpeg\" ]]; then\n\t\techo \"Obtaining ffmpeg library (win32)...\"\n\t\tmake FTE_TARGET=win32 plugins-rel NATIVE_PLUGINS=ffmpeg 2>&1 >>/dev/null\n\tfi\n\tif [ \"$BUILD_WIN64\" == \"y\" ] && [[ \"$PLUGINS_WIN64\" =~ \"ffmpeg\" ]]; then\n\t\techo \"Obtaining ffmpeg library (win64)...\"\n\t\tmake FTE_TARGET=win64 plugins-rel NATIVE_PLUGINS=ffmpeg 2>&1 >>/dev/null\n\tfi\n\tcd ~\nfi\n\n\n\necho \"Setup script complete.\"\necho \"When you run build_wip.sh output will be written to $BUILDFOLDER/*\"\n\n"
  },
  {
    "path": "build_wip.sh",
    "content": "#!/bin/bash\nSTART=$(date +%s)\n\nSVNROOT=$(cd \"$(dirname \"$(readlink \"$BASH_SOURCE\")\")\" && pwd)\nFTECONFIG=$SVNROOT/build.cfg\n\nHOME=`echo ~`\nBASE=$SVNROOT/..\n#set this if you want non-default branding, for customised builds.\n#export BRANDING=wastes\n\n#defaults, if we're not set up properly.\n#should be overriden in build.cfg\nBUILDFOLDER=$HOME/htdocs\nBUILDLOGFOLDER=$BUILDFOLDER/build_logs\nSVNROOT=$BASE/fteqw-code\nBUILD_LINUXx86=y\nBUILD_LINUXx64=y\nBUILD_WIN32=y\nBUILD_WIN64=y\nBUILD_ANDROID=y\nBUILD_WEB=y\nPLUGINS_LINUXx86=\"qi ezhud xmpp irc hl2\"\nPLUGINS_LINUXx64=\"qi ezhud xmpp irc hl2\"\nPLUGINS_LINUXx32=\"qi ezhud xmpp irc hl2\"\nPLUGINS_WIN32=\"ffmpeg ode qi ezhud xmpp irc hl2\"\nPLUGINS_WIN64=\"ffmpeg ode qi ezhud xmpp irc hl2\"\nTHREADS=\"-j 4\"\n\nTARGETS_LINUX=\"qcc-rel rel dbg vk-rel plugins-rel plugins-dbg\"\nTARGETS_WINDOWS=\"sv-rel m-rel qcc-rel qccgui-scintilla qccgui-dbg m-dbg sv-dbg plugins-dbg plugins-rel\"\nTARGETS_WEB=\"gl-rel\"\n\n\nif [ -e $FTECONFIG ]; then\n\t. $FTECONFIG\nelse\n\techo \"WARNING: $FTECONFIG does not exist yet.\"\nfi\n\nif [ \"$BUILD_CLEAN\" == \"n\" ]; then\n\tNOUPDATE=\"y\"\nfi\n\n#check args (and override config as desired)\nwhile [[ $# -gt 0 ]]\ndo\n\tcase $1 in\n\t-r)\n\t\tSVN_REV_ARG=\"-r $2\"\n\t\tNOUPDATE=\n\t\tshift\n\t\t;;\n\t-j)\n\t\tTHREADS=\"-j $2\"\n\t\tshift\n\t\t;;\n\t-help|--help)\n\t\techo \"  -r VER       Specifies the SVN revision to update to\"\n\t\techo \"  -j THREADS   Specifies how many jobs to make with\"\n\t\techo \"  --help       This text\"\n\t\techo \"  --noupdate   Don't do svn updates\"\n\t\techo \"  --unclean    Don't do make clean, for faster rebuilds\"\n\t\techo \"  --web        Build web target (excluding all others)\"\n\t\techo \"  --droid      Build android target (excluding others)\"\n\t\texit 0\n\t\t;;\n\t-build|--build)\n\t\tTARGET=\"FTE_CONFIG=$2\"\n\t\tshift\n\t\t;;\n\t--noupdate)\n\t\tNOUPDATE=\"y\"\n\t\t;;\n\t--unclean)\n\t\tBUILD_CLEAN=\"n\"\n\t\t;;\n\t--web)\n\t\tBUILD_LINUXx86=\"n\"\n\t\tBUILD_LINUXx64=\"n\"\n\t\tBUILD_LINUXarmhf=\"n\"\n\t\tBUILD_LINUXaarch64=\"n\"\n\t\tBUILD_WIN32=\"n\"\n\t\tBUILD_WIN64=\"n\"\n\t\tBUILD_ANDROID=\"n\"\n\t\tBUILD_WEB=\"y\"\n\t\t;;\n\t--droid)\n\t\tBUILD_LINUXx86=\"n\"\n\t\tBUILD_LINUXx64=\"n\"\n\t\tBUILD_LINUXarmhf=\"n\"\n\t\tBUILD_LINUXaarch64=\"n\"\n\t\tBUILD_WIN32=\"n\"\n\t\tBUILD_WIN64=\"n\"\n\t\tBUILD_ANDROID=\"y\"\n\t\tBUILD_WEB=\"n\"\n\t\t;;\n\t*)\n\t\techo \"Unknown option $1\"\n\t\t;;\n\tesac\n\tshift\ndone\n\nMAKEARGS=\"$THREADS $TARGET\"\n\n\n########### Emscripten / Web Stuff\nexport EMSDK=$EMSCRIPTENROOT\n#export WEB_PREJS=\"--pre-js $HOME/prejs.js\"\n\n########### Android Stuff. so messy...\n#This is some android password that you should keep private. You should keep the keystore file private too, of course. Frankly, that part is more important than this small random number.\nKEYPASSFILE=$BASE/.fte_keypass\nif [ ! -e $KEYPASSFILE ]; then\n\tdd if=/dev/urandom count=9 bs=1 2>/dev/null | base64 > $KEYPASSFILE\n\tchmod 400 $KEYPASSFILE\nfi\nKEYPASS=`cat $KEYPASSFILE`\nexport JAVA_HOME=/usr\nif [ ! -z \"$ANDROIDROOT\" ]; then \n\texport ANDROID_HOME=$ANDROIDROOT\nfi\nif [ ! -z \"$ANDROIDNDKROOT\" ]; then \n\texport ANDROID_NDK_ROOT=$ANDROIDNDKROOT\nelse\n\texport ANDROID_NDK_ROOT=$ANDROID_HOME/ndk-bundle\nfi\nexport KEYTOOLARGS=\"-keypass $KEYPASS -storepass $KEYPASS -dname \\\"CN=fteqw.com, OU=ID, O=FTE, L=Unknown, S=Unknown, C=GB\\\"\"\nexport JARSIGNARGS=\"-storepass $KEYPASS\"\n\n########### Various Output etc Paths\nQCCBUILDFOLDER=$BUILDFOLDER/fteqcc\nSVNFOLDER=$SVNROOT/engine/release\nARCHIVEFOLDER=$BUILDFOLDER/archive\nSVNDBGFOLDER=$SVNROOT/engine/debug\nWARNINGLEVEL=\"-w\"\nFILELOCK=$BASE/.fte_buildlock\n\n#./ccache-alias.sh\n\nexec 9>$FILELOCK\nif ! flock -n 9 ; then\n\techo \"Build script is already running!\";\n\texit 1\nfi\n\nmkdir -p $BUILDLOGFOLDER\nif [ ! -d $SVNROOT ]; then\n\t#just in case...\n\tsvn checkout https://svn.code.sf.net/p/fteqw/code/trunk $SVNROOT $SVN_REV_ARG\nfi\n\ncd $SVNROOT/\n\nif [ \"$NOUPDATE\" != \"y\" ]; then\n\techo \"SVN Update\"\n\tsvn update $SVN_REV_ARG\nfi\n\ncd engine\n\ndate > $BUILDLOGFOLDER/buildlog.txt\necho \"Starting build\" >> $BUILDLOGFOLDER/buildlog.txt\n\nfunction build {\n\tBUILDSTART=$(date +%s)\n\tNAME=$1\n\tDEST=$2\n\tshift; shift\n\tif [ \"$BUILD_CLEAN\" != \"n\" ]; then\n\t\tmake clean >> /dev/null\n\tfi\n\techo -n \"Making $NAME... \"\n\tdate > $BUILDLOGFOLDER/$DEST.txt\n\techo BUILD: $NAME >> $BUILDLOGFOLDER/$DEST.txt\n\techo PLUGINS: $NATIVE_PLUGINS >> $BUILDLOGFOLDER/$DEST.txt\n\techo make $MAKEARGS $* >> $BUILDLOGFOLDER/$DEST.txt 2>&1\n\tmake $MAKEARGS $* >> $BUILDLOGFOLDER/$DEST.txt 2>&1\n\tif [ $? -eq 0 ]; then\n\t\tBUILDEND=$(date +%s)\n\t\tBUILDTIME=$(( $BUILDEND - $BUILDSTART ))\n\t\techo \"$BUILDTIME seconds\"\n\t\techo \"$NAME done, took $BUILDTIME seconds\" >> $BUILDLOGFOLDER/buildlog.txt\n\t\trm -rf $BUILDFOLDER/$DEST >> /dev/null 2>&1\n\t\tmkdir $BUILDFOLDER/$DEST 2>> /dev/null\n\t\tmkdir $BUILDFOLDER/$DEST/debug 2>> /dev/null\n\t\tcp $SVNFOLDER/* $BUILDFOLDER/$DEST >> /dev/null 2>> /dev/null\n\t\tcp $SVNDBGFOLDER/* $BUILDFOLDER/$DEST/debug >> /dev/null 2>> /dev/null\n\t\trm -rf $BUILDFOLDER/$DEST/*.a >> /dev/null 2>&1\n\t\trm -rf $BUILDFOLDER/$DEST/debug/*.a >> /dev/null 2>&1\n\t\trmdir $BUILDFOLDER/$DEST/debug 2>> /dev/null\n\telse\n\t\techo \"$NAME failed\" >> $BUILDLOGFOLDER/buildlog.txt\n\t\techo \"failed\"\n\tfi\n}\n\nfunction build_fteqcc {\n\techo \"--- no code ---\"\n}\n\necho \"--- Engine builds ---\"\n#the -fno-finite-math-only is to avoid a glibc dependancy\nif [ \"$BUILD_LINUXx86\" != \"n\" ]; then\n\tNATIVE_PLUGINS=\"$PLUGINS_LINUXx86\" build \"Linux 32-bit\" linux_x86 FTE_TARGET=linux32 CPUOPTIMIZATIONS=-fno-finite-math-only $TARGETS_LINUX\nfi\nif [ \"$BUILD_LINUXx64\" != \"n\" ]; then\n\tNATIVE_PLUGINS=\"$PLUGINS_LINUXx64\" build \"Linux 64-bit\" linux_amd64 FTE_TARGET=linux64 CPUOPTIMIZATIONS=-fno-finite-math-only $TARGETS_LINUX\nfi\nif [ \"$BUILD_LINUXx32\" != \"n\" ]; then\n# \tCFLAGS=\"-DNO_JPEG\"\n\tNATIVE_PLUGINS=\"$PLUGINS_LINUXx32\" build \"Linux x32\" linux_x32 FTE_TARGET=linux_x32 CPUOPTIMIZATIONS=-fno-finite-math-only $TARGETS_LINUX\nfi\nif [ \"$BUILD_LINUXarmhf\" != \"n\" ]; then\n\t#debian/ubuntu's armhf targets armv7. we instead target armv6, because that means we work on rpi too (but still with hard-float). It should be compatible although we likely need more ops.\n\tNATIVE_PLUGINS=\"$PLUGINS_LINUXarmhf\" build \"Linux ARMhf\" linux_armhf FTE_TARGET=linux_armhf CPUOPTIMIZATIONS=-fno-finite-math-only $TARGETS_LINUX\nfi\nif [ \"$BUILD_LINUXaarch64\" != \"n\" ]; then\n\tNATIVE_PLUGINS=\"$PLUGINS_LINUXaarch64\" build \"Linux aarch64\" linux_aarch64 FTE_TARGET=linux_aarch64 CPUOPTIMIZATIONS=-fno-finite-math-only $TARGETS_LINUX\nfi\nif [ \"$BUILD_CYGWIN\" != \"n\" ]; then\n\tNATIVE_PLUGINS=\"qi ezhud\" build \"Cygwin\" cygwin qcc-rel rel dbg plugins-rel plugins-dbg\nfi\nif [ \"$BUILD_WIN32\" != \"n\" ]; then\n\tNATIVE_PLUGINS=\"$PLUGINS_WIN32\" build \"Windows 32-bit\" win32 FTE_TARGET=win32 CFLAGS=\"$WARNINGLEVEL\" $TARGETS_WINDOWS\nfi\nif [ \"$BUILD_WIN64\" != \"n\" ]; then\n\tNATIVE_PLUGINS=\"$PLUGINS_WIN64\" build \"Windows 64-bit\" win64 FTE_TARGET=win64 CFLAGS=\"$WARNINGLEVEL\" $TARGETS_WINDOWS\nfi\nif [ \"$BUILD_MSVC\" != \"n\" ]; then\n\tNATIVE_PLUGINS=\"$PLUGINS_WIN32\" build \"Windows MSVC 32-bit\" msvc FTE_TARGET=vc BITS=32 CFLAGS=\"$WARNINGLEVEL\" sv-rel gl-rel vk-rel mingl-rel m-rel d3d-rel qcc-rel qccgui-scintilla qccgui-dbg gl-dbg sv-dbg plugins-dbg plugins-rel\n\tNATIVE_PLUGINS=\"$PLUGINS_WIN64\" build \"Windows MSVC 64-bit\" msvc FTE_TARGET=vc BITS=64 CFLAGS=\"$WARNINGLEVEL\" sv-rel gl-rel vk-rel mingl-rel m-rel d3d-rel qcc-rel qccgui-scintilla qccgui-dbg gl-dbg sv-dbg plugins-dbg plugins-rel\nfi\nexport NATIVE_PLUGINS=\"qi ezhud xmpp irc\"\nif [ \"$BUILD_ANDROID\" != \"n\" ]; then\n\tNATIVE_PLUGINS=\"$PLUGINS_DROID\" build \"Android\" android droid-rel\nfi\nif [ \"$BUILD_DOS\" == \"y\" ]; then\n\t#no networking makes dedicated servers useless. and only a crappy sw renderer is implemented right now.\n\t#the qcc might be useful to someone though!\n\tbuild \"DOS\" dos m-rel qcc-rel\nfi\nif [ \"$BUILD_WEB\" != \"n\" ]; then\n\tsource $EMSDK/emsdk_env.sh >> /dev/null\n\tLTO= build \"Emscripten\" web FTE_TARGET=web $TARGETS_WEB CC=emcc\nfi\nif [ \"$BUILD_SDL_LINUXx86\" == \"y\" ]; then\n\tbuild \"Linux 32-bit (SDL)\" linux_x86_sdl FTE_TARGET=SDL2 BITS=32 $TARGETS_SDL\nfi\nif [ \"$BUILD_SDL_LINUXx64\" == \"y\" ]; then\n\tbuild \"Linux 64-bit (SDL)\" linux_amd64_sdl FTE_TARGET=SDL2 BITS=64 $TARGETS_SDL\nfi\nif [ \"$BUILD_SDL_WIN32\" == \"y\" ]; then\n\tbuild \"Windows 32-bit (SDL)\" win32_sdl FTE_TARGET=win32_SDL $TARGETS_SDL\n#\tCFLAGS=\"$WARNINGLEVEL -DNOLEGACY -DOMIT_QCC\" build \"Windows 32-bit nocompat\" nocompat FTE_TARGET=win32 LTO=1 NOCOMPAT=1 BOTLIB_CFLAGS=\"\" BOTLIB_OBJS=\"\" $TARGETS_SDL\nfi\nif [ \"$BUILD_SDL_WIN64\" == \"y\" ]; then\n\tbuild \"Windows 64-bit (SDL)\" win64_sdl FTE_TARGET=win64_SDL $TARGETS_SDL\nfi\n####build \"MorphOS\" morphos CFLAGS=\"-I$BASE/morphos/os-include/ -I$BASE/morphos/lib/ -L$BASE/morphos/lib/ -I$BASE/zlib/zlib-1.2.5 -L$BASE/zlib/zlib-1.2.5 -I./libs $WARNINGLEVEL\" gl-rel mingl-rel sv-rel qcc-rel\nif [ \"$BUILD_MAC\" != \"n\" ]; then\n\t#build \"MacOSX\" macosx_tiger CFLAGS=\"-I$BASE/mac/x86/include/ -L$BASE/mac/x86/lib -I./libs\" FTE_TARGET=macosx_x86 sv-rel gl-rel mingl-rel qcc-rel\n\t#FIXME: figure out how to do universal binaries or whatever they're called\n\tbuild \"MacOSX 32-bit\" osx32 CC=o32-clang CXX=o32-clang++ FTE_TARGET=osx_x86 BITS=32 sv-rel gl-rel mingl-rel qcc-rel\n\tbuild \"MacOSX 64-bit\" osx64 CC=o64-clang CXX=o64-clang++ FTE_TARGET=osx_x86_64 BITS=64 sv-rel gl-rel mingl-rel qcc-rel\nfi\n\n\n#third party stuff / misc crap\nif [ \"$BUILD_WEB\" != \"n\" ]; then\n\tcp $BASE/3rdparty/web/* $BUILDFOLDER/web/\nfi\nif [ \"$BUILD_WIN32\" != \"n\" ]; then\n\tif [ -e \"$BASE/3rdparty/win32/3rdparty.zip\" ]; then\n\t\tcp $BASE/3rdparty/win32/3rdparty.zip $BUILDFOLDER/win32/3rdparty.zip\n\telse\n\t\trm -f $BUILDFOLDER/win32/3rdparty.zip\n\tfi\n#\tif [ \"$BUILD_SDL_WIN32\" != \"n\" ]; then\n#\t\tcp $SVNROOT/engine/libs/SDL2-2.0.1/i686-w64-mingw32/bin/SDL2.dll $BUILDFOLDER/win32_sdl\n#\tfi\nfi\nif [ \"$BUILD_WIN64\" != \"n\" ]; then\n\tif [ -e \"$BASE/3rdparty/win64/3rdparty.zip\" ]; then\n\t\tcp $BASE/3rdparty/win64/3rdparty.zip $BUILDFOLDER/win64/3rdparty.zip\n\telse\n\t\trm -f $BUILDFOLDER/win64/3rdparty.zip\n\tfi\n#\tif [ \"$BUILD_SDL_WIN64\" != \"n\" ]; then\n#\t\tcp $SVNROOT/engine/libs/SDL2-2.0.1/x86_64-w64-mingw32/bin/SDL2.dll $BUILDFOLDER/win64_sdl\n#\tfi\nfi\nif [ -e \"$HOME/nocompat_readme.html\" ]; then\n\tcp $HOME/nocompat_readme.html $BUILDFOLDER/nocompat/README.html\nfi\n\n\n#call out to build_qc.sh to invoke native builds as appropriate.\ncase \"$(uname -m)\" in\nx86_64)\n\tif [ \"$BUILD_LINUXx64\" != \"n\" ]; then\n\t\trm -rf $QCCBUILDFOLDER 2>&1\n\t\tmkdir -p $QCCBUILDFOLDER\n\t\tcd $SVNROOT/\n\t\tFTEQCC=$BUILDFOLDER/linux_amd64/fteqcc64 FTEQW=$BUILDFOLDER/linux_amd64/fteqw64 QSS=$BUILDFOLDER/qss/quakespasm-spiked-linux64 ./build_qc.sh\n\tfi\n\t;;\ni386 | i486 | i586)\n\tif [ \"$BUILD_LINUXx86\" != \"n\" ]; then\n\t\trm -rf $QCCBUILDFOLDER 2>&1\n\t\tmkdir -p $QCCBUILDFOLDER\n\t\tcd $SVNROOT/\n\t\tFTEQCC=$BUILDFOLDER/linux_x86/fteqcc32 FTEQW=$BUILDFOLDER/linux_x86/fteqw32 QSS= ./build_qc.sh\n\tfi\n\t;;\nesac\n\ncd $SVNROOT/engine/\nsvn info > $BUILDFOLDER/version.txt\n\nif [ \"$BUILD_LINUXx86\" != \"n\" ]; then\n\tcp $BUILDFOLDER/linux_x86/fteqcc32 $QCCBUILDFOLDER/linux32-fteqcc\nfi\nif [ \"$BUILD_LINUXx64\" != \"n\" ]; then\n\tcp $BUILDFOLDER/linux_amd64/fteqcc64 $QCCBUILDFOLDER/linux64-fteqcc\nfi\nif [ \"$BUILD_LINUXx32\" != \"n\" ]; then\n\tcp $BUILDFOLDER/linux_x32/fteqccx32 $QCCBUILDFOLDER/linuxx32-fteqcc\nfi\nif [ \"$BUILD_LINUXarmhf\" != \"n\" ]; then\n\tcp $BUILDFOLDER/linux_armhf/fteqccarmhf $QCCBUILDFOLDER/linuxarmhf-fteqcc\nfi\nif [ \"$BUILD_LINUXaarch64\" != \"n\" ]; then\n\tcp $BUILDFOLDER/linux_armhf/fteqccaarch64 $QCCBUILDFOLDER/linuxaarch64-fteqcc\nfi\nif [ \"$BUILD_WIN32\" != \"n\" ]; then\n\tcp $BUILDFOLDER/win32/fteqcc.exe $QCCBUILDFOLDER/win32-fteqcc.exe\n\tcp $BUILDFOLDER/win32/fteqccgui.exe $QCCBUILDFOLDER/win32-fteqccgui.exe\nfi\nif [ \"$BUILD_WIN64\" != \"n\" ]; then\n\tcp $BUILDFOLDER/win64/fteqcc64.exe $QCCBUILDFOLDER/win64-fteqcc.exe\n\tcp $BUILDFOLDER/win64/fteqccgui64.exe $QCCBUILDFOLDER/win64-fteqccgui.exe\nfi\n#cp $BUILDFOLDER/morphos/fteqcc $QCCBUILDFOLDER/morphos-fteqcc\n#cp $BUILDFOLDER/macosx_tiger/fteqcc $QCCBUILDFOLDER/macosx_tiger-fteqcc\ncp $BUILDFOLDER/version.txt $QCCBUILDFOLDER/version.txt\n\nif [ \"$BUILD_WIN32\" != \"n\" ] && [ \"$BUILD_WIN64\" != \"n\" ]; then\n\techo Archiving output\n\tSVNVER=$(svnversion $SVNROOT)\n\tif [ -e $ARCHIVEFOLDER ]; then\n\t\tcd $BUILDFOLDER/\n\t\tzip -q -9 $ARCHIVEFOLDER/win_fteqw_$SVNVER.zip win32/fteglqw.exe win32/fteqwsv.exe win32/fteqccgui.exe win32/debug/fteglqw.exe win64/fteqw.exe win64/debug/fteglqw.exe\n\tfi\n\n\tif [ -e $BUILDFOLDER/fteqw_for_windows.zip ]; then\n\t\tcd $BUILDFOLDER/win32/\n\t\tzip -q -j -9 $BUILDFOLDER/fteqw_for_windows.zip fteglqw.exe fteqwsv.exe fteqccgui.exe fteplug_qi_x86.dll fteplug_xmpp_x86.dll fteplug_irc_x86.dll fteplug_ezhud_x86.dll\n\t\tcd $HOME/3rdparty_win32/\n\t\tzip -q -9 $BUILDFOLDER/fteqw_for_windows.zip ogg.dll vorbis.dll vorbisfile.dll freetype6.dll zlib1.dll\n\t\tmkdir -p $BASE/tmp/fte\n\t\tcd $BASE/tmp/\n\t\tcp $BUILDFOLDER/csaddon/menu.dat fte\n\t\tzip -q -9 $BUILDFOLDER/fteqw_for_windows.zip fte/menu.dat\n\tfi\n\n\t#~/afterquake/updatemini.sh\nfi\n\necho \"All done\"\n\nEND=$(date +%s)\nDIFF=$(( $END - $START ))\nMINS=$(( $DIFF / 60 ))\necho \"Total Compile Time: $MINS minutes\" >> $BUILDLOGFOLDER/buildlog.txt\necho \"Total Compile Time: $MINS minutes\"\n\ncd $HOME\n#./errorlog.sh\n#cd $HOME\n#rm .bitchxrc\n#cp ./fteqw/.bitchxrc ./\n#./BitchX -a irc.quakenet.org -A -c \"#fte\" -n A_Gorilla\n"
  },
  {
    "path": "dist/linux/com.riverbankcomputing.qscintilla.install.patch",
    "content": "diff -ur QScintilla_src-2.13.4.orig/src/qscintilla.pro QScintilla_src-2.13.4/src/qscintilla.pro\n--- QScintilla_src-2.13.4.orig/src/qscintilla.pro\t2023-01-15 19:13:30.751242600 +0100\n+++ QScintilla_src-2.13.4/src/qscintilla.pro\t2023-04-18 01:42:25.794709139 +0200\n@@ -70,22 +70,22 @@\n # Scintilla namespace rather than pollute the global namespace.\n #DEFINES += SCI_NAMESPACE\n \n-target.path = $$[QT_INSTALL_LIBS]\n+target.path = /app/lib\n INSTALLS += target\n \n-header.path = $$[QT_INSTALL_HEADERS]\n+header.path = /app/include\n header.files = Qsci\n INSTALLS += header\n \n-trans.path = $$[QT_INSTALL_TRANSLATIONS]\n+trans.path = /app/share/qt5/translations\n trans.files = qscintilla_*.qm\n INSTALLS += trans\n \n-qsci.path = $$[QT_INSTALL_DATA]\n+qsci.path = /app/share/qt5\n qsci.files = ../qsci\n INSTALLS += qsci\n \n-features.path = $$[QT_HOST_DATA]/mkspecs/features\n+features.path = /app/lib/qt5/mkspecs/features\n CONFIG(staticlib) {\n     features.files = $$PWD/features_staticlib/qscintilla2.prf\n } else {\n"
  },
  {
    "path": "dist/linux/linter-exceptions.json",
    "content": "{\n    \"org.fteqw.fteqw\": {\n        \"appstream-external-screenshot-url\": \"Screenshots do not need mirroring to Flathub\",\n        \"runtime-is-eol-org.kde.Platform-5.15-23.08\": \"fteqccgui needs migrating to Qt6 and fteqw to ffmpeg 7.x and SDL3 for current runtimes\",\n        \"module-fteqw-source-git-branch\": \"fteqw has no tagged releases\"\n    }\n}\n"
  },
  {
    "path": "dist/linux/org.fteqw.fteqw.desktop",
    "content": "[Desktop Entry]\nVersion=1.0\nType=Application\nName=FTEQW Game Engine\nComment=Cross-platform port for the Quake game engine\nExec=fteqw %u\nIcon=org.fteqw.fteqw\nTerminal=false\nCategories=Game;\nMimeType=application/x-quakeworlddemo;x-scheme-handler/quake;x-scheme-handler/qw;\nActions=quake;rerel;netquake;quake2;quake3;hexen2;hexen2mp\n\n[Desktop Action quake]\nName=Play Quake\nExec=fteqw %u -quake\n\n[Desktop Action rerel]\nName=Play Quake Rerelease\nExec=fteqw %u -quake_rerel\n\n[Desktop Action netquake]\nName=Play Classic Quake\nExec=fteqw %u -netquake\n\n[Desktop Action quake2]\nName=Play QuakeII\nExec=fteqw %u -quake2\n\n[Desktop Action quake3]\nName=Play Quake III Arena\nExec=fteqw %u -quake\n\n[Desktop Action hexen2]\nName=Play Hexen2\nExec=fteqw %u -hexen2\n\n[Desktop Action hexen2mp]\nName=Play Hexen2 Mission Pack\nExec=fteqw %u -portals\n\n"
  },
  {
    "path": "dist/linux/org.fteqw.fteqw.fixdownloads.patch",
    "content": "diff --git a/engine/client/m_download.c b/engine/client/m_download.c\nindex 2d618c1b1..74a4a51b4 100644\n--- a/engine/client/m_download.c\n+++ b/engine/client/m_download.c\n@@ -2205,7 +2205,9 @@ static void PM_PreparePackageList(void)\n \t{\n \t\tint parm;\n \t\tqofs_t sz = 0;\n-\t\tchar *f = FS_MallocFile(INSTALLEDFILES, FS_ROOT, &sz);\n+\t\tchar full_filename[256];\n+\t\tsnprintf(full_filename, sizeof(full_filename), \"%s/%s\", getenv(\"XDG_DATA_HOME\"), INSTALLEDFILES);\n+\t\tchar *f = FS_MallocFile(full_filename, FS_ROOT, &sz);\n \t\tloadedinstalled = true;\n \t\tif (f)\n \t\t{\n@@ -3526,11 +3528,13 @@ static void PM_WriteInstalledPackages(void)\n \tint i;\n \tchar *s;\n \tpackage_t *p;\n-\tvfsfile_t *f = FS_OpenVFS(INSTALLEDFILES, \"wbp\", FS_ROOT);\n+\tchar full_filename[256];\n+\tsnprintf(full_filename, sizeof(full_filename), \"%s/%s\", getenv(\"XDG_DATA_HOME\"), INSTALLEDFILES);\n+\tvfsfile_t *f = FS_OpenVFS(full_filename, \"wbp\", FS_ROOT);\n \tqboolean v3 = false;\n \tif (!f)\n \t{\n-\t\tif (FS_DisplayPath(INSTALLEDFILES, FS_ROOT, buf, sizeof(buf)))\n+\t\tif (FS_DisplayPath(full_filename, FS_ROOT, buf, sizeof(buf)))\n \t\t\tCon_Printf(\"package manager: Can't write %s\\n\", buf);\n \t\telse\n \t\t\tCon_Printf(\"package manager: Can't update installed list\\n\");\n"
  },
  {
    "path": "dist/linux/org.fteqw.fteqw.fixhomedir.patch",
    "content": "diff --git a/engine/common/fs.c b/engine/common/fs.c\nindex 68b59d1b9..412d01bb7 100644\n--- a/engine/common/fs.c\n+++ b/engine/common/fs.c\n@@ -466,7 +466,7 @@ static searchpath_t *gameonly_gamedir;\n \n char\tcom_gamepath[MAX_OSPATH];\t//c:\\games\\quake\n char\tcom_homepath[MAX_OSPATH];\t//c:\\users\\foo\\my docs\\fte\\quake\n-qboolean\tcom_homepathenabled;\n+qboolean\tcom_homepathenabled = true;\n static qboolean\tcom_homepathusable;\t//com_homepath is safe, even if not enabled.\n \n //char\tcom_configdir[MAX_OSPATH];\t//homedir/fte/configs\n@@ -2527,20 +2527,21 @@ static const char *FS_GetCleanPath(const char *pattern, qboolean silent, char *o\n \t\tif (*s == ':')\n \t\t{\n \t\t\tif (s == pattern+1 && (s[1] == '/' || s[1] == '\\\\'))\n-\t\t\t\tCon_ThrottlePrintf(&throttletimer, 0, \"Error: absolute path in filename %s\\n\", pattern);\n-\t\t\telse\n+\t\t\t\tCon_ThrottlePrintf(&throttletimer, 0, \"Warning: absolute path in filename %s\\n\", pattern);\n+\t\t\telse {\n \t\t\t\tCon_ThrottlePrintf(&throttletimer, 0, \"Error: alternative data stream in filename %s\\n\", pattern);\n-\t\t\treturn NULL;\n+\t\t\t\treturn NULL;\n+\t\t\t}\n \t\t}\n \t\telse if (*s == '\\\\' || *s == '/' || !*s)\n \t\t{\t//end of segment\n \t\t\tif (o == seg)\n \t\t\t{\n-\t\t\t\tif (o == outbuf)\n-\t\t\t\t{\n-\t\t\t\t\tCon_ThrottlePrintf(&throttletimer, 0, \"Error: absolute path in filename %s\\n\", pattern);\n-\t\t\t\t\treturn NULL;\n-\t\t\t\t}\n+\t\t\t\t// if (o == outbuf)\n+\t\t\t\t// {\n+\t\t\t\t// \tCon_ThrottlePrintf(&throttletimer, 0, \"Error: absolute path in filename %s\\n\", pattern);\n+\t\t\t\t// \treturn NULL;\n+\t\t\t\t// }\n \t\t\t\tif (!*s)\n \t\t\t\t{\n \t\t\t\t\t*o++ = '\\0';\n@@ -2987,7 +2988,7 @@ vfsfile_t *QDECL FS_OpenVFS(const char *filename, const char *mode, enum fs_rela\n \t\t\t}\n \t\t\telse\n \t\t\t{\n-\t\t\t\tif (!try_snprintf(fullname, sizeof(fullname), \"%s%s/%s\", com_homepath, gamedirfile, filename))\n+\t\t\t\tif (!try_snprintf(fullname, sizeof(fullname), \"%s/%s\", com_homepath, filename))\n \t\t\t\t\treturn NULL;\n \t\t\t\tif (*mode == 'w')\n \t\t\t\t\tCOM_CreatePath(fullname);\n"
  },
  {
    "path": "dist/linux/org.fteqw.fteqw.metainfo.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<component type=\"desktop-application\">\n  <id>org.fteqw.fteqw</id>\n  <name>FTEQW</name>\n  <developer id=\"org.fteqw.fteqw\">\n    <name>Forethought Entertainment</name>\n    <url>https://github.com/fte-team/fteqw/graphs/contributors</url>\n  </developer>\n  <metadata_license>CC0-1.0</metadata_license>\n  <project_license>GPL-2.0</project_license>\n  <summary>Modern Quake source port</summary>\n  <launchable type=\"desktop-id\">org.fteqw.fteqw.desktop</launchable>\n  <description>\n    <p>FTEQW is a highly versatile game engine originally based on QuakeWorld with support for\n      a number of Quake engine games.</p>\n    <p>Features:</p>\n    <ul>\n        <li>Single- and multiplayer support</li>\n        <li>Supports multiple games: Quake, QuakeWorld, Quake II, Quake III Arena, Hexen II, ...</li>\n        <li>A vast amount of map, model and image formats are supported</li>\n        <li>Advanced console, with descriptions and autocompletion</li>\n        <li>Plugin support, enabling use of FFMPEG, Bullet/ODE physics and more</li>\n        <li>Extensive suite of QuakeC/entity debugging features</li>\n        <li>Deep integration with FTEQCC, which can even be executed in-game</li>\n        <li>Support for split-screen local multiplayer</li>\n        <li>Voice-chat via Opus and Speex</li>\n        <li>Support for hundreds of players on a single server</li>\n        <li>Works on Windows, Linux, OpenBSD and more!</li>\n        <li>New features are added all the time in cooperation with modders!</li>\n    </ul>\n  </description>\n  <branding>\n    <color type=\"primary\" scheme_preference=\"light\">#c2a6f4</color>\n    <color type=\"primary\" scheme_preference=\"dark\">#1e191e</color>\n  </branding>\n  <provides>\n    <binary>fteqw</binary>\n    <binary>fteqcc</binary>\n    <binary>fteqccgui</binary>\n    <binary>ftemaster</binary>\n    <binary>qtv</binary>\n  </provides>\n  <supports>\n    <control>gamepad</control>\n  </supports>\n  <recommends>\n    <control>keyboard</control>\n    <control>pointing</control>\n  </recommends>\n  <keywords>\n    <keyword>retro</keyword>\n    <keyword>fps</keyword>\n    <keyword>shooter</keyword>\n    <keyword>sourceport</keyword>\n    <keyword>quake</keyword>\n  </keywords>\n  <url type=\"homepage\">https://fteqw.org/</url>\n  <url type=\"bugtracker\">https://github.com/fte-team/fteqw/issues</url>\n  <url type=\"contribute\">https://github.com/fte-team/fteqw</url>\n  <screenshots>\n    <screenshot type=\"default\">\n      <caption>Introduction level rendering in the default graphics setting via OpenGL</caption>\n      <image type=\"source\">https://www.fteqw.org/img/q1/1.png</image>\n    </screenshot>\n    <screenshot>\n      <caption>Integrated server browser with filtering capabilities</caption>\n      <image type=\"source\">https://www.fteqw.org/img/q1/2.png</image>\n    </screenshot>\n    <screenshot>\n      <caption>Graphics preset imitating a 256-colour software renderer</caption>\n      <image type=\"source\">https://www.fteqw.org/img/q1/3.png</image>\n    </screenshot>\n    <screenshot>\n      <caption>More advanced graphics setting with real-time lights, advanced particle engine and light coronas</caption>\n      <image type=\"source\">https://www.fteqw.org/img/q1/4.png</image>\n    </screenshot>\n    <screenshot>\n      <caption>EZHud plugin for user-defined HUDs</caption>\n      <image type=\"source\">https://www.fteqw.org/img/qw/4.png</image>\n    </screenshot>\n    <screenshot>\n      <caption>Optimised multiplayer HUD and player name tags</caption>\n      <image type=\"source\">https://www.fteqw.org/img/qw/3.png</image>\n    </screenshot>\n    <screenshot>\n      <caption>Action Quake II running in FTEQW</caption>\n      <image type=\"source\">https://www.fteqw.org/img/q2/4.png</image>\n    </screenshot>\n    <screenshot>\n      <caption>Quake II</caption>\n      <image type=\"source\">https://www.fteqw.org/img/q2/3.png</image>\n    </screenshot>\n    <screenshot>\n      <caption>Quake III Arena</caption>\n      <image type=\"source\">https://www.fteqw.org/img/q3/2.png</image>\n    </screenshot>\n    <screenshot>\n      <caption>HeXen II</caption>\n      <image type=\"source\">https://www.fteqw.org/img/h2/1.png</image>\n    </screenshot>\n    <screenshot>\n      <caption>HeXen II's particle system</caption>\n      <image type=\"source\">https://www.fteqw.org/img/h2/3.png</image>\n    </screenshot>\n    <screenshot>\n      <caption>HeXen II with realtime shadows and lighting</caption>\n      <image type=\"source\">https://www.fteqw.org/img/h2/4.png</image>\n    </screenshot>\n  </screenshots>\n  <content_rating type=\"oars-1.1\">\n    <content_attribute id=\"violence-fantasy\">intense</content_attribute>\n    <content_attribute id=\"violence-bloodshed\">mild</content_attribute>\n    <content_attribute id=\"social-chat\">intense</content_attribute>\n  </content_rating>\n  <releases>\n    <release version=\"latest\" date=\"2025-05-14\" />\n    <release version=\"6202\" date=\"2022-03-01\" />\n  </releases>\n</component>\n"
  },
  {
    "path": "dist/linux/org.fteqw.fteqw.sh",
    "content": "#!/bin/bash\nfunction check_game_data () {\n  if [[ \"$1\" == \"\" ]]; then\n    zenity --error --ok-label \"Quit\" --width=400 --title \"Could not find Quake game data\" \\\n    --text \"Please copy the Quake game data files (at least <tt>pak0.pak</tt>) to <tt><b>$XDG_DATA_HOME/id1/</b></tt>.\"\n    exit 1\n  fi\n}\n\nfunction check_exit_code () {\n  if [[ \"$1\" != \"0\" ]]; then\n    zenity --error --ok-label \"Quit\" --width=400 --title \"fteqw exited with an error\" \\\n        --text \"For a detailed error message, please run fteqw from a terminal window using\\n <tt><b>flatpak run $FLATPAK_ID</b></tt>.\"\n    exit 1\n  fi\n}\n\n/app/bin/fteqw -basedir $XDG_DATA_HOME \"$@\"\ncheck_exit_code $?\n"
  },
  {
    "path": "dist/linux/org.fteqw.fteqw.yml",
    "content": "---\n# Flatpak runtime version issues:\n#   - 22.08 has a slightly-too-old version of SDL2 that doesn't include SDL_misc.h\n#   - 24.08 includes ffmpeg 7.x, which fteqw fails to build against\n# \n# Options to resolve this:\n#   - manually build ffmpeg 6.x? on the 24.08 runtime\n#   - manually build a newer SDL2 on the 22.08 runtime\n#   - update fteqw to use SDL3 and ffmpeg 7.x (which are the 24.08 runtime versions)\n\napp-id: org.fteqw.fteqw\ncommand: org.fteqw.fteqw.sh\n\n# runtime: org.freedesktop.Platform\n# runtime-version: \"23.08\"\n# sdk: org.freedesktop.Sdk\n# The KDE runtime includes Qt which is useful for building fteqccgui.\n# However, Qt 5.15 is EOL so this runtime doesn't work super well.\nruntime: org.kde.Platform\nruntime-version: \"5.15-23.08\"\nsdk: org.kde.Sdk\n\nfinish-args:\n  # Network for multiplayer\n  - --share=network\n  # Hardware 3D and game controllers\n  - --device=all\n  # GUI\n  - --share=ipc\n  - --socket=fallback-x11\n  - --socket=wayland\n  # Audio\n  - --socket=pulseaudio\n\ncleanup:\n  - /include\n  - \"*.a\"\n  - /lib/cmake\n  - /lib/pkgconfig\n  - /share/aclocal\n  - /share/doc\n  - /share/man\n\nmodules:\n  - name: bullet\n    buildsystem: cmake-ninja\n    config-opts:\n      - -DOpenGL_GL_PREFERENCE=GLVND\n      - -DBUILD_SHARED_LIBS=ON\n      - -DBULLET2_MULTITHREADING=ON\n      - -DUSE_GRAPHICAL_BENCHMARK=OFF\n      - -DBUILD_EGL=OFF\n      - -DBUILD_EXTRAS=OFF\n      - -DBUILD_CPU_DEMOS=OFF\n      - -DBUILD_OPENGL3_DEMOS=OFF\n      - -DBUILD_BULLET2_DEMOS=OFF\n      - -DBUILD_UNIT_TESTS=OFF\n      - -DINSTALL_LIBS=ON\n    sources:\n    - type: archive\n      url: https://github.com/bulletphysics/bullet3/archive/3.25.tar.gz\n      dest-filename: bullet.tar.gz\n      sha256: c45afb6399e3f68036ddb641c6bf6f552bf332d5ab6be62f7e6c54eda05ceb77\n      x-checker-data:\n        type: json\n        url: https://api.github.com/repos/bulletphysics/bullet3/releases/latest\n        version-query: .tag_name\n        url-query: .tarball_url\n\n  - name: ode\n    buildsystem: cmake-ninja\n    config-opts:\n      - -DODE_WITH_DEMOS=OFF\n      - -DODE_WITH_TESTS=OFF\n    cleanup:\n      - /bin/ode-config\n    sources:\n      - type: archive\n        url: https://bitbucket.org/odedevs/ode/downloads/ode-0.16.6.tar.gz\n        sha256: c91a28c6ff2650284784a79c726a380d6afec87ecf7a35c32a6be0c5b74513e8\n        x-checker-data:\n          type: anitya\n          project-id: 2532\n          url-template: https://bitbucket.org/odedevs/ode/downloads/ode-$version.tar.gz\n\n  - name: openxr\n    buildsystem: cmake-ninja\n    sources:\n      - type: archive\n        url: https://github.com/KhronosGroup/OpenXR-SDK/archive/release-1.1.47.tar.gz\n        sha256: 82c7f4e3658578a22e438b5f005ecaf22c3f724b09fe031fa0f8ffc97c30c9ba\n        x-checker-data:\n          type: json\n          url: https://api.github.com/repos/KhronosGroup/OpenXR-SDK/releases/latest\n          version-query: .tag_name | sub(\"release-\", \"\")\n          url-query: .tarball_url\n\n  # libcef fails to build for reasons beyond my ability to debug\n  # - name: libcef\n  #   buildsystem: cmake-ninja\n  #   no-make-install: true\n  #   config-opts:\n  #     - -DCMAKE_BUILD_TYPE=RelWithDbgInfo\n  #   make-args:\n  #     - libcef_dll_wrapper\n  #   build-commands:\n  #     - mkdir -p /app/cef/libcef_dll_wrapper\n  #     - cp -R ./include /app/include\n  #     - cp -R ./Release /app/lib\n  #     - cp -R ./Resources /app/cef\n  #     - cp -R ./libcef_dll_wrapper/libcef_dll_wrapper.a /app/lib/libcef_dll_wrapper.a\n  #   cleanup:\n  #     - \"*.a\"\n  #     - \"./*\"\n  #   sources:\n  #     - type: archive\n  #       url: https://cdn-fastly.obsproject.com/downloads/cef_binary_4280_linux64.tar.bz2\n  #       sha256: d91c78349ecbfbfdfc18d5f882dc28b939028f1a631191c603b5d0d938ada972\n\n  # QScintilla is used by fteqccgui\n  - name: qscintilla\n    buildsystem: qmake\n    subdir: src\n    cleanup:\n      - /lib/qt5\n      - /share/qt5\n    make-install-args:\n      - DESTDIR=/app\n      - PREFIX=/app\n    config-opts:\n      - CONFIG+=release\n    sources:\n      - type: archive\n        url: https://www.riverbankcomputing.com/static/Downloads/QScintilla/2.14.1/QScintilla_src-2.14.1.tar.gz\n        sha256: dfe13c6acc9d85dfcba76ccc8061e71a223957a6c02f3c343b30a9d43a4cdd4d\n        x-checker-data:\n          type: anitya\n          project-id: 10082\n          url-template: https://www.riverbankcomputing.com/static/Downloads/QScintilla/$version/QScintilla_src-$version.tar.gz\n      - type: patch\n        path: com.riverbankcomputing.qscintilla.install.patch\n\n  - name: fteqw\n    buildsystem: cmake-ninja\n    builddir: true\n    config-opts:\n      - -DFTE_USE_SDL=true\n      - -DFTE_ENGINE_SERVER_ONLY=false\n      - -DFTE_TOOL_QTV=true\n      - -DFTE_TOOL_MASTER=true\n      - -DFTE_TOOL_HTTPSV=false\n      - -DFTE_TOOL_QCC=true\n      - -DFTE_TOOL_IQM=false\n      - -DFTE_TOOL_IMAGE=false\n      - -DFTE_INSTALL_BINDIR=bin\n      - -DFTE_PLUG_FFMPEG=false\n    sources:\n      # Launcher script\n      - type: file\n        path: org.fteqw.fteqw.sh\n      - type: git\n        url: https://github.com/fte-team/fteqw.git\n        branch: master\n      # In an ideal world, fteqw would be built off of tagged releases so the Flatpak infra can auto-update:\n      # - type: archive\n      #   url: https://api.github.com/repos/fte-team/fteqw/zipball/v6202\n      #   dest-filename: fteqw.zip\n      #   sha256: 1955a7c7eb2e6c3ba2da689d115d10f89c6e5e261577e67cd0ad176bea1b97ef\n      #   x-checker-data:\n      #     type: json\n      #     url: https://api.github.com/repos/fte-team/fteqw/releases/latest\n      #     version-query: .tag_name | sub(\"^v\"; \"\")\n      #     url-query: .zipball_url\n      # Patches to fix Flatpak sandbox compliance, currently WIP\n      - type: patch\n        path: org.fteqw.fteqw.fixdownloads.patch\n      - type: patch\n        path: org.fteqw.fteqw.fixhomedir.patch\n    post-install:\n      - install -Dm755 ${FLATPAK_BUILDER_BUILDDIR}/org.fteqw.fteqw.sh /app/bin/\n"
  },
  {
    "path": "documentation/Building.md",
    "content": "> Yay, you found out the secrit location to download the sauce code from!\n>\n> Right, urm, now what?\n> Yeah, good question.\n>\n> Urm.\n\n# Preface\n\nWelcome to the building guide for FTEQW, as there are many systems supported, there is a number of ways to build the engine.\n\nThis should cover most, if not all the supported systems and methods.\n\n### Contents\n\n- [Repo Layout](#repo-layout)\n- [Compiling](#compiling)\n- [Easy Build Bot System (Linux)](#easy-build-bot-system-linux)\n- [Windows Systems (cygwin)](#windows-systems-cygwin)\n- [Linux/BSD Systems](#linux-bsd-system)\n- [Android (FTEDroid) with cygwin](#android-ftedroid-with-cygwin)\n- [Browser (emscripten)](#browser-emscripten)\n- [FTEQCC](#fteqcc)\n- [FTEIMG](#fteimg)\n- [FTEIQM](#fteiqm)\n- [FTEQTV](#fteqtv)\n- [Plugins](#plugins)\n\n# Repo Layout\n\n- `.github/workflows:` Github Actions source files.\n\n- `documentation:` General help and introduction.\n\n- `engine:` FTEQW game engine itself. Both client and dedicated server.\n\n- `engine/release:` The Makefile writes its release-build binaries here. Intermediate files are contained within a sub-directory.\n\n- `engine/debug:` The Makefile writes its debug-build binaries here. Intermediate files are contained within a sub-directory.\n\n- `fteqtv:` The QTV proxy server program.\n\n- `plugins:` several optional plugins that do various interesting things, though not so interesting.\n\n- `q3asm2:` Spike's quick hack at a QVM Assembler which is not horribly slow. Ignore it.\n\n- `quakec:` Various QuakeC mods. Some interesting, some not.\n\n- `quakec/basemod:` TimeServ's attempt to bugfix and modify vanilla Quake.\n\n- `quakec/csaddon:` In-game CSQC-controlled editors. Currently contains the camquake featureset (thanks Jogi), rtlights editor, terrain editor ui, particle editor.\n\n- `quakec/csqctest:` Spike's CSQC sample mod. Originally created as a feature testbed for the CSQC API. Useful as a reference/sample, but you perhaps don't want to use it as a base.\n\n- `specs:` Modder/Advanced documentation and samples.\n\n# Compiling\n\nCompiling FTEQW is straightforward once you have the bare minimum of build dependencies (see `Dependencies.md` for more info).\n\nFor the binaries hosted here, we choose to statically link against many of the dependencies for portability reasons, while also linking against recent versions \nof libc for security reasons.\n\nAll binaries hosted here were built inside the `engine` dir of the src tree using GNU make, aka gmake.\n\n### Build Systems\n\nYou have the choice of two build systems:\n\n- Make\n- CMake\n\nThis guide will show commands for both.\n\n## Easy Build Bot System (Linux)\n\nIf you want to set up a Linux box that cross-compiles each target with your own private customisations, then you can run the `build_setup.sh` script to set up which targets you wish to support.\n\nYou can then just run the `build_wip.sh` script any time your code changes to have it rebuild every target you previously picked.\n\nThe setup script will install **android+emscripten** dependancies for you, so you're likely to find this an easier way to deal with those special targets.\n\n### Notes\n\n- The Android SDK can be a big download, while installing emscripten may require several hours to compile clang and about **40gb** of disk space if emscripten doesn't provide prebuilt stuff for your distro.\n\n- The script can also be run from cygwin, but does not support compiling for Linux then.\n\n## Windows Systems (cygwin)\n\nIf you want to compile a Win64 build in cygwin, it should be as simple as:\n\n\tmake makelibs FTE_TARGET=win64\n\tmake gl-rel FTE_TARGET=win64\n\nor\n\n\tTODO\n\nYou only should need gcc and make installed in cygwin for this.\n\n### Notes\n\nIt's currently not recommended to build using MSYS2, due to issues with zlib.\n\n## Linux/BSD Systems\n\nIt's usually as straight-forward as:\n\n\tmake makelibs\n\tmake m-rel\nor\n\n\tTODO\n\n### Notes\n\n- You can also change `FTE_TARGET` to be `win32`, `SDL2` and on Linux systems `linux32` and `linux64`.\n\n- On BSD, you don't need to pass anything specific, but they should also compile the `linux` targets as well as the `SDL2` target fine.\n\n- Not building with `makelibs` will attempt to dynamically link against your system-level versions of dependencies.\n  Sometimes you want this, sometimes you don't. You definitely want that if you're trying to link against the Steam runtime.\n\n##  Renders\n\n### Vulkan\n\n\tmake vk-rel\n\n### OpenGL \n\n\tmake gl-rel\n\n\tmake glcl-rel\n\n\tmake mingl-rel\n\nmake mcl-rel\n\n### DirectX\n\n\tmake d3d-rel\n  \n## Android (FTEDroid) with cygwin\n\nThe phone port requires the Android SDk and can be compiled with the following command:\n\n\tmake droid-rel PATH=C:\\Cygwin\\bin\\ DROID_SDK_PATH=/path/to/android-sdk DROID_NDK_PATH=/path/to/android-ndk-r7 ANT=/path/to/apache-ant-1.8.2/bin/ant JAVATOOL=\"/path/to/jdk1.7.0_02/bin/\" DROID_ARCH=\"armeabi x86\" -j4 DROID_PACKSU=/path/to/pak0.pak\n\nor\n\n\t-DFTE_ENGINE_FTEDROID=TRUE\n\nOn Linux/Unix systems you can omit the `PATH`, `ANT`, and `JAVATOOL` parts as they should already be in the path.\n\nThe `DROID_PACKSU` part is used to include the PAK file within the android package. Ideally you would use a PK3 file instead. \n\nAlso you would use something that will not violate id Software's copyright. THIS IS AN EXAMPLE ONLY. You can omit the setting entirely if you require the user to provide their own packages.\n\nFinally, install the `FTEDroid.apk` file on your Android device which should be located under the `release` folder.\n\n### Notes\n\n- There is no way to install the package with a different name at this time.\n\n- Touchscreen controls are built-in.\n\n- The APK looks for game data under:\n\n\tAndroid/data/com.fteqw/files\n\n- Configs may be located at:\n\n\t/fte\n\nor\n\n\tsdcard/fte\n\n## Browser (emscripten)\n\n\tmake FTE_TARGET=web web-rel\n\nor\n\n\t-DFTE_PLUG_CEF=TRUE\n\n## FTEQCC\n\n\tmake qcc-rel\n\nor\n\n\t-DFTE_TOOL_QCC=TRUE\n\n## FTEQCC GUI\n\n\tmake qccgui-rel\n\nor\n\n\t-DFTE_TOOL_QCCGUI=TRUE\n\n\n## Standalone QCVM\n\n\tTODO\n\nor\n\n\t-DFTE_TOOL_QCVM=TRUE\n\n## FTE Dedicated Server\n\n\tmake sv-rel\n\nor\n\n\tFTE_ENGINE_SERVER_ONLY=TRUE\n\n## FTE Master Server\n\n\tmake master-rel\n\nor\n\n\t-DFTE_TOOL_MASTER=TRUE\n\n## FTE Image Tool\n\n\tmake imgtool-rel\n\nor\n\n\t-DFTE_TOOL_IMAGE=TRUE\n\n## FTE IQM Tool\n\n\tmake iqm-rel\n\nor\n\n\t-DFTE_TOOL_IQM=TRUE\n\n## FTEQTV\n\n\tmake qtv-rel\n\nor\n\n\t-DFTE_TOOL_QTV=TRUE\n\n## Small HTTP Server\n\n\tmake httpserver\n\nor\n\n\t-DFTE_TOOL_HTTPSV=TRUE\n\n## Plugins\n\nTo build all currently stable plugins, it's as simple as:\n\n\tmake plugins-rel\n\nor\n\n\tTODO\n\nYou can specify which plugins get compiled by passing PLUGINS_NATIVE as an example:\n\n\tmake plugins-rel NATIVE_PLUGINS=\"ffmpeg bullet irc\"\n\nor\n\n\t-DFTE_PLUG_FFMPEG=TRUE\n\t-DFTE_PLUG_BULLET=TRUE\n\t-DFTE_PLUG_IRC=TRUE\n\nThe list of available plugins:\n\n> [!IMPORTANT] \n> Some plugins will require additional dependencies or flags on some systems, see `Dependencies.md` for more info.\n\n- Bullet Physics\n> Provides Rigid Body Physics.\n\n\tbullet\n\t-DFTE_PLUG_BULLET=TRUE\n\n- Call of Duty (1 & 2) Format Support\n> Provides compatability with Call Of Duty's file formats.\n\n\tcod\n\t-DFTE_PLUG_COD=TRUE\n\n- EzHUD\n> Provides compat with ezquake's hud scripts.\n\n\tezhud\n\t-DFTE_PLUG_EZHUD=TRUE\n\n- FFMPEG Video Decoding & RTMP Streaming\n> Provides support for more audio formats, as well as video playback and better capture support.\n\n\tffmpeg\n\t-DFTE_PLUG_FFMPEG=TRUE\n\n-  GnuTLS\n> Provides GnuTLS support for dtls/tls/https support. The crypto library that is actually used is controlled via the tls_provider cvar.\n\n\tgnutls\n\t-DFTE_PLUG_GNUTLS=TRUE\n\n- Half-Life 2\n> Adds support for reading various file formats used by Half-Life 2.\n\n\thl2\n\t-DFTE_PLUG_HL2=TRUE\n\n- IRC\n> Allows you to chat on IRC without tabbing out.\n\n\tirc\n\t-DFTE_PLUG_IRC=TRUE\n\n- libcef(Browser) Plugin\n>This plugin provides support for an in-game web browser.\n\n\tlibcef\n\t-DFTE_PLUG_CEF=TRUE\n\n- Name Maker Plugin\n> Provides a lame UI for selecting arbitrary non-ascii glyphs as part of your nickname.\n\n\tnamemaker\n\t-DFTE_PLUG_NAMEMAKER=TRUE\n\n- MPQ Archive Plugin\n> Adds support for reading .mpq files (Diablo 1 + 2, World of Warcraft).\n\n\tmpq\n\t-DFTE_PLUG_MPQ=TRUE\n\n- ODE Physics\n> Provides Rigid Body Physics behaviours.\n\n\tode\n\t-DFTE_PLUG_ODE=TRUE\n\n- OpenSSL\n> Provides OpenSSL support for dtls/tls/https support. The crypto library that is actually used is controlled via the tls_provider cvar.\n\n\topenssl\n\t-DFTE_PLUG_OPENSSL=TRUE\n\n- OpenXR Support\n> Provides support for Virtual Reality headsets and input devices.\n\n\topenxr\n\t-DFTE_PLUG_OPENXR=TRUE\n\n- Quaddicted Map Database (Quake Injector)\n> Provides easy access to the quaddicted map database. Once installed you can use eg 'map qi_dopa:start' to begin playing dopa, or load it via the menus.\n\n\tqi\n\t-DFTE_PLUG_QI=TRUE\n\n- Quake 3 Game Logic and VM Support\n> Provides compatability with Quake3's gamecode.\n\n\tquake3\n\t-DFTE_PLUG_QUAKE3=TRUE\n\n- TerrainGen Plugin\n> A lame example plugin for randomised terrain generation.\n\n\tterraingen\n\t-DFTE_PLUG_TERRAINGEN=TRUE\n\n- Timidity Plugin\n> Provides support for playback of midi files.\n\n\ttimidity\n\t-DFTE_PLUG_TIMIDITY=TRUE\n\n- XMPP/Jabber Protocol Support\n> XMPP/Jabber instant messenger plugin for chatting without tabbing out.\n\n\txmpp\n\t-DFTE_PLUG_XMPP=TRUE\n\n- X11 Display Server (Standalone)\n> Provides a primitive X11 server in the form of a video decoder plugin.\n\n\tx11server\n\t-DFTE_PLUG_X11SV=TRUE\n"
  },
  {
    "path": "documentation/Credits.md",
    "content": "# Credits\n\n### Developers\n\n- **[Spike](https://github.com/Shpoike)** - Creator of FTEQW & FTEQCC\n- **[eukara](https://github.com/eukara)** - Maintainer, QuakeC guru\n\n### Contributors\n\n- **[4LT](https://github.com/4LT)** - QC bugfixes\n- **Andreas Kirsch** - QC Additions & bugfixes\n- **[Blub](https://github.com/blubs)** - IQM & QC bugfixes\n- **Circlemaster** - Demo & Console additions\n- **[Daniel Svensson](https://github.com/dsvensson)** - Github Actions & Bugfixes\n- **[erysdren](https://github.com/erysdren)** - Haiku OS port\n- **[ewhac](https://github.com/ewhac)** - Joystick bugfixes & .gitignore\n- **[Fix](https://github.com/fhomolka)** - Cleanup & Bugfixes\n- **Hexum** - Linux Port & Fixes\n- **James \"Ender\" Brown** - Half-Life Model Support\n- **Jogi** - Menu Bugfixes\n- **[JohnNy_cz](https://github.com/johnnycz)** - Bugfixes\n- **KrimZon** - GLSL Additions & Bugfixes\n- **Lance \"Moodles\"** - Many Additions & Bugfixes\n- **Luis Gutierrez** - IQM Fixes\n- **Magnus**\n- **Mark Olsen** - Many Additions & Bugfixes\n- **Molgrum** - Many Additions & Bugfixes\n- **Theuaredead** - Extensive Win32 Bugtesting\n- **TimeServ** - Many Additions & Bugfixes\n- **[Xylemon](https://github.com/Xylemon)** - Technical support, Enthusiast\n"
  },
  {
    "path": "documentation/Dependencies.md",
    "content": "# Dependencies\n\nHere is a list of dependencies required for building FTEQW on several platforms.\n\n## Debian / Raspbian\n\n### Base\n\n\tapt-get install libgl-dev gnutls-dev\n\n### SDL2\n\n\tapt-get install libsdl2-dev\n\n### GLX / X11 (part of libsdl2-dev)\n\n\tapt-get install libx11-dev libxcursor-dev libxrender-dev\n\n### Plugin: ODE\n\n\tapt-get install autoconf automake libtool\n\n### Plugin: FFMPEG\n\n\tapt-get install libavformat-dev libswscale-dev\n\n## OpenBSD\n\n### SDL2\n\n\tpkg_add sdl2\n\n### Plugin: FFMPEG\n\n\tpkg_add ffmpeg\n\n## Arch Linux\n\n### Base\n\n\tpacman -S make gcc Xorg\n\n### Plugin: ODE\n\n\tpacman -S zip automake autoconf\n\n### Plugin: FFMPEG\n\n\tpacman -S ffmpeg4.4\n\nYou must pass these flags to compile the plugin:\n\n\tmake plugins-rel NATIVE_PLUGINS=\"ffmpeg\" AV_BASE=/usr/include/ffmpeg4.4/ AV_LDFLAGS=\"-l:libavcodec.so.58 -l:libavformat.so.58 -l:libavutil.so.56 -l:libswscale.so.5\"\n\n### SDL2\n\n\tpacman -S sdl2\n\n## OpenSUSE\n\n### Base\n\n\tzypper in make gcc gcc-c++ mesa-libGL-devel libgnutls-devel alsa-devel libopus-devel speex-devel libvorbis-devel\n\n### SDL2\n\n\tzypper in libSDL2-devel\n\n### GLX / X11\n\n\tzypper in libX11-devel libXcursor-devel libXrandr-devel\n\n### Plugin: ODE\n\n\tzypper in autoconf automake libtool zip\n\n### Plugin: FFMPEG\n\n\tzypper in ffmpeg-4-libavformat-devel ffmpeg-4-libswscale-devel\n\n## Fedora\n\n\tdnf install make gcc gcc-c++ mesa-libGL-devel gnutls-devel alsa-devel libopus-devel speex-devel libvorbis-devel\n\n### SDL2\n\n\tdnf install SDL2-devel\n\n### GLX / X11 (part of libsdl2-dev)\n\n\tdnf install libX11-devel libXcursor-devel libXrender-devel\n\n### Plugin: ODE\n\n\tdnf install autoconf automake libtool zip\n\n### Plugin: FFMPEG\n\nYou need to install the RPM Fusion repo if you don't have it. We recommend reading their official guide: https://rpmfusion.org/Configuration\n\nThen you can install the required version of FFMPEG:\n\n\tdnf install compat-ffmpeg4-devel\n\nFinally, you must pass these flags to compile the plugin:\n\n\tmake plugins-rel NATIVE_PLUGINS=\"ffmpeg\" AV_BASE=/usr/include/compat-ffmpeg4 AV_LDFLAGS=\"-l:libavcodec.so.58 -l:libavformat.so.58 -l:libavutil.so.56 -l:libswscale.so.5\"\n"
  },
  {
    "path": "documentation/QuickStart.md",
    "content": "# FTEQW README\n\nThis file contains the following sections:\n\n- [0. PREFACE](#0-preface)\n- [1. ABOUT](#1-about)\n- [2. FEATURES](#2-features)\n- [3. INSTALLING](#3-installing)\n- [4. NETWORK QUICK-START](#4-network-quick-start)\n- [5. TWEAKS](#5-tweaks)\n- [6. TROUBLESHOOTING](#6-troubleshooting)\n- [7. FURTHER READING](#7-further-reading)\n- [8. CONTACT](#8-contact)\n- [9. LICENSE](#9-license)\n\n\n# 0. PREFACE\n\nThank you for downloading FTEQW, we hope you enjoy the many advanced features and ways to play. We understand the engine can be a bit daunting at times, so we hope this will help you get up and running with little to no hasle.\n\n# 1. ABOUT\n\nFTEQW is an advanced portable Quake engine.\n\nIt supports multiple games running on idTech, plus its own set of\ngames that developers have created. Due to the vast amount of supported\nformats, features and innovations inside the engine and its very own\nQuakeC compiler (fteqcc), it's very much considered the swiss-army\nknife of Quake engines.\n\n# 2. FEATURES\n\n- Portable engine that runs on x86, amd64, ARM/64, PPC64LE and Web\n- Hybrid protocol engine that supports multiple games\n- Rendering API support for D3D8, D3D9, D3D11, OpenGL, Vulkan\n- Splitscreen support for Quake and most mods\n- In-game voice chat powered by either Opus or Speex\n- Advanced renderer features, powered by a strong material system\n  and support for both HLSL and GLSL shader code\n- Multiple audio backends, from OSS to SDL_Sound and OpenAL Soft,\n  plus API so developers can take advantage of AL EAX reverb features.\n  And yes, DirectSound.\n- Integrated next-generation QuakeC compiler and debugger\n  with support for breakpoints, real-time ingame attribute debugging\n  and much more\n- Support for IPv4 and IPv6\n- Video output presets, to make your games either look like the\n  original versions, or more modern with real-time lighting and more\n  accurate shading\n- Support for CD-DA/Red Book music replacement in a variety of formats,\n  such as Vorbis, MPEG-3, WAVE and FLAC (ffmpeg plugin required)\n\n# 3. INSTALLING\n\nPut the engine binary and desired plugins for your platform into the\nroot of the game directory.\n\nSupported games:\n\n- Quake and its Missionpacks\n- QuakeWorld\n- Quake II and its Missionpacks\n- Quake III Arena and Team Arena\n- HeXen II and its Missionpack\n- Half-Life 1 & 2 (via the Rad Therapy project)\n\nIf you want to be explicit about the game you're starting, you can pass\nthe command-line parameters (read below for a complete list) for the\nrespective game. This will make sure that in a crowded universal game\ndirectory, FTE starts the right game.\n\nIf you want to install music replacement files, you put them into the\n`music` folder with the `trackXX` naming convention, starting with `track02`.\n\n## Important note regarding Quake II based support\n\nIf you're running a 64-bit version of FTEQW, then you also need 64-bit\ngame-logic for Quake II. We recommend getting the game .dll/.so from the\nYamagi Quake II project for your respective platform. It's recommended\nthat you do for win32 as well, as that will ensure that save games work\nproperly and you can stop worrying about them becoming incompatible\nbetween other machines.\n\n## Android Installation\n\nWhen using the Android app, you need to copy your gamedata folders\nin the `Android/data/com.fteqw/files` folder.\n\nAn install of Quake should look like this for example:\n\n\tAndroid/data/com.fteqw/files/id1/pak0.pak\n\tAndroid/data/com.fteqw/files/id1/pak1.pak\n\n> [!WARNING] \n> FTEQW is included in other Android APKs such as idTech4a++,\n> please consult their documentation and help forums as we do not\n> provide support to these unofficial third-party ports.\n\n## Steam\n\nFTEQW can detect and load data from the `/SteamApps/common/GAMEHERE` folders.\nThis includes the Quake I and II Remasters.\n\n## Linux/Unix System Games Folder\n\nFTEQW supports loading the legacy Unix game folders and will look for\ngamedata in the following folders:\n\n- /usr/share/games/quake\n- /usr/share/games/quake2\n- /usr/share/games/quake3\n- /usr/share/games/hexen2\n- /usr/share/games/halflife\n- /usr/share/games/halflife2\n\n## 4. NETWORK QUICK-START\n\nUpon launching FTEQW, you can use the multiplayer menu's own built-in\nserver browser to join and connect to a vast array of matches across all\nthe different protocols. No more QuakeSpy/GameSpy 3D client required!\n\nIf you want to host a game, you can either run a listen server with\nthe ports forwarded, or host a listen server using frag-net.com's online\nservice. Start a new multiplayer server, change the \"Public\" setting to\n\"Holepunch\" and players will automatically see your game in their\nserver-browser once it's started.\n\nYou can also set FTEQW to run in a terminal/command-prompt for hosting\na dedicated server session.\nSimply pass the command-line argument like so:\n\n\tfteqw -dedicated\n\nThis will create an interactive shell reminiscent to the console that's\naccessible in-game.\n\nYou can host a game directly with frag-net.com without having to worry\nabout port-forwarding and have a map up and running like so:\n\n\tfteqw +set sv_public 2 +set sv_playerslots 8 +map dm4\n\n# 5. TWEAKS\n\nYou can apply tweaks by opening the console (SHIFT+ESC) and entering\ncommands into the line buffer. In there you can enter console variables\n(cvars) that affect how the game behaves, as well as enter console\ncommands that trigger an action in the engine. \n\nYou can always find a list of both of these with the console commands \n`cvarlist` and `cmdlist` respectively.\n\n## Some example commands\n\n`bind <key> <command>`\tBinds a command to a key, e.g. `bind F12 quit`\n`map <mapname>`\t\tStarts a new game on <mapname>, e.g. `map dm4`\n`connect <address>`\tEstablishes a connection to the specified \n\t\t\tIP/hostname address.\n`disconnect`\t\tCloses and remote or local game session.\n`cfg_save`\t\tSave amy unsaved configuration changes.\n`quit`\t\t\tQuits the game.\n\n## About console variables\n\nUnlike other Quake engines, FTEs console variable system is more akin to\nthose of later idTech engines. Console variables can be set temporarily\n(until the next engine restart) or permanently.\n\n`set sv_port 26000`\tSets the current port to 26000 for this session\n`seta sv_port 26000`\tSets the current port gets set to 26000 and makes\n\t\t\tsure it will get \"archived\", aka saved.\n\nSometimes, you'll be able to change a cvar without entering `set` or\n`seta` beforehand. This is due to the cvar/cmd suggestion system.\nWhen you simply set a cvar that way, `set` is assumed.\nSo those changes are only temporary.\n\n## Commandline arguments\n\nYou can pass cvars and commands directly to the engine binary, prefixed\nwith a plus symbol like so:\n\n\tfteqw +set sv_port 26000 +map dm4\n\nThere's also other interesting commandline only arguments you can pass:\n\n`-nohome`\t\tDon't attempt to save configs, saves, screenshots in the\n\t\t\thome or user directory.\n`-basedir <path>`\tSpecifies the root game directory path.\n`-basegame <dir>`\tSpecifies which folder to look in for main game data.\n`-game\t<dir>`\t\tSpecifies which mod folder to load over the game data\n`-window`\t\tTells the renderer to run in a window\n`-manifest <fmf>`\tSpecifies a game manifest to load.\n\t\t\tThis is for advanced game and mod switching.\n`-dedicated`\t\tRun the engine in dedicated server mode, no video out.\n\n## Changing games and mods\n\nLaunching a Quake 1 mod can be done like so:\n\n\tfteqw -game fortress\n\nThat will assume a basegame of `id1` and the mod `fortress` will be\nloaded on top of it.\n\nHowever, if you're playing a game that uses no data from Quake 1:\n\n\tfteqw -basegame openquartz\n\nThen it'll never even touch or peek into the folder `id1`.\nOf course you can pass `-game` after that, too.\n\nIf you want to specify a supported game for more accurate behavior\nand automatic data path finding, then you can pass these arguements\nto load a specific game and behaviour:\n\n\t-quake\n\t-quake2\n\t-quake3\n\t-hexen2\n\t-halflife\n\t-halflife2\n\n> [!NOTE]\n> Half-Life 1 & 2 will download the Rad-Therapy project in place of their game logic, and HL2 also requires the Source formats plugin.\n\n## Understanding manifests (advanced users)\n\nYou can setup custom game configurations with FTE's manifest files.\nThose can be quite advanced, as they might inherit multiple directories,\nchange the name of the window title, set binds, aliases and cvars ahead\nof time and much more.\n\n\tFTEMANIFEST 1\n\tGAME funky\n\tNAME \"Funky QW Game\"\n\tBASEGAME id1\n\tBASEGAME qw\n\tBASEGAME funky\n\nAn example manifest that will load id1, qw and then funky.\nIf you wanted, you can set default cvars and aliases in there like so:\n\n\t-set sv_example 1234\n\t-seta cl_foobar 5678\n\t-alias funky1 \"impulse 416\"\n\t+bind g funky1\n\nNote the dash and plus symbols, they actually notate when to execute the\ncommand in question. '-' means before the engine loads the game config\nwhereas '+' notates it will override anything that will usually be set\nby the game.\n\nYou'd then save this as, for example, funky.fmf and load the manifest\nvia the command-line:\n\n\tfteqw -manifest funky.fmf\n\n# 6. TROUBLESHOOTING\n\nIf you're running FTEQW on an older machine with Intel GMA graphics,\nyou probably want to try running the engine in D3D9 mode if you're\nencountering any graphical issues:\n\n\tfteqw +set vid_renderer d3d9\n\nIf you can only run OpenGL but still have graphical issues, try forcing\nsupport for the builtin GLSL off:\n\n\tfteqw +set gl_blacklist_debug_glsl 1\n\nIf FTEQW is seemingly not saving your settings, make sure you tell it to\nsave your config when you quit the game. If it still does not work\nsomehow, enter the console command `cfg_save` into the console. It\nshould output where the file gets saved or if there's any problems\nwriting the configuration file.\n\nIf OpenAL is causing crashes at launch (happens with some distributions,\nthat is out of our control) then try starting FTEQW with:\n\n\t fteqw +set s_al_disable 1\n\n# 7. FURTHER READING\n\nHere's a small selection of some links we recommend for more info on FTEQW:\n\n- [Spirit's excellent FTEQW wiki](https://quakewiki.org/wiki/FTEQW_Wiki)\n- [@eukara's must read FTEQCC manual/QuakeC help](https://icculus.org/~marco/txtfiles.html)\n- [@Shpoike's old FTEQW website and wiki](https://fte.triptohell.info/wiki/index.php/Main_Page)\n\n# 8. CONTACT\n\nIf you need more help, have suggestions or want to hang out with the\ndevelopers that make FTEQW what it is, join us on a platform listed below!\n\nBug reports are welcomed! See our [public bug tracker](https://github.com/fte-team/fteqw/issues).\n\n### Matrix\n\nhttps://matrix.to/#/#fte:matrix.org\n\n### IRC\n\n**Server:** irc.quakenet.org\n\n**Channel:** #fte\n\n### Forums\n\n**[Spike](https://forums.insideqc.com/memberlist.php?mode=viewprofile&u=26)** and **[eukara](https://forums.insideqc.com/memberlist.php?mode=viewprofile&u=949)** can be found on [insideqc.com](https://forums.insideqc.com/)\n\n### Discord\n\nhttps://discord.gg/p2ag7x6Ca6\n\n# 9. LICENSE\n\nCopyright (c) 2004-2025 FTE's team and its contributors\nQuake source (c) 1999 id Software\n\nFTEQW is supplied to you under the terms of the same license as the\noriginal Quake sources, the GNU General Public License Version 2.\nPlease read the `LICENSE` file for details.\n\nThe latest source & binaries are always available at:\n\n[fteqw.org](https://fteqw.org)\n\n[fteqcc.org](https://fteqcc.org)\n"
  },
  {
    "path": "documentation/Tools.md",
    "content": "# FTE Tools README\n\n# Preface\n\nThis file covers the tools include with the FTEQW source code.\n\nFTEQW features a set of tools for compiling and editing QuackeC, textures, models, packages, and other formats associated with idTech.\n\n### Contents\n\n- [FTEQCC](#fteqcc)\n- [IQM Tool](#iqm-tool)\n- [Image Tool](#image-tool)\n- [License](#license)\n\n# FTEQCC\n\nFTEQCC is the defacto compiler for FTE's unique variant of QuakeC called, **FTEQC**.\n\nIt is (generally) backwards compatible with standard QuakeC and is used to compile code usually found in the `src` folder. It will output a memory-safe and architecture independent `progs.dat` that is loaded in a virtual machine within the engine.\n\nClient-side progs are also able to be compiled if the code-base supports it into a `csprogs.dat` file.\n\nAnother unique feature of FTEQC is the concept of \"objects\", similar to other C-like languages such as C++ or Obj-C.\n\nSome games (HeXen II) and the Nuclide SDK have a concept of multi-progs, meaning uniquely named progs can be arbitrarily compiled and loaded alongside the main progs.\n\nFull FTEQCC documentation can be found [here](https://fte.triptohell.info/moodles/fteqcc/README.html)\n\n> [!TIP]\n> You can also compile within FTEQW itself by typing `compile` in the console (must have a `src` folder in the mounted directory)\n\n### GUI\n\nThere is a GUI available for FTEQCC built on the Qt toolkit. It is incomplete and features vary by platform.\n\n### Package Management\n\nThe commandline version of FTEQCC doubles up as a PAK/PK3 creator or extractor:\n\n\tfteqcc -l PACKAGENAME\n\nLists the contents of a file on the stdout.\n\n\tfteqcc -x PACKAGENAME\n\nExtracts all files from the named package to the working directory. You should normally use `../foo.pak` in order to avoid overwriting files unintentionally.\n\n\tfteqcc -x PACKAGENAME SUBDIR\n\nExtracts specific files from the named package.\n\n\tfteqcc -0 SUBDIR\n\nGenerates a `subdir.pak` file with the contents of that subdir. Unlike most pak generators, the pak will also be openable with any zip tool for users to easily view files (they shouldn't edit them though).\n\n\tfteqcc -9 SUBDIR\n\nGenerates a `subdir.pk3` file with the contents of that subdir. Contents will be compressed to reduce filesize, but won't work in vanilla quake.\n\n\tfteqcc -z SUBDIR\n\nGenerate a `subdir.pk3` dictionary file, and an (additional) `subdir.pXX` file (name generated sequentially). The dictionary file will be versioned with different servers potentially using different versions. This prevents re-downloading redundant copies of the same files for each different version. The spanned data files are not versioned, so these should only be generated by authoritive developers (ie: not same-filename forks).\n\nMany engines support interpreting `foo.pk3dir` subdirs as proto-packages, which makes it easier to test packages without constant re-compression.\n\n# IQM Tool\n\n### About:\n\nThis is a commandline tool for creating iqm files from intermediate files exported from modelling programs.\n\n### Possible Inputs:\n\n- SMD\n- GLTF\n- GLB\n- IQE\n- MD5Mesh\n- MD5Anim\n- FBX\n- OBJ\n\n### Outputs:\n\n- IQM / VVM\n\nThe default format. IQM supports skeletal animations and other features used by modern model formats.\n\n**VVM** is a moniker for the backwards-compatible extensions FTE supports which are not supported by other engines/tools. Think of it as a \"fork\" of the IQM model format.\n\nThe **VVM** variant was created by Vera Visions hence the name, \"Vera Visions Model\".\n\n- MDL\n\nQuake's model format (idTech 2). It is vertex animation based and has many limitations, therefore it's recommend to use IQM / VVM unless you are targeting stock Quake.\n\n> [!WARNING]\n> While Half-Life 1 & 2 also use the `.mdl` extension, it is a different format altogether and _not yet_ supported by IQM Tool.\n\n### Usage:\n\nIQM Tool Can be used two ways:\n\n\tiqm foo.cmd\n\nUses a command script to decide which files to read, modifiers for each sequence or mesh, etc.\n\n\tiqm foo.iqm foo.gltf\n\nConverts the gltf file to an iqm file. Animation rate will be guessed.\n\n\tiqm foo.mdl foo.gltf\n\nWrites out a Quake MDL file. Framegroups will be used for each animation sequence.\n\n### Command script:\n\n\t#COMMENT\n\t//COMMENT\n\nJust a comment, ignored.\n\n\texec filename\n\nInvokes an external script (useful when you have multiple models with the same animations or so).\n\n\tmodelflags BITS\n\nSets the output model's flags. Queryable in gamecode.\n\n\tmesh MESHNAME ATTRIBUTELIST\n\nOverrides attributes of an imported mesh to use for the visible part of the model. Use the `import` command to actually import the geometry.\n\nAttributes are:\n\n\tcontents BITSORNAMES\n\nControls which traces may impact the imported surfaces. You should normally specify 0 if you are using hitboxes. Default is CONTENTS_BODY.\n\n\tsurfaceflags BITSORNAMES\n\nMisc info eg reported by tracelines.\n\n\tbody NUM\n\nThe 'body' number which can be queried in gamecode (typically used to report eg headshots).\n\n\tgeomset SET ID\n\nSets this mesh to only draw when geomset SET has been configured as ID.\n\n\tlodrange MIN MAX\n\nThis mesh will only be drawn when the screen coverage is within the specified range. Exact interpretation can vary according to engine cvars.\n\n\thitbox BODYNUM BONENAME MINS MAXS\n\nCreates a cuboid mesh around the named bone (as a box in the base pose). The mesh will be given CONTENTS_BODY such that it will be hit by hitmesh traces.\nThe bodynum arg can be queried via gamecode.\n\n\tbone NAME ATTRIBUTELIST\n\n\trename NEWNAME\n\nThe bone read from input files will be written as NEWNAME in the output file (so gamecode can manipulate things consistently).\n\n\tgroup GROUPNUM\n\nBones can be reordered in the output according to their group numbers. Only the root bone of each group needs its group specified (children will inherit).\nThis simplies bone range operations in gamecode\n\n\timport FILENAME ATTRIBUTELIST\n\tmodel FILENAME ATTRIBUTELIST\n\tscene FILENAME ATTRIBUTELIST\n\tanimation FILENAME ATTRIBUTELIST\n\nLoads model AND animation data from an imported file.\n\nFile formats must match the given filename extensions.\n\nAttributes are:\n\n\n\tAny attributes supported by the mesh command (such attributes here define the default values for imported meshes).\n\tname NEWNAME\n\n\nThe animation will be queryable to gamecode as the NEWNAME.\n\n\tfps RATE\n\nOverrides the frame rate of the animation.\n\n\tloop\n\nSpecifies that the animation must loop.\n\n\tclamp\n\nSpecifies that the animation must NOT loop (animation will stop once it reaches the last pose).\n\n\tunpack\n\nGenerates single-pose animations, consistent with the way vanilla QC animates.\n\n\tpack\n\nGenerates multiple-pose animations, gamecode will be able to specify animation and time separately.\n\n\tnomesh\n\nDo NOT generate meshes from this file.\n\n\tnoanim\n\nDo NOT generate animations from this file.\n\n\tmaterialprefix PATH\n\nPrefixes the imported texture with an additional path (to avoid conflicts with other files).\n\n\tstart FRAME\n\tend\tFRAME\n\nLimits inputed data to the specified poses.\n\n\tscale SCALE\n\nResizes the input data.\n\n\trotate PITCH ROLL YAW\n\nRotates the imported data (quake is +x=forward, +y=left, +z=up).\n\n\ttranslate X Y Z\n\nMoves the mesh+bones around a bit.\n\n\tevent [SEQUENCE:]POSE EVCODE EVSTRING\n\nDefines a model event at the specified timestamp.\n\n\toutput FILENAME\n\toutput_iqm FILENAME\n\toutput_vvm FILENAME\n\toutput_qmdl FILENAME\n\toutput_md16 FILENAME\n\nSpecifies the file to be written. You may have one per supported output model format, each will contain roughly equivelent data (where supported).\n\n# Image Tool\n\n### About:\n\nThis is a tool for converting various image file types to more favourable ones.\nThis includes generating wad files, cubemap images, etc.\n\n### Wad Examples:\n\n\timgtool --ext mip [--PIXFMT] [--resize W H] *.EXT\n\nConverts the input files to quake's miptex format. Input files will not be changed.\n\nIf compression was specified, the resulting miptex will contain an additional high-colour alternative.\n\nIf `--resize` is used, the paletted data will be resized without affecting the high-colour alternative.\n\nSupported pixel formats:\n\n\trgba8,rgb8, rgb565,rgba5551,rgba4444, l8, bc1-7, etc1,etc2,etcp,etca, astc*x*, e5bgr9\n\n> [!NOTE] \n> Use of alternative formats requires a qbsp which does NOT strip this information. \n> The vanilla qbsp will work fine for this purpose, but more advanced qbsp utils have a tendancy to strip the info (including ericw's sadly).\n\n\timgtool -w WADFILE *.mip\n\nPacks the specified miptex files into the named wad file.\n\n\timgtool -x --ext EXT WADORBSP\n\nExtracts the textures from the specified wad or bsp file, saving them as EXT files.\n\n### General Examples:\n\n\timgtool --help\n\nShows a list of compressed pixel formats, and supported file formats.\n\n\timgtool -i FILE\n\nShows info about the named file(s).\n\n\timgtool --ext ktx [--PIXFMT] *.EXT\n\timgtool --ext dds [--PIXFMT] *.EXT\n\nConverts the input files to a hardware-friendly file format (with the specified pixel format). Input files will not be changed.\n\nktx supports all recognised hardware formats. dds is more limited (and eg doesn't support etc or astc).\n\nHUD artwork should generally be astc4x4 or bc7 for best quality.\n\nmodels and walls can be of lower quality, astc6x6, etc2, or bc1 are good choices.\n\nfor hdr pixel formats, try astc4x4_hdr or bc6.\n\t\n\timgtool --ext png *.EXT\n\nConverts the input files to png format for viewing/editing. Input files will not be changed.\n\n\timgtool --cube [--PIXFMT] -o mysky.dds mysky_*.tga\n\nConverts 6 input files to a single cubemap file.\n\nInputs will be reordered according to quake's typical cubemap postfixes (and flipped as appropriate), otherwise be careful with wildcards.\n\n\timgtool --2darray [--PIXFMT] -o foo.ktx IDX0 IDX1 IDXN\n\nConverts the input textures into a 2d texture array. Input file order matters.\n\t\n\timgtool --3d [--PIXFMT] -o foo.ktx LAYER0 LAYER1 LAYERN\n\nConverts the input textures into a 3d texture. Input file order matters.\n\n### Which pixel format to use:\n\n### ASTC:\n\n- The ldr-only profile is part of the gles3.2 spec (but likely to be emulated on nvidia).\n- ASTC has a range of block sizes with each block being 16 bytes, thus larger block sizes yield greater compression.\n- This format supports multiple planes, which reduces issues with multiple gradients in a block, which means it can cope with pixel art better than eg s3tc with larger block sizes.\n- Bits are distributed according to usage per block, this means it can use more bits for the rgb channels where alpha is constant, giving it more useful bits than eg bc3.\n- ASTC is able to use a second set of weights for any single channel (not just alpha), which makes it suitable for encoding normalmaps for instance.\n- To encode ASTC pixel formats, you will need to install astcenc - https://github.com/ARM-software/astc-encoder/releases (or use astc-encoded source files).\n\n### ETC2:\n\n- Part of the gles3.0/gl4.3 spec\n- ETC2 is a superset of ETC1, and has been somewhat obsoleted by ASTC.\n\n### BC1/2/3:\n\n- AKA s3tc, AKA dxt, available only via optional extensions, but is mandated by d3d 9_1 feature level.\n- These formats all encode two 565 colours per 4x4 block with 2-bits per pixel for interpolation, which can result in discolouration.\n- bc2 uses an additional 64bit block to encode 4bit alpha.\n- bc3 also uses an additional block for alpha, but does so simiarly to its rgb values (two 8bit alpha values, with 3-way interpolation).\n- To encode s3tc pixel formats, you will need to install libnvtt-bin (or export from image editors as dds or ktx files).\n\n### BC4/5:\n\n- AKA rgtc, part of the gl3.0 spec, or d3d10.\n- These formats reuse bc3's 'alpha' compression to encode one or two channels respectively. BC1 can be handy for heightmaps/greyscale/etc, while BC5 can be useful for 2-channel normalmaps. They are not generally useful as eg wall textures.\n\n### BC6/7:\n\n- AKA bptc, part of the gl4.2, or d3d11.\n- These formats support one or two planes per block, which means they can cope with pixel art quite a bit better than bc1/3.\n- While they're the same size as bc3 (twice that of bc1), they have multiple modes per block that allow them to eg avoid wasting bits on unused alpha data.\n- The difference is that BC7 encodes RGBA ldr data, while BC6 encodes RGB hdr data.\n- To encode s3tc pixel formats, you will need to install libnvtt-bin (or export from image editors as dds or ktx files).\n\n### Too Long didn't read:\n\n- Use ASTC if you're targetting modern mobile devices (astc6x6 for walls, astc4x4 for huds, or something).\n- Use ETC2 if you're targetting older mobile users.\n- Use BC1 for desktop model/wall textures, and bc3 for things with non-binary alpha channels.\n- Use BC7 for desktop hud textures or other textures where BC1 does a terrible job.\n- Use jpegs if you just want to get filesize down without caring about performance.\n- If the user's hardware doesn't support the used formats then FTE can software-decode, so if you're expecting both mobile+desktop users with a single set of textures then favour mobile (desktop GPUs have better memory bandwidth).\n\n# License\n\nCopyright (c) 2004-2025 FTE's team and its contributors\nQuake source (c) 1999 id Software\n\nFTEQW is supplied to you under the terms of the same license as the\noriginal Quake sources, the GNU General Public License Version 2.\nPlease read the `LICENSE` file for details.\n\nThe latest source & binaries are always available at:\n\n[fteqw.org](https://fteqw.org)\n\n[fteqcc.org](https://fteqcc.org)\n"
  },
  {
    "path": "dounifdef.sh",
    "content": "#!/bin/sh\n#this script is DANGEROUS\n#be sure to have committed *BEFORE* running this script.\n\n#Note: This script does not understand dead files (including botlib).\n#expect '-Wmisleading-indentation' warnings (that were previously muted by nearby ifdefs).\n#DO NOT COMMIT THE RESULTS TO FTE'S TRUNK\n\nCONFIG=wastes\n\n#must have trailing slashes\nSRCDIR=./\nNEWDIR=/tmp/fte-$CONFIG/\n\necho \"WARNING: This script will lock-in a build config upon your C files.\"\necho \"The resulting files will support only your choice of feature set, instead of having lots of unused code mixed in.\"\necho \"THIS IS DESTRUCTIVE SO MUST ONLY BE USED FOR FORKS.\"\nread -p \"Press name the build config (or ctrl+c to abort)\" CONFIG\n\nif [ \"$foo\" == \"\" ]; then\n\techo \"no config specified.\"\n\texit 1\nfi\n\nmkdir -p $NEWDIR\ncat $SRCDIR/engine/common/config_$CONFIG.h | grep \"#define\" | sed \"s/\\/\\/#define/#undef/g\" > $NEWDIR/unifdefrules\ncat $SRCDIR/engine/common/config_$CONFIG.h | grep \"#undef\" >> $NEWDIR/unifdefrules\n\nif [ \"$SRCDIR\" != \"$NEWDIR\" ]; then\n\techo \"Copying files to strip to $NEWDIR.\"\n\tcp -r $SRCDIR* $NEWDIR\nelse\n\techo \"WARNING: WRITING FILES IN PLACE MUST ONLY  BE USED FOR FORKS.\"\n\tread -p \"Press y<enter> to confirm (or ctrl+c to abort)\" foo\n\tif [ \"$foo\" != \"y\" ]; then\n\t\texit 1\n\tfi\nfi\ncd $NEWDIR\n\nfor FILENAME in engine/*/*.c; do\n\tunifdef -f unifdefrules -m $FILENAME\ndone\n\n#headers keep any defines that will be expanded in code.\ncat $NEWDIR/unifdefrules | grep -v FULLENGINENAME | grep -v DISTRIBUTION | grep -v ENGINEWEBSITE | grep -v MAX_SPLITS | grep GAME_SHORTNAME > $NEWDIR/unifdefhrules\n\nfor FILENAME in engine/*/*.h; do\n\tunifdef -f unifdefhrules -m $FILENAME\ndone\n\nrm $NEWDIR/unifdefrules\n\necho \"Files in $NEWDIR have now been stripped down.\"\necho \"Some things may require hand-editing to remove warnings (or just compile with CFLAGS=-Wno-misleading-indentation).\"\necho \"You still need to set FTE_CONFIG too.\"\nread -p \"Press enter to test-compile\" foo\n\ncd $NEWDIR/engine && make sv-rel m-rel -j8 FTE_CONFIG=$CONFIG -k\n"
  },
  {
    "path": "engine/BSDmakefile",
    "content": "#just a stub to make things easier.\n#you still need gmake and gcc.\n\nall:\n\tgmake all\n\nhelp:\n\tgmake help\n\nclean:\n\tgmake clean\n\ngl-dbg:\n\tgmake gl-dbg\n\nsw-dbg:\n\tgmake sw-dbg\n\nm-dbg:\n\tgmake m-dbg\n\nsv-dbg:\n\tgmake sv-dbg\n\ngl-rel:\n\tgmake gl-rel\n\nsw-rel:\n\tgmake sw-rel\n\nm-rel:\n\tgmake m-rel\n\nsv-rel:\n\tgmake sv-rel\n"
  },
  {
    "path": "engine/LICENSE",
    "content": "GNU GENERAL PUBLIC LICENSE\nVersion 2, June 1991 \n\nCopyright (C) 1989, 1991 Free Software Foundation, Inc.  \n59 Temple Place - Suite 330, Boston, MA  02111-1307, USA\n\nEveryone is permitted to copy and distribute verbatim copies\nof this license document, but changing it is not allowed.\nPreamble\nThe licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. \n\nWhen we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. \n\nTo protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. \n\nFor example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. \n\nWe protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. \n\nAlso, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. \n\nFinally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. \n\nThe precise terms and conditions for copying, distribution and modification follow. \n\nTERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION\n0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The \"Program\", below, refers to any such program or work, and a \"work based on the Program\" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term \"modification\".) Each licensee is addressed as \"you\". \n\nActivities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. \n\n1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. \n\nYou may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. \n\n2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: \n\n\na) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. \n\nb) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. \n\nc) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this 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.) \nThese 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 themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. \nThus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. \n\nIn addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. \n\n3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: \n\na) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, \n\nb) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, \n\nc) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) \nThe source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. \nIf distribution of executable or object code is made by offering 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. \n\n4. 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 void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. \n\n5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. \n\n6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. \n\n7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. \n\nIf any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. \n\nIt is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. \n\nThis section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. \n\n8. 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 may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. \n\n9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. \n\nEach version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and \"any later version\", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. \n\n10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. \n\nNO WARRANTY\n\n11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. \n\n12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. \n\n\nEND OF TERMS AND CONDITIONS\n"
  },
  {
    "path": "engine/Makefile",
    "content": "#FTEQW Makefile\n\n#make m-rel plugins-rel sv-rel [FTE_TARGET=$ARCH]\n\n#only limited forms of cross-making is supported\n#only the following 3 are supported\n#linux->win32 (FTE_TARGET=win32) RPM Package: \"mingw32-gcc\", DEB Package: \"mingw32\"\n#linux->win64 (FTE_TARGET=win64) RPM Package: \"mingw32-gcc\", DEB Package: \"mingw32\"\n#linux->win32 (FTE_TARGET=msvc32 MSVCPATH=? WINDOWSSDKDIR=?) invokes via wine.\n#linux->win64 (FTE_TARGET=msvc64 MSVCPATH=? WINDOWSSDKDIR=?) invokes via wine.\n#linux->linux 32 (FTE_TARGET=linux32)\n#linux->linux 64 (FTE_TARGET=linux64)\n#linux->linux x32 (FTE_TARGET=linux_x32)\n#linux->linux armhf (FTE_TARGET=linux_armhf)\n#linux->linux arm64/aarch64 (FTE_TARGET=linux_arm64)\n#linux->linux *others* (FTE_TARGET=linux CC=other-gcc)\n#linux->morphos (FTE_TARGET=morphos)\n#linux->macosx (FTE_TARGET=macosx) or (FTE_TARGET=macosx_x86)\n#linux->javascript (FTE_TARGET=web)\n#linux->droid (make droid)\n#win32->droid (make droid)\n#if you are cross compiling, you'll need to use FTE_TARGET=mytarget\n#note: cross compiling will typically require 'make makelibs FTE_TARGET=mytarget', which avoids installing lots of extra system packages.\n#FTE_TARGET=SDL2 is also a thing.\n#FTE_TARGET=SDL3 is also a thing.\n\ndefault: help\n\n#\nCC?=gcc\nWINDRES?=windres\nSTRIP?=strip\n\nSTRIPFLAGS?=--strip-unneeded --remove-section=.comment\n\nVISIBILITY_FLAGS=-fvisibility=hidden\t#just assume this for sanities sake. disable only for compilers that error.\nCPUOPTIMIZATIONS=-Os\n\nCOMPILE_SYS:=$(shell uname -o 2>&1)\n\n#canonicalize the source path. except emscripten warns about that like crazy. *sigh*\nifeq ($(FTE_TARGET),web)\n    BASE_DIR:=.\nelse ifeq ($(findstring msvc,$(FTE_TARGET)),msvc)\n    BASE_DIR:=.\nelse ifeq ($(FTE_TARGET),droid)\n\t#android tools suck, but plugins need to find the engine directory.\n    BASE_DIR:=../engine\nelse\n    BASE_DIR:=$(realpath .)\nendif\n\nifeq ($(SVNREVISION),)\n\t#try subversion firstly...\n#    SVN_VERSION:=$(shell test -d $(BASE_DIR)/../.svn && svnversion $(BASE_DIR))\n#    SVN_DATE:=$(shell test -d $(BASE_DIR)/../.svn && cd $(BASE_DIR) && svn info --show-item last-changed-date --no-newline)\n#    ifeq (,$(SVN_VERSION))\n#        #grab the svn version from git-svn (assuming no other modifications). this fails when there's extra commits (probably a good thing).\n#        SVN_VERSION=$(shell test -d $(BASE_DIR)/../.git && git svn find-rev `git rev-parse HEAD`)\n#        SVN_DATE:=$(shell test -d $(BASE_DIR)/../.git && git log -1 --format=%cs $(BASE_DIR))\n#    endif\n    ifeq (,$(SVN_VERSION))\n        #try to get git version info instead. this usually uses git-count-hash[-dirty] format\n        SVN_VERSION:=$(shell test -d $(BASE_DIR)/../.git && cd $(BASE_DIR) && echo $$((`git rev-list HEAD --count` + 29)))\n        GIT_VERSION:=$(shell test -d $(BASE_DIR)/../.git && cd $(BASE_DIR) && git describe --long --always --dirty)\n        SVN_DATE:=$(shell test -d $(BASE_DIR)/../.git && git log -1 --format=%cs $(BASE_DIR))\n        ifneq (,$(SVN_VERSION))\n            #make sure its prefixed with something specific. we use versions for versioning, which will confuse the update mechanism if they're inconsistent - like random hashses that have no implied ordering...\n            SVN_VERSION:=git-$(SVN_VERSION)-$(GIT_VERSION)\n        endif\n    endif\n\n    SVNREVISION=\n    ifneq (,$(SVN_VERSION))\n        SVNREVISION+=-DSVNREVISION=$(SVN_VERSION)\n    endif\nifneq (M,$(findstring M,$(SVN_VERSION)))\n    SVNREVISION+=-DSVNDATE=$(SVN_DATE)\nendif\nendif\nMAKE:=$(MAKE) --no-print-directory SVNREVISION=\"$(SVNREVISION)\" SVN_VERSION=\"$(SVN_VERSION)\"\n\n#WHOAMI:=$(shell whoami)\n\n\n#update these to download+build a different version. this assumes that the url+subdirs etc contain a consistant version everywhere.\nJPEGVER=9c\nZLIBVER=1.3.1\nPNGVER=1.6.45\nOGGVER=1.3.6\nVORBISVER=1.3.7\nVULKANVER=1.3.275.0\nSDL2VER=2.30.11\nSDL3VER=3.2.10\nSCINTILLAVER=373\nOPUSVER=1.3.1\nSPEEXVER=1.2.0\nSPEEXDSPVER=1.2.0\nFREETYPEVER=2.10.1\nBULLETVER=2.87\nOPENSSLVER=3.0.1\n\n#cygwin's make's paths confuses non-cygwin things\nRELEASE_DIR=$(BASE_DIR)/release\nDEBUG_DIR=$(BASE_DIR)/debug\nPROFILE_DIR=$(BASE_DIR)/profile\nNATIVE_ABSBASE_DIR:=$(realpath $(BASE_DIR))\nifeq ($(COMPILE_SYS),Cygwin)\n    OUT_DIR?=.\n    NATIVE_OUT_DIR:=$(shell cygpath -m $(OUT_DIR))\n    NATIVE_BASE_DIR:=$(shell cygpath -m $(BASE_DIR))\n    NATIVE_RELEASE_DIR:=$(shell cygpath -m $(RELEASE_DIR))\n    NATIVE_DEBUG_DIR:=$(shell cygpath -m $(DEBUG_DIR))\n    NATIVE_ABSBASE_DIR:=$(shell cygpath -m $(NATIVE_ABSBASE_DIR))\nendif\nNATIVE_OUT_DIR?=$(OUT_DIR)\nNATIVE_BASE_DIR?=$(BASE_DIR)\nNATIVE_RELEASE_DIR?=$(RELEASE_DIR)\nNATIVE_DEBUG_DIR?=$(DEBUG_DIR)\n\nEXE_NAME=fteqw\n\n#include the appropriate games.\nifneq (,$(BRANDING))\n    BRANDFLAGS+=-DBRANDING_INC=../game_$(BRANDING).h\n    -include game_$(BRANDING).mak\nendif\nFTE_CONFIG?=fteqw\nifneq ($(findstring msvc,$(FTE_TARGET)),msvc)\nifeq (,$(FTE_CONFIG_EXTRA))\n    FTE_CONFIG_EXTRA := $(shell $(CC) -xc -E -P -DFTE_TARGET_$(shell echo $(FTE_TARGET) | tr '[:lower:]' '[:upper:]') -DCOMPILE_OPTS $(CFLAGS) common/config_$(FTE_CONFIG).h)\nendif\nendif\nBRANDFLAGS+=-DCONFIG_FILE_NAME=config_$(FTE_CONFIG).h $(FTE_CONFIG_EXTRA)\nEXE_NAME=$(FTE_CONFIG)\nifeq (,$(findstring DNO_SPEEX,$(FTE_CONFIG_EXTRA)))\n    USE_SPEEX?=1\nendif\nifeq (,$(findstring DNO_OPUS,$(FTE_CONFIG_EXTRA)))\n    USE_OPUS=1\nendif\nifneq (,$(findstring DLINK_QUAKE3,$(FTE_CONFIG_EXTRA)))\n    LINK_QUAKE3=1\nendif\nifeq ($(findstring web,$(FTE_TARGET)),)\t#the web target uses javascript/browser loaders for common audio+image files instead of needing to embed our own, which keeps sizes down slightly.\nifeq (,$(findstring DNO_VORBISFILE,$(FTE_CONFIG_EXTRA)))\n    USE_VORBISFILE=1\nendif\nifneq (,$(findstring DLINK_JPEG,$(FTE_CONFIG_EXTRA)))\n    LINK_JPEG=1\nendif\nifneq (,$(findstring DLINK_PNG,$(FTE_CONFIG_EXTRA)))\n    LINK_ZLIB=1\n    LINK_PNG=1\nendif\nendif\nifneq (,$(findstring DLINK_FREETYPE,$(FTE_CONFIG_EXTRA)))\n    LINK_FREETYPE=1\n    LINK_ZLIB=1\n    LINK_PNG=1\nendif\nifneq (,$(findstring -Os,$(FTE_CONFIG_EXTRA)))\n    CPUOPTIMIZATIONS+=-Os\n    BRANDFLAGS:=$(filter-out -O%,$(BRANDFLAGS))\nendif\nifneq (,$(findstring DLINK_ODE,$(FTE_CONFIG_EXTRA)))\n\t#ode library will be statically linked.\n    LINK_ODE=1\nendif\nifneq (,$(findstring DLINK_INTERNAL_BULLET,$(FTE_CONFIG_EXTRA)))\n\t#bullet plugin will be built into the exe itself\n    INTERNAL_BULLET=1\nendif\nifneq (,$(findstring DLINK_EZHUD,$(FTE_CONFIG_EXTRA)))\n    LINK_EZHUD=1\nendif\nifneq (,$(findstring DLINK_OPENSSL,$(FTE_CONFIG_EXTRA)))\n    LINK_OPENSSL=1\nendif\n\nifeq ($(BITS),64)\n    CC:=$(CC) -m64\n    CXX:=$(CXX) -m64\nendif\nifeq ($(BITS),32)\n    CC:=$(CC) -m32\n    CXX:=$(CXX) -m32\nendif\n\n#correct the gcc build when cross compiling\nifneq (,$(findstring win32,$(FTE_TARGET)))\n\tifeq ($(shell $(CC) -v 2>&1 | grep mingw),)\n\t\t#CC didn't state that it was mingw... so try fixing that up\n\t\t#old/original mingw project, headers are not very up to date.\n\t\tifneq ($(shell which i586-mingw32msvc-gcc 2> /dev/null),)\n\t\t\t#yup, the alternative exists (this matches the one debian has)\n\t\t\tCC=i586-mingw32msvc-gcc\n\t\t\tCXX=i586-mingw32msvc-g++\n\t\t\tAR=i586-mingw32msvc-ar\n\t\t\tWINDRES=i586-mingw32msvc-windres\n\t\t\tSTRIP=i586-mingw32msvc-strip\n#\t\t\tBITS?=32\n\t\tendif\n\t\t#mingw64 provides a 32bit toolchain too, which has more up to date header files than the mingw32 project. so favour that if its installed.\n\t\tifneq ($(shell which i686-w64-mingw32-gcc 2> /dev/null),)\n\t\t\t#yup, the alternative exists (this matches the one debian has)\n\t\t\tCC=i686-w64-mingw32-gcc\n\t\t\tCXX=i686-w64-mingw32-g++\n\t\t\tAR=i686-w64-mingw32-ar\n\t\t\tWINDRES=i686-w64-mingw32-windres\n\t\t\tSTRIP=i686-w64-mingw32-strip\n#\t\t\tBITS?=32\n\t\tendif\n\tendif\nendif\n\n#correct the gcc build when cross compiling\nifneq (,$(findstring win64,$(FTE_TARGET)))\n\tifeq ($(shell $(CC) -v 2>&1 | grep mingw),)\n\t\t#CC didn't state that it was mingw... so try fixing that up\n\t\tifneq ($(shell which x86_64-w64-mingw32-gcc 2> /dev/null),)\n\t\t\t#yup, the alternative exists (this matches the one debian has)\n\t\t\tCC=x86_64-w64-mingw32-gcc -m64\n\t\t\tCXX=x86_64-w64-mingw32-g++ -m64\n\t\t\tAR=x86_64-w64-mingw32-ar\n\t\t\tWINDRES=x86_64-w64-mingw32-windres\n\t\t\tSTRIP=x86_64-w64-mingw32-strip\n#\t\t\tBITS=64\n\t\tendif\n\t\tifneq ($(shell which amd64-mingw32msvc-gcc 2> /dev/null),)\n\t\t\t#yup, the alternative exists (this matches the one debian has)\n\t\t\tCC=amd64-mingw32msvc-gcc -m64\n\t\t\tCXX=amd64-mingw32msvc-g++ -m64\n\t\t\tAR=amd64-mingw32msvc-ar\n\t\t\tWINDRES=amd64-mingw32msvc-windres\n\t\t\tSTRIP=amd64-mingw32msvc-strip\n#\t\t\tBITS=64\n\t\tendif\n\tendif\nendif\n\nifeq ($(FTE_TARGET),win32_sdl)\n    FTE_TARGET=win32_SDL\nendif\nifeq ($(FTE_TARGET),win64_sdl)\n\tFTE_TARGET=win64_SDL\nendif\n\nUSER_TARGET:=$(FTE_TARGET)\n\n#make droid-rel doesn't get the right stuff\n#add a small default config file. its only small. and some other stuff, because we can. This makes it much easier to get it up and running.\nDROID_PACKSU?= $(BASE_DIR)/droid/fte.cfg $(BASE_DIR)/droid/default.fmf $(BASE_DIR)/droid/configs/touch.cfg\nANDROID_HOME?=~/android-sdk-linux\n#ANDROID_NDK_ROOT?=~/android-ndk-r8e\n#ANDROID_NDK_ROOT?=$(ANDROID_HOME)/ndk-bundle\nANDROID_NDK_ROOT=$(ANDROID_HOME)/android-ndk-r14b\nANDROID_ZIPALIGN?=$(ZIPALIGN)\nANDROID_ZIPALIGN?=$(ANDROID_HOME)/tools/zipalign\nANT?=ant\nJAVA_HOME?=/usr\nJAVATOOL=$(JAVA_HOME)/bin/\nANDROID_SCRIPT=android\nDO_CMAKE=cmake -DCMAKE_C_COMPILER=\"$(firstword $(CC))\" -DCMAKE_C_FLAGS=\"$(wordlist 2,99,$(CC)) $(CPUOPTIMIZATIONS)\" -DCMAKE_CXX_COMPILER=\"$(firstword $(CXX))\" -DCMAKE_CXX_FLAGS=\"$(wordlist 2,99,$(CXX)) $(CPUOPTIMIZATIONS)\" \n\nifeq ($(DROID_ARCH),)\n\t#armeabi armeabi-v7a arm64-v8a x86 x86_64 mips mips64\n\tDROID_ARCH+=armeabi-v7a\t#old 32bit android. yucky.\n\tDROID_ARCH+=arm64-v8a\t#modern android devices are 64bit-only. urgh.\n\tDROID_ARCH+=x86\t\t\t#mostly for testing...\n\t#DROID_ARCH+=x86_64\t#starting with DROID_API_LEVEL 21\nendif\nifeq ($(FTE_TARGET),droid)\n\t#figure out the host system, required to find a usable compiler\n\tifneq ($(shell uname -o 2>&1 | grep Cygwin),)\n#\t\tifeq ($(shell uname -m 2>&1), i686)\n#\t\t\tANDROID_HOSTSYSTEM?=windows\n#\t\telse\n#\t\t\tANDROID_HOSTSYSTEM?=windows-$(shell uname -m)\n#\t\tendif\n\t\tANDROID_HOSTSYSTEM?=windows-x86_64\n\telse\n\t\tANDROID_HOSTSYSTEM?=linux-$(shell uname -m)\n\tendif\n\tDROID_ABI_VER?=4.9\n\n\t#omfg why the FUCK do we need all this bullshit? Why isn't there some sane way to do this that actually works regardless of ndk updates?!?\n\t#name is some random subdir that someone at google arbitrarily picked\n\t#arch is some random other name for a group of ABIs...\n\t#prefix is the 'standard' tupple that the toolchain was compiled to target (by default)\n\t#ver is whatever gcc version it is, or clang. so yeah, pretty much random.\n\t#cflags is whatever is needed to actually target that abi properly with the specific toolchain... -m64 etc.\n\tDROID_ABI_NAME___armeabi=arm-linux-androideabi\n\tDROID_ABI_PREFIX_armeabi=arm-linux-androideabi\n\tDROID_ABI_ARCH___armeabi=arm\n\tDROID_ABI_VER____armeabi?=$(DROID_ABI_VER)\n\tDROID_ABI_CFLAGS_armeabi=-march=armv5te -mtune=xscale -msoft-float\n\tDROID_ABI_NAME___armeabi-v7a=$(DROID_ABI_NAME___armeabi)\n\tDROID_ABI_PREFIX_armeabi-v7a=$(DROID_ABI_PREFIX_armeabi)\n\tDROID_ABI_ARCH___armeabi-v7a=$(DROID_ABI_ARCH___armeabi)\n\tDROID_ABI_VER____armeabi-v7a=$(DROID_ABI_VER____armeabi)\n\tDROID_ABI_CFLAGS_armeabi-v7a=-march=armv7-a -mfloat-abi=softfp -mfpu=vfpv3-d16\n\tDROID_ABI_NAME___arm64-v8a=$(DROID_ABI_NAME___armeabi)\n\tDROID_ABI_PREFIX_arm64-v8a=$(DROID_ABI_PREFIX_armeabi)\n\tDROID_ABI_ARCH___arm64-v8a=$(DROID_ABI_ARCH___armeabi)\n\tDROID_ABI_VER____arm64-v8a=$(DROID_ABI_VER____armeabi)\n\tDROID_ABI_CFLAGS_arm64-v8a=-m64\n\tDROID_ABI_NAME___x86=x86\n\tDROID_ABI_PREFIX_x86=i686-linux-android\n\tDROID_ABI_ARCH___x86=x86\n\tDROID_ABI_VER____x86?=$(DROID_ABI_VER)\n\tDROID_ABI_CFLAGS_x86=-march=i686 -mssse3 -mfpmath=sse -m32 -Os\n\tDROID_ABI_NAME___x86_64=x86_64\n\tDROID_ABI_PREFIX_x86_64=x86_64-linux-android\n\tDROID_ABI_ARCH___x86_64=x86_64\n\tDROID_ABI_VER____x86_64=$(DROID_ABI_VER____x86)\n\tDROID_ABI_CFLAGS_x86_64=-march=x86-64 -msse4.2 -mpopcnt -m64 -Os\n\t#DROID_ABI_NAME___mips=mipsel-linux-android\n\t#DROID_ABI_PREFIX_mips=mipsel-linux-android\n\t#DROID_ABI_ARCH___mips=mips\n\t#DROID_ABI_VER____mips?=$(DROID_ABI_VER)\n\t#DROID_ABI_CFLAGS_mips=\n\t#DROID_ABI_NAME___mips64=$(DROID_ABI_NAME___mips)\n\t#DROID_ABI_PREFIX_mips64=$(DROID_ABI_PREFIX_mips)\n\t#DROID_ABI_ARCH___mips64=$(DROID_ABI_ARCH___mips)\n\t#DROID_ABI_VER____mips64=$(DROID_ABI_VER____mips)\n\t#DROID_ABI_CFLAGS_mips64=-m64\n\n\tifeq (1,$(words [$(DROID_ARCH)]))\n\t\t#try and make sense of the above nonsense.\n\t\tDROID_ABI:=$(DROID_ABI_CFLAGS_$(DROID_ARCH))\n\t\tTOOLCHAINPATH:=$(ANDROID_NDK_ROOT)/toolchains/$(DROID_ABI_NAME___$(DROID_ARCH))-$(DROID_ABI_VER____$(DROID_ARCH))/prebuilt/$(ANDROID_HOSTSYSTEM)/bin/\n\t\tTOOLCHAIN:=$(TOOLCHAINPATH)$(DROID_ABI_PREFIX_$(DROID_ARCH))-\n\n\t\t#4 is the min that fte requires\n\t\tDROID_API_LEVEL?=9\n\t\tifeq ($(DROID_ARCH),x86)\n\t\t\t#google fecked up. anything before api_level 9 will fail to compile on x86\n\t\t\tDROID_API_LEVEL=9\n\t\tendif\n\t\tifeq ($(DROID_ARCH),x86_64)\n\t\t\t#google fecked up. anything before api_level 9 will fail to compile on x86\n\t\t\tDROID_API_LEVEL=21\n\t\tendif\n\t\tDROID_API_NAME?=android-$(DROID_API_LEVEL)\n\t\tDROID_PLAT_INC=arch-$(DROID_ABI_ARCH___$(DROID_ARCH))\n\t\tDROIDSYSROOT=$(realpath $(ANDROID_NDK_ROOT)/platforms/$(DROID_API_NAME)/$(DROID_PLAT_INC))\n\t\tifeq ($(DROIDSYSROOT),)\t#its possible that google removed whatever api we're trying to target, just switch up to the new default.\n\t\t\tBITCHANDMOAN:=$(shell echo targetting \\\"android-9\\\" instead of \\\"android-$(DROID_API_LEVEL)\\\" 1>&2)\n\t\t\tDROID_API_LEVEL=9\n\t\t\tDROID_API_NAME=android-$(DROID_API_LEVEL)\n\n\t\t\tDROIDSYSROOT=$(realpath $(ANDROID_NDK_ROOT)/platforms/$(DROID_API_NAME)/$(DROID_PLAT_INC))\n\t\t\tifeq ($(DROIDSYSROOT),)\t#its possible that google removed whatever api we're trying to target, just switch up to the new default.\n\t\t\t\tBITCHANDMOAN:=$(shell echo $(DROID_API_NAME) not available either - $(DROID_ARCH) 1>&2)\n\t\t\tendif\n\t\tendif\n\t\tDROIDSYSROOT:=$(DROIDSYSROOT)\n\tendif\n\n\t#if we're running under windows, then we want to run some other binary\n\tifeq ($(shell uname -o 2>&1 | grep Cygwin),)\n\t\t#set up for linux/mingw\n\t\tTOOLOVERRIDES=PATH=\"/usr/bin:$(realpath $(TOOLCHAINPATH))\" CFLAGS=--sysroot=\"$(realpath $(ANDROID_NDK_ROOT)/platforms/$(DROID_API_NAME)/$(DROID_PLAT_INC))\" CPPFLAGS=--sysroot=\"$(realpath $(ANDROID_NDK_ROOT)/platforms/$(DROID_API_NAME)/$(DROID_PLAT_INC))\" \n\t\tCONFIGARGS= --with-sysroot=\"$(realpath $(ANDROID_NDK_ROOT)/platforms/$(DROID_API_NAME)/$(DROID_PLAT_INC))\" \n\telse\n\t\t#we're running upon cygwin\n\t\t#FIXME: support mingw too...\n\n\t\tANDROID_SCRIPT=android.bat\n\t\t#make can't cope with absolute win32 paths in dependancy files\n\t\tDEPCC=\n\t\tDEPCXX=\n\n\t\tifneq ($(realpath $(TOOLCHAINPATH)),)\t#don't invoke cygpath when realpath returns nothing due to dodgy paths (which happens when stuff isn't set up right for the top-level makefile)\n\t\t\t#configure hates android, with its broken default sysroot and lack of path etc\n\t\t\tDROIDSYSROOT:=$(shell cygpath -m $(DROIDSYSROOT))\n\t\t\tTOOLOVERRIDES=PATH=\"/usr/bin:$(shell cygpath -u $(realpath $(TOOLCHAINPATH)))\" CFLAGS=--sysroot=\"$(DROIDSYSROOT)\" CPPFLAGS=--sysroot=\"$(DROIDSYSROOT)\" \n\t\t\tCONFIGARGS= --with-sysroot=\"$(shell cygpath -u $(realpath $(ANDROID_NDK_ROOT)/platforms/$(DROID_API_NAME)/$(DROID_PLAT_INC)))\" \n\t\tendif\n\tendif\n\n\tCC:=$(TOOLCHAIN)gcc --sysroot=\"$(DROIDSYSROOT)\" -DANDROID $(DROID_ABI) -fno-strict-aliasing\n\tCXX:=$(TOOLCHAIN)g++ --sysroot=\"$(DROIDSYSROOT)\" -DANDROID $(DROID_ABI) -fno-strict-aliasing\n\tDO_LD=+$(DO_ECHO) $(CC) -Wl,-soname,libftedroid.so -shared -Wl,--no-undefined -Wl,-z,noexecstack -o $@ $(LTO_LD) $(WCFLAGS) $(BRANDFLAGS) $(CFLAGS) -llog -lc -lm -lz\n\tLD:=$(TOOLCHAIN)ld\n\tAR:=$(TOOLCHAIN)ar\n\tSTRIP=$(TOOLCHAIN)strip\nendif\n\n\n\n#crosscompile macosx from linux, default target ppc 32bit\nifeq ($(FTE_TARGET),macosx)\n\tifeq ($(shell $(CC) -v 2>&1 | grep apple),)\n\t\tifneq ($(shell which powerpc-apple-darwin8-gcc 2> /dev/null),)\n\t\t\tCC=powerpc-apple-darwin8-gcc\n\t\t\tCXX=powerpc-apple-darwin8-g++\n\t\t\tSTRIP=powerpc-apple-darwin8-strip\n\t\t\t#seems, macosx has a more limited version of strip\n\t\t\tSTRIPFLAGS=\n\t\t\tBITS=32\n\t\t\tEXTENSION=_ppc\n\t\tendif\n\tendif\nendif\n\nifeq ($(FTE_TARGET),macosx_ppc64)\n\tifeq ($(shell $(CC) -v 2>&1 | grep apple),)\n\t\tifneq ($(shell which powerpc-apple-darwin8-gcc 2> /dev/null),)\n\t\t\tFTE_TARGET=macosx\n\t\t\tCC=powerpc-apple-darwin8-gcc -arch ppc64\n\t\t\tCXX=powerpc-apple-darwin8-g++ -arch ppc64\n\t\t\tSTRIP=powerpc-apple-darwin8-strip\n\t\t\t#seems, macosx has a more limited version of strip\n\t\t\tSTRIPFLAGS=\n\t\t\tBITS=64\n\t\t\tEXTENSION=_ppc\n\t\tendif\n\tendif\nendif\n\nifeq ($(FTE_TARGET),macosx_x86)\n\tifeq ($(shell $(CC) -v 2>&1 | grep apple),)\n\t\tifneq ($(shell which i686-apple-darwin8-gcc 2> /dev/null),)\n\t\t\tFTE_TARGET=macosx\n\t\t\t# i686-apple-darwin8-gcc's default target is i386, powerpc-apple-darwin8-gcc -arch i386 just invokes i686-apple-darwin8-gcc anyway\n\t\t\tCC=i686-apple-darwin8-gcc\n\t\t\tCXX=i686-apple-darwin8-g++\n\t\t\tSTRIP=i686-apple-darwin8-strip\n\t\t\t#seems, macosx has a more limited version of strip\n\t\t\tSTRIPFLAGS=\n\t\t\tEXTENSION=_x86\n\t\tendif\n\tendif\nendif\n\n#crosscompile morphos from linux\nifeq ($(FTE_TARGET),morphos)\n\tifeq ($(shell $(CC) -v 2>&1 | grep morphos),)\n\t\tifneq ($(shell which ppc-morphos-gcc 2> /dev/null),)\n\t\t\tCC=ppc-morphos-gcc\n\t\t\tCXX=ppc-morphos-g++\n\t\t\t#morphos strip has a 'feature', it strips permissions\n\t\t\tSTRIP=ppc-morphos-strip\n\t\tendif\n\tendif\nendif\n\nifeq ($(FTE_TARGET),dos)\n\t#at least from dos.\n\tCC=i586-pc-msdosdjgpp-gcc\n\tCXX=i586-pc-msdosdjgpp-g++\n\tSTRIP=i586-pc-msdosdjgpp-strip\n\tCFLAGS+=-DNO_ZLIB\nendif\n\n#if you have an x86, you can get gcc to build binaries using 3 different ABIs, instead of builds for just the default ABI\nifeq ($(FTE_TARGET),linux32)\n\tFTE_TARGET=linux\n\tCC=gcc -m32\n\tCXX=g++ -m32\n\tSTRIP=strip\n\tBITS=32\nendif\nifeq ($(FTE_TARGET),linux_x86)\n\tFTE_TARGET=linux\n\tCC=i686-linux-gnu-gcc\n\tPKGCONFIG=i686-linux-gnu-pkg-config\n\tCXX=i686-linux-gnu-g++\n\tSTRIP=i686-linux-gnu-strip\n\tBITS=32\nendif\nifeq ($(FTE_TARGET),linux_amd64)\n\tFTE_TARGET=linux\n\tCC=x86_64-linux-gnu-gcc\n\tPKGCONFIG=x86_64-linux-gnu-pkg-config\n\tCXX=x86_64-linux-gnu-g++\n\tSTRIP=x86_64-linux-gnu-strip\n\tBITS=64\nendif\nifeq ($(FTE_TARGET),linux_armhf)\n\t#debian's armhf is armv7, but armv6 works on RPI too.\n\tFTE_TARGET=linux\n\tCC=arm-linux-gnueabihf-gcc -marm -march=armv6 -mfpu=vfp -mfloat-abi=hard\n\tCXX=arm-linux-gnueabihf-g++ -marm -march=armv6 -mfpu=vfp -mfloat-abi=hard\n\tSTRIP=arm-linux-gnueabihf-strip\n\tBITS=armhf\nendif\nifeq ($(FTE_TARGET),linux_arm64)\n\tFTE_TARGET=linux\n\tCC=aarch64-linux-gnu-gcc\n\tCXX=aarch64-linux-gnu-g++\n\tSTRIP=aarch64-linux-gnu-strip\n\tBITS=arm64\n\tUSE_SPEEX=0 #fails to compile due to neon asm, I'm just going to disable it (will still soft-link).\nendif\nifeq ($(FTE_TARGET),linux_aarch64)\n\tFTE_TARGET=linux\n\tCC=aarch64-linux-gnu-gcc\n\tCXX=aarch64-linux-gnu-g++\n\tSTRIP=aarch64-linux-gnu-strip\n\tBITS=aarch64\n\tUSE_SPEEX=0 #fails to compile due to neon asm, I'm just going to disable it (will still soft-link).\nendif\nifeq ($(FTE_TARGET),linux_x32)\n\t#DO NOT CONFUSE WITH linux_x86. this target is amd64-with-32bit-pointers\n\t#note: the x32 abi is still not finished or something.\n\t#at the current time, you will need to edit your kernel's commandline to allow this stuff to run\n\t#try and use a proper cross-compiler if we can, otherwise fall back on multi-arch.\n\tFTE_TARGET=linux\nifneq ($(shell which x86_64-linux-gnux32-gcc 2> /dev/null),)\n\tCC=x86_64-linux-gnux32-gcc\n\tCXX=x86_64-linux-gnux32-g++\n\tSTRIP=x86_64-linux-gnux32-strip\nelse\n\tCC=gcc -mx32\n\tCXX=g++ -mx32\n\tSTRIP=strip\nendif\n\tBITS=x32\nendif\nifeq ($(FTE_TARGET),linux64)\n\tFTE_TARGET=linux\n\tCC=gcc -m64\n\tCXX=g++ -m64\n\tSTRIP=strip\n\tBITS=64\nendif\nifeq ($(FTE_TARGET),web)\n\tCC=emcc\n\tCXX=em++\n\tAR=emar\nendif\nifeq ($(FTE_TARGET),cygwin)\n\tFTE_TARGET=cyg\nendif\nifeq ($(shell uname -s),OpenBSD)\n\tPKGCONFIG=pkg-config\nendif\n\nifeq ($(FTE_TARGET),) \t#user didn't specify prefered target\n\tifneq ($(shell uname 2>&1 | grep CYGWIN),)\n\t\tFTE_TARGET=cyg\n\t\tANDROID_SCRIPT=android.bat\n\tendif\n\tifneq ($(shell $(CC) -v 2>&1 | grep mingw),)\n\t\tFTE_TARGET=win32\n\tendif\n\tifeq ($(FTE_TARGET),) \t#still not set\n\t\tUNAME_SYSTEM:=$(shell uname)\n\t\tifeq ($(UNAME_SYSTEM),Linux)\n\t\t\tFTE_TARGET=linux\n\t\tendif\n\t\tifeq ($(UNAME_SYSTEM),Darwin)\n\t\t\tFTE_TARGET=macosx\n\t\tendif\n\t\tifeq ($(UNAME_SYSTEM),FreeBSD)\n\t\t\tFTE_TARGET=bsd\n\t\tendif\n\t\tifeq ($(UNAME_SYSTEM),NetBSD)\n\t\t\tFTE_TARGET=bsd\n\t\tendif\n\t\tifeq ($(UNAME_SYSTEM),OpenBSD)\n\t\t\tFTE_TARGET=bsd\n\t\tendif\n\t\tifeq ($(UNAME_SYSTEM),MorphOS)\n\t\t\tFTE_TARGET=morphos\n\t\tendif\n\t\t#else I've no idea what it is you're running\n\tendif\n\n\tFTE_TARGET ?= unk\t#so go for sdl.\nendif\n\nifneq ($(shell ls|grep config.h),)\n\tHAVECONFIG=-DHAVE_CONFIG_H\nendif\n\nCLIENT_DIR=$(BASE_DIR)/client\nGL_DIR=$(BASE_DIR)/gl\nD3D_DIR=$(BASE_DIR)/d3d\nVK_DIR=$(BASE_DIR)/vk\nSW_DIR=$(BASE_DIR)/sw\nSERVER_DIR=$(BASE_DIR)/server\nCOMMON_DIR=$(BASE_DIR)/common\nHTTP_DIR=$(BASE_DIR)/http\n#LIBS_DIR=$(BASE_DIR)/libs\nLIBS_DIR?=.\nPROGS_DIR=$(BASE_DIR)/qclib\n\nifeq ($(NOCOMPAT),1)\n\tNCCFLAGS=-DNOLEGACY -DOMIT_QCC\n\tNCDIRPREFIX=nc\nendif\nALL_CFLAGS=$(HAVECONFIG) $(VISIBILITY_FLAGS) $(BRANDFLAGS) $(CFLAGS) $(BASE_CFLAGS) $(WCFLAGS) $(ARCH_CFLAGS) $(NCCFLAGS) -I$(ARCHLIBS)\nALL_CXXFLAGS=$(subst -Wno-pointer-sign,,$(ALL_CFLAGS))\n\n#cheap compile-everything-in-one-unit (compile becomes preprocess only)\nifneq ($(WPO),)\n\tLTO_CC= -E\n\tLTO_LD= -flto=jobserver -fwhole-program -x c\n\tLTO_END=ltoxnone\n\tLTO_START=ltoxc\nendif\n#proper/consistant link-time optimisations (requires gcc 4.5+ or so)\nifneq ($(LTO),)\n\tLTO_CC=-flto=jobserver -fvisibility=hidden\n\tLTO_LD=-flto=jobserver\nendif\n\n#DO_ECHO=@echo $< && \nDO_ECHO=@\n#DO_ECHO=\nDO_CC=$(DO_ECHO) $(CC) $(LTO_CC) $(ALL_CFLAGS) -o $@ -c $<\nDO_CXX=$(DO_ECHO) $(CXX) $(LTO_CC) $(ALL_CXXFLAGS) -o $@ -c $<\n\nifeq ($(findstring msvc,$(FTE_TARGET)),msvc)\n\tBASELDFLAGS=\n\tVISIBILITY_FLAGS=\nendif\nifeq ($(FTE_TARGET),cyg)\n\tBASELDFLAGS=-lm\nendif\nifeq ($(FTE_TARGET),dos)\n\tBASELDFLAGS=-lm\nendif\nifeq ($(FTE_TARGET),morphos)\n\tBASELDFLAGS=-lm\nendif\n\nifneq (,$(findstring bsd,$(FTE_TARGET)))\n\tBASELDFLAGS=-lm\nendif\nBASELDFLAGS ?= -lm -ldl -lpthread\n\nifeq (win,$(findstring cyg,$(FTE_TARGET))$(findstring win,$(FTE_TARGET)))\n\tBASELDFLAGS=-lm\n#\tMINGW_LIBS_DIR=$(LIBS_DIR)/mingw-libs\n\n#\tifeq ($(shell echo $(FTE_TARGET)|grep -v win64),)\n#\t\tMINGW_LIBS_DIR=$(LIBS_DIR)/mingw64-libs\n#\tendif\n\n#\tIMAGELDFLAGS=$(MINGW_LIBS_DIR)/libpng.a $(MINGW_LIBS_DIR)/libz.a $(MINGW_LIBS_DIR)/libjpeg.a\n#\tOGGVORBISLDFLAGS=$(MINGW_LIBS_DIR)/libvorbisfile.a $(MINGW_LIBS_DIR)/libvorbis.a $(MINGW_LIBS_DIR)/libogg.a\nendif\n\n#BASELDFLAGS=-lm  -lz\nXLDFLAGS=-L$(ARCHLIBS) $(IMAGELDFLAGS)\n\n#hack some other arguments based upon the toolchain\nifeq ($(findstring msvc,$(FTE_TARGET)),msvc)\n\tWARNINGFLAGS?=-W3 -D_CRT_SECURE_NO_WARNINGS\n\tGNUC_FUNCS=\nelse\n\tWARNINGFLAGS?=-Wall -Wno-pointer-sign -Wno-unknown-pragmas -Wno-format-zero-length -Wno-strict-aliasing #-Wcast-align\n#\tGNUC_FUNCS= -Dstrnicmp=strncasecmp -Dstricmp=strcasecmp\nendif\n\nSDL_INCLUDES=\n#-I$(LIBS_DIR)/sdl/include -I/usr/include/SDL -I$(LIBS_DIR)/sdl/include/SDL\nBASE_INCLUDES=-I$(CLIENT_DIR) -I$(SERVER_DIR) -I$(COMMON_DIR) -I$(GL_DIR) -I$(D3D_DIR) -I$(PROGS_DIR) -I. \nBASE_CFLAGS=$(WARNINGFLAGS) $(GNUC_FUNCS) $(BASE_INCLUDES) -I$(LIBS_DIR)/dxsdk9/include -I$(LIBS_DIR)/dxsdk7/include $(SDL_INCLUDES) $(SVNREVISION)\nCLIENT_ONLY_CFLAGS=-DCLIENTONLY\nSERVER_ONLY_CFLAGS=-DSERVERONLY\nJOINT_CFLAGS=\nDEBUG_CFLAGS?=-ggdb -g\nDEBUG_CFLAGS+=-DDEBUG -D_DEBUG\nRELEASE_CFLAGS?=$(CPUOPTIMIZATIONS)\n#\n#note: RELEASE_CFLAGS used to contain -ffast-math\n#however, its use resulted in the player getting stuck etc, so be warned if you try re-enabling it.\n#\n\nifeq ($(findstring msvc,$(FTE_TARGET)),msvc)\n\t#msvc doesn't do -dumpmachine.\n\t#we might as well get it to reuse the mingw libraries, if only because that makes those libraries easier to compile...\n\tifeq ($(FTE_TARGET),msvc32)\n\t\tBITS?=32\n\telse ifeq ($(FTE_TARGET),msvc64)\n\t\tBITS?=64\n\tendif\n\tBITS?=32\n\tifeq ($(BITS),64)\n\t\tARCH?=x86_64-w64-mingw32\n\telse\n\t\tARCH?=i686-w64-mingw32\n\tendif\nelse\n\t#some idiot decided that -dumpmachine shouldn't respect -m32 etc.\n\t#at the same time, -print-multiarch is not present, buggy, or just screwed in many gcc builds (ones that target a single arch will unhelpfully just give an empty string).\n\t#so try multiarch first, and if that fails risk dumpmachine giving the wrong values.\n\t#really we want dumpmachine's more specific cpu arch included here, so lets hope that idiot burns for all eternity. or something equally melodramatic.\n\tARCH:=$(shell $(CC) -print-multiarch 2>/dev/null)\n\tifneq ($(words $(ARCH)),1)\n\t\tARCH:=$(shell $(CC) -dumpmachine 2>/dev/null)\n\tendif\n\t#foo:=$(shell echo ARCH is $(ARCH) 1>&2 )\nendif\nARCHLIBS:=$(NATIVE_ABSBASE_DIR)/libs-$(ARCH)\n\n#incase our compiler doesn't support it (mingw)\nifeq ($(shell LANG=c $(CC) -rdynamic 2>&1 | grep unrecognized),)\n\tDEBUG_CFLAGS+= -rdynamic\nendif\n\nPKGCONFIG?=$(ARCH)-pkg-config\nifeq ($(shell which $(PKGCONFIG) 2> /dev/null),)\n\tFFS:=$(shell echo $(PKGCONFIG) not found 1>&2 )\n\tPKGCONFIG=/bin/false #don't end up using eg /usr/include when cross-compiling. makelibs is a valid workaround.\nendif\n#try to statically link\nifeq ($(COMPILE_SYS),Darwin)\n\tifneq (,$(findstring SDL,$(FTE_TARGET)))\n\t\tIMAGELDFLAGS := $(shell $(PKGCONFIG) libpng --variable=libdir)/libpng.a $(shell $(PKGCONFIG) libjpeg --variable=libdir)/libjpeg.a\n\t\tOGGVORBISLDFLAGS := $(shell $(PKGCONFIG) vorbisfile --variable=libdir)/libvorbisfile.a $(shell $(PKGCONFIG) vorbis --variable=libdir)/libvorbis.a $(shell $(PKGCONFIG) ogg --variable=libdir)/libogg.a\n\tendif\nendif\n\n# Statically link everything but SDL/AL for the SDL target,\n# this assumes makelibs; but we don't really care. These are portable builds\n# that can only make one assumption: SDL/AL are installed.\n# Dependencies like libjpeg9 don't exist everywhere.\nifeq ($(FTE_TARGET),SDL2)\n\tIMAGELDFLAGS:= libs-$(ARCH)/libpng.a libs-$(ARCH)/libjpeg.a\n\tOGGVORBISLDFLAGS:= libs-$(ARCH)/libvorbisfile.a libs-$(ARCH)/libvorbis.a libs-$(ARCH)/libogg.a\n\tBASE_CFLAGS+=-DLIBJPEG_STATIC -DLIBJPEG_STATIC -DLIBPNG_STATIC -DOPUS_STATIC -DSPEEX_STATIC -DFREETYPE_STATIC -DLIBVORBISFILE_STATIC\nendif\n\nVPATH := $(BASE_DIR) : $(CLIENT_DIR) : $(GL_DIR) : $(SW_DIR) : $(COMMON_DIR) : $(SERVER_DIR) : $(HTTP_DIR) : $(QUX_DIR) : $(PROGS_DIR) : $(D3D_DIR) : $(VK_DIR) : $(BASE_DIR)/web\n\nPROFILE_CFLAGS=-pg\nDX7SDK=-I./libs/dxsdk7/include/\n\nGLCFLAGS?=-DGLQUAKE\nD3DCFLAGS?=-DD3D9QUAKE -DD3D11QUAKE\nVKCFLAGS?=-DVKQUAKE\n\nCLIENT_OBJS = \\\n\ttextedit.o\t\\\n\tfragstats.o\t\\\n\tzqtp.o\t\\\n\tcl_demo.o\t\\\n\tcl_ents.o\t\\\n\tclq2_ents.o\t\\\n\tcl_input.o\t\\\n\tin_generic.o\t\\\n\tcl_main.o\t\\\n\tcl_parse.o\t\\\n\tcl_pred.o\t\\\n\tcl_tent.o\t\\\n\tcl_cam.o\t\\\n\tcl_screen.o\t\\\n\tpr_clcmd.o\t\\\n\tcl_ignore.o \\\n\tpr_csqc.o\t\\\n\tconsole.o\t\\\n\timage.o\t\\\n\tkeys.o\t\\\n\tmenu.o\t\\\n\tm_master.o\t\\\n\tm_multi.o\t\\\n\tm_items.o\t\\\n\tm_options.o\t\\\n\tm_single.o\t\\\n\tm_script.o\t\\\n\tm_native.o\t\\\n\tm_mp3.o\t\\\n\troq_read.o\t\\\n\tclq2_cin.o\t\\\n\tr_part.o\t\\\n\tp_script.o\t\\\n\tp_null.o\t\\\n\tp_classic.o\t\\\n\tr_partset.o\t\\\n\trenderer.o\t\\\n\trenderque.o\t\\\n\tsbar.o\t\\\n\tskin.o\t\\\n\tsnd_al.o\t\\\n\tsnd_dma.o\t\\\n\tsnd_mem.o\t\\\n\tsnd_mix.o\t\\\n\tsnd_mp3.o\t\\\n\tsnd_ov.o\t\\\n\tvalid.o\t\\\n\tvid_headless.o\t\\\n\tview.o\t\\\n\twad.o\t\t\t\\\n\t\t\t\t\\\n\tftpclient.o\t\t\\\n\t\t\t\t\\\n\t\t\t\t\\\n\tpr_menu.o\n\nVKQUAKE_OBJS =\t\t\\\n\tvk_init.o\t\\\n\tvk_backend.o\n\nGLQUAKE_OBJS =\t\t\\\n\tgl_draw.o\t\t\\\n\tgl_backend.o\t\t\\\n\tgl_rmain.o\t\t\\\n\tgl_rmisc.o\t\t\\\n\tgl_rsurf.o\t\t\\\n\tgl_screen.o\t\t\\\n\tgl_bloom.o\t\t\\\n\tgl_vidcommon.o\t\t\\\n\t$(VKQUAKE_OBJS)\n\nD3DQUAKE_OBJS =\t\t\\\n\td3d8_backend.o \\\n\td3d8_image.o\t\\\n\tvid_d3d8.o\t\\\n\td3d_backend.o \\\n\td3d_image.o\t\\\n\td3d_shader.o\t\\\n\tvid_d3d.o\t\\\n\td3d11_backend.o \\\n\td3d11_image.o\t\\\n\td3d11_shader.o\t\\\n\tvid_d3d11.o\n\t\nD3DGL_OBJS =\t\t\\\n\tgl_font.o \\\n\tgl_ngraph.o\t\t\\\n\tgl_shader.o\t\\\n\tgl_shadow.o\t\\\n\tgl_rlight.o\t\\\n\tgl_warp.o\t\\\n\tltface.o\t\\\n\tr_surf.o\t\\\n\tr_2d.o\n\nMP3_OBJS =\t\t\t\\\n\tfixed.o\t\t\\\n\tbit.o\t\t\t\\\n\ttimer.o\t\t\\\n\tstream.o\t\t\\\n\tframe.o\t\t\\\n\tsynth.o\t\t\\\n\tdecoder.o\t\t\\\n\tlayer12.o\t\t\\\n\tlayer3.o\t\t\\\n\thuffman.o\t\t\\\n\tmymad.o\n\nQCC_OBJS=\t\\\n\tcomprout.o\t\t\\\n\thash.o\t\t\\\n\tqcc_cmdlib.o\t\\\n\tqccmain.o\t\t\\\n\tqcc_pr_comp.o\t\\\n\tqcc_pr_lex.o\t\\\n\tqcd_main.o\nPROGS_OBJS =\t\t\\\n\t$(QCC_OBJS)\t\\\n\tinitlib.o\t\t\\\n\tpr_bgcmd.o\t\t\\\n\tpr_skelobj.o\t\t\\\n\tpr_edict.o\t\t\\\n\tpr_exec.o\t\t\\\n\tpr_multi.o\t\t\\\n\tpr_x86.o\t\t\\\n\tqcdecomp.o\n\nSERVER_OBJS = \t\t\\\n\tpr_cmds.o \t\t\\\n\tpr_q1qvm.o\t\\\n\tpr_lua.o\t\\\n\tsv_master.o \t\\\n\tsv_init.o \t\t\\\n\tsv_main.o \t\t\\\n\tsv_nchan.o \t\t\\\n\tsv_ents.o \t\t\\\n\tsv_send.o \t\t\\\n\tsv_user.o\t\t\\\n\tsv_sql.o\t\t\\\n\tsv_mvd.o\t\t\\\n\tsv_ccmds.o \t\t\\\n\tsv_cluster.o\t\t\\\n\tsv_rankin.o \t\\\n\tsv_chat.o \t\t\\\n\tsv_demo.o\t\t\\\n\tnet_preparse.o \t\\\n\tsavegame.o\t\t\\\n\tsvq2_ents.o \t\\\n\tsvq2_game.o \t\\\n\twebgen.o\t\t\\\n\tftpserver.o\t\t\\\n\thttpserver.o\nMASTER_OBJS=\t\t\\\n\tserver/sv_master.c\t\\\n\tcommon/net_wins.c\t\\\n\tcommon/net_ice.c \t\\\n\tcommon/cvar.c \t\t\\\n\tcommon/cmd.c \t\t\\\n\tcommon/sha1.c \t\t\\\n\tcommon/sha2.c\t\t\\\n\thttp/httpclient.c\t\\\n\tcommon/log.c\t\t\\\n\tcommon/fs.c\t\t\t\\\n\tcommon/fs_stdio.c\t\\\n\tcommon/common.c\t\t\\\n\tcommon/translate.c\t\\\n\tcommon/zone.c\t\t\\\n\tqclib/hash.c\t\t\\\n\tcommon/net_ssl_gnutls.c\n\nSERVERONLY_OBJS =\t\t\\\n\tsv_sys_unix.o\t\t\\\n\tsys_linux_threads.o\n\nWINDOWSSERVERONLY_OBJS = \\\n\tnet_ssl_winsspi.o \\\n\tsv_sys_win.o\t\\\n\tsys_win_threads.o\n\nWINDOWS_OBJS = \\\n\tsnd_win.o \\\n\tsnd_directx.o \\\n\tsnd_xaudio.o \\\n\tsnd_wasapi.o \\\n\tcd_win.o \\\n\tfs_win32.o \\\n\tin_win.o \\\n\tsys_win.o \\\n\tsys_win_threads.o \\\n\tnet_ssl_winsspi.o \\\n\t$(LTO_END) resources.o $(LTO_START)\n\nCOMMON_OBJS = \\\n\tgl_alias.o\t\t\\\n\tgl_hlmdl.o\t\\\n\tgl_heightmap.o\t\t\\\n\tgl_model.o\t\\\n\tcom_bih.o\t\\\n\tcom_mesh.o\t\\\n\tcommon.o \t\t\\\n\tjson.o\t\t\\\n\tcvar.o \t\t\\\n\tcmd.o \t\t\\\n\tcrc.o \t\t\\\n\tnet_ssl_gnutls.o \\\n\tnet_master.o\t\\\n\tfs.o\t\t\t\\\n\tfs_stdio.o\t\t\\\n\tfs_pak.o\t\t\\\n\tfs_zip.o\t\t\\\n\tfs_dzip.o\t\t\\\n\tfs_xz.o\t\t\\\n\tm_download.o\t\\\n\tmathlib.o \t\t\\\n\thuff.o\t\t\\\n\tmd4.o \t\t\\\n\tmd5.o \t\t\\\n\tsha1.o\t\t\\\n\tsha2.o\t\t\\\n\tlog.o \t\t\\\n\tnet_chan.o \t\t\\\n\tnet_wins.o \t\t\\\n\tnet_ice.o \t\t\\\n\thttpclient.o \t\\\n\tzone.o \t\t\\\n\tqvm.o\t\\\n\tr_d3.o\t\\\n\tgl_q2bsp.o\t\t\\\n\tglmod_doom.o \t\\\n\tworld.o \t\t\\\n\tsv_phys.o \t\t\\\n\tsv_move.o \t\t\\\n\tpmove.o\t\t\\\n\tpmovetst.o\t\t\\\n\tiwebiface.o\t\t\\\n\ttranslate.o\t\t\\\n\tplugin.o\t\t\\\n\tq1bsp.o\t\t\\\n\tq2pmove.o\n\nifeq (1,$(LINK_QUAKE3))\n    VPATH := $(VPATH) : $(BASE_DIR)/../plugins/quake3 : $(BASE_DIR)/../plugins/quake3/botlib\n    ALL_CFLAGS+=-DBOTLIB -DBOTLIB_STATIC -DSTATIC_Q3\n    COMMON_OBJS += \t\t\t\\\n\t\tclq3_parse.o\t\\\n\t\tclq3_ui.o\t\\\n\t\tclq3_cg.o \t\\\n\t\tsvq3_game.o\t\\\n\t\tq3common.o\t\\\n\t\tbe_aas_bspq3.o\t\t\\\n\t\tbe_aas_cluster.o\t\\\n\t\tbe_aas_debug.o\t\t\\\n\t\tbe_aas_entity.o\t\t\\\n\t\tbe_aas_file.o\t\t\\\n\t\tbe_aas_main.o\t\t\\\n\t\tbe_aas_move.o\t\t\\\n\t\tbe_aas_optimize.o\t\\\n\t\tbe_aas_reach.o\t\t\\\n\t\tbe_aas_route.o\t\t\\\n\t\tbe_aas_routealt.o\t\\\n\t\tbe_aas_sample.o\t\t\\\n\t\tbe_ai_char.o\t\t\\\n\t\tbe_ai_chat.o\t\t\\\n\t\tbe_ai_gen.o\t\t\\\n\t\tbe_ai_goal.o\t\t\\\n\t\tbe_ai_move.o\t\t\\\n\t\tbe_ai_weap.o\t\t\\\n\t\tbe_ai_weight.o\t\t\\\n\t\tbe_ea.o\t\t\t\\\n\t\tbe_interface.o\t\t\\\n\t\tl_crc.o\t\t\t\\\n\t\tl_libvar.o\t\t\\\n\t\tl_log.o\t\t\t\\\n\t\tl_memory.o\t\t\\\n\t\tl_precomp.o\t\t\\\n\t\tl_script.o\t\t\\\n\t\tl_struct.o\t\nendif\n\nCOMMONLIBFLAGS?=\nCOMMONLDDEPS?=\nCLIENTLIBFLAGS=$(COMMONLIBFLAGS) $(LIBOPUS_STATIC) $(LIBSPEEX_STATIC) $(OGGVORBISFILE_STATIC)\nSERVERLIBFLAGS=$(COMMONLIBFLAGS)\nCLIENTLDDEPS=$(COMMONLDDEPS) $(LIBOPUS_LDFLAGS) $(LIBSPEEX_LDFLAGS) $(OGGVORBISLDFLAGS)\nSERVERLDDEPS=$(COMMONLDDEPS)\nifeq (1,$(USE_OPUS))\n    LIBOPUS_STATIC=-DOPUS_STATIC\n    LIBOPUS_LDFLAGS=-lopus\n    ALL_CFLAGS+=-I/usr/include/opus\n    MAKELIBS+=libs-$(ARCH)/libopus.a\nendif\nifeq (1,$(USE_SPEEX))\n    LIBSPEEX_STATIC=-DSPEEX_STATIC\n    LIBSPEEX_LDFLAGS=-lspeex -lspeexdsp\n    MAKELIBS+=libs-$(ARCH)/libspeex.a libs-$(ARCH)/libspeexdsp.a\nendif\n\nifeq (1,$(USE_VORBISFILE))\n    OGGVORBISFILE_STATIC=-DLIBVORBISFILE_STATIC\n    OGGVORBISLDFLAGS ?= -lvorbisfile -lvorbis -logg\nelse\n    OGGVORBISLDFLAGS=\n    OGGVORBISFILE_STATIC=\nendif\n\n#freetype is annoying.\nifeq (1,$(LINK_FREETYPE))\n    CLIENTLIBFLAGS+=-DFREETYPE_STATIC\n    CLIENTLDDEPS+=$(FREETYPE_LDFLAGS)\nendif\nifneq (\"$(wildcard $(ARCHLIBS))\",\"\")\n    #makelibs has been used. we know what to use here.\n    FREETYPE_CFLAGS:=\n    FREETYPE_LDFLAGS:=-lfreetype\nelse\n    #we're using system libraries... lets hope pkg-config knows it and can handle any cross compiles.\n    FREETYPE_CFLAGS:=$(shell $(PKGCONFIG) freetype2 --cflags --silence-errors)\n    FREETYPE_LDFLAGS:=$(shell $(PKGCONFIG) freetype2 --libs --silence-errors)\n    ifeq (,$(FREETYPE_CFLAGS))\n        #system freetype headers not installed. don't try to include them!\n        FREETYPE_CFLAGS:=-DNO_FREETYPE\n        FREETYPE_LDFLAGS:=\n    endif\nendif\nALL_CFLAGS+=$(FREETYPE_CFLAGS)\n\nifeq (1,$(LINK_PNG))\n    CLIENTLIBFLAGS+=-DLIBPNG_STATIC\n    CLIENTLDDEPS+=-lpng\nendif\nifeq (1,$(LINK_JPEG))\n    CLIENTLIBFLAGS+=-DLIBJPEG_STATIC\n    CLIENTLDDEPS+=-ljpeg\nendif\nifeq (1,$(LINK_ZLIB))\n    CLIENTLIBFLAGS+=-DZLIB_STATIC\n    CLIENTLDDEPS+=-lz\n\tSERVERLDDEPS+=-lz\n\n    #and deflate64, because why not.\n    ifneq (\"$(wildcard $(ARCHLIBS)/infback9.h)\",\"\")\n        CLIENTLIBFLAGS+=-DZLIB_DEFLATE64\n        CLIENTLDDEPS+=-lz9\n        QCC_CFLAGS+=-DZLIB_DEFLATE64\n        QCC_LDFLAGS+=-lz9\n    endif\nendif\nifeq (1,$(LINK_ODE))\n    ALL_CFLAGS+=$(shell $(PKGCONFIG) ode --cflags --silence-errors) -DODE_STATIC\n    COMMONLDDEPS+=$(shell $(PKGCONFIG) ode --libs --silence-errors)\nendif\nifeq (1,$(strip $(INTERNAL_BULLET)))\n    COMMON_OBJS+=com_phys_bullet.o\n    ALL_CFLAGS+=-I/usr/include/bullet -I$(ARCHLIBS)/bullet3-$(BULLETVER)/src\n    COMMONLDDEPS+=-lBulletDynamics -lBulletCollision -lLinearMath\n    LDCC=$(CXX)\n    MAKELIBS+=libs-$(ARCH)/libBulletDynamics.a\nendif\nifeq (1,$(LINK_EZHUD))\n    VPATH := $(VPATH) : $(BASE_DIR)/../plugins/ezhud\n    ALL_CFLAGS+=-DSTATIC_EZHUD #let the plugins code know it needs to add the appropriate entry.\n    CLIENT_OBJS += \\\n\t\tezquakeisms.o\t\\\n\t\thud.o\t\t\t\\\n\t\thud_common.o\t\\\n\t\thud_editor.o\nendif\nifeq (1,$(LINK_OPENSSL))\n    ifeq (1,$(shell $(PKGCONFIG) --atleast-version 3 openssl && echo 1))\t#must exist... and if its not openssl3 then its not gpl3-compatible so refuse to use it.\n        VPATH := $(VPATH) : $(BASE_DIR)/../plugins\n        ALL_CFLAGS+=-DSTATIC_OPENSSL #let the plugins code know it needs to add the appropriate entry.\n        COMMON_OBJS += \\\n            net_ssl_openssl.o\n        ALL_DFLAGS+=$(shell $(PKGCONFIG) openssl --cflags --silence-errors)\n        COMMONLDDEPS+=$(shell $(PKGCONFIG) openssl --libs --silence-errors)\n    endif\nendif\n\nifeq (1,$(shell $(PKGCONFIG) --exists gnutls && echo 1))\n    ALL_CFLAGS+=$(shell $(PKGCONFIG) gnutls --cflags --silence-errors)\n    #ALL_CFLAGS+=-DGNUTLS_STATIC\n    #COMMONLDDEPS+=$(shell $(PKGCONFIG) gnutls --static --libs --silence-errors)\t#we soft-link, so we don't need the -lgnutls stuff.\nelse\n    ALL_CFLAGS+=-DNO_GNUTLS\nendif\n\n\n#the defaults for sdl come first\n#CC_MACHINE:=$(shell $(CC) -dumpmachine)\nifeq ($(FTE_TARGET),SDL2)\n    SDLCONFIG?=sdl2-config\n    FTE_FULLTARGET?=sdl2$(BITS)\nendif\nifeq ($(FTE_TARGET),SDL1)\n    SDLCONFIG?=sdl-config\n    FTE_FULLTARGET?=sdl1$(BITS)\nendif\nifeq ($(FTE_TARGET),SDL)\n    FTE_FULLTARGET?=sdl$(BITS)\nendif\nSDLCONFIG?=sdl-config\nFTE_FULLTARGET?=sdl$(FTE_TARGET)$(BITS)\n\nGLCL_OBJS=$(GL_OBJS) $(D3DGL_OBJS) $(GLQUAKE_OBJS) gl_vidsdl.o snd_sdl.o cd_sdl.o sys_sdl.o in_sdl.o\nGL_EXE_NAME=../$(EXE_NAME)-gl$(FTE_FULLTARGET)\nGLCL_EXE_NAME=../$(EXE_NAME)cl-gl$(FTE_FULLTARGET)\n\n#SDLCONFIG:=libs-$(ARCH)/sdl2_mingw/$(CC_MACHINE)/bin/sdl2-config --prefix=libs-$(ARCH)/sdl2_mingw/$(CC_MACHINE)\nifdef windir\n\tGL_LDFLAGS=$(GLLDFLAGS) -lmingw32 -lws2_32 `$(SDLCONFIG) --static-libs`\n\tVK_LDFLAGS=$(VKLDFLAGS) -lmingw32 -lws2_32 `$(SDLCONFIG) --static-libs`\n\tM_LDFLAGS=$(MLDFLAGS) -lmingw32 -lws2_32 `$(SDLCONFIG) --static-libs`\n\tSV_LDFLAGS=`$(SDLCONFIG) --static-libs`\nelse\n\tGL_LDFLAGS=$(GLLDFLAGS) $(IMAGELDFLAGS) `$(SDLCONFIG) --libs`\n\tVK_LDFLAGS=$(VKLDFLAGS) $(IMAGELDFLAGS) `$(SDLCONFIG) --libs`\n\tM_LDFLAGS=$(MLDFLAGS) $(IMAGELDFLAGS) `$(SDLCONFIG) --libs`\n\tSV_LDFLAGS=`$(SDLCONFIG) --libs`\nendif\nGL_CFLAGS=-DFTE_SDL $(GLCFLAGS) -DMULTITHREAD `$(SDLCONFIG) --cflags`\nGLB_DIR=gl_$(FTE_FULLTARGET)\nGLCL_DIR=glcl_$(FTE_FULLTARGET)\nSV_DIR?=sv_$(FTE_FULLTARGET)\n\nVKCL_OBJS=$(VKQUAKE_OBJS) $(D3DGL_OBJS) gl_bloom.o gl_vidsdl.o snd_sdl.o cd_sdl.o sys_sdl.o in_sdl.o\nVK_CFLAGS=-DFTE_SDL $(VKCFLAGS) -DMULTITHREAD `$(SDLCONFIG) --cflags`\nVKB_DIR=vk_$(FTE_FULLTARGET)\nVKCL_DIR=vk_$(FTE_FULLTARGET)\nVK_EXE_NAME=../$(EXE_NAME)-vk$(FTE_FULLTARGET)\nVKCL_EXE_NAME=../$(EXE_NAME)-vkcl$(FTE_FULLTARGET)\n\nSV_OBJS=$(COMMON_OBJS) $(SERVER_OBJS) $(PROGS_OBJS) $(SERVERONLY_OBJS)\nSV_EXE_NAME=../$(EXE_NAME)-sv$(FTE_FULLTARGET)\nSV_CFLAGS=-DFTE_SDL -DMULTITHREAD `$(SDLCONFIG) --cflags` $(SERVER_ONLY_CFLAGS)\n\nMINGL_DIR=mingl_$(FTE_FULLTARGET)\nMINGL_EXE_NAME=../$(EXE_NAME)-mingl$(FTE_FULLTARGET)\n\nMB_DIR=m_$(FTE_FULLTARGET)\nMCL_DIR=mcl_$(FTE_FULLTARGET)\nM_EXE_NAME=../$(EXE_NAME)-$(FTE_FULLTARGET)\nMCL_EXE_NAME=../$(EXE_NAME)-cl$(FTE_FULLTARGET)\nMCL_OBJS=$(D3DGL_OBJS) $(GLQUAKE_OBJS) $(SOFTWARE_OBJS) gl_vidsdl.o snd_sdl.o cd_sdl.o sys_sdl.o in_sdl.o \nM_CFLAGS=-DFTE_SDL $(VKCFLAGS) $(GLCFLAGS) -DMULTITHREAD `$(SDLCONFIG) --cflags`\n\nMASTER_OBJECTS=$(MASTER_OBJS) server/sv_sys_unix.c common/sys_linux_threads.c\n\nQCC_DIR=qcc$(BITS)\n\nifeq (,$(findstring NO_ZLIB,$(CFLAGS)))\n    SV_LDFLAGS+=-lz\n    GL_LDFLAGS+=-lz\n    VK_LDFLAGS+=-lz\n    M_LDFLAGS+=-lz\n    QCC_LDFLAGS+=-L$(ARCHLIBS) -lz\n    QTV_LDFLAGS+=-lz\nendif\n\n\n\n#specific targets override those defaults as needed.\nifneq (,$(findstring SDL3,$(FTE_TARGET))$(findstring sdl3,$(FTE_TARGET)))\n    FTE_FULLTARGET=$(subst SDL3,sdl3,$(BITS)-$(FTE_TARGET))\n    EXEPOSTFIX=\n    DIRPOSTFIX=$(subst SDL3,sdl3,$(BITS)-$(FTE_TARGET))\n\n    SDL3_PC=$(ARCHLIBS)/lib/pkgconfig/sdl3.pc\n    MAKELIBS+=$(SDL3_PC)\n    ifeq (1,$(shell $(PKGCONFIG) --exists $(SDL3_PC) sdl3 && echo 1))\n        #use the makelibs one if its there\n        SDL3_CFLAGS:=$(shell pkg-config --cflags $(SDL3_PC) sdl3)\n        SDL3_LDFLAGS:=$(shell pkg-config --libs $(SDL3_PC) sdl3)\n    else\n      ifeq (1,$(shell $(PKGCONFIG) --exists sdl3 && echo 1))\n        #maybe we have a system one\n        SDL3_CFLAGS:=$(shell pkg-config --cflags sdl3)\n        SDL3_LDFLAGS=$(shell pkg-config --libs sdl3)\n      else\n        #outright failure. fail the build (without breaking makelibs).\n        NEEDMAKELIBS=1\n      endif\n    endif\n    CLIENTLIBFLAGS+=$(SDL3_CFLAGS)\n\n    GL_CFLAGS=-DFTE_SDL3 $(GLCFLAGS) -DMULTITHREAD $(CLIENTLIBFLAGS)\n    VK_CFLAGS=-DFTE_SDL3 $(VKCFLAGS) -DMULTITHREAD $(CLIENTLIBFLAGS)\n    M_CFLAGS=-DFTE_SDL3 $(VKCFLAGS) $(GLCFLAGS) -DMULTITHREAD $(CLIENTLIBFLAGS)\n    SV_CFLAGS=-DFTE_SDL3 -DMULTITHREAD $(SDL3_CFLAGS) $(SERVER_ONLY_CFLAGS)\n    ifeq ($(BITS),32)\t#we want ofs_t to actually be 64bit.\n        CL_CFLAGS+=-D_LARGEFILE64_SOURCE\n        SV_CFLAGS+=-D_LARGEFILE64_SOURCE\n    endif\n\n    GL_LDFLAGS=$(GLLDFLAGS) $(IMAGELDFLAGS) $(SDL3_LDFLAGS)\n    VK_LDFLAGS=$(VKLDFLAGS) $(IMAGELDFLAGS) $(SDL3_LDFLAGS)\n    M_LDFLAGS =$(MLDFLAGS) $(IMAGELDFLAGS) $(SDL3_LDFLAGS)\n    SV_LDFLAGS=$(SDL3_LDFLAGS)\n\n    #BASELDFLAGS+=-Wl,--warn-common #glibc is just buggy.\n    ifeq (,$(findstring NO_ZLIB,$(CFLAGS)))\t#urgh, messy. this needs a better way\n        BASELDFLAGS+= -lz\n        QCC_LDFLAGS+=-L$(ARCHLIBS) -lz\n        QTV_LDFLAGS+=-lz\n    endif\n\n    GLCL_OBJS=$(GL_OBJS) $(D3DGL_OBJS) $(GLQUAKE_OBJS) gl_vidsdl.o snd_sdl.o cd_sdl.o sys_sdl.o in_sdl.o\n    VKCL_OBJS=$(VKQUAKE_OBJS) $(D3DGL_OBJS) gl_vidsdl.o snd_sdl.o cd_sdl.o sys_sdl.o in_sdl.o\n    MCL_OBJS =$(D3DGL_OBJS) $(GLQUAKE_OBJS) $(SOFTWARE_OBJS) gl_vidsdl.o snd_sdl.o cd_sdl.o sys_sdl.o in_sdl.o\n    SV_OBJS  =$(COMMON_OBJS) $(SERVER_OBJS) $(PROGS_OBJS) $(SERVERONLY_OBJS)\n\n    GL_EXE_NAME   =../$(EXE_NAME)-gl$(FTE_FULLTARGET)$(EXEPOSTFIX)\n    GLCL_EXE_NAME =../$(EXE_NAME)cl-gl$(FTE_FULLTARGET)$(EXEPOSTFIX)\n    VK_EXE_NAME   =../$(EXE_NAME)-vk$(FTE_FULLTARGET)$(EXEPOSTFIX)\n    VKCL_EXE_NAME =../$(EXE_NAME)-vkcl$(FTE_FULLTARGET)$(EXEPOSTFIX)\n    M_EXE_NAME    =../$(EXE_NAME)$(FTE_FULLTARGET)$(EXEPOSTFIX)\n    MCL_EXE_NAME  =../$(EXE_NAME)-cl$(FTE_FULLTARGET)$(EXEPOSTFIX)\n    MINGL_EXE_NAME=../$(EXE_NAME)-mingl$(FTE_FULLTARGET)$(EXEPOSTFIX)\n    SV_EXE_NAME   =../$(EXE_NAME)-sv$(FTE_FULLTARGET)$(EXEPOSTFIX)\n\n    GLB_DIR  =gl$(DIRPOSTFIX)\n    GLCL_DIR =glcl$(DIRPOSTFIX)\n    VKB_DIR  =vk$(DIRPOSTFIX)\n    VKCL_DIR =vkcl$(DIRPOSTFIX)\n    MB_DIR   =m$(DIRPOSTFIX)\n    MCL_DIR  =mcl$(DIRPOSTFIX)\n    MINGL_DIR=mingl$(DIRPOSTFIX)\n    SV_DIR   =sv$(DIRPOSTFIX)\n    QCC_DIR  =qcc$(BITS)\t#doesn't actually use sdl\n\n    ifeq (win,$(findstring win,$(FTE_TARGET)))\n        ifneq (,$(findstring win64,$(FTE_TARGET)))\n            BITS?=64\n        else\n            BITS?=32\n        endif\n        DO_CMAKE      +=-DCMAKE_SYSTEM_NAME=Windows -DCMAKE_FIND_ROOT_PATH_MODE_PROGRAM=\"NEVER\"\n        BASELDFLAGS   += -lws2_32\n        FTE_FULLTARGET =$(BITS)-sdl3\n        EXEPOSTFIX     =.exe\n\n        COMMON_OBJS   += fs_win32.o net_ssl_winsspi.o\n    endif\n\n$(SDL3_PC):\n\tcd $(BASE_DIR) && wget -N http://www.libsdl.org/release/SDL3-$(SDL3VER).tar.gz\n\tmkdir -p $(BASE_DIR)/libs-$(ARCH) && cd $(BASE_DIR)/libs-$(ARCH) && tar -xvzf $(BASE_DIR)/SDL3-$(SDL3VER).tar.gz\n\tcd $(BASE_DIR)/libs-$(ARCH)/SDL3-$(SDL3VER) && CFLAGS=\"$(CFLAGS) -Os\" $(TOOLOVERRIDES) $(DO_CMAKE) -DCMAKE_INSTALL_PREFIX=$(BASE_DIR)/libs-$(ARCH) -DSDL_TESTS=OFF -DSDL_EXAMPLES=OFF -DSDL_STATIC=ON -DSDL_SHARED=OFF -DSDL_RELOCATABLE=ON . && $(TOOLOVERRIDES) $(MAKE) install\nendif\n\n#FTE_TARGET=win32_SDL | FTE_TARGET=win64_SDL (MinGW32 + SDL | MinGW64 + SDL)\nifeq (win_SDL,$(findstring win,$(FTE_TARGET))$(findstring _SDL,$(FTE_TARGET))$(findstring _SDL3,$(FTE_TARGET)))\n    DO_CMAKE+=-DCMAKE_SYSTEM_NAME=Windows -DCMAKE_FIND_ROOT_PATH_MODE_PROGRAM=\"NEVER\"\n\n    ifneq (,$(findstring win64,$(FTE_TARGET)))\n        BITS=64\n    endif\n\n    EXEPOSTFIX=.exe\n\n    ARCH_PREDEP=$(BASE_DIR)/libs-$(ARCH)/SDL2-$(SDL2VER)/$(ARCH)/bin/sdl2-config\n    SDLCONFIG=$(ARCH_PREDEP) --prefix=$(BASE_DIR)/libs-$(ARCH)/SDL2-$(SDL2VER)/$(ARCH)\n    CLIENTLIBFLAGS+=`$(SDLCONFIG) --cflags`\n\n    COMMON_OBJS   += fs_win32.o net_ssl_winsspi.o\n\n    #the defaults for sdl come first\n    GLCL_OBJS=$(GL_OBJS) $(D3DGL_OBJS) $(GLQUAKE_OBJS) gl_vidsdl.o snd_sdl.o cd_sdl.o sys_sdl.o in_sdl.o snd_directx.o $(LTO_END) resources.o $(LTO_START)\n    GL_EXE_NAME=../$(EXE_NAME)-sdl-gl$(BITS)$(EXEPOSTFIX)\n    GLCL_EXE_NAME=../$(EXE_NAME)-sdl-glcl$(BITS)$(EXEPOSTFIX)\n    ifneq ($(SDL_STATIC),0)\n\t\t#statically link by default. dlls suck.\n        GL_LDFLAGS=$(GLLDFLAGS) -lmingw32 -lws2_32 `$(SDLCONFIG) --static-libs`\n        VK_LDFLAGS=$(GLLDFLAGS) -lmingw32 -lws2_32 `$(SDLCONFIG) --static-libs`\n        M_LDFLAGS=$(MLDFLAGS) -lmingw32 -lws2_32 `$(SDLCONFIG) --static-libs`\n\t\tD3D_LDFLAGS=$(MLDFLAGS) -lmingw32 -lws2_32 `$(SDLCONFIG) --static-libs`\n        SV_LDFLAGS=-lm -lmingw32 -lws2_32 -lwinmm `$(SDLCONFIG) --static-libs`\n        QCC_LDFLAGS=\n    else\n\t\t#or dynamically link when SDL_STATIC=0\n        GL_LDFLAGS=$(IMAGELDFLAGS) -lws2_32 -lmingw32 $(SDL_LDFLAGS) -mwindows -ldxguid -lwinmm -lole32 $(GLLDFLAGS) `$(SDLCONFIG) --libs` \n        VK_LDFLAGS=$(IMAGELDFLAGS) -lws2_32 -lmingw32 $(SDL_LDFLAGS) -mwindows -ldxguid -lwinmm -lole32 $(GLLDFLAGS) `$(SDLCONFIG) --libs` \n        M_LDFLAGS=$(IMAGELDFLAGS) -lws2_32 -lmingw32 $(SDL_LDFLAGS) -mwindows -ldxguid -lwinmm -lole32 $(MLDFLAGS) `$(SDLCONFIG) --libs`\n\t\tD3D_LDFLAGS=$(IMAGELDFLAGS) -lws2_32 -lmingw32 $(SDL_LDFLAGS) -mwindows -ldxguid -lwinmm -lole32 $(MLDFLAGS) `$(SDLCONFIG) --libs`\n        SV_LDFLAGS=-lm -lmingw32 -lws2_32 -lwinmm `$(SDLCONFIG) --libs`\n        QCC_LDFLAGS=\n    endif\n\n    GL_CFLAGS=-DFTE_SDL $(GLCFLAGS) $(CLIENTLIBFLAGS) $(DX7SDK)\n\n    GLB_DIR=gl_mgw_sdl$(BITS)\n    GLCL_DIR=glcl_mgw_sdl$(BITS)\n\t\n\t#don't use sdl at all for dedicated servers. it'd break running it as a console program etc. its not officially supported, but included anyway for ease of use.\n\tSV_DIR=sv_winsdl$(BITS)\n    SV_OBJS=$(COMMON_OBJS) $(SERVER_OBJS) $(PROGS_OBJS) $(WINDOWSSERVERONLY_OBJS) fs_win32.o $(LTO_END) resources.o $(LTO_START)\n    SV_EXE_NAME=../$(EXE_NAME)-sdl-sv$(BITS)$(EXEPOSTFIX)\n\tSV_LDFLAGS=-lm -lole32 -lws2_32 -lwinmm\n    SV_CFLAGS=$(SERVER_ONLY_CFLAGS) -mconsole #-DFTE_SDL\n\n    MINGL_DIR=mingl_sdlwin$(BITS)\n    MINGL_EXE_NAME=../$(EXE_NAME)-sdl-mingl$(BITS)$(EXEPOSTFIX)\n\n    MB_DIR=m_mgw_sdl$(BITS)\n    M_EXE_NAME=../$(EXE_NAME)-sdl$(BITS)$(EXEPOSTFIX)\n#with d3d...\n    #MCL_OBJS=$(D3DGL_OBJS) $(GLQUAKE_OBJS) $(SOFTWARE_OBJS) $(D3DQUAKE_OBJS) gl_vidsdl.o snd_sdl.o cd_sdl.o sys_sdl.o in_sdl.o snd_directx.o $(LTO_END) resources.o $(LTO_START)\n    #M_CFLAGS=$(D3DCFLAGS) $(VKCFLAGS) $(GLCFLAGS) -DFTE_SDL $(CLIENTLIBFLAGS) $(DX7SDK)\n#without d3d...\n    MCL_OBJS=$(D3DGL_OBJS) $(GLQUAKE_OBJS) $(SOFTWARE_OBJS) gl_vidsdl.o snd_sdl.o cd_sdl.o sys_sdl.o in_sdl.o snd_directx.o $(LTO_END) resources.o $(LTO_START)\n    M_CFLAGS=$(VKCFLAGS) $(GLCFLAGS) -DFTE_SDL $(CLIENTLIBFLAGS) $(DX7SDK)\n\n\t#unsupported, video code has winmsg/input junk in it.\n    D3DCL_OBJS=$(D3DQUAKE_OBJS) snd_sdl.o cd_sdl.o sys_sdl.o in_sdl.o snd_directx.o $(D3DGL_OBJS) $(LTO_END) resources.o $(LTO_START)\n    D3D_EXE_NAME=../$(EXE_NAME)-sdl-d3d$(BITS)$(EXEPOSTFIX)\n    D3DCL_EXE_NAME=../$(EXE_NAME)-sdl-d3dcl$(BITS)$(EXEPOSTFIX)\n    D3D_CFLAGS=$(D3DCFLAGS) -DFTE_SDL -DNO_XFLIP $(CLIENTLIBFLAGS) $(DX7SDK)\n    D3DB_DIR=sdl_d3d_mgw$(BITS)\n    D3DCL_DIR=sdl_d3dcl_mgw$(BITS)\n\n\n    VKCL_OBJS=$(VKQUAKE_OBJS) gl_bloom.o gl_vidsdl.o snd_sdl.o cd_sdl.o sys_sdl.o in_sdl.o snd_directx.o $(D3DGL_OBJS) $(LTO_END) resources.o $(LTO_START)\n    VK_EXE_NAME=../$(EXE_NAME)-sdl-vk$(BITS)$(EXEPOSTFIX)\n    VKCL_EXE_NAME=../$(EXE_NAME)-sdl-vkcl$(BITS)$(EXEPOSTFIX)\n    VK_CFLAGS=$(VKCFLAGS) -DFTE_SDL -DNO_XFLIP $(CLIENTLIBFLAGS) $(DX7SDK)\n    VKB_DIR=sdl_vk_mgw$(BITS)\n    VKCL_DIR=sdl_vkcl_mgw$(BITS)\n\t\n    ifeq ($(shell echo $(FTE_TARGET)|grep -E -i -v \"win32.*sdl\"),)\n        GL_CFLAGS+= -D_MINGW_VFPRINTF\n        VK_CFLAGS+= -D_MINGW_VFPRINTF\n        D3D_CFLAGS+= -D_MINGW_VFPRINTF\n        M_CFLAGS+= -D_MINGW_VFPRINTF\n    endif\nendif\n\n#FTE_TARGET=vc (Visual C)\nifeq ($(findstring msvc,$(FTE_TARGET)),msvc)\n\t#assumptions...\n\tifneq ($(shell uname -o 2>&1 | grep Cygwin),)\n\t\t#we're on cygwin\n\t\tMSVCPATH?=C:/Program Files (x86)/Microsoft Visual Studio 8/VC/\n\t\tWINDOWSSDKDIR?=C:/Program Files/Microsoft SDKs/Windows/v7.1/\n\t\tFIXPATH=`cygpath -m \"$(1)\"`\n\t\tINVOKE=\"$(1)\"\n\telse\n\t\t#we're PROBABLY on linux (invoke it via wine)\n\t\t#MSVCPATH=/mnt/win/Program Files (x86)/Microsoft Visual Studio 10.0/VC/\n\t\tMSVCPATH?=/mnt/win/Program Files (x86)/Microsoft Visual Studio 8/VC/\n\t\tWINDOWSSDKDIR?=/mnt/win/Program Files/Microsoft SDKs/Windows/v7.1/\n\t\t#functions to handle wine (or cygwin)\n\t\tFIXPATH=`winepath -w \"$(1)\"`\n\t\tINVOKE=WINEDEBUG=-all WINEPATH=\"$(WINEPATH)\" wine \"$(1)\"\n\tendif\n\n\t#our msvc library naming still assumes 32bit and thus no postfix for that target (microsoft usually reuse '32' for 64bit libraries too...).\n\tifeq ($(BITS),32)\n\t\tLIBBITS=\n\telse\n\t\tLIBBITS=$(BITS)\n\tendif\n\n\t#evil lameness.\n\tLIBS_DIR=./libs/\n\n\tifeq ($(BITS),64)\n\t\tMSVC_ARCH=amd64/\n\t\tWINSDK_ARCH=x64/\n\t\tWINEPATH=\n\telse\n\t\tMSVC_ARCH=\n\t\tWINSDK_ARCH=\n\t\tWINEPATH=$(MSVCPATH)../Common7/IDE/\n\tendif\n\n\tLIBOPUS_STATIC=\n\n    #-I\"$(MSVCPATH)/ATLMFC/INCLUDE\"\n\tMSVCINC+=-I\"$(shell winepath -w \"$(MSVCPATH)INCLUDE\")\"\n\tMSVCINC+=-I\"$(shell winepath -w \"$(WINDOWSSDKDIR)/Include\")\"\n\t#-I\"$(MSVCPATH)PlatformSDK/include\"\n\t#-I\"$(MSVCPATH)../SDK/v2.0/include\"\n\n\tMSVCLIB+=/libpath:\"$(call FIXPATH,$(MSVCPATH)lib/$(MSVC_ARCH))\"\n\tMSVCLIB+=/libpath:\"$(call FIXPATH,$(WINDOWSSDKDIR)Lib/$(WINSDK_ARCH))\"\n\tMSVCLIB+=/libpath:\"$(call FIXPATH,$(LIBS_DIR))\"\n\n\tARCHLIBS:=$(call FIXPATH,$(ARCHLIBS))\n\n\tMSVCINC:=$(MSVCINC)\n\tDO_WINDRES=$(DO_ECHO) $(WINDRES) /nologo $(BRANDFLAGS) $(MSVCINC) -I$(call FIXPATH,$(CLIENT_DIR)) -fo $(call FIXPATH,$@) $(call FIXPATH,$<)\n\tWINDRES=$(call INVOKE,$(WINDOWSSDKDIR)Bin/$(WINSDK_ARCH)RC.exe)\n\tLINK:=$(call INVOKE,$(MSVCPATH)/bin/$(MSVC_ARCH)link.exe)\n\tCC:=$(call INVOKE,$(MSVCPATH)/bin/$(MSVC_ARCH)cl.exe) $(MSVCINC) -DMSVCLIBSPATH=libs/\n\tCXX=$(CC)\n\n\t#library stuff... we'll be breaking attempts to statically link because deps are too painful otherwise.\n\tBASELDFLAGS=\n\tCLIENTLIBFLAGS=\n\tSERVERLIBFLAGS=\n\tCLIENTLDDEPS=\n\tSERVERLDDEPS=\n\n\t#override various flags that expected gcc.\n\tDEBUG_CFLAGS = -Od $(CPUOPTIMIZATIONS) /fp:fast\n\tRELEASE_CFLAGS = -O2 -Gy -GS- $(CPUOPTIMIZATIONS) /fp:fast\n\tRELEASE_LDFLAGS = /LTCG\n\n\t#we need some command tweaks.\n\tDO_CC=$(DO_ECHO) $(CC) /nologo $(ALL_CFLAGS) -Fo\"$(call FIXPATH,$@)\" -c \"$(call FIXPATH,$<)\"\n\tDO_CXX=$(DO_ECHO) $(CXX) /nologo $(ALL_CFLAGS) -Fo\"$(call FIXPATH,$@)\" -c \"$(call FIXPATH,$<)\"\n\tDO_LD=$(DO_ECHO) PATH=\"$(MSVCPATH)/Common7/IDE:$(PATH)\" $(LINK) /nologo /out:\"$(call FIXPATH,$@)\" /nodefaultlib:libc.lib /LARGEADDRESSAWARE /nodefaultlib:MSVCRT $(MSVCLIB) $(SDKLIB) /manifest:no /OPT:REF  wsock32.lib user32.lib kernel32.lib advapi32.lib winmm.lib libs/zlib$(LIBBITS).lib shell32.lib\n\tSTRIP=@echo SKIP: strip\n\tEXEPOSTFIX=.exe\n\n\t#gotta work around a whole load of paths.\n\tBASE_CFLAGS:=$(WARNINGFLAGS) $(GNUC_FUNCS) -I$(call FIXPATH,$(CLIENT_DIR)) -I$(call FIXPATH,$(SERVER_DIR)) -I$(call FIXPATH,$(COMMON_DIR)) -I$(call FIXPATH,$(GL_DIR)) -I$(call FIXPATH,$(D3D_DIR)) -I$(call FIXPATH,$(PROGS_DIR)) -I. -I$(LIBS_DIR) -I$(LIBS_DIR)/dxsdk9/include -I$(LIBS_DIR)/dxsdk7/include $(SDL_INCLUDES) $(BOTLIB_CFLAGS) $(SVNREVISION)\n\n\n\t#this stuff isn't supported.\n\tPRECOMPHEADERS=\n\tDEPCC=\n\tDEPCXX=\n\n\t#dedicated server stuff\n\tSV_CFLAGS=$(SERVER_ONLY_CFLAGS) $(W32_CFLAGS) -DMULTITHREAD -DMSVCLIBPATH=libs/\n\tSV_EXE_NAME=../$(EXE_NAME)-sv$(BITS)$(EXEPOSTFIX)\n\tSV_DIR=sv_vc$(BITS)\n\tSV_OBJS=$(COMMON_OBJS) $(SERVER_OBJS) $(PROGS_OBJS) $(WINDOWSSERVERONLY_OBJS) fs_win32.o resources.o\n\tSV_LDFLAGS=ole32.lib /subsystem:console\n\n\tGL_EXE_NAME=../$(EXE_NAME)-gl$(BITS)$(EXEPOSTFIX)\n\tGLCL_EXE_NAME=../$(EXE_NAME)-mingl$(BITS)\n\tGLB_DIR=gl_vc$(BITS)\n\tGLCL_DIR=glcl_vc$(BITS)\n\tGL_LDFLAGS=$(GLLDFLAGS) $(JPEGLIB) libs/libpng$(BITS).lib uuid.lib gdi32.lib ole32.lib /subsystem:windows\n\tGL_CFLAGS=$(GLCFLAGS) $(W32_CFLAGS) -DMULTITHREAD -DMSVCLIBPATH=libs/\n\tGLCL_OBJS=$(D3DGL_OBJS) $(GLQUAKE_OBJS) gl_vidnt.o $(WINDOWS_OBJS)\n\tGL_OBJS=\n\n\tMINGL_DIR=mingl_vc$(BITS)\n\tMINGL_EXE_NAME=../$(EXE_NAME)-mingl$(BITS)$(EXEPOSTFIX)\n\n\tVKCL_OBJS=$(VKQUAKE_OBJS) $(D3DGL_OBJS) gl_bloom.o gl_vidnt.o $(WINDOWS_OBJS)\n\tVK_EXE_NAME=../$(EXE_NAME)-vk$(BITS)$(EXEPOSTFIX)\n\tVKCL_EXE_NAME=../$(EXE_NAME)-vkcl$(BITS)$(EXEPOSTFIX)\n\tVK_CFLAGS=$(VKCFLAGS) $(CLIENTLIBFLAGS) $(DX7SDK) -DMULTITHREAD -DMSVCLIBPATH=libs/\n\tVK_LDFLAGS=$(JPEGLIB) libs/libpng$(BITS).lib uuid.lib gdi32.lib ole32.lib /subsystem:windows\n\tVKB_DIR=vk_vc$(BITS)\n\tVKCL_DIR=vkcl_vc$(BITS)\n\n\tD3DCL_OBJS=$(D3DQUAKE_OBJS) $(D3DGL_OBJS) $(WINDOWS_OBJS)\n\tD3D_EXE_NAME=../$(EXE_NAME)-d3d$(BITS)$(EXEPOSTFIX)\n\tD3DCL_EXE_NAME=../$(EXE_NAME)-d3dcl$(BITS)$(EXEPOSTFIX)\n\tD3D_LDFLAGS=$(JPEGLIB) libs/libpng$(BITS).lib uuid.lib gdi32.lib ole32.lib /subsystem:windows\n\tD3D_CFLAGS=$(D3DCFLAGS) $(W32_CFLAGS) $(DX7SDK) -DMULTITHREAD -DMSVCLIBPATH=libs/\n\tD3DB_DIR=d3d_vc$(BITS)\n\tD3DCL_DIR=d3dcl_vc$(BITS)\n\n\t#merged client stuff\n\tMCL_OBJS=$(D3DGL_OBJS) $(GLQUAKE_OBJS) $(SOFTWARE_OBJS) $(D3DQUAKE_OBJS) gl_vidnt.o gl_videgl.o $(WINDOWS_OBJS)\n\tM_EXE_NAME=../$(EXE_NAME)$(BITS)$(EXEPOSTFIX)\n\tMCL_EXE_NAME=../$(EXE_NAME)cl$(BITS)$(EXEPOSTFIX)\n\tM_CFLAGS=$(GLCFLAGS) $(W32_CFLAGS) $(D3DCFLAGS) $(DX7SDK) $(VKCFLAGS) -DMULTITHREAD $(CLIENTLIBFLAGS)\n\tMB_DIR=m_vc$(BITS)\n\tMCL_DIR=mcl_vc$(BITS)\n\tM_LDFLAGS=$(GLLDFLAGS) $(JPEGLIB) libs/libpng$(LIBBITS).lib uuid.lib gdi32.lib ole32.lib /subsystem:windows\nendif\n\n#FTE_TARGET=win32 | FTE_TARGET=win64 (MinGW32 | MinGW64)\nifeq (win,$(findstring win,$(FTE_TARGET))$(findstring _SDL,$(FTE_TARGET)))\n\t# The extra object file called resources.o is specific for MinGW to link the icon in\n#\tDO_CMAKE+=-DCMAKE_SYSTEM_NAME=Windows -DCMAKE_FIND_ROOT_PATH_MODE_PROGRAM=\"NEVER\"\n\n\tDO_CMAKE=cmake -DCMAKE_TOOLCHAIN_FILE=/home/spike/fteqw/fteqw-code/cmakesucks.cmake -DBUILD_SHARED_LIBS=OFF -DCMAKE_SYSTEM_NAME=Windows -DCMAKE_C_COMPILER=\"$(firstword $(CC))\" -DCMAKE_C_FLAGS=\"$(wordlist 2,99,$(CC))\" -DCMAKE_CXX_COMPILER=\"$(firstword $(CXX))\" -DCMAKE_CXX_FLAGS=\"$(wordlist 2,99,$(CXX))\" \n\n\t#cygwin's gcc requires an extra command to use mingw instead of cygwin (default paths, etc).\n\tifneq ($(shell $(CC) -dumpmachine 2>&1 | grep cygwin),)\n\t\tW32_CFLAGS=-mno-cygwin\n\tendif\n\t\n\tifeq ($(FTE_TARGET),win64)\n\t\tBITS=64\n\tendif\n\tQCC_DIR=winqcc$(BITS)\n\n\tBASELDFLAGS=\n\n\n\t# Allow 32bit FTE to access beyond the 2GB address space\n\tifeq ($(FTE_TARGET),win32)\n\t\tBASELDFLAGS=-Wl,--large-address-aware\n\tendif\n\t#Note: for deterministic builds, the following line disables timestamps for import/export tables. This is UNSAFE if there are any PE files bound to the compiled PE file. Our plugin dlls are dynamically loaded so this should not be an issue for us.\n\tBASELDFLAGS+=-Wl,--no-insert-timestamp\n\tDEBUG_LDFLAGS=-Wl,--no-dynamicbase\t#debug builds are useful for catching crashes. the resulting stack traces are not very useful if we don't even know what the base address should be.\n\tRELEASE_LDFLAGS=-Wl,--dynamicbase\t#release builds should attempt to use aslr\n\n\tBASELDFLAGS+=-lcomctl32\n\tEXEPOSTFIX=.exe\n\n\tQTV_LDFLAGS+=-lws2_32 -lwinmm\n\n\tSV_EXE_NAME=../$(EXE_NAME)sv$(BITS)$(EXEPOSTFIX)\n\tSV_LDFLAGS=-lws2_32 -lwinmm -lole32\n\tSV_DIR=sv_mingw$(BITS)\n\tSV_OBJS=$(COMMON_OBJS) $(SERVER_OBJS) $(PROGS_OBJS) $(WINDOWSSERVERONLY_OBJS) fs_win32.o $(LTO_END) resources.o $(LTO_START)\n\tSV_CFLAGS=$(SERVER_ONLY_CFLAGS) $(W32_CFLAGS)\n\n\tMASTER_OBJECTS=$(MASTER_OBJS) server/sv_sys_win.c common/sys_win_threads.c common/net_ssl_winsspi.c\n\n\tGLCL_OBJS=$(GL_OBJS) $(D3DGL_OBJS) $(GLQUAKE_OBJS) gl_vidnt.o $(WINDOWS_OBJS)\n\tGL_EXE_NAME=../fteglqw$(BITS)$(EXEPOSTFIX)\n\tGLCL_EXE_NAME=../fteglqwcl$(BITS)$(EXEPOSTFIX)\n\tGL_LDFLAGS=$(GLLDFLAGS) $(IMAGELDFLAGS) -ldxguid -lws2_32 -lwinmm -lgdi32 -lole32 -Wl,--subsystem,windows\n\tGL_CFLAGS=$(GLCFLAGS) $(W32_CFLAGS) $(DX7SDK) -DMULTITHREAD $(CLIENTLIBFLAGS)\n\tGLB_DIR=gl_mgw$(BITS)\n\tGLCL_DIR=glcl_mgw$(BITS)\n\n\tMCL_OBJS=$(D3DGL_OBJS) $(GLQUAKE_OBJS) $(SOFTWARE_OBJS) $(D3DQUAKE_OBJS) gl_vidnt.o gl_videgl.o $(WINDOWS_OBJS)\n\tM_EXE_NAME=../$(EXE_NAME)$(BITS)$(EXEPOSTFIX)\n\tMCL_EXE_NAME=../$(EXE_NAME)cl$(BITS)$(EXEPOSTFIX)\n\tM_LDFLAGS=$(GLLDFLAGS) $(IMAGELDFLAGS) -ldxguid -lws2_32 -lwinmm -lgdi32 -lole32 -Wl,--subsystem,windows\n\tM_CFLAGS=$(GLCFLAGS) $(W32_CFLAGS) $(D3DCFLAGS) $(DX7SDK) $(VKCFLAGS) -DMULTITHREAD $(CLIENTLIBFLAGS)\n\tMB_DIR=m_mgw$(BITS)\n\tMCL_DIR=mcl_mgw$(BITS)\n\n\tD3DCL_OBJS=$(D3DQUAKE_OBJS) $(D3DGL_OBJS) $(WINDOWS_OBJS)\n\tD3D_EXE_NAME=../fted3dqw$(BITS)$(EXEPOSTFIX)\n\tD3DCL_EXE_NAME=../fted3dclqw$(BITS)$(EXEPOSTFIX)\n\tD3D_LDFLAGS=$(IMAGELDFLAGS) -ldxguid -lws2_32 -lwinmm -lgdi32 -lole32 -Wl,--subsystem,windows\n\tD3D_CFLAGS=$(D3DCFLAGS) $(W32_CFLAGS) $(DX7SDK) -DMULTITHREAD $(CLIENTLIBFLAGS)\n\tD3DB_DIR=d3d_mgw$(BITS)\n\tD3DCL_DIR=d3dcl_mgw$(BITS)\n\n\tVKCL_OBJS=$(GLQUAKE_OBJS) $(D3DGL_OBJS) $(WINDOWS_OBJS) gl_vidnt.o\n\tVK_EXE_NAME=../ftevkqw$(BITS)$(EXEPOSTFIX)\n\tVKCL_EXE_NAME=../ftevkclqw$(BITS)$(EXEPOSTFIX)\n\tVK_LDFLAGS=$(IMAGELDFLAGS) -ldxguid -lws2_32 -lwinmm -lgdi32 -lole32 -Wl,--subsystem,windows\n\tVK_CFLAGS=$(VKCFLAGS) $(W32_CFLAGS) $(DX7SDK) -DMULTITHREAD $(CLIENTLIBFLAGS)\n\tVKB_DIR=vk_mgw$(BITS)\n\tVKCL_DIR=vkcl_mgw$(BITS)\n\n\tMINGL_EXE_NAME=../fteminglqw$(BITS)$(EXEPOSTFIX)\n\tMINGL_DIR=mingl_mgw$(BITS)\n\n\tifeq (,$(findstring NO_ZLIB,$(CFLAGS)))\n\t\tSV_LDFLAGS+=-lz\n\t\tGL_LDFLAGS+=-lz\n\t\tVK_LDFLAGS+=-lz\n\t\tM_LDFLAGS+=-lz\n\t\tQCC_LDFLAGS+=-L$(ARCHLIBS) -lz\n\tendif\n\tifeq ($(NOCOMPAT),1)\n\t\tSV_EXE_NAME=../engine-sv$(BITS)$(EXEPOSTFIX)\n\t\tGL_EXE_NAME=../engine-gl$(BITS)$(EXEPOSTFIX)\n\t\tVK_EXE_NAME=../engine-vk$(BITS)$(EXEPOSTFIX)\n\t\tM_EXE_NAME=../engine$(BITS)$(EXEPOSTFIX)\n\t\tD3D_EXE_NAME=../engine-d3d$(BITS)$(EXEPOSTFIX)\n\t\tMINGL_EXE_NAME=../engine-mingl$(BITS)$(EXEPOSTFIX)\n\tendif\nendif\n\nifeq ($(FTE_TARGET),bsd)\n\t#mostly uses the linux stuff.\n\t#oss, X, etc.\n\tCC=cc\n\tCXX=c++\n\tSV_DIR=sv_bsd\n\tSV_EXE_NAME=../$(EXE_NAME)-sv$(BITS)\n\tSV_LDFLAGS=-lpthread\n\tSV_CFLAGS=$(SERVER_ONLY_CFLAGS) -DMULTITHREAD\n\n\tGLCL_OBJS=$(GL_OBJS) $(D3DGL_OBJS) $(GLQUAKE_OBJS) gl_vidlinuxglx.o snd_linux.o cd_null.o sys_linux.o sys_linux_threads.o\n\tGL_EXE_NAME=../$(EXE_NAME)-gl\n\tGLCL_EXE_NAME=../$(EXE_NAME)-glcl\n\tGL_LDFLAGS= -L/usr/local/lib $(GLLDFLAGS) $(XLDFLAGS) -lpthread\n\tGL_CFLAGS=$(GLCFLAGS) -I/usr/local/include -I/usr/X11R6/include -I/usr/X11R6/include/freetype2 -DMULTITHREAD -DAUDIO_OSS\n\tGLB_DIR=gl_bsd\n\tGLCL_DIR=glcl_bsd\n\n\tMCL_OBJS=$(D3DGL_OBJS) $(GLQUAKE_OBJS) $(SOFTWARE_OBJS) gl_vidlinuxglx.o snd_linux.o cd_null.o sys_linux.o sys_linux_threads.o\n\tM_EXE_NAME=../$(EXE_NAME)\n\tMCL_EXE_NAME=../$(EXE_NAME)-cl\n\tM_LDFLAGS= -L/usr/local/lib -L/usr/X11R6/lib $(GLLDFLAGS) $(XLDFLAGS) -lpthread\n\tM_CFLAGS=$(VKCFLAGS) $(GLCFLAGS) -I/usr/local/include -I/usr/X11R6/include -I/usr/X11R6/include/freetype2 -DMULTITHREAD -DAUDIO_OSS\n\tMB_DIR=m_bsd\n\tMCL_DIR=mcl_bsd\n\n\tMINGL_EXE_NAME=../$(EXE_NAME)-mingl\n\tMINGL_DIR=mingl_bsd\n\n\t#openbsd has a special library for oss emulation.\n\tifeq ($(shell uname -s),OpenBSD)\n\t\tGL_LDFLAGS+= -lossaudio -lfreetype\n\t\tVK_LDFLAGS+= -lossaudio -lfreetype\n\t\tM_LDFLAGS+= -lossaudio -lfreetype\n\t\tM_CFLAGS+= -DFREETYPE_STATIC\n\t\tVK_CFLAGS+= -DFREETYPE_STATIC\n\t\tGL_CFLAGS+= -DFREETYPE_STATIC\n\tendif\n\n\tifeq (,$(findstring NO_ZLIB,$(CFLAGS)))\n\t\tSV_LDFLAGS+= -lz\n\t\tGL_LDFLAGS+= -lz\n\t\tVK_LDFLAGS+= -lz\n\t\tM_LDFLAGS+= -lz\n\tendif\nendif\nifneq (,$(findstring linux,$(FTE_TARGET)))\n\tSV_DIR=sv_linux$(BITS)\n\tSV_EXE_NAME=../$(EXE_NAME)-sv$(BITS)\n\tSV_LDFLAGS=\n\tSV_CFLAGS=$(SERVER_ONLY_CFLAGS) -DMULTITHREAD\n\n\tifneq (\"$(wildcard /usr/include/wayland-client.h)\",\"\")\n\t\tHAVE_WAYLAND=-DWAYLANDQUAKE\n\telse\n\t\tHAVE_WAYLAND=\n\tendif\n\tifneq (\"$(wildcard /usr/include/EGL/egl.h)\",\"\")\n\t\tHAVE_EGL=-DUSE_EGL\n\telse\n\t\tHAVE_EGL=\n\tendif\n\n\tCL_CFLAGS=-DMULTITHREAD -DDYNAMIC_SDL $(HAVE_EGL) $(HAVE_WAYLAND) -DX11QUAKE -DAUDIO_PULSE -DAUDIO_ALSA -DAUDIO_OSS -DAUDIO_SDL\n\tBASELDFLAGS+=-Wl,--warn-common\n\t\n\tifeq ($(BITS),32)\n\t\tCL_CFLAGS+=-D_LARGEFILE64_SOURCE\n\t\tSV_CFLAGS+=-D_LARGEFILE64_SOURCE\n\tendif\n\t\n\tQCC_DIR=linqcc$(BITS)\n\n\tGLCL_OBJS=$(GL_OBJS) $(D3DGL_OBJS) $(GLQUAKE_OBJS) gl_vidlinuxglx.o gl_vidwayland.o gl_videgl.o snd_pulse.o snd_alsa.o snd_linux.o snd_sdl.o cd_linux.o sys_linux.o sys_linux_threads.o\n\tGL_EXE_NAME=../$(EXE_NAME)-gl$(BITS)\n\tGLCL_EXE_NAME=../$(EXE_NAME)-glcl$(BITS)\n\tGL_LDFLAGS=$(GLLDFLAGS) $(XLDFLAGS)\n\tGL_CFLAGS=$(GLCFLAGS) -I/usr/X11R6/include $(CL_CFLAGS) $(CLIENTLIBFLAGS)\n\tGLB_DIR=gl_linux$(BITS)\n\tGLCL_DIR=glcl_linux$(BITS)\n\n\tVKCL_OBJS=$(GL_OBJS) $(D3DGL_OBJS) $(GLQUAKE_OBJS) gl_vidlinuxglx.o gl_vidwayland.o gl_videgl.o snd_pulse.o snd_alsa.o snd_linux.o snd_sdl.o cd_linux.o sys_linux.o sys_linux_threads.o\n\tVK_EXE_NAME=../$(EXE_NAME)-vk$(BITS)\n\tVKCL_EXE_NAME=../$(EXE_NAME)-vkcl$(BITS)\n\tVK_LDFLAGS=$(GLLDFLAGS) $(XLDFLAGS)\n\tVK_CFLAGS=$(VKCFLAGS) -I/usr/X11R6/include $(CL_CFLAGS) $(CLIENTLIBFLAGS)\n\tVKB_DIR=vk_linux$(BITS)\n\tVKCL_DIR=vkcl_linux$(BITS)\n\n\tMCL_OBJS=$(D3DGL_OBJS) $(GLQUAKE_OBJS) $(SOFTWARE_OBJS) gl_vidlinuxglx.o gl_vidwayland.o gl_videgl.o snd_linux.o snd_sdl.o snd_pulse.o snd_alsa.o cd_linux.o sys_linux.o sys_linux_threads.o\n\tM_EXE_NAME=../$(EXE_NAME)$(BITS)\n\tMCL_EXE_NAME=../$(EXE_NAME)-cl$(BITS)\n\tM_LDFLAGS=$(GL_LDFLAGS)\n\tM_CFLAGS=$(VKCFLAGS) $(GL_CFLAGS) $(CLIENTLIBFLAGS)\n\tMB_DIR=m_linux$(BITS)\n\tMCL_DIR=mcl_linux$(BITS)\n\n\tifeq (,$(findstring NO_ZLIB,$(CFLAGS)))\n\t\tSV_LDFLAGS+= -lz\n\t\tGL_LDFLAGS+= -lz\n\t\tVK_LDFLAGS+= -lz\n\t\tM_LDFLAGS+= -lz\n\tendif\n\n\n\tMINGL_EXE_NAME=../$(EXE_NAME)-mingl$(BITS)\n\tMINGL_DIR=mingl_linux$(BITS)\n\n\tifeq ($(NOCOMPAT),1)\n\t\tSV_EXE_NAME=../engine-sv$(BITS)$(EXEPOSTFIX)\n\t\tGL_EXE_NAME=../engine-gl$(BITS)$(EXEPOSTFIX)\n\t\tVK_EXE_NAME=../engine-vk$(BITS)$(EXEPOSTFIX)\n\t\tM_EXE_NAME=../engine$(BITS)$(EXEPOSTFIX)\n\t\tD3D_EXE_NAME=../engine-d3d$(BITS)$(EXEPOSTFIX)\n\t\tMINGL_EXE_NAME=../engine-mingl$(BITS)$(EXEPOSTFIX)\n\tendif\nendif\nifneq (,$(findstring rpi,$(FTE_TARGET)))\n\t#These next two lines enable cross compiling. If you're compiling natively you can just kill the two.\n\tRPI_SYSROOT:=$(realpath $(shell echo ~)/rpi/rpi-sysroot/)\n\tCC=~/rpi/tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian/bin/arm-linux-gnueabihf-gcc --sysroot=$(RPI_SYSROOT)\n\tCXX=~/rpi/tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian/bin/arm-linux-gnueabihf-g++ --sysroot=$(RPI_SYSROOT)\n\tSDLCONFIG=$(RPI_SYSROOT)/usr/bin/sdl-config --prefix=$(RPI_SYSROOT)/usr\n\tGL_CFLAGS+= -I$(RPI_SYSROOT)/opt/vc/include -I$(RPI_SYSROOT)/opt/vc/include/interface/vmcs_host/linux -I$(RPI_SYSROOT)/opt/vc/include/interface/vcos/pthreads -DFTE_RPI -DUSE_EGL\n\tGL_LDFLAGS+= -L$(RPI_SYSROOT)/opt/vc/lib -Wl,--sysroot=$(RPI_SYSROOT),-rpath=/opt/vc/lib,-rpath-link=$(RPI_SYSROOT)/opt/vc/lib -lbcm_host\n\tGLCL_OBJS+=gl_vidrpi.o\nendif\nifneq (,$(findstring fbdev,$(FTE_TARGET)))\n\tGL_CFLAGS+=-DUSE_EGL\n\tGLCL_OBJS+=gl_vidfbdev.o\n\tMCL_OBJS+=gl_vidfbdev.o\nendif\nifneq ($(shell echo $(FTE_TARGET)|grep macosx),)\n\tSV_DIR=sv_macosx$(EXTENSION)$(BITS)\n\tGLB_DIR=gl_macosx$(EXTENSION)$(BITS)\n\tGLCL_DIR=glcl_macosx$(EXTENSION)$(BITS)\n\tMINGL_DIR=mingl_macosx$(EXTENSION)$(BITS)\n\t\n\tGL_CFLAGS=$(GLCFLAGS) -D__MACOSX__ -L/sw/lib -I/sw/include -L/opt/local/lib -I/opt/local/include -I$(LIBS_DIR)\t\n\tifeq ($(FTE_TARGET),macosx_x86)\n\t\tGL_CFLAGS=$(GLCFLAGS) -D__MACOSX__ -L/sw/lib -I/sw/include -L/opt/local/lib -I/opt/local/include -I$(LIBS_DIR)\n\tendif\n\n\tGL_LDFLAGS=-framework AGL -framework OpenGL -framework Cocoa -framework AudioUnit\n\tGLCL_OBJS=$(GL_OBJS) $(D3DGL_OBJS) $(GLQUAKE_OBJS) gl_vidcocoa.mo gl_vidmacos.o sys_linux.o cd_null.o snd_macos.o sys_linux_threads.o\n\n\tGL_EXE_NAME=../$(EXE_NAME)-macosx-gl$(EXTENSION)$(BITS)\n\tGLCL_EXE_NAME=../$(EXE_NAME)cl-macosx-gl$(EXTENSION)$(BITS)\n\tM_EXE_NAME=../$(EXE_NAME)-macosx$(EXTENSION)$(BITS)\n\tMCL_EXE_NAME=../$(EXE_NAME)-macosx-cl$(EXTENSION)$(BITS)\n\tMINGL_EXE_NAME=../$(EXE_NAME)-macosx-mingl$(EXTENSION)$(BITS)\n\tMINGL_DIR=mingl_macosx$(EXTENSION)$(BITS)\n\t\n\tSV_OBJS=$(COMMON_OBJS) $(SERVER_OBJS) $(PROGS_OBJS) $(SERVERONLY_OBJS)\n\tSV_EXE_NAME=../$(EXE_NAME)-macosx-sv$(EXTENSION)$(BITS)\n\tSV_CFLAGS=$(SERVER_ONLY_CFLAGS)\n\tSV_LDFLAGS=-lz\t\n\n\t#seems, macosx has a more limited version of strip\n\tSTRIPFLAGS=\nendif\nifeq ($(FTE_TARGET),morphos)\n\t#-Wno-pointer-sign unrecognised \n\tWARNINGFLAGS=-Wall\n\n\tCFLAGS+=-D__MORPHOS_SHAREDLIBS\n\n\tSV_DIR=sv_morphos\n\tSV_LDFLAGS=-ldl -lz\n\n\tGLCL_OBJS=$(GL_OBJS) $(D3DGL_OBJS) $(GLQUAKE_OBJS) gl_vidmorphos.o in_morphos.o snd_morphos.o cd_null.o sys_morphos.o\n\tGL_EXE_NAME=../$(EXE_NAME)-morphos-gl\n\tGLCL_EXE_NAME=../$(EXE_NAME)-morphos-glcl\n\tGL_LDFLAGS=$(GLLDFLAGS) -ldl $(IMAGELDFLAGS) -lz\n\tGL_CFLAGS=$(GLCFLAGS) -noixemul -I./\n\tGLB_DIR=gl_morphos\n\tGLCL_DIR=glcl_morphos\n\n\tMCL_OBJS=$(D3DGL_OBJS) $(GLQUAKE_OBJS) $(SOFTWARE_OBJS) gl_vidmorphos.o vid_morphos.o in_morphos.o snd_morphos.o cd_null.o sys_morphos.o\n\tM_EXE_NAME=../$(EXE_NAME)-morphos\n\tMCL_EXE_NAME=../$(EXE_NAME)-morphos-cl\n\tM_LDFLAGS=$(GLLDFLAGS)\n\tM_CFLAGS=$(GLCFLAGS)\n\tMB_DIR=m_morphos\n\tMCL_DIR=mcl_morphos\n\n\tMINGL_EXE_NAME=../$(EXE_NAME)-morphos-mingl\n\tMINGL_DIR=mingl_morphos\n\t\n\tSV_OBJS=$(COMMON_OBJS) $(SERVER_OBJS) $(PROGS_OBJS) $(SERVERONLY_OBJS)\n\tSV_EXE_NAME=../$(EXE_NAME)-morphos-sv$(BITS)\n\tSV_CFLAGS=$(SERVER_ONLY_CFLAGS)\t\nendif\n\nifeq ($(FTE_TARGET),dos)\n\tEXEPOSTFIX=.exe\n\tSV_DIR=sv_dos\n\tGLB_DIR=gl_dos\n\tMB_DIR=m_dos\n\tMCL_DIR=mcl_dos\n\tMINGL_DIR=mingl_dos\n\tVKB_DIR=vk_dos\n\tVKCL_DIR=vkcl_dos\n\n\tIMAGELDFLAGS=\n\n\tSOFTWARE_OBJS=sw_rast.o sw_backend.o sw_image.o\n\n\tM_LDFLAGS=\n\tM_CFLAGS=-DSWQUAKE -DNO_ZLIB\n\tMCL_OBJS=$(SOFTWARE_OBJS) $(D3DGL_OBJS) sw_viddos.o cd_null.o sys_dos.o snd_sblaster.o\n\tM_EXE_NAME=../$(EXE_NAME)$(EXEPOSTFIX)\n\tSV_EXE_NAME=../$(EXE_NAME)sv$(BITS)$(EXEPOSTFIX)\n\tVK_EXE_NAME=../$(EXE_NAME)-vk$(BITS)$(EXEPOSTFIX)\n\n\tVKCL_OBJS=$(GL_OBJS) $(D3DGL_OBJS) $(GLQUAKE_OBJS) cd_null.o sys_dos.o snd_sblaster.o\nendif\n\nifeq ($(FTE_TARGET),cyg)\n\tSV_DIR=sv_cygwin\n\tSV_LDFLAGS=-lz\n\tSV_CFLAGS=$(SERVER_ONLY_CFLAGS)\n\n\tEXEPOSTFIX=.exe\n\tGLCL_OBJS=$(GL_OBJS) $(D3DGL_OBJS) $(GLQUAKE_OBJS) gl_vidlinuxglx.o snd_linux.o cd_null.o sys_linux.o sys_linux_threads.o\n\tGL_EXE_NAME=../$(EXE_NAME)-cyg-gl$(EXEPOSTFIX)\n\tGLCL_EXE_NAME=../$(EXE_NAME)-cyg-glcl$(EXEPOSTFIX)\n\tGL_LDFLAGS=$(GLLDFLAGS) $(XLDFLAGS) -lz -lltdl\n\tGL_CFLAGS=$(GLCFLAGS) -I/usr/X11R6/include $(CLIENTLIBFLAGS) -DUSE_LIBTOOL -DAUDIO_OSS\n\tGLB_DIR=gl_cygwin\n\tGLCL_DIR=glcl_cygwin\n\n\tMCL_OBJS=$(D3DGL_OBJS) $(GLQUAKE_OBJS) $(SOFTWARE_OBJS) gl_vidlinuxglx.o snd_linux.o cd_null.o sys_linux.o sys_linux_threads.o\n\tM_EXE_NAME=../$(EXE_NAME)-cyg$(EXEPOSTFIX)\n\tMCL_EXE_NAME=../$(EXE_NAME)-cyg-cl$(EXEPOSTFIX)\n\tM_LDFLAGS=$(GLLDFLAGS) $(XLDFLAGS) -lz -lltdl\n\tM_CFLAGS=$(GLCFLAGS) $(CLIENTLIBFLAGS) -DUSE_LIBTOOL -DAUDIO_OSS\n\tMB_DIR=m_cygwin\n\tMCL_DIR=mcl_cygwin\n\n\tMINGL_EXE_NAME=../$(EXE_NAME)-cyg-mingl$(EXEPOSTFIX)\n\tMINGL_DIR=mingl_cygwin\nendif\n\nifeq ($(FTE_TARGET),droid)\n\tBASELDFLAGS=-lz\n\t\n\t#erk! FIXME!\n\tCLIENTLDDEPS=\n\tSERVERLDDEPS=\n\n\tSYS_DROID_O=sys_droid.o sys_linux_threads.o\n\tGL_DROID_O=gl_viddroid.o $(SYS_DROID_O)\n\n\tSV_CFLAGS=$(SERVER_ONLY_CFLAGS) $(W32_CFLAGS)\n\tSV_LDFLAGS=\n\tSV_DIR=sv_droid-$(DROID_ARCH)\n\tSV_OBJS=$(COMMON_OBJS) $(SERVER_OBJS) $(PROGS_OBJS) $(SYS_DROID_O)\n\tSV_EXE_NAME=libftedroid.so\n\n\tGL_CFLAGS=$(GLCFLAGS)\n\tGL_LDFLAGS=$(GLLDFLAGS) -landroid\n\tGLCL_OBJS=$(GL_OBJS) $(D3DGL_OBJS) $(GLQUAKE_OBJS) $(GL_DROID_O) cd_null.o snd_droid.o\n\tGLB_DIR=gl_droid-$(DROID_ARCH)\n\tGL_EXE_NAME=libftedroid.so\t\n\n\tM_CFLAGS=$(VKCFLAGS) $(GLCFLAGS) -DMULTITHREAD\n\tM_LDFLAGS=$(GLLDFLAGS) -landroid -lEGL -lOpenSLES\n\tMCL_OBJS=$(GL_OBJS) $(D3DGL_OBJS) $(GLQUAKE_OBJS) $(GL_DROID_O) cd_null.o snd_opensl.o\n#snd_droid.o\n\tMB_DIR=m_droid-$(DROID_ARCH)\n\tM_EXE_NAME=libftedroid.so\nendif\n\nifeq ($(FTE_TARGET),web)\n\tCOMMON_OBJS+=sys_web.o fs_web.o\n\tWEB_PREJS ?= --pre-js web/prejs.js\n#\tWEB_MEMORY?=402653184\t#384mb\n#\tASMJS_MEMORY?=16777216\t#16mb\n#\tASMJS_MEMORY?=33554432\t#32mb\n\tASMJS_MEMORY?=268435456\t#256mb\n#\tASMJS_MEMORY?=536870912\t#512mb\n#\tASMJS_MEMORY?=1073741824 #1025mb\n#\tASMJS_MEMORY?=2147483648 #2048mb\n\tWEB_MEMORY?=$(ASMJS_MEMORY)\n\tJSLIBS=--js-library web/ftejslib.js\n\tEMCC_CFLAGS= -DFTE_TARGET_WEB\n\tEMCC_LDFLAGS=$(EMCC_CFLAGS) $(JSLIBS) $(WEB_PREJS)\n#\tEMCC_CFLAGS+= -s BINARYEN_TRAP_MODE='clamp'\t\t#fix bigfloat->int rounding crashes\n\tEMCC_LDFLAGS+= -s LEGACY_GL_EMULATION=0\t\t\t#simplify the opengl wrappers.\n\tEMCC_LDFLAGS+= -s NO_FILESYSTEM=1\t\t\t\t#we have our own.\n\tEMCC_LDFLAGS+= -s FILESYSTEM=0\t\t\t\t\t#we have our own.\n\tEMCC_LDFLAGS+= -s ALLOW_MEMORY_GROWTH=1\t\t\t#reduce crashes...\n\tEMCC_LDFLAGS+= -s MAX_WEBGL_VERSION=2\t\t\t#make use of what we can.\n\tEMCC_LDFLAGS+= -s TOTAL_STACK=5MB\t\t\t#big!\n   \tEMCC_LDFLAGS+=-s ERROR_ON_UNDEFINED_SYMBOLS=1\t#fairly obvious. no runtime errors please.\n\tRELEASE_CFLAGS=-DOMIT_QCC -DGL_STATIC $(EMCC_CFLAGS)\n\tDEBUG_CFLAGS=-gsource-map -DOMIT_QCC -DGL_STATIC $(EMCC_CFLAGS)\n\tRELEASE_LDFLAGS=-O3\t-s TOTAL_MEMORY=$(ASMJS_MEMORY) $(EMCC_LDFLAGS)\n#\tRELEASE_LDFLAGS=-O1\t-s TOTAL_MEMORY=$(WEB_MEMORY) $(EMCC_LDFLAGS)\n\tDEBUG_LDFLAGS=-O0 -gsource-map -s TOTAL_MEMORY=$(WEB_MEMORY) $(EMCC_LDFLAGS) -s SAFE_HEAP=1 -s ALIASING_FUNCTION_POINTERS=0 -s ASSERTIONS=2\n\tCC=emcc\n\tCXX=em++\n\tAR=emar\n\tBASELDFLAGS=-lz\n\tPRECOMPHEADERS=\n\n\t#mostly we inherit the sdl defaults. because we can, however emscripten does not support sdl cd code.\n\tGLCL_OBJS=$(GL_OBJS) $(D3DGL_OBJS) $(BOTLIB_OBJS) $(GLQUAKE_OBJS) gl_vidweb.o cd_null.o\n\tSDL_INCLUDES=\n\n\tSV_DIR=sv_web\n\tSV_LDFLAGS=\n\t#SV_OBJS=$(COMMON_OBJS) $(SERVER_OBJS) $(PROGS_OBJS)\n\tSV_EXE_NAME=../libftesv.js\n\tSV_CFLAGS=$(SERVER_ONLY_CFLAGS)\n\n\t#SV_LDFLAGS=\n\n\tSTRIP=@echo SKIP: strip\n\t#GLCL_OBJS=$(GL_OBJS) $(D3DGL_OBJS) $(GLQUAKE_OBJS) cd_null.o\n\t#GL_LDFLAGS=$(GLLDFLAGS)\n\tGLB_DIR=gl_web\n\tGLCL_DIR=glcl_web\n\tGL_EXE_NAME=../ftewebgl.js\n\tGLCL_EXE_NAME=../ftewebglcl.js\n\n\tGL_LDFLAGS=$(GLLDFLAGS) $(IMAGELDFLAGS)\n\tGL_CFLAGS=$(GLCFLAGS) $(CLIENTLIBFLAGS)\n\n\tIMAGELDFLAGS=\n\tSERVERLDDEPS=\n\n\t#generate deps properly\n\t#DEPCC=\n\t#DEPCXX=\nendif\n\nSV_DIR?=sv_sdl\nDEPCC?=$(CC)\nDEPCXX?=$(CXX)\nARCH:=$(ARCH)\nifeq ($(findstring msvc,$(FTE_TARGET)),msvc)\n\tBASELDFLAGS:=/libpath:\"$(ARCHLIBS)\" $(BASELDFLAGS)\nelse\n\tBASELDFLAGS:=-L$(ARCHLIBS) $(BASELDFLAGS)\nendif\n\n-include Makefile_private\n\nall: rel\nrel: sv-rel m-rel qcc-rel\ndbg: sv-dbg m-dbg qcc-dbg\nrelcl: glcl-rel mcl-rel\nprofile: sv-profile gl-profile mingl-profile\n\nreleases:\n\t#this is for releasing things from a linux box\n\t#just go through compiling absolutly everything\n\t-$(MAKE) FTE_TARGET=linux32 rel\n\t-$(MAKE) FTE_TARGET=linux64 rel\n\t-$(MAKE) FTE_TARGET=win32 rel\n\t-$(MAKE) FTE_TARGET=win64 rel\n\t-$(MAKE) FTE_TARGET=win32_SDL rel\n\t-$(MAKE) FTE_TARGET=win64_SDL rel\n\t-$(MAKE) FTE_TARGET=morphos rel\n\t-$(MAKE) FTE_TARGET=macosx rel\n#\t-$(MAKE) FTE_TARGET=linux32 relcl\n#\t-$(MAKE) FTE_TARGET=linux64 relcl\n#\t-$(MAKE) FTE_TARGET=win32 relcl\n\t-$(MAKE) droid-rel\n\t-$(MAKE) web-rel\n\nautoconfig: clean\n\t/bin/bash makeconfig.sh y\n\nconfig: clean\n\t/bin/bash makeconfig.sh\n\nifneq ($(OUT_DIR),)\n-include $(OUT_DIR)/*.o.d\nendif\n\n\nDO_WINDRES?=$(DO_ECHO) $(WINDRES) $(BRANDFLAGS) -I$(CLIENT_DIR) -O coff $< $@\n\n# This is for linking the FTE icon to the MinGW target\n$(OUT_DIR)/resources.o : winquake.rc\n\t$(DO_WINDRES)\n$(OUT_DIR)/fteqcc.o : fteqcc.rc\n\t$(DO_WINDRES)\n\n\n#$(OUT_DIR)/%.d: %.c\n#\t@set -e; rm -f $@; \\\n#\t$(CC) -MM $(ALL_CFLAGS) $< > $@.$$$$; \\\n#\tsed 's,\\($*\\)\\.o[ :]*,\\1.o $@ : ,g' < $@.$$$$ > $@; \\\n#\trm -f $@.$$$$\n\n$(OUT_DIR)/%.o $(OUT_DIR)/%.d : %.c\nifneq ($(DEPCC),)\n\t@-set -e; rm -f $@.d; \\\n\t $(DEPCC) -MM $(ALL_CFLAGS) $< > $@.d.$$$$; \\\n\t sed 's,\\($*\\)\\.o[ :]*,$@ $@.d : ,g' < $@.d.$$$$ > $@.d; \\\n\t sed -e 's/.*://' -e 's/\\\\$$//' < $@.d.$$$$ | fmt -1 | sed -e 's/^ *//' -e 's/$$/:/' >> $@.d; \\\n\t rm -f $@.d.$$$$\nendif\n\t$(DO_CC) -I$(OUT_DIR)\n\n$(OUT_DIR)/%.o $(OUT_DIR)/%.d : %.cpp\nifneq ($(DEPCXX),)\n\t@-set -e; rm -f $@.d; \\\n\t$(DEPCXX) -MM $(ALL_CXXFLAGS) $< > $@.d.$$$$; \\\n\tsed 's,\\($*\\)\\.o[ :]*,$@ $@.d : ,g' < $@.d.$$$$ > $@.d; \\\n\tsed -e 's/.*://' -e 's/\\\\$$//' < $@.d.$$$$ | fmt -1 | sed -e 's/^ *//' -e 's/$$/:/' >> $@.d; \\\n\trm -f $@.d.$$$$\nendif\n\t$(DO_CXX) -I$(OUT_DIR)\n\n$(OUT_DIR)/%.o $(OUT_DIR)/%.d : %.cxx\nifneq ($(DEPCXX),)\n\t@-set -e; rm -f $@.d; \\\n\t$(DEPCXX) -MM $(ALL_CXXFLAGS) $< > $@.d.$$$$; \\\n\tsed 's,\\($*\\)\\.o[ :]*,$@ $@.d : ,g' < $@.d.$$$$ > $@.d; \\\n\tsed -e 's/.*://' -e 's/\\\\$$//' < $@.d.$$$$ | fmt -1 | sed -e 's/^ *//' -e 's/$$/:/' >> $@.d; \\\n\trm -f $@.d.$$$$\nendif\n\t$(DO_CXX) -I$(OUT_DIR)\n\n$(OUT_DIR)/%.oo $(OUT_DIR)/%.d : %.c\nifneq ($(DEPCC),)\n\t@-set -e; rm -f $@.d; \\\n\t $(DEPCC) -MM $(ALL_CFLAGS) $< > $@.d.$$$$; \\\n\t sed 's,\\($*\\)\\.oo[ :]*,$@ $@.d : ,g' < $@.d.$$$$ > $@.d; \\\n\t sed -e 's/.*://' -e 's/\\\\$$//' < $@.d.$$$$ | fmt -1 | sed -e 's/^ *//' -e 's/$$/:/' >> $@.d; \\\n\t rm -f $@.d.$$$$\nendif\n\t$(DO_CC) -I$(OUT_DIR)\n\n$(OUT_DIR)/%.mo $(OUT_DIR)/%.d : %.m\n\t@-set -e; rm -f $@.d; \\\n\t $(DEPCC) -MM $(ALL_CFLAGS) $< > $@.d.$$$$; \\\n\t sed 's,\\($*\\)\\.mo[ :]*,$@ $@.d : ,g' < $@.d.$$$$ > $@.d; \\\n\t sed -e 's/.*://' -e 's/\\\\$$//' < $@.d.$$$$ | fmt -1 | sed -e 's/^ *//' -e 's/$$/:/' >> $@.d; \\\n\t rm -f $@.d.$$$$\n\t$(DO_CC) -I$(OUT_DIR)\n\n#enables use of precompiled headers in gcc 3.4 onwards.\n$(OUT_DIR)/quakedef.h.gch : quakedef.h\n\t$(CC) -x c-header $(ALL_CFLAGS) -o $@ -c $<\nPRECOMPHEADERS ?= $(OUT_DIR)/quakedef.h.gch\n\nifneq ($(OUT_DIR),)\nALL_CFLAGS+=-I$(OUT_DIR)\nendif\n\n#addprefix is to add the ./release/server/ part of the object name\n#foreach is needed as the OBJS is a list of variable names containing object lists.\n#which is needed as windows sucks too much for the chaining to carry a full list.\n#god knows how gcc loads the list properly.\n#or at least I hope he does. It makes no sence to mortals.\n\nLDCC ?=$(CC)\nDO_LD ?= +$(DO_ECHO) $(LDCC) -o $@ $(LTO_LD) $(WCFLAGS) $(BRANDFLAGS) $(CFLAGS)\n$(OUT_DIR)/$(EXE_NAME):   $(PRECOMPHEADERS) $(foreach fn, $(CUSTOMOBJS) $(foreach ol, $(OBJS), $($(ol))),$(if $(findstring ltox,$(fn)),,$(OUT_DIR)/$(fn)))\n\t$(DO_LD) $(foreach fn, $(CUSTOMOBJS) $(foreach ol, $(OBJS) $(LTO_END), $($(ol))),$(if $(findstring ltox,$(fn)),$(subst ltox,-x ,$(fn)),$(NATIVE_OUT_DIR)/$(fn)) ) $(LDFLAGS)\n\n$(OUT_DIR)/$(EXE_NAME).db: $(PRECOMPHEADERS) $(foreach fn, $(CUSTOMOBJS) $(foreach ol, $(OBJS), $($(ol))),$(if $(findstring ltox,$(fn)),,$(OUT_DIR)/$(fn)))\n\t$(DO_LD) $(foreach fn, $(CUSTOMOBJS) $(foreach ol, $(OBJS) $(LTO_END), $($(ol))),$(if $(findstring ltox,$(fn)),$(subst ltox,-x ,$(fn)),$(NATIVE_OUT_DIR)/$(fn)) ) $(LDFLAGS)\n\nifneq (,$(NEEDMAKELIBS))\n_out-dbg:\n\t@echo Dependancies missing. Use makelibs before compiling targets\n\t@false\n_out-rel:\n\t@echo Dependancies missing. Use makelibs before compiling targets\n\t@false\n_out-profile:\n\t@echo Dependancies missing. Use makelibs before compiling targets\n\t@false\nelse\nifeq (,$(findstring SKIP,$(STRIP)))\n#link to a .db file\n#then strip its debug data to the non-.db release binary\n_out-rel: $(ARCH_PREDEP)\n\t@$(MAKE) $(OUT_DIR)/$(EXE_NAME).db EXE_NAME=\"$(EXE_NAME)\" OUT_DIR=\"$(OUT_DIR)\" WCFLAGS=\"$(WCFLAGS) $(RELEASE_CFLAGS)\" LDFLAGS=\"$(BASELDFLAGS) $(LDFLAGS) $(RELEASE_LDFLAGS)\" OBJS=\"$(OBJS)\"\n\t@$(STRIP) $(STRIPFLAGS) $(OUT_DIR)/$(EXE_NAME).db -o $(OUT_DIR)/$(EXE_NAME)\nelse\n#STRIP macro won't work, don't do the .db thing and don't expect strip -o to work.\n_out-rel: $(ARCH_PREDEP)\n\t@$(MAKE) $(OUT_DIR)/$(EXE_NAME) EXE_NAME=\"$(EXE_NAME)\" OUT_DIR=\"$(OUT_DIR)\" WCFLAGS=\"$(WCFLAGS) $(RELEASE_CFLAGS)\" LDFLAGS=\"$(BASELDFLAGS) $(LDFLAGS) $(RELEASE_LDFLAGS)\" OBJS=\"$(OBJS)\"\n\t@echo not stripping $(OUT_DIR)/$(EXE_NAME)\nendif\n\n_out-dbg: $(ARCH_PREDEP)\n\t@$(MAKE) $(OUT_DIR)/$(EXE_NAME) EXE_NAME=\"$(EXE_NAME)\" OUT_DIR=\"$(OUT_DIR)\" WCFLAGS=\"$(WCFLAGS) $(DEBUG_CFLAGS)\" LDFLAGS=\"$(BASELDFLAGS) $(LDFLAGS) $(DEBUG_LDFLAGS)\" OBJS=\"$(OBJS)\"\n\n_out-profile: $(ARCH_PREDEP)\n\t@$(MAKE) $(OUT_DIR)/$(EXE_NAME) EXE_NAME=\"$(EXE_NAME)\" OUT_DIR=\"$(OUT_DIR)\" WCFLAGS=\"$(WCFLAGS) $(PROFILE_CFLAGS)\" LDFLAGS=\"$(BASELDFLAGS) $(LDFLAGS) $(PROFILE_LDFLAGS)\" OBJS=\"$(OBJS)\"\nendif\n\n_cl-rel: reldir\n\t@$(MAKE) _out-rel EXE_NAME=\"$(EXE_NAME)\" OUT_DIR=\"$(OUT_DIR)\" WCFLAGS=\"$(CLIENT_ONLY_CFLAGS) $(WCFLAGS)\" LDFLAGS=\"$(LDFLAGS)\" SOBJS=\"$(SOBJS)\" OBJS=\"SOBJS COMMON_OBJS CLIENT_OBJS PROGS_OBJS\"\n\n_cl-dbg: debugdir\n\t@$(MAKE) _out-dbg EXE_NAME=\"$(EXE_NAME)\" OUT_DIR=\"$(OUT_DIR)\" WCFLAGS=\"$(CLIENT_ONLY_CFLAGS) $(WCFLAGS)\" LDFLAGS=\"$(LDFLAGS)\" SOBJS=\"$(SOBJS)\" OBJS=\"SOBJS COMMON_OBJS CLIENT_OBJS PROGS_OBJS\"\n\n_cl-profile: reldir\n\t@$(MAKE) _out-profile EXE_NAME=\"$(EXE_NAME)\" OUT_DIR=\"$(OUT_DIR)\" WCFLAGS=\"$(CLIENT_ONLY_CFLAGS) $(WCFLAGS)\" LDFLAGS=\"$(LDFLAGS)\" SOBJS=\"$(SOBJS)\" OBJS=\"SOBJS COMMON_OBJS CLIENT_OBJS PROGS_OBJS\"\n\n_clsv-rel: reldir\n\t$(DO_ECHO) $(MAKE) _out-rel EXE_NAME=\"$(EXE_NAME)\" OUT_DIR=\"$(OUT_DIR)\" WCFLAGS=\"$(JOINT_CFLAGS) $(WCFLAGS)\" LDFLAGS=\"$(LDFLAGS)\" SOBJS=\"$(SOBJS)\" OBJS=\"SOBJS COMMON_OBJS CLIENT_OBJS PROGS_OBJS SERVER_OBJS\"\n\n_clsv-dbg: debugdir\n\t@$(MAKE) _out-dbg EXE_NAME=\"$(EXE_NAME)\" OUT_DIR=\"$(OUT_DIR)\" WCFLAGS=\"$(JOINT_CFLAGS) $(WCFLAGS)\" LDFLAGS=\"$(LDFLAGS)\" SOBJS=\"$(SOBJS)\" OBJS=\"SOBJS COMMON_OBJS CLIENT_OBJS PROGS_OBJS SERVER_OBJS\"\n\n_clsv-profile: reldir\n\t@$(MAKE) _out-profile EXE_NAME=\"$(EXE_NAME)\" OUT_DIR=\"$(OUT_DIR)\" WCFLAGS=\"$(JOINT_CFLAGS) $(WCFLAGS)\" LDFLAGS=\"$(LDFLAGS)\" SOBJS=\"$(SOBJS)\" OBJS=\"SOBJS COMMON_OBJS CLIENT_OBJS PROGS_OBJS SERVER_OBJS\"\n\nsv-tmp: reldir debugdir\n\t@$(MAKE) $(TYPE) OUT_DIR=\"$(OUT_DIR)\" EXE_NAME=\"$(SV_EXE_NAME)\" WCFLAGS=\"$(SV_CFLAGS)\" LDFLAGS=\"$(ARCH_LDFLAGS) $(SV_LDFLAGS) $(LDFLAGS) $(SERVERLDDEPS)\" OBJS=\"SV_OBJS\"\nsv-rel:\n\t@$(MAKE) sv-tmp TYPE=_out-rel OUT_DIR=\"$(RELEASE_DIR)/$(NCDIRPREFIX)$(SV_DIR)\"\nsv-dbg:\n\t@$(MAKE) sv-tmp TYPE=_out-dbg OUT_DIR=\"$(DEBUG_DIR)/$(NCDIRPREFIX)$(SV_DIR)\"\nsv-profile:\n\t@$(MAKE) sv-tmp TYPE=_out-profile OUT_DIR=\"$(PROFILE_DIR)/$(NCDIRPREFIX)$(SV_DIR)\"\n\nifeq (,$(D3D_EXE_NAME))\nd3dcl-tmp:\n\t@echo \"D3D builds are not supported for this platform.\"\nelse\nd3dcl-tmp:\n\t@$(MAKE) $(TYPE)   OUT_DIR=\"$(OUT_DIR)\" EXE_NAME=\"$(D3DCL_EXE_NAME)\" WCFLAGS=\"$(D3D_CFLAGS)\" LDFLAGS=\"$(D3D_LDFLAGS) $(LDFLAGS) $(CLIENTLDDEPS)\" SOBJS=\"$(D3DCL_OBJS)\"\nendif\nifeq (,$(D3D_EXE_NAME))\nd3d-tmp:\n\t@echo \"D3D builds are not supported for this platform.\"\nelse\nd3d-tmp:\n\t@$(MAKE) $(TYPE)   OUT_DIR=\"$(OUT_DIR)\" EXE_NAME=\"$(D3D_EXE_NAME)\" WCFLAGS=\"$(D3D_CFLAGS)\" LDFLAGS=\"$(D3D_LDFLAGS) $(LDFLAGS) $(CLIENTLDDEPS)\" SOBJS=\"$(D3DCL_OBJS)\"\nendif\n\nd3dcl-rel:\n\t@$(MAKE) d3dcl-tmp TYPE=_cl-rel OUT_DIR=\"$(RELEASE_DIR)/$(NCDIRPREFIX)$(D3DCL_DIR)\"\nd3dcl-dbg:\n\t@$(MAKE) d3dcl-tmp TYPE=_cl-dbg OUT_DIR=\"$(DEBUG_DIR)/$(NCDIRPREFIX)$(D3DCL_DIR)\"\nd3dcl-profile:\n\t@$(MAKE) d3dcl-tmp TYPE=_cl-profile OUT_DIR=\"$(PROFILE_DIR)/$(NCDIRPREFIX)$(D3DCL_DIR)\"\n\nd3d-rel:\n\t@$(MAKE) d3d-tmp TYPE=_clsv-rel OUT_DIR=\"$(RELEASE_DIR)/$(NCDIRPREFIX)$(D3DB_DIR)\"\nd3d-dbg:\n\t@$(MAKE) d3d-tmp TYPE=_clsv-dbg OUT_DIR=\"$(DEBUG_DIR)/$(NCDIRPREFIX)$(D3DB_DIR)\"\nd3d-profile:\n\t@$(MAKE) d3d-tmp TYPE=_clsv-profile OUT_DIR=\"$(PROFILE_DIR)/$(NCDIRPREFIX)$(D3DB_DIR)\"\n\n\n\nvkcl-tmp:\n\t@$(MAKE) $(TYPE)   OUT_DIR=\"$(OUT_DIR)\" EXE_NAME=\"$(VKCL_EXE_NAME)\" WCFLAGS=\"$(VK_CFLAGS)\" LDFLAGS=\"$(VK_LDFLAGS) $(LDFLAGS) $(CLIENTLDDEPS)\" SOBJS=\"$(VKCL_OBJS)\"\nvk-tmp:\n\t@$(MAKE) $(TYPE)   OUT_DIR=\"$(OUT_DIR)\" EXE_NAME=\"$(VK_EXE_NAME)\" WCFLAGS=\"$(VK_CFLAGS)\" LDFLAGS=\"$(VK_LDFLAGS) $(LDFLAGS) $(CLIENTLDDEPS)\" SOBJS=\"$(VKCL_OBJS)\"\n\nvkcl-rel:\n\t@$(MAKE) vkcl-tmp TYPE=_cl-rel OUT_DIR=\"$(RELEASE_DIR)/$(NCDIRPREFIX)$(VKCL_DIR)\"\nvkcl-dbg:\n\t@$(MAKE) vkcl-tmp TYPE=_cl-dbg OUT_DIR=\"$(DEBUG_DIR)/$(NCDIRPREFIX)$(VKCL_DIR)\"\nvkcl-profile:\n\t@$(MAKE) vkcl-tmp TYPE=_cl-profile OUT_DIR=\"$(PROFILE_DIR)/$(NCDIRPREFIX)$(VKCL_DIR)\"\n\nvk-rel:\n\t@$(MAKE) vk-tmp TYPE=_clsv-rel OUT_DIR=\"$(RELEASE_DIR)/$(NCDIRPREFIX)$(VKB_DIR)\"\nvk-dbg:\n\t@$(MAKE) vk-tmp TYPE=_clsv-dbg OUT_DIR=\"$(DEBUG_DIR)/$(NCDIRPREFIX)$(VKB_DIR)\"\nvk-profile:\n\t@$(MAKE) vk-tmp TYPE=_clsv-profile OUT_DIR=\"$(PROFILE_DIR)/$(NCDIRPREFIX)$(VKB_DIR)\"\n\n\nglcl-tmp:\n\t@$(MAKE) $(TYPE)   OUT_DIR=\"$(OUT_DIR)\" EXE_NAME=\"$(GLCL_EXE_NAME)\" WCFLAGS=\"$(GL_CFLAGS)\" LDFLAGS=\"$(GL_LDFLAGS) $(LDFLAGS) $(CLIENTLDDEPS)\" SOBJS=\"$(GLCL_OBJS)\"\ngl-tmp:\n\t@$(MAKE) $(TYPE)   OUT_DIR=\"$(OUT_DIR)\" EXE_NAME=\"$(GL_EXE_NAME)\" WCFLAGS=\"$(GL_CFLAGS)\" LDFLAGS=\"$(GL_LDFLAGS) $(LDFLAGS) $(CLIENTLDDEPS)\" SOBJS=\"$(GLCL_OBJS)\"\n\nglcl-rel:\n\t@$(MAKE) glcl-tmp TYPE=_cl-rel OUT_DIR=\"$(RELEASE_DIR)/$(NCDIRPREFIX)$(GLCL_DIR)\"\nglcl-dbg:\n\t@$(MAKE) glcl-tmp TYPE=_cl-dbg OUT_DIR=\"$(DEBUG_DIR)/$(NCDIRPREFIX)$(GLCL_DIR)\"\nglcl-profile:\n\t@$(MAKE) glcl-tmp TYPE=_cl-profile OUT_DIR=\"$(PROFILE_DIR)/$(NCDIRPREFIX)$(GLCL_DIR)\"\ngl-rel:\n\t@$(MAKE) gl-tmp TYPE=_clsv-rel OUT_DIR=\"$(RELEASE_DIR)/$(NCDIRPREFIX)$(GLB_DIR)\"\ngl-dbg:\n\t@$(MAKE) gl-tmp TYPE=_clsv-dbg OUT_DIR=\"$(DEBUG_DIR)/$(NCDIRPREFIX)$(GLB_DIR)\"\ngl-profile:\n\t@$(MAKE) gl-tmp TYPE=_clsv-profile OUT_DIR=\"$(PROFILE_DIR)/$(NCDIRPREFIX)$(GLB_DIR)\"\n\nmingl-tmp: reldir\n\t@$(MAKE) $(TYPE) OUT_DIR=\"$(OUT_DIR)\" EXE_NAME=\"$(MINGL_EXE_NAME)\" WCFLAGS=\"$(GL_CFLAGS) -DMINIMAL\" LDFLAGS=\"$(GL_LDFLAGS) $(LDFLAGS) $(CLIENTLDDEPS)\" SOBJS=\"$(GLCL_OBJS)\"\nmingl-rel:\n\t@$(MAKE) mingl-tmp TYPE=_cl-rel OUT_DIR=\"$(RELEASE_DIR)/$(NCDIRPREFIX)$(MINGL_DIR)\"\nmingl-dbg:\n\t@$(MAKE) mingl-tmp TYPE=_cl-dbg OUT_DIR=\"$(DEBUG_DIR)/$(NCDIRPREFIX)$(MINGL_DIR)\"\nmingl-profile:\n\t@$(MAKE) mingl-tmp TYPE=_cl-profile OUT_DIR=\"$(PROFILE_DIR)/$(NCDIRPREFIX)$(MINGL_DIR)\"\n\nmcl-tmp:\n\t@$(MAKE) $(TYPE)   OUT_DIR=\"$(OUT_DIR)\" EXE_NAME=\"$(MCL_EXE_NAME)\" WCFLAGS=\"$(M_CFLAGS)\" LDFLAGS=\"$(M_LDFLAGS) $(LDFLAGS) $(CLIENTLDDEPS)\" SOBJS=\"$(MCL_OBJS)\"\nm-tmp:\n\t$(DO_ECHO) $(MAKE) $(TYPE)   OUT_DIR=\"$(OUT_DIR)\" EXE_NAME=\"$(M_EXE_NAME)\" WCFLAGS=\"$(M_CFLAGS)\" LDFLAGS=\"$(M_LDFLAGS) $(LDFLAGS) $(CLIENTLDDEPS)\" SOBJS=\"$(MCL_OBJS)\"\n\nmcl-rel:\n\t@$(MAKE) mcl-tmp TYPE=_cl-rel OUT_DIR=\"$(RELEASE_DIR)/$(NCDIRPREFIX)$(MCL_DIR)\"\nmcl-dbg:\n\t@$(MAKE) mcl-tmp TYPE=_cl-dbg OUT_DIR=\"$(DEBUG_DIR)/$(NCDIRPREFIX)$(MCL_DIR)\"\nmcl-profile:\n\t@$(MAKE) mcl-tmp TYPE=_cl-profile OUT_DIR=\"$(PROFILE_DIR)/$(NCDIRPREFIX)$(MCL_DIR)\"\nm-rel:\n\t$(DO_ECHO) $(MAKE) m-tmp TYPE=_clsv-rel OUT_DIR=\"$(RELEASE_DIR)/$(NCDIRPREFIX)$(MB_DIR)\"\nm-dbg:\n\t@$(MAKE) m-tmp TYPE=_clsv-dbg OUT_DIR=\"$(DEBUG_DIR)/$(NCDIRPREFIX)$(MB_DIR)\"\nm-profile:\n\t@$(MAKE) m-tmp TYPE=_clsv-profile OUT_DIR=\"$(PROFILE_DIR)/$(NCDIRPREFIX)$(MB_DIR)\"\n\n.PHONY: m-tmp mcl-tmp mingl-tmp glcl-tmp gl-tmp sv-tmp _clsv-dbg _clsv-rel _cl-dbg _cl-rel _out-rel _out-dbg reldir debugdir makelibs wel-rel web-dbg httpserver iqmtool imgtool\n\n\n_qcc-tmp: $(REQDIR)\n\t@$(MAKE) $(TYPE) EXE_NAME=\"$(EXE_NAME)$(EXEPOSTFIX)\" PRECOMPHEADERS=\"\" OUT_DIR=\"$(OUT_DIR)\" WCFLAGS=\"$(QCC_CFLAGS) $(WCFLAGS)\" LDFLAGS=\"$(LDFLAGS) $(QCC_LDFLAGS)\" OBJS=\"QCC_OBJS SOBJS\"\nqcc-rel:\n\t@$(MAKE) _qcc-tmp TYPE=_out-rel REQDIR=reldir EXE_NAME=\"../fteqcc$(BITS)\" OUT_DIR=\"$(RELEASE_DIR)/$(NCDIRPREFIX)$(QCC_DIR)\" SOBJS=\"qcctui.o packager.o decomp.o $(if $(findstring win,$(FTE_TARGET)),fteqcc.o)\"\nqccgui-rel:\n\t@$(MAKE) _qcc-tmp TYPE=_out-rel REQDIR=reldir EXE_NAME=\"../fteqccgui$(BITS)\" LTO= OUT_DIR=\"$(RELEASE_DIR)/$(NCDIRPREFIX)$(QCC_DIR)gui\" SOBJS=\"qccgui.o qccguistuff.o packager.o decomp.o fteqcc.o\" LDFLAGS=\"$(LDFLAGS) -lole32 -lcomdlg32 -lcomctl32 -lshlwapi -mwindows\"\nqcc-dbg:\n\t@$(MAKE) _qcc-tmp TYPE=_out-dbg REQDIR=debugdir EXE_NAME=\"../fteqcc$(BITS)\" OUT_DIR=\"$(DEBUG_DIR)/$(NCDIRPREFIX)$(QCC_DIR)\" SOBJS=\"qcctui.o packager.o decomp.o $(if $(findstring win,$(FTE_TARGET)),fteqcc.o)\"\nqccgui-dbg:\n\t@$(MAKE) _qcc-tmp TYPE=_out-dbg REQDIR=debugdir EXE_NAME=\"../fteqccgui$(BITS)\" LTO= OUT_DIR=\"$(DEBUG_DIR)/$(NCDIRPREFIX)$(QCC_DIR)gui\" SOBJS=\"qccgui.o qccguistuff.o packager.o decomp.o fteqcc.o\" LDFLAGS=\"$(LDFLAGS) -lole32 -lcomdlg32 -lcomctl32 -lshlwapi -mwindows\"\n\n\n#scintilla is messy as fuck when building statically. but at least we can strip out the lexers we don't use this way.\n#note that this is only used in the 'qccgui-scintilla' target.\nSCINTILLA_FILES= AutoComplete.o CallTip.o CaseConvert.o CaseFolder.o CellBuffer.o CharacterCategory.o CharacterSet.o CharClassify.o ContractionState.o Decoration.o Document.o EditModel.o Editor.o EditView.o KeyMap.o Indicator.o LineMarker.o MarginView.o PerLine.o PlatWin.o PositionCache.o PropSetSimple.o RESearch.o RunStyles.o Selection.o Style.o UniConversion.o ViewStyle.o XPM.o ScintillaWin.o HanjaDic.o ScintillaBase.o Accessor.o Catalogue.o ExternalLexer.o LexerBase.o LexerModule.o LexerSimple.o StyleContext.o WordList.o LexCPP.o\nSCINTILLA_ROOT=$(BASE_DIR)/scintilla$(SCINTILLAVER)/scintilla\nSCINTILLA_DIRS=$(SCINTILLA_ROOT)/lexers:$(SCINTILLA_ROOT)/lexlib:$(SCINTILLA_ROOT)/src:$(SCINTILLA_ROOT)/win32\nSCINTILLA_INC=-I$(SCINTILLA_ROOT)/include -I$(SCINTILLA_ROOT)/lexlib -I$(SCINTILLA_ROOT)/win32 -I$(SCINTILLA_ROOT)/src\n$(RELEASE_DIR)/scintilla$(BITS).a: $(foreach f,$(SCINTILLA_FILES),$(OUT_DIR)/$(f))\n\t@$(AR) -r $@ $?\n\t@$(AR) -s $@\nscintilla$(BITS)_static:\n\t@test -f scintilla$(SCINTILLAVER).tar.gz || wget http://prdownloads.sourceforge.net/scintilla/scintilla$(SCINTILLAVER).tgz?download -O scintilla$(SCINTILLAVER).tar.gz\n\t@-test -f $(SCINTILLA_ROOT) || (mkdir $(BASE_DIR)/scintilla$(SCINTILLAVER) && cd $(BASE_DIR)/scintilla$(SCINTILLAVER) && tar -xvzf ../scintilla$(SCINTILLAVER).tar.gz && cd scintilla && mv lexers/LexCPP.cxx . && rm lexers/Lex*.cxx && mv LexCPP.cxx lexers/ && cd scripts && python LexGen.py)\n\t@$(MAKE) reldir OUT_DIR=$(RELEASE_DIR)/$(QCC_DIR)scin\n\t@$(MAKE) $(RELEASE_DIR)/scintilla$(BITS).a VPATH=\"$(SCINTILLA_DIRS)\" CFLAGS=\"$(SCINTILLA_INC) -DDISABLE_D2D -DSTATIC_BUILD -DSCI_LEXER -std=c++11\" OUT_DIR=$(RELEASE_DIR)/$(QCC_DIR)scin WCFLAGS=\"$(WCFLAGS) -Os\" WARNINGFLAGS=\nqccgui-scintilla: scintilla$(BITS)_static\n\t#LTO bugs out on WinMain for some reason, so try to disable it for this target.\n\t@LTO= $(MAKE) _qcc-tmp TYPE=_out-rel REQDIR=reldir EXE_NAME=\"../fteqccgui$(BITS)\" OUT_DIR=\"$(RELEASE_DIR)/$(QCC_DIR)scin\" SOBJS=\"qccgui.o qccguistuff.o packager.o decomp.o fteqcc.o\" WCFLAGS=\"$(WCFLAGS) -DSCISTATIC\" LDFLAGS=\"$(LDFLAGS) $(RELEASE_DIR)/scintilla$(BITS).a -static -luuid -lole32 -limm32 -lstdc++ -loleaut32 -lcomdlg32 -lcomctl32 -lshlwapi -mwindows\"\n\nifdef windir\noutdir:\nifneq \"$(OUT_DIR)\" \"\"\n\t@-mkdir -p \"$(subst /,\\, $(OUT_DIR))\"\nendif\ndebugdir: outdir\n\t@-mkdir -p \"$(subst /,\\, $(RELEASE_DIR))\"\nreldir: outdir\n\t@-mkdir -p \"$(subst /,\\, $(DEBUG_DIR))\"\nelse\noutdir:\nifneq \"$(OUT_DIR)\" \"\"\n\t@-mkdir -p \"$(OUT_DIR)\"\nendif\ndebugdir: outdir\nreldir: outdir\n\t@-mkdir -p \"$(RELEASE_DIR)\"\ndebugdir: outdir\n\t@-mkdir -p \"$(DEBUG_DIR)\"\nendif\n\nplugins-dbg:\n\t@-mkdir -p $(DEBUG_DIR)\n\t@if test -e ../plugins/Makefile; \\\n\tthen $(MAKE) native -C ../plugins  OUT_DIR=\"$(DEBUG_DIR)\" CC=\"$(CC) $(W32_CFLAGS) $(DEBUG_CFLAGS)\" CXX=\"$(CXX) $(W32_CFLAGS) $(subst -Wno-pointer-sign,,$(DEBUG_CFLAGS))\" PKGCONFIG=\"$(PKGCONFIG)\" ARCH=\"$(ARCH)\" BASE_CFLAGS=\"$(BASE_CFLAGS) $(BRANDFLAGS)\" BASE_CXXFLAGS=\"$(subst -Wno-pointer-sign,,$(BASE_CFLAGS)) $(BRANDFLAGS)\" FTE_TARGET=\"$(FTE_TARGET)\"; \\\n\t else echo no plugins directory installed; \\\n\tfi\nplugins:\n\nplugins-rel:\n\t@-mkdir -p $(RELEASE_DIR)\n\t@if test -e ../plugins/Makefile; \\\n\tthen $(MAKE) native -C ../plugins  OUT_DIR=\"$(RELEASE_DIR)\" CC=\"$(CC)\" CXX=\"$(CXX)\" PKGCONFIG=\"$(PKGCONFIG)\" ARCH=\"$(ARCH)\" BASE_CFLAGS=\"$(BASE_CFLAGS) $(BRANDFLAGS)\" BASE_CXXFLAGS=\"$(subst -Wno-pointer-sign,,$(BASE_CFLAGS)) $(BRANDFLAGS)\" FTE_TARGET=\"$(FTE_TARGET)\"; \\\n\t else echo no plugins directory installed; \\\n\tfi\nplugins-rel:\n\nhelp:\n\t@-echo \"Specfic targets:\"\n\t@-echo \"clean - removes all output (use make dirs afterwards)\"\n\t@-echo \"all - make all the targets possible\"\n\t@-echo \"rel - make the releases for the default system\"\n\t@-echo \"dbg - make the debug builds for the default system\"\n\t@-echo \"profile - make all the releases with profiling support for the default system\"\n\t@-echo \"\"\n\t@-echo \"Normal targets:\"\n\t@-echo \"(each of these targets must have the postfix -rel or -dbg)\"\n\t@-echo \"'sv-???' (Dedicated Server)\"\n\t@-echo \"'gl-???' (OpenGL rendering + Built-in Server)\"\n\t@-echo \"'m-???' (Merged client, OpenGL & D3D rendering + Dedicated server)\"\n\t@-echo \"'mingl-???' (Minimal featured OpenGL render)\"\n\t@-echo \"'d3d-???' (for windows builds)\"\n\t@-echo \"'mcl-???' (currently broken)\"\n\t@-echo \"'glcl-???' (currently broken)\"\n\t@-echo \"'droid-???' (cross compiles Android package)\"\n\t@-echo \"'web-???' (compiles javascript/emscripten page)\"\n\t@-echo \"\"\n\t@-echo \"Cross targets can be specified with FTE_TARGET=blah\"\n\t@-echo \"linux32, linux64 specify specific x86 archs\"\n\t@-echo \"SDL - Attempt to use sdl for the current target\"\n\t@-echo \"win32 - Mingw compile for win32\"\n\t@-echo \"vc - Attempts to use msvc8+ to compile. Note: uses profile guided optimisations. You must build+run the relevent profile target before a release target will compile properly. Debug doesn't care.\"\n\t@-echo \"android targets explicitly cross compile, and should generally not be given an FTE_TARGET.\"\n\nclean:\n\t-rm -f -r $(RELEASE_DIR)\n\t-rm -f -r $(DEBUG_DIR)\n\t-rm -f -r $(PROFILE_DIR)\n\t-rm -f -r droid/bin\n\t-rm -f -r droid/gen\n\t-rm -f -r droid/libs\n\t-rm -f droid/default.properties\n\t-rm -f droid/local.properties\n\t-rm -f droid/proguard.cfg\n\t-rm -f droid/build.xml\n\ndistclean: clean\n\t-rm -f droid/ftekeystore\n\n\n\n#################################################\n#webgl helpers\n\nifeq ($(FTE_TARGET),web)\n$(OUT_DIR)/$(EXE_NAME): web/ftejslib.js web/prejs.js\nendif\nifneq ($(shell which emcc 2> /dev/null),)\nEMCC?=emcc\nelse\n#EMCC?=/opt/emsdk_portable/emscripten/master/emcc\nEMCC?=emcc.bat --em-config $(shell cygpath -m $(USERPROFILE))/.emscripten\nendif\nifeq ($(EMSDK),)\n\t#just adds some extra paths (WINDOWS HOST ONLY)\n\t#assumes you installed the emscripten 1.22.0 sdk to EMSCRIPTENROOT\n\t#if you have a different version installed, you will need to fix up the paths yourself (or just use fte_target explicitly yourself).\n\tEMSCRIPTENROOT?=C:/Games/tools/Emscripten\n\t#EMSCRIPTENPATH=$(realpath $(EMSCRIPTENROOT)):$(realpath $(EMSCRIPTENROOT)/clang/e1.22.0_64bit):$(realpath $(EMSCRIPTENROOT)/node/0.10.17_64bit):$(realpath $(EMSCRIPTENROOT)/python/2.7.5.3_64bit):$(realpath $(EMSCRIPTENROOT)/emscripten/1.22.0):$(PATH)\n\tEMSCRIPTENPATH=$(realpath $(EMSCRIPTENROOT)):$(realpath $(EMSCRIPTENROOT)/clang/e1.35.0_64bit):$(realpath $(EMSCRIPTENROOT)/node/4.1.1_64bit/bin):$(realpath $(EMSCRIPTENROOT)/python/2.7.5.3_64bit):$(realpath $(EMSCRIPTENROOT)/emscripten/1.35.0):$(PATH)\nelse\n\tEMSCRIPTENPATH=$(PATH)\nendif\n\nweb-rel:\n\t@PATH=\"$(EMSCRIPTENPATH)\" $(MAKE) makelibs FTE_TARGET=web CC=\"$(EMCC)\" && PATH=\"$(EMSCRIPTENPATH)\" $(MAKE) gl-rel FTE_TARGET=web CC=\"$(EMCC)\"\n\t@cp $(BASE_DIR)/web/fteshell.html $(RELEASE_DIR)/ftewebgl.html\n\t@gzip -kf $(RELEASE_DIR)/ftewebgl.html\n\t@gzip -kf $(RELEASE_DIR)/ftewebgl.js\n\t@gzip -kf $(RELEASE_DIR)/ftewebgl.wasm\nwebcl-rel:\n\t@PATH=\"$(EMSCRIPTENPATH)\" $(MAKE) makelibs FTE_TARGET=web CC=\"$(EMCC)\" && PATH=\"$(EMSCRIPTENPATH)\" $(MAKE) glcl-rel FTE_TARGET=web CC=\"$(EMCC)\"\n\t@cp $(BASE_DIR)/web/fteshell.html $(RELEASE_DIR)/ftewebglcl.html\n\t@sed -i 's/ftewebgl.js/ftewebglcl.js/g' release/ftewebglcl.html\t#swap out the .js filename for the client-only one.\n\t@gzip -kf $(RELEASE_DIR)/ftewebglcl.html\n\t@gzip -kf $(RELEASE_DIR)/ftewebglcl.js\n\t@gzip -kf $(RELEASE_DIR)/ftewebglcl.wasm\n\n\nweb-dbg:\n\t@PATH=\"$(EMSCRIPTENPATH)\" $(MAKE) gl-dbg FTE_TARGET=web CC=\"$(EMCC)\"\n\t@cp $(BASE_DIR)/web/fteshell.html $(DEBUG_DIR)/ftewebgl.html\n\t@gzip -kf $(DEBUG_DIR)/ftewebgl.html\n\t@gzip -kf $(DEBUG_DIR)/ftewebgl.js\n\t@gzip -kf $(DEBUG_DIR)/ftewebgl.wasm\n\n#################################################\n#android\n\n#building for android will require:\n#download android sdk+ndk\n#ant installed\n\n#droid-dbg will install it on 'the current device', if you've got a device plugged in or an emulator running, it should just work.\n\n#makes an ant project for us\ndroid/build.xml:\n\t-cd droid && PATH=$$PATH:$(realpath $(ANDROID_HOME)/tools):$(realpath $(ANDROID_NDK_ROOT)) $(ANDROID_SCRIPT) update project -t android-9 -p . -n FTEDroid\n\n#build FTE as a library, then build the java+package (release)\ndroid/ftekeystore:\nifeq ($(KEYTOOLARGS),)\n\t@echo\n\t@echo In order to build a usable APK file it must be signed. That requires a private key.\n\t@echo Creation of a private key requries various bits of info...\n\t@echo You are expected to fill that stuff in now... By the way, don\\'t forget the password!\n\t@echo Note that every time you use make droid-rel, you will be required to enter a password.\n\t@echo You can use \\'make droid-opt\\' instead if you wish to build an optimised build without signing,\n\t@echo  but such packages will require a rooted device \\(or to be signed later\\).\n\t@echo Just press control-c if you don\\'t want to proceed.\n\t@echo Morality warning: never distribute droid/ftekeystore - always do make distclean before distributing.\n\t@echo\n\t$(JAVATOOL)keytool -genkey -v -keystore $@ -alias autogen -keyalg RSA -keysize 2048 -validity 10000\nelse\n\t@echo Generating keystore\n\t@$(JAVATOOL)keytool -genkey -keystore $@ -alias autogen -keyalg RSA -keysize 2048 -validity 10000 -noprompt $(KEYTOOLARGS)\nendif\n\ndroid-rel:\n\t@$(MAKE) FTE_TARGET=droid droid/build.xml droid/ftekeystore\n\t@$(foreach a, $(DROID_ARCH), $(MAKE) FTE_TARGET=droid m-rel plugins-rel DROID_ARCH=$a NATIVE_PLUGINS=\"$(NATIVE_PLUGINS)\"; )\n\t@-rm -rf droid/libs\n\t@$(foreach a, $(DROID_ARCH), mkdir -p droid/libs/$a; )\n\t-@$(foreach a, $(DROID_ARCH), cp $(RELEASE_DIR)/m_droid-$a/*.so droid/libs/$a/; )\n\n\t@cd droid && $(ANT) -q release\nifneq ($(DROID_PACKSU),)\n\t\t@echo Adding custom data files - non-compressed\n\t\tzip droid/bin/FTEDroid-release-unsigned.apk -0 -j $(DROID_PACKSU)\nendif\nifneq ($(DROID_PACKSC),)\n\t\t@echo Adding custom data files - compressed\n\t\tzip droid/bin/FTEDroid-release-unsigned.apk -9 -j $(DROID_PACKSC)\nendif\n\t@echo Signing package... I hope you remember your password.\n\t@$(JAVATOOL)jarsigner $(JARSIGNARGS) -digestalg SHA1 -sigalg MD5withRSA -keystore droid/ftekeystore droid/bin/FTEDroid-release-unsigned.apk autogen\n\t@-rm -f $(RELEASE_DIR)/FTEDroid.apk\n\t@$(ANDROID_ZIPALIGN) 4 droid/bin/FTEDroid-release-unsigned.apk $(NATIVE_RELEASE_DIR)/FTEDroid.apk\n\ndroid-opt:\n\t$(MAKE) FTE_TARGET=droid droid/build.xml droid/ftekeystore\n\t$(MAKE) FTE_TARGET=droid gl-rel\n\tmkdir -p droid/libs/armeabi\n\t@cp $(RELEASE_DIR)/libftedroid.so droid/libs/armeabi/\n\t@cd droid && $(ANT) release\n\tcp droid/bin/FTEDroid-unsigned.apk $(RELEASE_DIR)/FTEDroid.apk\n\n#build FTE as a library, then build the java+package (release). also installs it onto the 'current' device.\ndroid-dbg:\n\t$(MAKE) FTE_TARGET=droid droid/build.xml\n\t$(foreach a, $(DROID_ARCH), $(MAKE) FTE_TARGET=droid m-dbg plugins-dbg DROID_ARCH=$a NATIVE_PLUGINS=\"$(NATIVE_PLUGINS)\"; )\n\t-rm -rf droid/libs\n\t@$(foreach a, $(DROID_ARCH), mkdir -p droid/libs/$a; )\n\t-@$(foreach a, $(DROID_ARCH), cp $(DEBUG_DIR)/m_droid-$a/*.so droid/libs/$a/; )\n\t@cd droid && $(ANT) debug #&& $(ANT) debug install\n\tcp droid/bin/FTEDroid-debug.apk $(DEBUG_DIR)/FTEDroid.apk\n\ndroid-help:\n\t@-echo \"make droid-dbg - compiles engine with debug info and signs package with debug key. Attempts to install onto emulator.\"\n\t@-echo \"make droid-opt - compiles engine with optimisations, but does not sign package. Not useful.\"\n\t@-echo \"make droid-rel - compiles engine with optimisations, adds custom data files, signs with private key, requires password.\"\n\t@-echo\n\t@-echo \"Android Settings:\"\n\t@-echo \"DROID_PACKSC: specifies additional pak or pk3 files to compress into the package, which avoids extra configuration. Only used in release builds. You probably shouldn't use this except for really small packages. Any file seeks will give really poor performance.\"\n\t@-echo \"DROID_PACKSU: like DROID_PACKSC, but without compression. Faster loading times, but bigger. Use for anything that is already compressed (especially pk3s).\"\n\t@-echo \"ANDROID_HOME: path to the android sdk install path.\"\n\t@-echo \"ANDROID_NDK_ROOT: path to the android ndk install path.\"\n\t@-echo \"ANT: path and name of apache ant. Probably doesn't need to be set if you're on linux.\"\n\t@-echo \"JAVA_HOME: path of your java install. Commonly already set in environment settings.\"\n\t@-echo \"JAVATOOL: path to your java install's bin directory. Doesn't need to be set if its already in your path.\"\n\t@-echo \"JARSIGNARGS: Additional optional arguments to java's jarsigner program. You may want to put -storepass FOO in here, but what ever you do - keep it secure. Avoid bash history snooping, etc. If its not present, you will safely be prompted as required.\"\n\t@-echo\n\t@-echo \"Note that 'make droid-rel' will automatically generate a keystore. If you forget the password, just do a 'make dist-clean'.\"\n\n$(BASE_DIR)/libs-$(ARCH)/SDL2-$(SDL2VER)/i686-w64-mingw32/bin/sdl2-config:\n\twget http://www.libsdl.org/release/SDL2-devel-$(SDL2VER)-mingw.tar.gz -O $(BASE_DIR)/sdl2.tar.gz\n\tcd $(BASE_DIR)/libs-$(ARCH) && tar -xvzf $(BASE_DIR)/sdl2.tar.gz\n\trm $(BASE_DIR)/sdl2.tar.gz\n$(BASE_DIR)/libs-$(ARCH)/SDL2-$(SDL2VER)/x86_64-w64-mingw32/bin/sdl2-config: $(BASE_DIR)/libs-$(ARCH)/SDL2-$(SDL2VER)/i686-w64-mingw32/bin/sdl2-config\n\n#makes sure the configure scripts get the right idea.\nAR?=$(ARCH)-ar\n\n\nCONFIGARGS+= --host=$(ARCH) --enable-shared=no CC=\"$(CC)\"\nCONFIGARGS:= $(CONFIGARGS)\n#--disable-silent-rules\nOPUSCONFIGARGS=$(CONFIGARGS)\nSPEEXDSPCONFIGARGS=$(CONFIGARGS)\n\nTOOLOVERRIDES+=CFLAGS=\"$$CFLAGS -Os\"\nTOOLCONGIGUREOVERRIDES=$(TOOLOVERRIDES)\nTOOLMAKEOVERRIDES=$(TOOLOVERRIDES)\nifeq (web,$(FTE_TARGET))\n  TOOLCONFIGUREOVERRIDES=emconfigure\n  TOOLMAKEOVERRIDES=emmake\n  OPUSCONFIGARGS=--disable-rtcd --disable-hardening --enable-stack-protector=no --enable-shared=no --host=none\n  SPEEXDSPCONFIGARGS=--disable-neon --host=none\n  CONFIGARGS=--enable-shared=no --host=none\nendif\n\n\nlibs-$(ARCH)/libjpeg.a:\n\ttest -f jpegsrc.v$(JPEGVER).tar.gz || wget http://www.ijg.org/files/jpegsrc.v$(JPEGVER).tar.gz\n\ttest -f libs-$(ARCH)/libjpeg.a || (mkdir -p libs-$(ARCH) && cd libs-$(ARCH) && tar -xvzf ../jpegsrc.v$(JPEGVER).tar.gz && cd jpeg-$(JPEGVER) && $(CONFIGUREOVERRIDES) ./configure $(CONFIGARGS) && $(TOOLOVERRIDES) $(MAKE) && cp .libs/libjpeg.a ../ && $(TOOLOVERRIDES) $(AR) -s ../libjpeg.a && cp jconfig.h jerror.h jmorecfg.h jpeglib.h jversion.h ../ )\n\nlibs-$(ARCH)/libz.a libs-$(ARCH)/libz.pc:\n\ttest -f zlib-$(ZLIBVER).tar.gz || wget http://zlib.net/fossils/zlib-$(ZLIBVER).tar.gz\n\ttest -f libs-$(ARCH)/libz.a || (mkdir -p libs-$(ARCH) && cd libs-$(ARCH) && tar -xvzf ../zlib-$(ZLIBVER).tar.gz && cd zlib-$(ZLIBVER) && $(TOOLCONFIGUREOVERRIDES) ./configure --static && $(TOOLMAKEOVERRIDES) $(MAKE) libz.a CC=\"$(CC) $(W32_CFLAGS) -fPIC\" && cp libz.a ../ && $(TOOLOVERRIDES) $(AR) -s ../libz.a && cp zlib.h zconf.h zutil.h zlib.pc ../ )\nlibs-$(ARCH)/libz9.a: libs-$(ARCH)/libz.a\n\t(cd libs-$(ARCH)/zlib-$(ZLIBVER) && \\\n\t\t$(CC) -o contrib/infback9/infback9.o -c contrib/infback9/infback9.c -I.\t&& \\\n\t\t$(CC) -o contrib/infback9/inftree9.o -c contrib/infback9/inftree9.c -I.\t&& \\\n\t\tcp contrib/infback9/infback9.h .. && \\\n\t\t$(AR) rcs ../libz9.a contrib/infback9/infback9.o contrib/infback9/inftree9.o)\n\n\nlibs-$(ARCH)/libpng.a libs-$(ARCH)/libpng.pc: libs-$(ARCH)/libz.a libs-$(ARCH)/libz.pc\n\ttest -f libpng-$(PNGVER).tar.gz || wget http://prdownloads.sourceforge.net/libpng/libpng-$(PNGVER).tar.gz?download -O libpng-$(PNGVER).tar.gz\n\ttest -f libs-$(ARCH)/libpng.a || (mkdir -p libs-$(ARCH) && cd libs-$(ARCH) && tar -xvzf ../libpng-$(PNGVER).tar.gz && cd libpng-$(PNGVER) && $(TOOLOVERRIDES) ./configure CPPFLAGS=-I$(NATIVE_ABSBASE_DIR)/libs-$(ARCH)/ LDFLAGS=-L$(NATIVE_ABSBASE_DIR)/libs-$(ARCH)/ $(CONFIGARGS) --enable-static && $(TOOLOVERRIDES) $(MAKE) && cp .libs/libpng16.a ../libpng.a && cp libpng.pc png*.h ../ )\n\nlibs-$(ARCH)/libogg.a:\n\ttest -f libogg-$(OGGVER).tar.gz || wget http://downloads.xiph.org/releases/ogg/libogg-$(OGGVER).tar.gz\n\ttest -f libs-$(ARCH)/libogg.a || (mkdir -p libs-$(ARCH) && cd libs-$(ARCH) && tar -xvzf ../libogg-$(OGGVER).tar.gz && cd libogg-$(OGGVER) && $(TOOLOVERRIDES) ./configure $(CONFIGARGS) && $(TOOLOVERRIDES) $(MAKE) && cp src/.libs/libogg.a ../ && $(TOOLOVERRIDES) $(AR) -s ../libogg.a && mkdir ../ogg && cp include/ogg/*.h ../ogg)\n\nlibs-$(ARCH)/libvorbis.a: libs-$(ARCH)/libogg.a\n\ttest -f libvorbis-$(VORBISVER).tar.gz || wget http://downloads.xiph.org/releases/vorbis/libvorbis-$(VORBISVER).tar.gz\n\ttest -f libs-$(ARCH)/libvorbisfile.a || (mkdir -p libs-$(ARCH) && cd libs-$(ARCH) && tar -xvzf ../libvorbis-$(VORBISVER).tar.gz && cd libvorbis-$(VORBISVER) && $(TOOLOVERRIDES) ./configure PKG_CONFIG= $(CONFIGARGS) --disable-oggtest --with-ogg-libraries=.. --with-ogg-includes=$(NATIVE_ABSBASE_DIR)/libs-$(ARCH)/libogg-$(OGGVER)/include && $(TOOLOVERRIDES) $(MAKE) && cp lib/.libs/libvorbis.a ../ && cp lib/.libs/libvorbisfile.a ../  && mkdir ../vorbis && cp include/vorbis/*.h ../vorbis)\n\nlibs-$(ARCH)/libopus.a:\n\ttest -f opus-$(OPUSVER).tar.gz || wget https://archive.mozilla.org/pub/opus/opus-$(OPUSVER).tar.gz\n\ttest -f libs-$(ARCH)/libopus.a || (mkdir -p libs-$(ARCH) && cd libs-$(ARCH) && tar -xvzf ../opus-$(OPUSVER).tar.gz && cd opus-$(OPUSVER) && CFLAGS=\"-D_FORTIFY_SOURCE=0 $(CFLAGS) -Os\" $(TOOLCONFIGUREOVERRIDES) ./configure $(OPUSCONFIGARGS) --disable-extra-programs && $(TOOLMAKEOVERRIDES) $(MAKE) && cp .libs/libopus.a ../ && cp include/opus*.h ../)\n\nlibs-$(ARCH)/libspeex.a:\n\ttest -f speex-$(SPEEXVER).tar.gz || wget http://downloads.us.xiph.org/releases/speex/speex-$(SPEEXVER).tar.gz\n\ttest -f libs-$(ARCH)/libspeex.a || (mkdir -p libs-$(ARCH)/speex && cd libs-$(ARCH) && tar -xvzf ../speex-$(SPEEXVER).tar.gz && cd speex-$(SPEEXVER) && CFLAGS=\"$(CFLAGS) -Os\" $(TOOLCONFIGUREOVERRIDES) ./configure $(CONFIGARGS) --disable-binaries && $(TOOLMAKEOVERRIDES) $(MAKE) && cp libspeex/.libs/libspeex.a ../ && cp -r include/speex/*.h ../speex/)\n\nlibs-$(ARCH)/libspeexdsp.a:\n\ttest -f speexdsp-$(SPEEXDSPVER).tar.gz || wget http://downloads.xiph.org/releases/speex/speexdsp-$(SPEEXDSPVER).tar.gz\n\ttest -f libs-$(ARCH)/libspeexdsp.a || (mkdir -p libs-$(ARCH)/speex && cd libs-$(ARCH) && tar -xvzf ../speexdsp-$(SPEEXDSPVER).tar.gz && cd speexdsp-$(SPEEXDSPVER) && CFLAGS=\"$(CFLAGS) -Os\" $(TOOLCONFIGUREOVERRIDES) ./configure $(SPEEXDSPCONFIGARGS) && $(TOOLMAKEOVERRIDES) $(MAKE) && cp libspeexdsp/.libs/libspeexdsp.a ../ && cp -r include/speex/*.h ../speex/)\n\nlibs-$(ARCH)/libfreetype.a libs-$(ARCH)/ft2build.h: libs-$(ARCH)/libpng.a libs-$(ARCH)/libpng.pc\n\ttest -f freetype-$(FREETYPEVER).tar.gz || wget https://download-mirror.savannah.gnu.org/releases/freetype/freetype-$(FREETYPEVER).tar.gz\n\ttest -f libs-$(ARCH)/libfreetype.a || (mkdir -p libs-$(ARCH) && cd libs-$(ARCH) && tar -xvzf ../freetype-$(FREETYPEVER).tar.gz && cd freetype-$(FREETYPEVER) && PKG_CONFIG_LIBDIR=$(NATIVE_ABSBASE_DIR)/libs-$(ARCH) CFLAGS=\"$(CFLAGS) -Os\" $(TOOLOVERRIDES) ./configure CPPFLAGS=-I$(NATIVE_ABSBASE_DIR)/libs-$(ARCH)/ LDFLAGS=-L$(NATIVE_ABSBASE_DIR)/libs-$(ARCH)/ $(CONFIGARGS) --with-zlib=yes --with-png=yes --with-bzip2=no --with-harfbuzz=no && $(TOOLOVERRIDES) $(MAKE) && cp objs/.libs/libfreetype.a ../ && cp -r include/* ../)\n\nlibs-$(ARCH)/libBulletDynamics.a:\n\ttest -f bullet3-$(BULLETVER).tar.gz || wget https://github.com/bulletphysics/bullet3/archive/$(BULLETVER).tar.gz -O bullet3-$(BULLETVER).tar.gz\n\ttest -f libs-$(ARCH)/libBulletDynamics.a || (mkdir -p libs-$(ARCH) && cd libs-$(ARCH) && tar -xvzf ../bullet3-$(BULLETVER).tar.gz && cd bullet3-$(BULLETVER) && CFLAGS=\"$(CFLAGS) -Os\" $(TOOLOVERRIDES) $(DO_CMAKE) . && $(TOOLOVERRIDES) $(MAKE) LinearMath BulletDynamics BulletCollision && cp src/LinearMath/libLinearMath.a src/BulletDynamics/libBulletDynamics.a src/BulletCollision/libBulletCollision.a src/btBulletCollisionCommon.h src/btBulletDynamicsCommon.h ..)\n\nlibs-$(ARCH)/vulkan/vulkan.h:\n\ttest -f vulkan-sdk-$(VULKANVER).tar.gz || wget https://github.com/KhronosGroup/Vulkan-Headers/archive/refs/tags/vulkan-sdk-$(VULKANVER).tar.gz  \n\tcd libs-$(ARCH) && tar -xvzf ../vulkan-sdk-$(VULKANVER).tar.gz --strip-components=2 Vulkan-Headers-vulkan-sdk-$(VULKANVER)/include/\n\nifeq ($(FTE_TARGET),web)\nmakelibs: libs-$(ARCH)/libz.a $(MAKELIBS)\nelse\nMAKELIBS+=libs-$(ARCH)/vulkan/vulkan.h\nmakelibs: libs-$(ARCH)/libjpeg.a libs-$(ARCH)/libz9.a libs-$(ARCH)/libz.a libs-$(ARCH)/libpng.a libs-$(ARCH)/libogg.a libs-$(ARCH)/libvorbis.a libs-$(ARCH)/libopus.a libs-$(ARCH)/libspeex.a libs-$(ARCH)/libspeexdsp.a libs-$(ARCH)/libfreetype.a $(MAKELIBS)\nendif\n\nHTTP_OBJECTS=http/httpserver.c http/iwebiface.c common/fs_stdio.c http/ftpserver.c\n$(RELEASE_DIR)/httpserver$(BITS)$(EXEPOSTFIX): $(HTTP_OBJECTS)\n\t$(CC) -o $@ -Icommon -Iclient -Iqclib -Igl -Iserver -DWEBSERVER -DWEBSVONLY -Dstricmp=strcasecmp -Dstrnicmp=strncasecmp -DNO_PNG $(HTTP_OBJECTS)\nhttpserver: $(RELEASE_DIR)/httpserver$(BITS)$(EXEPOSTFIX)\n\nIQM_OBJECTS=../iqm/iqm.cpp ../imgtool.c client/image.c common/json.c ../plugins/models/gltf.c\n$(RELEASE_DIR)/iqmtool$(BITS)$(EXEPOSTFIX): $(IQM_OBJECTS)\nifeq (win,$(findstring win,$(FTE_TARGET)))\n\t$(CC) -o $@ $(IQM_OBJECTS) -Icommon -Iclient -Iqclib -Igl -Iserver $(ALL_CFLAGS) $(CLIENTLIBFLAGS) -DIQMTOOL $(BASELDFLAGS) $(CLIENTLDDEPS) --static -static-libgcc -static-libstdc++ -lstdc++ -lm -Os\nelse\n\t$(CC) -o $@ $(IQM_OBJECTS) -Icommon -Iclient -Iqclib -Igl -Iserver $(ALL_CFLAGS) -DIQMTOOL -lstdc++ -lm -ldl -Os\nendif\niqm-rel: reldir $(RELEASE_DIR)/iqmtool$(BITS)$(EXEPOSTFIX)\niqmtool-rel: reldir $(RELEASE_DIR)/iqmtool$(BITS)$(EXEPOSTFIX)\niqm: iqm-rel\niqmtool: iqmtool-rel\n\nIMGTOOL_OBJECTS=../imgtool.c client/image.c\n$(RELEASE_DIR)/imgtool$(BITS)$(EXEPOSTFIX): $(IMGTOOL_OBJECTS)\n\t$(CC) -o $@ $(IMGTOOL_OBJECTS) -lstdc++ -lm -Os $(ALL_CFLAGS) $(CLIENTLIBFLAGS) -DIMGTOOL $(BASELDFLAGS) $(CLIENTLDDEPS)\n\nimgtool-rel: $(RELEASE_DIR)/imgtool$(BITS)$(EXEPOSTFIX)\nimgtool: imgtool-rel\n\nifeq (win,$(findstring win,$(FTE_TARGET)))\n    MASTER_LDFLAGS=-lm -lz -lws2_32 -lwinmm -lole32\n    MASTER_OBJECTS+=common/fs_win32.c\nelse\n    MASTER_LDFLAGS=-lm -ldl -lz\nendif\n$(RELEASE_DIR)/ftemaster$(BITS)$(EXEPOSTFIX): $(MASTER_OBJECTS)\n\t$(CC) -o $@ $(MASTER_OBJECTS) -flto=jobserver -fvisibility=hidden -Icommon -Iclient -Iqclib -Igl -Iserver -DMASTERONLY -Dstricmp=strcasecmp -Dstrnicmp=strncasecmp $(MASTER_LDFLAGS) $(RELEASE_CFLAGS) $(RELEASE_LDFLAGS) $(ALL_CFLAGS)\n$(DEBUG_DIR)/ftemaster$(BITS)$(EXEPOSTFIX): $(MASTER_OBJECTS)\n\t$(CC) -o $@ $(MASTER_OBJECTS) -Icommon -Iclient -Iqclib -Igl -Iserver -DMASTERONLY -Dstricmp=strcasecmp -Dstrnicmp=strncasecmp $(MASTER_LDFLAGS) $(DEBUG_CFLAGS) $(DEBUG_LDFLAGS)\nmaster-rel: reldir $(RELEASE_DIR)/ftemaster$(BITS)$(EXEPOSTFIX)\nmaster-dbg: dbgdir $(DEBUG_DIR)/ftemaster$(BITS)$(EXEPOSTFIX)\nmaster: master-rel\n\nQTV_OBJECTS=\t\\\n\tnetchan.c\t\\\n\tparse.c\t\t\\\n\tmsg.c\t\t\\\n\tqw.c\t\t\\\n\tsource.c\t\\\n\tbsp.c\t\t\\\n\trcon.c\t\t\\\n\tmdfour.c\t\\\n\tmd5.c\t\t\\\n\tcrc.c\t\t\\\n\tcontrol.c\t\\\n\tforward.c\t\\\n\trelay.c\t\t\\\n\tpmove.c\t\t\\\n\tmenu.c\t\t\\\n\tmsg.c\t\t\\\n\thttpsv.c\t\\\n\tsha1.c\t\t\\\n\tlibqtvc/glibc_sucks.c\n$(RELEASE_DIR)/qtv$(BITS)$(EXEPOSTFIX): $(QTV_OBJECTS)\n\t$(CC) -o $@ $? -lstdc++ -lm $(BASE_INCLUDES) $(QTV_LDFLAGS) $(SVNREVISION)\nqtv-rel:\n\t@$(MAKE) $(RELEASE_DIR)/qtv$(BITS)$(EXEPOSTFIX) VPATH=\"$(BASE_DIR)/../fteqtv:$(VPATH)\"\nqtv: qtv-rel\n\nutils: httpserver iqmtool imgtool master qtv-rel\n\nprefix ?= /usr/local\nexec_prefix ?= $(prefix)\nbindir ?= $(exec_prefix)/bin\nsbindir ?= $(exec_prefix)/sbin\nINSTALL ?= install\nINSTALL_PROGRAM ?= $(INSTALL)\nINSTALL_DATA ?= ${INSTALL} -m 644\ninstall: sv-rel gl-rel mingl-rel qcc-rel\n\t$(INSTALL_PROGRAM) $(RELEASE_DIR)/$(EXE_NAME)-gl $(DESTDIR)$(bindir)/$(EXE_NAME)-gl\n\t$(INSTALL_PROGRAM) $(RELEASE_DIR)/$(EXE_NAME)-mingl $(DESTDIR)$(bindir)/$(EXE_NAME)-mingl\n\t$(INSTALL_PROGRAM) $(RELEASE_DIR)/$(EXE_NAME)-sv $(DESTDIR)$(bindir)/$(EXE_NAME)-sv\n\t$(INSTALL_PROGRAM) $(RELEASE_DIR)/fteqcc $(DESTDIR)$(bindir)/fteqcc\n\nversion:\n\t@echo $(SVN_VERSION)\n\t@echo $(SVN_DATE)\n\t@echo $(FTE_CONFIG_EXTRA)\n\n"
  },
  {
    "path": "engine/README.MSVC",
    "content": "This code compiles against libjpeg, libpng, zlib, dx7, libogg and libvorbis.\nYou can find the main MSVC 6 workspace in the ftequake directory.\nYou will need to build the gas2masm project's debug build first.\nAfter that, you will have a choice of FTE builds.\nIf you are running without libraries, you can pick the mingldebug build. For sw only builds, select the debug/release options. For dedicated builds, choose the logical one. The MDebug/MRelease builds are the merged binaries.\n\nimportant: msvc 6.0 standard install doesn't come with \"ml.exe\" which is needed to build fte, you can get it from either service pack 5 or 6. or grab it from somewhere i dunno\n\nzlib:\nlibs/zconf.h\nlibs/zlib.h\nlibs/zlib.lib\nYou will need zlib if you wish to build a version of FTE with png/zip/pk3 support.\nIf you don't have it, you can hunt out the line '#define AVAIL_ZLIB' in bothdefs.h and disable it.\n\nOgg Vorbis:\nlibs/ogg/*\nlibs/vorbis/*\nAt the time of writing, ogg vorbis support is not fully functional and is #ifdefed out. You will not need these libraries.\nLack of these files can be indicated by removing any '#define AVAIL_OGGVORBIS' line found in bothdefs.h (if they exist)\n\nlib jpeg:\nlibs/jpeg.lib\nlibs/jpeglib.h\nlibs/jmorecfg.h\nlibs/jconfig.h\nlibs/jerror.h\nThese files are optional and not strictly needed for anything other than screenshots and loading Quake3 textures.\nHunt out and kill '#define AVAIL_JPEGLIB' from bothdefs.h to disable the requirement.\nURL pending.\n\nlibpng:\nlibs/libpng.lib\nlibs/png.h\nlibs/pngconf.h\nThese files are for support of png textures and screenshots.\nHunt out and kill '#define AVAIL_PNGLIB' from bothdefs.h to disable the requirement.\nURL pending.\n\nDirectX 7 SDK:\nlibs/dxsdk7/include/*\nlibs/dxsdk7/lib/*\nThese are used for the d3d renderer. They are only benefitial in this way.\nThis feature is normally enabled via an ifdef in the project file. An '#define NODIRECTX' in bothdefs.h will disable all requirements of dx. It is normally only used in the merged binaries.\nWithout this define, it will expect to find DX5 headers and libraries in your compilers default directories.\nYou can obtain an uptodate copy of directx from Microsoft's Website.\n"
  },
  {
    "path": "engine/client/anorms.h",
    "content": "/*\nCopyright (C) 1996-1997 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  \n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n*/\n{-0.525731, 0.000000, 0.850651}, \n{-0.442863, 0.238856, 0.864188}, \n{-0.295242, 0.000000, 0.955423}, \n{-0.309017, 0.500000, 0.809017}, \n{-0.162460, 0.262866, 0.951056}, \n{0.000000, 0.000000, 1.000000}, \n{0.000000, 0.850651, 0.525731}, \n{-0.147621, 0.716567, 0.681718}, \n{0.147621, 0.716567, 0.681718}, \n{0.000000, 0.525731, 0.850651}, \n{0.309017, 0.500000, 0.809017}, \n{0.525731, 0.000000, 0.850651}, \n{0.295242, 0.000000, 0.955423}, \n{0.442863, 0.238856, 0.864188}, \n{0.162460, 0.262866, 0.951056}, \n{-0.681718, 0.147621, 0.716567}, \n{-0.809017, 0.309017, 0.500000}, \n{-0.587785, 0.425325, 0.688191}, \n{-0.850651, 0.525731, 0.000000}, \n{-0.864188, 0.442863, 0.238856}, \n{-0.716567, 0.681718, 0.147621}, \n{-0.688191, 0.587785, 0.425325}, \n{-0.500000, 0.809017, 0.309017}, \n{-0.238856, 0.864188, 0.442863}, \n{-0.425325, 0.688191, 0.587785}, \n{-0.716567, 0.681718, -0.147621}, \n{-0.500000, 0.809017, -0.309017}, \n{-0.525731, 0.850651, 0.000000}, \n{0.000000, 0.850651, -0.525731}, \n{-0.238856, 0.864188, -0.442863}, \n{0.000000, 0.955423, -0.295242}, \n{-0.262866, 0.951056, -0.162460}, \n{0.000000, 1.000000, 0.000000}, \n{0.000000, 0.955423, 0.295242}, \n{-0.262866, 0.951056, 0.162460}, \n{0.238856, 0.864188, 0.442863}, \n{0.262866, 0.951056, 0.162460}, \n{0.500000, 0.809017, 0.309017}, \n{0.238856, 0.864188, -0.442863}, \n{0.262866, 0.951056, -0.162460}, \n{0.500000, 0.809017, -0.309017}, \n{0.850651, 0.525731, 0.000000}, \n{0.716567, 0.681718, 0.147621}, \n{0.716567, 0.681718, -0.147621}, \n{0.525731, 0.850651, 0.000000}, \n{0.425325, 0.688191, 0.587785}, \n{0.864188, 0.442863, 0.238856}, \n{0.688191, 0.587785, 0.425325}, \n{0.809017, 0.309017, 0.500000}, \n{0.681718, 0.147621, 0.716567}, \n{0.587785, 0.425325, 0.688191}, \n{0.955423, 0.295242, 0.000000}, \n{1.000000, 0.000000, 0.000000}, \n{0.951056, 0.162460, 0.262866}, \n{0.850651, -0.525731, 0.000000}, \n{0.955423, -0.295242, 0.000000}, \n{0.864188, -0.442863, 0.238856}, \n{0.951056, -0.162460, 0.262866}, \n{0.809017, -0.309017, 0.500000}, \n{0.681718, -0.147621, 0.716567}, \n{0.850651, 0.000000, 0.525731}, \n{0.864188, 0.442863, -0.238856}, \n{0.809017, 0.309017, -0.500000}, \n{0.951056, 0.162460, -0.262866}, \n{0.525731, 0.000000, -0.850651}, \n{0.681718, 0.147621, -0.716567}, \n{0.681718, -0.147621, -0.716567}, \n{0.850651, 0.000000, -0.525731}, \n{0.809017, -0.309017, -0.500000}, \n{0.864188, -0.442863, -0.238856}, \n{0.951056, -0.162460, -0.262866}, \n{0.147621, 0.716567, -0.681718}, \n{0.309017, 0.500000, -0.809017}, \n{0.425325, 0.688191, -0.587785}, \n{0.442863, 0.238856, -0.864188}, \n{0.587785, 0.425325, -0.688191}, \n{0.688191, 0.587785, -0.425325}, \n{-0.147621, 0.716567, -0.681718}, \n{-0.309017, 0.500000, -0.809017}, \n{0.000000, 0.525731, -0.850651}, \n{-0.525731, 0.000000, -0.850651}, \n{-0.442863, 0.238856, -0.864188}, \n{-0.295242, 0.000000, -0.955423}, \n{-0.162460, 0.262866, -0.951056}, \n{0.000000, 0.000000, -1.000000}, \n{0.295242, 0.000000, -0.955423}, \n{0.162460, 0.262866, -0.951056}, \n{-0.442863, -0.238856, -0.864188}, \n{-0.309017, -0.500000, -0.809017}, \n{-0.162460, -0.262866, -0.951056}, \n{0.000000, -0.850651, -0.525731}, \n{-0.147621, -0.716567, -0.681718}, \n{0.147621, -0.716567, -0.681718}, \n{0.000000, -0.525731, -0.850651}, \n{0.309017, -0.500000, -0.809017}, \n{0.442863, -0.238856, -0.864188}, \n{0.162460, -0.262866, -0.951056}, \n{0.238856, -0.864188, -0.442863}, \n{0.500000, -0.809017, -0.309017}, \n{0.425325, -0.688191, -0.587785}, \n{0.716567, -0.681718, -0.147621}, \n{0.688191, -0.587785, -0.425325}, \n{0.587785, -0.425325, -0.688191}, \n{0.000000, -0.955423, -0.295242}, \n{0.000000, -1.000000, 0.000000}, \n{0.262866, -0.951056, -0.162460}, \n{0.000000, -0.850651, 0.525731}, \n{0.000000, -0.955423, 0.295242}, \n{0.238856, -0.864188, 0.442863}, \n{0.262866, -0.951056, 0.162460}, \n{0.500000, -0.809017, 0.309017}, \n{0.716567, -0.681718, 0.147621}, \n{0.525731, -0.850651, 0.000000}, \n{-0.238856, -0.864188, -0.442863}, \n{-0.500000, -0.809017, -0.309017}, \n{-0.262866, -0.951056, -0.162460}, \n{-0.850651, -0.525731, 0.000000}, \n{-0.716567, -0.681718, -0.147621}, \n{-0.716567, -0.681718, 0.147621}, \n{-0.525731, -0.850651, 0.000000}, \n{-0.500000, -0.809017, 0.309017}, \n{-0.238856, -0.864188, 0.442863}, \n{-0.262866, -0.951056, 0.162460}, \n{-0.864188, -0.442863, 0.238856}, \n{-0.809017, -0.309017, 0.500000}, \n{-0.688191, -0.587785, 0.425325}, \n{-0.681718, -0.147621, 0.716567}, \n{-0.442863, -0.238856, 0.864188}, \n{-0.587785, -0.425325, 0.688191}, \n{-0.309017, -0.500000, 0.809017}, \n{-0.147621, -0.716567, 0.681718}, \n{-0.425325, -0.688191, 0.587785}, \n{-0.162460, -0.262866, 0.951056}, \n{0.442863, -0.238856, 0.864188}, \n{0.162460, -0.262866, 0.951056}, \n{0.309017, -0.500000, 0.809017}, \n{0.147621, -0.716567, 0.681718}, \n{0.000000, -0.525731, 0.850651}, \n{0.425325, -0.688191, 0.587785}, \n{0.587785, -0.425325, 0.688191}, \n{0.688191, -0.587785, 0.425325}, \n{-0.955423, 0.295242, 0.000000}, \n{-0.951056, 0.162460, 0.262866}, \n{-1.000000, 0.000000, 0.000000}, \n{-0.850651, 0.000000, 0.525731}, \n{-0.955423, -0.295242, 0.000000}, \n{-0.951056, -0.162460, 0.262866}, \n{-0.864188, 0.442863, -0.238856}, \n{-0.951056, 0.162460, -0.262866}, \n{-0.809017, 0.309017, -0.500000}, \n{-0.864188, -0.442863, -0.238856}, \n{-0.951056, -0.162460, -0.262866}, \n{-0.809017, -0.309017, -0.500000}, \n{-0.681718, 0.147621, -0.716567}, \n{-0.681718, -0.147621, -0.716567}, \n{-0.850651, 0.000000, -0.525731}, \n{-0.688191, 0.587785, -0.425325}, \n{-0.587785, 0.425325, -0.688191}, \n{-0.425325, 0.688191, -0.587785}, \n{-0.425325, -0.688191, -0.587785}, \n{-0.587785, -0.425325, -0.688191}, \n{-0.688191, -0.587785, -0.425325}, \n"
  },
  {
    "path": "engine/client/api_menu.h",
    "content": " /*\r\n * Copyright (c) 2015-2018\r\n * Marco Cawthorne  All rights reserved.\r\n * \r\n * This is free software: you can redistribute it and/or modify\r\n * it under the terms of the GNU General Public License as published by\r\n * the Free Software Foundation, either version 3 of the License, or\r\n * (at your option) any later version.\r\n\r\n * This is distributed in the hope that it will be useful,\r\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\r\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r\n * GNU General Public License for more details.\r\n\r\n * You should have received a copy of the GNU General Public License\r\n * along with this. If not, see <http://www.gnu.org/licenses/>.\r\n */\r\n\r\n#define NATIVEMENU_API_VERSION_MIN 0\t//will be updated any time a symbol is renamed.\r\n#define NATIVEMENU_API_VERSION_MAX 0\t//bumped for any change.\r\n#ifndef NATIVEMENU_API_VERSION\t\t\t//so you can hold back the reported version in order to work with older engines.\r\n#define NATIVEMENU_API_VERSION NATIVEMENU_API_VERSION_MAX\t//version reported to the other side.\r\n#endif\r\n\r\nstruct vfsfile_s;\r\nstruct serverinfo_s;\r\nstruct searchpathfuncs_s;\r\nstruct model_s;\r\nstruct font_s;\r\nstruct shader_s;\r\n\r\n#ifndef __QUAKEDEF_H__\r\n\t#ifdef __cplusplus\r\n\t\ttypedef enum {qfalse, qtrue} qboolean;//false and true are forcivly defined.\r\n\t#else\r\n\t\ttypedef enum {false, true}\tqboolean;\r\n\t#endif\r\n\ttypedef float vec_t;\r\n\ttypedef vec_t vec2_t[2];\r\n\ttypedef vec_t vec3_t[3];\r\n\ttypedef vec_t vec4_t[4];\r\n\t#ifdef _MSC_VER\r\n\t\t#define QDECL __cdecl\r\n\t#else\r\n\t\t#define QDECL\r\n\t#endif\r\n\r\n\t#include <stdint.h>\r\n\ttypedef uint64_t qofs_t;\r\n#endif\r\n\r\n#if 1 //c++ or standard C\r\n\t#include \"cl_master.h\"\r\n#endif\r\nenum slist_test_e;\r\nenum hostcachekey_e;\t//obtained via calls to gethostcacheindexforkey\r\nenum fs_relative;\r\nenum com_tokentype_e;\r\n\r\nstruct menu_inputevent_args_s\r\n{\r\n\tenum {\r\n\t\tMIE_KEYDOWN\t\t= 0,\r\n\t\tMIE_KEYUP\t\t= 1,\r\n\t\tMIE_MOUSEDELTA\t= 2,\r\n\t\tMIE_MOUSEABS\t= 3,\r\n\t\tMIE_JOYAXIS\t\t= 4,\r\n\t} eventtype;\r\n\tunsigned int devid;\r\n\tunion\r\n\t{\r\n\t\tstruct\r\n\t\t{\r\n\t\t\tunsigned int scancode;\r\n\t\t\tunsigned int charcode;\r\n\t\t} key;\r\n\t\tstruct\r\n\t\t{\r\n\t\t\tfloat delta[2];\r\n\t\t\tfloat screen[2]; //virtual coords\r\n\t\t} mouse;\r\n\t\tstruct\r\n\t\t{\r\n\t\t\tunsigned int axis;\r\n\t\t\tfloat val;\r\n\t\t} axis;\r\n\t};\r\n};\r\n\r\ntypedef enum\r\n{\r\n\tMI_INIT,\t\t//initial startup\r\n\tMI_RENDERER,\t//renderer restarted, any models/shaders/textures handles are no longer valid\r\n\tMI_RESOLUTION,\t//video mode changed (scale or physical size) but without any gpu resources getting destroyed. you'll want to reload fonts.\r\n} mintreason_t;\r\n\r\ntypedef struct\r\n{\r\n\tstruct model_s *model;\r\n\tint frame[2];\r\n\tfloat frametime[2];\r\n\tfloat frameweight[2];\r\n\tvec4_t matrix[3]; //axis/angles+origin\r\n} menuentity_t;\r\ntypedef struct\r\n{\r\n\t//these are in virtual coords, thus they need to be floats so that they can be rounded to ints more cleanly... yeah, scaling sucks.\r\n\tvec2_t pos;\r\n\tvec2_t size;\r\n\r\n\tfloat time;\t//affects shader effects\r\n\tvec_t fov[2];\r\n\tvec4_t viewmatrix[3];\r\n\r\n\tstruct model_s *worldmodel;\r\n\tint numentities;\r\n\tmenuentity_t *entlist;\r\n} menuscene_t;\r\n\r\ntypedef struct {\r\n\tint\t\t\t\t\t\t\tapi_version;\t//this may be higher than you expect.\r\n\tconst char\t\t\t\t\t*engine_version;\r\n\r\n\tint (*checkextension)\t\t(const char *ext);\r\n\tvoid (QDECL *error)\t\t\t(const char *err, ...);\r\n\tvoid (*printf)\t\t\t\t(const char *text, ...);\r\n\tvoid (*dprintf)\t\t\t\t(const char *text, ...);\r\n\tvoid (*localcmd)\t\t\t(const char *cmd);\r\n\tfloat (*cvar_float)\t\t\t(const char *name);\r\n\tconst char *(*cvar_string)\t(const char *name, qboolean effective);\t//NULL if it doesn't exist. return value lasts until cvar_set is called, etc, so don't cache. effective=true reports its active value, not the value that the user wanted.\r\n\tconst char *(*cvar_default)\t(const char *name);\r\n\tvoid (*cvar_set)\t\t\t(const char *name, const char *value);\r\n\tvoid (*registercvar)\t\t(const char *name, const char *defaultvalue, unsigned int flags, const char *description);\r\n\tvoid (*registercommand)\t\t(const char *name, const char *description);\r\n\r\n\tchar *(*parsetoken)\t\t\t(const char *data, char *out, int outlen, enum com_tokentype_e *toktype);\r\n\r\n\tint (*isserver)\t\t\t\t(void);\r\n\tint (*getclientstate)\t\t(char const**disconnectionreason);\r\n\tvoid (*localsound)\t\t\t(const char *sample, int channel, float volume);\r\n\r\n\t// file input / search crap\r\n\tstruct vfsfile_s *(*fopen)\t(const char *filename, const char *modestring, enum fs_relative fsroot);\t//modestring should be one of rb,r+b,wb,w+b,ab,wbp. Mostly use a root of FS_GAMEONLY for writes, otherwise FS_GAME for reads.\r\n\tvoid (*fclose)\t\t\t\t(struct vfsfile_s *fhandle);\r\n\tchar *(*fgets)\t\t\t\t(struct vfsfile_s *fhandle, char *out, size_t outsize);\t//returns output buffer, or NULL\r\n\tvoid (*fprintf)\t\t\t\t(struct vfsfile_s *fhandle, const char *s, ...);\r\n\tvoid (*enumeratefiles)\t\t(const char *match, int (QDECL *callback)(const char *fname, qofs_t fsize, time_t mtime, void *ctx, struct searchpathfuncs_s *package), void *ctx);\r\n\tqboolean (QDECL *nativepath)(const char *fname, enum fs_relative relativeto, char *out, int outlen);\t//Converts a relative path to a printable system path. All paths are considered to be utf-8. WARNING: This means that windows users will need to use _wfopen etc if they use the resulting path of this function in any system calls. WARNING: this function can and WILL fail for dodgy paths (eg blocking writes to \"../engine.dll\")\r\n\r\n\t// Drawing stuff\r\n\tvoid (*drawsetcliparea)\t\t(float x, float y, float width, float height);\r\n\tvoid (*drawresetcliparea)\t(void);\r\n\tstruct shader_s *(*cachepic)(const char *name);\r\n\tqboolean (*drawgetimagesize)(struct shader_s *pic, int *x, int *y);\r\n\tvoid (*drawquad)\t\t\t(const vec2_t position[4], const vec2_t texcoords[4], struct shader_s *pic, const vec4_t rgba, unsigned int be_flags);\r\n\r\n\tfloat (*drawstring)\t\t\t(const vec2_t position, const char *text, struct font_s *font, float height, const vec4_t rgba, unsigned int be_flags);\r\n\tfloat (*stringwidth)\t\t(const char *text, struct font_s *font, float height);\r\n\tstruct font_s *(*loadfont)\t(const char *facename, float intendedheight);\t//with ttf fonts, you'll probably want one for each size.\r\n\tvoid (*destroyfont)\t\t\t(struct font_s *font);\r\n\r\n\t// 3D scene stuff\r\n\tstruct model_s *(*cachemodel)(const char *name);\r\n\tqboolean (*getmodelsize)\t(struct model_s *model, vec3_t out_mins, vec3_t out_maxs);\r\n\tvoid (*renderscene)\t\t\t(menuscene_t *scene);\r\n\r\n\t// Menu specific stuff\r\n\tvoid (*pushmenu)\t\t\t\t(void *ctx);\t//will have key focus.\r\n\tqboolean (*ismenupushed)\t\t(void *ctx);\t//reports if its still pushed (but not necessarily the active one!).\r\n\tvoid (*killmenu)\t\t\t\t(void *ctx);\t//force-removes a menu.\r\n\tint (*setmousecursor)\t\t\t(const char *cursorname, float hot_x, float hot_y, float scale);\t//forces absolute mouse coords whenever cursorname isn't NULL\r\n\tconst char *(*keynumtostring)\t(int keynum, int modifier);\r\n\tint (*stringtokeynum)\t\t\t(const char *key, int *modifier);\r\n\tint (*findkeysforcommand)\t\t(int bindmap, const char *command, int *out_scancodes, int *out_modifiers, int keycount);\r\n\r\n\t// Server browser stuff\r\n\tenum hostcachekey_e (*gethostcacheindexforkey)\t(const char *key);\r\n\tstruct serverinfo_s *(*getsortedhost)\t\t\t(int idx);\r\n\tchar *(*gethostcachestring)\t\t\t\t\t\t(struct serverinfo_s *host, enum hostcachekey_e fld);\r\n\tfloat (*gethostcachenumber)\t\t\t\t\t\t(struct serverinfo_s *host, enum hostcachekey_e fld);\r\n\tvoid (*resethostcachemasks)\t\t\t\t\t\t(void);\r\n\tvoid (*sethostcachemaskstring)\t\t\t\t\t(qboolean or_, enum hostcachekey_e fld, const char *str, enum slist_test_e op);\r\n\tvoid (*sethostcachemasknumber)\t\t\t\t\t(qboolean or_, enum hostcachekey_e fld, int num, enum slist_test_e op);\r\n\tvoid (*sethostcachesort)\t\t\t\t\t\t(enum hostcachekey_e fld, qboolean descending);\r\n\tint (*resorthostcache)\t\t\t\t\t\t\t(void);\r\n\tvoid (*refreshhostcache)\t\t\t\t\t\t(qboolean fullreset);\r\n\tqboolean (*sendhostcachequeries)\t\t\t\t(void);\t//returns true while there are still waiting for servers. should be called each frame while you still care about the servers.\r\n} menu_import_t;\r\n\r\ntypedef struct {\r\n\tint\t\tapi_version;\r\n\r\n\tvoid\t(*Init)\t\t\t\t(mintreason_t reason, float vwidth, float vheight, int pwidth, int pheight);\r\n\tvoid\t(*Shutdown)\t\t\t(mintreason_t reason);\r\n\tvoid\t(*DrawLoading)\t\t(double frametime);\t//pure loading screen.\r\n\tvoid\t(*Toggle)\t\t\t(int wantmode);\r\n\tqboolean(*ConsoleCommand)\t(const char *cmdline, int argc, char const*const*argv);\r\n\r\n\tvoid\t(*Draw)\t\t\t\t(void *ctx, double frametime);\t\t\t\t\t//draws a menu.\r\n\tqboolean(*InputEvent)\t\t(void *ctx, struct menu_inputevent_args_s ev);\t//return true to prevent the engine handling it (ie: because you already did).\r\n\tvoid\t(*Closed)\t\t\t(void *ctx);\t\t\t\t\t\t\t\t\t//a pushed menu was closed.\r\n} menu_export_t;\r\n\r\n#ifndef NATIVEEXPORT\r\n\t#ifdef _WIN32\r\n\t\t#define NATIVEEXPORTPROTO __declspec(dllexport)\r\n\t\t#define NATIVEEXPORT NATIVEEXPORTPROTO\r\n\t#else\r\n\t\t#define NATIVEEXPORTPROTO\r\n\t\t#define NATIVEEXPORT __attribute__((visibility(\"default\")))\r\n\t#endif\r\n#endif\r\n\r\nNATIVEEXPORTPROTO menu_export_t *QDECL GetMenuAPI\t(menu_import_t *import); \r\n"
  },
  {
    "path": "engine/client/bymorphed.h",
    "content": "/* GIMP RGBA C-Source image dump (bymorphed.c) */\n\nstatic const struct {\n  unsigned int \t width;\n  unsigned int \t height;\n  unsigned int \t bytes_per_pixel; /* 3:RGB, 4:RGBA */ \n  unsigned char\t pixel_data[32 * 32 * 4 + 1];\n} icon = {\n  32, 32, 4,\n  \"\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\300\\300\\300\\377\\200\\200\\200\\377\\0\\0\\0\\377\"\n  \"\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\300\\300\\300\\377\\300\\300\\300\\377\\300\\300\\300\"\n  \"\\377\\300\\300\\300\\377\\200\\200\\200\\377\\200\\200\\200\\377\\200\\200\\200\\377\\200\"\n  \"\\200\\200\\377\\200\\200\\200\\377\\200\\200\\200\\377\\200\\200\\200\\377\\300\\300\\300\"\n  \"\\377\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\"\n  \"\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\300\\300\\300\\377\\200\\200\\200\\377\\0\\0\\0\\377\\0\"\n  \"\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\300\\300\\300\\377\\300\\300\\300\\377\\300\\300\\300\"\n  \"\\377\\200\\200\\200\\377\\200\\200\\200\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\"\n  \"\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\200\\200\\200\\377\\200\\200\\200\"\n  \"\\377\\300\\300\\300\\377\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\"\n  \"\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\300\\300\\300\\377\\200\\200\\200\\377\\0\\0\\0\\377\\0\\0\\0\\377\"\n  \"\\300\\300\\300\\377\\300\\300\\300\\377\\300\\300\\300\\377\\300\\300\\300\\377\\200\\200\"\n  \"\\200\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\"\n  \"\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\"\n  \"\\377\\200\\200\\200\\377\\300\\300\\300\\377\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\"\n  \"\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\200\\200\\200\\377\\0\\0\\0\\377\\200\\200\\200\\377\\300\\300\\300\"\n  \"\\377\\300\\300\\300\\377\\300\\300\\300\\377\\200\\200\\200\\377\\0\\0\\0\\377\\0\\0\\0\\377\"\n  \"\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\"\n  \"\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\"\n  \"\\0\\377\\0\\0\\0\\377\\300\\300\\300\\377\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\"\n  \"\\200\\200\\200\\377\\0\\0\\0\\377\\0\\0\\0\\377\\300\\300\\300\\377\\300\\300\\300\\377\\300\"\n  \"\\300\\300\\377\\200\\200\\200\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\"\n  \"\\0\\377\\200\\200\\200\\377\\200\\200\\200\\377\\200\\200\\200\\377\\300\\300\\300\\377\\300\"\n  \"\\300\\300\\377\\200\\200\\200\\377\\200\\200\\200\\377\\200\\200\\200\\377\\0\\0\\0\\377\\0\"\n  \"\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\200\\200\\200\\377\"\n  \"\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\200\\200\\200\\377\\0\\0\\0\\377\\300\\300\\300\\377\"\n  \"\\300\\300\\300\\377\\300\\300\\300\\377\\200\\200\\200\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\"\n  \"\\0\\377\\0\\0\\0\\377\\200\\200\\200\\377\\300\\300\\300\\377\\300\\300\\300\\377\\300\\300\"\n  \"\\300\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\"\n  \"\\300\\300\\300\\377\\300\\300\\300\\377\\300\\300\\300\\377\\200\\200\\200\\377\\0\\0\\0\\377\"\n  \"\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\300\\300\\300\\377\\0\\0\\0\"\n  \"\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\377\\0\\0\\0\\377\\300\\300\\300\\377\\300\\300\\300\\377\\200\"\n  \"\\200\\200\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\200\\200\\200\\377\\300\"\n  \"\\300\\300\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\"\n  \"\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\"\n  \"\\377\\377\\377\\377\\377\\377\\377\\300\\300\\300\\377\\200\\200\\200\\377\\0\\0\\0\\377\\0\"\n  \"\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\300\\300\\300\\377\\0\\0\\0\\0\"\n  \"\\0\\0\\0\\0\\0\\0\\0\\377\\0\\0\\0\\377\\300\\300\\300\\377\\200\\200\\200\\377\\0\\0\\0\\377\\0\"\n  \"\\0\\0\\377\\0\\0\\0\\377\\200\\200\\200\\377\\300\\300\\300\\377\\300\\300\\300\\377\\377\\377\"\n  \"\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\"\n  \"\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\"\n  \"\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\300\\300\\300\\377\\300\\300\\300\\377\"\n  \"\\200\\200\\200\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\200\\200\\200\\377\"\n  \"\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\377\\300\\300\\300\\377\\200\\200\\200\\377\\0\\0\\0\\377\\0\\0\"\n  \"\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\300\\300\\300\\377\\300\\300\\300\\377\\377\\377\\377\\377\"\n  \"\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\"\n  \"\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\"\n  \"\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\300\\300\"\n  \"\\300\\377\\300\\300\\300\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\"\n  \"\\200\\200\\200\\377\\0\\0\\0\\0\\0\\0\\0\\377\\200\\200\\200\\377\\200\\200\\200\\377\\0\\0\\0\"\n  \"\\377\\0\\0\\0\\377\\0\\0\\0\\377\\200\\200\\200\\377\\300\\300\\300\\377\\300\\300\\300\\377\"\n  \"\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\"\n  \"\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\"\n  \"\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\\377\"\n  \"\\377\\377\\300\\300\\300\\377\\300\\300\\300\\377\\200\\200\\200\\377\\0\\0\\0\\377\\0\\0\\0\"\n  \"\\377\\0\\0\\0\\377\\0\\0\\0\\377\\200\\200\\200\\377\\0\\0\\0\\0\\300\\300\\300\\377\\200\\200\"\n  \"\\200\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\200\\200\\200\\377\\300\\300\\300\\377\\300\"\n  \"\\300\\300\\377\\300\\300\\300\\377\\377\\377\\377\\377\\300\\300\\300\\377\\200\\200\\200\"\n  \"\\377\\200\\200\\200\\377\\200\\200\\200\\377\\200\\200\\200\\377\\200\\200\\200\\377\\200\"\n  \"\\200\\200\\377\\200\\200\\200\\377\\200\\200\\200\\377\\200\\200\\200\\377\\200\\200\\200\"\n  \"\\377\\200\\200\\200\\377\\377\\377\\377\\377\\300\\300\\300\\377\\300\\300\\300\\377\\300\"\n  \"\\300\\300\\377\\200\\200\\200\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\300\"\n  \"\\300\\300\\377\\200\\200\\200\\377\\200\\200\\200\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\"\n  \"\\300\\300\\300\\377\\300\\300\\300\\377\\300\\300\\300\\377\\300\\300\\300\\377\\300\\300\"\n  \"\\300\\377\\300\\300\\300\\377\\200\\200\\200\\377\\300\\300\\300\\377\\300\\300\\300\\377\"\n  \"\\300\\300\\300\\377\\300\\300\\300\\377\\200\\200\\200\\377\\300\\300\\300\\377\\300\\300\"\n  \"\\300\\377\\200\\200\\200\\377\\300\\300\\300\\377\\300\\300\\300\\377\\300\\300\\300\\377\"\n  \"\\300\\300\\300\\377\\300\\300\\300\\377\\300\\300\\300\\377\\300\\300\\300\\377\\0\\0\\0\\377\"\n  \"\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\200\\200\\200\\377\\200\\200\\200\\377\\0\\0\\0\\377\"\n  \"\\0\\0\\0\\377\\0\\0\\0\\377\\200\\200\\200\\377\\300\\300\\300\\377\\300\\300\\300\\377\\200\"\n  \"\\200\\200\\377\\300\\300\\300\\377\\300\\300\\300\\377\\300\\300\\300\\377\\200\\200\\200\"\n  \"\\377\\200\\200\\200\\377\\300\\300\\300\\377\\300\\300\\300\\377\\300\\300\\300\\377\\200\"\n  \"\\200\\200\\377\\300\\300\\300\\377\\300\\300\\300\\377\\200\\200\\200\\377\\200\\200\\200\"\n  \"\\377\\300\\300\\300\\377\\300\\300\\300\\377\\300\\300\\300\\377\\200\\200\\200\\377\\300\"\n  \"\\300\\300\\377\\300\\300\\300\\377\\200\\200\\200\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\"\n  \"\\200\\200\\200\\377\\200\\200\\200\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\200\\200\\200\"\n  \"\\377\\300\\300\\300\\377\\300\\300\\300\\377\\200\\200\\200\\377\\300\\300\\300\\377\\300\"\n  \"\\300\\300\\377\\300\\300\\300\\377\\200\\200\\200\\377\\300\\300\\300\\377\\300\\300\\300\"\n  \"\\377\\300\\300\\300\\377\\300\\300\\300\\377\\200\\200\\200\\377\\300\\300\\300\\377\\300\"\n  \"\\300\\300\\377\\200\\200\\200\\377\\300\\300\\300\\377\\300\\300\\300\\377\\300\\300\\300\"\n  \"\\377\\300\\300\\300\\377\\200\\200\\200\\377\\300\\300\\300\\377\\300\\300\\300\\377\\200\"\n  \"\\200\\200\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\200\\200\\200\\377\\0\\0\"\n  \"\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\200\\200\\200\\377\\300\\300\\300\\377\\200\\200\\200\\377\"\n  \"\\200\\200\\200\\377\\300\\300\\300\\377\\300\\300\\300\\377\\300\\300\\300\\377\\200\\200\"\n  \"\\200\\377\\300\\300\\300\\377\\300\\300\\300\\377\\300\\300\\300\\377\\300\\300\\300\\377\"\n  \"\\200\\200\\200\\377\\300\\300\\300\\377\\300\\300\\300\\377\\200\\200\\200\\377\\200\\200\"\n  \"\\200\\377\\200\\200\\200\\377\\300\\300\\300\\377\\300\\300\\300\\377\\200\\200\\200\\377\"\n  \"\\200\\200\\200\\377\\300\\300\\300\\377\\200\\200\\200\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\"\n  \"\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\200\\200\\200\\377\"\n  \"\\300\\300\\300\\377\\200\\200\\200\\377\\200\\200\\200\\377\\300\\300\\300\\377\\300\\300\"\n  \"\\300\\377\\300\\300\\300\\377\\300\\300\\300\\377\\300\\300\\300\\377\\300\\300\\300\\377\"\n  \"\\300\\300\\300\\377\\300\\300\\300\\377\\300\\300\\300\\377\\300\\300\\300\\377\\300\\300\"\n  \"\\300\\377\\300\\300\\300\\377\\300\\300\\300\\377\\300\\300\\300\\377\\300\\300\\300\\377\"\n  \"\\200\\200\\200\\377\\0\\0\\0\\377\\200\\200\\200\\377\\300\\300\\300\\377\\200\\200\\200\\377\"\n  \"\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\"\n  \"\\0\\0\\377\\200\\200\\200\\377\\300\\300\\300\\377\\200\\200\\200\\377\\0\\0\\0\\377\\200\\200\"\n  \"\\200\\377\\300\\300\\300\\377\\300\\300\\300\\377\\300\\300\\300\\377\\300\\300\\300\\377\"\n  \"\\300\\300\\300\\377\\300\\300\\300\\377\\300\\300\\300\\377\\300\\300\\300\\377\\300\\300\"\n  \"\\300\\377\\300\\300\\300\\377\\300\\300\\300\\377\\300\\300\\300\\377\\300\\300\\300\\377\"\n  \"\\300\\300\\300\\377\\200\\200\\200\\377\\0\\0\\0\\377\\200\\200\\200\\377\\300\\300\\300\\377\"\n  \"\\200\\200\\200\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\"\n  \"\\377\\0\\0\\0\\377\\0\\0\\0\\377\\200\\200\\200\\377\\300\\300\\300\\377\\200\\200\\200\\377\"\n  \"\\0\\0\\0\\377\\200\\200\\200\\377\\300\\300\\300\\377\\300\\300\\300\\377\\300\\300\\300\\377\"\n  \"\\300\\300\\300\\377\\300\\300\\300\\377\\300\\300\\300\\377\\300\\300\\300\\377\\300\\300\"\n  \"\\300\\377\\300\\300\\300\\377\\300\\300\\300\\377\\300\\300\\300\\377\\200\\200\\200\\377\"\n  \"\\200\\200\\200\\377\\200\\200\\200\\377\\0\\0\\0\\377\\0\\0\\0\\377\\200\\200\\200\\377\\300\"\n  \"\\300\\300\\377\\200\\200\\200\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\"\n  \"\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\200\\200\\200\\377\\300\\300\\300\\377\\200\"\n  \"\\200\\200\\377\\0\\0\\0\\377\\0\\0\\0\\377\\200\\200\\200\\377\\300\\300\\300\\377\\200\\200\"\n  \"\\200\\377\\200\\200\\200\\377\\200\\200\\200\\377\\200\\200\\200\\377\\200\\200\\200\\377\"\n  \"\\200\\200\\200\\377\\200\\200\\200\\377\\200\\200\\200\\377\\200\\200\\200\\377\\200\\200\"\n  \"\\200\\377\\300\\300\\300\\377\\200\\200\\200\\377\\0\\0\\0\\377\\0\\0\\0\\377\\200\\200\\200\"\n  \"\\377\\300\\300\\300\\377\\200\\200\\200\\377\\200\\200\\200\\377\\0\\0\\0\\377\\0\\0\\0\\377\"\n  \"\\0\\0\\0\\377\\200\\200\\200\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\200\\200\"\n  \"\\200\\377\\200\\200\\200\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\200\\200\\200\\377\\200\"\n  \"\\200\\200\\377\\200\\200\\200\\377\\200\\200\\200\\377\\200\\200\\200\\377\\200\\200\\200\"\n  \"\\377\\200\\200\\200\\377\\200\\200\\200\\377\\200\\200\\200\\377\\200\\200\\200\\377\\200\"\n  \"\\200\\200\\377\\200\\200\\200\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\200\\200\\200\\377\"\n  \"\\200\\200\\200\\377\\200\\200\\200\\377\\200\\200\\200\\377\\0\\0\\0\\377\\0\\0\\0\\377\\200\"\n  \"\\200\\200\\377\\200\\200\\200\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\200\"\n  \"\\200\\200\\377\\200\\200\\200\\377\\200\\200\\200\\377\\0\\0\\0\\377\\0\\0\\0\\377\\200\\200\"\n  \"\\200\\377\\200\\200\\200\\377\\200\\200\\200\\377\\200\\200\\200\\377\\0\\0\\0\\377\\0\\0\\0\"\n  \"\\377\\0\\0\\0\\377\\0\\0\\0\\377\\200\\200\\200\\377\\200\\200\\200\\377\\200\\200\\200\\377\"\n  \"\\200\\200\\200\\377\\0\\0\\0\\377\\0\\0\\0\\377\\200\\200\\200\\377\\200\\200\\200\\377\\200\"\n  \"\\200\\200\\377\\200\\200\\200\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\200\\200\\200\\377\"\n  \"\\300\\300\\300\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\200\\200\\200\\377\"\n  \"\\200\\200\\200\\377\\200\\200\\200\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\200\\200\\200\"\n  \"\\377\\200\\200\\200\\377\\200\\200\\200\\377\\200\\200\\200\\377\\0\\0\\0\\377\\0\\0\\0\\377\"\n  \"\\200\\200\\200\\377\\200\\200\\200\\377\\200\\200\\200\\377\\200\\200\\200\\377\\0\\0\\0\\377\"\n  \"\\0\\0\\0\\377\\0\\0\\0\\377\\200\\200\\200\\377\\200\\200\\200\\377\\200\\200\\200\\377\\200\"\n  \"\\200\\200\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\300\\300\\300\\377\\0\\0\\0\\0\\200\\200\"\n  \"\\200\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\200\\200\\200\\377\\200\\200\"\n  \"\\200\\377\\200\\200\\200\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\200\\200\"\n  \"\\200\\377\\200\\200\\200\\377\\0\\0\\0\\377\\0\\0\\0\\377\\200\\200\\200\\377\\200\\200\\200\"\n  \"\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\200\\200\\200\\377\\200\\200\\200\"\n  \"\\377\\200\\200\\200\\377\\200\\200\\200\\377\\200\\200\\200\\377\\0\\0\\0\\377\\0\\0\\0\\377\"\n  \"\\200\\200\\200\\377\\0\\0\\0\\0\\0\\0\\0\\0\\200\\200\\200\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\"\n  \"\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\200\\200\\200\\377\\200\\200\\200\\377\\200\\200\\200\\377\"\n  \"\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\"\n  \"\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\200\\200\\200\\377\\200\\200\"\n  \"\\200\\377\\200\\200\\200\\377\\200\\200\\200\\377\\200\\200\\200\\377\\0\\0\\0\\377\\0\\0\\0\"\n  \"\\377\\0\\0\\0\\377\\200\\200\\200\\377\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\200\\200\\200\\377\\0\"\n  \"\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\200\\200\\200\\377\\200\\200\"\n  \"\\200\\377\\200\\200\\200\\377\\200\\200\\200\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\"\n  \"\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\200\\200\\200\\377\\200\\200\"\n  \"\\200\\377\\200\\200\\200\\377\\200\\200\\200\\377\\200\\200\\200\\377\\200\\200\\200\\377\"\n  \"\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\200\\200\\200\\377\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\"\n  \"\\0\\0\\0\\300\\300\\300\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\"\n  \"\\0\\0\\0\\377\\200\\200\\200\\377\\200\\200\\200\\377\\200\\200\\200\\377\\200\\200\\200\\377\"\n  \"\\200\\200\\200\\377\\200\\200\\200\\377\\0\\0\\0\\377\\0\\0\\0\\377\\200\\200\\200\\377\\200\"\n  \"\\200\\200\\377\\200\\200\\200\\377\\200\\200\\200\\377\\200\\200\\200\\377\\200\\200\\200\"\n  \"\\377\\300\\300\\300\\377\\200\\200\\200\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\"\n  \"\\377\\300\\300\\300\\377\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\300\\300\\300\"\n  \"\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\200\\200\"\n  \"\\200\\377\\200\\200\\200\\377\\200\\200\\200\\377\\200\\200\\200\\377\\200\\200\\200\\377\"\n  \"\\0\\0\\0\\377\\0\\0\\0\\377\\200\\200\\200\\377\\200\\200\\200\\377\\200\\200\\200\\377\\200\"\n  \"\\200\\200\\377\\200\\200\\200\\377\\300\\300\\300\\377\\200\\200\\200\\377\\0\\0\\0\\377\\0\"\n  \"\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\300\\300\\300\\377\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\"\n  \"\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\200\\200\\200\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\"\n  \"\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\200\\200\\200\\377\\200\\200\\200\\377\\200\\200\\200\"\n  \"\\377\\200\\200\\200\\377\\0\\0\\0\\377\\0\\0\\0\\377\\200\\200\\200\\377\\200\\200\\200\\377\"\n  \"\\200\\200\\200\\377\\300\\300\\300\\377\\200\\200\\200\\377\\200\\200\\200\\377\\0\\0\\0\\377\"\n  \"\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\200\\200\\200\\377\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\"\n  \"\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\300\\300\\300\\377\\0\\0\\0\\377\"\n  \"\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\200\\200\\200\"\n  \"\\377\\200\\200\\200\\377\\200\\200\\200\\377\\200\\200\\200\\377\\200\\200\\200\\377\\200\"\n  \"\\200\\200\\377\\200\\200\\200\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\"\n  \"\\0\\377\\0\\0\\0\\377\\300\\300\\300\\377\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\"\n  \"\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\300\\300\\300\\377\\200\\200\"\n  \"\\200\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\"\n  \"\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\"\n  \"\\377\\0\\0\\0\\377\\0\\0\\0\\377\\200\\200\\200\\377\\300\\300\\300\\377\\0\\0\\0\\0\\0\\0\\0\\0\"\n  \"\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\"\n  \"\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\200\\200\\200\\377\\200\\200\\200\\377\\0\\0\\0\\377\\0\\0\\0\"\n  \"\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\"\n  \"\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\200\\200\\200\\377\\200\\200\\200\\377\\0\\0\\0\\0\\0\"\n  \"\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\"\n  \"\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\300\\300\\300\"\n  \"\\377\\200\\200\\200\\377\\200\\200\\200\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\\377\\0\\0\\0\"\n  \"\\377\\0\\0\\0\\377\\0\\0\\0\\377\\200\\200\\200\\377\\200\\200\\200\\377\\300\\300\\300\\377\"\n  \"\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\"\n  \"\\0\\0\\0\\0\",\n};\n\n"
  },
  {
    "path": "engine/client/cd_linux.c",
    "content": "/*\nCopyright (C) 1996-1997 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  \n\nSee the included (GNU.txt) GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n*/\n// Quake is a trademark of Id Software, Inc., (c) 1996 Id Software, Inc. All\n// rights reserved.\n\n/*\n * All Unix porting was primarily focused on getting this to work on FreeBSD 14+\n * but hopefully will give others the ability to further improve upon it.\n * Most information sourced from the following documentation:\n * Linux CDROM ioctl: https://www.kernel.org/doc/html/latest/userspace-api/ioctl/cdrom.html\n * FreeBSD 14.0 cd man page: https://man.freebsd.org/cgi/man.cgi?query=cd&sektion=4&apropos=0&manpath=FreeBSD+14.0-RELEASE+and+Ports\n * The cdio header file found at /usr/include/sys/cdio.h (very well documented about what everything does)\n * - Brad\n */\n\n#include \"quakedef.h\"\n\n#ifndef HAVE_CDPLAYER\n\t//nothing\n#elif defined(__CYGWIN__)\n#include \"cd_null.c\"\n#else\n\n\n#include <stdio.h>\n#include <unistd.h>\n#include <stdlib.h>\n#include <sys/ioctl.h>\n#include <sys/file.h>\n#include <sys/types.h>\n#include <fcntl.h>\n#include <string.h>\n#include <time.h>\n#include <errno.h>\n\n#if defined(__linux__)\n#include <linux/cdrom.h>\n#elif defined(__unix__) && !defined(__CYGWIN__)\n#include <sys/cdio.h>\n#define CDROMEJECT CDDOEJECT\n#define CDROMCLOSETRAY CDDOCLOSE\n#define CDROMPAUSE CDDOPAUSE\n#define CDROMRESUME CDDORESUME\n#define CDROMRESET CDDORESET\n#define CDROMSTOP CDDOSTOP\n#define CDROMSTART CDDOSTART\n#define CDROMREADTOCHDR CDREADHEADER\n#define CDROMREADTOCENTRY CDIOREADTOCENTRY\n#define CDROMSUBCHNL CDREADSUBQ\n#define CDROM_MSF CD_MSF_FORMAT\n#define CDROM_DATA_TRACK CD_SUBQ_DATA\n#define CDROM_AUDIO_PAUSED CD_AS_PLAY_PAUSED\n#define CDROM_AUDIO_PLAY CD_AS_PLAY_IN_PROGRESS\n#endif\n\nstatic int cdfile = -1;\n#if defined(__unix__) && !defined(__APPLE__) && !defined(__CYGWIN__) && !defined(__linux__)\nstatic char cd_dev[64] = \"/dev/cd0\";\n#else\nstatic char cd_dev[64] = \"/dev/cdrom\";\n#endif\nstatic qboolean playing;\n\nvoid CDAudio_Eject(void)\n{\n\tif (cdfile == -1)\n\t\treturn; // no cd init'd\n\n\tif ( ioctl(cdfile, CDROMEJECT) == -1 ) \n\t\tCon_DPrintf(\"ioctl cdromeject failed\\n\");\n}\n\n\nvoid CDAudio_CloseDoor(void)\n{\n\tif (cdfile == -1)\n\t\treturn; // no cd init'd\n\n\tif ( ioctl(cdfile, CDROMCLOSETRAY) == -1 ) \n\t\tCon_DPrintf(\"ioctl cdromclosetray failed\\n\");\n}\n\nint CDAudio_GetAudioDiskInfo(void)\n{\n#ifdef __linux__\n\tstruct cdrom_tochdr tochdr;\n#elif defined(__unix__) && !defined(__CYGWIN__)\n\tstruct ioc_toc_header tochdr;\n\tstruct cd_sub_channel_track_info trk;\n#endif\n\n\tif (cdfile == -1)\n\t\treturn -1;\n\n\tif ( ioctl(cdfile, CDROMREADTOCHDR, &tochdr) == -1 ) \n\t{\n\t\tCon_DPrintf(\"ioctl cdromreadtochdr failed\\n\");\n\t\treturn -1;\n\t}\n\n#ifdef __linux__\n\tif (tochdr.cdth_trk0 < 1) \n#elif defined(__unix__) && !defined(__CYGWIN__)\n\tif (trk.track_number < 0) // track_number may not work correctly here - Brad\n#endif \n\t{\n\t\tCon_DPrintf(\"CDAudio: no music tracks\\n\");\n\t\treturn -1;\n\t}\n\n#ifdef __linux__\n\treturn tochdr.cdth_trk1;\n#elif defined(__unix__) && !defined(__CYGWIN__)\n\treturn tochdr.starting_track;\n#else\n\tCon_DPrintf(\"CDAudio: no music tracks\\n\");\n\treturn -1;\n#endif\n}\n\n\nvoid CDAudio_Play(int track)\n{\n#ifdef __linux__\n\tstruct cdrom_tocentry entry;\n\tstruct cdrom_ti ti;\n#elif defined(__unix__) && !defined(__CYGWIN__) // This all may be wrong, but it should be close - Brad\n\tstruct ioc_toc_header tochdr; // cd drive header info (for getting start and end track info mostly)\n\tstruct ioc_read_toc_single_entry entry; // individual audio track's entry info\n\tstruct cd_sub_channel_info ti; // individual audio track's subchannel info (for indexing and whatnot)\n\tstruct ioc_play_track play; // cd drive audio indexing\n#endif\n\n\tif (cdfile == -1)\n\t\treturn;\n\n\t// don't try to play a non-audio track\n#ifdef __linux__\n\tentry.cdte_track = track;\n\tentry.cdte_format = CDROM_MSF;\n#elif defined(__unix__) && !defined(__CYGWIN__)\n\tentry.track = track;\n\tentry.address_format = CDROM_MSF;\n#endif\n\n\tif ( ioctl(cdfile, CDROMREADTOCENTRY, &entry) == -1 )\n\t{\n\t\tCon_DPrintf(\"ioctl cdromreadtocentry failed\\n\");\n\t\treturn;\n\t}\n#ifdef __linux__\n\tif (entry.cdte_ctrl == CDROM_DATA_TRACK)\n#elif defined(__unix__) && !defined(__CYGWIN__)\n\tif (entry.entry.control == CDROM_DATA_TRACK)\n#endif\n\t{\n\t\tCon_Printf(\"CDAudio: track %i is not audio\\n\", track);\n\t\treturn;\n\t}\n\n#ifdef __linux__\n\tti.cdti_trk0 = track;\n\tti.cdti_trk1 = track;\n\tti.cdti_ind0 = 1;\n\tti.cdti_ind1 = 99;\n#elif defined(__unix__) && !defined(__CYGWIN__)\n\tif (ti.what.position.track_number == 0 || ti.what.position.track_number == 1)\n\t{\n\t\tentry.track = track;\n\t}\n\tif (ti.what.position.index_number == 0)\n\t{\n\t\tplay.start_track = tochdr.starting_track;\n\t}\n\tif (ti.what.position.index_number == 1)\n\t{\n\t\tplay.end_track = tochdr.ending_track;\n\t}\n\n#define CDROMPLAYTRKIND ti.what.position.track_number\n#endif\n\n\tif ( ioctl(cdfile, CDROMPLAYTRKIND, &ti) == -1 ) \n\t{\n\t\tCon_DPrintf(\"ioctl cdromplaytrkind failed\\n\");\n\t\treturn;\n\t}\n\n\tif ( ioctl(cdfile, CDROMRESUME) == -1 ) \n\t\tCon_DPrintf(\"ioctl cdromresume failed\\n\");\n\n\tplaying = true;\n\n\tif (!bgmvolume.value || !mastervolume.value)\n\t\tCDAudio_Pause ();\n}\n\n\nvoid CDAudio_Stop(void)\n{\n\tif (cdfile == -1)\n\t\treturn;\n\n\tif ( ioctl(cdfile, CDROMSTOP) == -1 )\n\t\tCon_DPrintf(\"ioctl cdromstop failed (%d)\\n\", errno);\n}\n\nvoid CDAudio_Pause(void)\n{\n\tif (cdfile == -1)\n\t\treturn;\n\n\tif ( ioctl(cdfile, CDROMPAUSE) == -1 ) \n\t\tCon_DPrintf(\"ioctl cdrompause failed\\n\");\n}\n\n\nvoid CDAudio_Resume(void)\n{\n\tif (cdfile == -1)\n\t\treturn;\n\t\n\tif ( ioctl(cdfile, CDROMRESUME) == -1 ) \n\t\tCon_DPrintf(\"ioctl cdromresume failed\\n\");\n}\n\nvoid CDAudio_Update(void)\n{\n#ifdef __linux__\n\tstruct cdrom_subchnl subchnl;\n\tstatic time_t lastchk;\n#elif defined(__unix__) && !defined(__CYGWIN__)\n\tstruct cd_sub_channel_info subchnl;\n\tstruct ioc_read_subchannel cdsc; // subchn.cdsc_format workaround - Brad\n\t// Note: there doesn't seem to be a way to check how much time is left for playing\n\t// cd audio without doing some extra manual work, so I'll be omitting it from\n\t// the unix checks for the time being - Brad\n#endif\n\n\tif (playing \n#ifdef __linux__\n\t\t&& lastchk < time(NULL)\n#endif\n\t   )\n\t{\n#ifdef __linux__\n\t\tlastchk = time(NULL) + 2; //two seconds between checks\n\t\tsubchnl.cdsc_format = CDROM_MSF;\n#elif defined(__unix__) && !defined(__CYGWIN__)\n\t\tcdsc.address_format = CDROM_MSF;\n#endif\n\t\tif (ioctl(cdfile, CDROMSUBCHNL, &subchnl) == -1 )\n\t\t{\n\t\t\tCon_DPrintf(\"ioctl cdromsubchnl failed\\n\");\n\t\t\tplaying = false;\n\t\t\treturn;\n\t\t}\n#ifdef __linux__\n\t\tif (subchnl.cdsc_audiostatus != CDROM_AUDIO_PLAY &&\n\t\t\tsubchnl.cdsc_audiostatus != CDROM_AUDIO_PAUSED)\n#elif defined(__unix__) && !defined(__CYGWIN__)\n\t\tif (subchnl.header.audio_status != CDROM_AUDIO_PLAY &&\n\t\t\tsubchnl.header.audio_status != CDROM_AUDIO_PAUSED)\n#endif\n\t\t{\n\t\t\tplaying = false;\n\t\t\tMedia_EndedTrack();\n\t\t}\n\t}\n}\n\nqboolean CDAudio_Startup(void)\n{\n\tint i;\n\n\tif (cdfile != -1)\n\t\treturn true;\n\n\tif ((i = COM_CheckParm(\"-cddev\")) != 0 && i < com_argc - 1)\n\t{\n\t\tQ_strncpyz(cd_dev, com_argv[i + 1], sizeof(cd_dev));\n\t\tcd_dev[sizeof(cd_dev) - 1] = 0;\n\t}\n\n\tif ((cdfile = open(cd_dev, O_RDONLY|O_NONBLOCK)) == -1)\n\t{\n\t\tCon_Printf(\"CDAudio_Init: open of \\\"%s\\\" failed (%i)\\n\", cd_dev, errno);\n\t\tcdfile = -1;\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\nvoid CDAudio_Init(void)\n{\n}\n\n\nvoid CDAudio_Shutdown(void)\n{\n\tif (cdfile == -1)\n\t\treturn;\n\tCDAudio_Stop();\n\tclose(cdfile);\n\tcdfile = -1;\n}\n#endif\n"
  },
  {
    "path": "engine/client/cd_null.c",
    "content": "#include \"quakedef.h\"\n\n#ifdef HAVE_CDPLAYER\n\n#ifdef _WIN32\n//not really needed, but nice none-the-less.\n#include \"winquake.h\"\nLONG CDAudio_MessageHandler(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)\n{\n\treturn 1;\n}\n#endif\n\nvoid CDAudio_Shutdown(void)\n{\n}\n\nvoid CDAudio_Update(void)\n{\n}\n\nvoid CDAudio_Init(void)\n{\n}\n\nvoid CDAudio_Play(int track)\n{\n}\n\nvoid CDAudio_Stop(void)\n{\n}\n\nvoid CDAudio_Pause(void)\n{\n}\n\nvoid CDAudio_Resume(void)\n{\n}\n\nvoid CDAudio_Eject(void)\n{\n}\n\nvoid CDAudio_CloseDoor(void)\n{\n}\n\nint CDAudio_GetAudioDiskInfo(void)\n{\n\treturn -2;\n}\n\nqboolean CDAudio_Startup(void)\n{\n\treturn false;\n}\n#endif\n"
  },
  {
    "path": "engine/client/cd_sdl.c",
    "content": "#include \"quakedef.h\"\n\n#ifdef FTE_SDL3\n#include <SDL3/SDL.h>\n#else\n#include <SDL.h>\n#endif\n\n#ifndef HAVE_CDPLAYER\n\t//nothing\n#elif SDL_MAJOR_VERSION >= 2\n//sdl2 has no cd support. sod off.\n#include \"cd_null.c\"\n#else\n\nextern\tcvar_t\tbgmvolume;\n\nstatic qboolean\tinitialized = false;\n\nstatic SDL_CD\t*cddevice;\n\n\nvoid CDAudio_Eject(void)\n{\n\tif (SDL_CDEject(cddevice))\n\t\tCon_DPrintf(\"SDL_CDEject failed\\n\");\n}\n\n\nvoid CDAudio_CloseDoor(void)\n{\n\tCon_Printf(\"SDL does not support this\\n\");\n}\n\n\nint CDAudio_GetAudioDiskInfo(void)\n{\n\tswitch (SDL_CDStatus(cddevice))\n\t{\n\tcase CD_ERROR:\n\t\tCon_Printf(\"SDL_CDStatus returned error\\n\");\n\t\treturn -1;\n\tcase CD_TRAYEMPTY:\n\t\treturn 0;\n\tdefault:\n\t\tbreak;\n\t}\n\treturn cddevice->numtracks;\n}\n\n\nvoid CDAudio_Play(int track)\n{\n\tif (SDL_CDPlayTracks(cddevice, track, 0, 1, 0))\n\t{\n\t\tCon_Printf(\"CDAudio: track %i is not audio\\n\", track);\n\t\treturn;\n\t}\n\n\tif (!bgmvolume.value)\n\t\tCDAudio_Pause ();\n\n\treturn;\n}\n\n\nvoid CDAudio_Stop(void)\n{\n\tif (SDL_CDStop(cddevice))\n\t\tCon_DPrintf(\"CDAudio: SDL_CDStop failed\");\n}\n\n\nvoid CDAudio_Pause(void)\n{\n\tif (SDL_CDPause(cddevice))\n\t\tCon_DPrintf(\"CDAudio: SDL_CDPause failed\");\n}\n\n\nvoid CDAudio_Resume(void)\n{\n\tif (SDL_CDResume(cddevice))\n\t{\n\t\tCon_DPrintf(\"CDAudio: SDL_CDResume failed\\n\");\n\t\treturn;\n\t}\n}\n\nvoid CDAudio_Update(void)\n{\n}\n\n\nvoid CDAudio_Init(void)\n{\n}\n\nqboolean CDAudio_Startup(void)\n{\n\tif (initialized)\n\t\treturn !!cddevice;\n\tif (!bgmvolume.value)\n\t\treturn false;\n\tinitialized = true;\n\n\tSDL_InitSubSystem(SDL_INIT_CDROM|SDL_INIT_NOPARACHUTE);\n\n\tif(!SDL_CDNumDrives())\n\t{\n\t\tCon_DPrintf(\"CDAudio_Init: No CD drives\\n\");\n\t\treturn false;\n\t}\n\n\tcddevice = SDL_CDOpen(0);\n\tif (!cddevice)\n\t{\n\t\tCon_Printf(\"CDAudio_Init: SDL_CDOpen failed\\n\");\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\nvoid CDAudio_Shutdown(void)\n{\n\tif (!initialized)\n\t\treturn;\n\tCDAudio_Stop();\n\n\tSDL_CDClose(cddevice);\n\tcddevice = NULL;\n\tinitialized = false;\n}\n#endif\n"
  },
  {
    "path": "engine/client/cd_win.c",
    "content": "/*\nCopyright (C) 1996-1997 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\nSee the included (GNU.txt) GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n*/\n// Quake is a trademark of Id Software, Inc., (c) 1996 Id Software, Inc. All\n// rights reserved.\n\n#include \"quakedef.h\"\n#include \"winquake.h\"\n\n#ifndef HAVE_CDPLAYER\n\t//nothing\n#elif defined(WINRT)\n#include \"cd_null.c\"\n#else\n\n#if defined(_MSC_VER) && (_MSC_VER < 1300)\n#define DWORD_PTR DWORD\n#endif\n\nextern\tHWND\tmainwindow;\nextern\tcvar_t\tbgmvolume;\n\nstatic qboolean\tinitialized;\nstatic qboolean\tinitializefailed;\nstatic DWORD resumeend;\nstatic qboolean\tpollneeded;\t//workaround for windows vista/7 bug, where notification simply does not work for end of tracks.\n\nstatic UINT\twDeviceID;\n\nint CDAudio_GetAudioDiskInfo(void)\n{\n\tDWORD\t\t\t\tdwReturn;\n\tstatic MCI_STATUS_PARMS\tmciStatusParms;\n\n\tif (!CDAudio_Startup())\n\t\treturn -1;\n\n\tif (!initialized)\n\t\treturn -1;\n\n\tmciStatusParms.dwItem = MCI_STATUS_READY;\n\tmciStatusParms.dwCallback = (DWORD_PTR)mainwindow;\n\tdwReturn = mciSendCommand(wDeviceID, MCI_STATUS, MCI_STATUS_ITEM | MCI_WAIT, (DWORD_PTR) (LPVOID) &mciStatusParms);\n\tif (dwReturn)\n\t{\n\t\tCon_DPrintf(\"CDAudio: drive ready test - get status failed\\n\");\n\t\treturn -1;\n\t}\n\tif (!mciStatusParms.dwReturn)\n\t{\n\t\tCon_DPrintf(\"CDAudio: drive not ready\\n\");\n\t\treturn -1;\n\t}\n\n\tmciStatusParms.dwItem = MCI_STATUS_NUMBER_OF_TRACKS;\n\tmciStatusParms.dwCallback = (DWORD_PTR)mainwindow;\n\tdwReturn = mciSendCommand(wDeviceID, MCI_STATUS, MCI_STATUS_ITEM | MCI_WAIT, (DWORD_PTR) (LPVOID) &mciStatusParms);\n\tif (dwReturn)\n\t{\n\t\tCon_DPrintf(\"CDAudio: get tracks - status failed\\n\");\n\t\treturn -1;\n\t}\n\tif (mciStatusParms.dwReturn < 1)\n\t{\n\t\tCon_DPrintf(\"CDAudio: no music tracks\\n\");\n\t\treturn -1;\n\t}\n\n\treturn mciStatusParms.dwReturn;\n}\nqboolean CDAudio_Startup(void)\n{\n\tDWORD\tdwReturn;\n\tstatic MCI_OPEN_PARMSA\tmciOpenParms;\n\tstatic MCI_SET_PARMS\tmciSetParms;\n\n\tif (initializefailed)\n\t\treturn false;\n\n\tif (initialized)\n\t\treturn true;\n\n\tmciOpenParms.lpstrDeviceType = \"cdaudio\";\n\tmciOpenParms.dwCallback = (DWORD_PTR)mainwindow;\n\tdwReturn = mciSendCommand(0, MCI_OPEN, MCI_OPEN_TYPE | MCI_OPEN_SHAREABLE, (DWORD_PTR) (LPVOID) &mciOpenParms);\n\tif (dwReturn)\n\t{\n\t\tCon_Printf(\"CDAudio_Init: MCI_OPEN failed (%i)\\n\", (int)dwReturn);\n\t\tinitializefailed = true;\n\t\treturn 0;\n\t}\n\twDeviceID = mciOpenParms.wDeviceID;\n\n\t// Set the time format to frames. vista+ simply cannot come with converting to/from seconds, or something (notifies don't work, status stays playing, position stops updating at about 3 frames from the end of the track).\n\tmciSetParms.dwTimeFormat = MCI_FORMAT_MSF;\n\tmciSetParms.dwCallback = (DWORD_PTR)mainwindow;\n\tdwReturn = mciSendCommand(wDeviceID, MCI_SET, MCI_SET_TIME_FORMAT, (DWORD_PTR)(LPVOID) &mciSetParms);\n\tif (dwReturn)\n\t{\n\t\tCon_Printf(\"MCI_SET_TIME_FORMAT failed (%i)\\n\", (int)dwReturn);\n\t\tmciSendCommand(wDeviceID, MCI_CLOSE, 0, (DWORD_PTR)NULL);\n\t\tinitializefailed = true;\n\t\treturn 0;\n\t}\n\n\tinitialized = true;\n\n\tif (CDAudio_GetAudioDiskInfo() <= 0)\n\t{\n\t\tCon_Printf(\"CDAudio_Init: No CD in player.\\n\");\n\t}\n\n\treturn true;\n}\nvoid CDAudio_Shutdown(void)\n{\n\tif (initialized)\n\t{\n\t\tCDAudio_Stop();\n\t\tif (mciSendCommand(wDeviceID, MCI_CLOSE, MCI_WAIT, (DWORD_PTR)NULL))\n\t\t\tCon_DPrintf(\"CDAudio_Shutdown: MCI_CLOSE failed\\n\");\n\t}\n\n\tinitialized = false;\n}\n\nvoid CDAudio_Eject(void)\n{\n\tDWORD\tdwReturn;\n\n\tdwReturn = mciSendCommand(wDeviceID, MCI_SET, MCI_SET_DOOR_OPEN, (DWORD_PTR)NULL);\n\tif (dwReturn)\n\t\tCon_DPrintf(\"MCI_SET_DOOR_OPEN failed (%i)\\n\", (int)dwReturn);\n}\n\nvoid CDAudio_CloseDoor(void)\n{\n\tDWORD\tdwReturn;\n\n\tdwReturn = mciSendCommand(wDeviceID, MCI_SET, MCI_SET_DOOR_CLOSED, (DWORD_PTR)NULL);\n\tif (dwReturn)\n\t\tCon_DPrintf(\"MCI_SET_DOOR_CLOSED failed (%i)\\n\", (int)dwReturn);\n}\n\n//try to add time values sensibly because:\n//a) microsoft api SUCKS and does not directly support frames.\n//b) microsoft buggily stops 3 frames short of the end of the track if we use tmsf...\n//c) frames added together will break things\n//d) we can subtract an offset so we can actually detect when its reached the end of a track\n//e) we need to do frames so we don't break if some track is exactly a multiple of a second long\nDWORD MSFToFrames(DWORD base)\n{\n\tint m = MCI_MSF_MINUTE(base);\n\tint s = MCI_MSF_SECOND(base);\n\tint f = MCI_MSF_FRAME(base);\n\n\ts += m*60;\n\tf += 75*s;\t//75 frames per second.\n\treturn f;\n}\nDWORD FramesToMSF(DWORD in)\n{\n\tDWORD m, s, f;\n\tf = in % 75;\n\tin /= 75;\n\ts = in % 60;\n\tin /= 60;\n\tm = in;\n\n\treturn MCI_MAKE_MSF(m, s, f);\n}\n\nvoid CDAudio_Play(int track)\n{\n\tDWORD\t\t\t\tdwReturn;\n\tstatic MCI_PLAY_PARMS\t\tmciPlayParms;\n\tstatic MCI_STATUS_PARMS\tmciStatusParms;\n\tDWORD trackstartposition;\n\n\tif (track < 1)\n\t{\n\t\tCon_DPrintf(\"CDAudio: Bad track number %u.\\n\", track);\n\t\treturn;\n\t}\n\n\t// don't try to play a non-audio track\n\tmciStatusParms.dwItem = MCI_CDA_STATUS_TYPE_TRACK;\n\tmciStatusParms.dwTrack = track;\n\tmciStatusParms.dwCallback = (DWORD_PTR)mainwindow;\n\tdwReturn = mciSendCommand(wDeviceID, MCI_STATUS, MCI_STATUS_ITEM | MCI_TRACK | MCI_WAIT, (DWORD_PTR) (LPVOID) &mciStatusParms);\n\tif (dwReturn)\n\t{\n\t\tCon_DPrintf(\"MCI_STATUS failed (%i)\\n\", (int)dwReturn);\n\t\treturn;\n\t}\n\tif (mciStatusParms.dwReturn != MCI_CDA_TRACK_AUDIO)\n\t{\n\t\tCon_Printf(\"CDAudio: track %i is not audio\\n\", track);\n\t\treturn;\n\t}\n\n\t// get the start of the track to be played\n\tmciStatusParms.dwItem = MCI_STATUS_POSITION;\n\tmciStatusParms.dwTrack = track;\n\tmciStatusParms.dwCallback = (DWORD_PTR)mainwindow;\n\tdwReturn = mciSendCommand(wDeviceID, MCI_STATUS, MCI_STATUS_ITEM | MCI_TRACK | MCI_WAIT, (DWORD_PTR) (LPVOID) &mciStatusParms);\n\tif (dwReturn)\n\t{\n\t\tCon_DPrintf(\"MCI_STATUS failed (%i)\\n\", (int)dwReturn);\n\t\treturn;\n\t}\n\ttrackstartposition = mciStatusParms.dwReturn;\n\n\t// get the length of the track to be played\n\tmciStatusParms.dwItem = MCI_STATUS_LENGTH;\n\tmciStatusParms.dwTrack = track;\n\tmciStatusParms.dwCallback = (DWORD_PTR)mainwindow;\n\tdwReturn = mciSendCommand(wDeviceID, MCI_STATUS, MCI_STATUS_ITEM | MCI_TRACK | MCI_WAIT, (DWORD_PTR) (LPVOID) &mciStatusParms);\n\tif (dwReturn)\n\t{\n\t\tCon_DPrintf(\"MCI_STATUS failed (%i)\\n\", (int)dwReturn);\n\t\treturn;\n\t}\n\n\t//set up to play from start to start+length\n\tmciPlayParms.dwFrom = trackstartposition;\n\tmciPlayParms.dwTo = resumeend = FramesToMSF(MSFToFrames(trackstartposition) + MSFToFrames(mciStatusParms.dwReturn) - 8);\t//-8 to avoid microsoft's potential fuck ups\n\tmciPlayParms.dwCallback = (DWORD_PTR)mainwindow;\n\tdwReturn = mciSendCommand(wDeviceID, MCI_PLAY, MCI_NOTIFY | MCI_FROM | MCI_TO, (DWORD_PTR)(LPVOID) &mciPlayParms);\n\tif (dwReturn)\n\t{\n\t\tCon_DPrintf(\"CDAudio: MCI_PLAY failed (%i)\\n\", (int)dwReturn);\n\t\treturn;\n\t}\n\n\tpollneeded = true;\n\n\tif (!bgmvolume.value)\n\t\tCDAudio_Pause ();\n\n\treturn;\n}\n\n\nvoid CDAudio_Stop(void)\n{\n\tDWORD\tdwReturn;\n\n\tpollneeded = false;\n\n\tdwReturn = mciSendCommand(wDeviceID, MCI_STOP, 0, (DWORD_PTR)NULL);\n\tif (dwReturn)\n\t\tCon_DPrintf(\"MCI_STOP failed (%i)\\n\", (int)dwReturn);\n}\n\n\nvoid CDAudio_Pause(void)\n{\n\tDWORD\t\t\t\tdwReturn;\n\tstatic MCI_GENERIC_PARMS\tmciGenericParms;\n\n\tif (!pollneeded)\n\t\treturn;\n\n\tmciGenericParms.dwCallback = (DWORD_PTR)mainwindow;\n\tdwReturn = mciSendCommand(wDeviceID, MCI_PAUSE, 0, (DWORD_PTR)(LPVOID) &mciGenericParms);\n\tif (dwReturn)\n\t\tCon_DPrintf(\"MCI_PAUSE failed (%i)\\n\", (int)dwReturn);\n\n\tpollneeded = false;\n}\n\n\nvoid CDAudio_Resume(void)\n{\n\tDWORD\t\t\tdwReturn;\n\tstatic MCI_PLAY_PARMS\tmciPlayParms;\n\n\tif (!bgmvolume.value)\n\t\treturn;\n\n\tmciPlayParms.dwFrom = resumeend;\n\tmciPlayParms.dwTo = resumeend;\n\tmciPlayParms.dwCallback = (DWORD_PTR)mainwindow;\n\tdwReturn = mciSendCommand(wDeviceID, MCI_PLAY, MCI_TO | MCI_NOTIFY, (DWORD_PTR)(LPVOID) &mciPlayParms);\n\tif (dwReturn)\n\t{\n\t\tCon_DPrintf(\"CDAudio: MCI_PLAY failed (%i)\\n\", (int)dwReturn);\n\t\treturn;\n\t}\n\n\tpollneeded = true;\n}\n\nLONG CDAudio_MessageHandler(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)\n{\n\tif (lParam != wDeviceID)\n\t\treturn 1;\n\n\tswitch (wParam)\n\t{\n\t\tcase MCI_NOTIFY_SUCCESSFUL:\n\t\t\tpollneeded = false;\n\t\t\tMedia_EndedTrack();\n\t\t\tbreak;\n\n\t\tcase MCI_NOTIFY_ABORTED:\n\t\tcase MCI_NOTIFY_SUPERSEDED:\n\t\t\tbreak;\n\n\t\tcase MCI_NOTIFY_FAILURE:\n\t\t\tCon_DPrintf(\"MCI_NOTIFY_FAILURE\\n\");\n\t\t\tCDAudio_Stop ();\n\t\t\tCDAudio_Shutdown();\n//\t\t\tMedia_EndedTrack();\n\t\t\tbreak;\n\n\t\tdefault:\n\t\t\tCon_DPrintf(\"Unexpected MM_MCINOTIFY type (%i)\\n\", (int)wParam);\n\t\t\treturn 1;\n\t}\n\n\treturn 0;\n}\n\nvoid CDAudio_Update(void)\n{\n\t//workaround for vista bug where MCI_NOTIFY does not work to signal the end of the track.\n\tif (pollneeded)\n\t{\n\t\tMCI_STATUS_PARMS mciStatusParms;\n\t\tmciStatusParms.dwCallback = (DWORD_PTR)mainwindow;\n\t\tmciStatusParms.dwItem = MCI_STATUS_POSITION;\n\t\tmciStatusParms.dwReturn = resumeend;\n\t\tmciStatusParms.dwTrack = 0;\n\t\tif (0 == mciSendCommand(wDeviceID, MCI_STATUS, MCI_WAIT|MCI_STATUS_ITEM, (DWORD_PTR)&mciStatusParms))\n\t\t{\n\t\t\tunsigned int c, f;\n\t\t\tint cm = MCI_MSF_MINUTE(mciStatusParms.dwReturn);\n\t\t\tint cs = MCI_MSF_SECOND(mciStatusParms.dwReturn);\n\t\t\tint cf = MCI_MSF_FRAME(mciStatusParms.dwReturn);\n\t\t\tint fm = MCI_MSF_MINUTE(resumeend);\n\t\t\tint fs = MCI_MSF_SECOND(resumeend);\n\t\t\tint ff = MCI_MSF_FRAME(resumeend);\n\t\t\tc = cf | (cs<<8) | (cm<<16);\n\t\t\tf = ff | (fs<<8) | (fm<<16);\n\t\t\tif (c >= f)\n\t\t\t{\n\t\t\t\tpollneeded = false;\n\t\t\t\tMedia_EndedTrack();\n\t\t\t}\n\t\t}\n\t}\n}\n\nvoid CDAudio_Init(void)\n{\n}\n#endif\n"
  },
  {
    "path": "engine/client/cdaudio.h",
    "content": "/*\nCopyright (C) 1996-1997 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  \n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n*/\n\n#ifdef HAVE_CDPLAYER\nvoid CDAudio_Init(void);\nqboolean CDAudio_Startup(void);\t//called when the cd isn't currently valid. returns if its valid or not.\nint CDAudio_GetAudioDiskInfo(void);//returns number of tracks available, or 0 if the cd is not valid.\nvoid CDAudio_Play(int track);\nvoid CDAudio_Stop(void);\nvoid CDAudio_Pause(void);\nvoid CDAudio_Resume(void);\nvoid CDAudio_Eject(void);\nvoid CDAudio_CloseDoor(void);\nvoid CDAudio_Shutdown(void);\nvoid CDAudio_Update(void);\n#else\n#define CDAudio_Update()\n#define CDAudio_Init()\n#define CDAudio_Shutdown()\n#define CDAudio_Pause()\n#define CDAudio_Resume()\n#endif\n\n"
  },
  {
    "path": "engine/client/cl_cam.c",
    "content": "/*\nCopyright (C) 1996-1997 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  \n\nSee the included (GNU.txt) GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n*/\n/* ZOID\n *\n * Player camera tracking in Spectator mode\n *\n * This takes over player controls for spectator automatic camera.\n * Player moves as a spectator, but the camera tracks and enemy player\n */\n\n#include \"quakedef.h\"\n#include \"winquake.h\"\n\n#define\tPM_SPECTATORMAXSPEED\t500\n#define\tPM_STOPSPEED\t100\n#define\tPM_MAXSPEED\t\t\t320\n#define BUTTON_JUMP 2\n#define BUTTON_ATTACK 1\n#define MAX_ANGLE_TURN 10\n\nchar cl_spectatorgroup[] = \"Spectator Tracking\";\n\nenum\n{\n\tTM_USER,\t\t\t//user hit jump and changed pov explicitly\n\tTM_HIGHTRACK,\t\t//tracking the player with the highest frags\n\tTM_KILLER,\t\t\t//switch to the previous person's killer.\n\tTM_MODHINTS,\t\t//using the mod's //at hints\n\tTM_STATS\t\t\t//parsing mvd stats and making our own choices\n} autotrackmode;\nchar *autotrack_statsrule;\nstatic void QDECL CL_AutoTrackChanged(cvar_t *v, char *oldval)\n{\n\tCam_AutoTrack_Update(v->string);\n}\n// track high fragger\ncvar_t cl_autotrack_team\t= CVARD(\"cl_autotrack_team\", \"\", \"Specifies a team name that should be auto-tracked (players on other teams will not be candidates for autotracking). Accepts * and ? wildcards for awkward chars.\");\ncvar_t cl_autotrack\t\t\t= CVARCD(\"cl_autotrack\", \"auto\", CL_AutoTrackChanged, \"Specifies the default tracking mode at the start of the map. Use the 'autotrack' command to reset/apply an auto-tracking mode without changing the default.\\nValid values are: high, ^hkiller^h, mod, user. Other values are treated as weighting scripts for mvd playback, where available.\");\ncvar_t cl_hightrack\t\t\t= CVARD(\"cl_hightrack\", \"0\", \"Obsolete. If you want hightrack, use '[cl_]autotrack high' instead.\");\n\n//cvar_t cl_camera_maxpitch = {\"cl_camera_maxpitch\", \"10\" };\n//cvar_t cl_camera_maxyaw = {\"cl_camera_maxyaw\", \"30\" };\ncvar_t cl_chasecam\t\t\t= CVAR(\"cl_chasecam\", \"1\");\ncvar_t cl_selfcam\t\t\t= CVAR(\"cl_selfcam\", \"1\");\n\nvoid Cam_AutoTrack_Update(const char *mode)\n{\n\tif (!mode)\n\t\tmode = cl_autotrack.string;\n\tZ_Free(autotrack_statsrule);\n\tautotrack_statsrule = NULL;\n\tif (!*mode || !Q_strcasecmp(mode, \"auto\"))\n\t{\n\t\tif (cls.demoplayback == DPB_MVD)\n\t\t{\n\t\t\tautotrackmode = TM_STATS;\n\t\t\tautotrack_statsrule = Z_StrDup(\"\");\t//default\n\t\t}\n\t\telse if (cl_hightrack.ival)\n\t\t\tautotrackmode = TM_HIGHTRACK;\n\t\telse\n\t\t\tautotrackmode = TM_MODHINTS;\n\t}\n\telse if (!Q_strcasecmp(mode, \"high\"))\n\t\tautotrackmode = TM_HIGHTRACK;\n\telse if (!Q_strcasecmp(mode, \"killer\"))\n\t\tautotrackmode = TM_KILLER;\n\telse if (!Q_strcasecmp(mode, \"mod\"))\n\t\tautotrackmode = TM_MODHINTS;\n\telse if (!Q_strcasecmp(mode, \"user\") || !Q_strcasecmp(mode, \"off\"))\n\t\tautotrackmode = TM_USER;\n\telse if (!Q_strcasecmp(mode, \"stats\"))\n\t{\n\t\tautotrackmode = TM_STATS;\n\t\tautotrack_statsrule = NULL;\n\t}\n\telse\n\t{\n\t\tautotrackmode = TM_STATS;\n\t\tautotrack_statsrule = Z_StrDup(mode);\n\t}\n}\nstatic void Cam_AutoTrack_f(void)\n{\n\tif (*Cmd_Argv(1))\n\t\tCam_AutoTrack_Update(Cmd_Argv(1));\n\telse\n\t\tCam_AutoTrack_Update(NULL);\n}\n\nstatic float CL_TrackScoreProp(player_info_t *pl, char rule, float *weights)\n{\n#ifdef QUAKESTATS\n\tfloat r;\n#endif\n\tswitch(rule)\n\t{\n\tcase '.':\t//currently being tracked. combine with currently alive or something\n\t\t{\n\t\t\tint i;\n\t\t\tfor (i = 0; i < cl.splitclients; i++)\n\t\t\t{\n\t\t\t\t//not valid if an earlier view is tracking it.\n\t\t\t\tif (Cam_TrackNum(&cl.playerview[i]) == pl-cl.players)\n\t\t\t\t\treturn 1;\n\t\t\t}\n\t\t\treturn 0;\n\t\t}\n#ifdef QUAKESTATS\n\tcase 'a':\t//armour value\n\t\treturn pl->statsf[STAT_ARMOR];\n\tcase 'h':\n\t\treturn pl->statsf[STAT_HEALTH];\n\tcase 'A':\t//armour type\n\t\tif\t\t(pl->stats[STAT_ITEMS] & IT_ARMOR3)\tr = weights[10];\n\t\telse if (pl->stats[STAT_ITEMS] & IT_ARMOR2)\tr = weights[9];\n\t\telse if (pl->stats[STAT_ITEMS] & IT_ARMOR1)\tr = weights[8];\n\t\telse\t\t\t\t\t\t\t\t\t\tr = 0;\n\t\treturn r;\n\tcase 'W':\t//best weapon\n\t\tr = 0;\n\t\tif (r < weights[0] && (pl->stats[STAT_ITEMS] & IT_AXE)) r = weights[0];\n\t\tif (r < weights[1] && (pl->stats[STAT_ITEMS] & IT_SHOTGUN)) r = weights[1];\n\t\tif (r < weights[2] && (pl->stats[STAT_ITEMS] & IT_SUPER_SHOTGUN)) r = weights[2];\n\t\tif (r < weights[3] && (pl->stats[STAT_ITEMS] & IT_NAILGUN)) r = weights[3];\n\t\tif (r < weights[4] && (pl->stats[STAT_ITEMS] & IT_SUPER_NAILGUN)) r = weights[4];\n\t\tif (r < weights[5] && (pl->stats[STAT_ITEMS] & IT_GRENADE_LAUNCHER)) r = weights[5];\n\t\tif (r < weights[6] && (pl->stats[STAT_ITEMS] & IT_ROCKET_LAUNCHER)) r = weights[6];\n\t\tif (r < weights[7] && (pl->stats[STAT_ITEMS] & IT_LIGHTNING)) r = weights[7];\n\t\treturn r;\n\tcase 'p':\t//powerups held\n\t\tr = 0;\n\t\tr += (pl->stats[STAT_ITEMS] & IT_INVISIBILITY)?weights[11]:0;\n\t\tr += (pl->stats[STAT_ITEMS] & IT_QUAD)?weights[12]:0;\n\t\tr += (pl->stats[STAT_ITEMS] & IT_INVULNERABILITY)?weights[13]:0;\n\t\treturn r;\n#endif\n\tcase 'f':\t//frags\n\t\treturn pl->frags;\n//\tcase 'F':\t//team frags\n//\t\treturn 0;\n#ifdef QUAKEHUD\n\tcase 'g':\t//deaths\n\t\treturn Stats_GetDeaths(pl - cl.players);\n#endif\n\tcase 'u':\t//userid\n\t\treturn pl - cl.players;\n\tcase 'c':\t//'current run time'\n\tcase 'C':\t//'current run frags'\n\tcase 'd':\t//'current run teamfrags'\n\tcase 'I':\t//ssg grabs\n\tcase 'j':\t//ng grabs\n\tcase 'J':\t//sng grabs\n\tcase 'k':\t//gl grabs\n\tcase 'K':\t//gl lost\n\tcase 'l':\t//rl grabs\n\tcase 'L':\t//rl lost\n\tcase 'm':\t//lg grabs\n\tcase 'M':\t//lg lost\n\tcase 'n':\t//mh grabs\n\tcase 'N':\t//ga grabs\n\tcase 'o':\t//ya grabs\n\tcase 'O':\t//ra grabs\n\tcase 'v':\t//average run time\n\tcase 'V':\t//average run frags\n\tcase 'w':\t//average run teamfrags\n\tcase 'x':\t//ring grabs\n\tcase 'X':\t//ring losts\n\tcase 'y':\t//quad grabs\n\tcase 'Y':\t//quad losts\n\tcase 'z':\t//pent grabs\n\tdefault:\n\t\treturn 0;\n\t}\n}\n#define PRI_TOP 3\n#define PRI_LOGIC 3\n#define PRI_ADD 2\n#define PRI_MUL 1\n#define PRI_VAL 0\nstatic float CL_TrackScore(player_info_t *pl, char **rule, float *weights, int pri)\n{\n\tfloat l, r;\n\tchar *s = *rule;\n\twhile (*s == ' ' || *s == '\\t')\n\t\ts++;\n\tif (!pri)\n\t{\n\t\tif (*s == '!')\n\t\t{\n\t\t\ts++;\n\t\t\tl = !CL_TrackScore(pl, &s, weights, pri);\n\t\t}\n\t\tif (*s == '(')\n\t\t{\n\t\t\tl = CL_TrackScore(pl, &s, weights, PRI_TOP);\n\t\t\twhile (*s == ' ' || *s == '\\t')\n\t\t\t\ts++;\n\t\t\tif (*s == ')')\n\t\t\t\ts++;\n\t\t}\n\t\telse if (*s == '%')\n\t\t{\n\t\t\tl = CL_TrackScoreProp(pl, *++s, weights);\n\t\t\ts++;\n\t\t}\n\t\telse if (*s == '#')\n\t\t{\n\t\t\tint i = strtoul(s+1, &s, 0);\n\t\t\tif (i >= 0 && i < MAX_CL_STATS)\n\t\t\t\tl = pl->statsf[i];\n\t\t\telse\n\t\t\t\tl = 0;\n\t\t}\n\t\telse\n\t\t\tl = strtod(s, &s);\n\t\twhile (*s == ' ' || *s == '\\t')\n\t\t\ts++;\n\t}\n\telse\n\t\tl = CL_TrackScore(pl, &s, weights, pri-1);\n\n\tfor (;;)\n\t{\n\t\tif (pri == PRI_MUL)\n\t\t{\n\t\t\tif (*s == '*')\n\t\t\t{\n\t\t\t\ts++;\n\t\t\t\tr = CL_TrackScore(pl, &s, weights, pri-1);\n\t\t\t\tl *= r;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\telse if (*s == '/')\n\t\t\t{\n\t\t\t\ts++;\n\t\t\t\tr = CL_TrackScore(pl, &s, weights, pri-1);\n\t\t\t\tl /= r;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\t\telse if (pri == PRI_ADD)\n\t\t{\n\t\t\tif (*s == '+')\n\t\t\t{\n\t\t\t\ts++;\n\t\t\t\tr = CL_TrackScore(pl, &s, weights, pri-1);\n\t\t\t\tl += r;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\telse if (*s == '-')\n\t\t\t{\n\t\t\t\ts++;\n\t\t\t\tr = CL_TrackScore(pl, &s, weights, pri-1);\n\t\t\t\tl -= r;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\t\telse if (pri == PRI_LOGIC)\n\t\t{\n\t\t\tif (*s == '|' && s[1] == '|')\n\t\t\t{\n\t\t\t\ts+=2;\n\t\t\t\tr = CL_TrackScore(pl, &s, weights, pri-1);\n\t\t\t\tl = l||r;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\telse if (*s == '&' && s[1] == '&')\n\t\t\t{\n\t\t\t\ts+=2;\n\t\t\t\tr = CL_TrackScore(pl, &s, weights, pri-1);\n\t\t\t\tl = l&&r;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\telse if (*s == '&')\n\t\t\t{\n\t\t\t\ts++;\n\t\t\t\tr = CL_TrackScore(pl, &s, weights, pri-1);\n\t\t\t\tl = (int)l&(int)r;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\telse if (*s == '|')\n\t\t\t{\n\t\t\t\ts++;\n\t\t\t\tr = CL_TrackScore(pl, &s, weights, pri-1);\n\t\t\t\tl = (int)l|(int)r;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\telse if (*s == '>' && s[1] == '=')\n\t\t\t{\n\t\t\t\ts+=2;\n\t\t\t\tr = CL_TrackScore(pl, &s, weights, pri-1);\n\t\t\t\tl = l>=r;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\telse if (*s == '<' && s[1] == '=')\n\t\t\t{\n\t\t\t\ts+=2;\n\t\t\t\tr = CL_TrackScore(pl, &s, weights, pri-1);\n\t\t\t\tl = l<=r;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\telse if (*s == '>')\n\t\t\t{\n\t\t\t\ts+=1;\n\t\t\t\tr = CL_TrackScore(pl, &s, weights, pri-1);\n\t\t\t\tl = l>r;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\telse if (*s == '<')\n\t\t\t{\n\t\t\t\ts+=1;\n\t\t\t\tr = CL_TrackScore(pl, &s, weights, pri-1);\n\t\t\t\tl = l>r;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\telse if (*s == '!' && s[1] == '=')\n\t\t\t{\n\t\t\t\ts+=2;\n\t\t\t\tr = CL_TrackScore(pl, &s, weights, pri-1);\n\t\t\t\tl = l!=r;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\telse if (*s == '=' && s[1] == '=')\n\t\t\t{\n\t\t\t\ts+=2;\n\t\t\t\tr = CL_TrackScore(pl, &s, weights, pri-1);\n\t\t\t\tl = l!=r;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\t\tbreak;\n\t}\n\t*rule = s;\n\treturn l;\n}\nstatic qboolean CL_MayAutoTrack(int seat, int player)\n{\n\tif (player < 0)\n\t\treturn false;\n\tif (!cl.players[player].userid || !cl.players[player].name[0] || cl.players[player].spectator)\n\t\treturn false;\n\tif (*cl_autotrack_team.string && !wildcmp(cl_autotrack_team.string, cl.players[player].team))\n\t\treturn false;\n\tfor (seat--; seat >= 0; seat--)\n\t{\n\t\t//not valid if an earlier view is tracking it.\n\t\tif (Cam_TrackNum(&cl.playerview[seat]) == player)\n\t\t\treturn false;\n\t}\n\treturn true;\n}\n//returns the player with the highest frags\nstatic int CL_FindHighTrack(int seat, char *rule)\n{\n\tint j = -1;\n\tint i;\n\tfloat max, score;\n\tplayer_info_t *s;\n\tfloat weights[14];\n\tchar *p;\n\tqboolean instant;\n\n\tinstant = (rule && *rule == '!');\n\tif (instant)\n\t\trule++;\n\n\tif (rule && *rule == '[')\n\t{\n\t\trule++;\n\t\tweights[0] = strtod(rule, &rule);\t//axe\n\t\tweights[1] = strtod(rule, &rule);\t//shot\n\t\tweights[2] = strtod(rule, &rule);\t//sshot\n\t\tweights[3] = strtod(rule, &rule);\t//nail\n\t\tweights[4] = strtod(rule, &rule);\t//snail\n\t\tweights[5] = strtod(rule, &rule);\t//gren\n\t\tweights[6] = strtod(rule, &rule);\t//rock\n\t\tweights[7] = strtod(rule, &rule);\t//lg\n\t\tweights[8] = strtod(rule, &rule);\t//ga\n\t\tweights[9] = strtod(rule, &rule);\t//ya\n\t\tweights[10] = strtod(rule, &rule);\t//ra\n\t\tweights[11] = strtod(rule, &rule);\t//ring\n\t\tweights[12] = strtod(rule, &rule);\t//quad\n\t\tweights[13] = strtod(rule, &rule);\t//pent\n\t\tif (*rule == ']')\n\t\t\trule++;\n\t}\n\telse\n\t{\n\t\tweights[0] = 1;\t//axe\n\t\tweights[1] = 2;\t//shot\n\t\tweights[2] = 3;\t//sshot\n\t\tweights[3] = 2;\t//nail\n\t\tweights[4] = 3;\t//snail\n\t\tweights[5] = 3;\t//gren\n\t\tweights[6] = 8;\t//rock\n\t\tweights[7] = 8;\t//lg\n\t\tweights[8] = 1;\t//ga\n\t\tweights[9] = 2;\t//ya\n\t\tweights[10] = 3;\t//ra\n\t\tweights[11] = 500;\t//ring\n\t\tweights[12] = 900;\t//quad\n\t\tweights[13] = 1000;\t//pent\n\t}\n\n\tif (!rule || !*rule)\n\t\trule = \"%a * %A + 50 * %W + %p + %f\";\n\n\t//set a default to the currently tracked player, to reuse the current player we're tracking if someone lower equalises.\n\tj = cl.playerview[seat].cam_spec_track;\n\tif (CL_MayAutoTrack(seat, j))\n\t{\n\t\tp = rule;\n\t\tmax = CL_TrackScore(&cl.players[j], &p, weights, PRI_TOP);\n\t}\n\telse\n\t{\n\t\tmax = -9999;\n\t\tj = -1;\n\t}\n\n\tfor (i = 0; i < cl.allocated_client_slots; i++)\n\t{\n\t\ts = &cl.players[i];\n\t\tif (j == i)\t//this was our default.\n\t\t\tcontinue;\n\t\tif (!CL_MayAutoTrack(seat, i))\n\t\t\tcontinue;\n\t\tif (cl.teamplay && seat && cl.playerview[0].cam_spec_track >= 0 && strcmp(cl.players[cl.playerview[0].cam_spec_track].team, s->team))\n\t\t\tcontinue;\t//when using multiview, keep tracking the team\n\t\tp = rule;\n\t\tscore = CL_TrackScore(s, &p, weights, PRI_TOP);\n\t\tif (score > max)\n\t\t{\n\t\t\tmax = score;\n\t\t\tj = i;\n\t\t}\n\t}\n\tif (j == -1 && cl.teamplay && seat)\n\t{\n\t\t//do it again, but with the teamplay check inverted\n\t\t//fixme: should probably reduce seat count instead, at least in demos.\n\t\tfor (i = 0; i < cl.allocated_client_slots; i++)\n\t\t{\n\t\t\ts = &cl.players[i];\n\t\t\tif (j == i)\t//this was our default.\n\t\t\t\tcontinue;\n\t\t\tif (!CL_MayAutoTrack(seat, i))\n\t\t\t\tcontinue;\n\t\t\tif (!(cl.teamplay && seat && cl.playerview[0].cam_spec_track >= 0 && strcmp(cl.players[cl.playerview[0].cam_spec_track].team, s->team)))\n\t\t\t\tcontinue;\t//when using multiview, keep tracking the team\n\t\t\tp = rule;\n\t\t\tscore = CL_TrackScore(s, &p, weights, PRI_TOP);\n\t\t\tif (score > max)\n\t\t\t{\n\t\t\t\tmax = score;\n\t\t\t\tj = i;\n\t\t\t}\n\t\t}\n\t}\n\n\tif (j != -1 && CL_MayAutoTrack(seat, cl.playerview[seat].cam_spec_track) && !instant)\n\t{\n\t\ti = cl.playerview[seat].cam_spec_track;\n\t\t//extra hacks to prevent 'random' switching mid-game\n#ifdef QUAKESTATS\n\t\tif ((cl.players[j].stats[STAT_ITEMS] ^ cl.players[i].stats[STAT_ITEMS]) & (IT_INVULNERABILITY|IT_QUAD))\n\t\t\t;\t//don't block if the players have different powerups\n\t\telse if ((cl.players[j].stats[STAT_ITEMS] & (IT_ROCKET_LAUNCHER|IT_LIGHTNING)) && !(cl.players[i].stats[STAT_ITEMS] & (IT_ROCKET_LAUNCHER|IT_LIGHTNING)))\n\t\t\t;\t//don't block the switch if the new player has a decent weapon, and the guy we're tracking does not.\n\t\telse if ((cl.players[j].stats[STAT_ITEMS] & (IT_ROCKET_LAUNCHER|IT_INVULNERABILITY))==(IT_ROCKET_LAUNCHER|IT_INVULNERABILITY) && (cl.players[i].stats[STAT_ITEMS] & (IT_ROCKET_LAUNCHER|IT_INVULNERABILITY)) != (IT_ROCKET_LAUNCHER|IT_INVULNERABILITY))\n\t\t\t;\t//don't block if we're switching to someone with pent+rl from someone that does not.\n\t\telse\n#endif\n\t\t\treturn cl.playerview[seat].cam_spec_track;\n\t}\n\treturn j;\n}\n\nstatic int CL_AutoTrack_Choose(int seat)\n{\n\tint best = -1;\n\tif (autotrackmode == TM_KILLER)\n\t\tbest = cl.autotrack_killer;\n\tif (autotrackmode == TM_MODHINTS && seat == 0 && cl.autotrack_hint >= 0)\n\t\tbest = cl.autotrack_hint;\n\tif (autotrackmode == TM_STATS && cls.demoplayback == DPB_MVD)\n\t\tbest = CL_FindHighTrack(seat, autotrack_statsrule);\n\tif (autotrackmode == TM_HIGHTRACK || best == -1)\n\t\tbest = CL_FindHighTrack(seat, \"%f\");\n\t//TM_USER should generally avoid autotracking\n\tcl.autotrack_killer = best;\t//killer should continue to track whatever is currently tracked until its changed by frag message parsing\n\treturn best;\n}\n\nstatic int Cam_FindSortedPlayer(int number);\n\nint selfcam=1;\n\nstatic float vlen(vec3_t v)\n{\n\treturn sqrt(v[0]*v[0] + v[1]*v[1] + v[2]*v[2]);\n}\n\n// returns true if weapon model should be drawn in camera mode\nqboolean Cam_DrawViewModel(playerview_t *pv)\n{\n\tif (pv->spectator)\n\t{\n\t\tif (pv->cam_state == CAM_EYECAM && cl_chasecam.ival)\n\t\t\treturn true;\n\t\treturn false;\n\t}\n\telse\n\t{\n\t\tif (selfcam == 1 && !r_refdef.externalview)\n\t\t\treturn true;\n\t\treturn false;\n\t}\n}\n\nint Cam_TrackNum(playerview_t *pv)\n{\n\tif (pv->spectator && CAM_ISLOCKED(pv))\n\t\treturn pv->cam_spec_track;\n\treturn -1;\n}\n\nvoid Cam_Unlock(playerview_t *pv)\n{\n\tif (pv->cam_state)\n\t{\n\t\tCL_SendSeatClientCommand(true, pv-cl.playerview, \"ptrack\");\n\t\tpv->cam_state = CAM_FREECAM;\n\t\tpv->viewentity = (cls.demoplayback)?0:(pv->playernum+1);\t//free floating\n\t\tSCR_CenterPrint(pv-cl.playerview, NULL, true);\n\t\tSbar_Changed();\n\n\t\t//flashgrens suck\n\t\tpv->cshifts[CSHIFT_SERVER].percent = 0;\n\n\t\tSkin_FlushPlayers();\n\t}\n}\n\nvoid Cam_Lock(playerview_t *pv, int playernum)\n{\n\tpv->cam_lastviewtime = -1000;\t//allow the wallcam to re-snap as soon as it can\n\n\tCL_SendSeatClientCommand(true, pv-cl.playerview, \"ptrack %i\", playernum);\n\n\tif (pv->cam_spec_track != playernum)\n\t{\t//flashgrens suck\n\t\tpv->cshifts[CSHIFT_SERVER].percent = 0;\n\t}\n\tpv->cam_spec_track = playernum;\n\tpv->cam_state = CAM_PENDING;\n\tpv->viewentity = (cls.demoplayback)?0:(pv->playernum+1);\t//free floating until actually locked\n\tSCR_CenterPrint(pv-cl.playerview, NULL, true);\n\n\t\n\tSkin_FlushPlayers();\n\n\tif (cls.demoplayback == DPB_MVD)\n\t{\n\t\tmemcpy(&pv->stats, cl.players[playernum].stats, sizeof(pv->stats));\n//\t\tpv->cam_state = CAM_;\n\t\n//\t\tpv->viewentity = playernum+1;\n\t\t/*\n\t\tpv->cam_state = cl_chasecam.ival?CAM_EYECAM:CAM_PENDING;\t//instantly lock if the player is valid.\n\t\tpv->viewentity = playernum+1;\n\t\t*/\n\n#ifdef QUAKESTATS\n\t\tif (cls.z_ext & Z_EXT_VIEWHEIGHT)\n\t\t\tpv->viewheight = cl.players[playernum].statsf[STAT_VIEWHEIGHT];\n#endif\n\t}\n\n\tSbar_Changed();\n\n#ifdef QWSKINS\n\t{\n\t\tint i;\n\t\tfor (i = 0; i < cl.allocated_client_slots; i++)\n\t\t\tCL_NewTranslation(i);\n\t}\n#endif\n}\n\ntrace_t Cam_DoTrace(vec3_t vec1, vec3_t vec2)\n{\n#if 0\n\tmemset(&pmove, 0, sizeof(pmove));\n\n\tpmove.numphysent = 1;\n\tmemset(&pmove.physents[0], 0, sizeof(physent_t));\n\tVectorClear (pmove.physents[0].origin);\n\tpmove.physents[0].model = cl.worldmodel;\n#endif\n\n\tVectorCopy (vec1, pmove.origin);\n\treturn PM_PlayerTrace(pmove.origin, vec2, MASK_PLAYERSOLID);\n}\n\t\n// Returns distance or 9999 if invalid for some reason\nstatic float Cam_TryFlyby(vec3_t selforigin, vec3_t playerorigin, vec3_t vec, qboolean checkvis)\n{\n\tvec3_t v;\n\ttrace_t trace;\n\tfloat len;\n\n\tpmove.player_mins[0] = pmove.player_mins[1] = -16;\n\tpmove.player_mins[2] = -24;\n\tpmove.player_maxs[0] = pmove.player_maxs[1] = 16;\n\tpmove.player_maxs[2] = 32;\n\n\tVectorAngles(vec, NULL, v, true);\n\tVectorCopy (v, pmove.angles);\n\tVectorNormalize(vec);\n\tVectorMA(playerorigin, 800, vec, v);\n\t// v is endpos\n\t// fake a player move\n\ttrace = Cam_DoTrace(playerorigin, v);\n\tif (/*trace.inopen ||*/ trace.inwater)\n\t\treturn 9999;\n\tVectorCopy(trace.endpos, vec);\n\tVectorSubtract(trace.endpos, playerorigin, v);\n\tlen = sqrt(v[0]*v[0] + v[1]*v[1] + v[2]*v[2]);\n\tif (len < 32 || len > 800)\n\t\treturn 9999;\n\tif (checkvis)\n\t{\n\t\tVectorSubtract(trace.endpos, selforigin, v);\n\t\tlen = sqrt(v[0]*v[0] + v[1]*v[1] + v[2]*v[2]);\n\n\t\ttrace = Cam_DoTrace(selforigin, vec);\n\t\tif (trace.fraction != 1 || trace.inwater)\n\t\t\treturn 9999;\n\t}\n\treturn len;\n}\n\n// Is player visible?\nstatic qboolean Cam_IsVisible(vec3_t playerorigin, vec3_t vec)\n{\n\ttrace_t trace;\n\tvec3_t v;\n\tfloat d;\n\n\tVectorClear(pmove.player_mins);\n\tVectorClear(pmove.player_maxs);\n\n\ttrace = Cam_DoTrace(playerorigin, vec);\n\tif (trace.fraction != 1 || /*trace.inopen ||*/ trace.inwater)\n\t\treturn false;\n\t// check distance, don't let the player get too far away or too close\n\tVectorSubtract(playerorigin, vec, v);\n\td = vlen(v);\n\tif (d < 16)\n\t\treturn false;\n\treturn true;\n}\n\nstatic qboolean InitFlyby(playerview_t *pv, vec3_t selforigin, vec3_t playerorigin, vec3_t playerviewangles, int checkvis) \n{\n\tvec3_t dirs[] = {\n\t\t{1,1,1},\n\t\t{1,-1,1},\n\t\t{1,1,0},\n\t\t{1,-1,1},\n\t\t{1,0,1},\n\t\t{1,0,-1},\n\t\t{-1,1,1},\n\t\t{-1,-1,1},\n\t\t{-1,0,0},\n\t\t{1,0,0},\n\t\t{0,0,-1},\n\t\t{0,0,1}\n\t\t};\n\tint dir;\n\tfloat f, max;\n\tvec3_t vec, vec2;\n\tvec3_t forward, right, up;\n\n\tVectorCopy(playerviewangles, vec);\n\tvec[0] = 0;\n\tAngleVectors (vec, forward, right, up);\n//\tfor (i = 0; i < 3; i++)\n//\t\tforward[i] *= 3;\n\n\tmax = 1000;\n\tfor (dir = 0; dir < sizeof(dirs)/sizeof(dirs[0]); dir++)\n\t{\n\t\tVectorScale(forward, dirs[dir][0], vec2);\n\t\tVectorMA(vec2, dirs[dir][1], right, vec2);\n\t\tVectorMA(vec2, dirs[dir][2], up, vec2);\n\t\tif ((f = Cam_TryFlyby(selforigin, playerorigin, vec2, checkvis)) < max)\n\t\t{\n\t\t\tmax = f;\n\t\t\tVectorCopy(vec2, vec);\n\t\t}\n\t}\n\t// ack, can't find him\n\tif (max >= 1000)\n\t{\n//\t\tCam_Unlock();\n\t\treturn false;\n\t}\n\n\tpv->cam_state = CAM_WALLCAM;\n\tpv->viewentity = pv->playernum+1;//pv->cam_spec_track+1;\n\tVectorCopy(vec, pv->cam_desired_position); \n\treturn true;\n}\n\nstatic void Cam_CheckHighTarget(playerview_t *pv)\n{\n\tint j;\n\tplayerview_t *spv;\n\n\tj = CL_AutoTrack_Choose(pv - cl.playerview);\n\tif (j >= 0)\n\t{\n\t\tif (pv->cam_spec_track != j || pv->cam_state == CAM_FREECAM)\n\t\t{\n#ifdef QUAKEHUD\n\t\t\tif (cl.teamplay)\n\t\t\t\tStats_Message(\"Now tracking:\\n%s\\n%s\", cl.players[j].name, cl.players[j].team);\n\t\t\telse\n\t\t\t\tStats_Message(\"Now tracking:\\n%s\", cl.players[j].name);\n#endif\n\t\t\tCam_Lock(pv, j);\n\t\t\t//un-lock any higher seats watching our new target. this keeps things ordered.\n\t\t\tfor (spv = pv+1; spv >= cl.playerview && spv < &cl.playerview[cl.splitclients]; spv++)\n\t\t\t{\n\t\t\t\tif (Cam_TrackNum(spv) == j)\n\t\t\t\t\tspv->cam_state = CAM_FREECAM;\n\t\t\t}\n\t\t}\n\t}\n\telse\n\t\tCam_Unlock(pv);\n}\n\nvoid Cam_SelfTrack(playerview_t *pv)\n{\n\tvec3_t vec;\n\tif (!cl.worldmodel || cl.worldmodel->loadstate != MLS_LOADED)\n\t\treturn;\n\n\tif (selfcam == 1)\n\t{\t//view-from-eyes\n\t}\n\telse\n\t{\n\t\tif (selfcam == 2)\n\t\t{\t//fixme:\n\t\t\tvec3_t forward, right, up;\n\t\t\ttrace_t tr;\n\t\t\tAngleVectors(r_refdef.viewangles, forward, right, up);\n\t\t\tVectorMA(pv->simorg, -128, forward, pv->cam_desired_position);\n\t\t\ttr = Cam_DoTrace(pv->simorg, pv->cam_desired_position);\n\t\t\tVectorCopy(tr.endpos, pv->cam_desired_position);\n\t\t}\n\t\telse\n\t\t{\t//view from a random wall\n\t\t\tif (pv->cam_state != CAM_WALLCAM || !Cam_IsVisible(pv->simorg, pv->cam_desired_position))\n\t\t\t{\n\t\t\t\tif (pv->cam_state != CAM_WALLCAM || realtime - pv->cam_lastviewtime > 0.1)\n\t\t\t\t{\n\t\t\t\t\tif (!InitFlyby(pv, pv->cam_desired_position, pv->simorg, pv->simangles, true))\n\t\t\t\t\t\tInitFlyby(pv, pv->cam_desired_position, pv->simorg, pv->simangles, false);\n\t\t\t\t\tpv->cam_lastviewtime = realtime;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tpv->cam_lastviewtime = realtime;\n\t\t\t}\n\n\t\t\t//tracking failed.\n\t\t\tif (pv->cam_state != CAM_WALLCAM)\n\t\t\t\treturn;\n\t\t}\n\n\n\t\t// move there locally immediately\n\t\tVectorCopy(pv->cam_desired_position, r_refdef.vieworg);\n\n\t\tVectorSubtract(pv->simorg, pv->cam_desired_position, vec);\n\t\tVectorAngles(vec, NULL, r_refdef.viewangles, false);\n\t}\n}\n\n//player entity became visible, lock on to them (now that we know where they are etc)\nvoid Cam_NowLocked(playerview_t *pv)\n{\n\tpv->cam_lastviewtime = realtime;\n\tif (!cl_chasecam.ival)\n\t{\n\t\tif (pv->cam_state != CAM_WALLCAM || !Cam_IsVisible(pv->simorg, pv->cam_desired_position))\n\t\t{\n\t\t\tif (pv->cam_state != CAM_WALLCAM || realtime - pv->cam_lastviewtime > 0.1)\n\t\t\t{\n\t\t\t\tif (!InitFlyby(pv, pv->cam_desired_position, pv->simorg, pv->simangles, true))\n\t\t\t\t\tInitFlyby(pv, pv->cam_desired_position, pv->simorg, pv->simangles, false);\n\t\t\t}\n\t\t}\n\t}\n}\n\n// ZOID\n//\n// Take over the user controls and track a player.\n// We find a nice position to watch the player and move there\nvoid Cam_Track(playerview_t *pv, usercmd_t *cmd)\n{\n\tplayer_state_t *player, *self;\n\tinframe_t *frame;\n\tvec3_t vec;\n\tfloat len;\n\n\tif (!pv->spectator || !cl.worldmodel)\t//can happen when the server changes level\n\t\treturn;\n\t\n\tif (autotrackmode != TM_USER && pv->cam_state == CAM_FREECAM)\n\t\tCam_CheckHighTarget(pv);\n\n\tif (pv->cam_state == CAM_FREECAM || cls.state != ca_active || cl.worldmodel->loadstate != MLS_LOADED)\n\t\treturn;\n\n\tif (CAM_ISLOCKED(pv) && (!cl.players[pv->cam_spec_track].name[0] || cl.players[pv->cam_spec_track].spectator))\n\t{\n\t\tpv->cam_state = CAM_FREECAM;\n\t\tif (autotrackmode != TM_USER)\n\t\t\tCam_CheckHighTarget(pv);\n\t\telse\n\t\t\tCam_Unlock(pv);\n\t\treturn;\n\t}\n\n\tframe = &cl.inframes[cl.validsequence & UPDATE_MASK];\n\tplayer = frame->playerstate + pv->cam_spec_track;\n\tif (!cmd)\n\t{\t//this is our fancy force-wallcam mode.\n\t\tif (pv->cam_state != CAM_WALLCAM || !Cam_IsVisible(player->origin, pv->cam_desired_position))\n\t\t{\n\t\t\tif (!InitFlyby(pv, pv->cam_desired_position, player->origin, player->viewangles, true))\n\t\t\t\tInitFlyby(pv, pv->cam_desired_position, player->origin, player->viewangles, false);\n\t\t}\n\t\treturn;\n\t}\n\tself = frame->playerstate + pv->playernum;\n\n\tif (!cl_chasecam.value && (pv->cam_state != CAM_WALLCAM || !Cam_IsVisible(player->origin, pv->cam_desired_position)))\n\t{\n\t\tif (pv->cam_state != CAM_WALLCAM || realtime - pv->cam_lastviewtime > 0.1)\n\t\t{\n\t\t\tif (!InitFlyby(pv, self->origin, player->origin, player->viewangles, true))\n\t\t\t\tInitFlyby(pv, self->origin, player->origin, player->viewangles, false);\n\t\t\tpv->cam_lastviewtime = realtime;\n\t\t}\n\t}\n\telse if (cl_chasecam.value && pv->cam_state == CAM_WALLCAM)\n\t\tpv->cam_state = CAM_PENDING;\n\telse\n\t{\n\t\tpv->cam_lastviewtime = realtime;\n\t}\n\n\t//tracking failed.\n\tif (pv->cam_state == CAM_FREECAM || pv->cam_state == CAM_PENDING)\n\t\treturn;\n\n\n\tif (cl_chasecam.value)\n\t{\n\t\tfloat *neworg;\n//\t\tfloat *newang;\n\t\tif (pv->nolocalplayer)\n\t\t\tneworg = cl.lerpents[pv->viewentity].origin;\n\t\telse\n\t\t\tneworg = player->origin;\n\n\t\tpv->cam_lastviewtime = realtime;\n\n//\t\tVectorCopy(newang, pv->viewangles);\n\t\tif (memcmp(neworg, &self->origin, sizeof(vec3_t)) != 0)\n\t\t{\n\t\t\tif (!cls.demoplayback)\n\t\t\t{\n\t\t\t\tMSG_WriteByte (&cls.netchan.message, clc_tmove);\n\t\t\t\tMSG_WriteCoord (&cls.netchan.message, neworg[0]);\n\t\t\t\tMSG_WriteCoord (&cls.netchan.message, neworg[1]);\n\t\t\t\tMSG_WriteCoord (&cls.netchan.message, neworg[2]);\n\t\t\t}\n\t\t\t// move there locally immediately\n\t\t\tVectorCopy(neworg, self->origin);\n\t\t}\n\t\tself->weaponframe = player->weaponframe;\n\n\t\tVectorCopy(player->viewangles, pv->viewangles);\n\t\treturn;\n\t}\n\t\n\t// Ok, move to our desired position and set our angles to view\n\t// the player\n\tVectorSubtract(pv->cam_desired_position, self->origin, vec);\n\tlen = vlen(vec);\n\tcmd->forwardmove = cmd->sidemove = cmd->upmove = 0;\n\tif (len > 16)\n\t{ // close enough?\n\t\tMSG_WriteByte (&cls.netchan.message, clc_tmove);\n\t\tMSG_WriteCoord (&cls.netchan.message, pv->cam_desired_position[0]);\n\t\tMSG_WriteCoord (&cls.netchan.message, pv->cam_desired_position[1]);\n\t\tMSG_WriteCoord (&cls.netchan.message, pv->cam_desired_position[2]);\n\t}\n\n\t// move there locally immediately\n\tVectorCopy(pv->cam_desired_position, self->origin);\n\n//\tVectorSubtract(player->origin, pv->cam_desired_position, vec);\n//\tVectorAngles(vec, NULL, pv->viewangles);\n//\tpv->viewangles[0] = -pv->viewangles[0];\n}\n\nvoid Cam_SetModAutoTrack(int userid)\n{\t//this is a hint from the server about who to track\n\tint slot;\n\tcl.autotrack_hint = -1;\n\tfor (slot = 0; slot < cl.allocated_client_slots; slot++)\n\t{\n\t\tif (cl.players[slot].userid == userid)\n\t\t{\n\t\t\tcl.autotrack_hint = slot;\n\t\t\treturn;\n\t\t}\n\t}\n\tCon_Printf(\"//at: invalid userid %i\\n\", userid);\n}\n\n/*static void Cam_TrackCrosshairedPlayer(playerview_t *pv)\n{\n\tinframe_t *frame;\n\tplayer_state_t *player;\n\tint i;\n\tfloat dot = 0.1, bestdot=0;\n\tint best = -1;\n\tvec3_t selforg;\n\tvec3_t dir;\n\n\tframe = &cl.inframes[cl.validsequence & UPDATE_MASK];\n\tplayer = frame->playerstate + pv->playernum;\n\tVectorCopy(player->origin, selforg);\n\n\tfor (i = 0; i < cl.allocated_client_slots; i++)\n\t{\n\t\tplayer = frame->playerstate + i;\n\t\tVectorSubtract(player->origin, selforg, dir);\n\t\tVectorNormalize(dir);\n\t\tdot = DotProduct(vpn, dir);\n\t\tif (dot > bestdot)\n\t\t{\n\t\t\tbestdot = dot;\n\t\t\tbest = i;\n\t\t}\n\t}\n//\tCon_Printf(\"Track %i? %f\\n\", best, bestdot);\n\tif (best > .707)\t//did we actually get someone?\n\t{\n\t\tCam_Lock(pv, best);\n\t}\n}*/\n\nvoid Cam_FinishMove(playerview_t *pv, usercmd_t *cmd)\n{\n\tint i;\n\tplayer_info_t\t*s;\n\tint end;\n\textern cvar_t cl_demospeed;\n\n\tif (cls.state != ca_active)\n\t\treturn;\n\n\tif (!pv->spectator && cls.demoplayback != DPB_MVD) // only in spectator mode\n\t\treturn;\n\n\tif (cls.demoplayback == DPB_MVD)\n\t{\n\t\tint nb = 0;\n\t\tnb |= (cmd->sidemove<0)?4:0;\n\t\tnb |= (cmd->sidemove>0)?8:0;\n\t\tnb |= (cmd->forwardmove<0)?16:0;\n\t\tnb |= (cmd->forwardmove>0)?32:0;\n\t\tnb |= (cmd->upmove<0)?64:0;\n\t\tnb |= (cmd->upmove>0)?128:0;\n\t\tif (Cam_TrackNum(pv) >= 0)\n\t\t{\n\t\t\tif (*cls.lastdemoname)\n\t\t\t{\t//changing rates or jumping doesn't make sense with mvds.\n\t\t\t\tif (nb & (nb ^ pv->cam_oldbuttons) & 4)\n\t\t\t\t\tCvar_SetValue(&cl_demospeed, max(cl_demospeed.value - 0.1, 0));\n\t\t\t\tif (nb & (nb ^ pv->cam_oldbuttons) & 8)\n\t\t\t\t\tCvar_SetValue(&cl_demospeed, min(cl_demospeed.value + 0.1, 10));\n\t\t\t\tif (nb & (nb ^ pv->cam_oldbuttons) & (4|8))\n\t\t\t\t\tCon_Printf(\"playback speed: %g%%\\n\", cl_demospeed.value*100);\n\t\t\t\tif (nb & (nb ^ pv->cam_oldbuttons) & 16)\n\t\t\t\t\tCbuf_AddText(\"demo_jump +10\", RESTRICT_LOCAL);\n\t\t\t\tif (nb & (nb ^ pv->cam_oldbuttons) & 32)\n\t\t\t\t\tCbuf_AddText(\"demo_jump -10\", RESTRICT_LOCAL);\n\t\t\t\tif (nb & (nb ^ pv->cam_oldbuttons) & (4|8))\n\t\t\t\t\tCon_Printf(\"playback speed: %g%%\\n\", cl_demospeed.value*100);\n\t\t\t}\n\t\t\tif (nb & (nb ^ pv->cam_oldbuttons) & 64)\n\t\t\t\tCvar_SetValue(&cl_splitscreen, max(cl_splitscreen.ival - 1, 0));\n\t\t\tif (nb & (nb ^ pv->cam_oldbuttons) & 128)\n\t\t\t\tCvar_SetValue(&cl_splitscreen, min(cl_splitscreen.ival + 1, MAX_SPLITS-1));\n\t\t}\n\t\tpv->cam_oldbuttons = (pv->cam_oldbuttons & 3) | (nb & ~3);\n\t\tif (cmd->impulse)\n\t\t{\n\t\t\tint pl = cmd->impulse;\n\n#if 1\n\t\t\tdo\n\t\t\t{\n\t\t\t\tCam_Lock(pv, Cam_FindSortedPlayer(pl));\n\t\t\t\tpv++;\n\t\t\t\tpl++;\n\t\t\t} while (pv >= &cl.playerview[0] && pv < &cl.playerview[cl.splitclients]);\n#else\n\t\t\tfor (i = 0; ; i++)\n\t\t\t{\n\t\t\t\tif (i == MAX_CLIENTS)\n\t\t\t\t{\n\t\t\t\t\tif (pl == cmd->impulse)\n\t\t\t\t\t\tbreak;\n\t\t\t\t\ti = 0;\n\t\t\t\t}\n\n\t\t\t\ts = &cl.players[i];\n\t\t\t\tif (s->name[0] && !s->spectator)\n\t\t\t\t{\n\t\t\t\t\tpl--;\n\t\t\t\t\tif (!pl)\n\t\t\t\t\t{\n\t\t\t\t\t\tCam_Lock(pv, i);\n\t\t\t\t\t\tif (pv >= &cl.playerview[0] && pv < &cl.playerview[cl.splitclients])\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tpl = 1;\n\t\t\t\t\t\t\tpv++;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n#endif\n\t\t\treturn;\n\t\t}\n\t}\n\n\tif (cmd->buttons & BUTTON_ATTACK) \n\t{\n\t\tif (!(pv->cam_oldbuttons & BUTTON_ATTACK)) \n\t\t{\n\t\t\tpv->cam_oldbuttons |= BUTTON_ATTACK;\n\n\t\t\tif (pv->cam_state != CAM_FREECAM)\n\t\t\t{\n\t\t\t\tCam_Unlock(pv);\n\t\t\t\tVectorCopy(pv->viewangles, cmd->angles);\n\t\t\t\tautotrackmode = TM_USER;\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t\treturn;\n\t}\n\telse\n\t{\n\t\tpv->cam_oldbuttons &= ~BUTTON_ATTACK;\n\t\tif (pv->cam_state == CAM_FREECAM && autotrackmode == TM_USER)\n\t\t{\n//\t\t\tif ((cmd->buttons & BUTTON_JUMP) && !(pv->cam_oldbuttons & BUTTON_JUMP))\n//\t\t\t\tCam_TrackCrosshairedPlayer(pv);\n\t\t\tpv->cam_oldbuttons = (pv->cam_oldbuttons&~BUTTON_JUMP) | (cmd->buttons & BUTTON_JUMP);\n\t\t\treturn;\n\t\t}\n\t}\n\n\tif (autotrackmode != TM_USER) \n\t{\n\t\tif ((cmd->buttons & BUTTON_JUMP) && !(pv->cam_oldbuttons & BUTTON_JUMP))\n\t\t\tautotrackmode = TM_USER;\n\t\telse\n\t\t{\n\t\t\tCam_CheckHighTarget(pv);\n\t\t\treturn;\n\t\t}\n\t}\n\n\tif (pv->cam_state != CAM_FREECAM) \n\t{\n\t\tif ((cmd->buttons & BUTTON_JUMP) && (pv->cam_oldbuttons & BUTTON_JUMP))\n\t\t\treturn;\t\t// don't pogo stick\n\n\t\tif (!(cmd->buttons & BUTTON_JUMP)) \n\t\t{\n\t\t\tpv->cam_oldbuttons &= ~BUTTON_JUMP;\n\t\t\treturn;\n\t\t}\n\t\tpv->cam_oldbuttons |= BUTTON_JUMP;\t// don't jump again until released\n\t}\n\n//\tCon_Printf(\"Selecting track target...\\n\");\n\n\tif (pv->cam_state != CAM_FREECAM)\n\t\tend =\t\t (pv->cam_spec_track + 1) % MAX_CLIENTS;\n\telse\n\t\tend = pv->cam_spec_track;\n\tend = max(0, end) % cl.allocated_client_slots;\n\ti = end;\n\tdo \n\t{\n\t\ts = &cl.players[i];\n\t\t//players with userid 0 are typically bots.\n\t\t//trying to spectate such bots just does not work (we have no idea which entity slot the bot is actually using, so don't try to track them).\n\t\tif (s->name[0] && !s->spectator && s->userid) \n\t\t{\n\t\t\tCam_Lock(pv, i);\n\t\t\treturn;\n\t\t}\n\t\ti = (i + 1) % cl.allocated_client_slots;\n\t} while (i != end);\n\t// stay on same guy?\n\ti = pv->cam_spec_track;\n\ts = &cl.players[i];\n\tif (s->name[0] && !s->spectator && s->userid) \n\t{\n\t\tCam_Lock(pv, i);\n\t\treturn;\n\t}\n\tCon_Printf(\"No target found ...\\n\");\n\tpv->cam_state = CAM_FREECAM;\n}\n\nvoid Cam_Reset(void)\n{\n\tunsigned int pnum;\n\tfor (pnum = 0; pnum < MAX_SPLITS; pnum++)\n\t{\n\t\tplayerview_t *pv = &cl.playerview[pnum];\n\t\tpv->cam_state = CAM_FREECAM;\n\t\tpv->cam_spec_track = 0;\n\t}\n}\n\nstatic int QDECL Cam_SortPlayers(const void *p1,const void *p2)\n{\n\tconst player_info_t *a = *(player_info_t**)p1;\n\tconst player_info_t *b = *(player_info_t**)p2;\n\n\tif (!*a->team)\n\t\treturn *b->team;\n\tif (!*b->team)\n\t\treturn -*a->team;\n//\tif (a->spectator || b->spectator)\n//\t\treturn a->spectator - b->spectator;\n\treturn Q_strcasecmp(a->team, b->team);\n}\nstatic int Cam_FindSortedPlayer(int number)\n{\n\tplayer_info_t *playerlist[MAX_CLIENTS], *s;\n\tint i;\n\tint slots;\n\tnumber--;\n\tfor (slots = 0, i = 0; i < cl.allocated_client_slots; i++)\n\t{\n\t\ts = &cl.players[i];\n\t\tif (s->name[0] && !s->spectator)\n\t\t\tplayerlist[slots++] = s;\n\t}\n\tif (!slots)\n\t\treturn 0;\n//\tif (number > slot)\n//\t\treturn cl.allocated_client_slots;\t//error\n\n\tqsort(playerlist, slots, sizeof(playerlist[0]), Cam_SortPlayers);\n\treturn playerlist[number % slots] - cl.players;\n}\n\nvoid Cam_TrackPlayer(int seat, char *cmdname, char *plrarg)\n{\n\tplayerview_t *pv = &cl.playerview[seat];\n\tint slot;\n\tplayer_info_t\t*s;\n\tchar *e;\n\n\tif (seat >= MAX_SPLITS)\n\t\treturn;\n\n\tif (cls.state <= ca_connected)\n\t{\n\t\tCon_Printf(\"Not connected.\\n\");\n\t\treturn;\n\t}\n\n\tif (!pv->spectator)\n\t{\n\t\tCon_Printf(\"Not spectating.\\n\");\n\t\treturn;\n\t}\n\n\tif (!Q_strcasecmp(plrarg, \"off\"))\n\t{\n\t\tCam_Unlock(pv);\n\t\treturn;\n\t}\n\n\tif (*plrarg == '#' && (slot=strtoul(plrarg+1, &e, 10)) && !*e)\n\t\tslot = Cam_FindSortedPlayer(slot);\n\telse\n\t{\n\t\t// search nicks first\n\t\tfor (slot = 0; slot < cl.allocated_client_slots; slot++)\n\t\t{\n\t\t\ts = &cl.players[slot];\n\t\t\tif (s->name[0] && !s->spectator && !Q_strcasecmp(s->name, plrarg))\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\tif (slot == cl.allocated_client_slots)\n\t{\n\t\t// didn't find nick, so search userids\n\t\tint userid;\n\t\tchar *c;\n\n\t\t// check if given arg is in fact a number\n\t\tc = plrarg;\n\t\twhile (*c)\n\t\t{\n\t\t\tif (*c < '0' || *c > '9')\n\t\t\t{\n\t\t\t\tCon_Printf(\"Couldn't find nick %s\\n\", plrarg);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tc++;\n\t\t}\n\n\t\tuserid = atoi(plrarg);\n\n\t\tfor (slot = 0; slot < cl.allocated_client_slots; slot++)\n\t\t{\n\t\t\ts = &cl.players[slot];\n\t\t\tif (s->name[0] && !s->spectator && s->userid == userid)\n\t\t\t\tbreak;\n\t\t}\n\n\t\tif (slot == cl.allocated_client_slots)\n\t\t{\n\t\t\tCon_Printf(\"Couldn't find userid %i\\n\", userid);\n\t\t\treturn;\n\t\t}\n\t}\n\n\tCam_Lock(pv, slot);\n}\n\nvoid Cam_Track_f(void)\n{\n\tint i, j;\n\n\tif (Cmd_Argc() < 2)\n\t{\n\t\tCon_Printf(\"Usage: %s userid|nick|off\\n\", Cmd_Argv(0));\n\t\treturn;\n\t}\n\n\ti = 1;\n\tj = Cmd_Argc() - 1;\n\tif (j > MAX_SPLITS)\n\t\tj = MAX_SPLITS;\n\n\twhile (j > 0)\n\t{\n\t\tCam_TrackPlayer(i - 1, Cmd_Argv(0), Cmd_Argv(i));\n\t\ti++;\n\t\tj--;\n\t}\n}\n\nvoid Cam_Track1_f(void)\n{\n\tif (Cmd_Argc() < 2)\n\t{\n\t\tCon_Printf(\"Usage: %s userid|nick|off\\n\", Cmd_Argv(0));\n\t\treturn;\n\t}\n\n\tCam_TrackPlayer(0, Cmd_Argv(0), Cmd_Argv(1));\n}\n\nvoid Cam_Track2_f(void)\n{\n\tif (Cmd_Argc() < 2)\n\t{\n\t\tCon_Printf(\"Usage: %s userid|nick|off\\n\", Cmd_Argv(0));\n\t\treturn;\n\t}\n\n\tCam_TrackPlayer(1, Cmd_Argv(0), Cmd_Argv(1));\n}\n\nvoid Cam_Track3_f(void)\n{\n\tif (Cmd_Argc() < 2)\n\t{\n\t\tCon_Printf(\"Usage: %s userid|nick|off\\n\", Cmd_Argv(0));\n\t\treturn;\n\t}\n\n\tCam_TrackPlayer(2, Cmd_Argv(0), Cmd_Argv(1));\n}\n\nvoid Cam_Track4_f(void)\n{\n\tif (Cmd_Argc() < 2)\n\t{\n\t\tCon_Printf(\"Usage: %s userid|nick|off\\n\", Cmd_Argv(0));\n\t\treturn;\n\t}\n\n\tCam_TrackPlayer(3, Cmd_Argv(0), Cmd_Argv(1));\n}\n\nvoid CL_InitCam(void)\n{\n\tCvar_Register (&cl_autotrack, cl_spectatorgroup);\n\tCvar_Register (&cl_autotrack_team, cl_spectatorgroup);\n\tCvar_Register (&cl_hightrack, cl_spectatorgroup);\n\tCvar_Register (&cl_chasecam, cl_spectatorgroup);\n//\tCvar_Register (&cl_camera_maxpitch, cl_spectatorgroup);\n//\tCvar_Register (&cl_camera_maxyaw, cl_spectatorgroup);\n//\tCvar_Register (&cl_selfcam, cl_spectatorgroup);\n\n\tCmd_AddCommand(\"autotrack\", Cam_AutoTrack_f);\t//reset it, if they jumped or something.\n\tCmd_AddCommand(\"track\", Cam_Track_f);\n\tCmd_AddCommand(\"track1\", Cam_Track1_f);\n\tCmd_AddCommand(\"track2\", Cam_Track2_f);\n\tCmd_AddCommand(\"track3\", Cam_Track3_f);\n\tCmd_AddCommand(\"track4\", Cam_Track4_f);\n}\n\n\n"
  },
  {
    "path": "engine/client/cl_demo.c",
    "content": "/*\nCopyright (C) 1996-1997 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\nSee the included (GNU.txt) GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n*/\n\n#include \"quakedef.h\"\n#include \"fs.h\"\n\nvoid CL_FinishTimeDemo (void);\nfloat demtime;\nfloat recdemostart;\t//keyed to Sys_DoubleTime\nint demoframe;\n\nint cls_lastto;\nstatic int cls_lasttype;\n\nvoid CL_PlayDemo(char *demoname, qboolean usesystempath);\nvoid CL_PlayDemoFile(vfsfile_t *f, char *demoname, qboolean issyspath);\n\nextern cvar_t qtvcl_forceversion1;\nextern cvar_t qtvcl_eztvextensions;\nextern cvar_t record_flush;\n\nstatic unsigned char demobuffer[1024*66];\t//input buffer\nstatic int demooffset;\t\t//start offset of demo buffer\nstatic int demobuffersize;\t//number of valid bytes within the buffer\nstatic int demopreparsedbytes;\t//number of bytes within the valid buffer that has already been pre-parsed.\nqboolean disablepreparse;\nqboolean endofdemo;\n\n#define BUFFERTIME 0.5\n/*\n==============================================================================\n\nDEMO CODE\n\nWhen a demo is playing back, all NET_SendMessages are skipped, and\nNET_GetMessages are read from the demo file.\n\nWhenever cl.time gets past the last received message, another message is\nread from the demo file.\n==============================================================================\n*/\n\n/*\n==============\nCL_StopPlayback\n\nCalled when a demo file runs out, or the user starts a game\n==============\n*/\nvoid CL_StopPlayback (void)\n{\n\tif (!cls.demoplayback)\n\t\treturn;\n\n\tMedia_CaptureDemoEnd();\n\n\tif (cls.demoinfile)\n\t\tVFS_CLOSE (cls.demoinfile);\n\tcls.demoinfile = NULL;\n\tcls.state = ca_disconnected;\n\tcls.demoplayback = DPB_NONE;\n\tcls.demoseeking = DEMOSEEK_NOT;\t//just in case\n\tcls.demotrack = -1;\n\tcls.demoeztv_ext = 0;\n\n\tif (cls.timedemo)\n\t\tCL_FinishTimeDemo ();\n\n\tTP_ExecTrigger(\"f_demoend\", true);\n}\n\n/*\n====================\nCL_WriteDemoCmd\n\nWrites the player0 user cmd (demos don't support split screen)\n====================\n*/\nvoid CL_WriteDemoCmd (usercmd_t *pcmd)\n{\n\tint\t\ti;\n\tfloat\tfl;\n\tqbyte\tc;\n\tq1usercmd_t cmd;\n\n\t//nq doesn't have this info\n\tif (cls.demorecording != DPB_QUAKEWORLD)\n\t\treturn;\n\n//Con_Printf(\"write: %ld bytes, %4.4f\\n\", msg->cursize, demtime);\n\n\tfl = LittleFloat(Sys_DoubleTime()-recdemostart);\n\tVFS_WRITE (cls.demooutfile, &fl, sizeof(fl));\n\n\tc = dem_cmd;\n\tVFS_WRITE (cls.demooutfile, &c, sizeof(c));\n\n\t// correct for byte order, bytes don't matter\n\n\tcmd.buttons = pcmd->buttons;\n\tcmd.impulse = pcmd->impulse;\n\tcmd.msec = pcmd->msec;\n\n\tfor (i = 0; i < 3; i++)\n\t\tcmd.angles[i] = LittleShort((pcmd->angles[i]*360.0)/65535);\n\n\tcmd.forwardmove = LittleShort(pcmd->forwardmove);\n\tcmd.sidemove    = LittleShort(pcmd->sidemove);\n\tcmd.upmove      = LittleShort(pcmd->upmove);\n\n\tVFS_WRITE (cls.demooutfile, &cmd, sizeof(cmd));\n\n\tfor (i=0 ; i<3 ; i++)\n\t{\n\t\tfl = LittleFloat (cl.playerview[0].viewangles[i]);\n\t\tVFS_WRITE (cls.demooutfile, &fl, 4);\n\t}\n\n\tif (record_flush.ival)\n\t\tVFS_FLUSH (cls.demooutfile);\n}\n\n/*\n====================\nCL_WriteDemoMessage\n\nDumps the current net message, prefixed by the length and view angles\n====================\n*/\nvoid CL_WriteDemoMessage (sizebuf_t *msg, int payloadoffset)\n{\n\tint\t\tlen;\n\tint\t\ti;\n\tfloat\tfl;\n\tqbyte\tc;\n\n//Con_Printf(\"write: %ld bytes, %4.4f\\n\", msg->cursize, demtime);\n\n\tswitch (cls.demorecording)\n\t{\n\tdefault:\n\t\treturn;\n\tcase DPB_QUAKEWORLD:\t//QW\n\t\tfl = LittleFloat(Sys_DoubleTime()-recdemostart);\n\t\tVFS_WRITE (cls.demooutfile, &fl, sizeof(fl));\n\n\t\tc = dem_read;\n\t\tVFS_WRITE (cls.demooutfile, &c, sizeof(c));\n\n\t\tif (*(int*)msg->data == -1 && payloadoffset==0)\n\t\t{\n\t\t\t//connectionless packet.\n\t\t\tlen = LittleLong (msg->cursize);\n\t\t\tVFS_WRITE (cls.demooutfile, &len, 4);\n\t\t\tVFS_WRITE (cls.demooutfile, msg->data + payloadoffset, msg->cursize - payloadoffset);\n\t\t}\n\t\telse\n\t\t{\n\t\t\t//regenerate a legacy netchan. no fragmentation support, but whatever. this ain't udp.\n\t\t\t//the length\n\t\t\tlen = LittleLong (msg->cursize - payloadoffset + 8);\n\t\t\tVFS_WRITE (cls.demooutfile, &len, 4);\n\t\t\t//hack the netchan here.\n\t\t\ti = cls.netchan.incoming_sequence;\n\t\t\tVFS_WRITE (cls.demooutfile, &i, 4);\n\t\t\ti = cls.netchan.incoming_acknowledged;\n\t\t\tVFS_WRITE (cls.demooutfile, &i, 4);\n\t\t\t//and the data\n\t\t\tVFS_WRITE (cls.demooutfile, msg->data + payloadoffset, msg->cursize - payloadoffset);\n\t\t}\n\t\tbreak;\n#ifdef Q2CLIENT\n\tcase DPB_QUAKE2:\n\t\tlen = LittleLong (net_message.cursize - payloadoffset);\n\t\tVFS_WRITE(cls.demooutfile, &len, sizeof(len));\n\t\tVFS_WRITE(cls.demooutfile, net_message.data + payloadoffset, net_message.cursize - payloadoffset);\n\t\tbreak;\n#endif\n#ifdef NQPROT\n\tcase DPB_NETQUAKE:\t//NQ\n\t\tlen = LittleLong (net_message.cursize - payloadoffset);\n\t\tVFS_WRITE(cls.demooutfile, &len, sizeof(len));\n\t\tfor (i=0 ; i<3 ; i++)\n\t\t{\n\t\t\tfloat f = LittleFloat (cl.playerview[0].viewangles[i]);\n\t\t\tVFS_WRITE(cls.demooutfile, &f, sizeof(f));\n\t\t}\n\t\tVFS_WRITE(cls.demooutfile, net_message.data + payloadoffset, net_message.cursize - payloadoffset);\n\t\tbreak;\n#endif\n\t}\n\tif (record_flush.ival)\n\t\tVFS_FLUSH (cls.demooutfile);\n}\n\nint demo_preparsedemo(unsigned char *buffer, int bytes)\n{\n\tint parsed = 0;\n\tint ofs;\n\tunsigned int length;\n#define dem_mask 7\n\tif (cls.demoplayback != DPB_MVD)\n\t\treturn bytes;\t//no need if its not an mvd (this simplifies it a little)\n\n\twhile (bytes>2)\n\t{\n\t\tswitch(buffer[1]&dem_mask)\n\t\t{\n\t\tcase dem_cmd:\n\t\t\tofs = -(int)(sizeof(q1usercmd_t));\n\t\t\tofs = 0;\n\t\t\tbreak;\n\t\tcase dem_set:\n\t\t\tofs = -(8);\n\t\t\tbreak;\n\t\tcase dem_multiple:\n\t\t\tofs = 6;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tofs = 2;\n\t\t\tbreak;\n\t\t}\n\t\tif (ofs > 0)\n\t\t{\n\t\t\tif (ofs+4 > bytes)\n\t\t\t\tbreak;\n\t\t\tlength = (buffer[ofs+0]<<0) + (buffer[ofs+1]<<8) + (buffer[ofs+2]<<16) + (buffer[ofs+3]<<24);\n\t\t\tif (length > MAX_OVERALLMSGLEN)\n\t\t\t{\n\t\t\t\tdisablepreparse = true;\n\t\t\t\tCon_Printf(\"Error looking ahead at demo\\n\");\n\t\t\t\treturn parsed;\n\t\t\t}\n\t\t\tofs+=4;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tlength = -ofs;\n\t\t\tofs = 2;\n\t\t}\n\t\t//ofs is now the offset of the data\n\t\tif (ofs+length > bytes)\n\t\t{\n\t\t\treturn parsed; //not got it all\n\t\t}\n\t\tif ((buffer[1]&dem_mask) == dem_all && (buffer[1] & ~dem_mask) && length < MAX_OVERALLMSGLEN)\n\t\t{\n\t\t\tnet_message.cursize = length;\n\t\t\tmemcpy(net_message.data, buffer+ofs, length);\n\t\t\tMSG_BeginReading(&net_message, cls.netchan.netprim);\n\t\t\tCLQW_ParseServerMessage();\n\t\t}\n\n\t\tparsed += ofs+length;\n\t\tbuffer += ofs+length;\n\t\tbytes -= ofs+length;\n\t}\n\n\treturn parsed;\n}\n\nint readdemobytes(int *readpos, void *data, int len)\n{\n\tint i;\n\tint trybytes;\n\tif (len < 0)\n\t\tHost_EndGame(\"Corrupt demo\");\n\n\t//if there's not enough space in the buffer, flush it now and allow grabbing a new chunk\n\t//try to ensure it happens periodically enough for the preparsing stuff to happen early.\n\tif (demooffset+*readpos+len > demobuffersize || demooffset > sizeof(demobuffer)/2)\n\t{\n\t\tmemmove(demobuffer, demobuffer+demooffset, demobuffersize);\n\t\tdemooffset = 0;\n\t}\n\n\tif (demopreparsedbytes < 0)\t//won't happen in normal running, but can still happen on corrupt data... if we don't disconnect first.\n\t{\n\t\tCon_Printf(\"reset preparsed (underflow)\\n\");\n\t\tdemopreparsedbytes = 0;\n\t}\n\tif (demopreparsedbytes > demobuffersize)\n\t{\n\t\tCon_Printf(\"reset preparsed (overflow)\\n\");\n\t\tdemopreparsedbytes = 0;\n\t}\n\n\ttrybytes = sizeof(demobuffer)-demooffset-demobuffersize;\n\tif (trybytes > 4096)\n\t\ti = VFS_READ(cls.demoinfile, demobuffer+demooffset+demobuffersize, trybytes);\n\telse\n\t\ti = 0;\n\tif (i > 0)\n\t{\n\t\tdemobuffersize += i;\n\t\tif (disablepreparse)\n\t\t\tdemopreparsedbytes = demobuffersize;\n\t\telse\n\t\t\tdemopreparsedbytes += demo_preparsedemo(demobuffer+demooffset+demopreparsedbytes, demobuffersize-demopreparsedbytes);\n\t}\n\n\tif (*readpos+len > demobuffersize)\n\t{\n\t\tif (i < 0 || (i == 0 && len && cls.demoinfile->seekstyle < SS_SLOW))\n\t\t{\t//0 means no data available yet, don't error on that (unless we can seek, in which case its not a stream and we won't get any more data later on).\n\t\t\tendofdemo = true;\n\t\t\treturn 0;\n\t\t}\n//\t\tlen = demobuffersize;\n\t\treturn 0;\n\t}\n\tmemcpy(data, demobuffer+demooffset+*readpos, len);\n\t*readpos += len;\n\treturn len;\n}\n\nvoid demo_flushbytes(int bytes)\n{\n\tif (demooffset+bytes > demobuffersize)\n\t\tSys_Error(\"demo_flushbytes: flushed too much!\\n\");\n\tdemooffset += bytes;\n\tdemobuffersize -= bytes;\n\n\tif (demopreparsedbytes < bytes)\n\t\tdemopreparsedbytes = 0;\n\telse\n\t\tdemopreparsedbytes -= bytes;\n}\n\nvoid demo_flushcache(void)\n{\n\tdemooffset = 0;\n\tdemobuffersize = 0;\n\tdemopreparsedbytes = 0;\n\n\t//no errors yet\n\tdisablepreparse = false;\n}\n\nvoid demo_resetcache(int bytes, void *data)\n{\n\tendofdemo = false;\n\tdemo_flushcache();\n\n\tdemooffset = 0;\n\tdemobuffersize = bytes;\n\tdemopreparsedbytes = 0;\n\tmemcpy(demobuffer, data, bytes);\n\n\t//preparse it now\n\tbytes = 0;\n\treaddemobytes(&bytes, NULL, 0);\n}\n\n\nvoid CL_ProgressDemoTime(void)\n{\n\textern cvar_t cl_demospeed;\n\n\tif (cl.parsecount && Media_PausedDemo(true))\n\t{\t//console visible whilst democapturing\n\t\tcls.netchan.last_received = realtime;\n\t\treturn;\n\t}\n\n\tif (cl.demopausedtilltime >= realtime)\n\t\treturn;\n\tcl.demopausedtilltime = 0;\n\tcl.demonudge = 0;\n\tif (cl_demospeed.value >= 0 && cls.state == ca_active)\n\t\tdemtime += host_frametime*cl_demospeed.value;\n\telse\n\t\tdemtime += host_frametime;\n}\n\nvoid CL_DemoJump_f(void)\n{\n\tfloat newtime;\n\tchar *s = (!strncmp(Cmd_Argv(0), \"demo_jump_\", 10))?Cmd_Argv(0)+10:Cmd_Argv(1);\n\tchar *colon = strchr(s, ':');\n\n\tif (!cls.demoplayback)\n\t{\n\t\tCon_Printf(\"not playing a demo, cannot jump.\\n\");\n\t\treturn;\n\t}\n\n\tif (Cmd_Argc() < 2)\n\t{\n\t\tCon_Printf(\"current time %.1f.\\n\", demtime);\n\t\treturn;\n\t}\n\n\tif (!*cls.lastdemoname)\n\t{\n\t\tCon_Printf(\"unable to seak in qtv streams.\\n\");\n\t\treturn;\t//can't seek live streams...\n\t}\n\n\tif (!strcmp(s, \"intermission\") || !strcmp(s, \"end\"))\n\t{\t//seeks until we see an svc_intermission\n\t\tcls.demoseeking = DEMOSEEK_INTERMISSION;\n\t\treturn;\n\t}\n\tif (!strcmp(s, \"mark\"))\n\t{\t//seeks until we see an svc_stufftext `//demomark`\n\t\tcls.demoseeking = DEMOSEEK_MARK;\n\t\treturn;\n\t}\n\n\tif (*s == '+' || *s == '-')\n\t{\n\t\tif (colon)\n\t\t{\n\t\t\tcolon++;\n\t\t\tnewtime = demtime + atoi(colon) + atoi(s)*60;\n\t\t}\n\t\telse\n\t\t\tnewtime = demtime + atoi(s);\n\t}\n\telse\n\t{\n\t\t//absolute seek time\n\t\tif (colon)\n\t\t{\n\t\t\tcolon++;\n\t\t\tnewtime = atoi(colon);\n\t\t\tnewtime += atoi(s)*60;\n\t\t}\n\t\telse\n\t\t\tnewtime = atoi(s);\n\t}\n\tif (newtime < 0)\n\t\tnewtime = 0;\n\n\tif (newtime >= demtime)\n\t\tcls.demoseektime = newtime;\n\telse\n\t{\n\t\tvfsfile_t *df = cls.demoinfile;\n\t\tCon_Printf(\"Rewinding demo\\n\");\n\t\tif (df->seekstyle != SS_UNSEEKABLE)\n\t\t{\n\t\t\tVFS_SEEK(df, 0);\n\t\t\tcls.demoinfile = NULL;\n\t\t\tCL_PlayDemoFile(df, cls.lastdemoname, cls.lastdemowassystempath);\n\t\t}\n\t\telse\n\t\t\tCL_PlayDemo(cls.lastdemoname, cls.lastdemowassystempath);\n\n\t\t//now fastparse it.\n\t\tcls.demoseektime = newtime;\n\t}\n\tcls.demoseeking = DEMOSEEK_TIME;\n}\n\nvoid CL_DemoNudge_f(void)\n{\n\textern cvar_t cl_demospeed;\n\tint move = atoi(Cmd_Argv(1));\n\tint newnudge;\n\n\tif (!cls.demoplayback)\n\t{\n\t\tCon_Printf(\"not playing a demo, cannot nudge.\\n\");\n\t\treturn;\n\t}\n\n\tif (!*cls.lastdemoname)\n\t{\n\t\tCon_Printf(\"unable to seak in qtv streams.\\n\");\n\t\treturn;\t//can't seek live streams...\n\t}\n\n\tif (!move)\n\t\tmove = 1;\n\n\tnewnudge = cl.demonudge + move;\n\tif (newnudge <= -(int)countof(cl.inframes))\n\t\tnewnudge = 1-(int)countof(cl.inframes);\n\n\tif (newnudge < 0)\n\t{\t//if we're nudging to a past frame, make sure that its actually valid.\n\t\tfor(;-(int)countof(cl.inframes) < newnudge && newnudge < 0;)\n\t\t{\n\t\t\tint i = cls.netchan.incoming_sequence+newnudge;\n\t\t\tif (i < 0)\n\t\t\t\tbreak;\n\t\t\tif (cl.inframes[i&UPDATE_MASK].frameid == i && !cl.inframes[i&UPDATE_MASK].invalid)\n\t\t\t{\n\t\t\t\tcl.demonudge = newnudge;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (move < 0)\n\t\t\t\tnewnudge--;\n\t\t\telse\n\t\t\t\tnewnudge++;\n\t\t}\n\t\tif (!newnudge)\n\t\t\tcl.demonudge = newnudge;\n\t}\n\telse\n\t\tcl.demonudge = newnudge;\n\n\tcl.demopausedtilltime = realtime + 3;\n}\n\n/*\n====================\nCL_GetDemoMessage\n\n  FIXME...\n====================\n*/\n\nvec3_t demoangles;\nfloat olddemotime = 0;\nfloat nextdemotime = 0;\nqboolean CL_GetDemoMessage (void)\n{\n\tint\t\tr, i, j, tracknum;\n\tfloat\tf;\n\tfloat\tdemotime;\n\tqbyte\tc, msecsadded=0;\n\tusercmd_t *pcmd;\n\tq1usercmd_t q1cmd;\n\tint demopos = 0;\n\tint msglength;\n\tstatic float throttle;\n\tstatic qboolean newseq;\n\n\tif (endofdemo)\n\t{\n\t\tendofdemo = false;\n\t\tCL_StopPlayback ();\n\n\t\tCL_NextDemo();\n\t\treturn 0;\n\t}\n\n#ifdef NQPROT\n\tif (cls.demoplayback == DPB_NETQUAKE\n#ifdef Q2CLIENT\n\t\t|| cls.demoplayback == DPB_QUAKE2\n#endif\n\t\t)\n\t{\t//read the nq demo\n\n\t\t//if we've finished reading the connection part of the demo, but not finished loading, pause the demo\n\t\tif (cls.signon == 1 && !cl.worldmodel)\n\t\t{\n\t\t\tdemtime = cl.gametime;\n\t\t\treturn 0;\n\t\t}\n\n\t\t//if this is the starting frame of a timedemo\n\t\tif (cls.timedemo)\n\t\tif (cls.td_startframe == -1 && cls.state == ca_active)\n\t\t{\t//start the timer only once we are connected.\n\t\t\t//make sure everything is loaded, to avoid stalls\n\n\t\t\tMenu_PopAll();\n\t\t\tCOM_WorkerFullSync();\n\n\t\t\tcls.td_starttime = Sys_DoubleTime();\n\t\t\tcls.td_startframe = host_framecount;\n\n\t\t\t//force the console up, we're done loading.\n\t\t\tKey_Dest_Remove(kdm_console);\n\t\t\tscr_con_current = 0;\n\t\t}\n\n#ifdef Q2CLIENT\n\t\t//q2 uses a fixed 10fps packet rate, even demos enforce it.\n\t\tif (cls.demoplayback == DPB_QUAKE2)\n\t\t{\n\t\t\tif (cls.timedemo)\n\t\t\t{\n\t\t\t\tif (demoframe == host_framecount)\n\t\t\t\t\treturn 0;\n\t\t\t\tdemoframe = host_framecount;\n\t\t\t}\n#if 1\n\t\t\telse if (demtime < cl.gametime && cl.gametime)\n\t\t\t{\n\t\t\t\tif (demtime <= cl.gametime-1)\n\t\t\t\t\tdemtime = cl.gametime;\n\t\t\t\treturn 0;\n\t\t\t}\n#else\n\t\t\telse if (cls.netchan.last_received == realtime || cls.netchan.last_received > realtime-0.1)\n\t\t\t\treturn 0;\n#endif\n\t\t}\n\t\telse\n#endif\n\t\t\tif (cls.demoplayback == DPB_NETQUAKE && cls.signon == 4/*SIGNONS*/)\n\t\t{\n\t\t\t/*if (!demtime)\n\t\t\t{\n\t\t\t\tcl.gametime = 0;\n\t\t\t\tcl.gametimemark = demtime;\n\t\t\t\tolddemotime = 0;\n\t\t\t\treturn 0;\n\t\t\t}*/\n\t\t\tcls.netchan.last_received = realtime;\n\t\t\tif (cls.demoseeking == DEMOSEEK_TIME)\n\t\t\t{\n\t\t\t\tif (cl.gametime > cls.demoseektime)\n\t\t\t\t{\n\t\t\t\t\tcls.demoseeking = DEMOSEEK_NOT;\n\t\t\t\t\treturn 0;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (cl.demonudge > 0)\n\t\t\t\tcl.demonudge--;\n\t\t\telse if ((cls.timedemo && host_framecount == demoframe) || (!cls.timedemo && demtime < cl.gametime && cl.gametime))// > dem_lasttime+demtime)\n\t\t\t{\n\t\t\t\tif (demtime <= cl.gametime-1)\n\t\t\t\t\tdemtime = cl.gametime;\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t\tdemoframe = host_framecount;\n\t\t}\n\t\telse if (cls.signon < 4)\n\t\t\tdemtime = 0;\n\t\tif (readdemobytes(&demopos, &msglength, 4) != 4)\n\t\t{\n\t\t\treturn 0;\n\t\t}\n\t\tmsglength = LittleLong (msglength);\n\t\tif (msglength == -1)\n\t\t{\n\t\t\tint tmppos = demopos;\n\t\t\t//q2 writes a length of -1 to mark eof. if this peek fails then it really is eof and we shouldn't fail from weird message length checks.\n\t\t\tif (readdemobytes(&tmppos, &msglength, 4) != 4)\n\t\t\t{\n\t\t\t\tendofdemo = true;\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t}\n\t\tif (cls.demoplayback == DPB_NETQUAKE)\n\t\t{\n\t\t\tfor (i=0 ; i<3 ; i++)\n\t\t\t{\n\t\t\t\treaddemobytes(&demopos, &f, 4);\n\t\t\t\tdemoangles[i] = LittleFloat (f);\n\t\t\t}\n\t\t}\n\n\t\tolddemotime = demtime;\n\n\t\tif (msglength > net_message.maxsize)\n\t\t{\n\t\t\tCon_Printf (\"Demo message > MAX_MSGLEN\\n\");\n\t\t\tCL_StopPlayback ();\n\t\t\treturn 0;\n\t\t}\n\t\tif (readdemobytes(&demopos, net_message.data, msglength) != msglength)\n\t\t{\n\t\t\treturn 0;\n\t\t}\n\t\tdemo_flushbytes(demopos);\n\t\tNET_UpdateRates(cls.sockets, true, msglength);\t//keep any rate calcs sane\n\t\tnet_message.cursize = msglength;\n\n\t\treturn 1;\n\t}\n#endif\n\nreadnext:\n\tif (demopos)\n\t{\n\t\tdemo_flushbytes(demopos);\n\t\tdemopos = 0;\n\t}\n\n\t// read the time from the packet\n\tif (cls.demoplayback == DPB_MVD)\n\t{\n\t\tif (demtime < 0)\n\t\t{\n\t\t\treaddemobytes(&demopos, NULL, 0);\t//keep it feeding through\n\t\t\treturn 0;\n\t\t}\n\t\tif (olddemotime > demtime)\n\t\t\tolddemotime = demtime;\n\t\tif (demtime + 1.0 < olddemotime)\n\t\t\tdemtime = olddemotime - 1.0;\n\n\t\tif (readdemobytes(&demopos, &msecsadded, sizeof(msecsadded)) != sizeof(msecsadded))\n\t\t{\n\t\t\tCon_ThrottlePrintf(&throttle, 1, \"Not enough buffered\\n\");\n\t\t\tolddemotime = demtime+1;\n\n\t\t\tdemotime = olddemotime;\n\t\t\tnextdemotime = demotime;\n\t\t\treturn 0;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tdemotime = olddemotime + msecsadded*(1.0f/1000);\n\t\t\tnextdemotime = demotime;\n\t\t}\n\t}\n\telse if (cls.demoplayback == DPB_QUAKEWORLD)\n\t{\n\t\tif (readdemobytes(&demopos, &demotime, sizeof(demotime)) != sizeof(demotime))\n\t\t{\n\t\t\tCon_ThrottlePrintf(&throttle, 1, \"Not enough buffered\\n\");\n\t\t\tolddemotime = demtime;\t//if we ran out of buffered demo, delay the demo parsing a little\n\t\t\treturn 0;\n\t\t}\n\t\tdemotime = LittleFloat(demotime);\n\t}\n\telse\n\t\treturn 0;\n\n\tif (cl.sendprespawn)\n\t{\n\t\tCL_RequestNextDownload();\n\t\tif (!cls.timedemo)\n\t\t\treturn 0;\n\t}\n\n\n// decide if it is time to grab the next message\n\tif (cls.demoseeking != DEMOSEEK_NOT)\n\t{\n\t\tdemtime = demotime;\t//warp\n\t\tif (cls.demoseeking == DEMOSEEK_TIME && demtime >= cls.demoseektime)\n\t\t\tcls.demoseeking = DEMOSEEK_NOT;\n\t}\n\telse if (cls.timedemo)\n\t{\n\t\tif (cls.td_lastframe < 0)\n\t\t\tcls.td_lastframe = demotime;\n\t\telse if (demotime > cls.td_lastframe)\n\t\t{\n\t\t\tcls.td_lastframe = demotime;\n\t\t\treturn 0;\t\t// next packet starts after the previous one. draw something before parsing it.\n\t\t}\n\t\tif (cls.td_startframe == -1 && cls.state == ca_active)\n\t\t{\t//start the timer only once we are connected.\n\t\t\tcls.td_starttime = Sys_DoubleTime();\n\t\t\tcls.td_startframe = host_framecount;\n\n\t\t\t//force the console up, we're done loading.\n\t\t\tKey_Dest_Remove(kdm_console);\n\t\t\tscr_con_current = 0;\n\t\t}\n\t\tif (cls.td_startframe == host_framecount+1)\n\t\t\tcls.td_starttime = Sys_DoubleTime();\n\t\tdemtime = demotime; // warp\n\t}\n\telse if (cl.demonudge > 0)\n\t{\n\t\tcl.demonudge--;\n\t\tdemtime = demotime; // warp\n\t}\n\telse if (!(cl.paused&~4) && cls.state >= ca_onserver)\n\t{\t// always grab until fully connected\n\t\tif (demtime + 1.0 < demotime)\n\t\t{\n\t\t\t// too far back\n\t\t\tdemtime = demotime - 1.0;\n\t\t\treturn 0;\n\t\t}\n\t\telse if (demtime < demotime)\n\t\t{\n\t\t\treturn 0;\t\t// don't need another message yet\n\t\t}\n\t}\n\telse\n\t\tdemtime = demotime; // we're warping\n\n\tif (cls.demoplayback == DPB_MVD)\n\t{\n\t\tif ((msecsadded || cls.netchan.incoming_sequence < 2) && olddemotime != demotime)\n\t\t{\n\t\t\tnewseq = true;\n\t\t\tcls.netchan.frame_latency = 0;\n\t\t\tcls.netchan.last_received = realtime; // just to happy timeout check\n\t\t}\n\t}\n\n\tif (cls.state < ca_demostart)\n\t\tHost_Error (\"CL_GetDemoMessage: cls.state != ca_active\");\n\n\t// get the msg type\n\tif (readdemobytes (&demopos, &c, sizeof(c)) != sizeof(c))\n\t{\n\t\tCon_ThrottlePrintf(&throttle, 1, \"Not enough buffered\\n\");\n\t\tolddemotime = demtime+1;\n\t\treturn 0;\n\t}\n\tswitch (c&7)\n\t{\n\tcase dem_cmd :\n\t\tif (cls.demoplayback == DPB_MVD)\n\t\t{\n\t\t\tCon_Printf(\"mvd demos/qtv streams should not contain dem_cmd\\n\");\n\t\t\tolddemotime = demtime+1;\n\t\t\tCL_StopPlayback ();\n\t\t\treturn 0;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// user sent input\n\t\t\ti = cl.movesequence & UPDATE_MASK;\n\t\t\tpcmd = &cl.outframes[i].cmd[0];\n\t\t\tr = readdemobytes (&demopos, &q1cmd, sizeof(q1cmd));\n\t\t\tif (r != sizeof(q1cmd))\n\t\t\t{\n\t\t\t\tCon_ThrottlePrintf(&throttle, 1, \"Not enough buffered\\n\");\n\t\t\t\tolddemotime = demtime+1;\n\t\t\t\tCL_StopPlayback ();\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t\t// byte order stuff\n\t\t\tfor (j = 0; j < 3; j++)\n\t\t\t{\n\t\t\t\tq1cmd.angles[j] = LittleFloat(q1cmd.angles[j]);\n\t\t\t\tpcmd->angles[j] = ((int)(q1cmd.angles[j]*65536.0/360)&65535);\n\t\t\t}\n\t\t\tpcmd->forwardmove = q1cmd.forwardmove\t= LittleShort(q1cmd.forwardmove);\n\t\t\tpcmd->sidemove = q1cmd.sidemove\t\t\t= LittleShort(q1cmd.sidemove);\n\t\t\tpcmd->upmove = q1cmd.upmove\t\t\t\t= LittleShort(q1cmd.upmove);\n\t\t\tpcmd->msec = q1cmd.msec;\n\t\t\tpcmd->buttons = q1cmd.buttons;\n\n\n\t\t\tcl.outframes[i].senttime = realtime;\n\t\t\tcl.outframes[i].server_message_num = cl.validsequence;\n\t\t\tcl.outframes[i].cmd_sequence = cl.movesequence;\n\t\t\tcls.netchan.outgoing_sequence++;\n\t\t\tcl.movesequence = cls.netchan.outgoing_sequence;\n\t\t\tfor (i=0 ; i<3 ; i++)\n\t\t\t{\n\t\t\t\treaddemobytes (&demopos, &f, 4);\n\t\t\t\tdemoangles[i] = LittleFloat (f);\n\t\t\t\tcl.playerview[0].viewangles[i] = LittleFloat (f);\n\t\t\t}\n\t\t\tgoto readnext;\n\t\t}\n\t\tbreak;\n\n\tcase dem_read:\nreadit:\n\t\t// get the next message\n\t\tif (readdemobytes (&demopos, &msglength, 4) != 4)\n\t\t{\n\t\t\tCon_ThrottlePrintf(&throttle, 1, \"Not enough buffered\\n\");\n\t\t\tolddemotime = demtime+1;\n\t\t\treturn 0;\n\t\t}\n\t\tmsglength = LittleLong (msglength);\n\t//Con_Printf(\"read: %ld bytes\\n\", msglength);\n\t\tif ((unsigned int)msglength > MAX_OVERALLMSGLEN)\n\t\t{\n\t\t\tCon_Printf (\"Demo message > MAX_OVERALLMSGLEN\\n\");\n\t\t\tCL_StopPlayback ();\n\t\t\treturn 0;\n\t\t}\n\t\tif (readdemobytes (&demopos, net_message.data, msglength) != msglength)\n\t\t{\n\t\t\tCon_ThrottlePrintf(&throttle, 1, \"Not enough buffered\\n\");\n\t\t\tolddemotime = demtime+1;\n\t\t\treturn 0;\n\t\t}\n\t\tNET_UpdateRates(cls.sockets, true, msglength);\t//keep any rate calcs sane\n\t\tnet_message.cursize = msglength;\n\n\t\tif (cls.demoplayback == DPB_MVD)\n\t\t{\n\t\t\tint seat;\n\t\t\tcl.defaultnetsplit = 0;\n\t\t\tswitch(cls_lasttype)\n\t\t\t{\n\t\t\tcase dem_multiple:\n\t\t\t\tif (!cls_lastto && (cls.ezprotocolextensions1 & EZPEXT1_HIDDEN_MESSAGES))\n\t\t\t\t{\t//an 'mvdsv hidden message' packet.\n\t\t\t\t\tMSG_BeginReading(&net_message, cls.netchan.netprim);\n\t\t\t\t\tCLEZ_ParseHiddenDemoMessage();\n\t\t\t\t\tolddemotime = demotime;\n\t\t\t\t\tgoto readnext;\n\t\t\t\t}\n\t\t\t\tfor (seat = 0; seat < cl.splitclients; seat++)\n\t\t\t\t{\n\t\t\t\t\ttracknum = cl.playerview[seat].cam_spec_track;\n\t\t\t\t\tif (cl.playerview[seat].cam_state == CAM_FREECAM)\n\t\t\t\t\t\ttracknum = -1;\n\t\t\t\t\tif (tracknum == -1 || !(cls_lastto & (1 << tracknum)))\n\t\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (seat == cl.splitclients)\n\t\t\t\t{\n\t\t\t\t\tolddemotime = demotime;\n\t\t\t\t\tgoto readnext;\n\t\t\t\t}\n\t\t\t\tcl.defaultnetsplit = seat;\n\t\t\t\tbreak;\n\t\t\tcase dem_single:\n\t\t\t\t{\n\t\t\t\t\tint maxseat = cl.splitclients;\n\t\t\t\t\t//only accept single messages that are directed to the first player view.\n\t\t\t\t\t//this is too problematic otherwise (apparently mvdsv doesn't use dem_multiple for team says any more).\n\t\t\t\t\tif (1)\n\t\t\t\t\t\tmaxseat = 1;\n\t\t\t\t\tfor (seat = 0; seat < maxseat; seat++)\n\t\t\t\t\t{\n\t\t\t\t\t\ttracknum = cl.playerview[seat].cam_spec_track;\n\t\t\t\t\t\tif (cl.playerview[seat].cam_state == CAM_FREECAM)\n\t\t\t\t\t\t\ttracknum = -1;\n\t\t\t\t\t\tif (tracknum == -1 || (cls_lastto != tracknum))\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tif (seat == maxseat)\n\t\t\t\t\t{\n\t\t\t\t\t\tolddemotime = demotime;\n\t\t\t\t\t\tgoto readnext;\n\t\t\t\t\t}\n\t\t\t\t\tcl.defaultnetsplit = seat;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase dem_all:\n\t\t\t\tif (c & ~dem_mask)\n\t\t\t\t{\n\t\t\t\t\tolddemotime = demotime;\n\t\t\t\t\tgoto readnext;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tbreak;\n\n\tcase dem_set:\n\t\tif (readdemobytes (&demopos, &j, 4) != 4)\n\t\t{\n\t\t\tolddemotime = demtime;\n\t\t\treturn 0;\n\t\t}\n\t\tif (readdemobytes (&demopos, &i, 4) != 4)\n\t\t{\n\t\t\tolddemotime = demtime;\n\t\t\treturn 0;\n\t\t}\n\t\tcls.demostarttime = demtime;\n\t\tcls.netchan.outgoing_sequence = LittleLong(j);\n\t\tcls.netchan.incoming_sequence = LittleLong(i);\n\t\tcl.movesequence = cls.netchan.outgoing_sequence;\n\n\t\tNET_UpdateRates(cls.sockets, false, demopos);\t//keep any rate calcs sane\n\n\t\tif (cls.demoplayback == DPB_MVD)\n\t\t\tcls.netchan.incoming_acknowledged = cls.netchan.incoming_sequence;\n\t\tgoto readnext;\n\n\tcase dem_multiple:\n\t\tif (readdemobytes (&demopos, &i, sizeof(i)) != sizeof(i))\n\t\t{\n\t\t\tolddemotime = demtime;\n\t\t\treturn 0;\n\t\t}\n\t\tcls_lastto = LittleLong(i);\n\t\tcls_lasttype = dem_multiple;\n\t\tgoto readit;\n\n\tcase dem_single:\n\t\tcls_lastto = c >> 3;\n\t\tcls_lasttype = dem_single;\n\t\tgoto readit;\n\n\tcase dem_stats:\n\t\tcls_lastto = c >> 3;\n\t\tcls_lasttype = dem_stats;\n\t\tgoto readit;\n\n\tcase dem_all:\n\t\tcls_lastto = 0;\n\t\tcls_lasttype = dem_all;\n\t\tgoto readit;\n\n\tdefault :\n\t\tCon_Printf(\"Corrupted demo.\\n\");\n\t\tCL_StopPlayback ();\n\t\treturn 0;\n\t}\n\tdemo_flushbytes(demopos);\n\n\tif (cls.demoplayback == DPB_MVD)\n\t{\n\t\tif (/*(msecsadded || cls.netchan.incoming_sequence < 2) && olddemotime != demotime ||*/ newseq)\n\t\t{\n\t\t\tnewseq = false;\n\t\t\tif (!(cls.fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS))\n\t\t\t{\n\t\t\t\tcls.netchan.incoming_sequence++;\n\t\t\t\tcls.netchan.incoming_acknowledged++;\n\t\t\t}\n\t\t\tcls.netchan.frame_latency = 0;\n\t\t\tcls.netchan.last_received = realtime; // just to happy timeout check\n\t\t}\n\t}\n\n\tolddemotime = demotime;\n\tnet_from.type = NA_INVALID;\n\treturn 1;\n}\n\n/*\n====================\nCL_Stop_f\n\nstop recording a demo\n====================\n*/\nvoid CL_Stop_f (void)\n{\n\tif (!cls.demorecording)\n\t{\n#if !defined(CLIENTONLY) && defined(MVD_RECORDING)\n\t\tSV_MVDStop_f();\n#else\n\t\tCon_Printf (\"Not recording a demo.\\n\");\n#endif\n\t\treturn;\n\t}\n\n// write a disconnect message to the demo file\n#ifdef Q2CLIENT\n\tif (cls.demorecording == DPB_QUAKE2)\n\t{\t//q2 demos signify eof with a -1 size\n\t\tint len = ~0;\n\t\tVFS_WRITE(cls.demooutfile, &len, sizeof(len));\n\t}\n\telse\n#endif\n\tif (cls.demorecording == DPB_QUAKEWORLD)\n\t{\n\t\tSZ_Clear (&net_message);\n\t\tMSG_WriteLong (&net_message, -1);\t// -1 sequence means out of band\n\t\tMSG_WriteByte (&net_message, svc_disconnect);\n\t\tMSG_WriteString (&net_message, \"EndOfDemo\");\n\t\tCL_WriteDemoMessage (&net_message, 0);\n\t}\n\n// finish up\n\tVFS_CLOSE (cls.demooutfile);\n\tcls.demooutfile = NULL;\n\tcls.demorecording = false;\n\tCon_Printf (\"Completed demo\\n\");\n\n\tFS_FlushFSHashFull();\t//FIXME: single name\n}\n\nvoid CL_WriteRecordQ2DemoMessage(sizebuf_t *msg)\n{\t//q2 is really simple, and doesn't have timings in its demo format.\n\tint len = LittleLong (msg->cursize);\n\tVFS_WRITE (cls.demooutfile, &len, 4);\n\tVFS_WRITE (cls.demooutfile, msg->data, msg->cursize);\n\n}\n/*\n====================\nCL_WriteDemoMessage\n\nDumps the specified net message as part of initial mid-map demo writing.\n====================\n*/\nvoid CL_WriteRecordDemoMessage (sizebuf_t *msg, int seq)\n{\n\tint\t\tlen;\n\tint\t\ti;\n\tfloat\tfl;\n\tqbyte\tc;\n\n//Con_Printf(\"write: %ld bytes, %4.4f\\n\", msg->cursize, demtime);\n\n\tif (!cls.demorecording)\n\t\treturn;\n\n#ifdef NQPROT\n\tif (cls.demorecording == DPB_NETQUAKE)\n\t{\n\t\tlen = LittleLong (msg->cursize);\n\t\tVFS_WRITE(cls.demooutfile, &len, sizeof(len));\n\t\tfor (i=0 ; i<3 ; i++)\n\t\t{\n\t\t\tfloat f = LittleFloat (cl.playerview[0].viewangles[i]);\n\t\t\tVFS_WRITE(cls.demooutfile, &f, sizeof(f));\n\t\t}\n\t}\n\telse\n#endif\n\t{\n\t\tfl = LittleFloat(Sys_DoubleTime()-recdemostart);\n\t\tVFS_WRITE (cls.demooutfile, &fl, sizeof(fl));\n\n\t\tc = dem_read;\n\t\tVFS_WRITE (cls.demooutfile, &c, sizeof(c));\n\n\t\tlen = LittleLong (msg->cursize + 8);\n\t\tVFS_WRITE (cls.demooutfile, &len, 4);\n\n\t\ti = LittleLong(seq);\n\t\tVFS_WRITE (cls.demooutfile, &i, 4);\n\t\tVFS_WRITE (cls.demooutfile, &i, 4);\n\t}\n\tVFS_WRITE (cls.demooutfile, msg->data, msg->cursize);\n\n\tSZ_Clear(msg);\n\n\tif (record_flush.ival)\n\t\tVFS_FLUSH (cls.demooutfile);\n}\n\n\nvoid CL_WriteSetDemoMessage (void)\n{\n\tint\t\tlen;\n\tfloat\tfl;\n\tqbyte\tc;\n\n//Con_Printf(\"write: %ld bytes, %4.4f\\n\", msg->cursize, demtime);\n\n\tif (cls.demorecording != DPB_QUAKEWORLD)\n\t\treturn;\n\n\tfl = LittleFloat(Sys_DoubleTime()-recdemostart);\n\tVFS_WRITE (cls.demooutfile, &fl, sizeof(fl));\n\n\tc = dem_set;\n\tVFS_WRITE (cls.demooutfile, &c, sizeof(c));\n\n\tlen = LittleLong(cls.netchan.outgoing_sequence);\n\tVFS_WRITE (cls.demooutfile, &len, 4);\n\tlen = LittleLong(cls.netchan.incoming_sequence);\n\tVFS_WRITE (cls.demooutfile, &len, 4);\n\n\tif (record_flush.ival)\n\t\tVFS_FLUSH (cls.demooutfile);\n}\n\n\n/*\nrecord a single player game.\n*/\n#ifndef CLIENTONLY\n#ifdef MVD_RECORDING\nmvddest_t *SV_MVD_InitRecordFile (char *name);\nqboolean SV_MVD_Record (mvddest_t *dest);\n#endif\nvoid CL_RecordMap_f (void)\n{\n\tchar demoname[MAX_QPATH];\n\tchar mapname[MAX_QPATH];\n\tchar demoext[8];\n\n\tif (Cmd_Argc() < 3)\n\t{\n\t\tCon_Printf(\"%s: demoname mapname\\n\", Cmd_Argv(0));\n\t\treturn;\n\t}\n\tQ_strncpyz(demoname, Cmd_Argv(1), sizeof(demoname));\n\tQ_strncpyz(mapname, Cmd_Argv(2), sizeof(mapname));\n\tCL_Disconnect_f();\n\n\tSV_SpawnServer (mapname, NULL, false, false, 0);\n\tif (!sv.state)\n\t\treturn;\n\n#ifdef MVD_RECORDING\n\tif (svs.allocated_client_slots > 1)\n\t\tCOM_DefaultExtension(demoname, \".mvd\", sizeof(demoname));\n\telse\n#endif\n\t\tCOM_DefaultExtension(demoname, \".qwd\", sizeof(demoname));\n\n\tCOM_FileExtension(demoname, demoext, sizeof(demoext));\n\n#if defined(AVAIL_GZDEC) && !defined(CLIENTONLY)\n\t{\n\t\textern cvar_t sv_demoAutoCompress;\n\t\tif (sv_demoAutoCompress.ival)\n\t\t\tQ_strncatz(demoname, \".gz\", sizeof(demoname));\n\t}\n#endif\n\n#ifdef MVD_RECORDING\n\tif (!strcmp(demoext, \"mvd\"))\n\t{\n\t\tif (!SV_MVD_Record (SV_MVD_InitRecordFile(demoname)))\n\t\t\tCL_Disconnect_f();\n//\t\tchar buf[512];\n//\t\tCbuf_AddText(va(\"mvdrecord %s\\n\", COM_QuotedString(demoname, buf, sizeof(buf))), RESTRICT_LOCAL);\n\t}\n\telse\n#endif\n\t{\n#ifdef NQPROT\n\t\tif (!strcmp(demoext, \"dem\"))\n\t\t\tcls.demorecording = DPB_NETQUAKE;\n\t\telse\n#endif\n\t\tif (!strcmp(demoext, \"qwd\"))\n\t\t\tcls.demorecording = DPB_QUAKEWORLD;\n\t\telse\n\t\t{\n\t\t\tCL_Disconnect_f();\n\t\t\treturn;\n\t\t}\n\n\t\tcls.demooutfile = FS_OpenVFS (demoname, \"wb\", FS_GAME);\n\t\tif (!cls.demooutfile)\n\t\t{\n\t\t\tCL_Disconnect_f();\n\t\t\treturn;\n\t\t}\n#ifdef AVAIL_GZDEC\n\t\tif (!Q_strcasecmp(\".gz\", COM_GetFileExtension(demoname, NULL)))\n\t\t\tcls.demooutfile = FS_GZ_WriteFilter(cls.demooutfile, true, true);\n#endif\n#ifdef NQPROT\n\t\tif (cls.demorecording == DPB_NETQUAKE)\n\t\t\tVFS_PUTS(cls.demooutfile, \"-1\\n\");\n#endif\n\t\tCL_WriteSetDemoMessage();\n\t}\n}\n#endif\n\n//qw-specific serverdata\nstatic void CLQW_RecordServerData(sizebuf_t *buf, int *seq)\n{\n\textern\tchar gamedirfile[];\n\tunsigned int i;\n\tinfosync_t sync;\n\tchar serverinfostring[1024];\n\n// send the serverdata\n\tMSG_WriteByte (buf, svc_serverdata);\n\tif (cls.fteprotocolextensions&~PEXT1_HIDEPROTOCOLS)\t//maintain demo compatability\n\t{\n\t\tMSG_WriteLong (buf, PROTOCOL_VERSION_FTE1);\n\t\tMSG_WriteLong (buf, cls.fteprotocolextensions&~PEXT1_HIDEPROTOCOLS);\n\t}\n\tif (cls.fteprotocolextensions2)\t//maintain demo compatability\n\t{\n\t\tMSG_WriteLong (buf, PROTOCOL_VERSION_FTE2);\n\t\tMSG_WriteLong (buf, cls.fteprotocolextensions2);\n\t}\n\tif (cls.ezprotocolextensions1)\n\t{\n\t\tMSG_WriteLong (buf, PROTOCOL_VERSION_EZQUAKE1);\n\t\tMSG_WriteLong (buf, cls.ezprotocolextensions1);\n\t}\n\tMSG_WriteLong (buf, PROTOCOL_VERSION_QW);\n\tMSG_WriteLong (buf, cl.servercount);\n\tMSG_WriteString (buf, gamedirfile);\n\n\tif (cls.fteprotocolextensions2 & PEXT2_MAXPLAYERS)\n\t{\n\t\tMSG_WriteByte (buf, cl.allocated_client_slots);\n\t\tMSG_WriteByte (buf, cl.splitclients);\n\t\tfor (i = 0; i < cl.splitclients; i++)\n\t\t\tMSG_WriteByte (buf, cl.playerview[i].playernum);\n\t}\n\telse\n\t{\n\t\tfor (i = 0; i < cl.splitclients; i++)\n\t\t{\n\t\t\tif (cl.playerview[i].spectator)\n\t\t\t\tMSG_WriteByte (buf, cl.playerview[i].playernum | 128);\n\t\t\telse\n\t\t\t\tMSG_WriteByte (buf, cl.playerview[i].playernum);\n\t\t}\n\t\tif (cls.fteprotocolextensions & PEXT_SPLITSCREEN)\n\t\t\tMSG_WriteByte (buf, 128);\n\t}\n\n\t// send full levelname\n\tMSG_WriteString (buf, cl.levelname);\n\n\t// send the movevars\n\tMSG_WriteFloat(buf, movevars.gravity);\n\tMSG_WriteFloat(buf, movevars.stopspeed);\n\tMSG_WriteFloat(buf, movevars.maxspeed);\n\tMSG_WriteFloat(buf, movevars.spectatormaxspeed);\n\tMSG_WriteFloat(buf, movevars.accelerate);\n\tMSG_WriteFloat(buf, movevars.airaccelerate);\n\tMSG_WriteFloat(buf, movevars.wateraccelerate);\n\tMSG_WriteFloat(buf, movevars.friction);\n\tMSG_WriteFloat(buf, movevars.waterfriction);\n\tMSG_WriteFloat(buf, movevars.entgravity);\n\n\t// send server info string\n\tmemset(&sync, 0, sizeof(sync));\n\tInfoBuf_ToString(&cl.serverinfo, serverinfostring, sizeof(serverinfostring), NULL, NULL, NULL, &sync, NULL);\n\tMSG_WriteByte (buf, svc_stufftext);\n\tMSG_WriteString (buf, va(\"fullserverinfo \\\"%s\\\"\\n\", serverinfostring));\n\twhile(sync.numkeys)\n\t{\n\t\tchar *keyname = sync.keys[0].name;\n\t\tchar enckey[2048], encdata[2048];\n\t\tif (InfoBuf_EncodeString(keyname,strlen(keyname), enckey, sizeof(enckey)))\n\t\t{\n\t\t\tsize_t k;\n\t\t\tif (InfoBuf_FindKey(&cl.serverinfo, keyname, &k))\n\t\t\t{\n\t\t\t\tsize_t offset, chunk;\n\t\t\t\tqboolean final;\n\t\t\t\tfor (offset = 0 ; offset < cl.serverinfo.keys[k].size; offset += chunk)\n\t\t\t\t{\n\t\t\t\t\tchunk = cl.serverinfo.keys[k].size - offset;\n\t\t\t\t\tif (chunk > 1024)\n\t\t\t\t\t\tchunk = 1024;\n\n\t\t\t\t\tif (!InfoBuf_EncodeString(cl.serverinfo.keys[k].value, chunk, encdata, sizeof(encdata)))\n\t\t\t\t\t\tbreak;\t//shouldn't happen.\n\t\t\t\t\n\t\t\t\t\tif (buf->cursize > 512)\n\t\t\t\t\t\tCL_WriteRecordDemoMessage (buf, (*seq)++);\n\n\t\t\t\t\tfinal = (offset+chunk == cl.serverinfo.keys[k].size) && !cl.serverinfo.keys[k].partial;\n\t\t\t\t\tif (!offset && final)\n\t\t\t\t\t{\t//vanilla compat. we must just have a lot of data.\n\t\t\t\t\t\tMSG_WriteByte(buf, svc_serverinfo);\n\t\t\t\t\t\tMSG_WriteString(buf, enckey);\n\t\t\t\t\t\tMSG_WriteString(buf, encdata);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\t//awkward stuff.\n\t\t\t\t\t\tMSG_WriteByte(buf, svc_setinfo);\n\t\t\t\t\t\tMSG_WriteByte(buf, 255); //special meaning to say that this is a partial update\n\t\t\t\t\t\tMSG_WriteByte(buf, 255);\t//the serverinfo\n\t\t\t\t\t\tMSG_WriteLong(buf, (final?0x80000000:0)|offset);\n\t\t\t\t\t\tMSG_WriteString(buf, enckey);\n\t\t\t\t\t\tMSG_WriteString(buf, encdata);\n\t\t\t\t\t}\n\t\t\t\t\toffset += chunk;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tInfoSync_Remove(&sync, 0);\n\t}\n\tInfoSync_Clear(&sync);\n}\n\n#ifdef NQPROT\nvoid CLNQ_WriteServerData(sizebuf_t *buf)\t//for demo recording\n{\n\tunsigned int protmain;\n\tunsigned int protfl = 0;\n\tunsigned int i;\n\tconst char *val;\n\n\t//This is for compat with DP.\n\tval = InfoBuf_ValueForKey(&cl.serverinfo, \"*csprogs\");\n\tif (*val)\n\t{\n\t\tMSG_WriteByte(buf, svc_stufftext);\n\t\tMSG_WriteString(buf, va(\"csqc_progcrc \\\"%s\\\"\\n\", val));\n\t}\n\tval = InfoBuf_ValueForKey(&cl.serverinfo, \"*csprogssize\");\n\tif (*val)\n\t{\n\t\tMSG_WriteByte(buf, svc_stufftext);\n\t\tMSG_WriteString(buf, va(\"csqc_progsize \\\"%s\\\"\\n\", val));\n\t}\n\tval = InfoBuf_ValueForKey(&cl.serverinfo, \"*csprogsname\");\n\tif (*val)\n\t{\n\t\tMSG_WriteByte(buf, svc_stufftext);\n\t\tMSG_WriteString(buf, va(\"csqc_progname \\\"%s\\\"\\n\", val));\n\t}\n\n\tMSG_WriteByte(buf, svc_serverdata);\n\tif (cls.fteprotocolextensions&~PEXT1_HIDEPROTOCOLS)\n\t{\n\t\tMSG_WriteLong (buf, PROTOCOL_VERSION_FTE1);\n\t\tMSG_WriteLong (buf, cls.fteprotocolextensions&~PEXT1_HIDEPROTOCOLS);\n\t}\n\tif (cls.fteprotocolextensions2)\n\t{\n\t\tMSG_WriteLong (buf, PROTOCOL_VERSION_FTE2);\n\t\tMSG_WriteLong (buf, cls.fteprotocolextensions2);\n\t}\n\n\tif (cls.netchan.message.prim.anglesize == 2)\n\t\tprotfl |= RMQFL_SHORTANGLE;\n\tif (cls.netchan.message.prim.anglesize == 4)\n\t\tprotfl |= RMQFL_FLOATANGLE;\n\tswitch(cls.netchan.message.prim.coordtype)\n\t{\n\tcase COORDTYPE_FLOAT_32:\t protfl |= RMQFL_FLOATCOORD;\tbreak;\n\tcase COORDTYPE_FIXED_28_4:\t protfl |= RMQFL_INT32COORD;\tbreak;\n\tcase COORDTYPE_FIXED_16_8:\t protfl |= RMQFL_24BITCOORD;\tbreak;\n\tdefault:\t//err?\n\tcase COORDTYPE_FIXED_13_3:\tbreak;\n\t}\n\tswitch(cls.protocol_nq)\n\t{\n\tdefault:\n\tcase CPNQ_ID:\t\tprotmain = PROTOCOL_VERSION_NQ;\t\tbreak;\n\tcase CPNQ_NEHAHRA:\tprotmain = PROTOCOL_VERSION_NEHD;\tbreak;\n\tcase CPNQ_BJP1:\t\tprotmain = PROTOCOL_VERSION_BJP1;\tbreak;\n\tcase CPNQ_BJP2:\t\tprotmain = PROTOCOL_VERSION_BJP2;\tbreak;\n\tcase CPNQ_BJP3:\t\tprotmain = PROTOCOL_VERSION_BJP3;\tbreak;\n\tcase CPNQ_FITZ666:\tprotmain = protfl?PROTOCOL_VERSION_RMQ:PROTOCOL_VERSION_FITZ;\tbreak;\t//this might break .scale, fte doesn't care, other engines might.\n\tcase CPNQ_DP5:\t\tprotmain = PROTOCOL_VERSION_DP5;\tbreak;\n\tcase CPNQ_DP6:\t\tprotmain = PROTOCOL_VERSION_DP6;\tbreak;\n\tcase CPNQ_DP7:\t\tprotmain = PROTOCOL_VERSION_DP7;\tbreak;\n\t}\n\n\tMSG_WriteLong (buf, protmain);\n\tif (protmain == PROTOCOL_VERSION_RMQ)\n\t\tMSG_WriteLong (buf, protfl);\n\n\tif (cls.fteprotocolextensions2 & PEXT2_PREDINFO)\n\t\tMSG_WriteString(buf, FS_GetGamedir(true));\n\tMSG_WriteByte (buf, cl.allocated_client_slots);\n\tMSG_WriteByte (buf, cl.deathmatch?GAME_DEATHMATCH:GAME_COOP);\n\tMSG_WriteString (buf, cl.levelname);\n\n\tfor (i = 1; cl.model_name[i] && i < MAX_PRECACHE_MODELS; i++)\n\t\tMSG_WriteString (buf, cl.model_name[i]);\n\tMSG_WriteByte (buf, 0);\n\n\tfor (i = 1; cl.sound_name[i] && i < MAX_PRECACHE_SOUNDS ; i++)\n\t\tMSG_WriteString (buf, cl.sound_name[i]);\n\tMSG_WriteByte (buf, 0);\n}\n#endif\n\nvoid CL_Record_Baseline(sizebuf_t *buf, entity_state_t *state, unsigned int fitzbits)\n{\n\tunsigned int j;\n\tif (fitzbits & FITZ_B_LARGEMODEL)\n\t\tMSG_WriteShort (buf, state->modelindex);\n\telse\n\t\tMSG_WriteByte (buf, state->modelindex);\n\tif (fitzbits & FITZ_B_LARGEFRAME)\n\t\tMSG_WriteShort (buf, state->frame);\n\telse\n\t\tMSG_WriteByte (buf, state->frame);\n\tMSG_WriteByte (buf, state->colormap);\n\tMSG_WriteByte (buf, state->skinnum);\n\tfor (j=0 ; j<3 ; j++)\n\t{\n\t\tMSG_WriteCoord (buf, state->origin[j]);\n\t\tMSG_WriteAngle (buf, state->angles[j]);\n\t}\n\t\n\tif (fitzbits & FITZ_B_ALPHA)\n\t\tMSG_WriteByte(buf, state->trans);\n\tif (fitzbits & RMQFITZ_B_SCALE)\n\t\tMSG_WriteByte(buf, state->scale);\n}\n\n//nq+qw generic stuff.\nstatic int CL_Record_ParticlesStaticsBaselines(sizebuf_t *buf, int seq)\n{\n\tunsigned int i;\n\tentity_state_t *es;\n\n\t//particleeffectnum stuff\n\tfor (i = 1; i < MAX_SSPARTICLESPRE; i++)\n\t{\n\t\tif (!cl.particle_ssname[i])\n\t\t\tbreak;\n\t\tMSG_WriteByte(buf, svcfte_precache);\n\t\tMSG_WriteShort(buf, PC_PARTICLE | i);\n\t\tMSG_WriteString(buf, cl.particle_ssname[i]);\n\n\t\tif (buf->cursize > buf->maxsize/2)\n\t\t\tCL_WriteRecordDemoMessage (buf, seq++);\n\t}\n\n\t//custom tents (needed for hexen2, if nothing else)\n\tfor (i = 0; ; i++)\n\t{\n\t\tif (!CL_WriteCustomTEnt(buf, i))\n\t\t\tbreak;\n\n\t\tif (buf->cursize > buf->maxsize/2)\n\t\t\tCL_WriteRecordDemoMessage (buf, seq++);\n\t}\n\n// spawnstatic\n\n\tfor (i = 0; i < cl.num_statics; i++)\n\t{\n\t\tes = &cl_static_entities[i].state;\n\n#ifndef CLIENTONLY\t//FIXME\n\t\tif (cls.fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS)\n\t\t{\n\t\t\tMSG_WriteByte(buf, svcfte_spawnstatic2);\n\t\t\tSVFTE_EmitBaseline(es, false, buf, cls.fteprotocolextensions2, cls.ezprotocolextensions1);\n\t\t}\n\t\t//else if (cls.fteprotocolextensions & PEXT_SPAWNSTATIC2) //qw deltas\n\t\telse\n#endif\n\t\t{\n\t\t\tunsigned int bits = 0;\n#ifdef NQPROT\n\t\t\tif (es->modelindex > 255)\n\t\t\t\tbits |= FITZ_B_LARGEMODEL;\n\t\t\tif (es->frame > 255)\n\t\t\t\tbits |= FITZ_B_LARGEFRAME;\n\t\t\tif (es->trans != 255)\n\t\t\t\tbits |= FITZ_B_ALPHA;\n\t\t\tif (es->scale != 16)\n\t\t\t\tbits |= RMQFITZ_B_SCALE;\n\t\t\tif (cls.protocol == CP_NETQUAKE && CPNQ_IS_BJP)\n\t\t\t{\n\t\t\t\tMSG_WriteByte (buf, svc_spawnstatic);\n\t\t\t\tbits = FITZ_B_LARGEMODEL;\t//bjp always uses shorts for models.\n\t\t\t}\n\t\t\telse if (cls.protocol == CP_NETQUAKE && cls.protocol_nq == CPNQ_FITZ666 && bits)\n\t\t\t{\n\t\t\t\tMSG_WriteByte (buf, svcfitz_spawnstatic2);\n\t\t\t\tMSG_WriteByte (buf, bits);\n\t\t\t}\n//\t\t\telse if (baselinetype2 >= CPNQ_DP5 && baselinetype2 <= CPNQ_DP7 && (bits & (FITZ_B_LARGEMODEL|FITZ_B_LARGEFRAME)))\n//\t\t\t{\n//\t\t\t\tMSG_WriteByte (buf, svcdp_spawnstatic2);\n//\t\t\t\tbits = FITZ_B_LARGEMODEL|FITZ_B_LARGEFRAME;\t//dp's baseline2 always has these (regular baseline is unmodified)\n//\t\t\t}\n\t\t\telse\n#endif\n\t\t\t{\n\t\t\t\t//classic protocol\n\t\t\t\tMSG_WriteByte (buf, svc_spawnstatic);\n\t\t\t\tbits = 0;\n\t\t\t}\n\t\t\tCL_Record_Baseline(buf, es, bits);\n\t\t}\n\n\t\tif (buf->cursize > buf->maxsize/2)\n\t\t\tCL_WriteRecordDemoMessage (buf, seq++);\n\t}\n\n// FIXME: static sounds\n\t// static sounds are skipped in demos, life is hard\n\n// baselines\n\n\tfor (i = 0; i < cl_baselines_count; i++)\n\t{\n\t\tes = cl_baselines + i;\n\n\t\tif (memcmp(es, &nullentitystate, sizeof(nullentitystate)))\n\t\t{\n#ifndef CLIENTONLY\t//FIXME\n\t\t\tif (cls.fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS)\n\t\t\t{\n\t\t\t\tMSG_WriteByte(buf, svcfte_spawnbaseline2);\n\t\t\t\tSVFTE_EmitBaseline(es, true, buf, cls.fteprotocolextensions2, cls.ezprotocolextensions1);\n\t\t\t}\n\t\t\t//else if (cls.fteprotocolextensions & PEXT_SPAWNSTATIC2) //qw deltas\n\t\t\telse\n#endif\n\t\t\t{\n\t\t\t\tunsigned int fitzbits = 0;\t//must take some consistent form for this to work\n#ifdef NQPROT\n\t\t\t\tif (es->modelindex > 255)\n\t\t\t\t\tfitzbits |= FITZ_B_LARGEMODEL;\n\t\t\t\tif (es->frame > 255)\n\t\t\t\t\tfitzbits |= FITZ_B_LARGEFRAME;\n\t\t\t\tif (es->trans != 255)\n\t\t\t\t\tfitzbits |= FITZ_B_ALPHA;\n\t\t\t\tif (es->scale != 16)\n\t\t\t\t\tfitzbits |= RMQFITZ_B_SCALE;\n\t\t\t\tif (cls.protocol == CP_NETQUAKE && CPNQ_IS_BJP)\n\t\t\t\t{\n\t\t\t\t\tMSG_WriteByte (buf, svc_spawnbaseline);\n\t\t\t\t\tfitzbits = FITZ_B_LARGEMODEL;\t//bjp always uses shorts for models.\n\t\t\t\t}\n\t\t\t\telse if (cls.protocol == CP_NETQUAKE && cls.protocol_nq == CPNQ_FITZ666 && fitzbits)\n\t\t\t\t{\n\t\t\t\t\tMSG_WriteByte (buf, svcfitz_spawnbaseline2);\n\t\t\t\t\tMSG_WriteByte (buf, fitzbits);\n\t\t\t\t}\n\t\t\t\telse if (cls.protocol == CP_NETQUAKE && CPNQ_IS_DP && (fitzbits & (FITZ_B_LARGEMODEL|FITZ_B_LARGEFRAME)))\n\t\t\t\t{\n\t\t\t\t\tMSG_WriteByte (buf, svcdp_spawnbaseline2);\n\t\t\t\t\tfitzbits = FITZ_B_LARGEMODEL|FITZ_B_LARGEFRAME;\t//dp's baseline2 always has these (regular baseline is unmodified)\n\t\t\t\t}\n\t\t\t\telse\n#endif\n\t\t\t\t{\n\t\t\t\t\tMSG_WriteByte (buf,svc_spawnbaseline);\n\t\t\t\t\tfitzbits = 0;\n\t\t\t\t}\n\t\t\t\tMSG_WriteEntity (buf, i);\n\n\t\t\t\tCL_Record_Baseline(buf, es, fitzbits);\n\t\t\t}\n\t\t\tif (buf->cursize > buf->maxsize/2)\n\t\t\t\tCL_WriteRecordDemoMessage (buf, seq++);\n\t\t}\n\t}\n\n\treturn seq;\n}\nstatic int CL_Record_Lightstyles(sizebuf_t *buf, int seq)\n{\n\tunsigned int i;\n// send all current light styles\n\tfor (i=0 ; i<cl_max_lightstyles ; i++)\n\t{\n\t\tif (i >= MAX_STANDARDLIGHTSTYLES)\n\t\t\tif (!*cl_lightstyle[i].map)\n\t\t\t\tcontinue;\n\n#ifdef PEXT_LIGHTSTYLECOL\n\t\tif ((cls.fteprotocolextensions & PEXT_LIGHTSTYLECOL) && (cl_lightstyle[i].colours[0]!=1||cl_lightstyle[i].colours[1]!=1||cl_lightstyle[i].colours[2]!=1) && *cl_lightstyle[i].map)\n\t\t{\n\t\t\tMSG_WriteByte (buf, svcfte_lightstylecol);\n\t\t\tMSG_WriteByte (buf, (unsigned char)i);\n\t\t\tMSG_WriteByte (buf, 0x87);\n\t\t\tMSG_WriteShort (buf, cl_lightstyle[i].colours[0]*1024);\n\t\t\tMSG_WriteShort (buf, cl_lightstyle[i].colours[1]*1024);\n\t\t\tMSG_WriteShort (buf, cl_lightstyle[i].colours[2]*1024);\n\t\t\tMSG_WriteString (buf, cl_lightstyle[i].map);\n\t\t}\n\t\telse\n#endif\n\t\t{\n\t\t\tMSG_WriteByte (buf, svc_lightstyle);\n\t\t\tMSG_WriteByte (buf, (unsigned char)i);\n\t\t\tMSG_WriteString (buf, cl_lightstyle[i].map);\n\t\t}\n\n\t\tif (buf->cursize > buf->maxsize/2)\n\t\t\tCL_WriteRecordDemoMessage (buf, seq++);\n\t}\n\treturn seq;\n}\n\n// send current status of all other players\nstatic int CL_RecordInitialPlayers(sizebuf_t *buf, int seq, qboolean isnq)\n{\n\tchar info[MAX_LOCALINFO_STRING];\n\tplayer_info_t *player;\n\tint i;\n\tfor (i = 0; i < cl.allocated_client_slots; i++)\n\t{\n\t\tplayer = cl.players + i;\n\n\t\tif (buf->cursize > buf->maxsize/2)\n\t\t\tCL_WriteRecordDemoMessage (buf, seq++);\n\n\t\tif (player->frags != 0)\n\t\t{\n\t\t\tMSG_WriteByte (buf, svc_updatefrags);\n\t\t\tMSG_WriteByte (buf, i);\n\t\t\tMSG_WriteShort(buf, player->frags);\n\t\t}\n\t\tif (isnq)\n\t\t{\n\t\t\tif (!*player->name)\n\t\t\t\tcontinue;\n\t\t\tMSG_WriteByte (buf, svc_updatename);\n\t\t\tMSG_WriteByte (buf, i);\n\t\t\tMSG_WriteString (buf, player->name);\n\n\t\t\tMSG_WriteByte (buf, svc_updatecolors);\n\t\t\tMSG_WriteByte (buf, i);\n\t\t\tMSG_WriteByte (buf, player->rtopcolor*16+player->rbottomcolor);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (player->ping != 0)\n\t\t\t{\n\t\t\t\tMSG_WriteByte (buf, svc_updateping);\n\t\t\t\tMSG_WriteByte (buf, i);\n\t\t\t\tMSG_WriteShort (buf, player->ping);\n\t\t\t}\n\n\t\t\tif (player->pl != 0)\n\t\t\t{\n\t\t\t\tMSG_WriteByte (buf, svc_updatepl);\n\t\t\t\tMSG_WriteByte (buf, i);\n\t\t\t\tMSG_WriteByte (buf, player->pl);\n\t\t\t}\n\n\t\t\tif (player->userinfo.numkeys)\n\t\t\t{\n\t\t\t\tMSG_WriteByte (buf, svc_updateentertime);\n\t\t\t\tMSG_WriteByte (buf, i);\n\t\t\t\tMSG_WriteFloat (buf, realtime - player->realentertime);\t//seconds since\n\t\t\t}\n\n\t\t\tif (player->userinfo.numkeys)\n\t\t\t{\n\t\t\t\tInfoBuf_ToString(&player->userinfo, info, min(buf->maxsize-buf->cursize-6, sizeof(info)), basicuserinfos, NULL, NULL, NULL, NULL);\n\t\t\t\tMSG_WriteByte (buf, svc_updateuserinfo);\n\t\t\t\tMSG_WriteByte (buf, i);\n\t\t\t\tMSG_WriteLong (buf, player->userid);\n\t\t\t\tMSG_WriteString (buf, info);\n\n\t\t\t\t//spam svc_setinfo for all the infos that didn't fit.\n\t\t\t}\n\t\t}\n\t}\n\treturn seq;\n}\nstatic int CL_RecordInitialStats(sizebuf_t *buf, int seq, qboolean isnq)\n{\n\tsize_t seat, i;\n\tfor (seat = 0; seat < cl.splitclients; seat++)\n\t{\n\t\t//higher stats should be 0 and thus not be sent, if not valid.\n\t\tfor (i = 0; i < MAX_CL_STATS; i++)\n\t\t{\n\t\t\tif (cl.playerview[seat].stats[i] || cl.playerview[seat].statsf[i])\n\t\t\t{\n\t\t\t\tdouble fs = cl.playerview[seat].statsf[i];\n\t\t\t\tdouble is = cl.playerview[seat].stats[i];\n\t\t\t\tif (seat)\n\t\t\t\t{\n\t\t\t\t\tMSG_WriteByte (buf, svcfte_choosesplitclient);\n\t\t\t\t\tMSG_WriteByte (buf, seat);\n\t\t\t\t}\n\t\t\t\tif ((int)fs == is)\n\t\t\t\t{\n\t\t\t\t\tMSG_WriteByte (buf, isnq?svcnq_updatestatlong:svcqw_updatestatlong);\n\t\t\t\t\tMSG_WriteByte (buf, i);\n\t\t\t\t\tMSG_WriteLong (buf, is);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tMSG_WriteByte (buf, svcfte_updatestatfloat);\n\t\t\t\t\tMSG_WriteByte (buf, i);\n\t\t\t\t\tMSG_WriteLong (buf, fs);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (cl.playerview[seat].statsstr[i])\n\t\t\t{\n\t\t\t\tif (seat)\n\t\t\t\t{\n\t\t\t\t\tMSG_WriteByte (buf, svcfte_choosesplitclient);\n\t\t\t\t\tMSG_WriteByte (buf, seat);\n\t\t\t\t}\n\t\t\t\tMSG_WriteByte (buf, svcfte_updatestatstring);\n\t\t\t\tMSG_WriteByte (buf, i);\n\t\t\t\tMSG_WriteString (buf, cl.playerview[seat].statsstr[i]);\n\t\t\t}\n\n\t\t\tif (buf->cursize > buf->maxsize/2)\n\t\t\t\tCL_WriteRecordDemoMessage (buf, seq++);\n\t\t}\n\t}\n\treturn seq;\n}\n\nconst char *Get_Q2ConfigString(int i);\n\n/*\n====================\nCL_Record_f\n\nrecord <demoname> <server>\n====================\n*/\nvoid CL_Record_f (void)\n{\n\tint\t\tc;\n\tchar\tname[MAX_OSPATH];\n\tsizebuf_t\tbuf;\n\tqbyte\tbuf_data[MAX_OVERALLMSGLEN];\n\tint n, i;\n\tchar *s, *p, *fname;\n\textern\tchar gamedirfile[];\n\tint seq = 1;\n\tconst char *defaultext;\n\n\tc = Cmd_Argc();\n\tif (c > 2)\n\t{\n#ifndef CLIENTONLY\n\t\tCL_RecordMap_f();\n#else\n\t\tCon_Printf (\"record <demoname>\\n\");\n#endif\n\t\treturn;\n\t}\n\n\tif (cls.state != ca_active)\n\t{\n\t\tCon_Printf (\"You must either be connected to record, or specify a map name to load.\\n\");\n\t\tCon_Printf (\"%s: <demoname> <mapname>\\n\", Cmd_Argv(0));\n\t\treturn;\n\t}\n\n#ifdef Q2CLIENT\n\tif (cls.protocol == CP_QUAKE2)\n\t\tdefaultext = \".dm2\";\n\telse\n#endif\n\t\tif (cls.protocol == CP_NETQUAKE && !CPNQ_IS_DP)\n\t\tdefaultext = \".dem\";\n\telse if (cls.protocol == CP_QUAKEWORLD)\n\t\tdefaultext = \".qwd\";\n\telse\n\t{\n\t\tCon_Printf(\"Unable to record mid-map - try a different network protocol\\n\");\n\t\treturn;\n\t}\n\n\tif (cls.demorecording)\n\t\tCL_Stop_f();\n\n\tif (c == 2)\t//user supplied a name\n\t{\n\t\tfname = Cmd_Argv(1);\n\t\t\n\t\t// See if the users supplied their own filename...\n\t\ts = strrchr(fname, '.');\n\t\t\n\t\t// They did.\n\t\tif ( s != NULL ) {\n\t\t\tif (!Q_strcasecmp(s, defaultext))\n\t\t\t\t*s = 0;\t//hack away that extension that they added, so that we don't get dupes.\n\t\t}\n\t}\n\telse\n\t{\t//automagically generate a name\n\t\tfname = TP_GenerateDemoName();\n\t}\n\n\twhile((p = strstr(fname, \"..\")))\n\t{\n\t\tp[0] = '_';\n\t\tp[1] = '_';\n\t}\n\n\t// Make sure the filename doesn't contain illegal characters\n\tp=fname;\n#ifdef MVD_RECORDING\n\tif (*sv_demoDir.string && !strncmp(p, sv_demoDir.string, strlen(sv_demoDir.string)) && p[strlen(sv_demoDir.string)] == '/')\n\t\tp += strlen(sv_demoDir.string)+1;\t//allow a demos/ prefix (primarily because of autodemos)\n#endif\n\tfor ( ; *p ; p++)\n\t{\n\t\tchar c;\n\t\t*p &= 0x7F;\t\t// strip high bit\n\t\tc = *p;\n\t\tif (c<=' ' || c=='?' || c=='*' || (c!=2&&(c=='\\\\' || c=='/')) || c==':'\n\t\t\t|| c=='<' || c=='>' || c=='\"' || c=='.')\n\t\t\t*p = '_';\n\t}\n\tQ_strncpyz(name, fname, sizeof(name)-4-strlen(defaultext));\n\n#if defined(AVAIL_GZDEC) && !defined(CLIENTONLY) && defined(MVD_RECORDING)\n\t{\n\t\textern cvar_t sv_demoAutoCompress;\n\t\tif (sv_demoAutoCompress.ival == 1 || !*sv_demoAutoCompress.string)\n\t\t\tdefaultext = va(\"%s.gz\", defaultext);\n\t}\n#endif\n\n//make a unique name (unless the user specified it).\n\tQ_strncatz(name, defaultext, sizeof(name));\n\tif (c != 2)\n\t{\n\t\tvfsfile_t *f;\n\n\t\tf = FS_OpenVFS (name, \"rb\", FS_GAME);\n\t\tif (f)\n\t\t{\n\t\t\t//remove the extension again\n\t\t\tQ_strncpyz(name, fname, sizeof(name)-4-strlen(defaultext));\n\t\t\tp = name + strlen(name);\n\t\t\tstrcat(p, \"_XX\");\n\t\t\tstrcat(p, defaultext);\n\t\t\tp++;\n\t\t\ti = 0;\n\t\t\tdo\n\t\t\t{\n\t\t\t\tVFS_CLOSE (f);\n\t\t\t\tp[0] = ((i/10)%10) + '0';\n\t\t\t\tp[1] = (i%10) + '0';\n\t\t\t\tf = FS_OpenVFS (name, \"rb\", FS_GAME);\n\t\t\t\ti++;\n\t\t\t} while (f && i < 100);\n\t\t}\n\t}\n\n//\n// open the demo file\n//\n\tcls.demooutfile = FS_OpenVFS (name, \"wb\", FS_GAMEONLY);\n\tif (!cls.demooutfile)\n\t{\n\t\tCon_Printf (\"ERROR: couldn't open.\\n\");\n\t\treturn;\n\t}\n\n#ifdef AVAIL_GZDEC\n\tif (!Q_strcasecmp(\".gz\", COM_GetFileExtension(name, NULL)))\n\t\tcls.demooutfile = FS_GZ_WriteFilter(cls.demooutfile, true, true);\n#endif\n\n\tcls.demohadkeyframe = false;\n\n\tCon_Printf (\"recording to %s.\\n\", name);\n\n\trecdemostart = Sys_DoubleTime();\n\n/*-------------------------------------------------*/\n\n\tmemset(&buf, 0, sizeof(buf));\n\tbuf.data = buf_data;\n\tbuf.maxsize = sizeof(buf_data);\n\tbuf.prim = cls.netchan.netprim;\n\tswitch(cls.protocol)\n\t{\n\tcase CP_QUAKEWORLD:\n\t\tif (!cls.fteprotocolextensions && !cls.fteprotocolextensions2)\n\t\t\tbuf.maxsize = MAX_QWMSGLEN;\t//poo compatibility... :P\n\n\t\tcls.demorecording = DPB_QUAKEWORLD;\n\n\t\tCLQW_RecordServerData(&buf, &seq);\n\n\t\t// send music\n\t\tMedia_WriteCurrentTrack(&buf);\n\n\t\t//paknames\n\t\t{\n\t\t\tchar buffer[1024];\n\t\t\tFS_GetPackNames(buffer, sizeof(buffer), 2, true); /*retain extensions, or we'd have to assume pk3*/\n\t\t\tif (*buffer)\n\t\t\t{\n\t\t\t\tMSG_WriteByte(&buf, svc_stufftext);\n\t\t\t\tSZ_Write(&buf, \"//paknames \", 11);\n\t\t\t\tSZ_Write(&buf, buffer, strlen(buffer));\n\t\t\t\tMSG_WriteString(&buf, \"\\n\");\n\t\t\t}\n\t\t}\n\t\t//Paks\n\t\t{\n\t\t\tchar buffer[1024];\n\t\t\tFS_GetPackHashes(buffer, sizeof(buffer), false);\n\t\t\tif (*buffer)\n\t\t\t{\n\t\t\t\tMSG_WriteByte(&buf, svc_stufftext);\n\t\t\t\tSZ_Write(&buf, \"//paks \", 7);\n\t\t\t\tSZ_Write(&buf, buffer, strlen(buffer));\n\t\t\t\tMSG_WriteString(&buf, \"\\n\");\n\t\t\t}\n\t\t}\n\t\t\n\t\t//FIXME: //at\n\t\t//FIXME: //wps\n\t\t//FIXME: //it\n\n\n\t\tMSG_WriteByte (&buf, svc_setpause);\n\t\tMSG_WriteByte (&buf, !!cl.paused);\n\n#ifdef PEXT_SETVIEW\n\t\tif (cl.playerview[0].viewentity != cl.playerview[0].playernum+1)\t//tell the player if we have a different view entity\n\t\t{\n\t\t\tMSG_WriteByte (&buf, svc_setview);\n\t\t\tMSG_WriteEntity (&buf, cl.playerview[0].viewentity);\n\t\t}\n#endif\n\t\t// flush packet\n\t\tCL_WriteRecordDemoMessage (&buf, seq++);\n\n\t// soundlist\n\t\tMSG_WriteByte (&buf, svc_soundlist);\n\t\tMSG_WriteByte (&buf, 0);\n\n\t\tn = 0;\n\t\ts = cl.sound_name[n+1];\n\t\twhile (s && *s)\n\t\t{\n\t\t\tMSG_WriteString (&buf, s);\n\t\t\tif (buf.cursize > buf.maxsize/2 && (n&0xff))\n\t\t\t{\n\t\t\t\tMSG_WriteByte (&buf, 0);\n\t\t\t\tMSG_WriteByte (&buf, n);\n\t\t\t\tCL_WriteRecordDemoMessage (&buf, seq++);\n\t\t\t\n\t\t\t\tif (n + 1 > 0xff)\n\t\t\t\t{\n\t\t\t\t\tMSG_WriteByte (&buf, svcfte_soundlistshort);\n\t\t\t\t\tMSG_WriteShort (&buf, n + 1);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tMSG_WriteByte (&buf, svc_soundlist);\n\t\t\t\t\tMSG_WriteByte (&buf, n + 1);\n\t\t\t\t}\n\t\t\t}\n\t\t\tn++;\n\t\t\ts = cl.sound_name[n+1];\n\t\t}\n\t\tif (buf.cursize)\n\t\t{\n\t\t\tMSG_WriteByte (&buf, 0);\n\t\t\tMSG_WriteByte (&buf, 0);\n\t\t\tCL_WriteRecordDemoMessage (&buf, seq++);\n\t\t}\n\n\t\t//FIXME: vweps\n\n\t// modellist\n\t\tMSG_WriteByte (&buf, svc_modellist);\n\t\tMSG_WriteByte (&buf, 0);\n\n\t\tn = 0;\n\t\ts = cl.model_name[n+1];\n\t\twhile (s && *s)\n\t\t{\n\t\t\tMSG_WriteString (&buf, s);\n\t\t\tif (buf.cursize > buf.maxsize/2 && (n&0xff))\n\t\t\t{\n\t\t\t\tMSG_WriteByte (&buf, 0);\n\t\t\t\tMSG_WriteByte (&buf, n);\n\t\t\t\tCL_WriteRecordDemoMessage (&buf, seq++);\n\n\t\t\t\tif (n + 1 > 0xff)\n\t\t\t\t{\n\t\t\t\t\tMSG_WriteByte (&buf, svcfte_modellistshort);\n\t\t\t\t\tMSG_WriteShort (&buf, n + 1);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tMSG_WriteByte (&buf, svc_modellist);\n\t\t\t\t\tMSG_WriteByte (&buf, n + 1);\n\t\t\t\t}\n\t\t\t}\n\t\t\tn++;\n\t\t\ts = cl.model_name[n+1];\n\t\t}\n\t\tif (buf.cursize)\n\t\t{\n\t\t\tMSG_WriteByte (&buf, 0);\n\t\t\tMSG_WriteByte (&buf, 0);\n\t\t\tCL_WriteRecordDemoMessage (&buf, seq++);\n\t\t}\n\n\t\tseq = CL_Record_ParticlesStaticsBaselines(&buf, seq);\n\n\t\tMSG_WriteByte (&buf, svc_stufftext);\n\t\tMSG_WriteString (&buf, va(\"cmd spawn %i\\n\", cl.servercount) );\n\n\t\tif (buf.cursize)\n\t\t\tCL_WriteRecordDemoMessage (&buf, seq++);\n\t\tseq = CL_RecordInitialPlayers(&buf, seq, false);\n\t\tseq = CL_Record_Lightstyles(&buf, seq);\n\t\tseq = CL_RecordInitialStats(&buf, seq, false);\n\n\t\t// get the client to check and download skins\n\t\t// when that is completed, a begin command will be issued\n\t\tMSG_WriteByte (&buf, svc_stufftext);\n\t\tMSG_WriteString (&buf, \"skins\\n\");\n\n\t\tCL_WriteRecordDemoMessage (&buf, seq++);\n\n\t\tCL_WriteSetDemoMessage();\n\n\t\t//FIXME: make sure the deltas are reset\n\n\t\t// done\n\t\tbreak;\n#if defined(Q2CLIENT) && defined(Q2SERVER)\n\tcase CP_QUAKE2:\n\t\tcls.demorecording = DPB_QUAKE2;\n\n\t\tMSG_WriteByte (&buf, svcq2_serverdata);\n\t\tif (cls.fteprotocolextensions)\t//maintain demo compatability\n\t\t{\n\t\t\tMSG_WriteLong (&buf, PROTOCOL_VERSION_FTE1);\n\t\t\tMSG_WriteLong (&buf, cls.fteprotocolextensions);\n\t\t}\n\t\tif (cls.fteprotocolextensions2)\t//maintain demo compatability\n\t\t{\n\t\t\tMSG_WriteLong (&buf, PROTOCOL_VERSION_FTE2);\n\t\t\tMSG_WriteLong (&buf, cls.fteprotocolextensions2);\n\t\t}\n\t\tMSG_WriteLong (&buf, cls.protocol_q2);\n\t\tMSG_WriteLong (&buf, 0x80000000 + cl.servercount);\n\t\tMSG_WriteByte (&buf, 1);\t//attract loop\n\t\tif (cls.protocol_q2 == PROTOCOL_VERSION_Q2EX)\n\t\t\tMSG_WriteByte (&buf, cl.q2svnetrate);\t//tick rate\n\t\tMSG_WriteString (&buf, gamedirfile);\n\t\tif (cls.protocol_q2 == PROTOCOL_VERSION_Q2EX && cl.playerview[0].playernum != -1 && cl.splitclients!=1)\n\t\t{\n\t\t\tMSG_WriteShort (&buf, -2);\n\t\t\tMSG_WriteShort (&buf, cl.splitclients);\n\t\t\tfor (i = 0; i < cl.splitclients; i++)\n\t\t\t\tMSG_WriteShort (&buf, cl.playerview[i].playernum);\n\t\t}\n\t\telse\n\t\t\tMSG_WriteShort (&buf, cl.playerview[0].playernum);\n\t\tMSG_WriteString (&buf, cl.levelname);\n\n\t\tfor (i = 0; i < Q2MAX_CONFIGSTRINGS; i++)\n\t\t{\n\t\t\tconst char *cs = Get_Q2ConfigString(i);\n\t\t\tif (buf.cursize + 4 + strlen(cs) > buf.maxsize)\n\t\t\t{\n\t\t\t\tCL_WriteRecordQ2DemoMessage (&buf);\n\t\t\t\tSZ_Clear (&buf);\n\t\t\t}\n\n\t\t\tMSG_WriteByte (&buf, svcq2_configstring);\n\t\t\tMSG_WriteShort (&buf, i);\n\t\t\tMSG_WriteString (&buf, cs);\n\t\t}\n\n\t\tCLQ2_WriteDemoBaselines(&buf);\n\n\t\tMSG_WriteByte (&buf, svcq2_stufftext);\n\t\tMSG_WriteString (&buf, \"precache\\n\");\n\t\tCL_WriteRecordQ2DemoMessage (&buf);\n\t\tbreak;\n#endif\n#ifdef NQPROT\n\tcase CP_NETQUAKE:\n\t\t//csqc stuff\n\t\tcls.demorecording = DPB_NETQUAKE;\n\t\tVFS_WRITE(cls.demooutfile, \"-1\\n\", 3);\t//stupid lame header thing.\n\n\t\tCLNQ_WriteServerData(&buf);\n\t\tMSG_WriteByte (&buf, svc_setpause);\n\t\tMSG_WriteByte (&buf, !!cl.paused);\n\t\tMSG_WriteByte (&buf, svc_setview);\n\t\tMSG_WriteEntity (&buf, cl.playerview[0].viewentity);\n\n\t\tMSG_WriteByte (&buf, svcnq_signonnum);\n\t\tMSG_WriteByte (&buf, 1);\n\t\tCL_WriteRecordDemoMessage (&buf, seq++);\n\n\t\tseq = CL_Record_ParticlesStaticsBaselines(&buf, seq);\n\t\t//fixme: brushes...\n\t\tMSG_WriteByte (&buf, svcnq_signonnum);\n\t\tMSG_WriteByte (&buf, 2);\n\t\tCL_WriteRecordDemoMessage (&buf, seq++);\n\t\tseq = CL_RecordInitialPlayers(&buf, seq, true);\n\t\tseq = CL_Record_Lightstyles(&buf, seq);\n\t\tseq = CL_RecordInitialStats(&buf, seq, true);\n\t\tMSG_WriteByte (&buf, svcnq_signonnum);\n\t\tMSG_WriteByte (&buf, 3);\n\t\tCL_WriteRecordDemoMessage (&buf, seq++);\n\t\tbreak;\n#endif\n\tdefault:\n\t\t//this should have been caught earlier\n\t\tCon_Printf(\"Unable to begin demo recording with this network protocol\\n\");\n\t\tCL_Stop_f();\n\t\tbreak;\n\t}\n\n\tcl.validsequence = 0; //ask for a sequence reset.\n\n\tif (cls.fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS)\n\t\tif (cl.numackframes < sizeof(cl.ackframes)/sizeof(cl.ackframes[0]))\n\t\t\tcl.ackframes[cl.numackframes++] = -1;\n}\n\nstatic int QDECL CompleteDemoList (const char *name, qofs_t flags, time_t mtime, void *parm, searchpathfuncs_t *spath)\n{\n\tstruct xcommandargcompletioncb_s *ctx = parm;\n\tconst char *ext = NULL;\n\text = COM_GetFileExtension(name, ext);\n\tif (!Q_strcasecmp(ext, \".gz\"))\n\t\text = COM_GetFileExtension(name, ext);\n\tif (\n#ifdef NQPROT\n\t\t!Q_strcasecmp(ext, \".dem\") || !Q_strcasecmp(ext, \".dem.gz\") ||\n#endif\n#ifdef Q2CLIENT\n\t\t!Q_strcasecmp(ext, \".dm2\") || !Q_strcasecmp(ext, \".dm2.gz\") ||\n#endif\n\t\t!Q_strcasecmp(ext, \".qwd\") || !Q_strcasecmp(ext, \".qwd.gz\") ||\n\t\t!Q_strcasecmp(ext, \".mvd\") || !Q_strcasecmp(ext, \".mvd.gz\"))\n//FIXME: enumerate .zip and .dz files too.\n\t{\n\t\tctx->cb(name, NULL, NULL, ctx);\n\t}\n\treturn true;\n}\nvoid CL_DemoList_c(int argn, const char *partial, struct xcommandargcompletioncb_s *ctx)\n{\n\tif (argn == 1)\n\t\tCOM_EnumerateFiles(va(\"%s*\", partial), CompleteDemoList, ctx);\n}\n/*\n====================\nCL_ReRecord_f\n\nrecord <demoname>\n====================\n*/\nvoid CL_ReRecord_f (void)\n{\n\tint\t\tc;\n\tchar\tname[MAX_OSPATH];\n\tchar *s;\n\n\tc = Cmd_Argc();\n\tif (c != 2)\n\t{\n\t\tCon_Printf (\"rerecord <demoname>\\n\");\n\t\treturn;\n\t}\n\n\tif (!*cls.servername) {\n\t\tCon_Printf(\"No server to reconnect to...\\n\");\n\t\treturn;\n\t}\n\n\tif (cls.demorecording)\n\t\tCL_Stop_f();\n\n\ts = Cmd_Argv(1);\n\tif (strstr(s, \"..\"))\n\t{\n\t\tCon_Printf (\"Relative paths not allowed.\\n\");\n\t\treturn;\n\t}\n\n\tQ_snprintfz (name, sizeof(name), \"%s\", s);\n\n\tCL_Disconnect(NULL);\n\n//\n// open the demo file\n//\n\tswitch (cls.protocol)\n\t{\n\tdefault:\n\tcase CP_QUAKEWORLD:\n\t\tcls.demorecording = DPB_QUAKEWORLD;\n\t\tCOM_RequireExtension (name, \".qwd\", sizeof(name));\n\t\tbreak;\n#ifdef NQPROT\n\tcase CP_NETQUAKE:\n\t\tcls.demorecording = DPB_NETQUAKE;\n\t\tCOM_RequireExtension (name, \".dem\", sizeof(name));\n\t\tbreak;\n#endif\n#ifdef Q2CLIENT\n\tcase CP_QUAKE2:\n\t\tcls.demorecording = DPB_QUAKE2;\n\t\tCOM_RequireExtension (name, \".dm2\", sizeof(name));\n\t\tbreak;\n#endif\n\t}\n\n\tcls.demooutfile = FS_OpenVFS (name, \"wb\", FS_GAME);\n\tif (!cls.demooutfile)\n\t{\n\t\tCon_Printf (\"ERROR: couldn't open.\\n\");\n\t\tcls.demorecording = DPB_NONE;\n\t\treturn;\n\t}\n\n#ifdef AVAIL_GZDEC\n\tif (!Q_strcasecmp(\".gz\", COM_GetFileExtension(name, NULL)))\n\t\tcls.demooutfile = FS_GZ_WriteFilter(cls.demooutfile, true, true);\n#endif\n\n\tCon_Printf (\"recording to %s.\\n\", name);\n\n#ifdef NQPROT\n\tif (cls.demorecording == DPB_NETQUAKE)\t//nq demos have some silly header.\n\t\tVFS_WRITE(cls.demooutfile, \"-1\\n\", 3);\n#endif\n\n\tCL_BeginServerReconnect();\n}\n\n/*\n====================\nCL_PlayDemo_f\n\nplay [demoname]\n====================\n*/\nvoid CL_PlayDownloadedDemo(struct dl_download *dl);\nvoid CL_PlayDemo_f (void)\n{\n\tchar *demoname;\n\tif (Cmd_Argc() != 2)\n\t{\n\t\tCon_Printf (\"playdemo <demoname> : plays a demo\\n\");\n\t\treturn;\n\t}\n\n\tif (cls.state == ca_demostart)\n\t\tcls.state = ca_disconnected;\n\telse\n\t\tcls.demonum = -1;\t//not via CL_NextDemo, don't confuse the user by playing random other demos.\n\n#ifdef WEBCLIENT\n#if 1\n\tif (!strncmp(Cmd_Argv(1), \"ftp://\", 6) || !strncmp(Cmd_Argv(1), \"http://\", 7) || !strncmp(Cmd_Argv(1), \"https://\", 7))\n\t{\n\t\tif (Cmd_ExecLevel == RESTRICT_LOCAL)\n\t\t\tHost_RunFile(Cmd_Argv(1), strlen(Cmd_Argv(1)), NULL);\n//\t\t\tHTTP_CL_Get(Cmd_Argv(1), COM_SkipPath(Cmd_Argv(1)), CL_PlayDownloadedDemo);\n\t\treturn;\n\t}\n#endif\n#endif\n\n\tdemoname = Cmd_Argv(1);\n\tif (*demoname == '#')\n\t{\n\t\tif (Cmd_FromGamecode())\n\t\t\treturn;\n\t\tCL_PlayDemo(demoname+1, true);\n\t}\n\telse\n\t\tCL_PlayDemo(demoname, false);\n}\n\n//dl is provided so that we can receive files via chunked/gziped http downloads and on systems that don't provide sockets etc. its tracked so we can cancel the download if the client aborts playback early.\nvoid CL_PlayDemoStream(vfsfile_t *file, char *filename, qboolean issyspath, int demotype, float bufferdelay, unsigned int eztv_ext)\n{\n\tint protocol = CP_UNKNOWN;\n\n\tif (demotype == DPB_NONE)\n\t{\n\t\t//peek etc?\n\t}\n\n\tswitch(demotype)\n\t{\n\tcase DPB_MVD:\n\tcase DPB_QUAKEWORLD:\n\t\tprotocol = CP_QUAKEWORLD;\n\t\tbreak;\n#ifdef Q2CLIENT\n\tcase DPB_QUAKE2:\n\t\tprotocol = CP_QUAKE2;\n\t\tbreak;\n#endif\n#ifdef NQPROT\n\tcase DPB_NETQUAKE:\n\t\tprotocol = CP_NETQUAKE;\n\t\tbreak;\n#endif\n\tdefault:\n\t\tbreak;\n\t}\n\n\tif (protocol == CP_UNKNOWN)\n\t{\n\t\tCon_Printf (\"ERROR: demo format not supported: \\\"%s\\\".\\n\", filename);\n\t\treturn;\n\t}\n\n//\n// disconnect from server\n//\n\tCL_Disconnect_f ();\n\n\tdemo_flushcache();\n\n\tNET_InitClient(true);\n//\n// open the demo file\n//\n\tcls.demoinfile = file;\n\tif (!cls.demoinfile)\n\t{\n\t\tCon_Printf (\"ERROR: couldn't open \\\"%s\\\".\\n\", filename);\n\t\tcls.demonum = -1;\t\t// stop demo loop\n\t\treturn;\n\t}\n\tif (filename)\n\t{\n\t\tQ_strncpyz (cls.lastdemoname, filename, sizeof(cls.lastdemoname));\n\t\tcls.lastdemowassystempath = issyspath;\n\t\tCon_Printf (\"Playing demo from %s.\\n\", filename);\n\t}\n\n\tcls.findtrack = (demotype == DPB_MVD);\n\n\tcls.demoplayback = demotype;\n\tcls.demoeztv_ext = eztv_ext;\n\tcls.protocol = protocol;\n\tcls.state = ca_demostart;\n\tnet_message.packing = SZ_RAWBYTES;\n\tNetchan_Setup (NCF_CLIENT, &cls.netchan, &net_from, 0, 0);\n\n\tdemtime = -bufferdelay;\n\tcls.demostarttime = 0;\n\tcl.gametime = -bufferdelay;\n\tcl.gametimemark = realtime;//demtime;\n\tif (demtime < -0.5)\n\t\tCon_Printf(\"Buffering for %g seconds\\n\", bufferdelay);\n\tcls.netchan.last_received=demtime;\n\n\tTP_ExecTrigger (\"f_demostart\", true);\n}\n\nvfsfile_t *CL_OpenFileInZipOrSys(char *name, qboolean usesystempath)\n{\n\tif (usesystempath)\n\t\treturn VFSOS_Open(name, \"rb\");\n\telse\n\t\treturn CL_OpenFileInPackage(NULL, name);\n}\n//tries to determine the demo type\nvoid CL_PlayDemoFile(vfsfile_t *f, char *demoname, qboolean issyspath)\n{\n#if defined(Q2CLIENT) || defined(NQPROT)\n\t//figure out where we started\n\tqofs_t start = VFS_TELL(f);\n#endif\n\n\tif (!VFS_GETLEN (f))\n\t{\n\t\tVFS_CLOSE(f);\n\t\tCon_Printf (\"demo \\\"%s\\\" is empty.\\n\", demoname);\n\t\treturn;\n\t}\n\n#ifdef Q2CLIENT\n\t//just assume if it has a known extension\n\tif (!Q_strcasecmp(demoname + strlen(demoname) - 3, \"dm2\") ||\n\t\t!Q_strcasecmp(demoname + strlen(demoname) - 6, \"dm2.gz\"))\n\t{\n\t\tCL_PlayDemoStream(f, demoname, issyspath, DPB_QUAKE2, 0, 0);\n\t\treturn;\n\t}\n#endif\n\tif (!Q_strcasecmp(demoname + strlen(demoname) - 3, \"mvd\") ||\n\t\t!Q_strcasecmp(demoname + strlen(demoname) - 6, \"mvd.gz\"))\n\t{\n\t\tCL_PlayDemoStream(f, demoname, issyspath, DPB_MVD, 0, 0);\n\t\treturn;\n\t}\n\tif (!Q_strcasecmp(demoname + strlen(demoname) - 3, \"qwd\") ||\n\t\t!Q_strcasecmp(demoname + strlen(demoname) - 6, \"qwd.gz\"))\n\t{\n\t\tCL_PlayDemoStream(f, demoname, issyspath, DPB_QUAKEWORLD, 0, 0);\n\t\treturn;\n\t}\n\n\n\n#ifdef NQPROT\n\t{\n\t\tint ft = 0, neg = false;\n\t\tchar chr;\n\t\t//not quake2, check if its NQ\n\t\t//work out if the first line is a int for the track number.\n\t\twhile ((VFS_READ(f, &chr, 1)==1) && (chr != '\\n'))\n\t\t{\n\t\t\tif (chr == ' ')\n\t\t\t\t;\n\t\t\telse if (chr == '-')\n\t\t\t\tneg = true;\n\t\t\telse if (chr < '0' || chr > '9')\n\t\t\t\tbreak;\n\t\t\telse\n\t\t\t\tft = ft * 10 + ((int)chr - '0');\n\t\t}\n\t\tif (neg)\n\t\t\tft *= -1;\n\t\tif (chr == '\\n')\n\t\t{\n\t\t\tif (ft > 0)\n\t\t\t\tcls.demotrack = ft;\n\t\t\telse\n\t\t\t\tcls.demotrack = -1;\n\n\t\t\tCL_PlayDemoStream(f, demoname, issyspath, DPB_NETQUAKE, 0, 0);\n\t\t\treturn;\n\t\t}\n\t\tVFS_SEEK(f, start);\n\t}\n#endif\n\n#ifdef Q2CLIENT\n\t{\n\t\tint len;\n\t\tchar type;\n\t\tint protocol;\n\t\t//check if its a quake2 demo.\n\t\twhile(VFS_READ(f, &len, sizeof(len)) == sizeof(len))\n\t\t{\n\t\t\tlen = LittleLong(len);\n\t\t\tif (len > MAX_OVERALLMSGLEN)\n\t\t\t\tbreak;\n\t\t\tlen--;\n\t\t\tVFS_READ(f, &type, sizeof(type));\n\t\t\twhile (len >= 2 && (type == svcq2_stufftext || type == svcq2_print))\n\t\t\t{\n\t\t\t\twhile (len > 0)\n\t\t\t\t{\n\t\t\t\t\tlen--;\n\t\t\t\t\tVFS_READ(f, &type, sizeof(type));\n\t\t\t\t\tif (!type)\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tif (len == 0)\n\t\t\t\t\tcontinue;\n\t\t\t\tlen--;\n\t\t\t\tVFS_READ(f, &type, sizeof(type));\n\t\t\t}\n\t\t\tif (len > 4 && type == svcq2_serverdata)\n\t\t\t{\n\t\t\t\tVFS_READ(f, &protocol, sizeof(protocol));\n\t\t\t\tprotocol = LittleLong(protocol);\n\t\t\t\tif (protocol >= PROTOCOL_VERSION_Q2_DEMO_MIN && protocol <= PROTOCOL_VERSION_Q2_DEMO_MAX)\n\t\t\t\t{\n\t\t\t\t\tVFS_SEEK(f, start);\n\t\t\t\t\tCL_PlayDemoStream(f, demoname, issyspath, DPB_QUAKE2, 0, 0);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (len)\n\t\t\t\tVFS_SEEK(f, VFS_TELL(f)+len);\n\t\t}\n\t\tVFS_SEEK(f, start);\n\t}\n#endif\n\n\t//it doesn't have a assumable extension, isn't q2, nor NQ. then it must be a QuakeWorld demo\n\t//could also be .qwz or .dmz or whatever that nq extension is. we don't support either.\n\n\t//mvd and qwd have no identifying markers, other than the extension.\n\tCL_PlayDemoStream(f, demoname, issyspath, DPB_QUAKEWORLD, 0, 0);\n}\n#ifdef WEBCLIENT\nvoid CL_PlayDownloadedDemo(struct dl_download *dl)\n{\n\tif (dl->status != DL_FINISHED || !dl->file)\n\t\tCon_Printf(\"Failed to download %s\\n\", dl->url);\n\telse\n\t{\n\t\tCL_PlayDemoFile(dl->file, dl->url, false);\n\t\tdl->file = NULL;\n\t}\n}\n#endif\nvoid CL_PlayDemo(char *demoname, qboolean usesystempath)\n{\n\tchar\tname[256];\n\tvfsfile_t *f;\n\tint i;\n\tchar *exts[] =\n\t{\n\t\t\".qwd\",\n\t\t\".dem\",\n\t\t\".mvd\",\n\t\t\".dm2\",\n\t};\n\n//\n// open the demo file\n//\n\tf = NULL;\n\tfor (i = 0; i < countof(exts); i++)\n\t{\n\t\tQ_strncpyz (name, demoname, sizeof(name));\n\t\tCOM_DefaultExtension (name, exts[i], sizeof(name));\n\t\tf = CL_OpenFileInZipOrSys(name, usesystempath);\n\t\tif (f)\n\t\t\tbreak;\n\n#ifdef AVAIL_GZDEC\n\t\tQ_strncpyz (name, demoname, sizeof(name));\n\t\tCOM_DefaultExtension (name, va(\"%s.gz\", exts[i]), sizeof(name));\n\t\tf = CL_OpenFileInZipOrSys(name, usesystempath);\n\t\tif (f)\n\t\t\tbreak;\n#endif\n\t}\n\tif (!f)\n\t{\n\t\tCon_Printf (\"ERROR: couldn't open \\\"%s\\\".\\n\", demoname);\n\t\tcls.demonum = -1;\t\t// stop demo loop\n\n\t\tTP_ExecTrigger (\"f_demoend\", true);\n\t\treturn;\n\t}\n\tQ_strncpyz (cls.lastdemoname, demoname, sizeof(cls.lastdemoname));\n\n#ifdef AVAIL_GZDEC\n\tif (!strcmp(COM_GetFileExtension(name,NULL), \".gz\"))\n\t\tf = FS_DecompressGZip(f, NULL);\n#endif\n\n\tCL_PlayDemoFile(f, name, usesystempath);\n}\n\n/*used with qtv*/\nvoid CL_Demo_ClientCommand(char *commandtext)\n{\n\tunsigned char b = 1;\n\tunsigned short len = LittleShort((unsigned short)(strlen(commandtext) + 4));\n#ifdef warningmsg\n#pragma warningmsg(\"this needs buffering safely\")\n#endif\n\tif (cls.demoplayback == DPB_MVD && cls.demoeztv_ext)\n\t{\n\t\tVFS_WRITE(cls.demoinfile, &len, sizeof(len));\n\t\tVFS_WRITE(cls.demoinfile, &b, sizeof(b));\n\t\tVFS_WRITE(cls.demoinfile, commandtext, strlen(commandtext)+1);\n\t}\n}\n\nchar *strchrrev(char *str, char chr)\n{\n\tconst char *firstchar = str;\n\tfor (str = str + strlen(str)-1; str>=firstchar; str--)\n\t\tif (*str == chr)\n\t\t\treturn str;\n\n\treturn NULL;\n}\n\n/*void CL_ParseQTVFile(vfsfile_t *f, const char *fname, qtvfile_t *result)\n{\n\tchar buffer[2048];\n\tchar *s;\n\tmemset(result, 0, sizeof(*result));\n\tif (!f)\n\t{\n\t\tCon_Printf(\"Couldn't open QTV file: %s\\n\", fname);\n\t\treturn;\n\t}\n\tif (!VFS_GETS(f, buffer, sizeof(buffer)-1))\n\t{\n\t\tCon_Printf(\"Empty QTV file: %s\\n\", fname);\n\t\tVFS_CLOSE(f);\n\t\treturn;\n\t}\n\ts = buffer;\n\twhile (*s == ' ' || *s == '\\t')\n\t\ts++;\n\tif (*s != '[')\n\t{\n\t\tCon_Printf(\"Bad QTV file: %s\\n\", fname);\n\t\tVFS_CLOSE(f);\n\t\treturn;\n\t}\n\ts++;\n\twhile (*s == ' ' || *s == '\\t')\n\t\ts++;\n\tif (strnicmp(s, \"QTV\", 3))\n\t{\n\t\tCon_Printf(\"Bad QTV file: %s\\n\", fname);\n\t\tVFS_CLOSE(f);\n\t\treturn;\n\t}\n\ts+=3;\n\twhile (*s == ' ' || *s == '\\t')\n\t\ts++;\n\tif (*s != ']')\n\t{\n\t\tCon_Printf(\"Bad QTV file: %s\\n\", fname);\n\t\tVFS_CLOSE(f);\n\t\treturn;\n\t}\n\ts++;\n\twhile (*s == ' ' || *s == '\\t' || *s == '\\r')\n\t\ts++;\n\tif (*s)\n\t{\n\t\tCon_Printf(\"Bad QTV file: %s\\n\", fname);\n\t\tVFS_CLOSE(f);\n\t\treturn;\n\t}\n\n\twhile (VFS_GETS(f, buffer, sizeof(buffer)-1))\n\t{\n\t\ts = COM_ParseToken(buffer, \":=\");\n\t\tif (*s != '=' && *s != ':')\n\t\t\ts = \"\";\n\t\telse\n\t\t\ts++;\n\n\t\tif (!stricmp(com_token, \"stream\"))\n\t\t{\n\t\t\tresult->connectiontype = QTVCT_STREAM;\n\t\t\ts = COM_ParseOut(s, result->server, sizeof(result->server));\n\t\t}\n\t\telse if (!stricmp(com_token, \"connect\"))\n\t\t{\n\t\t\tresult->connectiontype = QTVCT_CONNECT;\n\t\t\ts = COM_ParseOut(s, result->server, sizeof(result->server));\n\t\t}\n\t\telse if (!stricmp(com_token, \"join\"))\n\t\t{\n\t\t\tresult->connectiontype = QTVCT_JOIN;\n\t\t\ts = COM_ParseOut(s, result->server, sizeof(result->server));\n\t\t}\n\t\telse if (!stricmp(com_token, \"observe\"))\n\t\t{\n\t\t\tresult->connectiontype = QTVCT_OBSERVE;\n\t\t\ts = COM_ParseOut(s, result->server, sizeof(result->server));\n\t\t}\n\t\telse if (!stricmp(com_token, \"splash\"))\n\t\t{\n\t\t\ts = COM_ParseOut(s, result->splashscreen, sizeof(result->server));\n\t\t}\n\t}\n\tVFS_CLOSE(f);\n}*/\n\nvoid CL_ParseQTVDescriptor(vfsfile_t *f, const char *name)\n{\t//.qtv files are some sneaky way to deal with download links using file extension associations instead of special protocols.\n\t//they basically contain some directive:hostname line that tells us what to do and where from.\n\t//they should have mime type text/x-quaketvident with extension .qtv\n\tchar buffer[1024];\n\tchar *s;\n\n\tif (!f)\n\t{\n\t\tCon_Printf(\"Couldn't open QTV file: %s\\n\", name);\n\t\treturn;\n\t}\n\twhile (VFS_GETS(f, buffer, sizeof(buffer)-1))\n\t{\n\t\tif (!strncmp(buffer, \"Stream=\", 7) || !strncmp(buffer, \"Stream:\", 7))\n\t\t{\n\t\t\tfor (s = buffer + strlen(buffer)-1; s >= buffer; s--)\n\t\t\t{\n\t\t\t\tif (*s == '\\r' || *s == '\\n' || *s == ';')\n\t\t\t\t\t*s = 0;\n\t\t\t\telse\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\ts = buffer+7;\n\t\t\twhile(*s && *s <= ' ')\n\t\t\t\ts++;\n\t\t\tCbuf_AddText(va(\"qtvplay \\\"%s\\\"\\n\", s), Cmd_ExecLevel);\n\t\t\tbreak;\n\t\t}\n\t\tif (!strncmp(buffer, \"Connect=\", 8) || !strncmp(buffer, \"Connect:\", 8))\n\t\t{\n\t\t\tfor (s = buffer + strlen(buffer)-1; s >= buffer; s--)\n\t\t\t{\n\t\t\t\tif (*s == '\\r' || *s == '\\n' || *s == ';')\n\t\t\t\t\t*s = 0;\n\t\t\t\telse\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\ts = buffer+8;\n\t\t\twhile(*s && *s <= ' ')\n\t\t\t\ts++;\n\t\t\tCbuf_AddText(va(\"connect \\\"%s\\\"\\n\", s), Cmd_ExecLevel);\n\t\t\tbreak;\n\t\t}\n\t\tif (!strncmp(buffer, \"Join=\", 5) || !strncmp(buffer, \"Join:\", 5))\n\t\t{\n\t\t\tfor (s = buffer + strlen(buffer)-1; s >= buffer; s--)\n\t\t\t{\n\t\t\t\tif (*s == '\\r' || *s == '\\n' || *s == ';')\n\t\t\t\t\t*s = 0;\n\t\t\t\telse\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\ts = buffer+5;\n\t\t\twhile(*s && *s <= ' ')\n\t\t\t\ts++;\n\t\t\tCbuf_AddText(va(\"join \\\"%s\\\"\\n\", s), Cmd_ExecLevel);\n\t\t\tbreak;\n\t\t}\n\t\tif (!strncmp(buffer, \"Observe=\", 8) || !strncmp(buffer, \"Observe:\", 8))\n\t\t{\n\t\t\tfor (s = buffer + strlen(buffer)-1; s >= buffer; s--)\n\t\t\t{\n\t\t\t\tif (*s == '\\r' || *s == '\\n' || *s == ';')\n\t\t\t\t\t*s = 0;\n\t\t\t\telse\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\ts = buffer+8;\n\t\t\twhile(*s && *s <= ' ')\n\t\t\t\ts++;\n\t\t\tCbuf_AddText(va(\"observe \\\"%s\\\"\\n\", s), Cmd_ExecLevel);\n\t\t\tbreak;\n\t\t}\n\t}\n\tVFS_CLOSE(f);\n}\n\n#include \"netinc.h\"\n\nstatic struct pendingqtv_s\n{\n\tstruct pendingqtv_s *next;\n\tqboolean raw;\n\tchar hostname[1024];\n\tchar password[1024];\n\tchar requestbuffer[4096];\n\tsize_t requestsize;\n\tchar requestcmdbuffer[4096];\n\tint requestcmdsize;\n\tvfsfile_t *stream;\n\n\tchar postauth[1];\n} *pendingqtv;\n\nvoid CL_QTVPoll (void)\n{\n\tstruct pendingqtv_s **link, *qtv;\n\tfor (link = &pendingqtv; (qtv = *link); link = &qtv->next)\n\t{\n\t\tchar *s, *e, *colon;\n\t\tchar *tail = NULL;\n\t\tint len;\n\t\tchar *streamavailable = NULL;\n\t\tqboolean saidheader = false;\n\t#ifndef NOBUILTINMENUS\n\t\temenu_t *sourcesmenu = NULL;\n\t#endif\n\t\tint sourcenum = 0;\n\n\t\tint numplayers = 0;\n\t\tint numviewers = 0;\n\t\tqboolean init_numplayers = false;\n\t\tqboolean init_numviewers = false;\n\t\tqboolean iseztv = false;\n\t\tchar srchost[256];\n\t\tchar auth[64];\n\t\tchar challenge[128];\n\t\thashfunc_t *hashfunc = NULL;\n\n\t\t//try to finish sending\n\t\tif (qtv->requestcmdsize)\n\t\t{\n\t\t\tlen = VFS_WRITE(qtv->stream, qtv->requestcmdbuffer, qtv->requestcmdsize);\n\t\t\tif (len > 0)\n\t\t\t{\n\t\t\t\tmemmove(qtv->requestcmdbuffer, qtv->requestcmdbuffer+len, qtv->requestcmdsize-len);\n\t\t\t\tqtv->requestcmdsize -= len;\n\t\t\t}\n\t\t\tif (len < 0)\n\t\t\t\tgoto fail;\n\t\t}\n\n\t\tfor(;;)\n\t\t{\n\t\t\tlen = VFS_READ(qtv->stream, qtv->requestbuffer+qtv->requestsize, (sizeof(qtv->requestbuffer) - qtv->requestsize -1 > 0)?1:0);\n\t\t\tif (len <= 0)\n\t\t\t\tbreak;\n\t\t\tqtv->requestsize += len;\n\t\t}\n\t\tqtv->requestbuffer[qtv->requestsize] = '\\0';\n\n\t\tif (qtv->raw)\n\t\t{\n\t\t\ttail = qtv->requestbuffer;\n\t\t\tstreamavailable = \"\";\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (qtv->requestsize >= sizeof(qtv->requestbuffer) - 1)\n\t\t\t{\n\t\t\t\t//flag it as an error if the response is larger than we can handle.\n\t\t\t\t//this error gets ignored if the header is okay (any actual errors will get reported again by the demo code anyway), and only counts if the end of the reply header was not found.\n\t\t\t\tlen = -1;\n\t\t\t}\n\t\t\tif (!qtv->requestsize && len == 0)\n\t\t\t\tcontinue;\t//still trying.\n\n\t\t\t//make sure it's a compleate chunk.\n\t\t\tfor (s = qtv->requestbuffer; *s; s++)\n\t\t\t{\n\t\t\t\tif (s[0] == '\\n' && s[1] == '\\n')\n\t\t\t\t{\n\t\t\t\t\ttail = s+2;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tif (s[0] == '\\r' && s[1] == '\\n' && s[2] == '\\r' && s[3] == '\\n')\n\t\t\t\t{\n\t\t\t\t\ttail = s+4;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tif (s[0] == '\\r' && s[1] == '\\n' && s[2] == '\\n')\n\t\t\t\t{\n\t\t\t\t\ttail = s+3;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tif (s[0] == '\\n' && s[1] == '\\r' && s[2] == '\\n')\n\t\t\t\t{\n\t\t\t\t\ttail = s+3;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (!tail)\n\t\t{\n\t\t\tif (len < 0)\n\t\t\t{\n\t\t\t\tif (!qtv->requestsize)\n\t\t\t\t\tCon_Printf(\"Connection to QTV server closed without any reply.\\n\");\n\t\t\t\telse\n\t\t\t\t\tCon_Printf(\"invalid QTV handshake\\n\");\nfail:\n\t\t\t\tSCR_SetLoadingStage(LS_NONE);\n\t\t\t\tif (qtv->stream)\n\t\t\t\t\tVFS_CLOSE(qtv->stream);\n\t\t\t\tqtv->stream = NULL;\n\t\t\t\tqtv->requestsize = 0;\n\t\t\t\t*link = qtv->next;\n\t\t\t\tZ_Free(qtv);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\ts = qtv->requestbuffer;\n\n\t\tcolon = \"\";\n\t\t*auth = *challenge = 0;\n\t\tfor (e = s; e < tail; )\n\t\t{\n\t\t\tif (*e == '\\r')\n\t\t\t\t*e = '\\0';\n\t\t\telse if (*e == '\\n')\n\t\t\t{\n\t\t\t\t*e = '\\0';\n\t\t\t\tcolon = strchr(s, ':');\n\t\t\t\tif (colon)\n\t\t\t\t{\n\t\t\t\t\t*colon++ = '\\0';\n\t\t\t\t\tif (*colon && *(unsigned char*)colon <= ' ')\n\t\t\t\t\t\tcolon++;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tcolon = \"\";\n\n\t\t\t\tif (!strcmp(s, \"PERROR\"))\n\t\t\t\t{\t//permanent printable error\n\t\t\t\t\tCon_Printf(\"QTV Error:\\n%s\\n\", colon);\n\t\t\t\t}\n\t\t\t\telse if (!strcmp(s, \"PRINT\"))\n\t\t\t\t{\t//printable error\n\t\t\t\t\tCon_Printf(\"QTV:\\n%s\\n\", colon);\n\t\t\t\t}\n\t\t\t\telse if (!strcmp(s, \"TERROR\"))\n\t\t\t\t{\t//temporary printable error\n\t\t\t\t\tCon_Printf(\"QTV Error:\\n%s\\n\", colon);\n\t\t\t\t}\n\t\t\t\telse if (!strcmp(s, \"ADEMO\"))\n\t\t\t\t{\t//printable error\n\t\t\t\t\tCon_Printf(\"Demo%s is available\\n\", colon);\n\t\t\t\t}\n\t\t\t\telse if (!strcmp(s, \"AUTH\"))\n\t\t\t\t{\n\t\t\t\t\twhile (*colon && *(unsigned char*)colon <= ' ')\n\t\t\t\t\t\tcolon++;\n\t\t\t\t\tQ_strncpyz(auth, colon, sizeof(auth));\n\t\t\t\t}\n\t\t\t\telse if (!strcmp(s, \"CHALLENGE\"))\n\t\t\t\t{\n\t\t\t\t\twhile (*colon && *(unsigned char*)colon <= ' ')\n\t\t\t\t\t\tcolon++;\n\t\t\t\t\tQ_strncpyz(challenge, colon, sizeof(challenge));\n\t\t\t\t}\n\t\t\t\t//generic sourcelist responce\n\t\t\t\telse if (!strcmp(s, \"ASOURCE\"))\n\t\t\t\t{\t//printable source\n\t\t\t\t\tif (!saidheader)\n\t\t\t\t\t{\n\t\t\t\t\t\tsaidheader=true;\n\t\t\t\t\t\tCon_Printf(\"Available Sources:\\n\");\n\t\t\t\t\t}\n\t\t\t\t\tCon_Printf(\"%s\\n\", colon);\n\t\t\t\t\t//we're too lazy to even try and parse this\n\t\t\t\t}\n\n\t\t\t\telse if (!strcmp(s, \"BEGIN\"))\n\t\t\t\t{\n\t\t\t\t\twhile (*colon && *(unsigned char*)colon <= ' ')\n\t\t\t\t\t\tcolon++;\n\t\t\t\t\tstreamavailable = colon;\n\t\t\t\t}\n\n\t\t\t\t//eztv extensions to v1.0\n\t\t\t\telse if (!strcmp(s, \"QTV_EZQUAKE_EXT\"))\n\t\t\t\t{\n\t\t\t\t\tiseztv = atoi(colon);\n\t\t\t\t\tif (iseztv & ~(EZTV_DOWNLOAD|EZTV_SETINFO|EZTV_QTVUSERLIST))\n\t\t\t\t\t\tCon_Printf(CON_WARNING\"Warning: unknown eztv extensions %s\\n\", colon);\n\t\t\t\t}\n\n\t\t\t\t//v1.1 sourcelist response includes SRCSRV, SRCHOST, SRCPLYRS, SRCVIEWS, SRCID\n\t\t\t\telse if (!strcmp(s, \"SRCSRV\"))\n\t\t\t\t{\n\t\t\t\t\t//the proxy's source string (beware of file:blah without file:blah@blah)\n\t\t\t\t}\n\t\t\t\telse if (!strcmp(s, \"SRCHOST\"))\n\t\t\t\t{\n\t\t\t\t\t//the hostname from the server the stream came from\n\t\t\t\t\tQ_strncpyz(srchost, colon, sizeof(srchost));\n\t\t\t\t}\n\t\t\t\telse if (!strcmp(s, \"SRCPLYRS\"))\n\t\t\t\t{\n\t\t\t\t\t//number of active players actually playing on that stream\n\t\t\t\t\tnumplayers = atoi(colon);\n\t\t\t\t\tinit_numplayers = true;\n\t\t\t\t}\n\t\t\t\telse if (!strcmp(s, \"SRCVIEWS\"))\n\t\t\t\t{\n\t\t\t\t\t//number of people watching this stream on the proxy itself\n\t\t\t\t\tnumviewers = atoi(colon);\n\t\t\t\t\tinit_numviewers = true;\n\t\t\t\t}\n\t\t\t\telse if (!strcmp(s, \"SRCID\") && !streamavailable)\n\t\t\t\t{\n\t\t\t\t\tchar *streamid = colon;\n\n#ifndef NOBUILTINMENUS\n\t\t\t\t\t//now put it on a menu\n\t\t\t\t\tif (!sourcesmenu)\n\t\t\t\t\t{\n\t\t\t\t\t\tsourcesmenu = M_CreateMenu(0);\n\n\t\t\t\t\t\tMC_AddPicture(sourcesmenu, 16, 4, 32, 144, \"gfx/qplaque.lmp\");\n\t\t\t\t\t\tMC_AddCenterPicture(sourcesmenu, 4, 24, \"gfx/p_option.lmp\");\n\t\t\t\t\t}\n\t\t\t\t\tif (init_numplayers == true && init_numviewers == true)\n\t\t\t\t\t\tMC_AddConsoleCommand(sourcesmenu, 42, 170, (sourcenum++)*8 + 32, va(\"%s (p%i, v%i)\", *srchost?srchost:streamid, numplayers, numviewers), va(\"qtvplay %s@%s\\n\", streamid, qtv->hostname));\n\t\t\t\t\t//else\n\t\t\t\t\t//\tFIXME: add error message here\n#else\n\t\t\t\t\t(void)init_numviewers;\n\t\t\t\t\t(void)numviewers;\n\t\t\t\t\t(void)init_numplayers;\n\t\t\t\t\t(void)numplayers;\n\t\t\t\t\t(void)streamid;\n\t\t\t\t\t(void)sourcenum;\n#endif\n\t\t\t\t}\n\t\t\t\t//end of sourcelist entry\n\n\t\t\t\t//from e to s, we have a line\n\t\t\t\ts = e+1;\n\t\t\t}\n\t\t\te++;\n\t\t}\n\n\t\tif (streamavailable)\n\t\t{\n\t\t\tif (*streamavailable)\n\t\t\t\tCon_Printf(\"streaming \\\"%s\\\" via \\\"%s\\\"\\n\", streamavailable, qtv->hostname);\n\t\t\telse\n\t\t\t\tCon_Printf(\"qtv connection established to %s\\n\", qtv->hostname);\n\t\t\tCL_PlayDemoStream(qtv->stream, NULL, false, DPB_MVD, BUFFERTIME, iseztv);\n\t\t\tqtv->stream = NULL;\n\t\t\tdemo_resetcache(qtv->requestsize - (tail-qtv->requestbuffer), tail);\n\t\t\t*link = qtv->next;\n\t\t\tZ_Free(qtv);\n\t\t\treturn;\n\t\t}\n\n\t\t//something failed. if its giving us an auth type then we should be authing before sending our request...\n\n\t\tif (!strcmp(auth, \"NONE\"))\n\t\t\t;\n\t//\telse if (!strcmp(auth, \"PLAIN\"))\n\t//\telse if (!strcmp(auth, \"MD4\"))\n\t\telse if (!strcmp(auth, \"SHA1\"))\n\t\t\thashfunc = &hash_sha1;\n\t\telse if (!strcmp(auth, \"SHA2_256\"))\n\t\t\thashfunc = &hash_sha2_256;\n\t\telse if (!strcmp(auth, \"SHA2_512\"))\n\t\t\thashfunc = &hash_sha2_512;\n\t\telse if (*auth)\n\t\t\tCon_Printf(\"Server requires unsupported auth method: %s\\n\", auth);\n\n\t\tqtv->requestsize -= tail-qtv->requestbuffer;\n\t\tmemmove(qtv->requestbuffer, tail, qtv->requestsize);\n\t\tif (hashfunc && *qtv->postauth)\n\t\t{\n\t\t\tif (*qtv->password)\n\t\t\t{\n\t\t\t\tchar hash[DIGEST_MAXSIZE*2+1];\n\t\t\t\tqbyte digest[DIGEST_MAXSIZE];\n\n\t\t\t\tQ_snprintfz(hash, sizeof(hash), \"%s%s\", challenge, qtv->password);\n\t\t\t\tCalcHash(hashfunc, digest, sizeof(digest), hash, strlen(hash));\n\t\t\t\tBase64_EncodeBlock(digest, hashfunc->digestsize, hash, sizeof(hash));\n\n\t\t\t\tQ_snprintfz(qtv->requestcmdbuffer, sizeof(qtv->requestcmdbuffer),\n\t\t\t\t\t\"QTV\\n\"\n\t\t\t\t\t\"VERSION: 1.1\\n\"\n\t\t\t\t\t\"AUTH: %s\\n\"\n\t\t\t\t\t\"PASSWORD: \\\"%s\\\"\\n\"\n\t\t\t\t\t\"%s\\n\",\n\t\t\t\t\t\tauth, hash, qtv->postauth);\n\t\t\t\tqtv->requestcmdsize = strlen(qtv->requestcmdbuffer);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\telse\n\t\t\t\tCon_Printf(\"QTV server requires a password\\n\");\n\t\t}\n\n\t\tSCR_SetLoadingStage(LS_NONE);\n\t\tVFS_CLOSE(qtv->stream);\n\t\tqtv->stream = NULL;\n\t\tqtv->requestsize = 0;\n\t\t*link = qtv->next;\n\t\tZ_Free(qtv);\n\t\treturn;\n\t}\n}\nvoid CL_QTVPlay_Establish (const char *host, const char *password, const char *command)\n{\n\tstruct pendingqtv_s *qtv = Z_Malloc(sizeof(*qtv) + strlen(command));\n\tchar msg[4096];\n\tint msglen=0;\n\n//\tSCR_SetLoadingStage(LS_CONNECTION);\n\tqtv->stream = FS_OpenTCP(host, 27599, false);\n\tif (!qtv->stream)\n\t{\n\t\tSCR_SetLoadingStage(LS_NONE);\n\t\tCon_Printf(\"Couldn't connect to proxy\\n\");\n\t\tZ_Free(qtv);\n\t\treturn;\n\t}\n\n\tQ_strncpyz(qtv->hostname, host, sizeof(qtv->hostname));\n\tQ_strncpyz(qtv->password, password, sizeof(qtv->password));\n\n\tif (qtvcl_forceversion1.ival)\n\t{\n\t\tQ_snprintfz(msg+msglen, sizeof(msg)-msglen,\n\t\t\t\t\t\"QTV\\n\"\n\t\t\t\t\t\"VERSION: 1.0\\n\");\n\t}\n\telse\n\t{\n\t\tQ_snprintfz(msg+msglen, sizeof(msg)-msglen,\n\t\t\t\t\t\"QTV\\n\"\n\t\t\t\t\t\"VERSION: 1.1\\n\");\n\t}\n\tmsglen += strlen(msg+msglen);\n\n\tif (*password)\n\t{\n\t\tif (qtv->raw)\n\t\t{\n\t\t\t//just send it directly, we can't handle any kind of response and that includes the tripple handshake for the challenge info\n\t\t\tQ_snprintfz(msg+msglen, sizeof(msg)-msglen,\n\t\t\t\t\t\t\"AUTH: PLAIN\\n\"\n\t\t\t\t\t\t\"PASSWORD: %s\\n\"\n\t\t\t\t\t\t, password);\n\t\t}\n\t\telse\n\t\t{\n\t\t\t//report supported auth methods to the server. it'll pick one and send us a challenge.\n\t\t\tQ_snprintfz(msg+msglen, sizeof(msg)-msglen,\n\t\t\t\t\t\t\"AUTH: SHA2_512\\n\"\n\t\t\t\t\t\t\"AUTH: SHA2_256\\n\"\n\t\t\t\t\t\t\"AUTH: SHA1\\n\"\n//\t\t\t\t\t\t\"AUTH: MD4\\n\"\n//\t\t\t\t\t\t\"AUTH: CCITT\\n\"\n//\t\t\t\t\t\t\"AUTH: PLAIN\\n\"\n\t\t\t\t\t\t);\n\t\t}\n\t\tmsglen += strlen(msg+msglen);\n\n\t\tstrcpy(qtv->postauth, command);\n\t}\n\telse\n\t{\n\t//include supported auth methods, so server can pick one (and give suitable challenge in its response)\n\t\tQ_snprintfz(msg+msglen, sizeof(msg)-msglen,\n\t\t\t\t\t\"AUTH: NONE\\n\"\n\t\t\t\t\t\"\");\n\t\tmsglen += strlen(msg+msglen);\n\n\t\tQ_snprintfz(msg+msglen, sizeof(msg)-msglen, \"%s\", command);\n\t\tmsglen += strlen(msg+msglen);\n\t\t*qtv->postauth = 0;\n\t}\n\n\tif (qtv->raw)\n\t{\t//peer must either disconnect instantly, or respond with an mvd file without extra headers.\n\t\tQ_snprintfz(msg+msglen, sizeof(msg)-msglen,\n\t\t\t\t\"RAW: 1\\n\");\n\t\tmsglen += strlen(msg+msglen);\n\t}\n\n\tQ_snprintfz(msg+msglen, sizeof(msg)-msglen,\n\t\t\t\t\"\\n\");\n\tmsglen += strlen(msg+msglen);\n\n\n\tmemcpy(qtv->requestcmdbuffer, msg, msglen);\n\tqtv->requestcmdsize = msglen;\n\tqtv->requestsize = 0;\n\n\t//and link it in.\n\tqtv->next = pendingqtv;\n\tpendingqtv = qtv;\n}\n\nvoid CL_QTVPlay_f (void)\n{\n\tchar *host;\n\tconst char *password;\n\tchar *streamid;\n\tchar msg[4096];\n\tint msglen=0;\n\n\tif (Cmd_Argc() < 2)\n\t{\n\t\tCon_Printf(\"Usage: qtvplay [stream@][tls://]hostname[:port] [password]\\n\");\n\t\treturn;\n\t}\n\n\tstreamid = Cmd_Argv(1);\n\tpassword = Cmd_Argv(2);\n\thost = strchrrev(streamid, '@');\n\tif (host)\n\t\t*host++ = 0;\n\telse\n\t{\n\t\thost = streamid;\n\t\tstreamid = NULL;\n\t}\n\n\tif (streamid)\n\t{\n\t\tif (qtvcl_eztvextensions.ival)\n\t\t{\n\t\t\tQ_snprintfz(msg+msglen, sizeof(msg)-msglen,\n\t\t\t\t\t\"QTV_EZQUAKE_EXT: %u\\n\"\n\t\t\t\t\t\"USERINFO: \", EZTV_DOWNLOAD|EZTV_SETINFO|EZTV_QTVUSERLIST);\n\t\t\tmsglen += strlen(msg+msglen);\n\t\t\tInfoBuf_ToString(&cls.userinfo[0], msg+msglen, sizeof(msg)-msglen-1, basicuserinfos, NULL, NULL, NULL, NULL);\n\t\t\tmsglen += strlen(msg+msglen);\n\t\t\tQ_strncatz(msg+msglen, \"\\n\", sizeof(msg)-msglen);\n\t\t\tmsglen += strlen(msg+msglen);\n\t\t}\n\n\t\tQ_snprintfz(msg+msglen, sizeof(msg)-msglen,\n\t\t\t\"SOURCE: %s\\n\", streamid);\n\t\tmsglen += strlen(msg+msglen);\n\n\t\tSCR_SetLoadingStage(LS_CONNECTION);\n\t\tCL_QTVPlay_Establish(host, password, msg);\n\t}\n\telse\n\t{\n\t\tCL_QTVPlay_Establish(host, password, \"SOURCELIST\\n\");\n\t}\n\n}\n\nvoid CL_QTVList_f (void)\n{\n\tCL_QTVPlay_Establish(Cmd_Argv(1), Cmd_Argv(2), \"SOURCELIST\\n\");\n}\n\nvoid CL_QTVDemos_f (void)\n{\n\tCL_QTVPlay_Establish(Cmd_Argv(1), Cmd_Argv(2), \"DEMOLIST\\n\");\n}\n\n/*\n====================\nCL_FinishTimeDemo\n\n====================\n*/\nvoid CL_FinishTimeDemo (void)\n{\n\tint\t\tframes;\n\tfloat\ttime;\n\tcvar_t *vw;\n\n\tcls.timedemo = false;\n\n\t// loading frames don't count\n\tif (cls.td_startframe == -1)\n\t{\n\t\tCon_Printf (\"demo didn't finish loading\\n\");\n\t\tframes = 0;\n\t}\n\telse\n\t\tframes = (host_framecount - cls.td_startframe) - 1;\n\ttime = Sys_DoubleTime() - cls.td_starttime;\n\tif (!time)\n\t\ttime = 1;\n\tCon_Printf (\"%i frames %5.1f seconds %5.1f fps\\n\", frames, time, frames/time);\n\n\tcls.td_startframe = 0;\n\n\tTP_ExecTrigger (\"f_timedemoend\", true);\n\n\tvw = Cvar_FindVar(\"vid_wait\");\n\tCvar_Set(vw, vw->string);\n}\n\n/*\n====================\nCL_TimeDemo_f\n\ntimedemo [demoname]\n====================\n*/\nvoid CL_TimeDemo_f (void)\n{\n\tcvar_t *vw;\n\tif (Cmd_Argc() != 2)\n\t{\n\t\tCon_Printf (\"timedemo <demoname> : gets demo speeds\\n\");\n\t\treturn;\n\t}\n\n\tcls.demonum = -1;\t//stop the demo reel. the user will probably want to read the results.\n\n\tCL_PlayDemo_f ();\n\n\tif (cls.state != ca_demostart)\n\t\treturn;\n\n\tvw = Cvar_FindVar(\"vid_wait\");\n\tif (vw)\n\t{\n\t\tchar *t = vw->string;\n\t\tvw->string = \"0\";\n\t\tvw->value = 0;\n\t\tCvar_ForceCallback(vw);\n\t\tvw->string = t;\n\t}\n\n//read the initial frame so load times don't count as part of the time\n//\tCL_ReadPackets();\n\n// cls.td_starttime will be grabbed at the second frame of the demo, so\n// all the loading time doesn't get counted\n\n\tcls.timedemo = true;\n\tcls.td_starttime = Sys_DoubleTime();\n\tcls.td_startframe = -1;\n\tcls.td_lastframe = -1;\t\t// get a new message this frame\n}\n\n"
  },
  {
    "path": "engine/client/cl_ents.c",
    "content": "/*\nCopyright (C) 1996-1997 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n*/\n// cl_ents.c -- entity parsing and management\n\n#include \"quakedef.h\"\n#include \"particles.h\"\n#include \"shader.h\"\n#include \"glquake.h\"\n\nextern\tcvar_t\tcl_predict_players;\nextern\tcvar_t\tcl_predict_players_frac;\nextern\tcvar_t\tcl_predict_players_latency;\nextern\tcvar_t\tcl_predict_players_nudge;\nextern\tcvar_t\tcl_lerp_players;\nextern  cvar_t\tcl_lerp_maxinterval;\nextern\tcvar_t\tcl_lerp_maxdistance;\nextern\tcvar_t\tcl_solid_players;\nextern\tcvar_t\tcl_item_bobbing;\n\nextern\tcvar_t\tr_rocketlight;\nextern\tcvar_t\tr_lightflicker;\nextern\tcvar_t\tr_dimlight_colour;\nextern\tcvar_t\tr_brightlight_colour;\nextern\tcvar_t\tr_redlight_colour;\nextern\tcvar_t\tr_greenlight_colour;\nextern\tcvar_t\tr_bluelight_colour;\nextern\tcvar_t\tcl_r2g;\nextern\tcvar_t\tr_powerupglow;\nextern\tcvar_t\tv_powerupshell;\nextern\tcvar_t\tcl_nolerp;\nextern\tcvar_t\tcl_nolerp_netquake;\nextern\tcvar_t\tr_torch;\nextern  cvar_t r_shadows;\nextern\tcvar_t\tr_showbboxes;\nextern\tcvar_t gl_simpleitems;\nfloat r_blobshadows;\n\nextern\tcvar_t\tcl_gibfilter, cl_deadbodyfilter;\nextern int cl_playerindex;\nstatic qboolean cl_expandvisents;\n\nextern world_t csqc_world;\n\nstatic struct predicted_player\n{\n\tint flags;\n\tqboolean active;\n\tvec3_t origin;\t// predicted origin\n\n\tvec3_t\toldo;\n\tvec3_t\tolda;\n\tvec3_t\toldv;\n\tqboolean predict;\n\tplayer_state_t *oldstate;\n} predicted_players[MAX_CLIENTS];\n\nstatic void CL_LerpNetFrameState(framestate_t *fs, lerpents_t *le);\nvoid CL_PlayerFrameUpdated(player_state_t *plstate, entity_state_t *state, int sequence);\nvoid CL_AckedInputFrame(int inseq, int outseq, qboolean worldstateokay);\n\n\nextern int cl_playerindex, cl_h_playerindex, cl_rocketindex, cl_grenadeindex, cl_gib1index, cl_gib2index, cl_gib3index;\n\nqboolean CL_FilterModelindex(int modelindex, int frame)\n{\n\tif (modelindex == cl_playerindex)\n\t{\n\t\tif (cl_deadbodyfilter.ival == 2)\n\t\t{\n\t\t\tif (frame >= 41 && frame <= 102)\n\t\t\t\treturn true;\n\t\t}\n\t\telse if (cl_deadbodyfilter.ival)\n\t\t{\n\t\t\tif (frame == 49 || frame == 60 || frame == 69 || frame == 84 || frame == 93 || frame == 102)\n\t\t\t\treturn true;\n\t\t}\n\t}\n\n\tif (cl_gibfilter.ival && (\n\t\t\tmodelindex == cl_h_playerindex ||\n\t\t\tmodelindex == cl_gib1index ||\n\t\t\tmodelindex == cl_gib2index ||\n\t\t\tmodelindex == cl_gib3index))\n\t\treturn true;\n\treturn false;\n}\n\n\nstatic void *AllocateBoneSpace(packet_entities_t *pack, unsigned char bonecount, unsigned int *allocationpos)\n{\n\tsize_t space = bonecount * sizeof(short)*7;\n\tvoid *r;\n\tif (pack->bonedatacur + space > pack->bonedatamax)\n\t{\t//expand the storage as needed. messy, but whatever.\n\t\tpack->bonedatamax = pack->bonedatacur + space;\n\t\tpack->bonedata = BZ_Realloc(pack->bonedata, pack->bonedatamax);\n\t}\n\tr = pack->bonedata + pack->bonedatacur;\n\t*allocationpos = pack->bonedatacur;\n\tpack->bonedatacur += space;\n\treturn r;\n}\nvoid *GetBoneSpace(packet_entities_t *pack, unsigned int allocationpos)\n{\n\tif (allocationpos >= pack->bonedatacur)\n\t\treturn NULL;\n\treturn pack->bonedata + allocationpos;\n}\n\n//============================================================\n\nvoid CL_FreeDlights(void)\n{\n#ifdef RTLIGHTS\n\tint i;\n\tif (cl_dlights)\n\t\tfor (i = 0; i < rtlights_max; i++)\n\t\t{\n\t\t\tif (cl_dlights[i].customstyle)\n\t\t\t\tZ_Free(cl_dlights[i].customstyle);\n\t\t\tif (cl_dlights[i].worldshadowmesh)\n\t\t\t\tSH_FreeShadowMesh(cl_dlights[i].worldshadowmesh);\n\n#ifdef GLQUAKE\n\t\t\tif (cl_dlights[i].coronaocclusionquery)\n\t\t\t\tqglDeleteQueriesARB(1, &cl_dlights[i].coronaocclusionquery);\n#endif\n\t\t}\n#endif\n\n\trtlights_max = cl_maxdlights = 0;\n\tBZ_Free(cl_dlights);\n\tcl_dlights = NULL;\n}\nvoid CL_InitDlights(void)\n{\n\tCL_FreeDlights();\n\trtlights_max = cl_maxdlights = RTL_FIRST;\n\tcl_dlights = BZ_Realloc(cl_dlights, sizeof(*cl_dlights)*cl_maxdlights);\n\tmemset(cl_dlights, 0, sizeof(*cl_dlights)*cl_maxdlights);\n}\n\nvoid CL_CloneDlight(dlight_t *dl, dlight_t *src)\n{\n\tchar *customstyle = dl->customstyle;\n\tvoid *sm = dl->worldshadowmesh;\n\tunsigned int oq = dl->coronaocclusionquery;\n\tunsigned int oqr = (dl->key == src->key)?dl->coronaocclusionresult:false;\n\tmemcpy (dl, src, sizeof(*dl));\n\tdl->coronaocclusionquery = oq;\n\tdl->coronaocclusionresult = oqr;\n\tdl->rebuildcache = true;\n\tdl->worldshadowmesh = sm;\n\tdl->customstyle = src->customstyle?Z_StrDup(src->customstyle):NULL;\n\tZ_Free(customstyle);\n}\nstatic void CL_ClearDlight(dlight_t *dl, int key, qboolean reused)\n{\n\tvoid *sm = dl->worldshadowmesh;\n\tunsigned int oq = dl->coronaocclusionquery;\n\tunsigned int oqr = reused?dl->coronaocclusionresult:false;\n\tZ_Free(dl->customstyle);\n\tmemset (dl, 0, sizeof(*dl));\n\tdl->coronaocclusionquery = oq;\n\tdl->coronaocclusionresult = oqr;\n\tdl->rebuildcache = true;\n\tdl->worldshadowmesh = sm;\n\tdl->axis[0][0] = 1;\n\tdl->axis[1][1] = 1;\n\tdl->axis[2][2] = 1;\n\tdl->key = key;\n\tdl->flags = LFLAG_DYNAMIC;\n\tdl->color[0] = 1;\n\tdl->color[1] = 1;\n\tdl->color[2] = 1;\n\tdl->corona = bound(0, 1 * 0.25, 1);\n\tdl->coronascale = bound(0, r_flashblendscale.value, 1);\n\tdl->style = -1;\n#ifdef RTLIGHTS\n\tdl->lightcolourscales[0] = r_shadow_realtime_dlight_ambient.value;\n\tdl->lightcolourscales[1] = r_shadow_realtime_dlight_diffuse.value;\n\tdl->lightcolourscales[2] = r_shadow_realtime_dlight_specular.value;\n#endif\n//\tif (r_shadow_realtime_dlight_shadowmap.value)\n//\t\tdl->flags |= LFLAG_SHADOWMAP;\n}\n\ndlight_t *CL_AllocSlight(void)\n{\n\tdlight_t\t*dl;\n\tint i;\n\tfor (i = RTL_FIRST; i < rtlights_max; i++)\n\t{\n\t\tif (cl_dlights[i].radius <= 0)\n\t\t\tbreak;\n\t}\n\tif (i == rtlights_max)\n\t{\n\t\tif (rtlights_max == cl_maxdlights)\n\t\t{\n\t\t\tcl_maxdlights = rtlights_max+8;\n\t\t\tcl_dlights = BZ_Realloc(cl_dlights, sizeof(*cl_dlights)*cl_maxdlights);\n\t\t\tmemset(&cl_dlights[rtlights_max], 0, sizeof(*cl_dlights)*(cl_maxdlights-rtlights_max));\n\t\t}\n\t\ti = rtlights_max++;\n\t}\n\tdl = &cl_dlights[i];\n\n\tCL_ClearDlight(dl, 0, false);\n\tdl->flags = LFLAG_REALTIMEMODE;\n\tdl->corona = 0;\n\treturn dl;\n}\n\n/*\n===============\nCL_AllocDlight\n\n===============\n*/\ndlight_t *CL_AllocDlight (int key)\n{\n\tint\t\ti;\n\tdlight_t\t*dl;\n\n// first look for an exact key match\n\tif (key)\n\t{\n\t\tdl = cl_dlights+rtlights_first;\n\t\tfor (i=rtlights_first ; i<RTL_FIRST ; i++, dl++)\n\t\t{\n\t\t\tif (dl->key == key)\n\t\t\t{\n\t\t\t\tCL_ClearDlight(dl, key, true);\n\t\t\t\treturn dl;\n\t\t\t}\n\t\t}\n\t}\n\n\t//default to the first\n\tdl = &cl_dlights[rtlights_first?rtlights_first-1:0];\n\t//try and find one that is free\n\tfor (i=RTL_FIRST; i > rtlights_first && i > 0; )\n\t{\n\t\ti--;\n\t\tif (!cl_dlights[i].radius)\n\t\t{\n\t\t\tdl = &cl_dlights[i];\n\t\t\tbreak;\n\t\t}\n\t}\n\tif (rtlights_first > dl - cl_dlights)\n\t\trtlights_first = dl - cl_dlights;\n\n\tCL_ClearDlight(dl, key, false);\n\treturn dl;\n}\n\ndlight_t *CL_AllocDlightOrg (int keyidx, vec3_t keyorg)\n{\n\tint\t\ti;\n\tdlight_t\t*dl;\n\n// first look for an exact key match\n\tdl = cl_dlights+rtlights_first;\n\tfor (i=rtlights_first ; i<RTL_FIRST ; i++, dl++)\n\t{\n\t\tif (dl->key == keyidx && VectorCompare(dl->origin, keyorg))\n\t\t{\n\t\t\tCL_ClearDlight(dl, keyidx, true);\n\t\t\tVectorCopy(keyorg, dl->origin);\n\t\t\treturn dl;\n\t\t}\n\t}\n\n\t//default to the first\n\tdl = &cl_dlights[rtlights_first?rtlights_first-1:0];\n\t//try and find one that is free\n\tfor (i=RTL_FIRST; i > rtlights_first && i > 0; )\n\t{\n\t\ti--;\n\t\tif (!cl_dlights[i].radius)\n\t\t{\n\t\t\tdl = &cl_dlights[i];\n\t\t\tbreak;\n\t\t}\n\t}\n\tif (rtlights_first > dl - cl_dlights)\n\t\trtlights_first = dl - cl_dlights;\n\n\tCL_ClearDlight(dl, keyidx, false);\n\tVectorCopy(keyorg, dl->origin);\n\treturn dl;\n}\n\n\n/*\n===============\nCL_NewDlight\n===============\n*/\ndlight_t *CL_NewDlight (int key, const vec3_t org, float radius, float time,\n\t\t\t\t   float r, float g, float b)\n{\n\tdlight_t\t*dl;\n\n\tdl = CL_AllocDlight (key);\n\tVectorCopy(org, dl->origin);\n\tdl->radius = radius;\n\tdl->die = cl.time + time;\n\tdl->color[0] = r;\n\tdl->color[1] = g;\n\tdl->color[2] = b;\n\n\treturn dl;\n}\n\n\n/*\n===============\nCL_DecayLights\n\n===============\n*/\nvoid CL_DecayLights (void)\n{\n\tint\t\t\ti;\n\tdlight_t\t*dl;\n\tfloat frametime = host_frametime;\n\n\tif (cl.paused)\t//DON'T DO IT!!!\n\t\tframetime = 0;\n\n\tdl = cl_dlights+rtlights_first;\n\tfor (i=rtlights_first ; i<RTL_FIRST ; i++, dl++)\n\t{\n\t\tif (!dl->radius)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (!dl->die)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (dl->die < (float)cl.time)\n\t\t{\n\t\t\tif (i==rtlights_first)\n\t\t\t\trtlights_first++;\n\t\t\tdl->radius = 0;\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (r_dynamic.ival == 2)\n\t\t{\t//don't decay quite so fast, this should aproximate winquake a bit better.\n\t\t\tdl->die -= frametime * 0.5;\n\t\t\tdl->radius -= frametime*dl->decay * 0.5;\n\t\t}\n\t\telse\n\t\t\tdl->radius -= frametime*dl->decay;\n\t\tif (dl->radius < 0)\n\t\t{\n\t\t\tif (i==rtlights_first)\n\t\t\t\trtlights_first++;\n\t\t\tdl->radius = 0;\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (dl->channelfade[0])\n\t\t{\n\t\t\tdl->color[0] -= frametime*dl->channelfade[0];\n\t\t\tif (dl->color[0] < 0)\n\t\t\t\tdl->color[0] = 0;\n\t\t}\n\n\t\tif (dl->channelfade[1])\n\t\t{\n\t\t\tdl->color[1] -= frametime*dl->channelfade[1];\n\t\t\tif (dl->color[1] < 0)\n\t\t\t\tdl->color[1] = 0;\n\t\t}\n\n\t\tif (dl->channelfade[2])\n\t\t{\n\t\t\tdl->color[2] -= frametime*dl->channelfade[2];\n\t\t\tif (dl->color[2] < 0)\n\t\t\t\tdl->color[2] = 0;\n\t\t}\n\t}\n}\n\n\n/*\n=========================================================================\n\nPACKET ENTITY PARSING / LINKING\n\n=========================================================================\n*/\n\n/*\n==================\nCL_ParseDelta\n\nCan go from either a baseline or a previous packet_entity\n==================\n*/\n//int\tbitcounts[32];\t/// just for protocol profiling\nvoid CLQW_ParseDelta (entity_state_t *from, entity_state_t *to, int bits)\n{\n\tint\t\t\ti;\n#ifdef PROTOCOLEXTENSIONS\n\tint morebits=0;\n#endif\n\n\t// set everything to the state we are delta'ing from\n\t*to = *from;\n\n\tto->number = bits & 511;\n\tto->sequence = cls.netchan.incoming_sequence;\n\tbits &= ~511;\n\n\tif (bits & U_MOREBITS)\n\t{\t// read in the low order bits\n\t\ti = MSG_ReadByte ();\n\t\tbits |= i;\n\t}\n\n\t// count the bits for net profiling\n//\tfor (i=0 ; i<16 ; i++)\n//\t\tif (bits&(1<<i))\n//\t\t\tbitcounts[i]++;\n\n#ifdef PROTOCOLEXTENSIONS\n\tif ((bits & U_EVENMORE) && (cls.fteprotocolextensions & (PEXT_SCALE|PEXT_TRANS|PEXT_FATNESS|PEXT_HEXEN2|PEXT_COLOURMOD|PEXT_DPFLAGS|PEXT_MODELDBL|PEXT_ENTITYDBL|PEXT_ENTITYDBL2)))\n\t\tmorebits = MSG_ReadByte ();\n\tif (morebits & U_YETMORE)\n\t\tmorebits |= MSG_ReadByte()<<8;\n#endif\n\n\tif ((morebits & U_ENTITYDBL) && (cls.fteprotocolextensions & PEXT_ENTITYDBL))\n\t\tto->number += 512;\n\tif ((morebits & U_ENTITYDBL2) && (cls.fteprotocolextensions & PEXT_ENTITYDBL2))\n\t\tto->number += 1024;\n\n\tif (bits & U_MODEL)\n\t{\n\t\tto->modelindex = MSG_ReadByte ();\n\t\tif (morebits & U_MODELDBL && (cls.fteprotocolextensions & PEXT_MODELDBL))\n\t\t\tto->modelindex += 256;\n\t}\n\telse if (morebits & U_MODELDBL && (cls.fteprotocolextensions & PEXT_MODELDBL))\n\t\tto->modelindex = MSG_ReadShort();\n\n\tif (bits & U_FRAME)\n\t\tto->frame = MSG_ReadByte ();\n\n\tif (bits & U_COLORMAP)\n\t\tto->colormap = MSG_ReadByte();\n\n\tif (bits & U_SKIN)\n\t{\n\t\tto->skinnum = MSG_ReadByte();\n\t\tif (to->skinnum >= 256-32) /*final 32 skins are taken as a content value instead*/\n\t\t\tto->skinnum = (char)to->skinnum;\n\t}\n\n\tif (bits & U_EFFECTS)\n\t\tto->effects = (to->effects&0xff00)|MSG_ReadByte();\n\n\tif (bits & U_ORIGIN1)\n\t{\n\t\tif (cls.ezprotocolextensions1 & EZPEXT1_FLOATENTCOORDS)\n\t\t\tto->origin[0] = MSG_ReadCoordFloat ();\n\t\telse\n\t\t\tto->origin[0] = MSG_ReadCoord ();\n\t}\n\n\tif (bits & U_ANGLE1)\n\t\tto->angles[0] = MSG_ReadAngle ();\n\n\tif (bits & U_ORIGIN2)\n\t{\n\t\tif (cls.ezprotocolextensions1 & EZPEXT1_FLOATENTCOORDS)\n\t\t\tto->origin[1] = MSG_ReadCoordFloat ();\n\t\telse\n\t\t\tto->origin[1] = MSG_ReadCoord ();\n\t}\n\n\tif (bits & U_ANGLE2)\n\t\tto->angles[1] = MSG_ReadAngle ();\n\n\tif (bits & U_ORIGIN3)\n\t{\n\t\tif (cls.ezprotocolextensions1 & EZPEXT1_FLOATENTCOORDS)\n\t\t\tto->origin[2] = MSG_ReadCoordFloat ();\n\t\telse\n\t\t\tto->origin[2] = MSG_ReadCoord ();\n\t}\n\n\tif (bits & U_ANGLE3)\n\t\tto->angles[2] = MSG_ReadAngle ();\n\n\tto->solidsize = ES_SOLID_BSP;\n\tif (bits & U_SOLID)\n\t{\n\t\t//doesn't mean anything in vanilla. solidity is infered instead.\n\n\t}\n\n#ifdef PEXT_SCALE\n\tif ((morebits & U_SCALE) && (cls.fteprotocolextensions & PEXT_SCALE))\n\t\tto->scale = MSG_ReadByte();\n#endif\n#ifdef PEXT_TRANS\n\tif ((morebits & U_TRANS) && (cls.fteprotocolextensions & PEXT_TRANS))\n\t\tto->trans = MSG_ReadByte();\n#endif\n#ifdef PEXT_FATNESS\n\tif ((morebits & U_FATNESS) && (cls.fteprotocolextensions & PEXT_FATNESS))\n\t\tto->fatness = MSG_ReadChar();\n#endif\n\n\tif ((morebits & U_DRAWFLAGS) && (cls.fteprotocolextensions & PEXT_HEXEN2))\n\t\tto->hexen2flags = MSG_ReadByte();\n\tif ((morebits & U_ABSLIGHT) && (cls.fteprotocolextensions & PEXT_HEXEN2))\n\t\tto->abslight = MSG_ReadByte();\n\n\tif ((morebits & U_COLOURMOD) && (cls.fteprotocolextensions & PEXT_COLOURMOD))\n\t{\n\t\tto->colormod[0] = MSG_ReadByte();\n\t\tto->colormod[1] = MSG_ReadByte();\n\t\tto->colormod[2] = MSG_ReadByte();\n\t}\n\n\tif (morebits & U_DPFLAGS)// && cls.fteprotocolextensions & PEXT_DPFLAGS)\n\t{\n\t\t// these are bits for the 'flags' field of the entity_state_t\n\n\t\ti = MSG_ReadByte();\n\t\tto->dpflags = i;\n\t}\n\tif (!(cls.fteprotocolextensions & PEXT_DPFLAGS))\n\t{\n\t\tif (to->frame)\n\t\t\tto->dpflags |= RENDER_STEP;\n\t}\n\tif (morebits & U_TAGINFO)\n\t{\n\t\tto->tagentity = MSG_ReadShort();\n\t\tto->tagindex = MSG_ReadShort();\n\t}\n\tif (morebits & U_LIGHT)\n\t{\n\t\tto->light[0] = MSG_ReadShort();\n\t\tto->light[1] = MSG_ReadShort();\n\t\tto->light[2] = MSG_ReadShort();\n\t\tto->light[3] = MSG_ReadShort();\n\t\tto->lightstyle = MSG_ReadByte();\n\t\tto->lightpflags = MSG_ReadByte();\n\t}\n\n\tif (morebits & U_EFFECTS16)\n\t\tto->effects = (to->effects&0x00ff)|(MSG_ReadByte()<<8);\n}\n\n\n/*\n=================\nFlushEntityPacket\n=================\n*/\nvoid FlushEntityPacket (void)\n{\n\tint\t\t\tword;\n\tentity_state_t\tolde, newe;\n\n\tCon_DPrintf (\"FlushEntityPacket\\n\");\n\n\tmemset (&olde, 0, sizeof(olde));\n\n\tif ((cl.validsequence&UPDATE_MASK) == (cls.netchan.incoming_sequence&UPDATE_MASK))\n\t\tcl.validsequence = 0;\t\t// last-known-good sequence is becoming invalid.\n\tcl.inframes[cls.netchan.incoming_sequence&UPDATE_MASK].invalid = true;\n\n\t// read it all, but ignore it\n\twhile (1)\n\t{\n\t\tword = (unsigned short)MSG_ReadShort ();\n\t\tif (msg_badread)\n\t\t{\t// something didn't parse right...\n\t\t\tHost_EndGame (\"msg_badread in packetentities\");\n\t\t\treturn;\n\t\t}\n\n\t\tif (!word)\n\t\t\tbreak;\t// done\n\n\t\tCLQW_ParseDelta (&olde, &newe, word);\n\t}\n}\n\nvoid CLFTE_ReadDelta(unsigned int entnum, entity_state_t *news, entity_state_t *olds, entity_state_t *baseline, packet_entities_t *newp, packet_entities_t *oldp)\n{\n\tunsigned int predbits = 0;\n\tunsigned int bits;\n\n\tbits = MSG_ReadByte();\n\tif (bits & UF_EXTEND1)\n\t\tbits |= MSG_ReadByte()<<8;\n\tif (bits & UF_EXTEND2)\n\t\tbits |= MSG_ReadByte()<<16;\n\tif (bits & UF_EXTEND3)\n\t\tbits |= MSG_ReadByte()<<24;\n\tif (bits & UF_EXTEND4)\n\t\tHost_EndGame(\"ent update bit %#x\\n\", UF_EXTEND4);\n\n\tif (cl_shownet.ival >= 3)\n\t\tCon_Printf(\"%3i:     Update %4i 0x%x\\n\", MSG_GetReadCount(), entnum, bits);\n\n\tif (bits & UF_RESET)\n\t{\n//\t\tCon_Printf(\"%3i: Reset %i @ %i\\n\", msg_readcount, entnum, cls.netchan.incoming_sequence);\n\t\t*news = *baseline;\n\t}\n\telse if (!olds)\n\t{\n\t\t/*reset got lost, probably the data will be filled in later - FIXME: we should probably ignore this entity*/\n\t\tCon_DPrintf(\"New entity %i without reset\\n\", entnum);\n\t\t*news = nullentitystate;\n//\t\t*news = *baseline;\n\t}\n\telse\n\t\t*news = *olds;\n\tnews->number = entnum;\n\tnews->sequence = cls.netchan.incoming_sequence;\n\t\n\tif (bits & UF_FRAME)\n\t{\n\t\tif (cls.fteprotocolextensions2 & PEXT2_LERPTIME)\n\t\t\tnews->frame = MSG_ReadULEB128();\n\t\telse\n\t\t{\n\t\t\tif (bits & UF_16BIT_LERPTIME)\n\t\t\t\tnews->frame = MSG_ReadShort();\n\t\t\telse\n\t\t\t\tnews->frame = MSG_ReadByte();\n\t\t}\n\t}\n\n\tif (cls.ezprotocolextensions1 & EZPEXT1_FLOATENTCOORDS)\n\t{\n\t\tif (bits & UF_ORIGINXY)\n\t\t{\n\t\t\tnews->origin[0] = MSG_ReadFloat();\n\t\t\tnews->origin[1] = MSG_ReadFloat();\n\t\t}\n\t\tif (bits & UF_ORIGINZ)\n\t\t\tnews->origin[2] = MSG_ReadFloat();\n\t}\n\telse\n\t{\n\t\tif (bits & UF_ORIGINXY)\n\t\t{\n\t\t\tnews->origin[0] = MSG_ReadCoord();\n\t\t\tnews->origin[1] = MSG_ReadCoord();\n\t\t}\n\t\tif (bits & UF_ORIGINZ)\n\t\t\tnews->origin[2] = MSG_ReadCoord();\n\t}\n\n\tif ((bits & UF_PREDINFO) && !(cls.fteprotocolextensions2 & PEXT2_PREDINFO))\n\t{\n\t\t/*predicted stuff gets more precise angles*/\n\t\tif (bits & UF_ANGLESXZ)\n\t\t{\n\t\t\tnews->angles[0] = MSG_ReadAngle16();\n\t\t\tnews->angles[2] = MSG_ReadAngle16();\n\t\t}\n\t\tif (bits & UF_ANGLESY)\n\t\t\tnews->angles[1] = MSG_ReadAngle16();\n\t}\n\telse\n\t{\n\t\tif (bits & UF_ANGLESXZ)\n\t\t{\n\t\t\tnews->angles[0] = MSG_ReadAngle();\n\t\t\tnews->angles[2] = MSG_ReadAngle();\n\t\t}\n\t\tif (bits & UF_ANGLESY)\n\t\t\tnews->angles[1] = MSG_ReadAngle();\n\t}\n\n\tif (cls.fteprotocolextensions2 & PEXT2_LERPTIME)\n\t{\n\t\tif (bits & UF_16BIT_LERPTIME)\n\t\t\tnews->lerpend = cl.gametime + MSG_ReadULEB128()*(1/1000.0);\t//most things will animate at 100ms, so this will usually fit a single byte, without capping out.\n\t\tif (bits & UF_EFFECTS)\n\t\t\tnews->effects = MSG_ReadULEB128();\n\t\tif (bits & UF_EFFECTS2_OLD)\n\t\t\tHost_EndGame(\"Received unexpected (redefined) bit %#x\\n\", UF_EFFECTS2_OLD);\n\t}\n\telse\n\t{\n\t\tif ((bits & (UF_EFFECTS | UF_EFFECTS2_OLD)) == (UF_EFFECTS | UF_EFFECTS2_OLD))\n\t\t\tnews->effects = MSG_ReadLong();\n\t\telse if (bits & UF_EFFECTS2_OLD)\n\t\t\tnews->effects = (unsigned short)MSG_ReadShort();\n\t\telse if (bits & UF_EFFECTS)\n\t\t\tnews->effects = MSG_ReadByte();\n\t}\n\n\tnews->u.q1.movement[0] = 0;\n\tnews->u.q1.movement[1] = 0;\n\tnews->u.q1.movement[2] = 0;\n\tnews->u.q1.velocity[0] = 0;\n\tnews->u.q1.velocity[1] = 0;\n\tnews->u.q1.velocity[2] = 0;\n\tif (bits & UF_PREDINFO)\n\t{\n\t\tpredbits = MSG_ReadByte();\n\n\t\tif (predbits & UFP_FORWARD)\n\t\t\tnews->u.q1.movement[0] = MSG_ReadShort();\n\t\telse\n\t\t\tnews->u.q1.movement[0] = 0;\n\t\tif (predbits & UFP_SIDE)\n\t\t\tnews->u.q1.movement[1] = MSG_ReadShort();\n\t\telse\n\t\t\tnews->u.q1.movement[1] = 0;\n\t\tif (predbits & UFP_UP)\n\t\t\tnews->u.q1.movement[2] = MSG_ReadShort();\n\t\telse\n\t\t\tnews->u.q1.movement[2] = 0;\n\t\tif (predbits & UFP_MOVETYPE)\n\t\t\tnews->u.q1.pmovetype = MSG_ReadByte();\n\t\tif (predbits & UFP_VELOCITYXY)\n\t\t{\n\t\t\tnews->u.q1.velocity[0] = MSG_ReadShort();\n\t\t\tnews->u.q1.velocity[1] = MSG_ReadShort();\n\t\t}\n\t\telse\n\t\t{\n\t\t\tnews->u.q1.velocity[0] = 0;\n\t\t\tnews->u.q1.velocity[1] = 0;\n\t\t}\n\t\tif (predbits & UFP_VELOCITYZ)\n\t\t\tnews->u.q1.velocity[2] = MSG_ReadShort();\n\t\telse\n\t\t\tnews->u.q1.velocity[2] = 0;\n\t\tif (predbits & UFP_MSEC)\n\t\t\tnews->u.q1.msec = MSG_ReadByte();\n\t\telse\n\t\t\tnews->u.q1.msec = 0;\n\n\t\tif (cls.fteprotocolextensions2 & PEXT2_PREDINFO)\n\t\t{\n\t\t\tif (predbits & UFP_VIEWANGLE)\n\t\t\t{\n\t\t\t\tif (bits & UF_ANGLESXZ)\n\t\t\t\t{\n\t\t\t\t\tnews->u.q1.vangle[0] = MSG_ReadShort();\n\t\t\t\t\tnews->u.q1.vangle[2] = MSG_ReadShort();\n\t\t\t\t}\n\t\t\t\tif (bits & UF_ANGLESY)\n\t\t\t\t\tnews->u.q1.vangle[1] = MSG_ReadShort();\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (predbits & UFP_WEAPONFRAME_OLD)\n\t\t\t{\n\t\t\t\tnews->u.q1.weaponframe = MSG_ReadByte();\n\t\t\t\tif (news->u.q1.weaponframe & 0x80)\n\t\t\t\t\tnews->u.q1.weaponframe = (news->u.q1.weaponframe & 127) | (MSG_ReadByte()<<7);\n\t\t\t}\n\t\t}\n\t}\n\telse\n\t{\n\t\tnews->u.q1.msec = 0;\n\t}\n\n\tif (!(predbits & UFP_VIEWANGLE) || !(cls.fteprotocolextensions2 & PEXT2_PREDINFO))\n\t{\n\t\tif (bits & UF_ANGLESXZ)\n\t\t\tnews->u.q1.vangle[0] = ANGLE2SHORT(news->angles[0] * ((bits & UF_PREDINFO)?-3:-1));\n\t\tif (bits & UF_ANGLESY)\n\t\t\tnews->u.q1.vangle[1] = ANGLE2SHORT(news->angles[1]);\n\t\tif (bits & UF_ANGLESXZ)\n\t\t\tnews->u.q1.vangle[2] = ANGLE2SHORT(news->angles[2]);\n\t}\n\n\tif (bits & UF_MODEL)\n\t{\n\t\tif (cls.fteprotocolextensions2 & PEXT2_LERPTIME)\n\t\t\tnews->modelindex = MSG_ReadULEB128();\n\t\telse\n\t\t{\n\t\t\tif (bits & UF_16BIT_LERPTIME)\n\t\t\t\tnews->modelindex = MSG_ReadShort();\n\t\t\telse\n\t\t\t\tnews->modelindex = MSG_ReadByte();\n\t\t}\n\t}\n\tif (bits & UF_SKIN)\n\t{\n\t\tif (cls.fteprotocolextensions2 & PEXT2_LERPTIME)\n\t\t\tnews->skinnum = MSG_ReadULEB128()-64;\t//biased for content overrides\n\t\telse\n\t\t{\n\t\t\tif (bits & UF_16BIT_LERPTIME)\n\t\t\t\tnews->skinnum = MSG_ReadShort();\n\t\t\telse\n\t\t\t\tnews->skinnum = MSG_ReadByte();\n\t\t}\n\t}\n\tif (bits & UF_COLORMAP)\n\t{\n\t\tif (cls.fteprotocolextensions2 & PEXT2_LERPTIME)\n\t\t\tnews->colormap = MSG_ReadULEB128();\n\t\telse\n\t\t\tnews->colormap = MSG_ReadByte();\n\t}\n\n\tif (bits & UF_SOLID)\n\t{\n\t\tif (cls.fteprotocolextensions2 & PEXT2_NEWSIZEENCODING)\n\t\t{\n\t\t\tqbyte enc = MSG_ReadByte();\n\t\t\tif (enc == 0)\n\t\t\t\tnews->solidsize = ES_SOLID_NOT;\n\t\t\telse if (enc == 1)\n\t\t\t\tnews->solidsize = ES_SOLID_BSP;\n\t\t\telse if (enc == 2)\n\t\t\t\tnews->solidsize = ES_SOLID_HULL1;\n\t\t\telse if (enc == 3)\n\t\t\t\tnews->solidsize = ES_SOLID_HULL2;\n\t\t\telse if (enc == 16)\n\t\t\t\tnews->solidsize = MSG_ReadSize16(&net_message);\n\t\t\telse if (enc == 32)\n\t\t\t\tnews->solidsize = MSG_ReadLong();\n\t\t\telse\n\t\t\t\tSys_Error(\"Solid+Size encoding not known\");\n\t\t}\n\t\telse\n\t\t\tnews->solidsize = MSG_ReadSize16(&net_message);\n\t}\n\n\tif (bits & UF_FLAGS)\n\t{\n\t\tif (cls.fteprotocolextensions2 & PEXT2_LERPTIME)\n\t\t\tnews->dpflags = MSG_ReadULEB128();\n\t\telse\n\t\t\tnews->dpflags = MSG_ReadByte();\n\t}\n\n\tif (bits & UF_ALPHA)\n\t\tnews->trans = MSG_ReadByte();\n\tif (bits & UF_SCALE)\n\t\tnews->scale = MSG_ReadByte();\n\tif (bits & UF_BONEDATA)\n\t{\n\t\tunsigned char fl = MSG_ReadByte();\n\t\tif (fl & 0x80)\n\t\t{\n\t\t\t//this is NOT finalized\n\t\t\tshort *bonedata;\n\t\t\tint i;\n\t\t\tnews->bonecount = MSG_ReadByte();\n\t\t\tbonedata = AllocateBoneSpace(newp, news->bonecount, &news->boneoffset);\n\t\t\tfor (i = 0; i < news->bonecount*7; i++)\n\t\t\t\tbonedata[i] = MSG_ReadShort();\n\t\t}\n\t\telse\n\t\t\tnews->bonecount = 0;\t//oo, it went away.\n\t\tif (fl & 0x40)\n\t\t{\n\t\t\tif (cls.fteprotocolextensions2 & PEXT2_LERPTIME)\n\t\t\t{\n\t\t\t\tnews->basebone = MSG_ReadULEB128();\n\t\t\t\tnews->baseframe = MSG_ReadULEB128();\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tnews->basebone = MSG_ReadByte();\n\t\t\t\tnews->baseframe = MSG_ReadShort();\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tnews->basebone = 0;\n\t\t\tnews->baseframe = 0;\n\t\t}\n\n\t\t//fixme: basebone, baseframe, etc.\n\t\tif (fl & 0x3f)\n\t\t\tHost_EndGame(\"unsupported entity delta info\\n\");\n\t}\n\telse if (news->bonecount)\n\t{\t//still has bone data from the previous frame.\n\t\tshort *bonedata = AllocateBoneSpace(newp, news->bonecount, &news->boneoffset);\n\t\tmemcpy(bonedata, oldp->bonedata+olds->boneoffset, sizeof(short)*7*news->bonecount);\n\t}\n\n\tif (bits & UF_DRAWFLAGS)\n\t{\n\t\tnews->hexen2flags = MSG_ReadByte();\n\t\tif ((news->hexen2flags & MLS_MASK) >= MLS_ADDLIGHT)\n\t\t\tnews->abslight = MSG_ReadByte();\n\t\telse\n\t\t\tnews->abslight = 0;\n\t}\n\tif (bits & UF_TAGINFO)\n\t{\n\t\tnews->tagentity = MSGCL_ReadEntity();\n\t\tif (cls.fteprotocolextensions2 & PEXT2_LERPTIME)\n\t\t\tnews->tagindex = MSG_ReadULEB128()-1;\t//biased for q3-like portals.\n\t\telse\n\t\t{\n\t\t\tnews->tagindex = MSG_ReadByte();\n\t\t\tif (news->tagindex == 0xff)\n\t\t\t\tnews->tagindex = ~0;\n\t\t}\n\t}\n\tif (bits & UF_LIGHT)\n\t{\n\t\tnews->light[0] = MSG_ReadShort();\n\t\tnews->light[1] = MSG_ReadShort();\n\t\tnews->light[2] = MSG_ReadShort();\n\t\tnews->light[3] = MSG_ReadShort();\n\t\tif (cls.fteprotocolextensions2 & PEXT2_LERPTIME)\n\t\t\tnews->lightstyle = MSG_ReadULEB128();\n\t\telse\n\t\t\tnews->lightstyle = MSG_ReadByte();\n\t\tnews->lightpflags = MSG_ReadByte();\n\t}\n\tif (bits & UF_TRAILEFFECT)\n\t{\n\t\tif (cls.fteprotocolextensions2 & PEXT2_LERPTIME)\n\t\t{\n\t\t\tnews->u.q1.traileffectnum = MSG_ReadULEB128();\n\t\t\tnews->u.q1.emiteffectnum = MSG_ReadULEB128();\n\t\t}\n\t\telse\n\t\t{\n\t\t\tunsigned short s;\n\t\t\ts = MSG_ReadShort();\n\t\t\tnews->u.q1.traileffectnum = s & 0x3fff;\n\t\t\tif (s & 0x8000)\n\t\t\t\tnews->u.q1.emiteffectnum = MSG_ReadShort() & 0x3fff;\n\t\t\telse\n\t\t\t\tnews->u.q1.emiteffectnum = 0;\n\t\t}\n\n\t\tif (news->u.q1.traileffectnum >= countof(cl.particle_ssprecache))\n\t\t\tnews->u.q1.traileffectnum = 0;\n\t\tif (news->u.q1.emiteffectnum >= countof(cl.particle_ssprecache))\n\t\t\tnews->u.q1.emiteffectnum = 0;\n\t}\n\n\tif (bits & UF_COLORMOD)\n\t{\n\t\tnews->colormod[0] = MSG_ReadByte();\n\t\tnews->colormod[1] = MSG_ReadByte();\n\t\tnews->colormod[2] = MSG_ReadByte();\n\t}\n\tif (bits & UF_GLOW)\n\t{\n\t\tnews->glowsize = MSG_ReadByte();\n\t\tnews->glowcolour = MSG_ReadByte();\n\t\tnews->glowmod[0] = MSG_ReadByte();\n\t\tnews->glowmod[1] = MSG_ReadByte();\n\t\tnews->glowmod[2] = MSG_ReadByte();\n\t}\n\tif (bits & UF_FATNESS)\n\t\tnews->fatness = MSG_ReadByte();\n\tif (bits & UF_MODELINDEX2)\n\t{\n\t\tif (cls.fteprotocolextensions2 & PEXT2_LERPTIME)\n\t\t\tnews->modelindex2 = MSG_ReadULEB128();\n\t\telse\n\t\t{\n\t\t\tif (bits & UF_16BIT_LERPTIME)\n\t\t\t\tnews->modelindex2 = MSG_ReadShort();\n\t\t\telse\n\t\t\t\tnews->modelindex2 = MSG_ReadByte();\n\t\t}\n\t}\n\tif (bits & UF_GRAVITYDIR)\n\t{\n\t\tnews->u.q1.gravitydir[0] = MSG_ReadByte();\n\t\tnews->u.q1.gravitydir[1] = MSG_ReadByte();\n\t}\n\tif (bits & UF_UNUSED1)\n\t{\n\t\tHost_EndGame(\"ent update bit %#x\\n\", UF_UNUSED1);\n\t}\n}\n\nvoid CLFTE_ParseBaseline(entity_state_t *es, qboolean numberisimportant)\n{\n\tint entnum = 0;\n\tif (numberisimportant)\n\t\tentnum = MSGCL_ReadEntity();\n\tCLFTE_ReadDelta(entnum, es, &nullentitystate, &nullentitystate, NULL, NULL);\n}\n\n\nvoid CL_PredictEntityMovement(entity_state_t *estate, float age);\n\n/*\nNote: strictly speaking, you don't need multiple frames, just two and flip between them.\nFTE retains the full 64 frames because its interpolation will go multiple packets back in time to cover packet loss.\n*/\nvoid CLFTE_ParseEntities(void)\n{\n\tint\t\t\toldpacket, newpacket;\n\tpacket_entities_t\t*oldp, *newp, nullp;\n\tentity_state_t *news, *olds;\n\tunsigned int newnum, oldnum;\n\tint\t\t\toldindex;\n\tqboolean\tisvalid = false;\n\tqboolean removeflag;\n\tint inputframe = cls.netchan.incoming_sequence;\n#if defined(QUAKESTATS) || defined(NQPROT)\n\tint i;\n#endif\n\n//\tint i;\n//\tfor (i = cl.validsequence+1; i < cls.netchan.incoming_sequence; i++)\n//\t{\n//\t\tCon_Printf(\"CL: Dropped %i\\n\", i);\n//\t}\n\n\tif (cls.demoplayback == DPB_MVD)\n\t{\n\t\tcls.netchan.incoming_sequence++;\n\t\tcls.netchan.incoming_acknowledged++;\n\t}\n#ifdef NQPROT\n\telse if (cls.protocol == CP_NETQUAKE)\n\t{\n\t\tcls.netchan.incoming_sequence++;\n\t\tcl.last_servermessage = realtime;\n\t\tif (cls.fteprotocolextensions2 & PEXT2_PREDINFO)\n\t\t{\n\t\t\tinputframe = (unsigned short)MSG_ReadShort();\n\t\t\tinputframe = (cl.movesequence&0xffff0000) | inputframe;\n\t\t\tif (inputframe > cl.movesequence)\n\t\t\t\tinputframe -= 0x00010000;\t//err, if its in the future then cl.movesequence must have wrapped.\n\t\t}\n\t\telse\n\t\t\tinputframe = cl.movesequence;\n\n\t\tif (cl.numackframes == sizeof(cl.ackframes)/sizeof(cl.ackframes[0]))\n\t\t\tcl.numackframes--;\n\t\tif (!cl.validsequence)\n\t\t\tcl.ackframes[cl.numackframes++] = -1;\n\t\telse\n\t\t\tcl.ackframes[cl.numackframes++] = cls.netchan.incoming_unreliable;\n\n\t\t{\n\t\t\textern vec3_t demoangles;\n\t\t\tint fr = cls.netchan.incoming_sequence&UPDATE_MASK;\n\t\t\tfor (i = 0; i < MAX_SPLITS; i++)\n\t\t\t\tcl.inframes[fr&UPDATE_MASK].packet_entities.fixangles[i] = false;\n\t\t\tif (cls.demoplayback)\n\t\t\t{\n\t\t\t\tcl.inframes[fr&UPDATE_MASK].packet_entities.fixangles[0] = 2;\n\t\t\t\tVectorCopy(demoangles, cl.inframes[fr&UPDATE_MASK].packet_entities.fixedangles[0]);\n\t\t\t}\n\t\t}\n\n//\t\tif (cl.validsequence != cls.netchan.incoming_sequence-1)\n//\t\t\tCon_Printf(\"CLIENT: Dropped a frame\\n\");\n\t}\n#endif\n\n\tnewpacket = cls.netchan.incoming_sequence&UPDATE_MASK;\n\toldpacket = cl.validsequence&UPDATE_MASK;\n\tnewp = &cl.inframes[newpacket].packet_entities;\n\toldp = &cl.inframes[oldpacket].packet_entities;\n\tcl.inframes[newpacket].invalid = true;\n\tcl.inframes[newpacket].receivedtime = realtime;\n\tcl.inframes[newpacket].frameid = cls.netchan.incoming_sequence;\n\n#ifdef QUAKESTATS\n\tfor (i = 0; i < cl.splitclients; i++)\n\t{\n\t\tcl.inframes[newpacket&UPDATE_MASK].packet_entities.punchangle[i][0] = cl.playerview[i].statsf[STAT_PUNCHANGLE_X];\n\t\tcl.inframes[newpacket&UPDATE_MASK].packet_entities.punchangle[i][1] = cl.playerview[i].statsf[STAT_PUNCHANGLE_Y];\n\t\tcl.inframes[newpacket&UPDATE_MASK].packet_entities.punchangle[i][2] = cl.playerview[i].statsf[STAT_PUNCHANGLE_Z];\n\t\tcl.inframes[newpacket&UPDATE_MASK].packet_entities.punchorigin[i][0] = cl.playerview[i].statsf[STAT_PUNCHVECTOR_X];\n\t\tcl.inframes[newpacket&UPDATE_MASK].packet_entities.punchorigin[i][1] = cl.playerview[i].statsf[STAT_PUNCHVECTOR_Y];\n\t\tcl.inframes[newpacket&UPDATE_MASK].packet_entities.punchorigin[i][2] = cl.playerview[i].statsf[STAT_PUNCHVECTOR_Z];\n\t}\n#endif\n\n\n\tif (!cl.validsequence || cls.netchan.incoming_sequence-cl.validsequence >= UPDATE_BACKUP-1 || oldp == newp)\n\t{\n\t\t//yes, this results in a load of invalid packets for a while.\n\t\t//server is meant to notice and send a reset packet, which causes it to become valid again\n\t\toldp = &nullp;\n\t\toldp->num_entities = 0;\n\t\toldp->max_entities = 0;\n\t}\n\telse\n\t\tisvalid = true;\n\n\tnewp->servertime = MSG_ReadFloat();\n\n\tif (cl.gametime != newp->servertime)\n\t{\n\t\tcl.oldgametime = cl.gametime;\n\t\tcl.oldgametimemark = cl.gametimemark;\n\t\tcl.gametime = newp->servertime;\n\t\tcl.gametimemark = realtime;\n\t}\n\n\t/*clear all entities*/\n\tnewp->num_entities = 0;\n\tnewp->bonedatacur = 0;\n\toldindex = 0;\n\twhile(1)\n\t{\n\t\t//high bit means remove, second high bit means 22bit index\n\t\tnewnum = (unsigned short)(short)MSG_ReadShort();\n\t\tremoveflag = !!(newnum & 0x8000);\n\t\tif (newnum & 0x4000)\n\t\t\tnewnum = (newnum & 0x3fff) | (MSG_ReadByte()<<14);\n\t\telse\n\t\t\tnewnum &= ~0x8000;\n\n\t\tif ((!newnum && !removeflag) || msg_badread)\n\t\t{\n\t\t\t/*reached the end, don't forget old entities*/\n\t\t\twhile(oldindex < oldp->num_entities)\n\t\t\t{\n\t\t\t\tif (newp->num_entities >= newp->max_entities)\n\t\t\t\t{\n\t\t\t\t\tnewp->max_entities = newp->num_entities+1;\n\t\t\t\t\tnewp->entities = BZ_Realloc(newp->entities, sizeof(entity_state_t)*newp->max_entities);\n\t\t\t\t}\n\n\t\t\t\t//copy it over\n\t\t\t\tnews = &newp->entities[newp->num_entities++];\n\t\t\t\tolds = &oldp->entities[oldindex++];\n\t\t\t\t*news = *olds;\n\t\t\t\tif (news->bonecount)\n\t\t\t\t{\t//still has bone data somehow.\n\t\t\t\t\tshort *bonedata = AllocateBoneSpace(newp, news->bonecount, &news->boneoffset);\n\t\t\t\t\tmemcpy(bonedata, oldp->bonedata+olds->boneoffset, sizeof(short)*7*news->bonecount);\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\n\t\toldnum = (oldindex >= oldp->num_entities) ? 0xffffffff : oldp->entities[oldindex].number;\n\n\t\t/*if we skipped some, then they were unchanged*/\n\t\twhile (newnum > oldnum)\n\t\t{\n\t\t\tif (newp->num_entities >= newp->max_entities)\n\t\t\t{\n\t\t\t\tnewp->max_entities = newp->num_entities+1;\n\t\t\t\tnewp->entities = BZ_Realloc(newp->entities, sizeof(entity_state_t)*newp->max_entities);\n\t\t\t}\n\n\t\t\t//copy it over\n\t\t\tnews = &newp->entities[newp->num_entities++];\n\t\t\tolds = &oldp->entities[oldindex++];\n\t\t\t*news = *olds;\n\t\t\tif (news->bonecount)\n\t\t\t{\t//still has bone data somehow.\n\t\t\t\tshort *bonedata = AllocateBoneSpace(newp, news->bonecount, &news->boneoffset);\n\t\t\t\tmemcpy(bonedata, oldp->bonedata+olds->boneoffset, sizeof(short)*7*news->bonecount);\n\t\t\t}\n\n\t\t\toldnum = (oldindex >= oldp->num_entities) ? 0xffffffff : oldp->entities[oldindex].number;\n\t\t}\n\n\t\tif (removeflag)\n\t\t{\n\t\t\tif (cl_shownet.ival >= 3)\n\t\t\t\tCon_Printf(\"%3i:     Remove %i @ %i\\n\", MSG_GetReadCount(), newnum, cls.netchan.incoming_sequence);\n\n\t\t\tif (!newnum)\n\t\t\t{\n\t\t\t\t/*removal of world - means forget all entities*/\n\t\t\t\tif (cl_shownet.ival >= 3)\n\t\t\t\t\tCon_Printf(\"%3i:     Reset all\\n\", MSG_GetReadCount());\n\t\t\t\tnewp->num_entities = 0;\n\t\t\t\toldp = &nullp;\n\t\t\t\toldp->num_entities = 0;\n\t\t\t\toldp->max_entities = 0;\n\t\t\t\tisvalid = true;\n\n\t\t\t\tcls.demohadkeyframe = true;\t//we can reactivate deltas when recording now.\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (oldnum == newnum)\n\t\t\t\toldindex++;\n\t\t\tcontinue;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (!CL_CheckBaselines(newnum))\n\t\t\t\tHost_EndGame(\"CL_ParsePacketEntities: check baselines failed with size %i\", newnum);\n\n\t\t\tif (newp->num_entities >= newp->max_entities)\n\t\t\t{\n\t\t\t\tnewp->max_entities = newp->num_entities+1;\n\t\t\t\tnewp->entities = BZ_Realloc(newp->entities, sizeof(entity_state_t)*newp->max_entities);\n\t\t\t}\n\n\t\t\tif (oldnum == newnum)\n\t\t\t\tCLFTE_ReadDelta(newnum, &newp->entities[newp->num_entities++], &oldp->entities[oldindex++], cl_baselines + newnum, newp, oldp);\n\t\t\telse\n\t\t\t\tCLFTE_ReadDelta(newnum, &newp->entities[newp->num_entities++], NULL, cl_baselines + newnum, newp, NULL);\n\t\t}\n\t}\n\n\tif (cl.do_lerp_players)\n\t{\n\t\tfloat packetage = (realtime - cl.outframes[cl.ackedmovesequence & UPDATE_MASK].senttime) - cls.latency*cl_predict_players_latency.value + cl_predict_players_nudge.value;\n\t\t//predict in-place based upon calculated latencies and stuff, stuff can then be interpolated properly\n\t\tfor (oldindex = 0; oldindex < newp->num_entities; oldindex++)\n\t\t{\n\t\t\tCL_PredictEntityMovement(newp->entities + oldindex, (newp->entities[oldindex].u.q1.msec / 1000.0f + packetage) *0.5);\n\t\t}\n\t}\n\n\tif (isvalid)\n\t{\n\t\tcl.oldvalidsequence = cl.validsequence;\n\t\tcl.validsequence = cls.netchan.incoming_sequence;\n\t\tCL_AckedInputFrame(cls.netchan.incoming_sequence, inputframe, true);\n\t\tcl.inframes[newpacket].invalid = false;\n\t}\n\telse\n\t{\n\t\tnewp->num_entities = 0;\n\t\tcl.validsequence = 0;\n\n\t\tCL_AckedInputFrame(cls.netchan.incoming_sequence, inputframe, false);\n\t}\n}\n\n/*\n==================\nCL_ParsePacketEntities\n\nAn svc_packetentities has just been parsed, deal with the\nrest of the data stream.\n==================\n*/\nvoid CLQW_ParsePacketEntities (qboolean delta)\n{\n\tint\t\t\toldpacket, newpacket;\n\tpacket_entities_t\t*oldp, *newp, dummy;\n\tint\t\t\toldindex, newindex;\n\tint\t\t\tword, newnum, oldnum;\n\tqboolean\tfull;\n\tint\t\tfrom;\n\n\tnewpacket = cls.netchan.incoming_sequence&UPDATE_MASK;\n\tnewp = &cl.inframes[newpacket].packet_entities;\n\tcl.inframes[newpacket].invalid = false;\n\tcl.inframes[newpacket].frameid = cls.netchan.incoming_sequence;\n\tcl.inframes[newpacket].receivedtime = realtime;\n\n\tif (cls.protocol == CP_QUAKEWORLD && cls.demoplayback == DPB_MVD)\n\t{\n\t\textern float olddemotime;\t//time from the most recent demo packet\n\t\tcl.oldgametime = cl.gametime;\n\t\tcl.oldgametimemark = cl.gametimemark;\n\t\tcl.gametime = olddemotime + cl.demogametimebias;\n\t\tcl.gametimemark = realtime;\n\t}\n\telse if (!(cls.fteprotocolextensions & PEXT_ACCURATETIMINGS) && cls.protocol == CP_QUAKEWORLD)\n\t{\n\t\textern cvar_t cl_demospeed;\n\t\tfloat scale = cls.demoplayback?cl_demospeed.value:1;\n\t\tcl.oldgametime = cl.gametime;\n\t\tcl.oldgametimemark = cl.gametimemark;\n\t\tif (realtime - cl.gametimemark > 0)\n\t\t\tcl.gametime += (realtime - cl.gametimemark)*scale;//cl.frames[newpacket].senttime - cl.frames[(newpacket-1)&UPDATE_MASK].senttime;\n\t\tcl.gametimemark = realtime;\n\t}\n\n\tnewp->servertime = cl.gametime;\n\n\tif (delta)\n\t{\n\t\tfrom = MSG_ReadByte ();\n\n//\t\tCon_Printf(\"%i %i from %i\\n\", cls.netchan.outgoing_sequence, cls.netchan.incoming_sequence, from);\n\t\tif (cls.demoplayback == DPB_MVD)\n\t\t\tfrom = oldpacket = cls.netchan.incoming_sequence - 1;\n\t\toldpacket = cl.inframes[from & UPDATE_MASK].frameid;\n\n\t\tif (cl.inframes[from&UPDATE_MASK].invalid ||\t//old frame is unusable\n\t\t\tcls.netchan.outgoing_sequence - oldpacket >= UPDATE_BACKUP - 1)\t// we must have lost the sequence its trying to delta from (or just too old).\n\t\t{\n\t\t\tFlushEntityPacket ();\n\t\t\treturn;\n\t\t}\n\n\t\toldp = &cl.inframes[from & UPDATE_MASK].packet_entities;\n\t\tfull = false;\n\t}\n\telse\n\t{\t// this is a full update that we can start delta compressing from now\n\t\toldp = &dummy;\n\t\tdummy.num_entities = 0;\n\t\tfull = true;\n\t}\n\n\t//FIXME\n\tcl.oldvalidsequence = cl.validsequence;\n\tcl.validsequence = cls.netchan.incoming_sequence;\n\tCL_AckedInputFrame(cls.netchan.incoming_sequence, cls.netchan.incoming_sequence, true);\n\n\toldindex = 0;\n\tnewindex = 0;\n\tnewp->num_entities = 0;\n\n\twhile (1)\n\t{\n\t\tword = (unsigned short)MSG_ReadShort ();\n\t\tif (msg_badread)\n\t\t{\t// something didn't parse right...\n\t\t\tHost_EndGame (\"msg_badread in packetentities\");\n\t\t\treturn;\n\t\t}\n\n\t\tif (!word)\n\t\t{\n\t\t\twhile (oldindex < oldp->num_entities)\n\t\t\t{\t// copy all the rest of the entities from the old packet\n//Con_Printf (\"copy %i\\n\", oldp->entities[oldindex].number);\n\t\t\t\tif (newindex >= newp->max_entities)\n\t\t\t\t{\n\t\t\t\t\tnewp->max_entities = newindex+1;\n\t\t\t\t\tnewp->entities = BZ_Realloc(newp->entities, sizeof(entity_state_t)*newp->max_entities);\n\t\t\t\t}\n\t\t\t\tif (oldindex >= oldp->max_entities)\n\t\t\t\t\tHost_EndGame(\"Old packet entity too big\\n\");\n\t\t\t\tnewp->entities[newindex] = oldp->entities[oldindex];\n\t\t\t\tnewindex++;\n\t\t\t\toldindex++;\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t\tnewnum = word&511;\n\n\t\tif (word & U_MOREBITS)\n\t\t{\n\t\t\tint oldpos = MSG_GetReadCount();\n\t\t\tint excessive;\n\t\t\texcessive = MSG_ReadByte();\n\t\t\tif (excessive & U_EVENMORE)\n\t\t\t{\n\t\t\t\texcessive = MSG_ReadByte();\n\t\t\t\tif (excessive & U_ENTITYDBL)\n\t\t\t\t\tnewnum += 512;\n\t\t\t\tif (excessive & U_ENTITYDBL2)\n\t\t\t\t\tnewnum += 1024;\n\t\t\t}\n\n\t\t\tMSG_ReadSkip(oldpos-MSG_GetReadCount());//undo the read...\n\t\t}\n\t\toldnum = oldindex >= oldp->num_entities ? 9999 : oldp->entities[oldindex].number;\n\n\t\twhile (newnum > oldnum)\n\t\t{\n\t\t\tif (full)\n\t\t\t{\n\t\t\t\tCon_Printf (\"WARNING: oldcopy on full update\");\n\t\t\t\tFlushEntityPacket ();\n\t\t\t\treturn;\n\t\t\t}\n\n//Con_Printf (\"copy %i\\n\", oldnum);\n\t\t\t// copy one of the old entities over to the new packet unchanged\n\t\t\tif (newindex >= newp->max_entities)\n\t\t\t{\n\t\t\t\tnewp->max_entities = newindex+1;\n\t\t\t\tnewp->entities = BZ_Realloc(newp->entities, sizeof(entity_state_t)*newp->max_entities);\n\t\t\t}\n\t\t\tif (oldindex >= oldp->max_entities)\n\t\t\t\tHost_EndGame(\"Old packet entity too big\\n\");\n\t\t\tnewp->entities[newindex] = oldp->entities[oldindex];\n\t\t\tnewindex++;\n\t\t\toldindex++;\n\t\t\toldnum = oldindex >= oldp->num_entities ? 9999 : oldp->entities[oldindex].number;\n\t\t}\n\n\t\tif (newnum < oldnum)\n\t\t{\t// new from baseline\n//Con_Printf (\"baseline %i\\n\", newnum);\n\t\t\tif (word & U_REMOVE)\n\t\t\t{\t//really read the extra entity number if required\n\t\t\t\tif (word & U_MOREBITS)\n\t\t\t\t\tif (MSG_ReadByte() & U_EVENMORE)\n\t\t\t\t\t\tMSG_ReadByte();\n\n\t\t\t\tif (full)\n\t\t\t\t{\n\t\t\t\t\tcl.validsequence = 0;\n\t\t\t\t\tCon_Printf (\"WARNING: U_REMOVE on full update\\n\");\n\t\t\t\t\tFlushEntityPacket ();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (newindex >= newp->max_entities)\n\t\t\t{\n\t\t\t\tnewp->max_entities = newindex+1;\n\t\t\t\tnewp->entities = BZ_Realloc(newp->entities, sizeof(entity_state_t)*newp->max_entities);\n\t\t\t}\n\n\t\t\tif (!CL_CheckBaselines(newnum))\n\t\t\t\tHost_EndGame(\"CL_ParsePacketEntities: check baselines failed with size %i\", newnum);\n\t\t\tCLQW_ParseDelta (cl_baselines + newnum, &newp->entities[newindex], word);\n\t\t\tnewindex++;\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (newnum == oldnum)\n\t\t{\t// delta from previous\n\t\t\tif (full)\n\t\t\t{\n\t\t\t\tcl.validsequence = 0;\n\t\t\t\tCon_Printf (\"WARNING: delta on full update\");\n\t\t\t}\n\t\t\tif (word & U_REMOVE)\n\t\t\t{\n\t\t\t\tif (word & U_MOREBITS)\n\t\t\t\t\tif (MSG_ReadByte() & U_EVENMORE)\n\t\t\t\t\t\tMSG_ReadByte();\n\t\t\t\toldindex++;\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (newindex >= newp->max_entities)\n\t\t\t{\n\t\t\t\tnewp->max_entities = newindex+1;\n\t\t\t\tnewp->entities = BZ_Realloc(newp->entities, sizeof(entity_state_t)*newp->max_entities);\n\t\t\t}\n\n//Con_Printf (\"delta %i\\n\",newnum);\n\t\t\tCLQW_ParseDelta (&oldp->entities[oldindex], &newp->entities[newindex], word);\n\t\t\tnewindex++;\n\t\t\toldindex++;\n\t\t}\n\n\t}\n\n\tnewp->num_entities = newindex;\n}\n\n\nentity_state_t *CL_FindOldPacketEntity(int num)\n{\n\tint\t\t\t\t\tpnum;\n\tentity_state_t\t\t*s1;\n\tpacket_entities_t\t*pack;\n\tif (!cl.validsequence)\n\t\treturn NULL;\n\tpack = &cl.inframes[(cls.netchan.incoming_sequence-1)&UPDATE_MASK].packet_entities;\n\n\tfor (pnum=0 ; pnum<pack->num_entities ; pnum++)\n\t{\n\t\ts1 = &pack->entities[pnum];\n\n\t\tif (num == s1->number)\n\t\t\treturn s1;\n\t}\n\treturn NULL;\n}\n#ifdef NQPROT\nvoid DP5_ParseDelta(entity_state_t *s, packet_entities_t *pack)\n{\n\tunsigned int bits;\n\n\tif (cl_shownet.ival >= 3)\n\t\tCon_Printf(\"%3i:     Update %i\", MSG_GetReadCount(), s->number);\n\n\tbits = MSG_ReadByte();\n\tif (bits & E5_EXTEND1)\n\t{\n\t\tbits |= MSG_ReadByte() << 8;\n\t\tif (bits & E5_EXTEND2)\n\t\t{\n\t\t\tbits |= MSG_ReadByte() << 16;\n\t\t\tif (bits & E5_EXTEND3)\n\t\t\t\tbits |= MSG_ReadByte() << 24;\n\t\t}\n\t}\n\n\tif (cl_shownet.ival >= 3)\n\t{\n\t\tif (bits & E5_FULLUPDATE)\t\tCon_Printf(\" full\");\n\t\tif (bits & E5_ORIGIN)\t\t\tCon_Printf(\" origin\");\n\t\tif (bits & E5_ANGLES)\t\t\tCon_Printf(\" angles\");\n\t\tif (bits & E5_MODEL)\t\t\tCon_Printf(\" model\");\n\t\tif (bits & E5_FRAME)\t\t\tCon_Printf(\" frame\");\n\t\tif (bits & E5_SKIN)\t\t\t\tCon_Printf(\" kin\");\n\t\tif (bits & E5_EFFECTS)\t\t\tCon_Printf(\" effects\");\n\t\tif (bits & E5_EXTEND1)\t\t\tCon_Printf(\" extend1\");\n\t\tif (bits & E5_FLAGS)\t\t\tCon_Printf(\" flags\");\n\t\tif (bits & E5_ALPHA)\t\t\tCon_Printf(\" alpha\");\n\t\tif (bits & E5_SCALE)\t\t\tCon_Printf(\" scale\");\n\t\tif (bits & E5_ORIGIN32)\t\t\tCon_Printf(\" origin32\");\n\t\tif (bits & E5_ANGLES16)\t\t\tCon_Printf(\" angles16\");\n\t\tif (bits & E5_MODEL16)\t\t\tCon_Printf(\" model16\");\n\t\tif (bits & E5_COLORMAP)\t\t\tCon_Printf(\" colormap\");\n\t\tif (bits & E5_EXTEND2)\t\t\tCon_Printf(\" extend2\");\n\t\tif (bits & E5_ATTACHMENT)\t\tCon_Printf(\" attachment\");\n\t\tif (bits & E5_LIGHT)\t\t\tCon_Printf(\" light\");\n\t\tif (bits & E5_GLOW)\t\t\t\tCon_Printf(\" glow\");\n\t\tif (bits & E5_EFFECTS16)\t\tCon_Printf(\" effects16\");\n\t\tif (bits & E5_EFFECTS32)\t\tCon_Printf(\" effects32\");\n\t\tif (bits & E5_FRAME16)\t\t\tCon_Printf(\" frame16\");\n\t\tif (bits & E5_COLORMOD)\t\t\tCon_Printf(\" colormod\");\n\t\tif (bits & E5_EXTEND3)\t\t\tCon_Printf(\" extend3\");\n\t\tif (bits & E5_GLOWMOD)\t\t\tCon_Printf(\" glowmod\");\n\t\tif (bits & E5_COMPLEXANIMATION)\tCon_Printf(\" complexanimation\");\n\t\tif (bits & E5_TRAILEFFECTNUM)\tCon_Printf(\" traileffectnum\");\n\t\tif (bits & E5_UNUSED27)\t\t\tCon_Printf(\" unused27\");\n\t\tif (bits & E5_UNUSED28)\t\t\tCon_Printf(\" unused28\");\n\t\tif (bits & E5_UNUSED29)\t\t\tCon_Printf(\" unused29\");\n\t\tif (bits & E5_UNUSED30)\t\t\tCon_Printf(\" unused30\");\n\t\tif (bits & E5_EXTEND4)\t\t\tCon_Printf(\" extend4\");\n\t\tCon_Printf(\"\\n\");\n\t}\n\n\tif (bits & E5_ALLUNUSED)\n\t{\n\t\tHost_EndGame(\"Detected 'unused' bits in DP5+ entity delta - %x (%x)\\n\", bits, (bits & E5_ALLUNUSED));\n\t}\n\n\tif (bits & E5_FULLUPDATE)\n\t{\n\t\tint num;\n\t\tnum = s->number;\n\t\t*s = nullentitystate;\n\t\ts->number = num;\n\t\ts->solidsize = ES_SOLID_BSP;\n//\t\ts->active = true;\n\t}\n\tif (bits & E5_FLAGS)\n\t{\n\t\tint i = MSG_ReadByte();\n\t\ts->dpflags = i;\n\t}\n\tif (bits & E5_ORIGIN)\n\t{\n\t\tif (bits & E5_ORIGIN32)\n\t\t{\n\t\t\ts->origin[0] = MSG_ReadFloat();\n\t\t\ts->origin[1] = MSG_ReadFloat();\n\t\t\ts->origin[2] = MSG_ReadFloat();\n\t\t}\n\t\telse\n\t\t{\n\t\t\ts->origin[0] = MSG_ReadShort()*(1/8.0f);\n\t\t\ts->origin[1] = MSG_ReadShort()*(1/8.0f);\n\t\t\ts->origin[2] = MSG_ReadShort()*(1/8.0f);\n\t\t}\n\t}\n\tif (bits & E5_ANGLES)\n\t{\n\t\tif (bits & E5_ANGLES16)\n\t\t{\n\t\t\ts->angles[0] = MSG_ReadAngle16();\n\t\t\ts->angles[1] = MSG_ReadAngle16();\n\t\t\ts->angles[2] = MSG_ReadAngle16();\n\t\t}\n\t\telse\n\t\t{\n\t\t\ts->angles[0] = MSG_ReadChar() * (360.0/256);\n\t\t\ts->angles[1] = MSG_ReadChar() * (360.0/256);\n\t\t\ts->angles[2] = MSG_ReadChar() * (360.0/256);\n\t\t}\n\t}\n\tif (bits & E5_MODEL)\n\t{\n\t\tif (bits & E5_MODEL16)\n\t\t\ts->modelindex = (unsigned short) MSG_ReadShort();\n\t\telse\n\t\t\ts->modelindex = MSG_ReadByte();\n\t}\n\tif (bits & E5_FRAME)\n\t{\n\t\tif (bits & E5_FRAME16)\n\t\t\ts->frame = (unsigned short) MSG_ReadShort();\n\t\telse\n\t\t\ts->frame = MSG_ReadByte();\n\t}\n\tif (bits & E5_SKIN)\n\t\ts->skinnum = MSG_ReadByte();\n\tif (bits & E5_EFFECTS)\n\t{\n\t\tif (bits & E5_EFFECTS32)\n\t\t\ts->effects = (unsigned int) MSG_ReadLong();\n\t\telse if (bits & E5_EFFECTS16)\n\t\t\ts->effects = (unsigned short) MSG_ReadShort();\n\t\telse\n\t\t\ts->effects = MSG_ReadByte();\n\t}\n\tif (bits & E5_ALPHA)\n\t\ts->trans = MSG_ReadByte();\n\tif (bits & E5_SCALE)\n\t\ts->scale = MSG_ReadByte();\n\tif (bits & E5_COLORMAP)\n\t\ts->colormap = MSG_ReadByte();\n\tif (bits & E5_ATTACHMENT)\n\t{\n\t\ts->tagentity = MSGCL_ReadEntity();\n\t\ts->tagindex = MSG_ReadByte();\n\t}\n\tif (bits & E5_LIGHT)\n\t{\n\t\ts->light[0] = MSG_ReadShort();\n\t\ts->light[1] = MSG_ReadShort();\n\t\ts->light[2] = MSG_ReadShort();\n\t\ts->light[3] = MSG_ReadShort();\n\t\ts->lightstyle = MSG_ReadByte();\n\t\ts->lightpflags = MSG_ReadByte();\n\t}\n\tif (bits & E5_GLOW)\n\t{\n\t\ts->glowsize = MSG_ReadByte();\n\t\ts->glowcolour = MSG_ReadByte();\n\t}\n\tif (bits & E5_COLORMOD)\n\t{\n\t\ts->colormod[0] = MSG_ReadByte();\n\t\ts->colormod[1] = MSG_ReadByte();\n\t\ts->colormod[2] = MSG_ReadByte();\n\t}\n\tif (bits & E5_GLOWMOD)\n\t{\n\t\ts->glowmod[0] = MSG_ReadByte();\n\t\ts->glowmod[1] = MSG_ReadByte();\n\t\ts->glowmod[2] = MSG_ReadByte();\n\t}\n\tif (bits & E5_COMPLEXANIMATION)\n\t{\n\t\tint type = MSG_ReadByte();\n\t\tint i, numbones;\n\t\tif (type == 4)\n\t\t{\n\t\t\tshort *bonedata;\n\n\t\t\t/*modelindex = */MSG_ReadShort();\n\t\t\tnumbones = MSG_ReadByte();\n\n\t\t\tbonedata = AllocateBoneSpace(pack, numbones, &s->boneoffset);\n\t\t\ts->bonecount = numbones;\n\t\t\tfor (i = 0; i < numbones*7; i++)\n\t\t\t\tbonedata[i] = MSG_ReadShort();\n\t\t}\n\t\telse if (type < 4)\n\t\t{\t//n-way blends\n\t\t\ts->bonecount = 0;\n\t\t\ttype++;\n\t\t\tfor (i = 0; i < type; i++)\n\t\t\t\t/*frame = */MSG_ReadShort();\n\t\t\tfor (i = 0; i < type; i++)\n\t\t\t\t/*age = */MSG_ReadShort();\n\t\t\tfor (i = 0; i < type; i++)\n\t\t\t\t/*frac = */(type==1)?255:MSG_ReadByte();\n\t\t}\n\t\telse\n\t\t\tHost_Error(\"E5_COMPLEXANIMATION: Parse error - unknown type %i\\n\", type);\n\t}\n\tif (bits & E5_TRAILEFFECTNUM)\n\t\ts->u.q1.traileffectnum = MSG_ReadShort();\n}\n\nstatic int QDECL CLDP_SortEntities(const void *va, const void *vb)\n{\n\tconst entity_state_t *a = va, *b = vb;\n\tif (a->inactiveflag != b->inactiveflag)\n\t\treturn a->inactiveflag?1:-1;\n\tif (a->number != b->number)\n\t\treturn a->number < b->number?-1:1;\n\treturn 0;\n}\n\nvoid CLDP_ParseDarkPlaces5Entities(void)\t//the things I do.. :o(\n{\n\t//the incoming entities do not come in in any order. :(\n\t//well, they come in in order of priorities, but that's not useful to us.\n\t//I guess this means we'll have to go slowly.\n\n\t//dp deltas update in-place\n\t//this gets in the way of tracking multiple frames, and thus doesn't match fte too well\n\n\n\tpacket_entities_t\t*oldpack, *newpack;\n\n\tentity_state_t\t\t*to, *from;\n\tunsigned int read;\n\tint oldi;\n\tqboolean remove;\n\n\t//server->client sequence\n\tif (cl.numackframes == sizeof(cl.ackframes)/sizeof(cl.ackframes[0]))\n\t\tcl.numackframes--;\n\tcl.ackframes[cl.numackframes++] = MSG_ReadLong(); /*server sequence to be acked*/\n\n\t//client->server sequence ack\n\tif (cls.protocol_nq >= CPNQ_DP7)\n\t\tCL_AckedInputFrame(cls.netchan.incoming_sequence, MSG_ReadLong(), true); /*client input sequence which has been acked*/\n\n\tif (cl.validsequence)\n\t\toldpack = &cl.inframes[(cl.validsequence)&UPDATE_MASK].packet_entities;\n\telse\n\t\toldpack = NULL;\n\tcl.validsequence = cls.netchan.incoming_sequence;\n\tcl.inframes[(cls.netchan.incoming_sequence)&UPDATE_MASK].receivedtime = realtime;\n\tcl.inframes[(cls.netchan.incoming_sequence)&UPDATE_MASK].frameid = cls.netchan.incoming_sequence;\n\tnewpack = &cl.inframes[(cls.netchan.incoming_sequence)&UPDATE_MASK].packet_entities;\n\tnewpack->servertime = cl.gametime;\n\n\t//copy old state to new state\n\tif (newpack != oldpack)\n\t{\n\t\tif (oldpack)\n\t\t{\n\t\t\tnewpack->num_entities = oldpack->num_entities;\n\t\t\tnewpack->max_entities = newpack->num_entities+16;\t//for slop for new ents, to reduce reallocs\n\t\t\tnewpack->entities = BZ_Realloc(newpack->entities, sizeof(entity_state_t)*newpack->max_entities);\n\t\t\tmemcpy(newpack->entities, oldpack->entities, sizeof(entity_state_t)*newpack->num_entities);\n\t\t}\n\t\telse\n\t\t\tnewpack->num_entities = 0;\n\t\tnewpack->bonedatacur = 0;\n\n\t\t//flag them all as having old bones\n\t\t//they'll be renewed after parsing\n\t\tfor (oldi=0 ; oldi<newpack->num_entities ; oldi++)\n\t\t\tnewpack->entities[oldi].boneoffset |= 0x80000000;\n\t}\n\n\tfor (;;)\n\t{\n\t\tread = MSG_ReadShort();\n\t\tif (msg_badread)\n\t\t\tHost_EndGame(\"Corrupt entity message packet\\n\");\n\t\tremove = !!(read&0x8000);\n\t\tread&=0x7fff;\n\t\tif (remove && !read)\n\t\t\tbreak;\t//remove world signals end of packet.\n\n\t\tif (read >= MAX_EDICTS)\n\t\t\tHost_EndGame(\"Too many entities.\\n\");\n\n\t\tfrom = &nullentitystate;\n\t\tto = NULL;\n\n\t\tfor (oldi=0 ; oldi<newpack->num_entities ; oldi++)\n\t\t{\n\t\t\tif (read == newpack->entities[oldi].number)\n\t\t\t{\n\t\t\t\tfrom = &newpack->entities[oldi];\n\t\t\t\tto = &newpack->entities[oldi];\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tif (!to)\n\t\t{\t//okay, so this is new\n\t\t\tif (newpack->num_entities==newpack->max_entities)\n\t\t\t{\n\t\t\t\tnewpack->max_entities = newpack->num_entities+16;\n\t\t\t\tnewpack->entities = BZ_Realloc(newpack->entities, sizeof(entity_state_t)*newpack->max_entities);\n\t\t\t}\n\n\t\t\tto = &newpack->entities[newpack->num_entities];\n\t\t\tnewpack->num_entities++;\n\t\t}\n\n\t\tmemcpy(to, from, sizeof(*to));\n\t\tto->number = read;\n\n\t\tif (remove)\n\t\t{\t//ent is meant to be removed. flag it as such. we'll strip it out later.\n\t\t\tif (cl_shownet.ival >= 3)\n\t\t\t\tCon_Printf(\"Remove %i\\n\", read);\n\t\t\tto->inactiveflag = 1;\n\t\t\tto->bonecount = 0;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (cl_shownet.ival > 3)\n\t\t\t\tCon_Printf(\"Update %i\\n\", read);\n\t\t\tDP5_ParseDelta(to, newpack);\n\t\t\tto->sequence = cls.netchan.incoming_sequence;\n\t\t\tto->inactiveflag = 0;\n\t\t}\n\t}\n\n\tqsort(newpack->entities, newpack->num_entities, sizeof(entity_state_t), CLDP_SortEntities);\n\n\t//get rid of any removed ents (we sorted these to the end)\n\twhile (newpack->num_entities)\n\t{\n\t\tif (newpack->entities[newpack->num_entities-1].inactiveflag)\n\t\t\tnewpack->num_entities--;\n\t\telse\n\t\t\tbreak;\n\t}\n\n\t//make sure any bone states are refreshed\n\tfor (oldi=0, to = newpack->entities; oldi<newpack->num_entities ; oldi++, to++)\n\t{\n\t\tif (to->bonecount && (to->boneoffset & 0x80000000))\n\t\t{\n\t\t\tunsigned int oldoffset = to->boneoffset & 0x7fffffff;\n\t\t\tvoid *dest = AllocateBoneSpace(newpack, to->bonecount, &to->boneoffset);\n\t\t\tvoid *src = GetBoneSpace(oldpack, oldoffset);\n\t\t\tmemcpy(dest, src, to->bonecount * sizeof(short)*7);\n\t\t}\n\t}\n}\n\n#ifdef HEXEN2\n#define UH2_MOREBITS\t(1u<<0)\n#define UH2_ORIGIN1\t\t(1u<<1)\n#define UH2_ORIGIN2\t\t(1u<<2)\n#define UH2_ORIGIN3\t\t(1u<<3)\n#define UH2_ANGLE2\t\t(1u<<4)\n#define UH2_STEP\t\t(1u<<5)\n#define UH2_FRAME\t\t(1u<<6)\n#define UH2_SIGNAL\t\t(1u<<7)\n\n#define UH2_ANGLE1\t\t(1u<<8)\n#define UH2_ANGLE3\t\t(1u<<9)\n#define UH2_MODEL\t\t(1u<<10)\n//#define UH2_\t\t\t(1u<<11)\n//#define UH2_\t\t\t(1u<<12)\n//#define UH2_\t\t\t(1u<<13)\n#define UH2_LONGENTITY\t(1u<<14)\n#define UH2_EVENMORE\t(1u<<15)\n\n#define UH2_SKIN\t\t(1u<<16)\n#define UH2_EFFECTS\t\t(1u<<17)\n#define UH2_SCALE\t\t(1u<<18)\n#define UH2_COLORMAP\t(1u<<19)\n\nvoid CLH2_ParseEntities(void)\n{\n\t//h2mp apparently uses some sort of delta compression\n\t//there's three parts to this, the start, the updates, and the removes at the end.\n\t//so we can be a bit lazy and parse the 'fast updates' here and assert they end with a final clear.\n\t//entities are ordered.\n\n\tpacket_entities_t\t*oldpack, *newpack;\n\n\tentity_state_t\t\t*to, *from;\n\tunsigned int read, bits;\n\tint oldi;\n\tunsigned int removecount;\n\n\tint frame = MSG_ReadByte();\n\tint seq = MSG_ReadByte();\n\n\t//not really sure what to do with this.\n\t(void)frame;\n\t(void)seq;\n\n\tif (cl.validsequence)\n\t\toldpack = &cl.inframes[(cl.validsequence)&UPDATE_MASK].packet_entities;\n\telse\n\t\toldpack = NULL;\n\tcl.validsequence = cls.netchan.incoming_sequence;\n\tcl.inframes[(cls.netchan.incoming_sequence)&UPDATE_MASK].receivedtime = realtime;\n\tcl.inframes[(cls.netchan.incoming_sequence)&UPDATE_MASK].frameid = cls.netchan.incoming_sequence;\n\tnewpack = &cl.inframes[(cls.netchan.incoming_sequence)&UPDATE_MASK].packet_entities;\n\tnewpack->servertime = cl.gametime;\n\n\t//copy old state to new state\n\tif (newpack != oldpack)\n\t{\n\t\tif (oldpack)\n\t\t{\n\t\t\tnewpack->num_entities = oldpack->num_entities;\n\t\t\tnewpack->max_entities = newpack->num_entities+16;\t//for slop for new ents, to reduce reallocs\n\t\t\tnewpack->entities = BZ_Realloc(newpack->entities, sizeof(entity_state_t)*newpack->max_entities);\n\t\t\tmemcpy(newpack->entities, oldpack->entities, sizeof(entity_state_t)*newpack->num_entities);\n\t\t}\n\t\telse\n\t\t\tnewpack->num_entities = 0;\n\t\tnewpack->bonedatacur = 0;\n\n\t\t//flag them all as having old bones\n\t\t//they'll be renewed after parsing\n\t\tfor (oldi=0 ; oldi<newpack->num_entities ; oldi++)\n\t\t\tnewpack->entities[oldi].boneoffset |= 0x80000000;\n\t}\n\n\tfor (;;)\n\t{\n\t\tbits = MSG_ReadByte();\n\t\tif ((bits&0x80) == 0)\n\t\t\tbreak;\t//no fast-update bit!\n\t\tif (bits & UH2_MOREBITS)\n\t\t\tbits |= MSG_ReadByte()<<8;\n\t\tif (bits & UH2_EVENMORE)\n\t\t\tbits |= MSG_ReadByte()<<16;\n\t\tif (bits & UH2_LONGENTITY)\n\t\t\tread = MSG_ReadUInt16();\n\t\telse\n\t\t\tread = MSG_ReadByte();\n\n\t\tif (msg_badread)\n\t\t\tHost_EndGame(\"Corrupt entity message packet\\n\");\n\n\t\tif (!read)\n\t\t\tbreak;\t//remove world signals end of packet.\n\n\t\tif (read >= MAX_EDICTS)\n\t\t\tHost_EndGame(\"Too many entities.\\n\");\n\n\t\tif (!CL_CheckBaselines(read))\n\t\t\tHost_EndGame(\"CLNQ_ParseEntity: check baselines failed with size %i\", read);\n\t\tfrom = &cl_baselines[read];\n\t\tto = NULL;\n\n\t\tfor (oldi=0 ; oldi<newpack->num_entities ; oldi++)\n\t\t{\n\t\t\tif (read == newpack->entities[oldi].number)\n\t\t\t{\n\t\t\t\tfrom = &newpack->entities[oldi];\n\t\t\t\tto = &newpack->entities[oldi];\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tif (!to)\n\t\t{\t//okay, so this is new\n\t\t\tif (newpack->num_entities==newpack->max_entities)\n\t\t\t{\n\t\t\t\tnewpack->max_entities = newpack->num_entities+16;\n\t\t\t\tnewpack->entities = BZ_Realloc(newpack->entities, sizeof(entity_state_t)*newpack->max_entities);\n\t\t\t}\n\n\t\t\tto = &newpack->entities[newpack->num_entities];\n\t\t\tnewpack->num_entities++;\n\n\t\t\tif (cl_shownet.ival >= 3)\n\t\t\t\tCon_Printf(\"%3i:     New %i %x\\n\", MSG_GetReadCount(), to->number, bits);\n\t\t}\n\t\telse if (cl_shownet.ival >= 3)\n\t\t\tCon_Printf(\"%3i:     Update %i %x\\n\", MSG_GetReadCount(), to->number, bits);\n\n\t\tmemcpy(to, from, sizeof(*to));\n\t\tto->number = read;\n\n\t\tif (bits & UH2_MODEL)\tto->modelindex = MSG_ReadShort();\n\t\tif (bits & UH2_FRAME)\tto->frame = MSG_ReadByte();\n\t\tif (bits & UH2_COLORMAP)to->colormap = MSG_ReadByte();\n\t\tif (bits & UH2_SKIN)\tto->skinnum = MSG_ReadByte();\n\t\tif (bits & UH2_SKIN)\tto->hexen2flags = MSG_ReadByte();\t//yes, shared with skin\n\t\tif (bits & UH2_EFFECTS)\tto->effects = MSG_ReadByte();\n\t\tif (bits & UH2_ORIGIN1)\tto->origin[0] = MSG_ReadCoord();\n\t\tif (bits & UH2_ANGLE1)\tto->angles[0] = MSG_ReadAngle();\n\t\tif (bits & UH2_ORIGIN2)\tto->origin[1] = MSG_ReadCoord();\n\t\tif (bits & UH2_ANGLE2)\tto->angles[1] = MSG_ReadAngle();\n\t\tif (bits & UH2_ORIGIN3)\tto->origin[2] = MSG_ReadCoord();\n\t\tif (bits & UH2_ANGLE3)\tto->angles[2] = MSG_ReadAngle();\n\t\tif (bits & UH2_SCALE)\tto->scale = (MSG_ReadByte()/100.0)*16;\n\t\tif (bits & UH2_SCALE)\tto->abslight = MSG_ReadByte();\n\n\t\tto->sequence = cls.netchan.incoming_sequence;\n\t\tto->inactiveflag = 0;\n\t}\n\n\t//handle the removes\n\tif (bits != 48)\n\t\tHost_EndGame(\"Corrupt entity message packet\\n\");\n\tremovecount = (qbyte)MSG_ReadByte();\n\twhile (removecount --> 0)\n\t{\n\t\tread = MSG_ReadUInt16();\n\t\tfor (oldi=0 ; oldi<newpack->num_entities ; oldi++)\n\t\t{\n\t\t\tif (read == newpack->entities[oldi].number)\n\t\t\t{\n\t\t\t\tnewpack->entities[oldi].inactiveflag = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\t//sort them, just in case. the removes will bubble to the end.\n\tqsort(newpack->entities, newpack->num_entities, sizeof(entity_state_t), CLDP_SortEntities);\n\twhile (newpack->num_entities)\n\t{\t//pop those removes.\n\t\tif (newpack->entities[newpack->num_entities-1].inactiveflag)\n\t\t\tnewpack->num_entities--;\n\t\telse\n\t\t\tbreak;\n\t}\n\n\n\t//make sure any bone states are refreshed\n\tfor (oldi=0, to = newpack->entities; oldi<newpack->num_entities ; oldi++, to++)\n\t{\n\t\tif (to->bonecount && (to->boneoffset & 0x80000000))\n\t\t{\n\t\t\tunsigned int oldoffset = to->boneoffset & 0x7fffffff;\n\t\t\tvoid *dest = AllocateBoneSpace(newpack, to->bonecount, &to->boneoffset);\n\t\t\tvoid *src = GetBoneSpace(oldpack, oldoffset);\n\t\t\tmemcpy(dest, src, to->bonecount * sizeof(short)*7);\n\t\t}\n\t}\n}\n#endif\n\nvoid CLNQ_ParseEntity(unsigned int bits)\n{\n\tint i;\n\tint num;\n\tentity_state_t\t\t*state;//, *from;\n\tentity_state_t\t*base;\n\tpacket_entities_t\t*pack;\n\n\tqboolean isnehahra = CPNQ_IS_BJP||(cls.protocol_nq == CPNQ_NEHAHRA);\n\tqboolean floatcoords;\n\n\tif (cls.signon == 4 - 1)\n\t{\t// first update is the final signon stage\n\t\tcls.signon = 4;\n\t\tCLNQ_SignonReply ();\n\t}\n\tpack = &cl.inframes[cls.netchan.incoming_sequence&UPDATE_MASK].packet_entities;\n\n\n\tif (bits & NQU_MOREBITS)\n\t{\n\t\ti = MSG_ReadByte ();\n\t\tbits |= (i<<8);\n\t}\n\n\tif (bits & DPU_EXTEND1)\n\t{\n\t\tif (!isnehahra)\n\t\t{\n\t\t\ti = MSG_ReadByte ();\n\t\t\tbits |= (i<<16);\n\t\t\n\t\t\tif (bits & DPU_EXTEND2)\n\t\t\t{\n\t\t\t\ti = MSG_ReadByte ();\n\t\t\t\tbits |= (i<<24);\n\t\t\t}\n\t\t}\n\t}\n\n\tif (bits & NQU_LONGENTITY)\n\t\tnum = MSGCL_ReadEntity ();\n\telse\n\t\tnum = MSG_ReadByte ();\n\n//\tstate = CL_FindPacketEntity(num);\n//\tif (!state)\n\t{\n//\t\tif ((int)(lasttime*100) != (int)(realtime*100))\n//\t\t\tpack->num_entities=0;\n//\t\telse\n\t\t\tif (pack->num_entities==pack->max_entities)\n\t\t{\n\t\t\tpack->max_entities = pack->num_entities+1;\n\t\t\tpack->entities = BZ_Realloc(pack->entities, sizeof(entity_state_t)*pack->max_entities);\n\t\t\tmemset(pack->entities + pack->num_entities, 0, sizeof(entity_state_t));\n\t\t}\n\t\tstate = &pack->entities[pack->num_entities++];\n\t}\n\n//\tfrom = CL_FindOldPacketEntity(num);\t//this could be optimised.\n\n\tif (!CL_CheckBaselines(num))\n\t\tHost_EndGame(\"CLNQ_ParseEntity: check baselines failed with size %i\", num);\n\tbase = cl_baselines + num;\n\tmemcpy(state, base, sizeof(*state));\n\n\tstate->number = num;\n\tstate->sequence = cls.netchan.incoming_sequence;\n\tstate->solidsize = ES_SOLID_BSP;\n\n\tstate->dpflags = 0;\n\n\tfloatcoords = cls.qex && (bits & QE_U_FLOATCOORDS);\n\n\tif (bits & NQU_MODEL)\n\t{\n\t\tif (CPNQ_IS_BJP)\n\t\t\tstate->modelindex = MSG_ReadShort ();\n\t\telse\n\t\t\tstate->modelindex = MSG_ReadByte ();\n\t}\n\n\tif (bits & NQU_FRAME)\n\t\tstate->frame = MSG_ReadByte();\n\n\tif (bits & NQU_COLORMAP)\n\t\tstate->colormap = MSG_ReadByte();\n\n\tif (bits & NQU_SKIN)\n\t\tstate->skinnum = MSG_ReadByte();\n\n\tif (bits & NQU_EFFECTS)\n\t{\n\t\ti = MSG_ReadByte();\n\t\tif (cls.qex)\n\t\t{\n\t\t\tunsigned fixed = i & ~(REEF_QUADLIGHT|REEF_PENTLIGHT|REEF_CANDLELIGHT);\n\t\t\tif (i & REEF_QUADLIGHT)\n\t\t\t\tfixed |= EF_BLUE;\n\t\t\tif (i & REEF_PENTLIGHT)\n\t\t\t\tfixed |= EF_RED;\n\t\t\tif (i & REEF_CANDLELIGHT)\n\t\t\t\tfixed |= 0;\t//tiny light\n\t\t\ti = fixed;\n\t\t}\n\t\tstate->effects = i;\n\t}\n\n\tif (bits & NQU_ORIGIN1)\n\t\tstate->origin[0] = floatcoords?MSG_ReadFloat():MSG_ReadCoord ();\n\tif (bits & NQU_ANGLE1)\n\t\tstate->angles[0] = MSG_ReadAngle();\n\n\tif (bits & NQU_ORIGIN2)\n\t\tstate->origin[1] = floatcoords?MSG_ReadFloat():MSG_ReadCoord ();\n\tif (bits & NQU_ANGLE2)\n\t\tstate->angles[1] = MSG_ReadAngle();\n\n\tif (bits & NQU_ORIGIN3)\n\t\tstate->origin[2] = floatcoords?MSG_ReadFloat():MSG_ReadCoord ();\n\tif (bits & NQU_ANGLE3)\n\t\tstate->angles[2] = MSG_ReadAngle();\n\n\tif (bits & NQU_NOLERP)\n\t\tstate->dpflags |= RENDER_STEP;\n\n\tif (isnehahra)\n\t{\n\t\tif (bits & DPU_EXTEND1)\t//U_TRANS\n\t\t{\n\t\t\tfloat tmp = MSG_ReadFloat();\n\t\t\tfloat alpha = MSG_ReadFloat();\n\t\t\tif (tmp == 2)\n\t\t\t{\n\t\t\t\tif (MSG_ReadFloat() > 0.5)\n\t\t\t\t\tstate->effects |= EF_FULLBRIGHT;\n\t\t\t}\n\t\t\tif (!alpha)\n\t\t\t\talpha = 1;\n\t\t\tstate->trans = bound(0, 255 * alpha, 255);\n\t\t}\n\t}\n\telse if (cls.protocol_nq == CPNQ_FITZ666)\n\t{\n\t\tif (bits & FITZU_ALPHA)\n\t\t\tstate->trans = (MSG_ReadByte()-1)&0xff;\n\n\t\tif (bits & RMQU_SCALE)\n\t\t\tstate->scale = MSG_ReadByte();\n\n\t\tif (bits & FITZU_FRAME2)\n\t\t\tstate->frame = (state->frame & 0xff) | (MSG_ReadByte() << 8);\n\n\t\tif (bits & FITZU_MODEL2)\n\t\t\tstate->modelindex = (state->modelindex & 0xff) | (MSG_ReadByte() << 8);\n\n\t\tif (bits & FITZU_LERPFINISH)\n\t\t\tstate->lerpend = cl.gametime + MSG_ReadByte()/255.0f;\n\n\t\tif (cls.qex)\n\t\t{\n\t\t\tif (bits & QE_U_SOLIDTYPE)\t/*state->solidsize =*/ MSG_ReadByte();\t\t//needed for correct prediction\n\t\t\tif (bits & QE_U_ENTFLAGS)\t/*state->entflags = */ MSG_ReadULEB128();\t//for onground/etc state\n\t\t\tif (bits & QE_U_HEALTH)\t\t/*state->health =*/ MSG_ReadSignedQEX();\t//health... not really sure why, I suppose it changes player physics (they should have sent movetype instead though).\n\t\t\tif (bits & QE_U_UNKNOWN26)\t/*unknown =*/MSG_ReadByte();\n\t\t\tif (bits & QE_U_UNUSED27)\tCon_Printf(CON_WARNING\"QE_U_UNUSED27: %u\\n\", MSG_ReadByte());\n\n\t\t\tif (bits & QE_U_UNUSED28)\tCon_Printf(CON_WARNING\"QE_U_UNUSED28: %u\\n\", MSG_ReadByte());\n\t\t\tif (bits & QE_U_UNUSED29)\tCon_Printf(CON_WARNING\"QE_U_UNUSED29: %u\\n\", MSG_ReadByte());\n\t\t\tif (bits & QE_U_UNUSED30)\tCon_Printf(CON_WARNING\"QE_U_UNUSED30: %u\\n\", MSG_ReadByte());\n\t\t\tif (bits & QE_U_UNUSED31)\tCon_Printf(CON_WARNING\"QE_U_UNUSED31: %u\\n\", MSG_ReadByte());\n\t\t}\n\t}\n\telse\n\t{\t//dp tends to leak stuff, so parse as quakedp if the normal protocol doesn't define it as something better.\n\n//\t\tif (bits & DPU_DELTA)\t//should delta from the previous frame. DP doesn't generate this any more, so whatever.\n//\t\t\tHost_EndGame(\"CLNQ_ParseEntity: DPU_DELTA not supported\");\n\n\t\tif (bits & DPU_ALPHA)\n\t\t\tstate->trans = MSG_ReadByte();\n\n\t\tif (bits & DPU_SCALE)\n\t\t\tstate->scale = MSG_ReadByte();\n\n\t\tif (bits & DPU_EFFECTS2)\n\t\t\tstate->effects |= MSG_ReadByte() << 8;\n\n\t\tif (bits & DPU_GLOWSIZE)\n\t\t\tstate->glowsize = MSG_ReadByte();\n\n\t\tif (bits & DPU_GLOWCOLOR)\n\t\t\tstate->glowcolour = MSG_ReadByte();\n\n\t\tif (bits & DPU_COLORMOD)\n\t\t{\n\t\t\ti = MSG_ReadByte(); // follows format RRRGGGBB\n\t\t\tstate->colormod[0] = (qbyte)(((i >> 5) & 7) * (32.0f / 7.0f));\n\t\t\tstate->colormod[1] = (qbyte)(((i >> 2) & 7) * (32.0f / 7.0f));\n\t\t\tstate->colormod[2] = (qbyte)((i & 3) * (32.0f / 3.0f));\n\t\t}\n\n\t\tif (bits & DPU_GLOWTRAIL)\n\t\t\tstate->dpflags |= RENDER_GLOWTRAIL;\n\n\t\tif (bits & DPU_FRAME2)\n\t\t\tstate->frame |= MSG_ReadByte() << 8;\n\n\t\tif (bits & DPU_MODEL2)\n\t\t\tstate->modelindex |= MSG_ReadByte() << 8;\n\n\t\tif (bits & DPU_VIEWMODEL)\n\t\t\tstate->dpflags |= RENDER_VIEWMODEL;\n\t\tif (bits & DPU_EXTERIORMODEL)\n\t\t\tstate->dpflags |= RENDER_EXTERIORMODEL;\n\t}\n}\n#endif\n#ifdef PEXT_SETVIEW\nentity_state_t *CL_FindPacketEntity(int num)\n{\n\tint\t\t\t\t\tpnum;\n\tentity_state_t\t\t*s1;\n\tpacket_entities_t\t*pack = cl.currentpackentities;\n\tif (pack)\n\t\tfor (pnum=0 ; pnum<pack->num_entities ; pnum++)\n\t\t{\n\t\t\ts1 = &pack->entities[pnum];\n\n\t\t\tif (num == s1->number)\n\t\t\t\treturn s1;\n\t\t}\n\treturn NULL;\n}\n#endif\n\nvoid CL_RotateAroundTag(entity_t *ent, int entnum, int parenttagent, int parenttagnum)\n{\n\tentity_state_t *ps;\n\tfloat *org=NULL, *ang=NULL;\n\tvec3_t axis[3];\n\tfloat transform[12], parent[12], result[12], old[12], temp[12];\n\n\tmodel_t *model;\n\tframestate_t fstate;\n\n\tif (parenttagent >= cl.maxlerpents)\n\t{\n\t\tCon_Printf(\"tag entity out of range!\\n\");\n\t\treturn;\n\t}\n\n\t//old is the entity's relative transform (relative to the parent entity's tag)\n\told[0] = ent->axis[0][0];\n\told[1] = ent->axis[1][0];\n\told[2] = ent->axis[2][0];\n\told[3] = ent->origin[0];\n\told[4] = ent->axis[0][1];\n\told[5] = ent->axis[1][1];\n\told[6] = ent->axis[2][1];\n\told[7] = ent->origin[1];\n\told[8] = ent->axis[0][2];\n\told[9] = ent->axis[1][2];\n\told[10] = ent->axis[2][2];\n\told[11] = ent->origin[2];\n\n\tmemset(&fstate, 0, sizeof(fstate));\n\n\t//for visibility checks\n\tent->keynum = parenttagent;\n\n\tps = CL_FindPacketEntity(parenttagent);\n\tif (ps)\n\t{\n\t\tif (parenttagent >= cl.maxlerpents)\n\t\t{\n\t\t\torg = ps->origin;\n\t\t\tang = ps->angles;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tlerpents_t *le = &cl.lerpents[parenttagent];\n\t\t\torg = le->origin;\n\t\t\tang = le->angles;\n\t\t}\n\n\t\tif (ps->modelindex <= countof(cl.model_precache) && cl.model_precache[ps->modelindex] && cl.model_precache[ps->modelindex]->loadstate == MLS_LOADED)\n\t\t\tmodel = cl.model_precache[ps->modelindex];\n\t\telse\n\t\t\tmodel = NULL;\n\t\tif (model && model->type == mod_alias)\n\t\t\tAngleVectorsMesh(ang, axis[0], axis[1], axis[2]);\n\t\telse\n\t\t\tAngleVectors(ang, axis[0], axis[1], axis[2]);\n\t\tVectorInverse(axis[1]);\n\n\t\tparent[0] = axis[0][0];\n\t\tparent[1] = axis[1][0];\n\t\tparent[2] = axis[2][0];\n\t\tparent[3] = org[0];\n\t\tparent[4] = axis[0][1];\n\t\tparent[5] = axis[1][1];\n\t\tparent[6] = axis[2][1];\n\t\tparent[7] = org[1];\n\t\tparent[8] = axis[0][2];\n\t\tparent[9] = axis[1][2];\n\t\tparent[10] = axis[2][2];\n\t\tparent[11] = org[2];\n\n\t\tCL_LerpNetFrameState(&fstate, &cl.lerpents[parenttagent]);\n\n\t\t/*inherit certain properties from the parent entity*/\n\t\tif (ps->dpflags & RENDER_VIEWMODEL)\n\t\t\tent->flags |= RF_WEAPONMODEL|Q2RF_MINLIGHT|RF_DEPTHHACK;\n\t\tif ((ps->dpflags & RENDER_EXTERIORMODEL) || r_refdef.playerview->viewentity == ps->number)\n\t\t\tent->flags |= RF_EXTERNALMODEL;\n\n\t\t//hack for xonotic.\n\t\tif ((ent->flags & RF_WEAPONMODEL) && ent->playerindex == -1 && ps->colormap > 0 && ps->colormap <= cl.allocated_client_slots)\n\t\t{\n\t\t\tent->playerindex = ps->colormap-1;\n\t\t\tent->topcolour    = cl.players[ent->playerindex].dtopcolor;\n\t\t\tent->bottomcolour = cl.players[ent->playerindex].dbottomcolor;\n\t\t}\n\t}\n\telse\n\t{\n\t\textern int parsecountmod;\n//\t\tCon_Printf(\"tagent %i\\n\", tagent);\n\t\tif (parenttagent <= cl.allocated_client_slots && parenttagent > 0)\n\t\t{\n\t\t\tif (parenttagent == cl.playerview[0].playernum+1)\n\t\t\t{\n\t\t\t\torg = cl.playerview[0].simorg;\n\t\t\t\tang = cl.playerview[0].simangles;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\torg = cl.inframes[parsecountmod].playerstate[parenttagent-1].origin;\n\t\t\t\tang = cl.inframes[parsecountmod].playerstate[parenttagent-1].viewangles;\n\t\t\t}\n\t\t\tmodel = cl.model_precache[cl.inframes[parsecountmod].playerstate[parenttagent-1].modelindex];\n\n\t\t\tCL_LerpNetFrameState(&fstate, &cl.lerpplayers[parenttagent-1]);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tCL_LerpNetFrameState(&fstate, &cl.lerpents[parenttagent]);\n\t\t\tmodel = 0;\n\t\t}\n\t}\n\n\t{\n//\t\tfstate.g[FS_REG].lerpfrac = CL_EntLerpFactor(tagent);\n//\t\tfstate.g[FS_REG].frametime[0] = cl.time - cl.lerpents[tagent].framechange;\n//\t\tfstate.g[FS_REG].frametime[1] = cl.time - cl.lerpents[tagent].oldframechange;\n\n\t\tif (Mod_GetTag(model, parenttagnum, &fstate, transform))\n\t\t{\n//\t\t\tparent -> transform -> old\n\n\t\t\tR_ConcatTransforms((void*)parent, (void*)transform, (void*)temp);\n\t\t\tR_ConcatTransforms((void*)temp, (void*)old, (void*)result);\n\n\t\t\tent->axis[0][0] = result[0];\n\t\t\tent->axis[1][0] = result[1];\n\t\t\tent->axis[2][0] = result[2];\n\t\t\tent->origin[0] = result[3];\n\t\t\tent->axis[0][1] = result[4];\n\t\t\tent->axis[1][1] = result[5];\n\t\t\tent->axis[2][1] = result[6];\n\t\t\tent->origin[1] = result[7];\n\t\t\tent->axis[0][2] = result[8];\n\t\t\tent->axis[1][2] = result[9];\n\t\t\tent->axis[2][2] = result[10];\n\t\t\tent->origin[2] = result[11];\n\t\t}\n\t\telse\t//hrm.\n\t\t{\n\t\t\tR_ConcatTransforms((void*)parent, (void*)old, (void*)result);\n\n\t\t\tent->axis[0][0] = result[0];\n\t\t\tent->axis[1][0] = result[1];\n\t\t\tent->axis[2][0] = result[2];\n\t\t\tent->origin[0] = result[3];\n\t\t\tent->axis[0][1] = result[4];\n\t\t\tent->axis[1][1] = result[5];\n\t\t\tent->axis[2][1] = result[6];\n\t\t\tent->origin[1] = result[7];\n\t\t\tent->axis[0][2] = result[8];\n\t\t\tent->axis[1][2] = result[9];\n\t\t\tent->axis[2][2] = result[10];\n\t\t\tent->origin[2] = result[11];\n\t\t}\n\t}\n\n\tif (ps && ps->tagentity)\n\t\tCL_RotateAroundTag(ent, entnum, ps->tagentity, ps->tagindex);\n}\n\nvoid V_AddAxisEntity(entity_t *in)\n{\n\tentity_t *ent;\n\n\tif (cl_numvisedicts == cl_maxvisedicts)\n\t{\n\t\treturn;\t\t// object list is full\n\t}\n\tent = &cl_visedicts[cl_numvisedicts];\n\tcl_numvisedicts++;\n\n\t*ent = *in;\n}\nvoid V_ClearEntity(entity_t *e)\n{\n\tmemset(e, 0, sizeof(*e));\n\te->pvscache.num_leafs = -1;\n\te->playerindex = -1;\n\te->topcolour = TOP_DEFAULT;\n\te->bottomcolour = BOTTOM_DEFAULT;\n}\nentity_t *V_AddEntity(entity_t *in)\n{\n\tentity_t *ent;\n\n\tif (cl_numvisedicts == cl_maxvisedicts)\n\t{\n\t\treturn NULL;\t\t// object list is full\n\t}\n\tent = &cl_visedicts[cl_numvisedicts];\n\tcl_numvisedicts++;\n\n\t*ent = *in;\n\n\tAngleVectorsMesh(ent->angles, ent->axis[0], ent->axis[1], ent->axis[2]);\n\tVectorInverse(ent->axis[1]);\n\n\treturn ent;\n}\nentity_t *V_AddNewEntity(void)\n{\n\tentity_t *ent;\n\n\tif (cl_numvisedicts == cl_maxvisedicts)\n\t{\n\t\tcl_expandvisents = true;\n\t\treturn NULL;\t\t// object list is full\n\t}\n\tent = &cl_visedicts[cl_numvisedicts];\n\tcl_numvisedicts++;\n\treturn ent;\n}\n/*\nvoid VQ2_AddLerpEntity(entity_t *in)\t//a convienience function\n{\n\tentity_t *ent;\n\tfloat fwds, back;\n\tint i;\n\n\tif (cl_numvisedicts == MAX_VISEDICTS)\n\t\treturn;\t\t// object list is full\n\tent = &cl_visedicts[cl_numvisedicts];\n\tcl_numvisedicts++;\n\n\t*ent = *in;\n\n\tfwds = ent->framestate.g[FS_REG].lerpfrac;\n\tback = 1 - ent->framestate.g[FS_REG].lerpfrac;\n\tfor (i = 0; i < 3; i++)\n\t{\n\t\tent->origin[i] = in->origin[i]*fwds + in->oldorigin[i]*back;\n\t}\n\n\tent->framestate.g[FS_REG].lerpfrac = back;\n\n\tAngleVectorsMesh(ent->angles, ent->axis[0], ent->axis[1], ent->axis[2]);\n\tVectorInverse(ent->axis[1]);\n}\n*/\nint V_AddLight (int entsource, vec3_t org, float quant, float r, float g, float b)\n{\n\treturn CL_NewDlight (entsource, org, quant, -0.1, r*5, g*5, b*5) - cl_dlights;\n}\n\nvoid CLQ1_AddOrientedHalfSphere(shader_t *shader, float radius, float gap, float *matrix, float r, float g, float b, float a)\n{\n\t//use simple algo\n\t//a series of cylinders that gets progressively narrower\n\tconst int latsteps = 16;\n\tconst int lngsteps = 8;//16;\n\tfloat cradius;\n\tint v, i, j;\n\tscenetris_t *t;\n\tvec3_t corner;\n\tfloat x,y;\n\tint flags = BEF_NODLIGHT|BEF_NOSHADOWS;\n\n\tif (!r && !g && !b)\n\t\treturn;\n\n\t/*reuse the previous trigroup if its the same shader*/\n\tif (cl_numstris && cl_stris[cl_numstris-1].shader == shader && cl_stris[cl_numstris-1].flags == flags && cl_stris[cl_numstris-1].numvert < MAX_INDICIES-(latsteps-1)*(lngsteps-1))\n\t\tt = &cl_stris[cl_numstris-1];\n\telse\n\t{\n\t\tif (cl_numstris == cl_maxstris)\n\t\t{\n\t\t\tcl_maxstris += 8;\n\t\t\tcl_stris = BZ_Realloc(cl_stris, sizeof(*cl_stris)*cl_maxstris);\n\t\t}\n\t\tt = &cl_stris[cl_numstris++];\n\t\tt->shader = shader;\n\t\tt->numidx = 0;\n\t\tt->numvert = 0;\n\t\tt->firstidx = cl_numstrisidx;\n\t\tt->firstvert = cl_numstrisvert;\n\t\tt->flags = flags;\n\t}\n\n\tif (cl_numstrisvert + latsteps*lngsteps > cl_maxstrisvert)\n\t\tcl_stris_ExpandVerts(cl_numstrisvert + latsteps*lngsteps);\n\tif (cl_maxstrisidx < cl_numstrisidx+latsteps*(lngsteps-1)*6)\n\t{\n\t\tcl_maxstrisidx = cl_numstrisidx+latsteps*(lngsteps-1)*6 + 64;\n\t\tcl_strisidx = BZ_Realloc(cl_strisidx, sizeof(*cl_strisidx)*cl_maxstrisidx);\n\t}\n\n\tfor (i = 0; i < latsteps; i++)\n\t{\n\t\tx = sin(i * 2 * M_PI / latsteps);\n\t\ty = cos(i * 2 * M_PI / latsteps);\n\t\tfor (j = 0; j < lngsteps; j++)\n\t\t{\n\t\t\tv = i*lngsteps + j;\n\t\t\tcradius = sin(j * 0.5 * M_PI / (lngsteps-1))*radius;\n\t\t\tcorner[0] = x*cradius;\n\t\t\tcorner[1] = y*cradius;\n\t\t\tcorner[2] = (cos(j * 0.5 * M_PI / (lngsteps-1))*-radius) - gap;\n\t\t\tMatrix3x4_RM_Transform3(matrix, corner, cl_strisvertv[cl_numstrisvert+v]);\n\n\t\t\tcl_strisvertt[cl_numstrisvert+v][0] = 0;\n\t\t\tcl_strisvertt[cl_numstrisvert+v][1] = 0;\n\n\t\t\tcl_strisvertc[cl_numstrisvert+v][0] = r;\n\t\t\tcl_strisvertc[cl_numstrisvert+v][1] = g;\n\t\t\tcl_strisvertc[cl_numstrisvert+v][2] = b;\n\t\t\tcl_strisvertc[cl_numstrisvert+v][3] = a;\n\t\t}\n\t}\n\n\tif (radius < 0)\n\t{\n\t\tfor (i = 0; i < lngsteps-1; i++)\n\t\t{\n\t\t\tv = latsteps-1;\n\t\t\tfor (v = 0; v < latsteps-1; v++)\n\t\t\t{\n\t\t\t\tcl_strisidx[cl_numstrisidx++] = cl_numstrisvert-t->firstvert+lngsteps\t+ v*lngsteps + i;\n\t\t\t\tcl_strisidx[cl_numstrisidx++] = cl_numstrisvert-t->firstvert+0\t\t\t+ v*lngsteps + i;\n\t\t\t\tcl_strisidx[cl_numstrisidx++] = cl_numstrisvert-t->firstvert+1\t\t\t+ v*lngsteps + i;\n\t\t\t\tcl_strisidx[cl_numstrisidx++] = cl_numstrisvert-t->firstvert+lngsteps+1\t+ v*lngsteps + i;\n\t\t\t\tcl_strisidx[cl_numstrisidx++] = cl_numstrisvert-t->firstvert+lngsteps\t+ v*lngsteps + i;\n\t\t\t\tcl_strisidx[cl_numstrisidx++] = cl_numstrisvert-t->firstvert+1\t\t\t+ v*lngsteps + i;\n\t\t\t}\n\t\t\tcl_strisidx[cl_numstrisidx++] = cl_numstrisvert-t->firstvert\t\t\t\t\t+ i;\n\t\t\tcl_strisidx[cl_numstrisidx++] = cl_numstrisvert-t->firstvert+0\t\t\t\t+ v*lngsteps + i;\n\t\t\tcl_strisidx[cl_numstrisidx++] = cl_numstrisvert-t->firstvert+1\t\t\t\t+ v*lngsteps + i;\n\t\t\tcl_strisidx[cl_numstrisidx++] = cl_numstrisvert-t->firstvert+1\t\t\t\t+ i;\n\t\t\tcl_strisidx[cl_numstrisidx++] = cl_numstrisvert-t->firstvert\t\t\t\t\t+ i;\n\t\t\tcl_strisidx[cl_numstrisidx++] = cl_numstrisvert-t->firstvert+1\t\t\t\t+ v*lngsteps + i;\n\t\t}\n\t}\n\telse\n\t{\n\t\tfor (i = 0; i < lngsteps-1; i++)\n\t\t{\n\t\t\tv = latsteps-1;\n\t\t\tfor (v = 0; v < latsteps-1; v++)\n\t\t\t{\n\t\t\t\tcl_strisidx[cl_numstrisidx++] = cl_numstrisvert-t->firstvert+0\t\t\t+ v*lngsteps + i;\n\t\t\t\tcl_strisidx[cl_numstrisidx++] = cl_numstrisvert-t->firstvert+lngsteps\t+ v*lngsteps + i;\n\t\t\t\tcl_strisidx[cl_numstrisidx++] = cl_numstrisvert-t->firstvert+1\t\t\t+ v*lngsteps + i;\n\t\t\t\tcl_strisidx[cl_numstrisidx++] = cl_numstrisvert-t->firstvert+lngsteps\t+ v*lngsteps + i;\n\t\t\t\tcl_strisidx[cl_numstrisidx++] = cl_numstrisvert-t->firstvert+lngsteps+1\t+ v*lngsteps + i;\n\t\t\t\tcl_strisidx[cl_numstrisidx++] = cl_numstrisvert-t->firstvert+1\t\t\t+ v*lngsteps + i;\n\t\t\t}\n\t\t\tcl_strisidx[cl_numstrisidx++] = cl_numstrisvert-t->firstvert+0\t\t\t\t+ v*lngsteps + i;\n\t\t\tcl_strisidx[cl_numstrisidx++] = cl_numstrisvert-t->firstvert\t\t\t\t\t+ i;\n\t\t\tcl_strisidx[cl_numstrisidx++] = cl_numstrisvert-t->firstvert+1\t\t\t\t+ v*lngsteps + i;\n\t\t\tcl_strisidx[cl_numstrisidx++] = cl_numstrisvert-t->firstvert\t\t\t\t\t+ i;\n\t\t\tcl_strisidx[cl_numstrisidx++] = cl_numstrisvert-t->firstvert+1\t\t\t\t+ i;\n\t\t\tcl_strisidx[cl_numstrisidx++] = cl_numstrisvert-t->firstvert+1\t\t\t\t+ v*lngsteps + i;\n\t\t}\n\t}\n\n\tt->numvert += lngsteps*latsteps;\n\tt->numidx = cl_numstrisidx - t->firstidx;\n\tcl_numstrisvert += lngsteps*latsteps;\n}\n\nvoid CLQ1_AddOrientedSphere(shader_t *shader, float radius, float *matrix, float r, float g, float b, float a)\n{\n\tCLQ1_AddOrientedHalfSphere(shader, radius, 0, matrix, r, g, b, a);\n\tCLQ1_AddOrientedHalfSphere(shader, -radius, 0, matrix, r, g, b, a);\n}\n\nvoid CLQ1_AddOrientedCylinder(shader_t *shader, float radius, float height, qboolean capsule, float *matrix, float r, float g, float b, float a)\n{\n\tint sides = 16;\n\tint v;\n\tscenetris_t *t;\n\tvec3_t corner;\n\tint flags = BEF_NODLIGHT|BEF_NOSHADOWS;\n\n\tif (!r && !g && !b)\n\t\treturn;\n\n\tradius *= 0.5;\n\theight *= 0.5;\n\n\tif (capsule)\n\t\theight -= radius;\n\n\tif (height > 0)\n\t{\n\t\t/*reuse the previous trigroup if its the same shader*/\n\t\tif (cl_numstris && cl_stris[cl_numstris-1].shader == shader && cl_stris[cl_numstris-1].flags == flags)\n\t\t\tt = &cl_stris[cl_numstris-1];\n\t\telse\n\t\t{\n\t\t\tif (cl_numstris == cl_maxstris)\n\t\t\t{\n\t\t\t\tcl_maxstris += 8;\n\t\t\t\tcl_stris = BZ_Realloc(cl_stris, sizeof(*cl_stris)*cl_maxstris);\n\t\t\t}\n\t\t\tt = &cl_stris[cl_numstris++];\n\t\t\tt->shader = shader;\n\t\t\tt->numidx = 0;\n\t\t\tt->numvert = 0;\n\t\t\tt->firstidx = cl_numstrisidx;\n\t\t\tt->firstvert = cl_numstrisvert;\n\t\t\tt->flags = flags;\n\t\t}\n\n\t\tif (cl_numstrisvert + sides*2 > cl_maxstrisvert)\n\t\t\tcl_stris_ExpandVerts(cl_numstrisvert + sides*2);\n\t\tif (cl_maxstrisidx < cl_numstrisidx+sides*6)\n\t\t{\n\t\t\tcl_maxstrisidx = cl_numstrisidx+sides*6 + 64;\n\t\t\tcl_strisidx = BZ_Realloc(cl_strisidx, sizeof(*cl_strisidx)*cl_maxstrisidx);\n\t\t}\n\n\n\t\tfor (v = 0; v < sides*2; v++)\n\t\t{\n\t\t\tcorner[0] = sin((v>>1) * 2 * M_PI / sides)*radius;\n\t\t\tcorner[1] = cos((v>>1) * 2 * M_PI / sides)*radius;\n\t\t\tcorner[2] = (v & 1)?height:-height;\n\t\t\tMatrix3x4_RM_Transform3(matrix, corner, cl_strisvertv[cl_numstrisvert+v]);\n\n\t\t\tcl_strisvertt[cl_numstrisvert+v][0] = 0;\n\t\t\tcl_strisvertt[cl_numstrisvert+v][1] = 0;\n\n\t\t\tcl_strisvertc[cl_numstrisvert+v][0] = r;\n\t\t\tcl_strisvertc[cl_numstrisvert+v][1] = g;\n\t\t\tcl_strisvertc[cl_numstrisvert+v][2] = b;\n\t\t\tcl_strisvertc[cl_numstrisvert+v][3] = a;\n\t\t}\n\t\tfor (v = 0; v < sides-1; v++)\n\t\t{\n\t\t\tcl_strisidx[cl_numstrisidx++] = cl_numstrisvert-t->firstvert+2 + v*2;\n\t\t\tcl_strisidx[cl_numstrisidx++] = cl_numstrisvert-t->firstvert+1 + v*2;\n\t\t\tcl_strisidx[cl_numstrisidx++] = cl_numstrisvert-t->firstvert+0 + v*2;\n\t\t\tcl_strisidx[cl_numstrisidx++] = cl_numstrisvert-t->firstvert+3 + v*2;\n\t\t\tcl_strisidx[cl_numstrisidx++] = cl_numstrisvert-t->firstvert+1 + v*2;\n\t\t\tcl_strisidx[cl_numstrisidx++] = cl_numstrisvert-t->firstvert+2 + v*2;\n\t\t}\n\t\tcl_strisidx[cl_numstrisidx++] = cl_numstrisvert-t->firstvert+0;\n\t\tcl_strisidx[cl_numstrisidx++] = cl_numstrisvert-t->firstvert+1 + v*2;\n\t\tcl_strisidx[cl_numstrisidx++] = cl_numstrisvert-t->firstvert+0 + v*2;\n\t\tcl_strisidx[cl_numstrisidx++] = cl_numstrisvert-t->firstvert+1;\n\t\tcl_strisidx[cl_numstrisidx++] = cl_numstrisvert-t->firstvert+1 + v*2;\n\t\tcl_strisidx[cl_numstrisidx++] = cl_numstrisvert-t->firstvert+0;\n\n\t\tif (!capsule)\n\t\t{\n\t\t\tfor (v = 4; v < sides*2; v+=2)\n\t\t\t{\n\t\t\t\tcl_strisidx[cl_numstrisidx++] = cl_numstrisvert-t->firstvert+v;\n\t\t\t\tcl_strisidx[cl_numstrisidx++] = cl_numstrisvert-t->firstvert+(v-2);\n\t\t\t\tcl_strisidx[cl_numstrisidx++] = cl_numstrisvert-t->firstvert+0;\n\n\t\t\t\tcl_strisidx[cl_numstrisidx++] = cl_numstrisvert-t->firstvert+1;\n\t\t\t\tcl_strisidx[cl_numstrisidx++] = cl_numstrisvert-t->firstvert+(v-2)+1;\n\t\t\t\tcl_strisidx[cl_numstrisidx++] = cl_numstrisvert-t->firstvert+v+1;\n\t\t\t}\n\t\t}\n\n\t\tt->numvert += sides*2;\n\t\tt->numidx = cl_numstrisidx - t->firstidx;\n\t\tcl_numstrisvert += sides*2;\n\t}\n\n\tif (capsule)\n\t{\n\t\tCLQ1_AddOrientedHalfSphere(shader, radius, height, matrix, r, g, b, a);\n\t\tCLQ1_AddOrientedHalfSphere(shader, -radius, -height, matrix, r, g, b, a);\n\t}\n}\nvoid CLQ1_DrawLine(shader_t *shader, vec3_t v1, vec3_t v2, float r, float g, float b, float a)\n{\n\tscenetris_t *t;\n\tint flags = BEF_NODLIGHT|BEF_NOSHADOWS|BEF_LINES;\n\n\tif (cl_numstris && cl_stris[cl_numstris-1].shader == shader && cl_stris[cl_numstris-1].flags == flags)\n\t\tt = &cl_stris[cl_numstris-1];\n\telse\n\t{\n\t\tif (cl_numstris == cl_maxstris)\n\t\t{\n\t\t\tcl_maxstris += 8;\n\t\t\tcl_stris = BZ_Realloc(cl_stris, sizeof(*cl_stris)*cl_maxstris);\n\t\t}\n\t\tt = &cl_stris[cl_numstris++];\n\t\tt->shader = shader;\n\t\tt->numidx = 0;\n\t\tt->numvert = 0;\n\t\tt->firstidx = cl_numstrisidx;\n\t\tt->firstvert = cl_numstrisvert;\n\t\tt->flags = flags;\n\t}\n\tif (cl_numstrisvert + 2 > cl_maxstrisvert)\n\t\tcl_stris_ExpandVerts(cl_numstrisvert + 2);\n\tif (cl_maxstrisidx < cl_numstrisidx+2)\n\t{\n\t\tcl_maxstrisidx = cl_numstrisidx+2;\n\t\tcl_strisidx = BZ_Realloc(cl_strisidx, sizeof(*cl_strisidx)*cl_maxstrisidx);\n\t}\n\n\tVectorCopy(v1, cl_strisvertv[cl_numstrisvert+0]);\n\tcl_strisvertt[cl_numstrisvert+0][0] = 0;\n\tcl_strisvertt[cl_numstrisvert+0][1] = 0;\n\tcl_strisvertc[cl_numstrisvert+0][0] = r;\n\tcl_strisvertc[cl_numstrisvert+0][1] = g;\n\tcl_strisvertc[cl_numstrisvert+0][2] = b;\n\tcl_strisvertc[cl_numstrisvert+0][3] = a;\n\n\tVectorCopy(v2, cl_strisvertv[cl_numstrisvert+1]);\n\tcl_strisvertt[cl_numstrisvert+1][0] = 0;\n\tcl_strisvertt[cl_numstrisvert+1][1] = 0;\n\tcl_strisvertc[cl_numstrisvert+1][0] = r;\n\tcl_strisvertc[cl_numstrisvert+1][1] = g;\n\tcl_strisvertc[cl_numstrisvert+1][2] = b;\n\tcl_strisvertc[cl_numstrisvert+1][3] = a;\n\n\tcl_strisidx[cl_numstrisidx++] = cl_numstrisvert-t->firstvert+0;\n\tcl_strisidx[cl_numstrisidx++] = cl_numstrisvert-t->firstvert+1;\n\n\tt->numvert += 2;\n\tt->numidx = cl_numstrisidx - t->firstidx;\n\tcl_numstrisvert += 2;\n}\nvoid CLQ1_AddSpriteQuad(shader_t *shader, vec3_t mid, float radius)\n{\n\tfloat r=1, g=1, b=1;\n\tscenetris_t *t;\n\tint flags = BEF_NODLIGHT|BEF_NOSHADOWS;\n\n\tif (cl_numstris && cl_stris[cl_numstris-1].shader == shader && cl_stris[cl_numstris-1].flags == flags && cl_stris[cl_numstris-1].numvert + 4 <= MAX_INDICIES)\n\t\tt = &cl_stris[cl_numstris-1];\n\telse\n\t{\n\t\tif (cl_numstris == cl_maxstris)\n\t\t{\n\t\t\tcl_maxstris+=8;\n\t\t\tcl_stris = BZ_Realloc(cl_stris, sizeof(*cl_stris)*cl_maxstris);\n\t\t}\n\t\tt = &cl_stris[cl_numstris++];\n\t\tt->shader = shader;\n\t\tt->firstidx = cl_numstrisidx;\n\t\tt->firstvert = cl_numstrisvert;\n\t\tt->numvert = 0;\n\t\tt->numidx = 0;\n\t\tt->flags = flags;\n\t}\n\n\tif (cl_numstrisidx+6 > cl_maxstrisidx)\n\t{\n\t\tcl_maxstrisidx=cl_numstrisidx+6 + 64;\n\t\tcl_strisidx = BZ_Realloc(cl_strisidx, sizeof(*cl_strisidx)*cl_maxstrisidx);\n\t}\n\tif (cl_numstrisvert+4 > cl_maxstrisvert)\n\t\tcl_stris_ExpandVerts(cl_maxstrisvert+64);\n\n\t{\n\t\tVectorMA(mid, radius, vright,     cl_strisvertv[cl_numstrisvert]);\n\t\tVectorMA(cl_strisvertv[cl_numstrisvert], radius, vup,   cl_strisvertv[cl_numstrisvert]);\n\t\tVector4Set(cl_strisvertc[cl_numstrisvert], r, g, b, 0.2);\n\t\tVector2Set(cl_strisvertt[cl_numstrisvert], 1, 1);\n\t\tcl_numstrisvert++;\n\n\t\tVectorMA(mid, radius, vright,  cl_strisvertv[cl_numstrisvert]);\n\t\tVectorMA(cl_strisvertv[cl_numstrisvert], -radius, vup, cl_strisvertv[cl_numstrisvert]);\n\t\tVector4Set(cl_strisvertc[cl_numstrisvert], r, g, b, 0.2);\n\t\tVector2Set(cl_strisvertt[cl_numstrisvert], 1, 0);\n\t\tcl_numstrisvert++;\n\n\t\tVectorMA(mid, -radius, vright,    cl_strisvertv[cl_numstrisvert]);\n\t\tVectorMA(cl_strisvertv[cl_numstrisvert], -radius, vup,  cl_strisvertv[cl_numstrisvert]);\n\t\tVector4Set(cl_strisvertc[cl_numstrisvert], r, g, b, 0.2);\n\t\tVector2Set(cl_strisvertt[cl_numstrisvert], 0, 0);\n\t\tcl_numstrisvert++;\n\n\t\tVectorMA(mid, -radius, vright,    cl_strisvertv[cl_numstrisvert]);\n\t\tVectorMA(cl_strisvertv[cl_numstrisvert], radius, vup,   cl_strisvertv[cl_numstrisvert]);\n\t\tVector4Set(cl_strisvertc[cl_numstrisvert], r, g, b, 0.2);\n\t\tVector2Set(cl_strisvertt[cl_numstrisvert], 0, 1);\n\t\tcl_numstrisvert++;\n\t}\n\n\t/*build the triangles*/\n\tcl_strisidx[cl_numstrisidx++] = t->numvert + 0;\n\tcl_strisidx[cl_numstrisidx++] = t->numvert + 1;\n\tcl_strisidx[cl_numstrisidx++] = t->numvert + 2;\n\n\tcl_strisidx[cl_numstrisidx++] = t->numvert + 0;\n\tcl_strisidx[cl_numstrisidx++] = t->numvert + 2;\n\tcl_strisidx[cl_numstrisidx++] = t->numvert + 3;\n\n\n\tt->numidx = cl_numstrisidx - t->firstidx;\n\tt->numvert += 4;\n}\n#include \"shader.h\"\nvoid CL_DrawDebugPlane(float *normal, float dist, float r, float g, float b, qboolean enqueue)\n{\n\tconst float radius = 8192;\t//infinite is quite small nowadays.\n\tscenetris_t *t;\n\tif (!enqueue)\n\t\tcl_numstris = 0;\n\n\tif (cl_numstris == cl_maxstris)\n\t{\n\t\tcl_maxstris+=8;\n\t\tcl_stris = BZ_Realloc(cl_stris, sizeof(*cl_stris)*cl_maxstris);\n\t}\n\tt = &cl_stris[cl_numstris++];\n\tt->shader = R_RegisterShader(\"testplane\", SUF_NONE, \"{\\n{\\nmap $whiteimage\\nrgbgen vertex\\nalphagen vertex\\nblendfunc add\\nnodepth\\n}\\n}\\n\");\n\tt->firstidx = cl_numstrisidx;\n\tt->firstvert = cl_numstrisvert;\n\tt->numvert = 0;\n\tt->numidx = 0;\n\n\tif (cl_numstrisidx+6 > cl_maxstrisidx)\n\t{\n\t\tcl_maxstrisidx=cl_numstrisidx+6 + 64;\n\t\tcl_strisidx = BZ_Realloc(cl_strisidx, sizeof(*cl_strisidx)*cl_maxstrisidx);\n\t}\n\tif (cl_numstrisvert+4 > cl_maxstrisvert)\n\t\tcl_stris_ExpandVerts(cl_maxstrisvert+64);\n\n\t{\n\t\tvec3_t tmp = {0,0.04,0.96};\n\t\tvec3_t right, forward;\n\t\tCrossProduct(normal, tmp, right);\n\t\tVectorNormalize(right);\n\t\tCrossProduct(normal, right, forward);\n\t\tVectorNormalize(forward);\n\n\t\tVectorScale(                        normal,    dist,      cl_strisvertv[cl_numstrisvert]);\n\t\tVectorMA(cl_strisvertv[cl_numstrisvert], radius, right,     cl_strisvertv[cl_numstrisvert]);\n\t\tVectorMA(cl_strisvertv[cl_numstrisvert], radius, forward,   cl_strisvertv[cl_numstrisvert]);\n\t\tVector4Set(cl_strisvertc[cl_numstrisvert], r, g, b, 0.2);\n\t\tcl_numstrisvert++;\n\n\t\tVectorScale(                             normal,    dist, cl_strisvertv[cl_numstrisvert]);\n\t\tVectorMA(cl_strisvertv[cl_numstrisvert], radius, right,  cl_strisvertv[cl_numstrisvert]);\n\t\tVectorMA(cl_strisvertv[cl_numstrisvert], -radius, forward, cl_strisvertv[cl_numstrisvert]);\n\t\tVector4Set(cl_strisvertc[cl_numstrisvert], r, g, b, 0.2);\n\t\tcl_numstrisvert++;\n\n\t\tVectorScale(                             normal,    dist, cl_strisvertv[cl_numstrisvert]);\n\t\tVectorMA(cl_strisvertv[cl_numstrisvert], -radius, right,    cl_strisvertv[cl_numstrisvert]);\n\t\tVectorMA(cl_strisvertv[cl_numstrisvert], -radius, forward,  cl_strisvertv[cl_numstrisvert]);\n\t\tVector4Set(cl_strisvertc[cl_numstrisvert], r, g, b, 0.2);\n\t\tcl_numstrisvert++;\n\n\t\tVectorScale(                             normal,    dist, cl_strisvertv[cl_numstrisvert]);\n\t\tVectorMA(cl_strisvertv[cl_numstrisvert], -radius, right,    cl_strisvertv[cl_numstrisvert]);\n\t\tVectorMA(cl_strisvertv[cl_numstrisvert], radius, forward,   cl_strisvertv[cl_numstrisvert]);\n\t\tVector4Set(cl_strisvertc[cl_numstrisvert], r, g, b, 0.2);\n\t\tcl_numstrisvert++;\n\t}\n\n\n\n\n\t/*build the triangles*/\n\tcl_strisidx[cl_numstrisidx++] = t->numvert + 0;\n\tcl_strisidx[cl_numstrisidx++] = t->numvert + 1;\n\tcl_strisidx[cl_numstrisidx++] = t->numvert + 2;\n\n\tcl_strisidx[cl_numstrisidx++] = t->numvert + 0;\n\tcl_strisidx[cl_numstrisidx++] = t->numvert + 2;\n\tcl_strisidx[cl_numstrisidx++] = t->numvert + 3;\n\n\n\tt->numidx = cl_numstrisidx - t->firstidx;\n\tt->numvert += 4;\n\n\tif (!enqueue)\n\t{\n//\t\tint oldents = cl_numvisedicts;\n//\t\tcl_numvisedicts = 0;\n\t\tr_refdef.scenevis = NULL;\n\t\tBE_DrawWorld(NULL);\n\t\tcl_numstris = 0;\n//\t\tcl_numvisedicts = oldents;\n\t}\n}\nvoid CLQ1_AddOrientedCube(shader_t *shader, vec3_t mins, vec3_t maxs, float *matrix, float r, float g, float b, float a)\n{\n\tint v;\n\tscenetris_t *t;\n\tvec3_t corner;\n\tint flags = BEF_NODLIGHT|BEF_NOSHADOWS;\n\n\tif (!r && !g && !b)\n\t\treturn;\n\n\t/*reuse the previous trigroup if its the same shader*/\n\tif (cl_numstris && cl_stris[cl_numstris-1].shader == shader && cl_stris[cl_numstris-1].flags == flags && cl_stris[cl_numstris-1].numvert + 8 <= MAX_INDICIES)\n\t\tt = &cl_stris[cl_numstris-1];\n\telse\n\t{\n\t\tif (cl_numstris == cl_maxstris)\n\t\t{\n\t\t\tcl_maxstris += 8;\n\t\t\tcl_stris = BZ_Realloc(cl_stris, sizeof(*cl_stris)*cl_maxstris);\n\t\t}\n\t\tt = &cl_stris[cl_numstris++];\n\t\tt->shader = shader;\n\t\tt->numidx = 0;\n\t\tt->numvert = 0;\n\t\tt->firstidx = cl_numstrisidx;\n\t\tt->firstvert = cl_numstrisvert;\n\t\tt->flags = flags;\n\t}\n\n\n\tif (cl_numstrisvert + 8 > cl_maxstrisvert)\n\t\tcl_stris_ExpandVerts(cl_numstrisvert + 8 + 1024);\n\n\tif (cl_maxstrisidx < cl_numstrisidx+6*6)\n\t{\n\t\tcl_maxstrisidx = cl_numstrisidx + 6*6 + 1024;\n\t\tcl_strisidx = BZ_Realloc(cl_strisidx, sizeof(*cl_strisidx)*cl_maxstrisidx);\n\t}\n\n\n\tfor (v = 0; v < 8; v++)\n\t{\n\t\tcorner[0] = (v & 1)?mins[0]:maxs[0];\n\t\tcorner[1] = (v & 2)?mins[1]:maxs[1];\n\t\tcorner[2] = (v & 4)?mins[2]:maxs[2];\n\t\tif (matrix)\n\t\t\tMatrix3x4_RM_Transform3(matrix, corner, cl_strisvertv[cl_numstrisvert+v]);\n\t\telse\n\t\t\tVectorCopy(corner, cl_strisvertv[cl_numstrisvert+v]);\n\n\t\tcl_strisvertt[cl_numstrisvert+v][0] = 0;\n\t\tcl_strisvertt[cl_numstrisvert+v][1] = 0;\n\n\t\tcl_strisvertc[cl_numstrisvert+v][0] = r;\n\t\tcl_strisvertc[cl_numstrisvert+v][1] = g;\n\t\tcl_strisvertc[cl_numstrisvert+v][2] = b;\n\t\tcl_strisvertc[cl_numstrisvert+v][3] = a;\n\t}\n\n\t/*top*/\n\tcl_strisidx[cl_numstrisidx++] = cl_numstrisvert+2 - t->firstvert;\n\tcl_strisidx[cl_numstrisidx++] = cl_numstrisvert+1 - t->firstvert;\n\tcl_strisidx[cl_numstrisidx++] = cl_numstrisvert+0 - t->firstvert;\n\tcl_strisidx[cl_numstrisidx++] = cl_numstrisvert+3 - t->firstvert;\n\tcl_strisidx[cl_numstrisidx++] = cl_numstrisvert+1 - t->firstvert;\n\tcl_strisidx[cl_numstrisidx++] = cl_numstrisvert+2 - t->firstvert;\n\n\t/*bottom*/\n\tcl_strisidx[cl_numstrisidx++] = cl_numstrisvert+4 - t->firstvert;\n\tcl_strisidx[cl_numstrisidx++] = cl_numstrisvert+5 - t->firstvert;\n\tcl_strisidx[cl_numstrisidx++] = cl_numstrisvert+6 - t->firstvert;\n\tcl_strisidx[cl_numstrisidx++] = cl_numstrisvert+6 - t->firstvert;\n\tcl_strisidx[cl_numstrisidx++] = cl_numstrisvert+5 - t->firstvert;\n\tcl_strisidx[cl_numstrisidx++] = cl_numstrisvert+7 - t->firstvert;\n\n\t/*'left'*/\n\tcl_strisidx[cl_numstrisidx++] = cl_numstrisvert+5 - t->firstvert;\n\tcl_strisidx[cl_numstrisidx++] = cl_numstrisvert+4 - t->firstvert;\n\tcl_strisidx[cl_numstrisidx++] = cl_numstrisvert+0 - t->firstvert;\n\tcl_strisidx[cl_numstrisidx++] = cl_numstrisvert+1 - t->firstvert;\n\tcl_strisidx[cl_numstrisidx++] = cl_numstrisvert+5 - t->firstvert;\n\tcl_strisidx[cl_numstrisidx++] = cl_numstrisvert+0 - t->firstvert;\n\n\t/*right*/\n\tcl_strisidx[cl_numstrisidx++] = cl_numstrisvert+2 - t->firstvert;\n\tcl_strisidx[cl_numstrisidx++] = cl_numstrisvert+6 - t->firstvert;\n\tcl_strisidx[cl_numstrisidx++] = cl_numstrisvert+7 - t->firstvert;\n\tcl_strisidx[cl_numstrisidx++] = cl_numstrisvert+2 - t->firstvert;\n\tcl_strisidx[cl_numstrisidx++] = cl_numstrisvert+7 - t->firstvert;\n\tcl_strisidx[cl_numstrisidx++] = cl_numstrisvert+3 - t->firstvert;\n\n\t/*urm, the other way*/\n\tcl_strisidx[cl_numstrisidx++] = cl_numstrisvert+2 - t->firstvert;\n\tcl_strisidx[cl_numstrisidx++] = cl_numstrisvert+4 - t->firstvert;\n\tcl_strisidx[cl_numstrisidx++] = cl_numstrisvert+6 - t->firstvert;\n\tcl_strisidx[cl_numstrisidx++] = cl_numstrisvert+4 - t->firstvert;\n\tcl_strisidx[cl_numstrisidx++] = cl_numstrisvert+2 - t->firstvert;\n\tcl_strisidx[cl_numstrisidx++] = cl_numstrisvert+0 - t->firstvert;\n\n\t/*and its oposite*/\n\tcl_strisidx[cl_numstrisidx++] = cl_numstrisvert+7 - t->firstvert;\n\tcl_strisidx[cl_numstrisidx++] = cl_numstrisvert+5 - t->firstvert;\n\tcl_strisidx[cl_numstrisidx++] = cl_numstrisvert+3 - t->firstvert;\n\tcl_strisidx[cl_numstrisidx++] = cl_numstrisvert+1 - t->firstvert;\n\tcl_strisidx[cl_numstrisidx++] = cl_numstrisvert+3 - t->firstvert;\n\tcl_strisidx[cl_numstrisidx++] = cl_numstrisvert+5 - t->firstvert;\n\n\tt->numvert += 8;\n\tt->numidx = cl_numstrisidx - t->firstidx;\n\tcl_numstrisvert += 8;\n}\n#include \"pr_common.h\"\nvoid CLQ1_AddVisibleBBoxes(void)\n{\n\tworld_t *w;\n\twedict_t *e;\n\tint i;\n\tshader_t *s;\n\tvec3_t min, max, size;\n\n\t#pragma message(\"Temporary Code: BBoxes calling R2D_Flush\")\n\t/*\n\t* HACK(fhomolka): For some reason, bboxes like to mess with progs-drawn Polygons.\n\t* The clean way would be to understand WHY they mess with eachother, for now this must do.\n\t* TODO(fhomolka)\n\t* Comment by Spike: \"qc's polys should have been flushed inside renderscene\"\n\t*/\n\tif(R2D_Flush) R2D_Flush();\n\n\tswitch(r_showbboxes.ival & 3)\n\t{\n\tdefault:\n\t\treturn;\n\n\t#ifndef CLIENTONLY\n\tcase 1:\n\t\tw = &sv.world;\n\t\tbreak;\n\t#endif\n\t#ifdef CSQC_DAT\n\tcase 2:\n\t\t{\n\t\t\textern world_t csqc_world;\n\t\t\tw = &csqc_world;\n\t\t}\n\t\tbreak;\n\t#endif\n\tcase 3:\n\t\t{\n\t\t\tinframe_t *frame;\n\t\t\tpacket_entities_t *pak;\n\t\t\tentity_state_t *state;\n\t\t\tmodel_t *mod;\n\t\t\ts = R_RegisterShader(\"bboxshader\", SUF_NONE,\n\t\t\t\t\"{\\n\"\n\t\t\t\t\t\"polygonoffset\\n\"\n\t\t\t\t\t\"sort additive\\n\"\n\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\"map $whiteimage\\n\"\n\t\t\t\t\t\t\"blendfunc add\\n\"\n\t\t\t\t\t\t\"rgbgen vertex\\n\"\n\t\t\t\t\t\t\"alphagen vertex\\n\"\n\t\t\t\t\t\"}\\n\"\n\t\t\t\t\"}\\n\");\n\t\t\tframe = &cl.inframes[cl.parsecount & UPDATE_MASK];\n\t\t\tpak = &frame->packet_entities;\n\n\t\t\tfor (i=0 ; i<pak->num_entities ; i++)\n\t\t\t{\n\t\t\t\tstate = &pak->entities[i];\n\n\t\t\t\tif (state->solidsize == ES_SOLID_NOT && !state->skinnum)\n\t\t\t\t\tcontinue;\n\n\t\t\t\tif (state->solidsize == ES_SOLID_BSP)\n\t\t\t\t{\t/*bsp model size*/\n\t\t\t\t\tif (state->modelindex <= 0)\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\tif (!cl.model_precache[state->modelindex])\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t/*this makes non-inline bsp objects non-solid for prediction*/\n\t\t\t\t\tif ((cls.fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS) || ((*cl.model_precache[state->modelindex]->name == '*' || cl.model_precache[state->modelindex]->numsubmodels) && cl.model_precache[state->modelindex]->hulls[1].firstclipnode))\n\t\t\t\t\t{\n\t\t\t\t\t\tmod = cl.model_precache[state->modelindex];\n\t\t\t\t\t\tVectorAdd(state->origin, mod->mins, min);\n\t\t\t\t\t\tVectorAdd(state->origin, mod->maxs, max);\n\t\t\t\t\t\tCLQ1_AddOrientedCube(s, min, max, NULL, 0.1, 0, 0, 1);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t/*don't bother with angles*/\n\t\t\t\t\tCOM_DecodeSize(state->solidsize, min, max);\n\t\t\t\t\tVectorAdd(state->origin, min, min);\n\t\t\t\t\tVectorAdd(state->origin, max, max);\n\t\t\t\t\tCLQ1_AddOrientedCube(s, min, max, NULL, 0.1, 0, 0, 1);\n\n\t\t\t\t\tCOM_DecodeSize(state->solidsize, min, max);\n\t\t\t\t\tVectorAdd(state->u.q1.predorg, min, min);\n\t\t\t\t\tVectorAdd(state->u.q1.predorg, max, max);\n\t\t\t\t\tCLQ1_AddOrientedCube(s, min, max, NULL, 0, 0, 0.1, 1);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn;\n\t}\n\n\tif (!w->progs)\n\t\treturn;\n\t\n\ts = R_RegisterShader(\"bboxshader\", SUF_NONE,\n\t\t\"{\\n\"\n\t\t\t\"polygonoffset\\n\"\n\t\t\t\"sort additive\\n\"\n\t\t\t\"{\\n\"\n\t\t\t\t\"map $whiteimage\\n\"\n\t\t\t\t\"blendfunc add\\n\"\n\t\t\t\t\"rgbgen vertex\\n\"\n\t\t\t\t\"alphagen vertex\\n\"\n\t\t\t\"}\\n\"\n\t\t\"}\\n\");\n\tfor (i = 1; i < w->num_edicts; i++)\n\t{\n\t\te = WEDICT_NUM_PB(w->progs, i);\n\t\tif (ED_ISFREE(e))\n\t\t\tcontinue;\n\n\t\tif (r_showbboxes.ival & 4)\n\t\t{\n\t\t\t//shows the hulls instead\n\n\t\t\t/*mins is easy*/\n\t\t\tVectorAdd(e->v->origin, e->v->mins, min);\n\n\t\t\t/*maxs is weeeeird*/\n\t\t\tVectorSubtract (e->v->maxs, e->v->mins, size);\n\t\t\tif (size[0] < 3)\n\t\t\t\tVectorCopy(min, max);\n\t\t\telse if (size[0] <= 32)\n\t\t\t{\n\t\t\t\tmax[0] = min[0] + 32;\n\t\t\t\tmax[1] = min[1] + 32;\n\t\t\t\tmax[2] = min[2] + 56;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tmax[0] = min[0] + 64;\n\t\t\t\tmax[1] = min[1] + 64;\n\t\t\t\tmax[2] = min[2] + 88;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (e->v->solid == SOLID_BSP)\n\t\t\t{\n\t\t\t\tVectorCopy(e->v->absmin, min);\n\t\t\t\tVectorCopy(e->v->absmax, max);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tVectorAdd(e->v->origin, e->v->mins, min);\n\t\t\t\tVectorAdd(e->v->origin, e->v->maxs, max);\n\t\t\t}\n\t\t}\n\t\tif (e->xv->geomtype == GEOMTYPE_CAPSULE)\n\t\t{\n\t\t\tfloat rad = ((e->v->maxs[0]-e->v->mins[0]) + (e->v->maxs[1]-e->v->mins[1]))/4.0;\n\t\t\tfloat height = (e->v->maxs[2]-e->v->mins[2])/2;\n\t\t\tfloat matrix[12] = {1,0,0,0,0,1,0,0,0,0,1,0};\n\t\t\tmatrix[3] = e->v->origin[0];\n\t\t\tmatrix[7] = e->v->origin[1];\n\t\t\tmatrix[11] = e->v->origin[2] + (e->v->maxs[2]-height);\n\t\t\tCLQ1_AddOrientedCylinder(s, rad*2, height*2, true, matrix, (e->v->solid || e->v->movetype)?0.1:0, (e->v->movetype == MOVETYPE_STEP || e->v->movetype == MOVETYPE_TOSS || e->v->movetype == MOVETYPE_BOUNCE)?0.1:0, ((int)e->v->flags & (FL_ONGROUND | ((e->v->movetype == MOVETYPE_STEP)?FL_FLY:0)))?0.1:0, 1);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (!e->v->solid && !e->v->movetype)\n\t\t\t{\n\t\t\t\tvec3_t ep = {1,1,1};\n\t\t\t\tVectorAdd(max, ep, max);\n\t\t\t\tVectorSubtract(min, ep, min);\n\t\t\t\tCLQ1_AddOrientedCube(s, min, max, NULL, 0, 0.1, 0, 1);\n\t\t\t}\n\t\t\telse\n\t\t\t\tCLQ1_AddOrientedCube(s, min, max, NULL, (e->v->solid || e->v->movetype)?0.1:0, (e->v->movetype == MOVETYPE_STEP || e->v->movetype == MOVETYPE_TOSS || e->v->movetype == MOVETYPE_BOUNCE)?0.1:0, ((int)e->v->flags & (FL_ONGROUND | ((e->v->movetype == MOVETYPE_STEP)?FL_FLY:0)))?0.1:0, 1);\n\t\t}\n\t}\n}\n\ntypedef struct\n{\n\tscenetris_t *t;\n\tvec4_t rgbavalue;\n\n\tvec3_t axis[3];\n\tfloat offset[3];\n\tfloat scale[3];\n} cl_adddecal_ctx_t;\nstatic void CL_AddDecal_Callback(void *vctx, vec3_t *fte_restrict points, size_t numtris, shader_t *shader)\n{\n\tcl_adddecal_ctx_t *ctx = vctx;\n\tscenetris_t *t = ctx->t;\n\tsize_t numpoints = numtris*3;\n\tsize_t v;\n\n\t\n\tif (cl_numstrisvert + numpoints > cl_maxstrisvert)\n\t\tcl_stris_ExpandVerts(cl_numstrisvert + numpoints);\n\tif (cl_maxstrisidx < cl_numstrisidx+numpoints)\n\t{\n\t\tcl_maxstrisidx = cl_numstrisidx+numpoints + 64;\n\t\tcl_strisidx = BZ_Realloc(cl_strisidx, sizeof(*cl_strisidx)*cl_maxstrisidx);\n\t}\n\n\n\tfor (v = 0; v < numpoints; v++)\n\t{\n\t\tVectorCopy(points[v], cl_strisvertv[cl_numstrisvert+v]);\n\t\tcl_strisvertt[cl_numstrisvert+v][0] = 1+(DotProduct(points[v], ctx->axis[1]) - ctx->offset[1]) * ctx->scale[1];\n\t\tcl_strisvertt[cl_numstrisvert+v][1] = -(DotProduct(points[v], ctx->axis[2]) - ctx->offset[2]) * ctx->scale[2];\n\t\tcl_strisvertc[cl_numstrisvert+v][0] = ctx->rgbavalue[0];\n\t\tcl_strisvertc[cl_numstrisvert+v][1] = ctx->rgbavalue[1];\n\t\tcl_strisvertc[cl_numstrisvert+v][2] = ctx->rgbavalue[2];\n\t\tcl_strisvertc[cl_numstrisvert+v][3] = ctx->rgbavalue[3] * (1-fabs(DotProduct(points[v], ctx->axis[0]) - ctx->offset[0]) * ctx->scale[0]);\n\t}\n\tfor (v = 0; v < numpoints; v++)\n\t{\n\t\tcl_strisidx[cl_numstrisidx++] = cl_numstrisvert+v - t->firstvert;\n\t}\n\n\tt->numvert += numpoints;\n\tt->numidx += numpoints;\n\tcl_numstrisvert += numpoints;\n}\n\nvoid CL_AddDecal(shader_t *shader, vec3_t origin, vec3_t up, vec3_t side, vec3_t rgbvalue, float alphavalue)\n{\n\tscenetris_t *t;\n\tfloat l, s, radius, vradius;\n\tcl_adddecal_ctx_t ctx;\n\n\tVectorNegate(up, ctx.axis[0]);\n\tVectorCopy(side, ctx.axis[2]);\n\n\ts = DotProduct(ctx.axis[2], ctx.axis[2]);\n\tl = DotProduct(ctx.axis[0], ctx.axis[0]);\n\tvradius = 1/sqrt(l);\n\tradius = 1/sqrt(s);\n\n\tVectorScale(ctx.axis[0], vradius, ctx.axis[0]);\n\tVectorScale(ctx.axis[2], radius, ctx.axis[2]);\n\n\tCrossProduct(ctx.axis[0], ctx.axis[2], ctx.axis[1]);\n\n\tctx.offset[2] = DotProduct(origin, ctx.axis[2]) + 0.5*radius;\n\tctx.offset[1] = DotProduct(origin, ctx.axis[1]) + 0.5*radius;\n\tctx.offset[0] = DotProduct(origin, ctx.axis[0]);\n\n\tctx.scale[2] = 1/radius;\n\tctx.scale[1] = 1/radius;\n\tctx.scale[0] = 2/vradius;\n\n\tif (R2D_Flush)\n\t\tR2D_Flush();\n\n\t/*reuse the previous trigroup if its the same shader*/\n\tif (cl_numstris && cl_stris[cl_numstris-1].shader == shader && cl_stris[cl_numstris-1].flags == (BEF_NODLIGHT|BEF_NOSHADOWS))\n\t\tt = &cl_stris[cl_numstris-1];\n\telse\n\t{\n\t\tif (cl_numstris == cl_maxstris)\n\t\t{\n\t\t\tcl_maxstris += 8;\n\t\t\tcl_stris = BZ_Realloc(cl_stris, sizeof(*cl_stris)*cl_maxstris);\n\t\t}\n\t\tt = &cl_stris[cl_numstris++];\n\t\tt->shader = shader;\n\t\tt->numidx = 0;\n\t\tt->numvert = 0;\n\t\tt->flags = BEF_NODLIGHT|BEF_NOSHADOWS;\n\t\tt->firstidx = cl_numstrisidx;\n\t\tt->firstvert = cl_numstrisvert;\n\t}\n\n\tctx.t = t;\n\tVectorCopy(rgbvalue, ctx.rgbavalue);\n\tctx.rgbavalue[3] = alphavalue;\n\tMod_ClipDecal(cl.worldmodel, origin, ctx.axis[0], ctx.axis[1], ctx.axis[2], max(radius, vradius), 0,0, CL_AddDecal_Callback, &ctx);\n\n\tif (!t->numidx)\n\t\tcl_numstris--;\n}\n\nvoid R_AddItemTimer(vec3_t shadoworg, float yaw, float radius, float percent, vec3_t rgb)\n{\n\tvec3_t eang;\n\tshader_t *s;\n\tscenetris_t *t;\n\tcl_adddecal_ctx_t ctx;\n\n//\tif (!r_shadows.value)\n//\t\treturn;\n\n\ts = R_RegisterShader(\"timershader\", SUF_NONE,\n\t\t\"{\\n\"\n\t\t\t\"polygonoffset\\n\"\n\t\t\t\"fte_program itemtimer\\n\"\n\t\t\t\"{\\n\"\n\t\t\t\t\"map $diffuse\\n\"\n\t\t\t\t\"blendfunc src_alpha one\\n\"\n\t\t\t\t\"rgbgen vertex\\n\"\n\t\t\t\t\"alphagen vertex\\n\"\n\t\t\t\"}\\n\"\n\t\t\"}\\n\");\n\tif (!s->prog)\n\t\treturn;\n\tTEXASSIGN(s->defaulttextures->base, balltexture);\n\n\n\teang[0] = 0;\n\teang[1] = yaw;\n\teang[2] = 0;\n\tAngleVectors(eang, ctx.axis[1], ctx.axis[2], ctx.axis[0]);\n\tVectorNegate(ctx.axis[0], ctx.axis[0]);\n\n\tctx.offset[2] = DotProduct(shadoworg, ctx.axis[2]) + 0.5*radius;\n\tctx.offset[1] = DotProduct(shadoworg, ctx.axis[1]) + 0.5*radius;\n\tctx.offset[0] = DotProduct(shadoworg, ctx.axis[0]);\n\tctx.scale[1] = 1/radius;\n\tctx.scale[2] = 1/radius;\n\tctx.scale[0] = 0;//.5/radius;\n\n\tif (R2D_Flush)\n\t\tR2D_Flush();\n\n\t/*reuse the previous trigroup if its the same shader*/\n\tif (cl_numstris && cl_stris[cl_numstris-1].shader == s && cl_stris[cl_numstris-1].flags == (BEF_NODLIGHT|BEF_NOSHADOWS))\n\t\tt = &cl_stris[cl_numstris-1];\n\telse\n\t{\n\t\tif (cl_numstris == cl_maxstris)\n\t\t{\n\t\t\tcl_maxstris += 8;\n\t\t\tcl_stris = BZ_Realloc(cl_stris, sizeof(*cl_stris)*cl_maxstris);\n\t\t}\n\t\tt = &cl_stris[cl_numstris++];\n\t\tt->shader = s;\n\t\tt->flags = BEF_NODLIGHT|BEF_NOSHADOWS;\n\t\tt->numidx = 0;\n\t\tt->numvert = 0;\n\t\tt->firstidx = cl_numstrisidx;\n\t\tt->firstvert = cl_numstrisvert;\n\t}\n\n\tctx.t = t;\n\tVector4Set(ctx.rgbavalue, rgb[0], rgb[1], rgb[2], percent);\n\tMod_ClipDecal(cl.worldmodel, shadoworg, ctx.axis[0], ctx.axis[1], ctx.axis[2], radius, 0,0, CL_AddDecal_Callback, &ctx);\n\tif (!t->numidx)\n\t\tcl_numstris--;\n}\nvoid CLQ1_AddShadow(entity_t *ent)\n{\n\tfloat radius;\n\tvec3_t shadoworg;\n\tvec3_t eang;\n\tfloat tx, ty;\n\tshader_t *s;\n\tscenetris_t *t;\n\tcl_adddecal_ctx_t ctx;\n\n\tif (!r_blobshadows || !ent->model || (ent->model->type != mod_alias && ent->model->type != mod_halflife) || (ent->flags & RF_NOSHADOW))\n\t\treturn;\n\n\ts = R_RegisterShader(\"shadowshader\", SUF_NONE,\n\t\t\"{\\n\"\n\t\t\t\"polygonoffset\\n\"\n\t\t\t\"{\\n\"\n\t\t\t\t\"map $diffuse\\n\"\n\t\t\t\t\"blendfunc blend\\n\"\n\t\t\t\t\"rgbgen vertex\\n\"\n\t\t\t\t\"alphagen vertex\\n\"\n\t\t\t\"}\\n\"\n\t\t\"}\\n\");\n\tTEXASSIGN(s->defaulttextures->base, balltexture);\n\n\ttx = ent->model->maxs[0] - ent->model->mins[0];\n\tty = ent->model->maxs[1] - ent->model->mins[1];\n\n\tif (tx > ty)\n\t\tradius = tx;\n\telse\n\t\tradius = ty;\n\tradius/=2;\n\n\tshadoworg[0] = ent->origin[0];\n\tshadoworg[1] = ent->origin[1];\n\tshadoworg[2] = ent->origin[2] + ent->model->mins[2];\n\n\teang[0] = 0;\n\teang[1] = ent->angles[1];\n\teang[2] = 0;\n\tAngleVectors(eang, ctx.axis[1], ctx.axis[2], ctx.axis[0]);\n\tVectorNegate(ctx.axis[0], ctx.axis[0]);\n\n\tctx.offset[2] = DotProduct(shadoworg, ctx.axis[2]) + 0.5*radius;\n\tctx.offset[1] = DotProduct(shadoworg, ctx.axis[1]) + 0.5*radius;\n\tctx.offset[0] = DotProduct(shadoworg, ctx.axis[0]);\n\tctx.scale[1] = 1/radius;\n\tctx.scale[2] = 1/radius;\n\tctx.scale[0] = 0.5/radius;\n\n\tif (R2D_Flush)\n\t\tR2D_Flush();\n\n\t/*reuse the previous trigroup if its the same shader*/\n\tif (cl_numstris && cl_stris[cl_numstris-1].shader == s && cl_stris[cl_numstris-1].flags == (BEF_NODLIGHT|BEF_NOSHADOWS))\n\t\tt = &cl_stris[cl_numstris-1];\n\telse\n\t{\n\t\tif (cl_numstris == cl_maxstris)\n\t\t{\n\t\t\tcl_maxstris += 8;\n\t\t\tcl_stris = BZ_Realloc(cl_stris, sizeof(*cl_stris)*cl_maxstris);\n\t\t}\n\t\tt = &cl_stris[cl_numstris++];\n\t\tt->shader = s;\n\t\tt->flags = BEF_NODLIGHT|BEF_NOSHADOWS;\n\t\tt->numidx = 0;\n\t\tt->numvert = 0;\n\t\tt->firstidx = cl_numstrisidx;\n\t\tt->firstvert = cl_numstrisvert;\n\t}\n\n\tctx.t = t;\n\tVector4Set(ctx.rgbavalue, 0, 0, 0, r_blobshadows*((ent->flags & RF_TRANSLUCENT)?ent->shaderRGBAf[3]:1));\n\tMod_ClipDecal(cl.worldmodel, shadoworg, ctx.axis[0], ctx.axis[1], ctx.axis[2], radius, 0,0, CL_AddDecal_Callback, &ctx);\n\tif (!t->numidx)\n\t\tcl_numstris--;\n}\nvoid CLQ1_AddPowerupShell(entity_t *ent, qboolean viewweap, unsigned int effects)\n{\n\tentity_t *shell;\n\tif (!(effects & (EF_BLUE | EF_RED | EF_GREEN)) || !v_powerupshell.value || !ent)\n\t\treturn;\n\n\tif (cl_numvisedicts == cl_maxvisedicts)\n\t\treturn;\t\t// object list is full\n\tshell = &cl_visedicts[cl_numvisedicts++];\n\n\t*shell = *ent;\n\n\t/*view weapons are much closer to the screen, the scales don't work too well, so use a different shader with a smaller expansion*/\n\tif (viewweap)\n\t{\n\t\tshell->forcedshader = R_RegisterShader(\"powerups/shellweapon\", SUF_NONE,\n\t\t\t\t\"{\\n\"\n\t\t\t\t\t\"program defaultpowerupshell\\n\"\n\t\t\t\t\t\"sort additive\\n\"\n\t\t\t\t\t\"deformVertexes wave 100 sin 0.5 0 0 0\\n\"\n\t\t\t\t\t\"noshadows\\n\"\n\t\t\t\t\t\"surfaceparm nodlight\\n\"\n\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\"map $whiteimage\\n\"\n\t\t\t\t\t\t\"rgbgen entity\\n\"\n\t\t\t\t\t\t\"alphagen entity\\n\"\n\t\t\t\t\t\t\"blendfunc src_alpha one\\n\"\n\t\t\t\t\t\"}\\n\"\n\t\t\t\t\"}\\n\"\n\t\t\t);\n\t}\n\telse\n\t{\n\t\tshell->forcedshader = R_RegisterShader(\"powerups/shell\", SUF_NONE,\n\t\t\t\t\"{\\n\"\n\t\t\t\t\t\"program defaultpowerupshell\\n\"\n\t\t\t\t\t\"sort additive\\n\"\n\t\t\t\t\t\"deformVertexes wave 100 sin 3 0 0 0\\n\"\n\t\t\t\t\t\"noshadows\\n\"\n\t\t\t\t\t\"surfaceparm nodlight\\n\"\n\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\"map $whiteimage\\n\"\n\t\t\t\t\t\t\"rgbgen entity\\n\"\n\t\t\t\t\t\t\"alphagen entity\\n\"\n\t\t\t\t\t\t\"blendfunc src_alpha one\\n\"\n\t\t\t\t\t\"}\\n\"\n\t\t\t\t\"}\\n\"\n\t\t\t);\n\t}\n\tshell->shaderRGBAf[0] *= (effects & EF_RED)?1:0;\n\tshell->shaderRGBAf[1] *= (effects & EF_GREEN)?1:0;\n\tshell->shaderRGBAf[2] *= (effects & EF_BLUE)?1:0;\n\tshell->shaderRGBAf[3] *= v_powerupshell.value;\n\t/*let the shader do all the work*/\n\tshell->flags &= ~RF_TRANSLUCENT|RF_ADDITIVE;\n}\n\nstatic void CL_LerpNetFrameState(framestate_t *fs, lerpents_t *le)\n{\n\tint fsanim;\n\tfor (fsanim = 0; fsanim < FS_COUNT; fsanim++)\n\t{\n\t\tfs->g[fsanim].frame[0] = le->newframe[fsanim];\n\t\tfs->g[fsanim].frame[1] = le->oldframe[fsanim];\n\n\t\tfs->g[fsanim].frametime[0] = cl.servertime - le->newframestarttime[fsanim];\n\t\tfs->g[fsanim].frametime[1] = cl.servertime - le->oldframestarttime[fsanim];\n\n\t\tfs->g[fsanim].lerpweight[0] = (fs->g[fsanim].frametime[0]) / le->framelerpdeltatime[fsanim];\n\t\tfs->g[fsanim].lerpweight[0] = bound(0, fs->g[FS_REG].lerpweight[0], 1);\n\t\tfs->g[fsanim].lerpweight[1] = 1 - fs->g[fsanim].lerpweight[0];\n\t}\n\tfs->g[0].endbone = le->basebone;\n}\n\nstatic void CL_UpdateNetFrameLerpState(qboolean force, int curframe, int curbaseframe, int curbasebone, lerpents_t *le, float lerpend)\n{\n\tint fst, frame;\n\tif (curbasebone != le->basebone)\n\t{\n\t\t//FIXME: we should be able to treat 0 and 255 specially by ignoring the change and locking the respective value to the other's value.\n\t\tif (!curbasebone)\n\t\t\tcurbaseframe = curframe;\n\t\telse if (curbasebone == 255)\n\t\t\tcurframe = curbaseframe;\n\t\tle->basebone = curbasebone;\n\t}\n\tfor (fst = 0; fst < FS_COUNT; fst++)\n\t{\n\t\tframe = (fst==FST_BASE)?curbaseframe:curframe;\n\t\tif (force || frame != le->newframe[fst])\n\t\t{\n\t\t\tif (lerpend)\n\t\t\t\tle->framelerpdeltatime[fst] = bound(0, lerpend - cl.servertime, cl_lerp_maxinterval.value);\t//clamp to 10 tics per second\n\t\t\telse\n\t\t\t\tle->framelerpdeltatime[fst] = bound(0, cl.servertime - le->newframestarttime[fst], cl_lerp_maxinterval.value);\t//clamp to 10 tics per second\n\n\t\t\tif (!force)\n\t\t\t{\n\t\t\t\tle->oldframe[fst] = le->newframe[fst];\n\t\t\t\tle->oldframestarttime[fst] = le->newframestarttime[fst];\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tle->oldframe[fst] = frame;\n\t\t\t\tle->oldframestarttime[fst] = cl.servertime;\n\t\t\t}\n\t\t\tle->newframe[fst] = frame;\n\t\t\tle->newframestarttime[fst] = cl.servertime;\n\n//\t\t\tif (force)\n//\t\t\t{\n//\t\t\t\t//if its new, we need to tweak the age of the animation. looping anims won't appear any different, while non-looping ones will clamp to the last pose of the animation when its new.\n//\t\t\t\tle->oldframestarttime[fst] -= Mod_GetFrameDuration(le->model, 0, le->oldframe[fst]);\n//\t\t\t\tle->newframestarttime[fst] -= Mod_GetFrameDuration(le->model, 0, le->newframe[fst]);\n//\t\t\t}\n\t\t}\n\t}\n}\n\nvoid CL_ClearLerpEntsParticleState(void)\n{\n\tint i;\n\tfor (i = 0; i < cl.maxlerpents; i++)\n\t{\n\t\tpe->DelinkTrailstate(&(cl.lerpents[i].trailstate));\n\t\tpe->DelinkTrailstate(&(cl.lerpents[i].emitstate));\n\t}\n}\n\nvoid CL_LinkStaticEntities(void *pvs, int *areas)\n{\n\tint i;\n\tentity_t *ent;\n\tmodel_t\t\t*clmodel;\n\tstatic_entity_t *stat;\n\textern cvar_t r_drawflame, gl_part_flame;\n\tvec3_t mins, maxs;\n\n\tif (r_drawflame.ival < 0 || r_drawentities.ival == 0)\n\t\treturn;\n\n\tif (!cl.worldmodel)\n\t\treturn;\n\n\tfor (i = 0; i < cl.num_statics; i++)\n\t{\n\t\tif (cl_numvisedicts == cl_maxvisedicts)\n\t\t{\n\t\t\tcl_expandvisents=true;\n\t\t\tbreak;\n\t\t}\n\t\tstat = &cl_static_entities[i];\n\n\t\tclmodel = stat->ent.model;\n\n\t\tif (!clmodel)\n\t\t{\n\t\t\tif (stat->mdlidx < 0)\n\t\t\t{\n\t\t\t\tif (stat->mdlidx > -MAX_CSMODELS)\n\t\t\t\t\tclmodel = cl.model_csqcprecache[-stat->mdlidx];\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (stat->mdlidx < MAX_PRECACHE_MODELS)\n\t\t\t\t\tclmodel = cl.model_precache[stat->mdlidx];\n\t\t\t}\n\t\t\tif (!clmodel || clmodel->loadstate == MLS_LOADING)\n\t\t\t\tcontinue;\n\t\t\tif (!cl.worldmodel || cl.worldmodel->loadstate != MLS_LOADED)\n\t\t\t\tcontinue;\n\n\t\t\tstat->ent.model = clmodel;\n\n\t\t\t//figure out the correct axis for the model\n\t\t\tif (clmodel && clmodel->type == mod_alias && (cls.protocol == CP_QUAKEWORLD || cls.protocol == CP_NETQUAKE))\n\t\t\t{\t//q2 is fixed, but q1 pitches the wrong way, and hexen2 rolls the wrong way too.\n\t\t\t\tAngleVectorsMesh(stat->state.angles, stat->ent.axis[0], stat->ent.axis[1], stat->ent.axis[2]);\n\t\t\t}\n\t\t\telse\n\t\t\t\tAngleVectors(stat->state.angles, stat->ent.axis[0], stat->ent.axis[1], stat->ent.axis[2]);\n\t\t\tVectorInverse(stat->ent.axis[1]);\n\n\n\t\t\tif (clmodel)\n\t\t\t{\n\t\t\t\t//FIXME: wait for model to load so we know the correct size?\n\t\t\t\t/*FIXME: compensate for angle*/\n\t\t\t\tVectorAdd(stat->state.origin, clmodel->mins, mins);\n\t\t\t\tVectorAdd(stat->state.origin, clmodel->maxs, maxs);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tVectorCopy(stat->state.origin, mins);\n\t\t\t\tVectorCopy(stat->state.origin, maxs);\n\t\t\t}\n\t\t\tcl.worldmodel->funcs.FindTouchedLeafs(cl.worldmodel, &stat->ent.pvscache, mins, maxs);\n\t\t}\n\t\telse if (clmodel->loadstate != MLS_LOADED)\n\t\t{\n\t\t\tif (clmodel->loadstate == MLS_NOTLOADED)\t//flushed?\n\t\t\t\tMod_LoadModel(clmodel, MLV_WARN);\t\t//load it, but don't otherwise care for now.\n\t\t\tcontinue;\n\t\t}\n\n\t\t/*pvs test*/\n\t\tif (pvs && !cl.worldmodel->funcs.EdictInFatPVS(cl.worldmodel, &stat->ent.pvscache, pvs, areas))\n\t\t\tcontinue;\n\n\n\t\t// emit particles for statics (we don't need to cheat check statics)\n\t\tif (stat->state.u.q1.emiteffectnum)\n\t\t\tP_EmitEffect (stat->ent.origin, stat->ent.axis, MDLF_EMITFORWARDS, CL_TranslateParticleFromServer(stat->state.u.q1.emiteffectnum), &(stat->emit));\n\t\telse if (clmodel)\n\t\t{\n\t\t\tif (clmodel->particleeffect >= 0 && gl_part_flame.ival)\n\t\t\t\tP_EmitEffect(stat->ent.origin, stat->ent.axis, clmodel->engineflags, clmodel->particleeffect, &stat->emit);\n\t\t\tif ((!r_drawflame.ival) && (clmodel->engineflags & MDLF_FLAME))\n\t\t\t\tcontinue;\n\t\t}\n\n\t\t//prepare to draw it\n\t\tif (!clmodel || clmodel->loadstate != MLS_LOADED)\n\t\t\tcontinue;\n\n\t\tent = &cl_visedicts[cl_numvisedicts++];\n\t\t*ent = stat->ent;\n\t\tent->framestate.g[FS_REG].frametime[0] = cl.time;\n\t\tent->framestate.g[FS_REG].frametime[1] = cl.time;\n\n//  FIXME: no effects on static ents\n//\t\tCLQ1_AddPowerupShell(ent, false, stat->effects);\n\t}\n}\n\n//returns cos(angle)\nstatic float CompareAngles (const vec3_t angles1, const vec3_t angles2)\n{\n\tfloat\t\tangle;\n\tvec3_t dir1, dir2;\n\n\tangle = angles1[YAW] * (M_PI*2 / 360);\n\tdir1[1] = sin(angle);\n\tdir1[0] = cos(angle);\n\tif (angles1[PITCH])\n\t{\n\t\tangle = angles1[PITCH] * (M_PI*2 / 360);\n\t\tdir1[2] = -sin(angle);\n\t\tangle = cos(angle);\n\t\tdir1[0] *= angle;\n\t\tdir1[1] *= angle;\n\t}\n\telse\n\t\tdir1[2] = 0;\n\n\tangle = angles2[YAW] * (M_PI*2 / 360);\n\tdir2[1] = sin(angle);\n\tdir2[0] = cos(angle);\n\tif (angles2[PITCH])\n\t{\n\t\tangle = angles2[PITCH] * (M_PI*2 / 360);\n\t\tdir2[2] = -sin(angle);\n\t\tangle = cos(angle);\n\t\tdir2[0] *= angle;\n\t\tdir2[1] *= angle;\n\t}\n\telse\n\t\tdir2[2] = 0;\n\n\treturn DotProduct(dir1,dir2);\n}\n\n/*\n===============\nCL_LinkPacketEntities\n\n===============\n*/\nvoid R_FlameTrail(vec3_t start, vec3_t end, float seperation);\n\n/*\nInterpolates the two packets by the given time, writes its results into the lerpentities array.\n*/\nstatic void CL_TransitionPacketEntities(int newsequence, packet_entities_t *newpack, packet_entities_t *oldpack, float frac, float servertime)\n{\n\tlerpents_t\t\t*le;\n\tentity_state_t\t\t*snew, *sold;\n\tint\t\t\t\t\ti;\n\tint\t\t\t\t\toldpnum, newpnum;\n\tfloat\t\t\t\t*snew__origin;\n\tfloat\t\t\t\t*sold__origin;\n\tfloat\t\t\t\tcos_theta;\n\tint oldsequence;\n\textern cvar_t r_nolerp;\n\n\tqboolean\t\t\tisnew;\n\n\tvec3_t move;\n\n\tfloat a1, a2;\n\tfloat maxdist = cl_lerp_maxdistance.value*cl_lerp_maxdistance.value;\n\n\t/*\n\t\tseeing as how dropped packets cannot be filled in due to the reliable networking stuff,\n\t\tWe can simply detect changes and lerp towards them\n\t*/\n\n\t//we have two index-sorted lists of entities\n\t//we figure out which ones are new,\n\t//we don't care about old, as our caller will use the lerpents array we fill, and the entity numbers from the 'new' packet.\n\n\toldsequence = cl.lerpentssequence;\n\tif (!oldsequence)\n\t\toldsequence = -1;\t//something invalid, so everything is new\n\tcl.lerpentssequence = newsequence;\n\n\tcl.packfrac = frac;\n\tcl.currentpacktime = servertime;\n\tcl.currentpackentities = newpack;\n\tcl.previouspackentities = oldpack;\n\n\toldpnum=0;\n\tfor (newpnum=0 ; newpnum<newpack->num_entities ; newpnum++)\n\t{\n\t\tsnew = &newpack->entities[newpnum];\n\n\t\tsold = NULL;\n\t\tfor ( ; oldpnum<oldpack->num_entities ; )\n\t\t{\n\t\t\tsold = &oldpack->entities[oldpnum];\n\t\t\tif (sold->number >= snew->number)\n\t\t\t{\n\t\t\t\tif (sold->number > snew->number)\n\t\t\t\t\tsold = NULL;\t//woo, it's a new entity.\n\t\t\t\telse\n\t\t\t\t\toldpnum++;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\toldpnum++;\n\n#ifdef RAGDOLL\n\t\t\t//note: not entirely reliable\n\t\t\tle = &cl.lerpents[sold->number];\n\t\t\tif (sold->number < cl.maxlerpents && le->skeletalobject)\n\t\t\t\trag_removedeltaent(le);\n#endif\n\t\t}\n\n\t\tif (snew->number >= cl.maxlerpents)\n\t\t{\n\t\t\tint newmaxle = snew->number+16;\n\t\t\tcl.lerpents = BZ_Realloc(cl.lerpents, newmaxle*sizeof(lerpents_t));\n\t\t\tmemset(cl.lerpents + cl.maxlerpents, 0, sizeof(lerpents_t)*(newmaxle - cl.maxlerpents));\n\t\t\tcl.maxlerpents = newmaxle;\n\t\t}\n\n\t\tif (!sold)\n\t\t{\n\t\t\tisnew = true;\n\t\t\tsold = snew;\t//don't crash if anything tries poking sold\n\t\t}\n\t\telse\n\t\t\tisnew = false;\n\n\t\tle = &cl.lerpents[snew->number];\n\t\tif (le->sequence != oldsequence)\n\t\t\tisnew = true;\n\t\tle->sequence = newsequence;\n\t\tle->entstate = snew;\n\n\t\tif (snew->u.q1.pmovetype)\n\t\t{\n\t\t\tif (!cl.do_lerp_players)\n\t\t\t{\n\t\t\t\tentity_state_t *from;\n\t\t\t\tfloat age;\n\t\t\t\tpacket_entities_t *latest;\n\t\t\t\tif (isnew)\n\t\t\t\t{\n\t\t\t\t\t/*keep trails correct*/\n\t\t\t\t\tle->isnew = true;\n\t\t\t\t\tVectorCopy(le->origin, le->lastorigin);\n\t\t\t\t}\n\t\t\t\tCL_UpdateNetFrameLerpState(sold == snew, snew->frame, snew->baseframe, snew->basebone, le, snew->lerpend);\n\n\n\t\t\t\tfrom = sold;\t//eww\n\t\t\t\tage = servertime - oldpack->servertime;\n\t\t\t\tlatest = &cl.inframes[cl.validsequence & UPDATE_MASK].packet_entities;\n\t\t\t\tfor (i = 0; i < latest->num_entities; i++)\n\t\t\t\t{\n\t\t\t\t\tif (latest->entities[i].number == snew->number)\n\t\t\t\t\t{\n\t\t\t\t\t\tfrom = &latest->entities[i];\n\t\t\t\t\t\t//use realtime instead.\n\t\t\t\t\t\t//also, use the sent timings instead of received as those are assumed to be more reliable\n\t\t\t\t\t\tage = (realtime - cl.outframes[cl.ackedmovesequence & UPDATE_MASK].senttime) - cls.latency*cl_predict_players_latency.value + cl_predict_players_nudge.value;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (age > 1)\n\t\t\t\t\tage = 1;\n\n\t\t\t\tif (cl_predict_players.ival && pmove.numphysent)\n\t\t\t\t{\n\t\t\t\t\tCL_PredictEntityMovement(from, age);\n\t\t\t\t\tVectorCopy(from->u.q1.predorg, le->origin);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tVectorCopy(from->origin, le->origin);\n\t\t\t\tVectorCopy(from->angles, le->angles);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t//FIXME: find a packet where this entity changed.\n\n\t\t\tsnew__origin = snew->u.q1.predorg;\n\t\t\tsold__origin = sold->u.q1.predorg;\n\t\t\tcos_theta = 1;\t//don't cut off lerping when the player spins too fast.\n\t\t}\n\t\telse\n\t\t{\n\t\t\tsnew__origin = snew->origin;\n\t\t\tsold__origin = sold->origin;\n\t\t\tcos_theta = CompareAngles(sold->angles, snew->angles);\n\t\t}\n\n\t\tVectorSubtract(snew__origin, sold__origin, move);\n\t\tif (DotProduct(move, move) > maxdist || cos_theta < 0.707 || snew->modelindex != sold->modelindex || ((sold->effects ^ snew->effects) & EF_TELEPORT_BIT))\n\t\t{\n\t\t\tisnew = true;\t//disable lerping (and indirectly trails)\n//\t\t\tVectorClear(move);\n\t\t}\n\n\t\tVectorCopy(le->origin, le->lastorigin);\n\t\tif (isnew)\n\t\t{\n#ifdef RAGDOLL\t//make sure nothing gets stale\n\t\t\tif (le->skeletalobject)\n\t\t\t\trag_removedeltaent(le);\n#endif\n\n\t\t\tle->newsequence = snew->sequence;\n\n\t\t\t//new this frame (or we noticed something changed significantly)\n\t\t\tVectorCopy(snew__origin, le->origin);\n\t\t\tVectorCopy(snew->angles, le->angles);\n\n\t\t\tVectorCopy(snew__origin, le->oldorigin);\n\t\t\tVectorCopy(snew->angles, le->oldangle);\n\t\t\tVectorCopy(snew__origin, le->neworigin);\n\t\t\tVectorCopy(snew->angles, le->newangle);\n\n\t\t\tif (snew->lerpend)\n\t\t\t\tle->orglerpdeltatime = bound(0.001, snew->lerpend - newpack->servertime, cl_lerp_maxinterval.value);\n\t\t\telse\n\t\t\t\tle->orglerpdeltatime = newpack->servertime - oldpack->servertime;\n\t\t\tle->orglerpstarttime = oldpack->servertime;\n\n\t\t\tle->isnew = true;\n\t\t\tVectorCopy(le->origin, le->lastorigin);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif ((sold->effects ^ snew->effects) & EF_RESTARTANIM_BIT)\n\t\t\t\tisnew = true;\n\n\t\t\tif (snew->dpflags & RENDER_STEP)\n\t\t\t{\n\t\t\t\tfloat lfrac;\n\t\t\t\t//ignore the old packet entirely, except for maybe its time.\n\t\t\t\tif (!VectorEquals(le->neworigin, snew__origin) || !VectorEquals(le->newangle, snew->angles))\n\t\t\t\t{\n\t\t\t\t\tle->newsequence = snew->sequence;\n\t\t\t\t\tle->orglerpdeltatime = bound(0, oldpack->servertime - le->orglerpstarttime, cl_lerp_maxinterval.value);\t//clamp to 10 tics per second\n\t\t\t\t\tle->orglerpstarttime = oldpack->servertime;\n\n\t\t\t\t\tVectorCopy(le->neworigin, le->oldorigin);\n\t\t\t\t\tVectorCopy(le->newangle, le->oldangle);\n \n\t\t\t\t\tVectorCopy(snew__origin, le->neworigin);\n\t\t\t\t\tVectorCopy(snew->angles, le->newangle);\n\t\t\t\t}\n\n\t\t\t\tif (snew->lerpend)\n\t\t\t\t\tle->orglerpdeltatime = bound(0.001, snew->lerpend - le->orglerpstarttime, cl_lerp_maxinterval.value);\n\t\t\t\tlfrac = (servertime - le->orglerpstarttime) / le->orglerpdeltatime;\n\t\t\t\tlfrac = bound(0, lfrac, 1);\n\t\t\t\tif (r_nolerp.ival)\n\t\t\t\t{\n\t\t\t\t\tlfrac = 1;\n\t\t\t\t\tisnew = true;\n\t\t\t\t}\n\t\t\t\tfor (i = 0; i < 3; i++)\n\t\t\t\t{\n\t\t\t\t\tle->origin[i] = le->oldorigin[i] + lfrac*(le->neworigin[i] - le->oldorigin[i]);\n\n\t\t\t\t\ta1 = le->oldangle[i];\n\t\t\t\t\ta2 = le->newangle[i];\n\t\t\t\t\tif (a1 - a2 > 180)\n\t\t\t\t\t\ta1 -= 360;\n\t\t\t\t\tif (a1 - a2 < -180)\n\t\t\t\t\t\ta1 += 360;\n\t\t\t\t\tle->angles[i] = a1 + lfrac * (a2 - a1);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tfloat lfrac;\n\n\t\t\t\tif (le->newsequence != snew->sequence)\n\t\t\t\t{\n\t\t\t\t\tle->newsequence = snew->sequence;\n\t\t\t\t\tVectorCopy(le->neworigin, le->oldorigin);\n\t\t\t\t\tVectorCopy(le->newangle, le->oldangle);\n\t\t\t\t\tVectorCopy(snew__origin, le->neworigin);\n\t\t\t\t\tVectorCopy(snew->angles, le->newangle);\n\n\t\t\t\t\t//fixme: should be oldservertime\n\t\t\t\t\tle->orglerpdeltatime = bound(0.001, servertime-le->orglerpstarttime, cl_lerp_maxinterval.value);\n\t\t\t\t\tle->orglerpstarttime = servertime;\n\t\t\t\t}\n\n\t\t\t\tif (snew->lerpend)\n\t\t\t\t\tle->orglerpdeltatime = bound(0.001, snew->lerpend - le->orglerpstarttime, cl_lerp_maxinterval.value);\n\t\t\t\tlfrac = (servertime - le->orglerpstarttime) / le->orglerpdeltatime;\n\t\t\t\tlfrac = bound(0, lfrac, 1);\n\n\t\t\t\t//lerp based purely on the packet times,\n\t\t\t\tfor (i = 0; i < 3; i++)\n\t\t\t\t{\n\t\t\t\t\tle->origin[i] = le->oldorigin[i] + lfrac*(le->neworigin[i] - le->oldorigin[i]);\n\n\t\t\t\t\ta1 = le->oldangle[i];\n\t\t\t\t\ta2 = le->newangle[i];\n\t\t\t\t\tif (a1 - a2 > 180)\n\t\t\t\t\t\ta1 -= 360;\n\t\t\t\t\tif (a1 - a2 < -180)\n\t\t\t\t\t\ta1 += 360;\n\t\t\t\t\tle->angles[i] = a1 + lfrac * (a2 - a1);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n#ifdef RAGDOLL //this preprocessor is misnamed, but oh well\n\t\tif (snew->bonecount)\n\t\t{\n\t\t\tvoid *newbones = GetBoneSpace(newpack, snew->boneoffset);\n\t\t\tif (sold && snew->bonecount == sold->bonecount)\n\t\t\t\trag_lerpdeltaent(le, snew->bonecount, newbones, r_nolerp.ival?1:frac, GetBoneSpace(oldpack, sold->boneoffset));\n\t\t\telse\n\t\t\t\trag_lerpdeltaent(le, snew->bonecount, newbones, 1, newbones);\n\t\t}\n#endif\n\n\t\tCL_UpdateNetFrameLerpState(isnew, snew->frame, snew->baseframe, snew->basebone, le, snew->lerpend);\n\t}\n}\n\nstatic qboolean CL_ChooseInterpolationFrames(int *newf, int *oldf, float servertime)\n{\n\tint i;\n\tfloat newtime = 0;\n\t*oldf = -1;\n\t*newf = -1;\n\n\t//choose the two packets.\n\t//we should be picking the packet just after the server time, and the one just before\n\tfor (i = cls.netchan.incoming_sequence; i >= cls.netchan.incoming_sequence-UPDATE_MASK; i--)\n\t{\n\t\tif (cl.inframes[i&UPDATE_MASK].frameid != i || cl.inframes[i&UPDATE_MASK].invalid)\n\t\t\tcontinue;\t//packetloss/choke, it's really only a problem for the oldframe, but...\n\n\t\tif (cl.inframes[i&UPDATE_MASK].packet_entities.servertime >= servertime)\n\t\t{\n\t\t\tif (cl.inframes[i&UPDATE_MASK].packet_entities.servertime)\n\t\t\t{\n\t\t\t\tif (!newtime || newtime != cl.inframes[i&UPDATE_MASK].packet_entities.servertime)\t//if it's a duplicate, pick the latest (so just-shot rockets are still present)\n\t\t\t\t{\n\t\t\t\t\tnewtime = cl.inframes[i&UPDATE_MASK].packet_entities.servertime;\n\t\t\t\t\t*newf = i;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse if (newtime)\n\t\t{\n\t\t\tif (cl.inframes[i&UPDATE_MASK].packet_entities.servertime != newtime)\n\t\t\t{\t//it does actually lerp, and isn't an identical frame.\n\t\t\t\t*oldf = i;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\tif (*newf == -1)\n\t{\n\t\t/*\n\t\tThis can happen if the client's predicted time is greater than the most recently received packet.\n\t\tThis should of course not happen...\n\t\t*/\n//\t\tCon_DPrintf(\"Warning: No lerp-to frame packet\\n\");\n\n\t\t/*just grab the most recent frame that is valid*/\n\t\tfor (i = cls.netchan.incoming_sequence; i >= cls.netchan.incoming_sequence-UPDATE_MASK; i--)\n\t\t{\n\t\t\tif (cl.inframes[i&UPDATE_MASK].frameid != i || cl.inframes[i&UPDATE_MASK].invalid)\n\t\t\t\tcontinue;\t//packetloss/choke, it's really only a problem for the oldframe, but...\n\t\t\t*oldf = *newf = i;\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\telse if (*oldf == -1)\t//can happen at map start, and really laggy games, but really shouldn't in a normal game\n\t{\n\t\t*oldf = *newf;\n\t}\n\treturn true;\n}\n\nqboolean CL_MayLerp(void)\n{\n\t//force lerping when playing low-framerate demos.\n\tif (cls.demoplayback == DPB_MVD)\n\t\treturn true;\n#ifdef NQPROT\n\tif (cls.demoplayback == DPB_NETQUAKE)\n\t\treturn true;\n\n\tif (cls.protocol == CP_NETQUAKE)\t//this includes DP protocols.\n\t\treturn !cl_nolerp_netquake.ival;\n#endif\n\tif (cl_nolerp.ival == 2 && !cls.deathmatch)\n\t\treturn true;\n\treturn !cl_nolerp.ival;\n}\n\n/*fills in cl.lerpents and cl.currentpackentities*/\nvoid CL_TransitionEntities (void)\n{\n\tpacket_entities_t\t*packnew, *packold;\n\tint newf, newff, oldf, i;\n\tqboolean nolerp;\n\tfloat servertime, frac;\n\n\tif (cls.protocol == CP_QUAKEWORLD && cls.demoplayback == DPB_MVD)\n\t{\n\t\tnolerp = false;\n\t}\n\telse\n\t{\n\t\tnolerp = !CL_MayLerp() && cls.demoplayback != DPB_MVD;\n\t}\n\n\tif (cl.demonudge < 0)\n\t{\t//demo playback allows nudging to earlier frames, generally only when paused though...\n\t\tservertime = cl.inframes[(cls.netchan.incoming_sequence+cl.demonudge)&UPDATE_MASK].packet_entities.servertime;\n\t\tnolerp = true;\n\t}\n\telse if (nolerp)\n\t{\n\t\t//force our emulated time to as late as we can, if we're not using interpolation, which has the effect of disabling all interpolation\n\t\tservertime = cl.inframes[cls.netchan.incoming_sequence&UPDATE_MASK].packet_entities.servertime;\n\t}\n\telse\n\t{\n\t\t//otherwise go for the latest frame we can.\n\t\tservertime = cl.servertime;\n\t}\n\n//\tservertime -= 0.1;\n\n\t/*make sure we have some info for it, on failure keep the info from the last frame (its possible that the frame data can be changed by a network packet, but mneh, but chances are if there's no info then there are NO packets at all)*/\n\tif (!CL_ChooseInterpolationFrames(&newf, &oldf, servertime))\n\t\treturn;\n\n\tnewff = newf;\n\tnewf&=UPDATE_MASK;\n\toldf&=UPDATE_MASK;\n\t/*transition the ents and stuff*/\n\tpacknew = &cl.inframes[newf].packet_entities;\n\tpackold = &cl.inframes[oldf].packet_entities;\n\tif (packnew->servertime == packold->servertime)\n\t\tfrac = 1; //lerp totally into the new (avoid any division-by-0 issues here)\n\telse\n\t\tfrac = (servertime-packold->servertime)/(packnew->servertime-packold->servertime);\n\n//\tif (!cl.paused)\n//\t\tCon_DPrintf(\"%f %s%f^7 %f (%f) (%i) %f %s%f^7 %f\\n\", packold->servertime, (servertime<packold->servertime||packnew->servertime<servertime)?\"^1\":\"\",servertime, packnew->servertime, frac, newff, cl.oldgametime, (servertime<cl.oldgametime||cl.gametime<servertime)?\"^3\":\"\", servertime, cl.gametime);\n\n\tCL_TransitionPacketEntities(newff, packnew, packold, frac, servertime);\n\n\tfor (i = 0; i < cl.splitclients; i++)\n\t{\n\t\tVectorInterpolate(packold->punchangle[i], frac, packnew->punchangle[i], cl.playerview[i].punchangle_sv);\n\t\tVectorInterpolate(packold->punchorigin[i], frac, packnew->punchorigin[i], cl.playerview[i].punchorigin);\n\t}\n\n\n\t/*and transition players too*/\n\t{\n\t\tfloat frac, a1, a2;\n\t\tint i, p;\n\t\tvec3_t move;\n\t\tlerpents_t *le;\n\t\tplayer_state_t *pnew, *pold;\n\t\tif (!cl.do_lerp_players)\n\t\t{\n\t\t\tnewf = newff = oldf = cl.parsecount;\n\t\t\tnewf&=UPDATE_MASK;\n\t\t\toldf&=UPDATE_MASK;\n\t\t}\n\t\tif (packnew->servertime == packold->servertime)\n\t\t\tfrac = 1; //lerp totally into the new\n\t\telse\n\t\t\tfrac = (servertime-packold->servertime)/(packnew->servertime-packold->servertime);\n\t\tpnew = &cl.inframes[newf].playerstate[0];\n\t\tpold = &cl.inframes[oldf].playerstate[0];\n\t\tfor (p = 0; p < cl.allocated_client_slots; p++, pnew++, pold++)\n\t\t{\n\t\t\tif (pnew->messagenum != newff)\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\n\t\t\tle = &cl.lerpplayers[p];\n\t\t\tVectorSubtract(pnew->predorigin, pold->predorigin, move);\n\n\t\t\tif (DotProduct(move, move) > 120*120)\n\t\t\t\tfrac = 1;\n\n\t\t\t//lerp based purely on the packet times,\n\t\t\tfor (i = 0; i < 3; i++)\n\t\t\t{\n\t\t\t\tle->origin[i] = pold->predorigin[i] + frac*(move[i]);\n\n\t\t\t\ta1 = SHORT2ANGLE(pold->command.angles[i]);\n\t\t\t\ta2 = SHORT2ANGLE(pnew->command.angles[i]);\n\t\t\t\tif (a1 - a2 > 180)\n\t\t\t\t\ta1 -= 360;\n\t\t\t\tif (a1 - a2 < -180)\n\t\t\t\t\ta1 += 360;\n\t\t\t\tle->angles[i] = a1 + frac * (a2 - a1);\n\t\t\t}\n\t\t\tle->orglerpdeltatime = 0.1;\n\t\t\tle->orglerpstarttime = packold->servertime;\n\t\t}\n\t}\n}\n\nvoid CL_LinkPacketEntities (void)\n{\n\textern cvar_t gl_part_flame;\n\tentity_t\t\t\t*ent;\n\tpacket_entities_t\t*pack;\n\tentity_state_t\t\t*state;\n\tlerpents_t\t\t*le;\n\tmodel_t\t\t\t\t*model, *model2;\n\tvec3_t\t\t\t\told_origin;\n\tfloat\t\t\t\tautorotate;\n\tint\t\t\t\t\ti;\n\tint\t\t\t\t\tnewpnum;\n\t//, spnum;\n\tdlight_t\t\t\t*dl;\n\tvec3_t\t\t\t\tangles;\n\tstatic int flickertime;\n\tstatic int flicker;\n\tint trailef, trailidx;\n\tint modelflags;\n\tstruct itemtimer_s\t*timer, **timerlink;\n\tfloat timestep = cl.time-cl.lastlinktime;\n\textern cvar_t r_ignoreentpvs;\n\tvec3_t absmin, absmax;\n\tcl.lastlinktime = cl.time;\n\ttimestep = bound(0, timestep, 0.1);\n\n\tpack = cl.currentpackentities;\n\tif (!pack)\n\t\treturn;\n\n\ti = cl.currentpacktime*20;\n\tif (flickertime != i)\n\t{\n\t\tflickertime = i;\n\t\tflicker = rand();\n\t}\n\n\tautorotate = anglemod(100*cl.currentpacktime);\n\n#ifdef CSQC_DAT\n\tCSQC_DeltaStart(cl.currentpacktime);\n#endif\n\n\n\tfor (timerlink = &cl.itemtimers; (timer=*timerlink); )\n\t{\n\t\tif (cl.time > timer->end)\n\t\t{\n\t\t\t*timerlink = timer->next;\n\t\t\tZ_Free(timer);\n\t\t}\n\t\telse\n\t\t{\n\t\t\ttimerlink = &(*timerlink)->next;\n/*\t\t\tif (timer->entnum>0)\n\t\t\t{\n\t\t\t\tif (timer->entnum < cl.maxlerpents)\n\t\t\t\t{\n\t\t\t\t\tle = &cl.lerpents[timer->entnum];\n\t\t\t\t\tif (le->sequence == cl.lerpentssequence)\n\t\t\t\t\t\tVectorCopy(le->origin, timer->origin);\n\t\t\t\t}\n\t\t\t}*/\n\t\t\tR_AddItemTimer(timer->origin, cl.time*90 + timer->origin[0] + timer->origin[1] + timer->origin[2], timer->radius, (cl.time - timer->start) / timer->duration, timer->rgb);\n\t\t}\n\t}\n\n\tfor (newpnum=0 ; newpnum<pack->num_entities ; newpnum++)\n\t{\n\t\tstate = &pack->entities[newpnum];\n\n#ifdef CSQC_DAT\n\t\tif (CSQC_DeltaUpdate(state))\n\t\t\tcontinue;\n#endif\n\n\t\tif (cl_numvisedicts == cl_maxvisedicts)\n\t\t\tbreak;\n\n\t\tif (state->number >= cl.maxlerpents)\n\t\t\tcontinue;\n\n\t\tle = &cl.lerpents[state->number];\n\n\t\tent = &cl_visedicts[cl_numvisedicts];\n\n\t\tent->rtype = RT_MODEL;\n\t\tent->playerindex = -1;\n\t\tent->customskin = 0;\n\t\tent->topcolour = TOP_DEFAULT;\n\t\tent->bottomcolour = BOTTOM_DEFAULT;\n#ifdef HEXEN2\n\t\tent->h2playerclass = 0;\n#endif\n\t\tent->light_known = 0;\n\t\tent->forcedshader = NULL;\n\t\tent->shaderTime = 0;\n\n\t\tmemset(&ent->framestate, 0, sizeof(ent->framestate));\n\n\t\tVectorCopy(le->origin, ent->origin);\n\n\t\t//bots or powerup glows. items always glow, bots can be disabled\n\t\tif (state->modelindex != cl_playerindex || r_powerupglow.ival)\n\t\tif (state->effects & (EF_GREEN | EF_BLUE | EF_RED | EF_BRIGHTLIGHT | EF_DIMLIGHT))\n\t\t{\n\t\t\tvec3_t colour;\n\t\t\tfloat radius;\n\t\t\tcolour[0] = 0;\n\t\t\tcolour[1] = 0;\n\t\t\tcolour[2] = 0;\n\t\t\tradius = 0;\n\n\t\t\tif (state->effects & EF_BRIGHTLIGHT)\n\t\t\t{\n\t\t\t\tradius = max(radius,r_brightlight_colour.vec4[3]);\n\t\t\t\tif (!(state->effects & (EF_RED|EF_GREEN|EF_BLUE)))\n\t\t\t\t\tVectorAdd(colour, r_brightlight_colour.vec4, colour);\n\t\t\t}\n\t\t\tif (state->effects & EF_DIMLIGHT)\n\t\t\t{\n\t\t\t\tradius = max(radius,r_dimlight_colour.vec4[3]);\n\t\t\t\tif (!(state->effects & (EF_RED|EF_GREEN|EF_BLUE)))\n\t\t\t\t\tVectorAdd(colour, r_dimlight_colour.vec4, colour);\n\t\t\t}\n\t\t\tif (state->effects & EF_BLUE)\n\t\t\t{\n\t\t\t\tradius = max(radius,r_bluelight_colour.vec4[3]);\n\t\t\t\tVectorAdd(colour, r_bluelight_colour.vec4, colour);\n\t\t\t}\n\t\t\tif (state->effects & EF_RED)\n\t\t\t{\n\t\t\t\tradius = max(radius,r_redlight_colour.vec4[3]);\n\t\t\t\tVectorAdd(colour, r_redlight_colour.vec4, colour);\n\t\t\t}\n\t\t\tif (state->effects & EF_GREEN)\n\t\t\t{\n\t\t\t\tradius = max(radius,r_greenlight_colour.vec4[3]);\n\t\t\t\tVectorAdd(colour, r_greenlight_colour.vec4, colour);\n\t\t\t}\n\n\t\t\tif (radius)\n\t\t\t{\n\t\t\t\tradius += r_lightflicker.value?((flicker + state->number)&31):0;\n\t\t\t\tdl = CL_NewDlight(state->number, ent->origin, radius, 0.1, colour[0], colour[1], colour[2]);\n\n\t\t\t\tif (state->effects & EF_BRIGHTLIGHT)\n\t\t\t\t{\t//urgh. apparently correct for vanilla quake. puts the bright effect about where the firing point is. broken for hexen2, yet still consistent with the hexen2 engine...\n\t\t\t\t\tdl->origin[2] += 16;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif ((state->lightpflags & (PFLAGS_FULLDYNAMIC|PFLAGS_CORONA)) && ((state->lightpflags&PFLAGS_FULLDYNAMIC)||state->light[3]))\n\t\t{\n\t\t\tvec3_t colour;\n\t\t\tif (!state->light[0] && !state->light[1] && !state->light[2])\n\t\t\t{\n\t\t\t\tcolour[0] = colour[1] = colour[2] = 1;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tcolour[0] = state->light[0]/1024.0f;\n\t\t\t\tcolour[1] = state->light[1]/1024.0f;\n\t\t\t\tcolour[2] = state->light[2]/1024.0f;\n\t\t\t}\n\t\t\tdl = CL_NewDlight(state->number, ent->origin, state->light[3]?state->light[3]:350, 0.1, colour[0], colour[1], colour[2]);\n\t\t\tif (!(state->lightpflags & PFLAGS_FULLDYNAMIC))\t//corona-only lights shouldn't do much else.\n\t\t\t{\n\t\t\t\tdl->flags &= ~(LFLAG_LIGHTMAP|LFLAG_FLASHBLEND);\n#ifdef RTLIGHTS\n\t\t\t\t/*make sure there's no rtlight*/\n\t\t\t\tmemset(dl->lightcolourscales, 0, sizeof(dl->lightcolourscales));\n#endif\n\t\t\t}\n\t\t\tdl->corona = (state->lightpflags & PFLAGS_CORONA)?1:0;\n\t\t\tdl->coronascale = 0.25;\n\t\t\tdl->style = state->lightstyle;\n\t\t\tdl->flags &= ~LFLAG_FLASHBLEND;\n\t\t\tdl->flags |= (state->lightpflags & PFLAGS_NOSHADOW)?LFLAG_NOSHADOWS:0;\n#ifdef RTLIGHTS\n\t\t\tif (state->skinnum)\n\t\t\t{\n\t\t\t\tVectorCopy(le->angles, angles);\n\t\t\t\t//if (model && model->type == mod_alias)\n\t\t\t\t\tAngleVectorsMesh(angles, dl->axis[0], dl->axis[1], dl->axis[2]);\t//pflags matches alias models.\n\t\t\t\t//else\n\t\t\t\t//\tAngleVectors(angles, dl->axis[0], dl->axis[1], dl->axis[2]);\n\t\t\t\tVectorInverse(dl->axis[1]);\n\t\t\t\tR_LoadNumberedLightTexture(dl, state->skinnum);\n\t\t\t}\n#endif\n\t\t}\n\n\t\tif (r_torch.ival && state->number <= cl.allocated_client_slots)\n\t\t{\n\t\t\tdlight_t *dl;\n\t\t\tdl = CL_NewDlight(state->number, ent->origin, 300, r_torch.ival, 0.9, 0.9, 0.6);\n\t\t\tdl->flags |= LFLAG_SHADOWMAP|LFLAG_FLASHBLEND;\n\t\t\tdl->fov = 90;\n\t\t\tVectorCopy(le->angles, angles);\n\t\t\tangles[0] *= 3;\n//\t\t\tangles[1] += sin(realtime)*8;\n//\t\t\tangles[0] += cos(realtime*1.13)*5;\n\t\t\tAngleVectorsMesh(angles, dl->axis[0], dl->axis[1], dl->axis[2]);\n\t\t\tVectorInverse(dl->axis[1]);\n\n\t\t\tVectorMA(dl->origin, 16, dl->axis[0], dl->origin);\n\t\t}\n\n\t\t// if set to invisible, skip\n\t\tif (state->modelindex<1 || (state->effects & NQEF_NODRAW))\n\t\t{\n\t\t\tif (state->tagindex == 0xffff)\n\t\t\t{\n\t\t\t\tif (state->tagentity)\n\t\t\t\t{\n\t\t\t\t\tent->rtype = RT_PORTALCAMERA;\n\t\t\t\t\tent->keynum = state->tagentity;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tent->rtype = RT_PORTALSURFACE;\n\t\t\t\t\tVectorCopy(ent->origin, ent->oldorigin);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t\tcontinue;\n\t\t\tmodel = NULL;\n\n\t\t\tmodelflags = state->effects>>24;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (CL_FilterModelindex(state->modelindex, state->frame))\n\t\t\t\tcontinue;\n\n\t\t\tmodel = cl.model_precache[state->modelindex];\n\t\t\tif (!model)\n\t\t\t{\n\t\t\t\tCon_DPrintf(\"Bad modelindex (%i)\\n\", state->modelindex);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (model->loadstate != MLS_LOADED)\n\t\t\t{\n\t\t\t\tif (model->loadstate == MLS_NOTLOADED)\n\t\t\t\t\tMod_LoadModel(model, MLV_WARN);\n\t\t\t\tcontinue;\t//still waiting for it to load, don't poke anything here\n\t\t\t}\n\n\t\t\t//DP extension. .modelflags (which is sent in the high parts of effects) allows to specify exactly the q1-compatible flags.\n\t\t\t//the extra bit allows for setting to 0.\n\t\t\t//note that hexen2 has additional flags which cannot be expressed.\n\t\t\tmodelflags = state->effects>>24;\n\t\t\tif (!(state->effects & EF_NOMODELFLAGS))\n\t\t\t\tmodelflags |= model->flags;\n\t\t}\n\n#ifdef HAVE_LEGACY\n\t\tif (cl.model_precache_vwep[0] && state->modelindex2 < MAX_VWEP_MODELS)\n\t\t{\n\t\t\tif (state->modelindex == cl_playerindex && cl.model_precache_vwep[0]->loadstate == MLS_LOADED &&\n\t\t\t\tstate->modelindex2 && cl.model_precache_vwep[state->modelindex2] && cl.model_precache_vwep[state->modelindex2]->loadstate == MLS_LOADED)\n\t\t\t{\n\t\t\t\tmodel = cl.model_precache_vwep[0];\n\t\t\t\tmodel2 = cl.model_precache_vwep[state->modelindex2];\n\t\t\t}\n\t\t\telse\n\t\t\t\tmodel2 = NULL;\n\t\t}\n\t\telse\n#endif\n\t\t\tif (state->modelindex2 && state->modelindex2 < MAX_PRECACHE_MODELS)\n\t\t\tmodel2 = cl.model_precache[state->modelindex2];\n\t\telse\n\t\t\tmodel2 = NULL;\n\n\n\t\tif (r_ignoreentpvs.ival || !model)\n\t\t{\n\t\t\tent->pvscache.num_leafs = 0;\n#if defined(Q2BSPS) || defined(Q3BSPS) || defined(TERRAIN)\n\t\t\tent->pvscache.areanum = 0;\n\t\t\tent->pvscache.areanum2 = 0;\n\t\t\tent->pvscache.headnode = 0;\n#endif\n\t\t}\n\t\telse\n\t\t{\n\t\t\t/*bsp model size*/\n\t\t\tif (model->type == mod_brush && (state->angles[0]||state->angles[1]||state->angles[2]))\n\t\t\t{\n\t\t\t\tint i;\n\t\t\t\tfloat v;\n\t\t\t\tfloat max;\n\t\t\t\t//q2 method, works best with origin brushes.\n\t\t\t\tmax = 0;\n\t\t\t\tfor (i=0 ; i<3 ; i++)\n\t\t\t\t{\n\t\t\t\t\tv =fabs( model->mins[i]);\n\t\t\t\t\tif (v > max)\n\t\t\t\t\t\tmax = v;\n\t\t\t\t\tv =fabs( model->maxs[i]);\n\t\t\t\t\tif (v > max)\n\t\t\t\t\t\tmax = v;\n\t\t\t\t}\n\t\t\t\tfor (i=0 ; i<3 ; i++)\n\t\t\t\t{\n\t\t\t\t\tabsmin[i] = ent->origin[i] - max;\n\t\t\t\t\tabsmax[i] = ent->origin[i] + max;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tVectorAdd(model->mins, ent->origin, absmin);\n\t\t\t\tVectorAdd(model->maxs, ent->origin, absmax);\n\t\t\t}\n\t\t\tcl.worldmodel->funcs.FindTouchedLeafs(cl.worldmodel, &ent->pvscache, absmin, absmax);\n\t\t}\n\n\t\tcl_numvisedicts++;\n\n\t\tent->forcedshader = NULL;\n\n\t\tent->keynum = state->number;\n\n\t\tif (cl_r2g.value && state->modelindex == cl_rocketindex && cl_rocketindex != -1 && cl_grenadeindex != -1)\n\t\t\tmodel = cl.model_precache[cl_grenadeindex];\n\t\tent->model = model;\n\n\t\tent->flags = 0;\n\t\tif ((state->dpflags & RENDER_EXTERIORMODEL) || r_refdef.playerview->viewentity == state->number)\n\t\t\tent->flags |= RF_EXTERNALMODEL;\n\t\tif (state->dpflags & RENDER_VIEWMODEL)\n\t\t{\n\t\t\tent->flags |= RF_WEAPONMODEL|Q2RF_MINLIGHT|RF_DEPTHHACK;\n\t\t\tif (state->effects & DPEF_NOGUNBOB)\n\t\t\t\tent->flags |= RF_WEAPONMODELNOBOB;\n\t\t}\n\t\tif (state->effects & NQEF_ADDITIVE)\n\t\t\tent->flags |= RF_ADDITIVE;\n\t\tif (state->effects & EF_NODEPTHTEST)\n\t\t\tent->flags |= RF_NODEPTHTEST;\n\t\tif (state->effects & EF_NOSHADOW)\n\t\t\tent->flags |= RF_NOSHADOW;\n\t\tif (state->trans < 0xfe)\n\t\t{\n\t\t\tent->shaderRGBAf[3] = state->trans/(float)0xfe;\n\t\t\tent->flags |= RF_TRANSLUCENT;\n\t\t}\n\t\telse\n\t\t\tent->shaderRGBAf[3] = 1;\n\n/*\t\tif (le->origin[2] < r_refdef.waterheight != le->lastorigin[2] < r_refdef.waterheight)\n\t\t{\n\t\t\tP_RunParticleEffectTypeString(le->origin, NULL, 1, \"te_watertransition\");\n\t\t}\n*/\n\t\t// set colormap\n\t\tif (state->dpflags & RENDER_COLORMAPPED)\n\t\t{\n\t\t\tent->topcolour    = (state->colormap>>4) & 0xf;\n\t\t\tent->bottomcolour = (state->colormap>>0) & 0xf;\n\t\t}\n\t\telse if (state->colormap > 0 && state->colormap <= cl.allocated_client_slots)\n\t\t{\n\t\t\tent->playerindex = state->colormap-1;\n#ifdef HEXEN2\n\t\t\tent->h2playerclass = cl.players[ent->playerindex].h2playerclass;\n#endif\n\t\t\tent->topcolour    = cl.players[ent->playerindex].dtopcolor;\n\t\t\tent->bottomcolour = cl.players[ent->playerindex].dbottomcolor;\n\t\t}\n\n\t\t// set skin\n\t\tent->skinnum = state->skinnum;\n\n#ifdef HEXEN2\n\t\tent->abslight = state->abslight;\n\t\tent->drawflags = state->hexen2flags;\n#endif\n\n\t\tCL_LerpNetFrameState(&ent->framestate, le);\n\n#ifdef PEXT_SCALE\n\t\t//set scale\n\t\tent->scale = state->scale/16.0;\n#endif\n\t\tif (state->colormod[0] == 32 && state->colormod[1] == 32 && state->colormod[2] == 32)\n\t\t\tent->shaderRGBAf[0] = ent->shaderRGBAf[1] = ent->shaderRGBAf[2] = 1;\n\t\telse\n\t\t{\n\t\t\tent->flags |= RF_FORCECOLOURMOD;\n\t\t\tent->shaderRGBAf[0] = (state->colormod[0]*8.0f)/256;\n\t\t\tent->shaderRGBAf[1] = (state->colormod[1]*8.0f)/256;\n\t\t\tent->shaderRGBAf[2] = (state->colormod[2]*8.0f)/256;\n\t\t}\n\t\tVectorScale(state->glowmod, 8.0/256.0, ent->glowmod);\n\n#ifdef PEXT_FATNESS\n\t\t//set trans\n\t\tent->fatness = state->fatness/16.0;\n#endif\n\n\t\t//swap items with sprites if desired.\n\t\tif (gl_simpleitems.ival && ent->skinnum >= 0 && ent->skinnum < countof(model->simpleskin) && model)\n\t\t{\n\t\t\tif (!model->simpleskin[ent->skinnum])\n\t\t\t{\n\t\t\t\tchar basename[64], name[MAX_QPATH];\n\t\t\t\tCOM_FileBase(model->name, basename, sizeof(basename));\n\t\t\t\tif (!strncmp(model->name, \"maps/\", 5))\n\t\t\t\t\tQ_snprintfz(name, sizeof(name), \"textures/bmodels/simple_%s_%i.tga\", basename, ent->skinnum);\n\t\t\t\telse\n\t\t\t\t\tQ_snprintfz(name, sizeof(name), \"textures/models/simple_%s_%i.tga\", basename, ent->skinnum);\n\t\t\t\tmodel->simpleskin[ent->skinnum] = R_RegisterShader(name, 0, va(\"{\\nnomipmaps\\nprogram defaultsprite#MASK=0.5\\nsurfaceparm noshadows\\nsurfaceparm nodlight\\nsort seethrough\\n{\\nmap \\\"%s\\\"\\nalphafunc ge128\\n}\\n}\\n\", name));\n\t\t\t}\n\t\t\tVectorCopy(le->angles, angles);\n\n\t\t\tif (R_GetShaderSizes(model->simpleskin[ent->skinnum], NULL, NULL, false) > 0)\n\t\t\t{\n\t\t\t\tfloat tr[2];\n\t\t\t\tent->forcedshader = model->simpleskin[ent->skinnum];\n\t\t\t\tent->rtype = RT_SPRITE;\n\t\t\t\tent->scale *= 16;\n\n\t\t\t\ttr[0] = sin(le->angles[1] * M_PI / 180.0);\n\t\t\t\ttr[1] = cos(le->angles[1] * M_PI / 180.0);\n\t\t\t\tent->origin[1] += tr[0] * (model->maxs[0] + model->mins[0])*0.5 + tr[1] * (model->maxs[1] + model->mins[1])*0.5;\n\t\t\t\tent->origin[0] += tr[1] * (model->maxs[1] + model->mins[1])*0.5 - tr[0] * (model->maxs[0] + model->mins[0])*0.5;\n\t\t\t\tent->origin[2] += model->mins[2];\n\n\t\t\t\tent->origin[2] += ent->scale;\n\n\t\t\t\tif (cl_item_bobbing.value)\n\t\t\t\t\tent->origin[2] += 5+sin(cl.time*3+(ent->origin[0]+ent->origin[1])/8)*5.5;\t//don't let it into the ground\n\t\t\t}\n\t\t\telse if (modelflags & MF_ROTATE)\n\t\t\t{\t//surely there's a more sane way to handle this.\n\t\t\t\tangles[0] = 0;\n\t\t\t\tangles[1] = autorotate;\n\t\t\t\tangles[2] = 0;\n\n\t\t\t\tif (cl_item_bobbing.value)\n\t\t\t\t\tent->origin[2] += 5+sin(cl.time*3+(state->origin[0]+state->origin[1])/8)*5.5;\t//don't let it into the ground\n\t\t\t}\n\t\t}\n\t\t// rotate pickup objects locally\n\t\telse if (modelflags & MF_ROTATE)\n\t\t{\n\t\t\tangles[0] = 0;\n\t\t\tangles[1] = autorotate;\n\t\t\tangles[2] = 0;\n\n\t\t\tif (cl_item_bobbing.value)\n\t\t\t\tent->origin[2] += 5+sin(cl.time*3+(state->origin[0]+state->origin[1])/8)*5.5;\t//don't let it into the ground\n\t\t}\n\t\telse\n\t\t{\n\t\t\tfor (i=0 ; i<3 ; i++)\n\t\t\t{\n\t\t\t\tangles[i] = le->angles[i];\n\t\t\t}\n\t\t}\n\n\t\tVectorCopy(angles, ent->angles);\n\t\tif (model->type == mod_alias)\n\t\t\tAngleVectorsMesh(angles, ent->axis[0], ent->axis[1], ent->axis[2]);\n\t\telse\n\t\t\tAngleVectors(angles, ent->axis[0], ent->axis[1], ent->axis[2]);\n\t\tVectorInverse(ent->axis[1]);\n\n\t\t/*if this entity is in a player's slot...*/\n\t\tif (ent->keynum <= cl.allocated_client_slots)\n\t\t{\n\t\t\tif (!cl.playerview[0].nolocalplayer)\n\t\t\t\tent->keynum += MAX_EDICTS;\n\t\t}\n\n\t\tif (state->tagindex == 0xffff)\n\t\t{\n\t\t\tif (state->tagentity)\n\t\t\t{\n\t\t\t\tent->rtype = RT_PORTALCAMERA;\n\t\t\t\tent->keynum = state->tagentity;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tent->rtype = RT_PORTALSURFACE;\n\t\t\t\tVectorCopy(ent->origin, ent->oldorigin);\n\t\t\t}\n\t\t}\n\t\telse if (state->tagentity)\n\t\t{\t//ent is attached to a tag, rotate this ent accordingly.\n\t\t\tCL_RotateAroundTag(ent, state->number, state->tagentity, state->tagindex);\n\t\t}\n\n#ifdef RAGDOLL\n\t\tif (model && (model->dollinfo || le->skeletalobject))\n\t\t\trag_updatedeltaent(&csqc_world, ent, le);\n#endif\n\t\tent->framestate.g[FS_REG].frame[0] &= ~0x8000;\n\t\tent->framestate.g[FS_REG].frame[1] &= ~0x8000;\n\n\t\tCLQ1_AddShadow(ent);\n\t\tCLQ1_AddPowerupShell(ent, false, state->effects);\n\n\t\tif (model2)\n\t\t\tCL_AddVWeapModel (ent, model2);\n\n\t\t//figure out which trail this entity is using\n\t\tif (model)\n\t\t{\n\t\t\ttrailef = model->particletrail;\n\t\t\ttrailidx = model->traildefaultindex;\n\t\t}\n\t\telse\n\t\t{\n\t\t\ttrailef = P_INVALID;\n\t\t\ttrailidx = P_INVALID;\n\t\t}\n\t\tif ((state->effects & EF_HASPARTICLETRAIL) || modelflags)\n\t\t\tP_DefaultTrail (state->effects, modelflags, &trailef, &trailidx);\n\t\tif (state->u.q1.traileffectnum)\n\t\t\ttrailef = CL_TranslateParticleFromServer(state->u.q1.traileffectnum);\n\n\t\tif (state->u.q1.emiteffectnum)\n\t\t\tP_EmitEffect (ent->origin, ent->axis, MDLF_EMITFORWARDS, CL_TranslateParticleFromServer(state->u.q1.emiteffectnum), &(le->emitstate));\n\t\telse if (model && model->particleeffect != P_INVALID && cls.allow_anyparticles && gl_part_flame.ival)\n\t\t\tP_EmitEffect (ent->origin, ent->axis, model->engineflags, model->particleeffect, &(le->emitstate));\n\n\t\t// add automatic particle trails\n\t\tif (!model || (!(modelflags&~MF_ROTATE) && trailef < 0))\n\t\t\tcontinue;\n\n\t\tif (!cls.allow_anyparticles && !(modelflags & ~MF_ROTATE))\n\t\t\tcontinue;\n\n\t\tif (le->isnew)\n\t\t{\n\t\t\tle->isnew = false;\n\t\t\tpe->DelinkTrailstate(&(cl.lerpents[state->number].trailstate));\n\t\t\tpe->DelinkTrailstate(&(cl.lerpents[state->number].emitstate));\n\t\t\tcontinue;\t\t// not in last message\n\t\t}\n\n\t\tVectorCopy(le->lastorigin, old_origin);\n\t\tfor (i=0 ; i<3 ; i++)\n\t\t{\n\t\t\tif ( fabs(old_origin[i] - ent->origin[i]) > 128)\n\t\t\t{\t// no trail if too far\n\t\t\t\tVectorCopy (ent->origin, old_origin);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\t//and emit it\n//\t\tif (lasttime != cl.currentpacktime)\n\t\t{\n\t\t\tif (trailef == P_INVALID || pe->ParticleTrail (old_origin, ent->origin, trailef, timestep, ent->keynum, ent->axis, &(le->trailstate)))\n\t\t\t\tif (model->traildefaultindex >= 0)\n\t\t\t\t\tpe->ParticleTrailIndex(old_origin, ent->origin, P_INVALID, timestep, trailidx, 0, &(le->trailstate));\n\n\t\t\t//dlights are not so customisable.\n\t\t\tif (r_rocketlight.value && (modelflags & MF_ROCKET) && !(state->lightpflags & (PFLAGS_FULLDYNAMIC|PFLAGS_CORONA)))\n\t\t\t{\n\t\t\t\tfloat rad = 0;\n\t\t\t\textern cvar_t r_rocketlight_colour;\n\t\t\t\trad = r_rocketlight_colour.vec4[3];\n\t\t\t\trad += r_lightflicker.value?((flicker + state->number)&31):0;\n\n\t\t\t\tdl = CL_AllocDlight (state->number);\n\t\t\t\tmemcpy(dl->axis, ent->axis, sizeof(dl->axis));\n\t\t\t\tVectorCopy (ent->origin, dl->origin);\n\t\t\t\tdl->die = (float)cl.time;\n\t\t\t\tif (modelflags & MF_ROCKET)\n\t\t\t\t\tdl->origin[2] += 1; // is this even necessary\n\t\t\t\tdl->radius = rad * r_rocketlight.value;\n\t\t\t\tVectorCopy(r_rocketlight_colour.vec4, dl->color);\n\t\t\t}\n\t\t}\n\t}\n#ifdef CSQC_DAT\n\tCSQC_DeltaEnd();\n#endif\n\n\tCLQ1_AddVisibleBBoxes();\n\n#ifdef RTLIGHTS\n\tR_EditLights_DrawLights();\n#endif\n}\n\n/*\n=========================================================================\n\nPROJECTILE PARSING / LINKING\n\n=========================================================================\n*/\n\ntypedef struct\n{\n\tint\t\tmodelindex;\n\tvec3_t\torigin;\n\tvec3_t\tangles;\n} projectile_t;\n\n#define\tMAX_PROJECTILES\t32\nprojectile_t\tcl_projectiles[MAX_PROJECTILES];\nint\t\t\t\tcl_num_projectiles;\n\nextern int cl_spikeindex;\n\nvoid CL_ClearProjectiles (void)\n{\n\tcl_num_projectiles = 0;\n}\n\n/*\n=====================\nCL_ParseProjectiles\n\nNails are passed as efficient temporary entities\n=====================\n*/\nvoid CL_ParseProjectiles (int modelindex, qboolean nails2)\n{\n\tint\t\ti, c, j;\n\tqbyte\tbits[6];\n\tprojectile_t\t*pr;\n\n\tc = MSG_ReadByte ();\n\tfor (i=0 ; i<c ; i++)\n\t{\n\t\tif (nails2)\n\t\t\tMSG_ReadByte();\n\t\tfor (j=0 ; j<6 ; j++)\n\t\t\tbits[j] = MSG_ReadByte ();\n\n\t\tif (cl_num_projectiles == MAX_PROJECTILES)\n\t\t\tcontinue;\n\n\t\tpr = &cl_projectiles[cl_num_projectiles];\n\t\tcl_num_projectiles++;\n\n\t\tpr->modelindex = modelindex;\n\t\tpr->origin[0] = ( ( bits[0] + ((bits[1]&15)<<8) ) <<1) - 4096;\n\t\tpr->origin[1] = ( ( (bits[1]>>4) + (bits[2]<<4) ) <<1) - 4096;\n\t\tpr->origin[2] = ( ( bits[3] + ((bits[4]&15)<<8) ) <<1) - 4096;\n\t\tpr->angles[0] = 360*(((int)bits[4]>>4)/16.0f + 1/32.0f);\n\t\tpr->angles[1] = 360*(int)bits[5]/256.0f;\n\t}\n}\n\n/*\n=============\nCL_LinkProjectiles\n\n=============\n*/\nvoid CL_LinkProjectiles (void)\n{\n\tint\t\ti;\n\tprojectile_t\t*pr;\n\tentity_t\t\t*ent;\n\n\tfor (i=0, pr=cl_projectiles ; i<cl_num_projectiles ; i++, pr++)\n\t{\n\t\tif (pr->modelindex < 1)\n\t\t\tcontinue;\n\n\t\t// grab an entity to fill in\n\t\tif (cl_numvisedicts == cl_maxvisedicts)\n\t\t\tbreak;\t\t// object list is full\n\t\tent = &cl_visedicts[cl_numvisedicts];\n\t\tcl_numvisedicts++;\n\t\tmemset(ent, 0, sizeof(*ent));\n\n\t\tent->model = cl.model_precache[pr->modelindex];\n\t\tent->playerindex = -1;\n\t\tent->topcolour = TOP_DEFAULT;\n\t\tent->bottomcolour = BOTTOM_DEFAULT;\n\t\tent->framestate.g[FS_REG].lerpweight[0] = 1;\n\n#ifdef PEXT_SCALE\n\t\tent->scale = 1;\n#endif\n\n\t\tent->shaderRGBAf[0] = 1;\n\t\tent->shaderRGBAf[1] = 1;\n\t\tent->shaderRGBAf[2] = 1;\n\t\tent->shaderRGBAf[3] = 1;\n\n\t\tVectorCopy (pr->origin, ent->origin);\n\t\tVectorCopy (pr->angles, ent->angles);\n\n\t\tAngleVectorsMesh(ent->angles, ent->axis[0], ent->axis[1], ent->axis[2]);\n\t\tVectorInverse(ent->axis[1]);\n\t}\n}\n\n//========================================\n\nextern\tint\t\tcl_spikeindex, cl_playerindex, cl_flagindex, cl_rocketindex, cl_grenadeindex;\n\nentity_t *CL_NewTempEntity (void);\n\nstatic int MVD_TranslateFlags(int src)\n{\n\tint dst = 0;\n\n\tif (src & DF_EFFECTS)\n\t\tdst |= PF_EFFECTS;\n\tif (src & DF_SKINNUM)\n\t\tdst |= PF_SKINNUM;\n\tif (src & DF_DEAD)\n\t\tdst |= PF_DEAD;\n\tif (src & DF_GIB)\n\t\tdst |= PF_GIB;\n\tif (src & DF_WEAPONFRAME)\n\t\tdst |= PF_WEAPONFRAME;\n\tif (src & DF_MODEL)\n\t\tdst |= PF_MODEL;\n\n\treturn dst;\n}\n\n/*\n===================\nCL_ParsePlayerinfo\n===================\n*/\nextern int parsecountmod, oldparsecountmod;\nextern double parsecounttime;\n\nvoid CL_ParseClientdata (void);\nvoid CL_MVDUpdateSpectator(void)\n{\n\tCL_ParseClientdata();\n}\n\n\nvoid CLQW_ParsePlayerinfo (void)\n{\n\tfloat\t\t\tmsec;\n\tunsigned int\t\t\tflags;\n\tplayer_info_t\t*info;\n\tplayer_state_t\t*state, *oldstate;\n\tunsigned int\t\t\tnum;\n\tint\t\t\ti;\n\tint newf;\n\tvec3_t\t\torg, dist;\n\n\tif (cls.fteprotocolextensions2&PEXT2_LONGINDEXES)\n\t\tnum = MSG_ReadUInt64 ();\n\telse\n\t\tnum = MSG_ReadByte ();\n\tif (num >= MAX_CLIENTS)\n\t\tHost_EndGame (\"CL_ParsePlayerinfo: bad num\");\n\n\tinfo = &cl.players[num];\n\n\toldstate = &cl.inframes[oldparsecountmod].playerstate[num];\n\tstate = &cl.inframes[parsecountmod].playerstate[num];\n\n\tif (cls.demoplayback == DPB_MVD)\n\t{\n#ifdef QUAKESTATS\n\t\tint i;\n\t\tconst char *viewmodel = NULL;\n\t\tstatic struct {\n\t\t\tconst char *vmdl;\n\t\t\tconst char *vwep;\n\t\t} vwep_mapping[] =\n\t\t{\n\t\t\t{\"progs/v_axe.mdl\",\t\"progs/w_axe.mdl\"},\n\t\t\t{\"progs/v_shot.mdl\",\t\"progs/w_shot.mdl\"},\n\t\t\t{\"progs/v_shot2.mdl\",\t\"progs/w_shot2.mdl\"},\n\t\t\t{\"progs/v_nail.mdl\",\t\"progs/w_nail.mdl\"},\n\t\t\t{\"progs/v_nail2.mdl\",\t\"progs/w_nail2.mdl\"},\n\t\t\t{\"progs/v_rock.mdl\",\t\"progs/w_rock.mdl\"},\n\t\t\t{\"progs/v_rock2.mdl\",\t\"progs/w_rock2.mdl\"},\n\t\t\t{\"progs/v_light.mdl\",\t\"progs/w_light.mdl\"},\n\t\t};\n#endif\n\t\tplayer_state_t\tdummy;\n\t\tif (!cl.parsecount || info->prevcount > cl.parsecount || cl.parsecount - info->prevcount >= UPDATE_BACKUP - 1)\n\t\t{\n\t\t\tmemset(&dummy, 0, sizeof(dummy));\n\t\t\toldstate = &dummy;\n\t\t}\n\t\telse\n\t\t{\n\t\t\toldstate = &cl.inframes[info->prevcount & UPDATE_MASK].playerstate[num];\n\t\t}\n\t\tmemcpy(state, oldstate, sizeof(player_state_t));\n\t\tinfo->prevcount = cl.parsecount;\n\n#ifdef QUAKESTATS\n\t\tif (cls.findtrack && info->stats[STAT_HEALTH] > 0)\n\t\t{\t//FIXME: is this still needed with the autotrack stuff?\n\t\t\tCam_Lock(&cl.playerview[0], num);\n\t\t\tcls.findtrack = false;\n\t\t}\n#endif\n\n\t\tflags = MSG_ReadShort ();\n\t\tstate->flags = MVD_TranslateFlags(flags);\n\n\t\tstate->messagenum = cl.parsecount;\n\t\tstate->command.msec = 0;\n\t\tstate->command.impulse = 0;\n\n#ifdef QUAKESTATS\n\t\ti = cl.players[num].stats[STAT_WEAPONMODELI];\n\t\tif (i>0&&i<MAX_PRECACHE_MODELS && cl.model_name_vwep[0])\n\t\t\tviewmodel = cl.model_name[i];\n\t\tif(viewmodel)\n\t\t{\n\t\t\tfor (i = 0; i < countof(vwep_mapping); i++)\n\t\t\t{\n\t\t\t\tif (!strcmp(viewmodel, vwep_mapping[i].vmdl))\n\t\t\t\t{\n\t\t\t\t\tviewmodel = vwep_mapping[i].vwep;\n\t\t\t\t\tfor (i = 1; i < countof(cl.model_name_vwep); i++)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (!cl.model_name_vwep[i])\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tif (!strcmp(viewmodel, cl.model_name_vwep[i]))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tstate->command.impulse = i;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n#endif\n\n\t\tstate->frame = MSG_ReadByte ();\n\n\t\tstate->state_time = parsecounttime;\n\n\t\tfor (i = 0; i < 3; i++)\n\t\t{\n\t\t\tif (flags & (DF_ORIGINX << i))\n\t\t\t{\n\t\t\t\tif (cls.ezprotocolextensions1 & EZPEXT1_FLOATENTCOORDS)\n\t\t\t\t\tstate->origin[i] = MSG_ReadCoordFloat ();\n\t\t\t\telse\n\t\t\t\t\tstate->origin[i] = MSG_ReadCoord ();\n\t\t\t}\n\t\t}\n\n\t\tVectorSubtract(state->origin, oldstate->origin, dist);\n\t\tVectorScale(dist, 1/(cl.inframes[parsecountmod].packet_entities.servertime - cl.inframes[oldparsecountmod].packet_entities.servertime), state->velocity);\n\t\tVectorCopy (state->origin, state->predorigin);\n\n\t\tfor (i = 0; i < 3; i++)\n\t\t{\n\t\t\tif (flags & (DF_ANGLEX << i))\n\t\t\t{\n\t\t\t\tstate->command.angles[i] = MSG_ReadShort();\n\t\t\t}\n\t\t\tstate->viewangles[i] = state->command.angles[i] * (360.0/65536);\n\t\t}\n\n\t\tif (flags & DF_MODEL)\n\t\t\tstate->modelindex = MSG_ReadByte ();\n\n\t\tif (flags & DF_SKINNUM)\n\t\t\tstate->skinnum = MSG_ReadByte ();\n\n\t\tif (flags & DF_EFFECTS)\n\t\t\tstate->effects = MSG_ReadByte ();\n\n\t\tif (flags & DF_WEAPONFRAME)\n\t\t\tstate->weaponframe = MSG_ReadByte ();\n\n\t\tVectorSet(state->szmins, -16, -16, -24);\n\t\tVectorSet(state->szmaxs, 16, 16, 32);\n\t\tstate->scale = 1;\n\t\tstate->alpha = 255;\n\t\tstate->fatness = 0;\n\n\t\tstate->colourmod[0] = 32;\n\t\tstate->colourmod[1] = 32;\n\t\tstate->colourmod[2] = 32;\n\n\t\tstate->gravitydir[0] = 0;\n\t\tstate->gravitydir[1] = 0;\n\t\tstate->gravitydir[2] = -1;\n\n\t\tstate->pm_type = PM_NORMAL;\n\n#ifdef QUAKESTATS\n\t\tTP_ParsePlayerInfo(oldstate, state, info);\n\n\t\t//can't CL_SetStatInt as we don't know if its actually us or not\n\t\tcl.players[num].stats[STAT_WEAPONFRAME] = state->weaponframe;\n\t\tcl.players[num].statsf[STAT_WEAPONFRAME] = state->weaponframe;\n\t\tfor (i = 0; i < cl.splitclients; i++)\n\t\t{\n\t\t\tplayerview_t *pv = &cl.playerview[i];\n\t\t\tif (pv->cam_spec_track == num)\n\t\t\t{\n\t\t\t\tpv->stats[STAT_WEAPONFRAME] = state->weaponframe;\n\t\t\t\tpv->statsf[STAT_WEAPONFRAME] = state->weaponframe;\n\t\t\t}\n\t\t}\n#endif\n\n\t\t//add a new splitscreen autotrack view if we can\n\t\tif (cl.splitclients < MAX_SPLITS && !cl.players[num].spectator)\n\t\t{\n\t\t\tif (cl.splitclients < cl_splitscreen.value+1)\n\t\t\t{\n\t\t\t\tfor (i = 0; i < cl.splitclients; i++)\n\t\t\t\t{\n\t\t\t\t\tplayerview_t *pv = &cl.playerview[i];\n\t\t\t\t\tif (pv->cam_state != CAM_FREECAM && pv->cam_spec_track == num)\n\t\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tif (i == cl.splitclients)\n\t\t\t\t{\n\t\t\t\t\tplayerview_t *pv = &cl.playerview[cl.splitclients++];\n\t\t\t\t\tCam_Lock(pv, num);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn;\n\t}\n\n\tflags = (unsigned short)MSG_ReadShort ();\n\n\tif (cls.fteprotocolextensions & (PEXT_HULLSIZE|PEXT_TRANS|PEXT_SCALE|PEXT_FATNESS))\n\t{\n\t\tif (flags & PF_EXTRA_PFS)\n\t\t\tflags |= MSG_ReadByte()<<16;\n\t}\n\telse\n\t\tflags = (flags & 0x3fff) | ((flags & 0xc000)<<8);\n\n\tstate->flags = flags;\n\n\tstate->messagenum = cl.parsecount;\n\tif (cls.ezprotocolextensions1 & EZPEXT1_FLOATENTCOORDS)\n\t{\n\t\torg[0] = MSG_ReadCoordFloat ();\n\t\torg[1] = MSG_ReadCoordFloat ();\n\t\torg[2] = MSG_ReadCoordFloat ();\n\t}\n\telse\n\t{\n\t\torg[0] = MSG_ReadCoord ();\n\t\torg[1] = MSG_ReadCoord ();\n\t\torg[2] = MSG_ReadCoord ();\n\t}\n\n\tVectorCopy(org, state->origin);\n\n\tnewf = MSG_ReadByte ();\n\tif (state->frame != newf)\n\t{\n//\t\tstate->lerpstarttime = realtime;\n\t\tstate->frame = newf;\n\t}\n\n\t// the other player's last move was likely some time\n\t// before the packet was sent out, so accurately track\n\t// the exact time it was valid at\n\tif (flags & PF_MSEC)\n\t{\n\t\textern cvar_t cl_demospeed;\n\t\tmsec = MSG_ReadByte ();\n\t\tif (cls.demoplayback)\n\t\t\tstate->state_time = parsecounttime - msec*0.001 * cl_demospeed.value;\n\t\telse\n\t\t\tstate->state_time = parsecounttime - msec*0.001;\n\t}\n\telse\n\t{\n\t\tmsec = 0;\n\t\tstate->state_time = parsecounttime;\n\t}\n\n\tif (flags & PF_COMMAND)\n\t{\n\t\tMSGQW_ReadDeltaUsercmd (&nullcmd, &state->command, cl.protocol_qw);\n\n\t\tstate->viewangles[0] = state->command.angles[0] * (360.0/65536);\n\t\tstate->viewangles[1] = state->command.angles[1] * (360.0/65536);\n\t\tstate->viewangles[2] = state->command.angles[2] * (360.0/65536);\n\n\t\tif (!(cls.z_ext & Z_EXT_VWEP))\n\t\t\tstate->command.impulse = 0;\n\t}\n\n\tfor (i=0 ; i<3 ; i++)\n\t{\n\t\tif (flags & (PF_VELOCITY1<<i) )\n\t\t\tstate->velocity[i] = MSG_ReadShort();\n\t\telse\n\t\t\tstate->velocity[i] = 0;\n\t}\n\tif (flags & PF_MODEL)\n\t\tstate->modelindex = MSG_ReadByte ();\n\telse\n\t\tstate->modelindex = cl_playerindex;\n\n\tif (flags & PF_SKINNUM)\n\t{\n\t\tstate->skinnum = MSG_ReadByte ();\n\t\tif (state->skinnum & (1<<7) && (flags & PF_MODEL))\n\t\t{\n\t\t\tstate->modelindex+=256;\n\t\t\tstate->skinnum -= (1<<7);\n\t\t}\n\t}\n\telse\n\t\tstate->skinnum = 0;\n\n\tif (flags & PF_EFFECTS)\n\t\tstate->effects = MSG_ReadByte ();\n\telse\n\t\tstate->effects = 0;\n\n\tif (flags & PF_WEAPONFRAME)\n\t\tstate->weaponframe = MSG_ReadByte ();\n\telse\n\t\tstate->weaponframe = 0;\n\n\tVectorSet(state->szmins, -16, -16, -24);\n\tVectorSet(state->szmaxs, 16, 16, 32);\n\tstate->scale = 1;\n\tstate->alpha = 255;\n\tstate->fatness = 0;\n\n\tstate->gravitydir[0] = 0;\n\tstate->gravitydir[1] = 0;\n\tstate->gravitydir[2] = -1;\n\n#ifdef PEXT_SCALE\n\tif ((flags & PF_SCALE) && (cls.fteprotocolextensions & PEXT_SCALE))\n\t\tstate->scale = MSG_ReadByte()/50.0;\n#endif\n#ifdef PEXT_TRANS\n\tif ((flags & PF_TRANS) && (cls.fteprotocolextensions & PEXT_TRANS))\n\t\tstate->alpha = MSG_ReadByte();\n#endif\n#ifdef PEXT_FATNESS\n\tif ((flags & PF_FATNESS) && (cls.fteprotocolextensions & PEXT_FATNESS))\n\t\tstate->fatness = MSG_ReadChar();\n#endif\n#ifdef PEXT_HULLSIZE\n\tif ((cls.fteprotocolextensions & PEXT_HULLSIZE) && (flags & PF_HULLSIZE_Z))\n\t{\n\t\tint num;\n\t\tnum = MSG_ReadByte();\n\n\t\tif (!cl.worldmodel || cl.worldmodel->fromgame != fg_quake)\n\t\t{\n\t\t\tVectorScale(state->szmins, num/56.0f, state->szmins);\n\t\t\tVectorScale(state->szmaxs, num/56.0f, state->szmaxs);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tVectorCopy(cl.worldmodel->hulls[num&(MAX_MAP_HULLSM-1)].clip_mins, state->szmins);\n\t\t\tVectorCopy(cl.worldmodel->hulls[num&(MAX_MAP_HULLSM-1)].clip_maxs, state->szmaxs);\n\t\t}\n\t\tif (num & 128)\n\t\t{\t//this hack is for hexen2.\n\t\t\tstate->szmaxs[2] -= state->szmins[2];\n\t\t\tstate->szmins[2] = 0;\n\t\t}\n\t}\n\t//should be passed to player move func.\n#endif\n\tif (cls.z_ext & Z_EXT_PF_ONGROUND)\n\t\tstate->onground = !!(flags & PF_ONGROUND);\n\telse\n\t\tstate->onground = false;\n\n\tif ((cls.fteprotocolextensions & PEXT_COLOURMOD) && (flags & PF_COLOURMOD))\n\t{\n\t\tstate->colourmod[0] = MSG_ReadByte();\n\t\tstate->colourmod[1] = MSG_ReadByte();\n\t\tstate->colourmod[2] = MSG_ReadByte();\n\t}\n\telse\n\t{\n\t\tstate->colourmod[0] = 32;\n\t\tstate->colourmod[1] = 32;\n\t\tstate->colourmod[2] = 32;\n\t}\n\n\t//if we have no solidity info, guess.\n\tif (!(cls.z_ext & Z_EXT_PF_SOLID))\n\t{\n\t\tif (cl.players[num].spectator || state->flags & PF_DEAD)\n\t\t\tstate->flags &= ~PF_SOLID;\n\t\telse\n\t\t\tstate->flags |= PF_SOLID;\n\t}\n\n\tif (cls.z_ext & Z_EXT_PM_TYPE)\n\t{\n\t\tint pm_code;\n\n\t\tpm_code = (flags&PF_PMC_MASK) >> PF_PMC_SHIFT;\n\t\tif (pm_code == PMC_NORMAL || pm_code == PMC_NORMAL_JUMP_HELD)\n\t\t{\n\t\t\tif (flags & PF_DEAD)\n\t\t\t\tstate->pm_type = PM_DEAD;\n\t\t\telse\n\t\t\t{\n\t\t\t\tstate->pm_type = PM_NORMAL;\n\t\t\t\tstate->jump_held = (pm_code == PMC_NORMAL_JUMP_HELD);\n\t\t\t}\n\t\t}\n\t\telse if (pm_code == PMC_OLD_SPECTATOR)\n\t\t\tstate->pm_type = PM_OLD_SPECTATOR;\n\t\telse\n\t\t{\n\t\t\tif (cls.z_ext & Z_EXT_PM_TYPE_NEW)\n\t\t\t{\n\t\t\t\tif (pm_code == PMC_SPECTATOR)\n\t\t\t\t\tstate->pm_type = PM_SPECTATOR;\n\t\t\t\telse if (pm_code == PMC_FLY)\n\t\t\t\t\tstate->pm_type = PM_FLY;\n\t\t\t\telse if (pm_code == PMC_NONE)\n\t\t\t\t\tstate->pm_type = PM_NONE;\n\t\t\t\telse if (pm_code == PMC_FREEZE)\n\t\t\t\t\tstate->pm_type = PM_FREEZE;\n\t\t\t\telse if (pm_code == PMC_WALLWALK)\n\t\t\t\t\tstate->pm_type = PM_WALLWALK;\n\t\t\t\telse {\n\t\t\t\t\t// future extension?\n\t\t\t\t\tgoto guess_pm_type;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t// future extension?\n\t\t\t\tgoto guess_pm_type;\n\t\t\t}\n\t\t}\n\t}\n\telse\n\t{\nguess_pm_type:\n\t\tif (cl.players[num].spectator)\n\t\t\tstate->pm_type = PM_OLD_SPECTATOR;\n\t\telse if (flags & PF_DEAD)\n\t\t\tstate->pm_type = PM_DEAD;\n\t\telse\n\t\t\tstate->pm_type = PM_NORMAL;\n\t}\n\n#ifdef QUAKESTATS\n\tTP_ParsePlayerInfo(oldstate, state, info);\n\n\t//can't CL_SetStatInt as we don't know if its actually us or not\n\tfor (i = 0; i < cl.splitclients; i++)\n\t{\n\t\tplayerview_t *pv = &cl.playerview[i];\n\t\tif ((pv->spectator?pv->cam_spec_track:pv->playernum) == num)\n\t\t{\n\t\t\tpv->stats[STAT_WEAPONFRAME] = state->weaponframe;\n\t\t\tpv->statsf[STAT_WEAPONFRAME] = state->weaponframe;\n\t\t}\n\t}\n#endif\n\n\tif (cl.worldmodel && cl.do_lerp_players && cl_predict_players.ival)\n\t{\n\t\tplayer_state_t exact;\n\t\tmsec -= 1000 * (cls.latency*cl_predict_players_latency.value-cl_predict_players_nudge.value);\n//\t\tmsec = 1000*((realtime - cls.latency + 0.02) - state->state_time);\n\t\t// predict players movement\n\t\tstate->command.msec = bound(0, msec, 255);\n\n\t\t//FIXME: flag these and do the pred elsewhere.\n\t\tCL_SetSolidEntities();\n\t\tCL_SetSolidPlayers();\n\t\tCL_PredictUsercmd (0, num+1, state, &exact, &state->command);\t//uses player 0's maxspeed/grav...\n\t\tVectorCopy (exact.origin, state->predorigin);\n\t}\n\telse\n\t\tVectorCopy (state->origin, state->predorigin);\n}\n\n/*\nvoid CL_ParseClientPersist(void)\n{\n\tplayer_info_t\t*info;\n\tint flags;\n\tflags = MSG_ReadShort();\n\tinfo = &cl.players[lastplayerinfo];\n\tif (flags & 1)\n\t\tinfo->vweapindex = MSG_ReadShort();\n}\n*/\n\n/*\n================\nCL_AddFlagModels\n\nCalled when the CTF flags are set\n================\n*/\nvoid CL_AddFlagModels (entity_t *ent, int team)\n{\n\tint\t\ti;\n\tfloat\tf;\n\tvec3_t\tv_forward, v_right, v_up;\n\tentity_t\t*newent;\n\tvec3_t\tangles;\n\tfloat offs = 0;\n\n\tif (cl_flagindex == -1)\n\t\treturn;\n\n\tfor (i = 0; i < FRAME_BLENDS; i++)\n\t{\n\t\tif (!ent->framestate.g[FS_REG].lerpweight[i])\n\t\t\tcontinue;\n\t\tf = 14;\n\t\tif (ent->framestate.g[FS_REG].frame[i] >= 29 && ent->framestate.g[FS_REG].frame[i] <= 40) {\n\t\t\tif (ent->framestate.g[FS_REG].frame[i] >= 29 && ent->framestate.g[FS_REG].frame[i] <= 34) { //axpain\n\t\t\t\tif      (ent->framestate.g[FS_REG].frame[i] == 29) f = f + 2;\n\t\t\t\telse if (ent->framestate.g[FS_REG].frame[i] == 30) f = f + 8;\n\t\t\t\telse if (ent->framestate.g[FS_REG].frame[i] == 31) f = f + 12;\n\t\t\t\telse if (ent->framestate.g[FS_REG].frame[i] == 32) f = f + 11;\n\t\t\t\telse if (ent->framestate.g[FS_REG].frame[i] == 33) f = f + 10;\n\t\t\t\telse if (ent->framestate.g[FS_REG].frame[i] == 34) f = f + 4;\n\t\t\t} else if (ent->framestate.g[FS_REG].frame[i] >= 35 && ent->framestate.g[FS_REG].frame[i] <= 40) { // pain\n\t\t\t\tif      (ent->framestate.g[FS_REG].frame[i] == 35) f = f + 2;\n\t\t\t\telse if (ent->framestate.g[FS_REG].frame[i] == 36) f = f + 10;\n\t\t\t\telse if (ent->framestate.g[FS_REG].frame[i] == 37) f = f + 10;\n\t\t\t\telse if (ent->framestate.g[FS_REG].frame[i] == 38) f = f + 8;\n\t\t\t\telse if (ent->framestate.g[FS_REG].frame[i] == 39) f = f + 4;\n\t\t\t\telse if (ent->framestate.g[FS_REG].frame[i] == 40) f = f + 2;\n\t\t\t}\n\t\t} else if (ent->framestate.g[FS_REG].frame[i] >= 103 && ent->framestate.g[FS_REG].frame[i] <= 118) {\n\t\t\tif      (ent->framestate.g[FS_REG].frame[i] >= 103 && ent->framestate.g[FS_REG].frame[i] <= 104) f = f + 6;  //nailattack\n\t\t\telse if (ent->framestate.g[FS_REG].frame[i] >= 105 && ent->framestate.g[FS_REG].frame[i] <= 106) f = f + 6;  //light\n\t\t\telse if (ent->framestate.g[FS_REG].frame[i] >= 107 && ent->framestate.g[FS_REG].frame[i] <= 112) f = f + 7;  //rocketattack\n\t\t\telse if (ent->framestate.g[FS_REG].frame[i] >= 112 && ent->framestate.g[FS_REG].frame[i] <= 118) f = f + 7;  //shotattack\n\t\t}\n\n\t\toffs += f * ent->framestate.g[FS_REG].lerpweight[i];\n\t}\n\n\tnewent = CL_NewTempEntity ();\n\tnewent->model = cl.model_precache[cl_flagindex];\n\tnewent->skinnum = team;\n\tnewent->keynum = ent->keynum;\n\tnewent->flags |= ent->flags;\n\n\tAngleVectors (ent->angles, v_forward, v_right, v_up);\n\tv_forward[2] = -v_forward[2]; // reverse z component\n\tfor (i=0 ; i<3 ; i++)\n\t\tnewent->origin[i] = ent->origin[i] - offs*v_forward[i] + 22*v_right[i];\n\tnewent->origin[2] -= 16;\n\n\tVectorCopy (ent->angles, newent->angles);\n\tnewent->angles[2] -= 45;\n\n\tVectorCopy(newent->angles, angles);\n\tAngleVectorsMesh(angles, newent->axis[0], newent->axis[1], newent->axis[2]);\n\tVectorInverse(newent->axis[1]);\n}\n\nvoid CL_AddVWeapModel(entity_t *player, model_t *model)\n{\n\tentity_t\t*newent;\n//\tvec3_t\tangles;\n\tif (!model)\n\t\treturn;\n\tnewent = CL_NewTempEntity ();\n\n\tnewent->keynum = player->keynum;\n\tnewent->flags |= player->flags;\n\n\tVectorCopy(player->origin, newent->origin);\n\tVectorCopy(player->angles, newent->angles);\n\tnewent->skinnum = player->skinnum;\n\tnewent->model = model;\n\tnewent->framestate = player->framestate;\n\n\tAngleVectors(newent->angles, newent->axis[0], newent->axis[1], newent->axis[2]);\n\tVectorInverse(newent->axis[1]);\n}\n\n/*\n=============\nCL_LinkPlayers\n\nCreate visible entities in the correct position\nfor all current players\n=============\n*/\nvec3_t nametagorg[MAX_CLIENTS];\nqboolean nametagseen[MAX_CLIENTS];\nvoid CL_LinkPlayers (void)\n{\n\tint pnum;\n\tint\t\t\t\tj;\n\tplayer_info_t\t*info;\n\tplayer_state_t\t*state;\n\tplayer_state_t\texact;\n\tdouble\t\t\tplayertime;\n\tentity_t\t\t*ent;\n\tfloat\t\t\tmsec;\n\tinframe_t\t\t*frame;\n\tint\t\t\t\toldphysent;\n\tvec3_t\t\t\tangles;\n\tqboolean\t\tpredictplayers;\n\tmodel_t\t\t\t*model;\n\tstatic int\t\tflickertime;\n\tstatic int\t\tflicker;\n\tfloat\t\t\tpredictmsmult = 1000*cl_predict_players_frac.value;\n#ifdef HAVE_LEGACY\n\tint\t\t\t\tmodelindex2;\n#endif\n\textern cvar_t\tcl_demospeed;\n\tint displayseq;\n\n\tif (!cl.worldmodel || cl.worldmodel->loadstate != MLS_LOADED)\n\t\treturn;\n\n\tif (cl.paused)\n\t\tpredictmsmult = 0;\n\tif (cls.demoplayback)\n\t\tpredictmsmult *= cl_demospeed.value;\n\n\tplayertime = realtime - cls.latency*cl_predict_players_latency.value + cl_predict_players_nudge.value;\n\tif (playertime > realtime)\n\t\tplayertime = realtime;\n\n\tif (cl.demonudge < 0)\n\t\tdisplayseq = cl.lerpentssequence;\n\telse\n\t\tdisplayseq = cl.validsequence;\n\tframe = &cl.inframes[displayseq&UPDATE_MASK];\n\n\tpredictplayers = cl_predict_players.ival;\n\tif (cls.demoplayback == DPB_MVD)\n\t\tpredictplayers = false;\n\n\tfor (j=0, info=cl.players, state=frame->playerstate ; j < cl.allocated_client_slots\n\t\t; j++, info++, state++)\n\t{\n\t\tnametagseen[j] = false;\n\n\t\tif (state->messagenum != displayseq)\n\t\t{\n#ifdef CSQC_DAT\n\t\t\tCSQC_DeltaPlayer(j, NULL);\n#endif\n\t\t\tcontinue;\t// not present this frame\n\t\t}\n\n\t\tCL_UpdateNetFrameLerpState(false, state->frame, 0, 0, &cl.lerpplayers[j], 0);\n\t\tcl.lerpplayers[j].sequence = cl.lerpentssequence;\n\n#ifdef CSQC_DAT\n\t\tif (CSQC_DeltaPlayer(j, state))\n\t\t\tcontinue;\n#endif\n\n\t\tif (info->spectator || state->modelindex >= countof(cl.model_precache))\n\t\t\tcontinue;\n\n\t\t//the extra modelindex check is to stop lame mods from using vweps with rings\n#ifdef HAVE_LEGACY\n\t\tif (state->command.impulse && cl.model_precache_vwep[0] && cl.model_precache_vwep[0]->type != mod_dummy && state->modelindex == cl_playerindex)\n\t\t{\n\t\t\tmodel = cl.model_precache_vwep[0];\n\t\t\tmodelindex2 = state->command.impulse;\n\t\t}\n\t\telse\n#endif\n\t\t{\n\t\t\tmodel = cl.model_precache[state->modelindex];\n#ifdef HAVE_LEGACY\n\t\t\tmodelindex2 = 0;\n#endif\n\t\t}\n\n\t\t// spawn light flashes, even ones coming from invisible objects\n\t\tif (r_powerupglow.value && !(r_powerupglow.value == 2 && j == cl.playerview[0].playernum)\n\t\t\t&& (state->effects & (EF_BLUE|EF_RED|EF_GREEN|EF_BRIGHTLIGHT|EF_DIMLIGHT)))\n\t\t{\n\t\t\tvec3_t colour;\n\t\t\tfloat radius;\n\t\t\tcolour[0] = 0;\n\t\t\tcolour[1] = 0;\n\t\t\tcolour[2] = 0;\n\t\t\tradius = 0;\n\n\t\t\tif (state->effects & EF_BRIGHTLIGHT)\n\t\t\t{\n\t\t\t\tradius = max(radius,r_brightlight_colour.vec4[3]);\n\t\t\t\tif (!(state->effects & (EF_RED|EF_GREEN|EF_BLUE)))\n\t\t\t\t\tVectorAdd(colour, r_brightlight_colour.vec4, colour);\n\t\t\t}\n\t\t\tif (state->effects & EF_DIMLIGHT)\n\t\t\t{\n\t\t\t\tradius = max(radius,r_dimlight_colour.vec4[3]);\n\t\t\t\tif (!(state->effects & (EF_RED|EF_GREEN|EF_BLUE)))\n\t\t\t\t\tVectorAdd(colour, r_dimlight_colour.vec4, colour);\n\t\t\t}\n\t\t\tif (state->effects & EF_BLUE)\n\t\t\t{\n\t\t\t\tradius = max(radius,r_bluelight_colour.vec4[3]);\n\t\t\t\tVectorAdd(colour, r_bluelight_colour.vec4, colour);\n\t\t\t}\n\t\t\tif (state->effects & EF_RED)\n\t\t\t{\n\t\t\t\tradius = max(radius,r_redlight_colour.vec4[3]);\n\t\t\t\tVectorAdd(colour, r_redlight_colour.vec4, colour);\n\t\t\t}\n\t\t\tif (state->effects & EF_GREEN)\n\t\t\t{\n\t\t\t\tradius = max(radius,r_greenlight_colour.vec4[3]);\n\t\t\t\tVectorAdd(colour, r_greenlight_colour.vec4, colour);\n\t\t\t}\n\n\t\t\tif (radius)\n\t\t\t{\n\t\t\t\tvec3_t org;\n\t\t\t\tVectorCopy(state->origin, org);\n\t\t\t\t//make the light appear at the predicted position rather than anywhere else.\n\t\t\t\tfor (pnum = 0; pnum < cl.splitclients; pnum++)\n\t\t\t\t\tif (cl.playerview[pnum].playernum == j)\n\t\t\t\t\t\tVectorCopy(cl.playerview[pnum].simorg, org);\n\t\t\t\tif (model)\n\t\t\t\t{\n\t\t\t\t\torg[2] += model->mins[2];\n\t\t\t\t\torg[2] += 32;\n\t\t\t\t}\n\t\t\t\tif (r_lightflicker.value)\n\t\t\t\t{\n\t\t\t\t\tpnum = realtime*20;\n\t\t\t\t\tif (flickertime != pnum)\n\t\t\t\t\t{\n\t\t\t\t\t\tflickertime = pnum;\n\t\t\t\t\t\tflicker = rand();\n\t\t\t\t\t}\n\t\t\t\t\tradius += (flicker+j)&31;\n\t\t\t\t}\n\t\t\t\tCL_NewDlight(j+1, org, radius, 0.1, colour[0], colour[1], colour[2])->flags &= ~LFLAG_FLASHBLEND;\n\t\t\t}\n\t\t}\n\n\t\tif (state->modelindex < 1)\n\t\t\tcontinue;\n\n\t\tif (CL_FilterModelindex(state->modelindex, state->frame))\n\t\t\tcontinue;\n/*\n\t\tif (!Cam_DrawPlayer(j))\n\t\t\tcontinue;\n*/\n\t\t// grab an entity to fill in\n\t\tif (cl_numvisedicts == cl_maxvisedicts)\n\t\t\tbreak;\t\t// object list is full\n\t\tent = &cl_visedicts[cl_numvisedicts];\n\t\tcl_numvisedicts++;\n\t\tmemset(ent, 0, sizeof(*ent));\n\t\tent->keynum = j+1;\n\t\tent->model = model;\n\n\t\tent->skinnum = state->skinnum;\n\n\t\tCL_LerpNetFrameState(&ent->framestate,\t&cl.lerpplayers[j]);\n\n\t\t// set colormap\n\t\tent->playerindex = j;\n\t\tent->topcolour\t  = info->dtopcolor;\n\t\tent->bottomcolour = info->dbottomcolor;\n#ifdef HEXEN2\n\t\tent->h2playerclass = info->h2playerclass;\n#endif\n\n#ifdef PEXT_SCALE\n\t\tent->scale = state->scale;\n#endif\n\t\tent->glowmod[0] = ent->glowmod[1] = ent->glowmod[2] = 1;\n\t\tent->shaderRGBAf[0] = state->colourmod[0]/32.0f;\n\t\tent->shaderRGBAf[1] = state->colourmod[1]/32.0f;\n\t\tent->shaderRGBAf[2] = state->colourmod[2]/32.0f;\n\t\tent->shaderRGBAf[3] = state->alpha/255.0f;\n\t\tif (state->alpha != 255)\n\t\t\tent->flags |= RF_TRANSLUCENT;\n\n\t\tent->fatness = state->fatness;\n\t\t//\n\t\t// angles\n\t\t//\n\t\tangles[PITCH] = -state->viewangles[PITCH]/3;\n\t\tangles[YAW] = state->viewangles[YAW];\n\t\tangles[ROLL] = 0;\n\t\tangles[ROLL] = V_CalcRoll (angles, state->velocity)*4;\n\n\t\tif (j+1 == r_refdef.playerview->viewentity || (r_refdef.playerview->cam_state == CAM_EYECAM && r_refdef.playerview->cam_spec_track == j))\n\t\t\tent->flags |= RF_EXTERNALMODEL;\n\t\t// the player object gets added with flags | 2\n\t\tfor (pnum = 0; pnum < cl.splitclients; pnum++)\n\t\t{\n\t\t\tplayerview_t *pv = &cl.playerview[pnum];\n\t\t\tif (j == pv->playernum)\n\t\t\t{\n/*\t\t\t\tif (cl.spectator)\n\t\t\t\t{\n\t\t\t\t\tcl_numvisedicts--;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n*/\t\t\t\tangles[0] = -1*pv->viewangles[0] / 3;\n\t\t\t\tangles[1] = pv->viewangles[1];\n\t\t\t\tangles[2] = pv->viewangles[2];\n\t\t\t\tent->origin[0] = pv->simorg[0];\n\t\t\t\tent->origin[1] = pv->simorg[1];\n\t\t\t\tent->origin[2] = pv->simorg[2]+pv->crouch;\n\t\t\t}\n\t\t}\n\n\t\tif (model && model->type == mod_alias)\n\t\t{\n\t\t\tangles[0]*=r_meshpitch.value;\t//carmack screwed up when he added alias models - they pitch the wrong way.\n\t\t\tangles[2]*=r_meshroll.value;\t//hexen2 screwed it up even more - they roll the wrong way.\n\t\t}\n\t\tVectorCopy(angles, ent->angles);\n\t\tAngleVectors(angles, ent->axis[0], ent->axis[1], ent->axis[2]);\n\t\tVectorInverse(ent->axis[1]);\n\n\t\t// only predict half the move to minimize overruns\n\t\tmsec = predictmsmult*(playertime - state->state_time);\n\n\t\tif (pnum < cl.splitclients)\n\t\t{\t//this is a local player\n\t\t}\n\t\telse if (cl.do_lerp_players)\n\t\t{\n\t\t\tlerpents_t *le = &cl.lerpplayers[j];\n\t\t\tVectorCopy (le->origin, ent->origin);\n\n\t\t\tVectorCopy(le->angles, ent->angles);\n\t\t\tent->angles[0] /= 3;\n\t\t\tAngleVectors(ent->angles, ent->axis[0], ent->axis[1], ent->axis[2]);\n\t\t\tVectorInverse(ent->axis[1]);\n\t\t}\n\t\telse if (msec <= 0 || (!predictplayers))\n\t\t{\n\t\t\tVectorCopy (state->origin, ent->origin);\n//Con_DPrintf (\"nopredict\\n\");\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// predict players movement\n\t\t\tif (msec > 250)\n\t\t\t\tmsec = 250;\n\t\t\tstate->command.msec = msec;\n//Con_DPrintf (\"predict: %i\\n\", msec);\n\n\t\t\toldphysent = pmove.numphysent;\n\t\t\tCL_SetSolidPlayers ();\n\t\t\tCL_PredictUsercmd (0, j+1, state, &exact, &state->command);\t//uses player 0's maxspeed/grav...\n\t\t\tpmove.numphysent = oldphysent;\n\t\t\tVectorCopy (exact.origin, ent->origin);\n\t\t}\n\n\t\tVectorCopy(ent->origin, nametagorg[j]);\n\t\tnametagseen[j] = true;\n\n\t\tif (state->effects & QWEF_FLAG1)\n\t\t\tCL_AddFlagModels (ent, 0);\n\t\telse if (state->effects & QWEF_FLAG2)\n\t\t\tCL_AddFlagModels (ent, 1);\n#ifdef HAVE_LEGACY\n\t\tif (modelindex2)\n\t\t\tCL_AddVWeapModel (ent, cl.model_precache_vwep[modelindex2]);\n#endif\n\n\t\tCLQ1_AddShadow(ent);\n\t\tCLQ1_AddPowerupShell(ent, false, state->effects);\n\n\t\tif ((r_showbboxes.ival & 3) == 3)\n\t\t{\n\t\t\tvec3_t min, max;\n\t\t\tshader_t *s = R_RegisterShader(\"bboxshader\", SUF_NONE, NULL);\n\t\t\tif (s)\n\t\t\t{\n\t\t\t\tVectorAdd(state->origin, pmove.player_mins, min);\n\t\t\t\tVectorAdd(state->origin, pmove.player_maxs, max);\n\t\t\t\tCLQ1_AddOrientedCube(s, min, max, NULL, 0.1, 0, 0, 1);\n\n\t\t\t\tVectorAdd(ent->origin, pmove.player_mins, min);\n\t\t\t\tVectorAdd(ent->origin, pmove.player_maxs, max);\n\t\t\t\tCLQ1_AddOrientedCube(s, min, max, NULL, 0, 0, 0.1, 1);\n\t\t\t}\n\t\t}\n\n\n\t\tif (r_torch.ival)\n\t\t{\n\t\t\tdlight_t *dl;\n\t\t\tdl = CL_NewDlight(j+1, ent->origin, 300, r_torch.ival, 0.5, 0.5, 0.2);\n\t\t\tdl->flags |= LFLAG_SHADOWMAP|LFLAG_FLASHBLEND;\n\t\t\tdl->fov = 60;\n\t\t\tangles[0] *= 3;\n\t\t\tangles[1] += sin(realtime)*8;\n\t\t\tangles[0] += cos(realtime*1.13)*5;\n\t\t\tAngleVectors(angles, dl->axis[0], dl->axis[1], dl->axis[2]);\n\t\t}\n\t}\n}\n\nvoid CL_LinkViewModel(void)\n{\n#ifdef QUAKESTATS\n\textern cvar_t r_viewpreselgun;\n\tentity_t\tent;\n\n\tunsigned int plnum;\n\tunsigned int playereffects;\n\tfloat alpha;\n\tplayerview_t *pv = r_refdef.playerview;\n\tconst char *preselectedmodelname;\n\n\textern cvar_t cl_gunx, cl_guny, cl_gunz;\n\textern cvar_t cl_gunanglex, cl_gunangley, cl_gunanglez;\n\n\tif (r_drawviewmodel.value <= 0 || !Cam_DrawViewModel(r_refdef.playerview))\n\t\treturn;\n\n#ifdef Q2CLIENT\n\tif (cls.protocol == CP_QUAKE2)\n\t{\n\t\t//generate root matrix..\n\t\tVectorCopy(pv->simorg, r_refdef.weaponmatrix[3]);\n\t\tAngleVectors(pv->simangles, r_refdef.weaponmatrix[0], r_refdef.weaponmatrix[1], r_refdef.weaponmatrix[2]);\n\t\tVectorInverse(r_refdef.weaponmatrix[1]);\n\t\tmemcpy(r_refdef.weaponmatrix_bob, r_refdef.weaponmatrix, sizeof(r_refdef.weaponmatrix_bob));\n\n\t\tV_ClearEntity(&ent);\n\t\tent.model = pv->vm.oldmodel;\n\n\t\tent.framestate.g[FS_REG].frame[0] = pv->vm.prevframe;\n\t\tent.framestate.g[FS_REG].frame[1] = pv->vm.oldframe;\n\t\tent.framestate.g[FS_REG].frametime[0] = cl.time-pv->vm.lerptime;\n\t\tent.framestate.g[FS_REG].frametime[1] = cl.time-pv->vm.oldlerptime;\n\t\tent.framestate.g[FS_REG].lerpweight[0] = (cl.time - pv->vm.lerptime)*10;\n\t\tent.framestate.g[FS_REG].lerpweight[1] = 1 - ent.framestate.g[FS_REG].lerpweight[0];\n\n\t\tent.flags |= RF_WEAPONMODEL|RF_DEPTHHACK|RF_NOSHADOW;\n\t\tif (pv->handedness == 1)\n\t\t\tent.flags |= RF_XFLIP;\n\t\telse if (pv->handedness == 2)\n\t\t\treturn;\n\n\t\tent.shaderRGBAf[0] = ent.shaderRGBAf[1] = ent.shaderRGBAf[2] = 1;\n\t\tent.shaderRGBAf[3] = bound(0, r_drawviewmodel.value, 1);\n\n\t\tV_AddEntity (&ent);\n\t\treturn;\n\t}\n#endif\n\n\tif (!r_drawentities.ival)\n\t\treturn;\n\n\tif ((r_refdef.playerview->stats[STAT_ITEMS] & IT_INVISIBILITY) && r_drawviewmodelinvis.value <= 0)\n\t\treturn;\n\n\tif (r_refdef.playerview->stats[STAT_HEALTH] <= 0)\n\t\treturn;\n\n\tif (cl.intermissionmode != IM_NONE)\n\t\treturn;\n\n\tif (pv->stats[STAT_WEAPONMODELI] <= 0 || pv->stats[STAT_WEAPONMODELI] >= MAX_PRECACHE_MODELS)\n\t\treturn;\n\n\tif (r_drawviewmodel.value > 0 && r_drawviewmodel.value < 1)\n\t\talpha = r_drawviewmodel.value;\n\telse\n\t\talpha = 1;\n\n\tif ((pv->stats[STAT_ITEMS] & IT_INVISIBILITY)\n\t\t&& r_drawviewmodelinvis.value > 0\n\t\t&& r_drawviewmodelinvis.value < 1)\n\t\talpha *= r_drawviewmodelinvis.value;\n\n\t//FIXME: scale alpha by the player's alpha too\n\n\tif (alpha <= 0)\n\t\treturn;\n\n\tV_ClearEntity(&ent);\n\n#ifdef PEXT_SCALE\n\tent.scale = 1;\n#endif\n\n\tent.origin[0] = cl_gunz.value;\n\tent.origin[1] = -cl_gunx.value;\n\tent.origin[2] = -cl_guny.value;\n\n\tent.angles[0] = cl_gunanglex.value;\n\tent.angles[1] = cl_gunangley.value;\n\tent.angles[2] = cl_gunanglez.value;\n\n\tent.glowmod[0] = ent.glowmod[1] = ent.glowmod[2] = 1;\n\tent.shaderRGBAf[0] = ent.shaderRGBAf[1] = ent.shaderRGBAf[2] = 1;\n\tent.shaderRGBAf[3] = alpha;\n\tif (alpha != 1)\n\t{\n\t\tent.flags |= RF_TRANSLUCENT;\n\t}\n\n\tpreselectedmodelname = r_viewpreselgun.ival?IN_GetPreselectedViewmodelName(pv-cl.playerview):NULL;\n\tif (preselectedmodelname)\n\t\tent.model = Mod_ForName(preselectedmodelname, MLV_SILENT);\n\telse\n\t\tent.model = NULL;\n\tif (!ent.model)\n\tent.model = cl.model_precache[pv->stats[STAT_WEAPONMODELI]];\n\tif (!ent.model)\n\t{\n\t\tpv->vm.oldmodel = NULL;\n\t\treturn;\n\t}\n\n#ifdef HLCLIENT\n\tif (!CLHL_AnimateViewEntity(&ent))\n#endif\n\t{\n\t\t//if the model changed, reset everything.\n\t\tif (ent.model != pv->vm.oldmodel)\n\t\t{\n\t\t\tpv->vm.oldmodel = ent.model;\n\t\t\tpv->vm.oldframe = pv->vm.prevframe = pv->stats[STAT_WEAPONFRAME];\n\t\t\tpv->vm.oldlerptime = pv->vm.lerptime = cl.time;\n\t\t\tpv->vm.frameduration = 0.1;\n\t\t}\n\t\t//if the frame changed, update the oldframe to lerp into the new frame\n\t\telse if (pv->stats[STAT_WEAPONFRAME] != pv->vm.prevframe)\n\t\t{\n\t\t\tpv->vm.oldframe = pv->vm.prevframe;\n\t\t\tpv->vm.prevframe = pv->stats[STAT_WEAPONFRAME];\n\t\t\tpv->vm.oldlerptime = pv->vm.lerptime;\n\n\t\t\tpv->vm.frameduration = (cl.time - pv->vm.lerptime);\n\t\t\tif (pv->vm.frameduration < 0.01)//no faster than 100 times a second... to avoid divide by zero\n\t\t\t\tpv->vm.frameduration = 0.01;\n\t\t\tif (pv->vm.frameduration > 0.2)\t//no slower than 5 times a second\n\t\t\t\tpv->vm.frameduration = 0.2;\n\t\t\tpv->vm.lerptime = cl.time;\n\t\t}\n\t\t//work out the blend fraction\n\t\tent.framestate.g[FS_REG].frame[0] = pv->vm.prevframe;\n\t\tent.framestate.g[FS_REG].frame[1] = pv->vm.oldframe;\n\t\tent.framestate.g[FS_REG].frametime[0] = cl.time - pv->vm.lerptime;\n\t\tent.framestate.g[FS_REG].frametime[1] = cl.time - pv->vm.oldlerptime;\n\t\tent.framestate.g[FS_REG].lerpweight[0] = (cl.time-pv->vm.lerptime)/pv->vm.frameduration;\n\t\tent.framestate.g[FS_REG].lerpweight[0] = bound(0, ent.framestate.g[FS_REG].lerpweight[0], 1);\n\t\tent.framestate.g[FS_REG].lerpweight[1] = 1-ent.framestate.g[FS_REG].lerpweight[0];\n\t}\n\n\tent.flags |= RF_WEAPONMODEL|RF_DEPTHHACK|RF_NOSHADOW;\n\n\tplnum = -1;\n\tif (pv->spectator)\n\t\tplnum = Cam_TrackNum(pv);\n\tif (plnum == -1)\n\t\tplnum = r_refdef.playerview->playernum;\n\tplayereffects = 0;\n\tif (r_refdef.playerview->nolocalplayer && plnum < cl.maxlerpents)\n\t{\n\t\tif (plnum+1 < cl.maxlerpents)\n\t\t{\n\t\t\tlerpents_t *le = &cl.lerpents[plnum+1];\n\t\t\tif (le->entstate)\n\t\t\t{\n\t\t\t\tplayereffects = le->entstate->effects;\n#ifdef HEXEN2\n\t\t\t\tif (!le->entstate->modelindex || (le->entstate->hexen2flags & DRF_TRANSLUCENT))\n\t\t\t\t{\t//urgh.\n\t\t\t\t\tent.shaderRGBAf[3] *= .5;\n\t\t\t\t\tent.flags |= RF_TRANSLUCENT;\n\t\t\t\t}\n#endif\n\t\t\t}\n\t\t}\n\t}\n\telse if (plnum < cl.allocated_client_slots)\n\t\tplayereffects = cl.inframes[parsecountmod].playerstate[plnum].effects;\n\n\tif (playereffects & DPEF_NOGUNBOB)\n\t\tent.flags |= RF_WEAPONMODELNOBOB;\n\n/*\tent.topcolour = TOP_DEFAULT;//cl.players[plnum].ttopcolor;\n\tent.bottomcolour = cl.players[plnum].tbottomcolor;\n\tent.h2playerclass = cl.players[plnum].h2playerclass;\n*/\n\tCLQ1_AddPowerupShell(V_AddEntity(&ent), true, playereffects);\n\n\t//small hack to mask depth so only the front faces of the weaponmodel appear (no glitchy intra faces).\n\tif (alpha < 1 && qrenderer == QR_OPENGL)\n\t{\n\t\tent.forcedshader = \tR_RegisterShader(\"viewmodeldepthmask\", SUF_NONE,\n\t\t\t\t\"{\\n\"\n\t\t\t\t\t\"noshadows\\n\"\n\t\t\t\t\t\"surfaceparm nodlight\\n\"\n\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\"map $whiteimage\\n\"\n\t\t\t\t\t\t\"maskcolor\\n\"\n\t\t\t\t\t\t\"depthwrite\\n\"\n\t\t\t\t\t\"}\\n\"\n\t\t\t\t\"}\\n\"\n\t\t\t\t);\n\t\tent.shaderRGBAf[3] = 1;\n\t\tent.flags &= ~RF_TRANSLUCENT;\n\t\tV_AddEntity(&ent);\n\t\tent.forcedshader = NULL;\n\t\tent.shaderRGBAf[3] = alpha;\n\t\tent.flags |= RF_TRANSLUCENT;\n\t}\n#endif\n}\n\n//======================================================================\n\n/*\n===============\nCL_SetSolid\n\nBuilds all the pmove physents for the current frame\n===============\n*/\nvoid CL_SetSolidEntities (void)\n{\n\tint\t\ti;\n\tinframe_t\t*frame;\n\tpacket_entities_t\t*pak;\n\tentity_state_t\t\t*state;\n\tphysent_t\t\t\t*pent;\n\tmodel_t\t\t\t\t*mod;\n\n\tVALGRIND_MAKE_MEM_UNDEFINED(&pmove, sizeof(pmove));\n#ifdef CSQC_DAT\n\tpmove.world = &csqc_world;\n#endif\n\n\tmemset(&pmove.physents[0], 0, sizeof(physent_t));\n\tpmove.physents[0].model = cl.worldmodel;\n\tVectorClear (pmove.physents[0].origin);\n\tpmove.physents[0].info = 0;\n\tpmove.numphysent = 1;\n\n\tframe = &cl.inframes[cl.validsequence&UPDATE_MASK];\n\tpak = &frame->packet_entities;\n\n\tfor (i=0 ; i<pak->num_entities ; i++)\n\t{\n\t\tstate = &pak->entities[i];\n\n\t\tif (state->solidsize==ES_SOLID_NOT)\n\t\t\tcontinue;\n\n\t\tif (state->solidsize == ES_SOLID_BSP)\n\t\t{\t/*bsp model size*/\n\t\t\tif (state->modelindex <= 0)\n\t\t\t\tcontinue;\n\t\t\tmod = cl.model_precache[state->modelindex];\n\t\t\tif (!mod || mod->loadstate != MLS_LOADED)\n\t\t\t\tcontinue;\n\t\t\t/*vanilla protocols have no 'solid' information. all entities get assigned ES_SOLID_BSP, even if its not actually solid.\n\t\t\tso we need to make sure that item pickups are not erroneously considered solid, but doors etc are.\n\t\t\tnormally, ONLY inline models are considered solid when we have no solid info.\n\t\t\tmonsters will always be non-solid, too.\n\t\t\t*/\n\t\t\tif (!(cls.fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS) && mod->numsubmodels <= 1)\n\t\t\t\tcontinue;\n\t\n\t\t\tpent = &pmove.physents[pmove.numphysent];\n\t\t\tmemset(pent, 0, sizeof(physent_t));\n\t\t\tpent->model = mod;\n\t\t\tVectorCopy (state->angles, pent->angles);\n\t\t\tpent->angles[0]*=r_meshpitch.value;\n\t\t\tpent->angles[2]*=r_meshroll.value;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tpent = &pmove.physents[pmove.numphysent];\n\t\t\tmemset(pent, 0, sizeof(physent_t));\n\t\t\tpent->info = state->number;\n\t\t\t/*don't bother with angles*/\n\t\t\tCOM_DecodeSize(state->solidsize, pent->mins, pent->maxs);\n\t\t}\n\t\tif (++pmove.numphysent == MAX_PHYSENTS)\n\t\t\tbreak;\n\t\tVectorCopy(state->origin, pent->origin);\n\t\tpent->info = state->number;\n\n\t\tswitch((int)state->skinnum)\n\t\t{\n\t\tcase 0:\n\t\t\tbreak;\n\t\tcase Q1CONTENTS_LADDER:\n\t\t\tpent->nonsolid = true;\n\t\t\tpent->forcecontentsmask = FTECONTENTS_LADDER;\n\t\t\tbreak;\n\t\tcase Q1CONTENTS_SKY:\n\t\t\tpent->nonsolid = true;\n\t\t\tpent->forcecontentsmask = FTECONTENTS_SKY;\n\t\t\tbreak;\n\t\tcase Q1CONTENTS_LAVA:\n\t\t\tpent->nonsolid = true;\n\t\t\tpent->forcecontentsmask = FTECONTENTS_LAVA;\n\t\t\tbreak;\n\t\tcase Q1CONTENTS_SLIME:\n\t\t\tpent->nonsolid = true;\n\t\t\tpent->forcecontentsmask = FTECONTENTS_SLIME;\n\t\t\tbreak;\n\t\tcase Q1CONTENTS_WATER:\n\t\t\tpent->nonsolid = true;\n\t\t\tpent->forcecontentsmask = FTECONTENTS_WATER;\n\t\t\tbreak;\n\t\t}\n\t}\n\n}\n\n/*\n===\nCalculate the new position of players, without other player clipping\n\nWe do this to set up real player prediction.\nPlayers are predicted twice, first without clipping other players,\nthen with clipping against them.\nThis sets up the first phase.\n===\n*/\nvoid CL_SetUpPlayerPrediction(qboolean dopred)\n{\n\tint\t\t\t\tj;\n\tplayer_state_t\t*state;\n\tplayer_state_t\texact;\n\tdouble\t\t\tplayertime;\n\tint\t\t\t\tmsec;\n\tinframe_t\t\t\t*frame;\n\tstruct predicted_player *pplayer;\n\textern cvar_t cl_nopred, cl_demospeed;\n\tfloat predictmsmult = 1000*cl_predict_players_frac.value;\n\n\tint s;\n\n\tplayertime = realtime - cls.latency*cl_predict_players_latency.value + cl_predict_players_nudge.value;\n\tif (playertime > realtime)\n\t\tplayertime = realtime;\n\n\tif (cl_nopred.value || /*cls.demoplayback ||*/ cl.paused || cl.worldmodel->loadstate != MLS_LOADED)\n\t\treturn;\n\n\tif (cls.demoplayback)\n\t\tpredictmsmult *= cl_demospeed.value;\n\n\tframe = &cl.inframes[cl.parsecount&UPDATE_MASK];\n\n\tfor (j=0, pplayer = predicted_players, state=frame->playerstate;\n\t\tj < cl.allocated_client_slots;\n\t\tj++, pplayer++, state++)\n\t{\n\n\t\tpplayer->active = false;\n\n\t\tif (state->messagenum != cl.parsecount)\n\t\t\tcontinue;\t// not present this frame\n\n\t\tif (!state->modelindex)\n\t\t\tcontinue;\n\n\t\tpplayer->active = true;\n\t\tpplayer->flags = state->flags;\n\n\t\t// note that the local players are special, since they move locally\n\t\t// we use their last predicted postition\n\t\tfor (s = 0; s < cl.splitclients; s++)\n\t\t{\n\t\t\tif (j == cl.playerview[s].playernum)\n\t\t\t{\n\t\t\t\tVectorCopy(cl.inframes[cls.netchan.outgoing_sequence&UPDATE_MASK].playerstate[cl.playerview[s].playernum].origin, pplayer->origin);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (s == cl.splitclients)\n\t\t{\n\t\t\t// only predict half the move to minimize overruns\n\t\t\tmsec = predictmsmult*(playertime - state->state_time);\n\t\t\tif (msec <= 0 ||\n\t\t\t\t!cl_predict_players.ival ||\n\t\t\t\t!dopred)\n\t\t\t{\n\t\t\t\tVectorCopy (state->origin, pplayer->origin);\n\t//Con_DPrintf (\"nopredict\\n\");\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t// predict players movement\n\t\t\t\tif (msec > 250)\n\t\t\t\t\tmsec = 250;\n\t\t\t\tstate->command.msec = msec;\n\t//Con_DPrintf (\"predict: %i\\n\", msec);\n\n\t\t\t\tCL_PredictUsercmd (0, j+1, state, &exact, &state->command);\n\t\t\t\tVectorCopy (exact.origin, pplayer->origin);\n\t\t\t}\n\t\t}\n\t}\n}\n\n/*\n===============\nCL_SetSolid\n\nBuilds all the pmove physents for the current frame\nNote that CL_SetUpPlayerPrediction() must be called first!\npmove must be setup with world and solid entity hulls before calling\n(via CL_PredictMove)\n===============\n*/\nvoid CL_SetSolidPlayers (void)\n{\n\tint\t\tj;\n\tstruct predicted_player *pplayer;\n\tphysent_t *pent;\n\n\tif (!cl_solid_players.ival)\n\t\treturn;\n\n\tpent = pmove.physents + pmove.numphysent;\n\n\tif (pmove.numphysent == MAX_PHYSENTS)\t//too many.\n\t\treturn;\n\n\tfor (j=0, pplayer = predicted_players; j < cl.allocated_client_slots;\tj++, pplayer++)\n\t{\n\t\tif (!pplayer->active)\n\t\t\tcontinue;\t// not present this frame\n\t\tif (!(pplayer->flags & PF_SOLID))\n\t\t\tcontinue;\n\n\t\tmemset(pent, 0, sizeof(physent_t));\n\t\tVectorCopy(pplayer->origin, pent->origin);\n\t\tpent->info = j+1;\n\t\tVectorCopy(pmove.player_mins, pent->mins);\n\t\tVectorCopy(pmove.player_maxs, pent->maxs);\n\t\tif (++pmove.numphysent == MAX_PHYSENTS)\t//we just hit 88 miles per hour.\n\t\t\tbreak;\n\t\tpent++;\n\t}\n}\n\n/*\n===============\nCL_EmitEntities\n\nBuilds the visedicts array for cl.time\n\nMade up of: clients, packet_entities, nails, and tents\n===============\n*/\nvoid CL_ClearEntityLists(void)\n{\n\tcl_framecount++;\n\tif (cl_expandvisents || cl_numvisedicts+128 >= cl_maxvisedicts)\n\t{\n\t\tint newnum = cl_maxvisedicts + 256;\n\t\tentity_t *n = BZ_Realloc(cl_visedicts, newnum * sizeof(*n));\n\t\tif (n)\n\t\t{\n\t\t\tcl_visedicts = n;\n\t\t\tcl_maxvisedicts = newnum;\n\t\t}\n\t\tcl_expandvisents = false;\n\t}\n\tcl_numvisedicts = 0;\n\tcl_numstrisidx = 0;\n\tcl_numstrisvert = 0;\n\tcl_numstris = 0;\n}\nvoid CL_FreeVisEdicts(void)\n{\n\tcl_framecount++;\n\tBZ_Free(cl_visedicts);\n\tcl_visedicts = NULL;\n\tcl_maxvisedicts = 0;\n\tcl_numvisedicts = 0;\n}\n/*\nstatic void CL_WaterSplashes(void)\n{\n\tint i;\n\tentity_t *ent;\n\tvec3_t org;\n\n\tstatic unsigned int ltime;\n\tunsigned int ntime = cl.time*1000;\n\tif (ntime - ltime < 200)\n\t\treturn;\n\tltime = ntime;\n\n\tfor (i = 0; i < cl_numvisedicts; i++)\n\t{\n\t\tent = &cl_visedicts[i];\n\n\t\tif (ent->model)\n\t\t{\n\t\t\tif (ent->origin[2] + ent->model->mins[2] < r_refdef.waterheight &&\n\t\t\t\tent->origin[2] + ent->model->maxs[2] > r_refdef.waterheight)\n\t\t\t{\n\t\t\t\torg[0] = ent->origin[0];\n\t\t\t\torg[1] = ent->origin[1];\n\t\t\t\torg[2] = r_refdef.waterheight;\n\t\t\t\tP_RunParticleEffectTypeString(org, NULL, 1, \"te_watertransition\");\n\t\t\t}\n\t\t}\n\t}\n}\n*/\nvoid CL_EmitEntities (void)\n{\n\tif (cls.state != ca_active)\n\t\treturn;\n\n\tCL_DecayLights ();\n\n#ifdef Q2CLIENT\n\tif (cls.protocol == CP_QUAKE2)\n\t{\n\t\tCL_ClearEntityLists();\n\t\tCLQ2_AddEntities();\n\t\treturn;\n\t}\n#endif\n\tif (!cl.validsequence)\n\t\treturn;\n\n\tCL_ClearEntityLists();\n\n\tCL_LinkPlayers ();\n\tCL_LinkPacketEntities ();\n\tCL_LinkProjectiles ();\n\tCL_UpdateTEnts ();\n\n//\tCL_WaterSplashes();\n}\n\n\n\n\n\n\n\n\n\n\n\nvoid CL_ClearPredict(void)\n{\n\tmemset(predicted_players, 0, sizeof(predicted_players));\n}\n\n"
  },
  {
    "path": "engine/client/cl_ignore.c",
    "content": "/*\n\nCopyright (C) 2001-2002       A Nourai\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  \n\nSee the included (GNU.txt) GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n*/\n\n\n#include \"quakedef.h\"\n#include \"cl_ignore.h\"\n\n#include <ctype.h>\n\n#define MAX_TEAMIGNORELIST\t4\n#define\tFLOODLIST_SIZE\t\t10\n\nint Player_IdtoSlot (int id)\n{\n\tint j;\n\n\tfor (j = 0; j < cl.allocated_client_slots; j++)\n\t{\n\t\tif (cl.players[j].name[0] && cl.players[j].userid == id)\n\t\t\treturn j;\n\t}\n\treturn -1;\n}\n\nint Player_StringtoSlot(const char *arg)\n{\n\tint i, slot;\n\n\tfor (i = 0; i < cl.allocated_client_slots; i++)\n\t{\n\t\tif (cl.players[i].name[0] && !strncmp(arg, cl.players[i].name, MAX_SCOREBOARDNAME - 1))\n\t\t\treturn i;\n\t}\n\n\tif (!arg[0])\n\t\treturn PLAYER_NAME_NOMATCH;\n\n\tfor (i = 0; arg[i]; i++)\n\t{\n\t\tif (!isdigit(arg[i]))\n\t\t\treturn PLAYER_NAME_NOMATCH;\n\t}\n\treturn ((slot = Player_IdtoSlot(Q_atoi(arg))) >= 0) ? slot : PLAYER_ID_NOMATCH;\n}\n\nint Player_NametoSlot(const char *name)\n{\n\tint i;\n\n\tfor (i = 0; i < cl.allocated_client_slots; i++)\n\t{\n\t\tif (cl.players[i].name[0] && !strncmp(cl.players[i].name, name, MAX_SCOREBOARDNAME - 1))\n\t\t\treturn i;\n\t}\n\treturn PLAYER_NAME_NOMATCH;\n}\n\nint Player_SlottoId (int slot)\n{\t\n\treturn (slot >= 0 && slot < cl.allocated_client_slots && cl.players[slot].name[0]) ? cl.players[slot].userid : -1;\n}\n\nchar *Player_MyName (void)\n{\n\treturn cl.players[cl.playerview[0].playernum].name;\n}\n\n\n\n\n\n\n\n\ncvar_t\t\tignore_spec\t\t\t\t= CVARD(\"ignore_spec\", \"0\", \"0: Never ignore spectators.\\n1: Ignore spectators only when playing.\\n2: Always ignore spectators even when spectating.\");\ncvar_t\t\tignore_qizmo_spec\t\t= CVAR(\"ignore_qizmo_spec\", \"0\");\ncvar_t\t\tignore_mode\t\t\t\t= CVAR(\"ignore_mode\", \"0\");\ncvar_t\t\tignore_flood_duration\t= CVARD(\"ignore_flood_duration\", \"4\", \"Time limit for inbound messages to be considered duplicates.\");\ncvar_t\t\tignore_flood\t\t\t= CVARD(\"ignore_flood\", \"0\", \"Provides a way to reduce inbound spam from flooding out your chat (dupe messages are ignored).\\n0: No inbound flood protection.\\n1: Duplicate non-team messages will be filtered.\\n2: ALL duplicate messages will be filtered\");\ncvar_t\t\tignore_opponents\t\t= CVARD(\"ignore_opponents\", \"0\", \"0: Don't ignore chat from enemies.\\n1: Always ignore chat from opponents (note: can also ignore f_ruleset checks).\\n2: Ignore chat from opponents only during a match (requires servers that actually reports match state).\\n\");\n\nchar ignoreteamlist[MAX_TEAMIGNORELIST][16 + 1];\n\ntypedef struct flood_s\n{\n\tint playernum;\n\tchar data[2048];\n\tfloat time;\n} flood_t;\n\nstatic flood_t floodlist[FLOODLIST_SIZE];\nstatic int\t\tfloodindex;\n\nextern int PaddedPrint (char *s, int x);\n\nstatic qboolean IsIgnored(int slot)\n{\n\treturn cl.players[slot].ignored;\n}\n\nstatic void Display_Ignorelist(void)\n{\n\tint i;\n\tint x;\n\tqboolean foundone;\n\tplayerview_t *pv = &cl.playerview[0];\n\n\tx = 0;\n\tfoundone = false;\n\tfor (i = 0; i < cl.allocated_client_slots; i++)\n\t{\n\t\tif (cl.players[i].name[0] && cl.players[i].ignored)\n\t\t{\n\t\t\tif (!foundone)\n\t\t\t{\n\t\t\t\tCon_Printf (\"\\x02\" \"User Ignore List:\\n\");\n\t\t\t\tfoundone++;\n\t\t\t}\n\t\t\tx = PaddedPrint(cl.players[i].name, x);\n\t\t}\n\t}\n\tif (!foundone)\n\t\tCon_Printf(\"\\x02\" \"User Ignore List: empty\\n\");\n\telse if (x)\n\t\tCon_Printf (\"\\n\");\n\n\tx = 0;\n\tfoundone = false;\n\tfor (i = 0; i < cl.allocated_client_slots; i++)\n\t{\n\t\tif (cl.players[i].name[0] && cl.players[i].ignored)\n\t\t{\n\t\t\tif (!foundone)\n\t\t\t{\n\t\t\t\tCon_Printf (\"\\x02\" \"User Mute List:\\n\");\n\t\t\t\tfoundone++;\n\t\t\t}\n\t\t\tx = PaddedPrint(cl.players[i].name, x);\n\t\t}\n\t}\n\tif (!foundone)\n\t\tCon_Printf(\"\\x02\" \"User Mute List: empty\\n\");\n\telse if (x)\n\t\tCon_Printf (\"\\n\");\n\t\n\tif (ignoreteamlist[0][0])\n\t{\n\t\tx = 0;\n\t\tCon_Printf (\"\\x02\" \"Team Ignore List:\\n\");\n\t\tfor (i = 0; i < MAX_TEAMIGNORELIST && ignoreteamlist[i][0]; i++)\n\t\t\tx = PaddedPrint(ignoreteamlist[i], x);\n\t\tif (x)\n\t\t\tCon_Printf (\"\\n\");\n\t}\n\n\tif (ignore_opponents.ival)\n\t\tCon_Printf(\"\\x02\" \"Opponents are Ignored\\n\");\n\n\tif (ignore_spec.ival == 2 || (ignore_spec.ival == 1 && !pv->spectator))\n\t\tCon_Printf (\"\\x02\" \"Spectators are Ignored\\n\");\n\n\tif (ignore_qizmo_spec.ival)\n\t\tCon_Printf(\"\\x02\" \"Qizmo spectators are Ignored\\n\");\n\n\tCon_Printf(\"\\n\");\n}\n\nstatic qboolean Ignorelist_Add(int slot)\n{\n\tif (IsIgnored(slot))\n\t\treturn false;\n\n\tcl.players[slot].ignored = true;\n\tcl.players[slot].vignored = true;\n\tS_Voip_Ignore(slot, true);\n\treturn true;\n}\nstatic qboolean Ignorelist_VAdd(int slot)\n{\n\tif (cl.players[slot].vignored)\n\t\treturn false;\n\n\tcl.players[slot].vignored = true;\n\tS_Voip_Ignore(slot, true);\n\treturn true;\n}\nstatic qboolean Ignorelist_VDel(int slot)\n{\n\tif (!cl.players[slot].vignored)\n\t\treturn false;\n\n\tcl.players[slot].vignored = false;\n\tS_Voip_Ignore(slot, false);\n\treturn true;\n}\n\nstatic qboolean Ignorelist_Del(int slot)\n{\n\tif (cl.players[slot].ignored == false)\n\t\treturn false;\n\n\tcl.players[slot].ignored = false;\n\tcl.players[slot].vignored = true;\n\tS_Voip_Ignore(slot, false);\n\treturn true;\n}\n\nstatic void VIgnore_f(void)\n{\n\tint c, slot;\n\n\tif ((c = Cmd_Argc()) == 1)\n\t{\n\t\tDisplay_Ignorelist();\n\t\treturn;\n\t}\n\telse if (c != 2)\n\t{\n\t\tCon_Printf(\"Usage: %s [userid | name]\\n\", Cmd_Argv(0));\n\t\treturn;\n\t}\n\n\tif ((slot = Player_StringtoSlot(Cmd_Argv(1))) == PLAYER_ID_NOMATCH)\n\t{\n\t\tCon_Printf(\"%s : no player with userid %d\\n\", Cmd_Argv(0), Q_atoi(Cmd_Argv(1)));\n\t}\n\telse if (slot == PLAYER_NAME_NOMATCH)\n\t{\n\t\tCon_Printf(\"%s : no player with name %s\\n\", Cmd_Argv(0), Cmd_Argv(1));\n\t}\n\telse\n\t{\n\t\tif (Ignorelist_VAdd(slot))\n\t\t\tCon_Printf(\"Added user %s to mute list\\n\", cl.players[slot].name);\n\t\telse\n\t\t\tCon_Printf (\"User %s is already mute\\n\", cl.players[slot].name);\n\t}\n}\nstatic void VUnignore_f(void)\n{\n\tint c, slot;\n\n\tif ((c = Cmd_Argc()) == 1)\n\t{\n\t\tDisplay_Ignorelist();\n\t\treturn;\n\t}\n\telse if (c != 2)\n\t{\n\t\tCon_Printf(\"Usage: %s [userid | name]\\n\", Cmd_Argv(0));\n\t\treturn;\n\t}\n\n\tif ((slot = Player_StringtoSlot(Cmd_Argv(1))) == PLAYER_ID_NOMATCH)\n\t{\n\t\tCon_Printf(\"%s : no player with userid %d\\n\", Cmd_Argv(0), Q_atoi(Cmd_Argv(1)));\n\t}\n\telse if (slot == PLAYER_NAME_NOMATCH)\n\t{\n\t\tCon_Printf(\"%s : no player with name %s\\n\", Cmd_Argv(0), Cmd_Argv(1));\n\t}\n\telse\n\t{\n\t\tif (Ignorelist_VDel(slot))\n\t\t\tCon_Printf(\"Removed user %s from mute list\\n\", cl.players[slot].name);\n\t\telse\n\t\t\tCon_Printf (\"User %s already wasn't muted\\n\", cl.players[slot].name);\n\t}\n}\n\nstatic void Ignore_f(void)\n{\n\tint c, slot;\n\n\tif ((c = Cmd_Argc()) == 1)\n\t{\n\t\tDisplay_Ignorelist();\n\t\treturn;\n\t}\n\telse if (c != 2)\n\t{\n\t\tCon_Printf(\"Usage: %s [userid | name]\\n\", Cmd_Argv(0));\n\t\treturn;\n\t}\n\n\tif ((slot = Player_StringtoSlot(Cmd_Argv(1))) == PLAYER_ID_NOMATCH)\n\t{\n\t\tCon_Printf(\"%s : no player with userid %d\\n\", Cmd_Argv(0), Q_atoi(Cmd_Argv(1)));\n\t}\n\telse if (slot == PLAYER_NAME_NOMATCH)\n\t{\n\t\tCon_Printf(\"%s : no player with name %s\\n\", Cmd_Argv(0), Cmd_Argv(1));\n\t}\n\telse\n\t{\n\t\tif (Ignorelist_Add(slot))\n\t\t\tCon_Printf(\"Added user %s to ignore list\\n\", cl.players[slot].name);\n\t\telse\n\t\t\tCon_Printf (\"User %s is already ignored\\n\", cl.players[slot].name);\n\t}\n}\n\nstatic void IgnoreList_f(void)\n{\n\tif (Cmd_Argc() != 1)\n\t\tCon_Printf(\"%s : no arguments expected\\n\", Cmd_Argv(0));\n\telse\n\t\tDisplay_Ignorelist();\n}\n\nstatic void Ignore_ID_f(void)\n{\n\tint c, userid, i, slot;\n\tchar *arg;\n\n\tif ((c = Cmd_Argc()) == 1)\n\t{\n\t\tDisplay_Ignorelist();\n\t\treturn;\n\t}\n\telse if (c != 2)\n\t{\n\t\tCon_Printf(\"Usage: %s [userid]\\n\", Cmd_Argv(0));\n\t\treturn;\n\t}\n\targ = Cmd_Argv(1);\n\tfor (i = 0; arg[i]; i++)\n\t{\n\t\tif (!isdigit(arg[i]))\n\t\t{\n\t\t\tCon_Printf(\"Usage: %s [userid]\\n\", Cmd_Argv(0));\n\t\t\treturn;\n\t\t}\n\t}\n\tuserid = Q_atoi(arg);\n\tif ((slot = Player_IdtoSlot(userid)) == PLAYER_ID_NOMATCH)\n\t{\n\t\tCon_Printf(\"%s : no player with userid %d\\n\", Cmd_Argv(0), userid);\n\t\treturn;\n\t}\n\tif (Ignorelist_Add(slot))\n\t\tCon_Printf(\"Added user %s to ignore list\\n\", cl.players[slot].name);\n\telse\n\t\tCon_Printf (\"User %s is already ignored\\n\", cl.players[slot].name);\n}\n\nstatic void Unignore_f(void)\n{\n\tint c, slot;\n\n\tif ((c = Cmd_Argc()) == 1)\n\t{\n\t\tDisplay_Ignorelist();\n\t\treturn;\n\t}\n\telse if (c != 2)\n\t{\n\t\tCon_Printf(\"Usage: %s [userid | name]\\n\", Cmd_Argv(0));\n\t\treturn;\n\t}\n\n\tif ((slot = Player_StringtoSlot(Cmd_Argv(1))) == PLAYER_ID_NOMATCH)\n\t{\n\t\tCon_Printf(\"%s : no player with userid %d\\n\", Cmd_Argv(0), Q_atoi(Cmd_Argv(1)));\n\t}\n\telse if (slot == PLAYER_NAME_NOMATCH)\n\t{\n\t\tCon_Printf(\"%s : no player with name %s\\n\", Cmd_Argv(0), Cmd_Argv(1));\n\t}\n\telse\n\t{\t\t\n\t\tif (Ignorelist_Del(slot))\n\t\t\tCon_Printf(\"Removed user %s from ignore list\\n\", cl.players[slot].name);\n\t\telse\n\t\t\tCon_Printf(\"User %s is not being ignored\\n\", cl.players[slot].name);\n\t}\n}\n\nstatic void Unignore_ID_f(void)\n{\n\tint c, i, userid, slot;\n\tchar *arg;\n\n\tif ((c = Cmd_Argc()) == 1)\n\t{\n\t\tDisplay_Ignorelist();\n\t\treturn;\n\t}\n\telse if (c != 2)\n\t{\n\t\tCon_Printf(\"Usage: %s [userid]\\n\", Cmd_Argv(0));\n\t\treturn;\n\t}\n\targ = Cmd_Argv(1);\n\tfor (i = 0; arg[i]; i++)\n\t{\n\t\tif (!isdigit(arg[i]))\n\t\t{\n\t\t\tCon_Printf(\"Usage: %s [userid]\\n\", Cmd_Argv(0));\n\t\t\treturn;\n\t\t}\n\t}\n\tuserid = Q_atoi(arg);\n\tif ((slot = Player_IdtoSlot(userid)) == PLAYER_ID_NOMATCH)\n\t{\n\t\tCon_Printf(\"%s : no player with userid %d\\n\", Cmd_Argv(0), userid);\n\t\treturn;\n\t}\n\tif (Ignorelist_Del(slot))\n\t\tCon_Printf(\"Removed user %s from ignore list\\n\", cl.players[slot].name);\n\telse\n\t\tCon_Printf(\"User %s is not being ignored\\n\", cl.players[slot].name);\n}\n\nstatic void Ignoreteam_f(void)\n{\n\tint c, i, j;\n\tchar *arg;\n\n\tc = Cmd_Argc();\n\tif (c == 1)\n\t{\n\t\tDisplay_Ignorelist();\n\t\treturn;\n\t}\n\telse if (c != 2)\n\t{\n\t\tCon_Printf(\"Usage: %s [team]\\n\", Cmd_Argv(0));\n\t\treturn;\n\t}\n\targ = Cmd_Argv(1);\n\tfor (i = 0; i < cl.allocated_client_slots; i++)\n\t{\n\t\tif (cl.players[i].name[0] && !cl.players[i].spectator && !strcmp(arg, cl.players[i].team))\n\t\t{\n\t\t\tfor (j = 0; j < MAX_TEAMIGNORELIST && ignoreteamlist[j][0]; j++)\n\t\t\t{\n\t\t\t\tif (!strncmp(arg, ignoreteamlist[j], sizeof(ignoreteamlist[j]) - 1))\n\t\t\t\t{\n\t\t\t\t\tCon_Printf (\"Team %s is already ignored\\n\", arg);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (j == MAX_TEAMIGNORELIST)\n\t\t\t\tCon_Printf(\"You cannot ignore more than %d teams\\n\", MAX_TEAMIGNORELIST);\n\t\t\telse\n\t\t\t{\n\t\t\t\tQ_strncpyz(ignoreteamlist[j], arg, sizeof(ignoreteamlist[j]));\n\t\t\t\tif (j + 1 < MAX_TEAMIGNORELIST)\n\t\t\t\t\tignoreteamlist[j + 1][0] = 0;\n\t\t\t\tCon_Printf(\"Added team %s to ignore list\\n\", arg);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t}\n\tCon_Printf(\"%s : no team with name %s\\n\", Cmd_Argv(0), arg);\n}\n\nstatic void Unignoreteam_f(void)\n{\n\tint i, c, j;\n\tchar *arg;\n\n\tc = Cmd_Argc();\n\tif (c == 1)\n\t{\n\t\tDisplay_Ignorelist();\n\t\treturn;\n\t}\n\telse if (c != 2)\n\t{\n\t\tCon_Printf(\"Usage: %s [team]\\n\", Cmd_Argv(0));\n\t\treturn;\n\t}\t\n\targ = Cmd_Argv(1);\n\tfor (i = 0; i < MAX_TEAMIGNORELIST && ignoreteamlist[i][0]; i++)\n\t{\n\t\tif (!strncmp(arg, ignoreteamlist[i], sizeof(ignoreteamlist[i]) - 1))\n\t\t{\n\t\t\tfor (j = i; j < MAX_TEAMIGNORELIST && ignoreteamlist[j][0]; j++) \n\t\t\t\t;\n\t\t\tif ( --j >  i)\n\t\t\t\tQ_strncpyz(ignoreteamlist[i], ignoreteamlist[j], sizeof(ignoreteamlist[i]));\n\t\t\tignoreteamlist[j][0] = 0;\t\t\t\n\t\t\tCon_Printf(\"Removed team %s from ignore list\\n\", arg);\n\t\t\treturn;\n\t\t}\n\t}\n\tCon_Printf(\"Team %s is not being ignored\\n\", arg);\n}\n\nstatic void UnignoreAll_f (void)\n{\n\tint i;\n\n\tif (Cmd_Argc() != 1)\n\t{\n\t\tCon_Printf(\"%s : no arguments expected\\n\", Cmd_Argv(0));\n\t\treturn;\n\t}\n\tfor (i = 0; i < cl.allocated_client_slots; i++)\n\t\tIgnorelist_Del(i);\n\tCon_Printf(\"User ignore list cleared\\n\");\n}\n\nstatic void UnignoreteamAll_f (void)\n{\n\tif (Cmd_Argc() != 1)\n\t{\n\t\tCon_Printf(\"%s : no arguments expected\\n\", Cmd_Argv(0));\n\t\treturn;\n\t}\n\tignoreteamlist[0][0] = 0;\n\tCon_Printf(\"Team ignore list cleared\\n\");\n}\n\nchar Ignore_Check_Flood(player_info_t *sender, const char *s, int flags)\n{\n\tint i;\n\tint slot;\n\n\tif ( !(  \n\t\t\t( (ignore_flood.value == 1 && ((flags & TPM_NORMAL) || (flags & TPM_SPECTATOR))) ||\n\t\t\t(ignore_flood.value == 2 && flags != 0) )\n\t\t)  )\n\t{\n\t\treturn NO_IGNORE_NO_ADD;\n\t}\n\n\tif (!sender)\t//don't ignore system messages.\n\t\treturn NO_IGNORE_NO_ADD;\n\n\tslot = sender - cl.players;\n\n\tif (!cls.demoplayback && !strcmp(sender->name, Player_MyName()))\n\t{\n\t\treturn NO_IGNORE_NO_ADD;\n\t}\n\tfor (i = 0; i < FLOODLIST_SIZE; i++)\n\t{\n\t\tif (floodlist[i].playernum == slot && floodlist[i].data[0] && !strncmp(floodlist[i].data, s, sizeof(floodlist[i].data) - 1) &&\n\t\t\trealtime - floodlist[i].time < ignore_flood_duration.value) {\n\t\t\treturn IGNORE_NO_ADD;\n\t\t}\n\t}\n\treturn NO_IGNORE_ADD;\n}\n\nvoid Ignore_Flood_Add(player_info_t *sender, const char *s)\n{\n\tfloodlist[floodindex].playernum = sender - cl.players;\n\tfloodlist[floodindex].data[0] = 0;\n\tQ_strncpyz(floodlist[floodindex].data, s, sizeof(floodlist[floodindex].data));\n\tfloodlist[floodindex].time = realtime;\n\tfloodindex++;\n\tif (floodindex == FLOODLIST_SIZE)\n\t\tfloodindex = 0;\n}\n\n\nqboolean Ignore_Message(const char *sendername, const char *s, int flags)\n{\n\tint slot, i;\n\tplayerview_t *pv = &cl.playerview[0];\n\n\tif (!ignore_mode.ival && (flags & 2))\n\t\treturn false;\t\t\n\n\n\tif (ignore_spec.ival == 2 && (flags == 4 || (flags == 8 && ignore_mode.ival)))\n\t\treturn true;\n\telse if (ignore_spec.ival == 1 && (flags == 4) && !pv->spectator)\n\t\treturn true;\n\n\tif (!sendername)\n\t\treturn false;\n\n\tif ((slot = Player_NametoSlot(sendername)) == PLAYER_NAME_NOMATCH)\n\t\treturn false;\t\n\n\tif (IsIgnored(slot))\n\t\treturn true;\n\n\tif (ignore_opponents.ival && (\n\t\t\t\t(int) ignore_opponents.ival == 1 ||\n\t\t\t\t(cls.state >= ca_connected && cl.matchstate == MATCH_INPROGRESS && !cls.demoplayback && !pv->spectator) // match?\n\t\t\t\t) && \n\t\t\tflags == 1 && !pv->spectator && slot != pv->playernum &&\n\t\t\t(!cl.teamplay || strcmp(cl.players[slot].team, cl.players[pv->playernum].team))\n\t\t\t)\n\t{\n\t\treturn true;\n\t}\n\n\n\tif (!cl.teamplay)\n\t\treturn false;\n\n\tif (cl.players[slot].spectator || !strcmp(Player_MyName(), sendername))\n\t\treturn false;\t\n\n\tfor (i = 0; i < MAX_TEAMIGNORELIST && ignoreteamlist[i][0]; i++)\n\t{\n\t\tif (!strncmp(cl.players[slot].team, ignoreteamlist[i], sizeof(ignoreteamlist[i]) - 1))\n\t\t\treturn true;\n\t}\n\n\treturn false;\n}\n\nvoid Ignore_ResetFloodList(void)\n{\n\tint i;\n\n\tfor (i = 0; i < FLOODLIST_SIZE; i++)\n\t\tfloodlist[i].data[0] = 0;\n\tfloodindex = 0;\n}\n\nvoid Ignore_Init(void)\n{\n\tint i;\n\n#define IGNOREGROUP \"Player Ignoring\"\n\n\tfor (i = 0; i < MAX_TEAMIGNORELIST; i++)\n\t\tignoreteamlist[i][0] = 0;\n\tIgnore_ResetFloodList();\n\n\tCvar_Register (&ignore_flood_duration, IGNOREGROUP);\n\tCvar_Register (&ignore_flood, IGNOREGROUP);\n\tCvar_Register (&ignore_spec, IGNOREGROUP);\n\tCvar_Register (&ignore_qizmo_spec, IGNOREGROUP);\n\tCvar_Register (&ignore_mode, IGNOREGROUP);\n\tCvar_Register (&ignore_opponents, IGNOREGROUP);\n\n\tCmd_AddCommand (\"cl_voip_mute\", VIgnore_f);\n\tCmd_AddCommand (\"cl_voip_unmute\", VUnignore_f);\n\tCmd_AddCommand (\"ignore\", Ignore_f);\n\tCmd_AddCommand (\"ignorelist\", IgnoreList_f);\t\t\t\n\tCmd_AddCommand (\"unignore\", Unignore_f);\t\t\t\t\n\tCmd_AddCommand (\"ignore_team\", Ignoreteam_f);\t\t\n\tCmd_AddCommand (\"unignore_team\", Unignoreteam_f);\t\n\tCmd_AddCommand (\"unignoreAll\", UnignoreAll_f);\t\t\t\n\tCmd_AddCommand (\"unignoreAll_team\", UnignoreteamAll_f);\t\n\tCmd_AddCommand (\"unignore_id\", Unignore_ID_f);\t\t\n\tCmd_AddCommand (\"ignore_id\", Ignore_ID_f);\t\t\t\n}\n"
  },
  {
    "path": "engine/client/cl_ignore.h",
    "content": "/*\n\nCopyright (C) 2001-2002       A Nourai\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  \n\nSee the included (GNU.txt) GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n*/\n\n#ifndef __IGNORE_H_\n\n#define __IGNORE_H_\t\n\n\n#define NO_IGNORE_NO_ADD\t0\n#define\tNO_IGNORE_ADD\t\t1\n#define\tIGNORE_NO_ADD\t\t2\n\nvoid Ignore_Init(void);\nqboolean Ignore_Message(const char *sendername, const char *s, int flags);\nchar Ignore_Check_Flood(player_info_t *sender, const char *s, int flags);\nvoid Ignore_Flood_Add(player_info_t *sender, const char *s);\nvoid Ignore_ResetFloodList(void);\n\n\n#define\tPLAYER_ID_NOMATCH\t\t-1\n#define\tPLAYER_NAME_NOMATCH\t\t-2\n#define\tPLAYER_NUM_NOMATCH\t\t-3\nint Player_StringtoSlot(const char *arg);\n\n#endif\n"
  },
  {
    "path": "engine/client/cl_input.c",
    "content": "/*\nCopyright (C) 1996-1997 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  \n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n*/\n// cl.input.c  -- builds an intended movement command to send to the server\n\n#include \"quakedef.h\"\n\n#ifdef _WIN32\n#include \"winquake.h\"\t//fps indep stuff.\n#endif\n\nfloat in_sensitivityscale = 1;\n\n#ifdef NQPROT\nstatic cvar_t\tcl_movement = CVARD(\"cl_movement\",\"1\", \"Specifies whether to send movement sequence info over DPP7 protocols (other protocols are unaffected). Unlike cl_nopred, this can result in different serverside behaviour.\");\n#endif\n\ncvar_t\tcl_nodelta = CVAR(\"cl_nodelta\",\"0\");\n\ncvar_t\tcl_c2sdupe = CVARD(\"cl_c2sdupe\", \"0\", \"Send duplicate copies of packets to the server. This avoids extra latency caused by packetloss, but could also make the problem worse.\");\ncvar_t\tcl_c2spps = CVARD(\"cl_c2spps\", \"0\", \"Reduces outgoing packet rates by dropping up to a third of outgoing packets.\");\ncvar_t\tcl_c2sImpulseBackup = CVARD(\"cl_c2sImpulseBackup\",\"3\", \"Prevents the cl_c2spps setting from dropping redundant packets that contain impulses, in an attempt to keep impulses more reliable.\");\nstatic cvar_t\tcl_c2sMaxRedundancy = CVARD(\"cl_c2sMaxRedundancy\",\"5\", \"This is the maximum number of input frames to send in each input packet. Values greater than 1 provide redundancy and avoid prediction misses, though you might find cl_c2sdupe provides equivelent result and at lower latency. It is locked at 3 for vanilla quakeworld, and locked at 1 for vanilla netquake.\");\ncvar_t\tcl_netfps = CVARFD(\"cl_netfps\", \"150\", CVAR_ARCHIVE, \"Send up to this many packets to the server per second. The rate used is also limited by the server which usually forces a cap to this setting of 77. Low packet rates can result in extra extrapolation to try to hide the resulting latencies.\");\ncvar_t  cl_queueimpulses = CVARD(\"cl_queueimpulses\", \"0\", \"Queues unsent impulses instead of replacing them. This avoids the need for extra wait commands (and the timing issues of such commands), but potentially increases latency and can cause scripts to be desynced with regard to buttons and impulses.\");\ncvar_t\tcl_smartjump = CVARD(\"cl_smartjump\", \"1\", \"Makes the jump button act as +moveup when in water. This is typically quieter and faster.\");\ncvar_t\tcl_iDrive = CVARFD(\"cl_iDrive\", \"1\", CVAR_SEMICHEAT, \"Effectively releases movement keys when the opposing key is pressed. This avoids dead-time when both keys are pressed. This can be emulated with various scripts, but that's messy.\");\ncvar_t\tcl_run = CVARD(\"cl_run\", \"0\", \"Enables autorun, inverting the state of the +speed key.\");\ncvar_t\tcl_fastaccel = CVARD(\"cl_fastaccel\", \"1\", \"Begin moving at full speed instantly, instead of waiting a frame or so.\");\nextern cvar_t cl_rollspeed;\nstatic cvar_t cl_sendchatstate = CVARD(\"cl_sendchatstate\", \"1\", \"Announce your chat state to the server in a privacy-violating kind of way. This allows other players to see your afk/at-console status.\");\n\ncvar_t\tcl_prydoncursor = CVAR(\"cl_prydoncursor\", \"\");\t//for dp protocol\ncvar_t\tcl_instantrotate = CVARF(\"cl_instantrotate\", \"1\", CVAR_SEMICHEAT);\ncvar_t in_xflip = CVAR(\"in_xflip\", \"0\");\ncvar_t in_vraim = CVARD(\"in_vraim\", \"1\", \"When set to 1, the 'view' angle sent to the server is controlled by your vr headset instead of separately. This is for fallback behaviour and blocks mouse+joy+gamepad aiming.\");\n\ncvar_t\tprox_inmenu = CVAR(\"prox_inmenu\", \"0\");\n\nusercmd_t cl_pendingcmd[MAX_SPLITS];\n\n/*kinda a hack...*/\nunsigned int\t\tcon_splitmodifier;\ncvar_t\tcl_forceseat = CVARAD(\"in_forceseat\", \"0\", \"in_forcesplitclient\", \"Overrides the device identifiers to control a specific client from any device. This can be used for debugging mods, where you only have one keyboard/mouse.\");\nint CL_TargettedSplit(qboolean nowrap)\n{\n\tint mod;\n\n\t//explicitly targetted at some seat number from the server\n\tif (Cmd_ExecLevel >= RESTRICT_SERVER)\n\t\treturn Cmd_ExecLevel - RESTRICT_SERVER;\n\n\t//locally executed command.\n\tif (nowrap)\n\t\tmod = MAX_SPLITS;\n\telse\n\t\tmod = cl.splitclients;\n\tif (mod < 1)\n\t\treturn 0;\n\n\tif (con_splitmodifier > 0)\n\t\treturn (con_splitmodifier - 1) % mod;\n\telse if (cl_forceseat.ival > 0)\n\t\treturn (cl_forceseat.ival-1) % cl.splitclients;\n\telse\n\t\treturn 0;\n}\n\nvoid CL_Split_f(void)\n{\n\tint tmp;\n\tchar *c;\n\tc = Cmd_Argv(0);\n\ttmp = con_splitmodifier;\n\tif (*c == '+' || *c == '-')\n\t{\n\t\tcon_splitmodifier = c[2]-'0';\n\t\tCmd_ExecuteString(va(\"%c%s\", *c, Cmd_Args()), Cmd_ExecLevel);\n\t}\n\telse\n\t{\n\t\tcon_splitmodifier = c[1]-'0';\n\t\tCmd_ExecuteString(Cmd_Args(), Cmd_ExecLevel);\n\t}\n\tcon_splitmodifier = tmp;\n}\nvoid CL_SplitA_f(void)\n{\n\tint tmp;\n\tchar *c, *args;\n\tc = Cmd_Argv(0);\n\targs = COM_Parse(Cmd_Args());\n\tif (!args)\n\t\treturn;\n\twhile(*args == ' ' || *args == '\\t')\n\t\targs++;\n\ttmp = con_splitmodifier;\n\tcon_splitmodifier = atoi(com_token);\n\tif (*c == '+' || *c == '-')\n\t\tCmd_ExecuteString(va(\"%c%s\", *c, args), Cmd_ExecLevel);\n\telse\n\t\tCmd_ExecuteString(args, Cmd_ExecLevel);\n\tcon_splitmodifier = tmp;\n}\n\n/*\n===============================================================================\n\nKEY BUTTONS\n\nContinuous button event tracking is complicated by the fact that two different\ninput sources (say, mouse button 1 and the control key) can both press the\nsame button, but the button should only be released when both of the\npressing key have been released.\n\nWhen a key event issues a button command (+forward, +attack, etc), it appends\nits key number as a parameter to the command so it can be matched up with\nthe release.\n\nstate bit 0 is the current state of the key\nstate bit 1 is edge triggered on the up to down transition\nstate bit 2 is edge triggered on the down to up transition\n\n===============================================================================\n*/\n\n\nkbutton_t\tin_mlook, in_strafe, in_speed;\nstatic kbutton_t\tin_klook;\nstatic kbutton_t\tin_left, in_right, in_forward, in_back;\nstatic kbutton_t\tin_lookup, in_lookdown, in_moveleft, in_moveright;\nstatic kbutton_t\tin_use, in_jump, in_attack;\nstatic kbutton_t\tin_rollleft, in_rollright, in_up, in_down;\n\nstatic kbutton_t\tin_button[19+1];\n\n#define IN_IMPULSECACHE 32\nstatic int\t\t\tin_impulse[MAX_SPLITS][IN_IMPULSECACHE];\nstatic int\t\t\tin_nextimpulse[MAX_SPLITS];\nstatic int\t\t\tin_impulsespending[MAX_SPLITS];\nstatic void CL_QueueImpulse (int pnum, int newimp)\n{\n\tif (cl_queueimpulses.ival)\n\t{\n\t\tif (in_impulsespending[pnum]>=IN_IMPULSECACHE)\n\t\t{\n\t\t\tCon_Printf(\"Too many impulses, ignoring %i\\n\", newimp);\n\t\t\treturn;\n\t\t}\n\t\tin_impulse[pnum][(in_nextimpulse[pnum]+in_impulsespending[pnum])%IN_IMPULSECACHE] = newimp;\n\t\tin_impulsespending[pnum]++;\n\t}\n\telse\n\t{\n\t\tif (in_impulsespending[pnum])\n\t\t\tCon_DPrintf(\"Too many impulses, forgetting %i\\n\", in_impulse[pnum][(in_nextimpulse[pnum])%IN_IMPULSECACHE]);\n\t\tin_impulse[pnum][(in_nextimpulse[pnum])%IN_IMPULSECACHE] = newimp;\n\t\tin_impulsespending[pnum]=1;\n\t}\n}\n\nqboolean\tcursor_active;\n\n\n\n\n\nstatic qboolean KeyDown_Scan (kbutton_t *b, kbutton_t *anti, int k)\n{\n\tint pnum = CL_TargettedSplit(false);\n\t\n\tif (k == b->down[pnum][0] || k == b->down[pnum][1])\n\t\treturn false;\t\t// repeating key\n\t\n\tif (!b->down[pnum][0])\n\t\tb->down[pnum][0] = k;\n\telse if (!b->down[pnum][1])\n\t\tb->down[pnum][1] = k;\n\telse\n\t{\n\t\tCon_DPrintf (\"Three keys down for a button!\\n\");\n\t\treturn false;\n\t}\n\t\n\tif (b->state[pnum] & 1)\n\t\treturn false;\t\t// still down\n\tb->state[pnum] |= 1 + 2;\t// down + impulse down\n\n\tif (anti && (anti->state[pnum] & 1) && cl_iDrive.ival)\n\t{\t//anti-keys are the opposing key. so +forward can auto-release +back for slightly faster-responding keypresses.\n\t\tb->suppressed[pnum] = anti;\n\t\tanti->suppressed[pnum] = NULL;\n\t\tanti->state[pnum] &= ~1;\t\t// now up\n\t\tanti->state[pnum] |= 4; \t\t// impulse up\n\t}\n\treturn true;\n}\nstatic void KeyDown (kbutton_t *b, kbutton_t *anti)\n{\n\tint\t\tk;\n\tchar\t*c;\n\n\tc = Cmd_Argv(1);\n\tif (c[0])\n\t\tk = atoi(c);\n\telse\n\t\tk = -1;\t\t// typed manually at the console for continuous down\n\n\tKeyDown_Scan(b, anti, k);\n}\n\nstatic qboolean KeyUp_Scan (kbutton_t *b, int k)\n{\n\tint pnum = CL_TargettedSplit(false);\n\n\tif (k < 0)\n\t{ // typed manually at the console, assume for unsticking, so clear all\n\t\tb->suppressed[pnum] = NULL;\n\t\tb->down[pnum][0] = b->down[pnum][1] = 0;\n\t\tif (b->state[pnum] & ~4)\n\t\t{\n\t\t\tb->state[pnum] = 4;\t// impulse up\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\tif (b->down[pnum][0] == k)\n\t\tb->down[pnum][0] = 0;\n\telse if (b->down[pnum][1] == k)\n\t\tb->down[pnum][1] = 0;\n\telse\n\t\treturn false;\t\t// key up without coresponding down (menu pass through)\n\tif (b->down[pnum][0] || b->down[pnum][1])\n\t\treturn false;\t\t// some other key is still holding it down\n\n\tif (!(b->state[pnum] & 1))\n\t\treturn false;\t\t// still up (this should not happen)\n\tb->state[pnum] &= ~1;\t\t// now up\n\tb->state[pnum] |= 4; \t\t// impulse up\n\n\tif (b->suppressed[pnum])\n\t{\n\t\tif (b->suppressed[pnum]->down[pnum][0] || b->suppressed[pnum]->down[pnum][1])\n\t\t\tb->suppressed[pnum]->state[pnum] |= 1 + 2;\n\t\tb->suppressed[pnum] = NULL;\n\t}\n\treturn true;\n}\nstatic qboolean KeyUp (kbutton_t *b)\n{\n\tint\t\tk;\n\tchar\t*c;\n\n\tc = Cmd_Argv(1);\n\tif (c[0])\n\t\tk = atoi(c);\n\telse\n\t\tk = -1;\n\n\treturn KeyUp_Scan(b, k);\n}\n\n\n\n#ifdef QUAKESTATS\nstatic cvar_t\tcl_weaponhide = CVARD(\"cl_weaponhide\", \"0\", \"HACK: Attempt to switch weapon to another in order to cheat your killer out of any possible weapon upgrades.\\n0: original behaviour\\n1: switch away when +attack/+fire is released\\n2: switch away only in deathmatch\\n\");\nstatic cvar_t\tcl_weaponhide_preference = CVARAD(\"cl_weaponhide_preference\", \"2 1\", \"cl_weaponhide_axe\", \"The weapon you would like to try to switch to when cl_weaponhide is active\");\nstatic cvar_t\tcl_weaponpreselect = CVARD(\"cl_weaponpreselect\", \"0\", \"HACK: Controls the interaction between the ^aweapon^a and ^a+attack^a commands (does not affect ^aimpulse^a).\\n0: weapon switch happens instantly\\n1: weapon switch happens on next attack\\n2: instant only when already firing, otherwise delayed\\n3: delay until new attack only in deathmatch 1\\n4: delay until any attack only in deathmatch 1\");\nstatic cvar_t\tcl_weaponforgetorder = CVARD(\"cl_weaponforgetorder\", \"0\", \"The 'weapon' command will lock in its weapon choice, instead of choosing a different weapon between select+fire.\");\ncvar_t r_viewpreselgun = CVARD(\"r_viewpreselgun\", \"0\", \"HACK: Display the preselected weaponmodel, instead of the current weaponmodel.\");\nstatic int preselectedweapons[MAX_SPLITS];\nstatic int preselectedweapon[MAX_SPLITS][32];\nstatic kbutton_t in_wwheel;\nstatic float wwheeldir[MAX_SPLITS][2];\nstatic size_t wwheelsel[MAX_SPLITS];\nstatic double wwheelseltime[MAX_SPLITS];\n\nstatic struct weaponinfo_s {\n\tchar shortname[32]; //must not look like an impulse.\n\tint impulse; //primary key...\n\tint items_mask;\n\tint items_val;\n//\tint weapon_val;\t//unused...\n\tint ammostat;\n\tint ammomin;\n\n\tchar *icons[3]; //unselected,selected,ammo\n\tchar *viewmodel;\n} *weaponinfo;\nstatic size_t weaponinfo_count;\n\nstatic int IN_NameToWeaponIdx(const char *name)\n{\n\tsize_t i;\n\tchar *end;\n\ti = strtoul(name, &end, 10);\n\tif (i && !end)\n\t{\t//select by impulse when they specified something numeric.\n\t\tfor (i = 0; i < weaponinfo_count; i++)\n\t\t{\n\t\t\tif (weaponinfo[i].impulse == i)\n\t\t\t\treturn i;\n\t\t}\n\t}\n\telse\n\t{\n\t\tfor (i = 0; i < weaponinfo_count; i++)\n\t\t{\n\t\t\tif (!strcmp(name, weaponinfo[i].shortname))\n\t\t\t\treturn i;\n\t\t}\n\t}\n\treturn -1;\n}\nstatic int IN_RegisterWeapon(int impulse, const char *name, int items_mask, int items_val, int weapon_val, int ammostat, int ammomin, const char *viewmodel, const char *icon,const char *selicon, const char *ammoicon)\n{\n\tsize_t i;\n\tchar *end;\n\tif (impulse <= 0 || impulse > 255)\n\t\treturn -1;\t//no. just no...\n\tif (strtol(name, &end, 10) != impulse)\n\t{\n\t\tif (!*end)\n\t\t\treturn -1;\t//parsed as a number, which didn't match its impulse... don't break the impulse command.\n\t}\n\t//slots are unique/replaced by impulse\n\tfor (i = 0; i < weaponinfo_count; i++)\n\t{\n\t\tif (weaponinfo[i].impulse == impulse)\n\t\t\tbreak;\t//replace...\n\t}\n\tif (i == weaponinfo_count)\n\t\tZ_ReallocElements((void**)&weaponinfo, &weaponinfo_count, i+1, sizeof(*weaponinfo));\n\tweaponinfo[i].impulse = impulse;\n\n\tQ_strncpyz(weaponinfo[i].shortname, name, sizeof(weaponinfo[i].shortname));\n\tweaponinfo[i].items_mask = items_mask;\n\tweaponinfo[i].items_val = items_val;\n//\tweaponinfo[i].weapon_val;\t//unused...\n\tweaponinfo[i].ammostat = ammostat;\n\tweaponinfo[i].ammomin = ammomin;\n\n#define Z_StrDupPtr2(v,s) do{Z_Free(*v),*(v) = (s&&*s)?strcpy(Z_Malloc(strlen(s)+1), s):NULL;}while(0)\n\tZ_StrDupPtr2(&weaponinfo[i].viewmodel, viewmodel);\n\tZ_StrDupPtr2(&weaponinfo[i].icons[0], icon);\n\tZ_StrDupPtr2(&weaponinfo[i].icons[1], selicon);\n\tZ_StrDupPtr2(&weaponinfo[i].icons[2], ammoicon);\n\treturn i;\n}\nstatic void IN_RegisterWeapon_Clear(void)\n{\n\twhile(weaponinfo_count)\n\t\tZ_Free(weaponinfo[--weaponinfo_count].viewmodel);\n\tZ_Free(weaponinfo);\n\tweaponinfo = NULL;\n}\nvoid IN_RegisterWeapon_Reset(void)\n{\n\tvfsfile_t *f;\n\tIN_RegisterWeapon_Clear();\n\n\tf = FS_OpenVFS(\"wwheel.txt\", \"rb\", FS_GAME);\n\tif (f)\n\t{\t//from the rerelease:\n\t\t/*\n\t\tslot N\n\t\t{\n\t\t\timpulse N\n\t\t\ticon \"gfx/weapons/ww_foo_1.lmp\"\n\t\t\ticon_sel \"gfx/weapons/ww_foo_2.lmp\"\n\t\t\tammoicon \"FOO\"\n\t\t\tentvaroffs N\n\t\t\tweaponnum N\n\t\t}\n\t\t*/\n\t\tchar line[1024];\n\t\tchar *v;\n\t\tfor(;;)\n\t\t{\n\t\t\tif (!VFS_GETS(f, line, sizeof(line)))\n\t\t\t\tbreak;\n\t\t\tv = COM_Parse(line);\n\t\t\tif (!strcmp(com_token, \"slot\"))\t//slot N... just assume they're ordered.\n\t\t\t{\n\t\t\t\t//v = COM_Parse(line);\n\t\t\t\t//slot = com_token\n\t\t\t\tif (!VFS_GETS(f, line, sizeof(line)))\n\t\t\t\t\tbreak;\n\t\t\t\tv = COM_Parse(line);\n\t\t\t\tif (!strcmp(com_token, \"{\"))\n\t\t\t\t{\n\t\t\t\t\tint weaponbit=0;\n\t\t\t\t\tint impulse=0;\n\t\t\t\t\tint ammostat=-1;\n\t\t\t\t\tint ammocount=0;\n\t\t\t\t\tint field;\n\t\t\t\t\tchar *icon = NULL;\n\t\t\t\t\tchar *selicon = NULL;\n\t\t\t\t\tchar *ammoicon = NULL;\n\t\t\t\t\tchar *viewmodel = NULL;\n\t\t\t\t\tchar *name = NULL;\n\n\t\t\t\t\tfor (;;)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (!VFS_GETS(f, line, sizeof(line)))\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tv = COM_Parse(line);\n\t\t\t\t\t\tif (!strcmp(com_token, \"}\"))\n\t\t\t\t\t\t{\t//end of this weapon.\n\t\t\t\t\t\t\tIN_RegisterWeapon(impulse, name?name:va(\"%i\", impulse), weaponbit,weaponbit, weaponbit, ammostat,ammocount, viewmodel, icon, selicon, ammoicon);\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (!strcmp(com_token, \"impulse\"))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tv = COM_Parse(v);\n\t\t\t\t\t\t\timpulse = atoi(com_token);\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (!strcmp(com_token, \"weaponnum\"))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tv = COM_Parse(v);\n\t\t\t\t\t\t\tweaponbit = atoi(com_token);\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (!strcmp(com_token, \"icon\"))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tv = COM_Parse(v);\n\t\t\t\t\t\t\tZ_StrDupPtr(&icon, com_token);\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (!strcmp(com_token, \"icon_sel\"))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tv = COM_Parse(v);\n\t\t\t\t\t\t\tZ_StrDupPtr(&selicon, com_token);\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (!strcmp(com_token, \"ammoicon\"))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tv = COM_Parse(v);\n\t\t\t\t\t\t\tif (strchr(com_token, '.') || strchr(com_token, '/'))\n\t\t\t\t\t\t\t\tZ_StrDupPtr(&ammoicon, com_token);\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\tZ_StrDupPtr(&ammoicon, va(\"gfx/%s\", com_token));\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (!strcmp(com_token, \"entvaroffs\"))\n\t\t\t\t\t\t{\t//this seems to be a host-only thing. the server can poke the qc's fields directly but other clients can't.\n\t\t\t\t\t\t\t//remap known indexes to stats - this can only work for nq's system fields.\n\t\t\t\t\t\t\t//note that rogue does some windowing thing so our client can only know either nails or lavanails, not both. either way those are NOT the normal stats and these weapons will appear to have infinite ammo as a result, sorry.\n\t\t\t\t\t\t\t//they really should have used field names here, not numbers, but hey, not my spec... use our 'ammostat' instead for fancy mods.\n\t\t\t\t\t\t\tv = COM_Parse(v);\n\t\t\t\t\t\t\tfield = atoi(com_token);\n\t\t\t\t\t\t\tswitch(field)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase 216:\tammostat = STAT_SHELLS, ammocount = 1;\tbreak;\n\t\t\t\t\t\t\tcase 220:\tammostat = STAT_NAILS, ammocount = 1;\tbreak;\n\t\t\t\t\t\t\tcase 224:\tammostat = STAT_ROCKETS, ammocount = 1;\tbreak;\n\t\t\t\t\t\t\tcase 228:\tammostat = STAT_CELLS, ammocount = 1;\tbreak;\n\t\t\t\t\t\t\tdefault:\tammostat = -1, ammocount = 0;\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (!strcmp(com_token, \"ammostat\"))\n\t\t\t\t\t\t{\t//non-qe\n\t\t\t\t\t\t\tv = COM_Parse(v);\n\t\t\t\t\t\t\tammostat = atoi(com_token);\n\t\t\t\t\t\t\tif (ammocount <= 0)\n\t\t\t\t\t\t\t\tammocount = 1;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (!strcmp(com_token, \"ammomin\"))\n\t\t\t\t\t\t{\t//non-qe\n\t\t\t\t\t\t\tv = COM_Parse(v);\n\t\t\t\t\t\t\tammocount = atoi(com_token);\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (!strcmp(com_token, \"viewmodel\"))\n\t\t\t\t\t\t{\t//non-qe, so preselect can show the correct model before its actually changed.\n\t\t\t\t\t\t\tv = COM_Parse(v);\n\t\t\t\t\t\t\tZ_StrDupPtr(&viewmodel, com_token);\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (!strcmp(com_token, \"shortname\"))\n\t\t\t\t\t\t{\t//non-qe, for `+fire nq`\n\t\t\t\t\t\t\tv = COM_Parse(v);\n\t\t\t\t\t\t\tZ_StrDupPtr(&name, com_token);\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (*com_token)\n\t\t\t\t\t\t\tCon_Printf(\"Unexpected line in wwheel.txt: %s\\n\", line);\n\t\t\t\t\t}\n\t\t\t\t\tZ_Free(icon);\n\t\t\t\t\tZ_Free(selicon);\n\t\t\t\t\tZ_Free(ammoicon);\n\t\t\t\t\tZ_Free(viewmodel);\n\t\t\t\t\tZ_Free(name);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tCon_Printf(\"missing block, found: %s\\n\", line);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (*com_token)\n\t\t\t\tCon_Printf(\"Unexpected line in wwheel.txt: %s\\n\", line);\n\t\t}\n\t\tVFS_CLOSE(f);\n\t}\n\telse\n\t{\n\t\tIN_RegisterWeapon(2, \"sg\", IT_SHOTGUN,IT_SHOTGUN, IT_SHOTGUN, STAT_SHELLS,1, \"progs/v_shot.mdl\", NULL,NULL, \"gfx/sb_shells\");\n\t\tIN_RegisterWeapon(3, \"ssg\", IT_SUPER_SHOTGUN,IT_SUPER_SHOTGUN, IT_SUPER_SHOTGUN, STAT_SHELLS,1, \"progs/v_shot2.mdl\", NULL,NULL, \"gfx/sb_shells\");\n\t\tIN_RegisterWeapon(4, \"ng\", IT_NAILGUN,IT_NAILGUN, IT_NAILGUN, STAT_NAILS,1, \"progs/v_nail.mdl\", NULL,NULL, \"gfx/sb_nails\");\n\t\tIN_RegisterWeapon(5, \"sng\", IT_SUPER_NAILGUN,IT_SUPER_NAILGUN, IT_SUPER_NAILGUN, STAT_NAILS,1, \"progs/v_nail2.mdl\", NULL,NULL, \"gfx/sb_nails\");\n\t\tIN_RegisterWeapon(6, \"gl\", IT_GRENADE_LAUNCHER,IT_GRENADE_LAUNCHER, IT_GRENADE_LAUNCHER, STAT_ROCKETS,1, \"progs/v_rock.mdl\", NULL,NULL, \"gfx/sb_rocket\");\n\t\tIN_RegisterWeapon(7, \"rl\", IT_ROCKET_LAUNCHER,IT_ROCKET_LAUNCHER, IT_ROCKET_LAUNCHER, STAT_ROCKETS,1, \"progs/v_rock2.mdl\", NULL,NULL, \"gfx/sb_rocket\");\n\t\tIN_RegisterWeapon(8, \"lg\", IT_LIGHTNING,IT_LIGHTNING, IT_LIGHTNING, STAT_CELLS,1, \"progs/v_light.mdl\", NULL,NULL, \"gfx/sb_cells\");\n\t\tIN_RegisterWeapon(1, \"axe\", IT_AXE,IT_AXE, IT_AXE, -1,0, \"progs/v_axe.mdl\", NULL, NULL, NULL);\n\t}\n}\nstatic void IN_RegisterWeapon_f(void)\n{\n\tif (Cmd_Argc() <= 2)\n\t{\n\t\tconst char *arg = Cmd_Argv(1);\n\t\tif (!strcmp(arg, \"clear\"))\n\t\t\tIN_RegisterWeapon_Clear();\n\t\telse if (!strcmp(arg, \"reset\") || !strcmp(arg, \"quake\"))\t//'quake' for compat with dp.\n\t\t\tIN_RegisterWeapon_Reset();\n\t\telse\n\t\t\tCon_Printf(\"Unknown arg %s\\n\", Cmd_Argv(1));\n\t}\n\telse\n\t{\n\t\tIN_RegisterWeapon(\tatoi(Cmd_Argv(2)),\t//impulse\n\t\t\t\t\t\t\tCmd_Argv(1),\t\t//name\n\t\t\t\t\t\t\tatoi(Cmd_Argv(3)),\t//itemsmask\n\t\t\t\t\t\t\tatoi(Cmd_Argv(3)),\t//itemsval\n\t\t\t\t\t\t\tatoi(Cmd_Argv(4)),\t//weaponval\n\t\t\t\t\t\t\tatoi(Cmd_Argv(5)),\t//ammostat\n\t\t\t\t\t\t\tatoi(Cmd_Argv(6)),\t//ammomin\n\t\t\t\t\t\t\tCmd_Argv(7),\t\t//viewmodel\n\t\t\t\t\t\t\tCmd_Argv(8),\t\t//weaponicon\n\t\t\t\t\t\t\tCmd_Argv(9),\t\t//weaponicon_sel\n\t\t\t\t\t\t\tCmd_Argv(10));\t\t//ammoicon\n\t}\n}\n\n//hacks, because we have to guess what the mod is doing. we'll probably get it wrong, which sucks.\nstatic qboolean IN_HaveWeapon_Idx(int pnum, size_t widx)\n{\n\tif (widx < weaponinfo_count)\n\t\tif ((cl.playerview[pnum].stats[STAT_ITEMS]&weaponinfo[widx].items_mask) == weaponinfo[widx].items_val)\t//we have the weapon\n\t\t\tif (weaponinfo[widx].ammostat < 0 || cl.playerview[pnum].stats[weaponinfo[widx].ammostat] >= weaponinfo[widx].ammomin)\t\t\t\t//and we have enough ammo for it too.\n\t\t\t\treturn true;\n\treturn false;\n}\nstatic qboolean IN_HaveWeapon(int pnum, int impulse)\n{\n\tsize_t widx;\n\tfor (widx = 0; widx < weaponinfo_count; widx++)\n\t{\n\t\tif (weaponinfo[widx].impulse == impulse)\n\t\t\treturn IN_HaveWeapon_Idx(pnum, widx);\n\t}\n\treturn false;\t//we don't really know about it, but assume we can't because false negatives are better than false positives here.\n}\nstatic qboolean IN_HaveWeapon_Name(int pnum, char *name, int *impulse)\n{\n\tint widx = IN_NameToWeaponIdx(name);\n\tif (widx < 0)\n\t\treturn false;\n\tif (!IN_HaveWeapon_Idx(pnum, widx))\n\t\treturn false;\n\t*impulse = weaponinfo[widx].impulse;\n\treturn true;\n}\nstatic int IN_BestWeapon_Pre(unsigned int pnum);\n//if we're using weapon preselection, then we probably also want to show which weapon will be selected, instead of showing the shotgun the whole time.\n//this of course requires more hacks.\nconst char *IN_GetPreselectedViewmodelName(unsigned int pnum)\n{\n\tif (r_viewpreselgun.ival && cl_weaponpreselect.ival && pnum < countof(preselectedweapons) && preselectedweapons[pnum])\n\t{\n\t\tint best = IN_BestWeapon_Pre(pnum);\n\t\tsize_t widx;\n\t\tfor (widx = 0; widx < weaponinfo_count; widx++)\n\t\t{\n\t\t\tif (weaponinfo[widx].impulse == best)\n\t\t\t\treturn weaponinfo[widx].viewmodel;\n\t\t}\n\t}\n\treturn NULL;\n}\n\nstatic int IN_BestWeapon_Args(unsigned int pnum, int firstarg, int argcount)\n{\t//returns impulses.\n\tint i, imp;\n\tunsigned int best = 0;\n\n\tfor (i = firstarg + argcount; --i >= firstarg; )\n\t{\n\t\tif (IN_HaveWeapon_Name(pnum, Cmd_Argv(i), &imp))\n\t\t\tbest = imp;\n\t}\n\n\treturn best;\n}\nstatic int IN_BestWeapon_Pre(unsigned int pnum)\n{\n\tint i, imp;\n\tunsigned int best = 0;\n\n\tfor (i = preselectedweapons[pnum]; i-- > 0; )\n\t{\n\t\timp = preselectedweapon[pnum][i];\n\t\tif (IN_HaveWeapon(pnum, imp))\n\t\t\tbest = imp;\n\t}\n\n\treturn best;\n}\n\nstatic void IN_DoPostSelect(void)\n{\n\tint pnum = CL_TargettedSplit(false);\n\tif (cl_weaponpreselect.ival)\n\t{\n\t\tint best = IN_BestWeapon_Pre(pnum);\n\t\tif (best)\n\t\t\tCL_QueueImpulse(pnum, best);\n\t}\n\n\tif (in_wwheel.state[pnum]&1)\n\t{\n\t\tin_wwheel.state[pnum] = 0;\n\t\tpnum = CL_TargettedSplit(false);\n\t\tif (wwheelsel[pnum] < weaponinfo_count)\n\t\t\tCL_QueueImpulse(pnum, weaponinfo[wwheelsel[pnum]].impulse);\n\t}\n}\n//The weapon command autoselects a prioritised weapon like multi-arg impulse does.\n//however, it potentially makes the switch only on the next +attack.\nvoid IN_Weapon (void)\n{\n\tint newimp;\n\tint pnum = CL_TargettedSplit(false);\n\tint mode, best, i;\n\n\tpreselectedweapons[pnum] = 0;\n\tfor (i = 1; i < Cmd_Argc() && i <= countof(preselectedweapon[pnum]); i++)\n\t{\n\t\tbest = IN_NameToWeaponIdx(Cmd_Argv(i));\n\t\tif (best >= 0)\n\t\t\tbest = weaponinfo[best].impulse;\t//a known weapon\n\t\telse\n\t\t\tbest = atoi(Cmd_Argv(i));\t//fall back\n\t\tif (best > 0)\n\t\t\tpreselectedweapon[pnum][preselectedweapons[pnum]++] = best;\n\t}\n\n\tbest = IN_BestWeapon_Pre(pnum);\n\tif (best)\n\t{\n\t\tnewimp = best;\n\t\tif (cl_weaponforgetorder.ival)\n\t\t{\t//make sure the +attack sticks with the selected weapon.\n\t\t\tpreselectedweapon[pnum][0] = best;\n\t\t\tpreselectedweapons[pnum] = 1;\n\t\t}\n\t}\n\telse\n\t\treturn;\t//no new weapon...\n\n\tmode = cl_weaponpreselect.ival;\n\tif (mode == 3)\n\t\tmode = (cl.deathmatch==1)?1:0;\n\telse if (mode == 4)\n\t\tmode = (cl.deathmatch==1)?2:0;\n\n\tif (mode == 1)\n\t\treturn;\t//don't change yet.\n\tif (mode == 2 && !(in_attack.state[pnum]&3))\n\t\treturn;\t//2 changes instantly only when already firing.\n\n\tCL_QueueImpulse(pnum, newimp);\n}\n\n//+fire 8 7 [keycode]\n//does impulse 8 or 7 (according to held weapons) along with a +attack\nvoid IN_FireDown(void)\n{\n\tint pnum = CL_TargettedSplit(false);\n\tint k;\n\tint impulse;\n\n\timpulse = Cmd_Argc()-1;\n\tk = atoi(Cmd_Argv(impulse));\n\tif (k >= 32)\n\t\timpulse--;\t//scancode, don't treat that arg as a weapon number\n\telse\n\t\tk = -1;\n\n\timpulse = IN_BestWeapon_Args(pnum, 1, impulse);\n\tif (impulse)\n\t\tCL_QueueImpulse(pnum, impulse);\n\telse\n\t\tIN_DoPostSelect();\n\n\tKeyDown_Scan(&in_attack, NULL, k);\n}\nstatic void IN_DoWeaponHide(void)\n{\n\tif (cl_weaponhide.ival && !(cl_weaponhide.ival==2 && cl.deathmatch==1))\n\t{\n\t\tint impulse, best = 0;\n\t\tint pnum = CL_TargettedSplit(false);\n\t\tchar tok[64];\n\t\tchar *l = cl_weaponhide_preference.string;\n\t\tif (!strcmp(l, \"0\")) l = \"2\"; //for compat with ezquake's cl_weaponhide_axe cvar.\n\t\twhile(l && *l)\n\t\t{\n\t\t\tl = COM_ParseOut(l, tok, sizeof(tok));\n\t\t\tif (IN_HaveWeapon_Name(pnum, tok, &impulse))\n\t\t\t\tbest = impulse;\n\t\t}\n\t\tif (best)\n\t\t{\t//looks like we're switching away\n\t\t\tCL_QueueImpulse(pnum, best);\n\t\t}\n\t}\n}\n//-fire should trigger an impulse 1 or something.\nvoid IN_FireUp(void)\n{\n\tint k;\n\tint impulse;\n\n\t//any args are used in the +fire version and linger through to the -fire.\n\t//the only useful one is the keynum.\n\n\timpulse = Cmd_Argc()-1;\n\tk = atoi(Cmd_Argv(impulse));\n\tif (k >= 32)\n\t\timpulse--;\t//scancode, don't treat that arg as a weapon number\n\telse\n\t\tk = -1;\n\n\tif (KeyUp_Scan(&in_attack, k))\n\t\tIN_DoWeaponHide();\n}\n\nqboolean IN_WeaponWheelIsShown(void)\n{\n\tif (!(in_wwheel.state[0]&1) || !weaponinfo_count)\n\t\treturn false;\n\treturn true;\n}\nqboolean IN_WeaponWheelAccumulate(int pnum, float x, float y, float threshhold) //either mouse or controller\n{\n\tif (!(in_wwheel.state[pnum]&1) || !weaponinfo_count)\n\t\treturn false;\n\n\tif (x*x+y*y > threshhold*threshhold)\t//protects against deadzones.\n\t{\n\t\twwheeldir[pnum][0] += x;\n\t\twwheeldir[pnum][1] += y;\n\t}\n\treturn true;\n}\n#include \"shader.h\"\nqboolean IN_DrawWeaponWheel(int pnum)\n{\n\tint w;\n\tfloat pos[2], centre[2];\n\tconst float radius = 64;\n\tfloat d, a;\n\tshader_t *s;\n\tif (!(in_wwheel.state[pnum]&1) || !weaponinfo_count)\n\t\treturn false;\n\tR2D_ImageColours(1,1,1,1);\n\n\tcentre[0] = cl.playerview[pnum].gamerect.x + cl.playerview[pnum].gamerect.width/2;\n\tcentre[1] = cl.playerview[pnum].gamerect.y + cl.playerview[pnum].gamerect.height/2;\n\n\td = DotProduct2(wwheeldir[pnum],wwheeldir[pnum]);\n\ta = 32;\n\tif (d > a*a && d)\n\t{\n\t\twwheeldir[pnum][0] *= a/sqrt(d);\n\t\twwheeldir[pnum][1] *= a/sqrt(d);\n\t}\n\n\ta = atan2(wwheeldir[pnum][1], wwheeldir[pnum][0]);\n\tw = (a/(2*M_PI)+1) * weaponinfo_count + 0.5;\n\tw = w % weaponinfo_count;\n\n\tif (w != wwheelsel[pnum])\n\t{\n\t\twwheelseltime[pnum] = realtime;\n\t\twwheelsel[pnum] = w;\n\t}\n\n\ts = R2D_SafeCachePic(\"gfx/weaponwheel.lmp\");\n\tif (R_GetShaderSizes(s, NULL, NULL, false)>0)\n\t\tR2D_Image(centre[0]-radius*2, centre[1]-radius*2, radius*4, radius*4, 0, 0, 1, 1, s);\n\tfor (w = 0; w < weaponinfo_count; w++)\n\t{\n\t\tpos[0] = centre[0] + cos((w*2*M_PI) / weaponinfo_count)*radius;\n\t\tpos[1] = centre[1] + sin((w*2*M_PI) / weaponinfo_count)*radius;\n\n\t\tif (weaponinfo[w].icons[0])\n\t\t{\t//draw a shadow\n\t\t\tR2D_ImageColours(0,0,0,1);\n\t\t\tR2D_Image(pos[0]-24+2, pos[1]-16+2, 48, 32, 0, 0, 1, 1, R2D_SafeCachePic(weaponinfo[w].icons[0]));\n\t\t}\n\n\t\t//and the real icon (dark if unavailable)\n\t\tif (IN_HaveWeapon_Idx(pnum, w))\n\t\t\tR2D_ImageColours(1,1,1,1);\n\t\telse\n\t\t\tR2D_ImageColours(0.2,0.2,0.2,1);\n\t\tif (w == wwheelsel[pnum])\n\t\t\td = 1+sin((realtime - wwheelseltime[pnum])*10);\t//make it bounce\n\t\telse\n\t\t\td = 0;\n\t\tif (cl.playerview[pnum].stats[STAT_ACTIVEWEAPON] == weaponinfo[w].items_val && weaponinfo[w].icons[1])\n\t\t\tR2D_Image(pos[0]-24-d, pos[1]-16-d, 48, 32, 0, 0, 1, 1, R2D_SafeCachePic(weaponinfo[w].icons[1]));\n\t\telse if (weaponinfo[w].icons[0])\n\t\t\tR2D_Image(pos[0]-24-d, pos[1]-16-d, 48, 32, 0, 0, 1, 1, R2D_SafeCachePic(weaponinfo[w].icons[0]));\n\t\telse\n\t\t\tDraw_FunStringWidth(pos[0]-32-d, pos[1]-4-d, weaponinfo[w].shortname, 64, 2, cl.playerview[pnum].stats[STAT_ACTIVEWEAPON] == weaponinfo[w].items_val);\n\t}\n\tR2D_ImageColours(1,1,1,1);\n\n\tw = wwheelsel[pnum];\n\tif (weaponinfo[w].icons[2])\n\t\tR2D_Image(centre[0]-12, centre[1]-12, 24, 24, 0, 0, 1, 1, R2D_SafeCachePic(weaponinfo[w].icons[2]));\n\tDraw_FunStringWidth(centre[0]-32, centre[1] - 28, weaponinfo[w].shortname, 64, 2, false);\n\tif (weaponinfo[w].ammostat >= 0)\n\t\tDraw_FunStringWidth(centre[0]-32, centre[1] + 20, va(\"%s%d\", (cl.playerview[pnum].stats[weaponinfo[w].ammostat]<20)?S_COLOR_RED:\"\", cl.playerview[pnum].stats[weaponinfo[w].ammostat]), 64, 2, false);\n\n\tpos[0] = centre[0] + cos(a)*radius*0.6;\n\tpos[1] = centre[1] + sin(a)*radius*0.6;\n\tDraw_FunString(pos[0], pos[1], \"X\");\n\treturn true;\n}\nvoid IN_WWheelDown (void)\n{\n\tint pnum = CL_TargettedSplit(false);\n#ifdef CSQC_DAT\n\tif (CSQC_ConsoleCommand(pnum, Cmd_Argv(0)))\n\t\treturn;\n#endif\n\tif (!(in_wwheel.state[pnum]&1))\n\t{\n\t\tsize_t w;\n\t\tfor (w = 0; w < weaponinfo_count; w++)\n\t\t{\n\t\t\tif (cl.playerview[pnum].stats[STAT_ACTIVEWEAPON] == weaponinfo[w].items_val)\n\t\t\t{\t//this is our active weapon. start with it highlighted.\n\t\t\t\twwheelseltime[pnum] = realtime;\n\t\t\t\twwheelsel[pnum] = w;\n\n\t\t\t\twwheeldir[pnum][0] = cos((wwheelsel[pnum]*2*M_PI) / weaponinfo_count)*16;\n\t\t\t\twwheeldir[pnum][1] = sin((wwheelsel[pnum]*2*M_PI) / weaponinfo_count)*16;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\tKeyDown(&in_wwheel, NULL);\n}\nvoid IN_WWheelUp (void)\n{\n\tint pnum = CL_TargettedSplit(false);\n#ifdef CSQC_DAT\n\tif (CSQC_ConsoleCommand(pnum, Cmd_Argv(0)))\n\t\treturn;\n#endif\n\tif (!KeyUp(&in_wwheel))\n\t\treturn;\n\tif (wwheelsel[pnum] < weaponinfo_count)\n\t\tCL_QueueImpulse(pnum, weaponinfo[wwheelsel[pnum]].impulse);\n}\nvoid IN_IWheelDown (void)\n{\n}\nvoid IN_IWheelUp (void)\n{\n}\n#else\n#define IN_DoPostSelect()\n#define IN_DoWeaponHide()\n#endif\n\n\n//q2e compat. too lazy to use the wwheel info. let the gamecode do it the old way.\nvoid IN_WeapNext_f (void)\n{\n\tCL_SendClientCommand(true, \"weapnext\");\n}\nvoid IN_WeapPrev_f (void)\n{\n\tCL_SendClientCommand(true, \"weapprev\");\n}\n\n\nstatic void IN_KLookDown (void) {KeyDown(&in_klook, NULL);}\nstatic void IN_KLookUp (void) {KeyUp(&in_klook);}\nstatic void IN_MLookDown (void) {KeyDown(&in_mlook, NULL);}\nstatic void IN_MLookUp (void)\n{\n\tint pnum = CL_TargettedSplit(false);\n\tKeyUp(&in_mlook);\n\tif ( !(in_mlook.state[pnum]&1) &&  lookspring.ival)\n\t\tV_StartPitchDrift(&cl.playerview[pnum]);\n}\nstatic void IN_UpDown(void) {KeyDown(&in_up, &in_down);}\nstatic void IN_UpUp(void) {KeyUp(&in_up);}\nstatic void IN_DownDown(void) {KeyDown(&in_down, &in_up);}\nstatic void IN_DownUp(void) {KeyUp(&in_down);}\nstatic void IN_LeftDown(void) {KeyDown(&in_left, &in_right);}\nstatic void IN_LeftUp(void) {KeyUp(&in_left);}\nstatic void IN_RightDown(void) {KeyDown(&in_right, &in_left);}\nstatic void IN_RightUp(void) {KeyUp(&in_right);}\nstatic void IN_ForwardDown(void) {KeyDown(&in_forward, &in_back);}\nstatic void IN_ForwardUp(void) {KeyUp(&in_forward);}\nstatic void IN_BackDown(void) {KeyDown(&in_back, &in_forward);}\nstatic void IN_BackUp(void) {KeyUp(&in_back);}\nstatic void IN_LookupDown(void) {KeyDown(&in_lookup, &in_lookdown);}\nstatic void IN_LookupUp(void) {KeyUp(&in_lookup);}\nstatic void IN_LookdownDown(void) {KeyDown(&in_lookdown, &in_lookup);}\nstatic void IN_LookdownUp(void) {KeyUp(&in_lookdown);}\nstatic void IN_MoveleftDown(void) {KeyDown(&in_moveleft, &in_moveright);}\nstatic void IN_MoveleftUp(void) {KeyUp(&in_moveleft);}\nstatic void IN_MoverightDown(void) {KeyDown(&in_moveright, &in_moveleft);}\nstatic void IN_MoverightUp(void) {KeyUp(&in_moveright);}\nstatic void IN_RollLeftDown(void) {KeyDown(&in_rollleft, &in_rollright);}\nstatic void IN_RollLeftUp(void) {KeyUp(&in_rollleft);}\nstatic void IN_RollRightDown(void) {KeyDown(&in_rollright, &in_rollleft);}\nstatic void IN_RollRightUp(void) {KeyUp(&in_rollright);}\n\nstatic void IN_SpeedDown(void) {KeyDown(&in_speed, NULL);}\nstatic void IN_SpeedUp(void) {KeyUp(&in_speed);}\nstatic void IN_StrafeDown(void) {KeyDown(&in_strafe, NULL);}\nstatic void IN_StrafeUp(void) {KeyUp(&in_strafe);}\n\nstatic void IN_AttackDown(void) {IN_DoPostSelect(); KeyDown(&in_attack, NULL);}\nstatic void IN_AttackUp(void) {if (KeyUp(&in_attack)) IN_DoWeaponHide();}\n\nstatic void IN_UseDown (void) {KeyDown(&in_use, NULL);}\nstatic void IN_UseUp (void) {KeyUp(&in_use);}\nstatic void IN_JumpDown (void)\n{\n\tqboolean up;\n\tint pnum = CL_TargettedSplit(false);\n\tplayerview_t *pv = &cl.playerview[pnum];\n\n\n\tup = (cls.state == ca_active && cl_smartjump.ival && !prox_inmenu.ival);\n\tif (!up)\n\t\tup = false;\n#ifdef Q2CLIENT\n\telse if (cls.protocol == CP_QUAKE2)\n\t\tup = true;\t//always smartjump in q2.\n#endif\n\telse if (pv->spectator && pv->cam_state != CAM_FREECAM)\n\t\tup = false;\t//if we're tracking, don't confuse stuff.\n#ifdef QUAKESTATS\n\telse if (!pv->spectator && pv->stats[STAT_HEALTH] <= 0)\n\t\tup = false;\t//don't ever 'swim' when dead.\n\telse if (pv->pmovetype == PM_FLY || pv->pmovetype == PM_6DOF || pv->pmovetype == PM_SPECTATOR || pv->pmovetype == PM_OLD_SPECTATOR)\n\t\tup = true;\t//fling/spectating\n\telse if ((pv->pmovetype == PM_NORMAL || pv->pmovetype == PM_WALLWALK) && pv->waterlevel >= 2 && (!cl.teamfortress || !(in_forward.state[pnum] & 1)))\n\t\tup = true;\t//swimming. TF only (silently) smartjumps when NOT moving.\n#endif\n\telse\n\t\tup = false;\n\n\tKeyDown((up?&in_up:&in_jump), &in_down);\n}\nstatic void IN_JumpUp (void)\n{\n\tif (cl_smartjump.ival)\n\t\tKeyUp(&in_up);\n\tKeyUp(&in_jump);\n}\n\nstatic void IN_ButtonNDown(void) {KeyDown(&in_button[atoi(Cmd_Argv(0)+7)], NULL);}\nstatic void IN_ButtonNUp(void) {KeyUp(&in_button[atoi(Cmd_Argv(0)+7)]);}\n\nfloat in_rotate;\nstatic void IN_Rotate_f (void) {in_rotate += atoi(Cmd_Argv(1));}\n\n\nvoid IN_WriteButtons(vfsfile_t *f, qboolean all)\n{\n\tint s,b;\n\tstruct\n\t{\n\t\tkbutton_t\t*button;\n\t\tchar\t\t*name;\n\t} buttons [] =\n\t{\n\t\t{&in_mlook,\t\t\"mlook\"},\n\t\t{&in_klook,\t\t\"klook\"},\n\t\t{&in_left,\t\t\"left\"},\n\t\t{&in_right,\t\t\"right\"},\n\t\t{&in_forward,\t\"forward\"},\n\t\t{&in_back,\t\t\"back\"},\n\t\t{&in_lookup,\t\"lookup\"},\n\t\t{&in_lookdown,\t\"lookdown\"},\n\t\t{&in_moveleft,\t\"moveleft\"},\n\t\t{&in_moveright,\t\"moveright\"},\n\t\t{&in_strafe,\t\"strafe\"},\n\t\t{&in_speed,\t\t\"speed\"},\n\t\t{&in_use,\t\t\"use\"},\n\t\t{&in_jump,\t\t\"jump\"},\n\t\t{&in_attack,\t\"attack\"},\n\t\t{&in_rollleft,\t\"rollleft\"},\n\t\t{&in_rollright,\t\"rollright\"},\n\t\t{&in_up,\t\t\"up\"},\n\t\t{&in_down,\t\t\"down\"},\n\t};\n\n\ts = 0;\n\tVFS_PRINTF(f, \"\\n//Player 1 buttons\\n\");\n\tfor (b = 0; b < countof(buttons); b++)\n\t{\n\t\tif ((buttons[b].button->state[s]&1) && (buttons[b].button->down[s][0]==-1 || buttons[b].button->down[s][1]==-1))\n\t\t\tVFS_PRINTF(f, \"+%s\\n\", buttons[b].name);\n\t\telse if (b || all)\n\t\t\tVFS_PRINTF(f, \"-%s\\n\", buttons[b].name);\n\t}\n\tfor (b = 0; b < countof(in_button); b++)\n\t{\n\t\tif ((in_button[b].state[s]&1) && (in_button[b].down[s][0]==-1 || in_button[b].down[s][1]==-1))\n\t\t\tVFS_PRINTF(f, \"+button%i\\n\", b);\n\t\telse\n\t\t\tVFS_PRINTF(f, \"-button%i\\n\", b);\n\t}\n\tfor (s = 1; s < MAX_SPLITS; s++)\n\t{\n\t\tVFS_PRINTF(f, \"\\n//Player %i buttons\\n\", s);\n\t\tfor (b = 0; b < countof(buttons); b++)\n\t\t{\n\t\t\tif ((buttons[b].button->state[s]&1) && (buttons[b].button->down[s][0]==-1 || buttons[b].button->down[s][1]==-1))\n\t\t\t\tVFS_PRINTF(f, \"+p%i %s\\n\", s, buttons[b].name);\n\t\t\telse if (b || all)\n\t\t\t\tVFS_PRINTF(f, \"-p%i %s\\n\", s, buttons[b].name);\n\t\t}\n\t\tfor (b = 0; b < countof(in_button); b++)\n\t\t{\n\t\t\tif ((in_button[b].state[s]&1) && (in_button[b].down[s][0]==-1 || in_button[b].down[s][1]==-1))\n\t\t\t\tVFS_PRINTF(f, \"+p%i button%i\\n\", s, b);\n\t\t\telse\n\t\t\t\tVFS_PRINTF(f, \"-p%i button%i\\n\", s, b);\n\t\t}\n\t}\n\n\t//FIXME: save device remappings to config.\n}\n\n//This function incorporates Tonik's impulse  8 7 6 5 4 3 2 1 to select the prefered weapon on the basis of having it.\n//It also incorporates split screen input as well as impulse buffering\nvoid IN_Impulse (void)\n{\n\tint newimp;\n\tint pnum = CL_TargettedSplit(false);\n\n\tnewimp = Q_atoi(Cmd_Argv(1));\n\n#ifdef QUAKESTATS\n\tif (Cmd_Argc() > 2)\n\t{\n\t\tint best = IN_BestWeapon_Args(pnum, 1, Cmd_Argc() - 1);\n\t\tif (best)\n\t\t\tnewimp = best;\n\t}\n#endif\n\n\tCL_QueueImpulse(pnum, newimp);\n}\n\nvoid IN_Restart (void)\n{\n\tIN_Shutdown();\n\tIN_ReInit();\n\n\t//FIXME: re-assert explicit device re-mappings\n}\n\n/*\n===============\nCL_KeyState\n\nReturns 0.25 if a key was pressed and released during the frame,\n0.5 if it was pressed and held\n0 if held then released, and\n1.0 if held for the entire time\n===============\n*/\nfloat CL_KeyState (kbutton_t *key, int pnum, qboolean noslowstart)\n{\n\tfloat\t\tval;\n\tqboolean\timpulsedown, impulseup, down;\n\n\tnoslowstart = noslowstart && cl_fastaccel.ival;\n\t\n\timpulsedown = key->state[pnum] & 2;\n\timpulseup = key->state[pnum] & 4;\n\tdown = key->state[pnum] & 1;\n\tval = 0;\n\t\n\tif (impulsedown && !impulseup)\n\t{\n\t\tif (down)\n\t\t\tval = noslowstart?1.0:0.5;\t// pressed and held this frame\n\t\telse\n\t\t\tval = 0;\t//\tI_Error ();\n\t}\n\tif (impulseup && !impulsedown)\n\t{\n\t\tif (down)\n\t\t\tval = 0;\t//\tI_Error ();\n\t\telse\n\t\t\tval = 0;\t// released this frame\n\t}\n\tif (!impulsedown && !impulseup)\n\t{\n\t\tif (down)\n\t\t\tval = 1.0;\t// held the entire frame\n\t\telse\n\t\t\tval = 0;\t// up the entire frame\n\t}\n\tif (impulsedown && impulseup)\n\t{\n\t\tif (down)\n\t\t\tval = 0.75;\t// released and re-pressed this frame\n\t\telse\n\t\t\tval = 0.25;\t// pressed and released this frame\n\t}\n\n\tkey->state[pnum] &= 1;\t\t// clear impulses\n\t\n\treturn val;\n}\n\nvoid CL_ProxyMenuHook(char *command, kbutton_t *key)\n{\n\tif ((key->state[0] & 3) == 3)\t//2 is impulse down, 1 is held down\n\t{\n\t\tkey->state[0] = 0;\t\t// clear impulses\n\n\t\tCbuf_AddText(command, RESTRICT_DEFAULT);\n\t}\n}\n\nvoid CL_ProxyMenuHooks(void)\n{\n\tif (!prox_inmenu.ival)\n\t\treturn;\n\n\tCL_ProxyMenuHook(\"say proxy:menu down\\n\", &in_back);\n\tCL_ProxyMenuHook(\"say proxy:menu up\\n\", &in_forward);\n\n\tCL_ProxyMenuHook(\"say proxy:menu left\\n\", &in_left);\n\tCL_ProxyMenuHook(\"say proxy:menu right\\n\", &in_right);\n\n\tCL_ProxyMenuHook(\"say proxy:menu left\\n\", &in_moveleft);\n\tCL_ProxyMenuHook(\"say proxy:menu right\\n\", &in_moveright);\n\n\tCL_ProxyMenuHook(\"say proxy:menu use\\n\", &in_jump);\n}\n\n\n//==========================================================================\n\ncvar_t\tcl_upspeed = CVARF(\"cl_upspeed\",\"400\", CVAR_ARCHIVE);\ncvar_t\tcl_forwardspeed = CVARF(\"cl_forwardspeed\",\"400\", CVAR_ARCHIVE);\ncvar_t\tcl_backspeed = CVARFD(\"cl_backspeed\",\"\", CVAR_ARCHIVE, \"The base speed that you move backwards at. If empty, uses the value of cl_forwardspeed instead.\");\ncvar_t\tcl_sidespeed = CVARF(\"cl_sidespeed\",\"400\", CVAR_ARCHIVE);\n\ncvar_t\tcl_movespeedkey = CVAR(\"cl_movespeedkey\",\"2.0\");\n\ncvar_t\tcl_yawspeed = CVAR(\"cl_yawspeed\",\"140\");\ncvar_t\tcl_pitchspeed = CVAR(\"cl_pitchspeed\",\"150\");\n\ncvar_t\tcl_anglespeedkey = CVAR(\"cl_anglespeedkey\",\"1.5\");\n\n\n#define GATHERBIT(bname,bit)\tdo{if (bname.state[pnum] & 3)\t{bits |=   (1u<<(bit));} bname.state[pnum]\t&= ~2;}while(0)\n#define UNUSEDBUTTON(bnum)\t\tdo{if (in_button[bnum].state[pnum] & 3)\t{Con_Printf(\"+button%i is not supported on this protocol\\n\", bnum); } in_button[bnum].state[pnum]\t&= ~3;}while(0)\nvoid CL_GatherButtons (usercmd_t *cmd, int pnum)\n{\n\tunsigned int bits = 0;\n\tGATHERBIT(in_attack,\t\t0);\n#ifdef Q3CLIENT\n\tif (cls.protocol==CP_QUAKE3)\n\t{\t//quake3's buttons are nice and simple, buttonN -> bit|=(1<<N)\n\t\tint i;\n\t\tfor (i = 0; i < countof(in_button); i++)\n\t\t{\n\t\t\tGATHERBIT(in_button[i],\t\ti);\n\t\t}\n//\t\tbits |= 1<<1;\t//rtcw talking\n//\t\tbits |= 1<<4;\t//rtcw walking\n//\t\tbits |= 1<<7;\t//rtcw any key\n\t\tcmd->buttons = bits;\n\t\treturn;\n\t}\n#endif\n#ifdef Q2CLIENT\n\tif (cls.protocol==CP_QUAKE2 && cls.protocol_q2 == PROTOCOL_VERSION_Q2EX)\n\t{\t//buttons limited to 8 bits.\n\n\t\t//GATHERBIT(in_attack,\t\t0);\t//handled above\n\t\tGATHERBIT(in_use,\t\t\t1);\n\t\t//GATHERBIT(in_holster,\t\t2);\t//urgh\n\t\t//GATHERBIT(in_jump,\t\t3);\t//also set by +moveup\n\t\t//GATHERBIT(in_crouch,\t\t4);\t//also set by +movedown\n\t\t//GATHERBIT(in_unused,\t\t5);\n\t\t//GATHERBIT(in_unused,\t\t6);\n\t\t//GATHERBIT(in_any,\t\t\t7);\t//urgh\n\n\t\t//also let +button stuff map to bits.\n\t\tGATHERBIT(in_button[0],\t\t0);\n\t\tGATHERBIT(in_button[1],\t\t1);\n\t\tGATHERBIT(in_button[2],\t\t2);\n\t\tGATHERBIT(in_button[3],\t\t3);\n\t\tGATHERBIT(in_button[4],\t\t4);\n\t\tGATHERBIT(in_button[5],\t\t5);\n\t\tGATHERBIT(in_button[6],\t\t6);\n\t\tGATHERBIT(in_button[7],\t\t7);\n\n\t\tUNUSEDBUTTON(8);\n\t\tUNUSEDBUTTON(9);\n\t\tUNUSEDBUTTON(10);\n\t\tUNUSEDBUTTON(11);\n\t\tUNUSEDBUTTON(12);\n\t\tUNUSEDBUTTON(13);\n\t\tUNUSEDBUTTON(14);\n\t\tUNUSEDBUTTON(15);\n\t\tUNUSEDBUTTON(16);\n\t\tUNUSEDBUTTON(17);\n\t\tUNUSEDBUTTON(18);\n\t\tUNUSEDBUTTON(19);\n//\t\tUNUSEDBUTTON(20);\n\n\t\tcmd->buttons |= bits;\n\t\treturn;\n\t}\n#endif\n\n\t//quakec's numbered buttons make no sense and have no sane relation to bit numbers\n\tGATHERBIT(in_button[0],\t\t0);\n\tUNUSEDBUTTON(1);\t\t\t\t//officially, qc's button1 field is unusable (although qw folds button3 over to it)\n\tGATHERBIT(in_button[2],\t\t1);\tGATHERBIT(in_jump,\t\t\t1);\n\tGATHERBIT(in_button[3],\t\t2);\n\tGATHERBIT(in_button[4],\t\t3);\n\tGATHERBIT(in_button[5],\t\t4);\n\tGATHERBIT(in_button[6],\t\t5);\n\tGATHERBIT(in_button[7],\t\t6);\n\tGATHERBIT(in_button[8],\t\t7);\n\n\t//more inconsistencies, as required for dpcompat.\n\tGATHERBIT(in_use,\t\t\t(cls.protocol==CP_QUAKEWORLD)?4:8);\n\tbits |= (Key_Dest_Has(~kdm_game))\t?(1u<<9):0;\t\t//'buttonchat'. game is the lowest priority, anything else will take focus away. we consider that to mean 'chat' (although it could be menus).\n\tbits |= (cursor_active)\t\t\t\t?(1u<<10):0;\t//'cursor_active'. prydon cursor stuff.\n\tGATHERBIT(in_button[9],\t\t11);\n\tGATHERBIT(in_button[10],\t12);\n\tGATHERBIT(in_button[11],\t13);\n\tGATHERBIT(in_button[12],\t14);\n\tGATHERBIT(in_button[13],\t15);\n\n\tGATHERBIT(in_button[14],\t16);\n\tGATHERBIT(in_button[15],\t17);\n\tGATHERBIT(in_button[16],\t18);\n\tUNUSEDBUTTON(17);\n\tUNUSEDBUTTON(18);\n\tUNUSEDBUTTON(19);\n//\tUNUSEDBUTTON(20);\n\n//NQ protocol:\n//bit 30 means input_weapon field is sent. figured out at time of sending.\n//bit 31 means input_cursor* fields are sent. figured out at time of sending.\n\tcmd->buttons |= bits;\n}\n\nvoid CL_ClearPendingCommands(void)\n{\n\tsize_t seat, i;\n\tmemset(&cl_pendingcmd, 0, sizeof(cl_pendingcmd));\n\tfor (seat = 0; seat < countof(cl_pendingcmd); seat++)\n\t{\n\t\tfor (i=0 ; i<3 ; i++)\n\t\t\tcl_pendingcmd[seat].angles[i] = ((int)(cl.playerview[seat].viewangles[i]*65536.0/360)&65535);\n\t}\n}\n/*\n================\nCL_AdjustAngles\n\nMoves the local angle positions\n================\n*/\nvoid CL_AdjustAngles (int pnum, double frametime)\n{\n\tfloat\tspeed, quant;\n\tfloat\tup, down;\n\t\n\tif (in_speed.state[pnum] & 1)\n\t{\n\t\tif (ruleset_allow_frj.ival)\n\t\t\tspeed = cl_anglespeedkey.value;\n\t\telse\n\t\t\tspeed = bound(-2, cl_anglespeedkey.value, 2);\n\t}\n\telse\n\t\tspeed = 1;\n\n\tif (in_rotate && pnum==0 && !(cl.fpd & FPD_LIMIT_YAW))\n\t{\n\t\tquant = in_rotate;\n\t\tif (!cl_instantrotate.ival)\n\t\t\tquant *= speed*frametime;\n\t\tin_rotate -= quant;\n\t\tif (r_xflip.ival)\n\t\t\tquant *= -1;\n\t\tif (ruleset_allow_frj.ival)\n\t\t\tcl.playerview[pnum].viewanglechange[YAW] += quant;\n\t}\n\n\tif (!(in_strafe.state[pnum] & 1))\n\t{\n\t\tquant = cl_yawspeed.value*speed;\n\t\tif ((cl.fpd & FPD_LIMIT_YAW) || !ruleset_allow_frj.ival)\n\t\t\tquant = bound(-900, quant, 900);\n\t\tquant *= frametime;\n\t\tif (r_xflip.ival)\n\t\t\tquant *= -1;\n\t\tcl.playerview[pnum].viewanglechange[YAW] -= quant * CL_KeyState (&in_right, pnum, false);\n\t\tcl.playerview[pnum].viewanglechange[YAW] += quant * CL_KeyState (&in_left, pnum, false);\n\t}\n\tif (in_klook.state[pnum] & 1)\n\t{\n\t\tV_StopPitchDrift (&cl.playerview[pnum]);\n\t\tquant = cl_pitchspeed.value*speed;\n\t\tif ((cl.fpd & FPD_LIMIT_PITCH) || !ruleset_allow_frj.ival)\n\t\t\tquant = bound(-700, quant, 700);\n\t\tquant *= frametime;\n\t\tcl.playerview[pnum].viewanglechange[PITCH] -= quant * CL_KeyState (&in_forward, pnum, false);\n\t\tcl.playerview[pnum].viewanglechange[PITCH] += quant * CL_KeyState (&in_back, pnum, false);\n\t}\n\n\tquant = cl_rollspeed.value*speed;\n\tquant *= frametime;\n\tcl.playerview[pnum].viewanglechange[ROLL] -= quant * CL_KeyState (&in_rollleft, pnum, false);\n\tcl.playerview[pnum].viewanglechange[ROLL] += quant * CL_KeyState (&in_rollright, pnum, false);\n\t\n\tup = CL_KeyState (&in_lookup, pnum, false);\n\tdown = CL_KeyState(&in_lookdown, pnum, false);\n\n\tquant = cl_pitchspeed.value*speed;\n\tif ((cl.fpd & FPD_LIMIT_PITCH) || !ruleset_allow_frj.ival)\n\t\tquant = bound(-700, quant, 700);\n\tquant *= frametime;\n\tcl.playerview[pnum].viewanglechange[PITCH] -= quant * up;\n\tcl.playerview[pnum].viewanglechange[PITCH] += quant * down;\n\n\tif (up || down)\n\t\tV_StopPitchDrift (&cl.playerview[pnum]);\t\n}\n\n/*\n================\nCL_BaseMove\n\nSend the intended movement message to the server\n================\n*/\nstatic void CL_BaseMove (vec3_t moves, int pnum)\n{\n\tfloat fwdspeed = cl_forwardspeed.value;\n\tfloat sidespeed = cl_sidespeed.value;\n\tfloat backspeed = (*cl_backspeed.string?cl_backspeed.value:cl_forwardspeed.value);\n\tfloat upspeed = (*cl_backspeed.string?cl_backspeed.value:cl_forwardspeed.value);\n\tfloat scale = 1;\n//\n// adjust for speed key\n//\n#ifdef HEXEN2\n\textern qboolean\tsbar_hexen2;\n\tif (sbar_hexen2)\n\t{\t//hexen2 is a bit different. forwardspeed is treated as something of a boolean and we need to be able to cope with the boots-of-speed without forcing it always. not really sure why that's clientside instead of serverside, but oh well. evilness.\n\t\tscale = cl.playerview[pnum].statsf[STAT_H2_HASTED];\n\t\tif (!scale)\n\t\t\tscale = 1;\n\t\tif (((in_speed.state[pnum] & 1) ^ (cl_run.ival || fwdspeed > 200))\n\t\t\t&& scale <= 1)\t//don't go super fast with speed boots.\n\t\t\tscale *= cl_movespeedkey.value;\n\t\tfwdspeed = backspeed = 200;\n\t\tsidespeed = 225;\n\t}\n\telse\n#endif\n\tif ((in_speed.state[pnum] & 1) ^ cl_run.ival)\n\t\tscale *= cl_movespeedkey.value;\n\n\tif (r_xflip.ival)\n\t\tsidespeed *= -1;\n\n\tmoves[0] = 0;\n\tif (! (in_klook.state[pnum] & 1) )\n\t{\n\t\tmoves[0] += (fwdspeed * CL_KeyState (&in_forward, pnum, true) -\n\t\t\t\t\tbackspeed * CL_KeyState (&in_back, pnum, true));\n\t}\n\tmoves[1] = sidespeed * (CL_KeyState (&in_moveright, pnum, true) - CL_KeyState (&in_moveleft, pnum, true)) * (in_xflip.ival?-1:1);\n\tif (in_strafe.state[pnum] & 1)\n\t\tmoves[1] += sidespeed * (CL_KeyState (&in_right, pnum, true) - CL_KeyState (&in_left, pnum, true)) * (in_xflip.ival?-1:1);\n\tmoves[2] = upspeed * (CL_KeyState (&in_up, pnum, true) - CL_KeyState (&in_down, pnum, true));\n\n\tmoves[0] *= scale;\n\tmoves[1] *= scale;\n\tmoves[2] *= scale;\n}\n\nvoid CL_ClampPitch (int pnum, float frametime)\n{\n\tfloat mat[16];\n\tfloat roll;\n\tplayerview_t *pv = &cl.playerview[pnum];\n\n\tif (cl.intermissionmode != IM_NONE)\n\t{\n\t\tmemset(pv->viewanglechange, 0, sizeof(pv->viewanglechange));\n\t\treturn;\n\t}\n \tif (pv->pmovetype == PM_6DOF)\n\t{\n//\t\tvec3_t impact;\n//\t\tvec3_t norm;\n\t\tfloat mat2[16];\n//\t\tvec3_t cross;\n\t\tvec3_t view[4];\n//\t\tfloat dot;\n\t\tAngleVectors(pv->viewangles, view[0], view[1], view[2]);\n\t\tMatrix4x4_RM_FromVectors(mat, view[0], view[1], view[2], vec3_origin);\n\n\t\tMatrix4_Multiply(Matrix4x4_CM_NewRotation(-pv->viewanglechange[PITCH], 0, 1, 0), mat, mat2);\n\t\tMatrix4_Multiply(Matrix4x4_CM_NewRotation(pv->viewanglechange[YAW], 0, 0, 1), mat2, mat);\n#if 1\n\t\t//roll angles\n\t\tMatrix4_Multiply(Matrix4x4_CM_NewRotation(pv->viewanglechange[ROLL], 1, 0, 0), mat, mat2);\n#else\n\t\t//auto-roll\n\t\tMatrix3x4_RM_ToVectors(mat, view[0], view[1], view[2], view[3]);\n\n\t\tVectorMA(pv->simorg, -48, view[2], view[3]);\n\t\tif (!TraceLineN(pv->simorg, view[3], impact, norm))\n\t\t{\n\t\t\tnorm[0] = 0;\n\t\t\tnorm[1] = 0;\n\t\t\tnorm[2] = 1;\n\t\t}\n\n\t\t/*keep the roll relative to the 'ground'*/\n\t\tCrossProduct(norm, view[2], cross);\n\t\tdot = DotProduct(view[0], cross);\n\t\troll = timestep * 360 * -(dot);\n\t\tMatrix4_Multiply(Matrix4x4_CM_NewRotation(roll, 1, 0, 0), mat, mat2);\n#endif\n\t\tMatrix3x4_RM_ToVectors(mat2, view[0], view[1], view[2], view[3]);\n\t\tVectorAngles(view[0], view[2], pv->viewangles, false);\n\t\tVectorClear(pv->viewanglechange);\n\n\t\t//fixme: in_vraim stuff\n\t\tVectorCopy(pv->viewangles, pv->aimangles);\n\n\t\treturn;\n\t}\n#if 1\n\tif ((pv->gravitydir[2] != -1 || pv->viewangles[2]))\n\t{\n\t\tfloat surfm[16], invsurfm[16];\n\t\tfloat viewm[16];\n\t\tvec3_t view[4];\n\t\tvec3_t surf[3];\n\t\tvec3_t vang;\n\t\tvoid PerpendicularVector( vec3_t dst, const vec3_t src );\n\n\t\t/*calc current view matrix relative to the surface*/\n\t\tAngleVectors(pv->viewangles, view[0], view[1], view[2]);\n\t\tVectorNegate(view[1], view[1]);\n\n\t\t/*calculate the surface axis with up from the pmove code and right/forwards relative to the player's directions*/\n\t\tif (!pv->gravitydir[0] && !pv->gravitydir[1] && !pv->gravitydir[2])\n\t\t{\n\t\t\tVectorSet(surf[2], 0, 0, 1);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tVectorNegate(pv->gravitydir, surf[2]);\n\t\t}\n\t\tVectorNormalize(surf[2]);\n\t\tPerpendicularVector(surf[1], surf[2]);\n\t\tVectorNormalize(surf[1]);\n\t\tCrossProduct(surf[2], surf[1], surf[0]);\n\t\tVectorNegate(surf[0], surf[0]);\n\t\tVectorNormalize(surf[0]);\n\t\tMatrix4x4_RM_FromVectors(surfm, surf[0], surf[1], surf[2], vec3_origin);\n\t\tMatrix3x4_InvertTo4x4_Simple(surfm, invsurfm);\n\n\t\t/*calc current view matrix relative to the surface*/\n\t\tMatrix4x4_RM_FromVectors(viewm, view[0], view[1], view[2], vec3_origin);\n\t\tMatrix4_Multiply(viewm, invsurfm, mat);\n\t\t/*convert that back to angles*/\n\t\tMatrix3x4_RM_ToVectors(mat, view[0], view[1], view[2], view[3]);\n\t\tVectorAngles(view[0], view[2], vang, false);\n\n\t\t/*edit it*/\n\t\tvang[PITCH] += pv->viewanglechange[PITCH];\n\t\tvang[YAW] += pv->viewanglechange[YAW];\n\t\tif (vang[PITCH] <= -180)\n\t\t\tvang[PITCH] += 360;\n\t\tif (vang[PITCH] > 180)\n\t\t\tvang[PITCH] -= 360;\n\t\tif (vang[ROLL] >= 180)\n\t\t\tvang[ROLL] -= 360;\n\t\tif (vang[ROLL] < -180)\n\t\t\tvang[ROLL] += 360;\n\n\t\t/*keep the player looking relative to their ground (smoothlyish)*/\n\t\tif (!vang[ROLL])\n\t\t{\n\t\t\tif (!pv->viewanglechange[PITCH] && !pv->viewanglechange[YAW] && !pv->viewanglechange[ROLL])\n\t\t\t{\n\t\t\t\tVectorCopy(pv->viewangles, pv->aimangles);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (fabs(vang[ROLL]) < frametime*180)\n\t\t\t\tvang[ROLL] = 0;\n\t\t\telse if (vang[ROLL] > 0)\n\t\t\t{\n//\t\t\t\tCon_Printf(\"Roll %f\\n\", vang[ROLL]);\n\t\t\t\tvang[ROLL] -= frametime*180;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n//\t\t\t\tCon_Printf(\"Roll %f\\n\", vang[ROLL]);\n\t\t\t\tvang[ROLL] += frametime*180;\n\t\t\t}\n\t\t}\n\t\tVectorClear(pv->viewanglechange);\n\t\t/*clamp pitch*/\n\t\tif (vang[PITCH] > cl.maxpitch)\n\t\t\tvang[PITCH] = cl.maxpitch;\n\t\tif (vang[PITCH] < cl.minpitch)\n\t\t\tvang[PITCH] = cl.minpitch;\n\n\t\t/*turn those angles back to a matrix*/\n\t\tAngleVectors(vang, view[0], view[1], view[2]);\n\t\tVectorNegate(view[1], view[1]);\n\t\tMatrix4x4_RM_FromVectors(mat, view[0], view[1], view[2], vec3_origin);\n\t\t/*rotate back into world space*/\n\t\tMatrix4_Multiply(mat, surfm, viewm);\n\t\t/*and figure out the final result*/\n\t\tMatrix3x4_RM_ToVectors(viewm, view[0], view[1], view[2], view[3]);\n\t\tVectorAngles(view[0], view[2], cl.playerview[pnum].viewangles, false);\n\n\t\tif (pv->viewangles[ROLL] >= 360)\n\t\t\tpv->viewangles[ROLL] -= 360;\n\t\tif (pv->viewangles[ROLL] < 0)\n\t\t\tpv->viewangles[ROLL] += 360;\n\t\tif (pv->viewangles[PITCH] < -180)\n\t\t\tpv->viewangles[PITCH] += 360;\n\n\t\t//fixme: in_vraim stuff\n\t\tVectorCopy(pv->viewangles, pv->aimangles);\n\t\treturn;\n\t}\n#endif\n\tpv->viewangles[PITCH] += pv->viewanglechange[PITCH];\n\tpv->viewangles[YAW] += pv->viewanglechange[YAW];\n\tpv->viewangles[ROLL] += pv->viewanglechange[ROLL];\n\tpv->viewangles[YAW] /= 360;\n\tpv->viewangles[YAW] = pv->viewangles[YAW] - (int)pv->viewangles[YAW];\n\tpv->viewangles[YAW] *= 360;\n\tVectorClear(pv->viewanglechange);\n\n\tif (in_vraim.ival && (pv->vrdev[VRDEV_HEAD].status&VRSTATUS_ANG))\n\t{\t//overcomplicated code to replace the pitch+roll angles and add to the yaw angle.\n#if 0\n\t\tmatrix3x4 base, head, res;\n\t\tvec3_t na = {0, pv->viewangles[YAW], 0};\n\t\tvec3_t f,l,u,o;\n\t\tMatrix3x4_RM_FromAngles(na, vec3_origin, base[0]);\n\t\tfor (i=0 ; i<3 ; i++)\n\t\t\tna[i] = SHORT2ANGLE(pv->vrdev[VRDEV_HEAD].angles[i]);\n\t\tMatrix3x4_RM_FromAngles(na, pv->vrdev[VRDEV_HEAD].origin, head[0]);\n\t\tMatrix3x4_Multiply(head[0], base[0], res[0]);\n\t\tMatrix3x4_RM_ToVectors(res[0], f,l,u,o);\n\t\tVectorAngles(f,u,pv->aimangles,false);\n\t\tfor (i=0 ; i<3 ; i++)\n\t\t\tcmd->angles[i] = ANGLE2SHORT(na[i]);\n#else\n\t\tpv->aimangles[PITCH] = SHORT2ANGLE(pv->vrdev[VRDEV_HEAD].angles[PITCH]);\n\t\tpv->aimangles[YAW]   = SHORT2ANGLE(pv->vrdev[VRDEV_HEAD].angles[YAW]) + pv->viewangles[YAW];\n\t\tpv->aimangles[ROLL]  = SHORT2ANGLE(pv->vrdev[VRDEV_HEAD].angles[ROLL]);\n#endif\n\t}\n\telse\n\t\tVectorCopy(pv->viewangles, pv->aimangles);\n\n#ifdef Q2CLIENT\n\tif (cls.protocol == CP_QUAKE2)\n\t{\n\t\tfloat\tpitch;\n\t\tpitch = SHORT2ANGLE(cl.q2frame.seat[pnum].playerstate.pmove.delta_angles[PITCH]);\n\t\tif (pitch > 180)\n\t\t\tpitch -= 360;\n\n\t\tif (pv->viewangles[PITCH] + pitch < -360)\n\t\t\tpv->viewangles[PITCH] += 360; // wrapped\n\t\tif (pv->viewangles[PITCH] + pitch > 360)\n\t\t\tpv->viewangles[PITCH] -= 360; // wrapped\n\n\t\tif (pv->viewangles[PITCH] + pitch > cl.maxpitch)\n\t\t\tpv->viewangles[PITCH] = cl.maxpitch - pitch;\n\t\tif (pv->viewangles[PITCH] + pitch < cl.minpitch)\n\t\t\tpv->viewangles[PITCH] = cl.minpitch - pitch;\n\t}\n\telse\n#endif\n#ifdef Q3CLIENT\n\t\tif (cls.protocol == CP_QUAKE3)\t//q3 expects the cgame to do it\n\t{\n\t\t\t//no-op\n\t}\n\telse\n#endif\n\t{\n\t\tif (pv->viewangles[PITCH] > cl.maxpitch)\n\t\t\tpv->viewangles[PITCH] = cl.maxpitch;\n\t\tif (pv->viewangles[PITCH] < cl.minpitch)\n\t\t\tpv->viewangles[PITCH] = cl.minpitch;\n\n\t\tif (pv->aimangles[PITCH] > cl.maxpitch)\n\t\t\tpv->aimangles[PITCH] = cl.maxpitch;\n\t\tif (pv->aimangles[PITCH] < cl.minpitch)\n\t\t\tpv->aimangles[PITCH] = cl.minpitch;\n\t} \n\n//\tif (cl.viewangles[pnum][ROLL] > 50)\n//\t\tcl.viewangles[pnum][ROLL] = 50;\n//\tif (cl.viewangles[pnum][ROLL] < -50)\n//\t\tcl.viewangles[pnum][ROLL] = -50;\n\troll = frametime*pv->viewangles[ROLL]*30;\n\tif ((pv->viewangles[ROLL]-roll < 0) != (pv->viewangles[ROLL]<0))\n\t\tpv->viewangles[ROLL] = 0;\n\telse\n\t\tpv->viewangles[ROLL] -= frametime*pv->viewangles[ROLL]*3;\n}\n\n/*\n==============\nCL_FinishMove\n==============\n*/\nstatic void CL_FinishMove (usercmd_t *cmd, int pnum)\n{\n\tint\ti;\n\n\tCL_ClampPitch(pnum, 0);\n\n//\n// always dump the first two message, because it may contain leftover inputs\n// from the last level\n//\n\tif (cl.movesequence <= 2)\n\t{\n\t\tcmd->buttons = 0;\n\t\treturn;\n\t}\n//\n// figure button bits\n//\n\n\tCL_GatherButtons(cmd, pnum);\n\n\tfor (i=0 ; i<3 ; i++)\n\t\tcmd->angles[i] = (int)(ANGLE2SHORT(cl.playerview[pnum].aimangles[i]))&65535;\n\tcmd->vr[VRDEV_LEFT] = cl.playerview[pnum].vrdev[VRDEV_LEFT];\n\tcmd->vr[VRDEV_RIGHT] = cl.playerview[pnum].vrdev[VRDEV_RIGHT];\n\tcmd->vr[VRDEV_HEAD] = cl.playerview[pnum].vrdev[VRDEV_HEAD];\n\n\tif (in_impulsespending[pnum] && !cl.paused)\n\t{\n\t\tcmd->impulse = in_impulse[pnum][(in_nextimpulse[pnum])%IN_IMPULSECACHE];\n\t\tin_nextimpulse[pnum]++;\n\t\tin_impulsespending[pnum]--;\n\t}\n\telse\n\t\tcmd->impulse = 0;\n}\n\n\nstatic void CL_AccumlateInput(int plnum, float frametime/*extra contribution*/, float framemsecs/*total accumulated*/)\n{\n\tusercmd_t *cmd = &cl_pendingcmd[plnum];\n\tint i;\n\tstatic vec3_t mousemovements[MAX_SPLITS];\n\tvec3_t newmoves;\n\n\tfloat nscale = framemsecs?framemsecs / (framemsecs+cmd->msec):0;\n\tfloat oscale = 1 - nscale;\n\tunsigned int st;\n\n\tCL_BaseMove (newmoves, plnum);\n\n\tCL_AdjustAngles (plnum, frametime);\n\tif (!cmd->msec)\n\t\tVectorClear(mousemovements[plnum]);\n\tIN_Move (mousemovements[plnum], newmoves, plnum, frametime);\n\tCL_ClampPitch(plnum, frametime);\n\n\tfor (i=0 ; i<3 ; i++)\n\t\tcmd->angles[i] = ((int)(cl.playerview[plnum].viewangles[i]*65536.0/360)&65535);\n\n\tcmd->fservertime = cl.servertime;\n\tcmd->servertime = cl.time*1000;\n#ifdef CSQC_DAT\n\tcmd->fclienttime = realtime - cl.mapstarttime;\n#endif\n\n\tcmd->forwardmove = bound(-32768, cmd->forwardmove*oscale + newmoves[0]*nscale + mousemovements[plnum][0], 32767);\n\tcmd->sidemove = bound(-32768, cmd->sidemove*oscale + newmoves[1]*nscale + mousemovements[plnum][1], 32767);\n\tcmd->upmove = bound(-32768, cmd->upmove*oscale + newmoves[2]*nscale + mousemovements[plnum][2], 32767);\n\n\tif (!cmd->msec && framemsecs)\n\t{\n\t\tCL_GatherButtons(cmd, plnum);\t//buttons are from the initial state. don't blend them.\n\n\t\tCL_FinishMove(cmd, plnum);\n\t\tCbuf_Waited();\t//its okay to stop waiting now\n\t}\n\tcmd->msec = framemsecs;\n\n\tif (cl.movesequence >= 1)\n\t{\t//fix up the servertime value to make sure our msecs are actually correct.\n\t\tst = cl.outframes[(cl.movesequence-1)&UPDATE_MASK].cmd[plnum].servertime + (cmd->msec);\t//round it.\n\t\tif (abs((int)st-(int)cmd->servertime) < 50)\n\t\t{\n\t\t\tcmd->servertime = st;\n\t\t\tcmd->fservertime = (double)st/1000.0;\n\t\t}\n\t}\n\n\t// if we are spectator, try autocam\n//\tif (cl.spectator)\n\tCam_Track(&cl.playerview[plnum], &cl_pendingcmd[plnum]);\n\tCam_FinishMove(&cl.playerview[plnum], &cl_pendingcmd[plnum]);\n}\n\n\nstatic qboolean CLFTE_SendVRCmd (sizebuf_t *buf, unsigned int seats)\n{\n\t//compute the delay between receiving the frame we're acking and when we're sending the new frame\n\tunsigned int cldelay = (realtime - cl.inframes[cls.netchan.incoming_sequence&UPDATE_MASK].receivedtime)*10000;\t//this is to report actual network latency instead of just reporting our packet rate (framerates may still be a factor).\n\tunsigned int lost = CL_CalcNet(r_netgraph.value);\t//report packetloss\n\tunsigned int flags = 0;\n\tunsigned int first = cl.ackedmovesequence+1;\t//no point resending that which has already been acked.\n\tunsigned int last = cl.movesequence+1;\t\t\t//we want to ignore moveseq itself\n\tunsigned int frame, seat, count, i;\n\tconst usercmd_t *from, *to;\n\tqboolean dontdrop = false;\n\tif (first > last)\n\t\tfirst = last-1;\n\tif (first < last-(countof(cl.outframes)-2))\n\t\tfirst = last-(countof(cl.outframes)-2);\n\tif (first < 1)\n\t\tfirst = 1;\n\tif (last < first)\n\t\tcount = 0;\n\telse\n\t\tcount = last-first;\n\tif (count > max(1,cl_c2sMaxRedundancy.ival))\n\t\tcount = max(1,cl_c2sMaxRedundancy.ival);\n\tif (cl.inframes[cls.netchan.incoming_sequence&UPDATE_MASK].receivedtime<0)\n\t\tcldelay = 0;\t//erk?\n\n\tMSG_WriteByte (buf, clcfte_move);\n\n#ifdef NQPROT\n\tif (cls.protocol == CP_NETQUAKE)\t//nq uses fully separate packet+movement sequences (unlike qw).\n\t\tMSG_WriteShort(buf, (last-1)&0xffff);\n#endif\n\n\tif (seats!=1)\n\t\tflags |= VRM_SEATS;\n\tif (lost)\n\t\tflags |= VRM_LOSS;\n\tif (cldelay)\n\t\tflags |= VRM_DELAY;\n\tif (count!=3)\n\t\tflags |= VRM_FRAMES;\n\tif (cl.numackframes)\n\t\tflags |= VRM_ACKS;\n\tMSG_WriteUInt64 (buf, flags);\n\n\tif (flags & VRM_SEATS)\n\t\tMSG_WriteUInt64 (buf, seats);\n\tif (flags & VRM_FRAMES)\n\t\tMSG_WriteUInt64 (buf, count);\n\tif (flags & VRM_LOSS)\n\t\tMSG_WriteByte (buf, (qbyte)lost);\n\tif (flags & VRM_DELAY)\n\t\tMSG_WriteByte (buf, bound(0,cldelay,255));\t//a byte should always be enough for any framerate above 40, and we don't want peole to be able to lie so easily.\n\tif (flags & VRM_ACKS)\n\t{\n\t\tMSG_WriteUInt64(buf, cl.numackframes);\n\t\tfor (i = 0; i < cl.numackframes; i++)\n\t\t\tMSG_WriteLong(buf, cl.ackframes[i]);\n\t\tcl.numackframes = 0;\n\t}\n\n\tfor (seat = 0; seat < seats; seat++)\n\t{\n\t\tfrom = &nullcmd;\n\t\tfor (frame = last-count; frame < last; frame++)\n\t\t{\n\t\t\tto = &cl.outframes[frame&UPDATE_MASK].cmd[seat];\n\t\t\tMSGFTE_WriteDeltaUsercmd (buf, cl.playerview[seat].baseangles, from, to);\n\t\t\tif (to->impulse && (int)(last-frame)>=cl_c2sImpulseBackup.ival)\n\t\t\t\tdontdrop = true;\n\t\t\tfrom = to;\n\t\t}\n\t}\n\treturn dontdrop;\n}\n\n\nvoid CL_UpdatePrydonCursor(usercmd_t *from, int pnum)\n{\n\tint hit;\n\tvec3_t cursor_end;\n\n\tvec3_t temp;\n\tvec3_t cursor_impact_normal;\n\n\tcursor_active = true;\n\n\tif (!cl_prydoncursor.ival)\n\t{\t//center the cursor\n\t\tfrom->cursor_screen[0] = 0;\n\t\tfrom->cursor_screen[1] = 0;\n\t}\n\telse\n\t{\n\t\tfrom->cursor_screen[0] = mousecursor_x/(vid.width/2.0f) - 1;\n\t\tfrom->cursor_screen[1] = mousecursor_y/(vid.height/2.0f) - 1;\n\t\tif (from->cursor_screen[0] < -1)\n\t\t\tfrom->cursor_screen[0] = -1;\n\t\tif (from->cursor_screen[1] < -1)\n\t\t\tfrom->cursor_screen[1] = -1;\n\n\t\tif (from->cursor_screen[0] > 1)\n\t\t\tfrom->cursor_screen[0] = 1;\n\t\tif (from->cursor_screen[1] > 1)\n\t\t\tfrom->cursor_screen[1] = 1;\n\t}\n\n\tVectorClear(from->cursor_start);\n\ttemp[0] = (from->cursor_screen[0]+1)/2;\n\ttemp[1] = (-from->cursor_screen[1]+1)/2;\n\ttemp[2] = 1;\n\n\tVectorCopy(r_origin, from->cursor_start);\n\tMatrix4x4_CM_UnProject(temp, cursor_end, cl.playerview[pnum].viewangles, from->cursor_start, r_refdef.fov_x, r_refdef.fov_y);\n\n\tCL_SetSolidEntities();\n\t//don't bother with players, they don't exist in NQ...\n\n\tCL_TraceLine(from->cursor_start, cursor_end, from->cursor_impact, cursor_impact_normal, &hit);\n\tif (hit>0)\n\t\tfrom->cursor_entitynumber = hit;\n\telse if (hit < 0)\n\t\tfrom->cursor_entitynumber = 0;\t//FIXME: ask csqc for the entity's entnum\n\telse\n\t\tfrom->cursor_entitynumber = 0;\n\n//\tP_RunParticleEffect(cursor_impact, vec3_origin, 15, 16);\n}\n\n#ifdef NQPROT\nvoid CLNQ_SendMove (usercmd_t *cmd, int pnum, sizebuf_t *buf)\n{\n\tint i;\n\tunsigned int bits;\n\n\tif (cls.demoplayback!=DPB_NONE)\n\t\treturn;\t//err... don't bother... :)\n//\n// always dump the first two message, because it may contain leftover inputs\n// from the last level\n//\n\tif (cl.movesequence <= 2 || cls.state == ca_connected)\n\t{\n\t\tMSG_WriteByte (buf, clc_nop);\n\t\treturn;\n\t}\n\n\tif (cls.qex)\n\t{\n\t\tMSG_WriteByte (buf, clc_delta);\n\t\tMSG_WriteULEB128(buf, cl.movesequence);\n\t}\n\n\tMSG_WriteByte (buf, clc_move);\n\n\tif (cls.protocol_nq >= CPNQ_DP7)\n\t{\n\t\tif (!cl_movement.ival)\n\t\t\tMSG_WriteLong(buf, 0);\n\t\telse\n\t\t\tMSG_WriteLong(buf, cl.movesequence);\n\t}\n\telse if (cls.fteprotocolextensions2 & PEXT2_PREDINFO)\n\t\tMSG_WriteShort(buf, cl.movesequence&0xffff);\n\n\tMSG_WriteFloat (buf, cmd->fservertime);\t// use latest time. because ping reports!\n\n\tif (cls.qex)\n\t\tMSG_WriteByte(buf, 1);\n\n\tfor (i=0 ; i<3 ; i++)\n\t{\n\t\tif (cls.protocol_nq == CPNQ_FITZ666 || (cls.proquake_angles_hack && buf->prim.anglesize <= 1))\n\t\t{\n\t\t\t//fitz/proquake protocols are always 16bit for this angle and 8bit elsewhere. rmq is always at least 16bit\n\t\t\t//the above logic should satify everything.\n\t\t\tMSG_WriteAngle16 (buf, cl.playerview[pnum].viewangles[i]);\n\t\t}\n\t\telse\n\t\t\tMSG_WriteAngle (buf, cl.playerview[pnum].viewangles[i]);\n\t}\n\n\tMSG_WriteShort (buf, cmd->forwardmove);\n\tMSG_WriteShort (buf, cmd->sidemove);\n\tMSG_WriteShort (buf, cmd->upmove);\n\n\tbits = cmd->buttons;\n\tif (cls.fteprotocolextensions2 & PEXT2_PRYDONCURSOR)\n\t{\n\t\tif (cmd->cursor_screen[0] || cmd->cursor_screen[1] ||\n\t\t\tcmd->cursor_start[0] || cmd->cursor_start[1] || cmd->cursor_start[2] ||\n\t\t\tcmd->cursor_impact[0] || cmd->cursor_impact[1] || cmd->cursor_impact[2] ||\n\t\t\tcmd->cursor_entitynumber)\n\t\t\tbits |= (1u<<31);\t//set it if there's actually something to send.\n\t\tMSG_WriteLong (buf, bits);\n\t}\n\telse if (cls.protocol_nq >= CPNQ_DP6)\n\t{\n\t\tMSG_WriteLong (buf, bits);\n\t\tbits |= (1u<<31);\t//unconditionally set it (without writing it)\n\t}\n\telse\n\t\tMSG_WriteByte (buf, cmd->buttons);\n\tMSG_WriteByte (buf, cmd->impulse);\n\n\tif (bits & (1u<<31))\n\t{\n\t\tMSG_WriteShort (buf, cmd->cursor_screen[0] * 32767.0f);\n\t\tMSG_WriteShort (buf, cmd->cursor_screen[1] * 32767.0f);\n\t\tMSG_WriteFloat (buf, cmd->cursor_start[0]);\n\t\tMSG_WriteFloat (buf, cmd->cursor_start[1]);\n\t\tMSG_WriteFloat (buf, cmd->cursor_start[2]);\n\t\tMSG_WriteFloat (buf, cmd->cursor_impact[0]);\n\t\tMSG_WriteFloat (buf, cmd->cursor_impact[1]);\n\t\tMSG_WriteFloat (buf, cmd->cursor_impact[2]);\n\t\tMSG_WriteEntity (buf, cmd->cursor_entitynumber);\n\t}\n}\n\nvoid QDECL Name_Callback(struct cvar_s *var, char *oldvalue)\n{\n\tif (cls.state <= ca_connected)\n\t\treturn;\n\n\tif (cls.protocol != CP_NETQUAKE)\n\t\treturn;\n\n\tCL_SendClientCommand(true, \"name \\\"%s\\\"\\n\", var->string);\n}\n\nvoid CLNQ_SendCmd(sizebuf_t *buf)\n{\n\tint i;\n\tint seat;\n\tusercmd_t *cmd;\n\n\ti = cl.movesequence & UPDATE_MASK;\n\tcl.outframes[i].senttime = realtime;\n\tcl.outframes[i].latency = -1;\n\tcl.outframes[i].server_message_num = cl.validsequence;\n\tcl.outframes[i].cmd_sequence = cl.movesequence;\n\tcl.outframes[i].sentgametime = cl.movesequence_time;\n\n\tfor (seat = 0; seat < cl.splitclients; seat++)\n\t{\n\t\tcmd = &cl.outframes[i].cmd[seat];\n\t\t*cmd = cl_pendingcmd[seat];\n\t\tcmd->fservertime = cl.movesequence_time;\n//\t\tcmd->msec = (cl.time - cl.outframes[(i-1)&UPDATE_MASK].sentgametime)*1000;\n#ifdef CSQC_DAT\n\t\tCSQC_Input_Frame(seat, cmd);\n#endif\n\t}\n\tCL_ClearPendingCommands();\n\n\t//inputs are only sent once we receive an entity.\n\tif (cls.fteprotocolextensions2 & PEXT2_VRINPUTS)\n\t\tCLFTE_SendVRCmd(buf, (cls.signon != 4 || cls.state == ca_connected)?0:cl.splitclients);\n\telse\n\t{\n\t\tif (cls.signon == 4)\n\t\t{\n\t\t\tfor (seat = 0; seat < cl.splitclients; seat++)\n\t\t\t{\n\t\t\t\t// send the unreliable message\n\t//\t\t\tif (independantphysics[seat].impulse && !cls.netchan.message.cursize)\n\t//\t\t\t\tCLNQ_SendMove (&cl.outframes[i].cmd[seat], seat, &cls.netchan.message);\n\t//\t\t\telse\n\t\t\t\t\tCLNQ_SendMove (&cl.outframes[i].cmd[seat], seat, buf);\n\t\t\t}\n\t\t}\n\t\telse\n\t\t\tMSG_WriteByte (buf, clc_nop);\n\n\t\tfor (i = 0; i < cl.numackframes; i++)\n\t\t{\n\t\t\tMSG_WriteByte(buf, clcdp_ackframe);\n\t\t\tMSG_WriteLong(buf, cl.ackframes[i]);\n\t\t}\n\t\tcl.numackframes = 0;\n\t}\n}\n#else\nvoid Name_Callback(struct cvar_s *var, char *oldvalue)\n{\n\n}\n#endif\n\nfloat CL_FilterTime (double time, float wantfps, float limit, qboolean ignoreserver)\t//now returns the extra time not taken in this slot. Note that negative 1 means uncapped.\n{\n\tfloat fps, fpscap;\n\n\tif (cls.timedemo)\n\t\treturn -1;\n\n\tif (cls.protocol == CP_QUAKE3)\n\t\tignoreserver = true;\n\n\t/*ignore the server if we're playing demos, sending to the server only as replies, or if its meant to be disabled (netfps depending on where its called from)*/\n\tif (cls.demoplayback != DPB_NONE || (cls.protocol != CP_QUAKEWORLD && cls.protocol != CP_NETQUAKE) || ignoreserver)\n\t{\n\t\tif (!wantfps)\n\t\t\treturn -1;\n\t\tfps = max (1.0, wantfps);\n\t}\n\telse\n\t{\n\t\tfpscap = cls.maxfps ? max (30.0, cls.maxfps) : 0x7fff;\n#ifdef IRCCONNECT\n\t\tif (cls.netchan.remote_address.type == NA_IRC)\n\t\t\tfps = bound (0.1, wantfps, fpscap);\t//if we're connected via irc, allow a greatly reduced minimum cap\n\t\telse\n#endif\n\t\tif (wantfps < 1)\n\t\t\tfps = fpscap;\n\t\telse\n\t\t\tfps = bound (6.7, wantfps, fpscap);\t//we actually cap ourselves to 150msecs (1000/7 = 142)\n\t}\n\n\t//its not time yet\n\tif (ignoreserver)\n\t{\t//don't try to hold to milliseconds.\n\t\tif (time < 1000 / fps)\n\t\t\treturn 0;\n\t}\n\telse\n\t{\n\t\tif (time < ceil(1000 / fps))\n\t\t\treturn 0;\n\t}\n\n\t//clamp it if we have over 1.5 frame banked somehow\n\tif (limit && time - (1000 / fps) > (1000 / fps)*limit)\n\t\treturn (1000 / fps) * limit;\n\n\t//report how much spare time the caller now has\n\treturn time - (1000 / fps);\n}\n\ntypedef struct clcmdbuf_s {\n\tstruct clcmdbuf_s *next;\n\tint len;\n\tqboolean reliable;\n\tunsigned int seat;\n\tchar command[4];\t//this is dynamically allocated, so this is variably sized.\n} clcmdbuf_t;\nstatic clcmdbuf_t *clientcmdlist;\nvoid VARGS CL_SendSeatClientCommand(qboolean reliable, unsigned int seat, char *format, ...)\n{\n\tqboolean oldallow;\n\tva_list\t\targptr;\n\tchar\t\tstring[2048];\n\tclcmdbuf_t *buf, *prev;\n\n\tif (cls.demoplayback && !(cls.demoplayback == DPB_MVD && cls.demoeztv_ext))\n\t\treturn;\t//no point.\n\n\tva_start (argptr, format);\n\tQ_vsnprintfz (string,sizeof(string), format,argptr);\n\tva_end (argptr);\n\n#ifdef Q3CLIENT\n\tif (cls.protocol == CP_QUAKE3)\n\t{\n\t\tq3->cl.SendClientCommand(\"%s\", string);\n\t\treturn;\n\t}\n#endif\n\n\toldallow = CL_AllowIndependantSendCmd(false);\n\n\tbuf = Z_Malloc(sizeof(*buf)+strlen(string));\n\tstrcpy(buf->command, string);\n\tbuf->len = strlen(buf->command);\n\tbuf->reliable = reliable;\n\tbuf->seat = seat;\n\n\t//add to end of the list so that the first of the list is the first to be sent.\n\tif (!clientcmdlist)\n\t\tclientcmdlist = buf;\n\telse\n\t{\n\t\tfor (prev = clientcmdlist; prev->next; prev=prev->next)\n\t\t\t;\n\t\tprev->next = buf;\n\t}\n\n\tCL_AllowIndependantSendCmd(oldallow);\n}\nvoid VARGS CL_SendClientCommand(qboolean reliable, char *format, ...)\n{\n\tva_list\t\targptr;\n\tchar\t\tstring[2048];\n\n\tva_start (argptr, format);\n\tQ_vsnprintfz (string,sizeof(string), format,argptr);\n\tva_end (argptr);\n\n\tCL_SendSeatClientCommand(reliable, 0, \"%s\", string);\n}\n\n//sometimes a server will quickly restart twice.\n//connected clients will then receive TWO 'new' commands - both with the same servercount value.\n//the connection process then tries to proceed with two sets of commands until it fails catastrophically.\n//by attempting to strip out dupe commands we can usually avoid the issue\n//note that FTE servers track progress properly, so this is not an issue for us, but in the interests of compat with mvdsv...\n//however, FTE servers can send a little faster, so warnings about this can be awkward.\nint CL_RemoveClientCommands(char *command)\n{\n\tclcmdbuf_t *next, *first;\n\tint removed = 0;\n\tint len = strlen(command);\n\n\tCL_AllowIndependantSendCmd(false);\n\n\tif (!clientcmdlist)\n\t\treturn 0;\n\n\twhile(!strncmp(clientcmdlist->command, command, len))\n\t{\n\t\tnext = clientcmdlist->next;\n\t\tZ_Free(clientcmdlist);\n\t\tclientcmdlist=next;\n\t\tremoved++;\n\n\t\tif (!clientcmdlist)\n\t\t\treturn removed;\n\t}\n\tfirst = clientcmdlist;\n\twhile(first->next)\n\t{\n\t\tif (!strncmp(first->next->command, command, len))\n\t\t{\n\t\t\tnext = first->next->next;\n\t\t\tZ_Free(first->next);\n\t\t\tfirst->next = next;\n\t\t\tremoved++;\n\t\t}\n\t\telse\n\t\t\tfirst = first->next;\n\t}\n\n\treturn removed;\n}\n\nvoid CL_FlushClientCommands(void)\n{\n\tclcmdbuf_t *next;\n\tCL_AllowIndependantSendCmd(false);\n\n\twhile(clientcmdlist)\n\t{\n\t\tCon_DPrintf(\"Flushed command %s\\n\", clientcmdlist->command);\n\t\tnext = clientcmdlist->next;\n\t\tZ_Free(clientcmdlist);\n\t\tclientcmdlist=next;\n\t}\n}\n\nqboolean runningindepphys;\n#ifdef MULTITHREAD\nvoid *indeplock;\nvoid *indepthread;\n\nqboolean allowindepphys;\nqboolean CL_AllowIndependantSendCmd(qboolean allow)\n{\n\tqboolean ret = allowindepphys;\n\tif (!runningindepphys)\n\t\treturn ret;\n\n\tif (allowindepphys != allow && runningindepphys)\n\t{\n\t\tif (allow)\n\t\t\tSys_UnlockMutex(indeplock);\n\t\telse\n\t\t\tSys_LockMutex(indeplock);\n\t\tallowindepphys = allow;\n\t}\n\treturn ret;\n}\n\nint CL_IndepPhysicsThread(void *param)\n{\n\tdouble sleeptime;\n\tdouble fps;\n\tdouble time, lasttime;\n\tdouble spare;\n\tlasttime = Sys_DoubleTime();\n\twhile(runningindepphys)\n\t{\n\t\ttime = Sys_DoubleTime();\n\t\tspare = CL_FilterTime((time - lasttime)*1000, cl_netfps.value, 1.5, false);\n\t\tif (spare)\n\t\t{\n\t\t\ttime -= spare/1000.0f;\n\t\t\tSys_LockMutex(indeplock);\n\t\t\tif (cls.state)\n\t\t\t\tCL_SendCmd(time - lasttime, false);\n\t\t\tlasttime = time;\n\t\t\tSys_UnlockMutex(indeplock);\n\t\t}\n\n\t\tfps = cl_netfps.value;\n\t\tif (fps < 4)\n\t\t\tfps = 4;\n\t\twhile (fps < 100)\n\t\t\tfps*=2;\n\n\t\tsleeptime = 1/fps;\n\n\t\tSys_Sleep(sleeptime);\n\t}\n\treturn 0;\n}\n\nvoid CL_UseIndepPhysics(qboolean allow)\n{\n\tif (runningindepphys == allow)\n\t\treturn;\n\n\tif (allow)\n\t{\t//enable it\n\t\tindeplock = Sys_CreateMutex();\n\t\trunningindepphys = true;\n\n\t\tindepthread = Sys_CreateThread(\"indepphys\", CL_IndepPhysicsThread, NULL, THREADP_HIGHEST, 8192);\n\t\tallowindepphys = true;\n\t}\n\telse\n\t{\n\t\tCL_AllowIndependantSendCmd(true);\n\t\t//shut it down.\n\t\trunningindepphys = false;\t//tell thread to exit gracefully\n\t\tSys_WaitOnThread(indepthread);\n\t\tindepthread = NULL;\n\t\tSys_DestroyMutex(indeplock);\n\t\tindeplock = NULL;\n\t}\n}\n#else\nqboolean CL_AllowIndependantSendCmd(qboolean allow)\n{\n\treturn false;\n}\nvoid CL_UseIndepPhysics(qboolean allow)\n{\n}\n#endif\n\nvoid CL_UpdateSeats(void)\n{\n\tif (!cls.netchan.message.cursize && cl.allocated_client_slots > 1 && cls.state == ca_active && cl.splitclients && (cls.fteprotocolextensions & PEXT_SPLITSCREEN) && cl.worldmodel)\n\t{\n\t\tint targ = bound(1, cl_splitscreen.ival+1, MAX_SPLITS);\n\t\tif (cl.splitclients < targ)\n\t\t{\n\t\t\tchar *ver;\n\t\t\tchar buffer[2048];\n\t\t\tchar infostr[2048];\n\t\t\tinfobuf_t *info = &cls.userinfo[cl.splitclients];\n\n\t\t\t//some userinfos should always have a value\n\t\t\tif (!*InfoBuf_ValueForKey(info, \"name\"))\t//$name-2\n\t\t\t\tInfoBuf_SetKey(info, \"name\", va(\"%s-%i\", InfoBuf_ValueForKey(&cls.userinfo[0], \"name\"), cl.splitclients+1));\n\t\t\tif (cls.protocol != CP_QUAKE2)\n\t\t\t{\n\t\t\t\tif (!*InfoBuf_ValueForKey(info, \"team\"))\t//put players on the same team by default. this avoids team damage in coop, and if you're playing on the same computer then you probably want to be on the same team anyway.\n\t\t\t\t\tInfoBuf_SetKey(info, \"team\", InfoBuf_ValueForKey(&cls.userinfo[0], \"team\"));\n\t\t\t\tif (!*InfoBuf_ValueForKey(info, \"bottomcolor\"))\t//bottom colour implies team in nq\n\t\t\t\t\tInfoBuf_SetKey(info, \"bottomcolor\", InfoBuf_ValueForKey(&cls.userinfo[0], \"bottomcolor\"));\n\t\t\t\tif (!*InfoBuf_ValueForKey(info, \"topcolor\"))\t//should probably pick a random top colour or something\n\t\t\t\t\tInfoBuf_SetKey(info, \"topcolor\", InfoBuf_ValueForKey(&cls.userinfo[0], \"topcolor\"));\n\t\t\t}\n\t\t\tif (!*InfoBuf_ValueForKey(info, \"skin\"))\t//give players the same skin by default, because we can. q2 cares for teams. qw might as well (its not like anyone actually uses them thanks to enemy-skin forcing).\n\t\t\t\tInfoBuf_SetKey(info, \"skin\", InfoBuf_ValueForKey(&cls.userinfo[0], \"skin\"));\n\t\t\tInfoBuf_SetKey(info, \"chat\", \"\");\n\n#ifdef SVNREVISION\n\t\t\tif (strcmp(STRINGIFY(SVNREVISION), \"-\"))\n\t\t\t\tver = va(\"%s v%i.%02i %s\", DISTRIBUTION, FTE_VER_MAJOR, FTE_VER_MINOR, STRINGIFY(SVNREVISION));\n\t\t\telse\n#endif\n\t\t\t\tver = va(\"%s v%i.%02i\", DISTRIBUTION, FTE_VER_MAJOR, FTE_VER_MINOR);\n\t\t\tInfoBuf_SetStarKey(info, \"*ver\", ver);\n\t\t\tInfoBuf_ToString(info, infostr, sizeof(infostr), NULL, NULL, NULL, &cls.userinfosync, info);\n\n\t\t\tCL_SendClientCommand(true, \"addseat %i %s\", cl.splitclients+1, COM_QuotedString(infostr, buffer, sizeof(buffer), false));\n\t\t}\n\t\telse if (cl.splitclients > targ && targ >= 1)\n\t\t\tCL_SendClientCommand(true, \"addseat %i\", targ);\n\t}\n}\n\n\n/*\n=================\nCL_SendCmd\n=================\n*/\nqboolean CL_WriteDeltas (int plnum, sizebuf_t *buf)\n{\n\tint i;\n\tusercmd_t *cmd, *oldcmd;\n\tqboolean dontdrop = false;\n\n\n\ti = (cls.netchan.outgoing_sequence-2) & UPDATE_MASK;\n\tcmd = &cl.outframes[i].cmd[plnum];\n\tif (cl_c2sImpulseBackup.ival >= 2)\n\t\tdontdrop = dontdrop || cmd->impulse;\n\tMSGCL_WriteDeltaUsercmd (buf, &nullcmd, cmd);\n\toldcmd = cmd;\n\n\ti = (cls.netchan.outgoing_sequence-1) & UPDATE_MASK;\n\tif (cl_c2sImpulseBackup.ival >= 3)\n\t\tdontdrop = dontdrop || cmd->impulse;\n\tcmd = &cl.outframes[i].cmd[plnum];\n\tMSGCL_WriteDeltaUsercmd (buf, oldcmd, cmd);\n\toldcmd = cmd;\n\n\ti = (cls.netchan.outgoing_sequence) & UPDATE_MASK;\n\tif (cl_c2sImpulseBackup.ival >= 1)\n\t\tdontdrop = dontdrop || cmd->impulse;\n\tcmd = &cl.outframes[i].cmd[plnum];\n\tMSGCL_WriteDeltaUsercmd (buf, oldcmd, cmd);\n\n\treturn dontdrop;\n}\n\n#ifdef Q2CLIENT\nqboolean CLQ2_SendCmd (sizebuf_t *buf)\n{\n\tint seq_hash;\n\tqboolean dontdrop = false;\n\tusercmd_t *cmd;\n\tint checksumIndex, i;\n\tint lightlev;\n\tint seat;\n\n\tcl.movesequence = cls.netchan.outgoing_sequence;\t//make sure its correct even over map changes.\n\tseq_hash = cl.movesequence;\n\n\tfor (seat = 0; seat < cl.splitclients; seat++)\n\t{\n\t\t// send this and the previous cmds in the message, so\n\t\t// if the last packet was dropped, it can be recovered\n\t\ti = cl.movesequence & UPDATE_MASK;\n\t\tcmd = &cl.outframes[i].cmd[seat];\n\n\t\t//q2admin is retarded and kicks you if you get a stall.\n\t\tif (cmd->msec > 100)\n\t\t\tcmd->msec = 100;\n\n\t\tif (cls.protocol_q2 == PROTOCOL_VERSION_Q2EX)\n\t\t{\t//checksum byte got switched around to something that doesn't include so much sequence info. not really sure why.\n\t\t\tif (!seat)\n\t\t\t{\n\t\t\t\tMSG_WriteByte (buf, clcq2_move);\n\t\t\t\tif (!cl.q2frame.valid || cl_nodelta.ival || (cls.demorecording && !cls.demohadkeyframe))\n\t\t\t\t\tMSG_WriteLong (buf, -1);\t// no compression\n\t\t\t\telse\n\t\t\t\t\tMSG_WriteLong (buf, cl.q2frame.serverframe);\n\t\t\t}\n\n\t\t\tchecksumIndex = buf->cursize;\n\t\t\tMSG_WriteByte (buf, 0);\t//each seat has its own individual checksum for some reason (but no extra clcq2_move - player counts are not dynamic).\n\t\t}\n\t\telse if (seat)\n\t\t{\n\t\t\t//multi-seat still has an extra clc_move per seat\n\t\t\t//but no checksum (pointless when its opensource anyway)\n\t\t\t//no sequence (only seat 0 reports that)\n\t\t\tMSG_WriteByte (buf, clcq2_move);\n\t\t\tchecksumIndex = -1;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tMSG_WriteByte (buf, clcq2_move);\n\t\t\t// save the position for a checksum qbyte\n\t\t\tif (cls.protocol_q2 == PROTOCOL_VERSION_R1Q2 || cls.protocol_q2 == PROTOCOL_VERSION_Q2PRO)\n\t\t\t\tchecksumIndex = -1;\n\t\t\telse\n\t\t\t{\n\t\t\t\tchecksumIndex = buf->cursize;\n\t\t\t\tMSG_WriteByte (buf, 0);\n\t\t\t}\n\n\t\t\tif (!cl.q2frame.valid || cl_nodelta.ival || (cls.demorecording && !cls.demohadkeyframe))\n\t\t\t\tMSG_WriteLong (buf, -1);\t// no compression\n\t\t\telse\n\t\t\t\tMSG_WriteLong (buf, cl.q2frame.serverframe);\n\t\t}\n\n\t\tlightlev = R_LightPoint(cl.playerview[seat].simorg);\n\n\t//\tmsecs = msecs - (double)msecstouse;\n\n\t\ti = cls.netchan.outgoing_sequence & UPDATE_MASK;\n\t\tcmd = &cl.outframes[i].cmd[seat];\n\t\t*cmd = cl_pendingcmd[seat];\n\n\t\tcmd->lightlevel = (lightlev>255)?255:lightlev;\n\n\t\tcl.outframes[i].senttime = realtime;\n\t\tcl.outframes[i].latency = -1;\n\n\t\tif (cls.protocol_q2 == PROTOCOL_VERSION_Q2EX)\n\t\t{\n\t\t\tif (cmd->upmove >= 100)\n\t\t\t\tcmd->buttons |= 1<<3;\t//jump\n\t\t\telse if (cmd->upmove <= -100)\n\t\t\t\tcmd->buttons |= 1<<4;\t//crouch\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (in_jump.state[seat]&3 && cmd->upmove==0)\n\t\t\t\tcmd->upmove = 200;\n\t\t}\n\t\tif (cmd->buttons)\n\t\t\tcmd->buttons |= 128;\t//fixme: this isn't really what's meant by the anykey.\n\n\t// calculate a checksum over the move commands\n\t\tdontdrop |= CL_WriteDeltas(seat, buf);\n\n\t\tif (checksumIndex >= 0)\n\t\t{\n\t\t\tbuf->data[checksumIndex] = Q2COM_BlockSequenceCRCByte(\n\t\t\t\tbuf->data + checksumIndex + 1, buf->cursize - checksumIndex - 1,\n\t\t\t\tseq_hash);\n\t\t}\n\t}\n\tCL_ClearPendingCommands();\n\n\tif (cl.sendprespawn || !cls.protocol_q2)\n\t\tbuf->cursize = 0;\t//tastyspleen.net is alergic.\n\telse\n\t\tCL_UpdateSeats();\n\n\treturn dontdrop;\n}\n#endif\n\nqboolean CLQW_SendCmd (sizebuf_t *buf, qboolean actuallysend)\n{\n\tint seq_hash;\n\tqboolean dontdrop = false;\n\tusercmd_t *cmd;\n\tint checksumIndex, firstsize, plnum;\n\tint clientcount, lost;\n\tint curframe;\n\tint st = buf->cursize;\n\tint chatstate;\n\n\tcl.movesequence = cls.netchan.outgoing_sequence;\t//make sure its correct even over map changes.\n\tcurframe = cl.movesequence & UPDATE_MASK;\n\tseq_hash = cl.movesequence;\n\n\tcl.outframes[curframe].server_message_num = cl.validsequence;\n\tcl.outframes[curframe].cmd_sequence = cl.movesequence;\n\tcl.outframes[curframe].senttime = realtime;\n\tcl.outframes[curframe].latency = -1;\n\n// send this and the previous cmds in the message, so\n// if the last packet was dropped, it can be recovered\n\tclientcount = cl.splitclients;\n\n\tif (!clientcount)\n\t\tclientcount = 1;\n\n\tchatstate = 0;\n\tif (cl_sendchatstate.ival)\n\t{\n\t\tif (Key_Dest_Has(kdm_message|kdm_console|kdm_cwindows))\n\t\t\tchatstate |= 1;\t\t//chatting\n\t\telse if (Key_Dest_Has(~(kdm_game|kdm_centerprint)))\n\t\t\tchatstate |= 2;\t\t//afk. ezquake sends chatting, but neither are really appropriate.\n\t\tif (!vid.activeapp || vid.isminimized)\n\t\t\tchatstate |= 2;\t\t//afk.\n\t\t//FIXME: flag as afk if no new inputs for a while.\n\t}\n\tfor (plnum = 0; plnum<clientcount; plnum++)\n\t{\n\t\tif (cl.playerview[plnum].chatstate != chatstate)\n\t\t{\n\t\t\tif (chatstate)\n\t\t\t\tCL_SetInfo(plnum, \"chat\", va(\"%i\", chatstate));\n\t\t\telse\n\t\t\t\tCL_SetInfo(plnum, \"chat\", \"\");\n\t\t\tcl.playerview[plnum].chatstate = chatstate;\n\t\t}\n\n\t\tcmd = &cl.outframes[curframe].cmd[plnum];\n\t\t*cmd = cl_pendingcmd[plnum];\n\t\t\n\t\tcmd->lightlevel = 0;\n#ifdef CSQC_DAT\n\t\tif (!runningindepphys)\n\t\t\tCSQC_Input_Frame(plnum, cmd);\n#endif\n\t}\n\tCL_ClearPendingCommands();\n\n\tif (cls.fteprotocolextensions2 & PEXT2_VRINPUTS)\n\t\tdontdrop = CLFTE_SendVRCmd(buf, clientcount);\n\telse\n\t{\n\t\tcmd = &cl.outframes[curframe].cmd[0];\n\t\tif (cmd->cursor_screen[0] || cmd->cursor_screen[1] || cmd->cursor_entitynumber ||\n\t\t\tcmd->cursor_start[0] || cmd->cursor_start[1] || cmd->cursor_start[2] ||\n\t\t\tcmd->cursor_impact[0] || cmd->cursor_impact[1] || cmd->cursor_impact[2])\n\t\t{\n\t\t\tMSG_WriteByte (buf, clcfte_prydoncursor);\n\t\t\tMSG_WriteShort(buf, cmd->cursor_screen[0] * 32767.0f);\n\t\t\tMSG_WriteShort(buf, cmd->cursor_screen[1] * 32767.0f);\n\t\t\tMSG_WriteFloat(buf, cmd->cursor_start[0]);\n\t\t\tMSG_WriteFloat(buf, cmd->cursor_start[1]);\n\t\t\tMSG_WriteFloat(buf, cmd->cursor_start[2]);\n\t\t\tMSG_WriteFloat(buf, cmd->cursor_impact[0]);\n\t\t\tMSG_WriteFloat(buf, cmd->cursor_impact[1]);\n\t\t\tMSG_WriteFloat(buf, cmd->cursor_impact[2]);\n\t\t\tMSG_WriteEntity(buf, cmd->cursor_entitynumber);\n\t\t}\n\n\t\tMSG_WriteByte (buf, clc_move);\n\n\t\t// save the position for a checksum qbyte\n\t\tchecksumIndex = buf->cursize;\n\t\tMSG_WriteByte (buf, 0);\n\n\t\t// write our lossage percentage\n\t\tlost = CL_CalcNet(r_netgraph.value);\n\t\tMSG_WriteByte (buf, (qbyte)lost);\n\n\t\tfirstsize=0;\n\t\tfor (plnum = 0; plnum<clientcount; plnum++)\n\t\t{\n\t\t\tcmd = &cl.outframes[curframe].cmd[plnum];\n\n\t\t\tif (plnum)\n\t\t\t\tMSG_WriteByte (buf, clc_move);\n\n\t\t\tdontdrop = CL_WriteDeltas(plnum, buf) || dontdrop;\n\n\t\t\tif (!firstsize)\n\t\t\t\tfirstsize = buf->cursize;\n\t\t}\n\n\t// calculate a checksum over the move commands\n\n\t\tbuf->data[checksumIndex] = COM_BlockSequenceCRCByte(\n\t\t\tbuf->data + checksumIndex + 1, firstsize - checksumIndex - 1,\n\t\t\tseq_hash);\n\t}\n\n\t// request delta compression of entities\n\tif (cls.netchan.outgoing_sequence - cl.validsequence >= UPDATE_BACKUP-1)\n\t\tcl.validsequence = 0;\n\n\t//delta_sequence is the _expected_ previous sequences, so is set before it arrives.\n\tif (cl.validsequence && !cl_nodelta.ival && cls.state == ca_active)// && !cls.demorecording)\n\t{\n\t\tMSG_WriteByte (buf, clc_delta);\n//\t\tCon_Printf(\"%i\\n\", cl.validsequence);\n\t\tMSG_WriteByte (buf, cl.validsequence&255);\n\t}\n\n\tif (cl.sendprespawn || !actuallysend)\n\t\tbuf->cursize = st;\t//don't send movement commands while we're still supposedly downloading. mvdsv does not like that.\n\telse\n\t\tCL_UpdateSeats();\n\n\treturn dontdrop;\n}\n\nstatic void CL_SendUserinfoUpdate(void)\n{\n\tconst char *key = cls.userinfosync.keys[0].name;\n\tinfobuf_t *info = cls.userinfosync.keys[0].context;\n\tsize_t bloboffset = cls.userinfosync.keys[0].syncpos;\n\tunsigned int seat = info - cls.userinfo;\n\tsize_t blobsize;\n\tconst char *blobdata = InfoBuf_BlobForKey(info, key, &blobsize, NULL);\n\tsize_t sendsize = blobsize - bloboffset;\n\n\tconst char *s;\n\tqboolean final = true;\n\tchar enckey[2048];\n\tchar encval[2048];\n\n#ifdef Q3CLIENT\n\tif (cls.protocol == CP_QUAKE3)\n\t{\t//q3 sends it all in one go\n\t\tchar userinfo[2048];\n\t\tInfoSync_Strip(&cls.userinfosync, info);\t//can't track this stuff. all or nothing.\n\t\tif (info == &cls.userinfo[0])\n\t\t{\n\t\t\tInfoBuf_ToString(info, userinfo, sizeof(userinfo), NULL, NULL, NULL, NULL, NULL);\n\t\t\tq3->cl.SendClientCommand(\"userinfo \\\"%s\\\"\", userinfo);\n\t\t}\n\t\treturn;\n\t}\n#endif\n#ifdef Q2CLIENT\n\tif (cls.protocol == CP_QUAKE2 && !cls.fteprotocolextensions)\n\t{\n\t\tchar userinfo[2048];\n\t\tInfoSync_Strip(&cls.userinfosync, info);\t//can't track this stuff. all or nothing.\n\n\t\tif (cls.protocol_q2 == PROTOCOL_VERSION_Q2EX)\n\t\t{\n\t\t\textern size_t Q2EX_UserInfoToString(char *infostring, size_t maxsize, const char **ignore, int seats);\n\t\t\tQ2EX_UserInfoToString(userinfo, sizeof(userinfo), NULL, cl.splitclients);\n\n\t\t\tMSG_WriteByte (&cls.netchan.message, clcq2_userinfo);\n\t\t\tMSG_WriteString (&cls.netchan.message, userinfo+(*userinfo=='\\\\'?1:0));\n\t\t}\n\t\telse if (info == &cls.userinfo[0])\n\t\t{\n\t\t\tInfoBuf_ToString(info, userinfo, sizeof(userinfo), NULL, NULL, NULL, NULL, NULL);\n\n\t\t\tMSG_WriteByte (&cls.netchan.message, clcq2_userinfo);\n\t\t\tMSG_WriteString (&cls.netchan.message, userinfo);\n\t\t}\n\t\treturn;\n\t}\n#endif\n\n\tif (seat < max(1,cl.splitclients))\n\t{\n\t\tif (sendsize > 1023)\n\t\t{\n\t\t\tfinal = false;\n\t\t\tsendsize = 1023;\t//should be a multiple of 3\n\t\t}\n\n\t\tif (!InfoBuf_EncodeString(key, strlen(key), enckey, sizeof(enckey)) ||\n\t\t\t!InfoBuf_EncodeString(blobdata+bloboffset, sendsize, encval, sizeof(encval)))\n\t\t{\t//some buffer wasn't big enough... shouldn't happen.\n\t\t\tInfoSync_Remove(&cls.userinfosync, 0);\n\t\t\treturn;\n\t\t}\n\n\t\tif (final && !bloboffset && *encval != '\\xff' && *encval != '\\xff')\n\t\t{\t//vanilla-compatible info.\n\t\t\ts = va(\"setinfo \\\"%s\\\" \\\"%s\\\"\", enckey, encval);\n\t\t}\n\t\telse if (cls.fteprotocolextensions2 & PEXT2_INFOBLOBS)\n\t\t{\t//only flood servers that actually support it.\n\t\t\tif (final)\n\t\t\t\ts = va(\"setinfo \\\"%s\\\" \\\"%s\\\" %u\", enckey, encval, (unsigned int)bloboffset);\n\t\t\telse\n\t\t\t\ts = va(\"setinfo \\\"%s\\\" \\\"%s\\\" %u+\", enckey, encval, (unsigned int)bloboffset);\n\t\t}\n\t\telse\n\t\t{\t//server doesn't support it, just ignore the key\n\t\t\tInfoSync_Remove(&cls.userinfosync, 0);\n\t\t\treturn;\n\t\t}\n\t\tif (seat && (cls.fteprotocolextensions&PEXT_SPLITSCREEN))\n\t\t{\n\t\t\tMSG_WriteByte (&cls.netchan.message, (cls.protocol == CP_QUAKE2)?clcq2_stringcmd_seat:clcfte_stringcmd_seat);\n\t\t\tMSG_WriteByte (&cls.netchan.message, seat);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tMSG_WriteByte (&cls.netchan.message, (cls.protocol == CP_QUAKE2)?clcq2_stringcmd:clc_stringcmd);\n\t\t\tif (cls.protocol_q2 == PROTOCOL_VERSION_Q2EX)\n\t\t\t\tMSG_WriteByte (&cls.netchan.message, 1+seat);\n\t\t}\n\t\tMSG_WriteString (&cls.netchan.message, s);\n\t}\n\n\tif (bloboffset+sendsize == blobsize)\n\t\tInfoSync_Remove(&cls.userinfosync, 0);\n\telse\n\t\tcls.userinfosync.keys[0].syncpos += sendsize;\n}\n\nvoid CL_SendCmd (double frametime, qboolean mainloop)\n{\n\tsizebuf_t\tbuf;\n\tqbyte\t\tdata[MAX_DATAGRAM*16];\n\tint\t\t\ti, plnum;\n\tusercmd_t\t*cmd;\n\tfloat wantfps;\n\tint fullsend;\t//-1: send for sequence, with no usercmd. 0: update input frame, but don't send anything. 1: time for a new usercmd\n\n\tstatic float\tpps_balance = 0;\n\tstatic int\tdropcount = 0;\n\tstatic double msecs;\n\tstatic double msecsround;\n\tqboolean\tdontdrop=false;\n\tfloat usetime;\t\t//how many msecs we can use for the new frame\n\tint msecstouse;\t\t//usetime truncated to network precision (how much we'll actually eat)\n\tfloat framemsecs;\t//how long we're saying the input frame should be (differs from realtime with nq as we want to send frames reguarly, but note this might end up with funny-duration frames).\n\tqboolean xonoticworkaround;\n\n\tclcmdbuf_t *next;\n\n\tif (runningindepphys)\n\t{\n\t\tdouble curtime;\n\t\tstatic double lasttime;\n\t\tcurtime = Sys_DoubleTime();\n\t\tframetime = curtime - lasttime;\n\t\tlasttime = curtime;\n\t}\n\n\tCL_ProxyMenuHooks();\n\n\tif (cls.demoplayback != DPB_NONE || cls.state <= ca_demostart)\n\t{\n\t\tcursor_active = false;\n\t\tif (!cls.state || cls.demoplayback == DPB_MVD)\n\t\t{\n\t\t\textern cvar_t cl_splitscreen;\n\t\t\tcl.ackedmovesequence = cl.movesequence;\n\t\t\ti = cl.movesequence & UPDATE_MASK;\n\t\t\tcl.movesequence++;\n\t\t\tcl.outframes[i].server_message_num = cl.validsequence;\n\t\t\tcl.outframes[i].cmd_sequence = cl.movesequence;\n\t\t\tcl.outframes[i].senttime = realtime;\t\t// we haven't gotten a reply yet\n//\t\t\tcl.outframes[i].receivedtime = -1;\t\t// we haven't gotten a reply yet\n\n\t\t\tif (cl.splitclients > cl_splitscreen.ival+1)\n\t\t\t{\n\t\t\t\tcl.splitclients = cl_splitscreen.ival+1;\n\t\t\t\tif (cl.splitclients < 1)\n\t\t\t\t\tcl.splitclients = 1;\n\t\t\t}\n\t\t\tfor (plnum = 0; plnum < cl.splitclients; plnum++)\n\t\t\t{\n\t\t\t\tplayerview_t *pv = &cl.playerview[plnum];\n\t\t\t\tcmd = &cl.outframes[i].cmd[plnum];\n\n\t\t\t\tCL_AccumlateInput(plnum, frametime, frametime*1000);\n\t\t\t\t*cmd = cl_pendingcmd[plnum];\n\t\t\t\tmemset(&cl_pendingcmd[plnum], 0, sizeof(*cmd));\t//reset the pending for the next frame.\n\n#ifdef CSQC_DAT\n\t\t\t\tCSQC_Input_Frame(plnum, cmd);\n#endif\n\n\t\t\t\tif (cls.state == ca_active)\n\t\t\t\t{\n\t\t\t\t\tplayer_state_t *from, *to;\n\t\t\t\t\tfrom = &cl.inframes[cl.ackedmovesequence & UPDATE_MASK].playerstate[pv->playernum];\n\t\t\t\t\tto = &cl.inframes[cl.movesequence & UPDATE_MASK].playerstate[pv->playernum];\n\t\t\t\t\tCL_PredictUsercmd(pv->playernum, pv->viewentity, from, to, &cl.outframes[cl.ackedmovesequence & UPDATE_MASK].cmd[plnum]);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\twhile (clientcmdlist)\n\t\t\t{\n\t\t\t\tnext = clientcmdlist->next;\n\t\t\t\tCL_Demo_ClientCommand(clientcmdlist->command);\n\t\t\t\tCon_DLPrintf(2, \"Sending stringcmd %s\\n\", clientcmdlist->command);\n\t\t\t\tZ_Free(clientcmdlist);\n\t\t\t\tclientcmdlist = next;\n\t\t\t}\n\n\t\t\tcls.netchan.outgoing_sequence = cl.movesequence;\n\t\t}\n\n\t\tIN_Move (NULL, NULL, 0, frametime);\n\n\t\tCbuf_Waited();\t//its okay to stop waiting now\n\t\treturn; // sendcmds come from the demo\n\t}\n\n\tmemset(&buf, 0, sizeof(buf));\n\tbuf.maxsize = sizeof(data);\n\tbuf.cursize = 0;\n\tbuf.data = data;\n\tbuf.prim = cls.netchan.message.prim;\n\n\txonoticworkaround = cls.protocol == CP_NETQUAKE && CPNQ_IS_DP && cl.time && !cl.paused;\n\tif (xonoticworkaround)\n\t{\n\t\tif (cl.movesequence_time > cl.time + 0.5)\n\t\t\tcl.movesequence_time = cl.time + 0.5;\t//shouldn't really happen\n\t\tif (cl.movesequence_time < cl.time - 0.5)\n\t\t\tcl.movesequence_time = cl.time - 0.5;\t//shouldn't really happen\n\t\tframemsecs = (cl.time - cl.movesequence_time)*1000;\n\n\t\twantfps = cl_netfps.value;\n\t\tusetime = CL_FilterTime(framemsecs, wantfps, 5, false);\n\t\tif (usetime > 0)\n\t\t{\n\t\t\tusetime = framemsecs - usetime;\n\t\t\tfullsend = true;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tusetime = framemsecs - usetime;\n\t\t\tfullsend = false;\n\t\t}\n\t\tmsecstouse = usetime;\n\t\tframemsecs = msecstouse;\n\t\tmsecs = 0;\n\t}\n\telse\n\t{\n\t\tmsecs += frametime*1000;\n\n\t//\tCon_Printf(\"%f\\n\", msecs);\n\n\t\twantfps = cl_netfps.value;\n\t\tfullsend = true;\n\n\t\tmsecstouse = 0;\n\n\t#ifndef CLIENTONLY\n\t\tif (sv.state && cls.state != ca_active)\n\t\t{\t//HACK: if we're also the server, spam like a crazy person until we're on the server, for faster apparent load times.\n\t\t\tfullsend = -1;\t//send no movement command.\n\t\t\tmsecstouse = usetime = msecs;\n\t\t}\n\t\telse \n\t#endif\n\t\t{\n\t\t\t// while we're not playing send a slow keepalive fullsend to stop mvdsv from screwing up\n\t\t\tif (cls.state < ca_active && !cls.download)\n\t\t\t{\n\t\t\t\t#ifdef IRCCONNECT\t//don't spam irc.\n\t\t\t\tif (cls.netchan.remote_address.type == NA_IRC)\n\t\t\t\t\twantfps = 0.5;\n\t\t\t\telse\n\t\t\t\t#endif\n\t\t\t\t\twantfps = 12.5;\n\t\t\t}\n\t\t\tif (!runningindepphys && (cl_netfps.value > 0 || !fullsend))\n\t\t\t{\n\t\t\t\tfloat spare;\n\t\t\t\tspare = CL_FilterTime(msecs, wantfps, (/*cls.protocol == CP_NETQUAKE*/0?0:1.5), false);\n\t\t\t\tusetime = msecsround + (msecs - spare);\n\t\t\t\tmsecstouse = (int)usetime;\n\t\t\t\tif (!spare)\n\t\t\t\t\tfullsend = false;\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tmsecsround = usetime - msecstouse;\n\t\t\t\t\tmsecs = spare + msecstouse;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tusetime = msecsround + msecs;\n\t\t\t\tmsecstouse = (int)usetime;\n\t\t\t\tmsecsround = usetime - msecstouse;\n\t\t\t}\n\t\t}\n\n\t\tif (msecstouse > 200) // cap at 200 to avoid servers splitting movement more than four times\n\t\t\tmsecstouse = 200;\n\n\t\t// align msecstouse to avoid servers wasting our msecs\n\t\tif (msecstouse > 100)\n\t\t\tmsecstouse &= ~3; // align to 4\n\t\telse if (msecstouse > 50)\n\t\t\tmsecstouse &= ~1; // align to 2\n\n\t\tif (msecstouse <= 0)\t//FIXME\n\t\t\tfullsend = false;\n\t\tif (usetime <= 0)\n\t\t\treturn;\t//infinite frame times = weirdness.\n\n\t\tframemsecs = msecstouse;\n\n\t\tif (cls.protocol == CP_NETQUAKE)\n\t\t\tframemsecs = 1000*(cl.time - cl.movesequence_time);\n\t}\n\n#ifdef HLCLIENT\n\tif (!CLHL_BuildUserInput(msecstouse, &cl_pendingcmd[0]))\n#endif\n\tfor (plnum = 0; plnum < (cl.splitclients?cl.splitclients:1); plnum++)\n\t\tCL_AccumlateInput(plnum, frametime, framemsecs);\n\n\t//the main loop isn't allowed to send\n\tif (runningindepphys && mainloop)\n\t\treturn;\n\n//\tif (skipcmd)\n//\t\treturn;\n\n\tif (!fullsend)\n\t\treturn; // when we're actually playing we try to match netfps exactly to avoid gameplay problems\n\n//\tif (msecstouse > 127)\n//\t\tCon_Printf(\"%i\\n\", msecstouse, msecs);\n\n\t//HACK: 1000/77 = 12.98. nudge it just under so we never appear to be using 83fps at 77fps (which can trip cheat detection in mods that expect 72 fps when many servers are configured for 77)\n\t//so lets just never use 12.\n\tif (fullsend && cls.maxfps == 77)\n\t\tfor (plnum = 0; plnum < (cl.splitclients?cl.splitclients:1); plnum++)\n\t\t\tif (cl_pendingcmd[plnum].msec > 12.9 && cl_pendingcmd[plnum].msec < 13)\n\t\t\t\tcl_pendingcmd[plnum].msec = 13;\n\n#ifdef NQPROT\n\tif (cls.protocol != CP_NETQUAKE || cls.netchan.nqreliable_allowed)\n#endif\n\t{\n\t\tCL_SendDownloadReq(&buf);\n\n\t\t//only start spamming userinfo blobs once we receive the initial serverinfo.\n\t\twhile (cls.userinfosync.numkeys && cls.netchan.message.cursize < 512 && (cl.haveserverinfo || cls.protocol == CP_QUAKE2 || cls.protocol == CP_QUAKE3))\n\t\t\tCL_SendUserinfoUpdate();\n\n\t\twhile (clientcmdlist)\n\t\t{\n\t\t\tnext = clientcmdlist->next;\n\t\t\tif (clientcmdlist->reliable)\n\t\t\t{\n\t\t\t\tif (cls.netchan.message.cursize + 2+strlen(clientcmdlist->command)+100 > cls.netchan.message.maxsize)\n\t\t\t\t\tbreak;\n\t\t\t\tif (!strncmp(clientcmdlist->command, \"spawn\", 5) && cls.userinfosync.numkeys && cl.haveserverinfo)\n\t\t\t\t\tbreak;\t//HACK: don't send the spawn until all pending userinfos have been flushed.\n\t\t\t\tif (cls.protocol==CP_QUAKE2 && cls.protocol_q2==PROTOCOL_VERSION_Q2EX)\n\t\t\t\t{\n\t\t\t\t\tMSG_WriteByte (&cls.netchan.message, clcq2_stringcmd);\n\t\t\t\t\tMSG_WriteByte (&cls.netchan.message, clientcmdlist->seat+1);\n\t\t\t\t}\n\t\t\t\telse if (clientcmdlist->seat && (cls.fteprotocolextensions&PEXT_SPLITSCREEN))\n\t\t\t\t{\n\t\t\t\t\tMSG_WriteByte (&cls.netchan.message, clcfte_stringcmd_seat);\n\t\t\t\t\tMSG_WriteByte (&cls.netchan.message, clientcmdlist->seat);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tMSG_WriteByte (&cls.netchan.message, clc_stringcmd);\n\t\t\t\tMSG_WriteString (&cls.netchan.message, clientcmdlist->command);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (buf.cursize + 2+strlen(clientcmdlist->command)+100 <= buf.maxsize)\n\t\t\t\t{\n\t\t\t\t\tif (clientcmdlist->seat && (cls.fteprotocolextensions&PEXT_SPLITSCREEN))\n\t\t\t\t\t{\n\t\t\t\t\t\tMSG_WriteByte (&cls.netchan.message, clcfte_stringcmd_seat);\n\t\t\t\t\t\tMSG_WriteByte (&cls.netchan.message, clientcmdlist->seat);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\tMSG_WriteByte (&buf, clc_stringcmd);\n\t\t\t\t\tMSG_WriteString (&buf, clientcmdlist->command);\n\t\t\t\t}\n\t\t\t}\n\t\t\tCon_DLPrintf(2, \"Sending stringcmd %s\\n\", clientcmdlist->command);\n\t\t\tZ_Free(clientcmdlist);\n\t\t\tclientcmdlist = next;\n\t\t}\n\t}\n\n\t// if we're not doing clc_moves and etc, don't continue unless we wrote something previous\n\t// or we have something on the reliable buffer (or we're loopback and don't care about flooding)\n\tif (!fullsend && cls.netchan.remote_address.type != NA_LOOPBACK && buf.cursize < 1 && cls.netchan.message.cursize < 1)\n\t\treturn;\n\n\tif (fullsend)\n\t{\n\t\tif (!cls.state)\n\t\t{\n\t\t\tmsecs -= (double)msecstouse;\n\t\t\treturn;\n\t\t}\n\t\tcursor_active = false;\n\n\t\tfor (plnum = 0; plnum < cl.splitclients; plnum++)\n\t\t{\n\t\t\tcmd = &cl_pendingcmd[plnum];\n\t\t\tif (((cls.fteprotocolextensions2 & PEXT2_PRYDONCURSOR)||(cls.protocol == CP_NETQUAKE && cls.protocol_nq >= CPNQ_DP6)) && \n\t\t\t\t(*cl_prydoncursor.string && cl_prydoncursor.ival >= 0) && cls.state == ca_active)\n\t\t\t\tCL_UpdatePrydonCursor(cmd, plnum);\n\t\t\telse\n\t\t\t{\n\t\t\t\tVector2Clear(cmd->cursor_screen);\n\t\t\t\tVectorClear(cmd->cursor_start);\n\t\t\t\tVectorClear(cmd->cursor_impact);\n\t\t\t\tcmd->cursor_entitynumber = 0;\n\t\t\t}\n\t\t}\n\n\t\tif (xonoticworkaround)\n\t\t\tcl.movesequence_time += msecstouse/1000.0;\n\t\telse\n\t\t\tcl.movesequence_time = cl.time;\n\t\tswitch (cls.protocol)\n\t\t{\n#ifdef NQPROT\n\t\tcase CP_NETQUAKE:\n\t\t\tmsecs -= (double)msecstouse;\n\t\t\tCLNQ_SendCmd (&buf);\n\t\t\tdontdrop = true;\n\t\t\tbreak;\n#endif\n\t\tcase CP_QUAKEWORLD:\n\t\t\tmsecs -= (double)msecstouse;\n\t\t\tdontdrop = CLQW_SendCmd (&buf, fullsend == true);\n\t\t\tbreak;\n#ifdef Q2CLIENT\n\t\tcase CP_QUAKE2:\n\t\t\tmsecs -= (double)msecstouse;\n\t\t\tdontdrop = CLQ2_SendCmd (&buf);\n\t\t\tbreak;\n#endif\n#ifdef Q3CLIENT\n\t\tcase CP_QUAKE3:\n\t\t\tmsecs -= (double)msecstouse;\n\t\t\ti = cl.movesequence&UPDATE_MASK;\n\t\t\tmemcpy(cl.outframes[i].cmd, cl_pendingcmd, sizeof(usercmd_t)*bound(1, cl.splitclients, MAX_SPLITS));\n\t\t\tcl.outframes[i].cmd_sequence = cl.movesequence++;\n\t\t\tq3->cl.SendCmd(cls.sockets, cl.outframes[i].cmd, cl.movesequence, cl.time);\n\t\t\tcls.netchan.outgoing_sequence = cl.movesequence;\n\t\t\tCL_ClearPendingCommands();\n\n\t\t\t//don't bank too much, because that results in banking speedcheats\n\t\t\tif (msecs > 200)\n\t\t\t\tmsecs = 200;\n\t\t\treturn; // Q3 does it's own thing\n#endif\n\t\tdefault:\n\t\t\tHost_EndGame(\"Invalid protocol in CL_SendCmd: %i\", cls.protocol);\n\t\t\treturn;\n\t\t}\n\n\t\tif (cls.demorecording)\n\t\t\tCL_WriteDemoCmd(&cl.outframes[cl.movesequence & UPDATE_MASK].cmd[0]);\n\n//\t\tCon_DPrintf(\"generated sequence %i\\n\", cl.movesequence);\n\t\tcl.movesequence++;\n\n\t\t//clear enough of the pending command for the next frame.\n\t\tfor (plnum = 0; plnum < cl.splitclients; plnum++)\n\t\t{\n\t\t\tcl_pendingcmd[plnum].sequence = cl.movesequence;\n\t\t\tcl_pendingcmd[plnum].msec = 0;\n\t\t\tcl_pendingcmd[plnum].impulse = 0;\n//\t\t\tcl_pendingcmd[plnum].buttons = 0;\n\t\t}\n\t}\n\n#ifdef IRCCONNECT\n\tif (cls.netchan.remote_address.type == NA_IRC)\n\t{\n\t\tif (dropcount >= 2)\n\t\t{\n\t\t\tdropcount = 0;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// don't count this message when calculating PL\n\t\t\tcl.outframes[cls.netchan.outgoing_sequence&UPDATE_MASK].latency = -3;\n\t\t\t// drop this message\n\t\t\tcls.netchan.outgoing_sequence++;\n\t\t\tdropcount++;\n\t\t\treturn;\n\t\t}\n\t}\n\telse\n#endif\n\t//shamelessly stolen from fuhquake\n\t\tif (cl_c2spps.ival>0)\n\t{\n\t\tpps_balance += frametime;\n\t\t// never drop more than 2 messages in a row -- that'll cause PL\n\t\t// and don't drop if one of the last two movemessages have an impulse\n\t\tif (pps_balance > 0 || dropcount >= 2 || dontdrop)\n\t\t{\n\t\t\tfloat\tpps;\n\t\t\tpps = cl_c2spps.ival;\n\t\t\tif (pps < 10) pps = 10;\n\t\t\tif (pps > 72) pps = 72;\n\t\t\tpps_balance -= 1 / pps;\n\t\t\t// bound pps_balance. FIXME: is there a better way?\n\t\t\tif (pps_balance > 0.1) pps_balance = 0.1;\n\t\t\tif (pps_balance < -0.1) pps_balance = -0.1;\n\t\t\tdropcount = 0;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// don't count this message when calculating PL\n\t\t\tcl.outframes[(cl.movesequence-1) & UPDATE_MASK].latency = -3;\n\t\t\t// drop this message\n\t\t\tcls.netchan.outgoing_sequence++;\n\t\t\tdropcount++;\n\t\t\treturn;\n\t\t}\n\t}\n\telse\n\t{\n\t\tpps_balance = 0;\n\t\tdropcount = 0;\n\t}\n\n#ifdef VOICECHAT\n\tif (cls.protocol == CP_QUAKE2)\n\t\tS_Voip_Transmit(clcq2_voicechat, &buf);\n\telse\n\t\tS_Voip_Transmit(clcfte_voicechat, &buf);\n#endif\n\n//\n// deliver the message\n//\n\tcls.netchan.dupe = cl_c2sdupe.ival;\n\tNetchan_Transmit (&cls.netchan, buf.cursize, buf.data, 2500);\n\n\t//don't bank too much, because that results in banking speedcheats\n\tif (msecs > 200)\n\t\tmsecs = 200;\n\n\tif (cls.netchan.fatal_error)\n\t{\n\t\tcls.netchan.fatal_error = false;\n\t\tcls.netchan.message.overflowed = false;\n\t\tcls.netchan.message.cursize = 0;\n\t}\n}\n\nvoid CL_SendCvar_f (void)\n{\n\tcvar_t *var;\n\tchar *val;\n\tchar *name = Cmd_Argv(1);\n\n\tvar = Cvar_FindVar(name);\n\tif (!var)\n\t\tval = \"\";\n\telse if (var->flags & CVAR_NOUNSAFEEXPAND)\n\t\tval = \"\";\n\telse\n\t\tval = var->string;\n\tCL_SendSeatClientCommand(true, CL_TargettedSplit(false), \"sentcvar %s \\\"%s\\\"\", name, val);\n}\n\n/*\n============\nCL_InitInput\n============\n*/\nvoid CL_InitInput (void)\n{\n\tstatic char pcmd[MAX_SPLITS][3][6];\n\tunsigned int sp, i;\n#define inputnetworkcvargroup \"client networking options\"\n\tcl.splitclients = 1;\n\n\tCmd_AddCommand(\"rotate\", IN_Rotate_f);\n\tCmd_AddCommand(\"in_restart\", IN_Restart);\n\tCmd_AddCommand(\"sendcvar\", CL_SendCvar_f);\n\n\tCvar_Register (&cl_fastaccel, inputnetworkcvargroup);\n\tCvar_Register (&in_xflip, inputnetworkcvargroup);\n\tCvar_Register (&in_vraim, inputnetworkcvargroup);\n\tCvar_Register (&cl_nodelta, inputnetworkcvargroup);\n\n\tCvar_Register (&prox_inmenu, inputnetworkcvargroup);\n\n\tCvar_Register (&cl_c2sdupe, inputnetworkcvargroup);\n\tCvar_Register (&cl_c2sImpulseBackup, inputnetworkcvargroup);\n\tCvar_Register (&cl_c2sMaxRedundancy, inputnetworkcvargroup);\n\tCvar_Register (&cl_c2spps, inputnetworkcvargroup);\n\tCvar_Register (&cl_queueimpulses, inputnetworkcvargroup);\n\tCvar_Register (&cl_netfps, inputnetworkcvargroup);\n\tCvar_Register (&cl_run, inputnetworkcvargroup);\n\tCvar_Register (&cl_iDrive, inputnetworkcvargroup);\n\n#ifdef NQPROT\n\tCvar_Register (&cl_movement, inputnetworkcvargroup);\n#endif\n\tCvar_Register (&cl_sendchatstate, inputnetworkcvargroup);\n\tCvar_Register (&cl_smartjump, inputnetworkcvargroup);\n\n\tCvar_Register (&cl_prydoncursor, inputnetworkcvargroup);\n\tCvar_Register (&cl_instantrotate, inputnetworkcvargroup);\n\tCvar_Register (&cl_forceseat, inputnetworkcvargroup);\n\n\tfor (sp = 0; sp < MAX_SPLITS; sp++)\n\t{\n\t\tQ_snprintfz(pcmd[sp][0], sizeof(pcmd[sp][0]), \"p%i\", sp+1);\n\t\tQ_snprintfz(pcmd[sp][1], sizeof(pcmd[sp][1]), \"+p%i\", sp+1);\n\t\tQ_snprintfz(pcmd[sp][2], sizeof(pcmd[sp][2]), \"-p%i\", sp+1);\n\t\tCmd_AddCommand (pcmd[sp][0],\tCL_Split_f);\n\t\tCmd_AddCommand (pcmd[sp][1],\tCL_Split_f);\n\t\tCmd_AddCommand (pcmd[sp][2],\tCL_Split_f);\n\n/*default mlook to pressed, (on android we split the two sides of the screen)*/\n\t\tin_mlook.state[sp] = 1;\n\t}\n\n\t/*then alternative arged ones*/\n\tCmd_AddCommand (\"p\",\t\t\tCL_SplitA_f);\n\tCmd_AddCommand (\"+p\",\t\t\tCL_SplitA_f);\n\tCmd_AddCommand (\"-p\",\t\t\tCL_SplitA_f);\n\t\n\tCmd_AddCommand (\"+moveup\",\t\tIN_UpDown);\n\tCmd_AddCommand (\"-moveup\",\t\tIN_UpUp);\n\tCmd_AddCommand (\"+movedown\",\tIN_DownDown);\n\tCmd_AddCommand (\"-movedown\",\tIN_DownUp);\n\tCmd_AddCommand (\"+left\",\t\tIN_LeftDown);\n\tCmd_AddCommand (\"-left\",\t\tIN_LeftUp);\n\tCmd_AddCommand (\"+right\",\t\tIN_RightDown);\n\tCmd_AddCommand (\"-right\",\t\tIN_RightUp);\n\tCmd_AddCommand (\"+forward\",\t\tIN_ForwardDown);\n\tCmd_AddCommand (\"-forward\",\t\tIN_ForwardUp);\n\tCmd_AddCommand (\"+back\",\t\tIN_BackDown);\n\tCmd_AddCommand (\"-back\",\t\tIN_BackUp);\n\tCmd_AddCommand (\"+lookup\",\t\tIN_LookupDown);\n\tCmd_AddCommand (\"-lookup\",\t\tIN_LookupUp);\n\tCmd_AddCommand (\"+lookdown\",\tIN_LookdownDown);\n\tCmd_AddCommand (\"-lookdown\",\tIN_LookdownUp);\n\tCmd_AddCommand (\"+strafe\",\t\tIN_StrafeDown);\n\tCmd_AddCommand (\"-strafe\",\t\tIN_StrafeUp);\n\tCmd_AddCommand (\"+moveleft\",\tIN_MoveleftDown);\n\tCmd_AddCommand (\"-moveleft\",\tIN_MoveleftUp);\n\tCmd_AddCommand (\"+moveright\",\tIN_MoverightDown);\n\tCmd_AddCommand (\"-moveright\",\tIN_MoverightUp);\n\tCmd_AddCommand (\"+rollleft\",\tIN_RollLeftDown);\n\tCmd_AddCommand (\"-rollleft\",\tIN_RollLeftUp);\n\tCmd_AddCommand (\"+rollright\",\tIN_RollRightDown);\n\tCmd_AddCommand (\"-rollright\",\tIN_RollRightUp);\n\tCmd_AddCommand (\"+speed\",\t\tIN_SpeedDown);\n\tCmd_AddCommand (\"-speed\",\t\tIN_SpeedUp);\n\tCmd_AddCommand (\"+attack\",\t\tIN_AttackDown);\n\tCmd_AddCommand (\"-attack\",\t\tIN_AttackUp);\n\tCmd_AddCommand (\"+use\",\t\t\tIN_UseDown);\n\tCmd_AddCommand (\"-use\",\t\t\tIN_UseUp);\n\tCmd_AddCommand (\"+jump\",\t\tIN_JumpDown);\n\tCmd_AddCommand (\"-jump\",\t\tIN_JumpUp);\n\tCmd_AddCommandD(\"impulse\",\t\tIN_Impulse, \"Sends an impulse number to the server (read: weapon change).\");\n\tCmd_AddCommand (\"+klook\",\t\tIN_KLookDown);\n\tCmd_AddCommand (\"-klook\",\t\tIN_KLookUp);\n\tCmd_AddCommand (\"+mlook\",\t\tIN_MLookDown);\n\tCmd_AddCommand (\"-mlook\",\t\tIN_MLookUp);\n\n#ifdef QUAKESTATS\n\tCmd_AddCommand (\"+weaponwheel\",\t\tIN_WWheelDown);\n\tCmd_AddCommand (\"-weaponwheel\",\t\tIN_WWheelUp);\n\tCmd_AddCommandD (\"register_bestweapon\",\t\tIN_RegisterWeapon_f, \"Normally set via a mod's default.cfg file\");\n\tCmd_AddCommandD (\"bestweapon\",\t\tIN_Impulse, \"Works like 'impulse', for compat with other engines.\");\n\tCvar_Register (&cl_weaponhide, inputnetworkcvargroup);\n\tCvar_Register (&cl_weaponhide_preference, inputnetworkcvargroup);\n\tCvar_Register (&cl_weaponpreselect, inputnetworkcvargroup);\n\tCvar_Register (&cl_weaponforgetorder, inputnetworkcvargroup);\n\tCvar_Register (&r_viewpreselgun, inputnetworkcvargroup);\n#endif\n\n\tfor (i = 0; i < countof(in_button); i++)\n\t{\n\t\tstatic char bcmd[countof(in_button)][2][10];\n\t\tQ_snprintfz(bcmd[i][0], sizeof(bcmd[sp][0]), \"+button%i\", i);\n\t\tQ_snprintfz(bcmd[i][1], sizeof(bcmd[sp][1]), \"-button%i\", i);\n\t\tCmd_AddCommandD(bcmd[i][0],\t\tIN_ButtonNDown, \"This auxilliary command has mod-specific behaviour (often none).\");\n\t\tCmd_AddCommand (bcmd[i][1],\t\tIN_ButtonNUp);\n\t}\n}\n"
  },
  {
    "path": "engine/client/cl_main.c",
    "content": "/*\nCopyright (C) 1996-1997 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n*/\n// cl_main.c  -- client main loop\n\n#include \"quakedef.h\"\n#include \"winquake.h\"\n#include <sys/types.h>\n#include \"netinc.h\"\n#include \"cl_master.h\"\n#include \"cl_ignore.h\"\n#include \"shader.h\"\n#include \"vr.h\"\n#include <ctype.h>\n// callbacks\nvoid QDECL CL_Sbar_Callback(struct cvar_s *var, char *oldvalue);\n#ifdef NQPROT\nvoid QDECL Name_Callback(struct cvar_s *var, char *oldvalue);\n#else\n#define Name_Callback NULL\n#endif\nvoid GnuTLS_Shutdown(void);\n\nstatic void CL_ForceStopDownload (qboolean finish);\n\n// we need to declare some mouse variables here, because the menu system\n// references them even when on a unix system.\n\nqboolean\tnoclip_anglehack;\t\t// remnant from old quake\nint startuppending;\nextern int\tr_blockvidrestart;\n\nvoid Host_FinishLoading(void);\n\n\ncvar_t\tcl_crypt_rcon = CVARFD(\"cl_crypt_rcon\", \"1\", CVAR_ARCHIVE|CVAR_NOTFROMSERVER, \"Controls whether to send a hash instead of sending your rcon password as plain-text. Set to 1 for security, or 0 for backwards compatibility.\\nYour command and any responses will still be sent as plain text.\\nInstead, it is recommended to use rcon ONLY via dtls/tls/wss connections.\");\t//CVAR_NOTFROMSERVER prevents evil servers from degrading it to send plain-text passwords.\ncvar_t\trcon_password = CVARF(\"rcon_password\", \"\", CVAR_NOUNSAFEEXPAND);\n\ncvar_t\trcon_address = CVARF(\"rcon_address\", \"\", CVAR_NOUNSAFEEXPAND);\n\ncvar_t\tcl_timeout = CVAR(\"cl_timeout\", \"60\");\n\ncvar_t\tcl_shownet = CVARD(\"cl_shownet\",\"0\", \"Debugging var. 0 shows nothing. 1 shows incoming packet sizes. 2 shows individual messages. 3 shows entities too.\");\t// can be 0, 1, or 2\n\ncvar_t\tcl_disconnectreason = CVARAFD(\"_cl_disconnectreason\", \"\", /*q3*/\"com_errorMessage\", CVAR_NOSAVE, \"This cvar contains the reason for the last disconnection, so that mod menus can know why things failed.\");\n\ncvar_t\tcl_pure\t\t= CVARD(\"cl_pure\", \"0\", \"0=standard quake rules.\\n1=clients should prefer files within packages present on the server.\\n2=clients should use *only* files within packages present on the server.\\nDue to quake 1.01/1.06 differences, a setting of 2 is only reliable with total conversions.\\nIf sv_pure is set, the client will prefer the highest value set.\");\ncvar_t\tcl_sbar\t\t= CVARFC(\"cl_sbar\", \"0\", CVAR_ARCHIVE, CL_Sbar_Callback);\ncvar_t\tcl_hudswap\t= CVARF(\"cl_hudswap\", \"0\", CVAR_ARCHIVE);\ncvar_t\tcl_maxfps\t= CVARFD(\"cl_maxfps\", \"250\", CVAR_ARCHIVE, \"Sets the maximum allowed framerate. If you're using vsync or want to uncap framerates entirely then you should probably set this to 0. Set cl_yieldcpu 0 if you're trying to benchmark.\");\nstatic cvar_t\tcl_maxfps_slop\t= CVARFD(\"cl_maxfps_slop\", \"3\", CVAR_ARCHIVE, \"If a frame is delayed (eg because of poor system timer precision), this is how much sooner to pretend the frame happened (in milliseconds). If it is set too low then the average framerate will drop below the target, while too high may result in excessively fast frames.\");\nstatic cvar_t\tcl_idlefps\t= CVARAFD(\"cl_idlefps\", \"60\", \"cl_maxidlefps\"/*dp*/, CVAR_ARCHIVE, \"This is the maximum framerate to attain while idle/paused/unfocused.\");\ncvar_t\tcl_yieldcpu = CVARFD(\"cl_yieldcpu\", \"1\", CVAR_ARCHIVE, \"Attempt to yield between frames. This can resolve issues with certain drivers and background software, but can mean less consistant frame times. Will reduce power consumption/heat generation so should be set on laptops or similar (over-hot/battery powered) devices.\");\ncvar_t\tcl_nopext\t= CVARF(\"cl_nopext\", \"0\", CVAR_ARCHIVE);\nstatic cvar_t\tcl_pext_mask = CVAR(\"cl_pext_mask\", \"0xffffffff\");\ncvar_t\tcl_nolerp\t= CVARD(\"cl_nolerp\", \"0\", \"Disables interpolation. If set, missiles/monsters will be show exactly what was last received, which will be jerky. Does not affect players. A value of 2 means 'interpolate only in single-player/coop'.\");\n#ifdef NQPROT\ncvar_t\tcl_nolerp_netquake = CVARD(\"cl_nolerp_netquake\", \"0\", \"Disables interpolation when connected to an NQ server. Does affect players, even the local player. You probably don't want to set this.\");\nstatic cvar_t\tcl_fullpitch_nq = CVARAFD(\"cl_fullpitch\", \"0\", \"pq_fullpitch\", CVAR_SEMICHEAT, \"When set, attempts to unlimit the default view pitch. Note that some servers will screw over your angles if you use this, resulting in terrible gameplay, while some may merely clamp your angle serverside. This is also considered a cheat in quakeworld, ^1so this will not function there^7. For the equivelent in quakeworld, use serverinfo minpitch+maxpitch instead, which applies to all players fairly.\");\n#endif\nstatic cvar_t\tcl_vrui_force = CVARD(\"cl_vrui_force\", \"0\", \"Force the use of VR UIs, even with no VR headset active.\");\ncvar_t\tcl_vrui_lock = CVARD(\"cl_vrui_lock\", \"1\", \"Controls how the UI is positioned when using VR/XR. 0: Repositioned infront of the head when the console/menus are toggled. 1: Locked infront of the reference position (which may require the user to turn around to find it).\");\ncvar_t\t*hud_tracking_show;\ncvar_t\t*hud_miniscores_show;\nextern cvar_t net_compress;\n\ncvar_t\tcl_defaultport\t\t= \n\t#ifdef GAME_DEFAULTPORT\t//remove the confusing port alias if we're running as a TC, as well as info about irrelevant games.\n\t\tCVARFD(\"cl_defaultport\", STRINGIFY(PORT_DEFAULTSERVER),\t\t\t 0, \"The default port used to connect to servers.\")\n\t#else\n\t\tCVARAFD(\"cl_defaultport\", STRINGIFY(PORT_DEFAULTSERVER), \"port\", 0, \"The default port used to connect to servers.\"\n\t\t\t\"\\nQW: \"STRINGIFY(PORT_QWSERVER)\n\t\t\t\", NQ: \"STRINGIFY(PORT_NQSERVER)\n\t\t\t\", Q2: \"STRINGIFY(PORT_Q2SERVER)\n\t\t\t\", Q3: \"STRINGIFY(PORT_Q3SERVER)\n\t\t\".\"\n\t\t)\n\t#endif\n\t;\n\ncvar_t\tcfg_save_name = CVARFD(\"cfg_save_name\", \"fte\", CVAR_ARCHIVE|CVAR_NOTFROMSERVER, \"This is the config name that is saved by default when no argument is specified.\");\n\ncvar_t\tcl_splitscreen = CVARD(\"cl_splitscreen\", \"0\", \"Enables splitscreen support. See also: allow_splitscreen, in_rawinput*, the \\\"p\\\" command.\");\n\ncvar_t\tlookspring = CVARFD(\"lookspring\",\"0\", CVAR_ARCHIVE, \"Recentre the camera when the mouse-look is released.\");\ncvar_t\tlookstrafe = CVARFD(\"lookstrafe\",\"0\", CVAR_ARCHIVE, \"Mouselook enables mouse strafing.\");\ncvar_t\tsensitivity = CVARF(\"sensitivity\",\"10\", CVAR_ARCHIVE);\n\ncvar_t cl_staticsounds = CVARF(\"cl_staticsounds\", \"1\", CVAR_ARCHIVE);\n\ncvar_t\tm_pitch = CVARF(\"m_pitch\",\"0.022\", CVAR_ARCHIVE);\ncvar_t\tm_yaw = CVARF(\"m_yaw\",\"0.022\", CVAR_ARCHIVE);\ncvar_t\tm_forward = CVARF(\"m_forward\",\"1\", CVAR_ARCHIVE);\ncvar_t\tm_side = CVARF(\"m_side\",\"0.8\", CVAR_ARCHIVE);\n\ncvar_t\tcl_lerp_maxinterval = CVARD(\"cl_lerp_maxinterval\", \"0.3\", \"Maximum interval between keyframes, in seconds. Larger values can result in entities drifting very slowly when they move sporadically.\");\ncvar_t\tcl_lerp_maxdistance = CVARD(\"cl_lerp_maxdistance\", \"200\", \"Maximum distance that an entity may move between snapshots without being considered as having teleported.\");\ncvar_t\tcl_lerp_players = CVARD(\"cl_lerp_players\", \"0\", \"Set this to make other players smoother, though it may increase effective latency. Affects only QuakeWorld.\");\ncvar_t\tcl_predict_players\t\t\t= CVARD(\"cl_predict_players\", \"1\", \"Clear this cvar to see ents exactly how they are on the server.\");\ncvar_t\tcl_predict_players_frac\t\t= CVARD(\"cl_predict_players_frac\", \"0.9\", \"How much of other players to predict. Values less than 1 will help minimize overruns.\");\ncvar_t\tcl_predict_players_latency\t= CVARD(\"cl_predict_players_latency\", \"1.0\", \"Push the player back according to your latency, to give a smooth consistent simulation of the server.\");\ncvar_t\tcl_predict_players_nudge\t= CVARD(\"cl_predict_players_nudge\", \"0.02\", \"An extra nudge of time, to cover video latency.\");\ncvar_t\tcl_solid_players = CVARD(\"cl_solid_players\", \"1\", \"Consider other players as solid for player prediction.\");\ncvar_t\tcl_noblink = CVARD(\"cl_noblink\", \"0\", \"Disable the ^^b text blinking feature.\");\ncvar_t\tcl_servername = CVARFD(\"cl_servername\", \"\", CVAR_NOSET, \"The hostname of the last server you connected to\");\ncvar_t\tcl_serveraddress = CVARD(\"cl_serveraddress\", \"none\", \"The address of the last server you connected to\");\ncvar_t\tqtvcl_forceversion1 = CVAR(\"qtvcl_forceversion1\", \"0\");\ncvar_t\tqtvcl_eztvextensions = CVAR(\"qtvcl_eztvextensions\", \"1\");\n\ncvar_t\trecord_flush = CVARD(\"record_flush\", \"0\", \"If set, explicitly flushes demo data to disk while recording. This may be inefficient, depending on how your operating system is configured.\");\ncvar_t cl_demospeed = CVARF(\"cl_demospeed\", \"1\", 0);\ncvar_t\tcl_demoreel = CVARFD(\"cl_demoreel\", \"0\", CVAR_SAVE, \"When enabled, the engine will begin playing a demo loop on startup.\");\n\ncvar_t cl_loopbackprotocol = CVARD(\"cl_loopbackprotocol\", \"qw\", \"Which protocol to use for single-player/the internal client. Should be one of: qw, qwid, nqid, nq, fitz, bjp3, dp6, dp7, auto. If 'auto', will use qw protocols for qw mods, and nq protocols for nq mods.\");\n#ifdef FTE_TARGET_WEB\nstatic cvar_t cl_verify_urischeme = CVARAFD(\"cl_verify_urischeme\", \"2\", \"cl_verify_qwprotocol\"/*ezquake, inappropriate for misc schemes*/, CVAR_NOSAVE/*checked at startup, so its only really default.cfg that sets it*/, \"0: Do nothing.\\n1: Check whether our protocol scheme is registered and prompt the user to register associations.\\n2: Always re-register on every startup, without prompting. Sledgehammer style.\");\n#else\nstatic cvar_t cl_verify_urischeme = CVARAFD(\"cl_verify_urischeme\", \"0\", \"cl_verify_qwprotocol\"/*ezquake, inappropriate for misc schemes*/, CVAR_NOSAVE/*checked at startup, so its only really default.cfg that sets it*/, \"0: Do nothing.\\n1: Check whether our protocol scheme is registered and prompt the user to register associations.\\n2: Always re-register on every startup, without prompting. Sledgehammer style.\");\n#endif\n\ncvar_t cl_fakeframes = CVARD(\"cl_fakeframes\", \"0\", \"Slow GPU? Want to see higher framerates get reported! Unleash the power of the lie to see much higher framerates! Many people said it couldn't be done, that the people wouldn't accept it, but to hell with the neighsayers and non-believers! WE WANT BIGGER NUMBERS AND WE'RE DAMN WELL GONNA GET THEM!... For best results, combine with an external tool like fluid motion frames...\");\n\n\ncvar_t\tcl_threadedphysics = CVARD(\"cl_threadedphysics\", \"0\", \"When set, client input frames are generated and sent on a worker thread\");\n\n#ifdef QUAKESPYAPI\ncvar_t  localid = SCVAR(\"localid\", \"\");\nstatic qboolean allowremotecmd = true;\n#endif\n\ncvar_t\tr_drawflame = CVARD(\"r_drawflame\", \"1\", \"Set to -1 to disable ALL static entities. Set to 0 to disable only wall torches and standing flame. Set to 1 for everything drawn as normal.\");\n\nqboolean forcesaveprompt;\n\nextern int\t\t\ttotal_loading_size, current_loading_size, loading_stage;\n\n//\n// info mirrors\n//\ncvar_t\tpassword\t= CVARAF(\"password\",\t\"\",\t\"pq_password\", CVAR_USERINFO | CVAR_NOUNSAFEEXPAND); //this is parhaps slightly dodgy... added pq_password alias because baker seems to be using this for user accounts.\ncvar_t\tspectator\t= CVARF(\"spectator\",\t\"\",\t\t\tCVAR_USERINFO);\ncvar_t\tname\t\t= CVARFC(\"name\",\t\t\"Player\",\tCVAR_ARCHIVE | CVAR_USERINFO, Name_Callback);\ncvar_t\tteam\t\t= CVARF(\"team\",\t\t\t\"\",\t\t\tCVAR_ARCHIVE | CVAR_USERINFO);\ncvar_t\tskin\t\t= CVARAF(\"skin\",\t\t\"\",\t\t\t\"_cl_playerskin\"/*dp*/, CVAR_ARCHIVE | CVAR_USERINFO);\ncvar_t\tmodel\t\t= CVARAF(\"model\",\t\t\"\",\t\t\t\"_cl_playermodel\"/*dp*/, CVAR_ARCHIVE | CVAR_USERINFO);\ncvar_t\ttopcolor\t= CVARF(\"topcolor\",\t\t\"13\",\t\t\tCVAR_ARCHIVE | CVAR_USERINFO);\ncvar_t\tbottomcolor\t= CVARF(\"bottomcolor\",\t\"12\",\t\t\tCVAR_ARCHIVE | CVAR_USERINFO);\ncvar_t\trate\t\t= CVARAFD(\"rate\",\t\t\"30000\"/*\"6480\"*/,\t\t\"_cl_rate\"/*dp*/, CVAR_ARCHIVE | CVAR_USERINFO, \"A rough measure of the bandwidth to try to use while playing. Too high a value may result in 'buffer bloat'.\");\nstatic cvar_t\tdrate\t\t= CVARFD(\"drate\",\t\t\"3000000\",\tCVAR_ARCHIVE | CVAR_USERINFO, \"A rough measure of the bandwidth to try to use while downloading (in bytes per second).\");\t\t// :)\nstatic cvar_t\tnoaim\t\t= CVARF(\"noaim\",\t\t\"\",\t\t\tCVAR_ARCHIVE | CVAR_USERINFO);\ncvar_t\tmsg\t\t\t= CVARFD(\"msg\",\t\t\t\"1\",\t\tCVAR_ARCHIVE | CVAR_USERINFO, \"Filter console prints/messages. Only functions on QuakeWorld servers. 0=pickup messages. 1=death messages. 2=critical messages. 3=chat.\");\nstatic cvar_t\tb_switch\t= CVARF(\"b_switch\",\t\t\"\",\t\t\tCVAR_ARCHIVE | CVAR_USERINFO);\nstatic cvar_t\tw_switch\t= CVARF(\"w_switch\",\t\t\"\",\t\t\tCVAR_ARCHIVE | CVAR_USERINFO);\n#ifdef HEXEN2\ncvar_t\tcl_playerclass=CVARF(\"cl_playerclass\",\"\",\t\tCVAR_ARCHIVE | CVAR_USERINFO);\n#endif\n#ifdef Q2CLIENT\nstatic cvar_t\thand\t\t= CVARFD(\"hand\",\t\t\"\",\t\t\tCVAR_ARCHIVE | CVAR_USERINFO, \"For gamecode to know which hand to fire from.\\n0: Right\\n1: Left\\n2: Chest\");\n#endif\ncvar_t\tcl_nofake\t= CVARD(\"cl_nofake\",\t\t\"2\", \"value 0: permits \\\\r chars in chat messages\\nvalue 1: blocks all \\\\r chars\\nvalue 2: allows \\\\r chars, but only from teammates\");\ncvar_t\tcl_chatsound\t= CVAR(\"cl_chatsound\",\"1\");\ncvar_t\tcl_enemychatsound\t= CVAR(\"cl_enemychatsound\", \"misc/talk.wav\");\ncvar_t\tcl_teamchatsound\t= CVAR(\"cl_teamchatsound\", \"misc/talk.wav\");\n\ncvar_t\tr_torch\t\t\t\t\t= CVARFD(\"r_torch\",\t\"0\",\tCVAR_CHEAT, \"Generate a dynamic light at the player's position.\");\ncvar_t\tr_rocketlight\t\t\t= CVARFC(\"r_rocketlight\",\t\"1\", CVAR_ARCHIVE, Cvar_Limiter_ZeroToOne_Callback);\ncvar_t\tr_lightflicker\t\t\t= CVAR(\"r_lightflicker\",\t\"1\");\ncvar_t\tcl_r2g\t\t\t\t\t= CVARFD(\"cl_r2g\",\t\"0\", CVAR_ARCHIVE, \"Uses progs/grenade.mdl instead of progs/missile.mdl when 1.\");\ncvar_t\tr_powerupglow\t\t\t= CVAR(\"r_powerupglow\", \"1\");\ncvar_t\tv_powerupshell\t\t\t= CVARF(\"v_powerupshell\", \"0\", CVAR_ARCHIVE);\ncvar_t\tcl_gibfilter\t\t\t= CVARF(\"cl_gibfilter\", \"0\", CVAR_ARCHIVE);\ncvar_t\tcl_deadbodyfilter\t\t= CVARF(\"cl_deadbodyfilter\", \"0\", CVAR_ARCHIVE);\n\ncvar_t  cl_gunx\t\t\t\t\t= CVAR(\"cl_gunx\", \"0\");\ncvar_t  cl_guny\t\t\t\t\t= CVAR(\"cl_guny\", \"0\");\ncvar_t  cl_gunz\t\t\t\t\t= CVAR(\"cl_gunz\", \"0\");\n\ncvar_t  cl_gunanglex\t\t\t= CVAR(\"cl_gunanglex\", \"0\");\ncvar_t  cl_gunangley\t\t\t= CVAR(\"cl_gunangley\", \"0\");\ncvar_t  cl_gunanglez\t\t\t= CVAR(\"cl_gunanglez\", \"0\");\n\ncvar_t\tcl_proxyaddr\t\t\t= CVAR(\"cl_proxyaddr\", \"\");\ncvar_t\tcl_sendguid\t\t\t\t= CVARD(\"cl_sendguid\", \"\", \"Send a randomly generated 'globally unique' id to servers, which can be used by servers for score rankings and stuff. Different servers will see different guids. Delete the 'qkey' file in order to appear as a different user.\\nIf set to 2, all servers will see the same guid. Be warned that this can show other people the guid that you're using.\");\ncvar_t\tcl_downloads\t\t\t= CVARAFD(\"cl_downloads\", \"1\", /*q3*/\"cl_allowDownload\", CVAR_NOTFROMSERVER|CVAR_ARCHIVE, \"Allows you to block all automatic downloads.\");\ncvar_t\tcl_download_csprogs\t\t= CVARFD(\"cl_download_csprogs\", \"1\", CVAR_NOTFROMSERVER|CVAR_ARCHIVE, \"Download updated client gamecode if available. Warning: If you clear this to avoid downloading vm code, you should also clear cl_download_packages.\");\ncvar_t\tcl_download_redirection\t= CVARFD(\"cl_download_redirection\", \"2\", CVAR_NOTFROMSERVER|CVAR_ARCHIVE, \"Follow download redirection to download packages instead of individual files. Also allows the server to send nearly arbitary download commands.\\n2: allows redirection only to named packages files (and demos/*.mvd), which is a bit safer.\");\ncvar_t\tcl_download_packages\t= CVARFD(\"cl_download_packages\", \"1\", CVAR_NOTFROMSERVER, \"0=Do not download packages simply because the server is using them. 1=Download and load packages as needed (does not affect games which do not use this package). 2=Do download and install permanently (use with caution!)\");\ncvar_t\trequiredownloads\t\t= CVARAFD(\"cl_download_wait\", \"1\", /*old*/\"requiredownloads\", CVAR_ARCHIVE, \"0=join the game before downloads have even finished (might be laggy). 1=wait for all downloads to complete before joining.\");\ncvar_t\tmod_precache\t\t\t= CVARD(\"mod_precache\",\"1\", \"Controls when models are loaded.\\n0: Load them only when they're actually needed.\\n1: Load them upfront.\\n2: Lazily load them to shorten load times at the risk of brief stuttering during only the start of the map.\");\n\ncvar_t\tcl_muzzleflash\t\t\t= CVAR(\"cl_muzzleflash\", \"1\");\n\ncvar_t\tgl_simpleitems\t\t\t= CVARFD(\"gl_simpleitems\", \"0\", CVAR_ARCHIVE, \"Replace models with simpler sprites.\");\ncvar_t\tcl_item_bobbing\t\t\t= CVARFD(\"cl_model_bobbing\", \"0\", CVAR_ARCHIVE, \"Makes rotating pickup items bob too.\");\ncvar_t\tcl_countpendingpl\t\t= CVARD(\"cl_countpendingpl\", \"0\", \"If set to 1, packet loss percentages will show packets still in transit as lost, even if they might still be received.\");\n\ncvar_t\tcl_standardchat\t\t\t= CVARFD(\"cl_standardchat\", \"0\", CVAR_ARCHIVE, \"Disables auto colour coding in chat messages.\");\ncvar_t\tmsg_filter\t\t\t\t= CVARD(\"msg_filter\", \"0\", \"Filter out chat messages: 0=neither. 1=broadcast chat. 2=team chat. 3=all chat.\");\ncvar_t\tmsg_filter_frags\t\t= CVARD(\"msg_filter_frags\", \"0\", \"Prevents frag messages from appearing on the console.\");\ncvar_t\tmsg_filter_pickups\t\t= CVARD(\"msg_filter_pickups\", \"0\", \"Prevents pickup messages from appearing on the console. This would normally be filtered by 'msg 1', but nq servers cannot respect that (nor nq mods running in qw servers).\");\ncvar_t  cl_standardmsg\t\t\t= CVARFD(\"cl_standardmsg\", \"0\", CVAR_ARCHIVE, \"Disables auto colour coding in console prints.\");\ncvar_t  cl_parsewhitetext\t\t= CVARD(\"cl_parsewhitetext\", \"1\", \"When parsing chat messages, enable support for messages like: red{white}red\");\n\ncvar_t\tcl_dlemptyterminate\t\t= CVARD(\"cl_dlemptyterminate\", \"1\", \"Terminate downloads when reciving an empty download packet. This should help work around buggy mvdsv servers.\");\n\nstatic void QDECL Cvar_CheckServerInfo(struct cvar_s *var, char *oldvalue)\n{\t//values depend upon the serverinfo, so reparse for overrides.\n\tCL_CheckServerInfo();\n}\n\n#define RULESETADVICE \" You should not normally change this cvar from its permissive default, instead impose limits on yourself only through the 'ruleset' cvar.\"\ncvar_t\truleset_allow_playercount\t\t\t= CVARD(\"ruleset_allow_playercount\", \"1\", \"Specifies whether teamplay triggers that count nearby players are allowed in the current ruleset.\"RULESETADVICE);\ncvar_t\truleset_allow_frj\t\t\t\t\t= CVARD(\"ruleset_allow_frj\", \"1\", \"Specifies whether Forward-Rocket-Jump scripts are allowed in the current ruleset. If 0, limits on yaw speed will be imposed so they cannot be scripted.\"RULESETADVICE);\n\t\t\t\t\t\t\t\t\t\t\t\t//FIXME: rename ruleset_allow_frj to allow_scripts to match ezquake - 0: block multiple commands in binds, 1: cap angle speed changes, 2: vanilla quake\ncvar_t\truleset_allow_semicheats\t\t\t= CVARD(\"ruleset_allow_semicheats\", \"1\", \"If 0, this blocks a range of cvars that are marked as semi-cheats. Such cvars will be locked to their empty/0 value.\"RULESETADVICE);\ncvar_t\truleset_allow_packet\t\t\t\t= CVARD(\"ruleset_allow_packet\", \"1\", \"If 0, network packets sent via the 'packet' command will be blocked. This makes scripting timers a little harder.\"RULESETADVICE);\ncvar_t\truleset_allow_particle_lightning\t= CVARD(\"ruleset_allow_particle_lightning\", \"1\", \"A setting of 0 blocks using the particle system to replace lightning gun trails. This prevents making the trails thinner thus preventing them from obscuring your view of your enemies.\"RULESETADVICE);\ncvar_t\truleset_allow_overlongsounds\t\t= CVARD(\"ruleset_allow_overlong_sounds\", \"1\", \"A setting of 0 will block the use of extra-long pickup sounds as item respawn timers.\"RULESETADVICE);\ncvar_t\truleset_allow_larger_models\t\t\t= CVARD(\"ruleset_allow_larger_models\", \"1\", \"Enforces a maximum bounds limit on models, to prevent the use of additional spikes attached to the model from being used as a kind of wallhack.\"RULESETADVICE);\ncvar_t\truleset_allow_modified_eyes\t\t\t= CVARD(\"ruleset_allow_modified_eyes\", \"0\", \"When 0, completely hides progs/eyes.mdl if it is not strictly identical to vanilla quake.\"RULESETADVICE);\ncvar_t\truleset_allow_sensitive_texture_replacements = CVARD(\"ruleset_allow_sensitive_texture_replacements\", \"1\", \"Allows the replacement of certain model textures (as well as the models themselves). This prevents adding extra fullbrights to make them blatently obvious.\"RULESETADVICE);\ncvar_t\truleset_allow_localvolume\t\t\t= CVARD(\"ruleset_allow_localvolume\", \"1\", \"Allows the use of the snd_playersoundvolume cvar. Muting your own sounds can make it easier to hear where your opponent is.\"RULESETADVICE);\ncvar_t  ruleset_allow_shaders\t\t\t\t= CVARFD(\"ruleset_allow_shaders\", \"1\", CVAR_SHADERSYSTEM, \"When 0, this completely disables the use of external shader files, preventing custom shaders from being used for wallhacks.\"RULESETADVICE);\ncvar_t  ruleset_allow_watervis\t\t\t\t= CVARFCD(\"ruleset_allow_watervis\", \"1\", CVAR_SHADERSYSTEM, Cvar_CheckServerInfo, \"When 0, this enforces ugly opaque water.\"RULESETADVICE);\ncvar_t  ruleset_allow_fbmodels\t\t\t\t= CVARFD(\"ruleset_allow_fbmodels\", \"0\", CVAR_SHADERSYSTEM, \"When 1, allows all models to be displayed fullbright, completely ignoring the lightmaps. This feature exists only for parity with ezquake's defaults.\"RULESETADVICE);\ncvar_t  ruleset_allow_triggers\t\t\t\t= CVARAD(\"ruleset_allow_triggers\", \"1\", \"tp_msgtriggers\"/*ez*/, \"When 0, blocks the use of msg_trigger checks.\"RULESETADVICE);\n\nextern cvar_t cl_hightrack;\nextern cvar_t\tvid_renderer;\n\nchar cl_screengroup[] = \"Screen options\";\nchar cl_controlgroup[] = \"client operation options\";\nchar cl_inputgroup[] = \"client input controls\";\nchar cl_predictiongroup[] = \"Client side prediction\";\n\n\nclient_static_t\tcls;\nclient_state_t\tcl;\n\n// alot of this should probably be dynamically allocated\nentity_state_t\t*cl_baselines;\nstatic_entity_t *cl_static_entities;\nunsigned int    cl_max_static_entities;\nlightstyle_t\t*cl_lightstyle;\nsize_t\t\t\tcl_max_lightstyles;\ndlight_t\t\t*cl_dlights;\nsize_t\tcl_maxdlights; /*size of cl_dlights array*/\n\nint cl_baselines_count;\nsize_t rtlights_first, rtlights_max;\n\n// refresh list\n// this is double buffered so the last frame\n// can be scanned for oldorigins of trailing objects\nint\t\t\t\tcl_numvisedicts;\nint\t\t\t\tcl_maxvisedicts;\nentity_t\t\t*cl_visedicts;\nint\t\t\t\tcl_framecount;\n\nscenetris_t\t\t*cl_stris;\nvecV_t\t\t\t*fte_restrict cl_strisvertv;\nvec4_t\t\t\t*fte_restrict cl_strisvertc;\nvec2_t\t\t\t*fte_restrict cl_strisvertt;\nindex_t\t\t\t*fte_restrict cl_strisidx;\nunsigned int cl_numstrisidx;\nunsigned int cl_maxstrisidx;\nunsigned int cl_numstrisvert;\nunsigned int cl_maxstrisvert;\nunsigned int cl_numstris;\nunsigned int cl_maxstris;\n\nstatic struct\n{\n\tqboolean\t\t\ttrying;\n\tqboolean\t\t\tistransfer;\t\t//ignore the user's desired server (don't change connect.adr).\n\tqboolean\t\t\tresolving;\n\tint\t\t\t\t\tnumadr;\n\tint\t\t\t\t\tnextadr;\n\tnetadr_t\t\t\tadr[8];\t\t\t//addresses that we're trying to transfer to, one entry per dns result, eg both ::1 AND 127.0.0.1\n\tint\t\t\t\t\tprotocol;\t\t//nq/qw/q2/q3. guessed based upon server replies\n\tint\t\t\t\t\tsubprotocol;\t//the monkeys are trying to eat me.\n\tstruct\n\t{\n\t\t//flags\n\t\tunsigned int\t\tfte1;\n\t\tunsigned int\t\tfte2;\n\t\tunsigned int\t\tez1;\n\n\t\tint\t\t\t\t\tmtu;\t\t//0 for unsupported, otherwise a size.\n\t\tunsigned int\t\tcompresscrc;//0 for unsupported, otherwise the peer's hash\n\n\t\tunsigned char\t\tguidsalt[64];//server->client (for servers that want to share guids between themselves, with noticably lower security)\n\t} ext;\n\tint\t\t\t\t\tqport;\n\tint\t\t\t\t\tchallenge;\t\t//tracked as part of guesswork based upon what replies we get.\n\tint\t\t\t\t\tclchallenge;\t//generated by the client, to ensure the response wasn't spoofed/spammed.\n\tdouble\t\t\t\ttime;\t\t\t//for connection retransmits\n\tqboolean\t\t\tclogged;\t\t//ignore time...\n\tenum coninfomode_e\n\t{\n\t\tCIM_DEFAULT,\t//sends both a qw getchallenge and nq connect (also with postfixed getchallenge so modified servers can force getchallenge)\n\t\tCIM_NQONLY,\t\t//disables getchallenge (so fte servers treat us as an nq client). should not be used for dpp7 servers.\n\t\tCIM_QEONLY,\t\t//forces dtls and uses a different nq netchan version\n\t\tCIM_Q2EONLY,\t//forces dtls and uses a different nq netchan version\n\t}\t\t\t\t\tmode;\n\tenum coninfospec_e\n\t{\n\t\tCIS_DEFAULT,\t//default\n\t\tCIS_JOIN,\t\t//force join\n\t\tCIS_OBSERVE,\t//force observe\n\t}\t\t\t\t\tspec;\n\tint\t\t\t\t\tdefaultport;\n\tint\t\t\t\t\ttries;\t\t\t//increased each try, every fourth trys nq connect packets.\n\tunsigned char\t\tguid[64];\t\t//client->server guid (so doesn't change with transfers)\n\n\tstruct dtlspeercred_s peercred;\n} connectinfo;\n\nqboolean\tnomaster;\n\ndouble\t\toldrealtime;\t\t\t// last frame run\nint\t\t\thost_framecount;\n\nqbyte\t\t*host_basepal;\nqbyte\t\t*h2playertranslations;\n\ncvar_t\thost_speeds = CVAR(\"host_speeds\",\"0\");\t\t// set for running times\n\nint\t\t\tfps_count;\nqboolean\tforcesaveprompt;\n\njmp_buf \thost_abort;\n\nvoid Master_Connect_f (void);\n\nchar emodel_name[] =\n\t{ 'e' ^ 0xff, 'm' ^ 0xff, 'o' ^ 0xff, 'd' ^ 0xff, 'e' ^ 0xff, 'l' ^ 0xff, 0 };\nchar pmodel_name[] =\n\t{ 'p' ^ 0xff, 'm' ^ 0xff, 'o' ^ 0xff, 'd' ^ 0xff, 'e' ^ 0xff, 'l' ^ 0xff, 0 };\nchar prespawn_name[] =\n\t{ 'p'^0xff, 'r'^0xff, 'e'^0xff, 's'^0xff, 'p'^0xff, 'a'^0xff, 'w'^0xff, 'n'^0xff,\n\t\t' '^0xff, '%'^0xff, 'i'^0xff, ' '^0xff, '0'^0xff, ' '^0xff, '%'^0xff, 'i'^0xff, 0 };\nchar modellist_name[] =\n\t{ 'm'^0xff, 'o'^0xff, 'd'^0xff, 'e'^0xff, 'l'^0xff, 'l'^0xff, 'i'^0xff, 's'^0xff, 't'^0xff,\n\t\t' '^0xff, '%'^0xff, 'i'^0xff, ' '^0xff, '%'^0xff, 'i'^0xff, 0 };\nchar soundlist_name[] =\n\t{ 's'^0xff, 'o'^0xff, 'u'^0xff, 'n'^0xff, 'd'^0xff, 'l'^0xff, 'i'^0xff, 's'^0xff, 't'^0xff,\n\t\t' '^0xff, '%'^0xff, 'i'^0xff, ' '^0xff, '%'^0xff, 'i'^0xff, 0 };\n\nvrui_t vrui;\nvoid VRUI_SnapAngle(void)\n{\n//\tVectorCopy(cl.playerview[0].viewangles, vrui.angles);\n\tvrui.angles[0] = 0;\n\tif (cl_vrui_lock.ival)\t//if its locked, then its locked infront of the unpitched player entity\n\t\tvrui.angles[1] = cl.playerview[0].viewangles[1];\n\telse\t//otherwise its moved to infront of the player's head any time the menu is displayed.\n\t\tvrui.angles[1] = cl.playerview[0].aimangles[1];\n\tvrui.angles[2] = 0;\n}\n\nvoid CL_UpdateWindowTitle(void)\n{\n\tif (VID_SetWindowCaption)\n\t{\n\t\tif (cl.windowtitle)\n\t\t{\t//gamecode wanted some explicit title.\n\t\t\tVID_SetWindowCaption(cl.windowtitle);\n\t\t\treturn;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tchar title[2048];\n\t\t\tswitch (cls.state)\n\t\t\t{\n\t\t\tdefault:\n#ifdef HAVE_SERVER\n\t\t\t\tif (sv.state)\n\t\t\t\t\tQ_snprintfz(title, sizeof(title), \"%s: %s\", fs_gamename.string, svs.name);\n\t\t\t\telse\n#endif\n\t\t\t\tif (cls.demoplayback)\n\t\t\t\t\tQ_snprintfz(title, sizeof(title), \"%s: %s\", fs_gamename.string, cls.lastdemoname);\n\t\t\t\telse\n\t\t\t\t\tQ_snprintfz(title, sizeof(title), \"%s: %s\", fs_gamename.string, cls.servername);\n\t\t\t\tbreak;\n\t\t\tcase ca_disconnected:\n\t\t\t\tif (CSQC_UnconnectedOkay(false))\t//pure csqc mods can have a world model and yet be disconnected. we don't really know what the current map should be called though.\n\t\t\t\t\tQ_snprintfz(title, sizeof(title), \"%s\", fs_gamename.string);\n\t\t\t\telse\n\t\t\t\t\tQ_snprintfz(title, sizeof(title), \"%s: disconnected\", fs_gamename.string);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tVID_SetWindowCaption(title);\n\t\t}\n\t}\n}\n\n#ifdef __GLIBC__\n#include <malloc.h>\n#endif\nvoid CL_MakeActive(char *gamename)\n{\n\textern int fs_finds;\n\tif (fs_finds)\n\t{\n\t\tCon_DPrintf(\"%i additional FS searches\\n\", fs_finds);\n\t\tfs_finds = 0;\n\t}\n\tcl.matchgametimestart = 0;\n\tcls.state = ca_active;\n\n\t//this might be expensive, don't count any of this as time spent *playing* the demo. this avoids skipping the first $LOADDURATION seconds.\n\tcl.stillloading = true;\n\n\t//kill sounds left over from the last map.\n\tS_Purge(true);\n\n\t//kill models left over from the last map.\n\tMod_Purge(MP_MAPCHANGED);\n\n\t//and reload shaders now if needed (this was blocked earlier)\n\tShader_DoReload();\n\n\t//and now free any textures that were not still needed.\n\tImage_Purge();\n\n\tSCR_EndLoadingPlaque();\n\tCL_UpdateWindowTitle();\n\n#ifdef MVD_RECORDING\n\tif (sv_demoAutoRecord.ival && !sv.mvdrecording && !cls.demorecording && !cls.demoplayback && MVD_CheckSpace(false))\n\t{\t//don't auto-record if we're already recording... or playing a different demo.\n\t\textern cvar_t sv_demoAutoPrefix;\n\t\tchar timestamp[64];\n\t\ttime_t tm = time(NULL);\n\t\tstrftime(timestamp, sizeof(timestamp), \"%Y%m%d_%H%M%S\", localtime(&tm));\n\t\tCbuf_AddText(va(\"record %s%s%s%s_%s\\n\", sv_demoDir.string, *sv_demoDir.string?\"/\":\"\", sv_demoAutoPrefix.string, host_mapname.string, timestamp), RESTRICT_LOCAL);\n\t}\n#endif\n\n\tTP_ExecTrigger(\"f_begin\", true);\n\tif (cls.demoplayback)\n\t\tTP_ExecTrigger(\"f_spawndemo\", true);\n\telse\n\t\tTP_ExecTrigger(\"f_spawn\", false);\n\n#ifdef __GLIBC__\n\tmalloc_trim(0);\n#endif\n}\n/*\n==================\nCL_Quit_f\n==================\n*/\nvoid CL_Quit_f (void)\n{\n\tif (!host_initialized)\n\t\treturn;\n\n\tif (forcesaveprompt && strcmp(Cmd_Argv(1), \"force\"))\n\t{\n\t\tforcesaveprompt = false;\n\t\tif (Cmd_Exists(\"menu_quit\"))\n\t\t{\n\t\t\tCmd_ExecuteString(\"menu_quit\", RESTRICT_LOCAL);\n\t\t\treturn;\n\t\t}\n\t}\n\n\tTP_ExecTrigger(\"f_quit\", true);\n\tCbuf_Execute();\n/*\n#ifdef HAVE_SERVER\n\tif (!isDedicated)\n#endif\n\t{\n\t\tM_Menu_Quit_f ();\n\t\treturn;\n\t}*/\n\tSys_Quit ();\n}\n\n#ifdef NQPROT\nvoid CL_ConnectToDarkPlaces(char *challenge, netadr_t *adr)\n{\n\tchar\tdata[2048];\n\tcls.fteprotocolextensions = 0;\n\tcls.fteprotocolextensions2 = 0;\n\tcls.ezprotocolextensions1 = 0;\n\n\tconnectinfo.time = realtime;\t// for retransmit requests\n\n\tQ_snprintfz(data, sizeof(data), \"%c%c%c%cconnect\\\\protocol\\\\darkplaces \"STRINGIFY(NQ_NETCHAN_VERSION)\"\\\\protocols\\\\DP7 DP6 DP5 RMQ FITZ NEHAHRABJP2 NEHAHRABJP NEHAHRABJP3 QUAKE\\\\challenge\\\\%s\\\\name\\\\%s\", 255, 255, 255, 255, challenge, name.string);\n\n\tNET_SendPacket (cls.sockets, strlen(data), data, adr);\n\n\tcl.splitclients = 0;\n}\n#endif\n\nvoid CL_SupportedFTEExtensions(unsigned int *pext1, unsigned int *pext2, unsigned int *ezpext1)\n{\n\tunsigned int fteprotextsupported1 = Net_PextMask(PROTOCOL_VERSION_FTE1, false);\n\tunsigned int fteprotextsupported2 = Net_PextMask(PROTOCOL_VERSION_FTE2, false);\n\tunsigned int ezprotextsupported1 = Net_PextMask(PROTOCOL_VERSION_EZQUAKE1, false) & EZPEXT1_CLIENTADVERTISE;\n\n\tfteprotextsupported1 &= strtoul(cl_pext_mask.string, NULL, 16);\n//\tfteprotextsupported2 &= strtoul(cl_pext2_mask.string, NULL, 16);\n//\tezprotextsupported1 &= strtoul(cl_ezpext1_mask.string, NULL, 16);\n\n\tif (cl_nopext.ival)\n\t{\n\t\tfteprotextsupported1 = 0;\n\t\tfteprotextsupported2 = 0;\n\t\tezprotextsupported1 = 0;\n\t}\n\n\t*pext1 = fteprotextsupported1;\n\t*pext2 = fteprotextsupported2;\n\t*ezpext1 = ezprotextsupported1;\n}\n\nchar *CL_GUIDString(netadr_t *adr)\n{\n\tstatic qbyte buf[2048];\n\tstatic int buflen;\n\tqbyte digest[DIGEST_MAXSIZE];\n\tchar serveraddr[256];\n\tvoid *ctx;\n\n\tif (!*cl_sendguid.string && *connectinfo.ext.guidsalt)\n\t{\n\t\tserveraddr[0] = '#';\t//leading hash is to stop servers from being able to scrape from other servers (ones that don't use a custom/reproducible salt).\n\t\tQ_strncpyz(serveraddr+1, connectinfo.ext.guidsalt, sizeof(serveraddr)-1);\n\t}\n\telse if (cl_sendguid.ival == 2)\n\t\t*serveraddr = 0;\n\telse if (cl_sendguid.ival)\n\t\tNET_AdrToString(serveraddr, sizeof(serveraddr), adr);\n\telse\n\t\treturn NULL;\n\n\tif (*connectinfo.guid && connectinfo.istransfer)\n\t\treturn connectinfo.guid;\n\n\tif (!buflen)\n\t{\n\t\tvfsfile_t *f;\n\t\tf = FS_OpenVFS(\"qkey\", \"rb\", FS_ROOT);\n\t\tif (f)\n\t\t{\n\t\t\tbuflen = VFS_GETLEN(f);\n\t\t\tif (buflen > 2048)\n\t\t\t\tbuflen = 2048;\n\t\t\tbuflen = VFS_READ(f, buf, buflen);\n\t\t\tVFS_CLOSE(f);\n\t\t}\n\t\tif (buflen < 16)\n\t\t{\n\t\t\tbuflen = sizeof(buf);\n\t\t\tif (!Sys_RandomBytes(buf, buflen))\n\t\t\t{\n\t\t\t\tint i;\n\t\t\t\tsrand(time(NULL));\n\t\t\t\tfor (i = 0; i < buflen; i++)\n\t\t\t\t\tbuf[i] = rand() & 0xff;\n\t\t\t}\n\t\t\tf = FS_OpenVFS(\"qkey\", \"wb\", FS_ROOT);\n\t\t\tif (f)\n\t\t\t{\n\t\t\t\tVFS_WRITE(f, buf, buflen);\n\t\t\t\tVFS_CLOSE(f);\n\t\t\t}\n\t\t}\n\t}\n\n\tctx = alloca(hash_md4.contextsize);\n\thash_md4.init(ctx);\n\thash_md4.process(ctx, buf, buflen);\n\thash_md4.process(ctx, serveraddr, strlen(serveraddr));\n\thash_md4.terminate(digest, ctx);\n\tBase16_EncodeBlock(digest, hash_md4.digestsize, connectinfo.guid, sizeof(connectinfo.guid));\n\treturn connectinfo.guid;\n}\n\nstatic void CL_ConnectAbort(const char *format, ...)\n{\t//stops trying to connect, doesn't affect the _current_ connection, so usable for transfers.\n\tva_list\t\targptr;\n\tchar\t\treason[1024];\n\n\tif (format)\n\t{\n\t\tva_start (argptr, format);\n\t\tQ_vsnprintfz (reason, sizeof(reason), format,argptr);\n\t\tva_end (argptr);\n\n\t\tCvar_Set(&cl_disconnectreason, reason);\n\t\tCon_Printf (CON_ERROR\"%s\\n\", reason);\n\t}\n#ifdef HAVE_DTLS\n\twhile (connectinfo.numadr)\n\t\tNET_DTLS_Disconnect(cls.sockets, &connectinfo.adr[--connectinfo.numadr]);\n#endif\n\tconnectinfo.numadr = 0;\n\tSCR_EndLoadingPlaque();\n\tconnectinfo.trying = false;\n\n\tif (format)\n\t{\n\t\t//try and force the menu to show again. this should force the disconnectreason to show.\n\t\tif (!Key_Dest_Has(kdm_console))\n\t\t{\n#ifdef MENU_DAT\n\t\t\tif (!MP_Toggle(1))\n#endif\n\t\t\t\tMenu_Prompt(NULL, NULL, reason, NULL, NULL, \"Okay\", true);\n\t\t}\n\t}\n}\n\n\nsize_t Q2EX_UserInfoToString(char *infostring, size_t maxsize, const char **ignore, int seats)\n{\t//infoblobs are not a thing here. don't need to maintain sync objects - can't really do anything with them anyway.\n\tsize_t k, r = 1, l;\n\tchar *o = infostring;\n\tchar *e = infostring?infostring + maxsize-1:infostring;\n\tint s;\n\n\tfor (s = 0; s < seats; s++)\n\t{\n\t\tchar *pf = s?va(\"_%i\", s):\"\";\n\t\tsize_t pfl = strlen(pf);\n\t\tinfobuf_t *info = &cls.userinfo[s];\n\t\tfor (k = 0; k < info->numkeys; k++)\n\t\t{\n\t\t\tif (ignore)\n\t\t\t{\n\t\t\t\tfor (l = 0; ignore[l]; l++)\n\t\t\t\t{\n\t\t\t\t\tif (!strcmp(ignore[l], info->keys[k].name))\n\t\t\t\t\t\tbreak;\n\t\t\t\t\telse if (ignore[l][0] == '*' && !ignore[l][1] && *info->keys[k].name == '*')\n\t\t\t\t\t\tbreak;\t//read-only\n\t\t\t\t\telse if (ignore[l][0] == '_' && !ignore[l][1] && *info->keys[k].name == '_')\n\t\t\t\t\t\tbreak;\t//comment\n\t\t\t\t}\n\t\t\t\tif (ignore[l])\n\t\t\t\t\tcontinue;\t//ignore when in the list\n\t\t\t}\n\n\t\t\tif (!info->keys[k].large)\t//lower priorities don't bother with extended blocks. be sure to prioritise them explicitly. they'd just bug stuff out.\n\t\t\t{\n\t\t\t\tsize_t knl = strlen(info->keys[k].name);\n\t\t\t\tsize_t kvl = info->keys[k].size;\n\t\t\t\tr+= 1+knl+pfl+1+kvl;\n\t\t\t\tif (o + 1+knl+1+kvl >= e)\n\t\t\t\t\tcontinue;\n\t\t\t\to[0] = '\\\\';\n\t\t\t\tmemcpy(o+1, info->keys[k].name, knl);\n\t\t\t\tmemcpy(o+1+knl, pf, pfl);\n\t\t\t\to[1+knl+pfl] = '\\\\';\n\t\t\t\tmemcpy(o+1+knl+pfl+1, info->keys[k].value, kvl);\n\t\t\t\to[1+knl+pfl+1+kvl] = 0;\n\n\t\t\t\to += 1+knl+pfl+1+kvl;\n\t\t\t}\n\t\t}\n\t}\n\t*o = 0;\n\treturn r;\n}\n\n/*\n=======================\nCL_SendConnectPacket\n\ncalled by CL_Connect_f and CL_CheckResend\n======================\n*/\nstatic void CL_SendConnectPacket (netadr_t *to)\n{\n\textern cvar_t qport;\n\tnetadr_t\taddr;\n\tchar\tdata[2048];\n\tchar *info;\n\tdouble t1, t2;\n\tchar *a;\n\n// JACK: Fixed bug where DNS lookups would cause two connects real fast\n//       Now, adds lookup time to the connect time.\n//\t\t Should I add it to realtime instead?!?!\n\n\tif (!connectinfo.trying)\n\t\treturn;\n\n\tif (cl_nopext.ival)\t//imagine it's an unenhanced server\n\t{\n\t\tconnectinfo.ext.compresscrc = 0;\n\t}\n\n\tif (connectinfo.protocol == CP_QUAKEWORLD)\n\t{\n\t\tint fteprotextsupported1=0;\n\t\tint fteprotextsupported2=0;\n\t\tint ezprotextsupported1=0;\n\t\tCL_SupportedFTEExtensions(&fteprotextsupported1, &fteprotextsupported2, &ezprotextsupported1);\n\n\t\tconnectinfo.ext.fte1 &= fteprotextsupported1;\n\t\tconnectinfo.ext.fte2 &= fteprotextsupported2;\n\t\tconnectinfo.ext.ez1 &= ezprotextsupported1;\n\t}\n#ifdef Q2CLIENT\n\telse if (connectinfo.protocol == CP_QUAKE2)\n\t{\n\t\tif (!(scr_fov.flags & CVAR_USERINFO))\n\t\t{\t//q2 does server-controlled fov, so make sure the cvar is flagged properly.\n\t\t\t//FIXME: this hack needs better support, for dynamically switching between protocols without spamming too many cvars for other games.\n\t\t\tscr_fov.flags |= CVAR_USERINFO;\n\t\t\tCvar_Set(&scr_fov, scr_fov.string);\t//make sure the userinfo is set properly.\n\t\t}\n\n\t\tconnectinfo.ext.fte1 &= (PEXT_MODELDBL|PEXT_SOUNDDBL|PEXT_SPLITSCREEN);\n\t\tconnectinfo.ext.fte2 &= PEXT2_STUNAWARE;\n\t\tconnectinfo.ext.ez1 &= 0;\n\t}\n#endif\n\telse\n\t{\n\t\tconnectinfo.ext.fte1 = 0;\n\t\tconnectinfo.ext.fte2 = 0;\n\t\tconnectinfo.ext.ez1 = 0;\n\t}\n\n\tt1 = Sys_DoubleTime ();\n\n#ifdef HAVE_DTLS\n\tif (connectinfo.peercred.hash && net_enable_dtls.ival>0)\n\t{\n\t\tchar cert[8192];\n\t\tchar digest[DIGEST_MAXSIZE];\n\t\tint sz = NET_GetConnectionCertificate(cls.sockets, to, QCERT_PEERCERTIFICATE, cert, sizeof(cert));\n\t\tif (sz <= 0 || memcmp(connectinfo.peercred.digest, digest, CalcHash(connectinfo.peercred.hash, digest, sizeof(digest), cert, sz)))\n\t\t{\t//FIXME: we may have already pinned the bad cert, which may cause issues when reconnecting without FP info later.\n\t\t\tif (NET_GetConnectionCertificate(cls.sockets, to, QCERT_ISENCRYPTED, NULL, 0)<0)\n\t\t\t\tCL_ConnectAbort (\"Fingerprint specified, but server did not report any certificate\\n\");\n\t\t\telse\n\t\t\t\tCL_ConnectAbort (\"Server certificate does not match specified fingerprint\\n\");\n\t\t\treturn;\n\t\t}\n\t}\n#endif\n\n\tif (!to)\n\t{\n\t\tto = &addr;\n\t\tif (!NET_StringToAdr (cls.servername, PORT_DEFAULTSERVER, to))\n\t\t{\n\t\t\tCL_ConnectAbort (\"CL_SendConnectPacket: Bad server address \\\"%s\\\"\\n\", cls.servername);\n\t\t\treturn;\n\t\t}\n\t}\n\n\tNET_AdrToString(data, sizeof(data), to);\n\tCvar_ForceSet(&cl_serveraddress, data);\n//\tInfo_SetValueForStarKey (cls.userinfo, \"*ip\", data, MAX_INFO_STRING);\n\n\tif (!NET_IsClientLegal(to))\n\t{\n\t\tCL_ConnectAbort(\"Illegal server address\\n\");\n\t\treturn;\n\t}\n\n\tt2 = Sys_DoubleTime ();\n\n\tconnectinfo.time = realtime+t2-t1;\t// for retransmit requests\n\n\t//fixme: we shouldn't cycle these so much\n\tconnectinfo.qport = qport.value;\n\tif (to->type != NA_LOOPBACK)\n\t\tCvar_SetValue(&qport, (connectinfo.qport+1)&0xffff);\n\n\tif (connectinfo.protocol == CP_QUAKE2 && (connectinfo.subprotocol == PROTOCOL_VERSION_R1Q2 || connectinfo.subprotocol == PROTOCOL_VERSION_Q2PRO))\n\t\tconnectinfo.qport &= 0xff;\n\n#ifdef Q3CLIENT\n\tif (connectinfo.protocol == CP_QUAKE3)\n\t{\t//q3 requires some very strange things.\n\t\t//cl.splitclients = 1;\n\t\tif (q3)\n\t\t\tq3->cl.SendConnectPacket(cls.sockets, to, connectinfo.challenge, connectinfo.qport, cls.userinfo);\n\t\telse\n\t\t\tCL_ConnectAbort(\"Q3 plugin not loaded, cannot connect to q3 servers without it.\\n\");\n\t\treturn;\n\t}\n#endif\n\n\tif (connectinfo.subprotocol == PROTOCOL_VERSION_Q2EX)\n\t{\n\t\tsize_t foo;\n\t\tint seats = bound(1, cl_splitscreen.ival+1, MAX_SPLITS), i;\n\t\tQ_snprintfz(data, sizeof(data), \"%c%c%c%cconnect\", 255, 255, 255, 255);\n\n\t\t//qport+challenge were removed.\n\t\tQ_strncatz(data, va(\" %i %i\", connectinfo.subprotocol, seats), sizeof(data));\n\n\t\t//socials...\n\t\tfor (i = 0; i < seats; i++)\n\t\t{\n\t\t\tQ_strncatz(data, va(\" anon\"), sizeof(data));\n\n\t\t\t//also make sure they have a valid name field, Q2E kinda bugs out otherwise.\n\t\t\tif (!InfoBuf_FindKey(&cls.userinfo[i], \"name\", &foo))\n\t\t\t\tInfoBuf_SetValueForKey(&cls.userinfo[i], \"name\", va(\"%s-%i\", name.string, i+1));\n\t\t\tif (!InfoBuf_FindKey(&cls.userinfo[i], \"fov\", &foo))\n\t\t\t\tInfoBuf_SetValueForKey(&cls.userinfo[i], \"fov\", scr_fov.string);\n\t\t}\n\n\t\tQ_strncatz(data, \" \\\"\", sizeof(data));\n\t\t//note: this specific info string should lack the leading \\\\ char.\n\t\tQ_strncatz(data, va(\"qport\\\\%i\", connectinfo.qport), sizeof(data));\t//just in case a server enforces it (ie: someone connecting directly raw udp without the lobby junk)\n\t\tQ_strncatz(data, va(\"\\\\challenge\\\\%i\", connectinfo.challenge), sizeof(data));\t//just in case a server enforces it (ie: someone connecting directly raw udp without the lobby junk)\n\t\tif (connectinfo.spec==CIS_OBSERVE)\n\t\t\tQ_strncatz(data, \"\\\\spectator\\\\1\", sizeof(data));\n\n\t\t{\n\t\t\tconst char *ignorekeys[] = {\"prx\", \"*z_ext\", (connectinfo.spec!=CIS_DEFAULT)?\"spectator\":NULL, NULL};\n\t\t\tQ2EX_UserInfoToString(data+strlen(data), sizeof(data)-strlen(data), ignorekeys, seats);\n\t\t}\n\t\tQ_strncatz(data, \"\\\"\", sizeof(data));\n\t\tQ_strncatz(data, \"\\n\", sizeof(data));\n\n\t\tconnectinfo.ext.fte1 = connectinfo.ext.fte2 = connectinfo.ext.ez1 = 0;\n\t}\n\telse\n\t{\n\t\tQ_snprintfz(data, sizeof(data), \"%c%c%c%cconnect\", 255, 255, 255, 255);\n\n\t\tQ_strncatz(data, va(\" %i %i %i\", connectinfo.subprotocol, connectinfo.qport, connectinfo.challenge), sizeof(data));\n\n\t\t//userinfo0 has some twiddles for extensions from other qw engines.\n\t\tQ_strncatz(data, \" \\\"\", sizeof(data));\n\t\t//qwfwd proxy routing\n\t\tif ((a = strrchr(cls.servername, '@')))\n\t\t{\n\t\t\t*a = 0;\n\t\t\tQ_strncatz(data, va(\"\\\\prx\\\\%s\", cls.servername), sizeof(data));\n\t\t\t*a = '@';\n\t\t}\n\t\tif (connectinfo.spec==CIS_OBSERVE)\n\t\t\tQ_strncatz(data, \"\\\\spectator\\\\1\", sizeof(data));\n\t\t//the info itself\n\t\t{\n\t\t\tstatic const char *prioritykeys[] = {\"name\", \"password\", \"spectator\", \"lang\", \"rate\", \"team\", \"topcolor\", \"bottomcolor\", \"skin\", \"_\", \"*\", NULL};\n\t\t\tconst char *ignorekeys[] = {\"prx\", \"*z_ext\", (connectinfo.spec!=CIS_DEFAULT)?\"spectator\":NULL, NULL};\n\t\t\tInfoBuf_ToString(&cls.userinfo[0], data+strlen(data), sizeof(data)-strlen(data), prioritykeys, ignorekeys, NULL, &cls.userinfosync, &cls.userinfo[0]);\n\t\t}\n\t\tif (connectinfo.protocol == CP_QUAKEWORLD)\t//zquake extension info.\n\t\t\tQ_strncatz(data, va(\"\\\\*z_ext\\\\%i\", CLIENT_SUPPORTED_Z_EXTENSIONS), sizeof(data));\n\n\t\tQ_strncatz(data, \"\\\"\", sizeof(data));\n\n\t\tif (connectinfo.protocol == CP_QUAKE2 && connectinfo.subprotocol == PROTOCOL_VERSION_R1Q2)\n\t\t\tQ_strncatz(data, va(\" %d %d\", connectinfo.ext.mtu, 1905), sizeof(data));\t//mti, sub-sub-version\n\t\telse if (connectinfo.protocol == CP_QUAKE2 && connectinfo.subprotocol == PROTOCOL_VERSION_Q2PRO)\n\t\t\tQ_strncatz(data, va(\" %d 0 0 %d\", connectinfo.ext.mtu, 1021), sizeof(data));\t//mtu, netchan-fragmentation, zlib, sub-sub-version\n\n\t\tQ_strncatz(data, \"\\n\", sizeof(data));\n\t}\n\tif (connectinfo.ext.fte1)\n\t\tQ_strncatz(data, va(\"0x%x 0x%x\\n\", PROTOCOL_VERSION_FTE1, connectinfo.ext.fte1), sizeof(data));\n\tif (connectinfo.ext.fte2)\n\t\tQ_strncatz(data, va(\"0x%x 0x%x\\n\", PROTOCOL_VERSION_FTE2, connectinfo.ext.fte2), sizeof(data));\n\n\tif (connectinfo.ext.ez1)\n\t\tQ_strncatz(data, va(\"0x%x 0x%x\\n\", PROTOCOL_VERSION_EZQUAKE1, connectinfo.ext.ez1), sizeof(data));\n\n\t{\n\t\tint ourmtu;\n\t\tif (to->type == NA_LOOPBACK)\n\t\t\tourmtu = MAX_UDP_PACKET;\n\t\telse if (*net_mtu.string)\n\t\t\tourmtu = net_mtu.ival;\n\t\telse\n\t\t\tourmtu = 1440;\t//a safe bet. servers have an unsafe bet by default\n\t\tif (ourmtu < 0)\n\t\t\tourmtu = 0;\n\t\tif (connectinfo.ext.mtu > ourmtu)\n\t\t\tconnectinfo.ext.mtu = ourmtu;\n\t\tconnectinfo.ext.mtu &= ~7;\n\n\t\tif (connectinfo.ext.mtu > 0)\n\t\t\tQ_strncatz(data, va(\"0x%x %i\\n\", PROTOCOL_VERSION_FRAGMENT, connectinfo.ext.mtu), sizeof(data));\n\t}\n\n#ifdef HUFFNETWORK\n\tif (connectinfo.ext.compresscrc && net_compress.ival && Huff_CompressionCRC(connectinfo.ext.compresscrc))\n\t\tQ_strncatz(data, va(\"0x%x 0x%x\\n\", PROTOCOL_VERSION_HUFFMAN, LittleLong(connectinfo.ext.compresscrc)), sizeof(data));\n\telse\n#endif\n\t\tconnectinfo.ext.compresscrc = 0;\n\n\tinfo = CL_GUIDString(to);\n\tif (info)\n\t\tQ_strncatz(data, va(\"0x%x \\\"%s\\\"\\n\", PROTOCOL_INFO_GUID, info), sizeof(data));\n\n\tNET_SendPacket (cls.sockets, strlen(data), data, to);\n}\n\nchar *CL_TryingToConnect(void)\n{\n\tif (!connectinfo.trying)\n\t\treturn NULL;\n\n\tif (connectinfo.numadr < 1)\n\t\t;\n\telse if (connectinfo.adr[0].prot == NP_KEXLAN\n#ifdef SUPPORT_ICE\n\t\t\t|| connectinfo.adr[0].type == NA_ICE\n#endif\n\t\t\t)\n\t{\n\t\tchar status[1024];\n\t\tif (NET_GetConnectionCertificate(cls.sockets, &connectinfo.adr[0], QCERT_LOBBYSTATUS, status, sizeof(status))>0)\n\t\t\treturn va(\"%s\\n%s\", cls.servername, status);\n\t}\n\telse if (connectinfo.adr[0].prot == NP_RTC_TCP || connectinfo.adr[0].prot == NP_RTC_TLS)\n\t\treturn va(\"%s\\n%s\", cls.servername, \"Waiting for broker connection\");\n\treturn cls.servername;\n}\n\n#if defined(NQPROT) && defined(HAVE_SERVER)\nstatic void CL_NullReadPacket(void)\n{\t//just drop it all\n}\n#endif\n\n\nstruct resolvectx_s\n{\n\tnetadr_t adr[countof(connectinfo.adr)];\n\tsize_t found;\n\tchar servername[1];\n\t/*servername text*/\n};\nstatic void CL_ResolvedServer(void *vctx, void *data, size_t a, size_t b)\n{\n\tsize_t i;\n\tstruct resolvectx_s *ctx = vctx;\n\n\t//something screwed us over...\n\tif (strcmp(ctx->servername, cls.servername))\n\t\treturn;\n\n\tif (!ctx->found)\n\t{\n\t\tCL_ConnectAbort(\"Unable to resolve server address \\\"%s\\\"\\n\", ctx->servername);\n\t\treturn;\n\t}\n\n#ifdef HAVE_DTLS\n\tfor (i = 0; i < ctx->found; i++)\n\t{\n\t\tif (net_enable_dtls.ival>=4 || connectinfo.mode==CIM_QEONLY)// || (connectinfo.peercred.hash && net_enable_dtls.ival >= 1))\n\t\t{\t//if we've already established a dtls connection, stick with it\n\t\t\tif (ctx->adr[i].prot == NP_DGRAM)\n\t\t\t\tctx->adr[i].prot = NP_DTLS;\n\t\t}\n\t}\n#endif\n\n\tif (connectinfo.mode == CIM_Q2EONLY)\n\t{\n\t\tfor (i = 0; i < ctx->found; i++)\n\t\t{\t//if we've already established a dtls connection, stick with it, otherwise 'upgrade' from udp to 'kexlan' transport layer.\n\t\t\tif (ctx->adr[i].prot == NP_DGRAM)\n\t\t\t\tctx->adr[i].prot = NP_KEXLAN;\n\t\t}\n\t}\n\n\tconnectinfo.numadr = ctx->found;\n\tconnectinfo.nextadr = 0;\n\tconnectinfo.resolving = false;\n\tmemcpy(connectinfo.adr, ctx->adr, sizeof(*connectinfo.adr)*ctx->found);\n\n}\nstatic void CL_ResolveServer(void *vctx, void *data, size_t a, size_t b)\n{\n\tstruct resolvectx_s *ctx = vctx;\n\n\t//stupid logic for targ@prox2@[ws[s]://]prox1 chaining. just disable it if there's weird ws:// or whatever in there.\n\t//FIXME: really shouldn't be in there\n\tconst char *res = strrchr(ctx->servername, '/');\n\tconst char *host = strrchr(ctx->servername+1, '@');\n\tif (host && (!res || res > host))\n\t\thost++;\n\telse\n\t\thost = ctx->servername;\n\n\tctx->found = NET_StringToAdr2 (host, connectinfo.defaultport, ctx->adr, countof(ctx->adr), NULL);\n\n\tCOM_AddWork(WG_MAIN, CL_ResolvedServer, ctx, data, a, b);\n}\nqboolean CL_IsPendingServerAddress(netadr_t *adr)\n{\n\tsize_t i;\n\tfor (i = 0; i < connectinfo.numadr; i++)\n\t\tif (NET_CompareAdr(&connectinfo.adr[i], adr))\n\t\t\treturn true;\n\treturn false;\n}\nstatic qboolean CL_IsPendingServerBaseAddress(netadr_t *adr)\n{\n\tsize_t i;\n\tfor (i = 0; i < connectinfo.numadr; i++)\n\t\tif (NET_CompareBaseAdr(&connectinfo.adr[i], adr))\n\t\t\treturn true;\n\treturn false;\n}\n\n/*\n=================\nCL_CheckForResend\n\nResend a connect message if the last one has timed out\n\n=================\n*/\nvoid CL_CheckForResend (void)\n{\n\tchar\tdata[2048];\n\tdouble t1, t2;\n\tint contype = 0;\n\tqboolean keeptrying = true;\n\tnetadr_t *to;\n\n#ifdef HAVE_SERVER\n\tif (!cls.state && (!connectinfo.trying || sv.state != ss_clustermode) && sv.state)\n\t{\n\t\tconst char *lbp;\n#ifdef NQPROT\n\t\tqboolean proquakeangles = false;\n#endif\n#ifdef NETPREPARSE\n\t\textern cvar_t dpcompat_nopreparse;\n#endif\n\t\textern cvar_t sv_guidhash;\n\n\t\tif (connectinfo.time && realtime - connectinfo.time < 1.0)\n\t\t\treturn;\n\t\tmemset(&connectinfo, 0, sizeof(connectinfo));\n\t\tconnectinfo.time = realtime;\n\t\tQ_strncpyz (cls.servername, \"internalserver\", sizeof(cls.servername));\n\t\tCvar_ForceSet(&cl_servername, cls.servername);\n\t\tconnectinfo.numadr = NET_StringToAdr(cls.servername, 0, &connectinfo.adr[0]);\n\t\tconnectinfo.nextadr = 0;\n\t\tif (!connectinfo.numadr)\n\t\t\treturn;\t//erk?\n\n\t\tif (*cl_disconnectreason.string)\n\t\t\tCvar_Set(&cl_disconnectreason, \"\");\n\t\tconnectinfo.trying = true;\n\t\tconnectinfo.istransfer = false;\n\t\tconnectinfo.adr[0].prot = NP_DGRAM;\n\n\t\tNET_InitClient(sv.state != ss_clustermode);\n\n\t\t//netchan extensions... we skip the getchallenge part so we need to set these up still.\n\t\tconnectinfo.ext.mtu = 8192-16;\n\t\tconnectinfo.ext.compresscrc = 0;\n\t\tQ_strncpyz(connectinfo.ext.guidsalt, sv_guidhash.string, sizeof(connectinfo.ext.guidsalt));\n\n\t\tcls.state = ca_disconnected;\n\t\tswitch (svs.gametype)\n\t\t{\n#ifdef Q3CLIENT\n\t\tcase GT_QUAKE3:\n\t\t\tconnectinfo.protocol = CP_QUAKE3;\n\t\t\tbreak;\n#endif\n#ifdef Q2CLIENT\n\t\tcase GT_QUAKE2:\n\t\t\tconnectinfo.protocol = CP_QUAKE2;\n\t\t\tlbp = cl_loopbackprotocol.string;\n\t\t\tif (!strcmp(lbp, \"q2\"))\n\t\t\t{\t//vanilla\n\t\t\t\tconnectinfo.subprotocol = PROTOCOL_VERSION_Q2;\n\t\t\t\tconnectinfo.ext.fte1 = 0;\n\t\t\t}\n\t\t\telse if (!strcmp(lbp, \"q2e\") || !strcmp(lbp, STRINGIFY(PROTOCOL_VERSION_Q2EX)))\n\t\t\t{\t//no fte extensions\n\t\t\t\t//still provides big coords, bigger index sizes, and splitscreen(non-dynamic though)\n\t\t\t\tconnectinfo.subprotocol = PROTOCOL_VERSION_Q2EX;\n\t\t\t\tconnectinfo.ext.fte1 = 0;\n\t\t\t}\n\t\t\t//else if (!strcmp(lbp, \"r1q2\") || !strcmp(lbp, STRINGIFY(PROTOCOL_VERSION_R1Q2)))\n\t\t\t//else if (!strcmp(lbp, \"q2pro\") || !strcmp(lbp, STRINGIFY(PROTOCOL_VERSION_Q2PRO)))\n\t\t\telse\n\t\t\t{\n\t\t\t\tconnectinfo.subprotocol = PROTOCOL_VERSION_Q2EX;\n\t\t\t\tconnectinfo.ext.fte1 = 0;//PEXT_MODELDBL|PEXT_SOUNDDBL|PEXT_SPLITSCREEN;\n\t\t\t}\n\t\t\tconnectinfo.ext.fte2 = 0;\n\t\t\tconnectinfo.ext.ez1 = 0;\n\t\t\tbreak;\n#endif\n\t\tdefault:\n\t\t\tcl.movesequence = 0;\n\t\t\tlbp = cl_loopbackprotocol.string;\n\t\t\tif (!strcmp(lbp, \"\") || !strcmp(lbp, \"qw\") || progstype == PROG_H2)\n\t\t\t{\t//qw with all supported extensions -default\n\t\t\t\t//for hexen2 we always force fte's native qw protocol. other protocols won't cut it.\n\t\t\t\tconnectinfo.protocol = CP_QUAKEWORLD;\n\t\t\t\tconnectinfo.subprotocol = PROTOCOL_VERSION_QW;\n\t\t\t\tconnectinfo.ext.fte1 = Net_PextMask(PROTOCOL_VERSION_FTE1, false);\n\t\t\t\tconnectinfo.ext.fte2 = Net_PextMask(PROTOCOL_VERSION_FTE2, false);\n\t\t\t\tconnectinfo.ext.ez1 = Net_PextMask(PROTOCOL_VERSION_EZQUAKE1, false) & EZPEXT1_CLIENTADVERTISE;\n\t\t\t}\n\t\t\telse if (!strcmp(lbp, \"qwid\") || !strcmp(lbp, \"idqw\"))\n\t\t\t{\t//for recording .qwd files in any client\n\t\t\t\tconnectinfo.protocol = CP_QUAKEWORLD;\n\t\t\t\tconnectinfo.subprotocol = PROTOCOL_VERSION_QW;\n\t\t\t\tconnectinfo.ext.fte1 = 0;\n\t\t\t\tconnectinfo.ext.fte2 = 0;\n\t\t\t\tconnectinfo.ext.ez1 = 0;\n\t\t\t}\n#ifdef Q3CLIENT\n\t\t\telse if (!strcmp(lbp, \"q3\"))\n\t\t\t\tcls.protocol = CP_QUAKE3;\n#endif\n#ifdef NQPROT\n\t\t\telse if (!strcmp(lbp, \"random\"))\n\t\t\t{\t//for debugging.\n\t\t\t\tif (rand() & 1)\n\t\t\t\t{\n\t\t\t\t\tconnectinfo.protocol = CP_NETQUAKE;\n\t\t\t\t\tconnectinfo.subprotocol = CPNQ_FITZ666;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tconnectinfo.protocol = CP_QUAKEWORLD;\n\t\t\t\t\tconnectinfo.subprotocol = PROTOCOL_VERSION_QW;\n\t\t\t\t\tconnectinfo.ext.fte1 = Net_PextMask(PROTOCOL_VERSION_FTE1, false);\n\t\t\t\t\tconnectinfo.ext.fte2 = Net_PextMask(PROTOCOL_VERSION_FTE2, false);\n\t\t\t\t\tconnectinfo.ext.ez1 = Net_PextMask(PROTOCOL_VERSION_EZQUAKE1, false) & EZPEXT1_CLIENTADVERTISE;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (!strcmp(lbp, \"fitz\") || !strcmp(lbp, \"rmqe\") ||\n\t\t\t\t\t !strcmp(lbp, \"qs\") ||\n\t\t\t\t\t !strcmp(lbp, \"666\") || !strcmp(lbp, \"999\"))\n\t\t\t{\t//we don't really distinguish between fitz and rmq protocols. we just use 999 with bigcoords and 666 othewise.\n\t\t\t\tconnectinfo.protocol = CP_NETQUAKE;\n\t\t\t\tconnectinfo.subprotocol = CPNQ_FITZ666;\n\t\t\t}\n\t\t\telse if (!strcmp(lbp, \"qe\")||!strcmp(lbp, \"qex\")||!strcmp(lbp, \"kex\"))\n\t\t\t{\t//quake-ex has special quirks that cannot be defined by protocol numbers alone.\n\t\t\t\tconnectinfo.protocol = CP_NETQUAKE;\n\t\t\t\tconnectinfo.subprotocol = CPNQ_FITZ666;\n\t\t\t\tconnectinfo.mode = CIM_QEONLY;\n\t\t\t}\n\t\t\telse if (!strcmp(lbp, \"bjp1\") || !strcmp(lbp, \"bjp2\") || //placeholders only\n\t\t\t\t\t !strcmp(lbp, \"bjp3\") || !strcmp(lbp, \"bjp\"))\n\t\t\t{\n\t\t\t\tconnectinfo.protocol = CP_NETQUAKE;\n\t\t\t\tconnectinfo.subprotocol = CPNQ_BJP3;\n\t\t\t}\n\t\t\telse if (!strcmp(lbp, \"nq\"))\n\t\t\t{\n\t\t\t\tconnectinfo.protocol = CP_NETQUAKE;\n\t\t\t\tconnectinfo.subprotocol = CPNQ_ID;\n\t\t\t\tproquakeangles = true;\n\t\t\t}\n\t\t\telse if (!strcmp(lbp, \"nqid\") || !strcmp(lbp, \"idnq\"))\n\t\t\t{\n\t\t\t\tconnectinfo.protocol = CP_NETQUAKE;\n\t\t\t\tconnectinfo.subprotocol = CPNQ_ID;\n\t\t\t}\n\t\t\telse if (!strcmp(lbp, \"dp1\") || !strcmp(lbp, \"dpp1\")||\t//most of these are not supported, but parsed as placeholders on the slim chance that we ever do support them\n\t\t\t\t\t !strcmp(lbp, \"dp2\") || !strcmp(lbp, \"dpp2\")||\n\t\t\t\t\t !strcmp(lbp, \"dp3\") || !strcmp(lbp, \"dpp3\")||\n\t\t\t\t\t !strcmp(lbp, \"dp4\") || !strcmp(lbp, \"dpp4\")||\n\t\t\t\t\t !strcmp(lbp, \"dp5\") || !strcmp(lbp, \"dpp5\")||\t//we support this serverside, but not clientside.\n\t\t\t\t\t !strcmp(lbp, \"dp6\") || !strcmp(lbp, \"dpp6\"))\t//this one is supported.\n\t\t\t{\n\t\t\t\tconnectinfo.protocol = CP_NETQUAKE;\n\t\t\t\tconnectinfo.subprotocol = CPNQ_DP6;\n\t\t\t}\n\t\t\telse if (!strcmp(lbp, \"dp7\") || !strcmp(lbp, \"dpp7\") ||\n\t\t\t\t\t !strcmp(lbp, \"dp\") || !strcmp(lbp, \"xonotic\"))\t//family name, common usage.\n\t\t\t{\n\t\t\t\tconnectinfo.protocol = CP_NETQUAKE;\n\t\t\t\tconnectinfo.subprotocol = CPNQ_DP7;\n\t\t\t}\n\t\t\telse if (!strcmp(lbp, \"qss\") ||\n\t\t\t\t\t (progstype != PROG_QW && progstype != PROG_H2 && sv.state!=ss_clustermode && cl_splitscreen.ival <= 0))\t//h2 depends on various extensions and doesn't really match either protocol, but we go for qw because that gives us all sorts of extensions.\n\t\t\t{\n\t\t\t\tconnectinfo.protocol = CP_NETQUAKE;\n\t\t\t\tconnectinfo.subprotocol = CPNQ_FITZ666;\n\t\t\t\tconnectinfo.ext.fte1 = Net_PextMask(PROTOCOL_VERSION_FTE1, true);\n\t\t\t\tconnectinfo.ext.fte2 = Net_PextMask(PROTOCOL_VERSION_FTE2, true);\n\t\t\t\tconnectinfo.ext.ez1 = Net_PextMask(PROTOCOL_VERSION_EZQUAKE1, true) & EZPEXT1_CLIENTADVERTISE;\n\t\t\t}\n#endif\n\t\t\telse\n\t\t\t{\t//protocol wasn't recognised, and we didn't take the nq fallback, so that must mean we're going for qw.\n\t\t\t\tconnectinfo.protocol = CP_QUAKEWORLD;\n\t\t\t\tconnectinfo.subprotocol = PROTOCOL_VERSION_QW;\n\t\t\t\tconnectinfo.ext.fte1 = Net_PextMask(PROTOCOL_VERSION_FTE1, false);\n\t\t\t\tconnectinfo.ext.fte2 = Net_PextMask(PROTOCOL_VERSION_FTE2, false);\n\t\t\t\tconnectinfo.ext.ez1 = Net_PextMask(PROTOCOL_VERSION_EZQUAKE1, false) & EZPEXT1_CLIENTADVERTISE;\n\t\t\t}\n\n#ifdef NETPREPARSE\n\t\t\tif (dpcompat_nopreparse.ival)\n#endif\n\t\t\t{\n\t\t\t\t//disabling preparsing with hexen2 is unsupported.\n\t\t\t\tif (progstype == PROG_H2)\n\t\t\t\t\tCon_Printf(\"dpcompat_nopreparse is unsupported with hexen2\\n\");\n\t\t\t\telse if (progstype == PROG_QW && cls.protocol != CP_QUAKEWORLD)\n\t\t\t\t{\n\t\t\t\t\tconnectinfo.protocol = CP_QUAKEWORLD;\n\t\t\t\t\tconnectinfo.subprotocol = PROTOCOL_VERSION_QW;\n\t\t\t\t\tconnectinfo.ext.fte1 = Net_PextMask(PROTOCOL_VERSION_FTE1, false);\n\t\t\t\t\tconnectinfo.ext.fte2 = Net_PextMask(PROTOCOL_VERSION_FTE2, false);\n\t\t\t\t\tconnectinfo.ext.ez1 = Net_PextMask(PROTOCOL_VERSION_EZQUAKE1, false) & EZPEXT1_CLIENTADVERTISE;\n\t\t\t\t}\n\t\t\t\telse if (progstype != PROG_QW && cls.protocol == CP_QUAKEWORLD)\n\t\t\t\t{\n\t\t\t\t\tconnectinfo.protocol = CP_NETQUAKE;\n\t\t\t\t\tconnectinfo.subprotocol = CPNQ_DP7;\t//dpcompat_nopreparse is only really needed for DP mods that send unknowable svc_tempentity messages to the client.\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t//make sure the protocol within demos is actually correct/sane\n\t\t\tif (cls.demorecording == DPB_QUAKEWORLD && cls.protocol != CP_QUAKEWORLD)\n\t\t\t{\n\t\t\t\tconnectinfo.protocol = CP_QUAKEWORLD;\n\t\t\t\tconnectinfo.subprotocol = PROTOCOL_VERSION_QW;\n\t\t\t\tconnectinfo.ext.fte1 = Net_PextMask(PROTOCOL_VERSION_FTE1, false);\n\t\t\t\tconnectinfo.ext.fte2 = Net_PextMask(PROTOCOL_VERSION_FTE2, false);\n\t\t\t\tconnectinfo.ext.ez1 = Net_PextMask(PROTOCOL_VERSION_EZQUAKE1, false) & EZPEXT1_CLIENTADVERTISE;\n\t\t\t}\n#ifdef NQPROT\n\t\t\telse if (cls.demorecording == DPB_NETQUAKE && cls.protocol != CP_NETQUAKE)\n\t\t\t{\n\t\t\t\tconnectinfo.protocol = CP_NETQUAKE;\n\t\t\t\tconnectinfo.subprotocol = CPNQ_FITZ666;\n\t\t\t\t//FIXME: use pext.\n\t\t\t}\n#endif\n#ifdef Q2CLIENT\n\t\t\telse if (cls.demorecording == DPB_QUAKE2 && cls.protocol != CP_QUAKE2)\n\t\t\t{\n\t\t\t\tconnectinfo.protocol = CP_QUAKE2;\n\t\t\t\tconnectinfo.subprotocol = PROTOCOL_VERSION_Q2;\n\t\t\t\tconnectinfo.ext.fte1 = PEXT_MODELDBL|PEXT_SOUNDDBL|PEXT_SPLITSCREEN;\n\t\t\t\t//FIXME: use pext.\n\t\t\t}\n#endif\n\t\t\tbreak;\n\t\t}\n\n\t\tCL_FlushClientCommands();\t//clear away all client->server clientcommands.\n\n#ifdef NQPROT\n\t\tif (connectinfo.protocol == CP_NETQUAKE)\n\t\t{\n\t\t\tconnectinfo.numadr = NET_StringToAdr2 (cls.servername, connectinfo.defaultport, connectinfo.adr, 1, NULL);\n\t\t\tconnectinfo.nextadr = 0;\n\t\t\tif (!connectinfo.numadr)\n\t\t\t{\n\t\t\t\tCL_ConnectAbort(\"CL_CheckForResend: Bad server address \\\"%s\\\"\\n\", cls.servername);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tNET_AdrToString(data, sizeof(data), &connectinfo.adr[connectinfo.nextadr]);\n\n\t\t\t/*eat up the server's packets, to clear any lingering loopback packets (like disconnect commands... yes this might cause packetloss for other clients)*/\n\t\t\tsvs.sockets->ReadGamePacket = CL_NullReadPacket;\n\t\t\tNET_ReadPackets(svs.sockets);\n\t\t\tsvs.sockets->ReadGamePacket = SV_ReadPacket;\n\n\t\t\tnet_message.packing = SZ_RAWBYTES;\n\t\t\tnet_message.cursize = 0;\n\t\t\tMSG_BeginReading(&net_message, net_message.prim);\n\n\t\t\tif (connectinfo.mode == CIM_QEONLY)\n\t\t\t{\n\t\t\t\tnet_from = connectinfo.adr[connectinfo.nextadr];\n\t\t\t\tCmd_TokenizeString (va(\"connect %i %i %i \\\"\\\\name\\\\unconnected\\\"\", NQ_NETCHAN_VERSION_QEX, 0, SV_NewChallenge()), false, false);\n\n\t\t\t\tSVC_DirectConnect(0);\n\t\t\t}\n\t\t\telse if (connectinfo.subprotocol == CPNQ_ID && !proquakeangles)\n\t\t\t{\n\t\t\t\tnet_from = connectinfo.adr[connectinfo.nextadr];\n\t\t\t\tCmd_TokenizeString (va(\"connect %i %i %i \\\"\\\\name\\\\unconnected\\\"\", NQ_NETCHAN_VERSION, 0, SV_NewChallenge()), false, false);\n\n\t\t\t\tSVC_DirectConnect(0);\n\t\t\t}\n\t\t\telse if (connectinfo.subprotocol == CPNQ_BJP3)\n\t\t\t{\n\t\t\t\tnet_from = connectinfo.adr[connectinfo.nextadr];\n\t\t\t\tCmd_TokenizeString (va(\"connect %i %i %i \\\"\\\\name\\\\unconnected\\\\mod\\\\%i\\\"\", NQ_NETCHAN_VERSION, 0, SV_NewChallenge(), PROTOCOL_VERSION_BJP3), false, false);\n\n\t\t\t\tSVC_DirectConnect(0);\n\t\t\t}\n\t\t\telse if (connectinfo.subprotocol == CPNQ_FITZ666)\n\t\t\t{\n\t\t\t\tnet_from = connectinfo.adr[connectinfo.nextadr];\n\t\t\t\tCmd_TokenizeString (va(\"connect %i %i %i \\\"\\\\name\\\\unconnected\\\\mod\\\\%i\\\"\", NQ_NETCHAN_VERSION, 0, SV_NewChallenge(), PROTOCOL_VERSION_FITZ), false, false);\n\n\t\t\t\tSVC_DirectConnect(0);\n\t\t\t}\n\t\t\telse if (proquakeangles)\n\t\t\t{\n\t\t\t\tnet_from = connectinfo.adr[connectinfo.nextadr];\n\t\t\t\tCmd_TokenizeString (va(\"connect %i %i %i \\\"\\\\name\\\\unconnected\\\\mod\\\\1\\\"\", NQ_NETCHAN_VERSION, 0, SV_NewChallenge()), false, false);\n\n\t\t\t\tSVC_DirectConnect(0);\n\t\t\t}\n\t\t\telse if (1)\n\t\t\t{\n\t\t\t\tnet_from = connectinfo.adr[connectinfo.nextadr];\n\t\t\t\tQ_snprintfz(net_message.data, net_message.maxsize, \"xxxxconnect\\\\protocol\\\\darkplaces \"STRINGIFY(NQ_NETCHAN_VERSION)\"\\\\protocols\\\\DP7 DP6 DP5 RMQ FITZ NEHAHRABJP2 NEHAHRABJP NEHAHRABJP3 QUAKE\\\\challenge\\\\0x%x\\\\name\\\\%s\", SV_NewChallenge(), name.string);\n\t\t\t\tCmd_TokenizeString (net_message.data+4, false, false);\n\t\t\t\tSVC_DirectConnect(0);\n\t\t\t}\n\t\t\telse\n\t\t\t\tCL_ConnectToDarkPlaces(\"\", &connectinfo.adr[connectinfo.nextadr]);\n//\t\t\tconnectinfo.trying = false;\n\t\t}\n\t\telse\n#endif\n\t\t{\n\t\t\tif (!connectinfo.challenge)\n\t\t\t\tconnectinfo.challenge = rand();\n\t\t\tCL_SendConnectPacket (NULL);\n\t\t}\n\n\t\treturn;\n\t}\n#endif\n\n\tif (!connectinfo.trying)\n\t{\n\t\tif (*cl_servername.string)\n\t\t\tCvar_ForceSet(&cl_servername, \"\");\n\t\treturn;\n\t}\n\tif (startuppending || r_blockvidrestart || FS_DownloadingPackage())\n\t\treturn;\t//don't send connect requests until we've actually initialised fully. this isn't a huge issue, but makes the startup prints a little more sane.\n\n\tif (connectinfo.time && realtime - connectinfo.time < 5.0)\n\t{\n\t\tif (!connectinfo.clogged)\n\t\t\treturn;\n\t}\n\telse\n\t\tconnectinfo.clogged = false; //do the prints and everything.\n\n\tif (!cls.sockets)\t//only if its needed... we don't want to keep using a new port unless we have to\n\t\tNET_InitClient(false);\n\n#ifdef HAVE_DTLS\n\tif (connectinfo.numadr>0 && connectinfo.adr[0].prot == NP_DTLS)\n\t{\t//get through the handshake first, instead of waiting for a 5-sec timeout between polls.\n\t\tswitch(NET_SendPacket (cls.sockets, 0, NULL, &connectinfo.adr[0]))\n\t\t{\n\t\tcase NETERR_CLOGGED:\t//temporary failure\n\t\t\tconnectinfo.clogged = true;\n\t\t\treturn;\n\t\tcase NETERR_DISCONNECTED:\n\t\t\tCL_ConnectAbort(\"DTLS Certificate Verification Failure\\n\");\n\t\t\tbreak;\n\t\tcase NETERR_NOROUTE:\t//not an error here, just means we need to send a new handshake.\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tbreak;\n\t\t}\n\t}\n#endif\n\n\tt1 = Sys_DoubleTime ();\n\tif (!connectinfo.istransfer)\n\t{\n\t\tif ((!connectinfo.numadr||connectinfo.nextadr>connectinfo.numadr*60) && !connectinfo.resolving)\n\t\t{\n\t\t\tstruct resolvectx_s *rctx = Z_Malloc(sizeof(*rctx) + strlen(cls.servername));\n\t\t\tstrcpy(rctx->servername, cls.servername);\n\t\t\tconnectinfo.resolving = true;\n\t\t\tCOM_AddWork(WG_LOADER, CL_ResolveServer, rctx, NULL, 0, 0);\n\t\t}\n\t}\n\n\tCL_FlushClientCommands();\n\n\tt2 = Sys_DoubleTime ();\n\n\tCvar_ForceSet(&cl_servername, cls.servername);\n\n\tif (!connectinfo.numadr || !cls.sockets || connectinfo.resolving)\n\t\treturn;\t//nothing to do yet...\n\tif (!connectinfo.clogged)\n\t\tconnectinfo.time = realtime+t2-t1;\t// for retransmit requests\n\n\tto = &connectinfo.adr[connectinfo.nextadr%connectinfo.numadr];\n\tif (!NET_IsClientLegal(to))\n\t{\n\t\tCL_ConnectAbort (\"Illegal server address\\n\");\n\t\treturn;\n\t}\n\n\tif (!connectinfo.clogged)\n\t{\n#ifdef Q3CLIENT\n\t\tif (q3)\n\t\t\tq3->cl.SendAuthPacket(cls.sockets, to);\n#endif\n\n\t\tif ((connectinfo.istransfer || connectinfo.numadr>1) && to->prot != NP_RTC_TCP && to->prot != NP_RTC_TLS\n#ifdef SUPPORT_ICE\n\t\t&& to->type != NA_ICE\n#endif\n\t\t)\n\t\t\tCon_TPrintf (\"Connecting to %s\" S_COLOR_GRAY \"(%s)\" S_COLOR_WHITE \"...\\n\", cls.servername, NET_AdrToString(data, sizeof(data), to));\n\t\telse\n\t\t\tCon_TPrintf (\"Connecting to %s...\\n\", cls.servername);\n\t}\n\n\tif (connectinfo.clogged)\n\t\tconnectinfo.clogged = false;\n\n\tif (connectinfo.tries == 0 && connectinfo.nextadr < connectinfo.numadr)\n\t{\n\t\t//stupid logic for targ@prox2@[ws[s]://]prox1 chaining. just disable it if there's weird ws:// or whatever in there.\n\t\t//FIXME: really shouldn't be in there\n\t\tconst char *res = strrchr(cls.servername, '/');\n\t\tconst char *host = strrchr(cls.servername+1, '@');\n\t\tif (host && (!res || res > host))\n\t\t\thost++;\n\t\telse\n\t\t\thost = cls.servername;\n\n\t\tif (!NET_EnsureRoute(cls.sockets, \"conn\", &connectinfo.peercred, host, to, true))\n\t\t{\n\t\t\tCL_ConnectAbort (\"Unable to establish connection to %s\\n\", cls.servername);\n\t\t\treturn;\n\t\t}\n\t}\n\n\tif (to->prot == NP_DGRAM)\n\t\tconnectinfo.nextadr++;\t//cycle hosts with each ping (if we got multiple).\n\n\tif (connectinfo.mode==CIM_Q2EONLY)\n\t\tcontype |= 1;\t//don't ever try nq packets here.\n\telse if (connectinfo.mode==CIM_QEONLY || connectinfo.mode==CIM_NQONLY)\n\t\tcontype |= 2;\n\telse\n\t{\n\t\tcontype |= 1; /*always try qw type connections*/\n#ifdef VM_UI\n\t\tif (!(q3&&q3->ui.IsRunning()))\t//don't try to connect to nq servers when running a q3ui. I was getting annoying error messages from q3 servers due to this.\n#endif\n\t\t{\n\t\t\tCOM_Parse(com_protocolname.string);\n\t\t\tif (!strcmp(com_token, \"Quake2\"))\n\t\t\t{\n\t\t\t\tif (connectinfo.nextadr>3)\t//don't create an extra channel until we know our preferred one has failed.\n\t\t\t\t\tcontype |= 4; /*q2e's kex lan layer*/\n\t\t\t}\n\t\t\telse\n\t\t\t\tcontype |= 2; /*try nq connections periodically (or if its the default nq port)*/\n\t\t}\n\t}\n\n\t/*DP, QW, Q2, Q3*/\n\t/*NOTE: ioq3 has <challenge> <gamename> args. yes, a challenge to get a challenge.*/\n\tif (contype & 1)\n\t{\n\t\tchar tmp[256];\n\t\tif (strrchr(cls.servername, '@'))\t//if we're apparently using qwfwd then strictly adhere to vanilla's protocol so that qwfwd does not bug out. this will pollute gamedirs if stuff starts autodownloading from the wrong type of server, and probably show up vanilla-vs-rerelease glitches everywhere..\n\t\t\tQ_snprintfz (data, sizeof(data), \"%c%c%c%cgetchallenge\\n\", 255, 255, 255, 255);\n\t\telse\n\t\t\tQ_snprintfz (data, sizeof(data), \"%c%c%c%cgetchallenge %i %s\\n\", 255, 255, 255, 255, connectinfo.clchallenge, COM_QuotedString(com_protocolname.string, tmp, sizeof(tmp), false));\n\t\tswitch(NET_SendPacket (cls.sockets, strlen(data), data, to))\n\t\t{\n\t\tcase NETERR_CLOGGED:\t//temporary failure\n\t\t\tconnectinfo.clogged = true;\t//inhibits the wait between sends\n\t\t\tbreak;\n\t\tcase NETERR_SENT:\t\t//yay, works!\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tkeeptrying = false;\n\t\t\tbreak;\n\t\t}\n\t}\n\t/*NQ*/\n#ifdef NQPROT\n\tif ((contype & 2) && !connectinfo.clogged)\n\t{\n\t\tchar *e;\n\t\tint pwd;\n\t\tsizebuf_t sb;\n\t\tmemset(&sb, 0, sizeof(sb));\n\t\tsb.data = data;\n\t\tsb.maxsize = sizeof(data);\n\n\t\tMSG_WriteLong(&sb, LongSwap(NETFLAG_CTL | (strlen(NQ_NETCHAN_GAMENAME)+7)));\n\t\tMSG_WriteByte(&sb, CCREQ_CONNECT);\n\t\tMSG_WriteString(&sb, NQ_NETCHAN_GAMENAME);\n\t\tif (connectinfo.mode==CIM_QEONLY)\n\t\t\tMSG_WriteByte(&sb, NQ_NETCHAN_VERSION_QEX);\n\t\telse\n\t\t{\n\t\t\tMSG_WriteByte(&sb, NQ_NETCHAN_VERSION);\n\n\t\t\t/*NQ engines have a few extra bits on the end*/\n\t\t\t/*proquake servers wait for us to send them a packet before anything happens,\n\t\t\t  which means it corrects for our public port if our nat uses different public ports for different remote ports\n\t\t\t  thus all nq engines claim to be proquake\n\t\t\t*/\n\t\t\tif (!*password.string || !strcmp(password.string, \"none\"))\n\t\t\t\tpwd = 0;\n\t\t\telse\n\t\t\t{\n\t\t\t\tpwd = strtol(password.string, &e, 0);\n\t\t\t\tif (*e)\n\t\t\t\t\tpwd = CalcHashInt(&hash_md4, password.string, strlen(password.string));\n\t\t\t}\n\t\t\tMSG_WriteByte(&sb, MOD_PROQUAKE); /*'mod'*/\n\t\t\tMSG_WriteByte(&sb, 34); /*'mod' version*/\n\t\t\tMSG_WriteByte(&sb, 0); /*flags*/\n\t\t\tMSG_WriteLong(&sb, pwd); /*password*/\n\n\t\t\t/*FTE servers will detect this string and treat it as a qw challenge instead (if it allows qw clients), so protocol choice is deterministic*/\n\t\t\tif (contype & 1)\n\t\t\t{\n\t\t\t\tchar tmp[256];\n\t\t\t\tMSG_WriteString(&sb, va(\"getchallenge %i %s\\n\", connectinfo.clchallenge, COM_QuotedString(com_protocolname.string, tmp, sizeof(tmp), false)));\n\t\t\t}\n\t\t}\n\n\t\t*(int*)sb.data = LongSwap(NETFLAG_CTL | sb.cursize);\n\t\tswitch(NET_SendPacket (cls.sockets, sb.cursize, sb.data, to))\n\t\t{\n\t\tcase NETERR_CLOGGED:\t//temporary failure\n\t\tcase NETERR_SENT:\t\t//yay, works!\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tkeeptrying = false;\n\t\t\tbreak;\n\t\t}\n\t}\n#endif\n#ifdef Q2CLIENT\n\tif ((contype & 4) && !connectinfo.clogged)\n\t{\n#define KEXLAN_SHAMELESSSELFPROMOMAGIC \"\\x08\"\"CRANTIME\"\t//hey, if you can't shove your own nick in your network protocols then you're doing it wrong.\n#define KEXLAN_SUBPROTOCOL \"\\x08\"\"Quake II\"\t//this should be cvar-ised at some point, if its to ever be useful for anything but q2.\n\t\tstatic char pkt[] = \"\\x01\\x60\\x80\"KEXLAN_SHAMELESSSELFPROMOMAGIC KEXLAN_SUBPROTOCOL\"\\x01\";\n\t\tNET_SendPacket (cls.sockets, strlen(pkt), pkt, to);\n\t}\n#endif\n\n\tconnectinfo.tries++;\n\n\tif (!keeptrying)\n\t{\n\t\tif (to->prot != NP_DGRAM && connectinfo.nextadr+1 < connectinfo.numadr)\n\t\t{\n\t\t\tconnectinfo.nextadr++;\t//cycle hosts with each connection failure (if we got multiple addresses).\n\t\t\tconnectinfo.tries = 0;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tCL_ConnectAbort (\"Unable to connect to %s, giving up\\n\", cls.servername);\n\n\t\t\tNET_CloseClient();\n\t\t}\n\t}\n}\n\nstatic void CL_BeginServerConnect(char *chain, int port, qboolean noproxy, enum coninfomode_e mode, enum coninfospec_e spec)\n{\n\tconst char *host = chain;\n\tconst char *schemeend;\n\tchar *arglist, *c;\n\tsize_t presize;\n\n\tQ_strncpyz(cls.serverurl, chain, sizeof(cls.serverurl));\n\n\tfor (c = chain; *c; c++)\n\t{\n\t\tif (*c == '@')\n\t\t\thost=c+1;\n\t\telse if (*c == '/' || *c == '?')\n\t\t\tbreak;\t//stop if we find some path weirdness (like an authority).\n\t}\n\tpresize = host-chain;\n\tif (presize >= sizeof(cls.servername))\n\t{\n\t\tCL_ConnectAbort(\"server address too long\");\n\t\treturn;\t//no, get lost. panic.\n\t}\n\tmemcpy(cls.servername, chain, presize);\n\n\tschemeend = strstr(host, \"://\");\n\tif (schemeend)\n\t{\n\t\t//\"qw:tcp://host/observe\"\n\t\tconst char *schemestart = strchr(host, ':');\n\t\tconst struct urischeme_s *scheme;\n\n\t\tif (!schemestart || schemestart==schemeend)\n\t\t\tschemestart = host;\n\t\telse\n\t\t\tschemestart++;\n\n\t\t//the scheme is either a network scheme in which case we use it directly, or a game-specific scheme.\n\t\tscheme = NET_IsURIScheme(schemestart);\n\t\tif (scheme && scheme->prot == NP_INVALID)\n\t\t\tscheme = NULL;\t//qw:// or q3:// something that's just noise here.\n\t\tif (scheme && scheme->flags&URISCHEME_NEEDSRESOURCE)\n\t\t{\n\t\t\tQ_strncpyz (cls.servername+presize, schemestart, sizeof(cls.servername)-presize);\t//oh. will probably be okay then\n\t\t\targlist = NULL;\n\t\t}\n\t\telse\n\t\t{\t//not some '/foo' name, not rtc:// either...\n\t\t\tchar *sl = strchr(schemeend+3, '/');\n\t\t\tif (sl)\n\t\t\t{\n\t\t\t\tif (!strncmp(sl, \"/observe\", 8))\n\t\t\t\t{\n\t\t\t\t\tif (spec == CIS_DEFAULT)\n\t\t\t\t\t\tspec = CIS_OBSERVE;\n\t\t\t\t\telse if (spec != CIS_OBSERVE)\n\t\t\t\t\t\tCon_Printf(\"Ignoring 'observe'\\n\");\n\t\t\t\t\tmemmove(sl, sl+8, strlen(sl+8)+1);\n\t\t\t\t}\n\t\t\t\telse if (!strncmp(sl, \"/join\", 5))\n\t\t\t\t{\n\t\t\t\t\tif (spec == CIS_DEFAULT)\n\t\t\t\t\t\tspec = CIS_JOIN;\n\t\t\t\t\telse if (spec != CIS_OBSERVE)\n\t\t\t\t\t\tCon_Printf(\"Ignoring 'join'\\n\");\n\t\t\t\t\tmemmove(sl, sl+5, strlen(sl+5)+1);\n\t\t\t\t}\n\t\t\t\telse if (!strncmp(sl, \"/qtvplay\", 5))\n\t\t\t\t{\n\t\t\t\t\tchar buf[256];\n\t\t\t\t\t*sl = 0;\n\t\t\t\t\tCmd_ExecuteString(va(\"qtvplay %s\\n\", COM_QuotedString(schemeend+3, buf,sizeof(buf), false)), RESTRICT_LOCAL);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\telse if (!strncmp(sl, \"/\", 1) && (sl[1] == 0 || sl[1]=='?'))\n\t\t\t\t{\n\t\t\t\t\t//current spectator mode\n\t\t\t\t\tmemmove(sl, sl+1, strlen(sl+1)+1);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (scheme)\t//preserve the scheme, the netchan cares.\n\t\t\t\tQ_strncpyz (cls.servername+presize, schemestart, sizeof(cls.servername)-presize);\t//probably some game-specific mess that we don't know\n\t\t\telse\n\t\t\t\tQ_strncpyz (cls.servername+presize, schemeend+3, sizeof(cls.servername)-presize);\t//probably some game-specific mess that we don't know\n\t\t\targlist = strchr(cls.servername, '?');\n\t\t}\n\t}\n\telse\n\t{\n\t\tif (!strncmp(host, \"localhost\", 9))\n\t\t\tnoproxy = true;\t//FIXME: resolve the address here or something so that we don't end up using a proxy for lan addresses.\n\n\t\tif (strstr(host, \"://\") || *host == '/' || !*cl_proxyaddr.string || noproxy)\n\t\t\tQ_strncpyz (cls.servername+presize, host, sizeof(cls.servername)-presize);\n\t\telse\n\t\t\tQ_snprintfz(cls.servername+presize, sizeof(cls.servername)-presize, \"%s@%s\", host, cl_proxyaddr.string);\n\t\targlist = strchr(cls.servername, '?');\n\t}\n\n\tif (!port)\n\t\tport = cl_defaultport.value;\n\n\tCL_ConnectAbort(NULL);\n\tmemset(&connectinfo, 0, sizeof(connectinfo));\n\tif (*cl_disconnectreason.string)\n\t\tCvar_Set(&cl_disconnectreason, \"\");\n\tconnectinfo.trying = true;\n\tconnectinfo.defaultport = port;\n\tconnectinfo.protocol = CP_UNKNOWN;\n\tconnectinfo.mode = mode;\n\tconnectinfo.spec = spec;\n\tconnectinfo.clchallenge = rand()^(rand()<<16);\n\n\tconnectinfo.peercred.name = cls.servername;\n\tif (arglist)\n\t{\n\t\t*arglist++ = 0;\n\t\twhile (*arglist)\n\t\t{\n\t\t\tchar *e = strchr(arglist, '&');\n\t\t\tif (e)\n\t\t\t\t*e=0;\n\t\t\tif (!Q_strncasecmp(arglist, \"fp=\", 3))\n\t\t\t{\n\t\t\t\tsize_t l = 8*Base64_DecodeBlock(arglist+3, arglist+strlen(arglist), connectinfo.peercred.digest, sizeof(connectinfo.peercred.digest));\n\t\t\t\tif (l <= 160)\n\t\t\t\t\tconnectinfo.peercred.hash = &hash_sha1;\n\t\t\t\telse if (l <= 256)\n\t\t\t\t\tconnectinfo.peercred.hash = &hash_sha2_256;\n\t\t\t\telse if (l <= 512)\n\t\t\t\t\tconnectinfo.peercred.hash = &hash_sha2_512;\n\t\t\t\telse\n\t\t\t\t\tconnectinfo.peercred.hash = NULL;\n\t\t\t}\n\t\t\telse\n\t\t\t\tCon_Printf(CON_WARNING\"uri arg not known: \\\"%s\\\"\\n\", arglist);\n\n\t\t\tif (e)\n\t\t\t\targlist=e+1;\n\t\t\telse\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\tSCR_SetLoadingStage(LS_CONNECTION);\n\tCL_CheckForResend();\n}\n\nvoid CL_BeginServerReconnect(void)\n{\n#ifdef HAVE_SERVER\n\tif (isDedicated)\n\t{\n\t\tCon_TPrintf (\"Connect ignored - dedicated. set a renderer first\\n\");\n\t\treturn;\n\t}\n#endif\n#ifdef HAVE_DTLS\n\t{\n\t\tint i;\n\t\tfor (i = 0; i < connectinfo.numadr; i++)\n\t\t\tNET_DTLS_Disconnect(cls.sockets, &connectinfo.adr[i]);\n\t}\n#endif\n#ifdef SUPPORT_ICE\n\twhile (connectinfo.numadr)\t//remove any ICE addresses. probably we'll end up with no addresses left leaving us free to re-resolve giving us the original(ish) rtc connection.\n\t{\n\t\tif (connectinfo.adr[connectinfo.numadr-1].type != NA_ICE)\n\t\t\tbreak;\n\t\tconnectinfo.numadr--;\n\t}\n#endif\n\tif (*cl_disconnectreason.string)\n\t\tCvar_Set(&cl_disconnectreason, \"\");\n\tconnectinfo.trying = true;\n\tconnectinfo.istransfer = false;\n\tconnectinfo.time = 0;\n\tconnectinfo.tries = 0;\t//re-ensure routes.\n\tconnectinfo.nextadr = 0; //should at least be consistent, other than packetloss. yay.\n\tconnectinfo.clchallenge = rand()^(rand()<<16);\n\n\tNET_InitClient(false);\n}\n\nvoid CL_Transfer(netadr_t *adr)\n{\n\tconnectinfo.adr[0] = *adr;\n\tconnectinfo.numadr = 1;\n\tconnectinfo.istransfer = true;\n\tCL_CheckForResend();\n}\nvoid CL_Transfer_f(void)\n{\n\tchar oldguid[64];\n\tchar\t*server;\n\tif (Cmd_Argc() != 2)\n\t{\n\t\tCon_TPrintf (\"usage: cl_transfer <server>\\n\");\n\t\treturn;\n\t}\n\n\tCL_ConnectAbort(NULL);\n\tserver = Cmd_Argv (1);\n\tif (!*server)\n\t{\n\t\t//if they didn't specify a server, abort any active transfer/connection.\n\t\treturn;\n\t}\n\n\tQ_strncpyz(oldguid, connectinfo.guid, sizeof(oldguid));\n\tmemset(&connectinfo, 0, sizeof(connectinfo));\n\tconnectinfo.numadr = NET_StringToAdr(server, 0, &connectinfo.adr[0]);\n\tif (connectinfo.numadr)\n\t{\n\t\tconnectinfo.istransfer = true;\n\t\tQ_strncpyz(connectinfo.guid, oldguid, sizeof(oldguid));\t//retain the same guid on transfers\n\n\t\tCvar_Set(&cl_disconnectreason, \"Transferring....\");\n\t\tconnectinfo.trying = true;\n\t\tconnectinfo.defaultport = cl_defaultport.value;\n\t\tconnectinfo.protocol = CP_UNKNOWN;\n\t\tSCR_SetLoadingStage(LS_CONNECTION);\n\t\tCL_CheckForResend();\n\t}\n\telse\n\t{\n\t\tCon_Printf(\"cl_transfer: bad address\\n\");\n\t}\n}\n/*\n================\nCL_Connect_f\n\n================\n*/\nvoid CL_Connect_c(int argn, const char *partial, struct xcommandargcompletioncb_s *ctx);\nstatic void CL_Connect_f (void)\n{\n\tchar\t*server;\n\n\tif (Cmd_Argc() != 2)\n\t{\n\t\tCon_TPrintf (\"usage: connect <server>\\n\");\n\t\treturn;\n\t}\n\n\tserver = Cmd_Argv (1);\n\tserver = strcpy(alloca(strlen(server)+1), server);\n\n/*#ifdef HAVE_SERVER\n\tif (sv.state == ss_clustermode)\n\t\tCL_Disconnect (NULL);\n\telse\n#endif*/\n\t\tCL_Disconnect_f ();\n\n\tCL_BeginServerConnect(server, 0, false, CIM_DEFAULT, CIS_DEFAULT);\n}\n#if defined(CL_MASTER) && defined(HAVE_PACKET)\nstatic void CL_ConnectBestRoute_f (void)\n{\n\tchar\tserver[1024];\n\tint\t\tproxies;\n\tint\t\tdirectcost, chainedcost;\n\tif (Cmd_Argc() != 2)\n\t{\n\t\tCon_TPrintf (\"usage: connectbr <server>\\n\");\n\t\treturn;\n\t}\n\n\tproxies = Master_FindBestRoute(Cmd_Argv(1), server, sizeof(server), &directcost, &chainedcost);\n\tif (!*server)\n\t{\n\t\tCon_TPrintf (\"Unable to route to server\\n\");\n\t\treturn;\n\t}\n\telse if (proxies < 0)\n\t\tCon_TPrintf (\"Routing database is not initialised, connecting directly\\n\");\n\telse if (!proxies)\n\t\tCon_TPrintf (\"Routing table favours a direct connection\\n\");\n\telse if (proxies == 1)\n\t\tCon_TPrintf (\"Routing table favours a single proxy (%ims vs %ims)\\n\", chainedcost, directcost);\n\telse\n\t\tCon_TPrintf (\"Routing table favours chaining through %i proxies (%ims vs %ims)\\n\", proxies, chainedcost, directcost);\n\n/*#ifdef HAVE_SERVER\n\tif (sv.state == ss_clustermode)\n\t\tCL_Disconnect (NULL);\n\telse\n#endif*/\n\t\tCL_Disconnect_f ();\n\tCL_BeginServerConnect(server, 0, true, CIM_DEFAULT, CIS_DEFAULT);\n}\n#endif\n\nstatic void CL_Join_f (void)\n{\n\tchar\t*server;\n\n\tif (Cmd_Argc() != 2)\n\t{\n\t\tif (cls.state)\n\t\t{\t//Hmm. This server sucks.\n\t\t\tif ((cls.z_ext & Z_EXT_JOIN_OBSERVE) || cls.protocol != CP_QUAKEWORLD)\n\t\t\t\tCmd_ForwardToServer();\n\t\t\telse\n\t\t\t\tCbuf_AddText(\"\\nspectator 0;reconnect\\n\", RESTRICT_LOCAL);\n\t\t\treturn;\n\t\t}\n\t\tCon_Printf (\"join requires a connection or servername/ip\\n\");\n\t\treturn;\n\t}\n\n\tserver = Cmd_Argv (1);\n\tserver = strcpy(alloca(strlen(server)+1), server);\n\n\tCL_Disconnect_f ();\n\n\tCL_BeginServerConnect(server, 0, false, CIM_DEFAULT, CIS_JOIN);\n}\n\nvoid CL_Observe_f (void)\n{\n\tchar\t*server;\n\n\tif (Cmd_Argc() != 2)\n\t{\n\t\tif (cls.state)\n\t\t{\n\t\t\tif ((cls.z_ext & Z_EXT_JOIN_OBSERVE) || cls.protocol != CP_QUAKEWORLD)\n\t\t\t\tCmd_ForwardToServer();\n\t\t\telse\t//Hmm. This server sucks.\n\t\t\t\tCbuf_AddText(\"\\nspectator 1;reconnect\\n\", RESTRICT_LOCAL);\n\t\t\treturn;\n\t\t}\n\t\tCon_Printf (\"observe requires a connection or servername/ip\\n\");\n\t\treturn;\n\t}\n\n\tserver = Cmd_Argv (1);\n\tserver = strcpy(alloca(strlen(server)+1), server);\n\n\tCL_Disconnect_f ();\n\n\tCvar_Set(&spectator, \"1\");\n\n\tCL_BeginServerConnect(server, 0, false, CIM_DEFAULT, CIS_OBSERVE);\n}\n\n#ifdef NQPROT\nvoid CLNQ_Connect_f (void)\n{\n\tchar\t*server;\n\tenum coninfomode_e mode;\n\tint port = 26000;\n\n\tif (Cmd_Argc() != 2)\n\t{\n\t\tCon_TPrintf (\"usage: connect <server>\\n\");\n\t\treturn;\n\t}\n\n\tif (!strcmp(Cmd_Argv(0), \"connectqe\"))\n\t\tmode = CIM_QEONLY;\n\telse\n\t\tmode = CIM_NQONLY;\n\n\tserver = Cmd_Argv (1);\n\tserver = strcpy(alloca(strlen(server)+1), server);\n\n\tCL_Disconnect_f ();\n\n\tCL_BeginServerConnect(server, port, true, mode, CIS_DEFAULT/*doesn't really do spec/join stuff, but if the server asks for our info later...*/);\n}\n#endif\n#ifdef Q2CLIENT\nvoid CLQ2E_Connect_f (void)\n{\n\tchar\t*server;\n\n\tif (Cmd_Argc() != 2)\n\t{\n\t\tCon_TPrintf (\"usage: connect <server>\\n\");\n\t\treturn;\n\t}\n\n\tserver = Cmd_Argv (1);\n\tserver = strcpy(alloca(strlen(server)+1), server);\n\n\tCL_Disconnect_f ();\n\n\tCL_BeginServerConnect(server, PORT_Q2EXSERVER/*q2e servers ignore their own port cvar, so don't use the standard q2 port number here*/, true, CIM_Q2EONLY, CIS_DEFAULT);\n}\n#endif\n \n#ifdef IRCCONNECT\nvoid CL_IRCConnect_f (void)\n{\n\tCL_Disconnect_f ();\n\n\tif (FTENET_AddToCollection(cls.sockets, \"TCP\", Cmd_Argv(2), NA_IP, NP_IRC, false))\n\t{\n\t\tchar *server;\n\t\tserver = Cmd_Argv (1);\n\n\t\tCL_BeginServerConnect(va(\"irc://%s\", server), 0, true);\n\t}\n}\n#endif\n\n#ifdef TCPCONNECT\nvoid CL_TCPConnect_f (void)\n{\n\tif (!Q_strcasecmp(Cmd_Argv(0), \"tlsconnect\"))\n\t\tCbuf_InsertText(va(\"connect tls://%s\", Cmd_Argv(1)), Cmd_ExecLevel, true);\n\telse\n\t\tCbuf_InsertText(va(\"connect tcp://%s\", Cmd_Argv(1)), Cmd_ExecLevel, true);\n}\n#endif\n\n/*\n=====================\nCL_Rcon_f\n\n  Send the rest of the command line over as\n  an unconnected command.\n=====================\n*/\nvoid CL_Rcon_f (void)\n{\n\tchar\tmessage[1024];\n\tchar\t*password;\n\tint\t\ti;\n\tnetadr_t\tto;\n\n\ti = 1;\n\tpassword = rcon_password.string;\n\tif (!*password)\t//FIXME: this is strange...\n\t{\n\t\tif (Cmd_Argc() < 3)\n\t\t{\n\t\t\tCon_TPrintf (\"'rcon_password' is not set.\\n\");\n\t\t\tCon_TPrintf(\"usage: rcon (password) <command>\\n\");\n\t\t\treturn;\n\t\t}\n\t\tpassword = Cmd_Argv(1);\n\t\ti = 2;\n\t}\n\telse\n\t{\n\t\tif (Cmd_Argc() < 2)\n\t\t{\n\t\t\tCon_Printf(\"usage: rcon <command>\\n\");\n\t\t\treturn;\n\t\t}\n\t}\n\n\tmessage[0] = (char)255;\n\tmessage[1] = (char)255;\n\tmessage[2] = (char)255;\n\tmessage[3] = (char)255;\n\tmessage[4] = 0;\n\n\tQ_strncatz (message, \"rcon \", sizeof(message));\n\tif (cl_crypt_rcon.ival)\n\t{\n\t\tchar\tcryptpass[1024], crypttime[64];\n\t\tconst char *hex = \"0123456789ABCDEF\";\t//must be upper-case for compat with mvdsv.\n\t\ttime_t clienttime = time(NULL);\n\t\tsize_t digestsize;\n\t\tunsigned char digest[64];\n\t\tconst unsigned char **tokens = alloca(sizeof(*tokens)*(4+Cmd_Argc()*2));\n\t\tsize_t *tokensizes = alloca(sizeof(*tokensizes)*(4+Cmd_Argc()*2));\n\t\tint j, k;\n\t\tvoid *ctx = alloca(hash_sha1.contextsize);\n\n\t\tfor (j = 0; j < sizeof(time_t); j++)\n\t\t{\t//little-endian byte order, but big-endian nibble order. just screwed. for compat with ezquake.\n\t\t\tcrypttime[j*2+0] = hex[(clienttime>>(j*8+4))&0xf];\n\t\t\tcrypttime[j*2+1] = hex[(clienttime>>(j*8))&0xf];\n\t\t}\n\t\tcrypttime[j*2] = 0;\n\n\t\ttokens[0] = \"rcon \";\n\t\ttokens[1] = password;\n\t\ttokens[2] = crypttime;\n\t\ttokens[3] = \" \";\n\t\tfor (j=0 ; j<Cmd_Argc()-i ; j++)\n\t\t{\n\t\t\ttokens[4+j*2+0] = Cmd_Argv(i+j);\n\t\t\ttokens[4+j*2+1] = \" \";\n\t\t}\n\t\thash_sha1.init(ctx);\n\t\tfor (k = 0; k < 4+j*2; k++)\n\t\t\thash_sha1.process(ctx, tokens[k], strlen(tokens[k]));\n\t\thash_sha1.terminate(digest, ctx);\n\t\tdigestsize = hash_sha1.digestsize;\n\n\t\tfor (j = 0; j < digestsize; j++)\n\t\t{\n\t\t\tcryptpass[j*2+0] = hex[digest[j]>>4];\n\t\t\tcryptpass[j*2+1] = hex[digest[j]&0xf];\n\t\t}\n\t\tcryptpass[j*2] = 0;\n\t\tQ_strncatz (message, cryptpass, sizeof(message));\n\t\tQ_strncatz (message, crypttime, sizeof(message));\n\t}\n\telse\n\t\tQ_strncatz (message, password, sizeof(message));\n\tQ_strncatz (message, \" \", sizeof(message));\n\n\tfor ( ; i<Cmd_Argc() ; i++)\n\t{\n\t\tQ_strncatz (message, Cmd_Argv(i), sizeof(message));\n\t\tQ_strncatz (message, \" \", sizeof(message));\n\t}\n\n\tif (cls.state >= ca_connected)\n\t\tto = cls.netchan.remote_address;\n\telse\n\t{\n\t\tif (!strlen(rcon_address.string))\n\t\t{\n\t\t\tCon_TPrintf (\"You must either be connected,\\nor set the 'rcon_address' cvar\\nto issue rcon commands\\n\");\n\n\t\t\treturn;\n\t\t}\n\t\tif (!NET_StringToAdr (rcon_address.string, PORT_QWSERVER, &to))\n\t\t{\n\t\t\tCon_Printf(\"Unable to resolve target address\\n\");\n\t\t\treturn;\n\t\t}\n\t}\n\n\tNET_SendPacket (cls.sockets, strlen(message)+1, message, &to);\n}\n\nvoid CL_BlendFog(fogstate_t *result, fogstate_t *oldf, float time, fogstate_t *newf)\n{\n\tfloat nfrac;\n\tif (time >= newf->time)\n\t\tnfrac = 1;\n\telse if (time < oldf->time)\n\t\tnfrac = 0;\n\telse\n\t\tnfrac = (time - oldf->time) / (newf->time - oldf->time);\n\n\tFloatInterpolate(oldf->alpha, nfrac, newf->alpha, result->alpha);\n\tFloatInterpolate(oldf->depthbias, nfrac, newf->depthbias, result->depthbias);\n\tFloatInterpolate(oldf->density, nfrac, newf->density, result->density);\t//this should be non-linear, but that sort of maths is annoying.\n\tVectorInterpolate(oldf->colour, nfrac, newf->colour, result->colour);\n\n\tresult->time = time;\n}\nvoid CL_ResetFog(int ftype)\n{\n\t//blend from the current state, not the old state. this means things work properly if we've not reached the new state yet.\n\tCL_BlendFog(&cl.oldfog[ftype], &cl.oldfog[ftype], realtime, &cl.fog[ftype]);\n\n\t//reset the new state to defaults, to be filled in by the caller.\n\tmemset(&cl.fog[ftype], 0, sizeof(cl.fog[ftype]));\n\tcl.fog[ftype].time = realtime;\n\tcl.fog[ftype].density = 0;\n\tcl.fog[ftype].colour[0] = //SRGBf(0.3);\n\tcl.fog[ftype].colour[1] = //SRGBf(0.3);\n\tcl.fog[ftype].colour[2] = SRGBf(0.3);\n\tcl.fog[ftype].alpha = 1;\n\tcl.fog[ftype].depthbias = 0;\n\t/*\n\tcl.fog[ftype].end = 16384;\n\tcl.fog[ftype].height = 1<<30;\n\tcl.fog[ftype].fadedepth = 128;\n\t*/\n}\n\nstatic void CL_ReconfigureCommands(int newgame)\n{\n\tstatic int oldgame = ~0;\n\textern void SCR_SizeUp_f (void);\t//cl_screen\n\textern void SCR_SizeDown_f (void);\t//cl_screen\n#ifdef QUAKESTATS\n\textern void IN_Weapon (void);\t\t//cl_input\n\textern void IN_FireDown (void);\t\t//cl_input\n\textern void IN_FireUp (void);\t\t//cl_input\n\textern void IN_WWheelDown (void);\n\textern void IN_WWheelUp (void);\n\n\textern void IN_IWheelDown (void);\n\textern void IN_IWheelUp (void);\n\textern void IN_WeapNext_f (void);\n\textern void IN_WeapPrev_f (void);\n#endif\n\textern void CL_Say_f (void);\n\textern void CL_SayTeam_f (void);\n\textern void CL_Color_f (void);\n\tstatic const struct\n\t{\n\t\tconst char *name;\n\t\tvoid (*func) (void);\n\t\tconst char *description;\n\t\tunsigned int problemgames; //1<<CP_*\n\t} problemcmds[] =\n#define Q1 ((1u<<CP_QUAKEWORLD)|(1u<<CP_NETQUAKE))\n#define Q2 (1u<<CP_QUAKE2)\n#define Q3 (1u<<CP_QUAKE3)\n\t{\n\t\t{\"sizeup\",\t\tSCR_SizeUp_f,\t\"Increase viewsize\",\t\tQ3},\n\t\t{\"sizedown\",\tSCR_SizeDown_f,\t\"Decrease viewsize\",\t\tQ3},\n\t\t{\"color\",\t\tCL_Color_f,\t\t\"Change Player Colours\",\tQ3},\n\t\t{\"say\",\t\t\tCL_Say_f,\t\tNULL, Q3},\n\t\t{\"say_team\",\tCL_SayTeam_f,\tNULL, Q3},\n\n#ifdef QUAKESTATS\n\t\t{\"weapon\",\t\tIN_Weapon,\t\t\"Configures weapon priorities for the next +attack as an alternative for the impulse command\", ~Q1},\n\t\t{\"+fire\",\t\tIN_FireDown,\t\"'+fire 8 7' will fire lg if you have it and fall back on rl if you don't, and just fire your current weapon if neither are held. Releasing fire will then switch away to exploit a bug in most mods to deny your weapon upgrades to your killer.\", ~Q1},\n\t\t{\"-fire\",\t\tIN_FireUp,\t\tNULL, ~Q1},\n\t\t{\"+weaponwheel\",IN_WWheelDown,\t\"Quickly select a weapon without needing too many extra keybinds\", ~Q1},\n\t\t{\"-weaponwheel\",IN_WWheelUp,\tNULL, ~Q1},\n\n#ifdef Q2CLIENT\n\t\t{\"+wheel\",\t\tIN_WWheelDown,\t\"Quickly select a weapon without needing too many extra keybinds\", ~Q2},\n\t\t{\"-wheel\",\t\tIN_WWheelUp,\tNULL, ~Q2},\n\t\t{\"+wheel2\",\t\tIN_IWheelDown,\t\"Quickly use a powerup without needing too many extra keybinds\", ~Q2},\n\t\t{\"-wheel2\",\t\tIN_IWheelUp,\tNULL, ~Q2},\n\t\t{\"cl_weapnext\",\tIN_WeapNext_f,\t\"Select the next weapon\", ~Q2},\n\t\t{\"cl_weapprev\",\tIN_WeapPrev_f,\t\"Select the previous weapon\", ~Q2},\n#endif\n#endif\n\t};\n#undef Q1\n#undef Q2\n#undef Q3\n\n\tsize_t i;\n\n\tnewgame = 1<<newgame;\n\tfor (i = 0; i < countof(problemcmds); i++)\n\t{\n\t\tif ((problemcmds[i].problemgames & newgame) && !(problemcmds[i].problemgames & oldgame))\n\t\t\tCmd_RemoveCommand(problemcmds[i].name);\n\t\tif (!(problemcmds[i].problemgames & newgame) && (problemcmds[i].problemgames & oldgame))\n\t\t\tCmd_AddCommandD(problemcmds[i].name, problemcmds[i].func, problemcmds[i].description);\n\t}\n\toldgame = newgame;\n}\n\n/*\n=====================\nCL_ClearState\n\ngamestart==true says that we're changing map, as opposed to servers.\n=====================\n*/\nvoid CL_ClearState (qboolean gamestart)\n{\n\textern cvar_t cfg_save_auto;\n\tint\t\t\ti, j;\n\tdownloadlist_t *pendingdownloads, *faileddownloads;\n#ifdef HAVE_SERVER\n#define serverrunning (sv.state != ss_dead)\n#define tolocalserver NET_IsLoopBackAddress(&cls.netchan.remote_address)\n#else\n#define serverrunning false\n#define tolocalserver false\n#define SV_UnspawnServer()\n#endif\n\n\tCL_ReconfigureCommands(cls.protocol);\n\n\tCL_UpdateWindowTitle();\n\n\tCL_AllowIndependantSendCmd(false);\t//model stuff could be a problem.\n\n\tS_StopAllSounds (true);\n\tS_UntouchAll();\n\tS_ResetFailedLoad();\n\n\tCvar_ApplyLatches(CVAR_SERVEROVERRIDE, true);\n\n\tCon_DPrintf (\"Clearing memory\\n\");\n\tif (!serverrunning || !tolocalserver)\n\t{\n#ifdef HAVE_SERVER\n\t\tif (serverrunning && sv.state != ss_clustermode)\n\t\t\tSV_UnspawnServer();\n#endif\n\t\tMod_ClearAll ();\n\t\tr_regsequence++;\n\n\t\tCvar_ApplyLatches(CVAR_MAPLATCH, false);\n\t}\n\n\tCL_ClearParseState();\n\tCL_ClearTEnts();\n\tCL_ClearCustomTEnts();\n\tSurf_ClearSceneCache();\n#ifdef HEXEN2\n\tT_FreeInfoStrings();\n#endif\n\tSCR_ShowPic_ClearAll(false);\n\n\tif (cl.playerview[0].playernum == -1)\n\t{\t//left over from q2 connect.\n\t\tMedia_StopFilm(true);\n\t}\n\n\tfor (i = 0; i < UPDATE_BACKUP; i++)\n\t{\n\t\tif (cl.inframes[i].packet_entities.entities)\n\t\t{\n\t\t\tZ_Free(cl.inframes[i].packet_entities.entities);\n\t\t\tcl.inframes[i].packet_entities.entities = NULL;\n\t\t}\n\t}\n\n\tif (cl.lerpents)\n\t\tBZ_Free(cl.lerpents);\n\tif (cl.particle_ssprecaches)\n\t{\n\t\tfor (i = 0; i < MAX_SSPARTICLESPRE; i++)\n\t\t\tif (cl.particle_ssname[i])\n\t\t\t\tfree(cl.particle_ssname[i]);\n\t}\n\tif (cl.particle_csprecaches)\n\t{\n\t\tfor (i = 0; i < MAX_CSPARTICLESPRE; i++)\n\t\t\tif (cl.particle_csname[i])\n\t\t\t\tfree(cl.particle_csname[i]);\n\t}\n\tfor (i = 0; i < countof(cl.model_name); i++)\n\t\tif (cl.model_name[i])\n\t\t\tBZ_Free(cl.model_name[i]);\n#ifdef HAVE_LEGACY\n\tfor (i = 0; i < countof(cl.model_name_vwep); i++)\n\t\tif (cl.model_name_vwep[i])\n\t\t\tBZ_Free(cl.model_name_vwep[i]);\n#endif\n\tfor (i = 0; i < countof(cl.sound_name); i++)\n\t\tif (cl.sound_name[i])\n\t\t\tBZ_Free(cl.sound_name[i]);\n#ifdef Q2CLIENT\n\tfor (i = 0; i < Q2MAX_IMAGES; i++)\n\t\tif (cl.image_name[i])\n\t\t\tBZ_Free(cl.image_name[i]);\n\tfor (i = 0; i < Q2MAX_ITEMS; i++)\n\t\tif (cl.item_name[i])\n\t\t\tBZ_Free(cl.item_name[i]);\n#endif\n\n\twhile (cl.itemtimers)\n\t{\n\t\tstruct itemtimer_s *t = cl.itemtimers;\n\t\tcl.itemtimers = t->next;\n\t\tZ_Free(t);\n\t}\n\n\tif (!gamestart)\n\t{\n\t\tdownloadlist_t *next;\n\t\twhile(cl.downloadlist)\n\t\t{\n\t\t\tnext = cl.downloadlist->next;\n\t\t\tZ_Free(cl.downloadlist);\n\t\t\tcl.downloadlist = next;\n\t\t}\n\t\twhile(cl.faileddownloads)\n\t\t{\n\t\t\tnext = cl.faileddownloads->next;\n\t\t\tZ_Free(cl.faileddownloads);\n\t\t\tcl.faileddownloads = next;\n\t\t}\n\t}\n\tpendingdownloads = cl.downloadlist;\n\tfaileddownloads = cl.faileddownloads;\n\n#ifdef Q2CLIENT\n\tfor (i = 0; i < countof(cl.configstring_general); i++)\n\t{\n\t\tif (cl.configstring_general[i])\n\t\t\tZ_Free(cl.configstring_general[i]);\n\t}\n#endif\n\n\tfor (i = 0; i < MAX_SPLITS; i++)\n\t{\n\t\tfor (j = 0; j < MAX_CL_STATS; j++)\n\t\t\tif (cl.playerview[i].statsstr[j])\n\t\t\t\tZ_Free(cl.playerview[i].statsstr[j]);\n\t}\n\n\tZ_Free(cl.windowtitle);\n\tZ_Free(cl.serverpacknames);\n\tZ_Free(cl.serverpackhashes);\n\n\tInfoBuf_Clear(&cl.serverinfo, true);\n\n\tfor (i = 0; i < MAX_CLIENTS; i++)\n\t\tInfoBuf_Clear(&cl.players[i].userinfo, true);\n\n// wipe the entire cl structure\n\tmemset (&cl, 0, sizeof(cl));\n\n\tCL_ResetFog(FOGTYPE_AIR);\n\tCL_ResetFog(FOGTYPE_WATER);\n\tCL_ResetFog(FOGTYPE_SKYROOM);\n\n\tcl.mapstarttime = realtime;\n\tcl.gamespeed = 1;\n\tcl.protocol_qw = PROTOCOL_VERSION_QW;\t//until we get an svc_serverdata\n\tcl.allocated_client_slots = QWMAX_CLIENTS;\n#ifdef HAVE_SERVER\n\t//FIXME: we should just set it to 0 to make sure its set up properly elsewhere.\n\tif (sv.state)\n\t\tcl.allocated_client_slots = sv.allocated_client_slots;\n#endif\n\n//\tSZ_Clear (&cls.netchan.message);\n\n\tr_worldentity.model = NULL;\n\n// clear other arrays\n//\tmemset (cl_dlights, 0, sizeof(cl_dlights));\n\tZ_Free(cl_lightstyle);\n\tcl_lightstyle = NULL;\n\tcl_max_lightstyles = 0;\n\n\trtlights_first = rtlights_max = RTL_FIRST;\n\n\tfor (i = 0; i < MAX_SPLITS; i++)\n\t{\n\t\tVectorSet(cl.playerview[i].gravitydir, 0, 0, -1);\n\t\tcl.playerview[i].viewheight = DEFAULT_VIEWHEIGHT;\n\t\tcl.playerview[i].maxspeed = 320;\n\t\tcl.playerview[i].entgravity = 1;\n\n\t\tcl.playerview[i].chatstate = atoi(InfoBuf_ValueForKey(&cls.userinfo[i], \"chat\"));\n\t}\n#ifdef QUAKESTATS\n\tfor (i = 0; i < MAX_CLIENTS; i++)\t//in case some server doesn't support it\n\t\tcl.players[i].stats[STAT_VIEWHEIGHT] = cl.players[i].statsf[STAT_VIEWHEIGHT] = DEFAULT_VIEWHEIGHT;\n#endif\n\tcl.minpitch = -70;\n\tcl.maxpitch = 80;\n\n\tcl.oldgametime = 0;\n\tcl.gametime = 0;\n\tcl.gametimemark = 0;\n\tcl.splitclients = 1;\n\tcl.autotrack_hint = -1;\n\tcl.autotrack_killer = -1;\n\tcl.downloadlist = pendingdownloads;\n\tcl.faileddownloads = faileddownloads;\n\tcl.skyautorotate = 1;\n\n\tif (cfg_save_auto.ival && Cvar_UnsavedArchive())\n\t\tCmd_ExecuteString(\"cfg_save\\n\", RESTRICT_LOCAL);\n#ifdef CL_MASTER\n\tMasterInfo_WriteServers();\n#endif\n\n\tR_GAliasFlushSkinCache(false);\n}\n\n/*\n=====================\nCL_Disconnect\n\nSends a disconnect message to the server\nThis is also called on Host_Error, so it shouldn't cause any errors\n=====================\n*/\nvoid CL_Disconnect (const char *reason)\n{\n\tqbyte\tfinal[13];\n\tint i;\n\n\tif (reason)\n\t\tCvar_Set(&cl_disconnectreason, reason);\n\n\tconnectinfo.trying = false;\n\n\tSCR_SetLoadingStage(0);\n\n\tCvar_ApplyLatches(CVAR_SERVEROVERRIDE, true);\n\n// stop sounds (especially looping!)\n\tS_StopAllSounds (true);\n#ifdef VM_CG\n\tif (q3)\n\t\tq3->cl.Disconnect(cls.sockets);\n#endif\n#ifdef CSQC_DAT\n\tCSQC_Shutdown();\n#endif\n\t// if running a local server, shut it down\n\tif (cls.demoplayback != DPB_NONE)\n\t\tCL_StopPlayback ();\n\telse if (cls.state != ca_disconnected)\n\t{\n\t\tif (cls.demorecording)\n\t\t\tCL_Stop_f ();\n\n\t\tswitch(cls.protocol)\n\t\t{\n\t\tcase CP_NETQUAKE:\n#ifdef NQPROT\n\t\t\tfinal[0] = clc_disconnect;\n\t\t\tfinal[1] = clc_stringcmd;\n\t\t\tstrcpy (final+2, \"drop\");\n\t\t\tNetchan_Transmit (&cls.netchan, strlen(final)+1, final, 250000);\n\t\t\tNetchan_Transmit (&cls.netchan, strlen(final)+1, final, 250000);\n\t\t\tNetchan_Transmit (&cls.netchan, strlen(final)+1, final, 250000);\n#endif\n\t\t\tbreak;\n\t\tcase CP_PLUGIN:\n\t\t\tbreak;\n\t\tcase CP_QUAKE2:\n#ifdef Q2CLIENT\n\t\t\tfinal[0] = clcq2_stringcmd;\n\t\t\tif (cls.protocol_q2 == PROTOCOL_VERSION_Q2EX)\n\t\t\t{\n\t\t\t\tfinal[1] = 1;\n\t\t\t\tstrcpy (final+2, \"disconnect\");\n\t\t\t}\n\t\t\telse\n\t\t\t\tstrcpy (final+1, \"disconnect\");\n\t\t\tNetchan_Transmit (&cls.netchan, strlen(final)+1, final, 2500);\n\t\t\tNetchan_Transmit (&cls.netchan, strlen(final)+1, final, 2500);\n\t\t\tNetchan_Transmit (&cls.netchan, strlen(final)+1, final, 2500);\n#endif\n\t\t\tbreak;\n\t\tcase CP_QUAKE3:\n\t\t\tbreak;\n\t\tcase CP_QUAKEWORLD:\n\t\t\tfinal[0] = clc_stringcmd;\n\t\t\tstrcpy (final+1, \"drop\");\n\t\t\tNetchan_Transmit (&cls.netchan, strlen(final)+1, final, 2500);\n\t\t\tNetchan_Transmit (&cls.netchan, strlen(final)+1, final, 2500);\n\t\t\tNetchan_Transmit (&cls.netchan, strlen(final)+1, final, 2500);\n\t\t\tbreak;\n\t\tcase CP_UNKNOWN:\n\t\t\tbreak;\n\t\t}\n\n\t\tcls.state = ca_disconnected;\n\t\tcls.protocol = CP_UNKNOWN;\n\n\t\tcls.demoplayback = DPB_NONE;\n\t\tcls.demorecording = cls.timedemo = false;\n\n#ifdef HAVE_SERVER\n\t//running a server, and it's our own\n\t\tif (serverrunning && !tolocalserver && sv.state != ss_clustermode)\n\t\t\tSV_UnspawnServer();\n#endif\n\t}\n\tCam_Reset();\n\n\tif (cl.worldmodel)\n\t{\n\t\tMod_ClearAll();\n\t\tcl.worldmodel = NULL;\n\t}\n\n\tCL_Parse_Disconnected();\n\n\tCOM_FlushTempoaryPacks();\n\n\tr_worldentity.model = NULL;\n\tfor (i = 0; i < cl.splitclients; i++)\n\t\tcl.playerview[i].spectator = 0;\n\tcl.sendprespawn = false;\n\tcl.intermissionmode = IM_NONE;\n\tcl.oldgametime = 0;\n\n\tmemset(&r_refdef, 0, sizeof(r_refdef));\n\n#ifdef NQPROT\n\tcls.signon=0;\n#endif\n\tCL_StopUpload();\n\n\tCL_FlushClientCommands();\n\n#ifdef HAVE_SERVER\n\tif (!isDedicated)\n#endif\n\t{\n\t\tSCR_EndLoadingPlaque();\n\t\tV_ClearCShifts();\n\t}\n\n\tcl.servercount = 0;\n\tcls.findtrack = false;\n\tcls.realserverip.type = NA_INVALID;\n\n\twhile (cls.qtvviewers)\n\t{\n\t\tstruct qtvviewers_s *v = cls.qtvviewers;\n\t\tcls.qtvviewers = v->next;\n\t\tZ_Free(v);\n\t}\n\n#ifdef TCPCONNECT\n\t//disconnects it, without disconnecting the others.\n\tFTENET_AddToCollection(cls.sockets, \"conn\", NULL, NA_INVALID, NP_DGRAM);\n#endif\n\n\tCvar_ForceSet(&cl_servername, \"\");\n\n\tCL_ClearState(false);\n\n\tFS_PureMode(NULL, 0, NULL, NULL, NULL, NULL, 0);\n\n\tAlias_WipeStuffedAliases();\n\n\t//now start up the csqc/menu module again.\n//\t(void)CSQC_UnconnectedInit();\n}\n\n#undef serverrunning\n#undef tolocalserver\n\nvoid CL_Disconnect_f (void)\n{\n#ifdef HAVE_SERVER\n\tif (sv.state)\n\t\tSV_UnspawnServer();\n#endif\n\n\tCL_Disconnect (NULL);\n\tCL_ConnectAbort(NULL);\n\tNET_CloseClient();\n\n\t(void)CSQC_UnconnectedInit();\n}\n\nvoid CL_Disconnect2_f (void)\n{\n\tchar *reason = Cmd_Argv(1);\n\tif (*reason)\n\t\tCvar_Set(&cl_disconnectreason, reason);\n\tCL_Disconnect_f();\n}\n\n/*\n====================\nCL_User_f\n\nuser <name or userid>\n\nDump userdata / masterdata for a user\n====================\n*/\nvoid CL_User_f (void)\n{\n\tint\t\tuid;\n\tint\t\ti;\n\tqboolean found = false;\n\n#ifdef HAVE_SERVER\n\tif (sv.state)\n\t{\n\t\tSV_User_f();\n\t\treturn;\n\t}\n#endif\n\n\tif (Cmd_Argc() != 2)\n\t{\n\t\tCon_TPrintf (\"Usage: user <username / userid>\\n\");\n\t\treturn;\n\t}\n\n\tuid = atoi(Cmd_Argv(1));\n\n\tfor (i=0 ; i<MAX_CLIENTS ; i++)\n\t{\n\t\tif (!cl.players[i].name[0])\n\t\t\tcontinue;\n\t\tif (cl.players[i].userid == uid\n\t\t|| !strcmp(cl.players[i].name, Cmd_Argv(1)) )\n\t\t{\n\t\t\tif (!cl.players[i].userinfovalid)\n\t\t\t\tCon_Printf(\"name: %s\\ncolour %i %i\\nping: %i\\n\", cl.players[i].name, cl.players[i].rbottomcolor, cl.players[i].rtopcolor, cl.players[i].ping);\n\t\t\telse\n\t\t\t{\n\t\t\t\tInfoBuf_Print (&cl.players[i].userinfo, \"\");\n\t\t\t\tCon_Printf(\"[%u, %u]\\n\", (unsigned)cl.players[i].userinfo.totalsize, (unsigned)cl.players[i].userinfo.numkeys);\n\t\t\t}\n\t\t\tfound = true;\n\t\t}\n\t}\n\tif (!found)\n\t\tCon_TPrintf (\"User not in server.\\n\");\n}\n\n/*\n====================\nCL_Users_f\n\nDump userids for all current players\n====================\n*/\nvoid CL_Users_f (void)\n{\n\tint\t\ti;\n\tint\t\tc;\n\tstruct qtvviewers_s *v;\n\n\tc = 0;\n\tCon_TPrintf (\"userid frags name\\n\");\n\tCon_TPrintf (\"------ ----- ----\\n\");\n\tfor (i=0 ; i<MAX_CLIENTS ; i++)\n\t{\n\t\tif (cl.players[i].name[0])\n\t\t{\n\t\t\tCon_TPrintf (\"%6i %4i ^[%s\\\\player\\\\%i^]\\n\", cl.players[i].userid, cl.players[i].frags, cl.players[i].name, i);\n\t\t\tc++;\n\t\t}\n\t}\n\n\tfor (v = cls.qtvviewers; v; v = v->next)\n\t{\n\t\tCon_Printf (\"%6s %4s ^[%s^]\\n\", \"\", \"-\", v->name);\n\t}\n\n\tCon_TPrintf (\"%i total users\\n\", c);\n}\n\n\nstatic struct {\n\tconst char *name;\n\tunsigned int rgb;\n} csscolours[] = {\n\t//php-defined colours\n\t{\"aliceblue\",\t\t0xf0f8ff},\n\t{\"antiquewhite\",\t0xfaebd7},\n\t{\"aqua\",\t\t\t0x00ffff},\n\t{\"aquamarine\",\t\t0x7fffd4},\n\t{\"azure\",\t\t\t0xf0ffff},\n\t{\"beige\",\t\t\t0xf5f5dc},\n\t{\"bisque\",\t\t\t0xffe4c4},\n\t{\"black\",\t\t\t0x000000},\n\t{\"blanchedalmond\",\t0xffebcd},\n\t{\"blue\",\t\t\t0x0000ff},\n\t{\"blueviolet\",\t\t0x8a2be2},\n\t{\"brown\",\t\t\t0xa52a2a},\n\t{\"burlywood\",\t\t0xdeb887},\n\t{\"cadetblue\",\t\t0x5f9ea0},\n\t{\"chartreuse\",\t\t0x7fff00},\n\t{\"chocolate\",\t\t0xd2691e},\n\t{\"coral\",\t\t\t0xff7f50},\n\t{\"cornflowerblue\",\t0x6495ed},\n\t{\"cornsilk\",\t\t0xfff8dc},\n\t{\"crimson\",\t\t\t0xdc143c},\n\t{\"cyan\",\t\t\t0x00ffff},\n\t{\"darkblue\",\t\t0x00008b},\n\t{\"darkcyan\",\t\t0x008b8b},\n\t{\"darkgoldenrod\",\t0xb8860b},\n\t{\"darkgray\",\t\t0xa9a9a9},\n\t{\"darkgreen\",\t\t0x006400},\n\t{\"darkgrey\",\t\t0xa9a9a9},\n\t{\"darkkhaki\",\t\t0xbdb76b},\n\t{\"darkmagenta\",\t\t0x8b008b},\n\t{\"darkolivegreen\",\t0x556b2f},\n\t{\"darkorange\",\t\t0xff8c00},\n\t{\"darkorchid\",\t\t0x9932cc},\n\t{\"darkred\",\t\t\t0x8b0000},\n\t{\"darksalmon\",\t\t0xe9967a},\n\t{\"darkseagreen\",\t0x8fbc8f},\n\t{\"darkslateblue\",\t0x483d8b},\n\t{\"darkslategray\",\t0x2f4f4f},\n\t{\"darkslategrey\",\t0x2f4f4f},\n\t{\"darkturquoise\",\t0x00ced1},\n\t{\"darkviolet\",\t\t0x9400d3},\n\t{\"deeppink\",\t\t0xff1493},\n\t{\"deepskyblue\",\t\t0x00bfff},\n\t{\"dimgray\",\t\t\t0x696969},\n\t{\"dimgrey\",\t\t\t0x696969},\n\t{\"dodgerblue\",\t\t0x1e90ff},\n\t{\"firebrick\",\t\t0xb22222},\n\t{\"floralwhite\",\t\t0xfffaf0},\n\t{\"forestgreen\",\t\t0x228b22},\n\t{\"fuchsia\",\t\t\t0xff00ff},\n\t{\"gainsboro\",\t\t0xdcdcdc},\n\t{\"ghostwhite\",\t\t0xf8f8ff},\n\t{\"gold\",\t\t\t0xffd700},\n\t{\"goldenrod\",\t\t0xdaa520},\n\t{\"gray\",\t\t\t0x808080},\n\t{\"green\",\t\t\t0x008000},\n\t{\"greenyellow\",\t\t0xadff2f},\n\t{\"grey\",\t\t\t0x808080},\n\t{\"honeydew\",\t\t0xf0fff0},\n\t{\"hotpink\",\t\t\t0xff69b4},\n\t{\"indianred\",\t\t0xcd5c5c},\n\t{\"indigo\",\t\t\t0x4b0082},\n\t{\"ivory\",\t\t\t0xfffff0},\n\t{\"khaki\",\t\t\t0xf0e68c},\n\t{\"lavender\",\t\t0xe6e6fa},\n\t{\"lavenderblush\",\t0xfff0f5},\n\t{\"lawngreen\",\t\t0x7cfc00},\n\t{\"lemonchiffon\",\t0xfffacd},\n\t{\"lightblue\",\t\t0xadd8e6},\n\t{\"lightcoral\",\t\t0xf08080},\n\t{\"lightcyan\",\t\t0xe0ffff},\n\t{\"lightgoldenrodyellow\",0xfafad2},\n\t{\"lightgray\",\t\t0xd3d3d3},\n\t{\"lightgreen\",\t\t0x90ee90},\n\t{\"lightgrey\",\t\t0xd3d3d3},\n\t{\"lightpink\",\t\t0xffb6c1},\n\t{\"lightsalmon\",\t\t0xffa07a},\n\t{\"lightseagreen\",\t0x20b2aa},\n\t{\"lightskyblue\",\t0x87cefa},\n\t{\"lightslategray\",\t0x778899},\n\t{\"lightslategrey\",\t0x778899},\n\t{\"lightsteelblue\",\t0xb0c4de},\n\t{\"lightyellow\",\t\t0xffffe0},\n\t{\"lime\",\t\t\t0x00ff00},\n\t{\"limegreen\",\t\t0x32cd32},\n\t{\"linen\",\t\t\t0xfaf0e6},\n\t{\"magenta\",\t\t\t0xff00ff},\n\t{\"maroon\",\t\t\t0x800000},\n\t{\"mediumaquamarine\",0x66cdaa},\n\t{\"mediumblue\",\t\t0x0000cd},\n\t{\"mediumorchid\",\t0xba55d3},\n\t{\"mediumpurple\",\t0x9370db},\n\t{\"mediumseagreen\",\t0x3cb371},\n\t{\"mediumslateblue\",\t0x7b68ee},\n\t{\"mediumspringgreen\",0x00fa9a},\n\t{\"mediumturquoise\",\t0x48d1cc},\n\t{\"mediumvioletred\",\t0xc71585},\n\t{\"midnightblue\",\t0x191970},\n\t{\"mintcream\",\t\t0xf5fffa},\n\t{\"mistyrose\",\t\t0xffe4e1},\n\t{\"moccasin\",\t\t0xffe4b5},\n\t{\"navajowhite\",\t\t0xffdead},\n\t{\"navy\",\t\t\t0x000080},\n\t{\"oldlace\",\t\t\t0xfdf5e6},\n\t{\"olive\",\t\t\t0x808000},\n\t{\"olivedrab\",\t\t0x6b8e23},\n\t{\"orange\",\t\t\t0xffa500},\n\t{\"orangered\",\t\t0xff4500},\n\t{\"orchid\",\t\t\t0xda70d6},\n\t{\"palegoldenrod\",\t0xeee8aa},\n\t{\"palegreen\",\t\t0x98fb98},\n\t{\"paleturquoise\",\t0xafeeee},\n\t{\"palevioletred\",\t0xdb7093},\n\t{\"papayawhip\",\t\t0xffefd5},\n\t{\"peachpuff\",\t\t0xffdab9},\n\t{\"peru\",\t\t\t0xcd853f},\n\t{\"pink\",\t\t\t0xffc0cb},\n\t{\"plum\",\t\t\t0xdda0dd},\n\t{\"powderblue\",\t\t0xb0e0e6},\n\t{\"purple\",\t\t\t0x800080},\n\t{\"red\",\t\t\t\t0xff0000},\n\t{\"rosybrown\",\t\t0xbc8f8f},\n\t{\"royalblue\",\t\t0x4169e1},\n\t{\"saddlebrown\",\t\t0x8b4513},\n\t{\"salmon\",\t\t\t0xfa8072},\n\t{\"sandybrown\",\t\t0xf4a460},\n\t{\"seagreen\",\t\t0x2e8b57},\n\t{\"seashell\",\t\t0xfff5ee},\n\t{\"sienna\",\t\t\t0xa0522d},\n\t{\"silver\",\t\t\t0xc0c0c0},\n\t{\"skyblue\",\t\t\t0x87ceeb},\n\t{\"slateblue\",\t\t0x6a5acd},\n\t{\"slategray\",\t\t0x708090},\n\t{\"slategrey\",\t\t0x708090},\n\t{\"snow\",\t\t\t0xfffafa},\n\t{\"springgreen\",\t\t0x00ff7f},\n\t{\"steelblue\",\t\t0x4682b4},\n\t{\"tan\",\t\t\t\t0xd2b48c},\n\t{\"teal\",\t\t\t0x008080},\n\t{\"thistle\",\t\t\t0xd8bfd8},\n\t{\"tomato\",\t\t\t0xff6347},\n\t{\"turquoise\",\t\t0x40e0d0},\n\t{\"violet\",\t\t\t0xee82ee},\n\t{\"wheat\",\t\t\t0xf5deb3},\n\t{\"white\",\t\t\t0xffffff},\n\t{\"whitesmoke\",\t\t0xf5f5f5},\n\t{\"yellow\",\t\t\t0xffff00},\n\t{\"yellowgreen\",\t\t0x9acd32},\n};\n\nint CL_ParseColour(const char *colt)\n{\n\tchar *e;\n\tint col;\n\tsize_t i;\n\tif (!strncmp(colt, \"0x\", 2))\n\t\tcol = 0xff000000|strtoul(colt+2, NULL, 16);\n\telse\n\t{\n\t\tcol = strtoul(colt, &e, 0);\n\t\tif (*e)\n\t\t{\n\t\t\tcol = 0;\n\t\t\tfor (i = 0; i < countof(csscolours); i++)\n\t\t\t\tif (!Q_strcasecmp(colt, csscolours[i].name))\n\t\t\t\t{\n\t\t\t\t\tcol = 0xff000000 | csscolours[i].rgb;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tcol &= 15;\n\t\t\tif (col > 13)\n\t\t\t\tcol = 13;\n\t\t}\n\t}\n\treturn col;\n}\nconst char *CL_ColourName(const char *colt)\n{\n\tint col = CL_ParseColour(colt);\n\tsize_t i;\n\tif (col & 0xff000000)\n\t{\n\t\tcol &= ~0xff000000;\n\t\tfor (i = 0; i < countof(csscolours); i++)\n\t\t\tif (csscolours[i].rgb == col)\n\t\t\t\tcolt = csscolours[i].name;\n\t}\n\treturn colt;\n}\n\n\nvoid CL_Color_c(int argn, const char *partial, struct xcommandargcompletioncb_s *ctx)\n{\n\tint len;\n\tsize_t i;\n\tif (argn == 1 || argn == 2)\n\t{\n\t\tlen = strlen(partial);\n\t\tif (*partial >= '0' && *partial <= '9')\n\t\t\t;\n\t\telse for (i = 0; i < countof(csscolours); i++)\n\t\t{\n\t\t\tif (!Q_strncasecmp(partial, csscolours[i].name, len))\n\t\t\t\tctx->cb(csscolours[i].name, va(\"^x%x%x%x%s\", (csscolours[i].rgb>>20)&15, (csscolours[i].rgb>>12)&15, (csscolours[i].rgb>>4)&15, csscolours[i].name), NULL, ctx);\n\t\t}\n\t}\n}\nvoid CL_Color_f (void)\n{\n\t// just for quake compatability...\n\tint\t\ttop, bottom;\n\tchar\tnum[16];\n\tint  pnum = CL_TargettedSplit(true);\n\n\tqboolean server_owns_colour;\n\n\tchar *t;\n\tchar *b;\n\n\tif (Cmd_Argc() == 1)\n\t{\n\t\tconst char *t = InfoBuf_ValueForKey(&cls.userinfo[pnum], \"topcolor\");\n\t\tconst char *b = InfoBuf_ValueForKey(&cls.userinfo[pnum], \"bottomcolor\");\n\t\tt = CL_ColourName(t);\n\t\tb = CL_ColourName(b);\n\t\tif (!*t)\n\t\t\tt = \"0\";\n\t\tif (!*b)\n\t\t\tb = \"0\";\n\t\tif (!strcmp(t, b))\n\t\t\tCon_TPrintf (\"\\\"color\\\" is \\\"%s\\\"\\n\", t, b);\n\t\telse\n\t\t\tCon_TPrintf (\"\\\"color\\\" is \\\"%s %s\\\"\\n\", t, b);\n\t\tCon_TPrintf (\"usage: color <0xRRGGBB> [0xRRGGBB]\\n\");\n\t\treturn;\n\t}\n\n\tif (Cmd_FromGamecode())\n\t\tserver_owns_colour = true;\n\telse\n\t\tserver_owns_colour = false;\n\n\n\tt = Cmd_Argv(1);\n\tb = (Cmd_Argc()==2)?t:Cmd_Argv(2);\n\tif (!strcmp(t, \"-1\"))\n\t\tt = InfoBuf_ValueForKey(&cls.userinfo[pnum], \"topcolor\");\n\ttop = CL_ParseColour(t);\n\tif (!strcmp(b, \"-1\"))\n\t\tb = InfoBuf_ValueForKey(&cls.userinfo[pnum], \"bottomcolor\");\n\tbottom = CL_ParseColour(b);\n\n\tQ_snprintfz (num, sizeof(num), (top&0xff000000)?\"0x%06x\":\"%i\", top & 0xffffff);\n\tif (top == 0)\n\t\t*num = '\\0';\n\tif (Cmd_ExecLevel>RESTRICT_SERVER) //colour command came from server for a split client\n\t\tCbuf_AddText(va(\"p%i cmd setinfo topcolor \\\"%s\\\"\\n\", pnum+1, num), Cmd_ExecLevel);\n//\telse if (server_owns_colour)\n//\t\tCvar_LockFromServer(&topcolor, num);\n\telse\n\t\tCL_SetInfo(pnum, \"topcolor\", num);\n\tQ_snprintfz (num, sizeof(num), (bottom&0xff000000)?\"0x%06x\":\"%i\", bottom & 0xffffff);\n\tif (bottom == 0)\n\t\t*num = '\\0';\n\tif (Cmd_ExecLevel>RESTRICT_SERVER) //colour command came from server for a split client\n\t\tCbuf_AddText(va(\"p%i cmd setinfo bottomcolor \\\"%s\\\"\\n\", pnum+1, num), Cmd_ExecLevel);\n\telse if (server_owns_colour)\n\t\tCvar_LockFromServer(&bottomcolor, num);\n\telse\n\t\tCL_SetInfo (pnum, \"bottomcolor\", num);\n#ifdef NQPROT\n\tif (cls.protocol == CP_NETQUAKE)\n\t\tCmd_ForwardToServer();\n#endif\n}\n\nvoid CL_PakDownloads(int mode)\n{\n\t/*\n\tmode=0 no downloads (forced to 1 for pure)\n\tmode=1 archived names so local stuff is not poluted\n\tmode=2 downloaded packages will always be present. Use With Caution.\n\tmode&4 download even packages that are not referenced.\n\t*/\n\tchar local[256];\n\tchar *pname, *sep;\n\tchar *s = cl.serverpackhashes;\n\tint i;\n\n\tif (!cl.serverpakschanged || !mode)\n\t\treturn;\n\n\tCmd_TokenizeString(cl.serverpacknames, false, false);\n\tfor (i = 0; i < Cmd_Argc(); i++)\n\t{\n\t\ts = COM_Parse(s);\n\t\tpname = Cmd_Argv(i);\n\n\t\t//'*' prefix means 'referenced'. so if the server isn't using any files from it, don't bother downloading it.\n\t\tif (*pname == '*')\n\t\t\tpname++;\n\t\telse if (!(mode & 4))\n\t\t\tcontinue;\n\n\t\tsep = strchr(pname, '/');\n\t\tif (!sep || strchr(sep+1, '/'))\n\t\t\tcontinue;\t//don't try downloading weird ones here... paks inside paks is screwy stuff!\n\n\t\tif ((mode&3) != 2)\n\t\t{\n\t\t\t/*if we already have such a file, this is a no-op*/\n\t\t\tif (CL_CheckDLFile(va(\"package/%s\", pname)))\n\t\t\t\tcontinue;\n\t\t\tif (!FS_GenCachedPakName(pname, com_token, local, sizeof(local)))\n\t\t\t\tcontinue;\n\t\t}\n\t\telse\n\t\t\tQ_strncpyz(local, pname, sizeof(local));\n\t\tCL_CheckOrEnqueDownloadFile(pname, local, DLLF_ALLOWWEB|DLLF_NONGAME);\n\t}\n}\n\nvoid CL_CheckServerPacks(void)\n{\n\tstatic int oldpure;\n\tint pure = atof(InfoBuf_ValueForKey(&cl.serverinfo, \"sv_pure\"));\n\tif (pure < cl_pure.ival)\n\t\tpure = cl_pure.ival;\n\tpure = bound(0, pure, 2);\n\n\tif (!cl.serverpackhashes || cls.demoplayback)\n\t\tpure = 0;\n\n\tif (pure != oldpure || cl.serverpakschanged)\n\t{\n\t\tCL_PakDownloads((pure && !cl_download_packages.ival)?1:cl_download_packages.ival);\n\t\tFS_PureMode(NULL, pure, cl.serverpacknames, cl.serverpackhashes, NULL, NULL, cls.challenge);\n\n\t\tif (pure)\n\t\t{\n\t\t\t/*when enabling pure, kill cached models/sounds/etc*/\n\t\t\tCache_Flush();\n\t\t\t/*make sure cheating lamas can't use old shaders from a different srver*/\n\t\t\tShader_NeedReload(true);\n\t\t}\n\t}\n\toldpure = pure;\n\tcl.serverpakschanged = false;\n}\n\nvoid CL_CheckServerInfo(void)\n{\n\tchar *s;\n\tunsigned int allowed;\n#ifdef QUAKESTATS\n\tint oldstate;\n#endif\n#ifdef HAVE_SERVER\n\textern cvar_t sv_cheats;\n#endif\n\tint oldteamplay = cl.teamplay;\n\tqboolean spectating = true;\n\tint i;\n\tqboolean oldwatervis = cls.allow_watervis;\n\tint oldskyboxes = cls.allow_unmaskedskyboxes;\n\n\t//spectator 2 = spectator-with-scores, considered to be players. this means we don't want to allow spec cheats while they're inactive, because that would be weird.\n\tfor (i = 0; i < cl.splitclients; i++)\n\t\tif (cl.playerview[i].spectator != 1)\n\t\t\tspectating = false;\n\n\tcl.teamplay = atoi(InfoBuf_ValueForKey(&cl.serverinfo, \"teamplay\"));\n\tcls.deathmatch = cl.deathmatch = atoi(InfoBuf_ValueForKey(&cl.serverinfo, \"deathmatch\"));\n\n\tcls.allow_cheats = false;\n\tcls.allow_semicheats=true;\n\tcls.allow_unmaskedskyboxes=false;\n\tcls.allow_fbskins = 1;\n//\tcls.allow_fbskins = 0;\n//\tcls.allow_overbrightlight;\n\n\n\tcls.allow_csqc = atoi(InfoBuf_ValueForKey(&cl.serverinfo, \"anycsqc\")) || *InfoBuf_ValueForKey(&cl.serverinfo, \"*csprogs\");\n\tcl.csqcdebug = atoi(InfoBuf_ValueForKey(&cl.serverinfo, \"*csqcdebug\"));\n\n\ts = InfoBuf_ValueForKey(&cl.serverinfo, \"watervis\");\n\tif (spectating || cls.demoplayback || atoi(s) || (!*s && ruleset_allow_watervis.ival))\n\t\tcls.allow_watervis=true;\n\telse\n\t\tcls.allow_watervis=false;\n\n\ts = InfoBuf_ValueForKey(&cl.serverinfo, \"allow_skybox\");\n\tif (!*s)\n\t\ts = InfoBuf_ValueForKey(&cl.serverinfo, \"allow_skyboxes\");\n\tif (!*s)\n\t\tcls.allow_unmaskedskyboxes = (cl.worldmodel && cl.worldmodel->fromgame != fg_quake);\n\telse cls.allow_unmaskedskyboxes = !!atoi(s);\n\n\ts = InfoBuf_ValueForKey(&cl.serverinfo, \"fbskins\");\n\tif (*s)\n\t\tcls.allow_fbskins = atof(s);\n\telse if (cl.teamfortress)\n\t\tcls.allow_fbskins = 0;\n\telse\n\t\tcls.allow_fbskins = 1;\n\n\ts = InfoBuf_ValueForKey(&cl.serverinfo, \"*cheats\");\n\tif (spectating || cls.demoplayback || !stricmp(s, \"on\"))\n\t\tcls.allow_cheats = true;\n\n#ifdef HAVE_SERVER\n\t//allow cheats in single player regardless of sv_cheats.\n\t//(also directly read the sv_cheats cvar to avoid issues with nq protocols that don't support serverinfo.\n\tif (sv.state == ss_active && (sv.allocated_client_slots == 1 || sv_cheats.ival))\n\t\tcls.allow_cheats = true;\n#endif\n\n\ts = InfoBuf_ValueForKey(&cl.serverinfo, \"strict\");\n\tif ((!spectating && !cls.demoplayback && *s && strcmp(s, \"0\")) || !ruleset_allow_semicheats.ival)\n\t{\n\t\tcls.allow_semicheats = false;\n\t\tcls.allow_cheats\t= false;\n\t}\n\n\tcls.z_ext = atoi(InfoBuf_ValueForKey(&cl.serverinfo, \"*z_ext\")) & CLIENT_SUPPORTED_Z_EXTENSIONS;\n\n#ifdef NQPROT\n\tif (cls.protocol == CP_NETQUAKE && CPNQ_IS_DP)\n\t{\n\t\t//movevars come from stats.\n\t}\n\telse\n#endif\n\t{\n\t\tcls.maxfps = atof(InfoBuf_ValueForKey(&cl.serverinfo, \"maxfps\"));\n\t\tif (cls.maxfps < 20)\n\t\t\tcls.maxfps = 72;\n\n\t\t// movement vars for prediction\n\t\tcl.bunnyspeedcap = Q_atof(InfoBuf_ValueForKey(&cl.serverinfo, \"pm_bunnyspeedcap\"));\n\t\tmovevars.slidefix = (Q_atof(InfoBuf_ValueForKey(&cl.serverinfo, \"pm_slidefix\")) != 0);\n\t\tmovevars.slidyslopes = (Q_atof(InfoBuf_ValueForKey(&cl.serverinfo, \"pm_slidyslopes\")) != 0);\n\t\tmovevars.bunnyfriction = (Q_atof(InfoBuf_ValueForKey(&cl.serverinfo, \"pm_bunnyfriction\")) != 0);\n\t\tmovevars.airstep = (Q_atof(InfoBuf_ValueForKey(&cl.serverinfo, \"pm_airstep\")) != 0);\n\t\tmovevars.pground = (Q_atof(InfoBuf_ValueForKey(&cl.serverinfo, \"pm_pground\")) != 0);\n\t\tmovevars.stepdown = (Q_atof(InfoBuf_ValueForKey(&cl.serverinfo, \"pm_stepdown\")) != 0);\n\t\tmovevars.walljump = (Q_atof(InfoBuf_ValueForKey(&cl.serverinfo, \"pm_walljump\")));\n\t\tmovevars.ktjump = Q_atof(InfoBuf_ValueForKey(&cl.serverinfo, \"pm_ktjump\"));\n\t\tmovevars.autobunny = (Q_atof(InfoBuf_ValueForKey(&cl.serverinfo, \"pm_autobunny\")) != 0);\n\t\ts = InfoBuf_ValueForKey(&cl.serverinfo, \"pm_stepheight\");\n\t\tmovevars.stepheight = *s?Q_atof(s):PM_DEFAULTSTEPHEIGHT;\n\t\ts = InfoBuf_ValueForKey(&cl.serverinfo, \"pm_watersinkspeed\");\n\t\tmovevars.watersinkspeed = *s?Q_atof(s):60;\n\t\ts = InfoBuf_ValueForKey(&cl.serverinfo, \"pm_flyfriction\");\n\t\tmovevars.flyfriction = *s?Q_atof(s):4;\n\t\ts = InfoBuf_ValueForKey(&cl.serverinfo, \"pm_edgefriction\");\n\t\tmovevars.edgefriction = *s?Q_atof(s):2;\n\t\tif (!(movevars.flags&MOVEFLAG_VALID))\n\t\t\tmovevars.flags = (movevars.flags&~MOVEFLAG_QWEDGEBOX) | (*s?0:MOVEFLAG_QWEDGEBOX);\n\t}\n\tmovevars.coordtype = cls.netchan.netprim.coordtype;\n\n\t// Initialize cl.maxpitch & cl.minpitch\n\tif (cls.protocol == CP_QUAKEWORLD || cls.protocol == CP_NETQUAKE)\n\t{\n#ifdef NQPROT\n\t\ts = InfoBuf_ValueForKey(&cl.serverinfo, \"maxpitch\");\n\t\tcl.maxpitch = *s ? Q_atof(s) : ((cl_fullpitch_nq.ival && !cl.haveserverinfo)?90.0f:80.0f);\n\t\ts = InfoBuf_ValueForKey(&cl.serverinfo, \"minpitch\");\n\t\tcl.minpitch = *s ? Q_atof(s) : ((cl_fullpitch_nq.ival && !cl.haveserverinfo)?-90.0f:-70.0f);\n\n\t\tif (cls.protocol == CP_NETQUAKE)\n\t\t{\t//proquake likes spamming us with fixangles\n\t\t\t//should be about 0.5/65536, but there's some precision issues with such small numbers around 80, so we need to bias it more than we ought\n\t\t\tcl.maxpitch -= 1.0/2048;\n\t\t}\n#else\n\t\ts = InfoBuf_ValueForKey(&cl.serverinfo, \"maxpitch\");\n\t\tcl.maxpitch = *s ? Q_atof(s) : 80.0f;\n\t\ts = InfoBuf_ValueForKey(&cl.serverinfo, \"minpitch\");\n\t\tcl.minpitch = *s ? Q_atof(s) : -70.0f;\n#endif\n\t}\n\telse\n\t{\n\t\tcl.maxpitch = 90;\n\t\tcl.minpitch = -90;\n\t}\n\t//bound it, such that we never end up looking slightly more back than forwards\n\t//FIXME: we should probably tweak our movement code instead.\n\tcl.maxpitch = bound(-89.9, cl.maxpitch, 89.9);\n\tcl.minpitch = bound(-89.9, cl.minpitch, 89.9);\n\tcl.disablemouse = atoi(InfoBuf_ValueForKey(&cl.serverinfo, \"nomouse\"));\n\n\tcl.hexen2pickups = atoi(InfoBuf_ValueForKey(&cl.serverinfo, \"sv_pupglow\"));\n\n\tallowed = atoi(InfoBuf_ValueForKey(&cl.serverinfo, \"allow\"));\n\tif (allowed & 1)\n\t\tcls.allow_watervis = true;\n//\tif (allowed & 2)\n//\t\tcls.allow_rearview = true;\n\tif (allowed & 4)\n\t\tcls.allow_unmaskedskyboxes = true;\n//\tif (allowed & 8)\n//\t\tcls.allow_mirrors = true;\n\t//16\n\t//32\n//\tif (allowed & 128)\n//\t\tcls.allow_postproc = true;\n//\tif (allowed & 256)\n//\t\tcls.allow_lightmapgamma = true;\n\tif (allowed & 512)\n\t\tcls.allow_cheats = true;\n\n\tif (cls.allow_semicheats)\n\t\tcls.allow_anyparticles = true;\n\telse\n\t\tcls.allow_anyparticles = false;\n\n\n\tif (spectating || cls.demoplayback)\n\t\tcl.fpd = 0;\n\telse\n\t\tcl.fpd = atoi(InfoBuf_ValueForKey(&cl.serverinfo, \"fpd\"));\n\n\tcl.gamespeed = atof(InfoBuf_ValueForKey(&cl.serverinfo, \"*gamespeed\"))/100.f;\n\tif (cl.gamespeed < 0.1)\n\t\tcl.gamespeed = 1;\n\n#ifdef QUAKESTATS\n\ts = InfoBuf_ValueForKey(&cl.serverinfo, \"status\");\n\toldstate = cl.matchstate;\n\tif (!stricmp(s, \"standby\"))\n\t\tcl.matchstate = MATCH_STANDBY;\n\telse if (!stricmp(s, \"countdown\"))\n\t\tcl.matchstate = MATCH_COUNTDOWN;\n\telse\n\t{\n\t\tfloat time = strtod(s, &s);\n\t\tif (!strcmp(s, \" min left\") || !strcmp(s, \" mins left\"))\n\t\t\ttime *= 60;\n\t\telse if (!strcmp(s, \" sec left\") || !strcmp(s, \" secs left\"))\n\t\t\ttime *= 1;\n\t\telse if (!strcmp(s, \" hour left\") || !strcmp(s, \" hours left\"))\n\t\t\ttime *= 60*60;\n\t\telse\n\t\t\ttime = -1;\n\n\t\tif (time >= 0)\n\t\t{\n\t\t\t//always update it. this is to try to cope with overtime.\n\t\t\toldstate = cl.matchstate = MATCH_INPROGRESS;\n\t\t\tcl.matchgametimestart = cl.gametime + time - 60*atof(InfoBuf_ValueForKey(&cl.serverinfo, \"timelimit\"));\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (*s && cl.matchstate == MATCH_INPROGRESS)\n\t\t\t\tCon_DPrintf(\"Match state changed to unknown meaning: %s\\n\", s);\n\t\t\telse\n\t\t\t\tcl.matchstate = MATCH_DONTKNOW;\t//don't revert from inprogress to don't know\n\t\t}\n\t}\n\tif (oldstate != cl.matchstate)\n\t\tcl.matchgametimestart = cl.gametime;\n#endif\n\n\tCL_CheckServerPacks();\n\n\tCvar_ForceCheatVars(cls.allow_semicheats, cls.allow_cheats);\n\n\tif (oldteamplay != cl.teamplay)\n\t\tSkin_FlushPlayers();\n\tif (oldwatervis != cls.allow_watervis || oldskyboxes != cls.allow_unmaskedskyboxes)\n\t\tShader_NeedReload(false);\n\n\tCSQC_ServerInfoChanged();\n}\n\n/*\n==================\nCL_FullInfo_f\n\nAllow clients to change userinfo\n==================\n*/\nvoid CL_FullInfo_f (void)\n{\n\tchar\tkey[512];\n\tchar\tvalue[512];\n\tchar\t*o;\n\tchar\t*s;\n\tint pnum = CL_TargettedSplit(true);\n\n\tif (Cmd_Argc() != 2)\n\t{\n\t\tCon_TPrintf (\"fullinfo <complete info string>\\n\");\n\t\treturn;\n\t}\n\n\ts = Cmd_Argv(1);\n\tif (*s == '\\\\')\n\t\ts++;\n\twhile (*s)\n\t{\n\t\to = key;\n\t\twhile (*s && *s != '\\\\' && o < key + sizeof(key))\n\t\t\t*o++ = *s++;\n\t\tif (o == key + sizeof(key))\n\t\t{\n\t\t\tCon_Printf (\"key length too long\\n\");\n\t\t\treturn;\n\t\t}\n\t\t*o = 0;\n\n\t\tif (!*s)\n\t\t{\n\t\t\tCon_Printf (\"key %s has no value\\n\", key);\n\t\t\treturn;\n\t\t}\n\n\t\to = value;\n\t\ts++;\n\t\twhile (*s && *s != '\\\\' && o < value + sizeof(value))\n\t\t\t*o++ = *s++;\n\t\tif (o == value + sizeof(value))\n\t\t{\n\t\t\tCon_Printf (\"value length too long\\n\");\n\t\t\treturn;\n\t\t}\n\t\t*o = 0;\n\n\t\tif (*s)\n\t\t\ts++;\n\n\t\tif (!stricmp(key, pmodel_name) || !stricmp(key, emodel_name))\n\t\t\tcontinue;\n\n\t\tInfoBuf_SetKey (&cls.userinfo[pnum], key, value);\n\t}\n}\n\nvoid CL_SetInfoBlob (int pnum, const char *key, const char *value, size_t valuesize)\n{\n\tcvar_t *var;\n\tif (!pnum)\n\t{\n\t\tvar = Cvar_FindVar(key);\n\t\tif (var && (var->flags & CVAR_USERINFO))\n\t\t{\t//get the cvar code to set it. the server might have locked it.\n\t\t\tCvar_Set(var, value);\n\t\t\treturn;\n\t\t}\n\t}\n\telse if (pnum < 0 || pnum >= MAX_SPLITS)\n\t\treturn;\n\n\tInfoBuf_SetStarBlobKey(&cls.userinfo[pnum], key, value, valuesize);\n}\nvoid CL_SetInfo (int pnum, const char *key, const char *value)\n{\n\tCL_SetInfoBlob(pnum, key, value, strlen(value));\n}\n/*\n==================\nCL_SetInfo_f\n\nAllow clients to change userinfo\n==================\n*/\nvoid CL_SetInfo_f (void)\n{\n\tchar *key, *val;\n\tsize_t keysize, valsize;\n\tcvar_t *var;\n\tint pnum = CL_TargettedSplit(true);\n\tif (Cmd_Argc() == 1)\n\t{\n\t\tInfoBuf_Print (&cls.userinfo[pnum], \"\");\n\t\tCon_Printf(\"[%u]\\n\", (unsigned int)cls.userinfo[pnum].totalsize);\n\t\treturn;\n\t}\n\tif (Cmd_Argc() != 3)\n\t{\n\t\tCon_TPrintf (\"usage: setinfo [ <key> <value> ]\\n\");\n\t\treturn;\n\t}\n\tif (!stricmp(Cmd_Argv(1), pmodel_name) || !strcmp(Cmd_Argv(1), emodel_name))\n\t\treturn;\n\n\tif (Cmd_Argv(1)[0] == '*')\n\t{\n\t\tint i;\n\t\tif (!strcmp(Cmd_Argv(1), \"*\"))\n\t\t\tif (!strcmp(Cmd_Argv(2), \"\"))\n\t\t\t{\t//clear it out\n\t\t\t\tconst char *k;\n\t\t\t\tfor(i=0;;)\n\t\t\t\t{\n\t\t\t\t\tk = InfoBuf_KeyForNumber(&cls.userinfo[pnum], i);\n\t\t\t\t\tif (!k)\n\t\t\t\t\t\tbreak;\t//no more.\n\t\t\t\t\telse if (*k == '*')\n\t\t\t\t\t\ti++;\t//can't remove * keys\n\t\t\t\t\telse if ((var = Cvar_FindVar(k)) && (var->flags&CVAR_USERINFO))\n\t\t\t\t\t\ti++;\t//this one is a cvar.\n\t\t\t\t\telse\n\t\t\t\t\t\tInfoBuf_RemoveKey(&cls.userinfo[pnum], k);\t//we can remove this one though, so yay.\n\t\t\t\t}\n\n\t\t\t\treturn;\n\t\t\t}\n\t\tCon_Printf (\"Can't set * keys\\n\");\n\t\treturn;\n\t}\n\n\tkey = Cmd_Argv(1);\n\tval = Cmd_Argv(2);\n\n\tkey = InfoBuf_DecodeString(key, key+strlen(key), &keysize);\n\tval = InfoBuf_DecodeString(val, val+strlen(val), &valsize);\n\tif (keysize != strlen(key))\n\t\tCon_Printf (\"setinfo: ignoring key name with embedded null\\n\");\n\telse\n\t\tCL_SetInfoBlob(pnum, key, val, valsize);\n\tZ_Free(key);\n\tZ_Free(val);\n}\n\n#if 1//def _DEBUG\nvoid CL_SetInfoBlob_f (void)\n{\n\tqofs_t fsize;\n\tvoid *data;\n\tint pnum = CL_TargettedSplit(true);\n\tif (Cmd_Argc() == 1)\n\t{\n\t\tInfoBuf_Print (&cls.userinfo[pnum], \"\");\n\t\treturn;\n\t}\n\tif (Cmd_Argc() != 3)\n\t{\n\t\tCon_TPrintf (\"usage: setinfo [ <key> <filename> ]\\n\");\n\t\treturn;\n\t}\n\n\t//user isn't allowed to set pmodel, emodel, *foo as these could break stuff.\n\tif (!stricmp(Cmd_Argv(1), pmodel_name) || !strcmp(Cmd_Argv(1), emodel_name))\n\t\treturn;\n\tif (Cmd_Argv(1)[0] == '*')\n\t{\n\t\tCon_Printf (\"Can't set * keys\\n\");\n\t\treturn;\n\t}\n\n\tdata = FS_MallocFile(Cmd_Argv(2), FS_GAME, &fsize);\n\tif (!data)\n\t{\n\t\tCon_Printf (\"Unable to read %s\\n\", Cmd_Argv(2));\n\t\treturn;\n\t}\n\tif (fsize > 64*1024*1024)\n\t\tCon_Printf (\"File is over 64mb\\n\");\n\telse\n\t\tCL_SetInfoBlob(pnum, Cmd_Argv(1), data, fsize);\n\tFS_FreeFile(data);\n}\n#endif\n\nvoid CL_SaveInfo(vfsfile_t *f)\n{\n\tint i;\n\tfor (i = 0; i < MAX_SPLITS; i++)\n\t{\n\t\tVFS_WRITE(f, \"\\n\", 1);\n\t\tif (i)\n\t\t{\n\t\t\tVFS_WRITE(f, va(\"p%i setinfo * \\\"\\\"\\n\", i+1), 16);\n\t\t\tInfoBuf_WriteToFile(f, &cls.userinfo[i],  va(\"p%i setinfo\", i+1), 0);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tVFS_WRITE(f, \"setinfo * \\\"\\\"\\n\", 13);\n\t\t\tInfoBuf_WriteToFile(f, &cls.userinfo[i], \"setinfo\", CVAR_USERINFO);\n\t\t}\n\t}\n}\n\n/*\n====================\nCL_Packet_f\n\npacket <destination> <contents>\n\nContents allows \\n escape character\n====================\n*/\nvoid CL_Packet_f (void)\n{\n#ifdef FTE_TARGET_WEB\n\t//either this creates some expensive alternative rtc connection that screws us over, or just generally fails. don't allow it.\n\tCon_Printf (CON_WARNING \"Ignoring 'packet %s' request.\\n\", Cmd_Argv(1));\n#else\n\tchar\tsend[2048];\n\tint\t\ti, l;\n\tchar\t*in, *out;\n\tnetadr_t\tadr;\n\tstruct dtlspeercred_s cred = {Cmd_Argv(1)};\n\n\tif (Cmd_Argc() != 3)\n\t{\n\t\tCon_TPrintf (\"usage: packet <destination> <contents>\\n\");\n\t\treturn;\n\t}\n\n\tif (!NET_StringToAdr (Cmd_Argv(1), PORT_DEFAULTSERVER, &adr))\n\t{\n\t\tCon_Printf (\"Bad address: %s\\n\", Cmd_Argv(1));\n\t\treturn;\n\t}\n\n\n\tif (Cmd_FromGamecode())\t//some mvdsv servers stuffcmd a packet command which lets them know which ip the client is from.\n\t{\t\t\t\t\t\t//unfortunatly, 50% of servers are badly configured resulting in them poking local services that THEY MUST NOT HAVE ACCESS TO.\n\t\tconst char *addrdesc;\n\t\tconst char *realdesc;\n\t\tif (cls.demoplayback)\n\t\t{\n\t\t\tCon_DPrintf (\"Not sending realip packet from demo\\n\");\n\t\t\treturn;\n\t\t}\n\n\t\tif (!NET_CompareAdr(&adr, &cls.netchan.remote_address))\n\t\t{\n\t\t\tif (NET_ClassifyAddress(&adr, &addrdesc) < ASCOPE_LAN)\n\t\t\t{\n\t\t\t\tif (NET_ClassifyAddress(&cls.netchan.remote_address, &realdesc) < ASCOPE_LAN)\n\t\t\t\t{\t//this isn't necessarily buggy... but its still a potential exploit so we need to block it regardless.\n\t\t\t\t\tCon_Printf (CON_WARNING \"Ignoring buggy %s realip request for %s server.\\n\", addrdesc, realdesc);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tadr = cls.netchan.remote_address;\n\t\t\t\t\tCon_Printf (CON_WARNING \"Ignoring buggy %s realip request, sending to %s server instead.\\n\", addrdesc, realdesc);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tcls.realserverip = adr;\n\t\tCon_DPrintf (\"Sending realip packet\\n\");\n\t}\n\telse if (!ruleset_allow_packet.ival)\n\t{\n\t\tCon_Printf(\"Sorry, the %s command is disallowed\\n\", Cmd_Argv(0));\n\t\treturn;\n\t}\n\tcls.lastarbiatarypackettime = Sys_DoubleTime();\t//prevent the packet command from causing a reconnect on badly configured mvdsv servers.\n\n\tin = Cmd_Argv(2);\n\tout = send+4;\n\tsend[0] = send[1] = send[2] = send[3] = 0xff;\n\n\tl = strlen (in);\n\tfor (i=0 ; i<l ; i++)\n\t{\n\t\tif (in[i] == '\\\\' && in[i+1] == 'n')\n\t\t{\n\t\t\t*out++ = '\\n';\n\t\t\ti++;\n\t\t}\n\t\telse if (in[i] == '\\\\' && in[i+1] == '\\\\')\n\t\t{\n\t\t\t*out++ = '\\\\';\n\t\t\ti++;\n\t\t}\n\t\telse if (in[i] == '\\\\' && in[i+1] == 'r')\n\t\t{\n\t\t\t*out++ = '\\r';\n\t\t\ti++;\n\t\t}\n\t\telse if (in[i] == '\\\\' && in[i+1] == '\\\"')\n\t\t{\n\t\t\t*out++ = '\\\"';\n\t\t\ti++;\n\t\t}\n\t\telse if (in[i] == '\\\\' && in[i+1] == '0')\n\t\t{\n\t\t\t*out++ = '\\0';\n\t\t\ti++;\n\t\t}\n\t\telse\n\t\t\t*out++ = in[i];\n\t}\n\t*out = 0;\n\n\tif (!cls.sockets)\n\t\tNET_InitClient(false);\n\tif (!NET_EnsureRoute(cls.sockets, \"packet\", &cred, Cmd_Argv(1), &adr, true))\n\t\treturn;\n\tNET_SendPacket (cls.sockets, out-send, send, &adr);\n\n\tif (Cmd_FromGamecode())\n\t{\n\t\t//realip\n\t\tchar *temp = Z_Malloc(strlen(in)+1);\n\t\tstrcpy(temp, in);\n\t\tCmd_TokenizeString(temp, false, false);\n\t\tcls.realip_ident = atoi(Cmd_Argv(2));\n\t\tZ_Free(temp);\n\t}\n#endif\n}\n\n\n/*\n=====================\nCL_NextDemo\n\nCalled to play the next demo in the demo loop\n=====================\n*/\nvoid CL_NextDemo (void)\n{\n\tcvar_t *cl_autodemos;\n\tchar\tstr[1024];\n\n\tif (cls.demonum < 0)\n\t\treturn;\t\t// don't play demos\n\n\tcl_autodemos = Cvar_FindVar(\"cl_autodemos\");\n\tif (cl_autodemos && *cl_autodemos->string)\n\t{\n\t\tCmd_TokenizeString(cl_autodemos->string, false, false);\n\t\tif (!Cmd_Argc())\n\t\t{\t//none...\n\t\t\tcls.demonum = -1;\n\t\t\treturn;\n\t\t}\n\t\tif (cls.demonum >= Cmd_Argc())\n\t\t\tcls.demonum = 0;\t//restart the loop\n\n\t\tif (!strcmp(Cmd_Argv(cls.demonum), \"quit\"))\n\t\t\tQ_snprintfz (str, sizeof(str), \"quit\\n\");\n\t\telse\n\t\t\tQ_snprintfz (str, sizeof(str), \"playdemo \\\"demos/%s\\\"\\n\", Cmd_Argv(cls.demonum));\n\t}\n\telse\n\t{\n\t\tif (!cls.demos[cls.demonum][0] || cls.demonum >= MAX_DEMOS)\n\t\t{\n\t\t\tcls.demonum = 0;\n\t\t\tif (!cls.demos[cls.demonum][0])\n\t\t\t{\n\t//\t\t\tCon_Printf (\"No demos listed with startdemos\\n\");\n\t\t\t\tcls.demonum = -1;\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\tif (!strcmp(cls.demos[cls.demonum], \"quit\"))\n\t\t\tQ_snprintfz (str, sizeof(str), \"quit\\n\");\n\t\telse\n\t\t\tQ_snprintfz (str, sizeof(str), \"playdemo %s\\n\", cls.demos[cls.demonum]);\n\t}\n\tCbuf_InsertText (str, RESTRICT_LOCAL, false);\n\tcls.demonum++;\n\n\tif (!cls.state)\n\t\tcls.state = ca_demostart;\n}\n\n/*\n===============================================================================\n\nDEMO LOOP CONTROL\n\n===============================================================================\n*/\n\n\n/*\n==================\nCL_Startdemos_f\n==================\n*/\nvoid CL_Startdemos_f (void)\n{\n\tint\t\ti, c;\n\n\tc = Cmd_Argc() - 1;\n\tif (c > MAX_DEMOS)\n\t{\n\t\tCon_Printf (\"Max %i demos in demoloop\\n\", MAX_DEMOS);\n\t\tc = MAX_DEMOS;\n\t}\n\tCon_DPrintf (\"%i demo(s) in loop\\n\", c);\n\n\tfor (i=1 ; i<c+1 ; i++)\n\t\tQ_strncpyz (cls.demos[i-1], Cmd_Argv(i), sizeof(cls.demos[0]));\n\tfor ( ; i<MAX_DEMOS ; i++)\n\t\tQ_strncpyz (cls.demos[i-1], \"\", sizeof(cls.demos[0]));\n\n\tcls.demonum = -1;\n\t//don't start it here - we might have been given a +connect or whatever argument.\n}\n\n\n/*\n==================\nCL_Demos_f\n\nReturn to looping demos\n==================\n*/\nvoid CL_Demos_f (void)\n{\n\tconst char *mode = Cmd_Argv(1);\n\tif (!strcmp(mode, \"idle\"))\n\t{\t//'demos idle' only plays the demos when we're idle.\n\t\t//this can be used for menu backgrounds (engine and menuqc).\n\t\tif (cls.state || cls.demoplayback || CL_TryingToConnect())\n\t\t\treturn;\n\t}\n\t//else disconnects can switch gamedirs/paks and kill menuqc etc.\n\n\tif (cls.demonum == -1)\n\t\tcls.demonum = 1;\n\tCL_Disconnect_f ();\n\tCL_NextDemo ();\n}\n\n/*\n==================\nCL_Stopdemo_f\n\nstop demo\n==================\n*/\nvoid CL_Stopdemo_f (void)\n{\n\tif (cls.demoplayback == DPB_NONE)\n\t\treturn;\n\tCL_StopPlayback ();\n\tCL_Disconnect (NULL);\n}\n\n\n\n/*\n=================\nCL_Changing_f\n\nJust sent as a hint to the client that they should\ndrop to full console\n=================\n*/\nvoid CL_Changing_f (void)\n{\n\tchar *mapname = Cmd_Argv(1);\n\tif (cls.download && cls.download->method <= DL_QWPENDING)  // don't change when downloading\n\t\treturn;\n\n\tcls.demoseeking = DEMOSEEK_NOT;\t//don't seek over it\n\n\tif (*mapname)\n\t\tSCR_ImageName(mapname);\n\telse\n\t\tSCR_BeginLoadingPlaque();\n\n\tS_StopAllSounds (true);\n\tcl.intermissionmode = IM_NONE;\n\tif (cls.state)\n\t{\n\t\tcls.state = ca_connected;\t// not active anymore, but not disconnected\n\t\tCon_TPrintf (\"\\nChanging map...\\n\");\n\t}\n\telse\n\t\tCon_Printf(\"Changing while not connected\\n\");\n\n#ifdef NQPROT\n\tcls.signon=0;\n#endif\n}\n\n\n/*\n=================\nCL_Reconnect_f\n\nUser command, or NQ protocol command (messy).\n=================\n*/\nvoid CL_Reconnect_f (void)\n{\n\tif (cls.download && cls.download->method <= DL_QWPENDING)  // don't change when downloading\n\t\treturn;\n#ifdef NQPROT\n\tif (cls.protocol == CP_NETQUAKE && Cmd_IsInsecure())\n\t{\n\t\tCL_Changing_f();\n\t\treturn;\n\t}\n#endif\n\tS_StopAllSounds (true);\n\n\tif (cls.state == ca_connected)\n\t{\n\t\tCon_TPrintf (\"reconnecting...\\n\");\n\t\tCL_SendClientCommand(true, \"new\");\n\t\treturn;\n\t}\n\n\tif (!*cls.servername)\n\t{\n\t\tCon_TPrintf (\"No server to reconnect to...\\n\");\n\t\treturn;\n\t}\n\n#if defined(HAVE_SERVER) && defined(SUBSERVERS)\n\tif (sv.state == ss_clustermode)\n\t{\t//reconnecting while we're a cluster... o.O\n\t\tchar oldguid[sizeof(connectinfo.guid)];\n\t\tQ_strncpyz(oldguid, connectinfo.guid, sizeof(oldguid));\n\t\tmemset(&connectinfo, 0, sizeof(connectinfo));\n\t\tconnectinfo.istransfer = false;\n\t\tQ_strncpyz(connectinfo.guid, oldguid, sizeof(oldguid));\t//retain the same guid on transfers\n\n\t\tCvar_Set(&cl_disconnectreason, \"Transferring....\");\n\t\tconnectinfo.trying = true;\n\t\tconnectinfo.defaultport = cl_defaultport.value;\n\t\tconnectinfo.protocol = CP_UNKNOWN;\n\t\tSCR_SetLoadingStage(LS_CONNECTION);\n\t\tCL_CheckForResend();\n\t\treturn;\n\t}\n#endif\n\n\tCL_Disconnect(NULL);\n\tCL_BeginServerReconnect();\n}\n\nstatic void CL_ConnectionlessPacket_Connection(char *tokens)\n{\n\tunsigned int ncflags;\n\tint qportsize = -1;\n\tif (net_from.type == NA_INVALID)\n\t\treturn;\t//I've found a qizmo demo that contains one of these. its best left ignored.\n\n\tif (!CL_IsPendingServerAddress(&net_from))\n\t{\n\t\tif (net_from.type != NA_LOOPBACK)\n\t\t\tCon_TPrintf (\"ignoring connection\\n\");\n\t\treturn;\n\t}\n\n\tif (cls.state >= ca_connected)\n\t{\n\t\tif (!NET_CompareAdr(&cls.netchan.remote_address, &net_from))\n\t\t{\n#ifdef HAVE_SERVER\n\t\t\tif (sv.state != ss_clustermode)\n#endif\n\t\t\t\tCL_Disconnect (NULL);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (cls.demoplayback == DPB_NONE)\n\t\t\t\tCon_TPrintf (\"Dup connect received.  Ignored.\\n\");\n\t\t\treturn;\n\t\t}\n\t}\n\tif (net_from.type != NA_LOOPBACK)\n\t{\n//\t\t\tCon_TPrintf (S_COLOR_GRAY\"connection\\n\");\n\n#ifdef HAVE_SERVER\n\t\tif (sv.state && sv.state != ss_clustermode)\n\t\t\tSV_UnspawnServer();\n#endif\n\t}\n\n#if defined(Q2CLIENT)\n\tif (tokens && connectinfo.protocol == CP_QUAKE2)\n\t{\n\t\tif (cls.protocol_q2 == PROTOCOL_VERSION_R1Q2 || cls.protocol_q2 == PROTOCOL_VERSION_Q2PRO)\n\t\t\tqportsize = 1;\n\t\ttokens = COM_Parse(tokens);\t//skip the client_connect bit\n\t\twhile((tokens = COM_Parse(tokens)))\n\t\t{\n\t\t\tif (!strncmp(com_token, \"ac=\", 3))\n\t\t\t{\n\t\t\t\tif (atoi(com_token+3))\n\t\t\t\t{\n\t\t\t\t\tCL_ConnectAbort(\"Server requires anticheat support\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (!strncmp(com_token, \"nc=\", 3))\n\t\t\t\tqportsize = atoi(com_token+3)?1:2;\n\t\t\telse if (!strncmp(com_token, \"map=\", 4))\n\t\t\t\tSCR_ImageName(com_token+4);\n\t\t\telse if (!strncmp(com_token, \"dlserver=\", 9))\n\t\t\t\tQ_strncpyz(cls.downloadurl, com_token+9, sizeof(cls.downloadurl));\n\t\t\telse if (!strcmp(com_token, STRINGIFY(PROTOCOL_VERSION_Q2EX)))\n\t\t\t\tconnectinfo.subprotocol = atoi(com_token);\n\t\t\telse\n\t\t\t\tCon_DPrintf(\"client_connect: Unknown token \\\"%s\\\"\\n\", com_token);\n\t\t}\n\t}\n#endif\n\n\tconnectinfo.trying = false;\n\tcl.splitclients = 0;\n\tcls.protocol = connectinfo.protocol;\n\tcls.proquake_angles_hack = false;\n\tcls.fteprotocolextensions = connectinfo.ext.fte1;\n\tcls.fteprotocolextensions2 = connectinfo.ext.fte2;\n\tcls.ezprotocolextensions1 = connectinfo.ext.ez1;\n\tcls.challenge = connectinfo.challenge;\n\tncflags = NCF_CLIENT;\n\tif (connectinfo.ext.mtu)\n\t\tncflags |= NCF_FRAGABLE;\n\tif (connectinfo.ext.fte2&PEXT2_STUNAWARE)\n\t\tncflags |= NCF_STUNAWARE;\n\tNetchan_Setup (ncflags, &cls.netchan, &net_from, connectinfo.qport, connectinfo.ext.mtu);\n\tcls.protocol_q2 = (cls.protocol == CP_QUAKE2)?connectinfo.subprotocol:0;\n\tif (qportsize>=0)\n\t\tcls.netchan.qportsize = qportsize;\n\n#ifdef HUFFNETWORK\n\tcls.netchan.compresstable = Huff_CompressionCRC(connectinfo.ext.compresscrc);\n#else\n\tcls.netchan.compresstable = NULL;\n#endif\n\tCL_ParseEstablished();\n#ifdef Q3CLIENT\n\tif (cls.protocol == CP_QUAKE3)\n\t\tq3->cl.Established();\n\telse\n#endif\n\t\tCL_SendClientCommand(true, \"new\");\n\tcls.state = ca_connected;\n#ifdef QUAKESPYAPI\n\tallowremotecmd = false; // localid required now for remote cmds\n#endif\n\n\ttotal_loading_size = 100;\n\tcurrent_loading_size = 0;\n\tSCR_SetLoadingStage(LS_CLIENT);\n\n\tValidation_Apply_Ruleset();\n\n\tCL_WriteSetDemoMessage();\n}\n\n/*\n=================\nCL_ConnectionlessPacket\n\nResponses to broadcasts, etc\n=================\n*/\nvoid CL_ConnectionlessPacket (void)\n{\n\tchar\t*s;\n\tint\t\tc;\n\tchar\tadr[MAX_ADR_SIZE];\n\n\tMSG_BeginReading (&net_message, msg_nullnetprim);\n\tMSG_ReadLong ();        // skip the -1\n\n\tCmd_TokenizeString(net_message.data+4, false, false);\n\n\tif (net_message.cursize == sizeof(net_message_buffer))\n\t\tnet_message.data[sizeof(net_message_buffer)-1] = '\\0';\n\telse\n\t\tnet_message.data[net_message.cursize] = '\\0';\n\n#ifdef PLUGINS\n\tif (Plug_ConnectionlessClientPacket(net_message.data+4, net_message.cursize-4))\n\t\treturn;\n#endif\n\n\tc = MSG_ReadByte ();\n\n\t// ping from somewhere\n\tif (c == A2A_PING)\n\t{\n\t\tchar\tdata[256];\n\t\tint len;\n\n\t\tif (cls.realserverip.type == NA_INVALID)\n\t\t\treturn;\t//not done a realip yet\n\n\t\tif (NET_CompareBaseAdr(&cls.realserverip, &net_from) == false)\n\t\t\treturn;\t//only reply if it came from the real server's ip.\n\n\t\tdata[0] = 0xff;\n\t\tdata[1] = 0xff;\n\t\tdata[2] = 0xff;\n\t\tdata[3] = 0xff;\n\t\tdata[4] = A2A_ACK;\n\t\tdata[5] = 0;\n\n\t\t//ack needs two parameters to work with realip properly.\n\t\t//firstly it needs an auth message, so it can't be spoofed.\n\t\t//secondly, it needs a copy of the realip ident, so you can't report a different player's client (you would need access to their ip).\n\t\tdata[5] = ' ';\n\t\tQ_snprintfz(data+6, sizeof(data)-6, \"%i %i\", atoi(MSG_ReadString()), cls.realip_ident);\n\t\tlen = strlen(data);\n\n\t\tNET_SendPacket (cls.sockets, len, &data, &net_from);\n\t\treturn;\n\t}\n\n\tif (c == A2C_PRINT)\n\t{\n\t\tif (!strncmp(net_message.data+MSG_GetReadCount(), \"\\\\chunk\", 6))\n\t\t{\n\t\t\tif (NET_CompareBaseAdr(&cls.netchan.remote_address, &net_from) == false)\n\t\t\t\tif (cls.realserverip.type == NA_INVALID || NET_CompareBaseAdr(&cls.realserverip, &net_from) == false)\n\t\t\t\t\treturn;\t//only use it if it came from the real server's ip (this breaks on proxies).\n\n\t\t\tMSG_ReadLong();\n\t\t\tMSG_ReadChar();\n\t\t\tMSG_ReadChar();\n\n\t\t\tif (CL_ParseOOBDownload())\n\t\t\t{\n\t\t\t\tif (MSG_GetReadCount() != net_message.cursize)\n\t\t\t\t{\n\t\t\t\t\tCon_Printf (\"junk on the end of the packet\\n\");\n\t\t\t\t\tCL_Disconnect_f();\n\t\t\t\t}\n\t\t\t\tcls.netchan.last_received = realtime;\t//in case there's some virus scanner running on the server making it stall... for instance...\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t}\n\n\tif (cls.demoplayback == DPB_NONE && net_from.type != NA_LOOPBACK)\n\t\tCon_Printf (S_COLOR_GRAY\"%s: \", NET_AdrToString (adr, sizeof(adr), &net_from));\n//\tCon_DPrintf (\"%s\", net_message.data + 4);\n\n\tif (c == 'f')\t//using 'f' as a prefix so that I don't need lots of hacks\n\t{\n\t\ts = MSG_ReadStringLine ();\n\t\tif (!strcmp(s, \"redir\"))\n\t\t{\n\t\t\tnetadr_t adr;\n\t\t\tchar *data = MSG_ReadStringLine();\n\t\t\tCon_TPrintf (S_COLOR_GRAY\"redirect to %s\\n\", data);\n\t\t\tif (NET_StringToAdr(data, PORT_DEFAULTSERVER, &adr))\n\t\t\t{\n\t\t\t\tif (CL_IsPendingServerAddress(&net_from))\n\t\t\t\t{\n\t\t\t\t\tstruct dtlspeercred_s cred = {cls.servername}; //FIXME\n\t\t\t\t\tif (!NET_EnsureRoute(cls.sockets, \"redir\", &cred, data, &adr, true))\n\t\t\t\t\t\tCon_Printf (CON_ERROR\"Unable to redirect to %s\\n\", data);\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tconnectinfo.istransfer = true;\n\t\t\t\t\t\tconnectinfo.numadr = 1;\n\t\t\t\t\t\tconnectinfo.adr[0] = adr;\n\n\t\t\t\t\t\tdata = \"\\xff\\xff\\xff\\xffgetchallenge\\n\";\n\t\t\t\t\t\tNET_SendPacket (cls.sockets, strlen(data), data, &adr);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\telse if (!strcmp(s, \"reject\"))\n\t\t{\t//generic rejection. stop trying.\n\t\t\tchar *data = MSG_ReadStringLine();\n\t\t\tCon_Printf (\"reject\\n\");\n\t\t\tif (CL_IsPendingServerAddress(&net_from))\n\t\t\t\tCL_ConnectAbort(\"%s\\n\", data);\n\t\t\treturn;\n\t\t}\n\t\telse if (!strcmp(s, \"badname\"))\n\t\t{\t//rejected purely because of player name\n\t\t\tif (CL_IsPendingServerAddress(&net_from))\n\t\t\t\tCL_ConnectAbort(\"bad player name\\n\");\n\t\t}\n\t\telse if (!strcmp(s, \"badaccount\"))\n\t\t{\t//rejected because username or password is wrong\n\t\t\tif (CL_IsPendingServerAddress(&net_from))\n\t\t\t\tCL_ConnectAbort(\"invalid username or password\\n\");\n\t\t}\n\t\t\n\t\tCon_Printf (\"f%s\\n\", s);\n\t\treturn;\n\t}\n\n\tif (c == S2C_CHALLENGE)\n\t{\n\t\tstatic unsigned int lasttime = 0xdeadbeef;\n\t\tstatic netadr_t lastadr;\n\t\tunsigned int curtime = Sys_Milliseconds();\n#ifdef HAVE_DTLS\n\t\tint candtls = 0;\t//0=no,1=optional,2=mandatory\n#endif\n\n\t\ts = MSG_ReadString ();\n\t\tCOM_Parse(s);\n\n#ifdef Q3CLIENT\n\t\tif (!strcmp(com_token, \"onnectResponse\"))\n\t\t{\n\t\t\tconnectinfo.protocol = CP_QUAKE3;\n\t\t\tCL_ConnectionlessPacket_Connection(s);\n\t\t\treturn;\n\t\t}\n#endif\n#ifdef Q2CLIENT\n\t\tif (!strcmp(com_token, \"lient_connect\"))\n\t\t{\n\t\t\tconnectinfo.protocol = CP_QUAKE2;\n\t\t\tCL_ConnectionlessPacket_Connection(s);\n\t\t\treturn;\n\t\t}\n#endif\n\n\t\tCon_TPrintf (S_COLOR_GRAY\"challenge\\n\");\n\n\t\tif (!CL_IsPendingServerAddress(&net_from))\n\t\t{\n\t\t\tif (net_from.prot != NP_RTC_TCP && net_from.prot != NP_RTC_TLS)\n\t\t\t\tCon_Printf(CON_WARNING\"Challenge from wrong server, ignoring\\n\");\n\t\t\treturn;\n\t\t}\n\t\tconnectinfo.numadr = 1;\n\t\tconnectinfo.adr[0] = net_from; //lock in only this specific address.\n\n\t\tif (!strcmp(com_token, \"hallengeResponse\"))\n\t\t{\n\t\t\t/*Quake3 - \"\\xff\\xff\\xff\\xffchallengeResponse challenge [clchallenge protover]\" (no \\n)*/\n#ifdef Q3CLIENT\n\t\t\tif (connectinfo.protocol == CP_QUAKE3 || connectinfo.protocol == CP_UNKNOWN)\n\t\t\t{\n\t\t\t\t/*throttle*/\n\t\t\t\tif (curtime - lasttime < 500)\n\t\t\t\t\treturn;\n\t\t\t\tlasttime = curtime;\n\n\t\t\t\tmemset(&connectinfo.ext, 0, sizeof(connectinfo.ext));\n\n\t\t\t\tconnectinfo.protocol = CP_QUAKE3;\n\t\t\t\tconnectinfo.challenge = atoi(s+17);\n\t\t\t\tCL_SendConnectPacket (&net_from);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tCon_Printf(\"\\nChallenge from another protocol, ignoring Q3 challenge\\n\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\treturn;\n#else\n\t\t\tCon_Printf(\"\\nUnable to connect to Quake3\\n\");\n\t\t\treturn;\n#endif\n\t\t}\n\t\telse if (!strcmp(com_token, \"hallenge\"))\n\t\t{\n\t\t\t/*Quake2 or Darkplaces*/\n\t\t\tchar *s2;\n\n\t\t\tfor (s2 = s+9; *s2; s2++)\n\t\t\t{\n\t\t\t\tif ((*s2 < '0' || *s2 > '9') && *s2 != '-')\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (!strncmp(s2, \"FTE\", 3) || !strncmp(s2, \"QW\", 2))\n\t\t\t{\t//hack to work around NQ+QW+DP servers that reply with both qw and dp challenge requests.\n\t\t\t\t//we DON'T want to treat it as a dp server. because then we end up with nq-based protocols.\n\t\t\t\treturn;\n\t\t\t}\n\t\t\telse if (*s2 && *s2 != ' ')\n\t\t\t{//and if it's not, we're unlikly to be compatible with whatever it is that's talking at us.\n#ifdef NQPROT\n\t\t\t\tif (connectinfo.protocol == CP_NETQUAKE || connectinfo.protocol == CP_UNKNOWN)\n\t\t\t\t{\n\t\t\t\t\t/*throttle*/\n\t\t\t\t\tif (curtime - lasttime < 500)\n\t\t\t\t\t\treturn;\n\t\t\t\t\tlasttime = curtime;\n\n\t\t\t\t\tconnectinfo.protocol = CP_NETQUAKE;\n\t\t\t\t\tCL_ConnectToDarkPlaces(s+9, &net_from);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tCon_Printf(\"\\nChallenge from another protocol, ignoring DP challenge\\n\");\n#else\n\t\t\t\tCon_Printf(\"\\nUnable connect to DarkPlaces\\n\");\n#endif\n\t\t\t\treturn;\n\t\t\t}\n\n#ifdef Q2CLIENT\n\t\t\tif (connectinfo.protocol == CP_QUAKE2 || connectinfo.protocol == CP_UNKNOWN)\n\t\t\t{\n\t\t\t\tconnectinfo.protocol = CP_QUAKE2;\n\t\t\t\tif (connectinfo.mode == CIM_Q2EONLY)\n\t\t\t\t\tconnectinfo.subprotocol = PROTOCOL_VERSION_Q2EX;\n\t\t\t\telse\n\t\t\t\t\tconnectinfo.subprotocol = PROTOCOL_VERSION_Q2;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tCon_Printf(\"\\nChallenge from another protocol, ignoring Q2 challenge\\n\");\n\t\t\t\treturn;\n\t\t\t}\n#else\n\t\t\tCon_Printf(\"\\nUnable to connect to Quake2\\n\");\n\t\t\treturn;\n#endif\n\t\t\ts+=9;\n\t\t}\n\n\t\t/*no idea, assume a QuakeWorld challenge response ('c' packet)*/\n\n\t\telse if (connectinfo.protocol == CP_QUAKEWORLD || connectinfo.protocol == CP_UNKNOWN)\n\t\t{\n\t\t\tconnectinfo.protocol = CP_QUAKEWORLD;\n\t\t\tconnectinfo.subprotocol = PROTOCOL_VERSION_QW;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tCon_Printf(\"\\nChallenge from another protocol, ignoring QW challenge\\n\");\n\t\t\treturn;\n\t\t}\n\n\t\ts = COM_Parse(s);\t//read the challenge.\n\t\t/*throttle connect requests*/\n\t\tif (curtime - lasttime < 500 && NET_CompareAdr(&net_from, &lastadr) && connectinfo.challenge == atoi(com_token))\n\t\t\treturn;\n\t\tlasttime = curtime;\n\t\tlastadr = net_from;\n\t\tconnectinfo.challenge = atoi(com_token);\n\t\tmemset(&connectinfo.ext, 0, sizeof(connectinfo.ext));\n\n\t\twhile((s = COM_Parse(s)))\n\t\t{\n\t\t\tif (connectinfo.protocol == CP_QUAKE2 && !strncmp(com_token, \"p=\", 2))\n\t\t\t{\n\t\t\t\tchar *p = com_token+2;\n\t\t\t\tdo\n\t\t\t\t{\n\t\t\t\t\tswitch(strtoul(p, &p, 0))\n\t\t\t\t\t{\n\t\t\t\t\tcase PROTOCOL_VERSION_R1Q2:\n#ifdef AVAIL_ZLIB\t\t//r1q2 will typically send us compressed data, which is a problem if we can't handle that (q2pro has a way to disable it).\n\t\t\t\t\t\tif (connectinfo.subprotocol < PROTOCOL_VERSION_R1Q2)\n\t\t\t\t\t\t\tconnectinfo.subprotocol = PROTOCOL_VERSION_R1Q2;\n#endif\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase PROTOCOL_VERSION_Q2PRO:\n\t\t\t\t\t\tif (connectinfo.subprotocol < PROTOCOL_VERSION_Q2PRO)\n\t\t\t\t\t\t\tconnectinfo.subprotocol = PROTOCOL_VERSION_Q2PRO;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase PROTOCOL_VERSION_Q2EX:\n\t\t\t\t\t\tif (connectinfo.subprotocol < PROTOCOL_VERSION_Q2EX)\n\t\t\t\t\t\t\tconnectinfo.subprotocol = PROTOCOL_VERSION_Q2EX;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t} while (*p++ == ',');\n\t\t\t}\n\t\t}\n\n\t\t//if its over q2e's lan layer then pretend there was a p=2023 hint in there...\n\t\tif (connectinfo.protocol == CP_QUAKE2 && net_from.prot == NP_KEXLAN)\n\t\t\tif (connectinfo.subprotocol < PROTOCOL_VERSION_Q2EX)\n\t\t\t\tconnectinfo.subprotocol = PROTOCOL_VERSION_Q2EX;\n\n\t\tfor(;;)\n\t\t{\n\t\t\tint cmd = MSG_ReadLong ();\n\t\t\tif (msg_badread)\n\t\t\t\tbreak;\n\t\t\tif (cmd == PROTOCOL_VERSION_VARLENGTH)\n\t\t\t{\n\t\t\t\tint len = MSG_ReadLong();\n\t\t\t\tif (len < 0 || len > 8192)\n\t\t\t\t\tbreak;\n\t\t\t\tc = MSG_ReadLong();/*ident*/\n\t\t\t\tswitch(c)\n\t\t\t\t{\n\t\t\t\tcase PROTOCOL_INFO_GUID:\n\t\t\t\t\tif (len > sizeof(connectinfo.ext.guidsalt)-1)\n\t\t\t\t\t{\n\t\t\t\t\t\tMSG_ReadData(connectinfo.ext.guidsalt, sizeof(connectinfo.ext.guidsalt));\n\t\t\t\t\t\tMSG_ReadSkip(len-sizeof(connectinfo.ext.guidsalt));\n\t\t\t\t\t\tlen = sizeof(connectinfo.ext.guidsalt)-1;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\tMSG_ReadData(connectinfo.ext.guidsalt, len);\n\t\t\t\t\tconnectinfo.ext.guidsalt[len] = 0;\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tMSG_ReadSkip(len); /*payload*/\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tunsigned int l = MSG_ReadLong();\n\t\t\t\tswitch(cmd)\n\t\t\t\t{\n\t\t\t\tcase PROTOCOL_VERSION_FTE1:\t\t\tconnectinfo.ext.fte1 = l;\t\tbreak;\n\t\t\t\tcase PROTOCOL_VERSION_FTE2:\t\t\tconnectinfo.ext.fte2 = l;\t\tbreak;\n\t\t\t\tcase PROTOCOL_VERSION_EZQUAKE1:\t\tconnectinfo.ext.ez1 = l;\t\tbreak;\n\t\t\t\tcase PROTOCOL_VERSION_FRAGMENT:\t\tconnectinfo.ext.mtu = l;\t\tbreak;\n#ifdef HAVE_DTLS\n\t\t\t\tcase PROTOCOL_VERSION_DTLSUPGRADE:\tcandtls = l;\tbreak;\t//0:not enabled. 1:explicit use allowed. 2:favour it. 3: require it\n#endif\n#ifdef HUFFNETWORK\n\t\t\t\tcase PROTOCOL_VERSION_HUFFMAN:\t\tconnectinfo.ext.compresscrc = l;\tbreak;\n#endif\n\t\t\t\tcase PROTOCOL_INFO_GUID:\t\t\tQ_snprintfz(connectinfo.ext.guidsalt, sizeof(connectinfo.ext.guidsalt), \"0x%x\", l);\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n#ifdef HAVE_DTLS\n\t\tif ((candtls && net_enable_dtls.ival) && net_from.prot == NP_DGRAM && (connectinfo.peercred.hash || net_enable_dtls.ival>1 || candtls > 1) && !NET_IsEncrypted(&net_from))\n\t\t{\n\t\t\t//c2s getchallenge\t\t\t<no client details, only leaks that its quakelike, something you can maybe guess from port numbers>\n\t\t\t//s2c c%u\\0DTLS=$candtls\t<may leak server details>\n\t\t\t//<<YOU ARE HERE>>\n\t\t\t//c2s dtlsconnect %u [REALTARGET]\t<FIXME: target server is plain text, not entirely unlike tls1.2, but still worse than a vpn and could be improved>\n\t\t\t//s2c dtlsopened\t\t\t<no details at all, other than that the server is now willing to accept dtls handshakes etc>\n\t\t\t//c2s DTLS(getchallenge)\t<start here if you're using dtls:// scheme>\n\t\t\t//DTLS(etc)\n\n\t\t\t//NOTE: the dtlsconnect/dtlsopened parts are redundant and the non-dtls parts are now entirely optional (and should be skipped if the client requries/knows the server supports dtls)\n\t\t\t//the challenge response includes server capabilities, so we still need the getchallenge/response part of the handshake despite dtls making the actual challenge part redundant.\n\n\t\t\t//getchallenge has to be done twice, with the outer one only reporting whether dtls can/should be used.\n\t\t\t//this means the actual connect packet is already over dtls, which protects the user's userinfo.\n\t\t\t//FIXME: do rcon via dtls too, but requires tracking pending rcon packets until the handshake completes.\n\n\t\t\t//server says it can do dtls, but will still need to ask it to allocate extra resources for us (I hadn't gotten dtls cookies working properly at that point).\n\n\t\t\tif (net_enable_dtls.ival>0)\n\t\t\t{\n\t\t\t\tchar *pkt;\n\t\t\t\t//qwfwd proxy routing. it doesn't support it yet, but hey, if its willing to forward the dtls packets its all good.\n\t\t\t\tchar *at;\n\t\t\t\tif ((at = strrchr(cls.servername, '@')) && !strchr(cls.servername, '/'))\n\t\t\t\t{\n\t\t\t\t\t*at = 0;\n\t\t\t\t\tpkt = va(\"%c%c%c%c\"\"dtlsconnect %i %s\", 255, 255, 255, 255, connectinfo.challenge, cls.servername);\n\t\t\t\t\t*at = '@';\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tpkt = va(\"%c%c%c%c\"\"dtlsconnect %i\", 255, 255, 255, 255, connectinfo.challenge);\n\t\t\t\tNET_SendPacket (cls.sockets, strlen(pkt), pkt, &net_from);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\telse if (candtls >= 3)\n\t\t\t{\n\t\t\t\tCvar_Set(&cl_disconnectreason, va(\"DTLS is disabled, but server requires it. not connecting\\n\"));\n\t\t\t\tconnectinfo.trying = false;\n\t\t\t\tCon_Printf(\"DTLS is disabled, but server requires it. Set ^[/net_enable_dtls 1^] before connecting again.\\n\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tif (net_enable_dtls.ival>=3 && !NET_IsEncrypted(&net_from))\n\t\t{\n\t\t\tCvar_Set(&cl_disconnectreason, va(\"Server does not support/allow dtls. not connecting\\n\"));\n\t\t\tconnectinfo.trying = false;\n\t\t\tCon_Printf(\"Server does not support/allow dtls. not connecting.\\n\");\n\t\t\treturn;\n\t\t}\n#endif\n\n\t\tCL_SendConnectPacket (&net_from);\n\t\treturn;\n\t}\n#ifdef Q2CLIENT\n\tif (connectinfo.protocol == CP_QUAKE2)\n\t{\n\t\tchar *nl;\n\t\tMSG_ReadSkip(-1);\n\t\tc = MSG_GetReadCount();\n\t\ts = MSG_ReadString ();\n\t\tnl = strchr(s, '\\n');\n\t\tif (nl)\n\t\t{\n\t\t\tMSG_ReadSkip(c + nl-s + 1 - MSG_GetReadCount());\n\t\t\tmsg_badread = false;\n\t\t\t*nl = '\\0';\n\t\t}\n\n\t\tCOM_Parse(s);\n\t\tif (!strcmp(com_token, \"print\"))\n\t\t{\n\t\t\tCon_TPrintf (S_COLOR_GRAY\"print\\n\");\n\n\t\t\ts = MSG_ReadString ();\n\t\t\tif (connectinfo.trying && CL_IsPendingServerBaseAddress(&net_from) == false)\n\t\t\t\tCvar_Set(&cl_disconnectreason, s);\n\t\t\tCon_Printf (\"%s\", s);\n\t\t\treturn;\n\t\t}\n\t\telse if (!strcmp(com_token, \"client_connect\"))\n\t\t{\n\t\t\tconnectinfo.protocol = CP_QUAKE2;\n\t\t\tCL_ConnectionlessPacket_Connection(s);\n\t\t\treturn;\n\t\t}\n\t\telse if (!strcmp(com_token, \"disconnect\"))\n\t\t{\n\t\t\tif (NET_CompareAdr(&net_from, &cls.netchan.remote_address))\n\t\t\t{\n\t\t\t\tCvar_Set(&cl_disconnectreason, \"Disconnect request from server\");\n\t\t\t\tCon_Printf (\"disconnect\\n\");\n\t\t\t\tCL_Disconnect_f();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tCon_Printf(\"Ignoring random disconnect command\\n\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tCon_TPrintf (\"unknown connectionless packet for q2:  %s\\n\", s);\n\t\t\tMSG_ReadSkip(c - MSG_GetReadCount());\n\t\t\tc = MSG_ReadByte();\n\t\t}\n\t}\n#endif\n\n#ifdef NQPROT\n\tif (c == 'a')\n\t{\n\t\ts = MSG_ReadString ();\n\t\tCOM_Parse(s);\n\t\tif (!strcmp(com_token, \"ccept\"))\n\t\t{\n\t\t\t/*this is a DP server... but we don't know which version nor nq protocol*/\n\t\t\tCon_Printf (S_COLOR_GRAY\"accept\\n\");\n\t\t\tif (cls.state == ca_connected)\n\t\t\t\treturn;\t//we're already connected. don't do it again!\n\n\t\t\tif (!CL_IsPendingServerAddress(&net_from))\n\t\t\t{\n\t\t\t\t//if (net_from.type != NA_LOOPBACK)\n\t\t\t\tCon_TPrintf (\"ignoring connection\\n\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tValidation_Apply_Ruleset();\n\t\t\tNetchan_Setup(NCF_CLIENT, &cls.netchan, &net_from, connectinfo.qport, 0);\n\t\t\tCL_ParseEstablished();\n\n\t\t\tcls.netchan.isnqprotocol = true;\n\t\t\tcls.protocol = CP_NETQUAKE;\n\t\t\tcls.protocol_nq = CPNQ_ID;\t//assume vanilla protocol until we know better.\n\t\t\tcls.proquake_angles_hack = false;\n\t\t\tcls.challenge = connectinfo.challenge;\n\t\t\tconnectinfo.trying = false;\n\n\t\t\tcls.demonum = -1;\t\t\t// not in the demo loop now\n\t\t\tcls.state = ca_connected;\n\n\n\t\t\tSCR_BeginLoadingPlaque();\n\t\t\treturn;\n\t\t}\n\t}\n\n\tif (c == 'i')\n\t{\n\t\tif (!strncmp(net_message.data+4, \"infoResponse\\n\", 13))\n\t\t{\n\t\t\tCon_TPrintf (S_COLOR_GRAY\"infoResponse\\n\");\n\t\t\tInfo_Print(net_message.data+17, \"\");\n\t\t\treturn;\n\t\t}\n\t}\n\tif (c == 'g')\n\t{\n\t\tif (!strncmp(net_message.data+4, \"getserversResponse\", 18))\n\t\t{\n\t\t\tqbyte *b = net_message.data+4+18;\n\t\t\tCon_TPrintf (S_COLOR_GRAY\"getserversResponse\\n\");\n\t\t\twhile (b+7 <= net_message.data+net_message.cursize)\n\t\t\t{\n\t\t\t\tif (*b == '\\\\')\n\t\t\t\t{\n\t\t\t\t\tb+=1;\n\t\t\t\t\tCon_Printf(\"%u.%u.%u.%u:%u\\n\", b[0], b[1], b[2], b[3], b[5]|(b[4]<<8));\n\t\t\t\t\tb+=6;\n\t\t\t\t}\n\t\t\t\telse if (*b == '/')\n\t\t\t\t{\n\t\t\t\t\tb+=1;\n\t\t\t\t\tCon_Printf(\"[%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x]:%u\\n\", (b[0]<<8)|b[1], (b[2]<<8)|b[3], (b[4]<<8)|b[5], (b[6]<<8)|b[7], (b[8]<<8)|b[9], (b[10]<<8)|b[11], (b[12]<<8)|b[13], (b[14]<<8)|b[15], b[17]|(b[16]<<8));\n\t\t\t\t\tb+=18;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t}\n#endif\n\n\tif (c == 'd'/*M2C_MASTER_REPLY*/)\n\t{\n\t\ts = MSG_ReadString ();\n\t\tCOM_Parse(s);\n\n\t\tif (!strcmp(com_token, \"isconnect\"))\n\t\t{\n\t\t\tCon_Printf(\"Disconnect\\n\");\n\t\t\tif (CL_IsPendingServerAddress(&net_from))\n\t\t\t{\n\t\t\t\tCvar_Set(&cl_disconnectreason, \"Disconnect request from server\");\n\t\t\t\tCL_Disconnect_f();\n\t\t\t}\n\t\t}\n\t\telse if (!strcmp(com_token, \"tlsopened\"))\n\t\t{\t//server is letting us know that its now listening for a dtls handshake.\n#ifdef HAVE_DTLS\n\t\t\tdtlscred_t cred;\n\t\t\tCon_Printf (S_COLOR_GRAY\"dtlsopened\\n\");\n\t\t\tif (!CL_IsPendingServerAddress(&net_from))\n\t\t\t\treturn;\n\n\t\t\tmemset(&cred, 0, sizeof(cred));\n\t\t\tcred.peer = connectinfo.peercred;\n\t\t\tif (NET_DTLS_Create(cls.sockets, &net_from, &cred, true))\n\t\t\t{\n\t\t\t\tconnectinfo.numadr = 1;\t//fixate on this resolved address.\n\t\t\t\tconnectinfo.adr[0] = net_from;\n\t\t\t\tconnectinfo.adr[0].prot = NP_DTLS;\n\n\t\t\t\tconnectinfo.time = 0;\t//send a new challenge NOW.\n\t\t\t}\n\t\t\telse\n\t\t\t\tCL_ConnectAbort(\"Unable to initialise dtls driver. You may need to adjust tls_provider or disable dtls with ^[/net_enable_dtls 0^]\\n\");\t//this is a local issue, and not a result on remote packets.\n#else\n\t\t\tCon_Printf (\"dtlsopened (unsupported)\\n\");\n#endif\n\t\t}\n\t\telse if (*s != '\\n')\n\t\t{\t//qw master server list response\n\t\t\tCon_Printf (\"server ip list\\n\");\n\t\t}\n\t\telse\n\t\t{\n\t\t\tCon_Printf (\"disconnect\\n\");\n\t\t\tif (cls.demoplayback != DPB_NONE)\n\t\t\t{\n\t\t\t\tCon_Printf(\"Disconnect\\n\");\n\t\t\t\tCL_Disconnect_f();\n\t\t\t}\n\t\t}\n\t\treturn;\n\t}\n\n\tif (c == S2C_CONNECTION)\n\t{\n\t\ts = NULL;\n\t\tconnectinfo.protocol = CP_QUAKEWORLD;\n\t\tconnectinfo.subprotocol = PROTOCOL_VERSION_QW;\n\n\t\tCL_ConnectionlessPacket_Connection(NULL);\n\t\treturn;\n\t}\n#ifdef QUAKESPYAPI\n\t// remote command from gui front end\n\tif (c == A2C_CLIENT_COMMAND)\t//man I hate this.\n\t{\n\t\tchar\tcmdtext[2048];\n\n\t\tif (net_from.type == NA_INVALID || net_from.type != net_local_cl_ipadr.type || net_from.type != NA_IP\n\t\t\t|| ((*(unsigned *)net_from.address.ip != *(unsigned *)net_local_cl_ipadr.address.ip) && (*(unsigned *)net_from.address.ip != htonl(INADDR_LOOPBACK))))\n\t\t{\n\t\t\tCon_TPrintf (\"Command packet from remote host.  Ignored.\\n\");\n\t\t\treturn;\n\t\t}\n#if defined(_WIN32) && !defined(WINRT)\n\t\tShowWindow (mainwindow, SW_RESTORE);\n\t\tSetForegroundWindow (mainwindow);\n#endif\n\t\ts = MSG_ReadString ();\n\n\t\tCon_TPrintf (\"client command: %s\\n\", s);\n\n\t\tQ_strncpyz(cmdtext, s, sizeof(cmdtext));\n\n\t\ts = MSG_ReadString ();\n\n\t\twhile (*s && isspace(*s))\n\t\t\ts++;\n\t\twhile (*s && isspace(s[strlen(s) - 1]))\n\t\t\ts[strlen(s) - 1] = 0;\n\n\t\tif (!allowremotecmd && (!*localid.string || strcmp(localid.string, s)))\n\t\t{\n\t\t\tif (!*localid.string)\n\t\t\t{\n\t\t\t\tCon_TPrintf (\"^&C0Command packet received from local host, but no localid has been set.  You may need to upgrade your server browser.\\n\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tCon_TPrintf (\"^&C0Invalid localid on command packet received from local host. \\n|%s| != |%s|\\nYou may need to reload your server browser and game.\\n\",\n\t\t\t\ts, localid.string);\n\t\t\tCvar_Set(&localid, \"\");\n\t\t\treturn;\n\t\t}\n\n\t\tCbuf_AddText (cmdtext, RESTRICT_SERVER);\n\t\tallowremotecmd = false;\n\t\treturn;\n\t}\n#endif\n\t// print command from somewhere\n\tif (c == 'p')\n\t{\n\t\tif (!strncmp(net_message.data+4, \"print\\n\", 6))\n\t\t{\t//quake2+quake3 send rejects this way\n\t\t\tCon_TPrintf (S_COLOR_GRAY\"print\\n\");\n\t\t\tCon_Printf (\"%s\", net_message.data+10);\n\n\t\t\tif (connectinfo.trying && CL_IsPendingServerBaseAddress(&net_from) == false)\n\t\t\t\tCvar_Set(&cl_disconnectreason, net_message.data+10);\n\t\t\treturn;\n\t\t}\n\t}\n\tif (c == A2C_PRINT)\n\t{\t//closest quakeworld has to a reject message\n\t\tCon_TPrintf (S_COLOR_GRAY\"print\\n\");\n\n\t\ts = MSG_ReadString ();\n\t\tCon_Printf (\"%s\", s);\n\n\t\tif (connectinfo.trying && CL_IsPendingServerBaseAddress(&net_from) == false)\n\t\t\tCvar_Set(&cl_disconnectreason, s);\n\t\treturn;\n\t}\n\tif (c == 'r')\n\t{\t//darkplaces-style rejects\n\t\ts = MSG_ReadString ();\n\t\tCon_Printf(\"r%s\\n\", s);\n\n\t\tif (connectinfo.trying && CL_IsPendingServerBaseAddress(&net_from) == false)\n\t\t\tCvar_Set(&cl_disconnectreason, s);\n\t\treturn;\n\t}\n\n//happens in demos\n\tif (c == svc_disconnect && cls.demoplayback != DPB_NONE && net_from.type == NA_INVALID)\n\t{\n\t\tCL_NextDemo();\n\t\tHost_EndGame (NULL);\t//end of demo.\n\t\treturn;\n\t}\n\n\tCon_TPrintf (\"unknown connectionless packet:  %c\\n\", c);\n}\n\n#ifdef NQPROT\nvoid CLNQ_ConnectionlessPacket(void)\n{\n\tchar *s;\n\tint length;\n\tunsigned short port;\n\n\tif (net_message.cursize < 5)\n\t\treturn;\t//not enough size to be meaningful (qe does not include a port number)\n\n\tMSG_BeginReading (&net_message, msg_nullnetprim);\n\tlength = LongSwap(MSG_ReadLong ());\n\tif (!(length & NETFLAG_CTL))\n\t\treturn;\t//not an nq control packet.\n\tlength &= NETFLAG_LENGTH_MASK;\n\tif (length != net_message.cursize)\n\t\treturn;\t//not an nq packet.\n\n\tswitch(MSG_ReadByte())\n\t{\n\tcase CCREP_ACCEPT:\n\t\tconnectinfo.trying = false;\n\t\tif (cls.state >= ca_connected)\n\t\t{\n\t\t\tif (cls.demoplayback == DPB_NONE)\n\t\t\t\tCon_TPrintf (\"Dup connect received.  Ignored.\\n\");\n\t\t\treturn;\n\t\t}\n\n\t\tif (length == 5)\n\t\t{\t//QE strips the port entirely.\n\t\t\tcls.proquake_angles_hack = false;\n\t\t\tcls.protocol_nq = CPNQ_ID;\n\t\t\tCon_DPrintf(\"QuakeEx server...\\n\");\n\t\t}\n\t\telse\n\t\t{\n\t\t\tport = htons((unsigned short)MSG_ReadLong()); //this is the port that we're meant to respond to...\n\t\t\tif (msg_badread)\t//qe has no port specified. and that's fine when its over dtls anyway.\n\t\t\t\tport = 0;\n\n\n\t\t\tcls.proquake_angles_hack = false;\n\t\t\tcls.protocol_nq = CPNQ_ID;\n\t\t\tif (MSG_ReadByte() == 1)\t//a proquake server adds a little extra info\n\t\t\t{\n\t\t\t\tint ver = MSG_ReadByte();\n\t\t\t\tint flags = MSG_ReadByte();\n\t\t\t\tCon_DPrintf(\"ProQuake server %i.%i\\n\", ver/10, ver%10);\n\n//\t\t\t\tif (ver >= 34)\n\t\t\t\tcls.proquake_angles_hack = true;\n\t\t\t\tif (flags & 1)\n\t\t\t\t{\n\t\t\t\t\t//its a 'pure' server.\n\t\t\t\t\tCon_Printf(\"pure ProQuake server\\n\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (flags & 0x80)\n\t\t\t\t\tport = 0;\t//don't force the port.\n\t\t\t}\n\n\t\t\tif (port && port != net_from.port)\n\t\t\t{\n\t\t\t\tchar buf[256];\n\t\t\t\tnet_from.port = port;\n\t\t\t\tCon_Printf(\"redirecting to port %s\\n\", NET_AdrToString(buf, sizeof(buf), &net_from));\n\t\t\t}\n\t\t}\n\n\t\tValidation_Apply_Ruleset();\n\n\t\tcls.fteprotocolextensions = connectinfo.ext.fte1;\n\t\tcls.fteprotocolextensions2 = connectinfo.ext.fte2;\n\t\tcls.ezprotocolextensions1 = connectinfo.ext.ez1;\n\t\tNetchan_Setup (NCF_CLIENT, &cls.netchan, &net_from, connectinfo.qport, 0);\n\t\tCL_ParseEstablished();\n\t\tcls.netchan.isnqprotocol = true;\n\t\tcls.netchan.compresstable = NULL;\n\t\tcls.protocol = CP_NETQUAKE;\n\t\tcls.state = ca_connected;\n\n\t\ttotal_loading_size = 100;\n\t\tcurrent_loading_size = 0;\n\t\tSCR_SetLoadingStage(LS_CLIENT);\n\n#ifdef QUAKESPYAPI\n\t\tallowremotecmd = false; // localid required now for remote cmds\n#endif\n\n\t\tif (length == 5)\n\t\t\tcls.qex = (connectinfo.mode==CIM_QEONLY);\n\t\telse\n\t\t{\n\t\t\t//send a dummy packet.\n\t\t\t//this makes our local firewall think we initialised the conversation, so that we can receive their packets. however this only works if our nat uses the same public port for private ports.\n\t\t\tNetchan_Transmit(&cls.netchan, 1, \"\\x01\", 2500);\n\t\t}\n\t\treturn;\n\n\tcase CCREP_REJECT:\n\t\ts = MSG_ReadString();\n\t\tCon_Printf(\"Connect failed\\n%s\\n\", s);\n\n\t\tif (connectinfo.trying && CL_IsPendingServerBaseAddress(&net_from) == false)\n\t\t\tCvar_Set(&cl_disconnectreason, s);\n\t\treturn;\n\t}\n}\n#endif\n\nvoid CL_MVDUpdateSpectator (void);\nvoid CL_WriteDemoMessage (sizebuf_t *msg, int payloadoffset);\n\nvoid CL_ReadPacket(void)\n{\n\tif (!qrenderer)\n\t\treturn;\n\n#ifdef HAVE_DTLS\n\tif (*(int *)net_message.data != -1)\n\t\tif (NET_DTLS_Decode(cls.sockets))\n\t\t\tif (!net_message.cursize)\n\t\t\t\treturn;\n#endif\n\n#if defined(SUPPORT_ICE)\n\tif (ICE_WasStun(cls.sockets))\n\t\treturn;\n#endif\n\n#ifdef NQPROT\n\tif (cls.demoplayback == DPB_NETQUAKE)\n\t{\n\t\tMSG_BeginReading (&net_message, cls.netchan.netprim);\n\t\tcls.netchan.last_received = realtime;\n\t\tCLNQ_ParseServerMessage ();\n\t\treturn;\n\t}\n#endif\n#ifdef Q2CLIENT\n\tif (cls.demoplayback == DPB_QUAKE2)\n\t{\n\t\tMSG_BeginReading (&net_message, cls.netchan.netprim);\n\t\tcls.netchan.last_received = realtime;\n\t\tCLQ2_ParseServerMessage ();\n\t\treturn;\n\t}\n#endif\n\t//\n\t// remote command packet\n\t//\n\tif (*(int *)net_message.data == -1)\n\t{\n\t\tCL_ConnectionlessPacket ();\n\t\treturn;\n\t}\n\n\tif (cls.state == ca_disconnected)\n\t{\t//connect to nq servers, but don't get confused with sequenced packets.\n\t\tif (NET_WasSpecialPacket(cls.sockets))\n\t\t\treturn;\n#ifdef NQPROT\n\t\tCLNQ_ConnectionlessPacket ();\n#endif\n\t\treturn;\t//ignore it. We arn't connected.\n\t}\n\n\tif (net_message.cursize < 6 && cls.demoplayback != DPB_MVD) //MVDs don't have the whole sequence header thing going on\n\t{\n\t\tchar adr[MAX_ADR_SIZE];\n\t\tif (net_message.cursize == 1 && net_message.data[0] == A2A_ACK)\n\t\t\tCon_TPrintf (\"%s: Ack (Pong)\\n\", NET_AdrToString(adr, sizeof(adr), &net_from));\n\t\telse\n\t\t\tCon_TPrintf (\"%s: Runt packet (%i bytes)\\n\", NET_AdrToString(adr, sizeof(adr), &net_from), net_message.cursize);\n\t\treturn;\n\t}\n\n\t//\n\t// packet from server\n\t//\n\tif (!cls.demoplayback &&\n\t\t!NET_CompareAdr (&net_from, &cls.netchan.remote_address))\n\t{\n\t\tchar adr[MAX_ADR_SIZE];\n\t\tif (NET_WasSpecialPacket(cls.sockets))\n\t\t\treturn;\n\t\tCon_DPrintf (\"%s:sequenced packet from wrong server\\n\"\n\t\t\t,NET_AdrToString(adr, sizeof(adr), &net_from));\n\t\treturn;\n\t}\n\n\tif (cls.netchan.flags&NCF_STUNAWARE)\t//should be safe to do this here.\n\t\tif (NET_WasSpecialPacket(cls.sockets))\n\t\t\treturn;\n\n\tswitch(cls.protocol)\n\t{\n\tcase CP_NETQUAKE:\n#ifdef NQPROT\n\t\tswitch(NQNetChan_Process(&cls.netchan))\n\t\t{\n\t\tcase NQNC_IGNORED:\n\t\t\tbreak;\n\t\tcase NQNC_ACK:\n\t\tcase NQNC_RELIABLE:\n\t\tcase NQNC_UNRELIABLE:\n\t\t\tMSG_ChangePrimitives(cls.netchan.netprim);\n\t\t\tCL_WriteDemoMessage (&net_message, MSG_GetReadCount());\n\t\t\tCLNQ_ParseServerMessage ();\n\t\t\tbreak;\n\t\t}\n#endif\n\t\tbreak;\n\tcase CP_PLUGIN:\n\t\tbreak;\n\tcase CP_QUAKE2:\n#ifdef Q2CLIENT\n\t\tif (!Netchan_Process(&cls.netchan))\n\t\t\treturn;\t\t// wasn't accepted for some reason\n\t\tCLQ2_ParseServerMessage ();\n\t\tbreak;\n#endif\n\tcase CP_QUAKE3:\n#ifdef Q3CLIENT\n\t\t{\n\t\t\tcactive_t newstate = q3->cl.ParseServerMessage(&net_message);\n\t\t\tif (newstate != cls.state)\n\t\t\t{\n\t\t\t\tcls.state = newstate;\n\t\t\t\tif (cls.state == ca_active)\n\t\t\t\t\tCL_MakeActive(\"Quake3Arena\");\t//became active, can flush old stuff now.\n\t\t\t}\n\t\t}\n#endif\n\t\tbreak;\n\tcase CP_QUAKEWORLD:\n\t\tif (cls.demoplayback == DPB_MVD)\n\t\t{\n\t\t\tMSG_BeginReading(&net_message, cls.netchan.netprim);\n\t\t\tcls.netchan.last_received = realtime;\n\t\t\tcls.netchan.outgoing_sequence = cls.netchan.incoming_sequence;\n\t\t}\n\t\telse if (!Netchan_Process(&cls.netchan))\n\t\t\treturn;\t\t// wasn't accepted for some reason\n\n\t\tCL_WriteDemoMessage (&net_message, MSG_GetReadCount());\n\n\t\tif (cls.netchan.incoming_sequence > cls.netchan.outgoing_sequence)\n\t\t{\t//server should not be responding to packets we have not sent yet\n\t\t\tCon_DPrintf(\"Server is from the future! (%i packets)\\n\", cls.netchan.incoming_sequence - cls.netchan.outgoing_sequence);\n\t\t\tcls.netchan.outgoing_sequence = cls.netchan.incoming_sequence;\n\t\t}\n\t\tMSG_ChangePrimitives(cls.netchan.netprim);\n\t\tCLQW_ParseServerMessage ();\n\t\tbreak;\n\tcase CP_UNKNOWN:\n\t\tbreak;\n\t}\n}\n/*\n=================\nCL_ReadPackets\n=================\n*/\nvoid CL_ReadPackets (void)\n{\n\tif\t(cls.demoplayback != DPB_NONE)\n\t{\n\t\twhile(CL_GetDemoMessage())\n\t\t\tCL_ReadPacket();\n\t}\n\telse\n\t\tNET_ReadPackets(cls.sockets);\n\n\t//\n\t// check timeout\n\t//\n\tif (cls.state >= ca_connected\n\t && realtime - cls.netchan.last_received > cl_timeout.value && !cls.demoplayback)\n\t{\n#ifdef HAVE_SERVER\n\t\t/*don't timeout when we're the actual server*/\n\t\tif (!sv.state)\n#endif\n\t\t{\n\t\t\tCon_TPrintf (\"\\nServer connection timed out.\\n\");\n\t\t\tCL_Disconnect (\"Connection Timed Out\");\n\t\t\treturn;\n\t\t}\n\t}\n\n\tif (cls.demoplayback == DPB_MVD)\n\t{\n\t\tCL_MVDUpdateSpectator();\n\t}\n}\n\n//=============================================================================\n\nqboolean CL_AllowArbitaryDownload(const char *oldname, const char *localfile)\n{\n\tint allow;\n\t//never allow certain (native code) arbitary downloads.\n\tif (!Q_strncasecmp(localfile, \"game\", 4) ||\t//q2-ey things\n\t\t!Q_strcasecmp(localfile, \"progs.dat\") || !Q_strcasecmp(localfile, \"menu.dat\") || !Q_strcasecmp(localfile, \"csprogs.dat\") || !Q_strcasecmp(localfile, \"qwprogs.dat\") || //overriding gamecode is bad (csqc should be dlcached)\n\t\tstrstr(localfile, \"\\\\\") || strstr(localfile, \"..\") || strstr(localfile, \"./\") || strstr(localfile, \":\") || strstr(localfile, \"//\") ||\t//certain path patterns are just bad\n\t\tQ_strcasestr(localfile, \".qvm\") || Q_strcasestr(localfile, \".dll\") || Q_strcasestr(localfile, \".so\") || Q_strcasestr(localfile, \".dylib\"))\t//disallow any native code\n\t{\t//yes, I know the user can use a different progs from the one that is specified. If you leave it blank there will be no problem. (server isn't allowed to stuff progs cvar)\n\t\tCon_Printf(\"Ignoring arbitrary download to \\\"%s\\\" due to possible security risk\\n\", localfile);\n\t\treturn false;\n\t}\n\tallow = cl_download_redirection.ival;\n\tif (allow == 2)\n\t{\n\t\tchar ext[8];\n\t\tCOM_FileExtension(localfile, ext, sizeof(ext));\n\t\tif (!strncmp(localfile, \"demos/\", 6) && (!Q_strcasecmp(ext, \"mvd\") || !Q_strcasecmp(ext, \"gz\")))\n\t\t\treturn true;\t//mvdsv popularised the server sending 'download demo/foobar.mvd' in response to 'download demonum/5' aka 'cmd dl #'\n\t\telse if (!strncmp(localfile, \"package/\", 8) && (!Q_strcasecmp(ext, \"pak\") || !Q_strcasecmp(ext, \"pk3\") || !Q_strcasecmp(ext, \"pk4\")))\n\t\t\treturn true;\t//packages, woo.\n\t\t\t\t\t\t\t//fixme: we should probably try using package/$gamedir/foo.pak if we get redirected to that.\n\t\telse\n\t\t{\n\t\t\tCon_Printf(\"Ignoring non-package download redirection to \\\"%s\\\"\\n\", localfile);\n\t\t\treturn false;\n\t\t}\n\t}\n\tif (allow)\n\t\treturn true;\n\tCon_Printf(\"Ignoring download redirection to \\\"%s\\\". This server may require you to set cl_download_redirection to 2.\\n\", localfile);\n\treturn false;\n}\n\n#if defined(NQPROT) && defined(HAVE_LEGACY)\n//this is for DP compat.\nstatic void CL_Curl_f(void)\n{\n\t//curl --args url\n\tint i, argc = Cmd_Argc();\n\tconst char *arg, *gamedir, *localterse/*no dlcache*/= NULL;\n\tchar localname[MAX_QPATH];\n\tchar localnametmp[MAX_QPATH];\n\tint usage = 0;\n\tqboolean alreadyhave = false;\n\textern char *cl_dp_packagenames;\n\tunsigned int dlflags = DLLF_VERBOSE|DLLF_ALLOWWEB;\n\tconst char *ext;\n\tif (argc < 2)\n\t{\n\t\tCon_Printf(\"%s: No args\\n\", Cmd_Argv(0));\n\t\treturn;\n\t}\n//\tCon_Printf(\"%s %s\\n\", Cmd_Argv(0), Cmd_Args());\n\tfor (i = 1; i < argc; i++)\n\t{\n\t\targ = Cmd_Argv(i);\n\t\tif (!strcmp(arg, \"--info\"))\n\t\t{\n\t\t\tCon_Printf(\"%s %s: not implemented\\n\", Cmd_Argv(0), arg);\n\t\t\treturn;\n\t\t}\n\t\telse if (!strcmp(arg, \"--cancel\"))\n\t\t{\n\t\t\tCon_Printf(\"%s %s: not implemented\\n\", Cmd_Argv(0), arg);\n\t\t\treturn;\n\t\t}\n\t\telse if (!strcmp(arg, \"--pak\"))\n\t\t\tusage |= 1;\n\t\telse if (!strcmp(arg, \"--cachepic\"))\n\t\t\tusage |= 2;\n\t\telse if (!strcmp(arg, \"--skinframe\"))\n\t\t\tusage |= 4;\n\t\telse if (!strcmp(arg, \"--for\"))\n\t\t{\n\t\t\talreadyhave = true;\t//assume we have a package that satisfies the file name.\n\t\t\tfor (i++; i < argc-1; i++)\t//all but the last...\n\t\t\t{\n\t\t\t\targ = Cmd_Argv(i);\n\t\t\t\tif (!CL_CheckDLFile(arg))\n\t\t\t\t{\n\t\t\t\t\talreadyhave = false;\t//I guess we didn't after all.\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse if (!strcmp(arg, \"--forthismap\"))\n\t\t{\n\t\t\t//'don't reconnect on failure'\n\t\t\t//though I'm guessing its better expressed as just flagging it as mandatory.\n\t\t\tdlflags |= DLLF_REQUIRED;\n\t\t}\n\t\telse if (!strcmp(arg, \"--as\"))\n\t\t{\n\t\t\t//explicit local filename\n\t\t\tlocalterse = Cmd_Argv(++i);\n\t\t}\n\t\telse if (!strcmp(arg, \"--clear_autodownload\"))\n\t\t{\n\t\t\tZ_Free(cl_dp_packagenames);\n\t\t\tcl_dp_packagenames = NULL;\n\t\t\treturn;\n\t\t}\n\t\telse if (!strcmp(arg, \"--finish_autodownload\"))\n\t\t{\n\t\t\t//not really sure why this is needed\n//\t\t\tCon_Printf(\"%s %s: not implemented\\n\", Cmd_Argv(0), arg);\n\t\t\treturn;\n\t\t}\n\t\telse if (!strcmp(arg, \"--maxspeed=\"))\n\t\t\t;\n\t\telse if (*arg == '-')\n\t\t\tCon_Printf(\"%s: Unknown option %s\\n\", Cmd_Argv(0), arg);\n\t\telse\n\t\t\t;\t//usually just the last arg, but may also be some parameter for an unknown arg.\n\t}\n\targ = Cmd_Argv(argc-1);\n\tif (!localterse)\n\t{\n\t\tchar *t;\n\t\tlocalterse = strrchr(arg, '/');\n\t\tif (!localterse)\n\t\t\tlocalterse = arg;\n\t\tt = strchr(localterse, '?');\n\t\tif (t)\n\t\t\t*t = 0;\n\t\tif (t-localterse < countof(localnametmp))\n\t\t{\n\t\t\tmemcpy(localnametmp, localterse, t-localterse);\n\t\t\tlocalnametmp[t-localterse] = 0;\n\t\t\tlocalterse = localnametmp;\n\t\t}\n\t}\n\tif (!localterse)\n\t{\n\t\t//for compat, we should look for the last / and truncate on a ?.\n\t\tCon_Printf(\"%s: skipping download of %s, as the local name was not explicitly given\\n\", Cmd_Argv(0), arg);\n\t\treturn;\n\t}\n\text = COM_GetFileExtension(localterse, NULL);\n\tif (usage == 1 && (!strcmp(ext, \".pk3\") || !strcmp(ext, \".pak\")))\n\t{\n\t\tdlflags |= DLLF_NONGAME;\n\t\tgamedir = FS_GetGamedir(true);\n\t\tFS_GenCachedPakName(va(\"%s/%s\", gamedir, localterse), NULL, localname, sizeof(localname));\n\n\t\tif (!alreadyhave)\n\t\t\tif (!CL_CheckOrEnqueDownloadFile(arg, localname, dlflags))\n\t\t\t\tCon_Printf(\"Downloading %s to %s\\n\", arg, localname);\n\n\t\tif (cl_dp_packagenames)\n\t\t\tZ_StrCat(&cl_dp_packagenames, va(\"%s%s/%s\", cl_dp_packagenames?\" \":\"\", gamedir, localterse));\n\t}\n\telse\n\t{\n\t\tCon_Printf(\"%s: %s: non-package downloads are not supported\\n\", Cmd_Argv(0), arg);\n\t\treturn;\n\t}\n}\n#endif\n\n/*\n=====================\nCL_Download_f\n=====================\n*/\nvoid CL_Download_f (void)\n{\n//\tchar *p, *q;\n\tchar *url = Cmd_Argv(1);\n\tchar *localname = Cmd_Argv(2);\n\n#ifdef WEBCLIENT\n\tif (!strnicmp(url, \"http://\", 7) || !strnicmp(url, \"https://\", 8) || !strnicmp(url, \"ftp://\", 6))\n\t{\n\t\tif (Cmd_IsInsecure())\n\t\t\treturn;\n\t\tif (!*localname)\n\t\t{\n\t\t\tlocalname = strrchr(url, '/');\n\t\t\tif (localname)\n\t\t\t\tlocalname++;\n\t\t\telse\n\t\t\t{\n\t\t\t\tCon_TPrintf (\"no local name specified\\n\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\tHTTP_CL_Get(url, localname, NULL);//\"test.txt\");\n\t\treturn;\n\t}\n#endif\n\n\tif (!strnicmp(url, \"qw://\", 5) || !strnicmp(url, \"q2://\", 5))\n\t{\n\t\turl += 5;\n\t\tif (*url == '/')\t//a conforming url should always have a host section, an empty one is simply three slashes.\n\t\t\turl++;\n\t}\n\n\tif (!*localname)\n\t\tlocalname = url;\n\n\tif ((cls.state == ca_disconnected || cls.demoplayback) && !(cls.demoplayback == DPB_MVD && (cls.demoeztv_ext&EZTV_DOWNLOAD)))\n\t{\n\t\tCon_TPrintf (\"Must be connected.\\n\");\n\t\treturn;\n\t}\n\n\tif (cls.netchan.remote_address.type == NA_LOOPBACK)\n\t{\n\t\tCon_TPrintf (\"Must be connected.\\n\");\n\t\treturn;\n\t}\n\n\tif (Cmd_Argc() != 2 && Cmd_Argc() != 3)\n\t{\n\t\tCon_TPrintf (\"Usage: download <datafile> <localname>\\n\");\n\t\treturn;\n\t}\n\n\tif (Cmd_IsInsecure())\t//mark server specified downloads.\n\t{\n\t\tif (cls.download && cls.download->method == DL_QWPENDING)\n\t\t\tDL_Abort(cls.download, QDL_FAILED);\n\n\t\t//don't let gamecode order us to download random junk\n\t\tif (!CL_AllowArbitaryDownload(NULL, localname))\n\t\t\treturn;\n\n\t\tCL_CheckOrEnqueDownloadFile(url, localname, DLLF_REQUIRED|DLLF_VERBOSE);\n\t\treturn;\n\t}\n\n\tCL_EnqueDownload(url, localname, DLLF_USEREXPLICIT|DLLF_IGNOREFAILED|DLLF_REQUIRED|DLLF_OVERWRITE|DLLF_VERBOSE);\n}\n\nvoid CL_DownloadSize_f(void)\n{\n\tdownloadlist_t *dl;\n\tchar *rname;\n\tchar *size;\n\tchar *redirection;\n\n\t//if this is a demo.. urm?\n\t//ignore it. This saves any spam.\n\tif (cls.demoplayback)\n\t\treturn;\n\n\trname = Cmd_Argv(1);\n\tsize = Cmd_Argv(2);\n\tif (!strcmp(size, \"e\"))\n\t{\n\t\tCon_Printf(CON_ERROR\"Download of \\\"%s\\\" failed. Not found.\\n\", rname);\n\t\tCL_DownloadFailed(rname, NULL, DLFAIL_SERVERFILE);\n\t}\n\telse if (!strcmp(size, \"p\"))\n\t{\n\t\tif (cls.download && stricmp(cls.download->remotename, rname))\n\t\t{\n\t\t\tCon_Printf(CON_ERROR\"Download of \\\"%s\\\" failed. Not allowed.\\n\", rname);\n\t\t\tCL_DownloadFailed(rname, NULL, DLFAIL_SERVERCVAR);\n\t\t}\n\t}\n\telse if (!strcmp(size, \"r\"))\n\t{\t//'download this file instead'\n\t\tredirection = Cmd_Argv(3);\n\n\t\tif (!CL_AllowArbitaryDownload(rname, redirection))\n\t\t\treturn;\n\n\t\tdl = CL_DownloadFailed(rname, NULL, DLFAIL_REDIRECTED);\n\t\tCon_DPrintf(\"Download of \\\"%s\\\" redirected to \\\"%s\\\".\\n\", rname, redirection);\n\n\t\tif (!strncmp(redirection, \"package/\", 8))\n\t\t{\t//redirected to a package, make sure we cache it in the proper place.\n\t\t\tchar pkn[MAX_QPATH], pkh[32];\n\t\t\tchar localname[MAX_QPATH];\n\t\t\tchar *spn = cl.serverpacknames, *sph = cl.serverpackhashes;\n\t\t\t*pkh = 0;\n\t\t\twhile(spn && sph)\n\t\t\t{\n\t\t\t\tspn=COM_ParseOut(spn, pkn, sizeof(pkn));\n\t\t\t\tsph=COM_ParseOut(sph, pkh, sizeof(pkh));\n\t\t\t\tif (!spn || !sph)\n\t\t\t\t\tbreak;\n\t\t\t\tif (!strcmp(pkn, redirection+8))\n\t\t\t\t\tbreak;\n\t\t\t\t*pkh = 0;\n\t\t\t}\n\t\t\tif (*pkh)\n\t\t\t\tif (FS_GenCachedPakName(redirection+8, pkh, localname, sizeof(localname)))\n\t\t\t\t\tCL_CheckOrEnqueDownloadFile(redirection+8, localname, DLLF_NONGAME);\n\t\t}\n\t\telse\n\t\t\tCL_CheckOrEnqueDownloadFile(redirection, NULL, dl->flags);\n\t}\n\telse\n\t{\n\t\tfor (dl = cl.downloadlist; dl; dl = dl->next)\n\t\t{\n\t\t\tif (!strcmp(dl->rname, rname))\n\t\t\t{\n\t\t\t\tdl->size = strtoul(size, NULL, 0);\n\t\t\t\tdl->flags &= ~DLLF_SIZEUNKNOWN;\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n}\n\nvoid CL_FinishDownload(char *filename, char *tempname);\nstatic void CL_ForceStopDownload (qboolean finish)\n{\n\tqdownload_t *dl = cls.download;\n\tif (Cmd_IsInsecure())\n\t{\n\t\tCon_Printf(CON_WARNING \"Execution from server rejected for %s\\n\", Cmd_Argv(0));\n\t\treturn;\n\t}\n\tif (!dl)\n\t\treturn;\n\n\tif (!dl->file)\n\t{\n\t\tif (dl->method == DL_QWPENDING)\n\t\t\tfinish = false;\n\t\telse\n\t\t{\n\t\t\tCon_Printf(\"No files downloading by QW protocol\\n\");\n\t\t\treturn;\n\t\t}\n\t}\n\n\tif (finish)\n\t\tDL_Abort(dl, QDL_COMPLETED);\n\telse\n\t\tDL_Abort(dl, QDL_FAILED);\n\n\t// get another file if needed\n\tCL_RequestNextDownload ();\n}\nvoid CL_SkipDownload_f (void)\n{\n\tCL_ForceStopDownload(false);\n}\nvoid CL_FinishDownload_f (void)\n{\n\tCL_ForceStopDownload(true);\n}\n\n#if defined(_WIN32) && !defined(WINRT) && !defined(_XBOX)\n#include \"winquake.h\"\n/*\n=================\nCL_Minimize_f\n=================\n*/\nvoid CL_Windows_f (void)\n{\n\tif (!mainwindow)\n\t{\n\t\tCon_Printf(\"Cannot comply\\n\");\n\t\treturn;\n\t}\n//\tif (modestate == MS_WINDOWED)\n//\t\tShowWindow(mainwindow, SW_MINIMIZE);\n//\telse\n\t\tSendMessage(mainwindow, WM_SYSKEYUP, VK_TAB, 1 | (0x0F << 16) | (1<<29));\n}\n#endif\n\n#ifdef HAVE_SERVER\nvoid CL_ServerInfo_f(void)\n{\n\tif (!sv.state && cls.state && Cmd_Argc() < 2)\n\t{\n\t\tif (cl.haveserverinfo)\n\t\t{\n\t\t\tInfoBuf_Print (&cl.serverinfo, \"\");\n\t\t\tCon_Printf(\"[%u, %s]\\n\", (unsigned int)cl.serverinfo.totalsize, cls.servername);\n\t\t}\n\t\telse\n\t\t\tCmd_ForwardToServer ();\n\t}\n\telse\n\t{\n\t\tSV_Serverinfo_f();\t//allow it to be set... (whoops)\n\t}\n}\n#endif\n\n#ifdef FTPCLIENT\nvoid CL_FTP_f(void)\n{\n\tFTP_Client_Command(Cmd_Args(), NULL);\n}\n#endif\n\n//fixme: make a cvar\nvoid CL_Fog_f(void)\n{\n\tint ftype;\n\tvec3_t rgb;\n\tif (!Q_strcasecmp(Cmd_Argv(0), \"waterfog\"))\n\t\tftype = FOGTYPE_WATER;\n\telse if (!Q_strcasecmp(Cmd_Argv(0), \"skyroomfog\"))\n\t\tftype = FOGTYPE_SKYROOM;\n\telse //fog\n\t\tftype = FOGTYPE_AIR;\n\tif ((cl.fog_locked && !Cmd_FromGamecode() && !cls.allow_cheats) || Cmd_Argc() <= 1)\n\t{\n\t\tstatic const char *fognames[FOGTYPE_COUNT]={\"fog\",\"waterfog\",\"skyroomfog\"};\n\t\tif (Cmd_ExecLevel != RESTRICT_INSECURE)\n\t\t\tCon_Printf(\"Current %s %f (r:%f g:%f b:%f, a:%f bias:%f)\\n\", fognames[ftype], cl.fog[ftype].density, cl.fog[ftype].colour[0], cl.fog[ftype].colour[1], cl.fog[ftype].colour[2], cl.fog[ftype].alpha, cl.fog[ftype].depthbias);\n\t}\n\telse\n\t{\n\t\tCL_ResetFog(ftype);\n\t\tVectorSet(rgb, 0.3,0.3,0.3);\n\n\t\tswitch(Cmd_Argc())\n\t\t{\n\t\tcase 1:\n\t\t\tbreak;\n\t\tcase 2:\n\t\t\tcl.fog[ftype].density = atof(Cmd_Argv(1));\n\t\t\tbreak;\n\t\tcase 3:\n\t\t\tcl.fog[ftype].density = atof(Cmd_Argv(1));\n\t\t\trgb[0] = rgb[1] = rgb[2] = atof(Cmd_Argv(2));\n\t\t\tbreak;\n\t\tcase 4:\n\t\t\tcl.fog[ftype].density = 0.05;\t//make something up for vauge compat with fitzquake, so it doesn't get the default of 0\n\t\t\trgb[0] = atof(Cmd_Argv(1));\n\t\t\trgb[1] = atof(Cmd_Argv(2));\n\t\t\trgb[2] = atof(Cmd_Argv(3));\n\t\t\tbreak;\n\t\tcase 5:\n\t\tdefault:\n\t\t\tcl.fog[ftype].density = atof(Cmd_Argv(1));\n\t\t\trgb[0] = atof(Cmd_Argv(2));\n\t\t\trgb[1] = atof(Cmd_Argv(3));\n\t\t\trgb[2] = atof(Cmd_Argv(4));\n\t\t\tbreak;\n\t\t}\n\n\t\tif (rgb[0]>=2 || rgb[1]>=2 || rgb[2]>=2)\t//we allow SOME slop for hdr fog... hopefully we won't need it. this is mostly just an issue when skyfog is enabled[default .5] ('why is my sky white on map FOO')\n\t\t\tCon_Printf(CON_WARNING \"Fog colour of %g %g %g exceeds standard 0-1 range\\n\", rgb[0], rgb[1], rgb[2]);\n\t\tcl.fog[ftype].colour[0] = SRGBf(rgb[0]);\n\t\tcl.fog[ftype].colour[1] = SRGBf(rgb[1]);\n\t\tcl.fog[ftype].colour[2] = SRGBf(rgb[2]);\n\n\t\tif (cls.state == ca_active)\n\t\t\tcl.fog[ftype].time += 1;\n\n\t\t//fitz:\n\t\t//if (Cmd_Argc() >= 6) cl.fog[ftype].time += atof(Cmd_Argv(5));\n\t\t//dp:\n\t\tif (Cmd_Argc() >= 6) cl.fog[ftype].alpha = atof(Cmd_Argv(5));\n\t\tif (Cmd_Argc() >= 7) cl.fog[ftype].depthbias = atof(Cmd_Argv(6));\n\t\t//if (Cmd_Argc() >= 8) cl.fog[ftype].end = atof(Cmd_Argv(7));\n\t\t//if (Cmd_Argc() >= 9) cl.fog[ftype].height = atof(Cmd_Argv(8));\n\t\t//if (Cmd_Argc() >= 10) cl.fog[ftype].fadedepth = atof(Cmd_Argv(9));\n\n\t\tif (Cmd_FromGamecode())\n\t\t\tcl.fog_locked = !!cl.fog[ftype].density;\n\n#ifdef HAVE_LEGACY\n\t\tif (cl.fog[ftype].colour[0] > 1 || cl.fog[ftype].colour[1] > 1 || cl.fog[ftype].colour[2] > 1)\n\t\t\tCon_DPrintf(CON_WARNING \"Fog is oversaturated. This can result in compatibility issues.\\n\");\n#endif\n\t}\n}\n\n#ifdef _DEBUG\nvoid CL_FreeSpace_f(void)\n{\n\tchar buf[32];\n\tquint64_t freespace;\n\tconst char *freepath = Cmd_Argv(1);\n\tif (Sys_GetFreeDiskSpace(freepath, &freespace))\n\t\tCon_Printf(\"%s: %s available\\n\", freepath, FS_AbbreviateSize(buf,sizeof(buf),freespace));\n\telse\n\t\tCon_Printf(\"%s: disk free not queryable\\n\", freepath);\n}\n#endif\n\nvoid CL_CrashMeEndgame_f(void)\n{\n\tHost_EndGame(\"crashme! %s\", Cmd_Args());\n}\nvoid CL_CrashMeError_f(void)\n{\n\tSys_Error(\"crashme! %s\", Cmd_Args());\n}\n\n\nstatic char *ShowTime(unsigned int seconds)\n{\n\tchar buf[1024];\n\tchar *b = buf;\n\t*b = 0;\n\n\tif (seconds > 60)\n\t{\n\t\tif (seconds > 60*60)\n\t\t{\n\t\t\tif (seconds > 24*60*60)\n\t\t\t{\n\t\t\t\tstrcpy(b, va(\"%id \", seconds/(24*60*60)));\n\t\t\t\tb += strlen(b);\n\t\t\t\tseconds %= 24*60*60;\n\t\t\t}\n\n\t\t\tstrcpy(b, va(\"%ih \", seconds/(60*60)));\n\t\t\tb += strlen(b);\n\t\t\tseconds %= 60*60;\n\t\t}\n\t\tstrcpy(b, va(\"%im \", seconds/60));\n\t\tb += strlen(b);\n\t\tseconds %= 60;\n\t}\n\tstrcpy(b, va(\"%is\", seconds));\n\tb += strlen(b);\n\n\treturn va(\"%s\", buf);\n}\nvoid CL_Status_f(void)\n{\n#ifdef CSQC_DAT\n\textern world_t csqc_world;\n#endif\n\tchar adr[128];\n\tfloat pi, po, bi, bo;\n\n\tNET_PrintAddresses(cls.sockets);\n\tNET_PrintConnectionsStatus(cls.sockets);\n\tif (NET_GetRates(cls.sockets, &pi, &po, &bi, &bo))\n\t\tCon_Printf(\"packets,bytes/sec: in: %g %g  out: %g %g\\n\", pi, bi, po, bo);\t//not relevent as a limit.\n\n\tif (cls.state)\n\t{\n\t\tchar cert[8192];\n\t\tqbyte fp[DIGEST_MAXSIZE+1];\n\t\tchar b64[(DIGEST_MAXSIZE*4)/3+1];\n\t\tif (NET_GetConnectionCertificate(cls.sockets, &cls.netchan.remote_address, QCERT_ISENCRYPTED, NULL, 0))\n\t\t\tQ_strncpyz(b64, \"<UNENCRYPTED>\", sizeof(b64));\n\t\telse\n\t\t{\n\t\t\tint sz = NET_GetConnectionCertificate(cls.sockets, &cls.netchan.remote_address, QCERT_PEERCERTIFICATE, cert, sizeof(cert));\n\t\t\tif (sz<0)\n\t\t\t\tQ_strncpyz(b64, \"<UNAVAILABLE>\", sizeof(b64));\n\t\t\telse\n\t\t\t{\n\t\t\t\tsz = Base64_EncodeBlockURI(fp, CalcHash(&hash_certfp, fp,sizeof(fp), cert, sz), b64, sizeof(b64));\n\t\t\t\tb64[sz] = 0;\n\t\t\t}\n\t\t}\n\t\tCon_Printf(\"Server address   : %s\\n\", NET_AdrToString(adr, sizeof(adr), &cls.netchan.remote_address));\t//not relevent as a limit.\n\t\tCon_Printf(\"Server cert fp   : %s\\n\", b64);\t//not relevent as a limit.\n\n\t\tCon_Printf(\"Network MTU      : %u (max %u) %s\\n\", cls.netchan.mtu_cur, cls.netchan.mtu_max, (cls.netchan.flags&NCF_FRAGABLE)?\"\":\" (strict)\");\t//not relevent as a limit.\n\t\tswitch(cls.protocol)\n\t\t{\n\t\tdefault:\n\t\tcase CP_UNKNOWN:\n\t\t\tCon_Printf(\"Network Protocol : Unknown\\n\");\n\t\t\tbreak;\n\t\tcase CP_QUAKEWORLD:\n\t\t\tCon_Printf(\"Network Protocol : QuakeWorld\\n\");\n\t\t\tbreak;\n\t#ifdef NQPROT\n\t\tcase CP_NETQUAKE:\n\t\t\tswitch(cls.protocol_nq)\n\t\t\t{\n\t\t\tcase CPNQ_ID:\n\t\t\t\tif (cls.proquake_angles_hack)\n\t\t\t\t\tCon_Printf(\"Network Protocol : ProQuake\\n\");\n\t\t\t\telse\n\t\t\t\t\tCon_Printf(\"Network Protocol : NetQuake\\n\");\n\t\t\t\tbreak;\n\t\t\tcase CPNQ_NEHAHRA:\n\t\t\t\tCon_Printf(\"Network Protocol : Nehahra\\n\");\n\t\t\t\tbreak;\n\t\t\tcase CPNQ_BJP1:\n\t\t\t\tCon_Printf(\"Network Protocol : BJP1\\n\");\n\t\t\t\tbreak;\n\t\t\tcase CPNQ_BJP2:\n\t\t\t\tCon_Printf(\"Network Protocol : BJP2\\n\");\n\t\t\t\tbreak;\n\t\t\tcase CPNQ_BJP3:\n\t\t\t\tCon_Printf(\"Network Protocol : BJP3\\n\");\n\t\t\t\tbreak;\n\t\t\tcase CPNQ_H2MP:\n\t\t\t\tCon_Printf(\"Network Protocol : H2MP\\n\");\n\t\t\t\tbreak;\n\t\t\tcase CPNQ_FITZ666:\n\t\t\t\tCon_Printf(\"Network Protocol : FitzQuake\\n\");\n\t\t\t\tbreak;\n\t\t\tcase CPNQ_DP5:\n\t\t\t\tCon_Printf(\"Network Protocol : DPP5\\n\");\n\t\t\t\tbreak;\n\t\t\tcase CPNQ_DP6:\n\t\t\t\tCon_Printf(\"Network Protocol : DPP6\\n\");\n\t\t\t\tbreak;\n\t\t\tcase CPNQ_DP7:\n\t\t\t\tCon_Printf(\"Network Protocol : DPP7\\n\");\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tbreak;\n\t#endif\n\t#ifdef Q2CLIENT\n\t\tcase CP_QUAKE2:\n\t\t\tswitch (cls.protocol_q2)\n\t\t\t{\n\t\t\tcase PROTOCOL_VERSION_Q2:\n\t\t\t\tCon_Printf(\"Network Protocol : Quake2\\n\");\n\t\t\t\tbreak;\n\t\t\tcase PROTOCOL_VERSION_R1Q2:\n\t\t\t\tCon_Printf(\"Network Protocol : R1Q2\\n\");\n\t\t\t\tbreak;\n\t\t\tcase PROTOCOL_VERSION_Q2PRO:\n\t\t\t\tCon_Printf(\"Network Protocol : Q2Pro\\n\");\n\t\t\t\tbreak;\n\t\t\tcase PROTOCOL_VERSION_Q2EXDEMO:\n\t\t\tcase PROTOCOL_VERSION_Q2EX:\n\t\t\t\tCon_Printf(\"Network Protocol : Quake2Ex\\n\");\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tCon_Printf(\"Network Protocol : Quake2 (OLD)\\n\");\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tbreak;\n\t#endif\n\t#ifdef Q3CLIENT\n\t\tcase CP_QUAKE3:\n\t\t\tCon_Printf(\"Network Protocol : Quake3\\n\");\n\t\t\tbreak;\n\t#endif\n\t#ifdef PLUGINS\n\t\tcase CP_PLUGIN:\n\t\t\tCon_Printf(\"Network Protocol : (unknown, provided by plugin)\\n\");\n\t\t\tbreak;\n\t#endif\n\t\t}\n\n\t\t//just show the more interesting extensions.\n\t\tif (cls.fteprotocolextensions & PEXT_FLOATCOORDS)\n\t\t\tCon_Printf(\"\\textended coords\\n\");\n\t\tif (cls.fteprotocolextensions & PEXT_SPLITSCREEN)\n\t\t\tCon_Printf(\"\\tsplit screen\\n\");\n\t\tif (cls.fteprotocolextensions & PEXT_CSQC)\n\t\t\tCon_Printf(\"\\tcsqc info\\n\");\n\t\tif (cls.fteprotocolextensions2 & PEXT2_VOICECHAT)\n\t\t\tCon_Printf(\"\\tvoice chat\\n\");\n\t\tif (cls.fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS)\n\t\t\tCon_Printf(\"\\treplacement deltas\\n\");\n\t\tif (cls.fteprotocolextensions2 & PEXT2_VRINPUTS)\n\t\t\tCon_Printf(\"\\tvrinputs\\n\");\n\t\tif (cls.fteprotocolextensions2 & PEXT2_INFOBLOBS)\n\t\t\tCon_Printf(\"\\tinfoblobs\\n\");\n\t}\n\n\tif (cl.worldmodel)\n\t{\n\t\tCon_Printf(\"map uptime       : %s\\n\", ShowTime(cl.time));\n\t\tCOM_FileBase(cl.worldmodel->name, adr, sizeof(adr));\n\t\tCon_Printf (\"current map      : %s (%s)\\n\", adr, cl.levelname);\n\t}\n\n#ifdef CSQC_DAT\n\tif (csqc_world.progs)\n\t{\n\t\textern int num_sfx;\n\t\tint count = 0, i;\n\t\tedict_t *e;\n\t\tCon_Printf (\"csqc             : loaded\\n\");\n\t\tfor (i = 0; i < csqc_world.num_edicts; i++)\n\t\t{\n\t\t\te = EDICT_NUM_PB(csqc_world.progs, i);\n\t\t\tif (e && e->ereftype == ER_FREE && Sys_DoubleTime() - e->freetime > 0.5)\n\t\t\t\tcontinue;\t//free, and older than the zombie time\n\t\t\tcount++;\n\t\t}\n\t\tCon_Printf(\"csqc entities    : %i/%i/%i (mem: %.1f%%)\\n\", count, csqc_world.num_edicts, csqc_world.max_edicts, 100*(float)(csqc_world.progs->stringtablesize/(double)csqc_world.progs->stringtablemaxsize));\n\t\tfor (count = 1; count < MAX_CSMODELS; count++)\n\t\t\tif (!*cl.model_csqcname[count])\n\t\t\t\tbreak;\n\t\tCon_Printf(\"csqc models      : %i/%i\\n\", count, MAX_CSMODELS);\n\t\tCon_Printf(\"client sounds    : %i\\n\", num_sfx);\t//there is a limit, its just private. :(\n\n\t\tfor (count = 1; count < MAX_SSPARTICLESPRE; count++)\n\t\t\tif (!cl.particle_csname[count])\n\t\t\t\tbreak;\n\t\tif (count!=1)\n\t\t\tCon_Printf(\"csqc particles   : %i/%i\\n\", count, MAX_CSPARTICLESPRE);\n\t\tif (cl.csqcdebug)\n\t\t\tCon_Printf(\"csqc debug       : true\\n\");\n\t}\n\telse\n\t\tCon_Printf (\"csqc             : not loaded\\n\");\n#endif\n\tCon_Printf(\"gamedir          : %s\\n\", FS_GetGamedir(true));\n}\n\nvoid CL_Demo_SetSpeed_f(void)\n{\n\tchar *s = Cmd_Argv(1);\n\tif (s)\n\t{\n\t\tfloat f = atof(s)/100;\n\t\tCvar_SetValue(&cl_demospeed, f);\n\t}\n\telse\n\t\tCon_Printf(\"demo playback speed %g%%\\n\", cl_demospeed.value * 100);\n}\n\nstatic void CL_UserinfoChanged(void *ctx, const char *keyname)\n{\n\tInfoSync_Add(&cls.userinfosync, ctx, keyname);\n}\n\n\nvoid CL_Skygroup_f(void);\nvoid WAD_ImageList_f(void);\n/*\n=================\nCL_Init\n=================\n*/\nvoid CL_Init (void)\n{\n\textern void CL_Say_f (void);\n\textern void CL_SayMe_f (void);\n\textern void CL_SayTeam_f (void);\n#ifdef QWSKINS\n\textern\tcvar_t\t\tbaseskin;\n\textern\tcvar_t\t\tnoskins;\n#endif\n\tchar *ver;\n\tsize_t seat;\n\n\tcls.state = ca_disconnected;\n\tcls.demotrack = -1;\n\tcls.demonum = -1;\n\n#ifdef SVNREVISION\n\tif (strcmp(STRINGIFY(SVNREVISION), \"-\"))\n\t\tver = va(\"%s v%i.%02i %s\", DISTRIBUTION, FTE_VER_MAJOR, FTE_VER_MINOR, STRINGIFY(SVNREVISION));\n\telse\n#endif\n\t\tver = va(\"%s v%i.%02i\", DISTRIBUTION, FTE_VER_MAJOR, FTE_VER_MINOR);\n\n\tfor (seat = 0; seat < MAX_SPLITS; seat++)\n\t{\n\t\tcls.userinfo[seat].ChangeCTX = &cls.userinfo[seat];\n\t\tcls.userinfo[seat].ChangeCB = CL_UserinfoChanged;\n\t\tInfoBuf_SetStarKey (&cls.userinfo[seat], \"*ver\", ver);\n\t}\n\n\tInitValidation();\n\n\tCL_InitInput ();\n\tCL_InitTEnts ();\n\tCL_InitPrediction ();\n\tCL_InitCam ();\n\tCL_InitDlights();\n\tPM_Init ();\n\tTP_Init();\n\n//\n// register our commands\n//\n\tCLSCR_Init();\n#ifdef MENU_DAT\n\tMP_RegisterCvarsAndCmds();\n#endif\n#ifdef CSQC_DAT\n\tCSQC_RegisterCvarsAndThings();\n#endif\n\tCvar_Register (&host_speeds, cl_controlgroup);\n\n\tCvar_Register (&cfg_save_name, cl_controlgroup);\n\n\tCvar_Register (&cl_disconnectreason, cl_controlgroup);\n\tCvar_Register (&cl_proxyaddr, cl_controlgroup);\n\tCvar_Register (&cl_sendguid, cl_controlgroup);\n\tCvar_Register (&cl_defaultport, cl_controlgroup);\n\tCvar_Register (&cl_servername, cl_controlgroup);\n\tCvar_Register (&cl_serveraddress, cl_controlgroup);\n\tCvar_Register (&cl_demospeed, \"Demo playback\");\n\tCmd_AddCommand(\"demo_setspeed\", CL_Demo_SetSpeed_f);\n\tCvar_Register (&cl_upspeed, cl_inputgroup);\n\tCvar_Register (&cl_forwardspeed, cl_inputgroup);\n\tCvar_Register (&cl_backspeed, cl_inputgroup);\n\tCvar_Register (&cl_sidespeed, cl_inputgroup);\n\tCvar_Register (&cl_movespeedkey, cl_inputgroup);\n\tCvar_Register (&cl_yawspeed, cl_inputgroup);\n\tCvar_Register (&cl_pitchspeed, cl_inputgroup);\n\tCvar_Register (&cl_anglespeedkey, cl_inputgroup);\n\tCvar_Register (&cl_shownet,\tcl_screengroup);\n\tCvar_Register (&cl_sbar,\tcl_screengroup);\n\tCvar_Register (&cl_pure,\tcl_screengroup);\n\tCvar_Register (&cl_hudswap,\tcl_screengroup);\n\tCvar_Register (&cl_maxfps,\tcl_screengroup);\n\tCvar_Register (&cl_maxfps_slop,\tcl_screengroup);\n\tCvar_Register (&cl_idlefps, cl_screengroup);\n\tCvar_Register (&cl_yieldcpu, cl_screengroup);\n\tCvar_Register (&cl_timeout, cl_controlgroup);\n\tCvar_Register (&cl_vrui_force, cl_controlgroup);\n\tCvar_Register (&cl_vrui_lock, cl_controlgroup);\n\tCvar_Register (&lookspring, cl_inputgroup);\n\tCvar_Register (&lookstrafe, cl_inputgroup);\n\tCvar_Register (&sensitivity, cl_inputgroup);\n\n\tCvar_Register (&m_pitch, cl_inputgroup);\n\tCvar_Register (&m_yaw, cl_inputgroup);\n\tCvar_Register (&m_forward, cl_inputgroup);\n\tCvar_Register (&m_side, cl_inputgroup);\n\n\tCvar_Register (&cl_crypt_rcon,\tcl_controlgroup);\n\tCvar_Register (&rcon_password,\tcl_controlgroup);\n\tCvar_Register (&rcon_address,\tcl_controlgroup);\n\n\tCvar_Register (&cl_lerp_maxinterval, cl_controlgroup);\n\tCvar_Register (&cl_lerp_maxdistance, cl_controlgroup);\n\tCvar_Register (&cl_lerp_players, cl_controlgroup);\n\tCvar_Register (&cl_predict_players,\tcl_predictiongroup);\n\tCvar_Register (&cl_predict_players_frac,\tcl_predictiongroup);\n\tCvar_Register (&cl_predict_players_latency,\tcl_predictiongroup);\n\tCvar_Register (&cl_predict_players_nudge,\tcl_predictiongroup);\n\tCvar_Register (&cl_solid_players,\tcl_predictiongroup);\n\n#ifdef QUAKESPYAPI\n\tCvar_Register (&localid,\tcl_controlgroup);\n#endif\n\n\tCvar_Register (&cl_muzzleflash, cl_controlgroup);\n\n#ifdef QWSKINS\n\tCvar_Register (&baseskin,\t\"Teamplay\");\n\tCvar_Register (&noskins,\t\"Teamplay\");\n#endif\n\tCvar_Register (&cl_noblink,\t\"Console controls\");\t//for lack of a better group\n\n\tCvar_Register (&cl_item_bobbing, \"Item effects\");\n\tCvar_Register (&gl_simpleitems, \"Item effects\");\n\n\tCvar_Register (&cl_staticsounds, \"Item effects\");\n\n\tCvar_Register (&r_torch, \"Item effects\");\n\tCvar_Register (&r_rocketlight, \"Item effects\");\n\tCvar_Register (&r_lightflicker, \"Item effects\");\n\tCvar_Register (&cl_r2g, \"Item effects\");\n\tCvar_Register (&r_powerupglow, \"Item effects\");\n\tCvar_Register (&v_powerupshell, \"Item effects\");\n\n\tCvar_Register (&cl_gibfilter, \"Item effects\");\n\tCvar_Register (&cl_deadbodyfilter, \"Item effects\");\n\n\tCvar_Register (&cl_nolerp, \"Item effects\");\n#ifdef NQPROT\n\tCvar_Register (&cl_nolerp_netquake, \"Item effects\");\n\tCvar_Register (&cl_fullpitch_nq, \"Cheats\");\n#endif\n\n\tCvar_Register (&r_drawflame, \"Item effects\");\n\n\tCvar_Register (&cl_downloads, cl_controlgroup);\n\tCvar_Register (&cl_download_csprogs, cl_controlgroup);\n\tCvar_Register (&cl_download_redirection, cl_controlgroup);\n\tCvar_Register (&cl_download_packages, cl_controlgroup);\n\n\t//\n\t// info mirrors\n\t//\n\tCvar_Register (&name,\t\t\t\t\t\tcl_controlgroup);\n\tCvar_Register (&password,\t\t\t\t\tcl_controlgroup);\n\tCvar_Register (&spectator,\t\t\t\t\tcl_controlgroup);\n\tCvar_Register (&skin,\t\t\t\t\t\tcl_controlgroup);\n\tCvar_Register (&model,\t\t\t\t\t\tcl_controlgroup);\n\tCvar_Register (&team,\t\t\t\t\t\tcl_controlgroup);\n\tCvar_Register (&topcolor,\t\t\t\t\tcl_controlgroup);\n\tCvar_Register (&bottomcolor,\t\t\t\tcl_controlgroup);\n\tCvar_Register (&rate,\t\t\t\t\t\tcl_controlgroup);\n\tCvar_Register (&drate,\t\t\t\t\t\tcl_controlgroup);\n\tCvar_Register (&msg,\t\t\t\t\t\tcl_controlgroup);\n#ifdef Q2CLIENT\n\tCvar_Register (&hand,\t\t\t\t\t\tcl_controlgroup);\n#endif\n\tCvar_Register (&noaim,\t\t\t\t\t\tcl_controlgroup);\n\tCvar_Register (&b_switch,\t\t\t\t\tcl_controlgroup);\n\tCvar_Register (&w_switch,\t\t\t\t\tcl_controlgroup);\n#ifdef HEXEN2\n\tCvar_Register (&cl_playerclass,\t\t\t\tcl_controlgroup);\n#endif\n\n\tCvar_Register (&cl_demoreel,\t\t\t\tcl_controlgroup);\n\tCvar_Register (&record_flush,\t\t\t\t\tcl_controlgroup);\n\n\tCvar_Register (&cl_nofake,\t\t\t\t\tcl_controlgroup);\n\tCvar_Register (&cl_chatsound,\t\t\t\t\tcl_controlgroup);\n\tCvar_Register (&cl_enemychatsound,\t\t\t\tcl_controlgroup);\n\tCvar_Register (&cl_teamchatsound,\t\t\t\tcl_controlgroup);\n\n\tCvar_Register (&requiredownloads,\t\t\t\tcl_controlgroup);\n\tCvar_Register (&mod_precache,\t\t\t\t\tcl_controlgroup);\n\tCvar_Register (&cl_standardchat,\t\t\t\tcl_controlgroup);\n\tCvar_Register (&msg_filter,\t\t\t\t\t\tcl_controlgroup);\n\tCvar_Register (&msg_filter_frags,\t\t\t\tcl_controlgroup);\n\tCvar_Register (&cl_standardmsg,\t\t\t\t\tcl_controlgroup);\n\tCvar_Register (&cl_parsewhitetext,\t\t\t\tcl_controlgroup);\n\tCvar_Register (&cl_nopext,\t\t\t\t\t\tcl_controlgroup);\n\tCvar_Register (&cl_pext_mask,\t\t\t\t\tcl_controlgroup);\n\n\tCvar_Register (&cl_splitscreen,\t\t\t\t\tcl_controlgroup);\n\n\tCvar_Register (&cl_fakeframes,\t\t\t\t\tcl_controlgroup);\n\n#ifndef SERVERONLY\n\tCvar_Register (&cl_loopbackprotocol,\t\t\tcl_controlgroup);\n#endif\n\tCvar_Register (&cl_verify_urischeme,\t\t\tcl_controlgroup);\n\n\tCvar_Register (&cl_countpendingpl,\t\t\t\tcl_controlgroup);\n\tCvar_Register (&cl_threadedphysics,\t\t\t\tcl_controlgroup);\n\thud_tracking_show = Cvar_Get(\"hud_tracking_show\", \"1\", 0, \"statusbar\");\n\thud_miniscores_show = Cvar_Get(\"hud_miniscores_show\", \"1\", 0, \"statusbar\");\n\n\tCvar_Register (&cl_dlemptyterminate,\t\t\t\tcl_controlgroup);\n\n\tCvar_Register (&cl_gunx,\t\t\t\t\tcl_controlgroup);\n\tCvar_Register (&cl_guny,\t\t\t\t\tcl_controlgroup);\n\tCvar_Register (&cl_gunz,\t\t\t\t\tcl_controlgroup);\n\n\tCvar_Register (&cl_gunanglex,\t\t\t\t\tcl_controlgroup);\n\tCvar_Register (&cl_gunangley,\t\t\t\t\tcl_controlgroup);\n\tCvar_Register (&cl_gunanglez,\t\t\t\t\tcl_controlgroup);\n\n\tCvar_Register (&ruleset_allow_playercount,\t\t\tcl_controlgroup);\n\tCvar_Register (&ruleset_allow_frj,\t\t\t\tcl_controlgroup);\n\tCvar_Register (&ruleset_allow_semicheats,\t\t\tcl_controlgroup);\n\tCvar_Register (&ruleset_allow_packet,\t\t\t\tcl_controlgroup);\n\tCvar_Register (&ruleset_allow_particle_lightning,\t\tcl_controlgroup);\n\tCvar_Register (&ruleset_allow_overlongsounds,\t\t\tcl_controlgroup);\n\tCvar_Register (&ruleset_allow_larger_models,\t\t\tcl_controlgroup);\n\tCvar_Register (&ruleset_allow_modified_eyes,\t\t\tcl_controlgroup);\n\tCvar_Register (&ruleset_allow_sensitive_texture_replacements,\tcl_controlgroup);\n\tCvar_Register (&ruleset_allow_localvolume,\t\t\tcl_controlgroup);\n\tCvar_Register (&ruleset_allow_shaders,\t\t\tcl_controlgroup);\n\tCvar_Register (&ruleset_allow_watervis,\t\t\tcl_controlgroup);\n\tCvar_Register (&ruleset_allow_fbmodels,\t\t\tcl_controlgroup);\n\n\tCvar_Register (&qtvcl_forceversion1,\t\t\t\tcl_controlgroup);\n\tCvar_Register (&qtvcl_eztvextensions,\t\t\t\tcl_controlgroup);\n#ifdef FTPCLIENT\n\tCmd_AddCommand (\"ftp\", CL_FTP_f);\n#endif\n\n\tCmd_AddCommandD (\"changing\", CL_Changing_f, \"Part of network protocols. This command should not be used manually.\");\n\tCmd_AddCommand (\"disconnect\", CL_Disconnect_f);\n\tCmd_AddCommandAD (\"record\", CL_Record_f, CL_DemoList_c, NULL);\n\tCmd_AddCommandAD (\"rerecord\", CL_ReRecord_f, CL_DemoList_c, \"Reconnects to the previous/current server, but starts recording a clean demo.\");\n\tCmd_AddCommandD (\"stop\", CL_Stop_f, \"Stop demo recording.\");\n\tCmd_AddCommandAD (\"playdemo\", CL_PlayDemo_f, CL_DemoList_c, NULL);\n\tCmd_AddCommand (\"qtvplay\", CL_QTVPlay_f);\n\tCmd_AddCommand (\"qtvlist\", CL_QTVList_f);\n\tCmd_AddCommand (\"qtvdemos\", CL_QTVDemos_f);\n\tCmd_AddCommandD (\"demo_jump\",\t\tCL_DemoJump_f, \"Jump to a specified time in a demo. Prefix with a + or - for a relative offset. Seeking backwards will restart the demo and the fast forward, which can take some time in long demos.\");\n\tCmd_AddCommandD (\"demo_jump_mark\",\tCL_DemoJump_f, \"Jump to the next '//demomark' marker.\");\n\tCmd_AddCommandD (\"demo_jump_end\",\tCL_DemoJump_f, \"Jump to the next intermission message.\");\n\tCmd_AddCommandD (\"demo_nudge\", CL_DemoNudge_f, \"Nudge the demo by one frame. Argument should be +1 or -1. Nudging backwards is limited.\");\n\tCmd_AddCommandAD (\"timedemo\", CL_TimeDemo_f, CL_DemoList_c, NULL);\n#ifdef _DEBUG\n\tCmd_AddCommand (\"freespace\", CL_FreeSpace_f);\n\tCmd_AddCommand (\"crashme_endgame\", CL_CrashMeEndgame_f);\n\tCmd_AddCommand (\"crashme_error\", CL_CrashMeError_f);\n#endif\n\n\tCmd_AddCommandD (\"showpic\", SCR_ShowPic_Script_f, \t\"showpic <imagename> <placename> <x> <y> <zone> [width] [height] [touchcommand]\\nDisplays an image onscreen, that potentially has a key binding attached to it when clicked/touched.\\nzone should be one of: TL, TR, BL, BR, MM, TM, BM, ML, MR. This serves as an extra offset to move the image around the screen without any foreknowledge of the screen resolution.\");\n\tCmd_AddCommandD (\"showpic_removeall\", SCR_ShowPic_Remove_f, \t\"removes any pictures inserted with the showpic command.\");\n\n\tCmd_AddCommandD (\"startdemos\", CL_Startdemos_f, \"Sets the demoreel list, but does not start playing them (use the 'demos' command for that)\");\n\tCmd_AddCommandD (\"demos\", CL_Demos_f, \"Starts playing the demo reel.\");\n\tCmd_AddCommand (\"stopdemo\", CL_Stopdemo_f);\n\n\tCmd_AddCommand (\"skins\", Skin_Skins_f);\n#ifdef QWSKINS\n\tCmd_AddCommand (\"allskins\", Skin_AllSkins_f);\n#endif\n\n\tCmd_AddCommand (\"cl_status\", CL_Status_f);\n\tCmd_AddCommandD (\"quit\", CL_Quit_f, \"Use this command when you get angry. Does not save any cvars. Use cfg_save to save settings, or use the menu for a prompt.\");\n\n#if defined(CL_MASTER) && defined(HAVE_PACKET)\n\tCmd_AddCommandAD (\"connectbr\", CL_ConnectBestRoute_f, CL_Connect_c, \"connect address:port\\nConnect to a qw server using the best route we can detect.\");\n#endif\n\tCmd_AddCommandAD(\"connect\", CL_Connect_f, CL_Connect_c, \"connect scheme://address:port\\nConnect to a server. \"\n#if defined(FTE_TARGET_WEB)\n\t\t\"Use a scheme of rtc[s]://broker/gamename to connect via a webrtc broker.\"\n\t\t\"Use a scheme of ws[s]://server to connect via websockets.\"\n#elif defined(TCPCONNECT)\n\t\t\"Use a scheme of tcp:// or tls:// to connect via non-udp protocols.\"\n//\t\t\"Use a scheme of ws[s]://server to connect via websockets.\"\n#endif\n#ifdef HAVE_DTLS\n\t\t\"Use a scheme of dtls://server to connect securely.\"\n#endif\n#if defined(IRCCONNECT)\n\t\t\"Use irc://network:6667/user[@channel] to connect via an irc server. Not recommended.\"\n#endif\n#if defined(NQPROT) || defined(Q2CLIENT) || defined(Q3CLIENT)\n\t\t\"\\nDefault port is port \"STRINGIFY(PORT_DEFAULTSERVER)\".\"\n\t#ifndef GAME_DEFAULTPORT\n\t\t#ifdef NQPROT\n\t\t\t\t\" NQ:\"STRINGIFY(PORT_NQSERVER)\".\"\n\t\t#endif\n\t\t\t\t\" QW:\"STRINGIFY(PORT_QWSERVER)\".\"\n\t\t#ifdef Q2CLIENT\n\t\t\t\t\" Q2:\"STRINGIFY(PORT_Q2SERVER)\".\"\n\t\t#endif\n\t\t#ifdef Q3CLIENT\n\t\t\t\t\" Q3:\"STRINGIFY(PORT_Q3SERVER)\".\"\n\t\t#endif\n\t#endif\n#endif\n\t\t);\n\tCmd_AddCommandD (\"cl_transfer\", CL_Transfer_f, \"Connect to a different server, disconnecting from the current server only when the new server replies.\");\n#ifdef TCPCONNECT\n\tCmd_AddCommandAD (\"connecttcp\", CL_TCPConnect_f, CL_Connect_c, \"Connect to a server using the tcp:// prefix\");\n\tCmd_AddCommandAD (\"tcpconnect\", CL_TCPConnect_f, CL_Connect_c, \"Connect to a server using the tcp:// prefix\");\n#endif\n#ifdef IRCCONNECT\n\tCmd_AddCommand (\"connectirc\", CL_IRCConnect_f);\n#endif\n#ifdef NQPROT\n\tCmd_AddCommandD (\"connectnq\", CLNQ_Connect_f, \"Connects to the specified server, defaulting to port \"STRINGIFY(PORT_NQSERVER)\". Also disables QW/Q2/Q3/DP handshakes preventing them from being favoured, so should only be used when you actually want NQ protocols specifically.\");\n\t#ifdef HAVE_DTLS\n\tCmd_AddCommandD (\"connectqe\", CLNQ_Connect_f, \"Connects to the specified server, defaulting to port \"STRINGIFY(PORT_NQSERVER)\". Also forces the use of DTLS and QE-specific handshakes. You will also need to ensure the dtls_psk_* cvars are set properly or the server will refuse the connection.\");\n\t#endif\n#endif\n#ifdef Q2CLIENT\n\tCmd_AddCommandD (\"connectq2e\", CLQ2E_Connect_f, \"Connects to the specified server, defaulting to port \"STRINGIFY(PORT_Q2EXSERVER)\".\");\n#endif\n#ifdef HAVE_LEGACY\n\tCmd_AddCommandAD(\"qwurl\", CL_Connect_f, CL_Connect_c, \"For compat with ezquake.\");\n#endif\n\tCmd_AddCommand (\"reconnect\", CL_Reconnect_f);\n\tCmd_AddCommandAD (\"join\", CL_Join_f, CL_Connect_c, \"Switches away from spectator mode, optionally connecting to a different server.\");\n\tCmd_AddCommandAD (\"observe\", CL_Observe_f, CL_Connect_c, \"Switches to spectator mode, optionally connecting to a different server.\");\n\n\tCmd_AddCommand (\"rcon\", CL_Rcon_f);\n\tCmd_AddCommand (\"packet\", CL_Packet_f);\n\tCmd_AddCommand (\"user\", CL_User_f);\n\tCmd_AddCommand (\"users\", CL_Users_f);\n\n#if 1//def _DEBUG\n\tCmd_AddCommand (\"setinfoblob\", CL_SetInfoBlob_f);\n#endif\n\tCmd_AddCommand (\"setinfo\", CL_SetInfo_f);\n\tCmd_AddCommand (\"fullinfo\", CL_FullInfo_f);\n\n\tCmd_AddCommandAD (\"color\", CL_Color_f, CL_Color_c, NULL);\n#if defined(NQPROT) && defined(HAVE_LEGACY)\n\tCmd_AddCommandD (\"curl\",\tCL_Curl_f, \"For use by xonotic.\");\n#endif\n\tCmd_AddCommand (\"download\", CL_Download_f);\n\tCmd_AddCommandD (\"dlsize\", CL_DownloadSize_f, \"For internal use\");\n\tCmd_AddCommandD (\"nextul\", CL_NextUpload, \"For internal use\");\n\tCmd_AddCommandD (\"stopul\", CL_StopUpload, \"For internal use\");\n\n\tCmd_AddCommand (\"skipdl\", CL_SkipDownload_f);\n\tCmd_AddCommand (\"finishdl\", CL_FinishDownload_f);\n\n//\n// forward to server commands\n//\n\tCmd_AddCommand (\"god\", NULL);\t//cheats\n\tCmd_AddCommand (\"give\", NULL);\n\tCmd_AddCommand (\"noclip\", NULL);\n\tCmd_AddCommand (\"6dof\", NULL);\n\tCmd_AddCommand (\"spiderpig\", NULL);\n\tCmd_AddCommand (\"fly\", NULL);\n\tCmd_AddCommand (\"setpos\", NULL);\n\tCmd_AddCommand (\"notarget\", NULL);\n\n\tCmd_AddCommand (\"topten\", NULL);\n\n\tCmd_AddCommand (\"kill\", NULL);\n\tCmd_AddCommand (\"pause\", NULL);\n\tCmd_AddCommandAD (\"say\", CL_Say_f, Key_EmojiCompletion_c, NULL);\n\tCmd_AddCommandAD (\"me\", CL_SayMe_f, Key_EmojiCompletion_c, NULL);\n\tCmd_AddCommandAD (\"sayone\", CL_Say_f, Key_EmojiCompletion_c, NULL);\n\tCmd_AddCommandAD (\"say_team\", CL_SayTeam_f, Key_EmojiCompletion_c, NULL);\n#ifdef HAVE_SERVER\n\tCmd_AddCommand (\"serverinfo\", CL_ServerInfo_f);\n#else\n\tCmd_AddCommand (\"serverinfo\", NULL);\n#endif\n\n\tCmd_AddCommandD (\"fog\", CL_Fog_f, \"fog <density> <red> <green> <blue> <alpha> <depthbias>\");\n\tCmd_AddCommandD (\"waterfog\", CL_Fog_f, \"waterfog <density> <red> <green> <blue> <alpha> <depthbias>\");\n\tCmd_AddCommandD (\"skyroomfog\", CL_Fog_f, \"skyroomfog <density> <red> <green> <blue> <alpha> <depthbias>\");\n\tCmd_AddCommandD (\"skygroup\", CL_Skygroup_f, \"Provides a way to associate a skybox name with a series of maps, so that the requested skybox will override on a per-map basis.\");\n\tCmd_AddCommandD (\"r_imagelist_wad\", WAD_ImageList_f, \"displays the available wad images.\");\n//\n//  Windows commands\n//\n#if defined(_WIN32) && !defined(WINRT) && !defined(_XBOX)\n\tCmd_AddCommand (\"windows\", CL_Windows_f);\n#endif\n\n\tIgnore_Init();\n#ifdef QUAKEHUD\n\tStats_Init();\n#endif\n\tCL_ClearState(false);\t//make sure the cl.* fields are set properly if there's no ssqc or whatever.\n\tR_BumpLightstyles(1);\n}\n\n\n/*\n================\nHost_EndGame\n\nCall this to drop to a console without exiting the qwcl\n================\n*/\nNORETURN void VARGS Host_EndGame (const char *message, ...)\n{\n\tva_list\t\targptr;\n\tchar\t\tstring[1024];\n\n\tif (message)\n\t{\n\t\tva_start (argptr,message);\n\t\tvsnprintf (string,sizeof(string)-1, localtext(message),argptr);\n\t\tva_end (argptr);\n\t}\n\telse\n\t\t*string = 0;\n\n\tCOM_AssertMainThread(string);\n\n\tSCR_EndLoadingPlaque();\n\n\tif (message)\n\t{\n\t\tCon_TPrintf (\"^&C0Host_EndGame: %s\\n\", string);\n\t\tCon_Printf (\"\\n\");\n\t}\n\n\tSCR_EndLoadingPlaque();\n\n\tCL_Disconnect (string);\n\tCL_ConnectAbort(NULL);\n\n\tSV_UnspawnServer();\n\n\tCvar_Set(&cl_shownet, \"0\");\n\n\tlongjmp (host_abort, 1);\n}\n\n/*\n================\nHost_Error\n\nThis shuts down the client and exits qwcl\n================\n*/\nvoid VARGS Host_Error (const char *error, ...)\n{\n\tva_list\t\targptr;\n\tchar\t\tstring[1024];\n\tstatic\tqboolean inerror = false;\n\n\tif (inerror)\n\t\tSys_Error (\"Host_Error: recursively entered\");\n\tinerror = true;\n\n\tva_start (argptr,error);\n\tvsnprintf (string,sizeof(string)-1, localtext(error),argptr);\n\tva_end (argptr);\n\tCOM_AssertMainThread(string);\n\tCon_TPrintf (\"Host_Error: %s\\n\", string);\n\n\tCL_Disconnect (string);\n\tcls.demonum = -1;\n\n\tinerror = false;\n\n// FIXME\n\tSys_Error (\"Host_Error: %s\\n\",string);\n}\n\n\n/*\n===============\nHost_WriteConfiguration\n\nWrites key bindings and archived cvars to config.cfg\n===============\n*/\nvoid Host_WriteConfiguration (void)\n{\n\tvfsfile_t\t*f;\n\tchar savename[MAX_OSPATH];\n\tchar sysname[MAX_OSPATH];\n\n\tif (host_initialized && cfg_save_name.string && *cfg_save_name.string)\n\t{\n\t\tif (strchr(cfg_save_name.string, '.'))\n\t\t{\n\t\t\tCon_TPrintf (CON_ERROR \"Couldn't write config.cfg.\\n\");\n\t\t\treturn;\n\t\t}\n\n\t\tQ_snprintfz(savename, sizeof(savename), \"%s.cfg\", cfg_save_name.string);\n\n\t\tf = FS_OpenVFS(savename, \"wb\", FS_GAMEONLY);\n\t\tif (!f)\n\t\t{\n\t\t\tFS_DisplayPath(savename, FS_GAMEONLY, sysname, sizeof(sysname));\n\t\t\tCon_TPrintf (CON_ERROR \"Couldn't write %s.\\n\", sysname);\n\t\t\treturn;\n\t\t}\n\n\t\tKey_WriteBindings (f);\n\t\tCvar_WriteVariables (f, false, false);\n\n\t\tVFS_CLOSE (f);\n\n\t\tFS_DisplayPath(savename, FS_GAMEONLY, sysname, sizeof(sysname));\n\t\tCon_Printf(\"Wrote %s\\n\", savename);\n\t}\n}\n\n\n//============================================================================\n\n#if 0\n/*\n==================\nHost_SimulationTime\n\nThis determines if enough time has passed to run a simulation frame\n==================\n*/\nqboolean Host_SimulationTime(float time)\n{\n\tfloat fps;\n\n\tif (oldrealtime > realtime)\n\t\toldrealtime = 0;\n\n\tif (cl_maxfps.value)\n\t\tfps = max(30.0, min(cl_maxfps.value, 72.0));\n\telse\n\t\tfps = max(30.0, min(rate.value/80.0, 72.0));\n\n\tif (!cls.timedemo && (realtime + time) - oldrealtime < 1.0/fps)\n\t\treturn false;\t\t\t// framerate is too high\n\treturn true;\n}\n#endif\n\n#include \"fs.h\"\n#define HRF_OVERWRITE\t(1<<0)\n#define HRF_NOOVERWRITE\t(1<<1)\n//\t\t\t\t\t\t(1<<2)\n#define HRF_ABORT\t\t(1<<3)\n\n#define HRF_OPENED\t\t(1<<4)\n#define HRF_DOWNLOADED\t(1<<5)\t//file was actually downloaded, and not from the local system\n#define HRF_WAITING\t\t(1<<6)\t//file looks important enough that we should wait for it to start to download or something before we try doing other stuff.\n#define HRF_DECOMPRESS\t(1<<7)\t//need to degzip it, which prevents streaming.\n\n#define HRF_DEMO_MVD\t(1<<8)\n#define HRF_DEMO_QWD\t(1<<9)\n#define HRF_DEMO_DM2\t(1<<10)\n#define HRF_DEMO_DEM\t(1<<11)\n\n#define HRF_QTVINFO\t\t(1<<12)\n#define HRF_MANIFEST\t(1<<13)\n#define HRF_BSP\t\t\t(1<<14)\n#define HRF_PACKAGE\t\t(1<<15)\t//pak or pk3 that should be installed.\n#define\tHRF_ARCHIVE\t\t(1<<16)\t//zip - treated as a multiple-file 'installer'\n#define HRF_MODEL\t\t(1<<17)\n#define HRF_CONFIG\t\t(1<<18)\t//exec it on the console...\n\n#define HRF_ACTION (HRF_OVERWRITE|HRF_NOOVERWRITE|HRF_ABORT)\n#define HRF_DEMO\t\t(HRF_DEMO_MVD|HRF_DEMO_QWD|HRF_DEMO_DM2|HRF_DEMO_DEM)\n#define HRF_FILETYPES\t(HRF_DEMO|HRF_QTVINFO|HRF_MANIFEST|HRF_BSP|HRF_PACKAGE|HRF_ARCHIVE|HRF_MODEL|HRF_CONFIG)\ntypedef struct {\n\tstruct dl_download *dl;\n\tvfsfile_t *srcfile;\n\tvfsfile_t *dstfile;\n\tchar *packageinfo;\n\tunsigned int flags;\n\tchar fname[1];\t//system path or url.\n} hrf_t;\n\nextern int waitingformanifest;\nvoid Host_DoRunFile(hrf_t *f);\nvoid CL_PlayDemoStream(vfsfile_t *file, char *filename, qboolean issyspath, int demotype, float bufferdelay, unsigned int eztv_ext);\nvoid CL_ParseQTVDescriptor(vfsfile_t *f, const char *name);\n\n//guesses the file type based upon its file extension. mdl/md3/iqm distinctions are not important, so we can usually get away with this in the context of quake.\nunsigned int Host_GuessFileType(const char *mimetype, const char *filename)\n{\n\tif (mimetype)\n\t{\n\t\tif (!strcmp(mimetype, \"application/x-qtv\"))\t//what uses this?\n\t\t\treturn HRF_QTVINFO;\n\t\telse if (!strcmp(mimetype, \"text/x-quaketvident\"))\n\t\t\treturn HRF_QTVINFO;\n\t\telse if (!strcmp(mimetype, \"application/x-fteplugin\"))\n\t\t\treturn HRF_MANIFEST;\n\t\telse if (!strcmp(mimetype, \"application/x-ftemanifest\"))\n\t\t\treturn HRF_MANIFEST;\n\t\telse if (!strcmp(mimetype, \"application/x-multiviewdemo\"))\n\t\t\treturn HRF_DEMO_MVD;\n\t\telse if (!strcmp(mimetype, \"application/zip\"))\n\t\t\treturn HRF_ARCHIVE;\n//\t\telse if (!strcmp(mimetype, \"application/x-ftebsp\"))\n//\t\t\treturn HRF_BSP;\n//\t\telse if (!strcmp(mimetype, \"application/x-ftepackage\"))\n//\t\t\treturn HRF_PACKAGE;\n\t}\n\n\tif (filename)\n\t{\t//find the query or location part of the url, so we can ignore extra stuff.\n\t\tstruct\n\t\t{\n\t\t\tunsigned int type;\n\t\t\tconst char *ext;\n\t\t} exts[] =\n\t\t{\n\t\t\t//demo formats\n\t\t\t{HRF_DEMO_QWD, \"qwd\"},\n\t\t\t{HRF_DEMO_QWD|HRF_DECOMPRESS, \"qwd.gz\"},\n\t\t\t{HRF_DEMO_MVD, \"mvd\"},\n\t\t\t{HRF_DEMO_MVD|HRF_DECOMPRESS, \"mvd.gz\"},\n\t\t\t{HRF_DEMO_DM2, \"dm2\"},\n\t\t\t{HRF_DEMO_DM2|HRF_DECOMPRESS, \"dm2.gz\"},\n\t\t\t{HRF_DEMO_DEM, \"dem\"},\n\t\t\t{HRF_DEMO_DEM|HRF_DECOMPRESS, \"dem.gz\"},\n\t\t\t{HRF_QTVINFO, \"qtv\"},\n\t\t\t//other stuff\n\t\t\t{HRF_MANIFEST, \"fmf\"},\n\t\t\t{HRF_BSP, \"bsp\"},\n\t\t\t{HRF_BSP, \"map\"},\n\t\t\t{HRF_CONFIG, \"cfg\"},\n\t\t\t{HRF_CONFIG, \"rc\"},\n\t\t\t{HRF_PACKAGE, \"kpf\"},\n\t\t\t{HRF_PACKAGE, \"pak\"},\n\t\t\t{HRF_PACKAGE, \"pk3\"},\n\t\t\t{HRF_PACKAGE, \"pk4\"},\n\t\t\t{HRF_PACKAGE, \"wad\"},\n\t\t\t{HRF_ARCHIVE, \"zip\"},\n\t\t\t//model formats\n\t\t\t{HRF_MODEL, \"mdl\"},\n\t\t\t{HRF_MODEL, \"md2\"},\n\t\t\t{HRF_MODEL, \"md3\"},\n\t\t\t{HRF_MODEL, \"iqm\"},\n\t\t\t{HRF_MODEL, \"vvm\"},\n\t\t\t{HRF_MODEL, \"psk\"},\n\t\t\t{HRF_MODEL, \"zym\"},\n\t\t\t{HRF_MODEL, \"dpm\"},\n\t\t\t{HRF_MODEL, \"gltf\"},\n\t\t\t{HRF_MODEL, \"glb\"},\n\t\t\t//sprites\n\t\t\t{HRF_MODEL, \"spr\"},\n\t\t\t{HRF_MODEL, \"spr2\"},\n\t\t\t//static stuff\n\t\t\t{HRF_MODEL, \"obj\"},\n\t\t\t{HRF_MODEL, \"lwo\"},\n\t\t\t{HRF_MODEL, \"ase\"},\n\t\t};\n\t\tsize_t i;\n\t\tconst char *ext;\n\t\tconst char *stop = filename+strlen(filename);\n\t\tconst char *tag = strchr(filename, '?');\n\t\tif (tag && tag < stop)\n\t\t\tstop = tag;\n\t\ttag = strchr(filename, '#');\n\t\tif (tag && tag < stop)\n\t\t\tstop = tag;\n\n\t\text = COM_GetFileExtension(filename, stop);\n\t\tif (!Q_strstopcasecmp(ext, stop, \".php\"))\t//deal with extra extensions the easy way\n\t\t\text = COM_GetFileExtension(filename, stop=ext);\n\t\tif (!Q_strstopcasecmp(ext, stop, \".gz\") || !Q_strstopcasecmp(ext, stop, \".xz\"))\t//deal with extra extensions the easy way\n\t\t\text = COM_GetFileExtension(filename, ext);\n\t\tif (*ext == '.')\n\t\t\text++;\n\n\t\tfor (i = 0; i < countof(exts); i++)\n\t\t\tif (!Q_strstopcasecmp(ext, stop, exts[i].ext))\n\t\t\t\treturn exts[i].type;\n\t}\n\treturn 0;\n}\n\nvoid Host_RunFileDownloaded(struct dl_download *dl)\n{\n\thrf_t *f = dl->user_ctx;\n\tif(!f)\t//download was previously cancelled.\n\t\treturn;\n\tif (dl->status == DL_FAILED)\n\t{\n\t\tf->flags |= HRF_ABORT;\n\t\tf->srcfile = NULL;\n\t}\n\telse\n\t{\n\t\tif (f->srcfile)\t//erk?\n\t\t\tVFS_CLOSE(f->srcfile);\n\t\tf->flags |= HRF_OPENED;\n\t\tf->srcfile = dl->file;\n\t\tdl->file = NULL;\n\t}\n\n\tHost_DoRunFile(f);\n}\nqboolean Host_BeginFileDownload(struct dl_download *dl, char *mimetype)\n{\n\tqboolean result = false;\n\t//at this point the file is still downloading, so don't copy it out just yet.\n\thrf_t *f = dl->user_ctx;\n\n\tif (f->flags & HRF_WAITING)\n\t{\n\t\tf->flags &= ~HRF_WAITING;\n\t\twaitingformanifest--;\n\t}\n\n\tif (!(f->flags & HRF_FILETYPES))\n\t{\n\t\tf->flags |= Host_GuessFileType(mimetype, f->fname);\n\t\tif (!(f->flags & HRF_FILETYPES))\n\t\t{\n\t\t\tif (mimetype)\n\t\t\t\tCon_Printf(\"mime type \\\"%s\\\" nor file extension of \\\"%s\\\" not known\\n\", mimetype, f->fname);\n\t\t\telse\n\t\t\t\tCon_Printf(\"file extension of \\\"%s\\\" not known\\n\", f->fname);\n\t\t\t//file type not guessable from extension either.\n\t\t\tf->flags |= HRF_ABORT;\n\t\t\tHost_DoRunFile(f);\n\t\t\treturn false;\n\t\t}\n\n\t\tif ((f->flags & HRF_MANIFEST) && !(f->flags & HRF_WAITING))\n\t\t{\n\t\t\tf->flags |= HRF_WAITING;\n\t\t\twaitingformanifest++;\n\t\t}\n\t}\n\n#ifdef AVAIL_GZDEC\n\t//seeking means we can rewind\n\tif (f->flags & HRF_DECOMPRESS)\n\t{\t//if its a gzip, we'll probably need to decompress it ourselves... in case the server doesn't use content-encoding:gzip\n\t\t//our demo playback should decompress it when its fin ally available.\n\t\tdl->file = VFSPIPE_Open(1, true);\n\t\treturn true;\n\t}\n\telse\n#endif\n\t\t if (f->flags & HRF_DEMO_QWD)\n\t\tCL_PlayDemoStream((dl->file = VFSPIPE_Open(2, true)), f->fname, true, DPB_QUAKEWORLD, 0, 0);\n\telse if (f->flags & HRF_DEMO_MVD)\n\t\tCL_PlayDemoStream((dl->file = VFSPIPE_Open(2, true)), f->fname, true, DPB_MVD, 0, 0);\n#ifdef Q2CLIENT\n\telse if (f->flags & HRF_DEMO_DM2)\n\t\tCL_PlayDemoStream((dl->file = VFSPIPE_Open(2, true)), f->fname, true, DPB_QUAKE2, 0, 0);\n#endif\n#ifdef NQPROT\n\telse if (f->flags & HRF_DEMO_DEM)\n\t{\t//fixme: the demo code can't handle the cd track with streamed/missing-so-far writes.\n\t\tdl->file = VFSPIPE_Open(1, true);\t//make sure the reader will be seekable, so we can rewind.\n//\t\tCL_PlayDemoStream((dl->file = VFSPIPE_Open(2, true)), f->fname, DPB_NETQUAKE, 0, 0);\n\t}\n#endif\n\telse if (f->flags & (HRF_MANIFEST | HRF_QTVINFO))\n\t{\n\t\t//just use a pipe instead of a temp file, working around an issue with temp files on android\n\t\tdl->file = VFSPIPE_Open(1, false);\n\t\treturn true;\n\t}\n\telse if (f->flags & HRF_ARCHIVE)\n\t{\n\t\tchar cachename[MAX_QPATH];\n\t\tif (!FS_PathURLCache(f->fname, cachename, sizeof(cachename)))\n\t\t\treturn false;\n\t\tf->srcfile = FS_OpenVFS(cachename, \"rb\", FS_ROOT);\n\t\tif (f->srcfile)\n\t\t{\n\t\t\tf->flags |= HRF_OPENED;\n\t\t\tHost_DoRunFile(f);\n\t\t\treturn false;\n\t\t}\n\t\tFS_CreatePath(cachename, FS_ROOT);\n\t\tdl->file = FS_OpenVFS(cachename, \"wb\", FS_ROOT);\n\t\tif (dl->file)\n\t\t\treturn true;\t//okay, continue downloading.\n\t}\n\telse if (f->flags & HRF_DEMO)\n\t\tCon_Printf(\"%s: format not supported\\n\", f->fname);\t//demos that are not supported in this build for one reason or another\n\telse\n\t\treturn true;\n\n\t//demos stream, so we want to continue the http download, but we don't want to do anything with the result.\n\tif (f->flags & HRF_DEMO)\n\t\tresult = true;\n\telse\n\t{\n\t\tf->flags |= HRF_ABORT;\n\t\tHost_DoRunFile(f);\n\t}\n\treturn result;\n}\nvoid Host_RunFilePrompted(void *ctx, promptbutton_t button)\n{\n\thrf_t *f = ctx;\n\tswitch(button)\n\t{\n\tcase PROMPT_YES:\n\t\tf->flags |= HRF_OVERWRITE;\n\t\tbreak;\n\tcase PROMPT_NO:\n\t\tf->flags |= HRF_NOOVERWRITE;\n\t\tbreak;\n\tdefault:\n\t\tf->flags |= HRF_ABORT;\n\t\tbreak;\n\t}\n\tHost_DoRunFile(f);\n}\n\n#ifdef WEBCLIENT\nstatic qboolean isurl(char *url)\n{\n#ifdef FTE_TARGET_WEB\n\treturn true;\t//assume EVERYTHING is a url, because the local filesystem is pointless.\n#endif\n\treturn /*!strncmp(url, \"data:\", 5) || */!strncmp(url, \"http://\", 7) || !strncmp(url, \"https://\", 8);\n}\n#endif\n\nqboolean FS_FixupGamedirForExternalFile(char *input, char *filename, size_t fnamelen);\nvoid CL_PlayDemoFile(vfsfile_t *f, char *demoname, qboolean issyspath);\n\nvoid Host_DoRunFile(hrf_t *f)\n{\n\tchar qname[MAX_QPATH];\n\tchar displayname[MAX_QPATH];\n\tchar loadcommand[MAX_OSPATH];\n\tqboolean isnew = false;\n\tqboolean haschanged = false;\n\tenum fs_relative qroot = FS_GAME;\n\n\tif (f->flags & HRF_WAITING)\n\t{\n\t\tf->flags &= ~HRF_WAITING;\n\t\twaitingformanifest--;\n\t}\n\t\n\tif (f->flags & HRF_ABORT)\n\t{\ndone:\n\t\tif (f->flags & HRF_WAITING)\n\t\t\twaitingformanifest--;\n\n\t\tif (f->packageinfo)\n\t\t\tZ_Free(f->packageinfo);\n\t\tif (f->srcfile)\n\t\t\tVFS_CLOSE(f->srcfile);\n\t\tif (f->dstfile)\n\t\t\tVFS_CLOSE(f->dstfile);\n\t\tZ_Free(f);\n\t\treturn;\n\t}\n\n#ifdef WEBCLIENT\n\tif (isurl(f->fname) && !f->srcfile)\n\t{\n\t\tif (!(f->flags & HRF_OPENED))\n\t\t{\n\t\t\tstruct dl_download *dl;\n\t\t\tf->flags |= HRF_OPENED;\n\t\t\tdl = HTTP_CL_Get(f->fname, NULL, Host_RunFileDownloaded);\n\t\t\tif (dl)\n\t\t\t{\n\t\t\t\tf->flags |= HRF_DOWNLOADED;\n\t\t\t\tdl->notifystarted = Host_BeginFileDownload;\n\t\t\t\tdl->user_ctx = f;\n\n\t\t\t\tif (!(f->flags & HRF_WAITING))\n\t\t\t\t{\n\t\t\t\t\tf->flags |= HRF_WAITING;\n\t\t\t\t\twaitingformanifest++;\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n#endif\n\n\tif (!(f->flags & HRF_FILETYPES))\n\t{\n\t\tf->flags |= Host_GuessFileType(NULL, f->fname);\n\t\t\n\t\t//if we still don't know what it is, give up.\n\t\tif (!(f->flags & HRF_FILETYPES))\n\t\t{\n\t\t\tCon_Printf(\"Host_DoRunFile: unknown filetype for \\\"%s\\\"\\n\", f->fname);\n\t\t\tgoto done;\n\t\t}\n\n\t\tif (f->flags & HRF_MANIFEST)\n\t\t{\n\t\t\tif (!(f->flags & HRF_WAITING))\n\t\t\t{\n\t\t\t\tf->flags |= HRF_WAITING;\n\t\t\t\twaitingformanifest++;\n\t\t\t}\n\t\t}\n\t}\n\n\tif (f->flags & HRF_DEMO)\n\t{\n\t\tif (f->srcfile)\n\t\t{\n\t\t\tVFS_SEEK(f->srcfile, 0);\n#ifdef AVAIL_GZDEC\n\t\t\tf->srcfile = FS_DecompressGZip(f->srcfile, NULL);\n#endif\n\n\t\t\tif (f->flags & HRF_DEMO_QWD)\n\t\t\t\tCL_PlayDemoStream(f->srcfile, f->fname, true, DPB_QUAKEWORLD, 0, 0);\n#ifdef Q2CLIENT\n\t\t\telse if (f->flags & HRF_DEMO_DM2)\n\t\t\t\tCL_PlayDemoStream(f->srcfile, f->fname, true, DPB_QUAKE2, 0, 0);\n#endif\n#ifdef NQPROT\n\t\t\telse if (f->flags & HRF_DEMO_DEM)\n\t\t\t\tCL_PlayDemoFile(f->srcfile, f->fname, true);\t//should be able to handle the cd-track header.\n#endif\n\t\t\telse //if (f->flags & HRF_DEMO_MVD)\n\t\t\t\tCL_PlayDemoStream(f->srcfile, f->fname, true, DPB_MVD, 0, 0);\n\t\t\tf->srcfile = NULL;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t//play directly via system path, no prompts needed\n\t\t\tFS_FixupGamedirForExternalFile(f->fname, loadcommand, sizeof(loadcommand));\n\t\t\tCbuf_AddText(va(\"playdemo \\\"%s\\\"\\n\", loadcommand), RESTRICT_LOCAL);\n\t\t}\n\t\tgoto done;\n\t}\n\telse if (f->flags & HRF_BSP)\n\t{\n\t\tchar shortname[MAX_QPATH];\n\t\tCOM_StripExtension(COM_SkipPath(f->fname), shortname, sizeof(shortname));\n\t\tif (FS_FixupGamedirForExternalFile(f->fname, qname, sizeof(qname)) && !Q_strncasecmp(qname, \"maps/\", 5))\n\t\t{\n\t\t\tCOM_StripExtension(qname+5, loadcommand, sizeof(loadcommand));\n\t\t\tCbuf_AddText(va(\"map \\\"%s\\\"\\n\", loadcommand), RESTRICT_LOCAL);\n\t\t\tgoto done;\n\t\t}\n\n\t\tQ_snprintfz(loadcommand, sizeof(loadcommand), \"map \\\"%s\\\"\\n\", shortname);\n\t\tQ_snprintfz(displayname, sizeof(displayname), \"map: %s\", shortname);\n\t\tQ_snprintfz(qname, sizeof(qname), \"maps/%s.bsp\", shortname);\n\t}\n\telse if (f->flags & HRF_PACKAGE)\n\t{\n\t\tchar *shortname;\n\t\tshortname = COM_SkipPath(f->fname);\n\t\tQ_snprintfz(qname, sizeof(qname), \"%s\", shortname);\n\t\tQ_snprintfz(loadcommand, sizeof(loadcommand), \"fs_restart\\n\");\n\t\tQ_snprintfz(displayname, sizeof(displayname), \"package: %s\", shortname);\n\t}\n\telse if (f->flags & HRF_MANIFEST)\n\t{\n\t\tif (f->flags & HRF_OPENED)\n\t\t{\n\t\t\tif (f->srcfile)\n\t\t\t{\n\t\t\t\tftemanifest_t *man;\n\t\t\t\tint len = VFS_GETLEN(f->srcfile);\n\t\t\t\tint foo;\n\t\t\t\tchar *fdata = BZ_Malloc(len+1);\n\t\t\t\tfoo = VFS_READ(f->srcfile, fdata, len);\n\t\t\t\tfdata[len] = 0;\n\t\t\t\tif (foo != len || !len)\n\t\t\t\t{\n\t\t\t\t\tCon_Printf(\"Host_DoRunFile: unable to read file properly\\n\");\n\t\t\t\t\tBZ_Free(fdata);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\thost_parms.manifest = Z_StrDup(fdata);\n\t\t\t\t\tman = FS_Manifest_ReadMem(NULL, NULL, fdata);\n\t\t\t\t\tif (man)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (!man->updateurl)\n\t\t\t\t\t\t\tman->updateurl = Z_StrDup(f->fname);\n//\t\t\t\t\t\tif (f->flags & HRF_DOWNLOADED)\n\t\t\t\t\t\tman->blockupdate = true;\n\t\t\t\t\t\t//man->security = MANIFEST_SECURITY_DEFAULT;\n\t\t\t\t\t\tBZ_Free(fdata);\n\t\t\t\t\t\tFS_ChangeGame(man, true, true);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tCon_Printf(\"Manifest file %s does not appear valid\\n\", f->fname);\n\t\t\t\t\t\tBZ_Free(fdata);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tgoto done;\n\t\t\t}\n\t\t}\n\t}\n\telse if (f->flags & HRF_MODEL)\n\t{\n\t\tif (!FS_FixupGamedirForExternalFile(f->fname, loadcommand, sizeof(loadcommand)))\n\t\t\tCon_TPrintf(\"%s is not within the current gamedir\\n\", f->fname);\n\t\telse\n\t\t\tCbuf_AddText(va(\"modelviewer \\\"%s\\\"\\n\", loadcommand), RESTRICT_LOCAL);\n\t\tgoto done;\n\t}\n\telse if (f->flags & HRF_ARCHIVE)\n\t{\n\t\tchar cachename[MAX_QPATH];\n\t\tstruct gamepacks packagespaths[2];\n\t\tif (f->srcfile)\n\t\t\tVFS_CLOSE(f->srcfile);\n\t\tf->srcfile = NULL;\n\n\t\tmemset(packagespaths, 0, sizeof(packagespaths));\n\t\tpackagespaths[0].url = f->fname;\n\t\tpackagespaths[0].path = cachename;\n\t\tpackagespaths[0].package = NULL;\n\t\tif (FS_PathURLCache(f->fname, cachename, sizeof(cachename)))\n\t\t{\n\t\t\tCOM_Gamedir(\"\", packagespaths);\n\t\t}\n\t\tgoto done;\n\t}\n\telse if (f->flags & HRF_CONFIG)\n\t{\n\t\tif (!(f->flags & HRF_ACTION))\n\t\t{\n\t\t\tKey_Dest_Remove(kdm_console);\n\t\t\tMenu_Prompt(Host_RunFilePrompted, f, va(localtext(\"Exec %s?\\n\"), COM_SkipPath(f->fname)), \"Yes\", NULL, \"Cancel\", true);\n\t\t\treturn;\n\t\t}\n\t\tif (f->flags & HRF_OPENED)\n\t\t{\n\t\t\tsize_t len = VFS_GETLEN(f->srcfile);\n\t\t\tchar *fdata = BZ_Malloc(len+2);\n\t\t\tif (fdata)\n\t\t\t{\n\t\t\t\tVFS_READ(f->srcfile, fdata, len);\n\t\t\t\tfdata[len++] = '\\n';\n\t\t\t\tfdata[len] = 0;\n\t\t\t\tCbuf_AddText(fdata, RESTRICT_INSECURE);\n\t\t\t\tBZ_Free(fdata);\n\t\t\t}\n\t\t\tgoto done;\n\t\t}\n\t}\n\telse if (!(f->flags & HRF_QTVINFO))\n\t{\n\t\tCon_Printf(\"Host_DoRunFile: filetype not handled\\n\");\n\t\tgoto done;\n\t}\n\n\t//at this point we need the file to have been opened.\n\tif (!(f->flags & HRF_OPENED))\n\t{\n\t\tf->flags |= HRF_OPENED;\n\t\tif (!f->srcfile)\n\t\t{\n#ifdef WEBCLIENT\n\t\t\tif (isurl(f->fname))\n\t\t\t{\n\t\t\t\tstruct dl_download *dl = HTTP_CL_Get(f->fname, NULL, Host_RunFileDownloaded);\n\t\t\t\tif (dl)\n\t\t\t\t{\n\t\t\t\t\tdl->notifystarted = Host_BeginFileDownload;\n\t\t\t\t\tdl->user_ctx = f;\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n#endif\n\t\t\tf->srcfile = VFSOS_Open(f->fname, \"rb\");\t//input file is a system path, or something.\n\t\t}\n\t}\n\n\tif (!f->srcfile)\n\t{\n\t\tCon_TPrintf(\"Unable to open %s\\n\", f->fname);\n\t\tgoto done;\n\t}\n\n\tif (f->flags & HRF_PACKAGE)\n\t{\n#ifdef PACKAGEMANAGER\n\t\tif (f->packageinfo)\n\t\t\tZ_Free(f->packageinfo);\t//sucks that we have to do this again, to recompute the proper qname+qroot\n\t\tQ_strncpyz(qname, COM_SkipPath(f->fname), sizeof(qname));\n\t\tf->packageinfo = PM_GeneratePackageFromMeta(f->srcfile, qname,sizeof(qname), &qroot);\n\t\tif (!f->packageinfo)\n\t\t\tgoto done;\n#endif\n\t}\n\telse if (f->flags & HRF_MANIFEST)\n\t{\n\t\tHost_DoRunFile(f);\n\t\treturn;\n\t}\n\telse if (f->flags & HRF_QTVINFO)\n\t{\n\t\t//pass the file object to the qtv code instead of trying to install it.\n\t\tCL_ParseQTVDescriptor(f->srcfile, f->fname);\n\t\tf->srcfile = NULL;\n\n\t\tgoto done;\n\t}\n\n\tVFS_SEEK(f->srcfile, 0);\n\n\tif (f->flags & HRF_OVERWRITE)\n\t\t;//haschanged = isnew = true;\n\telse\n\t{\n\t\tf->dstfile = FS_OpenVFS(qname, \"rb\", (qroot==FS_GAMEONLY)?FS_GAME:qroot);\n\t\tif (f->dstfile)\n\t\t{\n\t\t\t//do a real diff.\n\t\t\tif (f->srcfile->seekstyle == SS_UNSEEKABLE || VFS_GETLEN(f->srcfile) != VFS_GETLEN(f->dstfile))\n\t\t\t{\n\t\t\t\t//if we can't seek, or the sizes differ, just assume that the file is modified.\n\t\t\t\thaschanged = true;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tint len = VFS_GETLEN(f->srcfile);\n\t\t\t\tchar sbuf[8192], dbuf[8192];\n\t\t\t\tif (len > sizeof(sbuf))\n\t\t\t\t\tlen = sizeof(sbuf);\n\t\t\t\tVFS_READ(f->srcfile, sbuf, len);\n\t\t\t\tVFS_READ(f->dstfile, dbuf, len);\n\t\t\t\thaschanged = memcmp(sbuf, dbuf, len);\n\t\t\t\tVFS_SEEK(f->srcfile, 0);\n\t\t\t}\n\t\t\tVFS_CLOSE(f->dstfile);\n\t\t\tf->dstfile = NULL;\n\t\t}\n\t\telse\n\t\t\tisnew = true;\n\t}\n\n\tif (!(f->flags & HRF_ACTION))\n\t{\n\t\tKey_Dest_Remove(kdm_console);\n\t\tif (haschanged)\n\t\t{\n\t\t\tMenu_Prompt(Host_RunFilePrompted, f, va(localtext(\"File already exists.\\nWhat would you like to do?\\n%s\\n\"), displayname), \"Overwrite\", \"Run old\", \"Cancel\", true);\n\t\t\treturn;\n\t\t}\n\t\telse if (isnew)\n\t\t{\n\t\t\tif (f->packageinfo && strstr(f->packageinfo, \"\\nguessed\"))\n\t\t\t\tMenu_Prompt(Host_RunFilePrompted, f, va(localtext(\"File appears new.\\nWould you like to install\\n%s\\n\"CON_ERROR\"File contains no metadata so will be installed to\\n%s\"), displayname, qname), \"Install!\", \"\", \"Cancel\", true);\n\t\t\telse\n\t\t\t\tMenu_Prompt(Host_RunFilePrompted, f, va(localtext(\"File appears new.\\nWould you like to install\\n%s\\n\"), displayname), \"Install!\", \"\", \"Cancel\", true);\n\t\t\treturn;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tMenu_Prompt(NULL, NULL, va(localtext(\"File is already installed\\n%s\\n\"), displayname), NULL, NULL, \"Cancel\", true);\n\t\t\tf->flags |= HRF_ABORT;\n\t\t}\n\t}\n\telse if (f->flags & HRF_OVERWRITE)\n\t{\n\t\tchar buffer[65536];\n\t\tint len;\n\t\tf->dstfile = FS_OpenVFS(qname, (f->flags & HRF_PACKAGE)?\"wbp\":\"wb\", qroot);\n\t\tif (f->dstfile)\n\t\t{\n#ifdef FTE_TARGET_WEB\n\t\t\tVFS_SEEK(f->dstfile, VFS_GETLEN(f->srcfile));\n\t\t\tVFS_WRITE(f->dstfile, \"zomg\", 0);\t//hack to ensure the file is there, avoiding excessive copies.\n\t\t\tVFS_SEEK(f->dstfile, 0);\n#endif\n\t\t\twhile(1)\n\t\t\t{\n\t\t\t\tlen = VFS_READ(f->srcfile, buffer, sizeof(buffer));\n\t\t\t\tif (len <= 0)\n\t\t\t\t\tbreak;\n\t\t\t\tVFS_WRITE(f->dstfile, buffer, len);\n\t\t\t}\n\n\t\t\tVFS_CLOSE(f->dstfile);\n\t\t\tf->dstfile = NULL;\n\t\t}\n\n#ifdef PACKAGEMANAGER\n\t\tif (f->flags & HRF_PACKAGE)\n\t\t\tPM_FileInstalled(qname, qroot, f->packageinfo, true);\n#endif\n\n\t\tif (!strcmp(loadcommand, \"fs_restart\\n\"))\n\t\t\tFS_ReloadPackFiles();\n\t\telse\n\t\t\tCbuf_AddText(loadcommand, RESTRICT_LOCAL);\n\t}\n\n\tgoto done;\n}\n\n//only valid once the host has been initialised, as it needs a working filesystem.\n//if file is specified, takes full ownership of said file, including destruction.\nqboolean Host_RunFile(const char *fname, int nlen, vfsfile_t *file)\n{\n\thrf_t *f;\n#if defined(FTE_TARGET_WEB)\n\tif (nlen >= 8 && !strncmp(fname, \"file:///\", 8))\n\t{\t//just here so we don't get confused by the arbitrary scheme check below.\n\t}\n#else\n\t//file urls need special handling, if only for percent-encoding.\n\tchar utf8[MAX_OSPATH*3];\n\tif (nlen >= 5 && !strncmp(fname, \"file:\", 5))\n\t{\n\t\tif (!Sys_ResolveFileURL(fname, nlen, utf8, sizeof(utf8)))\n\t\t{\n\t\t\tCon_Printf(\"Cannot resolve file url\\n\");\n\t\t\tif(file)\n\t\t\t\tVFS_CLOSE(file);\n\t\t\treturn false;\n\t\t}\n\t\tfname = utf8;\n\t\tnlen = strlen(fname);\n\t}\n#endif\n\telse if((nlen >= 7 && !strncmp(fname, \"http://\", 7)) ||\n\t\t\t(nlen >= 8 && !strncmp(fname, \"https://\", 8)))\n\t\t;\t//don't interpret these as our custom uri schemes\n\telse\n\t{\n\t\tconst char *schemeend = strstr(fname, \"://\");\n\n\t\tif (schemeend)\n\t\t{\t//this is also implemented by ezquake, so be careful here...\n\t\t\t//examples:\n\t\t\t//\t\"quake2://broker:port\"\n\t\t\t//\t\"quake2:rtc://broker:port/game\"\n\t\t\t//\t\"qw://[stream@]host[:port]/COMMAND\" join, spectate, qtvplay\n\t\t\t//we'll chop off any non-auth prefix, its just so we can handle multiple protocols via a single uri scheme.\n\t\t\tchar *t, *cmd, *args;\n\t\t\tconst char *url;\n\t\t\tchar buffer[8192];\n\t\t\tconst char *schemestart = strchr(fname, ':');\n\t\t\tint schemelen, urilen;\n\n\t\t\t//if its one of our explicit protocols then use the url as-is\n\t\t\tconst char *netschemes[] = {\"udp\", \"udp4\", \"udp6\", \"ipx\", \"tcp\", \"tcp4\", \"tcp6\", \"spx\", \"ws\", \"wss\", \"tls\", \"dtls\", \"ice\", \"rtc\", \"ices\", \"rtcs\", \"irc\", \"udg\", \"unix\"};\n\t\t\tint i;\n\t\t\tsize_t slen;\n\n\t\t\tif (!schemestart || schemestart==schemeend)\n\t\t\t\tschemestart = fname;\n\t\t\telse\n\t\t\t\tschemestart++;\n\t\t\tschemelen = schemeend-schemestart;\n\t\t\turilen = nlen-(schemestart-fname);\n\n\t\t\tfor (i = 0; i < countof(netschemes); i++)\n\t\t\t{\n\t\t\t\tslen = strlen(netschemes[i]);\n\t\t\t\tif (schemelen == slen && !strncmp(schemestart, netschemes[i], slen))\n\t\t\t\t{\n\t\t\t\t\tchar quoted[8192];\n\t\t\t\t\tchar *t = Z_Malloc(urilen+1);\n\t\t\t\t\tmemcpy(t, schemestart, urilen);\n\t\t\t\t\tt[urilen] = 0;\n\n\t\t\t\t\tCbuf_AddText(va(\"connect %s\\n\", COM_QuotedString(t, quoted, sizeof(quoted), false)), RESTRICT_LOCAL);\n\n\t\t\t\t\tif(file)\n\t\t\t\t\t\tVFS_CLOSE(file);\n\t\t\t\t\tZ_Free(t);\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tschemelen++;\n\t\t\tif (!strncmp(schemestart+schemelen, \"//\", 2))\n\t\t\t\tschemelen+=2;\n\n\t\t\tt = Z_Malloc(urilen+1);\n\t\t\tmemcpy(t, schemestart, urilen);\n\t\t\tt[urilen] = 0;\n\t\t\turl = t+schemelen;\n\n\t\t\t*buffer = 0;\n\t\t\tfor (args = t+schemelen; *args; args++)\n\t\t\t{\n\t\t\t\tif (*args == '?')\n\t\t\t\t{\n\t\t\t\t\t*args++ = 0;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tfor (cmd = t+schemelen; *cmd; cmd++)\n\t\t\t{\n\t\t\t\tif (*cmd == '/')\n\t\t\t\t{\n\t\t\t\t\t*cmd++ = 0;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t//quote the url safely.\n\t\t\turl = COM_QuotedString(url, buffer, sizeof(buffer), false);\n\n\t\t\t//now figure out what the command actually was\n\t\t\tif (!Q_strcasecmp(cmd, \"join\"))\n\t\t\t\tCbuf_AddText(va(\"join %s\\n\", url), RESTRICT_LOCAL);\n\t\t\telse if (!Q_strcasecmp(cmd, \"spectate\") || !strcmp(cmd, \"observe\"))\n\t\t\t\tCbuf_AddText(va(\"observe %s\\n\", url), RESTRICT_LOCAL);\n\t\t\telse if (!Q_strcasecmp(cmd, \"qtvplay\"))\n\t\t\t\tCbuf_AddText(va(\"qtvplay %s\\n\", url), RESTRICT_LOCAL);\n\t\t\telse if (!*cmd || !Q_strcasecmp(cmd, \"connect\"))\n\t\t\t\tCbuf_AddText(va(\"connect %s\\n\", url), RESTRICT_LOCAL);\n\t\t\telse\n\t\t\t\tCon_Printf(\"Unknown url command: %s\\n\", cmd);\n\n\t\t\tif(file)\n\t\t\t\tVFS_CLOSE(file);\n\t\t\tZ_Free(t);\n\t\t\treturn true;\n\t\t}\n\t}\n\n\tf = Z_Malloc(sizeof(*f) + nlen);\n\tmemcpy(f->fname, fname, nlen);\n\tf->fname[nlen] = 0;\n\tf->srcfile = file;\n\tif (file)\n\t\tf->flags |= HRF_OPENED;\n\n\t{\n\t\tchar dpath[MAX_OSPATH];\n\t\tFS_DisplayPath(f->fname, FS_SYSTEM, dpath,sizeof(dpath));\n\t\tCon_TPrintf(\"Opening external file: %s\\n\", dpath);\n\t}\n\n\tHost_DoRunFile(f);\n\treturn true;\n}\n\n/*\n==================\nHost_Frame\n\nRuns all active servers\n==================\n*/\nextern cvar_t cl_netfps;\n\nvoid CL_StartCinematicOrMenu(void);\nint\t\tnopacketcount;\nvoid SNDDMA_SetUnderWater(qboolean underwater);\ndouble Host_Frame (double time)\n{\n\tstatic double\t\ttime0 = 0;\n\tstatic double\t\ttime1 = 0;\n\tstatic double\t\ttime2 = 0;\n\tstatic double\t\ttime3 = 0;\n\tint\t\t\tpass0, pass1, pass2, pass3, i;\n//\tfloat fps;\n\tdouble newrealtime, spare;\n\tfloat maxfps;\n\tqboolean maxfpsignoreserver;\n\tqboolean idle;\n\tstatic qboolean hadwork;\n\tunsigned int vrflags;\n\tqboolean mustrenderbeforeread;\n\n\tRSpeedLocals();\n\n\tif (setjmp (host_abort) )\n\t{\n\t\treturn 0;\t\t\t// something bad happened, or the server disconnected\n\t}\n\n\tvrflags = vid.vr?vid.vr->SyncFrame(&time):0;\t\t\t\t//fiddle with frame timings\n\tnewrealtime = Media_TweekCaptureFrameTime(realtime, time);\t//fiddle with time some more\n\n\ttime = newrealtime - realtime;\n\trealtime = newrealtime;\n\n\tif (oldrealtime > realtime)\n\t\toldrealtime = realtime;\n\n\tif (cl.gamespeed<0.1)\n\t\tcl.gamespeed = 1;\n\ttime *= cl.gamespeed;\n\n#ifdef WEBCLIENT\n//\tFTP_ClientThink();\n\tHTTP_CL_Think(NULL, NULL);\n#endif\n\n\tif (r_blockvidrestart)\n\t{\n\t\tCbuf_Execute();\n\t\tif (waitingformanifest)\n\t\t{\n\t\t\tCOM_MainThreadWork();\n\t\t\treturn 0.1;\n\t\t}\n\t\tHost_FinishLoading();\n\t\treturn 0;\n\t}\n\tif (startuppending)\n\t\tCL_StartCinematicOrMenu();\n\n\tif (cl.paused)\n\t\tcl.gametimemark += time;\n\n\t//if we're at a menu/console/thing\n//\tidle = !Key_Dest_Has_Higher(kdm_menu);\n//\tidle = ((cls.state == ca_disconnected) || cl.paused) && idle;\t//idle if we're disconnected/paused and not at a menu\n\tidle = !vid.activeapp; //always idle when tabbed out\n\n\t//read packets early and always, so we don't have stuff waiting for reception quite so often.\n\t//should smooth out a few things, and increase download speeds.\n\tif (!cls.timedemo)\n\t\tCL_ReadPackets ();\n\n\tif (idle && cl_idlefps.value > 0 && !(vrflags&VRF_OVERRIDEFRAMETIME))\n\t{\n\t\tdouble idlesec = 1.0 / cl_idlefps.value;\n\t\tif (idlesec > 0.1)\n\t\t\tidlesec = 0.1; // limit to at least 10 fps\n#ifdef HAVE_MEDIA_ENCODER\n\t\tif (Media_Capturing())\n\t\t\tidlesec = 0;\n#endif\n\t\tif ((realtime - oldrealtime) < idlesec)\n\t\t{\n#ifdef HAVE_SERVER\n\t\t\tif (sv.state)\n\t\t\t{\n\t\t\t\tRSpeedRemark();\n\t\t\t\tSV_Frame();\n\t\t\t\tRSpeedEnd(RSPEED_SERVER);\n\t\t\t}\n\t\t\telse\n\t\t\t\tMSV_PollSlaves();\n#endif\n\t\t\twhile(COM_DoWork(0, false))\n\t\t\t\t;\n\t\t\treturn idlesec - (realtime - oldrealtime);\n\t\t}\n\t}\n\n#ifdef PLUGINS\n\tPlug_Tick();\n#endif\n\tNET_Tick();\n\n/*\n\tif (cl_maxfps.value)\n\t\tfps = cl_maxfps.value;//max(30.0, min(cl_maxfps.value, 72.0));\n\telse\n\t\tfps = max(30.0, min(rate.value/80.0, 72.0));\n\n\tif (!cls.timedemo && realtime - oldrealtime < 1.0/fps)\n\t\treturn;\t\t\t// framerate is too high\n\n\t*/\n#ifdef RUNTIMELIGHTING\n\tRelightThink();\t//think even on idle (which means small walls and a fast cpu can get more surfaces done.\n#endif\n\n#ifdef HAVE_SERVER\n\tif (sv.state && cls.state != ca_active)\n\t{\n\t\tmaxfpsignoreserver = false;\n\t\tmaxfps = 0;//cl_maxfps.ival;\n\t}\n\telse\n#endif\n\t\tif ((cl_netfps.value>0 || cls.demoplayback || runningindepphys))\n\t{\t//limit the fps freely, and expect the netfps to cope.\n\t\tmaxfpsignoreserver = true;\n\t\tmaxfps = cl_maxfps.ival;\n\t}\n\telse\n\t{\n\t\tmaxfpsignoreserver = false;\n\t\tmaxfps = (cl_maxfps.ival>0||cls.protocol!=CP_QUAKEWORLD)?cl_maxfps.value:((cl_netfps.value>0)?cl_netfps.value:cls.maxfps);\n\t\t/*gets buggy at times longer than 250ms (and 0/negative, obviously)*/\n\t\tif (maxfps < 4)\n\t\t\tmaxfps = 4;\n\t}\n\n\tif (vid.isminimized && (maxfps <= 0 || maxfps > 10))\n\t\tmaxfps = 10;\n\n\tif (maxfps > 0\n#ifdef HAVE_MEDIA_ENCODER\n\t\t&& Media_Capturing() != 2\n#endif\n\t\t&& !(vrflags&VRF_OVERRIDEFRAMETIME))\n\t{\n\t\tspare = CL_FilterTime((realtime - oldrealtime)*1000, maxfps, 1.5, maxfpsignoreserver);\n\t\tif (!spare)\n\t\t{\n\t\t\twhile(COM_DoWork(0, false))\n\t\t\t\t;\n\t\t\treturn (cl_yieldcpu.ival || vid.isminimized || idle)? (1.0 / maxfps - (realtime - oldrealtime)) : 0;\n\t\t}\n\t\tif (spare > cl_maxfps_slop.ival)\n\t\t\tspare = cl_maxfps_slop.ival;\n\t\tspare /= 1000;\n\t\tif (spare > 0.5/maxfps)\t//don't delay the next by\n\t\t\tspare = 0.5/maxfps;\n\t\tif (spare < 0 || cls.state < ca_onserver)\n\t\t\tspare = 0;\n\t}\n\telse\n\t\tspare = 0;\n\thost_frametime = (realtime-spare - oldrealtime)*cl.gamespeed;\n\toldrealtime = realtime-spare;\n\n\tif (host_speeds.ival)\n\t\ttime0 = Sys_DoubleTime ();\t//end-of-idle\n\n\tif (cls.demoplayback && !cl.stillloading)\n\t{\n\t\tqboolean haswork = cl.sendprespawn || COM_HasWork();\n\t\tif (!hadwork && !haswork)\n\t\t\tCL_ProgressDemoTime();\n\t\thadwork = haswork;\n\t}\n\tcl.stillloading = cl.sendprespawn\n#ifdef LOADERTHREAD\n\t\t|| (cls.state < ca_active && worker_flush.ival && COM_HasWork())\n#endif\n\t\t;\n\tCOM_MainThreadWork();\n\n//\tif (host_frametime > 0.2)\n//\t\thost_frametime = 0.2;\n\n\t// get new key events\n\tSys_SendKeyEvents ();\t//from windowing system\n\tINS_Move();\t\t\t\t//from things that need special polling\n\n\t// check what we got, and handle any click/button events\n\tIN_Commands ();\n\n\t// process console commands from said click/button events\n\tCbuf_Execute ();\n\n#ifdef HAVE_SERVER\n\tif (isDedicated)\t//someone changed it.\n\t{\n\t\tif (sv.state)\n\t\t{\n\t\t\tfloat ohft = host_frametime;\n\t\t\tRSpeedRemark();\n\t\t\tSV_Frame();\n\t\t\tRSpeedEnd(RSPEED_SERVER);\n\t\t\thost_frametime = ohft;\n\t\t}\n\t\telse\n\t\t\tMSV_PollSlaves();\n\t\treturn 0;\n\t}\n#endif\n\n\tcls.framecount++;\n\n\tRSpeedRemark();\n\n\tCL_UseIndepPhysics(cls.state != ca_disconnected && !!cl_threadedphysics.ival);\t//starts/stops the input frame thread.\n\n\tcl.do_lerp_players = cl_lerp_players.ival || cls.demoplayback==DPB_MVD || (cls.demoplayback && !cl_nolerp.ival && !cls.timedemo);\n\tCL_AllowIndependantSendCmd(false);\n\n\tmustrenderbeforeread = cls.protocol == CP_QUAKE2;\t//FIXME: quake2 MUST render a frame (or a later one) before it can read any acks from the server, otherwise its prediction screws up. I'm too lazy to rewrite that right now.\n//\tif (mustrenderbeforeread)\n\tCL_ReadPackets();\t//this should be redundant.\n\n\tCL_RequestNextDownload();\n\n\t// send intentions now\n\t// resend a connection request if necessary\n\tif (cls.state == ca_disconnected)\n\t{\n\t\tCL_SendCmd (host_frametime, true);\n//\t\tIN_Move(NULL, 0, time);\n\t\tCL_CheckForResend ();\n\n#ifdef VOICECHAT\n\t\tS_Voip_Transmit(0, NULL);\n#endif\n\t}\n\telse\n\t{\n\t\tif (connectinfo.trying)\n\t\t\tCL_CheckForResend ();\n\t\tCL_SendCmd (cl.gamespeed?host_frametime/cl.gamespeed:host_frametime, true);\n\n\t\tif (cls.state == ca_onserver && cl.validsequence && cl.worldmodel)\n\t\t{\t// first update is the final signon stage\n\t\t\tif (cls.protocol == CP_NETQUAKE)\n\t\t\t{\n\t\t\t\t//nq can send 'frames' without any entities before we're on the server, leading to short periods where the local player's position is not known. this is bad. so be more cautious with nq. this might break csqc.\n\t\t\t\tCL_TransitionEntities();\n\t\t\t\tif (cl.currentpackentities->num_entities || cl.currentpackentities->servertime\n#ifdef CSQC_DAT\n\t\t\t\t\t|| (cls.fteprotocolextensions & PEXT_CSQC)\n#endif\n\t\t\t\t\t)\n\t\t\t\t\tCL_MakeActive(\"Quake\");\n\t\t\t}\n\t\t\telse\n\t\t\t\tCL_MakeActive(\"QuakeWorld\");\n\t\t}\n\t}\n\tCL_AllowIndependantSendCmd(true);\n\n\tRSpeedEnd(RSPEED_PROTOCOL);\n\n#ifdef HAVE_SERVER\n\tif (sv.state)\n\t{\n\t\tfloat ohft = host_frametime;\n\t\tRSpeedRemark();\n\t\tSV_Frame();\n\t\tRSpeedEnd(RSPEED_SERVER);\n\t\thost_frametime = ohft;\n//\t\tif (cls.protocol != CP_QUAKE3 && cls.protocol != CP_QUAKE2)\n//\t\t\tCL_ReadPackets ();\t//q3's cgame cannot cope with input commands with the same time as the most recent snapshot value\n\t}\n\telse\n\t\tMSV_PollSlaves();\n#endif\n\n\t// fetch results from server... now that we've run it.\n\tif (!mustrenderbeforeread)\n\t{\n\t\tCL_AllowIndependantSendCmd(false);\n\t\tCL_ReadPackets ();\n\t\tCL_AllowIndependantSendCmd(true);\n\t}\n\n\tCL_CalcClientTime();\n\n\t// update video\n\tif (host_speeds.ival)\n\t\ttime1 = Sys_DoubleTime ();\n\n\tif (!VID_MayRefresh || VID_MayRefresh())\n\t{\n\t\tif (R2D_Flush)\n\t\t{\n\t\t\tR2D_Flush();\n\t\t\tCon_Printf(\"R2D_Flush was set outside of SCR_UpdateScreen\\n\");\n\t\t}\n\n\t\tcl.mouseplayerview = NULL;\n\t\tcl.mousenewtrackplayer = -1;\n\t\tfor (i = 0; i < MAX_SPLITS; i++)\n\t\t{\n\t\t\tcl.playerview[i].audio.defaulted = true;\n\t\t\tcl.playerview[i].audio.entnum = cl.playerview[i].viewentity;\n\t\t\tVectorClear(cl.playerview[i].audio.origin);\n\t\t\tVectorSet(cl.playerview[i].audio.forward, 1, 0, 0);\n\t\t\tVectorSet(cl.playerview[i].audio.right, 0, 1, 0);\n\t\t\tVectorSet(cl.playerview[i].audio.up, 0, 0, 1);\n\t\t\tcl.playerview[i].audio.reverbtype = 0;\n\t\t\tVectorClear(cl.playerview[i].audio.velocity);\n\t\t}\n\t\tif (cls.state && r_worldentity.model && r_worldentity.model->loadstate == MLS_NOTLOADED)\n\t\t\tMod_LoadModel(cl.worldmodel, MLV_WARNSYNC);\n\n\t\tif (SCR_UpdateScreen && !vid.isminimized)\n\t\t{\n\t\t\textern cvar_t r_stereo_method;\n\t\t\tr_refdef.warndraw = false;\n\t\t\tr_refdef.stereomethod = r_stereo_method.ival;\n\t\t\t{\n\t\t\t\tRSpeedMark();\n\t\t\t\tvid.ime_allow = false;\n\t\t\t\tvrui.enabled |= cl_vrui_force.ival || (vrflags&VRF_UIACTIVE);\n\t\t\t\tif (SCR_UpdateScreen())\n\t\t\t\t\tfps_count += 1+max(0, cl_fakeframes.ival);\n\t\t\t\tif (R2D_Flush)\n\t\t\t\t\tSys_Error(\"update didn't flush 2d cache\\n\");\n\t\t\t\tRSpeedEnd(RSPEED_TOTALREFRESH);\n\t\t\t}\n\t\t\tr_refdef.warndraw = true;\n\t\t}\n\t\telse\n\t\t\tfps_count++;\n\n\t\tsh_config.showbatches = false;\n\t}\n\n\tif (host_speeds.ival)\n\t\ttime2 = Sys_DoubleTime ();\n\n\t// update audio\n\tfor (i = 0 ; i < MAX_SPLITS; i++)\n\t{\n\t\tplayerview_t *pv = &cl.playerview[cl.splitclients?i % cl.splitclients:0];\n\t\tS_UpdateListener (i, pv->audio.entnum, pv->audio.origin, pv->audio.forward, pv->audio.right, pv->audio.up, pv->audio.reverbtype, pv->audio.velocity);\n\t}\n\n\tS_Update ();\n\n\tCDAudio_Update();\n\n\tif (host_speeds.ival)\n\t{\n\t\tpass0 = (time0 - time3)*1000000;\n\t\ttime3 = Sys_DoubleTime ();\n\t\tpass1 = (time1 - time0)*1000000;\n\t\tpass2 = (time2 - time1)*1000000;\n\t\tpass3 = (time3 - time2)*1000000;\n\t\tCon_Printf (\"%4i tot %4i idle %4i server %4i gfx %4i snd\\n\",\n\t\t\t\t\tpass0+pass1+pass2+pass3, pass0, pass1, pass2, pass3);\n\t}\n\n\n//\tIN_Commands ();\n\n\t// process console commands\n//\tCbuf_Execute ();\n\n\n\tCL_QTVPoll();\n\n#ifdef QUAKESTATS\n\tTP_UpdateAutoStatus();\n#endif\n\n\thost_framecount++;\n\tcl.lasttime = cl.time;\n\treturn 0;\n}\n\nstatic void simple_crypt(char *buf, int len)\n{\n\tif (!(*buf & 128))\n\t\treturn;\n\twhile (len--)\n\t\t*buf++ ^= 0xff;\n}\n\nvoid Host_FixupModelNames(void)\n{\n\tsimple_crypt(emodel_name, sizeof(emodel_name) - 1);\n\tsimple_crypt(pmodel_name, sizeof(pmodel_name) - 1);\n\tsimple_crypt(prespawn_name,  sizeof(prespawn_name)  - 1);\n\tsimple_crypt(modellist_name, sizeof(modellist_name) - 1);\n\tsimple_crypt(soundlist_name, sizeof(soundlist_name) - 1);\n}\n\n\n\n#ifdef Q3CLIENT\nvoid CL_ReadCDKey(void)\n{\t//q3 cdkey\n\t//you don't need one, just use a server without sv_strictauth set to 0.\n\tchar *buffer;\n\tbuffer = COM_LoadTempFile(\"q3key\", FSLF_IGNOREPURE, NULL);\n\tif (buffer)\t//a cdkey is meant to be 16 chars\n\t{\n\t\tchar *chr;\n\t\tfor (chr = buffer; *chr; chr++)\n\t\t{\n\t\t\tif (*(unsigned char*)chr < ' ')\n\t\t\t{\n\t\t\t\t*chr = '\\0';\t//don't get more than one line.\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tCvar_Get(\"cl_cdkey\", buffer, CVAR_MAPLATCH|CVAR_NOUNSAFEEXPAND, \"Q3 compatability\");\n\t}\n}\n#endif\n\n//============================================================================\n\nvoid CL_StartCinematicOrMenu(void)\n{\n\tCOM_MainThreadWork();\n\n\tif (com_installer && FS_DownloadingPackage())\n\t{\n\t\tstartuppending = true;\n\t\treturn;\n\t}\n\tif (cls.download)\n\t{\n\t\tstartuppending = true;\n\t\treturn;\n\t}\n\tCmd_StuffCmds();\n\tif (startuppending)\n\t{\n\t\tif (startuppending == 2)\t//installer finished.\n\t\t\tCbuf_AddText(\"\\nfs_restart\\nvid_restart\\n\", RESTRICT_LOCAL);\n\t\tstartuppending = false;\n\t\tKey_Dest_Remove(kdm_console);\t//make sure console doesn't stay up weirdly.\n\t}\n\n\tCbuf_AddText(\"menu_restart\\n\", RESTRICT_LOCAL);\n\n\tCon_TPrintf (\"^Ue080^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081 %s %sInitialized ^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue082\\n\", *fs_gamename.string?fs_gamename.string:\"Nothing\", com_installer?\"Installer \":\"\");\n\n\t//there might be some console command or somesuch waiting for the renderer to begin (demos or map command or whatever all need model support).\n\trealtime+=1;\n\tCbuf_Execute ();\t//server may have been waiting for the renderer\n\n\tCon_ClearNotify();\n\n\tif (com_installer)\n\t{\n\t\tcom_installer = false;\n#if 0\n\t\tKey_Dest_Remove(kdm_console);\t//make sure console doesn't stay up weirdly.\n\t\tM_Menu_Installer();\n\t\treturn;\n#endif\n\t}\n\n\tif (!sv_state && !cls.demoinfile && !cls.state && !*cls.servername)\n\t{\t//this is so default.cfg can define startup commands to exec if the engine starts up with no +connect / +map etc arg\n\t\tTP_ExecTrigger(\"f_startup\", true);\n\t\tCbuf_Execute ();\n\t}\n\n\t//and any startup cinematics\n#ifdef HAVE_MEDIA_DECODER\n\tif (!sv_state && !cls.demoinfile && !cls.state && !*cls.servername)\n\t{\n\t\tint ol_depth;\n\t\tint idcin_depth;\n\t\tint idroq_depth;\n\n\t\tidcin_depth = COM_FDepthFile(\"video/idlog.cin\", true);\t//q2\n\t\tidroq_depth = COM_FDepthFile(\"video/idlogo.roq\", true);\t//q3\n\t\tol_depth = COM_FDepthFile(\"video/openinglogos.roq\", true);\t//jk2\n\n\t\tif (ol_depth != FDEPTH_MISSING && (ol_depth <= idroq_depth || ol_depth <= idcin_depth))\n\t\t\tMedia_PlayFilm(\"video/openinglogos.roq\", true);\n\t\telse if (idroq_depth != FDEPTH_MISSING && idroq_depth <= idcin_depth)\n\t\t\tMedia_PlayFilm(\"video/idlogo.roq\", true);\n\t\telse if (idcin_depth != FDEPTH_MISSING)\n\t\t\tMedia_PlayFilm(\"video/idlog.cin\", true);\n\n#ifdef HAVE_LEGACY\n\t\t//and for fun (blame spirit):\n\t\tif (COM_FCheckExists(\"data/local/video/New_Bliz640x480.bik\"))\n\t\t\tMedia_PlayFilm(\"av:data/local/video/New_Bliz640x480.bik\", true);\n\t\tif (COM_FCheckExists(\"data/local/video/BlizNorth640x480.bik\"))\n\t\t\tMedia_PlayFilm(\"av:data/local/video/BlizNorth640x480.bik\", true);\n\t\tif (COM_FCheckExists(\"data/local/video/eng/d2intro640x292.bik\"))\n\t\t\tMedia_PlayFilm(\"av:data/local/video/eng/d2intro640x292.bik\", true);\n\t\tif (COM_FCheckExists(\"Data/Local/Video/ENG/D2x_Intro_640x292.bik\"))\n\t\t\tMedia_PlayFilm(\"av:Data/Local/Video/ENG/D2x_Intro_640x292.bik\", true);\n#endif\n\t}\n#endif\n\n\tif (!sv_state && !cls.demoinfile && !cls.state && !*cls.servername)\n\t{\n\t\tif (qrenderer > QR_NONE && !Key_Dest_Has(~kdm_game))\n\t\t{\n#ifndef NOBUILTINMENUS\n\t\t\tif (!cls.state && !Key_Dest_Has(~kdm_game) && !*FS_GetGamedir(false))\n\t\t\t\tM_Menu_Mods_f();\n#endif\n\t\t\tif (!cls.state && !Key_Dest_Has(~kdm_game) && cl_demoreel.ival)\n\t\t\t{\n\t\t\t\tcls.demonum = MAX_DEMOS;\n\t\t\t\tCL_NextDemo();\n\t\t\t}\n\t\t\tif (!cls.state && !Key_Dest_Has(~kdm_game))\n\t\t\t\t//if we're (now) meant to be using csqc for menus, make sure that its running.\n\t\t\t\tif (!CSQC_UnconnectedInit())\n\t\t\t\t\tM_ToggleMenu_f();\n\t\t}\n\t\t//Con_ForceActiveNow();\n\t}\n}\n\nvoid CL_ArgumentOverrides(void)\n{\n\tint i;\n\tif (COM_CheckParm (\"-window\") || COM_CheckParm (\"-startwindowed\"))\n\t\tCvar_Set(Cvar_FindVar(\"vid_fullscreen\"), \"0\");\n\tif (COM_CheckParm (\"-fullscreen\"))\n\t\tCvar_Set(Cvar_FindVar(\"vid_fullscreen\"), \"1\");\n\n\tif ((i = COM_CheckParm (\"-width\")))\t//width on it's own also sets height\n\t{\n\t\tCvar_Set(Cvar_FindVar(\"vid_width\"), com_argv[i+1]);\n\t\tCvar_SetValue(Cvar_FindVar(\"vid_height\"), (atoi(com_argv[i+1])/4)*3);\n\t}\n\tif ((i = COM_CheckParm (\"-height\")))\n\t\tCvar_Set(Cvar_FindVar(\"vid_height\"), com_argv[i+1]);\n\n\tif ((i = COM_CheckParm (\"-conwidth\")))\t//width on it's own also sets height\n\t{\n\t\tCvar_Set(Cvar_FindVar(\"vid_conwidth\"), com_argv[i+1]);\n\t\tCvar_SetValue(Cvar_FindVar(\"vid_conheight\"), (atoi(com_argv[i+1])/4)*3);\n\t}\n\tif ((i = COM_CheckParm (\"-conheight\")))\n\t\tCvar_Set(Cvar_FindVar(\"vid_conheight\"), com_argv[i+1]);\n\n\tif ((i = COM_CheckParm (\"-bpp\")))\n\t\tCvar_Set(Cvar_FindVar(\"vid_bpp\"), com_argv[i+1]);\n\n\tif (COM_CheckParm (\"-current\"))\n\t\tCvar_Set(Cvar_FindVar(\"vid_desktopsettings\"), \"1\");\n\n\tif (COM_CheckParm(\"-condebug\"))\n\t\tCvar_Set(Cvar_FindVar(\"log_enable\"), \"1\");\n\n\tif ((i = COM_CheckParm (\"-particles\")))\n\t\tCvar_Set(Cvar_FindVar(\"r_part_maxparticles\"), com_argv[i+1]);\n\n\tif (COM_CheckParm(\"-qmenu\"))\n\t\tCvar_ForceSet(Cvar_FindVar(\"forceqmenu\"), \"1\");\n}\n\n//note that this does NOT include commandline.\nvoid CL_ExecInitialConfigs(char *resetcommand, qboolean fullvidrestart)\n{\n#ifndef QUAKETC\n\tint qrc, hrc;\n#endif\n\tint def;\n\n\tCbuf_Execute ();\t//make sure any pending console commands are done with. mostly, anyway...\n\t\n\tCbuf_AddText(\"unbindall\\nshowpic_removeall\\n\", RESTRICT_LOCAL);\n\tCbuf_AddText(\"alias restart_ents \\\"changelevel . .\\\"\\n\",RESTRICT_LOCAL);\n\tCbuf_AddText(\"alias restart map_restart\\n\",RESTRICT_LOCAL);\n\tCbuf_AddText(\"alias startmap_sp \\\"map start\\\"\\n\", RESTRICT_LOCAL);\n#ifdef QUAKESTATS\n\tCbuf_AddText(\"alias +attack2 +button3\\n\", RESTRICT_LOCAL);\n\tCbuf_AddText(\"alias -attack2 -button3\\n\", RESTRICT_LOCAL);\n#endif\n\tCbuf_AddText(\"cl_warncmd 0\\n\", RESTRICT_LOCAL);\n\tCbuf_AddText(\"cvar_purgedefaults\\n\", RESTRICT_LOCAL);\t//reset cvar defaults to their engine-specified values. the tail end of 'exec default.cfg' will update non-cheat defaults to mod-specified values.\n\tCbuf_AddText(\"cvarreset *\\n\", RESTRICT_LOCAL);\t\t\t//reset all cvars to their current (engine) defaults\n#ifdef HAVE_SERVER\n\tCbuf_AddText(va(\"sv_gamedir \\\"%s\\\"\\n\", FS_GetGamedir(true)), RESTRICT_LOCAL);\n#endif\n\tCbuf_AddText(resetcommand, RESTRICT_LOCAL);\n\tCbuf_AddText(\"\\n\", RESTRICT_LOCAL);\n\tCOM_ParsePlusSets(true);\n\n#ifdef QUAKESTATS\n\tCbuf_AddText(\"register_bestweapon reset\\n\", RESTRICT_LOCAL);\n#endif\n\n\tdef = COM_FDepthFile(\"default.cfg\", true);\t//q2/q3/tc\n#ifdef QUAKETC\n\tCbuf_AddText (\"exec default.cfg\\n\", RESTRICT_LOCAL);\n\tif (COM_FDepthFile (\"config.cfg\", true) <= def)\n\t\tCbuf_AddText (\"exec config.cfg\\n\", RESTRICT_LOCAL);\n\tif (COM_FCheckExists (\"autoexec.cfg\"))\n\t\tCbuf_AddText (\"exec autoexec.cfg\\n\", RESTRICT_LOCAL);\n#else\n\t//who should we imitate?\n\tqrc = COM_FDepthFile(\"quake.rc\", true);\t//q1\n\thrc = COM_FDepthFile(\"hexen.rc\", true);\t//h2\n\n\tif (qrc <= def && qrc <= hrc && qrc!=FDEPTH_MISSING)\n\t{\n\t\tCbuf_AddText (\"exec quake.rc\\n\", RESTRICT_LOCAL);\n\t\tdef = qrc;\n\t}\n\telse if (hrc <= def && hrc!=FDEPTH_MISSING)\n\t{\n\t\tCbuf_AddText (\"exec hexen.rc\\n\", RESTRICT_LOCAL);\n\t\tdef = hrc;\n\t}\n\telse\n\t{\t//they didn't give us an rc file!\n//\t\tint cfg = COM_FDepthFile (\"config.cfg\", true);\n\t\tint q3cfg = COM_FDepthFile (\"q3config.cfg\", true);\n\t//\tCbuf_AddText (\"bind ` toggleconsole\\n\", RESTRICT_LOCAL);\t//in case default.cfg does not exist. :(\n\t\tCbuf_AddText (\"exec default.cfg\\n\", RESTRICT_LOCAL);\n\t\tif (q3cfg <= def && q3cfg!=FDEPTH_MISSING)\n\t\t\tCbuf_AddText (\"exec q3config.cfg\\n\", RESTRICT_LOCAL);\n\t\telse //if (cfg <= def && cfg!=0x7fffffff)\n\t\t\tCbuf_AddText (\"exec config.cfg\\n\", RESTRICT_LOCAL);\n\t\tif (def!=FDEPTH_MISSING)\n\t\t\tCbuf_AddText (\"exec autoexec.cfg\\n\", RESTRICT_LOCAL);\n\t}\n#endif\n#ifdef QUAKESPYAPI\n\tif (COM_FCheckExists (\"frontend.cfg\"))\n\t\tCbuf_AddText (\"exec frontend.cfg\\n\", RESTRICT_LOCAL);\n#endif\n\tCbuf_AddText (\"cl_warncmd 1\\n\", RESTRICT_LOCAL);\t//and then it's allowed to start moaning.\n\tCOM_ParsePlusSets(true);\n\n\tcom_parseutf8.ival = com_parseutf8.value;\n\n\t//if the renderer is already up and running, be prepared to reload content to match the new conback/font/etc\n\tif (r_blockvidrestart)\n\t\t;\n\telse if (fullvidrestart)\n\t\tCbuf_AddText (\"vid_restart\\n\", RESTRICT_LOCAL);\n\telse if (qrenderer != QR_NONE)\n\t\tCbuf_AddText (\"vid_reload\\n\", RESTRICT_LOCAL);\n//\tif (Key_Dest_Has(kdm_menu))\n//\t\tCbuf_AddText (\"closemenu\\ntogglemenu\\n\", RESTRICT_LOCAL);\t//make sure the menu has the right content loaded.\n\n\tCbuf_Execute ();\t//if the server initialisation causes a problem, give it a place to abort to\n\n\t//assuming they didn't use any waits in their config (fools)\n\t//the configs should be fully loaded.\n\t//so convert the backwards compable commandline parameters in cvar sets.\n\tCL_ArgumentOverrides();\n#ifdef HAVE_SERVER\n\tSV_ArgumentOverrides();\n#endif\n\n\t//and disable the 'you have unsaved stuff' prompt.\n\tCvar_Saved();\n\n\tRuleset_Scan();\n}\n\nstatic void Host_URIPrompt(void *ctx, promptbutton_t btn)\n{\n\tif (btn == PROMPT_YES)\n\t\tCbuf_AddText (\"\\nsys_register_file_associations\\n\", RESTRICT_LOCAL);\n}\n\nvoid Host_FinishLoading(void)\n{\n\tint i;\n\textern qboolean r_forceheadless;\n\tif (r_blockvidrestart == true)\n\t{\n\t\t//1 means we need to init the filesystem\n\n\t\t//the filesystem has retrieved its manifest, but might still be waiting for paks to finish downloading.\n\n\t\t//make sure the filesystem has some default if no manifest was loaded.\n\t\tFS_ChangeGame(NULL, true, true);\n\n\t\tif (waitingformanifest)\n\t\t{\n#ifdef MULTITHREAD\n\t\t\tSys_Sleep(0.1);\n#endif\n\t\t\treturn;\n\t\t}\n\n#ifdef PLUGINS\n\t\tPlug_Initialise(true);\n#endif\n\n\t\tCon_History_Load();\n\n\t\tr_blockvidrestart = 2;\n\n\t\tCL_ArgumentOverrides();\n\t#ifdef HAVE_SERVER\n\t\tSV_ArgumentOverrides();\n\t#endif\n\n\t\tCon_TPrintf (\"\\nEngine Version: %s\\n\", version_string());\n\n\t\tCon_DPrintf(\"This program is free software; you can redistribute it and/or \"\n\t\t\t\t\t\"modify it under the terms of the GNU General Public License \"\n\t\t\t\t\t\"as published by the Free Software Foundation; either version 2 \"\n\t\t\t\t\t\"of the License, or (at your option) any later version.\"\n\t\t\t\t\t\"\\n\"\n\t\t\t\t\t\"This program is distributed in the hope that it will be useful, \"\n\t\t\t\t\t\"but WITHOUT ANY WARRANTY; without even the implied warranty of \"\n\t\t\t\t\t\"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. \"\n\t\t\t\t\t\"\\n\"\n\t\t\t\t\t\"See the GNU General Public License for more details.\\n\");\n\n\t#if defined(_WIN32) && !defined(FTE_SDL) && !defined(_XBOX) && defined(MANIFESTDOWNLOADS)\n\t\tif (Sys_RunInstaller())\n\t\t\tSys_Quit();\n\t#endif\n\n\t\tMenu_Download_Update();\n\n#ifdef IPLOG\n\t\tIPLog_Merge_File(\"iplog.txt\");\n\t\tIPLog_Merge_File(\"iplog.dat\");\t//legacy crap, for compat with proquake\n#endif\n\n\t\tif (!PM_IsApplying())\n\t\t\tCmd_StuffCmds();\n\t}\n\n\tif (PM_IsApplying() == 1)\n\t{\n#ifdef MULTITHREAD\n\t\tSys_Sleep(0.1);\n#endif\n\t\treturn;\n\t}\n\n\t//open any files specified on the commandline (urls, paks, models, I dunno).\n\tfor (i = 1; i < com_argc; i++)\n\t{\n\t\tif (!com_argv[i])\n\t\t\tcontinue;\n\t\tif (*com_argv[i] == '+' || *com_argv[i] == '-')\n\t\t\tbreak;\n\t\tHost_RunFile(com_argv[i], strlen(com_argv[i]), NULL);\n\t}\n\n\t//android may find that it has no renderer at various points.\n\tif (r_forceheadless)\n\t\treturn;\n\n\tif (r_blockvidrestart == 2)\n\t{\t//2 is part of the initial startup\n\t\tRenderer_Start();\n\t\tCL_StartCinematicOrMenu();\n\t}\n\telse\t//3 flags for a renderer restart\n\t\tRenderer_Start();\n\n\n\tif (fs_manifest->schemes && Cmd_IsCommand(\"sys_register_file_associations\"))\n\t{\n\t\tif (cl_verify_urischeme.ival >= 2)\n\t\t\tCbuf_AddText (\"\\nsys_register_file_associations\\n\", RESTRICT_LOCAL);\n\t\telse if (cl_verify_urischeme.ival)\n\t\t{\n\t\t\tchar *scheme = Sys_URIScheme_NeedsRegistering();\n\t\t\tif (scheme)\n\t\t\t{\n\t\t\t\tMenu_Prompt(Host_URIPrompt, NULL, va(localtext(\"The URI scheme %s:// is not configured.\\nRegister now?\"), scheme), \"Register\", NULL, \"No\", true);\n\t\t\t\tZ_Free(scheme);\n\t\t\t}\n\t\t}\n\t}\n}\n\n/*\n====================\nHost_Init\n====================\n*/\nvoid Host_Init (quakeparms_t *parms)\n{\n/*#ifdef PACKAGEMANAGER\n\tchar engineupdated[MAX_OSPATH];\n#endif*/\n\tint man;\n\n\tcom_parseutf8.ival = 1;\t//enable utf8 parsing even before cvars are registered.\n\n\tCOM_InitArgv (parms->argc, parms->argv);\n\n\tif (setjmp (host_abort) )\n\t\tSys_Error(\"Host_Init: An error occured. Try the -condebug commandline parameter\\n\");\n\n\n\thost_parms = *parms;\n\n\tCvar_Init();\n\tMemory_Init ();\n\n\t/*memory is working, its safe to printf*/\n\tCon_Init ();\n\n\tSys_Init();\n\n\tCOM_ParsePlusSets(false);\n\tCbuf_Init ();\n\tCmd_Init ();\n\tCOM_Init ();\n\n/*this may be tripping some bullshit huristic in microsoft's insecurity mafia software(tbh I really don't know what they're detecting), plus causes firewall issues on updates.\n#ifdef PACKAGEMANAGER\n\t//we have enough of the filesystem inited now that we can read the package list and figure out which engine was last installed.\n\tif (PM_FindUpdatedEngine(engineupdated, sizeof(engineupdated)))\n\t{\n\t\tPM_Shutdown();\t//will restart later as needed, but we need to be sure that no files are open or anything.\n\t\tif (Sys_EngineWasUpdated(engineupdated))\n\t\t{\n\t\t\tCOM_Shutdown();\n\t\t\tCmd_Shutdown();\n\t\t\tSys_Shutdown();\n\t\t\tCon_Shutdown();\n\t\t\tMemory_DeInit();\n\t\t\tCvar_Shutdown();\n\t\t\tSys_Quit();\n\t\t\treturn;\n\t\t}\n\t\tPM_Shutdown();\t//will restart later as needed, but we need to be sure that no files are open or anything.\n\t}\n#endif\n*/\n\n\tV_Init ();\n\tNET_Init ();\n\n#if defined(Q2BSPS) || defined(Q3BSPS)\n\tCM_Init();\n#endif\n#ifdef TERRAIN\n\tTerr_Init();\n#endif\n\tHost_FixupModelNames();\n\n\tNetchan_Init ();\n\tRenderer_Init();\n\tMod_Init(true);\n\n#if defined(CSQC_DAT) || defined(MENU_DAT)\n\tPF_Common_RegisterCvars();\n#endif\n#ifdef HAVE_SERVER\n\tSV_Init(parms);\n#endif\n\n//\tW_LoadWadFile (\"gfx.wad\");\n\tKey_Init ();\n\tM_Init ();\n\tIN_Init ();\n\tS_Init ();\n\tcls.state = ca_disconnected;\n\tCDAudio_Init ();\n\tSbar_Init ();\n\tCL_Init ();\n\n#ifdef PLUGINS\n\tPlug_Initialise(false);\n#endif\n\n#ifdef TEXTEDITOR\n\tEditor_Init();\n#endif\n\n#ifdef CL_MASTER\n\tMaster_SetupSockets();\n#endif\n\n#ifdef Q3CLIENT\n\tCL_ReadCDKey();\n#endif\n\n\t//\tCon_Printf (\"Exe: \"__TIME__\" \"__DATE__\"\\n\");\n\t//Con_Printf (\"%4.1f megs RAM available.\\n\", parms->memsize/ (1024*1024.0));\n\n\tR_SetRenderer(NULL);//set the renderer stuff to unset...\n\n\tCvar_ParseWatches();\n\thost_initialized = true;\n\tforcesaveprompt = false;\n\n#ifdef PLUGINS\n\tPlug_Initialise(false);\n#endif\n\n\tSys_SendKeyEvents();\n\n\t//the engine is fully running, except the file system may be nulled out waiting for a manifest to download.\n\n\tman = COM_CheckParm(\"-manifest\");\n\tif (man && man < com_argc-1 && com_argv[man+1])\n\t\tHost_RunFile(com_argv[man+1], strlen(com_argv[man+1]), NULL);\n}\n\n/*\n===============\nHost_Shutdown\n\nFIXME: this is a callback from Sys_Quit and Sys_Error.  It would be better\nto run quit through here before the final handoff to the sys code.\n===============\n*/\nvoid Host_Shutdown(void)\n{\n\tsize_t i;\n\tif (!host_initialized)\n\t\treturn;\n\thost_initialized = false;\n\n\tCL_UseIndepPhysics(false);\n\n#ifdef WEBCLIENT\n\tHTTP_CL_Terminate();\n#endif\n\n\t//disconnect server/client/etc\n\tCL_Disconnect_f();\n\n\tM_Shutdown(true);\n\n#ifdef CSQC_DAT\n\tCSQC_Shutdown();\n#endif\n\n\tS_Shutdown(true);\n\tCDAudio_Shutdown ();\n\tIN_Shutdown ();\n\tR_ShutdownRenderer(true);\n\n#ifdef PLUGINS\n\tPlug_Shutdown(false);\n#endif\n\n//\tHost_WriteConfiguration ();\n#ifdef CL_MASTER\n\tMasterInfo_Shutdown();\n#endif\n\tCL_FreeDlights();\n\tCL_FreeVisEdicts();\n\tMod_Shutdown(true);\n\tWads_Flush();\n\tCon_History_Save();\t//do this outside of the console code so that the filesystem is still running at this point but still allowing the filesystem to make console prints (you might not see them, but they should be visible to sys_printf still, for debugging).\n#ifdef HAVE_SERVER\n\tSV_Shutdown();\n#else\n\tLog_ShutDown();\n\tNET_Shutdown ();\n\tFS_Shutdown();\n#endif\n#ifdef QUAKEHUD\n\tStats_Clear();\n#endif\n\tRuleset_Shutdown();\n\n\tCOM_DestroyWorkerThread();\n\n\tP_ShutdownParticleSystem();\n\tCvar_Shutdown();\n\tValidation_FlushFileList();\n\n#ifdef HAVE_GNUTLS\n\tGnuTLS_Shutdown();\n#endif\n\n\tCmd_Shutdown();\n#ifdef PACKAGEMANAGER\n\tPM_Shutdown(false);\n#endif\n\tKey_Unbindall_f();\n\n#ifdef PLUGINS\n\tPlug_Shutdown(true);\n#endif\n\n\tfor (i = 0; i < MAX_SPLITS; i++)\n\t\tInfoBuf_Clear(&cls.userinfo[i], true);\n\tInfoSync_Clear(&cls.userinfosync);\n\n\tCon_Shutdown();\n\tCOM_BiDi_Shutdown();\n\tMemory_DeInit();\n\n#ifdef HAVE_SERVER\n\tSV_WipeServerState();\n\tmemset(&svs, 0, sizeof(svs));\n#endif\n\tSys_Shutdown();\n}\n\n#ifndef HAVE_SERVER\nvoid SV_EndRedirect (void)\n{\n}\n#endif\n"
  },
  {
    "path": "engine/client/cl_master.h",
    "content": "#ifndef CL_MASTER_H\n#define CL_MASTER_H\n\nenum masterprotocol_e\n{\n\tMP_UNSPECIFIED,\n\tMP_QUAKEWORLD,\n#if defined(Q2CLIENT) || defined(Q2SERVER)\n\tMP_QUAKE2,\n#endif\n#if defined(Q3CLIENT) || defined(Q3SERVER)\n\tMP_QUAKE3,\n#endif\n#ifdef NQPROT\n\tMP_NETQUAKE,\n#endif\n\tMP_DPMASTER\n};\n\n#if defined(CL_MASTER) && defined(HAVE_CLIENT)\n#define SS_PROTOCOLMASK 0xf\n#define SS_UNKNOWN\t\t0\n#define SS_QUAKEWORLD\t1\n#define SS_NETQUAKE\t\t2\n#define SS_QUAKE2\t\t3\n#define SS_QUAKE3\t\t4\n#define SS_QEPROT\t\t5\t//needs dtls and a different ccreq version\n//#define SS_UNUSED\t\t6\n//#define SS_UNUSED\t\t7\n\n#define SS_LOCAL\t\t(1<<3u)\t//local servers are ones we detected without being listed on a master server (masters will report public ips, so these may appear as dupes if they're also public)\n#define SS_FTESERVER\t(1<<4u)\t//just highlighting differences, to give some impression of superiority.\n#define SS_FAVORITE\t\t(1<<5u)\t//filter all others.\n#define SS_KEEPINFO\t\t(1<<6u)\n#define SS_GETINFO\t\t(1<<7u)\t//explicitly query via getinfo\n#define SS_PROXY\t\t(1<<8u)\t//qizmo/qwfwd/qtv/eztv\n#define SS_RELAY\t\t(1<<9u)\t//supports the \\prx\\nexthop relay thing, and pingstatus requests for connectbr.\n\n#define PING_DEAD\t\t0xffff\t//default ping value to denote servers that are not responding.\n#define PING_UNKNOWN\t0xfffe\t//these servers are considered up, but we can't query them directly so can't determine the final ping from here.\n#define PING_MAX\t\t0xfffd\t//highest 'valid' ping value.\n\n\n//despite not supporting nq or q2, we still load them. We just filter them. This is to make sure we properly write the listing files.\nenum mastertype_e\n{\n\tMT_BAD,\t\t\t//this would be an error\n//\tMT_MASTERHTTPJSON,\n\tMT_MASTERHTTP,\n\tMT_MASTERUDP,\n\tMT_BCAST,\n\tMT_SINGLE,\n};\n\n\ntypedef enum hostcachekey_e\n{\n\tSLKEY_PING,\n\tSLKEY_MAP,\n\tSLKEY_NAME,\n\tSLKEY_ADDRESS,\n\tSLKEY_NUMPLAYERS,\n\tSLKEY_MAXPLAYERS,\n\tSLKEY_GAMEDIR,\n\n\tSLKEY_FREEPLAYERS,\n\tSLKEY_BASEGAME,\n\tSLKEY_FLAGS,\n\tSLKEY_TIMELIMIT,\n\tSLKEY_FRAGLIMIT,\n\n\tSLKEY_MOD,\n\tSLKEY_PROTOCOL,\n\tSLKEY_NUMBOTS,\t\t//uninteresting bots that will presumably get kicked if people join.\n\tSLKEY_NUMSPECTATORS,//spectators\n\tSLKEY_NUMHUMANS,\t//actual players\n\tSLKEY_QCSTATUS,\n\tSLKEY_CATEGORY,\t\t//urgh, hideous shite.\n//\tSLKEY_PLAYERS,\t//eep!\n\tSLKEY_ISFAVORITE,//eep!\n\tSLKEY_ISLOCAL,\n\tSLKEY_ISPROXY,\n\tSLKEY_SERVERINFO,\n\n\n\tSLKEY_TOOMANY,\n\tSLKEY_PLAYER0,\n\tSLKEY_CUSTOM = SLKEY_PLAYER0+MAX_CLIENTS\n} hostcachekey_t;\n\ntypedef enum slist_test_e\n{\n\tSLIST_TEST_CONTAINS,\n\tSLIST_TEST_NOTCONTAIN,\n\tSLIST_TEST_LESSEQUAL,\n\tSLIST_TEST_LESS,\n\tSLIST_TEST_EQUAL,\n\tSLIST_TEST_GREATER,\n\tSLIST_TEST_GREATEREQUAL,\n\tSLIST_TEST_NOTEQUAL,\n\tSLIST_TEST_STARTSWITH,\n\tSLIST_TEST_NOTSTARTSWITH\n} slist_test_t;\n\n\n//contains info about a server in greater detail. Could be too mem intensive.\ntypedef struct serverdetailedinfo_s\n{\n\tchar info[MAX_SERVERINFO_STRING];\n\n\tint numplayers;\n\n\tstruct serverdetailedplayerinfo_s\n\t{\n\t\tint userid;\n\t\tint frags;\n\t\tfloat time;\n\t\tint ping;\n\t\tchar name[64];\n\t\tchar skin[16];\t//is this even useful?\n\t\tchar team[16];\n\t\tchar topc;\n\t\tchar botc;\n\t\tqbyte isspec;\n\t} players[MAX_CLIENTS];\n} serverdetailedinfo_t;\n\n//hold minimum info.\ntypedef struct serverinfo_s\n{\n\tchar name[80];\t//hostname.\n\tnetadr_t adr;\n\tchar brokerid[64]; //'rtc[s]://adr//brokerid'\n\n\tshort special;\t//flags\n\tshort protocol;\n\n\tqbyte players;\n\tqbyte maxplayers;\n\tqbyte sends;\n\tqbyte status;\n#define SRVSTATUS_ALIVE\t\t1u\t//server is responding to pings\n#define SRVSTATUS_DISPLAYED\t2u\t//server passed all filters\n#define SRVSTATUS_GLOBAL\t4u\t//server was reported by one of the master servers (ie: global and not local)\n\n\tqbyte numspectators;\n\tqbyte numhumans;\n\tqbyte numbots;\n\tqbyte freeslots;\n\n\tint qccategory; //urgh\n\n\tchar qcstatus[128];\n\tchar modname[8+1];\n\n\tchar gamedir[8+1];\n\tchar map[16];\n\n//\tunsigned short gameversion;\n\tunsigned short ping;\n\n\tshort tl;\n\tshort fl;\n\n\tfloat refreshtime;\n\n\tserverdetailedinfo_t *moreinfo;\n\n\tstruct serverinfo_s *prevpeer;\n\tunsigned short cost;\n\tunsigned short numpeers;\n\tstruct peers_s\n\t{\n\t\tstruct serverinfo_s *peer;\n\t\tunsigned short ping;\n\t} *peers;\n\n\tstruct serverinfo_s *next;\n} serverinfo_t;\n\ntypedef struct master_s\n{\n\tstruct master_s *next;\n\tnetadr_t adr;\n\tchar *address;\t//text based address (http servers)\n\tstruct dl_download *dl;\n\tqbyte nosave;\n\tqbyte mastertype;\n\tqbyte protocoltype;\n\tint sends; /*needs to resend?*/\n\tchar name[1];\n} master_t;\n\nextern struct selectedserver_s\n{\n\tqboolean inuse;\n\tnetadr_t adr;\n\tchar\tbrokerid[64];\n\tfloat\trefreshtime;\n\tint\t\tlastplayer;\n\tchar\tlastrule[64];\n\n\tserverdetailedinfo_t *detail;\n\n\tint linenum;\n} selectedserver;\n\ntypedef struct player_s\n{\n\tchar name[16];\n\tint frags;\n\tint colour;\n\tchar skin[8];\n\tchar team[8];\n\tnetadr_t adr;\n\n\tstruct player_s *next;\n} player_t;\n\nvoid SListOptionChanged(serverinfo_t *newserver);\n\nextern serverinfo_t *firstserver;\nextern master_t *master;\nextern player_t *mplayers;\nextern qboolean sb_favouriteschanged;\n\nvoid Master_SetupSockets(void);\nqboolean CL_QueryServers(void);\nvoid Master_CheckPollSockets(void);\nvoid MasterInfo_Shutdown(void);\nvoid MasterInfo_WriteServers(void);\nserverinfo_t *Master_InfoForServer (netadr_t *addr, const char *brokerid);\nserverinfo_t *Master_InfoForNum (int num);\nunsigned int Master_TotalCount(void);\nunsigned int Master_NumPolled(void);\t//progress indicator\nunsigned int Master_NumAlive(void);\nvoid Master_SetupSockets(void);\nvoid MasterInfo_Refresh(qboolean doreset);\nvoid Master_QueryServer(serverinfo_t *server);\nvoid MasterInfo_WriteServers(void);\nchar *Master_ServerToString (char *s, int len, serverinfo_t *a);\t//like NET_AdrToString, but handles more complex addresses.\n\nhostcachekey_t Master_KeyForName(const char *keyname);\nfloat Master_ReadKeyFloat(serverinfo_t *server, unsigned int keynum);\nchar *Master_ReadKeyString(serverinfo_t *server, unsigned int keynum);\n\nint Master_SortServers(void);\nvoid Master_SetSortField(hostcachekey_t field, unsigned int sortflags);\nhostcachekey_t Master_GetSortField(void);\nqboolean Master_GetSortDescending(void);\n\nint Master_NumSorted(void);\nvoid Master_ClearMasks(void);\nserverinfo_t *Master_SortedServer(int idx);\nvoid Master_SetMaskString(qboolean or_, hostcachekey_t field, const char *param, slist_test_t testop);\nvoid Master_SetMaskInteger(qboolean or_, hostcachekey_t field, int param, slist_test_t testop);\nserverinfo_t *Master_FindRoute(netadr_t target);\n#endif\n\n\n\n#endif\n"
  },
  {
    "path": "engine/client/cl_parse.c",
    "content": "/*\nCopyright (C) 1996-1997 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n*/\n// cl_parse.c  -- parse a message received from the server\n\n#include \"quakedef.h\"\n#include \"cl_ignore.h\"\n#include \"shader.h\"\n#include \"fs.h\"\n\nvoid CL_GetNumberedEntityInfo (int num, float *org, float *ang);\nvoid CLDP_ParseDarkPlaces5Entities(void);\nvoid CLH2_ParseEntities(void);\nstatic void CL_SetStatNumeric (int pnum, unsigned int stat, int ivalue, float fvalue);\n#define CL_SetStatInt(pnum,stat,ival) do{int thevalue=ival; CL_SetStatNumeric(pnum,stat,thevalue,thevalue);}while(0)\n#define CL_SetStatFloat(pnum,stat,fval) do{float thevalue=fval; CL_SetStatNumeric(pnum,stat,thevalue,thevalue);}while(0)\nstatic qboolean CL_CheckModelResources (char *name);\n#ifdef NQPROT\nstatic char *CLNQ_ParseProQuakeMessage (char *s);\n#endif\nstatic void DLC_Poll(qdownload_t *dl);\nstatic void CL_ProcessUserInfo (int slot, player_info_t *player);\nstatic void CL_ParseStuffCmd(char *msg, int destsplit);\n\n#define MSG_ReadBigIndex() ((cls.fteprotocolextensions2&PEXT2_LONGINDEXES)?(unsigned int)MSG_ReadUInt64():MSG_ReadByte ())\n#define MSG_ReadPlayer() MSG_ReadBigIndex()\n\n#ifdef NQPROT\nchar *cl_dp_packagenames;\nstatic char cl_dp_csqc_progsname[128];\nstatic int cl_dp_csqc_progssize;\nstatic int cl_dp_csqc_progscrc;\nstatic int cl_dp_serverextension_download;\n#endif\n\n//tracks which svcs are using what data (per second)\nstatic size_t packetusage_saved[256];\nstatic size_t packetusage_pending[256];\nstatic double packetusageflushtime;\nstatic const double packetusage_interval=10;\n\n#ifdef AVAIL_ZLIB\n#ifndef ZEXPORT\n\t#define ZEXPORT VARGS\n#endif\n#include <zlib.h>\n#endif\n\n\nstatic const char *svc_qwstrings[] =\n{\n\t\"svc_bad\",\n\t\"svc_nop\",\n\t\"svc_disconnect\",\n\t\"svcqw_updatestatbyte\",\n\t\"svc_version\",\t\t// [long] server version\n\t\"svc_setview\",\t\t// [short] entity number\n\t\"svcqw_sound\",\t\t\t// <see code>\n\t\"svc_time\",\t\t\t// [float] server time\n\t\"svc_print\",\t\t\t// [string] null terminated string\n\t\"svc_stufftext\",\t\t// [string] stuffed into client's console buffer\n\t\t\t\t\t\t// the string should be \\n terminated\n\t\"svc_setangle\",\t\t// [vec3] set the view angle to this absolute value\n\n\t\"svc_serverdata\",\t\t// [long] version ...\n\t\"svc_lightstyle\",\t\t// [qbyte] [string]\n\t\"svc_updatename\",\t\t// [qbyte] [string]\n\t\"svc_updatefrags\",\t// [qbyte] [short]\n\t\"svc_clientdata\",\t\t// <shortbits + data>\n\t\"svc_stopsound\",\t\t// <see code>\n\t\"svc_updatecolors\",\t// [qbyte] [qbyte]\n\t\"svc_particle\",\t\t// [vec3] <variable>\n\t\"svc_damage\",\t\t\t// [qbyte] impact [qbyte] blood [vec3] from\n\n\t\"svc_spawnstatic\",\n\t\"svcfte_spawnstatic2\",\n\t\"svc_spawnbaseline\",\n\n\t\"svc_temp_entity\",\t\t// <variable>\n\t\"svc_setpause\",\n\t\"svc_signonnum\",\n\t\"svc_centerprint\",\n\t\"svc_killedmonster\",\n\t\"svc_foundsecret\",\n\t\"svc_spawnstaticsound\",\n\t\"svc_intermission\",\n\t\"svc_finale\",\n\n\t\"svc_cdtrack\",\n\t\"svc_sellscreen\",\n\n\t\"svc_smallkick\",\n\t\"svc_bigkick\",\n\n\t\"svc_updateping\",\n\t\"svc_updateentertime\",\n\n\t\"svc_updatestatlong\",\n\t\"svc_muzzleflash\",\n\t\"svc_updateuserinfo\",\n\t\"svc_download\",\n\t\"svc_playerinfo\",\n\t\"svc_nails\",\n\t\"svc_choke\",\n\t\"svc_modellist\",\n\t\"svc_soundlist\",\n\t\"svc_packetentities\",\n \t\"svc_deltapacketentities\",\n\t\"svc_maxspeed\",\n\t\"svc_entgravity\",\n\n\t\"svc_setinfo\",\n\t\"svc_serverinfo\",\n\t\"svc_updatepl\",\n\t\"MVD svc_nails2\",\n\t\"svcfte_soundextended\",\n\t\"svcfte_soundlistshort\",\n\t\"FTE svc_lightstylecol\",\n\t\"FTE svc_bulletentext\", // obsolete\n\t\"FTE svc_lightnings\",\n\t\"FTE svc_modellistshort\",\n\t\"FTE svc_ftesetclientpersist\",\n\t\"FTE svc_setportalstate\",\n\t\"FTE svc_particle2\",\n\t\"FTE svc_particle3\",\n\t\"FTE svc_particle4\",\n\t\"FTE svc_spawnbaseline2\",\n\t\"FTE svc_customtempent\",\n\t\"FTE svc_choosesplitclient\",\n\n\t\"svcfte_showpic\",\n\t\"svcfte_hidepic\",\n\t\"svcfte_movepic\",\n\t\"svcfte_updatepic\",\n\n\t\"NEW PROTOCOL(73)\",\n\n\t\"svcfte_effect\",\n\t\"svcfte_effect2\",\n\n\t\"svcfte_csqcentities\",\n\n\t\"svcfte_precache\",\n\n\t\"svcfte_updatestatstring\",\n\t\"svcfte_updatestatfloat\",\n\n\t\"svcfte_trailparticles\",\n\t\"svcfte_pointparticles\",\n\t\"svcfte_pointparticles1\",\n\n\t\"svcfte_cgamepacket\",\n\t\"svcfte_voicechat\",\n\t\"svcfte_setangledelta\",\n\t\"svcfte_updateentities\",\n\t\"svcfte_brushedit\",\n\t\"svcfte_updateseats\",\n\t\"svcfte_setinfoblob\",\t\t\t//89\n\t\"svcfte_cgamepacket_sized\",\t\t//90\n\t\"svcfte_temp_entity_sized\",\t\t//91\n\t\"svcfte_csqcentities_sized\",\t//92\n\t\"NEW PROTOCOL(93)\",\n\t\"NEW PROTOCOL(94)\",\n\t\"NEW PROTOCOL(95)\",\n\t\"NEW PROTOCOL(96)\",\n\t\"NEW PROTOCOL(97)\",\n\t\"NEW PROTOCOL(98)\",\n\t\"NEW PROTOCOL(99)\",\n\t\"NEW PROTOCOL(100)\",\n\t\"NEW PROTOCOL(101)\",\n\t\"NEW PROTOCOL(102)\",\n\t\"NEW PROTOCOL(103)\",\n\t\"NEW PROTOCOL(104)\",\n\t\"NEW PROTOCOL(105)\",\n\t\"NEW PROTOCOL(106)\",\n\t\"NEW PROTOCOL(107)\",\n\t\"NEW PROTOCOL(108)\",\n};\n\n#ifdef NQPROT\nstatic const char *svc_nqstrings[] =\n{\n\t\"nqsvc_bad\",\n\t\"nqsvc_nop\",\n\t\"nqsvc_disconnect\",\n\t\"nqsvc_updatestatlong\",\n\t\"nqsvc_version\",\t\t// [long] server version\n\t\"nqsvc_setview\",\t\t// [short] entity number\n\t\"nqsvc_sound\",\t\t\t// <see code>\n\t\"nqsvc_time\",\t\t\t// [float] server time\n\t\"nqsvc_print\",\t\t\t// [string] null terminated string\n\t\"nqsvc_stufftext\",\t\t// [string] stuffed into client's console buffer\n\t\t\t\t\t\t\t// the string should be \\n terminated\n\t\"nqsvc_setangle\",\t\t// [vec3] set the view angle to this absolute value\n\n\t\"nqsvc_serverinfo\",\t\t// [long] version\n\t\t\t\t\t\t\t// [string] signon string\n\t\t\t\t\t\t\t// [string]..[0]model cache [string]...[0]sounds cache\n\t\t\t\t\t\t\t// [string]..[0]item cache\n\t\"nqsvc_lightstyle\",\t\t// [qbyte] [string]\n\t\"nqsvc_updatename\",\t\t// [qbyte] [string]\n\t\"nqsvc_updatefrags\",\t// [qbyte] [short]\n\t\"nqsvc_clientdata\",\t\t// <shortbits + data>\n\t\"nqsvc_stopsound\",\t\t// <see code>\n\t\"nqsvc_updatecolors\",\t// [qbyte] [qbyte]\n\t\"nqsvc_particle\",\t\t// [vec3] <variable>\n\t\"nqsvc_damage\",\t\t\t// [qbyte] impact [qbyte] blood [vec3] from\n\n\t\"nqsvc_spawnstatic\",\n\t\"ftenq_spawnstatic2(21)\",\n\t\"nqsvc_spawnbaseline\",\n\n\t\"nqsvc_temp_entity\",\t// <variable>\n\t\"nqsvc_setpause\",\n\t\"nqsvc_signonnum\",\n\t\"nqsvc_centerprint\",\n\t\"nqsvc_killedmonster\",\n\t\"nqsvc_foundsecret\",\n\t\"nqsvc_spawnstaticsound\",\n\t\"nqsvc_intermission\",\n\t\"nqsvc_finale\",\t\t\t// [string] music [string] text\n\t\"nqsvc_cdtrack\",\t\t// [qbyte] track [qbyte] looptrack\n\t\"nqsvc_sellscreen\",\n\t\"nqsvc_cutscene\",\t//34\n\n\t\"NEW PROTOCOL\",\t//35\n\t\"NEW PROTOCOL\",\t//36\n\t\"fitz_skybox\",\t//37\n\t\"NEW PROTOCOL\",\t//38\n\t\"NEW PROTOCOL\",\t//39\n\t\"fitz_bf\",\t\t//40\n\t\"fitz_fog\",\t//41\n\t\"fitz_spawnbaseline2\",\t//42\n\t\"fitz_spawnstatic2\",\t//43\n\t\"fitz_spawnstaticsound2\",\t//44\n\t\"NEW PROTOCOL\",\t//45\n\t\"qex_updateping\",\t//46\n\t\"qex_updatesocial\",\t//47\n\t\"qex_updateplinfo\",\t//48\n\t\"qex_print\",\t//49\n\t\"dp_downloaddata / neh_skyboxsize / qex_servervars\",\t\t//50\n\t\"dp_updatestatubyte / neh_fog / qex_seq\",\t//51\n\t\"dp_effect / qex_achievement\",\t\t\t\t//52\n\t\"dp_effect2 / qex_chat\",\t\t\t//53\n\t\"dp6_precache / dp5_sound2 / qex_levelcompleted\",\t//54\n\t\"dp_spawnbaseline2 / qex_backtolobby\",\t\t//55\n\t\"dp_spawnstatic2 / qex_localsound\",\t//56 obsolete\n\t\"dp_entities / qex_prompt\",\t\t//57\n\t\"dp_csqcentities / qex_loccenterprint\",\t\t\t//58\n\t\"dp_spawnstaticsound2\",\t//59\n\t\"dp_trailparticles\",\t//60\n\t\"dp_pointparticles\",\t//61\n\t\"dp_pointparticles1\",\t//62\n\t\"NEW PROTOCOL(63)\",\t//63\n\t\"NEW PROTOCOL(64)\",\t//64\n\t\"NEW PROTOCOL(65)\",\t//65\n\t\"ftenq_spawnbaseline2\",\t//66\n\t\"NEW PROTOCOL(67)\",\t//67\n\t\"NEW PROTOCOL(68)\",\t//68\n\t\"NEW PROTOCOL(69)\",\t//69\n\t\"NEW PROTOCOL(70)\",\t//70\n\t\"NEW PROTOCOL(71)\",\t//71\n\t\"NEW PROTOCOL(72)\",\t//72\n\t\"NEW PROTOCOL(73)\",\t//73\n\t\"NEW PROTOCOL(74)\",\t//74\n\t\"NEW PROTOCOL(75)\",\t//75\n\t\"NEW PROTOCOL(76)\",\t//76\n\t\"NEW PROTOCOL(77)\",\t//77\n\t\"ftenq_updatestatstring\",\t//78\n\t\"ftenq_updatestatfloat\",\t//79\n\t\"NEW PROTOCOL(80)\",\t//80\n\t\"NEW PROTOCOL(81)\",\t//81\n\t\"NEW PROTOCOL(82)\",\t//82\n\t\"ftenq_cgamepacket\",\t//83\n\t\"ftenq_voicechat\",\t//84\n\t\"ftenq_setangledelta\",\t//85\n\t\"ftenq_updateentities\",\t//86\n\t\"NEW PROTOCOL(87)\",\t//87\n\t\"NEW PROTOCOL(88)\",\t//88\n\t\"ftenq_setinfoblob\",\t\t\t//89\n\t\"ftenq_cgamepacket_sized\",\t\t//90\n\t\"ftenq_temp_entity_sized\",\t\t//91\n\t\"ftenq_csqcentities_sized\",\t//92\n};\n#endif\n\nextern cvar_t requiredownloads, mod_precache, snd_precache, cl_standardchat, msg_filter, msg_filter_frags, msg_filter_pickups, cl_countpendingpl, cl_download_mapsrc;\nint\toldparsecountmod;\nint\tparsecountmod;\ndouble\tparsecounttime;\n\nint\t\tcl_spikeindex, cl_playerindex, cl_h_playerindex, cl_flagindex, cl_rocketindex, cl_grenadeindex, cl_gib1index, cl_gib2index, cl_gib3index;\n\n//called after disconnect, purges all memory that was allocated etc\nvoid CL_Parse_Disconnected(void)\n{\n\tif (cls.download)\n\t{\n\t\t//note: not all downloads abort when the server disconnects, as they're fully out of bounds (ie: http)\n\t\tif (cls.download->method <= DL_QWPENDING)\n\t\t\tDL_Abort(cls.download, QDL_DISCONNECT);\n\t}\n\n\t{\n\t\tdownloadlist_t *next;\n\t\twhile(cl.downloadlist)\n\t\t{\n\t\t\tnext = cl.downloadlist->next;\n\t\t\tZ_Free(cl.downloadlist);\n\t\t\tcl.downloadlist = next;\n\t\t}\n\t\twhile(cl.faileddownloads)\n\t\t{\n\t\t\tnext = cl.faileddownloads->next;\n\t\t\tZ_Free(cl.faileddownloads);\n\t\t\tcl.faileddownloads = next;\n\t\t}\n\t}\n\n\tCL_ClearParseState();\n}\n\n//=============================================================================\n\nfloat packet_latency[NET_TIMINGS];\n\nint CL_CalcNet (float scale)\n{\n\tint\t\ti;\n\toutframe_t\t*frame;\n\tint lost = 0;\n\tint percent;\n\tint sent;\n//\tchar st[80];\n\n\tsent = NET_TIMINGS;\n\n\tfor (i=cl.movesequence-UPDATE_BACKUP+1\n\t\t; i <= cl.movesequence\n\t\t; i++)\n\t{\n\t\tframe = &cl.outframes[i&UPDATE_MASK];\n\t\tif (i > cl.ackedmovesequence)\n\t\t{\n\t\t\t// no response yet\n\t\t\tif (cl_countpendingpl.ival)\n\t\t\t{\n\t\t\t\tpacket_latency[i&NET_TIMINGSMASK] = 9999;\n\t\t\t\tlost++;\n\t\t\t}\n\t\t\telse\n\t\t\t\tpacket_latency[i&NET_TIMINGSMASK] = 10000;\n\t\t}\n\t\telse if (frame->latency == -1)\n\t\t{\n\t\t\tpacket_latency[i&NET_TIMINGSMASK] = 9999;\t// dropped\n\t\t\tlost++;\n\t\t}\n\t\telse if (frame->latency == -2)\n\t\t\tpacket_latency[i&NET_TIMINGSMASK] = 10000;\t// choked\n\t\telse if (frame->latency == -3)\n\t\t{\n\t\t\tpacket_latency[i&NET_TIMINGSMASK] = 9997;\t// c2spps\n\t\t\tsent--;\n\t\t}\n//\t\telse if (frame->invalid)\n//\t\t\tpacket_latency[i&NET_TIMINGSMASK] = 9998;\t// invalid delta\n\t\telse\n\t\t\tpacket_latency[i&NET_TIMINGSMASK] = frame->latency * 60 * scale;\n\t}\n\n\tif (sent < 1)\n\t\tpercent = 100;\t//shouldn't ever happen.\n\telse\n\t\tpercent = lost * 100 / sent;\n\n\treturn percent;\n}\n\nvoid CL_CalcNet2 (float *pings, float *pings_min, float *pings_max, float *pingms_stddev, float *pingfr, int *pingfr_min, int *pingfr_max, float *dropped, float *choked, float *invalid)\n{\n\tint\t\ti;\n\toutframe_t\t*frame;\n\tint lost = 0;\n\tint pending = 0;\n\tint sent;\n\tint valid = 0;\n\tint fr;\n\tint nchoked = 0;\n\tint ninvalid = 0;\n//\tchar st[80];\n\n\t*pings = 0;\n\t*pings_max = 0;\n\t*pings_min = FLT_MAX;\n\t*pingfr = 0;\n\t*pingfr_max = 0;\n\t*pingfr_min = 0x7fffffff;\n\t*pingms_stddev = 0;\n\n\n\tsent = NET_TIMINGS;\n\n\tfor (i=cl.movesequence-UPDATE_BACKUP+1\n\t\t; i <= cl.movesequence\n\t\t; i++)\n\t{\n\t\tframe = &cl.outframes[i&UPDATE_MASK];\n\t\tif (i > cl.lastackedmovesequence)\n\t\t{\t// no response yet\n\t\t\tif (cl_countpendingpl.ival)\n\t\t\t\tlost++;\n\t\t}\n\t\telse if (frame->latency == -1)\n\t\t\tlost++;\t\t\t\t\t\t\t\t\t\t// lost\n\t\telse if (frame->latency == -2)\n\t\t\tnchoked++;\t\t\t\t\t\t\t\t\t// choked\n\t\telse if (frame->latency == -3)\n\t\t\tsent--;\t\t\t\t\t\t\t\t\t\t// c2spps\n\t\telse if (frame->latency == -4)\n\t\t\tninvalid++;\t\t\t\t\t\t\t\t\t//corrupt/wrong/dodgy/egads\n\t\telse\n\t\t{\n\t\t\t*pings += frame->latency;\n\t\t\tif (*pings_max < frame->latency)\n\t\t\t\t*pings_max = frame->latency;\n\t\t\tif (*pings_min > frame->latency)\n\t\t\t\t*pings_min = frame->latency;\n\n\t\t\tfr = frame->cmd_sequence-frame->server_message_num;\n\t\t\t*pingfr += fr;\n\t\t\tif (*pingfr_max < fr)\n\t\t\t\t*pingfr_max = fr;\n\t\t\tif (*pingfr_min > fr)\n\t\t\t\t*pingfr_min = fr;\n\t\t\tvalid++;\n\t\t}\n\t}\n\n\tif (valid)\n\t{\n\t\t*pings /= valid;\n\t\t*pingfr /= valid;\n\n\t\t//determine stddev, in milliseconds instead of seconds.\n\t\tfor (i=cl.movesequence-UPDATE_BACKUP+1; i <= cl.movesequence; i++)\n\t\t{\n\t\t\tframe = &cl.outframes[i&UPDATE_MASK];\n\t\t\tif (i <= cl.lastackedmovesequence && frame->latency >= 0)\n\t\t\t{\n\t\t\t\tfloat dev = (frame->latency - *pings) * 1000;\n\t\t\t\t*pingms_stddev += dev*dev;\n\t\t\t}\n\t\t}\n\t\t*pingms_stddev = sqrt(*pingms_stddev/valid);\n\t}\n\n\tif (pending == sent || sent < 1)\n\t\t*dropped = 1;\t//shouldn't ever happen.\n\telse\n\t\t*dropped = (float)lost / sent;\n\t*choked = (float)nchoked / sent;\n\t*invalid = (float)ninvalid / sent;\n}\n\nvoid CL_AckedInputFrame(int inseq, int outseq, qboolean worldstateokay)\n{\n\tunsigned int i;\n\tunsigned int newmod;\n\toutframe_t *frame;\n\n\t//calc the latency for this frame, but only if its not a dupe ack. we want the youngest, not the oldest, so we can calculate network latency rather than simply packet frequency\n\tif (outseq != cl.lastackedmovesequence)\n\t{\n\t\tnewmod = outseq & UPDATE_MASK;\n\t\tframe = &cl.outframes[newmod];\n\t// calculate latency\n\t\tframe->latency = realtime - frame->senttime;\n\t\tif (frame->latency < 0 || frame->latency > 1.0)\n\t\t{\n\t//\t\tCon_Printf (\"Odd latency: %5.2f\\n\", latency);\n\t\t}\n\t\telse\n\t\t{\n\t\t// drift the average latency towards the observed latency\n\t\t\tif (frame->latency < cls.latency)\n\t\t\t\tcls.latency = frame->latency;\n\t\t\telse\n\t\t\t\tcls.latency += 0.001;\t// drift up, so correction are needed\n\t\t}\n\n\t\tif (cls.protocol != CP_NETQUAKE && cl.inframes[inseq&UPDATE_MASK].invalid)\n\t\t\tframe->latency = -4;\n\n\t\t//and mark any missing ones as dropped\n\t\tfor (i = (cl.lastackedmovesequence+1) & UPDATE_MASK; i != newmod; i=(i+1)&UPDATE_MASK)\n\t\t{\n//nq has no concept of choking. outbound packets that are accepted during a single frame will be erroneoulsy considered dropped. nq never had a netgraph based upon outgoing timings.\n//\t\t\tCon_Printf(\"Dropped moveframe %i\\n\", i);\n\t\t\tif (cls.protocol == CP_NETQUAKE && CPNQ_IS_DP)\n\t\t\t{\t//dp doesn't ack every single packet. trying to report packet loss correctly is futile, we'll just get bad-mouthed.\n\t\t\t\tcl.outframes[i].latency = -2;\t//flag as choked\n\t\t\t}\n\t\t\telse\n\t\t\t\tcl.outframes[i].latency = -1;\t//flag as dropped\n\t\t}\n\t}\n\tcl.inframes[inseq&UPDATE_MASK].ackframe = outseq;\n\tif (worldstateokay)\n\t\tcl.ackedmovesequence = outseq;\n\tcl.lastackedmovesequence = outseq;\n}\n\n//=============================================================================\n\nint CL_IsDownloading(const char *localname)\n{\n\tdownloadlist_t *dl;\n\t/*check for dupes*/\n\tfor (dl = cl.downloadlist; dl; dl = dl->next)\t//It's already on our list. Ignore it.\n\t{\n\t\tif (!strcmp(dl->localname, localname))\n\t\t\treturn 2;\t//queued\n\t}\n\n\tif (cls.download)\n\t\tif (!strcmp(cls.download->localname, localname))\n\t\t\treturn 1;\t//downloading\n\treturn 0;\n}\n\n//note: this will overwrite existing files.\n//returns true if the download is going to be downloaded after the call.\nqboolean CL_EnqueDownload(const char *filename, const char *localname, unsigned int flags)\n{\n\textern cvar_t cl_downloads;\n\tdownloadlist_t *dl;\n\tqboolean webdl = false;\n\tchar ext[8];\n\tif ((flags & DLLF_TRYWEB) || !strncmp(filename, \"http://\", 7) || !strncmp(filename, \"https://\", 8))\n\t{\n\t\tflags |= DLLF_TRYWEB;\n\t\tif (!localname)\n\t\t\treturn false;\n\n\t\twebdl = true;\n\t}\n\telse\n\t{\n\t\tif (!localname)\n\t\t\tlocalname = filename;\n\n\t\tif (cls.state < ca_connected)\n\t\t\treturn false;\n\t\tif (cls.demoplayback && !(cls.demoplayback == DPB_MVD && (cls.demoeztv_ext&EZTV_DOWNLOAD)))\n\t\t\treturn false;\n\t}\n\tCOM_FileExtension(localname, ext, sizeof(ext));\n\tif (!stricmp(ext, \"dll\") || !stricmp(ext, \"so\") || strchr(localname, '\\\\') || strchr(localname, ':') || strstr(localname, \"..\"))\n\t{\n\t\tCL_DownloadFailed(filename, NULL, DLFAIL_UNTRIED);\n\t\tCon_Printf(\"Denying download of \\\"%s\\\"\\n\", filename);\n\t\treturn false;\n\t}\n\n\tif (!(flags & DLLF_USEREXPLICIT) && !cl_downloads.ival)\n\t{\n\t\tCL_DownloadFailed(filename, NULL, DLFAIL_CLIENTCVAR);\n\t\tif (flags & DLLF_VERBOSE)\n\t\t\tCon_Printf(\"cl_downloads setting prevents download of \\\"%s\\\"\\n\", filename);\n\t\treturn false;\n\t}\n\n\t/*reject if it already failed*/\n\tif (!(flags & DLLF_IGNOREFAILED))\n\t{\n#ifdef NQPROT\n\t\tif (!webdl && cls.protocol == CP_NETQUAKE)\n\t\t\tif (!cl_dp_serverextension_download)\n\t\t\t{\n\t\t\t\tCL_DownloadFailed(filename, NULL, DLFAIL_UNSUPPORTED);\n\t\t\t\treturn false;\n\t\t\t}\n#endif\n\n\t\tfor (dl = cl.faileddownloads; dl; dl = dl->next)\t//yeah, so it failed... Ignore it.\n\t\t{\n\t\t\tif (!strcmp(dl->rname, filename))\n\t\t\t{\n\t\t\t\tif (flags & DLLF_VERBOSE)\n\t\t\t\t\tCon_Printf(\"We've failed to download \\\"%s\\\" already\\n\", filename);\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t}\n\n\t/*check for dupes*/\n\tswitch(CL_IsDownloading(localname))\n\t{\n\tcase 2:\n\t\tif (flags & DLLF_VERBOSE)\n\t\t\tCon_Printf(\"Already waiting for \\\"%s\\\"\\n\", filename);\n\t\treturn true;\n\tdefault:\n\tcase 1:\n\t\tif (flags & DLLF_VERBOSE)\n\t\t\tCon_Printf(\"Already downloading \\\"%s\\\"\\n\", filename);\n\t\treturn true;\n\tcase 0:\n\t\tbreak;\n\t}\n\n\tif (!*filename)\n\t{\n\t\tCon_Printf(\"Download \\\"\\\"? Huh?\\n\");\n\t\treturn true;\n\t}\n\n\tdl = Z_Malloc(sizeof(downloadlist_t));\n\tQ_strncpyz(dl->rname, filename, sizeof(dl->rname));\n\tQ_strncpyz(dl->localname, localname, sizeof(dl->localname));\n\tdl->next = cl.downloadlist;\n\tdl->size = 0;\n\tdl->flags = flags | DLLF_SIZEUNKNOWN;\n\n\tif (!cl.downloadlist)\n\t\tflags &= ~DLLF_VERBOSE;\n\n\tcl.downloadlist = dl;\n\n\tif (!webdl && (cls.fteprotocolextensions & (PEXT_CHUNKEDDOWNLOADS\n#ifdef PEXT_PK3DOWNLOADS\n\t\t| PEXT_PK3DOWNLOADS\n#endif\n\t\t)) && !(dl->flags & DLLF_TEMPORARY))\n\t{\n\t\tCL_SendClientCommand(true, \"dlsize \\\"%s\\\"\", dl->rname);\n\t}\n\n\tif (flags & DLLF_VERBOSE)\n\t\tCon_Printf(\"Enqued download of \\\"%s\\\"\\n\", filename);\n\n\treturn true;\n}\n\nvoid CL_GetDownloadSizes(unsigned int *filecount, qofs_t *totalsize, qboolean *somesizesunknown)\n{\n\tdownloadlist_t *dl;\n\tqdownload_t *d;\n\t*filecount = 0;\n\t*totalsize = 0;\n\t*somesizesunknown = false;\n\tfor(dl = cl.downloadlist; dl; dl = dl->next)\n\t{\n\t\t*filecount += 1;\n\t\tif (dl->flags & DLLF_SIZEUNKNOWN)\n\t\t\t*somesizesunknown = true;\n\t\telse\n\t\t\t*totalsize += dl->size;\n\t}\n\n\td = cls.download;\n\tif (d)\n\t{\n\t\tif (d->sizeunknown)\n\t\t\t*somesizesunknown = true;\n\t\t*totalsize += d->size;\n\t}\n}\n\nstatic void CL_DisenqueDownload(char *filename)\n{\n\tdownloadlist_t *dl, *nxt;\n\tif(cl.downloadlist)\t//remove from enqued download list\n\t{\n\t\tif (!strcmp(cl.downloadlist->rname, filename))\n\t\t{\n\t\t\tdl = cl.downloadlist;\n\t\t\tcl.downloadlist = cl.downloadlist->next;\n\t\t\tZ_Free(dl);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tfor (dl = cl.downloadlist; dl->next; dl = dl->next)\n\t\t\t{\n\t\t\t\tif (!strcmp(dl->next->rname, filename))\n\t\t\t\t{\n\t\t\t\t\tnxt = dl->next->next;\n\t\t\t\t\tZ_Free(dl->next);\n\t\t\t\t\tdl->next = nxt;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n#ifdef WEBCLIENT\nstatic void CL_WebDownloadFinished(struct dl_download *dl)\n{\n\tif (dl->status == DL_FAILED)\n\t{\n\t\tif (dl->replycode == 404)\t//regular file-not-found\n\t\t\tCL_DownloadFailed(dl->url, &dl->qdownload, DLFAIL_SERVERFILE);\n\t\telse\t//other stuff is PROBABLY 403forbidden, but lets blame the server's config if its a tls issue etc.\n\t\t\tCL_DownloadFailed(dl->url, &dl->qdownload, DLFAIL_SERVERCVAR);\n\t\tif (dl->qdownload.flags & DLLF_ALLOWWEB)\t//re-enqueue it if allowed, but this time not from the web server.\n\t\t\tCL_EnqueDownload(dl->qdownload.localname, dl->qdownload.localname, dl->qdownload.flags & ~(DLLF_ALLOWWEB|DLLF_TRYWEB));\n\t}\n\telse if (dl->status == DL_FINISHED)\n\t{\n\t\tif (dl->file)\n\t\t\tVFS_CLOSE(dl->file);\n\t\tdl->file = NULL;\n\t\tCL_DownloadFinished(&dl->qdownload);\n\t}\n}\n#endif\n\nstatic void CL_SendDownloadStartRequest(downloadlist_t *pending)\n{\n\tchar *filename = pending->rname;\n\tchar *localname = pending->localname;\n\tunsigned int flags = pending->flags;\n\tstatic int dlsequence;\n\tqdownload_t *dl;\n\n\t//don't download multiple things at once... its leaky if nothing else.\n\tif (cls.download)\n\t\treturn;\n\n#ifdef WEBCLIENT\n\tif (flags & DLLF_TRYWEB)\n\t{\n\t\tstruct dl_download *wdl = HTTP_CL_Get(filename, localname, CL_WebDownloadFinished);\n\t\tif (wdl)\n\t\t{\n\t\t\tif (flags & DLLF_NONGAME)\n\t\t\t{\n\t\t\t\twdl->fsroot = FS_ROOT;\n\t\t\t\tif (!strncmp(localname, \"package/\", 8))\n\t\t\t\t\tQ_strncpyz(wdl->localname, localname+8, sizeof(wdl->localname));\n\t\t\t}\n\t\t\tif (!(flags & DLLF_TEMPORARY))\n\t\t\t\tCon_TPrintf (\"Downloading %s to %s...\\n\", wdl->url, wdl->localname);\n\t\t\twdl->qdownload.flags = flags;\n\n\t\t\tCL_DisenqueDownload(filename);\n\n\t\t\tcls.download = &wdl->qdownload;\n\t\t}\n\t\telse\n\t\t\tCL_DownloadFailed(filename, NULL, DLFAIL_CLIENTCVAR);\n\t\treturn;\n\t}\n#endif\n\t\n\tdl = Z_Malloc(sizeof(*dl));\n\tdl->filesequence = ++dlsequence;\n\n\tQ_strncpyz(dl->remotename, filename, sizeof(dl->remotename));\n\tQ_strncpyz(dl->localname, localname, sizeof(dl->localname));\n\tif (!(flags & DLLF_TEMPORARY))\n\t\tCon_TPrintf (\"Downloading %s...\\n\", dl->localname);\n\n\t// download to a temp name, and only rename\n\t// to the real name when done, so if interrupted\n\t// a runt file wont be left\n\tCOM_StripExtension (localname, dl->tempname, sizeof(dl->tempname)-5);\n\tQ_strncatz (dl->tempname, \".tmp\", sizeof(dl->tempname));\n\n#ifdef AVAIL_ZLIB\n\tif (cls.protocol == CP_QUAKE2 && cls.protocol_q2 == PROTOCOL_VERSION_R1Q2)\n\t\tCL_SendClientCommand(true, \"download %s 0 udp-zlib\", filename);\n\telse\n#endif\n\t\tCL_SendClientCommand(true, \"download %s\", filename);\n\n\tdl->method = DL_QWPENDING;\n\tdl->percent = 0;\n\tdl->sizeunknown = true;\n\tdl->flags = flags&DLLF_OVERWRITE;\n\n\tCL_DisenqueDownload(filename);\n\n\tcls.download = dl;\n}\n\n//Do any reloading for the file that just reloaded.\nvoid CL_DownloadFinished(qdownload_t *dl)\n{\n\tint i;\n\tchar ext[8];\n\n\tchar filename[MAX_QPATH];\n\tchar tempname[MAX_QPATH];\n\n\tQ_strncpyz(filename, dl->localname, sizeof(filename));\n\tQ_strncpyz(tempname, dl->tempname, sizeof(tempname));\n\n\tDL_Abort(dl, QDL_COMPLETED);\n\n\tFS_FlushFSHashWritten(filename);\n\n\tCOM_FileExtension(filename, ext, sizeof(ext));\n\n\n\t//should probably ask the filesytem code if its a package format instead.\n\tif (!strncmp(filename, \"package/\", 8) || !strncmp(ext, \"pk4\", 3) || !strncmp(ext, \"pk3\", 3) || !strncmp(ext, \"pak\", 3) || (dl->fsroot == FS_ROOT))\n\t{\n\t\tFS_ReloadPackFiles();\n\t\tCL_CheckServerInfo();\n\t}\n\telse if (!strcmp(filename, \"gfx/palette.lmp\"))\n\t{\n\t\tCbuf_AddText(\"vid_restart\\n\", RESTRICT_LOCAL);\n\t}\n\telse\n\t{\n\t\tCL_CheckModelResources(filename);\n\n\t\tMod_FileWritten(filename);\n\t\t{\n\t\t\tfor (i = 0; i < MAX_PRECACHE_MODELS; i++)\t//go and load this model now.\n\t\t\t{\n\t\t\t\tif (cl.model_name[i] && !strcmp(cl.model_name[i], filename))\n\t\t\t\t{\n\t\t\t\t\tif (cl.model_precache[i] && cl.model_precache[i]->loadstate == MLS_FAILED)\n\t\t\t\t\t\tcl.model_precache[i]->loadstate = MLS_NOTLOADED;\n\t\t\t\t\tCL_CheckModelResources(cl.model_name[i]);\n\t\t\t\t\tcl.model_precache[i] = Mod_ForName(cl.model_name[i], ((i==1)?MLV_WARNSYNC:MLV_WARN));\n\t\t\t\t\tif (i == 1)\n\t\t\t\t\t{\n\t\t\t\t\t\tcl.worldmodel = cl.model_precache[i];\n\t\t\t\t\t\t//just in case.\n\t\t\t\t\t\tif (cl.model_precache[1] && cl.model_precache[1]->loadstate == MLS_LOADED)\n\t\t\t\t\t\t\tFS_LoadMapPackFile(cl.model_precache[1]->name, cl.model_precache[1]->archive);\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tfor (i = 0; i < MAX_CSMODELS; i++)\t//go and load this model now.\n\t\t\t{\n\t\t\t\tif (!strcmp(cl.model_csqcname[i], filename))\n\t\t\t\t{\n\t\t\t\t\tif (cl.model_csqcprecache[i] && cl.model_csqcprecache[i]->loadstate == MLS_FAILED)\n\t\t\t\t\t\tcl.model_csqcprecache[i]->loadstate = MLS_NOTLOADED;\n\t\t\t\t\tCL_CheckModelResources(cl.model_csqcname[i]);\n\t\t\t\t\tcl.model_csqcprecache[i] = Mod_ForName(cl.model_csqcname[i], MLV_WARN);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n#ifdef HAVE_LEGACY\n\t\t\tfor (i = 0; i < MAX_VWEP_MODELS; i++)\n\t\t\t{\n\t\t\t\tif (cl.model_name_vwep[i] && !strcmp(cl.model_name_vwep[i], filename))\n\t\t\t\t{\n\t\t\t\t\tif (cl.model_precache_vwep[i] && cl.model_precache_vwep[i]->loadstate == MLS_FAILED)\n\t\t\t\t\t\tcl.model_precache_vwep[i]->loadstate = MLS_NOTLOADED;\n\t\t\t\t\tCL_CheckModelResources(cl.model_name_vwep[i]);\n\t\t\t\t\tcl.model_precache_vwep[i] = Mod_ForName(cl.model_name_vwep[i], MLV_WARN);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n#endif\n\t\t}\n\t\tS_ResetFailedLoad();\t//okay, so this can still get a little spammy in bad places...\n\n#ifdef QWSKINS\n\t\t//this'll do the magic for us\n\t\tSkin_FlushSkin(filename);\n#endif\n\t}\n}\n\nstatic qboolean CL_CheckFile(const char *filename)\n{\n\tif (strstr (filename, \"..\"))\n\t{\n\t\tCon_TPrintf (\"Refusing to download a path with ..\\n\");\n\t\treturn true;\n\t}\n\n\tif (COM_FCheckExists (filename))\n\t{\t// it exists, no need to download\n\t\treturn true;\n\t}\n\treturn false;\n}\n\nqboolean CL_CheckDLFile(const char *filename)\n{\n\tif (!strncmp(filename, \"package/\", 8))\n\t{\n\t\tvfsfile_t *f;\n\t\tf = FS_OpenVFS(filename+8, \"rb\", FS_ROOT);\n\t\tif (f)\n\t\t{\n\t\t\tVFS_CLOSE(f);\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\telse\n\t\treturn COM_FCheckExists(filename);\n}\n/*\n===============\nCL_CheckOrEnqueDownloadFile\n\nReturns true if the file exists, returns false if it triggered a download.\n===============\n*/\n\nqboolean\tCL_CheckOrEnqueDownloadFile (const char *filename, const char *localname, unsigned int flags)\n{\t//returns false if we don't have the file yet.\n\tCOM_AssertMainThread(\"CL_CheckOrEnqueDownloadFile\");\n\tif (flags & DLLF_NONGAME)\n\t{\n\t\t/*pak/pk3 downloads have an explicit leading package/ as an internal/network marker*/\n\t\tif (!strchr(filename, ':'))\n\t\t\tfilename = va(\"package/%s\", filename);\n\t\tlocalname = va(\"package/%s\", localname);\n\t}\n\t/*files with a leading * should not be downloaded (inline models, sexed sounds, etc). also block anyone trying to explicitly download a package/ because our code (wrongly) uses that name internally*/\n\telse if (*filename == '*' || !strncmp(filename, \"package/\", 8))\n\t\treturn true;\n\n\tif (!localname)\n\t\tlocalname = filename;\n\n#ifndef CLIENTONLY\n\t/*no downloading if we're the one we'd be downloading from*/\n\tif (sv.state)\n\t\treturn true;\n#endif\n\n\tif (!(flags & DLLF_OVERWRITE))\n\t{\n\t\tif (CL_CheckDLFile(localname))\n\t\t\treturn true;\n\t}\n\n\t//ZOID - can't download when recording\n\tif (cls.demorecording)\n\t{\n\t\tCon_TPrintf (\"Unable to download %s in record mode.\\n\", filename);\n#if defined(MVD_RECORDING) && defined(HAVE_SERVER)\n\t\tif (sv_demoAutoRecord.ival)\n\t\t\tCon_TPrintf (\"Note that ^[%s\\\\cmd\\\\%s 0\\\\^] is enabled.\\n\", sv_demoAutoRecord.name, sv_demoAutoRecord.name);\n#endif\n\t\treturn true;\n\t}\n\t//ZOID - can't download when playback\n//\tif (cls.demoplayback && cls.demoplayback != DPB_EZTV)\n//\t\treturn true;\n\n\tSCR_EndLoadingPlaque();\t//release console.\n\n\tif (flags & DLLF_ALLOWWEB)\n\t{\n\t\tconst char *dlURL = InfoBuf_ValueForKey(&cl.serverinfo, \"sv_dlURL\");\n\t\tif (!*dlURL)\n\t\t\tdlURL = cls.downloadurl;\n\t\tif (!*dlURL)\n\t\t\tdlURL = fs_dlURL.string;\n\t\tif (strncmp(dlURL, \"http://\", 7) && strncmp(dlURL, \"https://\", 8))\n\t\t\tdlURL = \"\";\t//only allow http+https here. just paranoid.\n\t\tflags &= ~(DLLF_TRYWEB|DLLF_ALLOWWEB);\n\t\tif (*dlURL && (flags & DLLF_NONGAME) && !strncmp(filename, \"package/\", 8))\n\t\t{\t//filename is something like: package/GAMEDIR/foo.pk3\n\t\t\tfilename = va(\"%s%s%s\", dlURL, ((dlURL[strlen(dlURL)-1]=='/')?\"\":\"/\"), filename+8);\n\t\t\tflags |= DLLF_TRYWEB|DLLF_ALLOWWEB;\n\t\t}\n\t\telse if (*dlURL)\n\t\t{\t//we don't really know which gamedir its meant to be for...\n#ifdef Q2CLIENT\t//ffs\n\t\t\tif (!strncmp(filename, \"pics/../\", 8))\n\t\t\t\tfilename += 8;\n#endif\n\t\t\tfilename = va(\"%s%s%s/%s\", dlURL, ((dlURL[strlen(dlURL)-1]=='/')?\"\":\"/\"), FS_GetGamedir(true), filename);\n\t\t\tflags |= DLLF_TRYWEB|DLLF_ALLOWWEB;\n\t\t}\n\t\telse if (*cl_download_mapsrc.string &&\n\t\t\t!strcmp(filename, localname) &&\n\t\t\t!strncmp(filename, \"maps/\", 5) &&\n\t\t\t!strcmp(filename + strlen(filename)-4, \".bsp\"))\n\t\t{\n\t\t\tchar base[MAX_QPATH];\n\t\t\tCOM_FileBase(filename, base, sizeof(base));\n#ifndef FTE_TARGET_WEB //don't care about prefixes in the web build, for site-relative uris.\n\t\t\tif (strncmp(cl_download_mapsrc.string, \"http://\", 7) && strncmp(cl_download_mapsrc.string, \"https://\", 8))\n\t\t\t{\n\t\t\t\tCon_Printf(\"%s: Scheme not specified, assuming https.\\n\", cl_download_mapsrc.name);\n\t\t\t\tfilename = va(\"https://%s/%s\", cl_download_mapsrc.string, filename+5);\n\t\t\t}\n\t\t\telse\n#endif\n\t\t\t\tfilename = va(\"%s%s\", cl_download_mapsrc.string, filename+5);\n\t\t\tflags |= DLLF_TRYWEB|DLLF_ALLOWWEB;\n\t\t}\n\t}\n\n\tif (!CL_EnqueDownload(filename, localname, flags))\n\t\treturn true;\t/*don't stall waiting for it if it failed*/\n\n\n\tif (!(flags & DLLF_IGNOREFAILED))\n\t{\n\t\tdownloadlist_t *dl;\n\t\tfor (dl = cl.faileddownloads; dl; dl = dl->next)\n\t\t{\n\t\t\tif (!strcmp(dl->rname, filename))\n\t\t\t{\n\t\t\t\t//if its on the failed list, don't block waiting for it to download\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t}\n\treturn false;\n}\n\n\n\nstatic qboolean CL_CheckMD2Skins (qbyte *precache_model)\n{\n\tqboolean ret = false;\n\tmd2_t *pheader;\n\tint skin = 1;\n\tchar *str;\n\n\tpheader = (md2_t *)precache_model;\n\tif (LittleLong (pheader->version) != MD2ALIAS_VERSION)\n\t{\n\t\t//bad version.\n\t\treturn false;\n\t}\n\n\tpheader = (md2_t *)precache_model;\n\tfor (skin = 0; skin < LittleLong(pheader->num_skins); skin++)\n\t{\n\t\tstr = (char *)precache_model +\n\t\t\tLittleLong(pheader->ofs_skins) +\n\t\t\tskin*MD2MAX_SKINNAME;\n\t\tCOM_CleanUpPath(str);\n\t\tif (!CL_CheckOrEnqueDownloadFile(str, str, 0))\n\t\t\tret = true;\n\t}\n\treturn ret;\n}\n\nstatic qboolean CL_CheckHLBspWads(char *file)\n{\n\tlump_t lump;\n\tdheader_t *dh;\n\tchar *s;\n\tchar *w;\n\tchar key[256];\n\tchar wads[4096];\n\tdh = (dheader_t *)file;\n\n\tlump.fileofs = LittleLong(dh->lumps[LUMP_ENTITIES].fileofs);\n\tlump.filelen = LittleLong(dh->lumps[LUMP_ENTITIES].filelen);\n\n\ts = file + lump.fileofs;\n\n\ts = COM_Parse(s);\n\tif (strcmp(com_token, \"{\"))\n\t\treturn false;\n\n\twhile (*s)\n\t{\n\t\ts = COM_ParseOut(s, key, sizeof(key));\n\t\tif (!strcmp(key, \"}\"))\n\t\t\tbreak;\n\n\t\ts = COM_ParseOut(s, wads, sizeof(wads));\n\n\t\tif (!strcmp(key, \"wad\"))\n\t\t{\n\t\t\ts = wads;\n\t\t\twhile ((s = COM_ParseToken(s, \";\")))\n\t\t\t{\n\t\t\t\tif (!strcmp(com_token, \";\"))\n\t\t\t\t\tcontinue;\n\t\t\t\twhile ((w = strchr(com_token, '\\\\')))\n\t\t\t\t\t*w = '/';\n\t\t\t\tw = COM_SkipPath(com_token);\n\t\t\t\tif (!CL_CheckFile(w))\n\t\t\t\t{\n\t\t\t\t\tCon_Printf(\"missing wad: %s\\n\", w);\n\t\t\t\t\tCL_CheckOrEnqueDownloadFile(va(\"textures/%s\", w), NULL, DLLF_REQUIRED);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\t}\n\treturn false;\n}\n\nstatic qboolean CL_CheckQ2BspWals(char *file)\n{\n\tqboolean gotone = false;\n#ifdef Q2BSPS\n\tq2dheader_t *dh;\n\tlump_t lump;\n\tq2texinfo_t *tinf;\n\tunsigned int i, j, count;\n\n\tdh = (q2dheader_t*)file;\n\tif (LittleLong(dh->version) != BSPVERSION_Q2)\n\t{\n\t\t//quake3? unknown?\n\t\treturn false;\n\t}\n\tlump.fileofs = LittleLong(dh->lumps[Q2LUMP_TEXINFO].fileofs);\n\tlump.filelen = LittleLong(dh->lumps[Q2LUMP_TEXINFO].filelen);\n\n\tcount = lump.filelen / sizeof(*tinf);\n\tif (lump.filelen != count*sizeof(*tinf))\n\t\treturn false;\n\n\t//grab the appropriate palette, just in case... but only if this won't confuse anything.\n\tif (CL_CheckDLFile(\"gfx/palette.lmp\"))\n\t\tif (!CL_CheckOrEnqueDownloadFile(\"pics/colormap.pcx\", NULL, 0))\n\t\t\tgotone = true;\n\n\ttinf = (q2texinfo_t*)(file + lump.fileofs);\n\tfor (i = 0; i < count; i++)\n\t{\n\t\t//ignore duplicate files (to save filesystem hits)\n\t\tfor (j = 0; j < i; j++)\n\t\t\tif (!strcmp(tinf[i].texture, tinf[j].texture))\n\t\t\t\tbreak;\n\n\t\tif (i == j)\n\t\t{\t//note: we do support formats other than .wal but we still need the .wal to figure out the correct scaling.\n\t\t\t//we make a special exception for .tga-without-.wal because other q2 engines already expect that, with pre-scaled textures (and thus lightmaps too).\n\t\t\tif (!CL_CheckDLFile(va(\"textures/%s.wal\", tinf[i].texture)))\n\t\t\t\tif (!CL_CheckDLFile(va(\"textures/%s.tga\", tinf[i].texture)))\n\t\t\t\t\tif (!CL_CheckOrEnqueDownloadFile(va(\"textures/%s.wal\", tinf[i].texture), NULL, DLLF_ALLOWWEB))\n\t\t\t\t\t\tgotone = true;\n\t\t}\n\t}\n\n\t//FIXME: parse entity lump for sky name.\n#endif\n\treturn gotone;\n}\n\nstatic qboolean CL_CheckModelResources (char *name)\n{\n\t//returns true if we triggered a download\n\tqboolean ret;\n\tqbyte *file;\n\n\tif (!(strstr(name, \".md2\") || strstr(name, \".bsp\")))\n\t\treturn false;\n\n\t// checking for skins in the model\n\n\tFS_LoadFile(name, (void **)&file);\n\tif (!file)\n\t{\n\t\treturn false; // couldn't load it\n\t}\n\tif (!memcmp(file, MD2IDALIASHEADER))\n\t\tret = CL_CheckMD2Skins(file);\n\telse if (!memcmp(file, BSPVERSIONHL))\n\t\tret = CL_CheckHLBspWads(file);\n\telse if (!memcmp(file, IDBSPHEADER))\n\t\tret = CL_CheckQ2BspWals(file);\n\telse\n\t\tret = false;\n\tFS_FreeFile(file);\n\n\treturn ret;\n}\n\n/*\n=================\nModel_NextDownload\n=================\n*/\nstatic void Model_CheckDownloads (void)\n{\n\tchar\t*s;\n\tint\t\ti;\n\tchar ext[8];\n\n//\tCon_TPrintf (TLC_CHECKINGMODELS);\n\n#ifdef Q2CLIENT\n\tif (cls.protocol == CP_QUAKE2)\n\t{\n\t\tfor (i = 0; i < Q2MAX_IMAGES; i++)\n\t\t{\n\t\t\tchar picname[256];\n\t\t\tif (!cl.image_name[i] || !*cl.image_name[i])\n\t\t\t\tcontinue;\n#if defined(HAVE_LEGACY)\n\t\t\tQ_snprintfz(picname, sizeof(picname), \"pics/%s.png\", cl.image_name[i]);\n\t\t\tif (COM_FCheckExists(picname))\n\t\t\t\treturn;\n#endif\n\t\t\tQ_snprintfz(picname, sizeof(picname), \"pics/%s.pcx\", cl.image_name[i]);\n\t\t\tif (!strncmp(cl.image_name[i], \"../\", 3))\t//some servers are just awkward.\n\t\t\t\tCL_CheckOrEnqueDownloadFile(picname, picname+8, DLLF_ALLOWWEB);\n\t\t\telse\n\t\t\t\tCL_CheckOrEnqueDownloadFile(picname, picname, DLLF_ALLOWWEB);\n\t\t}\n\t\tif (!CLQ2_RegisterTEntModels())\n\t\t\treturn;\n\t}\n#endif\n\n\tfor (i = 1; i < countof(cl.model_name) && cl.model_name[i]; i++)\n\t{\n\t\ts = cl.model_name[i];\n\t\tif (s[0] == '*')\n\t\t\tcontinue;\t// inline brush model\n\n\t\tif (!stricmp(COM_FileExtension(s, ext, sizeof(ext)), \"dsp\"))\t//doom sprites are weird, and not really downloadable via this system\n\t\t\tcontinue;\n\n#ifdef Q2CLIENT\n\t\tif (cls.protocol == CP_QUAKE2 && s[0] == '#')\t//this is a vweap\n\t\t\tcontinue;\n#endif\n\n\t\tCL_CheckOrEnqueDownloadFile(s, s, ((i==1)?DLLF_REQUIRED:0)|DLLF_ALLOWWEB);\t//world is required to be loaded.\n\t\tCL_CheckModelResources(s);\n\t}\n\n#ifdef HAVE_LEGACY\n\tfor (i = 0; i < MAX_VWEP_MODELS; i++)\n\t{\n\t\ts = cl.model_name_vwep[i];\n\t\tif (!s)\n\t\t\tcontinue;\n\t\tif (!stricmp(COM_FileExtension(s, ext, sizeof(ext)), \"dsp\"))\t//doom sprites are weird, and not really downloadable via this system\n\t\t\tcontinue;\n\n\t\tCL_CheckOrEnqueDownloadFile(s, s, DLLF_ALLOWWEB);\n\t\tCL_CheckModelResources(s);\n\t}\n#endif\n}\n\nstatic int CL_LoadModels(int stage, qboolean dontactuallyload)\n{\n\tint i;\n\n\tfloat giveuptime = Sys_DoubleTime()+1;\t//small things get padded into a single frame\n\n#define atstage() ((cl.contentstage == stage++ && !dontactuallyload)?true:false)\n#define endstage() ++cl.contentstage;if (!cls.timedemo && giveuptime<Sys_DoubleTime()) return -1\n#define skipstage() if (atstage())++cl.contentstage;else\n\n\tpmove.numphysent = 0;\n\tpmove.physents[0].model = NULL;\n\n#if defined(CSQC_DAT) && defined(NQPROT)\n\tif (cls.protocol == CP_NETQUAKE && atstage())\n\t{\t//we only need this for nq. for qw we checked for downloads with the other stuff.\n\t\t//there are also too many possible names to load... :(\n\t\textern cvar_t  cl_nocsqc;\n\t\tif (!cl_nocsqc.ival && !cls.demoplayback)\n\t\t{\n\t\t\tconst char *cscrc = InfoBuf_ValueForKey(&cl.serverinfo, \"*csprogs\");\n\t\t\tconst char *cssize = InfoBuf_ValueForKey(&cl.serverinfo, \"*csprogssize\");\n\t\t\tconst char *csname = InfoBuf_ValueForKey(&cl.serverinfo, \"*csprogsname\");\n\t\t\tunsigned int chksum = strtoul(cscrc, NULL, 0);\n\t\t\tsize_t chksize = strtoul(cssize, NULL, 0);\n\t\t\tSCR_SetLoadingFile(\"csprogs\");\n\t\t\tif (!*csname)\n\t\t\t\tcsname = \"csprogs.dat\";\n\t\t\tif (*cscrc && !CSQC_CheckDownload(csname, chksum, chksize))\t//only allow csqc if the server says so, and the 'checksum' matches.\n\t\t\t{\n\t\t\t\textern cvar_t cl_download_csprogs;\n\t\t\t\tunsigned int chksum = strtoul(cscrc, NULL, 0);\n\t\t\t\tif (cl_download_csprogs.ival)\n\t\t\t\t{\n\t\t\t\t\tchar *str = va(\"csprogsvers/%x.dat\", chksum);\n\t\t\t\t\tif (CL_IsDownloading(str))\n\t\t\t\t\t\treturn -1;\t//don't progress to loading it while we're still downloading it.\n\t\t\t\t\tif (CL_CheckOrEnqueDownloadFile(csname, str, DLLF_REQUIRED))\n\t\t\t\t\t\treturn -1;\t//its kinda required\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tCon_Printf(\"Not downloading csprogs.dat due to %s\\n\", cl_download_csprogs.name);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tendstage();\n\t}\n#endif\n\n#ifdef HLCLIENT\n\tif (atstage())\n\t{\n\t\tSCR_SetLoadingFile(\"hlclient\");\n\t\tCLHL_LoadClientGame();\n\t\tendstage();\n\t}\n#endif\n\n#ifdef CSQC_DAT\n\tif (atstage())\n\t{\n\t\tchar *s;\n\t\tqboolean anycsqc;\n\t\tchar *endptr;\n\t\tunsigned int chksum;\n\t\tsize_t progsize;\n\t\tconst char *progsname;\n\t\tanycsqc = atoi(InfoBuf_ValueForKey(&cl.serverinfo, \"anycsqc\"));\n\t\tif (cls.demoplayback)\n\t\t\tanycsqc = true;\n\t\ts = InfoBuf_ValueForKey(&cl.serverinfo, \"*csprogssize\");\n\t\tprogsize = strtoul(s, NULL, 0);\n\t\ts = InfoBuf_ValueForKey(&cl.serverinfo, \"*csprogs\");\n\t\tchksum = strtoul(s, &endptr, 0);\n\t\tprogsname = InfoBuf_ValueForKey(&cl.serverinfo, \"*csprogsname\");\n\t\tif (*endptr)\n\t\t{\n\t\t\tCon_Printf(\"corrupt *csprogs key in serverinfo\\n\");\n\t\t\tanycsqc = true;\n\t\t\tchksum = 0;\n\t\t}\n\t\tprogsname = *s?InfoBuf_ValueForKey(&cl.serverinfo, \"*csprogsname\"):NULL;\n\t\tSCR_SetLoadingFile(\"csprogs\");\n\t\tif (!CSQC_Init(anycsqc, progsname, chksum, progsize))\n\t\t{\n\t\t\tSbar_Start();\t//try and start this before we're actually on the server,\n\t\t\t\t\t\t\t//this'll stop the mod from sending so much stuffed data at us, whilst we're frozen while trying to load.\n\t\t\t\t\t\t\t//hopefully this'll make it more robust.\n\t\t\t\t\t\t\t//csqc is expected to use it's own huds, or to run on decent servers. :p\n\t\t}\n\t\tendstage();\n\t}\n#endif\n\n\tif (atstage())\n\t{\n\t\tSCR_SetLoadingFile(\"prenewmap\");\n\t\tSurf_PreNewMap();\n\n\t\tendstage();\n\t}\n\n\tif (cl.playerview[0].playernum == -1)\n\t{\t//q2 cinematic - don't load the models.\n\t\tcl.worldmodel = cl.model_precache[1] = Mod_ForName (\"\", MLV_WARN);\n\t}\n\telse\n\t{\n\t\tfor (i=1 ; i<MAX_PRECACHE_MODELS ; i++)\n\t\t{\n\t\t\tif (!cl.model_name[i])\n\t\t\t\tskipstage();\n\t\t\telse if (atstage())\n\t\t\t{\n#if 0\n\t\t\t\tSCR_SetLoadingFile(cl.model_name[i]);\n#ifdef CSQC_DAT\n\t\t\t\tif (i == 1)\n\t\t\t\t\tCSQC_LoadResource(cl.model_name[i], \"map\");\n\t\t\t\telse\n\t\t\t\t\tCSQC_LoadResource(cl.model_name[i], \"model\");\n#endif\n#endif\n#ifdef Q2CLIENT\n\t\t\t\tif (cls.protocol == CP_QUAKE2 && *cl.model_name[i] == '#')\n\t\t\t\t\tcl.model_precache[i] = NULL;\n\t\t\t\telse\n#endif\n\t\t\t\tif (!cls.timedemo && i!=1 && mod_precache.ival != 1)\n\t\t\t\t\tcl.model_precache[i] = Mod_FindName (Mod_FixName(cl.model_name[i], cl.model_name[1]));\n\t\t\t\telse\n\t\t\t\t\tcl.model_precache[i] = Mod_ForName (Mod_FixName(cl.model_name[i], cl.model_name[1]), MLV_WARN);\n\n\t\t\t\tS_ExtraUpdate();\n\n\t\t\t\tendstage();\n\t\t\t}\n\t\t}\n#ifdef HAVE_LEGACY\n\t\tfor (i = 0; i < MAX_VWEP_MODELS; i++)\n\t\t{\n\t\t\tif (!cl.model_name_vwep[i])\n\t\t\t\tskipstage();\n\t\t\telse if (atstage())\n\t\t\t{\n#if 0\n\t\t\t\tSCR_SetLoadingFile(cl.model_name_vwep[i]);\n#ifdef CSQC_DAT\n\t\t\t\tCSQC_LoadResource(cl.model_name_vwep[i], \"vwep\");\n#endif\n#endif\n\t\t\t\tcl.model_precache_vwep[i] = Mod_ForName (cl.model_name_vwep[i], MLV_WARN);\n\t\t\t\tendstage();\n\t\t\t}\n\t\t}\n#endif\n\t}\n\n\n\n\tif (atstage())\n\t{\n\t\tcl.worldmodel = cl.model_precache[1];\n\t\tif (!cl.worldmodel || cl.worldmodel->type == mod_dummy)\n\t\t{\n\t\t\tif (!cl.model_name[1])\n\t\t\t\tHost_EndGame(\"Worldmodel name wasn't sent\\n\");\n//\t\t\telse\n//\t\t\t\treturn stage;\n//\t\t\t\tHost_EndGame(\"Worldmodel wasn't loaded\\n\");\n\t\t}\n\t\t//the worldmodel can take a while to load, so be sure to wait.\n\t\tif (cl.worldmodel && cl.worldmodel->loadstate == MLS_LOADING)\n\t\t\treturn -1;\n\n\t\tFS_LoadMapPackFile(cl.worldmodel->name, cl.worldmodel->archive);\n\n\t\tSCR_SetLoadingFile(\"csprogs world\");\n\n\t\tendstage();\n\t}\n\n\tfor (i=1 ; i<MAX_CSMODELS ; i++)\n\t{\n\t\tif (!cl.model_csqcname[i])\n\t\t\tskipstage();\n\t\telse if (atstage())\n\t\t{\n#if 0\n\t\t\tSCR_SetLoadingFile(cl.model_csqcname[i]);\n#ifdef CSQC_DAT\n\t\t\tif (i == 1)\n\t\t\t\tCSQC_LoadResource(cl.model_csqcname[i], \"map\");\n\t\t\telse\n\t\t\t\tCSQC_LoadResource(cl.model_csqcname[i], \"model\");\n#endif\n#endif\n\t\t\tcl.model_csqcprecache[i] = Mod_ForName (cl.model_csqcname[i], MLV_WARN);\n\n\t\t\tS_ExtraUpdate();\n\n\t\t\tendstage();\n\t\t}\n\t}\n\n\tif (atstage())\n\t{\n\t\tSCR_SetLoadingFile(\"wads\");\n\t\tif (cl.worldmodel && cl.worldmodel->loadstate == MLS_LOADING)\n\t\t\treturn -1;\n\t\tif (cl.worldmodel && cl.worldmodel->loadstate == MLS_LOADING)\n\t\t\tCOM_WorkerPartialSync(cl.worldmodel, &cl.worldmodel->loadstate, MLS_LOADING);\n\t\tMod_ParseInfoFromEntityLump(cl.worldmodel);\n\n\t\tWad_NextDownload();\n\n\t\tendstage();\n\t}\n\n\tif (atstage())\n\t{\n\t\tSCR_SetLoadingFile(\"external textures\");\n\t\tif (cl.worldmodel && cl.worldmodel->loadstate == MLS_LOADING)\n\t\t\tCOM_WorkerPartialSync(cl.worldmodel, &cl.worldmodel->loadstate, MLS_LOADING);\n\t\tCL_CheckServerInfo(); //some serverinfo rules can change with map type, so make sure they're updated now we're sure we know it properly.\n\t\tif (cl.worldmodel && cl.worldmodel->loadstate == MLS_LOADED)\n\t\t\tMod_NowLoadExternal(cl.worldmodel);\n\n/*#ifdef Q2CLIENT\n\t\tif (cls.protocol == CP_QUAKE2 && cl.worldmodel && !cls.demoplayback &&\n\t\t\t\tcl.worldmodel->checksum  != cl.q2mapchecksum &&\n\t\t\t\tcl.worldmodel->checksum2 != cl.q2mapchecksum)\n\t\t\tHost_EndGame(\"Local map version differs from server: %i != '%i'\\n\", cl.worldmodel->checksum2, cl.q2mapchecksum);\n#endif*/\n\n\t\tendstage();\n\t}\n\n\n\t// all done\n\tif (atstage())\n\t{\n\t\tSCR_SetLoadingFile(\"newmap\");\n\n//\t\tif (!cl.worldmodel || cl.worldmodel->type == mod_dummy)\n//\t\t\tHost_EndGame(\"No worldmodel was loaded\\n\");\n\t\tSurf_NewMap (cl.worldmodel);\n\n\t\tpmove.physents[0].model = cl.worldmodel;\n\n\t\tendstage();\n\t}\n\n#ifdef CSQC_DAT\n\tif (atstage())\n\t{\t\t\n\t\tSCR_SetLoadingFile(\"csqc init\");\n\t\tCSQC_WorldLoaded();\n\n\t\tif (CSQC_Inited())\n\t\t{\n\t\t\tif (cls.fteprotocolextensions & PEXT_CSQC)\n\t\t\t\tCL_SendClientCommand(true, \"enablecsqc\");\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (cls.fteprotocolextensions & PEXT_CSQC)\n\t\t\t\tCL_SendClientCommand(true, \"disablecsqc\");\n\t\t}\n\t\tendstage();\n\t}\n#endif\n\n\treturn stage;\n}\n\nstatic int CL_LoadSounds(int stage, qboolean dontactuallyload)\n{\n\tint i;\n\tfloat giveuptime = Sys_DoubleTime()+0.1;\t//small things get padded into a single frame\n\n//#define atstage() ((cl.contentstage == stage++)?++cl.contentstage:false)\n//#define endstage() if (giveuptime<Sys_DoubleTime()) return -1;\n\n\tfor (i=1 ; i<MAX_PRECACHE_SOUNDS ; i++)\n\t{\n\t\tif (!cl.sound_name[i])\n\t\t\tbreak;\n\n\t\tif (atstage())\n\t\t{\n#if 0\n\t\t\tSCR_SetLoadingFile(cl.sound_name[i]);\n#ifdef CSQC_DAT\n\t\t\tCSQC_LoadResource(cl.sound_name[i], \"sound\");\n#endif\n#endif\n\t\t\tcl.sound_precache[i] = S_PrecacheSound (cl.sound_name[i]);\n\n\t\t\tS_ExtraUpdate();\n\t\t\tendstage();\n\t\t}\n\t}\n\treturn stage;\n}\n\nvoid Sound_CheckDownload(const char *s)\n{\n#if defined(HAVE_LEGACY) && defined(AVAIL_OGGVORBIS)\n\tchar mangled[512];\n#endif\n\tif (*s == '*')\t//q2 sexed sound\n\t\treturn;\n\n\tif (!S_HaveOutput())\n\t\treturn;\n\n\t//check without the sound/ prefix\n\tif (CL_CheckFile(s))\n\t\treturn;\t//we have it already\n\n#if defined(HAVE_LEGACY) && defined(AVAIL_OGGVORBIS)\n\t//the things I do for nexuiz... *sigh*\n\tCOM_StripExtension(s, mangled, sizeof(mangled));\n\tCOM_DefaultExtension(mangled, \".ogg\", sizeof(mangled));\n\tif (CL_CheckFile(mangled))\n\t\treturn;\n#endif\n\n\t//check with the sound/ prefix\n\ts = va(\"sound/%s\",s);\n\n\tif (CL_CheckFile(s))\n\t\treturn;\t//we have it already\n\n#if defined(HAVE_LEGACY) && defined(AVAIL_OGGVORBIS)\n\t//the things I do for nexuiz... *sigh*\n\tCOM_StripExtension(s, mangled, sizeof(mangled));\n\tCOM_DefaultExtension(mangled, \".ogg\", sizeof(mangled));\n\tif (CL_CheckFile(mangled))\n\t\treturn;\n#endif\n\t//download the one the server said.\n\tCL_CheckOrEnqueDownloadFile(s, NULL, DLLF_ALLOWWEB);\n}\n\n/*\n=================\nSound_NextDownload\n=================\n*/\nstatic void Sound_CheckDownloads (void)\n{\n\tint\t\ti;\n\n\n//\tCon_TPrintf (TLC_CHECKINGSOUNDS);\n\n#ifdef CSQC_DAT\n//\tif (cls.fteprotocolextensions & PEXT_CSQC)\n\t{\n\t\tchar\t*s;\n\t\ts = InfoBuf_ValueForKey(&cl.serverinfo, \"*csprogs\");\n\t\tif (*s)\t//only allow csqc if the server says so, and the 'checksum' matches.\n\t\t{\n\t\t\textern cvar_t cl_download_csprogs, cl_nocsqc;\n\t\t\tchar *endptr;\n\t\t\tunsigned int chksum = strtoul(s, &endptr, 0);\n\t\t\tif (cl_nocsqc.ival || cls.demoplayback || *endptr)\n\t\t\t{\n\t\t\t}\n\t\t\telse if (cl_download_csprogs.ival)\n\t\t\t{\n\t\t\t\tchar *str = va(\"csprogsvers/%x.dat\", chksum);\n\t\t\t\tCL_CheckOrEnqueDownloadFile(\"csprogs.dat\", str, DLLF_REQUIRED);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tCon_Printf(\"Not downloading csprogs.dat\\n\");\n\t\t\t}\n\t\t}\n\t}\n#endif\n\n\tfor (i = 1; i < countof(cl.model_name) && cl.sound_name[i]; i++)\n\t{\n\t\tSound_CheckDownload(cl.sound_name[i]);\n\t}\n}\n\n/*\n======================\nCL_RequestNextDownload\n======================\n*/\nvoid CL_RequestNextDownload (void)\n{\n\n\tint stage;\n\t/*already downloading*/\n\tif (cls.download && !cls.demoplayback)\n\t\treturn;\n\n\t/*request downloads only if we're at the point where we've received a complete list of them*/\n\tif (cl.sendprespawn || cls.state == ca_active)\n\t{\n\t\tif (cl.downloadlist)\n\t\t{\n\t\t\tdownloadlist_t *dl;\n\n\t\t\t//download required downloads first\n\t\t\tfor (dl = cl.downloadlist; dl; dl = dl->next)\n\t\t\t{\n\t\t\t\tif (dl->flags & DLLF_NONGAME)\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (!dl)\n\t\t\t{\n\t\t\t\tfor (dl = cl.downloadlist; dl; dl = dl->next)\n\t\t\t\t{\n\t\t\t\t\tif (dl->flags & DLLF_REQUIRED)\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tif (!dl)\n\t\t\t\t\tdl = cl.downloadlist;\n\t\t\t}\n\n\t\t\t/*if we don't require downloads don't queue requests until we're actually on the server, slightly more deterministic*/\n\t\t\tif (cls.state == ca_active || (requiredownloads.value && !(cls.demoplayback && !(dl->flags&DLLF_TRYWEB))) || (dl->flags & DLLF_REQUIRED))\n\t\t\t{\n\t\t\t\tif ((dl->flags & DLLF_OVERWRITE) || !CL_CheckFile (dl->localname))\n\t\t\t\t{\n\t\t\t\t\tCL_SendDownloadStartRequest(dl);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t//we already got this file somehow? must have come from a pak or something. don't spam.\n\t\t\t\t\tCon_DPrintf(\"Already have %s\\n\", dl->localname);\n\t\t\t\t\tCL_DisenqueDownload(dl->rname);\n\n\t\t\t\t\t//recurse a bit.\n\t\t\t\t\tCL_RequestNextDownload();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse if (cls.download && requiredownloads.value)\n\t\t\treturn;\n\t}\n\n\tif (cl.sendprespawn)\n\t{\t// get next signon phase\n\t\textern int total_loading_size, current_loading_size;\n\n\t\tif (!cl.contentstage)\n\t\t{\n\t\t\tint pure;\n\t\t\tstage = 0;\n\t\t\tstage = CL_LoadModels(stage, true);\n\t\t\tstage = CL_LoadSounds(stage, true);\n\t\t\ttotal_loading_size = stage;\n\t\t\tcl.contentstage = 0;\n\n\t\t\t//might be safer to do it later, but kinder to do it before wasting time.\n\t\t\tpure = FS_PureOkay();\n\t\t\tif (pure < 0 || (pure==0 && (cls.download || cl.downloadlist)))\n\t\t\t\treturn;\t//we're downloading something and may still be able to satisfy it.\n\t\t\tif (pure == 0 && !cls.demoplayback)\n\t\t\t{\t//failure!\n\t\t\t\tCon_Printf(CON_ERROR\"You are missing pure packages, and they could not be autodownloaded.\\nYou may need to purchase an update.\\n\");\n\t#ifdef HAVE_MEDIA_ENCODER\n\t\t\t\tif (cls.demoplayback && Media_Capturing())\n\t\t\t\t{\n\t\t\t\t\tCon_Printf(CON_ERROR \"Aborting capture\\n\");\n\t\t\t\t\tCL_StopPlayback();\n\t\t\t\t}\n\t#endif\n\t\t\t\tSCR_SetLoadingStage(LS_NONE);\n\t\t\t\tCL_Disconnect(\"Game Content differs from server\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\tstage = 0;\n\t\tstage = CL_LoadModels(stage, false);\n\t\tcurrent_loading_size = cl.contentstage;\n\t\tif (stage < 0)\n\t\t\treturn;\t//not yet\n\t\tstage = CL_LoadSounds(stage, false);\n\t\tcurrent_loading_size = cl.contentstage;\n\t\tif (stage < 0)\n\t\t\treturn;\n\t\t//if (cls.userinfosync.numkeys)\n\t\t//\treturn;\t//don't prespawn until we've actually sent all our initial userinfo.\n\t\tif (requiredownloads.ival && COM_HasWork())\n\t\t{\n\t\t\tSCR_SetLoadingFile(\"loading content\");\n\t\t\treturn;\n\t\t}\n\t\tSCR_SetLoadingFile(\"receiving game state\");\n\n\t\tcl.sendprespawn = false;\n\n\t\tif (cl_splitscreen.ival)\n\t\t{\n\t\t\tif (cls.fteprotocolextensions & PEXT_SPLITSCREEN)\n\t\t\t\t;\n\t\t\telse if (cls.protocol == CP_QUAKE2 && cls.protocol_q2 == PROTOCOL_VERSION_Q2EX)\n\t\t\t\t;\n\t\t\telse\n\t\t\t\tCon_TPrintf(CON_WARNING \"Splitscreen requested but not available on this server.\\n\");\n\t\t}\n\n\t\tif (cl.worldmodel && cl.worldmodel->loadstate == MLS_LOADING)\n\t\t\tCOM_WorkerPartialSync(cl.worldmodel, &cl.worldmodel->loadstate, MLS_LOADING);\n\n\t\tif (!cl.worldmodel || cl.worldmodel->loadstate != MLS_LOADED)\n\t\t{\n\t\t\tdownloadlist_t *dl = NULL;\n\t\t\tconst char *worldname = cl.worldmodel?cl.worldmodel->name:\"unknown\";\n\t\t\tif (cl.worldmodel)\n\t\t\t\tfor (dl = cl.faileddownloads; dl; dl = dl->next)\t//yeah, so it failed... Ignore it.\n\t\t\t\t\tif (!strcmp(dl->rname, cl.worldmodel->name))\n\t\t\t\t\t\tbreak;\n\t\t\tCon_Printf(\"\\n\\n-------------\\n\");\n\t\t\tswitch (dl?dl->failreason:DLFAIL_UNTRIED)\n\t\t\t{\n\t\t\tcase DLFAIL_UNSUPPORTED:\n\t\t\t\tCon_Printf(CON_ERROR \"Download of \\\"%s\\\" not supported on this server - cannot fully connect\\n\", worldname);\n\t\t\t\tbreak;\n\t\t\tcase DLFAIL_CORRUPTED:\n\t\t\t\tCon_Printf(CON_ERROR \"Download of \\\"%s\\\" corrupt/failed - cannot fully connect\\n\", worldname);\n\t\t\t\tbreak;\n\t\t\tcase DLFAIL_CLIENTCVAR:\n\t\t\t\tCon_Printf(CON_ERROR \"Downloading of \\\"%s\\\" blocked by clientside cvars - tweak cl_download* before retrying\\n\", worldname);\n\t\t\t\tbreak;\n\t\t\tcase DLFAIL_CLIENTFILE:\n\t\t\t\tCon_Printf(CON_ERROR \"Disk error downloading \\\"%s\\\" - cannot fully connect\\n\", worldname);\n\t\t\t\tbreak;\n\t\t\tcase DLFAIL_SERVERCVAR:\n\t\t\t\tCon_Printf(CON_ERROR \"Download of \\\"%s\\\" denied by server - cannot fully connect\\n\", worldname);\n\t\t\t\tbreak;\n\t\t\tcase DLFAIL_SERVERFILE:\n\t\t\t\tCon_Printf(CON_ERROR \"Download of \\\"%s\\\" unavailable - cannot fully connect\\n\", worldname);\n\t\t\t\tbreak;\n\t\t\tcase DLFAIL_REDIRECTED:\n\t\t\t\tCon_Printf(CON_ERROR \"Redirection failure downloading \\\"%s\\\" - cannot fully connect\\n\", worldname);\n\t\t\t\tbreak;\n\t\t\tcase DLFAIL_UNTRIED:\n\t\t\t\tif (COM_FCheckExists(worldname))\n\t\t\t\t{\n\t\t\t\t\tif (!cl.worldmodel)\n\t\t\t\t\t\tCon_Printf(CON_ERROR \"Couldn't load \\\"%s\\\" - worldmodel not set - cannot fully connect\\n\", worldname);\n\t\t\t\t\telse if (cl.worldmodel->loadstate == MLS_FAILED)\n\t\t\t\t\t\tCon_Printf(CON_ERROR \"Couldn't load \\\"%s\\\" - corrupt? - cannot fully connect\\n\", worldname);\n\t\t\t\t\telse if (cl.worldmodel->loadstate == MLS_LOADING)\n\t\t\t\t\t\tCon_Printf(CON_ERROR \"Couldn't load \\\"%s\\\" - still loading - cannot fully connect\\n\", worldname);\n\t\t\t\t\telse if (cl.worldmodel->loadstate == MLS_NOTLOADED)\n\t\t\t\t\t\tCon_Printf(CON_ERROR \"Couldn't load \\\"%s\\\" - worldmodel not loaded - cannot fully connect\\n\", worldname);\n\t\t\t\t\telse\n\t\t\t\t\t\tCon_Printf(CON_ERROR \"Couldn't load \\\"%s\\\" - corrupt? - cannot fully connect\\n\", worldname);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tCon_Printf(CON_ERROR \"Couldn't find \\\"%s\\\" - cannot fully connect\\n\", worldname);\n\t\t\t\tbreak;\n\t\t\t}\n#ifdef HAVE_MEDIA_ENCODER\n\t\t\tif (cls.demoplayback && Media_Capturing())\n\t\t\t{\n\t\t\t\tCon_Printf(CON_ERROR \"Aborting capture\\n\");\n\t\t\t\tCL_StopPlayback();\n\t\t\t}\n#endif\n\t\t\t//else should probably force the demo speed really fast or something\n\n\t\t\tSCR_SetLoadingStage(LS_NONE);\n\t\t\treturn;\n\t\t}\n\n\t\tCvar_ForceCallback(Cvar_FindVar(\"r_particlesdesc\"));\n\n#ifdef Q2CLIENT\n\t\tif (cls.protocol == CP_QUAKE2)\n\t\t{\n\t\t\tif (cls.protocol_q2 == PROTOCOL_VERSION_R1Q2)\n\t\t\t{\t//fixme: make dynamic...\n//\t\t\t\tMSG_WriteByte (&cls.netchan.message, clcr1q2_setting);\n//\t\t\t\tMSG_WriteShort (&cls.netchan.message, R1Q2_CLSET_NOGUN);\n//\t\t\t\tMSG_WriteShort (&cls.netchan.message, r_drawviewmodel.value <= 0);\n\n//\t\t\t\tMSG_WriteByte (&cls.netchan.message, clcr1q2_setting);\n//\t\t\t\tMSG_WriteShort (&cls.netchan.message, R1Q2_CLSET_PLAYERUPDATES);\n//\t\t\t\tMSG_WriteShort (&cls.netchan.message, 1);\n\n//\t\t\t\tMSG_WriteByte (&cls.netchan.message, clcr1q2_setting);\n//\t\t\t\tMSG_WriteShort (&cls.netchan.message, R1Q2_CLSET_FPS);\n//\t\t\t\tMSG_WriteShort (&cls.netchan.message, 30);\n\t\t\t}\n\n\t\t\tSkin_NextDownload();\n\t\t\tSCR_SetLoadingStage(LS_NONE);\n\t\t\tCL_SendClientCommand(true, \"begin %i\\n\", cl.servercount);\n\t\t}\n\t\telse\n#endif\n\t\t{\n\t\t\tif (cls.demoplayback == DPB_MVD && cls.demoeztv_ext)\n\t\t\t{\n\t\t\t\tif (CL_RemoveClientCommands(\"qtvspawn\"))\n\t\t\t\t\tCon_DPrintf(\"Multiple prespawns\\n\");\n\t\t\t\tCL_SendClientCommand(true, \"qtvspawn %i 0 %i\", cl.servercount, COM_RemapMapChecksum(cl.worldmodel, LittleLong(cl.worldmodel->checksum2)));\n\t\t\t\tSCR_SetLoadingStage(LS_NONE);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t// done with modellist, request first of static signon messages\n\t\t\t\tif (CL_RemoveClientCommands(\"prespawn\"))\n\t\t\t\t\tCon_DPrintf(\"Multiple prespawns\\n\");\n\t\t\t\tif (cls.protocol == CP_NETQUAKE)\n\t\t\t\t\tCL_SendClientCommand(true, \"prespawn\");\n\t\t\t\telse\n\t\t\t\t{\n\t\t//\t\t\tCL_SendClientCommand(\"prespawn %i 0 %i\", cl.servercount, cl.worldmodel->checksum2);\n\t\t\t\t\tCL_SendClientCommand(true, prespawn_name, cl.servercount, COM_RemapMapChecksum(cl.worldmodel, LittleLong(cl.worldmodel->checksum2)));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\n\t\tif (mod_precache.ival >= 2)\n\t\t{\n\t\t\tint i;\n\t\t\tfor (i=1 ; i<MAX_PRECACHE_MODELS ; i++)\n\t\t\t{\n\t\t\t\tif (cl.model_precache[i] && cl.model_precache[i]->loadstate == MLS_NOTLOADED)\n\t\t\t\t\tMod_LoadModel(cl.model_precache[i], MLV_WARN);\n\t\t\t}\n\t\t}\n\t\tif (snd_precache.ival >= 2)\n\t\t{\n\t\t\tint i;\n\t\t\tfor (i=1 ; i<MAX_PRECACHE_SOUNDS ; i++)\n\t\t\t{\n\t\t\t\tif (cl.sound_precache[i] && cl.sound_precache[i]->loadstate == SLS_NOTLOADED)\n\t\t\t\t\tS_LoadSound(cl.sound_precache[i], false);\n\t\t\t}\n\t\t}\n\t}\n}\n\nint CL_RequestADownloadChunk(void);\nvoid CL_SendDownloadReq(sizebuf_t *msg)\n{\n\tif (cls.demoplayback == DPB_MVD)\n\t\treturn;\t//tcp connection, so no need to constantly ask\n\n\tif (!cls.download)\n\t{\n\t\tif (cl.downloadlist)\n\t\t\tCL_RequestNextDownload();\n\t\treturn;\n\t}\n\n#ifdef PEXT_CHUNKEDDOWNLOADS\n\tif (cls.download->method == DL_QWCHUNKS)\n\t\tDLC_Poll(cls.download);\n#endif\n}\n\n#ifdef PEXT_ZLIBDL\n#include <zlib.h>\n\nstatic char *ZLibDownloadDecode(int *messagesize, char *input, int finalsize)\n{\n\tchar *outbuf = Hunk_TempAlloc(finalsize);\n\tz_stream zs;\n\n\t*messagesize = (*(short*)input);\n\tinput+=2;\n\n\tif (!*messagesize)\n\t{\n\t\t*messagesize = finalsize+2;\n\t\treturn input;\n\t}\n\n\tmemset(&zs, 0, sizeof(zs));\n\n\n\tzs.next_in = input;\n    zs.avail_in = *messagesize;\t//tell it that it has a lot. Possibly a bad idea.\n    zs.total_in = 0;\n\n    zs.next_out = outbuf;\n    zs.avail_out = finalsize;\t//this is the limiter.\n    zs.total_out = 0;\n\n    zs.data_type = Z_BINARY;\n\n\tinflateInit(&zs);\n\tinflate(&zs, Z_FINISH);\t//decompress it in one go.\n\tinflateEnd(&zs);\n\n\t*messagesize = zs.total_in+2;\n\treturn outbuf;\n}\n#endif\n\ndownloadlist_t *CL_DownloadFailed(const char *name, qdownload_t *qdl, enum dlfailreason_e failreason)\n{\n\t//add this to our failed list. (so we don't try downloading it again...)\n\tdownloadlist_t *failed, **link, *dl;\n\tfailed = Z_Malloc(sizeof(downloadlist_t));\n\tfailed->next = cl.faileddownloads;\n\tcl.faileddownloads = failed;\n\tQ_strncpyz(failed->rname, name, sizeof(failed->rname));\n\tfailed->failreason = failreason;\n\n\t//if this is what we're currently downloading, close it up now.\n\t//don't do this if we're just marking the file as unavailable for download.\n\tif (qdl && (!stricmp(qdl->remotename, name) || !*name))\n\t{\n\t\tDL_Abort(qdl, QDL_FAILED);\n\t}\n\n\tlink = &cl.downloadlist;\n\twhile(*link)\n\t{\n\t\tdl = *link;\n\t\tif (!strcmp(dl->rname, name))\n\t\t{\n\t\t\t*link = dl->next;\n\t\t\tfailed->flags |= dl->flags;\n\t\t\tZ_Free(dl);\n\t\t}\n\t\telse\n\t\t\tlink = &(*link)->next;\n\t}\n\n\treturn failed;\n}\n\n#ifdef PEXT_CHUNKEDDOWNLOADS\n\nint CL_DownloadRate(void)\n{\n\tqdownload_t *dl = cls.download;\n\tif (dl)\n\t{\n\t\tdouble curtime = Sys_DoubleTime();\n\t\tif (!dl->ratetime)\n\t\t{\n\t\t\tdl->ratetime = curtime;\n\t\t\treturn dl->completedbytes/(Sys_DoubleTime() - dl->starttime);\n\t\t}\n\t\tif (curtime - dl->ratetime > 1)\n\t\t{\n\t\t\tdl->rate = dl->ratebytes / (curtime - dl->ratetime);\n\t\t\tdl->ratetime = curtime;\n\t\t\tdl->ratebytes = 0;\n\t\t}\n\t\treturn dl->rate;\n\t}\n\treturn 0;\n}\n\n//called when the server acks the download. opens the local file and stuff. returns false on failure\nqboolean DL_Begun(qdownload_t *dl)\n{\n\t//figure out where the file is meant to be going.\n\tdl->prefixbytes = 0;\n\tif (!strncmp(dl->tempname, \"package/\", 8))\n\t{\n\t\tdl->prefixbytes = 8;\t//ignore the package/ part\n\t\tdl->fsroot = FS_ROOT;\t//and put it in the root dir (-basedir), and hope the name includes a gamedir part\n\t}\n\telse if (!strncmp(dl->tempname,\"skins/\",6))\n\t\tdl->fsroot = FS_PUBBASEGAMEONLY;\t//shared between gamedirs, so only use the basegame.\n\telse\n\t\tdl->fsroot = FS_PUBGAMEONLY;//FS_GAMEONLY;\t//other files are relative to the active gamedir.\n\n\tQ_snprintfz(dl->dclname, sizeof(dl->dclname), \"%s.dcl\", dl->tempname);\n\n\tif (dl->method == DL_QWCHUNKS)\n\t{\n\t\tqboolean error = false;\n\t\tchar partline[256];\n\t\tchar partterm[128];\n\t\tchar *p, t;\n\t\tqofs_t lastend = 0;\n\t\tqofs_t start, end;\n\t\tstruct dlblock_s **link = &dl->dlblocks;\n\t\tvfsfile_t *parts = FS_OpenVFS(dl->dclname+dl->prefixbytes, \"rb\", dl->fsroot);\n\t\tif (!parts)\n\t\t\terror = true;\n\t\twhile(!error && VFS_GETS(parts, partline, sizeof(partline)))\n\t\t{\n\t\t\tp = COM_ParseOut(partline, partterm, sizeof(partterm));\n\t\t\tt = *partterm;\n\t\t\tp = COM_ParseOut(p, partterm, sizeof(partterm));\n\t\t\tstart = strtoull(partterm, NULL, 0);\n\t\t\tp = COM_ParseOut(p, partterm, sizeof(partterm));\n\t\t\tend = strtoull(partterm, NULL, 0);\n\n\t\t\t(*link) = Z_Malloc(sizeof(**link));\n\t\t\t(*link)->start = start;\n\t\t\t(*link)->end = end;\n\t\t\t(*link)->state = (t == 'c')?DLB_RECEIVED:DLB_MISSING;\n\t\t\tlink = &(*link)->next;\n\n\t\t\tif (t == 'c')\n\t\t\t\tdl->completedbytes += end - start;\n\n\t\t\tif (start != lastend)\n\t\t\t\terror = true;\n\t\t\tlastend = end;\n\t\t}\n\t\tif (lastend != dl->size)\n\t\t\terror = true;\n\t\tif (parts)\n\t\t\tVFS_CLOSE(parts);\n\t\tif (!error)\n\t\t\tdl->file = FS_OpenVFS(dl->tempname+dl->prefixbytes, \"w+b\", dl->fsroot);\n\t}\n\tif (!dl->file)\n\t{\n\t\tstruct dlblock_s *b;\n\t\t//make sure we don't get confused if someone end-tasks us before the download is complete.\n\t\tFS_Remove(dl->dclname+dl->prefixbytes, dl->fsroot);\n\t\tdl->completedbytes = 0;\n\t\twhile (dl->dlblocks)\n\t\t{\n\t\t\tb = dl->dlblocks;\n\t\t\tdl->dlblocks = b->next;\n\t\t\tZ_Free(b);\n\t\t}\n\t\tFS_CreatePath(dl->tempname+dl->prefixbytes, dl->fsroot);\n\t\tdl->file = FS_OpenVFS(dl->tempname+dl->prefixbytes, \"wb\", dl->fsroot);\n\t}\n\tif (!dl->file)\n\t{\n\t\tchar displaypath[MAX_OSPATH];\n\t\tFS_DisplayPath(dl->tempname+dl->prefixbytes, dl->fsroot, displaypath, sizeof(displaypath));\n\t\tCon_TPrintf(\"Unable to open \\\"%s\\\"\\n\", displaypath);\n\t\treturn false;\n\t}\n\n\tif (dl->method == DL_QWPENDING)\n\t\tCon_TPrintf(\"method is still 'pending'\\n\");\n\n\tif (dl->method == DL_QWCHUNKS && !dl->dlblocks)\n\t{\n\t\tdl->dlblocks = Z_Malloc(sizeof(*dl->dlblocks));\n\t\tdl->dlblocks->start = 0;\n\t\tdl->dlblocks->end = dl->size;\n\t\tdl->dlblocks->state = DLB_MISSING;\n\t}\n\tdl->flags |= DLLF_BEGUN;\n\n\tdl->starttime = Sys_DoubleTime();\n\treturn true;\n}\n\nstatic void DL_Completed(qdownload_t *dl, qofs_t start, qofs_t end)\n{\n\tstruct dlblock_s *prev = NULL, *b, *n, *e;\n\tif (end <= start)\n\t\treturn;\t//ignore invalid ranges.\n\tfor (b = dl->dlblocks; b; )\n\t{\n\t\tif (b->state == DLB_RECEIVED)\n\t\t{\n\t\t\t//nothing to be done. dupe. somehow. or simply a different range.\n\t\t}\n\t\telse if (b->start >= start && b->end <= end)\n\t\t{\n\t\t\t//whole block\n//\t\t\tCon_Printf(\"Whole block\\n\");\n\t\t\tb->state = DLB_RECEIVED;\n\t\t\tdl->completedbytes += b->end - b->start;\n\t\t\tdl->ratebytes += b->end - b->start;\n\t\t}\n\t\telse if (start > b->start && end < b->end)\n\t\t{\n//\t\t\tCon_Printf(\"chop out middle\\n\");\n\t\t\t//in the middle, no need to merge\n\t\t\tn = Z_Malloc(sizeof(*n));\n\t\t\te = Z_Malloc(sizeof(*e));\n\t\t\te->next = b->next;\n\t\t\tn->next = e;\n\t\t\tb->next = n;\n\n\t\t\te->state = b->state;\n\t\t\te->sequence = b->sequence;\n\t\t\tn->state = DLB_RECEIVED;\n\n\t\t\te->end = b->end;\n\t\t\tb->end = start;\n\t\t\tn->start = start;\n\t\t\tn->end = end;\n\t\t\te->start = end;\n\n\t\t\tdl->completedbytes += n->end - n->start;\n\t\t\tdl->ratebytes += n->end - n->start;\n\t\t}\n\t\t//data overlaps the start (data end must be smaller than block end)\n\t\telse if (start <= b->start && end > b->start)\n\t\t{\n//\t\t\tCon_Printf(\"complete start\\n\");\n\n\t\t\t//split it. new(non-complete) block is second.\n\t\t\tn = Z_Malloc(sizeof(*n));\n\t\t\tn->next = b->next;\n\t\t\tb->next = n;\n\t\t\t//second block keeps original block's state. first block gets completed\n\t\t\tn->state = b->state;\n\t\t\tn->sequence = b->sequence;\n\t\t\tb->state = DLB_RECEIVED;\n\n\t\t\tn->start = end;\n\t\t\tn->end = b->end;\n\t\t\tb->end = end;\n\n\t\t\tdl->completedbytes += b->end - b->start;\n\t\t\tdl->ratebytes += b->end - b->start;\n\t\t}\n\t\t//new data overlaps the end\n\t\telse if (start > b->start && start < b->end)\n\t\t{\n//\t\t\tCon_Printf(\"complete end\\n\");\n\t\t\t//split it. new(completed) block is second.\n\t\t\tn = Z_Malloc(sizeof(*n));\n\t\t\tn->next = b->next;\n\t\t\tb->next = n;\n\t\t\t//second block keeps original block's state. first block gets completed\n\t\t\tn->state = DLB_RECEIVED;\n\t\t\tn->sequence = 0;\n\n\t\t\tn->start = end;\n\t\t\tn->end = b->end;\n\t\t\tb->end = end;\n\n\t\t\tdl->completedbytes += n->end - n->start;\n\t\t\tdl->ratebytes += n->end - n->start;\n\n\t\t\tprev = b;\n\t\t\tb = n;\n\t\t}\n\t\telse\n\t\t{//don't bother merging, as nothing changed\n\t\t\tprev = b;\n\t\t\tb = b->next;\n\t\t\tcontinue;\n\t\t}\n\n\t\t//merge with next block\n\t\tif (b->next && b->next->state == DLB_RECEIVED)\n\t\t{\n\t\t\tn = b->next;\n\t\t\tb->next = n->next;\n\t\t\tb->end = n->end;\n\t\t\tZ_Free(n);\n\t\t}\n\t\t//merge with previous block if possible\n\t\tif (prev && prev->state == DLB_RECEIVED)\n\t\t{\n\t\t\tn = b;\n\t\t\tprev->end = b->end;\n\t\t\tprev->next = b->next;\n\t\t\tZ_Free(b);\n\t\t\tb = prev->next;\n\t\t\tcontinue;//careful here\n\t\t}\n\t\tprev = b;\n\t\tb = b->next;\n\t}\n}\n\nstatic float chunkrate;\n\nstatic int CL_CountQueuedDownloads(void);\nstatic void CL_ParseChunkedDownload(qdownload_t *dl)\n{\n\tqbyte\t*svname;\n\tint flag;\n\tqofs_t filesize;\n\tqofs_t chunknum;\n\tchar data[DLBLOCKSIZE];\n\n\tchunknum = MSG_ReadLong();\n\tif (chunknum == -1)\n\t{\n\t\tflag = MSG_ReadLong();\n\t\tif (flag == 0x80000000)\n\t\t{\t//really big files need special handling here.\n\t\t\tflag = MSG_ReadLong();\n\t\t\tfilesize = qofs_Make(flag, MSG_ReadLong());\n\t\t\tflag = 0;\n\t\t}\n\t\telse\n\t\t\tfilesize = flag;\n\n\t\tsvname = MSG_ReadString();\n\t\tif (cls.demoplayback)\n\t\t{\t//downloading in demos is allowed ONLY for csprogs.dat\n\t\t\textern cvar_t cl_downloads, cl_download_csprogs;\n\t\t\tif (!cls.download && !dl &&\n\t\t\t\t\t!strcmp(svname, \"csprogs.dat\") && filesize && filesize == strtoul(InfoBuf_ValueForKey(&cl.serverinfo, \"*csprogssize\"), NULL, 0) &&\n\t\t\t\t\tcl_downloads.ival && cl_download_csprogs.ival)\n\t\t\t{\n\t\t\t\t//FIXME: should probably save this to memory instead of bloating it on disk.\n\t\t\t\tdl = Z_Malloc(sizeof(*dl));\n\n\t\t\t\tQ_strncpyz(dl->remotename, svname, sizeof(dl->remotename));\n\t\t\t\tQ_strncpyz(dl->localname, va(\"csprogsvers/%x.dat\", (unsigned int)strtoul(InfoBuf_ValueForKey(&cl.serverinfo, \"*csprogs\"), NULL, 0)), sizeof(dl->localname));\n\n\t\t\t\t// download to a temp name, and only rename\n\t\t\t\t// to the real name when done, so if interrupted\n\t\t\t\t// a runt file wont be left\n\t\t\t\tCOM_StripExtension (dl->localname, dl->tempname, sizeof(dl->tempname)-5);\n\t\t\t\tQ_strncatz (dl->tempname, \".tmp\", sizeof(dl->tempname));\n\n\t\t\t\tdl->method = DL_QWPENDING;\n\t\t\t\tdl->percent = 0;\n\t\t\t\tdl->sizeunknown = true;\n\t\t\t\tdl->flags = DLLF_OVERWRITE;\n\n\t\t\t\tif (COM_FCheckExists(dl->localname))\n\t\t\t\t{\n\t\t\t\t\tCon_DPrintf(\"Demo embeds redundant %s\\n\", dl->localname);\n\t\t\t\t\tZ_Free(dl);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tcls.download = dl;\n\t\t\t\tCon_Printf(\"Saving recorded file %s (%lu bytes)\\n\", dl->localname, (unsigned long)filesize);\n\t\t\t}\n\t\t\telse\n\t\t\t\treturn;\n\t\t}\n\n\t\tif (!*svname)\n\t\t{\n\t\t\t//stupid mvdsv.\n\t\t\t/*if (totalsize < 0)\n\t\t\t\tsvname = cls.downloadname;\n\t\t\telse*/\n\t\t\t{\n\t\t\t\tCon_Printf(\"ignoring nameless download\\n\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\tif (flag < 0)\n\t\t{\n\t\t\tenum dlfailreason_e failreason;\n\t\t\tif (flag == DLERR_REDIRECTFILE)\n\t\t\t{\n\t\t\t\tfailreason = DLFAIL_REDIRECTED;\n\t\t\t\tif (CL_AllowArbitaryDownload(dl->remotename, svname))\n\t\t\t\t{\n\t\t\t\t\tCon_Printf(\"Download of \\\"%s\\\" redirected to \\\"%s\\\"\\n\", dl->remotename, svname);\n\t\t\t\t\tif (!strncmp(svname, \"package/\", 8))\n\t\t\t\t\t{\n\t\t\t\t\t\tint i, c;\n\t\t\t\t\t\tchar *pn;\n\t\t\t\t\t\tCmd_TokenizeString(cl.serverpacknames, false, false);\n\t\t\t\t\t\tc = Cmd_Argc();\n\t\t\t\t\t\tfor (i = 0; i < c; i++)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tpn = Cmd_Argv(i);\n\t\t\t\t\t\t\tif (*pn == '*')\n\t\t\t\t\t\t\t\tpn++;\t//'required'... so shouldn't really be missing.\n\t\t\t\t\t\t\tif (!strcmp(pn, svname+8))\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (i == c)\n\t\t\t\t\t\t\tCon_Printf(\"However, package \\\"%s\\\" is unknown.\\n\", svname+8);\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tchar localname[MAX_OSPATH];\n\t\t\t\t\t\t\tchar *hash;\n\t\t\t\t\t\t\tCmd_TokenizeString(cl.serverpackhashes, false, false);\n\t\t\t\t\t\t\thash = Cmd_Argv(i);\n\t\t\t\t\t\t\tif (FS_GenCachedPakName(svname+8, hash, localname, sizeof(localname)))\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tif (CL_CheckOrEnqueDownloadFile(svname+8, localname, DLLF_NONGAME))\n\t\t\t\t\t\t\t\t\tif (!CL_CheckDLFile(dl->localname))\n\t\t\t\t\t\t\t\t\t\tCon_Printf(\"However, \\\"%s\\\" already exists. You may need to delete it.\\n\", svname);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\tCon_Printf(\"However, package \\\"%s\\\" is invalid.\\n\", svname+8);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\telse if (CL_CheckOrEnqueDownloadFile(svname, NULL, 0))\n\t\t\t\t\t{\n\t\t\t\t\t\tCon_Printf(\"However, \\\"%s\\\" already exists. You may need to delete it.\\n\", svname);\n\t\t\t\t\t\tfailreason = DLFAIL_CLIENTFILE;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tsvname = dl->remotename;\n\t\t\t}\n\t\t\telse if (flag == DLERR_UNKNOWN)\n\t\t\t{\n\t\t\t\tCon_Printf(\"Server reported an error when downloading file \\\"%s\\\"\\n\", svname);\n\t\t\t\tfailreason = DLFAIL_CORRUPTED;\n\t\t\t}\n\t\t\telse if (flag == DLERR_PERMISSIONS)\n\t\t\t{\n\t\t\t\tCon_Printf(\"Server permissions deny downloading file \\\"%s\\\"\\n\", svname);\n\t\t\t\tfailreason = DLFAIL_SERVERCVAR;\n\t\t\t}\n\t\t\telse //if (flag == DLERR_FILENOTFOUND)\n\t\t\t{\n\t\t\t\tCon_Printf(\"Couldn't find file \\\"%s\\\" on the server\\n\", svname);\n\t\t\t\tfailreason = DLFAIL_SERVERFILE;\n\t\t\t}\n\n\t\t\tif (dl)\n\t\t\t{\n\t\t\t\tCL_DownloadFailed(svname, dl, failreason);\n\n\t\t\t\tCL_RequestNextDownload();\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tif (!dl)\n\t\t{\n\t\t\tCon_Printf(\"ignoring download start. we're not meant to be downloading\\n\");\n\t\t\treturn;\n\t\t}\n\n\t\tif (dl->method == DL_QWCHUNKS)\n\t\t\tHost_EndGame(\"Received second download - \\\"%s\\\"\\n\", svname);\n\n\t\tif (stricmp(dl->remotename, svname))\n\t\t{\n\t\t\t//fixme: we should allow extension changes, in the case of ogg/mp3/wav, or tga/png/jpg/pcx, or the addition of .gz or whatever\n\t\t\tHost_EndGame(\"Server sent the wrong download - \\\"%s\\\" instead of \\\"%s\\\"\\n\", svname, dl->remotename);\n\t\t}\n\n\n\t\t//start the new download\n\t\tdl->method = DL_QWCHUNKS;\n\t\tdl->percent = 0;\n\t\tdl->size = filesize;\n\t\tdl->sizeunknown = false;\n\n\t\tdl->starttime = Sys_DoubleTime();\n\n\t\t/*\n\t\tstrcpy(cls.downloadname, svname);\n\t\tCOM_StripExtension(svname, cls.downloadtempname);\n\t\tCOM_DefaultExtension(cls.downloadtempname, \".tmp\");\n\t\t*/\n\n\t\tif (!DL_Begun(dl))\n\t\t{\n\t\t\tCL_DownloadFailed(svname, dl, DLFAIL_CLIENTFILE);\n\t\t\treturn;\n\t\t}\n\n\t\treturn;\n\t}\n\n//\tCon_Printf(\"Received dl block %i: \", chunknum);\n\n\tMSG_ReadData(data, DLBLOCKSIZE);\n\n\tif (!dl)\n\t{\n\t\tif (!cls.demoplayback)\t//mute it in demos.\n\t\t\tCon_Printf(\"ignoring download data packet\\n\");\n\t\treturn;\n\t}\n\n\tif (chunknum*DLBLOCKSIZE > dl->size+DLBLOCKSIZE)\n\t\treturn;\n\n\tif (!dl->file)\n\t\treturn;\n\n\tVFS_SEEK(dl->file, chunknum*DLBLOCKSIZE);\n\tif (dl->size - chunknum*DLBLOCKSIZE < DLBLOCKSIZE)\t//final block is actually meant to be smaller than we recieve.\n\t\tVFS_WRITE(dl->file, data, dl->size - chunknum*DLBLOCKSIZE);\n\telse\n\t\tVFS_WRITE(dl->file, data, DLBLOCKSIZE);\n\n\tDL_Completed(dl, chunknum*DLBLOCKSIZE, (chunknum+1)*DLBLOCKSIZE);\n\n\tdl->percent = dl->completedbytes/(float)dl->size*100;\n\n\tchunkrate += 1;\n\n\tif (dl->completedbytes == dl->size)\n\t\tCL_DownloadFinished(dl);\n}\n\nstatic int CL_CountQueuedDownloads(void)\n{\n\tint count = 0;\n\tdownloadlist_t *dl;\n\tfor (dl = cl.downloadlist; dl; dl = dl->next)\n\t\tcount++;\n\n\treturn count;\n}\n\nstatic void DLC_RequestDownloadChunks(qdownload_t *dl, float frametime)\n{\n\tchar *cmd;\n\tqofs_t chunk;\n\tstruct dlblock_s *b, *n;\n\tqboolean stillpending = false;\n\tqboolean haveloss = false;\n\tint chunks, chunksremaining;\n\tstatic float slop;\t//try to keep things as integers\n//\tint cmds = 20;\n\tif (frametime < 0)\n\t\tframetime = 0;\n\tif (frametime > 0.1)\n\t\tframetime = 0.1;\t//erg?\n\n\tif (chunkrate < 0)\n\t\tchunkrate = 0;\n\tslop += chunkrate*frametime;\n\tchunksremaining = slop;\n\tslop -= chunksremaining;\n\tif (chunksremaining < 1)\n\t{\n\t\tif (chunkrate < 30)\n\t\t\tchunksremaining = 1;\n\t\telse\n\t\t\treturn;\n/*\t\tif (!chunkrate)\n\t\t\tchunkrate = 72;\n\t\telse\n\t\t\tchunkrate+=frametime;\n\t\treturn;\n*/\t}\n\tif (chunksremaining > 100)\n\t{\t//we're going to need some sanity limit, for cpu resources.\n\t\tchunkrate -= (chunksremaining-100);\n\t\tchunksremaining = 100;\n\t}\n//Con_DPrintf(\"%i\\n\", chunksremaining);\n\tfor (b = dl->dlblocks; b; b = b->next)\n\t{\n\t\t//packetloss reverts blocks to missing.\n\t\tif (b->state == DLB_PENDING)\n\t\t{\n\t\t\tif (b->sequence < cls.netchan.incoming_sequence-10)\n\t\t\t{\n\t\t\t\thaveloss = true;\n\t\t\t\tb->state = DLB_MISSING;\n\t\t\t\tfor (;;)\t//merge it with the next if they're all invalid\n\t\t\t\t{\n\t\t\t\t\tn = b->next;\n\t\t\t\t\tif (!n)\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tif (n->state == DLB_MISSING || (n->state == DLB_PENDING && n->sequence < cls.netchan.incoming_sequence-10))\n\t\t\t\t\t{\n\t\t\t\t\t\tb->next = n->next;\n\t\t\t\t\t\tb->end = n->end;\n\t\t\t\t\t\tZ_Free(n);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t\tstillpending = true;\n\t\t}\n\t\tif (b->state == DLB_MISSING && chunksremaining)\n\t\t{\n\t\t\tchunk = b->start / DLBLOCKSIZE;\n\t\t\tchunks = 1;//((b->end+DLBLOCKSIZE-1)/DLBLOCKSIZE) - (b->start / DLBLOCKSIZE);\n\t\t\tif (chunks > chunksremaining)\n\t\t\t\tchunks = chunksremaining;\n\n\t\t\t//if this block is bigger than a chunk, split the two blocks.\n\t\t\tif (b->end - b->start > DLBLOCKSIZE*chunks)\n\t\t\t{\n\t\t\t\tn = Z_Malloc(sizeof(*n));\n\t\t\t\tn->next = b->next;\n\t\t\t\tn->start = (chunk+chunks)*DLBLOCKSIZE;\n\t\t\t\tn->end = b->end;\n\t\t\t\tb->end = n->start;\n\t\t\t\tn->state = DLB_MISSING;\n\t\t\t\tb->next = n;\n\t\t\t}\n\t\t\tb->state = DLB_PENDING;\n\t\t\tb->sequence = cls.netchan.outgoing_sequence;\n\t\t\tstillpending = true;\n\n\t\t\tif (chunks > 1)\n\t\t\t\tcmd = va(\"nextdl %u %3g %i %i\\n\", (unsigned int)chunk, dl->percent, dl->filesequence, chunks);\n\t\t\telse\n\t\t\t\tcmd = va(\"nextdl %u %3g %i\\n\", (unsigned int)chunk, dl->percent, dl->filesequence);\n\t\t\tCL_RemoveClientCommands(cmd);\n\t\t\tCL_SendClientCommand(false, \"%s\", cmd);\n\t\t\tchunksremaining -= chunks;\n\t\t\tif (chunksremaining <= 0)\n\t\t\t\tbreak;\n\t\t\t/*if (--cmds <= 0)\n\t\t\t{\n\t\t\t\tchunkrate -= chunksremaining;\n//\t\t\t\thaveloss = true;\n\t\t\t\tbreak;\n\t\t\t}*/\n\t\t}\n\t}\n\tif (haveloss)\n\t{\n\t\tchunkrate *= 0.98;\n\t}\n\tif (!stillpending)\n\t{\t//when there's nothing still pending, the download is complete.\n\t\tCon_DPrintf(\"Download took %i seconds (%i more)\\n\", (int)(Sys_DoubleTime() - dl->starttime), CL_CountQueuedDownloads());\n\t\tCL_DownloadFinished(dl);\n\t}\n}\n\nstatic void DLC_Poll(qdownload_t *dl)\n{\n\tstatic float lasttime;\n\tDLC_RequestDownloadChunks(dl, realtime - lasttime);\n\tlasttime = realtime;\n}\n\n#endif\n\nvoid DL_Abort(qdownload_t *dl, enum qdlabort aborttype)\n{\n\tstruct dlblock_s *b, *n;\n\n\tif (dl->file)\n\t{\n\t\tVFS_CLOSE(dl->file);\n\t\tdl->file = NULL;\n\t}\n\n\tif (dl->flags & DLLF_BEGUN)\n\t{\n\t\tdl->flags &= ~DLLF_BEGUN;\n\t\tif (aborttype == QDL_COMPLETED)\n\t\t{\n\t\t\t//this file isn't needed now the download has finished.\n\t\t\tFS_Remove(dl->dclname+dl->prefixbytes, dl->fsroot);\n\n\t\t\tif (dl->flags & DLLF_TEMPORARY)\n\t\t\t{\n#ifdef TERRAIN\n\t\t\t\tif (!Terr_DownloadedSection(dl->tempname+dl->prefixbytes))\n#endif\n\t\t\t\t\tCon_Printf(\"Downloaded unusable temporary file\\n\");\n\t\t\t\tFS_Remove(dl->tempname+dl->prefixbytes, dl->fsroot);\n\t\t\t}\n\t\t\telse if (Q_strcasecmp(dl->tempname, dl->localname))\n\t\t\t{\n\t\t\t\tif (dl->flags & DLLF_OVERWRITE)\n\t\t\t\t\tFS_Remove(dl->localname+dl->prefixbytes, dl->fsroot);\n\t\t\t\tif (!FS_Rename(dl->tempname+dl->prefixbytes, dl->localname+dl->prefixbytes, dl->fsroot))\n\t\t\t\t{\n\t\t\t\t\tchar displaytmp[MAX_OSPATH], displayfinal[MAX_OSPATH];\n\t\t\t\t\tFS_DisplayPath(dl->tempname+dl->prefixbytes, dl->fsroot, displaytmp, sizeof(displaytmp));\n\t\t\t\t\tFS_DisplayPath(dl->localname+dl->prefixbytes, dl->fsroot, displayfinal, sizeof(displayfinal));\n\t\t\t\t\tCon_Printf(\"Couldn't rename %s to %s\\n\", displaytmp, displayfinal);\n\t\t\t\t}\n\t\t\t}\n#ifdef PACKAGEMANAGER\n\t\t\tif (!strncmp(dl->localname, \"package/\", 8) && dl->fsroot == FS_ROOT)\n\t\t\t\tPM_FileInstalled(dl->localname+8, dl->fsroot, NULL, false);\n#endif\n\t\t}\n\t\telse\n\t\t{\n\t\t\t//file was aborted half way through...\n\t\t\tif (dl->dlblocks)\n\t\t\t{\n\t\t\t\t//save the list of valid chunks so we don't have to redownload those.\n\t\t\t\tvfsfile_t *parts;\n\t\t\t\tparts = FS_OpenVFS(dl->dclname+dl->prefixbytes, \"wb\", dl->fsroot);\n\t\t\t\tif (parts)\n\t\t\t\t{\n\t\t\t\t\tfor (b = dl->dlblocks; b; b = n)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (b->state == DLB_RECEIVED)\n\t\t\t\t\t\t\tVFS_PRINTF(parts, \"c %\"PRIx64\" %\"PRIx64\"\\n\", (quint64_t)b->start, (quint64_t)b->end);\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tfor(;;)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tn = b->next;\n\t\t\t\t\t\t\t\tif (n && n->state != DLB_RECEIVED)\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tb->end = n->end;\n\t\t\t\t\t\t\t\t\tb->next = n->next;\n\t\t\t\t\t\t\t\t\tZ_Free(n);\n\t\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tVFS_PRINTF(parts, \"m %\"PRIx64\" %\"PRIx64\"\\n\", (quint64_t)b->start, (quint64_t)b->end);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tn = b->next;\n\t\t\t\t\t\tZ_Free(b);\n\t\t\t\t\t}\n\t\t\t\t\tdl->dlblocks = NULL;\n\t\t\t\t\tVFS_CLOSE(parts);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tFS_Remove(dl->tempname + dl->prefixbytes, dl->fsroot);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t//download looks like it was non-resumable. just delete it.\n\t\t\t\tFS_Remove(dl->tempname + dl->prefixbytes, dl->fsroot);\n\t\t\t}\n\t\t}\n\n\t\tif (aborttype != QDL_DISCONNECT)\n\t\t{\n\t\t\tswitch(dl->method)\n\t\t\t{\n\t\t\tdefault:\n\t\t\t\tbreak;\n#ifdef Q3CLIENT\n\t\t\tcase DL_Q3:\n\t\t\t\tq3->cl.SendClientCommand(\"stopdl\");\n\t\t\t\tbreak;\n#endif\n\t\t\tcase DL_QW:\n\t\t\t\tbreak;\n\t\t\tcase DL_DARKPLACES:\n\t\t\t\tCL_SendClientCommand(true, \"stopdownload\");\n\t\t\t\tbreak;\n\t\t\tcase DL_QWCHUNKS:\n\t\t\t\t{\n\t\t\t\t\t//char *serverversion = InfoBuf_ValueForKey(&cl.serverinfo, \"*version\");\n\t\t\t\t\t//if (!strncmp(serverversion , \"MVDSV \", 6))\t//mvdsv will spam if we use stopdownload. and it'll misreport packetloss if we send nothing. grr.\n\t\t\t\t\t\tCL_SendClientCommand(true, \"nextdl -1 100 %i\", dl->filesequence);\n\t\t\t\t\t//else\n\t\t\t\t\t//\tCL_SendClientCommand(true, \"stopdownload\");\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\tfor (b = dl->dlblocks; b; b = n)\n\t{\n\t\tn = b->next;\n\t\tZ_Free(b);\n\t}\n\tdl->dlblocks = NULL;\n\n\tif (dl->method != DL_HTTP)\n\t\tZ_Free(dl);\n\tif (cls.download == dl)\n\t\tcls.download = NULL;\n}\n\n/*\n=====================\nCL_ParseDownload\n\nA download message has been received from the server\n=====================\n*/\nstatic void CL_ParseDownload (qboolean zlib)\n{\n\textern cvar_t cl_dlemptyterminate;\n\tint\t\tsize, percent;\n\tqbyte\tname[1024];\n\tqdownload_t *dl = cls.download;\n\n#ifdef PEXT_CHUNKEDDOWNLOADS\n\tif (cls.fteprotocolextensions & PEXT_CHUNKEDDOWNLOADS)\n\t{\n\t\tif (cls.demoplayback == DPB_MVD && cls.demoeztv_ext)\n\t\t\tHost_EndGame(\"CL_ParseDownload: chunked download on qtv proxy.\");\n\t\tCL_ParseChunkedDownload(dl);\n\t\treturn;\n\t}\n#endif\n\n\t// read the data\n\tsize = MSG_ReadShort ();\n\tpercent = MSG_ReadByte ();\n\n\tif (size == -2)\n\t{\n\t\t/*quakeforge*/\n\t\tMSG_ReadString();\n\t\treturn;\n\t}\n\tif (size == -3)\n\t{\n\t\tchar *requestedname;\n\t\tQ_strncpyz(name, MSG_ReadString(), sizeof(name));\n\t\trequestedname = MSG_ReadString();\n\t\tCon_DPrintf(\"Download for %s redirected to %s\\n\", requestedname, name);\n\t\t/*quakeforge http download redirection*/\n\t\tif (dl)\n\t\t\tCL_DownloadFailed(dl->remotename, dl, DLFAIL_REDIRECTED);\n\t\t//FIXME: find some safe way to do this and actually test it. we should already know the local name, but we might have gained a .gz or something (this is quakeforge after all).\n//\t\tCL_CheckOrEnqueDownloadFile(name, localname, DLLF_IGNOREFAILED);\n\t\treturn;\n\t}\n\n\tif (cls.demoplayback && !cls.demoeztv_ext)\n\t{\n\t\tif (size > 0)\n\t\t\tMSG_ReadSkip(size);\n\t\treturn; // not in demo playback, we don't know the name of the file.\n\t}\n\tif (!dl)\n\t{\n\t\t//download packet without file requested.\n\t\tif (size > 0)\n\t\t\tMSG_ReadSkip(size);\n\t\treturn; // not in demo playback\n\t}\n\n\tif (size < 0)\n\t{\n\t\tCon_TPrintf (\"File not found.\\n\");\n\n\t\tif (dl)\n\t\t\tCL_DownloadFailed(dl->remotename, dl, DLFAIL_SERVERFILE);\n\t\treturn;\n\t}\n\n\t// open the file if not opened yet\n\tif (dl->method == DL_QWPENDING)\n\t{\n\t\tdl->method = DL_QW;\n\t\tif (!DL_Begun(dl))\n\t\t{\n\t\t\tMSG_ReadSkip(size);\n\t\t\tCon_TPrintf (\"Failed to open %s\\n\", dl->tempname);\n\t\t\tCL_DownloadFailed(dl->remotename, dl, DLFAIL_CLIENTFILE);\n\t\t\tCL_RequestNextDownload ();\n\t\t\treturn;\n\t\t}\n\t\tSCR_EndLoadingPlaque();\n\t}\n\n\tif (zlib)\n\t{\n#if defined(AVAIL_ZLIB) && defined(Q2CLIENT)\n\t\tz_stream s;\n\t\tunsigned short clen = size;\n\t\tunsigned short ulen = MSG_ReadShort();\n\t\tchar cdata[8192];\n\t\tunsigned int done = 0;\n\t\tmemset(&s, 0, sizeof(s));\n\t\ts.next_in = net_message.data + MSG_GetReadCount();\n\t\ts.avail_in = clen;\n\t\tif (inflateInit2(&s, -15) != Z_OK)\n\t\t\tHost_EndGame (\"CL_ParseZDownload: unable to initialise zlib\");\n\t\tfor(;;)\n\t\t{\n\t\t\tint zerr;\n\t\t\ts.next_out = cdata;\n\t\t\ts.avail_out = sizeof(cdata);\n\t\t\tzerr = inflate(&s, Z_FULL_FLUSH);\n\t\t\tVFS_WRITE (dl->file, cdata, s.total_out - done);\n\t\t\tdone = s.total_out;\n\t\t\tif (zerr == Z_STREAM_END)\n\t\t\t\tbreak;\n\t\t\telse if (zerr == Z_OK)\n\t\t\t\tcontinue;\n\t\t\telse\n\t\t\t\tHost_EndGame (\"CL_ParseZDownload: stream truncated\");\n\t\t}\n\t\tif (inflateEnd(&s) != Z_OK)\n\t\t\tHost_EndGame (\"CL_ParseZDownload: stream truncated\");\n\t\tVFS_WRITE (dl->file, cdata, s.total_out - done);\n\t\tdone = s.total_out;\n\t\tif (s.total_out != ulen || s.total_in != clen)\n\t\t\tHost_EndGame (\"CL_ParseZDownload: stream truncated\");\n\n#else\n\t\tHost_EndGame(\"Unable to handle zlib downloads, zlib is not supported in this build\");\n#endif\n\t\tMSG_ReadSkip(size);\n\t}\n\telse\n#ifdef PEXT_ZLIBDL\n\tif (percent >= 101 && percent <= 201)// && cls.fteprotocolextensions & PEXT_ZLIBDL)\n\t{\n\t\tint compsize;\n\n\t\tpercent = percent - 101;\n\n\t\tVFS_WRITE (cls.download, ZLibDownloadDecode(&compsize, net_message.data + MSG_GetReadCount(), size), size);\n\n\t\tMSG_ReadSkip(compsize);\n\t}\n\telse\n#endif\n\t{\n\t\tVFS_WRITE (dl->file, net_message.data + MSG_GetReadCount(), size);\n\t\tMSG_ReadSkip(size);\n\t}\n\n\tdl->completedbytes += size;\n\tdl->ratebytes += size;\n\tif (dl->percent != percent)\t//try and guess the size (its most acurate when the percent value changes)\n\t\tdl->size = ((float)dl->completedbytes*100)/percent;\n\n\tif (percent != 100 && size == 0 && cl_dlemptyterminate.ival)\n\t{\n\t\tCon_Printf(CON_WARNING \"WARNING: Client received empty svc_download, assuming EOF.\\n\");\n\t\tpercent = 100;\n\t}\n\n\tif (percent != 100)\n\t{\n\t\t// request next block\n\t\tdl->percent = percent;\n\n\t\tCL_SendClientCommand(true, \"nextdl\");\n\t}\n\telse\n\t{\n\t\tCon_DPrintf(\"Download took %i seconds\\n\", (int)(Sys_DoubleTime() - dl->starttime));\n\n\t\tCL_DownloadFinished(dl);\n\n\t\t// get another file if needed\n\n\t\tCL_RequestNextDownload ();\n\t}\n}\n\nqboolean CL_ParseOOBDownload(void)\n{\n\tqdownload_t *dl = cls.download;\n\tif (!dl)\n\t\treturn false;\n\n\tif (MSG_ReadLong() != dl->filesequence)\n\t\treturn false;\n\n\tif (MSG_ReadChar() != svc_download)\n\t\treturn false;\n\n\tCL_ParseDownload(false);\n\treturn true;\n}\n\n#ifdef NQPROT\nstatic void CLDP_ParseDownloadData(void)\n{\n\tqdownload_t *dl = cls.download;\n\tunsigned char buffer[1<<16];\n\tqofs_t start;\n\tint size;\n\tstart = MSG_ReadLong();\n\tsize = (unsigned short)MSG_ReadShort();\n\n\tMSG_ReadData(buffer, size);\n\n\tif (!dl)\n\t\treturn;\n\n\tif (dl->file)\n\t{\n\t\tif (start > dl->completedbytes)\n\t\t{\t//this protocol cannot deal with gaps. we might as well wait until its repeated later.\n\t\t\t//don't ack values ahead of what we completed, we won't get good results if we do that. servers are dumb.\n\t\t\tstart = dl->completedbytes;\n\t\t\tsize = 0;\n\t\t}\n\t\telse if (start+size < dl->completedbytes)\n\t\t\t;\t//already completed this data\n\t\telse\n\t\t{\n\t\t\tint offset = dl->completedbytes-start;\t//we may already have completed some chunk already\n\n\t\t\tVFS_WRITE(dl->file, buffer+offset, size-offset);\n\t\t\tdl->completedbytes += size-offset;\n\t\t\tdl->ratebytes += size-offset;\t//for download rate calcs\n\t\t}\n\n\t\tdl->percent = (dl->completedbytes) / (float)dl->size * 100;\n\t}\n\n\t//we need to ack in order.\n\t//the server doesn't actually track packets, only position, however there's no way to tell it that we already have a chunk\n\t//we could send the acks unreliably, but any cl->sv loss would involve a sv->cl resend (because we can't dupe).\n\tMSG_WriteByte(&cls.netchan.message, clcdp_ackdownloaddata);\n\tMSG_WriteLong(&cls.netchan.message, start);\n\tMSG_WriteShort(&cls.netchan.message, size);\n}\n\nstatic void CLDP_ParseDownloadBegin(char *s)\n{\n\tqdownload_t *dl = cls.download;\n\tchar buffer[8192];\n\tqofs_t size, pos, chunk;\n\tchar *fname;\n\tCmd_TokenizeString(s, false, false);\n\tsize = (qofs_t)strtoull(Cmd_Argv(1), NULL, 0);\n\tfname = Cmd_Argv(2);\n\n\tif (!dl || strcmp(fname, dl->remotename))\n\t{\n#ifdef CSQC_DAT\n\t\tif (cls.demoplayback && !dl && cl_dp_csqc_progssize && size == cl_dp_csqc_progssize && !strcmp(fname, cl_dp_csqc_progsname))\n\t\t{\t//its somewhat common for demos to contain a copy of the csprogs, so that the same version is available when trying to play the demo back.\n\t\t\textern cvar_t cl_download_csprogs, cl_nocsqc;\n\t\t\tif (!cl_nocsqc.ival && cl_download_csprogs.ival)\n\t\t\t{\n\t\t\t\tfname = va(\"csprogsvers/%x.dat\", cl_dp_csqc_progscrc);\n\t\t\t\tif (CL_CheckDLFile(fname))\n\t\t\t\t\treturn;\t//we already have this version\n\n\t\t\t\t//Begin downloading it...\n\t\t\t}\n\t\t\telse\n\t\t\t\treturn;\t//silently ignore it\n\t\t}\n\t\telse\n#endif\n\t\t{\n\t\t\tCon_Printf(\"Warning: server started sending a file we did not request. Ignoring.\\n\");\n\t\t\treturn;\n\t\t}\n\t}\n\n\tif (!dl)\n\t{\n\t\tdl = Z_Malloc(sizeof(*dl));\n\t\tdl->filesequence = 0;\n\n\t\tQ_strncpyz(dl->remotename, fname, sizeof(dl->remotename));\n\t\tQ_strncpyz(dl->localname, fname, sizeof(dl->localname));\n\t\tCon_TPrintf (\"Downloading %s...\\n\", dl->localname);\n\n\t\t// download to a temp name, and only rename\n\t\t// to the real name when done, so if interrupted\n\t\t// a runt file wont be left\n\t\tCOM_StripExtension (dl->localname, dl->tempname, sizeof(dl->tempname)-5);\n\t\tQ_strncatz (dl->tempname, \".tmp\", sizeof(dl->tempname));\n\n\t\tdl->method = DL_DARKPLACES;\n\t\tdl->percent = 0;\n\t\tdl->sizeunknown = true;\n\t\tdl->flags = DLLF_REQUIRED;\n\t\tcls.download = dl;\n\t}\n\n\tif (dl->method == DL_QWPENDING)\n\t\tdl->method = DL_DARKPLACES;\n\tif (dl->method != DL_DARKPLACES)\n\t{\n\t\tCon_Printf(\"Warning: download method isn't right.\\n\");\n\t\treturn;\n\t}\n\n\tdl->sizeunknown = false;\n\tdl->size = size;\n\tif (!DL_Begun(dl))\n\t{\n\t\tCL_DownloadFailed(dl->remotename, dl, DLFAIL_CLIENTFILE);\n\t\treturn;\n\t}\n\n\tCL_SendClientCommand(true, \"sv_startdownload\");\n\n\t//fill the file with 0 bytes, for some reason\n\tmemset(buffer, 0, sizeof(buffer));\n\tfor (pos = 0, chunk = 1; chunk; pos += chunk)\n\t{\n\t\tchunk = size - pos;\n\t\tif (chunk > sizeof(buffer))\n\t\t\tchunk = sizeof(buffer);\n\t\tVFS_WRITE(dl->file, buffer, chunk);\n\t}\n\tVFS_SEEK(dl->file, 0);\n}\n\nstatic void CLDP_ParseDownloadFinished(char *s)\n{\n\tqdownload_t *dl = cls.download;\n\tunsigned int runningcrc = 0;\n\tconst hashfunc_t *hfunc = &hash_crc16;\n\tchar buffer[8192];\n\tqofs_t size, pos, chunk;\n\tif (!dl || !dl->file)\n\t\treturn;\n\n\tCmd_TokenizeString(s, false, false);\n\n\tVFS_CLOSE (dl->file);\n\n\tdl->file = FS_OpenVFS (dl->tempname+dl->prefixbytes, \"rb\", dl->fsroot);\n\tif (dl->file)\n\t{\n\t\tvoid *hashctx = alloca(hfunc->contextsize);\n\t\tsize = dl->size;\n\t\thfunc->init(hashctx);\n\t\tfor (pos = 0; pos < size; pos += chunk)\n\t\t{\n\t\t\tchunk = min(sizeof(buffer), size - pos);\n\t\t\tif (chunk != VFS_READ(dl->file, buffer, chunk))\n\t\t\t\tbreak;\n\t\t\thfunc->process(hashctx, buffer, chunk);\n\t\t}\n\t\tVFS_CLOSE (dl->file);\n\t\tdl->file = NULL;\n\n\t\trunningcrc = hashfunc_terminate_uint(hfunc, hashctx);\n\t}\n\telse\n\t{\n\t\tCon_Printf(\"Download failed: unable to check CRC of download\\n\");\n\t\tCL_DownloadFailed(dl->remotename, dl, DLFAIL_CLIENTFILE);\n\t\treturn;\n\t}\n\n\tif (size != atoi(Cmd_Argv(1)))\n\t{\n\t\tCon_Printf(\"Download failed: wrong file size\\n\");\n\t\tCL_DownloadFailed(dl->remotename, dl, DLFAIL_CORRUPTED);\n\t\treturn;\n\t}\n\tif (runningcrc != atoi(Cmd_Argv(2)))\n\t{\n\t\tCon_Printf(\"Download failed: wrong crc\\n\");\n\t\tCL_DownloadFailed(dl->remotename, dl, DLFAIL_CORRUPTED);\n\t\treturn;\n\t}\n\n\tCon_DPrintf(\"Download took %i seconds\\n\", (int)(Sys_DoubleTime() - dl->starttime));\n\n\tCL_DownloadFinished(dl);\n\n\t// get another file if needed\n\n\tCL_RequestNextDownload ();\n}\n#endif\n\nstatic vfsfile_t *upload_file;\nstatic qbyte *upload_data;\nstatic int upload_pos;\nstatic int upload_size;\n\nvoid CL_NextUpload(void)\n{\n\tqbyte\tbuffer[1024];\n\tint\t\tr;\n\tint\t\tpercent;\n\tint\t\tsize;\n\n\tr = upload_size - upload_pos;\n\tif (r > 768)\n\t\tr = 768;\n\n\tif (upload_data)\n\t{\n\t\tmemcpy(buffer, upload_data + upload_pos, r);\n\t}\n\telse if (upload_file)\n\t{\n\t\tr = VFS_READ(upload_file, buffer, r);\n\t\tif (r == 0)\n\t\t{\n\t\t\tCL_StopUpload();\n\t\t\treturn;\n\t\t}\n\t}\n\telse\n\t\treturn;\n\tMSG_WriteByte (&cls.netchan.message, clc_upload);\n\tMSG_WriteShort (&cls.netchan.message, r);\n\n\tupload_pos += r;\n\tsize = upload_size;\n\tif (!size)\n\t\tsize = 1;\n\tpercent = upload_pos*100/size;\n\tMSG_WriteByte (&cls.netchan.message, percent);\n\tSZ_Write (&cls.netchan.message, buffer, r);\n\nCon_DPrintf (\"UPLOAD: %6d: %d written\\n\", upload_pos - r, r);\n\n\tif (upload_pos != upload_size)\n\t\treturn;\n\n\tCon_TPrintf (\"Upload completed\\n\");\n\n\tCL_StopUpload();\n}\n\nvoid CL_StartUpload (qbyte *data, int size)\n{\n\tif (cls.state < ca_onserver)\n\t\treturn; // gotta be connected\n\n\t// override\n\tCL_StopUpload();\n\nCon_DPrintf(\"Upload starting of %d...\\n\", size);\n\n\tupload_data = BZ_Malloc(size);\n\tmemcpy(upload_data, data, size);\n\tupload_size = size;\n\tupload_pos = 0;\n\n\tCL_NextUpload();\n}\n\nqboolean CL_IsUploading(void)\n{\n\tif (upload_data || upload_file)\n\t\treturn true;\n\treturn false;\n}\n\nvoid CL_StopUpload(void)\n{\n\tif (upload_data)\n\t\tBZ_Free(upload_data);\n\tif (upload_file)\n\t\tVFS_CLOSE(upload_file);\n\tupload_file = NULL;\n\tupload_data = NULL;\n\tupload_pos = upload_size = 0;\n}\n\n#if 0\t//in case we ever want to add any uploads other than snaps\nstatic qboolean CL_StartUploadFile(char *filename)\n{\n\tif (!COM_CheckParm(\"-fileul\"))\n\t{\n\t\tCon_Printf(\"You must currently use the -fileul commandline parameter in order to use this functionality\\n\");\n\t\treturn false;\n\t}\n\n\tif (cls.state < ca_onserver)\n\t{\n\t\tCon_Printf(\"not connected\\n\");\n\t\treturn false; // gotta be connected\n\t}\n\n\tCL_StopUpload();\n\n\tupload_file = FS_OpenVFS(filename, \"rb\", FS_ROOT);\n\tupload_size = VFS_GETLEN(upload_file);\n\tupload_pos = 0;\n\n\tif (upload_file)\n\t{\n\t\tCL_NextUpload();\n\t\treturn true;\n\t}\n\treturn false;\n}\n#endif\n\n/*\n=====================================================================\n\n  SERVER CONNECTING MESSAGES\n\n=====================================================================\n*/\n\nvoid CL_ClearParseState(void)\n{\n\t// done with sounds, request models now\n\tmemset (cl.model_precache, 0, sizeof(cl.model_precache));\n\tcl_playerindex = -1;\n\tcl_h_playerindex = -1;\n\tcl_spikeindex = -1;\n\tcl_flagindex = -1;\n\tcl_rocketindex = -1;\n\tcl_grenadeindex = -1;\n\tcl_gib1index = -1;\n\tcl_gib2index = -1;\n\tcl_gib3index = -1;\n\n\tif (cl_baselines)\n\t{\n\t\tBZ_Free(cl_baselines);\n\t\tcl_baselines = NULL;\n\t}\n\tcl_baselines_count = 0;\n\n\tcl_max_static_entities = 0;\n\tif (cl_static_entities)\n\t{\n\t\tBZ_Free(cl_static_entities);\n\t\tcl_static_entities = NULL;\n\t}\n}\n\n/*\n==================\nCL_ParseServerData\n==================\n*/\nstatic void CLQW_ParseServerData (void)\n{\n\tint pnum;\n\tint clnum;\n\tchar\t*str;\n\tint protover, svcnt;\n\n\tfloat maxspeed, entgrav;\n\n\tif (cls.download && cls.download->method == DL_QWPENDING)\n\t{\n\t\t//if we didn't actually start downloading it yet, cancel the current download.\n\t\t//this is to avoid qizmo not responding to the download command, resulting in hanging downloads that cause the client to then be unable to connect anywhere simply because someone's skin was set.\n\t\tCL_DownloadFailed(cls.download->remotename, cls.download, DLFAIL_CORRUPTED);\n\t}\n\n\tCon_DPrintf (\"Serverdata packet %s.\\n\", cls.demoplayback?\"read\":\"received\");\n//\n// wipe the client_state_t struct\n//\n\n\tSCR_SetLoadingStage(LS_CLIENT);\n\tSCR_BeginLoadingPlaque();\n\n// parse protocol version number\n// allow 2.2 and 2.29 demos to play\n\tcls.fteprotocolextensions = 0;\n\tcls.fteprotocolextensions2 = 0;\n\tcls.ezprotocolextensions1 = 0;\n\tfor(;;)\n\t{\n\t\tprotover = MSG_ReadLong ();\n\t\tif (protover == PROTOCOL_VERSION_FTE1)\n\t\t{\n\t\t\tcls.fteprotocolextensions = MSG_ReadLong();\n\t\t\tcontinue;\n\t\t}\n\t\tif (protover == PROTOCOL_VERSION_FTE2)\n\t\t{\n\t\t\tcls.fteprotocolextensions2 = MSG_ReadLong();\n\t\t\tcontinue;\n\t\t}\n\t\tif (protover == PROTOCOL_VERSION_EZQUAKE1)\n\t\t{\n\t\t\tcls.ezprotocolextensions1 = MSG_ReadLong();\n\t\t\tcontinue;\n\t\t}\n\t\tif (protover == PROTOCOL_VERSION_VARLENGTH)\n\t\t{\n\t\t\tint ident;\n\t\t\tint len;\n\t\t\tchar data[1024];\n\t\t\tident = MSG_ReadLong();\n\t\t\tlen = MSG_ReadLong();\n\t\t\tif (len <= sizeof(data))\n\t\t\t{\n\t\t\t\tMSG_ReadData(data, len);\n\t\t\t\tswitch(ident)\n\t\t\t\t{\n\t\t\t\tdefault:\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\t\tif (protover == PROTOCOL_VERSION_QW)\t//this ends the version info\n\t\t\tbreak;\n\t\tif (cls.demoplayback && (protover >= 24 && protover <= 28))\t//older versions, maintain demo compatability.\n\t\t\tbreak;\n\t\tHost_EndGame (\"Server returned version %i, not %i\\n\", protover, PROTOCOL_VERSION_QW);\n\t}\n\n\tif (developer.ival || cl_shownet.ival)\n\t{\n\t\tif (cls.fteprotocolextensions2||cls.fteprotocolextensions||cls.ezprotocolextensions1)\n\t\t\tCon_TPrintf (\"Using FTE extensions 0x%x%08x %#x\\n\", cls.fteprotocolextensions2, cls.fteprotocolextensions, cls.ezprotocolextensions1);\n\t}\n\tif (cls.fteprotocolextensions & ~PEXT_CLIENTSUPPORT)\n\t\tCon_TPrintf (CON_WARNING\"Using unknown fte-pext1 extensions (%#x)\\n\", cls.fteprotocolextensions&~PEXT_CLIENTSUPPORT);\n\tif (cls.fteprotocolextensions2 & ~PEXT2_CLIENTSUPPORT)\n\t\tCon_TPrintf (CON_WARNING\"Using unknown fte-pext2 extensions (%#x)\\n\", cls.fteprotocolextensions2&~PEXT2_CLIENTSUPPORT);\n\tif (cls.ezprotocolextensions1 & ~EZPEXT1_CLIENTSUPPORT)\n\t\tCon_TPrintf (CON_WARNING\"Using unknown ezquake extensions (%#x)\\n\", cls.ezprotocolextensions1&~EZPEXT1_CLIENTSUPPORT);\n\n\tif ((cls.ezprotocolextensions1 & EZPEXT1_HIDDEN_MESSAGES) && !cls.demoplayback)\n\t\tCon_TPrintf (CON_WARNING\"Server's EZPEXT1_HIDDEN_MESSAGES extension does not make sense outside of demos\\n\");\t//we do not request this at all. its safe to blame the server.\n\n\tif (cls.fteprotocolextensions & PEXT_FLOATCOORDS)\n\t{\n\t\tcls.netchan.netprim.coordtype = COORDTYPE_FLOAT_32;\n\t\tcls.netchan.netprim.anglesize = 2;\n\t}\n\telse\n\t{\n\t\tcls.netchan.netprim.coordtype = COORDTYPE_FIXED_13_3;\n\t\tcls.netchan.netprim.anglesize = 1;\n\t}\n\tcls.netchan.message.prim = cls.netchan.netprim;\n\tMSG_ChangePrimitives(cls.netchan.netprim);\n\n\tsvcnt = MSG_ReadLong ();\n\n\t// game directory\n\tstr = MSG_ReadString ();\n\tCon_DPrintf(\"Server is using gamedir \\\"%s\\\"\\n\", str);\n\tif (!*str)\n\t\tstr = \"qw\";\t//FIXME: query active manifest's basegamedir\n\n#ifndef CLIENTONLY\n\tif (!sv.state)\n#endif\n\t\tCOM_Gamedir(str, NULL);\n\n\tCL_ClearState (true);\n#ifdef QUAKEHUD\n\tStats_NewMap();\n#endif\n\tcl.servercount = svcnt;\n\tcl.protocol_qw = protover;\n\n\tCvar_ForceCallback(Cvar_FindVar(\"r_particlesdesc\"));\n\n\tcl.teamfortress = !!Q_strcasestr(str, \"fortress\");\n\n\tif (cl.gamedirchanged)\n\t{\n\t\tcl.gamedirchanged = false;\n#ifndef CLIENTONLY\n\t\tif (!sv.state)\n#endif\n\t\t\tWads_Flush();\n\t}\n\n\t/*mvds have different parsing*/\n\tif (cls.demoplayback == DPB_MVD)\n\t{\n\t\textern float olddemotime;\n\t\tint i,j;\n\n\t\tif (cls.fteprotocolextensions2 & PEXT2_MAXPLAYERS)\n\t\t{\n\t\t\tcl.allocated_client_slots = MSG_ReadPlayer();\n\t\t\tif (cl.allocated_client_slots > MAX_CLIENTS)\n\t\t\t{\n\t\t\t\tCon_Printf(CON_ERROR\"Server has too many client slots (%u > %u)\\n\", cl.allocated_client_slots, MAX_CLIENTS);\n\t\t\t\tcl.allocated_client_slots = MAX_CLIENTS;\n\t\t\t}\n\t\t}\n\n\t\tcl.gametime = MSG_ReadFloat();\n\t\tcl.gametimemark = realtime;\n\t\tcl.oldgametime = cl.gametime;\n\t\tcl.oldgametimemark = realtime;\n\n\t\tcl.demogametimebias = cl.gametime - olddemotime;\n\n\t\tfor (j = 0; j < MAX_SPLITS; j++)\n\t\t{\n\t\t\tcl.playerview[j].playernum = cl.allocated_client_slots + j;\n\t\t\tcl.playerview[j].viewentity = 0;\t//free floating.\n\t\t\tcl.playerview[j].spectator = true;\n\t\t\tfor (i = 0; i < UPDATE_BACKUP; i++)\n\t\t\t{\n\t\t\t\tcl.inframes[i].playerstate[cl.playerview[j].playernum].pm_type = PM_SPECTATOR;\n\t\t\t\tcl.inframes[i].playerstate[cl.playerview[j].playernum].messagenum = 1;\n\t\t\t}\n\t\t}\n\n\t\tcl.splitclients = 1;\n\t}\n\telse if (cls.fteprotocolextensions2 & PEXT2_MAXPLAYERS)\n\t{\n//\t\tqboolean spec = false;\n\t\tcl.allocated_client_slots = MSG_ReadPlayer();\n\t\tif (cl.allocated_client_slots > MAX_CLIENTS)\n\t\t{\n\t\t\tCon_Printf(CON_ERROR\"Server has too many client slots (%u > %u)\\n\", cl.allocated_client_slots, MAX_CLIENTS);\n\t\t\tcl.allocated_client_slots = MAX_CLIENTS;\n\t\t}\n\n\t\t/*parsing here is slightly different to allow us 255 max players instead of 127*/\n\t\tcl.splitclients = (qbyte)MSG_ReadByte();\n\t\tif (cls.fteprotocolextensions2 & PEXT2_VRINPUTS)\n\t\t\t;\n\t\telse if (cl.splitclients & 128)\n\t\t{\n//\t\t\tspec = true;\n\t\t\tcl.splitclients &= ~128;\n\t\t}\n\t\tif (cl.splitclients > MAX_SPLITS)\n\t\t\tHost_EndGame(\"Server sent us too many seats (%u > %u)\\n\", cl.splitclients, MAX_SPLITS);\n\t\tfor (pnum = 0; pnum < cl.splitclients; pnum++)\n\t\t{\n\t\t\tcl.playerview[pnum].spectator = true;\n#ifdef QUAKESTATS\n\t\t\tif (cls.z_ext & Z_EXT_VIEWHEIGHT)\n\t\t\t\tcl.playerview[pnum].viewheight = cl.playerview[pnum].statsf[STAT_VIEWHEIGHT];\n#endif\n\t\t\tcl.playerview[pnum].playernum = (qbyte)MSG_ReadByte();\n\t\t\tif (cl.playerview[pnum].playernum >= cl.allocated_client_slots)\n\t\t\t\tHost_EndGame(\"unsupported local player slot\\n\");\n\t\t\tcl.playerview[pnum].viewentity = cl.playerview[pnum].playernum+1;\n\t\t}\n\t}\n\telse \n\t{\n\t\t// parse player slot, high bit means spectator\n\t\tpnum = MSG_ReadByte ();\n\t\tfor (clnum = 0; ; clnum++)\n\t\t{\n\t\t\tif (clnum == MAX_SPLITS)\n\t\t\t\tHost_EndGame(\"Server sent us over %u seats\\n\", MAX_SPLITS);\n#ifdef QUAKESTATS\n\t\t\tif (cls.z_ext & Z_EXT_VIEWHEIGHT)\n\t\t\t\tcl.playerview[pnum].viewheight = cl.playerview[pnum].statsf[STAT_VIEWHEIGHT];\n#endif\n\t\t\tcl.playerview[clnum].playernum = pnum;\n\t\t\tif (cl.playerview[clnum].playernum & 128)\n\t\t\t{\n\t\t\t\tcl.playerview[clnum].spectator = true;\n\t\t\t\tcl.playerview[clnum].playernum &= ~128;\n\t\t\t}\n\t\t\telse\n\t\t\t\tcl.playerview[clnum].spectator = false;\n\n\t\t\tif (cl.playerview[clnum].playernum >= cl.allocated_client_slots)\n\t\t\t\tHost_EndGame(\"unsupported local player slot\\n\");\n\n\t\t\tcl.playerview[clnum].viewentity = cl.playerview[clnum].playernum+1;\n\t\t\tif (!(cls.fteprotocolextensions & PEXT_SPLITSCREEN))\n\t\t\t\tbreak;\n\n\t\t\tpnum = MSG_ReadByte ();\n\t\t\tif (pnum == 128)\n\t\t\t\tbreak;\n\t\t}\n\t\tcl.splitclients = clnum+1;\n\t}\n\n\t// get the full level name\n\tstr = MSG_ReadString ();\n\tQ_strncpyz (cl.levelname, str, sizeof(cl.levelname));\n\n\tif (cl.protocol_qw >= 25)\n\t{\n\t\t// get the movevars\n\t\tmovevars.gravity\t\t\t= MSG_ReadFloat();\n\t\tmovevars.stopspeed\t\t\t= MSG_ReadFloat();\n\t\tmaxspeed\t\t\t\t\t= MSG_ReadFloat();\n\t\tmovevars.spectatormaxspeed\t= MSG_ReadFloat();\n\t\tmovevars.accelerate\t\t\t= MSG_ReadFloat();\n\t\tmovevars.airaccelerate\t\t= MSG_ReadFloat();\n\t\tmovevars.wateraccelerate\t= MSG_ReadFloat();\n\t\tmovevars.friction\t\t\t= MSG_ReadFloat();\n\t\tmovevars.waterfriction\t\t= MSG_ReadFloat();\n\t\tentgrav\t\t\t\t\t\t= MSG_ReadFloat();\n\t}\n\telse\n\t{\n\t\tmovevars.gravity\t\t\t= 800;\n\t\tmovevars.stopspeed\t\t\t= 100;\n\t\tmaxspeed\t\t\t\t\t= 320;\n\t\tmovevars.spectatormaxspeed\t= 500;\n\t\tmovevars.accelerate\t\t\t= 10;\n\t\tmovevars.airaccelerate\t\t= 0.7f;\n\t\tmovevars.wateraccelerate\t= 10;\n\t\tmovevars.friction\t\t\t= 6.0f;\n\t\tmovevars.waterfriction\t\t= 1;\n\t\tentgrav\t\t\t\t\t\t= 1;\n\t}\n\tmovevars.flags = MOVEFLAG_QWCOMPAT;\n\n\tfor (clnum = 0; clnum < cl.splitclients; clnum++)\n\t{\n\t\tcl.playerview[clnum].maxspeed = maxspeed;\n\t\tcl.playerview[clnum].entgravity = entgrav;\n\t}\n\n\t// seperate the printfs so the server message can have a color\n#if 1\n\tCon_Printf (\"\\n\\n\");\n\tCon_Printf (\"^Ue01d^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01f\");\n\tCon_Printf (\"\\n\\n\");\n\tCon_Printf (\"\\1%s\\n\", str);\n#else\n\tCon_TPrintf (\"\\n\\n^Ue01d^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01f\\n\\n\");\n\tCon_Printf (\"%c%s\\n\", 2, str);\n#endif\n\n\tif (CL_RemoveClientCommands(\"new\"))\t//mvdsv is really appaling some times.\n\t{\n\t//\tCon_Printf(\"Multiple 'new' commands?!?!? This server needs reinstalling!\\n\");\n\t}\n\n\tmemset(cl.sound_name, 0, sizeof(cl.sound_name));\n\tif (cls.demoplayback == DPB_MVD && (cls.demoeztv_ext&EZTV_DOWNLOAD))\n\t{\n\t\tif (CL_RemoveClientCommands(\"qtvsoundlist\"))\n\t\t\tCon_DPrintf(\"Multiple soundlists\\n\");\n\t\tCL_SendClientCommand (true, \"qtvsoundlist %i 0\", cl.servercount);\n\t}\n\telse\n\t{\n\t\tif (CL_RemoveClientCommands(\"soundlist\") && !(cls.fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS))\n\t\t\tCon_DPrintf(\"Multiple soundlists\\n\");\n\t\t// ask for the sound list next\n//\t\tCL_SendClientCommand (\"soundlist %i 0\", cl.servercount);\n\t\tCL_SendClientCommand (true, soundlist_name, cl.servercount, 0);\n\t}\n\n\t// now waiting for downloads, etc\n\tcls.state = ca_onserver;\n\tCam_AutoTrack_Update(NULL);\n\n\tcl.sendprespawn = false;\n\n#ifdef VOICECHAT\n\tS_Voip_MapChange();\n#endif\n\n#ifdef CSQC_DAT\n\tCSQC_Shutdown();\t//revive it when we get the serverinfo saying the checksum.\n#endif\n}\n\n#ifdef Q2CLIENT\nstatic void CLQ2_ParseServerData (void)\n{\n\tchar\t*str;\n\tint\t\ti;\n\tint svcnt;\n\tint rate;\n//\tint cflag;\n\n\tmemset(&cls.netchan.netprim, 0, sizeof(cls.netchan.netprim));\n\tcls.netchan.netprim.coordtype = COORDTYPE_FIXED_13_3;\n\tcls.netchan.netprim.anglesize = 1;\n\tcls.fteprotocolextensions = 0;\n\tcls.fteprotocolextensions2 = 0;\n\tcls.ezprotocolextensions1 = 0;\n\tcls.demohadkeyframe = true;\t//assume that it did, so this stuff all gets recorded.\n\n\tCon_DPrintf (\"Serverdata packet %s.\\n\", cls.demoplayback?\"read\":\"received\");\n//\n// wipe the client_state_t struct\n//\n\tSCR_SetLoadingStage(LS_CLIENT);\n\tSCR_BeginLoadingPlaque();\n//\tCL_ClearState ();\n\tcls.state = ca_onserver;\n\n// parse protocol version number\n\ti = MSG_ReadLong ();\n\n\tif (i == PROTOCOL_VERSION_FTE1)\n\t{\n\t\tcls.fteprotocolextensions = i = MSG_ReadLong();\n\t\tif (i & PEXT_FLOATCOORDS)\n\t\t\ti -= PEXT_FLOATCOORDS;\n\t\tif (i & PEXT_SOUNDDBL)\n\t\t\ti -= PEXT_SOUNDDBL;\n\t\tif (i & PEXT_MODELDBL)\n\t\t\ti -= PEXT_MODELDBL;\n\t\tif (i & PEXT_SPLITSCREEN)\n\t\t\ti -= PEXT_SPLITSCREEN;\n\t\tif (i)\n\t\t\tHost_EndGame (\"Unsupported q2 protocol extensions: %x\", i);\n\t\ti = MSG_ReadLong ();\n\n\t\tif (cls.fteprotocolextensions & PEXT_FLOATCOORDS)\n\t\t{\n\t\t\tcls.netchan.netprim.coordtype = COORDTYPE_FLOAT_32;\n\t\t\tcls.netchan.netprim.anglesize = 2;\n\t\t}\n\t}\n\n\tif (i == PROTOCOL_VERSION_R1Q2)\n\t\tCon_DPrintf(\"Using R1Q2 protocol\\n\");\n\telse if (i == PROTOCOL_VERSION_Q2PRO)\n\t\tCon_DPrintf(\"Using Q2PRO protocol\\n\");\n\telse if (i == PROTOCOL_VERSION_Q2EXDEMO)\n\t{\n\t\ti = PROTOCOL_VERSION_Q2EX;\t//close enough, don't distinguish. only real difference is 16bit coords for WritePos etc.\n\t\tCon_DPrintf(\"Using Q2EXDemo protocol\\n\");\n\t}\n\telse if (i == PROTOCOL_VERSION_Q2EX)\n\t{\n\t\tcls.netchan.netprim.coordtype = COORDTYPE_FLOAT_32;\n\t\tcls.netchan.netprim.anglesize = 2;\n\t\tCon_DPrintf(\"Using Q2EX protocol\\n\");\n\t}\n\telse if (i > PROTOCOL_VERSION_Q2 || i < (cls.demoplayback?PROTOCOL_VERSION_Q2_DEMO_MIN:PROTOCOL_VERSION_Q2_MIN))\n\t\tHost_EndGame (\"Q2 Server returned version %i, not %i\", i, PROTOCOL_VERSION_Q2);\n\tcls.protocol_q2 = i;\n\n\tsvcnt = MSG_ReadLong ();\n\t/*cl.attractloop =*/ MSG_ReadByte ();\n\n\tif (cls.protocol_q2 == PROTOCOL_VERSION_Q2EX)\n\t\trate = MSG_ReadByte();\n\telse\n\t\trate = 10;\t//fixed to a poopy 10hz.\n\n\t// game directory\n\tstr = MSG_ReadString ();\n\t// set gamedir\n\tif (!*str)\n\t\tCOM_Gamedir(\"baseq2\", NULL);\n\telse\n\t\tCOM_Gamedir(str, NULL);\n\n\tCvar_Get(\"timescale\", \"1\", 0, \"Q2Admin hacks\");\t//Q2Admin will kick players who have a timescale set to something other than 1\n\t\t\t\t\t\t\t\t\t\t\t\t\t//FTE doesn't actually have a timescale cvar, so create one to 'fool' q2admin.\n\t\t\t\t\t\t\t\t\t\t\t\t\t//I can't really blame q2admin for rejecting engines that don't have this cvar, as it could have been renamed via a hex-edit.\n\n\tCL_ClearState (true);\n\tCLQ2_ClearState ();\n\tcl.minpitch = -89;\n\tcl.maxpitch = 89;\n\tcl.servercount = svcnt;\n\tCam_AutoTrack_Update(NULL);\n\t\n#ifdef QUAKEHUD\n\tStats_NewMap();\n#endif\n\n\t// parse player entity number\n\tcl.playerview[0].playernum = MSG_ReadShort ();\n\tcl.playerview[0].viewentity = cl.playerview[0].playernum+1;\n\tcl.playerview[0].spectator = false;\n\tcl.splitclients = 1;\n\n\tif (cls.protocol_q2 == PROTOCOL_VERSION_Q2EX && cl.playerview[0].playernum==-2)\n\t{\n\t\ti = MSG_ReadShort();\n\t\tif (i > MAX_SPLITS)\n\t\t\tHost_EndGame (\"server's splitscreen count too high (%u > %u)\\n\", i, MAX_SPLITS);\n\t\tcl.splitclients = i;\n\t\tfor (i = 0; i < cl.splitclients; i++)\n\t\t{\n\t\t\tcl.playerview[i].playernum = MSG_ReadShort ();\n\t\t\tcl.playerview[i].viewentity = cl.playerview[i].playernum+1;\n\t\t\tcl.playerview[i].spectator = false;\n\t\t}\n\t}\n\n\tcl.numq2visibleweapons = 1;\t//give it a default.\n\tcl.q2visibleweapons[0] = \"weapon.md2\";\n\tcl.q2svnetrate = rate;\n\tif (cl.q2svnetrate < 1)\t//wut?...\n\t\tcl.q2svnetrate = 10;\n\n\t// get the full level name\n\tstr = MSG_ReadString ();\n\tQ_strncpyz (cl.levelname, str, sizeof(cl.levelname));\n\n\tif (cls.protocol_q2 == PROTOCOL_VERSION_R1Q2)\n\t{\n\t\tunsigned short r1q2ver;\n\t\tqboolean isenhanced = MSG_ReadByte();\n\t\tif (isenhanced)\n\t\t\tHost_EndGame (\"R1Q2 server is running an unsupported mod\");\n\t\tr1q2ver = MSG_ReadShort();\t//protocol version... limit... yeah, buggy.\n\t\tif (r1q2ver > 1905)\n\t\t\tHost_EndGame (\"R1Q2 server version %i not supported\", r1q2ver);\n\n\t\tif (r1q2ver >= 1903)\n\t\t{\n\t\t\tMSG_ReadByte();\t//'used to be advanced deltas'\n\t\t\tMSG_ReadByte(); //strafejump hack\n\t\t}\n\t\tif (r1q2ver >= 1904)\n\t\t\tcls.netchan.netprim.flags |= NPQ2_R1Q2_UCMD;\n\t\tif (r1q2ver >= 1905)\n\t\t\tcls.netchan.netprim.flags |= NPQ2_SOLID32;\n\t}\n\telse if (cls.protocol_q2 == PROTOCOL_VERSION_Q2PRO)\n\t{\n\t\tunsigned short q2prover = MSG_ReadShort();\t//q2pro protocol version\n\t\tif (q2prover < 1011 || q2prover > 1021)\n\t\t\tHost_EndGame (\"Q2PRO server version %i not supported\", q2prover);\n\t\tMSG_ReadByte();\t//server state (ie: demo playback vs actual game)\n\t\tMSG_ReadByte(); //strafejump hack\n\t\tMSG_ReadByte(); //q2pro qw-mode. kinda silly for us tbh.\n\t\tif (q2prover >= 1014)\n\t\t\tcls.netchan.netprim.flags |= NPQ2_SOLID32;\n\t\tif (q2prover >= 1018)\n\t\t\tcls.netchan.netprim.flags |= NPQ2_ANG16;\n\t\tif (q2prover >= 1015)\n\t\t\tMSG_ReadByte();\t//some kind of waterjump hack enable\n\t}\n\n\tcls.netchan.message.prim = cls.netchan.netprim;\n\tMSG_ChangePrimitives(cls.netchan.netprim);\n\n\tif (cl.playerview[0].playernum == -1)\n\t{\t// playing a cinematic or showing a pic, not a level\n\t\tSCR_EndLoadingPlaque();\n\t\tCL_MakeActive(\"Quake2\");\n\t\tif (!COM_FCheckExists(str) && !COM_FCheckExists(va(\"video/%s\", str)))\n\t\t{\n\t\t\tint i;\n\t\t\tchar basename[64], *t;\n\t\t\tchar *exts[] = {\".ogv\", \".roq\", \".cin\"};\n\t\t\tCOM_StripExtension(COM_SkipPath(str), basename, sizeof(basename));\n\t\t\tfor(i = 0; i < countof(exts); i++)\n\t\t\t{\n\t\t\t\tt = va(\"video/%s%s\", basename, exts[i]);\n\t\t\t\tif (COM_FCheckExists(t))\n\t\t\t\t{\n\t\t\t\t\tstr = t;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (!Media_PlayFilm(str, false))\n\t\t{\n\t\t\tCL_SendClientCommand(true, \"nextserver %i\", cl.servercount);\n\t\t}\n\t}\n\telse\n\t{\n\t\t// seperate the printfs so the server message can have a color\n\t\tCon_TPrintf (\"\\n\\n^Ue01d^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01f\\n\\n\");\n\t\tCon_Printf (\"%c%s\\n\", 2, str);\n\n\t\tMedia_StopFilm(true);\n\n\t\t// need to prep refresh at next oportunity\n\t\t//cl.refresh_prepped = false;\n\t}\n\n\tCvar_ForceCallback(Cvar_FindVar(\"r_particlesdesc\"));\n\n\tSurf_PreNewMap();\n\tCL_CheckServerInfo();\n}\n#endif\n\n\nvoid CL_ParseEstablished(void)\n{\n#ifdef NQPROT\n\tcls.qex = false;\n\tZ_Free(cl_dp_packagenames);\n\tcl_dp_packagenames = NULL;\n\tcl_dp_serverextension_download = false;\n\t*cl_dp_csqc_progsname = 0;\n\tcl_dp_csqc_progscrc = 0;\n\tcl_dp_csqc_progssize = 0;\n#endif\n\n\tif (cls.netchan.remote_address.type != NA_LOOPBACK)\n\t{\n\t\tchar fp[DIGEST_MAXSIZE*3+1+4];\n\t\tchar dig[DIGEST_MAXSIZE+1];\n\t\tchar cert[8192];\n\t\tconst char *security;\n\t\tswitch(cls.protocol)\n\t\t{\n\t\tcase CP_QUAKEWORLD:\tCon_DPrintf(S_COLOR_GRAY\"QW \");\tbreak;\n\t\tcase CP_NETQUAKE:\tCon_Printf (S_COLOR_GRAY\"NQ \");\tbreak;\n\t\tcase CP_QUAKE2:\t\tCon_Printf (S_COLOR_GRAY\"Q2 \");\tbreak;\n\t\tcase CP_QUAKE3:\t\tCon_Printf (S_COLOR_GRAY\"Q3 \");\tbreak;\n\t\tdefault: break;\n\t\t}\n\t\t*fp = 0;\n\t\tif (NET_IsEncrypted(&cls.netchan.remote_address))\n\t\t{\n\t\t\tif (cls.netchan.remote_address.prot == NP_DTLS)\n\t\t\t{\n\t\t\t\tint sz = NET_GetConnectionCertificate(cls.sockets, &cls.netchan.remote_address, QCERT_PEERCERTIFICATE, cert,sizeof(cert));\n\t\t\t\tif (sz >= 0)\n\t\t\t\t{\n\t\t\t\t\tQ_strncpyz(fp, \"?fp=\",sizeof(fp));\n\t\t\t\t\tsz = 4+Base64_EncodeBlockURI(dig, CalcHash(&hash_certfp, dig,sizeof(dig), cert, sz), fp+4,sizeof(fp)-4);\n\t\t\t\t\tfp[sz] = 0;\n\t\t\t\t}\n\t\t\t}\n\t\t\tsecurity = localtext(\"^[\"S_COLOR_GREEN\"encrypted\\\\tip\\\\Any passwords will be sent securely, but will still be readable by the server admin\\n^]\");\n\t\t}\n\t\telse\n\t\t\tsecurity = localtext(\"^[\"S_COLOR_RED\"plain-text\\\\tip\\\\\"CON_WARNING\"Do not type passwords as they can potentially be seen by network sniffers^]\");\n\n\t\tCon_Printf (\"\\r\");\n\t\tCon_TPrintf (\"Connected to ^[\"S_COLOR_BLUE\"%s\"S_COLOR_GRAY\"%s\\\\type\\\\connect %s%s^] (%s).\\n\", cls.servername, fp, cls.servername, fp, security);\n\t}\n}\n\n#ifdef NQPROT\nstatic void CLNQ_ParseProtoVersion(void)\n{\n\tint protover;\n\tstruct netprim_s netprim;\n\n\tcls.fteprotocolextensions = 0;\n\tcls.fteprotocolextensions2 = 0;\n\tcls.ezprotocolextensions1 = 0;\n\tfor(;;)\n\t{\n\t\tprotover = MSG_ReadLong ();\n\t\tswitch(protover)\n\t\t{\n\t\tcase PROTOCOL_VERSION_FTE1:\n\t\t\tcls.fteprotocolextensions = MSG_ReadLong();\n\t\t\tcontinue;\n\t\tcase PROTOCOL_VERSION_FTE2:\n\t\t\tcls.fteprotocolextensions2 = MSG_ReadLong();\n\t\t\tcontinue;\n\t\tdefault:\n\t\t\tbreak;\n\t\t}\n\t\tbreak;\n\t}\n\n\tnetprim.coordtype = COORDTYPE_FIXED_13_3;\n\tnetprim.anglesize = 1;\n\n\tcls.protocol_nq = CPNQ_ID;\n\tcls.z_ext = 0;\n\n#ifdef HAVE_LEGACY\n\tif (protover == PROTOCOL_VERSION_NQ && cls.demoplayback)\n\t{\n\t\tif (!Q_strcasecmp(FS_GetGamedir(true), \"nehahra\"))\n\t\t\tprotover = PROTOCOL_VERSION_NEHD;\t//if we're using the nehahra gamedir, pretend that it was the nehahra protocol version. clients should otherwise be using a different protocol.\n\t}\n#endif\n\n\tif (protover == PROTOCOL_VERSION_NEHD)\n\t{\n\t\tcls.protocol_nq = CPNQ_NEHAHRA;\n\t\tCon_DPrintf(\"Nehahra demo net protocol\\n\");\n\t}\n\telse if (protover == PROTOCOL_VERSION_FITZ)\n\t{\n\t\t//fitzquake 0.85\n\t\tcls.protocol_nq = CPNQ_FITZ666;\n\t\tCon_DPrintf(\"FitzQuake 666 protocol\\n\");\n\t}\n\telse if (protover == PROTOCOL_VERSION_RMQ)\n\t{\n\t\tint fl;\n\t\tcls.protocol_nq = CPNQ_FITZ666;\n\t\tCon_DPrintf(\"RMQ extensions to FitzQuake's protocol\\n\");\n\t\tfl = MSG_ReadLong();\n\n\t\tif (((fl & RMQFL_SHORTANGLE) && (fl & RMQFL_FLOATANGLE)) ||\n\t\t\t((fl & RMQFL_24BITCOORD) && (fl & RMQFL_INT32COORD)) ||\n\t\t\t((fl & RMQFL_24BITCOORD) && (fl & RMQFL_FLOATCOORD)) ||\n\t\t\t((fl & RMQFL_INT32COORD) && (fl & RMQFL_FLOATCOORD)) )\n\t\t\tHost_EndGame(\"Server is using conflicting RMQ protocol bits - %#x\\n\", fl);\n\n\t\tif (fl & RMQFL_SHORTANGLE)\n\t\t\tnetprim.anglesize = 2;\n\t\tif (fl & RMQFL_FLOATANGLE)\n\t\t\tnetprim.anglesize = 4;\n\t\tif (fl & RMQFL_24BITCOORD)\n\t\t\tnetprim.coordtype = COORDTYPE_FIXED_16_8;\n\t\tif (fl & RMQFL_INT32COORD)\n\t\t\tnetprim.coordtype = COORDTYPE_FIXED_28_4;\n\t\tif (fl & RMQFL_FLOATCOORD)\n\t\t\tnetprim.coordtype = COORDTYPE_FLOAT_32;\n\n\t\tfl &= ~(RMQFL_SHORTANGLE|RMQFL_FLOATANGLE|RMQFL_24BITCOORD|RMQFL_INT32COORD|RMQFL_FLOATCOORD|RMQFL_EDICTSCALE);\n\t\tif (fl)\n\t\t\tHost_EndGame(\"Server is using unsupported RMQ extensions - %#x\\n\", fl);\n\t}\n\telse if (protover == PROTOCOL_VERSION_DP5)\n\t{\n\t\t//darkplaces5\n\t\tcls.protocol_nq = CPNQ_DP5;\n\t\tnetprim.coordtype = COORDTYPE_FLOAT_32;\n\t\tnetprim.anglesize = 2;\n\n\t\tCon_DPrintf(\"DP5 protocols\\n\");\n\t}\n\telse if (protover == PROTOCOL_VERSION_DP6)\n\t{\n\t\t//darkplaces6 (it's a small difference from dp5)\n\t\tcls.protocol_nq = CPNQ_DP6;\n\t\tnetprim.coordtype = COORDTYPE_FLOAT_32;\n\t\tnetprim.anglesize = 2;\n\n\t\tcls.z_ext = Z_EXT_VIEWHEIGHT;\n\n\t\tCon_DPrintf(\"DP6 protocols\\n\");\n\t}\n\telse if (protover == PROTOCOL_VERSION_DP7)\n\t{\n\t\t//darkplaces7 (it's a small difference from dp5)\n\t\tcls.protocol_nq = CPNQ_DP7;\n\t\tnetprim.coordtype = COORDTYPE_FLOAT_32;\n\t\tnetprim.anglesize = 2;\n\n\t\tcls.z_ext = Z_EXT_VIEWHEIGHT;\n\n\t\tCon_DPrintf(\"DP7 protocols\\n\");\n\t}\n\telse if (protover == PROTOCOL_VERSION_H2)\n\t{\n\t\tif (cls.demoplayback)\n\t\t\tcls.protocol_nq = CPNQ_H2MP;\n\t\telse\n\t\t\tHost_EndGame (\"\\nUnable to connect to standard Hexen2 servers. Host the game with \"DISTRIBUTION\"\\n\");\n\t}\n\telse if (protover == PROTOCOL_VERSION_BJP1)\n\t{\n\t\tcls.protocol_nq = CPNQ_BJP1;\n\t\tCon_DPrintf(\"bjp1 %i protocol\\n\", PROTOCOL_VERSION_BJP1);\n\t}\n\telse if (protover == PROTOCOL_VERSION_BJP2)\n\t{\n\t\tcls.protocol_nq = CPNQ_BJP2;\n\t\tCon_DPrintf(\"bjp2 %i protocol\\n\", PROTOCOL_VERSION_BJP2);\n\t}\n\telse if (protover == PROTOCOL_VERSION_BJP3)\n\t{\n\t\tcls.protocol_nq = CPNQ_BJP3;\n\t\tCon_DPrintf(\"bjp3 %i protocol\\n\", PROTOCOL_VERSION_BJP3);\n\t}\n\telse if (protover == PROTOCOL_VERSION_NQ)\n\t\tCon_DPrintf(\"Standard NQ protocols\\n\");\n\telse\n\t\tHost_EndGame (\"Server is using protocol version %i, which is not supported by this version of \" FULLENGINENAME \".\", protover);\n\tif (cls.fteprotocolextensions & PEXT_FLOATCOORDS)\n\t{\n\t\tif (netprim.anglesize < 2)\n\t\t\tnetprim.anglesize = 2;\n\t\tif (netprim.coordtype < COORDTYPE_FLOAT_32)\n\t\t\tnetprim.coordtype = COORDTYPE_FLOAT_32;\n\t}\n\tcls.netchan.message.prim = cls.netchan.netprim = netprim;\n\tMSG_ChangePrimitives(netprim);\n}\n\nstatic int CL_Darkplaces_Particle_Precache(const char *pname)\n{\n\tint i;\n\tfor (i = 1; i < MAX_SSPARTICLESPRE; i++)\n\t{\n\t\tif (!cl.particle_ssname[i])\n\t\t{\n\t\t\tcl.particle_ssname[i] = strdup(pname);\n\t\t\tcl.particle_ssprecache[i] = P_FindParticleType(pname);\n\t\t\tcl.particle_ssprecaches = true;\n\t\t\treturn i;\n\t\t}\n\t\tif (!strcmp(cl.particle_ssname[i], pname))\n\t\t\treturn i;\n\t}\n\treturn 0;\t//failed\n}\n\n//FIXME: move to header\nvoid CL_KeepaliveMessage(void){}\nstatic void CLNQ_ParseServerData(void)\t\t//Doesn't change gamedir - use with caution.\n{\n\tint\tnummodels, numsounds;\n\tchar\t*str = NULL;\n\tint gametype;\n\tCon_DPrintf (\"Serverdata packet %s.\\n\", cls.demoplayback?\"read\":\"received\");\n\tSCR_SetLoadingStage(LS_CLIENT);\n\tCL_ClearState (true);\n#ifdef QUAKEHUD\n\tStats_NewMap();\n#endif\n\tCvar_ForceCallback(Cvar_FindVar(\"r_particlesdesc\"));\n\n\tCLNQ_ParseProtoVersion();\n\n\tif (cls.qex)\n\t{\n\t\tcl.allocated_client_slots = MSG_ReadPlayer();\n\t\tstr = MSG_ReadString();\n\t}\n\telse\n\t{\n\t\tif (cls.fteprotocolextensions2 & PEXT2_PREDINFO)\n\t\t\tstr = MSG_ReadString();\n\t\tcl.allocated_client_slots = MSG_ReadPlayer();\n\t}\n\tif (str)\n\t{\n#ifndef CLIENTONLY\n\t\tif (!sv.state)\n#endif\n\t\t\tCOM_Gamedir(str, NULL);\n\t}\n\tif (cl.allocated_client_slots > MAX_CLIENTS)\n\t{\n\t\tcl.allocated_client_slots = MAX_CLIENTS;\n\t\tCon_Printf (\"\\nWarning, this server supports more than %i clients, additional clients will do bad things\\n\", MAX_CLIENTS);\n\t}\n\n\tcl.splitclients = 1;\n\n\n\tgametype = MSG_ReadByte ();\n\n\tstr = MSG_ReadString ();\n\tQ_strncpyz (cl.levelname, str, sizeof(cl.levelname));\n\n\t// seperate the printfs so the server message can have a color\n#if 1\n\tCon_Printf (\"\\n\\n\");\n\tCon_Printf (\"^Ue01d^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01f\");\n\tCon_Printf (\"\\n\\n\");\n\tCon_Printf (\"\\1%s\\n\", str);\n#else\n\tCon_TPrintf (\"\\n\\n^Ue01d^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01f\\n\\n\");\n\tCon_Printf (\"%c%s\\n\", 2, str);\n#endif\n\n\tSCR_BeginLoadingPlaque();\n\n\tSurf_PreNewMap();\n\n\tfor (nummodels=0; nummodels < countof(cl.model_name); nummodels++)\n\t{\n\t\tif (cl.model_name[nummodels])\n\t\t{\n\t\t\tZ_Free(cl.model_name[nummodels]);\n\t\t\tcl.model_name[nummodels] = NULL;\n\t\t}\n\t}\n\tfor (nummodels=1 ; ; nummodels++)\n\t{\n\t\tstr = MSG_ReadString ();\n\t\tif (!str[0])\n\t\t\tbreak;\n\t\tif (nummodels==MAX_PRECACHE_MODELS)\n\t\t{\n\t\t\tCon_TPrintf (\"Server sent too many model precaches\\n\");\n\t\t\treturn;\n\t\t}\n\t\tZ_StrDupPtr(&cl.model_name[nummodels], str);\n\t\tif (*str != '*' && strcmp(str, \"null\"))\t//not inline models!\n\t\t\tCL_CheckOrEnqueDownloadFile(str, NULL, ((nummodels==1)?DLLF_REQUIRED|DLLF_ALLOWWEB:0));\n\n\t\t//qw has a special network protocol for spikes.\n\t\tif (!strcmp(cl.model_name[nummodels],\"progs/spike.mdl\"))\n\t\t\tcl_spikeindex = nummodels;\n\t\tif (!strcmp(cl.model_name[nummodels],\"progs/player.mdl\"))\n\t\t\tcl_playerindex = nummodels;\n#ifdef HAVE_LEGACY\n\t\tif (cl.model_name_vwep[0] && !strcmp(cl.model_name[nummodels],cl.model_name_vwep[0]) && cl_playerindex == -1)\n\t\t\tcl_playerindex = nummodels;\n#endif\n\t\tif (!strcmp(cl.model_name[nummodels],\"progs/h_player.mdl\"))\n\t\t\tcl_h_playerindex = nummodels;\n\t\tif (!strcmp(cl.model_name[nummodels],\"progs/flag.mdl\"))\n\t\t\tcl_flagindex = nummodels;\n\n\t\t//rocket to grenade\n\t\tif (!strcmp(cl.model_name[nummodels],\"progs/missile.mdl\"))\n\t\t\tcl_rocketindex = nummodels;\n\t\tif (!strcmp(cl.model_name[nummodels],\"progs/grenade.mdl\"))\n\t\t\tcl_grenadeindex = nummodels;\n\n\t\t//cl_gibfilter\n\t\tif (!strcmp(cl.model_name[nummodels],\"progs/gib1.mdl\"))\n\t\t\tcl_gib1index = nummodels;\n\t\tif (!strcmp(cl.model_name[nummodels],\"progs/gib2.mdl\"))\n\t\t\tcl_gib2index = nummodels;\n\t\tif (!strcmp(cl.model_name[nummodels],\"progs/gib3.mdl\"))\n\t\t\tcl_gib3index = nummodels;\n\n\t\tMod_TouchModel (str);\n\t}\n\n\tfor (numsounds=0; numsounds < countof(cl.sound_name); numsounds++)\n\t{\n\t\tif (cl.sound_name[numsounds])\n\t\t{\n\t\t\tZ_Free(cl.sound_name[numsounds]);\n\t\t\tcl.sound_name[numsounds] = NULL;\n\t\t}\n\t}\n\tfor (numsounds=1 ; ; numsounds++)\n\t{\n\t\tstr = MSG_ReadString ();\n\t\tif (!str[0])\n\t\t\tbreak;\n\t\tif (numsounds==MAX_PRECACHE_SOUNDS)\n\t\t{\n\t\t\tCon_TPrintf (\"Server sent too many sound precaches\\n\");\n\t\t\treturn;\n\t\t}\n\t\tZ_StrDupPtr(&cl.sound_name[numsounds], str);\n\t\tcl.sound_precache[numsounds] = S_FindName(cl.sound_name[numsounds], true, false);\n\n\t\tSound_CheckDownload(str);\n\t}\n\n\tcls.signon = 0;\n\tcls.state = ca_onserver;\n\tCam_AutoTrack_Update(NULL);\n\n\n\t//fill in the csqc stuff\n\tif (!cl_dp_csqc_progscrc)\n\t{\n\t\tInfoBuf_RemoveKey(&cl.serverinfo, \"*csprogs\");\n\t\tInfoBuf_RemoveKey(&cl.serverinfo, \"*csprogssize\");\n\t\tInfoBuf_RemoveKey(&cl.serverinfo, \"*csprogsname\");\n\t}\n\telse\n\t{\n\t\tInfoBuf_SetStarKey(&cl.serverinfo, \"*csprogs\",\t\tva(\"%i\", cl_dp_csqc_progscrc));\n\t\tInfoBuf_SetStarKey(&cl.serverinfo, \"*csprogssize\", va(\"%i\", cl_dp_csqc_progssize));\n\t\tInfoBuf_SetStarKey(&cl.serverinfo, \"*csprogsname\", va(\"%s\", cl_dp_csqc_progsname));\n\t}\n\n\tif (cl_dp_packagenames)\n\t{\n\t\tchar *in = cl_dp_packagenames;\n\t\tif (cl.serverpacknames)\n\t\t\tZ_StrCat(&cl.serverpacknames, \" \");\n\t\tZ_StrCat(&cl.serverpacknames, in);\n\t\twhile ((in = COM_Parse(in)))\n\t\t{\n\t\t\tZ_StrCat(&cl.serverpackhashes, cl.serverpackhashes?\" -\":\"-\");\t//no hash info.\n\t\t\tcl.serverpakschanged = true;\n\t\t}\n\t}\n\n\n\t//update gamemode\n\tif (gametype != GAME_COOP)\n\t\tInfoBuf_SetKey(&cl.serverinfo, \"deathmatch\", \"1\");\n\telse\n\t\tInfoBuf_SetKey(&cl.serverinfo, \"deathmatch\", \"0\");\n\tInfoBuf_SetKey(&cl.serverinfo, \"teamplay\", \"0\");\n\n\t//allow some things by default that quakeworld bans by default\n\tInfoBuf_SetKey(&cl.serverinfo, \"watervis\", \"1\");\n\n\t//prohibit some things that QW/FTE has enabled by default, which would be frowned upon in NQ\n\tInfoBuf_SetKey(&cl.serverinfo, \"fbskins\", \"0\");\n\n\t//pretend it came from the server, and update cheat/permissions/etc\n\tCL_CheckServerInfo();\n\n\t#if _MSC_VER > 1200\n\tSys_RecentServer(\"+connectnq\", cls.servername, cls.servername, \"Join NQ Server\");\n\t#endif\n\n\tif (CPNQ_IS_DP)\t//DP's protocol requires client+server to have exactly the same data files. this is shit, but in the interests of compatibility...\n\t\tCOM_Effectinfo_Enumerate(CL_Darkplaces_Particle_Precache);\n\n#ifdef VOICECHAT\n\tS_Voip_MapChange();\n#endif\n\n#ifdef CSQC_DAT\n\tCSQC_Shutdown();\n#endif\n}\nstatic void CLQEX_ParseServerVars(void)\n{\n\tunsigned int bits = MSG_ReadULEB128();\n\n\tif (bits & QEX_GV_DEATHMATCH)\n\t\tInfoBuf_SetStarKey(&cl.serverinfo, \"deathmatch\", va(\"%i\", MSG_ReadByte ()));\n\tif (bits & QEX_GV_IDEALPITCHSCALE)\n\t\tMSG_ReadFloat ();\n\tif (bits & QEX_GV_FRICTION)\n\t\tmovevars.friction = MSG_ReadFloat ();\n\tif (bits & QEX_GV_EDGEFRICTION)\n\t\tInfoBuf_SetStarKey(&cl.serverinfo, \"pm_edgefriction\", va(\"%g\", MSG_ReadFloat ()));\n\tif (bits & QEX_GV_STOPSPEED)\n\t\tmovevars.stopspeed = MSG_ReadFloat ();\n\tif (bits & QEX_GV_MAXVELOCITY)\n\t\t/*movevars.maxvelocity =*/ MSG_ReadFloat ();\n\tif (bits & QEX_GV_GRAVITY)\n\t\tmovevars.gravity = MSG_ReadFloat ();\n\tif (bits & QEX_GV_NOSTEP)\n\t\t/*movevars.nostep =*/ MSG_ReadByte ();\n\tif (bits & QEX_GV_MAXSPEED)\n\t\tmovevars.maxspeed = MSG_ReadFloat ();\n\tif (bits & QEX_GV_ACCELERATE)\n\t\tmovevars.accelerate = MSG_ReadFloat ();\n\tif (bits & QEX_GV_CONTROLLERONLY)\n\t\tInfoBuf_SetStarKey(&cl.serverinfo, \"nomouse\", va(\"%i\", MSG_ReadByte ()));\n\tif (bits & QEX_GV_TIMELIMIT)\n\t\tInfoBuf_SetStarKey(&cl.serverinfo, \"timelimit\", va(\"%g\", MSG_ReadFloat ()));\n\tif (bits & QEX_GV_FRAGLIMIT)\n\t\tInfoBuf_SetStarKey(&cl.serverinfo, \"fraglimit\", va(\"%g\", MSG_ReadFloat ()));\n\tif (bits & QEX_GV_TEAMPLAY)\n\t\tInfoBuf_SetStarKey(&cl.serverinfo, \"teamplay\", va(\"%i\", MSG_ReadByte ()));\n\tif (bits & ~QEX_GV_ALL)\n\t\tCon_Printf(\"CLQEX_ParseServerVars: Unknown bits %#x\\n\", bits & ~QEX_GV_ALL);\n\n\tCL_CheckServerInfo();\n}\nstatic void CLQEX_ParsePrompt(void)\n{\n\tint a, count = MSG_ReadByte(), imp;\n\tconst char *s;\n\tchar message[65536];\n\tsize_t ofs = 0;\n\n\tif (count == 0)\n\t{\n\t\tSCR_CenterPrint(0, NULL, true);\n\t\treturn;\n\t}\n\t*message = 0;\n\ts = MSG_ReadString();\n\tQ_strncatz(message+ofs, \"/S/C/.\", sizeof(message)-ofs);\n\tofs += strlen(message+ofs);\n\tTL_Reformat(com_language, message+ofs, sizeof(message)-ofs, 1, &s);\n\tofs += strlen(message+ofs);\n\n\tQ_strncatz(message+ofs, \"\\n\", sizeof(message)-ofs);\n\tofs += strlen(message+ofs);\n\tfor (a = 0; a < count; a++)\n\t{\n\t\ts = MSG_ReadString();\n\t\timp = MSG_ReadByte();\n\t\tQ_strncatz(message+ofs, \"^[[\", sizeof(message)-ofs);\n\t\tofs += strlen(message+ofs);\n\t\tTL_Reformat(com_language, message+ofs, sizeof(message)-ofs, 1, &s);\n\t\tofs += strlen(message+ofs);\n\t\tQ_strncatz(message+ofs, va(\"]\\\\impulse\\\\%i^]\\n\", imp), sizeof(message)-ofs);\n\t\tofs += strlen(message+ofs);\n\t}\n\tSCR_CenterPrint(0, message, true);\n}\nstatic char *CLQEX_ReadStrings(void)\n{\n\tunsigned short count = MSG_ReadShort(), a;\n\tconst char *arg[256];\n\tstatic char formatted[8192];\n\tchar inputs[65536];\n\tsize_t ofs = 0;\n\tfor (a = 0; a < count && a < countof(arg); )\n\t{\n\t\targ[a++] = MSG_ReadStringBuffer(inputs+ofs, sizeof(inputs)-ofs-1);\n\t\tofs += strlen(inputs+ofs)+1;\n\t\tif (ofs >= sizeof(inputs))\n\t\t\tbreak;\n\t}\n\tfor (; a < count; a++)\n\t\tMSG_ReadString(); //don't lose space, though we can't buffer it.\n\n\tTL_Reformat(com_language, formatted, sizeof(formatted), a, arg);\n\treturn formatted;\n}\n\nstatic void CLNQ_SendInitialUserInfo(void *ctx, const char *key, const char *value)\n{\n\tInfoSync_Add(&cls.userinfosync, ctx, key);\n}\nvoid CLNQ_SignonReply (void)\n{\n\textern cvar_t\ttopcolor;\n\textern cvar_t\tbottomcolor;\n\textern cvar_t\trate;\n\textern cvar_t\tmodel;\n\textern cvar_t\tskin;\n\nCon_DPrintf (\"CL_SignonReply: %i\\n\", cls.signon);\n\n\tswitch (cls.signon)\n\t{\n\tcase 1:\n\t\tcl.sendprespawn = true;\n\t\tSCR_SetLoadingFile(\"loading data\");\n\t\tCL_RequestNextDownload();\t//this sucks, but sometimes mods send csqc-specific messages to us before things are properly inited. if we start doing stuff now then we can minimize the chances of dodgy mods screwing with us. FIXME: warn about receiving csqc messages before begin.\n\t\tbreak;\n\n\tcase 2:\n\t\tCL_SendClientCommand(true, \"name \\\"%s\\\"\\n\", name.string);\n\t\tCL_SendClientCommand(true, \"color %i %i\\n\", topcolor.ival, bottomcolor.ival);\n\t\tif (cl.haveserverinfo)\n\t\t\tInfoBuf_Enumerate(&cls.userinfo[0], &cls.userinfo[0], CLNQ_SendInitialUserInfo);\n\t\telse if (CPNQ_IS_DP)\n\t\t{\t//dp needs a couple of extras to work properly in certain cases. don't send them on other servers because that generally results in error messages.\n\t\t\tCL_SendClientCommand(true, \"rate %s\", rate.string);\n\t\t\tCL_SendClientCommand(true, \"playermodel %s\", model.string);\n\t\t\tCL_SendClientCommand(true, \"playerskin %s\", skin.string);\n\t\t}\n\t\tCL_SendClientCommand(true, \"spawn %s\", \"\");\n\t\tbreak;\n\n\tcase 3:\n\t\tCL_SendClientCommand(true, \"begin\");\n\t\tbreak;\n\n\tcase 4:\n\t\tSCR_EndLoadingPlaque ();\t\t// allow normal screen updates\n\t\tSCR_SetLoadingStage(LS_NONE);\n\t\tbreak;\n\t}\n}\n\n#define\tDEFAULT_VIEWHEIGHT\t22\nstatic void CLNQ_ParseClientdata (void)\n{\n\tint\t\ti;\n\tconst int seat = 0;\n\tplayer_state_t *pl = &cl.inframes[cl.validsequence&UPDATE_MASK].playerstate[cl.playerview[seat].playernum];\n\n\tunsigned int bits;\n\n\tbits = (unsigned short)MSG_ReadShort();\n\n\tif (bits & SU_EXTEND1)\n\t\tbits |= (MSG_ReadByte() << 16);\n\tif (bits & SU_EXTEND2)\n\t\tbits |= (MSG_ReadByte() << 24);\n\n\tif (bits & SU_VIEWHEIGHT)\n\t\tCL_SetStatInt(0, STAT_VIEWHEIGHT, MSG_ReadChar ());\n\telse if ((!CPNQ_IS_DP || cls.protocol_nq <= CPNQ_DP5) && cls.protocol_nq != CPNQ_H2MP)\n\t\tCL_SetStatInt(0, STAT_VIEWHEIGHT, DEFAULT_VIEWHEIGHT);\n\n\tif (bits & SU_IDEALPITCH)\n\t\tCL_SetStatInt(0, STAT_IDEALPITCH, MSG_ReadChar ());\n\telse if (cls.protocol_nq != CPNQ_H2MP)\n\t\tCL_SetStatInt(0, STAT_IDEALPITCH, 0);\n\n\tif (cls.protocol_nq == CPNQ_H2MP && (bits & (1<<8)/*SU_IDEALROLL*/))\n\t\tMSG_ReadChar ();\n\n\tfor (i=0 ; i<3 ; i++)\n\t{\n\t\tif (bits & (SU_PUNCH1<<i) )\n\t\t\tCL_SetStatFloat(seat, STAT_PUNCHANGLE_X+i, CPNQ_IS_DP?MSG_ReadAngle16():MSG_ReadChar());\n\t\telse if (cls.protocol_nq != CPNQ_H2MP)\n\t\t\tCL_SetStatFloat(seat, STAT_PUNCHANGLE_X+i, 0);\n\n\t\tif (CPNQ_IS_DP && bits & (DPSU_PUNCHVEC1<<i))\n\t\t\tCL_SetStatFloat(seat, STAT_PUNCHVECTOR_X+i, MSG_ReadCoord());\n\t\telse if (cls.protocol_nq != CPNQ_H2MP)\n\t\t\tCL_SetStatFloat(seat, STAT_PUNCHVECTOR_X+i, 0);\n\n\t\tif (bits & (SU_VELOCITY1<<i))\n\t\t{\n\t\t\tif (CPNQ_IS_DP || (cls.qex && (bits & QEX_SU_FLOATCOORDS)))\n\t\t\t\tpl->velocity[i] = MSG_ReadFloat();\n\t\t\telse\n\t\t\t\tpl->velocity[i] = MSG_ReadChar()*16;\n\t\t}\n\t\telse if (cls.protocol_nq != CPNQ_H2MP)\n\t\t\tpl->velocity[i] = 0;\n\t}\n\n\tif ((bits & SU_ITEMS) || cls.protocol_nq == CPNQ_ID)\t//hipnotic bug - hipnotic demos don't always have SU_ITEMS set, yet they update STAT_ITEMS anyway.\n\t\tCL_SetStatInt(0, STAT_ITEMS, MSG_ReadLong());\n\n\tpl->onground = (bits & SU_ONGROUND) != 0;\n\tif (bits & SU_INWATER)\n\t\tpl->flags |= PF_INWATER;\t//mostly just means smartjump should be used.\n\telse\n\t\tpl->flags &= ~PF_INWATER;\n\n\tif (cls.protocol_nq == CPNQ_DP5)\n\t{\n\t\tCL_SetStatInt(0, STAT_WEAPONFRAME, (bits & SU_WEAPONFRAME)?(unsigned short)MSG_ReadShort():0);\n\t\tCL_SetStatInt(0, STAT_ARMOR, (bits & SU_ARMOR)?MSG_ReadShort():0);\n\t\tCL_SetStatInt(0, STAT_WEAPONMODELI, (bits & SU_WEAPONMODEL)?MSG_ReadShort():0);\n\n\t\tCL_SetStatInt(0, STAT_HEALTH, MSG_ReadShort());\n\n\t\tCL_SetStatInt(0, STAT_AMMO, MSG_ReadShort());\n\n\t\tCL_SetStatInt(0, STAT_SHELLS, MSG_ReadShort());\n\t\tCL_SetStatInt(0, STAT_NAILS, MSG_ReadShort());\n\t\tCL_SetStatInt(0, STAT_ROCKETS, MSG_ReadShort());\n\t\tCL_SetStatInt(0, STAT_CELLS, MSG_ReadShort());\n\n\t\tCL_SetStatInt(0, STAT_ACTIVEWEAPON, (unsigned short)MSG_ReadShort());\n\t}\n\telse if (CPNQ_IS_DP && cls.protocol_nq > CPNQ_DP5)\n\t{\n\t\t/*nothing in dp6+*/\n\t}\n\telse if (cls.protocol_nq == CPNQ_H2MP)\n\t{\t//only changed stuff\n\t\tif (bits & SU_WEAPONFRAME)\tCL_SetStatInt(0, STAT_WEAPONFRAME, MSG_ReadByte());\n\t\tif (bits & SU_ARMOR)\t\tCL_SetStatInt(0, STAT_ARMOR, MSG_ReadByte());\n\t\tif (bits & SU_WEAPONMODEL)\tCL_SetStatInt(0, STAT_WEAPONMODELI, MSG_ReadUInt16());\n\t}\t//nothing else.\n\telse\n\t{\n\t\tint weaponmodel = 0, armour = 0, weaponframe = 0, health = 0, currentammo = 0, shells = 0, nails = 0, rockets = 0, cells = 0, activeweapon = 0;\n\n\t\tif (bits & SU_WEAPONFRAME)\tweaponframe |= (unsigned char)MSG_ReadByte();\n\t\tif (bits & SU_ARMOR)\t\tarmour |= (unsigned char)MSG_ReadByte();\n\t\tif (bits & SU_WEAPONMODEL)\n\t\t{\n\t\t\tif (CPNQ_IS_BJP)\n\t\t\t\tweaponmodel |= (unsigned short)MSG_ReadShort();\n\t\t\telse\n\t\t\t\tweaponmodel |= (unsigned char)MSG_ReadByte();\n\t\t}\n\t\thealth |= MSG_ReadShort();\n\t\tcurrentammo |= MSG_ReadByte();\n\t\tshells |= MSG_ReadByte();\n\t\tnails |= MSG_ReadByte();\n\t\trockets |= MSG_ReadByte();\n\t\tcells |= MSG_ReadByte();\n\t\tactiveweapon |= MSG_ReadByte();\n\n\t\tif (cls.protocol_nq == CPNQ_FITZ666)\n\t\t{\n\t\t\tif (bits & FITZSU_WEAPONMODEL2)\n\t\t\t\tweaponmodel |= MSG_ReadByte() << 8;\n\t\t\tif (bits & FITZSU_ARMOR2)\n\t\t\t\tarmour |= MSG_ReadByte() << 8;\n\t\t\tif (bits & FITZSU_AMMO2)\n\t\t\t\tcurrentammo |= MSG_ReadByte() << 8;\n\t\t\tif (bits & FITZSU_SHELLS2)\n\t\t\t\tshells |= MSG_ReadByte() << 8;\n\t\t\tif (bits & FITZSU_NAILS2)\n\t\t\t\tnails |= MSG_ReadByte() << 8;\n\t\t\tif (bits & FITZSU_ROCKETS2)\n\t\t\t\trockets |= MSG_ReadByte() << 8;\n\t\t\tif (bits & FITZSU_CELLS2)\n\t\t\t\tcells |= MSG_ReadByte() << 8;\n\t\t\tif (bits & FITZSU_WEAPONFRAME2)\n\t\t\t\tweaponframe |= MSG_ReadByte() << 8;\n\t\t\tif (bits & FITZSU_WEAPONALPHA)\n\t\t\t\tMSG_ReadByte();\n\n\t\t\tif (cls.qex)\n\t\t\t{\n\t\t\t\tif (bits & QEX_SU_ENTFLAGS)\t/*entflags =*/ MSG_ReadULEB128();\n\t\t\t}\n\t\t}\n\n\t\tCL_SetStatInt(0, STAT_WEAPONFRAME, weaponframe);\n\t\tCL_SetStatInt(0, STAT_ARMOR, armour);\n\t\tCL_SetStatInt(0, STAT_WEAPONMODELI, weaponmodel);\n\n\t\tCL_SetStatInt(0, STAT_HEALTH, health);\n\n\t\tCL_SetStatInt(0, STAT_AMMO, currentammo);\n\n\t\tCL_SetStatInt(0, STAT_SHELLS, shells);\n\t\tCL_SetStatInt(0, STAT_NAILS, nails);\n\t\tCL_SetStatInt(0, STAT_ROCKETS, rockets);\n\t\tCL_SetStatInt(0, STAT_CELLS, cells);\n\n\t\tCL_SetStatInt(0, STAT_ACTIVEWEAPON, activeweapon);\n\t}\n\n\tif (CPNQ_IS_DP)\n\t{\n\t\tif (bits & DPSU_VIEWZOOM)\n\t\t{\n\t\t\tif (cls.protocol_nq >= CPNQ_DP5)\n\t\t\t\ti = (unsigned short) MSG_ReadShort();\n\t\t\telse\n\t\t\t\ti = MSG_ReadByte();\n\t\t\tif (i < 2)\n\t\t\t\ti = 2;\n\t\t\tCL_SetStatFloat(0, STAT_VIEWZOOM, i*(STAT_VIEWZOOM_SCALE/255.0));\n\t\t}\n\t\telse\n\t\t\tCL_SetStatFloat(0, STAT_VIEWZOOM, STAT_VIEWZOOM_SCALE);\n\t}\n}\n#endif\n/*\n==================\nCL_ParseSoundlist\n==================\n*/\nstatic void CL_ParseSoundlist (qboolean lots)\n{\n\tint\tnumsounds;\n\tchar\t*str;\n\tint n;\n\n// precache sounds\n//\tmemset (cl.sound_precache, 0, sizeof(cl.sound_precache));\n\n\tif (lots)\n\t\tnumsounds = MSG_ReadShort();\n\telse\n\t\tnumsounds = (cl.protocol_qw>=26)?MSG_ReadByte():0;\n\n\tfor (;;)\n\t{\n\t\tstr = MSG_ReadString ();\n\t\tif (!str[0])\n\t\t\tbreak;\n\t\tnumsounds++;\n\t\tif (numsounds >= MAX_PRECACHE_SOUNDS)\n\t\t\tHost_EndGame (\"Server sent too many sound_precache\");\n\n//\t\tif (strlen(str)>4)\n//\t\tif (!strcmp(str+strlen(str)-4, \".mp3\"))\t//don't let the server send us a specific mp3. convert it to wav and this way we know not to look outside the quake path for it.\n//\t\t\tstrcpy(str+strlen(str)-4, \".wav\");\n\n\t\tZ_StrDupPtr(&cl.sound_name[numsounds], str);\n\t}\n\n\tn = (cl.protocol_qw>=26)?MSG_ReadByte():0;\n\n\tif (n)\n\t{\n\t\tif (cls.demoplayback == DPB_MVD && (cls.demoeztv_ext&EZTV_DOWNLOAD))\n\t\t\t;\n\t\telse\n\t\t{\n\t\t\tif (CL_RemoveClientCommands(\"soundlist\") && !(cls.fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS))\n\t\t\t\tCon_DPrintf(\"Multiple soundlists\\n\");\n//\t\t\tCL_SendClientCommand(\"soundlist %i %i\", cl.servercount, n);\n\t\t\tCL_SendClientCommand(true, soundlist_name, cl.servercount, (numsounds&0xff00) + n);\n\t\t}\n\t\treturn;\n\t}\n\n#ifdef Q2CLIENT\n\tif (cls.protocol == CP_QUAKE2)\n\t{\n\t\tCL_AllowIndependantSendCmd(false);\t//stop it now, the indep stuff *could* require model tracing.\n\n\t\tcl.sendprespawn = true;\n\t\tSCR_SetLoadingFile(\"loading data\");\n\t}\n\telse\n#endif\n\t{\n\t\tif (cls.demoplayback == DPB_MVD && cls.demoeztv_ext)\n\t\t{\n\t\t\tif (CL_RemoveClientCommands(\"qtvmodellist\"))\n\t\t\t\tCon_DPrintf(\"Multiple modellists\\n\");\n\t\t\tCL_SendClientCommand (true, \"qtvmodellist %i 0\", cl.servercount);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (CL_RemoveClientCommands(\"modellist\") && !(cls.fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS))\n\t\t\t\tCon_DPrintf(\"Multiple modellists\\n\");\n//\t\t\tCL_SendClientCommand (\"modellist %i 0\", cl.servercount);\n\t\t\tCL_SendClientCommand (true, modellist_name, cl.servercount, 0);\n\t\t}\n\t}\n}\n\n/*\n==================\nCL_ParseModellist\n==================\n*/\nstatic void CL_ParseModellist (qboolean lots)\n{\n\tint\tnummodels;\n\tchar\t*str;\n\tint n;\n\n// precache models and note certain default indexes\n\tif (lots)\n\t\tnummodels = MSG_ReadShort();\n\telse\n\t\tnummodels = (cl.protocol_qw>=26)?MSG_ReadByte():0;\n\n\tfor (;;)\n\t{\n\t\tstr = MSG_ReadString ();\n\t\tif (!str[0])\n\t\t\tbreak;\n\t\tnummodels++;\n\t\tif (nummodels>=MAX_PRECACHE_MODELS)\n\t\t\tHost_EndGame (\"Server sent too many model_precache\");\n\t\tZ_StrDupPtr(&cl.model_name[nummodels], str);\n\t\tif (nummodels==1)\n\t\t\tSCR_ImageName(cl.model_name[nummodels]);\n\n\t\t//qw has a special network protocol for spikes.\n\t\tif (!strcmp(cl.model_name[nummodels],\"progs/spike.mdl\"))\n\t\t\tcl_spikeindex = nummodels;\n\t\tif (!strcmp(cl.model_name[nummodels],\"progs/player.mdl\"))\n\t\t\tcl_playerindex = nummodels;\n#ifdef HAVE_LEGACY\n\t\tif (cl.model_name_vwep[0] && !strcmp(cl.model_name[nummodels],cl.model_name_vwep[0]) && cl_playerindex == -1)\n\t\t\tcl_playerindex = nummodels;\n#endif\n\t\tif (!strcmp(cl.model_name[nummodels],\"progs/h_player.mdl\"))\n\t\t\tcl_h_playerindex = nummodels;\n\t\tif (!strcmp(cl.model_name[nummodels],\"progs/flag.mdl\"))\n\t\t\tcl_flagindex = nummodels;\n\n\t\t//rocket to grenade\n\t\tif (!strcmp(cl.model_name[nummodels],\"progs/missile.mdl\"))\n\t\t\tcl_rocketindex = nummodels;\n\t\tif (!strcmp(cl.model_name[nummodels],\"progs/grenade.mdl\"))\n\t\t\tcl_grenadeindex = nummodels;\n\n\t\t//cl_gibfilter\n\t\tif (!strcmp(cl.model_name[nummodels],\"progs/gib1.mdl\"))\n\t\t\tcl_gib1index = nummodels;\n\t\tif (!strcmp(cl.model_name[nummodels],\"progs/gib2.mdl\"))\n\t\t\tcl_gib2index = nummodels;\n\t\tif (!strcmp(cl.model_name[nummodels],\"progs/gib3.mdl\"))\n\t\t\tcl_gib3index = nummodels;\n\n\t\t//we have the names, we might as well START loading them now.\n\t\tif (COM_HasWorkers(WG_LOADER))\n\t\t\tMod_ForName (cl.model_name[nummodels], MLV_SILENT);\n\t}\n\n\tn = (cl.protocol_qw>=26)?MSG_ReadByte():0;\n\n\tif (n)\n\t{\n\t\tif (cls.demoplayback == DPB_MVD && cls.demoeztv_ext)\n\t\t\t;\n\t\telse\n\t\t{\n\t\t\tif (CL_RemoveClientCommands(\"modellist\") && !(cls.fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS))\n\t\t\t\tCon_DPrintf(\"Multiple modellists\\n\");\n//\t\t\tCL_SendClientCommand(\"modellist %i %i\", cl.servercount, n);\n\t\t\tCL_SendClientCommand(true, modellist_name, cl.servercount, (nummodels&0xff00) + n);\n\t\t}\n\t\treturn;\n\t}\n\n#ifdef QUAKESTATS\n\tif (cls.demoplayback == DPB_MVD && !cl.model_name_vwep[0] && !(cls.fteprotocolextensions2&PEXT2_REPLACEMENTDELTAS))\n\t\tCL_ParseStuffCmd(\"//vwep vwplayer w_axe w_shot w_shot2 w_nail w_nail2 w_rock w_rock2 w_light\\n\", 0);\n#endif\n\n\tSCR_SetLoadingFile(\"loading data\");\n\n\t//we need to try to load it now if we can, so any embedded archive will be loaded *before* we start looking for other content...\n\tcl.model_precache[1] = cl.model_name[1]?Mod_ForName (cl.model_name[1], MLV_SILENTSYNC):NULL;\n\tif (cl.model_precache[1] && cl.model_precache[1]->loadstate == MLS_LOADED)\n\t\tFS_LoadMapPackFile(cl.model_precache[1]->name, cl.model_precache[1]->archive);\n\n\tSound_CheckDownloads();\n\tModel_CheckDownloads();\n\n\tCL_AllowIndependantSendCmd(false);\t//stop it now, the indep stuff *could* require model tracing.\n\n\t//set the flag to load models and send prespawn\n\tcl.sendprespawn = true;\n}\n\n#ifdef Q2CLIENT\nstatic void CLQ2_ParseClientinfo(int i, char *s)\n{\n\tchar *model, *name;\n\tplayer_info_t *player;\n\t//s contains \"name\\model/skin\"\n\t//q2 doesn't really do much with userinfos.\n\n\tif (i >= MAX_CLIENTS)\n\t\treturn;\n\n\tplayer = &cl.players[i];\n\n\tInfoBuf_Clear(&player->userinfo, true);\n\tcl.players[i].userinfovalid = true;\n\n\tmodel = strchr(s, '\\\\');\n\tif (model)\n\t{\n\t\t*model = '\\0';\n\t\tmodel++;\n\t\tname = s;\n\t}\n\telse\n\t{\n\t\tname = \"Unnammed\";\n\t\tmodel = \"male\";\n\t}\n#if 0\n\tskin = strchr(model, '/');\n\tif (skin)\n\t{\n\t\t*skin = '\\0';\n\t\tskin++;\n\t}\n\telse\n\t\tskin = \"\";\n\tInfoBuf_SetValueForKey(&player->userinfo, \"model\", model);\n\tInfoBuf_SetValueForKey(&player->userinfo, \"skin\", skin);\n#else\n\tInfoBuf_SetValueForKey(&player->userinfo, \"skin\", model);\n#endif\n\tInfoBuf_SetValueForKey(&player->userinfo, \"name\", name);\n\n\tcl.players[i].userid = i;\n\tcl.players[i].rbottomcolor = 1;\n\tcl.players[i].rtopcolor = 1;\n\tCL_ProcessUserInfo (i, player);\n}\n\nvoid CLQ2EX_ParseLightConfigString(int i, const char *s);\nstatic void CLQ2_UpdateConfigString (unsigned int i, char *s)\n{\n\tif (i >= 0x8000 && i < 0x8000+MAX_PRECACHE_MODELS)\n\t{\n\t\ti -= 0x8000;\n\t\tgoto parsemodelindex;\n\t}\n\telse if (i >= 0xc000 && i < 0xc000+MAX_PRECACHE_SOUNDS)\n\t{\n\t\ti -= 0xc000;\n\t\tgoto parsesoundindex;\n\t}\n\tif (cls.protocol_q2 != PROTOCOL_VERSION_Q2EX)\n\t{\t//remap from vanilla to q2e\n#define PASTE(a,b) (a##b)\n#define REMAPR(n,l) \t\tif (i >= Q2CS_##n && i < Q2CS_##n+Q2MAX_##l) i = i-Q2CS_##n+Q2EXCS_##n; else\n#define REMAPS(n)\t\t\tif (i == PASTE(Q2CS_,n)) i = i-PASTE(Q2CS_,n)+PASTE(Q2EXCS_,n); else\n#define Q2MAX_STATUSBAR (Q2CS_AIRACCEL-Q2CS_STATUSBAR)\n\t\tREMAPS(NAME)\n\t\tREMAPS(CDTRACK)\n\t\tREMAPS(SKY)\n\t\tREMAPS(SKYAXIS)\n\t\tREMAPS(SKYROTATE)\n\t\tREMAPR(STATUSBAR, STATUSBAR)\n\t\tREMAPS(AIRACCEL)\n\t\tREMAPS(MAXCLIENTS)\n\t\tREMAPS(MAPCHECKSUM)\n\t\tREMAPR(MODELS, MODELS)\n\t\tREMAPR(SOUNDS, SOUNDS)\n\t\tREMAPR(IMAGES, IMAGES)\n\t\tREMAPR(LIGHTS, LIGHTSTYLES)\n\t\tREMAPR(ITEMS, ITEMS)\n\t\tREMAPR(PLAYERSKINS, CLIENTS)\n\t\tREMAPR(GENERAL, GENERAL)\n\t\tHost_EndGame (\"configstring %i > Q2MAX_CONFIGSTRINGS\", i);\n\t}\n\n\tif ((unsigned int)i >= Q2EXMAX_CONFIGSTRINGS)\n\t\tHost_EndGame (\"configstring %i > Q2EXMAX_CONFIGSTRINGS\", i);\n\n//\tstrncpy (olds, cl.configstrings[i], sizeof(olds));\n//\tolds[sizeof(olds) - 1] = 0;\n\n//\tstrcpy (cl.configstrings[i], s);\n\n\t// do something apropriate\n\n\tif (i == Q2EXCS_NAME)\n\t{\n\t\tQ_strncpyz (cl.levelname, s, sizeof(cl.levelname));\n\t}\n\telse if (i == Q2EXCS_SKY)\n\t\tR_SetSky(s);\n\telse if (i == Q2EXCS_SKYAXIS || i == Q2EXCS_SKYROTATE)\n\t{\n\t\tif (i == Q2EXCS_SKYROTATE)\n\t\t{\n\t\t\ts = COM_Parse(s);\n\t\t\tcl.skyrotate = atof(com_token);\n\t\t\ts = COM_Parse(s);\n\t\t\tif (*com_token)\n\t\t\t\tcl.skyautorotate = atoi(com_token);\n\t\t}\n\t\telse\n\t\t{\n\t\t\ts = COM_Parse(s);\n\t\t\tif (s)\n\t\t\t{\n\t\t\t\tcl.skyaxis[0] = atof(com_token);\n\t\t\t\ts = COM_Parse(s);\n\t\t\t\tif (s)\n\t\t\t\t{\n\t\t\t\t\tcl.skyaxis[1] = atof(com_token);\n\t\t\t\t\ts = COM_Parse(s);\n\t\t\t\t\tif (s)\n\t\t\t\t\t\tcl.skyaxis[2] = atof(com_token);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (cl.skyrotate)\n\t\t{\n\t\t\tif (cl.skyaxis[0]||cl.skyaxis[1]||cl.skyaxis[2])\n\t\t\t\tCvar_LockFromServer(&r_skybox_orientation, va(\"%g %g %g %g\", cl.skyaxis[0], cl.skyaxis[1], cl.skyaxis[2], cl.skyrotate));\n\t\t\telse\n\t\t\t\tCvar_LockFromServer(&r_skybox_orientation, va(\"0 0 1 %g\", cl.skyrotate));\n\t\t}\n\t\telse\n\t\t\tCvar_LockFromServer(&r_skybox_orientation, \"\");\n\t\tCvar_LockFromServer(&r_skybox_autorotate, va(\"%i\", cl.skyautorotate));\n\t}\n\telse if (i == Q2EXCS_STATUSBAR)\n\t{\n\t\tQ_strncpyz(cl.q2statusbar, s, sizeof(cl.q2statusbar));\n\t}\n\telse if (i > Q2EXCS_STATUSBAR && i < Q2EXCS_AIRACCEL)\n\t\t; //trailing statusbar\n\telse if (i == Q2EXCS_MAXCLIENTS)\n\t{\n\t\ti = atoi(s);\n\t\tif (i > 1)\n\t\t\tcl.allocated_client_slots = i;\n\t}\n\telse if (i >= Q2EXCS_LIGHTS && i < Q2EXCS_LIGHTS+Q2EXMAX_LIGHTSTYLES)\n\t{\n\t\tR_UpdateLightStyle(i-Q2EXCS_LIGHTS, s, 1, 1, 1);\n\t}\n\telse if (i >= Q2EXCS_RTLIGHTS && i < Q2EXCS_RTLIGHTS+Q2EXMAX_RTLIGHTS)\n\t{\n\t\ti -= Q2EXCS_RTLIGHTS;\n\t\tCLQ2EX_ParseLightConfigString(i, s);\n\t}\n\telse if (i == Q2EXCS_CDTRACK)\n\t{\n\t\tMedia_NamedTrack (s, NULL);\n\t}\n\telse if (i == Q2EXCS_AIRACCEL)\n\t\tQ_strncpyz(cl.q2airaccel, s, sizeof(cl.q2airaccel));\n\telse if (i >= Q2EXCS_MODELS && i < Q2EXCS_MODELS+Q2EXMAX_MODELS)\n\t{\n\t\ti-= Q2EXCS_MODELS;\nparsemodelindex:\n\t\tif ((unsigned int)i >= countof(cl.model_name))\n\t\t\treturn;\n\t\tif (*s == '/')\n\t\t\ts++;\t//*sigh*\n\t\tif (i == 255)\n\t\t\ts = \"*playermodel\";\t//something special.\n\t\tZ_StrDupPtr(&cl.model_name[i], s);\n\t\tif (cl.model_name[i][0] == '#')\n\t\t{\n\t\t\tif (cl.numq2visibleweapons < Q2MAX_VISIBLE_WEAPONS)\n\t\t\t{\n\t\t\t\tcl.q2visibleweapons[cl.numq2visibleweapons] = cl.model_name[i]+1;\n\t\t\t\tcl.numq2visibleweapons++;\n\t\t\t}\n\t\t\tcl.model_precache[i] = NULL;\n\t\t}\n\t\telse if (cl.contentstage)\n\t\t\tcl.model_precache[i] = Mod_ForName (cl.model_name[i], MLV_WARN);\n\t}\n\telse if (i >= Q2EXCS_SOUNDS && i < Q2EXCS_SOUNDS+Q2MAX_SOUNDS)\n\t{\n\t\ti-= Q2EXCS_SOUNDS;\nparsesoundindex:\n\t\tif ((unsigned int)i >= countof(cl.sound_name))\n\t\t\treturn;\n\t\tif (*s == '/')\n\t\t\ts++;\t//*sigh*\n\t\tZ_StrDupPtr(&cl.sound_name[i], s);\n\t\tif (cl.contentstage)\n\t\t\tcl.sound_precache[i] = S_PrecacheSound (s);\n\t}\n\telse if (i >= Q2EXCS_IMAGES && i < Q2EXCS_IMAGES+Q2MAX_IMAGES)\n\t{\n\t\ti -= Q2EXCS_IMAGES;\n\t\tif ((unsigned int)i >= countof(cl.image_name))\n\t\t\treturn;\n\t\tZ_StrDupPtr(&cl.image_name[i], s);\n\t}\n\telse if (i >= Q2EXCS_ITEMS && i < Q2EXCS_ITEMS+Q2MAX_ITEMS)\n\t{\n\t\ti -= Q2EXCS_ITEMS;\n\t\tif ((unsigned int)i >= countof(cl.item_name))\n\t\t\treturn;\n\t\tZ_StrDupPtr(&cl.item_name[i], s);\n\t}\n\telse if (i >= Q2EXCS_GENERAL && i < Q2EXCS_GENERAL+Q2EXMAX_GENERAL)\n\t{\n\t\ti -= Q2EXCS_GENERAL;\n\t\tif ((unsigned int)i >= Q2MAX_CLIENTS)//countof(cl.configstring_general))\n\t\t\treturn;\n\t\tZ_StrDupPtr(&cl.configstring_general[i], s);\n\t}\n\telse if (i >= Q2EXCS_PLAYERSKINS && i < Q2EXCS_PLAYERSKINS+Q2EXMAX_CLIENTS)\n\t{\n\t\ti -= Q2EXCS_GENERAL;\n\t\ti += Q2EXMAX_CLIENTS;\n\t\tif ((unsigned int)i >= countof(cl.configstring_general))\n\t\t\treturn;\n\t\tZ_StrDupPtr(&cl.configstring_general[i], s);\n\n\t\tCLQ2_ParseClientinfo (i, s);\n\t}\n\telse if (i == Q2EXCS_MAPCHECKSUM)\n\t{\n\t\tint serverchecksum = (int)strtol(s, NULL, 10);\n\t\tif (cl.worldmodel)\n\t\t{\n\t\t\tif (cl.worldmodel->loadstate == MLS_LOADING)\n\t\t\t\tCOM_WorkerPartialSync(cl.worldmodel, &cl.worldmodel->loadstate, MLS_LOADING);\n\n\t\t\t// the Q2 client normally exits here, however for our purposes we might as well ignore it\n\t\t\tif (cl.worldmodel->checksum != serverchecksum &&\n\t\t\t\tcl.worldmodel->checksum2!= serverchecksum)\n\t\t\t\tCon_Printf(CON_WARNING \"WARNING: Client checksum does not match server checksum (%i != %i)\", cl.worldmodel->checksum2, serverchecksum);\n\t\t}\n\n\t\tcl.q2mapchecksum = serverchecksum;\n\t}\n\telse if (i >= Q2ECS_WHEEL_WEAPONS && i < Q2ECS_WHEEL_WEAPONS+Q2EXMAX_WWHEEL)\n\t\t;\n\telse if (i >= Q2ECS_WHEEL_AMMO && i < Q2ECS_WHEEL_AMMO+Q2EXMAX_WWHEEL)\n\t\t;\n\telse if (i >= Q2ECS_WHEEL_POWERUPS && i < Q2ECS_WHEEL_POWERUPS+Q2EXMAX_WWHEEL)\n\t\t;\n\telse if (i == Q2ECS_CD_LOOP_COUNT)\n\t\t;\n\telse if (i == Q2ECS_GAME_STYLE)\n\t\t;\n\n#define\tQ2EXCS_SOUNDS\t\t\t(Q2EXCS_MODELS\t\t\t+Q2EXMAX_MODELS)\n#define\tQ2EXCS_IMAGES\t\t\t(Q2EXCS_SOUNDS\t\t\t+Q2EXMAX_SOUNDS)\n#define\tQ2EXCS_LIGHTS\t\t\t(Q2EXCS_IMAGES\t\t\t+Q2EXMAX_IMAGES)\n#define\tQ2EXCS_RTLIGHTS\t\t\t(Q2EXCS_LIGHTS\t\t\t+Q2EXMAX_LIGHTSTYLES)\n\telse\n\t\tCon_Printf(CON_WARNING\"Config string %i unsupported\\n\", i);\n}\nstatic void CLQ2_ParseConfigString (void)\n{\n\tunsigned int\ti = MSG_ReadUInt16();\n\tchar\t\t\t*s = MSG_ReadString();\n\tCLQ2_UpdateConfigString(i, s);\n}\n#endif\n\n\nqboolean CL_CheckBaselines (int size)\n{\n\tint i;\n\n\tif (size < 0)\n\t\treturn false;\n\tif (size > MAX_EDICTS)\n\t\treturn false;\n\n\tsize = (size + 64) & ~63; // round up to next 64\n\tif (size <= cl_baselines_count)\n\t\treturn true;\n\n\tcl_baselines = BZ_Realloc(cl_baselines, sizeof(*cl_baselines)*size);\n\tfor (i = cl_baselines_count; i < size; i++)\n\t{\n\t\tmemcpy(cl_baselines + i, &nullentitystate, sizeof(*cl_baselines));\n\t\tif (cls.protocol == CP_NETQUAKE && cls.protocol_nq == CPNQ_H2MP)\n\t\t\tcl_baselines[i].hexen2flags = 0;\n\t}\n\n\tcl_baselines_count = size;\n\n\treturn true;\n}\n\n/*\n==================\nCL_ParseBaseline\n==================\n*/\nstatic void CL_ParseBaseline (entity_state_t *es, int baselinetype2)\n{\n\tint\t\t\ti;\n\tunsigned int bits;\n\n\tmemcpy(es, &nullentitystate, sizeof(entity_state_t));\n\n\tif (baselinetype2 == CPNQ_FITZ666)\n\t\tbits = MSG_ReadByte();\t//fitzquake has actual flags. yay extensibility. just a shame they're not the same as other entity updates.\n\telse if (baselinetype2 >= CPNQ_DP5 && baselinetype2 <= CPNQ_DP7)\n\t\tbits = FITZ_B_LARGEMODEL|FITZ_B_LARGEFRAME;\t//dp's baseline2 always has these (regular baseline is unmodified)\n\telse if (cls.protocol == CP_NETQUAKE && CPNQ_IS_BJP)\n\t\tbits = FITZ_B_LARGEMODEL;\t//bjp always uses shorts for models.\n\telse if (cls.protocol == CP_NETQUAKE && cls.protocol_nq == CPNQ_H2MP)\n\t\tbits = FITZ_B_LARGEMODEL;\t//urgh\n\telse\n\t\tbits = 0;\t//vanilla nq or qw\n\n\tes->modelindex = (bits & FITZ_B_LARGEMODEL) ? (unsigned short)MSG_ReadShort() : MSG_ReadByte();\n\tes->frame = (bits & FITZ_B_LARGEFRAME) ? (unsigned short)MSG_ReadShort() : MSG_ReadByte();\n\tes->colormap = MSG_ReadByte();\n\tes->skinnum = MSG_ReadByte();\n\n\tif (cls.protocol == CP_NETQUAKE && cls.protocol_nq == CPNQ_H2MP)\n\t{\n\t\tes->scale = (MSG_ReadByte()/100.0)*16;\n\t\tes->hexen2flags = MSG_ReadByte();\n\t\tes->abslight = MSG_ReadByte();\n\t}\n\n\tfor (i=0 ; i<3 ; i++)\n\t{\n\t\tes->origin[i] = MSG_ReadCoord ();\n\t\tes->angles[i] = MSG_ReadAngle ();\n\t}\n\n\tes->trans = (bits & FITZ_B_ALPHA) ? MSG_ReadByte() : 255;\n#ifdef NQPROT\n\tif (cls.qex)\n\t{\n\t\tif (bits & QEX_B_SOLID)\n\t\t\t/*es->solidtype =*/ MSG_ReadByte();\n\t\tif (bits & QEX_B_UNKNOWN4)\n\t\t\tCon_Printf(CON_WARNING\"QEX_B_UNKNOWN4: %x\\n\", MSG_ReadByte());\n\t\tif (bits & QEX_B_UNKNOWN5)\n\t\t\tCon_Printf(CON_WARNING\"QEX_B_UNKNOWN5: %x\\n\", MSG_ReadByte());\n\t\tif (bits & QEX_B_UNKNOWN6)\n\t\t\tCon_DPrintf(CON_WARNING\"QEX_B_UNKNOWN6: %x\\n\", MSG_ReadByte());\n\t\tif (bits & QEX_B_UNKNOWN7)\n\t\t\tCon_Printf(CON_WARNING\"QEX_B_UNKNOWN7: %x\\n\", MSG_ReadByte());\n\t}\n\telse\n#endif\n\t\tes->scale = (bits & RMQFITZ_B_SCALE) ? MSG_ReadByte() : 16;\n}\nstatic void CL_ParseBaselineDelta (void)\n{\n\tentity_state_t es;\n\n\tif (cls.fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS)\n\t\tCLFTE_ParseBaseline(&es, true);\n\telse\n\t\tCLQW_ParseDelta(&nullentitystate, &es, (unsigned short)MSG_ReadShort());\n\tif (!CL_CheckBaselines(es.number))\n\t\tHost_EndGame(\"CL_ParseBaselineDelta: check baselines failed with size %i\", es.number);\n\tmemcpy(cl_baselines + es.number, &es, sizeof(es));\n}\n\n#ifdef Q2CLIENT\nstatic void CLQ2_Precache_f (void)\n{\n\tModel_CheckDownloads();\n\tSound_CheckDownloads();\n\n\tcl.contentstage = 0;\n\tcl.sendprespawn = true;\n\tSCR_SetLoadingFile(\"loading data\");\n}\n#endif\n\n\n\n/*\n=====================\nCL_ParseStatic\n\nStatic entities are non-interactive world objects\nlike torches\n=====================\n*/\nvoid R_StaticEntityToRTLight(int i);\nstatic void CL_ParseStaticProt (int baselinetype)\n{\n\tentity_t *ent;\n\tint\t\ti;\n\tentity_state_t\tes;\n\tvec3_t mins,maxs;\n\n\tif (baselinetype >= 0)\n\t{\n\t\tCL_ParseBaseline(&es, baselinetype);\n\t\ti = cl.num_statics;\n\t\tcl.num_statics++;\n\t}\n\telse\n\t{\n\t\t//new deltaed style ('full' extension support)\n\t\tif (cls.fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS)\n\t\t\tCLFTE_ParseBaseline(&es, false);\n\t\telse\n\t\t\tCLQW_ParseDelta(&nullentitystate, &es, (unsigned short)MSG_ReadShort());\n\n\t\tif (!es.number)\n\t\t\ti = cl.num_statics++;\n\t\telse\n\t\t{\n\t\t\tes.number+=MAX_EDICTS;\n\n\t\t\tfor (i = 0; i < cl.num_statics; i++)\n\t\t\t\tif (cl_static_entities[i].ent.keynum == es.number)\n\t\t\t\t{\n\t\t\t\t\tpe->DelinkTrailstate (&cl_static_entities[i].emit);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\tif (i == cl.num_statics)\n\t\t\t\tcl.num_statics++;\n\t\t}\n\t}\n\n\tif (i == cl_max_static_entities)\n\t{\n\t\tcl_max_static_entities += 16;\n\t\tcl_static_entities = BZ_Realloc(cl_static_entities, sizeof(*cl_static_entities)*cl_max_static_entities);\n\t}\n\n\tcl_static_entities[i].mdlidx = es.modelindex;\n\tcl_static_entities[i].emit = trailkey_null;\n\n\tcl_static_entities[i].state = es;\n\tent = &cl_static_entities[i].ent;\n\tV_ClearEntity(ent);\n\tmemset(&cl_static_entities[i].ent.pvscache, 0, sizeof(cl_static_entities[i].ent.pvscache));\n\n\tent->keynum = es.number;\n\n// copy it to the current state\n\tent->model = cl.model_precache[es.modelindex];\n\tmemset(&ent->framestate, 0, sizeof(ent->framestate));\n\tent->framestate.g[FS_REG].frame[0] = ent->framestate.g[FS_REG].frame[1] = es.frame;\n\tent->framestate.g[FS_REG].lerpweight[0] = 1;\n\tent->skinnum = es.skinnum;\n#ifdef HEXEN2\n\tent->drawflags = es.hexen2flags;\n\tent->abslight = es.abslight;\n#endif\n\n#ifdef PEXT_SCALE\n\tent->scale = es.scale/16.0;\n#endif\n\tent->glowmod[0] = (8.0f/256.0f)*es.glowmod[0];\n\tent->glowmod[1] = (8.0f/256.0f)*es.glowmod[1];\n\tent->glowmod[2] = (8.0f/256.0f)*es.glowmod[2];\n\tent->shaderRGBAf[0] = (8.0f/256.0f)*es.colormod[0];\n\tent->shaderRGBAf[1] = (8.0f/256.0f)*es.colormod[1];\n\tent->shaderRGBAf[2] = (8.0f/256.0f)*es.colormod[2];\n\n\tent->fatness = es.fatness/16.0;\n\n\tent->flags = 0;\n\tif (es.dpflags & RENDER_VIEWMODEL)\n\t\tent->flags |= RF_WEAPONMODEL|Q2RF_MINLIGHT|RF_DEPTHHACK;\n\tif (es.dpflags & RENDER_EXTERIORMODEL)\n\t\tent->flags |= RF_EXTERNALMODEL;\n\tif (es.effects & NQEF_ADDITIVE)\n\t\tent->flags |= RF_ADDITIVE;\n\tif (es.effects & EF_NODEPTHTEST)\n\t\tent->flags |= RF_NODEPTHTEST;\n\tif (es.effects & EF_NOSHADOW)\n\t\tent->flags |= RF_NOSHADOW;\n\tif (es.trans < 0xfe)\n\t{\n\t\tent->shaderRGBAf[3] = es.trans/(float)0xfe;\n\t\tent->flags |= RF_TRANSLUCENT;\n\t}\n\telse\n\t\tent->shaderRGBAf[3] = 1.0;\n\n\tVectorCopy (es.origin, ent->origin);\n\tVectorCopy (es.angles, ent->angles);\n\tif (ent->model && ent->model->type == mod_alias)\n\t\tAngleVectorsMesh(es.angles, ent->axis[0], ent->axis[1], ent->axis[2]);\n\telse\n\t\tAngleVectors(es.angles, ent->axis[0], ent->axis[1], ent->axis[2]);\n\tVectorInverse(ent->axis[1]);\n\n\tif (!cl.worldmodel || cl.worldmodel->loadstate != MLS_LOADED)\n\t\treturn;\n\tif (ent->model)\n\t{\n\t\t//FIXME: wait for model to load so we know the correct size?\n\t\t/*FIXME: compensate for angle*/\n\t\tVectorAdd(es.origin, ent->model->mins, mins);\n\t\tVectorAdd(es.origin, ent->model->maxs, maxs);\n\t}\n\telse\n\t{\n\t\tVectorCopy(es.origin, mins);\n\t\tVectorCopy(es.origin, maxs);\n\t}\n\tcl.worldmodel->funcs.FindTouchedLeafs(cl.worldmodel, &cl_static_entities[i].ent.pvscache, mins, maxs);\n\n#ifdef RTLIGHTS\n\t//and now handle any rtlight fields on it\n\tR_StaticEntityToRTLight(i);\n#endif\n}\n\n/*\n===================\nCL_ParseStaticSound\n===================\n*/\nstatic void CL_ParseStaticSound (unsigned int flags)\n{\n\textern cvar_t cl_staticsounds;\n\tvec3_t\t\torg;\n\tsize_t\t\tsound_num;\n\tfloat\t\tvol, atten;\n\tint\t\t\ti;\n\n\tif (flags & ~(1))\n\t\tHost_EndGame(\"CL_ParseStaticSound: unsupported flags & %x\\n\", flags&~(1));\n\n\tfor (i=0 ; i<3 ; i++)\n\t\torg[i] = MSG_ReadCoord ();\n\tif (flags || (cls.protocol == CP_NETQUAKE && (cls.protocol_nq == CPNQ_BJP2 || cls.protocol_nq == CPNQ_H2MP)))\n\t{\n\t\tif (cls.fteprotocolextensions2&PEXT2_LERPTIME)\n\t\t\tsound_num = (unsigned short)MSG_ReadULEB128();\n\t\telse\n\t\t\tsound_num = (unsigned short)MSG_ReadShort();\n\t}\n\telse\n\t\tsound_num = MSG_ReadByte ();\n\tvol = MSG_ReadByte ()/255.0;\n\tatten = MSG_ReadByte ()/64.0;\n\n\tif (sound_num >= countof(cl.sound_precache))\n\t\treturn;\t//no crashing, please.\n\n\tvol *= cl_staticsounds.value;\n\tif (vol < 0)\n\t\treturn;\n\n\tS_StaticSound (cl.sound_precache[sound_num], org, vol, atten);\n}\n\n\n\n/*\n=====================================================================\n\nACTION MESSAGES\n\n=====================================================================\n*/\n\n/*\n==================\nCL_ParseStartSoundPacket\n==================\n*/\nstatic void CLQW_ParseStartSoundPacket(void)\n{\n\tvec3_t  pos;\n\tint \tchannel, ent;\n\tint \tsound_num;\n\tint \tvolume;\n\tfloat \tattenuation;\n\tint\t\ti;\n\n\tchannel = MSG_ReadShort();\n\n\tif (channel & QWSND_VOLUME)\n\t\tvolume = MSG_ReadByte ();\n\telse\n\t\tvolume = DEFAULT_SOUND_PACKET_VOLUME;\n\n\tif (channel & QWSND_ATTENUATION)\n\t\tattenuation = MSG_ReadByte () / 64.0;\n\telse\n\t\tattenuation = DEFAULT_SOUND_PACKET_ATTENUATION;\n\n\tsound_num = MSG_ReadByte ();\n\n\tfor (i=0 ; i<3 ; i++)\n\t\tpos[i] = MSG_ReadCoord ();\n\n\tent = (channel>>3)&1023;\n\tchannel &= 7;\n\n\tif (ent > MAX_EDICTS)\n\t\tHost_EndGame (\"CL_ParseStartSoundPacket: ent = %i\", ent);\n\n#ifdef CSQC_DAT\n\tif (!CSQC_StartSound(ent, channel, cl.sound_name[sound_num], pos, volume/255.0, attenuation, 1, 0, 0))\n#endif\n\t{\n\t\tif (!sound_num)\n\t\t\tS_StopSound(ent, channel);\n\t\telse\n\t\t\tS_StartSound (ent, channel, cl.sound_precache[sound_num], pos, NULL, volume/255.0, attenuation, 0, 0, 0);\n\t}\n\n#ifdef QUAKESTATS\n\tfor (i = 0; i < cl.splitclients; i++)\n\t{\n\t\tif (ent == cl.playerview[i].playernum+1)\n\t\t{\n\t\t\tTP_CheckPickupSound(cl.sound_name[sound_num], pos, i);\n\t\t\treturn;\n\t\t}\n\t}\n\tTP_CheckPickupSound(cl.sound_name[sound_num], pos, -1);\n#endif\n}\n\n#ifdef Q2CLIENT\nstatic void CLQ2_ParseStartSoundPacket(void)\n{\n\tvec3_t  pos;\n\tint \tchannel, ent;\n\tint \tsound_num;\n\tfloat \tvolume;\n\tfloat \tattenuation;\n\tint\t\tflags;\n\tfloat\tofs;\n\tsfx_t\t*sfx;\n\n\tflags = MSG_ReadByte ();\n\tif (flags & Q2SND_EXTRABITS)\n\t\tflags |= MSG_ReadByte ()<<8;\n\n\tif (cls.protocol_q2 == PROTOCOL_VERSION_Q2EX || ((flags & Q2SNDFTE_LARGEIDX) && (cls.fteprotocolextensions & PEXT_SOUNDDBL)))\n\t\tsound_num = MSG_ReadUInt16();\n\telse\n\t\tsound_num = MSG_ReadByte ();\n\n\tif (flags & Q2SND_VOLUME)\n\t\tvolume = MSG_ReadByte () / 255.0;\n\telse\n\t\tvolume = Q2DEFAULT_SOUND_PACKET_VOLUME;\n\n\tif (flags & Q2SND_ATTENUATION)\n\t\tattenuation = MSG_ReadByte () / 64.0;\n\telse\n\t\tattenuation = Q2DEFAULT_SOUND_PACKET_ATTENUATION;\n\n\tif (flags & Q2SND_OFFSET)\n\t\tofs = MSG_ReadByte () / 1000.0;\n\telse\n\t\tofs = 0;\n\n\tif (flags & Q2SND_ENT)\n\t{\t// entity reletive\n\t\tif (cls.protocol_q2 == PROTOCOL_VERSION_Q2EX && (flags & Q2SNDEX_LARGEENT))\n\t\t\tchannel = MSG_ReadLong();\n\t\telse\n\t\t\tchannel = MSG_ReadShort();\n\t\tent = channel>>3;\n\t\tif (ent > MAX_EDICTS)\n\t\t\tHost_EndGame (\"CL_ParseStartSoundPacket: ent = %i\", ent);\n\n\t\tchannel &= 7;\n\t}\n\telse\n\t{\n\t\tent = 0;\n\t\tchannel = 0;\n\t}\n\n\tif (flags & Q2SND_POS)\n\t{\t// positioned in space\n\t\tMSG_ReadPos (pos);\n\n//FIXME\n//\t\tif (!(flags & Q2SNDEX_EXPLICITPOS))\n//\t\t\tCL_GetNumberedEntityInfo(ent, pos, NULL);\n\t}\n\telse\t// use entity number\n\t{\n\t\tCL_GetNumberedEntityInfo(ent, pos, NULL);\n\t}\n\n\tif (!cl.sound_precache[sound_num])\n\t\treturn;\n\n\tsfx = cl.sound_precache[sound_num];\n\tif (sfx->name[0] == '*')\n\t{\t//a 'sexed' sound\n\t\tif (ent > 0 && ent <= MAX_CLIENTS)\n\t\t{\n\t\t\tchar *model = InfoBuf_ValueForKey(&cl.players[ent-1].userinfo, \"skin\");\n\t\t\tchar *skin;\n\t\t\tskin = strchr(model, '/');\n\t\t\tif (skin)\n\t\t\t\t*skin = '\\0';\n\t\t\tif (*model)\n\t\t\t\tsfx = S_PrecacheSound(va(\"players/%s/%s\", model, cl.sound_precache[sound_num]->name+1));\n\t\t}\n\t\t//fall back to male if it failed to load.\n\t\t//note: threaded loading can still make it silent the first time we hear it.\n\t\tif (sfx->loadstate == SLS_FAILED)\n\t\t\tsfx = S_PrecacheSound(va(\"players/male/%s\", cl.sound_precache[sound_num]->name+1));\n\t}\n\tS_StartSound (ent, channel, sfx, pos, NULL, volume, attenuation, ofs, 0, 0);\n}\n#endif\n\n//returns the player if they're not spectating. \nstatic int CL_TryTrackNum(playerview_t *pv)\n{\n\tif (pv->spectator && pv->cam_state != CAM_FREECAM && pv->cam_spec_track >= 0)\n\t\treturn pv->cam_spec_track;\n\treturn pv->playernum;\n}\n\n#if defined(NQPROT) || defined(PEXT_SOUNDDBL)\nstatic void CLNQ_ParseStartSoundPacket(void)\n{\n\tvec3_t  pos, vel;\n\tint \tchannel, ent;\n\tunsigned int \tsound_num;\n\tint \tvolume;\n\tint \tfield_mask;\n\tfloat \tattenuation;\n \tint\t\ti;\n\tfloat\tpitchadj;\n\tfloat\ttimeofs;\n\tunsigned int flags;\n\n\tfield_mask = MSG_ReadByte();\n\n\tif (field_mask & FTESND_MOREFLAGS)\n\t\tfield_mask |= MSG_ReadUInt64()<<8;\n\n\tif (field_mask & NQSND_VOLUME)\n\t\tvolume = MSG_ReadByte ();\n\telse\n\t\tvolume = DEFAULT_SOUND_PACKET_VOLUME;\n\n\tif (field_mask & NQSND_ATTENUATION)\n\t\tattenuation = MSG_ReadByte () / 64.0;\n\telse\n\t\tattenuation = DEFAULT_SOUND_PACKET_ATTENUATION;\n\n\tif (field_mask & FTESND_PITCHADJ)\n\t\tpitchadj = MSG_ReadByte()/100.0;\n\telse\n\t\tpitchadj = 1;\n\n\tif (field_mask & FTESND_TIMEOFS)\n\t\ttimeofs = MSG_ReadShort() / 1000.0;\n\telse\n\t\ttimeofs = 0;\n\n\tif (field_mask & FTESND_VELOCITY)\n\t{\n\t\tvel[0] = MSG_ReadShort()/8.0;\n\t\tvel[1] = MSG_ReadShort()/8.0;\n\t\tvel[2] = MSG_ReadShort()/8.0;\n\t}\n\telse\n\t\tVectorClear(vel);\n\n\tif (field_mask & DPSND_SPEEDUSHORT4000)\n\t\tpitchadj = (unsigned short)MSG_ReadShort() / 4000.0;\n\n\tflags = field_mask>>8;\n\tflags &= CF_NETWORKED;\n\n\tif (field_mask & NQSND_LARGEENTITY)\n\t{\n\t\tent = MSGCL_ReadEntity();\n\t\tchannel = MSG_ReadByte();\n\t}\n\telse\n\t{\t//regular\n\t\tchannel = MSG_ReadShort ();\n\t\tent = channel >> 3;\n\t\tchannel &= 7;\n\t}\n\n\t//channel = (channel & 7) | ((channel & 0x0f1) << 1); //this line undoes the reliable=(channel&8) gap from qwssqc... but frankly just pass the flags arg properly. csqc's builtin doesn't use it, so don't give an inconsistent gap at all here.\n\n\tif ((field_mask & NQSND_LARGESOUND) || (cls.protocol == CP_NETQUAKE && (cls.protocol_nq == CPNQ_BJP2 || cls.protocol_nq == CPNQ_BJP3))) //bjp kinda sucks\n\t\tsound_num = (unsigned short)MSG_ReadShort();\n\telse\n\t\tsound_num = (unsigned char)MSG_ReadByte ();\n\n\tfor (i=0 ; i<3 ; i++)\n\t\tpos[i] = MSG_ReadCoord ();\n\n\tif (ent > MAX_EDICTS)\n\t\tHost_EndGame (\"CL_ParseStartSoundPacket: ent = %i\", ent);\n\tif (sound_num >= MAX_PRECACHE_SOUNDS)\n\t\tHost_EndGame (\"CL_ParseStartSoundPacket: sndidx = %i\", sound_num);\n\n\tif (!cl.sound_name[sound_num])\n\t\treturn;\t//nope, not precached yet... silly raqces.\n\n#ifdef CSQC_DAT\n\tif (!CSQC_StartSound(ent, channel, cl.sound_name[sound_num], pos, volume/255.0, attenuation, pitchadj, timeofs, flags))\n#endif\n\t{\n\t\tif (!sound_num)\n\t\t\tS_StopSound(ent, channel);\n\t\telse\n\t\t\tS_StartSound (ent, channel, cl.sound_precache[sound_num], pos, vel, volume/255.0, attenuation, timeofs, pitchadj, flags);\n\t}\n\n#ifdef QUAKESTATS\n\tfor (i = 0; i < cl.splitclients; i++)\n\t{\n\t\tif (ent == cl.playerview[i].playernum+1)\n\t\t{\n\t\t\tTP_CheckPickupSound(cl.sound_name[sound_num], pos, i);\n\t\t\treturn;\n\t\t}\n\t}\n\tTP_CheckPickupSound(cl.sound_name[sound_num], pos, -1);\n#endif\n}\n#endif\n\n\n/*\n==================\nCL_ParseClientdata\n\nServer information pertaining to this client only, sent every frame\n==================\n*/\nvoid CL_ParseClientdata (void)\n{\n\tint\t\t\t\ti;\n\n// calculate simulated time of message\n\toldparsecountmod = parsecountmod;\n\n\ti = cls.netchan.incoming_acknowledged;\n#ifdef NQPROT\n\tif (cls.demoplayback == DPB_NETQUAKE)\n\t{\n\t\ti = cls.netchan.incoming_sequence-1;\n\t\tcl.oldparsecount = i - 1;\n\t\toldparsecountmod = cl.oldparsecount & UPDATE_MASK;\n\t}\n\telse\n#endif\n\tif (cls.demoplayback == DPB_MVD)\n\t{\n\t\tcl.oldparsecount = i - 1;\n\t\toldparsecountmod = cl.oldparsecount & UPDATE_MASK;\n\t}\n\tcl.parsecount = i;\n\tparsecountmod = i&UPDATE_MASK;\n\tparsecounttime = realtime;//cl.outframes[i].senttime;\n\n\tif (cls.protocol == CP_QUAKEWORLD)\n\t\tCL_AckedInputFrame(cls.netchan.incoming_sequence, cl.parsecount, false);\n}\n\n#ifdef QWSKINS\nstatic qboolean CLQ2_PlayerSkinIsOkay(skinid_t id)\n{\n\tskinfile_t *sk = Mod_LookupSkin(id);\n\tif (!sk)\t//err...\n\t\treturn false;\n\tif (sk->nummappings != 1 || *sk->mappings[0].surface)\n\t\treturn true;\t//looks like its a custom skin, ignore it.\n\treturn R_GetShaderSizes(sk->mappings[0].shader, NULL, NULL, true) > 0;\n}\nstatic int QDECL CLQ2_EnumeratedSkin(const char *name, qofs_t size, time_t mtime, void *ptr, searchpathfuncs_t *spath)\n{\n\t//follows the form of players/$MODELNAME/$SKINNAME_i.$EXT\n\tplayer_info_t\t*player = ptr;\n\tif (!player->skinid)\n\t{\n\t\tchar *e;\n\t\te = strstr(name, \"_i.\");\n\t\tif (e)\n\t\t{\n\t\t\t*e = 0;\n\t\t\tplayer->skinid = Mod_ReadSkinFile(va(\"%s.skin\", name), va(\"replace \\\"\\\" \\\"%s.pcx\\\"\", name));\n\t\t}\n\t}\n\treturn true;\n}\n\n/*\n=====================\nCL_NewTranslation\n=====================\n*/\nvoid CL_NewTranslation (int slot)\n{\n\tint\t\ttop, bottom;\n\tint local;\n\tqboolean mayforce;\n\n\tchar *s;\n\tplayer_info_t\t*player;\n\n\tif (slot >= MAX_CLIENTS)\n\t\tHost_Error (\"CL_NewTranslation: slot > MAX_CLIENTS\");\n\n\tplayer = &cl.players[slot];\n\n\tif (cls.protocol == CP_QUAKE2)\n\t{\n\t\tchar *mod, *skin, *dogtag;\n\t\tplayer->qwskin = NULL;\n\t\tplayer->skinid = 0;\n\t\tplayer->model = NULL;\n\t\tplayer->ttopcolor = TOP_DEFAULT;\n\t\tplayer->tbottomcolor = BOTTOM_DEFAULT;\n\n\t\tmod = InfoBuf_ValueForKey(&player->userinfo, \"skin\");\n\t\tskin = strchr(mod, '/');\n\t\tif (skin)\n\t\t{\n\t\t\t*skin++ = 0;\n\t\t\tdogtag = strchr(skin, '\\\\');\n\t\t\tif (dogtag)\n\t\t\t\t*dogtag++ = 0;\n\t\t}\n\t\tif (!mod || !*mod)\n\t\t\tmod = \"male\";\n\t\tif (!skin || !*skin || !COM_FCheckExists(va(\"players/%s/%s.pcx\", mod, skin)))\n\t\t\tskin = \"grunt\";\n\n\t\tplayer->model = Mod_ForName(va(\"players/%s/tris.md2\", mod), MLV_WARNSYNC);\n\t\tif (player->model->loadstate == MLS_FAILED && strcmp(mod, \"male\"))\n\t\t{\t//fall back on male if the model doesn't exist. yes, sexist, but also statistically most likely to represent the actual player.\n\t\t\tmod = \"male\";\n\t\t\tplayer->model = Mod_ForName(va(\"players/%s/tris.md2\", mod), 0);\n\t\t}\n\t\tplayer->skinid = Mod_RegisterSkinFile(va(\"players/%s/%s.skin\", mod,skin));\n\t\tif (!player->skinid)\n\t\t\tplayer->skinid = Mod_ReadSkinFile(va(\"players/%s/%s.skin\", mod,skin), va(\"replace \\\"\\\" \\\"players/%s/%s.pcx\\\"\", mod,skin));\n\t\tif (!CLQ2_PlayerSkinIsOkay(player->skinid))\n\t\t{\n\t\t\tplayer->skinid = 0;\n\t\t\tCOM_EnumerateFiles(va(\"players/%s/*_i.*\", mod), CLQ2_EnumeratedSkin, player);\n\t\t}\n\t\treturn;\n\t}\n\n\tmayforce = !(cl.fpd & FPD_NO_FORCE_COLOR);\n#if MAX_SPLITS > 1\n\tif (mayforce && cl.splitclients > 1 && cl.teamplay)\n\t{\t//if we're using splitscreen, only allow team/enemy forcing if all split clients are on the same team\n\t\tchar *needteam;\n\t\tint i;\n\t\tneedteam = cl.players[CL_TryTrackNum(&cl.playerview[0])].team;\n\t\tfor (i = 1; i < cl.splitclients; i++)\n\t\t{\n\t\t\tif (strcmp(needteam, cl.players[CL_TryTrackNum(&cl.playerview[i])].team))\n\t\t\t{\n\t\t\t\tmayforce = false;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n#endif\n\n\ts = Skin_FindName (player);\n\tCOM_StripExtension(s, s, MAX_QPATH);\n\tif (player->qwskin && stricmp(s, player->qwskin->name))\n\t\tplayer->qwskin = NULL;\n\tplayer->skinid = 0;\n\tplayer->model = NULL;\n\n\ttop = player->rtopcolor;\n\tbottom = player->rbottomcolor;\n\n\tif (mayforce)\n\t{\n\t\tlocal = CL_TryTrackNum(&cl.playerview[0]);\n\t\tif ((cl.teamplay || cls.protocol == CP_NETQUAKE) && !strcmp(player->team, cl.players[local].team))\n\t\t{\n\t\t\tif (cl_teamtopcolor != ~0)\n\t\t\t\ttop = cl_teamtopcolor;\n\t\t\tif (cl_teambottomcolor != ~0)\n\t\t\t\tbottom = cl_teambottomcolor;\n\n\t\t\tif (player->colourised)\n\t\t\t{\n\t\t\t\tif (player->colourised->topcolour != ~0)\n\t\t\t\t\ttop = player->colourised->topcolour;\n\t\t\t\tif (player->colourised->bottomcolour != ~0)\n\t\t\t\t\tbottom = player->colourised->bottomcolour;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (cl_enemytopcolor != ~0)\n\t\t\t\ttop = cl_enemytopcolor;\n\t\t\tif (cl_enemybottomcolor != ~0)\n\t\t\t\tbottom = cl_enemybottomcolor;\n\t\t}\n\t}\n/*\n\tif (top > 13 || top < 0)\n\t\ttop = 13;\n\tif (bottom > 13 || bottom < 0)\n\t\tbottom = 13;\n*/\n\t//other renderers still need the team stuff set, but that's all\n\tplayer->ttopcolor = top;\n\tplayer->tbottomcolor = bottom;\n}\n#endif\n\n/*\n==============\nCL_UpdateUserinfo\n==============\n*/\nstatic void CL_ProcessUserInfo (int slot, player_info_t *player)\n{\n\tint i;\n\tchar *col;\n\tint ospec = player->spectator;\n\n\tif (cls.protocol == CP_NETQUAKE)\n\t\tplayer->userid = slot;\n\tQ_strncpyz (player->name, InfoBuf_ValueForKey (&player->userinfo, \"name\"), sizeof(player->name));\n\tQ_strncpyz (player->team, InfoBuf_ValueForKey (&player->userinfo, \"team\"), sizeof(player->team));\n\n\tRuleset_Check(InfoBuf_ValueForKey (&player->userinfo, RULESET_USERINFO), player->ruleset, sizeof(player->ruleset));\n\n\tcol = InfoBuf_ValueForKey (&player->userinfo, \"topcolor\");\n\tif (!strncmp(col, \"0x\", 2))\n\t\tplayer->rtopcolor = 0xff000000|strtoul(col+2, NULL, 16);\n\telse\n\t\tplayer->rtopcolor = atoi(col);\n\n\tcol = InfoBuf_ValueForKey (&player->userinfo, \"bottomcolor\");\n\tif (!strncmp(col, \"0x\", 2))\n\t\tplayer->rbottomcolor = 0xff000000|strtoul(col+2, NULL, 16);\n\telse\n\t\tplayer->rbottomcolor = atoi(col);\n\n\ti = atoi(InfoBuf_ValueForKey (&player->userinfo, \"*spectator\"));\n\tif (i == 2)\n\t\tplayer->spectator = 2;\n\telse if (i)\n\t\tplayer->spectator = true;\n\telse\n\t\tplayer->spectator = false;\n/*\n\tif (player->rtopcolor > 13)\n\t\tplayer->rtopcolor = 13;\n\tif (player->rbottomcolor > 13)\n\t\tplayer->rbottomcolor = 13;\n*/\n\n\tplayer->chatstate = atoi(InfoBuf_ValueForKey (&player->userinfo, \"chat\"));\n\n#ifdef HEXEN2\n\t/*if we're running hexen2, they have to be some class...*/\n\tplayer->h2playerclass = atoi(InfoBuf_ValueForKey (&player->userinfo, \"cl_playerclass\"));\n\tif (player->h2playerclass > 5)\n\t\tplayer->h2playerclass = 5;\n\tif (player->h2playerclass < 1)\n\t\tplayer->h2playerclass = 1;\n#endif\n\n#ifdef QWSKINS\n\tplayer->model = NULL;\n\tplayer->colourised = TP_FindColours(player->name);\n#endif\n\n\t// If it's us\n\tfor (i = 0; i < cl.splitclients; i++)\n\t\tif (slot == cl.playerview[i].playernum)\n\t\t\tbreak;\n\tif (i < cl.splitclients && player->name[0])\n\t{\n\t\tif (cl.playerview[i].spectator != player->spectator)\n\t\t{\n\t\t\tcl.playerview[i].spectator = player->spectator;\n\t\t\tfor (i = 0; i < cl.splitclients; i++)\n\t\t\t{\n\t\t\t\tCam_Unlock(&cl.playerview[i]);\n\t\t\t}\n\t\t\tCL_CheckServerInfo();\n\t\t}\n\t\t// Update the rules since spectators can bypass everything but players can't\n\t\telse if (ospec != player->spectator)\n\t\t\tCL_CheckServerInfo();\n\n\t\tSkin_FlushPlayers();\n\t}\n#ifdef QWSKINS\n\telse if (cl.teamplay && cl.playerview[0].spectator && slot == Cam_TrackNum(&cl.playerview[0]))\t//skin forcing cares about the team of the guy we're tracking.\n\t\tSkin_FlushPlayers();\n\telse if (cls.state >= ca_onserver)\n\t\tSkin_Find (player);\n\n\tCL_NewTranslation (slot);\n#endif\n\tSbar_Changed ();\n\n\tCSQC_PlayerInfoChanged(slot);\n}\n\n/*\n==============\nCL_UpdateUserinfo\n==============\n*/\nstatic void CL_UpdateUserinfo (void)\n{\n\tunsigned int\t\tslot;\n\tplayer_info_t\t*player;\n\n\tslot = MSG_ReadPlayer();\n\tif (slot >= MAX_CLIENTS)\n\t\tHost_EndGame (\"CL_ParseServerMessage: svc_updateuserinfo > MAX_SCOREBOARD\");\n\n\tplayer = &cl.players[slot];\n\tplayer->userid = MSG_ReadLong ();\n\tInfoBuf_FromString(&player->userinfo, MSG_ReadString(), false);\n\tplayer->userinfovalid = true;\n\n\tCL_ProcessUserInfo (slot, player);\n\n\n\n\tif (slot == cl.playerview[0].playernum && player->name[0])\n\t{\n\t\tchar *qz;\n\t\tqz = InfoBuf_ValueForKey(&player->userinfo, \"Qizmo\");\n\t\tif (*qz)\n\t\t\tTP_ExecTrigger(\"f_qizmoconnect\", false);\n\t}\n}\n\nstatic void CL_ParseSetInfoBlob (void)\n{\n\tunsigned int slot = MSG_ReadPlayer();\n\tchar *key = MSG_ReadString();\n\tsize_t keysize;\n\tunsigned int offset = MSG_ReadLong();\n\tqboolean final = !!(offset & 0x80000000);\n\tunsigned short valsize = MSG_ReadShort();\n\tchar *val = BZ_Malloc(valsize);\n\tMSG_ReadData(val, valsize);\n\toffset &= ~0x80000000;\n\tkey = InfoBuf_DecodeString(key, key+strlen(key), &keysize);\n\n\tif (slot-- == 0)\n\t{\n\t\tInfoBuf_SyncReceive(&cl.serverinfo, key, keysize, val, valsize, offset, final);\n\t\tif (final)\n\t\t\tCL_CheckServerInfo();\n\t}\n\telse if (slot >= MAX_CLIENTS)\n\t\tCon_Printf(\"INVALID SETINFO %i: %s=%s\\n\", slot, key, val);\n\telse\n\t{\n\t\tplayer_info_t *player = &cl.players[slot];\n\t\tif (offset)\n\t\t\tCon_DLPrintf(2,\"SETINFO %s: %s+=%s\\n\", player->name, key, val);\n\t\telse\n\t\t\tCon_DLPrintf(strcmp(key, \"chat\")?1:2,\"SETINFO %s: %s=%s\\n\", player->name, key, val);\n\n\t\tInfoBuf_SyncReceive(&player->userinfo, key, keysize, val, valsize, offset, final);\n\t\tplayer->userinfovalid = true;\n\n\t\tif (final)\n\t\t\tCL_ProcessUserInfo (slot, player);\n\t}\n\n\tZ_Free(key);\n\tZ_Free(val);\n}\n/*\n==============\nCL_SetInfo\n==============\n*/\nstatic void CL_ParseSetInfo (void)\n{\n\tunsigned int\t\tslot;\n\tplayer_info_t\t*player;\n\tchar *val;\n\tchar key[512];\n\n\tslot = MSG_ReadPlayer ();\n\n\tMSG_ReadStringBuffer(key, sizeof(key));\n\tval = MSG_ReadString();\n\n\tif (slot >= MAX_CLIENTS)\n\t\tCon_Printf(\"INVALID SETINFO %i: %s=%s\\n\", slot, key, val);\n\telse\n\t{\n\t\tplayer = &cl.players[slot];\n\n\t\tif (cl_shownet.value == 3)\n\t\t\tCon_Printf(\"\\t%i(%s): %s=\\\"%s\\\"\\n\", slot, player->name, key, val);\n\t\telse\n\t\t\tCon_DLPrintf(strcmp(key, \"chat\")?1:2,\"SETINFO %s: %s=%s\\n\", player->name, key, val);\n\n\t\tInfoBuf_SetStarKey(&player->userinfo, key, val);\n\t\tplayer->userinfovalid = true;\n\n\t\tCL_ProcessUserInfo (slot, player);\n\t}\n}\n\n/*\n==============\nCL_ServerInfo\n==============\n*/\nstatic void CL_ServerInfo (void)\n{\n//\tint\t\tslot;\n//\tplayer_info_t\t*player;\n\tchar key[MAX_QWMSGLEN];\n\tchar value[MAX_QWMSGLEN];\n\n\tQ_strncpyz (key, MSG_ReadString(), sizeof(key));\n\tQ_strncpyz (value, MSG_ReadString(), sizeof(value));\n\n\tif (cl_shownet.value == 3)\n\t\tCon_Printf(\"\\t%s=%s\\n\", key, value);\n\telse\n\t\tCon_DPrintf(\"SERVERINFO: %s=%s\\n\", key, value);\n\n\tInfoBuf_SetStarKey(&cl.serverinfo, key, value);\n\n\tCL_CheckServerInfo();\n}\n\n/*\n=====================\nCL_SetStat\n=====================\n*/\nstatic void CL_SetStat_Internal (int pnum, int stat, int ivalue, float fvalue)\n{\n\tif (cl.playerview[pnum].stats[stat] != ivalue)\n\t\tSbar_Changed ();\n\n#ifdef QUAKESTATS\n\tif (stat == STAT_ITEMS)\n\t{\t// set flash times\n\t\tint\tj;\n\t\tfor (j=0 ; j<32 ; j++)\n\t\t\tif ( (ivalue & (1<<j)) && !(cl.playerview[pnum].stats[stat] & (1<<j)))\n\t\t\t\tcl.playerview[pnum].item_gettime[j] = cl.time;\n\t}\n\n\tif (stat == STAT_WEAPONMODELI)\n\t{\n\t\tif (cl.playerview[pnum].stats[stat] != ivalue)\n\t\t{\n\t\t\tif (ivalue == 0)\n\t\t\t\tTP_ExecTrigger (\"f_reloadstart\", false);\n\t\t\telse if (cl.playerview[pnum].stats[stat] == 0)\n\t\t\t\tTP_ExecTrigger (\"f_reloadend\", false);\n\t\t}\n\t}\n\n\tif (stat == STAT_VIEWHEIGHT && ((cls.z_ext & Z_EXT_VIEWHEIGHT) || cls.protocol == CP_NETQUAKE))\n\t\tcl.playerview[pnum].viewheight = fvalue;\n#endif\n\n\tcl.playerview[pnum].stats[stat] = ivalue;\n\tcl.playerview[pnum].statsf[stat] = fvalue;\n\n#ifdef QUAKESTATS\n\tif (pnum == 0)\n\t\tTP_StatChanged(stat, ivalue);\n#endif\n}\n\n#ifdef NQPROT\nstatic void CL_SetStatMovevar(int pnum, int stat, int ivalue, float value)\n{\n\tswitch(stat)\n\t{\n\tcase STAT_FRAGLIMIT:\n\t\tif (cls.protocol == CP_NETQUAKE && CPNQ_IS_DP)\n\t\t\tInfoBuf_SetKey(&cl.serverinfo, \"fraglimit\", va(\"%g\", value));\n\t\tbreak;\n\tcase STAT_TIMELIMIT:\n\t\tif (cls.protocol == CP_NETQUAKE && CPNQ_IS_DP)\n\t\t\tInfoBuf_SetKey(&cl.serverinfo, \"timelimit\", va(\"%g\", value));\n\t\tbreak;\n\tcase STAT_MOVEVARS_AIRACCEL_QW_STRETCHFACTOR:\t//0\n\tcase STAT_MOVEVARS_AIRCONTROL_PENALTY:\t\t\t//0\n\tcase STAT_MOVEVARS_AIRSPEEDLIMIT_NONQW:\t\t\t//0\n\tcase STAT_MOVEVARS_AIRSTRAFEACCEL_QW:\t\t\t//0\n\tcase STAT_MOVEVARS_AIRCONTROL_POWER:\t\t\t//2\n\tcase STAT_MOVEVARS_WARSOWBUNNY_AIRFORWARDACCEL:\t//0\n\tcase STAT_MOVEVARS_WARSOWBUNNY_ACCEL:\t\t\t//0\n\tcase STAT_MOVEVARS_WARSOWBUNNY_TOPSPEED:\t\t//0\n\tcase STAT_MOVEVARS_WARSOWBUNNY_TURNACCEL:\t\t//0\n\tcase STAT_MOVEVARS_WARSOWBUNNY_BACKTOSIDERATIO:\t//0\n\tcase STAT_MOVEVARS_AIRSTOPACCELERATE:\t\t\t//0\n\tcase STAT_MOVEVARS_AIRSTRAFEACCELERATE:\t\t\t//0\n\tcase STAT_MOVEVARS_MAXAIRSTRAFESPEED:\t\t\t//0\n\tcase STAT_MOVEVARS_AIRCONTROL:\t\t\t\t\t//0\n\tcase STAT_MOVEVARS_WALLFRICTION:\t\t\t\t//0\n\tcase STAT_MOVEVARS_TIMESCALE:\t\t\t\t\t//sv_gamespeed\n\tcase STAT_MOVEVARS_JUMPVELOCITY:\t\t\t\t//270\n\tcase STAT_MOVEVARS_EDGEFRICTION:\t\t\t\t//2\n\tcase STAT_MOVEVARS_MAXAIRSPEED:\t\t\t\t\t//30\n\tcase STAT_MOVEVARS_AIRACCEL_QW:\t\t\t\t\t//1\n\tcase STAT_MOVEVARS_AIRACCEL_SIDEWAYS_FRICTION:\t//0\n\t\tbreak;\n\n\tcase STAT_MOVEVARS_STEPHEIGHT:\t\t\t\t\t//18\n\t\tmovevars.stepheight = value;\n\t\tbreak;\n\tcase STAT_MOVEVARS_TICRATE:\t\t//cl_maxfps limiter hint\n\t\tif (cls.protocol == CP_NETQUAKE && CPNQ_IS_DP)\n\t\t{\n\t\t\tif (value <= 0)\n\t\t\t\tcls.maxfps = 1.0/value;\n\t\t\telse\n\t\t\t\tcls.maxfps = 72;\n\t\t}\n\t\tbreak;\n\tcase STAT_MOVEFLAGS:\n\t\tmovevars.flags = ivalue;\n\t\tbreak;\n\tcase STAT_MOVEVARS_GRAVITY:\n\t\tmovevars.gravity = value;\n\t\tbreak;\n\tcase STAT_MOVEVARS_STOPSPEED:\n\t\tmovevars.stopspeed = value;\n\t\tbreak;\n\tcase STAT_MOVEVARS_MAXSPEED:\n\t\tcl.playerview[pnum].maxspeed = value;\n\t\tbreak;\n\tcase STAT_MOVEVARS_SPECTATORMAXSPEED:\n\t\tmovevars.spectatormaxspeed = value;\n\t\tbreak;\n\tcase STAT_MOVEVARS_ACCELERATE:\n\t\tmovevars.accelerate = value;\n\t\tbreak;\n\tcase STAT_MOVEVARS_AIRACCELERATE:\n\t\tmovevars.airaccelerate = value;\n\t\tbreak;\n\tcase STAT_MOVEVARS_WATERACCELERATE:\n\t\tmovevars.wateraccelerate = value;\n\t\tbreak;\n\tcase STAT_MOVEVARS_FRICTION:\n\t\tmovevars.friction = value;\n\t\tbreak;\n\tcase STAT_MOVEVARS_WATERFRICTION:\n\t\tmovevars.waterfriction = value;\n\t\tbreak;\n\tcase STAT_MOVEVARS_ENTGRAVITY:\n\t\tcl.playerview[pnum].entgravity = value;\n\t\tbreak;\n\t}\n}\n#endif\n\n//the two values are expected to be the same, they're just both provided for precision.\nstatic void CL_SetStatNumeric (int pnum, unsigned int stat, int ivalue, float fvalue)\n{\n\tif (stat < 0 || stat >= MAX_CL_STATS)\n\t\treturn;\n//\t\tHost_EndGame (\"CL_SetStat: %i is invalid\", stat);\n\n#ifdef QUAKESTATS\n\tif (stat == STAT_TIME && (cls.fteprotocolextensions & PEXT_ACCURATETIMINGS))\n\t{\n\t\tcl.oldgametime = cl.gametime;\n\t\tcl.oldgametimemark = cl.gametimemark;\n\n\t\tcl.gametime = fvalue * 0.001;\n\t\tcl.gametimemark = realtime;\n\t}\n#endif\n\n\tif (cls.demoplayback == DPB_MVD)\n\t{\n\t\textern int cls_lastto;\n\t\tcl.players[cls_lastto].stats[stat]=ivalue;\n\t\tcl.players[cls_lastto].statsf[stat]=fvalue;\n\n\t\tif (cl_shownet.value == 3)\n\t\t\tCon_Printf(\"\\t%i: %i=%g\\n\", cls_lastto, stat, fvalue);\n\n\t\tfor (pnum = 0; pnum < cl.splitclients; pnum++)\n\t\t\tif (cl.playerview[pnum].cam_spec_track == cls_lastto && cl.playerview[pnum].cam_state != CAM_FREECAM)\n\t\t\t\tCL_SetStat_Internal(pnum, stat, ivalue, fvalue);\n\t}\n\telse\n\t{\n\t\tunsigned int pl = cl.playerview[pnum].playernum;\n\t\tif (pl < MAX_CLIENTS)\n\t\t{\n\t\t\tcl.players[pl].stats[stat]=ivalue;\n\t\t\tcl.players[pl].statsf[stat]=fvalue;\n\t\t}\n\n\t\tif (cl_shownet.value == 3)\n\t\t\tCon_Printf(\"\\t%i(%i): %i=%g\\n\", pnum, pl, stat, fvalue);\n\n\t\tCL_SetStat_Internal(pnum, stat, ivalue, fvalue);\n\t}\n\n#ifdef QUAKESTATS\n\tif (stat == STAT_VIEWHEIGHT && ((cls.z_ext & Z_EXT_VIEWHEIGHT) || cls.protocol == CP_NETQUAKE))\n\t\tcl.playerview[pnum].viewheight = fvalue;\n#endif\n\n#ifdef NQPROT\n\tif (cls.protocol == CP_NETQUAKE && (CPNQ_IS_DP || (cls.fteprotocolextensions2 & PEXT2_PREDINFO)))\n\t{\n\t\tif (cls.fteprotocolextensions2 & PEXT2_PREDINFO)\n\t\t\tCL_SetStatMovevar(pnum, stat, ivalue, fvalue);\n\t\telse\n\t\t\tCL_SetStatMovevar(pnum, stat, ivalue, *(float*)&ivalue);\t//DP sucks.\n\t}\n#endif\n}\n\nstatic void CL_SetStatString (int pnum, int stat, const char *value)\n{\n\tif (stat < 0 || stat >= MAX_CL_STATS)\n\t\treturn;\n//\t\tHost_EndGame (\"CL_SetStat: %i is invalid\", stat);\n\n\tif (cls.demoplayback == DPB_MVD)\n\t{\n\t\textern int cls_lastto;\n\t\t//Z_Free(cl.players[cls_lastto].statsstr[stat]);\n\t\t//cl.players[cls_lastto].statsstr[stat]=Z_StrDup(value);\n\n\t\tfor (pnum = 0; pnum < cl.splitclients; pnum++)\n\t\t\tif (cl.playerview[pnum].cam_spec_track == cls_lastto && cl.playerview[pnum].cam_state != CAM_FREECAM)\n\t\t\t{\n\t\t\t\tif (cl.playerview[pnum].statsstr[stat])\n\t\t\t\t\tZ_Free(cl.playerview[pnum].statsstr[stat]);\n\t\t\t\tcl.playerview[pnum].statsstr[stat] = Z_StrDup(value);\n\t\t\t}\n\t}\n\telse\n\t{\n\t\tif (cl.playerview[pnum].statsstr[stat])\n\t\t\tZ_Free(cl.playerview[pnum].statsstr[stat]);\n\t\tcl.playerview[pnum].statsstr[stat] = Z_StrDup(value);\n\t}\n}\n\n/*\n//if we're going to 'spend' another byte for longer indexes, we might as well spend an extra 4 bits on the type too, allowing for 64bit types etc.\nstatic void CL_ParseExtendedStat(int destsplit)\n{\n//float/double/sint/uint\n//string\n\tquint64_t id = MSG_ReadUInt64();\n\tunsigned int type;\n\ttype = id&0xf;\n\tid>>=4;\t//we're never going to have that many stats.\n\tswitch(type)\n\t{\n\tcase ev_void:\t//might as well.\n\t\tCL_SetStatNumeric(destsplit, id, 0, 0);\n\t\tbreak;\n\tcase ev_string:\n\t\tCL_SetStatString(destsplit, id, MSG_ReadString());\n\t\tbreak;\n\tcase ev_float:\n\t\t{\n\t\t\tfloat f = MSG_ReadFloat();\n\t\t\tCL_SetStatNumeric(destsplit, id, f, f);\n\t\t}\n\t\tbreak;\n\tcase ev_vector:\n\t\t{\n\t\t\tfloat f;\n\t\t\tf = MSG_ReadFloat();CL_SetStatNumeric(destsplit, id+0, f, f);\n\t\t\tf = MSG_ReadFloat();CL_SetStatNumeric(destsplit, id+1, f, f);\n\t\t\tf = MSG_ReadFloat();CL_SetStatNumeric(destsplit, id+2, f, f);\n\t\t}\n\t\tbreak;\n\tcase ev_entity:\n\t\t{\n\t\t\tunsigned int i = MSGCL_ReadEntity();\n\t\t\tCL_SetStatNumeric(destsplit, id, i, i);\n\t\t}\n\t\tbreak;\n//\tcase ev_field:\n//\tcase ev_function:\n//\tcase ev_pointer:\n\tcase ev_integer:\n\t\t{\n\t\t\tsigned int i = MSG_ReadLong();\n\t\t\tCL_SetStatNumeric(destsplit, id, i, i);\n\t\t}\n\t\tbreak;\n\tcase ev_uint:\n\t\t{\n\t\t\tunsigned int i = MSG_ReadLong();\n\t\t\tCL_SetStatNumeric(destsplit, id, i, i);\n\t\t}\n\t\tbreak;\n\tcase ev_int64:\n\t\t{\n\t\t\tqint64_t i = MSG_ReadInt64();\n\t\t\tCL_SetStatNumeric(destsplit, id, i, i);\n\t\t}\n\t\tbreak;\n\tcase ev_uint64:\n\t\t{\n\t\t\tquint64_t i = MSG_ReadUInt64();\n\t\t\tCL_SetStatNumeric(destsplit, id, i, i);\n\t\t}\n\t\tbreak;\n\tcase ev_double:\n\t\t{\n\t\t\tdouble f = MSG_ReadDouble();\n\t\t\tCL_SetStatNumeric(destsplit, id, f, f);\n\t\t}\n\t\tbreak;\n\tdefault:\n\t\tHost_EndGame(\"CL_ParseExtendedStat: type %i is unsupported\", type);\n\t\tbreak;\n\t}\n}*/\n\n/*\n==============\nCL_MuzzleFlash\n==============\n*/\nstatic void CL_MuzzleFlash (int entnum)\n{\n\tdlight_t\t*dl;\n\tplayer_state_t\t*pl;\n\n\tpacket_entities_t *pack;\n\tentity_state_t *s1;\n\tint pnum;\n\tvec3_t org = {0,0,0};\n\tvec3_t axis[3] = {{0,0,0}};\n\tint dlightkey = 0;\n\textern int pt_muzzleflash;\n\textern cvar_t cl_muzzleflash;\n\n\t//was it us?\n\tif (!cl_muzzleflash.ival) // remove all muzzleflashes\n\t\treturn;\n\n\tif (cl_muzzleflash.value == 2)\n\t{\n\t\t//muzzleflash 2 removes muzzleflashes on us\n\t\tfor (pnum = 0; pnum < cl.splitclients; pnum++)\n\t\t\tif (entnum-1 == cl.playerview[pnum].playernum)\n\t\t\t\treturn;\n\t}\n\n\tif (!dlightkey)\n\t{\n\t\tpack = &cl.inframes[cl.validsequence&UPDATE_MASK].packet_entities;\n\n\t\tfor (pnum=0 ; pnum<pack->num_entities ; pnum++)\t//try looking for an entity with that id first\n\t\t{\n\t\t\ts1 = &pack->entities[pnum];\n\n\t\t\tif (s1->number == entnum)\n\t\t\t{\n\t\t\t\tdlightkey = entnum;\n\t\t\t\tVectorCopy(s1->origin, org);\n\t\t\t\tAngleVectors(s1->angles, axis[0], axis[1], axis[2]);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\tif (!dlightkey)\n\t{\t//that ent number doesn't exist, go for a player with that number\n\t\tif ((unsigned)(entnum) <= cl.allocated_client_slots && entnum > 0)\n\t\t{\n\t\t\tpl = &cl.inframes[cl.validsequence&UPDATE_MASK].playerstate[entnum-1];\n\n\t\t\tif (pl->messagenum == cl.validsequence)\n\t\t\t{\n\t\t\t\tdlightkey = -entnum;\n\t\t\t\tVectorCopy(pl->origin, org);\n\t\t\t\tAngleVectors(pl->viewangles, axis[0], axis[1], axis[2]);\n\t\t\t\tif (pl->szmins[2] == 0)\t/*hull is 0-based, so origin is bottom of model, move the light up slightly*/\n\t\t\t\t\torg[2] += pl->szmaxs[2]/2;\n\t\t\t}\n\t\t}\n\t}\n\n\tif (!dlightkey)\n\t\treturn;\n\n\tif (P_RunParticleEffectType(org, axis[0], 1, pt_muzzleflash))\n\t{\n\t\textern cvar_t r_muzzleflash_colour;\n\t\textern cvar_t r_muzzleflash_fade;\n\n\t\tdl = CL_AllocDlight (dlightkey);\n\t\tVectorMA (org, 15, axis[0], dl->origin);\n\t\tmemcpy(dl->axis, axis, sizeof(dl->axis));\n\n\t\tdl->minlight = 32;\n\t\tdl->die = cl.time + 0.1;\n\n\t\tVectorCopy(r_muzzleflash_colour.vec4, dl->color);\n\t\tdl->radius = r_muzzleflash_colour.vec4[3] + (rand()&31);\n\t\tVectorCopy(r_muzzleflash_fade.vec4, dl->channelfade);\n\t\tdl->decay = r_muzzleflash_fade.vec4[3];\n#ifdef RTLIGHTS\n\t\tdl->lightcolourscales[2] = 4;\n#endif\n\t}\n}\n\n//return if we want to print the message.\nstatic char *CL_ParseChat(char *text, player_info_t **player, int *msgflags)\n{\n\textern cvar_t cl_chatsound, cl_nofake, cl_teamchatsound, cl_enemychatsound;\n\tint flags;\n\tint offset=0;\n\tqboolean\tsuppress_talksound;\n\tchar *p;\n\tchar *s;\n\tint check_flood;\n\n\tflags = TP_CategorizeMessage (text, &offset, player);\n\t*msgflags = flags;\n\n\ts = text + offset;\n\n\tif (flags)\n\t{\n\t\tif (!cls.demoplayback)\n\t\t\tSys_ServerActivity();\t//chat always flashes the screen..\n\n\t\tif (*player && Ignore_Message((*player)->name, s, flags))\n\t\t\treturn NULL;\n\n\t\t//check f_ stuff\n\t\tif (*player && (!strncmp(s, \"f_\", 2)|| !strncmp(s, \"q_\", 2)))\n\t\t{\n\t\t\tValidation_Auto_Response(*player - cl.players, s);\n\t\t\treturn s;\n\t\t}\n\n\t\tValidation_CheckIfResponse(text);\n\n#ifdef PLUGINS\n\t\tif (!Plug_ChatMessage(text + offset, *player ? (int)(*player - cl.players) : -1, flags))\n\t\t\treturn NULL;\n#endif\n\n\t\tif (flags & (TPM_TEAM|TPM_OBSERVEDTEAM) && !TP_FilterMessage(text + offset))\n\t\t\treturn NULL;\n\n#ifdef QUAKEHUD\n\t\tif (flags & (TPM_TEAM|TPM_OBSERVEDTEAM) && Sbar_UpdateTeamStatus(*player, text+offset))\n\t\t\treturn NULL;\n#endif\n\n\n\t\tif ((int)msg_filter.value & flags)\n\t\t\treturn NULL;\t//filter chat\n\n\t\tcheck_flood = Ignore_Check_Flood(*player, s, flags);\n\t\tif (check_flood == IGNORE_NO_ADD)\n\t\t\treturn NULL;\n\t\telse if (check_flood == NO_IGNORE_ADD)\n\t\t\tIgnore_Flood_Add(*player, s);\n\t}\n#ifdef PLUGINS\n\telse\n\t{\n\t\tif (!Plug_ServerMessage(text + offset, PRINT_CHAT))\n\t\t\treturn NULL;\n\t}\n#endif\n\n\tsuppress_talksound = false;\n\n\tif (flags == 2 || (!cl.teamplay && flags))\n\t\tsuppress_talksound = TP_CheckSoundTrigger (text + offset);\n\n\tif (cls.demoseeking ||\n\t\t!cl_chatsound.value ||\t\t// no sound at all\n\t\t(cl_chatsound.value == 2 && flags != 2))\t// only play sound in mm2\n\t\tsuppress_talksound = true;\n\n\n\tif (!suppress_talksound)\n\t{\n\t\tif (flags & (TPM_OBSERVEDTEAM|TPM_TEAM) && cl.teamplay)\n\t\t\tS_LocalSound (cl_teamchatsound.string);\n\t\telse\n\t\t\tS_LocalSound (cl_enemychatsound.string);\n\t}\n\n\tif (flags)\n\t{\n\t\tif (cl_nofake.value == 1 || (cl_nofake.value == 2 && !(flags & (TPM_OBSERVEDTEAM | TPM_TEAM))))\n\t\t{\n\t\t\tfor (p = s; *p; p++)\n\t\t\t\tif (*p == 13 || (*p == 10 && p[1]))\n\t\t\t\t\t*p = ' ';\n\t\t}\n\t}\n\n\treturn s;\n}\n\n// CL_PlayerColor: returns color and mask for player_info_t\nstatic int CL_PlayerColor(player_info_t *plr, qboolean *name_coloured)\n{\n\tchar *t;\n\tunsigned int c;\n\n\t*name_coloured = false;\n\n\tif (cl.teamfortress) //override based on team\n\t{\n\t\t//damn spies\n\t\tif (!Q_strcasecmp(plr->team, \"red\"))\n\t\t\tc = 1;\n\t\telse if (!Q_strcasecmp(plr->team, \"blue\"))\n\t\t\tc = 5;\n\t\telse\n\t\t// TODO: needs some work\n\t\tswitch (plr->rbottomcolor)\n\t\t{\t//translate q1 skin colours to console colours\n\t\tcase 10:\n\t\tcase 1:\n\t\t\t*name_coloured = true;\n\t\tcase 4:\t//red\n\t\t\tc = 1;\n\t\t\tbreak;\n\t\tcase 11:\n\t\t\t*name_coloured = true;\n\t\tcase 3: // green\n\t\t\tc = 2;\n\t\t\tbreak;\n\t\tcase 5:\n\t\t\t*name_coloured = true;\n\t\tcase 12:\n\t\t\tc = 3;\n\t\t\tbreak;\n\t\tcase 6:\n\t\tcase 7:\n\t\t\t*name_coloured = true;\n\t\tcase 8:\n\t\tcase 9:\n\t\t\tc = 6;\n\t\t\tbreak;\n\t\tcase 2: // light blue\n\t\t\t*name_coloured = true;\n\t\tcase 13: //blue\n\t\tcase 14: //blue\n\t\t\tc = 5;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\t*name_coloured = true;\n\t\tcase 0: // white\n\t\t\tc = 7;\n\t\t\tbreak;\n\t\t}\n\t}\n\telse if (cl.teamplay)\n\t{\n\t\t// team name hacks\n\t\tif (!strcmp(plr->team, \"red\"))\n\t\t\tc = 1;\n\t\telse if (!strcmp(plr->team, \"blue\"))\n\t\t\tc = 5;\n\t\telse\n\t\t{\n\t\t\tchar *t;\n\n\t\t\tt = plr->team;\n\t\t\tc = 0;\n\n\t\t\tfor (t = plr->team; *t; t++)\n\t\t\t{\n\t\t\t\tc >>= 1;\n\t\t\t\tc ^= *t; // TODO: very weak hash, replace\n\t\t\t}\n\n\t\t\tif ((c / 7) & 1)\n\t\t\t\t*name_coloured = true;\n\n\t\t\tc = 1 + (c % 7);\n\t\t}\n\t}\n\telse\n\t{\n\t\t// override chat color with tc infokey\n\t\t// 0-6 is standard colors (red to white)\n\t\t// 7-13 is using secondard charactermask\n\t\t// 14 and afterwards repeats\n\t\tt = InfoBuf_ValueForKey(&plr->userinfo, \"tc\");\n\t\tif (*t)\n\t\t\tc = atoi(t);\n\t\telse\n\t\t\tc = plr->userid; // Quake2 can start from 0\n\n\t\tif ((c / 7) & 1)\n\t\t\t*name_coloured = true;\n\n\t\tc = 1 + (c % 7);\n\t}\n\n\treturn c;\n}\n\nvoid TTS_SayChatString(char **stringtosay);\n\n// CL_PrintChat: takes chat strings and performs name coloring and cl_parsewhitetext parsing\n// NOTE: text in rawmsg/msg is assumed destroyable and should not be used afterwards\nvoid CL_PrintChat(player_info_t *plr, char *msg, int plrflags)\n{\n\textern cvar_t con_separatechat;\n\tchar *name = NULL;\n\tint c;\n\tqboolean name_coloured = false;\n\textern cvar_t cl_parsewhitetext;\n\tqboolean memessage = false;\n\tchar fullchatmessage[2048];\n\n\tfullchatmessage[0] = 0;\n\t/*if (plrflags & TPM_FAKED)\n\t{\n\t\tname = rawmsg; // use rawmsg pointer and msg modification to generate null-terminated string\n\t\tif (msg)\n\t\t\t*(msg - 2) = 0; // it's assumed that msg has 2 chars before it due to strstr\n\t}*/\n\n\tif (0)//*msg == '\\r')\n\t{\n\t\tname = msg;\n\t\tmsg = strstr(msg, \": \");\n\t\tif (msg)\n\t\t{\n\t\t\tname++;\n\t\t\t*msg = 0;\n\t\t\tmsg+=2;\n\t\t\tplrflags &= ~TPM_TEAM|TPM_OBSERVEDTEAM;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tmsg = name;\n\t\t\tname = NULL;\n\t\t}\n\t}\n\n\tif (msg[0] == '/' && msg[1] == 'm' && msg[2] == 'e' && msg[3] == ' ')\n\t{\n\t\tmsg += 4;\n\t\tmemessage = true; // special /me formatting\n\t}\n\n\tif (plr && !name) // use special formatting with a real chat message\n\t\tname = plr->name; // use player's name\n\n\tif (cl_standardchat.ival)\n\t{\n\t\tname_coloured = true;\n\t\tc = 7;\n\t}\n\telse\n\t{\n\t\tif (plrflags & TPM_SPECTATOR) // is an observer\n\t\t{\n\t\t\t// TODO: we don't even check for this yet...\n\t\t\tif (plrflags & (TPM_TEAM | TPM_OBSERVEDTEAM)) // is on team\n\t\t\t\tc = 0; // blacken () on observers\n\t\t\telse\n\t\t\t{\n\t\t\t\tname_coloured = true;\n\t\t\t\tc = 7;\n\t\t\t}\n\t\t}\n\t\telse if (plr)\n\t\t\tc = CL_PlayerColor(plr, &name_coloured);\n\t\telse\n\t\t{\n\t\t\t// defaults for fake clients\n\t\t\tname_coloured = true;\n\t\t\tc = 7;\n\t\t}\n\t}\n\n\tc = '0' + c;\n\n\tif (plrflags & TPM_QTV)\n\t\tQ_strncatz(fullchatmessage, \"QTV ^m\", sizeof(fullchatmessage));\n\telse if (name)\n\t{\n\t\tif (memessage)\n\t\t{\n\t\t\tif (!cl_standardchat.value && (plrflags & TPM_SPECTATOR))\n\t\t\t\tQ_strncatz(fullchatmessage, \"^0*^7 \", sizeof(fullchatmessage));\n\t\t\telse\n\t\t\t\tQ_strncatz(fullchatmessage, \"* \", sizeof(fullchatmessage));\n\t\t}\n\t\telse\n\t\t\tQ_strncatz(fullchatmessage, \"\\1\", sizeof(fullchatmessage));\n\n#if defined(HAVE_SPEECHTOTEXT)\n\t\tTTS_SayChatString(&msg);\n#endif\n\n\t\tif (plrflags & (TPM_TEAM|TPM_OBSERVEDTEAM)) // for team chat don't highlight the name, just the brackets\n\t\t{\n\t\t\tQ_strncatz(fullchatmessage, va(\"(^[^7%s%s^d\\\\player\\\\%i^])\", name_coloured?\"^m\":\"\", name, (int)(plr-cl.players)), sizeof(fullchatmessage));\n\t\t}\n\t\telse if (cl_standardchat.ival)\n\t\t{\n\t\t\tQ_strncatz(fullchatmessage, va(\"^[^7%s%s^d\\\\player\\\\%i^]\", name_coloured?\"^m\":\"\", name, (int)(plr-cl.players)), sizeof(fullchatmessage));\n\t\t}\n\t\telse\n\t\t{\n\t\t\tQ_strncatz(fullchatmessage, va(\"^[^7%s^%c%s^d\\\\player\\\\%i^]\", name_coloured?\"^m\":\"\", c, name, (int)(plr-cl.players)), sizeof(fullchatmessage));\n\t\t}\n\n\t\tif (!memessage)\n\t\t{\n\t\t\t// only print seperator with an actual player name\n\t\t\tif (!cl_standardchat.value && (plrflags & TPM_SPECTATOR))\n\t\t\t\tQ_strncatz(fullchatmessage, \"^0: ^d\", sizeof(fullchatmessage));\n\t\t\telse\n\t\t\t\tQ_strncatz(fullchatmessage, \": \", sizeof(fullchatmessage));\n\t\t}\n\t\telse\n\t\t\tQ_strncatz(fullchatmessage, \" \", sizeof(fullchatmessage));\n\t}\n\telse\n\t\tQ_strncatz(fullchatmessage, \"\\1\", sizeof(fullchatmessage));\n\n\t// print message\n\tif (cl_parsewhitetext.value && (cl_parsewhitetext.value == 1 || (plrflags & (TPM_TEAM|TPM_OBSERVEDTEAM))))\n\t{\n\t\tchar *t, *u;\n\n\t\twhile ((t = strchr(msg, '{')))\n\t\t{\n\t\t\tint c;\n\t\t\tif (t > msg && t[-1] == '^')\n\t\t\t{\n\t\t\t\tfor (c = 1; t-c > msg; c++)\n\t\t\t\t{\n\t\t\t\t\tif (t[-c] == '^')\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tif (c & 1)\n\t\t\t\t{\n\t\t\t\t\t*t = '\\0';\n\t\t\t\t\tQ_strncatz(fullchatmessage, va(\"%s{\", msg), sizeof(fullchatmessage));\n\t\t\t\t\tmsg = t+1;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\t\t\tu = strchr(t, '}');\n\t\t\tif (u)\n\t\t\t{\n\t\t\t\t*t = 0;\n\t\t\t\t*u = 0;\n\t\t\t\tQ_strncatz(fullchatmessage, va(\"%s\", msg), sizeof(fullchatmessage));\n\t\t\t\tQ_strncatz(fullchatmessage, va(\"^m%s^m\", t+1), sizeof(fullchatmessage));\n\t\t\t\tmsg = u+1;\n\t\t\t}\n\t\t\telse\n\t\t\t\tbreak;\n\t\t}\n\t\tQ_strncatz(fullchatmessage, va(\"%s\", msg), sizeof(fullchatmessage));\n\t}\n\telse\n\t{\n\t\tQ_strncatz(fullchatmessage, va(\"%s\", msg), sizeof(fullchatmessage));\n\t}\n\n#ifdef CSQC_DAT\n\tif (CSQC_ParsePrint(fullchatmessage, PRINT_CHAT))\n\t\treturn;\n#endif\n\n\n\tif (con_separatechat.ival)\n\t{\n\t\tif (!con_chat)\n\t\t\tcon_chat = Con_Create(\"chat\", CONF_HIDDEN|CONF_NOTIFY|CONF_NOTIFY_BOTTOM);\n\t\tif (con_chat)\n\t\t{\n\t\t\tCon_PrintCon(con_chat, fullchatmessage, con_chat->parseflags);\n\n\t\t\tif (con_separatechat.ival == 1)\n\t\t\t{\n\t\t\t\tconsole_t *c = Con_GetMain();\n\t\t\t\tCon_PrintCon(c, fullchatmessage, c->parseflags|PFS_NONOTIFY);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n\n\tCon_Printf(\"%s\", fullchatmessage);\n}\n\n// CL_PrintStandardMessage: takes non-chat net messages and performs name coloring\n// NOTE: msg is considered destroyable\nstatic char acceptedchars[] = {'.', '?', '!', '\\'', ',', ':', ' ', '\\0'};\nstatic void CL_PrintStandardMessage(char *msgtext, int printlevel)\n{\n\tint i;\n\tplayer_info_t *p, *foundp = NULL;\n\textern cvar_t cl_standardmsg, msg;\n\tchar *begin = msgtext;\n\tchar fullmessage[2048];\n\n\tchar *found;\n\n\tif (printlevel < msg.ival)\n\t\treturn;\n\n\tfullmessage[0] = 0;\n\n\twhile(*msgtext)\n\t{\n\t\tfound = NULL;\n\t\t// search for player names in message\n\t\tfor (i = 0, p = cl.players; i < cl.allocated_client_slots; p++, i++)\n\t\t{\n\t\t\tchar *v;\n\t\t\tchar *name;\n\t\t\tint len;\n\n\t\t\tname = p->name;\n\t\t\tif (!(*name))\n\t\t\t\tcontinue;\n\t\t\tlen = strlen(name);\n\t\t\tv = strstr(msgtext, name);\n\t\t\twhile (v)\n\t\t\t{\n\t\t\t\t// name parsing rules\n\t\t\t\tif (v != begin && *(v-1) != ' ') // must be space before name\n\t\t\t\t{\n\t\t\t\t\t\tv = strstr(v+len, name);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t{\n\t\t\t\t\tint i;\n\t\t\t\t\tchar aftername = *(v + len);\n\n\t\t\t\t\t// search for accepted chars in char after name in msg\n\t\t\t\t\tfor (i = 0; i < sizeof(acceptedchars); i++)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (acceptedchars[i] == aftername)\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (sizeof(acceptedchars) == i)\n\t\t\t\t\t{\n\t\t\t\t\t\tv = strstr(v+len, name);\n\t\t\t\t\t\tcontinue; // no accepted char found\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (!found || v < found)\n\t\t\t\t{\n\t\t\t\t\tfound = v;\n\t\t\t\t\tfoundp = p;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tif (found)\n\t\t{\n\t\t\tqboolean coloured;\n\t\t\tchar c;\n\t\t\tint len = strlen(foundp->name);\n\n\t\t\t// print msg chunk\n\t\t\t*found = 0; // cut off message\n\t\t\tQ_strncatz(fullmessage, msgtext, sizeof(fullmessage));\n\t\t\tmsgtext = found + len; // update search point\n\n\t\t\t// get name color\n\t\t\tif (foundp->spectator || cl_standardmsg.ival)\n\t\t\t{\n\t\t\t\tcoloured = false;\n\t\t\t\tc = '7';\n\t\t\t}\n\t\t\telse\n\t\t\t\tc = '0' + CL_PlayerColor(foundp, &coloured);\n\n\t\t\t// print name\n\t\t\tQ_strncatz(fullmessage, va(\"^[%s^%c%s^d\\\\player\\\\%i^]\", coloured?\"^m\":\"\", c, foundp->name, (int)(foundp - cl.players)), sizeof(fullmessage));\n\t\t}\n\t\telse\n\t\t\tbreak; //nope, can't find anyone in there...\n\t}\n\n\t// print final chunk\n\tQ_strncatz(fullmessage, msgtext, sizeof(fullmessage));\n#ifdef HAVE_LEGACY\n\tif (scr_usekfont.ival)\n\t\tCon_PrintFlags(fullmessage, PFS_FORCEUTF8, 0);\n\telse\n#endif\n\t\tCon_Printf(\"%s\", fullmessage);\n}\n\nstatic char printtext[4096];\nstatic void CL_ParsePrint(const char *msg, int level)\n{\n\tchar n, *e;\n\tif (strlen(printtext) + strlen(msg)+2 >= sizeof(printtext))\n\t{\n\t\tCon_Printf(\"%s\", printtext);\n\t\tQ_strncpyz(printtext, msg, sizeof(printtext));\n\t}\n\telse\n\t\tstrcat(printtext, msg);\t//safe due to size on if.\n#ifdef HAVE_LEGACY\t//eztv is fucking nasty.\n\tif (level == PRINT_CHAT && *printtext == '#' && printtext[1] >= '0' && printtext[1] <= '9' && !strchr(printtext, '\\n'))\n\t\tstrcat(printtext, \"\\n\");\n#endif\n\twhile((e = strchr(printtext, '\\n')) || (e = strchr(printtext, '\\r')))\n\t{\n\t\tn = e[1];\n\t\te[1] = 0;\n\n\t\tif (!cls.demoseeking)\n\t\t{\n\t\t\tif (level == PRINT_CHAT)\n\t\t\t{\n\t\t\t\tchar *body;\n\t\t\t\tint msgflags;\n\t\t\t\tplayer_info_t *plr = NULL;\n\n\t\t\t\tif (!TP_SuppressMessage(printtext))\n\t\t\t\t{\n\t\t\t\t\tbody = CL_ParseChat(printtext, &plr, &msgflags);\n\t\t\t\t\tif (body)\n\t\t\t\t\t\tCL_PrintChat(plr, body, msgflags);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n#ifdef CSQC_DAT\n\t\t\t\tif (!CSQC_ParsePrint(printtext, level))\n#endif\n#ifdef PLUGINS\n\t\t\t\tif (Plug_ServerMessage(printtext, level))\n#endif\n#ifdef QUAKEHUD\n\t\t\t\t\tif (!Stats_ParsePickups(printtext) || !msg_filter_pickups.ival)\n\t\t\t\t\t\tif (!Stats_ParsePrintLine(printtext) || !msg_filter_frags.ival)\n#else\n\t\t\t\t\tif (!msg_filter_pickups.ival)\n\t\t\t\t\t\tif (!msg_filter_frags.ival)\n#endif\n\t\t\t\t\t\t\tCL_PrintStandardMessage(printtext, level);\n\t\t\t}\n\t\t}\n\n\t\tTP_SearchForMsgTriggers(printtext, level);\n\t\te[1] = n;\n\t\te++;\n\n\t\tmemmove(printtext, e, strlen(e)+1);\n\t}\n}\n\nstatic void CL_ParseWeaponStats(void)\n{\n#ifdef QUAKEHUD\n\tint pl = atoi(Cmd_Argv(0));\n\tchar *wname = Cmd_Argv(1);\n\tunsigned int total = strtoul(Cmd_Argv(2), NULL, 0);\n\tunsigned int hit = strtoul(Cmd_Argv(3), NULL, 0);\n\tunsigned int idx;\n\n\tif (pl >= cl.allocated_client_slots)\n\t\treturn;\n\n\tfor (idx = 0; idx < countof(cl.players[pl].weaponstats); idx++)\n\t{\n\t\tif (!strcmp(cl.players[pl].weaponstats[idx].wname, wname) || !*cl.players[pl].weaponstats[idx].wname)\n\t\t{\n\t\t\tQ_strncpyz(cl.players[pl].weaponstats[idx].wname, wname, sizeof(cl.players[pl].weaponstats[idx].wname));\n\t\t\tcl.players[pl].weaponstats[idx].total = total;\n\t\t\tcl.players[pl].weaponstats[idx].hit = hit;\n\t\t\treturn;\n\t\t}\n\t}\n#endif\n}\n\nstatic void CL_ParseItemTimer(void)\n{\n\t//it [cur/]duration x y z radius 0xRRGGBB \"timername\" owningent\n\tfloat timeout;// = atof(Cmd_Argv(0));\n\tvec3_t org = {\tatof(Cmd_Argv(1)),\n\t\t\t\t\tatof(Cmd_Argv(2)),\n\t\t\t\t\tatof(Cmd_Argv(3))};\n\tfloat radius =\tatof(Cmd_Argv(4));\n\tunsigned int rgb = (Cmd_Argc() > 5)?strtoul(Cmd_Argv(5), NULL, 16):0x202020;\n//\tchar *timername =\tCmd_Argv(6);\n\tunsigned int entnum = strtoul(Cmd_Argv(7), NULL, 0);\n\tstruct itemtimer_s *timer;\n\tfloat start = cl.time;\n\tchar *e;\n\ttimeout = strtod(Cmd_Argv(0), &e);\n\tif (*e == '/')\n\t{\n\t\tstart += timeout;\n\t\ttimeout = atof(e+1);\n\t\tstart -= timeout;\n\t}\n\n\tif (!timeout)\n\t\ttimeout = FLT_MAX;\n\tif (!radius)\n\t\tradius = 32;\n\n\tfor (timer = cl.itemtimers; timer; timer = timer->next)\n\t{\n\t\tif (entnum)\n\t\t{\n\t\t\tif (timer->entnum == entnum)\n\t\t\t\tbreak;\n\t\t}\n\t\telse if (VectorCompare(timer->origin, org))\n\t\t\tbreak;\n\t}\n\tif (!timer)\n\t{\t//didn't find it.\n\t\ttimer = Z_Malloc(sizeof(*timer));\n\t\ttimer->next = cl.itemtimers;\n\t\tcl.itemtimers = timer;\n\t}\n\n\tVectorCopy(org, timer->origin);\n\ttimer->radius = radius;\n\ttimer->duration = timeout;\n\ttimer->entnum = entnum;\n\ttimer->start = start;\n\ttimer->end = start + timer->duration;\n\ttimer->rgb[0] = ((rgb>>16)&0xff)/255.0;\n\ttimer->rgb[1] = ((rgb>> 8)&0xff)/255.0;\n\ttimer->rgb[2] = ((rgb    )&0xff)/255.0;\n}\n\n#ifdef PLUGINS\nstatic void CL_ParseTeamInfo(void)\n{\n\tunsigned int pidx = atoi(Cmd_Argv(1));\n\tvec3_t org =\n\t{\n\t\tatof(Cmd_Argv(2)),\n\t\tatof(Cmd_Argv(3)),\n\t\tatof(Cmd_Argv(4))\n\t};\n\tfloat health = atof(Cmd_Argv(5));\n\tfloat armour = atof(Cmd_Argv(6));\n\tunsigned int items = strtoul(Cmd_Argv(7), NULL, 0);\n\tchar *nick = Cmd_Argv(8);\n\n\tif (pidx < cl.allocated_client_slots)\n\t{\n\t\tplayer_info_t *pl = &cl.players[pidx];\n\t\tpl->tinfo.time = cl.time+5;\n\t\tpl->tinfo.health = health;\n\t\tpl->tinfo.armour = armour;\n\t\tpl->tinfo.items = items;\n\t\tVectorCopy(org, pl->tinfo.org);\n\t\tQ_strncpyz(pl->tinfo.nick, nick, sizeof(pl->tinfo.nick));\n\t}\n}\n#endif\n\n\nstatic char stufftext[4096];\nstatic void CL_ParseStuffCmd(char *msg, int destsplit)\t//this protects stuffcmds from network segregation.\n{\n\tint cbuflevel;\n#ifdef NQPROT\n\tif (!*stufftext && *msg == 1)\n\t{\n\t\tif (developer.ival)\n\t\t{\n\t\t\tCon_DPrintf(\"Proquake Message:\\n\");\n\t\t\tCon_HexDump(msg, strlen(msg), 1, 16);\n\t\t}\n\t\tmsg = CLNQ_ParseProQuakeMessage(msg);\n\t}\n#endif\n\n\tQ_strncatz(stufftext, msg, sizeof(stufftext)-1);\n\twhile((msg = strchr(stufftext, '\\n')))\n\t{\n\t\t*msg = '\\0';\n\t\tcbuflevel = RESTRICT_SERVERSEAT(destsplit);\n\t\tCon_DLPrintf((cls.state==ca_active)?1:2, \"stufftext%i: %s\\n\", destsplit, stufftext);\n\t\tif (!strncmp(stufftext, \"fullserverinfo \", 15) || !strncmp(stufftext, \"//fullserverinfo \", 17))\n\t\t{\n\t\t\tCmd_TokenizeString(stufftext+2, false, false);\n\t\t\tif (Cmd_Argc() == 2)\n\t\t\t{\n\t\t\t\tcl.haveserverinfo = true;\n\t\t\t\tInfoBuf_FromString(&cl.serverinfo, Cmd_Argv(1), false);\n\t\t\t\tCL_CheckServerInfo();\n\t\t\t}\n\n\t\t\t#if _MSC_VER > 1200\n\t\t\tif (cls.netchan.remote_address.type != NA_LOOPBACK)\n\t\t\t\tSys_RecentServer(\"+connect\", cls.servername, va(\"%s (%s)\", InfoBuf_ValueForKey(&cl.serverinfo, \"hostname\"), cls.servername), \"Join QW Server\");\n\t\t\t#endif\n\t\t}\n\t\telse if (!strncmp(stufftext, \"//svi \", 6))\t//for serverinfo over NQ protocols\n\t\t{\n\t\t\tCmd_TokenizeString(stufftext+2, false, false);\n\t\t\tCon_DPrintf(\"SERVERINFO: %s=%s\\n\", Cmd_Argv(1), Cmd_Argv(2));\n\t\t\tInfoBuf_SetStarKey(&cl.serverinfo, Cmd_Argv(1), Cmd_Argv(2));\n\t\t\tCL_CheckServerInfo();\n\t\t}\n\t\telse if (!strncmp(stufftext, \"//ls \", 5))\t//for extended lightstyles\n\t\t{\n\t\t\tvec3_t rgb;\n\t\t\tCmd_TokenizeString(stufftext+2, false, false);\n\t\t\trgb[0] = ((Cmd_Argc()>3)?atof(Cmd_Argv(3)):1);\n\t\t\trgb[1] = ((Cmd_Argc()>5)?atof(Cmd_Argv(4)):rgb[0]);\n\t\t\trgb[2] = ((Cmd_Argc()>5)?atof(Cmd_Argv(5)):rgb[0]);\n\t\t\tR_UpdateLightStyle(atoi(Cmd_Argv(1)), Cmd_Argv(2), rgb[0], rgb[1], rgb[2]);\n\t\t}\n\n#ifdef NQPROT\n\t\t//DP's download protocol\n\t\telse if (cls.protocol == CP_NETQUAKE && !strncmp(stufftext, \"cl_serverextension_download \", 28))\t//<supported>. server lets us know that it supports it.\n\t\t\tcl_dp_serverextension_download = true;\t//warning, this is sent BEFORE svc_serverdata, so cannot use cl.foo\n\t\telse if (cls.protocol == CP_NETQUAKE && !strncmp(stufftext, \"cl_downloadbegin \", 17))\t\t//<size> <name>.  server [reliably] lets us know that its going to start sending data.\n\t\t\tCLDP_ParseDownloadBegin(stufftext);\n\t\telse if (cls.protocol == CP_NETQUAKE && !strncmp(stufftext, \"cl_downloadfinished \", 20))\t\t//<size> <crc>. server [reliably] lets us know that we acked the entire thing\n\t\t\tCLDP_ParseDownloadFinished(stufftext);\n\t\telse if (cls.protocol == CP_NETQUAKE && !strcmp(stufftext, \"stopdownload\"))\t\t\t\t\t\t//download command reported failure. safe to request the next.\n\t\t{\n\t\t\tif (cls.download)\n\t\t\t\tCL_DownloadFailed(cls.download->remotename, cls.download, DLFAIL_CORRUPTED);\n\t\t}\n\n\t\t//DP servers use these to report the correct csprogs.dat file+version to use.\n\t\t//WARNING: these are sent BEFORE svc_serverdata, so we cannot store this state into cl.foo\n\t\t//we poke the data into cl.serverinfo once we get the following svc_serverdata.\n\t\t//we then clobber it from a fullserverinfo message if its an fte server running dpp7, but hey.\n\t\telse if (cls.protocol == CP_NETQUAKE && !strncmp(stufftext, \"csqc_progname \", 14))\n\t\t\tCOM_ParseOut(stufftext+14, cl_dp_csqc_progsname, sizeof(cl_dp_csqc_progsname));\n\t\telse if (cls.protocol == CP_NETQUAKE && !strncmp(stufftext, \"csqc_progsize \", 14))\n\t\t\tcl_dp_csqc_progssize = atoi(stufftext+14);\n\t\telse if (cls.protocol == CP_NETQUAKE && !strncmp(stufftext, \"csqc_progcrc \", 13))\n\t\t\tcl_dp_csqc_progscrc = atoi(stufftext+13);\n\n\t\t//NQ servers/mods like spamming this. Its annoying, but we might as well use it if we can, while also muting it.\n\t\telse if (!strncmp(stufftext, \"cl_fullpitch \", 13) || !strncmp(stufftext, \"pq_fullpitch \", 13))\n\t\t{\n\t\t\tif (!cl.haveserverinfo)\n\t\t\t{\n\t\t\t\tInfoBuf_SetKey(&cl.serverinfo, \"maxpitch\", (atoi(stufftext+13))? \"90\":\"\");\n\t\t\t\tInfoBuf_SetKey(&cl.serverinfo, \"minpitch\", (atoi(stufftext+13))?\"-90\":\"\");\n\t\t\t\tCL_CheckServerInfo();\n\t\t\t}\n\t\t}\n#endif\n\n\t\telse if (!strncmp(stufftext, \"//paknames \", 11))\t//so that the client knows what to download...\n\t\t{\t\t\t\t\t\t\t\t\t\t\t\t\t//there's a couple of prefixes involved etc\n\t\t\tZ_StrCat(&cl.serverpacknames, stufftext+(cl.serverpackhashes?11:10));\n\t\t\tcl.serverpakschanged = true;\n\t\t}\n\t\telse if (!strncmp(stufftext, \"//paks \", 7))\t\t\t//gives the client a list of hashes to match against\n\t\t{\t\t\t\t\t\t\t\t\t\t\t\t\t//the client can re-order for cl_pure support, or download dupes to avoid version mismatches\n\t\t\tZ_StrCat(&cl.serverpackhashes, stufftext+(cl.serverpackhashes?7:6));\n\t\t\tcl.serverpakschanged = true;\n\t\t\tCL_CheckServerPacks();\n\t\t}\n#ifdef HAVE_LEGACY\n\t\telse if (!strncmp(stufftext, \"//vwep \", 7))\t\t\t//list of vwep model indexes, because using the normal model precaches wasn't cool enough\n\t\t{\t\t\t\t\t\t\t\t\t\t\t\t\t//(from zquake/ezquake)\n\t\t\tint i;\n\t\t\tchar *mname;\n\t\t\tCmd_TokenizeString(stufftext+7, false, false);\n\t\t\tfor (i = 0; i < Cmd_Argc(); i++)\n\t\t\t{\n\t\t\t\tmname = Cmd_Argv(i);\n\t\t\t\tif (strcmp(mname, \"-\"))\n\t\t\t\t{\n\t\t\t\t\tmname = va(\"progs/%s.mdl\", Cmd_Argv(i));\n\t\t\t\t\tZ_StrDupPtr(&cl.model_name_vwep[i], mname);\n\t\t\t\t\tif (cls.state == ca_active)\n\t\t\t\t\t{\n\t\t\t\t\t\tCL_CheckOrEnqueDownloadFile(cl.model_name_vwep[i], NULL, 0);\n\t\t\t\t\t\tcl.model_precache_vwep[i] = Mod_ForName(cl.model_name_vwep[i], MLV_WARN);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tZ_Free(cl.model_name_vwep[i]);\n\t\t\t\t\tcl.model_name_vwep[i] = NULL;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n#endif\n\t\telse if (cls.demoplayback && !strncmp(stufftext, \"playdemo \", 9))\n\t\t{\t//some demos (like speed-demos-archive's marathon runs) chain multiple demos with playdemo commands\n\t\t\t//these should still chain properly even when the demo is in some archive(like .dz) or subdir\n\t\t\tchar newdemo[MAX_OSPATH], temp[MAX_OSPATH], *s;\n\t\t\tCmd_TokenizeString(stufftext, false, false);\n\t\t\ts = Cmd_Argv(1);\n\t\t\tif (strchr(s, ':') || strchr(s, '/') || strchr(s, '\\\\'))\n\t\t\t\tQ_strncpyz(newdemo, s, sizeof(newdemo));\n\t\t\telse\n\t\t\t{\n\t\t\t\tnewdemo[0] = 0;\n\t\t\t\tif (cls.lastdemowassystempath)\n\t\t\t\t\tQ_strncatz(newdemo, \"#\", sizeof(newdemo));\n\t\t\t\tQ_strncatz(newdemo, cls.lastdemoname, sizeof(newdemo));\n\t\t\t\t*COM_SkipPath(newdemo) = 0;\n\t\t\t\tQ_strncatz(newdemo, Cmd_Argv(1), sizeof(newdemo));\n\t\t\t}\n\n\t\t\tCbuf_AddText (\"playdemo \", cbuflevel);\n\t\t\tCbuf_AddText (COM_QuotedString(newdemo, temp, sizeof(temp), false), cbuflevel);\n\t\t\tCbuf_AddText (\"\\n\", cbuflevel);\n\t\t}\n#ifdef CSQC_DAT\n\t\telse if (CSQC_StuffCmd(destsplit, stufftext, msg))\n\t\t{\n\t\t}\n#endif\n\t\telse if (!strncmp(stufftext, \"//querycmd \", 11))\t//for servers to check if a command exists or not.\n\t\t{\n\t\t\tCOM_Parse(stufftext + 11);\n\t\t\tif (Cmd_Exists(com_token))\n\t\t\t{\n\t\t\t\tCbuf_AddText (\"cmd cmdsupported \", cbuflevel);\n\t\t\t\tCbuf_AddText (com_token, cbuflevel);\n\t\t\t\tCbuf_AddText (\"\\n\", cbuflevel);\n\t\t\t}\n\t\t}\n\t\telse if (!strncmp(stufftext, \"//exectrigger \", 14))\t\t//so that mods can add whatever 'alias grabbedarmour' or whatever triggers that users might want to script responses for, without errors about unknown commands\n\t\t{\n\t\t\tCOM_Parse(stufftext + 14);\n\t\t\tif (Cmd_AliasExist(com_token, cbuflevel))\n\t\t\t\tCmd_ExecuteString(com_token, cbuflevel);\t//do this NOW so that it's done before any models or anything are loaded\n\t\t}\n\t\telse if (!strncmp(stufftext, \"//set \", 6))\t\t\t\t//equivelent to regular set, except non-spammy if it doesn't exist, and happens instantly without extra latency.\n\t\t{\n\t\t\tCmd_ExecuteString(stufftext+2, cbuflevel);\t//do this NOW so that it's done before any models or anything are loaded\n\t\t}\n\t\telse if (!strncmp(stufftext, \"//at \", 5))\t\t\t\t//ktx autotrack hints\n\t\t{\n\t\t\tCam_SetModAutoTrack(atoi(stufftext+5));\n\t\t}\n\t\telse if (!strncmp(stufftext, \"//wps \", 5))\t\t\t\t//ktx weapon statistics\n\t\t{\n\t\t\tCmd_TokenizeString(stufftext+5, false, false);\n\t\t\tCL_ParseWeaponStats();\n\t\t}\n\t\telse if (!strncmp(stufftext, \"//kickfile \", 11))\t\t//FTE sends this to give a more friendly error about modified BSP files, although it could be used for more stuff.\n\t\t{\n\t\t\tflocation_t loc;\n\t\t\tCmd_TokenizeString(stufftext+2, false, false);\n\t\t\tif (FS_FLocateFile(Cmd_Argv(1), FSLF_IFFOUND, &loc))\n\t\t\t{\n\t\t\t\tif (!*loc.rawname)\n\t\t\t\t\tCon_Printf(\"You have been kicked due to the file \"U8(\"%s\")\" being modified, inside \"U8(\"%s\")\"\\n\", Cmd_Argv(1), loc.search->logicalpath);\n\t\t\t\telse\n\t\t\t\t\tCon_Printf(\"You have been kicked due to the file \"U8(\"%s\")\" being modfied, located at \"U8(\"%s\")\"\\n\", Cmd_Argv(1), loc.rawname);\n\t\t\t}\n\t\t}\n\t\telse if (!strncmp(stufftext, \"//it \", 5))\t\t\t\t//it <timeout> <org xyz> <radius> <rgb> <timername> <entnum>\n\t\t{\n\t\t\tCmd_TokenizeString(stufftext+5, false, false);\n\t\t\tCL_ParseItemTimer();\n\t\t}\n\t\telse if (!strncmp(stufftext, \"//fui \", 6))\t\t\t\t//ui <slot> <key> <value>. Full user info updates.\n\t\t{\n\t\t\tunsigned int slot;\n\t\t\tconst char *value;\n\t\t\tCmd_TokenizeString(stufftext+6, false, false);\n\t\t\tslot = atoi(Cmd_Argv(0));\n\t\t\tvalue = Cmd_Argv(1);\n\t\t\tif (slot < MAX_CLIENTS)\n\t\t\t{\n\t\t\t\tplayer_info_t *player = &cl.players[slot];\n\t\t\t\tCon_DPrintf(\"SETINFO %s: %s\\n\", player->name, value);\n\t\t\t\tInfoBuf_FromString(&player->userinfo, value, false);\n\t\t\t\tplayer->userinfovalid = true;\n\t\t\t\tCL_ProcessUserInfo (slot, player);\n\t\t\t}\n\t\t}\n\t\telse if (!strncmp(stufftext, \"//ui \", 5))\t\t\t\t//ui <slot> <key> <value>. Partial user info updates.\n\t\t{\n\t\t\tunsigned int slot;\n\t\t\tconst char *key, *value;\n\t\t\tCmd_TokenizeString(stufftext+5, false, false);\n\t\t\tslot = atoi(Cmd_Argv(0));\n\t\t\tkey = Cmd_Argv(1);\n\t\t\tvalue = Cmd_Argv(2);\n\t\t\tif (slot < MAX_CLIENTS)\n\t\t\t{\n\t\t\t\tplayer_info_t *player = &cl.players[slot];\n\t\t\t\tCon_DPrintf(\"SETINFO %s: %s=%s\\n\", player->name, key, value);\n\t\t\t\tInfoBuf_SetValueForStarKey (&player->userinfo, key, value);\n\t\t\t\tCL_ProcessUserInfo (slot, player);\n\t\t\t}\n\t\t}\n\t\telse if (!strncmp(stufftext, \"//qul \", 6))\t//qtv user list\n\t\t{\n\t\t\tunsigned int cmd, id;\n\t\t\tconst char *name;\n\t\t\tstruct qtvviewers_s **link, *v;\n\t\t\tCmd_TokenizeString(stufftext+5, false, false);\n\t\t\tcmd = atoi(Cmd_Argv(0));\n\t\t\tid = atoi(Cmd_Argv(1));\n\t\t\tname = Cmd_Argv(2);\n\t\t\tfor (link = &cls.qtvviewers; (v=*link); link = &v->next)\n\t\t\t{\n\t\t\t\tif (v->userid == id)\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tswitch(cmd)\n\t\t\t{\n\t\t\tcase 3://del\n\t\t\t\tif (v)\n\t\t\t\t{\n\t\t\t\t\t*link = v->next;\n\t\t\t\t\tZ_Free(v);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase 2://change\n\t\t\tcase 1://add\n\t\t\t\tif (!v)\n\t\t\t\t{\t//new...\n\t\t\t\t\tv = Z_Malloc(sizeof(*v));\n\t\t\t\t\tv->next = cls.qtvviewers;\n\t\t\t\t\tcls.qtvviewers = v;\n\t\t\t\t\tv->userid = id;\n\t\t\t\t}\n\t\t\t\tQ_strncpyz(v->name, name, sizeof(v->name));\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n#ifdef PLUGINS\n\t\telse if (!strncmp(stufftext, \"//tinfo \", 8))\t\t\t//ktx-team-info <pidx> <org xyz> <health> <armour> <STAT_ITEMS> <nickname>\n\t\t{\n\t\t\tCmd_TokenizeString(stufftext+2, false, false);\n\t\t\tCL_ParseTeamInfo();\n\t\t\tPlug_Command_f();\t//FIXME: deprecate this call\n\t\t}\n\t\telse if (!strncmp(stufftext, \"//sn \", 5))\n\t\t{\n\t\t\tCmd_TokenizeString(stufftext+2, false, false);\n\t\t\tPlug_Command_f();\n\t\t}\n#endif\n\t\telse if (!strncmp(stufftext, \"//demomark\", 10) && (stufftext[10]==0||stufftext[10]==' ') && cls.demoseeking == DEMOSEEK_MARK)\n\t\t{\t//found the next marker. we're done seeking.\n\t\t\tcls.demoseeking = DEMOSEEK_NOT;\n\t\t\t//FIXME: pause it.\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (!strncmp(stufftext, \"cmd \", 4))\n\t\t\t\tCbuf_AddText (va(\"p%i \", destsplit+1), cbuflevel);\t//without this, in_forceseat can break directed cmds.\n\t\t\tCbuf_AddText (stufftext, cbuflevel);\n\t\t\tCbuf_AddText (\"\\n\", cbuflevel);\n\t\t}\n\t\tmsg++;\n\n\t\tmemmove(stufftext, msg, strlen(msg)+1);\n\t}\n}\n\nstatic void CL_ParsePrecache(void)\n{\n\tint i, code = (unsigned short)MSG_ReadShort();\n\tchar *s = MSG_ReadString();\n\ti = code & ~PC_TYPE;\n\tswitch(code & PC_TYPE)\n\t{\n\tcase PC_MODEL:\n\t\tif (i >= 1 && i < MAX_PRECACHE_MODELS)\n\t\t{\n\t\t\tmodel_t *model;\n\t\t\tZ_StrDupPtr(&cl.model_name[i], s);\n\n\t\t\tCL_CheckOrEnqueDownloadFile(s, s, DLLF_ALLOWWEB);\n\t\t\tmodel = Mod_ForName(Mod_FixName(s, cl.model_name[1]), (i == 1&&!cl.sendprespawn)?MLV_ERROR:MLV_WARN);\n//\t\t\tif (!model)\n//\t\t\t\tCon_Printf(\"svc_precache: Mod_ForName(\\\"%s\\\") failed\\n\", s);\n\t\t\tcl.model_precache[i] = model;\n\t\t}\n\t\telse\n\t\t\tCon_Printf(\"svc_precache: model index %i outside range %i...%i\\n\", i, 1, MAX_PRECACHE_MODELS);\n\t\tbreak;\n\tcase PC_UNUSED:\n\t\tbreak;\n\tcase PC_SOUND:\n\t\tif (i >= 1 && i < MAX_PRECACHE_SOUNDS)\n\t\t{\n\t\t\tsfx_t *sfx;\n\t\t\tif (S_HaveOutput())\n\t\t\t\tCL_CheckOrEnqueDownloadFile(va(\"sound/%s\", s), NULL, DLLF_ALLOWWEB);\n\t\t\tsfx = S_PrecacheSound (s);\n//\t\t\tif (!sfx)\n//\t\t\t\tCon_Printf(\"svc_precache: S_PrecacheSound(\\\"%s\\\") failed\\n\", s);\n\t\t\tcl.sound_precache[i] = sfx;\n\t\t\tZ_StrDupPtr(&cl.sound_name[i], s);\n\t\t}\n\t\telse\n\t\t\tCon_Printf(\"svc_precache: sound index %i outside range %i...%i\\n\", i, 1, MAX_PRECACHE_SOUNDS);\n\t\tbreak;\n\tcase PC_PARTICLE:\n\t\tif (i >= 1 && i < MAX_SSPARTICLESPRE)\n\t\t{\n\t\t\tZ_StrDupPtr(&cl.particle_ssname[i], s);\n\t\t\tcl.particle_ssprecache[i] = P_FindParticleType(s);\n\t\t\tcl.particle_ssprecaches = true;\n\t\t}\n\t\telse\n\t\t\tCon_Printf(\"svc_precache: particle index %i outside range %i...%i\\n\", i, 1, MAX_SSPARTICLESPRE);\n\t\tbreak;\n\t}\n}\n\nvoid Con_HexDump(qbyte *packet, size_t len, size_t badoffset, size_t stride)\n{\n\tint i;\n\tint pos;\n\n\tpos = 0;\n\twhile(pos < len)\n\t{\n\t\tCon_Printf(\"%5i \", pos);\n\t\tfor (i = 0; i < stride; i++)\n\t\t{\n\t\t\tif (pos >= len)\n\t\t\t\tCon_Printf(\" - \");\n\t\t\telse if (pos == badoffset)\n\t\t\t\tCon_Printf(\"^b^1%2x \", packet[pos]);\n\t\t\telse\n\t\t\t\tCon_Printf(\"%2x \", packet[pos]);\n\t\t\tpos++;\n\t\t}\n\t\tpos-=stride;\n\t\tfor (i = 0; i < stride; i++)\n\t\t{\n\t\t\tif (pos >= len)\n\t\t\t\tCon_Printf(\"X\");\n\t\t\telse if (packet[pos] == 0 || packet[pos] == '\\t' || packet[pos] == '\\r' || packet[pos] == '\\n')\n\t\t\t{\n\t\t\t\tif (pos == badoffset)\n\t\t\t\t\tCon_Printf(\"^b^1.\");\n\t\t\t\telse\n\t\t\t\t\tCon_Printf(\".\");\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (pos == badoffset)\n\t\t\t\t\tCon_Printf(\"^b^1%c\", packet[pos]);\n\t\t\t\telse\n\t\t\t\t\tCon_Printf(\"%c\", packet[pos]);\n\t\t\t}\n\t\t\tpos++;\n\t\t}\n\t\tCon_Printf(\"\\n\");\n\t}\n\n}\nvoid CL_DumpPacket(void)\n{\n\tCon_HexDump(net_message.data, net_message.cursize, MSG_GetReadCount()-1, 16);\n}\n\nstatic void CL_ParsePortalState(void)\n{\n\tint mode = MSG_ReadByte();\n\tint p = -1, a1 = -1, a2 = -1, state = -1;\n#define PS_NEW\t\t\t(1<<7)\n#define PS_AREANUMS\t\t(1<<6)\t//q3 style\n#define PS_PORTALNUM\t(1<<5)\t//q2 style\n#define PS_LARGE\t\t(1<<1)\n#define PS_OPEN\t\t\t(1<<0)\n\n\tif (mode & PS_NEW)\n\t{\n\t\tstate = mode&1;\n\t\tif (!(mode & PS_AREANUMS) && !(mode & PS_PORTALNUM))\n\t\t\tmode |= PS_PORTALNUM;\t//legacy crap\n\n\t\tif (mode & PS_PORTALNUM)\n\t\t{\t//q2 style\n\t\t\tif (mode&PS_LARGE)\n\t\t\t\tp = MSG_ReadShort();\n\t\t\telse\n\t\t\t\tp = MSG_ReadByte();\n\t\t}\n\t\tif (mode & PS_AREANUMS)\n\t\t{\t//q3 style\n\t\t\tif (mode&PS_LARGE)\n\t\t\t{\n\t\t\t\ta1 = MSG_ReadShort();\n\t\t\t\ta2 = MSG_ReadShort();\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\ta1 = MSG_ReadByte();\n\t\t\t\ta2 = MSG_ReadByte();\n\t\t\t}\n\t\t}\n\t}\n\telse\n\t{\t//legacy crap\n\t\tCon_Printf(CON_WARNING\"svc_setportalstate: legacy mode\\n\");\n\t\tmode |= MSG_ReadByte()<<8;\n\t\tp = (mode & 0x7fff);\n\t\tstate = !!(mode & 0x8000);\n\t}\n\n#ifdef HAVE_SERVER\n\t//reduce race conditions when we're both client+server.\n\tif (sv.active)\n\t\treturn;\n#endif\n\n\tif (cl.worldmodel && cl.worldmodel->loadstate==MLS_LOADED && cl.worldmodel->funcs.SetAreaPortalState)\n\t\tcl.worldmodel->funcs.SetAreaPortalState(cl.worldmodel, p, a1, a2, state);\n}\n\nstatic void CL_ParseBaseAngle(int seat)\n{\n\tint i;\n\tshort diff[3];\n\tshort newbase[3];\n\tvec3_t newang;\n\n\tinframe_t *inf = &cl.inframes[cls.netchan.incoming_sequence&UPDATE_MASK];\n\tqbyte fl = MSG_ReadByte();\t//pitch yaw roll lock\n\tfor (i=0 ; i<3 ; i++)\n\t{\n\t\tif (fl & (1u<<i))\n\t\t\tnewbase[i] = MSG_ReadShort();\n\t\telse\n\t\t\tnewbase[i] = 0;\n\n\t\tdiff[i] = newbase[i]-cl.playerview[seat].baseangles[i];\n\t\tcl.playerview[seat].baseangles[i] = newbase[i];\n\n\t\tif (fl & 8)\t//locking the view\n\t\t\tnewang[i] = SHORT2ANGLE(newbase[i]);\n\t\telse\t//free-look\n\t\t\tnewang[i] = cl.playerview[seat].viewangles[i] + SHORT2ANGLE(diff[i]);\n\t}\n\n\tif (cl.ackedmovesequence)\n\t{\n\t\t//tweak all unacked input frames to the new base. the server will do similar on receipt of them.\n\t\ti = min(64, cl.movesequence-cl.ackedmovesequence);\n\t\tfor (i = cl.movesequence-i; i < cl.movesequence; i++)\n\t\t{\n\t\t\tcl.outframes[i & UPDATE_MASK].cmd[seat].angles[0] += diff[0];\n\t\t\tcl.outframes[i & UPDATE_MASK].cmd[seat].angles[1] += diff[1];\n\t\t\tcl.outframes[i & UPDATE_MASK].cmd[seat].angles[2] += diff[2];\n\t\t}\n\t}\n\n\tif (!CSQC_Parse_SetAngles(seat, newang, false))\n\t{\n\t\tif (fl & 8)\n\t\t{\n\t\t\tinf->packet_entities.fixangles[seat] = true;\n\t\t\tVectorCopy (newang, inf->packet_entities.fixedangles[seat]);\n\t\t}\n\t\tVectorCopy (newang, cl.playerview[seat].viewangles);\n\t}\n\tVectorCopy (newang, cl.playerview[seat].intermissionangles);\n\n\tif (fl & 8)\n\t\tVRUI_SnapAngle();\n}\n\nvoid CLEZ_ParseHiddenDemoMessage(void)\n{\n\tfor(;;)\n\t{\n\t\tstatic float throttle;\n\t\tint size = MSG_ReadLong();\n\t\tunsigned int cmd;\n\t\tif (size == -1 || msg_badread)\n\t\t\tbreak;\n\n\t\tcmd = MSG_ReadUInt16();\n\t\tswitch(cmd)\n\t\t{\n\t\tcase 0xFFFF://mvdhidden_extended\n\t\t\treturn;\t//can't handle it... protocol is stupid.\n\n\t\tcase 0x0003://mvdhidden_demoinfo\n\t\t\tMSG_ReadUInt16();\t\t//'more'\n\t\t\tMSG_ReadSkip(size-2);\t//probably json\n\t\t\tbreak;\n\t\tcase 0x0007://mvdhidden_dmgdone\n\t\t\t{\n\t\t\t\tlerpents_t *le;\n\t\t\t\tunsigned short typeandflags = MSG_ReadUInt16();\n\t\t\t\tunsigned short attacker\t= MSG_ReadUInt16();\n\t\t\t\tunsigned short targ = MSG_ReadUInt16();\n\t\t\t\tshort dmg = MSG_ReadShort();\n\t\t\t\tunsigned short issplash = !!(typeandflags&0x8000);\n\t\t\t\tunsigned short isteamdamage = (attacker==targ) || (cl.teamplay && attacker-1<countof(cl.players)&&targ-1<countof(cl.players)&&!strcmp(cl.players[attacker].team, cl.players[targ].team));\n\n\t\t\t\ttypeandflags &= ~0x8000;\n\n\t\t\t\t//let csqc handle it consistently with other ktx quirks.\n\t\t\t\tfor (cmd = 0; cmd < cl.splitclients; cmd++)\n\t\t\t\t\tif (CL_TryTrackNum(&cl.playerview[cmd]) == attacker)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (targ-1u < countof(cl.lerpplayers) && cl.lerpplayers[targ-1].sequence == cl.lerpentssequence)\n\t\t\t\t\t\t\tle = &cl.lerpplayers[targ-1];\t//favour use player indexes... stoopid qw.\n\t\t\t\t\t\telse if (targ < cl.maxlerpents && cl.lerpents[targ].sequence == cl.lerpentssequence)\n\t\t\t\t\t\t\tle = &cl.lerpents[targ];\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tbreak;\t//nope, can't place it... despite it being an mvd.\n\t\t\t\t\t\tCL_ParseStuffCmd(va(\"//ktx di %g %g %g %d %d %d %d\\n\", le->origin[0], le->origin[1], le->origin[2], typeandflags, dmg, issplash, isteamdamage), cmd);\n\t\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\n/*\t\tcase 0x0000://mvdhidden_antilag_position\n\t\tcase 0x0001://mvdhidden_usercmd\t\t\t\t\t\t// <byte: source playernum> <todo>\n\t\tcase 0x0002://mvdhidden_usercmd_weapons\t\t\t\t// <byte: source playernum> <int: items> <byte[4]: ammo> <byte: result> <byte*: weapon priority (nul terminated)>\n\t\tcase 0x0004://mvdhidden_commentary_track\n\t\tcase 0x0005://mvdhidden_commentary_data\n\t\tcase 0x0006://mvdhidden_commentary_text_segment\n\t\tcase 0x0008://mvdhidden_usercmd_weapons_ss\t\t\t// (same format as mvdhidden_usercmd_weapons)\n\t\tcase 0x0009://mvdhidden_usercmd_weapon_instruction\t// <byte: playernum> <byte: flags> <int: sequence#> <int: mode> <byte[10]: weaponlist>\n\t\tcase 0x000A://mvdhidden_paused_duration\n*/\n\t\tdefault:\n\t\t\tCon_ThrottlePrintf(&throttle, 1, \"CL_ParseHiddenDemoMessage: Unknown cmd %i\\n\", cmd);\n\t\t\tMSG_ReadSkip(size);\n\t\t\tbreak;\n\t\t}\n\t}\n}\n\n\n#define SHOWNETEOM(x) if(cl_shownet.value>=2)Con_Printf (\"%3i:%s\\n\", MSG_GetReadCount(), x);\n#define SHOWNET(x) if(cl_shownet.value>=2)Con_Printf (\"%3i:%s\\n\", MSG_GetReadCount()-1, x);\n#define SHOWNET2(x, y) if(cl_shownet.value>=2)Con_Printf (\"%3i:%3i:%s\\n\", MSG_GetReadCount()-1, y, x);\n/*\n=====================\nCL_ParseServerMessage\n=====================\n*/\nvoid CLQW_ParseServerMessage (void)\n{\n\tint\t\t\tcmd;\n\tchar\t\t*s;\n\tunsigned int u;\n\tint\t\t\ti, j;\n\tint\t\t\tdestsplit;\n\tvec3_t ang;\n\tfloat f;\n\tqboolean\tsuggestcsqcdebug = false;\n\tinframe_t\t*inf;\n\textern vec3_t demoangles;\n\tunsigned int cmdstart;\n\n\tcl.last_servermessage = realtime;\n\tCL_ClearProjectiles ();\n\n\t//clear out fixangles stuff\n\tinf = &cl.inframes[cls.netchan.incoming_sequence&UPDATE_MASK];\n\tfor (j = 0; j < MAX_SPLITS; j++)\n\t\tinf->packet_entities.fixangles[j] = false;\n\tif (cls.demoplayback == DPB_QUAKEWORLD)\n\t{\n\t\tinf->packet_entities.fixangles[0] = 2;\n\t\tVectorCopy(demoangles, inf->packet_entities.fixedangles[0]);\n\t}\n\telse if (cl.intermissionmode != IM_NONE)\n\t{\n\t\tfor (destsplit = 0; destsplit < cl.splitclients; destsplit++)\n\t\t{\n\t\t\tinf->packet_entities.fixangles[destsplit] = 2;\n\t\t\tVectorCopy(cl.playerview[destsplit].intermissionangles, inf->packet_entities.fixedangles[destsplit]);\n\t\t}\n\t}\n\n//\n// if recording demos, copy the message out\n\n\n\t//\n\tif (cl_shownet.value == 1)\n\t\tCon_Printf (\"%i \",net_message.cursize);\n\telse if (cl_shownet.value >= 2)\n\t\tCon_Printf (\"------------------\\n\");\n\n\n\tCL_ParseClientdata ();\n\n\t//vanilla QW has no timing info in the client and depends upon the client for all timing.\n\t//using the demo's timing for interpolation prevents unneccesary drift, and solves issues with demo seeking and other such things.\n\tif (cls.demoplayback == DPB_QUAKEWORLD && !(cls.fteprotocolextensions & PEXT_ACCURATETIMINGS))\n\t{\n\t\textern float demtime;\n\t\tif (cl.gametime != demtime)\n\t\t{\n\t\t\tcl.oldgametime = cl.gametime;\n\t\t\tcl.oldgametimemark = cl.gametimemark;\n\t\t\tcl.gametime = demtime;\n\t\t\tcl.gametimemark = realtime;\n\t\t}\n\t}\n\n\tif (realtime > packetusageflushtime)\n\t{\n\t\tmemcpy(packetusage_saved, packetusage_pending, sizeof(packetusage_saved));\n\t\tmemset(packetusage_pending, 0, sizeof(packetusage_pending));\n\t\tpacketusageflushtime = realtime + packetusage_interval;\n\t}\n\n//\n// parse the message\n//\n\twhile (1)\n\t{\n\t\tif (msg_badread)\n\t\t{\n\t\t\tCL_DumpPacket();\n\t\t\tHost_EndGame (\"CLQW_ParseServerMessage: Bad server message\");\n\t\t\tbreak;\n\t\t}\n\n\t\tcmdstart = MSG_GetReadCount();\n\t\tcmd = MSG_ReadByte ();\n\n\t\tif (cmd == svcfte_choosesplitclient)\n\t\t{\n\t\t\tSHOWNET2(svc_qwstrings[cmd], cmd);\n\n\t\t\tdestsplit = MSG_ReadByte() % MAX_SPLITS;\n\t\t\tcmd = MSG_ReadByte();\n\t\t}\n\t\telse\n\t\t\tdestsplit = cl.defaultnetsplit;\n\n\t\tif (cmd == -1)\n\t\t{\n\t\t\tSHOWNETEOM(\"END OF MESSAGE\");\n\t\t\tbreak;\n\t\t}\n\n\t\tSHOWNET2(svc_qwstrings[cmd], cmd);\n\n\t// other commands\n\t\tswitch (cmd)\n\t\t{\n\t\tdefault:\n\t\t\tCL_DumpPacket();\n\t\t\tHost_EndGame (\"CLQW_ParseServerMessage: Illegible server message (%i@%i)%s\", cmd, MSG_GetReadCount()-1, (!cl.csqcdebug && suggestcsqcdebug)?\"\\n'sv_csqcdebug 1' might aid in debugging this.\":\"\" );\n\t\t\treturn;\n\n\t\tcase svc_time:\n\t\t\tcl.oldgametime = cl.gametime;\n\t\t\tcl.gametime = MSG_ReadFloat();\n\t\t\tcl.gametimemark = realtime;\n\t\t\tbreak;\n\n\t\tcase svc_nop:\n//\t\t\tCon_Printf (\"svc_nop\\n\");\n\t\t\t//If we're using cl_shownet 2, combine large padding blocks into a single line, otherwise we'll get 1000+ lines from a single packet.\n\t\t\twhile (svc_nop == MSG_PeekByte())\n\t\t\t\tMSG_ReadByte();\n\t\t\tbreak;\n\n\t\tcase svc_disconnect:\n\t\t\tif (cls.demoplayback == DPB_MVD)\t//eztv fails to detect the end of demos.\n\t\t\t{\n\t\t\t\ts = MSG_ReadString();\n\t\t\t\tCon_Printf(CON_WARNING\"svc_disconnect: %s\\n\", s);\n\t\t\t}\n\t\t\telse if (cls.demoplayback)\n\t\t\t{\n\t\t\t\tCL_Disconnect(NULL);\n\t\t\t\tCL_NextDemo();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\telse if (cls.state == ca_connected)\n\t\t\t{\n\t\t\t\tHost_EndGame (\"Server disconnected\\n\");\n\t\t\t}\n\t\t\telse\n\t\t\t\tHost_EndGame (\"Server disconnected\");\n\t\t\tbreak;\n\n\t\tcase svc_print:\n\t\t\ti = MSG_ReadByte ();\n\t\t\ts = MSG_ReadString ();\n\t\t\tCL_ParsePrint(s, i);\n\t\t\tbreak;\n\n\t\tcase svc_centerprint:\n\t\t\ts = MSG_ReadString ();\n\n#ifdef PLUGINS\n\t\t\tif (Plug_CenterPrintMessage(s, destsplit))\n#endif\n\t\t\t\tSCR_CenterPrint (destsplit, s, false);\n\t\t\tbreak;\n\n\t\tcase svc_stufftext:\n\t\t\ts = MSG_ReadString ();\n\t\t\tCL_ParseStuffCmd(s, destsplit);\n\t\t\tbreak;\n\n\t\tcase svc_damage:\n\t\t\tV_ParseDamage (&cl.playerview[destsplit]);\n\t\t\tbreak;\n\n\t\tcase svc_serverdata:\n\t\t\tCbuf_Execute ();\t\t// make sure any stuffed commands are done\n \t\t\tCLQW_ParseServerData ();\n\t\t\tbreak;\n\t\tcase svcfte_splitscreenconfig:\n\t\t\tj = cl.splitclients;\n\t\t\tcl.splitclients = MSG_ReadByte();\n\t\t\tfor (i = 0; i < cl.splitclients && i < MAX_SPLITS; i++)\n\t\t\t{\n\t\t\t\tcl.playerview[i].playernum = MSG_ReadByte();\n\t\t\t\tcl.playerview[i].viewentity = cl.playerview[i].playernum+1;\n\t\t\t\tif (i>=j)\t//its new.\n\t\t\t\t\tcl.playerview[i].chatstate = 0;\n\t\t\t}\n\t\t\tif (i < cl.splitclients)\n\t\t\t{\n\t\t\t\tCon_Printf(\"Server sent us too many seats!\\n\");\n\t\t\t\tfor (; i < cl.splitclients; i++)\n\t\t\t\t{\t//svcfte_choosesplitclient has a modulo that is also broken, but at least there's no parse errors this way\n\t\t\t\t\tMSG_ReadByte();\n//\t\t\t\t\tCL_SendSeatClientCommand(true, i, drop\");\n\t\t\t\t}\n\t\t\t\tcl.splitclients = MAX_SPLITS;\n\t\t\t}\n\t\t\tbreak;\n#ifdef PEXT_SETVIEW\n\t\tcase svc_setview:\n\t\t\tif (!(cls.fteprotocolextensions & PEXT_SETVIEW))\n\t\t\t\tCon_Printf(\"^1PEXT_SETVIEW is meant to be disabled\\n\");\n\t\t\tcl.playerview[destsplit].viewentity=MSGCL_ReadEntity();\n\t\t\tbreak;\n#endif\n\t\tcase svcfte_setanglebase:\n\t\t\tCL_ParseBaseAngle(destsplit);\n\t\t\tbreak;\n\t\tcase svcfte_setangledelta:\n\t\t\tfor (i=0 ; i<3 ; i++)\n\t\t\t\tang[i] = cl.playerview[destsplit].viewangles[i] + MSG_ReadAngle16 ();\n\t\t\tif (!CSQC_Parse_SetAngles(destsplit, ang, true))\n\t\t\t\tVectorCopy (ang, cl.playerview[destsplit].viewangles);\n\t\t\tVectorCopy (cl.playerview[destsplit].viewangles, cl.playerview[destsplit].simangles);\n\t\t\tVectorCopy (cl.playerview[destsplit].viewangles, cl.playerview[destsplit].intermissionangles);\n\t\t\tbreak;\n\t\tcase svc_setangle:\n\t\t\tif (cls.demoplayback == DPB_MVD)\n\t\t\t{\n\t\t\t\t//I really don't get the point of fixangles in an mvd. just to disable interpolation for that frame?\n\t\t\t\tint pl = MSG_ReadByte();\n\t\t\t\tfor (i=0 ; i<3 ; i++)\n\t\t\t\t\tang[i] = MSG_ReadAngle();\n\t\t\t\tfor (j = 0; j < cl.splitclients; j++)\n\t\t\t\t{\n\t\t\t\t\tplayerview_t *pv = &cl.playerview[j];\n\t\t\t\t\tif (Cam_TrackNum(pv) == pl)\n\t\t\t\t\t{\n\t\t\t\t\t\tinf->packet_entities.fixangles[j] = true;\n\t\t\t\t\t\tVectorCopy(ang, inf->packet_entities.fixedangles[j]);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tinframe_t *inf = &cl.inframes[cls.netchan.incoming_sequence&UPDATE_MASK];\n\t\t\t\tint fixtype = 2;\n\t\t\t\tif (cls.ezprotocolextensions1 & EZPEXT1_SETANGLEREASON)\n\t\t\t\t\tfixtype = MSG_ReadByte();\t//0=unknown, 1=tele, 2=spawn\n\t\t\t\tfor (i=0 ; i<3 ; i++)\n\t\t\t\t\tang[i] = MSG_ReadAngle();\n\t\t\t\tif (fixtype == 1)\n\t\t\t\t{\t//relative\n\t\t\t\t\tVectorAdd(ang, cl.playerview[destsplit].viewangles, ang);\n\t\t\t\t\tVectorSubtract(ang, cl.outframes[cls.netchan.incoming_sequence&UPDATE_MASK].cmd->angles, ang);\n\t\t\t\t}\n\t\t\t\telse fixtype = 2;\t//snap\n\t\t\t\tif (!CSQC_Parse_SetAngles(destsplit, ang, fixtype==1))\n\t\t\t\t{\n\t\t\t\t\tinf->packet_entities.fixangles[destsplit] = true;\n\t\t\t\t\tVectorCopy (ang, cl.playerview[destsplit].viewangles);\n\t\t\t\t\tVectorCopy (ang, inf->packet_entities.fixedangles[destsplit]);\n\t\t\t\t}\n\t\t\t\tVectorCopy (cl.playerview[destsplit].viewangles, cl.playerview[destsplit].intermissionangles);\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase svc_lightstyle:\n\t\t\ti = MSG_ReadByte ();\n\t\t\tif (i >= MAX_NET_LIGHTSTYLES)\n\t\t\t\tHost_EndGame (\"svc_lightstyle > MAX_LIGHTSTYLES\");\n\t\t\ts = MSG_ReadString();\n\t\t\tif (cl_shownet.value == 3)\n\t\t\t\tCon_Printf(\"\\t%i=\\\"%s\\\"\\n\", i, s);\n\t\t\tR_UpdateLightStyle(i, s, 1, 1, 1);\n\t\t\tbreak;\n#ifdef PEXT_LIGHTSTYLECOL\n\t\tcase svcfte_lightstylecol:\n\t\t\tif (!(cls.fteprotocolextensions & PEXT_LIGHTSTYLECOL))\n\t\t\t\tHost_EndGame(\"PEXT_LIGHTSTYLECOL is meant to be disabled\\n\");\n\t\t\t{\n\t\t\t\tint bits;\n\t\t\t\tvec3_t rgb;\n\t\t\t\ti = MSG_ReadByte ();\n\t\t\t\tbits = MSG_ReadByte();\n\t\t\t\tif (bits & 0x40)\n\t\t\t\t\ti |= MSG_ReadByte()<<8;\t//high bits of style index.\n\t\t\t\tif (bits & 0x80)\n\t\t\t\t{\n\t\t\t\t\trgb[0] = (bits&1)?MSG_ReadShort()/1024.0:0;\n\t\t\t\t\trgb[1] = (bits&2)?MSG_ReadShort()/1024.0:0;\n\t\t\t\t\trgb[2] = (bits&4)?MSG_ReadShort()/1024.0:0;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\trgb[0] = (bits&1)?1:0;\n\t\t\t\t\trgb[1] = (bits&2)?1:0;\n\t\t\t\t\trgb[2] = (bits&4)?1:0;\n\t\t\t\t}\n\n\t\t\t\tif (i >= MAX_NET_LIGHTSTYLES)\n\t\t\t\t\tHost_EndGame (\"svc_lightstyle > MAX_LIGHTSTYLES\");\n\t\t\t\tR_UpdateLightStyle(i, MSG_ReadString(), rgb[0], rgb[1], rgb[2]);\n\t\t\t}\n\t\t\tbreak;\n#endif\n\n\t\tcase svc_sound:\n\t\t\tCLQW_ParseStartSoundPacket();\n\t\t\tbreak;\n#ifdef PEXT_SOUNDDBL\n\t\tcase svcfte_soundextended:\n\t\t\tCLNQ_ParseStartSoundPacket();\n\t\t\tbreak;\n#endif\n\n\t\tcase svc_stopsound:\n\t\t\ti = MSG_ReadShort();\n\t\t\tS_StopSound(i>>3, i&7);\n\t\t\tbreak;\n\n#ifdef PEXT2_VOICECHAT\n\t\tcase svcfte_voicechat:\n\t\t\tS_Voip_Parse();\n\t\t\tbreak;\n#endif\n\n#ifdef TERRAIN\n\t\tcase svcfte_brushedit:\n\t\t\tCL_Parse_BrushEdit();\n\t\t\tbreak;\n#endif\n\n\t\tcase svc_updatefrags:\n\t\t\tSbar_Changed ();\n\t\t\tu = MSG_ReadPlayer();\n\t\t\tif (u >= MAX_CLIENTS)\n\t\t\t\tHost_EndGame (\"CL_ParseServerMessage: svc_updatefrags > MAX_SCOREBOARD\");\n\t\t\tcl.players[u].frags = MSG_ReadShort ();\n\t\t\tbreak;\n\n\t\tcase svc_updateping:\n\t\t\tu = MSG_ReadPlayer();\n\t\t\tif (u >= MAX_CLIENTS)\n\t\t\t\tHost_EndGame (\"CL_ParseServerMessage: svc_updateping > MAX_SCOREBOARD\");\n\t\t\tcl.players[u].ping = MSG_ReadShort ();\n\t\t\tbreak;\n\n\t\tcase svc_updatepl:\n\t\t\tu = MSG_ReadPlayer();\n\t\t\tif (u >= MAX_CLIENTS)\n\t\t\t\tHost_EndGame (\"CL_ParseServerMessage: svc_updatepl > MAX_SCOREBOARD\");\n\t\t\tcl.players[u].pl = MSG_ReadByte ();\n\t\t\tbreak;\n\n\t\tcase svc_updateentertime:\n\t\t// time is sent over as seconds ago\n\t\t\tu = MSG_ReadPlayer();\n\t\t\tif (u >= MAX_CLIENTS)\n\t\t\t\tHost_EndGame (\"CL_ParseServerMessage: svc_updateentertime > MAX_SCOREBOARD\");\n\t\t\tcl.players[u].realentertime = realtime - MSG_ReadFloat ();\n\t\t\tbreak;\n\n\t\tcase svc_spawnbaseline:\n\t\t\ti = MSGCL_ReadEntity ();\n\t\t\tif (!CL_CheckBaselines(i))\n\t\t\t\tHost_EndGame(\"CL_ParseServerMessage: svc_spawnbaseline failed with size %i\", i);\n\t\t\tCL_ParseBaseline (cl_baselines + i, CPNQ_ID);\n\t\t\tbreak;\n\t\tcase svcfte_spawnbaseline2:\n\t\t\tCL_ParseBaselineDelta ();\n\t\t\tbreak;\n\t\tcase svc_spawnstatic:\n\t\t\tCL_ParseStaticProt (CPNQ_ID);\n\t\t\tbreak;\n\t\tcase svcfte_spawnstatic2:\n\t\t\tCL_ParseStaticProt (-1);\n\t\t\tbreak;\n\t\tcase svc_temp_entity:\n#ifdef NQPROT\n\t\t\tCL_ParseTEnt (false);\n#else\n\t\t\tCL_ParseTEnt ();\n#endif\n\t\t\tbreak;\n\t\tcase svcfte_temp_entity_sized:\n\t\t\tCL_ParseTEnt_Sized();\n\t\t\tbreak;\n\t\tcase svcfte_customtempent:\n\t\t\tCL_ParseCustomTEnt();\n\t\t\tbreak;\n\n\t\tcase svc_particle:\n\t\t\tCLNQ_ParseParticleEffect ();\n\t\t\tbreak;\n\t\tcase svcfte_particle2:\n\t\t\tCL_ParseParticleEffect2 ();\n\t\t\tbreak;\n\t\tcase svcfte_particle3:\n\t\t\tCL_ParseParticleEffect3 ();\n\t\t\tbreak;\n\t\tcase svcfte_particle4:\n\t\t\tCL_ParseParticleEffect4 ();\n\t\t\tbreak;\n\n\t\tcase svc_killedmonster:\n\t\t\t//fixme: update all player stats\n#ifdef QUAKESTATS\n\t\t\tcl.playerview[destsplit].stats[STAT_MONSTERS]++;\n\t\t\tcl.playerview[destsplit].statsf[STAT_MONSTERS]++;\n#endif\n\t\t\tbreak;\n\t\tcase svc_foundsecret:\n\t\t\t//fixme: update all player stats\n#ifdef QUAKESTATS\n\t\t\tcl.playerview[destsplit].stats[STAT_SECRETS]++;\n\t\t\tcl.playerview[destsplit].statsf[STAT_SECRETS]++;\n#endif\n\t\t\tbreak;\n\n\t\tcase svcqw_updatestatbyte:\n\t\t\ti = MSG_ReadByte ();\n\t\t\tj = MSG_ReadByte ();\n\t\t\tCL_SetStatNumeric(destsplit, i, j, j);\n\t\t\tbreak;\n\t\tcase svcqw_updatestatlong:\n\t\t\ti = MSG_ReadByte ();\n\t\t\tj = MSG_ReadLong ();\t//make qbyte if nq compatability?\n\t\t\tCL_SetStatNumeric (destsplit, i, j, j);\n\t\t\tbreak;\n\n\t\tcase svcfte_updatestatstring:\n\t\t\ti = MSG_ReadByte();\n\t\t\ts = MSG_ReadString();\n\t\t\tCL_SetStatString (destsplit, i, s);\n\t\t\tbreak;\n\t\tcase svcfte_updatestatfloat:\n\t\t\ti = MSG_ReadByte();\n\t\t\tf = MSG_ReadFloat();\n\t\t\tCL_SetStatNumeric (destsplit, i, f, f);\n\t\t\tbreak;\n/*\t\tcase svcfte_updatebigstat:\n\t\t\tCL_ParseExtendedStat();\n\t\t\tbreak;*/\n\n\t\tcase svc_spawnstaticsound:\n\t\t\tCL_ParseStaticSound (false);\n\t\t\tbreak;\n\t\tcase svcfte_spawnstaticsound2:\n\t\t\tCL_ParseStaticSound (MSG_ReadByte());\n\t\t\tbreak;\n\n\t\tcase svc_cdtrack:\n\t\t\t{\n\t\t\t\t//quakeworld got a crippled svc_cdtrack.\n\t\t\t\tunsigned int firsttrack;\n\t\t\t\tfirsttrack = MSG_ReadByte ();\n\t\t\t\tMedia_NumberedTrack (firsttrack, firsttrack);\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase svc_intermission:\n\t\t\tif (cl.intermissionmode == IM_NONE)\n\t\t\t{\n\t\t\t\tTP_ExecTrigger (\"f_mapend\", false);\n\t\t\t\tif (cl.playerview[destsplit].spectator || cls.demoplayback)\n\t\t\t\t\tTP_ExecTrigger (\"f_specmapend\", true);\n\t\t\t\telse\n\t\t\t\t\tTP_ExecTrigger (\"f_playmapend\", true);\n\t\t\t\tcl.completed_time = cl.gametime;\n\t\t\t}\n\t\t\tcl.intermissionmode = IM_QWSCORES;\n\t\t\tfor (i=0 ; i<3 ; i++)\n\t\t\t\tcl.playerview[destsplit].simorg[i] = MSG_ReadCoord ();\n\t\t\tfor (i=0 ; i<3 ; i++)\n\t\t\t\tcl.playerview[destsplit].intermissionangles[i] = MSG_ReadAngle ();\n\n\t\t\tif (cls.demoseeking == DEMOSEEK_INTERMISSION)\n\t\t\t{\n\t\t\t\tcls.demoseeking = DEMOSEEK_NOT; //reached it.\n\t\t\t\t//FIXME: pause it.\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase svc_finale:\n\t\t\tif (cl.intermissionmode == IM_NONE)\n\t\t\t{\n\t\t\t\tfor (i = 0; i < MAX_SPLITS; i++)\n\t\t\t\t\tcl.playerview[i].simorg[2] += cl.playerview[i].viewheight;\n\t\t\t\tVectorCopy (cl.playerview[destsplit].simangles, cl.playerview[destsplit].intermissionangles);\n\t\t\t\tcl.completed_time = cl.gametime;\n\t\t\t}\n\t\t\tcl.intermissionmode = IM_NQFINALE;\n\t\t\tSCR_CenterPrint (destsplit, TL_Translate(com_language, MSG_ReadString ()), false);\n\t\t\tbreak;\n\n\t\tcase svc_sellscreen:\n\t\t\tCmd_ExecuteString (\"help\", RESTRICT_SERVER);\n\t\t\tbreak;\n\n\t\tcase svc_smallkick:\n\t\t\tcl.playerview[destsplit].punchangle_cl = -2;\n\t\t\tbreak;\n\t\tcase svc_bigkick:\n\t\t\tcl.playerview[destsplit].punchangle_cl = -4;\n\t\t\tbreak;\n\n\t\tcase svc_muzzleflash:\n\t\t\tCL_MuzzleFlash (MSGCL_ReadEntity());\n\t\t\tbreak;\n\n\t\tcase svc_updateuserinfo:\n\t\t\tCL_UpdateUserinfo ();\n\t\t\tbreak;\n\n\t\tcase svc_setinfo:\n\t\t\tCL_ParseSetInfo ();\n\t\t\tbreak;\n\t\tcase svc_serverinfo:\n\t\t\tCL_ServerInfo ();\n\t\t\tbreak;\n\t\tcase svcfte_setinfoblob:\n\t\t\tCL_ParseSetInfoBlob();\n\t\t\tbreak;\n\n\t\tcase svc_download:\n\t\t\tCL_ParseDownload (false);\n\t\t\tbreak;\n\n\t\tcase svc_playerinfo:\n\t\t\tCLQW_ParsePlayerinfo ();\n\t\t\tbreak;\n\n\t\tcase svc_nails:\n\t\t\tCL_ParseProjectiles (cl_spikeindex, false);\n\t\t\tbreak;\n\t\tcase svc_nails2:\n\t\t\tCL_ParseProjectiles (cl_spikeindex, true);\n\t\t\tbreak;\n\n\t\tcase svc_chokecount:\t\t// some preceding packets were choked\n\t\t\ti = MSG_ReadByte ();\n\t\t\tfor (j=0 ; j<i ; j++)\n\t\t\t\tcl.outframes[(cls.netchan.incoming_acknowledged-1-j)&UPDATE_MASK].latency = -2;\n\t\t\tbreak;\n\n\t\tcase svc_modellist:\n\t\t\tCL_ParseModellist (false);\n\t\t\tbreak;\n\t\tcase svcfte_modellistshort:\n\t\t\tCL_ParseModellist (true);\n\t\t\tbreak;\n\n\t\tcase svc_soundlist:\n\t\t\tCL_ParseSoundlist (false);\n\t\t\tbreak;\n#ifdef PEXT_SOUNDDBL\n\t\tcase svcfte_soundlistshort:\n\t\t\tCL_ParseSoundlist (true);\n\t\t\tbreak;\n#endif\n\n\t\tcase svc_packetentities:\n\t\t\tCLQW_ParsePacketEntities (false);\n\t\t\tbreak;\n\n\t\tcase svc_deltapacketentities:\n\t\t\tCLQW_ParsePacketEntities (true);\n\t\t\tbreak;\n\t\tcase svcfte_updateentities:\n\t\t\tCLFTE_ParseEntities();\n\t\t\tbreak;\n\n\t\tcase svc_maxspeed:\n\t\t\tcl.playerview[destsplit].maxspeed = MSG_ReadFloat();\n\t\t\tbreak;\n\n\t\tcase svc_entgravity:\n\t\t\tcl.playerview[destsplit].entgravity = MSG_ReadFloat();\n\t\t\tbreak;\n\n\t\tcase svc_setpause:\n\t\t\tcl.paused = MSG_ReadByte ();\n//\t\t\tMedia_SetPauseTrack(!!cl.paused);\n\t\t\tbreak;\n\n//\t\tcase svc_ftesetclientpersist:\n//\t\t\tCL_ParseClientPersist();\n//\t\t\tbreak;\n\t\tcase svc_setportalstate:\n\t\t\tCL_ParsePortalState();\n\t\t\tbreak;\n\n\t\tcase svcfte_showpic:\n\t\t\tSCR_ShowPic_Create();\n\t\t\tbreak;\n\t\tcase svcfte_hidepic:\n\t\t\tSCR_ShowPic_Hide();\n\t\t\tbreak;\n\t\tcase svcfte_movepic:\n\t\t\tSCR_ShowPic_Move();\n\t\t\tbreak;\n\t\tcase svcfte_updatepic:\n\t\t\tSCR_ShowPic_Update();\n\t\t\tbreak;\n\n\t\tcase svcfte_effect:\n\t\t\tCL_ParseEffect(false);\n\t\t\tbreak;\n\t\tcase svcfte_effect2:\n\t\t\tCL_ParseEffect(true);\n\t\t\tbreak;\n\n#ifdef CSQC_DAT\n\t\tcase svcfte_csqcentities:\n\t\t\tsuggestcsqcdebug = true;\n\t\t\tCSQC_ParseEntities(cl.csqcdebug);\n\t\t\tbreak;\n\t\tcase svcfte_csqcentities_sized:\n\t\t\tCSQC_ParseEntities(true);\n\t\t\tbreak;\n#endif\n\t\tcase svcfte_precache:\n\t\t\tCL_ParsePrecache();\n\t\t\tbreak;\n\n\t\tcase svcfte_trailparticles:\n\t\t\tCL_ParseTrailParticles();\n\t\t\tbreak;\n\t\tcase svcfte_pointparticles:\n\t\t\tCL_ParsePointParticles(false);\n\t\t\tbreak;\n\t\tcase svcfte_pointparticles1:\n\t\t\tCL_ParsePointParticles(true);\n\t\t\tbreak;\n\n\t\tcase svcfte_cgamepacket_sized:\n#ifdef CSQC_DAT\n\t\t\tif (CSQC_ParseGamePacket(destsplit, true))\n\t\t\t\tbreak;\n#endif\n\t\t\tCon_Printf(\"Unable to parse gamecode packet\\n\");\n\t\t\tbreak;\n\t\tcase svcfte_cgamepacket:\n\t\t\tsuggestcsqcdebug = true;\n#ifdef HLCLIENT\n\t\t\tif (CLHL_ParseGamePacket())\n\t\t\t\tbreak;\n#endif\n#ifdef CSQC_DAT\n\t\t\tif (CSQC_ParseGamePacket(destsplit, cl.csqcdebug))\n\t\t\t\tbreak;\n#endif\n\t\t\tCon_Printf(\"Unable to parse gamecode packet\\n\");\n\t\t\tbreak;\n\t\t}\n\n\t\tpacketusage_pending[cmd] += MSG_GetReadCount()-cmdstart;\n\t}\n}\n\n#ifdef Q2CLIENT\nstatic void CLQ2_ParseZPacket(void)\n{\n#ifndef AVAIL_ZLIB\n\tHost_EndGame (\"CLQ2_ParseZPacket: zlib not supported in this build\");\n#else\n\tz_stream s;\n\tchar *indata, *outdata;\t//we're hacking stuff onto the end of the current buffer, to avoid issues if something errors out and doesn't leave net_message in a clean state\n\tunsigned short clen = MSG_ReadShort();\n\tunsigned short ulen = MSG_ReadShort();\n\tsizebuf_t restoremsg;\n\tif (clen > net_message.cursize-MSG_GetReadCount())\n\t\tHost_EndGame (\"CLQ2_ParseZPacket: svcr1q2_zpacket truncated\");\n\tif (ulen > net_message.maxsize-net_message.cursize)\n\t\tHost_EndGame (\"CLQ2_ParseZPacket: svcr1q2_zpacket overflow\");\n\tindata = net_message.data + MSG_GetReadCount();\n\toutdata = net_message.data + net_message.cursize;\n\tMSG_ReadSkip(clen);\n\trestoremsg = net_message;\n\tnet_message.currentbit = net_message.cursize<<3;\n\tnet_message.cursize += ulen;\n\n\tmemset(&s, 0, sizeof(s));\n\ts.next_in = indata;\n\ts.avail_in = clen;\n\ts.total_in = 0;\n\ts.next_out = outdata;\n\ts.avail_out = ulen;\n\ts.total_out = 0;\n\tif (inflateInit2(&s, -15) != Z_OK)\n\t\tHost_EndGame (\"CLQ2_ParseZPacket: unable to initialise zlib\");\n\tif (inflate(&s, Z_FINISH) != Z_STREAM_END)\n\t\tHost_EndGame (\"CLQ2_ParseZPacket: stream truncated\");\n\tif (inflateEnd(&s) != Z_OK)\n\t\tHost_EndGame (\"CLQ2_ParseZPacket: stream truncated\");\n\tif (s.total_out != ulen || s.total_in != clen)\n\t\tHost_EndGame (\"CLQ2_ParseZPacket: stream truncated\");\n\n\tCLQ2_ParseServerMessage();\n\tnet_message = restoremsg;\n\tmsg_badread = false;\n#endif\n}\nstatic void CLR1Q2_ParseSetting(void)\n{\n\tint setting = MSG_ReadLong();\n\tint value = MSG_ReadLong();\n\n\tif (setting == R1Q2_SVSET_FPS)\n\t{\n\t\tcl.q2svnetrate = value;\n\t\tif (cl.validsequence)\n\t\t\tCon_Printf(\"warning: fps rate changed mid-game\\n\");\t//fixme: we need to clean up lerping stuff. if its now lower, we might have a whole load of things waiting ages for a timeout.\n\t}\n}\nstatic void CLQ2EX_ParseFog(void)\n{\n\tfloat d=0;\n\tqbyte r=255, g=255, b=255;\n\tunsigned short t=0;\n\tunsigned int flags = MSG_ReadByte();\n\tif (flags & (1<<7))\n\t\tflags |= MSG_ReadByte()<<8;\n\n\tif (flags & (1<<0))\t\td = MSG_ReadFloat();\t//density\n\tif (flags & (1<<0))\t\tMSG_ReadByte();\t//density\n\tif (flags & (1<<1))\t\tr = MSG_ReadByte();\t\t//r\n\tif (flags & (1<<2))\t\tg = MSG_ReadByte();\t\t//g\n\tif (flags & (1<<3))\t\tb = MSG_ReadByte();\t\t//b\n\tif (flags & (1<<4))\t\tt = MSG_ReadUInt16();\t//time\n\n\tif (flags & (1<<5))\t\tMSG_ReadFloat();\t//h falloff\n\tif (flags & (1<<6))\t\tMSG_ReadFloat();\t//h density\n\tif (flags & (1<<8))\t\tMSG_ReadByte();\t\t//h s r\n\tif (flags & (1<<9))\t\tMSG_ReadByte();\t\t//h s g\n\tif (flags & (1<<10))\tMSG_ReadByte();\t\t//h s b\n\tif (flags & (1<<11))\tMSG_ReadLong();\t\t//h s dist\n\tif (flags & (1<<12))\tMSG_ReadByte();\t\t//h e r\n\tif (flags & (1<<13))\tMSG_ReadByte();\t\t//h e g\n\tif (flags & (1<<14))\tMSG_ReadByte();\t\t//h e b\n\tif (flags & (1<<15))\tMSG_ReadLong();\t\t//h e dist\n\n\tCL_ResetFog(FOGTYPE_AIR);\n\tcl.fog[FOGTYPE_AIR].density = d;\n\tcl.fog[FOGTYPE_AIR].colour[0] = SRGBf(r/255.0f);\n\tcl.fog[FOGTYPE_AIR].colour[1] = SRGBf(g/255.0f);\n\tcl.fog[FOGTYPE_AIR].colour[2] = SRGBf(b/255.0f);\n\tcl.fog[FOGTYPE_AIR].time += (t) / 100.0;\n\tcl.fog_locked = !!cl.fog[FOGTYPE_AIR].density;\n}\nstatic char *CLQ2EX_ReformatBinds(int seat, const char *source, char *buffer, size_t buffersize)\n{\t//q2e has various \"%bind:cmd:$desc%\" tags that should be expanded to some \"<BUTTON> Desc\\n\" string instead, for tutorialy areas.\n\t//we're too dumb to know about any artwork for each key.\n\t//note that multiple can exist in any centerprint.\n\tchar *s = buffer;\n\tif (s != source)\n\t\tQ_strncpyz(buffer, source, buffersize);\n\twhile (!strncmp(s, \"%bind:\", 6))\n\t{\n\t\tint keys[1], keymods[countof(keys)];\n\t\tchar *cmd = s+6;\n\t\tchar *desc = strchr(cmd, ':');\n\t\tchar *e;\n\t\tconst char *rep, *key;\n\t\tif (!desc)\n\t\t\tbreak;\n\t\te = strchr(desc, '%');\n\t\tif (!e)\n\t\t\tbreak;\n\t\t*desc++ = 0;\n\t\t*e++ = 0;\n\t\trep = TL_Translate(com_language, desc);\t//sigh\n\n\t\tif (M_FindKeysForCommand(0, seat, cmd, keys, keymods, countof(keys)) > 0)\n\t\t\tkey = Key_KeynumToLocalString (keys[0], keymods[0]);\n\t\telse\n\t\t\tkey = \"UNBOUND\";\n\n\t\trep = va(\"<%s> %s\\n\", key, rep);\n\n\t\tif (s+strlen(rep) >= buffer+buffersize)\n\t\t\trep = \"\";\t//would overflow, just strip it out instead.\n\t\tmemmove(s+strlen(rep), e, strlen(e)+1);\n\t\tmemcpy(s, rep, strlen(rep));\n\t\ts += strlen(rep);\n\t}\n\treturn buffer;\n}\nstatic void CLQ2EX_LocPrint(int targetseat)\n{\n\tint seat;\n\tqbyte flags = MSG_ReadByte();\n\tqbyte level = flags&7;\n\tqboolean broadcast = !!(flags & (1<<3));\t//meaningful clientside for splitscreen, at least with centerprints.\n\tqboolean nonotify = flags & (1<<4);\n\tunsigned short count, a;\n\tconst char *arg[256];\n\tstatic char formatted[8192];\n\tchar inputs[8192];\n\tsize_t ofs = 0;\n\tchar *s;\n\targ[0] = MSG_ReadString();\n\tcount = MSG_ReadByte();\n\tfor (a = 1; a <= count && a < countof(arg); )\n\t{\n\t\targ[a] = MSG_ReadStringBuffer(inputs+ofs, sizeof(inputs)-ofs-1);\n\t\tif (!strncmp(arg[a], \"##P\", 3))\n\t\t{\n\t\t\tint p = atoi(arg[a]+3);\n\t\t\tif (p < cl.allocated_client_slots)\n\t\t\t{\n\t\t\t\tinputs[ofs] = 0;\n\t\t\t\targ[a] = cl.players[p].name;\t//FIXME: do colours here instead of CL_ParsePrint?\n\t\t\t}\n\t\t}\n\t\ta++;\n\t\tofs += strlen(inputs+ofs)+1;\n\t\tif (ofs >= sizeof(inputs))\n\t\t\tbreak;\n\t}\n\tfor (; a <= count; a++)\n\t\tMSG_ReadString(); //don't lose space, though we can't buffer it.\n\n\tTL_Reformat(com_language, formatted, sizeof(formatted), a, arg);\n\n\tif (level == 4)\n\t{\t//'typewriter'...\n\t\tfor (seat = (broadcast?0:targetseat); (broadcast?(seat < cl.splitclients):(seat==targetseat)); seat++)\n\t\t{\n\t\t\ts = va(\"/Q/W%s/.%s\",nonotify?\"/N0\":\"\", CLQ2EX_ReformatBinds(seat, formatted, inputs,sizeof(inputs)));\n\t\t\tSCR_CenterPrint(seat, s, false);\n\t\t}\n\t\treturn;\n\t}\n\telse if (level == 5)\n\t{\t//centerprint\n\t\tfor (seat = (broadcast?0:targetseat); (broadcast?(seat < cl.splitclients):(seat==targetseat)); seat++)\n\t\t{\n\t\t\ts = va(\"%s/.%s\",nonotify?\"/N0\":\"\", CLQ2EX_ReformatBinds(seat, formatted, inputs,sizeof(inputs)));\n\t\t\tSCR_CenterPrint(seat, s, false);\n\t\t}\n\t\treturn;\n\t}\n\telse if (level >= 6)\n\t\tlevel = 3;\t//TTS\n\tCL_ParsePrint(formatted, level);\n}\nstatic void CLQ2EX_ParseDamage(int seat)\n{\t//not really sure what this is useful for, when there's already damage blend info in the playerstate messages.\n\tvec3_t dir;\n\tqbyte count = MSG_ReadByte();\n\twhile (count --> 0)\n\t{\n\t\t/*bits =*/ MSG_ReadByte();\n\t\t//dam = (bits&0x1f)*3;\n\t\t//health= !!(bits&0x20);\n\t\t//armour= !!(bits&0x40);\n\t\t//shield= !!(bits&0x80);\n\t\tMSG_ReadDir(dir);\n\t}\n}\nextern sizebuf_t\t*msg_readmsg;\n#ifdef AVAIL_ZLIB\nsize_t ZLib_DecompressBuffer(qbyte *in, size_t insize, qbyte *out, size_t maxoutsize);\n#endif\nstatic void CLQ2EX_ParseBlast(void(*func)(void))\n{\n#ifdef AVAIL_ZLIB\n\tsizebuf_t tmp, *prevmsg = msg_readmsg;\n\tunsigned int csize = MSG_ReadUInt16();\n\tunsigned int usize = MSG_ReadUInt16();\n\tvoid *cdata = msg_readmsg->data + (msg_readmsg->currentbit>>3);\n\tMSG_ReadSkip(csize);\n\tif (msg_badread)\n\t\treturn;\t//erk?!?\n\n\t//create a new uncompressed buffer...\n\tMSG_BeginWriting (&tmp, msg_readmsg->prim, alloca(usize), usize);\n\tif (ZLib_DecompressBuffer(cdata, csize, SZ_GetSpace (&tmp, usize), usize) != usize)\n\t{\n\t\tmsg_badread = true;\n\t\treturn;\t//wut?\n\t}\n\n\t//and read it...\n\tMSG_BeginReading(&tmp, msg_readmsg->prim);\n\twhile(tmp.currentbit>>3 < msg_readmsg->cursize)\n\t\tfunc();\n\n\t//before returning to our original buffer.\n\tmsg_readmsg = prevmsg;\n#else\n\tmsg_badread = true;\n#endif\n}\nstatic void CLQ2EX_ParseRouteMarker(void)\n{\n\tqboolean isfirst;\n\tvec3_t pos;\n\tvec3_t dir;\n\tfloat timeout = 10;\n\tfloat radius = 16;\n\n\tstruct itemtimer_s *timer;\n\tfloat start = cl.time;\n\n\tisfirst = MSG_ReadByte();\n\tMSG_ReadPos(pos);\n\tMSG_ReadDir(dir);\n//\tpos[2]-=24;\n\n\tif (isfirst)\n\t\tradius*=2;\n\n\ttimer = Z_Malloc(sizeof(*timer));\n\ttimer->next = cl.itemtimers;\n\tcl.itemtimers = timer;\n\n\tVectorCopy(pos, timer->origin);\n\ttimer->start = cl.time;\n\ttimer->duration = timeout;\n\ttimer->radius = radius;\n\ttimer->duration = timeout;\n\ttimer->entnum = 0;\n\ttimer->start = start;\n\ttimer->end = start + timer->duration;\n\ttimer->rgb[0] = 0.15;\n\ttimer->rgb[1] = 1;\n\ttimer->rgb[2] = 0.1;\n}\nstatic void CLQ2EX_ParseLevelRestart(void)\n{\t//fast restart... allowing us to skip (full)configstrings and baselines\n\tint id;\n\tchar *str;\n\twhile (!msg_badread)\n\t{\n\t\tid = MSG_ReadShort();\n\t\tif (id==-1)\n\t\t\tbreak;\t//end of our svc.\n\t\tstr = MSG_ReadString();\n\t\tCLQ2_UpdateConfigString(id, str);\n\t}\n\n\t//clear out some stuff that should not be lingering...\n\tfor (id = 0; id < cl.splitclients; id++)\n\t\tSCR_CenterPrint(id, NULL, true);\n}\n\nvoid CL_WriteDemoMessage (sizebuf_t *msg, int payloadoffset);\nstatic const char *q2svcnames[] = {\n\t\"svcq2_bad\",\t\t\t\t//0\n\n\t// these ops are known to the game dll\n\t\"svcq2_muzzleflash\",\t\t//1\n\t\"svcq2_muzzleflash2\",\t\t//2\n\t\"svcq2_temp_entity\",\t\t//3\n\t\"svcq2_layout\",\t\t\t\t//4\n\t\"svcq2_inventory\",\t\t\t//5\n\n\t// the rest are private to the client and server\n\t\"q2_nop\",\t\t\t\t\t//6\n\t\"q2_disconnect\",\t\t\t//7\n\t\"q2_reconnect\",\t\t\t\t//8\n\t\"q2_sound\",\t\t\t\t\t//9\n\t\"q2_print\",\t\t\t\t\t//10\n\t\"q2_stufftext\",\t\t\t\t//11\n\t\"q2_serverdata\",\t\t\t//12\n\t\"q2_configstring\",\t\t\t//13\n\t\"q2_spawnbaseline\",\t\t\t//14\n\t\"q2_centerprint\",\t\t\t//15\n\t\"q2_download\",\t\t\t\t//16\n\t\"q2_playerinfo\",\t\t\t//17\n\t\"q2_packetentities\",\t\t//18\n\t\"q2_deltapacketentities\",\t//19\n\t\"q2_frame\",\t\t\t\t\t//20\n\n\t//extended tat.\n\t\"q2ex_splitclient/r1q2_zpacket\",\t//21\n\t\"q2ex_configblast/r1q2_zdownload\",\t//22\n\t\"q2ex_spawnbaselineblast/r1q2_playerupdate/q2pro_gamestate\",\t//23\n\t\"q2ex_Levelrestart/r1q2_setting/q2pro_setting\",\t//24\n\t\"q2ex_danage\",\t\t\t\t//25\n\t\"q2ex_locprint\",\t\t\t//26\n\t\"q2ex_fog\",\t\t\t\t\t//27\n\t\"q2ex_waiting\",\t\t\t\t//28\n\t\"q2ex_botchat\",\t\t\t\t//29\n\t\"q2ex_mapmarker\",\t\t\t//30\n\t\"q2ex_routemarker\",\t\t\t//31\n\t\"q2ex_achievement\",\t\t\t//32\n};\nvoid CLQ2_ParseServerMessage (void)\n{\n\tint\t\t\t\tcmd, last=-1;\n\tconst char\t\t*s;\n\tint\t\t\t\ti;\n\tunsigned int\tseat;\n//\tint\t\t\t\tj;\n\tint startpos = MSG_GetReadCount();\n\n\tcl.last_servermessage = realtime;\n\tCL_ClearProjectiles ();\n\n//\n// if recording demos, copy the message out\n//\n\tif (cl_shownet.value == 1)\n\t\tCon_Printf (\"%i \",net_message.cursize);\n\telse if (cl_shownet.value >= 2)\n\t\tCon_Printf (\"------------------\\n\");\n\n\n\tCL_ParseClientdata ();\n\n//\n// parse the message\n//\n\twhile (1)\n\t{\n\t\tif (msg_badread)\n\t\t{\n\t\t\tif (cl_shownet.ival)\n\t\t\t\tCL_DumpPacket();\n\t\t\tHost_EndGame (\"CLQ2_ParseServerMessage: Bad server message (last %i)\", last);\n\t\t\tbreak;\n\t\t}\n\n\t\tcmd = MSG_ReadByte ();\n\n\t\tseat = 0;\n\t\tif (cmd == svcq2_playerinfo && (cls.fteprotocolextensions & PEXT_SPLITSCREEN))\n\t\t{\t//playerinfo should not normally be seen here.\n\t\t\t//so we can just 'borrow' it for seat numbers for targetted svcs.\n\t\t\tSHOWNET(va(\"%i\", cmd));\n\t\t\tseat = MSG_ReadByte ();\nseatedcommand:\n\t\t\tif (seat >= MAX_SPLITS)\n\t\t\t\tHost_EndGame (\"CLQ2_ParseServerMessage: Unsupported seat (%i)\", seat);\n\t\t\tcmd = MSG_ReadByte ();\n\t\t}\n\n\t\tif (cmd == -1)\n\t\t{\n\t\t\tSHOWNETEOM(\"END OF MESSAGE\");\n\t\t\tbreak;\n\t\t}\n\n\t\tSHOWNET(cmd>=countof(q2svcnames)?va(\"%i\", cmd):q2svcnames[cmd]);\n\n\t// other commands\n\t\tswitch (cmd)\n\t\t{\n\t\tdefault:\nisillegible:\n\t\t\tif (cls.protocol_q2 == PROTOCOL_VERSION_R1Q2 || cls.protocol_q2 == PROTOCOL_VERSION_Q2PRO)\n\t\t\t{\n\t\t\t\tswitch(cmd & 0x1f)\n\t\t\t\t{\n\t\t\t\tcase svcq2_frame:\t\t\t//20 (the bastard to implement.)\n\t\t\t\t\tCLQ2_ParseFrame(cmd>>5);\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tif (cl_shownet.ival)\n\t\t\t\t\t\tCL_DumpPacket();\n\t\t\t\t\tHost_EndGame (\"CLQ2_ParseServerMessage: Illegible server message (%i, last %i)\", cmd, last);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (cl_shownet.ival)\n\t\t\t\tCL_DumpPacket();\n\t\t\tHost_EndGame (\"CLQ2_ParseServerMessage: Illegible server message (%i, last %i)\", cmd, last);\n\t\t\treturn;\n\n\t//known to game\n\t\tcase svcq2_muzzleflash:\n\t\t\tCLQ2_ParseMuzzleFlash();\n\t\t\tbreak;\n\t\tcase svcq2_muzzleflash2:\n\t\t\tCLQ2_ParseMuzzleFlash2();\n\t\t\treturn;\n\t\tcase svcq2_temp_entity:\n\t\t\tCLQ2_ParseTEnt();\n\t\t\tbreak;\n\t\tcase svcq2_layout:\n\t\t\ts = MSG_ReadString ();\n\t\t\tQ_strncpyz (cl.q2layout[seat], s, sizeof(cl.q2layout[seat]));\n\t\t\tbreak;\n\t\tcase svcq2_inventory:\n\t\t\tCLQ2_ParseInventory(seat);\n\t\t\tbreak;\n\n\t// the rest are private to the client and server\n\t\tcase svcq2_nop:\t\t\t//6\n\t\t\tbreak;\t//q2e sends these when it has nothing else to send, for some reason.\n\t\t\t//or they're never seen and thus probably a bug.\n\t\t\tHost_EndGame (\"CL_ParseServerMessage: svcq2_nop not implemented\");\n\t\t\treturn;\n\t\tcase svcq2_disconnect:\n\t\t\tif (cls.state == ca_connected)\n\t\t\t\tHost_EndGame (\"Server disconnected\\n\"\n\t\t\t\t\t\"Server version may not be compatible\");\n\t\t\telse\n\t\t\t\tHost_EndGame (\"Server disconnected\");\n\t\t\treturn;\n\t\tcase svcq2_reconnect:\t//8. this is actually kinda weird to have\n\t\t\tCon_TPrintf (\"reconnecting...\\n\");\n#if 1\n\t\t\tCL_Disconnect(\"Reconnect request\");\n\t\t\tCL_BeginServerReconnect();\n\t\t\treturn;\n#else\n\t\t\tCL_SendClientCommand(true, \"new\");\n\t\t\tbreak;\n#endif\n\t\tcase svcq2_sound:\t\t//9\t\t\t// <see code>\n\t\t\tCLQ2_ParseStartSoundPacket();\n\t\t\tbreak;\n\t\tcase svcq2_print:\t\t//10\t\t\t// [qbyte] id [string] null terminated string\n\t\t\ti = MSG_ReadByte ();\n\t\t\ts = MSG_ReadString ();\n\t\t\tif (i != PRINT_CHAT)\n\t\t\t\ts = TL_Translate(com_language, s);\n\n\t\t\tCL_ParsePrint(s, i);\n\t\t\tbreak;\n\t\tcase svcq2_stufftext:\t//11\t\t\t// [string] stuffed into client's console buffer, should be \\n terminated\n\t\t\ts = MSG_ReadString ();\n\t\t\tCon_DPrintf (\"stufftext: %s\\n\", s);\n\t\t\tif (!strncmp(s, \"precache\", 8))\t//big major hack. Q2 uses a command that q1 has as a cvar.\n\t\t\t{\t//call the q2 precache function.\n\t\t\t\tCLQ2_Precache_f();\n\t\t\t}\n\t\t\telse\n\t\t\t\tCbuf_AddText (s, RESTRICT_SERVER);\t//don't let the local user cheat\n\t\t\tbreak;\n\t\tcase svcq2_serverdata:\t//12\t\t\t// [long] protocol ...\n\t\t\tCbuf_Execute ();\t\t// make sure any stuffed commands are done\n\t\t\tCLQ2_ParseServerData ();\n\t\t\tbreak;\n\t\tcase svcq2_configstring:\t//13\t\t// [short] [string]\n\t\t\tCLQ2_ParseConfigString();\n\t\t\tbreak;\n\t\tcase svcq2_spawnbaseline://14\n\t\t\tCLQ2_ParseBaseline();\n\t\t\tbreak;\n\t\tcase svcq2_centerprint:\t//15\t\t// [string] to put in center of the screen\n\t\t\ts = TL_Translate(com_language, MSG_ReadString ());\n\t\t\tif (*s == '%')\t//grr!\n\t\t\t\ts = CLQ2EX_ReformatBinds(seat, s, alloca(8192), 8192);\n\n#ifdef PLUGINS\n\t\t\tif (Plug_CenterPrintMessage(s, seat))\n#endif\n\t\t\t\tSCR_CenterPrint (seat, s, false);\n\t\t\tbreak;\n\t\tcase svcq2_download:\t\t//16\t\t// [short] size [size bytes]\n\t\t\tCL_ParseDownload(false);\n\t\t\tbreak;\n\t\tcase svcq2_playerinfo:\t//17\t\t\t// variable\n\t\t\tHost_EndGame (\"CL_ParseServerMessage: svcq2_playerinfo not as part of svcq2_frame\");\n\t\t\treturn;\n\t\tcase svcq2_packetentities://18\t\t\t// [...]\n\t\t\tHost_EndGame (\"CL_ParseServerMessage: svcq2_packetentities not as part of svcq2_frame\");\n\t\t\treturn;\n\t\tcase svcq2_deltapacketentities://19\t// [...]\n\t\t\tHost_EndGame (\"CL_ParseServerMessage: svcq2_deltapacketentities not as part of svcq2_frame\");\n\t\t\treturn;\n\t\tcase svcq2_frame:\t\t\t//20 (the bastard to implement.)\n\t\t\tCLQ2_ParseFrame(0);\n\t\t\tbreak;\n\n\t\tcase 21:\n\t\t\tif (cls.protocol_q2 == PROTOCOL_VERSION_R1Q2 || cls.protocol_q2 == PROTOCOL_VERSION_Q2PRO)\n\t\t//case svcr1q2_zpacket:\t//r1q2, just try to ignore it.\n\t\t\t\tCLQ2_ParseZPacket();\n\t\t\telse if (cls.protocol_q2 == PROTOCOL_VERSION_Q2EX)\n\t\t\t{\n\t\t//case svcq2ex_splitclient:\n\t\t\t\tseat = MSG_ReadByte()-1;\n\t\t\t\tgoto seatedcommand;\n\t\t\t}\n\t\t\telse\n\t\t\t\tgoto isillegible;\n\t\t\tbreak;\n\t\tcase 22:\n\t\t\tif (cls.protocol_q2 == PROTOCOL_VERSION_R1Q2 || cls.protocol_q2 == PROTOCOL_VERSION_Q2PRO)\n\t\t//case svcr1q2_zdownload:\n\t\t\t\tCL_ParseDownload(true);\n\t\t\telse if (cls.protocol_q2 == PROTOCOL_VERSION_Q2EX)\n\t\t//case svcq2ex_configblast:\n\t\t\t\tCLQ2EX_ParseBlast(CLQ2_ParseConfigString);\n\t\t\telse\n\t\t\t\tgoto isillegible;\n\t\t\tbreak;\n\t\tcase 23:\n\t\t\tif (cls.protocol_q2 == PROTOCOL_VERSION_R1Q2)\n\t\t//case svcr1q2_playerupdate:\n\t\t\t\tCLR1Q2_ParsePlayerUpdate();\n\t\t\telse if (cls.protocol_q2 == PROTOCOL_VERSION_Q2EX)\n\t\t//case svcq2ex_spawnbaselineblast:\n\t\t\t\tCLQ2EX_ParseBlast(CLQ2_ParseBaseline);\n\t\t\telse\n\t\t\t\tgoto isillegible;\n\t\t\tbreak;\n\t\tcase 24:\n\t\t\tif (cls.protocol_q2 == PROTOCOL_VERSION_R1Q2)\n\t\t//case svcr1q2_setting:\n\t\t\t\tCLR1Q2_ParseSetting();\n\t\t\telse if (cls.protocol_q2 == PROTOCOL_VERSION_Q2EX)\n\t\t//case svcq2ex_levelrestart:\n\t\t\t\tCLQ2EX_ParseLevelRestart();\n\t\t\telse\n\t\t\t\tgoto isillegible;\n\t\t\tbreak;\n\t\tcase 25:\n\t\t\tif (cls.protocol_q2 == PROTOCOL_VERSION_Q2EX)\n\t\t//case svcq2ex_danage:\n\t\t\t\tCLQ2EX_ParseDamage(seat);\n\t\t\telse\n\t\t\t\tgoto isillegible;\n\t\t\tbreak;\n\t\tcase 26:\n\t\t\tif (cls.protocol_q2 == PROTOCOL_VERSION_Q2EX)\n\t\t//case svcq2ex_locprint:\n\t\t\t\tCLQ2EX_LocPrint(seat);\n\t\t\telse\n\t\t\t\tgoto isillegible;\n\t\t\tbreak;\n\t\tcase 27:\n\t\t\tif (cls.protocol_q2 == PROTOCOL_VERSION_Q2EX)\n\t\t//case svcq2ex_fog:\n\t\t\t\tCLQ2EX_ParseFog();\n\t\t\telse\n\t\t\t\tgoto isillegible;\n\t\t\tbreak;\n\t\tcase 28:\n\t\t\tif (cls.protocol_q2 == PROTOCOL_VERSION_Q2EX)\n\t\t//case svcq2ex_waiting:\n\t\t\t\tMSG_ReadByte();\t//number of players remaining\n\t\t\telse\n\t\t\t\tgoto isillegible;\n\t\t\tbreak;\n\t\tcase 29:\n\t\t\tif (cls.protocol_q2 == PROTOCOL_VERSION_Q2EX)\n\t\t\t{\n\t\t//case svcq2ex_botchat:\n\t\t\t\tMSG_ReadString();\n\t\t\t\tMSG_ReadUInt16();\n\t\t\t\tMSG_ReadString();\n\t\t\t}\n\t\t\telse\n\t\t\t\tgoto isillegible;\n\t\t\tbreak;\n\t\tcase 30:\n\t\t\tif (cls.protocol_q2 == PROTOCOL_VERSION_Q2EX)\n\t\t\t{\n\t\t\t//case svcq2ex_mapmarker:\n\t\t\t\tvec3_t pos;\n\t\t\t\t/*id=*/MSG_ReadUInt16();\n\t\t\t\t/*time=*/MSG_ReadUInt16();\n\t\t\t\tMSG_ReadPos(pos);\n\t\t\t\t/*image=*/MSG_ReadUInt16();\n\t\t\t\t/*paltint=*/MSG_ReadByte();\n\t\t\t\t/*flags=*/MSG_ReadByte();\n\t\t\t}\n\t\t\telse\n\t\t\t\tgoto isillegible;\n\t\t\tbreak;\n\t\tcase 31:\n\t\t\tif (cls.protocol_q2 == PROTOCOL_VERSION_Q2EX)\n\t\t//case svcq2ex_routemarker:\n\t\t\t\tCLQ2EX_ParseRouteMarker();\n\t\t\telse\n\t\t\t\tgoto isillegible;\n\t\t\tbreak;\n\t\tcase 32:\n\t\t\tif (cls.protocol_q2 == PROTOCOL_VERSION_Q2EX)\n\t\t//case svcq2ex_muzzleflash3:\n\t\t\t\tCLQ2EX_ParseMuzzleFlash3();\n\t\t\telse\n\t\t\t\tgoto isillegible;\n\t\t\tbreak;\n\t\tcase 33:\n\t\t\tif (cls.protocol_q2 == PROTOCOL_VERSION_Q2EX)\n\t\t//case svcq2ex_achievement:\n\t\t\t\tCon_Printf(\"ACHIEVEMENT! %s\\n\", MSG_ReadString());\n\t\t\telse\n\t\t\t\tgoto isillegible;\n\t\t\tbreak;\n\t\t}\n\t\tlast = cmd;\n\t}\n\tCL_SetSolidEntities ();\n\n\tif (cls.demohadkeyframe)\n\t\tCL_WriteDemoMessage(&net_message, startpos);\t//FIXME: incomplete frames might be awkward\n}\n#endif\n\n#ifdef NQPROT\n//Proquake specific stuff\n#define pqc_nop\t\t\t1\n#define pqc_new_team\t2\n#define pqc_erase_team\t3\n#define pqc_team_frags\t4\n#define\tpqc_match_time\t5\n#define pqc_match_reset\t6\n#define pqc_ping_times\t7\nstatic int MSG_ReadBytePQ (char **s)\n{\n\tint ret = (*s)[0] * 16 + (*s)[1] - 272;\n\t*s+=2;\n\treturn ret;\n}\nstatic int MSG_ReadShortPQ (char **s)\n{\n\treturn MSG_ReadBytePQ(s) * 256 + MSG_ReadBytePQ(s);\n}\nstatic char *CLNQ_ParseProQuakeMessage (char *s)\n{\n\tint cmd;\n\tint ping;\n\tint team, shirt, frags;\n\n\ts++;\n\tcmd = *s++;\n\n\tswitch (cmd)\n\t{\n\tdefault:\n\t\tCon_DPrintf(\"Unrecognised ProQuake Message %i\\n\", cmd);\n\t\tbreak;\n\tcase pqc_new_team:\n\t\tcl.teamplay = true;\n\t\tteam = MSG_ReadBytePQ(&s) - 16;\n\t\tshirt = MSG_ReadBytePQ(&s) - 16;\n\t\tSbar_PQ_Team_New(team, shirt);\n\t\tbreak;\n\n\tcase pqc_erase_team:\n\t\tteam = MSG_ReadBytePQ(&s) - 16;\n\t\tSbar_PQ_Team_New(team, 0);\n\t\tSbar_PQ_Team_Frags(team, 0);\n\t\tbreak;\n\n\tcase pqc_team_frags:\n\t\tteam = MSG_ReadBytePQ(&s) - 16;\n\t\tfrags = MSG_ReadShortPQ(&s);\n\t\tif (frags & 32768)\n\t\t\tfrags = frags - 65536;\n\t\tSbar_PQ_Team_Frags(team, frags);\n\t\tbreak;\n\n\tcase pqc_match_time:\n\t\tcl.matchgametimestart = MSG_ReadBytePQ(&s)*60;\n\t\tcl.matchgametimestart += MSG_ReadBytePQ(&s);\n\t\tcl.matchgametimestart = cl.gametime - cl.matchgametimestart;\n\t\tbreak;\n\n\tcase pqc_match_reset:\n\t\tSbar_PQ_Team_Reset();\n\t\tbreak;\n\n\tcase pqc_ping_times:\n\t\tcl.last_ping_request = realtime;\n\t\twhile ((ping = MSG_ReadShortPQ(&s)))\n\t\t{\n\t\t\tif ((ping / 4096) >= MAX_CLIENTS)\n\t\t\t\tHost_Error (\"CL_ParseProQuakeMessage: pqc_ping_times > MAX_CLIENTS\");\n\t\t\tcl.players[ping / 4096].ping = ping & 4095;\n\t\t}\n\t\tbreak;\n\t}\n\treturn s;\n}\n\nstatic qboolean CLNQ_ParseNQPrints(char *s)\n{\n\tint i;\n\tchar *start = s;\n\tif (!strcmp(s, \"Client ping times:\\n\") && !cls.qex)\n\t{\n\t\tcl.nqparseprint = CLNQPP_PINGS;\n\t\treturn true;\n\t}\n\telse if (cl.nqparseprint == CLNQPP_PINGS)\n\t{\n\t\tchar *pingstart;\n\t\tcl.nqparseprint = CLNQPP_NONE;\n\t\twhile(*s == ' ')\n\t\t\ts++;\n\t\tpingstart = s;\n\t\tif (*s == '-')\n\t\t\ts++;\n\t\tif (*s >= '0' && *s <= '9')\n\t\t{\n\t\t\twhile(*s >= '0' && *s <= '9')\n\t\t\t\ts++;\n\t\t\tif (*s == ' ' && s-start >= 3)\n\t\t\t{\n\t\t\t\ts++;\n\t\t\t\tstart = s;\n\t\t\t\ts = strchr(s, '\\n');\n\t\t\t\tif (!s)\n\t\t\t\t\treturn false;\n\t\t\t\t*s = 0;\n\n\t\t\t\tfor (i = 0; i < cl.allocated_client_slots; i++)\n\t\t\t\t{\n\t\t\t\t\tif (!strcmp(start, cl.players[i].name))\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tif (i == cl.allocated_client_slots)\n\t\t\t\t{\n\n\t\t\t\t}\n\t\t\t\tif (i != cl.allocated_client_slots)\n\t\t\t\t{\n\t\t\t\t\tcl.players[i].ping = atoi(pingstart);\n\t\t\t\t}\n\t\t\t\tcl.nqparseprint = CLNQPP_PINGS;\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\n\t\ts = start;\n\t}\n\n\tif (!strncmp(s, \"host:    \", 9) && !cls.qex)\n\t{\n\t\tcl.nqparseprint = CLNQPP_STATUS;\n\t\treturn cls.nqexpectingstatusresponse;\n\t}\n\telse if (cl.nqparseprint == CLNQPP_STATUS)\n\t{\n\t\tif (!strncmp(s, \"players: \", 9))\n\t\t{\n\t\t\tcl.nqparseprint = CLNQPP_STATUSPLAYER;\n\t\t\treturn cls.nqexpectingstatusresponse;\n\t\t}\n\t\telse if (strchr(s, ':'))\n\t\t\treturn cls.nqexpectingstatusresponse;\n\t\t\n\t\tcl.nqparseprint = CLNQPP_NONE;\t//error of some kind...\n\t\tcls.nqexpectingstatusresponse = false;\n\t}\n\tif (cl.nqparseprint == CLNQPP_STATUSPLAYER)\n\t{\n\t\tif (*s == '#')\n\t\t{\n\t\t\tcl.nqparseprint = CLNQPP_STATUSPLAYERIP;\n\t\t\tcl.nqparseprintplayer = atoi(s+1)-1;\n\t\t\tif (cl.nqparseprintplayer >= 0 && cl.nqparseprintplayer < cl.allocated_client_slots)\n\t\t\t\treturn cls.nqexpectingstatusresponse;\n\t\t}\n\t\tcl.nqparseprint = CLNQPP_NONE;\t//error of some kind...\n\t\tcls.nqexpectingstatusresponse = false;\n\t}\n\tif (cl.nqparseprint == CLNQPP_STATUSPLAYERIP)\n\t{\n\t\tif (!strncmp(s, \"   \", 3))\n\t\t{\n\t\t\twhile(*s == ' ')\n\t\t\t\ts++;\n\t\t\tCOM_ParseOut(s, cl.players[cl.nqparseprintplayer].ip, sizeof(cl.players[cl.nqparseprintplayer].ip));\n\t\t\tIPLog_Add(cl.players[cl.nqparseprintplayer].ip, cl.players[cl.nqparseprintplayer].name);\n\t\t\tif (*cl.players[cl.nqparseprintplayer].ip != '[' && *cl.players[cl.nqparseprintplayer].ip < '0' && *cl.players[cl.nqparseprintplayer].ip > '9')\n\t\t\t\t*cl.players[cl.nqparseprintplayer].ip = 0;\t//non-numeric addresses are not useful.\n\t\t\tcl.nqparseprint = CLNQPP_STATUSPLAYER;\n\t\t\treturn cls.nqexpectingstatusresponse;\n\t\t}\n\t\tcl.nqparseprint = CLNQPP_NONE;\t//error of some kind...\n\t\tcls.nqexpectingstatusresponse = false;\n\t}\n\n\treturn false;\n}\n\nstatic void CLNQ_CheckPlayerIsSpectator(unsigned int i)\n{\n\tcl.players[i].spectator =\n\t\t(cl.players[i].frags==-999) ||\t//DP mods tend to use -999\n\t\t(cl.players[i].frags==-99);\t//crmod uses -99 for spectators, which is annoying.\n\t//we can't add any colour checks, as apparently this fucks up too.\n\n\tif (!*cl.players[i].name)\n\t\tcl.players[i].spectator = false;\n}\n\n#ifdef HEXEN2\n#define svch2_particle2 34\n#define svch2_cutscene 35\n#define svch2_midiname 36\n#define svch2_updateclass 37\n#define svch2_particle3 38\n#define svch2_particle4 39\n#define svch2_setviewflags 40\n#define svch2_clearviewflags 41\n#define svch2_starteffect 42\n#define svch2_endeffect 43\n#define svch2_plaque 44\n#define svch2_particleexplosion 45\n#define svch2_setviewtint 46\n#define svch2_ref 47\n#define svch2_clearedicts 48\n#define svch2_updateinv 49\n#define svch2_setanglelerp 50\n#define svch2_updatekoth 51\n#define svch2_togglestatbar 52\n#define svch2_soundpos 53\nstatic qboolean CLH2_ParseServerSubMessage (int cmd)\n{\n\tconst int destsplit = 0;\n\tunsigned int u;\n\tint i,j;\n\tconst int svch2_first = svch2_particle2;\n\tstatic const char *svc_h2strings[] =\n\t{\n\t\t\"h2_particle2\",\n\t\t\"h2_cutscene\",\n\t\t\"h2_midiname\",\n\t\t\"h2_updateclass\",\n\t\t\"h2_particle3\",\n\t\t\"h2_particle4\",\n\t\t\"h2_setviewflags\",\n\t\t\"h2_clearviewflags\",\n\t\t\"h2_starteffect\",\n\t\t\"h2_endeffect\",\n\t\t\"h2_plaque\",\n\t\t\"h2_particleexplosion\",\n\t\t\"h2_setviewtint\",\n\t\t\"h2_ref\",\n\t\t\"h2_clearedicts\",\n\t\t\"h2_updateinv\",\n\t\t\"h2_setanglelerp\",\n\t\t\"h2_updatekoth\",\n\t\t\"h2_togglestatbar\",\n\t\t\"h2_soundpos\",\n\t};\n\t//no fastupdate checks needed here.\n\tSHOWNET2((cmd>=svch2_first&&cmd<svch2_first+countof(svc_h2strings))?svc_h2strings[cmd-svch2_first]:svc_nqstrings[cmd>(sizeof(svc_nqstrings)/sizeof(char*))?0:cmd], cmd);\n\n\tswitch (cmd)\n\t{\n\tcase svch2_midiname:\n\t\tMSG_ReadString();\n\t\tbreak;\n\tcase svch2_particle2:\n\t\tCL_ParseParticleEffect2 ();\n\t\tbreak;\n\tcase svch2_particle3:\n\t\tCL_ParseParticleEffect3 ();\n\t\tbreak;\n\tcase svch2_particle4:\n\t\tCL_ParseParticleEffect4 ();\n\t\tbreak;\n\tcase svch2_starteffect:\n\t\ti = MSG_ReadByte(); //handle\n\t\tj = MSG_ReadByte();\t//type\n\t\tswitch(j)\n\t\t{\n\t\tcase 1 /*ce_rain*/:\n\t\t\tMSG_ReadCoord();\n\t\t\tMSG_ReadCoord();\n\t\t\tMSG_ReadCoord();\n\t\t\tMSG_ReadCoord();\n\t\t\tMSG_ReadCoord();\n\t\t\tMSG_ReadCoord();\n\t\t\tMSG_ReadCoord();\n\t\t\tMSG_ReadCoord();\n\t\t\tMSG_ReadCoord();\n\t\t\tMSG_ReadCoord();\n\t\t\tMSG_ReadCoord();\n\t\t\tMSG_ReadCoord();\n\t\t\tMSG_ReadShort();\n\t\t\tMSG_ReadShort();\n\t\t\tMSG_ReadFloat();\n\t\t\tbreak;\n\t\tcase 4/*ce_white_smoke*/:\n\t\t\tMSG_ReadCoord();\n\t\t\tMSG_ReadCoord();\n\t\t\tMSG_ReadCoord();\n\t\t\tMSG_ReadFloat();\n\t\t\tMSG_ReadFloat();\n\t\t\tMSG_ReadFloat();\n\t\t\tMSG_ReadFloat();\n\t\t\tMSG_ReadFloat();\n\t\t\tbreak;\n\t\tcase 5/*ce_bluespark*/:\n\t\tcase 6/*ce_yellowspark*/:\n\t\tcase 9/*ce_sm_white_flash*/:\n\t\tcase 13/*ce_sm_blue_flash*/:\n\t\tcase 15/*ce_sm_explosion*/:\n\t\tcase 16/*ce_lg_explosion*/:\n\t\tcase 17/*ce_floor_explosion*/:\n\t\tcase 19/*ce_blue_explosion*/:\n\t\tcase 32/*ce_xbow_explosion*/:\n\t\tcase 33/*ce_new_explosion*/:\n\t\tcase 34/*ce_magic_missile_explosion*/:\n\t\tcase 38/*ce_teleporterpuffs*/:\n\t\t\tMSG_ReadCoord();\n\t\t\tMSG_ReadCoord();\n\t\t\tMSG_ReadCoord();\n\t\t\tbreak;\n\t\tcase 39/*ce_teleporterbody*/:\n\t\t\tMSG_ReadCoord();\n\t\t\tMSG_ReadCoord();\n\t\t\tMSG_ReadCoord();\n\t\t\tMSG_ReadFloat();\n\t\t\tMSG_ReadFloat();\n\t\t\tMSG_ReadFloat();\n\t\t\tMSG_ReadFloat();\n\t\t\tbreak;\n\t\tcase 40/*ce_boneshard*/:\n\t\t\tMSG_ReadCoord();\n\t\t\tMSG_ReadCoord();\n\t\t\tMSG_ReadCoord();\n\n\t\t\tMSG_ReadFloat();\n\t\t\tMSG_ReadFloat();\n\t\t\tMSG_ReadFloat();\n\n\t\t\tMSG_ReadFloat();\n\t\t\tMSG_ReadFloat();\n\t\t\tMSG_ReadFloat();\n\n\t\t\tMSG_ReadFloat();\n\t\t\tMSG_ReadFloat();\n\t\t\tMSG_ReadFloat();\n\t\t\tbreak;\n\t\tcase 43/*ce_snow*/:\n\t\t\tMSG_ReadCoord();\n\t\t\tMSG_ReadCoord();\n\t\t\tMSG_ReadCoord();\n\t\t\tMSG_ReadCoord();\n\t\t\tMSG_ReadCoord();\n\t\t\tMSG_ReadCoord();\n\t\t\tMSG_ReadByte();\n\t\t\tMSG_ReadCoord();\n\t\t\tMSG_ReadCoord();\n\t\t\tMSG_ReadCoord();\n\t\t\tMSG_ReadByte();\n\t\t\tbreak;\n\t\tcase 55/*ce_chunk*/:\n\t\t\tMSG_ReadCoord();\n\t\t\tMSG_ReadCoord();\n\t\t\tMSG_ReadCoord();\n\t\t\tMSG_ReadByte();\n\t\t\tMSG_ReadCoord();\n\t\t\tMSG_ReadCoord();\n\t\t\tMSG_ReadCoord();\n\t\t\tMSG_ReadByte();\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tHost_EndGame (\"svch2_starteffect: unknown type %i\", j);\n\t\t\treturn false;\n\t\t}\n\t\tbreak;\n\tcase svch2_endeffect:\n\t\tMSG_ReadByte();\t//handle\n\t\tbreak;\n\tcase svch2_updateclass:\n\t\tu = MSG_ReadPlayer();\n\t\tj = MSG_ReadByte();\n\t\tif (u < MAX_CLIENTS)\n\t\t\tInfoBuf_SetValueForKey(&cl.players[u].userinfo, \"cl_playerclass\", va(\"%i\", j));\n\t\tbreak;\n\tcase svch2_updateinv:\n\t\tcmd = MSG_ReadByte();\n\t\ti = j = 0;\n\t\tif (cmd&(1<<0))\ti |= MSG_ReadByte()<<0;\n\t\tif (cmd&(1<<1))\ti |= MSG_ReadByte()<<8;\n\t\tif (cmd&(1<<2))\ti |= MSG_ReadByte()<<16;\n\t\tif (cmd&(1<<3))\ti |= MSG_ReadByte()<<24;\n\t\tif (cmd&(1<<4))\tj |= MSG_ReadByte()<<0;\n\t\tif (cmd&(1<<5))\tj |= MSG_ReadByte()<<8;\n\t\tif (cmd&(1<<6))\tj |= MSG_ReadByte()<<16;\n\t\tif (cmd&(1<<7))\tj |= MSG_ReadByte()<<24;\n\t\tif (i & (1u<< 0)) CL_SetStatInt(destsplit, STAT_HEALTH, MSG_ReadShort());\n\t\tif (i & (1u<< 1)) CL_SetStatInt(destsplit, STAT_H2_LEVEL, MSG_ReadByte());\n\t\tif (i & (1u<< 2)) CL_SetStatInt(destsplit, STAT_H2_INTELLIGENCE, MSG_ReadByte());\n\t\tif (i & (1u<< 3)) CL_SetStatInt(destsplit, STAT_H2_WISDOM, MSG_ReadByte());\n\t\tif (i & (1u<< 4)) CL_SetStatInt(destsplit, STAT_H2_STRENGTH, MSG_ReadByte());\n\t\tif (i & (1u<< 5)) CL_SetStatInt(destsplit, STAT_H2_DEXTERITY, MSG_ReadByte());\n\t\tif (i & (1u<< 6)) CL_SetStatInt(destsplit, STAT_ACTIVEWEAPON, MSG_ReadByte());\n\t\tif (i & (1u<< 7)) CL_SetStatInt(destsplit, STAT_H2_BLUEMANA, MSG_ReadByte());\n\t\tif (i & (1u<< 8)) CL_SetStatInt(destsplit, STAT_H2_GREENMANA, MSG_ReadByte());\n\t\tif (i & (1u<< 9)) CL_SetStatInt(destsplit, STAT_H2_EXPERIENCE, MSG_ReadLong());\n\t\tif (i & (1u<<10)) CL_SetStatInt(destsplit, STAT_H2_CNT_TORCH, MSG_ReadByte());\n\t\tif (i & (1u<<11)) CL_SetStatInt(destsplit, STAT_H2_CNT_H_BOOST, MSG_ReadByte());\n\t\tif (i & (1u<<12)) CL_SetStatInt(destsplit, STAT_H2_CNT_SH_BOOST, MSG_ReadByte());\n\t\tif (i & (1u<<13)) CL_SetStatInt(destsplit, STAT_H2_CNT_MANA_BOOST, MSG_ReadByte());\n\t\tif (i & (1u<<14)) CL_SetStatInt(destsplit, STAT_H2_CNT_TELEPORT, MSG_ReadByte());\n\t\tif (i & (1u<<15)) CL_SetStatInt(destsplit, STAT_H2_CNT_TOME, MSG_ReadByte());\n\t\tif (i & (1u<<16)) CL_SetStatInt(destsplit, STAT_H2_CNT_SUMMON, MSG_ReadByte());\n\t\tif (i & (1u<<17)) CL_SetStatInt(destsplit, STAT_H2_CNT_INVISIBILITY, MSG_ReadByte());\n\t\tif (i & (1u<<18)) CL_SetStatInt(destsplit, STAT_H2_CNT_GLYPH, MSG_ReadByte());\n\t\tif (i & (1u<<19)) CL_SetStatInt(destsplit, STAT_H2_CNT_HASTE, MSG_ReadByte());\n\t\tif (i & (1u<<20)) CL_SetStatInt(destsplit, STAT_H2_CNT_BLAST, MSG_ReadByte());\n\t\tif (i & (1u<<21)) CL_SetStatInt(destsplit, STAT_H2_CNT_POLYMORPH, MSG_ReadByte());\n\t\tif (i & (1u<<22)) CL_SetStatInt(destsplit, STAT_H2_CNT_FLIGHT, MSG_ReadByte());\n\t\tif (i & (1u<<23)) CL_SetStatInt(destsplit, STAT_H2_CNT_CUBEOFFORCE, MSG_ReadByte());\n\t\tif (i & (1u<<24)) CL_SetStatInt(destsplit, STAT_H2_CNT_INVINCIBILITY, MSG_ReadByte());\n\t\tif (i & (1u<<25)) CL_SetStatFloat(destsplit, STAT_H2_ARTIFACT_ACTIVE, MSG_ReadFloat());\n\t\tif (i & (1u<<26)) CL_SetStatFloat(destsplit, STAT_H2_ARTIFACT_LOW, MSG_ReadFloat());\n\t\tif (i & (1u<<27)) CL_SetStatInt(destsplit, STAT_H2_MOVETYPE, MSG_ReadByte());\n\t\tif (i & (1u<<28)) CL_SetStatInt(destsplit, STAT_H2_CAMERAMODE, MSG_ReadByte());\n\t\tif (i & (1u<<29)) CL_SetStatFloat(destsplit, STAT_H2_HASTED, MSG_ReadFloat());\n\t\tif (i & (1u<<30)) CL_SetStatInt(destsplit, STAT_H2_INVENTORY, MSG_ReadByte());\n\t\tif (i & (1u<<31)) CL_SetStatFloat(destsplit, STAT_H2_RINGS_ACTIVE, MSG_ReadFloat());\n\t\tif (j & (1u<< 0)) CL_SetStatFloat(destsplit, STAT_H2_RINGS_LOW, MSG_ReadFloat());\n\t\tif (j & (1u<< 1)) CL_SetStatInt(destsplit, STAT_H2_ARMOUR1, MSG_ReadByte());\n\t\tif (j & (1u<< 2)) CL_SetStatInt(destsplit, STAT_H2_ARMOUR2, MSG_ReadByte());\n\t\tif (j & (1u<< 3)) CL_SetStatInt(destsplit, STAT_H2_ARMOUR3, MSG_ReadByte());\n\t\tif (j & (1u<< 4)) CL_SetStatInt(destsplit, STAT_H2_ARMOUR4, MSG_ReadByte());\n\t\tif (j & (1u<< 5)) CL_SetStatInt(destsplit, STAT_H2_FLIGHT_T, MSG_ReadByte());\n\t\tif (j & (1u<< 6)) CL_SetStatInt(destsplit, STAT_H2_WATER_T, MSG_ReadByte());\n\t\tif (j & (1u<< 7)) CL_SetStatInt(destsplit, STAT_H2_TURNING_T, MSG_ReadByte());\n\t\tif (j & (1u<< 8)) CL_SetStatInt(destsplit, STAT_H2_REGEN_T, MSG_ReadByte());\n\t\tif (j & (1u<< 9)) /*CL_SetStatFloat(destsplit, STAT_H2_HASTE_T,*/( MSG_ReadFloat());\n\t\tif (j & (1u<<10)) /*CL_SetStatFloat(destsplit, STAT_H2_TOMB_T,*/( MSG_ReadFloat());\n\t\tif (j & (1u<<11)) CL_SetStatString(destsplit, STAT_H2_PUZZLE1, MSG_ReadString());\n\t\tif (j & (1u<<12)) CL_SetStatString(destsplit, STAT_H2_PUZZLE2, MSG_ReadString());\n\t\tif (j & (1u<<13)) CL_SetStatString(destsplit, STAT_H2_PUZZLE3, MSG_ReadString());\n\t\tif (j & (1u<<14)) CL_SetStatString(destsplit, STAT_H2_PUZZLE4, MSG_ReadString());\n\t\tif (j & (1u<<15)) CL_SetStatString(destsplit, STAT_H2_PUZZLE5, MSG_ReadString());\n\t\tif (j & (1u<<16)) CL_SetStatString(destsplit, STAT_H2_PUZZLE6, MSG_ReadString());\n\t\tif (j & (1u<<17)) CL_SetStatString(destsplit, STAT_H2_PUZZLE7, MSG_ReadString());\n\t\tif (j & (1u<<18)) CL_SetStatString(destsplit, STAT_H2_PUZZLE8, MSG_ReadString());\n\t\tif (j & (1u<<19)) CL_SetStatInt(destsplit, STAT_H2_MAXHEALTH, MSG_ReadShort());\n\t\tif (j & (1u<<20)) CL_SetStatInt(destsplit, STAT_H2_MAXMANA, MSG_ReadByte());\n\t\tif (j & (1u<<21)) CL_SetStatFloat(destsplit, STAT_H2_FLAGS, MSG_ReadFloat());\n\t\tif (j & (1u<<22)) CL_SetStatInt(destsplit, STAT_H2_OBJECTIVE1, MSG_ReadLong());\n\t\tif (j & (1u<<23)) CL_SetStatInt(destsplit, STAT_H2_OBJECTIVE2, MSG_ReadLong());\n\t\t//if (j & (1u<<24)) CL_SetStat(destsplit, STAT_H2_, MSG_Read());\n\t\t//if (j & (1u<<25)) CL_SetStat(destsplit, STAT_H2_, MSG_Read());\n\t\t//if (j & (1u<<26)) CL_SetStat(destsplit, STAT_H2_, MSG_Read());\n\t\t//if (j & (1u<<27)) CL_SetStat(destsplit, STAT_H2_, MSG_Read());\n\t\t//if (j & (1u<<28)) CL_SetStat(destsplit, STAT_H2_, MSG_Read());\n\t\t//if (j & (1u<<29)) CL_SetStat(destsplit, STAT_H2_, MSG_Read());\n\t\t//if (j & (1u<<30)) CL_SetStat(destsplit, STAT_H2_, MSG_Read());\n\t\t//if (j & (1u<<31)) CL_SetStat(destsplit, STAT_H2_, MSG_Read());\n\t\tbreak;\n\tcase svch2_ref:\n\t\tif (cls.signon == 4 - 1)\n\t\t{\t// first update is the final signon stage\n\t\t\tcls.signon = 4;\n\t\t\tCLNQ_SignonReply ();\n\t\t}\n\t\tCLH2_ParseEntities();\n\t\tbreak;\n\tcase svch2_clearedicts:\t//should have been handled by CLH2_ParseEntities (this also applies to fastupdates)\n\t\tHost_EndGame (\"CLNQ_ParseServerMessage: Unexpected server message (%i@%i)\", cmd, MSG_GetReadCount()-1);\n\t\treturn false;\n\tcase svch2_togglestatbar:\n\t\tbreak;\t//wtf\n\tcase svch2_setanglelerp:\t//urgh... demo playback has its own angle values, and with greater precision.\n\t\tMSG_ReadAngle();\n\t\tMSG_ReadAngle();\n\t\tMSG_ReadAngle();\n\t\tbreak;\n\tcase svch2_cutscene:\n\tcase svch2_setviewflags:\n\tcase svch2_clearviewflags:\n\tcase svch2_plaque:\n\tcase svch2_particleexplosion:\n\tcase svch2_setviewtint:\n\tcase svch2_updatekoth:\n\tcase svch2_soundpos:\n\t\t//we're only aiming for demo compat, so we ignore this tat.\n\t\tHost_EndGame (\"CLH2_ParseServerMessage: Unimplemented server message (%i@%i)\", cmd, MSG_GetReadCount()-1);\n\t\treturn false;\n\tdefault:\n\t\treturn false;\n\t}\n\treturn true;\t//handled.\n}\n#endif\n\n\nvoid CLNQ_ParseServerMessage (void)\n{\n\tconst int\tdestsplit = 0;\n\tint\t\t\tcmd;\n\tchar\t\t*s;\n\tunsigned int u;\n\tint\t\t\ti, j;\n\tvec3_t\t\tang;\n\tunsigned int cmdstart;\n\n//\tcl.last_servermessage = realtime;\n\tCL_ClearProjectiles ();\n\n//\n// if recording demos, copy the message out\n//\n\tif (cl_shownet.value == 1)\n\t\tCon_Printf (\"%i \",net_message.cursize);\n\telse if (cl_shownet.value >= 2)\n\t\tCon_Printf (\"------------------\\n\");\n\n//\n// parse the message\n//\n\twhile (1)\n\t{\n\t\tif (msg_badread)\n\t\t{\n\t\t\tCL_DumpPacket();\n\t\t\tHost_EndGame (\"CL_ParseServerMessage: Bad server message\");\n\t\t\tbreak;\n\t\t}\n\n\t\tcmdstart = MSG_GetReadCount();\n\t\tcmd = MSG_ReadByte ();\n\n\t\tif (cmd == -1)\n\t\t{\n\t\t\tSHOWNETEOM(\"END OF MESSAGE\");\n\t\t\tbreak;\n\t\t}\n\n#ifdef HEXEN2\n\t\tif (cls.protocol_nq == CPNQ_H2MP)\n\t\t{\n\t\t\tif (CLH2_ParseServerSubMessage(cmd))\n\t\t\t{\n\t\t\t\tpacketusage_pending[cmd] += MSG_GetReadCount()-cmdstart;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t//handle as a regular nq packet.\n\t\t}\n\t\telse\n#endif\n\t\t{\n\t\t\tif (cmd & 128)\n\t\t\t{\n\t\t\t\tSHOWNET(\"fast update\");\n\t\t\t\tCLNQ_ParseEntity(cmd&127);\n\t\t\t\tpacketusage_pending[128] += MSG_GetReadCount()-cmdstart;\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tSHOWNET2(svc_nqstrings[cmd>(sizeof(svc_nqstrings)/sizeof(char*))?0:cmd], cmd);\n\t\t}\n\n\t// other commands\n\t\tswitch (cmd)\n\t\t{\n\t\tdefault:\n\t\tbadsvc:\n\t\tcase svc_bad:\n\t\t\tCL_DumpPacket();\n\t\t\tHost_EndGame (\"CLNQ_ParseServerMessage: Illegible server message (%i@%i)\", cmd, MSG_GetReadCount()-1);\n\t\t\treturn;\n\n\t\tcase svc_nop:\n//\t\t\tCon_Printf (\"svc_nop\\n\");\n\t\t\tbreak;\n\n\t\tcase svc_print:\n\t\t\ts = MSG_ReadString ();\n\t\t\t//fallthrough...\n\t\tsvcprint:\n\t\t\tif (*s == 1 || *s == 2)\n\t\t\t{\n\t\t\t\t//FIXME: should be using the first char of the line, not the first char of the last segment.\n\t\t\t\tCL_ParsePrint(s+1, PRINT_CHAT);\n\t\t\t}\n\t\t\telse if (CLNQ_ParseNQPrints(s))\n\t\t\t\tbreak;\n\t\t\telse\n\t\t\t\tCL_ParsePrint(s, PRINT_HIGH);\n\t\t\tbreak;\n\n\t\tcase svc_disconnect:\n\t\t\tCL_Disconnect(cls.demoplayback?NULL:\"Server disconnected\");\t//don't show any errors on end-of-demo.\n\t\t\tCL_NextDemo();\n\t\t\treturn;\n\n\t\tcase svc_centerprint:\n\t\t\ts = MSG_ReadString ();\n\t\t\tsvccentreprint:\n#ifdef PLUGINS\n\t\t\tif (Plug_CenterPrintMessage(s, destsplit))\n#endif\n\t\t\t\tSCR_CenterPrint (destsplit, s, false);\n\t\t\tbreak;\n\n\t\tcase svc_stufftext:\n\t\t\ts = MSG_ReadString ();\n\t\t\tCL_ParseStuffCmd(s, destsplit);\n\t\t\tbreak;\n\n\t\tcase svc_version:\n\t\t\tCLNQ_ParseProtoVersion();\n\t\t\tbreak;\n\t\tcase svc_serverdata:\n\t\t\tif (*printtext)\n\t\t\t{\t//work around a missing-eol issue.\n\t\t\t\tCL_PrintStandardMessage(printtext, PRINT_HIGH);\n\t\t\t\tprinttext[0] = 0;\n\t\t\t}\n\t\t\tCbuf_Execute ();\t\t// make sure any stuffed commands are done\n\t\t\tCLNQ_ParseServerData ();\n\t\t\tbreak;\n\n\t\tcase svcdp_precache:\n\t\t//also svcqex_levelcompleted\n\t\t\tif (cls.qex)\n\t\t\t{\t//svcqex_levelcompleted\n\t\t\t\t//not really sure why this even exists.\n\t\t\t\tMSG_ReadSkip(10);\n\t\t\t\tMSG_ReadString();\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tCL_ParsePrecache();\n\t\t\tbreak;\n\n\t\tcase svc_cdtrack:\n\t\t\t{\n\t\t\t\tunsigned int firsttrack;\n\t\t\t\tunsigned int looptrack;\n\t\t\t\tfirsttrack = MSG_ReadByte ();\n\t\t\t\tlooptrack = MSG_ReadByte ();\n\n\t\t\t\tif (cls.demotrack != -1)\n\t\t\t\t\tfirsttrack = looptrack = cls.demotrack;\n\n\t\t\t\tMedia_NumberedTrack (firsttrack, looptrack);\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase svc_setview:\n\t\t\ti=MSGCL_ReadEntity();\n\t\t\tif (!cl.playerview[destsplit].viewentity)\n\t\t\t{\n\t\t\t\tif (!i || i > cl.allocated_client_slots)\n\t\t\t\t\tcl.playerview[destsplit].playernum = cl.allocated_client_slots;\t//the mvd spectator slot.\n\t\t\t\telse\n\t\t\t\t\tcl.playerview[destsplit].playernum = (unsigned int)i-1;\n\t\t\t}\n\t\t\tcl.playerview[destsplit].viewentity = i;\n\t\t\tbreak;\n\n\t\tcase svcnq_signonnum:\n\t\t\ti = MSG_ReadByte ();\n\n\t\t\tif (i <= cls.signon)\n\t\t\t\tHost_EndGame (\"Received signon %i when at %i\", i, cls.signon);\n\t\t\tcls.signon = i;\n\t\t\tCLNQ_SignonReply ();\n\t\t\tbreak;\n\t\tcase svc_setpause:\n\t\t\tcl.paused = MSG_ReadByte ();\n//\t\t\tMedia_SetPauseTrack(!!cl.paused);\n\t\t\tbreak;\n\n\t\tcase svc_spawnstaticsound:\n\t\t\tCL_ParseStaticSound (false);\n\t\t\tbreak;\n\n\t\tcase svc_spawnstatic:\n\t\t\tCL_ParseStaticProt (CPNQ_ID);\n\t\t\tbreak;\n\n\t\tcase svc_spawnbaseline:\n\t\t\ti = MSGCL_ReadEntity ();\n\t\t\tif (!CL_CheckBaselines(i))\n\t\t\t\tHost_EndGame(\"CLNQ_ParseServerMessage: svc_spawnbaseline failed with size %i\", i);\n\t\t\tCL_ParseBaseline (cl_baselines + i, CPNQ_ID);\n\t\t\tbreak;\n\n\t\t//PEXT_REPLACEMENTDELTAS\n\t\tcase svcfte_updateentities:\n\t\t\tif (cls.signon == 4 - 1)\n\t\t\t{\t// first update is the final signon stage\n\t\t\t\tcls.signon = 4;\n\t\t\t\tCLNQ_SignonReply ();\n\t\t\t}\n\t\t\tCL_ParseClientdata ();\n\t\t\tCLFTE_ParseEntities();\n\t\t\tbreak;\n\t\tcase svcfte_spawnstatic2:\n\t\t\tCL_ParseStaticProt (-1);\n\t\t\tbreak;\n\t\tcase svcfte_spawnbaseline2:\n\t\t\tCL_ParseBaselineDelta ();\n\t\t\tbreak;\n\n\t\tcase svcfte_cgamepacket_sized:\n#ifdef CSQC_DAT\n\t\t\tif (CSQC_ParseGamePacket(destsplit, true))\n\t\t\t\tbreak;\n#endif\n\t\t\tCon_Printf(\"Unable to parse gamecode packet\\n\");\n\t\t\tbreak;\n\t\tcase svcfte_cgamepacket:\n#ifdef HLCLIENT\n\t\t\tif (CLHL_ParseGamePacket())\n\t\t\t\tbreak;\n#endif\n#ifdef CSQC_DAT\n\t\t\tif (CSQC_ParseGamePacket(destsplit, cl.csqcdebug))\n\t\t\t\tbreak;\n#endif\n\t\t\tCon_Printf(\"Unable to parse gamecode packet\\n\");\n\t\t\tbreak;\n\n\t\tcase svc_time:\n\t\t\tCL_ParseClientdata ();\n\n\t\t\t//fixme: move this stuff to a common place\n//\t\t\tcl.playerview[destsplit].oldfixangle = cl.playerview[destsplit].fixangle;\n//\t\t\tVectorCopy(cl.playerview[destsplit].fixangles, cl.playerview[destsplit].oldfixangles);\n//\t\t\tcl.playerview[destsplit].fixangle = FIXANGLE_NO;\n\t\t\tif (cls.demoplayback)\n\t\t\t{\n//\t\t\t\textern vec3_t demoangles;\n//\t\t\t\tcl.playerview[destsplit].fixangle = FIXANGLE_FIXED;\n//\t\t\t\tVectorCopy(demoangles, cl.playerview[destsplit].fixangles);\n\t\t\t}\n\n\t\t\tcls.netchan.outgoing_sequence++;\n\t\t\tcls.netchan.incoming_sequence = cls.netchan.outgoing_sequence-1;\n\t\t\tcl.validsequence = cls.netchan.incoming_sequence;\n\n\t\t\tcl.last_servermessage = realtime;\n\n\t\t\tcl.oldgametime = cl.gametime;\n\t\t\tcl.oldgametimemark = cl.gametimemark;\n\t\t\tcl.gametime = MSG_ReadFloat();\n\t\t\tcl.gametimemark = realtime;\n\n\t\t\tif (cls.fteprotocolextensions2 & PEXT2_PREDINFO)\n\t\t\t{\n\t\t\t\tunsigned int seq = (cl.ackedmovesequence&0xffff0000) | MSG_ReadShort();\n\t\t\t\tif (seq > cl.ackedmovesequence)\n\t\t\t\t\tseq -= 0x10000;\t//protect against wraps.\n\t\t\t\tcl.ackedmovesequence = seq;\n\t\t\t}\n\n\t\t\t{\n\t\t\t\textern vec3_t demoangles;\n\t\t\t\tint fr = cls.netchan.incoming_sequence&UPDATE_MASK;\n\t\t\t\tif (cls.demoplayback)\n\t\t\t\t{\n\t\t\t\t\tcl.inframes[fr&UPDATE_MASK].packet_entities.fixangles[destsplit] = true;\n\t\t\t\t\tVectorCopy(demoangles, cl.inframes[fr&UPDATE_MASK].packet_entities.fixedangles[destsplit]);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tcl.inframes[fr&UPDATE_MASK].packet_entities.fixangles[destsplit] = false;\n\t\t\t}\n\t\t\tcl.inframes[cls.netchan.incoming_sequence&UPDATE_MASK].receivedtime = realtime;\n\t\t\tcl.inframes[cls.netchan.incoming_sequence&UPDATE_MASK].frameid = cls.netchan.incoming_sequence;\n\n\t\t\tif (CPNQ_IS_DP || cls.protocol_nq == CPNQ_H2MP)\n\t\t\t{\n\t\t\t\tint n = cls.netchan.incoming_sequence&UPDATE_MASK, o = (cls.netchan.incoming_sequence-1)&UPDATE_MASK;\n\t\t\t\tcl.inframes[n].packet_entities.num_entities = cl.inframes[o].packet_entities.num_entities;\n\t\t\t\tif (cl.inframes[n].packet_entities.max_entities < cl.inframes[o].packet_entities.num_entities)\n\t\t\t\t{\n\t\t\t\t\tcl.inframes[n].packet_entities.max_entities = cl.inframes[o].packet_entities.max_entities;\n\t\t\t\t\tcl.inframes[n].packet_entities.entities = BZ_Realloc(cl.inframes[n].packet_entities.entities, sizeof(entity_state_t) *  cl.inframes[n].packet_entities.max_entities);\n\t\t\t\t}\n\t\t\t\tmemcpy(cl.inframes[n].packet_entities.entities, cl.inframes[o].packet_entities.entities, sizeof(entity_state_t) * cl.inframes[o].packet_entities.num_entities);\n\t\t\t\tcl.inframes[n].packet_entities.servertime = cl.inframes[o].packet_entities.servertime;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n//\t\t\t\tcl.inframes[(cls.netchan.incoming_sequence-1)&UPDATE_MASK].packet_entities = cl.frames[cls.netchan.incoming_sequence&UPDATE_MASK].packet_entities;\n\t\t\t\tcl.inframes[cl.validsequence&UPDATE_MASK].packet_entities.num_entities=0;\n\t\t\t\tcl.inframes[cl.validsequence&UPDATE_MASK].packet_entities.servertime = cl.gametime;\n\t\t\t}\n#ifdef QUAKESTATS\n\t\t\tfor (i = 0; i < cl.splitclients; i++)\n\t\t\t{\n\t\t\t\tcl.inframes[cl.validsequence&UPDATE_MASK].packet_entities.punchangle[i][0] = cl.playerview[i].statsf[STAT_PUNCHANGLE_X];\n\t\t\t\tcl.inframes[cl.validsequence&UPDATE_MASK].packet_entities.punchangle[i][1] = cl.playerview[i].statsf[STAT_PUNCHANGLE_Y];\n\t\t\t\tcl.inframes[cl.validsequence&UPDATE_MASK].packet_entities.punchangle[i][2] = cl.playerview[i].statsf[STAT_PUNCHANGLE_Z];\n\t\t\t\tcl.inframes[cl.validsequence&UPDATE_MASK].packet_entities.punchorigin[i][0] = cl.playerview[i].statsf[STAT_PUNCHVECTOR_X];\n\t\t\t\tcl.inframes[cl.validsequence&UPDATE_MASK].packet_entities.punchorigin[i][1] = cl.playerview[i].statsf[STAT_PUNCHVECTOR_Y];\n\t\t\t\tcl.inframes[cl.validsequence&UPDATE_MASK].packet_entities.punchorigin[i][2] = cl.playerview[i].statsf[STAT_PUNCHVECTOR_Z];\n\t\t\t}\n#endif\n\t\t\tbreak;\n\n\t\tcase svc_updatename:\n\t\t\tSbar_Changed ();\n\t\t\tu = MSG_ReadPlayer ();\n\t\t\tif (u >= MAX_CLIENTS)\n\t\t\t\tMSG_ReadString();\n\t\t\telse\n\t\t\t{\n\t\t\t\tstrcpy(cl.players[u].name, MSG_ReadString());\n\t\t\t\tif (*cl.players[u].name)\n\t\t\t\t\tcl.players[u].userid = u+1;\n\t\t\t\tInfoBuf_SetValueForKey(&cl.players[u].userinfo, \"name\", cl.players[u].name);\n\t\t\t\tif (!cl.nqplayernamechanged)\n\t\t\t\t\tcl.nqplayernamechanged = realtime+2;\n\n\t\t\t\tCLNQ_CheckPlayerIsSpectator(u);\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase svc_updatefrags:\n\t\t\tSbar_Changed ();\n\t\t\tu = MSG_ReadPlayer ();\n\t\t\tif (u >= MAX_CLIENTS)\n\t\t\t\tMSG_ReadShort();\n\t\t\telse\n\t\t\t{\n\t\t\t\tcl.players[u].frags = MSG_ReadShort();\n\t\t\t\tCLNQ_CheckPlayerIsSpectator(u);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase svc_updatecolors:\n\t\t\t{\n\t\t\t\tint a;\n\t\t\t\tu = MSG_ReadPlayer ();\n\t\t\t\ta = MSG_ReadByte ();\n\t\t\t\tif (u < cl.allocated_client_slots)\n\t\t\t\t{\n//\t\t\t\t\tcl.players[u].rtopcolor = a&0x0f;\n//\t\t\t\t\tcl.players[u].rbottomcolor = (a&0xf0)>>4;\n//\t\t\t\t\tsprintf(cl.players[u].team, \"%2d\", cl.players[u].rbottomcolor);\n\n\t\t\t\t\tInfoBuf_SetValueForKey(&cl.players[u].userinfo, \"topcolor\", va(\"%i\", (a&0xf0)>>4));\n\t\t\t\t\tInfoBuf_SetValueForKey(&cl.players[u].userinfo, \"bottomcolor\", va(\"%i\", (a&0x0f)));\n\t\t\t\t\tInfoBuf_SetValueForKey(&cl.players[u].userinfo, \"team\", va(\"%i\", (a&0x0f)+1));\n\t\t\t\t\tCL_ProcessUserInfo (u, &cl.players[u]);\n\n//\t\t\t\t\tCLNQ_CheckPlayerIsSpectator(u);\n\n#ifdef QWSKINS\n\t\t\t\t\tif (cls.state == ca_active)\n\t\t\t\t\t\tSkin_Find (&cl.players[u]);\n\t\t\t\t\tif (u == cl.playerview[destsplit].playernum)\n\t\t\t\t\t\tSkin_FlushPlayers();\n\t\t\t\t\tCL_NewTranslation (u);\n#endif\n\t\t\t\t\tSbar_Changed ();\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\t\tcase svc_lightstyle:\n\t\t\ti = MSG_ReadByte ();\n\t\t\tif (i >= MAX_NET_LIGHTSTYLES)\n\t\t\t{\n\t\t\t\tCon_Printf(\"svc_lightstyle: %i >= MAX_LIGHTSTYLES\\n\", i);\n\t\t\t\tMSG_ReadString();\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tR_UpdateLightStyle(i, MSG_ReadString(), 1, 1, 1);\n\t\t\tbreak;\n\n\t\tcase svcnq_updatestatlong:\n\t\t\ti = MSG_ReadByte ();\n\t\t\tj = MSG_ReadLong ();\n\t\t\tCL_SetStatNumeric (0, i, j, j);\n\t\t\tbreak;\n\t\tcase svcdp_updatestatbyte:\n\t\t//case svcneh_fog:\n\t\t//also svcqex_seq\n\t\t\tif (cls.qex)\n\t\t\t{\t//svcqex_seq\n\t\t\t\tunsigned seq = MSG_ReadULEB128();\n\t\t\t\tif (!cls.demoplayback && 0)\n\t\t\t\t\tCL_AckedInputFrame(cls.netchan.incoming_sequence, seq, true);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (CPNQ_IS_BJP || cls.protocol_nq == CPNQ_NEHAHRA)\n\t\t\t{\n\t\t\t\tCL_ResetFog(FOGTYPE_AIR);\n\t\t\t\tif (MSG_ReadByte())\n\t\t\t\t{\n\t\t\t\t\tcl.fog[FOGTYPE_AIR].density = MSG_ReadFloat();\n\t\t\t\t\tcl.fog[FOGTYPE_AIR].colour[0] = SRGBf(MSG_ReadByte()/255.0f);\n\t\t\t\t\tcl.fog[FOGTYPE_AIR].colour[1] = SRGBf(MSG_ReadByte()/255.0f);\n\t\t\t\t\tcl.fog[FOGTYPE_AIR].colour[2] = SRGBf(MSG_ReadByte()/255.0f);\n\t\t\t\t\tcl.fog[FOGTYPE_AIR].time += 0.25;\t//change fairly fast, but not instantly\n\t\t\t\t}\n\t\t\t\tcl.fog_locked = !!cl.fog[FOGTYPE_AIR].density;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\ti = MSG_ReadByte ();\n\t\t\t\tj = MSG_ReadByte ();\n\t\t\t\tCL_SetStatNumeric (0, i, j, j);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase svcfte_updatestatstring:\n\t\t\ti = MSG_ReadByte();\n\t\t\ts = MSG_ReadString();\n\t\t\tCL_SetStatString (destsplit, i, s);\n\t\t\tbreak;\n\t\tcase svcfte_updatestatfloat:\n\t\t\ti = MSG_ReadByte();\n\t\t\t{\n\t\t\tfloat f = MSG_ReadFloat();\n\t\t\tCL_SetStatNumeric (destsplit, i, f, f);\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase svcfte_setanglebase:\n\t\t\tCL_ParseBaseAngle(destsplit);\n\t\t\tbreak;\n\t\tcase svcfte_setangledelta:\n\t\t\tfor (i=0 ; i<3 ; i++)\n\t\t\t\tang[i] = cl.playerview[destsplit].viewangles[i] + MSG_ReadAngle16 ();\n\t\t\tif (!CSQC_Parse_SetAngles(destsplit, ang, true))\n\t\t\t\tVectorCopy (ang, cl.playerview[destsplit].viewangles);\n\t\t\tVectorCopy (cl.playerview[destsplit].viewangles, cl.playerview[destsplit].simangles);\n\t\t\tVectorCopy (cl.playerview[destsplit].viewangles, cl.playerview[destsplit].intermissionangles);\n\t\t\tbreak;\n\t\tcase svc_setangle:\n\t\t\t{\n\t\t\t\tinframe_t *inf = &cl.inframes[cls.netchan.incoming_sequence&UPDATE_MASK];\n\t\t\t\tif (cls.ezprotocolextensions1 & EZPEXT1_SETANGLEREASON)\n\t\t\t\t\tMSG_ReadByte();\t//0=unknown, 1=tele, 2=spawn\n\t\t\t\tfor (i=0 ; i<3 ; i++)\n\t\t\t\t\tang[i] = MSG_ReadAngle();\n\t\t\t\tif (!CSQC_Parse_SetAngles(destsplit, ang, false))\n\t\t\t\t{\n\t\t\t\t\tinf->packet_entities.fixangles[destsplit] = true;\n\t\t\t\t\tVectorCopy (ang, cl.playerview[destsplit].viewangles);\n\t\t\t\t\tVectorCopy (ang, inf->packet_entities.fixedangles[destsplit]);\n\t\t\t\t}\n\t\t\t\tVectorCopy (cl.playerview[destsplit].viewangles, cl.playerview[destsplit].intermissionangles);\n\t\t\t\tVRUI_SnapAngle();\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase svcnq_clientdata:\n\t\t\tCLNQ_ParseClientdata ();\n\t\t\tbreak;\n\n\t\tcase svc_sound:\n\t\t\tCLNQ_ParseStartSoundPacket();\n\t\t\tbreak;\n\t\tcase svc_stopsound:\n\t\t\ti = MSG_ReadShort();\n\t\t\tS_StopSound(i>>3, i&7);\n\t\t\tbreak;\n\n#ifdef PEXT2_VOICECHAT\n\t\tcase svcfte_voicechat:\n\t\t\tS_Voip_Parse();\n\t\t\tbreak;\n#endif\n\n\t\tcase svc_temp_entity:\n\t\t\tCL_ParseTEnt (true);\n\t\t\tbreak;\n\t\tcase svcfte_temp_entity_sized:\n\t\t\tCL_ParseTEnt_Sized();\n\t\t\tbreak;\n\n\t\tcase svc_particle:\n\t\t\tCLNQ_ParseParticleEffect ();\n\t\t\tbreak;\n\n\t\tcase svc_killedmonster:\n\t\t\tcl.playerview[destsplit].stats[STAT_MONSTERS]++;\n\t\t\tcl.playerview[destsplit].statsf[STAT_MONSTERS]++;\n\t\t\tbreak;\n\n\t\tcase svc_foundsecret:\n\t\t\tcl.playerview[destsplit].stats[STAT_SECRETS]++;\n\t\t\tcl.playerview[destsplit].statsf[STAT_SECRETS]++;\n\t\t\tbreak;\n\n\t\tcase svc_intermission:\n\t\t\tif (cl.intermissionmode == IM_NONE)\n\t\t\t{\n\t\t\t\tTP_ExecTrigger (\"f_mapend\", false);\n\t\t\t\tif (cl.playerview[destsplit].spectator || cls.demoplayback)\n\t\t\t\t\tTP_ExecTrigger (\"f_specmapend\", true);\n\t\t\t\telse\n\t\t\t\t\tTP_ExecTrigger (\"f_playmapend\", true);\n\t\t\t\tcl.completed_time = cl.gametime;\n\t\t\t}\n\t\t\tcl.intermissionmode = IM_NQSCORES;\n\n\t\t\tif (cls.demoseeking == DEMOSEEK_INTERMISSION)\n\t\t\t{\n\t\t\t\tcls.demoseeking = DEMOSEEK_NOT; //reached it.\n\t\t\t\t//FIXME: pause it.\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase svc_finale:\n\t\t\tif (cl.intermissionmode == IM_NONE)\n\t\t\t{\n\t\t\t\tTP_ExecTrigger (\"f_mapend\", false);\n\t\t\t\tif (cl.playerview[destsplit].spectator || cls.demoplayback)\n\t\t\t\t\tTP_ExecTrigger (\"f_specmapend\", true);\n\t\t\t\telse\n\t\t\t\t\tTP_ExecTrigger (\"f_playmapend\", true);\n\t\t\t\tcl.completed_time = cl.gametime;\n\t\t\t}\n\t\t\tcl.intermissionmode = IM_NQFINALE;\n\t\t\tSCR_CenterPrint (destsplit, MSG_ReadString (), false);\n\t\t\tbreak;\n\n\t\tcase svc_cutscene:\n\t\t\tif (cl.intermissionmode == IM_NONE)\n\t\t\t{\n\t\t\t\tTP_ExecTrigger (\"f_mapend\", false);\n\t\t\t\tif (cl.playerview[destsplit].spectator || cls.demoplayback)\n\t\t\t\t\tTP_ExecTrigger (\"f_specmapend\", true);\n\t\t\t\telse\n\t\t\t\t\tTP_ExecTrigger (\"f_playmapend\", true);\n\t\t\t\tcl.completed_time = cl.gametime;\n\t\t\t}\n\t\t\tcl.intermissionmode = IM_NQCUTSCENE;\n\t\t\tSCR_CenterPrint (destsplit, MSG_ReadString (), false);\n\t\t\tbreak;\n\n\t\tcase svc_sellscreen:\t//pantsie\n\t\t\tCmd_ExecuteString (\"help 0\", RESTRICT_SERVER);\n\t\t\tbreak;\n\n\t\tcase svc_damage:\n\t\t\tV_ParseDamage (&cl.playerview[destsplit]);\n\t\t\tbreak;\n\n\t\tcase svcfitz_skybox:\n\t\t\tR_SetSky(MSG_ReadString());\n\t\t\tbreak;\n\t\tcase svcfitz_bf:\n\t\t\tCmd_ExecuteString(\"bf\", RESTRICT_SERVER);\n\t\t\tbreak;\n\t\tcase svcfitz_fog:\n\t\t\tCL_ResetFog(FOGTYPE_AIR);\n\t\t\tcl.fog[FOGTYPE_AIR].density = MSG_ReadByte()/255.0f;\n\t\t\tcl.fog[FOGTYPE_AIR].colour[0] = SRGBf(MSG_ReadByte()/255.0f);\n\t\t\tcl.fog[FOGTYPE_AIR].colour[1] = SRGBf(MSG_ReadByte()/255.0f);\n\t\t\tcl.fog[FOGTYPE_AIR].colour[2] = SRGBf(MSG_ReadByte()/255.0f);\n\t\t\tcl.fog[FOGTYPE_AIR].time += ((unsigned short)MSG_ReadShort()) / 100.0;\n\t\t\tcl.fog_locked = !!cl.fog[FOGTYPE_AIR].density;\n\t\t\tbreak;\n\t\tcase svcfitz_spawnbaseline2:\n\t\t\ti = MSGCL_ReadEntity ();\n\t\t\tif (!CL_CheckBaselines(i))\n\t\t\t\tHost_EndGame(\"CLNQ_ParseServerMessage: svcfitz_spawnbaseline2 failed with ent %i\", i);\n\t\t\tCL_ParseBaseline (cl_baselines + i, CPNQ_FITZ666);\n\t\t\tbreak;\n\t\tcase svcfitz_spawnstatic2:\n\t\t\tCL_ParseStaticProt (CPNQ_FITZ666);\n\t\t\tbreak;\n\t\tcase svcfitz_spawnstaticsound2:\n\t\t\tCL_ParseStaticSound(true);\n\t\t\tbreak;\n\n\n\t\tcase svcnq_effect:\n\t\t//also svcqex_achievement\n\t\t\tif (cls.qex)\n\t\t\t{\t//svcqex_achievement\n\t\t\t\tMSG_ReadString();\n\t\t\t\tbreak;\n\t\t\t}\n#ifdef HAVE_LEGACY\n\t\t\tif (!memcmp(net_message.data+cmdstart+1, \"ACH_\", 4))\n\t\t\t{\t//HIDEOUS UGLY HACK!\n\t\t\t\tint l = 0;\n\t\t\t\tchar *s = net_message.data+cmdstart+5;\n\t\t\t\twhile (*s)\n\t\t\t\t{\n\t\t\t\t\tif ((*s >= 'A' && *s <= 'Z') || *s == '_')\n\t\t\t\t\t{\n\t\t\t\t\t\ts++;\n\t\t\t\t\t\tl++;\n\t\t\t\t\t}\n\t\t\t\t\telse break;\n\t\t\t\t}\n\t\t\t\tif (!*s && l >= 8) //'ACH_PACIFIST' seems the shortest existing one.\n\t\t\t\t{\t//got to the end of the string and found only capitals... good chance its qe debris\n\t\t\t\t\ts = MSG_ReadString();\n\t\t\t\t\tCon_Printf(CON_WARNING \"Got svcnq_effect - assuming stray svcqe_achievement(%s)\\n\", s);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n#endif\n\t\t\tCL_ParseEffect(false);\n\t\t\tbreak;\n\t\tcase svcnq_effect2:\n\t\t//also svcqex_chat\n\t\t\tif (cls.qex)\n\t\t\t{\t//svcqex_chat\n\t\t\t\t//in qex this text is in some small special chat box. which disappears quickly rendering its contents kinda unreadable. and its messagemode stuff seems broken too, so whatever. and it seems to have newline issues.\n\t\t\t\t//FIXME: figure out the player index so we can kickban/mute/etc them.\n\t\t\t\tqbyte plcolour = MSG_ReadByte();\n\t\t\t\tqbyte chatcolour = MSG_ReadByte();\n\t\t\t\tchar *pcols[] = {S_COLOR_WHITE,S_COLOR_GREEN,S_COLOR_CYAN, S_COLOR_YELLOW};\n\t\t\t\tchar *ccols[] = {S_COLOR_WHITE,S_COLOR_CYAN};\t//say, say_team\n\t\t\t\tCon_Printf(\"^[%s%s\"/*\"\\\\player\\\\%i\"*/\"^]: \", pcols[plcolour%countof(pcols)], MSG_ReadString());\n\t\t\t\tCon_Printf(\"%s%s\\n\", ccols[chatcolour%countof(ccols)], MSG_ReadString());\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tCL_ParseEffect(true);\n\t\t\tbreak;\n\n\t\tcase svcdp_entities:\n\t\t\tif (cls.qex)\n\t\t\t{\t//svcqex_prompt\n\t\t\t\tCLQEX_ParsePrompt();\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (cls.signon == 4 - 1)\n\t\t\t{\t// first update is the final signon stage\n\t\t\t\tcls.signon = 4;\n\t\t\t\tCLNQ_SignonReply ();\n\t\t\t}\n\t\t\t//well, it's really any protocol, but we're only going to support version 5 (through 7).\n\t\t\tCLDP_ParseDarkPlaces5Entities();\n\t\t\tbreak;\n\t\tcase svcdp_spawnbaseline2:\n\t\t\ti = MSGCL_ReadEntity ();\n\t\t\tif (!CL_CheckBaselines(i))\n\t\t\t\tHost_EndGame(\"CLNQ_ParseServerMessage: svcdp_spawnbaseline2 failed with ent %i\", i);\n\t\t\tCL_ParseBaseline (cl_baselines + i, CPNQ_DP5);\n\t\t\tbreak;\n\n\t\tcase svcdp_spawnstatic2:\n\t\t\tCL_ParseStaticProt (CPNQ_DP5);\n\t\t\tbreak;\n\t\tcase svcdp_spawnstaticsound2:\n\t\t\tCL_ParseStaticSound(true);\n\t\t\tbreak;\n\n#ifdef CSQC_DAT\n\t\tcase svcdp_csqcentities:\n\t\t\tif (cls.qex)\n\t\t\t{\n\t\t\t\ts = CLQEX_ReadStrings();\n\t\t\t\tgoto svccentreprint;\n\t\t\t}\n\t\t\tCSQC_ParseEntities(false);\n\t\t\tbreak;\n\t\tcase svcfte_csqcentities_sized:\n\t\t\tCSQC_ParseEntities(true);\n\t\t\tbreak;\n#endif\n\n\t\tcase svcdp_downloaddata:\n\t\t//also svcqex_servervars:\n\t\t\tif (cls.qex)\n\t\t\t\tCLQEX_ParseServerVars();\n\t\t\telse\n\t\t\t\tCLDP_ParseDownloadData();\n\t\t\tbreak;\n\n\t\tcase svcdp_trailparticles:\n\t\t\tCL_ParseTrailParticles();\n\t\t\tbreak;\n\t\tcase svcdp_pointparticles:\n\t\t\tCL_ParsePointParticles(false);\n\t\t\tbreak;\n\t\tcase svcdp_pointparticles1:\n\t\t\tCL_ParsePointParticles(true);\n\t\t\tbreak;\n\n\t\tcase svcqex_updateping:\n\t\t\tif (cls.qex)\n\t\t\t{\t//svcqex_updateping\n\t\t\t\tint ping;\n\t\t\t\ti = MSG_ReadByte();\n\t\t\t\tping = MSG_ReadSignedQEX();\n\t\t\t\tif (i < MAX_CLIENTS)\n\t\t\t\t\tcl.players[i].ping = ping;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tgoto badsvc;\n\t\tcase svcqex_updatesocial:\n\t\t\tif (cls.qex)\n\t\t\t{\t//svcqex_updatesocial\n\t\t\t\t//both ints are -1 for lan/direct clients, and 0 for the host. I guess this is for chatting to people via steam.\n\t\t\t\t/*slot =*/ MSG_ReadByte();\n\t\t\t\t/*??? =*/ MSG_ReadLong();\n\t\t\t\t/*??? =*/ MSG_ReadLong();\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tgoto badsvc;\n\n\t\tcase svcqex_updateplinfo:\n\t\t\tif (cls.qex)\n\t\t\t{\t//svcqex_updateplinfo\n\t\t\t\tunsigned int slot = MSG_ReadByte();\n\t\t\t\tint health = MSG_ReadSignedQEX();\n\t\t\t\tint armour = MSG_ReadSignedQEX();\n\t\t\t\tif (slot < MAX_CLIENTS)\n\t\t\t\t{\n\t\t\t\t\tInfoBuf_SetValueForKey(&cl.players[slot].userinfo, \"health\", va(\"%i\", health));\n\t\t\t\t\tInfoBuf_SetValueForKey(&cl.players[slot].userinfo, \"health\", va(\"%i\", armour));\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tgoto badsvc;\n\t\tcase svcqex_locprint:\n\t\t\tif (cls.qex)\n\t\t\t{\t//svcqex_'raw'print\n\t\t\t\ts = CLQEX_ReadStrings();\n\t\t\t\tgoto svcprint;\n\t\t\t}\n\t\t\tgoto badsvc;\n\t\t}\n\n\t\tpacketusage_pending[cmd] += MSG_GetReadCount()-cmdstart;\n\t}\n}\n#endif\n\nstruct sortedsvcs_s\n{\n\tconst char *name;\n\tsize_t bytes;\n};\nstatic int QDECL sorttraffic(const void *l, const void *r)\n{\n\tconst struct sortedsvcs_s *a=l, *b=r;\n\n\tif (a->bytes==b->bytes)\n\t\treturn 0;\n\tif (a->bytes>b->bytes)\n\t\treturn -1;\n\treturn 1;\n}\nvoid CL_ShowTrafficUsage(float x, float y)\n{\n\tconst char **svcnames, *n;\n\tsize_t svccount, i, j=0;\n\tsize_t total;\n\tstruct sortedsvcs_s sorted[256];\n\tswitch(cls.protocol)\n\t{\n#ifdef NQPROT\n\tcase CP_NETQUAKE:\n\t\tsvcnames = svc_nqstrings;\n\t\tsvccount = countof(svc_nqstrings);\n\t\tbreak;\n#endif\n\tcase CP_QUAKEWORLD:\n\t\tsvcnames = svc_qwstrings;\n\t\tsvccount = countof(svc_qwstrings);\n\t\tbreak;\n\tdefault:\n\t\treturn;\t//panic!\n\t}\n\ttotal = 0;\n\tfor (i = 0; i < 256; i++)\n\t\ttotal += packetusage_saved[i];\n\tfor (i = 0; i < 256; i++)\n\t{\n\t\tif (!packetusage_saved[i])\n\t\t\tcontinue;\t//don't show if there's no point.\n\t\tif (i < svccount)\n\t\t\tn = svcnames[i];\n\t\telse\n\t\t\tn = va(\"svc %u\", (unsigned)i);\n\t\tsorted[j].name = n;\n\t\tsorted[j].bytes = packetusage_saved[i];\n\t\tj++;\n\t}\n\tqsort(sorted, j, sizeof(*sorted), sorttraffic);\n\n\tfor (i = 0; i < j; i++)\n\t{\n\t\tDraw_FunString(x, y, va(\"%22s:%5.1f%% (%.0f/s)\", sorted[i].name, (100.0*sorted[i].bytes)/total, (sorted[i].bytes/packetusage_interval)));\n\t\ty+=8;\n\t}\n}\n"
  },
  {
    "path": "engine/client/cl_plugin.inc",
    "content": "//included directly from plugin.c\n//this is the client-only things.\n\nstatic plugin_t *protocolclientplugin;\n\n\nstatic void PlugMenu_Close(menu_t *m, qboolean forced)\n{\n\tZ_Free(m);\n}\nstatic qboolean PlugMenu_Event(menu_t *m, int eventtype, int keyparam, int unicodeparam)\t//eventtype = draw/keydown/keyup, param = time/key\n{\n\tplugin_t *oc=currentplug;\n\tqboolean ret;\n\n\tcurrentplug = m->ctx;\n\tret = currentplug->menufunction(eventtype, keyparam, unicodeparam, mousecursor_x, mousecursor_y, vid.width, vid.height);\n\tcurrentplug=oc;\n\treturn ret;\n}\nstatic qboolean PlugMenu_KeyEvent(menu_t *m, qboolean isdown, unsigned int devid, int key, int unicode)\n{\n\treturn PlugMenu_Event(m, isdown?1:2, key, unicode);\n}\nstatic void PlugMenu_Draw(menu_t *m)\n{\n\tPlugMenu_Event (m, 0, (realtime*1000), 0);\n}\nstatic qboolean QDECL Plug_SetMenuFocus (qboolean wantkeyfocus, const char *cursorname, float hot_x, float hot_y, float scale) //null cursorname=relmouse, set/empty cursorname=absmouse\n{\n\tmenu_t *m;\n\tif (qrenderer == QR_NONE)\n\t\treturn false;\n\n\tm = Menu_FindContext(currentplug);\n\n\tif (wantkeyfocus)\n\t{\n\t\tif (!m)\n\t\t{\n\t\t\tm = Z_Malloc(sizeof(*m));\n\t\t\tm->ctx = currentplug;\n\t\t\tm->cursor = &key_customcursor[kc_plugin];\n\t\t\tm->release = PlugMenu_Close;\n\t\t\tm->keyevent = PlugMenu_KeyEvent;\n\t\t\tm->drawmenu = PlugMenu_Draw;\n\t\t\tMenu_Push(m, false);\n\t\t}\n\t}\n\telse if (m)\n\t\tMenu_Unlink(m, false);\n\n\tif (wantkeyfocus)\n\t{\n\t\tstruct key_cursor_s *mc = &key_customcursor[kc_plugin];\n\n\t\tif (cursorname)\n\t\t{\n\t\t\tif (scale <= 0)\n\t\t\t\tscale = 1;\n\t\t\tif (strcmp(cursorname, mc->name) || mc->hotspot[0] != hot_x || mc->hotspot[1] != hot_y || mc->scale != scale)\n\t\t\t{\n\t\t\t\tQ_strncpyz(mc->name, cursorname, sizeof(mc->name));\n\t\t\t\tmc->hotspot[0] = hot_x;\n\t\t\t\tmc->hotspot[1] = hot_y;\n\t\t\t\tmc->scale = scale;\n\t\t\t\tmc->dirty = true;\n\t\t\t}\n\t\t}\n\t}\n\treturn true;\n}\nstatic qboolean QDECL Plug_HasMenuFocus(void)\n{\n\treturn topmenu&&topmenu->ctx==currentplug && Key_Dest_Has(kdm_menu);\n}\n\nstatic int QDECL Plug_Key_GetKeyCode(const char *keyname, int *modifier)\n{\n\tint modifier_;\n\tif (!modifier)\n\t\tmodifier = &modifier_;\n\treturn Key_StringToKeynum(keyname, modifier);\n}\nstatic const char *QDECL Plug_Key_GetKeyName(int keycode, int modifier)\n{\n\treturn Key_KeynumToString(keycode, modifier);\n}\n\nconst char *QDECL Plug_Key_GetKeyBind(int bindmap, int keynum, int modifier)\n{\n\treturn Key_GetBinding(keynum, bindmap, modifier);\n}\nvoid QDECL Plug_Key_SetKeyBind(int bindmap, int keycode, int modifier, const char *newbinding)\n{\n\tif (bindmap && !modifier)\n\t\tmodifier = (bindmap-1) | KEY_MODIFIER_ALTBINDMAP;\n\tKey_SetBinding (keycode, modifier, newbinding, RESTRICT_LOCAL);\n}\n\nstatic unsigned int IN_GetKeyDest(void)\n{\n\treturn key_dest_mask;\n}\n\nqboolean QDECL Plug_Input_IsKeyDown(int key)\n{\n\textern unsigned int keydown[K_MAX];\n\tif (key >= 0 && key < K_MAX)\n\t\treturn !!keydown[key];\n\treturn false;\n}\nvoid QDECL Plug_Input_ClearKeyStates(void)\n{\n\tKey_ClearStates();\n}\nvoid QDECL Plug_Input_SetSensitivityScale(float scale)\n{\n\tin_sensitivityscale = scale;\n}\nunsigned int QDECL Plug_Input_GetMoveCount(void)\n{\n\treturn cl.movesequence;\n}\nusercmd_t *QDECL Plug_Input_GetMoveEntry(unsigned int move)\n{\n\tif (move == cl.movesequence)\n\t\treturn NULL;\t//the partial\n\telse if (move >= cl.movesequence)\n\t\treturn NULL;\t//too new\n\telse if (cl.outframes[move&UPDATE_MASK].cmd_sequence != move)\n\t\treturn NULL;\t//too old or otherwise missing\n\telse\n\t\treturn &cl.outframes[move&UPDATE_MASK].cmd[0];\n}\n\n/*\n\nstatic void QDECL Plug_SCR_CenterPrint(int seat, const char *text)\n{\n\tif (qrenderer != QR_NONE)\n\t\tSCR_CenterPrint(seat, text, true);\n}\n*/\n\n\n\n#include \"shader.h\"\n\nstatic qboolean Plug_Draw_GetScreenSize(float *vsize, unsigned int *psize)\n{\n\tif (qrenderer<=0)\n\t\treturn false;\n\tif (vsize)\n\t\tvsize[0] = vid.width, vsize[1] = vid.height;\n\tif (psize)\n\t\tpsize[0] = vid.pixelwidth, psize[1] = vid.pixelheight;\n\treturn true;\n}\n\nstatic qhandle_t Plug_Draw_LoadImage(const char *name, int type, const char *script)\n{\n\tshader_t *pic;\n\tif (qrenderer != QR_NONE)\n\t{\n\t\tif (type == 3)\n\t\t\tpic = NULL;\n\t\telse if (type == 2)\n\t\t\tpic = R_RegisterShader(name, SUF_NONE, script);\n\t\telse\n\t\t\tpic = R2D_SafeCachePic(name);\n\t}\n\telse\n\t\tpic = NULL;\n\n\tif (pic)\n\t\treturn pic->id+1;\n\treturn 0;\n}\n\nstatic qhandle_t QDECL Plug_Draw_LoadImageData(const char *name, const char *mimetype, void *codeddata, size_t datalength)\n{\n\tqhandle_t ret = 0;\n\timage_t *t;\n\tqbyte *rgbdata;\n\tunsigned int width, height;\n\tuploadfmt_t format;\n\t\t\n\tif ((rgbdata = ReadRawImageFile(codeddata, datalength, &width, &height, &format, false, name)))\n\t{\n\t\tt = Image_FindTexture(name, NULL, IF_PREMULTIPLYALPHA|IF_NOMIPMAP|IF_UIPIC|IF_CLAMP);\n\t\tif (!TEXVALID(t))\n\t\t\tt = Image_CreateTexture(name, NULL, IF_PREMULTIPLYALPHA|IF_NOMIPMAP|IF_UIPIC|IF_CLAMP);\n\t\tif (TEXVALID(t))\n\t\t{\n\t\t\tImage_Upload(t, format, rgbdata, NULL, width, height, 1, IF_PREMULTIPLYALPHA|IF_NOMIPMAP|IF_UIPIC|IF_CLAMP);\n\t\t\tret = Plug_Draw_LoadImage(name, 3, NULL);\n\t\t}\n\t\t\n\t\tBZ_Free(rgbdata);\n\t}\n\treturn ret;\n}\nstatic qhandle_t QDECL Plug_Draw_LoadImageShader(const char *name, const char *script)\n{\n\treturn Plug_Draw_LoadImage(name, 2, script);\n}\nstatic qhandle_t QDECL Plug_Draw_LoadImagePic(const char *name)\n{\n\treturn Plug_Draw_LoadImage(name, 0, NULL);\n}\nstatic shader_t *Plug_Draw_ShaderFromId(qhandle_t id)\n{\n\tif (--id >= r_numshaders)\n\t\treturn NULL;\n\treturn r_shaders[id];\n}\nstatic void Plug_Draw_UnloadImage(qhandle_t id)\n{\n\tR_UnloadShader(Plug_Draw_ShaderFromId(id));\n}\n\nstatic int QDECL Plug_Draw_ImageSize(qhandle_t image, float *w, float *h)\n{\n\tint iw, ih, ret;\n\n\tif (image > 0 && image <= r_numshaders)\n\t{\n\t\tret = R_GetShaderSizes(r_shaders[image-1], &iw, &ih, true);\n\t\tif (w)\n\t\t\t*w = iw;\n\t\tif (h)\n\t\t\t*h = ih;\n\t\treturn ret;\n\t}\n\treturn -1;\n}\n\nstatic int QDECL Plug_Draw_Image(float x, float y, float w, float h, float s1, float t1, float s2, float t2, qhandle_t image)\n{\n\tif (image > 0 && image <= r_numshaders)\n\t{\n\t\tR2D_Image(x, y, w, h, s1, t1, s2, t2, r_shaders[image-1]);\n\t\treturn 1;\n\t}\n\treturn 0;\n}\nstatic int QDECL Plug_Draw_Image2dQuad(const vec2_t *points, const vec2_t *texcoords, const vec4_t *colours, qhandle_t image)\n{\n\tif (image > 0 && image <= r_numshaders)\n\t{\n\t\tR2D_Image2dQuad(points, texcoords, colours, r_shaders[image-1]);\n\t\treturn 1;\n\t}\n\treturn 0;\n}\n//x1,y1,x2,y2\nstatic void QDECL Plug_Draw_Line(float x1, float y1, float x2, float y2)\n{\n\tR2D_Line(x1,y1, x2,y2, NULL);\n}\nstatic void QDECL Plug_Draw_Character(float x, float y, unsigned int character)\n{\n\tfloat px, py;\n\tif (qrenderer == QR_NONE)\n\t\treturn;\n\tFont_BeginScaledString(font_default, x, y, 8, 8, &px, &py);\n\tFont_DrawScaleChar(px, py, CON_WHITEMASK, character);\n\tFont_EndString(font_default);\n}\nstatic void QDECL Plug_Draw_CharacterH(float x, float y, float h, unsigned int flags, unsigned int charc)\n{\n\tconchar_t cmask = CON_WHITEMASK;\n\tif (qrenderer == QR_NONE)\n\t\treturn;\n\tif (flags & 1)\n\t\tcmask |= CON_2NDCHARSETTEXT;\n\tif (!(flags & 2))\n\t\tcmask |= 0xe000;\n\tFont_BeginScaledString(font_default, x, y, h, h, &x, &y);\n\tFont_DrawScaleChar(x, y, cmask, charc);\n\tFont_EndString(font_default);\n}\nstatic void QDECL Plug_Draw_String(float x, float y, const char *string)\n{\n\tint ipx, px, py;\n\tconchar_t buffer[2048], *str;\n\tunsigned int codeflags, codepoint;\n\tif (qrenderer == QR_NONE)\n\t\treturn;\n\tCOM_ParseFunString(CON_WHITEMASK, string, buffer, sizeof(buffer), false);\n\tstr = buffer;\n\tFont_BeginString(font_default, x, y, &px, &py);\n\tipx = px;\n\twhile(*str)\n\t{\n\t\tstr = Font_Decode(str, &codeflags, &codepoint);\n\t\tif (codepoint == '\\n')\n\t\t\tpy += Font_CharHeight();\n\t\telse if (codepoint == '\\r')\n\t\t\tpx = ipx;\n\t\telse\n\t\t\tpx = Font_DrawChar(px, py, codeflags, codepoint);\n\t}\n\tFont_EndString(font_default);\n}\nstatic void QDECL Plug_Draw_StringH(float x, float y, float h, unsigned int flags, const char *instr)\n{\n\tfloat ipx;\n\tconchar_t buffer[2048], *str, cmask = CON_WHITEMASK;\n\tunsigned int codeflags, codepoint;\n\tunsigned int parseflags = 0;\n\tif (qrenderer == QR_NONE)\n\t\treturn;\n\tif (flags & 1)\n\t\tcmask |= CON_2NDCHARSETTEXT;\n\tif (flags & 2)\n\t\tparseflags |= PFS_FORCEUTF8;\n\tCOM_ParseFunString(CON_WHITEMASK, instr, buffer, sizeof(buffer), parseflags);\n\tstr = buffer;\n\tFont_BeginScaledString(font_default, x, y, h, h, &x, &y);\n\tipx = x;\n\twhile(*str)\n\t{\n\t\tstr = Font_Decode(str, &codeflags, &codepoint);\n\t\tif (codepoint == '\\n')\n\t\t\ty += Font_CharScaleHeight();\n\t\telse if (codepoint == '\\r')\n\t\t\tx = ipx;\n\t\telse\n\t\t\tx = Font_DrawScaleChar(x, y, codeflags, codepoint);\n\t}\n\tFont_EndString(font_default);\n}\n\nstatic float QDECL Plug_Draw_StringWidth(float h, unsigned int flags, const char *instr)\n{\n\tconchar_t buffer[2048], *str, cmask = CON_WHITEMASK;\n\tunsigned int parseflags = 0;\n\tfloat px,py;\n\tif (qrenderer == QR_NONE)\n\t\treturn 0;\n\tif (flags & 1)\n\t\tcmask |= CON_2NDCHARSETTEXT;\n\tif (flags & 2)\n\t\tparseflags |= PFS_FORCEUTF8;\n\tstr = COM_ParseFunString(CON_WHITEMASK, instr, buffer, sizeof(buffer), parseflags);\n\t\n\tFont_BeginScaledString(font_default, 0, 0, h, h, &px, &py);\n\tpx = Font_LineScaleWidth(buffer, str);\n\tFont_EndString(NULL);\n\t\n\t//put it back in virtual space\n\treturn (px*(float)vid.width) / (float)vid.rotpixelwidth;\n}\n\nstatic void QDECL Plug_Draw_Fill(float x, float y, float width, float height)\n{\n\tif (qrenderer != QR_NONE)\n\t\tR2D_FillBlock(x, y, width, height);\n}\nstatic void QDECL Plug_Draw_ColourP(int palcol, float a)\n{\n\tif (palcol>=0 && palcol<=255)\n\t\tR2D_ImagePaletteColour(palcol, a);\n}\nstatic void QDECL Plug_Draw_Colour4f(float r, float g, float b, float a)\n{\n\tR2D_ImageColours(r,g,b,a);\n}\n\nstatic void QDECL Plug_Draw_RedrawScreen(void)\n{\n\tSCR_UpdateScreen();\n}\n\n#ifdef HAVE_MEDIA_DECODER\nstatic void QDECL Plug_Media_SetState(cin_t *cin, int state)\n{\n\tMedia_SetState(cin, state);\n}\nstatic int QDECL Plug_Media_GetState(cin_t *cin)\n{\n\treturn Media_GetState(cin);\n}\n#endif\n\nstatic qhandle_t Plug_Scene_ModelToId(model_t *mod)\n{\n\tif (!mod)\n\t\treturn 0;\n\treturn (mod-mod_known)+1;\n}\nstatic model_t *Plug_Scene_ModelFromId(qhandle_t id)\n{\n\textern int mod_numknown;\n\tif ((unsigned)(--id) >= mod_numknown)\n\t\treturn NULL;\n\treturn mod_known+id;\n}\nstatic qhandle_t Plug_Scene_ShaderForSkin(qhandle_t modelid, int surfaceidx, int skinnum, float time)\n{\n\tshader_t *s = Mod_ShaderForSkin(Plug_Scene_ModelFromId(modelid), surfaceidx, skinnum, time, NULL);\n\treturn s->id+1;\n}\nstatic void QDECL Plug_Scene_Clear(void)\n{\n\tCL_ClearEntityLists();\n\trtlights_first = RTL_FIRST;\n}\nstatic unsigned int Plug_Scene_AddPolydata(struct shader_s *s, unsigned int beflags, size_t numverts, size_t numidx, vecV_t **vertcoord, vec2_t **texcoord, vec4_t **colour, index_t **indexes)\n{\n\tunsigned int ret;\n\tscenetris_t *t;\n\n\t/*reuse the previous trigroup if its the same shader*/\n\tif (cl_numstris && cl_stris[cl_numstris-1].shader == s && cl_stris[cl_numstris-1].flags == beflags)\n\t\tt = &cl_stris[cl_numstris-1];\n\telse\n\t{\n\t\tif (cl_numstris == cl_maxstris)\n\t\t{\n\t\t\tcl_maxstris += 8;\n\t\t\tcl_stris = BZ_Realloc(cl_stris, sizeof(*cl_stris)*cl_maxstris);\n\t\t}\n\t\tt = &cl_stris[cl_numstris++];\n\t\tt->shader = s;\n\t\tt->flags = beflags;\n\t\tt->numidx = 0;\n\t\tt->numvert = 0;\n\t\tt->firstidx = cl_numstrisidx;\n\t\tt->firstvert = cl_numstrisvert;\n\t}\n\tret = cl_numstrisvert - t->firstvert;\n\n\tif (cl_maxstrisvert < cl_numstrisvert+numverts)\n\t\tcl_stris_ExpandVerts(cl_numstrisvert+numverts + 64);\n\tif (cl_maxstrisidx < cl_numstrisidx+numidx)\n\t{\n\t\tcl_maxstrisidx = cl_numstrisidx+numidx + 64;\n\t\tcl_strisidx = BZ_Realloc(cl_strisidx, sizeof(*cl_strisidx)*cl_maxstrisidx);\n\t}\n\n\t*vertcoord = cl_strisvertv+cl_numstrisvert;\n\t*texcoord = cl_strisvertt+cl_numstrisvert;\n\t*colour = cl_strisvertc+cl_numstrisvert;\n\t*indexes = cl_strisidx+cl_numstrisidx;\n\n\tt->numvert += numverts;\n\tt->numidx += numidx;\n\tcl_numstrisvert += numverts;\n\tcl_numstrisidx += numidx;\n\n\treturn ret;\n}\n\nvoid R_DrawNameTags(void);\nstatic void Plug_Scene_RenderScene(plugrefdef_t *in, size_t areabytes, const qbyte *areadata)\n{\n\tsize_t i;\n\textern cvar_t r_torch;\n\tif (R2D_Flush)\n\t\tR2D_Flush();\n\n\tVectorCopy(in->viewaxisorg[0], r_refdef.viewaxis[0]);\n\tVectorCopy(in->viewaxisorg[1], r_refdef.viewaxis[1]);\n\tVectorCopy(in->viewaxisorg[2], r_refdef.viewaxis[2]);\n\tVectorCopy(in->viewaxisorg[3], r_refdef.vieworg);\n\n\tVectorSet(r_refdef.weaponmatrix[0], 1,0,0);\n\tVectorSet(r_refdef.weaponmatrix[1], 0,1,0);\n\tVectorSet(r_refdef.weaponmatrix[2], 0,0,1);\n\tVectorSet(r_refdef.weaponmatrix[3], 0,0,0);\n\n\tVectorSet(r_refdef.weaponmatrix_bob[0], 1,0,0);\n\tVectorSet(r_refdef.weaponmatrix_bob[1], 0,1,0);\n\tVectorSet(r_refdef.weaponmatrix_bob[2], 0,0,1);\n\tVectorSet(r_refdef.weaponmatrix_bob[3], 0,0,0);\n\n\tVectorAngles(r_refdef.viewaxis[0], r_refdef.viewaxis[2], r_refdef.viewangles, false);\t//do we actually still need this?\n\tr_refdef.flags = in->flags;\n\tr_refdef.fov_x = in->fov[0];\n\tr_refdef.fov_y = in->fov[1];\n\tr_refdef.fovv_x = in->fov_viewmodel[0];\n\tr_refdef.fovv_y = in->fov_viewmodel[1];\n\tr_refdef.vrect.x = in->rect.x;\n\tr_refdef.vrect.y = in->rect.y;\n\tr_refdef.vrect.width = in->rect.w;\n\tr_refdef.vrect.height = in->rect.h;\n\tr_refdef.time = in->time;\n\tr_refdef.useperspective = true;\n\tr_refdef.mindist = bound(0.1, gl_mindist.value, 4);\n\tr_refdef.maxdist = gl_maxdist.value;\n\tr_refdef.playerview = &cl.playerview[0];\n\n\tif (in->flags & RDF_SKYROOMENABLED)\n\t{\n\t\tr_refdef.skyroom_enabled = true;\n\t\tVectorCopy(in->skyroom_org, r_refdef.skyroom_pos);\n\t}\n\telse\n\t\tr_refdef.skyroom_enabled = false;\n\n\tif (r_refdef.vrect.y < 0)\n\t{\t//evil hack to work around player model ui bug.\n\t\t//if the y coord is off screen, reduce the height to keep things centred, and reduce the fov to compensate.\n\t\tr_refdef.vrect.height += r_refdef.vrect.y*2;\n\t\tr_refdef.fov_y = in->fov[1] * r_refdef.vrect.height / in->rect.h;\n\t\tr_refdef.fovv_y = in->fov_viewmodel[1] * r_refdef.vrect.height / in->rect.h;\n\t\tr_refdef.vrect.y = 0;\n\t}\n\n\tmemset(&r_refdef.globalfog, 0, sizeof(r_refdef.globalfog));\n\n\tif (r_torch.ival)\n\t{\n\t\tdlight_t *dl;\n\t\tdl = CL_NewDlight(0, r_refdef.vieworg, 300, r_torch.ival, 0.5, 0.5, 0.2);\n\t\tdl->flags |= LFLAG_SHADOWMAP|LFLAG_FLASHBLEND;\n\t\tdl->fov = 60;\n\t\tVectorCopy(r_refdef.viewaxis[0], dl->axis[0]);\n\t\tVectorCopy(r_refdef.viewaxis[1], dl->axis[1]);\n\t\tVectorCopy(r_refdef.viewaxis[2], dl->axis[2]);\n\t}\n\n\tr_refdef.areabitsknown = areabytes>0;\n\tfor (i = 0; i < sizeof(r_refdef.areabits)/sizeof(int) && i < areabytes/sizeof(int); i++)\n\t\t((int*)r_refdef.areabits)[i] = ((int*)areadata)[i] ^ ~0;\n\tR_PushDlights();\n\tR_RenderView();\n\tR_DrawNameTags();\n\tr_refdef.playerview = NULL;\n\tr_refdef.time = 0;\n}\n\nstatic void QDECL Plug_LocalSound(const char *soundname, int channel, float volume)\n{\n\tif (qrenderer != QR_NONE)\n\t\tS_LocalSound2(soundname, channel, volume);\n}\n\n\nstatic void QDECL Plug_CL_SetLoadscreenState(qboolean state)\n{\n\tif (state)\n\t\tSCR_BeginLoadingPlaque();\n\telse\n\t\tSCR_EndLoadingPlaque();\n}\n\nstatic int QDECL Plug_CL_GetStats(int pnum, unsigned int *stats, int maxstats)\n{\n\tint i = 0;\n\tint max;\n\n\tif (qrenderer == QR_NONE || !cls.state)\n\t\treturn 0;\n\n\tmax = maxstats;\n\tif (max > MAX_CL_STATS)\n\t\tmax = MAX_CL_STATS;\n\tif (pnum < 0)\n\t{\n\t\tpnum = -pnum-1;\n\t\tif (pnum < MAX_CLIENTS)\n\t\t{\n\t\t\tfor (i = 0; i < max; i++)\n\t\t\t\tstats[i] = cl.players[pnum].stats[i];\n\t\t}\n\t}\n\telse if (pnum < cl.splitclients)\n\t{\n\t\tfor (i = 0; i < max; i++)\n\t\t{\t//fill stats with the right player's stats\n\t\t\tstats[i] = cl.playerview[pnum].stats[i];\n\t\t}\n\t}\n\n\tmax = i;\n\tfor (; i < maxstats; i++)\t//plugin has too many stats (wow)\n\t\tstats[i] = 0;\t\t\t\t\t//fill the rest.\n\treturn max;\n}\n\nstatic void QDECL Plug_GetPlayerInfo(int playernum, plugclientinfo_t *out)\n{\n\tint i;\n\n\t//queries for the local seats\n\tif (playernum < 0)\n\t\tplayernum = cl.playerview[-playernum-1].playernum;\n\n\tif (playernum < 0 || playernum >= MAX_CLIENTS)\n\t{\n\t\tmemset(out, 0, sizeof(*out));\n\t\treturn;\n\t}\n\n\ti = playernum;\n\tif (out)\n\t{\n\t\tout->bottomcolour = cl.players[i].rbottomcolor;\n\t\tout->topcolour = cl.players[i].rtopcolor;\n\t\tout->frags = cl.players[i].frags;\n\t\tQ_strncpyz(out->name, cl.players[i].name, PLUGMAX_SCOREBOARDNAME);\n\t\tout->ping = cl.players[i].ping;\n\t\tout->pl = cl.players[i].pl;\n\t\tout->activetime = realtime - cl.players[i].realentertime;\n\t\tout->userid = cl.players[i].userid;\n\t\tout->spectator = cl.players[i].spectator;\n\t\tInfoBuf_ToString(&cl.players[i].userinfo, out->userinfo, sizeof(out->userinfo), basicuserinfos, NULL, NULL, NULL, NULL);\n\t\tQ_strncpyz(out->team, cl.players[i].team, sizeof(out->team));\n\t}\n}\n\nstatic size_t QDECL Plug_GetLocalPlayerNumbers(size_t first, size_t count, int *playernums, int *spectracks)\n{\n\tsize_t i;\n\tif (count < 0 || count > 1000) count = 0;\n\tif (first > cl.splitclients) first = cl.splitclients;\n\tif (first+count > cl.splitclients) count = cl.splitclients-first;\n\tfor (i = 0; i < count; i++)\n\t{\n\t\tplayernums[i] = cl.playerview[first+i].playernum;\n\t\tspectracks[i] = Cam_TrackNum(&cl.playerview[first+i]);\n\t}\n\treturn count;\n}\n\nstatic void QDECL Plug_GetServerInfoRaw(char *outptr, size_t outlen)\n{\n\textern float demtime;\n\n\tInfoBuf_ToString(&cl.serverinfo, outptr, outlen, NULL, NULL, NULL, NULL, NULL);\n\tQ_strncatz(outptr, va(\"\\\\intermission\\\\%i\", cl.intermissionmode), outlen);\n\tswitch(cls.demoplayback)\n\t{\n\tcase DPB_NONE:\n\t\tbreak;\n\tcase DPB_MVD:\n\t\tQ_strncatz(outptr, \"\\\\demotype\\\\mvd\", outlen);\n\t\tbreak;\n\tcase DPB_QUAKEWORLD:\n\t\tQ_strncatz(outptr, \"\\\\demotype\\\\qw\", outlen);\n\t\tbreak;\n#ifdef NQPROT\n\tcase DPB_NETQUAKE:\n\t\tQ_strncatz(outptr, \"\\\\demotype\\\\nq\", outlen);\n\t\tbreak;\n#endif\n#ifdef Q2CLIENT\n\tcase DPB_QUAKE2:\n\t\tQ_strncatz(outptr, \"\\\\demotype\\\\q2\", outlen);\n\t\tbreak;\n#endif\n\t}\n\tQ_strncatz(outptr, va(\"\\\\demotime\\\\%f\", demtime-cls.demostarttime), outlen);\n\n#ifdef QUAKEHUD\n\tif (cl.playerview[0].statsf[STAT_MATCHSTARTTIME])\n\t\tQ_strncatz(outptr, va(\"\\\\matchstart\\\\%f\", cl.playerview[0].statsf[STAT_MATCHSTARTTIME]/1000), outlen);\n\telse\n#endif\n\t\tQ_strncatz(outptr, va(\"\\\\matchstart\\\\%f\", cl.matchgametimestart), outlen);\n}\nstatic size_t QDECL Plug_GetServerInfoBlob(const char *key, void *outptr, size_t outsize)\n{\n\tchar tmp[32];\n\tsize_t blobsize;\n\tconst char *blob = InfoBuf_BlobForKey(&cl.serverinfo, key, &blobsize, NULL);\n\tif (!blob)\n\t{\t//inescapable hacks\n\t\tif (!strcmp(key, \"matchstart\"))\n\t\t{\n\t\t\tfloat matchstart = cl.matchgametimestart;\n#ifdef QUAKEHUD\n\t\t\tif (cl.playerview[0].statsf[STAT_MATCHSTARTTIME])\n\t\t\t\tmatchstart = cl.playerview[0].statsf[STAT_MATCHSTARTTIME]/1000;\n#endif\n\t\t\tsnprintf(tmp, sizeof(tmp), \"%f\", matchstart), blob=tmp;\n\t\t}\n\t\telse if (!strcmp(key, \"demotime\"))\n\t\t{\n\t\t\textern float demtime;\n\t\t\tsnprintf(tmp, sizeof(tmp), \"%f\", demtime-cls.demostarttime), blob=tmp;\n\t\t}\n\t\telse if (!strcmp(key, \"demotype\"))\n\t\t{\n\t\t\tswitch(cls.demoplayback)\n\t\t\t{\n\t\t\tcase DPB_NONE:\n\t\t\t\tbreak;\n\t\t\tcase DPB_MVD:\n\t\t\t\tblob = \"mvd\";\n\t\t\t\tbreak;\n\t\t\tcase DPB_QUAKEWORLD:\n\t\t\t\tblob = \"qw\";\n\t\t\t\tbreak;\n#ifdef NQPROT\n\t\t\tcase DPB_NETQUAKE:\n\t\t\t\tblob = \"nq\";\n\t\t\t\tbreak;\n#endif\n#ifdef Q2CLIENT\n\t\t\tcase DPB_QUAKE2:\n\t\t\t\tblob = \"q2\";\n\t\t\t\tbreak;\n#endif\n\t\t\t}\n\t\t}\n\t\telse if (!strcmp(key, \"intermission\"))\n\t\t\tsnprintf(tmp, sizeof(tmp), \"%i\", cl.intermissionmode), blob=tmp;\n\t\tif (blob)\n\t\t\tblobsize = strlen(blob);\n\t}\n\tif (outptr)\n\t{\n\t\tif (blobsize > outsize)\n\t\t\treturn 0;\t//error\n\t\tmemcpy(outptr, blob, blobsize);\n\t\treturn blobsize;\n\t}\n\telse\n\t\treturn blobsize;\n}\n\nstatic void QDECL Plug_SetUserInfo(int seat, const char *key, const char *value)\n{\n\tCL_SetInfo(seat, key, value);\n}\nstatic void QDECL Plug_SetUserInfoBlob(int seat, const char *key, const void *value, size_t size)\n{\n\tCL_SetInfoBlob(seat, key, value, size);\n}\n\nstatic size_t QDECL Plug_GetUserInfoBlob(int seat, const char *key, void *outptr, size_t outsize)\n{\n\tsize_t blobsize;\n\tconst char *blob;\n\tif (seat >= countof(cls.userinfo))\n\t\tblob = NULL, blobsize = 0;\n\telse\n\t\tblob = InfoBuf_BlobForKey(&cls.userinfo[seat], key, &blobsize, NULL);\n\tif (outptr)\n\t{\n\t\tif (blobsize > outsize)\n\t\t\treturn 0;\t//error\n\t\tmemcpy(outptr, blob, blobsize);\n\t\treturn blobsize;\n\t}\n\telse\n\t\treturn blobsize;\n}\n\nvoid QDECL Plug_CL_ClearState(void)\n{\n\tCL_ClearState(true);\n}\nvoid QDECL Plug_CL_UpdateGameTime(double servertime)\n{\n\tcl.oldgametime = cl.gametime;\n\tcl.oldgametimemark = cl.gametimemark;\n\tcl.gametime = servertime;\n\tcl.gametimemark = realtime;\n}\n\nstatic qboolean QDECL Plug_GetLastInputFrame(int seat, usercmd_t *outcmd)\n{\n\tunsigned int curframe = (cl.movesequence-1u) & UPDATE_MASK;\n\tif (!cl.movesequence || seat < 0 || seat >= cl.splitclients)\n\t\treturn false;\n\t*outcmd = cl.outframes[curframe].cmd[seat];\n\treturn true;\n}\n\n#define has(x) (((quintptr_t)&((plugnetinfo_t*)NULL)->x + sizeof(((plugnetinfo_t*)NULL)->x)) <= outlen)\n//aka: misc other hud timing crap\nstatic size_t QDECL Plug_GetNetworkInfo(plugnetinfo_t *outptr, size_t outlen)\n{\n\tif (has(capturing))\n\t{\n#ifdef HAVE_MEDIA_ENCODER\n\t\toutptr->capturing = Media_Capturing();\n#else\n\t\toutptr->capturing = 0;\n#endif\n\t}\n\t\n\tif (has(seats))\n\t\toutptr->seats = cl.splitclients;\n\tif (has(ping))\t\t\n\t\tCL_CalcNet2 (&outptr->ping.s_avg, &outptr->ping.s_mn, &outptr->ping.s_mx, &outptr->ping.ms_stddev, &outptr->ping.fr_avg, &outptr->ping.fr_mn, &outptr->ping.fr_mx, &outptr->loss.dropped, &outptr->loss.choked, &outptr->loss.invalid);\n\t\t\n\tif (has(mlatency))\n\t\toutptr->mlatency = 0;\n\tif (has(mrate))\n\t\toutptr->mrate = IN_DetermineMouseRate();\n\tif (has(vlatency))\n\t\toutptr->vlatency = 0;\n\t\t\n\tif (has(speed))\n\t\tVectorCopy(outptr->speed, r_refdef.playerview->simvel);\n\n\tif (has(clrate))\n\t\tNET_GetRates(cls.sockets, &outptr->clrate.in_pps, &outptr->clrate.out_pps, &outptr->clrate.in_bps, &outptr->clrate.out_bps);\t\t\n\tif (has(svrate))\n\t{\n\t\tmemset(&outptr->svrate, 0, sizeof(outptr->svrate));\n#ifndef CLIENTONLY\n\t\tNET_GetRates(svs.sockets, &outptr->svrate.in_pps, &outptr->svrate.out_pps, &outptr->svrate.in_bps, &outptr->svrate.out_bps);\n#endif\n\t}\n\t\n\treturn min(outlen,sizeof(*outptr));\n}\n#undef has\n\n#ifdef QUAKEHUD\nstatic float QDECL Plug_GetTrackerOwnFrags(int seat, char *outptr, size_t outlen)\n{\n\tif (!outlen)\n\t\treturn 0;\n\telse\n\t\treturn Stats_GetLastOwnFrag(seat, outptr, outlen);\n}\nstatic void QDECL Plug_GetPredInfo(int seat, vec3_t outvel)\n{\n\tif ((unsigned)seat < MAX_SPLITS)\n\t\tVectorCopy(cl.playerview[seat].simvel, outvel);\n}\n#endif\n\nstatic void QDECL Plug_GetLocationName(const float *locpoint, char *outbuffer, size_t bufferlen)\n{\n\tconst char *result = TP_LocationName(locpoint);\n\tQ_strncpyz(outbuffer, result, bufferlen);\n}\n\n#ifdef QUAKEHUD\nstatic size_t QDECL Plug_GetTeamInfo(teamplayerinfo_t *players, size_t maxplayers, qboolean showenemies, int seat)\n{\n\tint count = 0;\n\tint i;\n\tint self;\n\tlerpents_t\t\t*le;\n\tplayer_info_t\t*pl;\n\n\tmaxplayers = min(maxplayers, cl.allocated_client_slots);\n\t\n\tCvar_Get(\"ti\", \"1\", CVAR_USERINFO, \"Hacks because ktx sucks. Must be 1 in order to receive team information in ktx.\");\n\t\n\tif (seat >= 0)\n\t{\n\t\tself = cl.playerview[seat].playernum;\n\t\tif (cl.playerview[seat].cam_state != CAM_FREECAM)\n\t\t\tself = cl.playerview[seat].cam_spec_track;\n\t}\n\telse\n\t\tself = -1;\n\t\n\tfor (i = 0; i < cl.allocated_client_slots && maxplayers > 0; i++)\n\t{\n\t\tif (!*cl.players[i].name)\t//empty slot\n\t\t\tcontinue;\n\t\tif (cl.players[i].spectator)\t//shoo!\n\t\t\tcontinue;\n\t\tif (i == self)\n\t\t\tcontinue;\n\t\tif (!showenemies && strcmp(cl.players[i].team, cl.players[self].team))\n\t\t\tcontinue;\n\t\tplayers->client = i;\n\n\t\tpl = &cl.players[i];\n\t\tif (pl->tinfo.time > cl.time)\n\t\t{\t//mod is explicitly telling us this junk\n\t\t\tplayers->items = pl->tinfo.items;\n\t\t\tplayers->health = pl->tinfo.health;\n\t\t\tplayers->armor = pl->tinfo.armour;\n\t\t\tVectorCopy(pl->tinfo.org, players->org);\n\t\t\tQ_strncpyz(players->nick, pl->tinfo.nick, sizeof(players->nick));\n\t\t}\n\t\telse if (i == self)\n\t\t{\t//oh hey look, its me.\n\t\t\tplayers->items = cl.playerview[seat].stats[STAT_ITEMS];\n\t\t\tplayers->armor = cl.playerview[seat].statsf[STAT_ARMOR];\n\t\t\tplayers->health = cl.playerview[seat].statsf[STAT_HEALTH];\n\t\t\tQ_strncpyz(players->nick, \"\", sizeof(players->nick));\n\t\t}\n\t\telse if (cls.demoplayback == DPB_MVD)\n\t\t{\t//scrape it from the mvd (assuming there is one...\n\t\t\tplayers->items = cl.players[i].stats[STAT_ITEMS];\n\t\t\tplayers->armor = cl.players[i].statsf[STAT_ARMOR];\n\t\t\tplayers->health = cl.players[i].statsf[STAT_HEALTH];\n\t\t\tQ_strncpyz(players->nick, \"\", sizeof(players->nick));\n\t\t\t\n\t\t\tVectorClear(players->org);\n\t\t}\n\t\telse\n\t\t\tcontinue;\t//no stats, don't bother telling the plugin.\n\n\t\t//scrape origin from interpolation, if its more valid.\n\t\tif (i+1 < cl.maxlerpents && cl.lerpentssequence && cl.lerpents[i+1].sequence == cl.lerpentssequence)\n\t\t{\n\t\t\tle = &cl.lerpents[i+1];\n\t\t\tVectorCopy(le->origin, players->org);\n\t\t}\n\t\telse if (cl.lerpentssequence && cl.lerpplayers[i].sequence == cl.lerpentssequence)\n\t\t{\n\t\t\tle = &cl.lerpplayers[i];\n\t\t\tVectorCopy(le->origin, players->org);\n\t\t}\n\n\t\tplayers++;\n\t\tmaxplayers--;\n\t\tcount++;\n\t}\n\t\n\treturn count;\n}\n#endif\n#ifdef QUAKEHUD\nstatic int QDECL Plug_GetWeaponStats(int self, struct wstats_s *result, size_t maxresults)\n{\n\t//FIXME: we should support some way to clear this to 0 again, other than nosave.\n\tCvar_Get(\"wpsx\", \"1\", CVAR_USERINFO|CVAR_NOSAVE, \"Hacks because ktx sucks. Must be 1 in order to receive weapon stats information in ktx.\");\n\n\tif (self < 0)\n\t{\n\t\tunsigned int seat = (unsigned)(-self-1)%MAX_SPLITS;\n\t\tself = cl.playerview[seat].playernum;\n\t\tif (cl.playerview[seat].cam_state != CAM_FREECAM)\n\t\t\tself = cl.playerview[seat].cam_spec_track;\n\t}\n\tif (self < 0)\n\t\treturn 0;\n\n\tif (maxresults > countof(cl.players[self].weaponstats))\n\t\tmaxresults = countof(cl.players[self].weaponstats);\n\tmemcpy(result, cl.players[self].weaponstats, sizeof(*result) * maxresults);\n\treturn maxresults;\n}\n#endif\n\nstatic qboolean QDECL Plug_Con_SubPrint(const char *name, const char *text)\n{\n\tconsole_t *con;\n\tif (!name)\n\t\tname = \"\";\n\n\tif (qrenderer == QR_NONE)\n\t{\n\t\tif (!*name)\n\t\t{\n\t\t\tCon_Printf(\"%s\", text);\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\tcon = Con_FindConsole(name);\n\tif (!con)\n\t{\n\t\tcon = Con_Create(name, 0);\n\t\tCon_SetActive(con);\n\n\t\tif (currentplug->conexecutecommand)\n\t\t{\n\t\t\tcon->notif_x = 0;\n\t\t\tcon->notif_y = 8*4;\n\t\t\tcon->notif_w = vid.width;\n\t\t\tcon->notif_t = 8;\n\t\t\tcon->notif_l = 4;\n\t\t\tcon->flags |= CONF_NOTIFY;\n\t\t\tcon->userdata = currentplug;\n\t\t\tcon->linebuffered = Plug_SubConsoleCommand;\n\t\t}\n\t}\n\n\tCon_PrintCon(con, text, con->parseflags);\n\n\treturn true;\n}\nstatic qboolean QDECL Plug_Con_RenameSub(const char *oldname, const char *newname)\n{\n\tconsole_t *con;\n\tif (qrenderer == QR_NONE)\n\t\treturn false;\n\tcon = Con_FindConsole(oldname);\n\tif (!con)\n\t\treturn false;\n\n\tQ_strncpyz(con->name, newname, sizeof(con->name));\n\n\treturn true;\n}\nstatic qboolean QDECL Plug_Con_IsActive(const char *conname)\n{\n\tconsole_t *con;\n\tif (qrenderer == QR_NONE)\n\t\treturn false;\n\tcon = Con_FindConsole(conname);\n\tif (!con)\n\t\treturn false;\n\n\treturn Con_IsActive(con);\n}\nstatic qboolean QDECL Plug_Con_SetActive(const char *conname)\n{\n\tconsole_t *con;\n\tif (qrenderer == QR_NONE)\n\t\treturn false;\n\tcon = Con_FindConsole(conname);\n\tif (!con)\n\t\tcon = Con_Create(conname, 0);\n\n\tCon_SetActive(con);\n\treturn true;\n}\nstatic qboolean QDECL Plug_Con_Destroy(const char *conname)\n{\n\tconsole_t *con;\n\tif (qrenderer == QR_NONE)\n\t\treturn false;\n\tcon = Con_FindConsole(conname);\n\tif (!con)\n\t\treturn false;\n\n\tCon_Destroy(con);\n\treturn true;\n}\nstatic qboolean QDECL Plug_Con_NameForNum(qintptr_t connum, char *outconname, size_t connamesize)\n{\n\tif (qrenderer == QR_NONE)\n\t\treturn false;\n\n\treturn Con_NameForNum(connum, outconname, connamesize);\n}\n\nstatic float QDECL Plug_Con_GetConsoleFloat(const char *conname, const char *attrib)\n{\n\tfloat ret;\n\tconsole_t *con = Con_FindConsole(conname);\n\tret = -1;\n\n\tif (!con)\n\t\tret = -1;\n\telse if (!strcmp(attrib, \"unseen\"))\n\t\tret = con->unseentext;\n\telse if (!strcmp(attrib, \"markup\"))\t\n\t{\n\t\tif (con->parseflags & PFS_NOMARKUP)\n\t\t\tret = 0;\n\t\telse if (con->parseflags & PFS_KEEPMARKUP)\n\t\t\tret = 2;\n\t\telse\n\t\t\tret = 1;\n\t}\n\telse if (!strcmp(attrib, \"forceutf8\"))\n\t\tret = (con->parseflags&PFS_FORCEUTF8)?true:false;\n\telse if (!strcmp(attrib, \"hidden\"))\n\t\tret = (con->flags & CONF_HIDDEN)?true:false;\n\telse if (!strcmp(attrib, \"iswindow\"))\n\t\tret = (con->flags & CONF_ISWINDOW)?true:false;\n\telse if (!strcmp(attrib, \"maxlines\"))\n\t\tret = con->maxlines;\n\telse if (!strcmp(attrib, \"wnd_x\"))\n\t\tret = con->wnd_x;\n\telse if (!strcmp(attrib, \"wnd_y\"))\n\t\tret = con->wnd_y;\n\telse if (!strcmp(attrib, \"wnd_w\"))\n\t\tret = con->wnd_w;\n\telse if (!strcmp(attrib, \"wnd_h\"))\n\t\tret = con->wnd_h;\n\telse if (!strcmp(attrib, \"linecount\"))\n\t\tret = con->linecount;\n\n\treturn ret;\n}\n\nstatic qboolean QDECL Plug_Con_SetConsoleFloat(const char *conname, const char *attrib, float val)\n{\n\tconsole_t *con = Con_FindConsole(conname);\n\n\tif (!con)\n\t{\n\t\tcon = Con_Create(conname, 0);\n\t\tif (!con)\n\t\t\treturn false;\n\t\tcon->userdata = currentplug;\n\t\tcon->linebuffered = Plug_SubConsoleCommand;\n\t}\n\n\tif (!strcmp(attrib, \"unseen\"))\n\t\tcon->unseentext = !!val;\n\telse if (!strcmp(attrib, \"markup\"))\t\n\t{\n\t\tint cur = val;\n\t\tcon->parseflags &= ~(PFS_NOMARKUP|PFS_KEEPMARKUP);\n\t\tif (cur == 0)\n\t\t\tcon->parseflags |= PFS_NOMARKUP;\n\t\telse if (cur == 2)\n\t\t\tcon->parseflags |= PFS_KEEPMARKUP;\n\t}\n\telse if (!strcmp(attrib, \"forceutf8\"))\n\t\tcon->parseflags = (con->parseflags & ~PFS_FORCEUTF8) | (val?PFS_FORCEUTF8:0);\n\telse if (!strcmp(attrib, \"hidden\"))\n\t\tcon->flags = (con->flags & ~CONF_HIDDEN) | (val?CONF_HIDDEN:0);\n\telse if (!strcmp(attrib, \"iswindow\"))\n\t{\n\t\tcon->flags = (con->flags & ~CONF_ISWINDOW) | (val?CONF_ISWINDOW:0);\n\t\tcon->flags = (con->flags & ~CONF_NOTIFY) | (val>1?CONF_NOTIFY:0);\n\t\tif (con_curwindow == con && !(con->flags & CONF_ISWINDOW))\n\t\t\tcon_curwindow = NULL;\n\t\telse if (!con_curwindow && (con->flags & CONF_ISWINDOW))\n\t\t\tcon_curwindow = con;\n\t}\n\telse if (!strcmp(attrib, \"maxlines\"))\n\t\tcon->maxlines = val;\n\telse if (!strcmp(attrib, \"wnd_x\"))\n\t\tcon->wnd_x = val;\n\telse if (!strcmp(attrib, \"wnd_y\"))\n\t\tcon->wnd_y = val;\n\telse if (!strcmp(attrib, \"wnd_w\"))\n\t\tcon->wnd_w = val;\n\telse if (!strcmp(attrib, \"wnd_h\"))\n\t\tcon->wnd_h = val;\n\telse if (!strcmp(attrib, \"linebuffered\"))\n\t{\n\t\tcon->userdata = currentplug;\n\t\tif (val == 2)\n\t\t\tcon->linebuffered = NULL;//Con_Navigate;\n\t\telse if (val == 1)\n\t\t\tcon->linebuffered = Plug_SubConsoleCommand;\n\t\telse\n\t\t\tcon->linebuffered = NULL;\n\t}\n\telse if (!strcmp(attrib, \"linecount\"))\n\t{\n\t\tif (val == 0)\n\t\t{\n\t\t\tint pfl = con->parseflags;\n\t\t\tCon_ClearCon(con);\n\t\t\tcon->parseflags = pfl;\n\t\t}\n\t\telse\n\t\t\treturn false;\n\t}\n\telse\n\t\treturn false;\n\treturn true;\n}\n\nstatic qboolean QDECL Plug_Con_GetConsoleString(const char *conname, const char *attrib, char *value, size_t size)\n{\n\tconsole_t *con = Con_FindConsole(conname);\n\n\tif (!con)\n\t\treturn false;\n\telse if (!strcmp(attrib, \"footer\"))\n\t\t;\n\telse if (!strcmp(attrib, \"title\"))\n\t{\n\t\tQ_strncpyz(value, con->title, size);\n\t}\n\telse if (!strcmp(attrib, \"icon\"))\n\t{\n\t\tQ_strncpyz(value, con->icon, size);\n\t}\n\telse if (!strcmp(attrib, \"prompt\"))\n\t{\n\t\tQ_strncpyz(value, con->prompt, size);\n\t}\n\telse if (!strcmp(attrib, \"backimage\"))\n\t{\n\t\tif (con->backshader)\n\t\t\tQ_strncpyz(value, con->backshader->name, size);\n\t\telse\n\t\t\tQ_strncpyz(value, con->backimage, size);\n\t}\n\telse\n\t\treturn false;\n\treturn true;\n}\nstatic qboolean QDECL Plug_Con_SetConsoleString(const char *conname, const char *attrib, const char *value)\n{\n\tconsole_t *con = Con_FindConsole(conname);\n\n\tif (!con)\n\t{\n\t\tcon = Con_Create(conname, 0);\n\t\tif (!con)\n\t\t\treturn false;\n\t\tcon->userdata = currentplug;\n\t\tcon->linebuffered = Plug_SubConsoleCommand;\n\t}\n\tif (!con)\n\t\treturn false;\n\telse if (!strcmp(attrib, \"footer\"))\n\t\tCon_Footerf(con, false, \"%s\", value);\n\telse if (!strcmp(attrib, \"title\"))\n\t\tQ_strncpyz(con->title, value, sizeof(con->title));\n\telse if (!strcmp(attrib, \"icon\"))\n\t\tQ_strncpyz(con->icon, value, sizeof(con->icon));\n\telse if (!strcmp(attrib, \"prompt\"))\n\t\tQ_strncpyz(con->prompt, value, sizeof(con->prompt));\n\telse if (!strcmp(attrib, \"backimage\"))\n\t{\n\t\tQ_strncpyz(con->backimage, value, sizeof(con->backimage));\n\t\tif (con->backshader)\n\t\t\tR_UnloadShader(con->backshader);\n\t}\n\telse if (!strcmp(attrib, \"backvideomap\"))\n\t{\n\t\tQ_strncpyz(con->backimage, \"\", sizeof(con->backimage));\n\t\tif (con->backshader)\n\t\t\tR_UnloadShader(con->backshader);\n\t\tif (qrenderer != QR_NONE)\n\t\t\tcon->backshader = R_RegisterCustom(NULL, va(\"consolevid_%s\", con->name), SUF_NONE, Shader_DefaultCinematic, value);\n\t\telse\n\t\t\tcon->backshader = NULL;\n\t}\n\telse\n\t\treturn false;\n\treturn true;\n}\n\nstatic void QDECL Plug_S_RawAudio(int sourceid, void *data, int speed, int samples, int channels, int width, float volume)\n{\n\tS_RawAudio(sourceid, data, speed, samples, channels, width, volume);\n}\nstatic void QDECL S_Spacialize(unsigned int seat, int entnum, vec3_t origin, vec3_t axis[3], int reverb, vec3_t velocity)\n{\n\tif (seat >= countof(cl.playerview))\n\t\treturn;\n\tcl.playerview[seat].audio.defaulted = false;\n\tcl.playerview[seat].audio.entnum = entnum;\n\tVectorCopy(origin, cl.playerview[seat].audio.origin);\n\tVectorCopy(axis[0], cl.playerview[seat].audio.forward);\n\tVectorCopy(axis[1], cl.playerview[seat].audio.right);\n\tVectorCopy(axis[2], cl.playerview[seat].audio.up);\n\tcl.playerview[seat].audio.reverbtype = reverb;\n\tVectorCopy(velocity, cl.playerview[seat].audio.velocity);\n}\nstatic sfx_t *QDECL Plug_S_PrecacheSound(const char *sndname)\n{\n\treturn S_PrecacheSound(sndname);\n}\n\nstatic void Plug_Client_Close(plugin_t *plug)\n{\n\tmenu_t *m = Menu_FindContext(currentplug);\n\n\tif (m)\n\t\tMenu_Unlink(m, true);\n\tif (protocolclientplugin == plug)\n\t{\n\t\tprotocolclientplugin = NULL;\n\t\tif (cls.protocol == CP_PLUGIN)\n\t\t\tcls.protocol = CP_UNKNOWN;\n\t}\n}\n\n\n\n\n\n\n"
  },
  {
    "path": "engine/client/cl_pred.c",
    "content": "/*\nCopyright (C) 1996-1997 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n*/\n#include \"quakedef.h\"\n#include \"winquake.h\"\n\ncvar_t\tcl_predict_extrapolate = CVARD(\"cl_predict_extrapolate\", \"\", \"If 1, enables prediction based upon partial input frames which can change over time resulting in a swimmy feel but does not need to interpolate. If 0, prediction will stay in the past and thus use only completed frames. Interpolation will then be used to smooth movement.\\nThis cvar only applies when video and input frames are independant (ie: cl_netfps is set).\");\nstatic cvar_t\tcl_predict_timenudge = CVARD(\"cl_predict_timenudge\", \"0\", \"A debug feature. You should normally leave this as 0. Nudges local player prediction into the future if positive (resulting in extrapolation), or into the past if negative (resulting in laggy interpolation). Value is in seconds, so small decimals are required. This cvar applies even if input frames are tied to video frames.\");\ncvar_t\tcl_lerp_smooth = CVARD(\"cl_lerp_smooth\", \"2\", \"If 2, will act as 1 when playing demos/singleplayer and otherwise act as if set to 0 (ie: deathmatch).\\nIf 1, interpolation will run in the past, resulting in really smooth movement at the cost of latency (even on bunchy german ISDNs).\\nIf 0, interpolation will be based upon packet arrival times and may judder due to packet loss.\");\nstatic cvar_t\tcl_lerp_driftbias = CVARD(\"cl_lerp_driftbias\", \"0\", \"Additional bias, can be set to a negative value to hold interpolation in the past.\");\nstatic cvar_t\tcl_lerp_driftfrac = CVARD(\"cl_lerp_driftfrac\", \"0\", \"Proportion of the latest time vs the older time to favour drifting towards.\");\ncvar_t\tcl_nopred = CVARD(\"cl_nopred\",\"0\", \"Disables clientside movement prediction.\");\nstatic cvar_t\tcl_pushlatency = CVAR(\"pushlatency\",\"-999\");\n\nextern float\tpm_airaccelerate;\n\nextern usercmd_t cl_pendingcmd[MAX_SPLITS];\n\n#ifdef Q2CLIENT\n#define\tMAX_PARSE_ENTITIES\t1024\nextern entity_state_t\tclq2_parse_entities[MAX_PARSE_ENTITIES];\n\nchar *Get_Q2ConfigString(int i);\n\nvoid VARGS Q2_Pmove (q2pmove_t *pmove);\n#define\tQ2PMF_DUCKED\t\t\t1\n#define\tQ2PMF_JUMP_HELD\t\t2\n#define\tQ2PMF_ON_GROUND\t\t4\n#define\tQ2PMF_TIME_WATERJUMP\t8\t// pm_time is waterjump\n#define\tQ2PMF_TIME_LAND\t\t16\t// pm_time is time before rejump\n#define\tQ2PMF_TIME_TELEPORT\t32\t// pm_time is non-moving time\n#define Q2PMF_NO_PREDICTION\t64\t// temporarily disables prediction (used for grappling hook)\n\nstatic struct\n{\n\tvec3_t origin;\n\tint seq;\n} cl_predictions[MAX_SPLITS][UPDATE_BACKUP];\n\n\n/*\n===================\nCL_CheckPredictionError\n===================\n*/\nvoid CLQ2_CheckPredictionError (void)\n{\n\tint\t\tframe;\n\tint\t\tdelta[3];\n\tint\t\ti;\n\tint\t\tlen;\n\tint\t\tseat;\n\tq2player_state_t *ps;\n\tplayerview_t *pv;\n\n\tfor (seat = 0; seat < cl.splitclients; seat++)\n\t{\n\t\tps = &cl.q2frame.seat[seat].playerstate;\n\t\tpv = &cl.playerview[seat];\n\n\t\tif (cl_nopred.value || (ps->pmove.pm_flags & Q2PMF_NO_PREDICTION))\n\t\t\tcontinue;\n\n\t\t// calculate the last usercmd_t we sent that the server has processed\n\t\tframe = cl.ackedmovesequence;\n\t\tframe &= (UPDATE_MASK);\n\n\t\tif (cl_predictions[seat][frame].seq != cl.ackedmovesequence)\n\t\t\tcontinue;\n\n\t\t// compare what the server returned with what we had predicted it to be\n\t\tVectorSubtract (ps->pmove.origin, cl_predictions[seat][frame].origin, delta);\n\n\t\t// save the prediction error for interpolation\n\t\tlen = abs(delta[0]) + abs(delta[1]) + abs(delta[2]);\n\t\tif (len > 640)\t// 80 world units\n\t\t{\t// a teleport or something\n\t\t\tVectorClear (pv->prediction_error);\n\t\t}\n\t\telse\n\t\t{\n//\t\t\tif (/*cl_showmiss->value && */(delta[0] || delta[1] || delta[2]) )\n//\t\t\t\tCon_Printf (\"prediction miss on %i: %i\\n\", cl.q2frame.serverframe,\n//\t\t\t\tdelta[0] + delta[1] + delta[2]);\n\n\t\t\tVectorCopy (ps->pmove.origin, cl_predictions[seat][frame].origin);\n\n\t\t\t// save for error itnerpolation\n\t\t\tfor (i=0 ; i<3 ; i++)\n\t\t\t\tpv->prediction_error[i] = delta[i]*0.125;\n\t\t}\n\t}\n}\n\n\n/*\n====================\nCL_ClipMoveToEntities\n\n====================\n*/\nint predignoreentitynum;\nvoid CLQ2_ClipMoveToEntities ( vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, trace_t *tr )\n{\n\tint\t\t\ti;\n\ttrace_t\t\ttrace;\n\tfloat\t\t*angles;\n\tentity_state_t\t*ent;\n\tint\t\t\tnum;\n\tmodel_t\t\t*cmodel;\n\tvec3_t\t\tbmins, bmaxs;\n\n\tfor (i=0 ; i<cl.q2frame.num_entities ; i++)\n\t{\n\t\tnum = (cl.q2frame.parse_entities + i)&(MAX_PARSE_ENTITIES-1);\n\t\tent = &clq2_parse_entities[num];\n\n\t\tif (ent->solidsize == ES_SOLID_NOT)\n\t\t\tcontinue;\n\n\t\tif (ent->number == predignoreentitynum)\n\t\t\tcontinue;\n\n\t\tif (ent->solidsize == ES_SOLID_BSP)\n\t\t{\t// special value for bmodel\n\t\t\tcmodel = cl.model_precache[ent->modelindex];\n\t\t\tif (!cmodel)\n\t\t\t\tcontinue;\n\t\t\tangles = ent->angles;\n\t\t}\n\t\telse\n\t\t{\t// encoded bbox\n\t\t\tCOM_DecodeSize(ent->solidsize, bmins, bmaxs);\n\t\t\tcmodel = CM_TempBoxModel (bmins, bmaxs);\n\t\t\tangles = vec3_origin;\t// boxes don't rotate\n\t\t}\n\n\t\tif (tr->allsolid)\n\t\t\treturn;\n\n\t\tWorld_TransformedTrace (cmodel, 0, 0, start, end, mins, maxs, false, &trace, ent->origin, angles, MASK_PLAYERSOLID);\n\n\t\tif (trace.allsolid || trace.startsolid || trace.fraction < tr->fraction)\n\t\t{\n\t\t\ttrace.ent = (struct edict_s *)ent;\n\t\t\t*tr = trace;\n\t\t}\n\t}\n}\n\n\n/*\n================\nCL_PMTrace\n================\n*/\nq2trace_t\tVARGS CLQ2_PMTrace (vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end)\n{\n\tq2trace_t\tq2t;\n\ttrace_t\t\tt;\n\n\t// check against world\n\tcl.worldmodel->funcs.NativeTrace(cl.worldmodel, 0, NULLFRAMESTATE, NULL, start, end, mins, maxs, false, MASK_PLAYERSOLID, &t);\n\tif (t.fraction < 1.0)\n\t\tt.ent = (struct edict_s *)1;\n\n\t// check all other solid models\n\tCLQ2_ClipMoveToEntities (start, mins, maxs, end, &t);\n\n\tq2t.allsolid = t.allsolid;\n\tq2t.contents = t.contents;\n\tVectorCopy(t.endpos, q2t.endpos);\n\tq2t.ent = t.ent;\n\tq2t.fraction = t.fraction;\n\tq2t.plane = t.plane;\n\tq2t.startsolid = t.startsolid;\n\tq2t.surface = t.surface;\n\n\treturn q2t;\n}\n\nint\t\tVARGS CLQ2_PMpointcontents (vec3_t point)\n{\n\tint\t\t\ti;\n\tentity_state_t\t*ent;\n\tint\t\t\tnum;\n\tmodel_t\t\t*cmodel;\n\tint\t\t\tcontents;\n\tvec3_t\t\taxis[3], relpos;\n\n\tcontents = cl.worldmodel->funcs.PointContents(cl.worldmodel, NULL, point);\n\n\tfor (i=0 ; i<cl.q2frame.num_entities ; i++)\n\t{\n\t\tnum = (cl.q2frame.parse_entities + i)&(MAX_PARSE_ENTITIES-1);\n\t\tent = &clq2_parse_entities[num];\n\n\t\tif (ent->solidsize != ES_SOLID_BSP) // special value for bmodel\n\t\t\tcontinue;\n\n\t\tcmodel = cl.model_precache[ent->modelindex];\n\t\tif (!cmodel)\n\t\t\tcontinue;\n\n\t\tAngleVectors (ent->angles, axis[0], axis[1], axis[2]);\n\t\tVectorNegate(axis[1], axis[1]);\n\t\tVectorSubtract(point, ent->origin, relpos);\n\t\tcontents |= cmodel->funcs.PointContents(cmodel, axis, relpos);\n\t}\n\n\treturn contents;\n}\n\n/*\n=================\nCL_PredictMovement\n\nSets cl.predicted_origin and cl.predicted_angles\n=================\n*/\nstatic void CLQ2_UserCmdToQ2(q2usercmd_t *out, const usercmd_t *cmd)\n{\n\tout->msec = cmd->msec;\n\tout->buttons = cmd->buttons;\n\tVectorCopy(cmd->angles, out->angles);\n\tout->forwardmove = cmd->forwardmove;\n\tout->sidemove = cmd->sidemove;\n\tout->upmove = cmd->upmove;\n\tout->impulse = cmd->impulse;\n\tout->lightlevel = cmd->lightlevel;\n}\nstatic void CLQ2_PredictMovement (int seat)\t//q2 doesn't support split clients.\n{\n\tint\t\t\tack, current;\n\tint\t\t\tframe;\n\tint\t\t\toldframe;\n\tq2pmove_t\tpm;\n\tint\t\t\tstep;\n\tint\t\t\toldz;\n\tint\t\t\ti;\n\tq2player_state_t *ps = &cl.q2frame.seat[seat].playerstate;\n\tplayerview_t *pv = &cl.playerview[seat];\n\n\tif (cls.state != ca_active)\n\t\treturn;\n\n//\tif (cl_paused->value)\n//\t\treturn;\n\t\n\tif (cl_nopred.value || cls.demoplayback || (ps->pmove.pm_flags & Q2PMF_NO_PREDICTION))\n\t{\t// just set angles\n\t\tfor (i=0 ; i<3 ; i++)\n\t\t{\n\t\t\tpv->predicted_angles[i] = pv->viewangles[i] + SHORT2ANGLE(ps->pmove.delta_angles[i]);\n\t\t}\n\t\treturn;\n\t}\n\tack = cl.ackedmovesequence;\t//index was received\n\tcurrent = cl.movesequence;\t//count generated, [current] is thus invalid.\n\n\t// if we are too far out of date, just freeze\n\tif (current - ack >= UPDATE_MASK)\n\t{\n//\t\tif (cl_showmiss->value)\n//\t\t\tCon_Printf (\"exceeded CMD_BACKUP\\n\");\n\t\treturn;\n\t}\n\n\t// copy current state to pmove\n\tmemset (&pm, 0, sizeof(pm));\n\tpm.trace = CLQ2_PMTrace;\n\tpm.pointcontents = CLQ2_PMpointcontents;\n\n\tpm_airaccelerate = atof(Get_Q2ConfigString(Q2CS_AIRACCEL));\n\n\tpm.s = ps->pmove;\n\n//\tSCR_DebugGraph (current - ack - 1, 0);\n\n\tframe = 0;\n\n\tpredignoreentitynum = cl.q2frame.seat[seat].clientnum+1;//cl.playerview[seat].playernum+1;\n\n\t// run frames\n\twhile (++ack < current)\n\t{\n\t\tframe = ack & (UPDATE_MASK);\n\t\tCLQ2_UserCmdToQ2(&pm.cmd, &cl.outframes[frame].cmd[seat]);\n\t\tQ2_Pmove (&pm);\n\n\t\t// save for debug checking\n\t\tVectorCopy (pm.s.origin, cl_predictions[seat][frame].origin);\n\t\tcl_predictions[seat][frame].seq = ack;\n\t}\n\n\tif (cl_pendingcmd[seat].msec)\n\t{\n\t\tCLQ2_UserCmdToQ2(&pm.cmd, &cl_pendingcmd[seat]);\n\t\tQ2_Pmove (&pm);\n\t}\n\n\toldframe = (ack-1) & (UPDATE_MASK);\n\toldz = cl_predictions[seat][oldframe].origin[2];\n\tstep = pm.s.origin[2] - oldz;\n\tif (step > 63 && step < 160 && (pm.s.pm_flags & Q2PMF_ON_GROUND) )\n\t{\n\t\tpv->predicted_step = step * 0.125;\n\t\tpv->predicted_step_time = realtime;// - host_frametime;// * 0.5;\n\t}\n\n\tpv->onground = !!(pm.s.pm_flags & Q2PMF_ON_GROUND);\n\n\n\t// copy results out for rendering\n\tpv->predicted_origin[0] = pm.s.origin[0]*0.125;\n\tpv->predicted_origin[1] = pm.s.origin[1]*0.125;\n\tpv->predicted_origin[2] = pm.s.origin[2]*0.125;\n\n\tVectorScale (pm.s.velocity, 0.125, pv->simvel);\n\tVectorCopy (pm.viewangles, pv->predicted_angles);\n}\n\n/*\n=================\nCL_NudgePosition\n\nIf pmove.origin is in a solid position,\ntry nudging slightly on all axis to\nallow for the cut precision of the net coordinates\n=================\n*/\nvoid CL_NudgePosition (void)\n{\n\tvec3_t\tbase;\n\tint\t\tx, y;\n\n\tif (cl.worldmodel->funcs.PointContents (cl.worldmodel, NULL, pmove.origin) == FTECONTENTS_EMPTY)\n\t\treturn;\n\n\tVectorCopy (pmove.origin, base);\n\tfor (x=-1 ; x<=1 ; x++)\n\t{\n\t\tfor (y=-1 ; y<=1 ; y++)\n\t\t{\n\t\t\tpmove.origin[0] = base[0] + x * 1.0/8;\n\t\t\tpmove.origin[1] = base[1] + y * 1.0/8;\n\t\t\tif (cl.worldmodel->funcs.PointContents (cl.worldmodel, NULL, pmove.origin) == FTECONTENTS_EMPTY)\n\t\t\t\treturn;\n\t\t}\n\t}\n\tCon_DPrintf (\"CL_NudgePosition: stuck\\n\");\n}\n\n#endif\n\n/*\n==============\nCL_PredictUsercmd\n==============\n*/\nvoid CL_PredictUsercmd (int pnum, int entnum, player_state_t *from, player_state_t *to, usercmd_t *u)\n{\n\t// split up very long moves\n\tif (u->msec > 50)\n\t{\n\t\tplayer_state_t temp;\n\t\tusercmd_t split;\n\n\t\tsplit = *u;\n\t\tsplit.msec = u->msec / 2;\t//special care to avoid forgetting an msec here and there\n\n\t\tif (split.msec > 500)\n\t\t{\n\t\t\tsplit.msec = 500;\n\t\t\tCL_PredictUsercmd (pnum, entnum, from, to, &split);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tCL_PredictUsercmd (pnum, entnum, from, &temp, &split);\n\t\t\tsplit.msec = u->msec - split.msec;\n\t\t\tCL_PredictUsercmd (pnum, entnum, &temp, to, &split);\n\t\t}\n\t\treturn;\n\t}\n\tif (!cl.worldmodel || cl.worldmodel->loadstate != MLS_LOADED)\n\t\treturn;\n\n\tVectorCopy (from->origin, pmove.origin);\n\tVectorCopy (u->angles, pmove.angles);\n\tVectorCopy (from->velocity, pmove.velocity);\n\tVectorCopy (from->gravitydir, pmove.gravitydir);\n\n\tif (IS_NAN(pmove.velocity[0]))\n\t{\n\t\tCon_DPrintf(\"nan velocity!\\n\");\n\t\tpmove.velocity[0] = 0;\n\t\tpmove.velocity[1] = 0;\n\t\tpmove.velocity[2] = 0;\n\t}\n\n\tpmove.onground = from->onground;\n\tpmove.jump_msec = (cls.z_ext & Z_EXT_PM_TYPE) ? 0 : from->jump_msec;\n\tpmove.jump_held = from->jump_held;\n\tpmove.waterjumptime = from->waterjumptime;\n\tpmove.pm_type = from->pm_type;\n\n\tpmove.cmd = *u;\n\tpmove.skipent = entnum;\n\n\tmovevars.entgravity = cl.playerview[pnum].entgravity;\n\tmovevars.maxspeed = cl.playerview[pnum].maxspeed;\n\tmovevars.bunnyspeedcap = cl.bunnyspeedcap;\n\tpmove.onladder = false;\n\tpmove.safeorigin_known = false;\n\tpmove.capsule = false;\t//FIXME\n\n\tVectorCopy(from->szmins, pmove.player_mins);\n\tVectorCopy(from->szmaxs, pmove.player_maxs);\n\n\tPM_PlayerMove (cl.gamespeed);\n\n\tto->waterjumptime = pmove.waterjumptime;\n\tto->jump_held = pmove.jump_held;\n\tto->jump_msec = pmove.jump_msec;\n\tpmove.jump_msec = 0;\n\n\tVectorCopy (pmove.origin, to->origin);\n\tVectorCopy (pmove.angles, to->viewangles);\n\tVectorCopy (pmove.velocity, to->velocity);\n\tVectorCopy (pmove.gravitydir, to->gravitydir);\n\tto->onground = pmove.onground;\n\n\tto->weaponframe = from->weaponframe;\n\tto->pm_type = from->pm_type;\n\n\tVectorCopy(pmove.player_mins, to->szmins);\n\tVectorCopy(pmove.player_maxs, to->szmaxs);\n}\n\n\n//Used when cl_nopred is 1 to determine whether we are on ground, otherwise stepup smoothing code produces ugly jump physics\nvoid CL_CatagorizePosition (playerview_t *pv, float *org)\n{\n\t//fixme: in nq, we are told by the server and should skip this, which avoids needing to know the player's size.\n\tif (pv->spectator && !CAM_ISLOCKED(pv))\n\t{\n\t\tpv->onground = false;\t// in air\n\t\treturn;\n\t}\n\tVectorClear (pmove.velocity);\n\tVectorCopy (org, pmove.origin);\n\tpmove.numtouch = 0;\n\tPM_CategorizePosition ();\n\tpv->onground = pmove.onground;\n}\n//Smooth out stair step ups.\n//Called before CL_EmitEntities so that the player's lightning model origin is updated properly\nvoid CL_CalcCrouch (playerview_t *pv)\n{\n\tqboolean teleported;\n\tvec3_t delta;\n\tfloat orgz = -DotProduct(pv->simorg, pv->gravitydir);\t//compensate for running on walls.\n\n\tVectorSubtract(pv->simorg, pv->oldorigin, delta);\n\n\tteleported = Length(delta)>48;\n\n\tif (teleported)\n\t{\n\t\t// possibly teleported or respawned\n\t\tpv->oldz = orgz;\n\t\tpv->extracrouch = 0;\n\t\tpv->crouchspeed = 100;\n\t\tpv->crouch = 0;\n\t\tVectorCopy (pv->simorg, pv->oldorigin);\n\t\treturn;\n\t}\n\n\tVectorCopy (pv->simorg, pv->oldorigin);\n\n\tif (pv->onground && orgz - pv->oldz)\n\t{\n\t\tif (pv->oldz > orgz)\n\t\t{\t//stepping down should be a little faster than stepping up.\n\t\t\t//so steps will still feel a little juddery. my knees hate walking down steep hills, so I guess this is similar.\n\t\t\tif (pv->crouchspeed > 0)\n\t\t\t\tpv->crouchspeed = -pv->crouchspeed*2;\n\n\t\t\tif (orgz - pv->oldz < -movevars.stepheight-2)\n\t\t\t{\n\t\t\t\t// if on steep stairs, increase speed\n\t\t\t\tif (pv->crouchspeed > -160*2)\n\t\t\t\t{\n\t\t\t\t\tpv->extracrouch = orgz - pv->oldz + host_frametime * 400 + 15;\n\t\t\t\t\tpv->extracrouch = max(pv->extracrouch, -5);\n\t\t\t\t}\n\t\t\t\tpv->crouchspeed = -160*2;\n\t\t\t}\n\n\t\t\tpv->oldz += host_frametime * pv->crouchspeed;\n\t\t\tif (pv->oldz < orgz)\n\t\t\t\tpv->oldz = orgz;\n\n\t\t\tif (pv->oldz > orgz + 15 - pv->extracrouch)\n\t\t\t\tpv->oldz = orgz + 15 + pv->extracrouch;\n\t\t\tif (pv->extracrouch < -host_frametime*400)\n\t\t\t\tpv->extracrouch += host_frametime * 400;\n\t\t\telse if (pv->extracrouch < 0)\n\t\t\t\tpv->extracrouch = 0;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (pv->crouchspeed < 0)\n\t\t\t\tpv->crouchspeed = -pv->crouchspeed/2;\n\n\t\t\tif (orgz - pv->oldz > movevars.stepheight+2)\n\t\t\t{\n\t\t\t\t// if on steep stairs, increase speed\n\t\t\t\tif (pv->crouchspeed < 160)\n\t\t\t\t{\n\t\t\t\t\tpv->extracrouch = orgz - pv->oldz - host_frametime * 200 - 15;\n\t\t\t\t\tpv->extracrouch = min(pv->extracrouch, 5);\n\t\t\t\t}\n\t\t\t\tpv->crouchspeed = 160;\n\t\t\t}\n\n\t\t\tpv->oldz += host_frametime * pv->crouchspeed;\n\t\t\tif (pv->oldz > orgz)\n\t\t\t\tpv->oldz = orgz;\n\t\t\n\n//\t\t\tif (orgz - pv->oldz > 15 + pv->extracrouch)\n\t\t\tif (pv->oldz < orgz - 15 + pv->extracrouch)\n\t\t\t\tpv->oldz = orgz - 15 - pv->extracrouch;\n\t\t\tif (pv->extracrouch >= host_frametime * 200)\n\t\t\t\tpv->extracrouch -= host_frametime * 200;\n\t\t\telse if (pv->extracrouch > 0)\n\t\t\t\tpv->extracrouch = 0;\n\t\t}\n\n\t\tpv->crouch = pv->oldz - orgz;\n\t}\n\telse\n\t{\n\t\t// in air or moving down\n\t\tpv->oldz = orgz;\n\t\tif (pv->crouch > 0)\n\t\t{\n\t\t\t//step-down\n\t\t\tpv->crouch -= host_frametime * 150;\n\t\t\tif (orgz - pv->oldz > 0)\n\t\t\t\tpv->crouch += orgz - pv->oldz;\t//if the view moved down, remove that amount from our crouching to avoid unneeded bobbing\n\t\t\tif (pv->crouch > 0)\n\t\t\t\tpv->crouch = 0;\n\t\t\tpv->crouchspeed = -100;\n\t\t}\n\t\telse\n\t\t{\t//step-up\n\t\t\tpv->crouch += host_frametime * 150;\n\t\t\tif (orgz - pv->oldz < 0)\n\t\t\t\tpv->crouch -= orgz - pv->oldz;\t//if the view moved down, remove that amount from our crouching to avoid unneeded bobbing\n\t\t\tif (pv->crouch > 0)\n\t\t\t\tpv->crouch = 0;\n\t\t\tpv->crouchspeed = 100;\n\t\t}\n\t\tpv->extracrouch = 0;\n\t}\n}\n\nfloat LerpAngles360(float to, float from, float frac)\n{\n\tfloat delta;\n\tdelta = (from-to);\n\n\tif (delta > 180)\n\t\tdelta -= 360;\n\tif (delta < -180)\n\t\tdelta += 360;\n\n\treturn to + frac*delta;\n}\n\nshort LerpAngles16(short to, short from, float frac)\n{\n\tint delta;\n\tdelta = (from-to);\n\n\tif (delta > 32767)\n\t\tdelta -= 65535;\n\tif (delta < -32767)\n\t\tdelta += 65535;\n\n\treturn to + frac*delta;\n}\n\nvoid CL_CalcClientTime(void)\n{\n\textern cvar_t cl_demospeed;\n\tif (!cls.state)\n\t{\n\t\tif (!cl.implicitpause)\n\t\t\tcl.servertime += host_frametime;\n\t\tcl.time = cl.servertime;\n\t\treturn;\n\t}\n\telse// if (cls.protocol != CP_QUAKE3)\n\t{\n//\t\tfloat oldst = realtime;\n\n\t\tif (cls.demoplayback && cls.timedemo)\n\t\t{\t//more deterministic. one frame is drawn per demo packet parsed. so sync to it as closely as possible.\n\t\t\t/*NOTE: this also has the effect of speeding up particles etc*/\n\t\t\textern float olddemotime;\n\t\t\tcl.servertime = olddemotime;\n\t\t}\n\t\t//q2 has no drifting (our code can't cope with picking anything beyond old/new snapshots, and frankly its 10fps which is horrendous enough as it is).\n\t\t//q3 always drifts (gamecode does snapshot selection).\n\t\t//qw code can drift (but oh noes! my latency!)\n\t\t//FIXME: nq code should be able to drift, but is apparently buggy somewhere and ends up uncomfortably stuttery right now.\n\t\t//default is to drift in demos+SP but not live (oh noes! added latency!)\n\t\tif (cls.protocol == CP_QUAKE2 || cls.protocol==CP_NETQUAKE/*FIXME*/ || (cls.protocol != CP_QUAKE3 && (!cl_lerp_smooth.ival || (cl_lerp_smooth.ival == 2 && !(cls.demoplayback || cl.allocated_client_slots == 1 || cl.playerview[0].spectator))) && cls.demoplayback!=DPB_MVD))\n\t\t{\t//no drift logic\n\t\t\tdouble f;\n\t\t\textern cvar_t cl_demospeed;\n\t\t\tf = cl.gametime - cl.oldgametime;\n\t\t\tif (f > 0.1)\n\t\t\t\tf = 0.1;\n\t\t\tf = (realtime - cl.gametimemark) / (f);\n\t\t\tif (cls.demoplayback && cl_demospeed.value > 0 && cls.state == ca_active)\n\t\t\t\tf *= cl_demospeed.value;\n\t\t\tf = bound(0, f, 1);\n\t\t\tcl.servertime = cl.oldgametime + f*(cl.gametime-cl.oldgametime);\n\t\t}\n\t\telse\n\t\t{\t//funky magic drift logic. we be behind the most recent frame in order to attempt to cover network congestions (which is apparently common in germany).\n\t\t\tfloat min, max;\n\t\t\tfloat r;\n\n//\t\t\toldst = cl.servertime;\n\n\t\t\tmax = cl.gametime;\n\t\t\tmin = cl.oldgametime;\n\t\t\tFloatInterpolate(min, cl_lerp_driftfrac.value, max, min);\n\t\t\tmin += cl_lerp_driftbias.value;\n\t\t\tif (max < min)\n\t\t\t\tmax = min;\n\n\t\t\tif (cls.demoplayback && cl_demospeed.value > 0 && cls.state == ca_active)\n\t\t\t\tr = cl_demospeed.value;\n\t\t\telse\n\t\t\t\tr = 1;\n\n\t\t\tif (max)\n\t\t\t\tcl.servertime += host_frametime*r;\n\t\t\telse\n\t\t\t\tcl.servertime = 0;\n\n\t\t\tif (!cl.oldgametime)\n\t\t\t\tcl.servertime = max; //map start (or reload/connect or something). snap to current.\n\t\t\telse if (cl.servertime > min)\n\t\t\t{\n\t\t\t\tif (cl.servertime > max)\n\t\t\t\t{\n\t\t\t\t\tcl.servertime = max;\n//\t\t\t\t\tCon_Printf(\"clamped to new time\\n\");\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tcl.servertime -= 0.02*(max - cl.servertime)*r;\n\t\t\t\t\tif (cl.servertime < cl.time)\n\t\t\t\t\t\tcl.servertime = cl.time;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (cl.servertime < min)\n\t\t\t{\n\t\t\t\tif (cl.servertime < min-0.5)\n\t\t\t\t{\n\t\t\t\t\tcl.servertime = min-0.5;\n//\t\t\t\t\tCon_Printf(\"clamped to old time\\n\");\n\t\t\t\t}\n\t\t\t\telse if (cl.servertime < min-0.3)\n\t\t\t\t{\n\t\t\t\t\tcl.servertime += 0.02*(min - cl.servertime)*r;\n//\t\t\t\t\tCon_Printf(\"running really slow\\n\");\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tcl.servertime += 0.01*(min - cl.servertime)*r;\n//\t\t\t\t\tCon_Printf(\"running slow\\n\");\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tcl.time = cl.servertime;\n/*\t\tif (oldst == 0)\n\t\t{\n\t\t\tint i;\n\t\t\tfor (i = 0; i < cl.allocated_client_slots; i++)\n\t\t\t{\n\t\t\t\tcl.players[i].entertime += cl.servertime;\n\t\t\t}\n\t\t}\n*/\n\t\treturn;\n\t}\n\n#if 0\n\tif (cls.protocol == CP_NETQUAKE || (cls.demoplayback && cls.demoplayback != DPB_MVD && cls.demoplayback != DPB_EZTV))\n\t{\n\t\tfloat want;\n//\t\tfloat off;\n\n\t\twant = cl.oldgametime + realtime - cl.gametimemark;\n//\t\toff = (want - cl.time);\n\t\tif (want>cl.time)\t//don't decrease\n\t\t\tcl.time = want;\n\n//\t\tCon_Printf(\"Drifted to %f off by %f\\n\", cl.time, off);\n\n//\t\tCon_Printf(\"\\n\");\n\t\tif (cl.time > cl.gametime)\n\t\t{\n\t\t\tcl.time = cl.gametime;\n//\t\t\tCon_Printf(\"max TimeClamp\\n\");\n\t\t}\n\t\tif (cl.time < cl.oldgametime)\n\t\t{\n\t\t\tcl.time = cl.oldgametime;\n//\t\t\tCon_Printf(\"old TimeClamp\\n\");\n\t\t}\n\n\t}\n\telse\n\t{\n\t\tif (cl_pushlatency.value > 0)\n\t\t\tCvar_Set (&cl_pushlatency, \"0\");\n\n\t\tcl.time = realtime - cls.latency - cl_pushlatency.value*0.001;\n\t\tif (cl.time > realtime)\n\t\t\tcl.time = realtime;\n\t}\n#endif\n}\n\nstatic void CL_DecodeStateSize(unsigned int solid, int modelindex, vec3_t mins, vec3_t maxs)\n{\n\tif (solid == ES_SOLID_BSP)\n\t{\n\t\tif (modelindex < MAX_PRECACHE_MODELS && cl.model_precache[modelindex] && cl.model_precache[modelindex]->loadstate == MLS_LOADED)\n\t\t{\n\t\t\tVectorCopy(cl.model_precache[modelindex]->mins, mins);\n\t\t\tVectorCopy(cl.model_precache[modelindex]->maxs, maxs);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tVectorClear(mins);\n\t\t\tVectorClear(maxs);\n\t\t}\n\t}\n\telse if (solid)\n\t\tCOM_DecodeSize(solid, mins, maxs);\n\telse\n\t{\n\t\tVectorClear(mins);\n\t\tVectorClear(maxs);\n\t}\n}\n\n/*called on packet reception*/\n#include \"pr_common.h\"\nstatic void CL_EntStateToPlayerState(player_state_t *plstate, entity_state_t *state)\n{\n\tvec3_t a;\n\tint pmtype;\n\tunsigned int flags = plstate->flags;\n\tqboolean onground = plstate->onground;\n\tqboolean jumpheld = plstate->jump_held;\n\tvec3_t vel;\n\tVectorCopy(plstate->velocity, vel);\n\tmemset(plstate, 0, sizeof(*plstate));\n\tplstate->jump_held = jumpheld;\n\n\tswitch(state->u.q1.pmovetype & 0x3f)\n\t{\n\tcase MOVETYPE_NOCLIP:\n\t\tif (cls.z_ext & Z_EXT_PM_TYPE_NEW)\n\t\t\tpmtype = PM_SPECTATOR;\n\t\telse\n\t\t\tpmtype = PM_OLD_SPECTATOR;\n\t\tbreak;\n\t\n\tcase MOVETYPE_FLY:\n\t\tpmtype = PM_FLY;\n\t\tbreak;\n\tcase MOVETYPE_NONE:\n\t\tpmtype = PM_NONE;\n\t\tbreak;\n\tcase MOVETYPE_BOUNCE:\n\tcase MOVETYPE_TOSS:\n\t\tpmtype = PM_DEAD;\n\t\tbreak;\n\tcase MOVETYPE_WALLWALK:\n\t\tpmtype = PM_WALLWALK;\n\t\tbreak;\n\tcase MOVETYPE_6DOF:\n\t\tpmtype = PM_6DOF;\n\t\tbreak;\n\tdefault:\n\t\tpmtype = PM_NORMAL;\n\t\tbreak;\n\t}\n\n\tVectorCopy(state->origin, plstate->origin);\n\tif (cls.protocol == CP_NETQUAKE && !(cls.fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS))\n\t{\t//nq is annoying, this stuff wasn't part of the entity state, so don't break it\n\t\tVectorCopy(vel, plstate->velocity);\n\t\tpmtype = PM_NORMAL;\n\t\tplstate->onground = onground;\n\t}\n\telse\n\t{\n\t\tVectorScale(state->u.q1.velocity, 1/8.0, plstate->velocity);\n\t\tplstate->onground = !!(state->u.q1.pmovetype&128);\n\t\tplstate->jump_held = !!(state->u.q1.pmovetype&64);\n\t}\n\tplstate->pm_type = pmtype;\n\tplstate->flags = flags & PF_INWATER;\n\n\tplstate->viewangles[0] = SHORT2ANGLE(state->u.q1.vangle[0]);\n\tplstate->viewangles[1] = SHORT2ANGLE(state->u.q1.vangle[1]);\n\tplstate->viewangles[2] = SHORT2ANGLE(state->u.q1.vangle[2]);\n\n\tif (!state->u.q1.gravitydir[0] && !state->u.q1.gravitydir[1])\n\t\tVectorSet(plstate->gravitydir, 0, 0, -1);\n\telse\n\t{\n\t\ta[0] = ((192+state->u.q1.gravitydir[0])/256.0f) * 360;\n\t\ta[1] = (state->u.q1.gravitydir[1]/256.0f) * 360;\n\t\ta[2] = 0;\n\t\tAngleVectors(a, plstate->gravitydir, NULL, NULL);\n\t}\n\n\tif (!state->solidsize || !(cls.fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS))\n\t{\n\t\tVectorSet(plstate->szmins, -16, -16, -24);\n\t\tVectorSet(plstate->szmaxs, 16, 16, 32);\n\t}\n\telse\n\t\tCL_DecodeStateSize(state->solidsize, state->modelindex, plstate->szmins, plstate->szmaxs);\n}\nstatic void CL_EntStateToPlayerCommand(usercmd_t *cmd, entity_state_t *state, float age)\n{\n\tint msec;\n\tfloat extra;\n\tmemset(cmd, 0, sizeof(*cmd));\n\n\textra = /*-cls.latency + */ 0.02;\t\t\t\t//network latency\n\textra += age;\t//if the state is not exactly current\n//\textra += realtime - cl.inframes[cl.validsequence&UPDATE_MASK].receivedtime;\n//\textra += (cl.inframes[cl.validsequence&UPDATE_MASK].receivedtime - cl.inframes[cl.oldvalidsequence&UPDATE_MASK].receivedtime)*4;\n\tmsec = 1000*extra;\n//\tCon_DPrintf(\"%i: age = %i, stale=%i\\n\", state->number, msec, state->u.q1.msec);\n\tmsec += state->u.q1.msec;\t//this is the age on the server\n\tcmd->msec = bound(0, msec, 250);\n\n\tcmd->forwardmove = state->u.q1.movement[0];\n\tcmd->sidemove = state->u.q1.movement[1];\n\tcmd->upmove = state->u.q1.movement[2];\n\n\tcmd->angles[0] = state->u.q1.vangle[0];// * -3 *65536/360.0;\n\tcmd->angles[1] = state->u.q1.vangle[1];// * 65536/360.0;\n\tcmd->angles[2] = state->u.q1.vangle[2];// * 65536/360.0;\n}\n\nvoid CL_PredictEntityMovement(entity_state_t *estate, float age)\n{\n\tplayer_state_t startstate, resultstate;\n\tusercmd_t cmd;\n\tint oldphysent;\n\textern cvar_t cl_predict_players;\n\t//build the entitystate state into a player state for prediction to use\n\n\tif (!estate->u.q1.pmovetype || !cl_predict_players.ival || age <= 0)\n\t\tVectorCopy(estate->origin, estate->u.q1.predorg);\n\telse\n\t{\n\t\tVectorClear(startstate.velocity);\n\t\tstartstate.onground = false;\n\t\tstartstate.jump_held = false;\n\t\tstartstate.flags = 0;\n\t\tCL_EntStateToPlayerState(&startstate, estate);\n\t\tCL_EntStateToPlayerCommand(&cmd, estate, age);\n\n//\t\tcmd.forwardmove = 5000;\n//\t\tcmd.msec = sin(realtime*6) * 128 + 128;\n\t\toldphysent = pmove.numphysent;\n\t\tpmove.onground = startstate.onground;\n\t\tCL_PredictUsercmd(0, estate->number, &startstate, &resultstate, &cmd);\t//uses player 0's maxspeed/grav...\n\t\tpmove.numphysent = oldphysent;\n\n\t\tVectorCopy(resultstate.origin, estate->u.q1.predorg);\n\t}\n}\n\nfloat CL_GetPredictionRealtime(playerview_t *pv)\n{\n\tfloat simtime;\n//these are to make svc_viewentity work better\n\tfloat netfps = cl_netfps.value;\n\n\tif (!netfps)\n\t{\n\t\t//every video frame has its own input frame.\n\t\tsimtime = realtime;\n\t}\n\telse\n\t{\n\t\tqboolean extrap = cl_predict_extrapolate.ival;\n//\t\tfloat fps = 1/host_frametime;\n//\t\tfps = bound(6.7, fps, cls.maxfps);\n\t\tnetfps = bound(6.7, netfps, cls.maxfps);\n//\t\tif (netfps > fps)\n//\t\t\tnetfps = fps;\n\t\tif (!*cl_predict_extrapolate.string)\n\t\t\textrap = netfps < 30;\n\t\tif (cls.protocol == CP_NETQUAKE && CPNQ_IS_DP)\n\t\t\textrap = true;\t//DP servers do a nasty thing where they send packets without any entities. This messes with our timings. Its much smoother to just always use extrapolation in this case (otherwise we'd have to backdate too much for prediction to do much).\n\t\tif (!extrap)\n\t\t{\n\t\t\t//interpolate. The input rate is completely smoothed out, at the cost of some latency.\n\t\t\t//You can still get juddering if the video rate doesn't match the monitor refresh rate (and isn't so high that it doesn't matter).\n\t\t\t//note that the code below will back-date input frames if the server acks too fast.\n\t\t\tsimtime = realtime - (1.0/netfps);\n\t\t}\n\t\telse\n\t\t{\n\t\t\t//extrapolate if we've a low net rate. This should reduce apparent lag, but will be jerky if the net rate is not an (inverse) multiple of the monitor rate.\n\t\t\t//this is in addition to any monitor desync.\n\t\t\tsimtime = realtime;\n\t\t}\n\t}\n\n\tif (cls.demoplayback == DPB_QUAKEWORLD || pv->cam_state == CAM_EYECAM)\n\t\tsimtime -= cls.latency;\t//push back when playing demos.\n\tsimtime += bound(-0.5, cl_predict_timenudge.value, 0.5);\n\n\treturn simtime;\n}\n\nqboolean CSQC_GetSSQCEntityOrigin(unsigned int ssqcent, float *out);\n/*\n==============\nCL_PredictMove\n==============\n*/\nvoid CL_PredictMovePNum (int seat)\n{\n\t//when this is called, the entity states have been interpolated.\n\t//interpolation state should be updated to match prediction state, so entities move correctly in mirrors/portals.\n\n\t//this entire function is pure convolouted bollocks.\n\tstruct {\n\t\tint frame;\n\t\tdouble time;\n\t\tplayer_state_t *state;\n\t\tusercmd_t *cmd;\n\t} from, to;\n\tplayerview_t *pv = &cl.playerview[seat];\n\tint\t\t\ti;\n\tfloat\t\tf;\n\toutframe_t\t*backdate;\n\tplayer_state_t framebuf[2];\t//need two framebufs so we can interpolate between two states.\n\tstatic player_state_t nullstate;\n\tint\t\t\toldphysent;\n\tdouble\t\tsimtime;\t//this is server time if nopred is set (lerp-only), and local time if we're predicting\n\textern cvar_t cl_netfps;\n\tlerpents_t\t*le;\n\tqboolean\tnopred;\n\tqboolean\tlerpangles = false;\n\tint\t\t\ttrackent;\n\tqboolean\tcam_nowlocked = false;\n\tusercmd_t indcmd;\n\t\n\tsimtime = CL_GetPredictionRealtime(pv);\n\n\tpv->nolocalplayer = !!(cls.fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS) || (cls.protocol != CP_QUAKEWORLD);\n\n\tif (!pv->spectator && (pv->cam_state != CAM_FREECAM || pv->cam_spec_track != -1))\t//just in case\n\t{\n\t\tif (pv->cam_state != CAM_FREECAM)\n\t\t\tpv->viewentity = (cls.demoplayback)?0:(pv->playernum+1);\n\t\tpv->cam_state = CAM_FREECAM;\n\t\tpv->cam_spec_track = -1;\n\t}\n\n#ifdef Q2CLIENT\n\tif (cls.protocol == CP_QUAKE2)\n\t{\n\t\tif (!cl.worldmodel || cl.worldmodel->loadstate != MLS_LOADED)\n\t\t\treturn;\n\t\tpv->crouch = 0;\n\t\tCLQ2_PredictMovement(seat);\n\t\treturn;\n\t}\n#endif\n\n\tif (cl.paused && !(cls.demoplayback!=DPB_MVD) && pv->cam_state == CAM_FREECAM)\n\t\treturn;\n\n\tif (!cl.validsequence)\n\t{\n\t\treturn;\n\t}\n\n\tif (cl.intermissionmode == IM_QWSCORES)\n\t{\n\t\t//quakeworld locks view position once you hit intermission.\n\t\tVectorCopy (pv->intermissionangles, pv->simangles);\n\t\treturn;\n\t}\n\telse if (cl.intermissionmode != IM_NONE)\n\t\tlerpangles = false;\t//will do angles later.\n\telse\n\t{\n\t\tif (cl.currentpackentities && cl.currentpackentities->fixangles[seat])\n\t\t{\n\t\t\tif (cl.previouspackentities && cl.previouspackentities->fixangles[seat]==cl.currentpackentities->fixangles[seat])\n\t\t\t{\n\t\t\t\tfor (i = 0; i < 3; i++)\n\t\t\t\t\tpv->simangles[i] = LerpAngles360(cl.currentpackentities->fixedangles[seat][i], cl.previouspackentities->fixedangles[seat][i], 1-(cl.previouspackentities->fixangles[seat]?cl.packfrac:1));\n\t\t\t}\n\t\t\telse\n\t\t\t\tVectorCopy(cl.currentpackentities->fixedangles[seat], pv->simangles);\n\n\t\t\tif (cls.demoplayback)\n\t\t\t\tVectorCopy(pv->simangles, pv->viewangles);\n\n\t\t\tif (cl.currentpackentities->fixangles[seat] == 2)\n\t\t\t\tlerpangles = (cls.demoplayback == DPB_QUAKEWORLD);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tlerpangles = (cls.demoplayback == DPB_QUAKEWORLD);\n\t\t\tVectorCopy (pv->aimangles, pv->simangles);\n\t\t}\n\t}\n\n\t//if we now know where our target player is, we can finally lock on to them.\n\tif (pv->cam_state == CAM_PENDING && pv->cam_spec_track >= 0 && pv->cam_spec_track < cl.allocated_client_slots && pv->viewentity != pv->cam_spec_track+1)\n\t{\n\t\tif ((cl.inframes[cl.validsequence & UPDATE_MASK].playerstate[pv->cam_spec_track].messagenum == cl.validsequence) ||\n\t\t\t(pv->cam_spec_track+1 < cl.maxlerpents && cl.lerpents[pv->cam_spec_track+1].sequence == cl.lerpentssequence) ||\n\t\t\tCSQC_GetSSQCEntityOrigin(pv->cam_spec_track+1, NULL))\n\t\t{\n\t\t\tpv->cam_state = CAM_EYECAM;\n\t\t\tpv->viewentity = pv->cam_spec_track+1;\n\t\t\tcam_nowlocked = true;\n\t\t}\n\t}\n\n\tif (pv->cam_state == CAM_WALLCAM)\n\t\ttrackent = pv->cam_spec_track+1;\n\telse\n\t\ttrackent = pv->viewentity;\n\n\tnopred = cl_nopred.ival;\n\n\t//don't wrap\n\tif (!cl.ackedmovesequence)\n\t\tnopred = true;\n\telse if (cl.movesequence - cl.ackedmovesequence >= UPDATE_BACKUP-1)\n\t\tnopred = true;\n\n\t//these things also force-disable prediction\n\tif (cls.demoplayback==DPB_MVD ||\n\t\tcl.intermissionmode != IM_NONE || cl.paused || pv->pmovetype == PM_NONE || pv->pmovetype == PM_FREEZE || CAM_ISLOCKED(pv))\n\t{\n\t\tnopred = true;\n\t}\n\n\t// figure out the first frame to lerp from.\n\t// we generate one new input frame every 1/72th of a second, with a refresh rate of 60hz that's blatently obvious\n\t// if we live in the present, we'll only have half a frame. in order to avoid extrapolation (which can give a swimmy feel), we live in the past by one frame time period\n\t// if we're running somewhere with a low latency, we can get a reply from the server before our next input frame is even generated, so we need to go backwards beyond the current state\n\n\tif (nopred)\n\t{\n\t\tlerpangles = false;\n\t\t//match interpolation info\n\t\tfrom.frame = ((char*)cl.previouspackentities - (char*)&cl.inframes[0].packet_entities) / sizeof(inframe_t);\n\t\tfrom.time = cl.inframes[from.frame & UPDATE_MASK].packet_entities.servertime;\n\t\tto.frame = ((char*)cl.currentpackentities - (char*)&cl.inframes[0].packet_entities) / sizeof(inframe_t);\n\t\tto.time = cl.inframes[to.frame & UPDATE_MASK].packet_entities.servertime;\n\t\tsimtime = cl.currentpacktime;\n\t\tto.cmd = from.cmd = NULL;\n\t}\n\telse\n\t{\n\t\tto.frame = from.frame = 0;\n\t\tto.time = from.time = 0;\n\t\tto.cmd = from.cmd = NULL;\n\n\t\t//try to find the inbound frame that sandwiches the realtime that we're trying to simulate.\n\t\t//if we're predicting, this will be some time in the future, and thus we'll be forced to pick the most recent frame.\n\t\t//if we're interpolating, we'll need to grab the frame before that.\n\t\t//we're only interested in inbound frames, not outbound, but its outbound frames that contain the prediction timing, so we need to look that up\n\t\t//(note that in qw, inframe[i].ack==i holds true, but this code tries to be generic for unsyncronised protocols)\n\t\t//(note that in nq, using outbound times means we'll skip over dupe states without noticing, and input packets with dupes should also be handled gracefully)\n//\t\tCon_DPrintf(\"in:%i:%i out:%i:%i ack:%i\\n\", cls.netchan.incoming_sequence, cl.validsequence, cls.netchan.outgoing_sequence,cl.movesequence, cl.ackedmovesequence);\n\t\tfor (i = cl.validsequence; i >= cls.netchan.incoming_sequence - UPDATE_MASK; i--)\n\t\t{\n\t\t\tint out;\n\t\t\t//skip frames which were not received, or are otherwise invalid. yay packetloss\n\t\t\tif (cl.inframes[i & UPDATE_MASK].frameid != i || cl.inframes[i & UPDATE_MASK].invalid)\n\t\t\t{\n//\t\t\t\tCon_DPrintf(\"stale incoming command %i\\n\", i);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t//each inbound frame tracks the outgoing frame that was last applied to it, and its outgoing frames that contain our timing info\n\t\t\tout = cl.inframes[i&UPDATE_MASK].ackframe;\n\t\t\tbackdate = &cl.outframes[out & UPDATE_MASK];\n\t\t\tif (backdate->cmd_sequence != out)\n\t\t\t{\n//\t\t\t\tCon_DPrintf(\"stale outgoing command %i (%i:%i:%i)\\n\", i, out, backdate->cmd_sequence, backdate->server_message_num);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t//okay, looks valid\n\n\t\t\t//if this is the first one we found, make sure both from+to are set properly\n\t\t\tif (!from.frame)\n\t\t\t{\n\t\t\t\tfrom.frame = i;\n\t\t\t\tfrom.time = backdate->senttime;\n\t\t\t}\n\t\t\tto = from;\n\t\t\tto.state = NULL;\n\n\t\t\tfrom.frame = i;\n\t\t\tfrom.time = backdate->senttime;\n\t\t\tfrom.cmd = &backdate->cmd[seat];\n\t\t\tif (cl.inframes[to.frame&UPDATE_MASK].ackframe > pv->prop.sequence)\n\t\t\t\tcontinue; //if we didn't predict to this frame yet, then the waterjump etc state will be invalid, so try to go for an older frame so that it actually propagates properly.\n\t\t\tif (from.time < simtime && from.frame != to.frame)\n\t\t\t\tbreak;\t//okay, we found the first frame that is older, no need to continue looking\n\t\t}\n\t}\n\n//\tCon_DPrintf(\"sim%f, %i(%i-%i): old%f, cur%f\\n\", simtime, cl.ackedmovesequence, fromframe, toframe, fromtime, totime);\n\n\tif ((pv->cam_state == CAM_WALLCAM || pv->cam_state == CAM_EYECAM) && trackent && trackent <= cl.allocated_client_slots)\n\t{\n\t\tfrom.state = &cl.inframes[from.frame & UPDATE_MASK].playerstate[trackent-1];\n\t\tto.state = &cl.inframes[to.frame & UPDATE_MASK].playerstate[trackent-1];\n\t}\n\telse\n\t{\n\t\tif (cls.demoplayback==DPB_MVD)\n\t\t{\n\t\t\tpv->nolocalplayer = false;\n\t\t\tfrom.state = &cl.inframes[cl.ackedmovesequence & UPDATE_MASK].playerstate[pv->playernum];\n\t\t\tto.state = &cl.inframes[cl.movesequence & UPDATE_MASK].playerstate[pv->playernum];\n\t\t}\n\t\telse\n\t\t{\n\t\t\tfrom.state = &cl.inframes[from.frame & UPDATE_MASK].playerstate[pv->playernum];\n\t\t\tto.state = &cl.inframes[to.frame & UPDATE_MASK].playerstate[pv->playernum];\n\t\t}\n\t}\n\tpv->pmovetype = to.state->pm_type;\n\tle = &cl.lerpplayers[pv->playernum];\n\n\tif (!from.cmd)\n\t\tfrom.cmd = &cl.outframes[from.frame & UPDATE_MASK].cmd[pv->playernum];\n\tif (!to.cmd)\n\t\tto.cmd = &cl.outframes[to.frame & UPDATE_MASK].cmd[pv->playernum];\n\n\t//if our network protocol doesn't have a concept of separate players, make sure our player states are updated from those entities\n\t//fixme: use entity states instead of player states to avoid the extra work here\n\tif (pv->nolocalplayer)\n\t{\n\t\tpacket_entities_t *pe;\n\t\tpe = &cl.inframes[from.frame & UPDATE_MASK].packet_entities;\n\t\tif (!pe->num_entities && !from.frame)\n\t\t\tpe = &cl.inframes[to.frame & UPDATE_MASK].packet_entities;\n\t\tfor (i = 0; i < pe->num_entities; i++)\n\t\t{\n\t\t\tif (pe->entities[i].number == trackent)\n\t\t\t{\n\t\t\t\tCL_EntStateToPlayerState(from.state, &pe->entities[i]);\n\t\t\t\tif (nopred)\n\t\t\t\t\tfrom.time -= (pe->entities[i].u.q1.msec / 1000.0f);\t//correct the time to match stale players\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (i == pe->num_entities && pv->nolocalplayer)\n\t\t{\n\t\t\tif (cls.state >= ca_active)\n\t\t\t\treturn;\t//no player, nothing makes sense any more.\n\t\t\tfrom.state = &nullstate;\n\t\t\tnopred = true;\n\t\t}\n\n\t\tpe = &cl.inframes[to.frame & UPDATE_MASK].packet_entities;\n\t\tfor (i = 0; i < pe->num_entities; i++)\n\t\t{\n\t\t\tif (pe->entities[i].number == trackent)\n\t\t\t{\n\t\t\t\tCL_EntStateToPlayerState(to.state, &pe->entities[i]);\n\t\t\t\tif (nopred)\n\t\t\t\t\tto.time -= (pe->entities[i].u.q1.msec / 1000.0f);\t//correct the time to match stale players. FIXME: this can push the simtime into the 'future' resulting in stuttering\n\t\t\t\tif (cls.fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS)\n\t\t\t\t{\n#ifdef QUAKESTATS\n\t\t\t\t\t//putting weapon frames in there was a stupid idea. qwisms I guess.\n\t\t\t\t\tif (!(cls.fteprotocolextensions2 & PEXT2_PREDINFO))\n\t\t\t\t\t{\n\t\t\t\t\t\tpv->stats[STAT_WEAPONFRAME] = cl.players[pv->playernum].stats[STAT_WEAPONFRAME] = pe->entities[i].u.q1.weaponframe;\n\t\t\t\t\t\tpv->statsf[STAT_WEAPONFRAME] = cl.players[pv->playernum].statsf[STAT_WEAPONFRAME] = pe->entities[i].u.q1.weaponframe;\n\t\t\t\t\t}\n#endif\n\t\t\t\t\tpv->pmovetype = to.state->pm_type;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (i == pe->num_entities && pv->nolocalplayer)\n\t\t{\n\t\t\tto.state = &nullstate;\n\t\t\tnopred = true;\n\t\t}\n\t\tif (pv->nolocalplayer && trackent < cl.maxlerpents)\n\t\t{\n\t\t\tle = &cl.lerpents[trackent];\n\t\t\tif (le->sequence != cl.lerpentssequence)\n\t\t\t\tnopred = true;\t//err, guys, this guy ain't valid... we don't know who we are! no point predicting.\n\t\t}\n\t}\n\n\t// predict forward until cl.time <= to->senttime\n\toldphysent = pmove.numphysent;\n\tCL_SetSolidPlayers();\n\tpmove.skipent = trackent;\n\n\t//just in case we don't run any prediction\n\tVectorCopy(to.state->gravitydir, pmove.gravitydir);\n\n\t//if all else fails...\n\tpmove.pm_type = to.state->pm_type;\n\tpmove.onground = to.state->onground;\n\tVectorCopy(to.state->szmins, pmove.player_mins);\n\tVectorCopy(to.state->szmaxs, pmove.player_maxs);\n\n\tif (!nopred)\n\t{\n\t\tint stopframe;\n\t\t//Con_Printf(\"Pred %i to %i\\n\", to.frame+1, min(from.frame+UPDATE_BACKUP, cl.movesequence));\n\n\t\t//fix up sequence numbers for nq\n\t\tint validsequence = cl.inframes[cl.validsequence&UPDATE_MASK].ackframe;\n\t\tfrom.frame = cl.inframes[from.frame&UPDATE_MASK].ackframe;\n\t\tto.frame = cl.inframes[to.frame&UPDATE_MASK].ackframe;\n\t\tfor (i=to.frame+1, stopframe=min(from.frame+UPDATE_BACKUP, cl.movesequence) ; i < stopframe; i++)\n\t\t{\n\t\t\toutframe_t *of = &cl.outframes[i & UPDATE_MASK];\n\t\t\tif (to.time >= simtime)\n\t\t\t\tbreak;\n\t\t\tif (of->cmd_sequence != i)\n\t\t\t{\n//\t\t\t\tCon_DPrintf(\"trying to predict a frame which is no longer valid\\n\");\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\t//okay, move it forward a frame.\n\t\t\tfrom = to;\n\n\t\t\tto.cmd = &of->cmd[seat];\n\t\t\tto.time = of->senttime;\n\t\t\tto.frame = i;//qw debug\n\t\t\tto.state = &framebuf[to.frame&1];\n\n\t\t\tif (from.frame == pv->prop.sequence && pv->prop.sequence)\n\t\t\t{\n\t\t\t\tif (!(cls.z_ext & Z_EXT_PF_ONGROUND))\n\t\t\t\t\tfrom.state->onground = pv->prop.onground;\n\t\t\t\tif (!(cls.z_ext & Z_EXT_PM_TYPE))\n\t\t\t\t\tfrom.state->jump_held = pv->prop.jump_held;\n\t\t\t\tfrom.state->jump_msec = pv->prop.jump_msec;\n\t\t\t\tfrom.state->waterjumptime = pv->prop.waterjumptime;\n\t\t\t\tif (!(cls.fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS))\n\t\t\t\t\tVectorCopy(pv->prop.gravitydir, from.state->gravitydir);\n\t\t\t}\n\t\t\tCL_PredictUsercmd (seat, trackent, from.state, to.state, to.cmd);\n\t\t\tif (i <= validsequence && simtime >= to.time)\n\t\t\t{\t//this frame is final keep track of our propagated values.\n\t\t\t\tpv->prop.onground = pmove.onground;\n\t\t\t\tpv->prop.jump_held = pmove.jump_held;\n\t\t\t\tpv->prop.jump_msec = pmove.jump_msec;\n\t\t\t\tpv->prop.waterjumptime = pmove.waterjumptime;\n\t\t\t\tVectorCopy(pmove.gravitydir, pv->prop.gravitydir);\n\t\t\t\tpv->prop.sequence = i;\n\t\t\t}\n\t\t}\n\n\t\tif (simtime > to.time)\n\t\t{\n\t\t\t//extrapolate X extra seconds\n\t\t\tfloat msec;\n\n\t\t\tmsec = ((simtime - to.time) * 1000);\n\t\t\tif (msec >= 1)\n\t\t\t{\n\t\t\t\tfrom = to;\n\n\t\t\t\tif (cl_pendingcmd[seat].msec && !cls.demoplayback)\n\t\t\t\t\tindcmd = cl_pendingcmd[seat];\n\t\t\t\telse\n\t\t\t\t\tindcmd = *to.cmd;\n\t\t\t\tto.cmd = &indcmd;\n\t\t\t\tto.time = simtime;\n\t\t\t\tto.frame+=1;\n\t\t\t\tto.state = &framebuf[to.frame&1];\n\n\t\t\t\tif (cls.demoplayback)\n\t\t\t\t{\n\t\t\t\t\textern cvar_t cl_demospeed;\n\t\t\t\t\tmsec *= cl_demospeed.value;\n\t\t\t\t}\n\n\t\t\t\tto.cmd->msec = bound(0, msec, 250);\n\n\t\t\t\tif (from.frame == pv->prop.sequence && pv->prop.sequence)\n\t\t\t\t{\t//overwrite non-networked state, to propagate it as required.\n\t\t\t\t\tif (!(cls.z_ext & Z_EXT_PF_ONGROUND))\n\t\t\t\t\t\tfrom.state->onground = pv->prop.onground;\n\t\t\t\t\tif (!(cls.z_ext & Z_EXT_PM_TYPE))\n\t\t\t\t\t\tfrom.state->jump_held = pv->prop.jump_held;\n\t\t\t\t\tfrom.state->jump_msec = pv->prop.jump_msec;\n\t\t\t\t\tfrom.state->waterjumptime = pv->prop.waterjumptime;\n\t\t\t\t\tif (!(cls.fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS))\n\t\t\t\t\t\tVectorCopy(pv->prop.gravitydir, from.state->gravitydir);\n\t\t\t\t}\n//\t\t\t\tCon_DPrintf(\" extrap %i: %f-%f (%g)\\n\", toframe, fromtime, simtime, simtime-fromtime);\n\t\t\t\tCL_PredictUsercmd (seat, trackent, from.state, to.state, to.cmd);\n\t\t\t}\n\t\t}\n\t\tpv->onground = pmove.onground;\n\t\tpv->pmovetype = to.state->pm_type;\n\t}\n\n\tpmove.numphysent = oldphysent;\n\n\tif (to.time == from.time)\n\t{\n\t\tVectorCopy (to.state->velocity, pv->simvel);\n\t\tVectorCopy (to.state->origin, pv->simorg);\n\n\t\tif (trackent && trackent != pv->playernum+1 && pv->cam_state == CAM_EYECAM)\n\t\t\tVectorCopy(to.state->viewangles, pv->simangles);\n//Con_DPrintf(\"%f %f %f\\n\", fromtime, simtime, totime);\n\t}\n\telse\n\t{\n\t\tvec3_t move;\n\t\t// now interpolate some fraction of the final frame\n\t\tf = (simtime - from.time) / (to.time - from.time);\n\n\t\tif (f < 0)\n\t\t\tf = 0;\n\t\tif (f > 1)\n\t\t\tf = 1;\n//Con_DPrintf(\"%i:%f %f %i:%f (%f)\\n\", fromframe, fromtime, simtime, toframe, totime, f);\n\t\tVectorSubtract(to.state->origin, from.state->origin, move);\n\t\tif (DotProduct(move, move) > 128*128)\n\t\t{\n\t\t\t// teleported, so don't lerp\n\t\t\tVectorCopy (to.state->velocity, pv->simvel);\n\t\t\tVectorCopy (to.state->origin, pv->simorg);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tfor (i=0 ; i<3 ; i++)\n\t\t\t{\n\t\t\t\tpv->simorg[i] = (1-f)*from.state->origin[i]   + f*to.state->origin[i];\n\t\t\t\tpv->simvel[i] = (1-f)*from.state->velocity[i] + f*to.state->velocity[i];\n\n\t\t\t\tif (trackent && trackent != pv->playernum+1 && pv->cam_state == CAM_EYECAM)\n\t\t\t\t{\n\t\t\t\t\tpv->simangles[i] = LerpAngles360(from.state->viewangles[i], to.state->viewangles[i], f);// * (360.0/65535);\n//\t\t\t\t\tpv->viewangles[i] = LerpAngles16(fromstate->command.angles[i], tostate->command.angles[i], f) * (360.0/65535);\n\t\t\t\t}\n\t\t\t\telse if (lerpangles)\n\t\t\t\t\tpv->simangles[i] = LerpAngles16(from.cmd->angles[i], to.cmd->angles[i], f) * (360.0/65535);\n\t\t\t}\n\t\t}\n\t}\n\tif (cls.protocol == CP_NETQUAKE && nopred)\n\t{\n\t\tpv->onground = to.state->onground;\n\t\tif (to.state->flags & PF_INWATER)\n\t\t{\n\t\t\tpmove.watertype = FTECONTENTS_WATER;\t//don't really know.\n\t\t\tpmove.waterlevel = 3;\t//pick one at random.\n\t\t}\n\t\telse\n\t\t{\n\t\t\tpmove.watertype = FTECONTENTS_EMPTY;\n\t\t\tpmove.waterlevel = 0;\n\t\t}\n\t}\n\telse\n\t\tCL_CatagorizePosition(pv, to.state->origin);\n\n\tCL_CalcCrouch (pv);\n\tpv->waterlevel = pmove.waterlevel;\n\tif (!DotProduct(pmove.gravitydir,pmove.gravitydir))\n\t\tVectorSet(pmove.gravitydir, 0, 0, -1);\n\telse\n\t\tVectorCopy(pmove.gravitydir, pv->gravitydir);\n\n\tif (cl.intermissionmode != IM_NONE && le)\n\t{\n\t\tVectorCopy(le->angles, pv->simangles);\n\t\tVectorCopy(pv->simangles, pv->viewangles);\n\t}\n\telse if (le && pv->cam_state == CAM_FREECAM)\n\t{\n\t\t//keep the entity tracking the prediction position, so mirrors don't go all weird\n\t\tVectorMA(pv->simorg, -pv->crouch, pv->gravitydir, le->origin);\n#ifdef QUAKESTATS\n\t\tif (pv->stats[STAT_HEALTH] > 0)\n#endif\n\t\t{\n\t\t\tVectorScale(pv->simangles, 1, le->angles);\n\t\t\tif (pv->pmovetype != PM_6DOF)\n\t\t\t\tle->angles[0] *= 0.333;\n\t\t\tle->angles[0] *= r_meshpitch.value;\n\t\t\tle->angles[2] *= r_meshroll.value;\n\t\t}\n\t}\n\n\tif (cam_nowlocked)\n\t\tCam_NowLocked(pv);\n\tif (pv->cam_state == CAM_WALLCAM)\n\t{\n\t\tvec3_t dir;\n\n\t\tVectorSubtract(pv->simorg, pv->cam_desired_position, dir);\n\t\tVectorAngles(dir, NULL, pv->simangles, false);\n\t\tVectorCopy(pv->simangles, pv->viewangles);\n\t\tpv->viewangles[0] = anglemod(pv->viewangles[0]);\n\t\tif (pv->viewangles[0] > 180)\n\t\t\tpv->viewangles[0] -= 360;\n\t\tVectorCopy(pv->cam_desired_position, pv->simorg);\n\t\tVectorClear(pv->simvel);\n\t}\n\tif (cam_nowlocked)\n\t{\n\t\t//invalidate the roll, so we don't spin when switching povs\n\t\tpv->rollangle = V_CalcRoll(pv->simangles, pv->simvel);\n\t\tpv->vm.oldmodel = NULL;\t//invalidate the viewmodel, so the lerps get reset\n\t}\n}\n\nvoid CL_PredictMove (void)\n{\n\tint i;\n\n\t// Set up prediction for other players\n\tCL_SetUpPlayerPrediction(false);\n\n\t// do client side motion prediction\n\tfor (i = 0; i < cl.splitclients; i++)\n\t\tCL_PredictMovePNum(i);\n\n\t// Set up prediction for other players\n\tCL_SetUpPlayerPrediction(true);\n\n\tVALGRIND_MAKE_MEM_UNDEFINED(&pmove.onground, sizeof(pmove.onground));\n}\n\n\n/*\n==============\nCL_InitPrediction\n==============\n*/\nvoid CL_InitPrediction (void)\n{\n\textern char cl_predictiongroup[];\n\tCvar_Register (&cl_pushlatency, cl_predictiongroup);\n\tCvar_Register (&cl_nopred,\tcl_predictiongroup);\n\tCvar_Register (&cl_predict_extrapolate,\tcl_predictiongroup);\n\tCvar_Register (&cl_predict_timenudge,\tcl_predictiongroup);\n\tCvar_Register (&cl_lerp_smooth,\tcl_predictiongroup);\n\n\tCvar_Register (&cl_lerp_driftbias,\tcl_predictiongroup);\n\tCvar_Register (&cl_lerp_driftfrac,\tcl_predictiongroup);\n}\n"
  },
  {
    "path": "engine/client/cl_screen.c",
    "content": "/*\nCopyright (C) 1996-1997 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n*/\n\n// cl_screen.c -- master for refresh, status bar, console, chat, notify, etc\n\n#include \"quakedef.h\"\n#ifdef GLQUAKE\n#include \"glquake.h\"//would prefer not to have this\n#endif\n#include \"shader.h\"\n#include \"gl_draw.h\"\n#include \"fs.h\"\n\n//name of the current backdrop for the loading screen\nchar levelshotname[MAX_QPATH];\nextern cvar_t con_textsize;\n\n\nvoid RSpeedShow(void)\n{\n\tint i;\n\tstatic int samplerspeeds[RSPEED_MAX];\n\tstatic int samplerquant[RQUANT_MAX];\n\tint savedsamplerquant[RQUANT_MAX];\t//so we don't count the r_speeds debug spam in draw counts.\n\tchar *RSpNames[RSPEED_MAX];\n\tchar *RQntNames[RQUANT_MAX];\n\tchar *s;\n\tstatic int framecount;\n\tint frameinterval = 100;\n\tint tsize;\n\n\tif (!r_speeds.ival)\n\t\treturn;\n\n\tif (con_textsize.value < 0)\n\t\ttsize = (-con_textsize.value * vid.height) / vid.pixelheight;\t//size defined in physical pixels\n\telse\n\t\ttsize = con_textsize.value;\t//size defined in virtual pixels.\n\tif (!tsize)\n\t\ttsize = 8;\n\n\tmemset(RSpNames, 0, sizeof(RSpNames));\n\n\tRSpNames[RSPEED_TOTALREFRESH]\t= \"Total refresh\";\n\tRSpNames[RSPEED_CSQCPHYSICS]\t= \" CSQC Physics\";\n\tRSpNames[RSPEED_CSQCREDRAW]\t\t= \" CSQC Drawing\";\n\tRSpNames[RSPEED_LINKENTITIES]\t= \"  Entity setup\";\n\tRSpNames[RSPEED_WORLDNODE]\t\t= \"  World walking\";\n\tRSpNames[RSPEED_DYNAMIC]\t\t= \"  Lightmap updates\";\n\tRSpNames[RSPEED_OPAQUE]\t\t\t= \"  Opaque Batches\";\n\tRSpNames[RSPEED_RTLIGHTS]\t\t= \"  RT Lights\";\n\tRSpNames[RSPEED_TRANSPARENTS]\t= \"  Transparent Batches\";\n\tRSpNames[RSPEED_PARTICLES]\t\t= \"  Particle phys/sort\";\n\tRSpNames[RSPEED_PARTICLESDRAW]\t= \"  Particle drawing\";\n\tRSpNames[RSPEED_2D]\t\t\t\t= \" 2d Elements\";\n\tRSpNames[RSPEED_PALETTEFLASHES]\t= \" Palette flashes\";\n\n\tRSpNames[RSPEED_SETUP]\t\t\t= \"Acquire Wait\";\n\tRSpNames[RSPEED_SUBMIT]\t\t\t= \"submit/finish\";\n\tRSpNames[RSPEED_PRESENT]\t\t= \"Present\";\n\tRSpNames[RSPEED_ACQUIRE]\t\t= \"Acquire Request\";\n\n\tRSpNames[RSPEED_PROTOCOL]\t\t= \"Client Protocol\";\n\tRSpNames[RSPEED_SERVER]\t\t\t= \"Server\";\n\tRSpNames[RSPEED_AUDIO]\t\t\t= \"Audio\";\n\n\tmemset(RQntNames, 0, sizeof(RQntNames));\n\tRQntNames[RQUANT_MSECS]\t\t\t\t\t= \"Microseconds\";\n\tRQntNames[RQUANT_PRIMITIVEINDICIES]\t\t= \"Draw Indicies\";\n\tRQntNames[RQUANT_DRAWS]\t\t\t\t\t= \"Draw Calls\";\n\tRQntNames[RQUANT_2DBATCHES]\t\t\t\t= \"2d Batches\";\n\tRQntNames[RQUANT_WORLDBATCHES]\t\t\t= \"World Batches\";\n\tRQntNames[RQUANT_ENTBATCHES]\t\t\t= \"Ent Batches\";\n\tRQntNames[RQUANT_SHADOWINDICIES]\t\t= \"Shadow Indicies\";\n\tRQntNames[RQUANT_SHADOWEDGES]\t\t\t= \"Shadow Edges\";\n\tRQntNames[RQUANT_SHADOWSIDES]\t\t\t= \"Shadowmap Sides\";\n\tRQntNames[RQUANT_LITFACES]\t\t\t\t= \"Lit faces\";\n\n\tRQntNames[RQUANT_RTLIGHT_DRAWN]\t\t\t= \"Lights Drawn\";\n\tRQntNames[RQUANT_RTLIGHT_CULL_FRUSTUM]\t= \"Lights offscreen\";\n\tRQntNames[RQUANT_RTLIGHT_CULL_PVS]\t\t= \"Lights PVS Culled\";\n\tRQntNames[RQUANT_RTLIGHT_CULL_SCISSOR]\t= \"Lights Scissored\";\n\n\tmemcpy(savedsamplerquant, rquant, sizeof(savedsamplerquant));\n\tif (r_speeds.ival > 1)\n\t{\n\t\tfor (i = 0; i < RSPEED_MAX; i++)\n\t\t{\n\t\t\ts = va(\"%g %-24s\", samplerspeeds[i]/(float)frameinterval, RSpNames[i]);\n\t\t\tDraw_FunStringWidthFont(font_console, 0, i*tsize, s, vid.width, true, false);\n\t\t}\n\t}\n\tfor (i = 0; i < RQUANT_MAX; i++)\n\t{\n\t\ts = va(\"%u.%.3u %-24s\", samplerquant[i]/frameinterval, (samplerquant[i]%100), RQntNames[i]);\n\t\tDraw_FunStringWidthFont(font_console, 0, (i+RSPEED_MAX)*tsize, s, vid.width, true, false);\n\t}\n\tif (r_speeds.ival > 1)\n\t{\n\t\ts = va(\"%f %-24s\", (frameinterval*1000*1000.0f)/samplerspeeds[RSPEED_TOTALREFRESH], \"Framerate (refresh only)\");\n\t\tDraw_FunStringWidthFont(font_console, 0, (i+RSPEED_MAX)*tsize, s, vid.width, true, false);\n\t}\n\tmemcpy(rquant, savedsamplerquant, sizeof(rquant));\n\n\tif (++framecount>=frameinterval)\n\t{\n\t\tfor (i = 0; i < RSPEED_MAX; i++)\n\t\t{\n\t\t\tsamplerspeeds[i] = rspeeds[i];\n\t\t\trspeeds[i] = 0;\n\t\t}\n\t\tfor (i = 0; i < RQUANT_MAX; i++)\n\t\t{\n\t\t\tsamplerquant[i] = rquant[i];\n\t\t\trquant[i] = 0;\n\t\t}\n\t\tframecount=0;\n\t}\n}\n\n\n/*\n\nbackground clear\nrendering\nturtle/net/ram icons\nsbar\ncenterprint / slow centerprint\nnotify lines\nintermission / finale overlay\nloading plaque\nconsole\nmenu\n\nrequired background clears\nrequired update regions\n\n\nsyncronous draw mode or async\nOne off screen buffer, with updates either copied or xblited\nNeed to double buffer?\n\n\nasync draw will require the refresh area to be cleared, because it will be\nxblited, but sync draw can just ignore it.\n\nsync\ndraw\n\nCenterPrint ()\nSlowPrint ()\nScreen_Update ();\nCon_Printf ();\n\nnet\nturn off messages option\n\nthe refresh is always rendered, unless the console is full screen\n\n\nconsole is:\n\tnotify lines\n\thalf\n\tfull\n\n\n*/\n\n\n\n\n\nfloat mousecursor_x, mousecursor_y;\nfloat mousemove_x, mousemove_y;\n\nfloat multicursor_x[8], multicursor_y[8];\nqboolean multicursor_active[8];\n\nfloat           scr_con_current;\t//current console lines shown\nfloat           scr_con_target;\t\t//the target number of lines (not a local, because it helps to know if we're at the target yet, etc)\n\nqboolean\t\tscr_con_forcedraw;\n\nextern cvar_t\t\t\tscr_viewsize;\nextern cvar_t\t\t\tscr_fov;\nextern cvar_t\t\t\tscr_conspeed;\nextern cvar_t\t\t\tscr_centertime;\nextern cvar_t\t\t\tscr_logcenterprint;\nextern cvar_t\t\t\tscr_showturtle;\nextern cvar_t\t\t\tscr_turtlefps;\nextern cvar_t\t\t\tscr_showpause;\nextern cvar_t\t\t\tscr_printspeed;\nextern cvar_t\t\t\tscr_allowsnap;\nextern cvar_t\t\t\tscr_sshot_type;\nextern cvar_t\t\t\tscr_sshot_prefix;\nextern cvar_t\t\t\tcrosshair;\nextern cvar_t\t\t\tscr_consize;\ncvar_t\t\t\tscr_neticontimeout = CVAR(\"scr_neticontimeout\", \"0.3\");\ncvar_t\t\t\tscr_diskicontimeout = CVAR(\"scr_diskicontimeout\", \"0.3\");\n\nqboolean        scr_initialized;                // ready to draw\n\nmpic_t          *scr_net;\nmpic_t          *scr_turtle;\n\nint                     clearconsole;\nint                     clearnotify;\n\nviddef_t        vid;                            // global video state\n\nvrect_t         scr_vrect;\n\nqboolean        scr_disabled_for_loading;\nqboolean        scr_drawloading;\nfloat           scr_disabled_time;\n\ncvar_t\tcon_stayhidden = CVARFD(\"con_stayhidden\", \"1\", CVAR_NOTFROMSERVER, \"0: allow console to pounce on the user\\n1: console stays hidden unless explicitly invoked\\n2:toggleconsole command no longer works\\n3: shift+escape key no longer works\");\ncvar_t\tshow_fps\t= CVARAFD(\"show_fps\"/*qw*/, \"0\", \"scr_showfps\"/*qs*/, CVAR_ARCHIVE, \"Displays the current framerate on-screen.\\n0: Off.\\n1: framerate average over a second.\\n2: Show a frametimes graph (with additional timing info).\\n-1: Normalized graph that focuses on the variation ignoring base times.\");\ncvar_t\tshow_fps_x\t= CVAR(\"show_fps_x\", \"-1\");\ncvar_t\tshow_fps_y\t= CVAR(\"show_fps_y\", \"-1\");\ncvar_t\tshow_clock\t= CVAR(\"cl_clock\", \"0\");\ncvar_t\tshow_clock_x\t= CVAR(\"cl_clock_x\", \"0\");\ncvar_t\tshow_clock_y\t= CVAR(\"cl_clock_y\", \"-1\");\ncvar_t\tshow_gameclock\t= CVAR(\"cl_gameclock\", \"0\");\ncvar_t\tshow_gameclock_x\t= CVAR(\"cl_gameclock_x\", \"0\");\ncvar_t\tshow_gameclock_y\t= CVAR(\"cl_gameclock_y\", \"-1\");\ncvar_t\tshow_speed\t= CVAR(\"show_speed\", \"0\");\ncvar_t\tshow_speed_x\t= CVAR(\"show_speed_x\", \"-1\");\ncvar_t\tshow_speed_y\t= CVAR(\"show_speed_y\", \"-9\");\ncvar_t\tscr_showdisk\t= CVAR(\"scr_showdisk\", \"0\");\ncvar_t\tscr_showdisk_x\t= CVAR(\"scr_showdisk_x\", \"-24\");\ncvar_t\tscr_showdisk_y\t= CVAR(\"scr_showdisk_y\", \"0\");\ncvar_t\tscr_showobituaries = CVAR(\"scr_showobituaries\", \"0\");\n\ncvar_t\tscr_loadingrefresh = CVARD(\"scr_loadingrefresh\", \"0\", \"Force redrawing of the loading screen, in order to display EVERY resource that is loaded\");\ncvar_t\tscr_showloading\t= CVAR(\"scr_showloading\", \"1\");\n//things to configure the legacy loading screen\ncvar_t\tscr_loadingscreen_picture = CVAR(\"scr_loadingscreen_picture\", \"gfx/loading\");\ncvar_t\tscr_loadingscreen_aspect = CVARD(\"scr_loadingscreen_aspect\", \"0\", \"Controls the aspect of levelshot images.\\n0: Use source image's aspect.\\n1: Force 4:3 aspect (ignore image's aspect), for best q3 compat.\\n2: Ignore aspect considerations and just smear it over the entire screen.\\n-1: Disable levelshot use.\");\ncvar_t\tscr_loadingscreen_scale = CVAR(\"scr_loadingscreen_scale\", \"1\");\ncvar_t\tscr_loadingscreen_scale_limit = CVAR(\"scr_loadingscreen_scale_limit\", \"2\");\n\nvoid *scr_curcursor;\n\n\nstatic void SCR_CPrint_f(void)\n{\n\tint seat = CL_TargettedSplit(false);\n\tif (Cmd_Argc() == 2)\n\t\tSCR_CenterPrint(seat, Cmd_Argv(1), true);\n\telse\n\t\tSCR_CenterPrint(seat, Cmd_Args(), true);\n}\n\nextern char cl_screengroup[];\nvoid CLSCR_Init(void)\n{\n\tint i;\n\tCmd_AddCommand(\"cprint\", SCR_CPrint_f);\n\n\tCvar_Register(&con_stayhidden, cl_screengroup);\n\tCvar_Register(&scr_loadingrefresh, cl_screengroup);\n\tCvar_Register(&scr_showloading, cl_screengroup);\n\tCvar_Register(&scr_loadingscreen_picture, cl_screengroup);\n\tCvar_Register(&scr_loadingscreen_scale, cl_screengroup);\n\tCvar_Register(&scr_loadingscreen_scale_limit, cl_screengroup);\n\tCvar_Register(&scr_loadingscreen_aspect, cl_screengroup);\n\tCvar_Register(&show_fps, cl_screengroup);\n\tCvar_Register(&show_fps_x, cl_screengroup);\n\tCvar_Register(&show_fps_y, cl_screengroup);\n\tCvar_Register(&show_clock, cl_screengroup);\n\tCvar_Register(&show_clock_x, cl_screengroup);\n\tCvar_Register(&show_clock_y, cl_screengroup);\n\tCvar_Register(&show_gameclock, cl_screengroup);\n\tCvar_Register(&show_gameclock_x, cl_screengroup);\n\tCvar_Register(&show_gameclock_y, cl_screengroup);\n\tCvar_Register(&show_speed, cl_screengroup);\n\tCvar_Register(&show_speed_x, cl_screengroup);\n\tCvar_Register(&show_speed_y, cl_screengroup);\n\tCvar_Register(&scr_neticontimeout, cl_screengroup);\n\tCvar_Register(&scr_showdisk, cl_screengroup);\n\tCvar_Register(&scr_showdisk_x, cl_screengroup);\n\tCvar_Register(&scr_showdisk_y, cl_screengroup);\n\tCvar_Register(&scr_diskicontimeout, cl_screengroup);\n\tCvar_Register(&scr_showobituaries, cl_screengroup);\n\n\n\tmemset(&key_customcursor, 0, sizeof(key_customcursor));\n\tfor (i = 0; i < kc_max; i++)\n\t\tkey_customcursor[i].dirty = true;\n\tscr_curcursor = NULL;\n\tif (rf && rf->VID_SetCursor)\n\t\trf->VID_SetCursor(scr_curcursor);\n}\n\n/*\n===============================================================================\n\nCENTER PRINTING\n\n===============================================================================\n*/\n\ntypedef struct cprint_s {\n\tunsigned int\tflags;\n\n\tconchar_t\t\t*string;\n\tconchar_t\t\t*cursorchar;\t//pointer into string\n\tsize_t\t\t\tstringbytes;\n\tsize_t\t\t\tcharcount;\n\tchar\t\t\ttitleimage[MAX_QPATH];\n\tfloat\t\t\ttime_start;   // for slow victory printing\n\tfloat\t\t\ttime_off;\n\tint\t\t\t\terase_lines;\n\tint\t\t\t\terase_center;\n\n\tint\t\t\t\toldmousex, oldmousey;\t//so the cursorchar can be changed by keyboard without constantly getting stomped on.\n\n\tstruct cprint_s *queue;\t//switch to the next on timeout.\n} cprint_t;\n\nstatic cprint_t *scr_centerprint[MAX_SPLITS];\n\n// SCR_StringToRGB: takes in \"<index>\" or \"<r> <g> <b>\" and converts to an RGB vector\nvoid SCR_StringToRGB (char *rgbstring, float *rgb, float rgbinputscale)\n{\n\tchar *t;\n\n\t//hex values\n\tif (!strncmp(rgbstring, \"0x\", 2))\n\t{\n\t\tchar *end;\n\t\tunsigned int val = strtoul(rgbstring+2, &end, 16);\n\t\tif (end == rgbstring + 5)\n\t\t{\n\t\t\trgb[0] = ((val&0xf00)>>8)/15.0;\n\t\t\trgb[1] = ((val&0x0f0)>>4)/15.0;\n\t\t\trgb[2] = ((val&0x00f)>>0)/15.0;\n\t\t}\n\t\telse if (end == rgbstring + 8)\n\t\t{\n\t\t\trgb[0] = ((val&0xff0000)>>12)/255.0;\n\t\t\trgb[1] = ((val&0x00ff00)>> 8)/255.0;\n\t\t\trgb[2] = ((val&0x0000ff)>> 0)/255.0;\n\t\t}\n\t\telse\n\t\t\trgb[0] = rgb[1] = rgb[2] = 1;\n\n\t\treturn ;\n\t}\n\n\tt = strstr(rgbstring, \" \");\n\n\tif (!t) // palette index\n\t{\n\t\tqbyte *pal;\n\t\tint i = atoi(rgbstring);\n\t\ti = bound(0, i, 255);\n\n\t\tpal = host_basepal;\n\n\t\tpal += (i * 3);\n\t\t// convert r8g8b8 to rgb floats\n\t\trgb[0] = (float)(pal[0]);\n\t\trgb[1] = (float)(pal[1]);\n\t\trgb[2] = (float)(pal[2]);\n\n\t\tVectorScale(rgb, 1/255.0, rgb);\n\t}\n\telse // use RGB coloring (input is scaled from 0-rgbinputscale)\n\t{\n\t\tt++;\n\t\trgb[0] = atof(rgbstring);\n\t\trgb[1] = atof(t);\n\t\tt = strstr(t, \" \"); // find last value\n\t\tif (t)\n\t\t\trgb[2] = atof(t+1);\n\t\telse\n\t\t\trgb[2] = 0.0;\n\n\t\trgbinputscale = 1/rgbinputscale;\n\t\tVectorScale(rgb, rgbinputscale, rgb);\n\t} // i contains the crosshair color\n}\n\nstatic qboolean SCR_CenterPrintPop(int pnum)\n{\n\tcprint_t *p = scr_centerprint[pnum];\n\tif (!p)\n\t\treturn false;\n\tscr_centerprint[pnum] = p->queue;\n\tZ_Free(p->string);\n\tZ_Free(p);\n\n\t//timers start NOW (not when its first queued, obviously)\n\tp = scr_centerprint[pnum];\n\tif (p)\n\t\tp->time_start = cl.time;\n\treturn true;\n}\n\n/*\n==============\nSCR_CenterPrint\n\nCalled for important messages that should stay in the center of the screen\nfor a few moments\n==============\n*/\nvoid SCR_CenterPrint (int pnum, const char *str, qboolean skipgamecode)\n{\n\tunsigned int pfl = 0;\n\tsize_t i;\n\tcprint_t *p, **l;\n\tqboolean doqueue = false;\n\tqboolean donotify = ((scr_logcenterprint.ival && !cl.deathmatch) || scr_logcenterprint.ival == 2);\n#ifdef HAVE_LEGACY\n\tif (scr_usekfont.ival)\n\t\tpfl |= PFS_FORCEUTF8;\n#endif\n\tif (!str)\n\t{\n\t\tif (cl.intermissionmode == IM_NONE)\n\t\t\twhile(SCR_CenterPrintPop(pnum))\n\t\t\t\t;\n\t\treturn;\n\t}\n\tif (!skipgamecode)\n\t{\n#ifdef CSQC_DAT\n\t\tif (CSQC_CenterPrint(pnum, str))\t//csqc nabbed it.\n\t\t\treturn;\n#endif\n\t}\n\n\tif (Cmd_AliasExist(\"f_centerprint\", RESTRICT_LOCAL))\n\t{\n\t\tcvar_t *var;\n\t\tvar = Cvar_FindVar (\"scr_centerprinttext\");\n\t\tif (!var)\n\t\t\tvar = Cvar_Get(\"scr_centerprinttext\", \"\", 0, \"Script Notifications\");\n\t\tif (var)\n\t\t{\n\t\t\tCvar_Set(var, str);\n\t\t\tCbuf_AddText(\"f_centerprint\\n\", RESTRICT_LOCAL);\n\t\t}\n\t}\n\n\tp = Z_Malloc(sizeof(*p));\n\tp->flags = 0;\n\tp->titleimage[0] = 0;\n\tp->cursorchar = NULL;\n\tp->time_off = scr_centertime.value;\n\n\tif (*str != '/')\n\t{\n\t\tif (cl.intermissionmode != IM_NONE)\n\t\t{\n\t\t\tp->flags |= CPRINT_TYPEWRITER | CPRINT_PERSIST | CPRINT_TALIGN;\n\t\t\tif (cl.intermissionmode != IM_NQCUTSCENE)\n\t\t\t\tQ_strncpyz(p->titleimage, \"gfx/finale.lmp\", sizeof(p->titleimage));\n\t\t}\n\t}\n\n\twhile (*str == '/')\n\t{\n\t\tif (str[1] == '.')\n\t\t{\n/* /. means text actually starts after, no more flags */\n\t\t\tstr+=2;\n\t\t\tbreak;\n\t\t}\n\t\telse if (str[1] == 'P')\n\t\t{\n\t\t\tp->flags |= CPRINT_PERSIST | CPRINT_BACKGROUND;\n\t\t\tp->flags &= ~CPRINT_TALIGN;\n\t\t}\n\t\telse if (str[1] == 'C')\n\t\t\tp->flags |= CPRINT_CURSOR;\t//this can be a little jarring if there's no links, so this forces consistent behaviour.\n\t\telse if (str[1] == 'W')\t//wait between each char\n\t\t\tp->flags ^= CPRINT_TYPEWRITER;\n\t\telse if (str[1] == 'Q')\t//queue\n\t\t\tdoqueue = true;\n\t\telse if (str[1] == 'D')\t//explicit delay\n\t\t{\n\t\t\tp->time_off = strtod(str+2, (char**)&str);\n\t\t\tcontinue;\n\t\t}\n\t\telse if (str[1] == 'S')\t//Stay\n\t\t\tp->flags ^= CPRINT_PERSIST;\n\t\telse if (str[1] == 'M')\t//'Mask' the background so that its readable.\n\t\t\tp->flags ^= CPRINT_BACKGROUND;\n\t\telse if (str[1] == 'N')\t//'Notify' spam it to the console even in deathmatch.\n\t\t{\n\t\t\tdonotify = strtol(str+2, (char**)&str, 10);\n\t\t\tcontinue;\n\t\t}\n\t\telse if (str[1] == 'O')\t//Obituaries are shown at the bottom, ish.\n\t\t\tp->flags ^= CPRINT_OBITUARTY;\n\t\telse if (str[1] == 'B')\n\t\t\tp->flags ^= CPRINT_BALIGN;\t//Note: you probably want to add some blank lines...\n\t\telse if (str[1] == 'T')\n\t\t\tp->flags ^= CPRINT_TALIGN;\n\t\telse if (str[1] == 'L')\n\t\t\tp->flags ^= CPRINT_LALIGN;\n\t\telse if (str[1] == 'R')\n\t\t\tp->flags ^= CPRINT_RALIGN;\n\t\telse if (str[1] == 'F')\n\t\t{\t//'F' is reserved for special handling via svc_finale\n\t\t\tif (cl.intermissionmode == IM_NONE)\n\t\t\t{\n\t\t\t\tTP_ExecTrigger (\"f_mapend\", false);\n\t\t\t\tif (cl.playerview[pnum].spectator || cls.demoplayback)\n\t\t\t\t\tTP_ExecTrigger (\"f_specmapend\", true);\n\t\t\t\telse\n\t\t\t\t\tTP_ExecTrigger (\"f_playmapend\", true);\n\t\t\t\tcl.completed_time = cl.time;\n\t\t\t}\n\t\t\tstr+=2;\n\t\t\tswitch(*str++)\n\t\t\t{\n\t\t\tcase 'R':\t//remove intermission\n\t\t\t\tcl.intermissionmode = IM_NONE;\n\t\t\t\tbreak;\n\t\t\tcase 'I':\t//standard intermission\n\t\t\tcase 'S':\t//score board / map stats\n\t\t\t\tcl.intermissionmode = IM_NQSCORES;\n\t\t\t\tbreak;\n\t\t\tcase 'F':\t//finale\n\t\t\t\tcl.intermissionmode = IM_NQFINALE;\n\t\t\t\tbreak;\n\t\t\tcase 'f':\t//finale, but with the view offset properly\n\t\t\t\tcl.intermissionmode = IM_H2FINALE;\n\t\t\t\tbreak;\n\t\t\tcase 0:\n\t\t\t\tstr--;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tbreak;\t//no idea. suck it up for compat.\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\telse if (str[1] == 'I')\n\t\t{\n\t\t\tconst char *e;\n\t\t\tint l;\n\t\t\tstr+=2;\n\t\t\te = strchr(str, ':');\n\t\t\tif (!e)\n\t\t\t\te = strchr(str, ' ');\t//probably an error\n\t\t\tif (!e)\n\t\t\t\te = str + strlen(str)-1;\t//error\n\t\t\tl = e - str;\n\t\t\tif (l >= sizeof(p->titleimage))\n\t\t\t\tl = sizeof(p->titleimage)-1;\n\t\t\tstrncpy(p->titleimage, str, l);\n\t\t\tp->titleimage[l] = 0;\n\t\t\tstr = e+1;\n\t\t\tcontinue;\n\t\t}\n\t\telse\n\t\t\tbreak;\n\t\tstr += 2;\n\t}\n\n\tif (!doqueue)\t//kill all prior ones if we're not queueing\n\t\twhile (SCR_CenterPrintPop(pnum))\n\t\t\t;\n\t//add it to the end\n\tfor (l = &scr_centerprint[pnum]; *l; l = &(*l)->queue)\n\t\t;\n\t*l = p;\n\n\n\tif (donotify && !(p->flags & CPRINT_PERSIST))\n\t{\n\t\t//don't spam too much.\n\t\tif (*str && strncmp(cl.lastcenterprint, str, sizeof(cl.lastcenterprint)-1))\n\t\t{\n\t\t\tQ_strncpyz(cl.lastcenterprint, str, sizeof(cl.lastcenterprint));\n\t\t\tCon_CenterPrint(str);\n\t\t}\n\t}\n\n\tfor (;;)\n\t{\n\t\tp->charcount = COM_ParseFunString(CON_WHITEMASK, str, p->string, p->stringbytes, pfl) - p->string;\n\n\t\tif ((p->charcount+1)*sizeof(*p->string) < p->stringbytes)\n\t\t\tbreak;\n\t\telse\n\t\t{\n\t\t\tp->stringbytes=p->stringbytes*2+sizeof(*p->string);\n\t\t\tZ_Free(p->string);\n\t\t\tp->string = Z_Malloc(p->stringbytes);\n\t\t}\n\t}\n\n\tif (!(p->flags & CPRINT_CURSOR))\n\t{\t//autodetect links\n\t\tfor (i = 0; i < p->charcount; i++)\n\t\t{\n\t\t\tif (p->string[i] == CON_LINKSTART)\n\t\t\t{\n\t\t\t\tp->flags |= CPRINT_CURSOR;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\tp->time_start = cl.time;\n\tVRUI_SnapAngle();\n}\n\nvoid VARGS Stats_Message(char *msg, ...)\n{\n\tva_list\t\targptr;\n\tchar str[2048];\n\tcprint_t *p = scr_centerprint[0];\n\tif (!scr_showobituaries.ival)\n\t\treturn;\n\tif (p)\t//don't show if one is shown... FIXME: queue these too (some depth cutoff?)\n\t\treturn;\n\n\tva_start (argptr, msg);\n\tvsnprintf (str,sizeof(str)-1, msg, argptr);\n\tva_end (argptr);\n\n\tp = scr_centerprint[0] = Z_Malloc(sizeof(*p));\n\tp->flags = CPRINT_OBITUARTY;\n\tp->titleimage[0] = 0;\n\n\tfor (;;)\n\t{\n\t\tp->charcount = COM_ParseFunString(CON_WHITEMASK, str, p->string, p->stringbytes, false) - p->string;\n\n\t\tif ((p->charcount+1)*sizeof(*p->string) < p->stringbytes)\n\t\t\tbreak;\n\t\telse\n\t\t{\n\t\t\tp->stringbytes=p->stringbytes*2+sizeof(*p->string);\n\t\t\tZ_Free(p->string);\n\t\t\tp->string = Z_Malloc(p->stringbytes);\n\t\t}\n\t}\n\n\tp->time_off = scr_centertime.value;\n\tp->time_start = cl.time;\n}\n\nstatic char *SCR_CopyCenterPrint(cprint_t *p)\t//reads the link under the mouse cursor. non-links are ignored.\n{\n\tsize_t maxlen, outlen;\n\tchar *result;\n\n\tconchar_t *start = p->cursorchar, *end;\n\n\tif (!start)\t//cursor isn't over anything.\n\t\treturn NULL;\n\n\t//scan backwards to find any link enclosure\n\tfor(end = start-1; end >= p->string; end--)\n\t{\n\t\tif (*end == CON_LINKSTART)\n\t\t{\n\t\t\t//found one\n\t\t\tstart = end;\n\t\t\tbreak;\n\t\t}\n\t\tif (*end == CON_LINKEND)\n\t\t{\n\t\t\t//some other link ended here. don't use its start.\n\t\t\tbreak;\n\t\t}\n\t}\n\t//scan forwards to find the end of the selected link\n\tif (start < p->string+p->charcount && *start == CON_LINKSTART)\n\t{\n\t\tfor(end = start; end < p->string + p->charcount; end++)\n\t\t{\n\t\t\tif (*end == CON_LINKEND)\n\t\t\t{\n\t\t\t\tend++;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\telse// if (onlyiflink)\n\t\treturn NULL;\n\n\tmaxlen = 1024*1024;\n\tresult = Z_Malloc(maxlen+1);\n\n\toutlen = COM_DeFunString(start, end, result, maxlen, false, false) - result;\n\n\tresult[outlen++] = 0;\n\treturn result;\n}\n\n#define MAX_CPRINT_LINES 512\nint SCR_DrawCenterString (vrect_t *playerrect, cprint_t *p, struct font_s *font)\n{\n\tint\t\t\t\tl;\n\tint\t\t\t\ty, x;\n\tint\t\t\t\tleft;\n\tint\t\t\t\tright;\n\tint\t\t\t\ttop;\n\tint\t\t\t\tbottom;\n\tint\t\t\t\tremaining;\n\tshader_t\t\t*pic;\n\tint\t\t\t\tch;\n\tint mousex,mousey, mousemoved;\n\n\tconchar_t *line_start[MAX_CPRINT_LINES];\n\tconchar_t *line_end[MAX_CPRINT_LINES];\n\tint linecount;\n\n\tvrect_t rect = *playerrect;\n\n// the finale prints the characters one at a time\n\tif (p->flags & CPRINT_TYPEWRITER)\n\t\tremaining = scr_printspeed.value * (cl.time - p->time_start);\n\telse\n\t\tremaining = 9999;\n\n\tp->erase_center = 0;\n\n\tif (*p->titleimage)\n\t\tpic = R2D_SafeCachePic (p->titleimage);\n\telse\n\t\tpic = NULL;\n\n\tif (p->flags & CPRINT_BACKGROUND)\n\t{\t//hexen2 style plaque.\n\t\tint w = 320;\n\t\tif (rect.width > w)\n\t\t{\n\t\t\trect.x = (rect.x + rect.width/2) - (w / 2);\n\t\t\trect.width = w;\n\t\t}\n\n\t\tif (rect.width < 32)\n\t\t\treturn 0;\n\t\trect.x += 16;\n\t\trect.width -= 32;\n\t}\n\n\ty = rect.y;\n\n\tif (pic)\n\t{\n\t\t//the pic is just a header\n\t\tif (!(p->flags & CPRINT_BACKGROUND))\n\t\t{\n\t\t\tint w, h;\n\t\t\tR_GetShaderSizes(pic, &w, &h, false);\n\t\t\tw *= 24.0/h;\n\t\t\th = 24;\n\t\t\ty+= 16;\n\t\t\tR2D_ScalePic (rect.x + (rect.width-w)/2, y, w, h, pic);\n\t\t\ty+= h;\n\t\t\ty+= 8;\n\t\t}\n\t}\n\n\tFont_BeginString(font, mousecursor_x, mousecursor_y, &mousex, &mousey);\n\tFont_BeginString(font, rect.x, y, &left, &top);\n\tFont_BeginString(font, rect.x+rect.width, rect.y+rect.height, &right, &bottom);\n\tlinecount = Font_LineBreaks(p->string, p->string + p->charcount, (p->flags & CPRINT_NOWRAP)?0x7fffffff:(right - left), MAX_CPRINT_LINES, line_start, line_end);\n\n\tmousemoved = mousex != p->oldmousex || mousey != p->oldmousey;\n\tp->oldmousex = mousex;\n\tp->oldmousey = mousey;\n\n\tch = Font_CharHeight();\n\n\tif (p->flags & CPRINT_TALIGN)\n\t\ty = top;\n\telse if (p->flags & CPRINT_BALIGN)\n\t\ty = bottom - ch*linecount;\n\telse if (p->flags & CPRINT_OBITUARTY)\n\t\t//'obituary' messages appear at the bottom of the screen\n\t\ty = (bottom-top - ch*linecount) * 0.65 + top;\n\telse\n\t{\n\t\tif (linecount <= 5)\n\t\t{\n\t\t\t//small messages appear above and away from the crosshair\n\t\t\ty = (bottom-top - ch*linecount) * 0.35 + top;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t//longer messages are fully centered\n\t\t\ty = (bottom-top - ch*linecount) * 0.5 + top;\n\t\t}\n\t}\n\n\tif (p->flags & CPRINT_BACKGROUND)\n\t{\t//hexen2 style plaque.\n\t\tFont_EndString(font);\n\n\t\tif (*p->titleimage && pic)\n\t\t{\n\t\t\tint w, h;\n\t\t\tR_GetShaderSizes(pic, &w, &h, false);\n\t\t\tR2D_Letterbox(playerrect->x, playerrect->y, playerrect->width, playerrect->height, pic, w, h);\n\t\t}\n\t\telse\n\t\t\tDraw_ApproxTextBox(rect.x, (y * (float)vid.height) / (float)vid.pixelheight, rect.width, linecount*Font_CharVHeight(font));\n\n\t\tFont_BeginString(font, rect.x, y, &left, &top);\n\t}\n\n\tx = (left+right)/2;\n\tfor (l = 0; l < linecount; l++, y += ch)\n\t{\n\t\tif (y >= bottom)\n\t\t\tbreak;\n\t\tif (p->flags & CPRINT_RALIGN)\n\t\t\tx = right - Font_LineWidth(line_start[l], line_end[l]);\n\t\telse if (p->flags & CPRINT_LALIGN)\n\t\t\tx = left;\n\t\telse\n\t\t\tx = left + (right - left - Font_LineWidth(line_start[l], line_end[l]))/2;\n\n\t\tif (mousemoved && mousey >= y && mousey < y+ch)\n\t\t{\n\t\t\tconchar_t *linkstart;\n\t\t\tlinkstart = Font_CharAt(mousex - x, line_start[l], line_end[l]);\n\n\t\t\t//scan backwards to find any link enclosure\n\t\t\tif (linkstart)\n\t\t\t\tfor(linkstart = linkstart-1; linkstart >= line_start[l]; linkstart--)\n\t\t\t\t{\n\t\t\t\t\tif (*linkstart == CON_LINKSTART)\n\t\t\t\t\t{\n\t\t\t\t\t\t//found one\n\t\t\t\t\t\tp->cursorchar = linkstart;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tif (*linkstart == CON_LINKEND)\n\t\t\t\t\t{\n\t\t\t\t\t\t//some other link ended here. don't use its start.\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t}\n\n\t\tremaining -= line_end[l]-line_start[l];\n\t\tif (remaining <= 0)\n\t\t{\n\t\t\tif (p->time_off && (p->flags&(CPRINT_TYPEWRITER|CPRINT_PERSIST))==CPRINT_TYPEWRITER)\n\t\t\t\tp->time_off = scr_centertime.value;\t//reset the timeout while we're still truncating it.\n\t\t\tline_end[l] += remaining;\n\t\t\tif (line_end[l] <= line_start[l])\n\t\t\t\tline_end[l] = line_start[l];\n\t\t}\n\n\t\tif (p->cursorchar && p->cursorchar >= line_start[l] && p->cursorchar < line_end[l] && *p->cursorchar == CON_LINKSTART)\n\t\t{\n\t\t\tconchar_t *linkend;\n\t\t\tint s, e;\n\t\t\tfor (linkend = p->cursorchar; linkend < line_end[l]; linkend++)\n\t\t\t\tif (*linkend == CON_LINKEND)\n\t\t\t\t\tbreak;\n\n\t\t\ts = x+Font_LineWidth(line_start[l], p->cursorchar);\n\t\t\te = x+Font_LineWidth(line_start[l], linkend);\n\n\t\t\t//draw a 2-pixel underscore (behind the text).\n\t\t\tR2D_ImageColours(SRGBA(0.3,0.3,0.3, 1));\t//mouseover.\n\t\t\tR2D_FillBlock((s*vid.width)/(float)vid.rotpixelwidth, ((y+Font_CharHeight()-2)*vid.height)/(float)vid.rotpixelheight, ((e - s)*vid.width)/(float)vid.rotpixelwidth, (2*vid.height)/(float)vid.rotpixelheight);\n\t\t\tR2D_Flush();\n\t\t\tR2D_ImageColours(SRGBA(1, 1, 1, 1));\n\t\t}\n\n\t\tFont_LineDraw(x, y, line_start[l], line_end[l]);\n\n\t\tif (remaining <= 0 || l == linecount-1)\n\t\t{\n\t\t\tif ((p->flags&(CPRINT_TYPEWRITER|CPRINT_PERSIST))==CPRINT_TYPEWRITER && l < linecount && ((int)(realtime*4)&1))\n\t\t\t{\n\t\t\t\tx = x+Font_LineWidth(line_start[l], line_end[l]);\n\t\t\t\tFont_DrawChar(x, y, CON_WHITEMASK, 0xe00b);\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tFont_EndString(font);\n\n\treturn linecount;\n}\n\nstatic void Key_CenterPrintActivate(int pnum)\n{\n\tchar *link;\n\tcprint_t *p = scr_centerprint[pnum];\n\tlink = SCR_CopyCenterPrint(p);\n\tif (link)\n\t{\n\n\t\tif (link[0] == '^' && link[1] == '[')\n\t\t{\n\t\t\t//looks like it might be a link!\n\t\t\tchar *end = NULL;\n\t\t\tchar *info;\n\t\t\tfor (info = link + 2; *info; )\n\t\t\t{\n\t\t\t\tif (info[0] == '^' && info[1] == ']')\n\t\t\t\t\tbreak; //end of tag, with no actual info, apparently\n\t\t\t\tif (*info == '\\\\')\n\t\t\t\t\tbreak;\n\t\t\t\telse if (info[0] == '^' && info[1] == '^')\n\t\t\t\t\tinfo+=2;\n\t\t\t\telse\n\t\t\t\t\tinfo++;\n\t\t\t}\n\t\t\tfor(end = info; *end; )\n\t\t\t{\n\t\t\t\tif (end[0] == '^' && end[1] == ']')\n\t\t\t\t{\n\t\t\t\t\t//okay, its a valid link that they clicked\n\t\t\t\t\t*end = 0;\n\n#ifdef PLUGINS\n\t\t\t\t\tif (!Plug_ConsoleLink(link+2, info, \"\"))\n#endif\n#ifdef CSQC_DAT\n\t\t\t\t\tif (!CSQC_ConsoleLink(link+2, info))\n#endif\n\t\t\t\t\t\tKey_DefaultLinkClicked(NULL, link+2, info);\n\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tif (end[0] == '^' && end[1] == '^')\n\t\t\t\t\tend+=2;\n\t\t\t\telse\n\t\t\t\t\tend++;\n\t\t\t}\n\t\t}\n\t}\n}\nqboolean Key_Centerprint(int key, int unicode, unsigned int devid)\n{\n\tint pnum;\n\tcprint_t *p;\n\n\tif (key == K_MOUSE1)\n\t{\n\t\t//figure out which player has the cursor\n\t\tfor (pnum = 0; pnum < cl.splitclients; pnum++)\n\t\t{\n\t\t\tif (cl.playerview[pnum].gamerectknown == cls.framecount)\n\t\t\t\tKey_CenterPrintActivate(pnum);\n\t\t}\n\t\treturn true;\t//handled\n\t}\n\telse if (key == K_MOUSE2)\n\t{\n\t\tfor (pnum = 0; pnum < cl.splitclients; pnum++)\n\t\t{\n\t\t\tp = scr_centerprint[pnum];\n\t\t\tif (p)\n\t\t\t\tp->flags &= ~CPRINT_CURSOR;\n\t\t}\n\t\treturn true;\n\t}\n\telse if ((key == K_ENTER ||\n\t\t\t  key == K_KP_ENTER ||\n\t\t\t  key == K_GP_DIAMOND_RIGHT) && devid < countof(scr_centerprint))\n\t{\n\t\tp = scr_centerprint[devid];\n\t\tif (p && p->cursorchar)\n\t\t\tKey_CenterPrintActivate(devid);\n\t\treturn true;\n\t}\n\telse if ((key == K_UPARROW ||\n\t\t\t  key == K_LEFTARROW ||\n\t\t\t  key == K_KP_UPARROW ||\n\t\t\t  key == K_KP_LEFTARROW ||\n\t\t\t  key == K_GP_DPAD_UP ||\n\t\t\t  key == K_GP_DPAD_LEFT) && devid < countof(scr_centerprint))\n\t{\n\t\tp = scr_centerprint[devid];\n\t\tif (p)\n\t\t{\n\t\t\tif (!p->cursorchar)\n\t\t\t\tp->cursorchar = p->string + p->charcount;\n\t\t\twhile (--p->cursorchar >= p->string)\n\t\t\t{\n\t\t\t\tif (*p->cursorchar == CON_LINKSTART)\n\t\t\t\t\treturn true;\t//found one\n\t\t\t}\n\t\t\tp->cursorchar = NULL;\n\t\t}\n\t\treturn true;\n\t}\n\telse if ((key == K_DOWNARROW ||\n\t\t\t  key == K_RIGHTARROW ||\n\t\t\t  key == K_KP_DOWNARROW ||\n\t\t\t  key == K_KP_RIGHTARROW ||\n\t\t\t  key == K_GP_DPAD_DOWN ||\n\t\t\t  key == K_GP_DPAD_RIGHT) && devid < countof(scr_centerprint))\n\t{\n\t\tp = scr_centerprint[devid];\n\t\tif (p)\n\t\t{\n\t\t\tif (!p->cursorchar)\n\t\t\t\tp->cursorchar = p->string-1;\n\t\t\twhile (++p->cursorchar < p->string + p->charcount)\n\t\t\t{\n\t\t\t\tif (*p->cursorchar == CON_LINKSTART)\n\t\t\t\t\treturn true;\t//found one\n\t\t\t}\n\t\t\tp->cursorchar = NULL;\t//hit the end\n\t\t}\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\nvoid SCR_CheckDrawCenterString (void)\n{\n\tint pnum;\n\tcprint_t *p;\n\n\tKey_Dest_Remove(kdm_centerprint);\n\n\tfor (pnum = 0; pnum < cl.splitclients; pnum++)\n\t{\n#ifdef QUAKESTATS\n\t\tif (IN_DrawWeaponWheel(pnum))\n\t\t{\t//we won't draw the cprint, but it also won't fade while the wwheel is shown.\n\t\t\tcontinue;\n\t\t}\n#endif\n\n\t\tp = scr_centerprint[pnum];\n\t\tif (!p)\n\t\t\tcontinue;\n\t\tif (p->time_off <= 0 && !(p->flags & CPRINT_PERSIST))\t//'/P' prefix doesn't time out\n\t\t{\t//this one is done. move to the next.\n\t\t\tif (SCR_CenterPrintPop(pnum))\n\t\t\t\tpnum++;\n\t\t\tcontinue;\n\t\t}\n\n\t\tp->time_off -= host_frametime;\n\n\t\tif (Key_Dest_Has(~(kdm_game|kdm_centerprint)))\t//don't let progs guis/centerprints interfere with the game menu\n\t\t\tcontinue;\t\t\t\t\t//should probably allow the console with a scissor region or something.\n\n#ifdef QUAKEHUD\n\t\tif (cl.playerview[pnum].sb_showscores || cl.playerview[pnum].sb_showteamscores)\t//this was annoying\n\t\t\tcontinue;\n#endif\n\n\t\tif (cl.playerview[pnum].gamerectknown == cls.framecount)\n\t\t{\n\t\t\tSCR_DrawCenterString(&cl.playerview[pnum].gamerect, p, font_default);\n\t\t\tif (p->flags & CPRINT_CURSOR)\n\t\t\t\tKey_Dest_Add(kdm_centerprint);\n\t\t}\n\t}\n}\n\nint R_DrawTextField(int x, int y, int w, int h, const char *text, unsigned int defaultmask, unsigned int fieldflags, struct font_s *font, vec2_t fontscale)\n{\n\tcprint_t p;\n\tvrect_t r;\n\tconchar_t buffer[16384];\t//FIXME: make dynamic.\n\tint lines;\n\n\tp.cursorchar = NULL;\n\tp.string = buffer;\n\tp.stringbytes = sizeof(buffer);\n\tr.x = x;\n\tr.y = y;\n\tr.width = w;\n\tr.height = h;\n\n\tp.flags = fieldflags;\n\tp.charcount = COM_ParseFunString(defaultmask, text, p.string, p.stringbytes, false) - p.string;\n\tp.time_off = scr_centertime.value;\n\tp.time_start = cl.time;\n\t*p.titleimage = 0;\n\n\n\tlines = SCR_DrawCenterString(&r, &p, font);\n\n//\tSCR_CopyCenterPrint(&p);\n\treturn lines;\n}\n\nqboolean SCR_HardwareCursorIsActive(void)\n{\n\tif (Key_MouseShouldBeFree())\n\t\treturn !!scr_curcursor;\n\treturn false;\n}\nvoid SCR_DrawCursor(void)\n{\n\textern cvar_t cl_cursor, cl_cursorbiasx, cl_cursorbiasy, cl_cursorscale, cl_prydoncursor;\n\tmpic_t *p;\n\tchar *newc;\n\tint prydoncursornum = 0;\n\textern qboolean cursor_active;\n\tstruct key_cursor_s *kcurs;\n\tvoid *oldcurs = NULL;\n\n\tif (cursor_active && cl_prydoncursor.ival > 0)\n\t\tprydoncursornum = cl_prydoncursor.ival;\n\telse if (!Key_MouseShouldBeFree())\n\t\treturn;\n\n\tif ((key_dest_mask & key_dest_absolutemouse & kdm_prompt))\n\t{\n\t\tif (promptmenu&&promptmenu->cursor)\n\t\t\tkcurs = promptmenu->cursor;\n\t\telse\n\t\t\tkcurs = &key_customcursor[kc_console];\n\t}\n\t//choose the cursor based upon the module that has primary focus\n\telse if (key_dest_mask & key_dest_absolutemouse & (kdm_console|kdm_cwindows))\n\t\tkcurs = &key_customcursor[kc_console];\n\telse if ((key_dest_mask & key_dest_absolutemouse & kdm_menu))\n\t{\n\t\tif (topmenu&&topmenu->cursor)\n\t\t\tkcurs = topmenu->cursor;\n\t\telse\n\t\t\tkcurs = &key_customcursor[kc_console];\n\t}\n\telse// if (key_dest_mask & key_dest_absolutemouse)\n\t\tkcurs = &key_customcursor[prydoncursornum?kc_console:kc_game];\n\n\tif (kcurs == &key_customcursor[kc_console])\n\t{\n\t\tif (!*cl_cursor.string || prydoncursornum>1)\n\t\t\tnewc = va(\"gfx/prydoncursor%03i.lmp\", prydoncursornum);\n\t\telse\n\t\t\tnewc = cl_cursor.string;\n\t\tif (strcmp(kcurs->name, newc) || kcurs->hotspot[0] != cl_cursorbiasx.value || kcurs->hotspot[1] != cl_cursorbiasy.value || kcurs->scale != cl_cursorscale.value)\n\t\t{\n\t\t\tkcurs->dirty = true;\n\t\t\tQ_strncpyz(kcurs->name, newc, sizeof(kcurs->name));\n\t\t\tkcurs->hotspot[0] = cl_cursorbiasx.value;\n\t\t\tkcurs->hotspot[1] = cl_cursorbiasy.value;\n\t\t\tkcurs->scale = cl_cursorscale.value;\n\t\t}\n\t}\n\n\tif (vrui.enabled && kcurs->handle)\n\t\tkcurs->dirty = true;\n\n\tif (kcurs->dirty)\n\t{\n\t\tif (kcurs->scale <= 0 || !*kcurs->name)\n\t\t{\n\t\t\tkcurs->hotspot[0] = cl_cursorbiasx.value;\n\t\t\tkcurs->hotspot[1] = cl_cursorbiasy.value;\n\t\t\tkcurs->scale = cl_cursorscale.value;\n\t\t}\n\n\t\tkcurs->dirty = false;\n\t\toldcurs = kcurs->handle;\n\t\tif (rf->VID_CreateCursor && !vrui.enabled && strcmp(kcurs->name, \"none\"))\n\t\t{\n\t\t\timage_t dummytex;\n\t\t\tflocation_t loc;\n\t\t\tchar bestname[MAX_QPATH];\n\t\t\tunsigned int bestflags;\n\t\t\tqbyte *rgbadata;\n\t\t\tuploadfmt_t format;\n\t\t\tvoid *filedata = NULL;\n\t\t\tint filelen = 0, width, height;\n\n\t\t\tkcurs->handle = NULL;\n\n\t\t\tmemset(&dummytex, 0, sizeof(dummytex));\n\t\t\tdummytex.flags = IF_NOREPLACE;\t//no dds files\n\t\t\t*bestname = 0;\n\n\t\t\t//first try the named image, if possible\n\t\t\tif (!filedata && *kcurs->name)\n\t\t\t{\n\t\t\t\tdummytex.ident = kcurs->name;\n\t\t\t\tif (Image_LocateHighResTexture(&dummytex, &loc, bestname, sizeof(bestname), &bestflags))\n\t\t\t\t\tfilelen = FS_LoadFile(bestname, &filedata);\n\t\t\t}\n\t\t\tif (!filedata)\n\t\t\t{\n\t\t\t\tdummytex.ident = \"gfx/cursor.lmp\";\n\t\t\t\tif (Image_LocateHighResTexture(&dummytex, &loc, bestname, sizeof(bestname), &bestflags))\n\t\t\t\t\tfilelen = FS_LoadFile(bestname, &filedata);\n\t\t\t}\n\t\t\tif (!filedata)\n\t\t\t{\n\t\t\t\tstatic qbyte lamecursor[] =\n\t\t\t\t{\n#define W 0x8f,0x8f,0x8f,0xff,\n#define B 0x00,0x00,0x00,0xff,\n#define T 0x00,0x00,0x00,0x00,\n\t\t\t\t\tW T T T T T T T\n\t\t\t\t\tW W T T T T T T\n\t\t\t\t\tW B W T T T T T\n\t\t\t\t\tW B B W T T T T\n\n\t\t\t\t\tW B B B W T T T\n\t\t\t\t\tW B B B B W T T\n\t\t\t\t\tW B B B B B W T\n\t\t\t\t\tW B B B B B B W\n\n\t\t\t\t\tW B B B B W W W\n\t\t\t\t\tW B W B B W T T\n\t\t\t\t\tW W T W B B W T\n\t\t\t\t\tW T T W B B W T\n\n\t\t\t\t\tT T T T W B B W\n\t\t\t\t\tT T T T W B B W\n\t\t\t\t\tT T T T T W W T\n#undef W\n#undef B\n\t\t\t\t};\n\t\t\t\tkcurs->handle = rf->VID_CreateCursor(lamecursor, 8, 15, PTI_LLLA8, 0, 0, 1);\t//try the fallback\n\t\t\t}\n\t\t\telse if (!filedata)\n\t\t\t\tFS_FreeFile(filedata);\t//format not okay, just free it.\n\t\t\telse\n\t\t\t{\t//raw file loaded.\n\t\t\t\trgbadata = ReadRawImageFile(filedata, filelen, &width, &height, &format, true, bestname);\n\t\t\t\tFS_FreeFile(filedata);\n\t\t\t\tif (rgbadata)\n\t\t\t\t{\t//image loaded properly, yay\n\t\t\t\t\tif ((format==PTI_BGRX8 || format==PTI_RGBX8 || format==PTI_LLLX8) && !strchr(bestname, ':'))\n\t\t\t\t\t\tImage_ReadExternalAlpha(rgbadata, width, height, bestname, &format);\n\n\t\t\t\t\tkcurs->handle = rf->VID_CreateCursor(rgbadata, width, height, format, kcurs->hotspot[0], kcurs->hotspot[1], kcurs->scale);\t//try the fallback\n\t\t\t\t\tBZ_Free(rgbadata);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse\n\t\t\tkcurs->handle = NULL;\n\t}\n\n\tif (scr_curcursor != kcurs->handle)\n\t{\n\t\tscr_curcursor = kcurs->handle;\n\t\trf->VID_SetCursor(scr_curcursor);\n\t}\n\tif (oldcurs)\n\t\trf->VID_DestroyCursor(oldcurs);\n\n\tif (scr_curcursor)\n\t\treturn;\n\t//system doesn't support a hardware cursor, so try to draw a software one.\n\n\tif (!strcmp(kcurs->name, \"none\"))\n\t\treturn;\n\n\tp = R2D_SafeCachePic(kcurs->name);\n\tif (!p || !R_GetShaderSizes(p, NULL, NULL, false))\n\t\tp = R2D_SafeCachePic(\"gfx/cursor.lmp\");\n\tif (p && R_GetShaderSizes(p, NULL, NULL, false))\n\t{\n\t\tR2D_ImageColours(1, 1, 1, 1);\n\t\tR2D_Image(mousecursor_x-kcurs->hotspot[0], mousecursor_y-kcurs->hotspot[1], p->width*cl_cursorscale.value, p->height*cl_cursorscale.value, 0, 0, 1, 1, p);\n\t}\n\telse\n\t{\n\t\tfloat x, y;\n\t\tFont_BeginScaledString(font_default, mousecursor_x, mousecursor_y, 8, 8, &x, &y);\n\t\tx -= Font_CharScaleWidth(CON_WHITEMASK, '+' | 0xe000)/2;\n\t\ty -= Font_CharHeight()/2;\n\t\tFont_DrawScaleChar(x, y, CON_WHITEMASK, '+' | 0xe000);\n\t\tFont_EndString(font_default);\n\t}\n}\nstatic void SCR_DrawSimMTouchCursor(void)\n{\n\tint i;\n\tfloat x, y;\n\tfor (i = 0; i < 8; i++)\n\t{\n\t\tif (multicursor_active[i])\n\t\t{\n\t\t\tFont_BeginScaledString(font_default, multicursor_x[i], multicursor_y[i], 8, 8, &x, &y);\n\t\t\tx -= Font_CharScaleWidth(CON_WHITEMASK, '+' | 0xe000)/2;\n\t\t\ty -= Font_CharHeight()/2;\n\t\t\tFont_DrawScaleChar(x, y, CON_WHITEMASK, '+' | 0xe000);\n\t\t\tFont_EndString(font_default);\n\t\t}\n\t}\n}\n\n////////////////////////////////////////////////////////////////\n//TEI_SHOWLMP2 (not 3)\n//\ntypedef struct showpic_s {\n\tstruct showpic_s *next;\n\tqbyte zone;\n\tqboolean persist;\n\tfloat fadedelay;\n\tshort x, y, w, h;\n\tchar *name;\n\tchar *picname;\n\tchar *tcommand;\n} showpic_t;\nshowpic_t *showpics;\ndouble showpics_touchtime;\n\nstatic void SP_RecalcXY ( float *xx, float *yy, int origin )\n{\n\tint midx, midy;\n\tfloat x,y;\n\n\tx = xx[0];\n\ty = yy[0];\n\n\tmidy = vid.height * 0.5;// >>1\n\tmidx = vid.width * 0.5;// >>1\n\n\tswitch (origin)\n\t{\n\t\t//tei's original encoding\n\t\tcase SL_ORG_NW:\n\t\t\tbreak;\n\t\tcase SL_ORG_NE:\n\t\t\tx = vid.width - x;//Inv\n\t\t\tbreak;\n\t\tcase SL_ORG_SW:\n\t\t\ty = vid.height - y;//Inv\n\t\t\tbreak;\n\t\tcase SL_ORG_SE:\n\t\t\ty = vid.height - y;//inv\n\t\t\tx = vid.width - x;//Inv\n\t\t\tbreak;\n\t\tcase SL_ORG_CC:\n\t\t\ty = midy + (y - 8000);//NegCoded\n\t\t\tx = midx + (x - 8000);//NegCoded\n\t\t\tbreak;\n\t\tcase SL_ORG_CN:\n\t\t\tx = midx + (x - 8000);//NegCoded\n\t\t\tbreak;\n\t\tcase SL_ORG_CS:\n\t\t\tx = midx + (x - 8000);//NegCoded\n\t\t\ty = vid.height - y;//Inverse\n\t\t\tbreak;\n\t\tcase SL_ORG_CW:\n\t\t\ty = midy + (y - 8000);//NegCoded\n\t\t\tbreak;\n\t\tcase SL_ORG_CE:\n\t\t\ty = midy + (y - 8000);//NegCoded\n\t\t\tx = vid.height - x; //Inverse\n\t\t\tbreak;\n\n\t\t//spike's attempt to provide sane origins that are a little more predictable.\n\t\tcase SL_ORG_TL:\n\t\t\tbreak;\n\t\tcase SL_ORG_TR:\n\t\t\tx += vid.width;\n\t\t\tbreak;\n\t\tcase SL_ORG_BL:\n\t\t\ty += vid.height;\n\t\t\tbreak;\n\t\tcase SL_ORG_BR:\n\t\t\tx += vid.width;\n\t\t\ty += vid.height;\n\t\t\tbreak;\n\t\tcase SL_ORG_MM:\n\t\t\tx += midx;\n\t\t\ty += midy;\n\t\t\tbreak;\n\t\tcase SL_ORG_TM:\n\t\t\tx += midx;\n\t\t\tbreak;\n\t\tcase SL_ORG_BM:\n\t\t\tx += midx;\n\t\t\ty += vid.height;\n\t\t\tbreak;\n\t\tcase SL_ORG_ML:\n\t\t\ty += midy;\n\t\t\tbreak;\n\t\tcase SL_ORG_MR:\n\t\t\tx += vid.height;\n\t\t\ty += midy;\n\t\t\tbreak;\n\n\t\tdefault:\n\t\t\tbreak;\n\t}\n\n\txx[0] = x;\n\tyy[0] = y;\n}\nvoid SCR_ShowPics_Draw(void)\n{\n\tdownloadlist_t *failed;\n\tfloat x, y, a = 1;\n\tshowpic_t *sp;\n\tmpic_t *p;\n\tfor (sp = showpics; sp; sp = sp->next)\n\t{\n\t\tx = sp->x;\n\t\ty = sp->y;\n\t\tSP_RecalcXY(&x, &y, sp->zone);\n\t\tif (!*sp->picname)\n\t\t\tcontinue;\n\n\t\tfor (failed = cl.faileddownloads; failed; failed = failed->next)\n\t\t{\t//don't try displaying ones that we know to have failed.\n\t\t\tif (!strcmp(failed->rname, sp->picname))\n\t\t\t\tbreak;\n\t\t}\n\t\tif (failed)\n\t\t\tcontinue;\n\n\t\tif (sp->fadedelay && showpics_touchtime+sp->fadedelay < realtime)\n\t\t{\n\t\t\ta = 1+(showpics_touchtime+sp->fadedelay-realtime);\n\t\t\tif (a > 1)\n\t\t\t\ta = 1;\t//shouldn't really happen, w/e.\n\t\t\telse if (a <= 0)\n\t\t\t\tcontinue;\n\t\t\tR2D_ImageColours(1,1,1,a);\n\t\t}\n\t\telse if (a != 1)\n\t\t\tR2D_ImageColours(1,1,1,a=1);\n\n\t\tp = R2D_SafeCachePic(sp->picname);\n\t\tif (!p || !R_GetShaderSizes(p, NULL, NULL, false))\n\t\t{\n\t\t\tif (sp->h > 8)\n\t\t\t\tDraw_FunStringWidth(x-2, y + (sp->h-8)/2, sp->name, sp->w+4, 2, false);\t//slightly wider, to try to somewhat deal with 16:10 resolutions...\n\t\t}\n\t\telse\n\t\t\tR2D_ScalePic(x, y, sp->w?sp->w:p->width, sp->h?sp->h:p->height, p);\n\t}\n\n\tif (a != 1)\n\t\tR2D_ImageColours(1,1,1,1);\n}\nconst char *SCR_ShowPics_ClickCommand(float cx, float cy, qboolean istouch)\n{\n\tdownloadlist_t *failed;\n\tfloat x, y, w, h;\n\tshowpic_t *sp;\n\tmpic_t *p;\n\tqboolean tryload = istouch && !showpics_touchtime;\n\tfloat bestdist = istouch?16:1;\n\tconst char *best = NULL;\n\tshowpics_touchtime = realtime;\n\tfor (sp = showpics; sp; sp = sp->next)\n\t{\n\t\tif (!sp->tcommand || !*sp->tcommand)\n\t\t\tcontinue;\n\n\t\ttryload = false;\n\n\t\tx = sp->x;\n\t\ty = sp->y;\n\t\tw = sp->w;\n\t\th = sp->h;\n\t\tSP_RecalcXY(&x, &y, sp->zone);\n\n\t\tif (!w || !h)\n\t\t{\n\t\t\tif (!*sp->picname)\n\t\t\t\tcontinue;\n\t\t\tfor (failed = cl.faileddownloads; failed; failed = failed->next)\n\t\t\t{\t//don't try displaying ones that we know to have failed.\n\t\t\t\tif (!strcmp(failed->rname, sp->picname))\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (failed)\n\t\t\t\tcontinue;\n\t\t\tp = R2D_SafeCachePic(sp->picname);\n\t\t\tif (!p)\n\t\t\t\tcontinue;\n\t\t\tw = w?w:sp->w;\n\t\t\th = h?h:sp->h;\n\t\t}\n\n\t\tx = bound(x, cx, x+w)-cx;\n\t\ty = bound(y, cy, y+h)-cy;\n\t\tx = max(fabs(x),fabs(y));\n\t\tif (bestdist > x)\n\t\t{\t//looks like this one is closer to the cursor\n\t\t\tbestdist = x;\n\t\t\tbest = sp->tcommand;\n\t\t\tif (bestdist <= 0)\t//use the first that's inside.\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\tif (tryload)\n\t\tCbuf_AddText(\"exec touch.cfg\\n\", RESTRICT_LOCAL);\n\treturn best;\n}\n\n//all=false clears only server pics, not ones from configs.\nvoid SCR_ShowPic_ClearAll(qboolean persistflag)\n{\n\tshowpic_t **link, *sp;\n\tint pnum;\n\n\tfor (pnum = 0; pnum < MAX_SPLITS; pnum++)\n\t{\n\t\twhile (SCR_CenterPrintPop(pnum))\n\t\t\t;\n\t}\n\n\tif (!persistflag)\n\t\tshowpics_touchtime = 0;\t//map change. gamedir may have changed too.\n\tfor (link = &showpics; (sp=*link); )\n\t{\n\t\tif (sp->persist != persistflag)\n\t\t{\n\t\t\tlink = &sp->next;\n\t\t\tcontinue;\n\t\t}\n\t\t\n\t\t*link = sp->next;\n\n\t\tZ_Free(sp->name);\n\t\tZ_Free(sp->picname);\n\t\tZ_Free(sp->tcommand);\n\t\tZ_Free(sp);\n\t}\n}\n\nshowpic_t *SCR_ShowPic_Find(char *name)\n{\n\tshowpic_t *sp, *last;\n\tfor (sp = showpics; sp; sp = sp->next)\n\t{\n\t\tif (!strcmp(sp->name, name))\n\t\t\treturn sp;\n\t}\n\n\tif (showpics)\n\t{\n\t\tfor (last = showpics; last->next; last = last->next)\n\t\t\t;\n\t}\n\telse\n\t\tlast = NULL;\n\tsp = Z_Malloc(sizeof(showpic_t));\n\tif (last)\n\t{\n\t\tlast->next = sp;\n\t\tsp->next = NULL;\n\t}\n\telse\n\t{\n\t\tsp->next = showpics;\n\t\tshowpics = sp;\n\t}\n\tsp->name = Z_Malloc(strlen(name)+1);\n\tstrcpy(sp->name, name);\n\tsp->picname = Z_Malloc(1);\n\tsp->x = 0;\n\tsp->y = 0;\n\tsp->zone = 0;\n\n\treturn sp;\n}\n\nvoid SCR_ShowPic_Create(void)\n{\n\tint zone = MSG_ReadByte();\n\tshowpic_t *sp;\n\tchar *s;\n\n\tsp = SCR_ShowPic_Find(MSG_ReadString());\n\n\ts = MSG_ReadString();\n\n\tZ_Free(sp->picname);\n\tsp->picname = Z_Malloc(strlen(s)+1);\n\tstrcpy(sp->picname, s);\n\tsp->zone = zone;\n\tsp->x = MSG_ReadShort();\n\tsp->y = MSG_ReadShort();\n\n\tCL_CheckOrEnqueDownloadFile(sp->picname, sp->picname, 0);\n}\n\nvoid SCR_ShowPic_Hide(void)\n{\n\tshowpic_t *sp, *prev;\n\n\tsp = SCR_ShowPic_Find(MSG_ReadString());\n\n\tif (sp == showpics)\n\t\tshowpics = sp->next;\n\telse\n\t{\n\t\tfor (prev = showpics; prev->next != sp; prev = prev->next)\n\t\t\t;\n\t\tprev->next = sp->next;\n\t}\n\n\tZ_Free(sp->name);\n\tZ_Free(sp->picname);\n\tZ_Free(sp);\n}\n\nvoid SCR_ShowPic_Move(void)\n{\n\tint zone = MSG_ReadByte();\n\tshowpic_t *sp;\n\n\tsp = SCR_ShowPic_Find(MSG_ReadString());\n\n\tsp->zone = zone;\n\tsp->x = MSG_ReadShort();\n\tsp->y = MSG_ReadShort();\n}\n\nvoid SCR_ShowPic_Update(void)\n{\n\tshowpic_t *sp;\n\tchar *s;\n\n\tsp = SCR_ShowPic_Find(MSG_ReadString());\n\n\ts = MSG_ReadString();\n\n\tZ_Free(sp->picname);\n\tsp->picname = Z_Malloc(strlen(s)+1);\n\tstrcpy(sp->picname, s);\n\n\tCL_CheckOrEnqueDownloadFile(sp->picname, sp->picname, 0);\n}\n\nstatic int SCR_ShowPic_MapZone(char *zone)\n{\n\t//sane coding scheme\n\tif (!Q_strcasecmp(zone, \"tr\"))\n\t\treturn SL_ORG_TR;\n\tif (!Q_strcasecmp(zone, \"tl\"))\n\t\treturn SL_ORG_TL;\n\tif (!Q_strcasecmp(zone, \"br\"))\n\t\treturn SL_ORG_BR;\n\tif (!Q_strcasecmp(zone, \"bl\"))\n\t\treturn SL_ORG_BL;\n\tif (!Q_strcasecmp(zone, \"mm\"))\n\t\treturn SL_ORG_MM;\n\tif (!Q_strcasecmp(zone, \"tm\"))\n\t\treturn SL_ORG_TM;\n\tif (!Q_strcasecmp(zone, \"bm\"))\n\t\treturn SL_ORG_BM;\n\tif (!Q_strcasecmp(zone, \"mr\"))\n\t\treturn SL_ORG_MR;\n\tif (!Q_strcasecmp(zone, \"ml\"))\n\t\treturn SL_ORG_MR;\n\n\t//compasy directions (but uses tei's coding scheme...)\n\tif (!Q_strcasecmp(zone, \"nw\"))\n\t\treturn SL_ORG_NW;\n\tif (!Q_strcasecmp(zone, \"ne\"))\n\t\treturn SL_ORG_NE;\n\tif (!Q_strcasecmp(zone, \"sw\"))\n\t\treturn SL_ORG_SW;\n\tif (!Q_strcasecmp(zone, \"sw\"))\n\t\treturn SL_ORG_SE;\n\tif (!Q_strcasecmp(zone, \"cc\"))\n\t\treturn SL_ORG_CC;\n\tif (!Q_strcasecmp(zone, \"cn\"))\n\t\treturn SL_ORG_CN;\n\tif (!Q_strcasecmp(zone, \"cs\"))\n\t\treturn SL_ORG_CS;\n\tif (!Q_strcasecmp(zone, \"cw\"))\n\t\treturn SL_ORG_CW;\n\tif (!Q_strcasecmp(zone, \"ce\"))\n\t\treturn SL_ORG_CE;\n\n\treturn atoi(zone);\n}\nvoid SCR_ShowPic_Script_f(void)\n{\n\tchar *imgname;\n\tchar *name;\n\tchar *tcommand;\n\tint x, y, w, h;\n\tint zone;\n\tshowpic_t *sp;\n\tfloat fadedelay;\n\n\timgname = Cmd_Argv(1);\n\tname = Cmd_Argv(2);\n\tx = atoi(Cmd_Argv(3));\n\ty = atoi(Cmd_Argv(4));\n\tzone = SCR_ShowPic_MapZone(Cmd_Argv(5));\n\n\tw = atoi(Cmd_Argv(6));\n\th = atoi(Cmd_Argv(7));\n\ttcommand = Cmd_Argv(8);\n\tfadedelay = atof(Cmd_Argv(9));\n\n\n\tsp = SCR_ShowPic_Find(name);\n\n\tZ_Free(sp->picname);\n\tZ_Free(sp->tcommand);\n\tsp->picname = Z_StrDup(imgname);\n\tsp->tcommand = Z_StrDup(tcommand);\n\n\tsp->zone = zone;\n\tsp->x = x;\n\tsp->y = y;\n\tsp->w = w;\n\tsp->h = h;\n\tsp->fadedelay = fadedelay;\n\n\tif (!sp->persist)\n\t\tsp->persist = !Cmd_FromGamecode();\n\n}\n\nvoid SCR_ShowPic_Remove_f(void)\n{\n\tSCR_ShowPic_ClearAll(!Cmd_FromGamecode());\n}\n\n//=============================================================================\n\nvoid QDECL SCR_Fov_Callback (struct cvar_s *var, char *oldvalue)\n{\n\tif (var->value < 10)\n\t\tCvar_ForceSet(var, \"10\");\n\t//highs are capped elsewhere. this allows fisheye to use this cvar automatically.\n}\n\nvoid QDECL SCR_Viewsize_Callback (struct cvar_s *var, char *oldvalue)\n{\n\tif (var->value < 30)\n\t{\n\t\tCvar_ForceSet (var, \"30\");\n\t\treturn;\n\t}\n\tif (var->value > 120)\n\t{\n\t\tCvar_ForceSet (var, \"120\");\n\t\treturn;\n\t}\n}\n\nvoid QDECL CL_Sbar_Callback(struct cvar_s *var, char *oldvalue)\n{\n}\n\nvoid SCR_CrosshairPosition(playerview_t *pview, float *x, float *y)\n{\n\textern cvar_t cl_crossx, cl_crossy, crosshaircorrect, v_viewheight;\n\n\tvrect_t rect;\n\trect = r_refdef.vrect;\n\n\tif (cl.worldmodel && crosshaircorrect.ival)\n\t{\n\t\tfloat adj;\n\t\ttrace_t tr;\n\t\tvec3_t end;\n\t\tvec3_t start;\n\t\tvec3_t right, up, fwds;\n\n\t\tAngleVectors(pview->simangles, fwds, right, up);\n\n\t\tVectorCopy(pview->simorg, start);\n\t\tstart[2]+=16;\n\t\tVectorMA(start, 100000, fwds, end);\n\n\t\tmemset(&tr, 0, sizeof(tr));\n\t\ttr.fraction = 1;\n\t\tcl.worldmodel->funcs.NativeTrace(cl.worldmodel, 0, NULLFRAMESTATE, NULL, start, end, vec3_origin, vec3_origin, false, MASK_WORLDSOLID, &tr);\n\t\tstart[2]-=16;\n\t\tif (tr.fraction != 1)\n\t\t{\n\t\t\tadj=pview->viewheight;\n\t\t\tif (v_viewheight.value < -7)\n\t\t\t\tadj+=-7;\n\t\t\telse if (v_viewheight.value > 4)\n\t\t\t\tadj+=4;\n\t\t\telse\n\t\t\t\tadj+=v_viewheight.value;\n\n\t\t\tstart[2]+=adj;\n\t\t\tMatrix4x4_CM_Project(tr.endpos, end, pview->simangles, start, r_refdef.fov_x, r_refdef.fov_y);\n\t\t\t*x = rect.x+rect.width*end[0] + cl_crossx.value;\n\t\t\t*y = rect.y+rect.height*(1-end[1]) + cl_crossy.value;\n\t\t\treturn;\n\t\t}\n\t}\n\n\t*x = rect.x + rect.width/2 + cl_crossx.value;\n\t*y = rect.y + rect.height/2 + cl_crossy.value;\n}\n\n/*\n=================\nSCR_SizeUp_f\n\nKeybinding command\n=================\n*/\nvoid SCR_SizeUp_f (void)\n{\n\tif (Cmd_FromGamecode())\n\t\tCvar_ForceSet(&scr_viewsize,va(\"%i\", scr_viewsize.ival+10));\n\telse\n\t\tCvar_SetValue (&scr_viewsize,scr_viewsize.value+10);\n}\n\n\n/*\n=================\nSCR_SizeDown_f\n\nKeybinding command\n=================\n*/\nvoid SCR_SizeDown_f (void)\n{\n\tif (Cmd_FromGamecode())\n\t\tCvar_ForceSet(&scr_viewsize,va(\"%i\", scr_viewsize.ival-10));\n\telse\n\t\tCvar_SetValue (&scr_viewsize,scr_viewsize.value-10);\n}\n\n//============================================================================\n\nvoid SCR_StringXY(const char *str, float x, float y)\n{\n\tconst char *s2;\n\tint px, py;\n\tunsigned int codepoint;\n\tint error;\n\t//pick the largest of the two. this is to avoid awkwardness with fonts that lack a version <12 pixels high.\n\tstruct font_s *font = (Font_CharVHeight(font_console)>Font_CharVHeight(font_default))?font_console:font_default;\n\n\tFont_BeginString(font, ((x<0)?vid.width:x), ((y<0)?vid.height - sb_lines:y), &px, &py);\n\n\tif (x < 0)\n\t{\n\t\tfor (s2 = str; *s2; )\n\t\t{\n\t\t\tcodepoint = unicode_decode(&error, s2, &s2, true);\n\t\t\tpx -= Font_CharWidth(CON_WHITEMASK, codepoint);\n\t\t}\n\t}\n\n\tif (y < 0)\n\t\tpy += y*Font_CharHeight();\n\n\twhile (*str)\n\t{\n\t\tcodepoint = unicode_decode(&error, str, &str, true);\n\t\tpx = Font_DrawChar(px, py, CON_WHITEMASK, codepoint);\n\t}\n\tFont_EndString(font);\n}\n\n/*\n==============\nSCR_DrawTurtle\n==============\n*/\nvoid SCR_DrawTurtle (void)\n{\n\tstatic int      count;\n\n\tif (!scr_showturtle.ival || !scr_turtle)\n\t\treturn;\n\n\tif (host_frametime <= 1.0/scr_turtlefps.value)\n\t{\n\t\tcount = 0;\n\t\treturn;\n\t}\n\n\tcount++;\n\tif (count < 3)\n\t\treturn;\n\n\tR2D_ScalePic (scr_vrect.x, scr_vrect.y, 64, 64, scr_turtle);\n}\n\nvoid SCR_DrawDisk (void)\n{\n\textern float fs_accessed_time;\n\tif ((float)realtime - fs_accessed_time > scr_diskicontimeout.value)\n\t\treturn;\n\tif (!draw_disc || !scr_showdisk.ival)\n\t\treturn;\n\n\tif (!R_GetShaderSizes(draw_disc, NULL, NULL, true))\n\t\tSCR_StringXY(\"FS\", scr_showdisk_x.value, scr_showdisk_y.value);\n\telse\n\t\tR2D_ScalePic (scr_showdisk_x.value + (scr_showdisk_x.value<0?vid.width:0), scr_showdisk_y.value + (scr_showdisk_y.value<0?vid.height:0), 24, 24, draw_disc);\n}\n\n/*\n==============\nSCR_DrawNet\n==============\n*/\nvoid SCR_DrawNet (void)\n{\n\tif (realtime - cls.netchan.last_received < scr_neticontimeout.value)\n\t\treturn;\n\tif (cls.demoplayback || !scr_net)\n\t\treturn;\n\n\tR2D_ScalePic (scr_vrect.x+64, scr_vrect.y, 64, 64, scr_net);\n}\n\nvoid R_GetGPUUtilisation(float *gpu, float *mem)\n{\n#ifdef __linux__\n\tstatic qboolean tried;\n\ttypedef void *nvmlDevice_t;\n\tstruct nvmlUtilization_s\n\t{\n\t\tunsigned int gpu;\n\t\tunsigned int mem;\n\t} util = {~0u,~0u};\n\tstatic int (*nvmlDeviceGetUtilizationRates) (nvmlDevice_t device, struct nvmlUtilization_s *utilization);\n\tstatic nvmlDevice_t dev;\n\tif (!tried)\n\t{\n\t\tint (*nvmlInit_v2) (void);\n\t\tint (*nvmlDeviceGetHandleByIndex_v2) (unsigned int index, nvmlDevice_t *device);\n\t\tdllhandle_t *nvml;\n\t\tdllfunction_t funcs[] =\n\t\t{\n\t\t\t{(void**)&nvmlInit_v2, \"nvmlInit_v2\"},\n\t\t\t{(void**)&nvmlDeviceGetHandleByIndex_v2, \"nvmlDeviceGetHandleByIndex_v2\"},\n\t\t\t{(void**)&nvmlDeviceGetUtilizationRates, \"nvmlDeviceGetUtilizationRates\"},\n\t\t\t{NULL}\n\t\t};\n\t\ttried = true;\n\n\t\tnvml = Sys_LoadLibrary(\"libnvidia-ml.so.1\", funcs);\n\t\tif (nvml)\n\t\t{\n\t\t\tif (!nvmlInit_v2())\n\t\t\t\tif (nvmlDeviceGetHandleByIndex_v2(0, &dev))\n\t\t\t\t\tdev = NULL;\n\t\t}\n\t}\n\n\tif (dev)\n\t\tnvmlDeviceGetUtilizationRates(dev, &util);\n\t*gpu = (util.gpu == ~0u)?-1:(util.gpu/100.0);\n\t*mem = (util.mem == ~0u)?-1:(util.mem/100.0);\n#else\n\t*gpu = *mem = -1;\n#endif\n}\n\nvoid SCR_DrawFPS (void)\n{\n\textern cvar_t show_fps;\n\tstatic double lastupdatetime;\n\tstatic double lastsystemtime;\n\tdouble t;\n\textern int fps_count;\n\tstatic float lastfps;\n\tchar str[80];\n\textern cvar_t cl_fakeframes;\n\n\tfloat frametime;\n\n\tstatic float gpu=-1, gpumem=-1;\n\n\tif (!show_fps.ival)\n\t\treturn;\n\n\tt = Sys_DoubleTime();\n\tif ((t - lastupdatetime) >= 1.0)\n\t{\n\t\tlastfps = fps_count/(t - lastupdatetime);\n\t\tfps_count = 0;\n\t\tlastupdatetime = t;\n\n\t\tif (developer.ival)\n\t\t\tR_GetGPUUtilisation(&gpu, &gpumem);\t//not all that accurate, but oh well.\n\t\telse\n\t\t\tgpu=gpumem=-1;\n\t}\n\tframetime = t - lastsystemtime;\n\tlastsystemtime = t;\n\n\tif (show_fps.value < 0)\n\t\tR_FrameTimeGraph(frametime, 0);\n\telse if (show_fps.value > 1)\n\t\tR_FrameTimeGraph(frametime, show_fps.value-1);\n\tif (cl_fakeframes.ival!=0)\n\t\tsprintf(str, \"%3.1f FPS(x%i)\", lastfps, max(0,cl_fakeframes.ival)+1);\n\telse\n\t\tsprintf(str, \"%3.1f FPS\", lastfps);\n\tSCR_StringXY(str, show_fps_x.value, show_fps_y.value);\n\n\tif (gpu>=0)\n\t{\n\t\tsprintf(str, \"%.0f%% GPU\", gpu*100);\n\t\tSCR_StringXY(str, show_fps_x.value, (show_fps_y.value>=0)?(show_fps_y.value+8):(show_fps_y.value-1));\n\t}\n/*\tif (gpumem>=0)\n\t{\n\t\tsprintf(str, \"%.0f%% VRAM Bus\", gpumem*100);\n\t\tSCR_StringXY(str, show_fps_x.value, (show_fps_y.value>=0)?(show_fps_y.value+16):(show_fps_y.value-2));\n\t}*/\n}\n\nvoid SCR_DrawClock(void)\n{\n\tstruct tm *newtime;\n\ttime_t long_time;\n\tchar str[16];\n\n\tif (!show_clock.ival)\n\t\treturn;\n\n\ttime( &long_time );\n\tnewtime = localtime( &long_time );\n\tif (show_clock.ival == 2)\n\t\tstrftime( str, sizeof(str)-1, \"%I:%M %p   \", newtime);\n\telse\n\t\tstrftime( str, sizeof(str)-1, \"%H:%M    \", newtime);\n\n\tSCR_StringXY(str, show_clock_x.value, show_clock_y.value);\n}\n\nvoid SCR_DrawGameClock(void)\n{\n#ifdef QUAKESTATS\n\tfloat showtime;\n\tint minutes;\n\tint seconds;\n\tchar str[16];\n\tint flags;\n\tfloat timelimit;\n\n\tif (!show_gameclock.ival)\n\t\treturn;\n\n\tflags = (show_gameclock.value-1);\n\tif (flags & 1)\n\t\ttimelimit = 60 * atof(InfoBuf_ValueForKey(&cl.serverinfo, \"timelimit\"));\n\telse\n\t\ttimelimit = 0;\n\n\tif (cl.matchstate == MATCH_STANDBY)\n\t\tshowtime = cl.servertime;\n\telse if (cl.playerview[0].statsf[STAT_MATCHSTARTTIME])\n\t\tshowtime = timelimit - (cl.servertime - cl.playerview[0].statsf[STAT_MATCHSTARTTIME]/1000);\n\telse \n\t\tshowtime = timelimit - (cl.servertime - cl.matchgametimestart);\n\n\tif (showtime < 0)\n\t\tshowtime *= -1;\n\n\tminutes = showtime/60;\n\tseconds = (int)showtime - (minutes*60);\n\tsprintf(str, \"%02i:%02i\", minutes, seconds);\n\n\tSCR_StringXY(str, show_gameclock_x.value, show_gameclock_y.value);\n#endif\n}\n\n/*\n==============\nDrawPause\n==============\n*/\nvoid SCR_DrawPause (void)\n{\n\tmpic_t  *pic;\n\n\tif (!scr_showpause.ival)               // turn off for screenshots\n\t\treturn;\n\n\tif (!cl.paused)\n\t\treturn;\n\n#ifndef CLIENTONLY\n\tif (sv.active && sv.paused == PAUSE_AUTO)\n\t\treturn;\n#endif\n\n\tif (Key_Dest_Has(kdm_menu))\n\t\treturn;\n\n\tpic = R2D_SafeCachePic (\"gfx/pause.lmp\");\n\tif (pic)\n\t{\n\t\tR2D_ScalePic ( (vid.width - pic->width)/2,\n\t\t\t(vid.height - 48 - pic->height)/2, pic->width, pic->height, pic);\n\t}\n\telse\n\t\tDraw_FunString((vid.width-strlen(\"Paused\")*8)/2, (vid.height-8)/2, \"Paused\");\n}\n\n\n\n/*\n==============\nSCR_DrawLoading\n==============\n*/\n\nint\t\t\ttotal_loading_size, current_loading_size, loading_stage;\nchar\t\t*loadingfile;\nint CL_DownloadRate(void);\n\nint SCR_GetLoadingStage(void)\n{\n\treturn loading_stage;\n}\nvoid SCR_SetLoadingStage(int stage)\n{\n\tswitch(stage)\n\t{\n\tcase LS_NONE:\n\t\tif (loadingfile)\n\t\t\tZ_Free(loadingfile);\n\t\tloadingfile = NULL;\n\t\tscr_disabled_for_loading = scr_drawloading = false;\n\t\tbreak;\n\tcase LS_CONNECTION:\n\t\tSCR_SetLoadingFile(\"waiting for connection...\");\n\t\tbreak;\n\tcase LS_SERVER:\n\t\tSCR_SetLoadingFile(\"starting server...\");\n\t\tbreak;\n\tcase LS_CLIENT:\n\t\tSCR_SetLoadingFile(\"receiving map info\");\n\t\tbreak;\n\t}\n\tloading_stage = stage;\n}\nvoid SCR_SetLoadingFile(char *str)\n{\n\tif (loadingfile && !strcmp(loadingfile, str))\n\t\treturn;\n\n\tif (loadingfile)\n\t\tZ_Free(loadingfile);\n\tloadingfile = Z_Malloc(strlen(str)+1);\n\tstrcpy(loadingfile, str);\n\n\tif (scr_loadingrefresh.ival)\n\t{\n\t\tqboolean oldwarndraw = r_refdef.warndraw;\n\t\tr_refdef.warndraw = false;\n\t\tSCR_UpdateScreen();\n\t\tr_refdef.warndraw = oldwarndraw;\n\t}\n}\n\nvoid SCR_DrawLoading (qboolean opaque)\n{\n\tint sizex, x, y, w, h;\n\tmpic_t  *pic;\n\tchar *s;\n\n\tif (CSQC_UseGamecodeLoadingScreen())\n\t\treturn;\t//will be drawn as part of the regular screen updates\n#ifdef MENU_DAT\n\tif (MP_UsingGamecodeLoadingScreen())\n\t{\n\t\tif (opaque)\n\t\t\tMP_Draw();\n\t\treturn;\t//menuqc should have just drawn whatever overlays it wanted.\n\t}\n#endif\n#ifdef MENU_NATIVECODE\n\tif (mn_entry && mn_entry->DrawLoading)\n\t{\n\t\tmn_entry->DrawLoading(host_frametime);\n\t\treturn;\n\t}\n#endif\n\n\t//int mtype = M_GameType(); //unused variable\n\ty = vid.height/2;\n\n\tif (R2D_DrawLevelshot())\n\t\t;\n\telse if (opaque)\n\t{\n\t\tR2D_ImageColours(0, 0, 0, 1);\n\t\tR2D_FillBlock(0, 0, vid.width, vid.height);\n\t\tR2D_ImageColours(1, 1, 1, 1);\n\t\tR2D_ConsoleBackground (0, vid.height, true);\n\t}\n\n\tif (scr_showloading.ival)\n\t{\n\t\tchar *qname = scr_loadingscreen_picture.string;\n\n#ifdef HEXEN2\n\t\tint qdepth = COM_FDepthFile(qname, true);\n\t\tint h2depth = COM_FDepthFile(\"gfx/menu/loading.lmp\", true);\n\n\t\tif (qdepth < h2depth && h2depth != FDEPTH_MISSING)\n\t\t{\t//hexen2 files.\n\t\t\t//hexen2 has some fancy sliders built into its graphics in specific places. so this is messy.\n\t\t\tpic = R2D_SafeCachePic (\"gfx/menu/loading.lmp\");\n\t\t\tif (R_GetShaderSizes(pic, &w, &h, true))\n\t\t\t{\n\t\t\t\tint\t\tsize, count, offset;\n\n\t\t\t\tif (!scr_drawloading && loading_stage == 0)\n\t\t\t\t\treturn;\n\n\t\t\t\toffset = (vid.width - w)/2;\n\t\t\t\tR2D_ScalePic (offset, 0, w, h, pic);\n\n\t\t\t\tif (loading_stage == LS_NONE)\n\t\t\t\t\treturn;\n\n\t\t\t\tif (total_loading_size)\n\t\t\t\t\tsize = current_loading_size * 106 / total_loading_size;\n\t\t\t\telse\n\t\t\t\t\tsize = 0;\n\n\t\t\t\tif (loading_stage == LS_CLIENT)\n\t\t\t\t\tcount = size;\n\t\t\t\telse\n\t\t\t\t\tcount = 106;\n\n\t\t\t\tR2D_ImagePaletteColour (136, 1.0);\n\t\t\t\tR2D_FillBlock (offset+42, 87, count, 1);\n\t\t\t\tR2D_FillBlock (offset+42, 87+5, count, 1);\n\t\t\t\tR2D_ImagePaletteColour (138, 1.0);\n\t\t\t\tR2D_FillBlock (offset+42, 87+1, count, 4);\n\n\t\t\t\tif (loading_stage == LS_SERVER)\n\t\t\t\t\tcount = size;\n\t\t\t\telse\n\t\t\t\t\tcount = 0;\n\n\t\t\t\tR2D_ImagePaletteColour(168, 1.0);\n\t\t\t\tR2D_FillBlock (offset+42, 97, count, 1);\n\t\t\t\tR2D_FillBlock (offset+42, 97+5, count, 1);\n\t\t\t\tR2D_ImagePaletteColour(170, 1.0);\n\t\t\t\tR2D_FillBlock (offset+42, 97+1, count, 4);\n\n\t\t\t\ty = 104;\n\t\t\t}\n\t\t}\n\t\telse\n#endif\n\t\t{\t//quake files\n\t\t\tpic = R2D_SafeCachePic (qname);\n\t\t\tif (R_GetShaderSizes(pic, &w, &h, true))\n\t\t\t{\n\t\t\t\tfloat f = 1;\n\t\t\t\tw *= scr_loadingscreen_scale.value;\n\t\t\t\th *= scr_loadingscreen_scale.value;\n\t\t\t\tswitch(scr_loadingscreen_scale_limit.ival)\n\t\t\t\t{\n\t\t\t\tdefault:\n\t\t\t\t\tbreak;\n\t\t\t\tcase 1:\n\t\t\t\t\tf = max(w/vid.width,h/vid.height);\n\t\t\t\t\tbreak;\n\t\t\t\tcase 2:\n\t\t\t\t\tf = min(w/vid.width,h/vid.height);\n\t\t\t\t\tbreak;\n\t\t\t\tcase 3:\n\t\t\t\t\tf = w/vid.width;\n\t\t\t\t\tbreak;\n\t\t\t\tcase 4:\n\t\t\t\t\tf = h/vid.height;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tif (f > 1)\n\t\t\t\t{\n\t\t\t\t\tw /= f;\n\t\t\t\t\th /= f;\n\t\t\t\t}\n\t\t\t\tx = ((int)vid.width - w)/2;\n\t\t\t\ty = ((int)vid.height - 48 - h)/2;\n\t\t\t\tR2D_ScalePic (x, y, w, h, pic);\n\t\t\t\tx = (vid.width/2) - 96;\n\t\t\t\ty += h + 8;\n\n\t\t\t\t//make sure our loading crap will be displayed.\n\t\t\t\tif (y > vid.height-32)\n\t\t\t\t\ty = vid.height-32;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tx = (vid.width/2) - 96;\n\t\t\t\ty = (vid.height/2) - 8;\n\n\t\t\t\tDraw_FunString((vid.width-7*8)/2, y-16, \"Loading\");\n\t\t\t}\n\n\t\t\tif (!total_loading_size)\n\t\t\t\ttotal_loading_size = 1;\n\t\t\tif (loading_stage > LS_CONNECTION)\n\t\t\t{\n\t\t\t\tsizex = current_loading_size * 192 / total_loading_size;\n\t\t\t\tif (loading_stage == LS_SERVER)\n\t\t\t\t{\n\t\t\t\t\tR2D_ImageColours(1.0, 0.0, 0.0, 1.0);\n\t\t\t\t\tR2D_FillBlock(x, y, sizex, 16);\n\t\t\t\t\tR2D_ImageColours(0.0, 0.0, 0.0, 1.0);\n\t\t\t\t\tR2D_FillBlock(x+sizex, y, 192-sizex, 16);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tR2D_ImageColours(1.0, 1.0, 0.0, 1.0);\n\t\t\t\t\tR2D_FillBlock(x, y, sizex, 16);\n\t\t\t\t\tR2D_ImageColours(1.0, 0.0, 0.0, 1.0);\n\t\t\t\t\tR2D_FillBlock(x+sizex, y, 192-sizex, 16);\n\t\t\t\t}\n\n\t\t\t\tR2D_ImageColours(1, 1, 1, 1);\n\t\t\t\tDraw_FunString(x+8, y+4, va(\"Loading %s... %i%%\",\n\t\t\t\t\t(loading_stage == LS_SERVER) ? \"server\" : \"client\",\n\t\t\t\t\tcurrent_loading_size * 100 / total_loading_size));\n\t\t\t}\n\t\t\ty += 16;\n\n\t\t\tif (loadingfile)\n\t\t\t{\n\t\t\t\tDraw_FunString(x+8, y+4, loadingfile);\n\t\t\t\ty+=16;\n\t\t\t}\n\t\t}\n\t}\n\tR2D_ImageColours(1, 1, 1, 1);\n\n\tif (cl.downloadlist || cls.download)\n\t{\n\t\tunsigned int fcount;\n\t\tqofs_t tsize;\n\t\tqboolean sizeextra;\n\n\t\tx = vid.width/2 - 160;\n\n\t\tCL_GetDownloadSizes(&fcount, &tsize, &sizeextra);\n\t\t//downloading files?\n\t\tif (cls.download)\n\t\t\tDraw_FunString(x+8, y+4, va(\"Downloading %s... %i%%\",\n\t\t\t\tcls.download->localname,\n\t\t\t\t(int)cls.download->percent));\n\n\t\tif (tsize > 1024*1024*16)\n\t\t{\n\t\t\tDraw_FunString(x+8, y+8+4, va(\"%5ukbps %8umb%s remaining (%i files)\",\n\t\t\t\t(unsigned int)(CL_DownloadRate()/1000.0f),\n\t\t\t\t(unsigned int)(tsize/(1024*1024)),\n\t\t\t\tsizeextra?\"+\":\"\",\n\t\t\t\tfcount));\n\t\t}\n\t\telse\n\t\t{\n\t\t\tDraw_FunString(x+8, y+8+4, va(\"%5ukbps %8ukb%s remaining (%i files)\",\n\t\t\t\t(unsigned int)(CL_DownloadRate()/1000.0f),\n\t\t\t\t(unsigned int)(tsize/1024),\n\t\t\t\tsizeextra?\"+\":\"\",\n\t\t\t\tfcount));\n\t\t}\n\n\t\ty+= 16+8;\n\t}\n\telse if ((s=CL_TryingToConnect()))\n\t{\n\t\tchar *nl;\n\t\tif (!strchr(s, '\\n'))\n\t\t\ts = va(\"^bConnecting to: %s...\", s);\n\n\t\tfor (;s; s = nl)\n\t\t{\n\t\t\tnl = strchr(s, '\\n');\n\t\t\tif (nl)\n\t\t\t\t*nl++ = 0;\n\t\t\tDraw_FunStringWidth(0, y+4, s, vid.width, 2, false);\n\t\t\ty += 8;\n\t\t}\n\t}\n}\n\nvoid SCR_BeginLoadingPlaque (void)\n{\n\tif (cls.state != ca_active && cls.protocol != CP_QUAKE3)\n\t\treturn;\n\n\tif (!scr_initialized)\n\t\treturn;\n\n//\tif (key_dest == key_console) //not really appropriate if client is to show it on a remote server.\n//\t\treturn;\n\n// redraw with no console and the loading plaque\n\tif (!scr_disabled_for_loading)\n\t{\n\t\tqboolean oldwarndraw = r_refdef.warndraw;\n\t\tSbar_Changed ();\n\t\tr_refdef.warndraw = false;\n\t\tscr_drawloading = true;\n\t\tSCR_UpdateScreen ();\n\t\tscr_drawloading = false;\n\t\tscr_disabled_for_loading = true;\n\t\tr_refdef.warndraw = oldwarndraw;\n\t}\n\n\tscr_disabled_time = Sys_DoubleTime();\t//realtime tends to change... Hmmm....\n}\n\nvoid SCR_EndLoadingPlaque (void)\n{\n//\tif (!scr_initialized)\n//\t\treturn;\n\n\tscr_disabled_for_loading = false;\n\t*levelshotname = '\\0';\n\tSCR_SetLoadingStage(0);\n\tscr_drawloading = false;\n}\n\nvoid SCR_ImageName (const char *mapname)\n{\n\t//assume levelshots/foo\n\tstrcpy(levelshotname, \"levelshots/\");\n\tCOM_FileBase(mapname, levelshotname + strlen(levelshotname), sizeof(levelshotname)-strlen(levelshotname));\n\n\tif (qrenderer && scr_loadingscreen_aspect.ival >= 0 && *mapname)\n\t{\n\t\tR_LoadHiResTexture(levelshotname, NULL, IF_NOWORKER|IF_UIPIC|IF_NOPICMIP|IF_NOMIPMAP|IF_CLAMP);\n\n\t\tif (!R_GetShaderSizes(R2D_SafeCachePic (levelshotname), NULL, NULL, true))\n\t\t{\n\t\t\t//try maps/foo\n\t\t\tstrcpy(levelshotname, \"maps/\");\n\t\t\tCOM_FileBase(mapname, levelshotname + strlen(levelshotname), sizeof(levelshotname)-strlen(levelshotname));\n\t\t\tif (!R_GetShaderSizes(R2D_SafeCachePic (levelshotname), NULL, NULL, true))\n\t\t\t{\n\t\t\t\t*levelshotname = '\\0';\n\t\t\t\tif (scr_disabled_for_loading)\n\t\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n\telse\n\t{\n\t\t*levelshotname = '\\0';\n\t\tif (scr_disabled_for_loading)\n\t\t\treturn;\n\t}\n\n\tscr_drawloading = true;\n\tif (qrenderer != QR_NONE)\n\t{\n\t\tqboolean oldwarndraw = r_refdef.warndraw;\n\t\tr_refdef.warndraw = false;\n\t\tSbar_Changed ();\n\t\tSCR_UpdateScreen ();\n\t\tr_refdef.warndraw = oldwarndraw;\n\t}\n\n\tscr_disabled_time = Sys_DoubleTime();\t//realtime tends to change... Hmmm....\n\tscr_disabled_for_loading = true;\n}\n\n\n//=============================================================================\n\n\n/*\n==================\nSCR_SetUpToDrawConsole\n==================\n*/\nvoid SCR_SetUpToDrawConsole (void)\n{\n\textern int startuppending;\t//true if we're downloading media or something and have not yet triggered the startup action (read: main menu or cinematic)\n\tfloat fullscreenpercent = 1;\n#ifdef ANDROID\n\t//android has an onscreen imm that we don't want to obscure\n\tfullscreenpercent = scr_consize.value;\n#endif\n\tif (!con_stayhidden.ival && (!Key_Dest_Has(~(kdm_console|kdm_game))) && (!cl.sendprespawn && cl.worldmodel && cl.worldmodel->loadstate != MLS_LOADED))\n\t{\n\t\t//force console to fullscreen if we're loading stuff (but don't necessarily force focus)\n//\t\tKey_Dest_Add(kdm_console);\n\t\tscr_con_target = scr_con_current = vid.height * fullscreenpercent;\n\t}\n\telse if (!startuppending && !Key_Dest_Has(kdm_menu) && (!Key_Dest_Has(~((!con_stayhidden.ival?kdm_console:0)|kdm_prompt|kdm_game))) && SCR_GetLoadingStage() == LS_NONE && cls.state < ca_active && !CSQC_UnconnectedOkay(false))\n\t{\n\t\t//go fullscreen if we're not doing anything\n\t\tif (con_curwindow && !cls.state && !scr_drawloading && !Key_Dest_Has(kdm_console))\n\t\t{\n\t\t\tKey_Dest_Add(kdm_cwindows);\n\t\t\tscr_con_target = 0; // not looking at an normal console\n\t\t}\n#ifdef VM_UI\n\t\telse if (q3 && q3->ui.OpenMenu())\n\t\t\tscr_con_current = scr_con_target = 0;\t//force instantly hidden.\n#endif\n\t\telse\n\t\t{\n\t\t\tqboolean legacyfullscreen = false;\n\t\t\tif (cls.state < ca_demostart)\n\t\t\t{\n\t\t\t\tif (con_stayhidden.ival)\n\t\t\t\t{\t//go to the menu instead of the console.\n\t\t\t\t\textern int startuppending;\n\t\t\t\t\tif (!scr_drawloading && SCR_GetLoadingStage() == LS_NONE)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (CL_TryingToConnect())\t//if we're trying to connect, make sure there's a loading/connecting screen showing instead of forcing the menu visible\n\t\t\t\t\t\t\tSCR_SetLoadingStage(LS_CONNECTION);\n\t\t\t\t\t\telse if (!Key_Dest_Has(kdm_menu) && !Key_Dest_Has(kdm_prompt) && !PM_IsApplying() && !startuppending)\t//don't force anything until the startup stuff has been done\n\t\t\t\t\t\t\tM_ToggleMenu_f();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\t//nothing happening, make sure the console is visible or something.\n\t\t\t\t\tif (!scr_drawloading)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (SCR_GetLoadingStage() == LS_NONE && CL_TryingToConnect())\t//if we're trying to connect, make sure there's a loading/connecting screen showing instead of forcing the menu visible\n\t\t\t\t\t\t\tSCR_SetLoadingStage(LS_CONNECTION);\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tKey_Dest_Add(kdm_console);\n\t\t\t\t\t}\n\t\t\t\t\tlegacyfullscreen = true;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (startuppending)\n\t\t\t\tscr_con_target = 0;\t//not made any decisions yet\n\t\t\telse if (Key_Dest_Has(kdm_console) || legacyfullscreen)\n\t\t\t\tscr_con_current = scr_con_target = vid.height * fullscreenpercent; // force instantly to fullscreen\n\t\t\telse\n\t\t\t\tscr_con_target = 0;\n\t\t}\n\t}\n\telse if (Key_Dest_Has(kdm_console))\n\t{\n\t\t//go half-screen if we're meant to have the console visible\n\t\tscr_con_target = vid.height*scr_consize.value;    // half screen\n\t\tif (scr_con_target < 32)\n\t\t\tscr_con_target = 32;\t//prevent total loss of console.\n\t\telse if (scr_con_target>vid.height)\n\t\t\tscr_con_target = vid.height;\n\t}\n\telse\n\t\tscr_con_target = 0;                               // scroll to nothing\n\n\tif (scr_con_target < scr_con_current)\n\t{\n\t\tscr_con_current -= scr_conspeed.value*host_frametime * (vid.height/320.0f);\n\t\tif (scr_con_target > scr_con_current)\n\t\t\tscr_con_current = scr_con_target;\n\n\t}\n\telse if (scr_con_target > scr_con_current)\n\t{\n\t\tscr_con_current += scr_conspeed.value*host_frametime * (vid.height/320.0f);\n\t\tif (scr_con_target < scr_con_current)\n\t\t\tscr_con_current = scr_con_target;\n\t}\n\n\tif (scr_con_current>vid.height)\n\t\tscr_con_current = vid.height;\n\n\tif (clearconsole++ < vid.numpages)\n\t{\n\t\tSbar_Changed ();\n\t}\n\telse if (clearnotify++ < vid.numpages)\n\t{\n\t}\n}\n\n/*\n==================\nSCR_DrawConsole\n==================\n*/\nvoid SCR_DrawConsole (qboolean noback)\n{\n\tif (!scr_con_current)\n\t{\n\t\tif (!Key_Dest_Has(kdm_console|kdm_menu))\n\t\t\tCon_DrawNotify ();      // only draw notify in game\n\t}\n\tif (vrui.enabled)\t//make the console all-or-nothing when its a vr ui.\n\t\tCon_DrawConsole (scr_con_target?vid.height:0, noback);\n\telse\n\t\tCon_DrawConsole (scr_con_current, noback);\n\tif (scr_con_current || Key_Dest_Has(kdm_cwindows))\n\t\tclearconsole = 0;\n}\n\n\n/*\n==============================================================================\n\n\t\t\t\t\t\tSCREEN SHOTS\n\n==============================================================================\n*/\n\n/*\n==================\nSCR_ScreenShot_f\n==================\n*/\nstatic void SCR_ScreenShot_f (void)\n{\n\tchar\t\t\tdisplayname[1024];\n\tchar            pcxname[MAX_QPATH];\n\tint                     i;\n\tvfsfile_t *vfs;\n\tvoid *rgbbuffer;\n\tint stride, width, height;\n\tenum uploadfmt fmt;\n\n\tif (!VID_GetRGBInfo)\n\t{\n\t\tCon_Printf(\"Screenshots are not supported with the current renderer\\n\");\n\t\treturn;\n\t}\n\n\tif (Cmd_Argc() == 2)\n\t{\n\t\tQ_strncpyz(pcxname, Cmd_Argv(1), sizeof(pcxname));\n\t\tif (strstr (pcxname, \"..\") || strchr(pcxname, ':') || *pcxname == '.' || *pcxname == '/')\n\t\t{\n\t\t\tCon_Printf(\"Screenshot name refused\\n\");\n\t\t\treturn;\n\t\t}\n\t\tCOM_DefaultExtension (pcxname, scr_sshot_type.string, sizeof(pcxname));\n\t}\n\telse\n\t{\n\t\tint stop = 1000;\n\t\tchar date[MAX_QPATH];\n\t\ttime_t tm = time(NULL);\n\t\tstrftime(date, sizeof(date), \"%Y%m%d%H%M%S\", localtime(&tm));\n\t//\n\t// find a file name to save it to\n\t//\n\t\tfor (i=0 ; i<stop ; i++)\n\t\t{\n\t\t\tQ_snprintfz(pcxname, sizeof(pcxname), \"%s%s-%i.%s\", scr_sshot_prefix.string, date, i, scr_sshot_type.string);\n\n\t\t\tif (!(vfs = FS_OpenVFS(pcxname, \"rb\", FS_GAMEONLY)))\n\t\t\t\tbreak;  // file doesn't exist\n\t\t\tVFS_CLOSE(vfs);\n\t\t}\n\t\tif (i==stop)\n\t\t{\n\t\t\tCon_Printf (\"SCR_ScreenShot_f: Couldn't create sequentially named file\\n\");\n\t\t\treturn;\n\t\t}\n\t}\n\n\tFS_DisplayPath(pcxname, FS_GAMEONLY, displayname, sizeof(displayname));\n\n\trgbbuffer = VID_GetRGBInfo(&stride, &width, &height, &fmt);\n\tif (rgbbuffer)\n\t{\n\t\t//regarding metadata - we don't really know what's on the screen, so don't write something that may be wrong (eg: if there's only a console, don't claim that its a 360 image)\n\t\tif (SCR_ScreenShot(pcxname, FS_GAMEONLY, &rgbbuffer, 1, stride, width, height, fmt, false))\n\t\t{\n\t\t\tCon_Printf (\"Wrote %s\\n\", displayname);\n\t\t\tBZ_Free(rgbbuffer);\n\t\t\treturn;\n\t\t}\n\t\tBZ_Free(rgbbuffer);\n\t\tCon_Printf (CON_ERROR \"Couldn't write %s\\n\", displayname);\n\t}\n\telse\n\t\tCon_Printf (CON_ERROR \"Couldn't get colour buffer for screenshot\\n\");\n}\n\nvoid *SCR_ScreenShot_Capture(int fbwidth, int fbheight, int *stride, enum uploadfmt *fmt, qboolean no2d, qboolean hdr)\n{\n\tint width, height;\n\tvoid *buf;\n\tqboolean okay = false;\n\tqboolean usefbo;\n\tqboolean oldwarndraw = r_refdef.warndraw;\n\n\tif (fbwidth == vid.fbpwidth && fbheight == vid.fbpheight && qrenderer != QR_VULKAN)\n\t\tusefbo = false;\n#ifdef GLQUAKE\n\telse if (qrenderer == QR_OPENGL && gl_config.ext_framebuffer_objects)\n\t\tusefbo = true;\n#endif\n\telse\n\t\treturn NULL;\n\n\tif (usefbo)\n\t{\n\t\tr_refdef.warndraw = true;\n\t\tQ_strncpyz(r_refdef.rt_destcolour[0].texname, \"megascreeny\", sizeof(r_refdef.rt_destcolour[0].texname));\n\t\t/*vid.framebuffer =*/R2D_RT_Configure(r_refdef.rt_destcolour[0].texname, fbwidth, fbheight, (hdr&&sh_config.texfmt[PTI_RGBA16F])?PTI_RGBA16F:PTI_RGBA8, RT_IMAGEFLAGS);\n\t\tBE_RenderToTextureUpdate2d(true);\n\t}\n\telse\n\t\tr_refdef.warndraw = false;\n\n\tR2D_FillBlock(0, 0, vid.fbvwidth, vid.fbvheight);\n\n#ifdef VM_CG\n\tif (!okay && q3 && q3->cg.Redraw(cl.time))\n\t\tokay = true;\n#endif\n#ifdef CSQC_DAT\n\tif (!okay && CSQC_DrawView())\n\t\tokay = true;\n\tif (usefbo)// && !*r_refdef.rt_destcolour[0].texname)\n\t{\t//csqc protects its own. lazily.\n\t\tQ_strncpyz(r_refdef.rt_destcolour[0].texname, \"megascreeny\", sizeof(r_refdef.rt_destcolour[0].texname));\n\t\tBE_RenderToTextureUpdate2d(true);\n\t}\n#endif\n\tif (!okay && r_worldentity.model)\n\t{\n\t\tV_RenderView (no2d);\n\t\tokay = true;\n\t}\n\tif (R2D_Flush)\n\t\tR2D_Flush();\n\n\t//fixme: add a way to get+save the depth values too\n\tif (!okay)\n\t{\n\t\tbuf = NULL;\n\t\t*stride = width = height = 0;\n\t\t*fmt = TF_INVALID;\n\t}\n\telse\n\t\tbuf = VID_GetRGBInfo(stride, &width, &height, fmt);\n\n\tif (usefbo)\n\t{\n\t\tR2D_RT_Configure(r_refdef.rt_destcolour[0].texname, 0, 0, 0, RT_IMAGEFLAGS);\n\t\tQ_strncpyz(r_refdef.rt_destcolour[0].texname, \"\", sizeof(r_refdef.rt_destcolour[0].texname));\n\t\tBE_RenderToTextureUpdate2d(true);\n\t}\n\n\tr_refdef.warndraw = oldwarndraw;\n\n\tif (!buf || width != fbwidth || height != fbheight)\n\t{\n\t\t*fmt = TF_INVALID;\n\t\tBZ_Free(buf);\n\t\treturn NULL;\n\t}\n\treturn buf;\n}\n\nstatic void SCR_ScreenShot_Mega_f(void)\n{\n\textern cvar_t r_projection;\n\tint stride[2];\n\tint width[2];\n\tint height[2];\n\tvoid *buffers[2];\n\tenum uploadfmt fmt[2];\n\tint numbuffers = 0;\n\tint buf;\n\tchar filename[MAX_QPATH];\n\tstereomethod_t osm = r_refdef.stereomethod;\n\tint projection = r_projection.ival;\n\n\t//massage the rendering code to redraw the screen with an fbo forced.\n\t//this allows us to generate screenshots which are not otherwise possible to actually draw.\n\t//this includes larger resolutions (although the lack of stiching leaves us subject to gpu limits of about 16k)\n\n\tchar *screenyname = Cmd_Argv(1);\n\tunsigned int fbwidth = strtoul(Cmd_Argv(2), NULL, 0);\n\tunsigned int fbheight = strtoul(Cmd_Argv(3), NULL, 0);\n\tchar ext[8];\n\n\tif (Cmd_IsInsecure())\n\t\treturn;\n\n\tif (qrenderer <= QR_HEADLESS)\n\t{\n\t\tCon_Printf(\"No renderer active\\n\");\n\t\treturn;\n\t}\n\n\tif (strcmp(Cmd_Argv(0), \"screenshot_mega\"))\n\t{\t//screenshot_360, screenshot_stereo etc probably don't want to capture mega screenshots\n\t\t//so default to something more realistic instead.\n\t\tif (!fbwidth)\n\t\t\tfbwidth = vid.pixelwidth;\n\t\tif (!fbheight)\n\t\t\tfbheight = vid.pixelheight;\n\t}\n\n\tif (!fbwidth)\n\t\tfbwidth = sh_config.texture2d_maxsize;\n\tfbwidth = bound(1, fbwidth, sh_config.texture2d_maxsize);\n\tif (!fbheight)\n\t\tfbheight = (fbwidth * 3)/4;\n\tfbheight = bound(1, fbheight, sh_config.texture2d_maxsize);\n\n\tif (strstr (screenyname, \"..\") || strchr(screenyname, ':') || *screenyname == '.' || *screenyname == '/')\n\t\tscreenyname = \"\";\n\tif (!*screenyname)\n\t{\n\t\tscreenyname = \"megascreeny\";\n\t\tQ_snprintfz(filename, sizeof(filename), \"%s%s\", scr_sshot_prefix.string, screenyname);\n\t}\n\telse\n\t{\n\t\tchar *mangle;\n\t\tQ_snprintfz(filename, sizeof(filename), \"%s\", scr_sshot_prefix.string);\n\t\tmangle = COM_SkipPath(filename);\n\t\tQ_snprintfz(mangle, sizeof(filename) - (mangle-filename), \"%s\", screenyname);\n\t}\n\n\tif (!strcmp(Cmd_Argv(0), \"screenshot_360\"))\n\t\tr_projection.ival = PROJ_EQUIRECTANGULAR;\n\n\tif (!strcmp(Cmd_Argv(0), \"screenshot_stereo\"))\n\t\tCOM_DefaultExtension (filename, \"png\", sizeof(filename));\t//png/pns is the only format that can really cope with this right now.\n\telse\n\t\tCOM_DefaultExtension (filename, scr_sshot_type.string, sizeof(filename));\n\n\tCOM_FileExtension(filename, ext, sizeof(ext));\n\tif (!strcmp(Cmd_Argv(0), \"screenshot_stereo\") || !strcmp(ext, \"pns\") || osm == STEREO_QUAD)\n\t\tnumbuffers = 2;\t//stereo png\n\telse\n\t\tnumbuffers = 1;\n\n\tif (numbuffers == 2 && Q_strcasecmp(ext, \"png\") && Q_strcasecmp(ext, \"pns\"))\n\t{\n\t\tCon_Printf(\"Only png format is supported for stereo screenshots\\n\");\n\t\treturn;\n\t}\n\n\tfor(buf = 0; buf < numbuffers; buf++)\n\t{\n\t\tif (numbuffers == 2)\n\t\t{\n\t\t\tif (buf)\n\t\t\t\tr_refdef.stereomethod = STEREO_RIGHTONLY;\n\t\t\telse\n\t\t\t\tr_refdef.stereomethod = STEREO_LEFTONLY;\n\t\t}\n\n\t\tbuffers[buf] = SCR_ScreenShot_Capture(fbwidth, fbheight, &stride[buf], &fmt[buf], false, false);\n\t\twidth[buf] = fbwidth;\n\t\theight[buf] = fbheight;\n\n\t\tif (width[buf] != width[0] || height[buf] != height[0] || fmt[buf] != fmt[0])\n\t\t{\t//invalid is better than unmatched.\n\t\t\tBZ_Free(buffers[buf]);\n\t\t\tbuffers[buf] = NULL;\n\t\t}\n\t}\n\n\tif (numbuffers == 2 && !buffers[1])\n\t\tnumbuffers = 1;\n\n\t//okay, we drew something, we're good to save a screeny.\n\tif (buffers[0])\n\t{\n\t\tif (SCR_ScreenShot(filename, FS_GAMEONLY, buffers, numbuffers, stride[0], width[0], height[0], fmt[0], true))\n\t\t{\n\t\t\tchar\t\t\tdisplayname[1024];\n\t\t\tFS_DisplayPath(filename, FS_GAMEONLY, displayname, sizeof(displayname));\n\t\t\tCon_Printf (\"Wrote %s\\n\", displayname);\n\t\t}\n\t}\n\telse\n\t\tCon_Printf(\"Unable to capture screenshot\\n\");\n\n\tfor(buf = 0; buf < numbuffers; buf++)\n\t\tBZ_Free(buffers[buf]);\n\n\tr_refdef.stereomethod = osm;\n\tr_projection.ival = projection;\n}\n\nstatic void SCR_ScreenShot_VR_f(void)\n{\n\tchar *screenyname = Cmd_Argv(1);\n\tconst char *ext;\n\tint width = atoi(Cmd_Argv(2));\n\tint stride=0;\n\t//we spin the camera around, taking slices from equirectangular screenshots\n\tchar filename[MAX_QPATH];\n\tint height;\t//equirectangular 360 * 180 gives a nice clean ratio\n\tint px = 4;\n\tint step = atof(Cmd_Argv(3));\n\tvoid *buffer[2], *buf;\n\tenum uploadfmt fmt;\n\tint lx, rx, x, y;\n\tvec3_t baseang;\n\tfloat ang;\n\tqboolean fail = false;\n\textern cvar_t r_projection, r_stereo_separation, r_stereo_convergence;\n\tqboolean stereo;\n\tVectorCopy(r_refdef.viewangles, baseang);\n\n\tif (width <= 2)\n\t\twidth = 2048;\n\tif (width > sh_config.texture2d_maxsize)\n\t\twidth = sh_config.texture2d_maxsize;\n\theight = width/2;\n\tif (step <= 0)\n\t\tstep = 5;\n\n\tbuffer[0] = BZF_Malloc (width*height*2*px);\n\tbuffer[1] = (qbyte*)buffer[0] + width*height*px;\n\n\tif (strstr (screenyname, \"..\") || strchr(screenyname, ':') || *screenyname == '.' || *screenyname == '/')\n\t\tscreenyname = \"\";\n\tif (!*screenyname)\n\t{\n\t\tscreenyname = \"vr\";\n\t\tQ_snprintfz(filename, sizeof(filename), \"%s%s\", scr_sshot_prefix.string, screenyname);\n\t}\n\telse\n\t{\n\t\tchar *mangle;\n\t\tQ_snprintfz(filename, sizeof(filename), \"%s\", scr_sshot_prefix.string);\n\t\tmangle = COM_SkipPath(filename);\n\t\tQ_snprintfz(mangle, sizeof(filename) - (mangle-filename), \"%s\", screenyname);\n\t}\n\tCOM_DefaultExtension (filename, scr_sshot_type.string, sizeof(filename));\n\n\text = COM_GetFileExtension(filename, NULL);\n\tstereo = !Q_strcasecmp(ext, \".pns\") || !Q_strcasecmp(ext, \".jns\");\n\n\n\n\tif (!buffer[0])\n\t{\n\t\tCon_Printf(\"out of memory\\n\");\n\t\treturn;\n\t}\n\n\tr_projection.ival = PROJ_EQUIRECTANGULAR;\n\tfor (lx = 0; lx < width; lx = rx)\n\t{\n\t\trx = lx + step;\n\t\tif (rx > width)\n\t\t\trx = width;\n\n\t\tr_refdef.stereomethod = STEREO_OFF;\n\n\t\tcl.playerview->simangles[0] = 0;\t//pitch is BAD\n\t\tcl.playerview->simangles[1] = baseang[1] + r_stereo_convergence.value*0.5;\n\t\tcl.playerview->simangles[2] = 0; //roll is BAD\n\t\tVectorCopy(cl.playerview->simangles, cl.playerview->viewangles);\n\n\t\t//FIXME: it should be possible to do this more inteligently, and get both strips with a single render.\n\t\t//FIXME: we should render to a PBO instead, so that the gpu+cpu don't need to sync until the very end.\n\t\t//FIXME: we should be using scissoring to avoid redrawing the entire screen (also tweak cull planes)\n\n\t\tang = M_PI*2*(baseang[1]/360.0 + (lx+0.5*(rx-lx))/width);\n\t\tr_refdef.eyeoffset[0] = sin(ang) * r_stereo_separation.value * 0.5;\n\t\tr_refdef.eyeoffset[1] = cos(ang) * r_stereo_separation.value * 0.5;\n\t\tr_refdef.eyeoffset[2] = 0;\n\t\tbuf = SCR_ScreenShot_Capture(width, height, &stride, &fmt, true, false);\n\t\tswitch(fmt)\n\t\t{\n\t\tcase TF_BGRA32:\n\t\t\tfor (y = 0; y < height; y++)\n\t\t\tfor (x = lx; x < rx; x++)\n\t\t\t\t((unsigned int*)buffer[0])[y*width + x] = ((unsigned int*)buf)[y*width + x];\n\t\t\tbreak;\n\t\tcase TF_RGB24:\n\t\t\tfor (y = 0; y < height; y++)\n\t\t\tfor (x = lx; x < rx; x++)\n\t\t\t{\n\t\t\t\t((qbyte*)buffer[0])[(y*width + x)*4+0] = ((qbyte*)buf)[(y*width + x)*3+2];\n\t\t\t\t((qbyte*)buffer[0])[(y*width + x)*4+1] = ((qbyte*)buf)[(y*width + x)*3+1];\n\t\t\t\t((qbyte*)buffer[0])[(y*width + x)*4+2] = ((qbyte*)buf)[(y*width + x)*3+0];\n\t\t\t\t((qbyte*)buffer[0])[(y*width + x)*4+3] = 255;\n\t\t\t}\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tfail = true;\n\t\t\tbreak;\n\t\t}\n\t\tBZ_Free(buf);\n\n\n\t\tcl.playerview->simangles[0] = 0;\t//pitch is BAD\n\t\tcl.playerview->simangles[1] = baseang[1] - r_stereo_convergence.value*0.5;\n\t\tcl.playerview->simangles[2] = 0; //roll is BAD\n\t\tVectorCopy(cl.playerview->simangles, cl.playerview->viewangles);\n\n\n\t\tr_refdef.eyeoffset[0] *= -1;\n\t\tr_refdef.eyeoffset[1] *= -1;\n\t\tr_refdef.eyeoffset[2] = 0;\n\t\tbuf = SCR_ScreenShot_Capture(width, height, &stride, &fmt, true, false);\n\t\tswitch(fmt)\n\t\t{\n\t\tcase TF_BGRA32:\n\t\t\tfor (y = 0; y < height; y++)\n\t\t\tfor (x = lx; x < rx; x++)\n\t\t\t\t((unsigned int*)buffer)[y*width + x] = ((unsigned int*)buf)[y*width + x];\n\t\t\tbreak;\n\t\tcase TF_RGB24:\n\t\t\tfor (y = 0; y < height; y++)\n\t\t\tfor (x = lx; x < rx; x++)\n\t\t\t{\n\t\t\t\t((qbyte*)buffer[1])[(y*width + x)*4+0] = ((qbyte*)buf)[(y*width + x)*3+2];\n\t\t\t\t((qbyte*)buffer[1])[(y*width + x)*4+1] = ((qbyte*)buf)[(y*width + x)*3+1];\n\t\t\t\t((qbyte*)buffer[1])[(y*width + x)*4+2] = ((qbyte*)buf)[(y*width + x)*3+0];\n\t\t\t\t((qbyte*)buffer[1])[(y*width + x)*4+3] = 255;\n\t\t\t}\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tfail = true;\n\t\t\tbreak;\n\t\t}\n\t\tBZ_Free(buf);\n\t}\n\n\tif (fail)\n\t\tCon_Printf (\"Unable to capture suitable screen image\\n\");\n\telse if (SCR_ScreenShot(filename, FS_GAMEONLY, buffer, (stereo?2:1), stride, width, height*(stereo?1:2), TF_BGRX32, true))\n\t{\n\t\tchar\t\t\tdisplayname[1024];\n\t\tFS_DisplayPath(filename, FS_GAMEONLY, displayname, sizeof(displayname));\n\t\tCon_Printf (\"Wrote %s\\n\", displayname);\n\t}\n\n\tBZ_Free(buffer[0]);\n\n\tr_projection.ival = r_projection.value;\n\tVectorCopy(baseang, r_refdef.viewangles);\n\tVectorClear(r_refdef.eyeoffset);\n}\n\nstatic void SCR_ScreenShot_Cubemap_Internal(vec3_t origin, int size, const char *fname, const char *fnamefmt, qboolean envmap)\n{\n\tvoid *buffer;\n\tint stride, fbwidth, fbheight;\n\tuploadfmt_t fmt;\n\tchar filename[MAX_QPATH];\n\tchar displayname[1024];\n\tint i, firstside;\n\tchar olddrawviewmodel[64];\t//hack, so we can set r_drawviewmodel to 0 so that it doesn't appear in screenshots even if the csqc is generating new data.\n\tvec3_t oldangles;\n\tvoid *facedata;\n\tstruct pendingtextureinfo mips;\n\tstatic const struct\n\t{\n\t\tvec3_t angle;\n\t\tconst char *postfix;\n\t\tqboolean verticalflip;\n\t\tqboolean horizontalflip;\n\t} sides[] =\n\t{\n\t\t//standard cubemap (flipping is done on save)\n\t\t{{0, 0, 90}, \"_px\", true},\n\t\t{{0, 180, -90}, \"_nx\", true},\n\t\t{{0, 90, 0}, \"_py\", true},\t//upside down\n\t\t{{0, 270, 0}, \"_ny\", false, true},\n\t\t{{-90, 0, 90}, \"_pz\", true},\n\t\t{{90, 0, 90}, \"_nz\", true},\n\n\t\t//annoying envmap (requires processing to flip/etc the images before they can be loaded into a texture)\n\t\t{{0, 270, 0}, \"_ft\"},\n\t\t{{0, 90, 0}, \"_bk\"},\n\t\t{{0, 0, 0}, \"_rt\"},\n\t\t{{0, 180, 0}, \"_lf\"},\n\t\t{{90, 0, 0}, \"_dn\"},\n\t\t{{-90, 0, 0}, \"_up\"}\n\t};\n\tconst char *ext;\n\tunsigned int bb, bw, bh, bd;\n\trefdef_t oldrefdef;\n\n\tif (!cls.state || !cl.worldmodel || cl.worldmodel->loadstate != MLS_LOADED)\n\t{\n\t\tCon_Printf(\"Please start a map first\\n\");\n\t\treturn;\n\t}\n\n\tfirstside = envmap?6:0;\n\n\toldrefdef = r_refdef;\n\n\tr_refdef.stereomethod = STEREO_OFF;\n\tQ_strncpyz(olddrawviewmodel, r_drawviewmodel.string, sizeof(olddrawviewmodel));\n\tCvar_Set(&r_drawviewmodel, \"0\");\n\n\tif (origin)\n\t{\n\t\tr_refdef.externalview = true;\n\t\tr_refdef.forcevis = false;\n\t\tr_refdef.flags |= RDF_DISABLEPARTICLES;\n\t\tr_refdef.forcedvis = NULL;\n\t\tr_refdef.areabitsknown = false;\n\t\tr_refdef.sceneareas = NULL;\n\t\tr_refdef.fixedview = true;\n\t\tVectorCopy(origin, r_refdef.fixedvieworg);\n\t\t// r_refdef.fixedviewangles is set below for each screenshot\n\t}\n\telse\n\t\torigin = r_refdef.vieworg;\n\n\tVectorCopy(cl.playerview->viewangles, oldangles);\n\n\tfbheight = size&~1;\n\tif (fbheight < 1)\n\t\tfbheight = 512;\n\tfbwidth = fbheight;\n\n\text = COM_GetFileExtension(fname, NULL);\n\tif (!*fname || ext == fname)\n\t{\t//generate a default filename if none exists yet.\n\t\tchar base[MAX_QPATH];\n\t\tCOM_FileBase(cl.worldmodel->name, base, sizeof(base));\n\t\tfname = va(fnamefmt, base, (int)origin[0], (int)origin[1], (int)origin[2]);\n\t}\n\tif (!strcmp(ext, \".ktx\") || !strcmp(ext, \".dds\"))\n\t{\n\t\tqboolean fail = false;\n\t\tmips.type = PTI_CUBE;\n\t\tmips.encoding = PTI_INVALID;\n\t\tmips.extrafree = NULL;\n\t\tmips.mipcount = 1;\n\n\t\tbb=0;\n\t\tfor (i = 0; i < 6; i++)\n\t\t{\n\t\t\tif (r_refdef.fixedview)\n\t\t\t\tVectorCopy(sides[i].angle, r_refdef.fixedviewangles);\n\t\t\telse\n\t\t\t{\n\t\t\t\tVectorCopy(sides[i].angle, cl.playerview->aimangles);\n\t\t\t\tVectorCopy(cl.playerview->aimangles, cl.playerview->viewangles);\n\t\t\t}\n\n\t\t\t//don't use hdr when saving dds files. it generally means dx10 dds files and most tools suck too much and then I get blamed for writing 'corrupt' dds files.\n\t\t\tfacedata = SCR_ScreenShot_Capture(fbwidth, fbheight, &stride, &fmt, true, !!strcmp(ext, \".dds\"));\n\t\t\tif (!facedata)\n\t\t\t\tbreak;\n\t\t\tif (!bb)\n\t\t\t{\n\t\t\t\tImage_BlockSizeForEncoding(fmt, &bb, &bw, &bh, &bd);\n\t\t\t\tif (!bb || bw != 1 || bh != 1 || bd != 1 || fbwidth != fbheight)\n\t\t\t\t{\t//erk, no block compression here...\n\t\t\t\t\tBZ_Free(facedata);\n\t\t\t\t\tbreak;\t//zomgwtfbbq\n\t\t\t\t}\n\t\t\t\tmips.mip[0].datasize = bb*((fbwidth+bw-1)/bw)*((fbheight+bh-1)/bh);\n\t\t\t\tmips.mip[0].width = fbwidth;\n\t\t\t\tmips.mip[0].height = fbheight;\n\t\t\t\tmips.mip[0].depth = 6;\n\t\t\t\tmips.mip[0].datasize *= mips.mip[0].depth;\n\t\t\t\tmips.mip[0].data = BZ_Malloc(mips.mip[0].datasize);\n\t\t\t\tmips.mip[0].needfree = true;\n\n\t\t\t\tmips.encoding = fmt;\n\t\t\t}\n\t\t\telse if (fmt != mips.encoding || fbwidth != mips.mip[0].width || fbheight != mips.mip[0].height)\n\t\t\t{\n\t\t\t\tBZ_Free(facedata);\n\t\t\t\tbreak;\t//zomgwtfbbq\n\t\t\t}\n\n\t\t\tImage_FlipImage(facedata, (qbyte*)mips.mip[0].data + i*(mips.mip[0].datasize/6), &fbwidth, &fbheight, bb, sides[i].horizontalflip, sides[i].verticalflip^(stride<0), false);\n\t\t\tBZ_Free(facedata);\n\t\t}\n\t\tif (i == 6)\n\t\t{\n\t\t\tif (mips.encoding == PTI_RGB32F || mips.encoding == PTI_RGBA16F || mips.encoding == PTI_RGBA32F)\n\t\t\t{\t//convert to a more memory-efficient hdr format.\n\t\t\t\tqboolean pixelformats[PTI_MAX] = {0};\n\t\t\t\tpixelformats[PTI_E5BGR9] = true;\n\t\t\t\tImage_ChangeFormat(&mips, pixelformats, mips.encoding, fname);\n\t\t\t}\n\t\t\telse if ((mips.encoding == PTI_RGBX8 || mips.encoding == PTI_BGRX8) && !strcmp(ext, \".dds\"))\n\t\t\t{\t//gimp-dds plugin is buggy. convert to a less problematic format, so that I don't get invalid complaints.\n\t\t\t\tqboolean pixelformats[PTI_MAX] = {0};\n\t\t\t\tpixelformats[PTI_BGR8] = true;\n\t\t\t\tImage_ChangeFormat(&mips, pixelformats, mips.encoding, fname);\n\t\t\t}\n\n\t\t\tQ_snprintfz(filename, sizeof(filename), \"textures/%s\", fname);\n\t\t\tCOM_DefaultExtension (filename, ext, sizeof(filename));\n\n\t\t\text = COM_GetFileExtension(filename, NULL);\n\t\t\tif (fail)\n\t\t\t\tCon_Printf(\"Unable to generate cubemap data\\n\");\n#ifdef IMAGEFMT_DDS\n\t\t\telse if (!strcmp(ext, \".dds\"))\n\t\t\t{\n\t\t\t\tif (Image_WriteDDSFile(filename, FS_GAMEONLY, &mips))\n\t\t\t\t{\n\t\t\t\t\tFS_DisplayPath(filename, FS_GAMEONLY, displayname, sizeof(displayname));\n\t\t\t\t\tCon_Printf (\"Wrote %s\\n\", displayname);\n\t\t\t\t}\n\t\t\t}\n#endif\n#ifdef IMAGEFMT_KTX\n\t\t\telse if (!strcmp(ext, \".ktx\"))\n\t\t\t{\n\t\t\t\tif (Image_WriteKTXFile(filename, FS_GAMEONLY, &mips))\n\t\t\t\t{\n\t\t\t\t\tFS_DisplayPath(filename, FS_GAMEONLY, displayname, sizeof(displayname));\n\t\t\t\t\tCon_Printf (\"Wrote %s\\n\", displayname);\n\t\t\t\t}\n\t\t\t}\n#endif\n\t\t\telse\n\t\t\t\tCon_Printf (\"Unknown format %s\\n\", filename);\n\t\t}\n\t\tif (mips.mip[0].needfree)\n\t\t\tBZ_Free(mips.mip[0].data);\n\t}\n\telse\n\t{\n\t\tfor (i = firstside; i < firstside+6; i++)\n\t\t{\n\t\t\tif (r_refdef.fixedview)\n\t\t\t\tVectorCopy(sides[i].angle, r_refdef.fixedviewangles);\n\t\t\telse\n\t\t\t{\n\t\t\t\tVectorCopy(sides[i].angle, cl.playerview->aimangles);\n\t\t\t\tVectorCopy(cl.playerview->aimangles, cl.playerview->viewangles);\n\t\t\t}\n\n\t\t\tbuffer = SCR_ScreenShot_Capture(fbwidth, fbheight, &stride, &fmt, true, false);\n\t\t\tif (buffer)\n\t\t\t{\n\t\t\t\tImage_BlockSizeForEncoding(fmt, &bb, &bw, &bh, &bd);\n\t\t\t\tif (sides[i].horizontalflip)\n\t\t\t\t{\n\t\t\t\t\tint y, x, p;\n\t\t\t\t\tchar *bad = buffer;\n\t\t\t\t\tchar *in = buffer, *out;\n\t\t\t\t\tbuffer = out = BZ_Malloc(fbwidth*fbheight*bb);\n\t\t\t\t\tfor (y = 0; y < fbheight; y++, in += abs(stride), out += fbwidth*bb)\n\t\t\t\t\t{\n\t\t\t\t\t\tfor (x = 0; x < fbwidth*bb; x+=bb)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tfor (p = 0; p < bb; p++)\n\t\t\t\t\t\t\t\tout[x+p] = in[(fbwidth-1)*bb-x+p];\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tBZ_Free(bad);\n\t\t\t\t\tif (stride < 0)\n\t\t\t\t\t\tstride = -fbwidth*bb;\n\t\t\t\t\telse\n\t\t\t\t\t\tstride = fbwidth*bb;\n\t\t\t\t}\n\t\t\t\tif (sides[i].verticalflip)\n\t\t\t\t\tstride = -stride;\n\n\t\t\t\tQ_snprintfz(filename, sizeof(filename), \"textures/%s%s\", fname, sides[i].postfix);\n\t\t\t\tCOM_DefaultExtension (filename, scr_sshot_type.string, sizeof(filename));\n\n\t\t\t\tif (SCR_ScreenShot(filename, FS_GAMEONLY, &buffer, 1, stride, fbwidth, fbheight, fmt, false))\n\t\t\t\t{\n\t\t\t\t\tFS_DisplayPath(filename, FS_GAMEONLY, displayname, sizeof(displayname));\n\t\t\t\t\tCon_Printf (\"Wrote %s\\n\", displayname);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tFS_DisplayPath(filename, FS_GAMEONLY, displayname, sizeof(displayname));\n\t\t\t\t\tCon_Printf (\"Failed to write %s\\n\", displayname);\n\t\t\t\t}\n\t\t\t\tBZ_Free(buffer);\n\t\t\t}\n\t\t}\n\t}\n\n\tr_refdef = oldrefdef;\n\n\tCvar_Set(&r_drawviewmodel, olddrawviewmodel);\n\n\tVectorCopy(oldangles, cl.playerview->viewangles);\n}\n\nvoid SCR_ScreenShot_Cubemap(vec3_t origin, int size)\n{\n\tSCR_ScreenShot_Cubemap_Internal(origin, size, \"\", \"env/%s_%i_%i_%i\", false);\n}\n\nstatic void SCR_ScreenShot_Cubemap_f(void)\n{\n\tSCR_ScreenShot_Cubemap_Internal(NULL, atoi(Cmd_Argv(2)), Cmd_Argv(1), \"%s/%i_%i_%i\", false);\n}\n\nstatic void SCR_ScreenShot_Envmap_f(void)\n{\n\tSCR_ScreenShot_Cubemap_Internal(NULL, atoi(Cmd_Argv(2)), Cmd_Argv(1), \"%s/%i_%i_%i\", true);\n}\n\n#ifdef IMAGEFMT_PCX\n// from gl_draw.c\nqbyte\t\t*draw_chars;\t\t\t\t// 8*8 graphic characters\n\nstatic void SCR_DrawCharToSnap (int num, qbyte *dest, int width)\n{\n\tint\t\trow, col;\n\tqbyte\t*source;\n\tint\t\tdrawline;\n\tint\t\tx;\n\n\tif (!draw_chars)\n\t{\n\t\tsize_t lumpsize;\n\t\tqbyte lumptype;\n\t\tdraw_chars = W_GetLumpName(\"conchars\", &lumpsize, &lumptype);\n//\t\tif (lumptype != )\n//\t\t\tdraw_chars = NULL;\n\t\tif (!draw_chars || lumpsize != 128*128)\n\t\t\treturn;\n\t}\n\n\trow = num>>4;\n\tcol = num&15;\n\tsource = draw_chars + (row<<10) + (col<<3);\n\n\tdrawline = 8;\n\n\twhile (drawline--)\n\t{\n\t\tfor (x=0 ; x<8 ; x++)\n\t\t\tif (source[x] && source[x]!=255)\n\t\t\t\tdest[x] = source[x];\n\t\tsource += 128;\n\t\tdest -= width;\n\t}\n\n}\n\nstatic void SCR_DrawStringToSnap (const char *s, qbyte *buf, int x, int y, int width)\n{\n\tqbyte *dest;\n\tconst unsigned char *p;\n\n\tdest = buf + ((y * width) + x);\n\n\tp = (const unsigned char *)s;\n\twhile (*p) {\n\t\tSCR_DrawCharToSnap(*p++, dest, width);\n\t\tdest += 8;\n\t}\n}\n\n\n/*\n==================\nSCR_RSShot\n==================\n*/\nint MipColor(int r, int g, int b);\nqboolean SCR_RSShot (void)\n{\n\tint stride;\n\tint truewidth;\n\tint trueheight;\n\n\tint     x, y;\n\tunsigned char\t\t*src, *dest;\n\tunsigned char\t\t*newbuf;\n\tint w, h;\n\tint dx, dy, dex, dey, nx;\n\tint r, b, g;\n\tint count;\n\tfloat fracw, frach;\n\tchar st[80];\n\ttime_t now;\n\tenum uploadfmt fmt;\n\tint src_size;\n\tint src_red;\n\tint src_green;\n\tint src_blue;\n\n\tif (!scr_allowsnap.ival)\n\t\treturn false;\n\n\tif (CL_IsUploading())\n\t\treturn false; // already one pending\n\n\tif (cls.state < ca_onserver)\n\t\treturn false; // gotta be connected\n\n\tif (!VID_GetRGBInfo || !scr_initialized)\n\t{\n\t\treturn false;\n\t}\n\n\tCon_Printf(\"Remote screen shot requested.\\n\");\n\n//\n// save the pcx file\n//\n\tnewbuf = VID_GetRGBInfo(&truewidth, &trueheight, &stride, &fmt);\n\n\tif (fmt == TF_INVALID)\n\t\treturn false;\n\tswitch(fmt)\n\t{\n\tcase TF_RGB24:\n\tcase TF_RGBA32:\n\t\tsrc_size = (fmt==TF_RGB24)?3:4;\n\t\tsrc_red = 0;\n\t\tsrc_green = 1;\n\t\tsrc_blue = 2;\n\t\tbreak;\n\tcase TF_BGR24:\n\tcase TF_BGRA32:\n\t\tsrc_size =  (fmt==TF_BGR24)?3:4;\n\t\tsrc_red = 2;\n\t\tsrc_green = 1;\n\t\tsrc_blue = 0;\n\t\tbreak;\n\tdefault:\n\t\tBZ_Free(newbuf);\n\t\treturn false;\n\t}\n\t//FIXME: bgra\n\n\tw = RSSHOT_WIDTH;\n\th = RSSHOT_HEIGHT;\n\n\tfracw = (float)truewidth / (float)w;\n\tfrach = (float)trueheight / (float)h;\n\n\t//scale down first.\n\tfor (y = 0; y < h; y++) {\n\t\tdest = newbuf + (w*src_size * y);\n\n\t\tfor (x = 0; x < w; x++) {\n\t\t\tr = g = b = 0;\n\n\t\t\tdx = x * fracw;\n\t\t\tdex = (x + 1) * fracw;\n\t\t\tif (dex == dx) dex++; // at least one\n\t\t\tdy = y * frach;\n\t\t\tdey = (y + 1) * frach;\n\t\t\tif (dey == dy) dey++; // at least one\n\n\t\t\tcount = 0;\n\t\t\tfor (/* */; dy < dey; dy++) {\n\t\t\t\tsrc = newbuf + (truewidth * src_size * dy) + dx * src_size;\n\t\t\t\tfor (nx = dx; nx < dex; nx++) {\n\t\t\t\t\tr += src[src_red];\n\t\t\t\t\tg += src[src_green];\n\t\t\t\t\tb += src[src_blue];\n\t\t\t\t\tsrc += src_size;\n\t\t\t\t\tcount++;\n\t\t\t\t}\n\t\t\t}\n\t\t\tr /= count;\n\t\t\tg /= count;\n\t\t\tb /= count;\n\t\t\t*dest++ = r;\n\t\t\t*dest++ = g;\n\t\t\t*dest++ = b;\n\t\t}\n\t}\n\n\t// convert to eight bit\n\tfor (y = 0; y < h; y++) {\n\t\tsrc = newbuf + (w * src_size * y);\n\t\tdest = newbuf + (w * y);\n\n\t\tfor (x = 0; x < w; x++) {\n\t\t\t*dest++ = MipColor(src[0], src[1], src[2]);\n\t\t\tsrc += src_size;\n\t\t}\n\t}\n\n\ttime(&now);\n\tstrcpy(st, ctime(&now));\n\tst[strlen(st) - 1] = 0;\n\tSCR_DrawStringToSnap (st, newbuf, w - strlen(st)*8, h - 1, w);\n\n\tQ_strncpyz(st, cls.servername, sizeof(st));\n\tSCR_DrawStringToSnap (st, newbuf, w - strlen(st)*8, h - 11, w);\n\n\tQ_strncpyz(st, name.string, sizeof(st));\n\tSCR_DrawStringToSnap (st, newbuf, w - strlen(st)*8, h - 21, w);\n\n\tWritePCXfile (\"snap.pcx\", FS_GAMEONLY, newbuf, w, h, w, host_basepal, true);\n\n\tBZ_Free(newbuf);\n\n\treturn true;\n}\n#endif\n\n//=============================================================================\n\n\n//=============================================================================\n\n/*\n===============\nSCR_BringDownConsole\n\nBrings the console down and fades the palettes back to normal\n================\n*/\nvoid SCR_BringDownConsole (void)\n{\n\tint pnum;\n\n\tfor (pnum = 0; pnum < cl.splitclients; pnum++)\n\t{\n\t\twhile (SCR_CenterPrintPop(pnum))\n\t\t\t;\n\t\tcl.playerview[pnum].cshifts[CSHIFT_CONTENTS].percent = 0;              // no area contents palette on next frame\n\t}\n}\n\nvoid SCR_TileClear (int skipbottom)\n{\n\tif (r_refdef.vrect.width < r_refdef.grect.width)\n\t{\n\t\tfloat w;\n\t\t// left\n\t\tR2D_TileClear (r_refdef.grect.x, r_refdef.grect.y, r_refdef.vrect.x-r_refdef.grect.x, r_refdef.grect.height - skipbottom);\n\t\t// right\n\t\tw = (r_refdef.grect.x+r_refdef.grect.width) - (r_refdef.vrect.x+r_refdef.vrect.width);\n\t\tR2D_TileClear ((r_refdef.grect.x+r_refdef.grect.width) - (w), r_refdef.grect.y, w, r_refdef.grect.height - skipbottom);\n\t}\n\tif (r_refdef.vrect.height < r_refdef.grect.height)\n\t{\n\t\t// top\n\t\tR2D_TileClear (r_refdef.vrect.x, r_refdef.grect.y,\n\t\t\tr_refdef.vrect.width,\n\t\t\tr_refdef.vrect.y - r_refdef.grect.y);\n\t\t// bottom\n\t\tR2D_TileClear (r_refdef.vrect.x,\n\t\t\tr_refdef.vrect.y + r_refdef.vrect.height,\n\t\t\tr_refdef.vrect.width,\n\t\t\t(r_refdef.grect.y+r_refdef.grect.height) - skipbottom - (r_refdef.vrect.y + r_refdef.vrect.height));\n\t}\n}\n\n\n\n// The 2d refresh stuff.\nvoid SCR_DrawTwoDimensional(qboolean nohud)\n{\n\tqboolean consolefocused = !!Key_Dest_Has(kdm_console|kdm_cwindows);\n\tRSpeedMark();\n\n\tR2D_ImageColours(1, 1, 1, 1);\n\n\t//\n\t// draw any areas not covered by the refresh\n\t//\n\tif (r_netgraph.value)\n\t\tR_NetGraph ();\n\n\tif (scr_drawloading || loading_stage)\n\t{\n\t\tSCR_DrawLoading(false);\n\n\t\tSCR_ShowPics_Draw();\n\n\t\tif (!scr_disabled_for_loading)\n\t\t\tconsolefocused = false;\n\t}\n\telse if (nohud)\n\t{\n\t\tSCR_DrawDisk();\n\t\tSCR_DrawFPS ();\n\t\tSCR_CheckDrawCenterString ();\n\t}\n\telse if (cl.intermissionmode == IM_NQFINALE || cl.intermissionmode == IM_NQCUTSCENE || cl.intermissionmode == IM_H2FINALE)\n\t{\n\t\tSCR_CheckDrawCenterString ();\n\t}\n\telse if (cl.intermissionmode != IM_NONE)\n\t{\n\t\tSbar_IntermissionOverlay (r_refdef.playerview);\n\t}\n\telse\n\t{\n\t\tSCR_DrawNet ();\n\t\tSCR_DrawDisk();\n\t\tSCR_DrawFPS ();\n\t\tSCR_DrawClock();\n\t\tSCR_DrawGameClock();\n\t\tSCR_DrawTurtle ();\n\t\tSCR_DrawPause ();\n\t\tSCR_ShowPics_Draw();\n\t\tSCR_CheckDrawCenterString ();\n\t}\n\n//#ifdef TEXTEDITOR\n//\tif (editoractive)\n//\t\tEditor_Draw();\n//#endif\n\n\t//if the console is not focused, show it scrolling back up behind the menu\n\tif (!consolefocused)\n\t\tSCR_DrawConsole (false);\n\n\tMenu_Draw();\n\n\t//but if the console IS focused, then always show it infront.\n\tif (consolefocused)\n\t\tSCR_DrawConsole (false);\n\n\tPrompts_Draw();\n\n\tSCR_DrawCursor();\n\tSCR_DrawSimMTouchCursor();\n\n\tif (R2D_Flush)\n\t\tR2D_Flush();\n\n\tRSpeedEnd(RSPEED_2D);\n}\n\n\n\n\n\n\n/*\n==================\nSCR_Init\n==================\n*/\nvoid SCR_Init (void)\n{\n//\n// register our commands\n//\n\tCmd_AddCommandD (\"screenshot_mega\",SCR_ScreenShot_Mega_f, \"screenshot_mega <name> [width] [height]\\nTakes a screenshot with explicit sizes that are not tied to the size of your monitor, allowing for true monstrosities.\");\n\tCmd_AddCommandD (\"screenshot_stereo\",SCR_ScreenShot_Mega_f, \"screenshot_stereo <name> [width] [height]\\nTakes a simple stereo screenshot.\");\n\tCmd_AddCommandD (\"screenshot_360\",SCR_ScreenShot_Mega_f, \"screenshot_360 <name> [width] [height]\\nTakes an equirectangular screenshot.\");\n\tCmd_AddCommandD (\"screenshot_vr\",SCR_ScreenShot_VR_f, \"screenshot_vr <name> [width]\\nTakes a spherical stereoscopic panorama image, for viewing with VR displays.\");\n\tCmd_AddCommandD (\"screenshot_cubemap\",SCR_ScreenShot_Cubemap_f, \"screenshot_cubemap <name> [size]\\nTakes 6 screenshots forming a single cubemap.\");\n\tCmd_AddCommandD (\"envmap\",SCR_ScreenShot_Envmap_f, \"Legacy name for the screenshot_cubemap command.\");\t//legacy \n\tCmd_AddCommand (\"screenshot\",SCR_ScreenShot_f);\n\n\tscr_net = R2D_SafePicFromWad (\"net\");\n\tscr_turtle = R2D_SafePicFromWad (\"turtle\");\n\n\tscr_initialized = true;\n}\n\nvoid SCR_DeInit (void)\n{\n\tint i;\n\tif (scr_curcursor)\n\t{\n\t\trf->VID_SetCursor(scr_curcursor);\n\t\tscr_curcursor = NULL;\n\t}\n\tfor (i = 0; i < countof(key_customcursor); i++)\n\t{\n\t\tif (key_customcursor[i].handle)\n\t\t{\n\t\t\trf->VID_DestroyCursor(key_customcursor[i].handle);\n\t\t\tkey_customcursor[i].handle = NULL;\n\t\t}\n\t\tkey_customcursor[i].dirty = true;\n\t}\n\tfor (i = 0; i < countof(scr_centerprint); i++)\n\t{\n\t\twhile (SCR_CenterPrintPop(i))\n\t\t\t;\n\t}\n\tif (scr_initialized)\n\t{\n\t\tscr_initialized = false;\n\n\t\tCmd_RemoveCommand (\"screenshot\");\n\t\tCmd_RemoveCommand (\"screenshot_mega\");\n\t\tCmd_RemoveCommand (\"screenshot_stereo\");\n\t\tCmd_RemoveCommand (\"screenshot_vr\");\n\t\tCmd_RemoveCommand (\"screenshot_360\");\n\t\tCmd_RemoveCommand (\"screenshot_cubemap\");\n\t\tCmd_RemoveCommand (\"envmap\");\n\t}\n}\n"
  },
  {
    "path": "engine/client/cl_tent.c",
    "content": "/*\nCopyright (C) 1996-1997 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n*/\n// cl_tent.c -- client side temporary entities\n\n#include \"quakedef.h\"\n#include \"particles.h\"\nentity_state_t *CL_FindPacketEntity(int num);\n\n#ifdef Q2CLIENT\nstatic const char\t*q2efnames[] =\n{\n\t\"TEQ2_GUNSHOT\",\n\t\"TEQ2_BLOOD\",\n\t\"TEQ2_BLASTER\",\n\t\"TEQ2_RAILTRAIL\",\n\t\"TEQ2_SHOTGUN\",\n\t\"TEQ2_EXPLOSION1\",\n\t\"TEQ2_EXPLOSION2\",\n\t\"TEQ2_ROCKET_EXPLOSION\",\n\t\"TEQ2_GRENADE_EXPLOSION\",\n\t\"TEQ2_SPARKS\",\n\tNULL,//\"TEQ2_SPLASH\",\n\t\"TEQ2_BUBBLETRAIL\",\n\t\"TEQ2_SCREEN_SPARKS\",\n\t\"TEQ2_SHIELD_SPARKS\",\n\t\"TEQ2_BULLET_SPARKS\",\n\tNULL,//\"TEQ2_LASER_SPARKS\",\n\tNULL,//\"TEQ2_PARASITE_ATTACK\",\n\t\"TEQ2_ROCKET_EXPLOSION_WATER\",\n\t\"TEQ2_GRENADE_EXPLOSION_WATER\",\n\tNULL,//\"TEQ2_MEDIC_CABLE_ATTACK\",\n\t\"TEQ2_BFG_EXPLOSION\",\n\t\"TEQ2_BFG_BIGEXPLOSION\",\n\t\"TEQ2_BOSSTPORT\",\n\tNULL,//\"TEQ2_BFG_LASER\",\n\tNULL,//\"TEQ2_GRAPPLE_CABLE\",\n\t\"TEQ2_WELDING_SPARKS\",\n\t\"TEQ2_GREENBLOOD\",\n\t\"TEQ2_BLUEHYPERBLASTER\",\n\t\"TEQ2_PLASMA_EXPLOSION\",\n\t\"TEQ2_TUNNEL_SPARKS\",\n\t\"TEQ2_BLASTER2\",\n\t\"TEQ2_RAILTRAIL2\",\t//not implemented in vanilla\n\tNULL,//\"TEQ2_FLAME\",\t//not implemented in vanilla\n\tNULL,//\"TEQ2_LIGHTNING\",\n\t\"TEQ2_DEBUGTRAIL\",\n\t\"TEQ2_PLAIN_EXPLOSION\",\n\t\"TEQ2_FLASHLIGHT\",\n\t\"TEQ2_FORCEWALL\",\n\tNULL,//\"TEQ2_HEATBEAM\",\n\tNULL,//\"TEQ2_MONSTER_HEATBEAM\",\n\tNULL,//\"TEQ2_STEAM\",\n\t\"TEQ2_BUBBLETRAIL2\",\n\t\"TEQ2_MOREBLOOD\",\n\t\"TEQ2_HEATBEAM_SPARKS\",\n\t\"TEQ2_HEATBEAM_STEAM\",\n\t\"TEQ2_CHAINFIST_SMOKE\",\n\t\"TEQ2_ELECTRIC_SPARKS\",\n\t\"TEQ2_TRACKER_EXPLOSION\",\n\t\"TEQ2_TELEPORT_EFFECT\",\n\t\"TEQ2_DBALL_GOAL\",\n\tNULL,//\"TEQ2_WIDOWBEAMOUT\",\n\tNULL,//\"TEQ2_NUKEBLAST\",\n\t\"TEQ2_WIDOWSPLASH\",\n\t\"TEQ2_EXPLOSION1_BIG\",\n\t\"TEQ2_EXPLOSION1_NP\",\n\t\"TEQ2_FLECHETTE\",\n\n//RERELEASE\n\t\"TEQ2EX_BLUEHYPERBLASTER\",\n\t\"TEQ2EX_BFGZAP\",\n\t\"TEQ2EX_BERSERK_SLAM\",\n\t\"TEQ2EX_GRAPPLE_CABLE_2\",\n\t\"TEQ2EX_POWER_SPLASH\",\n\t\"TEQ2EX_LIGHTNING_BEAM\",\n\t\"TEQ2EX_EXPLOSION1_NL\",\n\t\"TEQ2EX_EXPLOSION2_NL\",\n//RERELEASE\n\n\n//\tNULL,//\"TEQ2_CR_LEADERBLASTER\",\n//\tNULL,//\"TEQ2_CR_BLASTER_MUZZLEFLASH\",\n//\tNULL,//\"TEQ2_CR_BLUE_MUZZLEFLASH\",\n//\tNULL,//\"TEQ2_CR_SMART_MUZZLEFLASH\",\n//\tNULL,//\"TEQ2_CR_LEADERFIELD\",\n//\tNULL,//\"TEQ2_CR_DEATHFIELD\",\n//\tNULL,//\"TEQ2_CR_BLASTERBEAM\",\n//\tNULL,//\"TEQ2_CR_STAIN\",\n\tNULL,//\"TEQ2_CR_FIRE\",\n\tNULL,//\"TEQ2_CR_CABLEGUT\",\n\tNULL,//\"TEQ2_CR_SMOKE\",\n\n\t//the rest have no specific value meanings\n\n\t//slashes block\n\t\"te_splashunknown\",\n\t\"te_splashsparks\",\n\t\"te_splashbluewater\",\n\t\"te_splashbrownwater\",\n\t\"te_splashslime\",\n\t\"te_splashlava\",\n\t\"te_splashblood\",\n\t\"te_splashelect\",\n\n\t\"TR_BLASTERTRAIL\",\n\t\"TR_BLASTERTRAIL2\",\n\t\"TRQ2_GIB\",\n\t\"TRQ2_GREENGIB\",\n\t\"TRQ2_ROCKET\",\n\t\"TRQ2_GRENADE\",\n\n\t\"TR_TRAP\",\n\t\"TR_FLAG1\",\n\t\"TR_FLAG2\",\n\t\"TR_TAGTRAIL\",\n\t\"TR_TRACKER\",\n\t\"TR_IONRIPPER\",\n\t\"TR_PLASMA\",\n\n\t\"EF_BFGPARTICLES\",\n\t\"EF_FLIES\",\n\t\"EF_TRAP\",\n\t\"EF_TRACKERSHELL\",\n\n\t\n\t\"ev_item_respawn\",\n\t\"ev_player_teleport\",\n\t\"ev_footstep\",\n\t\"ev_other_footstep\",\n\t\"ev_ladder_step\",\n};\nint pt_q2[sizeof(q2efnames)/sizeof(q2efnames[0])];\n#endif\n\nint\n\tpt_muzzleflash=P_INVALID,\n\tpt_gunshot=P_INVALID,\n\tptdp_gunshotquad=P_INVALID,\n\tpt_spike=P_INVALID,\n\tptdp_spikequad=P_INVALID,\n\tpt_superspike=P_INVALID,\n\tptdp_superspikequad=P_INVALID,\n\tpt_wizspike=P_INVALID,\n\tpt_knightspike=P_INVALID,\n\tpt_explosion=P_INVALID,\n\tptdp_explosionquad=P_INVALID,\n\tpt_tarexplosion=P_INVALID,\n\tpt_teleportsplash=P_INVALID,\n\tpt_lavasplash=P_INVALID,\n\tptdp_smallflash=P_INVALID,\n\tptdp_flamejet=P_INVALID,\n\tptdp_flame=P_INVALID,\n\tptdp_blood=P_INVALID,\n\tptdp_spark=P_INVALID,\n\tptdp_plasmaburn=P_INVALID,\n\tptdp_tei_g3=P_INVALID,\n\tptdp_tei_smoke=P_INVALID,\n\tptdp_tei_bigexplosion=P_INVALID,\n\tptdp_tei_plasmahit=P_INVALID,\n\tptdp_stardust=P_INVALID,\n\trt_rocket=P_INVALID,\n\trt_grenade=P_INVALID,\n\trt_blood=P_INVALID,\n\trt_wizspike=P_INVALID,\n\trt_slightblood=P_INVALID,\n\trt_knightspike=P_INVALID,\n\trt_vorespike=P_INVALID,\n\trtdp_nexuizplasma=P_INVALID,\n\trtdp_glowtrail=P_INVALID,\n\n\tptqw_gunshot=P_INVALID,\n\tptqw_blood=P_INVALID,\n\tptqw_lightningblood=P_INVALID,\n\n\trtqw_railtrail=P_INVALID,\n\tptfte_bullet=P_INVALID,\n\tptfte_superbullet=P_INVALID;\n\ntypedef struct tentmodels_s {\n\t/*static stuff*/\n\tchar *modelname;\n\tchar *beamparticles;\n\tchar *beamimpactparticle;\n\tchar *beamimpactmodel;\n\tfloat impactscale;\n\tstruct tentmodels_s *partner;\n\tint bflags;\n\n\t/*cached stuff*/\n\tmodel_t *model;\n\tmodel_t *impactmodel;\n\tint ef_beam;\n\tint ef_impact;\n} tentmodels_t;\n\nstruct beam_s {\n\ttentmodels_t *info;\n\tint\t\tentity;\n\tshort\ttag;\n//\tshort\tpad;\n//\tqbyte\tpad;\n\tqbyte\tbflags;\n\tqbyte\ttype;\n\tqbyte\tskin;\n\tunsigned int rflags;\n\n\tfloat\tendtime;\n\tfloat\talpha;\n\tvec3_t\tstart, end;\n\tvec3_t\toffset;\t//when attached, this is the offset from the owning entity. probably only z is meaningful.\n//\tint\t\tparticlecolour;\t//some effects have specific colours. which is weird.\n\ttrailkey_t trailstate;\n\ttrailkey_t emitstate;\n};\n\nbeam_t\t\t*cl_beams;\nint\t\t\tcl_beams_max;\n\ntypedef struct\n{\n\tvec3_t\torigin;\n\tvec3_t\toldorigin;\n\tvec3_t\tvelocity;\n\n\tint firstframe;\n\tint numframes;\n\n\tint\t\ttype;\n\tvec3_t\tangles;\n\tvec3_t\tavel;\n\tvec3_t\trgb;\n\tint\t\tflags;\n\tfloat\tgravity;\n\tfloat\tstartalpha;\n\tfloat\tendalpha;\n\tfloat\tscale;\n\tfloat\tstart;\n\tfloat\tframerate;\n\tmodel_t\t*model;\n\tint skinnum;\n\tint\t\ttraileffect;\n\ttrailkey_t trailstate;\n} explosion_t;\n\nstatic explosion_t\t*cl_explosions;\nstatic int\t\t\tcl_explosions_max;\n\nstatic int explosions_running;\nstatic int beams_running;\n\nstatic tentmodels_t beamtypes[] =\n{\n\t{\"progs/bolt.mdl\",\t\t\t\t\t\t\t\"TE_LIGHTNING1\",\t\t\t\t\"TE_LIGHTNING1_END\"},\n\t{\"progs/bolt2.mdl\",\t\t\t\t\t\t\t\"TE_LIGHTNING2\",\t\t\t\t\"TE_LIGHTNING2_END\"},\n\t{\"progs/bolt3.mdl\",\t\t\t\t\t\t\t\"TE_LIGHTNING3\",\t\t\t\t\"TE_LIGHTNING3_END\"},\n\t{\"progs/beam.mdl\",\t\t\t\t\t\t\t\"te_beam\",\t\t\t\t\t\t\"te_beam_end\"},\t//a CTF addition, but has other potential uses, sadly.\n\n\t{\"models/monsters/parasite/segment/tris.md2\",\"te_parasite_attack\",\t\t\t\"te_parasite_attack_end\"},\n\t{\"models/ctf/segment/tris.md2\",\t\t\t\t\"te_grapple_cable\",\t\t\t\t\"te_grapple_cable_end\"},\n\t{\"models/proj/beam/tris.md2\",\t\t\t\t\"te_heatbeam\",\t\t\t\t\t\"te_heatbeam_end\"},\n\t{\"models/proj/lightning/tris.md2\",\t\t\t\"TE_LIGHTNING2\",\t\t\t\t\"TE_LIGHTNING2_END\"},\n\n\t{\"models/stltng2.mdl\",\t\t\t\t\t\t\"te_stream_lightning_small\",\tNULL},\n\t{\"models/stchain.mdl\",\t\t\t\t\t\t\"te_stream_chain\",\t\t\t\tNULL},\n\t{\"models/stsunsf1.mdl\",\t\t\t\t\t\t\"te_stream_sunstaff1\",\t\t\tNULL,\t\"models/stsunsf3.mdl\", 0.8, &beamtypes[BT_H2SUNSTAFF1_SUB]}, //the core beam\n\t{\"models/stsunsf2.mdl\",\t\t\t\t\t\tNULL,\t\t\t\t\t\t\tNULL,\t\"models/stsunsf4.mdl\", 1.5},//the transparenty bit.\n\t{\"models/stsunsf1.mdl\",\t\t\t\t\t\t\"te_stream_sunstaff2\",\t\t\tNULL},\n\t{\"models/stlghtng.mdl\",\t\t\t\t\t\t\"te_stream_lightning\",\t\t\tNULL},\n\t{\"models/stclrbm.mdl\",\t\t\t\t\t\t\"te_stream_colorbeam\",\t\t\tNULL},\n\t{\"models/stice.mdl\",\t\t\t\t\t\t\"te_stream_icechunks\",\t\t\tNULL},\n\t{\"models/stmedgaz.mdl\",\t\t\t\t\t\t\"te_stream_gaze\",\t\t\t\tNULL},\n\t{\"models/fambeam.mdl\",\t\t\t\t\t\t\"te_stream_famine\",\t\t\t\tNULL},\n};\n\n\nstatic sfx_t\t\t\t*cl_sfx_wizhit;\nsfx_t\t\t\t*cl_sfx_knighthit;\nstatic sfx_t\t\t\t*cl_sfx_tink1;\nstatic sfx_t\t\t\t*cl_sfx_ric1;\nstatic sfx_t\t\t\t*cl_sfx_ric2;\nstatic sfx_t\t\t\t*cl_sfx_ric3;\nsfx_t\t\t\t*cl_sfx_r_exp3;\n\ncvar_t\tcl_expsprite = CVARFD(\"cl_expsprite\", \"1\", CVAR_ARCHIVE, \"Display a central sprite in explosion effects. QuakeWorld typically does so, NQ mods should not (which is problematic when played with the qw protocol).\");\ncvar_t  r_explosionlight = CVARFC(\"r_explosionlight\", \"1\", CVAR_ARCHIVE, Cvar_Limiter_ZeroToOne_Callback);\nstatic cvar_t  r_explosionlight_colour = CVARFD(\"r_explosionlight_colour\", \"4.0 2.0 0.5\", CVAR_ARCHIVE, \"This controls the initial RGB values of EF_EXPLOSION effects.\");\nstatic cvar_t  r_explosionlight_fade = CVARFD(\"r_explosionlight_fade\", \"0.784 0.92 0.48\", CVAR_ARCHIVE, \"This controls the per-second RGB decay values of EF_EXPLOSION effects.\");\ncvar_t  r_dimlight_colour = CVARFD(\"r_dimlight_colour\", \"2.0 1.0 0.5 200\", CVAR_ARCHIVE, \"The red, green, blue, radius values for EF_DIMLIGHT effects (used for quad+pent in vanilla quake).\");\ncvar_t  r_brightlight_colour = CVARFD(\"r_brightlight_colour\", \"2.0 1.0 0.5 400\", CVAR_ARCHIVE, \"The red, green, blue, radius values for EF_BRIGHTLIGHT effects (unused in vanilla quake).\");\ncvar_t  r_redlight_colour = CVARFD(\"r_redlight_colour\", \"3.0 0.5 0.5 200\", CVAR_ARCHIVE, \"The red, green, blue, radius values for EF_RED effects (typically used for pentagram in quakeworld).\");\ncvar_t  r_greenlight_colour = CVARFD(\"r_greenlight_colour\", \"0.5 3.0 0.5 200\", CVAR_ARCHIVE, \"The red, green, blue, radius values for EF_GREEN effects (rarely used).\");\ncvar_t  r_bluelight_colour = CVARFD(\"r_bluelight_colour\", \"0.5 0.5 3.0 200\", CVAR_ARCHIVE, \"The red, green, blue, radius values for EF_BLUE effects (typically used for quad-damage in quakeworld)\");\ncvar_t  r_rocketlight_colour = CVARFD(\"r_rocketlight_colour\", \"2.0 1.0 0.25 200\", CVAR_ARCHIVE, \"This controls the RGB+radius values of MF_ROCKET effects.\");\ncvar_t  r_muzzleflash_colour = CVARFD(\"r_muzzleflash_colour\", \"1.5 1.3 1.0 200\", CVAR_ARCHIVE, \"This controls the initial RGB+radius of EF_MUZZLEFLASH/svc_muzzleflash effects.\");\ncvar_t  r_muzzleflash_fade = CVARFD(\"r_muzzleflash_fade\", \"1.5 0.75 0.375 1000\", CVAR_ARCHIVE, \"This controls the per-second RGB+radius decay of EF_MUZZLEFLASH/svc_muzzleflash effects.\");\ncvar_t\tcl_truelightning = CVARFD(\"cl_truelightning\", \"0\",\tCVAR_SEMICHEAT, \"Manipulate the end position of the player's own beams, to hide lag. Can be set to fractional values to reduce the effect.\");\nstatic cvar_t  cl_beam_trace = CVARD(\"cl_beam_trace\", \"0\", \"Clips the length of any beams according to any walls they may have impacted.\");\nstatic cvar_t  cl_beam_alpha = CVARAFD(\"cl_beam_alpha\", \"1\", \"r_shaftalpha\", 0, \"Specifies the translucency of the lightning beam segments.\");\nstatic cvar_t\tcl_legacystains = CVARD(\"cl_legacystains\", \"1\", \"WARNING: this cvar will default to 0 and later removed at some point\");\t//FIXME: do as the description says!\nstatic cvar_t\tcl_shaftlight = CVAR(\"gl_shaftlight\", \"0.8\");\nstatic cvar_t\tcl_part_density_fade_start = CVARD(\"cl_part_density_fade_start\", \"1024\", \"Specifies the distance at which ssqc's pointparticles will start to get less dense.\");\nstatic cvar_t\tcl_part_density_fade = CVARD(\"cl_part_density_fade\", \"1024\", \"Specifies the distance over which ssqc pointparticles density fades from all to none. If this is set to 0 then particles will spawn at their normal density regardless of location on the map.\");\n\ntypedef struct {\n\tsfx_t **sfx;\n\tchar *efname;\n} tentsfx_t;\ntentsfx_t tentsfx[] =\n{\n\t{&cl_sfx_wizhit, \"wizard/hit.wav\"},\n\t{&cl_sfx_knighthit, \"hknight/hit.wav\"},\n\t{&cl_sfx_tink1, \"weapons/tink1.wav\"},\n\t{&cl_sfx_ric1, \"weapons/ric1.wav\"},\n\t{&cl_sfx_ric2, \"weapons/ric2.wav\"},\n\t{&cl_sfx_ric3, \"weapons/ric3.wav\"},\n\t{&cl_sfx_r_exp3, \"weapons/r_exp3.wav\"}\n};\n\nvec3_t playerbeam_end[MAX_SPLITS];\n\ntypedef struct associatedeffect_s\n{\n\tstruct associatedeffect_s *next;\n\tchar mname[MAX_QPATH];\n\tchar pname[MAX_QPATH];\n\tenum\n\t{\n\t\tAE_TRAIL,\n\t\tAE_EMIT,\n\t} type;\n\tunsigned int meflags;\n} associatedeffect_t;\nassociatedeffect_t *associatedeffect;\nchar part_parsenamespace[MAX_QPATH];\nvoid CL_AssociateEffect_f(void)\n{\n\tchar *modelname = Cmd_Argv(1);\n\tchar *effectname = Cmd_Argv(2);\n\tint type, i;\n\tunsigned int flags = 0;\n\tstruct associatedeffect_s *ae;\n\tif (Cmd_Argc() == 1)\n\t{\n\t\tfor(ae = associatedeffect; ae; ae = ae->next)\n\t\t{\n\t\t\tCon_Printf(\"^h%c^h %s: %s%s%s\\n\", (ae->type==AE_TRAIL)?'t':'e', ae->mname, ae->pname,\n\t\t\t\t(ae->meflags & MDLF_EMITREPLACE)?\" (replace)\":\"\",\n\t\t\t\t(ae->meflags & MDLF_EMITFORWARDS)?\" (foward)\":\"\");\n\t\t}\n\t\treturn;\n\t}\n\tif (!strcmp(Cmd_Argv(0), \"r_trail\"))\n\t\ttype = AE_TRAIL;\n\telse\n\t{\n\t\ttype = AE_EMIT;\n\n\t\tfor (i = 3; i < Cmd_Argc(); i++)\n\t\t{\n\t\t\tconst char *fn = Cmd_Argv(i);\n\t\t\tif (!strcmp(fn, \"replace\") || !strcmp(fn, \"1\"))\n\t\t\t\tflags |= MDLF_EMITREPLACE;\n\t\t\telse if (!strcmp(fn, \"forwards\") || !strcmp(fn, \"forward\"))\n\t\t\t\tflags |= MDLF_EMITFORWARDS;\n\t\t\telse if (!strcmp(fn, \"0\"))\n\t\t\t\t;\t//1 or 0 are legacy, meaning replace or not\n\t\t\telse\n\t\t\t\tCon_DPrintf(\"%s %s: unknown flag %s\\n\", Cmd_Argv(0), modelname, fn);\n\t\t}\n\t}\n\n\tif (\n\t\tstrstr(modelname, \"player\") ||\n\t\tstrstr(modelname, \"eyes\") ||\n\t\tstrstr(modelname, \"flag\") ||\n\t\tstrstr(modelname, \"tf_stan\") ||\n\t\tstrstr(modelname, \".bsp\") ||\n\t\tstrstr(modelname, \"turr\"))\n\t{\n\t\tCon_Printf(\"Sorry: Not allowed to attach effects to model \\\"%s\\\"\\n\", modelname);\n\t\treturn;\n\t}\n\n\tif (*part_parsenamespace && !strchr(effectname, '.'))\n\t\teffectname = va(\"%s.%s\", part_parsenamespace, effectname);\n\n\tif (strlen (modelname) >= MAX_QPATH || strlen(effectname) >= MAX_QPATH)\n\t\treturn;\n\n\t/*replace the old one if it exists*/\n\tfor(ae = associatedeffect; ae; ae = ae->next)\n\t{\n\t\tif (!strcmp(ae->mname, modelname))\n\t\t\tif ((ae->type==AE_TRAIL) == (type==AE_TRAIL))\n\t\t\t\tbreak;\n\t}\n\tif (!ae)\n\t{\n\t\tae = Z_Malloc(sizeof(*ae));\n\t\tstrcpy(ae->mname, modelname);\n\t\tae->next = associatedeffect;\n\t\tassociatedeffect = ae;\n\t}\n\tae->type = type;\n\tae->meflags = flags;\n\tstrcpy(ae->pname, effectname);\n\n\tif (pe)\n\t\tCL_RegisterParticles();\n}\n\nvoid CL_InitTEntSounds (void)\n{\n\tint i;\n\tfor (i = 0; i < sizeof(tentsfx)/sizeof(tentsfx[0]); i++)\n\t{\n\t\tif (COM_FCheckExists(va(\"sound/%s\", tentsfx[i].efname)))\n\t\t\t*tentsfx[i].sfx = S_PrecacheSound (tentsfx[i].efname);\n\t\telse\n\t\t\t*tentsfx[i].sfx = NULL;\n\t}\n}\n\n/*\n=================\nCL_ParseTEnts\n=================\n*/\nvoid CL_InitTEnts (void)\n{\n\tint i;\n\tfor (i = 0; i < sizeof(tentsfx)/sizeof(tentsfx[0]); i++)\n\t\t*tentsfx[i].sfx = NULL;\n\n\tCmd_AddCommand(\"r_effect\", CL_AssociateEffect_f);\n\tCmd_AddCommand(\"r_trail\", CL_AssociateEffect_f);\n\n\tCvar_Register (&cl_expsprite, \"Temporary entity control\");\n\tCvar_Register (&cl_truelightning, \"Temporary entity control\");\n\tCvar_Register (&cl_beam_trace, \"Temporary entity control\");\n\tCvar_Register (&cl_beam_alpha, \"Temporary entity control\");\n\tCvar_Register (&r_explosionlight, \"Temporary entity control\");\n\tCvar_Register (&r_explosionlight_colour, \"Temporary entity control\");\n\tCvar_Register (&r_explosionlight_fade, \"Temporary entity control\");\n\tCvar_Register (&r_muzzleflash_colour, \"Temporary entity control\");\n\tCvar_Register (&r_muzzleflash_fade, \"Temporary entity control\");\n\tCvar_Register (&r_dimlight_colour, \"Temporary entity control\");\n\tCvar_Register (&r_redlight_colour, \"Temporary entity control\");\n\tCvar_Register (&r_greenlight_colour, \"Temporary entity control\");\n\tCvar_Register (&r_bluelight_colour, \"Temporary entity control\");\n\tCvar_Register (&r_brightlight_colour, \"Temporary entity control\");\n\tCvar_Register (&r_rocketlight_colour, \"Temporary entity control\");\n\tCvar_Register (&cl_legacystains, \"Temporary entity control\");\n\tCvar_Register (&cl_shaftlight, \"Temporary entity control\");\n\n\tCvar_Register (&cl_part_density_fade_start, \"Temporary entity control\");\n\tCvar_Register (&cl_part_density_fade, \"Temporary entity control\");\n}\n\nvoid CL_ShutdownTEnts (void)\n{\n\tstruct associatedeffect_s *ae;\n\twhile(associatedeffect)\n\t{\n\t\tae = associatedeffect;\n\t\tassociatedeffect = ae->next;\n\t\tBZ_Free(ae);\n\t}\n}\n\nvoid CL_ClearTEntParticleState (void)\n{\n\tint i;\n\tfor (i = 0; i < cl_beams_max; i++)\n\t{\n\t\tif (cl_beams[i].trailstate)\n\t\t\tP_DelinkTrailstate(&(cl_beams[i].trailstate));\n\t\tif (cl_beams[i].emitstate)\n\t\t\tP_DelinkTrailstate(&(cl_beams[i].emitstate));\n\t}\n}\n\nvoid P_LoadedModel(model_t *mod)\n{\n\tstruct associatedeffect_s *ae;\n\n\tmod->particleeffect = P_INVALID;\n\tmod->particletrail = P_INVALID;\n\tmod->engineflags &= ~(MDLF_EMITREPLACE|MDLF_EMITFORWARDS);\n\tmod->engineflags |= MDLF_RECALCULATERAIN;\n\tfor(ae = associatedeffect; ae; ae = ae->next)\n\t{\n\t\tif (!strcmp(ae->mname, mod->name))\n\t\t{\n\t\t\tswitch(ae->type)\n\t\t\t{\n\t\t\tcase AE_TRAIL:\n\t\t\t\tmod->particletrail = P_FindParticleType(ae->pname);\n\t\t\t\tbreak;\n\t\t\tcase AE_EMIT:\n\t\t\t\tmod->particleeffect = P_FindParticleType(ae->pname);\n\t\t\t\tmod->engineflags |= ae->meflags;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\tif (mod->particletrail == P_INVALID)\n\t\tP_DefaultTrail(0, mod->flags, &mod->particletrail, &mod->traildefaultindex);\n}\n\nvoid CL_RefreshCustomTEnts(void);\nvoid CL_RegisterParticles(void)\n{\n\tmodel_t *mod;\n\textern model_t\t*mod_known;\n\textern int\t\tmod_numknown;\n\tint i;\n\tfor (i=0 , mod=mod_known ; i<mod_numknown ; i++, mod++)\n\t{\n\t\tif (mod->loadstate == MLS_LOADED)\n\t\t{\n\t\t\tP_LoadedModel(mod);\n\t\t}\n\t}\n\n\tpt_muzzleflash\t\t\t= P_FindParticleType(\"TE_MUZZLEFLASH\");\n\tpt_gunshot\t\t\t\t= P_FindParticleType(\"TE_GUNSHOT\");\t/*shotgun*/\n\tptdp_gunshotquad\t\t= P_FindParticleType(\"TE_GUNSHOTQUAD\");\t/*DP: quadded shotgun*/\n\tpt_spike\t\t\t\t= P_FindParticleType(\"TE_SPIKE\");\t/*nailgun*/\n\tptdp_spikequad\t\t\t= P_FindParticleType(\"TE_SPIKEQUAD\");\t/*DP: quadded nailgun*/\n\tpt_superspike\t\t\t= P_FindParticleType(\"TE_SUPERSPIKE\");\t/*nailgun*/\n\tptdp_superspikequad\t\t= P_FindParticleType(\"TE_SUPERSPIKEQUAD\");\t/*DP: quadded nailgun*/\n\tpt_wizspike\t\t\t\t= P_FindParticleType(\"TE_WIZSPIKE\");\t//scrag missile impact\n\tpt_knightspike\t\t\t= P_FindParticleType(\"TE_KNIGHTSPIKE\"); //hellknight missile impact\n\tpt_explosion\t\t\t= P_FindParticleType(\"TE_EXPLOSION\");/*rocket/grenade launcher impacts/far too many things*/\n\tptdp_explosionquad\t\t= P_FindParticleType(\"TE_EXPLOSIONQUAD\");\t/*nailgun*/\n\tpt_tarexplosion\t\t\t= P_FindParticleType(\"TE_TAREXPLOSION\");//tarbaby/spawn dying.\n\tpt_teleportsplash\t\t= P_FindParticleType(\"TE_TELEPORT\");/*teleporters*/\n\tpt_lavasplash\t\t\t= P_FindParticleType(\"TE_LAVASPLASH\");\t//e1m7 boss dying.\n\tptdp_smallflash\t\t\t= P_FindParticleType(\"TE_SMALLFLASH\");\t//DP:\n\tptdp_flamejet\t\t\t= P_FindParticleType(\"TE_FLAMEJET\");\t//DP:\n\tptdp_flame\t\t\t\t= P_FindParticleType(\"EF_FLAME\");\t//DP:\n\tptdp_blood\t\t\t\t= P_FindParticleType(\"TE_BLOOD\"); /*when you hit something with the shotgun/axe/nailgun - nq uses the general particle builtin*/\n\tptdp_spark\t\t\t\t= P_FindParticleType(\"TE_SPARK\");//DPTE_SPARK\n\tptdp_plasmaburn\t\t\t= P_FindParticleType(\"TE_PLASMABURN\");\n\tptdp_tei_g3\t\t\t\t= P_FindParticleType(\"TE_TEI_G3\");\n\tptdp_tei_smoke\t\t\t= P_FindParticleType(\"TE_TEI_SMOKE\");\n\tptdp_tei_bigexplosion\t= P_FindParticleType(\"TE_TEI_BIGEXPLOSION\");\n\tptdp_tei_plasmahit\t\t= P_FindParticleType(\"TE_TEI_PLASMAHIT\");\n\tptdp_stardust\t\t\t= P_FindParticleType(\"EF_STARDUST\");\n\trt_rocket\t\t\t\t= P_FindParticleType(\"TR_ROCKET\");\t/*rocket trail*/\n\trt_grenade\t\t\t\t= P_FindParticleType(\"TR_GRENADE\");\t/*grenade trail*/\n\trt_blood\t\t\t\t= P_FindParticleType(\"TR_BLOOD\");\t/*blood trail*/\n\trt_wizspike\t\t\t\t= P_FindParticleType(\"TR_WIZSPIKE\");\n\trt_slightblood\t\t\t= P_FindParticleType(\"TR_SLIGHTBLOOD\");\n\trt_knightspike\t\t\t= P_FindParticleType(\"TR_KNIGHTSPIKE\");\n\trt_vorespike\t\t\t= P_FindParticleType(\"TR_VORESPIKE\");\n\t//rtdp_neharasmoke\t\t= P_FindParticleType(\"TR_NEHAHRASMOKE\");\n\trtdp_nexuizplasma\t\t= P_FindParticleType(\"TR_NEXUIZPLASMA\");\n\trtdp_glowtrail\t\t\t= P_FindParticleType(\"TR_GLOWTRAIL\");\n\t/*internal to psystem*/   P_FindParticleType(\"SVC_PARTICLE\");\n\n\tptqw_gunshot\t\t\t= (pt_gunshot!=P_INVALID)?pt_gunshot:P_FindParticleType(\"TE_QWGUNSHOT\");\t/*shotgun*/\n\tptqw_blood\t\t\t\t= (ptdp_blood!=P_INVALID)?ptdp_blood:P_FindParticleType(\"TE_QWBLOOD\");\n\tptqw_lightningblood\t\t= P_FindParticleType(\"TE_LIGHTNINGBLOOD\");\n\n#ifdef Q2CLIENT\n\tif (cls.protocol == CP_QUAKE2)\n\t{\n\t\tfor (i = 0; i < sizeof(pt_q2)/sizeof(pt_q2[0]); i++)\n\t\t{\n\t\t\tif (!q2efnames[i])\n\t\t\t{\n\t\t\t\tpt_q2[i] = P_INVALID;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tpt_q2[i] = P_FindParticleType(va(\"q2part.%s\", q2efnames[i]));\n\n#ifdef _DEBUG\n\t\t\tif (pt_q2[i] == P_INVALID && pt_q2[0] != P_INVALID)\n\t\t\t\tCon_Printf(\"effect q2part.%s was not declared\\n\", q2efnames[i]);\n#endif\n\t\t}\n\n\t\t/*ptq2_blood\t\t\t\t= P_FindParticleType(\"q2part.TEQ2_BLOOD\");\n\t\trtq2_railtrail\t\t\t= P_FindParticleType(\"q2part.TR_RAILTRAIL\");\n\t\trtq2_blastertrail\t\t= P_FindParticleType(\"q2part.TR_BLASTERTRAIL\");\n\t\tptq2_blasterparticles\t= P_FindParticleType(\"q2part.TE_BLASTERPARTICLES\");\n\t\trtq2_bubbletrail\t\t= P_FindParticleType(\"q2part.TE_BUBBLETRAIL\");\n\t\trtq2_gib\t\t\t\t= P_FindParticleType(\"q2part.TR_GIB\");\n\t\trtq2_rocket\t\t\t\t= P_FindParticleType(\"q2part.TR_ROCKET\");\n\t\trtq2_grenade\t\t\t= P_FindParticleType(\"q2part.TR_GRENADE\");\n\t\tptq2_bfgparticles\t\t= P_FindParticleType(\"q2part.TR_BFGPARTICLES\");\n\t\tptq2_flies\t\t\t\t= P_FindParticleType(\"q2part.TR_FLIES\");\n\t\tptq2_trap\t\t\t\t= P_FindParticleType(\"q2part.TR_TRAP\");\n\t\tptq2_trackershell\t\t= P_FindParticleType(\"q2part.TR_TRACKERSHELL\");*/\n\t}\n\telse\n\t{\n\t\tfor (i = 0; i < sizeof(pt_q2)/sizeof(pt_q2[0]); i++)\n\t\t\tpt_q2[i] = P_INVALID;\n\t}\n#endif\n\n\trtqw_railtrail\t\t\t= P_FindParticleType(\"TE_RAILTRAIL\");\n\tptfte_bullet\t\t\t= P_FindParticleType(\"TE_BULLET\");\n\tptfte_superbullet\t\t= P_FindParticleType(\"TE_SUPERBULLET\");\n\n\tCL_RefreshCustomTEnts();\n\n\tfor (i = 0; i < countof(beamtypes); i++)\n\t{\n\t\t//we can normally expect the server to have precache_modeled these models, so any lookups should be just a lookup, and thus relatively cheap.\n\t\tbeamtypes[i].model = NULL;\n\t\tbeamtypes[i].impactmodel = NULL;\n\t\tbeamtypes[i].ef_beam = beamtypes[i].beamparticles?P_FindParticleType(beamtypes[i].beamparticles):P_INVALID;\n\t\tbeamtypes[i].ef_impact = beamtypes[i].beamimpactparticle?P_FindParticleType(beamtypes[i].beamimpactparticle):P_INVALID;\n\t}\n\n\t//FIXME\n\tfor (i = 0; i < cl_explosions_max; i++)\n\t\tcl_explosions[i].model = NULL;\n}\n\n#ifdef Q2CLIENT\nenum {\n\tq2cl_mod_explode,\n\tq2cl_mod_smoke,\n\tq2cl_mod_flash,\n\tq2cl_mod_parasite_tip,\n\tq2cl_mod_explo4,\n\tq2cl_mod_bfg_explo,\n\tq2cl_mod_powerscreen,\n\tq2cl_mod_max\n};\ntentmodels_t q2tentmodels[q2cl_mod_max] = {\n\t{\"models/objects/explode/tris.md2\"},\n\t{\"models/objects/smoke/tris.md2\"},\n\t{\"models/objects/flash/tris.md2\"},\n\t{\"models/monsters/parasite/tip/tris.md2\"},\n\t{\"models/objects/r_explode/tris.md2\"},\n\t{\"sprites/s_bfg2.sp2\"},\n\t{\"models/items/armor/effect/tris.md2\"}\n};\n\nint CLQ2_RegisterTEntModels (void)\n{\n//\tint i;\n//\tfor (i = 0; i < q2cl_mod_max; i++)\n//\t\tif (!CL_CheckOrDownloadFile(q2tentmodels[i].modelname, false))\n//\t\t\treturn false;\n\n\treturn true;\n}\n#endif\n\nstatic void CL_ClearExplosion(explosion_t *exp, vec3_t org)\n{\n\texp->endalpha = 0;\n\texp->startalpha = 1;\n\texp->rgb[0] = 1.0f;\n\texp->rgb[1] = 1.0f;\n\texp->rgb[2] = 1.0f;\n\texp->scale = 1;\n\texp->gravity = 0;\n\texp->flags = 0;\n\texp->model = NULL;\n\texp->firstframe = -1;\n\texp->framerate = 10;\n\tVectorClear(exp->velocity);\n\tVectorClear(exp->angles);\n\tVectorClear(exp->avel);\n\tif (pe)\n\t\tP_DelinkTrailstate(&(exp->trailstate));\n\texp->traileffect = P_INVALID;\n\tVectorCopy(org, exp->origin);\n\tVectorCopy(org, exp->oldorigin);\n}\n\n/*\n=================\nCL_ClearTEnts\n=================\n*/\nvoid CL_ClearTEnts (void)\n{\n\tint i;\n\tCL_ClearTEntParticleState();\n\tCL_ShutdownTEnts();\n\n\tcl_beams_max = 0;\n\tBZ_Free(cl_beams);\n\tcl_beams = NULL;\n\tbeams_running = 0;\n\n\tfor (i = 0; i < cl_explosions_max; i++)\n\t\tCL_ClearExplosion(cl_explosions+i, vec3_origin);\n\tcl_explosions_max = 0;\n\tBZ_Free(cl_explosions);\n\tcl_explosions = NULL;\n\texplosions_running = 0;\n}\n\n/*\n=================\nCL_AllocExplosion\n=================\n*/\nexplosion_t *CL_AllocExplosion (vec3_t org)\n{\n\tint\t\ti;\n\tfloat\ttime;\n\tint\t\tindex;\n\n\tfor (i=0; i < explosions_running; i++)\n\t{\n\t\tif (!cl_explosions[i].model)\n\t\t{\n\t\t\tCL_ClearExplosion(&cl_explosions[i], org);\n\t\t\treturn &cl_explosions[i];\n\t\t}\n\t}\n\n//\tif (i == explosions_running && i < cl_maxexplosions.ival)\n\t{\n\t\tif (i == cl_explosions_max)\n\t\t{\n\t\t\tcl_explosions_max = (i+1)*2;\n\t\t\tcl_explosions = BZ_Realloc(cl_explosions, sizeof(*cl_explosions)*cl_explosions_max);\n\t\t\tmemset(cl_explosions + i, 0, sizeof(*cl_explosions)*(cl_explosions_max-i));\n\t\t}\n\n\t\texplosions_running++;\n\t\tCL_ClearExplosion(&cl_explosions[i], org);\n\t\treturn &cl_explosions[i];\n\t}\n\n// find the oldest explosion\n\ttime = cl.time;\n\tindex = 0;\n\n\tfor (i=0 ; i<cl_explosions_max ; i++)\n\t\tif (cl_explosions[i].start < time)\n\t\t{\n\t\t\ttime = cl_explosions[i].start;\n\t\t\tindex = i;\n\t\t}\n\tCL_ClearExplosion(&cl_explosions[index], org);\n\treturn &cl_explosions[index];\n}\n\n/*\n=================\nCL_ParseBeam\n=================\n*/\nbeam_t\t*CL_NewBeam (int entity, int tag, tentmodels_t *btype)\n{\n\tbeam_t\t*b;\n\tint i;\n\n// override any beam with the same entity (unless they used world)\n\tif (entity)\n\t{\n\t\tfor (i=0, b=cl_beams; i < beams_running; i++, b++)\n\t\t\tif (b->entity == entity && b->tag == tag)\n\t\t\t\tgoto found;\n\t}\n\n// find a free beam\n\tfor (i=0, b=cl_beams; i < beams_running; i++, b++)\n\t{\n\t\tif (!b->info)\n\t\t\tgoto found;\n\t}\n\n\n//\tif (i >= cl_maxbeams.ival)\n//\t\treturn NULL;\n\tif (i == cl_beams_max)\n\t{\n\t\tint nm = (i+1)*2;\n\t\tCL_ClearTEntParticleState();\n\n\t\tcl_beams = BZ_Realloc(cl_beams, nm*sizeof(*cl_beams));\n\t\tmemset(cl_beams + cl_beams_max, 0, sizeof(*cl_beams)*(nm-cl_beams_max));\n\t\tcl_beams_max = nm;\n\t}\n\n\tbeams_running++;\n\tb = &cl_beams[i];\n\nfound:\n\tb->info = btype;\n\tb->bflags = 0;\n\tVectorClear(b->offset);\n\treturn b;\n}\n#define\tSTREAM_ATTACHTOPLAYER\t1\t//if owned by the viewentity then attach to camera (but don't for other entities).\n#define STREAM_JITTER\t\t\t2\t//moves up to 30qu forward/back (40qu per sec)\n#define STREAM_ATTACHED\t\t\t16\t//attach it to any entity's origin\n#define STREAM_TRANSLUCENT\t\t32\nbeam_t *CL_AddBeam (enum beamtype_e tent, int ent, vec3_t start, vec3_t end)\t//fixme: use TE_ numbers instead of 0 - 5\n{\n\tbeam_t\t*b;\n\n\tmodel_t *m;\n\tint btype, etype;\n\tint i;\n\tvec3_t impact, normal;\n\tvec3_t extra;\n\n\t//zquake compat requires some parsing weirdness.\n\tswitch(tent)\n\t{\n\tcase BT_Q1LIGHTNING1:\n\t\tif (ent < 0 && ent >= -512)\t//a zquake concept. ent between -1 and -maxplayers is to be taken to be a railtrail from a particular player instead of a beam.\n\t\t{\n\t\t\t// TODO: add support for those finnicky colored railtrails...\n\t\t\tif (P_ParticleTrail(start, end, rtqw_railtrail, 0.1, -ent, NULL, NULL))\n\t\t\t\tP_ParticleTrailIndex(start, end, P_INVALID, 0.1, 208, 8, NULL);\n\t\t\treturn NULL;\n\t\t}\n\t\tbreak;\n\tcase BT_Q1LIGHTNING2:\n\t\tif (ent < 0 && ent >= -MAX_CLIENTS)\t//based on the railgun concept - this adds a rogue style TE_BEAM effect.\n\t\t\ttent = BT_Q1BEAM;\n\t\tbreak;\n\tdefault:\n\t\tbreak;\n\t}\n\n\tbtype = beamtypes[tent].ef_beam;\n\tetype = beamtypes[tent].ef_impact;\n\n\t/*don't bother loading the model if we have a particle effect for it instead*/\n\tif (ruleset_allow_particle_lightning.ival && btype >= 0)\n\t\tm = NULL;\n\telse\n\t{\n\t\tm = beamtypes[tent].model;\n\t\tif (!m)\n\t\t\tm = beamtypes[tent].model = Mod_ForName(beamtypes[tent].modelname, MLV_WARN);\n\t}\n\n\tif (m && m->loadstate != MLS_LOADED)\n\t\tCL_CheckOrEnqueDownloadFile(m->name, NULL, 0);\n\n\t// save end position for truelightning\n\tif (ent)\n\t{\n\t\tfor (i = 0; i < cl.splitclients; i++)\n\t\t{\n\t\t\tplayerview_t *pv = &cl.playerview[i];\n\t\t\tif (ent == ((pv->cam_state == CAM_EYECAM)?(pv->cam_spec_track+1):(pv->playernum+1)))\n\t\t\t{\n\t\t\t\tVectorCopy(end, playerbeam_end[i]);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\tif (etype >= 0 && cls.state == ca_active && etype != P_INVALID)\n\t{\n\t\tif (cl_beam_trace.ival)\n\t\t{\n\t\t\tVectorSubtract(end, start, normal);\n\t\t\tVectorNormalize(normal);\n\t\t\tVectorMA(end, 4, normal, extra);\t//extend the end-point by four\n\t\t\tif (CL_TraceLine(start, extra, impact, normal, NULL)>=1)\n\t\t\t\tetype = -1;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tVectorCopy(end, impact);\n\t\t\tnormal[0] = normal[1] = normal[2] = 0;\n\t\t}\n\t}\n\n\tb = CL_NewBeam(ent, -1, &beamtypes[tent]);\n\tif (!b)\n\t{\n\t\tCon_Printf (\"beam list overflow!\\n\");\n\t\treturn NULL;\n\t}\n\n\tb->rflags = RF_NOSHADOW;\n\tb->entity = ent;\n\tb->info = &beamtypes[tent];\n\tb->tag = -1;\n\tb->bflags |= /*STREAM_ATTACHED|*/STREAM_ATTACHTOPLAYER;\n\tb->endtime = cl.time + 0.2;\n\tb->alpha = bound(0, cl_beam_alpha.value, 1);\n\tif(b->alpha < 1)\n\t\tb->rflags |= RF_TRANSLUCENT;\n\tb->skin = 0;\n\tVectorCopy (start, b->start);\n\tVectorCopy (end, b->end);\n\n\tif (etype >= 0)\n\t{\n\t\tP_RunParticleEffectState (impact, normal, 1, etype, &(b->emitstate));\n\t\tif (cl_legacystains.ival) Surf_AddStain(end, -10, -10, -10, 20);\n\t}\n\treturn b;\n}\nvoid CL_ParseBeamOffset (enum beamtype_e tent)\n{\n\tint\t\tent;\n\tvec3_t\tstart, end, offset;\n\tbeam_t *b;\n\n\tent = MSGCL_ReadEntity ();\n\n\tstart[0] = MSG_ReadCoord ();\n\tstart[1] = MSG_ReadCoord ();\n\tstart[2] = MSG_ReadCoord ();\n\n\tend[0] = MSG_ReadCoord ();\n\tend[1] = MSG_ReadCoord ();\n\tend[2] = MSG_ReadCoord ();\n\n\tMSG_ReadPos(offset);\n\n\tb = CL_AddBeam(tent, ent, start, end);\n\tif (b)\n\t\tVectorCopy(offset, b->offset);\n}\nbeam_t *CL_ParseBeam (enum beamtype_e tent)\n{\n\tint\t\tent;\n\tvec3_t\tstart, end;\n\n\tent = MSGCL_ReadEntity ();\n\n\tstart[0] = MSG_ReadCoord ();\n\tstart[1] = MSG_ReadCoord ();\n\tstart[2] = MSG_ReadCoord ();\n\n\tend[0] = MSG_ReadCoord ();\n\tend[1] = MSG_ReadCoord ();\n\tend[2] = MSG_ReadCoord ();\n\n\treturn CL_AddBeam(tent, ent, start, end);\n}\n\n//finds the latest non-lerped position\nfloat *CL_FindLatestEntityOrigin(int entnum)\n{\n\tint i;\n\tpacket_entities_t *pe;\n\tint framenum = cl.validsequence & UPDATE_MASK;\n\tpe = &cl.inframes[framenum].packet_entities;\n\tfor (i = 0; i < pe->num_entities; i++)\n\t{\n\t\tif (pe->entities[i].number == entnum)\n\t\t\treturn pe->entities[i].origin;\n\t}\n\tif (entnum > 0 && entnum <= MAX_CLIENTS)\n\t{\n\t\tentnum--;\n\t\tif (cl.inframes[framenum].playerstate[entnum].messagenum == cl.parsecount)\n\t\t\treturn cl.inframes[framenum].playerstate[entnum].origin;\n\t}\n\treturn NULL;\n}\n\nvoid CL_ParseStream (int type)\n{\n\tint\t\tent;\n\tvec3_t\tstart, end;\n\tbeam_t\t*b, *b2;\n\tint flags;\n\tint tag;\n\tfloat duration;\n\tint skin;\n\ttentmodels_t *info;\n\n\tent = MSGCL_ReadEntity();\n\tflags = MSG_ReadByte();\n\ttag = flags&15;\n\tflags-=tag;\n\tduration = (float)MSG_ReadByte()*0.05;\n\tskin = 0;\n\tif(type == TEH2_STREAM_COLORBEAM)\n\t{\n\t\tskin = MSG_ReadByte();\n\t}\n\tstart[0] = MSG_ReadCoord();\n\tstart[1] = MSG_ReadCoord();\n\tstart[2] = MSG_ReadCoord();\n\tend[0] = MSG_ReadCoord();\n\tend[1] = MSG_ReadCoord();\n\tend[2] = MSG_ReadCoord();\n\n\tswitch(type)\n\t{\n\tcase TEH2_STREAM_LIGHTNING_SMALL:\n\t\tinfo = &beamtypes[BT_H2LIGHTNING_SMALL];\n\t\tflags |= STREAM_JITTER;\n\t\tbreak;\n\tcase TEH2_STREAM_LIGHTNING:\n\t\tinfo = &beamtypes[BT_H2LIGHTNING];\n\t\tflags |= STREAM_JITTER;\n\t\tbreak;\n\tcase TEH2_STREAM_ICECHUNKS:\n\t\tinfo = &beamtypes[BT_H2ICECHUNKS];\n\t\tflags |= STREAM_JITTER;\n\t\tif (cl_legacystains.ival) Surf_AddStain(end, -10, -10, 0, 20);\n\t\tbreak;\n\tcase TEH2_STREAM_SUNSTAFF1:\n\t\tinfo = &beamtypes[BT_H2SUNSTAFF1];\n\t\tbreak;\n\tcase TEH2_STREAM_SUNSTAFF2:\n\t\tinfo = &beamtypes[BT_H2SUNSTAFF2];\n\t\tif (cl_legacystains.ival) Surf_AddStain(end, -10, -10, -10, 20);\n\t\tbreak;\n\tcase TEH2_STREAM_COLORBEAM:\n\t\tinfo = &beamtypes[BT_H2COLORBEAM];\n\t\tbreak;\n\tcase TEH2_STREAM_GAZE:\n\t\tinfo = &beamtypes[BT_H2GAZE];\n\t\tbreak;\n\tcase TEH2_STREAM_FAMINE:\n\t\tinfo = &beamtypes[BT_H2FAMINE];\n\t\tbreak;\n\tcase TEH2_STREAM_CHAIN:\n\t\tinfo = &beamtypes[BT_H2CHAIN];\n\t\tbreak;\n\tdefault:\n\t\tCon_Printf(\"CL_ParseStream: type %i\\n\", type);\n\t\tinfo = &beamtypes[BT_H2LIGHTNING];\n\t\tbreak;\n\t}\n\n\tb = CL_NewBeam(ent, tag, info);\n\tif (!b)\n\t{\n\t\tCon_Printf (\"beam list overflow!\\n\");\n\t\treturn;\n\t}\n\t\t\n\tb->rflags = RF_NOSHADOW;\n\tb->entity = ent;\n\tb->tag = tag;\n\tb->bflags = flags;\n\tb->endtime = cl.time + duration;\n\tb->alpha = 1;\n\tb->skin = skin;\n\tVectorCopy (start, b->start);\n\tVectorCopy (end, b->end);\n\n\tif (b->bflags & STREAM_ATTACHED)\n\t{\n\t\tfloat *entorg = CL_FindLatestEntityOrigin(ent);\n\t\tif (!entorg)\n\t\t\tb->bflags &= ~STREAM_ATTACHED;\t//not found, attached isn't valid.\n\t\telse\n\t\t{\n\t\t\tVectorSubtract(b->start, entorg, b->offset);\n\t\t}\n\t}\n\n\t//special handling...\n\tif (info->partner && info->ef_beam == P_INVALID)\n\t{\n\t\tb2 = CL_NewBeam(ent, tag+128, info->partner);\n\t\tif (b2)\n\t\t{\n\t\t\tP_DelinkTrailstate(&b2->trailstate);\n\t\t\tP_DelinkTrailstate(&b2->emitstate);\n\t\t\tmemcpy(b2, b, sizeof(*b2));\n\t\t\tb2->trailstate = b2->emitstate = 0;\n\t\t\tb2->info = info->partner;\n\t\t\tb2->tag = tag+128;\n\t\t\tb2->trailstate = trailkey_null;\n\t\t\tb2->emitstate = trailkey_null;\n\t\t\tb2->alpha = 0.5;\n\t\t\tb2->rflags = RF_TRANSLUCENT|RF_NOSHADOW;\n\t\t}\n\t}\n}\n\n/*\n=================\nCL_ParseTEnt\n=================\n*/\n\n#ifdef NQPROT\nvoid CL_ParseTEnt (qboolean nqprot)\n#else\nvoid CL_ParseTEnt (void)\n#endif\n{\n#ifndef NQPROT\n#define nqprot false\t//it's easier\n#endif\n\tint\t\ttype;\n\tvec3_t\tpos, pos2;\n\tdlight_t\t*dl;\n\tint\t\trnd;\n//\texplosion_t\t*ex;\n\tint\t\tcnt, colour;\n\n#ifdef CSQC_DAT\n\t//I know I'm going to regret this.\n\tif (CSQC_ParseTempEntity())\n\t\treturn;\n#endif\n\n\ttype = MSG_ReadByte ();\n\n\tif (nqprot)\n\t{\n\t\t//easiest way to handle these\n\t\t//should probably also do qwgunshot ones with nq protocols or something\n\t\tswitch(type)\n\t\t{\n\t\tcase TENQ_EXPLOSION2:\n\t\t\ttype = TEQW_EXPLOSION2;\n\t\t\tbreak;\n\t\tcase TENQ_BEAM:\n\t\t\ttype = TEQW_BEAM;\n\t\t\tbreak;\n\t\tcase TENQ_QWEXPLOSION:\n\t\t\ttype = TEQW_QWEXPLOSION;\n\t\t\tbreak;\n\t\tcase TENQ_NQEXPLOSION:\n\t\t\ttype = TEQW_NQEXPLOSION;\n\t\t\tbreak;\n\t\tcase TENQ_NQGUNSHOT:\n\t\t\ttype = TEQW_NQGUNSHOT;\n\t\t\tbreak;\n\t\tcase TENQ_QWGUNSHOT:\n\t\t\ttype = TEQW_QWGUNSHOT;\n\t\t\tbreak;\n\t\tcase TENQ_RAILTRAIL:\n\t\t\ttype = TEQW_RAILTRAIL;\n\t\t\tbreak;\n\t\tcase TENQ_NEHLIGHTNING4:\n\t\t\ttype = TEQW_NEHLIGHTNING4;\n\t\t\tbreak;\n//\t\tcase TENQ_NEHSMOKE:\n//\t\t\ttype = TEQW_NEHSMOKE;\n//\t\t\tbreak;\n\n\t\tdefault:\n\t\t\tbreak;\n\t\t}\n\t}\n\t//else QW values\n\n\t//right, nq vs qw doesn't matter now, supposedly.\n\t\n\tif (cl_shownet.ival >= 2)\n\t{\n\t\tstatic char *te_names[] = {\n\t\t\t/* 0*/\"spike\", \"superspike\", \"qwgunshot\", \"qwexplosion\",\n\t\t\t/* 4*/\"tarexplosion\", \"lightning1\", \"lightning2\", \"wizspike\",\n\t\t\t/* 8*/\"knightspike\", \"lightning3\", \"lavasplash\", \"teleport\",\n\t\t\t/*12*/\"blood\", \"lightningblood\", \"bullet\", \"superbullet\",\t//bullets deprecated\n\t\t\t/*16*/\"neh_explosion3\", \"railtrail\", \"beam\", \"explosion2\",\n\t\t\t/*20*/\"nqexplosion\", \"nqgunshot\", NULL, NULL,\n\t\t\t/*24*/\"h2lightsml\", \"h2chain\", \"h2sunstf1\", \"h2sunstf2\",\n\t\t\t/*28*/\"h2light\", \"h2cb\", \"h2ic\", \"h2gaze\",\n\t\t\t/*32*/\"h2famine\", \"h2partexp\",NULL,NULL,\n\t\t\t/*36*/NULL,NULL,NULL,NULL,\n\t\t\t/*40*/NULL,NULL,NULL,NULL,\n\t\t\t/*44*/NULL,NULL,NULL,NULL,\n\t\t\t/*48*/NULL,NULL,\"dpblood\",\"dpspark\"\n\t\t\t/*52*/\"dpbloodshower\",\"dpexplosionrgb\",\"dpparticlecube\",\"dpparticlerain\",\n\t\t\t/*56*/\"dpparticlesnow\",\"dpgunshotquad\",\"dpspikequad\",\"dpsuperspikequad\",\n\t\t\t/*60*/NULL,NULL,NULL,NULL,\n\t\t\t/*64*/NULL,NULL,NULL,NULL,\n\t\t\t/*68*/NULL,NULL,\"dpexplosionquad\",NULL,\n\t\t\t/*72*/\"dpsmallflash\",\"dpcustomflash\",\"dpflamejet\",\"dpplasmaburn\",\n\t\t\t/*76*/\"dpteig3\",\"dpsmoke\",\"dpteibigexplosion\",\"dpteiplasmahit\",\n\t\t\t/*80*/\n\t\t};\n\t\tif (type < countof(te_names) && te_names[type])\n\t\t\tCon_Printf(\"  te_%s\\n\", te_names[type]);\n\t\telse\n\t\t\tCon_Printf(\"  te_unknown_%i\\n\", type);\n\t}\n\n\tswitch (type)\n\t{\n\tcase TE_WIZSPIKE:\t\t\t// spike hitting wall\n\t\tpos[0] = MSG_ReadCoord ();\n\t\tpos[1] = MSG_ReadCoord ();\n\t\tpos[2] = MSG_ReadCoord ();\n\n\t\tif (cl_legacystains.ival) Surf_AddStain(pos, -10, 0, -10, 20);\n\n\t\tif (P_RunParticleEffectType(pos, NULL, 1, pt_wizspike))\n\t\t\tP_RunParticleEffect (pos, vec3_origin, 20, 30);\n\n\t\tS_StartSound (0, 0, cl_sfx_wizhit, pos, NULL, 1, 1, 0, 0, 0);\n\t\tbreak;\n\n\tcase TE_KNIGHTSPIKE:\t\t\t// spike hitting wall\n\t\tpos[0] = MSG_ReadCoord ();\n\t\tpos[1] = MSG_ReadCoord ();\n\t\tpos[2] = MSG_ReadCoord ();\n\n\t\tif (cl_legacystains.ival) Surf_AddStain(pos, -10, -10, -10, 20);\n\n\t\tif (P_RunParticleEffectType(pos, NULL, 1, pt_knightspike))\n\t\t\tP_RunParticleEffect (pos, vec3_origin, 226, 20);\n\n\t\tS_StartSound (0, 0, cl_sfx_knighthit, pos, NULL, 1, 1, 0, 0, 0);\n\t\tbreak;\n\n\tcase TEDP_SPIKEQUAD:\n\t\tpos[0] = MSG_ReadCoord ();\n\t\tpos[1] = MSG_ReadCoord ();\n\t\tpos[2] = MSG_ReadCoord ();\n\n\t\tif (cl_legacystains.ival) Surf_AddStain(pos, -10, -10, -10, 20);\n\n\t\tif (P_RunParticleEffectType(pos, NULL, 1, ptdp_spikequad))\n\t\t\tif (P_RunParticleEffectType(pos, NULL, 1, pt_spike))\n\t\t\t\tif (P_RunParticleEffectType(pos, NULL, 10, pt_gunshot))\n\t\t\t\t\tP_RunParticleEffect (pos, vec3_origin, 0, 10);\n\n\t\tif ( rand() % 5 )\n\t\t\tS_StartSound (0, 0, cl_sfx_tink1, pos, NULL, 1, 1, 0, 0, 0);\n\t\telse\n\t\t{\n\t\t\trnd = rand() & 3;\n\t\t\tif (rnd == 1)\n\t\t\t\tS_StartSound (0, 0, cl_sfx_ric1, pos, NULL, 1, 1, 0, 0, 0);\n\t\t\telse if (rnd == 2)\n\t\t\t\tS_StartSound (0, 0, cl_sfx_ric2, pos, NULL, 1, 1, 0, 0, 0);\n\t\t\telse\n\t\t\t\tS_StartSound (0, 0, cl_sfx_ric3, pos, NULL, 1, 1, 0, 0, 0);\n\t\t}\n\t\tbreak;\n\tcase TE_SPIKE:\t\t\t// spike hitting wall\n\t\tpos[0] = MSG_ReadCoord ();\n\t\tpos[1] = MSG_ReadCoord ();\n\t\tpos[2] = MSG_ReadCoord ();\n\n\t\tif (cl_legacystains.ival) Surf_AddStain(pos, -10, -10, -10, 20);\n\n\t\tif (P_RunParticleEffectType(pos, NULL, 1, pt_spike))\n\t\t\tif (P_RunParticleEffectType(pos, NULL, 10, pt_gunshot))\n\t\t\t\tP_RunParticleEffect (pos, vec3_origin, 0, 10);\n\n\t\tif ( rand() % 5 )\n\t\t\tS_StartSound (0, 0, cl_sfx_tink1, pos, NULL, 1, 1, 0, 0, 0);\n\t\telse\n\t\t{\n\t\t\trnd = rand() & 3;\n\t\t\tif (rnd == 1)\n\t\t\t\tS_StartSound (0, 0, cl_sfx_ric1, pos, NULL, 1, 1, 0, 0, 0);\n\t\t\telse if (rnd == 2)\n\t\t\t\tS_StartSound (0, 0, cl_sfx_ric2, pos, NULL, 1, 1, 0, 0, 0);\n\t\t\telse\n\t\t\t\tS_StartSound (0, 0, cl_sfx_ric3, pos, NULL, 1, 1, 0, 0, 0);\n\t\t}\n\t\tbreak;\n\tcase TEDP_SUPERSPIKEQUAD:\t\t\t// super spike hitting wall\n\t\tpos[0] = MSG_ReadCoord ();\n\t\tpos[1] = MSG_ReadCoord ();\n\t\tpos[2] = MSG_ReadCoord ();\n\n\t\tif (cl_legacystains.ival) Surf_AddStain(pos, -10, -10, -10, 20);\n\n\t\tif (P_RunParticleEffectType(pos, NULL, 1, ptdp_superspikequad))\n\t\t\tif (P_RunParticleEffectType(pos, NULL, 1, pt_superspike))\n\t\t\t\tif (P_RunParticleEffectType(pos, NULL, 2, pt_spike))\n\t\t\t\t\tif (P_RunParticleEffectType(pos, NULL, 20, pt_gunshot))\n\t\t\t\t\t\tP_RunParticleEffect (pos, vec3_origin, 0, 20);\n\n\t\tif ( rand() % 5 )\n\t\t\tS_StartSound (0, 0, cl_sfx_tink1, pos, NULL, 1, 1, 0, 0, 0);\n\t\telse\n\t\t{\n\t\t\trnd = rand() & 3;\n\t\t\tif (rnd == 1)\n\t\t\t\tS_StartSound (0, 0, cl_sfx_ric1, pos, NULL, 1, 1, 0, 0, 0);\n\t\t\telse if (rnd == 2)\n\t\t\t\tS_StartSound (0, 0, cl_sfx_ric2, pos, NULL, 1, 1, 0, 0, 0);\n\t\t\telse\n\t\t\t\tS_StartSound (0, 0, cl_sfx_ric3, pos, NULL, 1, 1, 0, 0, 0);\n\t\t}\n\t\tbreak;\n\tcase TE_SUPERSPIKE:\t\t\t// super spike hitting wall\n\t\tpos[0] = MSG_ReadCoord ();\n\t\tpos[1] = MSG_ReadCoord ();\n\t\tpos[2] = MSG_ReadCoord ();\n\n\t\tif (cl_legacystains.ival) Surf_AddStain(pos, -10, -10, -10, 20);\n\n\t\tif (P_RunParticleEffectType(pos, NULL, 1, pt_superspike))\n\t\t\tif (P_RunParticleEffectType(pos, NULL, 2, pt_spike))\n\t\t\t\tif (P_RunParticleEffectType(pos, NULL, 20, pt_gunshot))\n\t\t\t\t\tP_RunParticleEffect (pos, vec3_origin, 0, 20);\n\n\t\tif ( rand() % 5 )\n\t\t\tS_StartSound (0, 0, cl_sfx_tink1, pos, NULL, 1, 1, 0, 0, 0);\n\t\telse\n\t\t{\n\t\t\trnd = rand() & 3;\n\t\t\tif (rnd == 1)\n\t\t\t\tS_StartSound (0, 0, cl_sfx_ric1, pos, NULL, 1, 1, 0, 0, 0);\n\t\t\telse if (rnd == 2)\n\t\t\t\tS_StartSound (0, 0, cl_sfx_ric2, pos, NULL, 1, 1, 0, 0, 0);\n\t\t\telse\n\t\t\t\tS_StartSound (0, 0, cl_sfx_ric3, pos, NULL, 1, 1, 0, 0, 0);\n\t\t}\n\t\tbreak;\n\n#ifdef PEXT_TE_BULLET\n\tcase TE_BULLET:\n\t\tif (!(cls.fteprotocolextensions & PEXT_TE_BULLET))\n\t\t\tHost_EndGame(\"Thought PEXT_TE_BULLET was disabled\");\n\t\tpos[0] = MSG_ReadCoord ();\n\t\tpos[1] = MSG_ReadCoord ();\n\t\tpos[2] = MSG_ReadCoord ();\n\n\t\tif (cl_legacystains.ival) Surf_AddStain(pos, -10, -10, -10, 20);\n\n\t\tif (P_RunParticleEffectType(pos, NULL, 1, ptfte_bullet))\n\t\t\tif (P_RunParticleEffectType(pos, NULL, 10, pt_gunshot))\n\t\t\t\tP_RunParticleEffect (pos, vec3_origin, 0, 10);\n\n\t\tif ( rand() % 5 )\n\t\t\tS_StartSound (0, 0, cl_sfx_tink1, pos, NULL, 1, 1, 0, 0, 0);\n\t\telse\n\t\t{\n\t\t\trnd = rand() & 3;\n\t\t\tif (rnd == 1)\n\t\t\t\tS_StartSound (0, 0, cl_sfx_ric1, pos, NULL, 1, 1, 0, 0, 0);\n\t\t\telse if (rnd == 2)\n\t\t\t\tS_StartSound (0, 0, cl_sfx_ric2, pos, NULL, 1, 1, 0, 0, 0);\n\t\t\telse\n\t\t\t\tS_StartSound (0, 0, cl_sfx_ric3, pos, NULL, 1, 1, 0, 0, 0);\n\t\t}\n\t\tbreak;\n\tcase TEQW_SUPERBULLET:\n\t\tpos[0] = MSG_ReadCoord ();\n\t\tpos[1] = MSG_ReadCoord ();\n\t\tpos[2] = MSG_ReadCoord ();\n\n\t\tif (cl_legacystains.ival) Surf_AddStain(pos, -10, -10, -10, 20);\n\n\t\tif (P_RunParticleEffectType(pos, NULL, 1, ptfte_superbullet))\n\t\t\tif (P_RunParticleEffectType(pos, NULL, 2, ptfte_bullet))\n\t\t\t\tif (P_RunParticleEffectType(pos, NULL, 20, pt_gunshot))\n\t\t\t\t\tP_RunParticleEffect (pos, vec3_origin, 0, 20);\n\n\t\tif ( rand() % 5 )\n\t\t\tS_StartSound (0, 0, cl_sfx_tink1, pos, NULL, 1, 1, 0, 0, 0);\n\t\telse\n\t\t{\n\t\t\trnd = rand() & 3;\n\t\t\tif (rnd == 1)\n\t\t\t\tS_StartSound (0, 0, cl_sfx_ric1, pos, NULL, 1, 1, 0, 0, 0);\n\t\t\telse if (rnd == 2)\n\t\t\t\tS_StartSound (0, 0, cl_sfx_ric2, pos, NULL, 1, 1, 0, 0, 0);\n\t\t\telse\n\t\t\t\tS_StartSound (0, 0, cl_sfx_ric3, pos, NULL, 1, 1, 0, 0, 0);\n\t\t}\n\t\tbreak;\n#endif\n\n\tcase TEDP_EXPLOSIONQUAD:\t\t\t// rocket explosion\n\t// particles\n\t\tpos[0] = MSG_ReadCoord ();\n\t\tpos[1] = MSG_ReadCoord ();\n\t\tpos[2] = MSG_ReadCoord ();\n\t\tif (P_RunParticleEffectType(pos, NULL, 1, ptdp_explosionquad))\n\t\t\tif (P_RunParticleEffectType(pos, NULL, 1, pt_explosion))\n\t\t\t\tP_RunParticleEffect(pos, NULL, 107, 1024); // should be 97-111\n\n\t\tif (cl_legacystains.ival) Surf_AddStain(pos, -1, -1, -1, 100);\n\n\t// light\n\t\tif (r_explosionlight.value)\n\t\t{\n\t\t\tdl = CL_AllocDlight (0);\n\t\t\tVectorCopy (pos, dl->origin);\n\t\t\tdl->radius = 150 + r_explosionlight.value*200;\n\t\t\tdl->die = cl.time + 1;\n\t\t\tdl->decay = 300;\n\n\t\t\tdl->color[0] = 4.0;\n\t\t\tdl->color[1] = 2.0;\n\t\t\tdl->color[2] = 0.5;\n\t\t\tdl->channelfade[0] = 0.196;\n\t\t\tdl->channelfade[1] = 0.23;\n\t\t\tdl->channelfade[2] = 0.12;\n\t\t}\n\n\n\t// sound\n\t\tS_StartSound (0, 0, cl_sfx_r_exp3, pos, NULL, 1, 1, 0, 0, 0);\n\n\t// sprite\n\t\tif (cl_expsprite.ival) // temp hopefully\n\t\t{\n\t\t\texplosion_t *ex = CL_AllocExplosion (pos);\n\t\t\tex->start = cl.time;\n\t\t\tex->model = Mod_ForName (\"progs/s_explod.spr\", MLV_WARN);\n\t\t\tex->endalpha = ex->startalpha;\t//don't fade out\n\t\t}\n\t\tbreak;\n\tcase TEQW_NQEXPLOSION:\t//nq-style, no sprite\n\tcase TEQW_QWEXPLOSION:\t\t\t\t//qw-style, with (optional) sprite\n\t// particles\n\t\tpos[0] = MSG_ReadCoord ();\n\t\tpos[1] = MSG_ReadCoord ();\n\t\tpos[2] = MSG_ReadCoord ();\n\t\tif (P_RunParticleEffectType(pos, NULL, 1, pt_explosion))\n\t\t\tP_RunParticleEffect(pos, NULL, 107, 1024); // should be 97-111\n\n\t\tif (cl_legacystains.ival) Surf_AddStain(pos, -1, -1, -1, 100);\n\n\t// light\n\t\tif (r_explosionlight.value)\n\t\t{\n\t\t\tdl = CL_AllocDlight (0);\n\t\t\tVectorCopy (pos, dl->origin);\n\t\t\tdl->radius = 150 + r_explosionlight.value*200;\n\t\t\tdl->die = cl.time + 0.75;\n\t\t\tdl->decay = dl->radius*2;\n\n\t\t\tVectorCopy(r_explosionlight_colour.vec4, dl->color);\n\t\t\tVectorCopy(r_explosionlight_fade.vec4, dl->channelfade);\n\t\t}\n\n\n\t// sound\n\t\tS_StartSound (0, 0, cl_sfx_r_exp3, pos, NULL, 1, 1, 0, 0, 0);\n\n\t// sprite\n\t\tif (type == TEQW_QWEXPLOSION && cl_expsprite.ival) // temp hopefully\n\t\t{\n\t\t\texplosion_t *ex = CL_AllocExplosion (pos);\n\t\t\tex->start = cl.time;\n\t\t\tex->model = Mod_ForName (\"progs/s_explod.spr\", MLV_WARN);\n\t\t\tex->endalpha = ex->startalpha;\t//don't fade out\n\t\t}\n\t\tbreak;\n\n\tcase TEQW_EXPLOSION2:\n\t\t{\n\t\t\tint colorStart;\n\t\t\tint colorLength;\n\t\t\tint ef;\n\t\t\tpos[0] = MSG_ReadCoord ();\n\t\t\tpos[1] = MSG_ReadCoord ();\n\t\t\tpos[2] = MSG_ReadCoord ();\n\t\t\tcolorStart = MSG_ReadByte ();\n\t\t\tcolorLength = MSG_ReadByte ();\n\n\t\t\tef = P_FindParticleType(va(\"TE_EXPLOSION2_%i_%i\", colorStart, colorLength));\n\t\t\tif (ef == P_INVALID)\n\t\t\t\tef = pt_explosion;\n\t\t\tP_RunParticleEffectType(pos, NULL, 1, ef);\n\t\t\tif (r_explosionlight.value)\n\t\t\t{\n\t\t\t\tdl = CL_AllocDlight (0);\n\t\t\t\tVectorCopy (pos, dl->origin);\n\t\t\t\tdl->radius = 350;\n\t\t\t\tdl->die = cl.time + 0.5;\n\t\t\t\tdl->decay = 300;\n\t\t\t}\n\t\t\tS_StartSound (0, 0, cl_sfx_r_exp3, pos, NULL, 1, 1, 0, 0, 0);\n\t\t}\n\t\tbreak;\n\n\tcase TE_EXPLOSION3_NEH:\n\tcase TEDP_EXPLOSIONRGB:\n\t\tpos[0] = MSG_ReadCoord ();\n\t\tpos[1] = MSG_ReadCoord ();\n\t\tpos[2] = MSG_ReadCoord ();\n\t\tif (P_RunParticleEffectType(pos, NULL, 1, pt_explosion))\n\t\t\tP_RunParticleEffect(pos, NULL, 107, 1024); // should be 97-111\n\n\t\tif (cl_legacystains.ival) Surf_AddStain(pos, -1, -1, -1, 100);\n\n\t\tif (type == TEDP_EXPLOSIONRGB)\n\t\t{\n\t\t\tpos2[0] = MSG_ReadByte()/255.0;\n\t\t\tpos2[1] = MSG_ReadByte()/255.0;\n\t\t\tpos2[2] = MSG_ReadByte()/255.0;\n\t\t}\n\t\telse\n\t\t{\t//TE_EXPLOSION3_NEH\n\t\t\tpos2[0] = MSG_ReadCoord();\n\t\t\tpos2[1] = MSG_ReadCoord();\n\t\t\tpos2[2] = MSG_ReadCoord();\n\t\t}\n\n\t// light\n\t\tif (r_explosionlight.value)\n\t\t{\n\t\t\tdl = CL_AllocDlight (0);\n\t\t\tVectorCopy (pos, dl->origin);\n\t\t\tdl->radius = 150 + r_explosionlight.value*200;\n\t\t\tdl->die = cl.time + 0.5;\n\t\t\tdl->decay = 300;\n\n\t\t\tdl->color[0] = 0.4f*pos2[0];\n\t\t\tdl->color[1] = 0.4f*pos2[1];\n\t\t\tdl->color[2] = 0.4f*pos2[2];\n\t\t\tdl->channelfade[0] = 0;\n\t\t\tdl->channelfade[1] = 0;\n\t\t\tdl->channelfade[2] = 0;\n\t\t}\n\n\t\tS_StartSound (0, 0, cl_sfx_r_exp3, pos, NULL, 1, 1, 0, 0, 0);\n\t\tbreak;\n\n\tcase TEDP_TEI_BIGEXPLOSION:\n\t\tpos[0] = MSG_ReadCoord ();\n\t\tpos[1] = MSG_ReadCoord ();\n\t\tpos[2] = MSG_ReadCoord ();\n\t\tif (P_RunParticleEffectType(pos, NULL, 1, ptdp_tei_bigexplosion))\n\t\t\tif (P_RunParticleEffectType(pos, NULL, 1, pt_explosion))\n\t\t\t\tP_RunParticleEffect(pos, NULL, 107, 1024); // should be 97-111\n\n\t\tif (cl_legacystains.ival) Surf_AddStain(pos, -1, -1, -1, 100);\n\n\t// light\n\t\tif (r_explosionlight.value)\n\t\t{\n\t\t\tdl = CL_AllocDlight (0);\n\t\t\tVectorCopy (pos, dl->origin);\n\t\t\t// no point in doing this the fuh/ez way\n\t\t\tdl->radius = 500*r_explosionlight.value;\n\t\t\tdl->die = cl.time + 1;\n\t\t\tdl->decay = 500;\n\n\t\t\tdl->color[0] = 2.0f;\n\t\t\tdl->color[1] = 1.5f;\n\t\t\tdl->color[2] = 0.75f;\n\t\t\tdl->channelfade[0] = 0;\n\t\t\tdl->channelfade[1] = 0;\n\t\t\tdl->channelfade[2] = 0;\n\t\t}\n\n\t\tS_StartSound (0, 0, cl_sfx_r_exp3, pos, NULL, 1, 1, 0, 0, 0);\n\t\tbreak;\n\n\tcase TE_TAREXPLOSION:\t\t\t// tarbaby explosion\n\t\tpos[0] = MSG_ReadCoord ();\n\t\tpos[1] = MSG_ReadCoord ();\n\t\tpos[2] = MSG_ReadCoord ();\n\t\tP_RunParticleEffectType(pos, NULL, 1, pt_tarexplosion);\n\n\t\tS_StartSound (0, 0, cl_sfx_r_exp3, pos, NULL, 1, 1, 0, 0, 0);\n\t\tbreak;\n\n\tcase TE_LIGHTNING1:\t\t\t\t// lightning bolts\n\t\tCL_ParseBeam (BT_Q1LIGHTNING1);\n\t\tbreak;\n\tcase TE_LIGHTNING2:\t\t\t\t// lightning bolts\n\t\tCL_ParseBeam (BT_Q1LIGHTNING2);\n\t\tbreak;\n\tcase TE_LIGHTNING3:\t\t\t\t// lightning bolts\n\t\tCL_ParseBeam (BT_Q1LIGHTNING3);\n\t\tbreak;\n\tcase TEQW_NEHLIGHTNING4:\n\t\tCon_DPrintf(\"TEQW_NEHLIGHTNING4 not implemented\\n\");\n\t\tMSG_ReadString();\n\t\tCL_ParseBeam (BT_Q1LIGHTNING2);\n\t\tbreak;\n\n\tcase TE_LAVASPLASH:\n\t\tpos[0] = MSG_ReadCoord ();\n\t\tpos[1] = MSG_ReadCoord ();\n\t\tpos[2] = MSG_ReadCoord ();\n\t\tP_RunParticleEffectType(pos, NULL, 1, pt_lavasplash);\n\t\tbreak;\n\n\tcase TE_TELEPORT:\n\t\tpos[0] = MSG_ReadCoord ();\n\t\tpos[1] = MSG_ReadCoord ();\n\t\tpos[2] = MSG_ReadCoord ();\n\t\tP_RunParticleEffectType(pos, NULL, 1, pt_teleportsplash);\n\t\tbreak;\n\n\tcase TEDP_GUNSHOTQUAD:\t\t\t// bullet hitting wall\n\t\tpos[0] = MSG_ReadCoord ();\n\t\tpos[1] = MSG_ReadCoord ();\n\t\tpos[2] = MSG_ReadCoord ();\n\n\t\tif (cl_legacystains.ival) Surf_AddStain(pos, -10, -10, -10, 20);\n\n\t\tif (P_RunParticleEffectType(pos, NULL, 1, ptdp_gunshotquad))\n\t\t\tif (P_RunParticleEffectType(pos, NULL, 1, pt_gunshot))\n\t\t\t\tP_RunParticleEffect (pos, vec3_origin, 0, 20);\n\n\t\tbreak;\n\n\tcase TEQW_QWGUNSHOT:\t\t\t// bullet hitting wall\n\tcase TEQW_NQGUNSHOT:\n\t\tif (type == TEQW_NQGUNSHOT)\n\t\t\tcnt = 1;\n\t\telse\n\t\t\tcnt = MSG_ReadByte ();\n\t\tpos[0] = MSG_ReadCoord ();\n\t\tpos[1] = MSG_ReadCoord ();\n\t\tpos[2] = MSG_ReadCoord ();\n\n\t\tif (cl_legacystains.ival) Surf_AddStain(pos, -10, -10, -10, 20);\n\n\t\tif (P_RunParticleEffectType(pos, NULL, cnt, pt_gunshot))\n\t\t\tP_RunParticleEffect (pos, vec3_origin, 0, 20*cnt);\n\n\t\tbreak;\n\n\tcase TEQW_QWBLOOD:\t\t\t\t// bullets hitting body\n\t\tcnt = MSG_ReadByte ();\n\t\tpos[0] = MSG_ReadCoord ();\n\t\tpos[1] = MSG_ReadCoord ();\n\t\tpos[2] = MSG_ReadCoord ();\n\n\t\tif (cl_legacystains.ival) Surf_AddStain(pos, 0, -10, -10, 40);\n\n\t\tif (P_RunParticleEffectType(pos, NULL, cnt, ptqw_blood))\n\t\t\tif (P_RunParticleEffectType(pos, NULL, cnt, ptdp_blood))\n\t\t\t\tP_RunParticleEffect (pos, vec3_origin, 73, 20*cnt);\n\n\t\tbreak;\n\n\tcase TEQW_LIGHTNINGBLOOD:\t\t// lightning hitting body\n\t\tpos[0] = MSG_ReadCoord ();\n\t\tpos[1] = MSG_ReadCoord ();\n\t\tpos[2] = MSG_ReadCoord ();\n\n\t\tif (cl_legacystains.ival) Surf_AddStain(pos, 1, -10, -10, 20);\n\n\t\tif (P_RunParticleEffectType(pos, NULL, 1, ptqw_lightningblood))\n\t\t\tP_RunParticleEffect (pos, vec3_origin, 225, 50);\n\n\t\tbreak;\n\n\tcase TEQW_BEAM:\n\t\tCL_ParseBeam (BT_Q1BEAM);\n\t\tbreak;\n\n\tcase TEQW_RAILTRAIL:\n\t\tpos[0] = MSG_ReadCoord ();\n\t\tpos[1] = MSG_ReadCoord ();\n\t\tpos[2] = MSG_ReadCoord ();\n\n\t\tpos2[0] = MSG_ReadCoord ();\n\t\tpos2[1] = MSG_ReadCoord ();\n\t\tpos2[2] = MSG_ReadCoord ();\n\n\t\tif (P_ParticleTrail(pos, pos2, rtqw_railtrail, 1, 0, NULL, NULL))\n\t\t\tP_ParticleTrailIndex(pos, pos2, P_INVALID, 1, 208, 8, NULL);\n\t\tbreak;\n\n\tcase TEH2_STREAM_LIGHTNING_SMALL:\n\tcase TEH2_STREAM_CHAIN:\n\tcase TEH2_STREAM_SUNSTAFF1:\n\tcase TEH2_STREAM_SUNSTAFF2:\n\tcase TEH2_STREAM_LIGHTNING:\n\tcase TEH2_STREAM_COLORBEAM:\n\tcase TEH2_STREAM_ICECHUNKS:\n\tcase TEH2_STREAM_GAZE:\n\tcase TEH2_STREAM_FAMINE:\n\t\tCL_ParseStream (type);\n\t\tbreak;\n\n\tcase TEDP_BLOOD:\n\t\tpos[0] = MSG_ReadCoord ();\n\t\tpos[1] = MSG_ReadCoord ();\n\t\tpos[2] = MSG_ReadCoord ();\n\n\t\tpos2[0] = MSG_ReadChar ();\n\t\tpos2[1] = MSG_ReadChar ();\n\t\tpos2[2] = MSG_ReadChar ();\n\n\t\tcnt = MSG_ReadByte ();\n\n\t\tP_RunParticleEffectType(pos, pos2, cnt, ptdp_blood);\n\t\tbreak;\n\n\tcase TEDP_SPARK:\n\t\tpos[0] = MSG_ReadCoord ();\t//org\n\t\tpos[1] = MSG_ReadCoord ();\n\t\tpos[2] = MSG_ReadCoord ();\n\n\t\tpos2[0] = MSG_ReadChar ();\t//vel\n\t\tpos2[1] = MSG_ReadChar ();\n\t\tpos2[2] = MSG_ReadChar ();\n\n\t\tcnt = MSG_ReadByte ();\n\t\t{\n\t\t\tP_RunParticleEffectType(pos, pos2, cnt, ptdp_spark);\n\t\t}\n\t\tbreak;\n\n\tcase TEDP_BLOODSHOWER:\n\t\t{\n\t\t\tvec3_t vel = {0,0,0};\n\t\t\tpos[0] = MSG_ReadCoord ();\n\t\t\tpos[1] = MSG_ReadCoord ();\n\t\t\tpos[2] = MSG_ReadCoord ();\n\n\t\t\tpos2[0] = MSG_ReadCoord ();\n\t\t\tpos2[1] = MSG_ReadCoord ();\n\t\t\tpos2[2] = MSG_ReadCoord ();\n\n\t\t\tvel[2] = -MSG_ReadCoord ();\n\n\t\t\tcnt = MSG_ReadShort ();\n\n\t\t\tP_RunParticleCube(P_FindParticleType(\"te_bloodshower\"), pos, pos2, vel, vel, cnt, 0, false, 0);\n\t\t}\n\t\tbreak;\n\n\tcase TEDP_SMALLFLASH:\n\t\tpos[0] = MSG_ReadCoord ();\n\t\tpos[1] = MSG_ReadCoord ();\n\t\tpos[2] = MSG_ReadCoord ();\n\n\t\t// light\n\t\tdl = CL_AllocDlight (0);\n\t\tVectorCopy (pos, dl->origin);\n\t\tdl->radius = 200;\n\t\tdl->decay = 1000;\n\t\tdl->die = cl.time + 0.2;\n\t\tdl->color[0] = 2.0;\n\t\tdl->color[1] = 2.0;\n\t\tdl->color[2] = 2.0;\n\t\tbreak;\n\n\tcase TEDP_CUSTOMFLASH:\n\t\tpos[0] = MSG_ReadCoord ();\n\t\tpos[1] = MSG_ReadCoord ();\n\t\tpos[2] = MSG_ReadCoord ();\n\n\t\t\t// light\n\t\tdl = CL_AllocDlight (0);\n\t\tVectorCopy (pos, dl->origin);\n\t\tdl->radius = MSG_ReadByte()*8;\n\t\tpos2[0] = (MSG_ReadByte() + 1) * (1.0 / 256.0);\n\t\tdl->die = cl.time + pos2[0];\n\t\tdl->decay = dl->radius / pos2[0];\n\n\t\tdl->color[0] = MSG_ReadByte()*(1.0f/127.0f);\n\t\tdl->color[1] = MSG_ReadByte()*(1.0f/127.5f);\n\t\tdl->color[2] = MSG_ReadByte()*(1.0f/127.0f);\n\n\t\tbreak;\n\n\tcase TEDP_FLAMEJET:\n\t\t// origin\n\t\tpos[0] = MSG_ReadCoord ();\n\t\tpos[1] = MSG_ReadCoord ();\n\t\tpos[2] = MSG_ReadCoord ();\n\n\t\t// velocity\n\t\tpos2[0] = MSG_ReadCoord ();\n\t\tpos2[1] = MSG_ReadCoord ();\n\t\tpos2[2] = MSG_ReadCoord ();\n\n\t\t// count\n\t\tcnt = MSG_ReadByte ();\n\n\t\tif (P_RunParticleEffectType(pos, pos2, cnt, ptdp_flamejet))\n\t\t\tP_RunParticleEffect (pos, pos2, 232, cnt);\n\t\tbreak;\n\n\tcase TEDP_PLASMABURN:\n\t\t// origin\n\t\tpos[0] = MSG_ReadCoord ();\n\t\tpos[1] = MSG_ReadCoord ();\n\t\tpos[2] = MSG_ReadCoord ();\n\n\t\t// stain (Hopefully this is close to how DP does it)\n\t\tif (cl_legacystains.ival) Surf_AddStain(pos, -10, -10, -10, 30);\n\n\t\tif (P_RunParticleEffectType(pos, NULL, 1, P_FindParticleType(\"te_plasmaburn\")))\n\t\t\tP_RunParticleEffect(pos, vec3_origin, 15, 50);\n\t\tbreak;\n\n\tcase TEDP_TEI_G3:\t//nexuiz's nex beam\n\t\tpos[0] = MSG_ReadCoord ();\n\t\tpos[1] = MSG_ReadCoord ();\n\t\tpos[2] = MSG_ReadCoord ();\n\n\t\tpos2[0] = MSG_ReadCoord ();\n\t\tpos2[1] = MSG_ReadCoord ();\n\t\tpos2[2] = MSG_ReadCoord ();\n\n\t\t//sigh...\n\t\tMSG_ReadCoord ();\n\t\tMSG_ReadCoord ();\n\t\tMSG_ReadCoord ();\n\n\t\tif (P_ParticleTrail(pos, pos2, P_FindParticleType(\"te_nexbeam\"), 1, 0, NULL, NULL))\n\t\t\tP_ParticleTrailIndex(pos, pos2, P_INVALID, 1, 15, 0, NULL);\n\t\tbreak;\n\n\tcase TEDP_SMOKE:\n\t\t//org\n\t\tpos[0] = MSG_ReadCoord ();\n\t\tpos[1] = MSG_ReadCoord ();\n\t\tpos[2] = MSG_ReadCoord ();\n\n\t\t//dir\n\t\tpos2[0] = MSG_ReadCoord ();\n\t\tpos2[1] = MSG_ReadCoord ();\n\t\tpos2[2] = MSG_ReadCoord ();\n\n\t\t//count\n\t\tcnt = MSG_ReadByte ();\n\t\t{\n\t\t\tP_RunParticleEffectType(pos, pos2, cnt, ptdp_tei_smoke);\n\t\t}\n\t\tbreak;\n\n\tcase TEDP_TEI_PLASMAHIT:\n\t\tpos[0] = MSG_ReadCoord ();\n\t\tpos[1] = MSG_ReadCoord ();\n\t\tpos[2] = MSG_ReadCoord ();\n\n\t\t//dir\n\t\tpos2[0] = MSG_ReadCoord ();\n\t\tpos2[1] = MSG_ReadCoord ();\n\t\tpos2[2] = MSG_ReadCoord ();\n\t\tcnt = MSG_ReadByte ();\n\n\t\t{\n\t\t\tP_RunParticleEffectType(pos, pos2, cnt, ptdp_tei_plasmahit);\n\t\t}\n\t\tbreak;\n\n\tcase TEDP_PARTICLECUBE:\n\t\t{\n\t\t\tvec3_t dir;\n\t\t\tint jitter;\n\t\t\tint gravity;\n\n\t\t\t//min\n\t\t\tpos[0] = MSG_ReadCoord();\n\t\t\tpos[1] = MSG_ReadCoord();\n\t\t\tpos[2] = MSG_ReadCoord();\n\n\t\t\t//max\n\t\t\tpos2[0] = MSG_ReadCoord();\n\t\t\tpos2[1] = MSG_ReadCoord();\n\t\t\tpos2[2] = MSG_ReadCoord();\n\n\t\t\t//dir\n\t\t\tdir[0] = MSG_ReadCoord();\n\t\t\tdir[1] = MSG_ReadCoord();\n\t\t\tdir[2] = MSG_ReadCoord();\n\n\t\t\tcnt = MSG_ReadShort();\t//count\n\t\t\tcolour = MSG_ReadByte ();\t//colour\n\t\t\tgravity = MSG_ReadByte ();\t//gravity flag\n\t\t\tjitter = MSG_ReadCoord();\t//jitter\n\n\t\t\tP_RunParticleCube(P_INVALID, pos, pos2, dir, dir, cnt, colour, gravity, jitter);\n\t\t}\n\t\tbreak;\n\tcase TEDP_PARTICLERAIN:\n\t\t{\n\t\t\tvec3_t dir;\n\n\t\t\t//min\n\t\t\tpos[0] = MSG_ReadCoord();\n\t\t\tpos[1] = MSG_ReadCoord();\n\t\t\tpos[2] = MSG_ReadCoord();\n\n\t\t\t//max\n\t\t\tpos2[0] = MSG_ReadCoord();\n\t\t\tpos2[1] = MSG_ReadCoord();\n\t\t\tpos2[2] = MSG_ReadCoord();\n\n\t\t\t//dir\n\t\t\tdir[0] = MSG_ReadCoord();\n\t\t\tdir[1] = MSG_ReadCoord();\n\t\t\tdir[2] = MSG_ReadCoord();\n\n\t\t\tcnt = (unsigned short)MSG_ReadShort();\t//count\n\t\t\tcolour = MSG_ReadByte ();\t//colour\n\n\t\t\tP_RunParticleWeather(pos, pos2, dir, cnt, colour, \"rain\");\n\t\t}\n\t\tbreak;\n\tcase TEDP_PARTICLESNOW:\n\t\t{\n\t\t\tvec3_t dir;\n\n\t\t\t//min\n\t\t\tpos[0] = MSG_ReadCoord();\n\t\t\tpos[1] = MSG_ReadCoord();\n\t\t\tpos[2] = MSG_ReadCoord();\n\n\t\t\t//max\n\t\t\tpos2[0] = MSG_ReadCoord();\n\t\t\tpos2[1] = MSG_ReadCoord();\n\t\t\tpos2[2] = MSG_ReadCoord();\n\n\t\t\t//dir\n\t\t\tdir[0] = MSG_ReadCoord();\n\t\t\tdir[1] = MSG_ReadCoord();\n\t\t\tdir[2] = MSG_ReadCoord();\n\n\t\t\tcnt = (unsigned short)MSG_ReadShort();\t//count\n\t\t\tcolour = MSG_ReadByte ();\t//colour\n\n\t\t\tP_RunParticleWeather(pos, pos2, dir, cnt, colour, \"snow\");\n\t\t}\n\t\tbreak;\n\n//\tcase TEQW_NEHRAILTRAIL:\n//\tcase TEQW_NEHEXPLOSION3:\n//\tcase TEQW_NEHLIGHTNING4:\n//\tcase TEQW_NEHSMOKE:\n\n\tdefault:\n\t\tHost_EndGame (\"CL_ParseTEnt: bad type - %i\", type);\n\t}\n}\n\nvoid CL_ParseTEnt_Sized (void)\n{\n\tunsigned short sz = MSG_ReadShort();\n\tint start = MSG_GetReadCount();\n\n\tfor(;;)\n\t{\n#ifdef NQPROT\n\t\tif (sz&0x8000)\n\t\t{\n\t\t\tsz&=~0x8000;\n\t\t\tCL_ParseTEnt(true);\n\t\t}\n\t\telse\n\t\t\tCL_ParseTEnt(false);\n#else\n\t\tCL_ParseTEnt();\n#endif\n\n\t\tif (MSG_GetReadCount() < start + sz)\n\t\t{\t//try to be more compatible with xonotic.\n\t\t\tint next = MSG_ReadByte();\n\t\t\tif (next == svc_temp_entity)\n\t\t\t\tcontinue;\n\n\t\t\tCon_Printf(\"Sized temp_entity data too large (next byte %i, %i bytes unread)\\n\", next, (start+sz)-MSG_GetReadCount()-1);\n\t\t\tMSG_ReadSkip(start+sz-MSG_GetReadCount());\n\t\t\treturn;\n\t\t}\n\t\tbreak;\n\t}\n\n\n\tif (MSG_GetReadCount() != start + sz)\n\t{\n\t\tCon_Printf(\"Tempentity size did not match parsed size misread a gamecode packet (%i bytes too much)\\n\", MSG_GetReadCount() - (start+sz));\n\t\tMSG_ReadSkip(start+sz-MSG_GetReadCount());\n\t}\n}\n\ntypedef struct {\n\tchar name[64];\n\tint netstyle;\n\tint particleeffecttype;\n\tchar stain[3];\n\tqbyte radius;\n\tvec3_t dlightrgb;\n\tfloat dlightradius;\n\tfloat dlighttime;\n\tvec3_t dlightcfade;\n} clcustomtents_t;\n\ntypedef struct custtentinst_s\n{\n\tstruct custtentinst_s *next;\n\tclcustomtents_t *type;\n\tint id;\n\tvec3_t pos;\n\tvec3_t pos2;\n\tvec3_t dir;\n\tint count;\n} custtentinst_t;\ncusttentinst_t *activepcusttents;\n\nvoid CL_SpawnCustomTEnt(custtentinst_t *info)\n{\n\tclcustomtents_t *t = info->type;\n\tqboolean failed;\n\tif (t->netstyle & CTE_ISBEAM)\n\t{\n\t\tif (t->netstyle & (CTE_CUSTOMVELOCITY|CTE_CUSTOMDIRECTION))\n\t\t{\n\t\t\tvec3_t org;\n\t\t\tint i, j;\n\t\t\t//FIXME: pvs cull\n\t\t\tif (t->particleeffecttype == -1)\n\t\t\t\tfailed = true;\n\t\t\telse\n\t\t\t{\n\t\t\t\tfailed = false;\n\t\t\t\tfor (i=0 ; i<info->count ; i++)\n\t\t\t\t{\n\t\t\t\t\tfor (j=0 ; j<3 ; j++)\n\t\t\t\t\t{\n\t\t\t\t\t\torg[j] = info->pos[j] + (info->pos2[j] - info->pos[j])*frandom();\n\t\t\t\t\t}\n\n\t\t\t\t\tfailed |= P_RunParticleEffectType(org, info->dir, 1, t->particleeffecttype);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse\n\t\t\tfailed = P_ParticleTrail(info->pos, info->pos2, t->particleeffecttype, 1, 0, NULL, NULL);\n\t}\n\telse\n\t{\n\t\tif (t->netstyle & (CTE_CUSTOMVELOCITY|CTE_CUSTOMDIRECTION))\n\t\t\tfailed = P_RunParticleEffectType(info->pos, info->dir, info->count, t->particleeffecttype);\n\t\telse\n\t\t\tfailed = P_RunParticleEffectType(info->pos, NULL, info->count, t->particleeffecttype);\n\t}\n\n\tif (failed)\n\t\tCon_DPrintf(\"Failed to create effect %s\\n\", t->name);\n\n\tif (t->netstyle & CTE_STAINS)\n\t{\t//added at pos2 - end of trail\n\t\tSurf_AddStain(info->pos2, t->stain[0], t->stain[1], t->stain[2], 40);\n\t}\n\tif (t->netstyle & CTE_GLOWS)\n\t{\t//added at pos1 firer's end.\n\t\tdlight_t\t*dl;\n\t\tdl = CL_AllocDlight (0);\n\t\tVectorCopy (info->pos, dl->origin);\n\t\tdl->radius = t->dlightradius*4;\n\t\tdl->die = cl.time + t->dlighttime;\n\t\tdl->decay = t->radius/t->dlighttime;\n\n\t\tdl->color[0] = t->dlightrgb[0];\n\t\tdl->color[1] = t->dlightrgb[1];\n\t\tdl->color[2] = t->dlightrgb[2];\n\n\t\tif (t->netstyle & CTE_CHANNELFADE)\n\t\t{\n\t\t\tdl->channelfade[0] = t->dlightcfade[0];\n\t\t\tdl->channelfade[1] = t->dlightcfade[1];\n\t\t\tdl->channelfade[2] = t->dlightcfade[2];\n\t\t}\n\n\t\t/*\n\t\tif (dl->color[0] < 0)\n\t\t\tdl->channelfade[0] = 0;\n\t\telse\n\t\t\tdl->channelfade[0] = dl->color[0]/t->dlighttime;\n\n\t\tif (dl->color[1] < 0)\n\t\t\tdl->channelfade[1] = 0;\n\t\telse\n\t\t\tdl->channelfade[1] = dl->color[0]/t->dlighttime;\n\n\t\tif (dl->color[2] < 0)\n\t\t\tdl->channelfade[2] = 0;\n\t\telse\n\t\t\tdl->channelfade[2] = dl->color[0]/t->dlighttime;\n\t\t*/\n\t}\n}\n\nvoid CL_RunPCustomTEnts(void)\n{\n\tcusttentinst_t *ef;\n\tstatic float lasttime;\n\tfloat since = cl.time - lasttime;\n\tif (since < 0)\n\t\tlasttime = cl.time;\n\telse if (since < 1/60.0)\n\t\treturn;\n\tlasttime = cl.time;\n\tfor (ef = activepcusttents; ef; ef = ef->next)\n\t{\n\t\tCL_SpawnCustomTEnt(ef);\n\t}\n}\n\nclcustomtents_t customtenttype[255];\t//network based.\n\nqboolean CL_WriteCustomTEnt(sizebuf_t *buf, int id)\n{\n\tclcustomtents_t *t;\n\tif ((unsigned)id >= 255u)\n\t\treturn false;\n\tt = &customtenttype[id];\n\tif (*t->name)\n\t{\n\t\tMSG_WriteByte(buf, svcfte_customtempent);\n\t\tMSG_WriteByte(buf, 255);\n\t\tMSG_WriteByte(buf, id);\n\t\tMSG_WriteByte(buf, t->netstyle);\n\t\tMSG_WriteString(buf, t->name);\n\n\t\tif (t->netstyle & CTE_STAINS)\n\t\t{\n\t\t\tMSG_WriteChar(buf, t->stain[0]);\n\t\t\tMSG_WriteChar(buf, t->stain[1]);\n\t\t\tMSG_WriteChar(buf, t->stain[2]);\n\t\t\tMSG_WriteByte(buf, t->radius);\n\t\t}\n\t\tif (t->netstyle & CTE_GLOWS)\n\t\t{\n\t\t\tMSG_WriteByte(buf, t->dlightrgb[0]*255);\n\t\t\tMSG_WriteByte(buf, t->dlightrgb[1]*255);\n\t\t\tMSG_WriteByte(buf, t->dlightrgb[2]*255);\n\t\t\tMSG_WriteByte(buf, t->dlightradius);\n\t\t\tMSG_WriteByte(buf, t->dlighttime*16);\n\t\t\tif (t->netstyle & CTE_CHANNELFADE)\n\t\t\t{\n\t\t\t\tMSG_WriteByte(buf, t->dlightcfade[0]*64);\n\t\t\t\tMSG_WriteByte(buf, t->dlightcfade[1]*64);\n\t\t\t\tMSG_WriteByte(buf, t->dlightcfade[2]*64);\n\t\t\t}\n\t\t}\n\t}\n\treturn true;\n}\n\nvoid CL_ParseCustomTEnt(void)\n{\n\tchar *str;\n\tclcustomtents_t *t;\n\tint type = MSG_ReadByte();\n\tcusttentinst_t info;\n\n\tif (type == 255)\t//255 is register\n\t{\n\t\ttype = MSG_ReadByte();\n\t\tif (type == 255)\n\t\t\tHost_EndGame(\"Custom temp type 255 isn't valid\\n\");\n\t\tt = &customtenttype[type];\n\n\t\tt->netstyle = MSG_ReadByte();\n\t\tstr = MSG_ReadString();\n\t\tQ_strncpyz(t->name, str, sizeof(t->name));\n\t\tt->particleeffecttype = P_FindParticleType(str);\n\t\tif (cl_shownet.ival >= 3)\n\t\t\tCon_Printf(\"\\tdefine \\\"%s\\\" (%x)\\n\", t->name, t->netstyle);\n\n\t\tif (t->netstyle & CTE_STAINS)\n\t\t{\n\t\t\tt->stain[0] = MSG_ReadChar();\n\t\t\tt->stain[1] = MSG_ReadChar();\n\t\t\tt->stain[2] = MSG_ReadChar();\n\t\t\tt->radius = MSG_ReadByte();\n\t\t}\n\t\telse\n\t\t\tt->radius = 0;\n\t\tif (t->netstyle & CTE_GLOWS)\n\t\t{\n\t\t\tt->dlightrgb[0] = MSG_ReadByte()/255.0f;\n\t\t\tt->dlightrgb[1] = MSG_ReadByte()/255.0f;\n\t\t\tt->dlightrgb[2] = MSG_ReadByte()/255.0f;\n\t\t\tt->dlightradius = MSG_ReadByte();\n\t\t\tt->dlighttime = MSG_ReadByte()/16.0f;\n\t\t\tif (t->netstyle & CTE_CHANNELFADE)\n\t\t\t{\n\t\t\t\tt->dlightcfade[0] = MSG_ReadByte()/64.0f;\n\t\t\t\tt->dlightcfade[1] = MSG_ReadByte()/64.0f;\n\t\t\t\tt->dlightcfade[2] = MSG_ReadByte()/64.0f;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t\tt->dlighttime = 0;\n\t\treturn;\n\t}\n\n\tt = &customtenttype[type];\n\n\tif (cl_shownet.ival >= 3)\n\t\tCon_Printf(\"\\tspawn \\\"%s\\\" (%x)\\n\", t->name, t->netstyle);\n\n\tinfo.type = t;\n\tif (t->netstyle & CTE_PERSISTANT)\n\t{\n\t\tinfo.id = MSG_ReadShort();\n\t}\n\telse\n\t\tinfo.id = 0;\n\n\tif (info.id & 0x8000)\n\t{\n\t\tVectorClear(info.pos);\n\t\tVectorClear(info.pos2);\n\t\tinfo.count = 0;\n\t\tVectorClear(info.dir);\n\t}\n\telse\n\t{\n\t\tMSG_ReadPos (info.pos);\n\n\t\tif (t->netstyle & CTE_ISBEAM)\n\t\t\tMSG_ReadPos (info.pos2);\n\t\telse\n\t\t\tVectorCopy(info.pos, info.pos2);\n\n\t\tif (t->netstyle & CTE_CUSTOMCOUNT)\n\t\t\tinfo.count = MSG_ReadByte();\n\t\telse\n\t\t\tinfo.count = 1;\n\n\t\tif (t->netstyle & CTE_CUSTOMVELOCITY)\n\t\t{\n\t\t\tinfo.dir[0] = MSG_ReadCoord();\n\t\t\tinfo.dir[1] = MSG_ReadCoord();\n\t\t\tinfo.dir[2] = MSG_ReadCoord();\n\t\t}\n\t\telse if (t->netstyle & CTE_CUSTOMDIRECTION)\n\t\t\tMSG_ReadDir (info.dir);\n\t\telse\n\t\t\tVectorClear(info.dir);\n\t}\n\n\tif (t->netstyle & CTE_PERSISTANT)\n\t{\n\t\tif (info.id & 0x8000)\n\t\t{\n\t\t\tcusttentinst_t **link, *o;\n\t\t\tfor (link = &activepcusttents; *link; )\n\t\t\t{\n\t\t\t\to = *link;\n\t\t\t\tif (o->id == info.id)\n\t\t\t\t{\n\t\t\t\t\t*link = o->next;\n\t\t\t\t\tZ_Free(o);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tlink = &(*link)->next;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\t//heap fragmentation is going to suck here.\n\t\t\tcusttentinst_t *n = Z_Malloc(sizeof(*n));\n\t\t\tinfo.next = activepcusttents;\n\t\t\t*n = info;\n\t\t\tactivepcusttents = n;\n\t\t}\n\t}\n\telse\n\t\tCL_SpawnCustomTEnt(&info);\n}\nvoid CL_RefreshCustomTEnts(void)\n{\n\tint i;\n\tfor (i = 0; i < sizeof(customtenttype)/sizeof(customtenttype[0]); i++)\n\t{\n\t\tcustomtenttype[i].particleeffecttype = (!*customtenttype[i].name)?-1:P_FindParticleType(customtenttype[i].name);\n\n//\t\tif (customtenttype[i].particleeffecttype == P_INVALID && *customtenttype[i].name)\n//\t\t\tCon_DPrintf(\"%s was not loaded\\n\", customtenttype[i].name);\n\t}\n\n\tif (cl.particle_ssprecaches)\n\t{\n\t\tfor (i = 0; i < MAX_SSPARTICLESPRE; i++)\n\t\t{\n\t\t\tif (cl.particle_ssname[i])\n\t\t\t\tcl.particle_ssprecache[i] = P_FindParticleType(cl.particle_ssname[i]);\n\t\t\telse\n\t\t\t\tcl.particle_ssprecache[i] = P_INVALID;\n\t\t}\n\t}\n\tif (cl.particle_csprecaches)\n\t{\n\t\tfor (i = 0; i < MAX_CSPARTICLESPRE; i++)\n\t\t{\n\t\t\tif (cl.particle_csname[i])\n\t\t\t\tcl.particle_csprecache[i] = P_FindParticleType(cl.particle_csname[i]);\n\t\t\telse\n\t\t\t\tcl.particle_csprecache[i] = P_INVALID;\n\t\t}\n\t}\n#ifdef CSQC_DAT\n\tCSQC_ResetTrails();\n#endif\n}\nvoid CL_ClearCustomTEnts(void)\n{\n\tint i;\n\tcusttentinst_t *p;\n\twhile(activepcusttents)\n\t{\n\t\tp = activepcusttents;\n\t\tactivepcusttents = p->next;\n\t\tZ_Free(p);\n\t}\n\tfor (i = 0; i < sizeof(customtenttype)/sizeof(customtenttype[0]); i++)\n\t{\n\t\t*customtenttype[i].name = 0;\n\t\tcustomtenttype[i].particleeffecttype = -1;\n\t}\n}\n\nint CL_TranslateParticleFromServer(int qceffect)\n{\n\tif (cl.particle_ssprecaches && qceffect >= 0 && qceffect < MAX_SSPARTICLESPRE)\n\t{\n\t\t/*proper precaches*/\n\t\treturn cl.particle_ssprecache[qceffect];\n\t}\n\telse if (-qceffect >= 0 && -qceffect < MAX_CSPARTICLESPRE)\n\t{\n\t\tqceffect = -qceffect;\n\t\treturn cl.particle_csprecache[qceffect];\n\t}\n\telse\n\t\treturn P_INVALID;\n}\n\nvoid CL_ParseTrailParticles(void)\n{\n\tint entityindex;\n\tint effectindex;\n\tvec3_t start, end;\n\ttrailkey_t *tk;\n\n\tentityindex = MSGCL_ReadEntity();\n\teffectindex = (unsigned short)MSG_ReadShort();\n\n\tstart[0] = MSG_ReadCoord();\n\tstart[1] = MSG_ReadCoord();\n\tstart[2] = MSG_ReadCoord();\n\tend[0] = MSG_ReadCoord();\n\tend[1] = MSG_ReadCoord();\n\tend[2] = MSG_ReadCoord();\n\n\teffectindex = CL_TranslateParticleFromServer(effectindex);\n\n\tif (entityindex>0 && (unsigned int)entityindex < cl.maxlerpents)\n\t\ttk = &cl.lerpents[entityindex].trailstate;\n\telse\n\t\ttk = NULL;\n\n\tif (P_ParticleTrail(start, end, effectindex, 1, entityindex, NULL, tk))\n\t\tP_ParticleTrail(start, end, rt_blood, 1, entityindex, NULL, tk);\n}\n\nvoid CL_ParsePointParticles(qboolean compact)\n{\n\tvec3_t\t\torg, dir;\n\tunsigned int effectindex;\n\tfloat count;\n\n\teffectindex = (unsigned short)MSG_ReadShort();\n\torg[0] = MSG_ReadCoord();\n\torg[1] = MSG_ReadCoord();\n\torg[2] = MSG_ReadCoord();\n\tif (compact)\n\t{\n\t\tdir[0] = dir[1] = dir[2] = 0;\n\t\tcount = 1;\n\t}\n\telse\n\t{\n\t\tdir[0] = MSG_ReadCoord();\n\t\tdir[1] = MSG_ReadCoord();\n\t\tdir[2] = MSG_ReadCoord();\n\t\tcount = (unsigned short)MSG_ReadShort();\n\t}\n\n\teffectindex = CL_TranslateParticleFromServer(effectindex);\n\n\tif (cl.splitclients <= 1 && cl_part_density_fade.value > 0)\n\t{\n\t\tvec3_t move;\n\t\tfloat dist;\n\t\tVectorSubtract(org, cl.playerview[0].audio.origin, move);\n\t\tdist = VectorLength(move);\n\t\tif (dist > cl_part_density_fade_start.value)\n\t\t{\n\t\t\tdist -= cl_part_density_fade_start.value;\n\t\t\tcount = count - dist/cl_part_density_fade.value;\n\t\t\tif (count < 0)\n\t\t\t\treturn;\n\t\t}\n\t}\n\n\tif (P_RunParticleEffectType(org, dir, count, effectindex))\n\t\tP_RunParticleEffect (org, dir, 15, 15);\n}\n\nvoid CLNQ_ParseParticleEffect (void)\n{\n\tvec3_t\t\torg, dir;\n\tint\t\t\ti, msgcount, color;\n\n\tfor (i=0 ; i<3 ; i++)\n\t\torg[i] = MSG_ReadCoord ();\n\tfor (i=0 ; i<3 ; i++)\n\t\tdir[i] = MSG_ReadChar () * (1.0/16);\n\tmsgcount = MSG_ReadByte ();\n\tcolor = MSG_ReadByte ();\n\n\tif (msgcount == 255)\n\t{\n\t\t// treat as spriteless explosion (qtest/some mods require this)\n\t\tif (P_RunParticleEffectType(org, NULL, 1, pt_explosion))\n\t\t\tP_RunParticleEffect(org, NULL, 107, 1024); // should be 97-111\n\t}\n\telse\n\t\tP_RunParticleEffect (org, dir, color, msgcount);\n}\nvoid CL_ParseParticleEffect2 (void)\n{\n\tvec3_t\t\torg, dmin, dmax;\n\tint\t\t\ti, msgcount, color, effect;\n\n\tfor (i=0 ; i<3 ; i++)\n\t\torg[i] = MSG_ReadCoord ();\n\tfor (i=0 ; i<3 ; i++)\n\t\tdmin[i] = MSG_ReadFloat ();\n\tfor (i=0 ; i<3 ; i++)\n\t\tdmax[i] = MSG_ReadFloat ();\n\tcolor = MSG_ReadShort ();\n\tmsgcount = MSG_ReadByte ();\n\teffect = MSG_ReadByte ();\n\n\tP_RunParticleEffect2 (org, dmin, dmax, color, effect, msgcount);\n}\nvoid CL_ParseParticleEffect3 (void)\n{\n\tvec3_t\t\torg, box;\n\tint\t\t\ti, msgcount, color, effect;\n\n\tfor (i=0 ; i<3 ; i++)\n\t\torg[i] = MSG_ReadCoord ();\n\tfor (i=0 ; i<3 ; i++)\n\t\tbox[i] = MSG_ReadByte ();\n\tcolor = MSG_ReadShort ();\n\tmsgcount = MSG_ReadByte ();\n\teffect = MSG_ReadByte ();\n\n\tP_RunParticleEffect3 (org, box, color, effect, msgcount);\n}\nvoid CL_ParseParticleEffect4 (void)\n{\n\tvec3_t\t\torg;\n\tint\t\t\ti, msgcount, color, effect;\n\tfloat\t\tradius;\n\n\tfor (i=0 ; i<3 ; i++)\n\t\torg[i] = MSG_ReadCoord ();\n\tradius = MSG_ReadByte();\n\tcolor = MSG_ReadShort ();\n\tmsgcount = MSG_ReadByte ();\n\teffect = MSG_ReadByte ();\n\n\tP_RunParticleEffect4 (org, radius, color, effect, msgcount);\n}\n\nvoid CL_SpawnSpriteEffect(vec3_t org, vec3_t dir, vec3_t orientationup, model_t *model, int startframe, int framecount, float framerate, float alpha, float scale, float randspin, float gravity, int traileffect, unsigned int renderflags, int skinnum, float red, float green, float blue)\n{\n\texplosion_t\t*ex;\n\n\tex = CL_AllocExplosion (org);\n\tex->start = cl.time;\n\tex->model = model;\n\tex->firstframe = startframe;\n\tex->numframes = framecount;\n\tex->framerate = framerate;\n\tex->skinnum = skinnum;\n\tex->traileffect = traileffect;\n\tex->scale = scale;\n\tex->rgb[0] = red;\n\tex->rgb[1] = green;\n\tex->rgb[2] = blue;\n\n\tex->flags |= renderflags;\n\n\t//sprites always use a fixed alpha. models can too if the alpha is < 0\n\tif (model->type == mod_sprite || alpha < 0)\n\t\tex->endalpha = fabs(alpha);\n\tex->startalpha = fabs(alpha);\n\n\tif (ex->endalpha < 1 || ex->startalpha < 1)\n\t\tex->flags |= RF_TRANSLUCENT;\n\n\tif (randspin)\n\t{\n\t\tex->angles[0] = frandom()*360;\n\t\tex->angles[1] = frandom()*360;\n\t\tex->angles[2] = frandom()*360;\n\n\t\tex->avel[0] = crandom()*randspin;\n\t\tex->avel[1] = crandom()*randspin;\n\t\tex->avel[2] = crandom()*randspin;\n\t}\n\tex->gravity = gravity;\n\n\tif (orientationup)\n\t{\n\t\tex->angles[0] = acos(orientationup[2])/M_PI*180;\n\t\tif (orientationup[0])\n\t\t\tex->angles[1] = atan2(orientationup[1], orientationup[0])/M_PI*180;\n\t\telse if (orientationup[1] > 0)\n\t\t\tex->angles[1] = 90;\n\t\telse if (orientationup[1] < 0)\n\t\t\tex->angles[1] = 270;\n\t\telse\n\t\t\tex->angles[1] = 0;\n\t\tex->angles[0]*=r_meshpitch.value;\n\t\tex->angles[2]*=r_meshroll.value;\n\t}\n\n\n\tif (dir)\n\t{\n//\t\tvec3_t spos;\n//\t\tfloat dlen;\n//\t\tdlen = -10/VectorLength(dir);\n//\t\tVectorMA(ex->origin, dlen, dir, spos);\n//\t\tTraceLineN(spos, org, ex->origin, NULL);\n\n\t\tVectorCopy(dir, ex->velocity);\n\t}\n\telse\n\t\tVectorClear(ex->velocity);\n}\n\n// [vector] org [byte] modelindex [byte] startframe [byte] framecount [byte] framerate\n// [vector] org [short] modelindex [short] startframe [byte] framecount [byte] framerate\nvoid CL_ParseEffect (qboolean effect2)\n{\n\tvec3_t org;\n\tint modelindex;\n\tint startframe;\n\tint framecount;\n\tint framerate;\n\tmodel_t *mod;\n\n\torg[0] = MSG_ReadCoord();\n\torg[1] = MSG_ReadCoord();\n\torg[2] = MSG_ReadCoord();\n\n\tif (effect2)\n\t\tmodelindex = MSG_ReadShort();\n\telse\n\t\tmodelindex = MSG_ReadByte();\n\n\tif (effect2)\n\t\tstartframe = MSG_ReadShort();\n\telse\n\t\tstartframe = MSG_ReadByte();\n\n\tframecount = MSG_ReadByte();\n\tframerate = MSG_ReadByte();\n\n\tmod = cl.model_precache[modelindex];\n\tCL_SpawnSpriteEffect(org, NULL, NULL, mod, startframe, framecount, framerate, mod->type==mod_sprite?-1:1, 1, 0, 0, P_INVALID, 0, 0, 1.0f, 1.0f, 1.0f);\n}\n\n#ifdef Q2CLIENT\nvoid CL_SmokeAndFlash(vec3_t origin)\n{\n\texplosion_t\t*ex;\n\n\tex = CL_AllocExplosion (origin);\n\tVectorClear(ex->angles);\n//\tex->type = ex_misc;\n\tex->numframes = 4;\n\tex->flags = RF_TRANSLUCENT;\n\tex->start = cl.time;\n\tex->model = Mod_ForName (q2tentmodels[q2cl_mod_smoke].modelname, MLV_WARN);\n\n\tex = CL_AllocExplosion (origin);\n\tVectorClear(ex->angles);\n//\tex->type = ex_flash;\n\tex->flags = RF_FULLBRIGHT;\n\tex->numframes = 2;\n\tex->start = cl.time;\n\tex->model = Mod_ForName (q2tentmodels[q2cl_mod_flash].modelname, MLV_WARN);\n}\n\nvoid CL_Laser (vec3_t start, vec3_t end, int colors)\n{\n\texplosion_t\t*ex = CL_AllocExplosion(start);\n\tex->firstframe = 0;\n\tex->numframes = 10;\n\tex->startalpha = 0.33f;\n\tex->endalpha = 0;\n\tex->model = NULL;\n\tex->skinnum = (colors >> ((rand() % 4)*8)) & 0xff;\n\tVectorCopy (start, ex->origin);\n\tVectorCopy (end, ex->oldorigin);\n\tex->flags = RF_TRANSLUCENT | Q2RF_BEAM;\n\tex->start = cl.time;\n\tex->framerate = 100; // smoother fading\n}\n\nvoid CLQ2_ParseSteam(void)\n{\n\tvec3_t pos, dir;\n\t/*qbyte colour;\n\tshort magnitude;\n\tunsigned int duration;*/\n\tsigned int id = MSG_ReadShort();\n\t/*qbyte count =*/ MSG_ReadByte();\n\tMSG_ReadPos(pos);\n\tMSG_ReadDir(dir);\n\t/*colour =*/ MSG_ReadByte();\n\t/*magnitude =*/ MSG_ReadShort();\n\n\tif (id != -1)\n\t\t/*duration =*/ MSG_ReadLong();\n\telse\n\t\t/*duration = 0;*/\n\n\tCon_Printf(\"FIXME: CLQ2_ParseSteam: stub\\n\");\n}\n\nvoid CLQ2_ParseTEnt (void)\n{\n\tbeam_t *b;\n\tq2particleeffects_t\t\ttype;\n\tint\t\tpt;\n\tvec3_t\tpos, pos2, dir;\n\tint\t\tcnt;\n\tint\t\tcolor;\n\tint\t\tr;\n\tint\t\tent;\n//\tint\t\tmagnitude;\n//\texplosion_t\t*ex;\n\n\ttype = MSG_ReadByte ();\n\n\tif (type <= Q2TE_MAX)\n\t{\n\t\tif (cl_shownet.ival > 1)\n\t\t\tCon_Printf(\"\\t%s\\n\", q2efnames[type]);\n\t\tpt = pt_q2[type];\n\t}\n\telse\n\t{\n\t\tif (cl_shownet.ival > 1)\n\t\t\tCon_Printf(\"\\tTE%u\\n\", type);\n\t\tpt = P_INVALID;\n\t}\n\tswitch (type)\n\t{\n\tcase Q2TE_GUNSHOT:\t//grey tall thing with smoke+sparks\n\tcase Q2TE_BLOOD:\t\t//red tall thing\n\tcase Q2TE_SPARKS:\t\t//orange tall thing (with not many particles)\n\tcase Q2TE_BLASTER:\t//regular blaster\n\tcase Q2TE_SHOTGUN:\t//gunshot with less particles\n\tcase Q2TE_SCREEN_SPARKS://green+grey tall\n\tcase Q2TE_SHIELD_SPARKS://blue+grey tall\n\tcase Q2TE_BULLET_SPARKS://orange+grey tall+smoke\n\tcase Q2TE_GREENBLOOD:\t//yellow...\n\tcase Q2TE_BLASTER2:\t//green version of te_blaster\n\tcase Q2TE_MOREBLOOD:\t//te_blood*2\n\tcase Q2TE_HEATBEAM_SPARKS://white outwards puffs\n\tcase Q2TE_HEATBEAM_STEAM://orange outwards puffs\n\tcase Q2TE_ELECTRIC_SPARKS://blue tall\n\tcase Q2TE_FLECHETTE:\t//grey version of te_blaster\n\t\tMSG_ReadPos (pos);\n\t\tMSG_ReadDir (dir);\n\t\tP_RunParticleEffectType(pos, dir, 1, pt);\n\t\tbreak;\n\tcase Q2TE_BFG_LASER:\n\t\tMSG_ReadPos (pos);\n\t\tMSG_ReadPos (pos2);\n\t\tCL_Laser(pos, pos2, 0xd0d1d2d3);\n\t\tbreak;\n\tcase Q2TE_RAILTRAIL:\t//blue spiral, grey particles\n\tcase Q2TE_BUBBLETRAIL:\t//grey sparse trail, slow riser\n//\tcase Q2TE_BFG_LASER:\t//green laser\n\tcase Q2TE_DEBUGTRAIL:\t//long lived blue trail\n\tcase Q2TE_BUBBLETRAIL2:\t//grey rising trail\n\tcase Q2TE_BLUEHYPERBLASTER:\t//TE_BLASTER without model+light\n\tcase Q2TEEX_RAILTRAIL2:\n\tcase Q2TEEX_BFGZAP:\n\t\tMSG_ReadPos (pos);\n\t\tMSG_ReadPos (pos2);\n\t\tP_ParticleTrail(pos, pos2, pt, 1, 0, NULL, NULL);\n\t\tP_RunParticleEffectTypeString(pos2, NULL, 1, va(\"%s_end\", q2efnames[type]));\n\t\tbreak;\n\tcase Q2TE_EXPLOSION1:\t//column\n\tcase Q2TE_EXPLOSION2:\t//splits\n\tcase Q2TE_ROCKET_EXPLOSION://top blob/column\n\tcase Q2TE_GRENADE_EXPLOSION://indistinguishable from TE_EXPLOSION2\n\tcase Q2TE_ROCKET_EXPLOSION_WATER://rocket but with different sound\n\tcase Q2TE_GRENADE_EXPLOSION_WATER://different sound\n\tcase Q2TE_BFG_EXPLOSION://green light+sprite\n\tcase Q2TE_BFG_BIGEXPLOSION://green+white fast particles\n\tcase Q2TE_BOSSTPORT://splitting+merging+upwards particles.\n\tcase Q2TE_PLASMA_EXPLOSION://looks like rocket explosion to me\n\tcase Q2TE_PLAIN_EXPLOSION://looks like rocket explosion to me\n\tcase Q2TE_CHAINFIST_SMOKE://small smoke\n\tcase Q2TE_TRACKER_EXPLOSION://black light, slow particles\n\tcase Q2TE_TELEPORT_EFFECT://q1-style teleport\n\tcase Q2TE_DBALL_GOAL://q1-style teleport\n\tcase Q2TE_NUKEBLAST://dome expansion (blue/white particles)\n\tcase Q2TE_WIDOWSPLASH://dome (orange+gravity)\n\tcase Q2TE_EXPLOSION1_BIG://buggy model\n\tcase Q2TE_EXPLOSION1_NP://looks like a rocket explosion to me\n\t\tMSG_ReadPos (pos);\n\t\tP_RunParticleEffectType(pos, NULL, 1, pt);\n\t\tbreak;\n\tcase Q2TE_SPLASH:\n\t\tcnt = MSG_ReadByte ();\n\t\tMSG_ReadPos (pos);\n\t\tMSG_ReadDir (dir);\n\t\tr = MSG_ReadByte () + Q2SPLASH_UNKNOWN;\n\t\tif (r > Q2SPLASH_MAX)\n\t\t\tr = Q2SPLASH_UNKNOWN;\n\t\tpt = pt_q2[r];\n\t\tP_RunParticleEffectType(pos, NULL, 1, pt);\n\t\tbreak;\n\n\tcase Q2TE_PARASITE_ATTACK:\n\tcase Q2TE_MEDIC_CABLE_ATTACK:\n\t\tCL_ParseBeam (BT_Q2PARASITE);\n\t\tbreak;\n\tcase Q2TE_HEATBEAM:\n\t\tb = CL_ParseBeam (BT_Q2HEATBEAM);\t//2, 7, -3\n\t\tif (b)\n\t\t{\n\t\t\tb->bflags |= STREAM_ATTACHED;\n\t\t\tVectorSet(b->offset, 2, 7, -3);\n\t\t}\n\t\tbreak;\n\tcase Q2TE_MONSTER_HEATBEAM:\n\t\tb = CL_ParseBeam (BT_Q2HEATBEAM);\n\t\tif (b)\n\t\t\tb->bflags |= STREAM_ATTACHED;\n\t\tbreak;\n\tcase Q2TE_GRAPPLE_CABLE:\n\t\tCL_ParseBeamOffset (BT_Q2GRAPPLE);\n\t\tbreak;\n\n\tcase Q2TE_LIGHTNING:\n\t\tent = MSGCL_ReadEntity ();\n\t\t/*toent =*/ MSGCL_ReadEntity ();\t//ident only.\n\t\tpos[0] = MSG_ReadCoord ();\n\t\tpos[1] = MSG_ReadCoord ();\n\t\tpos[2] = MSG_ReadCoord ();\n\t\tpos2[0] = MSG_ReadCoord ();\n\t\tpos2[1] = MSG_ReadCoord ();\n\t\tpos2[2] = MSG_ReadCoord ();\n\t\tCL_AddBeam(BT_Q2LIGHTNING, ent, pos, pos2);\n\t\tbreak;\n\n\tcase Q2TE_LASER_SPARKS:\n\tcase Q2TE_WELDING_SPARKS:\n\tcase Q2TE_TUNNEL_SPARKS:\n\t\tcnt = MSG_ReadByte ();\n\t\tMSG_ReadPos (pos);\n\t\tMSG_ReadDir (dir);\n\t\tcolor = MSG_ReadByte ();\n\t\tP_RunParticleEffectPalette(va(\"q2part.%s\", q2efnames[type]), pos, dir, color, cnt);\n\t\tbreak;\n\n\tcase Q2TE_STEAM:\n\t\tCLQ2_ParseSteam();\n\t\tbreak;\n\n\tcase Q2TE_FORCEWALL:\n\t\tMSG_ReadPos (pos);\n\t\tMSG_ReadPos (pos2);\n\t\tcolor = MSG_ReadByte ();\n\t\tP_ParticleTrailIndex(pos, pos2, pt, 1, color, 0, NULL);\n\t\tbreak;\n\n\tcase Q2TE_FLASHLIGHT:\t//white 400-radius dlight\n\t\tMSG_ReadPos(pos);\n\t\tent = MSG_ReadShort();\n\t\tP_ParticleTrail(pos, pos, pt, 1, ent, NULL, NULL);\n\t\tbreak;\n\tcase Q2TE_WIDOWBEAMOUT:\t\t/*requires state tracking to keep it splurting constantly for 2.1 secs*/\n\t\tent = MSG_ReadShort();\n\t\tMSG_ReadPos(pos);\n\t\tCon_Printf(\"FIXME: Q2TE_WIDOWBEAMOUT not implemented\\n\");\n\t\tbreak;\n\n\t//case Q2TE_RAILTRAIL2:\t\t/*not implemented in vanilla*/\n\tcase Q2TE_FLAME:\t\t\t/*not implemented in vanilla*/\n\t\tHost_EndGame (\"CLQ2_ParseTEnt: bad/non-implemented type %i\", type);\n\t\tbreak;\n\n\tcase Q2TEEX_BLUEHYPERBLASTER:\n\tcase Q2TEEX_BERSERK_SLAM:\n\t\tMSG_ReadPos (pos);\n\t\tMSG_ReadDir (pos2);\n\t\tP_RunParticleEffectType(pos, dir, 1, pt);\n\t\tbreak;\n\n\tcase Q2TEEX_GRAPPLE_CABLE_2:\n\t\tCL_ParseBeam (BT_Q2GRAPPLE);\n\t\tbreak;\n\n\tcase Q2TEEX_POWER_SPLASH:\n\t\tMSGCL_ReadEntity();\n\t\tMSG_ReadByte();\n\t\tbreak;\n\tcase Q2TEEX_LIGHTNING_BEAM:\n\t\tCL_ParseBeam (BT_Q2LIGHTNING);\n\t\tbreak;\n\tcase Q2TEEX_EXPLOSION1_NL:\n\tcase Q2TEEX_EXPLOSION2_NL:\n\t\tMSG_ReadPos (pos);\n\t\tP_RunParticleEffectType(pos, NULL, 1, pt);\n\t\tbreak;\n\n\n\t//My old attempt at running AlienArena years ago. probably not enough now. Other engines will have other effects.\n/*\tcase CRTE_LEADERBLASTER:\n\t\tHost_EndGame (\"CLQ2_ParseTEnt: bad/non-implemented type %i\", type);\n\tcase CRTE_BLASTER_MUZZLEFLASH:\n\t\tMSG_ReadPos (pos);\n\t\tex = CL_AllocExplosion (pos);\n\t\tex->flags = RF_FULLBRIGHT|RF_NOSHADOW;\n\t\tex->start = cl.q2frame.servertime - 100;\n\t\tCL_NewDlight(0, pos, 350, 0.5, 0.2*5, 0.1*5, 0*5);\n\t\tP_RunParticleEffectTypeString(pos, NULL, 1, \"te_muzzleflash\");\n\t\tbreak;\n\tcase CRTE_BLUE_MUZZLEFLASH:\n\t\tMSG_ReadPos (pos);\n\t\tex = CL_AllocExplosion (pos);\n\t\tex->flags = RF_FULLBRIGHT|RF_NOSHADOW;\n\t\tex->start = cl.q2frame.servertime - 100;\n\t\tCL_NewDlight(0, pos, 350, 0.5, 0.2*5, 0.1*5, 0*5);\n\t\tP_RunParticleEffectTypeString(pos, NULL, 1, \"te_blue_muzzleflash\");\n\t\tbreak;\n\tcase CRTE_SMART_MUZZLEFLASH:\n\t\tMSG_ReadPos (pos);\n\t\tex = CL_AllocExplosion (pos);\n\t\tex->flags = RF_FULLBRIGHT|RF_NOSHADOW;\n\t\tex->start = cl.q2frame.servertime - 100;\n\t\tCL_NewDlight(0, pos, 350, 0.5, 0.2*5, 0*5, 0.2*5);\n\t\tP_RunParticleEffectTypeString(pos, NULL, 1, \"te_smart_muzzleflash\");\n\t\tbreak;\n\tcase CRTE_LEADERFIELD:\n\t\tHost_EndGame (\"CLQ2_ParseTEnt: bad/non-implemented type %i\", type);\n\tcase CRTE_DEATHFIELD:\n\t\tMSG_ReadPos (pos);\n\t\tex = CL_AllocExplosion (pos);\n\t\tVectorCopy (pos, ex->origin);\n\t\tex->flags = RF_FULLBRIGHT|RF_NOSHADOW;\n\t\tex->start = cl.q2frame.servertime - 100;\n\t\tCL_NewDlight(0, pos, 350, 0.5, 0.2*5, 0*5, 0.2*5);\n\t\tP_RunParticleEffectTypeString(pos, NULL, 1, \"te_deathfield\");\n\t\tbreak;\n\tcase CRTE_BLASTERBEAM:\n\t\tMSG_ReadPos (pos);\n\t\tMSG_ReadPos (pos2);\n\t\tP_ParticleTrail(pos, pos2, P_FindParticleType(\"q2part.TR_BLASTERTRAIL2\"), 1, 0, NULL, NULL);\n\t\tbreak;\n\tcase CRTE_STAIN:\n\t\tHost_EndGame (\"CLQ2_ParseTEnt: bad/non-implemented type %i\", type);\n\tcase CRTE_FIRE:\n\t\tHost_EndGame (\"CLQ2_ParseTEnt: bad/non-implemented type %i\", type);\n\tcase CRTE_CABLEGUT:\n\t\tHost_EndGame (\"CLQ2_ParseTEnt: bad/non-implemented type %i\", type);\n\tcase CRTE_SMOKE:\n\t\tHost_EndGame (\"CLQ2_ParseTEnt: bad/non-implemented type %i\", type);\n*/\n\n\tdefault:\n\t\tHost_EndGame (\"CLQ2_ParseTEnt: bad/non-implemented type %i\", type);\n\t\tbreak;\n\t}\n}\n#endif\n\n\n/*\n=================\nCL_NewTempEntity\n=================\n*/\nentity_t *CL_NewTempEntity (void)\n{\n\tentity_t\t*ent;\n\n\tif (cl_numvisedicts == cl_maxvisedicts)\n\t\treturn NULL;\n\tent = &cl_visedicts[cl_numvisedicts];\n\tcl_numvisedicts++;\n\tent->keynum = 0;\n\n\tmemset (ent, 0, sizeof(*ent));\n\tent->playerindex = -1;\n\tent->topcolour = TOP_DEFAULT;\n\tent->bottomcolour = BOTTOM_DEFAULT;\n\n#ifdef PEXT_SCALE\n\tent->scale = 1;\n#endif\n\tent->shaderRGBAf[0] = 1;\n\tent->shaderRGBAf[1] = 1;\n\tent->shaderRGBAf[2] = 1;\n\tent->shaderRGBAf[3] = 1;\n\treturn ent;\n}\n\nqboolean CSQC_GetEntityOrigin(unsigned int csqcent, float *out);\n\n/*\n=================\nCL_UpdateBeams\n=================\n*/\nvoid CL_UpdateBeams (float frametime)\n{\n\tint bnum;\n\tint\t\t\ti, j;\n\tbeam_t\t\t*b;\n\tvec3_t\t\tdist, org;\n\tfloat\t\t*vieworg;\n\tfloat\t\td;\n\tentity_t\t*ent;\n\tentity_state_t *st;\n\tfloat\t\tyaw, pitch;\n\tfloat\t\tforward, offset;\n\tint lastrunningbeam = -1;\n\ttentmodels_t *type;\n\n\textern cvar_t cl_truelightning, v_viewheight;\n\n// update lightning\n\tfor (bnum=0, b=cl_beams; bnum < beams_running; bnum++, b++)\n\t{\n\t\ttype = b->info;\n\t\tif (!type)\n\t\t\tcontinue;\n\n\t\tif (b->endtime < cl.time)\n\t\t{\n\t\t\tif (!cl.paused)\n\t\t\t{\t/*don't let lightning decay while paused*/\n\t\t\t\tP_DelinkTrailstate(&b->trailstate);\n\t\t\t\tP_DelinkTrailstate(&b->emitstate);\n\t\t\t\tb->info = NULL;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\n\t\tlastrunningbeam = bnum;\n\n\t// if coming from the player, update the start position\n\t\tif ((b->bflags & STREAM_ATTACHTOPLAYER) && b->entity > 0 && b->entity <= cl.allocated_client_slots)\n\t\t{\n\t\t\tfor (j = 0; j < cl.splitclients; j++)\n\t\t\t{\n\t\t\t\tplayerview_t *pv = &cl.playerview[j];\n\t\t\t\tif (b->entity == pv->viewentity)\n\t\t\t\t{\n//\t\t\t\t\tplayer_state_t\t*pl;\n\t\t//\t\t\tVectorSubtract(cl.simorg, b->start, org);\n\t\t//\t\t\tVectorAdd(b->end, org, b->end);\t\t//move the end point by simorg-start\n\n//\t\t\t\t\tpl = &cl.inframes[cl.parsecount&UPDATE_MASK].playerstate[b->entity-1];\n//\t\t\t\t\tif (pl->messagenum == cl.parsecount || cls.protocol == CP_NETQUAKE)\n\t\t\t\t\t{\n\t\t\t\t\t\tvec3_t\tfwd, org, ang, viewang;\n\t\t\t\t\t\tfloat\tdelta, f, len;\n\n//\t\t\t\t\t\tif (cl.spectator && pv->cam_auto)\n//\t\t\t\t\t\t{\t//if we're tracking someone, use their origin explicitly.\n//\t\t\t\t\t\t\tvieworg = pl->origin;\n//\t\t\t\t\t\t}\n//\t\t\t\t\t\telse\n#ifdef Q2CLIENT\n\t\t\t\t\t\tif (cls.protocol == CP_QUAKE2)\n\t\t\t\t\t\t\tvieworg = pv->predicted_origin;\n\t\t\t\t\t\telse\n#endif\n\t\t\t\t\t\t\tvieworg = pv->simorg;\n\n\t\t\t\t\t\tif (cl_truelightning.ival > 1 && cl.movesequence > cl_truelightning.ival)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\toutframe_t *frame = &cl.outframes[(cl.movesequence-cl_truelightning.ival)&UPDATE_MASK];\n\t\t\t\t\t\t\tviewang[0] = SHORT2ANGLE(frame->cmd[j].angles[0]);\n\t\t\t\t\t\t\tviewang[1] = SHORT2ANGLE(frame->cmd[j].angles[1]);\n\t\t\t\t\t\t\tviewang[2] = SHORT2ANGLE(frame->cmd[j].angles[2]);\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tVectorCopy(pv->simangles, viewang);\n\n\t\t\t\t\t\tVectorCopy (vieworg, b->start);\n\t\t\t\t\t\tb->start[2] += pv->crouch + bound(-7, v_viewheight.value, 4);\n\n\t\t\t\t\t\tf = bound(0, cl_truelightning.value, 1);\n\n\t\t\t\t\t\tif (!f)\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tVectorSubtract (playerbeam_end[j], vieworg, org);\n\t\t\t\t\t\tlen = VectorLength(org);\n\t\t\t\t\t\torg[2] -= 22;\t\t// adjust for view height\n\t\t\t\t\t\tVectorAngles (org, NULL, ang, false);\n\n\t\t\t\t\t\t// lerp pitch\n\t\t\t\t\t\tdelta = anglemod(viewang[0] - ang[0]);\n\t\t\t\t\t\tif (delta > 180)\n\t\t\t\t\t\t\tdelta -= 360;\n\t\t\t\t\t\tif (ang[0] < -180)\n\t\t\t\t\t\t\tang[0] += 360;\n\t\t\t\t\t\tang[0] += delta * f;\n\n\t\t\t\t\t\t// lerp yaw\n\t\t\t\t\t\tdelta = anglemod(viewang[1] - ang[1]);\n\t\t\t\t\t\tif (delta > 180)\n\t\t\t\t\t\t\tdelta -= 360;\n\t\t\t\t\t\tif (delta < -180)\n\t\t\t\t\t\t\tdelta += 360;\n\t\t\t\t\t\tang[1] += delta * f;\n\t\t\t\t\t\tang[2] = 0;\n\n\t\t\t\t\t\tAngleVectors (ang, fwd, ang, ang);\n\t\t\t\t\t\tVectorCopy(fwd, ang);\n\t\t\t\t\t\tVectorScale (fwd, len, fwd);\n\t\t\t\t\t\tVectorCopy (vieworg, org);\n\t\t\t\t\t\torg[2] += 16;\n\t\t\t\t\t\tVectorAdd (org, fwd, b->end);\n\n\t\t\t\t\t\tif (cl_beam_trace.ival)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tvec3_t normal;\n\t\t\t\t\t\t\tVectorMA(org, len+4, ang, fwd);\n\t\t\t\t\t\t\tif (CL_TraceLine(org, fwd, ang, normal, NULL) < 1)\n\t\t\t\t\t\t\t\tVectorCopy (ang, b->end);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tVectorCopy (b->start, org);\n\t\t}\n#ifdef CSQC_DAT\n\t\telse if ((b->bflags & 1) && b->entity > MAX_EDICTS)\n\t\t{\n\t\t\tCSQC_GetEntityOrigin(b->entity-MAX_EDICTS, org);\n\t\t\tVectorCopy (b->start, org);\n\t\t}\n#endif\n\t\telse if (b->bflags & STREAM_ATTACHED)\n\t\t{\n\t\t\tplayer_state_t\t*pl;\n\t\t\tst = CL_FindPacketEntity(b->entity);\n\t\t\tif (st)\n\t\t\t{\n\t\t\t\tVectorCopy(st->origin, b->start);\n\t\t\t}\n\t\t\telse if (b->entity <= cl.allocated_client_slots && b->entity > 0)\n\t\t\t{\n\t\t\t\tpl = &cl.inframes[cl.parsecount&UPDATE_MASK].playerstate[b->entity-1];\n\t\t\t\tVectorCopy(pl->origin, b->start);\n\t\t\t}\n\t\n\t\t\tVectorCopy (b->start, org);\n\t\t}\n\t\telse\n\t\t\tVectorCopy (b->start, org);\n\t\tVectorAdd(org, b->offset, org);\n\n\t// calculate pitch and yaw\n\t\tVectorSubtract (b->end, org, dist);\n\n\t\tif (dist[1] == 0 && dist[0] == 0)\n\t\t{\n\t\t\tyaw = 0;\n\t\t\tif (dist[2] > 0)\n\t\t\t\tpitch = 90;\n\t\t\telse\n\t\t\t\tpitch = 270;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tyaw = (atan2(dist[1], dist[0]) * 180 / M_PI);\n\t\t\tif (yaw < 0)\n\t\t\t\tyaw += 360;\n\n\t\t\tforward = sqrt (dist[0]*dist[0] + dist[1]*dist[1]);\n\t\t\tpitch = (atan2(dist[2], forward) * 180 / M_PI);\n\t\t\tif (pitch < 0)\n\t\t\t\tpitch += 360;\n\t\t}\n\n\t\tif (ruleset_allow_particle_lightning.ival || !type->modelname)\n\t\t\tif (type->ef_beam >= 0 && !P_ParticleTrail(org, b->end, type->ef_beam, frametime, b->entity, NULL, &b->trailstate))\n\t\t\t\tcontinue;\n\t\tif (!type->model)\n\t\t{\n\t\t\ttype->model = type->modelname?Mod_ForName(type->modelname, MLV_WARN):NULL;\n\t\t\tif (!type->model)\n\t\t\t\tcontinue;\n\t\t}\n\n\t// add new entities for the lightning\n\t\td = VectorNormalize(dist);\n\n\t\tif(b->bflags & STREAM_JITTER)\n\t\t{\n\t\t\toffset = (int)(cl.time*40)%30;\n\t\t\tfor(i = 0; i < 3; i++)\n\t\t\t{\n\t\t\t\torg[i] += dist[i]*offset;\n\t\t\t}\n\t\t}\n\n\t\twhile (d > 0)\n\t\t{\n\t\t\tent = CL_NewTempEntity ();\n\t\t\tif (!ent)\n\t\t\t\treturn;\n\t\t\tVectorCopy (org, ent->origin);\n\t\t\tent->model = type->model;\n\t\t\tent->skinnum = b->skin;\n#ifdef HEXEN2\n\t\t\tent->drawflags |= MLS_ABSLIGHT;\n\t\t\tent->abslight = 64 + 128 * bound(0, cl_shaftlight.value, 1);\n#endif\n\t\t\tent->shaderRGBAf[3] = b->alpha;\n\t\t\tent->flags = b->rflags;\n\n\t\t\tent->angles[0] = -pitch;\n\t\t\tent->angles[1] = yaw;\n\t\t\tent->angles[2] = rand()%360;\n\t\t\tAngleVectorsFLU(ent->angles, ent->axis[0], ent->axis[1], ent->axis[2]);\n\t\t\tent->angles[0] = pitch;\n\t\t\tent->framestate.g[FS_REG].lerpweight[0] = 1;\n\t\t\tent->framestate.g[FS_REG].frame[0] = 0;\n\t\t\tent->framestate.g[FS_REG].frametime[0] = cl.time - (b->endtime - 0.2);\n\n\t\t\tfor (i=0 ; i<3 ; i++)\n\t\t\t\torg[i] += dist[i]*30;\n\t\t\td -= 30;\n\t\t}\n\n\t\tif (type->beamimpactmodel)\n\t\t{\n\t\t\tif (!type->impactmodel)\n\t\t\t{\n\t\t\t\ttype->impactmodel = Mod_ForName(type->beamimpactmodel, MLV_WARN);\n\t\t\t\tif (!type->impactmodel)\n\t\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tent = CL_NewTempEntity ();\n\t\t\tif (!ent)\n\t\t\t\treturn;\n\t\t\tVectorCopy (b->end, ent->origin);\n\t\t\tent->model = type->impactmodel;\n#ifdef HEXEN2\n\t\t\tent->drawflags |= MLS_ABSLIGHT;\n\t\t\tent->abslight = 128;\n#endif\n\t\t\tent->shaderRGBAf[3] = b->alpha;\n\t\t\tent->flags = b->rflags;\n\t\t\tent->scale = type->impactscale;\n\n\t\t\tent->angles[0] = -pitch;\n\t\t\tent->angles[1] = yaw;\n\t\t\tent->angles[2] = rand()%360;\n\t\t\tAngleVectorsFLU(ent->angles, ent->axis[0], ent->axis[1], ent->axis[2]);\n\t\t\tent->angles[0] = pitch;\n\t\t\tent->framestate.g[FS_REG].lerpweight[0] = 1;\n\t\t\tent->framestate.g[FS_REG].frame[0] = 0;\n\t\t\tent->framestate.g[FS_REG].frametime[0] = cl.time - (b->endtime - 0.2);\n\t\t}\n\t}\n\n\tbeams_running = lastrunningbeam+1;\n}\n\n/*\n=================\nCL_UpdateExplosions\n=================\n*/\nvoid CL_UpdateExplosions (float frametime)\n{\n\tint\t\t\ti;\n\tfloat\t\tf;\n\tint\t\t\tof;\n\tint numframes;\n\tint firstframe;\n\texplosion_t\t*ex;\n\tentity_t\t*ent;\n\tint lastrunningexplosion = -1;\n\tvec3_t pos, norm;\n\tfloat\t\tscale;\n\n\tfor (i=0, ex=cl_explosions; i < explosions_running; i++, ex++)\n\t{\n\t\tif (!ex->model && !(ex->flags&Q2RF_BEAM))\n\t\t\tcontinue;\n\n\t\tlastrunningexplosion = i;\n\t\tif (ex->model)\n\t\t{\n\t\t\tif (ex->model->loadstate == MLS_LOADING)\n\t\t\t\tcontinue;\n\t\t\tif (ex->model->loadstate != MLS_LOADED)\n\t\t\t{\n\t\t\t\tex->model = NULL;\n\t\t\t\tex->flags = 0;\n\t\t\t\tP_DelinkTrailstate(&(ex->trailstate));\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\n\t\tf = ex->framerate*(cl.time - ex->start);\n\n\t\tif (ex->firstframe >= 0)\n\t\t{\n\t\t\tfirstframe = ex->firstframe;\n\t\t\tnumframes = ex->numframes;\n\n\t\t\tif (!numframes)\n\t\t\t\tnumframes = ex->model->numframes - firstframe;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tfirstframe = 0;\n\t\t\tnumframes = ex->model->numframes;\n\t\t}\n\n\t\tof = (int)f-1;\n\t\tif (ex->endalpha && (int)f == numframes)\n\t\t{\n\t\t\tscale = 1-(f-(int)f);\t//if we have endalpha not 0, then there is a final 'bonus' frame where the model scales down to 0. use numframes+framerate to control how fast it fades.\n\t\t\tf = of;\t//clamp it to the old frame\n\t\t}\n\t\telse if ((int)f >= numframes || (int)f < 0)\n\t\t{\n\t\t\tex->model = NULL;\n\t\t\tex->flags = 0;\n\t\t\tP_DelinkTrailstate(&(ex->trailstate));\n\t\t\tcontinue;\n\t\t}\n\t\telse\n\t\t\tscale = 1;\n\t\tif (of < 0)\n\t\t\tof = 0;\n\n\t\tent = CL_NewTempEntity ();\n\t\tif (!ent)\n\t\t\treturn;\n\n\t\tif (ex->gravity)\n\t\t{\n\t\t\tVectorMA(ex->origin, frametime, ex->velocity, pos);\n\t\t\tif (ex->velocity[0] || ex->velocity[1] || ex->velocity[2])\n\t\t\t{\n\t\t\t\tVectorClear(norm);\n\t\t\t\tif (CL_TraceLine(ex->origin, pos, ent->origin, norm, NULL) < 1)\n\t\t\t\t{\n\t\t\t\t\tfloat sc = DotProduct(ex->velocity, norm) * -1.5;\n\t\t\t\t\tVectorMA(ex->velocity, sc, norm, ex->velocity);\n\t\t\t\t\tVectorScale(ex->velocity, 0.9, ex->velocity);\n\t\t\t\t\tif (norm[2] > 0.7 && DotProduct(ex->velocity, ex->velocity) < 100)\n\t\t\t\t\t{\n\t\t\t\t\t\tVectorClear(ex->velocity);\n\t\t\t\t\t\tVectorClear(ex->avel);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tex->velocity[2] -= ex->gravity * frametime;\n\t\t\t\tVectorCopy(ent->origin, ex->origin);\n\t\t\t}\n\t\t\telse\n\t\t\t\tVectorCopy(ex->origin, ent->origin);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tVectorMA (ex->origin, f, ex->velocity, ent->origin);\n\t\t}\n\t\tVectorMA(ex->angles, frametime, ex->avel, ex->angles);\n\n\t\tVectorCopy (ex->oldorigin, ent->oldorigin);\n\t\tVectorCopy (ex->angles, ent->angles);\n\t\tent->skinnum = ex->skinnum;\n\t\tent->angles[0]*=r_meshpitch.value;\n\t\tent->angles[2]*=r_meshroll.value;\n\t\tAngleVectors(ent->angles, ent->axis[0], ent->axis[1], ent->axis[2]);\n\t\tVectorInverse(ent->axis[1]);\n\t\tent->model = ex->model;\n\t\tent->framestate.g[FS_REG].frame[1] = (int)f+firstframe;\n\t\tent->framestate.g[FS_REG].frame[0] = of+firstframe;\n\t\tent->framestate.g[FS_REG].lerpweight[1] = (f - (int)f);\n\t\tent->framestate.g[FS_REG].lerpweight[0] = 1-ent->framestate.g[FS_REG].lerpweight[1];\n\t\tent->shaderRGBAf[0] = ex->rgb[0];\n\t\tent->shaderRGBAf[1] = ex->rgb[1];\n\t\tent->shaderRGBAf[2] = ex->rgb[2];\n\t\tent->shaderRGBAf[3] = (1.0 - f/(numframes))*(ex->startalpha-ex->endalpha) + ex->endalpha;\n\t\tent->flags = ex->flags;\n\t\tent->scale = scale*ex->scale;\n#ifdef HEXEN2\n\t\tent->drawflags = SCALE_ORIGIN_ORIGIN;\n#endif\n\t\tcl.worldmodel->funcs.FindTouchedLeafs(cl.worldmodel, &ent->pvscache, ent->origin, ent->origin);\n\n\t\tif (ex->traileffect != P_INVALID)\n\t\t\tpe->ParticleTrail(ent->oldorigin, ent->origin, ex->traileffect, frametime, 0, ent->axis, &(ex->trailstate));\n\t\tif (!(ex->flags & Q2RF_BEAM))\n\t\t\tVectorCopy(ent->origin, ex->oldorigin);\t//don't corrupt q2 beams\n\t\tif (ex->flags & Q2RF_BEAM)\n\t\t{\n\t\t\tent->rtype = RT_BEAM;\n\t\t\tent->shaderRGBAf[0] = ((d_8to24srgbtable[ex->skinnum & 0xFF] >>  0) & 0xFF)/255.0;\n\t\t\tent->shaderRGBAf[1] = ((d_8to24srgbtable[ex->skinnum & 0xFF] >>  8) & 0xFF)/255.0;\n\t\t\tent->shaderRGBAf[2] = ((d_8to24srgbtable[ex->skinnum & 0xFF] >> 16) & 0xFF)/255.0;\n\t\t}\n\t\telse if (ex->skinnum < 0)\n\t\t{\n\t\t\tent->skinnum = 7*f/(numframes);\n\t\t}\n\t}\n\n\texplosions_running = lastrunningexplosion + 1;\n}\n\nentity_state_t *CL_FindPacketEntity(int num);\n\n/*\n=================\nCL_UpdateTEnts\n=================\n*/\nvoid CL_UpdateTEnts (void)\n{\n\tstatic float oldtime;\n\tfloat frametime = cl.time - oldtime;\n\tif (frametime < 0 || frametime > 100)\n\t\tframetime = 0;\n\toldtime = cl.time;\n\n\tCL_UpdateBeams (frametime);\n\tCL_UpdateExplosions (frametime);\n\tCL_RunPCustomTEnts ();\n}\n"
  },
  {
    "path": "engine/client/clhl_game.c",
    "content": "#include \"quakedef.h\"\r\n#include \"shader.h\"\r\n\r\n#ifdef _WIN32\r\n#include \"winquake.h\"\r\n#endif\r\n\r\n#ifdef HLCLIENT\r\nextern unsigned int r2d_be_flags;\r\nstruct hlcvar_s *QDECL GHL_CVarGetPointer(char *varname);\r\n\r\n\r\n\r\n#if defined(_MSC_VER)\r\n#\tif _MSC_VER >= 1300\r\n#\t\tdefine __func__ __FUNCTION__\r\n#\telse\r\n#\t\tdefine __func__ \"unknown\"\r\n#\tendif\r\n#else\r\n\t//I hope you're c99 and have a __func__\r\n#endif\r\n\r\n//extern cvar_t temp1;\r\n#define ignore(s) Con_Printf(\"Fixme: \" s \"\\n\")\r\n#define notimpl(l) Con_Printf(\"halflife cl builtin not implemented on line %i\\n\", l)\r\n#define notimpf(f) Con_Printf(\"halflife cl builtin %s not implemented\\n\", f)\r\n#define notimp() Con_Printf(\"halflife cl builtin %s not implemented\\n\", __func__)\r\n#define bi_begin() //if (temp1.ival)Con_Printf(\"enter %s\\n\", __func__)\r\n#define bi_end() //if (temp1.ival)Con_Printf(\"leave %s\\n\", __func__)\r\n#define bi_trace() bi_begin(); bi_end()\r\n\r\n#if HLCLIENT >= 1\r\n#define HLCL_API_VERSION HLCLIENT\r\n#else\r\n#define HLCL_API_VERSION 7\r\n#endif\r\n\r\n\r\n\r\nvoid *vgui_panel;\r\nvoid *(QDECL *vgui_init)(void);\r\nvoid (QDECL *vgui_frame)(void);\r\nvoid (QDECL *vgui_key)(int down, int scan);\r\nvoid (QDECL *vgui_mouse)(int x, int y);\r\n\r\nqboolean VGui_Setup(void)\r\n{\r\n\tvoid *vguidll;\r\n\r\n\tdllfunction_t funcs[] =\r\n\t{\r\n\t\t{(void*)&vgui_init, \"init\"},\r\n\t\t{(void*)&vgui_frame, \"frame\"},\r\n\t\t{(void*)&vgui_key, \"key\"},\r\n\t\t{(void*)&vgui_mouse, \"mouse\"},\r\n\t\t{NULL}\r\n\t};\r\n\r\n\tvguidll = Sys_LoadLibrary(\"vguiwrap\", funcs);\r\n\r\n\tif (vguidll)\r\n\t\tvgui_panel = vgui_init();\r\n\treturn !!vgui_panel;\r\n}\r\n\r\n\r\n\r\n#define HLPIC model_t*\r\n\r\ntypedef struct\r\n{\r\n\tint\tl;\r\n\tint r;\r\n\tint t;\r\n\tint b;\r\n} hlsubrect_t;\r\n\r\ntypedef struct\r\n{\r\n\tvec3_t origin;\r\n\r\n#if HLCL_API_VERSION >= 7\r\n\tvec3_t viewangles;\r\n\tint weapons;\r\n\tfloat fov;\r\n#else\r\n\tfloat viewheight;\r\n\tfloat maxspeed;\r\n\tvec3_t viewangles;\r\n\tvec3_t punchangles;\r\n\tint keys;\r\n\tint weapons;\r\n\tfloat fov;\r\n\tfloat idlescale;\r\n\tfloat mousesens;\r\n#endif\r\n} hllocalclientdata_t;\r\n\r\ntypedef struct\r\n{\r\n\tshort lerpmsecs;\r\n\tqbyte msec;\r\n\t//pad1\r\n\tvec3_t viewangles;\r\n\tfloat forwardmove;\r\n\tfloat sidemove;\r\n\tfloat upmove;\r\n\tqbyte lightlevel;\r\n\t//pad1\r\n\tunsigned short buttons;\r\n\tqbyte impulse;\r\n\tqbyte weaponselect;\r\n\t//pad2\r\n\tint impact_index;\r\n\tvec3_t impact_position;\r\n} hlusercmd_t;\r\n\r\ntypedef struct\r\n{\r\n\tchar name[64];\r\n\tchar sprite[64];\r\n\tint unk;\r\n\tint forscrwidth;\r\n\thlsubrect_t rect;\r\n} hlspriteinf_t;\r\n\r\ntypedef struct \r\n{\r\n\tchar *name;\r\n\tshort ping;\r\n\tqbyte isus;\r\n\tqbyte isspec;\r\n\tqbyte pl;\r\n\t//pad3\r\n\tchar *model;\r\n\tshort tcolour;\r\n\tshort bcolour;\r\n} hlplayerinfo_t;\r\n\r\ntypedef struct\r\n{\r\n\tint size;\r\n\tint width;\r\n\tint height;\r\n\tint flags;\r\n\tint charheight;\r\n\tshort charwidths[256];\r\n} hlscreeninfo_t;\r\n\r\ntypedef struct\r\n{\r\n\tint effect;\r\n\tbyte_vec4_t c1;\r\n\tbyte_vec4_t c2;\r\n\tfloat x;\r\n\tfloat y;\r\n\tfloat fadein;\r\n\tfloat fadeout;\r\n\tfloat holdtime;\r\n\tfloat fxtime;\r\n\tchar *name;\r\n\tchar *message;\r\n} hlmsginfo_t;\r\n\r\ntypedef struct\r\n{\r\n\tHLPIC (QDECL *pic_load) (char *picname);\r\n\tint (QDECL *pic_getnumframes) (HLPIC pic);\r\n\tint (QDECL *pic_getheight) (HLPIC pic, int frame);\r\n\tint (QDECL *pic_getwidth) (HLPIC pic, int frame);\r\n\tvoid (QDECL *pic_select) (HLPIC pic, int r, int g, int b);\r\n\tvoid (QDECL *pic_drawcuropaque) (int frame, int x, int y, void *loc);\r\n\tvoid (QDECL *pic_drawcuralphatest) (int frame, int x, int y, void *loc);\r\n\tvoid (QDECL *pic_drawcuradditive) (int frame, int x, int y, void *loc);\r\n\tvoid (QDECL *pic_enablescissor) (int x, int y, int width, int height);\r\n\tvoid (QDECL *pic_disablescissor) (void);\r\n\thlspriteinf_t *(QDECL *pic_parsepiclist) (char *filename, int *numparsed);\r\n\r\n\tvoid (QDECL *fillrgba) (int x, int y, int width, int height, int r, int g, int b, int a);\r\n\tint (QDECL *getscreeninfo) (hlscreeninfo_t *info);\r\n\tvoid (QDECL *setcrosshair) (HLPIC pic, hlsubrect_t rect, int r, int g, int b);\t//I worry about stuff like this\r\n\t\r\n\tstruct hlcvar_s *(QDECL *cvar_register) (char *name, char *defvalue, int flags);\r\n\tfloat (QDECL *cvar_getfloat) (char *name);\r\n\tchar *(QDECL *cvar_getstring) (char *name);\r\n\r\n\tvoid (QDECL *cmd_register) (char *name, void (*func) (void));\r\n\tvoid (QDECL *hooknetmsg) (char *msgname, void *func);\r\n\tvoid (QDECL *forwardcmd) (char *command);\r\n\tvoid (QDECL *localcmd) (char *command);\r\n\r\n\tvoid (QDECL *getplayerinfo) (int entnum, hlplayerinfo_t *result);\r\n\r\n\tvoid (QDECL *startsound_name) (char *name, float vol);\r\n\tvoid (QDECL *startsound_idx) (int idx, float vol);\r\n\r\n\tvoid (QDECL *anglevectors) (float *ina, float *outf, float *outr, float *outu);\r\n\r\n\thlmsginfo_t *(QDECL *get_message_info) (char *name);\t//translated+scaled+etc intro stuff\r\n\tint (QDECL *drawchar) (int x, int y, int charnum, int r, int g, int b);\r\n\tint (QDECL *drawstring) (int x, int y, char *string);\r\n#if HLCL_API_VERSION >= 7\r\n\tvoid (QDECL *settextcolour) (float r, float b, float g);\r\n#endif\r\n\tvoid (QDECL *drawstring_getlen) (char *string, int *outlen, int *outheight);\r\n\tvoid (QDECL *consoleprint) (char *str);\r\n\tvoid (QDECL *centerprint) (char *str);\r\n\r\n#if HLCL_API_VERSION >= 7\r\n\tint (QDECL *getwindowcenterx)(void);\t//yes, really, window center. for use with Get/SetCursorPos, the windows function.\r\n\tint (QDECL *getwindowcentery)(void);\t//yes, really, window center. for use with Get/SetCursorPos, the windows function.\r\n\tvoid (QDECL *getviewnangles)(float*ang);\r\n\tvoid (QDECL *setviewnangles)(float*ang);\r\n\tvoid (QDECL *getmaxclients)(float*ang);\r\n\tvoid (QDECL *cvar_setvalue)(char *cvarname, char *value);\r\n\r\n\tint (QDECL *cmd_argc)(void);\r\n\tchar *(QDECL *cmd_argv)(int i);\r\n\tvoid (QDECL *con_printf)(char *fmt, ...);\r\n\tvoid (QDECL *con_dprintf)(char *fmt, ...);\r\n\tvoid (QDECL *con_notificationprintf)(int pos, char *fmt, ...);\r\n\tvoid (QDECL *con_notificationprintfex)(void *info, char *fmt, ...);\t//arg1 is of specific type\r\n\tchar *(QDECL *physkey)(char *key);\r\n\tchar *(QDECL *serverkey)(char *key);\r\n\tfloat (QDECL *getclientmaxspeed)(void);\r\n\tint (QDECL *checkparm)(char *str, char **next);\r\n\tint (QDECL *keyevent)(int key, int down);\r\n\tvoid (QDECL *getmousepos)(int *outx, int *outy);\r\n\tint (QDECL *movetypeisnoclip)(void);\r\n\tstruct hlclent_s *(QDECL *getlocalplayer)(void);\r\n\tstruct hlclent_s *(QDECL *getviewent)(void);\r\n\tstruct hlclent_s *(QDECL *getentidx)(int idx);\r\n\tfloat (QDECL *getlocaltime)(void);\r\n\tvoid (QDECL *calcshake)(void);\r\n\tvoid (QDECL *applyshake)(float *,float *,float);\r\n\tint (QDECL *pointcontents)(float *point, float *truecon);\r\n\tint (QDECL *waterentity)(float *point);\r\n\tvoid (QDECL *traceline) (float *start, float *end, int flags, int hull, int forprediction);\r\n\r\n\tmodel_t *(QDECL *loadmodel)(char *modelname, int *mdlindex);\r\n\tint (QDECL *addrentity)(int type, void *ent);\r\n\r\n\tmodel_t *(QDECL *modelfrompic) (HLPIC pic);\r\n\tvoid (QDECL *soundatloc)(char*sound, float volume, float *org);\r\n\r\n\tunsigned short (QDECL *precacheevent)(int evtype, char *name);\r\n\tvoid (QDECL *playevent)(int flags, struct hledict_s *ent, unsigned short evindex, float delay, float *origin, float *angles, float f1, float f2, int i1, int i2, int b1, int b2);\r\n\tvoid (QDECL *weaponanimate)(int anim, int body);\r\n\tfloat (QDECL *randfloat) (float minv, float maxv);\r\n\tlong (QDECL *randlong) (long minv, long maxv);\r\n\tvoid (QDECL *hookevent) (char *name, void (*func)(struct hlevent_s *event));\r\n\tint (QDECL *con_isshown) (void);\r\n\tchar *(QDECL *getgamedir) (void);\r\n\tstruct hlcvar_s *(QDECL *cvar_find) (char *name);\r\n\tchar *(QDECL *lookupbinding) (char *command);\r\n\tchar *(QDECL *getlevelname) (void);\r\n\tvoid (QDECL *getscreenfade) (struct hlsfade_s *fade);\r\n\tvoid (QDECL *setscreenfade) (struct hlsfade_s *fade);\r\n\tvoid *(QDECL *vgui_getpanel) (void);\r\n\tvoid (QDECL *vgui_paintback) (int extents[4]);\r\n\r\n\tvoid *(QDECL *loadfile) (char *path, int onhunk, int *length);\r\n\tchar *(QDECL *parsefile) (char *data, char *token);\r\n\tvoid (QDECL *freefile) (void *file);\r\n\r\n\tstruct hl_tri_api_s\r\n\t{\r\n\t\tint vers;\r\n\t\tint sentinal;\r\n\t} *triapi;\r\n\tstruct hl_sfx_api_s\r\n\t{\r\n\t\tint vers;\r\n\t\tint sentinal;\r\n\t} *efxapi;\r\n\tstruct hl_event_api_s \r\n\t{\r\n\t\tint vers;\r\n\t\tint sentinal;\r\n\t} *eventapi;\r\n\tstruct hl_demo_api_s \r\n\t{\r\n\t\tint (QDECL *isrecording)(void);\r\n\t\tint (QDECL *isplaying)(void);\r\n\t\tint (QDECL *istimedemo)(void);\r\n\t\tvoid (QDECL *writedata)(int size, void *data);\r\n\r\n\t\tint sentinal;\r\n\t} *demoapi;\r\n\tstruct hl_net_api_s \r\n\t{\r\n\t\tint vers;\r\n\t\tint sentinal;\r\n\t} *netapi;\r\n\r\n\tstruct hl_voicetweek_s\r\n\t{\r\n\t\tint sentinal;\r\n\t} *voiceapi;\r\n\r\n\tint (QDECL *forcedspectator) (void);\r\n\tmodel_t *(QDECL *loadmapsprite) (char *name);\r\n\r\n\tvoid (QDECL *fs_addgamedir) (char *basedir, char *appname);\r\n\tint (QDECL *expandfilename) (char *filename, char *outbuff, int outsize);\r\n\r\n\tchar *(QDECL *player_key) (int pnum, char *key);\r\n\tvoid (QDECL *player_setkey) (char *key, char *value);\t//wait, no pnum?\r\n\r\n\tqboolean (QDECL *getcdkey) (int playernum, char key[16]);\r\n\tint (QDECL *trackerfromplayer) (int pl);\r\n\tint (QDECL *playerfromtracker) (int tr);\r\n\tint (QDECL *sendcmd_unreliable) (char *cmd);\r\n\tvoid (QDECL *getsysmousepos) (long *xandy);\r\n\tvoid (QDECL *setsysmousepos) (int x, int y);\r\n\tvoid (QDECL *setmouseenable) (qboolean enable);\r\n#endif\r\n\r\n\tint sentinal;\r\n} CLHL_enginecgamefuncs_t;\r\n\r\n\r\ntypedef struct\r\n{\r\n\tint (QDECL *HUD_VidInit) (void);\r\n\tint (QDECL *HUD_Init) (void);\r\n\tint (QDECL *HUD_Shutdown) (void);\r\n\tint (QDECL *HUD_Redraw) (float maptime, int inintermission);\r\n\tint (QDECL *HUD_UpdateClientData) (hllocalclientdata_t *localclientdata, float maptime);\r\n\tint (QDECL *HUD_Reset) (void);\r\n#if HLCL_API_VERSION >= 7\r\n\tvoid (QDECL *CL_CreateMove) (float frametime, hlusercmd_t *cmd, int isplaying);\r\n\tvoid (QDECL *IN_ActivateMouse) (void);\r\n\tvoid (QDECL *IN_DeactivateMouse) (void);\r\n\tvoid (QDECL *IN_MouseEvent) (int buttonmask);\r\n#endif\r\n} CLHL_cgamefuncs_t;\r\n\r\n\r\n\r\n//FIXME\r\ntypedef struct\r\n{\r\n\tvec3_t\torigin;\r\n\tvec3_t\toldorigin;\r\n\r\n\tint firstframe;\r\n\tint numframes;\r\n\r\n\tint\t\ttype;\r\n\tvec3_t\tangles;\r\n\tint\t\tflags;\r\n\tfloat\talpha;\r\n\tfloat\tstart;\r\n\tfloat\tframerate;\r\n\tmodel_t\t*model;\r\n\tint skinnum;\r\n} explosion_t;\r\n\r\n\r\n\r\n\r\n\r\ntypedef struct\r\n{\r\n\tchar name[64];\r\n\tint (QDECL *hook) (char *name, int bufsize, void *bufdata);\r\n} CLHL_UserMessages_t;\r\nCLHL_UserMessages_t usermsgs[256];\r\n\r\nint numnewhooks;\r\nCLHL_UserMessages_t pendingusermsgs[256];\r\n\r\n\r\nstatic HLPIC selectedpic;\r\n\r\nfloat hl_viewmodelsequencetime;\r\nint hl_viewmodelsequencecur;\r\nint hl_viewmodelsequencebody;\r\n\r\n\r\n\r\n\r\n\r\nHLPIC QDECL CLGHL_pic_load (char *picname)\r\n{\r\n\tvoid *ret;\r\n\tbi_begin();\r\n\tret = Mod_ForName(picname, false);\r\n\tbi_end();\r\n\treturn ret;\r\n//\treturn R2D_SafeCachePic(picname);\r\n}\r\nint QDECL CLGHL_pic_getnumframes (HLPIC pic)\r\n{\r\n\tbi_trace();\r\n\tif (pic)\r\n\t\treturn pic->numframes;\r\n\telse\r\n\t\treturn 0;\r\n}\r\n\r\nstatic mspriteframe_t *getspriteframe(HLPIC pic, int frame)\r\n{\r\n\tmsprite_t\t\t*psprite;\r\n\tmspritegroup_t *pgroup;\r\n\tbi_trace();\r\n\tif (!pic)\r\n\t\treturn NULL;\r\n\tpsprite = pic->meshinfo;\r\n\tif (!psprite)\r\n\t\treturn NULL;\r\n\r\n\tif (psprite->frames[frame].type == SPR_SINGLE)\r\n\t\treturn psprite->frames[frame].frameptr;\r\n\telse\r\n\t{\r\n\t\tpgroup = (mspritegroup_t *)psprite->frames[frame].frameptr;\r\n\t\treturn pgroup->frames[0];\r\n\t}\r\n}\r\nstatic mpic_t *getspritepic(HLPIC pic, int frame)\r\n{\r\n\tmspriteframe_t *f;\r\n\tbi_trace();\r\n\tf = getspriteframe(pic, frame);\r\n\tif (f)\r\n\t\treturn f->shader;\r\n\treturn NULL;\r\n}\r\n\r\nint QDECL CLGHL_pic_getheight (HLPIC pic, int frame)\r\n{\r\n\tmspriteframe_t *pframe;\r\n\tbi_trace();\r\n\r\n\tpframe = getspriteframe(pic, frame);\r\n\tif (!pframe)\r\n\t\treturn 0;\r\n\r\n\treturn pframe->shader->width;\r\n}\r\nint QDECL CLGHL_pic_getwidth (HLPIC pic, int frame)\r\n{\r\n\tmspriteframe_t *pframe;\r\n\tbi_trace();\r\n\r\n\tpframe = getspriteframe(pic, frame);\r\n\tif (!pframe)\r\n\t\treturn 0;\r\n\r\n\treturn pframe->shader->height;\r\n}\r\nvoid QDECL CLGHL_pic_select (HLPIC pic, int r, int g, int b)\r\n{\r\n\tbi_trace();\r\n\tselectedpic = pic;\r\n\tR2D_ImageColours(r/255.0f, g/255.0f, b/255.0f, 1);\r\n}\r\nvoid QDECL CLGHL_pic_drawcuropaque (int frame, int x, int y, hlsubrect_t *loc)\r\n{\r\n\tmpic_t *pic = getspritepic(selectedpic, frame);\r\n\tbi_trace();\r\n\tif (!pic)\r\n\t\treturn;\r\n\r\n\t//faster SW render: no blends/holes\r\n\tpic->flags &= ~1;\r\n\r\n\tR2D_Image(x, y,\r\n\t\tloc->r-loc->l, loc->b-loc->t,\r\n\t\t(float)loc->l/pic->width, (float)loc->t/pic->height,\r\n\t\t(float)loc->r/pic->width, (float)loc->b/pic->height,\r\n\t\tpic);\r\n}\r\nvoid QDECL CLGHL_pic_drawcuralphtest (int frame, int x, int y, hlsubrect_t *loc)\r\n{\r\n\tmpic_t *pic = getspritepic(selectedpic, frame);\r\n\tbi_trace();\r\n\tif (!pic)\r\n\t\treturn;\r\n\t//use some kind of alpha\r\n\tpic->flags |= 1;\r\n\r\n\tR2D_Image(x, y,\r\n\t\tloc->r-loc->l, loc->b-loc->t,\r\n\t\t(float)loc->l/pic->width, (float)loc->t/pic->height,\r\n\t\t(float)loc->r/pic->width, (float)loc->b/pic->height,\r\n\t\tpic);\r\n}\r\nvoid QDECL CLGHL_pic_drawcuradditive (int frame, int x, int y, hlsubrect_t *loc)\r\n{\r\n\tmpic_t *pic = getspritepic(selectedpic, frame);\r\n\r\n\tbi_trace();\r\n\tif (!pic)\r\n\t\treturn;\r\n\r\n\tr2d_be_flags = BEF_FORCEADDITIVE;\r\n\r\n\t//use some kind of alpha\r\n\tpic->flags |= 1;\r\n\tif (loc)\r\n\t{\r\n\t\tR2D_Image(x, y,\r\n\t\t\tloc->r-loc->l, loc->b-loc->t,\r\n\t\t\t(float)loc->l/pic->width, (float)loc->t/pic->height,\r\n\t\t\t(float)loc->r/pic->width, (float)loc->b/pic->height,\r\n\t\t\tpic);\r\n\t}\r\n\telse\r\n\t{\r\n\t\tR2D_Image(x, y,\r\n\t\t\tpic->width, pic->height,\r\n\t\t\t0, 0,\r\n\t\t\t1, 1,\r\n\t\t\tpic);\r\n\t}\r\n\tr2d_be_flags = 0;\r\n}\r\nvoid QDECL CLGHL_pic_enablescissor (int x, int y, int width, int height)\r\n{\r\n\tsrect_t srect;\r\n\r\n\tbi_trace();\r\n\r\n\tsrect.x = x / vid.width;\r\n\tsrect.y = y / vid.height;\r\n\tsrect.width = width / vid.width;\r\n\tsrect.height = height / vid.height;\r\n\tsrect.dmin = -99999;\r\n\tsrect.dmax = 99999;\r\n\tsrect.y = (1-srect.y) - srect.height;\r\n\tBE_Scissor(&srect);\r\n}\r\nvoid QDECL CLGHL_pic_disablescissor (void)\r\n{\r\n\tbi_trace();\r\n\tBE_Scissor(NULL);\r\n}\r\nhlspriteinf_t *QDECL CLGHL_pic_parsepiclist (char *filename, int *numparsed)\r\n{\r\n\thlspriteinf_t *result;\r\n\tint entry;\r\n\tint entries;\r\n\tvoid *file;\r\n\tchar *pos;\r\n\r\n\tbi_trace();\r\n\r\n\t*numparsed = 0;\r\n\r\n\tFS_LoadFile(filename, &file);\r\n\tif (!file)\r\n\t\treturn NULL;\r\n\tpos = file;\r\n\r\n\tpos = COM_Parse(pos);\r\n\tentries = atoi(com_token);\r\n\r\n\t//name, res, pic, x, y, w, h\r\n\r\n\tresult = Z_Malloc(sizeof(*result)*entries);\r\n\tfor (entry = 0; entry < entries; entry++)\r\n\t{\r\n\t\tpos = COM_Parse(pos);\r\n\t\tQ_strncpyz(result[entry].name, com_token, sizeof(result[entry].name));\r\n\r\n\t\tpos = COM_Parse(pos);\r\n\t\tresult[entry].forscrwidth = atoi(com_token);\r\n\r\n\t\tpos = COM_Parse(pos);\r\n\t\tQ_strncpyz(result[entry].sprite, com_token, sizeof(result[entry].name));\r\n\r\n\t\tpos = COM_Parse(pos);\r\n\t\tresult[entry].rect.l = atoi(com_token);\r\n\r\n\t\tpos = COM_Parse(pos);\r\n\t\tresult[entry].rect.t = atoi(com_token);\r\n\r\n\t\tpos = COM_Parse(pos);\r\n\t\tresult[entry].rect.r = result[entry].rect.l+atoi(com_token);\r\n\r\n\t\tpos = COM_Parse(pos);\r\n\t\tresult[entry].rect.b = result[entry].rect.t+atoi(com_token);\r\n\r\n\t\tif (pos)\r\n\t\t\t*numparsed = entry;\r\n\t}\r\n\r\n\tif (!pos || COM_Parse(pos))\r\n\t\tCon_Printf(\"unexpected end of file\\n\");\r\n\r\n\tFS_FreeFile(file);\r\n\r\n\treturn result;\r\n}\r\n\r\nvoid QDECL CLGHL_fillrgba (int x, int y, int width, int height, int r, int g, int b, int a)\r\n{\r\n\tbi_trace();\r\n}\r\nint QDECL CLGHL_getscreeninfo (hlscreeninfo_t *info)\r\n{\r\n\tint i;\r\n\tbi_trace();\r\n\tif (info->size != sizeof(*info))\r\n\t\treturn false;\r\n\r\n\tinfo->width = vid.width;\r\n\tinfo->height = vid.height;\r\n\tinfo->flags = 0;\r\n\tinfo->charheight = 8;\r\n\tfor (i = 0; i < 256; i++)\r\n\t\tinfo->charwidths[i] = 8;\r\n\r\n\treturn true;\r\n}\r\nvoid QDECL CLGHL_setcrosshair (HLPIC pic, hlsubrect_t rect, int r, int g, int b)\r\n{\r\n\tbi_trace();\r\n}\r\n\r\nstruct hlcvar_s *QDECL CLGHL_cvar_register (char *name, char *defvalue, int flags)\r\n{\r\n\tbi_trace();\r\n\tif (Cvar_Get(name, defvalue, 0, \"Halflife cvars\"))\r\n\t\treturn GHL_CVarGetPointer(name);\r\n\telse\r\n\t\treturn NULL;\r\n}\r\nfloat QDECL CLGHL_cvar_getfloat (char *name)\r\n{\r\n\tcvar_t *var = Cvar_FindVar(name);\r\n\tbi_trace();\r\n\tif (var)\r\n\t\treturn var->value;\r\n\treturn 0;\r\n}\r\nchar *QDECL CLGHL_cvar_getstring (char *name)\r\n{\r\n\tcvar_t *var = Cvar_FindVar(name);\r\n\tbi_trace();\r\n\tif (var)\r\n\t\treturn var->string;\r\n\treturn \"\";\r\n}\r\n\r\nvoid QDECL CLGHL_cmd_register (char *name, xcommand_t func)\r\n{\r\n\tbi_trace();\r\n\tCmd_AddCommand(name, func);\r\n}\r\nvoid QDECL CLGHL_hooknetmsg (char *msgname, void *func)\r\n{\r\n\tint i;\r\n\tbi_trace();\r\n\t//update the current list now.\r\n\tfor (i = 0; i < sizeof(usermsgs)/sizeof(usermsgs[0]); i++)\r\n\t{\r\n\t\tif (!strcmp(usermsgs[i].name, msgname))\r\n\t\t{\r\n\t\t\tusermsgs[i].hook = func;\r\n\t\t\tbreak;\t//one per name\r\n\t\t}\r\n\t}\r\n\r\n\t//we already asked for it perhaps?\r\n\tfor (i = 0; i < numnewhooks; i++)\r\n\t{\r\n\t\tif (!strcmp(pendingusermsgs[i].name, msgname))\r\n\t\t{\r\n\t\t\tpendingusermsgs[i].hook = func;\r\n\t\t\treturn;\t//nothing to do\r\n\t\t}\r\n\t}\r\n\r\n\tQ_strncpyz(pendingusermsgs[numnewhooks].name, msgname, sizeof(pendingusermsgs[i].name));\r\n\tpendingusermsgs[numnewhooks].hook = func;\r\n\tnumnewhooks++;\r\n}\r\nvoid QDECL CLGHL_forwardcmd (char *command)\r\n{\r\n\tbi_trace();\r\n\tCL_SendClientCommand(true, \"%s\", command);\r\n}\r\nvoid QDECL CLGHL_localcmd (char *command)\r\n{\r\n\tbi_trace();\r\n\tCbuf_AddText(command, RESTRICT_SERVER);\r\n}\r\n\r\nvoid QDECL CLGHL_getplayerinfo (int entnum, hlplayerinfo_t *result)\r\n{\r\n\tplayer_info_t *player;\r\n\tbi_trace();\r\n\tentnum--;\r\n\tif (entnum < 0 || entnum >= MAX_CLIENTS)\r\n\t\treturn;\r\n\r\n\tplayer = &cl.players[entnum];\r\n\tresult->name = player->name;\r\n\tresult->ping = player->ping;\r\n\tresult->tcolour = player->rtopcolor;\r\n\tresult->bcolour = player->rbottomcolor;\r\n\tresult->isus = true;\r\n\tresult->isspec = player->spectator;\r\n\tresult->pl = player->pl;\r\n\tif (player->qwskin)\r\n\t\tresult->model = player->qwskin->name;\r\n\telse\r\n\t\tresult->model = \"\";\r\n}\r\n\r\nvoid QDECL CLGHL_startsound_name (char *name, float vol)\r\n{\r\n\tsfx_t *sfx = S_PrecacheSound (name);\r\n\tbi_trace();\r\n\tif (!sfx)\r\n\t{\r\n\t\tCon_Printf (\"CLGHL_startsound_name: can't cache %s\\n\", name);\r\n\t\treturn;\r\n\t}\r\n\tS_StartSound (-1, -1, sfx, vec3_origin, vol, 1, 0, 0, 0);\r\n}\r\nvoid QDECL CLGHL_startsound_idx (int idx, float vol)\r\n{\r\n\tsfx_t *sfx = cl.sound_precache[idx];\r\n\tbi_trace();\r\n\tif (!sfx)\r\n\t{\r\n\t\tCon_Printf (\"CLGHL_startsound_name: index not precached %s\\n\", name);\r\n\t\treturn;\r\n\t}\r\n\tS_StartSound (-1, -1, sfx, vec3_origin, vol, 1, 0, 0, 0);\r\n}\r\n\r\nvoid QDECL CLGHL_anglevectors (float *ina, float *outf, float *outr, float *outu)\r\n{\r\n\tbi_trace();\r\n\tAngleVectors(ina, outf, outr, outu);\r\n}\r\n\r\nhlmsginfo_t *QDECL CLGHL_get_message_info (char *name)\r\n{\r\n\t//fixme: add parser for titles.txt\r\n\thlmsginfo_t *ret;\r\n\tbi_trace();\r\n\tret = Z_Malloc(sizeof(*ret));\r\n\tmemset(ret, 0, sizeof(*ret));\r\n\tret->name = name;\r\n\tret->message = name;\r\n\tret->x = -1;\r\n\tret->y = -1;\r\n\t*(int*)&ret->c1 = 0xffffffff;\r\n\t*(int*)&ret->c2 = 0xffffffff;\r\n\tret->effect = 0;\r\n\tret->fadein = 1;\r\n\tret->fadeout = 1.5;\r\n\tret->fxtime = 0;\r\n\tret->holdtime = 3;\r\n\treturn ret;\r\n}\r\nint QDECL CLGHL_drawchar (int x, int y, int charnum, int r, int g, int b)\r\n{\r\n\tbi_trace();\r\n\treturn 0;\r\n}\r\nint QDECL CLGHL_drawstring (int x, int y, char *string)\r\n{\r\n\tbi_trace();\r\n\treturn 0;\r\n}\r\nvoid QDECL CLGHL_settextcolour(float r, float g, float b)\r\n{\r\n\tbi_trace();\r\n}\r\nvoid QDECL CLGHL_drawstring_getlen (char *string, int *outlen, int *outheight)\r\n{\r\n\tbi_trace();\r\n\t*outlen = strlen(string)*8;\r\n\t*outheight = 8;\r\n}\r\nvoid QDECL CLGHL_consoleprint (char *str)\r\n{\r\n\tbi_trace();\r\n\tCon_Printf(\"%s\", str);\r\n}\r\nvoid QDECL CLGHL_centerprint (char *str)\r\n{\r\n\tbi_trace();\r\n\tSCR_CenterPrint(0, str, true);\r\n}\r\n\r\n\r\nint QDECL CLGHL_getwindowcenterx(void)\r\n{\r\n\tbi_trace();\r\n\treturn window_center_x;\r\n}\r\nint QDECL CLGHL_getwindowcentery(void)\r\n{\r\n\tbi_trace();\r\n\treturn window_center_y;\r\n}\r\nvoid QDECL CLGHL_getviewnangles(float*ang)\r\n{\r\n\tbi_trace();\r\n\tVectorCopy(cl.playerview[0].viewangles, ang);\r\n}\r\nvoid QDECL CLGHL_setviewnangles(float*ang)\r\n{\r\n\tbi_trace();\r\n\tVectorCopy(ang, cl.playerview[0].viewangles);\r\n}\r\nvoid QDECL CLGHL_getmaxclients(float*ang){notimpf(__func__);}\r\nvoid QDECL CLGHL_cvar_setvalue(char *cvarname, char *value){notimpf(__func__);}\r\n\r\nint QDECL CLGHL_cmd_argc(void)\r\n{\r\n\tbi_trace();\r\n\treturn Cmd_Argc();\r\n}\r\nchar *QDECL CLGHL_cmd_argv(int i)\r\n{\r\n\tbi_trace();\r\n\treturn Cmd_Argv(i);\r\n}\r\n#define CLGHL_con_printf Con_Printf\r\n#define CLGHL_con_dprintf Con_DPrintf\r\nvoid QDECL CLGHL_con_notificationprintf(int pos, char *fmt, ...){notimpf(__func__);}\r\nvoid QDECL CLGHL_con_notificationprintfex(void *info, char *fmt, ...){notimpf(__func__);}\r\nchar *QDECL CLGHL_physkey(char *key){notimpf(__func__);return NULL;}\r\nchar *QDECL CLGHL_serverkey(char *key){notimpf(__func__);return NULL;}\r\nfloat QDECL CLGHL_getclientmaxspeed(void)\r\n{\r\n\tbi_trace();\r\n\treturn 320;\r\n}\r\nint QDECL CLGHL_checkparm(char *str, const char **next)\r\n{\r\n\tint i;\r\n\tbi_trace();\r\n\ti = COM_CheckParm(str);\r\n\tif (next)\r\n\t{\r\n\t\tif (i && i+1<com_argc)\r\n\t\t\t*next = com_argv[i+1];\r\n\t\telse\r\n\t\t\t*next = NULL;\r\n\t}\r\n\treturn i;\r\n}\r\nint QDECL CLGHL_keyevent(int key, int down)\r\n{\r\n\tbi_trace();\r\n\tif (key >= 241 && key <= 241+5)\r\n\t\tKey_Event(0, K_MOUSE1+key-241, 0, down);\r\n\telse\r\n\t\tCon_Printf(\"CLGHL_keyevent: Unrecognised HL key code\\n\");\r\n\treturn true;\t//fixme: check the return type\r\n}\r\nvoid QDECL CLGHL_getmousepos(int *outx, int *outy){notimpf(__func__);}\r\nint QDECL CLGHL_movetypeisnoclip(void)\r\n{\r\n\tbi_trace();\r\n\tif (cl.playerview[0].pmovetype == PM_SPECTATOR)\r\n\t\treturn true;\r\n\treturn false;\r\n}\r\nstruct hlclent_s *QDECL CLGHL_getlocalplayer(void){notimpf(__func__);return NULL;}\r\nstruct hlclent_s *QDECL CLGHL_getviewent(void){notimpf(__func__);return NULL;}\r\nstruct hlclent_s *QDECL CLGHL_getentidx(int idx)\r\n{\r\n\tbi_trace();\r\n\tnotimpf(__func__);return NULL;\r\n}\r\nfloat QDECL CLGHL_getlocaltime(void){return cl.time;}\r\nvoid QDECL CLGHL_calcshake(void){notimpf(__func__);}\r\nvoid QDECL CLGHL_applyshake(float *origin, float *angles, float factor){notimpf(__func__);}\r\nint QDECL CLGHL_pointcontents(float *point, float *truecon){notimpf(__func__);return 0;}\r\nint QDECL CLGHL_entcontents(float *point){notimpf(__func__);return 0;}\r\nvoid QDECL CLGHL_traceline(float *start, float *end, int flags, int hull, int forprediction){notimpf(__func__);}\r\n\r\nmodel_t *QDECL CLGHL_loadmodel(char *modelname, int *mdlindex){notimpf(__func__);return Mod_ForName(modelname, false);}\r\nint QDECL CLGHL_addrentity(int type, void *ent){notimpf(__func__);return 0;}\r\n\r\nmodel_t *QDECL CLGHL_modelfrompic(HLPIC pic){notimpf(__func__);return NULL;}\r\nvoid QDECL CLGHL_soundatloc(char*sound, float volume, float *org){notimpf(__func__);}\r\n\r\nunsigned short QDECL CLGHL_precacheevent(int evtype, char *name){notimpf(__func__);return 0;}\r\nvoid QDECL CLGHL_playevent(int flags, struct hledict_s *ent, unsigned short evindex, float delay, float *origin, float *angles, float f1, float f2, int i1, int i2, int b1, int b2){notimpf(__func__);}\r\nvoid QDECL CLGHL_weaponanimate(int newsequence, int body)\r\n{\r\n\tbi_trace();\r\n\thl_viewmodelsequencetime = cl.time;\r\n\thl_viewmodelsequencecur = newsequence;\r\n\thl_viewmodelsequencebody = body;\r\n}\r\nfloat QDECL CLGHL_randfloat(float minv, float maxv){notimpf(__func__);return minv;}\r\nlong QDECL CLGHL_randlong(long minv, long maxv){notimpf(__func__);return minv;}\r\nvoid QDECL CLGHL_hookevent(char *name, void (*func)(struct hlevent_s *event))\r\n{\r\n\tbi_trace();\r\n\tCon_Printf(\"CLGHL_hookevent: not implemented. %s\\n\", name);\r\n//\tnotimpf(__func__);\r\n}\r\nint QDECL CLGHL_con_isshown(void)\r\n{\r\n\tbi_trace();\r\n\treturn scr_con_current > 0;\r\n}\r\nchar *QDECL CLGHL_getgamedir(void)\r\n{\r\n\textern char\tgamedirfile[];\r\n\tbi_trace();\r\n\treturn gamedirfile;\r\n}\r\nstruct hlcvar_s *QDECL CLGHL_cvar_find(char *name)\r\n{\r\n\tbi_trace();\r\n\treturn GHL_CVarGetPointer(name);\r\n}\r\nchar *QDECL CLGHL_lookupbinding(char *command)\r\n{\r\n\tbi_trace();\r\n\treturn NULL;\r\n}\r\nchar *QDECL CLGHL_getlevelname(void)\r\n{\r\n\tbi_trace();\r\n\tif (!cl.worldmodel)\r\n\t\treturn \"\";\r\n\treturn cl.worldmodel->name;\r\n}\r\nvoid QDECL CLGHL_getscreenfade(struct hlsfade_s *fade){notimpf(__func__);}\r\nvoid QDECL CLGHL_setscreenfade(struct hlsfade_s *fade){notimpf(__func__);}\r\nvoid *QDECL CLGHL_vgui_getpanel(void)\r\n{\r\n\tbi_trace();\r\n\treturn vgui_panel;\r\n}\r\nvoid QDECL CLGHL_vgui_paintback(int extents[4])\r\n{\r\n\tbi_trace();\r\n\tnotimpf(__func__);\r\n}\r\n\r\nvoid *QDECL CLGHL_loadfile(char *path, int alloctype, int *length)\r\n{\r\n\tvoid *ptr = NULL;\r\n\tint flen = -1;\r\n\tbi_trace();\r\n\tif (alloctype == 5)\r\n\t{\r\n\t\tflen = FS_LoadFile(path, &ptr);\r\n\t}\r\n\telse\r\n\t\tnotimpf(__func__);\t//don't leak, just fail\r\n\r\n\tif (length)\r\n\t\t*length = flen;\r\n\r\n\treturn ptr;\r\n}\r\nchar *QDECL CLGHL_parsefile(char *data, char *token)\r\n{\r\n\tbi_trace();\r\n\treturn COM_ParseOut(data, token, 1024);\r\n}\r\nvoid QDECL CLGHL_freefile(void *file)\r\n{\r\n\tbi_trace();\r\n\t//only valid for alloc type 5\r\n\tFS_FreeFile(file);\r\n}\r\n\r\n\r\nint QDECL CLGHL_forcedspectator(void)\r\n{\r\n\tbi_trace();\r\n\treturn cls.demoplayback;\r\n}\r\nmodel_t *QDECL CLGHL_loadmapsprite(char *name)\r\n{\r\n\tbi_trace();\r\n\tnotimpf(__func__);return NULL;\r\n}\r\n\r\nvoid QDECL CLGHL_fs_addgamedir(char *basedir, char *appname){notimpf(__func__);}\r\nint QDECL CLGHL_expandfilename(char *filename, char *outbuff, int outsize){notimpf(__func__);return false;}\r\n\r\nchar *QDECL CLGHL_player_key(int pnum, char *key){notimpf(__func__);return NULL;}\r\nvoid QDECL CLGHL_player_setkey(char *key, char *value){notimpf(__func__);return;}\r\n\r\nqboolean QDECL CLGHL_getcdkey(int playernum, char key[16]){notimpf(__func__);return false;}\r\nint QDECL CLGHL_trackerfromplayer(int pslot){notimpf(__func__);return 0;}\r\nint QDECL CLGHL_playerfromtracker(int tracker){notimpf(__func__);return 0;}\r\nint QDECL CLGHL_sendcmd_unreliable(char *cmd){notimpf(__func__);return 0;}\r\nvoid QDECL CLGHL_getsysmousepos(long *xandy)\r\n{\r\n\tbi_trace();\r\n#ifdef _WIN32\r\n\tGetCursorPos((LPPOINT)xandy);\r\n#endif\r\n}\r\nvoid QDECL CLGHL_setsysmousepos(int x, int y)\r\n{\r\n\tbi_trace();\r\n#ifdef _WIN32\r\n\tSetCursorPos(x, y);\r\n#endif\r\n}\r\nvoid QDECL CLGHL_setmouseenable(qboolean enable)\r\n{\r\n\tbi_trace();\r\n\tCvar_Set(&in_windowed_mouse, enable?\"1\":\"0\");\r\n}\r\n\r\n\r\n\r\n#if HLCL_API_VERSION >= 7\r\nint QDECL CLGHL_demo_isrecording(void)\r\n{\r\n\tbi_trace();\r\n\treturn cls.demorecording;\r\n}\r\nint QDECL CLGHL_demo_isplaying(void)\r\n{\r\n\tbi_trace();\r\n\treturn cls.demoplayback;\r\n}\r\nint QDECL CLGHL_demo_istimedemo(void)\r\n{\r\n\tbi_trace();\r\n\treturn cls.timedemo;\r\n}\r\nvoid QDECL CLGHL_demo_writedata(int size, void *data)\r\n{\r\n\tbi_trace();\r\n\tnotimpf(__func__);\r\n}\r\n\r\nstruct hl_demo_api_s hl_demo_api = \r\n{\r\n\tCLGHL_demo_isrecording,\r\n\tCLGHL_demo_isplaying,\r\n\tCLGHL_demo_istimedemo,\r\n\tCLGHL_demo_writedata,\r\n\r\n\t0xdeadbeef\r\n};\r\n#endif\r\n\r\nCLHL_cgamefuncs_t CLHL_cgamefuncs;\r\nCLHL_enginecgamefuncs_t CLHL_enginecgamefuncs =\r\n{\r\n\tCLGHL_pic_load,\r\n\tCLGHL_pic_getnumframes,\r\n\tCLGHL_pic_getheight,\r\n\tCLGHL_pic_getwidth,\r\n\tCLGHL_pic_select,\r\n\tCLGHL_pic_drawcuropaque,\r\n\tCLGHL_pic_drawcuralphtest,\r\n\tCLGHL_pic_drawcuradditive,\r\n\tCLGHL_pic_enablescissor,\r\n\tCLGHL_pic_disablescissor,\r\n\tCLGHL_pic_parsepiclist,\r\n\r\n\tCLGHL_fillrgba,\r\n\tCLGHL_getscreeninfo,\r\n\tCLGHL_setcrosshair,\r\n\r\n\tCLGHL_cvar_register,\r\n\tCLGHL_cvar_getfloat,\r\n\tCLGHL_cvar_getstring,\r\n\r\n\tCLGHL_cmd_register,\r\n\tCLGHL_hooknetmsg,\r\n\tCLGHL_forwardcmd,\r\n\tCLGHL_localcmd,\r\n\r\n\tCLGHL_getplayerinfo,\r\n\r\n\tCLGHL_startsound_name,\r\n\tCLGHL_startsound_idx,\r\n\r\n\tCLGHL_anglevectors,\r\n\r\n\tCLGHL_get_message_info,\r\n\tCLGHL_drawchar,\r\n\tCLGHL_drawstring,\r\n#if HLCL_API_VERSION >= 7\r\n\tCLGHL_settextcolour,\r\n#endif\r\n\tCLGHL_drawstring_getlen,\r\n\tCLGHL_consoleprint,\r\n\tCLGHL_centerprint,\r\n\r\n#if HLCL_API_VERSION >= 7\r\n\tCLGHL_getwindowcenterx,\r\n\tCLGHL_getwindowcentery,\r\n\tCLGHL_getviewnangles,\r\n\tCLGHL_setviewnangles,\r\n\tCLGHL_getmaxclients,\r\n\tCLGHL_cvar_setvalue,\r\n\r\n\tCLGHL_cmd_argc,\r\n\tCLGHL_cmd_argv,\r\n\tCLGHL_con_printf,\r\n\tCLGHL_con_dprintf,\r\n\tCLGHL_con_notificationprintf,\r\n\tCLGHL_con_notificationprintfex,\r\n\tCLGHL_physkey,\r\n\tCLGHL_serverkey,\r\n\tCLGHL_getclientmaxspeed,\r\n\tCLGHL_checkparm,\r\n\tCLGHL_keyevent,\r\n\tCLGHL_getmousepos,\r\n\tCLGHL_movetypeisnoclip,\r\n\tCLGHL_getlocalplayer,\r\n\tCLGHL_getviewent,\r\n\tCLGHL_getentidx,\r\n\tCLGHL_getlocaltime,\r\n\tCLGHL_calcshake,\r\n\tCLGHL_applyshake,\r\n\tCLGHL_pointcontents,\r\n\tCLGHL_entcontents,\r\n\tCLGHL_traceline,\r\n\r\n\tCLGHL_loadmodel,\r\n\tCLGHL_addrentity,\r\n\r\n\tCLGHL_modelfrompic,\r\n\tCLGHL_soundatloc,\r\n\r\n\tCLGHL_precacheevent,\r\n\tCLGHL_playevent,\r\n\tCLGHL_weaponanimate,\r\n\tCLGHL_randfloat,\r\n\tCLGHL_randlong,\r\n\tCLGHL_hookevent,\r\n\tCLGHL_con_isshown,\r\n\tCLGHL_getgamedir,\r\n\tCLGHL_cvar_find,\r\n\tCLGHL_lookupbinding,\r\n\tCLGHL_getlevelname,\r\n\tCLGHL_getscreenfade,\r\n\tCLGHL_setscreenfade,\r\n\tCLGHL_vgui_getpanel,\r\n\tCLGHL_vgui_paintback,\r\n\r\n\tCLGHL_loadfile,\r\n\tCLGHL_parsefile,\r\n\tCLGHL_freefile,\r\n\r\n\tNULL, //triapi;\r\n\tNULL, //efxapi;\r\n\tNULL, //eventapi;\r\n\t&hl_demo_api,\r\n\tNULL, //netapi;\r\n\r\n//sdk 2.3+\r\n\tNULL, //voiceapi;\r\n\r\n\tCLGHL_forcedspectator,\r\n\tCLGHL_loadmapsprite,\r\n\r\n\tCLGHL_fs_addgamedir,\r\n\tCLGHL_expandfilename,\r\n\r\n\tCLGHL_player_key,\r\n\tCLGHL_player_setkey,\r\n\r\n\tCLGHL_getcdkey,\r\n\tCLGHL_trackerfromplayer,\r\n\tCLGHL_playerfromtracker,\r\n\tCLGHL_sendcmd_unreliable,\r\n\tCLGHL_getsysmousepos,\r\n\tCLGHL_setsysmousepos,\r\n\tCLGHL_setmouseenable,\r\n#endif\r\n\r\n\t0xdeadbeef\r\n};\r\n\r\ndllhandle_t *clg;\r\n\r\nint CLHL_GamecodeDoesMouse(void)\r\n{\r\n#if HLCL_API_VERSION >= 7\r\n\tif (clg && CLHL_cgamefuncs.CL_CreateMove)\r\n\t\treturn true;\r\n#endif\r\n\treturn false;\r\n}\r\n\r\nint CLHL_MouseEvent(unsigned int buttonmask)\r\n{\r\n#if HLCL_API_VERSION >= 7\r\n\tif (!CLHL_GamecodeDoesMouse())\r\n\t\treturn false;\r\n\r\n\tCLHL_cgamefuncs.IN_MouseEvent(buttonmask);\r\n\treturn true;\r\n#else\r\n\treturn false;\r\n#endif\r\n}\r\n\r\nvoid CLHL_SetMouseActive(int activate)\r\n{\r\n#if HLCL_API_VERSION >= 7\r\n\tstatic int oldactive;\r\n\tif (!clg)\r\n\t{\r\n\t\toldactive = false;\r\n\t\treturn;\r\n\t}\r\n\tif (activate == oldactive)\r\n\t\treturn;\r\n\toldactive = activate;\r\n\r\n\tif (activate)\r\n\t{\r\n\t\tif (CLHL_cgamefuncs.IN_ActivateMouse)\r\n\t\t\tCLHL_cgamefuncs.IN_ActivateMouse();\r\n\t}\r\n\telse\r\n\t{\r\n\t\tif (CLHL_cgamefuncs.IN_DeactivateMouse)\r\n\t\t\tCLHL_cgamefuncs.IN_DeactivateMouse();\r\n\t}\r\n#endif\r\n}\r\n\r\nvoid CLHL_UnloadClientGame(void)\r\n{\r\n\tif (!clg)\r\n\t\treturn;\r\n\r\n\tCLHL_SetMouseActive(false);\r\n\tif (CLHL_cgamefuncs.HUD_Shutdown)\r\n\t\tCLHL_cgamefuncs.HUD_Shutdown();\r\n\tSys_CloseLibrary(clg);\r\n\tmemset(&CLHL_cgamefuncs, 0, sizeof(CLHL_cgamefuncs));\r\n\tclg = NULL;\r\n\r\n\thl_viewmodelsequencetime = 0;\r\n}\r\n\r\nvoid CLHL_LoadClientGame(void)\r\n{\r\n\tchar fullname[MAX_OSPATH];\r\n\tchar path[MAX_OSPATH];\r\n\tvoid *iterator;\r\n\r\n\tint (QDECL *initfunc)(CLHL_enginecgamefuncs_t *funcs, int version);\r\n\tdllfunction_t funcs[] =\r\n\t{\r\n\t\t{(void*)&initfunc, \"Initialize\"},\r\n\t\t{NULL}\r\n\t};\r\n\r\n\tCLHL_UnloadClientGame();\r\n\r\n\tmemset(&CLHL_cgamefuncs, 0, sizeof(CLHL_cgamefuncs));\r\n\r\n\tclg = NULL;\r\n\titerator = NULL;\r\n\t//FIXME: dlls in gamepaths is evil \r\n\twhile(COM_IteratePaths(&iterator, path, sizeof(path), NULL, 0))\r\n\t{\r\n\t\tsnprintf (fullname, sizeof(fullname), \"%s%s\", path, \"cl_dlls/client\");\r\n\t\tclg = Sys_LoadLibrary(fullname, funcs);\r\n\t\tif (clg)\r\n\t\t\tbreak;\r\n\t}\r\n\r\n\tif (!clg)\r\n\t\treturn;\r\n\r\n\tif (!initfunc(&CLHL_enginecgamefuncs, HLCL_API_VERSION))\r\n\t{\r\n\t\tCon_Printf(\"HalfLife cldll is not version %i\\n\", HLCL_API_VERSION);\r\n\t\tSys_CloseLibrary(clg);\r\n\t\tclg = NULL;\r\n\t\treturn;\r\n\t}\r\n\r\n\tCLHL_cgamefuncs.HUD_VidInit = (void*)Sys_GetAddressForName(clg, \"HUD_VidInit\");\r\n\tCLHL_cgamefuncs.HUD_Init = (void*)Sys_GetAddressForName(clg, \"HUD_Init\");\r\n\tCLHL_cgamefuncs.HUD_Shutdown = (void*)Sys_GetAddressForName(clg, \"HUD_Shutdown\");\r\n\tCLHL_cgamefuncs.HUD_Redraw = (void*)Sys_GetAddressForName(clg, \"HUD_Redraw\");\r\n\tCLHL_cgamefuncs.HUD_UpdateClientData = (void*)Sys_GetAddressForName(clg, \"HUD_UpdateClientData\");\r\n\tCLHL_cgamefuncs.HUD_Reset = (void*)Sys_GetAddressForName(clg, \"HUD_Reset\");\r\n#if HLCL_API_VERSION >= 7\r\n\tCLHL_cgamefuncs.CL_CreateMove = (void*)Sys_GetAddressForName(clg, \"CL_CreateMove\");\r\n\tCLHL_cgamefuncs.IN_ActivateMouse = (void*)Sys_GetAddressForName(clg, \"IN_ActivateMouse\");\r\n\tCLHL_cgamefuncs.IN_DeactivateMouse = (void*)Sys_GetAddressForName(clg, \"IN_DeactivateMouse\");\r\n\tCLHL_cgamefuncs.IN_MouseEvent = (void*)Sys_GetAddressForName(clg, \"IN_MouseEvent\");\r\n#endif\r\n\r\n\tVGui_Setup();\r\n\r\n\tif (CLHL_cgamefuncs.HUD_Init)\r\n\t\tCLHL_cgamefuncs.HUD_Init();\r\n\tif (CLHL_cgamefuncs.HUD_VidInit)\r\n\t\tCLHL_cgamefuncs.HUD_VidInit();\r\n\t{\r\n\t\tstruct hlkbutton_s\r\n\t\t{\r\n\t\t\tint\t\tdown[2];\t\t// key nums holding it down\r\n\t\t\tint\t\tstate;\t\t\t// low bit is down state\r\n\t\t} *but;\r\n\t\tstruct hlkbutton_s *(QDECL *pKB_Find) (const char *foo) = (void*)Sys_GetAddressForName(clg, \"KB_Find\");\r\n\t\tif (pKB_Find)\r\n\t\t{\r\n\t\t\tbut = pKB_Find(\"in_mlook\");\r\n\t\t\tif (but)\r\n\t\t\t\tbut->state |= 1;\r\n\t\t}\r\n\t}\r\n}\r\n\r\nint CLHL_BuildUserInput(int msecs, usercmd_t *cmd)\r\n{\r\n#if HLCL_API_VERSION >= 7\r\n\thlusercmd_t hlcmd;\r\n\tif (!clg || !CLHL_cgamefuncs.CL_CreateMove)\r\n\t\treturn false;\r\n\r\n\tCLHL_cgamefuncs.CL_CreateMove(msecs/255.0f, &hlcmd, cls.state>=ca_active && !cls.demoplayback);\r\n\r\n#define ANGLE2SHORT(x) (x) * (65536/360.0)\r\n\tcmd->msec = msecs;\r\n\tcmd->angles[0] = ANGLE2SHORT(hlcmd.viewangles[0]);\r\n\tcmd->angles[1] = ANGLE2SHORT(hlcmd.viewangles[1]);\r\n\tcmd->angles[2] = ANGLE2SHORT(hlcmd.viewangles[2]);\r\n\tcmd->forwardmove = hlcmd.forwardmove;\r\n\tcmd->sidemove = hlcmd.sidemove;\r\n\tcmd->upmove = hlcmd.upmove;\r\n\tcmd->weapon = hlcmd.weaponselect;\r\n\tcmd->impulse = hlcmd.impulse;\r\n\tcmd->buttons = hlcmd.buttons & 0xff;\t//FIXME: quake's protocols are more limited than this\r\n\tcmd->lightlevel = hlcmd.lightlevel;\r\n\treturn true;\r\n#else\r\n\treturn false;\r\n#endif\r\n}\r\n\r\nint CLHL_DrawHud(void)\r\n{\r\n\textern kbutton_t in_attack;\r\n\thllocalclientdata_t state;\r\n\tint ret;\r\n\r\n\tif (!clg || !CLHL_cgamefuncs.HUD_Redraw)\r\n\t\treturn false;\r\n\r\n\tmemset(&state, 0, sizeof(state));\r\n\r\n//\tstate.origin;\r\n//\tstate.viewangles;\r\n#if HLCL_API_VERSION < 7\r\n//\tstate.viewheight;\r\n//\tstate.maxspeed;\r\n//\tstate.punchangles;\r\n\tstate.idlescale = 0;\r\n\tstate.mousesens = 0;\r\n\tstate.keys = (in_attack.state[0]&3)?1:0;\r\n#endif\r\n\tstate.weapons = cl.playerview[0].stats[STAT_ITEMS];\r\n\tstate.fov = 90;\r\n\r\n\tV_StopPitchDrift(&cl.playerview[0]);\r\n\r\n\tCLHL_cgamefuncs.HUD_UpdateClientData(&state, cl.time);\r\n\r\n\tret = CLHL_cgamefuncs.HUD_Redraw(cl.time, cl.intermissionmode != IM_NONE);\t\r\n\treturn ret;\r\n}\r\n\r\nint CLHL_AnimateViewEntity(entity_t *ent)\r\n{\r\n\tfloat time;\r\n\tif (!hl_viewmodelsequencetime)\r\n\t\treturn false;\r\n\r\n\ttime = cl.time - hl_viewmodelsequencetime;\r\n\tent->framestate.g[FS_REG].frame[0] = hl_viewmodelsequencecur;\r\n\tent->framestate.g[FS_REG].frame[1] = hl_viewmodelsequencecur;\r\n\tent->framestate.g[FS_REG].frametime[0] = time;\r\n\tent->framestate.g[FS_REG].frametime[1] = time;\r\n\treturn true;\r\n}\r\n\r\nexplosion_t *CL_AllocExplosion (vec3_t org);\r\n\r\nint CLHL_ParseGamePacket(void)\r\n{\r\n\tint subcode;\r\n\tchar *end;\r\n\tchar *str;\r\n\tint tempi;\r\n\r\n\tend = net_message.data+msg_readcount+2+MSG_ReadShort();\r\n\r\n\tif (end > net_message.data+net_message.cursize)\r\n\t\treturn false;\r\n\r\n\tsubcode = MSG_ReadByte();\r\n\r\n\tif (usermsgs[subcode].hook)\r\n\t\tif (usermsgs[subcode].hook(usermsgs[subcode].name, end - (net_message.data+msg_readcount), net_message.data+msg_readcount))\r\n\t\t{\r\n\t\t\tmsg_readcount = end - net_message.data;\r\n\t\t\treturn true;\r\n\t\t}\r\n\r\n\tswitch(subcode)\r\n\t{\r\n\tcase 1:\r\n\t\t//register the server-sent code.\r\n\t\ttempi = MSG_ReadByte();\r\n\t\tstr = MSG_ReadString();\r\n\t\tQ_strncpyz(usermsgs[tempi].name, str, sizeof(usermsgs[tempi].name));\r\n\r\n\t\t//get the builtin to reregister its hooks.\r\n\t\tfor (tempi = 0; tempi < numnewhooks; tempi++)\r\n\t\t\tCLGHL_hooknetmsg(pendingusermsgs[tempi].name, pendingusermsgs[tempi].hook);\r\n\t\tbreak;\r\n\tcase svc_temp_entity:\r\n\t\t{\r\n\t\t\tvec3_t startp, endp, vel;\r\n\t\t\tfloat ang;\r\n\t\t\tint midx;\r\n\t\t\tint mscale;\r\n\t\t\tint mrate;\r\n\t\t\tint flags;\r\n\t\t\tint lifetime;\r\n\t\t\texplosion_t *ef;\r\n\r\n\t\t\tsubcode = MSG_ReadByte();\r\n\r\n\t\t\tswitch(subcode)\r\n\t\t\t{\r\n\t\t\tcase 3:\r\n\t\t\t\tMSG_ReadPos(startp);\r\n\t\t\t\tmidx = MSG_ReadShort();\r\n\t\t\t\tmscale = MSG_ReadByte();\r\n\t\t\t\tmrate = MSG_ReadByte();\r\n\t\t\t\tflags = MSG_ReadByte();\r\n\t\t\t\tif (!(flags & 8))\r\n\t\t\t\t\tP_RunParticleEffectType(startp, NULL, 1, pt_explosion);\r\n\t\t\t\tif (!(flags & 4))\r\n\t\t\t\t\tS_StartSound(0, 0, S_PrecacheSound(\"explosion\"), startp, 1, 1, 0, 0, 0);\r\n\t\t\t\tif (!(flags & 2))\r\n\t\t\t\t\tCL_NewDlight(0, startp, 200, 1, 2.0,2.0,2.0);\r\n\r\n\t\t\t\tef = CL_AllocExplosion(startp);\r\n\t\t\t\tef->start = cl.time;\r\n\t\t\t\tef->model = cl.model_precache[midx];\r\n\t\t\t\tef->framerate = mrate;\r\n\t\t\t\tef->firstframe = 0;\r\n\t\t\t\tef->numframes = ef->model->numframes;\r\n\t\t\t\tif (!(flags & 1))\r\n\t\t\t\t\tef->flags = RF_ADDITIVE;\r\n\t\t\t\telse\r\n\t\t\t\t\tef->flags = 0;\r\n\t\t\t\tbreak;\r\n\t\t\tcase 4:\r\n\t\t\t\tMSG_ReadPos(startp);\r\n\t\t\t\tP_RunParticleEffectType(startp, NULL, 1, pt_tarexplosion);\r\n\t\t\t\tbreak;\r\n\t\t\tcase 5:\r\n\t\t\t\tMSG_ReadPos(startp);\r\n\t\t\t\tMSG_ReadShort();\r\n\t\t\t\tMSG_ReadByte();\r\n\t\t\t\tMSG_ReadByte();\r\n\t\t\t\tbreak;\r\n\t\t\tcase 6:\r\n\t\t\t\tMSG_ReadPos(startp);\r\n\t\t\t\tMSG_ReadPos(endp);\r\n\t\t\t\tbreak;\r\n\t\t\tcase 9:\r\n\t\t\t\tMSG_ReadPos(startp);\r\n\t\t\t\tP_RunParticleEffectType(startp, NULL, 1, ptdp_spark);\r\n\t\t\t\tbreak;\r\n\t\t\tcase 22:\r\n\t\t\t\tMSG_ReadShort();\r\n\t\t\t\tMSG_ReadShort();\r\n\t\t\t\tMSG_ReadByte();\r\n\t\t\t\tMSG_ReadByte();\r\n\t\t\t\tMSG_ReadByte();\r\n\t\t\t\tMSG_ReadByte();\r\n\t\t\t\tMSG_ReadByte();\r\n\t\t\t\tbreak;\r\n\t\t\tcase 23:\r\n\t\t\t\tMSG_ReadPos(startp);\r\n\t\t\t\tMSG_ReadShort();\r\n\t\t\t\tMSG_ReadByte();\r\n\t\t\t\tMSG_ReadByte();\r\n\t\t\t\tMSG_ReadByte();\r\n\t\t\t\tbreak;\r\n\t\t\tcase 106:\r\n\t\t\t\tMSG_ReadPos(startp);\r\n\t\t\t\tMSG_ReadPos(vel);\r\n\t\t\t\tang = MSG_ReadAngle();\r\n\t\t\t\tmidx = MSG_ReadShort();\r\n\t\t\t\tMSG_ReadByte();\r\n\t\t\t\tlifetime = MSG_ReadByte();\r\n\r\n\t\t\t\tef = CL_AllocExplosion(startp);\r\n\t\t\t\tef->start = cl.time;\r\n\t\t\t\tef->angles[1] = ang;\r\n\t\t\t\tef->model = cl.model_precache[midx];\r\n\t\t\t\tef->firstframe = 0;\r\n\t\t\t\tef->numframes = lifetime;\r\n\t\t\t\tef->flags = 0;\r\n\t\t\t\tef->framerate = 10;\r\n\t\t\t\tbreak;\r\n\t\t\tcase 108:\r\n\t\t\t\tMSG_ReadPos(startp);\r\n\t\t\t\tMSG_ReadPos(endp);\r\n\t\t\t\tMSG_ReadPos(vel);\r\n\t\t\t\tMSG_ReadByte();\r\n\t\t\t\tMSG_ReadShort();\r\n\t\t\t\tMSG_ReadByte();\r\n\t\t\t\tMSG_ReadByte();\r\n\t\t\t\tMSG_ReadByte();\r\n\t\t\t\tbreak;\r\n\t\t\tcase 109:\r\n\t\t\t\tMSG_ReadPos(startp);\r\n\t\t\t\tMSG_ReadShort();\r\n\t\t\t\tMSG_ReadByte();\r\n\t\t\t\tbreak;\r\n\t\t\tcase 116:\r\n\t\t\t\tMSG_ReadPos(startp);\r\n\t\t\t\tMSG_ReadByte();\r\n\t\t\t\tbreak;\r\n\t\t\tcase 117:\r\n\t\t\t\tMSG_ReadPos(startp);\r\n\t\t\t\tMSG_ReadByte()+256;\r\n\t\t\t\tbreak;\r\n\t\t\tcase 118:\r\n\t\t\t\tMSG_ReadPos(startp);\r\n\t\t\t\tMSG_ReadByte()+256;\r\n\t\t\t\tbreak;\r\n\t\t\tdefault:\r\n\t\t\t\tCon_Printf(\"CLHL_ParseGamePacket: Unable to parse gamecode tempent %i\\n\", subcode);\r\n\t\t\t\tmsg_readcount = end - net_message.data;\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\t\t}\r\n\t\tif (msg_readcount != end - net_message.data)\r\n\t\t{\r\n\t\t\tCon_Printf(\"CLHL_ParseGamePacket: Gamecode temp entity %i not parsed correctly read %i bytes too many\\n\", subcode, msg_readcount - (end - net_message.data));\r\n\t\t\tmsg_readcount = end - net_message.data;\r\n\t\t}\r\n\t\tbreak;\r\n\tcase svc_intermission:\r\n\t\t//nothing.\r\n\t\tcl.intermissionmode = IM_NQSCORES;\r\n\t\tbreak;\r\n\tcase svc_cdtrack:\r\n\t\t{\r\n\t\t\tunsigned int firsttrack;\r\n\t\t\tunsigned int looptrack;\r\n\t\t\tfirsttrack = MSG_ReadByte ();\r\n\t\t\tlooptrack = MSG_ReadByte ();\r\n\t\t\tMedia_NumberedTrack (firsttrack, looptrack);\r\n\t\t}\r\n\t\tbreak;\r\n\r\n\tcase 35: //svc_weaponanimation:\r\n\t\ttempi = MSG_ReadByte();\r\n\t\tCLGHL_weaponanimate(tempi, MSG_ReadByte());\r\n\t\tbreak;\r\n\tcase 37: //svc_roomtype\r\n\t\ttempi = MSG_ReadShort();\r\n//\t\tS_SetUnderWater(tempi==14||tempi==15||tempi==16);\r\n\t\tbreak;\r\n\tdefault:\r\n\t\tCon_Printf(\"Unrecognised gamecode packet %i (%s)\\n\", subcode, usermsgs[subcode].name);\r\n\t\tmsg_readcount = end - net_message.data;\r\n\t\tbreak;\r\n\t}\r\n\r\n\tif (msg_readcount != end - net_message.data)\r\n\t{\r\n\t\tCon_Printf(\"CLHL_ParseGamePacket: Gamecode packet %i not parsed correctly read %i bytestoo many\\n\", subcode, msg_readcount - (end - net_message.data));\r\n\t\tmsg_readcount = end - net_message.data;\r\n\t}\r\n\treturn true;\r\n}\r\n\r\n#endif\r\n"
  },
  {
    "path": "engine/client/client.h",
    "content": "/*\nCopyright (C) 1996-1997 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n*/\n// client.h\n\n#include \"../common/particles.h\"\n\nenum\n{\n\tSKIN_NOTLOADED,\t//not trying to load it. shouldn't really happen, but can.\n\tSKIN_LOADING,\t//still loading. just do something else for now...\n\tSKIN_LOADED,\n\tSKIN_FAILED\n};\ntypedef struct qwskin_s\n{\n\tchar\t\tname[64];\n\tint\t\t\tloadstate;\t\t// the name isn't a valid skin\n\n\t//qw skin info\n\tint\t\t\twidth;\n\tint\t\t\theight;\n\tvoid\t\t*skindata;\n\n\t//for hardware 32bit texture overrides\n\ttexnums_t\ttextures;\n} qwskin_t;\n\n// player_state_t is the information needed by a player entity\n// to do move prediction and to generate a drawable entity\ntypedef struct\n{\n\tint\t\t\tmessagenum;\t\t// all player's won't be updated each frame\n\n\tdouble\t\tstate_time;\t\t// not the same as the packet time,\n\t\t\t\t\t\t\t\t// because player commands come asyncronously\n\n\tusercmd_t\tcommand;\t\t// last command for prediction\n\n\tvec3_t\t\torigin;\n\tvec3_t\t\tpredorigin;\t\t// pre-predicted pos for other players (allowing us to just lerp when active)\n\tvec3_t\t\tviewangles;\t\t// only for demos, not from server\n\tvec3_t\t\tvelocity;\n\tint\t\t\tweaponframe;\n\n\tunsigned int\t\t\tmodelindex;\n\tint\t\t\tframe;\n\tint\t\t\tskinnum;\n\tint\t\t\teffects;\n\n#ifdef PEXT_SCALE\n\tfloat scale;\n#endif\n\tqbyte colourmod[3];\n\tqbyte alpha;\n#ifdef PEXT_FATNESS\n\tfloat fatness;\n#endif\n\n\tint\t\t\tflags;\t\t\t// dead, gib, etc\n\n\tint\t\t\tpm_type;\n\n\tvec3_t\t\tszmins, szmaxs;\n\n\t//maybe-propagated... use the networked value if available.\n\tfloat\t\twaterjumptime;\t//never networked...\n\tqboolean\tonground;\t//networked with Z_EXT_PF_ONGROUND||replacementdeltas\n\tqboolean\tjump_held;\t//networked with Z_EXT_PM_TYPE\n\tint\t\t\tjump_msec;\t// hack for fixing bunny-hop flickering on non-ZQuake servers\n\tvec3_t\t\tgravitydir;\t//networked with replacementdeltas\n} player_state_t;\n\n\n#if defined(Q2CLIENT) || defined(Q2SERVER)\ntypedef enum\n{\n\t// can accelerate and turn\n\tQ2PM_NORMAL,\n\tQ2PM_SPECTATOR,\n\t// no acceleration or turning\n\tQ2PM_DEAD,\n\tQ2PM_GIB,\t\t// different bounding box\n\tQ2PM_FREEZE\n} q2pmtype_t;\nenum\n{\t//urgh.\n\t// can accelerate and turn\n\tQ2EPM_NORMAL,\n\tQ2EPM_GRAPPLE,\n\tQ2EPM_SPECTATOR,\n\tQ2EPM_SPECTATOR2,\n\t// no acceleration or turning\n\tQ2EPM_DEAD,\n\tQ2EPM_GIB,\t\t// different bounding box\n\tQ2EPM_FREEZE\n};\ntypedef struct\n{\t//shared with q2 dll\n\n\tq2pmtype_t\tpm_type;\n\n\tshort\t\torigin[3];\t\t// 13.3\n\tshort\t\tvelocity[3];\t// 13.3\n\tqbyte\t\tpm_flags;\t\t// ducked, jump_held, etc\n\tqbyte\t\tpm_time;\t\t// each unit = 8 ms\n\tshort\t\tgravity;\n\tshort\t\tdelta_angles[3];\t// add to command angles to get view direction\n\t\t\t\t\t\t\t\t\t// changed by spawns, rotating objects, and teleporters\n//\tshort\t\tpad;\n} q2pmove_state_t;\n\ntypedef struct\n{\t//shared with q2 dll so cannot be changed\n\n\tq2pmove_state_t\tpmove;\t\t// for prediction\n\n\t// these fields do not need to be communicated bit-precise\n\n\tvec3_t\t\tviewangles;\t\t// for fixed views\n\tvec3_t\t\tviewoffset;\t\t// add to pmovestate->origin\n\tvec3_t\t\tkick_angles;\t// add to view direction to get render angles\n\t\t\t\t\t\t\t\t// set by weapon kicks, pain effects, etc\n\n\tvec3_t\t\tgunangles;\n\tvec3_t\t\tgunoffset;\n\tint\t\t\tgunindex;\n\tint\t\t\tgunframe;\n\n\tfloat\t\tblend[4];\t\t// rgba full screen effect\n\n\tfloat\t\tfov;\t\t\t// horizontal field of view\n\n\tint\t\t\trdflags;\t\t// refdef flags\n\n\tshort\t\tstats[Q2MAX_STATS];\t\t// fast status bar updates\n} q2player_state_t;\n#endif\n\ntypedef struct colourised_s {\n\tchar name[64];\n\tunsigned int topcolour;\n\tunsigned int bottomcolour;\n\tchar skin[64];\n\tstruct colourised_s *next;\n} colourised_t;\n\n#define\tMAX_SCOREBOARDNAME\t64\n#define MAX_DISPLAYEDNAME\t16\ntypedef struct player_info_s\n{\n\tint\t\t\tuserid;\n\tinfobuf_t\tuserinfo;\n\tqboolean\tuserinfovalid;\t//set if we actually know the userinfo (ie: false on vanilla nq servers)\n\tchar\t\tteamstatus[128];\n\tfloat\t\tteamstatustime;\n\n\t// scoreboard information\n\tint\t\tspectator;\n\tchar\tname[MAX_SCOREBOARDNAME];\n\tchar\tteam[MAX_INFO_KEY];\n\tfloat\trealentertime;\t//pegged against realtime, to cope with potentially not knowing the server's time when we first receive this message\n\tint\t\tfrags;\n\tint\t\tping;\n\tqbyte\tpl;\n\tchar\truleset[19]; //has colour markup to say if its accepted or not\n\n\tchar\tip[128];\n\n\tstruct\n\t{\n\t\tfloat time;\t//invalid if too old.\n\t\tint health;\n\t\tint armour;\n\t\tunsigned int items;\n\t\tvec3_t org;\n\t\tchar nick[8];\t//kinda short, yes.\n\t} tinfo;\n\n\tqboolean ignored;\n\tqboolean vignored;\n\tunsigned int chatstate;\n\n\t// skin information\n\tunsigned int\t\trtopcolor;\t//real, according to their userinfo\n\tunsigned int\t\trbottomcolor;\n\n#ifdef QWSKINS\n\tcolourised_t *colourised;\n\n\tqwskin_t\t*qwskin;\n\tqwskin_t\t*lastskin;\t//last-known-good skin\n\n\tunsigned int\t\tttopcolor;\t//team, according to colour forcing\n\tunsigned int\t\ttbottomcolor;\n\n\tstruct model_s\t*model;\n\t#define dtopcolor ttopcolor\n\t#define dbottomcolor tbottomcolor\n#else\n\t#define dtopcolor rtopcolor\n\t#define dbottomcolor rbottomcolor\n#endif\n\tskinid_t\tskinid;\n\n//\tunsigned short vweapindex;\n#ifdef HEXEN2\n\tunsigned char h2playerclass;\n#endif\n\n\tint prevcount;\n\n#ifdef QUAKEHUD\t\n\tstruct wstats_s \n\t{\n\t\tchar wname[16];\n\t\tunsigned int hit;\n\t\tunsigned int total;\n\t} weaponstats[16];\n#endif\n\n\tint stats[MAX_CL_STATS];\n\tfloat statsf[MAX_CL_STATS];\n} player_info_t;\n\n\ntypedef struct\n{\n\tdouble\t\tsenttime;\t\t\t// time cmd was sent off\n\tfloat\t\tlatency;\t\t\t// the time the packet was acked. -1=choked, -2=dropped, -3=never sent, -4=reply came back invalid\n\n\t// generated on client side\n\tusercmd_t\tcmd[MAX_SPLITS];\t// cmd that generated the frame\n\tint\t\t\tcmd_sequence;\t\t//the outgoing move sequence. if not equal to expected, that index was stale and is no longer valid\n\tint\t\t\tserver_message_num;\t//the inbound frame that was valid when this command was generated\n\n\tint server_time;\n\tint client_time;\n\tfloat sentgametime;\t//nq timings are based upon server time echos.\n} outframe_t;\n\ntypedef struct\n{\n\t// received from server\n\tint\t\t\tframeid;\t\t//the sequence number of the frame, so we can easily detect which frames are valid without poking all in advance, etc\n\tint\t\t\tackframe;\t\t//the outgoing sequence this frame acked (for prediction backlerping).\n\tdouble\t\treceivedtime;\t// time message was received, or -1\n\tplayer_state_t\tplayerstate[MAX_CLIENTS+MAX_SPLITS];\t// message received that reflects performing\n\t\t\t\t\t\t\t\t// the usercmd\n\tpacket_entities_t\tpacket_entities;\n\tqboolean\tinvalid;\t\t// true if the packet_entities delta was invalid\n} inframe_t;\n\n#ifdef Q2CLIENT\ntypedef struct\n{\n\tqboolean\t\tvalid;\t\t\t// cleared if delta parsing was invalid\n\tint\t\t\t\tserverframe;\n\tint\t\t\t\tservertime;\t\t// server time the message is valid for (in msec)\n\tint\t\t\t\tdeltaframe;\n\tstruct {\n\t\tqbyte\t\t\t\tareabits[MAX_Q2MAP_AREAS/8];\t\t// portalarea visibility bits\n\t\tq2player_state_t\tplayerstate;\n\t\tint\t\t\t\t\tclientnum;\n\t} seat[MAX_SPLITS];\n\tint\t\t\t\tnum_entities;\n\tint\t\t\t\tparse_entities;\t// non-masked index into cl_parse_entities array\n} q2frame_t;\n#endif\n\ntypedef struct\n{\n\tint\t\tdestcolor[3];\n\tfloat\t\tpercent;\t\t// 0-256\n} cshift_t;\n\n#define\tCSHIFT_CONTENTS\t0\n#define\tCSHIFT_DAMAGE\t1\n#define\tCSHIFT_BONUS\t2\n#define\tCSHIFT_POWERUP\t3\n#define CSHIFT_SERVER\t4\n#define\tNUM_CSHIFTS\t\t5\n\n\n//\n// client_state_t should hold all pieces of the client state\n//\n//the light array works thusly:\n//dlights are allocated DL_LAST downwards to 0, static wlights are allocated DL_LAST+1 to MAX_RTLIGHTS.\n//thus to clear the dlights but not rtlights, set the first light to RTL_FIRST\n#define dlightbitmask_t\t\tsize_t\n#define DL_LAST\t\t\t\t(sizeof(dlightbitmask_t)*8-1)\n#define RTL_FIRST\t\t\t(sizeof(dlightbitmask_t)*8)\n\n#define LFLAG_NORMALMODE\t(1<<0) /*ppl with r_shadow_realtime_dlight*/\n#define LFLAG_REALTIMEMODE\t(1<<1) /*ppl with r_shadow_realtime_world*/\n#define LFLAG_LIGHTMAP\t\t(1<<2)\n#define LFLAG_FLASHBLEND\t(1<<3)\n\n#define LFLAG_NOSHADOWS\t\t(1<<8)\n#define LFLAG_SHADOWMAP\t\t(1<<9)\n#define LFLAG_CREPUSCULAR\t(1<<10)\t//weird type of sun light that gives god rays\n#define LFLAG_ORTHO\t\t\t(1<<11)\t//sun-style -light\n#define LFLAG_FORCECACHE\t(1<<12)\t//shadowmap/surfaces should be cached from one frame to the next.\n\n#define LFLAG_INTERNAL\t\t(LFLAG_LIGHTMAP|LFLAG_FLASHBLEND|LFLAG_FORCECACHE)\t//these are internal to FTE, and never written to disk (ie: .rtlights files shouldn't contain these)\n#define LFLAG_DYNAMIC (LFLAG_LIGHTMAP | LFLAG_FLASHBLEND | LFLAG_NORMALMODE)\n\ntypedef struct dlight_s\n{\n\tint\t\tkey;\t\t\t\t// so entities can reuse same entry\n\tvec3_t\torigin;\n\tvec3_t\taxis[3];\n\tvec3_t\tangles;\t\t\t\t//used only for reflection, to avoid things getting rounded/cycled.\n\tvec3_t\trotation;\t\t\t//cubemap/spotlight rotation\n\tfloat\tradius;\n\tfloat\tdie;\t\t\t\t// stop lighting after this time\n\tfloat\tdecay;\t\t\t\t// drop this each second\n\tfloat\tminlight;\t\t\t// don't add when contributing less\n\tfloat   color[3];\n\tfloat\tchannelfade[3];\n#ifdef RTLIGHTS\n\tvec3_t lightcolourscales; //ambient, diffuse, specular\n#endif\n\tfloat\tcorona;\n\tfloat\tcoronascale;\n\tfloat\tfade[2];\n\n\tunsigned int flags;\n\tchar\tcubemapname[MAX_QPATH];\n\tchar\t*customstyle;\n\n\tint coronaocclusionquery;\n\tunsigned int coronaocclusionresult;\n\n\t//the following are used for rendering (client code should clear on create)\n\tqboolean rebuildcache;\n\tstruct\tshadowmesh_s *worldshadowmesh;\n\ttexid_t cubetexture;\n\tstruct {\n\t\tfloat updatetime;\n\t} face [6];\n\tint style;\t//multiply by style values if >= 0 && < MAX_LIGHTSTYLES\n\tfloat\tfov; //spotlight\n\tfloat\tnearclip; //for spotlights...\n\tstruct dlight_s *next;\n} dlight_t;\n\ntypedef struct\n{\n\tint\t\tlength;\n\tchar\tmap[MAX_STYLESTRING];\n\tvec3_t\tcolours;\n\tint\t\tcolourkey;\t//compacted version of the colours, for comparison against caches\n} lightstyle_t;\n\n#define\tMAX_DEMOS\t\t8\n#define\tMAX_DEMONAME\t16\n\ntypedef enum {\nca_disconnected, \t// full screen console with no connection\nca_demostart,\t\t// waiting to start up a demo (still disconnected but there should be a playdemo command in the cbuf somewhere so don't do other stuff)\nca_connected,\t\t// netchan_t established, waiting for svc_serverdata\nca_onserver,\t\t// processing data lists, donwloading, etc\nca_active\t\t\t// everything is in, so frames can be rendered\n} cactive_t;\n\ntypedef enum {\n\tdl_none,\n\tdl_model,\n\tdl_sound,\n\tdl_skin,\n\tdl_wad,\n\tdl_single,\n\tdl_singlestuffed\n} dltype_t;\t\t// download type\n\ntypedef struct qdownload_s\n{\n\tenum {DL_QW, DL_QWCHUNKS, DL_Q3, DL_DARKPLACES, DL_QWPENDING, DL_HTTP, DL_FTP} method;\n\tvfsfile_t\t\t*file;\t\t// file transfer from server\n\tchar\t\t\tdclname[MAX_OSPATH];\t//file to read/write the chunklist from, for download resumption.\n\tchar\t\t\ttempname[MAX_OSPATH];\t//file its currently writing to.\n\tchar\t\t\tlocalname[MAX_OSPATH];\t//file its going to be renamed to.\n\tint\t\t\t\tprefixbytes;\t\t\t//number of bytes that prefix the above names (ie: package/ or nothing).\n\tchar\t\t\tremotename[MAX_OSPATH];\t//file its coming from.\n\tfloat\t\t\tpercent;\t\t\t\t//for progress indicator.\n\tfloat\t\t\tstarttime;\t\t\t\t//for speed info\n\tqofs_t\t\t\tcompletedbytes;\t\t\t//number of bytes downloaded, for progress/speed info\n\tqofs_t\t\t\tsize;\t\t\t\t\t//total size (may be a guess)\n\tqboolean\t\tsizeunknown;\t\t\t//says that size is a guess\n\tunsigned int\tfilesequence;\t\t\t//unique file id.\n\tenum fs_relative fsroot;\t\t\t\t//where the local+temp file is meant to be relative to.\n\n\tdouble\t\t\tratetime;\t\t\t\t//periodically updated\n\tint\t\t\t\trate;\t\t\t\t\t//ratebytes/ratetimedelta\n\tint\t\t\t\tratebytes;\t\t\t\t//updated by download reception code, and cleared when ratetime is bumped\n\tunsigned int\tflags;\n\n\t//chunked downloads uses this\n\tstruct dlblock_s\n\t{\n\t\tqofs_t start;\n\t\tqofs_t end;\n\t\tenum\n\t\t{\n\t\t\tDLB_MISSING,\n\t\t\tDLB_PENDING,\n\t\t\tDLB_RECEIVED\n\t\t} state:16;\n\t\tunsigned int sequence;\t//sequence is only valid on pending blocks.\n\n\t\tstruct dlblock_s *next;\n\t} *dlblocks;\n} qdownload_t;\nenum qdlabort\n{\n\tQDL_FAILED,\t\t//delete file, tell server.\n\tQDL_DISCONNECT,\t//delete file, don't tell server.\n\tQDL_COMPLETED,\t//rename file, tell server.\n};\nqboolean DL_Begun(qdownload_t *dl);\nvoid DL_Abort(qdownload_t *dl, enum qdlabort aborttype);\t\t//just frees the download's resources. does not delete the temp file.\nqboolean CL_AllowArbitaryDownload(const char *oldname, const char *localfile);\n\n\n//\n// the client_static_t structure is persistant through an arbitrary number\n// of server connections\n//\ntypedef struct\n{\n// connection information\n\tcactive_t\tstate;\n\n\t/*Specifies which protocol family we're speaking*/\n\tenum {\n\t\tCP_UNKNOWN,\n\t\tCP_QUAKEWORLD,\n\t\tCP_NETQUAKE,\n\t\tCP_QUAKE2,\n\t\tCP_QUAKE3,\n\t\tCP_PLUGIN\n\t} protocol;\n\n\t/*QuakeWorld protocol flags*/\n#ifdef PROTOCOLEXTENSIONS\n\tunsigned int fteprotocolextensions;\n\tunsigned int fteprotocolextensions2;\n\tunsigned int ezprotocolextensions1;\n#endif\n\tunsigned int z_ext;\n\n\t/*NQ Protocol flags*/\n\tenum\n\t{\n\t\tCPNQ_ID,\n\t\tCPNQ_NEHAHRA,\n\t\tCPNQ_BJP1,\t//16bit models, strict 8bit sounds (otherwise based on nehahra)\n\t\tCPNQ_BJP2,\t//16bit models, strict 16bit sounds\n\t\tCPNQ_BJP3,\t//16bit models, flagged 16bit sounds, 8bit static sounds.\n\t\tCPNQ_H2MP,\t//urgh\n\t\tCPNQ_FITZ666, /*and rmqe999 protocol, which is a strict superset*/\n\t\tCPNQ_DP5,\n\t\tCPNQ_DP6,\n\t\tCPNQ_DP7\n\t} protocol_nq;\n\t#define CPNQ_IS_DP (cls.protocol_nq >= CPNQ_DP5)\n\t#define CPNQ_IS_BJP (cls.protocol_nq >= CPNQ_BJP1 && cls.protocol_nq <= CPNQ_BJP3)\n\tqboolean proquake_angles_hack;\t//angles are always 16bit\n#ifdef NQPROT\n\tqboolean qex;\t//we're connected to a QuakeEx server, which means lots of special workarounds that are not controlled via the actual protocol version.\n#endif\n\n\tint protocol_q2;\n\tchar downloadurl[MAX_OSPATH];\t//where to download files from (for q2pro compat)\n\n\tqboolean findtrack;\n\n\tint framecount;\n\n\tint realip_ident;\n\tnetadr_t realserverip;\n\n// network stuff\n\tnetchan_t\tnetchan;\n\tfloat lastarbiatarypackettime;\t//used to mark when packets were sent to prevent mvdsv servers from causing us to disconnect.\n\n\tinfobuf_t\tuserinfo[MAX_SPLITS];\n\tinfosync_t\tuserinfosync;\n\n\tchar\t\tserverurl[MAX_OSPATH*4];\t// eg qw://foo:27500/join?fp=blah\n\tchar\t\tservername[MAX_OSPATH];\t\t// internal parsing, eg dtls://foo:27500\n\n\tstruct ftenet_connections_s *sockets;\n\n\tqdownload_t *download;\n\n// demo loop control\n\tint\t\t\tdemonum;\t\t// -1 = don't play demos\n\tchar\t\tdemos[MAX_DEMOS][MAX_DEMONAME];\t\t// when not playing\n\n// demo recording info must be here, because record is started before\n// entering a map (and clearing client_state_t)\n\tvfsfile_t\t*demooutfile;\n\n\tenum{DPB_NONE,DPB_QUAKEWORLD,DPB_MVD,\n#ifdef NQPROT\n\t\tDPB_NETQUAKE,\n#endif\n#ifdef Q2CLIENT\n\t\tDPB_QUAKE2\n#endif\n\t}\tdemoplayback, demorecording;\n\tunsigned int demoeztv_ext;\n\t\t#define EZTV_DOWNLOAD\t\t(1u<<0)\t//also changes modellist/soundlist stuff to keep things synced\n\t\t#define EZTV_SETINFO\t\t(1u<<1)\t//proxy wants setinfo + ptrack commands\n\t\t#define EZTV_QTVUSERLIST\t(1u<<2)\t//'//qul cmd id [name]' commands from proxy.\n\tqboolean\tdemohadkeyframe;\t//q2 needs to wait for a packet with a key frame, supposedly.\n\tenum\n\t{\n\t\tDEMOSEEK_NOT,\n\t\tDEMOSEEK_TIME,\t//stops one we reach demoseektime\n\t\tDEMOSEEK_MARK,\t//stops once we reach a '//demomark'\n\t\tDEMOSEEK_INTERMISSION,\t//stops once we reach an svc_intermission\n\t} demoseeking;\n\tfloat\t\tdemoseektime;\n\tint\t\t\tdemotrack;\n\tqboolean\ttimedemo;\n\tchar\t\tlastdemoname[MAX_OSPATH];\t//empty if is a qtv stream\n\tqboolean\tlastdemowassystempath;\n\tvfsfile_t\t*demoinfile;\n\tfloat\t\ttd_lastframe;\t\t// to meter out one message a frame\n\tint\t\t\ttd_startframe;\t\t// host_framecount at start\n\tfloat\t\ttd_starttime;\t\t// realtime at second frame of timedemo\n\tfloat\t\tdemostarttime;\t\t// the time of the first frame, so we don't get weird results with qw demos\n\n\tstruct qtvviewers_s\n\t{\t//(other) people on a qtv. in case people give a damn.\n\t\tstruct qtvviewers_s *next;\n\t\tint\t\t\tuserid;\n\t\tchar\t\tname[128];\n\t} *qtvviewers;\n\n\tint\t\t\tchallenge;\n\n\tfloat\t\tlatency;\t\t// rolling average\n\n\tchar\t\tallow_unmaskedskyboxes;\t//skyboxes/domes do not need to be depth-masked when set. FIXME: we treat this as an optimisation hint, but some hl/q2/q3 maps require strict do-not-mask rules to look right.\n\tqboolean\tallow_anyparticles;\n\tqboolean\tallow_watervis;\t//fixme: not checked any more\n\tfloat\t\tallow_fbskins;\t//fraction of allowance\n\tqboolean\tallow_cheats;\n\tqboolean\tallow_semicheats;\t//defaults to true, but this allows a server to enforce a strict ruleset (smackdown type rules).\n\tqboolean\tallow_csqc;\t\t\t//disables some legacy/compat things, like proquake parsing.\n\tfloat\t\tmaxfps;\t//server capped\n\tint\t\t\tdeathmatch;\n\n#ifdef NQPROT\n\tint signon;\n#endif\n\n\tcolourised_t *colourised;\n\tqboolean\tnqexpectingstatusresponse;\n} client_static_t;\n\nextern client_static_t\tcls;\n\nenum dlfailreason_e\n{\n\tDLFAIL_UNTRIED,\t\t//...\n\tDLFAIL_UNSUPPORTED,\t//eg vanilla nq\n\tDLFAIL_CORRUPTED,\t//something weird happened (hash fail)\n\tDLFAIL_CLIENTCVAR,\t//clientside cvar blocked the download\n\tDLFAIL_CLIENTFILE,\t//some sort of error writing the file\n\tDLFAIL_SERVERCVAR,\t//serverside setting blocked the download\n\tDLFAIL_REDIRECTED,\t//server told us to download a different file\n\tDLFAIL_SERVERFILE,\t//server couldn't find the file\n};\ntypedef struct downloadlist_s {\n\tchar rname[128];\n\tchar localname[128];\n\tunsigned int size;\n\tunsigned int flags;\n#define DLLF_VERBOSE\t\t(1u<<0)\t\t//tell the user that its downloading\n#define DLLF_REQUIRED\t\t(1u<<1)\t\t//means that it won't load models etc until its downloaded (ie: requiredownloads 0 makes no difference)\n#define DLLF_OVERWRITE\t\t(1u<<2)\t\t//overwrite it even if it already exists\n#define DLLF_SIZEUNKNOWN\t(1u<<3)\t\t//download's size isn't known\n#define DLLF_IGNOREFAILED\t(1u<<4)\t\t//\n#define DLLF_NONGAME\t\t(1u<<5)\t\t//means the requested download filename+localname is gamedir explicit (so id1/foo.txt is distinct from qw/foo.txt)\n#define DLLF_TEMPORARY\t\t(1u<<6)\t\t//download it, but don't actually save it (DLLF_OVERWRITE doesn't actually overwrite, but does ignore any local files)\n#define DLLF_USEREXPLICIT\t(1u<<7)\t\t//use explicitly requested it, ignore the cl_downloads cvar.\n\n#define DLLF_BEGUN\t\t\t(1u<<8)\t\t//server has confirmed that the file exists, is readable, and we've opened a file. should not be set on new requests.\n#define DLLF_ALLOWWEB\t\t(1u<<9)\t\t//failed http downloads should retry but from the game server itself\n#define DLLF_TRYWEB\t\t\t(1u<<10)\t//should be trying to download it from a website...\n\n\tenum dlfailreason_e failreason;\n\tstruct downloadlist_s *next;\n} downloadlist_t;\n\n\ntypedef struct {\n\t//current persistant state\n\ttrailkey_t trailstate;\t//when to next throw out a trail\n\ttrailkey_t emitstate;    //when to next emit\n\n\t//current origin\n\tvec3_t origin;\t//current render position\n\tvec3_t angles;\n\n\t//previous rendering frame (for trails)\n\tvec3_t lastorigin;\n\tqboolean isnew;\n\tqboolean isplayer;\n\n\t//intermediate values for frame lerping\n\t//separate upper+lower lerps\n\tfloat framelerpdeltatime[FS_COUNT];\n\tfloat newframestarttime[FS_COUNT];\n\tint newframe[FS_COUNT];\n\tfloat oldframestarttime[FS_COUNT];\n\tint oldframe[FS_COUNT];\n\tqbyte basebone;\n\n\t//intermediate values for origin lerping of stepping things\n\tint newsequence;\n\tfloat orglerpdeltatime;\n\tfloat orglerpstarttime;\n\tvec3_t neworigin; /*origin that we're lerping towards*/\n\tvec3_t oldorigin; /*origin that we're lerping away from*/\n\tvec3_t newangle;\n\tvec3_t oldangle;\n\n\t//for further info\n\tint skeletalobject;\n\tint sequence;\t/*so we know if the ent is still valid*/\n\tentity_state_t *entstate;\n} lerpents_t;\n\nenum\n{\n\tFOGTYPE_AIR,\n\tFOGTYPE_WATER,\n\tFOGTYPE_SKYROOM,\n\n\tFOGTYPE_COUNT\n};\n\n//state associated with each player 'seat' (one for each splitscreen client)\n//note that this doesn't include networking inputlog info.\nstruct playerview_s\n{\n\tint\t\t\tplayernum;\t\t//cl.players index for this player.\n\tqboolean\tnolocalplayer;\t//inhibit use of qw-style players, predict based on entities.\n\tqboolean\tspectator;\n#ifdef PEXT_SETVIEW\n\tint\t\t\tviewentity;\t\t//view is attached to this entity.\n#endif\n\n\t// information for local display\n\tint\t\t\tstats[MAX_CL_STATS];\t// health, etc\n\tfloat\t\tstatsf[MAX_CL_STATS];\t// health, etc\n\tchar\t\t*statsstr[MAX_CL_STATS];\t// health, etc\n\tfloat\t\titem_gettime[32];\t// cl.time of aquiring item, for blinking\n\tfloat\t\tfaceanimtime;\t\t// use anim frame if cl.time < this\n\n#ifdef QUAKEHUD\n\tqboolean\tsb_showscores;\n\tqboolean\tsb_showteamscores;\n#ifdef HEXEN2\n\tint\t\t\tsb_hexen2_cur_item;//hexen2 hud\n\tfloat\t\tsb_hexen2_item_time;\n\tfloat\t\tsb_hexen2_extra_info_lines;\n\tqboolean\tsb_hexen2_extra_info;//show the extra stuff\n\tqboolean\tsb_hexen2_infoplaque;\n#endif\n#endif\n\n\n// the client maintains its own idea of view angles, which are\n// sent to the server each frame.  And only reset at level change\n// and teleport times\n\tvec3_t\t\taimangles;\t\t\t//angles actually being sent to the server (different due to in_vraim)\n\tvec3_t\t\tviewangles;\t\t\t//current angles\n\tvec3_t\t\tviewanglechange;\t//angles set by input code this frame\n\tshort\t\tbaseangles[3];\t\t//networked angles are relative to this value\n\tvec3_t\t\tintermissionangles;\t//absolute angles for intermission\n\tvec3_t\t\tgravitydir;\n\n\t// pitch drifting vars\n\tfloat\t\tpitchvel;\n\tqboolean\tnodrift;\n\tfloat\t\tdriftmove;\n\tdouble\t\tlaststop;\n\n\tint gamerectknown;\t\t//equals cls.framecount if valid\n\tvrect_t\tgamerect;\t\t//position the player's main view was drawn at this frame.\n\n\t//prediction state\n\tint\t\t\tpmovetype;\n\tfloat\t\tentgravity;\n\tfloat\t\tmaxspeed;\n\tvec3_t\t\tsimorg;\n\tvec3_t\t\tsimvel;\n\tvec3_t\t\tsimangles;\n\tfloat\t\trollangle;\n\tfloat\t\thdr_last;\n\n\tint\t\t\tchatstate;\t//1=talking, 2=afk\n\n\tfloat\t\tcrouch;\t\t\t// local amount for smoothing stepups\n\tvec3_t\t\toldorigin;\t\t// to track step smoothing\n\tfloat\t\toldz, extracrouch, crouchspeed; // to track step smoothing\n\tqboolean\tonground;\n\tfloat\t\tviewheight;\n\tint\t\t\twaterlevel;\t\t//for smartjump\n\n\t//for values that are propagated from one frame to the next\n\t//the next frame will always predict from the one we're tracking, where possible.\n\t//these values should all regenerate naturally from networked state (misses should be small/rare and fade when the physics catches up).\n\tstruct playerpredprop_s\n\t{\n\t\tfloat\t\twaterjumptime;\n\t\tqboolean\tonground;\n\t\tvec3_t\t\tgravitydir;\n\t\tqboolean\tjump_held;\n\t\tint\t\t\tjump_msec;\t\t// hack for fixing bunny-hop flickering on non-ZQuake servers\n\n\t\tint\t\t\tsequence;\n\t} prop;\n\n#ifdef Q2CLIENT\n\tfloat forcefov;\n\tint handedness;\t//0=right,1=left,2=center/hidden\n\tvec3_t predicted_origin;\n\tvec3_t predicted_angles;\n\tvec3_t prediction_error;\n\tfloat predicted_step_time;\n\tfloat predicted_step;\n#endif\n\n\t//temporary view kick from weapon firing, angles+origins\n\tfloat\t\tpunchangle_cl;\t\t// qw-style angles\n\tvec3_t\t\tpunchangle_sv;\t\t// nq-style\n\tvec3_t\t\tpunchorigin;\t\t// nq-style\n\n\tfloat\t\tv_dmg_time;\t\t//various view knockbacks.\n\tfloat\t\tv_dmg_roll;\n\tfloat\t\tv_dmg_pitch;\n\n\tdouble\t\tbobtime;\t\t//sine wave\n\tdouble\t\tbobcltime;\t\t//for tracking time increments\n\tfloat\t\tbob;\t\t\t//bob height\n\n\n\tvec3_t\t\tcam_desired_position;\t// where the camera wants to be\n\tint\t\t\tcam_oldbuttons;\t\t\t//\n\tdouble\t\tcam_lastviewtime;\t\t// timer for wallcam\n\tfloat\t\tcam_reautotrack;\t\t// timer to throttle tracking changes.\n\tint\t\t\tcam_spec_track;\t\t\t// player# of who we are tracking / want to track / might want to track\n\tenum\n\t{\n\t\tCAM_FREECAM\t= 0,\t\t//not attached to another player. we are our own thing (or actually playing).\n\t\tCAM_PENDING = 1,\t\t//we want to lock on to cam_spec_track, but we don't have their position / stats yet. still freecamming\n\t\tCAM_WALLCAM = 2,\t\t//locked, cl_chasecam=0. we're watching them from a wall.\n\t\tCAM_EYECAM\t= 3\t\t\t//locked, cl_chasecam=1. we know where they are, we're in their eyes.\n\n#define CAM_ISLOCKED(pv) ((pv)->cam_state > CAM_PENDING)\n\t} cam_state;\n\n\tcshift_t\tcshifts[NUM_CSHIFTS];\t// color shifts for damage, powerups and content types\n\tvec4_t\t\tscreentint;\n\tvec4_t\t\tbordertint;\t//won't contain v_cshift values, only powerup+contents+damage+bf flashes\n\n//\tentity_t\tviewent;\t// is this not utterly redundant yet?\n\tstruct\n\t{\n\t\tstruct model_s *oldmodel;\n\t\tfloat lerptime;\n\t\tfloat oldlerptime;\n\t\tfloat frameduration;\n\t\tint prevframe;\n\t\tint oldframe;\n\t} vm;\n\n\tstruct\n\t{\n\t\tqboolean defaulted;\n\t\tint entnum;\n\t\tvec3_t origin;\n\t\tvec3_t forward;\n\t\tvec3_t right;\n\t\tvec3_t up;\n\t\tsize_t reverbtype;\n\t\tvec3_t velocity;\n\t} audio;\n\n\tstruct vrdevinfo_s vrdev[VRDEV_COUNT];\n};\n\n//\n// the client_state_t structure is wiped completely at every\n// server signon\n//\ntypedef struct\n{\n\tint\t\t\tfpd;\n\tint\t\t\tservercount;\t// server identification for prespawns\n\n\tint\t\t\tprotocol_qw;\n\n\tfloat\t\tgamespeed;\n\tqboolean\tcsqcdebug;\t//redundant, remove '*csqcdebug' serverinfo key.\n\tqboolean\tallowsendpacket;\n\n\tqboolean\tstillloading;\t// set when doing something slow, and the game is still loading.\n\n\tqboolean\thaveserverinfo;\t//nq servers will usually be false. don't override stuff if we already know better.\n\tinfobuf_t\tserverinfo;\n\tchar\t\t*serverpacknames;\n\tchar\t\t*serverpackhashes;\n\tqboolean\tserverpakschanged;\n\n\tint\t\t\tparsecount;\t\t// server message counter\n\tint\t\t\toldparsecount;\n\tint\t\t\toldvalidsequence;\n\tint\t\t\tackedmovesequence;\t//in quakeworld/q2 this is always equal to validsequence. nq can differ. may only be updated when validsequence is updated.\n\tint\t\t\tlastackedmovesequence;\t//can be higher than ackedmovesequence when the received frame was unusable.\n\tint\t\t\tvalidsequence;\t// this is the sequence number of the last good\n\t\t\t\t\t\t\t\t// packetentity_t we got.  If this is 0, we can't\n\t\t\t\t\t\t\t\t// render a frame yet\n\tint\t\t\tmovesequence;\t// client->server frames\n\tfloat\t\tmovesequence_time;\t// client->server frame timestamp (vs cl.time)\n\n//\tint\t\t\tspectator;\n\tint\t\t\tautotrack_hint;\t\t//the latest hint from the mod, might be negative for invalid.\n\tint\t\t\tautotrack_killer;\t//if someone kills the guy we're tracking, this is the guy we should switch to.\n\n\tdouble\t\tlast_ping_request;\t// while showing scoreboard\n\tdouble\t\tlast_servermessage;\n\n\t//list of ent frames that still need to be acked.\n\tunsigned int numackframes;\n\tint ackframes[64];\n\n#ifdef Q2CLIENT\n\tq2frame_t\tq2frame;\n\tq2frame_t\tq2frames[Q2UPDATE_BACKUP];\n#endif\n\n// sentcmds[cl.netchan.outgoing_sequence & UPDATE_MASK] = cmd\n\toutframe_t\toutframes[UPDATE_BACKUP];\t//user inputs (cl.ackedmovesequence+1 to cl.movesequence are still pending)\n\tinframe_t\tinframes[UPDATE_BACKUP];\t//server state (cl.validsequence is the most recent set)\n\tlerpents_t\t*lerpents;\n\tint\t\t\tmaxlerpents;\t//number of slots allocated.\n\tint\t\t\tlerpentssequence;\n\tlerpents_t\tlerpplayers[MAX_CLIENTS];\n\n\t//when running splitscreen, we have multiple viewports all active at once\n\tunsigned int\tsplitclients;\t//we are running this many clients split screen.\n\tplayerview_t\tplayerview[MAX_SPLITS];\n\tunsigned int\tdefaultnetsplit;//which multiview splitscreen to parse the message for (set by mvd playback code)\n\n\t// localized movement vars\n\tfloat\t\tbunnyspeedcap;\n\n// the client simulates or interpolates movement to get these values\n\tdouble\t\ttime;\t\t\t// this is the time value that the client\n\t\t\t\t\t\t\t\t// is rendering at.  always <= realtime\n\tdouble\t\tlasttime;\t\t//cl.time from last frame.\n\tdouble\t\tlastlinktime;\t//cl.time from last frame.\n\tdouble\t\tmapstarttime;\t//for computing csqc's cltime.\n\n\tdouble servertime;\t//current server time, bound between gametime and gametimemark\n\tfloat mtime;\t\t//server time as on the server when we last received a packet. not allowed to decrease.\n\tfloat oldmtime;\t\t//server time as on the server for the previously received packet.\n\n\tdouble gametime;\n\tdouble gametimemark;\n\tdouble oldgametime;\t\t//used as the old time to lerp cl.time from.\n\tdouble oldgametimemark;\t//if it's 0, cl.time will casually increase.\n\tfloat demogametimebias;\t//mvd timings are weird.\n\tint\t  demonudge;\t\t//\n\tfloat demopausedtilltime;//demo is paused until realtime>this\n\n\tfloat minpitch;\n\tfloat maxpitch;\n\n\tqboolean\tpaused;\t\t\t// send over by server\n\tqboolean\timplicitpause;\t//for csqc. only a hint, respected only in singleplayer.\n\n\tenum\n\t{\n\t\tIM_NONE,\t\t//off.\n\t\tIM_NQSCORES,\t//+showscores forced, view still attached to regular view\n\t\tIM_NQFINALE,\t//slow centerprint text etc, view still attached to regular view. no hud\n\t\tIM_NQCUTSCENE,\t//IM_NQFINALE, but without the 'finale' header on centerprints.\n\t\tIM_H2FINALE,\t//IM_NQFINALE, but with the view offset by the player's viewheight.\n\n\t\tIM_QWSCORES\t\t//intermission, view locked at a specific point\n\t} intermissionmode;\t// don't change view angle, full screen, etc\n\tfloat\t\tcompleted_time;\t// latched ffrom time at intermission start\n\n//\n// information that is static for the entire time connected to a server\n//\n#ifdef HAVE_LEGACY\n\tchar\t\t\t\t*model_name_vwep[MAX_VWEP_MODELS];\n\tstruct model_s\t\t*model_precache_vwep[MAX_VWEP_MODELS];\n#endif\n\tchar\t\t\t\t*model_name[MAX_PRECACHE_MODELS];\n\tstruct model_s\t\t*model_precache[MAX_PRECACHE_MODELS];\n\tchar\t\t\t\t*sound_name[MAX_PRECACHE_SOUNDS];\n\tstruct sfx_s\t\t*sound_precache[MAX_PRECACHE_SOUNDS];\n\tchar\t\t\t\t*particle_ssname[MAX_SSPARTICLESPRE];\n\tint\t\t\t\t\tparticle_ssprecache[MAX_SSPARTICLESPRE];\t//these are actually 1-based, so 0 can be used to lazy-init them. I cheat.\n\n#ifdef Q2CLIENT\n#define Q2MAX_VISIBLE_WEAPONS 32 //q2 has about 20.\n\tint\t\tnumq2visibleweapons;\t//q2 sends out visible-on-model weapons in a wierd gender-nutral way.\n\tchar\t*q2visibleweapons[Q2MAX_VISIBLE_WEAPONS];//model names beginning with a # are considered 'sexed', and are loaded on a per-client basis. yay. :(\n\n\tchar\t\t*configstring_general[Q2MAX_CLIENTS|Q2MAX_GENERAL];\n\tchar\t\t*image_name[Q2MAX_IMAGES];\n\tchar\t\t*item_name[Q2MAX_ITEMS];\n\tshort\t\tinventory[MAX_SPLITS][Q2MAX_ITEMS];\n#endif\n\n\tchar\t\t\t\tmodel_csqcname[MAX_CSMODELS][MAX_QPATH];\n\tstruct model_s\t\t*model_csqcprecache[MAX_CSMODELS];\n\tchar\t\t\t\t*particle_csname[MAX_CSPARTICLESPRE];\n\tint\t\t\t\t\tparticle_csprecache[MAX_CSPARTICLESPRE];\t//these are actually 1-based, so we can be lazy and do a simple negate.\n\n\tqboolean\t\t\tparticle_ssprecaches;\t//says to not try to do any dp-compat hacks.\n\tqboolean\t\t\tparticle_csprecaches;\t//says to not try to do any dp-compat hacks.\n\n\t//used for q2 sky/configstrings\n\tchar skyname[MAX_QPATH];\n\tfloat skyrotate;\n\tqboolean skyautorotate;\n\tvec3_t skyaxis;\n\n\tqboolean\tfog_locked;\t\t\t//FIXME: make bitmask\n\tfogstate_t\tfog[FOGTYPE_COUNT];\t//0 = air, 1 = water. if water has no density fall back on air.\n\tfogstate_t\toldfog[FOGTYPE_COUNT];\n\n\tchar\t\tlevelname[40];\t// for display on solo scoreboard\n\tchar\t\t*windowtitle;\t// fully overrides the window caption.\n\n// refresh related state\n\tstruct model_s\t*worldmodel;\t// cl_entitites[0].model\n\tint\t\t\tnum_entities;\t// stored bottom up in cl_entities array\n\tint\t\t\tnum_statics;\t// stored top down in cl_entitiers\n\n// all player information\n\tunsigned int    allocated_client_slots;\n\tplayer_info_t\tplayers[MAX_CLIENTS];\n\n\n\tdownloadlist_t *downloadlist;\n\tdownloadlist_t *faileddownloads;\n\n\tqboolean gamedirchanged;\n\n#ifdef Q2CLIENT\n\tchar\t\tq2airaccel[16];\n\tchar\t\tq2statusbar[1024];\n\tchar\t\tq2layout[MAX_SPLITS][1024];\n\tint\t\t\tq2mapchecksum;\n\tint parse_entities;\n\tfloat lerpfrac;\n\tfloat q2svnetrate; //number of frames we expect to receive per second (required to calculate the server time correctly).\n#endif\n\n\tchar lastcenterprint[1024];\t//prevents too much spam with console centerprint logging.\n\n\n\n\tstruct itemtimer_s\n\t{\n\t\tfloat end;\n\t\tint entnum;\n\t\tfloat start;\n\t\tfloat duration;\n\t\tvec3_t origin;\n\t\tvec3_t rgb;\n\t\tfloat radius;\n\t\tstruct itemtimer_s *next;\n\t} *itemtimers;\n\n\t//interpolation+snapshots\n\tfloat\tpackfrac;\n\tpacket_entities_t\t*currentpackentities;\n\tpacket_entities_t\t*previouspackentities;\n\tfloat\t\t\t\tcurrentpacktime;\n\tqboolean\t\t\tdo_lerp_players;\n\n\tplayerview_t\t\t*mouseplayerview;\t//for mouse/scoreboard interaction when playing mvds.\n\tint\t\t\t\t\tmousenewtrackplayer;\n\n\tint teamplay;\n\tint deathmatch;\n\n\tqboolean teamfortress;\t// *sigh*. This is used for teamplay stuff. This sucks.\n\tqboolean hexen2pickups;\n\tqboolean disablemouse;\t//no mouse inputs (for controller-only games, though we do also allow keyboards if only because of joy2key type stuff)\n\n\tqboolean sendprespawn;\n\tint contentstage;\n\n\tdouble matchgametimestart;\n\tenum {\n\t\tMATCH_DONTKNOW,\t//assumed to be in progress.\n\t\tMATCH_COUNTDOWN,\n\t\tMATCH_STANDBY,\n\t\tMATCH_INPROGRESS\n\t} matchstate;\n\n\tenum {\n\t\tCLNQPP_NONE,\n\t\tCLNQPP_PINGS,\n\t\tCLNQPP_STATUS, //\"host:    *\\n\" ... \"players: *\\n\\n\"\n\t\tCLNQPP_STATUSPLAYER,\t//#...\\n\n\t\tCLNQPP_STATUSPLAYERIP,\t//   foobar\\n\n\t}\tnqparseprint;\n\tint\t\t\tnqparseprintplayer;\n\tfloat\t\tnqplayernamechanged;\n} client_state_t;\n\nextern unsigned int\t\tcl_teamtopcolor;\nextern unsigned int\t\tcl_teambottomcolor;\nextern unsigned int\t\tcl_enemytopcolor;\nextern unsigned int\t\tcl_enemybottomcolor;\n\n//FPD values\n//(commented out ones are ones that we don't support)\n//#define FPD_NO_SAY_MACROS\t\t\t(1 << 0)\n//#define FPD_NO_TIMERS\t\t\t\t(1 << 1)\n//#define FPD_NO_SOUNDTRIGGERS\t\t(1 << 2)\n#define FPD_NO_FAKE_LAG\t\t\t\t(1 << 3)\n#define FPD_ANOUNCE_FAKE_LAG\t\t(1 << 4)\n//#define FPD_HIDE_ENEMY_VICINITY\t(1 << 5)\n//#define FPD_NO_SPEC_CHAT\t\t\t(1 << 6)\n//#define FPD_HIDE_X_Y_MACRO\t\t(1 << 7)\n#define FPD_NO_FORCE_SKIN\t\t\t(1 << 8)\n#define FPD_NO_FORCE_COLOR\t\t\t(1 << 9)\n#define FPD_LIMIT_PITCH\t\t\t\t(1 << 14)\t//limit scripted pitch changes\n#define FPD_LIMIT_YAW\t\t\t\t(1 << 15)\t//limit scripted yaw changes\n\n//\n// cvars\n//\nextern  cvar_t\tcl_warncmd;\nextern\tcvar_t\tcl_upspeed;\nextern\tcvar_t\tcl_forwardspeed;\nextern\tcvar_t\tcl_backspeed;\nextern\tcvar_t\tcl_sidespeed;\n\nextern\tcvar_t\tcl_movespeedkey;\n\nextern\tcvar_t\tcl_yawspeed;\nextern\tcvar_t\tcl_pitchspeed;\n\nextern\tcvar_t\tcl_anglespeedkey;\n\nextern\tcvar_t\tcl_shownet;\nextern\tcvar_t\tcl_sbar;\nextern\tcvar_t\tcl_hudswap;\n\nextern\tcvar_t\tcl_pitchdriftspeed;\nextern\tcvar_t\tlookspring;\nextern\tcvar_t\tlookstrafe;\nextern\tcvar_t\tsensitivity;\n\nextern\tcvar_t\tm_pitch;\nextern\tcvar_t\tm_yaw;\nextern\tcvar_t\tm_forward;\nextern\tcvar_t\tm_side;\n\n#ifndef SERVERONLY\nextern\tcvar_t\tname;\n#endif\n\n\nextern cvar_t ruleset_allow_playercount;\nextern cvar_t ruleset_allow_frj;\nextern cvar_t ruleset_allow_semicheats;\nextern cvar_t ruleset_allow_packet;\nextern cvar_t ruleset_allow_particle_lightning;\nextern cvar_t ruleset_allow_overlongsounds;\nextern cvar_t ruleset_allow_larger_models;\nextern cvar_t ruleset_allow_modified_eyes;\nextern cvar_t ruleset_allow_sensitive_texture_replacements;\nextern cvar_t ruleset_allow_localvolume;\nextern cvar_t ruleset_allow_shaders;\nextern cvar_t ruleset_allow_watervis;\nextern cvar_t ruleset_allow_triggers;\n\n#ifndef SERVERONLY\nextern\tclient_state_t\tcl;\n#endif\n\ntypedef struct\n{\n\tentity_t\t\tent;\n\tentity_state_t\tstate;\n\ttrailkey_t      emit;\n\tint\tmdlidx;\t/*negative are csqc indexes*/\n} static_entity_t;\n\n// FIXME, allocate dynamically\nextern\tentity_state_t *cl_baselines;\nextern\tstatic_entity_t\t\t*cl_static_entities;\nextern\tunsigned int\tcl_max_static_entities;\nextern\tlightstyle_t\t*cl_lightstyle;\nextern\tsize_t\t\t\tcl_max_lightstyles;\nextern\tdlight_t\t\t*cl_dlights;\nextern\tsize_t cl_maxdlights;\n\nextern\tint\t\t\t\td_lightstylevalue[MAX_NET_LIGHTSTYLES];\n\nextern size_t rtlights_first, rtlights_max;\nextern int cl_baselines_count;\n\nextern\tqboolean\tnomaster;\n\n//=============================================================================\n\n\n//\n// cl_main\n//\nvoid CL_InitDlights(void);\nvoid CL_FreeDlights(void);\ndlight_t *CL_AllocDlight (int key);\t//allocates or reuses the light with the specified key index\ndlight_t *CL_AllocDlightOrg (int keyidx, vec3_t keyorg); //reuses the light at the specified origin...\ndlight_t *CL_AllocSlight (void);\t//allocates a new static light\ndlight_t *CL_NewDlight (int key, const vec3_t origin, float radius, float time, float r, float g, float b);\ndlight_t *CL_NewDlightCube (int key, const vec3_t origin, vec3_t angles, float radius, float time, vec3_t colours);\nvoid CL_CloneDlight(dlight_t *dl, dlight_t *src);\t//copies one light to another safely\nvoid\tCL_DecayLights (void);\n\nvoid CLQW_ParseDelta (struct entity_state_s *from, struct entity_state_s *to, int bits);\n\nvoid CL_Init (void);\nvoid Host_WriteConfiguration (void);\nvoid CL_CheckServerInfo(void);\nvoid CL_CheckServerPacks(void);\n\nvoid CL_EstablishConnection (char *host);\n\nvoid CL_Disconnect (const char *reason);\nvoid CL_Disconnect_f (void);\nvoid CL_Reconnect_f (void);\nvoid CL_NextDemo (void);\nvoid CL_Startdemos_f (void);\nvoid CL_Demos_f (void);\nvoid CL_Stopdemo_f (void);\nvoid CL_Changing_f (void);\nvoid CL_Reconnect_f (void);\nvoid CL_ConnectionlessPacket (void);\nqboolean CL_DemoBehind(void);\nvoid CL_SaveInfo(vfsfile_t *f);\nvoid CL_SetInfo (int pnum, const char *key, const char *value);\nvoid CL_SetInfoBlob (int pnum, const char *key, const char *value, size_t valuesize);\n\nchar *CL_TryingToConnect(void);\n\nvoid CL_ExecInitialConfigs(char *defaultexec, qboolean fullvidrestart);\n\nextern\tint\t\t\t\tcl_framecount;\t//number of times the entity lists have been cleared+reset.\nextern\tint\t\t\t\tcl_numvisedicts;\nextern\tint\t\t\t\tcl_maxvisedicts;\nextern\tentity_t\t\t*cl_visedicts;\n\n/*these are for q3 really*/\ntypedef struct {\n\tstruct shader_s *shader;\n\tint firstvert;\n\tint firstidx;\n\tint numvert;\n\tint numidx;\n\tunsigned int flags;\n} scenetris_t;\nextern scenetris_t\t\t*cl_stris;\nextern vecV_t\t\t\t*fte_restrict cl_strisvertv;\nextern vec4_t\t\t\t*fte_restrict cl_strisvertc;\nextern vec2_t\t\t\t*fte_restrict cl_strisvertt;\n//extern vec3_t\t\t\t*fte_restrict cl_strisvertn[3];\nextern index_t\t\t\t*fte_restrict cl_strisidx;\nextern unsigned int cl_numstrisidx;\nextern unsigned int cl_maxstrisidx;\nextern unsigned int cl_numstrisvert;\nextern unsigned int cl_maxstrisvert;\nextern unsigned int cl_numstrisnormals;\nextern unsigned int cl_maxstrisnormals;\nextern unsigned int cl_numstris;\nextern unsigned int cl_maxstris;\n\n#define cl_stris_ExpandVerts(max) \\\n\tdo {\t\t\t\\\n\t\tcl_maxstrisvert = max;\t\\\n\t\tcl_strisvertv = BZ_Realloc(cl_strisvertv, sizeof(*cl_strisvertv)*cl_maxstrisvert);\t\\\n\t\tcl_strisvertt = BZ_Realloc(cl_strisvertt, sizeof(*cl_strisvertt)*cl_maxstrisvert);\t\\\n\t\tcl_strisvertc = BZ_Realloc(cl_strisvertc, sizeof(*cl_strisvertc)*cl_maxstrisvert);\t\\\n/*\t\tcl_strisvertn[0] = BZ_Realloc(cl_strisvertn[0], sizeof(*cl_strisvertn[0])*cl_maxstrisvert);\t\\\n\t\tcl_strisvertn[1] = BZ_Realloc(cl_strisvertn[1], sizeof(*cl_strisvertn[1])*cl_maxstrisvert);\t\\\n\t\tcl_strisvertn[2] = BZ_Realloc(cl_strisvertn[2], sizeof(*cl_strisvertn[2])*cl_maxstrisvert);\t\\\n*/\t} while(0)\n\nextern char emodel_name[], pmodel_name[], prespawn_name[], modellist_name[], soundlist_name[];\n\n//CL_TraceLine traces against network(positive)+csqc(negative) ents. returns frac(1 on failure), and impact, normal, ent values\nfloat CL_TraceLine (vec3_t start, vec3_t end, vec3_t impact, vec3_t normal, int *ent);\nentity_t *TraceLineR (vec3_t start, vec3_t end, vec3_t impact, vec3_t normal, qboolean bsponly);\n\n//\n// cl_input\n//\ntypedef struct kbutton_s\n{\n\tint\t\tdown[MAX_SPLITS][2];\t\t// key nums holding it down\n\tint\t\tstate[MAX_SPLITS];\t\t\t// low bit is down state\n\n\tstruct kbutton_s *suppressed[MAX_SPLITS];\t//the button that was suppressed by this one getting pressed\n} kbutton_t;\n\nextern\tkbutton_t\tin_mlook;\nextern \tkbutton_t \tin_strafe;\nextern \tkbutton_t \tin_speed;\n\nextern\tfloat in_sensitivityscale;\n\nvoid CL_MakeActive(char *gamename);\nvoid CL_UpdateWindowTitle(void);\n\n#ifdef QUAKESTATS\nconst char *IN_GetPreselectedViewmodelName(unsigned int pnum);\nqboolean IN_WeaponWheelAccumulate(int pnum, float x, float y, float threshhold);\nqboolean IN_DrawWeaponWheel(int pnum);\nqboolean IN_WeaponWheelIsShown(void);\t//to decide when the game should be auto-paused.\n#endif\nvoid CL_InitInput (void);\nvoid CL_SendCmd (double frametime, qboolean mainloop);\nvoid CL_SendMove (usercmd_t *cmd);\n#ifdef NQPROT\nvoid CL_ParseTEnt (qboolean nqprot);\n#else\nvoid CL_ParseTEnt (void);\n#endif\nvoid CL_ParseTEnt_Sized (void);\nvoid CL_UpdateTEnts (void);\n\nenum beamtype_e\n{\t//these are internal ids, matching the beam table\n\tBT_Q1LIGHTNING1,\n\tBT_Q1LIGHTNING2,\n\tBT_Q1LIGHTNING3,\n\tBT_Q1BEAM,\n\n\tBT_Q2PARASITE,\n\tBT_Q2GRAPPLE,\n\tBT_Q2HEATBEAM,\n\tBT_Q2LIGHTNING,\n\n\tBT_H2LIGHTNING_SMALL,\n\tBT_H2CHAIN,\n\tBT_H2SUNSTAFF1,\n\tBT_H2SUNSTAFF1_SUB,\t//inner beam hack\n\tBT_H2SUNSTAFF2,\t\t//same model as 1, but different particle effect\n\tBT_H2LIGHTNING,\n\tBT_H2COLORBEAM,\n\tBT_H2ICECHUNKS,\n\tBT_H2GAZE,\n\tBT_H2FAMINE,\n};\ntypedef struct beam_s beam_t;\nbeam_t *CL_AddBeam (enum beamtype_e tent, int ent, vec3_t start, vec3_t end);\n\nvoid CL_ClearState (qboolean gamestart);\nvoid CLQ2_ClearState(void);\n\nvoid CL_ReadPackets (void);\nvoid CL_ReadPacket(void);\n\nint  CL_ReadFromServer (void);\nvoid CL_WriteToServer (usercmd_t *cmd);\n\nint Master_FindBestRoute(char *server, char *out, size_t outsize, int *directcost, int *chainedcost);\n\nfloat CL_KeyState (kbutton_t *key, int pnum, qboolean noslowstart);\nconst char *Key_KeynumToString (int keynum, int modifier);\nconst char *Key_KeynumToLocalString (int keynum, int modifier);\nint Key_StringToKeynum (const char *str, int *modifier);\nconst char *Key_GetBinding(int keynum, int bindmap, int modifier);\nvoid Key_GetBindMap(int *bindmaps);\nvoid Key_SetBindMap(int *bindmaps);\n\nvoid CL_UseIndepPhysics(qboolean allow);\nextern qboolean runningindepphys;\nqboolean CL_AllowIndependantSendCmd(qboolean allow);\t//returns previous state.\n\nvoid CL_FlushClientCommands(void);\nvoid VARGS CL_SendClientCommand(qboolean reliable, char *format, ...) LIKEPRINTF(2);\nvoid VARGS CL_SendSeatClientCommand(qboolean reliable, unsigned int seat, char *format, ...) LIKEPRINTF(3);\nfloat CL_FilterTime (double time, float wantfps, float limit, qboolean ignoreserver);\nint CL_RemoveClientCommands(char *command);\n\n//\n// cl_demo.c\n//\nvoid CL_StopPlayback (void);\nqboolean CL_GetDemoMessage (void);\nvoid CL_WriteDemoCmd (usercmd_t *pcmd);\nvoid CL_Demo_ClientCommand(char *commandtext);\t//for QTV.\n\nvoid CL_WriteSetDemoMessage (void);\t//'restarts' a qwd, when we have reloads/map changes in them\nvoid CL_WriteRecordQ2DemoMessage(sizebuf_t *msg);\nvoid CL_Stop_f (void);\nvoid CL_Record_f (void);\nvoid CL_ReRecord_f (void);\nvoid CL_DemoList_c(int argn, const char *partial, struct xcommandargcompletioncb_s *ctx);\nvoid CL_PlayDemo_f (void);\nvoid CL_QTVPlay_f (void);\nvoid CL_QTVPoll (void);\nvoid CL_QTVList_f (void);\nvoid CL_QTVDemos_f (void);\nvoid CL_DemoJump_f(void);\nvoid CL_DemoNudge_f(void);\nvoid CL_ProgressDemoTime(void);\nvoid CL_TimeDemo_f (void);\ntypedef struct \n{\n\tenum\n\t{\n\t\tQTVCT_NONE,\n\t\tQTVCT_STREAM,\n\t\tQTVCT_CONNECT,\n\t\tQTVCT_JOIN,\n\t\tQTVCT_OBSERVE,\n\t\tQTVCT_MAP\n\t} connectiontype;\n\tenum\n\t{\n\t\tQTVCT_NETQUAKE,\n\t\tQTVCT_QUAKEWORLD,\n\t\tQTVCT_QUAKE2,\n\t\tQTVCT_QUAKE3\n\t} protocol;\n\tchar server[256];\n\tchar splashscreen[256];\n\t//char *datafiles;\n} qtvfile_t;\nvoid CL_ParseQTVFile(vfsfile_t *f, const char *fname, qtvfile_t *result);\n\n//\n// cl_parse.c\n//\n#define NET_TIMINGS 256\n#define NET_TIMINGSMASK 255\nextern float\tpacket_latency[NET_TIMINGS];\nint CL_CalcNet (float scale);\nvoid CL_CalcNet2 (float *pings, float *pings_min, float *pings_max, float *pingms_stddev, float *pingfr, int *pingfr_min, int *pingfr_max, float *dropped, float *choked, float *invalid);\nvoid CL_ClearParseState(void);\nvoid CL_Parse_Disconnected(void);\nvoid CL_DumpPacket(void);\nvoid CL_ParseEstablished(void);\nvoid CLQW_ParseServerMessage (void);\nvoid CLEZ_ParseHiddenDemoMessage (void);\nvoid CLNQ_ParseServerMessage (void);\n#ifdef Q2CLIENT\nvoid CLQ2_ParseServerMessage (void);\n#endif\nvoid CL_ShowTrafficUsage(float x, float y);\nvoid CL_NewTranslation (int slot);\n\nint CL_IsDownloading(const char *localname);\nqboolean CL_CheckDLFile(const char *filename);\nqboolean CL_CheckOrEnqueDownloadFile (const char *filename, const char *localname, unsigned int flags);\nqboolean CL_EnqueDownload(const char *filename, const char *localname, unsigned int flags);\ndownloadlist_t *CL_DownloadFailed(const char *name, qdownload_t *qdl, enum dlfailreason_e failreason);\nint CL_DownloadRate(void);\nvoid CL_GetDownloadSizes(unsigned int *filecount, qofs_t *totalsize, qboolean *somesizesunknown);\nqboolean CL_ParseOOBDownload(void);\nvoid CL_DownloadFinished(qdownload_t *dl);\nvoid CL_RequestNextDownload (void);\nvoid CL_SendDownloadReq(sizebuf_t *msg);\nvoid Sound_CheckDownload(const char *s); /*checkorenqueue a sound file*/\n\nqboolean CL_IsUploading(void);\nvoid CL_NextUpload(void);\nvoid CL_StartUpload (qbyte *data, int size);\nvoid CL_StopUpload(void);\n\nqboolean CL_CheckBaselines (int size);\n\n//\n// view.c\n//\nvoid V_StartPitchDrift (playerview_t *pv);\nvoid V_StopPitchDrift (playerview_t *pv);\n\nvoid V_RenderView (qboolean no2d);\nvoid V_Register (void);\nvoid V_ParseDamage (playerview_t *pv);\nvoid V_SetContentsColor (int contents);\n\n//used directly by csqc\nvoid V_CalcRefdef (playerview_t *pv);\nvoid V_ClearRefdef(playerview_t *pv);\nvoid V_ApplyRefdef(void);\nvoid V_CalcGunPositionAngle (playerview_t *pv, float bob);\nfloat V_CalcBob (playerview_t *pv, qboolean queryold);\nvoid DropPunchAngle (playerview_t *pv);\n\nint V_EditExternalModels(int newviewentity, entity_t *viewentities, int maxviewenties);\nvoid V_DepthSortEntities(float *vieworg);\n\n\n//\n// cl_tent\n//\nvoid CL_RegisterParticles(void);\nvoid CL_InitTEnts (void);\nvoid CL_InitTEntSounds (void);\nvoid CL_ClearTEnts (void);\nvoid CL_ClearTEntParticleState (void);\nvoid CL_ClearCustomTEnts(void);\nvoid CL_ParseCustomTEnt(void);\nqboolean CL_WriteCustomTEnt(sizebuf_t *buf, int id);\nvoid CL_ParseEffect (qboolean effect2);\n\nvoid CLNQ_ParseParticleEffect (void);\nvoid CL_ParseParticleEffect2 (void);\nvoid CL_ParseParticleEffect3 (void);\nvoid CL_ParseParticleEffect4 (void);\n\nint CL_TranslateParticleFromServer(int sveffect);\nvoid CL_ParseTrailParticles(void);\nvoid CL_ParsePointParticles(qboolean compact);\nvoid CL_SpawnSpriteEffect(vec3_t org, vec3_t dir, vec3_t orientationup, struct model_s *model, int startframe, int framecount, float framerate, float alpha, float scale, float randspin, float gravity, int traileffect, unsigned int renderflags, int skinnum, float red, float green, float blue);\t/*called from the particlesystem*/\n\n//\n// cl_ents.c\n//\nvoid CL_SetSolidPlayers (void);\nvoid CL_SetUpPlayerPrediction(qboolean dopred);\nvoid CL_LinkStaticEntities(void *pvs, int *areas);\nvoid CL_TransitionEntities (void); /*call at the start of the frame*/\nvoid CL_EmitEntities (void);\nvoid CL_ClearProjectiles (void);\nvoid CL_ParseProjectiles (int modelindex, qboolean nails2);\nvoid CLQW_ParsePacketEntities (qboolean delta);\nvoid CLFTE_ParseEntities (void);\nvoid CLFTE_ParseBaseline(entity_state_t *es, qboolean numberisimportant);\nvoid CL_SetSolidEntities (void);\nvoid CLQW_ParsePlayerinfo (void);\nvoid CL_ParseClientPersist(void);\n//these last ones are needed for csqc handling of engine-bound ents.\nvoid CL_ClearEntityLists(void);\nvoid CL_FreeVisEdicts(void);\nvoid CL_LinkViewModel(void);\nvoid CL_LinkPlayers (void);\nvoid CL_LinkPacketEntities (void);\nvoid CL_LinkProjectiles (void);\nvoid CL_ClearLerpEntsParticleState (void);\nqboolean CL_MayLerp(void);\n\n//\n//pr_csqc.c\n//\n#ifdef CSQC_DAT\nqboolean CSQC_Inited(void);\nvoid\t CSQC_RendererRestarted(qboolean initing);\nqboolean CSQC_UnconnectedOkay(qboolean inprinciple);\nqboolean CSQC_UnconnectedInit(void);\nqboolean CSQC_CheckDownload(const char *name, unsigned int checksum, size_t checksize);\t//reports whether we already have a usable csprogs.dat\nqboolean CSQC_Init (qboolean anycsqc, const char *csprogsname, unsigned int checksum, size_t progssize);\nqboolean CSQC_ConsoleLink(char *text, char *info);\nvoid\t CSQC_RegisterCvarsAndThings(void);\nqboolean CSQC_SetupToRenderPortal(int entnum);\nqboolean CSQC_DrawView(void);\nqboolean CSQC_DrawHud(playerview_t *pv);\nqboolean CSQC_DrawScores(playerview_t *pv);\nqboolean CSQC_UseGamecodeLoadingScreen(void);\nvoid\t CSQC_Shutdown(void);\nqboolean CSQC_StuffCmd(int lplayernum, char *cmd, char *cmdend);\nvoid\t CSQC_MapEntityEdited(int modelindex, int idx, const char *newe);\n//qboolean CSQC_LoadResource(char *resname, char *restype);\nqboolean CSQC_ParsePrint(char *message, int printlevel);\nqboolean CSQC_ParseGamePacket(int seat, qboolean sized);\nqboolean CSQC_CenterPrint(int seat, const char *cmd);\nvoid\t CSQC_ServerInfoChanged(void);\nvoid\t CSQC_PlayerInfoChanged(int player);\nqboolean CSQC_Parse_Damage(int seat, float save, float take, vec3_t source);\nqboolean CSQC_Parse_SetAngles(int seat, vec3_t newangles, qboolean wasdelta);\nvoid\t CSQC_Input_Frame(int seat, usercmd_t *cmd);\nvoid\t CSQC_WorldLoaded(void);\nqboolean CSQC_ParseTempEntity(void);\nqboolean CSQC_ConsoleCommand(int seat, const char *cmd);\nqboolean CSQC_KeyPress(int key, int unicode, qboolean down, unsigned int devid);\nqboolean CSQC_MouseMove(float xdelta, float ydelta, unsigned int devid);\nqboolean CSQC_MousePosition(float xabs, float yabs, unsigned int devid);\nqboolean CSQC_JoystickAxis(int axis, float value, unsigned int devid);\nqboolean CSQC_Accelerometer(float x, float y, float z);\nqboolean CSQC_Gyroscope(float x, float y, float z);\nint\t\t CSQC_StartSound(int entnum, int channel, char *soundname, vec3_t pos, float vol, float attenuation, float pitchmod, float timeofs, unsigned int flags);\nvoid\t CSQC_ParseEntities(qboolean sized);\nconst char *CSQC_GetExtraFieldInfo(void *went, char *out, size_t outsize);\nvoid\t CSQC_ResetTrails(void);\n\nqboolean CSQC_DeltaPlayer(int playernum, player_state_t *state);\nvoid\t CSQC_DeltaStart(float time);\nqboolean CSQC_DeltaUpdate(entity_state_t *src);\nvoid\t CSQC_DeltaEnd(void);\n\nvoid\t CSQC_CvarChanged(cvar_t *var);\n#else\n#define CSQC_UnconnectedOkay(inprinciple) false\n#define CSQC_UnconnectedInit() false\n#define CSQC_UseGamecodeLoadingScreen() false\n#define CSQC_Parse_SetAngles(seat,newangles,wasdelta) false\n#define CSQC_ServerInfoChanged()\n#define CSQC_PlayerInfoChanged(player)\n#endif\n\n//\n// cl_pred.c\n//\nvoid CL_InitPrediction (void);\nvoid CL_PredictMove (void);\nvoid CL_PredictUsercmd (int seat, int entnum, player_state_t *from, player_state_t *to, usercmd_t *u);\n#ifdef Q2CLIENT\nvoid CLQ2_CheckPredictionError (void);\n#endif\nvoid CL_CalcClientTime(void);\n\n//\n// cl_cam.c\n//\nqboolean Cam_DrawViewModel(playerview_t *pv);\nint Cam_TrackNum(playerview_t *pv);\nvoid Cam_Unlock(playerview_t *pv);\t\t\t\t//revert to freecam or so, because that entity failed.\nvoid Cam_Lock(playerview_t *pv, int playernum);\t//attempt to lock on to the given player.\nvoid Cam_NowLocked(playerview_t *pv);\t\t\t\t\t\t//player was located, track them now\nvoid Cam_SelfTrack(playerview_t *pv);\nvoid Cam_Track(playerview_t *pv, usercmd_t *cmd);\nvoid Cam_SetModAutoTrack(int userid);\nvoid Cam_FinishMove(playerview_t *pv, usercmd_t *cmd);\nvoid Cam_Reset(void);\nvoid Cam_TrackPlayer(int seat, char *cmdname, char *plrarg);\nvoid CL_InitCam(void);\nvoid Cam_AutoTrack_Update(const char *mode);\t//reset autotrack setting (because we started a new map or whatever)\n\n//\n//zqtp.c\n//\n#define TPM_UNKNOWN    0\n#define TPM_NORMAL     1\n#define TPM_TEAM       2\n#define TPM_SPECTATOR  4\n#define TPM_FAKED     16\n#define TPM_OBSERVEDTEAM  32\n#define TPM_QTV       64\t\t//should only be qtv_say_game/qtv_say_team_game\n\nvoid\t\tCL_Say (qboolean team, char *extra);\nint\t\t\tTP_CategorizeMessage (char *s, int *offset, player_info_t **plr);\nvoid\t\tTP_ExecTrigger (char *s, qboolean indemos);\t\t//executes one of the user's f_foo aliases from some engine-defined event.\nqboolean\tTP_FilterMessage (char *s);\nvoid\t\tTP_Init(void);\nqboolean\tTP_HaveLocations(void);\nchar*\t\tTP_LocationName (const vec3_t location);\nvoid\t\tTP_NewMap (void);\nqboolean\tTP_CheckSoundTrigger (char *str);\t\t\t\t//plays sound files when some substring exists in chat.\nvoid\t\tTP_SearchForMsgTriggers (char *s, int level);\t//msg_trigger: executes aliases when a chat message contains some user-defined string.\nqboolean\tTP_SuppressMessage(char *buf);\t//true when the message contains macro results that the local player isn't meant to see (teamplay messages that contain enemy player counts for instance)\nchar\t\t*TP_GenerateDemoName(void);\t\t//makes something up.\n#ifdef QUAKESTATS\n//hack zone: this stuff makes assumptions about quake-only stats+items+rules and stuff.\nvoid\t\tTP_CheckPickupSound(char *s, vec3_t org, int seat);\nvoid\t\tTP_ParsePlayerInfo(player_state_t *oldstate, player_state_t *state, player_info_t *info);\nqboolean\tTP_IsPlayerVisible(vec3_t origin);\nqboolean\tTP_SoundTrigger(char *message);\nvoid\t\tTP_StatChanged (int stat, int value);\nvoid\t\tTP_UpdateAutoStatus(void);\n#endif\n#ifdef QWSKINS\ncolourised_t *TP_FindColours(char *name);\n#endif\n\n//\n// skin.c\n//\n\ntypedef struct\n{\n    char\tmanufacturer;\n    char\tversion;\n    char\tencoding;\n    char\tbits_per_pixel;\n    unsigned short\txmin,ymin,xmax,ymax;\n    unsigned short\thres,vres;\n    unsigned char\tpalette[48];\n    char\treserved;\n    char\tcolor_planes;\n    unsigned short\tbytes_per_line;\n    unsigned short\tpalette_type;\n    char\tfiller[58];\n//    unsigned char\tdata;\t\t\t// unbounded\n} pcx_t;\nqbyte *ReadPCXData(qbyte *buf, int length, int width, int height, qbyte *result);\n\n\nqwskin_t *Skin_Lookup (char *fullname);\nchar *Skin_FindName (player_info_t *sc);\nvoid\tSkin_Find (player_info_t *sc);\nqbyte\t*Skin_TryCache8 (qwskin_t *skin);\nvoid\tSkin_Skins_f (void);\nvoid\tSkin_FlushSkin(char *name);\nvoid\tSkin_AllSkins_f (void);\nvoid\tSkin_NextDownload (void);\nvoid Skin_FlushPlayers(void);\nvoid Skin_FlushAll(void);\n\n#define RSSHOT_WIDTH 320\n#define RSSHOT_HEIGHT 200\n\n\n\n\n\n//valid.c\n#define RULESET_USERINFO\t\"*rs\"\t//the userinfo used to report the ruleset name to other clients. FIXME: remove the _dbg when we think its all working properly.\nvoid\t\tRuleset_Check(char *keyval, char *out, size_t outsize);\nvoid\t\tRuleset_Scan(void);\nvoid\t\tRuleset_Shutdown(void);\nconst char *Ruleset_GetRulesetName(void);\nqboolean\tRuleset_FileLoaded(const char *filename, const qbyte *filedata, size_t filesize);\t//return false if the file is not permitted.\nvoid\tValidation_Apply_Ruleset(void);\nvoid\tValidation_FlushFileList(void);\nvoid\tValidation_CheckIfResponse(char *text);\nvoid\tValidation_DelatchRulesets(void);\nvoid\tInitValidation(void);\nvoid\tValidation_Auto_Response(int playernum, char *s);\n\nextern\tqboolean f_modified_particles;\nextern\tqboolean care_f_modified;\n\n\n//random files (fixme: clean up)\n\n#ifdef Q2CLIENT\nunsigned int CLQ2_GatherSounds(vec3_t *positions, unsigned int *entnums, sfx_t **sounds, unsigned int max);\nvoid CLQ2_ParseTEnt (void);\nvoid CLQ2_AddEntities (void);\nvoid CLQ2_ParseBaseline (void);\nvoid CLQ2_ClearParticleState(void);\nvoid CLR1Q2_ParsePlayerUpdate(void);\nvoid CLQ2_ParseFrame (int extrabits);\nvoid CLQ2_ParseMuzzleFlash (void);\nvoid CLQ2_ParseMuzzleFlash2 (void);\nvoid CLQ2EX_ParseMuzzleFlash3 (void);\nvoid CLQ2_ParseInventory (int seat);\nint CLQ2_RegisterTEntModels (void);\nvoid CLQ2_WriteDemoBaselines(sizebuf_t *buf);\n#endif\n\n#ifdef HLCLIENT\n//networking\nvoid CLHL_LoadClientGame(void);\nint CLHL_ParseGamePacket(void);\nint CLHL_AnimateViewEntity(entity_t *ent);\n//screen\nint CLHL_DrawHud(void);\n//inputs\nint CLHL_GamecodeDoesMouse(void);\nint CLHL_MouseEvent(unsigned int buttonmask);\nvoid CLHL_SetMouseActive(int activate);\nint CLHL_BuildUserInput(int msecs, usercmd_t *cmd);\n#endif\n\n#ifdef NQPROT\nvoid CLNQ_ParseEntity(unsigned int bits);\nvoid NQ_P_ParseParticleEffect (void);\nvoid CLNQ_SignonReply (void);\nvoid NQ_BeginConnect(char *to);\nvoid NQ_ContinueConnect(char *to);\nint CLNQ_GetMessage (void);\n#endif\n\nvoid CL_BeginServerReconnect(void);\nqboolean CL_IsPendingServerAddress(netadr_t *adr);\nvoid CL_Transfer(netadr_t *adr);\n\nvoid SV_User_f (void);\t//called by client version of the function\nvoid SV_Serverinfo_f (void);\nvoid SV_ConSay_f(void);\n\n\n\n#ifdef TEXTEDITOR\nextern console_t *editormodal;\nvoid Editor_Draw(void);\nvoid Editor_Init(void);\nstruct pubprogfuncs_s;\nvoid Editor_ProgsKilled(struct pubprogfuncs_s *dead);\n#else\n#define editormodal false\n#endif\n\nvoid SCR_StringToRGB (char *rgbstring, float *rgb, float rgbinputscale);\n\nstruct model_s;\nvoid CL_AddVWeapModel(entity_t *player, struct model_s *model);\n\ntypedef struct cin_s cin_t;\n#ifdef HAVE_MEDIA_DECODER\n\n#ifdef Q2CLIENT /*q2 cinematics*/\nstruct cinematics_s;\nvoid CIN_StopCinematic (struct cinematics_s *cin);\nstruct cinematics_s *CIN_PlayCinematic (char *arg);\nint CIN_RunCinematic (struct cinematics_s *cin, float playbacktime, qbyte **outdata, int *outwidth, int *outheight, qbyte **outpalette);\nvoid CIN_Rewind(struct cinematics_s *cin);\n#endif\n\ntypedef enum\n{\n\tCINSTATE_INVALID,\t//also reported for not playing\n\tCINSTATE_PLAY,\n\tCINSTATE_LOOP,\n\tCINSTATE_PAUSE,\n\tCINSTATE_ENDED,\n\tCINSTATE_FLUSHED,\t//video will restart from beginning\n} cinstates_t;\n/*media playing system*/\nqboolean Media_PlayFilm(char *name, qboolean enqueue);\nqboolean Media_StopFilm(qboolean all);\nstruct cin_s *Media_StartCin(char *name);\ntexid_tf Media_UpdateForShader(cin_t *cin);\nvoid Media_ShutdownCin(cin_t *cin);\n\n//these accept NULL for cin to mean the current fullscreen video\nvoid Media_Send_Command(cin_t *cin, const char *command);\nvoid Media_Send_MouseMove(cin_t *cin, float x, float y);\nvoid Media_Send_Resize(cin_t *cin, int x, int y);\nvoid Media_Send_GetSize(cin_t *cin, int *x, int *y, float *aspect);\nvoid Media_Send_KeyEvent(cin_t *cin, int button, int unicode, int event);\nvoid Media_Send_Reset(cin_t *cin);\nvoid Media_SetState(cin_t *cin, cinstates_t newstate);\ncinstates_t Media_GetState(cin_t *cin);\nconst char *Media_Send_GetProperty(cin_t *cin, const char *key);\n\n#else\n#define Media_PlayFilm(n,e) false\n#define Media_StopFilm(a) (void)true\n#endif\n\nvoid Media_SaveTracks(vfsfile_t *outcfg);\nvoid Media_Init(void);\nqboolean Media_NamedTrack(const char *initialtrack, const char *looptrack);\t//new background music interface\nvoid Media_NumberedTrack(unsigned int initialtrack, unsigned int looptrack);\t\t\t\t//legacy cd interface for protocols that only support numbered tracks.\nvoid Media_EndedTrack(void);\t//cd is no longer running, media code needs to pick a new track (cd track or faketrack)\n\nvoid MVD_Interpolate(void);\n\nint Stats_GetKills(int playernum);\nint Stats_GetTKills(int playernum);\nint Stats_GetDeaths(int playernum);\nint Stats_GetTouches(int playernum);\nint Stats_GetCaptures(int playernum);\nqboolean Stats_HaveFlags(int mode);\nqboolean Stats_HaveKills(void);\nfloat Stats_GetLastOwnFrag(int seat, char *res, int reslen);\nvoid VARGS Stats_Message(char *msg, ...) LIKEPRINTF(1);\nqboolean Stats_ParsePrintLine(const char *line);\nqboolean Stats_ParsePickups(const char *line);\nvoid Stats_NewMap(void);\nvoid Stats_Clear(void);\nvoid Stats_Init(void);\n\n//enum uploadfmt;\n/*struct mediacallbacks_s\n{\t//functions provided by the engine/renderer, for faster/off-thread updates\n\tqboolean pbocanoffthread;\n\tqboolean (VARGS *PBOLock)(struct mediacallbacks_s *ctx, size_t width, size_t height, uploadfmt_t fmt, qboolean *lost);\n\tvoid (VARGS *PBOUpdate)(struct mediacallbacks_s *ctx, void *data, size_t width, size_t height, int stride);\n\tvoid (VARGS *PBOUnlock)(struct mediacallbacks_s *ctx);\n\n\tvoid (VARGS *AudioStream) (void *auddata, int rate, int frames, int channels, int width);\n\n\tvoid (VARGS *WorkQueue) (void *wctx, void (VARGS *callback)(void *data), void *data);\n\tvoid (VARGS *WorkSync)  (void *wctx, int *address, int oldvalue);\t//blocks until the address changes. make sure you queued something that will change it from that value. oldvalue is present to avoid races. if you're reading the address then you should probably volatile it to avoid compiler opts reading it twice (fixme: needs a proper barrier).\n};\n*/\ntypedef struct\n{\n\tsize_t structsize;\n\tconst char *drivername;\n\tvoid *(VARGS *createdecoder)(const char *name);\n\tqboolean (VARGS *decodeframe)(void *ctx, qboolean nosound, qboolean forcevideo, double mediatime, void (QDECL *uploadtexture)(void *ectx, uploadfmt_t fmt, int width, int height, void *data, void *palette), void *ectx);\n\tvoid (VARGS *shutdown)(void *ctx);\n\tvoid (VARGS *rewind)(void *ctx);\n\n\t//these are any interactivity functions you might want...\n\tvoid (VARGS *cursormove) (void *ctx, float posx, float posy);\t//pos is 0-1\n\tvoid (VARGS *key) (void *ctx, int code, int unicode, int event);\n\tqboolean (VARGS *setsize) (void *ctx, int width, int height);\n\tvoid (VARGS *getsize) (void *ctx, int *width, int *height);\n\tvoid (VARGS *changestream) (void *ctx, const char *streamname);\n\n\tqboolean (VARGS *getproperty) (void *ctx, const char *field, char *out, size_t *outsize);\t//if out is null, returns required buffer size. returns 0 on failure / buffer too small\n\n//\tvoid *(VARGS *createdecoderCB)(const char *name, struct mediacallbacks_s *callbacks);\n} media_decoder_funcs_t;\ntypedef struct\n{\n\tsize_t structsize;\n\tconst char *drivername;\n\tconst char *description;\n\tconst char *extensions;\n\tvoid *(VARGS *capture_begin) (char *streamname, int videorate, int width, int height, int *sndkhz, int *sndchannels, int *sndbits);\n\tvoid (VARGS *capture_video) (void *ctx, int frame, void *data, int stride, int width, int height, enum uploadfmt fmt);\n\tvoid (VARGS *capture_audio) (void *ctx, void *data, int bytes);\n\tvoid (VARGS *capture_end) (void *ctx);\n} media_encoder_funcs_t;\nextern struct plugin_s *currentplug;\nqboolean Media_RegisterDecoder(struct plugin_s *plug, media_decoder_funcs_t *funcs);\nqboolean Media_UnregisterDecoder(struct plugin_s *plug, media_decoder_funcs_t *funcs);\nqboolean Media_RegisterEncoder(struct plugin_s *plug, media_encoder_funcs_t *funcs);\nqboolean Media_UnregisterEncoder(struct plugin_s *plug, media_encoder_funcs_t *funcs);\n"
  },
  {
    "path": "engine/client/clq2_cin.c",
    "content": "#include \"quakedef.h\"\n#if defined(HAVE_MEDIA_DECODER) && defined(Q2CLIENT)\n\ntypedef struct\n{\n\tqbyte\t*data;\n\tsize_t\tcount;\n} cblock_t;\n\ntypedef struct cinematics_s\n{\n\tqboolean\trestart_sound;\n\tint\t\ts_rate;\n\tint\t\ts_width;\n\tint\t\ts_channels;\n\n\tint\t\twidth;\n\tint\t\theight;\n\tqbyte\t*pic;\n\tqbyte\t*pic_pending;\n\n\t// order 1 huffman stuff\n\tint\t\t*hnodes1;\t// [256][256][2];\n\tint\t\tnumhnodes1[256];\n\n\tint\t\th_used[512];\n\tint\t\th_count[512];\n\n\n\tint cinematictime;\n\tqboolean cinematicpalette_active;\n\tqbyte cinematicpalette[768];\n\n\tqofs_t filestart;\n\tvfsfile_t *cinematic_file;\n\tint cinematicframe;\n} cinematics_t;\n\n\nvoid CIN_StopCinematic (cinematics_t *cin)\n{\n\tcin->cinematictime = 0;\t// done\n\tif (cin->pic)\n\t{\n\t\tZ_Free (cin->pic);\n\t\tcin->pic = NULL;\n\t}\n\tif (cin->pic_pending)\n\t{\n\t\tZ_Free (cin->pic_pending);\n\t\tcin->pic_pending = NULL;\n\t}\n\tif (cin->cinematic_file)\n\t{\n\t\tVFS_CLOSE (cin->cinematic_file);\n\t\tcin->cinematic_file = NULL;\n\t}\n\tif (cin->hnodes1)\n\t{\n\t\tZ_Free (cin->hnodes1);\n\t\tcin->hnodes1 = NULL;\n\t}\n\n\tZ_Free(cin);\n}\n\n//==========================================================================\n\n/*\n==================\nSmallestNode1\n==================\n*/\nstatic int\tSmallestNode1 (cinematics_t *cin, int numhnodes)\n{\n\tint\t\ti;\n\tint\t\tbest, bestnode;\n\n\tbest = 99999999;\n\tbestnode = -1;\n\tfor (i=0 ; i<numhnodes ; i++)\n\t{\n\t\tif (cin->h_used[i])\n\t\t\tcontinue;\n\t\tif (!cin->h_count[i])\n\t\t\tcontinue;\n\t\tif (cin->h_count[i] < best)\n\t\t{\n\t\t\tbest = cin->h_count[i];\n\t\t\tbestnode = i;\n\t\t}\n\t}\n\n\tif (bestnode == -1)\n\t\treturn -1;\n\n\tcin->h_used[bestnode] = true;\n\treturn bestnode;\n}\n\n\n/*\n==================\nHuff1TableInit\n\nReads the 64k counts table and initializes the node trees\n==================\n*/\nstatic void Huff1TableInit (cinematics_t *cin)\n{\n\tint\t\tprev;\n\tint\t\tj;\n\tint\t\t*node, *nodebase;\n\tqbyte\tcounts[256];\n\tint\t\tnumhnodes;\n\n\tcin->hnodes1 = Z_Malloc (256*256*2*4);\n\tmemset (cin->hnodes1, 0, 256*256*2*4);\n\n\tfor (prev=0 ; prev<256 ; prev++)\n\t{\n\t\tmemset (cin->h_count,0,sizeof(cin->h_count));\n\t\tmemset (cin->h_used,0,sizeof(cin->h_used));\n\n\t\t// read a row of counts\n\t\tif (VFS_READ (cin->cinematic_file, counts, sizeof(counts)) != sizeof(counts))\n\t\t\tCon_Printf(\"Huff1TableInit: read error\\n\");\n\t\tfor (j=0 ; j<256 ; j++)\n\t\t\tcin->h_count[j] = counts[j];\n\n\t\t// build the nodes\n\t\tnumhnodes = 256;\n\t\tnodebase = cin->hnodes1 + prev*256*2;\n\n\t\twhile (numhnodes != 511)\n\t\t{\n\t\t\tnode = nodebase + (numhnodes-256)*2;\n\n\t\t\t// pick two lowest counts\n\t\t\tnode[0] = SmallestNode1 (cin, numhnodes);\n\t\t\tif (node[0] == -1)\n\t\t\t\tbreak;\t// no more\n\n\t\t\tnode[1] = SmallestNode1 (cin, numhnodes);\n\t\t\tif (node[1] == -1)\n\t\t\t\tbreak;\n\n\t\t\tcin->h_count[numhnodes] = cin->h_count[node[0]] + cin->h_count[node[1]];\n\t\t\tnumhnodes++;\n\t\t}\n\n\t\tcin->numhnodes1[prev] = numhnodes-1;\n\t}\n}\n\n/*\n==================\nHuff1Decompress\n==================\n*/\nstatic cblock_t Huff1Decompress (cinematics_t *cin, cblock_t in)\n{\n\tqbyte\t\t*input;\n\tqbyte\t\t*out_p;\n\tint\t\t\tnodenum;\n\tint\t\t\tcount;\n\tcblock_t\tout;\n\tint\t\t\tinbyte;\n\tint\t\t\t*hnodes, *hnodesbase;\n//int\t\ti;\n\n\t// get decompressed count\n\tcount = in.data[0] + (in.data[1]<<8) + (in.data[2]<<16) + (in.data[3]<<24);\n\tinput = in.data + 4;\n\tout_p = out.data = Z_Malloc (count);\n\n\t// read bits\n\n\thnodesbase = cin->hnodes1 - 256*2;\t// nodes 0-255 aren't stored\n\n\thnodes = hnodesbase;\n\tnodenum = cin->numhnodes1[0];\n\twhile (count)\n\t{\n\t\tinbyte = *input++;\n\t\t//-----------\n\t\tif (nodenum < 256)\n\t\t{\n\t\t\thnodes = hnodesbase + (nodenum<<9);\n\t\t\t*out_p++ = nodenum;\n\t\t\tif (!--count)\n\t\t\t\tbreak;\n\t\t\tnodenum = cin->numhnodes1[nodenum];\n\t\t}\n\t\tnodenum = hnodes[nodenum*2 + (inbyte&1)];\n\t\tinbyte >>=1;\n\t\t//-----------\n\t\tif (nodenum < 256)\n\t\t{\n\t\t\thnodes = hnodesbase + (nodenum<<9);\n\t\t\t*out_p++ = nodenum;\n\t\t\tif (!--count)\n\t\t\t\tbreak;\n\t\t\tnodenum = cin->numhnodes1[nodenum];\n\t\t}\n\t\tnodenum = hnodes[nodenum*2 + (inbyte&1)];\n\t\tinbyte >>=1;\n\t\t//-----------\n\t\tif (nodenum < 256)\n\t\t{\n\t\t\thnodes = hnodesbase + (nodenum<<9);\n\t\t\t*out_p++ = nodenum;\n\t\t\tif (!--count)\n\t\t\t\tbreak;\n\t\t\tnodenum = cin->numhnodes1[nodenum];\n\t\t}\n\t\tnodenum = hnodes[nodenum*2 + (inbyte&1)];\n\t\tinbyte >>=1;\n\t\t//-----------\n\t\tif (nodenum < 256)\n\t\t{\n\t\t\thnodes = hnodesbase + (nodenum<<9);\n\t\t\t*out_p++ = nodenum;\n\t\t\tif (!--count)\n\t\t\t\tbreak;\n\t\t\tnodenum = cin->numhnodes1[nodenum];\n\t\t}\n\t\tnodenum = hnodes[nodenum*2 + (inbyte&1)];\n\t\tinbyte >>=1;\n\t\t//-----------\n\t\tif (nodenum < 256)\n\t\t{\n\t\t\thnodes = hnodesbase + (nodenum<<9);\n\t\t\t*out_p++ = nodenum;\n\t\t\tif (!--count)\n\t\t\t\tbreak;\n\t\t\tnodenum = cin->numhnodes1[nodenum];\n\t\t}\n\t\tnodenum = hnodes[nodenum*2 + (inbyte&1)];\n\t\tinbyte >>=1;\n\t\t//-----------\n\t\tif (nodenum < 256)\n\t\t{\n\t\t\thnodes = hnodesbase + (nodenum<<9);\n\t\t\t*out_p++ = nodenum;\n\t\t\tif (!--count)\n\t\t\t\tbreak;\n\t\t\tnodenum = cin->numhnodes1[nodenum];\n\t\t}\n\t\tnodenum = hnodes[nodenum*2 + (inbyte&1)];\n\t\tinbyte >>=1;\n\t\t//-----------\n\t\tif (nodenum < 256)\n\t\t{\n\t\t\thnodes = hnodesbase + (nodenum<<9);\n\t\t\t*out_p++ = nodenum;\n\t\t\tif (!--count)\n\t\t\t\tbreak;\n\t\t\tnodenum = cin->numhnodes1[nodenum];\n\t\t}\n\t\tnodenum = hnodes[nodenum*2 + (inbyte&1)];\n\t\tinbyte >>=1;\n\t\t//-----------\n\t\tif (nodenum < 256)\n\t\t{\n\t\t\thnodes = hnodesbase + (nodenum<<9);\n\t\t\t*out_p++ = nodenum;\n\t\t\tif (!--count)\n\t\t\t\tbreak;\n\t\t\tnodenum = cin->numhnodes1[nodenum];\n\t\t}\n\t\tnodenum = hnodes[nodenum*2 + (inbyte&1)];\n\t\tinbyte >>=1;\n\t}\n\n\tif (input - in.data != in.count && input - in.data != in.count+1)\n\t{\n\t\tCon_Printf (\"Decompression overread by %i\\n\", (int)((input - in.data) - in.count));\n\t}\n\tout.count = out_p - out.data;\n\n\treturn out;\n}\n\n/*\n==================\nSCR_ReadNextFrame\n==================\n*/\nstatic qbyte *CIN_ReadNextFrame (cinematics_t *cin)\n{\n\tint\t\tr;\n\tint\t\tcommand;\n\tqbyte\tsamples[22050/14*4];\n\tqbyte\tcompressed[0x20000];\n\tint\t\tsize;\n\tqbyte\t*pic;\n\tcblock_t\tin, huf1;\n\tint\t\tstart, end, count;\n\n\t// read the next frame\n\tr = VFS_READ (cin->cinematic_file, &command, 4);\n\tif (r == 0)\t\t// we'll give it one more chance\n\t\tr = VFS_READ (cin->cinematic_file, &command, 4);\n\n\tif (r != 4)\n\t\treturn NULL;\n\tcommand = LittleLong(command);\n\tif (command == 2)\n\t\treturn NULL;\t// last frame marker\n\n\tif (command == 1)\n\t{\t// read palette\n\t\tVFS_READ (cin->cinematic_file, cin->cinematicpalette, sizeof(cin->cinematicpalette));\n\t}\n\n\t// decompress the next frame\n\tVFS_READ (cin->cinematic_file, &size, 4);\n\tsize = LittleLong(size);\n\tif (size > sizeof(compressed) || size < 1)\n\t\tHost_Error (\"Bad compressed frame size\");\n\tif (VFS_READ (cin->cinematic_file, compressed, size) != size)\n\t\tCon_Printf(\"CIN_ReadNextFrame: Failed reading video data\\n\");\n\n\t// read sound. the number of samples per frame is not actually constant, despite a constant rate and fps...\n\t// life is shit like that.\n\tstart = (cin->cinematicframe*cin->s_rate)/14;\n\tend = ((cin->cinematicframe+1)*cin->s_rate)/14;\n\tcount = end - start;\n\n\tif (VFS_READ (cin->cinematic_file, samples, count*cin->s_width*cin->s_channels) != count*cin->s_width*cin->s_channels)\n\t\tCon_Printf(\"CIN_ReadNextFrame: Failed reading audio data\\n\");\n\n\tif (cin->s_width == 1)\n\t\tCOM_CharBias(samples, count*cin->s_channels);\n\telse if (cin->s_width == 2)\n\t\tCOM_SwapLittleShortBlock((short *)samples, count*cin->s_channels);\n\n\tS_RawAudio (SOURCEID_CINEMATIC, samples, cin->s_rate, count, cin->s_channels, cin->s_width, volume.value );\n\n\tin.data = compressed;\n\tin.count = size;\n\n\thuf1 = Huff1Decompress (cin, in);\n\n\tpic = huf1.data;\n\n\tcin->cinematicframe++;\n\n\treturn pic;\n}\n\nvoid CIN_Rewind(cinematics_t *cin)\n{\n\tVFS_SEEK(cin->cinematic_file, cin->filestart);\n\n\tcin->cinematicframe = 0;\n\tcin->cinematictime = -1000/14;\n}\n\n/*\n==================\nSCR_RunCinematic\n\n0 = error\n1 = success\n2 = success but nothing changed\n==================\n*/\nint CIN_RunCinematic (cinematics_t *cin, float playbacktime, qbyte **outdata, int *outwidth, int *outheight, qbyte **outpalette)\n{\n\tint\t\tframe;\n\n\tif (cin->cinematictime-3 > playbacktime*1000)\n\t\tcin->cinematictime = playbacktime*1000;\n\n\tframe = (playbacktime*1000 - cin->cinematictime)*14/1000;\n\tif (cin->pic && frame <= cin->cinematicframe)\n\t{\n\t\t*outdata = cin->pic;\n\t\t*outwidth = cin->width;\n\t\t*outheight = cin->height;\n\t\t*outpalette = cin->cinematicpalette;\n\t\treturn 2;\n\t}\n\tif (frame > cin->cinematicframe+1)\n\t{\n\t\tCon_DPrintf (\"Dropped frame: %i > %i\\n\", frame, cin->cinematicframe+1);\n\t\tcin->cinematictime = playbacktime*1000 - cin->cinematicframe*1000/14;\n\t}\n\tif (cin->pic)\n\t{\n\t\tZ_Free (cin->pic);\n\t\tcin->pic = NULL;\n\t}\n\tcin->pic = CIN_ReadNextFrame (cin);\n\tif (!cin->pic)\n\t{\n\t\treturn 0;\n\t}\n\n\t*outdata = cin->pic;\n\t*outwidth = cin->width;\n\t*outheight = cin->height;\n\t*outpalette = cin->cinematicpalette;\n\treturn 1;\n}\n\n/*\n==================\nSCR_PlayCinematic\n\n==================\n*/\ncinematics_t *CIN_PlayCinematic (char *arg)\n{\n\tint\t\twidth, height;\n\tchar\tname[MAX_OSPATH];\n//\tint\t\told_khz;\n\tvfsfile_t *file;\n\n\tcinematics_t *cin = NULL;\n\n\tfile = FS_OpenVFS(arg, \"rb\", FS_GAME);\n\n\tif (!file)\n\t{\n\t\tsnprintf (name, sizeof(name), \"video/%s\", arg);\n\t\tfile = FS_OpenVFS(name, \"rb\", FS_GAME);\n\t}\n\n\tif (file)\n\t\tcin = Z_Malloc(sizeof(*cin));\n\tif (cin)\n\t{\n\t\tcin->cinematicframe = 0;\n\t\tcin->cinematic_file = file;\n\n\t\tVFS_READ (cin->cinematic_file, &width, 4);\n\t\tVFS_READ (cin->cinematic_file, &height, 4);\n\t\tcin->width = LittleLong(width);\n\t\tcin->height = LittleLong(height);\n\n\t\tVFS_READ (cin->cinematic_file, &cin->s_rate, 4);\n\t\tcin->s_rate = LittleLong(cin->s_rate);\n\t\tVFS_READ (cin->cinematic_file, &cin->s_width, 4);\n\t\tcin->s_width = LittleLong(cin->s_width);\n\t\tVFS_READ (cin->cinematic_file, &cin->s_channels, 4);\n\t\tcin->s_channels = LittleLong(cin->s_channels);\n\n\t\tHuff1TableInit (cin);\n\n\t\tcin->filestart = VFS_TELL(cin->cinematic_file);\n\n\t\tcin->cinematicframe = 0;\n\t\tcin->pic = CIN_ReadNextFrame (cin);\n\t\tcin->cinematictime = 0;\n\t}\n\telse\n\t{\n\t\tCon_Printf(CON_WARNING \"Cinematic %s not found.\\n\", arg);\n\t}\n\treturn cin;\n}\n\n#endif\n"
  },
  {
    "path": "engine/client/clq2_ents.c",
    "content": "#include \"quakedef.h\"\n#include \"particles.h\"\n\n#ifdef Q2CLIENT\n#include \"shader.h\"\n\n//q2pro's framerate scaling runs the entire server at a higher rate (including gamecode).\n//this allows lower latency on player movements without breaking things too much\n//animations are still assumed to run at 10fps, so those need some fixup too\n//events are keyed to not renew twice within the same 10fps window, unless the entity was actually updated.\n\nextern cvar_t r_drawviewmodel;\n\nextern cvar_t cl_nopred;\ntypedef enum\n{\n\tQ2EV_NONE,\n\tQ2EV_ITEM_RESPAWN,\n\tQ2EV_FOOTSTEP,\n\tQ2EV_FALLSHORT,\n\tQ2EV_FALL,\n\tQ2EV_FALLFAR,\n\tQ2EV_PLAYER_TELEPORT,\n\tQ2EV_OTHER_TELEPORT,\n\tQ2EXEV_OTHER_FOOTSTEP,\n\tQ2EXEV_LADDER_STEP,\n} q2entity_event_t;\n\n#define Q2PMF_NO_PREDICTION\t64\t// temporarily disables prediction (used for grappling hook)\n\nfloat LerpAngle (float a2, float a1, float frac)\n{\n\tif (a1 - a2 > 180)\n\t\ta1 -= 360;\n\tif (a1 - a2 < -180)\n\t\ta1 += 360;\n\treturn a2 + frac * (a1 - a2);\n}\n\n// entity_state_t->effects\n// Effects are things handled on the client side (lights, particles, frame animations)\n// that happen constantly on the given entity.\n// An entity that has effects will be sent to the client\n// even if it has a zero index model.\n#define\tQ2EF_ROTATE\t\t\t\t0x00000001\t\t// rotate (bonus items)\n#define\tQ2EF_GIB\t\t\t\t0x00000002\t\t// leave a trail\n#define\tQ2EF_BLASTER\t\t\t0x00000008\t\t// redlight + trail\n#define\tQ2EF_ROCKET\t\t\t\t0x00000010\t\t// redlight + trail\n#define\tQ2EF_GRENADE\t\t\t0x00000020\n#define\tQ2EF_HYPERBLASTER\t\t0x00000040\n#define\tQ2EF_BFG\t\t\t\t0x00000080\n#define Q2EF_COLOR_SHELL\t\t0x00000100\n#define Q2EF_POWERSCREEN\t\t0x00000200\n#define\tQ2EF_ANIM01\t\t\t\t0x00000400\t\t// automatically cycle between frames 0 and 1 at 2 hz\n#define\tQ2EF_ANIM23\t\t\t\t0x00000800\t\t// automatically cycle between frames 2 and 3 at 2 hz\n#define Q2EF_ANIM_ALL\t\t\t0x00001000\t\t// automatically cycle through all frames at 2hz\n#define Q2EF_ANIM_ALLFAST\t\t0x00002000\t\t// automatically cycle through all frames at 10hz\n#define\tQ2EF_FLIES\t\t\t\t0x00004000\n#define\tQ2EF_QUAD\t\t\t\t0x00008000\n#define\tQ2EF_PENT\t\t\t\t0x00010000\n#define\tQ2EF_TELEPORTER\t\t\t0x00020000\t\t// particle fountain\n#define Q2EF_FLAG1\t\t\t\t0x00040000\n#define Q2EF_FLAG2\t\t\t\t0x00080000\n// RAFAEL\n#define Q2EF_IONRIPPER\t\t\t0x00100000\n#define Q2EF_GREENGIB\t\t\t0x00200000\n#define\tQ2EF_BLUEHYPERBLASTER\t0x00400000\n#define Q2EF_SPINNINGLIGHTS\t\t0x00800000\n#define Q2EF_PLASMA\t\t\t\t0x01000000\n#define Q2EF_TRAP\t\t\t\t0x02000000\n\n//ROGUE\n#define Q2EF_TRACKER\t\t\t0x04000000\n#define\tQ2EF_DOUBLE\t\t\t\t0x08000000\n#define\tQ2EF_SPHERETRANS\t\t0x10000000\n#define Q2EF_TAGTRAIL\t\t\t0x20000000\n#define Q2EF_HALF_DAMAGE\t\t0x40000000\n#define Q2EF_TRACKERTRAIL\t\t0x80000000\n//ROGUE\n\n\n\n\n#define Q2MAX_STATS\t32\n\ntypedef struct q2centity_s\n{\n\tentity_state_t\tbaseline;\t\t// delta from this if not from a previous frame\n\tentity_state_t\tcurrent;\n\tentity_state_t\tprev;\t\t\t// will always be valid, but might just be a copy of current\n\n\tint\t\t\tserverframe;\t\t// if not current, this ent isn't in the frame\n\n\ttrailkey_t trailstate;\n\ttrailkey_t emitstate;\n//\tfloat\t\ttrailcount;\t\t\t// for diminishing grenade trails\n\tvec3_t\t\tlerp_origin;\t\t// for trails (variable hz)\n\tint\t\t\toldframe;\n\tfloat\t\toldframetime;\n\n\tfloat\t\tfly_stoptime;\n} q2centity_t;\n\nstatic void Q2S_StartSound(vec3_t origin, int entnum, int entchannel, sfx_t *sfx, float fvol, float attenuation, float delay)\n{\n\tS_StartSound(entnum, entchannel, sfx, origin, NULL, fvol, attenuation, -delay, 0, 0);\n}\nsfx_t *S_PrecacheSexedSound(int entnum, const char *soundname)\n{\n\tif (soundname[0] == '*')\n\t{\t//a 'sexed' sound\n\t\tif (entnum > 0 && entnum <= MAX_CLIENTS)\n\t\t{\n\t\t\tchar *model = InfoBuf_ValueForKey(&cl.players[entnum-1].userinfo, \"skin\");\n\t\t\tchar *skin;\n\t\t\tskin = strchr(model, '/');\n\t\t\tif (skin)\n\t\t\t\t*skin = '\\0';\n\t\t\tif (*model)\n\t\t\t{\n\t\t\t\tsfx_t *sfx = S_PrecacheSound(va(\"players/%s/%s\", model, soundname+1));\n\t\t\t\tif (sfx && sfx->loadstate != SLS_FAILED)\t//warning: the sound might still be loading (and later fail).\n\t\t\t\t\treturn sfx;\n\t\t\t}\n\t\t}\n\t\treturn S_PrecacheSound(va(\"players/male/%s\", soundname+1));\n\t}\n\treturn S_PrecacheSound(soundname);\n}\n\n\nvoid CLQ2_EntityEvent(entity_state_t *es)\n{\n\tswitch (es->u.q2.event)\n\t{\n\tcase Q2EV_NONE:\n\tcase Q2EV_OTHER_TELEPORT:\t//inihibits interpolation. not an event in itself.\n\t\tbreak;\n\tcase Q2EV_ITEM_RESPAWN:\n\t\tpe->RunParticleEffectState(es->origin, NULL, 1, pt_q2[Q2PT_RESPAWN], NULL);\n\t\tbreak;\n\tcase Q2EV_PLAYER_TELEPORT:\n\t\tpe->RunParticleEffectState(es->origin, NULL, 1, pt_q2[Q2PT_PLAYER_TELEPORT], NULL);\n\t\tbreak;\n\tcase Q2EV_FOOTSTEP:\n\t\tpe->RunParticleEffectState(es->origin, NULL, 1, pt_q2[Q2PT_FOOTSTEP], NULL);\n\t\tbreak;\n\tcase Q2EV_FALLSHORT:\n\t\tQ2S_StartSound (NULL, es->number, CHAN_AUTO, S_PrecacheSound (\"player/land1.wav\"), 1, ATTN_NORM, 0);\n\t\tbreak;\n\tcase Q2EV_FALL:\n\t\tQ2S_StartSound (NULL, es->number, CHAN_AUTO, S_PrecacheSexedSound (es->number, \"*fall2.wav\"), 1, ATTN_NORM, 0);\n\t\tbreak;\n\tcase Q2EV_FALLFAR:\n\t\tQ2S_StartSound (NULL, es->number, CHAN_AUTO, S_PrecacheSexedSound (es->number, \"*fall1.wav\"), 1, ATTN_NORM, 0);\n\t\tbreak;\n\tcase Q2EXEV_OTHER_FOOTSTEP:\n\t\tpe->RunParticleEffectState(es->origin, NULL, 1, pt_q2[Q2PT_OTHER_FOOTSTEP], NULL);\n\t\tbreak;\n\tcase Q2EXEV_LADDER_STEP:\n\t\tpe->RunParticleEffectState(es->origin, NULL, 1, pt_q2[Q2PT_LADDER_STEP], NULL);\n\t\tbreak;\n\tdefault:\n\t\tCon_Printf(\"event %u not supported\\n\", es->u.q2.event);\n\t\tbreak;\n\t}\n};\nvoid CLQ2_TeleporterParticles(entity_state_t *es){};\n\n/*these are emissive effects (ie: emitted each frame), but they're also mutually exclusive, so sharing emitstate is fine*/\nvoid CLQ2_Tracker_Shell(q2centity_t *ent, vec3_t org)\n{\n\tP_EmitEffect (org, NULL, 0, pt_q2[Q2PT_TRACKERSHELL], &(ent->emitstate));\n};\nvoid CLQ2_BfgParticles(q2centity_t *ent, vec3_t org)\n{\n\tP_EmitEffect (org, NULL, 0, pt_q2[Q2PT_BFGPARTICLES], &(ent->emitstate));\n};\nvoid CLQ2_FlyEffect(q2centity_t *ent, vec3_t org)\n{\n\tfloat starttime, n;\n\tfloat cltime = cl.time;\n\tint count;\n\tif (cl.paused)\n\t\treturn;\n\n\t//q2 ramps up to 162 within the first 20 secs, sits at 162 for the next 20, then ramps back down for the 20 after that.\n\t//I have no idea how pvs issues are handled.\n\n\tif (ent->fly_stoptime < cltime)\n\t{\n\t\tstarttime = cltime;\n\t\tent->fly_stoptime = cltime + 60;\n\t}\n\telse\n\t{\n\t\tstarttime = ent->fly_stoptime - 60;\n\t}\n\n\tn = cltime - starttime;\n\tif (n < 20)\n\t\tcount = n * 162 / 20.0;\n\telse\n\t{\n\t\tn = ent->fly_stoptime - cltime;\n\t\tif (n < 20)\n\t\t\tcount = n * 162 / 20.0;\n\t\telse\n\t\t\tcount = 162;\n\t}\n\tif (count < 0)\n\t\treturn;\n\n\t//these are assumed to be spawned anew \n\tpe->RunParticleEffectState(org, NULL, count, pt_q2[Q2PT_FLIES], &(ent->emitstate));\n};\n\n\n#define MAX_Q2EDICTS 1024\n#define\tMAX_PARSE_ENTITIES\t1024\n\n\nstatic q2centity_t cl_entities[MAX_Q2EDICTS];\nentity_state_t\tclq2_parse_entities[MAX_PARSE_ENTITIES];\n\nvoid CL_SmokeAndFlash(vec3_t origin);\n\nvoid CL_GetNumberedEntityInfo (int num, float *org, float *ang)\n{\n\tq2centity_t\t*ent;\n\n\tif (num < 0 || num >= MAX_Q2EDICTS)\n\t\tHost_EndGame (\"CL_GetNumberedEntityInfo: bad ent\");\n\tent = &cl_entities[num];\n\n\tif (org)\n\t\tVectorCopy (ent->current.origin, org);\n\tif (ang)\n\t\tVectorCopy (ent->current.angles, ang);\n\n\n\t// FIXME: bmodel issues...\n}\n\n#ifdef Q2SERVER\nvoid CLQ2_WriteDemoBaselines(sizebuf_t *buf)\n{\n\tint i;\n\tq2entity_state_t\tnullstate = {0};\n\tfor (i = 0; i < MAX_Q2EDICTS; i++)\n\t{\n\t\tq2entity_state_t\tes;\n\t\tentity_state_t\t\t*base = &cl_entities[i].baseline;\n\t\tif (!base->modelindex)\n\t\t\tcontinue;\n\n\t\t//I brought these copies on myself...\n\t\tes.number = i;\n\t\tVectorCopy(base->origin, es.origin);\n\t\tVectorCopy(base->angles, es.angles);\n\t\tVectorCopy(base->u.q2.old_origin, es.old_origin);\n\t\tes.modelindex = base->modelindex;\n\t\tes.modelindex2 = base->modelindex2;\n\t\tes.modelindex3 = base->u.q2.modelindex3;\n\t\tes.modelindex4 = base->u.q2.modelindex4;\n\t\tes.frame = base->frame;\n\t\tes.skinnum = base->skinnum;\n\t\tes.effects = base->effects;\n\t\tes.renderfx = base->u.q2.renderfx;\n\t\tes.solid = base->solidsize;\n\t\tes.sound = base->u.q2.sound;\n\t\tes.event = base->u.q2.event;\n\n\t\tif (buf->cursize > buf->maxsize/2)\n\t\t{\n\t\t\tCL_WriteRecordQ2DemoMessage (buf);\n\t\t\tSZ_Clear (buf);\n\t\t}\n\n\t\tMSG_WriteByte (buf, svcq2_spawnbaseline);\n\t\tMSGQ2_WriteDeltaEntity(&nullstate, &es, buf, true, true, cls.protocol_q2==PROTOCOL_VERSION_Q2EX);\n\t}\n}\n#endif\n\nvoid CLQ2_ClearState(void)\n{\n\tint i;\n\tmemset(cl_entities, 0, sizeof(cl_entities));\n\tfor (i = 0; i < MAX_Q2EDICTS; i++)\n\t\tcl_entities[i].baseline = nullentitystate;\n}\n\n#include \"q2m_flash.c\"\nvoid CLQ2_RunMuzzleFlash2 (int ent, int flash_number)\n{\n\tvec3_t\t\torigin;\n\tdlight_t\t*dl;\n\tvec3_t\t\tforward, right, up;\n\tchar\t\tsoundname[64];\n\tint ef;\n\n\tif (flash_number < 0 || flash_number >= sizeof(monster_flash_offset)/sizeof(monster_flash_offset[0]))\n\t\treturn;\n\n\t// locate the origin\n\tAngleVectors (cl_entities[ent].current.angles, forward, right, up);\n\torigin[0] = cl_entities[ent].current.origin[0] + forward[0] * monster_flash_offset[flash_number].offset[0] + right[0] * monster_flash_offset[flash_number].offset[1];\n\torigin[1] = cl_entities[ent].current.origin[1] + forward[1] * monster_flash_offset[flash_number].offset[0] + right[1] * monster_flash_offset[flash_number].offset[1];\n\torigin[2] = cl_entities[ent].current.origin[2] + forward[2] * monster_flash_offset[flash_number].offset[0] + right[2] * monster_flash_offset[flash_number].offset[1] + monster_flash_offset[flash_number].offset[2];\n\n\tef = P_FindParticleType(monster_flash_offset[flash_number].name);\n\tif (ef != P_INVALID)\n\t{\n\t\tP_RunParticleEffectType(origin, NULL, 1, ef);\n\t\treturn;\n\t}\n\n\t//the rest of the function is legacy code.\n\n\tdl = CL_AllocDlight (ent);\n\tVectorCopy (origin,  dl->origin);\n\tdl->radius = 200 + (rand()&31);\n//\tdl->minlight = 32;\n\tdl->die = cl.time + 0.1;\n\n\tswitch (flash_number)\n\t{\n\tcase Q2MZ2_INFANTRY_MACHINEGUN_1:\n\tcase Q2MZ2_INFANTRY_MACHINEGUN_2:\n\tcase Q2MZ2_INFANTRY_MACHINEGUN_3:\n\tcase Q2MZ2_INFANTRY_MACHINEGUN_4:\n\tcase Q2MZ2_INFANTRY_MACHINEGUN_5:\n\tcase Q2MZ2_INFANTRY_MACHINEGUN_6:\n\tcase Q2MZ2_INFANTRY_MACHINEGUN_7:\n\tcase Q2MZ2_INFANTRY_MACHINEGUN_8:\n\tcase Q2MZ2_INFANTRY_MACHINEGUN_9:\n\tcase Q2MZ2_INFANTRY_MACHINEGUN_10:\n\tcase Q2MZ2_INFANTRY_MACHINEGUN_11:\n\tcase Q2MZ2_INFANTRY_MACHINEGUN_12:\n\tcase Q2MZ2_INFANTRY_MACHINEGUN_13:\n\t\tdl->color[0] = 1;dl->color[1] = 1;dl->color[2] = 0;\n\t\tP_RunParticleEffect (origin, vec3_origin, 0, 40);\n\t\tCL_SmokeAndFlash(origin);\n\t\tQ2S_StartSound (NULL, ent, CHAN_WEAPON, S_PrecacheSound(\"infantry/infatck1.wav\"), 1, ATTN_NORM, 0);\n\t\tbreak;\n\n\tcase Q2MZ2_SOLDIER_MACHINEGUN_1:\n\tcase Q2MZ2_SOLDIER_MACHINEGUN_2:\n\tcase Q2MZ2_SOLDIER_MACHINEGUN_3:\n\tcase Q2MZ2_SOLDIER_MACHINEGUN_4:\n\tcase Q2MZ2_SOLDIER_MACHINEGUN_5:\n\tcase Q2MZ2_SOLDIER_MACHINEGUN_6:\n\tcase Q2MZ2_SOLDIER_MACHINEGUN_7:\n\tcase Q2MZ2_SOLDIER_MACHINEGUN_8:\n\t\tdl->color[0] = 1;dl->color[1] = 1;dl->color[2] = 0;\n\t\tP_RunParticleEffect (origin, vec3_origin, 0, 40);\n\t\tCL_SmokeAndFlash(origin);\n\t\tQ2S_StartSound (NULL, ent, CHAN_WEAPON, S_PrecacheSound(\"soldier/solatck3.wav\"), 1, ATTN_NORM, 0);\n\t\tbreak;\n\n\tcase Q2MZ2_GUNNER_MACHINEGUN_1:\n\tcase Q2MZ2_GUNNER_MACHINEGUN_2:\n\tcase Q2MZ2_GUNNER_MACHINEGUN_3:\n\tcase Q2MZ2_GUNNER_MACHINEGUN_4:\n\tcase Q2MZ2_GUNNER_MACHINEGUN_5:\n\tcase Q2MZ2_GUNNER_MACHINEGUN_6:\n\tcase Q2MZ2_GUNNER_MACHINEGUN_7:\n\tcase Q2MZ2_GUNNER_MACHINEGUN_8:\n\t\tdl->color[0] = 1;dl->color[1] = 1;dl->color[2] = 0;\n\t\tP_RunParticleEffect (origin, vec3_origin, 0, 40);\n\t\tCL_SmokeAndFlash(origin);\n\t\tQ2S_StartSound (NULL, ent, CHAN_WEAPON, S_PrecacheSound(\"gunner/gunatck2.wav\"), 1, ATTN_NORM, 0);\n\t\tbreak;\n\n\tcase Q2MZ2_ACTOR_MACHINEGUN_1:\n\tcase Q2MZ2_SUPERTANK_MACHINEGUN_1:\n\tcase Q2MZ2_SUPERTANK_MACHINEGUN_2:\n\tcase Q2MZ2_SUPERTANK_MACHINEGUN_3:\n\tcase Q2MZ2_SUPERTANK_MACHINEGUN_4:\n\tcase Q2MZ2_SUPERTANK_MACHINEGUN_5:\n\tcase Q2MZ2_SUPERTANK_MACHINEGUN_6:\n\tcase Q2MZ2_TURRET_MACHINEGUN:\t\t\t// PGM\n\t\tdl->color[0] = 1;dl->color[1] = 1;dl->color[2] = 0;\n\n\t\tP_RunParticleEffect (origin, vec3_origin, 0, 40);\n\t\tCL_SmokeAndFlash(origin);\n\t\tQ2S_StartSound (NULL, ent, CHAN_WEAPON, S_PrecacheSound(\"infantry/infatck1.wav\"), 1, ATTN_NORM, 0);\n\t\tbreak;\n\n\tcase Q2MZ2_BOSS2_MACHINEGUN_L1:\n\tcase Q2MZ2_BOSS2_MACHINEGUN_L2:\n\tcase Q2MZ2_BOSS2_MACHINEGUN_L3:\n\tcase Q2MZ2_BOSS2_MACHINEGUN_L4:\n\tcase Q2MZ2_BOSS2_MACHINEGUN_L5:\n\tcase Q2MZ2_CARRIER_MACHINEGUN_L1:\t\t// PMM\n\tcase Q2MZ2_CARRIER_MACHINEGUN_L2:\t\t// PMM\n\t\tdl->color[0] = 1;dl->color[1] = 1;dl->color[2] = 0;\n\n\t\tP_RunParticleEffect (origin, vec3_origin, 0, 40);\n\t\tCL_SmokeAndFlash(origin);\n\t\tQ2S_StartSound (NULL, ent, CHAN_WEAPON, S_PrecacheSound(\"infantry/infatck1.wav\"), 1, ATTN_NONE, 0);\n\t\tbreak;\n\n\tcase Q2MZ2_SOLDIER_BLASTER_1:\n\tcase Q2MZ2_SOLDIER_BLASTER_2:\n\tcase Q2MZ2_SOLDIER_BLASTER_3:\n\tcase Q2MZ2_SOLDIER_BLASTER_4:\n\tcase Q2MZ2_SOLDIER_BLASTER_5:\n\tcase Q2MZ2_SOLDIER_BLASTER_6:\n\tcase Q2MZ2_SOLDIER_BLASTER_7:\n\tcase Q2MZ2_SOLDIER_BLASTER_8:\n\tcase Q2MZ2_TURRET_BLASTER:\t\t\t// PGM\n\t\tdl->color[0] = 1;dl->color[1] = 1;dl->color[2] = 0;\n\t\tQ2S_StartSound (NULL, ent, CHAN_WEAPON, S_PrecacheSound(\"soldier/solatck2.wav\"), 1, ATTN_NORM, 0);\n\t\tbreak;\n\n\tcase Q2MZ2_FLYER_BLASTER_1:\n\tcase Q2MZ2_FLYER_BLASTER_2:\n\t\tdl->color[0] = 1;dl->color[1] = 1;dl->color[2] = 0;\n\t\tQ2S_StartSound (NULL, ent, CHAN_WEAPON, S_PrecacheSound(\"flyer/flyatck3.wav\"), 1, ATTN_NORM, 0);\n\t\tbreak;\n\n\tcase Q2MZ2_MEDIC_BLASTER_1:\n\t\tdl->color[0] = 1;dl->color[1] = 1;dl->color[2] = 0;\n\t\tQ2S_StartSound (NULL, ent, CHAN_WEAPON, S_PrecacheSound(\"medic/medatck1.wav\"), 1, ATTN_NORM, 0);\n\t\tbreak;\n\n\tcase Q2MZ2_HOVER_BLASTER_1:\n\t\tdl->color[0] = 1;dl->color[1] = 1;dl->color[2] = 0;\n\t\tQ2S_StartSound (NULL, ent, CHAN_WEAPON, S_PrecacheSound(\"hover/hovatck1.wav\"), 1, ATTN_NORM, 0);\n\t\tbreak;\n\n\tcase Q2MZ2_FLOAT_BLASTER_1:\n\t\tdl->color[0] = 1;dl->color[1] = 1;dl->color[2] = 0;\n\t\tQ2S_StartSound (NULL, ent, CHAN_WEAPON, S_PrecacheSound(\"floater/fltatck1.wav\"), 1, ATTN_NORM, 0);\n\t\tbreak;\n\n\tcase Q2MZ2_SOLDIER_SHOTGUN_1:\n\tcase Q2MZ2_SOLDIER_SHOTGUN_2:\n\tcase Q2MZ2_SOLDIER_SHOTGUN_3:\n\tcase Q2MZ2_SOLDIER_SHOTGUN_4:\n\tcase Q2MZ2_SOLDIER_SHOTGUN_5:\n\tcase Q2MZ2_SOLDIER_SHOTGUN_6:\n\tcase Q2MZ2_SOLDIER_SHOTGUN_7:\n\tcase Q2MZ2_SOLDIER_SHOTGUN_8:\n\t\tdl->color[0] = 1;dl->color[1] = 1;dl->color[2] = 0;\n\t\tCL_SmokeAndFlash(origin);\n\t\tQ2S_StartSound (NULL, ent, CHAN_WEAPON, S_PrecacheSound(\"soldier/solatck1.wav\"), 1, ATTN_NORM, 0);\n\t\tbreak;\n\n\tcase Q2MZ2_TANK_BLASTER_1:\n\tcase Q2MZ2_TANK_BLASTER_2:\n\tcase Q2MZ2_TANK_BLASTER_3:\n\t\tdl->color[0] = 1;dl->color[1] = 1;dl->color[2] = 0;\n\t\tQ2S_StartSound (NULL, ent, CHAN_WEAPON, S_PrecacheSound(\"tank/tnkatck3.wav\"), 1, ATTN_NORM, 0);\n\t\tbreak;\n\n\tcase Q2MZ2_TANK_MACHINEGUN_1:\n\tcase Q2MZ2_TANK_MACHINEGUN_2:\n\tcase Q2MZ2_TANK_MACHINEGUN_3:\n\tcase Q2MZ2_TANK_MACHINEGUN_4:\n\tcase Q2MZ2_TANK_MACHINEGUN_5:\n\tcase Q2MZ2_TANK_MACHINEGUN_6:\n\tcase Q2MZ2_TANK_MACHINEGUN_7:\n\tcase Q2MZ2_TANK_MACHINEGUN_8:\n\tcase Q2MZ2_TANK_MACHINEGUN_9:\n\tcase Q2MZ2_TANK_MACHINEGUN_10:\n\tcase Q2MZ2_TANK_MACHINEGUN_11:\n\tcase Q2MZ2_TANK_MACHINEGUN_12:\n\tcase Q2MZ2_TANK_MACHINEGUN_13:\n\tcase Q2MZ2_TANK_MACHINEGUN_14:\n\tcase Q2MZ2_TANK_MACHINEGUN_15:\n\tcase Q2MZ2_TANK_MACHINEGUN_16:\n\tcase Q2MZ2_TANK_MACHINEGUN_17:\n\tcase Q2MZ2_TANK_MACHINEGUN_18:\n\tcase Q2MZ2_TANK_MACHINEGUN_19:\n\t\tdl->color[0] = 1;dl->color[1] = 1;dl->color[2] = 0;\n\t\tP_RunParticleEffect (origin, vec3_origin, 0, 40);\n\t\tCL_SmokeAndFlash(origin);\n\t\tsnprintf(soundname, sizeof(soundname), \"tank/tnkatk2%c.wav\", 'a' + rand() % 5);\n\t\tQ2S_StartSound (NULL, ent, CHAN_WEAPON, S_PrecacheSound(soundname), 1, ATTN_NORM, 0);\n\t\tbreak;\n\n\tcase Q2MZ2_CHICK_ROCKET_1:\n\tcase Q2MZ2_TURRET_ROCKET:\t\t\t// PGM\n\t\tdl->color[0] = 1;dl->color[1] = 0.5;dl->color[2] = 0.2;\n\t\tQ2S_StartSound (NULL, ent, CHAN_WEAPON, S_PrecacheSound(\"chick/chkatck2.wav\"), 1, ATTN_NORM, 0);\n\t\tbreak;\n\n\tcase Q2MZ2_TANK_ROCKET_1:\n\tcase Q2MZ2_TANK_ROCKET_2:\n\tcase Q2MZ2_TANK_ROCKET_3:\n\t\tdl->color[0] = 1;dl->color[1] = 0.5;dl->color[2] = 0.2;\n\t\tQ2S_StartSound (NULL, ent, CHAN_WEAPON, S_PrecacheSound(\"tank/tnkatck1.wav\"), 1, ATTN_NORM, 0);\n\t\tbreak;\n\n\tcase Q2MZ2_SUPERTANK_ROCKET_1:\n\tcase Q2MZ2_SUPERTANK_ROCKET_2:\n\tcase Q2MZ2_SUPERTANK_ROCKET_3:\n\tcase Q2MZ2_BOSS2_ROCKET_1:\n\tcase Q2MZ2_BOSS2_ROCKET_2:\n\tcase Q2MZ2_BOSS2_ROCKET_3:\n\tcase Q2MZ2_BOSS2_ROCKET_4:\n\tcase Q2MZ2_CARRIER_ROCKET_1:\n//\tcase Q2MZ2_CARRIER_ROCKET_2:\n//\tcase Q2MZ2_CARRIER_ROCKET_3:\n//\tcase Q2MZ2_CARRIER_ROCKET_4:\n\t\tdl->color[0] = 1;dl->color[1] = 0.5;dl->color[2] = 0.2;\n\t\tQ2S_StartSound (NULL, ent, CHAN_WEAPON, S_PrecacheSound(\"tank/rocket.wav\"), 1, ATTN_NORM, 0);\n\t\tbreak;\n\n\tcase Q2MZ2_GUNNER_GRENADE_1:\n\tcase Q2MZ2_GUNNER_GRENADE_2:\n\tcase Q2MZ2_GUNNER_GRENADE_3:\n\tcase Q2MZ2_GUNNER_GRENADE_4:\n\t\tdl->color[0] = 1;dl->color[1] = 0.5;dl->color[2] = 0;\n\t\tQ2S_StartSound (NULL, ent, CHAN_WEAPON, S_PrecacheSound(\"gunner/gunatck3.wav\"), 1, ATTN_NORM, 0);\n\t\tbreak;\n\n\tcase Q2MZ2_GLADIATOR_RAILGUN_1:\n\t// PMM\n\tcase Q2MZ2_CARRIER_RAILGUN:\n\tcase Q2MZ2_WIDOW_RAIL:\n\t// pmm\n\t\tdl->color[0] = 0.5;dl->color[1] = 0.5;dl->color[2] = 1.0;\n\t\tbreak;\n\n// --- Xian's shit starts ---\n\tcase Q2MZ2_MAKRON_BFG:\n\t\tdl->color[0] = 0.5;dl->color[1] = 1 ;dl->color[2] = 0.5;\n\t\t//Q2S_StartSound (NULL, ent, CHAN_WEAPON, S_RegisterSound(\"makron/bfg_fire.wav\"), 1, ATTN_NORM, 0);\n\t\tbreak;\n\n\tcase Q2MZ2_MAKRON_BLASTER_1:\n\tcase Q2MZ2_MAKRON_BLASTER_2:\n\tcase Q2MZ2_MAKRON_BLASTER_3:\n\tcase Q2MZ2_MAKRON_BLASTER_4:\n\tcase Q2MZ2_MAKRON_BLASTER_5:\n\tcase Q2MZ2_MAKRON_BLASTER_6:\n\tcase Q2MZ2_MAKRON_BLASTER_7:\n\tcase Q2MZ2_MAKRON_BLASTER_8:\n\tcase Q2MZ2_MAKRON_BLASTER_9:\n\tcase Q2MZ2_MAKRON_BLASTER_10:\n\tcase Q2MZ2_MAKRON_BLASTER_11:\n\tcase Q2MZ2_MAKRON_BLASTER_12:\n\tcase Q2MZ2_MAKRON_BLASTER_13:\n\tcase Q2MZ2_MAKRON_BLASTER_14:\n\tcase Q2MZ2_MAKRON_BLASTER_15:\n\tcase Q2MZ2_MAKRON_BLASTER_16:\n\tcase Q2MZ2_MAKRON_BLASTER_17:\n\t\tdl->color[0] = 1;dl->color[1] = 1;dl->color[2] = 0;\n\t\tQ2S_StartSound (NULL, ent, CHAN_WEAPON, S_PrecacheSound(\"makron/blaster.wav\"), 1, ATTN_NORM, 0);\n\t\tbreak;\n\t\n\tcase Q2MZ2_JORG_MACHINEGUN_L1:\n\tcase Q2MZ2_JORG_MACHINEGUN_L2:\n\tcase Q2MZ2_JORG_MACHINEGUN_L3:\n\tcase Q2MZ2_JORG_MACHINEGUN_L4:\n\tcase Q2MZ2_JORG_MACHINEGUN_L5:\n\tcase Q2MZ2_JORG_MACHINEGUN_L6:\n\t\tdl->color[0] = 1;dl->color[1] = 1;dl->color[2] = 0;\n\t\tP_RunParticleEffect (origin, vec3_origin, 0, 40);\n\t\tCL_SmokeAndFlash(origin);\n\t\tQ2S_StartSound (NULL, ent, CHAN_WEAPON, S_PrecacheSound(\"boss3/xfire.wav\"), 1, ATTN_NORM, 0);\n\t\tbreak;\n\n\tcase Q2MZ2_JORG_MACHINEGUN_R1:\n\tcase Q2MZ2_JORG_MACHINEGUN_R2:\n\tcase Q2MZ2_JORG_MACHINEGUN_R3:\n\tcase Q2MZ2_JORG_MACHINEGUN_R4:\n\tcase Q2MZ2_JORG_MACHINEGUN_R5:\n\tcase Q2MZ2_JORG_MACHINEGUN_R6:\n\t\tdl->color[0] = 1;dl->color[1] = 1;dl->color[2] = 0;\n\t\tP_RunParticleEffect (origin, vec3_origin, 0, 40);\n\t\tCL_SmokeAndFlash(origin);\n\t\tbreak;\n\n\tcase Q2MZ2_JORG_BFG_1:\n\t\tdl->color[0] = 0.5;dl->color[1] = 1 ;dl->color[2] = 0.5;\n\t\tbreak;\n\n\tcase Q2MZ2_BOSS2_MACHINEGUN_R1:\n\tcase Q2MZ2_BOSS2_MACHINEGUN_R2:\n\tcase Q2MZ2_BOSS2_MACHINEGUN_R3:\n\tcase Q2MZ2_BOSS2_MACHINEGUN_R4:\n\tcase Q2MZ2_BOSS2_MACHINEGUN_R5:\n\tcase Q2MZ2_CARRIER_MACHINEGUN_R1:\t\t\t// PMM\n\tcase Q2MZ2_CARRIER_MACHINEGUN_R2:\t\t\t// PMM\n\n\t\tdl->color[0] = 1;dl->color[1] = 1;dl->color[2] = 0;\n\n\t\tP_RunParticleEffect (origin, vec3_origin, 0, 40);\n\t\tCL_SmokeAndFlash(origin);\n\t\tbreak;\n\n// ======\n// ROGUE\n\tcase Q2MZ2_STALKER_BLASTER:\n\tcase Q2MZ2_DAEDALUS_BLASTER:\n\tcase Q2MZ2_MEDIC_BLASTER_2:\n\tcase Q2MZ2_WIDOW_BLASTER:\n\tcase Q2MZ2_WIDOW_BLASTER_SWEEP1:\n\tcase Q2MZ2_WIDOW_BLASTER_SWEEP2:\n\tcase Q2MZ2_WIDOW_BLASTER_SWEEP3:\n\tcase Q2MZ2_WIDOW_BLASTER_SWEEP4:\n\tcase Q2MZ2_WIDOW_BLASTER_SWEEP5:\n\tcase Q2MZ2_WIDOW_BLASTER_SWEEP6:\n\tcase Q2MZ2_WIDOW_BLASTER_SWEEP7:\n\tcase Q2MZ2_WIDOW_BLASTER_SWEEP8:\n\tcase Q2MZ2_WIDOW_BLASTER_SWEEP9:\n\tcase Q2MZ2_WIDOW_BLASTER_100:\n\tcase Q2MZ2_WIDOW_BLASTER_90:\n\tcase Q2MZ2_WIDOW_BLASTER_80:\n\tcase Q2MZ2_WIDOW_BLASTER_70:\n\tcase Q2MZ2_WIDOW_BLASTER_60:\n\tcase Q2MZ2_WIDOW_BLASTER_50:\n\tcase Q2MZ2_WIDOW_BLASTER_40:\n\tcase Q2MZ2_WIDOW_BLASTER_30:\n\tcase Q2MZ2_WIDOW_BLASTER_20:\n\tcase Q2MZ2_WIDOW_BLASTER_10:\n\tcase Q2MZ2_WIDOW_BLASTER_0:\n\tcase Q2MZ2_WIDOW_BLASTER_10L:\n\tcase Q2MZ2_WIDOW_BLASTER_20L:\n\tcase Q2MZ2_WIDOW_BLASTER_30L:\n\tcase Q2MZ2_WIDOW_BLASTER_40L:\n\tcase Q2MZ2_WIDOW_BLASTER_50L:\n\tcase Q2MZ2_WIDOW_BLASTER_60L:\n\tcase Q2MZ2_WIDOW_BLASTER_70L:\n\tcase Q2MZ2_WIDOW_RUN_1:\n\tcase Q2MZ2_WIDOW_RUN_2:\n\tcase Q2MZ2_WIDOW_RUN_3:\n\tcase Q2MZ2_WIDOW_RUN_4:\n\tcase Q2MZ2_WIDOW_RUN_5:\n\tcase Q2MZ2_WIDOW_RUN_6:\n\tcase Q2MZ2_WIDOW_RUN_7:\n\tcase Q2MZ2_WIDOW_RUN_8:\n\t\tdl->color[0] = 0;dl->color[1] = 1;dl->color[2] = 0;\n\t\tQ2S_StartSound (NULL, ent, CHAN_WEAPON, S_PrecacheSound(\"tank/tnkatck3.wav\"), 1, ATTN_NORM, 0);\n\t\tbreak;\n\n\tcase Q2MZ2_WIDOW_DISRUPTOR:\n\t\tdl->color[0] = -1;dl->color[1] = -1;dl->color[2] = -1;\n\t\tQ2S_StartSound (NULL, ent, CHAN_WEAPON, S_PrecacheSound(\"weapons/disint2.wav\"), 1, ATTN_NORM, 0);\n\t\tbreak;\n\n\tcase Q2MZ2_WIDOW_PLASMABEAM:\n\tcase Q2MZ2_WIDOW2_BEAMER_1:\n\tcase Q2MZ2_WIDOW2_BEAMER_2:\n\tcase Q2MZ2_WIDOW2_BEAMER_3:\n\tcase Q2MZ2_WIDOW2_BEAMER_4:\n\tcase Q2MZ2_WIDOW2_BEAMER_5:\n\tcase Q2MZ2_WIDOW2_BEAM_SWEEP_1:\n\tcase Q2MZ2_WIDOW2_BEAM_SWEEP_2:\n\tcase Q2MZ2_WIDOW2_BEAM_SWEEP_3:\n\tcase Q2MZ2_WIDOW2_BEAM_SWEEP_4:\n\tcase Q2MZ2_WIDOW2_BEAM_SWEEP_5:\n\tcase Q2MZ2_WIDOW2_BEAM_SWEEP_6:\n\tcase Q2MZ2_WIDOW2_BEAM_SWEEP_7:\n\tcase Q2MZ2_WIDOW2_BEAM_SWEEP_8:\n\tcase Q2MZ2_WIDOW2_BEAM_SWEEP_9:\n\tcase Q2MZ2_WIDOW2_BEAM_SWEEP_10:\n\tcase Q2MZ2_WIDOW2_BEAM_SWEEP_11:\n\t\tdl->radius = 300 + (rand()&100);\n\t\tdl->color[0] = 1;dl->color[1] = 1;dl->color[2] = 0;\n\t\tdl->die = cl.time + 200;\n\t\tbreak;\n// ROGUE\n// ======\n\n// --- Xian's shit ends ---\n\n  //hmm... he must take AGES on the loo.... :p\n\n\tdefault:\n\t\tCon_Printf(CON_WARNING\"CLQ2_RunMuzzleFlash2: %i not implemented\\n\", flash_number);\n\t\tbreak;\n\t}\n}\n\nvoid CLQ2_ParseMuzzleFlash (void)\n{\n\tvec3_t\t\tfv, rv, dummy;\n\tdlight_t\t*dl;\n\tint\t\t\ti, weapon;\n\tvec3_t\t\torg, ang;\n\tint\t\t\tsilenced;\n\tfloat\t\tvolume;\n\tchar\t\tsoundname[64];\n\n\ti = (unsigned short)(short)MSG_ReadShort ();\n\tif (i < 1 || i >= Q2MAX_EDICTS)\n\t\tHost_Error (\"CL_ParseMuzzleFlash: bad entity\");\n\n\tweapon = MSG_ReadByte ();\n\tsilenced = weapon & Q2MZ_SILENCED;\n\tweapon &= ~Q2MZ_SILENCED;\n\n\tCL_GetNumberedEntityInfo(i, org, ang);\n\n\tdl = CL_AllocDlight (i);\n\tVectorCopy (org,  dl->origin);\n\tAngleVectors (ang, fv, rv, dummy);\n\tVectorMA (dl->origin, 18, fv, dl->origin);\n\tVectorMA (dl->origin, 16, rv, dl->origin);\n\tif (silenced)\n\t\tdl->radius = 100 + (rand()&31);\n\telse\n\t\tdl->radius = 200 + (rand()&31);\n\tdl->minlight = 32;\n\tdl->die = cl.time+0.05; //+ 0.1;\n\tdl->decay = 1;\n\n\tdl->channelfade[0] = 2;\n\tdl->channelfade[1] = 2;\n\tdl->channelfade[2] = 2;\n\n\tif (silenced)\n\t\tvolume = 0.2;\n\telse\n\t\tvolume = 1;\n\n\n\tswitch (weapon)\n\t{\n\tcase Q2MZ_BLASTER:\n\t\tdl->color[0] = 1;dl->color[1] = 1;dl->color[2] = 0;\n\t\tQ2S_StartSound (NULL, i, CHAN_WEAPON, S_PrecacheSound(\"weapons/blastf1a.wav\"), volume, ATTN_NORM, 0);\n\t\tbreak;\n\tcase Q2MZ_BLUEHYPERBLASTER:\n\t\tdl->color[0] = 0;dl->color[1] = 0;dl->color[2] = 1;\n\t\tQ2S_StartSound (NULL, i, CHAN_WEAPON, S_PrecacheSound(\"weapons/hyprbf1a.wav\"), volume, ATTN_NORM, 0);\n\t\tbreak;\n\tcase Q2MZ_HYPERBLASTER:\n\t\tdl->color[0] = 1;dl->color[1] = 1;dl->color[2] = 0;\n\t\tQ2S_StartSound (NULL, i, CHAN_WEAPON, S_PrecacheSound(\"weapons/hyprbf1a.wav\"), volume, ATTN_NORM, 0);\n\t\tbreak;\n\tcase Q2MZ_MACHINEGUN:\n\t\tdl->color[0] = 1;dl->color[1] = 1;dl->color[2] = 0;\n\t\tQ_snprintfz(soundname, sizeof(soundname), \"weapons/machgf%ib.wav\", (rand() % 5) + 1);\n\t\tQ2S_StartSound (NULL, i, CHAN_WEAPON, S_PrecacheSound(soundname), volume, ATTN_NORM, 0);\n\t\tbreak;\n\n\tcase Q2MZ_SHOTGUN:\n\t\tdl->color[0] = 1;dl->color[1] = 1;dl->color[2] = 0;\n\t\tQ2S_StartSound (NULL, i, CHAN_WEAPON, S_PrecacheSound(\"weapons/shotgf1b.wav\"), volume, ATTN_NORM, 0);\n\t\tQ2S_StartSound (NULL, i, CHAN_AUTO,   S_PrecacheSound(\"weapons/shotgr1b.wav\"), volume, ATTN_NORM, 0.1);\n\t\tbreak;\n\tcase Q2MZ_SSHOTGUN:\n\t\tdl->color[0] = 1;dl->color[1] = 1;dl->color[2] = 0;\n\t\tQ2S_StartSound (NULL, i, CHAN_WEAPON, S_PrecacheSound(\"weapons/sshotf1b.wav\"), volume, ATTN_NORM, 0);\n\t\tbreak;\n\tcase Q2MZ_CHAINGUN1:\n\t\tdl->radius = 200 + (rand()&31);\n\t\tdl->color[0] = 1;dl->color[1] = 0.25;dl->color[2] = 0;\n\t\tQ_snprintfz(soundname, sizeof(soundname), \"weapons/machgf%ib.wav\", (rand() % 5) + 1);\n\t\tQ2S_StartSound (NULL, i, CHAN_WEAPON, S_PrecacheSound(soundname), volume, ATTN_NORM, 0);\n\t\tbreak;\n\tcase Q2MZ_CHAINGUN2:\n\t\tdl->radius = 225 + (rand()&31);\n\t\tdl->color[0] = 1;dl->color[1] = 0.5;dl->color[2] = 0;\n\t\tdl->die = cl.time  + 0.1;\t// long delay\n\t\tQ_snprintfz(soundname, sizeof(soundname), \"weapons/machgf%ib.wav\", (rand() % 5) + 1);\n\t\tQ2S_StartSound (NULL, i, CHAN_WEAPON, S_PrecacheSound(soundname), volume, ATTN_NORM, 0);\n\t\tQ_snprintfz(soundname, sizeof(soundname), \"weapons/machgf%ib.wav\", (rand() % 5) + 1);\n\t\tQ2S_StartSound (NULL, i, CHAN_AUTO, S_PrecacheSound(soundname), volume, ATTN_NORM, 0.05);\n\t\tbreak;\n\tcase Q2MZ_CHAINGUN3:\n\t\tdl->radius = 250 + (rand()&31);\n\t\tdl->color[0] = 1;dl->color[1] = 1;dl->color[2] = 0;\n\t\tdl->die = cl.time  + 0.1;\t// long delay\n\t\tQ_snprintfz(soundname, sizeof(soundname), \"weapons/machgf%ib.wav\", (rand() % 5) + 1);\n\t\tQ2S_StartSound (NULL, i, CHAN_WEAPON, S_PrecacheSound(soundname), volume, ATTN_NORM, 0);\n\t\tQ_snprintfz(soundname, sizeof(soundname), \"weapons/machgf%ib.wav\", (rand() % 5) + 1);\n\t\tQ2S_StartSound (NULL, i, CHAN_AUTO, S_PrecacheSound(soundname), volume, ATTN_NORM, 0.033);\n\t\tQ_snprintfz(soundname, sizeof(soundname), \"weapons/machgf%ib.wav\", (rand() % 5) + 1);\n\t\tQ2S_StartSound (NULL, i, CHAN_AUTO, S_PrecacheSound(soundname), volume, ATTN_NORM, 0.066);\n\t\tbreak;\n\n\tcase Q2MZ_RAILGUN:\n\t\tdl->color[0] = 0.5;dl->color[1] = 0.5;dl->color[2] = 1;\n\t\tQ2S_StartSound (NULL, i, CHAN_WEAPON, S_PrecacheSound(\"weapons/railgf1a.wav\"), volume, ATTN_NORM, 0);\n\t\tbreak;\n\tcase Q2MZ_ROCKET:\n\t\tdl->color[0] = 1;dl->color[1] = 0.5;dl->color[2] = 0.2;\n\t\tQ2S_StartSound (NULL, i, CHAN_WEAPON, S_PrecacheSound(\"weapons/rocklf1a.wav\"), volume, ATTN_NORM, 0);\n\t\tQ2S_StartSound (NULL, i, CHAN_AUTO,   S_PrecacheSound(\"weapons/rocklr1b.wav\"), volume, ATTN_NORM, 0.1);\n\t\tbreak;\n\tcase Q2MZ_GRENADE:\n\t\tdl->color[0] = 1;dl->color[1] = 0.5;dl->color[2] = 0;\n\t\tQ2S_StartSound (NULL, i, CHAN_WEAPON, S_PrecacheSound(\"weapons/grenlf1a.wav\"), volume, ATTN_NORM, 0);\n\t\tQ2S_StartSound (NULL, i, CHAN_AUTO,   S_PrecacheSound(\"weapons/grenlr1b.wav\"), volume, ATTN_NORM, 0.1);\n\t\tbreak;\n\tcase Q2MZ_BFG:\n\t\tdl->color[0] = 0;dl->color[1] = 1;dl->color[2] = 0;\n\t\tQ2S_StartSound (NULL, i, CHAN_WEAPON, S_PrecacheSound(\"weapons/bfg__f1y.wav\"), volume, ATTN_NORM, 0);\n\t\tbreak;\n\n\tcase Q2MZ_LOGIN:\n\t\tdl->color[0] = 0;dl->color[1] = 1; dl->color[2] = 0;\n\t\tdl->die = cl.time + 1.0;\n\t\tQ2S_StartSound (NULL, i, CHAN_WEAPON, S_PrecacheSound(\"weapons/grenlf1a.wav\"), 1, ATTN_NORM, 0);\n//\t\tCL_LogoutEffect (pl->current.origin, weapon);\n\t\tbreak;\n\tcase Q2MZ_LOGOUT:\n\t\tdl->color[0] = 1;dl->color[1] = 0; dl->color[2] = 0;\n\t\tdl->die = cl.time + 1.0;\n\t\tQ2S_StartSound (NULL, i, CHAN_WEAPON, S_PrecacheSound(\"weapons/grenlf1a.wav\"), 1, ATTN_NORM, 0);\n//\t\tCL_LogoutEffect (pl->current.origin, weapon);\n\t\tbreak;\n\tcase Q2MZ_RESPAWN:\n\t\tdl->color[0] = 1;dl->color[1] = 1; dl->color[2] = 0;\n\t\tdl->die = cl.time + 1.0;\n\t\tQ2S_StartSound (NULL, i, CHAN_WEAPON, S_PrecacheSound(\"weapons/grenlf1a.wav\"), 1, ATTN_NORM, 0);\n//\t\tCL_LogoutEffect (pl->current.origin, weapon);\n\t\tbreak;\n\t// RAFAEL\n\tcase Q2MZ_PHALANX:\n\t\tdl->color[0] = 1;dl->color[1] = 0.5; dl->color[2] = 0.5;\n\t\tQ2S_StartSound (NULL, i, CHAN_WEAPON, S_PrecacheSound(\"weapons/plasshot.wav\"), volume, ATTN_NORM, 0);\n\t\tbreak;\n\t// RAFAEL\n\tcase Q2MZ_IONRIPPER:\n\t\tdl->color[0] = 1;dl->color[1] = 0.5; dl->color[2] = 0.5;\n\t\tQ2S_StartSound (NULL, i, CHAN_WEAPON, S_PrecacheSound(\"weapons/rippfire.wav\"), volume, ATTN_NORM, 0);\n\t\tbreak;\n\n// ======================\n// PGM\n\tcase Q2MZ_ETF_RIFLE:\n\t\tdl->color[0] = 0.9;dl->color[1] = 0.7;dl->color[2] = 0;\n\t\tQ2S_StartSound (NULL, i, CHAN_WEAPON, S_PrecacheSound(\"weapons/nail1.wav\"), volume, ATTN_NORM, 0);\n\t\tbreak;\n\tcase Q2MZ_SHOTGUN2:\n\t\tdl->color[0] = 1;dl->color[1] = 1;dl->color[2] = 0;\n\t\tQ2S_StartSound (NULL, i, CHAN_WEAPON, S_PrecacheSound(\"weapons/shotg2.wav\"), volume, ATTN_NORM, 0);\n\t\tbreak;\n\tcase Q2MZ_HEATBEAM:\n\t\tdl->color[0] = 1;dl->color[1] = 1;dl->color[2] = 0;\n\t\tdl->die = cl.time + 100;\n\t//\tQ2S_StartSound (NULL, i, CHAN_WEAPON, S_PrecacheSound(\"weapons/bfg__l1a.wav\"), volume, ATTN_NORM, 0);\n\t\tbreak;\n\tcase Q2MZ_BLASTER2:\n\t\tdl->color[0] = 0;dl->color[1] = 1;dl->color[2] = 0;\n\t\t// FIXME - different sound for blaster2 ??\n\t\tQ2S_StartSound (NULL, i, CHAN_WEAPON, S_PrecacheSound(\"weapons/blastf1a.wav\"), volume, ATTN_NORM, 0);\n\t\tbreak;\n\tcase Q2MZ_TRACKER:\n\t\t// negative flashes handled the same in gl/soft until CL_AddDLights\n\t\tdl->color[0] = -1;dl->color[1] = -1;dl->color[2] = -1;\n\t\tQ2S_StartSound (NULL, i, CHAN_WEAPON, S_PrecacheSound(\"weapons/disint2.wav\"), volume, ATTN_NORM, 0);\n\t\tbreak;\n\tcase Q2MZ_NUKE1:\n\t\tdl->color[0] = 1;dl->color[1] = 0;dl->color[2] = 0;\n\t\tdl->die = cl.time + 100;\n\t\tbreak;\n\tcase Q2MZ_NUKE2:\n\t\tdl->color[0] = 1;dl->color[1] = 1;dl->color[2] = 0;\n\t\tdl->die = cl.time + 100;\n\t\tbreak;\n\tcase Q2MZ_NUKE4:\n\t\tdl->color[0] = 0;dl->color[1] = 0;dl->color[2] = 1;\n\t\tdl->die = cl.time + 100;\n\t\tbreak;\n\tcase Q2MZ_NUKE8:\n\t\tdl->color[0] = 0;dl->color[1] = 1;dl->color[2] = 1;\n\t\tdl->die = cl.time + 100;\n\t\tbreak;\n// PGM\n// ======================\n\n\tcase Q2EXMZ_BFG2:\n\tcase Q2EXMZ_PHALANX2:\n\tcase Q2EXMZ_PROX:\n\t//case Q2EXMZ_ETF_RIFLE_2:\t//erk?\n\t\tCon_Printf(CON_WARNING\"CLQ2_ParseMuzzleFlash: %i not implemented\\n\", weapon);\n\t\tbreak;\n\tdefault:\n\t\tHost_EndGame (\"CL_ParseMuzzleFlash: bad effect index\");\n\t}\n}\n\nvoid CLQ2_ParseMuzzleFlash2 (void)\n{\n\tint\t\t\tent;\n\tint\t\t\tflash_number;\n\n\tent = (unsigned short)(short)MSG_ReadShort ();\n\tif (ent < 1 || ent >= Q2MAX_EDICTS)\n\t\tHost_EndGame (\"CL_ParseMuzzleFlash2: bad entity\");\n\n\tflash_number = MSG_ReadByte ();\n\n\tCLQ2_RunMuzzleFlash2(ent, flash_number);\n}\nvoid CLQ2EX_ParseMuzzleFlash3 (void)\n{\t//same as 2, but with a short for more numbers.\n\tint\t\t\tent;\n\tint\t\t\tflash_number;\n\n\tent = (unsigned short)(short)MSG_ReadShort ();\n\tif (ent < 1 || ent >= Q2MAX_EDICTS)\n\t\tHost_EndGame (\"CL_ParseMuzzleFlash3: bad entity\");\n\n\tflash_number = MSG_ReadUInt16 ();\n\n\tCLQ2_RunMuzzleFlash2(ent, flash_number);\n}\n\nvoid CLQ2_ParseInventory (int seat)\n{\n\tunsigned int\t\ti;\n\tfor (i=0 ; i<Q2MAX_ITEMS ; i++)\n\t\tcl.inventory[seat][i] = MSG_ReadShort ();\n}\n\n/*\n=========================================================================\n\nFRAME PARSING\n\n=========================================================================\n*/\n\n/*\n=================\nCL_ParseEntityBits\n\nReturns the entity number and the header bits\n=================\n*/\n//static int\tbitcounts[32];\t/// just for protocol profiling\nstatic int CLQ2_ParseEntityBits (quint64_t *bits)\n{\n\tquint64_t\tb, total;\n//\tint\t\t\ti;\n\tint\t\t\tnumber;\n\n\ttotal = MSG_ReadByte ();\n\tif (total & Q2U_MOREBITS1)\n\t{\n\t\tb = MSG_ReadByte ();\n\t\ttotal |= b<<8;\n\t}\n\tif (total & Q2U_MOREBITS2)\n\t{\n\t\tb = MSG_ReadByte ();\n\t\ttotal |= b<<16;\n\t}\n\tif (total & Q2U_MOREBITS3)\n\t{\n\t\tb = MSG_ReadByte ();\n\t\ttotal |= b<<24;\n\t}\n\n\tif (total & Q2UEX_MOREBITS4)\n\t{\n\t\tb = MSG_ReadByte ();\n\t\ttotal |= b<<32;\n\t}\n\n\t// count the bits for net profiling\n/*\tfor (i=0 ; i<32 ; i++)\n\t\tif (total&(1<<i))\n\t\t\tbitcounts[i]++;\n*/\n\tif (total & Q2U_NUMBER16)\n\t\tnumber = (unsigned short)MSG_ReadShort ();\n\telse\n\t\tnumber = MSG_ReadByte ();\n\n\t*bits = total;\n\n\treturn number;\n}\n\nstatic unsigned int MSG_ReadSizeQ2E (void)\n{\n\tunsigned int solid = MSG_ReadLong();\n\tif (solid != ES_SOLID_BSP && solid != ES_SOLID_NOT)\n\t\tsolid =\t(((solid>>0)&0xff)<<0)\n\t\t\t|\t(((solid>>16)&0xff)<<8)\n\t\t\t|\t((((solid>>24)&0xff)+32768-32)<<16);\t/*up can be negative*/\n\treturn solid;\n}\n\n/*\n==================\nCL_ParseDelta\n\nCan go from either a baseline or a previous packet_entity\n==================\n*/\nstatic void CLQ2_ParseDelta (entity_state_t *from, entity_state_t *to, int number, quint64_t bits)\n{\n\t// set everything to the state we are delta'ing from\n\t*to = *from;\n\n\tVectorCopy (from->origin, to->u.q2.old_origin);\n\tto->number = number;\n\n\tif (bits & Q2U_MODEL)\n\t{\n\t\tif (bits & Q2UX_INDEX16)\n\t\t\tto->modelindex = MSG_ReadShort();\n\t\telse\n\t\t\tto->modelindex = MSG_ReadByte ();\n\t}\n\tif (bits & Q2U_MODEL2)\n\t{\n\t\tif (bits & Q2UX_INDEX16)\n\t\t\tto->modelindex2 = MSG_ReadShort();\n\t\telse\n\t\t\tto->modelindex2 = MSG_ReadByte ();\n\t}\n\tif (bits & Q2U_MODEL3)\n\t{\n\t\tif (bits & Q2UX_INDEX16)\n\t\t\tto->u.q2.modelindex3 = MSG_ReadShort();\n\t\telse\n\t\t\tto->u.q2.modelindex3 = MSG_ReadByte ();\n\t}\n\tif (bits & Q2U_MODEL4)\n\t{\n\t\tif (bits & Q2UX_INDEX16)\n\t\t\tto->u.q2.modelindex4 = MSG_ReadShort();\n\t\telse\n\t\t\tto->u.q2.modelindex4 = MSG_ReadByte ();\n\t}\n\t\t\n\tif (bits & Q2U_FRAME8)\n\t\tto->frame = MSG_ReadByte ();\n\tif (bits & Q2U_FRAME16)\n\t\tto->frame = MSG_ReadUInt16();\n\tif ((bits & Q2U_FRAME8) && (bits & Q2U_FRAME16))\n\t\tCon_Printf(\"\\t8bit AND 16bit frame?\\n\");\n\n\tif ((bits & Q2U_SKIN8) && (bits & Q2U_SKIN16))\t\t//used for laser colors\n\t\tto->skinnum = MSG_ReadLong();\n\telse if (bits & Q2U_SKIN8)\n\t\tto->skinnum = MSG_ReadByte();\n\telse if (bits & Q2U_SKIN16)\n\t\tto->skinnum = MSG_ReadUInt16();\n\n\tif (bits & (Q2U_EFFECTS8|Q2U_EFFECTS16|Q2UEX_EFFECTS64))\n\t{\n\t\tunsigned int lo = 0;\n\t\tif (bits&Q2UEX_EFFECTS64)\n\t\t\tlo = MSG_ReadLong();\n\n\t\tif ( (bits & (Q2U_EFFECTS8|Q2U_EFFECTS16)) == (Q2U_EFFECTS8|Q2U_EFFECTS16) )\n\t\t\tto->effects = MSG_ReadLong();\n\t\telse if (bits & Q2U_EFFECTS16)\n\t\t\tto->effects = MSG_ReadUInt16();\n\t\telse\n\t\t\tto->effects = MSG_ReadByte();\n\n\t\tif (bits&Q2UEX_EFFECTS64)\n\t\t\tto->effects = ((quint64_t)to->effects<<32) | lo;\n\t}\n\n\tif ( (bits & (Q2U_RENDERFX8|Q2U_RENDERFX16)) == (Q2U_RENDERFX8|Q2U_RENDERFX16) )\n\t\tto->u.q2.renderfx = MSG_ReadLong() & 0x07ffffff;\t//only the standard ones actually supported by vanilla q2 without corrupting fte's own ones.\n\telse if (bits & Q2U_RENDERFX8)\n\t\tto->u.q2.renderfx = MSG_ReadByte();\n\telse if (bits & Q2U_RENDERFX16)\n\t\tto->u.q2.renderfx = MSG_ReadUInt16();\n\n\tif (cls.protocol_q2 == PROTOCOL_VERSION_Q2EX)\n\t{\n\t\tif (bits & Q2U_SOLID)\n\t\t\tto->solidsize = MSG_ReadSizeQ2E();\n\n\t\tif (!to->solidsize)\n\t\t{\t//demos reportedly compress these.\n\t\t\tif (bits & Q2U_ORIGIN1)\n\t\t\t\tto->origin[0] = MSG_ReadCoord ();\n\t\t\tif (bits & Q2U_ORIGIN2)\n\t\t\t\tto->origin[1] = MSG_ReadCoord ();\n\t\t\tif (bits & Q2U_ORIGIN3)\n\t\t\t\tto->origin[2] = MSG_ReadCoord ();\n\n\t\t\tif (bits & Q2U_OLDORIGIN)\n\t\t\t{\n\t\t\t\tto->u.q2.old_origin[0] = MSG_ReadCoord();\n\t\t\t\tto->u.q2.old_origin[1] = MSG_ReadCoord();\n\t\t\t\tto->u.q2.old_origin[2] = MSG_ReadCoord();\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (bits & Q2U_ORIGIN1)\n\t\t\t\tto->origin[0] = MSG_ReadFloat ();\n\t\t\tif (bits & Q2U_ORIGIN2)\n\t\t\t\tto->origin[1] = MSG_ReadFloat ();\n\t\t\tif (bits & Q2U_ORIGIN3)\n\t\t\t\tto->origin[2] = MSG_ReadFloat ();\n\n\t\t\tif (bits & Q2U_OLDORIGIN)\n\t\t\t{\n\t\t\t\tto->u.q2.old_origin[0] = MSG_ReadFloat();\n\t\t\t\tto->u.q2.old_origin[1] = MSG_ReadFloat();\n\t\t\t\tto->u.q2.old_origin[2] = MSG_ReadFloat();\n\t\t\t}\n\t\t}\n\n\t\tif (bits & Q2U_ANGLE1)\n\t\t\tto->angles[0] = MSG_ReadFloat();\n\t\tif (bits & Q2U_ANGLE2)\n\t\t\tto->angles[1] = MSG_ReadFloat();\n\t\tif (bits & Q2U_ANGLE3)\n\t\t\tto->angles[2] = MSG_ReadFloat();\n\t}\n\telse\n\t{\n\t\tif (bits & Q2U_ORIGIN1)\n\t\t\tto->origin[0] = MSG_ReadCoord ();\n\t\tif (bits & Q2U_ORIGIN2)\n\t\t\tto->origin[1] = MSG_ReadCoord ();\n\t\tif (bits & Q2U_ORIGIN3)\n\t\t\tto->origin[2] = MSG_ReadCoord ();\n\n\t\tif ((bits & Q2UX_ANGLE16) && (net_message.prim.flags & NPQ2_ANG16))\n\t\t{\n\t\t\tif (bits & Q2U_ANGLE1)\n\t\t\t\tto->angles[0] = MSG_ReadAngle16();\n\t\t\tif (bits & Q2U_ANGLE2)\n\t\t\t\tto->angles[1] = MSG_ReadAngle16();\n\t\t\tif (bits & Q2U_ANGLE3)\n\t\t\t\tto->angles[2] = MSG_ReadAngle16();\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (bits & Q2U_ANGLE1)\n\t\t\t\tto->angles[0] = MSG_ReadAngle();\n\t\t\tif (bits & Q2U_ANGLE2)\n\t\t\t\tto->angles[1] = MSG_ReadAngle();\n\t\t\tif (bits & Q2U_ANGLE3)\n\t\t\t\tto->angles[2] = MSG_ReadAngle();\n\t\t}\n\n\t\tif (bits & Q2U_OLDORIGIN)\n\t\t\tMSG_ReadPos (to->u.q2.old_origin);\n\t}\n\n\tif (bits & Q2U_SOUND)\n\t{\n\t\tif (cls.protocol_q2 == PROTOCOL_VERSION_Q2EX)\n\t\t{\n\t\t\tto->u.q2.sound = MSG_ReadUInt16();\n\t\t\t/*to->u.q2.soundvol =*/ (to->u.q2.sound&0x4000)?MSG_ReadByte():255;\n\t\t\t/*to->u.q2.soundattn =*/ (to->u.q2.sound&0x8000)?MSG_ReadByte():3;\n\t\t\tto->u.q2.sound &= 0x3fff;\n\t\t}\n\t\telse if (bits & Q2UX_INDEX16)\n\t\t\tto->u.q2.sound = MSG_ReadUInt16();\n\t\telse\n\t\t\tto->u.q2.sound = MSG_ReadByte ();\n\t}\n\n\tif (bits & Q2U_EVENT)\n\t\tto->u.q2.event = MSG_ReadByte ();\n\telse\n\t\tto->u.q2.event = 0;\n\n\tif (cls.protocol_q2 != PROTOCOL_VERSION_Q2EX)\n\t{\n\t\tif (bits & Q2U_SOLID)\n\t\t{\n\t\t\tif (net_message.prim.flags & NPQ2_SOLID32)\n\t\t\t\tto->solidsize = MSG_ReadLong();\n\t\t\telse\n\t\t\t\tto->solidsize = MSG_ReadSize16 (&net_message);\n\t\t}\n\t}\n\n\tif (bits & Q2UEX_ALPHA)\n\t\tto->trans = MSG_ReadByte();\n\tif (bits & Q2UEX_SCALE)\n\t\tto->scale = MSG_ReadByte();\n\tif (bits & Q2UEX_INSTANCE)\n\t\tto->u.q2.instance = MSG_ReadByte();\n\tif (bits & Q2UEX_OWNER)\n\t\tto->u.q2.owner = MSG_ReadShort();\n\tif (bits & Q2UEX_OLDFRAME)\n\t\tto->u.q2.oldframe = MSG_ReadUInt16();\n}\n\nvoid CLQ2_ClearParticleState(void)\n{\n\tint i;\n\tfor (i = 0; i < MAX_Q2EDICTS; i++)\n\t{\n\t\tP_DelinkTrailstate(&cl_entities[i].trailstate);\n\t}\n}\n\n/*\n==================\nCL_DeltaEntity\n\nParses deltas from the given base and adds the resulting entity\nto the current frame\n==================\n*/\nstatic void CLQ2_DeltaEntity (q2frame_t *frame, int newnum, entity_state_t *old, uint64_t bits)\n{\n\tq2centity_t\t*ent;\n\tentity_state_t\t*state;\n\n\tent = &cl_entities[newnum];\n\n\tstate = &clq2_parse_entities[cl.parse_entities & (MAX_PARSE_ENTITIES-1)];\n\tcl.parse_entities++;\n\tframe->num_entities++;\n\n\tCLQ2_ParseDelta (old, state, newnum, bits);\n\n\t// some data changes will force no lerping\n\tif (state->modelindex != ent->current.modelindex\n\t\t|| state->modelindex2 != ent->current.modelindex2\n\t\t|| state->u.q2.modelindex3 != ent->current.u.q2.modelindex3\n\t\t|| state->u.q2.modelindex4 != ent->current.u.q2.modelindex4\n\t\t|| fabs(state->origin[0] - ent->current.origin[0]) > 512\n\t\t|| fabs(state->origin[1] - ent->current.origin[1]) > 512\n\t\t|| fabs(state->origin[2] - ent->current.origin[2]) > 512\n\t\t|| state->u.q2.event == Q2EV_PLAYER_TELEPORT\n\t\t|| state->u.q2.event == Q2EV_OTHER_TELEPORT\n\t\t)\n\t{\n\t\tent->serverframe = -99;\n\t}\n\n\tif (ent->serverframe != cl.q2frame.serverframe - 1)\n\t{\t// wasn't in last update, so initialize some things\n\t\t// clear trailstate\n\t\tP_DelinkTrailstate(&ent->trailstate);\n\n\t\t// duplicate the current state so lerping doesn't hurt anything\n\t\tent->prev = *state;\n\t\tif (state->u.q2.event == Q2EV_OTHER_TELEPORT)\n\t\t{\n\t\t\tVectorCopy (state->origin, ent->prev.origin);\n\t\t\tVectorCopy (state->origin, ent->lerp_origin);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tVectorCopy (state->u.q2.old_origin, ent->prev.origin);\n\t\t\tVectorCopy (state->u.q2.old_origin, ent->lerp_origin);\n\t\t}\n\n\t\tent->oldframe = state->u.q2.oldframe;\n\t\tent->oldframetime = cl.oldgametime;\n\t}\n\telse\n\t{\t// shuffle the last state to previous\n\t\tent->prev = ent->current;\n\n\t\tif (old->frame != state->frame)\n\t\t{\n\t\t\tent->oldframe = old->frame;\n\t\t\tent->oldframetime = cl.oldgametime;\n\t\t}\n\t}\n\n\tent->serverframe = cl.q2frame.serverframe;\n\tent->current = *state;\n}\n\n/*\n==================\nCL_ParsePacketEntities\n\nAn svc_packetentities has just been parsed, deal with the\nrest of the data stream.\n==================\n*/\nstatic void CLQ2_ParsePacketEntities (q2frame_t *oldframe, q2frame_t *newframe)\n{\n\tunsigned int\t\tnewnum;\n\tquint64_t\t\t\tbits;\n\tentity_state_t\t*oldstate=NULL;\n\tunsigned int\t\toldindex, oldnum;\n\n\tcl.validsequence = cls.netchan.incoming_sequence;\n\tcl.ackedmovesequence = cls.netchan.incoming_acknowledged;\n\n\tcl.outframes[cl.ackedmovesequence&UPDATE_MASK].latency = realtime - cl.outframes[cl.ackedmovesequence&UPDATE_MASK].senttime;\n\n\tnewframe->parse_entities = cl.parse_entities;\n\tnewframe->num_entities = 0;\n\n\t// delta from the entities present in oldframe\n\toldindex = 0;\n\tif (!oldframe)\n\t\toldnum = 99999;\n\telse\n\t{\n\t\tif (oldindex >= oldframe->num_entities)\n\t\t\toldnum = 99999;\n\t\telse\n\t\t{\n\t\t\toldstate = &clq2_parse_entities[(oldframe->parse_entities+oldindex) & (MAX_PARSE_ENTITIES-1)];\n\t\t\toldnum = oldstate->number;\n\t\t}\n\t}\n\n\twhile (1)\n\t{\n\t\tnewnum = CLQ2_ParseEntityBits (&bits);\n\t\tif (newnum >= MAX_Q2EDICTS)\n\t\t\tHost_EndGame (\"CL_ParsePacketEntities: bad number:%i\", newnum);\n\n\t\tif (MSG_GetReadCount() > net_message.cursize)\n\t\t\tHost_EndGame (\"CL_ParsePacketEntities: end of message\");\n\n\t\tif (!newnum)\n\t\t\tbreak;\n\n\t\twhile (oldnum < newnum)\n\t\t{\t// one or more entities from the old packet are unchanged\n\t\t\tif (cl_shownet.ival == 3)\n\t\t\t\tCon_Printf (\"%i:   unchanged: %i\\n\", MSG_GetReadCount(), oldnum);\n\t\t\tCLQ2_DeltaEntity (newframe, oldnum, oldstate, 0);\n\t\t\t\n\t\t\toldindex++;\n\n\t\t\tif (!oldframe || oldindex >= oldframe->num_entities)\n\t\t\t\toldnum = 99999;\n\t\t\telse\n\t\t\t{\n\t\t\t\toldstate = &clq2_parse_entities[(oldframe->parse_entities+oldindex) & (MAX_PARSE_ENTITIES-1)];\n\t\t\t\toldnum = oldstate->number;\n\t\t\t}\n\t\t}\n\n\t\tif (bits & Q2U_REMOVE)\n\t\t{\t// the entity present in oldframe is not in the current frame\n\t\t\tif (cl_shownet.ival == 3)\n\t\t\t\tCon_Printf (\"%3i:   remove: %i\\n\", MSG_GetReadCount(), newnum);\n\t\t\tif (oldnum != newnum)\n\t\t\t\tCon_Printf (\"U_REMOVE: oldnum != newnum\\n\");\n\n\t\t\toldindex++;\n\n\t\t\tif (!oldframe || oldindex >= oldframe->num_entities)\n\t\t\t\toldnum = 99999;\n\t\t\telse\n\t\t\t{\n\t\t\t\toldstate = &clq2_parse_entities[(oldframe->parse_entities+oldindex) & (MAX_PARSE_ENTITIES-1)];\n\t\t\t\toldnum = oldstate->number;\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (oldnum == newnum)\n\t\t{\t// delta from previous state\n\t\t\tif (cl_shownet.ival == 3)\n\t\t\t\tCon_Printf (\"%3i:   delta: %i\\n\", MSG_GetReadCount(), newnum);\n\t\t\tCLQ2_DeltaEntity (newframe, newnum, oldstate, bits);\n\n\t\t\toldindex++;\n\n\t\t\tif (oldindex >= oldframe->num_entities)\n\t\t\t\toldnum = 99999;\n\t\t\telse\n\t\t\t{\n\t\t\t\toldstate = &clq2_parse_entities[(oldframe->parse_entities+oldindex) & (MAX_PARSE_ENTITIES-1)];\n\t\t\t\toldnum = oldstate->number;\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (oldnum > newnum)\n\t\t{\t// delta from baseline\n\t\t\tif (cl_shownet.ival == 3)\n\t\t\t\tCon_Printf (\"%3i:   baseline: %i\\n\", MSG_GetReadCount(), newnum);\n\t\t\tCLQ2_DeltaEntity (newframe, newnum, &cl_entities[newnum].baseline, bits);\n\t\t\tcontinue;\n\t\t}\n\n\t}\n\n\t// any remaining entities in the old frame are copied over\n\twhile (oldnum != 99999)\n\t{\t// one or more entities from the old packet are unchanged\n\t\tif (cl_shownet.ival == 3)\n\t\t\tCon_Printf (\"%3i:   unchanged: %i\\n\", MSG_GetReadCount(), oldnum);\n\t\tCLQ2_DeltaEntity (newframe, oldnum, oldstate, 0);\n\t\t\n\t\toldindex++;\n\n\t\tif (oldindex >= oldframe->num_entities)\n\t\t\toldnum = 99999;\n\t\telse\n\t\t{\n\t\t\toldstate = &clq2_parse_entities[(oldframe->parse_entities+oldindex) & (MAX_PARSE_ENTITIES-1)];\n\t\t\toldnum = oldstate->number;\n\t\t}\n\t}\n}\n\nvoid CLQ2_ParseBaseline (void)\n{\n\tentity_state_t\t*es;\n\tquint64_t\t\t\t\tbits;\n\tint\t\t\t\tnewnum;\n\n\tnewnum = CLQ2_ParseEntityBits (&bits);\n\tes = &cl_entities[newnum].baseline;\n\tCLQ2_ParseDelta (&nullentitystate, es, newnum, bits);\n}\n\n\n/*\n===================\nCL_ParsePlayerstate\n===================\n*/\nvoid CLQ2_ParsePlayerstate (int seat, q2frame_t *oldframe, q2frame_t *newframe, int extflags)\n{\n\tint\t\t\tflags;\n\tq2player_state_t\t*state;\n\tint\t\t\ti;\n\tint\t\t\tstatbits;\n\tqboolean q2e = cls.protocol_q2==PROTOCOL_VERSION_Q2EX;\n\n\tstate = &newframe->seat[seat].playerstate;\n\n\t// clear to old value before delta parsing\n\tif (oldframe)\n\t{\n\t\t*state = oldframe->seat[seat].playerstate;\n\t\tnewframe->seat[seat].clientnum = oldframe->seat[seat].clientnum;\n\t}\n\telse\n\t{\n\t\tmemset (state, 0, sizeof(*state));\n\t\tnewframe->seat[seat].clientnum = cl.playerview[seat].playernum;\n\t}\n\n\tflags = MSG_ReadUInt16 ();\n\tif (flags & Q2PS_EXTRABITS)\n\t\tflags |= (q2e?MSG_ReadUInt16():MSG_ReadByte())<<16;\n\n\t//\n\t// parse the pmove_state_t\n\t//\n\tif (flags & Q2PS_M_TYPE)\n\t{\n\t\tstate->pmove.pm_type = MSG_ReadByte ();\n\t\tif (q2e)\n\t\t{\t//sigh... q2e added some extra pmove types that we don't support.\n\t\t\tswitch((int)state->pmove.pm_type)\n\t\t\t{\n\t\t\tcase Q2EPM_NORMAL: state->pmove.pm_type = Q2PM_NORMAL; break;\n\t\t\tcase Q2EPM_GRAPPLE: state->pmove.pm_type = Q2PM_NORMAL; break;\t//FIXME: not supported\n\t\t\tcase Q2EPM_SPECTATOR: state->pmove.pm_type = Q2PM_SPECTATOR; break;\n\t\t\tcase Q2EPM_SPECTATOR2: state->pmove.pm_type = Q2PM_SPECTATOR; break;\t//FIXME: not supported.\n\t\t\tcase Q2EPM_DEAD: state->pmove.pm_type = Q2PM_DEAD; break;\n\t\t\tcase Q2EPM_GIB: state->pmove.pm_type = Q2PM_GIB; break;\n\t\t\tcase Q2EPM_FREEZE: state->pmove.pm_type = Q2PM_FREEZE; break;\n\t\t\t}\n\t\t}\n\t}\n\n\tif (flags & Q2PS_M_ORIGIN)\n\t{\n\t\tif (q2e)\n\t\t{\n\t\t\tstate->pmove.origin[0] = MSG_ReadFloat ()*8;\n\t\t\tstate->pmove.origin[1] = MSG_ReadFloat ()*8;\n\t\t\tif (extflags & Q2PSX_OLD)\n\t\t\t\tstate->pmove.origin[2] = MSG_ReadFloat ()*8;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tstate->pmove.origin[0] = MSG_ReadShort ();\n\t\t\tstate->pmove.origin[1] = MSG_ReadShort ();\n\t\t\tif (extflags & Q2PSX_OLD)\n\t\t\t\tstate->pmove.origin[2] = MSG_ReadShort ();\n\t\t}\n\t}\n\tif (extflags & Q2PSX_M_ORIGIN2)\n\t\tstate->pmove.origin[2] = MSG_ReadShort ();\n\n\tif (flags & Q2PS_M_VELOCITY)\n\t{\n\t\tif (q2e)\n\t\t{\n\t\t\tstate->pmove.velocity[0] = MSG_ReadFloat ()*8;\n\t\t\tstate->pmove.velocity[1] = MSG_ReadFloat ()*8;\n\t\t\tif (extflags & Q2PSX_OLD)\n\t\t\t\tstate->pmove.velocity[2] = MSG_ReadFloat ()*8;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tstate->pmove.velocity[0] = MSG_ReadShort ();\n\t\t\tstate->pmove.velocity[1] = MSG_ReadShort ();\n\t\t\tif (extflags & Q2PSX_OLD)\n\t\t\t\tstate->pmove.velocity[2] = MSG_ReadShort ();\n\t\t}\n\t}\n\tif (extflags & Q2PSX_M_VELOCITY2)\n\t\tstate->pmove.velocity[2] = MSG_ReadShort ();\n\n\tif (flags & Q2PS_M_TIME)\n\t\tstate->pmove.pm_time = q2e?MSG_ReadUInt16():MSG_ReadByte ();\n\n\tif (flags & Q2PS_M_FLAGS)\n\t\tstate->pmove.pm_flags = q2e?MSG_ReadUInt16():MSG_ReadByte ();\n\n\tif (flags & Q2PS_M_GRAVITY)\n\t\tstate->pmove.gravity = MSG_ReadShort ();\n\n\tif (flags & Q2PS_M_DELTA_ANGLES)\n\t{\n\t\tif (q2e)\n\t\t{\n\t\t\tstate->pmove.delta_angles[0] = ANGLE2SHORT(MSG_ReadFloat ());\n\t\t\tstate->pmove.delta_angles[1] = ANGLE2SHORT(MSG_ReadFloat ());\n\t\t\tstate->pmove.delta_angles[2] = ANGLE2SHORT(MSG_ReadFloat ());\n\t\t}\n\t\telse\n\t\t{\n\t\t\tstate->pmove.delta_angles[0] = MSG_ReadShort ();\n\t\t\tstate->pmove.delta_angles[1] = MSG_ReadShort ();\n\t\t\tstate->pmove.delta_angles[2] = MSG_ReadShort ();\n\t\t}\n\t}\n\n//\tif (cl.attractloop)\n//\t\tstate->pmove.pm_type = Q2PM_FREEZE;\t\t// demo playback\n\n\t//\n\t// parse the rest of the player_state_t\n\t//\n\tif (flags & Q2PS_VIEWOFFSET)\n\t{\n\t\tif (q2e)\n\t\t{\n\t\t\tstate->viewoffset[0] = MSG_ReadShort () * (1/16.f);\n\t\t\tstate->viewoffset[1] = MSG_ReadShort () * (1/16.f);\n\t\t\tstate->viewoffset[2] = MSG_ReadShort () * (1/16.f);\n\t\t\tstate->viewoffset[2] += MSG_ReadChar();\n\t\t}\n\t\telse\n\t\t{\n\t\t\tstate->viewoffset[0] = MSG_ReadChar () * 0.25;\n\t\t\tstate->viewoffset[1] = MSG_ReadChar () * 0.25;\n\t\t\tstate->viewoffset[2] = MSG_ReadChar () * 0.25;\n\t\t}\n\t}\n\n\tif (flags & Q2PS_VIEWANGLES)\n\t{\n\t\tif (q2e)\n\t\t{\n\t\t\tstate->viewangles[0] = MSG_ReadFloat ();\n\t\t\tstate->viewangles[1] = MSG_ReadFloat ();\n\t\t\tif (extflags & Q2PSX_OLD)\n\t\t\t\tstate->viewangles[2] = MSG_ReadFloat ();\n\t\t}\n\t\telse\n\t\t{\n\t\t\tstate->viewangles[0] = MSG_ReadAngle16 ();\n\t\t\tstate->viewangles[1] = MSG_ReadAngle16 ();\n\t\t\tif (extflags & Q2PSX_OLD)\n\t\t\t\tstate->viewangles[2] = MSG_ReadAngle16 ();\n\t\t}\n\t}\n\tif (extflags & Q2PSX_VIEWANGLE2)\n\t\tstate->viewangles[2] = MSG_ReadAngle16 ();\n\n\tif (flags & Q2PS_KICKANGLES)\n\t{\n\t\tif (q2e)\n\t\t{\n\t\t\tstate->kick_angles[0] = MSG_ReadShort () * (1/1024.f);\n\t\t\tstate->kick_angles[1] = MSG_ReadShort () * (1/1024.f);\n\t\t\tstate->kick_angles[2] = MSG_ReadShort () * (1/1024.f);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tstate->kick_angles[0] = MSG_ReadChar () * 0.25;\n\t\t\tstate->kick_angles[1] = MSG_ReadChar () * 0.25;\n\t\t\tstate->kick_angles[2] = MSG_ReadChar () * 0.25;\n\t\t}\n\t}\n\n\tif (flags & Q2PS_WEAPONINDEX)\n\t{\n\t\tif (q2e)\n\t\t{\n\t\t\tstate->gunindex = MSG_ReadUInt16 ();\n\t\t\t//state->gunskin = state->gunindex>>13\n\t\t\tstate->gunindex &= 0x1fff;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (flags & Q2FTEPS_INDEX16)\n\t\t\t\tstate->gunindex = MSG_ReadShort ();\n\t\t\telse\n\t\t\t\tstate->gunindex = MSG_ReadByte ();\n\t\t}\n\t}\n\n\tif (flags & Q2PS_WEAPONFRAME)\n\t{\n\t\tif (q2e)\n\t\t{\n\t\t\tunsigned int fl = MSG_ReadUInt16 ();\n\t\t\tstate->gunframe = fl & 0x1ff;\n\t\t\tif (fl & (1<< 9)) state->gunoffset[0] = MSG_ReadFloat ();\n\t\t\tif (fl & (1<<10)) state->gunoffset[1] = MSG_ReadFloat ();\n\t\t\tif (fl & (1<<11)) state->gunoffset[2] = MSG_ReadFloat ();\n\t\t\tif (fl & (1<<12)) state->gunangles[0] = MSG_ReadFloat ();\n\t\t\tif (fl & (1<<13)) state->gunangles[1] = MSG_ReadFloat ();\n\t\t\tif (fl & (1<<14)) state->gunangles[2] = MSG_ReadFloat ();\n\t\t\tif (fl & (1<<15)) /*state->gunangles[2] =*/ MSG_ReadByte ();\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (flags & Q2FTEPS_INDEX16)\n\t\t\t\tstate->gunframe = MSG_ReadShort ();\n\t\t\telse\n\t\t\t\tstate->gunframe = MSG_ReadByte ();\n\t\t\tif (extflags & Q2PSX_OLD)\n\t\t\t{\n\t\t\t\tstate->gunoffset[0] = MSG_ReadChar ()*0.25;\n\t\t\t\tstate->gunoffset[1] = MSG_ReadChar ()*0.25;\n\t\t\t\tstate->gunoffset[2] = MSG_ReadChar ()*0.25;\n\t\t\t\tstate->gunangles[0] = MSG_ReadChar ()*0.25;\n\t\t\t\tstate->gunangles[1] = MSG_ReadChar ()*0.25;\n\t\t\t\tstate->gunangles[2] = MSG_ReadChar ()*0.25;\n\t\t\t}\n\t\t}\n\t}\n\tif (extflags & Q2PSX_GUNOFFSET)\n\t{\n\t\tstate->gunoffset[0] = MSG_ReadChar ()*0.25;\n\t\tstate->gunoffset[1] = MSG_ReadChar ()*0.25;\n\t\tstate->gunoffset[2] = MSG_ReadChar ()*0.25;\n\t}\n\tif (extflags & Q2PSX_GUNANGLES)\n\t{\n\t\tstate->gunangles[0] = MSG_ReadChar ()*0.25;\n\t\tstate->gunangles[1] = MSG_ReadChar ()*0.25;\n\t\tstate->gunangles[2] = MSG_ReadChar ()*0.25;\n\t}\n\n\tif (flags & Q2PS_BLEND)\n\t{\n\t\tstate->blend[0] = MSG_ReadByte ()/255.0;\n\t\tstate->blend[1] = MSG_ReadByte ()/255.0;\n\t\tstate->blend[2] = MSG_ReadByte ()/255.0;\n\t\tstate->blend[3] = MSG_ReadByte ()/255.0;\n\t}\n\n\tif (flags & Q2PS_FOV)\n\t\tstate->fov = MSG_ReadByte ();\n\n\tif (flags & Q2PS_RDFLAGS)\n\t\tstate->rdflags = MSG_ReadByte ();\n\n\t// parse stats\n\tif (extflags & (Q2PSX_OLD|Q2PSX_STATS))\n\t\tstatbits = MSG_ReadLong ();\n\telse\n\t\tstatbits = 0;\n\tif (statbits)\n\t{\n\t\tfor (i=0 ; i<min(32,Q2MAX_STATS) ; i++)\n\t\t\tif (statbits & (1<<i) )\n\t\t\t\tstate->stats[i] = MSG_ReadShort();\n\t}\n\tif (q2e)\n\t{\n\t\t// parse stats\n\t\tstatbits = MSG_ReadLong ();\n\t\tif (statbits)\n\t\t{\n\t\t\tfor (i=0 ; i<Q2MAX_STATS-32 ; i++)\n\t\t\t\tif (statbits & (1<<i) )\n\t\t\t\t\tstate->stats[32+i] = MSG_ReadShort();\n\t\t\tfor ( ; i<32 ; i++)\n\t\t\t\tif (statbits & (1<<i) )\n\t\t\t\t\tMSG_ReadShort();\n\t\t}\n\n\t\tif (flags & Q2EXPS_DAMAGEBLEND)\n\t\t{\n\t\t\t/*state->damageblend[0] = (1/255.f) */ MSG_ReadByte ();\n\t\t\t/*state->damageblend[1] = (1/255.f) */ MSG_ReadByte ();\n\t\t\t/*state->damageblend[2] = (1/255.f) */ MSG_ReadByte ();\n\t\t\t/*state->damageblend[3] = (1/255.f) */ MSG_ReadByte ();\n\t\t}\n\t\tif (flags & Q2EXPS_TEAMID)\n\t\t\tMSG_ReadByte();\n\t}\n\telse\n\t{\n\t\tif ((extflags & Q2PSX_CLIENTNUM) || (flags & Q2FTEPS_CLIENTNUM))\n\t\t\tnewframe->seat[seat].clientnum = MSG_ReadByte();\n\t}\n}\n\n\n/*\n==================\nCL_FireEntityEvents\n\n==================\n*/\nvoid CLQ2_FireEntityEvents (q2frame_t *frame)\n{\n\tentity_state_t\t\t*s1;\n\tint\t\t\t\t\tpnum, num;\n\n\tfor (pnum = 0 ; pnum<frame->num_entities ; pnum++)\n\t{\n\t\tnum = (frame->parse_entities + pnum)&(MAX_PARSE_ENTITIES-1);\n\t\ts1 = &clq2_parse_entities[num];\n\t\tif (s1->u.q2.event)\n\t\t\tCLQ2_EntityEvent (s1);\n\n\t\t// EF_TELEPORTER acts like an event, but is not cleared each frame\n\t\tif (s1->effects & Q2EF_TELEPORTER)\n\t\t\tCLQ2_TeleporterParticles (s1);\n\t}\n}\n\nvoid CLR1Q2_ParsePlayerUpdate(void)\n{\n\tunsigned int framenum = MSG_ReadLong();\n\tq2frame_t\t*frame = &cl.q2frames[framenum & Q2UPDATE_MASK];\n\tint seat;\n\tif (frame->serverframe != framenum)\n\t\tCon_DPrintf(\"svcr1q2_playerupdate: stale frame\\n\");\n\telse if (!frame->valid)\n\t\tCon_DPrintf(\"svcr1q2_playerupdate: invalid frame\\n\");\t//excrement happens.\n\telse\n\t{\n\t\tint pnum;\n\t\tvec3_t neworg;\n\t\tentity_state_t *st;\n\t\tfor (pnum = 0; pnum < frame->num_entities; pnum++)\n\t\t{\n\t\t\tst = &clq2_parse_entities[(frame->parse_entities+pnum) & (MAX_PARSE_ENTITIES-1)];\n\n\t\t\t//I don't like how r1q2 does its maxclients, so I'm just going to go on message size instead\n\t\t\tif (MSG_GetReadCount() == net_message.cursize)\n\t\t\t\tbreak;\n\n\t\t\t//the local client(s) is not included, thanks to prediction covering that.\n\t\t\tfor (seat = 0; seat < cl.splitclients; seat++)\n\t\t\t{\n\t\t\t\tif (st->number == cl.playerview[0].playernum+1)\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (seat != cl.splitclients)\n\t\t\t\tcontinue;\n\n\t\t\tif (st->number != 1)\n\t\t\t\tcontinue;\n\n\t\t\t//FIXME: handle this, with lerping and stuff.\n\t\t\tMSG_ReadPos(neworg);\n\t\t}\n\n\t\t//just for sanity's sake\n\t\tif (MSG_GetReadCount() != net_message.cursize)\n\t\t\tmsg_badread = true;\n\t}\n\t//this should be the only/last thing in these packets, because if it isn't then we're screwed when a packet got lost\n\tnet_message.currentbit = net_message.cursize<<3;\n}\n\n#define SHOWNET(x) if(cl_shownet.value>=2)Con_Printf (\"%3i:%s\\n\", MSG_GetReadCount()-1, x);\n/*\n================\nCL_ParseFrame\n================\n*/\nvoid CLQ2_ParseFrame (int extrabits)\n{\n\tint\t\t\tcmd;\n\tint\t\t\tlen;\n\tq2frame_t\t\t*old;\n\tint i,j, chokecount;\n\tstatic float throttle;\n\n\tmemset (&cl.q2frame, 0, sizeof(cl.q2frame));\n\n#if 0\n\tCLQ2_ClearProjectiles(); // clear projectiles for new frame\n#endif\n\n\tif (cls.protocol_q2 == PROTOCOL_VERSION_R1Q2 || cls.protocol_q2 == PROTOCOL_VERSION_Q2PRO)\n\t{\n\t\tunsigned int bits = MSG_ReadLong();\n\t\tcl.q2frame.serverframe = bits & 0x07ffffff;\n\t\ti = bits >> 27;\n\t\tif (i == 31)\n\t\t\tcl.q2frame.deltaframe = -1;\n\t\telse\n\t\t\tcl.q2frame.deltaframe = cl.q2frame.serverframe - i;\n\t\tbits = MSG_ReadByte();\n\t\tchokecount = bits & 0xf;\n\t\textrabits = (extrabits<<4) | (bits>>4);\n\t}\n\telse\n\t{\n\t\tcl.q2frame.serverframe = MSG_ReadLong ();\n\t\tcl.q2frame.deltaframe = MSG_ReadLong ();\n\t\tif (cls.protocol_q2 > 26)\n\t\t\tchokecount = MSG_ReadByte ();\n\t\telse\n\t\t\tchokecount = 0;\n\n\t\textrabits = Q2PSX_OLD;\n\t}\n\n\tcl.q2frame.servertime = cl.q2frame.serverframe*(1000/cl.q2svnetrate);\n\n\tcl.oldgametime = cl.gametime;\n\tcl.oldgametimemark = cl.gametimemark;\n\tcl.gametime = cl.q2frame.servertime/1000.;\n\tcl.gametimemark = realtime;\n\n\tfor (j=0 ; j<chokecount ; j++)\n\t\tcl.outframes[ (cls.netchan.incoming_acknowledged-1-j)&UPDATE_MASK ].latency = -2;\n\n\tif (cl_shownet.value == 3)\n\t\tCon_Printf (\"   frame:%i  delta:%i\\n\", cl.q2frame.serverframe, cl.q2frame.deltaframe);\n\n\t// If the frame is delta compressed from data that we\n\t// no longer have available, we must suck up the rest of\n\t// the frame, but not use it, then ask for a non-compressed\n\t// message \n\tif (cl.q2frame.deltaframe <= 0)\n\t{\n\t\tcl.q2frame.valid = true;\t\t// uncompressed frame\n\t\told = NULL;\n\t\tcls.demohadkeyframe = true;\t//yay! all is right with the world!\n\t}\n\telse\n\t{\n\t\told = &cl.q2frames[cl.q2frame.deltaframe & Q2UPDATE_MASK];\n\t\tif (!old->valid)\n\t\t{\t// should never happen\n\t\t\tCon_ThrottlePrintf (&throttle, 0, CON_WARNING\"Delta from invalid frame (not supposed to happen!).\\n\");\n\t\t\told = NULL;\n\t\t}\n\t\telse if (old->serverframe != cl.q2frame.deltaframe)\n\t\t{\t// The frame that the server did the delta from\n\t\t\t// is too old, so we can't reconstruct it properly.\n\t\t\tCon_ThrottlePrintf (&throttle, 0, CON_WARNING\"Delta frame too old.\\n\");\n\t\t}\n\t\telse if (cl.parse_entities - old->parse_entities > MAX_PARSE_ENTITIES-128)\n\t\t{\n\t\t\tCon_ThrottlePrintf (&throttle, 0, CON_WARNING\"Delta parse_entities too old.\\n\");\n\t\t}\n\t\telse\n\t\t\tcl.q2frame.valid = true;\t// valid delta parse\n\t}\n\n\t// clamp time \n\tif (cl.time > cl.q2frame.servertime/1000.0)\n\t\tcl.time = cl.q2frame.servertime/1000.0;\n\telse if (cl.time < (cl.q2frame.servertime - 100)/1000.0)\n\t\tcl.time = (cl.q2frame.servertime - 100)/1000.0;\n\n\tif (cls.protocol_q2 == PROTOCOL_VERSION_Q2EX)\n\t{\n\t\tfor (i = 0; i<cl.splitclients; i++)\n\t\t{\n\t\t\tlen = MSG_ReadByte ();\n\t\t\tif (len > countof(cl.q2frame.seat[i].areabits))\n\t\t\t\tHost_EndGame (\"CL_ParseFrame: too many areas\");\n\t\t\tMSG_ReadData (&cl.q2frame.seat[i].areabits, len);\n\n\t\t\tcmd = MSG_ReadByte ();\n\t\t\tif (cmd != svcq2_playerinfo)\n\t\t\t\tHost_EndGame (\"CL_ParseFrame: not playerinfo\");\n\t\t\tSHOWNET(\"svcq2_playerinfo\");\n\t\t\tCLQ2_ParsePlayerstate (i, old, &cl.q2frame, extrabits);\n\t\t}\n\t\tcmd = MSG_ReadByte ();\n//\t\tSHOWNET(svc_strings[cmd]);\n\t\tif (cmd != svcq2_packetentities)\n\t\t\tHost_EndGame (\"CL_ParseFrame: not packetentities\");\n\t\tSHOWNET(\"svcq2_packetentities\");\n\t}\n\telse\n\t{\n\t\t// read areabits\n\t\tlen = MSG_ReadByte ();\n\t\tif (len > MAX_Q2MAP_AREAS/8)\n\t\t\tHost_EndGame (\"CL_ParseFrame: too many areas\");\n\t\tMSG_ReadData (&cl.q2frame.seat[0].areabits, len);\n\n\t\t// normally playerstate then packet entities\n\t\t//in splitscreen we may have multiple player states, one per player.\n\t\tif (cls.protocol_q2 != PROTOCOL_VERSION_R1Q2 && cls.protocol_q2 != PROTOCOL_VERSION_Q2PRO)\n\t\t{\n\t\t\tfor (cl.splitclients = 0; ; )\n\t\t\t{\n\t\t\t\tcmd = MSG_ReadByte ();\n\t//\t\t\tSHOWNET(svc_strings[cmd]);\n\t\t\t\tif (cmd == svcq2_playerinfo && cl.splitclients < MAX_SPLITS)\n\t\t\t\t{\n\t\t\t\t\tif (cl.splitclients != 0)\n\t\t\t\t\t\tmemcpy(cl.q2frame.seat[cl.splitclients].areabits, cl.q2frame.seat[0].areabits, len);\n\t\t\t\t\tSHOWNET(\"svcq2_playerinfo\");\n\t\t\t\t\tCLQ2_ParsePlayerstate (cl.splitclients++, old, &cl.q2frame, extrabits);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (!cl.splitclients)\n\t\t\t\tHost_EndGame (\"CL_ParseFrame: no playerinfo\");\n\t\t\tif (cmd != svcq2_packetentities)\n\t\t\t\tHost_EndGame (\"CL_ParseFrame: not packetentities\");\n\t\t\tSHOWNET(\"svcq2_packetentities\");\n\t\t}\n\t\telse\n\t\t{\n\t\t\tcl.splitclients = 1;\n\t\t\tCLQ2_ParsePlayerstate (0, old, &cl.q2frame, extrabits);\n\t\t}\n\t}\n\tCLQ2_ParsePacketEntities (old, &cl.q2frame);\n\n\tfor (cmd = 0; cmd < MAX_SPLITS; cmd++)\n\t\tcl.playerview[cmd].viewentity = cl.q2frame.seat[cmd].clientnum+1;\n\n\t// save the frame off in the backup array for later delta comparisons\n\tcl.q2frames[cl.q2frame.serverframe & Q2UPDATE_MASK] = cl.q2frame;\n\n\tcl.parsecount = cl.q2frame.serverframe;\n\tif (cl.q2frame.valid)\n\t{\n\t\t// getting a valid frame message ends the connection process\n\t\tif (cls.state != ca_active)\n\t\t{\n\t\t\tCL_MakeActive(\"Quake2\");\n\n//\t\t\tcl.force_refdef = true;\n//\t\t\tcl.predicted_origin[0] = cl.q2frame.playerstate[0].pmove.origin[0]*0.125;\n//\t\t\tcl.predicted_origin[1] = cl.q2frame.playerstate[0].pmove.origin[1]*0.125;\n//\t\t\tcl.predicted_origin[2] = cl.q2frame.playerstate[0].pmove.origin[2]*0.125;\n//\t\t\tVectorCopy (cl.q2frame.playerstate[0].viewangles, cl.predicted_angles);\n//\t\t\tif (cls.disable_servercount != cl.servercount\n//\t\t\t\t&& cl.refresh_prepped)\n\t\t\t\tSCR_EndLoadingPlaque ();\t// get rid of loading plaque\n\t\t}\n//\t\tcl.sound_prepped = true;\t// can start mixing ambient sounds\n\t\n\t\t// fire entity events\n\t\tCLQ2_FireEntityEvents (&cl.q2frame);\n#ifdef Q2BSPS\n\t\tCLQ2_CheckPredictionError ();\n#endif\n\n\t\tcl.validsequence = cl.q2frame.serverframe;\n\t}\n}\n\n/*\n==========================================================================\n\nINTERPOLATE BETWEEN FRAMES TO GET RENDERING PARMS\n\n==========================================================================\n*/\n/*\nstruct model_s *S_RegisterSexedModel (entity_state_t *ent, char *base)\n{\n\tint\t\t\t\tn;\n\tchar\t\t\t*p;\n\tstruct model_s\t*mdl;\n\tchar\t\t\tmodel[MAX_QPATH];\n\tchar\t\t\tbuffer[MAX_QPATH];\n\n\t// determine what model the client is using\n\tmodel[0] = 0;\n\tn = CS_PLAYERSKINS + ent->number - 1;\n\tif (cl.configstrings[n][0])\n\t{\n\t\tp = strchr(cl.configstrings[n], '\\\\');\n\t\tif (p)\n\t\t{\n\t\t\tp += 1;\n\t\t\tstrcpy(model, p);\n\t\t\tp = strchr(model, '/');\n\t\t\tif (p)\n\t\t\t\t*p = 0;\n\t\t}\n\t}\n\t// if we can't figure it out, they're male\n\tif (!model[0])\n\t\tstrcpy(model, \"male\");\n\n\tCom_sprintf (buffer, sizeof(buffer), \"players/%s/%s\", model, base+1);\n\tmdl = re.RegisterModel(buffer);\n\tif (!mdl) {\n\t\t// not found, try default weapon model\n\t\tCom_sprintf (buffer, sizeof(buffer), \"players/%s/weapon.md2\", model);\n\t\tmdl = re.RegisterModel(buffer);\n\t\tif (!mdl) {\n\t\t\t// no, revert to the male model\n\t\t\tCom_sprintf (buffer, sizeof(buffer), \"players/%s/%s\", \"male\", base+1);\n\t\t\tmdl = re.RegisterModel(buffer);\n\t\t\tif (!mdl) {\n\t\t\t\t// last try, default male weapon.md2\n\t\t\t\tCom_sprintf (buffer, sizeof(buffer), \"players/male/weapon.md2\");\n\t\t\t\tmdl = re.RegisterModel(buffer);\n\t\t\t}\n\t\t} \n\t}\n\n\treturn mdl;\n}\n\n*/\n\n//returns a list of all the ents currently trying to play a sound.\nunsigned int CLQ2_GatherSounds(vec3_t *positions, unsigned int *entnums, sfx_t **sounds, unsigned int max)\n{\n\tentity_state_t\t\t*s1;\n\tsfx_t *sfx;\n\tunsigned int pnum;\n\tunsigned int count = 0;\n\tq2frame_t *frame = &cl.q2frame;\n\tfor (pnum = 0 ; pnum<frame->num_entities ; pnum++)\n\t{\n\t\ts1 = &clq2_parse_entities[(frame->parse_entities+pnum)&(MAX_PARSE_ENTITIES-1)];\n\t\tif (s1->u.q2.sound > 0 && s1->u.q2.sound < MAX_PRECACHE_SOUNDS)\n\t\t{\n\t\t\tsfx = cl.sound_precache[s1->u.q2.sound];\n\t\t\tif (sfx)\n\t\t\t{\n\t\t\t\tif (count == max)\n\t\t\t\t{\n\t\t\t\t\tCon_DPrintf(\"Exceeded limit of %d looped sounds\\n\", max);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\t//fixme: sexed sounds\n\t\t\t\tentnums[count] = s1->number;\n\t\t\t\tVectorCopy(s1->origin, positions[count]);\n\t\t\t\tsounds[count] = sfx;\n\t\t\t\tcount++;\n\t\t\t}\n\t\t}\n\t}\n\treturn count;\n}\n\n\nstatic struct q2ex_rtlight_s\n{\n\tqboolean dirty;\n\tint key, type, res, style;\n\tfloat radius, intensity, fade[2], cone, conedir[3];\n\tvec3_t axis[3];\n\tvec3_t angles;\n} q2ex_rtlight[Q2EXMAX_RTLIGHTS];\nvoid CLQ2EX_ParseLightConfigString(int i, const char *s)\n{\n\tstruct q2ex_rtlight_s *l = &q2ex_rtlight[i];\n\tl->dirty = true;\n\ts = COM_ParseToken(s, \";\");\tl->key\t\t\t= atoi(com_token);\ts = COM_ParseToken(s, \";\");\n\ts = COM_ParseToken(s, \";\");\tl->type\t\t\t= atoi(com_token);\ts = COM_ParseToken(s, \";\");\n\ts = COM_ParseToken(s, \";\");\tl->radius\t\t= atof(com_token);\ts = COM_ParseToken(s, \";\");\n\ts = COM_ParseToken(s, \";\");\tl->res\t\t\t= atoi(com_token);\ts = COM_ParseToken(s, \";\");\n\ts = COM_ParseToken(s, \";\");\tl->intensity\t= atof(com_token);\ts = COM_ParseToken(s, \";\");\n\ts = COM_ParseToken(s, \";\");\tl->fade[0]\t\t= atof(com_token);\ts = COM_ParseToken(s, \";\");\n\ts = COM_ParseToken(s, \";\");\tl->fade[1]\t\t= atof(com_token);\ts = COM_ParseToken(s, \";\");\n\ts = COM_ParseToken(s, \";\");\tl->style\t\t= atoi(com_token);\ts = COM_ParseToken(s, \";\");\n\ts = COM_ParseToken(s, \";\");\tl->cone\t\t\t= atof(com_token);\ts = COM_ParseToken(s, \";\");\n\ts = COM_ParseToken(s, \";\");\tl->axis[0][0]\t= atof(com_token);\ts = COM_ParseToken(s, \";\");\n\ts = COM_ParseToken(s, \";\");\tl->axis[0][1]\t= atof(com_token);\ts = COM_ParseToken(s, \";\");\n\ts = COM_ParseToken(s, \";\");\tl->axis[0][2]\t= atof(com_token);\ts = COM_ParseToken(s, \";\");\n\n\t//faff around with angles and stuff.\n\tVectorVectors(l->axis[0], l->axis[1], l->axis[2]);\n\tVectorNegate(l->axis[1], l->axis[1]);\n\tVectorAngles(l->axis[0], l->axis[2], l->angles, false);\n}\nstatic void CLQ2EX_UpdateRTLight(int key, vec3_t org)\n{\n\tint\t\t\ti;\n\tdlight_t\t*dl;\n\tstruct q2ex_rtlight_s *l;\n\n\tfor (i = 0, l = q2ex_rtlight; i < countof(q2ex_rtlight); i++, l++)\n\t{\n\t\tif (l->key == key)\n\t\t{\t//okay, we found the configstring entry...\n\n\t\t\t//see if its current or not...\n\t\t\tdl = cl_dlights+rtlights_first;\n\t\t\tfor (i=rtlights_first ; i<rtlights_max ; i++, dl++)\n\t\t\t{\n\t\t\t\tif (dl->key == key)\n\t\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tif (i==rtlights_max)\n\t\t\t{\n\t\t\t\tdl = CL_AllocSlight();\n\t\t\t\tdl->flags = LFLAG_NORMALMODE|LFLAG_REALTIMEMODE;\n\t\t\t\tdl->key = key;\n\t\t\t\tl->dirty = true;\n\t\t\t}\n\n\t\t\tif (!l->dirty && !(dl->flags&LFLAG_FORCECACHE))\n\t\t\t{\n\t\t\t\tif (!VectorCompare(org, dl->origin))\n\t\t\t\t{\t//it moved :( keep the light tracking the ent and invalidate the cache.\n\t\t\t\t\tVectorCopy(org, dl->origin);\n\t\t\t\t\tdl->rebuildcache = true;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\t//LOTS of stuff changed.\n\t\t\t\tl->dirty = false;\n\t\t\t\tdl->flags |= LFLAG_FORCECACHE;\n\n\t\t\t\t//ent info\n\t\t\t\tVectorCopy(org, dl->origin);\n\n\t\t\t\t//config string info\n\t\t\t\tdl->radius = l->radius;\n//FIXME: we're not using the suggested resolution.\n\t\t\t\tVectorSet(dl->color, l->intensity,l->intensity,l->intensity);\n\t\t\t\tdl->fade[0] = l->fade[0];\n\t\t\t\tdl->fade[1] = l->fade[1];\n\t\t\t\tdl->style = l->style;\n\n\t\t\t\tif (l->type)\n\t\t\t\t\tdl->fov = l->cone*2;\n\t\t\t\tVectorCopy(l->angles, dl->angles);\n\t\t\t\tVectorCopy(l->axis[0], dl->axis[0]);\n\t\t\t\tVectorCopy(l->axis[1], dl->axis[1]);\n\t\t\t\tVectorCopy(l->axis[2], dl->axis[2]);\n\n\t\t\t\tdl->corona = 0;\n\t\t\t\tdl->rebuildcache = true;\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t}\n}\n\n/*\n===============\nCL_AddPacketEntities\n\n===============\n*/\nvoid R_AddItemTimer(vec3_t shadoworg, float yaw, float radius, float percent, vec3_t rgb);\nstatic void CLQ2_AddPacketEntities (q2frame_t *frame)\n{\n\tentity_t\t\t\tent;\n\tentity_state_t\t\t*s1;\n\tfloat\t\t\t\tautorotate;\n\tint\t\t\t\t\ti;\n\tint\t\t\t\t\tpnum;\n\tq2centity_t\t\t\t*cent;\n\tint\t\t\t\t\tautoanim;\n//\tq2clientinfo_t\t\t*ci;\n\tplayer_info_t\t\t*player;\n\tunsigned int\t\teffects, renderfx;\n\tfloat back, fwds;\n\n\tfloat timestep = cl.gametime-cl.oldgametime;\n\tstruct itemtimer_s **timerlink, *timer;\n\n\t// bonus items rotate at a fixed rate\n\tautorotate = anglemod(cl.time*100);\n\n\t// brush models can auto animate their frames\n\tautoanim = 2*cl.time;\n\n\tmemset (&ent, 0, sizeof(ent));\n\n\tfor (timerlink = &cl.itemtimers; (timer=*timerlink); )\n\t{\n\t\tif (cl.time > timer->end)\n\t\t{\n\t\t\t*timerlink = timer->next;\n\t\t\tZ_Free(timer);\n\t\t}\n\t\telse\n\t\t{\n\t\t\ttimerlink = &(*timerlink)->next;\n\t\t\t/*if (timer->entnum)\n\t\t\t{\n\t\t\t\tif (timer->entnum >= cl.maxlerpents)\n\t\t\t\t\tcontinue;\n\t\t\t\tle = &cl.lerpents[timer->entnum];\n\t\t\t\tif (le->sequence != cl.lerpentssequence)\n\t\t\t\t\tcontinue;\n//\t\t\t\tVectorCopy(le->origin, timer->origin);\n\t\t\t}*/\n\t\t\tR_AddItemTimer(timer->origin, cl.time*90 + timer->origin[0] + timer->origin[1] + timer->origin[2], timer->radius, (cl.time - timer->start) / timer->duration, timer->rgb);\n\t\t}\n\t}\n\n\tfor (pnum = 0 ; pnum<frame->num_entities ; pnum++)\n\t{\n\t\ts1 = &clq2_parse_entities[(frame->parse_entities+pnum)&(MAX_PARSE_ENTITIES-1)];\n\n\t\tcent = &cl_entities[s1->number];\n\n\t\teffects = s1->effects;\n\t\trenderfx = s1->u.q2.renderfx;\n\n\t\tent.rtype = RT_MODEL;\n\t\tent.keynum = s1->number;\n\n\t\tent.scale = s1->scale/16.f;\n\t\tent.shaderRGBAf[0] = 1;\n\t\tent.shaderRGBAf[1] = 1;\n\t\tent.shaderRGBAf[2] = 1;\n\t\tent.shaderRGBAf[3] = 1;\n\t\tent.glowmod[0] = 1;\n\t\tent.glowmod[1] = 1;\n\t\tent.glowmod[2] = 1;\n\t\tent.fatness = 0;\n\t\tent.topcolour = 1;\n\t\tent.bottomcolour = 1;\n#ifdef HEXEN2\n\t\tent.h2playerclass = 0;\n#endif\n\t\tent.playerindex = -1;\n\t\tent.customskin = 0;\n\n\t\t// set frame\n\t\tif (effects & Q2EF_ANIM01)\n\t\t\tent.framestate.g[FS_REG].frame[0] = autoanim & 1;\n\t\telse if (effects & Q2EF_ANIM23)\n\t\t\tent.framestate.g[FS_REG].frame[0] = 2 + (autoanim & 1);\n\t\telse if (effects & Q2EF_ANIM_ALL)\n\t\t\tent.framestate.g[FS_REG].frame[0] = autoanim;\n\t\telse if (effects & Q2EF_ANIM_ALLFAST)\n\t\t\tent.framestate.g[FS_REG].frame[0] = cl.time / 100;\n\t\telse\n\t\t\tent.framestate.g[FS_REG].frame[0] = s1->frame;\n\n\t\t// quad and pent can do different things on client\n\t\tif (effects & Q2EF_PENT)\n\t\t{\n\t\t\teffects &= ~Q2EF_PENT;\n\t\t\teffects |= Q2EF_COLOR_SHELL;\n\t\t\trenderfx |= Q2RF_SHELL_RED;\n\t\t}\n\n\t\tif (effects & Q2EF_QUAD)\n\t\t{\n\t\t\teffects &= ~Q2EF_QUAD;\n\t\t\teffects |= Q2EF_COLOR_SHELL;\n\t\t\trenderfx |= Q2RF_SHELL_BLUE;\n\t\t}\n//======\n// PMM\n\t\tif (effects & Q2EF_DOUBLE)\n\t\t{\n\t\t\teffects &= ~Q2EF_DOUBLE;\n\t\t\teffects |= Q2EF_COLOR_SHELL;\n\t\t\trenderfx |= Q2RF_SHELL_DOUBLE;\n\t\t}\n\n\t\tif (effects & Q2EF_HALF_DAMAGE)\n\t\t{\n\t\t\teffects &= ~Q2EF_HALF_DAMAGE;\n\t\t\teffects |= Q2EF_COLOR_SHELL;\n\t\t\trenderfx |= Q2RF_SHELL_HALF_DAM;\n\t\t}\n// pmm\n//======\n\t\tent.framestate.g[FS_REG].frame[1] = cent->prev.frame;\n\t\tent.framestate.g[FS_REG].lerpweight[0] = cl.lerpfrac;\n\n\t\tif (renderfx & (Q2RF_FRAMELERP|Q2RF_BEAM))\n\t\t{\t// step origin discretely, because the frames\n\t\t\t// do the animation properly\n\t\t\tVectorCopy (cent->current.origin, ent.origin);\n\t\t\tVectorCopy (cent->current.u.q2.old_origin, ent.oldorigin);\n\t\t}\n\t\telse\n\t\t{\t// interpolate origin\n\t\t\tfor (i=0 ; i<3 ; i++)\n\t\t\t{\n\t\t\t\tent.origin[i] = ent.oldorigin[i] = cent->prev.origin[i] + cl.lerpfrac * \n\t\t\t\t\t(cent->current.origin[i] - cent->prev.origin[i]);\n\t\t\t}\n\t\t}\n\n\t\tif (ent.framestate.g[FS_REG].frame[0] != cent->oldframe)\n\t\t{\n\t\t\tent.framestate.g[FS_REG].frame[1] = cent->oldframe;\n\t\t\tent.framestate.g[FS_REG].lerpweight[0] = 10*(cl.time - cent->oldframetime);\t//anim rate is still 1ms... could guess at it, but urgh\n\n\t\t\tif (ent.framestate.g[FS_REG].lerpweight[0] >= 1)\n\t\t\t{\n\t\t\t\tent.framestate.g[FS_REG].lerpweight[0] = 1;\n\t\t\t\tcent->oldframe = ent.framestate.g[FS_REG].frame[0];\n\t\t\t}\n\t\t}\n\n\t\tent.framestate.g[FS_REG].lerpweight[1] = 1-ent.framestate.g[FS_REG].lerpweight[0];\n\n\t\tif (renderfx & Q2EXRF_FLARE)\n\t\t{\n/*This flag marks an entity as being rendered with a flare instead of the usual entity rendering. Flares overload some fields:\n* `s.renderfx & RF_SHELL_RED` causes the flare to have an outer red rim.\n* `s.renderfx & RF_SHELL_GREEN` causes the flare to have an outer green rim.\n* `s.renderfx & RF_SHELL_BLUE` causes the flare to have an outer blue rim.\n* `s.renderfx & RF_FLARE_LOCK_ANGLE` causes the flare to not rotate towards the viewer.\n* `s.renderfx & RF_CUSTOMSKIN` causes the flare to use the custom image index in `s.frame`.\n* `s.modelindex2` is the start distance of fading the flare out.\n* `s.modelindex3` is the end distance of fading the flare out.\n* `s.skinnum` is the RGBA of the flare.*/\n\t\t\tcontinue;\n\t\t}\n\t\tif (renderfx & Q2EXRF_CUSTOM_LIGHT)\n\t\t{\t//0xRRGGBBXX apparently\n\t\t\tV_AddLight (ent.keynum, ent.origin,\n\t\t\t\t\t\tent.framestate.g[FS_REG].frame[0]/*radius*/,\n\t\t\t\t\t\t((ent.skinnum >> 24) & 0xFF)/255.0/*r*/,\n\t\t\t\t\t\t((ent.skinnum >> 16) & 0xFF)/255.0/*g*/,\n\t\t\t\t\t\t((ent.skinnum >>  8) & 0xFF)/255.0/*b*/);\n\t\t\tcontinue;\t//probably modelindex 1. we don't want to actually draw that. but probably do want to lerp it.\n\t\t}\n\t\tif (renderfx & Q2REX_CASTSHADOW)\n\t\t{\n\t\t\tCLQ2EX_UpdateRTLight(ent.keynum, ent.origin);\n\t\t\tcontinue;\n\t\t}\n\n\t\t// create a new entity\n\t\n\t\t// tweak the color of beams\n\t\tif ( renderfx & Q2RF_BEAM )\n\t\t{\t// the four beam colors are encoded in 32 bits of skinnum (hack)\n\t\t\tent.skinnum = (s1->skinnum >> ((rand() % 4)*8)) & 0xff;\n\t\t\tent.shaderRGBAf[0] = ((d_8to24srgbtable[ent.skinnum & 0xFF] >>  0) & 0xFF)/255.0;\n\t\t\tent.shaderRGBAf[1] = ((d_8to24srgbtable[ent.skinnum & 0xFF] >>  8) & 0xFF)/255.0;\n\t\t\tent.shaderRGBAf[2] = ((d_8to24srgbtable[ent.skinnum & 0xFF] >> 16) & 0xFF)/255.0;\n\t\t\tent.shaderRGBAf[3] = 0.30;\n\t\t\tent.model = NULL;\n\t\t\tent.framestate.g[FS_REG].lerpweight[0] = 1;\n\t\t\tent.framestate.g[FS_REG].lerpweight[1] = 0;\n\t\t\tent.rtype = RT_BEAM;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// set skin\n\t\t\tif (s1->modelindex == 255)\n\t\t\t{\t// use custom player skin\n\t\t\t\tent.skinnum = 0;\n\n\t\t\t\tplayer = &cl.players[(s1->skinnum&0xff)%cl.allocated_client_slots];\n\t\t\t\tent.model = player->model;\n\t\t\t\tif (!ent.model || ent.model->loadstate != MLS_LOADED)\t//we need to do better than this\n\t\t\t\t{\n\t\t\t\t\tent.model = Mod_ForName(\"players/male/tris.md2\", MLV_SILENT);\n\t\t\t\t\tent.customskin = Mod_RegisterSkinFile(\"players/male/grunt.skin\");\n\t\t\t\t\tif (!ent.customskin)\n\t\t\t\t\t\tent.customskin = Mod_ReadSkinFile(\"players/male/grunt.skin\", \"replace \\\"\\\" \\\"players/male/grunt.pcx\\\"\");\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tent.customskin = player->skinid;\n\t\t\t\tent.playerindex = (s1->skinnum&0xff)%cl.allocated_client_slots;\n/*\t\t\t\tci = &cl.clientinfo[s1->skinnum & 0xff];\n//\t\t\t\tent.skin = ci->skin;\n\t\t\t\tent.model = ci->model;\n\t\t\t\tif (!ent.skin || !ent.model)\n\t\t\t\t{\n\t\t\t\t\tent.skin = cl.baseclientinfo.skin;\n\t\t\t\t\tent.model = cl.baseclientinfo.model;\n\t\t\t\t}\n\n//============\n//PGM\n\t\t\t\tif (renderfx & Q2RF_USE_DISGUISE)\n\t\t\t\t{\n\t\t\t\t\tif(!strncmp((char *)ent.skin, \"players/male\", 12))\n\t\t\t\t\t{\n\t\t\t\t\t\tent.skin = re.RegisterSkin (\"players/male/disguise.pcx\");\n\t\t\t\t\t\tent.model = re.RegisterModel (\"players/male/tris.md2\");\n\t\t\t\t\t}\n\t\t\t\t\telse if(!strncmp((char *)ent.skin, \"players/female\", 14))\n\t\t\t\t\t{\n\t\t\t\t\t\tent.skin = re.RegisterSkin (\"players/female/disguise.pcx\");\n\t\t\t\t\t\tent.model = re.RegisterModel (\"players/female/tris.md2\");\n\t\t\t\t\t}\n\t\t\t\t\telse if(!strncmp((char *)ent.skin, \"players/cyborg\", 14))\n\t\t\t\t\t{\n\t\t\t\t\t\tent.skin = re.RegisterSkin (\"players/cyborg/disguise.pcx\");\n\t\t\t\t\t\tent.model = re.RegisterModel (\"players/cyborg/tris.md2\");\n\t\t\t\t\t}\n\t\t\t\t}*/\n//PGM\n//============\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tent.skinnum = s1->skinnum;\n//\t\t\t\tent.skin = NULL;\n\t\t\t\tent.model = cl.model_precache[s1->modelindex];\n\t\t\t}\n\t\t}\n\n\t\t// only used for black hole model right now, FIXME: do better\n\t\tif (renderfx == RF_TRANSLUCENT)\n\t\t\tent.shaderRGBAf[3] = 0.70;\n\t\telse if (s1->trans != 255)\n\t\t{\n\t\t\trenderfx |= RF_TRANSLUCENT;\n\t\t\tent.shaderRGBAf[3] = s1->trans/255.0f;\n\t\t}\n\t\telse\n\t\t\tent.shaderRGBAf[3] = 1;\n\n\t\t// render effects (fullbright, translucent, etc)\n\t\tif ((effects & Q2EF_COLOR_SHELL))\n\t\t\tent.flags = 0;\t// renderfx go on color shell entity\n\t\telse\n\t\t\tent.flags = renderfx;\n\n\t\t// calculate angles\n\t\tif (effects & Q2EF_ROTATE)\n\t\t{\t// some bonus items auto-rotate\n\t\t\tent.angles[0] = 0;\n\t\t\tent.angles[1] = autorotate;\n\t\t\tent.angles[2] = 0;\n\t\t}\n\t\t// RAFAEL\n\t\telse if (effects & Q2EF_SPINNINGLIGHTS)\n\t\t{\n\t\t\tent.angles[0] = 0;\n\t\t\tent.angles[1] = anglemod(cl.time/2) + s1->angles[1];\n\t\t\tent.angles[2] = 180;\n\t\t\t{\n\t\t\t\tvec3_t forward;\n\t\t\t\tvec3_t start;\n\n\t\t\t\tAngleVectors (ent.angles, forward, NULL, NULL);\n\t\t\t\tVectorMA (ent.origin, 64, forward, start);\n\t\t\t\tV_AddLight (ent.keynum, start, 100, 0.2, 0, 0);\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\t// interpolate angles\n\t\t\tfloat\ta1, a2;\n\n\t\t\tfor (i=0 ; i<3 ; i++)\n\t\t\t{\n\t\t\t\ta1 = cent->current.angles[i];\n\t\t\t\ta2 = cent->prev.angles[i];\n\t\t\t\tent.angles[i] = LerpAngle (a2, a1, cl.lerpfrac);\n\t\t\t}\n\t\t}\n\n\t\tent.angles[0]*=r_meshpitch.value;\t//q2 has it fixed. consistency...\n\t\tent.angles[2]*=r_meshroll.value;\t//h2 doesn't. consistency...\n\n\t\tif (s1->number == cl.playerview[0].playernum+1)\t//woo! this is us!\n\t\t{\n//\t\t\tVectorCopy(cl.predicted_origin, ent.origin);\n//\t\t\tVectorCopy(cl.predicted_origin, ent.oldorigin);\n\t\t\tent.flags |= RF_EXTERNALMODEL;\t// only draw from mirrors\n\t\t\trenderfx |= RF_EXTERNALMODEL;\n\n\t\t\tif (effects & Q2EF_FLAG1)\n\t\t\t\tV_AddLight (ent.keynum, ent.origin, 225, 0.2, 0.05, 0.05);\n\t\t\telse if (effects & Q2EF_FLAG2)\n\t\t\t\tV_AddLight (ent.keynum, ent.origin, 225, 0.05, 0.05, 0.2);\n\t\t\telse if (effects & Q2EF_TAGTRAIL)\t\t\t\t\t\t//PGM\n\t\t\t\tV_AddLight (ent.keynum, ent.origin, 225, 0.2, 0.2, 0.0);\t//PGM\n\t\t\telse if (effects & Q2EF_TRACKERTRAIL)\t\t\t\t\t//PGM\n\t\t\t\tV_AddLight (ent.keynum, ent.origin, 225, -0.2, -0.2, -0.2);\t//PGM\n\t\t}\n\n\t\t// if set to invisible, skip\n\t\tif (!s1->modelindex)\n\t\t\tcontinue;\n\n\t\tif (effects & Q2EF_BFG)\n\t\t{\n\t\t\tent.flags |= RF_TRANSLUCENT;\n\t\t\tent.shaderRGBAf[3] = 0.30;\n\t\t}\n\n\t\t// RAFAEL\n\t\tif (effects & Q2EF_PLASMA)\n\t\t{\n\t\t\tent.flags |= RF_TRANSLUCENT;\n\t\t\tent.shaderRGBAf[3] = 0.6;\n\t\t}\n\n\t\tif (effects & Q2EF_SPHERETRANS)\n\t\t{\n\t\t\tent.flags |= RF_TRANSLUCENT;\n\t\t\t// PMM - *sigh*  yet more EF overloading\n\t\t\tif (effects & Q2EF_TRACKERTRAIL)\n\t\t\t\tent.shaderRGBAf[3] = 0.6;\n\t\t\telse\n\t\t\t\tent.shaderRGBAf[3] = 0.3;\n\t\t}\n//pmm\n\n\t\t/*lerp the ent now*/\n\t\tfwds = ent.framestate.g[FS_REG].lerpweight[0];\n\t\tback = ent.framestate.g[FS_REG].lerpweight[1];\n\t\tfor (i = 0; i < 3; i++)\n\t\t{\n\t\t\tent.origin[i] = ent.origin[i]*fwds + ent.oldorigin[i]*back;\n\t\t}\n\n\t\tif ((renderfx & Q2RF_IR_VISIBLE) && (r_refdef.flags & Q2RDF_IRGOGGLES))\n\t\t{\n\t\t\t//IR googles make ir visible ents visible in pure red.\n\t\t\tent.shaderRGBAf[0] = 1;\n\t\t\tent.shaderRGBAf[1] = 0;\n\t\t\tent.shaderRGBAf[2] = 0;\n\t\t\t//bypasses world lighting\n\t\t\tent.light_known = true;\n\t\t\tVectorSet(ent.light_avg, 1, 1, 1);\n\t\t\tVectorSet(ent.light_range, 0, 0, 0);\n\t\t\t//(yes, its a bit shit. not even a post-process thing)\n\t\t}\n\n\t\t// add to refresh list\n\t\tV_AddEntity (&ent);\n\t\tent.light_known = false;\n\t\tent.customskin = 0;\n\n\n\t\t// color shells generate a seperate entity for the main model\n\t\tif (effects & Q2EF_COLOR_SHELL)\n\t\t{\n\t\t\t// PMM - at this point, all of the shells have been handled\n\t\t\t// if we're in the rogue pack, set up the custom mixing, otherwise just\n\t\t\t// keep going\n\n\t\t\t// all of the solo colors are fine.  we need to catch any of the combinations that look bad\n\t\t\t// (double & half) and turn them into the appropriate color, and make double/quad something special\n\t\t\tif (renderfx & Q2RF_SHELL_HALF_DAM)\n\t\t\t{\n\t\t\t\t\n\t\t\t\t{\n\t\t\t\t\t// ditch the half damage shell if any of red, blue, or double are on\n\t\t\t\t\tif (renderfx & (Q2RF_SHELL_RED|Q2RF_SHELL_BLUE|Q2RF_SHELL_DOUBLE))\n\t\t\t\t\t\trenderfx &= ~Q2RF_SHELL_HALF_DAM;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (renderfx & Q2RF_SHELL_DOUBLE)\n\t\t\t{\n\n\t\t\t\t{\n\t\t\t\t\t// lose the yellow shell if we have a red, blue, or green shell\n\t\t\t\t\tif (renderfx & (Q2RF_SHELL_RED|Q2RF_SHELL_BLUE|Q2RF_SHELL_GREEN))\n\t\t\t\t\t\trenderfx &= ~Q2RF_SHELL_DOUBLE;\n\t\t\t\t\t// if we have a red shell, turn it to purple by adding blue\n\t\t\t\t\tif (renderfx & Q2RF_SHELL_RED)\n\t\t\t\t\t\trenderfx |= Q2RF_SHELL_BLUE;\n\t\t\t\t\t// if we have a blue shell (and not a red shell), turn it to cyan by adding green\n\t\t\t\t\telse if (renderfx & Q2RF_SHELL_BLUE)\n\t\t\t\t\t{\n\t\t\t\t\t\t// go to green if it's on already, otherwise do cyan (flash green)\n\t\t\t\t\t\tif (renderfx & Q2RF_SHELL_GREEN)\n\t\t\t\t\t\t\trenderfx &= ~Q2RF_SHELL_BLUE;\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\trenderfx |= Q2RF_SHELL_GREEN;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t// pmm\n\t\t\tent.flags = renderfx;\n\t\t\tent.shaderRGBAf[3] = 0.20;\n\t\t\tent.shaderRGBAf[0] = (!!(renderfx & Q2RF_SHELL_RED));\n\t\t\tent.shaderRGBAf[1] = (!!(renderfx & Q2RF_SHELL_GREEN));\n\t\t\tent.shaderRGBAf[2] = (!!(renderfx & Q2RF_SHELL_BLUE));\n\t\t\tent.forcedshader = R_RegisterCustom(NULL, \"q2/shell\", SUF_NONE, Shader_DefaultSkinShell, NULL);\n\t\t\tent.fatness = 2;\n\t\t\tV_AddEntity (&ent);\n\t\t\tent.fatness = 0;\n\t\t}\n\t\tent.forcedshader = NULL;\n\n//\t\tent.skin = NULL;\t\t// never use a custom skin on others\n\t\tent.skinnum = 0;\n\t\tent.flags &= RF_EXTERNALMODEL;\n\t\tent.shaderRGBAf[3] = 1;\n\n\t\t// duplicate for linked models\n\t\tif (s1->modelindex2)\n\t\t{\n\t\t\tif (s1->modelindex2 == 255)\n\t\t\t{\t// custom weapon\n\t\t\t\tchar *modelname;\n\t\t\t\tchar *skin;\n\t\t\t\tent.model=NULL;\n\n\t\t\t\tplayer = &cl.players[(s1->skinnum&0xff)%MAX_CLIENTS];\n\t\t\t\tmodelname = InfoBuf_ValueForKey(&player->userinfo, \"skin\");\n\t\t\t\tif (!modelname[0])\n\t\t\t\t\tmodelname = \"male\";\n\t\t\t\tskin = strchr(modelname, '/');\n\t\t\t\tif (skin) *skin = '\\0';\n\n\t\t\t\ti = (s1->skinnum >> 8); // 0 is default weapon model\n\t\t\t\tif (i < 0 || i >= cl.numq2visibleweapons)\n\t\t\t\t\ti = 0;\t//0 is always valid\n\t\t\t\tent.model = Mod_ForName(va(\"players/%s/%s\", modelname, cl.q2visibleweapons[i]), MLV_WARN);\n\t\t\t\tif (ent.model->loadstate == MLS_FAILED && i)\n\t\t\t\t{\n\t\t\t\t\ti = 0;\n\t\t\t\t\tent.model = Mod_ForName(va(\"players/%s/%s\", modelname, cl.q2visibleweapons[i]), MLV_WARN);\n\t\t\t\t}\n\t\t\t\tif (ent.model->loadstate == MLS_FAILED && strcmp(modelname, \"male\"))\n\t\t\t\t\tent.model = Mod_ForName(va(\"players/%s/%s\", \"male\", cl.q2visibleweapons[i]), MLV_WARN);\n\t\t\t}\n\t\t\telse\n\t\t\t\tent.model = cl.model_precache[s1->modelindex2];\n\n\t\t\t// PMM - check for the defender sphere shell .. make it translucent\n\t\t\t// replaces the previous version which used the high bit on modelindex2 to determine transparency\n/*\t\t\tif (!Q_strcasecmp (cl.model_name[(s1->modelindex2)], \"models/items/shell/tris.md2\"))\n\t\t\t{\n\t\t\t\tent.alpha = 0.32;\n\t\t\t\tent.flags |= Q2RF_TRANSLUCENT;\n\n\t\t\t\tV_AddEntity (&ent);\n\n\t\t\t\t// make sure these get reset.\n\t\t\t\tent.flags &= RF_EXTERNALMODEL;\n\t\t\t\tent.shaderRGBAf[3] = 1;\n\t\t\t}\n\t\t\telse\n*/\t\t\t// pmm\n\n\t\t\tV_AddEntity (&ent);\n\t\t}\n\t\tif (s1->u.q2.modelindex3)\n\t\t{\n\t\t\tent.model = cl.model_precache[s1->u.q2.modelindex3];\n\t\t\tV_AddEntity (&ent);\n\t\t}\n\t\tif (s1->u.q2.modelindex4)\n\t\t{\n\t\t\tent.model = cl.model_precache[s1->u.q2.modelindex4];\n\t\t\tV_AddEntity (&ent);\n\t\t}\n\n\t\tif ( effects & Q2EF_POWERSCREEN )\n\t\t{\n\t\t\tent.model = Mod_ForName(\"models/items/armor/effect/tris.md2\", MLV_WARN);\n\t\t\tent.framestate.g[FS_REG].frame[0] = 0;\n\t\t\tent.framestate.g[FS_REG].frame[0] = 0;\n\t\t\tent.flags |= (RF_TRANSLUCENT | Q2RF_SHELL_GREEN);\n\t\t\tent.shaderRGBAf[3] = 0.30;\n\t\t\tV_AddEntity (&ent);\n\t\t}\n\n\t\t// add automatic particle trails\n\t\tif ( (effects&~Q2EF_ROTATE) )\n\t\t{\n\t\t\tif (effects & Q2EF_ROCKET)\n\t\t\t{\n\t\t\t\t//FIXME: cubemap orientation\n\t\t\t\tif (P_ParticleTrail(cent->lerp_origin, ent.origin, pt_q2[Q2RT_ROCKET], timestep, ent.keynum, NULL, &cent->trailstate))\n\t\t\t\t\tif (P_ParticleTrail(cent->lerp_origin, ent.origin, rt_rocket, timestep, ent.keynum, NULL, &cent->trailstate))\n\t\t\t\t\t{\n\t\t\t\t\t\tP_ParticleTrailIndex(cent->lerp_origin, ent.origin, P_INVALID, timestep, 0xdc, 4, &cent->trailstate);\n\t\t\t\t\t\tV_AddLight (ent.keynum, ent.origin, 200, 0.2, 0.1, 0.05);\n\t\t\t\t\t}\n\t\t\t}\n\t\t\t// PGM - Do not reorder EF_BLASTER and EF_HYPERBLASTER. \n\t\t\t// EF_BLASTER | EF_TRACKER is a special case for EF_BLASTER2... Cheese!\n\t\t\telse if (effects & Q2EF_BLASTER)\n\t\t\t{\n//PGM\n\t\t\t\tif (effects & Q2EF_TRACKER)\t// lame... problematic?\n\t\t\t\t{\n\t\t\t\t\tif (P_ParticleTrail(cent->lerp_origin, ent.origin, pt_q2[Q2RT_BLASTERTRAIL2], timestep, ent.keynum, NULL, &cent->trailstate))\n\t\t\t\t\t{\n\t\t\t\t\t\tP_ParticleTrailIndex(cent->lerp_origin, ent.origin, P_INVALID, timestep, 0xd0, 1, &cent->trailstate);\n\t\t\t\t\t\tV_AddLight (ent.keynum, ent.origin, 200, 0, 0.2, 0);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tif (P_ParticleTrail(cent->lerp_origin, ent.origin, pt_q2[Q2RT_BLASTERTRAIL], timestep, ent.keynum, NULL, &cent->trailstate))\n\t\t\t\t\t{\n\t\t\t\t\t\tP_ParticleTrailIndex(cent->lerp_origin, ent.origin, P_INVALID, timestep, 0xe0, 1, &cent->trailstate);\n\t\t\t\t\t\tV_AddLight (ent.keynum, ent.origin, 200, 0.2, 0.2, 0);\n\t\t\t\t\t}\n\t\t\t\t}\n//PGM\n\t\t\t}\n\t\t\telse if (effects & Q2EF_HYPERBLASTER)\n\t\t\t{\n\t\t\t\tif (effects & Q2EF_TRACKER)\t\t\t\t\t\t// PGM\toverloaded for blaster2.\n\t\t\t\t\tV_AddLight (ent.keynum, ent.origin, 200, 0, 0.2, 0);\t\t// PGM\n\t\t\t\telse\t\t\t\t\t\t\t\t\t\t\t// PGM\n\t\t\t\t\tV_AddLight (ent.keynum, ent.origin, 200, 0.2, 0.2, 0);\n\t\t\t}\n\t\t\telse if (effects & Q2EF_GIB)\n\t\t\t{\n\t\t\t\tif (P_ParticleTrail(cent->lerp_origin, ent.origin, pt_q2[Q2RT_GIB], timestep, ent.keynum, NULL, &cent->trailstate))\n\t\t\t\t\tif (P_ParticleTrail(cent->lerp_origin, ent.origin, rt_blood, timestep, ent.keynum, NULL, &cent->trailstate))\n\t\t\t\t\t\tP_ParticleTrailIndex(cent->lerp_origin, ent.origin, P_INVALID, timestep, 0xe8, 8, &cent->trailstate);\n\t\t\t}\n\t\t\telse if (effects & Q2EF_GRENADE)\n\t\t\t{\n\t\t\t\tif (P_ParticleTrail(cent->lerp_origin, ent.origin, pt_q2[Q2RT_GRENADE], timestep, ent.keynum, NULL, &cent->trailstate))\n\t\t\t\t\tif (P_ParticleTrail(cent->lerp_origin, ent.origin, rt_grenade, timestep, ent.keynum, NULL, &cent->trailstate))\n\t\t\t\t\t\tP_ParticleTrailIndex(cent->lerp_origin, ent.origin, P_INVALID, timestep, 4, 8, &cent->trailstate);\n\t\t\t}\n\t\t\telse if (effects & Q2EF_FLIES)\n\t\t\t{\n\t\t\t\tCLQ2_FlyEffect (cent, ent.origin);\n\t\t\t}\n\t\t\telse if (effects & Q2EF_BFG)\n\t\t\t{\n\t\t\t\tstatic int bfg_lightramp[6] = {300, 400, 600, 300, 150, 75};\n\n\t\t\t\tif (effects & Q2EF_ANIM_ALLFAST)\n\t\t\t\t{\n\t\t\t\t\tCLQ2_BfgParticles (cent, ent.origin);\n\t\t\t\t\ti = 200;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\ti = bfg_lightramp[s1->frame];\n\t\t\t\t}\n\t\t\t\tV_AddLight (ent.keynum, ent.origin, i, 0, 0.2, 0);\n\t\t\t}\n\t\t\t// RAFAEL\n\t\t\telse if (effects & Q2EF_TRAP)\n\t\t\t{\n\t\t\t\tent.origin[2] += 32;\n\t\t\t\tP_ParticleTrail(cent->lerp_origin, ent.origin, pt_q2[Q2RT_TRAP], timestep, ent.keynum, NULL, &cent->trailstate);\n\t\t\t}\n\t\t\telse if (effects & Q2EF_FLAG1)\n\t\t\t{\n\t\t\t\tif (P_ParticleTrail(cent->lerp_origin, ent.origin, pt_q2[Q2RT_FLAG1], timestep, ent.keynum, NULL, &cent->trailstate))\n\t\t\t\t{\n\t\t\t\t\tP_ParticleTrailIndex(cent->lerp_origin, ent.origin, P_INVALID, timestep, 242, 1, &cent->trailstate);\n\t\t\t\t\tV_AddLight (ent.keynum, ent.origin, 225, 0.2, 0.05, 0.05);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (effects & Q2EF_FLAG2)\n\t\t\t{\n\t\t\t\tif (P_ParticleTrail(cent->lerp_origin, ent.origin, pt_q2[Q2RT_FLAG2], timestep, ent.keynum, NULL, &cent->trailstate))\n\t\t\t\t{\n\t\t\t\t\tP_ParticleTrailIndex(cent->lerp_origin, ent.origin, P_INVALID, timestep, 115, 1, &cent->trailstate);\n\t\t\t\t\tV_AddLight (ent.keynum, ent.origin, 225, 0.05, 0.05, 0.2);\n\t\t\t\t}\n\t\t\t}\n//======\n//ROGUE\n\t\t\telse if (effects & Q2EF_TAGTRAIL)\n\t\t\t{\n\t\t\t\tif (P_ParticleTrail(cent->lerp_origin, ent.origin, pt_q2[Q2RT_TAGTRAIL], timestep, ent.keynum, NULL, &cent->trailstate))\n\t\t\t\t{\n\t\t\t\t\tP_ParticleTrailIndex(cent->lerp_origin, ent.origin, P_INVALID, timestep, 220, 1, &cent->trailstate);\n\t\t\t\t\tV_AddLight (ent.keynum, ent.origin, 225, 0.2, 0.2, 0.0);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (effects & Q2EF_TRACKERTRAIL)\n\t\t\t{\n\t\t\t\tif (effects & Q2EF_TRACKER)\n\t\t\t\t{\n\t\t\t\t\tfloat intensity;\n\n\t\t\t\t\tintensity = 50 + (500 * (sin(cl.time/500.0) + 1.0));\n\n\t\t\t\t\t// FIXME - check out this effect in rendition\n\t\t\t\t\tV_AddLight (ent.keynum, ent.origin, intensity, -0.2, -0.2, -0.2);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tCLQ2_Tracker_Shell (cent, cent->lerp_origin);\n\t\t\t\t\tV_AddLight (ent.keynum, ent.origin, 155, -0.2, -0.2, -0.2);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (effects & Q2EF_TRACKER)\n\t\t\t{\n\t\t\t\tif (P_ParticleTrail(cent->lerp_origin, ent.origin, pt_q2[Q2RT_TRACKER], timestep, ent.keynum, NULL, &cent->trailstate))\n\t\t\t\t{\n\t\t\t\t\tP_ParticleTrailIndex(cent->lerp_origin, ent.origin, P_INVALID, timestep, 0, 1, &cent->trailstate);\n\t\t\t\t\tV_AddLight (ent.keynum, ent.origin, 200, -0.2, -0.2, -0.2);\n\t\t\t\t}\n\t\t\t}\n//ROGUE\n//======\n\t\t\t// RAFAEL\n\t\t\telse if (effects & Q2EF_GREENGIB)\n\t\t\t{\n\t\t\t\tif (P_ParticleTrail(cent->lerp_origin, ent.origin, pt_q2[Q2RT_GREENGIB], timestep, ent.keynum, NULL, &cent->trailstate))\n\t\t\t\t\tP_ParticleTrailIndex(cent->lerp_origin, ent.origin, P_INVALID, timestep, 219, 8, &cent->trailstate);\n\t\t\t}\n\t\t\t// RAFAEL\n\t\t\telse if (effects & Q2EF_IONRIPPER)\n\t\t\t{\n\t\t\t\tif (P_ParticleTrail(cent->lerp_origin, ent.origin, pt_q2[Q2RT_IONRIPPER], timestep, ent.keynum, NULL, &cent->trailstate))\n\t\t\t\t{\n\t\t\t\t\tP_ParticleTrailIndex(cent->lerp_origin, ent.origin, P_INVALID, timestep, 228, 4, &cent->trailstate);\n\t\t\t\t\tV_AddLight (ent.keynum, ent.origin, 100, 0.2, 0.1, 0.1);\n\t\t\t\t}\n\t\t\t}\n\t\t\t// RAFAEL\n\t\t\telse if (effects & Q2EF_BLUEHYPERBLASTER)\n\t\t\t{\n\t\t\t\tV_AddLight (ent.keynum, ent.origin, 200, 0, 0, 0.2);\n\t\t\t}\n\t\t\t// RAFAEL\n\t\t\telse if (effects & Q2EF_PLASMA)\n\t\t\t{\n\t\t\t\tif (effects & Q2EF_ANIM_ALLFAST)\n\t\t\t\t{\n\t\t\t\t\tP_ParticleTrail(cent->lerp_origin, ent.origin, pt_q2[Q2RT_PLASMA], timestep, ent.keynum, NULL, &cent->trailstate);\n\t\t\t\t}\n\t\t\t\tV_AddLight (ent.keynum, ent.origin, 130, 0.2, 0.1, 0.1);\n\t\t\t}\n\t\t}\n\n\t\tVectorCopy (ent.origin, cent->lerp_origin);\n\t}\n}\n\n\n\n/*\n==============\nCL_AddViewWeapon\n==============\n*/\nstatic void CLQ2_AddViewWeapon (int seat, q2player_state_t *ps, q2player_state_t *ops)\n{\n\tplayerview_t *pv = &cl.playerview[seat];\n\tstruct model_s *om = pv->vm.oldmodel;\n\n\tpv->vm.oldmodel = NULL;\n\n\t// allow the gun to be completely removed\n\tif (!r_drawviewmodel.value)\n\t\treturn;\n\n\tif (!Cam_DrawViewModel(pv))\n\t\treturn;\n\n\t// don't draw gun if in wide angle view\n\tif (ps->fov > 90 && !*scr_fov_viewmodel.string)\n\t\treturn;\n\n\t//generate root matrix..\n\tVectorCopy(pv->simorg, r_refdef.weaponmatrix[3]);\n\tAngleVectors(pv->simangles, r_refdef.weaponmatrix[0], r_refdef.weaponmatrix[1], r_refdef.weaponmatrix[2]);\n\tVectorInverse(r_refdef.weaponmatrix[1]);\n\tmemcpy(r_refdef.weaponmatrix_bob, r_refdef.weaponmatrix, sizeof(r_refdef.weaponmatrix_bob));\n\n\tif (om != cl.model_precache[ps->gunindex])\n\t{\n\t\tpv->vm.prevframe = pv->vm.oldframe = ps->gunframe;\n\t\tpv->vm.lerptime = pv->vm.oldlerptime = cl.oldgametime;\n\t}\n\tpv->vm.oldmodel = cl.model_precache[ps->gunindex];\n\tif (!pv->vm.oldmodel)\n\t\treturn;\n\n\tif (ps->gunframe != pv->vm.prevframe)\n\t{\n\t\tpv->vm.oldframe = pv->vm.prevframe;\n\t\tpv->vm.oldlerptime = pv->vm.lerptime;\n\n\t\tpv->vm.prevframe = ps->gunframe;\n\t\tpv->vm.lerptime = cl.time;\n\t}\n}\n\n\n/*\n===============\nCL_CalcViewValues\n\nSets r_refdef view values\n===============\n*/\nvoid CLQ2_CalcViewValues (int seat)\n{\n\tint\t\t\ti;\n\tfloat\t\tlerp, backlerp;\n\tq2frame_t\t\t*oldframe;\n\tq2player_state_t\t*ps, *ops;\n\tplayerview_t *pv = &cl.playerview[seat];\n\n\tr_refdef.areabitsknown = true;\n\tmemcpy(r_refdef.areabits, cl.q2frame.seat[seat].areabits, sizeof(r_refdef.areabits));\n\n\tr_refdef.useperspective = true;\n\tr_refdef.mindist = bound(0.1, gl_mindist.value, 4);\n\tr_refdef.maxdist = gl_maxdist.value;\n\n\t// find the previous frame to interpolate from\n\tps = &cl.q2frame.seat[seat].playerstate;\n\ti = (cl.q2frame.serverframe - 1) & Q2UPDATE_MASK;\n\toldframe = &cl.q2frames[i];\n\tif (oldframe->serverframe != cl.q2frame.serverframe-1 || !oldframe->valid)\n\t\toldframe = &cl.q2frame;\t\t// previous frame was dropped or involid\n\tops = &oldframe->seat[seat].playerstate;\n\n\t// see if the player entity was teleported this frame\n\tif ( abs(ops->pmove.origin[0] - ps->pmove.origin[0]) > 256*8\n\t\t|| abs(ops->pmove.origin[1] - ps->pmove.origin[1]) > 256*8\n\t\t|| abs(ops->pmove.origin[2] - ps->pmove.origin[2]) > 256*8)\n\t\tops = ps;\t\t// don't interpolate\n\n\tlerp = cl.lerpfrac;\n\n\t// calculate the origin\n\tif (cl.worldmodel && (!cl_nopred.value) && !(cl.q2frame.seat[seat].playerstate.pmove.pm_flags & Q2PMF_NO_PREDICTION) && !cls.demoplayback)\n\t{\t// use predicted values\n\t\tfloat\tdelta;\n\n\t\tbacklerp = 1.0 - lerp;\n\t\tfor (i=0 ; i<3 ; i++)\n\t\t{\n\t\t\tpv->simorg[i] = pv->predicted_origin[i] + ops->viewoffset[i] \n\t\t\t\t+ lerp * (ps->viewoffset[i] - ops->viewoffset[i])\n\t\t\t\t- backlerp * pv->prediction_error[i];\n\t\t}\n\n\t\t// smooth out stair climbing\n\t\tdelta = realtime - pv->predicted_step_time;\n\t\tif (delta < 0.1)\n\t\t\tpv->simorg[2] -= pv->predicted_step * (0.1 - delta)*10;\n\t}\n\telse\n\t{\t// just use interpolated values\n\t\tfor (i=0 ; i<3 ; i++)\n\t\t\tpv->simorg[i] =  ops->pmove.origin[i]*0.125 + ops->viewoffset[i]\n\t\t\t\t+ lerp *\t( ps->pmove.origin[i]*0.125 +  ps->viewoffset[i]\n\t\t\t\t-\t\t\t(ops->pmove.origin[i]*0.125 + ops->viewoffset[i]) );\n\t}\n\n\t// if not running a demo or on a locked frame, add the local angle movement\n\tif (cl.worldmodel && ps->pmove.pm_type < Q2PM_DEAD && !cls.demoplayback)\n\t{\t// use predicted values\n\t\tfor (i=0 ; i<3 ; i++)\n\t\t\tpv->simangles[i] = pv->predicted_angles[i];\n\t}\n\telse\n\t{\t// just use interpolated values\n\t\tfor (i=0 ; i<3 ; i++)\n\t\t\tpv->simangles[i] = LerpAngle (ops->viewangles[i], ps->viewangles[i], lerp);\n\t}\n\n\tfor (i=0 ; i<3 ; i++)\n\t\tpv->simangles[i] += v_gunkick_q2.value * LerpAngle (ops->kick_angles[i], ps->kick_angles[i], lerp);\n\n//\tVectorCopy(r_refdef.viewangles, cl.viewangles);\n\n//\tAngleVectors (r_refdef.viewangles, v_forward, v_right, v_up);\n\n\t// interpolate field of view\n\tpv->forcefov = ops->fov + lerp * (ps->fov - ops->fov);\n\tpv->handedness = atoi(InfoBuf_ValueForKey(&cls.userinfo[seat], \"hand\"));\n\n\t//do interpolate blend alpha, but only if the rgb didn't change\n\t// don't interpolate blend color\n\tfor (i=0 ; i<3 ; i++)\n\t\tpv->screentint[i] = ps->blend[i];\n\tif (ps->blend[0] == ops->blend[0] && ps->blend[1] == ops->blend[1] && ps->blend[2] == ops->blend[2] && (!ps->blend[3]) == (!ops->blend[3]))\n\t\tpv->screentint[3] = (ops->blend[3] + lerp * (ps->blend[3]-ops->blend[3]))*gl_cshiftenabled.value;\n\telse\n\t\tpv->screentint[3] = ps->blend[3]*gl_cshiftenabled.value;\n\n\t// add the weapon\n\tCLQ2_AddViewWeapon (seat, ps, ops);\n}\n\n/*\n===============\nCL_AddEntities\n\nEmits all entities, particles, and lights to the refresh\n===============\n*/\nvoid CLQ2_AddEntities (void)\n{\n\tint seat;\n\tif (cls.state != ca_active)\n\t\treturn;\n\n\tcl.lerpfrac = 1.0 - (cl.q2frame.servertime/1000.0 - cl.time) * cl.q2svnetrate;\n//\tCon_Printf(\"%g: %g\\n\", cl.q2frame.servertime - (cl.time*1000), cl.lerpfrac);\n\tcl.lerpfrac = bound(0, cl.lerpfrac, 1);\n\n\tfor (seat = 0; seat < cl.splitclients; seat++)\n\t\tCLQ2_CalcViewValues (seat);\n\tCLQ2_AddPacketEntities (&cl.q2frame);\n#if 0\n\tCLQ2_AddProjectiles ();\n#endif\n\tCL_UpdateTEnts ();\n\n#ifdef _DEBUG\n\tif (chase_active.ival)\n\t{\n\t\tplayerview_t *pv = &cl.playerview[0];\n\t\tvec3_t axis[3];\n\t\tvec3_t camorg;\n//\t\ttrace_t tr;\n\t\tAngleVectors(r_refdef.viewangles, axis[0], axis[1], axis[2]);\n\t\tVectorMA(r_refdef.vieworg, -chase_back.value, axis[0], camorg);\n\t\tVectorMA(camorg, -chase_up.value, pv->gravitydir, camorg);\n//\t\tif (cl.worldmodel && cl.worldmodel->funcs.NativeTrace(cl.worldmodel, 0, NULLFRAMESTATE, NULL, r_refdef.vieworg, camorg, vec3_origin, vec3_origin, true, MASK_WORLDSOLID, &tr))\n\t\tVectorCopy(camorg, r_refdef.vieworg);\n\n\t\tV_EditExternalModels(0, NULL, 0);\n\t}\n#endif\n\n#ifdef RTLIGHTS\n\tR_EditLights_DrawLights();\n#endif\n}\n\n#endif\n"
  },
  {
    "path": "engine/client/console.c",
    "content": "/*\nCopyright (C) 1996-1997 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n*/\n// console.c\n\n#include \"quakedef.h\"\n#include \"shader.h\"\n\nconsole_t\t*con_head;\t\t\t// first console in the list\nconsole_t\t*con_curwindow;\t\t// the (window) console that's currently got focus.\nconsole_t\t*con_current;\t\t// points to whatever is the active console (the one that has focus ONLY when kdm_console)\nconsole_t\t*con_mouseover;\t\t// points to whichever console's title is currently mouseovered, or null\n\nconsole_t\t*con_main;\t\t\t// the default console that text will be thrown at. recreated as needed.\nconsole_t\t*con_chat;\t\t\t// points to a chat console\n\n#define Font_ScreenWidth() (vid.pixelwidth)\n\nstatic int Con_DrawProgress(int left, int right, int y);\nstatic int Con_DrawConsoleLines(console_t *con, conline_t *l, float displayscroll, int sx, int ex, int y, int top, int selactive, int selsx, int selex, int selsy, int seley, float lineagelimit);\n\n#ifdef QTERM\n#include <windows.h>\ntypedef struct qterm_s {\n\tconsole_t *console;\n\tqboolean running;\n\tHANDLE process;\n\tHANDLE pipein;\n\tHANDLE pipeout;\n\n\tHANDLE pipeinih;\n\tHANDLE pipeoutih;\n\n\tstruct qterm_s *next;\n} qterm_t;\n\nqterm_t *qterms;\nqterm_t *activeqterm;\n#endif\n\n//int \t\tcon_linewidth;\t// characters across screen\n//int\t\t\tcon_totallines;\t\t// total lines in console scrollback\n\nstatic float\t\tcon_cursorspeed = 4;\n\n\nstatic cvar_t\t\tcon_numnotifylines = CVAR(\"con_notifylines\",\"4\");\t\t//max lines to show\nstatic cvar_t\t\tcon_notifytime = CVAR(\"con_notifytime\",\"3\");\t\t//seconds\nstatic cvar_t\t\tcon_notify_x = CVAR(\"con_notify_x\",\"0\");\nstatic cvar_t\t\tcon_notify_y = CVAR(\"con_notify_y\",\"0\");\nstatic cvar_t\t\tcon_notify_w = CVAR(\"con_notify_w\",\"1\");\nstatic cvar_t\t\tcon_centernotify = CVAR(\"con_centernotify\", \"0\");\nstatic cvar_t\t\tcon_displaypossibilities = CVAR(\"con_displaypossibilities\", \"1\");\nstatic cvar_t\t\tcon_showcompletion = CVAR(\"con_showcompletion\", \"1\");\nstatic cvar_t\t\tcon_maxlines = CVAR(\"con_maxlines\", \"1024\");\ncvar_t\t\t\t\tcl_chatmode = CVARD(\"cl_chatmode\", \"2\", \"0(nq) - everything is assumed to be a console command. prefix with 'say', or just use a messagemode bind\\n1(q3) - everything is assumed to be chat, unless its prefixed with a /\\n2(qw) - anything explicitly recognised as a command will be used as a command, anything unrecognised will be a chat message.\\n/ prefix is supported in all cases.\\nctrl held when pressing enter always makes any implicit chat into team chat instead.\");\nstatic cvar_t\t\tcon_numnotifylines_chat = CVAR(\"con_numnotifylines_chat\", \"8\");\nstatic cvar_t\t\tcon_notifytime_chat = CVAR(\"con_notifytime_chat\", \"8\");\ncvar_t\t\t\t\tcon_separatechat = CVAR(\"con_separatechat\", \"0\");\nstatic cvar_t\t\tcon_timestamps = CVAR(\"con_timestamps\", \"0\");\nstatic cvar_t\t\tcon_timeformat = CVAR(\"con_timeformat\", \"(%H:%M:%S) \");\ncvar_t\t\t\t\tcon_textsize = CVARD(\"con_textsize\", \"8\", \"Resize the console text to be a different height, scaled separately from the hud. The value is the height in (virtual) pixels.\");\nstatic cvar_t\t\tcon_savehistory = CVARD(\"con_savehistory\", \"1\", \"Write/update conhistory.txt\");\nextern cvar_t log_developer;\n\nvoid con_window_cb(cvar_t *var, char *oldval)\n{\n\tif (!con_main)\n\t\treturn;\t//doesn't matter right now.\n\n\tif (var->ival)\n\t{\n\t\tcon_main->flags &= ~CONF_NOTIFY;\n\t\tif (!(con_main->flags & CONF_ISWINDOW))\n\t\t{\n\t\t\tcon_main->flags |= CONF_ISWINDOW;\n\t\t\tif (con_current == con_main)\n\t\t\t\tCon_SetActive(con_main);\n\t\t}\n\t}\n\telse\n\t{\n\t\tcon_main->flags |= CONF_NOTIFY;\n\t\tif (con_main->flags & CONF_ISWINDOW)\n\t\t{\n\t\t\tcon_main->flags &= ~CONF_ISWINDOW;\n\t\t\tif (con_curwindow == con_main)\n\t\t\t\tCon_SetActive(con_main);\n\t\t}\n\t}\n}\nstatic cvar_t con_window = CVARCD(\"con_window\", \"0\", con_window_cb, \"States whether the console should be a floating window as in source engine games, or a top-of-the-screen-only thing.\");\n\n#define\tNUM_CON_TIMES 24\n\nqboolean\tcon_initialized;\n\n/*makes sure the console object works*/\nvoid Con_Finit (console_t *con)\n{\n\tif (con->current == NULL)\n\t{\n\t\tcon->oldest = con->current = Z_Malloc(sizeof(conline_t));\n\t\tcon->linecount = 0;\n\t}\n\tif (con->display == NULL)\n\t\tcon->display = con->current;\n\n\tcon->selstartline = NULL;\n\tcon->selendline = NULL;\n\n\tcon->defaultcharbits = CON_WHITEMASK;\n\tcon->parseflags = 0;\n}\n\n/*returns a bitmask:\n1: currently active\n2: has text that has not been seen yet\n*/\nint Con_IsActive (console_t *con)\n{\n\treturn (con == con_current) | (con->unseentext*2);\n}\n/*kills a console_t object. will never destroy the main console (which will only be cleared)*/\nvoid Con_Destroy (console_t *con)\n{\n\tshader_t *shader;\n\tconsole_t **link;\n\tconline_t *t;\n\n\tif (con->close)\n\t{\n\t\tcon->close(con, true);\n\t\tcon->close = NULL;\n\t}\n\n\t/*purge the lines from the console*/\n\twhile (con->current)\n\t{\n\t\tt = con->current;\n\t\tcon->current = t->older;\n\t\tZ_Free(t);\n\t}\n\tcon->display = con->current = con->oldest = NULL;\n\n\tCon_Footerf(con, false, \"\");\n\tif (con->completionline)\n\t\tZ_Free(con->completionline);\n\tcon->completionline = NULL;\n\n\tfor (link = &con_head; *link; link = &(*link)->next)\n\t{\n\t\tif (*link == con)\n\t\t{\n\t\t\t(*link) = con->next;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tshader = con->backshader;\n\n\tBZ_Free(con);\n\n\t//make sure any special references are fixed up now that its gone\n\tif (con_mouseover == con)\n\t\tcon_mouseover = NULL;\n\tif (con_current == con)\n\t\tcon_current = con_head;\n\n\tif (con_curwindow == con)\n\t{\n\t\tfor (con_curwindow = con_head; con_curwindow; con_curwindow = con_curwindow->next)\n\t\t{\n\t\t\tif (con_curwindow->flags & CONF_ISWINDOW)\n\t\t\t\tbreak;\n\t\t}\n\t\tif (!con_curwindow)\n\t\t\tKey_Dest_Remove(kdm_cwindows);\n\t}\n\tcon_mouseover = NULL;\n\n\tif (shader)\n\t\tR_UnloadShader(shader);\n}\n\n/*just purges the background images for various consoles on restart/shutdown*/\nvoid Con_FlushBackgrounds(void)\n{\n\tconsole_t *con;\n\t//fixme: we really need to handle videomaps differently here, for vid_restarts.\n\tfor (con = con_head; con; con = con->next)\n\t{\n\t\tif (con->backshader)\n\t\t\tR_UnloadShader(con->backshader);\n\t\tcon->backshader = NULL;\n\t}\n}\n\n/*obtains a console_t without creating*/\nconsole_t *Con_FindConsole(const char *name)\n{\n\tconsole_t *con;\n\tif (!strcmp(name, \"current\") && con_current)\n\t\treturn con_current;\n\tif (!strcmp(name, \"head\") && con_current)\n\t\treturn con_head;\n\tfor (con = con_head; con; con = con->next)\n\t{\n\t\tif (!strcmp(con->name, name))\n\t\t\treturn con;\n\t}\n\treturn NULL;\n}\n/*creates a potentially duplicate console_t - please use Con_FindConsole first, as its confusing otherwise*/\nconsole_t *Con_Create(const char *name, unsigned int flags)\n{\n\tconsole_t *con, *p;\n\tif (!name)\n\t{\n\t\tstatic unsigned long seq;\n\t\tname = va(\"c%lu\", seq++);\n\t}\n\tif (!strcmp(name, \"current\"))\n\t\treturn NULL;\n\tif (!strcmp(name, \"head\"))\n\t\treturn NULL;\n\tcon = Z_Malloc(sizeof(console_t));\n\tQ_strncpyz(con->name, name, sizeof(con->name));\n\tQ_strncpyz(con->title, name, sizeof(con->title));\n\tQ_strncpyz(con->prompt, \"]\", sizeof(con->prompt));\n\n\tcon->flags = flags;\n\tCon_Finit(con);\n\n\t//insert at end. make it active if you must.\n\tif (!con_head)\n\t\tcon_head = con;\n\telse\n\t{\n\t\tfor (p = con_head; p->next; p = p->next)\n\t\t\t;\n\t\tp->next = con;\n\t}\n\n\treturn con;\n}\n\nstatic qboolean Con_Main_BlockClose(console_t *con, qboolean force)\n{\n\tif (!force)\n\t{\t//trying to close it just hides it (this is to avoid it getting cleared).\n\t\tif (con_curwindow == con)\n\t\t\tKey_Dest_Remove(kdm_cwindows);\n\t\treturn false;\n\t}\n\tcon_main = NULL;\t//its forced to die. and don't forget it.\n\treturn true;\n}\nconsole_t *Con_GetMain(void)\n{\n\tif (!con_main)\n\t{\n\t\tcon_main = Con_Create(\"\", 0);\n\n\t\tcon_main->linebuffered = Con_ExecuteLine;\n\t\tcon_main->commandcompletion = true;\n\t\tcon_main->wnd_w = 640;\n\t\tcon_main->wnd_h = 480;\n\t\tcon_main->wnd_x = 0;\n\t\tcon_main->wnd_y = 0;\n\t\tcon_main->close = Con_Main_BlockClose;\n\t\tQ_strncpyz(con_main->title, \"MAIN\", sizeof(con_main->title));\n\t\tQ_strncpyz(con_main->prompt, \"]\", sizeof(con_main->prompt));\n\n\t\tCvar_ForceCallback(&con_window);\n\t}\n\treturn con_main;\n}\n/*sets a console as the active one*/\nvoid Con_SetActive (console_t *con)\n{\n\tif (con->flags & CONF_ISWINDOW)\n\t{\n\t\tconsole_t *prev;\n\t\tKey_Dest_Add(kdm_cwindows);\n\t\tKey_Dest_Remove(kdm_console);\n\n\t\tif (con_curwindow == con)\n\t\t\treturn;\n\n\t\tfor (prev = con_head; prev; prev = prev->next)\n\t\t{\n\t\t\tif (prev->next == con)\n\t\t\t{\n\t\t\t\tprev->next = con->next;\n\t\t\t\twhile(prev->next)\n\t\t\t\t{\n\t\t\t\t\tprev = prev->next;\n\t\t\t\t}\n\t\t\t\tprev->next = con;\n\t\t\t\tcon->next = NULL;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tcon_curwindow = con;\n\t}\n\telse\n\t{\n\t\tif (con_curwindow == con)\n\t\t\tcon_curwindow = NULL;\n\t\tKey_Dest_Add(kdm_console);\n\t\tKey_Dest_Remove(kdm_cwindows);\n\t\tcon_current = con;\n\t}\n\n\tCon_Footerf(con, false, \"\");\n\tcon->buttonsdown = CB_NONE;\n}\n/*for enumerating consoles*/\nqboolean Con_NameForNum(int num, char *buffer, int buffersize)\n{\n\tconsole_t *con;\n\tfor (con = con_head; con; con = con->next, num--)\n\t{\n\t\tif (num <= 0)\n\t\t{\n\t\t\tQ_strncpyz(buffer, con->name, buffersize);\n\t\t\treturn true;\n\t\t}\n\t}\n\tif (buffersize>0)\n\t\t*buffer = '\\0';\n\treturn false;\n}\n\n#ifdef QTERM\nvoid QT_Kill(qterm_t *qt, qboolean killconsole)\n{\n\tqterm_t **link;\n\tqt->console->close = NULL;\n\tqt->console->userdata = NULL;\n\tqt->console->redirect = NULL;\n\tif (killconsole)\n\t\tCon_Destroy(qt->console);\n\n\t//yes this loop will crash if you're not careful. it makes it easier to debug.\n\tfor (link = &qterms; ; link = &(*link)->next)\n\t{\n\t\tif (*link == qt)\n\t\t{\n\t\t\t*link = qt->next;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tCloseHandle(qt->pipein);\n\tCloseHandle(qt->pipeout);\n\n\tCloseHandle(qt->pipeinih);\n\tCloseHandle(qt->pipeoutih);\n\tCloseHandle(qt->process);\n\n\tZ_Free(qt);\n}\nvoid QT_Update(void)\n{\n\tchar buffer[2048];\n\tDWORD ret;\n\tqterm_t *qt, *n;\n\tfor (qt = qterms; qt; )\n\t{\n\t\tif (qt->running)\n\t\t{\n\t\t\tif (WaitForSingleObject(qt->process, 0) == WAIT_TIMEOUT)\n\t\t\t{\n\t\t\t\tif ((ret=GetFileSize(qt->pipeout, NULL)))\n\t\t\t\t{\n\t\t\t\t\tif (ret!=INVALID_FILE_SIZE)\n\t\t\t\t\t{\n\t\t\t\t\t\tReadFile(qt->pipeout, buffer, sizeof(buffer)-32, &ret, NULL);\n\t\t\t\t\t\tbuffer[ret] = '\\0';\n\t\t\t\t\t\tCon_PrintCon(qt->console, buffer, PFS_NOMARKUP);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tCon_PrintCon(qt->console, \"Process ended\\n\", PFS_NOMARKUP);\n\t\t\t\tqt->running = false;\n\t\t\t}\n\t\t}\n\n\t\tn = qt->next;\n\t\tif (!qt->running)\n\t\t{\n\t\t\tif (!Con_IsActive(qt->console))\n\t\t\t\tQT_Kill(qt, true);\n\t\t}\n\t\tqt = n;\n\t}\n}\n\nqboolean QT_KeyPress(console_t *con, unsigned int unicode, int key)\n{\n\tqbyte k[2];\n\tqterm_t *qt = con->userdata;\n\tDWORD send = key;\t//get around a gcc warning\n\n\n\tk[0] = key;\n\tk[1] = '\\0';\n\n\tif (qt->running)\n\t{\n\t\tif (*k == '\\r')\n\t\t{\n//\t\t\t\t\t*k = '\\r';\n//\t\t\t\t\tWriteFile(qt->pipein, k, 1, &key, NULL);\n//\t\t\t\t\tCon_PrintCon(k, &qt->console, PFS_NOMARKUP);\n\t\t\t*k = '\\n';\n\t\t}\n//\t\tif (GetFileSize(qt->pipein, NULL)<512)\n\t\t{\n\t\t\tWriteFile(qt->pipein, k, 1, &send, NULL);\n\t\t\tCon_PrintCon(qt->console, k, PFS_NOMARKUP);\n\t\t}\n\t}\n\treturn true;\n}\n\nqboolean\tQT_Close(struct console_s *con, qboolean force)\n{\n\tqterm_t *qt = con->userdata;\n\tQT_Kill(qt, false);\n\n\treturn true;\n}\n\nvoid QT_Create(char *command)\n{\n\tHANDLE StdIn[2];\n\tHANDLE StdOut[2];\n\tqterm_t *qt;\n\tSECURITY_ATTRIBUTES sa;\n\tSTARTUPINFO SUInf;\n\tPROCESS_INFORMATION ProcInfo;\n\n\tint ret;\n\n\tqt = Z_Malloc(sizeof(*qt));\n\n\tmemset(&sa,0,sizeof(sa));\n\tsa.nLength=sizeof(sa);\n\tsa.bInheritHandle=true;\n\n\tCreatePipe(&StdOut[0], &StdOut[1], &sa, 1024);\n\tCreatePipe(&StdIn[1], &StdIn[0], &sa, 1024);\n\n\tmemset(&SUInf, 0, sizeof(SUInf));\n\tSUInf.cb = sizeof(SUInf);\n\tSUInf.dwFlags = STARTF_USESTDHANDLES;\n/*\n\tqt->pipeout\t\t= StdOut[0];\n\tqt->pipein\t\t= StdIn[0];\n*/\n\tqt->pipeoutih\t= StdOut[1];\n\tqt->pipeinih\t= StdIn[1];\n\n\tif (!DuplicateHandle(GetCurrentProcess(), StdIn[0],\n\t\t\t\t\t\tGetCurrentProcess(), &qt->pipein, 0,\n\t\t\t\t\t\tFALSE,                  // not inherited\n\t\t\t\t\t\tDUPLICATE_SAME_ACCESS))\n\t\tqt->pipein = StdIn[0];\n\telse\n\t\tCloseHandle(StdIn[0]);\n\tif (!DuplicateHandle(GetCurrentProcess(), StdOut[0],\n\t\t\t\t\t\tGetCurrentProcess(), &qt->pipeout, 0,\n\t\t\t\t\t\tFALSE,                  // not inherited\n\t\t\t\t\t\tDUPLICATE_SAME_ACCESS))\n\t\tqt->pipeout = StdOut[0];\n\telse\n\t\tCloseHandle(StdOut[0]);\n\n\tSUInf.hStdInput\t\t= qt->pipeinih;\n\tSUInf.hStdOutput\t= qt->pipeoutih;\n\tSUInf.hStdError\t\t= qt->pipeoutih;\t//we don't want to have to bother working out which one was written to first.\n\n\tif (!SetStdHandle(STD_OUTPUT_HANDLE, SUInf.hStdOutput))\n\t\tCon_Printf(\"Windows sucks\\n\");\n\tif (!SetStdHandle(STD_ERROR_HANDLE, SUInf.hStdError))\n\t\tCon_Printf(\"Windows sucks\\n\");\n\tif (!SetStdHandle(STD_INPUT_HANDLE, SUInf.hStdInput))\n\t\tCon_Printf(\"Windows sucks\\n\");\n\n\tprintf(\"Started app\\n\");\n\tret = CreateProcess(NULL, command, NULL, NULL, true, CREATE_NO_WINDOW, NULL, NULL, &SUInf, &ProcInfo);\n\n\tqt->process = ProcInfo.hProcess;\n\tCloseHandle(ProcInfo.hThread);\n\n\tqt->running = true;\n\n\tqt->console = Con_Create(\"QTerm\", 0);\n\tqt->console->redirect = QT_KeyPress;\n\tqt->console->close = QT_Close;\n\tqt->console->userdata = qt;\n\tCon_PrintCon(qt->console, \"Started Process\\n\", PFS_NOMARKUP);\n\tCon_SetActive(qt->console);\n\n\tqt->next = qterms;\n\tqterms = activeqterm = qt;\n}\n\nvoid Con_QTerm_f(void)\n{\n\tif(Cmd_IsInsecure())\n\t\tCon_Printf(\"Server tried stuffcmding a restricted command: qterm %s\\n\", Cmd_Args());\n\telse\n\t\tQT_Create(Cmd_Args());\n}\n#endif\n\n\n\n\nvoid Key_ClearTyping (void)\n{\n\tkey_lines[edit_line] = BZ_Realloc(key_lines[edit_line], 1);\n\tkey_lines[edit_line][0] = 0;\t// clear any typing\n\tkey_linepos = 0;\n}\n\nvoid Con_History_Load(void)\n{\n\tchar line[8192];\n\tchar *cr;\n\tvfsfile_t *file = FS_OpenVFS(\"conhistory.txt\", \"rb\", FS_ROOT);\n\n\tfor (edit_line=0 ; edit_line<=CON_EDIT_LINES_MASK ; edit_line++)\n\t{\n\t\tkey_lines[edit_line] = BZ_Realloc(key_lines[edit_line], 1);\n\t\tkey_lines[edit_line][0] = 0;\n\t}\n\tedit_line = 0;\n\tkey_linepos = 0;\n\n\tif (file)\n\t{\n\t\twhile (VFS_GETS(file, line, sizeof(line)-1))\n\t\t{\n\t\t\t//strip a trailing \\r if its from windows.\n\t\t\tcr = line + strlen(line);\n\t\t\tif (cr > line && cr[-1] == '\\r')\n\t\t\t\tcr[-1] = '\\0';\n\t\t\tkey_lines[edit_line] = BZ_Realloc(key_lines[edit_line], strlen(line)+1);\n\t\t\tstrcpy(key_lines[edit_line], line);\n\t\t\tedit_line = (edit_line+1) & CON_EDIT_LINES_MASK;\n\t\t}\n\t\tVFS_CLOSE(file);\n\t}\n\thistory_line = edit_line;\n}\nvoid Con_History_Save(void)\n{\n\tvfsfile_t *file;\n\tint line;\n\n\tif (!FS_GameIsInitialised())\n\t\treturn;\n\n\tif (!con_savehistory.ival)\n\t\treturn;\n\n\tfile = FS_OpenVFS(\"conhistory.txt\", \"wb\", FS_ROOT);\n\tif (file)\n\t{\n\t\tline = edit_line - CON_EDIT_LINES_MASK;\n\t\tif (line < 0)\n\t\t\tline = 0;\n\t\tfor(; line < edit_line; line++)\n\t\t{\n\t\t\tVFS_PUTS(file, key_lines[line]);\n#ifdef _WIN32\t//use an \\r\\n for readability with notepad.\n\t\t\tVFS_PUTS(file, \"\\r\\n\");\n#else\n\t\t\tVFS_PUTS(file, \"\\n\");\n#endif\n\t\t}\n\t\tVFS_CLOSE(file);\n\t}\n}\n\n/*\n================\nCon_ToggleConsole_f\n================\n*/\nvoid Con_ToggleConsole_Force(void)\n{\n\tconsole_t *con = Con_GetMain();\n\n\tSCR_EndLoadingPlaque();\n\tKey_ClearTyping ();\n\n\tif (con->flags & CONF_ISWINDOW)\n\t{\n\t\tif (con_curwindow == con && Key_Dest_Has(kdm_cwindows))\n\t\t{\n\t\t\tcon_curwindow = NULL;\n\t\t\tKey_Dest_Remove(kdm_cwindows);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tcon_curwindow = con;\n\t\t\tKey_Dest_Add(kdm_cwindows);\n\t\t\tVRUI_SnapAngle();\n\t\t}\n\t}\n\telse\n\t{\n\t\tif (Key_Dest_Has(kdm_console))\n\t\t\tKey_Dest_Remove(kdm_console);\n\t\telse\n\t\t{\n\t\t\tKey_Dest_Add(kdm_console);\n\t\t\tVRUI_SnapAngle();\n\t\t}\n\t}\n}\nvoid Con_ToggleConsole_f (void)\n{\n\textern cvar_t con_stayhidden;\n\n\tCon_GetMain();\n\n\tif (!con_curwindow)\n\t{\n\t\tfor (con_curwindow = con_head; con_curwindow; con_curwindow = con_curwindow->next)\n\t\t\tif (con_curwindow->flags & CONF_ISWINDOW)\n\t\t\t\tbreak;\n\t}\n\n\tif (con_curwindow && !Key_Dest_Has(kdm_cwindows|kdm_console))\n\t{\n\t\tKey_Dest_Add(kdm_cwindows);\n\t\treturn;\n\t}\n\n#ifdef CSQC_DAT\n\tif (!editormodal && CSQC_ConsoleCommand(-1, \"toggleconsole\"))\n\t{\n\t\tKey_Dest_Remove(kdm_console);\n\t\treturn;\n\t}\n#endif\n\n\tif (con_stayhidden.ival >= 3)\n\t{\n\t\tKey_Dest_Remove(kdm_cwindows);\n\t\treturn;\t//its hiding!\n\t}\n\n\tCon_ToggleConsole_Force();\n}\n\nvoid Con_ClearCon(console_t *con)\n{\n\tconline_t *t;\n\twhile (con->current)\n\t{\n\t\tt = con->current;\n\t\tcon->current = t->older;\n\t\tZ_Free(t);\n\t}\n\tcon->display = con->current = con->oldest = NULL;\n\tcon->selstartline = NULL;\n\tcon->selendline = NULL;\n\n\t/*reset the line pointers, create an active line*/\n\tCon_Finit(con);\n}\n\n/*\n================\nCon_Clear_f\n================\n*/\nvoid Con_Clear_f (void)\n{\n\tconsole_t *con = Con_FindConsole(Cmd_Argv(1));\n\tif (!con || Cmd_IsInsecure())\n\t\treturn;\n\tCon_ClearCon(con);\n}\n\n\nvoid Cmd_ConEchoCenter_f(void)\n{\n\tconsole_t *con;\n\tcon = Con_FindConsole(Cmd_Argv(1));\n\tif (!con)\n\t\tcon = Con_Create(Cmd_Argv(1), 0);\n\tif (con)\n\t{\n\t\tCmd_ShiftArgs(1, false);\n\t\tCon_PrintCon(con, Cmd_Args(), con->parseflags|PFS_NONOTIFY|PFS_CENTERED );\n\t\tCon_PrintCon(con, \"\\n\", con->parseflags|PFS_NONOTIFY|PFS_CENTERED);\n\t}\n}\nvoid Cmd_ConEcho_f(void)\n{\n\tconsole_t *con;\n\tcon = Con_FindConsole(Cmd_Argv(1));\n\tif (!con)\n\t\tcon = Con_Create(Cmd_Argv(1), 0);\n\tif (con)\n\t{\n\t\tCmd_ShiftArgs(1, false);\n\t\tCon_PrintCon(con, Cmd_Args(), con->parseflags);\n\t\tCon_PrintCon(con, \"\\n\", con->parseflags);\n\t}\n}\n\nvoid Cmd_ConClear_f(void)\n{\n\tconsole_t *con;\n\tcon = Con_FindConsole(Cmd_Argv(1));\n\tif (con)\n\t\tCon_ClearCon(con);\n}\nvoid Cmd_ConClose_f(void)\n{\n\tconsole_t *con;\n\tcon = Con_FindConsole(Cmd_Argv(1));\n\tif (con)\n\t\tCon_Destroy(con);\n}\nvoid Cmd_ConActivate_f(void)\n{\n\tconsole_t *con;\n\tcon = Con_FindConsole(Cmd_Argv(1));\n\tif (con)\n\t\tCon_SetActive(con);\n}\n\n/*\n================\nCon_MessageMode_f\n================\n*/\nvoid Con_MessageMode_f (void)\n{\n\tchat_team = false;\n\tKey_Dest_Add(kdm_message);\n\tKey_Dest_Remove(kdm_console);\n}\n\n/*\n================\nCon_MessageMode2_f\n================\n*/\nvoid Con_MessageMode2_f (void)\n{\n\tchat_team = true;\n\tKey_Dest_Add(kdm_message);\n\tKey_Dest_Remove(kdm_console);\n}\n\nvoid Con_ForceActiveNow(void)\n{\n\tKey_Dest_Add(kdm_console);\n\tscr_con_target = scr_con_current = vid.height;\n}\n\n/*\n================\nCon_Init\n================\n*/\nvoid Log_Init (void);\n\nvoid Con_Init (void)\n{\n\tcon_current = NULL;\n\tcon_head = NULL;\n\n\tcon_main = Con_GetMain();\n\n\tcon_initialized = true;\n//\tCon_TPrintf (\"Console initialized.\\n\");\n\n//\n// register our commands\n//\n\tCvar_Register (&con_centernotify, \"Console controls\");\n\tCvar_Register (&con_notifytime, \"Console controls\");\n\tCvar_Register (&con_notify_x, \"Console controls\");\n\tCvar_Register (&con_notify_y, \"Console controls\");\n\tCvar_Register (&con_notify_w, \"Console controls\");\n\tCvar_Register (&con_numnotifylines, \"Console controls\");\n\tCvar_Register (&con_displaypossibilities, \"Console controls\");\n\tCvar_Register (&con_showcompletion, \"Console controls\");\n\tCvar_Register (&cl_chatmode, \"Console controls\");\n\tCvar_Register (&con_maxlines, \"Console controls\");\n\tCvar_Register (&con_numnotifylines_chat, \"Console controls\");\n\tCvar_Register (&con_notifytime_chat, \"Console controls\");\n\tCvar_Register (&con_separatechat, \"Console controls\");\n\tCvar_Register (&con_timestamps, \"Console controls\");\n\tCvar_Register (&con_timeformat, \"Console controls\");\n\tCvar_Register (&con_textsize, \"Console controls\");\n\tCvar_Register (&con_window, \"Console controls\");\n\tCvar_Register (&con_savehistory, \"Console controls\");\n\tCvar_ForceCallback(&con_window);\n\n\tCmd_AddCommand (\"toggleconsole\", Con_ToggleConsole_f);\n\tCmd_AddCommand (\"messagemode\", Con_MessageMode_f);\n\tCmd_AddCommand (\"messagemode2\", Con_MessageMode2_f);\n\tCmd_AddCommand (\"clear\", Con_Clear_f);\n#ifdef QTERM\n\tCmd_AddCommand (\"qterm\", Con_QTerm_f);\n#endif\n\n\tCmd_AddCommandD (\"conecho_center\", Cmd_ConEchoCenter_f, \"conecho_center consolename The Text To Echo\\nUse \\\"\\\" for the main console.\\nAny added lines will be aligned to the middle of the console.\");\n\tCmd_AddCommandD (\"conecho\", Cmd_ConEcho_f, \"conecho consolename The Text To Echo\\nEchos text to a named console instead of just the main one.\");\n\tCmd_AddCommandD (\"conclear\", Cmd_ConClear_f, \"Clears a named console (instead of just the main one)\");\n\tCmd_AddCommandD (\"conclose\", Cmd_ConClose_f, \"Destroys a named console\");\n\tCmd_AddCommandD (\"conactivate\", Cmd_ConActivate_f, \"Brings focus to the named console. Will not do anything if the named console is not created yet (so be sure to do any echos before using this command)\");\n\n\tLog_Init();\n}\n\nvoid Con_Shutdown(void)\n{\n\tint i;\n\n\tfor (i = 0; i <= CON_EDIT_LINES_MASK; i++)\n\t{\n\t\tBZ_Free(key_lines[i]);\n\t}\n\n\twhile(con_head)\n\t\tCon_Destroy(con_head);\n\tcon_initialized = false;\n}\n\nvoid TTS_SayConString(conchar_t *stringtosay);\n\n/*\n================\nCon_Print\n\nHandles cursor positioning, line wrapping, etc\nAll console printing must go through this in order to be logged to disk\nIf no console is visible, the notify window will pop up.\n================\n*/\n\n//reallocates a line (with its buffer), and updates its links. if shrinking, be sure to reduce the length\nconline_t *Con_ResizeLineBuffer(console_t *con, conline_t *old, unsigned int length)\n{\n\tconline_t *l;\n\n\told->maxlength = length & 0xffff;\n\tif (old->maxlength < old->length)\n\t\treturn NULL;\t//overflow.\n\tl = BZ_Realloc(old, sizeof(*l)+(old->maxlength)*sizeof(conchar_t));\n\n\tif (l->newer)\n\t\tl->newer->older = l;\n\tif (l->older)\n\t\tl->older->newer = l;\n\n\tif (con->selstartline == old)\n\t\tcon->selstartline = l;\n\tif (con->selendline == old)\n\t\tcon->selendline = l;\n\tif (con->display == old)\n\t\tcon->display = l;\n\tif (con->oldest == old)\n\t\tcon->oldest = l;\n\tif (con->current == old)\n\t\tcon->current = l;\n\tif (con->footerline == old)\n\t\tcon->footerline = l;\n\tif (con->userline == old)\n\t\tcon->userline = l;\n\tif (con->highlightline == old)\n\t\tcon->highlightline = old;\n\treturn l;\n}\n\nqboolean Con_InsertConChars (console_t *con, conline_t *line, int offset, conchar_t *c, int len)\n{\n\tconchar_t *o;\n\n\tif (line->length+len > line->maxlength)\n\t{\n\t\tline = Con_ResizeLineBuffer(con, line, line->length+len + 8);\n\t\tif (!line)\n\t\t\treturn false;\t//overflowed!\n\t}\n\n\to = (conchar_t *)(line+1);\n\tif (line->length-offset)\n\t\tmemmove(o+offset+len, o+offset, (line->length-offset)*sizeof(conchar_t));\n\tmemcpy(o+offset, c, sizeof(*o) * len);\n\tline->length+=len;\n\treturn true;\n}\n\nvoid Con_PrintCon (console_t *con, const char *txt, unsigned int parseflags)\n{\n\tconchar_t expanded[4096];\n\tconchar_t *c, *n;\n\tconline_t *reuse;\n\tint maxlines;\n\tunsigned flags, codepoint;\n\n\tif (con->maxlines)\n\t\tmaxlines = con->maxlines;\n\telse\n\t\tmaxlines = con_maxlines.ival;\n\n\tCOM_ParseFunString(con->defaultcharbits, txt, expanded, sizeof(expanded), parseflags);\n\n\tc = expanded;\n\tif (*c)\n\t\tcon->unseentext = true;\n\tfor (;*c; c=n)\n\t{\n\t\tn = Font_Decode(c, &flags, &codepoint);\n\t\tif (codepoint=='\\r' && !(flags&CON_HIDDEN))\n\t\t\tcon->cr = true;\n\t\telse if (codepoint=='\\n' && !(flags&CON_HIDDEN))\n\t\t{\n\t\t\tcon->cr = false;\n\t\t\treuse = NULL;\n\t\t\twhile (con->linecount >= maxlines)\n\t\t\t{\n\t\t\t\tif (con->oldest == con->current)\n\t\t\t\t\tbreak;\n\n\t\t\t\tif (con->selstartline == con->oldest)\n\t\t\t\t\tcon->selstartline = NULL;\n\t\t\t\tif (con->selendline == con->oldest)\n\t\t\t\t\tcon->selendline = NULL;\n\n\t\t\t\tif (con->display == con->oldest)\n\t\t\t\t\tcon->display = con->oldest->newer;\n\t\t\t\tcon->oldest = con->oldest->newer;\n\t\t\t\tif (reuse)\n\t\t\t\t\tZ_Free(con->oldest->older);\n\t\t\t\telse\n\t\t\t\t\treuse = con->oldest->older;\n\t\t\t\tcon->oldest->older = NULL;\n\t\t\t\tcon->linecount--;\n\t\t\t}\n\t\t\tcon->linecount++;\n\t\t\tcon->current->time = realtime;\n\t\t\tcon->current->flags = 0;\n\t\t\tif (parseflags & PFS_CENTERED)\n\t\t\t\tcon->current->flags |= CONL_CENTERED;\n\t\t\tif (parseflags & PFS_NONOTIFY)\n\t\t\t\tcon->current->flags |= CONL_NONOTIFY;\n\t\t\telse if (!Key_Dest_Has(~kdm_game) && (con->flags & CONF_NOTIFY))\n\t\t\t\tVRUI_SnapAngle();\n\n#if defined(HAVE_SPEECHTOTEXT)\n\t\t\tif (con->current)\n\t\t\t\tTTS_SayConString((conchar_t*)(con->current+1));\n#endif\n\n\t\t\tif (!reuse)\n\t\t\t{\n\t\t\t\treuse = Z_Malloc(sizeof(conline_t) + sizeof(conchar_t));\n\t\t\t\treuse->maxlength = 1;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\treuse->newer = NULL;\n\t\t\t\treuse->older = NULL;\n\t\t\t}\n\t\t\treuse->id = (++con->nextlineid) & 0xffff;\n\t\t\treuse->older = con->current;\n\t\t\tcon->current->newer = reuse;\n\t\t\tcon->current = reuse;\n\t\t\tcon->current->length = 0;\n\t\t\tif (con->display == con->current->older && con->displayscroll==0)\n\t\t\t\tcon->display = con->current;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (con->cr)\n\t\t\t{\n\t\t\t\tcon->current->length = 0;\n\t\t\t\tcon->cr = false;\n\t\t\t}\n\t\t\tif (!con->current->numlines)\n\t\t\t\tcon->current->numlines = 1;\n\n\t\t\tif (!con->current->length && con_timestamps.ival && !(parseflags & PFS_CENTERED))\n\t\t\t{\n\t\t\t\tchar timeasc[64];\n\t\t\t\tconchar_t timecon[64], *timeconend;\n\t\t\t\ttime_t rawtime;\n\t\t\t\ttime (&rawtime);\n\t\t\t\tstrftime(timeasc, sizeof(timeasc), con_timeformat.string, localtime (&rawtime));\n\t\t\t\ttimeconend = COM_ParseFunString(con->defaultcharbits, timeasc, timecon, sizeof(timecon), false);\n\t\t\t\tCon_InsertConChars(con, con->current, con->current->length, timecon, timeconend-timecon);\n\t\t\t}\n\n\t\t\t//FIXME: don't do this a char at a time\n\t\t\tCon_InsertConChars(con, con->current, con->current->length, c, n-c);\n\t\t}\n\t}\n\n\tcon->current->time = realtime;\n}\n\nvoid Con_CenterPrint(const char *txt)\n{\n\tconsole_t *c = Con_GetMain();\n\tint flags = c->parseflags|PFS_NONOTIFY|PFS_CENTERED;\n\tCon_PrintCon(c, \"^Ue01d^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01f\\n\", flags);\n\tCon_PrintCon(c, txt, flags);\t//client console\n\tCon_PrintCon(c, \"\\n^Ue01d^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01f\\n\", flags);\n}\n\nvoid Con_Print (const char *txt)\n{\n\tconsole_t *c = Con_GetMain();\n\tCon_PrintCon(c, txt, c->parseflags);\t//client console\n}\nvoid Con_PrintFlags(const char *txt, unsigned int setflags, unsigned int clearflags)\n{\n\tconsole_t *c = Con_GetMain();\n\tsetflags |= c->parseflags;\n\tsetflags &= ~clearflags;\n\n// also echo to debugging console\n\tSys_Printf (\"%s\", txt);\t// also echo to debugging console\n\n// log all messages to file\n\tCon_Log (txt);\n\n\tif (con_initialized)\n\t\tCon_PrintCon(c, txt, setflags);\n}\n\nvoid Con_CycleConsole(void)\n{\n\tconsole_t *first = con_current?con_current:con_head;\n\twhile(1)\n\t{\n\t\tcon_current = con_current->next;\n\t\tif (!con_current)\n\t\t\tcon_current = con_head;\n\t\tif (con_current == first)\n\t\t{\n\t\t\tif (con_current->flags & (CONF_HIDDEN|CONF_ISWINDOW))\n\t\t\t\tcon_current = NULL; //no valid consoles\n\t\t\tbreak;\t//we wrapped? oh noes\n\t\t}\n\n\t\tif (con_current->flags & (CONF_HIDDEN|CONF_ISWINDOW))\n\t\t\tcontinue;\t//this is a valid choice\n\t\tbreak;\n\t}\n}\n\n/*\n================\nCon_Printf\n\nHandles cursor positioning, line wrapping, etc\n================\n*/\n\n#ifdef HAVE_SERVER\nextern redirect_t\tsv_redirected;\nextern char\tsv_redirected_buf[8000];\nvoid SV_FlushRedirect (void);\n#endif\nvfsfile_t *con_pipe;\n\n#define\tMAXPRINTMSG\t4096\nstatic void Con_PrintFromThread (void *ctx, void *data, size_t a, size_t b)\n{\n\tCon_Printf(\"%s\", (char*)data);\n\tBZ_Free(data);\n}\n\nvfsfile_t *Con_POpen(const char *conname)\n{\n\tif (!conname || !*conname)\n\t{\n\t\tif (con_pipe)\n\t\t\tVFS_CLOSE(con_pipe);\n\t\tcon_pipe = VFSPIPE_Open(2, false);\n\t\treturn con_pipe;\n\t}\n\treturn NULL;\n}\n\n// FIXME: make a buffer size safe vsprintf?\nvoid VARGS Con_Printf (const char *fmt, ...)\n{\n\tva_list\t\targptr;\n\tchar\t\tmsg[MAXPRINTMSG];\n\n\tva_start (argptr,fmt);\n\tvsnprintf (msg,sizeof(msg), fmt,argptr);\n\tva_end (argptr);\n\n\tif (!Sys_IsMainThread())\n\t{\n\t\tCOM_AddWork(WG_MAIN, Con_PrintFromThread, NULL, Z_StrDup(msg), 0, 0);\n\t\treturn;\n\t}\n\n#ifdef HAVE_SERVER\n\t// add to redirected message\n\tif (sv_redirected)\n\t{\n\t\tif (strlen (msg) + strlen(sv_redirected_buf) > sizeof(sv_redirected_buf) - 1)\n\t\t\tSV_FlushRedirect ();\n\t\tstrcat (sv_redirected_buf, msg);\n\t\treturn;\n\t}\n#endif\n\n// also echo to debugging console\n\tSys_Printf (\"%s\", msg);\t// also echo to debugging console\n\n// log all messages to file\n\tCon_Log (msg);\n\n\tif (con_pipe)\n\t\tVFS_PUTS(con_pipe, msg);\n\n\tif (!con_initialized)\n\t\treturn;\n\n// write it to the scrollable buffer\n\tCon_Print (msg);\n}\n\nvoid VARGS Con_SafePrintf (const char *fmt, ...)\n{\t//obsolete version of the function\n\tva_list\t\targptr;\n\tchar\t\tmsg[MAXPRINTMSG];\n\n\tva_start (argptr,fmt);\n\tvsnprintf (msg,sizeof(msg)-1, fmt,argptr);\n\tva_end (argptr);\n\n// write it to the scrollable buffer\n\tCon_Printf (\"%s\", msg);\n}\n\nvoid VARGS Con_TPrintf (translation_t text, ...)\n{\n\tva_list\t\targptr;\n\tchar\t\tmsg[MAXPRINTMSG];\n\tconst char *fmt = localtext(text);\n\n\tva_start (argptr,text);\n\tvsnprintf (msg,sizeof(msg), fmt,argptr);\n\tva_end (argptr);\n\n// write it to the scrollable buffer\n\tCon_Printf (\"%s\", msg);\n}\n\nvoid VARGS Con_SafeTPrintf (translation_t text, ...)\n{\n\tva_list\t\targptr;\n\tchar\t\tmsg[MAXPRINTMSG];\n\tconst char *fmt = localtext(text);\n\n\tva_start (argptr,text);\n\tvsnprintf (msg,sizeof(msg), fmt,argptr);\n\tva_end (argptr);\n\n// write it to the scrollable buffer\n\tCon_Printf (\"%s\", msg);\n}\n\nstatic void Con_DPrintFromThread (void *ctx, void *data, size_t a, size_t b)\n{\n\tif (log_developer.ival || !a)\n\t\tCon_Log(data);\n\tif (developer.ival >= (int)a)\n\t{\n\t\tconsole_t *c = Con_GetMain();\n\t\tSys_Printf (\"%s\", (const char*)data);\t// also echo to debugging console\n\t\tCon_PrintCon(c, data, c->parseflags);\n\t}\n\tBZ_Free(data);\n}\n/*\n================\nCon_DPrintf\n\nA Con_Printf that only shows up if the \"developer\" cvar is set\n================\n*/\nvoid VARGS Con_DPrintf (const char *fmt, ...)\n{\n\tva_list\t\targptr;\n\tchar\t\tmsg[MAXPRINTMSG];\n\n#ifdef CRAZYDEBUGGING\n\tva_start (argptr,fmt);\n\tvsnprintf (msg,sizeof(msg)-1, fmt,argptr);\n\tva_end (argptr);\n\tSys_Printf(\"%s\", msg);\n\treturn;\n#else\n\tif (!developer.ival && !log_developer.ival)\n\t\treturn; // early exit\n#endif\n\n\tva_start (argptr,fmt);\n\tvsnprintf (msg,sizeof(msg)-1, fmt,argptr);\n\tva_end (argptr);\n\n\tif (!Sys_IsMainThread())\n\t{\n\t\tCOM_AddWork(WG_MAIN, Con_DPrintFromThread, NULL, Z_StrDup(msg), 1, 0);\n\t\treturn;\n\t}\n\n\tif (log_developer.ival)\n\t\tCon_Log(msg);\n\tif (developer.ival)\n\t{\n\t\tconsole_t *c = Con_GetMain();\n\t\tSys_Printf (\"%s\", msg);\t// also echo to debugging console\n\t\tCon_PrintCon(c, msg, c->parseflags);\n\t}\n}\nvoid VARGS Con_DLPrintf (int level, const char *fmt, ...)\n{\n\tva_list\t\targptr;\n\tchar\t\tmsg[MAXPRINTMSG];\n\n#ifdef CRAZYDEBUGGING\n\tva_start (argptr,fmt);\n\tvsnprintf (msg,sizeof(msg)-1, fmt,argptr);\n\tva_end (argptr);\n\tSys_Printf(\"%s\", msg);\n\treturn;\n#else\n\tif (developer.ival<level && (!log_developer.ival && level))\n\t\treturn; // early exit\n#endif\n\n\tva_start (argptr,fmt);\n\tvsnprintf (msg,sizeof(msg)-1, fmt,argptr);\n\tva_end (argptr);\n\n\tif (!Sys_IsMainThread())\n\t{\n\t\tCOM_AddWork(WG_MAIN, Con_DPrintFromThread, NULL, Z_StrDup(msg), level, 0);\n\t\treturn;\n\t}\n\n\tif (log_developer.ival || !level)\n\t\tCon_Log(msg);\n\tif (developer.ival >= level)\n\t{\n\t\tSys_Printf (\"%s\", msg);\t// also echo to debugging console\n\t\tif (con_initialized)\n\t\t{\n\t\t\tconsole_t *c = Con_GetMain();\n\t\t\tCon_PrintCon(c, msg, c->parseflags);\n\t\t}\n\t}\n}\n\nvoid VARGS Con_ThrottlePrintf (float *timer, int developerlevel, const char *fmt, ...)\n{\n\tva_list\t\targptr;\n\tchar\t\tmsg[MAXPRINTMSG];\n\tfloat now = realtime;\n\n\tif (*timer > now)\n\t\t;\t//in the future? zomg\n\telse if (*timer >= now-1)\n\t\treturn;\t//within the last second\n\t*timer = now;\t//in the future? zomg\n\n\tva_start (argptr,fmt);\n\tvsnprintf (msg,sizeof(msg)-1, fmt,argptr);\n\tva_end (argptr);\n\n\tif (developerlevel)\n\t\tCon_DLPrintf(developerlevel, \"%s\", msg);\n\telse\n\t\tCon_Printf(\"%s\", msg);\n}\n\nstatic void Con_FooterMarked(console_t *con, qboolean append, conchar_t *marked, conchar_t *markedend)\n{\n\tint oldlen, newlen;\n\tconline_t *newf = NULL, *l;\n\tunsigned fl, cp;\n\tconchar_t *nl, *n;\n\n\tif (!append)\n\t{\n\t\twhile(con->footerline)\n\t\t{\n\t\t\tl = con->footerline;\n\t\t\tcon->footerline = l->older;\n\t\t\tif (con->selstartline == l)\n\t\t\t\tcon->selstartline = NULL;\n\t\t\tif (con->selendline == l)\n\t\t\t\tcon->selendline = NULL;\n\t\t\tZ_Free(l);\n\t\t}\n\t\tcon->footerline = NULL;\n\t}\n\tfor (append = true; marked < markedend; marked = n, append = false)\n\t{\n\t\tn = markedend;\n\t\tfor (nl = marked; nl < markedend; nl=n)\n\t\t{\n\t\t\tn = Font_Decode(nl, &fl, &cp);\n\t\t\tif (cp == '\\n' && !(fl&CONF_HIDDEN))\n\t\t\t\tbreak;\n\t\t}\n\n\t\tnewlen = nl - marked;\n\t\tif (append && con->footerline)\n\t\t\toldlen = con->footerline->length;\n\t\telse\n\t\t\toldlen = 0;\n\n\t\tif (newlen || !append)\n\t\t{\n\t\t\tnewf = Z_Malloc(sizeof(*newf) + (oldlen + newlen) * sizeof(conchar_t));\n\t\t\tif (append && con->footerline)\n\t\t\t{\n\t\t\t\tmemcpy(newf, con->footerline, sizeof(*con->footerline)+oldlen*sizeof(conchar_t));\n\t\t\t\tZ_Free(con->footerline);\n\t\t\t}\n\t\t\telse\n\t\t\t\tnewf->older = con->footerline;\n\t\t\tif (newf->older)\n\t\t\t\tnewf->older->newer = newf;\n\n\t\t\tmemcpy((conchar_t*)(newf+1)+oldlen, marked, newlen*sizeof(conchar_t));\n\t\t\tnewf->length = oldlen + newlen;\n\t\t\tcon->footerline = newf;\n\t\t}\n\t}\n}\n\n/*description text at the bottom of the console*/\nvoid Con_Footerf(console_t *con, qboolean append, const char *fmt, ...)\n{\n\tva_list\t\targptr;\n\tchar\t\tmsg[MAXPRINTMSG];\n\tconchar_t\tmarked[MAXPRINTMSG], *markedend;\n\n\tif (!con)\n\t\tcon = con_current;\n\tif (!con)\n\t\treturn;\n\n\tva_start (argptr,fmt);\n\tvsnprintf (msg,sizeof(msg)-1, fmt,argptr);\n\tva_end (argptr);\n\tmarkedend = COM_ParseFunString((COLOR_YELLOW << CON_FGSHIFT)|(con->backshader?CON_NONCLEARBG:0), msg, marked, sizeof(marked), false);\n\n\tCon_FooterMarked(con, append, marked, markedend);\n}\n\n/*\n==============================================================================\n\nDRAWING\n\n==============================================================================\n*/\n\nqboolean COM_InsertIME(conchar_t *buffer, size_t buffersize, conchar_t **cursor, conchar_t **textend)\n{\n\tconchar_t *in = vid.ime_preview;\n\tif (in && *in && *textend+vid.ime_previewlen < buffer+buffersize)\n\t{\n\t\tmemmove(buffer + (*cursor-buffer) + vid.ime_previewlen, *cursor, ((*textend-*cursor)+1)*sizeof(conchar_t));\n\t\tmemcpy(buffer + (*cursor-buffer), in, vid.ime_previewlen*sizeof(conchar_t));\n\t\t*cursor += vid.ime_caret;\n\t\t*textend += vid.ime_previewlen;\n\n\t\treturn true;\n\t}\n\treturn false;\n}\n\n/*\n================\nCon_DrawInput\n\nThe input line scrolls horizontally if typing goes beyond the right edge\ny is the bottom of the input\nreturn value is the top of the region\n================\n*/\nint Con_DrawInput (console_t *con, qboolean focused, int left, int right, int y, qboolean selactive, int selsx, int selex, int selsy, int seley)\n{\n\tint\t\ti;\n\tint lhs, rhs;\n\tint p;\n\tunsigned char\t*text, *fname = NULL;\n\textern int con_commandmatch;\n\tconchar_t maskedtext[2048];\n\tconchar_t *endmtext;\n\tconchar_t *cursor;\n\tconchar_t *cchar;\n\tconchar_t *textstart;\n\tsize_t textsize;\n\tqboolean cursorframe;\n\tunsigned int codeflags, codepoint;\n\tint cursorpos;\n\tqboolean hidecomplete;\n\n\tint x;\n\n\tif (focused)\n\t{\n\t\tvid.ime_allow = true;\n\t\tvid.ime_position[0] = ((float)left/vid.pixelwidth)*vid.width;\n\t\tvid.ime_position[1] = ((float)y/vid.pixelheight)*vid.height;\n\t}\n\n\tif (!con->linebuffered || con->linebuffered == Con_Navigate)\n\t{\n\t\tif (con->footerline)\n\t\t{\n\t\t\ty = Con_DrawConsoleLines(con, con->footerline, 0, left, right, y, 0, selactive, selsx, selex, selsy, seley, 0);\n\t\t}\n\t\treturn y;\t//fixme: draw any unfinished lines of the current console instead.\n\t}\n\n\ty -= Font_CharHeight();\n\n\tif (!focused)\n\t\treturn y;\t\t// don't draw anything (always draw if not active)\n\n\ttext = key_lines[edit_line];\n\n\tcursorpos = key_linepos;\n\n\t//copy it to an alternate buffer and fill in text colouration escape codes.\n\t//if it's recognised as a command, colour it yellow.\n\t//if it's not a command, and the cursor is at the end of the line, leave it as is,\n\t//\tbut add to the end to show what the compleation will be.\n\n\ttextstart = COM_ParseFunString(CON_WHITEMASK, con->prompt, maskedtext, sizeof(maskedtext) - sizeof(maskedtext[0]), PFS_FORCEUTF8);\n\ttextsize = (countof(maskedtext) - (textstart-maskedtext) - 1) * sizeof(maskedtext[0]);\n\ti = text[cursorpos];\n\ttext[cursorpos] = 0;\n\tcursor = COM_ParseFunString(CON_WHITEMASK, text, textstart, textsize, PFS_KEEPMARKUP | PFS_FORCEUTF8);\n\t//okay, so that's where the cursor is. heal the input string and reparse (so we don't mess up escapes)\n\ttext[cursorpos] = i;\n\tendmtext = COM_ParseFunString(CON_WHITEMASK, text, textstart, textsize, PFS_KEEPMARKUP | PFS_FORCEUTF8);\n//\tendmtext = COM_ParseFunString(CON_WHITEMASK, text+key_linepos, cursor, ((char*)maskedtext)+sizeof(maskedtext) - (char*)(cursor+1), PFS_KEEPMARKUP | PFS_FORCEUTF8);\n\n\thidecomplete = COM_InsertIME(maskedtext, countof(maskedtext), &cursor, &endmtext);\n/*\tif (cursorpos == strlen(text) && vid.ime_preview)\n\t{\n\t\tendmtext = COM_ParseFunString(COLOR_MAGENTA<<CON_FGSHIFT, vid.ime_preview, endmtext, (countof(maskedtext) - (endmtext-maskedtext) - 1) * sizeof(maskedtext[0]), PFS_KEEPMARKUP | PFS_FORCEUTF8);\n\t\tcursor += strlen(vid.ime_preview);\n\t\tcursorpos += strlen(vid.ime_preview);\n\t\ttext = va(\"%s%s\", text, vid.ime_preview);\n\t}\n*/\n\n\tif ((char*)endmtext == (char*)(maskedtext-2) + sizeof(maskedtext))\n\t\tendmtext[-1] = CON_WHITEMASK | '+' | CON_NONCLEARBG;\n\tendmtext[1] = 0;\n\n\tif ((*cursor & CON_HIDDEN) && cursor[0] != (CON_HIDDEN|'^') && cursor[1] != CON_LINKSTART)\n\t{\t//if we're in the middle of a link (but not at the very first char - to make life prettier) then reveal the hidden text so that you can actually see what you're editing.\n\t\tfor (i = cursor-textstart; textstart[i]; i++)\n\t\t{\n\t\t\tif (textstart[i] == CON_LINKSTART)\n\t\t\t\tbreak;\n\t\t\tif (textstart[i] == CON_LINKEND)\n\t\t\t{\n\t\t\t\ttextstart[i] &= ~CON_HIDDEN;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\ttextstart[i] &= ~CON_HIDDEN;\n\t\t}\n\t\tfor (i = cursor-textstart; i>=0; i--)\n\t\t{\n\t\t\tif (textstart[i] == CON_LINKEND)\n\t\t\t\tbreak;\n\t\t\tif (textstart[i] == CON_LINKSTART)\n\t\t\t{\n\t\t\t\ttextstart[i] &= ~CON_HIDDEN;\n\t\t\t\tif (--i >= 0)\t//make the ^ of the ^[ shown too.\n\t\t\t\t\ttextstart[i] &= ~CON_HIDDEN;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\ttextstart[i] &= ~CON_HIDDEN;\n\t\t}\n\t}\n\n\ti = 0;\n\tx = left;\n\n\tif (!hidecomplete && con->commandcompletion && con_showcompletion.ival && text[0] && !(text[0] == '/' && !text[1]))\n\t{\n\t\tif (cl_chatmode.ival && (text[0] == '/' || (cl_chatmode.ival == 2 && Cmd_IsCommand(text))))\n\t\t{\t//color the first token yellow, it's a valid command\n\t\t\tfor (p = 0; (textstart[p]&CON_CHARMASK)>' '; p++)\n\t\t\t\ttextstart[p] = (textstart[p]&CON_CHARMASK) | (COLOR_YELLOW<<CON_FGSHIFT);\n\t\t}\n\n\t\tif (cursor == endmtext)\t//cursor is at end\n\t\t{\n\t\t\tint cmdstart;\n\t\t\tcmdstart = text[0] == '/'?1:0;\n\t\t\tfname = Cmd_CompleteCommand(text+cmdstart, true, true, max(1, con_commandmatch), NULL);\n\t\t\tif (fname && strlen(fname) < 256)\t//we can compleate it to:\n\t\t\t{\n\t\t\t\tfor (p = min(strlen(fname), cursorpos-cmdstart); fname[p]>0; p++)\n\t\t\t\t\ttextstart[p+cmdstart] = (unsigned int)fname[p] | (COLOR_GREEN<<CON_FGSHIFT);\n\t\t\t\tif (p < cursorpos-cmdstart)\n\t\t\t\t\tp = cursorpos-cmdstart;\n\t\t\t\tp = min(p+cmdstart, sizeof(maskedtext)/sizeof(maskedtext[0]) - 3);\n\t\t\t\ttextstart[p] = 0;\n\t\t\t\ttextstart[p+1] = 0;\n\t\t\t}\n\t\t}\n\t}\n\n\tif (!vid.activeapp)\n\t\tcursorframe = 0;\n\telse\n\t\tcursorframe = ((int)(realtime*con_cursorspeed)&1);\n\n\t//FIXME: support tab somehow\n\tfor (lhs = 0, cchar = maskedtext; cchar < cursor; )\n\t{\n\t\tcchar = Font_Decode(cchar, &codeflags, &codepoint);\n\t\tlhs += Font_CharWidth(codeflags, codepoint);\n\t}\n\tfor (rhs = 0, cchar = cursor; *cchar; )\n\t{\n\t\tcchar = Font_Decode(cchar, &codeflags, &codepoint);\n\t\trhs += Font_CharWidth(codeflags, codepoint);\n\t}\n\n\t//put the cursor in the middle\n\tx = (right-left)/2 + left;\n\t//move the line to the right if there's not enough text to touch the right hand side\n\tif (x < right-rhs - Font_CharWidth(CON_WHITEMASK, 0xe000|11))\n\t\tx = right - rhs - Font_CharWidth(CON_WHITEMASK, 0xe000|11);\n\t//if the left hand side is on the right of the left point (overrides right alignment)\n\tif (x > lhs + left)\n\t\tx = lhs + left;\n\n\tlhs = x - lhs;\n\tfor (cchar = maskedtext; cchar < cursor; )\n\t{\n\t\tcchar = Font_Decode(cchar, &codeflags, &codepoint);\n\t\tlhs = Font_DrawChar(lhs, y, codeflags, codepoint);\n\t}\n\trhs = x;\n\tcchar = Font_Decode(cursor, &codeflags, &codepoint);\n\tif (cursorframe)\n\t{\n//\t\textern cvar_t com_parseutf8;\n//\t\tif (com_parseutf8.ival)\n//\t\t\tFont_DrawChar(rhs, y, (*cursor&~(CON_BGMASK|CON_FGMASK)) | (COLOR_BLUE<<CON_BGSHIFT) | CON_NONCLEARBG | CON_WHITEMASK);\n//\t\telse\n\t\t\tFont_DrawChar(rhs, y, CON_WHITEMASK, 0xe000|11);\n\t}\n\telse if (codepoint)\n\t{\n\t\tFont_DrawChar(rhs, y, codeflags, codepoint);\n\t}\n\tif (codepoint)\n\t{\n\t\trhs += Font_CharWidth(codeflags, codepoint);\n\t\twhile (*cchar)\n\t\t{\n\t\t\tcchar = Font_Decode(cchar, &codeflags, &codepoint);\n\t\t\trhs = Font_DrawChar(rhs, y, codeflags, codepoint);\n\t\t}\n\t}\n\n\t/*if its getting completed to something, show some help about the command that is going to be used*/\n\tif (con->footerline)\n\t{\n\t\ty = Con_DrawConsoleLines(con, con->footerline, 0, left, right, y, 0, selactive, selsx, selex, selsy, seley, 0);\n\t}\n\n\t/*just above that, we have the tab completion list*/\n\tif (con_commandmatch && con_displaypossibilities.value)\n\t{\n\t\tconchar_t *end, *s;\n\t\tconst char *cmd;//, *desc;\n\t\tint cmdstart;\n\t\tsize_t newlen;\n\t\tcmd_completion_t *c;\n\t\tcmdstart = text[0] == '/'?1:0;\n\t\tend = maskedtext;\n\n\t\tif (!con->completionline || con->completionline->length + 512 > con->completionline->maxlength)\n\t\t{\n\t\t\tnewlen = (con->completionline?con->completionline->length:0) + 2048;\n\n\t\t\tZ_Free(con->completionline);\n\t\t\tcon->completionline = Z_Malloc(sizeof(*con->completionline) + newlen*sizeof(conchar_t));\n\t\t\tcon->completionline->maxlength = newlen;\n\t\t}\n\t\tcon->completionline->length = 0;\n\n\t\tc = Cmd_Complete(text+cmdstart, true);\n\n\t\tfor (i = 0; i < c->num; i++)\n\t\t{\n\t\t\tint col = (con_commandmatch == i+1)?3:2;\n\t\t\ts = (conchar_t*)(con->completionline+1);\n\n\t\t\t//note: if cl_chatmode is 0, then we shouldn't show the leading /, however that is how the console link stuff recognises it as command text, so we always display it.\n\t\t\tcmd = c->completions[i].text;\n//\t\t\tdesc = c->completions[i].desc;\n//\t\t\tif (desc)\n//\t\t\t\tend = COM_ParseFunString((COLOR_GREEN<<CON_FGSHIFT), va(\"^[^%i/%s\\\\tip\\\\%s^]\\t\", col, cmd, desc), s+con->completionline->length, (con->completionline->maxlength-con->completionline->length)*sizeof(maskedtext[0]), true);\n//\t\t\telse\n\t\t\t\tend = COM_ParseFunString((COLOR_GREEN<<CON_FGSHIFT), va(\"^[^%i/%s^]\\t\", col, cmd), s+con->completionline->length, (con->completionline->maxlength-con->completionline->length)*sizeof(maskedtext[0]), true);\n\t\t\tcon->completionline->length = end - s;\n\t\t}\n\t\tif (c->extra)\n\t\t{\n\t\t\ts = (conchar_t*)(con->completionline+1);\n\t\t\tend = COM_ParseFunString((COLOR_WHITE<<CON_FGSHIFT), va(\"%u MORE\", (unsigned)c->extra), s+con->completionline->length, (con->completionline->maxlength-con->completionline->length)*sizeof(maskedtext[0]), true);\n\t\t\tcon->completionline->length = end - s;\n\t\t}\n\n\t\tif (con->completionline->length)\n\t\t\ty = Con_DrawConsoleLines(con, con->completionline, 0, left, right, y, 0, selactive, selsx, selex, selsy, seley, 0);\n\t}\n\n\treturn y;\n}\n\n/*\n================\nCon_DrawNotify\n\nDraws the last few lines of output transparently over the game top\n================\n*/\nvoid Con_DrawNotifyOne (console_t *con)\n{\n\tconchar_t *starts[NUM_CON_TIMES], *ends[NUM_CON_TIMES];\n\tfloat alphas[NUM_CON_TIMES], a;\n\tconchar_t *c;\n\tconline_t *l;\n\tint lines=con->notif_l;\n\tint line;\n\tint nx, y;\n\tint nw;\n\tint x;\n\tunsigned int codeflags, codepoint;\n\n\tint maxlines;\n\tfloat t;\n\n\tFont_BeginString(font_console, con->notif_x * vid.width, con->notif_y * vid.height, &nx, &y);\n\tFont_Transform(con->notif_w * vid.width, 0, &nw, NULL);\n\n\tif (con->notif_l < 0)\n\t\tcon->notif_l = 0;\n\tif (con->notif_l > NUM_CON_TIMES)\n\t\tcon->notif_l = NUM_CON_TIMES;\n\tlines = maxlines = con->notif_l;\n\n\tif (!con->notif_x && !con->notif_y && con->notif_w == 1)\n\t\ty = Con_DrawProgress(0, nw, 0);\n\n\tl = con->current;\n\tif (!l->length)\n\t\tl = l->older;\n\tfor (; l && lines > con->notif_l-maxlines; l = l->older)\n\t{\n\t\tif (l->flags & CONL_NONOTIFY)\n\t\t\tcontinue; //hidden from notify\n\t\tt = realtime - (l->time+con->notif_t);\n\t\tif (t > 0)\n\t\t{\n\t\t\tif (t > con->notif_fade)\n\t\t\t{\n\t\t\t\tl->flags |= CONL_NONOTIFY;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\ta = 1 - (t/con->notif_fade);\n\t\t}\n\t\telse a = 1;\n\n\t\tline = Font_LineBreaks((conchar_t*)(l+1), (conchar_t*)(l+1)+l->length, nw, lines, starts, ends);\n\t\tif (!line && lines > 0)\n\t\t{\n\t\t\tlines--;\n\t\t\tstarts[lines] = NULL;\n\t\t\tends[lines] = NULL;\n\t\t\talphas[lines] = a;\n\t\t}\n\t\twhile(line --> 0 && lines > 0)\n\t\t{\n\t\t\tlines--;\n\t\t\tstarts[lines] = starts[line];\n\t\t\tends[lines] = ends[line];\n\t\t\talphas[lines] = a;\n\t\t}\n\t\tif (lines == 0)\n\t\t\tbreak;\n\t}\n\n\t//clamp it properly\n\twhile (lines < con->notif_l-maxlines)\n\t{\n\t\tlines++;\n\t}\n\tif (con->flags & CONF_NOTIFY_BOTTOM)\n\t\ty -= (con->notif_l - lines) * Font_CharHeight();\n\n\twhile (lines < con->notif_l)\n\t{\n\t\tx = 0;\n\t\tR2D_ImageColours(1, 1, 1, alphas[lines]);\n\t\tif (con->flags & CONF_NOTIFY_RIGHT)\n\t\t{\n\t\t\tfor (c = starts[lines]; c < ends[lines]; )\n\t\t\t{\n\t\t\t\tc = Font_Decode(c, &codeflags, &codepoint);\n\t\t\t\tx += Font_CharWidth(codeflags, codepoint);\n\t\t\t}\n\t\t\tx = (nw - x);\n\t\t}\n\t\telse if (con_centernotify.value)\n\t\t{\n\t\t\tfor (c = starts[lines]; c < ends[lines]; )\n\t\t\t{\n\t\t\t\tc = Font_Decode(c, &codeflags, &codepoint);\n\t\t\t\tx += Font_CharWidth(codeflags, codepoint);\n\t\t\t}\n\t\t\tx = (nw - x) / 2;\n\t\t}\n\t\tFont_LineDraw(nx+x, y, starts[lines], ends[lines]);\n\n\t\ty += Font_CharHeight();\n\n\t\tlines++;\n\t}\n\n\tFont_EndString(font_console);\n\n\tR2D_ImageColours(1,1,1,1);\n}\n\nvoid Con_ClearNotify(void)\n{\n\tconsole_t *con;\n\tconline_t *l;\n\tfor (con = con_head; con; con = con->next)\n\t{\n\t\tfor (l = con->current; l; l = l->older)\n\t\t\tl->flags |= CONL_NONOTIFY;\n\t}\n}\nvoid Con_DrawNotify (void)\n{\n\textern int startuppending;\n\tconsole_t *con;\n\n\tif (con_main)\n\t{\n\t\t/*keep the main console up to date*/\n\t\tcon_main->notif_l = con_numnotifylines.ival;\n\t\tcon_main->notif_w = con_notify_w.value;\n\t\tcon_main->notif_x = con_notify_x.value;\n\t\tcon_main->notif_y = con_notify_y.value;\n\t\tcon_main->notif_t = con_notifytime.value;\n\t}\n\n\tif (con_chat)\n\t{\n\t\tcon_chat->notif_l = con_numnotifylines_chat.ival;\n\t\tcon_chat->notif_w = 1;\n\t\tcon_chat->notif_y = (vid.height - sb_lines - 8*4) / vid.width;\n\t\tcon_chat->notif_t = con_notifytime_chat.value;\n\t}\n\n\tif (startuppending)\n\t{\n\t\tint x,y;\n\t\tFont_BeginString(font_console, 0, 0, &x, &y);\n\t\tCon_DrawProgress(0, vid.width, 0);\n\t\tFont_EndString(font_console);\n\t}\n\telse\n\t{\n\t\tfor (con = con_head; con; con = con->next)\n\t\t{\n\t\t\tif (con->flags & CONF_NOTIFY)\n\t\t\t\tCon_DrawNotifyOne(con);\n\t\t}\n\t}\n\n\tif (Key_Dest_Has(kdm_message))\n\t{\n\t\tint x, y;\n\t\tconchar_t *starts[8];\n\t\tconchar_t *ends[8];\n\t\tconchar_t markup[MAXCMDLINE+64];\n\t\tconchar_t *c, *end;\n\t\tchar demoji[8192];\n\t\tchar *foo = va(chat_team?\"say_team: %s\":\"say: %s\", Key_Demoji(demoji, sizeof(demoji), chat_buffer?(char*)chat_buffer:\"\"));\n\t\tint lines, i, pos;\n\t\tFont_BeginString(font_console, 0, 0, &x, &y);\n\t\ty = con_numnotifylines.ival * Font_CharHeight();\n\n\t\ti = chat_team?10:5;\n\t\tpos = strlen(foo)+i;\n\t\tpos = min(pos, chat_bufferpos + i);\n\n\t\t//figure out where the cursor is, if its safe\n\t\ti = foo[pos];\n\t\tfoo[pos] = 0;\n\t\tc = COM_ParseFunString(CON_WHITEMASK, foo, markup, sizeof(markup), PFS_KEEPMARKUP|PFS_FORCEUTF8);\n\t\tfoo[pos] = i;\n\n\t\t//k, build the string properly.\n\t\tend = COM_ParseFunString(CON_WHITEMASK, foo, markup, sizeof(markup) - sizeof(markup[0])-1, PFS_KEEPMARKUP | PFS_FORCEUTF8);\n\n\t\t//and overwrite the cursor so that it blinks.\n\t\t*end = ' '|CON_WHITEMASK;\n\t\tif (((int)(realtime*con_cursorspeed)&1))\n\t\t\t*c = 0xe00b|CON_WHITEMASK;\n\t\tif (c == end)\n\t\t\tend++;\n\n\t\tlines = Font_LineBreaks(markup, end, Font_ScreenWidth(), countof(starts), starts, ends);\n\t\tfor (i = 0; i < lines; i++)\n\t\t{\n\t\t\tx = 0;\n\t\t\tFont_LineDraw(x, y, starts[i], ends[i]);\n\t\t\ty += Font_CharHeight();\n\t\t}\n\t\tFont_EndString(font_console);\n\n\t\tvid.ime_allow = true;\n\t\tvid.ime_position[0] = 0;\n\t\tvid.ime_position[1] = y;\n\t}\n}\n\n//send all the stuff that was con_printed to sys_print.\n//This is so that system consoles in windows can scroll up and have all the text.\nvoid Con_PrintToSys(void)\n{\n\tconsole_t *curcon = con_main;\n\tconline_t *l;\n\tint i;\n\tconchar_t *t;\n\tchar buf[16];\n\n\tif (!curcon)\n\t\treturn;\n\n\tfor (l = curcon->oldest; l; l = l->newer)\n\t{\n\t\tt = (conchar_t*)(l+1);\n\t\t//fixme: utf8?\n\t\tfor (i = 0; i < l->length; i++)\n\t\t{\n\t\t\tif (!(t[i] & CON_HIDDEN))\n\t\t\t{\n\t\t\t\tif (com_parseutf8.ival>0)\n\t\t\t\t{\n\t\t\t\t\tint cl = utf8_encode(buf, t[i]&CON_CHARMASK, sizeof(buf)-1);\n\t\t\t\t\tif (cl)\n\t\t\t\t\t{\n\t\t\t\t\t\tbuf[cl] = 0;\n\t\t\t\t\t\tSys_Printf(\"%s\", buf);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tSys_Printf(\"%c\", t[i]&0xff);\n\t\t\t}\n\t\t}\n\t\tSys_Printf(\"\\n\");\n\t}\n}\n\n//returns the bottom of the progress bar\nstatic int Con_DrawProgress(int left, int right, int y)\n{\n\tconchar_t\t\t\tdlbar[1024], *chr;\n\tunsigned char\tprogresspercenttext[128];\n\tconst char *progresstext = NULL;\n\tconst char *txt;\n\tint x, tw;\n\tint i;\n\tint barwidth, barleft;\n\tfloat progresspercent = 0;\n\tunsigned int codeflags, codepoint;\n\t*progresspercenttext = 0;\n\n\t// draw the download bar\n\t// figure out width\n\tif (cls.download)\n\t{\n\t\tunsigned int count;\n\t\tqofs_t total;\n\t\tqboolean extra;\n\t\tprogresstext = cls.download->localname;\n\t\tprogresspercent = cls.download->percent;\n\n\t\tif (cls.download->sizeunknown && cls.download->size == 0)\n\t\t\tprogresspercent = -1;\n\n\t\tCL_GetDownloadSizes(&count, &total, &extra);\n\n\t\tif (progresspercent < 0)\n\t\t{\n\t\t\tif ((int)(realtime/2)&1 || total == 0)\n\t\t\t\tsprintf(progresspercenttext, \" (%ukB/s)\", CL_DownloadRate()/1000);\n\t\t\telse\n\t\t\t{\n\t\t\t\tchar tmp[64];\n\t\t\t\tsprintf(progresspercenttext, \" (%s%s)\", FS_AbbreviateSize(tmp,sizeof(tmp), total), extra?\"+\":\"\");\n\t\t\t}\n\n\t\t\t//do some marquee thing, so the user gets the impression that SOMETHING is happening.\n\t\t\tprogresspercent = realtime - (int)realtime;\n\t\t\tif ((int)realtime & 1)\n\t\t\t\tprogresspercent  = 1 - progresspercent;\n\t\t\tprogresspercent *= 100;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif ((int)(realtime/2)&1 || total == 0)\n\t\t\t\tsprintf(progresspercenttext, \" %5.1f%% (%ukB/s)\", progresspercent, CL_DownloadRate()/1000);\n\t\t\telse\n\t\t\t{\n\t\t\t\tsprintf(progresspercenttext, \" %5.1f%% (%u%sKiB)\", progresspercent, (int)(total/1024), extra?\"+\":\"\");\n\t\t\t}\n\t\t}\n\t}\n#ifdef RUNTIMELIGHTING\n\telse if ((progresstext=RelightGetProgress(&progresspercent)))\n\t{\n\t\tsprintf(progresspercenttext, \" %02d%%\", (int)progresspercent);\n\t}\n#endif\n\n\t//at this point:\n\t//progresstext: what is being downloaded/done (can end up truncated)\n\t//progresspercent: its percentage (used only for the slider)\n\t//progresspercenttext: that percent as text, essentually the right hand part of the bar.\n\n\tif (progresstext)\n\t{\n\t\t//chop off any leading path\n\t\tif ((txt = strrchr(progresstext, '/')) != NULL)\n\t\t\ttxt++;\n\t\telse\n\t\t\ttxt = progresstext;\n\n\t\tx = 0;\n\t\tCOM_ParseFunString(CON_WHITEMASK, txt, dlbar, sizeof(dlbar), false);\n\t\tfor (i=0,chr = dlbar; *chr; )\n\t\t{\n\t\t\tchr = Font_Decode(chr, &codeflags, &codepoint);\n\t\t\tx += Font_CharWidth(codeflags, codepoint);\n\t\t\ti++;\n\t\t}\n\n\t\t//if the string is wider than a third of the screen\n\t\tif (x > (right - left)/3)\n\t\t{\n\t\t\t//truncate the file name and add ...\n\t\t\tx += 3*Font_CharWidth(CON_WHITEMASK, '.');\n\t\t\twhile (x > (right - left)/3)\n\t\t\t{\n\t\t\t\tchr = Font_DecodeReverse(chr, dlbar, &codeflags, &codepoint);\n\t\t\t\tx -= Font_CharWidth(codeflags, codepoint);\n\t\t\t}\n\n\t\t\tdlbar[i++] = '.'|CON_WHITEMASK;\n\t\t\tdlbar[i++] = '.'|CON_WHITEMASK;\n\t\t\tdlbar[i++] = '.'|CON_WHITEMASK;\n\t\t\tdlbar[i] = 0;\n\t\t}\n\n\t\t//i is the char index of the dlbar so far, x is the char width of it.\n\n\t\t//add a couple chars\n\t\tdlbar[i] = ':'|CON_WHITEMASK;\n\t\tx += Font_CharWidth(CON_WHITEMASK, ':');\n\t\ti++;\n\t\tdlbar[i] = ' '|CON_WHITEMASK;\n\t\tx += Font_CharWidth(CON_WHITEMASK, ' ');\n\t\ti++;\n\n\t\tCOM_ParseFunString(CON_WHITEMASK, progresspercenttext, dlbar+i, sizeof(dlbar)-i*sizeof(conchar_t), false);\n\t\tfor (chr = &dlbar[i], tw = 0; *chr; )\n\t\t{\n\t\t\tchr = Font_Decode(chr, &codeflags, &codepoint);\n\t\t\ttw += Font_CharWidth(codeflags, codepoint);\n\t\t}\n\n\t\tbarwidth = (right-left) - (x + tw);\n\n\t\t//draw the right hand side\n\t\tx = right - tw;\n\t\tfor (chr = &dlbar[i]; *chr; )\n\t\t{\n\t\t\tchr = Font_Decode(chr, &codeflags, &codepoint);\n\t\t\tx = Font_DrawChar(x, y, codeflags, codepoint);\n\t\t}\n\n\t\t//draw the left hand side\n\t\tx = left;\n\t\tfor (chr = dlbar; chr < &dlbar[i]; )\n\t\t{\n\t\t\tchr = Font_Decode(chr, &codeflags, &codepoint);\n\t\t\tx = Font_DrawChar(x, y, codeflags, codepoint);\n\t\t}\n\n\t\t//and in the middle we have lots of stuff\n\n\t\tbarwidth -= (Font_CharWidth(CON_WHITEMASK, 0xe080) + Font_CharWidth(CON_WHITEMASK, 0xe082));\n\t\tx = Font_DrawChar(x, y, CON_WHITEMASK, 0xe080);\n\t\tbarleft = x;\n\t\tfor(;;)\n\t\t{\n\t\t\tif (x + Font_CharWidth(CON_WHITEMASK, 0xe081) > barleft+barwidth)\n\t\t\t\tbreak;\n\t\t\tx = Font_DrawChar(x, y, CON_WHITEMASK, 0xe081);\n\t\t}\n\t\tx = Font_DrawChar(x, y, CON_WHITEMASK, 0xe082);\n\n\t\tif (progresspercent >= 0)\n\t\t\tFont_DrawChar(barleft+(barwidth*progresspercent)/100 - Font_CharWidth(CON_WHITEMASK, 0xe083)/2, y, CON_WHITEMASK, 0xe083);\n\n\t\ty += Font_CharHeight();\n\t}\n\treturn y;\n}\n\n//draws console selection choices at the top of the screen, if multiple consoles are available\n//its ctrl+tab to switch between them\nint Con_DrawAlternateConsoles(int lines)\n{\n\tchar *txt;\n\tint x, y = 0, lx;\n\tint consshown = 0;\n\tconsole_t *con, *om = con_mouseover;\n\tconchar_t buffer[512], *end, *start;\n\tunsigned int codeflags, codepoint;\n\n\tfor (con = con_head; con; con = con->next)\n\t{\n\t\tif (!(con->flags & (CONF_HIDDEN|CONF_ISWINDOW)))\n\t\t\tconsshown++;\n\t}\n\n\tif (lines == (int)scr_con_target && consshown > 1)\n\t{\n\t\tint mx, my, h;\n\t\tFont_BeginString(font_console, mousecursor_x, mousecursor_y, &mx, &my);\n\t\tFont_BeginString(font_console, 0, y, &x, &y);\n\t\th = Font_CharHeight();\n\t\tfor (x = 0, con = con_head; con; con = con->next)\n\t\t{\n\t\t\tif (con->flags & (CONF_HIDDEN|CONF_ISWINDOW))\n\t\t\t\tcontinue;\n\t\t\ttxt = con->title;\n\n\t\t\t//yeah, om is an evil 1-frame delay. whatever\n\t\t\tend = COM_ParseFunString(CON_WHITEMASK, va(\"^&%c%i%s\", ((con!=om)?'F':'B'), (con==con_current)+con->unseentext*4, txt), buffer, sizeof(buffer), false);\n\n\t\t\tlx = 0;\n\t\t\tfor (lx = x, start = buffer; start < end; )\n\t\t\t{\n\t\t\t\tstart = Font_Decode(start, &codeflags, &codepoint);\n\t\t\t\tlx = Font_CharEndCoord(font_console, lx, codeflags, codepoint);\n\t\t\t}\n\t\t\tif (lx > Font_ScreenWidth())\n\t\t\t{\n\t\t\t\tx = 0;\n\t\t\t\ty += h;\n\t\t\t}\n\t\t\tfor (lx = x, start = buffer; start < end; )\n\t\t\t{\n\t\t\t\tstart = Font_Decode(start, &codeflags, &codepoint);\n\t\t\t\tlx = Font_DrawChar(lx, y, codeflags, codepoint);\n\t\t\t}\n\t\t\tlx += 8;\n\t\t\tif (mx >= x && mx < lx && my >= y && my < y+h)\n\t\t\t\tcon_mouseover = con;\n\t\t\tx = lx;\n\t\t}\n\t\ty+= h;\n\t\tFont_EndString(font_console);\n\n\t\ty = (y*(int)vid.height) / (float)vid.rotpixelheight;\n\t}\n\treturn y;\n}\n\nstatic void Con_DrawImageClip(float x, float y, float w, float h, float bottom, shader_t *pic)\n{\n\tif (bottom < y+h)\n\t{\n\t\tif (bottom <= y)\n\t\t\treturn;\n\t\tR2D_Image(x,y,w,bottom-y,0,0,1,(bottom-y)/h,pic);\n\t}\n\telse\n\t\tR2D_Image(x,y,w,h,0,0,1,1,pic);\n}\n\n//draws the conline_t list bottom-up within the width of the screen until the top of the screen is reached.\n//if text is selected, the selstartline globals will be updated, so make sure the lines persist or check them.\nstatic int Con_DrawConsoleLines(console_t *con, conline_t *l, float displayscroll, int sx, int ex, int y, int top, int selactive, int selsx, int selex, int selsy, int seley, float lineagelimit)\n{\n\tint linecount;\n\tconchar_t *starts[64], *ends[sizeof(starts)/sizeof(starts[0])];\n\tconchar_t *s, *e, *c;\n\tint x;\n\tint charh = Font_CharHeight();\n\tunsigned int codeflags, codepoint;\n\tfloat alphaval = 1;\n\tfloat chop;\n\n\tchop = displayscroll * Font_CharHeight();\n\n\tif (l != con->completionline)\n\tif (l != con->footerline)\n\tif (l != con->current)\n\t{\n\t\ty -= Font_CharHeight();\n\t// draw arrows to show the buffer is backscrolled\n\t\tfor (x = sx ; x<ex; )\n\t\t\tx = (Font_DrawChar (x, y, CON_WHITEMASK, '^')-x)*4+x;\n\n\t\tif (chop)\n\t\t{\n\t\t\ty -= Font_CharHeight();\n\t\t\tchop += 2*Font_CharHeight();\n\t\t}\n\t}\n\n\ty += chop;\n\n\tif (selactive != -1)\n\t{\n\t\tif (!selactive)\n\t\t\tselactive = 2;\t//calculate, but don't draw (to track mouse-over)\n\n\t\t//deactivate the selection if the start and end is outside\n\t\tif (\n\t\t\t(selsx < sx && selex < sx) ||\n\t\t\t(selsx > ex && selex > ex) ||\n\t\t\t(selsy < top && seley < top) ||\n\t\t\t(selsy > y && seley > y)\n\t\t\t)\n\t\t\tselactive = false;\t//don't track it at all\n\n\t\tif (selactive)\n\t\t{\n\t\t\t//clip it\n\t\t\tif (selsx < sx)\n\t\t\t\tselsx = sx;\n\t\t\tif (selex < sx)\n\t\t\t\tselex = sx;\n\n\t\t\tif (selsy > y)\n\t\t\t\tselsy = y;\n\t\t\tif (seley > y)\n\t\t\t\tseley = y;\n\n\t\t\t//scale the y coord to be in lines instead of pixels\n\t\t\tselsy -= y;\n\t\t\tseley -= y;\n\t//\t\tselsy -= charh;\n\t//\t\tseley -= charh;\n\n\t\t\t//invert the selections to make sense, text-wise\n\t\t\t/*if (selsy == seley)\n\t\t\t{\n\t\t\t\t//single line selected backwards\n\t\t\t\tif (selex < selsx)\n\t\t\t\t{\n\t\t\t\t\tx = selex;\n\t\t\t\t\tselex = selsx;\n\t\t\t\t\tselsx = x;\n\t\t\t\t}\n\t\t\t}\n\t\t\t*/\n\t\t\tif (seley <= selsy)\n\t\t\t{\t//selection goes upwards\n\t\t\t\tx = selsy;\n\t\t\t\tselsy = seley;\n\t\t\t\tseley = x;\n\n\t\t\t\tx = selex;\n\t\t\t\tselex = selsx;\n\t\t\t\tselsx = x;\n\t\t\t\tcon->flags &= ~CONF_BACKSELECTION;\n\t\t\t}\n\t\t\telse\n\t\t\t\tcon->flags |= CONF_BACKSELECTION;\n\t//\t\tselsy *= Font_CharHeight();\n\t//\t\tseley *= Font_CharHeight();\n\t\t\tselsy += y;\n\t\t\tseley += y;\n\t\t}\n\t}\n\n\tif (l && l == con->current && l->length == 0 && con->userline != l)\n\t\tl = l->older;\n\tfor (; l; l = l->older)\n\t{\n\t\tshader_t *pic = NULL;\n\t\tfloat picw=0, pich=0;\n\t\ts = (conchar_t*)(l+1);\n\n\t\tif (lineagelimit)\n\t\t{\n\t\t\talphaval = realtime - (l->time+lineagelimit);\n\t\t\tif (alphaval > 0)\n\t\t\t{\n\t\t\t\tfloat fadetime = con->notif_fade?con->notif_fade:1;\n\t\t\t\tif (alphaval > fadetime)\n\t\t\t\t\tbreak;\t//we're done here\n\t\t\t\talphaval = 1 - (alphaval/fadetime);\n\t\t\t}\n\t\t\telse\n\t\t\t\talphaval = 1;\n\t\t}\n\n\t\tif (l->length >= 2 && *s == CON_LINKSTART && (s[1]&CON_CHARMASK) == '\\\\')\n\t\t{\t//leading tag with no text, look for an image in there\n\t\t\tconchar_t *e;\n\t\t\tchar linkinfo[256];\n\t\t\tint linkinfolen = 0;\n\t\t\tfor (e = s+1; e < s+l->length; e++)\n\t\t\t{\n\t\t\t\tif (*e == CON_LINKEND)\n\t\t\t\t{\n\t\t\t\t\tchar *imgname;\n\t\t\t\t\tlinkinfo[linkinfolen] = 0;\n\n\t\t\t\t\timgname = Info_ValueForKey(linkinfo, \"imgptr\");\n\t\t\t\t\tif (*imgname)\n\t\t\t\t\t{\n\t\t\t\t\t\timage_t *img = Image_TextureIsValid(strtoull(imgname, NULL, 0));\n\t\t\t\t\t\tif (img && (img->flags & IF_TEXTYPEMASK)==IF_TEXTYPE_CUBE)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tpic = R_RegisterShader(\"tiprawimgcube\", 0, \"{\\nprogram postproc_equirectangular\\n{\\nmap \\\"$cube:$reflectcube\\\"\\n}\\n}\");\n\t\t\t\t\t\t\tpic->defaulttextures->reflectcube = img;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (img && (img->flags & IF_TEXTYPEMASK)==IF_TEXTYPE_2D_ARRAY)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tpic = R_RegisterShader(\"tiprawimgarray\", 0, \"{\\nprogram default2danim\\n{\\nmap \\\"$2darray:$diffuse\\\"\\n}\\n}\");\n\t\t\t\t\t\t\tpic->defaulttextures->base = img;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tpic = R2D_SafeCachePic(\"tiprawimg\");\n\t\t\t\t\t\t\tpic->defaulttextures->base = img;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (img)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (!img->width || !img->height || !TEXLOADED(img))\n\t\t\t\t\t\t\t\tpicw = pich = 64;\n\t\t\t\t\t\t\telse if (img->width > img->height)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tpicw = 64;\n\t\t\t\t\t\t\t\tpich = (64.0*img->height)/img->width;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tpicw = (64.0*img->width)/img->height;\n\t\t\t\t\t\t\t\tpich = 64;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\n\t\t\t\t\timgname = Info_ValueForKey(linkinfo, \"img\");\n\t\t\t\t\tif (*imgname)\n\t\t\t\t\t{\n\t\t\t\t\t\tchar *fl = Info_ValueForKey(linkinfo, \"imgtype\");\n\t\t\t\t\t\tif (*fl)\n\t\t\t\t\t\t\tpic = R_RegisterCustom(NULL, imgname, atoi(fl), NULL, NULL);\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tpic = R_RegisterPic(imgname, NULL);\n\t\t\t\t\t\tif (pic)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\timgname = Info_ValueForKey(linkinfo, \"s\");\n\t\t\t\t\t\t\tif (*imgname)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tif (pic->width <= 0 || pic->height <= 0)\n\t\t\t\t\t\t\t\t\tpicw = pich = 64;\n\t\t\t\t\t\t\t\telse if (pic->width > pic->height)\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tpicw = atof(imgname);\n\t\t\t\t\t\t\t\t\tpich = picw * (float)pic->height/pic->width;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tpich = atof(imgname);\n\t\t\t\t\t\t\t\t\tpicw = pich * (float)pic->width/pic->height;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\timgname = Info_ValueForKey(linkinfo, \"w\");\n\t\t\t\t\t\t\t\tif (*imgname)\n\t\t\t\t\t\t\t\t\tpicw = atof(imgname);\n\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t\tpicw = -1;\n\t\t\t\t\t\t\t\timgname = Info_ValueForKey(linkinfo, \"h\");\n\t\t\t\t\t\t\t\tif (*imgname)\n\t\t\t\t\t\t\t\t\tpich = atof(imgname);\n\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t\tpich = -1;\n\n\t\t\t\t\t\t\t\tif (picw<0 && pich<0)\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tif (pic->width && pic->height)\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tpich = (pic->height * vid.pixelheight) / vid.height;\n\t\t\t\t\t\t\t\t\t\tpicw = (pic->width * vid.pixelwidth) / vid.width;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t\t\tpicw = pich = 64;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\telse if (picw<0)\n\t\t\t\t\t\t\t\t\tpicw = pich * (float)pic->width/pic->height;\n\t\t\t\t\t\t\t\telse if (pich<0)\n\t\t\t\t\t\t\t\t\tpich = picw * (float)pic->height/pic->width;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tpicw *= charh/8.0;\n\t\t\t\t\t\t\tpich *= charh/8.0;\n\n\t\t\t\t\t\t\tif (picw >= ex-sx)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tpich *= (float)(ex-sx) / picw;\n\t\t\t\t\t\t\t\tpicw = ex-sx;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t//a fall back image (mostly for delay-loading or whatever.\n\t\t\t\t\t\tif (R_GetShaderSizes(pic, NULL, NULL, false) <= 0)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\timgname = Info_ValueForKey(linkinfo, \"fbimg\");\n\t\t\t\t\t\t\tif (*imgname)\n\t\t\t\t\t\t\t\tpic = R_RegisterPic(imgname, NULL);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tlinkinfolen += unicode_encode(linkinfo+linkinfolen, (*e & CON_CHARMASK), sizeof(linkinfo)-1-linkinfolen, true);\n\t\t\t}\n\t\t}\n\n\t\tif (con->flags & CONF_NOWRAP)\n\t\t{\n\t\t\tlinecount = 1;\n\t\t\tstarts[0] = s;\n\t\t\tends[0] = s+l->length;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tlinecount = Font_LineBreaks(s, s+l->length, ex-sx-picw, sizeof(starts)/sizeof(starts[0]), starts, ends);\n\t\t\t//if Con_LineBreaks didn't find any lines at all, then it was an empty line, and we need to ensure that its still drawn\n\t\t\tif (linecount == 0 && !pic)\n\t\t\t{\n\t\t\t\tlinecount = 1;\n\t\t\t\tstarts[0] = ends[0] = s;\n\t\t\t}\n\t\t}\n\n\t\tif (pic)\n\t\t{\n\t\t\tfloat szx = (float)vid.width / vid.pixelwidth;\n\t\t\tfloat szy = (float)vid.height / vid.pixelheight;\n\t\t\tint texth = (linecount) * Font_CharHeight();\n\t\t\tif (R2D_Flush)\n\t\t\t\tR2D_Flush();\n\t\t\tR2D_ImageColours(1.0, 1.0, 1.0, 1.0);\n\t\t\tif (texth > pich)\n\t\t\t{\n\t\t\t\ttexth = pich + (texth-pich)/2;\n\t\t\t\tCon_DrawImageClip(sx*szx, (y-texth)*szy, picw*szx, pich*szy, (y-chop+Font_CharHeight())*szy, pic);\n\t\t\t\tpich = 0;\t//don't pad the text...\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tCon_DrawImageClip(sx*szx, (y-pich)*szy, picw*szx, pich*szy, (y-chop+Font_CharHeight())*szy, pic);\n\t\t\t\tpich -= texth;\n\t\t\t\ty-= pich/2;\t//skip some space above and below the text block, to keep the text and image aligned.\n\n\t\t\t\tif (chop)\n\t\t\t\t\tchop -= pich/2;\n\t\t\t}\n\t\t\tif (R2D_Flush)\n\t\t\t\tR2D_Flush();\n\n//\t\t\tif (selsx < picw && selex < picw)\n\n\t\t\tl->numlines = ceil((texth+pich)/Font_CharHeight());\n\t\t}\n\t\telse\n\t\t\tl->numlines = linecount;\n\n\t\twhile(linecount-- > 0)\n\t\t{\n\t\t\ts = starts[linecount];\n\t\t\te = ends[linecount];\n\n\t\t\ty -= Font_CharHeight();\n\n\t\t\tif (chop)\n\t\t\t{\n\t\t\t\tchop -= Font_CharHeight();\n\t\t\t\tif (chop < 0)\n\t\t\t\t\tchop = 0;\n\t\t\t\telse\n\t\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (top && y < top)\n\t\t\t\tbreak;\n\n\t\t\tif (l->flags & (CONL_BREAKPOINT|CONL_EXECUTION))\n\t\t\t{\n\t\t\t\tif (l->flags & CONL_EXECUTION)\n\t\t\t\t{\n\t\t\t\t\tif (l->flags & CONL_BREAKPOINT)\n\t\t\t\t\t\tR2D_ImageColours(SRGBA(0.3,0.15,0.0, alphaval));\n\t\t\t\t\telse\n\t\t\t\t\t\tR2D_ImageColours(SRGBA(0.3,0.3,0.0, alphaval));\n\t\t\t\t}\n\t\t\t\telse //if (l->flags & CONL_BREAKPOINT)\n\t\t\t\t\tR2D_ImageColours(SRGBA(0.3,0.0,0.0, alphaval));\n\t\t\t\tR2D_FillBlock((sx*(float)vid.width)/(float)vid.rotpixelwidth, (y*vid.height)/(float)vid.rotpixelheight, ((ex - sx)*vid.width)/(float)vid.rotpixelwidth, (Font_CharHeight()*vid.height)/(float)vid.rotpixelheight);\n\t\t\t\tR2D_Flush();\n\t\t\t}\n\n\t\t\tif (selactive < 0)\n\t\t\t{\t//display an existing selection\n\t\t\t\tint sstart = picw;\n\t\t\t\tint send = sstart;\n\t\t\t\tint center;\n\t\t\t\tif (selactive == -2 || l == con->selendline || l == con->selstartline)\n\t\t\t\t{\n\t\t\t\t\tfor (c = s; c < e; )\n\t\t\t\t\t{\n\t\t\t\t\t\tc = Font_Decode(c, &codeflags, &codepoint);\n\t\t\t\t\t\tsend = Font_CharEndCoord(font_console, send, codeflags, codepoint);\n\t\t\t\t\t}\n\t\t\t\t\t//show something on blank lines\n\t\t\t\t\tif (send == sstart)\n\t\t\t\t\t\tsend = Font_CharEndCoord(font_console, send, CON_WHITEMASK, ' ');\n\n\t\t\t\t\tcenter = sx;\n\t\t\t\t\tif (l->flags&CONL_CENTERED)\n\t\t\t\t\t\tcenter += ((ex-sx) - send)/2;\n\t\t\t\t\n\t\t\t\t\tif (l == con->selendline)\n\t\t\t\t\t{\n\t\t\t\t\t\tselactive = -2;\t//all following lines need to be selected, until we see the other end of the selection\n\t\t\t\t\t\tsend = sstart;\n\t\t\t\t\t\tfor (c = s; c < (conchar_t*)(con->selendline+1)+con->selendoffset; )\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tc = Font_Decode(c, &codeflags, &codepoint);\n\t\t\t\t\t\t\tsend = Font_CharEndCoord(font_console, send, codeflags, codepoint);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif (l == con->selstartline)\n\t\t\t\t\t{\n\t\t\t\t\t\tfor (c = s; c < (conchar_t*)(con->selstartline+1)+con->selstartoffset; )\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tc = Font_Decode(c, &codeflags, &codepoint);\n\t\t\t\t\t\t\tsstart = Font_CharEndCoord(font_console, sstart, codeflags, codepoint);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (c == (conchar_t*)(con->selstartline+1)+con->selstartoffset)\n\t\t\t\t\t\t\tselactive = 0;\t//no need to track any other selections.\n\t\t\t\t\t}\n\n\t\t\t\t\tsstart += center;\n\t\t\t\t\tsend += center;\n\n\t\t\t\t\tR2D_ImageColours(SRGBA(0.1,0.1,0.3, alphaval));\n\t\t\t\t\tif (send < sstart)\n\t\t\t\t\t\tR2D_FillBlock((send*(float)vid.width)/(float)vid.rotpixelwidth, (y*vid.height)/(float)vid.rotpixelheight, ((sstart - send)*vid.width)/(float)vid.rotpixelwidth, (Font_CharHeight()*vid.height)/(float)vid.rotpixelheight);\n\t\t\t\t\telse\n\t\t\t\t\t\tR2D_FillBlock((sstart*(float)vid.width)/(float)vid.rotpixelwidth, (y*vid.height)/(float)vid.rotpixelheight, ((send - sstart)*vid.width)/(float)vid.rotpixelwidth, (Font_CharHeight()*vid.height)/(float)vid.rotpixelheight);\n\t\t\t\t\tR2D_Flush();\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (selactive)\n\t\t\t{\n\t\t\t\tif (y+charh >= selsy)\n\t\t\t\t{\n\t\t\t\t\tif (y < seley)\n\t\t\t\t\t{\n\t\t\t\t\t\tint sstart;\n\t\t\t\t\t\tint send;\n\t\t\t\t\t\tint center;\n\t\t\t\t\t\tsend = sstart = picw;\n\t\t\t\t\t\tfor (c = s; c < e; )\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tc = Font_Decode(c, &codeflags, &codepoint);\n\t\t\t\t\t\t\tsend = Font_CharEndCoord(font_console, send, codeflags, codepoint);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t//show something on blank lines\n\t\t\t\t\t\tif (send == sstart)\n\t\t\t\t\t\t\tsend = Font_CharEndCoord(font_console, send, CON_WHITEMASK, ' ');\n\n\t\t\t\t\t\tcenter = sx;\n\t\t\t\t\t\tif (l->flags&CONL_CENTERED)\n\t\t\t\t\t\t\tcenter += ((ex-sx) - send)/2;\n\n\t\t\t\t\t\tif (y+charh >= seley && y < selsy)\n\t\t\t\t\t\t{\t//if they're both on the same line, make sure sx is to the left of ex, so our stuff makes sense\n\t\t\t\t\t\t\tif (selex < selsx)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tx = selex;\n\t\t\t\t\t\t\t\tselex = selsx;\n\t\t\t\t\t\t\t\tselsx = x;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (y+charh >= seley)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tsend = sstart;\n\t\t\t\t\t\t\tfor (c = s; c < e; )\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tc = Font_Decode(c, &codeflags, &codepoint);\n\t\t\t\t\t\t\t\tsend = Font_CharEndCoord(font_console, send, codeflags, codepoint);\n\n\t\t\t\t\t\t\t\tif (send+center > selex)\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tcon->selendline = l;\n\t\t\t\t\t\t\tif (s)\n\t\t\t\t\t\t\t\tcon->selendoffset = c - (conchar_t*)(l+1);\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\tcon->selendoffset = 0;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (y < selsy)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tfor (c = s; c < e; )\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tFont_Decode(c, &codeflags, &codepoint);\n\t\t\t\t\t\t\t\tx = Font_CharEndCoord(font_console, sstart, codeflags, codepoint);\n\t\t\t\t\t\t\t\tif (x+center > selsx)\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\tc = Font_Decode(c, &codeflags, &codepoint);\n\t\t\t\t\t\t\t\tsstart = x;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tcon->selstartline = l;\n\t\t\t\t\t\t\tif (s)\n\t\t\t\t\t\t\t\tcon->selstartoffset = c - (conchar_t*)(l+1);\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\tcon->selstartoffset = 0;\n\n\t\t\t\t\t\t\tif (selactive == 2 && s)\n\t\t\t\t\t\t\t{\t//checking for mouseover\n\t\t\t\t\t\t\t\t//scan earlier to find any link enclosure\n\t\t\t\t\t\t\t\tfor(c--; c >= (conchar_t*)(l+1); c--)\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tif (*c == CON_LINKSTART)\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tselactive = 3;\t//we're mouse-overing a link!\n\t\t\t\t\t\t\t\t\t\tcon->selstartoffset = c - (conchar_t*)(l+1);\n\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tif (*c == CON_LINKEND)\n\t\t\t\t\t\t\t\t\t\tbreak;\t//some other link ended here. don't use its start.\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tif (selactive == 3 && con->selendline==l)\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tfor (; c < (conchar_t*)(l+1)+l->length; c++)\n\t\t\t\t\t\t\t\t\t\tif (*c == CON_LINKEND)\n\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\tcon->selendoffset = c - (conchar_t*)(l+1);\n\t\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\tsstart = picw;\n\t\t\t\t\t\t\t\t\tfor (c = s; c < (conchar_t*)(l+1)+con->selstartoffset; )\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tc = Font_Decode(c, &codeflags, &codepoint);\n\t\t\t\t\t\t\t\t\t\tsstart = Font_CharEndCoord(font_console, sstart, codeflags, codepoint);\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tsend = sstart;\n\t\t\t\t\t\t\t\t\tfor (; c < (conchar_t*)(l+1)+con->selendoffset; )\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tc = Font_Decode(c, &codeflags, &codepoint);\n\t\t\t\t\t\t\t\t\t\tsend = Font_CharEndCoord(font_console, send, codeflags, codepoint);\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tsstart += center;\n\t\t\t\t\t\tsend += center;\n\n\t\t\t\t\t\tif (selactive != 2)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (selactive == 1)\n\t\t\t\t\t\t\t\tR2D_ImageColours(SRGBA(0.1,0.1,0.3, alphaval));\t//selected\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\tR2D_ImageColours(SRGBA(0.3,0.3,0.3, alphaval));\t//mouseover.\n\n\t\t\t\t\t\t\tif (send < sstart)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tcenter = sstart;\n\t\t\t\t\t\t\t\tsstart = send;\n\t\t\t\t\t\t\t\tsend = center;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (selactive == 3)\t//2 pixels high\n\t\t\t\t\t\t\t\tR2D_FillBlock((sstart*vid.width)/(float)vid.rotpixelwidth, ((y+Font_CharHeight()-2)*vid.height)/(float)vid.rotpixelheight, ((send - sstart)*vid.width)/(float)vid.rotpixelwidth, (2*vid.height)/(float)vid.rotpixelheight);\n\t\t\t\t\t\t\telse\t\t\t\t//full height\n\t\t\t\t\t\t\t\tR2D_FillBlock((sstart*vid.width)/(float)vid.rotpixelwidth, (y*vid.height)/(float)vid.rotpixelheight, ((send - sstart)*vid.width)/(float)vid.rotpixelwidth, (Font_CharHeight()*vid.height)/(float)vid.rotpixelheight);\n\t\t\t\t\t\t\tR2D_Flush();\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tR2D_ImageColours(1.0, 1.0, 1.0, alphaval);\n\n\t\t\tx = sx + picw;\n\n\t\t\tif (l->flags&CONL_CENTERED)\n\t\t\t{\n\t\t\t\tint send = 0;\n\t\t\t\tfor (c = s; c < e; )\n\t\t\t\t{\n\t\t\t\t\tc = Font_Decode(c, &codeflags, &codepoint);\n\t\t\t\t\tsend = Font_CharEndCoord(font_console, send, codeflags, codepoint);\n\t\t\t\t}\n\n\t\t\t\tx += ((ex-sx) - send)/2;\n\t\t\t}\n\n\t\t\tFont_LineDraw(x, y, s, e);\n\n\n\t\t\tif (con->userline == l && s <= (conchar_t*)(l+1)+con->useroffset && (conchar_t*)(l+1)+con->useroffset <= e)\n\t\t\tif ((int)(realtime*4)&1)\n\t\t\t{\n\t\t\t\tint sstart;\n\t\t\t\tsstart = picw;\n\t\t\t\tfor (c = s; c < (conchar_t*)(l+1)+con->useroffset; )\n\t\t\t\t{\n\t\t\t\t\tc = Font_Decode(c, &codeflags, &codepoint);\n\t\t\t\t\tsstart = Font_CharEndCoord(font_console, sstart, codeflags, codepoint);\n\t\t\t\t}\n\t\t\t\tFont_DrawChar(sx+sstart, y, CON_WHITEMASK, 0xe00b);\n\t\t\t}\n\n\t\t\tif (y < top)\n\t\t\t\tbreak;\n\t\t}\n\t\ty -= pich/2;\n\t\tif (chop)\n\t\t\tchop -= pich/2;\n\t\tif (y < top)\n\t\t\tbreak;\n\t}\n\treturn y;\n}\n\nvoid Draw_ExpandedString(float x, float y, conchar_t *str);\n\nstatic void Con_DrawModelPreview(model_t *model, float x, float y, float w, float h)\n{\n\tplayerview_t pv;\n\tentity_t ent;\n\tvec3_t fwd, rgt, up;\n\tvec3_t lightpos = {1, 1, 0};\n\tfloat transforms[12];\n\tfloat scale;\n\n\tif (R2D_Flush)\n\t\tR2D_Flush();\n\n\tmemset(&pv, 0, sizeof(pv));\n\n\tCL_DecayLights ();\n\tCL_ClearEntityLists();\n\tV_ClearRefdef(&pv);\n\tr_refdef.drawsbar = false;\n\tV_CalcRefdef(&pv);\n\n\tr_refdef.grect.width = w;\n\tr_refdef.grect.height = h;\n\tr_refdef.grect.x = x;\n\tr_refdef.grect.y = y;\n\tr_refdef.time = realtime;\n\n\tr_refdef.flags = RDF_NOWORLDMODEL;\n\n\tr_refdef.afov = 60;\n\tr_refdef.fov_x = 0;\n\tr_refdef.fov_y = 0;\n\tr_refdef.dirty |= RDFD_FOV;\n\n\tVectorClear(r_refdef.viewangles);\n\tr_refdef.viewangles[0] = 20;\n//\tr_refdef.viewangles[1] = realtime * 90;\n\tAngleVectors(r_refdef.viewangles, fwd, rgt, up);\n\tVectorScale(fwd, -64, r_refdef.vieworg);\n\n\tmemset(&ent, 0, sizeof(ent));\n\tent.model = model;\n\tent.scale = 1;\n\tent.angles[1] = realtime*90;//mods->yaw;\n//\tent.angles[0] = realtime*23.4;//mods->pitch;\n\tAngleVectorsMesh(ent.angles, ent.axis[0], ent.axis[1], ent.axis[2]);\n\tVectorInverse(ent.axis[1]);\n\n\t//ent.origin[2] -= (ent.model->maxs[2]-ent.model->mins[2]) * 0.5 + ent.model->mins[2];\n\n\tent.scale = 1;\n\tscale = max(max(fabs(ent.model->maxs[0]-ent.model->mins[0]), fabs(ent.model->maxs[1]-ent.model->mins[1])), fabs(ent.model->maxs[2]-ent.model->mins[2]));\n\tscale = scale?64.0/scale:1;\n\tent.origin[2] -= (ent.model->maxs[2]-ent.model->mins[2]) * 0.5 + ent.model->mins[2];\n\tVector4Set(ent.shaderRGBAf, 1, 1, 1, 1);\n\tVectorScale(ent.axis[0], scale, ent.axis[0]);\n\tVectorScale(ent.axis[1], scale, ent.axis[1]);\n\tVectorScale(ent.axis[2], scale, ent.axis[2]);\n\tent.topcolour = TOP_DEFAULT;\n\tent.bottomcolour = BOTTOM_DEFAULT;\n//\tent.fatness = sin(realtime)*5;\n\tent.playerindex = -1;\n\tent.skinnum = 0;\n\tent.shaderTime = 0;//realtime;\n\tent.framestate.g[FS_REG].lerpweight[0] = 1;\n\tent.framestate.g[FS_REG].frametime[0] = ent.framestate.g[FS_REG].frametime[1] = realtime;\n\tent.framestate.g[FS_REG].endbone = 0x7fffffff;\n\tif (model->submodelof)\n\t\t;\n\telse\n\t{\n\t\tent.customskin = Mod_RegisterSkinFile(va(\"%s_0.skin\", model->publicname));\n\t\tif (ent.customskin == 0)\n\t\t{\n\t\t\tchar haxxor[MAX_QPATH];\n\t\t\tCOM_StripExtension(model->publicname, haxxor, sizeof(haxxor));\n\t\t\tent.customskin = Mod_RegisterSkinFile(va(\"%s_default.skin\", haxxor));\n\t\t}\n\t}\n\n\tVector4Set(ent.shaderRGBAf, 1,1,1,1);\n\tVectorSet(ent.glowmod, 1,1,1);\n\tent.light_avg[0] = ent.light_avg[1] = ent.light_avg[2] = 0.66;\n\tent.light_range[0] = ent.light_range[1] = ent.light_range[2] = 0.33;\n\n\tV_ApplyRefdef();\n\n\tif (ent.model->camerabone>0 && Mod_GetTag(ent.model, ent.model->camerabone, &ent.framestate, transforms))\n\t{\n\t\tVectorClear(ent.origin);\n\t\tAngleVectorsMesh(ent.angles, ent.axis[0], ent.axis[1], ent.axis[2]);\n\t\tVectorInverse(ent.axis[1]);\n\t\tscale = 1;\n\t\t{\n\t\t\tvec3_t fwd, up;\n\t\t\tfloat camera[12], et[12] = {\n\t\t\t\tent.axis[0][0], ent.axis[1][0], ent.axis[2][0], ent.origin[0],\n\t\t\t\tent.axis[0][1], ent.axis[1][1], ent.axis[2][1], ent.origin[1],\n\t\t\t\tent.axis[0][2], ent.axis[1][2], ent.axis[2][2], ent.origin[2],\n\t\t\t\t};\n\n\t\t\tR_ConcatTransforms((void*)et, (void*)transforms, (void*)camera);\n\t\t\tVectorSet(fwd, camera[2], camera[6], camera[10]);\n\t\t\tVectorNegate(fwd, fwd);\n\t\t\tVectorSet(up, camera[1], camera[5], camera[9]);\n\t\t\tVectorSet(r_refdef.vieworg, camera[3], camera[7], camera[11]);\n\t\t\tVectorAngles(fwd, up, r_refdef.viewangles, false);\n\t\t}\n\t}\n\telse\n\t{\n\t\tent.angles[1] = realtime*90;//mods->yaw;\n\t\tAngleVectorsMesh(ent.angles, ent.axis[0], ent.axis[1], ent.axis[2]);\n\t\tVectorScale(ent.axis[0], scale, ent.axis[0]);\n\t\tVectorScale(ent.axis[1], -scale, ent.axis[1]);\n\t\tVectorScale(ent.axis[2], scale, ent.axis[2]);\n\t}\n\n\tent.scale = scale;\n\n\tVectorNormalize(lightpos);\n\tent.light_dir[0] = DotProduct(lightpos, ent.axis[0]);\n\tent.light_dir[1] = DotProduct(lightpos, ent.axis[1]);\n\tent.light_dir[2] = DotProduct(lightpos, ent.axis[2]);\n\n\tent.light_known = 2;\n\n\tV_AddEntity(&ent);\n\n\tR_RenderView();\n}\n\nstatic void Con_DrawMouseOver(console_t *mouseconsole)\n{\n\tchar *tiptext = NULL;\n\tshader_t *shader = NULL;\n\tmodel_t *model = NULL;\n\tsfx_t\t*audio = NULL;\n\n\tchar *mouseover;\n\tif (!mouseconsole->mouseover || !mouseconsole->mouseover(mouseconsole, &tiptext, &shader))\n\t{\n\t\tmouseover = Con_CopyConsole(mouseconsole, false, true, true);\n\t\tif (mouseover)\n\t\t{\n\t\t\tchar *end = strstr(mouseover, \"^]\");\n\t\t\tchar *info = strchr(mouseover, '\\\\');\n\t\t\tif (!info)\n\t\t\t\tinfo = \"\";\n\t\t\tif (end)\n\t\t\t\t*end = 0;\n#ifdef PLUGINS\n\t\t\tif (!Plug_ConsoleLinkMouseOver(mousecursor_x, mousecursor_y, mouseover+2, info))\n#endif\n\t\t\t{\n\t\t\t\tchar *key;\n\t\t\t\tkey = Info_ValueForKey(info, \"tipimg\");\n\t\t\t\tif (*key)\n\t\t\t\t{\n\t\t\t\t\tchar *fl = Info_ValueForKey(info, \"tipimgtype\");\n\t\t\t\t\tif (*fl)\n\t\t\t\t\t\tshader = R_RegisterCustom(NULL, key, atoi(fl), NULL, NULL);\n\t\t\t\t\telse\n\t\t\t\t\t\tshader = R2D_SafeCachePic(key);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\timage_t *img = NULL;\n\t\t\t\t\tkey = Info_ValueForKey(info, \"tiprawimg\");\n\t\t\t\t\tif (*key)\n\t\t\t\t\t{\n\t\t\t\t\t\timg = Image_FindTexture(key, NULL, IF_NOREPLACE|IF_PREMULTIPLYALPHA|IF_TEXTYPE_ANY);\n\t\t\t\t\t\tif (!img)\n\t\t\t\t\t\t\timg = Image_FindTexture(key, NULL, IF_NOREPLACE|IF_TEXTYPE_ANY);\n\t\t\t\t\t\tif (!img)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tsize_t fsize;\n\t\t\t\t\t\t\tchar *buf;\n\t\t\t\t\t\t\timg = Image_CreateTexture(key, NULL, IF_NOREPLACE|IF_PREMULTIPLYALPHA|IF_TEXTYPE_ANY);\n\t\t\t\t\t\t\tif ((buf = FS_LoadMallocFile (key, &fsize)))\n\t\t\t\t\t\t\t\tImage_LoadTextureFromMemory(img, img->flags|IF_NOWORKER, key, key, buf, fsize);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tkey = Info_ValueForKey(info, \"tipimgptr\");\n\t\t\t\t\tif (*key)\n\t\t\t\t\t\timg = Image_TextureIsValid(strtoull(key, NULL, 0));\n\t\t\t\t\tif (img && img->status == TEX_LOADED)\n\t\t\t\t\t{\n\t\t\t\t\t\tif ((img->flags & IF_TEXTYPEMASK)==IF_TEXTYPE_CUBE)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tshader = R_RegisterShader(\"tiprawimgcube\", 0, \"{\\nprogram postproc_equirectangular\\n{\\nmap \\\"$cube:$reflectcube\\\"\\n}\\n}\");\n\t\t\t\t\t\t\tshader->defaulttextures->reflectcube = img;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if ((img->flags & IF_TEXTYPEMASK)==IF_TEXTYPE_2D_ARRAY)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tshader = R_RegisterShader(\"tiprawimgarray\", 0, \"{\\nprogram default2danim\\n{\\nmap \\\"$2darray:$diffuse\\\"\\n}\\n}\");\n\t\t\t\t\t\t\tshader->defaulttextures->base = img;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if ((img->flags&IF_TEXTYPEMASK) == IF_TEXTYPE_2D)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tshader = R2D_SafeCachePic(\"tiprawimg\");\n\t\t\t\t\t\t\tshader->defaulttextures->base = img;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (shader)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tshader->width = img->width;\n\t\t\t\t\t\t\tshader->height = img->height;\n\t\t\t\t\t\t\tif (shader->width > 320)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tshader->height *= 320.0/shader->width;\n\t\t\t\t\t\t\t\tshader->width = 320;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (shader->height > 240)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tshader->width *= 240.0/shader->height;\n\t\t\t\t\t\t\t\tshader->height = 240;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\tshader = NULL;\n\t\t\t\t\tif (!vrui.enabled)\n\t\t\t\t\t{\n\t\t\t\t\t\tkey = Info_ValueForKey(info, \"modelviewer\");\n\t\t\t\t\t\tif (*key)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tmodel = Mod_ForName(key, MLV_WARN);\n\t\t\t\t\t\t\tif (model->loadstate != MLS_LOADED)\n\t\t\t\t\t\t\t\tmodel = NULL;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tkey = Info_ValueForKey(info, \"playaudio\");\n\t\t\t\t\tif (*key)\n\t\t\t\t\t{\n\t\t\t\t\t\taudio = S_PrecacheSound(key);\n\t\t\t\t\t\tif (audio && audio->loadstate != SLS_LOADED)\n\t\t\t\t\t\t\taudio = NULL;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\ttiptext = Info_ValueForKey(info, \"tip\");\n\t\t\t}\n\t\t\tZ_Free(mouseover);\n\t\t}\n\t}\n\tif ((tiptext && *tiptext) || shader || model || audio)\n\t{\n\t\t//FIXME: draw a proper background.\n\t\t//FIXME: support line breaks.\n\t\tconchar_t buffer[2048], *starts[64], *ends[countof(starts)], *eot;\n\t\tint lines, i, px, py;\n\t\tfloat tw, th;\n\t\tfloat ih = 0, iw = 0;\n\t\tfloat x = mousecursor_x+8;\n\t\tfloat y = mousecursor_y+8;\n\n\t\tFont_BeginString(font_console, x, y, &px, &py);\n\t\teot = COM_ParseFunString(CON_WHITEMASK, tiptext, buffer, sizeof(buffer), false);\n\t\tif (audio)\n\t\t{\n\t\t\tstruct sfxcache_s cache;\n\t\t\tchar name[MAX_OSPATH];\n\t\t\tfloat len;\n\t\t\t*name = 0;\n\t\t\tlen = audio->decoder.querydata?audio->decoder.querydata(audio, &cache, name, sizeof(name)):-1;\n\t\t\tif (len >= 0)\n\t\t\t{\n\t\t\t\teot = COM_ParseFunString(CON_WHITEMASK, va(\"\\n\\n%s\\n%gkhz, %s, %ibit, %g seconds%s\",\n\t\t\t\t\t\t\tname, cache.speed/1000.0, cache.numchannels==1?\"mono\":\"stereo\", QAF_BYTES(cache.format)*8, len, audio->loopstart>=0?\" looped\":\"\"\n\t\t\t\t\t\t\t), eot, sizeof(buffer)-((char*)eot-(char*)buffer), false);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tcache = *(struct sfxcache_s *)audio->decoder.buf;\n\t\t\t\tlen = (double)cache.length / cache.speed;\n\t\t\t\teot = COM_ParseFunString(CON_WHITEMASK, va(\"\\n\\n\\n%gkhz, %s, %ibit, %g seconds%s\",\n\t\t\t\t\t\t\tcache.speed/1000.0, cache.numchannels==1?\"mono\":\"stereo\", QAF_BYTES(cache.format)*8, len, audio->loopstart>=0?\" looped\":\"\"\n\t\t\t\t\t\t\t), eot, sizeof(buffer)-((char*)eot-(char*)buffer), false);\n\t\t\t}\n\t\t}\n\t\tlines = Font_LineBreaks(buffer, eot, (256.0 * vid.pixelwidth) / vid.width, countof(starts), starts, ends);\n\t\tth = (Font_CharHeight()*lines * vid.height) / vid.pixelheight;\n\n\t\tif (model)\n\t\t{\n\t\t\tiw = 128;\n\t\t\tih = 128;\n\t\t}\n\t\telse if (shader)\n\t\t{\n\t\t\tint w, h;\n\t\t\tif (R_GetShaderSizes(shader, &w, &h, false) >= 0)\n\t\t\t{\n\t\t\t\tiw = w;\n\t\t\t\tih = h;\n\t\t\t}\n\t\t\telse\n\t\t\t\tshader = NULL;\n\t\t}\n\t\tif (iw  > (vid.width/4.0))\n\t\t{\n\t\t\tih *= (vid.width/4.0)/iw;\n\t\t\tiw *= (vid.width/4.0)/iw;\n\t\t}\n\t\tif (ih  > (vid.height/4.0))\n\t\t{\n\t\t\tiw *= (vid.width/4.0)/ih;\n\t\t\tih *= (vid.width/4.0)/ih;\n\t\t}\n\n\t\tif (x + iw/2 + 8 + 256 > vid.width)\n\t\t\tx = vid.width - (iw/2 + 8 + 256);\n\t\tif (x < iw/2)\n\t\t\tx = iw/2;\n\t\tx += iw/2 + 8;\n\n\t\tif (y+max(th, ih) > vid.height)\n\t\t\ty = mousecursor_y - 8 - max(th, ih);\n\t\tif (y < 0)\n\t\t\ty = 0;\n\n\t\tFont_BeginString(font_console, x, y + (max(th, ih) - th)/2, &px, &py);\n\t\tfor (i = 0, tw = 0; i < lines; i++)\n\t\t{\n\t\t\tint lw = Font_LineWidth(starts[i], ends[i]);\n\t\t\tif (lw > tw)\n\t\t\t\ttw = lw;\n\t\t}\n\t\ttw *= (float)vid.width / vid.pixelwidth;\n\t\tFont_EndString(font_console);\n\t\tR2D_ImageColours(0, 0, 0, .75);\n\t\tR2D_FillBlock(x, y + (max(th, ih) - th)/2, tw, th);\n\t\tR2D_ImageColours(1, 1, 1, 1);\n\t\tFont_BeginString(font_console, x, y + (max(th, ih) - th)/2, &px, &py);\n\t\tfor (i = 0; i < lines; i++)\n\t\t{\n\t\t\tFont_LineDraw(px, py, starts[i], ends[i]);\n\t\t\tpy += Font_CharHeight();\n\t\t}\n\t\tFont_EndString(font_console);\n\n\t\tif (model)\n\t\t\tCon_DrawModelPreview(model, x-8-iw, y+((th>ih)?(th-ih)/2:0), iw, ih);\n\t\tif (shader)\n\t\t{\n\t\t\tif (th > ih)\n\t\t\t\ty += (th-ih)/2;\n\t\t\tR2D_Image(x-8-iw, y, iw, ih, 0, 0, 1, 1, shader);\n\t\t}\n\t}\n}\n\n/*\n================\nCon_DrawConsole\n\nDraws the console with the solid background\n================\n*/\nvoid Con_DrawConsole (int lines, qboolean noback)\n{\n\textern qboolean scr_con_forcedraw;\n\tint x, y, sx, ex;\n\tconline_t *l;\n\tint selsx, selsy, selex, seley, selactive;\n\tqboolean haveprogress;\n\tconsole_t *w, *mouseconsole;\n\tfloat fadetime;\n\n\tif (!con_current)\n\t\tcon_current = Con_GetMain();\n\n\tcon_mouseover = NULL;\n\n\t//draw any windowed consoles (under main console)\n\tfor (w = con_head; w; w = w->next)\n\t{\n\t\tsrect_t srect;\n\t\tif ((w->flags & (CONF_HIDDEN|CONF_ISWINDOW)) != CONF_ISWINDOW)\n\t\t\tcontinue;\n\n\t\tif (Key_Dest_Has(kdm_cwindows))\n\t\t\tfadetime = 0;\t//nothing fades when focused.\n\t\telse\n\t\t\tfadetime = 4;\n\n\t\tif (w->wnd_w > vid.width)\n\t\t\tw->wnd_w = vid.width;\n\t\tif (w->wnd_h > vid.height)\n\t\t\tw->wnd_h = vid.height;\n\t\tif (w->wnd_w < 64)\n\t\t\tw->wnd_w = 64;\n\t\tif (w->wnd_h < 16)\n\t\t\tw->wnd_h = 16;\n\t\t//windows that move off the top of the screen somehow are bad.\n\t\tif (w->wnd_y > vid.height - 8)\n\t\t\tw->wnd_y = vid.height - 8;\n\t\tif (w->wnd_y < 0)\n\t\t\tw->wnd_y = 0;\n\t\tif (w->wnd_x > vid.width-32)\n\t\t\tw->wnd_x = vid.width-32;\n\t\tif (w->wnd_x < -w->wnd_w+32)\n\t\t\tw->wnd_x = -w->wnd_w+32;\n\n\t\tif (w->wnd_h < 8)\n\t\t\tw->wnd_h = 8;\n\n\t\tif (mousecursor_x >= w->wnd_x && mousecursor_x < w->wnd_x+w->wnd_w && mousecursor_y >= w->wnd_y && mousecursor_y < w->wnd_y+w->wnd_h && mousecursor_y > lines)\n\t\t\tcon_mouseover = w;\n\n\t\tw->mousecursor[0] = mousecursor_x - (w->wnd_x+8);\n\t\tw->mousecursor[1] = mousecursor_y - w->wnd_y;\n\n\t\tif (Key_Dest_Has(kdm_cwindows))\n\t\t{\n\t\t\tint top = 8;\t//padding at the top\n\t\t\tif (con_curwindow==w)\n\t\t\t\tR2D_ImageColours(SRGBA(0.0, 0.05, 0.1, 0.8));\n\t\t\telse\n\t\t\t\tR2D_ImageColours(SRGBA(0.0, 0.05, 0.1, 0.5));\n\t\t\tR2D_FillBlock(w->wnd_x, w->wnd_y, w->wnd_w, w->wnd_h);\n\t\t\tR2D_ImageColours(1, 1, 1, 1);\n\n\t\t\t//fixme: scale up this font...\n\t\t\tDraw_FunStringWidth(w->wnd_x, w->wnd_y, w->title, w->wnd_w-top, 2, (con_curwindow==w)?true:false);\n\t\t\tDraw_FunStringWidth(w->wnd_x+w->wnd_w-top, w->wnd_y, \"X\", top, 2, ((w->buttonsdown == CB_CLOSE && w->mousecursor[0] > w->wnd_w-(8+top) && w->mousecursor[1] < top) || (con_curwindow==w && w->mousecursor[0] >= w->wnd_w-(8+top) && w->mousecursor[0] < w->wnd_w-8 && w->mousecursor[1] >= 0 && w->mousecursor[1] < 8))?true:false);\n\n\t\t\tif (w->backshader || *w->backimage)\n\t\t\t{\n\t\t\t\tshader_t *shader = w->backshader;\n\t\t\t\tif (!shader)\n\t\t\t\t\tshader = w->backshader = R_RegisterPic(w->backimage, NULL);// R_RegisterCustom(w->backimage, SUF_NONE, Shader_DefaultCinematic, w->backimage);\n\t\t\t\tif (shader)\n\t\t\t\t{\n\t\t\t\t\tfloat backx = w->wnd_x+8;\n\t\t\t\t\tfloat backy = w->wnd_y+top;\n\t\t\t\t\tfloat backw = w->wnd_w-16;\n\t\t\t\t\tfloat backh = w->wnd_h-8-top;\n#ifdef HAVE_MEDIA_DECODER\n\t\t\t\t\tcin_t *cin = R_ShaderGetCinematic(shader);\n\t\t\t\t\tif (cin)\n\t\t\t\t\t{\n\t\t\t\t\t\tconst char *url = Media_Send_GetProperty(cin, \"url\");\n\t\t\t\t\t\tif (url)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tfloat x = 0;\n//\t\t\t\t\t\t\tfloat r = x+w->wnd_w-16;\n\t\t\t\t\t\t\tconst char *buttons[] = {\"bck\", \"fwd\", \"rld\", \"home\", \"\", ((w->linebuffered == Con_Navigate)?(char*)key_lines[edit_line]:url)};\n\t\t\t\t\t\t\tconst char *buttoncmds[] = {\"cmd:back\", \"cmd:forward\", \"cmd:refresh\",\n\t\t\t\t\t\t\t#ifdef QUAKETC\t//total conversions should have their own website.\n\t\t\t\t\t\t\t\tENGINEWEBSITE\n\t\t\t\t\t\t\t#else\t\t\t//otherwise use some more useful page, for quake mods.\n\t\t\t\t\t\t\t\t\"cmd:home\"\n\t\t\t\t\t\t\t#endif\n\t\t\t\t\t\t\t\t, NULL, NULL};\n\t\t\t\t\t\t\tfloat tw;\n\t\t\t\t\t\t\tint i, fl;\n\n\t\t\t\t\t\t\tfor (i = 0; i < countof(buttons); i++)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tif (i == countof(buttons)-1)\n\t\t\t\t\t\t\t\t\ttw = FLT_MAX;\n\t\t\t\t\t\t\t\telse if (i == countof(buttons)-2)\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttw = 8+8;\n\t\t\t\t\t\t\t\t\tif (*w->icon)\n\t\t\t\t\t\t\t\t\t\tR2D_Image(w->wnd_x+8+x, w->wnd_y+top, tw, tw, 0, 0, 1, 1, R_RegisterPic(w->icon, NULL));\n\t\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t\t\ttw = 0;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\telse if (i == countof(buttons)-3)\n\t\t\t\t\t\t\t\t\ttw = 40;\n\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t\ttw = 32;\n\t\t\t\t\t\t\t\tfl = con_curwindow==w;\n\t\t\t\t\t\t\t\tif (w->mousecursor[1] >= 8 && w->mousecursor[1] < 16 && w->mousecursor[0] >= x && w->mousecursor[0] < x+tw)\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tfl |= 2;\n\t\t\t\t\t\t\t\t\tif (w->buttonsdown == CB_ACTIONBAR)\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tw->buttonsdown = CB_NONE;\n\t\t\t\t\t\t\t\t\t\tif (buttoncmds[i])\n\t\t\t\t\t\t\t\t\t\t\tMedia_Send_Command(cin, buttoncmds[i]);\n\t\t\t\t\t\t\t\t\t\telse if (w->linebuffered != Con_Navigate)\n\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\tKey_ConsoleReplace(url);\n\t\t\t\t\t\t\t\t\t\t\tw->linebuffered = Con_Navigate;\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tif (tw > w->wnd_w-16 - x)\n\t\t\t\t\t\t\t\t\ttw = w->wnd_w-16 - x;\n\t\t\t\t\t\t\t\tDraw_FunStringWidth(w->wnd_x+8+x, w->wnd_y+top, buttons[i], tw, false, fl);\n\t\t\t\t\t\t\t\tx += tw;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\ttop += 8;\n\t\t\t\t\t\t\tbacky += 8;\n\t\t\t\t\t\t\tbackh -= 8;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t//convert these to pixels.\n\t\t\t\t\t\tbackx = (backx*(int)vid.rotpixelwidth) / (float)vid.width;\n\t\t\t\t\t\tbacky = (backy*(int)vid.rotpixelheight) / (float)vid.height;\n\t\t\t\t\t\tbackw = (backw*(int)vid.rotpixelwidth) / (float)vid.width;\n\t\t\t\t\t\tbackh = (backh*(int)vid.rotpixelheight) / (float)vid.height;\n\t\t\t\t\t\t//snap to pixels. this avoids issues with linear filtering\n\t\t\t\t\t\tbackx = (int)backx;\n\t\t\t\t\t\tbacky = (int)backy;\n\t\t\t\t\t\tbackw = (int)backw;\n\t\t\t\t\t\tbackh = (int)backh;\n\t\t\t\t\t\tMedia_Send_Resize(cin, backw, backh);\n\t\t\t\t\t\t//convert back to screen coords now.\n\t\t\t\t\t\tbackx = (backx*(int)vid.width) / (float)vid.rotpixelwidth;\n\t\t\t\t\t\tbacky = (backy*(int)vid.height) / (float)vid.rotpixelheight;\n\t\t\t\t\t\tbackw = (backw*(int)vid.width) / (float)vid.rotpixelwidth;\n\t\t\t\t\t\tbackh = (backh*(int)vid.height) / (float)vid.rotpixelheight;\n\n\t\t\t\t\t\tMedia_Send_MouseMove(cin, (w->mousecursor[0]) / backw, (w->mousecursor[1]-top) / backh);\n\t\t\t\t\t\tif (con_curwindow==w)\n\t\t\t\t\t\t\tMedia_Send_Command(cin, \"cmd:focus\");\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tMedia_Send_Command(cin, \"cmd:unfocus\");\n\t\t\t\t\t}\n#endif\n\t\t\t\t\tR2D_Image(backx, backy, backw, backh, 0, 0, 1, 1, shader);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tw->unseentext = false;\n\t\t}\n\t\telse\n\t\t\tw->buttonsdown = 0;\n\n\t\tsrect.x = (w->wnd_x+8) / vid.width;\n\t\tsrect.y = (w->wnd_y+8) / vid.height;\n\t\tsrect.width = (w->wnd_w-16) / vid.width;\n\t\tsrect.height = (w->wnd_h-16) / vid.height;\n\t\tsrect.dmin = -99999;\n\t\tsrect.dmax = 99999;\n\t\tsrect.y = (1-srect.y) - srect.height;\n\t\tif (srect.width && srect.height)\n\t\t{\n\t\t\tif (!fadetime)\n\t\t\t{\n\t\t\t\tR2D_ImageColours(SRGBA(0, 0.1, 0.2, 1.0));\n\t\t\t\tif ((w->buttonsdown & CB_SIZELEFT) || (con_curwindow==w && w->mousecursor[0] >= -8 && w->mousecursor[0] < 0 && w->mousecursor[1] >= 8 && w->mousecursor[1] < w->wnd_h))\n\t\t\t\t\tR2D_FillBlock(w->wnd_x, w->wnd_y+8, 8, w->wnd_h-8);\n\t\t\t\tif ((w->buttonsdown & CB_SIZERIGHT) || (con_curwindow==w && w->mousecursor[0] >= w->wnd_w-16 && w->mousecursor[0] < w->wnd_w-8 && w->mousecursor[1] >= 8 && w->mousecursor[1] < w->wnd_h))\n\t\t\t\t\tR2D_FillBlock(w->wnd_x+w->wnd_w-8, w->wnd_y+8, 8, w->wnd_h-8);\n\t\t\t\tif ((w->buttonsdown & CB_SIZEBOTTOM) || (con_curwindow==w && w->mousecursor[0] >= -8 && w->mousecursor[0] < w->wnd_w-8 && w->mousecursor[1] >= w->wnd_h-8 && w->mousecursor[1] < w->wnd_h))\n\t\t\t\t\tR2D_FillBlock(w->wnd_x, w->wnd_y+w->wnd_h-8, w->wnd_w, 8);\n\t\t\t}\n\t\t\tif (R2D_Flush)\n\t\t\t\tR2D_Flush();\n\t\t\tBE_Scissor(&srect);\n\t\t\tCon_DrawOneConsole(w, con_curwindow == w && Key_Dest_Has(kdm_console|kdm_cwindows) == kdm_cwindows, font_console, w->wnd_x+8, w->wnd_y, w->wnd_w-16, w->wnd_h-8, fadetime);\n\t\t\tif (R2D_Flush)\n\t\t\t\tR2D_Flush();\n\t\t\tBE_Scissor(NULL);\n\t\t}\n\n\t\tif (w->selstartline)\n\t\t\tmouseconsole = w;\n\t\tif (!con_curwindow)\n\t\t\tcon_curwindow = w;\n\t}\n\n\t//draw main console...\n\tif (lines > 0 && con_current && !(con_current->flags & CONF_ISWINDOW))\n\t{\n\t\tint top;\n#ifdef QTERM\n\t\tif (qterms)\n\t\t\tQT_Update();\n#endif\n\n// draw the background\n\t\tif (!noback)\n\t\t\tR2D_ConsoleBackground (0, lines, scr_con_forcedraw);\n\n\t\tcon_current->unseentext = false;\n\n\t\tcon_current->vislines = lines;\n\n\t\ttop = Con_DrawAlternateConsoles(lines);\n\n\t\tif (!con_current->display)\n\t\t\tcon_current->display = con_current->current;\n\n\t\tx = 8;\n\t\ty = lines;\n\n\t\tcon_current->mousecursor[0] = mousecursor_x;\n\t\tcon_current->mousecursor[1] = mousecursor_y;\n\t\tif (!(con_current->flags & CONF_KEEPSELECTION))\n\t\t{\n\t\t\tcon_current->selstartline = NULL;\n\t\t\tcon_current->selendline = NULL;\n\t\t}\n\t\tselactive = Key_GetConsoleSelectionBox(con_current, &selsx, &selsy, &selex, &seley);\n\n\t\tif ((con_current->flags & CONF_KEEPSELECTION) && con_current->selstartline && con_current->selendline && con_current->buttonsdown != CB_SELECTED && con_current->buttonsdown != CB_TAPPED)\n\t\t\tselactive = -1;\n\n\t\tFont_BeginString(font_console, x, y, &x, &y);\n\t\tFont_BeginString(font_console, selsx, selsy, &selsx, &selsy);\n\t\tFont_BeginString(font_console, selex, seley, &selex, &seley);\n\t\tex = Font_ScreenWidth();\n\t\tsx = x;\n\t\tex -= sx;\n\n\t\ty -= Font_CharHeight();\n\t\thaveprogress = Con_DrawProgress(x, ex - x, y) != y;\n\t\ty = Con_DrawInput (con_current, Key_Dest_Has(kdm_console), x, ex - x, y, selactive, selsx, selex, selsy, seley);\n\n\t\tl = con_current->display;\n\n\t\ty = Con_DrawConsoleLines(con_current, l, con_current->displayscroll, sx, ex, y, top, selactive, selsx, selex, selsy, seley, 0);\n\n\t\tif (!haveprogress && lines == vid.height)\n\t\t{\n\t\t\tchar *version = version_string();\n\t\t\tint i;\n\t\t\tFont_BeginString(font_console, vid.width, lines, &x, &y);\n\t\t\ty -= Font_CharHeight();\n\t\t\t//assumption: version == ascii\n\t\t\tfor (i = 0; version[i]; i++)\n\t\t\t\tx -= Font_CharWidth(CON_WHITEMASK|CON_HALFALPHA, version[i]);\n\t\t\tfor (i = 0; version[i]; i++)\n\t\t\t\tx = Font_DrawChar(x, y, CON_WHITEMASK|CON_HALFALPHA, version[i]);\n\t\t}\n\n\t\tFont_EndString(font_console);\n\t\tmouseconsole = con_mouseover?con_mouseover:con_current;\n\n\n\t\tif (con_current->buttonsdown == CB_SELECTED || con_current->buttonsdown == CB_TAPPED)\n\t\t{\t//select was released...\n\t\t\tconsole_t *con = con_current;\n\t\t\tchar *buffer;\n\t\t\tqboolean tapped = con->buttonsdown==CB_TAPPED;\n\t\t\tcon->buttonsdown = CB_NONE;\n\t\t\tif (con->selstartline)\n\t\t\t{\n\t\t\t\tif (tapped)\n\t\t\t\t\tcon->flags &= ~CONF_KEEPSELECTION;\n\t\t\t\telse\n\t\t\t\t\tcon->flags |= CONF_KEEPSELECTION;\n\t\t\t\tif (con->userline)\n\t\t\t\t{\n\t\t\t\t\tif (con->flags & CONF_BACKSELECTION)\n\t\t\t\t\t{\n\t\t\t\t\t\tcon->userline = con->selendline;\n\t\t\t\t\t\tcon->useroffset = con->selendoffset;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tcon->userline = con->selstartline;\n\t\t\t\t\t\tcon->useroffset = con->selstartoffset;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (con->selstartline == con->selendline && con->selendoffset <= con->selstartoffset+1)\n\t\t\t\t{\n\t\t\t\t\tif (keydown[K_LSHIFT] || keydown[K_RSHIFT])\n\t\t\t\t\t\t;\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tbuffer = Con_CopyConsole(con, false, true, false);\n\t\t\t\t\t\tif (buffer)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tKey_HandleConsoleLink(con, buffer);\n\t\t\t\t\t\t\tZ_Free(buffer);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tbuffer = Con_CopyConsole(con, true, false, true);\t//don't keep markup if we're copying to the clipboard\n\t\t\t\t\tif (buffer)\n\t\t\t\t\t{\n\t\t\t\t\t\tSys_SaveClipboard(CBT_SELECTION,  buffer);\n\t\t\t\t\t\tZ_Free(buffer);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\telse\n\t\tmouseconsole = con_mouseover?con_mouseover:NULL;\n\n\tif (mouseconsole && mouseconsole->selstartline)\n\t\tCon_DrawMouseOver(mouseconsole);\n}\n\nvoid Con_DrawOneConsole(console_t *con, qboolean focused, struct font_s *font, float fx, float fy, float fsx, float fsy, float lineagelimit)\n{\n\tint selactive, selsx, selsy, selex, seley;\n\tint x, y, sx, sy;\n\tFont_BeginString(font, fx, fy, &x, &y);\n\tFont_BeginString(font, fx+fsx, fy+fsy, &sx, &sy);\n\n\tif (con == con_current && Key_Dest_Has(kdm_console))\n\t{\n\t\tselactive = false;\t//don't change selections if this is the main console and we're looking at the console, because that main console has focus instead anyway.\n\t\tselsx = selsy = selex = seley = 0;\n\t}\n\telse\n\t{\n\t\tselactive = Key_GetConsoleSelectionBox(con, &selsx, &selsy, &selex, &seley);\n\t\tif ((con->flags & CONF_KEEPSELECTION) && con->selstartline && con->selendline)\n\t\t{\n\t\t\tselactive = -1;\n\t\t\tselsx = selsy = selex = seley = 0;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tcon->selstartline = NULL;\n\t\t\tcon->selendline = NULL;\n\t\t\tFont_BeginString(font, selsx, selsy, &selsx, &selsy);\n\t\t\tFont_BeginString(font, selex, seley, &selex, &seley);\n\t\t\tselsx += x;\n\t\t\tselsy += y;\n\t\t\tselex += x;\n\t\t\tseley += y;\n\t\t}\n\t}\n\n\tR2D_ImageColours(1, 1, 1, 1);\n\tsy = Con_DrawInput (con, focused, x, sx, sy, selactive, selsx, selex, selsy, seley);\n\n\tsx -= con->displayoffset;\n\tselsx -= con->displayoffset;\n\tselex -= con->displayoffset;\n\n\tif (!con->display)\n\t\tcon->display = con->current;\n\tCon_DrawConsoleLines(con, con->display, con->displayscroll, x, sx, sy, y, selactive, selsx, selex, selsy, seley, lineagelimit);\n\n\n\tif (con->buttonsdown == CB_SELECTED || con->buttonsdown == CB_TAPPED)\n\t{\t//select was released...\n\t\tchar *buffer;\n\t\tqboolean tapped = con->buttonsdown==CB_TAPPED;\n\t\tcon->buttonsdown = CB_NONE;\n\t\tif (con->selstartline)\n\t\t{\n\t\t\tif (tapped)\n\t\t\t\tcon->flags &= ~CONF_KEEPSELECTION;\n\t\t\telse\n\t\t\t\tcon->flags |= CONF_KEEPSELECTION;\n\t\t\tif (con->userline)\n\t\t\t{\n\t\t\t\tif (con->flags & CONF_BACKSELECTION)\n\t\t\t\t{\n\t\t\t\t\tcon->userline = con->selendline;\n\t\t\t\t\tcon->useroffset = con->selendoffset;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tcon->userline = con->selstartline;\n\t\t\t\t\tcon->useroffset = con->selstartoffset;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (con->selstartline == con->selendline && con->selendoffset <= con->selstartoffset+1)\n\t\t\t{\n\t\t\t\tif (keydown[K_LSHIFT] || keydown[K_RSHIFT])\n\t\t\t\t\t;\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tbuffer = Con_CopyConsole(con, false, true, false);\n\t\t\t\t\tif (buffer)\n\t\t\t\t\t{\n\t\t\t\t\t\tKey_HandleConsoleLink(con, buffer);\n\t\t\t\t\t\tZ_Free(buffer);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tbuffer = Con_CopyConsole(con, true, false, true);\t//don't keep markup if we're copying to the clipboard\n\t\t\t\tif (buffer)\n\t\t\t\t{\n\t\t\t\t\tSys_SaveClipboard(CBT_SELECTION,  buffer);\n\t\t\t\t\tZ_Free(buffer);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tFont_EndString(font);\n}\n\n//false=don't walk over it.\n//true=fine and dandy\n//2=ignore only if at the end.\nstatic qbyte Con_IsTokenChar(unsigned int chr)\n{\n\tif (chr >= 0x80)\t//unicode chars are all continuation\n\t\treturn true;\n\tif (chr == '(' || chr == ')' || chr == '{' || chr == '}')\n\t\treturn false;\n\tif (chr == '/' || chr == '\\\\')\n\t\treturn 2;\t//on left only\n\tif (chr == '.' || chr == ':')\n\t\treturn 3;\t//disallow only if followed by whitespace\n\tif (chr >= 'a' && chr <= 'z')\n\t\treturn true;\n\tif (chr >= 'A' && chr <= 'Z')\n\t\treturn true;\n\tif (chr >= '0' && chr <= '9')\n\t\treturn true;\n\tif (chr == '[' || chr == ']' || chr == '_')\n\t\treturn true;\n\treturn false;\n}\nvoid Con_ExpandConsoleSelection(console_t *con)\n{\n\tconchar_t *cur, *n;\n\tconline_t *l;\n\tconchar_t *lstart;\n\tconchar_t *lend;\n\tunsigned int cf, uc;\n\n\t//no selection to expand...\n\tif (!con->selstartline || !con->selendline)\n\t\treturn;\n\n\tl = con->selstartline;\n\tlstart = (conchar_t*)(l+1);\n\tcur = lstart + con->selstartoffset;\n\n\tif (con->selstartline == con->selendline)\n\t{\n\t\tif (con->selstartoffset+1 == con->selendoffset)\n\t\t{\n\t\t\t//they only selected a single char?\n\t\t\t//fix that up to select the entire token\n\t\t\twhile (cur > lstart)\n\t\t\t{\n\t\t\t\tcur--;\n\t\t\t\tuc = (*cur & CON_CHARMASK);\n\t\t\t\tif (!Con_IsTokenChar(uc))\n\t\t\t\t{\n\t\t\t\t\tcur++;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tif (*cur == CON_LINKSTART)\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tfor (n = lstart+con->selendoffset; con->selendoffset < l->length; )\n\t\t\t{\n\t\t\t\tn = Font_Decode(n, &cf, &uc);\n\t\t\t\tif (Con_IsTokenChar(uc)==3)\n\t\t\t\t\tcontinue;\n\n\t\t\t\tif (Con_IsTokenChar(uc)==1 && lstart[con->selendoffset] != CON_LINKEND)\n\t\t\t\t\tcon->selendoffset = n-lstart;\n\t\t\t\telse\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\t/*while (con->selendoffset > l->length)\n\t\t\t{\n\t\t\t\tuc = (((conchar_t*)(l+1))[con->selendoffset] & CON_CHARMASK);\n\t\t\t\tif (Con_IsTokenChar(uc) == 2)\n\t\t\t\t\tcon->selendoffset--;\n\t\t\t\telse\n\t\t\t\t\tbreak;\n\t\t\t}*/\n\t\t}\n\t}\n\n\t//scan backwards to find any link enclosure\n\tfor(lend = cur-1; lend >= (conchar_t*)(l+1); lend--)\n\t{\n\t\tif (*lend == CON_LINKSTART)\n\t\t{\n\t\t\t//found one\n\t\t\tcur = lend;\n\t\t\tbreak;\n\t\t}\n\t\tif (*lend == CON_LINKEND)\n\t\t{\n\t\t\t//some other link ended here. don't use its start.\n\t\t\tbreak;\n\t\t}\n\t}\n\t//scan forwards to find the end of the selected link\n\tif (l->length && cur < (conchar_t*)(l+1)+l->length && *cur == CON_LINKSTART)\n\t{\n\t\tfor(lend = (conchar_t*)(con->selendline+1) + con->selendoffset; lend < (conchar_t*)(con->selendline+1) + con->selendline->length; lend++)\n\t\t{\n\t\t\tif (*lend == CON_LINKEND)\n\t\t\t{\n\t\t\t\tcon->selendoffset = lend+1 - (conchar_t*)(con->selendline+1);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\tcon->selstartoffset = cur-(conchar_t*)(l+1);\n}\nchar *Con_CopyConsole(console_t *con, qboolean nomarkup, qboolean onlyiflink, qboolean forceutf8)\n{\n\tconchar_t *cur;\n\tconline_t *l;\n\tconchar_t *lend;\n\tchar *result;\n\tint outlen, maxlen;\n\tint finalendoffset;\n\tunsigned int uc;\n\n\tif (!con->selstartline || !con->selendline)\n\t\treturn NULL;\n\n//\tfor (cur = (conchar_t*)(selstartline+1), finalendoffset = 0; cur < (conchar_t*)(selstartline+1) + selstartline->length; cur++, finalendoffset++)\n//\t\tresult[finalendoffset] = *cur & 0xffff;\n\n\tl = con->selstartline;\n\tcur = (conchar_t*)(l+1) + con->selstartoffset;\n\tfinalendoffset = con->selendoffset;\n\n\tif (con->selstartline == con->selendline)\n\t{\n\t\tif (con->selstartoffset+1 == finalendoffset)\n\t\t{\n\t\t\t//they only selected a single char?\n\t\t\t//fix that up to select the entire token\n\t\t\twhile (cur > (conchar_t*)(l+1))\n\t\t\t{\n\t\t\t\tcur--;\n\t\t\t\tuc = (*cur & CON_CHARMASK);\n\t\t\t\tif (!Con_IsTokenChar(uc))\n\t\t\t\t{\n\t\t\t\t\tcur++;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tif (*cur == CON_LINKSTART)\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\twhile (finalendoffset < l->length)\n\t\t\t{\n\t\t\t\tuc = (((conchar_t*)(l+1))[finalendoffset] & CON_CHARMASK);\n\t\t\t\tif (Con_IsTokenChar(uc)==1 && ((conchar_t*)(l+1))[finalendoffset] != CON_LINKEND)\n\t\t\t\t\tfinalendoffset++;\n\t\t\t\telse\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\t/*while (finalendoffset > l->length)\n\t\t\t{\n\t\t\t\tuc = (((conchar_t*)(l+1))[finalendoffset] & CON_CHARMASK);\n\t\t\t\tif (Con_IsTokenChar(uc) == 2)\n\t\t\t\t\tfinalendoffset--;\n\t\t\t\telse\n\t\t\t\t\tbreak;\n\t\t\t}*/\n\t\t}\n\t}\n\n\t//scan backwards to find any link enclosure\n\tfor(lend = cur-1; lend >= (conchar_t*)(l+1); lend--)\n\t{\n\t\tif (*lend == CON_LINKSTART)\n\t\t{\n\t\t\t//found one\n\t\t\tcur = lend;\n\t\t\tbreak;\n\t\t}\n\t\tif (*lend == CON_LINKEND)\n\t\t{\n\t\t\t//some other link ended here. don't use its start.\n\t\t\tbreak;\n\t\t}\n\t}\n\t//scan forwards to find the end of the selected link\n\tif (l->length && cur < (conchar_t*)(l+1)+l->length && *cur == CON_LINKSTART)\n\t{\n\t\tfor(lend = (conchar_t*)(con->selendline+1) + finalendoffset; lend < (conchar_t*)(con->selendline+1) + con->selendline->length; lend++)\n\t\t{\n\t\t\tif (*lend == CON_LINKEND)\n\t\t\t{\n\t\t\t\tfinalendoffset = lend+1 - (conchar_t*)(con->selendline+1);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\telse if (onlyiflink)\n\t\treturn NULL;\n\n\tmaxlen = 1024*1024;\n\tresult = Z_Malloc(maxlen+1);\n\n\toutlen = 0;\n\tfor (;;)\n\t{\n\t\tif (l == con->selendline)\n\t\t\tlend = (conchar_t*)(l+1) + finalendoffset;\n\t\telse\n\t\t\tlend = (conchar_t*)(l+1) + l->length;\n\n\t\toutlen = COM_DeFunString(cur, lend, result + outlen, maxlen - outlen, nomarkup, forceutf8||!!(con->parseflags & PFS_FORCEUTF8)) - result;\n\n\t\tif (l == con->selendline)\n\t\t\tbreak;\n\n\t\tl = l->newer;\n\t\tif (!l)\n\t\t{\n\t\t\tCon_Printf(\"Error: Bad console buffer\\n\");\n\t\t\tbreak;\n\t\t}\n\n\t\tif (outlen+3 > maxlen)\n\t\t\tbreak;\n//#ifdef _WIN32\n//\t\tresult[outlen++] = '\\r';\n//#endif\n\t\tresult[outlen++] = '\\n';\n\t\tcur = (conchar_t*)(l+1);\n\t}\n\tresult[outlen++] = 0;\n\n\treturn result;\n}\n"
  },
  {
    "path": "engine/client/fragstats.c",
    "content": "\n#include \"quakedef.h\"\n\n#ifdef QUAKEHUD\n#define MAX_WEAPONS 64 //fixme: make dynamic.\n\ntypedef enum {\n\t//one componant\n\tff_death,\n\tff_tkdeath,\n\tff_suicide,\n\tff_bonusfrag,\n\tff_tkbonus,\n\tff_flagtouch,\n\tff_flagcaps,\n\tff_flagdrops,\n\n\t//two componant\n\tff_frags,\t\t//must be the first of the two componant\n\tff_fragedby,\n\tff_tkills,\n\tff_tkilledby,\n} fragfilemsgtypes_t;\n\ntypedef struct statmessage_s {\n\tfragfilemsgtypes_t type;\n\tint wid;\n\tsize_t l1, l2;\n\tchar *msgpart1;\n\tchar *msgpart2;\n\tstruct statmessage_s *next;\n} statmessage_t;\n\ntypedef unsigned short stat;\ntypedef struct {\n\tstat totaldeaths;\n\tstat totalsuicides;\n\tstat totalteamkills;\n\tstat totalkills;\n\tstat totaltouches;\n\tstat totalcaps;\n\tstat totaldrops;\n\n\t//I was going to keep track of kills with a certain gun - too much memory\n\t//track only your own and total weapon kills rather than per client\n\tstruct wt_s {\n\t\t//these include you.\n\t\tstat kills;\n\t\tstat teamkills;\n\t\tstat suicides;\t\t\n\n\t\tstat ownkills;\n\t\tstat owndeaths;\n\t\tstat ownteamkills;\n\t\tstat ownteamdeaths;\n\t\tstat ownsuicides;\n\t\tchar *fullname;\n\t\tchar *abrev;\n\t\tchar *image;\n\t\tchar *codename;\n\t} weapontotals[MAX_WEAPONS];\n\n\tstruct ct_s {\n\t\tstat caps;\t\t//times they captured the flag\n\t\tstat drops;\t\t//times lost the flag\n\t\tstat grabs;\t\t//times grabbed flag\n\n\t\tstat owndeaths;\t//times you killed them\n\t\tstat ownkills;\t//times they killed you\n\t\tstat deaths;\t//times they died (including by you)\n\t\tstat kills;\t\t//times they killed (including by you)\n\t\tstat teamkills;\t//times they killed a team member.\n\t\tstat teamdeaths;\t//times they died to a team member.\n\t\tstat suicides;\t//times they were stupid.\n\t} clienttotals[MAX_CLIENTS];\n\n\tqboolean readcaps;\n\tqboolean readkills;\n\tstatmessage_t *message;\n} fragstats_t;\n\nstatic void TrackerCallback(struct cvar_s *var, char *oldvalue);\nstatic cvar_t r_tracker_frags = CVARD(\"r_tracker_frags\", \"0\", \"0: like vanilla quake\\n1: shows only your kills/deaths\\n2: shows all kills\\n\");\nstatic cvar_t r_tracker_time = CVARCD(\"r_tracker_time\", \"4\", TrackerCallback, \"how long it takes for r_tracker messages to start fading\\n\");\nstatic cvar_t r_tracker_fadetime = CVARCD(\"r_tracker_fadetime\", \"1\", TrackerCallback, \"how long it takes for r_tracker messages to fully fade once they start fading\\n\");\nstatic cvar_t r_tracker_x = CVARCD(\"r_tracker_x\", \"0.5\", TrackerCallback, \"left position of the r_tracker messages, as a fraction of the screen's width, eg 0.5\\n\");\nstatic cvar_t r_tracker_y = CVARCD(\"r_tracker_y\", \"0.333\", TrackerCallback, \"top position of the r_tracker messages, as a fraction of the screen's height, eg 0.333\\n\");\nstatic cvar_t r_tracker_w = CVARCD(\"r_tracker_w\", \"0.5\", TrackerCallback, \"width of the r_tracker messages, as a fraction of the screen's width, eg 0.5\\n\");\nstatic cvar_t r_tracker_lines = CVARAFCD(\"r_tracker_lines\", \"8\", \"r_tracker_messages\", 0, TrackerCallback, \"number of r_tracker messages to display\\n\");\nstatic void Tracker_Update(console_t *tracker)\n{\n\ttracker->notif_l = tracker->maxlines = max(1,r_tracker_lines.ival);\n\ttracker->notif_x = r_tracker_x.value;\n\ttracker->notif_y = r_tracker_y.value;\n\ttracker->notif_w = max(0,r_tracker_w.value);\n\ttracker->notif_t = max(0,r_tracker_time.value);\n\ttracker->notif_fade = max(0,r_tracker_fadetime.value);\n\n\t//if its mostly on one side of the screen, align it accordingly.\n\tif (tracker->notif_x + tracker->notif_w*0.5 >= 0.5)\n\t\ttracker->flags |= CONF_NOTIFY_RIGHT;\n\telse\n\t\ttracker->flags &= ~(CONF_NOTIFY_RIGHT);\n}\nstatic void TrackerCallback(struct cvar_s *var, char *oldvalue)\n{\n\tconsole_t *tracker = Con_FindConsole(\"tracker\");\n\tif (tracker)\n\t\tTracker_Update(tracker);\n}\n\n\nstatic fragstats_t fragstats;\n\nint Stats_GetKills(int playernum)\n{\n\treturn fragstats.clienttotals[playernum].kills;\n}\nint Stats_GetTKills(int playernum)\n{\n\treturn fragstats.clienttotals[playernum].teamkills;\n}\nint Stats_GetDeaths(int playernum)\n{\n\treturn fragstats.clienttotals[playernum].deaths;\n}\nint Stats_GetTouches(int playernum)\n{\n\treturn fragstats.clienttotals[playernum].grabs;\n}\nint Stats_GetCaptures(int playernum)\n{\n\treturn fragstats.clienttotals[playernum].caps;\n}\n\nqboolean Stats_HaveFlags(int showtype)\n{\n\tint i;\n\tif (showtype)\n\t\treturn fragstats.readcaps;\n\tfor (i = 0; i < cl.allocated_client_slots; i++)\n\t{\n\t\tif (fragstats.clienttotals[i].caps ||\n\t\t\tfragstats.clienttotals[i].drops ||\n\t\t\tfragstats.clienttotals[i].grabs)\n\t\t\treturn fragstats.readcaps;\n\t}\n\treturn false;\n}\nqboolean Stats_HaveKills(void)\n{\n\treturn fragstats.readkills;\n}\n\nstatic char lastownfragplayer[64];\nstatic float lastownfragtime;\nfloat Stats_GetLastOwnFrag(int seat, char *res, int reslen)\n{\n\tif (seat)\n\t{\n\t\tif (reslen)\n\t\t\t*res = 0;\n\t\treturn 0;\n\t}\n\n\t//erk, realtime was reset?\n\tif (lastownfragtime > (float)realtime)\n\t\tlastownfragtime = 0;\n\n\tQ_strncpyz(res, lastownfragplayer, reslen);\n\treturn realtime - lastownfragtime;\n};\nstatic void Stats_OwnFrag(char *name)\n{\n\tQ_strncpyz(lastownfragplayer, name, sizeof(lastownfragplayer));\n\tlastownfragtime = realtime;\n}\n\nvoid VARGS Stats_Message(char *msg, ...);\n\nqboolean Stats_TrackerImageLoaded(const char *in)\n{\n\tint error;\n\tif (in)\n\t\treturn Font_TrackerValid(unicode_decode(&error, in, &in, true));\n\treturn false;\n}\nstatic char *Stats_GenTrackerImageString(char *in)\n{\t//images are of the form \"foo \\sg\\ bar \\q\\\"\n\t//which should eg be remapped to: \"foo ^Ue200 bar foo ^Ue201\"\n\tchar res[256];\n\tchar image[MAX_QPATH];\n\tchar *outi;\n\tchar *out;\n\tint i;\n\tif (!in || !*in)\n\t\treturn NULL;\n\n\tfor (out = res; *in && out < res+sizeof(res)-10; )\n\t{\n\t\tif (*in == '\\\\')\n\t\t{\n\t\t\tin++;\n\t\t\tfor (outi = image; *in && outi < image+sizeof(image)-10; )\n\t\t\t{\n\t\t\t\tif (*in == '\\\\')\n\t\t\t\t\tbreak;\n\t\t\t\t*outi++ = *in++;\n\t\t\t}\n\t\t\t*outi = 0;\n\t\t\tin++;\n\t\t\t\n\t\t\ti = Font_RegisterTrackerImage(va(\"tracker/%s\", image));\n\t\t\tif (i)\n\t\t\t{\n\t\t\t\tchar hexchars[16] = \"0123456789abcdef\";\n\t\t\t\t*out++ = '^';\n\t\t\t\t*out++ = 'U';\n\t\t\t\t*out++ = hexchars[(i>>12)&15];\n\t\t\t\t*out++ = hexchars[(i>>8)&15];\n\t\t\t\t*out++ = hexchars[(i>>4)&15];\n\t\t\t\t*out++ = hexchars[(i>>0)&15];\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t//just copy the short name over, not much else we can do.\n\t\t\t\tfor(outi = image; out < res+sizeof(res)-10 && *outi; )\n\t\t\t\t\t*out++ = *outi++;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t\t*out++ = *in++;\n\t}\n\t*out = 0;\n\treturn Z_StrDup(res);\n}\n\nvoid Stats_FragMessage(int p1, int wid, int p2, qboolean teamkill)\n{\n\tstatic const char *nonplayers[] = {\n\t\t\"BUG\",\n\t\t\"(teamkill)\",\n\t\t\"(suicide)\",\n\t\t\"(death)\",\n\t\t\"(unknown)\",\n\t\t\"(fixme)\",\n\t\t\"(fixme)\"\n\t};\n\tchar message[512];\n\tconsole_t *tracker;\n\tstruct wt_s *w = &fragstats.weapontotals[wid];\n\tconst char *p1n = (p1 < 0)?nonplayers[-p1]:cl.players[p1].name;\n\tconst char *p2n = (p2 < 0)?nonplayers[-p2]:cl.players[p2].name;\n\tint localplayer = (cl.playerview[0].cam_state == CAM_EYECAM)?cl.playerview[0].cam_spec_track:cl.playerview[0].playernum;\n\n#define YOU_GOOD\t\tS_COLOR_GREEN\n#define YOU_BAD\t\t\tS_COLOR_BLUE\n#define TEAM_GOOD\t\tS_COLOR_GREEN\n#define TEAM_BAD\t\tS_COLOR_RED\n#define TEAM_VBAD\t\tS_COLOR_BLUE\n#define TEAM_NEUTRAL\tS_COLOR_WHITE\t//enemy team thing that does not directly affect us\n#define ENEMY_GOOD\t\tS_COLOR_RED\n#define ENEMY_BAD\t\tS_COLOR_GREEN\n#define ENEMY_NEUTRAL\tS_COLOR_WHITE\n\n\n\tchar *p1c = S_COLOR_WHITE;\n\tchar *p2c = S_COLOR_WHITE;\n\n\tif (!r_tracker_frags.ival)\n\t\treturn;\n\tif (r_tracker_frags.ival < 2)\n\t\tif (p1 != localplayer && p2 != localplayer)\n\t\t\treturn;\n\n\tif (teamkill)\n\t{//team kills/suicides are always considered bad.\n\t\tif (p1 == localplayer)\n\t\t\tp1c = YOU_BAD;\n\t\telse if (cl.teamplay && !strcmp(cl.players[p1].team, cl.players[localplayer].team))\n\t\t\tp1c = TEAM_VBAD;\n\t\telse\n\t\t\tp1c = TEAM_NEUTRAL;\n\t\tp2c = p1c;\n\t}\n\telse if (p1 == p2)\n\t\tp1c = p2c = YOU_BAD;\n\telse if (cl.teamplay && p1 >= 0 && p2 >= 0 && !strcmp(cl.players[p1].team, cl.players[p2].team))\n\t\tp1c = p2c = TEAM_VBAD;\n\telse\n\t{\n\t\tif (p2 >= 0)\n\t\t{\n\t\t\t//us/teammate killing is good - unless it was a teammate.\n\t\t\tif (p2 == localplayer)\n\t\t\t\tp2c = YOU_GOOD;\n\t\t\telse if (cl.teamplay && !strcmp(cl.players[p2].team, cl.players[localplayer].team))\n\t\t\t\tp2c = TEAM_GOOD;\n\t\t\telse\n\t\t\t\tp2c = ENEMY_GOOD;\n\t\t}\n\t\tif (p1 >= 0)\n\t\t{\n\t\t\t//us/teammate dying is bad.\n\t\t\tif (p1 == localplayer)\n\t\t\t\tp1c = YOU_BAD;\n\t\t\telse if (cl.teamplay && !strcmp(cl.players[p1].team, cl.players[localplayer].team))\n\t\t\t\tp1c = TEAM_BAD;\n\t\t\telse\n\t\t\t\tp1c = p2c;\n\t\t}\n\t}\n\n\tQ_snprintfz(message, sizeof(message), \"%s%s ^7%s %s%s\\n\", p2c, p2n, Stats_TrackerImageLoaded(w->image)?w->image:w->abrev, p1c, p1n);\n\n\ttracker = Con_FindConsole(\"tracker\");\n\tif (!tracker)\n\t{\n\t\ttracker = Con_Create(\"tracker\", CONF_HIDDEN|CONF_NOTIFY|CONF_NOTIFY_BOTTOM);\n\t\tTracker_Update(tracker);\n\t}\n\tCon_PrintCon(tracker, message, tracker->parseflags);\n}\n\nvoid Stats_Evaluate(fragfilemsgtypes_t mt, int wid, int p1, int p2)\n{\n\tqboolean u1;\n\tqboolean u2;\n\n\tif (mt == ff_frags || mt == ff_tkills)\n\t{\n\t\tint tmp = p1;\n\t\tp1 = p2;\n\t\tp2 = tmp;\n\t}\n\n\tu1 = (p1 == (cl.playerview[0].playernum));\n\tu2 = (p2 == (cl.playerview[0].playernum));\n\n\tif (p1 == -1)\n\t\tp1 = p2;\n\tif (p2 == -1)\n\t\tp2 = p1;\n\n\t//messages are killed weapon killer\n\tswitch(mt)\n\t{\n\tcase ff_death:\n\t\tif (u1)\n\t\t{\n\t\t\tfragstats.weapontotals[wid].owndeaths++;\n\t\t\tfragstats.weapontotals[wid].ownkills++;\n\t\t}\n\n\t\tfragstats.weapontotals[wid].kills++;\n\t\tif (p1 >= 0)\n\t\t\tfragstats.clienttotals[p1].deaths++;\n\t\tfragstats.totaldeaths++;\n\n\t\tStats_FragMessage(p1, wid, -3, true);\n\n\t\tif (u1)\n\t\t\tStats_Message(\"You died\\n%s deaths: %i\\n\", fragstats.weapontotals[wid].fullname, fragstats.weapontotals[wid].owndeaths);\n\t\tbreak;\n\tcase ff_suicide:\n\t\tif (u1)\n\t\t{\n\t\t\tfragstats.weapontotals[wid].ownsuicides++;\n\t\t\tfragstats.weapontotals[wid].owndeaths++;\n\t\t\tfragstats.weapontotals[wid].ownkills++;\n\t\t}\n\n\t\tfragstats.weapontotals[wid].suicides++;\n\t\tfragstats.weapontotals[wid].kills++;\n\t\tif (p1 >= 0)\n\t\t{\n\t\t\tfragstats.clienttotals[p1].suicides++;\n\t\t\tfragstats.clienttotals[p1].deaths++;\n\t\t}\n\t\tfragstats.totalsuicides++;\n\t\tfragstats.totaldeaths++;\n\n\t\tStats_FragMessage(p1, wid, -2, true);\n\t\tif (u1)\n\t\t\tStats_Message(\"You killed your own dumb self\\n%s suicides: %i (%i)\\n\", fragstats.weapontotals[wid].fullname, fragstats.weapontotals[wid].ownsuicides, fragstats.weapontotals[wid].suicides);\n\t\tbreak;\n\tcase ff_bonusfrag:\n\t\tif (u1)\n\t\t\tfragstats.weapontotals[wid].ownkills++;\n\t\tfragstats.weapontotals[wid].kills++;\n\t\tif (p1 >= 0)\n\t\t\tfragstats.clienttotals[p1].kills++;\n\t\tfragstats.totalkills++;\n\n\t\tStats_FragMessage(-4, wid, p1, false);\n\t\tif (u1)\n\t\t{\n\t\t\tStats_OwnFrag(\"someone\");\n\t\t\tStats_Message(\"You killed someone\\n%s kills: %i\\n\", fragstats.weapontotals[wid].fullname, fragstats.weapontotals[wid].ownkills);\n\t\t}\n\t\tbreak;\n\tcase ff_tkbonus:\n\t\tif (u1)\n\t\t\tfragstats.weapontotals[wid].ownkills++;\n\t\tfragstats.weapontotals[wid].kills++;\n\t\tfragstats.clienttotals[p1].kills++;\n\t\tfragstats.totalkills++;\n\n\t\tif (u1)\n\t\t\tfragstats.weapontotals[wid].ownteamkills++;\n\t\tfragstats.weapontotals[wid].teamkills++;\n\t\tif (p1 >= 0)\n\t\t\tfragstats.clienttotals[p1].teamkills++;\n\t\tfragstats.totalteamkills++;\n\n\t\tStats_FragMessage(-1, wid, p1, true);\n\n\t\tif (u1)\n\t\t{\n\t\t\tStats_Message(\"You killed your teammate\\n%s teamkills: %i\\n\", fragstats.weapontotals[wid].fullname, fragstats.weapontotals[wid].ownteamkills);\n\t\t}\n\t\tbreak;\n\tcase ff_flagtouch:\n\t\tfragstats.clienttotals[p1].grabs++;\n\t\tfragstats.totaltouches++;\n\n\t\tif (u1 && p1 >= 0)\n\t\t{\n\t\t\tStats_Message(\"You grabbed the flag\\nflag grabs: %i (%i)\\n\", fragstats.clienttotals[p1].grabs, fragstats.totaltouches);\n\t\t}\n\t\tbreak;\n\tcase ff_flagcaps:\n\t\tif (p1 >= 0)\n\t\t\tfragstats.clienttotals[p1].caps++;\n\t\tfragstats.totalcaps++;\n\n\t\tif (u1 && p1 >= 0)\n\t\t{\n\t\t\tStats_Message(\"You captured the flag\\nflag captures: %i (%i)\\n\", fragstats.clienttotals[p1].caps, fragstats.totalcaps);\n\t\t}\n\t\tbreak;\n\tcase ff_flagdrops:\n\t\tif (p1 >= 0)\n\t\t\tfragstats.clienttotals[p1].drops++;\n\t\tfragstats.totaldrops++;\n\n\t\tif (u1 && p1 >= 0)\n\t\t{\n\t\t\tStats_Message(\"You dropped the flag\\nflag drops: %i (%i)\\n\", fragstats.clienttotals[p1].drops, fragstats.totaldrops);\n\t\t}\n\t\tbreak;\n\n\t//p1 died, p2 killed\n\tcase ff_frags:\n\tcase ff_fragedby:\n\t\tfragstats.weapontotals[wid].kills++;\n\n\t\tif (p1 >= 0)\n\t\t\tfragstats.clienttotals[p1].deaths++;\n\t\tfragstats.totaldeaths++;\n\t\tif (u1)\n\t\t{\n\t\t\tfragstats.weapontotals[wid].owndeaths++;\n\t\t\tif (p1 >= 0 && p2 >= 0)\n\t\t\t\tStats_Message(\"%s killed you\\n%s deaths: %i (%i/%i)\\n\", cl.players[p2].name, fragstats.weapontotals[wid].fullname, fragstats.clienttotals[p2].owndeaths, fragstats.weapontotals[wid].owndeaths, fragstats.totaldeaths);\n\t\t}\n\n\t\tif (p2 >= 0)\n\t\t\tfragstats.clienttotals[p2].kills++;\n\t\tfragstats.totalkills++;\n\t\tif (u2)\n\t\t{\n\t\t\tif (p1 >= 0)\n\t\t\t\tStats_OwnFrag(cl.players[p1].name);\n\t\t\tfragstats.weapontotals[wid].ownkills++;\n\t\t\tif (p1 >= 0 && p2 >= 0)\n\t\t\t\tStats_Message(\"You killed %s\\n%s kills: %i (%i/%i)\\n\", cl.players[p1].name, fragstats.weapontotals[wid].fullname, fragstats.clienttotals[p2].kills, fragstats.weapontotals[wid].kills, fragstats.totalkills);\n\t\t}\n\n\t\tStats_FragMessage(p1, wid, p2, false);\n\t\tbreak;\n\tcase ff_tkdeath:\n\t\t//killed by a teammate, but we don't know who\n\t\t//kinda useless, but this is all some mods give us\n\t\tfragstats.weapontotals[wid].teamkills++;\n\t\tfragstats.weapontotals[wid].kills++;\n\t\tfragstats.totalkills++;\t\t//its a kill, but we don't know who from\n\t\tfragstats.totalteamkills++;\n\n\t\tif (u1)\n\t\t\tfragstats.weapontotals[wid].owndeaths++;\n\t\tfragstats.clienttotals[p1].teamdeaths++;\n\t\tfragstats.clienttotals[p1].deaths++;\n\t\tfragstats.totaldeaths++;\n\n\t\tStats_FragMessage(p1, wid, -1, true);\n\n\t\tif (u1)\n\t\t\tStats_Message(\"Your teammate killed you\\n%s deaths: %i\\n\", fragstats.weapontotals[wid].fullname, fragstats.weapontotals[wid].owndeaths);\n\t\tbreak;\n\n\tcase ff_tkills:\n\tcase ff_tkilledby:\n\t\t//p1 killed by p2 (kills is already inverted)\n\t\tfragstats.weapontotals[wid].teamkills++;\n\t\tfragstats.weapontotals[wid].kills++;\n\n\t\tif (u1)\n\t\t{\n\t\t\tfragstats.weapontotals[wid].ownteamdeaths++;\n\t\t\tfragstats.weapontotals[wid].owndeaths++;\n\t\t}\n\t\tif (p1 >= 0)\n\t\t{\n\t\t\tfragstats.clienttotals[p1].teamdeaths++;\n\t\t\tfragstats.clienttotals[p1].deaths++;\n\t\t}\n\t\tfragstats.totaldeaths++;\n\n\t\tif (u2)\n\t\t{\n\t\t\tfragstats.weapontotals[wid].ownkills++;\n\t\t\tfragstats.weapontotals[wid].ownkills++;\n\t\t}\n\t\tif (p2 >= 0)\n\t\t{\n\t\t\tfragstats.clienttotals[p2].teamkills++;\n\t\t\tfragstats.clienttotals[p2].kills++;\n\t\t}\n\t\tfragstats.totalkills++;\n\n\t\tfragstats.totalteamkills++;\n\n\t\tStats_FragMessage(p1, wid, p2, false);\n\t\tif (u1 && p2 >= 0)\n\t\t\tStats_Message(\"%s killed you\\n%s deaths: %i (%i/%i)\\n\", cl.players[p2].name, fragstats.weapontotals[wid].fullname, fragstats.clienttotals[p2].owndeaths, fragstats.weapontotals[wid].owndeaths, fragstats.totaldeaths);\n\t\tif (u2 && p1 >= 0 && p2 >= 0)\n\t\t{\n\t\t\tStats_OwnFrag(cl.players[p1].name);\n\t\t\tStats_Message(\"You killed %s\\n%s kills: %i (%i/%i)\\n\", cl.players[p1].name, fragstats.weapontotals[wid].fullname, fragstats.clienttotals[p2].kills, fragstats.weapontotals[wid].kills, fragstats.totalkills);\n\t\t}\n\t\tbreak;\n\t}\n}\n\nstatic int Stats_FindWeapon(char *codename, qboolean create)\n{\n\tint i;\n\n\tif (!strcmp(codename, \"NONE\"))\n\t\treturn 0;\n\tif (!strcmp(codename, \"NULL\"))\n\t\treturn 0;\n\tif (!strcmp(codename, \"NOWEAPON\"))\n\t\treturn 0;\n\n\tfor (i = 1; i < MAX_WEAPONS; i++)\n\t{\n\t\tif (!fragstats.weapontotals[i].codename)\n\t\t{\n\t\t\tfragstats.weapontotals[i].codename = Z_Malloc(strlen(codename)+1);\n\t\t\tstrcpy(fragstats.weapontotals[i].codename, codename);\n\t\t\treturn i;\n\t\t}\n\n\t\tif (!stricmp(fragstats.weapontotals[i].codename, codename))\n\t\t{\n\t\t\tif (create)\n\t\t\t\treturn -2;\n\t\t\treturn i;\n\t\t}\n\t}\n\treturn -1;\n}\n\nstatic void Stats_StatMessage(fragfilemsgtypes_t type, int wid, char *token1, char *token2)\n{\n\tstatmessage_t *ms;\n\tchar *t;\n\tms = Z_Malloc(sizeof(statmessage_t) + strlen(token1)+1 + (token2 && *token2?strlen(token2)+1:0));\n\tt = (char *)(ms+1);\n\tms->msgpart1 = t;\n\tstrcpy(t, token1);\n\tms->l1 = strlen(ms->msgpart1);\n\tif (token2 && *token2)\n\t{\n\t\tt += strlen(t)+1;\n\t\tms->msgpart2 = t;\n\t\tstrcpy(t, token2);\n\t\tms->l2 = strlen(ms->msgpart2);\n\t}\n\tms->type = type;\n\tms->wid = wid;\n\n\tms->next = fragstats.message;\n\tfragstats.message = ms;\n\n\t//we have a message type, save the fact that we have it.\n\tif (type == ff_flagtouch || type == ff_flagcaps || type == ff_flagdrops)\n\t\tfragstats.readcaps = true;\n\tif (type == ff_frags || type == ff_fragedby)\n\t\tfragstats.readkills = true;\n}\n\nvoid Stats_Clear(void)\n{\n\tint i;\n\tstatmessage_t *ms;\n\n\twhile (fragstats.message)\n\t{\n\t\tms = fragstats.message;\n\t\tfragstats.message = ms->next;\n\t\tZ_Free(ms);\n\t}\n\n\tfor (i = 1; i < MAX_WEAPONS; i++)\n\t{\n\t\tif (fragstats.weapontotals[i].codename)\tZ_Free(fragstats.weapontotals[i].codename);\n\t\tif (fragstats.weapontotals[i].fullname)\tZ_Free(fragstats.weapontotals[i].fullname);\n\t\tif (fragstats.weapontotals[i].abrev)\tZ_Free(fragstats.weapontotals[i].abrev);\n\t\tif (fragstats.weapontotals[i].image)\tZ_Free(fragstats.weapontotals[i].image);\n\t}\n\n\tmemset(&fragstats, 0, sizeof(fragstats));\n}\n\n#define Z_Copy(tk) tz = Z_Malloc(strlen(tk)+1);strcpy(tz, tk)\t//remember the braces\n\nvoid Stats_Init(void)\n{\n\tCvar_Register(&r_tracker_frags, NULL);\n\tCvar_Register(&r_tracker_time, NULL);\n\tCvar_Register(&r_tracker_fadetime, NULL);\n\tCvar_Register(&r_tracker_x, NULL);\n\tCvar_Register(&r_tracker_y, NULL);\n\tCvar_Register(&r_tracker_w, NULL);\n\tCvar_Register(&r_tracker_lines, NULL);\n}\nstatic void Stats_LoadFragFile(char *name)\n{\n\tchar filename[MAX_QPATH];\n\tchar *file;\n\tchar *end;\n\tchar *tk, *tz;\n\tchar oend;\n\n\tStats_Clear();\n\n\tstrcpy(filename, name);\n\tCOM_DefaultExtension(filename, \".dat\", sizeof(filename));\n\n\tfile = COM_LoadTempFile(filename, 0, NULL);\n\tif (!file || !*file)\n\t{\n\t\tCon_DPrintf(\"Couldn't load %s\\n\", filename);\n\t\treturn;\n\t}\n\telse\n\t\tCon_DPrintf(\"Loaded %s\\n\", filename);\n\n\toend = 1;\n\tfor (;oend;)\n\t{\n\t\tfor (end = file; *end && *end != '\\n'; end++)\n\t\t\t;\n\t\toend = *end;\n\t\t*end = '\\0';\n\t\tCmd_TokenizeString(file, true, false);\n\t\tfile = end+1;\n\t\tif (!Cmd_Argc())\n\t\t\tcontinue;\n\n\t\ttk = Cmd_Argv(0);\n\t\tif (!stricmp(tk, \"#fragfile\"))\n\t\t{\n\t\t\ttk = Cmd_Argv(1);\n\t\t\t\t if (!stricmp(tk, \"version\"))\t\t{}\n\t\t\telse if (!stricmp(tk, \"gamedir\"))\t\t{}\n\t\t\telse Con_Printf(\"Unrecognised #meta \\\"%s\\\"\\n\", tk);\n\t\t}\n\t\telse if (!stricmp(tk, \"#meta\"))\n\t\t{\n\t\t\ttk = Cmd_Argv(1);\n\t\t\t\t if (!stricmp(tk, \"title\"))\t\t\t{}\n\t\t\telse if (!stricmp(tk, \"description\"))\t{}\n\t\t\telse if (!stricmp(tk, \"author\"))\t\t{}\n\t\t\telse if (!stricmp(tk, \"email\"))\t\t\t{}\n\t\t\telse if (!stricmp(tk, \"webpage\"))\t\t{}\n\t\t\telse {Con_Printf(\"Unrecognised #meta \\\"%s\\\"\\n\", tk);continue;}\n\t\t}\n\t\telse if (!stricmp(tk, \"#define\"))\n\t\t{\n\t\t\ttk = Cmd_Argv(1);\n\t\t\tif (!stricmp(tk, \"weapon_class\") ||\n\t\t\t\t!stricmp(tk, \"wc\"))\t\n\t\t\t{\n\t\t\t\tint wid;\n\n\t\t\t\ttk = Cmd_Argv(2);\n\n\t\t\t\twid = Stats_FindWeapon(tk, true);\n\t\t\t\tif (wid == -1)\n\t\t\t\t{Con_Printf(\"Too many weapon definitions. The max is %i\\n\", MAX_WEAPONS);continue;}\n\t\t\t\telse if (wid < -1)\n\t\t\t\t{Con_Printf(\"Weapon \\\"%s\\\" is already defined\\n\", tk);continue;}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tfragstats.weapontotals[wid].fullname = Z_Copy(Cmd_Argv(3));\n\t\t\t\t\tfragstats.weapontotals[wid].abrev = Z_Copy(Cmd_Argv(4));\n\t\t\t\t\tfragstats.weapontotals[wid].image = Stats_GenTrackerImageString(Cmd_Argv(5));\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (!stricmp(tk, \"obituary\") ||\n\t\t\t\t\t !stricmp(tk, \"obit\"))\n\t\t\t{\n\t\t\t\tint fftype;\n\t\t\t\ttk = Cmd_Argv(2);\n\n\t\t\t\t\t if (!stricmp(tk, \"PLAYER_DEATH\"))\t\t\t{fftype = ff_death;}\n\t\t\t\telse if (!stricmp(tk, \"PLAYER_SUICIDE\"))\t\t{fftype = ff_suicide;}\n\t\t\t\telse if (!stricmp(tk, \"X_FRAGS_UNKNOWN\"))\t\t{fftype = ff_bonusfrag;}\n\t\t\t\telse if (!stricmp(tk, \"X_TEAMKILLS_UNKNOWN\"))\t{fftype = ff_tkbonus;}\n\t\t\t\telse if (!stricmp(tk, \"X_TEAMKILLED_UNKNOWN\"))\t{fftype = ff_tkdeath;}\n\t\t\t\telse if (!stricmp(tk, \"X_FRAGS_Y\"))\t\t\t\t{fftype = ff_frags;}\n\t\t\t\telse if (!stricmp(tk, \"X_FRAGGED_BY_Y\"))\t\t{fftype = ff_fragedby;}\n\t\t\t\telse if (!stricmp(tk, \"X_TEAMKILLS_Y\"))\t\t\t{fftype = ff_tkills;}\n\t\t\t\telse if (!stricmp(tk, \"X_TEAMKILLED_BY_Y\"))\t\t{fftype = ff_tkilledby;}\n\t\t\t\telse {Con_Printf(\"Unrecognised obituary \\\"%s\\\"\\n\", tk);continue;}\n\n\t\t\t\tStats_StatMessage(fftype, Stats_FindWeapon(Cmd_Argv(3), false), Cmd_Argv(4), Cmd_Argv(5));\n\t\t\t}\n\t\t\telse if (!stricmp(tk, \"flag_alert\") ||\n\t\t\t\t\t !stricmp(tk, \"flag_msg\"))\n\t\t\t{\n\t\t\t\tint fftype;\n\t\t\t\ttk = Cmd_Argv(2);\n\n\t\t\t\t\t if (!stricmp(tk, \"X_TOUCHES_FLAG\"))\t\t{fftype = ff_flagtouch;}\n\t\t\t\telse if (!stricmp(tk, \"X_GETS_FLAG\"))\t\t\t{fftype = ff_flagtouch;}\n\t\t\t\telse if (!stricmp(tk, \"X_TAKES_FLAG\"))\t\t\t{fftype = ff_flagtouch;}\n\t\t\t\telse if (!stricmp(tk, \"X_CAPTURES_FLAG\"))\t\t{fftype = ff_flagcaps;}\n\t\t\t\telse if (!stricmp(tk, \"X_CAPS_FLAG\"))\t\t\t{fftype = ff_flagcaps;}\n\t\t\t\telse if (!stricmp(tk, \"X_SCORES\"))\t\t\t\t{fftype = ff_flagcaps;}\n\t\t\t\telse if (!stricmp(tk, \"X_DROPS_FLAG\"))\t\t\t{fftype = ff_flagdrops;}\n\t\t\t\telse if (!stricmp(tk, \"X_FUMBLES_FLAG\"))\t\t{fftype = ff_flagdrops;}\n\t\t\t\telse if (!stricmp(tk, \"X_LOSES_FLAG\"))\t\t\t{fftype = ff_flagdrops;}\n\t\t\t\telse {Con_Printf(\"Unrecognised flag alert \\\"%s\\\"\\n\", tk);continue;}\n\n\t\t\t\tStats_StatMessage(fftype, 0, Cmd_Argv(3), NULL);\n\t\t\t}\n\t\t\telse\n\t\t\t{Con_Printf(\"Unrecognised directive \\\"%s\\\"\\n\", tk);continue;}\n\t\t}\n\t\telse\n\t\t{Con_Printf(\"Unrecognised directive \\\"%s\\\"\\n\", tk);continue;}\n\t}\n}\n\n\nstatic int qm_strcmp(const char *s1, const char *s2)//not like strcmp at all...\n{\n\twhile(*s1)\n\t{\n\t\tif ((*s1++&0x7f)!=(*s2++&0x7f))\n\t\t\treturn 1;\n\t}\n\treturn 0;\n}\n/*\nstatic int qm_stricmp(char *s1, char *s2)//not like strcmp at all...\n{\n\tint c1,c2;\n\twhile(*s1)\n\t{\n\t\tc1 = *s1++&0x7f;\n\t\tc2 = *s2++&0x7f;\n\n\t\tif (c1 >= 'A' && c1 <= 'Z')\n\t\t\tc1 = c1 - 'A' + 'a';\n\n\t\tif (c2 >= 'A' && c2 <= 'Z')\n\t\t\tc2 = c2 - 'A' + 'a';\n\n\t\tif (c1!=c2)\n\t\t\treturn 1;\n\t}\n\treturn 0;\n}\n*/\n\nstatic int Stats_ExtractName(const char **line)\n{\n\tint i;\n\tint bm;\n\tint ml = 0;\n\tint l;\n\tbm = -1;\n\tfor (i = 0; i < cl.allocated_client_slots; i++)\n\t{\n\t\tif (!qm_strcmp(cl.players[i].name, *line))\n\t\t{\n\t\t\tl = strlen(cl.players[i].name);\n\t\t\tif (l > ml)\n\t\t\t{\n\t\t\t\tbm = i;\n\t\t\t\tml = l;\n\t\t\t}\n\t\t}\n\t}\n\t*line += ml;\n\treturn bm;\n}\n\nqboolean Stats_ParsePickups(const char *line)\n{\n#ifdef HAVE_LEGACY\n\t//fixme: rework this to support custom strings, with custom pickup icons\n\tif (!Q_strncmp(line, \"You got the \", 12))\t//weapons, ammo, keys, powerups\n\t\treturn true;\n\tif (!Q_strncmp(line, \"You got armor\", 13))\t//caaake...\n\t\treturn true;\n\tif (!Q_strncmp(line, \"You get \", 8))\t//backpacks\n\t\treturn true;\n\tif (!Q_strncmp(line, \"You receive \", 12)) //%i health\\n\n\t\treturn true;\n#endif\n\treturn false;\n}\n\nqboolean Stats_ParsePrintLine(const char *line)\n{\n\tstatmessage_t *ms;\n\tint p1;\n\tint p2;\n\tconst char *m2;\n\n\tp1 = Stats_ExtractName(&line);\n\tif (p1<0)\t//reject it.\n\t{\n\t\treturn false;\n\t}\n\t\n\tfor (ms = fragstats.message; ms; ms = ms->next)\n\t{\n\t\tif (!Q_strncmp(ms->msgpart1, line, ms->l1))\n\t\t{\n\t\t\tif (ms->type >= ff_frags)\n\t\t\t{\t//two players\n\t\t\t\tm2 = line + ms->l1;\n\t\t\t\tp2 = Stats_ExtractName(&m2);\n\t\t\t\tif ((!ms->msgpart2 && *m2=='\\n') || (ms->msgpart2 && !Q_strncmp(ms->msgpart2, m2, ms->l2)))\n\t\t\t\t{\n\t\t\t\t\tStats_Evaluate(ms->type, ms->wid, p1, p2);\n\t\t\t\t\treturn true;\t//done.\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\t//one player\n\t\t\t\tStats_Evaluate(ms->type, ms->wid, p1, p1);\n\t\t\t\treturn true;\t//done.\n\t\t\t}\n\t\t}\n\t}\n\treturn false;\n}\n\nvoid Stats_NewMap(void)\n{\n\tStats_LoadFragFile(\"fragfile\");\n}\n#endif\n"
  },
  {
    "path": "engine/client/fte_eukara64.h",
    "content": "/* GIMP RGBA C-Source image dump (fte_eukara64.c) */\n\nstatic const struct {\n  unsigned int  \t width;\n  unsigned int  \t height;\n  unsigned int  \t bytes_per_pixel; /* 2:RGB16, 3:RGB, 4:RGBA */ \n  unsigned char \t pixel_data[64 * 64 * 4 + 1];\n} icon = {\n  64, 64, 4,\n  \"\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\"\n  \"\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\"\n  \"\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\255\\252\\270\\26wu~Sigp\\205\"\n  \"a_g\\257YX_\\315USZ\\350SQX\\370OMT\\377OMT\\377SQX\\370USZ\\350ZX_\\315a_g\\257ig\"\n  \"p\\205wu~S\\255\\252\\270\\26\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\"\n  \"\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\"\n  \"\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\"\n  \"\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\"\n  \"\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\"\n  \"\\201~\\211\\1\\0\\0\\0\\0\\0\\0\\0\\0\\203\\177\\2132jgq\\215[Ya\\364FEJ\\377327\\377%$'\\377\"\n  \"\\32\\31\\32\\377\\22\\22\\23\\377\\15\\15\\16\\377\\12\\12\\13\\377\\10\\11\\11\\377\\10\\11\\11\"\n  \"\\377\\12\\12\\13\\377\\15\\15\\15\\377\\22\\22\\23\\377\\32\\31\\32\\377%$'\\377327\\377FE\"\n  \"J\\377[Ya\\364jgq\\215\\202\\177\\2132\\0\\0\\0\\0\\0\\0\\0\\0\\201~\\211\\1\\0\\0\\0\\0\\0\\0\\0\"\n  \"\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\"\n  \"\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\"\n  \"\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\"\n  \"\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0pmv\\3\\0\\0\\0\\0\\320\\313\\335\\11om\"\n  \"vs[Y`\\365?>B\\377&%(\\377\\20\\20\\21\\377\\6\\6\\6\\377\\2\\2\\2\\377\\1\\1\\1\\377\\1\\1\\1\"\n  \"\\377\\3\\3\\3\\377\\5\\5\\5\\377\\5\\5\\6\\377\\7\\7\\7\\377\\7\\7\\7\\377\\5\\5\\6\\377\\5\\5\\5\\377\"\n  \"\\3\\3\\3\\377\\1\\1\\1\\377\\1\\1\\1\\377\\2\\2\\2\\377\\6\\6\\6\\377\\20\\20\\21\\377&%(\\377?>\"\n  \"B\\377[Y`\\365omvs\\320\\313\\332\\11\\0\\0\\0\\0pmv\\3\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\"\n  \"\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\"\n  \"\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\"\n  \"\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0vt}\\3\"\n  \"\\0\\0\\0\\0\\377\\377\\377\\2olv\\222OMT\\377/.1\\377\\22\\22\\23\\377\\5\\5\\5\\377\\1\\1\\0\"\n  \"\\377\\3\\3\\3\\377\\10\\7\\10\\377\\17\\17\\20\\377\\26\\26\\25\\377\\32\\33\\27\\377\\36\\37\\33\"\n  \"\\377#$\\40\\377'(%\\377)*'\\377)*'\\377'(%\\377$%\\40\\377\\37\\40\\33\\377\\32\\33\\27\"\n  \"\\377\\25\\26\\25\\377\\17\\16\\20\\377\\10\\7\\10\\377\\3\\3\\3\\377\\1\\1\\0\\377\\5\\5\\5\\377\"\n  \"\\22\\22\\23\\377/.1\\377OMT\\377olv\\222\\377\\377\\377\\2\\0\\0\\0\\0vt}\\3\\0\\0\\0\\0\\0\\0\"\n  \"\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\"\n  \"\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\"\n  \"\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0~|\\206\\0\\0\\0\\0\\0\\0\\0\"\n  \"\\0\\0{x\\203bTRY\\377,+.\\377\\14\\14\\14\\373\\3\\3\\3\\377\\2\\2\\2\\377\\7\\7\\7\\377\\17\\20\"\n  \"\\16\\377\\27\\31\\23\\377%'#\\377<:D\\377SMk\\377re\\236\\377\\210u\\302\\377\\225\\200\"\n  \"\\334\\377\\236\\206\\354\\377\\236\\206\\357\\377\\235\\204\\357\\377\\231\\202\\355\\377\"\n  \"\\217z\\335\\377\\200o\\303\\377l`\\237\\377RKl\\377<:E\\377()$\\377\\31\\33\\23\\377\\17\"\n  \"\\20\\16\\377\\7\\7\\7\\377\\2\\2\\2\\377\\3\\3\\3\\377\\14\\14\\14\\373,+.\\377TRY\\377{x\\203\"\n  \"b\\0\\0\\0\\0\\0\\0\\0\\0~|\\206\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\"\n  \"\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\"\n  \"\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0ur}\\1\\0\\0\"\n  \"\\0\\0\\355\\350\\374\\13c`h\\32597<\\377\\20\\20\\20\\374\\2\\2\\2\\377\\3\\3\\3\\377\\12\\12\"\n  \"\\12\\377\\24\\25\\22\\377%&\\\"\\377B@P\\377}n\\251\\377\\245\\214\\351\\377\\266\\230\\377\"\n  \"\\377\\275\\235\\377\\377\\262\\225\\377\\377\\252\\221\\370\\377\\242\\214\\352\\377\\235\"\n  \"\\211\\340\\377\\232\\210\\332\\377\\230\\206\\327\\377\\230\\204\\333\\377\\230\\204\\342\"\n  \"\\377\\234\\205\\357\\377\\240\\207\\377\\377\\245\\211\\377\\377\\236\\204\\377\\377\\216\"\n  \"y\\352\\377ob\\251\\377C@Q\\377((#\\377\\26\\27\\22\\377\\12\\12\\12\\377\\3\\3\\3\\377\\2\\2\"\n  \"\\2\\377\\20\\20\\20\\37487;\\377b`h\\325\\355\\350\\374\\13\\0\\0\\0\\0ur{\\1\\0\\0\\0\\0\\0\\0\"\n  \"\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\"\n  \"\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\"\n  \"\\0\\0\\0\\0\\0\\0\\23\\16\\27\\0\\0\\0\\0\\0\\177|\\207VQOV\\377\\36\\36\\40\\377\\5\\5\\5\\377\\1\"\n  \"\\1\\1\\377\\11\\11\\11\\377\\30\\31\\25\\377,--\\377bY\\177\\377\\244\\215\\337\\377\\304\\243\"\n  \"\\377\\377\\272\\235\\377\\377\\252\\224\\356\\377\\242\\221\\331\\377\\234\\221\\307\\377\"\n  \"\\231\\220\\271\\377\\231\\221\\264\\377\\232\\222\\262\\377\\231\\221\\257\\377\\231\\221\"\n  \"\\257\\377\\226\\217\\254\\377\\224\\214\\251\\377\\220\\211\\246\\377\\214\\205\\243\\377\"\n  \"\\212\\201\\244\\377\\210~\\252\\377\\212|\\274\\377\\216|\\324\\377\\230\\200\\373\\377\\234\"\n  \"\\202\\377\\377\\206r\\340\\377ZR~\\37700/\\377\\31\\32\\26\\377\\11\\11\\11\\377\\1\\1\\1\\377\"\n  \"\\5\\5\\6\\377\\36\\36\\40\\377QOV\\377\\177|\\206V\\0\\0\\0\\0\\15\\16\\27\\0\\0\\0\\0\\0\\0\\0\\0\"\n  \"\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\"\n  \"\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0tr|\\0\\0\\0\\0\\0\\0\"\n  \"\\0\\0\\0mjs\\233@?D\\377\\21\\21\\22\\377\\2\\2\\2\\377\\5\\5\\5\\377\\24\\24\\23\\377'(&\\377\"\n  \"g]\\204\\377\\265\\231\\363\\377\\310\\246\\377\\377\\265\\233\\371\\377\\246\\227\\336\\377\"\n  \"\\241\\227\\304\\377\\244\\234\\276\\377\\247\\240\\276\\377\\253\\244\\300\\377\\256\\247\"\n  \"\\301\\377\\260\\250\\302\\377\\260\\251\\301\\377\\256\\250\\277\\377\\256\\250\\276\\377\"\n  \"\\254\\245\\273\\377\\250\\242\\270\\377\\244\\236\\264\\377\\237\\231\\260\\377\\233\\225\"\n  \"\\253\\377\\224\\216\\245\\377\\216\\210\\240\\377\\210\\200\\234\\377\\202{\\233\\377\\203\"\n  \"x\\261\\377\\214x\\333\\377\\227}\\377\\377\\213v\\363\\377\\\\S\\202\\377,,(\\377\\24\\25\"\n  \"\\23\\377\\5\\5\\5\\377\\2\\2\\2\\377\\21\\21\\22\\377@?D\\377ljs\\233\\0\\0\\0\\0\\0\\0\\0\\0tr\"\n  \"{\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\"\n  \"\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0tr{\\0\\0\\0\\0\\0\\0\\0\\0\\0g\"\n  \"dm\\277326\\377\\12\\12\\12\\377\\1\\1\\1\\377\\12\\12\\12\\377\\35\\36\\33\\377FDT\\377\\253\"\n  \"\\223\\341\\377\\316\\254\\377\\377\\270\\237\\372\\377\\247\\231\\331\\377\\247\\236\\305\"\n  \"\\377\\256\\246\\304\\377\\265\\256\\311\\377\\273\\265\\316\\377\\277\\270\\321\\377\\302\"\n  \"\\273\\322\\377\\302\\274\\322\\377\\302\\274\\322\\377\\301\\273\\321\\377\\301\\272\\317\"\n  \"\\377\\277\\270\\316\\377\\274\\266\\313\\377\\272\\263\\310\\377\\264\\257\\303\\377\\260\"\n  \"\\252\\276\\377\\253\\245\\272\\377\\246\\241\\264\\377\\240\\232\\257\\377\\231\\223\\247\"\n  \"\\377\\220\\213\\240\\377\\210\\202\\231\\377\\200y\\225\\377~t\\237\\377\\207u\\323\\377\"\n  \"\\224{\\377\\377\\202o\\335\\377FBT\\377\\37\\40\\33\\377\\12\\12\\12\\377\\1\\1\\1\\377\\12\"\n  \"\\12\\12\\377326\\377gdm\\277\\0\\0\\0\\0\\0\\0\\0\\0tr{\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\"\n  \"\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\"\n  \"\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0gdn\\315--/\\377\\4\\5\\5\\376\\1\\1\\1\\377\\20\\20\\20\\377\"\n  \"$%\\\"\\377sg\\224\\377\\321\\257\\377\\377\\305\\246\\377\\377\\254\\235\\342\\377\\254\\243\"\n  \"\\311\\377\\264\\254\\313\\377\\276\\266\\322\\377\\307\\277\\331\\377\\313\\304\\334\\377\"\n  \"\\316\\310\\337\\377\\317\\311\\340\\377\\317\\311\\341\\377\\317\\310\\341\\377\\315\\305\"\n  \"\\336\\377\\314\\304\\336\\377\\311\\302\\334\\377\\307\\300\\332\\377\\304\\275\\326\\377\"\n  \"\\301\\272\\322\\377\\276\\267\\317\\377\\271\\263\\311\\377\\266\\260\\305\\377\\261\\253\"\n  \"\\300\\377\\254\\247\\272\\377\\247\\241\\264\\377\\240\\232\\255\\377\\231\\223\\247\\377\"\n  \"\\217\\212\\236\\377\\206\\200\\226\\377}v\\217\\377{q\\236\\377\\211u\\347\\377\\220x\\377\"\n  \"\\377aX\\217\\377)*%\\377\\20\\20\\20\\377\\1\\1\\1\\377\\4\\5\\5\\376--/\\377gdm\\315\\0\\0\"\n  \"\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\"\n  \"\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\23\\17\\30\\0\\0\\0\\0\\0gem\\277--\"\n  \"/\\377\\3\\3\\4\\376\\3\\3\\3\\377\\24\\24\\24\\377,-.\\377\\234\\210\\310\\377\\330\\263\\377\"\n  \"\\377\\271\\242\\366\\377\\255\\243\\323\\377\\266\\256\\315\\377\\303\\273\\327\\377\\314\"\n  \"\\305\\337\\377\\323\\314\\344\\377\\327\\320\\350\\377\\330\\321\\352\\377\\331\\321\\353\"\n  \"\\377\\330\\320\\354\\377\\327\\317\\353\\377\\326\\316\\353\\377\\324\\314\\351\\377\\323\"\n  \"\\312\\350\\377\\317\\310\\345\\377\\314\\305\\342\\377\\311\\302\\337\\377\\306\\277\\333\"\n  \"\\377\\304\\274\\330\\377\\277\\270\\322\\377\\274\\265\\317\\377\\270\\261\\311\\377\\263\"\n  \"\\254\\304\\377\\256\\250\\275\\377\\251\\243\\267\\377\\243\\236\\260\\377\\234\\227\\251\"\n  \"\\377\\224\\217\\241\\377\\213\\205\\231\\377\\200z\\220\\377xq\\213\\377}o\\271\\377\\216\"\n  \"u\\377\\377ue\\276\\377331\\377\\24\\24\\23\\377\\3\\3\\3\\377\\3\\4\\4\\376-,/\\377gem\\277\"\n  \"\\0\\0\\0\\0\\15\\16\\27\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\"\n  \"\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0ur}\\1\\0\\0\\0\\0mjs\\233326\\377\\5\\5\\5\\376\\3\"\n  \"\\3\\3\\377\\30\\30\\30\\377236\\377\\262\\230\\342\\377\\324\\260\\377\\377\\263\\242\\354\"\n  \"\\377\\261\\251\\317\\377\\300\\270\\326\\377\\316\\306\\341\\377\\327\\320\\351\\377\\334\"\n  \"\\325\\356\\377\\336\\327\\361\\377\\340\\330\\364\\377\\340\\327\\364\\377\\340\\330\\367\"\n  \"\\377\\336\\326\\366\\377\\333\\322\\365\\377\\331\\320\\363\\377\\327\\315\\360\\377\\324\"\n  \"\\312\\356\\377\\321\\307\\353\\377\\316\\305\\350\\377\\313\\302\\344\\377\\310\\277\\341\"\n  \"\\377\\305\\274\\335\\377\\302\\271\\331\\377\\277\\267\\325\\377\\273\\264\\320\\377\\270\"\n  \"\\262\\314\\377\\264\\256\\306\\377\\257\\251\\277\\377\\252\\243\\271\\377\\244\\237\\262\"\n  \"\\377\\236\\231\\253\\377\\226\\222\\243\\377\\216\\210\\233\\377\\202}\\221\\377xq\\207\\377\"\n  \"wl\\234\\377\\210q\\375\\377|j\\327\\37787:\\377\\27\\27\\30\\377\\3\\3\\3\\377\\5\\5\\5\\376\"\n  \"326\\377ljs\\233\\0\\0\\0\\0ur}\\1\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\"\n  \"\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\177|\\206\\0\\0\\0\\0\\0\\177|\\207V@?D\\377\\12\\12\\12\\377\"\n  \"\\2\\2\\1\\377\\24\\24\\24\\377337\\377\\264\\232\\345\\377\\315\\253\\377\\377\\262\\243\\347\"\n  \"\\377\\266\\255\\321\\377\\306\\276\\333\\377\\324\\315\\347\\377\\334\\325\\356\\377\\342\"\n  \"\\333\\365\\377\\344\\334\\370\\377\\345\\334\\373\\377\\346\\336\\374\\377\\343\\333\\375\"\n  \"\\377\\343\\331\\375\\377\\337\\325\\372\\377\\335\\323\\371\\377\\332\\321\\367\\377\\327\"\n  \"\\316\\364\\377\\325\\314\\362\\377\\322\\310\\357\\377\\320\\306\\354\\377\\314\\303\\351\"\n  \"\\377\\311\\301\\345\\377\\306\\276\\341\\377\\302\\272\\334\\377\\300\\267\\331\\377\\274\"\n  \"\\263\\324\\377\\271\\261\\320\\377\\266\\256\\313\\377\\262\\254\\306\\377\\257\\250\\300\"\n  \"\\377\\251\\242\\270\\377\\243\\235\\262\\377\\235\\231\\253\\377\\227\\222\\243\\377\\216\"\n  \"\\211\\232\\377\\202}\\221\\377xq\\212\\377sj\\221\\377\\203n\\361\\377{h\\331\\37787;\\377\"\n  \"\\23\\24\\23\\377\\2\\2\\1\\377\\12\\12\\12\\377@?D\\377\\200|\\207V\\0\\0\\0\\0~|\\205\\0\\0\\0\"\n  \"\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\357\"\n  \"\\350\\374\\13QOV\\377\\22\\21\\22\\377\\1\\1\\1\\377\\20\\20\\20\\377,..\\377\\257\\226\\342\"\n  \"\\377\\313\\252\\377\\377\\261\\242\\345\\377\\267\\257\\322\\377\\311\\301\\337\\377\\331\"\n  \"\\321\\353\\377\\342\\333\\364\\377\\346\\336\\372\\377\\351\\337\\376\\377\\352\\341\\377\"\n  \"\\377\\351\\340\\377\\377\\346\\334\\377\\377\\343\\331\\377\\377\\341\\327\\377\\377\\336\"\n  \"\\324\\374\\377\\334\\322\\373\\377\\331\\316\\370\\377\\326\\313\\367\\377\\324\\310\\365\"\n  \"\\377\\320\\305\\362\\377\\316\\303\\357\\377\\312\\277\\353\\377\\310\\275\\347\\377\\304\"\n  \"\\272\\344\\377\\301\\267\\336\\377\\277\\265\\333\\377\\273\\263\\326\\377\\271\\260\\322\"\n  \"\\377\\265\\255\\315\\377\\262\\251\\310\\377\\256\\247\\304\\377\\254\\244\\276\\377\\250\"\n  \"\\242\\270\\377\\242\\234\\261\\377\\235\\227\\252\\377\\225\\221\\242\\377\\215\\210\\231\"\n  \"\\377\\202}\\217\\377vp\\207\\377qi\\215\\377\\201l\\357\\377yg\\322\\377333\\377\\17\\17\"\n  \"\\20\\377\\1\\1\\1\\377\\22\\21\\22\\377QOV\\377\\356\\351\\372\\13\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\"\n  \"\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0vt}\\3\\0\\0\\0\\0c`h\\325\\36\\36\\40\"\n  \"\\377\\2\\2\\1\\377\\12\\12\\12\\377$&\\\"\\377\\231\\205\\310\\377\\316\\254\\377\\377\\257\\240\"\n  \"\\345\\377\\267\\255\\320\\377\\312\\302\\337\\377\\332\\323\\354\\377\\345\\335\\367\\377\"\n  \"\\351\\340\\375\\377\\354\\342\\377\\377\\353\\343\\377\\377\\350\\336\\377\\377\\346\\334\"\n  \"\\377\\377\\342\\330\\377\\377\\337\\325\\376\\377\\333\\320\\374\\377\\326\\313\\370\\377\"\n  \"\\323\\310\\365\\377\\316\\303\\360\\377\\313\\277\\354\\377\\307\\274\\350\\377\\303\\270\"\n  \"\\344\\377\\300\\265\\341\\377\\275\\262\\335\\377\\273\\260\\333\\377\\271\\256\\327\\377\"\n  \"\\267\\254\\325\\377\\266\\253\\324\\377\\263\\252\\320\\377\\262\\251\\316\\377\\257\\247\"\n  \"\\312\\377\\256\\247\\306\\377\\254\\244\\302\\377\\250\\241\\276\\377\\247\\240\\272\\377\"\n  \"\\243\\236\\265\\377\\240\\231\\257\\377\\231\\224\\247\\377\\223\\216\\237\\377\\213\\206\"\n  \"\\227\\377\\200{\\215\\377un\\205\\377og\\213\\377\\202l\\371\\377pa\\267\\377++&\\377\\12\"\n  \"\\12\\12\\377\\2\\2\\1\\377\\36\\36\\40\\377c`h\\325\\0\\0\\0\\0vt}\\3\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\"\n  \"\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0|x\\203b97<\\377\\6\\6\\6\\377\\5\\5\\5\\377\\36\"\n  \"\\37\\33\\377qe\\223\\377\\321\\256\\377\\377\\260\\237\\351\\377\\263\\252\\316\\377\\310\"\n  \"\\300\\335\\377\\331\\322\\354\\377\\344\\334\\366\\377\\350\\337\\374\\377\\352\\341\\377\"\n  \"\\377\\350\\337\\377\\377\\344\\331\\376\\377\\337\\325\\374\\377\\331\\317\\367\\377\\321\"\n  \"\\310\\361\\377\\312\\277\\352\\377\\302\\267\\342\\377\\273\\260\\333\\377\\264\\252\\324\"\n  \"\\377\\257\\244\\317\\377\\253\\240\\313\\377\\250\\235\\310\\377\\244\\232\\305\\377\\242\"\n  \"\\230\\303\\377\\237\\225\\277\\377\\235\\224\\276\\377\\233\\221\\272\\377\\231\\221\\267\"\n  \"\\377\\231\\220\\265\\377\\231\\220\\263\\377\\232\\221\\265\\377\\233\\222\\265\\377\\235\"\n  \"\\225\\266\\377\\236\\227\\265\\377\\237\\227\\265\\377\\237\\230\\264\\377\\237\\230\\262\"\n  \"\\377\\236\\230\\260\\377\\232\\225\\252\\377\\225\\217\\242\\377\\217\\213\\234\\377\\210\"\n  \"\\203\\223\\377}x\\212\\377pk\\202\\377oe\\221\\377\\203l\\377\\377^T\\211\\377\\40!\\34\"\n  \"\\377\\5\\5\\5\\377\\6\\6\\6\\37787<\\377{x\\203b\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\"\n  \"\\0\\0\\0\\0\\0\\0\\0pmv\\3\\377\\377\\377\\2TRY\\377\\20\\20\\21\\374\\2\\2\\1\\377\\24\\24\\23\"\n  \"\\377FCT\\377\\313\\252\\377\\377\\263\\235\\363\\377\\256\\244\\312\\377\\302\\272\\327\\377\"\n  \"\\324\\315\\346\\377\\337\\330\\361\\377\\343\\333\\367\\377\\344\\334\\373\\377\\337\\326\"\n  \"\\370\\377\\327\\315\\361\\377\\313\\303\\347\\377\\302\\271\\335\\377\\266\\254\\322\\377\"\n  \"\\260\\246\\315\\377\\261\\246\\316\\377\\246\\234\\304\\377\\223\\212\\257\\377\\204|\\240\"\n  \"\\377wp\\221\\377me\\205\\377f_~\\377b[y\\377`Yw\\377_Yv\\377_Xu\\377aZw\\377c\\\\y\\377\"\n  \"jc\\201\\377qi\\210\\377yq\\220\\377\\202z\\232\\377\\212\\202\\242\\377\\205~\\234\\377\"\n  \"\\205~\\231\\377\\210\\202\\234\\377\\215\\207\\240\\377\\221\\212\\243\\377\\223\\215\\244\"\n  \"\\377\\223\\216\\241\\377\\220\\212\\235\\377\\213\\206\\226\\377\\203\\177\\217\\377xt\\205\"\n  \"\\377nh|\\377qd\\251\\377\\200k\\373\\377GBT\\377\\24\\25\\23\\377\\2\\2\\1\\377\\20\\20\\21\"\n  \"\\374TRY\\377\\377\\377\\377\\2pmv\\3\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0om\"\n  \"v\\222,+.\\377\\2\\2\\2\\377\\11\\11\\11\\377()&\\377\\246\\217\\341\\377\\277\\241\\377\\377\"\n  \"\\250\\236\\315\\377\\273\\263\\320\\377\\315\\306\\340\\377\\331\\322\\352\\377\\334\\325\"\n  \"\\357\\377\\331\\320\\356\\377\\316\\306\\346\\377\\276\\265\\325\\377\\261\\252\\311\\377\"\n  \"\\262\\252\\314\\377\\233\\223\\264\\377\\201z\\227\\377_Yo\\37785C\\377\\\"\\37)\\377\\34\"\n  \"\\32#\\377\\26\\25\\34\\377\\22\\20\\27\\377\\17\\16\\23\\377\\16\\15\\21\\377\\15\\14\\22\\377\"\n  \"\\16\\14\\22\\377\\16\\15\\22\\377\\16\\15\\22\\377\\16\\15\\22\\377\\17\\16\\24\\377\\22\\20\\27\"\n  \"\\377\\26\\24\\34\\377\\33\\31\\\"\\377\\\"\\37)\\377,*5\\377GCS\\377^Xn\\377mg\\177\\377ys\"\n  \"\\213\\377zu\\213\\377{u\\212\\377\\203~\\222\\377\\207\\203\\225\\377\\210\\203\\223\\377\"\n  \"\\205\\201\\220\\377}y\\211\\377to\\200\\377jcx\\377wf\\327\\377tc\\313\\377-.*\\377\\11\"\n  \"\\11\\11\\377\\2\\2\\2\\377,+.\\377olv\\222\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\201~\\211\"\n  \"\\1\\320\\312\\333\\11OMT\\377\\14\\14\\14\\373\\3\\3\\3\\377\\27\\30\\25\\377e\\\\\\203\\377\\310\"\n  \"\\247\\377\\377\\247\\231\\333\\377\\257\\247\\305\\377\\304\\275\\327\\377\\316\\307\\337\"\n  \"\\377\\320\\311\\341\\377\\306\\277\\331\\377\\264\\256\\310\\377\\261\\251\\307\\377\\232\"\n  \"\\223\\256\\377pk\\200\\37731<\\377\\37\\36%\\377\\21\\20\\23\\377\\13\\12\\16\\377\\12\\11\"\n  \"\\14\\377\\11\\10\\14\\377\\10\\7\\12\\377\\12\\11\\15\\377\\14\\13\\17\\377\\14\\13\\20\\377\\7\"\n  \"\\6\\12\\377\\2\\2\\3\\377\\1\\1\\1\\377\\1\\1\\1\\377\\2\\2\\3\\377\\6\\5\\10\\377\\13\\12\\17\\377\"\n  \"\\15\\14\\17\\377\\13\\12\\16\\377\\12\\11\\13\\377\\11\\10\\13\\377\\12\\11\\15\\377\\14\\13\\17\"\n  \"\\377\\20\\17\\24\\377\\34\\32\\40\\377(&.\\377FCQ\\377d_q\\377pk\\177\\377rm~\\377ws\\203\"\n  \"\\377~y\\211\\377}y\\207\\377xs\\203\\377lhz\\377i`\\202\\377}g\\371\\377WOz\\377\\31\\32\"\n  \"\\26\\377\\3\\3\\3\\377\\14\\14\\14\\373OMT\\377\\320\\312\\333\\11\\201~\\211\\1\\0\\0\\0\\0\\0\"\n  \"\\0\\0\\0\\0\\0\\0\\0omvs/-1\\377\\3\\4\\4\\377\\12\\12\\12\\377---\\377\\260\\225\\363\\377\\262\"\n  \"\\231\\366\\377\\244\\234\\300\\377\\267\\260\\312\\377\\302\\274\\322\\377\\300\\272\\320\"\n  \"\\377\\257\\250\\300\\377\\253\\245\\275\\377\\216\\207\\240\\377DAM\\377\\35\\34!\\377\\14\"\n  \"\\14\\16\\377\\13\\12\\14\\377\\7\\6\\10\\377\\7\\6\\10\\377\\14\\12\\16\\377\\17\\16\\23\\377\\21\"\n  \"\\20\\25\\377\\32\\27!\\377,(8\\37783I\\377;6N\\377$\\40/\\377\\7\\6\\12\\377\\0\\0\\0\\377\"\n  \"\\0\\0\\0\\377\\5\\4\\6\\377\\33\\31%\\37794K\\37794J\\3771,?\\377#\\40-\\377\\26\\24\\34\\377\"\n  \"\\21\\17\\25\\377\\16\\15\\22\\377\\12\\11\\14\\377\\7\\7\\10\\377\\11\\11\\13\\377\\15\\14\\17\"\n  \"\\377\\27\\26\\33\\377-*2\\377TP^\\377idt\\377jet\\377qnz\\377tp}\\377okz\\377gbt\\377\"\n  \"o`\\265\\377wd\\332\\377323\\377\\12\\12\\11\\377\\4\\4\\4\\377.-1\\377pmvs\\0\\0\\0\\0\\0\\0\"\n  \"\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0ZX`\\365\\22\\22\\23\\377\\2\\2\\2\\377\\24\\25\\21\\377aX~\\377\\301\"\n  \"\\241\\377\\377\\240\\223\\317\\377\\250\\241\\275\\377\\265\\256\\306\\377\\260\\252\\275\"\n  \"\\377\\252\\244\\271\\377\\227\\221\\246\\377DAL\\377\\27\\26\\33\\377\\20\\17\\22\\377\\10\"\n  \"\\10\\11\\377\\4\\5\\6\\377\\10\\10\\12\\377\\13\\13\\15\\377\\25\\24\\32\\377,)6\\377MGb\\377\"\n  \"zo\\235\\377\\223\\207\\301\\377\\230\\212\\311\\377\\223\\206\\306\\377\\211|\\270\\377U\"\n  \"Ms\\377\\15\\14\\23\\377\\1\\1\\1\\377\\0\\0\\0\\377\\13\\11\\16\\377>8T\\377{o\\246\\377\\202\"\n  \"v\\256\\377\\205z\\260\\377\\200u\\250\\377vl\\231\\377[Ts\\37795H\\377&#.\\377\\23\\22\"\n  \"\\27\\377\\14\\14\\17\\377\\10\\7\\11\\377\\6\\6\\7\\377\\14\\14\\16\\377\\24\\23\\26\\377,*1\\377\"\n  \"UQ]\\377fao\\377fcn\\377kgu\\377gbs\\377d]z\\377wc\\354\\377ULv\\377\\26\\27\\23\\377\"\n  \"\\2\\2\\2\\377\\22\\22\\23\\377ZX`\\365\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\203\\177\\2132?>B\\377\"\n  \"\\5\\5\\5\\377\\7\\7\\7\\377%'\\\"\\377\\237\\210\\340\\377\\255\\224\\365\\377\\234\\223\\270\"\n  \"\\377\\246\\237\\267\\377\\241\\234\\256\\377\\237\\231\\254\\377a]j\\377\\34\\33\\37\\377\"\n  \"\\20\\20\\22\\377\\6\\5\\7\\377\\4\\4\\5\\377\\12\\11\\13\\377\\21\\17\\23\\377&$/\\377YSn\\377\"\n  \"\\213\\201\\257\\377\\243\\226\\321\\377\\261\\242\\347\\377\\261\\241\\352\\377\\270\\246\"\n  \"\\363\\377\\274\\253\\372\\377\\264\\241\\365\\377\\235\\215\\334\\377]S\\202\\377\\22\\20\"\n  \"\\31\\377\\2\\1\\1\\377\\0\\0\\0\\377\\16\\14\\23\\377F>b\\377\\213}\\302\\377\\236\\216\\331\"\n  \"\\377\\245\\225\\341\\377\\237\\221\\326\\377\\224\\207\\304\\377\\214\\200\\266\\377\\210\"\n  \"}\\255\\377wo\\225\\377f_~\\377B=P\\377\\\"\\40(\\377\\20\\20\\23\\377\\10\\10\\12\\377\\5\\5\"\n  \"\\6\\377\\13\\13\\14\\377\\27\\27\\32\\37775<\\377]Ze\\377]Zd\\377b^l\\377`Zm\\377m^\\267\"\n  \"\\377n^\\303\\377++&\\377\\6\\7\\7\\377\\5\\5\\5\\377>>B\\377\\202\\177\\2132\\0\\0\\0\\0\\0\\0\"\n  \"\\0\\0jgq\\215&%(\\377\\1\\1\\1\\377\\20\\20\\16\\377B?O\\377\\273\\234\\377\\377\\233\\214\"\n  \"\\320\\377\\227\\220\\252\\377\\225\\217\\242\\377\\222\\215\\236\\377:8?\\377\\24\\23\\26\"\n  \"\\377\\10\\10\\11\\377\\3\\3\\3\\377\\10\\10\\11\\377\\21\\21\\24\\377,)4\\377vn\\216\\377\\242\"\n  \"\\226\\306\\377\\255\\241\\331\\377\\266\\247\\351\\377\\305\\264\\374\\377\\320\\276\\377\"\n  \"\\377\\331\\304\\377\\377\\335\\307\\377\\377\\334\\305\\377\\377\\315\\267\\377\\377\\257\"\n  \"\\235\\373\\377aV\\213\\377\\23\\20\\34\\377\\2\\1\\2\\377\\0\\0\\0\\377\\16\\14\\24\\377I@i\\377\"\n  \"\\233\\212\\334\\377\\262\\240\\373\\377\\277\\253\\377\\377\\275\\252\\376\\377\\265\\244\"\n  \"\\366\\377\\253\\234\\343\\377\\241\\224\\321\\377\\223\\207\\274\\377\\203z\\245\\377}t\\232\"\n  \"\\377nh\\205\\377QL_\\377#\\\"*\\377\\20\\17\\23\\377\\6\\6\\7\\377\\5\\5\\5\\377\\20\\20\\22\\377\"\n  \"%#(\\377SQZ\\377XT_\\377ZWe\\377`X}\\377ta\\351\\377D@S\\377\\17\\20\\16\\377\\1\\1\\1\\377\"\n  \"&%(\\377jgp\\215\\0\\0\\0\\0\\0\\0\\0\\0[Y`\\364\\20\\21\\21\\377\\3\\3\\3\\377\\30\\31\\23\\377\"\n  \"yj\\251\\377\\260\\224\\377\\377\\216\\206\\254\\377\\212\\204\\231\\377\\211\\204\\225\\377\"\n  \",+0\\377\\17\\16\\20\\377\\4\\5\\5\\377\\4\\4\\4\\377\\15\\15\\17\\377\\36\\35#\\377mf~\\377\\245\"\n  \"\\233\\304\\377\\254\\240\\323\\377\\277\\261\\360\\377\\317\\276\\376\\377\\332\\307\\377\"\n  \"\\377\\340\\315\\377\\377\\346\\321\\377\\377\\351\\322\\377\\377\\353\\322\\377\\377\\346\"\n  \"\\316\\377\\377\\325\\275\\377\\377\\267\\241\\377\\377cW\\221\\377\\23\\21\\34\\377\\2\\1\\2\"\n  \"\\377\\0\\0\\0\\377\\16\\14\\25\\377JAn\\377\\240\\216\\356\\377\\271\\245\\377\\377\\306\\261\"\n  \"\\377\\377\\310\\262\\377\\377\\301\\255\\377\\377\\273\\251\\375\\377\\263\\243\\356\\377\"\n  \"\\253\\234\\336\\377\\241\\224\\315\\377\\224\\211\\271\\377\\205{\\242\\377un\\214\\377l\"\n  \"e~\\377FBP\\377\\32\\31\\35\\377\\12\\11\\13\\377\\4\\4\\5\\377\\13\\12\\14\\377\\40\\37\\\"\\377\"\n  \"PMW\\377QNZ\\377WRe\\377l[\\311\\377^S\\224\\377\\34\\35\\27\\377\\3\\3\\3\\377\\20\\21\\21\"\n  \"\\377[Y`\\364\\0\\0\\0\\0\\255\\252\\270\\26FDJ\\377\\6\\6\\6\\377\\10\\10\\10\\377%&#\\377\\236\"\n  \"\\206\\352\\377\\233\\207\\340\\377\\203}\\227\\377\\206\\201\\222\\377539\\377\\16\\15\\17\"\n  \"\\377\\3\\3\\3\\377\\5\\5\\5\\377\\21\\17\\22\\377<9E\\377\\226\\216\\260\\377\\247\\234\\304\"\n  \"\\377\\273\\257\\343\\377\\317\\300\\376\\377\\332\\312\\377\\377\\342\\317\\377\\377\\346\"\n  \"\\323\\377\\377\\352\\324\\377\\377\\355\\325\\377\\377\\357\\325\\377\\377\\356\\323\\377\"\n  \"\\377\\352\\317\\377\\377\\326\\275\\377\\377\\267\\242\\377\\377dX\\226\\377\\24\\21\\36\\377\"\n  \"\\1\\1\\2\\377\\0\\0\\0\\377\\16\\14\\26\\377KBs\\377\\241\\216\\366\\377\\273\\245\\377\\377\"\n  \"\\313\\263\\377\\377\\312\\263\\377\\377\\305\\261\\377\\377\\301\\254\\377\\377\\271\\250\"\n  \"\\372\\377\\263\\244\\354\\377\\255\\236\\337\\377\\244\\230\\320\\377\\233\\220\\301\\377\"\n  \"\\216\\204\\254\\377{t\\221\\377mf\\177\\377[Vh\\377%#*\\377\\15\\14\\16\\377\\3\\3\\4\\377\"\n  \"\\12\\12\\13\\377%$(\\377PMW\\377OKZ\\377`T\\232\\377kZ\\302\\377++(\\377\\10\\7\\10\\377\"\n  \"\\6\\6\\6\\377FDJ\\377\\255\\252\\267\\26wu\\177S327\\377\\2\\2\\2\\377\\17\\17\\20\\377;:C\"\n  \"\\377\\253\\217\\377\\377\\214~\\277\\377{v\\213\\377VS]\\377\\21\\21\\22\\377\\3\\4\\4\\377\"\n  \"\\6\\5\\6\\377\\21\\21\\24\\377PM[\\377\\236\\226\\270\\377\\253\\241\\310\\377\\304\\271\\353\"\n  \"\\377\\326\\306\\377\\377\\335\\315\\377\\377\\345\\321\\377\\377\\347\\323\\377\\377\\352\"\n  \"\\325\\377\\377\\354\\325\\377\\377\\356\\325\\377\\377\\357\\324\\377\\377\\357\\323\\377\"\n  \"\\377\\350\\314\\377\\377\\330\\275\\377\\377\\272\\241\\377\\377fX\\233\\377\\24\\21!\\377\"\n  \"\\1\\1\\2\\377\\0\\0\\0\\377\\17\\15\\27\\377LBy\\377\\243\\215\\377\\377\\275\\245\\377\\377\"\n  \"\\312\\262\\377\\377\\312\\263\\377\\377\\307\\261\\377\\377\\302\\255\\377\\377\\274\\250\"\n  \"\\377\\377\\267\\246\\365\\377\\257\\240\\345\\377\\252\\234\\332\\377\\243\\230\\315\\377\"\n  \"\\233\\221\\277\\377\\220\\210\\254\\377\\200x\\225\\377je{\\377a]n\\377)'.\\377\\16\\15\"\n  \"\\17\\377\\3\\3\\3\\377\\17\\17\\20\\37775<\\377LIU\\377VNt\\377n[\\327\\377<:D\\377\\16\\16\"\n  \"\\16\\377\\2\\2\\2\\377326\\377wu~Sjgp\\205%$'\\377\\1\\1\\1\\377\\25\\26\\25\\377RKj\\377\"\n  \"\\255\\220\\377\\377~t\\237\\377xt\\206\\377\\40\\37\\\"\\377\\7\\7\\10\\377\\4\\4\\4\\377\\23\"\n  \"\\22\\26\\377OKY\\377\\234\\226\\264\\377\\255\\243\\311\\377\\307\\273\\352\\377\\326\\310\"\n  \"\\377\\377\\336\\316\\377\\377\\343\\321\\377\\377\\347\\323\\377\\377\\347\\323\\377\\377\"\n  \"\\351\\323\\377\\377\\354\\324\\377\\377\\355\\323\\377\\377\\357\\323\\377\\377\\355\\320\"\n  \"\\377\\377\\352\\315\\377\\377\\332\\274\\377\\377\\271\\240\\377\\377eW\\237\\377\\24\\21\"\n  \"\\\"\\377\\1\\1\\2\\377\\0\\0\\0\\377\\17\\15\\31\\377LB}\\377\\244\\215\\377\\377\\277\\244\\377\"\n  \"\\377\\313\\261\\377\\377\\312\\262\\377\\377\\310\\261\\377\\377\\301\\254\\377\\377\\275\"\n  \"\\250\\377\\377\\266\\244\\370\\377\\257\\240\\351\\377\\254\\235\\340\\377\\244\\230\\321\"\n  \"\\377\\237\\223\\305\\377\\231\\217\\270\\377\\216\\205\\246\\377\\177x\\225\\377gbu\\377\"\n  \"[Wg\\377$#(\\377\\11\\11\\12\\377\\5\\5\\6\\377\\36\\34\\40\\377IEP\\377LHZ\\377iX\\321\\377\"\n  \"MFg\\377\\24\\24\\23\\377\\1\\1\\1\\377%$'\\377igp\\205`_f\\257\\32\\31\\33\\377\\1\\1\\1\\377\"\n  \"\\32\\33\\26\\377nb\\235\\377\\240\\206\\377\\377wp\\215\\377[Xe\\377\\24\\24\\26\\377\\3\\4\"\n  \"\\3\\377\\16\\15\\17\\37775=\\377\\225\\216\\251\\377\\247\\237\\300\\377\\305\\272\\346\\377\"\n  \"\\324\\307\\372\\377\\332\\315\\377\\377\\337\\317\\377\\377\\343\\320\\377\\377\\344\\320\"\n  \"\\377\\377\\347\\322\\377\\377\\351\\322\\377\\377\\353\\322\\377\\377\\354\\321\\377\\377\"\n  \"\\354\\320\\377\\377\\356\\320\\377\\377\\353\\313\\377\\377\\330\\272\\377\\377\\272\\237\"\n  \"\\377\\377fW\\243\\377\\23\\21$\\377\\1\\1\\2\\377\\0\\0\\0\\377\\17\\15\\33\\377MB\\202\\377\"\n  \"\\244\\215\\377\\377\\276\\243\\376\\377\\314\\260\\377\\377\\313\\262\\377\\377\\306\\257\"\n  \"\\377\\377\\301\\253\\377\\377\\274\\250\\377\\377\\266\\243\\372\\377\\260\\237\\354\\377\"\n  \"\\253\\234\\337\\377\\246\\230\\323\\377\\240\\224\\310\\377\\232\\220\\275\\377\\223\\212\"\n  \"\\256\\377\\212\\202\\240\\377ys\\213\\377`\\\\l\\377PLY\\377\\25\\25\\30\\377\\4\\4\\5\\377\"\n  \"\\22\\21\\23\\377>;E\\377IDS\\377bT\\267\\377YO\\212\\377\\33\\34\\31\\377\\1\\1\\1\\377\\32\"\n  \"\\31\\33\\377a^f\\257ZX`\\315\\22\\22\\23\\377\\3\\3\\3\\377\\36\\37\\32\\377\\201p\\302\\377\"\n  \"\\226\\201\\353\\377tn\\207\\377LIT\\377\\16\\16\\20\\377\\5\\5\\5\\377\\23\\23\\25\\377|w\\213\"\n  \"\\377\\226\\220\\252\\377\\273\\261\\330\\377\\316\\302\\360\\377\\326\\311\\375\\377\\333\"\n  \"\\314\\377\\377\\335\\315\\377\\377\\342\\317\\377\\377\\342\\317\\377\\377\\345\\317\\377\"\n  \"\\377\\350\\320\\377\\377\\350\\320\\377\\377\\353\\320\\377\\377\\354\\320\\377\\377\\356\"\n  \"\\316\\377\\377\\351\\311\\377\\377\\330\\271\\377\\377\\273\\237\\377\\377dT\\244\\377\\23\"\n  \"\\20$\\377\\1\\1\\2\\377\\0\\0\\0\\377\\17\\15\\34\\377NB\\205\\377\\244\\213\\377\\377\\275\\242\"\n  \"\\376\\377\\314\\260\\377\\377\\314\\261\\377\\377\\305\\256\\377\\377\\301\\253\\377\\377\"\n  \"\\273\\246\\377\\377\\265\\242\\374\\377\\257\\237\\355\\377\\251\\233\\340\\377\\245\\227\"\n  \"\\325\\377\\240\\224\\311\\377\\233\\220\\275\\377\\224\\213\\261\\377\\215\\205\\245\\377\"\n  \"\\201z\\225\\377kgz\\377ZVe\\377+)/\\377\\10\\10\\11\\377\\15\\15\\16\\37775=\\377GDQ\\377\"\n  \"]P\\241\\377`T\\241\\377!!\\37\\377\\3\\3\\3\\377\\22\\22\\23\\377ZW_\\315US[\\350\\15\\15\"\n  \"\\16\\377\\5\\5\\5\\377##\\37\\377\\216y\\335\\377\\216|\\330\\377tn\\204\\377GEO\\377\\15\"\n  \"\\15\\16\\377\\10\\10\\10\\3771/6\\377\\212\\204\\233\\377\\247\\237\\275\\377\\304\\272\\343\"\n  \"\\377\\320\\305\\364\\377\\326\\311\\376\\377\\332\\312\\377\\377\\333\\313\\377\\377\\337\"\n  \"\\315\\377\\377\\337\\314\\377\\377\\342\\314\\377\\377\\345\\316\\377\\377\\347\\316\\377\"\n  \"\\377\\351\\315\\377\\377\\353\\316\\377\\377\\355\\314\\377\\377\\347\\307\\377\\377\\330\"\n  \"\\270\\377\\377\\273\\235\\377\\377eU\\250\\377\\23\\20#\\377\\1\\1\\1\\377\\0\\0\\0\\377\\17\"\n  \"\\15\\34\\377NB\\212\\377\\244\\212\\377\\377\\276\\241\\377\\377\\314\\257\\377\\377\\313\"\n  \"\\257\\377\\377\\306\\255\\377\\377\\300\\251\\377\\377\\271\\245\\377\\377\\265\\241\\376\"\n  \"\\377\\257\\235\\355\\377\\251\\232\\340\\377\\245\\227\\325\\377\\236\\222\\307\\377\\232\"\n  \"\\216\\275\\377\\224\\214\\262\\377\\216\\206\\246\\377\\206\\177\\232\\377uo\\204\\377]Y\"\n  \"h\\377<:B\\377\\16\\15\\17\\377\\13\\13\\15\\37753:\\377HDQ\\377YN\\220\\377dU\\262\\377\"\n  \"''&\\377\\5\\5\\5\\377\\15\\15\\16\\377USZ\\350SQX\\370\\12\\12\\12\\377\\5\\5\\5\\377''$\\377\"\n  \"\\226\\200\\356\\377\\214z\\316\\377uo\\206\\377PMW\\377\\20\\17\\21\\377\\13\\13\\14\\377\"\n  \"@=G\\377\\216\\210\\240\\377\\256\\246\\305\\377\\305\\273\\345\\377\\320\\303\\362\\377\\323\"\n  \"\\306\\376\\377\\327\\307\\377\\377\\330\\310\\377\\377\\333\\311\\377\\377\\336\\312\\377\"\n  \"\\377\\341\\313\\377\\377\\343\\313\\377\\377\\345\\313\\377\\377\\346\\313\\377\\377\\351\"\n  \"\\312\\377\\377\\353\\312\\377\\377\\346\\305\\377\\377\\330\\266\\377\\377\\271\\234\\377\"\n  \"\\377dT\\244\\377\\21\\17\\33\\377\\1\\1\\1\\377\\0\\0\\0\\377\\16\\15\\26\\377NA\\206\\377\\242\"\n  \"\\212\\377\\377\\277\\240\\377\\377\\312\\255\\377\\377\\312\\256\\377\\377\\305\\254\\377\"\n  \"\\377\\276\\247\\377\\377\\270\\243\\377\\377\\263\\240\\375\\377\\255\\234\\355\\377\\250\"\n  \"\\231\\340\\377\\243\\225\\324\\377\\235\\220\\306\\377\\230\\216\\275\\377\\223\\211\\262\"\n  \"\\377\\215\\205\\245\\377\\206\\177\\234\\377vq\\207\\377^Zj\\377ECM\\377\\20\\20\\22\\377\"\n  \"\\17\\17\\20\\377:7?\\377HER\\377YN\\212\\377fV\\270\\377,,-\\377\\5\\5\\6\\377\\12\\12\\12\"\n  \"\\377SQW\\370OMT\\377\\10\\11\\11\\377\\7\\7\\7\\377)*'\\377\\226\\177\\361\\377\\214{\\312\"\n  \"\\377yr\\211\\377fcp\\377\\30\\27\\32\\377\\16\\15\\16\\377<:C\\377\\215\\206\\236\\377\\253\"\n  \"\\243\\302\\377\\303\\270\\342\\377\\314\\300\\357\\377\\321\\304\\374\\377\\324\\305\\377\"\n  \"\\377\\327\\306\\377\\377\\331\\307\\377\\377\\333\\307\\377\\377\\336\\307\\377\\377\\337\"\n  \"\\310\\377\\377\\343\\311\\377\\377\\343\\310\\377\\377\\347\\311\\377\\377\\347\\310\\377\"\n  \"\\377\\344\\303\\377\\377\\327\\265\\377\\377\\270\\233\\377\\377bS\\234\\377\\21\\17\\30\\377\"\n  \"\\1\\1\\1\\377\\0\\0\\0\\377\\16\\14\\23\\377K@}\\377\\242\\210\\377\\377\\274\\237\\376\\377\"\n  \"\\311\\254\\377\\377\\310\\255\\377\\377\\304\\252\\377\\377\\273\\245\\377\\377\\267\\242\"\n  \"\\377\\377\\261\\236\\374\\377\\253\\232\\353\\377\\247\\230\\337\\377\\241\\223\\321\\377\"\n  \"\\234\\220\\306\\377\\227\\214\\273\\377\\222\\211\\260\\377\\214\\203\\243\\377\\205}\\233\"\n  \"\\377to\\205\\377^Yi\\377C@J\\377\\21\\21\\23\\377\\27\\26\\32\\377CAJ\\377JGT\\377YO\\210\"\n  \"\\377gW\\275\\377.-/\\377\\7\\7\\7\\377\\10\\11\\11\\377OMT\\377OMT\\377\\10\\11\\11\\377\\7\"\n  \"\\7\\7\\377)*(\\377\\226\\177\\361\\377\\215|\\313\\377|w\\216\\377~y\\212\\3770.3\\377\\21\"\n  \"\\21\\23\\377*)/\\377\\210\\202\\231\\377\\236\\227\\265\\377\\273\\262\\331\\377\\307\\273\"\n  \"\\351\\377\\315\\300\\367\\377\\321\\302\\377\\377\\322\\302\\377\\377\\325\\304\\377\\377\"\n  \"\\327\\304\\377\\377\\332\\304\\377\\377\\335\\306\\377\\377\\340\\306\\377\\377\\340\\305\"\n  \"\\377\\377\\343\\306\\377\\377\\344\\305\\377\\377\\342\\301\\377\\377\\323\\262\\377\\377\"\n  \"\\265\\231\\377\\377bS\\235\\377\\22\\17\\31\\377\\1\\1\\1\\377\\0\\0\\0\\377\\16\\14\\23\\377\"\n  \"K@|\\377\\237\\207\\377\\377\\271\\235\\376\\377\\307\\252\\377\\377\\305\\252\\377\\377\\301\"\n  \"\\247\\377\\377\\272\\244\\377\\377\\265\\241\\377\\377\\257\\234\\370\\377\\252\\230\\351\"\n  \"\\377\\244\\226\\334\\377\\237\\221\\317\\377\\233\\217\\304\\377\\225\\212\\271\\377\\220\"\n  \"\\207\\256\\377\\212\\201\\242\\377\\200y\\224\\377ni~\\377\\\\Wg\\37764<\\377\\24\\23\\26\"\n  \"\\377)(,\\377MKU\\377NJY\\377ZP\\212\\377gW\\275\\377/./\\377\\7\\7\\7\\377\\10\\11\\11\\377\"\n  \"OMT\\377SQX\\370\\12\\12\\12\\377\\5\\5\\5\\377((%\\377\\223}\\355\\377\\217}\\322\\377\\203\"\n  \"|\\226\\377\\177z\\213\\377mjw\\377\\33\\32\\36\\377\\30\\27\\32\\377ok}\\377\\220\\212\\244\"\n  \"\\377\\256\\245\\312\\377\\277\\264\\340\\377\\307\\272\\357\\377\\315\\276\\375\\377\\317\"\n  \"\\277\\377\\377\\322\\301\\377\\377\\324\\301\\377\\377\\327\\302\\377\\377\\331\\302\\377\"\n  \"\\377\\333\\302\\377\\377\\334\\302\\377\\377\\340\\303\\377\\377\\342\\302\\377\\377\\335\"\n  \"\\277\\377\\377\\317\\257\\377\\377\\262\\226\\377\\377eU\\246\\377\\23\\21\\37\\377\\1\\1\\1\"\n  \"\\377\\0\\0\\0\\377\\16\\15\\26\\377L@\\205\\377\\235\\204\\377\\377\\270\\233\\376\\377\\303\"\n  \"\\247\\377\\377\\303\\250\\377\\377\\276\\245\\377\\377\\270\\242\\377\\377\\262\\236\\376\"\n  \"\\377\\255\\233\\365\\377\\247\\227\\345\\377\\242\\223\\330\\377\\236\\220\\315\\377\\230\"\n  \"\\214\\301\\377\\223\\211\\265\\377\\215\\204\\252\\377\\205~\\234\\377yr\\214\\377c_q\\377\"\n  \"YTd\\377$\\\"(\\377\\36\\35!\\377GDM\\377NJU\\377RN^\\377\\\\R\\217\\377eV\\271\\377---\\377\"\n  \"\\5\\5\\6\\377\\12\\12\\12\\377SQW\\370USZ\\350\\15\\15\\16\\377\\4\\4\\5\\377$%\\40\\377\\212\"\n  \"v\\335\\377\\221~\\334\\377\\205\\177\\231\\377\\212\\204\\227\\377\\207\\203\\222\\377WT\"\n  \"^\\377\\37\\35\\\"\\3771.6\\377\\214\\206\\237\\377\\231\\221\\261\\377\\263\\250\\320\\377\"\n  \"\\277\\264\\345\\377\\307\\271\\365\\377\\313\\274\\376\\377\\317\\275\\377\\377\\320\\276\"\n  \"\\377\\377\\322\\276\\377\\377\\326\\277\\377\\377\\327\\300\\377\\377\\331\\300\\377\\377\"\n  \"\\333\\300\\377\\377\\335\\277\\377\\377\\331\\272\\377\\377\\313\\255\\377\\377\\260\\224\"\n  \"\\377\\377eU\\254\\377\\25\\22'\\377\\1\\1\\2\\377\\0\\0\\0\\377\\17\\15\\33\\377MA\\215\\377\"\n  \"\\233\\203\\377\\377\\263\\230\\377\\377\\277\\244\\377\\377\\277\\245\\377\\377\\273\\244\"\n  \"\\377\\377\\264\\237\\377\\377\\257\\234\\375\\377\\253\\230\\361\\377\\244\\224\\340\\377\"\n  \"\\240\\221\\325\\377\\233\\216\\311\\377\\225\\212\\274\\377\\217\\205\\261\\377\\210\\200\"\n  \"\\243\\377}v\\222\\377mg~\\377_[l\\377=;E\\377#!&\\377><D\\377ROY\\377UR^\\377UQb\\377\"\n  \"^R\\230\\377dU\\261\\377))(\\377\\5\\4\\5\\377\\15\\15\\16\\377USZ\\350ZX_\\315\\22\\22\\23\"\n  \"\\377\\3\\3\\2\\377\\37\\40\\33\\377}l\\303\\377\\226\\200\\353\\377\\205~\\233\\377\\221\\213\"\n  \"\\240\\377\\216\\211\\231\\377\\214\\207\\230\\377TQ]\\377#!'\\377GDQ\\377\\224\\215\\253\"\n  \"\\377\\234\\223\\266\\377\\261\\246\\322\\377\\275\\260\\350\\377\\303\\265\\365\\377\\311\"\n  \"\\270\\377\\377\\314\\273\\377\\377\\320\\272\\377\\377\\322\\274\\377\\377\\323\\274\\377\"\n  \"\\377\\324\\274\\377\\377\\325\\274\\377\\377\\331\\274\\377\\377\\325\\270\\377\\377\\306\"\n  \"\\251\\377\\377\\251\\220\\377\\377cT\\247\\377\\25\\22'\\377\\1\\1\\2\\377\\0\\0\\0\\377\\17\"\n  \"\\15\\34\\377LA\\206\\377\\230\\200\\377\\377\\256\\225\\377\\377\\273\\241\\377\\377\\274\"\n  \"\\243\\377\\377\\266\\240\\377\\377\\261\\234\\377\\377\\255\\232\\370\\377\\247\\226\\351\"\n  \"\\377\\242\\223\\333\\377\\235\\217\\317\\377\\230\\213\\304\\377\\220\\206\\266\\377\\212\"\n  \"\\200\\251\\377\\177w\\227\\377pj\\203\\377d_t\\377MJW\\377*(/\\377?<E\\377VT^\\377WU\"\n  \"^\\377[Wd\\377VRc\\377aS\\246\\377`R\\241\\377\\\"#\\40\\377\\3\\3\\3\\377\\22\\22\\23\\377\"\n  \"ZW_\\315a^f\\257\\32\\31\\33\\377\\1\\1\\1\\377\\33\\33\\27\\377j^\\236\\377\\232\\202\\375\"\n  \"\\377\\204|\\234\\377\\221\\214\\240\\377\\231\\224\\246\\377\\222\\215\\236\\377\\214\\207\"\n  \"\\232\\377eaq\\377*(/\\377LIX\\377\\231\\220\\262\\377\\237\\225\\274\\377\\256\\243\\325\"\n  \"\\377\\272\\254\\347\\377\\301\\262\\367\\377\\305\\264\\377\\377\\311\\267\\377\\377\\314\"\n  \"\\267\\377\\377\\316\\271\\377\\377\\321\\272\\377\\377\\322\\271\\377\\377\\323\\271\\377\"\n  \"\\377\\320\\263\\377\\377\\300\\245\\377\\377\\245\\215\\377\\377`R\\244\\377\\25\\22%\\377\"\n  \"\\1\\1\\2\\377\\0\\0\\0\\377\\17\\15\\33\\377J?\\202\\377\\223~\\374\\377\\253\\223\\377\\377\"\n  \"\\271\\237\\377\\377\\267\\240\\377\\377\\263\\235\\377\\377\\260\\233\\376\\377\\251\\227\"\n  \"\\360\\377\\243\\223\\341\\377\\236\\217\\324\\377\\230\\213\\307\\377\\222\\206\\272\\377\"\n  \"\\212\\200\\255\\377\\200w\\234\\377rk\\207\\377lf~\\377PL]\\3770.6\\377JFR\\377ZVb\\377\"\n  \"[Wb\\377_\\\\g\\377\\\\Xf\\377VQc\\377dT\\270\\377YO\\212\\377\\34\\34\\31\\377\\1\\1\\1\\377\"\n  \"\\32\\31\\33\\377a^f\\257igp\\205%$'\\377\\1\\1\\1\\377\\25\\25\\25\\377QKk\\377\\240\\204\"\n  \"\\377\\377\\203z\\244\\377\\215\\207\\236\\377\\234\\226\\250\\377\\236\\232\\254\\377\\227\"\n  \"\\220\\245\\377\\216\\211\\237\\377\\203}\\224\\377B?K\\377=:H\\377\\214\\203\\244\\377\\242\"\n  \"\\230\\303\\377\\250\\233\\320\\377\\262\\244\\341\\377\\273\\253\\363\\377\\301\\257\\376\"\n  \"\\377\\305\\262\\377\\377\\312\\265\\377\\377\\313\\265\\377\\377\\315\\265\\377\\377\\314\"\n  \"\\264\\377\\377\\312\\261\\377\\377\\275\\243\\377\\377\\241\\213\\377\\377^Q\\234\\377\\24\"\n  \"\\22#\\377\\1\\1\\2\\377\\0\\0\\0\\377\\17\\15\\31\\377I?{\\377\\217|\\355\\377\\250\\221\\377\"\n  \"\\377\\263\\234\\377\\377\\261\\235\\377\\377\\261\\234\\377\\377\\251\\227\\364\\377\\244\"\n  \"\\223\\346\\377\\236\\217\\327\\377\\227\\212\\311\\377\\221\\205\\274\\377\\210}\\254\\377\"\n  \"~u\\234\\377vn\\216\\377lf\\177\\377GCS\\377=:E\\377[Wg\\377^Zi\\377_[i\\377eam\\377\"\n  \"b^j\\377[Wf\\377VQf\\377gU\\314\\377NGi\\377\\24\\25\\22\\377\\1\\1\\1\\377%$'\\377igp\\205\"\n  \"wu~S326\\377\\2\\2\\2\\377\\16\\16\\20\\377<:D\\377\\230\\177\\377\\377\\205x\\267\\377\\211\"\n  \"\\203\\232\\377\\231\\224\\247\\377\\242\\235\\257\\377\\243\\235\\262\\377\\235\\230\\257\"\n  \"\\377\\222\\214\\244\\377\\226\\217\\253\\377wq\\212\\377?;J\\377_Yr\\377\\233\\220\\276\"\n  \"\\377\\247\\233\\322\\377\\255\\237\\335\\377\\262\\243\\351\\377\\272\\251\\372\\377\\301\"\n  \"\\255\\377\\377\\304\\257\\377\\377\\307\\261\\377\\377\\310\\261\\377\\377\\304\\254\\377\"\n  \"\\377\\265\\237\\377\\377\\234\\207\\367\\377\\\\P\\224\\377\\25\\21\\40\\377\\1\\1\\2\\377\\0\"\n  \"\\0\\0\\377\\17\\15\\27\\377H>t\\377\\214y\\335\\377\\241\\215\\372\\377\\255\\230\\377\\377\"\n  \"\\257\\232\\377\\377\\253\\230\\370\\377\\245\\223\\350\\377\\237\\216\\331\\377\\226\\211\"\n  \"\\312\\377\\215\\201\\270\\377\\206z\\254\\377~u\\237\\377xo\\222\\377ZTl\\377C?N\\377X\"\n  \"Tg\\377fat\\377b^o\\377gbr\\377hdr\\377gep\\377a^j\\377YWd\\377YQw\\377gV\\313\\377\"\n  \">;F\\377\\16\\16\\16\\377\\2\\2\\2\\377326\\377wu~S\\255\\252\\270\\26FDJ\\377\\6\\6\\6\\377\"\n  \"\\10\\7\\10\\377()#\\377\\212u\\351\\377\\212x\\317\\377\\203|\\226\\377\\223\\216\\241\\377\"\n  \"\\240\\232\\254\\377\\245\\240\\264\\377\\246\\240\\267\\377\\243\\234\\266\\377\\233\\225\"\n  \"\\260\\377\\227\\217\\254\\377\\233\\222\\264\\377}v\\223\\377UPg\\377b[z\\377\\220\\205\"\n  \"\\266\\377\\254\\235\\335\\377\\260\\241\\351\\377\\267\\246\\370\\377\\276\\253\\377\\377\"\n  \"\\302\\255\\377\\377\\302\\255\\377\\377\\277\\251\\377\\377\\260\\233\\377\\377\\227\\204\"\n  \"\\346\\377ZO\\213\\377\\24\\21\\37\\377\\1\\1\\2\\377\\0\\0\\0\\377\\16\\14\\26\\377G=l\\377\\206\"\n  \"v\\314\\377\\235\\212\\353\\377\\252\\226\\373\\377\\252\\227\\364\\377\\246\\224\\352\\377\"\n  \"\\240\\220\\335\\377\\227\\210\\314\\377\\217\\202\\274\\377\\207|\\257\\377yp\\231\\377]\"\n  \"Vt\\377PKb\\377`[r\\377mg\\177\\377idy\\377jex\\377lhz\\377mix\\377kgt\\377fcn\\377\"\n  \"_\\\\h\\377WSc\\377\\\\Q\\222\\377dT\\266\\377--+\\377\\7\\7\\10\\377\\6\\6\\6\\377FDJ\\377\\255\"\n  \"\\252\\270\\26\\0\\0\\0\\0[Y`\\364\\20\\21\\21\\377\\3\\3\\3\\377\\32\\33\\24\\377l`\\247\\377\"\n  \"\\223{\\370\\377~v\\225\\377\\213\\206\\233\\377\\232\\225\\246\\377\\243\\236\\261\\377\\246\"\n  \"\\240\\266\\377\\250\\241\\272\\377\\245\\236\\272\\377\\242\\233\\270\\377\\235\\225\\267\"\n  \"\\377\\233\\222\\265\\377\\235\\224\\275\\377\\230\\215\\272\\377vm\\223\\377kb\\207\\377\"\n  \"yo\\235\\377\\262\\242\\355\\377\\267\\245\\371\\377\\273\\250\\377\\377\\275\\250\\377\\377\"\n  \"\\272\\245\\377\\377\\254\\231\\372\\377\\223\\202\\331\\377WM\\202\\377\\23\\21\\35\\377\\2\"\n  \"\\1\\2\\377\\0\\0\\0\\377\\16\\14\\25\\377D<f\\377\\204u\\303\\377\\232\\210\\340\\377\\244\\222\"\n  \"\\353\\377\\244\\224\\351\\377\\241\\221\\337\\377\\232\\213\\321\\377\\224\\207\\305\\377\"\n  \"pf\\221\\377bZ}\\377c\\\\{\\377sk\\215\\377vo\\215\\377qj\\204\\377qj\\203\\377qm\\202\\377\"\n  \"qm\\200\\377pl}\\377njy\\377jgs\\377c`l\\377\\\\Xf\\377UPb\\377aS\\265\\377ZO\\216\\377\"\n  \"\\35\\35\\30\\377\\3\\3\\3\\377\\20\\21\\21\\377[Ya\\364\\0\\0\\0\\0\\0\\0\\0\\0jgq\\215&%'\\377\"\n  \"\\1\\1\\1\\377\\17\\20\\16\\377C@P\\377\\227}\\377\\377\\177t\\252\\377\\203}\\224\\377\\223\"\n  \"\\216\\240\\377\\236\\231\\252\\377\\244\\236\\262\\377\\247\\241\\270\\377\\250\\240\\274\"\n  \"\\377\\247\\240\\276\\377\\246\\236\\277\\377\\244\\234\\300\\377\\241\\230\\275\\377\\240\"\n  \"\\226\\301\\377\\243\\230\\311\\377\\245\\230\\317\\377\\250\\233\\326\\377\\254\\236\\341\"\n  \"\\377\\263\\243\\355\\377\\267\\245\\366\\377\\267\\245\\373\\377\\264\\242\\375\\377\\247\"\n  \"\\226\\355\\377\\217\\200\\315\\377WM}\\377\\24\\22\\36\\377\\1\\1\\2\\377\\0\\1\\0\\377\\17\\15\"\n  \"\\26\\377E>d\\377\\201t\\271\\377\\225\\205\\324\\377\\240\\220\\340\\377\\241\\220\\334\\377\"\n  \"\\235\\215\\323\\377\\227\\212\\310\\377\\217\\203\\273\\377\\211~\\257\\377\\202x\\243\\377\"\n  \"~u\\233\\377zr\\224\\377xq\\215\\377xq\\213\\377wq\\211\\377up\\205\\377sn\\201\\377qm\"\n  \"|\\377lhv\\377gdo\\377a]j\\377XTb\\377VOn\\377fT\\314\\377FAV\\377\\17\\20\\15\\377\\1\"\n  \"\\1\\1\\377&%(\\377jgp\\215\\0\\0\\0\\0\\0\\0\\0\\0\\203\\177\\2132>>B\\377\\5\\5\\5\\377\\7\\7\"\n  \"\\7\\377()\\\"\\377\\202o\\337\\377\\210t\\330\\377|v\\220\\377\\212\\204\\230\\377\\226\\221\"\n  \"\\242\\377\\237\\232\\254\\377\\242\\234\\261\\377\\245\\237\\270\\377\\246\\237\\272\\377\"\n  \"\\246\\237\\275\\377\\247\\236\\301\\377\\246\\235\\302\\377\\246\\234\\304\\377\\245\\233\"\n  \"\\310\\377\\247\\233\\315\\377\\250\\234\\323\\377\\256\\237\\335\\377\\257\\241\\344\\377\"\n  \"\\261\\241\\352\\377\\261\\241\\356\\377\\257\\235\\354\\377\\243\\223\\341\\377\\215~\\304\"\n  \"\\377aW\\211\\377\\26\\24\\37\\377\\2\\2\\3\\377\\1\\1\\1\\377\\23\\20\\32\\377NFn\\377\\201s\"\n  \"\\264\\377\\224\\204\\312\\377\\233\\214\\323\\377\\234\\215\\322\\377\\230\\213\\311\\377\"\n  \"\\224\\207\\301\\377\\220\\204\\267\\377\\210~\\253\\377\\204z\\243\\377\\202y\\235\\377~\"\n  \"w\\225\\377|u\\221\\377zt\\216\\377xr\\210\\377to\\204\\377rn\\177\\377njy\\377ifr\\377\"\n  \"c`l\\377]Yf\\377UP`\\377]P\\232\\377cT\\260\\377,,)\\377\\6\\7\\6\\377\\5\\5\\5\\377>>B\\377\"\n  \"\\203\\177\\2132\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0ZX`\\365\\22\\22\\23\\377\\2\\2\\2\\377\\26\\27\"\n  \"\\22\\377XQ|\\377\\222z\\377\\377zp\\231\\377\\201{\\221\\377\\217\\212\\234\\377\\230\\224\"\n  \"\\244\\377\\235\\227\\253\\377\\241\\234\\262\\377\\244\\235\\267\\377\\244\\235\\272\\377\"\n  \"\\245\\236\\275\\377\\246\\235\\301\\377\\246\\234\\302\\377\\246\\234\\306\\377\\247\\234\"\n  \"\\313\\377\\251\\234\\320\\377\\251\\234\\324\\377\\253\\235\\332\\377\\255\\237\\337\\377\"\n  \"\\254\\236\\342\\377\\252\\233\\341\\377\\240\\222\\330\\377\\211|\\271\\377nc\\225\\377\\32\"\n  \"\\30$\\377\\3\\3\\4\\377\\2\\2\\3\\377\\26\\24\\36\\377_V\\200\\377~q\\251\\377\\221\\204\\303\"\n  \"\\377\\227\\212\\311\\377\\227\\212\\305\\377\\225\\211\\301\\377\\221\\206\\271\\377\\214\"\n  \"\\201\\260\\377\\210\\177\\251\\377\\205|\\242\\377\\201y\\232\\377~v\\223\\377|u\\220\\377\"\n  \"ys\\212\\377vq\\205\\377sn\\201\\377ol{\\377jes\\377fco\\377`\\\\h\\377XTb\\377TNf\\377\"\n  \"cS\\305\\377RIs\\377\\27\\30\\24\\377\\2\\2\\2\\377\\22\\22\\23\\377ZX`\\365\\0\\0\\0\\0\\0\\0\"\n  \"\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0omvs/-1\\377\\3\\4\\4\\377\\12\\12\\11\\37710/\\377\\210r\\357\\377\"\n  \"\\202r\\316\\377yr\\212\\377\\206\\200\\224\\377\\221\\214\\235\\377\\231\\224\\245\\377\\234\"\n  \"\\225\\252\\377\\237\\232\\261\\377\\241\\232\\264\\377\\242\\233\\270\\377\\243\\233\\272\"\n  \"\\377\\243\\233\\276\\377\\244\\232\\300\\377\\244\\232\\303\\377\\245\\233\\311\\377\\247\"\n  \"\\233\\316\\377\\247\\234\\322\\377\\250\\233\\324\\377\\251\\233\\330\\377\\246\\230\\327\"\n  \"\\377\\235\\220\\316\\377\\207|\\263\\377vl\\236\\377!\\36,\\377\\6\\5\\7\\377\\4\\3\\5\\377\"\n  \"\\33\\31%\\377ka\\216\\377|q\\244\\377\\217\\202\\273\\377\\224\\207\\301\\377\\224\\210\\276\"\n  \"\\377\\220\\205\\267\\377\\214\\203\\261\\377\\212\\201\\252\\377\\206~\\244\\377\\203{\\233\"\n  \"\\377\\177x\\225\\377}v\\222\\377zt\\214\\377vq\\206\\377sn\\202\\377qm~\\377kgu\\377g\"\n  \"dp\\377b`k\\377[Xe\\377TP^\\377[O\\221\\377dT\\273\\377537\\377\\12\\12\\11\\377\\3\\4\\4\"\n  \"\\377/-1\\377omvs\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\201~\\211\\1\\320\\312\\332\\11OMT\\377\"\n  \"\\14\\14\\14\\373\\3\\3\\3\\377\\31\\32\\26\\377ZR\\200\\377\\217w\\377\\377wm\\227\\377|v\\213\"\n  \"\\377\\211\\203\\225\\377\\220\\214\\234\\377\\226\\221\\243\\377\\232\\225\\251\\377\\234\"\n  \"\\227\\257\\377\\236\\226\\261\\377\\236\\227\\264\\377\\237\\227\\267\\377\\240\\230\\273\"\n  \"\\377\\240\\230\\275\\377\\242\\230\\277\\377\\242\\230\\303\\377\\243\\227\\307\\377\\243\"\n  \"\\227\\311\\377\\242\\226\\314\\377\\241\\224\\313\\377\\234\\217\\307\\377\\212\\177\\263\"\n  \"\\377si\\225\\3772.A\\377\\10\\7\\13\\377\\7\\7\\11\\377)&5\\377md\\216\\377}s\\242\\377\\215\"\n  \"\\202\\264\\377\\221\\205\\267\\377\\217\\204\\264\\377\\214\\202\\256\\377\\212\\200\\250\"\n  \"\\377\\206~\\242\\377\\203{\\233\\377\\177x\\225\\377}v\\222\\377zt\\214\\377wq\\206\\377\"\n  \"tn\\202\\377qm~\\377liw\\377hdq\\377c`k\\377^[g\\377VQ`\\377TMg\\377cR\\305\\377SKv\"\n  \"\\377\\32\\33\\27\\377\\3\\3\\3\\377\\14\\14\\14\\373OMT\\377\\320\\312\\333\\11\\201~\\211\\1\"\n  \"\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0olv\\222,+.\\377\\2\\2\\2\\377\\11\\11\\11\\377,,(\"\n  \"\\377~l\\331\\377\\205q\\344\\377tm\\206\\377~y\\214\\377\\210\\203\\224\\377\\220\\214\\234\"\n  \"\\377\\224\\217\\242\\377\\231\\223\\247\\377\\232\\224\\253\\377\\232\\224\\256\\377\\233\"\n  \"\\225\\260\\377\\233\\224\\263\\377\\234\\224\\266\\377\\235\\224\\267\\377\\235\\223\\270\"\n  \"\\377\\236\\224\\274\\377\\236\\223\\300\\377\\236\\223\\303\\377\\234\\221\\302\\377\\227\"\n  \"\\214\\274\\377\\211\\177\\254\\377ri\\217\\377?;P\\377\\15\\14\\20\\377\\13\\12\\16\\3779\"\n  \"5I\\377jb\\206\\377\\177v\\237\\377\\212\\200\\254\\377\\214\\202\\256\\377\\213\\201\\253\"\n  \"\\377\\210\\177\\245\\377\\205}\\237\\377\\202z\\231\\377\\177w\\224\\377}v\\221\\377zs\\214\"\n  \"\\377vq\\207\\377to\\202\\377qm\\177\\377miy\\377hdq\\377dam\\377_\\\\g\\377WTb\\377SN\"\n  \"]\\377^Q\\245\\377bS\\255\\3770//\\377\\11\\11\\10\\377\\2\\2\\2\\377,+.\\377olv\\222\\0\\0\"\n  \"\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0pmw\\3\\377\\377\\377\\2TRY\\377\\20\\20\\21\\374\"\n  \"\\2\\2\\1\\377\\24\\25\\23\\377FBS\\377\\213s\\377\\377yk\\264\\377tm\\203\\377~y\\214\\377\"\n  \"\\207\\203\\223\\377\\216\\211\\231\\377\\221\\214\\236\\377\\225\\217\\243\\377\\226\\220\"\n  \"\\250\\377\\227\\220\\251\\377\\227\\220\\254\\377\\230\\220\\256\\377\\231\\221\\261\\377\"\n  \"\\231\\221\\262\\377\\230\\220\\263\\377\\230\\220\\263\\377\\230\\217\\266\\377\\230\\216\"\n  \"\\267\\377\\224\\212\\264\\377\\211\\200\\250\\377sk\\215\\377KE\\\\\\377\\22\\20\\26\\377\\21\"\n  \"\\17\\24\\377F@V\\377ld\\205\\377\\200v\\234\\377\\206}\\244\\377\\207\\177\\244\\377\\207\"\n  \"~\\241\\377\\204|\\233\\377\\201y\\227\\377~w\\223\\377|v\\220\\377yr\\212\\377vq\\206\\377\"\n  \"sn\\202\\377qm~\\377niy\\377idr\\377ebn\\377`]h\\377YUb\\377SO^\\377WN\\177\\377dR\\305\"\n  \"\\377GBW\\377\\24\\25\\23\\377\\2\\2\\1\\377\\20\\20\\21\\374TRY\\377\\377\\377\\377\\2plv\\3\"\n  \"\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0|x\\202b97;\\377\\6\\6\\6\\377\"\n  \"\\5\\5\\5\\377\\40!\\33\\377`W\\214\\377\\212r\\377\\377si\\227\\377sn\\205\\377~y\\212\\377\"\n  \"\\206\\201\\222\\377\\214\\210\\227\\377\\216\\211\\233\\377\\222\\214\\240\\377\\223\\215\"\n  \"\\244\\377\\224\\215\\246\\377\\223\\215\\247\\377\\224\\215\\250\\377\\223\\215\\251\\377\"\n  \"\\224\\214\\254\\377\\224\\213\\254\\377\\224\\213\\255\\377\\223\\212\\255\\377\\217\\207\"\n  \"\\252\\377\\207\\177\\241\\377qk\\210\\377VQh\\377\\27\\26\\34\\377\\27\\25\\33\\377RMc\\377\"\n  \"kd\\201\\377}v\\225\\377\\204|\\233\\377\\203|\\233\\377\\202{\\230\\377\\200y\\224\\377\"\n  \"}v\\222\\377{u\\215\\377xr\\210\\377up\\204\\377sn\\202\\377qm~\\377miw\\377idr\\377e\"\n  \"bn\\377`]h\\377ZWc\\377TOa\\377TMk\\377bR\\305\\377WN\\201\\377!\\\"\\35\\377\\5\\5\\5\\377\"\n  \"\\6\\6\\6\\37787<\\377{x\\202b\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\"\n  \"\\0\\0\\0\\0vs}\\3\\0\\0\\0\\0b`h\\325\\36\\36\\40\\377\\2\\2\\1\\377\\12\\12\\12\\377**%\\377r\"\n  \"c\\272\\377\\204n\\373\\377of\\215\\377rl\\203\\377{v\\210\\377\\203~\\215\\377\\211\\204\"\n  \"\\224\\377\\212\\205\\226\\377\\215\\210\\233\\377\\217\\212\\236\\377\\220\\212\\241\\377\"\n  \"\\217\\211\\242\\377\\220\\211\\243\\377\\220\\212\\244\\377\\220\\211\\245\\377\\217\\210\"\n  \"\\246\\377\\217\\207\\245\\377\\214\\204\\243\\377\\205}\\233\\377rk\\205\\377`[p\\377\\36\"\n  \"\\34$\\377\\36\\34#\\377]Wm\\377lf~\\377}v\\221\\377\\200y\\226\\377\\200x\\225\\377~w\\222\"\n  \"\\377|u\\217\\377zt\\213\\377wr\\207\\377to\\203\\377sm\\200\\377pl}\\377mhv\\377hdq\\377\"\n  \"ebm\\377`\\\\h\\377ZWc\\377TP`\\377SMf\\377`P\\272\\377^Q\\234\\377--+\\377\\12\\12\\12\"\n  \"\\377\\2\\2\\1\\377\\36\\36\\40\\377c`h\\325\\0\\0\\0\\0vs|\\3\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\"\n  \"\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\356\\350\\374\\13QOV\\377\\22\\21\\22\\377\"\n  \"\\1\\1\\1\\377\\17\\17\\20\\377332\\377yg\\321\\377\\177j\\356\\377mf\\210\\377pk\\201\\377\"\n  \"yt\\206\\377\\200|\\213\\377\\205\\201\\220\\377\\207\\202\\223\\377\\211\\204\\226\\377\\214\"\n  \"\\206\\232\\377\\213\\207\\233\\377\\214\\205\\235\\377\\213\\205\\235\\377\\213\\204\\235\"\n  \"\\377\\212\\204\\236\\377\\212\\203\\235\\377\\210\\202\\235\\377\\203|\\227\\377rl\\204\\377\"\n  \"faw\\377)'/\\377)'0\\377d^t\\377mg~\\377ys\\214\\377{v\\217\\377|v\\216\\377{u\\214\\377\"\n  \"xs\\210\\377vp\\204\\377sn\\201\\377qm~\\377okz\\377jft\\377gcq\\377dal\\377_\\\\g\\377\"\n  \"ZVc\\377TPa\\377SMd\\377^O\\262\\377`R\\253\\37776:\\377\\17\\17\\17\\377\\1\\1\\1\\377\\22\"\n  \"\\21\\22\\377QOV\\377\\355\\351\\373\\13\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\"\n  \"\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\177|\\206\\0\\0\\0\\0\\0\\177|\\207V@?D\\377\\12\\12\"\n  \"\\12\\377\\2\\2\\1\\377\\24\\24\\23\\37798;\\377xf\\323\\377}i\\355\\377lc\\207\\377mh~\\377\"\n  \"up\\201\\377|w\\206\\377\\177{\\212\\377\\202~\\215\\377\\205\\177\\220\\377\\206\\201\\223\"\n  \"\\377\\207\\203\\225\\377\\210\\202\\226\\377\\207\\202\\230\\377\\207\\201\\227\\377\\206\"\n  \"\\201\\230\\377\\204~\\225\\377\\200z\\221\\377sn\\203\\377d`r\\377:7B\\377;8D\\377a]o\"\n  \"\\377ni}\\377wr\\207\\377yt\\211\\377xs\\210\\377vq\\204\\377to\\202\\377rn\\200\\377p\"\n  \"m|\\377mhx\\377idr\\377gco\\377c`k\\377^[f\\377XUb\\377TPa\\377SLe\\377^O\\260\\377\"\n  \"`Q\\255\\377=:B\\377\\23\\23\\22\\377\\2\\2\\2\\377\\12\\12\\12\\377@?C\\377\\177|\\207V\\0\"\n  \"\\0\\0\\0~|\\205\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\"\n  \"\\0\\0\\0\\0\\0\\0\\0ur}\\1\\0\\0\\0\\0ljs\\233326\\377\\5\\5\\5\\376\\3\\3\\3\\377\\27\\27\\27\\377\"\n  \"98;\\377ud\\314\\377}h\\365\\377kb\\214\\377jdw\\377ql}\\377ws\\202\\377|x\\206\\377\\177\"\n  \"z\\211\\377\\200{\\213\\377\\201}\\216\\377\\202}\\217\\377\\203\\177\\220\\377\\203}\\221\"\n  \"\\377\\202|\\221\\377\\201{\\220\\377~x\\215\\377to\\202\\377fbs\\377GDO\\377HDQ\\377d\"\n  \"_p\\377oj}\\377vq\\204\\377vq\\204\\377uq\\202\\377so\\177\\377qm}\\377njx\\377jgt\\377\"\n  \"gdp\\377ebm\\377b_j\\377]Zf\\377WTa\\377SO]\\377SMk\\377_P\\271\\377`R\\253\\377<:B\"\n  \"\\377\\26\\26\\26\\377\\3\\3\\3\\377\\5\\5\\5\\376326\\377ljs\\233\\0\\0\\0\\0ur}\\1\\0\\0\\0\\0\"\n  \"\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\"\n  \"\\0\\0\\0\\0\\15\\16\\27\\0\\0\\0\\0\\0gem\\277-,/\\377\\3\\4\\4\\376\\3\\3\\3\\377\\24\\24\\23\\377\"\n  \"443\\377m_\\263\\377\\177i\\376\\377na\\245\\377gat\\377jfx\\377ql|\\377uq\\200\\377x\"\n  \"u\\202\\377zw\\204\\377|x\\206\\377|x\\207\\377}x\\211\\377~y\\211\\377}x\\211\\377zv\\206\"\n  \"\\377to\\200\\377iet\\377RP[\\377UQ]\\377gcr\\377njz\\377rn}\\377qn}\\377pl{\\377nj\"\n  \"x\\377kft\\377heq\\377fcn\\377c_k\\377_\\\\g\\377ZVc\\377UQ`\\377RN]\\377WN~\\377bP\\305\"\n  \"\\377_R\\234\\37776:\\377\\23\\23\\22\\377\\3\\3\\3\\377\\3\\4\\4\\376-,/\\377gem\\277\\0\\0\"\n  \"\\0\\0\\15\\16\\27\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\"\n  \"\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0gdm\\315-,/\"\n  \"\\377\\4\\5\\5\\376\\1\\1\\1\\377\\20\\20\\20\\377++'\\377]S\\207\\377}h\\364\\377sb\\320\\377\"\n  \"f]\\177\\377e`r\\377iev\\377miy\\377qn|\\377sp}\\377vr\\177\\377vr\\200\\377ws\\200\\377\"\n  \"ws\\201\\377uq\\177\\377qm{\\377jes\\377\\\\Ye\\377^Zg\\377gbp\\377kgu\\377mjv\\377mi\"\n  \"v\\377jgt\\377hep\\377fbn\\377c_k\\377`\\\\h\\377[Xd\\377WSa\\377SO^\\377TNf\\377]O\\244\"\n  \"\\377dR\\304\\377WN\\201\\377--+\\377\\17\\17\\17\\377\\1\\1\\1\\377\\4\\5\\5\\376-,/\\377g\"\n  \"dm\\315\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\"\n  \"\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0tr{\\0\\0\\0\\0\"\n  \"\\0\\0\\0\\0\\0gdm\\277326\\377\\12\\12\\12\\377\\1\\1\\1\\377\\12\\12\\12\\377\\37\\40\\34\\377\"\n  \"GBU\\377ra\\307\\377yd\\360\\377m_\\262\\377c\\\\y\\377c]q\\377ear\\377idt\\377kgv\\377\"\n  \"njw\\377okx\\377okx\\377okw\\377miu\\377ifr\\377c`k\\377ebl\\377fbn\\377heq\\377hd\"\n  \"p\\377fbn\\377d`l\\377a^j\\377^[g\\377[Wd\\377VSa\\377SO_\\377SMe\\377ZP\\220\\377b\"\n  \"Q\\303\\377aR\\254\\377GCX\\377\\40!\\35\\377\\12\\12\\12\\377\\1\\1\\1\\377\\12\\12\\12\\377\"\n  \"326\\377gdm\\277\\0\\0\\0\\0\\0\\0\\0\\0tq{\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\"\n  \"\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\"\n  \"\\0\\0\\0\\0\\0tr{\\0\\0\\0\\0\\0\\0\\0\\0\\0ljr\\233@?D\\377\\21\\21\\22\\377\\2\\2\\2\\377\\5\\5\"\n  \"\\5\\377\\24\\25\\23\\377..+\\377VNy\\377s`\\323\\377ua\\347\\377k\\\\\\266\\377cZ\\200\\377\"\n  \"_Zo\\377a\\\\o\\377b^o\\377d_p\\377eap\\377ebo\\377d`m\\377c_l\\377b^k\\377a]j\\377`\"\n  \"]i\\377`\\\\h\\377_[i\\377^Zg\\377\\\\Xf\\377XTc\\377VQb\\377TNa\\377UNm\\377\\\\O\\231\\377\"\n  \"bQ\\301\\377cS\\271\\377RKu\\37700/\\377\\24\\25\\23\\377\\5\\5\\5\\377\\2\\2\\2\\377\\21\\21\"\n  \"\\22\\377@?C\\377ljs\\233\\0\\0\\0\\0\\0\\0\\0\\0tr{\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\"\n  \"\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\"\n  \"\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\15\\16\\22\\0\\0\\0\\0\\0\\177|\\206VQ\"\n  \"OV\\377\\36\\36\\40\\377\\5\\5\\5\\377\\1\\1\\1\\377\\11\\11\\11\\377\\32\\33\\26\\377323\\377\"\n  \"TLv\\377m]\\300\\377t`\\350\\377m\\\\\\314\\377eY\\243\\377`W\\202\\377]Wm\\377\\\\Wi\\377\"\n  \"[Wj\\377[Wi\\377[Vg\\377[Vf\\377ZUf\\377XTe\\377XTe\\377WRd\\377VQc\\377VOe\\377WO\"\n  \"t\\377[P\\220\\377`R\\263\\377eS\\312\\377aR\\256\\377QIs\\377537\\377\\32\\33\\27\\377\"\n  \"\\11\\11\\11\\377\\1\\1\\1\\377\\5\\5\\5\\377\\36\\36\\40\\377QOV\\377\\177|\\206V\\0\\0\\0\\0\\15\"\n  \"\\16\\27\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\"\n  \"\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\"\n  \"\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0ur{\\1\\0\\0\\0\\0\\355\\350\\372\\13c`h\\32587<\\377\"\n  \"\\20\\20\\20\\374\\2\\2\\2\\377\\3\\3\\3\\377\\12\\12\\11\\377\\27\\30\\23\\377*+'\\377EAT\\377\"\n  \"^S\\223\\377k[\\303\\377p]\\334\\377n[\\332\\377iY\\303\\377eW\\257\\377bU\\236\\377_T\"\n  \"\\224\\377^S\\217\\377]S\\217\\377^S\\221\\377^R\\227\\377`R\\245\\377bS\\265\\377eT\\310\"\n  \"\\377gU\\313\\377cT\\264\\377YN\\214\\377FAV\\377,,)\\377\\27\\30\\24\\377\\12\\12\\11\\377\"\n  \"\\3\\3\\3\\377\\2\\2\\2\\377\\20\\20\\20\\37497<\\377c`h\\325\\355\\346\\372\\13\\0\\0\\0\\0ur\"\n  \"{\\1\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\"\n  \"\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\"\n  \"\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0~|\\205\\0\\0\\0\\0\\0\\0\\0\\0\\0{x\\202\"\n  \"bTRY\\377,+.\\377\\14\\14\\14\\373\\3\\3\\3\\377\\2\\2\\2\\377\\7\\7\\7\\377\\17\\20\\15\\377\\34\"\n  \"\\35\\27\\377,,)\\377=;E\\377NGi\\377[P\\214\\377aT\\243\\377eV\\263\\377gW\\273\\377g\"\n  \"X\\277\\377gW\\276\\377eU\\270\\377cT\\257\\377_R\\237\\377YO\\212\\377NGj\\377=:F\\377\"\n  \"--+\\377\\35\\35\\31\\377\\17\\20\\15\\377\\7\\7\\7\\377\\2\\2\\2\\377\\3\\3\\3\\377\\14\\14\\14\"\n  \"\\373,+.\\377TRY\\377|x\\202b\\0\\0\\0\\0\\0\\0\\0\\0~{\\205\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\"\n  \"\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\"\n  \"\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\"\n  \"\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0vt}\\3\\0\\0\\0\\0\\377\\377\\377\\2olv\\222\"\n  \"OMT\\377/.1\\377\\22\\22\\23\\377\\5\\5\\5\\377\\1\\1\\0\\377\\3\\3\\3\\377\\10\\7\\10\\377\\16\"\n  \"\\16\\16\\377\\24\\25\\23\\377\\34\\34\\31\\377\\\"#\\40\\377))(\\377---\\3770/0\\3770/0\\377\"\n  \".-.\\377))(\\377\\\"#!\\377\\34\\34\\31\\377\\24\\25\\23\\377\\16\\16\\16\\377\\7\\7\\10\\377\"\n  \"\\3\\3\\3\\377\\1\\1\\0\\377\\5\\5\\5\\377\\22\\22\\23\\377/.1\\377OMT\\377olv\\222\\377\\377\"\n  \"\\377\\2\\0\\0\\0\\0vs}\\3\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\"\n  \"\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\"\n  \"\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\"\n  \"\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0plv\\3\\0\\0\\0\\0\\320\\312\\332\\11om\"\n  \"vsZY`\\365?>B\\377&%(\\377\\21\\20\\21\\377\\6\\6\\6\\377\\2\\2\\2\\377\\1\\1\\1\\377\\1\\1\\1\"\n  \"\\377\\3\\3\\3\\377\\5\\5\\5\\377\\5\\5\\6\\377\\7\\7\\7\\377\\7\\7\\7\\377\\5\\5\\6\\377\\5\\5\\5\\377\"\n  \"\\3\\3\\3\\377\\1\\1\\1\\377\\1\\1\\1\\377\\2\\2\\2\\377\\6\\6\\6\\377\\21\\20\\21\\377&%(\\377?>\"\n  \"B\\377ZY`\\365omvs\\320\\312\\333\\11\\0\\0\\0\\0plv\\3\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\"\n  \"\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\"\n  \"\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\"\n  \"\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\"\n  \"\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\201~\\211\\1\\0\\0\\0\\0\\0\\0\\0\\0\\202\\177\\2132jgq\\215\"\n  \"[Y`\\364FEJ\\377326\\377%$'\\377\\32\\31\\32\\377\\22\\22\\23\\377\\15\\15\\16\\377\\12\\12\"\n  \"\\13\\377\\10\\11\\11\\377\\10\\11\\11\\377\\12\\12\\13\\377\\15\\15\\15\\377\\22\\22\\23\\377\"\n  \"\\32\\31\\32\\377%$'\\377326\\377FDJ\\377[Y`\\364jgq\\215\\202\\177\\2132\\0\\0\\0\\0\\0\\0\"\n  \"\\0\\0\\201~\\211\\1\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\"\n  \"\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\"\n  \"\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\"\n  \"\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\"\n  \"\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\255\\252\\267\"\n  \"\\26wu~Sigp\\205a_g\\257YX_\\315USZ\\350SQW\\370OMT\\377OMT\\377SQW\\370USZ\\350ZX\"\n  \"_\\315a_f\\257igp\\205wu~S\\255\\251\\267\\26\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\"\n  \"\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\"\n  \"\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\"\n  \"\\0\\0\\0\\0\\0\\0\\0\",\n};\n"
  },
  {
    "path": "engine/client/image.c",
    "content": "#include \"quakedef.h\"\n#include \"shader.h\"\n#include \"glquake.h\"\t//we need some of the gl format enums\n\n#ifdef __GNUC__\n#pragma\n#endif\n\n#ifndef HAVE_CLIENT\n//#define Con_Printf(f, ...)\nfloat\tFloatSwap (float l)\n{\n\tunion {qbyte b[4]; float f;} in, out;\n\tin.f = l;\n\tout.b[0] = in.b[3];\n\tout.b[1] = in.b[2];\n\tout.b[2] = in.b[1];\n\tout.b[3] = in.b[0];\n\treturn out.f;\n}\n\n#undef S_COLOR_BLACK\n#undef S_COLOR_RED\n#undef S_COLOR_GREEN\n#undef S_COLOR_YELLOW\n#undef S_COLOR_BLUE\n#undef S_COLOR_CYAN\n#undef S_COLOR_WHITE\n#undef S_COLOR_TRANS\n#undef S_COLOR_WHITE\n#undef S_COLOR_GRAY\n#else\n#ifdef IMAGEFMT_TGA\ncvar_t r_dodgytgafiles = CVARD(\"r_dodgytgafiles\", \"0\", \"Many old glquake engines had a buggy tga loader that ignored bottom-up flags. Naturally people worked around this and the world was plagued with buggy images. Most engines have now fixed the bug, but you can reenable it if you have bugged tga files.\");\n#endif\n#ifdef IMAGEFMT_PCX\ncvar_t r_dodgypcxfiles = CVARD(\"r_dodgypcxfiles\", \"0\", \"When enabled, this will ignore the palette stored within pcx files, for compatibility with quake2.\");\n#endif\ncvar_t r_dodgymiptex = CVARD(\"r_dodgymiptex\", \"1\", \"When enabled, this will force regeneration of mipmaps, discarding mips1-4 like glquake did. This may eg solve fullbright issues with some maps, but may reduce distant detail levels.\");\ncvar_t r_keepimages = CVARD(\"r_keepimages\", \"0\", \"Retain unused images in memory for slightly faster map loading.\\n0: Redundant images will be purged after each map change.\\n1: Images will be retained until vid_reload (potentially consuming a lot of ram).\");\ncvar_t r_ignoremapprefixes = CVARD(\"r_ignoremapprefixes\", \"0\",  \"Ignores when textures were loaded from map-specific paths.\\n0: textures/foo/tex.tga will not be confused with textures/foo/tex.tga.\\n1: The same texture might be loaded multiple times over.\");\n\nchar *r_defaultimageextensions =\n#ifdef IMAGEFMT_DDS\n\t\"dds \"\t//compressed or something\n#endif\n#ifdef IMAGEFMT_KTX\n\t\"ktx \"\t//compressed or something. not to be confused with the qw mod by the same name. GL requires that etc2 compression is supported by modern drivers, but not necessarily the hardware. as such, dds with its s3tc bias should always come first (as the patents mean that drivers are much less likely to advertise it when they don't support it properly).\n\t//\"ktx2 \"\n#endif\n#ifdef IMAGEFMT_TGA\n\t\"tga\"\t//fairly fast to load\n\t//\" htga\"\n#endif\n#if defined(IMAGEFMT_PNG) || defined(FTE_TARGET_WEB)\n\t\" png\"\t//pngs, fairly common, but slow\n#endif\n#ifdef IMAGEFMT_BMP\n\t//\" bmp\"\t//wtf? at least not lossy\n\t//\" ico\"\t//noone wants this...\n#endif\n#if defined(IMAGEFMT_JPG) || defined(FTE_TARGET_WEB)\n\t\" jpg\"\t//q3 uses some jpegs, for some reason\n\t//\" jpeg\"\t//thankfuly the quake community stuck to .jpg instead\n#endif\n#ifdef IMAGEFMT_PBM\n\t//\" pfm\" //float version (technically seperate, but similarish)\n\t//\" pbm\" //1-bit v grey\n\t//\" pgm\" //greyscale\n\t//\" ppm\" //rgb values\n\t//\" pam\" //'arbitrary' version\n#endif\n#ifdef IMAGEFMT_PSD\n\t//\" psd\" //paintshop images (8bit+16bit, but base layer only)\n#endif\n#ifdef IMAGEFMT_XCF\n\t//\" xcf\" //gimp's own format\n#endif\n#ifdef IMAGEFMT_HDR\n\t//\" hdr\" //some file that uses RGBE formatted data, for hdr images.\n#endif\n#ifdef IMAGEFMT_PKM\n\t//\" pkm\"\t//compressed format, but lacks mipmaps which makes it terrible to use.\n#endif\n#ifdef IMAGEFMT_ASTC\n\t//\" astc\"\t//compressed format, but lacks mipmaps which makes it terrible to use.\n#endif\n#ifdef IMAGEFMT_GIF\n\t//\" gif\"\n#endif\n#ifdef IMAGEFMT_PIC\n\t//\" pic\"\n#endif\n#ifdef IMAGEFMT_PCX\n\t\" pcx\"\t//pcxes are the original gamedata of q2. So we don't want them to override pngs.\n#endif\n#ifdef IMAGEFMT_LMP\n\t//\" lmp\" //lame outdated junk. any code that expects a lmp will use that extension, so don't bother swapping out extensions for it.\n#endif\n\t;\n\nstatic void QDECL R_ImageExtensions_Callback(struct cvar_s *var, char *oldvalue);\ncvar_t r_imageextensions\t\t\t= CVARCD(\"r_imageextensions\", NULL, R_ImageExtensions_Callback, \"The list of image file extensions which might exist on disk (note that this does not list all supported formats, only the extensions that should be searched for).\");\ncvar_t r_image_downloadsizelimit\t= CVARFD(\"r_image_downloadsizelimit\", \"131072\", CVAR_NOTFROMSERVER, \"The maximum allowed file size of images loaded from a web-based url. 0 disables completely, while empty imposes no limit.\");\nextern cvar_t\t\t\tscr_sshot_compression;\nextern cvar_t gl_lerpimages;\nextern cvar_t gl_picmip2d;\nextern cvar_t gl_picmip;\nextern cvar_t gl_picmip_world;\nextern cvar_t gl_picmip_sprites;\nextern cvar_t gl_picmip_other;\nextern cvar_t r_shadow_bumpscale_basetexture;\nextern cvar_t r_shadow_bumpscale_bumpmap;\nextern cvar_t r_shadow_heightscale_basetexture;\nextern cvar_t r_shadow_heightscale_bumpmap;\n\n\nstatic bucket_t *imagetablebuckets[256];\nstatic hashtable_t imagetable;\nstatic image_t *imagelist;\n#endif\n\n#ifdef AVAIL_STBI\n\t#if defined(IMAGEFMT_PNG) && !defined(AVAIL_PNGLIB) && !defined(FTE_TARGET_WEB)\n\t\t#define STBI_ONLY_PNG\n\t\t#define STBIW_ONLY_PNG\n\t\t#undef IMAGEFMT_PNG\n\t#endif\n\t#if defined(IMAGEFMT_JPG) && !defined(AVAIL_JPEGLIB) && !defined(FTE_TARGET_WEB)\n\t\t#define STBI_ONLY_JPEG\n\t\t#define STBIW_ONLY_JPEG\n\t\t#undef IMAGEFMT_JPG\n\t#endif\n\t#if defined(IMAGEFMT_BMP) && 0 //use our own implementation, giving .ico too\n\t\t#define STBI_ONLY_BMP\n\t\t#define STBIW_ONLY_BMP\n\t\t#undef IMAGEFMT_BMP\n\t#endif\n\t#if defined(IMAGEFMT_PSD) && 0 //use our own implementation\n\t\t#define STBI_ONLY_PSD\n\t\t#undef IMAGEFMT_PSD\n\t#endif\n\t#if defined(IMAGEFMT_TGA) && 0 //use our own implementation, giving htga and some twiddles.\n\t\t#define STBI_ONLY_TGA\n\t\t#define STBIW_ONLY_TGA\n\t\t#undef IMAGEFMT_TGA\n\t#endif\n\t#if defined(IMAGEFMT_GIF) //&& 0\n\t\t#define STBI_ONLY_GIF\n\t\t#undef IMAGEFMT_GIF\n\t#endif\n\t#if defined(IMAGEFMT_HDR) && 0 //use our own implementation, we're not using the stbi_loadf stuff anyway\n\t\t#define STBI_ONLY_HDR\n\t\t#define STBIW_ONLY_HDR\n\t\t#undef IMAGEFMT_HDR\n\t#endif\n\t#if defined(IMAGEFMT_PIC) //&& 0\n\t\t#define STBI_ONLY_PIC\n\t\t#undef IMAGEFMT_PIC\n\t#endif\n\t#if defined(IMAGEFMT_PBM) && 0 //use our own implementation, giving pfm.pbm.pam too\n\t\t#define STBI_ONLY_PNM\n\t\t#undef IMAGEFMT_PBM\n\t#endif\n\n\t//now we know whether we need stbi or not, pull in the right stuff.\n\t#if defined(STBI_ONLY_PNG) || defined(STBI_ONLY_JPEG) || defined(STBI_ONLY_BMP) || defined(STBI_ONLY_PSD) || defined(STBI_ONLY_TGA) || defined(STBI_ONLY_GIF) || defined(STBI_ONLY_HDR) || defined(STBI_ONLY_PIC) || defined(STBI_ONLY_PNM)\n\t\t#define STBI_NO_STDIO\n\t\t#define STBI_MALLOC BZ_Malloc\n\t\t#define STBI_REALLOC BZ_Realloc\n\t\t#define STBI_FREE BZ_Free\n\t\t//#define STBI_NO_FAILURE_STRINGS //not thread safe, so don't bother showing messages.\n\t\t#define STB_IMAGE_IMPLEMENTATION\n\t\t#include \"../libs/stb_image.h\"\t//from https://raw.githubusercontent.com/nothings/stb/master/stb_image.h\n\t#endif\n\t#if defined(STBIW_ONLY_PNG) || defined(STBIW_ONLY_JPEG) || defined(STBIW_ONLY_BMP) || defined(STBIW_ONLY_TGA) || defined(STBIW_ONLY_HDR)\n\t\t#define STBI_WRITE_NO_STDIO\n\t\t#define STB_IMAGE_WRITE_STATIC\n\t\t#define STBIWDEF fte_inlinestatic\n\t\t#define STB_IMAGE_WRITE_IMPLEMENTATION\n\t\t#include \"../libs/stb_image_write.h\"\t//from https://raw.githubusercontent.com/nothings/stb/master/stb_image_write.h\n\t#endif\n#endif\n\n#if defined(IMAGEFMT_GIF) && !defined(FTE_TARGET_WEB)\n\t#pragma message(\"IMAGEFMT_GIF requires AVAIL_STBI\")\n\t#undef IMAGEFMT_GIF\n#endif\n#if defined(IMAGEFMT_PNG) && !defined(AVAIL_PNGLIB) && !defined(FTE_TARGET_WEB)\n\t#pragma message(\"IMAGEFMT_PNG requires AVAIL_PNGLIB or AVAIL_STBI\")\n\t#undef IMAGEFMT_PNG\n#elif !defined(IMAGEFMT_PNG) && defined(AVAIL_PNGLIB)\n\t#undef AVAIL_PNGLIB\n#endif\n#if defined(IMAGEFMT_JPG) && !defined(AVAIL_JPEGLIB) && !defined(FTE_TARGET_WEB)\n\t#pragma message(\"IMAGEFMT_JPG requires AVAIL_JPEGLIB or AVAIL_STBI\")\n\t#undef IMAGEFMT_JPG\n#elif !defined(IMAGEFMT_JPG) && defined(AVAIL_JPEGLIB)\n\t#undef AVAIL_JPEGLIB\n#endif\n\n#if defined(IMAGEFMT_EXR) && defined(FTE_TARGET_WEB)\n\t#undef IMAGEFMT_EXR\n#endif\n\n#ifndef LIBPNG_STATIC\n#define DYNAMIC_LIBPNG\n#endif\n#ifndef LIBJPEG_STATIC\n#define DYNAMIC_LIBJPEG\n#endif\n\n#ifdef DECOMPRESS_ASTC\n#define ASTC_PUBLIC\n#ifdef ASTC3D\n#define ASTC_WITH_3D\n#endif\n#include \"image_astc.h\"\n#endif\n\n//for soft-decoding e5bgr9's exponent. multiply by the per-channel mantissa for a 0-1 result.\nconst float rgb9e5tab[32] = {\n\t//aka: pow(2, biasedexponent - bias-bits) where bias is 15 and bits is 9\n\t1.0/(1<<24),\t1.0/(1<<23),\t1.0/(1<<22),\t1.0/(1<<21),\t1.0/(1<<20),\t1.0/(1<<19),\t1.0/(1<<18),\t1.0/(1<<17),\n\t1.0/(1<<16),\t1.0/(1<<15),\t1.0/(1<<14),\t1.0/(1<<13),\t1.0/(1<<12),\t1.0/(1<<11),\t1.0/(1<<10),\t1.0/(1<<9),\n\t1.0/(1<<8),\t\t1.0/(1<<7),\t\t1.0/(1<<6),\t\t1.0/(1<<5),\t\t1.0/(1<<4),\t\t1.0/(1<<3),\t\t1.0/(1<<2),\t\t1.0/(1<<1),\n\t1.0,\t\t\t1.0*(1<<1),\t\t1.0*(1<<2),\t\t1.0*(1<<3),\t\t1.0*(1<<4),\t\t1.0*(1<<5),\t\t1.0*(1<<6),\t\t1.0*(1<<7),\n};\nstatic float HalfToFloat(unsigned short val);\nstatic unsigned short FloatToHalf(float val);\n\n\n\nstatic struct\n{\n\tvoid *module;\n\tplugimageloaderfuncs_t *funcs;\n} *imageloader;\nstatic size_t\t\timageloader_count;\nqboolean Image_RegisterLoader(void *module, plugimageloaderfuncs_t *driver)\n{\n\tint i;\n\tif (!driver)\n\t{\n\t\tfor (i = 0; i < imageloader_count; )\n\t\t{\n\t\t\tif (imageloader[i].module == module)\n\t\t\t{\n\t\t\t\tmemmove(&imageloader[i], &imageloader[i+1], imageloader_count-(i+1));\n\t\t\t\timageloader_count--;\n\t\t\t}\n\t\t\telse\n\t\t\t\ti++;\n\t\t}\n\t\treturn true;\n\t}\n\telse\n\t{\n\t\tvoid *n = BZ_Malloc(sizeof(*imageloader)*(imageloader_count+1));\n\t\tmemcpy(n, imageloader, sizeof(*imageloader)*imageloader_count);\n\t\tZ_Free(imageloader);\n\t\timageloader = n;\n\t\timageloader[imageloader_count].module = module;\n\t\timageloader[imageloader_count].funcs = driver;\n\t\timageloader_count++;\n\t\treturn true;\n\t}\n}\n\n\n#if defined(AVAIL_JPEGLIB) || defined(AVAIL_PNGLIB)\nstatic void GenerateXMPData(char *blob, size_t blobsize, int width, int height, unsigned int metainfo)\n{\t//XMP is a general thing that applies to multiple formats - or at least png+jpeg.\n\t//we need this if we want to correctly flag the data as a 360 image.\n#ifdef HAVE_CLIENT\n\tQ_snprintfz(blob, blobsize,\n\t\t\"<x:xmpmeta xmlns:x='adobe:ns:meta/'>\"\n\t\t\t\"<rdf:RDF xmlns:rdf='http://www.w3.org/1999/02/22-rdf-syntax-ns#'>\"\n\t\t);\n\n\tif (metainfo)\n\t\tQ_snprintfz(blob, blobsize,\n\t\t\t\t\t\"<rdf:Description rdf:about='' xmlns:GPano=\\\"http://ns.google.com/photos/1.0/panorama/\\\">\"\n\t\t\t\t\t\t\"<GPano:ProjectionType>equirectangular</GPano:ProjectionType>\"\n\t\t\t\t\t\t\"<GPano:PosePitchDegrees>%f</GPano:PosePitchDegrees>\"\n\t\t\t\t\t\t\"<GPano:PoseHeadingDegrees>%f</GPano:PoseHeadingDegrees>\"\n\t\t\t\t\t\t\"<GPano:PoseRollDegrees>%f</GPano:PoseRollDegrees>\"\n\t\t\t\t\t\t\"<GPano:InitialViewHeadingDegrees>%f</GPano:InitialViewHeadingDegrees>\"\n\t\t\t\t\t\t\"<GPano:InitialViewPitchDegrees>%f</GPano:InitialViewPitchDegrees>\"\n\t\t\t\t\t\t\"<GPano:InitialViewRollDegrees>%f</GPano:InitialViewRollDegrees>\"\n\t\t\t\t\t\t\"<GPano:CroppedAreaLeftPixels>0</GPano:CroppedAreaLeftPixels>\"\n\t\t\t\t\t\t\"<GPano:CroppedAreaTopPixels>0</GPano:CroppedAreaTopPixels>\"\n\t\t\t\t\t\t\"<GPano:CroppedAreaImageWidthPixels>%i</GPano:CroppedAreaImageWidthPixels>\"\n\t\t\t\t\t\t\"<GPano:CroppedAreaImageHeightPixels>%i</GPano:CroppedAreaImageHeightPixels>\"\n\t\t\t\t\t\t\"<GPano:FullPanoWidthPixels>%i</GPano:FullPanoWidthPixels>\"\n\t\t\t\t\t\t\"<GPano:FullPanoHeightPixels>%i</GPano:FullPanoHeightPixels>\"\n\t\t\t\t\t\"</rdf:Description>\",\n\t\t\tr_refdef.viewangles[0], r_refdef.viewangles[1], r_refdef.viewangles[2],\n\t\t\tr_refdef.viewangles[0], r_refdef.viewangles[1], r_refdef.viewangles[2],\n\t\t\twidth, height, width, height);\n\n\tQ_snprintfz(blob+strlen(blob), blobsize-strlen(blob),\n\t\t\t\"</rdf:RDF>\"\n\t\t\"</x:xmpmeta>\"\n\t\t);\n#else\n\tblob[blobsize] = 0;\n#endif\n}\n#endif\n\n#ifdef IMAGEFMT_TGA\n#ifndef _WIN32\n#include <unistd.h>\n#endif\n\ntypedef struct {\t//cm = colourmap\n\tchar\tid_len;\t\t//0\n\tchar\tcm_type;\t//1\n\tqbyte\tversion;\t//2\n\t\tchar pad1;\n\tshort\tcm_idx;\t\t//3\n\tshort\tcm_len;\t\t//5\n\tchar\tcm_size;\t//7\n\t\tchar pad2;\n\tshort\toriginx;\t//8 (ignored)\n\tshort\toriginy;\t//10 (ignored)\n\tshort\twidth;\t\t//12-13\n\tshort\theight;\t\t//14-15\n\tqbyte\tbpp;\t\t//16\n\tqbyte\tattribs;\t//17\n} tgaheader_t;\n\nstatic char *ReadGreyTargaFile (qbyte *data, int flen, tgaheader_t *tgahead, int asgrey)\t//preswapped header\n{\n\tint\t\t\t\tcolumns, rows;\n\tint\t\t\t\trow, column;\n\tqbyte\t\t\t*pixbuf, *pal;\n\tqboolean\t\tflipped;\n\n\tqbyte *pixels = BZ_Malloc(tgahead->width * tgahead->height * (asgrey?1:4));\n\n\tif (tgahead->version!=1\n\t\t&& tgahead->version!=3)\n\t{\n\t\tCon_Printf(\"LoadGrayTGA: Only type 1 and 3 greyscale targa images are understood.\\n\");\n\t\tBZ_Free(pixels);\n\t\treturn NULL;\n\t}\n\n\tif (tgahead->version==1 && tgahead->bpp != 8 &&\n\t\ttgahead->cm_size != 24 && tgahead->cm_len != 256)\n\t{\n\t\tCon_Printf(\"LoadGrayTGA: Strange palette type\\n\");\n\t\tBZ_Free(pixels);\n\t\treturn NULL;\n\t}\n\n\tcolumns = tgahead->width;\n\trows = tgahead->height;\n\n\tflipped = !((tgahead->attribs & 0x20) >> 5);\n#ifdef HAVE_CLIENT\n\tif (r_dodgytgafiles.value)\n\t\tflipped = true;\n#endif\n\n\tif (tgahead->version == 1)\n\t{\t//paletted data...\n\t\tpal = data;\n\t\tdata += tgahead->cm_len*3;\n\t\tif (asgrey)\n\t\t{\n\t\t\tfor(row=rows-1; row>=0; row--)\n\t\t\t{\n\t\t\t\tif (flipped)\n\t\t\t\t\tpixbuf = pixels + row*columns;\n\t\t\t\telse\n\t\t\t\t\tpixbuf = pixels + ((rows-1)-row)*columns;\n\n\t\t\t\tfor(column=0; column<columns; column++)\n\t\t\t\t\t*pixbuf++= *data++;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tfor(row=rows-1; row>=0; row--)\n\t\t\t{\n\t\t\t\tif (flipped)\n\t\t\t\t\tpixbuf = pixels + row*columns*4;\n\t\t\t\telse\n\t\t\t\t\tpixbuf = pixels + ((rows-1)-row)*columns*4;\n\n\t\t\t\tfor(column=0; column<columns; column++)\n\t\t\t\t{\n\t\t\t\t\t*pixbuf++= pal[*data*3+2];\n\t\t\t\t\t*pixbuf++= pal[*data*3+1];\n\t\t\t\t\t*pixbuf++= pal[*data*3+0];\n\t\t\t\t\t*pixbuf++= 255;\n\t\t\t\t\tdata++;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn pixels;\n\t}\n\t//version 3 now. pure greyscale\n\n\tif (asgrey)\n\t{\n\t\tfor(row=rows-1; row>=0; row--)\n\t\t{\n\t\t\tif (flipped)\n\t\t\t\tpixbuf = pixels + row*columns;\n\t\t\telse\n\t\t\t\tpixbuf = pixels + ((rows-1)-row)*columns;\n\n\t\t\tpixbuf = pixels + row*columns;\n\t\t\tfor(column=0; column<columns; column++)\n\t\t\t\t*pixbuf++= *data++;\n\t\t}\n\t}\n\telse\n\t{\n\t\tfor(row=rows-1; row>=0; row--)\n\t\t{\n\t\t\tif (flipped)\n\t\t\t\tpixbuf = pixels + row*columns*4;\n\t\t\telse\n\t\t\t\tpixbuf = pixels + ((rows-1)-row)*columns*4;\n\n\t\t\tfor(column=0; column<columns; column++)\n\t\t\t{\n\t\t\t\t*pixbuf++= *data;\n\t\t\t\t*pixbuf++= *data;\n\t\t\t\t*pixbuf++= *data;\n\t\t\t\t*pixbuf++= 255;\n\t\t\t\tdata++;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn pixels;\n}\n\n#define MISSHORT(ptr) (*(ptr) | (*(ptr+1) << 8))\n//remember to free it\n//greyonly causes the function to fail if given anything but greyscale images\n//this is for detecting heightmaps instead of normalmaps.\nvoid *ReadTargaFile(qbyte *buf, int length, int *width, int *height, uploadfmt_t *format, qboolean greyonly, uploadfmt_t forceformat)\n{\n\t//tga files sadly lack a true magic header thing.\n\tunsigned char *data;\n\n\tqboolean flipped;\n\n\ttgaheader_t tgaheader;\t//things are misaligned, so no pointer.\n\n\tif (length < 18 || buf[1] > 1)\n\t\treturn NULL;\t//probably not a tga...\n\n\ttgaheader.id_len = buf[0];\n\ttgaheader.cm_type = buf[1];\n\ttgaheader.version = buf[2];\n\ttgaheader.cm_idx = MISSHORT(buf+3);\n\ttgaheader.cm_len = MISSHORT(buf+5);\n\ttgaheader.cm_size = buf[7];\n\ttgaheader.originx = MISSHORT(buf+8);\n\ttgaheader.originy = MISSHORT(buf+10);\n\ttgaheader.width = MISSHORT(buf+12);\n\ttgaheader.height = MISSHORT(buf+14);\n\ttgaheader.bpp = buf[16];\n\ttgaheader.attribs = buf[17];\n\n\tswitch(tgaheader.version)\n\t{\n\tcase 0:\t//No image data included.\n\t\treturn NULL;\t//not really valid for us. reject it after all\n\tcase 1:\t//Uncompressed, color-mapped images.\n\tcase 2:\t//Uncompressed, RGB images.\n\tcase 3:\t//Uncompressed, black and white images.\n\tcase 9:\t//Runlength encoded color-mapped images.\n\tcase 10:\t//Runlength encoded RGB images.\n\tcase 11:\t//Runlength encoded, black and white images.\n\tcase 32:\t//Compressed color-mapped data, using Huffman, Delta, and runlength encoding.\n\tcase 33:\t//Compressed color-mapped data, using Huffman, Delta, and runlength encoding.  4-pass quadtree-type process.\n\t\tif (buf[16] != 8 && buf[16] != 16 && buf[16] != 24 && buf[16] != 32)\n\t\t\treturn NULL;\t//unsupported bitdepths\n\t\tbreak;\n\t//0x80+ are third-party extensions...\n\tcase 0x82:\t//half-float rgb\n\tcase 0x83:\t//half-float greyscale\n\t\tif (forceformat != PTI_INVALID)\n\t\t\treturn NULL;\n\t\tif ((buf[16]&15) || buf[16]<16 || buf[16] > 16*4)\n\t\t\treturn NULL;\t//unsupported bitdepths\n\t\tbreak;\n\tdefault:\n\t\treturn NULL;\n\t}\n\t//validate the size to some sanity limit.\n\tif ((unsigned short)tgaheader.width > 16384 || (unsigned short)tgaheader.height > 16384)\n\t\treturn NULL;\n\n\n\tflipped = !((tgaheader.attribs & 0x20) >> 5);\n#ifdef HAVE_CLIENT\n\tif (r_dodgytgafiles.value)\n\t\tflipped = true;\n#endif\n\n\tdata=buf+18;\n\tdata += tgaheader.id_len;\n\n\t*width = tgaheader.width;\n\t*height = tgaheader.height;\n\n\tif (greyonly)\t//grey only, load as 8 bit..\n\t{\n\t\tif (!(tgaheader.version == 1) && !(tgaheader.version == 3) && !(tgaheader.version == 11))\n\t\t\treturn NULL;\n\t}\n\tif (tgaheader.version == 1 || tgaheader.version == 3)\n\t{\n\t\tif (forceformat==PTI_L8 || forceformat==PTI_RGBA8 || forceformat==PTI_RGBX8)\n\t\t\t*format = forceformat;\n\t\telse if (tgaheader.version == 3)\n\t\t\t*format = PTI_L8;\n\t\telse\n\t\t\t*format = PTI_RGBX8;\n\t\treturn ReadGreyTargaFile(data, length, &tgaheader, *format==PTI_L8);\n\t}\n\telse if (tgaheader.version == 10 || tgaheader.version == 9 || tgaheader.version == 11)\n\t{\n\t\t//9:RLE paletted\n\t\t//10:RLE bgr(a)\n\t\t//11:RLE greyscale\n#undef getc\n#define getc(x) *data++\n\t\tunsigned int row, rows=tgaheader.height, column, columns=tgaheader.width, packetHeader, packetSize, j;\n\t\tqbyte *pixbuf, *targa_rgba;\n\t\tunsigned int inraw;\n\n\t\tqbyte blue, red, green, alphabyte;\n\n\t\tbyte_vec4_t palette[256];\n\t\tenum\n\t\t{\n\t\t\trle_p8,\n\t\t\trle_a1rgb5,\n\t\t\trle_bgr8,\n\t\t\trle_bgra8,\n\t\t\trle_l8,\n\t\t\trle_l8a8,\n\t\t} rlemode;\n\t\tint outbytes;\n\n\t\t*format = PTI_RGBX8;\n\t\tif (tgaheader.version == 9)\n\t\t{\t//RLE palette\n\t\t\tif (tgaheader.bpp == 8)\t//FIXME: tgaheader.bpp can be 8, 15, or 16.\n\t\t\t\trlemode = rle_p8;\n\t\t\telse\n\t\t\t\treturn NULL;\n\n\t\t\tfor (row = 0; row < 256; row++)\n\t\t\t{\n\t\t\t\tpalette[row][0] = row;\n\t\t\t\tpalette[row][1] = row;\n\t\t\t\tpalette[row][2] = row;\n\t\t\t\tpalette[row][3] = 255;\n\t\t\t}\n\n\t\t\tif (forceformat == PTI_L8 || forceformat == PTI_INVALID)\n\t\t\t\t*format = forceformat = PTI_L8;\n\t\t\telse\n\t\t\t\t*format = forceformat = PTI_LLLX8;\n\n\n\t\t\tif (tgaheader.cm_type)\n\t\t\t{\n\t\t\t\tqboolean grey = true;\n\t\t\t\tswitch(tgaheader.cm_size)\n\t\t\t\t{\n\t\t\t\tdefault:\n\t\t\t\t\treturn NULL;\n\t\t\t\tcase 24:\n\t\t\t\t\tfor (row = 0; row < tgaheader.cm_len; row++)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (data[0] != data[1] || data[0] != data[2])\n\t\t\t\t\t\t\tgrey = false;\n\t\t\t\t\t\tpalette[row][0] = *data++;\n\t\t\t\t\t\tpalette[row][1] = *data++;\n\t\t\t\t\t\tpalette[row][2] = *data++;\n\t\t\t\t\t\tpalette[row][3] = 255;\n\t\t\t\t\t}\n\t\t\t\t\tif (grey && forceformat == PTI_INVALID)\n\t\t\t\t\t\t*format = PTI_L8;\n\t\t\t\t\telse if (forceformat != PTI_L8)\n\t\t\t\t\t\t*format = PTI_RGBA8;\n\t\t\t\t\tbreak;\n\t\t\t\tcase 32:\n\t\t\t\t\tfor (row = 0; row < tgaheader.cm_len; row++)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (data[0] != data[1] || data[0] != data[2] || data[3] != 0xff)\n\t\t\t\t\t\t\tgrey = false;\n\t\t\t\t\t\tpalette[row][0] = *data++;\n\t\t\t\t\t\tpalette[row][1] = *data++;\n\t\t\t\t\t\tpalette[row][2] = *data++;\n\t\t\t\t\t\tpalette[row][3] = *data++;\n\t\t\t\t\t}\n\t\t\t\t\tif (grey && forceformat == PTI_INVALID)\n\t\t\t\t\t\t*format = PTI_L8;\n\t\t\t\t\telse if (forceformat != PTI_L8)\n\t\t\t\t\t\t*format = PTI_RGBA8;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse if (tgaheader.version == 10)\n\t\t{\t//RLE truecolour\n\t\t\tif (tgaheader.bpp == 16)\n\t\t\t\trlemode = rle_a1rgb5;\n\t\t\telse if (tgaheader.bpp == 24)\n\t\t\t\trlemode = rle_bgr8;\n\t\t\telse if (tgaheader.bpp == 32)\n\t\t\t\trlemode = rle_bgra8;\n\t\t\telse\n\t\t\t\treturn NULL;\n\n\t\t\t*format = (tgaheader.bpp==24)?PTI_RGBX8:PTI_RGBA8;\n\t\t}\n\t\telse if (tgaheader.version == 11)\n\t\t{\t//RLE greyscale\n\t\t\tif (tgaheader.bpp == 8)\n\t\t\t\trlemode = rle_l8;\n\t\t\telse if (tgaheader.bpp == 16)\n\t\t\t\trlemode = rle_l8a8;\n\t\t\telse\n\t\t\t\treturn NULL;\n\n\t\t\tif (forceformat == PTI_L8)\n\t\t\t\t*format = forceformat;\n\t\t\telse if (rlemode==rle_l8a8)\n\t\t\t\t*format = PTI_LLLA8; //should probably use PTI_L8A8, but the caller will know to optimise\n\t\t\telse\n\t\t\t\t*format = PTI_LLLX8;\n\t\t}\n\t\telse\n\t\t\treturn NULL;\n\n\t\tif (*format == PTI_L8)\n\t\t\toutbytes = 1;\n\t\telse if (*format == PTI_L8A8)\n\t\t\toutbytes = 2;\n\t\telse\n\t\t\toutbytes = 4;\n\t\ttarga_rgba=BZ_Malloc(rows*columns*outbytes);\n\t\tfor(row=rows; row-->0; )\n\t\t{\n\t\t\tif (flipped)\n\t\t\t\tpixbuf = targa_rgba + row*columns*outbytes;\n\t\t\telse\n\t\t\t\tpixbuf = targa_rgba + ((rows-1)-row)*columns*outbytes;\n\t\t\tfor(column=0; column<columns; )\n\t\t\t{\n\t\t\t\tpacketHeader=*data++;\n\t\t\t\tpacketSize = 1 + (packetHeader & 0x7f);\n\t\t\t\tif (packetHeader & 0x80)\n\t\t\t\t{\t// run-length packet\n\t\t\t\t\tswitch (rlemode)\n\t\t\t\t\t{\n\t\t\t\t\tcase rle_p8:\n\t\t\t\t\t\tblue = palette[*data][0];\n\t\t\t\t\t\tgreen = palette[*data][1];\n\t\t\t\t\t\tred = palette[*data][2];\n\t\t\t\t\t\talphabyte = palette[*data][3];\n\t\t\t\t\t\tdata++;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase rle_l8a8:\n\t\t\t\t\t\tblue = green = red = *data++;\n\t\t\t\t\t\talphabyte = *data++;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase rle_l8:\n\t\t\t\t\t\tblue = green = red = *data++;\n\t\t\t\t\t\talphabyte = 255;\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase rle_a1rgb5:\n\t\t\t\t\t\tinraw = data[0] | (data[1]<<8);\n\t\t\t\t\t\tdata+=2;\n\t\t\t\t\t\talphabyte = (inraw&0x8000)?255:0;\n\t\t\t\t\t\tred = ((inraw>>10)&0x1f)<<3;\n\t\t\t\t\t\tgreen = ((inraw>>5)&0x1f)<<3;\n\t\t\t\t\t\tblue = ((inraw>>0)&0x1f)<<3;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase rle_bgr8:\n\t\t\t\t\t\tblue = *data++;\n\t\t\t\t\t\tgreen = *data++;\n\t\t\t\t\t\tred = *data++;\n\t\t\t\t\t\talphabyte = 255;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase rle_bgra8:\n\t\t\t\t\t\tblue = *data++;\n\t\t\t\t\t\tgreen = *data++;\n\t\t\t\t\t\tred = *data++;\n\t\t\t\t\t\talphabyte = *data++;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tblue = green = red = alphabyte = 255;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (*format!=PTI_L8)\t//keep colours\n\t\t\t\t\t{\n\t\t\t\t\t\tfor(j=0;j<packetSize;j++)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t*pixbuf++=red;\n\t\t\t\t\t\t\t*pixbuf++=green;\n\t\t\t\t\t\t\t*pixbuf++=blue;\n\t\t\t\t\t\t\t*pixbuf++=alphabyte;\n\t\t\t\t\t\t\tcolumn++;\n\t\t\t\t\t\t\tif (column==columns)\n\t\t\t\t\t\t\t{ // run spans across rows\n\t\t\t\t\t\t\t\tcolumn=0;\n\t\t\t\t\t\t\t\tif (row>0)\n\t\t\t\t\t\t\t\t\trow--;\n\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t\tgoto breakOut;\n\t\t\t\t\t\t\t\tif (flipped)\n\t\t\t\t\t\t\t\t\tpixbuf = targa_rgba + row*columns*4;\n\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t\tpixbuf = targa_rgba + ((rows-1)-row)*columns*4;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\telse\t//convert to greyscale\n\t\t\t\t\t{\n\t\t\t\t\t\tfor(j=0;j<packetSize;j++)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t*pixbuf++ = red*NTSC_RED + green*NTSC_GREEN + blue*NTSC_BLUE;\n\t\t\t\t\t\t\tcolumn++;\n\t\t\t\t\t\t\tif (column==columns)\n\t\t\t\t\t\t\t{ // run spans across rows\n\t\t\t\t\t\t\t\tcolumn=0;\n\t\t\t\t\t\t\t\tif (row>0)\n\t\t\t\t\t\t\t\t\trow--;\n\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t\tgoto breakOut;\n\t\t\t\t\t\t\t\tif (flipped)\n\t\t\t\t\t\t\t\t\tpixbuf = targa_rgba + row*columns*1;\n\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t\tpixbuf = targa_rgba + ((rows-1)-row)*columns*1;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{                            // non run-length packet\n\t\t\t\t\tif (*format!=PTI_L8)\t//keep colours\n\t\t\t\t\t{\n\t\t\t\t\t\tfor(j=0;j<packetSize;j++)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tswitch (rlemode)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase rle_p8:\n\t\t\t\t\t\t\t\tblue = palette[*data][0];\n\t\t\t\t\t\t\t\tgreen = palette[*data][1];\n\t\t\t\t\t\t\t\tred = palette[*data][2];\n\t\t\t\t\t\t\t\talphabyte = palette[*data][3];\n\t\t\t\t\t\t\t\tdata++;\n\t\t\t\t\t\t\t\t*pixbuf++ = red;\n\t\t\t\t\t\t\t\t*pixbuf++ = green;\n\t\t\t\t\t\t\t\t*pixbuf++ = blue;\n\t\t\t\t\t\t\t\t*pixbuf++ = alphabyte;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tcase rle_l8a8:\n\t\t\t\t\t\t\t\tblue = green = red = *data++;\n\t\t\t\t\t\t\t\talphabyte = *data++;\n\t\t\t\t\t\t\t\t*pixbuf++ = red;\n\t\t\t\t\t\t\t\t*pixbuf++ = green;\n\t\t\t\t\t\t\t\t*pixbuf++ = blue;\n\t\t\t\t\t\t\t\t*pixbuf++ = alphabyte;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tcase rle_l8:\n\t\t\t\t\t\t\t\tblue = green = red = *data++;\n\t\t\t\t\t\t\t\t*pixbuf++ = red;\n\t\t\t\t\t\t\t\t*pixbuf++ = green;\n\t\t\t\t\t\t\t\t*pixbuf++ = blue;\n\t\t\t\t\t\t\t\t*pixbuf++ = 255;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tcase rle_a1rgb5:\n\t\t\t\t\t\t\t\tinraw = data[0] | (data[1]<<8);\n\t\t\t\t\t\t\t\tdata+=2;\n\t\t\t\t\t\t\t\talphabyte = (inraw&0x8000)?255:0;\n\t\t\t\t\t\t\t\tred = ((inraw>>10)&0x1f)<<3;\n\t\t\t\t\t\t\t\tgreen = ((inraw>>5)&0x1f)<<3;\n\t\t\t\t\t\t\t\tblue = ((inraw>>0)&0x1f)<<3;\n\n\t\t\t\t\t\t\t\t*pixbuf++ = red;\n\t\t\t\t\t\t\t\t*pixbuf++ = green;\n\t\t\t\t\t\t\t\t*pixbuf++ = blue;\n\t\t\t\t\t\t\t\t*pixbuf++ = alphabyte;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tcase rle_bgr8:\n\t\t\t\t\t\t\t\tblue = *data++;\n\t\t\t\t\t\t\t\tgreen = *data++;\n\t\t\t\t\t\t\t\tred = *data++;\n\t\t\t\t\t\t\t\t*pixbuf++ = red;\n\t\t\t\t\t\t\t\t*pixbuf++ = green;\n\t\t\t\t\t\t\t\t*pixbuf++ = blue;\n\t\t\t\t\t\t\t\t*pixbuf++ = 255;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tcase rle_bgra8:\n\t\t\t\t\t\t\t\tblue = *data++;\n\t\t\t\t\t\t\t\tgreen = *data++;\n\t\t\t\t\t\t\t\tred = *data++;\n\t\t\t\t\t\t\t\talphabyte = *data++;\n\t\t\t\t\t\t\t\t*pixbuf++ = red;\n\t\t\t\t\t\t\t\t*pixbuf++ = green;\n\t\t\t\t\t\t\t\t*pixbuf++ = blue;\n\t\t\t\t\t\t\t\t*pixbuf++ = alphabyte;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tcolumn++;\n\t\t\t\t\t\t\tif (column==columns)\n\t\t\t\t\t\t\t{ // pixel packet run spans across rows\n\t\t\t\t\t\t\t\tcolumn=0;\n\t\t\t\t\t\t\t\tif (row>0)\n\t\t\t\t\t\t\t\t\trow--;\n\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t\tgoto breakOut;\n\t\t\t\t\t\t\t\tif (flipped)\n\t\t\t\t\t\t\t\t\tpixbuf = targa_rgba + row*columns*4;\n\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t\tpixbuf = targa_rgba + ((rows-1)-row)*columns*4;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\telse\t//convert to grey\n\t\t\t\t\t{\n\t\t\t\t\t\tfor(j=0;j<packetSize;j++)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tswitch (rlemode)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase rle_p8:\n\t\t\t\t\t\t\t\tblue = palette[*data][0];\n\t\t\t\t\t\t\t\tgreen = palette[*data][1];\n\t\t\t\t\t\t\t\tred = palette[*data][2];\n\t\t\t\t\t\t\t\t*pixbuf++ = (blue + green + red)/3;\n\t\t\t\t\t\t\t\tdata++;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tcase rle_l8a8:\n\t\t\t\t\t\t\t\tblue = green = red = *data++;\n\t\t\t\t\t\t\t\talphabyte = *data++;\n\t\t\t\t\t\t\t\t*pixbuf++ = green;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tcase rle_l8:\n\t\t\t\t\t\t\t\tblue = green = red = *data++;\n\t\t\t\t\t\t\t\t*pixbuf++ = green;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tcase rle_a1rgb5:\n\t\t\t\t\t\t\t\tinraw = data[0] | (data[1]<<8);\n\t\t\t\t\t\t\t\tdata+=2;\n\t\t\t\t\t\t\t\talphabyte = (inraw&0x8000)?255:0;\n\t\t\t\t\t\t\t\tred = ((inraw>>10)&0x1f)<<3;\n\t\t\t\t\t\t\t\tgreen = ((inraw>>5)&0x1f)<<3;\n\t\t\t\t\t\t\t\tblue = ((inraw>>0)&0x1f)<<3;\n\n\t\t\t\t\t\t\t\t*pixbuf++ = red*NTSC_RED + green*NTSC_GREEN + blue*NTSC_BLUE;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tcase rle_bgr8:\n\t\t\t\t\t\t\t\tblue = *data++;\n\t\t\t\t\t\t\t\tgreen = *data++;\n\t\t\t\t\t\t\t\tred = *data++;\n\t\t\t\t\t\t\t\t*pixbuf++ = red*NTSC_RED + green*NTSC_GREEN + blue*NTSC_BLUE;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tcase rle_bgra8:\n\t\t\t\t\t\t\t\tblue = *data++;\n\t\t\t\t\t\t\t\tgreen = *data++;\n\t\t\t\t\t\t\t\tred = *data++;\n\t\t\t\t\t\t\t\talphabyte = *data++;\n\t\t\t\t\t\t\t\t*pixbuf++ = red*NTSC_RED + green*NTSC_GREEN + blue*NTSC_BLUE;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tcolumn++;\n\t\t\t\t\t\t\tif (column==columns)\n\t\t\t\t\t\t\t{ // pixel packet run spans across rows\n\t\t\t\t\t\t\t\tcolumn=0;\n\t\t\t\t\t\t\t\tif (row>0)\n\t\t\t\t\t\t\t\t\trow--;\n\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t\tgoto breakOut;\n\t\t\t\t\t\t\t\tif (flipped)\n\t\t\t\t\t\t\t\t\tpixbuf = targa_rgba + row*columns*1;\n\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t\tpixbuf = targa_rgba + ((rows-1)-row)*columns*1;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tbreakOut:;\n\n\t\treturn targa_rgba;\n\t}\n\telse if ((tgaheader.version == 0x82||tgaheader.version == 0x83) && forceformat && forceformat!=PTI_RGBA16F)\n\t\tCon_Printf(\"HTGA: required output format is not half-float\\n\");\n\telse if ((tgaheader.version == 0x82||tgaheader.version == 0x83) && !(tgaheader.bpp&15) && tgaheader.bpp>=16 && tgaheader.bpp<=16*4)\n\t{\t//packed r[g[b[a]]]f\n\t\tunsigned short *initbuf, *inrow, *outrow;\n\t\tint x, y, mul;\n\n\t\tif (tgaheader.version == 0x83 && tgaheader.bpp==16)\n\t\t\t*format = forceformat = PTI_R16F;\n\t\telse\n\t\t\t*format = forceformat = PTI_RGBA16F; //gray+alpha needs to be rgbaf\n\n\t\tinitbuf = BZ_Malloc(tgaheader.height*tgaheader.width* ((forceformat==PTI_R16F)?2:8));\n\n\t\tmul = tgaheader.bpp/8;\n//flip +convert to 32 bit\n\t\toutrow = &initbuf[(int)(0)*tgaheader.width*mul];\n\t\tfor (y = 0; y < tgaheader.height; y+=1)\n\t\t{\n\t\t\tif (flipped)\n\t\t\t\tinrow = (unsigned short*)&data[(int)(tgaheader.height-y-1)*tgaheader.width*mul];\n\t\t\telse\n\t\t\t\tinrow = (unsigned short*)&data[(int)(y)*tgaheader.width*mul];\n\n\t\t\tswitch(mul)\n\t\t\t{\n\t\t\tdefault:\t//bug!\n\t\t\t\tfor (x = 0; x < tgaheader.width; x+=1)\n\t\t\t\t{\n\t\t\t\t\t*outrow++ = 0;\n\t\t\t\t\t*outrow++ = 0;\n\t\t\t\t\t*outrow++ = 0;\n\t\t\t\t\t*outrow++ = 0xf<<10;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase 2:\t//Lum\n\t\t\t\tif (forceformat == PTI_R16F)\n\t\t\t\t{\n\t\t\t\t\tfor (x = 0; x < tgaheader.width; x+=1)\n\t\t\t\t\t\t*outrow++ = *inrow++;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tfor (x = 0; x < tgaheader.width; x+=1)\n\t\t\t\t\t{\n\t\t\t\t\t\t*outrow++ = *inrow;\n\t\t\t\t\t\t*outrow++ = *inrow;\n\t\t\t\t\t\t*outrow++ = *inrow;\n\t\t\t\t\t\t*outrow++ = 0xf<<10; //1.0\n\t\t\t\t\t\tinrow+=1;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase 4:\n\t\t\t\tif (tgaheader.version == 0x83)\n\t\t\t\t{\t//treat as LumAlpha\n\t\t\t\t\tfor (x = 0; x < tgaheader.width; x+=1)\n\t\t\t\t\t{\n\t\t\t\t\t\t*outrow++ = inrow[0];\n\t\t\t\t\t\t*outrow++ = inrow[0];\n\t\t\t\t\t\t*outrow++ = inrow[0];\n\t\t\t\t\t\t*outrow++ = inrow[1];\n\t\t\t\t\t\tinrow+=2;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\t//RG\n\t\t\t\t\tfor (x = 0; x < tgaheader.width; x+=1)\n\t\t\t\t\t{\n\t\t\t\t\t\t*outrow++ = inrow[0];\n\t\t\t\t\t\t*outrow++ = inrow[1];\n\t\t\t\t\t\t*outrow++ = 0;\n\t\t\t\t\t\t*outrow++ = 0xf<<10; //1.0\n\t\t\t\t\t\tinrow+=2;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase 6: //BGR\n\t\t\t\tfor (x = 0; x < tgaheader.width; x+=1)\n\t\t\t\t{\n\t\t\t\t\t*outrow++ = inrow[2];\n\t\t\t\t\t*outrow++ = inrow[1];\n\t\t\t\t\t*outrow++ = inrow[0];\n\t\t\t\t\t*outrow++ = 0xf<<10; //1.0\n\t\t\t\t\tinrow+=3;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase 8: //BGRA16F, swizzle to rgba\n\t\t\t\tfor (x = 0; x < tgaheader.width; x+=1)\n\t\t\t\t{\n\t\t\t\t\t*outrow++ = inrow[2];\n\t\t\t\t\t*outrow++ = inrow[1];\n\t\t\t\t\t*outrow++ = inrow[0];\n\t\t\t\t\t*outrow++ = inrow[3];\n\t\t\t\t\tinrow+=4;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\treturn initbuf;\n\t}\n\telse if (tgaheader.version == 2)\n\t{\t//packed format\n\t\tqbyte *initbuf, *inrow, *outrow;\n\t\tint x, y, mul;\n\t\tqbyte blue, red, green;\n\n\t\tif (tgaheader.bpp == 8)\n\t\t\treturn NULL;\n\t\tinitbuf=BZ_Malloc(tgaheader.height*tgaheader.width* ((forceformat==PTI_L8)?1:4));\n\n\t\tmul = tgaheader.bpp/8;\n//flip +convert to 32 bit\n\t\tif (forceformat==PTI_L8)\n\t\t{\n\t\t\t*format = forceformat;\n\t\t\toutrow = &initbuf[(int)(0)*tgaheader.width];\n\t\t}\n\t\telse\n\t\t{\n\t\t\toutrow = &initbuf[(int)(0)*tgaheader.width*mul];\n\t\t\t*format = (mul==4)?PTI_RGBA8:PTI_RGBX8;\n\t\t}\n\t\tfor (y = 0; y < tgaheader.height; y+=1)\n\t\t{\n\t\t\tif (flipped)\n\t\t\t\tinrow = &data[(int)(tgaheader.height-y-1)*tgaheader.width*mul];\n\t\t\telse\n\t\t\t\tinrow = &data[(int)(y)*tgaheader.width*mul];\n\n\t\t\tif (forceformat!=PTI_L8)\n\t\t\t{\n\t\t\t\tswitch(mul)\n\t\t\t\t{\n\t\t\t\tcase 2:\n\t\t\t\t\tfor (x = 0; x < tgaheader.width; x+=1)\n\t\t\t\t\t{\n\t\t\t\t\t\t*outrow++ = ((inrow[1] & 0x7c)>>2) *8;\t\t\t\t\t//red\n\t\t\t\t\t\t*outrow++ = (((inrow[1] & 0x03)<<3) + ((inrow[0] & 0xe0)>>5))*8;\t//green\n\t\t\t\t\t\t*outrow++ = (inrow[0] & 0x1f)*8;\t\t\t\t\t//blue\n\t\t\t\t\t\t*outrow++ = (int)(inrow[1]&0x80)*2-1;\t\t\t//alpha?\n\t\t\t\t\t\tinrow+=2;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase 3:\n\t\t\t\t\tfor (x = 0; x < tgaheader.width; x+=1)\n\t\t\t\t\t{\n\t\t\t\t\t\t*outrow++ = inrow[2];\n\t\t\t\t\t\t*outrow++ = inrow[1];\n\t\t\t\t\t\t*outrow++ = inrow[0];\n\t\t\t\t\t\t*outrow++ = 255;\n\t\t\t\t\t\tinrow+=3;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase 4:\n\t\t\t\t\tfor (x = 0; x < tgaheader.width; x+=1)\n\t\t\t\t\t{\n\t\t\t\t\t\t*outrow++ = inrow[2];\n\t\t\t\t\t\t*outrow++ = inrow[1];\n\t\t\t\t\t\t*outrow++ = inrow[0];\n\t\t\t\t\t\t*outrow++ = inrow[3];\n\t\t\t\t\t\tinrow+=4;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tswitch(mul)\n\t\t\t\t{\n\t\t\t\tcase 2:\n\t\t\t\t\tfor (x = 0; x < tgaheader.width; x+=1)\n\t\t\t\t\t{\n\t\t\t\t\t\tred = ((inrow[1] & 0x7c)>>2) *8;\t\t\t\t\t//red\n\t\t\t\t\t\tgreen = (((inrow[1] & 0x03)<<3) + ((inrow[0] & 0xe0)>>5))*8;\t//green\n\t\t\t\t\t\tblue = (inrow[0] & 0x1f)*8;\t\t\t\t\t//blue\n//\t\t\t\t\t\talphabyte = (int)(inrow[1]&0x80)*2-1;\t\t\t//alpha?\n\n\t\t\t\t\t\t*outrow++ = red*NTSC_RED + green*NTSC_GREEN + blue*NTSC_BLUE;\n\t\t\t\t\t\tinrow+=2;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase 3:\n\t\t\t\t\tfor (x = 0; x < tgaheader.width; x+=1)\n\t\t\t\t\t{\n\t\t\t\t\t\tred = inrow[2];\n\t\t\t\t\t\tgreen = inrow[1];\n\t\t\t\t\t\tblue = inrow[0];\n\t\t\t\t\t\t*outrow++ = red*NTSC_RED + green*NTSC_GREEN + blue*NTSC_BLUE;\n\t\t\t\t\t\tinrow+=3;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase 4:\n\t\t\t\t\tfor (x = 0; x < tgaheader.width; x+=1)\n\t\t\t\t\t{\n\t\t\t\t\t\tred = inrow[2];\n\t\t\t\t\t\tgreen = inrow[1];\n\t\t\t\t\t\tblue = inrow[0];\n\t\t\t\t\t\t*outrow++ = red*NTSC_RED + green*NTSC_GREEN + blue*NTSC_BLUE;\n\t\t\t\t\t\tinrow+=4;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (forceformat!=PTI_L8)\n\t\t{\n\t\t\tfor (x = 0; x < tgaheader.width*tgaheader.height*4; x+=4)\n\t\t\t{\n\t\t\t\tif (initbuf[x+0] != initbuf[x+1] || initbuf[x+0] != initbuf[x+2] || initbuf[x+3] != 0xff)\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (x == tgaheader.width*tgaheader.height*4)\n\t\t\t{\t//no alpha\n\t\t\t\tif (forceformat==PTI_INVALID)\n\t\t\t\t\t*format = PTI_LLLX8;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tfor (; x < tgaheader.width*tgaheader.height*4; x+=4)\n\t\t\t\t{\n\t\t\t\t\tif (initbuf[x+0] != initbuf[x+1] || initbuf[x+0] != initbuf[x+2])\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tif (x == tgaheader.width*tgaheader.height*4)\n\t\t\t\t{\t//okay, there's some alpha data in there.\n\t\t\t\t\tif (forceformat==PTI_INVALID)\n\t\t\t\t\t\t*format = PTI_LLLA8;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn initbuf;\n\t}\n\telse\n\t\tCon_Printf(\"TGA: Unsupported version\\n\");\n\treturn NULL;\n}\n\nqboolean WriteTGA(const char *filename, enum fs_relative fsroot, const qbyte *fte_restrict rgb_buffer, qintptr_t bytestride, int width, int height, enum uploadfmt fmt)\n{\n\tqboolean success = false;\n\tsize_t c, i;\n\tvfsfile_t *vfs;\n\tint ipx,opx;\n\tqboolean rgb;\n\tstatic const unsigned char footer[26] =\n\t{\t//added in v2, just makes it clear that it is actually a tga\n\t\t0,0,0,0,//extension area offset\n\t\t0,0,0,0,//developer area offset\n\t\t'T','R','U','E','V','I','S','I','O','N','-','X','F','I','L','E', //the truth is out there\n\t\t'.', 0\n\t};\n\tunsigned char header[18];\n\tmemset (header, 0, 18);\n\n\tif (fmt == PTI_RGBA16F)\n\t{\n\t\theader[2] = 0x82;\t//uncompressed RGB half-float\n\t\topx = 8;\n\t\tipx = 8;\n\t\trgb = true;\n\t}\n\telse if (fmt == PTI_R16F)\n\t{\n\t\theader[2] = 0x83;\t//uncompressed greyscale half-float\n\t\topx = 2;\n\t\tipx = 2;\n\t\trgb = false;\n\t}\n\telse\n\t{\n\t\theader[2] = 2;\t\t\t// uncompressed true-colour type\n\t\tif (fmt == PTI_ARGB1555)\n\t\t{\n\t\t\trgb = false;\n\t\t\tipx = 2;\n\t\t\topx = 2;\n\t\t\theader[17] |= 1&0xf;\t//1bit alpha.\n\t\t}\n\t\telse if (fmt == PTI_RGBA8 || fmt == PTI_BGRA8)\n\t\t{\n\t\t\trgb = fmt==TF_RGBA32;\n\t\t\tipx = 4;\n\t\t\topx = 4;\n\t\t\theader[17] |= 8&0xf;\t//alpha is 8bit\n\t\t}\n\t\telse if (fmt == PTI_RGBX8 || fmt == PTI_BGRX8)\n\t\t{\n\t\t\trgb = fmt==PTI_RGBX8;\n\t\t\tipx = 4;\n\t\t\topx = 3;\n\t\t}\n\t\telse if (fmt == PTI_RGB8 || fmt == PTI_BGR8)\n\t\t{\n\t\t\trgb = fmt==PTI_RGB8;\n\t\t\tipx = 3;\n\t\t\topx = 3;\n\t\t}\n\t\t/*else if (fmt == PTI_RGBA16)\n\t\t{\n\t\t\trgb = true;\n\t\t\tipx = 8;\n\t\t\topx = 8;\n\t\t}*/\n\t\telse if (fmt==PTI_LLLA8)\n\t\t{\n\t\t\trgb = false;\n\t\t\tipx = 4;\n\t\t\topx = 2;\n\t\t\theader[2] = 3;\t\t\t// greyscale\n\t\t\theader[17] |= 8&0xf;\t\t\t// with alpha\n\t\t}\n\t\telse if (fmt==PTI_LLLX8)\n\t\t{\n\t\t\trgb = false;\n\t\t\tipx = 4;\n\t\t\topx = 1;\n\t\t\theader[2] = 3;\t\t\t// greyscale\n\t\t}\n\t\telse if (fmt == PTI_L8)\n\t\t{\n\t\t\trgb = false;\n\t\t\tipx = 1;\n\t\t\topx = 1;\n\t\t\theader[2] = 3;\t\t\t// greyscale\n\t\t}\n\t\telse if (fmt == PTI_L8A8)\n\t\t{\n\t\t\trgb = false;\n\t\t\tipx = 2;\n\t\t\topx = 2;\n\t\t\theader[2] = 3;\t\t\t// greyscale\n\t\t\theader[17] |= 8&0xf;\t\t\t//with alpha\n\t\t}\n\t\telse\n\t\t\treturn false;\n\t}\n\n\tFS_CreatePath(filename, fsroot);\n\tvfs = FS_OpenVFS(filename, \"wb\", fsroot);\n\tif (vfs)\n\t{\n\t\theader[12] = width&255;\n\t\theader[13] = width>>8;\n\t\theader[14] = height&255;\n\t\theader[15] = height>>8;\n\t\theader[16] = opx*8;\t\t// pixel size\n\t\theader[17] |= 0x00;\t\t// flags\n\n\t\tif (bytestride < 0)\n\t\t{\t//if we're upside down, lets just use an upside down tga.\n\t\t\trgb_buffer += bytestride*(height-1);\n\t\t\tbytestride = -bytestride;\n\t\t\t//now we can just do everything without worrying about rows\n\t\t}\n\t\telse\t//our data is top-down, set up the header to also be top-down.\n\t\t\theader[17] |= 0x20;\n\n\t\tVFS_WRITE(vfs, header, sizeof(header));\n\t\tif (ipx == opx && !rgb)\n\t\t{\t//can just directly write it\n\t\t\t//bgr24, bgra24\n\t\t\tc = (size_t)width*height*opx;\n\n\t\t\tVFS_WRITE(vfs, rgb_buffer, c);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tqbyte *fte_restrict rgb_out = malloc((size_t)width*opx*height);\n\n\t\t\tif (opx == 1)\n\t\t\t{\t//L8, LLLX8\n\t\t\t\tc = (size_t)width*height;\n\t\t\t\tfor (i=0 ; i<c ; i++)\n\t\t\t\t\trgb_out[i] = rgb_buffer[i*ipx+0];\n\t\t\t}\n\t\t\telse if (opx == 2)\n\t\t\t{\t//L8A8, LLLA8\n\t\t\t\tc = (size_t)width*height;\n\t\t\t\tfor (i=0 ; i<c ; i++)\n\t\t\t\t{\n\t\t\t\t\trgb_out[i*2+0] = rgb_buffer[i*ipx+0];\n\t\t\t\t\trgb_out[i*2+1] = rgb_buffer[i*ipx+ipx-1];\n\t\t\t\t}\n\t\t\t}\n\t\t\t//no need to swap alpha, and if we're just swapping alpha will be fine in-place.\n\t\t\telse if (opx == 8)\n\t\t\t{\t//rgba16, rgba16f\n\t\t\t\t//(output is bgra still)\n\t\t\t\tc = (size_t)width*height;\n\t\t\t\tfor (i=0 ; i<c ; i++)\n\t\t\t\t{\n\t\t\t\t\trgb_out[i*opx+0] = rgb_buffer[i*ipx+4];\n\t\t\t\t\trgb_out[i*opx+1] = rgb_buffer[i*ipx+5];\n\t\t\t\t\trgb_out[i*opx+2] = rgb_buffer[i*ipx+2];\n\t\t\t\t\trgb_out[i*opx+3] = rgb_buffer[i*ipx+3];\n\t\t\t\t\trgb_out[i*opx+4] = rgb_buffer[i*ipx+0];\n\t\t\t\t\trgb_out[i*opx+5] = rgb_buffer[i*ipx+1];\n\t\t\t\t\trgb_out[i*opx+6] = rgb_buffer[i*ipx+6];\n\t\t\t\t\trgb_out[i*opx+7] = rgb_buffer[i*ipx+7];\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (opx == 4)\n\t\t\t{\t//rgba32, bgra32\n\t\t\t\tint rc = rgb?0:2;\n\t\t\t\tint bc = rgb?2:0;\n\t\t\t\tc = (size_t)width*height;\n\t\t\t\tfor (i=0 ; i<c ; i++)\n\t\t\t\t{\n\t\t\t\t\trgb_out[i*4+0] = rgb_buffer[i*ipx+bc];\n\t\t\t\t\trgb_out[i*4+1] = rgb_buffer[i*ipx+1];\n\t\t\t\t\trgb_out[i*4+2] = rgb_buffer[i*ipx+rc];\n\t\t\t\t\trgb_out[i*4+3] = rgb_buffer[i*ipx+3];\n\t\t\t\t}\n\t\t\t}\n\t\t\telse //if (opx == 3)\n\t\t\t{\t//rgba32, bgra32\n\t\t\t\tint rc = rgb?0:2;\n\t\t\t\tint bc = rgb?2:0;\n\t\t\t\tc = (size_t)width*height;\n\t\t\t\tfor (i=0 ; i<c ; i++)\n\t\t\t\t{\n\t\t\t\t\trgb_out[i*3+0] = rgb_buffer[i*ipx+bc];\n\t\t\t\t\trgb_out[i*3+1] = rgb_buffer[i*ipx+1];\n\t\t\t\t\trgb_out[i*3+2] = rgb_buffer[i*ipx+rc];\n\t\t\t\t}\n\t\t\t}\n\t\t\tc *= opx;\n\n\t\t\tVFS_WRITE(vfs, rgb_out, c);\n\t\t\tfree(rgb_out);\n\t\t}\n\t\tVFS_WRITE(vfs, footer, sizeof(footer));\n\n\t\tsuccess = VFS_CLOSE(vfs);\n\t}\n\treturn success;\n}\n#endif\n\n#ifdef AVAIL_PNGLIB\n\t#ifndef AVAIL_ZLIB\n\t\t#error PNGLIB requires ZLIB\n\t#endif\n\n\t#undef channels\n\n\t#ifndef PNG_SUCKS_WITH_SETJMP\n\t\t#include \"png.h\"\n\t#endif\n\n\t#ifdef DYNAMIC_LIBPNG\n\t\t#define PSTATIC(n)\n\t\tstatic dllhandle_t *libpng_handle;\n\t\t#define LIBPNG_LOADED() (libpng_handle != NULL)\n\t#else\n\t\t#define LIBPNG_LOADED() 1\n\t\t#define PSTATIC(n) = &n\n\t\t#ifdef _MSC_VER\n\t\t\t#ifdef _WIN64\n\t\t\t\t#pragma comment(lib, MSVCLIBSPATH \"libpng64.lib\")\n\t\t\t#else\n\t\t\t\t#pragma comment(lib, MSVCLIBSPATH \"libpng.lib\")\n\t\t\t#endif\n\t\t#endif\n\t#endif\n\n#ifndef PNG_NORETURN\n#define PNG_NORETURN\n#endif\n#ifndef PNG_ALLOCATED\n#define PNG_ALLOCATED\n#endif\n\n#if PNG_LIBPNG_VER < 10500\n\t#define png_const_infop png_infop\n\t#define png_const_structp png_structp\n\t#define png_const_bytep png_bytep\n\t#define png_const_unknown_chunkp png_unknown_chunkp\n\t#define png_const_textp png_textp\n\t#define png_const_colorp png_colorp\n#endif\n#if PNG_LIBPNG_VER < 10600\n\t#define png_inforp png_infop\n\t#define png_const_inforp png_const_infop\n\t#define png_structrp png_structp\n\t#define png_const_structrp png_const_structp\n#endif\n\nstatic void (PNGAPI *qpng_error) PNGARG((png_const_structrp png_ptr, png_const_charp error_message)) PSTATIC(png_error);\nstatic void (PNGAPI *qpng_read_end) PNGARG((png_structp png_ptr, png_infop info_ptr)) PSTATIC(png_read_end);\nstatic void (PNGAPI *qpng_read_image) PNGARG((png_structp png_ptr, png_bytepp image)) PSTATIC(png_read_image);\nstatic png_byte (PNGAPI *qpng_get_bit_depth) PNGARG((png_const_structp png_ptr, png_const_inforp info_ptr)) PSTATIC(png_get_bit_depth);\nstatic png_byte (PNGAPI *qpng_get_channels) PNGARG((png_const_structp png_ptr, png_const_inforp info_ptr)) PSTATIC(png_get_channels);\n#if PNG_LIBPNG_VER < 10400\n\tstatic png_uint_32 (PNGAPI *qpng_get_rowbytes) PNGARG((png_const_structp png_ptr, png_const_inforp info_ptr)) PSTATIC(png_get_rowbytes);\n#else\n\tstatic png_size_t (PNGAPI *qpng_get_rowbytes) PNGARG((png_const_structp png_ptr, png_const_inforp info_ptr)) PSTATIC(png_get_rowbytes);\n#endif\nstatic void (PNGAPI *qpng_read_update_info) PNGARG((png_structp png_ptr, png_infop info_ptr)) PSTATIC(png_read_update_info);\nstatic void (PNGAPI *qpng_set_strip_16) PNGARG((png_structp png_ptr)) PSTATIC(png_set_strip_16);\nstatic void (PNGAPI *qpng_set_swap) PNGARG((png_structp png_ptr)) PSTATIC(png_set_swap);\nstatic void (PNGAPI *qpng_set_expand) PNGARG((png_structp png_ptr)) PSTATIC(png_set_expand);\nstatic void (PNGAPI *qpng_set_gray_to_rgb) PNGARG((png_structp png_ptr)) PSTATIC(png_set_gray_to_rgb);\nstatic void (PNGAPI *qpng_set_tRNS_to_alpha) PNGARG((png_structp png_ptr)) PSTATIC(png_set_tRNS_to_alpha);\nstatic png_uint_32 (PNGAPI *qpng_get_valid) PNGARG((png_const_structp png_ptr, png_const_infop info_ptr, png_uint_32 flag)) PSTATIC(png_get_valid);\n#if PNG_LIBPNG_VER >= 10400\nstatic void (PNGAPI *qpng_set_expand_gray_1_2_4_to_8) PNGARG((png_structp png_ptr)) PSTATIC(png_set_expand_gray_1_2_4_to_8);\n#else\nstatic void (PNGAPI *qpng_set_gray_1_2_4_to_8) PNGARG((png_structp png_ptr)) PSTATIC(png_set_gray_1_2_4_to_8);\n#endif\nstatic void (PNGAPI *qpng_set_bgr) PNGARG((png_structp png_ptr)) PSTATIC(png_set_bgr);\nstatic void (PNGAPI *qpng_set_filler) PNGARG((png_structp png_ptr, png_uint_32 filler, int flags)) PSTATIC(png_set_filler);\nstatic void (PNGAPI *qpng_set_palette_to_rgb) PNGARG((png_structp png_ptr)) PSTATIC(png_set_palette_to_rgb);\nstatic png_uint_32 (PNGAPI *qpng_get_IHDR) PNGARG((png_const_structrp png_ptr, png_const_inforp info_ptr, png_uint_32 *width, png_uint_32 *height,\n\t\t\tint *bit_depth, int *color_type, int *interlace_method, int *compression_method, int *filter_method)) PSTATIC(png_get_IHDR);\nstatic png_uint_32 (PNGAPI *qpng_get_PLTE) PNGARG((png_const_structrp png_ptr, png_inforp info_ptr, png_colorp *palette, int *num_palette)) PSTATIC(png_get_PLTE);\nstatic png_uint_32 (PNGAPI *qpng_get_tRNS) PNGARG((png_const_structrp png_ptr, png_inforp info_ptr, png_bytep *trans_alpha, int *num_trans, png_color_16p *trans_color)) PSTATIC(png_get_tRNS);\n\nstatic void (PNGAPI *qpng_read_info) PNGARG((png_structp png_ptr, png_infop info_ptr)) PSTATIC(png_read_info);\nstatic void (PNGAPI *qpng_set_sig_bytes) PNGARG((png_structp png_ptr, int num_bytes)) PSTATIC(png_set_sig_bytes);\nstatic void (PNGAPI *qpng_set_read_fn) PNGARG((png_structp png_ptr, png_voidp io_ptr, png_rw_ptr read_data_fn)) PSTATIC(png_set_read_fn);\nstatic void (PNGAPI *qpng_destroy_read_struct) PNGARG((png_structpp png_ptr_ptr, png_infopp info_ptr_ptr, png_infopp end_info_ptr_ptr)) PSTATIC(png_destroy_read_struct);\nstatic png_infop (PNGAPI *qpng_create_info_struct) PNGARG((png_const_structrp png_ptr)) PSTATIC(png_create_info_struct);\nstatic png_structp (PNGAPI *qpng_create_read_struct) PNGARG((png_const_charp user_png_ver, png_voidp error_ptr, png_error_ptr error_fn, png_error_ptr warn_fn)) PSTATIC(png_create_read_struct);\nstatic int (PNGAPI *qpng_sig_cmp) PNGARG((png_const_bytep sig, png_size_t start, png_size_t num_to_check)) PSTATIC(png_sig_cmp);\n\nstatic void (PNGAPI *qpng_write_end) PNGARG((png_structrp png_ptr, png_inforp info_ptr)) PSTATIC(png_write_end);\nstatic void (PNGAPI *qpng_write_image) PNGARG((png_structrp png_ptr, png_bytepp image)) PSTATIC(png_write_image);\nstatic void (PNGAPI *qpng_write_info) PNGARG((png_structrp png_ptr, png_const_inforp info_ptr)) PSTATIC(png_write_info);\n#ifdef PNG_TEXT_SUPPORTED\nstatic void (PNGAPI *qpng_set_text) PNGARG((png_const_structrp png_ptr, png_infop info_ptr, png_const_textp text_ptr, int num_text)) PSTATIC(png_set_text);\n#endif\nstatic void (PNGAPI *qpng_set_IHDR) PNGARG((png_const_structrp png_ptr, png_infop info_ptr, png_uint_32 width, png_uint_32 height,\n\t\t\tint bit_depth, int color_type, int interlace_method, int compression_method, int filter_method)) PSTATIC(png_set_IHDR);\n//static void png_set_PLTE(void);\nstatic void (PNGAPI *qpng_set_PLTE) PNGARG((png_structrp png_ptr, png_inforp info_ptr, png_const_colorp palette, int num_palette)) PSTATIC(png_set_PLTE);\nstatic void (PNGAPI *qpng_set_compression_level) PNGARG((png_structrp png_ptr, int level)) PSTATIC(png_set_compression_level);\nstatic void (PNGAPI *qpng_init_io) PNGARG((png_structp png_ptr, png_FILE_p fp)) PSTATIC(png_init_io);\nstatic png_voidp (PNGAPI *qpng_get_io_ptr) PNGARG((png_const_structrp png_ptr)) PSTATIC(png_get_io_ptr);\nstatic void (PNGAPI *qpng_destroy_write_struct) PNGARG((png_structpp png_ptr_ptr, png_infopp info_ptr_ptr)) PSTATIC(png_destroy_write_struct);\nstatic png_structp (PNGAPI *qpng_create_write_struct) PNGARG((png_const_charp user_png_ver, png_voidp error_ptr, png_error_ptr error_fn, png_error_ptr warn_fn)) PSTATIC(png_create_write_struct);\nstatic void (PNGAPI *qpng_set_unknown_chunks) PNGARG((png_const_structrp png_ptr, png_inforp info_ptr, png_const_unknown_chunkp unknowns, int num_unknowns)) PSTATIC(png_set_unknown_chunks);\n\nstatic png_voidp (PNGAPI *qpng_get_error_ptr) PNGARG((png_const_structrp png_ptr)) PSTATIC(png_get_error_ptr);\n\nqboolean LibPNG_Init(void)\n{\n#ifdef DYNAMIC_LIBPNG\n\tstatic dllfunction_t pngfuncs[] =\n\t{\n\t\t{(void **) &qpng_error,\t\t\t\t\t\t\t\"png_error\"},\n\t\t{(void **) &qpng_read_end,\t\t\t\t\t\t\"png_read_end\"},\n\t\t{(void **) &qpng_read_image,\t\t\t\t\t\"png_read_image\"},\n\t\t{(void **) &qpng_get_bit_depth,\t\t\t\t\t\"png_get_bit_depth\"},\n\t\t{(void **) &qpng_get_channels,\t\t\t\t\t\"png_get_channels\"},\n\t\t{(void **) &qpng_get_rowbytes,\t\t\t\t\t\"png_get_rowbytes\"},\n\t\t{(void **) &qpng_read_update_info,\t\t\t\t\"png_read_update_info\"},\n\t\t{(void **) &qpng_set_strip_16,\t\t\t\t\t\"png_set_strip_16\"},\n\t\t{(void **) &qpng_set_swap,\t\t\t\t\t\t\"png_set_swap\"},\n\t\t{(void **) &qpng_set_expand,\t\t\t\t\t\"png_set_expand\"},\n\t\t{(void **) &qpng_set_gray_to_rgb,\t\t\t\t\"png_set_gray_to_rgb\"},\n\t\t{(void **) &qpng_set_tRNS_to_alpha,\t\t\t\t\"png_set_tRNS_to_alpha\"},\n\t\t{(void **) &qpng_get_valid,\t\t\t\t\t\t\"png_get_valid\"},\n#if PNG_LIBPNG_VER > 10400\n\t\t{(void **) &qpng_set_expand_gray_1_2_4_to_8,\t\"png_set_expand_gray_1_2_4_to_8\"},\n#else\n\t\t{(void **) &qpng_set_gray_1_2_4_to_8,\t\"png_set_gray_1_2_4_to_8\"},\n#endif\n\t\t{(void **) &qpng_set_bgr,\t\t\t\t\t\t\"png_set_bgr\"},\n\t\t{(void **) &qpng_set_filler,\t\t\t\t\t\"png_set_filler\"},\n\t\t{(void **) &qpng_set_palette_to_rgb,\t\t\t\"png_set_palette_to_rgb\"},\n\t\t{(void **) &qpng_get_IHDR,\t\t\t\t\t\t\"png_get_IHDR\"},\n\t\t{(void **) &qpng_get_PLTE,\t\t\t\t\t\t\"png_get_PLTE\"},\n\t\t{(void **) &qpng_get_tRNS,\t\t\t\t\t\t\"png_get_tRNS\"},\n\t\t{(void **) &qpng_read_info,\t\t\t\t\t\t\"png_read_info\"},\n\t\t{(void **) &qpng_set_sig_bytes,\t\t\t\t\t\"png_set_sig_bytes\"},\n\t\t{(void **) &qpng_set_read_fn,\t\t\t\t\t\"png_set_read_fn\"},\n\t\t{(void **) &qpng_destroy_read_struct,\t\t\t\"png_destroy_read_struct\"},\n\t\t{(void **) &qpng_create_info_struct,\t\t\t\"png_create_info_struct\"},\n\t\t{(void **) &qpng_create_read_struct,\t\t\t\"png_create_read_struct\"},\n\t\t{(void **) &qpng_sig_cmp,\t\t\t\t\t\t\"png_sig_cmp\"},\n\n#ifdef PNG_TEXT_SUPPORTED\n\t\t{(void **) &qpng_set_text,\t\t\t\t\t\t\"png_set_text\"},\n#endif\n\t\t{(void **) &qpng_write_end,\t\t\t\t\t\t\"png_write_end\"},\n\t\t{(void **) &qpng_write_image,\t\t\t\t\t\"png_write_image\"},\n\t\t{(void **) &qpng_write_info,\t\t\t\t\t\"png_write_info\"},\n\t\t{(void **) &qpng_set_IHDR,\t\t\t\t\t\t\"png_set_IHDR\"},\n\t\t{(void **) &qpng_set_PLTE,\t\t\t\t\t\t\"png_set_PLTE\"},\n\t\t{(void **) &qpng_set_compression_level,\t\t\t\"png_set_compression_level\"},\n\t\t{(void **) &qpng_init_io,\t\t\t\t\t\t\"png_init_io\"},\n\t\t{(void **) &qpng_get_io_ptr,\t\t\t\t\t\"png_get_io_ptr\"},\n\t\t{(void **) &qpng_destroy_write_struct,\t\t\t\"png_destroy_write_struct\"},\n\t\t{(void **) &qpng_create_write_struct,\t\t\t\"png_create_write_struct\"},\n\t\t{(void **) &qpng_set_unknown_chunks,\t\t\t\"png_set_unknown_chunks\"},\n\n\t\t{(void **) &qpng_get_error_ptr,\t\t\t\t\t\"png_get_error_ptr\"},\n\t\t{NULL, NULL}\n\t};\n\tstatic qboolean tried;\n\tif (!tried)\n\t{\n\t\ttried = true;\n\n\t\tif (!LIBPNG_LOADED())\n\t\t{\n\t\t\tchar *libnames[] =\n\t\t\t{\n\t\t\t#ifdef _WIN32\n\t\t\t\t\"libpng\" STRINGIFY(PNG_LIBPNG_VER_DLLNUM)\n\t\t\t#else\n\t\t\t\t//linux...\n\t\t\t\t//lsb uses 'libpng12.so' specifically, so make sure that works.\n\t\t\t\t\"libpng\" STRINGIFY(PNG_LIBPNG_VER_MAJOR) STRINGIFY(PNG_LIBPNG_VER_MINOR) \".so.\" STRINGIFY(PNG_LIBPNG_VER_SONUM),\n\t\t\t\t\"libpng\" STRINGIFY(PNG_LIBPNG_VER_MAJOR) STRINGIFY(PNG_LIBPNG_VER_MINOR) \".so\",\n\t\t\t\t\"libpng.so.\" STRINGIFY(PNG_LIBPNG_VER_SONUM)\n\t\t\t\t\"libpng.so\",\n\t\t\t#endif\n\t\t\t};\n\t\t\tsize_t i;\n\t\t\tfor (i = 0; i < countof(libnames); i++)\n\t\t\t{\n\t\t\t\tif (libnames[i])\n\t\t\t\t{\n\t\t\t\t\tlibpng_handle = Sys_LoadLibrary(libnames[i], pngfuncs);\n\t\t\t\t\tif (libpng_handle)\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!libpng_handle)\n\t\t\t\tCon_Printf(\"Unable to load %s\\n\", libnames[0]);\n\t\t}\n\n//\t\tif (!LIBPNG_LOADED())\n//\t\t\tlibpng_handle = Sys_LoadLibrary(\"libpng\", pngfuncs);\n\t}\n#endif\n\treturn LIBPNG_LOADED();\n}\n\ntypedef struct {\n\tchar *data;\n\tint readposition;\n\tint filelen;\n} pngreadinfo_t;\n\nstatic void VARGS readpngdata(png_structp png_ptr,png_bytep data,png_size_t len)\n{\n\tpngreadinfo_t *ri = (pngreadinfo_t*)qpng_get_io_ptr(png_ptr);\n\tif (ri->readposition+len > ri->filelen)\n\t{\n\t\tqpng_error(png_ptr, \"unexpected eof\");\n\t\treturn;\n\t}\n\tmemcpy(data, &ri->data[ri->readposition], len);\n\tri->readposition+=len;\n}\n\nstruct pngerr\n{\n\tconst char *fname;\n\tjmp_buf jbuf;\n};\nstatic void VARGS png_onerror(png_structp png_ptr, png_const_charp error_msg)\n{\n\tstruct pngerr *err = qpng_get_error_ptr(png_ptr);\n\tCon_Printf(\"libpng %s: %s\\n\", err->fname, error_msg);\n\tlongjmp(err->jbuf, 1);\n\tabort();\n}\n\nstatic void VARGS png_onwarning(png_structp png_ptr, png_const_charp warning_msg)\n{\n\tstruct pngerr *err = qpng_get_error_ptr(png_ptr);\n\tCon_DPrintf(\"libpng %s: %s\\n\", err->fname, warning_msg);\n}\n\nqbyte *ReadPNGFile(const char *fname, qbyte *buf, int length, int *width, int *height, uploadfmt_t *format, qboolean force_rgb32)\n{\n\tqbyte header[8], **rowpointers = NULL, *data = NULL;\n\tpng_structp png;\n\tpng_infop pnginfo;\n\tint y, bitdepth, colortype, interlace, compression, filter, channels;\n\tunsigned long rowbytes;\n\tpngreadinfo_t ri;\n\tpng_uint_32 pngwidth, pngheight;\n\tstruct pngerr errctx;\n\n\tif (!LibPNG_Init())\n\t{\n\t\tCon_Printf(\"libpng not loaded\\n\");\n\t\treturn NULL;\n\t}\n\n\tmemcpy(header, buf, 8);\n\n\terrctx.fname = fname;\n\tif (setjmp(errctx.jbuf))\n\t{\nerror:\n\t\tif (data)\n\t\t\tBZ_Free(data);\n\t\tif (rowpointers)\n\t\t\tBZ_Free(rowpointers);\n\t\tqpng_destroy_read_struct(&png, &pnginfo, NULL);\n\t\tCon_Printf(\"libpng error\\n\");\n\t\treturn NULL;\n\t}\n\n\tif (qpng_sig_cmp(header, 0, 8))\n\t{\n\t\tCon_Printf(\"libpng signature mismatch\\n\");\t//we already checked the first four bytes so this shouldn't be spammy\n\t\treturn NULL;\n\t}\n\n\tif (!(png = qpng_create_read_struct(PNG_LIBPNG_VER_STRING, &errctx, png_onerror, png_onwarning)))\n\t{\n\t\tCon_Printf(\"png_create_read_struct failed\\n\");\t//we already checked the first four bytes so this shouldn't be spammy\n\t\treturn NULL;\n\t}\n\n\tif (!(pnginfo = qpng_create_info_struct(png)))\n\t{\n\t\tCon_Printf(\"png_create_info_struct failed\\n\");\t//we already checked the first four bytes so this shouldn't be spammy\n\t\tqpng_destroy_read_struct(&png, &pnginfo, NULL);\n\t\treturn NULL;\n\t}\n\n\tri.data=buf;\n\tri.readposition=8;\n\tri.filelen=length;\n\tqpng_set_read_fn(png, &ri, readpngdata);\n\n\tqpng_set_sig_bytes(png, 8);\n\tqpng_read_info(png, pnginfo);\n\tqpng_get_IHDR(png, pnginfo, &pngwidth, &pngheight, &bitdepth, &colortype, &interlace, &compression, &filter);\n\n\t*width = pngwidth;\n\t*height = pngheight;\n\n\tif (colortype == PNG_COLOR_TYPE_PALETTE)\n\t{\n\t\tint numpal = 0, numtrans = 0;\n\t\tpng_colorp pal = NULL;\n\t\tpng_bytep trans = NULL;\n\t\tif (bitdepth==8 && !force_rgb32)\n\t\t{\n\t\t\tqpng_get_tRNS(png, pnginfo, &trans, &numtrans, NULL);\n\t\t\tqpng_get_PLTE(png, pnginfo, &pal, &numpal);\n\t\t}\n\t\tif (numpal == 256)\n\t\t{\n\t\t\tfor (numpal = 0; numpal < 256; numpal++)\n\t\t\t{\n\t\t\t\tif (pal[numpal].red == host_basepal[numpal*3+0] &&\n\t\t\t\t\tpal[numpal].green == host_basepal[numpal*3+1] &&\n\t\t\t\t\tpal[numpal].blue == host_basepal[numpal*3+2])\n\t\t\t\t{\n\t\t\t\t\tif (numpal < numtrans && trans[numpal] != 255)\n\t\t\t\t\t\tbreak; //we require it to be fully opaque, because our PTI_P8 has no transparency info(nor does quake) and we don't want to make assumptions here.\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tbreak; //bum\n\t\t\t}\n\t\t}\n\t\tif (numpal != 256)\n\t\t{\t//the palette isn't ours. give up and just treat it as rgb(a)\n\t\t\tqpng_set_palette_to_rgb(png);\n\t\t\tqpng_set_filler(png, ~0u, PNG_FILLER_AFTER);\n\t\t\tcolortype = PNG_COLOR_TYPE_RGB;\n\t\t}\n\t}\n\n\tif (colortype == PNG_COLOR_TYPE_GRAY && bitdepth < 8)\n\t{\t//don't handle small greyscale formats\n\t\t#if PNG_LIBPNG_VER > 10400\n\t\t\tqpng_set_expand_gray_1_2_4_to_8(png);\n\t\t#else\n\t\t\tqpng_set_gray_1_2_4_to_8(png);\n\t\t#endif\n\t}\n\n\tif (colortype != PNG_COLOR_TYPE_PALETTE && qpng_get_valid( png, pnginfo, PNG_INFO_tRNS))\n\t{\n\t\tqpng_set_tRNS_to_alpha(png);\n\t\t//for these types, the trns lump specifies a specific colour to treat as transparent.\n\t\t//make sure our resulting format reflects it.\n\t\tif (colortype == PNG_COLOR_TYPE_GRAY)\n\t\t\tcolortype = PNG_COLOR_TYPE_GRAY_ALPHA;\n\t\telse if (colortype == PNG_COLOR_TYPE_RGB)\n\t\t\tcolortype = PNG_COLOR_TYPE_RGB_ALPHA;\n\t}\n\n\tif (bitdepth >= 8 && colortype == PNG_COLOR_TYPE_RGB)\n\t\tqpng_set_filler(png, ~0u, PNG_FILLER_AFTER);\n\n\t//expand greyscale to rgb when we're forcing 32bit output (with padding, as appropriate).\n\tif ((colortype == PNG_COLOR_TYPE_GRAY || colortype == PNG_COLOR_TYPE_GRAY_ALPHA)&&force_rgb32)\n\t{\n\t\tqpng_set_gray_to_rgb( png );\n\t\tif (colortype == PNG_COLOR_TYPE_GRAY)\n\t\t\tqpng_set_filler(png, ~0u, PNG_FILLER_AFTER);\n\t}\n\n\tif (bitdepth < 8)\n\t\tqpng_set_expand (png);\n\telse if (bitdepth == 16 && (force_rgb32 || colortype!=PNG_COLOR_TYPE_RGB_ALPHA))\n\t\tqpng_set_strip_16(png);\n\telse if (bitdepth == 16)\n        qpng_set_swap(png);\n\n\tqpng_read_update_info(png, pnginfo);\n\trowbytes = qpng_get_rowbytes(png, pnginfo);\n\tchannels = qpng_get_channels(png, pnginfo);\n\tbitdepth = qpng_get_bit_depth(png, pnginfo);\n\n\tif (colortype == PNG_COLOR_TYPE_PALETTE)\n\t\t*format = PTI_P8;\n\telse if (bitdepth == 8 && channels == 1 && !force_rgb32)\n\t\t*format = PTI_L8;\n\telse if (bitdepth == 8 && channels == 2 && !force_rgb32)\n\t\t*format = (colortype == PNG_COLOR_TYPE_GRAY_ALPHA)?PTI_L8A8:PTI_RG8;\n\telse if (bitdepth == 8 && channels == 3 && !force_rgb32)\n\t\t*format = PTI_RGB8;\n\telse if (bitdepth == 8 && channels == 4)\n\t{\n\t\tif (colortype == PNG_COLOR_TYPE_GRAY)\n\t\t\t*format = PTI_LLLX8;\n\t\telse if (colortype == PNG_COLOR_TYPE_GRAY_ALPHA)\n\t\t\t*format = PTI_LLLA8;\n\t\telse if (colortype == PNG_COLOR_TYPE_RGB)\n\t\t\t*format = PTI_RGBX8;\n\t\telse //if (colortype == PNG_COLOR_TYPE_RGB_ALPHA)\n\t\t\t*format = PTI_RGBA8;\n\t}\n\telse if (bitdepth == 16 && channels == 4 && !force_rgb32)\n\t\t*format = PTI_RGBA16;\n\telse\n\t{\n\t\tCon_Printf (\"Bad PNG color depth and/or bpp (%s)\\n\", fname);\n\t\tqpng_destroy_read_struct(&png, &pnginfo, NULL);\n\t\treturn NULL;\n\t}\n\n#if 0//defined(PNG_READ_APNG_SUPPORTED) && !defined(DYNAMIC_LIBPNG)\n\tif (depth)\n\t\t*depth = 1;\n\tif (depth && png_get_valid(png, pnginfo, PNG_INFO_acTL))\t//looks like an apng\n\t{\n\t\tpng_uint_32 numframes, numplays;\n\t\tpng_uint_32 f;\n\t\tpng_uint_32 framewidth, frameheight, framex, framey;\n\t\tpng_uint_16 framedelay, framedelaydiv;\n\t\tpng_byte framedispose, frameblend;\n\t\tpng_get_acTL(png, pnginfo, &numframes, &numplays);\n\t\tdata = BZF_Malloc(*height * rowbytes * numframes);\n\t\trowpointers = BZF_Malloc(*height * sizeof(*rowpointers));\n\t\tif (!data || !rowpointers || !numframes)\n\t\t\tgoto error;\n\t\tfor (f = 0; f < numframes; f++)\n\t\t{\n\t\t\tpng_read_frame_head(png, pnginfo);\n\t\t\tpng_get_next_frame_fcTL(png, pnginfo, &framewidth, &frameheight, &framex, &framey, &framedelay, &framedelaydiv, &framedispose, &frameblend);\n\n\t\t\tfor (y = 0; y < *height; y++)\n\t\t\t\trowpointers[y] = data + f**height*rowbytes + y * rowbytes;\n\n\t\t\tqpng_read_image(png, rowpointers);\n\t\t\t//FIXME: merge in the preceding frame as appropriate...\n\t\t}\n\n\t\t*depth = numframes;\n\t}\n\telse\n#endif\n\t{\n\t\tdata = BZF_Malloc(*height * rowbytes);\n\t\trowpointers = BZF_Malloc(*height * sizeof(*rowpointers));\n\n\t\tif (!data || !rowpointers)\n\t\t\tgoto error;\n\n\t\tfor (y = 0; y < *height; y++)\n\t\t\trowpointers[y] = data + y * rowbytes;\n\n\t\tqpng_read_image(png, rowpointers);\n\t}\n\tqpng_read_end(png, NULL);\n\n\tqpng_destroy_read_struct(&png, &pnginfo, NULL);\n\tBZ_Free(rowpointers);\n\treturn data;\n}\n\n\n\nint Image_WritePNG (const char *filename, enum fs_relative fsroot, int compression, void **buffers, int numbuffers, qintptr_t bufferstride, int width, int height, enum uploadfmt fmt, qboolean writemetadata)\n{\n\tchar systemname[MAX_OSPATH];\t//FIXME: replace with png_set_write_fn\n\tint i;\n\tFILE *fp;\n\tpng_structp png_ptr;\n\tpng_infop info_ptr;\n\tpng_byte **row_pointers;\n\tstruct pngerr errctx;\n\tint pxsize;\n\tint outwidth = width;\n\n\tqbyte stereochunk = 0;\t//cross-eyed\n\tpng_unknown_chunk unknowns = {\"sTER\", &stereochunk, sizeof(stereochunk), PNG_HAVE_PLTE};\n\n\tint bw,bh,bd,chanbits;\n\tqboolean havepad, bgr;\n\tint colourtype;\n\n\tswitch(fmt)\n\t{\n\tcase PTI_BGR8:\n\t\thavepad = false;\n\t\tcolourtype = PNG_COLOR_TYPE_RGB;\n\t\tchanbits = 8;\n\t\tbgr = true;\n\t\tbreak;\n\tcase PTI_BGRX8:\n\t\thavepad = true;\n\t\tcolourtype = PNG_COLOR_TYPE_RGB;\n\t\tchanbits = 8;\n\t\tbgr = true;\n\t\tbreak;\n\tcase PTI_BGRA8:\n\t\thavepad = false;\n\t\tcolourtype = PNG_COLOR_TYPE_RGB_ALPHA;\n\t\tchanbits = 8;\n\t\tbgr = true;\n\t\tbreak;\n\n\tcase PTI_RGB8:\n\t\thavepad = false;\n\t\tcolourtype = PNG_COLOR_TYPE_RGB;\n\t\tchanbits = 8;\n\t\tbgr = false;\n\t\tbreak;\n\tcase PTI_RGBX8:\n\tcase PTI_LLLX8:\n\t\thavepad = true;\n\t\tcolourtype = PNG_COLOR_TYPE_RGB;\n\t\tchanbits = 8;\n\t\tbgr = false;\n\t\tbreak;\n\n\tcase PTI_RGBA16:\n\t\thavepad = false;\n\t\tcolourtype = PNG_COLOR_TYPE_RGB_ALPHA;\n\t\tchanbits = 16;\n\t\tbgr = false;\n\t\tbreak;\n\n\tcase PTI_P8:\n\t\thavepad = false;\n\t\tcolourtype = PNG_COLOR_TYPE_PALETTE;\n\t\tchanbits = 8;\n\t\tbgr = false;\n\t\tbreak;\n\tcase PTI_L8:\n\t\thavepad = false;\n\t\tcolourtype = PNG_COLOR_TYPE_GRAY;\n\t\tchanbits = 8;\n\t\tbgr = false;\n\t\tbreak;\n\tcase PTI_L8A8:\n\t\thavepad = false;\n\t\tcolourtype = PNG_COLOR_TYPE_GRAY_ALPHA;\n\t\tchanbits = 8;\n\t\tbgr = false;\n\t\tbreak;\n\n\tcase PTI_RGBA8:\n\tcase PTI_LLLA8:\n\t\thavepad = false;\n\t\tcolourtype = PNG_COLOR_TYPE_RGB_ALPHA;\n\t\tchanbits = 8;\n\t\tbgr = false;\n\t\tbreak;\n\tdefault:\n\t\treturn false;\n\t}\n\tImage_BlockSizeForEncoding(fmt, &pxsize, &bw, &bh, &bd);\n\n\tif (!FS_SystemPath(filename, fsroot, systemname, sizeof(systemname)))\n\t\treturn false;\n\n\tif (numbuffers == 2)\n\t{\n\t\toutwidth = width;\n\t\tif (outwidth & 7)\t//standard stereo images must be padded to 8 pixels width padding between\n\t\t\toutwidth += 8-(outwidth & 7);\n\t\toutwidth += width;\n\t}\n\telse\t//arrange them all horizontally\n\t\toutwidth = width * numbuffers;\n\n\tif (!LibPNG_Init())\n\t\treturn false;\n\n\tif (!(fp = fopen (systemname, \"wb\")))\n\t{\n\t\tFS_CreatePath (systemname, FS_SYSTEM);\n\t\tif (!(fp = fopen (systemname, \"wb\")))\n\t\t\treturn false;\n\t}\n\n\terrctx.fname = filename;\n\tif (setjmp(errctx.jbuf))\n\t{\nerr:\n\t\tqpng_destroy_write_struct(&png_ptr, &info_ptr);\n\t\tfclose(fp);\n\t\treturn false;\n\t}\n\n\tif (!(png_ptr = qpng_create_write_struct(PNG_LIBPNG_VER_STRING, &errctx, png_onerror, png_onwarning)))\n\t{\n\t\tfclose(fp);\n\t\treturn false;\n\t}\n\n\tif (!(info_ptr = qpng_create_info_struct(png_ptr)))\n\t{\n\t\tqpng_destroy_write_struct(&png_ptr, (png_infopp) NULL);\n\t\tfclose(fp);\n\t\treturn false;\n\t}\n\n\tqpng_init_io(png_ptr, fp);\n\tcompression = bound(0, compression, 100);\n\n// had to add these when I migrated from libpng 1.4.x to 1.5.x\n#ifndef Z_NO_COMPRESSION\n#define Z_NO_COMPRESSION\t\t\t0\n#endif\n#ifndef Z_BEST_COMPRESSION\n#define Z_BEST_COMPRESSION\t\t\t9\n#endif\n\tqpng_set_compression_level(png_ptr, Z_NO_COMPRESSION + (compression*(Z_BEST_COMPRESSION-Z_NO_COMPRESSION))/100);\n\n\tif (bgr)\n\t\tqpng_set_bgr(png_ptr);\n\tqpng_set_IHDR(png_ptr, info_ptr, outwidth, height, chanbits, colourtype, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);\n\n\tif (colourtype == PNG_COLOR_TYPE_PALETTE)\n\t{\n\t\tpng_color pal[256];\n\t\tfor (i = 0; i < countof(pal); i++) {\n\t\t\tpal[i].red   = host_basepal[i*3+0];\n\t\t\tpal[i].green = host_basepal[i*3+1];\n\t\t\tpal[i].blue  = host_basepal[i*3+2];\n\t\t}\n\t\tqpng_set_PLTE(png_ptr, info_ptr, pal, countof(pal));\n\t\t//png_set_tRNS\n\t}\n\n#if defined(PNG_TEXT_SUPPORTED) && defined(PNG_ITXT_COMPRESSION_NONE)\n\tif (writemetadata)\n\t{\n\t\tchar blob[8192];\n\t\tpng_text pngtext = {PNG_ITXT_COMPRESSION_NONE, \"XML:com.adobe.xmp\"};\n\t\tpngtext.text = blob;\n\t\tGenerateXMPData(blob, sizeof(blob), width, height, writemetadata);\n\t\tpngtext.itxt_length = strlen(pngtext.text);\n\t\tqpng_set_text(png_ptr, info_ptr, &pngtext, 1);\n\t}\n#endif\n\n\tif (numbuffers == 2)\t//flag it as a standard stereographic image\n\t\tqpng_set_unknown_chunks(png_ptr, info_ptr, &unknowns, 1);\n\n\tqpng_write_info(png_ptr, info_ptr);\n\tif (havepad)\n\t\tqpng_set_filler(png_ptr, 0, PNG_FILLER_AFTER);\n\n\tif (numbuffers == 2)\n\t{\t//standard stereographic png image.\n\t\tqbyte *pixels, *left, *right;\n\t\t//we need to pack the data into a single image for libpng to use\n\t\trow_pointers = Z_Malloc (sizeof(png_byte *) * height + outwidth*height*pxsize);\t//must be zeroed, because I'm too lazy to specially deal with padding.\n\t\tif (!row_pointers)\n\t\t\tgoto err;\n\t\tpixels = (qbyte*)row_pointers + height;\n\t\t//png requires right then left, which is a bit weird.\n\t\t//they're meant to be viewable by going cross-eyed (if needed)\n\t\tright = pixels;\n\t\tleft = right + (outwidth-width)*pxsize;\n\n\t\tfor (i = 0; i < height; i++)\n\t\t{\n\t\t\tif ((qbyte*)buffers[1])\n\t\t\t\tmemcpy(right + i*outwidth*pxsize, (qbyte*)buffers[1] + i*bufferstride, pxsize * width);\n\t\t\tif ((qbyte*)buffers[0])\n\t\t\t\tmemcpy(left + i*outwidth*pxsize, (qbyte*)buffers[0] + i*bufferstride, pxsize * width);\n\t\t\trow_pointers[i] = pixels + i * outwidth * pxsize;\n\t\t}\n\t}\n\telse if (numbuffers == 1)\n\t{\n\t\trow_pointers = BZ_Malloc (sizeof(png_byte *) * height);\n\t\tif (!row_pointers)\n\t\t\tgoto err;\n\t\tfor (i = 0; i < height; i++)\n\t\t\trow_pointers[i] = (qbyte*)buffers[0] + i * bufferstride;\n\t}\n\telse\n\t{\t//pack all images horizontally, because preventing people from doing the whole cross-eyed thing is cool, or something.\n\t\tqbyte *pixels;\n\t\tint j;\n\t\t//we need to pack the data into a single image for libpng to use\n\t\trow_pointers = BZ_Malloc (sizeof(png_byte *) * height + outwidth*height*pxsize);\n\t\tif (!row_pointers)\n\t\t\tgoto err;\n\t\tpixels = (qbyte*)row_pointers + height;\n\t\tfor (i = 0; i < height; i++)\n\t\t{\n\t\t\tfor (j = 0; j < numbuffers; j++)\n\t\t\t{\n\t\t\t\tif (buffers[j])\n\t\t\t\t\tmemcpy(pixels+(width*j + i*outwidth)*pxsize, (qbyte*)buffers[j] + i*bufferstride, pxsize * width);\n\t\t\t}\n\t\t\trow_pointers[i] = pixels + i * outwidth * pxsize;\n\t\t}\n\t}\n\tqpng_write_image(png_ptr, row_pointers);\n\tqpng_write_end(png_ptr, info_ptr);\n\tBZ_Free(row_pointers);\n\tqpng_destroy_write_struct(&png_ptr, &info_ptr);\n\tif (0==fclose(fp))\n\t\treturn true;\n\tCon_Printf(\"File error writing %s\\n\", filename);\n\treturn false;\n}\n\n\n#endif\n\n#ifdef AVAIL_JPEGLIB\n#define XMD_H\t//fix for mingw\n\n#if defined(_MSC_VER)\n\t#define JPEG_API VARGS\n\t#include \"jpeglib.h\"\n\t#include \"jerror.h\"\n#else\n\t#include <jpeglib.h>\n\t#include <jerror.h>\n#endif\n\n#ifdef DYNAMIC_LIBJPEG\n\t#define JSTATIC(n)\n\tstatic dllhandle_t *libjpeg_handle;\n\t#define LIBJPEG_LOADED() (libjpeg_handle != NULL)\n#else\n\t#ifdef _MSC_VER\n\t\t#ifdef _WIN64\n\t\t\t#pragma comment(lib, MSVCLIBSPATH \"libjpeg64.lib\")\n\t\t#else\n\t\t\t#pragma comment(lib, MSVCLIBSPATH \"jpeg.lib\")\n\t\t#endif\n\t#endif\n\t#define JSTATIC(n) = &n\n\t#define LIBJPEG_LOADED() (1)\n#endif\n\n#ifndef JPEG_FALSE\n#define JPEG_boolean boolean\n#endif\n\n#define qjpeg_create_compress(cinfo) \\\n    qjpeg_CreateCompress((cinfo), JPEG_LIB_VERSION, \\\n\t\t\t(size_t) sizeof(struct jpeg_compress_struct))\n#define qjpeg_create_decompress(cinfo) \\\n    qjpeg_CreateDecompress((cinfo), JPEG_LIB_VERSION, \\\n\t\t\t  (size_t) sizeof(struct jpeg_decompress_struct))\n\n#ifdef DYNAMIC_LIBJPEG\nstatic boolean (VARGS *qjpeg_resync_to_restart) JPP((j_decompress_ptr cinfo, int desired))\t\t\t\t\t\t\t\t\tJSTATIC(jpeg_resync_to_restart);\nstatic boolean (VARGS *qjpeg_finish_decompress) JPP((j_decompress_ptr cinfo))\t\t\t\t\t\t\t\t\t\t\t\tJSTATIC(jpeg_finish_decompress);\nstatic JDIMENSION (VARGS *qjpeg_read_scanlines) JPP((j_decompress_ptr cinfo, JSAMPARRAY scanlines, JDIMENSION max_lines))\tJSTATIC(jpeg_read_scanlines);\nstatic boolean (VARGS *qjpeg_start_decompress) JPP((j_decompress_ptr cinfo))\t\t\t\t\t\t\t\t\t\t\t\tJSTATIC(jpeg_start_decompress);\nstatic int (VARGS *qjpeg_read_header) JPP((j_decompress_ptr cinfo, boolean require_image))\t\t\t\t\t\t\t\t\tJSTATIC(jpeg_read_header);\nstatic void (VARGS *qjpeg_CreateDecompress) JPP((j_decompress_ptr cinfo, int version, size_t structsize))\t\t\t\t\tJSTATIC(jpeg_CreateDecompress);\nstatic void (VARGS *qjpeg_destroy_decompress) JPP((j_decompress_ptr cinfo))\t\t\t\t\t\t\t\t\t\t\t\t\tJSTATIC(jpeg_destroy_decompress);\n\nstatic struct jpeg_error_mgr * (VARGS *qjpeg_std_error) JPP((struct jpeg_error_mgr * err))\t\t\t\t\t\t\t\t\tJSTATIC(jpeg_std_error);\n\nstatic void (VARGS *qjpeg_finish_compress) JPP((j_compress_ptr cinfo))\t\t\t\t\t\t\t\t\t\t\t\t\t\tJSTATIC(jpeg_finish_compress);\nstatic JDIMENSION (VARGS *qjpeg_write_scanlines) JPP((j_compress_ptr cinfo, JSAMPARRAY scanlines, JDIMENSION num_lines))\tJSTATIC(jpeg_write_scanlines);\nstatic void (VARGS *qjpeg_write_marker) JPP((j_compress_ptr cinfo, int marker, const JOCTET *dataptr, unsigned int datalen))JSTATIC(jpeg_write_marker);\nstatic void (VARGS *qjpeg_start_compress) JPP((j_compress_ptr cinfo, boolean write_all_tables))\t\t\t\t\t\t\t\tJSTATIC(jpeg_start_compress);\nstatic void (VARGS *qjpeg_set_quality) JPP((j_compress_ptr cinfo, int quality, boolean force_baseline))\t\t\t\t\t\tJSTATIC(jpeg_set_quality);\nstatic void (VARGS *qjpeg_set_defaults) JPP((j_compress_ptr cinfo))\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tJSTATIC(jpeg_set_defaults);\nstatic void (VARGS *qjpeg_CreateCompress) JPP((j_compress_ptr cinfo, int version, size_t structsize))\t\t\t\t\t\tJSTATIC(jpeg_CreateCompress);\nstatic void (VARGS *qjpeg_destroy_compress) JPP((j_compress_ptr cinfo))\t\t\t\t\t\t\t\t\t\t\t\t\t\tJSTATIC(jpeg_destroy_compress);\n#endif\n\nqboolean LibJPEG_Init(void)\n{\n\t#ifdef DYNAMIC_LIBJPEG\n\tstatic dllfunction_t jpegfuncs[] =\n\t{\n\t\t{(void **) &qjpeg_resync_to_restart,\t\t\"jpeg_resync_to_restart\"},\n\t\t{(void **) &qjpeg_finish_decompress,\t\t\"jpeg_finish_decompress\"},\n\t\t{(void **) &qjpeg_read_scanlines,\t\t\t\"jpeg_read_scanlines\"},\n\t\t{(void **) &qjpeg_start_decompress,\t\t\t\"jpeg_start_decompress\"},\n\t\t{(void **) &qjpeg_read_header,\t\t\t\t\"jpeg_read_header\"},\n\t\t{(void **) &qjpeg_CreateDecompress,\t\t\t\"jpeg_CreateDecompress\"},\n\t\t{(void **) &qjpeg_destroy_decompress,\t\t\"jpeg_destroy_decompress\"},\n\n\t\t{(void **) &qjpeg_std_error,\t\t\t\t\"jpeg_std_error\"},\n\n\t\t{(void **) &qjpeg_finish_compress,\t\t\t\"jpeg_finish_compress\"},\n\t\t{(void **) &qjpeg_write_scanlines,\t\t\t\"jpeg_write_scanlines\"},\n\t\t{(void **) &qjpeg_write_marker,\t\t\t\t\"jpeg_write_marker\"},\n\t\t{(void **) &qjpeg_start_compress,\t\t\t\"jpeg_start_compress\"},\n\t\t{(void **) &qjpeg_set_quality,\t\t\t\t\"jpeg_set_quality\"},\n\t\t{(void **) &qjpeg_set_defaults,\t\t\t\t\"jpeg_set_defaults\"},\n\t\t{(void **) &qjpeg_CreateCompress,\t\t\t\"jpeg_CreateCompress\"},\n\t\t{(void **) &qjpeg_destroy_compress,\t\t\t\"jpeg_destroy_compress\"},\n\n\t\t{NULL, NULL}\n\t};\n\n\tif (!LIBJPEG_LOADED())\n\t\tlibjpeg_handle = Sys_LoadLibrary(\"libjpeg\", jpegfuncs);\n#ifndef _WIN32\n\tif (!LIBJPEG_LOADED())\n\t\tlibjpeg_handle = Sys_LoadLibrary(\"libjpeg\"ARCH_DL_POSTFIX\".8\", jpegfuncs);\n\tif (!LIBJPEG_LOADED())\n\t\tlibjpeg_handle = Sys_LoadLibrary(\"libjpeg\"ARCH_DL_POSTFIX\".62\", jpegfuncs);\n#endif\n\t#endif\n\n\tif (!LIBJPEG_LOADED())\n\t\tCon_Printf(\"Unable to init libjpeg\\n\");\n\treturn LIBJPEG_LOADED();\n}\n\n/*begin jpeg read*/\n\nstruct my_error_mgr {\n  struct jpeg_error_mgr pub;\t/* \"public\" fields */\n\n  jmp_buf setjmp_buffer;\t/* for return to caller */\n};\n\ntypedef struct my_error_mgr * my_error_ptr;\n\n/*\n * Here's the routine that will replace the standard error_exit method:\n */\n\nMETHODDEF(void)\nmy_error_exit (j_common_ptr cinfo)\n{\n  /* cinfo->err really points to a my_error_mgr struct, so coerce pointer */\n  my_error_ptr myerr = (my_error_ptr) cinfo->err;\n\n  /* Always display the message. */\n  /* We could postpone this until after returning, if we chose. */\n  (*cinfo->err->output_message) (cinfo);\n\n  /* Return control to the setjmp point */\n  longjmp(myerr->setjmp_buffer, 1);\n}\n\n\n/*\n * Sample routine for JPEG decompression.  We assume that the source file name\n * is passed in.  We want to return 1 on success, 0 on error.\n */\n\n\n\n\n/* Expanded data source object for stdio input */\n\ntypedef struct {\n  struct jpeg_source_mgr pub;\t/* public fields */\n\n  qbyte * infile;\t\t/* source stream */\n  int currentpos;\n  int maxlen;\n  JOCTET * buffer;\t\t/* start of buffer */\n  JPEG_boolean start_of_file;\t/* have we gotten any data yet? */\n} my_source_mgr;\n\ntypedef my_source_mgr * my_src_ptr;\n\n#define INPUT_BUF_SIZE  4096\t/* choose an efficiently fread'able size */\n\n\nMETHODDEF(void)\ninit_source (j_decompress_ptr cinfo)\n{\n  my_src_ptr src = (my_src_ptr) cinfo->src;\n\n  src->start_of_file = true;\n}\n\nMETHODDEF(JPEG_boolean)\nfill_input_buffer (j_decompress_ptr cinfo)\n{\n\tmy_source_mgr *src = (my_source_mgr*) cinfo->src;\n\tsize_t nbytes;\n\n\tnbytes = src->maxlen - src->currentpos;\n\tif (nbytes > INPUT_BUF_SIZE)\n\t\tnbytes = INPUT_BUF_SIZE;\n\tmemcpy(src->buffer, &src->infile[src->currentpos], nbytes);\n\tsrc->currentpos+=nbytes;\n\n\tif (nbytes <= 0)\n\t{\n\t\tif (src->start_of_file)\t/* Treat empty input file as fatal error */\n\t\t\tERREXIT(cinfo, JERR_INPUT_EMPTY);\n\t\tWARNMS(cinfo, JWRN_JPEG_EOF);\n\t\t/* Insert a fake EOI marker */\n\t\tsrc->buffer[0] = (JOCTET) 0xFF;\n\t\tsrc->buffer[1] = (JOCTET) JPEG_EOI;\n\t\tnbytes = 2;\n\t}\n\n\tsrc->pub.next_input_byte = src->buffer;\n\tsrc->pub.bytes_in_buffer = nbytes;\n\tsrc->start_of_file = false;\n\n\treturn true;\n}\n\n\nMETHODDEF(void)\nskip_input_data (j_decompress_ptr cinfo, long num_bytes)\n{\n  my_source_mgr *src = (my_source_mgr*) cinfo->src;\n\n  if (num_bytes > 0) {\n    while (num_bytes > (long) src->pub.bytes_in_buffer) {\n      num_bytes -= (long) src->pub.bytes_in_buffer;\n      (void) fill_input_buffer(cinfo);\n    }\n    src->pub.next_input_byte += (size_t) num_bytes;\n    src->pub.bytes_in_buffer -= (size_t) num_bytes;\n  }\n}\n\n\n\nMETHODDEF(void)\nterm_source (j_decompress_ptr cinfo)\n{\n}\n\n\n#undef GLOBAL\n#define GLOBAL(x) x\n\nGLOBAL(void)\nftejpeg_mem_src (j_decompress_ptr cinfo, qbyte * infile, int maxlen)\n{\n  my_source_mgr *src;\n\n  if (cinfo->src == NULL) {\t/* first time for this JPEG object? */\n    cinfo->src = (struct jpeg_source_mgr *)\n      (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT,\n\t\t\t\t  sizeof(my_source_mgr));\n    src = (my_source_mgr*) cinfo->src;\n    src->buffer = (JOCTET *)\n      (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT,\n\t\t\t\t  INPUT_BUF_SIZE * sizeof(JOCTET));\n  }\n\n  src = (my_source_mgr*) cinfo->src;\n  src->pub.init_source = init_source;\n  src->pub.fill_input_buffer = fill_input_buffer;\n  src->pub.skip_input_data = skip_input_data;\n  #ifdef DYNAMIC_LIBJPEG\n  \tsrc->pub.resync_to_restart = qjpeg_resync_to_restart; /* use default method */\n  #else\n  \tsrc->pub.resync_to_restart = jpeg_resync_to_restart; /* use default method */\n  #endif\n  src->pub.term_source = term_source;\n  src->infile = infile;\n  src->pub.bytes_in_buffer = 0; /* forces fill_input_buffer on first read */\n  src->pub.next_input_byte = NULL; /* until buffer loaded */\n\n  src->currentpos = 0;\n  src->maxlen = maxlen;\n}\n\nqbyte *ReadJPEGFile(qbyte *infile, int length, int *width, int *height)\n{\n\tqbyte *mem=NULL, *in, *out;\n\tint i;\n\n\t/* This struct contains the JPEG decompression parameters and pointers to\n\t* working space (which is allocated as needed by the JPEG library).\n\t*/\n\tstruct jpeg_decompress_struct cinfo;\n\t/* We use our private extension JPEG error handler.\n\t* Note that this struct must live as long as the main JPEG parameter\n\t* struct, to avoid dangling-pointer problems.\n\t*/\n\tstruct my_error_mgr jerr;\n\t/* More stuff */\n\tJSAMPARRAY buffer;\t\t/* Output row buffer */\n\tsize_t size_stride;\t\t/* physical row width in output buffer */\n\n\tmemset(&cinfo, 0, sizeof(cinfo));\n\n\tif (!LIBJPEG_LOADED())\n\t{\n\t\tCon_DPrintf(\"libjpeg not available.\\n\");\n\t\treturn NULL;\n\t}\n\n\t/* Step 1: allocate and initialize JPEG decompression object */\n\n\t/* We set up the normal JPEG error routines, then override error_exit. */\n\t#ifdef DYNAMIC_LIBJPEG\n\t\tcinfo.err = qjpeg_std_error(&jerr.pub);\n\t#else\n\t\tcinfo.err = jpeg_std_error(&jerr.pub);\n\t#endif\n\tjerr.pub.error_exit = my_error_exit;\n\t/* Establish the setjmp return context for my_error_exit to use. */\n\tif (setjmp(jerr.setjmp_buffer))\n\t{\n\t\t// If we get here, the JPEG code has signaled an error.\n\t\tCon_DPrintf(\"libjpeg failed to decode a file.\\n\");\nbadjpeg:\n\t\t#ifdef DYNAMIC_LIBJPEG\n\t\t\tqjpeg_destroy_decompress(&cinfo);\n\t\t#else\n\t\t\tjpeg_destroy_decompress(&cinfo);\n\t\t#endif\n\n\t\tif (mem)\n\t\t\tBZ_Free(mem);\n\t\treturn NULL;\n\t}\n\t#ifdef DYNAMIC_LIBJPEG\n\t\tqjpeg_create_decompress(&cinfo);\n\t#else\n\t\tjpeg_create_decompress(&cinfo);\n\t#endif\n\n\tftejpeg_mem_src(&cinfo, infile, length);\n\n\t#ifdef DYNAMIC_LIBJPEG\n\t\t(void) qjpeg_read_header(&cinfo, true);\n\t#else\n\t\t(void) jpeg_read_header(&cinfo, true);\n\t#endif\n\n\t#ifdef DYNAMIC_LIBJPEG\n\t\t(void) qjpeg_start_decompress(&cinfo);\n\t#else\n\t\t(void) jpeg_start_decompress(&cinfo);\n\t#endif\n\n\n\tif (cinfo.output_components == 0)\n\t{\n\t\tCon_DPrintf(\"No JPEG Components, not a JPEG.\\n\");\n\t\tgoto badjpeg;\n\t}\n\tif (cinfo.output_components!=3 && cinfo.output_components != 1)\n\t{\n\t\tCon_DPrintf(\"Bad number of components in JPEG: '%d', should be '3'.\\n\",cinfo.output_components);\n\t\tgoto badjpeg;\n\t}\n\tif (cinfo.output_height > ~0u / (cinfo.output_width*4))\n\t{\t//even 64bit processes can suffer when oom.\n\t\tCon_Printf(\"Refusing to load excessively large jpeg of %u * %u.\\n\",cinfo.output_width, cinfo.output_width);\n\t\tgoto badjpeg;\n\t}\n\tsize_stride = cinfo.output_width * cinfo.output_components;\n\t/* Make a one-row-high sample array that will go away when done with image */\n\tbuffer = (*cinfo.mem->alloc_sarray) ((j_common_ptr) &cinfo, JPOOL_IMAGE, size_stride, 1);\n\n\tout=mem=BZF_Malloc(cinfo.output_height*(size_t)cinfo.output_width*4u);\n\tif (!mem)\n\t\tCon_Printf(\"Malloc failure on %u * %u jpeg image.\\n\", cinfo.output_height, cinfo.output_width);\n\telse\n\t{\n//\t\tmemset(out, 0, cinfo.output_height*(size_t)cinfo.output_width*4u);\n\t\tif (cinfo.output_components == 1)\n\t\t{\n\t\t\twhile (cinfo.output_scanline < cinfo.output_height)\n\t\t\t{\n\t\t\t\t#ifdef DYNAMIC_LIBJPEG\n\t\t\t\t\t(void) qjpeg_read_scanlines(&cinfo, buffer, 1);\n\t\t\t\t#else\n\t\t\t\t\t(void) jpeg_read_scanlines(&cinfo, buffer, 1);\n\t\t\t\t#endif\n\n\t\t\t\tin = buffer[0];\n\t\t\t\tfor (i = 0; i < cinfo.output_width; i++)\n\t\t\t\t{//luminance to rgba\n\t\t\t\t\t*out++ = *in;\n\t\t\t\t\t*out++ = *in;\n\t\t\t\t\t*out++ = *in;\n\t\t\t\t\t*out++ = 255;\n\t\t\t\t\tin++;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\twhile (cinfo.output_scanline < cinfo.output_height)\n\t\t\t{\n\t\t\t\t#ifdef DYNAMIC_LIBJPEG\n\t\t\t\t\t(void) qjpeg_read_scanlines(&cinfo, buffer, 1);\n\t\t\t\t#else\n\t\t\t\t\t(void) jpeg_read_scanlines(&cinfo, buffer, 1);\n\t\t\t\t#endif\n\n\t\t\t\tin = buffer[0];\n\t\t\t\tfor (i = 0; i < cinfo.output_width; i++)\n\t\t\t\t{//rgb to rgba\n\t\t\t\t\t*out++ = *in++;\n\t\t\t\t\t*out++ = *in++;\n\t\t\t\t\t*out++ = *in++;\n\t\t\t\t\t*out++ = 255;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t#ifdef DYNAMIC_LIBJPEG\n\t\t(void) qjpeg_finish_decompress(&cinfo);\n\t#else\n\t\t(void) jpeg_finish_decompress(&cinfo);\n\t#endif\n\n\t#ifdef DYNAMIC_LIBJPEG\n\t\tqjpeg_destroy_decompress(&cinfo);\n\t#else\n\t\tjpeg_destroy_decompress(&cinfo);\n\t#endif\n\n\t*width = cinfo.output_width;\n\t*height = cinfo.output_height;\n\n\treturn mem;\n\n}\n/*end read*/\n/*begin write*/\n\n\n#ifndef DYNAMIC_LIBJPEG\n#define qjpeg_std_error\t\t\tjpeg_std_error\n#define qjpeg_destroy_compress\tjpeg_destroy_compress\n#define qjpeg_CreateCompress\tjpeg_CreateCompress\n#define qjpeg_set_defaults\t\tjpeg_set_defaults\n#define qjpeg_set_quality\t\tjpeg_set_quality\n#define qjpeg_write_marker\t\tjpeg_write_marker\n#define qjpeg_start_compress\tjpeg_start_compress\n#define qjpeg_write_scanlines\tjpeg_write_scanlines\n#define qjpeg_finish_compress\tjpeg_finish_compress\n#define qjpeg_destroy_compress\tjpeg_destroy_compress\n#endif\n#define OUTPUT_BUF_SIZE 4096\ntypedef struct  {\n\tstruct jpeg_error_mgr pub;\n\n\tjmp_buf setjmp_buffer;\n} jpeg_error_mgr_wrapper;\n\ntypedef struct {\n\tstruct jpeg_destination_mgr pub;\n\n\tvfsfile_t *vfs;\n\n\n\tJOCTET  buffer[OUTPUT_BUF_SIZE];\t\t/* start of buffer */\n} my_destination_mgr;\n\nMETHODDEF(void) init_destination (j_compress_ptr cinfo)\n{\n\tmy_destination_mgr *dest = (my_destination_mgr*) cinfo->dest;\n\n\tdest->pub.next_output_byte = dest->buffer;\n\tdest->pub.free_in_buffer = OUTPUT_BUF_SIZE;\n}\nMETHODDEF(JPEG_boolean) empty_output_buffer (j_compress_ptr cinfo)\n{\n\tmy_destination_mgr *dest = (my_destination_mgr*) cinfo->dest;\n\n\tVFS_WRITE(dest->vfs, dest->buffer, OUTPUT_BUF_SIZE);\n\tdest->pub.next_output_byte = dest->buffer;\n\tdest->pub.free_in_buffer = OUTPUT_BUF_SIZE;\n\n\treturn true;\n}\nMETHODDEF(void) term_destination (j_compress_ptr cinfo)\n{\n\tmy_destination_mgr *dest = (my_destination_mgr*) cinfo->dest;\n\n\tVFS_WRITE(dest->vfs, dest->buffer, OUTPUT_BUF_SIZE - dest->pub.free_in_buffer);\n\tdest->pub.next_output_byte = dest->buffer;\n\tdest->pub.free_in_buffer = OUTPUT_BUF_SIZE;\n}\n\nstatic void ftejpeg_mem_dest (j_compress_ptr cinfo, vfsfile_t *vfs)\n{\n\tmy_destination_mgr *dest;\n\n\tif (cinfo->dest == NULL)\n\t{\t/* first time for this JPEG object? */\n\t\tcinfo->dest = (struct jpeg_destination_mgr *)\n\t\t\t\t\t\t(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT,\n\t\t\t\t\t\tsizeof(my_destination_mgr));\n\t\tdest = (my_destination_mgr*) cinfo->dest;\n\t\t//    dest->buffer = (JOCTET *)\n\t\t//      (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT,\n\t\t//\t\t\t\t  OUTPUT_BUF_SIZE * sizeof(JOCTET));\n\t}\n\n\tdest = (my_destination_mgr*) cinfo->dest;\n\tdest->pub.init_destination = init_destination;\n\tdest->pub.empty_output_buffer = empty_output_buffer;\n\tdest->pub.term_destination = term_destination;\n\tdest->pub.free_in_buffer = 0; /* forces fill_input_buffer on first read */\n\tdest->pub.next_output_byte = NULL; /* until buffer loaded */\n\tdest->vfs = vfs;\n}\n\n\n\nMETHODDEF(void) jpeg_error_exit (j_common_ptr cinfo)\n{\n\tlongjmp(((jpeg_error_mgr_wrapper *) cinfo->err)->setjmp_buffer, 1);\n}\nqboolean screenshotJPEG(char *filename, enum fs_relative fsroot, int compression, qbyte *screendata, qintptr_t stride, int screenwidth, int screenheight, enum uploadfmt fmt, unsigned int writemeta)\n{\n\tvfsfile_t\t*outfile;\n\tjpeg_error_mgr_wrapper jerr;\n\tstruct jpeg_compress_struct cinfo;\n\tJSAMPROW row_pointer[1];\n\tqboolean ret = false;\n\n\tqbyte *tmpdata = NULL;\n\n\tint ic;\n\tqboolean byteswap;\n\n\tswitch(fmt)\n\t{\n\tcase PTI_RGB8:\t//yay! nothing to do.\n\t\tbyteswap = false;\n\t\tic = 3;\n\t\tbreak;\n\tcase PTI_BGR8:\n\t\tbyteswap = true;\n\t\tic = 3;\n\t\tbreak;\n\tcase PTI_BGRA8:\n\tcase PTI_BGRX8:\n\t\tbyteswap = true;\n\t\tic = 4;\n\t\tbreak;\n\tcase PTI_RGBA8:\n\tcase PTI_RGBX8:\n\tcase PTI_LLLA8:\n\tcase PTI_LLLX8:\n\t\tbyteswap = false;\n\t\tic = 4;\n\t\tbreak;\n\tcase PTI_L8:\n//\tcase PTI_L8A8:\n\t\tbyteswap = false;\n\t\tic = 1;\n\t\tbreak;\n\tdefault:\t//nope, not happening.\n\t\tCon_Printf(\"screenshotJPEG: image format not supported\\n\");\n\t\treturn false;\n\t}\n\n\tif (byteswap || ic == 4)\n\t{\t//jpeg doesn't support many input layouts...\n\t\tqbyte *in=screendata;\n\t\tqbyte *out=tmpdata=BZ_Malloc(screenwidth*screenheight*3);\n\t\tsize_t y, x;\n\t\tint rc = byteswap?2:0;\n\t\tint bc = byteswap?0:2;\n\t\tfor (y = 0; y < screenheight; y++)\n\t\t{\n\t\t\tfor (x = 0; x < screenwidth; x++, in+=ic, out+=3)\n\t\t\t{\n\t\t\t\tout[0] = in[rc];\n\t\t\t\tout[1] = in[1];\n\t\t\t\tout[2] = in[bc];\n\t\t\t}\n\t\t\tin-=screenwidth*ic;\n\t\t\tin+=stride;\n\t\t}\n\t\tic = 3;\n\t\tstride = screenwidth*3;\n\t\tscreendata = tmpdata;\n\t}\n\n\tif (LIBJPEG_LOADED())\n\t{\n\t\tFS_CreatePath (filename, fsroot);\n\t\tif ((outfile = FS_OpenVFS(filename, \"wb\", fsroot)))\n\t\t{\n\t\t\tcinfo.err = qjpeg_std_error(&jerr.pub);\n\t\t\tjerr.pub.error_exit = jpeg_error_exit;\n\t\t\tif (setjmp(jerr.setjmp_buffer))\n\t\t\t{\n\t\t\t\tqjpeg_destroy_compress(&cinfo);\n\t\t\t\tVFS_CLOSE(outfile);\n\t\t\t\tFS_Remove(filename, FS_GAME);\n\t\t\t\tCon_Printf(\"Failed to create jpeg\\n\");\n\t\t\t\tBZ_Free(tmpdata);\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tqjpeg_create_compress(&cinfo);\n\n\t\t\t\tftejpeg_mem_dest(&cinfo, outfile);\n\t\t\t\tcinfo.image_width = screenwidth;\n\t\t\t\tcinfo.image_height = screenheight;\n\t\t\t\tcinfo.input_components = ic;\n\t\t\t\tcinfo.in_color_space = (ic<3)?JCS_GRAYSCALE:JCS_RGB;\n\t\t\t\tqjpeg_set_defaults(&cinfo);\n\t\t\t\tqjpeg_set_quality (&cinfo, bound(0, compression, 100), true);\n\t\t\t\tqjpeg_start_compress(&cinfo, true);\n\n\t\t\t\tif (writemeta)\n\t\t\t\t{\n\t\t\t\t\tstatic const char header[] = \"http://ns.adobe.com/xap/1.0/\";\n\t\t\t\t\tchar blob[8192];\n\t\t\t\t\tmemcpy(blob, header, sizeof(header)); //MUST include the null terminator.\n\t\t\t\t\tGenerateXMPData(blob+sizeof(header), sizeof(blob)-sizeof(header), screenwidth, screenheight, writemeta);\n\t\t\t\t\tqjpeg_write_marker(&cinfo, JPEG_APP0+1, blob, sizeof(header)+strlen(blob+sizeof(header)));\n\t\t\t\t}\n\n\t\t\t\twhile (cinfo.next_scanline < cinfo.image_height)\n\t\t\t\t{\n\t\t\t\t\t*row_pointer = screendata+(cinfo.next_scanline * stride);\n\t\t\t\t\tqjpeg_write_scanlines(&cinfo, row_pointer, 1);\n\t\t\t\t}\n\t\t\t\tqjpeg_finish_compress(&cinfo);\n\t\t\t\tqjpeg_destroy_compress(&cinfo);\n\t\t\t\tret = true;\n\t\t\t}\n\n\t\t\tVFS_CLOSE(outfile);\n\t\t}\n\t}\n\tBZ_Free(tmpdata);\n\treturn ret;\n}\n#endif\n\n#ifdef IMAGEFMT_PCX\n/*\n==============\nWritePCXfile\n==============\n*/\nqboolean WritePCXfile (const char *filename, enum fs_relative fsroot, qbyte *data, int width, int height,\n\tint rowbytes, qbyte *palette, qboolean upload) //data is 8bit.\n{\n\tint\t\ti, j, length;\n\tpcx_t\t*pcx;\n\tqbyte\t\t*pack;\n\tqboolean ret;\n\n\tpcx = BZ_Malloc(width*height*2+1000);\n\tif (pcx == NULL)\n\t{\n\t\tCon_Printf(\"WritePCXfile: not enough memory\\n\");\n\t\treturn false;\n\t}\n\n\tpcx->manufacturer = 0x0a;\t// PCX id\n\tpcx->version = 5;\t\t\t// 256 color\n \tpcx->encoding = 1;\t\t// uncompressed\n\tpcx->bits_per_pixel = 8;\t\t// 256 color\n\tpcx->xmin = 0;\n\tpcx->ymin = 0;\n\tpcx->xmax = LittleShort((short)(width-1));\n\tpcx->ymax = LittleShort((short)(height-1));\n\tpcx->hres = LittleShort((short)width);\n\tpcx->vres = LittleShort((short)height);\n\tQ_memset (pcx->palette,0,sizeof(pcx->palette));\n\tpcx->color_planes = 1;\t\t// chunky image\n\tpcx->bytes_per_line = LittleShort((short)width);\n\tpcx->palette_type = LittleShort(2);\t\t// not a grey scale\n\tQ_memset (pcx->filler,0,sizeof(pcx->filler));\n\n// pack the image\n\tpack = (qbyte *)(pcx+1);\n\tfor (i=0 ; i<height ; i++)\n\t{\n\t\tfor (j=0 ; j<width ; j++)\n\t\t{\n\t\t\tif ( (*data & 0xc0) != 0xc0)\n\t\t\t\t*pack++ = *data++;\n\t\t\telse\n\t\t\t{\n\t\t\t\t*pack++ = 0xc1;\n\t\t\t\t*pack++ = *data++;\n\t\t\t}\n\t\t}\n\n\t\tdata += rowbytes - width;\n\t}\n\n// write the palette\n\t*pack++ = 0x0c;\t// palette ID qbyte\n\tfor (i=0 ; i<768 ; i++)\n\t\t*pack++ = *palette++;\n\n// write output file\n\tlength = pack - (qbyte *)pcx;\n\n#ifdef HAVE_CLIENT\n\tif (upload)\n\t{\n\t\tCL_StartUpload((void *)pcx, length);\n\t\tret = true;\n\t}\n\telse\n#endif\n\t\tret = COM_WriteFile (filename, fsroot, pcx, length);\n\tBZ_Free(pcx);\n\n\treturn ret;\n}\n\n/*\n============\nLoadPCX\n============\n*/\nqbyte *ReadPCXFile(qbyte *buf, int length, int *width, int *height)\n{\n\tpcx_t\t*pcx;\n//\tpcx_t pcxbuf;\n\tqbyte\t*palette;\n\tqbyte\t*pix;\n\tint\t\tx, y;\n\tint\t\tdataByte, runLength;\n\tint\t\tcount;\n\tqbyte *data;\n\n\tqbyte\t*pcx_rgb;\n\n\tunsigned short xmin, ymin, swidth, sheight;\n\n//\n// parse the PCX file\n//\n\n\tif (length < sizeof(*pcx))\n\t\treturn NULL;\n\n\tpcx = (pcx_t *)buf;\n\n\txmin = LittleShort(pcx->xmin);\n\tymin = LittleShort(pcx->ymin);\n\tswidth = LittleShort(pcx->xmax)-xmin+1;\n\tsheight = LittleShort(pcx->ymax)-ymin+1;\n\n\tif (pcx->manufacturer != 0x0a\n\t\t|| pcx->version != 5\n\t\t|| pcx->encoding != 1\n\t\t|| pcx->bits_per_pixel != 8\n\t\t|| pcx->color_planes != 1\n\t\t|| swidth >= 1024\n\t\t|| sheight >= 1024)\n\t{\n\t\treturn NULL;\n\t}\n\n\t*width = swidth;\n\t*height = sheight;\n\n#ifdef HAVE_CLIENT\n\tif (r_dodgypcxfiles.value)\n\t\tpalette = host_basepal;\n\telse\n#endif\n\t\tpalette = buf + length-768;\n\n\tdata = (char *)(pcx+1);\n\n\tcount = (swidth) * (sheight);\n\tpcx_rgb = BZ_Malloc( count * 4);\n\n\tfor (y=0 ; y<sheight ; y++)\n\t{\n\t\tpix = pcx_rgb + 4*y*(swidth);\n\t\tfor (x=0 ; x<swidth ; )\n\t\t{\n\t\t\tdataByte = *data;\n\t\t\tdata++;\n\n\t\t\tif((dataByte & 0xC0) == 0xC0)\n\t\t\t{\n\t\t\t\trunLength = dataByte & 0x3F;\n\t\t\t\tif (x+runLength>swidth)\n\t\t\t\t{\n\t\t\t\t\tCon_Printf(\"corrupt pcx\\n\");\n\t\t\t\t\tBZ_Free(pcx_rgb);\n\t\t\t\t\treturn NULL;\n\t\t\t\t}\n\t\t\t\tdataByte = *data;\n\t\t\t\tdata++;\n\t\t\t}\n\t\t\telse\n\t\t\t\trunLength = 1;\n\n\t\t\twhile(runLength-- > 0)\n\t\t\t{\n\t\t\t\tpix[0] = palette[dataByte*3];\n\t\t\t\tpix[1] = palette[dataByte*3+1];\n\t\t\t\tpix[2] = palette[dataByte*3+2];\n\t\t\t\tpix[3] = 255;\n\t\t\t\tif (dataByte == 255)\n\t\t\t\t{\n\t\t\t\t\tpix[0] = 0;\t//linear filtering can mean transparent pixel colours are visible. black is a more neutral colour.\n\t\t\t\t\tpix[1] = 0;\n\t\t\t\t\tpix[2] = 0;\n\t\t\t\t\tpix[3] = 0;\n\t\t\t\t}\n\t\t\t\tpix += 4;\n\t\t\t\tx++;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn pcx_rgb;\n}\n\nqbyte *ReadPCXData(qbyte *buf, int length, int width, int height, qbyte *result)\n{\n\tpcx_t\t*pcx;\n//\tpcx_t pcxbuf;\n//\tqbyte\t*palette;\n\tqbyte\t*pix;\n\tint\t\tx, y;\n\tint\t\tdataByte, runLength;\n//\tint\t\tcount;\n\tqbyte *data;\n\n\tunsigned short xmin, ymin, swidth, sheight;\n\n//\n// parse the PCX file\n//\n\n\tpcx = (pcx_t *)buf;\n\n\txmin = LittleShort(pcx->xmin);\n\tymin = LittleShort(pcx->ymin);\n\tswidth = LittleShort(pcx->xmax)-xmin+1;\n\tsheight = LittleShort(pcx->ymax)-ymin+1;\n\n\tif (pcx->manufacturer != 0x0a\n\t\t|| pcx->version != 5\n\t\t|| pcx->encoding != 1\n\t\t|| pcx->bits_per_pixel != 8)\n\t{\n\t\treturn NULL;\n\t}\n\n\tif (width != swidth ||\n\t\theight > sheight)\n\t{\n\t\tCon_Printf(\"unsupported pcx size\\n\");\n\t\treturn NULL;\t//we can't feed the requester with enough info\n\t}\n\n\tdata = (char *)(pcx+1);\n\n\tfor (y=0 ; y<height ; y++)\n\t{\n\t\tpix = result + y*swidth;\n\t\tfor (x=0 ; x<swidth ; )\n\t\t{\n\t\t\tdataByte = *data;\n\t\t\tdata++;\n\n\t\t\tif((dataByte & 0xC0) == 0xC0)\n\t\t\t{\n\t\t\t\trunLength = dataByte & 0x3F;\n\t\t\t\tif (x+runLength>swidth)\n\t\t\t\t{\n\t\t\t\t\tCon_Printf(\"corrupt pcx\\n\");\n\t\t\t\t\treturn NULL;\n\t\t\t\t}\n\t\t\t\tdataByte = *data;\n\t\t\t\tdata++;\n\t\t\t}\n\t\t\telse\n\t\t\t\trunLength = 1;\n\n\t\t\twhile(runLength-- > 0)\n\t\t\t{\n\t\t\t\t*pix++ = dataByte;\n\t\t\t\tx++;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn result;\n}\n\nqbyte *ReadPCXPalette(qbyte *buf, int len, qbyte *out)\n{\n\tpcx_t\t*pcx;\n\n\n//\n// parse the PCX file\n//\n\n\tpcx = (pcx_t *)buf;\n\n\tif (pcx->manufacturer != 0x0a\n\t\t|| pcx->version != 5\n\t\t|| pcx->encoding != 1\n\t\t|| pcx->bits_per_pixel != 8\n\t\t|| LittleShort(pcx->xmax) >= 1024\n\t\t|| LittleShort(pcx->ymax) >= 1024)\n\t{\n\t\treturn NULL;\n\t}\n\n\tmemcpy(out, (qbyte *)pcx + len - 768, 768);\n\n\treturn out;\n}\n#endif\n\n#ifdef IMAGEFMT_BMP\ntypedef struct bmpheader_s\n{\n\tunsigned int\tSizeofBITMAPINFOHEADER;\n\tsigned int\t\tWidth;\n\tsigned int\t\tHeight;\n\tunsigned short\tPlanes;\n\tunsigned short\tBitCount;\n\tunsigned int\tCompression;\n\tunsigned int\tImageSize;\n\tsigned int\t\tTargetDeviceXRes;\n\tsigned int\t\tTargetDeviceYRes;\n\tunsigned int\tNumofColorIndices;\n\tunsigned int\tNumofImportantColorIndices;\n} bmpheader_t;\ntypedef struct bmpheaderv4_s\n{\n\tunsigned int\tRedMask;\n\tunsigned int\tGreenMask;\n\tunsigned int\tBlueMask;\n\tunsigned int\tAlphaMask;\n\tqbyte\t\t\tColourSpace[4];\t//\"Win \" or \"sRGB\"\n\tqbyte\t\t\tColourSpaceCrap[12*3];\n\tunsigned int\tGamma[3];\n} bmpheaderv4_t;\n\nstatic qbyte *ReadRawBMPFile(qbyte *buf, int length, int *width, int *height, size_t OffsetofBMPBits)\n{\n\tunsigned int i;\n\tbmpheader_t h;\n\tqbyte *data;\n\n\tmemcpy(&h, buf, sizeof(h));\t\n\th.SizeofBITMAPINFOHEADER = LittleLong(h.SizeofBITMAPINFOHEADER);\n\th.Width = LittleLong(h.Width);\n\th.Height = LittleLong(h.Height);\n\th.Planes = LittleShort(h.Planes);\n\th.BitCount = LittleShort(h.BitCount);\n\th.Compression = LittleLong(h.Compression);\n\th.ImageSize = LittleLong(h.ImageSize);\n\th.TargetDeviceXRes = LittleLong(h.TargetDeviceXRes);\n\th.TargetDeviceYRes = LittleLong(h.TargetDeviceYRes);\n\th.NumofColorIndices = LittleLong(h.NumofColorIndices);\n\th.NumofImportantColorIndices = LittleLong(h.NumofImportantColorIndices);\n\n\tif (h.Compression)\t//RLE? BITFIELDS (gah)?\n\t\treturn NULL;\n\n\tif (!OffsetofBMPBits)\n\t\th.Height /= 2;\t//icons are weird.\n\n\t*width = h.Width;\n\t*height = h.Height;\n\n\tif (h.BitCount == 4)\t//4 bit\n\t{\n\t\tint x, y;\n\t\tunsigned int *data32;\n\t\tunsigned int\tpal[16];\n\t\tif (!h.NumofColorIndices)\n\t\t\th.NumofColorIndices = (int)pow(2, h.BitCount);\n\t\tif (h.NumofColorIndices>16)\n\t\t\treturn NULL;\n\t\tif (h.Width&1)\n\t\t\treturn NULL;\n\n\t\tdata = buf;\n\t\tdata += sizeof(h);\n\n\t\tfor (i = 0; i < h.NumofColorIndices; i++)\n\t\t{\n\t\t\tpal[i] = data[i*4+2] + (data[i*4+1]<<8) + (data[i*4+0]<<16) + (255u/*data[i*4+3]*/<<24);\n\t\t}\n\n\t\tif (OffsetofBMPBits)\n\t\t\tbuf += OffsetofBMPBits;\n\t\telse\n\t\t\tbuf = data+h.NumofColorIndices*4;\n\t\tdata32 = BZ_Malloc(h.Width * h.Height*4);\n\t\tfor (y = 0; y < h.Height; y++)\n\t\t{\n\t\t\ti = (h.Height-1-y) * (h.Width);\n\t\t\tfor (x = 0; x < h.Width/2; x++)\n\t\t\t{\n\t\t\t\tdata32[i++] = pal[buf[x]>>4];\n\t\t\t\tdata32[i++] = pal[buf[x]&15];\n\t\t\t}\n\t\t\tbuf += (h.Width+1)>>1;\n\t\t}\n\n\t\tif (!OffsetofBMPBits)\n\t\t{\n\t\t\tfor (y = 0; y < h.Height; y++)\n\t\t\t{\n\t\t\t\ti = (h.Height-1-y) * (h.Width);\n\t\t\t\tfor (x = 0; x < h.Width; x++)\n\t\t\t\t{\n\t\t\t\t\tif (buf[x>>3]&(1<<(7-(x&7))))\n\t\t\t\t\t\tdata32[i] &= 0x00ffffff;\n\t\t\t\t\ti++;\n\t\t\t\t}\n\t\t\t\tbuf += (h.Width+7)>>3;\n\t\t\t}\n\t\t}\n\n\t\treturn (qbyte *)data32;\n\t}\n\telse if (h.BitCount == 8)\t//8 bit\n\t{\n\t\tint x, y;\n\t\tunsigned int *data32;\n\t\tunsigned int\tpal[256];\n\t\tif (!h.NumofColorIndices)\n\t\t\th.NumofColorIndices = (int)pow(2, h.BitCount);\n\t\tif (h.NumofColorIndices>256)\n\t\t\treturn NULL;\n\n\t\tdata = buf;\n\t\tdata += sizeof(h);\n\n\t\tfor (i = 0; i < h.NumofColorIndices; i++)\n\t\t{\n\t\t\tpal[i] = data[i*4+2] + (data[i*4+1]<<8) + (data[i*4+0]<<16) + (255u/*data[i*4+3]*/<<24);\n\t\t}\n\n\t\tif (OffsetofBMPBits)\n\t\t\tbuf += OffsetofBMPBits;\n\t\telse\n\t\t\tbuf += h.SizeofBITMAPINFOHEADER + h.NumofColorIndices*4;\n\t\tdata32 = BZ_Malloc(h.Width * h.Height*4);\n\t\tfor (y = 0; y < h.Height; y++)\n\t\t{\n\t\t\ti = (h.Height-1-y) * (h.Width);\n\t\t\tfor (x = 0; x < h.Width; x++)\n\t\t\t{\n\t\t\t\tdata32[i] = pal[buf[x]];\n\t\t\t\ti++;\n\t\t\t}\n\t\t\t//BMP rows are 32-bit aligned.\n\t\t\tbuf += (h.Width+3)&~3;\n\t\t}\n\n\t\tif (!OffsetofBMPBits)\n\t\t{\n\t\t\tfor (y = 0; y < h.Height; y++)\n\t\t\t{\n\t\t\t\ti = (h.Height-1-y) * (h.Width);\n\t\t\t\tfor (x = 0; x < h.Width; x++)\n\t\t\t\t{\n\t\t\t\t\tif (buf[x>>3]&(1<<(7-(x&7))))\n\t\t\t\t\t\tdata32[i] &= 0x00ffffff;\n\t\t\t\t\ti++;\n\t\t\t\t}\n\t\t\t\tbuf += (h.Width+7)>>3;\n\t\t\t}\n\t\t}\n\n\t\treturn (qbyte *)data32;\n\t}\n\telse if (h.BitCount == 24)\t//24 bit... no 16?\n\t{\n\t\tint x, y;\n\t\tif (OffsetofBMPBits)\n\t\t\tbuf += OffsetofBMPBits;\n\t\telse\n\t\t\tbuf += h.SizeofBITMAPINFOHEADER;\n\t\tdata = BZ_Malloc(h.Width * h.Height*4);\n\t\tfor (y = 0; y < h.Height; y++)\n\t\t{\n\t\t\ti = (h.Height-1-y) * (h.Width);\n\t\t\tfor (x = 0; x < h.Width; x++)\n\t\t\t{\n\t\t\t\tdata[i*4+0] = buf[x*3+2];\n\t\t\t\tdata[i*4+1] = buf[x*3+1];\n\t\t\t\tdata[i*4+2] = buf[x*3+0];\n\t\t\t\tdata[i*4+3] = 255;\n\t\t\t\ti++;\n\t\t\t}\n\t\t\tbuf += h.Width*3;\n\t\t}\n\n\t\treturn data;\n\t}\n\telse if (h.BitCount == 32)\n\t{\n\t\tint x, y;\n\t\tif (OffsetofBMPBits)\n\t\t\tbuf += OffsetofBMPBits;\n\t\telse\n\t\t\tbuf += h.SizeofBITMAPINFOHEADER;\n\t\tdata = BZ_Malloc(h.Width * h.Height*4);\n\t\tfor (y = 0; y < h.Height; y++)\n\t\t{\n\t\t\ti = (h.Height-1-y) * (h.Width);\n\t\t\tfor (x = 0; x < h.Width; x++)\n\t\t\t{\n\t\t\t\tdata[i*4+0] = buf[x*4+2];\n\t\t\t\tdata[i*4+1] = buf[x*4+1];\n\t\t\t\tdata[i*4+2] = buf[x*4+0];\n\t\t\t\tdata[i*4+3] = buf[x*4+3];\n\t\t\t\ti++;\n\t\t\t}\n\t\t\tbuf += h.Width*4;\n\t\t}\n\n\t\treturn data;\n\t}\n\telse\n\t\treturn NULL;\n\n\treturn NULL;\n}\n\nstatic qbyte *ReadBMPFile(qbyte *buf, int length, int *width, int *height)\n{\n\tunsigned short Type\t\t\t\t= buf[0] | (buf[1]<<8);\n\tunsigned short Size\t\t\t\t= buf[2] | (buf[3]<<8) | (buf[4]<<16) | (buf[5]<<24);\n//\tunsigned short Reserved1\t\t= buf[6] | (buf[7]<<8);\n//\tunsigned short Reserved2\t\t= buf[8] | (buf[9]<<8);\n\tunsigned short OffsetofBMPBits\t= buf[10] | (buf[11]<<8) | (buf[12]<<16) | (buf[13]<<24);\n\tif (Type != ('B'|('M'<<8)))\n\t\treturn NULL;\n\tif (Size > length)\n\t\treturn NULL;\t//it got truncated at some point\n\treturn ReadRawBMPFile(buf + 14, length-14, width, height, OffsetofBMPBits - 14);\n}\n\nqboolean WriteBMPFile(char *filename, enum fs_relative fsroot, qbyte *in, qintptr_t instride, int width, int height, uploadfmt_t fmt)\n{\n\tint y;\n\tbmpheader_t h;\n\tbmpheaderv4_t h4;\n\tqbyte *data;\n\tqbyte *out;\n\tint outstride;\n\tint bits = 32;\n\tint extraheadersize = sizeof(h4);\n\tsize_t fsize;\n\tqboolean success;\n\n\tmemset(&h4, 0, sizeof(h4));\n\th4.ColourSpace[0] = 'W';\n\th4.ColourSpace[1] = 'i';\n\th4.ColourSpace[2] = 'n';\n\th4.ColourSpace[3] = ' ';\n\tswitch(fmt)\n\t{\n\tcase TF_RGBA32:\n\t\th4.RedMask\t\t= 0x000000ff;\n\t\th4.GreenMask\t= 0x0000ff00;\n\t\th4.BlueMask\t\t= 0x00ff0000;\n\t\th4.AlphaMask\t= 0xff000000;\n\t\tbreak;\n\tcase TF_BGRA32:\n\t\th4.RedMask\t\t= 0x00ff0000;\n\t\th4.GreenMask\t= 0x0000ff00;\n\t\th4.BlueMask\t\t= 0x000000ff;\n\t\th4.AlphaMask\t= 0xff000000;\n\t\tbreak;\n\tcase TF_RGBX32:\n\t\th4.RedMask\t\t= 0x000000ff;\n\t\th4.GreenMask\t= 0x0000ff00;\n\t\th4.BlueMask\t\t= 0x00ff0000;\n\t\th4.AlphaMask\t= 0x00000000;\n\t\tbreak;\n\tcase TF_BGRX32:\n\t\th4.RedMask\t\t= 0x00ff0000;\n\t\th4.GreenMask\t= 0x0000ff00;\n\t\th4.BlueMask\t\t= 0x000000ff;\n\t\th4.AlphaMask\t= 0x00000000;\n\t\tbreak;\n\tcase TF_RGB24:\n\t\th4.RedMask\t\t= 0x000000ff;\n\t\th4.GreenMask\t= 0x0000ff00;\n\t\th4.BlueMask\t\t= 0x00ff0000;\n\t\th4.AlphaMask\t= 0x00000000;\n\t\tbits = 3;\n\t\tbreak;\n\tcase TF_BGR24:\n\t\th4.RedMask\t\t= 0x00ff0000;\n\t\th4.GreenMask\t= 0x0000ff00;\n\t\th4.BlueMask\t\t= 0x000000ff;\n\t\th4.AlphaMask\t= 0x00000000;\n\t\tbits = 3;\n\t\textraheadersize = 0;\n\t\tbreak;\n\n\tdefault:\n\t\treturn false;\n\t}\n\n\n\toutstride = width * (bits/8);\n\toutstride = (outstride+3)&~3;\t//bmp pads rows to a multiple of 4 bytes.\n\n//\th.Size = 14+sizeof(h)+extraheadersize + outstride*height;\n//\th.Reserved1 = 0;\n//\th.Reserved2 = 0;\n//\th.OffsetofBMPBits = 2+sizeof(h)+extraheadersize;\t//yes, this is misaligned.\n\th.SizeofBITMAPINFOHEADER = (sizeof(h)-12)+extraheadersize;\n\th.Width = width;\n\th.Height = height;\n\th.Planes = 1;\n\th.BitCount = bits;\n\th.Compression = extraheadersize?3/*BI_BITFIELDS*/:0/*BI_RGB aka BGR...*/;\n\th.ImageSize = outstride*height;\n\th.TargetDeviceXRes = 2835;//72DPI\n\th.TargetDeviceYRes = 2835;\n\th.NumofColorIndices = 0;\n\th.NumofImportantColorIndices = 0;\n\n\t//bmp is bottom-up so flip it now.\n\tin += instride*(height-1);\n\tinstride *= -1;\n\n\tfsize = 14+sizeof(h)+extraheadersize + outstride*height;\t//size\n\tout = data = BZ_Malloc(fsize);\n\t//Type\n\t*out++ = 'B';\n\t*out++ = 'M';\n\t//Size\n\t*out++ = fsize&0xff;\n\t*out++ = (fsize>>8)&0xff;\n\t*out++ = (fsize>>16)&0xff;\n\t*out++ = (fsize>>24)&0xff;\n\t//Reserved1\n\ty = 0;\n\t*out++ = y&0xff;\n\t*out++ = (y>>8)&0xff;\n\t//Reserved1\n\ty = 0;\n\t*out++ = y&0xff;\n\t*out++ = (y>>8)&0xff;\n\t//OffsetofBMPBits\n\ty = 2+sizeof(h)+extraheadersize;\t//yes, this is misaligned.\n\t*out++ = y&0xff;\n\t*out++ = (y>>8)&0xff;\n\t*out++ = (y>>16)&0xff;\n\t*out++ = (y>>24)&0xff;\n\t//bmpheader\n\tmemcpy(out, &h, sizeof(h));\n\tout += sizeof(h);\n\t//v4 header\n\tmemcpy(out, &h4, extraheadersize);\n\tout += extraheadersize;\n\n\t//data\n\tfor (y = 0; y < height; y++)\n\t{\n\t\tmemcpy(out, in, width * (bits/8));\n\t\tmemset(out+width*(bits/8), 0, outstride-width*(bits/8));\n\t\tout += outstride;\n\t\tin += instride;\n\t}\n\n\tsuccess = COM_WriteFile(filename, fsroot, data, fsize);\n\tBZ_Free(data);\n\n\treturn success;\n}\n\nstatic qbyte *ReadICOFile(const char *fname, qbyte *buf, int length, int *width, int *height, uploadfmt_t *fmt)\n{\n\tqbyte *ret;\n\tsize_t imgcount = buf[4] | (buf[5]<<8);\n\tstruct\n\t{\n\t\tqbyte bWidth;\n\t\tqbyte bHeight;\n\t\tqbyte bColorCount;\n\t\tqbyte bReserved;\n\t\tunsigned short wPlanes;\n\t\tunsigned short wBitCount;\n\t\tunsigned short dwSize_low;\n\t\tunsigned short dwSize_high;\n\t\tunsigned short dwOffset_low;\n\t\tunsigned short dwOffset_high;\n\t} *img = (void*)(buf+6), *bestimg = NULL;\n\tsize_t bestpixels = 0;\n\tsize_t bestdepth = 0;\n\n\t//always favour the png first\n\tfor (imgcount = buf[4] | (buf[5]<<8), img = (void*)(buf+6); imgcount-->0; img++)\n\t{\n\t\tsize_t cc = img->wBitCount;\n\t\tsize_t px = (img->bWidth?img->bWidth:256) * (img->bHeight?img->bHeight:256);\n\t\tif (!cc)\t//if that was omitted, try and guess it based on raw image size. this is an over estimate.\n\t\t\tcc = 8 * (img->dwSize_low | (img->dwSize_high<<16)) / px;\n\n\t\tif (!bestimg || cc > bestdepth || (cc == bestdepth && px > bestpixels))\n\t\t{\n\t\t\tbestimg = img;\n\t\t\tbestdepth = cc;\n\t\t\tbestpixels = px;\n\t\t}\n\t}\n\n\tif (bestimg)\n\t{\n\t\tqbyte *indata = buf + (bestimg->dwOffset_low | (bestimg->dwOffset_high<<16));\n\t\tsize_t insize = (bestimg->dwSize_low | (bestimg->dwSize_high<<16));\n#ifdef AVAIL_PNGLIB\n\t\tif (insize > 4 && (indata[0] == 137 && indata[1] == 'P' && indata[2] == 'N' && indata[3] == 'G') && (ret = ReadPNGFile(fname, indata, insize, width, height, fmt, true)))\n\t\t{\n\t\t\tTRACE((\"dbg: Read32BitImageFile: icon png\\n\"));\n\t\t\treturn ret;\n\t\t}\n\t\telse\n#endif\n\t\tif ((ret = ReadRawBMPFile(indata, insize, width, height, 0)))\n\t\t{\n\t\t\tif (fmt)\n\t\t\t\t*fmt = PTI_RGBA8;\n\t\t\tTRACE((\"dbg: Read32BitImageFile: icon bmp\\n\"));\n\t\t\treturn ret;\n\t\t}\n\t}\n\n\treturn NULL;\n}\n#endif\n\n#ifdef IMAGEFMT_PBM\nstatic int PBM_ParseNum(qbyte **buf, qbyte *end)\n{\n\tqbyte token[256];\n\tsize_t l;\n\twhile (*buf < end)\n\t{\n\t\tif (**buf <= ' ')\n\t\t\t(*buf)++;\n\t\telse if (**buf == '#')\n\t\t{\n\t\t\twhile (*buf < end && **buf != '\\n' && **buf != '\\r')\n\t\t\t\t(*buf)++;\n\t\t}\n\t\telse\n\t\t\tbreak;\n\t}\n\n\tfor (l = 0; *buf < end && **buf > ' ';)\n\t\tif (l < countof(token))\n\t\t\ttoken[l++] = *(*buf)++;\n\ttoken[l] = 0;\n\treturn strtol(token, NULL, 0);\n}\nstatic qbyte *ReadPBMFile(qbyte *buf, size_t len, const char *fname, int *width, int *height, uploadfmt_t *format)\n{\t//this isn't expected to be fast.\n\tqbyte *end = buf+len;\n\tint maxval = *width = *height = 0;\n\tqbyte *r, *bo;\n\tunsigned short *so;\n\tsize_t l, x, y;\n\tfloat m, *fo, *fi;\n\tint c = buf[1];\n\tbuf+=2;\n\tswitch(c)\n\t{\n\tcase '7': //arbitrary\n\t\t//WIDTH HEIGHT DEPTH MAXVAL TUPLTYPE ENDHDR\n\t\treturn NULL;\n\tcase '6':\t//raw ppm\n\tcase '3':\t//plain ppm\n\t\t*width = PBM_ParseNum(&buf, end);\n\t\t*height = PBM_ParseNum(&buf, end);\n\t\tmaxval = PBM_ParseNum(&buf, end);\n\n\t\tif (maxval > 255)\n\t\t{\n\t\t\tr = BZ_Malloc(*width**height*8);\n\t\t\tfor(y = 0; y < *height; y++)\n\t\t\tfor(x = 0, so=(unsigned short*)r+(*height-y-1)*4**width; x < *height; x++)\n\t\t\t{\n\t\t\t\t*so++ = (65535u*PBM_ParseNum(&buf, end))/maxval;\n\t\t\t\t*so++ = (65535u*PBM_ParseNum(&buf, end))/maxval;\n\t\t\t\t*so++ = (65535u*PBM_ParseNum(&buf, end))/maxval;\n\t\t\t\t*so++ = 65535u;\n\t\t\t}\n\t\t\t*format = PTI_RGBA16;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tr = BZ_Malloc(*width**height*4);\n\t\t\tfor(y = 0; y < *height; y++)\n\t\t\tfor(x = 0, bo=(qbyte*)r+(*height-y-1)*4**width; x < *height; x++)\n\t\t\t{\n\t\t\t\t*bo++ = (255u*PBM_ParseNum(&buf, end))/maxval;\n\t\t\t\t*bo++ = (255u*PBM_ParseNum(&buf, end))/maxval;\n\t\t\t\t*bo++ = (255u*PBM_ParseNum(&buf, end))/maxval;\n\t\t\t\t*bo++ = 255u;\n\t\t\t}\n\t\t\t*format = PTI_RGBA8;\n\t\t}\n\t\treturn r;\n\tcase '5':\t//raw pgm\n\tcase '2':\t//plain pgm\n\tcase '4':\t//raw pbm\n\tcase '1':\t//plain pbm\n\t\t*width = PBM_ParseNum(&buf, end);\n\t\t*height = PBM_ParseNum(&buf, end);\n\n\t\tif (c == '4' || c == '1')\n\t\t\tmaxval = 1;\n\t\telse\n\t\t\tmaxval = PBM_ParseNum(&buf, end);\n\n\t\tl = (size_t)*width*(size_t)*height;\n\t\tif (maxval > 255)\n\t\t{\n\t\t\tr = BZ_Malloc(*width**height*sizeof(*so));\n\t\t\tfor(y = 0; y < *height; y++)\n\t\t\tfor(x = 0, so=(unsigned short*)r+(*height-y-1)**width; x < *height; x++)\n\t\t\t\t*so++ = (65535u*PBM_ParseNum(&buf, end))/maxval;\n\t\t\t*format = PTI_R16;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tr = BZ_Malloc(*width**height*sizeof(*bo));\n\t\t\tfor(y = 0; y < *height; y++)\n\t\t\tfor(x = 0, bo=(qbyte*)r+(*height-y-1)**width; x < *height; x++)\n\t\t\t\t*bo++ = (255u*PBM_ParseNum(&buf, end))/maxval;\n\t\t\t*format = PTI_R8;\n\t\t}\n\t\treturn r;\n\n\tcase 'F':\t//rgb pfm\n\tcase 'f':\t//grey pfm\n\t\t*width = PBM_ParseNum(&buf, end);\n\t\t*height = PBM_ParseNum(&buf, end);\n\t\tm = PBM_ParseNum(&buf, end);\n\n\t\tif (*buf == '\\n')\n\t\t\tbuf++;\n\t\tfi = (float*)buf;\n\n\t\tl = (size_t)*width*(size_t)*height;\n\t\tif ((qbyte*)(fi+l*((c=='F')?3:1)) != end)\n\t\t\treturn NULL;\n\t\tr = BZ_Malloc(l*sizeof(float) * ((c=='F')?4:1));\n\t\tif (c == 'F')\n\t\t{\n\t\t\tif (m < 0)\n\t\t\t{\n\t\t\t\tr = BZ_Malloc(*width**height*4*sizeof(float));\n\t\t\t\tfor(y = 0; y < *height; y++)\n\t\t\t\tfor(x = 0, fo=(float*)r+(*height-y-1)*4**width; x < *height; x++)\n\t\t\t\t{\n\t\t\t\t\t*fo++ = LittleFloat(*fi++);\n\t\t\t\t\t*fo++ = LittleFloat(*fi++);\n\t\t\t\t\t*fo++ = LittleFloat(*fi++);\n\t\t\t\t\t*fo++ = 1;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tr = BZ_Malloc(*width**height*4*sizeof(float));\n\t\t\t\tfor(y = 0; y < *height; y++)\n\t\t\t\tfor(x = 0, fo=(float*)r+(*height-y-1)*4**width; x < *height; x++)\n\t\t\t\t{\n\t\t\t\t\t*fo++ = BigFloat(*fi++);\n\t\t\t\t\t*fo++ = BigFloat(*fi++);\n\t\t\t\t\t*fo++ = BigFloat(*fi++);\n\t\t\t\t\t*fo++ = 1;\n\t\t\t\t}\n\t\t\t}\n\t\t\t*format = PTI_RGBA32F;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tr = BZ_Malloc(l*sizeof(float));\n\t\t\tif (m < 0)\n\t\t\t{\n\t\t\t\tr = BZ_Malloc(*width**height*sizeof(float));\n\t\t\t\tfor(y = 0; y < *height; y++)\n\t\t\t\tfor(x = 0, fo=(float*)r+(*height-y-1)**width; x < *height; x++)\n\t\t\t\t\t*fo++ = LittleFloat(*fi++);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tr = BZ_Malloc(*width**height*sizeof(float));\n\t\t\t\tfor(y = 0; y < *height; y++)\n\t\t\t\tfor(x = 0, fo=(float*)r+(*height-y-1)**width; x < *height; x++)\n\t\t\t\t\t*fo++ = BigFloat(*fi++);\n\t\t\t}\n\t\t\t*format = PTI_R32F;\n\t\t}\n\t\treturn r; //erk?\n\t}\n\treturn NULL;\n}\n#endif\n#ifdef IMAGEFMT_HDR\t\t\t//baselayer only.\n//Radiance files are some weird BGRE8 hdr format that somehow managed to get reasonably well supported\nstatic void *ReadRadianceFile(qbyte *buf, size_t len, const char *fname, int *width, int *height, uploadfmt_t *format)\n{\t//this isn't expected to be fast.\n\tqbyte *end = buf+len;\n\tsize_t l, x, y, w, h, e;\n\tfloat *r, *o, m;\n\tqbyte rgbe[4];\n\n\tchar fmt[128];\n\tchar line[256];\n\tbyte_vec4_t *row = NULL;\n\tw = h = 0;\n\t*fmt = 0;\n\twhile (buf < end)\n\t{\n\t\tl = 0;\n\t\twhile(buf < end && *buf != '\\n')\n\t\t{\n\t\t\tif (*buf == '\\r' && buf[1] == '\\n')\n\t\t\t\tcontinue;\n\t\t\tif (l < countof(line)-1)\n\t\t\t\tline[l++] = *buf++;\n\t\t}\n\t\tline[l] = 0;\n\t\tbuf++;\n\t\tif (!strncmp(line, \"FORMAT=\", 7))\n\t\t\tQ_strncpyz(fmt, line+7, sizeof(fmt));\n\t\tif (!l)\n\t\t\tbreak;\n\t}\n\tif (strncmp(buf, \"-Y \", 3))\n\t{\n\t\tCon_Printf(\"%s uses unsupported orientation\\n\", fname);\n\t\treturn NULL;\n\t}\n\th = strtol(buf+3, (char**)&buf, 0);\n\tif (strncmp(buf, \" +X \", 4))\n\t{\n\t\tCon_Printf(\"%s uses unsupported orientation\\n\", fname);\n\t\treturn NULL;\n\t}\n\tw = strtol(buf+4, (char**)&buf, 0);\n\tif (*buf == '\\r')\n\t\tbuf++;\n\tif (*buf++ != '\\n')\n\t\treturn NULL;\n\n\tif (strcmp(fmt, \"32-bit_rle_rgbe\"))\n\t{\n\t\tCon_Printf(\"%s uses unsupported pixel format (%s)\\n\", fname, fmt);\n\t\treturn NULL;\n\t}\n\n\tr = o = BZ_Malloc(sizeof(float)*4*w*h);\n\tif (!r)\n\t\treturn NULL;\n\tfor (y=0; y < h && buf+4<end; y++)\n\t{\n\t\tif (buf[0] == 2 && buf[1] == 2 && buf[2] < 127)\n\t\t{ //'new' rle logic\n\t\t\tint c, v;\n\t\t\tbuf+=4;\n\t\t\tif (!row)\n\t\t\t\trow = BZ_Malloc(w*sizeof(*row));\n\t\t\t//encoded separately\n\t\t\tfor (c = 0; c < 4; c++)\n\t\t\t{\n\t\t\t\tfor (x=0; x < w; )\n\t\t\t\t{\n\t\t\t\t\tif (buf+1 >= end)\n\t\t\t\t\t\tgoto fail;\n\t\t\t\t\tif (*buf > 128)\n\t\t\t\t\t{\n\t\t\t\t\t\te = x + (*buf++ - 128);\n\t\t\t\t\t\tv = *buf++;\n\t\t\t\t\t\tif (e == x || e > w)\n\t\t\t\t\t\t\tgoto fail;\n\t\t\t\t\t\twhile (x < e)\n\t\t\t\t\t\t\trow[x++][c] = v;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\te = x + *buf++;\n\t\t\t\t\t\tif (e == x || e > w || buf+(e-x) >= end)\n\t\t\t\t\t\t\tgoto fail;\n\t\t\t\t\t\twhile (x < e)\n\t\t\t\t\t\t\trow[x++][c] = *buf++;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t//decompressed the entire line, can make sense of it now.\n\t\t\tfor (x=0; x < w; x++)\n\t\t\t{\n\t\t\t\tm = ldexp(1,row[x][3]-136);\n\t\t\t\t*o++ = m * row[x][0];\n\t\t\t\t*o++ = m * row[x][1];\n\t\t\t\t*o++ = m * row[x][2];\n\t\t\t\t*o++ = 1;\n\t\t\t}\n\t\t}\n\t\telse if (buf[0] == 1 && buf[1] == 1 && buf[2] == 1)\n\t\t{ //old rle logic\n\t\t\tCon_Printf(\"%s uses unsupported (old) RLE compression\\n\", fname);\n\t\t\tgoto fail;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tfor (x=0; x < w && buf+4<end ; x++)\n\t\t\t{\n\t\t\t\trgbe[0] = *buf++;\n\t\t\t\trgbe[1] = *buf++;\n\t\t\t\trgbe[2] = *buf++;\n\t\t\t\trgbe[3] = *buf++;\n\n\t\t\t\tm = ldexp(1,rgbe[3]-136);\n\t\t\t\t*o++ = m * rgbe[0];\n\t\t\t\t*o++ = m * rgbe[1];\n\t\t\t\t*o++ = m * rgbe[2];\n\t\t\t\t*o++ = 1;\n\t\t\t}\n\t\t}\n\t}\n\tif (row)\n\t\tBZ_Free(row);\n\t*width = w;\n\t*height = h;\n\t//FIXME: should probably convert to e5bgr9 or something.\n\t*format = PTI_RGBA32F;\n\treturn r;\nfail:\n\tif (row)\n\t\tBZ_Free(row);\n\tBZ_Free(r);\n\treturn NULL;\n}\n#endif\n\n#ifdef IMAGEFMT_PSD\t\t\t//baselayer only.\n// https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/\nstruct psdctx_s\n{\n\tqbyte *buf;\n\tqbyte *end;\n};\nstatic qbyte PSD_Byte(struct psdctx_s *ctx)\n{\n\tif (ctx->buf == ctx->end)\n\t\treturn 0;\n\treturn *ctx->buf++;\n}\nstatic unsigned short PSD_UShort(struct psdctx_s *ctx)\n{\n\treturn (PSD_Byte(ctx)<<8)|PSD_Byte(ctx);\n}\nstatic unsigned int PSD_UInt(struct psdctx_s *ctx)\n{\n\treturn (PSD_Byte(ctx)<<24)|(PSD_Byte(ctx)<<16)|(PSD_Byte(ctx)<<8)|PSD_Byte(ctx);\n}\nstatic void *PSD_Block(struct psdctx_s *ctx, size_t sz)\n{\n\tvoid *r;\n\tif (ctx->buf+sz <= ctx->end)\n\t{\n\t\tr = ctx->buf;\n\t\tctx->buf += sz;\n\t}\n\telse\n\t\tr = NULL;\n\treturn r;\n}\nstatic void *ReadPSDFile(qbyte *buf, size_t len, const char *fname, int *outwidth, int *outheight, uploadfmt_t *outformat)\n{\n\tunsigned short ver, chans, depth, clrmode, cmp;\n\tunsigned int width, height, clrsize, ressize, lyrsize;\n\tstruct psdctx_s ctx;\n\tsize_t l, c, y;\n\tctx.buf = buf;\n\tctx.end = buf+len;\n\n\t/*magic\t=*/ PSD_UInt(&ctx);\n\tver = PSD_UShort(&ctx);\n\tif (ver != 1)\n\t{\n\t\tCon_Printf(\"%s unsupported .psd version\\n\", fname);\n\t\treturn NULL;\n\t}\n\t/*reserved*/PSD_Block(&ctx, 6);\n\tchans   = PSD_UShort(&ctx);\n\twidth   = PSD_UInt(&ctx);\n\theight  = PSD_UInt(&ctx);\n\tdepth   = PSD_UShort(&ctx);\n\tclrmode = PSD_UShort(&ctx);\n\tclrsize = PSD_UInt(&ctx);\n\t/*palette =*/ PSD_Block(&ctx, clrsize);\n\tressize = PSD_UInt(&ctx);\n\t/*resdata =*/ PSD_Block(&ctx, ressize);\n\tlyrsize = PSD_UInt(&ctx);\n\t/*lyrdata =*/ PSD_Block(&ctx, lyrsize);\n\tcmp\t\t= PSD_UShort(&ctx);\n\n\tif (width <= 0 || height <= 0 || width > 16384 || height > 16384)\n\t{\n\t\tCon_Printf(\"%s too large dimensions (%u * %u)\\n\", fname, width, height);\n\t\treturn NULL;\n\t}\n\tif (chans <= 0)\n\t{\n\t\tCon_Printf(\"%s has no colour channels\\n\", fname);\n\t\treturn NULL;\n\t}\n\tif (clrmode == 3 && (depth == 8 || depth == 16)) //RGB\n\t\t;\n\telse\n\t{\n\t\tCon_Printf(\"%s not 8 or 16bpp RGB .psd image\\n\", fname);\n\t\treturn NULL;\n\t}\n\n\t//the data is planer\n\tif (cmp == 0 || (cmp == 1 && depth==8))\n\t{\n\t\tif (cmp)\n\t\t\tPSD_Block(&ctx, 2*chans*height); //2 byte run size per plane*scanline\n\t\tif (depth == 16)\n\t\t{\n\t\t\tunsigned short *r, *o;\n\t\t\tr = o = BZ_Malloc(sizeof(*o)*4*width*height);\n\t\t\tfor(c = 0; c < 4; c++)\n\t\t\t{\n\t\t\t\to = r+c;\n\t\t\t\tif (c < chans)\n\t\t\t\t{\n\t\t\t\t\tfor(l = 0; l < width*height; l++, o+=4)\n\t\t\t\t\t\t*o = PSD_UShort(&ctx);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\t//pad colour to 0, alpha 1\n\t\t\t\t\tfor(l = 0; l < width*height; l++, o+=4)\n\t\t\t\t\t\t*o = (c==3)?0xffff:0;\n\t\t\t\t}\n\t\t\t}\n\t\t\t*outwidth = width;\n\t\t\t*outheight = height;\n\t\t\t*outformat = PTI_RGBA16;\n\t\t\treturn r;\n\t\t}\n\t\telse if (depth == 8)\n\t\t{\n\t\t\tqbyte *r, *o;\n\t\t\tr = o = BZ_Malloc(sizeof(*o)*4*width*height);\n\t\t\tfor(c = 0; c < 4; c++)\n\t\t\t{\n\t\t\t\to = r+c;\n\t\t\t\tif (c < chans)\n\t\t\t\t{\n\t\t\t\t\tif (cmp == 1)\n\t\t\t\t\t{\n\t\t\t\t\t\tfor(y = 0; y < height; y++)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tfor(l = 0; l < width; )\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tqbyte run = PSD_Byte(&ctx), val;\n\t\t\t\t\t\t\t\tif (run < 128)\n\t\t\t\t\t\t\t\t{\t//copy\n\t\t\t\t\t\t\t\t\trun++;\n\t\t\t\t\t\t\t\t\tfor (; l < width && run --> 0; l++, o+=4)\n\t\t\t\t\t\t\t\t\t\t*o = PSD_Byte(&ctx);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\trun = 1-(char)run;\n\t\t\t\t\t\t\t\t\tval = PSD_Byte(&ctx);\n\t\t\t\t\t\t\t\t\tfor (; l < width && run --> 0; l++, o+=4)\n\t\t\t\t\t\t\t\t\t\t*o = val;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\telse for(l = 0; l < width*height; l++, o+=4)\n\t\t\t\t\t\t*o = PSD_Byte(&ctx);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\t//pad colour to 0, alpha 1\n\t\t\t\t\tfor(l = 0; l < width*height; l++, o+=4)\n\t\t\t\t\t\t*o = (c==3)?0xff:0;\n\t\t\t\t}\n\t\t\t}\n\t\t\t*outwidth = width;\n\t\t\t*outheight = height;\n\t\t\tif (chans >= 4)\n\t\t\t\t*outformat = PTI_RGBA8;\n\t\t\telse\n\t\t\t\t*outformat = PTI_RGBX8;\n\t\t\treturn r;\n\t\t}\n\t}\n\telse if (cmp == 1)\n\t{\n\t}\n\tCon_Printf(\"%s unsupported compression type (%u)\\n\", fname, cmp);\n\treturn NULL;\n}\n#endif\n\n#ifdef IMAGEFMT_XCF\nstruct xcf_s\n{\n\tconst qbyte *filestart;\n\tqofs_t filesize;\n\tqofs_t offset;\n\n\tunsigned int version;\n\tqbyte compression;\n\tsize_t width;\n\tsize_t height;\n\tint basetype, precision;\n\n\tenum uploadfmt outformat;\n\tsize_t numlayers;\n\tsize_t *layeroffsets;\n\tqbyte *flat;\n};\nstatic size_t XCF_ReadData(struct xcf_s *f, qbyte *out, size_t len)\n{\n\tsize_t avail;\n\tif (f->offset >= f->filesize)\n\t\tavail = 0;\n\telse\n\t\tavail = f->filesize - f->offset;\n\tif (len > avail)\n\t\tmemset(out+avail, 0, len-avail);\n\telse\n\t\tavail = len;\n\tmemcpy(out, f->filestart+f->offset, avail);\n\tf->offset += avail;\n\treturn avail;\n}\nstatic unsigned char XCF_ReadByte(struct xcf_s *f)\n{\n\tif (f->offset >= f->filesize)\n\t\treturn 0;\n\treturn f->filestart[f->offset++];\n}\nstatic unsigned int XCF_Read32(struct xcf_s *f)\n{\t//XCF is natively big-endian.\n\tqbyte w[4];\n\tXCF_ReadData(f, w, sizeof(w));\n\treturn (w[0]<<24)|(w[1]<<16)|(w[2]<<8)|(w[3]<<0);\n}\nstatic float XCF_ReadFloat(struct xcf_s *f)\n{\t//XCF is natively big-endian.\n\tunion\n\t{\n\t\tunsigned int u;\n\t\tfloat f;\n\t} u;\n\tqbyte w[4];\n\tXCF_ReadData(f, w, sizeof(w));\n\tu.u = (w[0]<<24)|(w[1]<<16)|(w[2]<<8)|(w[3]<<0);\n\treturn u.f;\n}\nstatic qofs_t XCF_ReadOffset(struct xcf_s *f)\n{\t//XCF is natively big-endian.\n\tqbyte w[8];\n\tsize_t h, l;\n\tif (f->version >= 11)\n\t{\n\t\tXCF_ReadData(f, w, sizeof(w));\n\t\th = (w[0]<<24)|(w[1]<<16)|(w[2]<<8)|(w[3]<<0);\n\t\tl = (w[4]<<24)|(w[5]<<16)|(w[6]<<8)|(w[7]<<0);\n\t\treturn qofs_Make(l, h);\n\t}\n\treturn XCF_Read32(f);\n}\nstatic size_t XCF_ReadString(struct xcf_s *f, char *out, size_t outsize)\n{\n\tsize_t len = XCF_Read32(f);\n\tif (!len)\n\t\t*out = 0;\n\telse\n\t{\n\t\tif (outsize > len)\n\t\t\toutsize = len;\n\t\toutsize--;\n\t\tXCF_ReadData(f, out, outsize);\n\t\tout[outsize]=0;\n\t\tf->offset += len-outsize;\n\t}\n\treturn len;\n}\nstatic void XCF_ReadHeaderProperties(struct xcf_s *f)\n{\t//XCF is natively big-endian.\n\tunsigned int proptype, propsize;\n\n\tfor(;;)\n\t{\n\t\tproptype = XCF_Read32(f);\n\t\tpropsize = XCF_Read32(f);\n\t\tif (!proptype)\n\t\t\tbreak;\n\t\telse if (proptype == 17)\t//compression\n\t\t\tXCF_ReadData(f, &f->compression,1);\n\t\telse if (proptype == 19)\t//prop_resolution\n\t\t\tf->offset += propsize;\n\t\telse if (proptype == 20)\t//tatoo\n\t\t\tf->offset += propsize;\n\t\telse if (proptype == 21)\t//parasites\n\t\t\tf->offset += propsize;\n\t\telse if (proptype == 22)\t//prop_unit\n\t\t\tf->offset += propsize;\n\t\telse\n\t\t{\n\t\t\tCon_Printf(\"Unknown image property %i\\n\", proptype);\n\t\t\tf->offset += propsize;\n\t\t}\n\t}\n}\nstatic qboolean XCF_ReadTile(struct xcf_s *f, qbyte *out, int pxsize, int bytestride, int bytew, int h)\n{\n\tif (f->compression==0)\n\t{\n\t\twhile(h --> 0)\n\t\t{\n\t\t\tXCF_ReadData(f, out, bytew);\n\t\t\tout += bytestride;\n\t\t}\n\t\treturn true;\n\t}\n\telse if (f->compression == 1)\n\t{\n\t\tint runsize;\n\t\tint runtype;\n\t\tint runval;\n\t\tint rowbyte;\n\t\tint c = 0, y;\n\t\tqbyte *outdata = out;\n\t\t//data is ordered by channels\nnextchan:\n\t\tif (c == pxsize)\n\t\t\treturn true;\n\t\tout = outdata+c;\n\t\trowbyte = 0;\n\t\tc++;\n\t\tfor(y=0;;)\n\t\t{\n\t\t\trunsize = XCF_ReadByte(f);\n\t\t\tif (runsize <= 127)\n\t\t\t{\t//compressed run\n\t\t\t\tif (runsize == 127)\n\t\t\t\t{\n\t\t\t\t\trunsize = XCF_ReadByte(f)<<8;\n\t\t\t\t\trunsize |= XCF_ReadByte(f);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\trunsize++;\n\t\t\t\trunval = XCF_ReadByte(f);\n\t\t\t\truntype = 0;\n\t\t\t}\n\t\t\telse\n\t\t\t{\t//unpacked run\n\t\t\t\tif (runsize == 128)\n\t\t\t\t{\n\t\t\t\t\trunsize = XCF_ReadByte(f)<<8;\n\t\t\t\t\trunsize |= XCF_ReadByte(f);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\trunsize = 256-runsize;\n\t\t\t\trunval = -1;\n\t\t\t\truntype = 1;\n\t\t\t}\n\t\t\tif (!runsize)\n\t\t\t\treturn false;\t//buggy input\n\n\t\t\twhile(runsize --> 0)\n\t\t\t{\n\t\t\t\tif (runtype)\n\t\t\t\t\trunval = XCF_ReadByte(f);\n\t\t\t\tout[rowbyte] = runval;\n\t\t\t\trowbyte+=pxsize;\n\n\t\t\t\tif (rowbyte == bytew)\n\t\t\t\t{\t//reached the end of the row, move on to the next\n\t\t\t\t\tif (++y == h)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (runsize)\n\t\t\t\t\t\t\treturn false;\t//runsize was too long...\n\t\t\t\t\t\tgoto nextchan; //reached the end of the tile.\n\t\t\t\t\t}\n\t\t\t\t\trowbyte = 0;\n\t\t\t\t\tout += bytestride;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\telse\n\t\treturn false;\n}\nstruct xcf_heirachy_s\n{\n\tquint32_t width;\n\tquint32_t height;\n\tquint32_t bpp;\n\tqbyte\t*data;\n};\nstatic struct xcf_heirachy_s XCF_ReadHeirachy(struct xcf_s *f)\n{\n\tstruct xcf_heirachy_s ctx;\n\tquint32_t x, y, lw, lh;\n\tqofs_t ofs, tofs;\n\tif (!f->offset)\n\t{\n\t\tmemset(&ctx, 0, sizeof(ctx));\n\t\treturn ctx;\n\t}\n\tctx.width = XCF_Read32(f);\n\tctx.height = XCF_Read32(f);\n\tctx.bpp = XCF_Read32(f);\n\tctx.data = NULL;\n\n\tofs = XCF_ReadOffset(f);\n\twhile (XCF_ReadOffset(f))\n\t\t;\t//we don't care about these dummy offsets. we could use them for mipmaps but we don't expect them to be valid or bug-free\n\n\tf->offset = ofs;\t//jump to level 0\n\tlw = XCF_Read32(f);\n\tlh = XCF_Read32(f);\n\tif (lw == ctx.width && lh == ctx.height)\n\t{\n\t\tctx.data = BZ_Malloc(ctx.width*(size_t)ctx.bpp*ctx.height);\n\n\t\tfor (y = 0; y < ctx.height; y+=lh)\n\t\t{\n\t\t\tlh = min(64, ctx.height-y);\n\t\t\tfor (x = 0; x < ctx.width; x+=lw)\n\t\t\t{\n\t\t\t\tlw = min(64, ctx.width-x);\n\t\t\t\ttofs = XCF_ReadOffset(f);\n\n\t\t\t\tofs = f->offset;\n\t\t\t\tf->offset = tofs;\n\t\t\t\tif (!XCF_ReadTile(f, ctx.data + x*ctx.bpp + y*ctx.width*ctx.bpp, ctx.bpp, ctx.width*ctx.bpp, lw*ctx.bpp, lh))\n\t\t\t\t{\n\t\t\t\t\tBZ_Free(ctx.data);\n\t\t\t\t\tctx.data = NULL;\n\t\t\t\t\treturn ctx;\n\t\t\t\t}\n\t\t\t\tf->offset = ofs;\n\t\t\t}\n\t\t}\n\t}\n\treturn ctx;\n}\nstatic struct xcf_heirachy_s XCF_ReadChannel(struct xcf_s *f)\n{\n\tstruct xcf_heirachy_s h;\n\tchar name[1024];\n\tquint32_t width = XCF_Read32(f);\n\tquint32_t height = XCF_Read32(f);\n\tunsigned int proptype, propsize;\n\tXCF_ReadString(f, name, sizeof(name));\n\tfor(;;)\n\t{\n\t\tproptype = XCF_Read32(f);\n\t\tpropsize = XCF_Read32(f);\n\t\tif (!proptype)\n\t\t\tbreak;\n\t\telse if (proptype == 3) //prop_active_channel\n\t\t\t; //ui state\n\t\telse if (proptype == 16) //prop_colour\n\t\t\tf->offset += 3; //ui state\n\t\telse if (proptype == 38) //prop_colour\n\t\t\tf->offset += 3*4; //ui state\n\t\telse if (proptype == 4) //prop_selection\n\t\t\t; //ui state\n\t\telse if (proptype == 14) //prop_showmask\n\t\t\tf->offset += 4; //ui state\n\t\telse\n\t\t{\n\t\t\tCon_DPrintf(\"Unknown channel property %i\\n\", proptype);\n\t\t\tf->offset += propsize;\n\t\t}\n\t}\n\tf->offset = XCF_ReadOffset(f);\n\th = XCF_ReadHeirachy(f);\n\tif (h.width != width || h.height != height)\n\t{\n\t\tBZ_Free(h.data);\n\t\th.data = NULL;\n\t}\n\treturn h;\n}\n\nstatic float XCF_BigFloat(void *in)\n{\n\tunion\n\t{\n\t\tqbyte b[4];\n\t\tfloat f;\n\t} u;\n\tu.b[0] = ((qbyte*)in)[3];\n\tu.b[1] = ((qbyte*)in)[2];\n\tu.b[2] = ((qbyte*)in)[1];\n\tu.b[3] = ((qbyte*)in)[0];\n\treturn u.f;\n}\nstatic float XCF_BigShort(void *in)\n{\n\treturn (((qbyte*)in)[0]<<8)|(((qbyte*)in)[1]<<0);\n}\nstatic float XCF_HalfFloat(void *in)\n{\n\treturn HalfToFloat(XCF_BigShort(in));\n}\n\nstatic qboolean XCF_CombineLayer(struct xcf_s *f)\n{\n\tstruct xcf_heirachy_s h, l={0};\n\tunsigned int proptype, propsize;\n\tsize_t width, height, type, heirachyoffset, layermaskoffset;\n\tchar name[1024];\n\tquint32_t applylayermask = false;\n\tqint32_t blendmode = 0;\n\tqboolean unsupported = false;\n\tquint32_t x,y, ofsx=0,ofsy=0;\n\tquint32_t visible = true;\n\tfloat opacity = 1;\n\twidth = XCF_Read32(f);\n\theight = XCF_Read32(f);\n\ttype = XCF_Read32(f);\n\tXCF_ReadString(f, name, sizeof(name));\n\n\tfor(;;)\n\t{\n\t\tproptype = XCF_Read32(f);\n\t\tpropsize = XCF_Read32(f);\n\t\tif (!proptype)\n\t\t\tbreak;\n\t\telse if (proptype == 2) //prop_active_layer\n\t\t\t; //ui state\n\t\telse if (proptype == 11) //prop_apply_mask\n\t\t\tapplylayermask = XCF_Read32(f);\n\t\telse if (proptype == 35) //prop_composite_mode\n\t\t\t/*compositemode =*/ XCF_Read32(f);\n\t\telse if (proptype == 36) //prop_composite_space\n\t\t\t/*colourspace = */ XCF_Read32(f);\n\t\telse if (proptype == 12) //prop_edit_mask\n\t\t\t/*editmask = */ XCF_Read32(f); //ui data, uninteresting\n\t\telse if (proptype == 5) //prop_floating_selection\n\t\t{\t//just ignore this layer.\n\t\t\t//FIXME: the *other* layer needs to composite differently\n\t\t\treturn true;\n\t\t\tXCF_ReadOffset(f);\n\t\t}\n\t\telse if (proptype == 6) //prop_opacity\n\t\t\topacity = XCF_Read32(f)/255.0;\n\t\telse if (proptype == 33) //prop_float_opacity\n\t\t\topacity = XCF_ReadFloat(f);\n\t\telse if (proptype == 34) //prop_colour_tag\n\t\t\t/*tag = */XCF_Read32(f);\n\t\telse if (proptype == 32) //prop_lock_content\n\t\t\t/*lockcontent = */XCF_Read32(f);\n\t\telse if (proptype == 29) //prop_group_item\n\t\t{\n\t\t\tCon_DPrintf(\"Unsupported layer property: prop_group_item\\n\");\n\t\t\tvisible = false;\n//\t\t\tunsupported = true; //panic\n\t\t}\n\t\telse if (proptype == 30) //prop_item_path\n\t\t{\n\t\t\tCon_DPrintf(\"Unsupported layer property: prop_item_path\\n\");\n\t\t\tvisible = false;\n//\t\t\tunsupported = true;\n\t\t\twhile(XCF_ReadOffset(f))\n\t\t\t\t;\n\t\t}\n\t\telse if (proptype == 31) //prop_group_item_flags\n\t\t\tXCF_Read32(f);\n\t\telse if (proptype == 9) //prop_linked\n\t\t\t/*layerislinked = */ XCF_Read32(f); //ui data, uninteresting\n\t\telse if (proptype == 10) //prop_lock_alpha\n\t\t\tXCF_Read32(f); //ui state\n\t\telse if (proptype == 7) //prop_mode\n\t\t\tblendmode = XCF_Read32(f);\n\t\telse if (proptype == 8) //prop_visible\n\t\t\tvisible = XCF_Read32(f);\n\t\telse if (proptype == 15) //prop_offsets\n\t\t{\n\t\t\tofsx = XCF_Read32(f);\n\t\t\tofsy = XCF_Read32(f);\n\t\t}\n\t\telse if (proptype == 13) //prop_show_mask\n\t\t\tXCF_Read32(f); //ui data, uninteresting\n\t\telse if (proptype == 26) //prop_text_layer_flags\n\t\t\tXCF_Read32(f); //ui data, uninteresting\n\t\telse if (proptype == 28) //prop_lock_content\n\t\t\tXCF_Read32(f); //ui data, uninteresting\n\t\telse if (proptype == 20) //prop_tattoo\n\t\t\tXCF_Read32(f); //ui data, uninteresting\n\t\telse if (proptype == 37) //prop_blend_space\n\t\t\t/*blendspace = */XCF_Read32(f);\n\t\telse\n\t\t{\n\t\t\tCon_DPrintf(\"Unknown layer(\\\"%s\\\") property %i\\n\", name, proptype);\n\t\t\tf->offset += propsize;\n\t\t}\n\t}\n\n\theirachyoffset = XCF_ReadOffset(f);\n\tlayermaskoffset = XCF_ReadOffset(f);\n\n\tif (unsupported || !visible)\n\t\theirachyoffset = layermaskoffset = 0;\n\n\tf->offset = heirachyoffset;\n\th = XCF_ReadHeirachy(f);\n\n\tif (applylayermask)\n\t{\t//blend the alpha...?\n\t\tf->offset = layermaskoffset;\n\t\tl = XCF_ReadChannel(f);\n\t\tif (!l.data || l.bpp!=1 || l.width != width || l.height != height)\n\t\t\tapplylayermask = false; //erk?\n\t}\n\n\tif (!h.data)\n\t{\t//its valid to have just a layermask...\n\t\th.bpp = 0; //don't try reading anything.\n\t\th.width = width;\n\t\th.height = height;\n\t}\n\telse if (h.width != width || h.height != height)\n\t\tunsupported = true;\t//level0 must match the layer's size\n\n\tif (unsupported || !visible)\n\t\t;\n\telse\n\t{\n\t\twidth = min(h.width, f->width-ofsx);\n\t\theight = min(h.height, f->height-ofsy);\n\t\tif (ofsx < f->width && ofsy < f->height)\n\t\t{\n\t\t\tqbyte *in=h.data;\n\t\t\tvec4_t px;\n\t\t\tfloat sa, da, k;\n\t\t\tif (f->outformat == PTI_RGBA32F)\n\t\t\t{\n\t\t\t\tfloat *out = (float*)f->flat + ofsx*4 + ofsy*f->width*4;\n\t\t\t\tfor (y = 0; y < height; y++, out += 4*(f->width-width), in += h.bpp*(h.width-width))\n\t\t\t\t{\n\t\t\t\t\tfor (x = 0; x < width; x++, out+=4, in+=h.bpp)\n\t\t\t\t\t{\n\t\t\t\t\t\tswitch(h.bpp)\n\t\t\t\t\t\t{\n\t\t\t\t\t\tcase 16: Vector4Set(px, XCF_BigFloat(in+0), XCF_BigFloat(in+4), XCF_BigFloat(in+8), XCF_BigFloat(in+12)); break;\n\t\t\t\t\t\tcase 12: Vector4Set(px, XCF_BigFloat(in+0), XCF_BigFloat(in+4), XCF_BigFloat(in+8), 1); break;\n\t\t\t\t\t\tcase 8:\tVector4Set(px, XCF_BigFloat(in+0), XCF_BigFloat(in+0), XCF_BigFloat(in+0), XCF_BigFloat(in+4)); break;\n\t\t\t\t\t\tcase 4:\tVector4Set(px, XCF_BigFloat(in+0), XCF_BigFloat(in+0), XCF_BigFloat(in+0), 1); break;\n\t\t\t\t\t\t//other bpp are invalid\n\t\t\t\t\t\tdefault:\n\t\t\t\t\t\tcase 0: Vector4Set(px,  1,  1,  1,  1);\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (applylayermask)\n\t\t\t\t\t\t\tpx[3] *= l.data[x+y*width]/(float)0xff; //always bytes.\n\t\t\t\t\t\tpx[3] *= opacity;\n\t\t\t\t\t\tswitch(blendmode)\n\t\t\t\t\t\t{\n\t\t\t\t\t\tcase 0:\t\t//normal(legacy)\n\t\t\t\t\t\tcase 28:\t//normal\n\t\t\t\t\t\t\tda = out[3];\n\t\t\t\t\t\t\tsa = px[3];\n\t\t\t\t\t\t\tk = 1-(1-da)*(1-sa);\n\t\t\t\t\t\t\tout[3] = k;\n\t\t\t\t\t\t\tk = sa/k;\n\t\t\t\t\t\t\tout[0] = out[0]*(1-k)+px[0]*k;\n\t\t\t\t\t\t\tout[1] = out[1]*(1-k)+px[1]*k;\n\t\t\t\t\t\t\tout[2] = out[2]*(1-k)+px[2]*k;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\tCon_Printf(\"xcf: blend mode %i is not supported\\n\", blendmode);\n\t\t\t\t\t\t\tunsupported = true;\n\t\t\t\t\t\t\tgoto parseerror;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (f->outformat == PTI_RGBA16F)\n\t\t\t{\n\t\t\t\tunsigned short *out = (unsigned short*)f->flat + ofsx*4 + ofsy*f->width*4;\n\t\t\t\tfor (y = 0; y < height; y++, out += 4*(f->width-width), in += h.bpp*(h.width-width))\n\t\t\t\t{\n\t\t\t\t\tfor (x = 0; x < width; x++, out+=4, in+=h.bpp)\n\t\t\t\t\t{\n\t\t\t\t\t\tswitch(h.bpp)\n\t\t\t\t\t\t{\n\t\t\t\t\t\tcase 8: Vector4Set(px, XCF_HalfFloat(in+0), XCF_HalfFloat(in+2), XCF_HalfFloat(in+4), XCF_HalfFloat(in+6)); break;\n\t\t\t\t\t\tcase 6: Vector4Set(px, XCF_HalfFloat(in+0), XCF_HalfFloat(in+2), XCF_HalfFloat(in+4), 1); break;\n\t\t\t\t\t\tcase 4:\tVector4Set(px, XCF_HalfFloat(in+0), XCF_HalfFloat(in+0), XCF_HalfFloat(in+0), XCF_HalfFloat(in+2)); break;\n\t\t\t\t\t\tcase 2:\tVector4Set(px, XCF_HalfFloat(in+0), XCF_HalfFloat(in+0), XCF_HalfFloat(in+0), 1); break;\n\t\t\t\t\t\t//other bpp are invalid\n\t\t\t\t\t\tdefault:\n\t\t\t\t\t\tcase 0: Vector4Set(px,  1,  1,  1,  1);\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (applylayermask)\n\t\t\t\t\t\t\tpx[3] *= l.data[x+y*width]/(float)0xff; //always bytes.\n\t\t\t\t\t\tpx[3] *= opacity;\n\t\t\t\t\t\tswitch(blendmode)\n\t\t\t\t\t\t{\n\t\t\t\t\t\tcase 0:\t\t//normal(legacy)\n\t\t\t\t\t\tcase 28:\t//normal\n\t\t\t\t\t\t\tda = HalfToFloat(out[3]);\n\t\t\t\t\t\t\tsa = px[3];\n\t\t\t\t\t\t\tk = 1-(1-da)*(1-sa);\n\t\t\t\t\t\t\tout[3] = FloatToHalf(k);\n\t\t\t\t\t\t\tk = sa/k;\n\t\t\t\t\t\t\tout[0] = FloatToHalf(HalfToFloat(out[0])*(1-k)+px[0]*k);\n\t\t\t\t\t\t\tout[1] = FloatToHalf(HalfToFloat(out[1])*(1-k)+px[1]*k);\n\t\t\t\t\t\t\tout[2] = FloatToHalf(HalfToFloat(out[2])*(1-k)+px[2]*k);\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\tCon_Printf(\"xcf: blend mode %i is not supported\\n\", blendmode);\n\t\t\t\t\t\t\tunsupported = true;\n\t\t\t\t\t\t\tgoto parseerror;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (f->outformat == PTI_RGBA16)\n\t\t\t{\n\t\t\t\tunsigned short *out = (unsigned short*)f->flat + ofsx*4 + ofsy*f->width*4;\n\t\t\t\tfor (y = 0; y < height; y++, out += 4*(f->width-width), in += h.bpp*(h.width-width))\n\t\t\t\t{\n\t\t\t\t\tfor (x = 0; x < width; x++, out+=4, in+=h.bpp)\n\t\t\t\t\t{\n\t\t\t\t\t\tswitch(h.bpp)\n\t\t\t\t\t\t{\n\t\t\t\t\t\tcase 8: Vector4Set(px, XCF_BigShort(in+0), XCF_BigShort(in+2), XCF_BigShort(in+4), XCF_BigShort(in+6));\tbreak;\n\t\t\t\t\t\tcase 6: Vector4Set(px, XCF_BigShort(in+0), XCF_BigShort(in+2), XCF_BigShort(in+4),  0xffff);\tbreak;\n\t\t\t\t\t\tcase 4: Vector4Set(px, XCF_BigShort(in+0), XCF_BigShort(in+0), XCF_BigShort(in+0), XCF_BigShort(in+2));\tbreak;\n\t\t\t\t\t\tcase 2: Vector4Set(px, XCF_BigShort(in+0), XCF_BigShort(in+0), XCF_BigShort(in+0),  0xffff);\tbreak;\n\t\t\t\t\t\tdefault:\n\t\t\t\t\t\tcase 0: Vector4Set(px,  0xffff,  0xffff,  0xffff,  0xffff);\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (applylayermask)\n\t\t\t\t\t\t\tpx[3] *= l.data[x+y*width]/(float)0xff;\n\t\t\t\t\t\tpx[3] *= opacity;\n\t\t\t\t\t\tswitch(blendmode)\n\t\t\t\t\t\t{\n\t\t\t\t\t\tcase 0:\t\t//normal(legacy)\n\t\t\t\t\t\tcase 28:\t//normal\n\t\t\t\t\t\t\tda = out[3]/(float)0xffff;\n\t\t\t\t\t\t\tsa = px[3]/0xffff;\n\t\t\t\t\t\t\tk = 1-(1-da)*(1-sa);\n\t\t\t\t\t\t\tout[3] = k*0xffff;\n\t\t\t\t\t\t\tk = sa/k;\n\t\t\t\t\t\t\tout[0] = out[0]*(1-k)+px[0]*k;\n\t\t\t\t\t\t\tout[1] = out[1]*(1-k)+px[1]*k;\n\t\t\t\t\t\t\tout[2] = out[2]*(1-k)+px[2]*k;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\tCon_Printf(\"xcf: blend mode %i is not supported\\n\", blendmode);\n\t\t\t\t\t\t\tunsupported = true;\n\t\t\t\t\t\t\tgoto parseerror;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (f->outformat == PTI_RGBA8)\n\t\t\t{\n\t\t\t\tqbyte *out = f->flat + ofsx*4 + ofsy*f->width*4;\n\t\t\t\tfor (y = 0; y < height; y++, out += 4*(f->width-width), in += h.bpp*(h.width-width))\n\t\t\t\t{\n\t\t\t\t\tfor (x = 0; x < width; x++, out+=4, in+=h.bpp)\n\t\t\t\t\t{\n\t\t\t\t\t\tswitch(h.bpp)\n\t\t\t\t\t\t{\n\t\t\t\t\t\tcase 4: Vector4Set(px, in[0], in[1], in[2], in[3]);\tbreak;\n\t\t\t\t\t\tcase 3: Vector4Set(px, in[0], in[1], in[2],  0xff);\tbreak;\n\t\t\t\t\t\tcase 2: Vector4Set(px, in[0], in[0], in[0], in[1]);\tbreak;\n\t\t\t\t\t\tcase 1: Vector4Set(px, in[0], in[0], in[0],  0xff);\tbreak;\n\t\t\t\t\t\tdefault:\n\t\t\t\t\t\tcase 0: Vector4Set(px,  0xff,  0xff,  0xff,  0xff);\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (applylayermask)\n\t\t\t\t\t\t\tpx[3] *= l.data[x+y*width]/(float)0xff;\n\t\t\t\t\t\tpx[3] *= opacity;\n\t\t\t\t\t\tswitch(blendmode)\n\t\t\t\t\t\t{\n\t\t\t\t\t\tcase 0:\t\t//normal(legacy)\n\t\t\t\t\t\tcase 28:\t//normal\n\t\t\t\t\t\t\tda = out[3]/255.0;\n\t\t\t\t\t\t\tsa = px[3]/255;\n\t\t\t\t\t\t\tk = 1-(1-da)*(1-sa);\n\t\t\t\t\t\t\tout[3] = k*255;\n\t\t\t\t\t\t\tk = sa/k;\n\t\t\t\t\t\t\tout[0] = out[0]*(1-k)+px[0]*k;\n\t\t\t\t\t\t\tout[1] = out[1]*(1-k)+px[1]*k;\n\t\t\t\t\t\t\tout[2] = out[2]*(1-k)+px[2]*k;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\tCon_Printf(\"xcf: blend mode %i is not supported\\n\", blendmode);\n\t\t\t\t\t\t\tunsupported = true;\n\t\t\t\t\t\t\tgoto parseerror;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tCon_Printf(\"xcf: colour precision %i is not supported\\n\", f->precision);\n\t\t\t\tunsupported = true;\n\t\t\t\tgoto parseerror;\n\t\t\t}\n\t\t}\n\t}\n\nparseerror:\n\tBZ_Free(l.data);\n\tBZ_Free(h.data);\n\n\t(void)type;\n\t(void)layermaskoffset;\n\n\treturn !unsupported;\n}\nstatic qbyte *ReadXCFFile(const qbyte *filedata, size_t len, const char *fname, int *width, int *height, uploadfmt_t *format)\n{\n\tsize_t offs;\n\tstruct xcf_s ctx;\n\tunsigned int bb,bw,bh,bd;\n\tif (len < 14 || strncmp(filedata, \"gimp xcf \", 9) || filedata[13])\n\t\treturn NULL;\n\tmemset(&ctx, 0, sizeof(ctx));\n\tctx.version = atoi(filedata+10);\n\tctx.filestart = filedata;\n\tctx.filesize = len;\n\tctx.offset = 14;\n\n\tctx.precision = 150;\n\tctx.outformat = PTI_RGBA8;\n\tctx.width = XCF_Read32(&ctx);\n\tctx.height = XCF_Read32(&ctx);\n\tctx.basetype = XCF_Read32(&ctx);\n\tif (ctx.basetype != 0/*rgb*/ && ctx.basetype != 1/*grey*/)\n\t{\n\t\tCon_Printf(\"%s: xcf paletted mode is not supported\\n\", fname);\n\t\treturn NULL; //doesn't really matter what format it is, we're going to output rgba regardless. we can just do it based upon the bytes per pixel.\n\t}\n\tif (ctx.version >= 4)\n\t{\n\t\tctx.precision = XCF_Read32(&ctx);\n\t\tif (ctx.version < 7)\n\t\t\tctx.precision = 150; //dev versions have different interpretations, just ignore it so that we don't have to handle that mess.\n\t\tswitch(ctx.precision)\n\t\t{\n\t\tcase 100:\tctx.outformat = PTI_RGBA8;\tbreak;\n\t\tcase 150:\tctx.outformat = PTI_RGBA8/*_SRGB*/;\tbreak; //usually this one... but we don't care too much about srgb... for some reason.\n\t\tcase 200:\tctx.outformat = PTI_RGBA16;\tbreak;\n\t\tcase 250:\tctx.outformat = PTI_RGBA16/*_SRGB*/;\tbreak;\n\t\t//case 300:\tctx.outformat = PTI_RGBA32;\tbreak;\n\t\t//case 350:\tctx.outformat = PTI_RGBA32/*_SRGB*/;\tbreak;\n\t\tcase 500:\tctx.outformat = PTI_RGBA16F;\tbreak;\n\t\tcase 550:\tctx.outformat = PTI_RGBA16F/*_SRGB*/;\tbreak;\n\t\tcase 600:\tctx.outformat = PTI_RGBA32F;\tbreak;\n\t\tcase 650:\tctx.outformat = PTI_RGBA32F/*_SRGB*/;\tbreak;\n\t\tdefault:\n\t\t\tCon_Printf(\"%s: xcf colour precision is not supported\\n\", fname);\n\t\t\treturn NULL;\n\t\t}\n\t}\n\tif (!format && ctx.outformat != PTI_RGBA8)\n\t\treturn NULL;\t//caller insists on rgba8 :(\n\tXCF_ReadHeaderProperties(&ctx);\n\twhile((offs=XCF_ReadOffset(&ctx)))\n\t{\n\t\tctx.layeroffsets = realloc(ctx.layeroffsets, sizeof(*ctx.layeroffsets)*(ctx.numlayers+1));\n\t\tctx.layeroffsets[ctx.numlayers++] = offs;\n\t}\n\t//channels\n\n\t//without any layers, its fully transparent\n\tImage_BlockSizeForEncoding(ctx.outformat, &bb,&bw,&bh,&bd); //just for the bb...\n\tctx.flat = Z_Malloc(ctx.width*ctx.height*bb);\n\tif (format)\n\t\t*format = ctx.outformat;\n\t*width = ctx.width;\n\t*height = ctx.height;\n\n\twhile(ctx.numlayers --> 0)\n\t{\n\t\tctx.offset = ctx.layeroffsets[ctx.numlayers];\n\t\tif (!XCF_CombineLayer(&ctx))\n\t\t{\n\t\t\tZ_Free(ctx.flat);\n\t\t\tctx.flat = NULL;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\n\tBZ_Free(ctx.layeroffsets);\n\treturn ctx.flat;\n}\n#endif\n\n#ifdef IMAGEFMT_EXR\n#if 0\n\t#include \"OpenEXR/ImfCRgbaFile.h\"\n#else\n\ttypedef void ImfInputFile;\n\ttypedef void ImfHeader;\n\ttypedef void ImfRgba;\n#endif\nstatic struct\n{\n\tvoid *handle;\n\tImfInputFile   *(*OpenInputFile)\t\t(const char name[]);\n\tint\t\t\t\t(*CloseInputFile)\t\t(ImfInputFile *in);\n\tint\t\t\t\t(*InputSetFrameBuffer)\t(ImfInputFile *in, ImfRgba *base, size_t xStride, size_t yStride);\n\tint\t\t\t\t(*InputReadPixels)\t\t(ImfInputFile *in, int scanLine1, int scanLine2);\n\tconst ImfHeader*(*InputHeader)\t\t\t(const ImfInputFile *in);\n\n\tvoid\t\t\t(*HeaderDataWindow) (const ImfHeader *hdr, int *xMin, int *yMin, int *xMax, int *yMax);\n} exr;\nstatic void InitLibrary_OpenEXR(void)\n{\n#ifdef IMF_MAGIC\n\texr.OpenInputFile\t\t= ImfOpenInputFile;\n\texr.CloseInputFile\t\t= ImfCloseInputFile;\n\texr.InputSetFrameBuffer\t= ImfInputSetFrameBuffer;\n\texr.InputReadPixels\t\t= ImfInputReadPixels;\n\texr.InputHeader\t\t\t= ImfInputHeader;\n\texr.HeaderDataWindow\t= ImfHeaderDataWindow;\n#else\n\tdllfunction_t funcs[] =\n\t{\n\t\t{(void**)&exr.OpenInputFile,\t\t\"ImfOpenInputFile\"},\n\t\t{(void**)&exr.CloseInputFile,\t\t\"ImfCloseInputFile\"},\n\t\t{(void**)&exr.InputSetFrameBuffer,\t\"ImfInputSetFrameBuffer\"},\n\t\t{(void**)&exr.InputReadPixels,\t\t\"ImfInputReadPixels\"},\n\t\t{(void**)&exr.InputHeader,\t\t\t\"ImfInputHeader\"},\n\t\t{(void**)&exr.HeaderDataWindow,\t\t\"ImfHeaderDataWindow\"},\n\t\t{NULL}\n\t};\n\t#ifdef __linux__\n\t\t//its some shitty c++ library, so while the C api that we use is stable, nothing else is so it has lots of random names.\n\t\tif (!exr.handle)\n\t\t\texr.handle = Sys_LoadLibrary(\"libIlmImf-2_3.so.24\", funcs);\t//debian sid(bullseye)\n\t\tif (!exr.handle)\n\t\t\texr.handle = Sys_LoadLibrary(\"libIlmImf-2_2.so.23\", funcs);\t//debian buster\n\t\tif (!exr.handle)\n\t\t\texr.handle = Sys_LoadLibrary(\"libIlmImf-2_2.so.22\", funcs);\t//debian stretch\n\t#endif\n\n\t//try the generic/dev name.\n\tif (!exr.handle)\n\t\texr.handle = Sys_LoadLibrary(\"libIlmImf\", funcs);\n#endif\n}\n#ifdef _WIN32\n#include <io.h>\n#include <windows.h>\n#endif\nstatic void *ReadEXRFile(qbyte *buf, size_t len, const char *fname, int *outwidth, int *outheight, uploadfmt_t *outformat)\n{\n\tchar tname[] = \"/tmp/exr.XXXXXX\";\n#ifndef _WIN32\n\tint fd;\n#endif\n\tImfInputFile *ctx;\n\tconst ImfHeader *hdr;\n\tvoid *result;\n\n\tif (!exr.handle)\n\t{\n\t\tCon_Printf(\"%s: libIlmImf not loaded\\n\", fname);\n\t\treturn NULL;\n\t}\n\n\t//shitty API that only supports filenames, so now that we've read it from a file, write it to a file so that it can be read. See, shitty.\n#ifdef _WIN32\n\tif (!_mktemp(tname))\n\t{\n\t\tDWORD sizewritten;\n\t\tHANDLE fd = CreateFileA(tname, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_TEMPORARY|FILE_FLAG_DELETE_ON_CLOSE, NULL);\n\t\tWriteFile(fd, buf, len, &sizewritten, NULL); //assume we managed to write it all\n\t\tctx = exr.OpenInputFile(tname); //is this be ansi or oem or utf-16 or what? lets assume that it has no idea either.\n\t\tCloseHandle(fd);\n#else\n\tfd = mkstemp(tname);\t//bsd4.3/posix1-2001\n\tif (fd >= 0)\n\t{\n\t\tif (write(fd, buf, len) == len)\n\t\t\tctx = exr.OpenInputFile(tname);\n\t\telse\n\t\t\tctx = NULL;\n\t\tclose(fd);\t//we don't need the input file now.\n\t\tunlink(tname);\n#endif\n\n\t\tif (ctx)\n\t\t{\n\t\t\tint xmin, xmax, ymin, ymax;\n\t\t\thdr = exr.InputHeader(ctx);\n\t\t\texr.HeaderDataWindow(hdr, &xmin,&ymin, &xmax,&ymax);\n\t\t\t*outwidth = (xmax-xmin)+1;\n\t\t\t*outheight = (ymax-ymin)+1;\n\t\t\tresult = BZ_Malloc(sizeof(short)*4u*(size_t)*outwidth**outheight);\n\t\t\texr.InputSetFrameBuffer(ctx, (char*)result-xmin*8-ymin*(size_t)*outwidth*8, 1, *outwidth);\n\t\t\texr.InputReadPixels(ctx, ymin, ymax);\n\t\t\texr.CloseInputFile(ctx);\n\t\t\t*outformat = PTI_RGBA16F;\t//output is always half-floats.\n\t\t\treturn result;\n\t\t}\n\t}\n\treturn NULL;\n}\n#endif\n\n#ifdef HAVE_CLIENT\n\n\n// saturate function, stolen from jitspoe\nvoid SaturateR8G8B8(qbyte *data, int size, float sat)\n{\n\tint i;\n\tfloat r, g, b, v;\n\n\tif (sat > 1)\n\t{\n\t\tfor(i=0; i < size; i+=3)\n\t\t{\n\t\t\tr = data[i];\n\t\t\tg = data[i+1];\n\t\t\tb = data[i+2];\n\n\t\t\tv = r * NTSC_RED + g * NTSC_GREEN + b * NTSC_BLUE;\n\t\t\tr = v + (r - v) * sat;\n\t\t\tg = v + (g - v) * sat;\n\t\t\tb = v + (b - v) * sat;\n\n\t\t\t// bounds check\n\t\t\tif (r < 0)\n\t\t\t\tr = 0;\n\t\t\telse if (r > 255)\n\t\t\t\tr = 255;\n\n\t\t\tif (g < 0)\n\t\t\t\tg = 0;\n\t\t\telse if (g > 255)\n\t\t\t\tg = 255;\n\n\t\t\tif (b < 0)\n\t\t\t\tb = 0;\n\t\t\telse if (b > 255)\n\t\t\t\tb = 255;\n\n\t\t\t// scale down to avoid overbright lightmaps\n\t\t\tv = v / (r * NTSC_RED + g * NTSC_GREEN + b * NTSC_BLUE);\n\t\t\tif (v > NTSC_SUM)\n\t\t\t\tv = NTSC_SUM;\n\t\t\telse\n\t\t\t\tv *= v;\n\n\t\t\tdata[i]   = r*v;\n\t\t\tdata[i+1] = g*v;\n\t\t\tdata[i+2] = b*v;\n\t\t}\n\t}\n\telse // avoid bounds check for desaturation\n\t{\n\t\tif (sat < 0)\n\t\t\tsat = 0;\n\n\t\tfor(i=0; i < size; i+=3)\n\t\t{\n\t\t\tr = data[i];\n\t\t\tg = data[i+1];\n\t\t\tb = data[i+2];\n\n\t\t\tv = r * NTSC_RED + g * NTSC_GREEN + b * NTSC_BLUE;\n\n\t\t\tdata[i]   = v + (r - v) * sat;\n\t\t\tdata[i+1] = v + (g - v) * sat;\n\t\t\tdata[i+2] = v + (b - v) * sat;\n\t\t}\n\t}\n}\n\nvoid BoostGamma(qbyte *rgba, int width, int height, uploadfmt_t fmt)\n{\n\t//note: should not be used where hardware gamma is supported.\n\tint i;\n\n\tswitch(fmt)\n\t{\n\tcase PTI_L8:\n\t\tfor (i=0 ; i<width*height ; i++)\n\t\t\trgba[i] = gammatable[rgba[i]];\n\t\tbreak;\n\tcase PTI_LLLX8:\n\tcase PTI_LLLA8:\n\tcase PTI_RGBA8:\n\tcase PTI_RGBX8:\n\tcase PTI_RGBA8_SRGB:\n\tcase PTI_RGBX8_SRGB:\n\tcase PTI_BGRA8:\n\tcase PTI_BGRX8:\n\tcase PTI_BGRA8_SRGB:\n\tcase PTI_BGRX8_SRGB:\n\t\tfor (i=0 ; i<width*height*4 ; i+=4)\n\t\t{\n\t\t\trgba[i+0] = gammatable[rgba[i+0]];\n\t\t\trgba[i+1] = gammatable[rgba[i+1]];\n\t\t\trgba[i+2] = gammatable[rgba[i+2]];\n\t\t\t//and not alpha\n\t\t}\n\t\tbreak;\n\tdefault:\n\t\tbreak;\n\t}\n}\n\n\nstatic void Image_LoadTexture_Failed(void *ctx, void *data, size_t a, size_t b)\n{\n\ttexid_t tex = ctx;\n\ttex->status = TEX_FAILED;\n}\nstatic void Image_FixupImageSize(texid_t tex, unsigned int w, unsigned int h, unsigned int d)\n{\n\ttex->width = w;\n\ttex->height = h;\n\ttex->depth = d;\n\n\t//ezhud breaks without this. I assume other things will too. this is why you shouldn't depend upon querying an image's size.\n\tif (!strncmp(tex->ident, \"gfx/\", 4))\n\t{\n\t\tsize_t lumpsize;\n\t\tqbyte lumptype;\n\t\tqpic_t *pic = W_GetLumpName(tex->ident+4, &lumpsize, &lumptype);\n\t\tif (pic && lumptype == TYP_QPIC && lumpsize >= 8)\n\t\t{\n\t\t\tw = LittleLong(pic->width);\n\t\t\th = LittleLong(pic->height);\n\t\t\tif (lumpsize == 8 + w*h)\n\t\t\t{\n\t\t\t\ttex->width = w;\n\t\t\t\ttex->height = h;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tvfsfile_t *f;\n\t\t\tconst char *ext = COM_GetFileExtension(tex->ident, NULL);\n\t\t\tif (!strcmp(ext, \".lmp\"))\n\t\t\t\tf = FS_OpenVFS(tex->ident, \"rb\", FS_GAME);\n\t\t\telse if (!*ext)\n\t\t\t{\n\t\t\t\tchar nname[MAX_QPATH+4];\n\t\t\t\tQ_snprintfz(nname, sizeof(nname), \"%s.lmp\", tex->ident);\n\t\t\t\tf = FS_OpenVFS(nname, \"rb\", FS_GAME);\n\t\t\t}\n\t\t\telse\n\t\t\t\tf = NULL;\n\t\t\tif (f)\n\t\t\t{\n\t\t\t\tunsigned int wh[2];\n\t\t\t\tsize_t size = VFS_GETLEN(f);\n\t\t\t\tVFS_READ(f, wh, 8);\n\t\t\t\tVFS_CLOSE(f);\n\n\t\t\t\tif (size == 8+wh[0]*wh[1])\n\t\t\t\t{\n\t\t\t\t\ttex->width = wh[0];\n\t\t\t\t\ttex->height = wh[1];\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\t//FIXME: check loaded wad files too.\n}\nstatic void Image_LoadTextureMips(void *ctx, void *data, size_t a, size_t b)\n{\n\tint i;\n\ttexid_t tex = ctx;\n\tstruct pendingtextureinfo *mips = data;\n\n\t//setting the dimensions here can break npot textures, so lets not do that.\n//\ttex->width = mips->mip[0].width;\n//\ttex->height = mips->mip[0].height;\n\n\t//d3d9 needs to reconfigure samplers depending on whether the data is srgb or not.\n\tswitch(mips->encoding)\n\t{\n\tcase PTI_RGBA8_SRGB:\n\tcase PTI_RGBX8_SRGB:\n\tcase PTI_BGRA8_SRGB:\n\tcase PTI_BGRX8_SRGB:\n\tcase PTI_BC1_RGB_SRGB:\n\tcase PTI_BC1_RGBA_SRGB:\n\tcase PTI_BC2_RGBA_SRGB:\n\tcase PTI_BC3_RGBA_SRGB:\n\tcase PTI_BC7_RGBA_SRGB:\n\tcase PTI_ETC2_RGB8_SRGB:\n\tcase PTI_ETC2_RGB8A1_SRGB:\n\tcase PTI_ETC2_RGB8A8_SRGB:\n\tcase PTI_ASTC_4X4_SRGB:\n\tcase PTI_ASTC_5X4_SRGB:\n\tcase PTI_ASTC_5X5_SRGB:\n\tcase PTI_ASTC_6X5_SRGB:\n\tcase PTI_ASTC_6X6_SRGB:\n\tcase PTI_ASTC_8X5_SRGB:\n\tcase PTI_ASTC_8X6_SRGB:\n\tcase PTI_ASTC_10X5_SRGB:\n\tcase PTI_ASTC_10X6_SRGB:\n\tcase PTI_ASTC_8X8_SRGB:\n\tcase PTI_ASTC_10X8_SRGB:\n\tcase PTI_ASTC_10X10_SRGB:\n\tcase PTI_ASTC_12X10_SRGB:\n\tcase PTI_ASTC_12X12_SRGB:\n\t\ttex->flags |= IF_SRGB;\n\t\tbreak;\n\tdefault:\n\t\ttex->flags &= ~IF_SRGB;\n\t\tbreak;\n\t}\n\n\tif ((tex->flags & IF_TEXTYPEMASK)==IF_TEXTYPE_ANY)\n\t\ttex->flags = (tex->flags&~IF_TEXTYPEMASK)|(mips->type<<IF_TEXTYPESHIFT);\n\n\tif (rf->IMG_LoadTextureMips(tex, mips))\n\t{\n\t\ttex->format = mips->encoding;\n\t\ttex->status = TEX_LOADED;\n\t}\n\telse\n\t{\t//failure can happen because a) lost device. b) out of device memory. c) format not supported.\n\t\t//FIXME: handle oom properly.\n\t\ttex->format = TF_INVALID;\n\t\ttex->status = TEX_FAILED;\n\t}\n\n\tfor (i = 0; i < mips->mipcount; i++)\n\t\tif (mips->mip[i].needfree)\n\t\t\tBZ_Free(mips->mip[i].data);\n\tif (mips->extrafree)\n\t\tBZ_Free(mips->extrafree);\n\tBZ_Free(mips);\n}\n#endif\n\n#ifdef IMAGEFMT_KTX\ntypedef struct\n{\n\tchar magic[12];\n\tunsigned int endianness;\n\n\tunsigned int gltype;\n\tunsigned int gltypesize;\n\tunsigned int glformat;\n\tunsigned int glinternalformat;\n\n\tunsigned int glbaseinternalformat;\n\tunsigned int pixelwidth;\n\tunsigned int pixelheight;\n\tunsigned int pixeldepth;\n\n\tunsigned int numberofarrayelements;\n\tunsigned int numberoffaces;\n\tunsigned int numberofmipmaplevels;\n\tunsigned int bytesofkeyvaluedata;\n} ktxheader_t;\nqboolean Image_WriteKTXFile(const char *filename, enum fs_relative fsroot, struct pendingtextureinfo *mips)\n{\n\tunsigned int bb,bw,bh,bd;\n\tvfsfile_t *file;\n\tktxheader_t header = {{0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31, 0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A}, 0x04030201/*endianness*/,\n\t\t0/*type*/, 1/*typesize*/, 0/*format*/, 0/*internalformat*/,\n\t\t0/*base*/, mips->mip[0].width, mips->mip[0].height, 0/*depth*/,\n\t\t0/*array elements*/, 1, mips->mipcount, 0/*kvdatasize*/};\n\tsize_t mipnum;\n\tif (mips->type==PTI_CUBE_ARRAY)\n\t{\n\t\tif (!mips->mip[0].depth || mips->mip[0].depth % 6)\n\t\t{\n\t\t\tCon_Printf(\"Image_WriteKTXFile: malformed cube\\n\");\n\t\t\treturn false;\t//malformed...\n\t\t}\n\t\theader.numberoffaces = 6;\n\t\theader.numberofarrayelements = mips->mip[0].depth/6;\n\t}\n\telse if (mips->type==PTI_CUBE)\n\t{\n\t\tif (mips->mip[0].depth != 6)\n\t\t{\n\t\t\tCon_Printf(\"Image_WriteKTXFile: malformed cube\\n\");\n\t\t\treturn false;\t//malformed...\n\t\t}\n\t\theader.numberofarrayelements = 0;\n\t\theader.numberoffaces = 6;\n\t}\n\telse if (mips->type==PTI_2D_ARRAY)\n\t{\n\t\tif (!mips->mip[0].depth)\n\t\t\treturn false;\n\t\theader.numberofarrayelements = mips->mip[0].depth;\n\t}\n\telse if (mips->type == PTI_3D)\n\t\theader.pixeldepth = mips->mip[0].depth;\n\telse if (mips->type == PTI_2D)\n\t{\n\t\tif (mips->mip[0].depth != 1)\n\t\t\treturn false;\n\t}\n\telse\n\t{\n\t\tCon_Printf(\"Image_WriteKTXFile: unsupported texture type\\n\");\n\t\treturn false;\n\t}\n\n\tImage_BlockSizeForEncoding(mips->encoding, &bb, &bw, &bh, &bd);\n\n\tsafeswitch(mips->encoding)\n\t{\n\tcase PTI_ETC1_RGB8:\t\t\theader.glinternalformat = 0x8D64/*GL_ETC1_RGB8_OES*/; break;\n\tcase PTI_ETC2_RGB8:\t\t\theader.glinternalformat = 0x9274/*GL_COMPRESSED_RGB8_ETC2*/; break;\n\tcase PTI_ETC2_RGB8_SRGB:\theader.glinternalformat = 0x9275/*GL_COMPRESSED_SRGB8_ETC2*/; break;\n\tcase PTI_ETC2_RGB8A1:\t\theader.glinternalformat = 0x9276/*GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2*/; break;\n\tcase PTI_ETC2_RGB8A1_SRGB:\theader.glinternalformat = 0x9277/*GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2*/; break;\n\tcase PTI_ETC2_RGB8A8:\t\theader.glinternalformat = 0x9278/*GL_COMPRESSED_RGBA8_ETC2_EAC*/; break;\n\tcase PTI_ETC2_RGB8A8_SRGB:\theader.glinternalformat = 0x9279/*GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC*/; break;\n\tcase PTI_EAC_R11:\t\t\theader.glinternalformat = 0x9270/*GL_COMPRESSED_R11_EAC*/; break;\n\tcase PTI_EAC_R11_SNORM:\t\theader.glinternalformat = 0x9271/*GL_COMPRESSED_SIGNED_R11_EAC*/; break;\n\tcase PTI_EAC_RG11:\t\t\theader.glinternalformat = 0x9272/*GL_COMPRESSED_RG11_EAC*/; break;\n\tcase PTI_EAC_RG11_SNORM:\theader.glinternalformat = 0x9273/*GL_COMPRESSED_SIGNED_RG11_EAC*/; break;\n\tcase PTI_BC1_RGB:\t\t\theader.glinternalformat = 0x83F0/*GL_COMPRESSED_RGB_S3TC_DXT1_EXT*/; break;\n\tcase PTI_BC1_RGB_SRGB:\t\theader.glinternalformat = 0x8C4C/*GL_COMPRESSED_SRGB_S3TC_DXT1_EXT*/; break;\n\tcase PTI_BC1_RGBA:\t\t\theader.glinternalformat = 0x83F1/*GL_COMPRESSED_RGBA_S3TC_DXT1_EXT*/; break;\n\tcase PTI_BC1_RGBA_SRGB:\t\theader.glinternalformat = 0x8C4D/*GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT*/; break;\n\tcase PTI_BC2_RGBA:\t\t\theader.glinternalformat = 0x83F2/*GL_COMPRESSED_RGBA_S3TC_DXT3_EXT*/; break;\n\tcase PTI_BC2_RGBA_SRGB:\t\theader.glinternalformat = 0x8C4E/*GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT*/; break;\n\tcase PTI_BC3_RGBA:\t\t\theader.glinternalformat = 0x83F3/*GL_COMPRESSED_RGBA_S3TC_DXT5_EXT*/; break;\n\tcase PTI_BC3_RGBA_SRGB:\t\theader.glinternalformat = 0x8C4F/*GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT*/; break;\n\tcase PTI_BC4_R_SNORM:\t\theader.glinternalformat = 0x8DBC/*GL_COMPRESSED_SIGNED_RED_RGTC1*/; break;\n\tcase PTI_BC4_R:\t\t\t\theader.glinternalformat = 0x8DBB/*GL_COMPRESSED_RED_RGTC1*/; break;\n\tcase PTI_BC5_RG_SNORM:\t\theader.glinternalformat = 0x8DBE/*GL_COMPRESSED_SIGNED_RG_RGTC2*/; break;\n\tcase PTI_BC5_RG:\t\t\theader.glinternalformat = 0x8DBD/*GL_COMPRESSED_RG_RGTC2*/; break;\n\tcase PTI_BC6_RGB_UFLOAT:\theader.glinternalformat = 0x8E8F/*GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB*/; break;\n\tcase PTI_BC6_RGB_SFLOAT:\theader.glinternalformat = 0x8E8E/*GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB*/; break;\n\tcase PTI_BC7_RGBA:\t\t\theader.glinternalformat = 0x8E8C/*GL_COMPRESSED_RGBA_BPTC_UNORM_ARB*/; break;\n\tcase PTI_BC7_RGBA_SRGB:\t\theader.glinternalformat = 0x8E8D/*GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB*/; break;\n\tcase PTI_ASTC_4X4_HDR:\t\t//sadly gl/ktx does not distinguish between ldr+hdr, which presents problems that will have to be handled by the loader.\n\tcase PTI_ASTC_4X4_LDR:\t\theader.glinternalformat = 0x93B0/*GL_COMPRESSED_RGBA_ASTC_4x4_KHR*/; break;\n\tcase PTI_ASTC_5X4_HDR:\n\tcase PTI_ASTC_5X4_LDR:\t\theader.glinternalformat = 0x93B1/*GL_COMPRESSED_RGBA_ASTC_5x4_KHR*/; break;\n\tcase PTI_ASTC_5X5_HDR:\n\tcase PTI_ASTC_5X5_LDR:\t\theader.glinternalformat = 0x93B2/*GL_COMPRESSED_RGBA_ASTC_5x5_KHR*/; break;\n\tcase PTI_ASTC_6X5_HDR:\n\tcase PTI_ASTC_6X5_LDR:\t\theader.glinternalformat = 0x93B3/*GL_COMPRESSED_RGBA_ASTC_6x5_KHR*/; break;\n\tcase PTI_ASTC_6X6_HDR:\n\tcase PTI_ASTC_6X6_LDR:\t\theader.glinternalformat = 0x93B4/*GL_COMPRESSED_RGBA_ASTC_6x6_KHR*/; break;\n\tcase PTI_ASTC_8X5_HDR:\n\tcase PTI_ASTC_8X5_LDR:\t\theader.glinternalformat = 0x93B5/*GL_COMPRESSED_RGBA_ASTC_8x5_KHR*/; break;\n\tcase PTI_ASTC_8X6_HDR:\n\tcase PTI_ASTC_8X6_LDR:\t\theader.glinternalformat = 0x93B6/*GL_COMPRESSED_RGBA_ASTC_8x6_KHR*/; break;\n\tcase PTI_ASTC_8X8_HDR:\n\tcase PTI_ASTC_8X8_LDR:\t\theader.glinternalformat = 0x93B7/*GL_COMPRESSED_RGBA_ASTC_8x8_KHR*/; break;\n\tcase PTI_ASTC_10X5_HDR:\n\tcase PTI_ASTC_10X5_LDR:\t\theader.glinternalformat = 0x93B8/*GL_COMPRESSED_RGBA_ASTC_10x5_KHR*/; break;\n\tcase PTI_ASTC_10X6_HDR:\n\tcase PTI_ASTC_10X6_LDR:\t\theader.glinternalformat = 0x93B9/*GL_COMPRESSED_RGBA_ASTC_10x6_KHR*/; break;\n\tcase PTI_ASTC_10X8_HDR:\n\tcase PTI_ASTC_10X8_LDR:\t\theader.glinternalformat = 0x93BA/*GL_COMPRESSED_RGBA_ASTC_10x8_KHR*/; break;\n\tcase PTI_ASTC_10X10_HDR:\n\tcase PTI_ASTC_10X10_LDR:\theader.glinternalformat = 0x93BB/*GL_COMPRESSED_RGBA_ASTC_10x10_KHR*/; break;\n\tcase PTI_ASTC_12X10_HDR:\n\tcase PTI_ASTC_12X10_LDR:\theader.glinternalformat = 0x93BC/*GL_COMPRESSED_RGBA_ASTC_12x10_KHR*/; break;\n\tcase PTI_ASTC_12X12_HDR:\n\tcase PTI_ASTC_12X12_LDR:\theader.glinternalformat = 0x93BD/*GL_COMPRESSED_RGBA_ASTC_12x12_KHR*/; break;\n\tcase PTI_ASTC_4X4_SRGB:\t\theader.glinternalformat = 0x93D0/*GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR*/; break;\n\tcase PTI_ASTC_5X4_SRGB:\t\theader.glinternalformat = 0x93D1/*GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR*/; break;\n\tcase PTI_ASTC_5X5_SRGB:\t\theader.glinternalformat = 0x93D2/*GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR*/; break;\n\tcase PTI_ASTC_6X5_SRGB:\t\theader.glinternalformat = 0x93D3/*GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR*/; break;\n\tcase PTI_ASTC_6X6_SRGB:\t\theader.glinternalformat = 0x93D4/*GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR*/; break;\n\tcase PTI_ASTC_8X5_SRGB:\t\theader.glinternalformat = 0x93D5/*GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR*/; break;\n\tcase PTI_ASTC_8X6_SRGB:\t\theader.glinternalformat = 0x93D6/*GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR*/; break;\n\tcase PTI_ASTC_8X8_SRGB:\t\theader.glinternalformat = 0x93D7/*GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR*/; break;\n\tcase PTI_ASTC_10X5_SRGB:\theader.glinternalformat = 0x93D8/*GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR*/; break;\n\tcase PTI_ASTC_10X6_SRGB:\theader.glinternalformat = 0x93D9/*GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR*/; break;\n\tcase PTI_ASTC_10X8_SRGB:\theader.glinternalformat = 0x93DA/*GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR*/; break;\n\tcase PTI_ASTC_10X10_SRGB:\theader.glinternalformat = 0x93DB/*GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR*/; break;\n\tcase PTI_ASTC_12X10_SRGB:\theader.glinternalformat = 0x93DC/*GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR*/; break;\n\tcase PTI_ASTC_12X12_SRGB:\theader.glinternalformat = 0x93DD/*GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR*/; break;\n#ifdef ASTC3D\n\tcase PTI_ASTC_3X3X3_HDR:\n\tcase PTI_ASTC_3X3X3_LDR:\theader.glinternalformat = 0x93C0/*GL_COMPRESSED_RGBA_ASTC_3x3x3_OES*/; break;\n\tcase PTI_ASTC_4X3X3_HDR:\n\tcase PTI_ASTC_4X3X3_LDR:\theader.glinternalformat = 0x93C1/*GL_COMPRESSED_RGBA_ASTC_4x3x3_OES*/; break;\n\tcase PTI_ASTC_4X4X3_HDR:\n\tcase PTI_ASTC_4X4X3_LDR:\theader.glinternalformat = 0x93C2/*GL_COMPRESSED_RGBA_ASTC_4x4x3_OES*/; break;\n\tcase PTI_ASTC_4X4X4_HDR:\n\tcase PTI_ASTC_4X4X4_LDR:\theader.glinternalformat = 0x93C3/*GL_COMPRESSED_RGBA_ASTC_4x4x5_OES*/; break;\n\tcase PTI_ASTC_5X4X4_HDR:\n\tcase PTI_ASTC_5X4X4_LDR:\theader.glinternalformat = 0x93C4/*GL_COMPRESSED_RGBA_ASTC_5x4x4_OES*/; break;\n\tcase PTI_ASTC_5X5X4_HDR:\n\tcase PTI_ASTC_5X5X4_LDR:\theader.glinternalformat = 0x93C5/*GL_COMPRESSED_RGBA_ASTC_5x5x4_OES*/; break;\n\tcase PTI_ASTC_5X5X5_HDR:\n\tcase PTI_ASTC_5X5X5_LDR:\theader.glinternalformat = 0x93C6/*GL_COMPRESSED_RGBA_ASTC_5x5x5_OES*/; break;\n\tcase PTI_ASTC_6X5X5_HDR:\n\tcase PTI_ASTC_6X5X5_LDR:\theader.glinternalformat = 0x93C7/*GL_COMPRESSED_RGBA_ASTC_6x5x5_OES*/; break;\n\tcase PTI_ASTC_6X6X5_HDR:\n\tcase PTI_ASTC_6X6X5_LDR:\theader.glinternalformat = 0x93C8/*GL_COMPRESSED_RGBA_ASTC_6x6x5_OES*/; break;\n\tcase PTI_ASTC_6X6X6_HDR:\n\tcase PTI_ASTC_6X6X6_LDR:\theader.glinternalformat = 0x93C9/*GL_COMPRESSED_RGBA_ASTC_6x6x6_OES*/; break;\n\tcase PTI_ASTC_3X3X3_SRGB:\theader.glinternalformat = 0x93E0/*GL_COMPRESSED_SRGB8_ALPHA8_ASTC_3x3x3_OES*/; break;\n\tcase PTI_ASTC_4X3X3_SRGB:\theader.glinternalformat = 0x93E1/*GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x3x3_OES*/; break;\n\tcase PTI_ASTC_4X4X3_SRGB:\theader.glinternalformat = 0x93E2/*GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4x3_OES*/; break;\n\tcase PTI_ASTC_4X4X4_SRGB:\theader.glinternalformat = 0x93E3/*GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4x4_OES*/; break;\n\tcase PTI_ASTC_5X4X4_SRGB:\theader.glinternalformat = 0x93E4/*GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4x4_OES*/; break;\n\tcase PTI_ASTC_5X5X4_SRGB:\theader.glinternalformat = 0x93E5/*GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5x4_OES*/; break;\n\tcase PTI_ASTC_5X5X5_SRGB:\theader.glinternalformat = 0x93E6/*GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5x5_OES*/; break;\n\tcase PTI_ASTC_6X5X5_SRGB:\theader.glinternalformat = 0x93E7/*GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5x5_OES*/; break;\n\tcase PTI_ASTC_6X6X5_SRGB:\theader.glinternalformat = 0x93E8/*GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6x5_OES*/; break;\n\tcase PTI_ASTC_6X6X6_SRGB:\theader.glinternalformat = 0x93E9/*GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6x6_OES*/; break;\n#endif\n\n\tcase PTI_BGRA8:\t\t\t\theader.glinternalformat = 0x8058/*GL_RGBA8*/;\t\t\t\theader.glbaseinternalformat = 0x1908/*GL_RGBA*/;\t\t\theader.glformat = 0x80E1/*GL_BGRA*/;\t\t\theader.gltype = 0x1401/*GL_UNSIGNED_BYTE*/;\t\t\t\t\theader.gltypesize = 1; break;\n\tcase PTI_RGBA8:\t\t\t\theader.glinternalformat = 0x8058/*GL_RGBA8*/;\t\t\t\theader.glbaseinternalformat = 0x1908/*GL_RGBA*/;\t\t\theader.glformat = 0x1908/*GL_RGBA*/;\t\t\theader.gltype = 0x1401/*GL_UNSIGNED_BYTE*/;\t\t\t\t\theader.gltypesize = 1; break;\n\tcase PTI_BGRA8_SRGB:\t\theader.glinternalformat = 0x8C43/*GL_SRGB8_ALPHA8*/;\t\theader.glbaseinternalformat = 0x1908/*GL_RGBA*/;\t\t\theader.glformat = 0x80E1/*GL_BGRA*/;\t\t\theader.gltype = 0x1401/*GL_UNSIGNED_BYTE*/;\t\t\t\t\theader.gltypesize = 1; break;\n\tcase PTI_RGBA8_SRGB:\t\theader.glinternalformat = 0x8C43/*GL_SRGB8_ALPHA8*/;\t\theader.glbaseinternalformat = 0x1908/*GL_RGBA*/;\t\t\theader.glformat = 0x1908/*GL_RGBA*/;\t\t\theader.gltype = 0x1401/*GL_UNSIGNED_BYTE*/;\t\t\t\t\theader.gltypesize = 1; break;\n\tcase PTI_L8:\t\t\t\theader.glinternalformat = 0x8040/*GL_LUMINANCE8*/;\t\t\theader.glbaseinternalformat = 0x1909/*GL_LUMINANCE*/;\t\theader.glformat = 0x1909/*GL_LUMINANCE*/;\t\theader.gltype = 0x1401/*GL_UNSIGNED_BYTE*/;\t\t\t\t\theader.gltypesize = 1; break;\n\tcase PTI_L8A8:\t\t\t\theader.glinternalformat = 0x8045/*GL_LUMINANCE8_ALPHA8*/;\theader.glbaseinternalformat = 0x190A/*GL_LUMINANCE_ALPHA*/;\theader.glformat = 0x190A/*GL_LUMINANCE_ALPHA*/;\theader.gltype = 0x1401/*GL_UNSIGNED_BYTE*/;\t\t\t\t\theader.gltypesize = 1; break;\n\tcase PTI_L8_SRGB:\t\t\theader.glinternalformat = 0x8C47/*GL_SLUMINANCE8*/;\t\t\theader.glbaseinternalformat = 0x1909/*GL_LUMINANCE*/;\t\theader.glformat = 0x1909/*GL_LUMINANCE*/;\t\theader.gltype = 0x1401/*GL_UNSIGNED_BYTE*/;\t\t\t\t\theader.gltypesize = 1; break;\n\tcase PTI_L8A8_SRGB:\t\t\theader.glinternalformat = 0x8C45/*GL_SLUMINANCE8_ALPHA8*/;\theader.glbaseinternalformat = 0x190A/*GL_LUMINANCE_ALPHA*/;\theader.glformat = 0x190A/*GL_LUMINANCE_ALPHA*/;\theader.gltype = 0x1401/*GL_UNSIGNED_BYTE*/;\t\t\t\t\theader.gltypesize = 1; break;\n\tcase PTI_RGB8:\t\t\t\theader.glinternalformat = 0x8051/*GL_RGB8*/;\t\t\t\theader.glbaseinternalformat = 0x1907/*GL_RGB*/;\t\t\t\theader.glformat = 0x1907/*GL_RGB*/;\t\t\t\theader.gltype = 0x1401/*GL_UNSIGNED_BYTE*/;\t\t\t\t\theader.gltypesize = 1; break;\n\tcase PTI_BGR8:\t\t\t\theader.glinternalformat = 0x8051/*GL_RGB8*/;\t\t\t\theader.glbaseinternalformat = 0x1907/*GL_RGB*/;\t\t\t\theader.glformat = 0x80E0/*GL_BGR*/;\t\t\t\theader.gltype = 0x1401/*GL_UNSIGNED_BYTE*/;\t\t\t\t\theader.gltypesize = 1; break;\n\tcase PTI_RGB8_SRGB:\t\t\theader.glinternalformat = 0x8C41/*GL_SRGB8*/;\t\t\t\theader.glbaseinternalformat = 0x1907/*GL_RGB*/;\t\t\t\theader.glformat = 0x1907/*GL_RGB*/;\t\t\t\theader.gltype = 0x1401/*GL_UNSIGNED_BYTE*/;\t\t\t\t\theader.gltypesize = 1; break;\n\tcase PTI_BGR8_SRGB:\t\t\theader.glinternalformat = 0x8C41/*GL_SRGB8*/;\t\t\t\theader.glbaseinternalformat = 0x1907/*GL_RGB*/;\t\t\t\theader.glformat = 0x80E0/*GL_BGR*/;\t\t\t\theader.gltype = 0x1401/*GL_UNSIGNED_BYTE*/;\t\t\t\t\theader.gltypesize = 1; break;\n\tcase PTI_R16:\t\t\t\theader.glinternalformat = 0x822A/*GL_R16*/;\t\t\t\t\theader.glbaseinternalformat = 0x1903/*GL_RED*/;\t\t\t\theader.glformat = 0x1903/*GL_RED*/;\t\t\t\theader.gltype = 0x1403/*GL_UNSIGNED_SHORT*/;\t\t\t\theader.gltypesize = 2; break;\n\tcase PTI_RGBA16:\t\t\theader.glinternalformat = 0x805B/*GL_RGBA16*/;\t\t\t\theader.glbaseinternalformat = 0x1903/*GL_RED*/;\t\t\t\theader.glformat = 0x1903/*GL_RED*/;\t\t\t\theader.gltype = 0x1403/*GL_UNSIGNED_SHORT*/;\t\t\t\theader.gltypesize = 2; break;\n\tcase PTI_R16F:\t\t\t\theader.glinternalformat = 0x822D/*GL_R16F*/;\t\t\t\theader.glbaseinternalformat = 0x1903/*GL_RED*/;\t\t\t\theader.glformat = 0x1903/*GL_RED*/;\t\t\t\theader.gltype = 0x140B/*GL_HALF_FLOAT*/;\t\t\t\t\theader.gltypesize = 2; break;\n\tcase PTI_R32F:\t\t\t\theader.glinternalformat = 0x822E/*GL_R32F*/;\t\t\t\theader.glbaseinternalformat = 0x1903/*GL_RED*/;\t\t\t\theader.glformat = 0x1903/*GL_RED*/;\t\t\t\theader.gltype = 0x1406/*GL_FLOAT*/;\t\t\t\t\t\t\theader.gltypesize = 4; break;\n\tcase PTI_RGBA16F:\t\t\theader.glinternalformat = 0x881A/*GL_RGBA16F*/;\t\t\t\theader.glbaseinternalformat = 0x1908/*GL_RGBA*/;\t\t\theader.glformat = 0x1908/*GL_RGBA*/;\t\t\theader.gltype = 0x140B/*GL_HALF_FLOAT*/;\t\t\t\t\theader.gltypesize = 2; break;\n\tcase PTI_RGB32F:\t\t\theader.glinternalformat = 0x8815/*GL_RGB32F*/;\t\t\t\theader.glbaseinternalformat = 0x1907/*GL_RGB*/;\t\t\t\theader.glformat = 0x1907/*GL_RGB*/;\t\t\t\theader.gltype = 0x1406/*GL_FLOAT*/;\t\t\t\t\t\t\theader.gltypesize = 4; break;\n\tcase PTI_RGBA32F:\t\t\theader.glinternalformat = 0x8814/*GL_RGBA32F*/;\t\t\t\theader.glbaseinternalformat = 0x1908/*GL_RGBA*/;\t\t\theader.glformat = 0x1908/*GL_RGBA*/;\t\t\theader.gltype = 0x1406/*GL_FLOAT*/;\t\t\t\t\t\t\theader.gltypesize = 4; break;\n\tcase PTI_A2BGR10:\t\t\theader.glinternalformat = 0x8059/*GL_RGB10_A2*/;\t\t\theader.glbaseinternalformat = 0x1908/*GL_RGBA*/;\t\t\theader.glformat = 0x1908/*GL_RGBA*/;\t\t\theader.gltype = 0x8368/*GL_UNSIGNED_INT_2_10_10_10_REV*/;\theader.gltypesize = 4; break;\n\tcase PTI_E5BGR9:\t\t\theader.glinternalformat = 0x8C3D/*GL_RGB9_E5*/;\t\t\t\theader.glbaseinternalformat = 0x8C3D/*GL_RGB9_E5*/;\t\t\theader.glformat = 0x1907/*GL_RGB*/;\t\t\t\theader.gltype = 0x8C3E/*GL_UNSIGNED_INT_5_9_9_9_REV*/;\t\theader.gltypesize = 4; break;\n\tcase PTI_B10G11R11F:\t\theader.glinternalformat = 0x8C3A/*GL_R11F_G11F_B10F*/;\t\theader.glbaseinternalformat = 0x8C3D/*GL_R11F_G11F_B10F*/;\theader.glformat = 0x1907/*GL_RGB*/;\t\t\t\theader.gltype = 0x8C3B/*GL_UNSIGNED_INT_10_11_11_REV*/;\t\theader.gltypesize = 4; break;\n\tcase PTI_P8:\n\tcase PTI_R8:\t\t\t\theader.glinternalformat = 0x8229/*GL_R8*/;\t\t\t\t\theader.glbaseinternalformat = 0x1903/*GL_RED*/;\t\t\t\theader.glformat = 0x1903/*GL_RED*/;\t\t\t\theader.gltype = 0x1401/*GL_UNSIGNED_BYTE*/;\t\t\t\t\theader.gltypesize = 1; break;\n\tcase PTI_RG8:\t\t\t\theader.glinternalformat = 0x822B/*GL_RG8*/;\t\t\t\t\theader.glbaseinternalformat = 0x8227/*GL_RG*/;\t\t\t\theader.glformat = 0x8227/*GL_RG*/;\t\t\t\theader.gltype = 0x1401/*GL_UNSIGNED_BYTE*/;\t\t\t\t\theader.gltypesize = 1; break;\n\tcase PTI_R8_SNORM:\t\t\theader.glinternalformat = 0x8F94/*GL_R8_SNORM*/;\t\t\theader.glbaseinternalformat = 0x1903/*GL_RED*/;\t\t\t\theader.glformat = 0x1903/*GL_RED*/;\t\t\t\theader.gltype = 0x1400/*GL_BYTE*/;\t\t\t\t\t\t\theader.gltypesize = 1; break;\n\tcase PTI_RG8_SNORM:\t\t\theader.glinternalformat = 0x8F95/*GL_RG8_SNORM*/;\t\t\theader.glbaseinternalformat = 0x8227/*GL_RG*/;\t\t\t\theader.glformat = 0x8227/*GL_RG*/;\t\t\t\theader.gltype = 0x1400/*GL_BYTE*/;\t\t\t\t\t\t\theader.gltypesize = 1; break;\n\tcase PTI_BGRX8:\t\t\t\theader.glinternalformat = 0x8051/*GL_RGB8*/;\t\t\t\theader.glbaseinternalformat = 0x1907/*GL_RGB*/;\t\t\t\theader.glformat = 0x80E1/*GL_BGRA*/;\t\t\theader.gltype = 0x1401/*GL_UNSIGNED_BYTE*/;\t\t\t\t\theader.gltypesize = 1; break;\n\tcase PTI_RGBX8:\t\t\t\theader.glinternalformat = 0x8051/*GL_RGB8*/;\t\t\t\theader.glbaseinternalformat = 0x1907/*GL_RGB*/;\t\t\t\theader.glformat = 0x1908/*GL_RGBA*/;\t\t\theader.gltype = 0x1401/*GL_UNSIGNED_BYTE*/;\t\t\t\t\theader.gltypesize = 1; break;\n\tcase PTI_BGRX8_SRGB:\t\theader.glinternalformat = 0x8C41/*GL_SRGB8*/;\t\t\t\theader.glbaseinternalformat = 0x1908/*GL_RGBA*/;\t\t\theader.glformat = 0x80E1/*GL_BGRA*/;\t\t\theader.gltype = 0x1401/*GL_UNSIGNED_BYTE*/;\t\t\t\t\theader.gltypesize = 1; break;\n\tcase PTI_RGBX8_SRGB:\t\theader.glinternalformat = 0x8C41/*GL_SRGB8*/;\t\t\t\theader.glbaseinternalformat = 0x1908/*GL_RGBA*/;\t\t\theader.glformat = 0x1908/*GL_RGBA*/;\t\t\theader.gltype = 0x1401/*GL_UNSIGNED_BYTE*/;\t\t\t\t\theader.gltypesize = 1; break;\n\tcase PTI_RGB565:\t\t\theader.glinternalformat = 0x8D62/*GL_RGB565*/;\t\t\t\theader.glbaseinternalformat = 0x1907/*GL_RGB*/;\t\t\t\theader.glformat = 0x1907/*GL_RGB*/;\t\t\t\theader.gltype = 0x8363/*GL_UNSIGNED_SHORT_5_6_5*/;\t\t\theader.gltypesize = 2; break;\n\tcase PTI_RGBA4444:\t\t\theader.glinternalformat = 0x8056/*GL_RGBA4*/;\t\t\t\theader.glbaseinternalformat = 0x1908/*GL_RGBA*/;\t\t\theader.glformat = 0x1908/*GL_RGBA*/;\t\t\theader.gltype = 0x8033/*GL_UNSIGNED_SHORT_4_4_4_4*/;\t\theader.gltypesize = 2; break;\n\tcase PTI_ARGB4444:\t\t\theader.glinternalformat = 0x8056/*GL_RGBA4*/;\t\t\t\theader.glbaseinternalformat = 0x1908/*GL_RGBA*/;\t\t\theader.glformat = 0x80E1/*GL_BGRA*/;\t\t\theader.gltype = 0x8365/*GL_UNSIGNED_SHORT_4_4_4_4_REV*/;\theader.gltypesize = 2; break;\n\tcase PTI_RGBA5551:\t\t\theader.glinternalformat = 0x8057/*GL_RGB5_A1*/;\t\t\t\theader.glbaseinternalformat = 0x1908/*GL_RGBA*/;\t\t\theader.glformat = 0x1908/*GL_RGBA*/;\t\t\theader.gltype = 0x8034/*GL_UNSIGNED_SHORT_5_5_5_1*/;\t\theader.gltypesize = 2; break;\n\tcase PTI_ARGB1555:\t\t\theader.glinternalformat = 0x8057/*GL_RGB5_A1*/;\t\t\t\theader.glbaseinternalformat = 0x1908/*GL_RGBA*/;\t\t\theader.glformat = 0x80E1/*GL_BGRA*/;\t\t\theader.gltype = 0x8366/*GL_UNSIGNED_SHORT_1_5_5_5_REV*/;\theader.gltypesize = 2; break;\n\tcase PTI_DEPTH16:\t\t\theader.glinternalformat = 0x81A5/*GL_DEPTH_COMPONENT16*/;\theader.glbaseinternalformat = 0x1902/*GL_DEPTH_COMPONENT*/;\theader.glformat = 0x1902/*GL_DEPTH_COMPONENT*/;\theader.gltype = 0x1403/*GL_UNSIGNED_SHORT*/;\t\t\t\theader.gltypesize = 2; break;\n\tcase PTI_DEPTH24:\t\t\theader.glinternalformat = 0x81A6/*GL_DEPTH_COMPONENT24*/;\theader.glbaseinternalformat = 0x1902/*GL_DEPTH_COMPONENT*/;\theader.glformat = 0x1902/*GL_DEPTH_COMPONENT*/;\theader.gltype = 0x1405/*GL_UNSIGNED_INT*/;\t\t\t\t\theader.gltypesize = 3; break;\n\tcase PTI_DEPTH32:\t\t\theader.glinternalformat = 0x81A7/*GL_DEPTH_COMPONENT32*/;\theader.glbaseinternalformat = 0x1902/*GL_DEPTH_COMPONENT*/;\theader.glformat = 0x1902/*GL_DEPTH_COMPONENT*/;\theader.gltype = 0x1406/*GL_FLOAT*/;\t\t\t\t\t\t\theader.gltypesize = 4; break;\n\tcase PTI_DEPTH24_8:\t\t\theader.glinternalformat = 0x88F0/*GL_DEPTH24_STENCIL8*/;\theader.glbaseinternalformat = 0x84F9/*GL_DEPTH_STENCIL*/;\theader.glformat = 0x84F9/*GL_DEPTH_STENCIL*/;\theader.gltype = 0x84FA/*GL_UNSIGNED_INT_24_8*/;\t\t\t\theader.gltypesize = 4; break;\n\n#ifdef FTE_TARGET_WEB\n\tcase PTI_WHOLEFILE:\n#endif\n\tcase PTI_EMULATED:\n\tcase PTI_MAX:\n\t\treturn false;\n\n\tsafedefault:\n\t\treturn false;\n\t}\n\n\tif (strchr(filename, '*') || strchr(filename, ':'))\n\t\treturn false;\n\n\tfile = FS_OpenVFS(filename, \"wb\", fsroot);\n\tif (!file)\n\t\treturn false;\n\tVFS_WRITE(file, &header, sizeof(header));\n\n\tfor (mipnum = 0; mipnum < mips->mipcount; )\n\t{\n\t\tunsigned int sz;\n\t\t//translate to blocks\n\t\tunsigned int browbytes = bb * ((mips->mip[mipnum].width+bw-1)/bh);\n\t\tunsigned int padbytes = (browbytes&3)?4-(browbytes&3):0;\n\t\tunsigned int brows = (mips->mip[mipnum].height+bh-1)/bh;\n\t\tunsigned int blayers = (mips->mip[mipnum].depth+bd-1)/bd;\n\t\tif (mips->mip[mipnum].datasize != browbytes*brows*blayers)\n\t\t{\t//should probably be a sys_error\n\t\t\tCon_Printf(\"WriteKTX mip %u missized\\n\", (unsigned)mipnum);\n\t\t\tVFS_CLOSE(file);\n\t\t\treturn false;\n\t\t}\n\t\tswitch(mips->type)\n\t\t{\n\t\tcase PTI_ANY:\n\t\t\tVFS_CLOSE(file);\n\t\t\treturn false;\n\t\tcase PTI_CUBE:\t//special case, size is per-face\n\t\t\tsz = (browbytes+padbytes) * brows;\n\t\t\tbreak;\n\t\tcase PTI_2D:\n\t\tcase PTI_2D_ARRAY:\n\t\tcase PTI_CUBE_ARRAY:\n\t\tcase PTI_3D:\n\t\t\tsz = (browbytes+padbytes) * brows * blayers;\n\t\t\tbreak;\n\t\t}\n\t\tVFS_WRITE(file, &sz, 4);\n\t\tbrows *= blayers;\n\t\tif (padbytes)\n\t\t{\n\t\t\tunsigned int pad = 0, y;\n\t\t\tfor (y = 0; y < brows; y++)\n\t\t\t{\n\t\t\t\tVFS_WRITE(file, (qbyte*)mips->mip[mipnum].data + browbytes*y, browbytes);\n\t\t\t\tVFS_WRITE(file, &pad, 4-(browbytes&3));\n\t\t\t}\n\t\t}\n\t\telse\n\t\t\tVFS_WRITE(file, mips->mip[mipnum].data, browbytes*brows);\n\t\tmipnum++;\n\t}\n\n\tVFS_CLOSE(file);\n\treturn true;\n}\n\n#define LongSwap(i) (((i&0xff000000) >> 24)|((i&0x00ff0000) >> 8)|((i&0x0000ff00) << 8)|((i&0x000000ff) << 24))\n#define ShortSwap(i) (((i&0xff00) >> 8)|((i&0x00ff) << 8))\nstatic struct pendingtextureinfo *Image_ReadKTX1File(unsigned int flags, const char *fname, qbyte *filedata, size_t filesize)\n{\n\tstatic const char magic[12] = {0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31, 0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A};\n\tktxheader_t header;\n\tint nummips;\n\tint mipnum;\n\tint datasize;\n\tunsigned int *swap, w, h, d, f, l, browbytes,padbytes,y,x,rows;\n\tstruct pendingtextureinfo *mips;\n\tint encoding = TF_INVALID;\n\tconst qbyte *fileend = filedata + filesize;\n\n\tunsigned int blockwidth, blockheight, blockdepth, blockbytes;\n\n\tif (filesize < sizeof(ktxheader_t) || memcmp(filedata, magic, sizeof(magic)))\n\t\treturn NULL;\t//not a ktx file\n\n\theader = *(const ktxheader_t*)filedata;\n\tif (header.endianness == 0x01020304)\n\t{\t//swap the rest of the header.\n\t\tfor (swap = &header.endianness; swap < (unsigned int*)(&header+1); swap++)\n\t\t\t*swap = LongSwap(*swap);\n\t}\n\telse if (header.endianness != 0x04030201)\n\t\treturn NULL;\n\n\tnummips = header.numberofmipmaplevels;\n\tif (nummips < 1)\n\t\tnummips = 1;\n\n//\tif (header->numberofarrayelements != 0)\n//\t\treturn NULL;\t//don't support array textures\n\tif (header.numberoffaces == 1)\n\t\t;\t//non-cubemap\n\telse if (header.numberoffaces == 6)\n\t{\n\t\tif (header.numberofarrayelements != 0)\n\t\t\treturn NULL;\t//don't support array textures\n\n\t\tif (header.pixeldepth != 0)\n\t\t\treturn NULL;\n//\t\tif (header->numberofmipmaplevels != 1)\n//\t\t\treturn false;\t//only allow cubemaps that have no mips\n\t}\n\telse\n\t\treturn NULL;\t//don't allow weird cubemaps\n//\tif (header->pixeldepth && header->pixelwidth != header->pixeldepth && header->pixelheight != header->pixeldepth)\n//\t\treturn NULL;\t//we only support 3d textures where width+height+depth are the same. too lazy to change it now.\n\n\t/*FIXME: validate format+type for non-compressed formats*/\n\tswitch(header.glinternalformat)\n\t{\n\tcase 0x8D64/*GL_ETC1_RGB8_OES*/:\t\t\t\t\t\t\tencoding = PTI_ETC1_RGB8;\t\t\tbreak;\n\tcase 0x9270/*GL_COMPRESSED_R11_EAC*/:\t\t\t\t\t\tencoding = PTI_EAC_R11;\t\t\t\tbreak;\n\tcase 0x9271/*GL_COMPRESSED_SIGNED_R11_EAC*/:\t\t\t\tencoding = PTI_EAC_R11_SNORM;\t\tbreak;\n\tcase 0x9272/*GL_COMPRESSED_RG11_EAC*/:\t\t\t\t\t\tencoding = PTI_EAC_RG11;\t\t\tbreak;\n\tcase 0x9273/*GL_COMPRESSED_SIGNED_RG11_EAC*/:\t\t\t\tencoding = PTI_EAC_RG11_SNORM;\t\tbreak;\n\tcase 0x9274/*GL_COMPRESSED_RGB8_ETC2*/:\t\t\t\t\t\tencoding = PTI_ETC2_RGB8;\t\t\tbreak;\n\tcase 0x9275/*GL_COMPRESSED_SRGB8_ETC2*/:\t\t\t\t\tencoding = PTI_ETC2_RGB8_SRGB;\t\tbreak;\n\tcase 0x9276/*GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2*/:\tencoding = PTI_ETC2_RGB8A1;\t\t\tbreak;\n\tcase 0x9277/*GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2*/:encoding = PTI_ETC2_RGB8A1_SRGB;\tbreak;\n\tcase 0x9278/*GL_COMPRESSED_RGBA8_ETC2_EAC*/:\t\t\t\tencoding = PTI_ETC2_RGB8A8;\t\t\tbreak;\n\tcase 0x9279/*GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC*/:\t\t\tencoding = PTI_ETC2_RGB8A8_SRGB;\tbreak;\n\tcase 0x83F0/*GL_COMPRESSED_RGB_S3TC_DXT1_EXT*/:\t\t\t\tencoding = PTI_BC1_RGB;\t\t\t\tbreak;\n\tcase 0x8C4C/*GL_COMPRESSED_SRGB_S3TC_DXT1_EXT*/:\t\t\tencoding = PTI_BC1_RGB_SRGB;\t\tbreak;\n\tcase 0x83F1/*GL_COMPRESSED_RGBA_S3TC_DXT1_EXT*/:\t\t\tencoding = PTI_BC1_RGBA;\t\t\tbreak;\n\tcase 0x8C4D/*GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT*/:\t\tencoding = PTI_BC1_RGBA_SRGB;\t\tbreak;\n\tcase 0x83F2/*GL_COMPRESSED_RGBA_S3TC_DXT3_EXT*/:\t\t\tencoding = PTI_BC2_RGBA;\t\t\tbreak;\n\tcase 0x8C4E/*GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT*/:\t\tencoding = PTI_BC2_RGBA_SRGB;\t\tbreak;\n\tcase 0x83F3/*GL_COMPRESSED_RGBA_S3TC_DXT5_EXT*/:\t\t\tencoding = PTI_BC3_RGBA;\t\t\tbreak;\n\tcase 0x8C4F/*GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT*/:\t\tencoding = PTI_BC3_RGBA_SRGB;\t\tbreak;\n\tcase 0x8DBC/*GL_COMPRESSED_SIGNED_RED_RGTC1*/:\t\t\t\tencoding = PTI_BC4_R_SNORM;\t\t\tbreak;\n\tcase 0x8DBB/*GL_COMPRESSED_RED_RGTC1*/:\t\t\t\t\t\tencoding = PTI_BC4_R;\t\t\t\tbreak;\n\tcase 0x8DBE/*GL_COMPRESSED_SIGNED_RG_RGTC2*/:\t\t\t\tencoding = PTI_BC5_RG_SNORM;\t\tbreak;\n\tcase 0x8DBD/*GL_COMPRESSED_RG_RGTC2*/:\t\t\t\t\t\tencoding = PTI_BC5_RG;\t\t\t\tbreak;\n\tcase 0x8E8F/*GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB*/:\tencoding = PTI_BC6_RGB_UFLOAT;\t\tbreak;\n\tcase 0x8E8E/*GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB*/:\t\tencoding = PTI_BC6_RGB_SFLOAT;\t\tbreak;\n\tcase 0x8E8C/*GL_COMPRESSED_RGBA_BPTC_UNORM_ARB*/:\t\t\tencoding = PTI_BC7_RGBA;\t\t\tbreak;\n\tcase 0x8E8D/*GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB*/:\t\tencoding = PTI_BC7_RGBA_SRGB;\t\tbreak;\n\tcase 0x93B0/*GL_COMPRESSED_RGBA_ASTC_4x4_KHR*/:\t\t\t\tencoding = PTI_ASTC_4X4_LDR;\t\tbreak;\n\tcase 0x93B1/*GL_COMPRESSED_RGBA_ASTC_5x4_KHR*/:\t\t\t\tencoding = PTI_ASTC_5X4_LDR;\t\tbreak;\n\tcase 0x93B2/*GL_COMPRESSED_RGBA_ASTC_5x5_KHR*/:\t\t\t\tencoding = PTI_ASTC_5X5_LDR;\t\tbreak;\n\tcase 0x93B3/*GL_COMPRESSED_RGBA_ASTC_6x5_KHR*/:\t\t\t\tencoding = PTI_ASTC_6X5_LDR;\t\tbreak;\n\tcase 0x93B4/*GL_COMPRESSED_RGBA_ASTC_6x6_KHR*/:\t\t\t\tencoding = PTI_ASTC_6X6_LDR;\t\tbreak;\n\tcase 0x93B5/*GL_COMPRESSED_RGBA_ASTC_8x5_KHR*/:\t\t\t\tencoding = PTI_ASTC_8X5_LDR;\t\tbreak;\n\tcase 0x93B6/*GL_COMPRESSED_RGBA_ASTC_8x6_KHR*/:\t\t\t\tencoding = PTI_ASTC_8X6_LDR;\t\tbreak;\n\tcase 0x93B7/*GL_COMPRESSED_RGBA_ASTC_8x8_KHR*/:\t\t\t\tencoding = PTI_ASTC_8X8_LDR;\t\tbreak;\n\tcase 0x93B8/*GL_COMPRESSED_RGBA_ASTC_10x5_KHR*/:\t\t\tencoding = PTI_ASTC_10X5_LDR;\t\tbreak;\n\tcase 0x93B9/*GL_COMPRESSED_RGBA_ASTC_10x6_KHR*/:\t\t\tencoding = PTI_ASTC_10X6_LDR;\t\tbreak;\n\tcase 0x93BA/*GL_COMPRESSED_RGBA_ASTC_10x8_KHR*/:\t\t\tencoding = PTI_ASTC_10X8_LDR;\t\tbreak;\n\tcase 0x93BB/*GL_COMPRESSED_RGBA_ASTC_10x10_KHR*/:\t\t\tencoding = PTI_ASTC_10X10_LDR;\t\tbreak;\n\tcase 0x93BC/*GL_COMPRESSED_RGBA_ASTC_12x10_KHR*/:\t\t\tencoding = PTI_ASTC_12X10_LDR;\t\tbreak;\n\tcase 0x93BD/*GL_COMPRESSED_RGBA_ASTC_12x12_KHR*/:\t\t\tencoding = PTI_ASTC_12X12_LDR;\t\tbreak;\n\tcase 0x93D0/*GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR*/:\t\tencoding = PTI_ASTC_4X4_SRGB;\t\tbreak;\n\tcase 0x93D1/*GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR*/:\t\tencoding = PTI_ASTC_5X4_SRGB;\t\tbreak;\n\tcase 0x93D2/*GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR*/:\t\tencoding = PTI_ASTC_5X5_SRGB;\t\tbreak;\n\tcase 0x93D3/*GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR*/:\t\tencoding = PTI_ASTC_6X5_SRGB;\t\tbreak;\n\tcase 0x93D4/*GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR*/:\t\tencoding = PTI_ASTC_6X6_SRGB;\t\tbreak;\n\tcase 0x93D5/*GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR*/:\t\tencoding = PTI_ASTC_8X5_SRGB;\t\tbreak;\n\tcase 0x93D6/*GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR*/:\t\tencoding = PTI_ASTC_8X6_SRGB;\t\tbreak;\n\tcase 0x93D7/*GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR*/:\t\tencoding = PTI_ASTC_8X8_SRGB;\t\tbreak;\n\tcase 0x93D8/*GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR*/:\tencoding = PTI_ASTC_10X5_SRGB;\t\tbreak;\n\tcase 0x93D9/*GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR*/:\tencoding = PTI_ASTC_10X6_SRGB;\t\tbreak;\n\tcase 0x93DA/*GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR*/:\tencoding = PTI_ASTC_10X8_SRGB;\t\tbreak;\n\tcase 0x93DB/*GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR*/:\tencoding = PTI_ASTC_10X10_SRGB;\t\tbreak;\n\tcase 0x93DC/*GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR*/:\tencoding = PTI_ASTC_12X10_SRGB;\t\tbreak;\n\tcase 0x93DD/*GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR*/:\tencoding = PTI_ASTC_12X12_SRGB;\t\tbreak;\n\tcase 0x80E1/*GL_BGRA_EXT*/:\t\t\t\t\t\t\t\t\tencoding = PTI_BGRA8;\t\t\t\tbreak;\t//not even an internal format\n\tcase 0x1908/*GL_RGBA*/:\n\tcase 0x8058/*GL_RGBA8*/:\t\t\t\t\t\t\t\t\tencoding = (header.glformat==0x80E1/*GL_BGRA*/)?PTI_BGRA8:PTI_RGBA8;\t\t\tbreak;\t//unsized types shouldn't really be here\n\tcase 0x805B/*GL_RGBA16*/:\t\t\t\t\t\t\t\t\tencoding = PTI_RGBA16;\t\t\t\tbreak;\n\tcase 0x8C43/*GL_SRGB8_ALPHA8*/:\t\t\t\t\t\t\t\tencoding = (header.glformat==0x80E1/*GL_BGRA*/)?PTI_BGRA8_SRGB:PTI_RGBA8_SRGB;\tbreak;\n\tcase 0x8040/*GL_LUMINANCE8*/:\t\t\t\t\t\t\t\tencoding = PTI_L8;\t\t\t\t\tbreak;\n\tcase 0x8045/*GL_LUMINANCE8_ALPHA8*/:\t\t\t\t\t\tencoding = PTI_L8A8;\t\t\t\tbreak;\n\tcase 0x881A/*GL_RGBA16F_ARB*/:\t\t\t\t\t\t\t\tencoding = PTI_RGBA16F;\t\t\t\tbreak;\n\tcase 0x8815/*GL_RGB32F_ARB*/:\t\t\t\t\t\t\t\tencoding = PTI_RGB32F;\t\t\t\tbreak;\n\tcase 0x8814/*GL_RGBA32F_ARB*/:\t\t\t\t\t\t\t\tencoding = PTI_RGBA32F;\t\t\t\tbreak;\n\tcase 0x8059/*GL_RGB10_A2*/:\t\t\t\t\t\t\t\t\tencoding = PTI_A2BGR10;\t\t\t\tbreak;\n\tcase 0x8229/*GL_R8*/:\t\t\t\t\t\t\t\t\t\tencoding = PTI_R8;\t\t\t\t\tbreak;\n\tcase 0x822A/*GL_R16*/:\t\t\t\t\t\t\t\t\t\tencoding = PTI_R16;\t\t\t\t\tbreak;\n\tcase 0x822B/*GL_RG8*/:\t\t\t\t\t\t\t\t\t\tencoding = PTI_RG8;\t\t\t\t\tbreak;\n\tcase 0x8F94/*GL_R8_SNORM*/:\t\t\t\t\t\t\t\t\tencoding = PTI_R8_SNORM;\t\t\tbreak;\n\tcase 0x8F95/*GL_RG8_SNORM*/:\t\t\t\t\t\t\t\tencoding = PTI_RG8_SNORM;\t\t\tbreak;\n\tcase 0x81A5/*GL_DEPTH_COMPONENT16*/:\t\t\t\t\t\tencoding = PTI_DEPTH16;\t\t\t\tbreak;\n\tcase 0x81A6/*GL_DEPTH_COMPONENT24*/:\t\t\t\t\t\tencoding = PTI_DEPTH24;\t\t\t\tbreak;\n\tcase 0x81A7/*GL_DEPTH_COMPONENT32*/:\t\t\t\t\t\tencoding = PTI_DEPTH32;\t\t\t\tbreak;\n\tcase 0x88F0/*GL_DEPTH24_STENCIL8*/:\t\t\t\t\t\t\tencoding = PTI_DEPTH24_8;\t\t\tbreak;\n\tcase 0x822D/*GL_R16F*/:\t\t\t\t\t\t\t\t\t\tencoding = PTI_R16F;\t\t\t\tbreak;\n\tcase 0x822E/*GL_R32F*/:\t\t\t\t\t\t\t\t\t\tencoding = PTI_R32F;\t\t\t\tbreak;\n\n\tcase 0x8C40/*GL_SRGB*/:\n\tcase 0x8C41/*GL_SRGB8*/:\n\t\tif (header.glformat==0x80E1/*GL_BGRA*/)\n\t\t\tencoding = PTI_BGRX8_SRGB;\n\t\telse if (header.glformat==0x1908/*GL_RGBA*/)\n\t\t\tencoding = PTI_RGBX8_SRGB;\n\t\tbreak;\n\n\tcase 0x1907/*GL_RGB*/:\t//invalid sized format. treat as GL_RGB8, and do weird checks.\n\tcase 0x8051/*GL_RGB8*/:\t//other sized RGB formats are treated based upon the data format rather than the sized format, in case they were meant to be converted by the driver...\n\tcase 0x8C3D/*GL_RGB9_E5*/:\n\tcase 0x8D62/*GL_RGB565*/:\n\tcase 0x8C3A/*GL_R11F_G11F_B10F*/:\n\t\tif (header.glformat == 0x80E0/*GL_BGR*/)\n\t\t\tencoding = PTI_BGR8;\n\t\telse if (header.glformat == 0x80E1/*GL_BGRA*/)\n\t\t\tencoding = PTI_BGRX8;\n\t\telse if (header.glformat == 0x1907/*GL_RGB*/)\n\t\t{\n\t\t\tif (header.gltype == 0x8C3B/*GL_UNSIGNED_INT_10F_11F_11F_REV*/)\n\t\t\t\tencoding = PTI_B10G11R11F;\n\t\t\telse if (header.gltype == 0x8C3E/*GL_UNSIGNED_INT_5_9_9_9_REV*/)\n\t\t\t\tencoding = PTI_E5BGR9;\n\t\t\telse if (header.gltype == 0x8363/*GL_UNSIGNED_SHORT_5_6_5*/)\n\t\t\t\tencoding = PTI_RGB565;\n\t\t\telse\n\t\t\t\tencoding = PTI_RGB8;\n\t\t}\n\t\telse if (header.glformat == 0x1908/*GL_RGBA*/)\n\t\t\tencoding = PTI_RGBX8;\n\t\telse\n\t\t\tencoding = PTI_RGB8;\n\t\tbreak;\n\tcase 0x8056/*GL_RGBA4*/:\n\tcase 0x8057/*GL_RGB5_A1*/:\n\t\tif (header.glformat == 0x1908/*GL_RGBA*/ && header.gltype == 0x8034/*GL_UNSIGNED_SHORT_5_5_5_1*/)\n\t\t\tencoding = PTI_RGBA5551;\n\t\telse if (header.glformat == 0x80E1/*GL_BGRA*/ && header.gltype == 0x8366/*GL_UNSIGNED_SHORT_1_5_5_5_REV*/)\n\t\t\tencoding = PTI_ARGB1555;\n\t\telse if (header.glformat == 0x1908/*GL_RGBA*/ && header.gltype == 0x8033/*GL_UNSIGNED_SHORT_4_4_4_4*/)\n\t\t\tencoding = PTI_RGBA4444;\n\t\telse if (header.glformat == 0x80E1/*GL_BGRA*/ && header.gltype == 0x8365/*GL_UNSIGNED_SHORT_4_4_4_4_REV*/)\n\t\t\tencoding = PTI_ARGB4444;\n\t\tbreak;\n\n\tdefault:\n\t\tencoding = TF_INVALID;\n\t\tbreak;\n\t}\n\tif (encoding == TF_INVALID)\n\t{\n\t\tCon_Printf(\"Unsupported ktx internalformat %x in %s\\n\", header.glinternalformat, fname);\n\t\treturn NULL;\n\t}\n\n//\tif (!sh_config.texfmt[encoding])\n//\t{\n//\t\tCon_Printf(\"KTX %s: encoding %x not supported on this system\\n\", fname, header->glinternalformat);\n//\t\treturn false;\n//\t}\n\n\tmips = Z_Malloc(sizeof(*mips));\n\tmips->mipcount = 0;\n\tif (header.pixeldepth)\n\t\tmips->type = PTI_3D;\n\telse if (header.numberoffaces==6)\n\t{\n\t\tif (header.numberofarrayelements)\n\t\t{\n\t\t\theader.pixeldepth = header.numberofarrayelements*6;\n\t\t\tmips->type = PTI_CUBE_ARRAY;\n\t\t}\n\t\telse\n\t\t\tmips->type = PTI_CUBE;\n\t}\n\telse\n\t{\n\t\tif (header.numberofarrayelements)\n\t\t{\n\t\t\theader.pixeldepth = header.numberofarrayelements;\n\t\t\tmips->type = PTI_2D_ARRAY;\n\t\t}\n\t\telse\n\t\t{\n\t\t\theader.pixeldepth = 1;\n\t\t\tmips->type = PTI_2D;\n\t\t}\n\t}\n\tmips->extrafree = filedata;\n\tmips->encoding = encoding;\n\n\tfiledata += sizeof(header);\t\t\t//skip the header...\n\tfiledata += header.bytesofkeyvaluedata;\t//skip the keyvalue stuff\n\n\tif (nummips * header.numberoffaces > countof(mips->mip))\n\t\tnummips = countof(mips->mip) / header.numberoffaces;\n\n\tImage_BlockSizeForEncoding(encoding, &blockbytes, &blockwidth, &blockheight, &blockdepth);\n\n\tw = header.pixelwidth;\n\th = max(1, header.pixelheight);\n\td = max(1, header.pixeldepth);\n\tf = max(1, header.numberoffaces);\n\tl = max(1, header.numberofarrayelements);\n\n\tfor (mipnum = 0; mipnum < nummips; mipnum++)\n\t{\n\t\tdatasize = *(int*)filedata;\n\t\tfiledata += 4;\n\n\t\tif (header.endianness == 0x01020304)\n\t\t\tdatasize = LongSwap(datasize);\n\n\t\tbrowbytes = blockbytes * ((w+blockwidth-1)/blockwidth);\n\t\tpadbytes = (browbytes & 3)?4-(browbytes&3):0;\n\t\trows = ((h+blockheight-1)/blockheight)*\n\t\t\t   ((d+blockdepth-1)/blockdepth);\n\t\tif (datasize != (browbytes+padbytes) * rows)\n\t\t{\n\t\t\tCon_Printf(\"%s: mip %i does not match expected size (%u, required %u)\\n\", fname, mipnum, datasize, (browbytes+padbytes) * rows);\n\t\t\tbreak;\n\t\t}\n\n\t\tif (filedata + datasize*f*l > fileend)\n\t\t{\n\t\t\tCon_Printf(\"%s: truncation at mip %i\\n\", fname, mipnum);\n\t\t\tbreak;\n\t\t}\n\n\t\tif (mips->mipcount >= countof(mips->mip))\n\t\t\tbreak;\n\t\tmips->mip[mips->mipcount].width = w;\n\t\tmips->mip[mips->mipcount].height = h;\n\t\tmips->mip[mips->mipcount].depth = d*l*f;\n\n\t\tif (padbytes || header.endianness == 0x01020304)\n\t\t{\t//gah.\n\t\t\t//the ktx format is 4-byte aligned. our internal representation is tightly packed (consistent with everything but gl).\n\t\t\t//in the case of byteswapping, any data types should work out okay (no misaligned stuff).\n\t\t\trows *= l*f;\n\t\t\tmips->mip[mips->mipcount].needfree = true;\n\t\t\tmips->mip[mips->mipcount].datasize = browbytes * rows;\n\t\t\tmips->mip[mips->mipcount].data = BZ_Malloc(mips->mip[mips->mipcount].datasize);\n\t\t\tif (header.gltypesize == 4 && header.endianness == 0x01020304)\n\t\t\t{\n\t\t\t\tfor (y = 0; y < rows; y++)\n\t\t\t\t\tfor (x = 0; x < browbytes>>2; x++)\n\t\t\t\t\t\t((int*)((qbyte*)mips->mip[mips->mipcount].data + y*browbytes))[x] = LongSwap(((int*)filedata + y*browbytes+padbytes)[x]);\n\t\t\t}\n\t\t\telse if (header.gltypesize == 2 && header.endianness == 0x01020304)\n\t\t\t{\n\t\t\t\tfor (y = 0; y < rows; y++)\n\t\t\t\t\tfor (x = 0; x < browbytes>>1; x++)\n\t\t\t\t\t\t((short*)((qbyte*)mips->mip[mips->mipcount].data + y*browbytes))[x] = ShortSwap(((short*)filedata + y*browbytes+padbytes)[x]);\n\t\t\t}\n\t\t\telse\n\t\t\t{\t//erk, panic...\n\t\t\t\tfor (y = 0; y < rows; y++)\n\t\t\t\t\tmemcpy((qbyte*)mips->mip[mips->mipcount].data + y*browbytes, filedata + y*browbytes+padbytes, browbytes);\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tmips->mip[mips->mipcount].datasize = datasize * l*f;\n\t\t\tmips->mip[mips->mipcount].data = filedata;\n\t\t}\n\t\tmips->mipcount++;\n\n\t\tfiledata += datasize *l*f;\n\n\t\tw = max(1, w>>1);\n\t\th = max(1, h>>1);\n\t\tif (mips->type == PTI_3D)\n\t\t\td = max(1, d>>1);\n\t}\n\n\tif (!mips->mipcount)\n\t{\n\t\tZ_Free(mips);\n\t\treturn NULL;\n\t}\n\n#ifdef ASTC_WITH_HDRTEST\n\tif (encoding >= PTI_ASTC_4X4_LDR && encoding < PTI_ASTC_4X4_SRGB)\n\t{\n\t\tint face;\n\t\tfor (face = 0; face < header.numberoffaces; face++)\n\t\t{\n\t\t\tif (ASTC_BlocksAreHDR(mips->mip[face].data, mips->mip[face].datasize, blockwidth, blockheight, 1))\n\t\t\t{\t//convert it to one of the hdr formats if we can.\n\t\t\t\tmips->encoding = PTI_ASTC_4X4_HDR+(encoding-PTI_ASTC_4X4_LDR);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n#endif\n\n\treturn mips;\n}\n\ntypedef struct\n{\n\tchar magic[12];\n\tquint32_t vkFormat;\n\tquint32_t typesize;\n\tquint32_t pixelwidth;\n\tquint32_t pixelheight;\n\tquint32_t pixeldepth;\n\tquint32_t layercount;\n\tquint32_t facecount;\n\tquint32_t levelcount;\n\tquint32_t compressionscheme;\n\n\tquint32_t dfdoffset;\n\tquint32_t dfdsize;\n\tquint32_t kvdoffset;\n\tquint32_t kvdsize;\n\tquint64_t sgdoffset;\n\tquint64_t sgdsize;\n} ktx2header_t;\ntypedef struct\n{\n\tquint64_t offset;\n\tquint64_t compsize;\n\tquint64_t rawsize;\n} ktx2lavelheader_t;\nstatic struct pendingtextureinfo *Image_ReadKTX2File(unsigned int flags, const char *fname, qbyte *filedata, size_t filesize)\n{\n\tstatic const char magic[12] = {0xAB, 0x4B, 0x54, 0x58, 0x20, 0x32, 0x30, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A};\n\tktx2header_t header;\n\tconst ktx2lavelheader_t *levelheader;\n\tint mipnum;\n\tunsigned int w, h, d;\n\tstruct pendingtextureinfo *mips;\n\tint encoding = TF_INVALID, itype;\n\n\tunsigned int bw, bh, bd, bb;\n\n\tif (filesize < sizeof(ktxheader_t) || memcmp(filedata, magic, sizeof(magic)))\n\t\treturn NULL;\t//not a ktx file\n\n\theader = *(const ktx2header_t*)filedata;\n\tlevelheader = (const ktx2lavelheader_t*)((const ktx2header_t*)filedata+1);\n\n\theader.vkFormat\t\t\t= LittleLong(header.vkFormat);\n\theader.typesize\t\t\t= LittleLong(header.typesize);\n\theader.pixelwidth\t\t= LittleLong(header.pixelwidth);\n\theader.pixelheight\t\t= LittleLong(header.pixelheight);\n\theader.pixeldepth\t\t= LittleLong(header.pixeldepth);\n\theader.layercount\t\t= LittleLong(header.layercount);\n\theader.facecount\t\t= LittleLong(header.facecount);\n\theader.levelcount\t\t= LittleLong(header.levelcount);\n\theader.compressionscheme= LittleLong(header.compressionscheme);\n\theader.dfdoffset\t\t= LittleLong(header.dfdoffset);\n\theader.dfdsize\t\t\t= LittleLong(header.dfdsize);\n\theader.kvdoffset\t\t= LittleLong(header.kvdoffset);\n\theader.kvdsize\t\t\t= LittleLong(header.kvdsize);\n\theader.sgdoffset\t\t= LittleI64(header.sgdoffset);\n\theader.sgdsize\t\t\t= LittleI64(header.sgdsize);\n\n\tif (!header.pixelheight)\n\t\theader.pixelheight = 1;\t//we don't support 1D textures. force it to 2d.\n\tif (header.pixeldepth)\n\t{\n\t\tif (header.layercount || header.facecount!=1)\n\t\t\treturn NULL;\t//neither 3d arrays nor 3d cubes are supported, nor do they really make sense.\n\t\theader.layercount = 1;\n\t\titype = PTI_3D;\n\t}\n\telse\n\t{\n\t\theader.pixeldepth = 1;\n\n\t\tif (header.facecount==6)\n\t\t{\t//cube...\n\t\t\tif (header.layercount)\n\t\t\t\titype = PTI_CUBE_ARRAY;\n\t\t\telse itype = PTI_CUBE, header.layercount=1;\n\t\t}\n\t\telse if (header.facecount == 1)\n\t\t{\t//boring 2d\n\t\t\tif (header.layercount)\n\t\t\t\titype = PTI_2D_ARRAY;\n\t\t\telse itype = PTI_2D, header.layercount=1;\n\t\t}\n\t\telse\n\t\t\treturn NULL;\t//not allowed\n\t}\n\n\tw = header.pixelwidth;\n\th = header.pixelheight;\n\td = header.pixeldepth*header.facecount*header.layercount;\n\n\tif (!header.levelcount)\n\t\theader.levelcount = 1;\t//means we must auto-generate the mip pyramid. should warn if texflags doesn't match.\n\n\tswitch (header.compressionscheme)\n\t{\n\tcase 0:\t//no compression\n\t\tbreak;\n\tcase 1: //basis... w/e\n\tcase 2:\t//zstd... we have no decompression lib\n\tcase 3: //zlib... we probably have zlib! but the docs imply zlib yet states raw deflate (so requires a passing the windowsize to zlib's inflateInit2 as negative, which is non-obvious).\n\tdefault:\n\t\treturn NULL;\t//we don't support this junk. gzip it. you'll get better compression than doing it per-level.\n\t}\n\n\t/*FIXME: validate format+type for non-compressed formats*/\n\tswitch(header.vkFormat)\n\t{\n//\tcase 1/*VK_FORMAT_R4G4_UNORM_PACK8*/:\t\t\t\tencoding = PTI_RG4;\t\t\tbreak;\n\tcase 2/*VK_FORMAT_R4G4B4A4_UNORM_PACK16*/:\t\t\tencoding = PTI_RGBA4444;\tbreak;\n//\tcase 3/*VK_FORMAT_B4G4R4A4_UNORM_PACK16*/:\t\t\tencoding = PTI_BGRA4444;\tbreak;\n\tcase 4/*VK_FORMAT_R5G6B5_UNORM_PACK16*/:\t\t\tencoding = PTI_RGB565;\t\tbreak;\n//\tcase 5/*VK_FORMAT_B5G6R5_UNORM_PACK16*/:\t\t\tencoding = PTI_BGR565;\t\tbreak;\n\tcase 6/*VK_FORMAT_R5G5B5A1_UNORM_PACK16*/:\t\t\tencoding = PTI_RGBA5551;\tbreak;\n//\tcase 7/*VK_FORMAT_B5G5R5A1_UNORM_PACK16*/:\t\t\tencoding = PTI_BGRA5551;\tbreak;\n\tcase 8/*VK_FORMAT_A1R5G5B5_UNORM_PACK16*/:\t\t\tencoding = PTI_ARGB1555;\tbreak;\n\tcase 9/*VK_FORMAT_R8_UNORM*/:\t\t\t\t\t\tencoding = PTI_R8;\t\t\tbreak;\n\tcase 10/*VK_FORMAT_R8_SNORM*/:\t\t\t\t\t\tencoding = PTI_R8_SNORM;\tbreak;\n//\tcase 11/*VK_FORMAT_R8_USCALED*/:\n//\tcase 12/*VK_FORMAT_R8_SSCALED*/:\n//\tcase 13/*VK_FORMAT_R8_UINT*/:\n//\tcase 14/*VK_FORMAT_R8_SINT*/:\n\tcase 15/*VK_FORMAT_R8_SRGB*/:\t\t\t\t\t\tencoding = PTI_L8_SRGB;\t\tbreak;\t//erk\n\tcase 16/*VK_FORMAT_R8G8_UNORM*/:\t\t\t\t\tencoding = PTI_RG8;\t\t\tbreak;\n\tcase 17/*VK_FORMAT_R8G8_SNORM*/:\t\t\t\t\tencoding = PTI_RG8_SNORM;\tbreak;\n//\tcase 18/*VK_FORMAT_R8G8_USCALED*/:\n//\tcase 19/*VK_FORMAT_R8G8_SSCALED*/:\n//\tcase 20/*VK_FORMAT_R8G8_UINT*/:\n//\tcase 21/*VK_FORMAT_R8G8_SINT*/:\n//\tcase 22/*VK_FORMAT_R8G8_SRGB*/:\n\tcase 23/*VK_FORMAT_R8G8B8_UNORM*/:\t\t\t\t\tencoding = PTI_RGB8;\t\tbreak;\n//\tcase 24/*VK_FORMAT_R8G8B8_SNORM*/:\n//\tcase 25/*VK_FORMAT_R8G8B8_USCALED*/:\n//\tcase 26/*VK_FORMAT_R8G8B8_SSCALED*/:\n//\tcase 27/*VK_FORMAT_R8G8B8_UINT*/:\n//\tcase 28/*VK_FORMAT_R8G8B8_SINT*/:\n\tcase 29/*VK_FORMAT_R8G8B8_SRGB*/:\t\t\t\t\tencoding = PTI_RGB8_SRGB;\tbreak;\n\tcase 30/*VK_FORMAT_B8G8R8_UNORM*/:\t\t\t\t\tencoding = PTI_BGR8;\t\tbreak;\n//\tcase 31/*VK_FORMAT_B8G8R8_SNORM*/:\n//\tcase 32/*VK_FORMAT_B8G8R8_USCALED*/:\n//\tcase 33/*VK_FORMAT_B8G8R8_SSCALED*/:\n//\tcase 34/*VK_FORMAT_B8G8R8_UINT*/:\n//\tcase 35/*VK_FORMAT_B8G8R8_SINT*/:\n\tcase 36/*VK_FORMAT_B8G8R8_SRGB*/:\t\t\t\t\tencoding = PTI_BGR8_SRGB;\tbreak;\n\tcase 37/*VK_FORMAT_R8G8B8A8_UNORM*/:\t\t\t\tencoding = PTI_RGBA8;\t\tbreak;\n//\tcase 38/*VK_FORMAT_R8G8B8A8_SNORM*/:\n//\tcase 39/*VK_FORMAT_R8G8B8A8_USCALED*/:\n//\tcase 40/*VK_FORMAT_R8G8B8A8_SSCALED*/:\n//\tcase 41/*VK_FORMAT_R8G8B8A8_UINT*/:\n//\tcase 42/*VK_FORMAT_R8G8B8A8_SINT*/:\n\tcase 43/*VK_FORMAT_R8G8B8A8_SRGB*/:\t\t\t\t\tencoding = PTI_RGBA8_SRGB;\tbreak;\n\tcase 44/*VK_FORMAT_B8G8R8A8_UNORM*/:\t\t\t\tencoding = PTI_BGRA8;\t\tbreak;\n//\tcase 45/*VK_FORMAT_B8G8R8A8_SNORM*/:\n//\tcase 46/*VK_FORMAT_B8G8R8A8_USCALED*/:\n//\tcase 47/*VK_FORMAT_B8G8R8A8_SSCALED*/:\n//\tcase 48/*VK_FORMAT_B8G8R8A8_UINT*/:\n//\tcase 49/*VK_FORMAT_B8G8R8A8_SINT*/:\n\tcase 50/*VK_FORMAT_B8G8R8A8_SRGB*/:\t\t\t\t\tencoding = PTI_BGRA8_SRGB;\tbreak;\n//\tcase 51/*VK_FORMAT_A8B8G8R8_UNORM_PACK32*/:\n//\tcase 52/*VK_FORMAT_A8B8G8R8_SNORM_PACK32*/:\n//\tcase 53/*VK_FORMAT_A8B8G8R8_USCALED_PACK32*/:\n//\tcase 54/*VK_FORMAT_A8B8G8R8_SSCALED_PACK32*/:\n//\tcase 55/*VK_FORMAT_A8B8G8R8_UINT_PACK32*/:\n//\tcase 56/*VK_FORMAT_A8B8G8R8_SINT_PACK32*/:\n//\tcase 57/*VK_FORMAT_A8B8G8R8_SRGB_PACK32*/:\n//\tcase 58/*VK_FORMAT_A2R10G10B10_UNORM_PACK32*/:\n//\tcase 59/*VK_FORMAT_A2R10G10B10_SNORM_PACK32*/:\n//\tcase 60/*VK_FORMAT_A2R10G10B10_USCALED_PACK32*/:\n//\tcase 61/*VK_FORMAT_A2R10G10B10_SSCALED_PACK32*/:\n//\tcase 62/*VK_FORMAT_A2R10G10B10_UINT_PACK32*/:\n//\tcase 63/*VK_FORMAT_A2R10G10B10_SINT_PACK32*/:\n\tcase 64/*VK_FORMAT_A2B10G10R10_UNORM_PACK32*/:\t\tencoding = PTI_A2BGR10;\t\tbreak;\n//\tcase 65/*VK_FORMAT_A2B10G10R10_SNORM_PACK32*/:\n//\tcase 66/*VK_FORMAT_A2B10G10R10_USCALED_PACK32*/:\n//\tcase 67/*VK_FORMAT_A2B10G10R10_SSCALED_PACK32*/:\n//\tcase 68/*VK_FORMAT_A2B10G10R10_UINT_PACK32*/:\n//\tcase 69/*VK_FORMAT_A2B10G10R10_SINT_PACK32*/:\n\tcase 70/*VK_FORMAT_R16_UNORM*/:\t\t\t\t\t\tencoding = PTI_R16;\t\t\tbreak;\n//\tcase 71/*VK_FORMAT_R16_SNORM*/:\n//\tcase 72/*VK_FORMAT_R16_USCALED*/:\n//\tcase 73/*VK_FORMAT_R16_SSCALED*/:\n//\tcase 74/*VK_FORMAT_R16_UINT*/:\n//\tcase 75/*VK_FORMAT_R16_SINT*/:\n\tcase 76/*VK_FORMAT_R16_SFLOAT*/:\t\t\t\t\tencoding = PTI_R16F;\t\tbreak;\n//\tcase 77/*VK_FORMAT_R16G16_UNORM*/:\n//\tcase 78/*VK_FORMAT_R16G16_SNORM*/:\n//\tcase 79/*VK_FORMAT_R16G16_USCALED*/:\n//\tcase 80/*VK_FORMAT_R16G16_SSCALED*/:\n//\tcase 81/*VK_FORMAT_R16G16_UINT*/:\n//\tcase 82/*VK_FORMAT_R16G16_SINT*/:\n//\tcase 83/*VK_FORMAT_R16G16_SFLOAT*/:\n//\tcase 84/*VK_FORMAT_R16G16B16_UNORM*/:\n//\tcase 85/*VK_FORMAT_R16G16B16_SNORM*/:\n//\tcase 86/*VK_FORMAT_R16G16B16_USCALED*/:\n//\tcase 87/*VK_FORMAT_R16G16B16_SSCALED*/:\n//\tcase 88/*VK_FORMAT_R16G16B16_UINT*/:\n//\tcase 89/*VK_FORMAT_R16G16B16_SINT*/:\n\tcase 90/*VK_FORMAT_R16G16B16_SFLOAT*/:\t\t\t\tencoding = PTI_RGB32F;\t\tbreak;\n\tcase 91/*VK_FORMAT_R16G16B16A16_UNORM*/:\t\t\tencoding = PTI_RGBA16;\t\tbreak;\n//\tcase 92/*VK_FORMAT_R16G16B16A16_SNORM*/:\n//\tcase 93/*VK_FORMAT_R16G16B16A16_USCALED*/:\n//\tcase 94/*VK_FORMAT_R16G16B16A16_SSCALED*/:\n//\tcase 95/*VK_FORMAT_R16G16B16A16_UINT*/:\n//\tcase 96/*VK_FORMAT_R16G16B16A16_SINT*/:\n\tcase 97/*VK_FORMAT_R16G16B16A16_SFLOAT*/:\t\t\tencoding = PTI_RGBA16F;\t\tbreak;\n//\tcase 98/*VK_FORMAT_R32_UINT*/:\n//\tcase 99/*VK_FORMAT_R32_SINT*/:\n\tcase 100/*VK_FORMAT_R32_SFLOAT*/:\t\t\t\t\tencoding = PTI_R32F;\t\tbreak;\n//\tcase 101/*VK_FORMAT_R32G32_UINT*/:\n//\tcase 102/*VK_FORMAT_R32G32_SINT*/:\n//\tcase 103/*VK_FORMAT_R32G32_SFLOAT*/:\n//\tcase 104/*VK_FORMAT_R32G32B32_UINT*/:\n//\tcase 105/*VK_FORMAT_R32G32B32_SINT*/:\n\tcase 106/*VK_FORMAT_R32G32B32_SFLOAT*/:\t\t\t\tencoding = PTI_RGB32F;\t\tbreak;\n//\tcase 107/*VK_FORMAT_R32G32B32A32_UINT*/:\n//\tcase 108/*VK_FORMAT_R32G32B32A32_SINT*/:\n\tcase 109/*VK_FORMAT_R32G32B32A32_SFLOAT*/:\t\t\tencoding = PTI_RGBA32F;\t\tbreak;\n//\tcase 110/*VK_FORMAT_R64_UINT*/:\n//\tcase 111/*VK_FORMAT_R64_SINT*/:\n//\tcase 112/*VK_FORMAT_R64_SFLOAT*/:\n//\tcase 113/*VK_FORMAT_R64G64_UINT*/:\n//\tcase 114/*VK_FORMAT_R64G64_SINT*/:\n//\tcase 115/*VK_FORMAT_R64G64_SFLOAT*/:\n//\tcase 116/*VK_FORMAT_R64G64B64_UINT*/:\n//\tcase 117/*VK_FORMAT_R64G64B64_SINT*/:\n//\tcase 118/*VK_FORMAT_R64G64B64_SFLOAT*/:\n//\tcase 119/*VK_FORMAT_R64G64B64A64_UINT*/:\n//\tcase 120/*VK_FORMAT_R64G64B64A64_SINT*/:\n//\tcase 121/*VK_FORMAT_R64G64B64A64_SFLOAT*/:\n\tcase 122/*VK_FORMAT_B10G11R11_UFLOAT_PACK32*/:\t\tencoding = PTI_B10G11R11F;\tbreak;\n\tcase 123/*VK_FORMAT_E5B9G9R9_UFLOAT_PACK32*/:\t\tencoding = PTI_E5BGR9;\t\tbreak;\n\t//case 124/*VK_FORMAT_D16_UNORM*/:\t\t\t\t\tencoding = PTI_DEPTH16;\t\tbreak;\n\t//case 125/*VK_FORMAT_X8_D24_UNORM_PACK32*/:\t\tencoding = PTI_DEPTH24;\t\tbreak;\n\t//case 126/*VK_FORMAT_D32_SFLOAT*/:\t\t\t\t\tencoding = PTI_DEPTH32;\t\tbreak;\n//\tcase 127/*VK_FORMAT_S8_UINT*/:\n//\tcase 128/*VK_FORMAT_D16_UNORM_S8_UINT*/:\n\t//case 129/*VK_FORMAT_D24_UNORM_S8_UINT*/:\t\t\tencoding = PTI_DEPTH24_8;\tbreak;\n//\tcase 130/*VK_FORMAT_D32_SFLOAT_S8_UINT*/:\n\tcase 131/*VK_FORMAT_BC1_RGB_UNORM_BLOCK*/:\t\t\tencoding = PTI_BC1_RGB;\t\t\tbreak;\n\tcase 132/*VK_FORMAT_BC1_RGB_SRGB_BLOCK*/:\t\t\tencoding = PTI_BC1_RGB_SRGB;\tbreak;\n\tcase 133/*VK_FORMAT_BC1_RGBA_UNORM_BLOCK*/:\t\t\tencoding = PTI_BC1_RGBA;\t\tbreak;\n\tcase 134/*VK_FORMAT_BC1_RGBA_SRGB_BLOCK*/:\t\t\tencoding = PTI_BC1_RGBA_SRGB;\tbreak;\n\tcase 135/*VK_FORMAT_BC2_UNORM_BLOCK*/:\t\t\t\tencoding = PTI_BC2_RGBA;\t\tbreak;\n\tcase 136/*VK_FORMAT_BC2_SRGB_BLOCK*/:\t\t\t\tencoding = PTI_BC2_RGBA_SRGB;\tbreak;\n\tcase 137/*VK_FORMAT_BC3_UNORM_BLOCK*/:\t\t\t\tencoding = PTI_BC3_RGBA;\t\tbreak;\n\tcase 138/*VK_FORMAT_BC3_SRGB_BLOCK*/:\t\t\t\tencoding = PTI_BC1_RGBA_SRGB;\tbreak;\n\tcase 139/*VK_FORMAT_BC4_UNORM_BLOCK*/:\t\t\t\tencoding = PTI_BC4_R;\t\t\tbreak;\n\tcase 140/*VK_FORMAT_BC4_SNORM_BLOCK*/:\t\t\t\tencoding = PTI_BC4_R_SNORM;\t\tbreak;\n\tcase 141/*VK_FORMAT_BC5_UNORM_BLOCK*/:\t\t\t\tencoding = PTI_BC5_RG;\t\t\tbreak;\n\tcase 142/*VK_FORMAT_BC5_SNORM_BLOCK*/:\t\t\t\tencoding = PTI_BC5_RG_SNORM;\tbreak;\n\tcase 143/*VK_FORMAT_BC6H_UFLOAT_BLOCK*/:\t\t\tencoding = PTI_BC6_RGB_UFLOAT;\tbreak;\n\tcase 144/*VK_FORMAT_BC6H_SFLOAT_BLOCK*/:\t\t\tencoding = PTI_BC6_RGB_SFLOAT;\tbreak;\n\tcase 145/*VK_FORMAT_BC7_UNORM_BLOCK*/:\t\t\t\tencoding = PTI_BC7_RGBA;\t\tbreak;\n\tcase 146/*VK_FORMAT_BC7_SRGB_BLOCK*/:\t\t\t\tencoding = PTI_BC7_RGBA_SRGB;\tbreak;\n\tcase 147/*VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK*/:\t\tencoding = PTI_ETC2_RGB8;\t\tbreak;\n\tcase 148/*VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK*/:\t\tencoding = PTI_ETC2_RGB8_SRGB;\tbreak;\n\tcase 149/*VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK*/:\tencoding = PTI_ETC2_RGB8A1;\t\tbreak;\n\tcase 150/*VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK*/:\t\tencoding = PTI_ETC2_RGB8A1_SRGB;break;\n\tcase 151/*VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK*/:\tencoding = PTI_ETC2_RGB8A8;\t\tbreak;\n\tcase 152/*VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK*/:\t\tencoding = PTI_ETC2_RGB8A8_SRGB;break;\n\tcase 153/*VK_FORMAT_EAC_R11_UNORM_BLOCK*/:\t\t\tencoding = PTI_EAC_R11;\t\t\tbreak;\n\tcase 154/*VK_FORMAT_EAC_R11_SNORM_BLOCK*/:\t\t\tencoding = PTI_EAC_R11_SNORM;\tbreak;\n\tcase 155/*VK_FORMAT_EAC_R11G11_UNORM_BLOCK*/:\t\tencoding = PTI_EAC_RG11;\t\tbreak;\n\tcase 156/*VK_FORMAT_EAC_R11G11_SNORM_BLOCK*/:\t\tencoding = PTI_EAC_RG11_SNORM;\tbreak;\n\tcase 157/*VK_FORMAT_ASTC_4x4_UNORM_BLOCK*/:\t\t\tencoding = PTI_ASTC_4X4_LDR;\tbreak;\n\tcase 158/*VK_FORMAT_ASTC_4x4_SRGB_BLOCK*/:\t\t\tencoding = PTI_ASTC_4X4_SRGB;\tbreak;\n\tcase 159/*VK_FORMAT_ASTC_5x4_UNORM_BLOCK*/:\t\t\tencoding = PTI_ASTC_5X4_LDR;\tbreak;\n\tcase 160/*VK_FORMAT_ASTC_5x4_SRGB_BLOCK*/:\t\t\tencoding = PTI_ASTC_5X4_SRGB;\tbreak;\n\tcase 161/*VK_FORMAT_ASTC_5x5_UNORM_BLOCK*/:\t\t\tencoding = PTI_ASTC_5X5_LDR;\tbreak;\n\tcase 162/*VK_FORMAT_ASTC_5x5_SRGB_BLOCK*/:\t\t\tencoding = PTI_ASTC_5X5_SRGB;\tbreak;\n\tcase 163/*VK_FORMAT_ASTC_6x5_UNORM_BLOCK*/:\t\t\tencoding = PTI_ASTC_6X5_LDR;\tbreak;\n\tcase 164/*VK_FORMAT_ASTC_6x5_SRGB_BLOCK*/:\t\t\tencoding = PTI_ASTC_6X5_SRGB;\tbreak;\n\tcase 165/*VK_FORMAT_ASTC_6x6_UNORM_BLOCK*/:\t\t\tencoding = PTI_ASTC_6X6_LDR;\tbreak;\n\tcase 166/*VK_FORMAT_ASTC_6x6_SRGB_BLOCK*/:\t\t\tencoding = PTI_ASTC_6X6_SRGB;\tbreak;\n\tcase 167/*VK_FORMAT_ASTC_8x5_UNORM_BLOCK*/:\t\t\tencoding = PTI_ASTC_8X5_LDR;\tbreak;\n\tcase 168/*VK_FORMAT_ASTC_8x5_SRGB_BLOCK*/:\t\t\tencoding = PTI_ASTC_8X5_SRGB;\tbreak;\n\tcase 169/*VK_FORMAT_ASTC_8x6_UNORM_BLOCK*/:\t\t\tencoding = PTI_ASTC_8X6_LDR;\tbreak;\n\tcase 170/*VK_FORMAT_ASTC_8x6_SRGB_BLOCK*/:\t\t\tencoding = PTI_ASTC_8X6_SRGB;\tbreak;\n\tcase 171/*VK_FORMAT_ASTC_8x8_UNORM_BLOCK*/:\t\t\tencoding = PTI_ASTC_8X8_LDR;\tbreak;\n\tcase 172/*VK_FORMAT_ASTC_8x8_SRGB_BLOCK*/:\t\t\tencoding = PTI_ASTC_8X8_SRGB;\tbreak;\n\tcase 173/*VK_FORMAT_ASTC_10x5_UNORM_BLOCK*/:\t\tencoding = PTI_ASTC_10X5_LDR;\tbreak;\n\tcase 174/*VK_FORMAT_ASTC_10x5_SRGB_BLOCK*/:\t\t\tencoding = PTI_ASTC_10X5_SRGB;\tbreak;\n\tcase 175/*VK_FORMAT_ASTC_10x6_UNORM_BLOCK*/:\t\tencoding = PTI_ASTC_10X6_LDR;\tbreak;\n\tcase 176/*VK_FORMAT_ASTC_10x6_SRGB_BLOCK*/:\t\t\tencoding = PTI_ASTC_10X6_SRGB;\tbreak;\n\tcase 177/*VK_FORMAT_ASTC_10x8_UNORM_BLOCK*/:\t\tencoding = PTI_ASTC_10X8_LDR;\tbreak;\n\tcase 178/*VK_FORMAT_ASTC_10x8_SRGB_BLOCK*/:\t\t\tencoding = PTI_ASTC_10X8_SRGB;\tbreak;\n\tcase 179/*VK_FORMAT_ASTC_10x10_UNORM_BLOCK*/:\t\tencoding = PTI_ASTC_10X10_LDR;\tbreak;\n\tcase 180/*VK_FORMAT_ASTC_10x10_SRGB_BLOCK*/:\t\tencoding = PTI_ASTC_10X10_SRGB;\tbreak;\n\tcase 181/*VK_FORMAT_ASTC_12x10_UNORM_BLOCK*/:\t\tencoding = PTI_ASTC_12X10_LDR;\tbreak;\n\tcase 182/*VK_FORMAT_ASTC_12x10_SRGB_BLOCK*/:\t\tencoding = PTI_ASTC_12X10_SRGB;\tbreak;\n\tcase 183/*VK_FORMAT_ASTC_12x12_UNORM_BLOCK*/:\t\tencoding = PTI_ASTC_12X12_LDR;\tbreak;\n\tcase 184/*VK_FORMAT_ASTC_12x12_SRGB_BLOCK*/:\t\tencoding = PTI_ASTC_12X12_SRGB;\tbreak;\n\n\tcase 0/*VK_FORMAT_UNDEFINED*/:\n\tdefault:\n\t\tencoding = PTI_INVALID;\n\t\tbreak;\n\t}\n\tif (encoding == PTI_INVALID)\n\t{\n\t\t//TODO: we might be able to make sense of these by decoding the DFD.\n\t\tCon_Printf(CON_WARNING\"%s: Unsupported ktx2 vkformat %x\\n\", fname, header.vkFormat);\n\t\treturn NULL;\n\t}\n\n\tif (header.kvdsize)\n\t{\n\t\tsize_t kvd = header.kvdoffset;\n\t\tconst qbyte *key;\t//utf-8.\n\t\tconst qbyte *val;\t//often utf-8, but might be binary.\n\t\tsize_t kvdend = kvd+header.kvdsize;\n//\t\tVALGRIND_MAKE_MEM_UNDEFINED(filedata+kvdend, 1);\n\t\twhile(kvd+4 <= kvdend)\n\t\t{\n\t\t\tquint32_t len = (filedata[kvd+0]<<0)|(filedata[kvd+1]<<8)|(filedata[kvd+2]<<16)|(filedata[kvd+3]<<24), klen;\n\t\t\tquint32_t vlen;\n\t\t\tkvd+=4;\n\t\t\tif (kvd+len > kvdend)\n\t\t\t\tbreak;\t//some sort of error\n\t\t\tfor(klen = 0;;)\n\t\t\t{\n\t\t\t\tif (!filedata[kvd+klen++])\n\t\t\t\t\tbreak;\n\t\t\t\tif (klen >= len)\n\t\t\t\t{\n\t\t\t\t\tCon_Printf(CON_WARNING\"%s: unterminated kvd key\\n\", fname);\n\t\t\t\t\treturn NULL; //we NEED a null for it to be valid.\n\t\t\t\t}\n\t\t\t}\n\t\t\tkey = filedata+kvd;\n\t\t\tval = filedata+kvd+klen;\n\t\t\tvlen = len-klen;\n\t\t\tif (!strcmp(key, \"KTXwriter\"))\n\t\t\t\t;\n\t\t\telse if (!strcmp(key, \"KTXcubemapIncomplete\"))\n\t\t\t{\n\t\t\t\tCon_Printf(CON_WARNING\"%s: incomplete cubemaps are not supported\\n\", fname);\n\t\t\t\treturn NULL;\t//would be seen as a 2darray.\n\t\t\t}\n\t\t\telse if (!strcmp(key, \"KTXorientation\"))\n\t\t\t{\n\t\t\t\tif (vlen >= 2 && val[0] && val[1] == 'u')\n\t\t\t\t\tCon_Printf(\"%s: warning: image is bottom up\\n\", fname);\n\t\t\t}\n\t\t\telse if (!strcmp(key, \"KTXglFormat\"))\n\t\t\t\t/*uninteresting, we're not loading it directly*/;\n\t\t\telse if (!strcmp(key, \"KTXdxgiFormat__\"))\t//why do the docs say the trailing underscores?\n\t\t\t\t/*uninteresting, we're not loading it directly*/;\n\t\t\telse if (!strcmp(key, \"KTXmetalPixelFormat\"))\n\t\t\t\t/*uninteresting, we're not loading it directly*/;\n\t\t\telse if (!strcmp(key, \"KTXswizzle\"))\n\t\t\t{\n\t\t\t\tif (encoding == PTI_R8 && vlen >= 5 && !strcmp(val, \"rrr1\"))\n\t\t\t\t\tencoding = PTI_L8;\n//\t\t\t\telse if (encoding == PTI_R8_SRGB && vlen >= 5 && !strcmp(val, \"rrr1\"))\n//\t\t\t\t\tencoding = PTI_L8_SRGB;\n\t\t\t\telse if (encoding == PTI_RG8 && vlen >= 5 && !strcmp(val, \"rrrg\"))\n\t\t\t\t\tencoding = PTI_L8A8;\n\t\t\t\telse\n\t\t\t\t\tCon_Printf(\"%s: unsupported swizzle: %s\\n\", fname, val);\n\t\t\t}\n\t\t\telse if (!strcmp(key, \"KTXwriterScParams\"))\n\t\t\t\t;\n\t\t\telse if (!strcmp(key, \"KTXastcDecodeMode\"))\n\t\t\t\t;\n\t\t\telse if (!strcmp(key, \"KTXanimData\"))\n\t\t\t\t/*uint32_t duration, timescale, loopcount*/;\n\t\t\telse\n\t\t\t\tCon_Printf(\"%s: unhandled kvd: %s\\n\", fname, key);\n\t\t\tkvd+=(len+3)&~3;\t//padding... strangely the start offset does not 'need' to be aligned. weird.\n\t\t}\n\t\tif (kvd != kvdend)\n\t\t\tCon_Printf(CON_WARNING\"%s: misparsed kvd data\\n\", fname);\n//\t\tVALGRIND_MAKE_MEM_DEFINED_IF_ADDRESSABLE(filedata+kvdend, 1);\n\t}\n\n\tImage_BlockSizeForEncoding(encoding, &bb, &bw, &bh, &bd);\n\n\tmips = Z_Malloc(sizeof(*mips));\n\tmips->encoding = encoding;\n\tmips->type = itype;\n\tmips->mipcount = 0;\n\tmips->extrafree = filedata;\n\tmips->encoding = encoding;\n\n\tif (header.levelcount > countof(mips->mip))\n\t\theader.levelcount = countof(mips->mip);\n\n\tfor (mipnum = 0; mipnum < header.levelcount; mipnum++, levelheader++)\n\t{\n\t\tsize_t ofs = LittleI64(levelheader->offset);\n\t\tqbyte *src = filedata + ofs;\n\t\tsize_t csz = LittleI64(levelheader->compsize);\n\t\tsize_t rsz = LittleI64(levelheader->rawsize);\n\t\tsize_t needsize =\t((w+bw-1)/bw)*\n\t\t\t\t\t\t\t((h+bh-1)/bh)*\n\t\t\t\t\t\t\t((d+bd-1)/bd)*\n\t\t\t\t\t\t\tbb;\n\t\tif (rsz != needsize)\n\t\t{\n\t\t\tCon_Printf(CON_WARNING\"%s: mip %i does not match expected size (%u, required %u)\\n\", fname, mipnum, (unsigned int)rsz, (unsigned int)needsize);\n\t\t\tbreak;\n\t\t}\n\t\tif (ofs+csz > filesize || csz > filesize || ofs+csz < ofs)\n\t\t{\n\t\t\tCon_Printf(CON_WARNING\"%s: truncation at mip %i\\n\", fname, mipnum);\n\t\t\tbreak;\n\t\t}\n\t\tif (rsz != csz)\n\t\t{\n\t\t\tCon_Printf(CON_WARNING\"%s: compression size mismatch\\n\", fname);\n\t\t\tbreak;\n\t\t}\n\n\t\tif (mips->mipcount >= countof(mips->mip))\n\t\t\tbreak;\n\n\t\tmips->mip[mips->mipcount].width = w;\n\t\tmips->mip[mips->mipcount].height = h;\n\t\tmips->mip[mips->mipcount].depth = d;\n\t\tmips->mip[mips->mipcount].datasize = rsz;\n\t\tmips->mip[mips->mipcount].data = src;\n#ifndef FTE_LITTLE_ENDIAN\n\t\tswitch(header.typesize)\n\t\t{\n\t\tcase 1:\n\t\t\tbreak;\n\t\tcase 4:\n\t\t\t{\n\t\t\t\tquint32_t *in = mips->mip[mips->mipcount].data;\n\t\t\t\tquint32_t *out = mips->mip[mips->mipcount].data = BZ_Malloc(rsz);\n\t\t\t\tmips->mip[mips->mipcount].needfree = true;\n\t\t\t\tfor (csz = 0; csz < rsz; csz+=4)\n\t\t\t\t\t*out++ = LittleLong(*in++);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 2:\n\t\t\t{\n\t\t\t\tquint16_t *in = mips->mip[mips->mipcount].data;\n\t\t\t\tquint16_t *out = mips->mip[mips->mipcount].data = BZ_Malloc(rsz);\n\t\t\t\tmips->mip[mips->mipcount].needfree = true;\n\t\t\t\tfor (csz = 0; csz < rsz; csz+=2)\n\t\t\t\t\t*out++ = LittleShort(*in++);\n\t\t\t}\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tCon_Printf(CON_WARNING\"%s: unsupported type size.\\n\", fname);\n\t\t\tZ_Free(mips);\n\t\t\treturn NULL;\n\t\t}\n#endif\n\t\tmips->mipcount++;\n\n\t\tw = max(1, w>>1);\n\t\th = max(1, h>>1);\n\t\tif (mips->type == PTI_3D)\n\t\t\td = max(1, d>>1);\n\t}\n\n\tif (!mips->mipcount)\n\t{\n\t\tZ_Free(mips);\n\t\treturn NULL;\n\t}\n\n#ifdef ASTC_WITH_HDRTEST\n\tif (encoding >= PTI_ASTC_4X4_LDR && encoding < PTI_ASTC_4X4_SRGB)\n\t{\t//assumption: if any levels are hdr then level0 will contain such a block. might be nicer to start mid-way though, for less blocks.\n\t\tif (ASTC_BlocksAreHDR(mips->mip[0].data, mips->mip[0].datasize, bw, bh, bd))\n\t\t{\t//convert it to one of the hdr formats if we can.\n\t\t\tmips->encoding = PTI_ASTC_4X4_HDR+(encoding-PTI_ASTC_4X4_LDR);\n\t\t}\n\t}\n#endif\n\n\treturn mips;\n}\nstatic struct pendingtextureinfo *Image_ReadKTXFile(unsigned int flags, const char *fname, qbyte *filedata, size_t filesize)\n{\n\tif (filesize >= 12)\n\t{\n\t\tif (filedata[0] == 0xAB && filedata[1] == 0x4B && filedata[2] == 0x54 && filedata[3] == 0x58 && filedata[4] == 0x20)\n\t\t{\n\t\t\tif (filedata[5] == '2')\n\t\t\t\treturn Image_ReadKTX2File(flags, fname, filedata, filesize);\n\t\t\telse\n\t\t\t\treturn Image_ReadKTX1File(flags, fname, filedata, filesize);\n\t\t}\n\t}\n\treturn NULL;\t//not enough size for the header.\n}\n#endif\n\n#ifdef IMAGEFMT_ASTC\nstatic struct pendingtextureinfo *Image_ReadASTCFile(unsigned int flags, const char *fname, qbyte *filedata, size_t filesize)\n{\n\tstruct pendingtextureinfo *mips;\n\tint encoding = PTI_INVALID, blockbytes, blockwidth, blockheight, blockdepth;\n\tstatic const struct {\n\t\tint w, h, d;\n\t\tint fmt;\n\t} sizes[] =\n\t{\n\t\t{4,4,1,PTI_ASTC_4X4_LDR},\n\t\t{5,4,1,PTI_ASTC_5X4_LDR},\n\t\t{5,5,1,PTI_ASTC_5X5_LDR},\n\t\t{6,5,1,PTI_ASTC_6X5_LDR},\n\t\t{6,6,1,PTI_ASTC_6X6_LDR},\n\t\t{8,5,1,PTI_ASTC_8X5_LDR},\n\t\t{8,6,1,PTI_ASTC_8X6_LDR},\n\t\t{10,5,1,PTI_ASTC_10X5_LDR},\n\t\t{10,6,1,PTI_ASTC_10X6_LDR},\n\t\t{8,8,1,PTI_ASTC_8X8_LDR},\n\t\t{10,8,1,PTI_ASTC_10X8_LDR},\n\t\t{10,10,1,PTI_ASTC_10X10_LDR},\n\t\t{12,10,1,PTI_ASTC_12X10_LDR},\n\t\t{12,12,1,PTI_ASTC_12X12_LDR},\n#ifdef ASTC3D\n\t\t{3,3,3,PTI_ASTC_3X3X3_LDR},\n\t\t{4,3,3,PTI_ASTC_4X3X3_LDR},\n\t\t{4,4,3,PTI_ASTC_4X4X3_LDR},\n\t\t{4,4,4,PTI_ASTC_4X4X4_LDR},\n\t\t{5,4,4,PTI_ASTC_5X4X4_LDR},\n\t\t{5,5,4,PTI_ASTC_5X5X4_LDR},\n\t\t{5,5,5,PTI_ASTC_5X5X5_LDR},\n\t\t{6,5,5,PTI_ASTC_6X5X5_LDR},\n\t\t{6,6,5,PTI_ASTC_6X6X5_LDR},\n\t\t{6,6,6,PTI_ASTC_6X6X6_LDR},\n#endif\n\t};\n\tint i;\n\tint size[3] = {\n\t\tfiledata[7]|(filedata[8]<<8)|(filedata[9]<<16),\n\t\tfiledata[10]|(filedata[11]<<8)|(filedata[12]<<16),\n\t\tfiledata[13]|(filedata[14]<<8)|(filedata[15]<<16)};\n\tfor (i = 0; i < countof(sizes); i++)\n\t{\n\t\tif (sizes[i].w == filedata[4] && sizes[i].h == filedata[5] && sizes[i].d == filedata[6])\n\t\t{\n\t\t\tencoding = sizes[i].fmt;\n\t\t\tbreak;\n\t\t}\n\t}\n\tif (!encoding)\n\t\treturn NULL;\t//block size not known\n\tImage_BlockSizeForEncoding(encoding, &blockbytes, &blockwidth, &blockheight, &blockdepth);\n\tif (16+blockbytes*((size[0]+blockwidth-1)/blockwidth)*((size[1]+blockheight-1)/blockheight)*((size[2]+blockdepth-1)/blockdepth) != filesize)\n\t\treturn NULL;\t//err, not the right size!\n\n\tmips = Z_Malloc(sizeof(*mips));\n\tmips->mipcount = 1;\t//this format doesn't support mipmaps. so there's only one level.\n\tmips->type = PTI_2D;\n\tmips->extrafree = filedata;\n\tmips->encoding = encoding;\n\tmips->mip[0].data = filedata+16;\n\tmips->mip[0].datasize = filesize-16;\n\tmips->mip[0].width = size[0];\n\tmips->mip[0].height = size[1];\n\tmips->mip[0].depth = size[2];\n\tmips->mip[0].needfree = false;\n\n#ifdef ASTC_WITH_HDRTEST\n\tif (ASTC_BlocksAreHDR(mips->mip[0].data, mips->mip[0].datasize, blockwidth, blockheight, 1))\n\t{\t//convert it to one of the hdr formats if we can.\n\t\tmips->encoding = PTI_ASTC_4X4_HDR+(encoding-PTI_ASTC_4X4_LDR);\n\t}\n#endif\n\treturn mips;\n}\n#endif\n\n#ifdef IMAGEFMT_PKM\nstatic struct pendingtextureinfo *Image_ReadPKMFile(unsigned int flags, const char *fname, qbyte *filedata, size_t filesize)\n{\n\tstruct pendingtextureinfo *mips;\n\tunsigned int encoding, blockbytes, blockwidth, blockheight, blockdepth;\n\tunsigned short ver, dfmt;\n\tunsigned short datawidth, dataheight;\n\tunsigned short imgwidth, imgheight;\n\tif (filesize < 16 || filedata[0] != 'P' || filedata[1] != 'K' || filedata[2] != 'M' || filedata[3] != ' ')\n\t\treturn NULL;\n\tver = (filedata[4]<<8) | filedata[5];\n\tdfmt = (filedata[6]<<8) | filedata[7];\n\tdatawidth = (filedata[8]<<8) | filedata[9];\n\tdataheight = (filedata[10]<<8) | filedata[11];\n\timgwidth = (filedata[12]<<8) | filedata[13];\n\timgheight = (filedata[14]<<8) | filedata[15];\n\tif (((imgwidth+3)&~3)!=datawidth || ((imgheight+3)&~3)!=dataheight)\n\t\treturn NULL;\t//these are all 4*4 blocks.\n\tif (ver == ((1<<8)|0) && ver == ((2<<8)|0))\n\t{\n\t\tif (dfmt == 0)\t//should only be in v1\n\t\t\tencoding = PTI_ETC1_RGB8;\n\t\t//following should only be in v2, but we don't care.\n\t\telse if (dfmt == 1)\n\t\t\tencoding = PTI_ETC2_RGB8;\n\t\telse if (dfmt == 2)\n\t\t\treturn NULL;\t//'old' rgba8 format that's not supported.\n\t\telse if (dfmt == 3)\n\t\t\tencoding = PTI_ETC2_RGB8A8;\n\t\telse if (dfmt == 4)\n\t\t\tencoding = PTI_ETC2_RGB8A1;\n\t\telse if (dfmt == 5)\n\t\t\tencoding = PTI_EAC_R11;\n\t\telse if (dfmt == 6)\n\t\t\tencoding = PTI_EAC_RG11;\n\t\telse if (dfmt == 7)\n\t\t\tencoding = PTI_EAC_R11_SNORM;\n\t\telse if (dfmt == 8)\n\t\t\tencoding = PTI_EAC_RG11_SNORM;\n\t\telse if (dfmt == 9)\n\t\t\tencoding = PTI_ETC2_RGB8_SRGB;\t//srgb\n\t\telse if (dfmt == 10)\n\t\t\tencoding = PTI_ETC2_RGB8A8_SRGB;\t//srgb\n\t\telse if (dfmt == 11)\n\t\t\tencoding = PTI_ETC2_RGB8A1_SRGB;\t//srgb\n\t\telse\n\t\t\treturn NULL;\t//unknown/unsupported\n\t}\n\telse\n\t\treturn NULL;\n\n\tImage_BlockSizeForEncoding(encoding, &blockbytes, &blockwidth, &blockheight, &blockdepth);\n\tif (16+((datawidth+blockwidth-1)/blockwidth)*((dataheight+blockheight-1)/blockheight)*blockbytes != filesize)\n\t\treturn NULL;\t//err, not the right size!\n\n\tmips = Z_Malloc(sizeof(*mips));\n\tmips->mipcount = 1;\t//this format doesn't support mipmaps. so there's only one level.\n\tmips->type = PTI_2D;\n\tmips->extrafree = filedata;\n\tmips->encoding = encoding;\n\tmips->mip[0].data = filedata+16;\n\tmips->mip[0].datasize = filesize-16;\n\tmips->mip[0].width = imgwidth;\n\tmips->mip[0].height = imgheight;\n\tmips->mip[0].depth = 1;\n\tmips->mip[0].needfree = false;\n\treturn mips;\n}\n#endif\n\n#ifdef IMAGEFMT_DDS\ntypedef struct {\n\tunsigned int dwSize;\n\tunsigned int dwFlags;\n\tunsigned int dwFourCC;\n\n\tunsigned int bitcount;\n\tunsigned int redmask;\n\tunsigned int greenmask;\n\tunsigned int bluemask;\n\tunsigned int alphamask;\n} ddspixelformat_t;\n\ntypedef struct {\n\tunsigned int dwSize;\n\tunsigned int dwFlags;\n\tunsigned int dwHeight;\n\tunsigned int dwWidth;\n\tunsigned int dwPitchOrLinearSize;\n\tunsigned int dwDepth;\n\tunsigned int dwMipMapCount;\n\tunsigned int dwReserved1[11];\n\tddspixelformat_t ddpfPixelFormat;\n\tunsigned int ddsCaps[4];\n\tunsigned int dwReserved2;\n} ddsheader_t;\ntypedef struct {\n\tunsigned int dxgiformat;\n\tunsigned int resourcetype; //0=unknown, 1=buffer, 2=1d, 3=2d, 4=3d\n\tunsigned int miscflag;\t//singular... yeah. 4=cubemap.\n\tunsigned int arraysize;\n\tunsigned int miscflags2;\n} dds10header_t;\n\nstatic struct pendingtextureinfo *Image_ReadDDSFile(unsigned int flags, const char *fname, qbyte *filedata, size_t filesize)\n{\n\tint nummips;\n\tint mipnum;\n\tint datasize;\n\tunsigned int w, h, d;\n\tunsigned int blockwidth, blockheight, blockdepth, blockbytes;\n\tstruct pendingtextureinfo *mips;\n\tint encoding;\n\tint layers = 1, layer;\n\tint ttype;\n\n\tddsheader_t fmtheader;\n\tdds10header_t fmt10header;\n\tqbyte *fileend = filedata + filesize;\n\n\tif (filesize < sizeof(fmtheader) || *(int*)filedata != (('D'<<0)|('D'<<8)|('S'<<16)|(' '<<24)))\n\t\treturn NULL;\n\n\tmemcpy(&fmtheader, filedata+4, sizeof(fmtheader));\n\tif (fmtheader.dwSize != sizeof(fmtheader))\n\t\treturn NULL;\t//corrupt/different version\n\tfmtheader.dwSize += 4;\n\tmemset(&fmt10header, 0, sizeof(fmt10header));\n\n\tfmt10header.arraysize = (fmtheader.ddsCaps[1] & 0x200)?6:1; //cubemaps need 6 faces...\n\n\tnummips = fmtheader.dwMipMapCount;\n\tif (nummips < 1)\n\t\tnummips = 1;\n\tif (nummips > countof(mips->mip))\n\t\treturn NULL;\n\n\tif (!(fmtheader.ddpfPixelFormat.dwFlags & 4))\n\t{\n#define IsPacked(bits,r,g,b,a)\tfmtheader.ddpfPixelFormat.bitcount==bits&&fmtheader.ddpfPixelFormat.redmask==r&&fmtheader.ddpfPixelFormat.greenmask==g&&fmtheader.ddpfPixelFormat.bluemask==b&&fmtheader.ddpfPixelFormat.alphamask==a\n\t\tif (IsPacked(24, 0xff0000, 0x00ff00, 0x0000ff, 0))\n\t\t\tencoding = PTI_BGR8;\n\t\telse if (IsPacked(24, 0x000000, 0x00ff00, 0xff0000, 0))\n\t\t\tencoding = PTI_RGB8;\n\t\telse if (IsPacked(32, 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000))\n\t\t\tencoding = PTI_BGRA8;\n\t\telse if (IsPacked(32, 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000))\n\t\t\tencoding = PTI_RGBA8;\n\t\telse if (IsPacked(32, 0x00ff0000, 0x0000ff00, 0x000000ff, 0))\n\t\t\tencoding = PTI_BGRX8;\n\t\telse if (IsPacked(32, 0x000000ff, 0x0000ff00, 0x00ff0000, 0))\n\t\t\tencoding = PTI_RGBX8;\n\t\telse if (IsPacked(32, 0x000003ff, 0x000ffc00, 0x3ff00000, 0xc0000000))\n\t\t\tencoding = PTI_A2BGR10;\n\t\telse if (IsPacked(16, 0xf800, 0x07e0, 0x001f, 0))\n\t\t\tencoding = PTI_RGB565;\n\t\telse if (IsPacked(16, 0xf800, 0x07c0, 0x003e, 0x0001))\n\t\t\tencoding = PTI_RGBA5551;\n\t\telse if (IsPacked(16, 0x7c00, 0x03e0, 0x001f, 0x8000))\n\t\t\tencoding = PTI_ARGB1555;\n\t\telse if (IsPacked(16, 0xf000, 0x0f00, 0x00f0, 0x000f))\n\t\t\tencoding = PTI_RGBA4444;\n\t\telse if (IsPacked(16, 0x0f00, 0x00f0, 0x000f, 0xf000))\n\t\t\tencoding = PTI_ARGB4444;\n\t\telse if (IsPacked( 8, 0x000000ff, 0x00000000, 0x00000000, 0x00000000))\n\t\t\tencoding = (fmtheader.ddpfPixelFormat.dwFlags&0x20000)?PTI_L8:PTI_R8;\n\t\telse if (IsPacked(16, 0x000000ff, 0x00000000, 0x00000000, 0x0000ff00))\n\t\t\tencoding = PTI_L8A8;\n\t\telse\n\t\t{\n\t\t\tCon_Printf(\"Unsupported non-fourcc dds in %s\\n\", fname);\n\t\t\tCon_Printf(\" bits: %u\\n\", fmtheader.ddpfPixelFormat.bitcount);\n\t\t\tCon_Printf(\"  red: %08x\\n\", fmtheader.ddpfPixelFormat.redmask);\n\t\t\tCon_Printf(\"green: %08x\\n\", fmtheader.ddpfPixelFormat.greenmask);\n\t\t\tCon_Printf(\" blue: %08x\\n\", fmtheader.ddpfPixelFormat.bluemask);\n\t\t\tCon_Printf(\"alpha: %08x\\n\", fmtheader.ddpfPixelFormat.alphamask);\n\t\t\tCon_Printf(\" used: %08x\\n\", fmtheader.ddpfPixelFormat.redmask^fmtheader.ddpfPixelFormat.greenmask^fmtheader.ddpfPixelFormat.bluemask^fmtheader.ddpfPixelFormat.alphamask);\n\t\t\treturn NULL;\n\t\t}\n#undef IsPacked\n\t}\n\telse if (*(int*)&fmtheader.ddpfPixelFormat.dwFourCC == (('D'<<0)|('X'<<8)|('T'<<16)|('1'<<24)))\n\t\tencoding = PTI_BC1_RGBA;\t//alpha or not? Assume yes, and let the drivers decide.\n\telse if (*(int*)&fmtheader.ddpfPixelFormat.dwFourCC == (('D'<<0)|('X'<<8)|('T'<<16)|('2'<<24)))\t//dx3 with premultiplied alpha\n\t{\n//\t\tif (!(tex->flags & IF_PREMULTIPLYALPHA))\n//\t\t\treturn false;\n\t\tencoding = PTI_BC2_RGBA;\n\t}\n\telse if (*(int*)&fmtheader.ddpfPixelFormat.dwFourCC == (('D'<<0)|('X'<<8)|('T'<<16)|('3'<<24)))\n\t{\n//\t\tif (tex->flags & IF_PREMULTIPLYALPHA)\n//\t\t\treturn false;\n\t\tencoding = PTI_BC2_RGBA;\n\t}\n\telse if (*(int*)&fmtheader.ddpfPixelFormat.dwFourCC == (('D'<<0)|('X'<<8)|('T'<<16)|('4'<<24)))\t//dx5 with premultiplied alpha\n\t{\n//\t\tif (!(tex->flags & IF_PREMULTIPLYALPHA))\n//\t\t\treturn false;\n\t\tencoding = PTI_BC3_RGBA;\n\t}\n\telse if (*(int*)&fmtheader.ddpfPixelFormat.dwFourCC == (('D'<<0)|('X'<<8)|('T'<<16)|('5'<<24)))\n\t{\n//\t\tif (tex->flags & IF_PREMULTIPLYALPHA)\n//\t\t\treturn false;\n\t\tencoding = PTI_BC3_RGBA;\n\t}\n\telse if (*(int*)&fmtheader.ddpfPixelFormat.dwFourCC == (('A'<<0)|('T'<<8)|('I'<<16)|('1'<<24))\n\t\t||   *(int*)&fmtheader.ddpfPixelFormat.dwFourCC == (('B'<<0)|('C'<<8)|('4'<<16)|('U'<<24)))\n\t\tencoding = PTI_BC4_R;\n\telse if (*(int*)&fmtheader.ddpfPixelFormat.dwFourCC == (('A'<<0)|('T'<<8)|('I'<<16)|('2'<<24))\n\t\t||   *(int*)&fmtheader.ddpfPixelFormat.dwFourCC == (('B'<<0)|('C'<<8)|('5'<<16)|('U'<<24)))\n\t\tencoding = PTI_BC5_RG;\n\telse if (*(int*)&fmtheader.ddpfPixelFormat.dwFourCC == (('B'<<0)|('C'<<8)|('4'<<16)|('S'<<24)))\n\t\tencoding = PTI_BC4_R_SNORM;\n\telse if (*(int*)&fmtheader.ddpfPixelFormat.dwFourCC == (('B'<<0)|('C'<<8)|('5'<<16)|('S'<<24)))\n\t\tencoding = PTI_BC5_RG_SNORM;\n\telse if (*(int*)&fmtheader.ddpfPixelFormat.dwFourCC == (('E'<<0)|('T'<<8)|('C'<<16)|('2'<<24)))\n\t\tencoding = PTI_ETC2_RGB8;\n\telse if (*(int*)&fmtheader.ddpfPixelFormat.dwFourCC == (('D'<<0)|('X'<<8)|('1'<<16)|('0'<<24)))\n\t{\n\t\t//this has some weird extra header with dxgi format types.\n\t\tmemcpy(&fmt10header, filedata+fmtheader.dwSize, sizeof(fmt10header));\n\t\tfmtheader.dwSize += sizeof(fmt10header);\n\t\tswitch(fmt10header.dxgiformat)\n\t\t{\n\t\tcase 0x0/*DXGI_FORMAT_UNKNOWN*/:\t\t\t\tencoding = PTI_INVALID;\t\t\tbreak;\n\t\tcase 0x1/*DXGI_FORMAT_R32G32B32A32_TYPELESS*/:\tencoding = PTI_INVALID;\t\t\tbreak;\n\t\tcase 0x2/*DXGI_FORMAT_R32G32B32A32_FLOAT*/:\t\tencoding = PTI_RGBA32F;\t\t\tbreak;\n//\t\tcase 0x3/*DXGI_FORMAT_R32G32B32A32_UINT*/:\t\tencoding = PTI_INVALID;\t\t\tbreak;\n//\t\tcase 0x4/*DXGI_FORMAT_R32G32B32A32_SINT*/:\t\tencoding = PTI_INVALID;\t\t\tbreak;\n//\t\tcase 0x5/*DXGI_FORMAT_R32G32B32_TYPELESS*/:\t\tencoding = PTI_INVALID;\t\t\tbreak;\n\t\tcase 0x6/*DXGI_FORMAT_R32G32B32_FLOAT*/:\t\tencoding = PTI_RGB32F;\t\t\tbreak;\n//\t\tcase 0x7/*DXGI_FORMAT_R32G32B32_UINT*/:\t\t\tencoding = PTI_INVALID;\t\t\tbreak;\n//\t\tcase 0x8/*DXGI_FORMAT_R32G32B32_SINT*/:\t\t\tencoding = PTI_INVALID;\t\t\tbreak;\n//\t\tcase 0x9/*DXGI_FORMAT_R16G16B16A16_TYPELESS*/:\tencoding = PTI_INVALID;\t\t\tbreak;\n\t\tcase 0xa/*DXGI_FORMAT_R16G16B16A16_FLOAT*/:\t\tencoding = PTI_RGBA16F;\t\t\tbreak;\n\t\tcase 0xb/*DXGI_FORMAT_R16G16B16A16_UNORM*/:\t\tencoding = PTI_RGBA16;\t\t\tbreak;\n//\t\tcase 0xc/*DXGI_FORMAT_R16G16B16A16_UINT*/:\t\tencoding = PTI_INVALID;\t\t\tbreak;\n//\t\tcase 0xd/*DXGI_FORMAT_R16G16B16A16_SNORM*/:\t\tencoding = PTI_INVALID;\t\t\tbreak;\n//\t\tcase 0xe/*DXGI_FORMAT_R16G16B16A16_SINT*/:\t\tencoding = PTI_INVALID;\t\t\tbreak;\n//\t\tcase 0xf/*DXGI_FORMAT_R32G32_TYPELESS*/:\t\tencoding = PTI_INVALID;\t\t\tbreak;\n//\t\tcase 0x10/*DXGI_FORMAT_R32G32_FLOAT*/:\t\t\tencoding = PTI_INVALID;\t\t\tbreak;\n//\t\tcase 0x11/*DXGI_FORMAT_R32G32_UINT*/:\t\t\tencoding = PTI_INVALID;\t\t\tbreak;\n//\t\tcase 0x12/*DXGI_FORMAT_R32G32_SINT*/:\t\t\tencoding = PTI_INVALID;\t\t\tbreak;\n//\t\tcase 0x13/*DXGI_FORMAT_R32G8X24_TYPELESS*/:\t\tencoding = PTI_INVALID;\t\t\tbreak;\n//\t\tcase 0x14/*DXGI_FORMAT_D32_FLOAT_S8X24_UINT*/:\tencoding = PTI_DEPTH32_8;\t\tbreak;\n//\t\tcase 0x15/*DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS*/:encoding = PTI_INVALID;\t\tbreak;\n//\t\tcase 0x16/*DXGI_FORMAT_X32_TYPELESS_G8X24_UINT*/:encoding = PTI_INVALID;\t\tbreak;\n//\t\tcase 0x17/*DXGI_FORMAT_R10G10B10A2_TYPELESS*/:\tencoding = PTI_INVALID;\t\t\tbreak;\n\t\tcase 0x18/*DXGI_FORMAT_R10G10B10A2_UNORM*/:\t\tencoding = PTI_A2BGR10;\t\t\tbreak;\n//\t\tcase 0x19/*DXGI_FORMAT_R10G10B10A2_UINT*/:\t\tencoding = PTI_INVALID;\t\t\tbreak;\n\t\tcase 0x1a/*DXGI_FORMAT_R11G11B10_FLOAT*/:\t\tencoding = PTI_B10G11R11F;\t\tbreak;\n//\t\tcase 0x1b/*DXGI_FORMAT_R8G8B8A8_TYPELESS*/:\t\tencoding = PTI_INVALID;\t\t\tbreak;\n\t\tcase 0x1c/*DXGI_FORMAT_R8G8B8A8_UNORM*/:\t\tencoding = PTI_RGBA8;\t\t\tbreak;\n\t\tcase 0x1d/*DXGI_FORMAT_R8G8B8A8_UNORM_SRGB*/:\tencoding = PTI_RGBA8_SRGB;\t\tbreak;\n//\t\tcase 0x1e/*DXGI_FORMAT_R8G8B8A8_UINT*/:\t\t\tencoding = PTI_INVALID;\t\t\tbreak;\n//\t\tcase 0x1f/*DXGI_FORMAT_R8G8B8A8_SNORM*/:\t\tencoding = PTI_INVALID;\t\t\tbreak;\n//\t\tcase 0x20/*DXGI_FORMAT_R8G8B8A8_SINT*/:\t\t\tencoding = PTI_INVALID;\t\t\tbreak;\n//\t\tcase 0x21/*DXGI_FORMAT_R16G16_TYPELESS*/:\t\tencoding = PTI_INVALID;\t\t\tbreak;\n//\t\tcase 0x22/*DXGI_FORMAT_R16G16_FLOAT*/:\t\t\tencoding = PTI_INVALID;\t\t\tbreak;\n//\t\tcase 0x23/*DXGI_FORMAT_R16G16_UNORM*/:\t\t\tencoding = PTI_INVALID;\t\t\tbreak;\n//\t\tcase 0x24/*DXGI_FORMAT_R16G16_UINT*/:\t\t\tencoding = PTI_INVALID;\t\t\tbreak;\n//\t\tcase 0x25/*DXGI_FORMAT_R16G16_SNORM*/:\t\t\tencoding = PTI_INVALID;\t\t\tbreak;\n//\t\tcase 0x26/*DXGI_FORMAT_R16G16_SINT*/:\t\t\tencoding = PTI_INVALID;\t\t\tbreak;\n//\t\tcase 0x27/*DXGI_FORMAT_R32_TYPELESS*/:\t\t\tencoding = PTI_INVALID;\t\t\tbreak;\n\t\tcase 0x28/*DXGI_FORMAT_D32_FLOAT*/:\t\t\t\tencoding = PTI_DEPTH32;\t\t\tbreak;\n\t\tcase 0x29/*DXGI_FORMAT_R32_FLOAT*/:\t\t\t\tencoding = PTI_R32F;\t\t\tbreak;\n//\t\tcase 0x2a/*DXGI_FORMAT_R32_UINT*/:\t\t\t\tencoding = PTI_INVALID;\t\t\tbreak;\n//\t\tcase 0x2b/*DXGI_FORMAT_R32_SINT*/:\t\t\t\tencoding = PTI_INVALID;\t\t\tbreak;\n//\t\tcase 0x2c/*DXGI_FORMAT_R24G8_TYPELESS*/:\t\tencoding = PTI_INVALID;\t\t\tbreak;\n//\t\tcase 0x2d/*DXGI_FORMAT_D24_UNORM_S8_UINT*/:\t\tencoding = PTI_DEPTH24_8;\t\tbreak;\n//\t\tcase 0x2e/*DXGI_FORMAT_R24_UNORM_X8_TYPELESS*/:\tencoding = PTI_INVALID;\t\t\tbreak;\n//\t\tcase 0x2f/*DXGI_FORMAT_X24_TYPELESS_G8_UINT*/:\tencoding = PTI_INVALID;\t\t\tbreak;\n//\t\tcase 0x30/*DXGI_FORMAT_R8G8_TYPELESS*/:\t\t\tencoding = PTI_INVALID;\t\t\tbreak;\n\t\tcase 0x31/*DXGI_FORMAT_R8G8_UNORM*/:\t\t\tencoding = PTI_RG8;\t\t\t\tbreak;\n//\t\tcase 0x32/*DXGI_FORMAT_R8G8_UINT*/:\t\t\t\tencoding = PTI_INVALID;\t\t\tbreak;\n\t\tcase 0x33/*DXGI_FORMAT_R8G8_SNORM*/:\t\t\tencoding = PTI_RG8_SNORM;\t\tbreak;\n//\t\tcase 0x34/*DXGI_FORMAT_R8G8_SINT*/:\t\t\t\tencoding = PTI_INVALID;\t\t\tbreak;\n//\t\tcase 0x35/*DXGI_FORMAT_R16_TYPELESS*/:\t\t\tencoding = PTI_INVALID;\t\t\tbreak;\n\t\tcase 0x36/*DXGI_FORMAT_R16_FLOAT*/:\t\t\t\tencoding = PTI_R16F;\t\t\tbreak;\n\t\tcase 0x37/*DXGI_FORMAT_D16_UNORM*/:\t\t\t\tencoding = PTI_DEPTH16;\t\t\tbreak;\n\t\tcase 0x38/*DXGI_FORMAT_R16_UNORM*/:\t\t\t\tencoding = PTI_R16;\t\t\t\tbreak;\n//\t\tcase 0x39/*DXGI_FORMAT_R16_UINT*/:\t\t\t\tencoding = PTI_INVALID;\t\t\tbreak;\n//\t\tcase 0x3a/*DXGI_FORMAT_R16_SNORM*/:\t\t\t\tencoding = PTI_INVALID;\t\t\tbreak;\n//\t\tcase 0x3b/*DXGI_FORMAT_R16_SINT*/:\t\t\t\tencoding = PTI_INVALID;\t\t\tbreak;\n//\t\tcase 0x3c/*DXGI_FORMAT_R8_TYPELESS*/:\t\t\tencoding = PTI_INVALID;\t\t\tbreak;\n\t\tcase 0x3d/*DXGI_FORMAT_R8_UNORM*/:\t\t\t\tencoding = PTI_R8;\t\t\t\tbreak;\n//\t\tcase 0x3e/*DXGI_FORMAT_R8_UINT*/:\t\t\t\tencoding = PTI_INVALID;\t\t\tbreak;\n\t\tcase 0x3f/*DXGI_FORMAT_R8_SNORM*/:\t\t\t\tencoding = PTI_R8_SNORM;\t\tbreak;\n//\t\tcase 0x40/*DXGI_FORMAT_R8_SINT*/:\t\t\t\tencoding = PTI_INVALID;\t\t\tbreak;\n//\t\tcase 0x41/*DXGI_FORMAT_A8_UNORM*/:\t\t\t\tencoding = PTI_A8;\t\t\t\tbreak;\n//\t\tcase 0x42/*DXGI_FORMAT_R1_UNORM*/:\t\t\t\tencoding = PTI_INVALID;\t\t\tbreak;\n\t\tcase 0x43/*DXGI_FORMAT_R9G9B9E5_SHAREDEXP*/:\tencoding = PTI_E5BGR9;\t\t\tbreak;\n//\t\tcase 0x44/*DXGI_FORMAT_R8G8_B8G8_UNORM*/:\t\tencoding = PTI_INVALID;\t\t\tbreak;\n//\t\tcase 0x45/*DXGI_FORMAT_G8R8_G8B8_UNORM*/:\t\tencoding = PTI_INVALID;\t\t\tbreak;\n//\t\tcase 0x46/*DXGI_FORMAT_BC1_TYPELESS*/:\t\t\tencoding = PTI_INVALID;\t\t\tbreak;\n\t\tcase 0x47/*DXGI_FORMAT_BC1_UNORM*/:\t\t\t\tencoding = PTI_BC1_RGBA;\t\tbreak;\n\t\tcase 0x48/*DXGI_FORMAT_BC1_UNORM_SRGB*/:\t\tencoding = PTI_BC1_RGBA_SRGB;\tbreak;\n//\t\tcase 0x49/*DXGI_FORMAT_BC2_TYPELESS*/:\t\t\tencoding = PTI_INVALID;\t\t\tbreak;\n\t\tcase 0x4a/*DXGI_FORMAT_BC2_UNORM*/:\t\t\t\tencoding = PTI_BC2_RGBA;\t\tbreak;\n\t\tcase 0x4b/*DXGI_FORMAT_BC2_UNORM_SRGB*/:\t\tencoding = PTI_BC2_RGBA_SRGB;\tbreak;\n//\t\tcase 0x4c/*DXGI_FORMAT_BC3_TYPELESS*/:\t\t\tencoding = PTI_INVALID;\t\t\tbreak;\n\t\tcase 0x4d/*DXGI_FORMAT_BC3_UNORM*/:\t\t\t\tencoding = PTI_BC3_RGBA;\t\tbreak;\n\t\tcase 0x4e/*DXGI_FORMAT_BC3_UNORM_SRGB*/:\t\tencoding = PTI_BC3_RGBA_SRGB;\tbreak;\n//\t\tcase 0x4f/*DXGI_FORMAT_BC4_TYPELESS*/:\t\t\tencoding = PTI_INVALID;\t\t\tbreak;\n\t\tcase 0x50/*DXGI_FORMAT_BC4_UNORM*/:\t\t\t\tencoding = PTI_BC4_R;\t\t\tbreak;\n\t\tcase 0x51/*DXGI_FORMAT_BC4_SNORM*/:\t\t\t\tencoding = PTI_BC4_R_SNORM;\t\tbreak;\n//\t\tcase 0x52/*DXGI_FORMAT_BC5_TYPELESS*/:\t\t\tencoding = PTI_INVALID;\t\t\tbreak;\n\t\tcase 0x53/*DXGI_FORMAT_BC5_UNORM*/:\t\t\t\tencoding = PTI_BC5_RG;\t\t\tbreak;\n\t\tcase 0x54/*DXGI_FORMAT_BC5_SNORM*/:\t\t\t\tencoding = PTI_BC5_RG_SNORM;\tbreak;\n\t\tcase 0x55/*DXGI_FORMAT_B5G6R5_UNORM*/:\t\t\tencoding = PTI_RGB565;\t\t\tbreak;\n\t\tcase 0x56/*DXGI_FORMAT_B5G5R5A1_UNORM*/:\t\tencoding = PTI_ARGB1555;\t\tbreak;\n\t\tcase 0x57/*DXGI_FORMAT_B8G8R8A8_UNORM*/:\t\tencoding = PTI_BGRA8;\t\t\tbreak;\n\t\tcase 0x58/*DXGI_FORMAT_B8G8R8X8_UNORM*/:\t\tencoding = PTI_BGRX8;\t\t\tbreak;\n//\t\tcase 0x59/*DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM*/:encoding = PTI_INVALID;\t\tbreak;\n//\t\tcase 0x5a/*DXGI_FORMAT_B8G8R8A8_TYPELESS*/:\t\tencoding = PTI_INVALID;\t\t\tbreak;\n\t\tcase 0x5b/*DXGI_FORMAT_B8G8R8A8_UNORM_SRGB*/:\tencoding = PTI_BGRA8_SRGB;\t\tbreak;\n//\t\tcase 0x5c/*DXGI_FORMAT_B8G8R8X8_TYPELESS*/:\t\tencoding = PTI_INVALID;\t\t\tbreak;\n\t\tcase 0x5d/*DXGI_FORMAT_B8G8R8X8_UNORM_SRGB*/:\tencoding = PTI_BGRX8_SRGB;\t\tbreak;\n//\t\tcase 0x5e/*DXGI_FORMAT_BC6H_TYPELESS*/:\t\t\tencoding = PTI_INVALID;\t\t\tbreak;\n\t\tcase 0x5f/*DXGI_FORMAT_BC6H_UF16*/:\t\t\t\tencoding = PTI_BC6_RGB_UFLOAT;\tbreak;\n\t\tcase 0x60/*DXGI_FORMAT_BC6H_SF16*/:\t\t\t\tencoding = PTI_BC6_RGB_SFLOAT;\tbreak;\n//\t\tcase 0x61/*DXGI_FORMAT_BC7_TYPELESS*/:\t\t\tencoding = PTI_INVALID;\t\t\tbreak;\n\t\tcase 0x62/*DXGI_FORMAT_BC7_UNORM*/:\t\t\t\tencoding = PTI_BC7_RGBA;\t\tbreak;\n\t\tcase 0x63/*DXGI_FORMAT_BC7_UNORM_SRGB*/:\t\tencoding = PTI_BC7_RGBA_SRGB;\tbreak;\n//\t\tcase 0x64/*DXGI_FORMAT_AYUV*/:\t\t\t\t\tencoding = PTI_INVALID;\t\t\tbreak;\n//\t\tcase 0x65/*DXGI_FORMAT_Y410*/:\t\t\t\t\tencoding = PTI_INVALID;\t\t\tbreak;\n//\t\tcase 0x66/*DXGI_FORMAT_Y416*/:\t\t\t\t\tencoding = PTI_INVALID;\t\t\tbreak;\n//\t\tcase 0x67/*DXGI_FORMAT_NV12*/:\t\t\t\t\tencoding = PTI_INVALID;\t\t\tbreak;\n//\t\tcase 0x68/*DXGI_FORMAT_P010*/:\t\t\t\t\tencoding = PTI_INVALID;\t\t\tbreak;\n//\t\tcase 0x69/*DXGI_FORMAT_P016*/:\t\t\t\t\tencoding = PTI_INVALID;\t\t\tbreak;\n//\t\tcase 0x6a/*DXGI_FORMAT_420_OPAQUE*/:\t\t\tencoding = PTI_INVALID;\t\t\tbreak;\n//\t\tcase 0x6b/*DXGI_FORMAT_YUY2*/:\t\t\t\t\tencoding = PTI_INVALID;\t\t\tbreak;\n//\t\tcase 0x6c/*DXGI_FORMAT_Y210*/:\t\t\t\t\tencoding = PTI_INVALID;\t\t\tbreak;\n//\t\tcase 0x6d/*DXGI_FORMAT_Y216*/:\t\t\t\t\tencoding = PTI_INVALID;\t\t\tbreak;\n//\t\tcase 0x6e/*DXGI_FORMAT_NV11*/:\t\t\t\t\tencoding = PTI_INVALID;\t\t\tbreak;\n//\t\tcase 0x6f/*DXGI_FORMAT_AI44*/:\t\t\t\t\tencoding = PTI_INVALID;\t\t\tbreak;\n//\t\tcase 0x70/*DXGI_FORMAT_IA44*/:\t\t\t\t\tencoding = PTI_INVALID;\t\t\tbreak;\n//\t\tcase 0x71/*DXGI_FORMAT_P8*/:\t\t\t\t\tencoding = PTI_INVALID;\t\t\tbreak;\n//\t\tcase 0x72/*DXGI_FORMAT_A8P8*/:\t\t\t\t\tencoding = PTI_INVALID;\t\t\tbreak;\n\t\tcase 0x73/*DXGI_FORMAT_B4G4R4A4_UNORM*/:\t\tencoding = PTI_ARGB4444;\t\tbreak;\n//\t\tcase 0x82/*DXGI_FORMAT_P208*/:\t\t\t\t\tencoding = PTI_INVALID;\t\t\tbreak;\n//\t\tcase 0x83/*DXGI_FORMAT_V208*/:\t\t\t\t\tencoding = PTI_INVALID;\t\t\tbreak;\n//\t\tcase 0x84/*DXGI_FORMAT_V408*/:\t\t\t\t\tencoding = PTI_INVALID;\t\t\tbreak;\n\t\tcase 134:\tencoding = PTI_ASTC_4X4_LDR;\tbreak;\n\t\tcase 135:\tencoding = PTI_ASTC_4X4_SRGB;\tbreak;\n\t\tcase 138:\tencoding = PTI_ASTC_5X4_LDR;\tbreak;\n\t\tcase 139:\tencoding = PTI_ASTC_5X4_SRGB;\tbreak;\n\t\tcase 142:\tencoding = PTI_ASTC_5X5_LDR;\tbreak;\n\t\tcase 143:\tencoding = PTI_ASTC_5X5_SRGB;\tbreak;\n\t\tcase 146:\tencoding = PTI_ASTC_6X5_LDR;\tbreak;\n\t\tcase 147:\tencoding = PTI_ASTC_6X5_SRGB;\tbreak;\n\t\tcase 150:\tencoding = PTI_ASTC_6X6_LDR;\tbreak;\n\t\tcase 151:\tencoding = PTI_ASTC_6X6_SRGB;\tbreak;\n\t\tcase 154:\tencoding = PTI_ASTC_8X5_LDR;\tbreak;\n\t\tcase 155:\tencoding = PTI_ASTC_8X5_SRGB;\tbreak;\n\t\tcase 158:\tencoding = PTI_ASTC_8X6_LDR;\tbreak;\n\t\tcase 159:\tencoding = PTI_ASTC_8X6_SRGB;\tbreak;\n\t\tcase 162:\tencoding = PTI_ASTC_8X8_LDR;\tbreak;\n\t\tcase 163:\tencoding = PTI_ASTC_8X8_SRGB;\tbreak;\n\t\tcase 166:\tencoding = PTI_ASTC_10X5_LDR;\tbreak;\n\t\tcase 167:\tencoding = PTI_ASTC_10X5_SRGB;\tbreak;\n\t\tcase 170:\tencoding = PTI_ASTC_10X6_LDR;\tbreak;\n\t\tcase 171:\tencoding = PTI_ASTC_10X6_SRGB;\tbreak;\n\t\tcase 174:\tencoding = PTI_ASTC_10X8_LDR;\tbreak;\n\t\tcase 175:\tencoding = PTI_ASTC_10X8_SRGB;\tbreak;\n\t\tcase 178:\tencoding = PTI_ASTC_10X10_LDR;\tbreak;\n\t\tcase 179:\tencoding = PTI_ASTC_10X10_SRGB;\tbreak;\n\t\tcase 182:\tencoding = PTI_ASTC_12X10_LDR;\tbreak;\n\t\tcase 183:\tencoding = PTI_ASTC_12X10_SRGB;\tbreak;\n\t\tcase 186:\tencoding = PTI_ASTC_12X12_LDR;\tbreak;\n\t\tcase 187:\tencoding = PTI_ASTC_12X12_SRGB;\tbreak;\n\n\t\tdefault:\n\t\t\tCon_Printf(\"Unsupported dds10 dxgi in %s - %u\\n\", fname, fmt10header.dxgiformat);\n\t\t\treturn NULL;\n\t\t}\n\t}\n\telse\n\t{\n\t\tCon_Printf(\"Unsupported dds fourcc in %s - \\\"%c%c%c%c\\\"\\n\", fname,\n\t\t\t((char*)&fmtheader.ddpfPixelFormat.dwFourCC)[0],\n\t\t\t((char*)&fmtheader.ddpfPixelFormat.dwFourCC)[1],\n\t\t\t((char*)&fmtheader.ddpfPixelFormat.dwFourCC)[2],\n\t\t\t((char*)&fmtheader.ddpfPixelFormat.dwFourCC)[3]);\n\t\treturn NULL;\n\t}\n\n\tif ((fmtheader.ddsCaps[1] & 0x200) && (fmtheader.ddsCaps[1] & 0xfc00) != 0xfc00)\n\t\treturn NULL;\t//cubemap without all 6 faces defined.\n\n\tImage_BlockSizeForEncoding(encoding, &blockbytes, &blockwidth, &blockheight, &blockdepth);\n\tif (!blockbytes)\n\t\treturn NULL;\t//werid/unsupported\n\n\tif (fmtheader.dwFlags & 8)\n\t{\t//explicit pitch flag. we don't support any padding, so this check exists just to be sure none is required.\n\t\tw = max(1, fmtheader.dwWidth);\n\t\tif (fmtheader.dwPitchOrLinearSize != blockbytes*(w+blockwidth-1)/blockwidth)\n\t\t\treturn NULL;\n\t}\n\tif (fmtheader.dwFlags & 0x80000)\n\t{\t//linear size flag. we don't support any padding, so this check exists just to be sure none is required.\n\t\t//linear-size of the top-level mip.\n\t\tsize_t linearsize;\n\t\tw = max(1, fmtheader.dwWidth);\n\t\th = max(1, fmtheader.dwHeight);\n\t\td = max(1, fmtheader.dwDepth);\n\t\tlinearsize = ((w+blockwidth-1)/blockwidth)*\n\t\t\t\t\t\t\t((h+blockheight-1)/blockheight)*\n\t\t\t\t\t\t\t((d+blockdepth-1)/blockdepth)*\n\t\t\t\t\t\t\tblockbytes;\n\t\tif (fmtheader.dwPitchOrLinearSize != linearsize)\n\t\t\treturn NULL;\n\t}\n\n\tif (fmtheader.ddsCaps[1] & 0x200)\n\t{\n\t\tif (fmt10header.arraysize % 6)\t//weird number of faces.\n\t\t\treturn NULL;\n\n\t\tif (fmt10header.arraysize == 6)\n\t\t{\n\t\t\tttype = PTI_CUBE;\n\t\t\tlayers = 6;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tttype = PTI_CUBE_ARRAY;\n\t\t\tlayers = fmt10header.arraysize;\n\t\t}\n\t}\n\telse if (fmtheader.ddsCaps[1] & 0x200000)\n\t{\n\t\tif (fmt10header.arraysize != 1)\t//no 2d arrays\n\t\t\treturn NULL;\n\t\tttype = PTI_3D;\n\t}\n\telse\n\t{\n\t\tif (fmt10header.arraysize == 1)\n\t\t\tttype = PTI_2D;\n\t\telse\n\t\t\tttype = PTI_2D_ARRAY;\n\t\tlayers = fmt10header.arraysize;\n\t}\n\n\tmips = Z_Malloc(sizeof(*mips));\n\tmips->mipcount = 0;\n\tmips->type = ttype;\n\tmips->extrafree = filedata;\n\tmips->encoding = encoding;\n\n\tfiledata += fmtheader.dwSize;\n\n\tw = max(1, fmtheader.dwWidth);\n\th = max(1, fmtheader.dwHeight);\n\td = max(1, fmtheader.dwDepth);\n\n\tif (layers == 1)\n\t{\t//can just use the data without copying.\n\t\tfor (mipnum = 0; mipnum < nummips; mipnum++)\n\t\t{\n\t\t\tdatasize = ((w+blockwidth-1)/blockwidth) * ((h+blockheight-1)/blockheight) * ((d+blockdepth-1)/blockdepth) * blockbytes;\n\n\t\t\tmips->mip[mipnum].data = filedata;\n\t\t\tmips->mip[mipnum].datasize = datasize;\n\t\t\tmips->mip[mipnum].width = w;\n\t\t\tmips->mip[mipnum].height = h;\n\t\t\tmips->mip[mipnum].depth = d;\n\t\t\tfiledata += datasize;\n\n\t\t\tw = max(1, w>>1);\n\t\t\th = max(1, h>>1);\n\t\t\td = max(1, d>>1);\n\t\t}\n\t\tmips->mipcount = mipnum;\n\n\t\tif (filedata > fileend)\n\t\t{\t//overflow... corrupt dds?\n\t\t\tZ_Free(mips);\n\t\t\treturn NULL;\n\t\t}\n\t}\n\telse\n\t{\t//we need to copy stuff in order to pack it properly. :(\n\t\t//allocate space and calc mip sizes\n\t\tfor (mipnum = 0; mipnum < nummips; mipnum++)\n\t\t{\n\t\t\tdatasize = ((w+blockwidth-1)/blockwidth) * ((h+blockheight-1)/blockheight) * (layers*((d+blockdepth-1)/blockdepth)) * blockbytes;\n\t\t\tmips->mip[mipnum].data = BZ_Malloc(datasize);\n\t\t\tmips->mip[mipnum].datasize = datasize;\n\t\t\tmips->mip[mipnum].width = w;\n\t\t\tmips->mip[mipnum].height = h;\n\t\t\tmips->mip[mipnum].depth = layers*d;\n\n\t\t\tw = max(1, w>>1);\n\t\t\th = max(1, h>>1);\n\t\t\td = max(1, d>>1);\n\t\t}\n\t\tmips->mipcount = mipnum;\n\t\t//and now copy over the data\n\t\tfor (layer = 0; layer < layers; layer++)\n\t\t{\n\t\t\tfor (mipnum = 0; mipnum < nummips; mipnum++)\n\t\t\t{\n\t\t\t\tdatasize = mips->mip[mipnum].datasize/layers;\n\t\t\t\tif (filedata+datasize > fileend)\n\t\t\t\t{\t//overflow... corrupt dds?\n\t\t\t\t\tfor (mipnum = 0; mipnum < nummips; mipnum++)\n\t\t\t\t\t\tZ_Free(mips->mip[mipnum].data);\n\t\t\t\t\tZ_Free(mips);\n\t\t\t\t\treturn NULL;\n\t\t\t\t}\n\t\t\t\tmemcpy((qbyte*)mips->mip[mipnum].data+datasize*layer, filedata, datasize);\n\t\t\t\tfiledata += datasize;\n\t\t\t}\n\t\t}\n\t\t//and now we're done with the source file. we might as well free it early.\n\t\tBZ_Free(mips->extrafree);\n\t\tmips->extrafree = NULL;\n\t}\n\n\treturn mips;\n}\n\nqboolean Image_WriteDDSFile(const char *filename, enum fs_relative fsroot, struct pendingtextureinfo *mips)\n{\n\tvfsfile_t *file;\n\tsize_t mipnum;\n\tsize_t a;\n\tdds10header_t h10={0};\n\tddsheader_t h9={0};\n\tint *endian;\n\n\tunsigned int blockbytes, blockwidth, blockheight, blockdepth;\n\tunsigned int arraysize;\n\n\tImage_BlockSizeForEncoding(mips->encoding, &blockbytes, &blockwidth, &blockheight, &blockdepth);\n\n\th9.dwSize = sizeof(h9);\n\th9.ddpfPixelFormat.dwSize = sizeof(h9.ddpfPixelFormat);\n\th9.dwFlags = 0;\n\th9.dwFlags |= 1;\t\t\t//CAPS\n\th9.dwFlags |= 2;\t\t\t//HEIGHT\n\th9.dwFlags |= 4;\t\t\t//WIDTH\n\th9.dwFlags |= 0x1000;\t\t//PIXELFORMAT\n\tif (blockwidth != 1 || blockheight != 1)\n\t{\n\t\th9.dwFlags |= 0x80000;\t//LINEARSIZE\n\t\th9.dwPitchOrLinearSize =\t((mips->mip[0].width+blockwidth-1)/blockwidth)*\n\t\t\t\t\t\t\t\t\t((mips->mip[0].height+blockheight-1)/blockheight)*\n\t\t\t\t\t\t\t\t\t(mips->type==PTI_3D?((mips->mip[0].depth+blockdepth-1)/blockdepth):1)*\n\t\t\t\t\t\t\t\t\tblockbytes;\n\t}\n\telse\n\t{\n\t\th9.dwFlags |= 8;\t\t//PITCH\n\t\th9.dwPitchOrLinearSize = mips->mip[0].width*blockbytes;\n\t}\n\tif (mips->mipcount > 1)\n\t\th9.dwFlags |= 0x20000;\t//MIPMAPCOUNT\n\th9.dwWidth = mips->mip[0].width;\n\th9.dwHeight = mips->mip[0].height;\n\th9.dwDepth = mips->mip[0].depth;\n\n\th9.ddpfPixelFormat.dwSize = 32;\n\th9.ddsCaps[0] = 0x1000;\t\t//TEXTURE\n\tif (mips->mipcount > 1)\n\t\th9.ddsCaps[0] |= 0x8;\t\t//COMPLEX\n\th9.ddsCaps[1] = 0;\n\th10.miscflag = 0;\n\th10.miscflags2 = 0;\n\th9.dwMipMapCount = mips->mipcount;\n\n\tarraysize = mips->mip[0].depth;\n\tswitch(mips->type)\n\t{\n\tcase PTI_ANY:\n\t\treturn false;\n\tcase PTI_3D:\n\t\tarraysize = 1;\n\t\th9.ddsCaps[1] |= 0x200000;\t//VOLUME\n\t\th10.resourcetype = 4;\t//3d\n\t\tbreak;\n\tcase PTI_CUBE_ARRAY:\n\t\tif (mips->mip[0].depth <= 1)\t//in dds arraysize=1 is NOT an array, leaving us with an ambiguity issue\n\t\t\treturn false;\n\t\th9.dwDepth = 1;\n\t\th10.resourcetype = 3;\t//2d\n\t\th9.ddsCaps[1] |= 0x200|0xfc00;\t\t//CUBEMAP+faces\n\t\th10.miscflag |= 4;//DDS_RESOURCE_MISC_TEXTURECUBE - otherwise they're basicaly just 2d_arrays\n\t\tbreak;\n\tcase PTI_CUBE:\n\t\tif (mips->mip[0].depth != 6)\t//wut?!?\n\t\t\treturn false;\n\t\th9.dwDepth = 1;\n\t\th10.resourcetype = 3;\t//2d\n\t\th9.ddsCaps[1] |= 0x200|0xfc00;\t\t//CUBEMAP+faces\n\t\th10.miscflag |= 4;//DDS_RESOURCE_MISC_TEXTURECUBE - otherwise they're basicaly just 2d_arrays\n\t\tbreak;\n\tcase PTI_2D_ARRAY:\n\t\tif (mips->mip[0].depth <= 1)\t//in dds arraysize=1 is NOT an array, leaving us with an ambiguity issue\n\t\t\treturn false;\n\t\th9.dwDepth = 1;\n\t\th10.resourcetype = 3;\t//2d\n\t\tbreak;\n\tcase PTI_2D:\n\t\tif (mips->mip[0].depth != 1)\t//wut?!?\n\t\t\treturn false;\n\t\th9.dwDepth = 1;\n\t\th10.resourcetype = 3;\t//2d\n\t\tbreak;\n\t}\n\tif (h9.dwMipMapCount > 1)\n\t\th9.ddsCaps[0] |= 0x400000;\t//MIPMAP\n\n\th10.arraysize = arraysize;\n\n\th10.dxgiformat = 0;\n\n#define DX9FOURCC(a,b,c,d)\t\th9.ddpfPixelFormat.dwFlags=4/*DDPF_FOURCC*/,\t\\\n\t\t\t\t\t\t\t\th9.ddpfPixelFormat.dwFourCC=(a<<0)|(b<<8)|(c<<16)|(d<<24)\n#define DX9FMT(bits,r,g,b,a,fl) h9.ddpfPixelFormat.dwFlags=fl,\t\t\\\n\t\t\t\t\t\t\t\th9.ddpfPixelFormat.bitcount=bits,\t\\\n\t\t\t\t\t\t\t\th9.ddpfPixelFormat.redmask=r,\t\t\\\n\t\t\t\t\t\t\t\th9.ddpfPixelFormat.greenmask=g,\t\t\\\n\t\t\t\t\t\t\t\th9.ddpfPixelFormat.bluemask=b,\t\t\\\n\t\t\t\t\t\t\t\th9.ddpfPixelFormat.alphamask=a\n#define DX9RGB\t\t\t0x40\n#define DX9RGBA\t\t\t(0x40|0x1)\n#define DX9LUM\t\t\t0x20000\n#define DX9LUMALPHA\t\t(0x20000|0x1)\n\tsafeswitch(mips->encoding)\n\t{\n//\tcase PTI_INVALID:\t\t\th10.dxgiformat = 0x0/*DXGI_FORMAT_UNKNOWN*/;\t\t\t\tbreak;\n//\tcase PTI_INVALID:\t\t\th10.dxgiformat = 0x1/*DXGI_FORMAT_R32G32B32A32_TYPELESS*/;\tbreak;\n\tcase PTI_RGBA32F:\t\t\th10.dxgiformat = 0x2/*DXGI_FORMAT_R32G32B32A32_FLOAT*/;\t\tbreak;\n//\tcase PTI_INVALID:\t\t\th10.dxgiformat = 0x3/*DXGI_FORMAT_R32G32B32A32_UINT*/;\t\tbreak;\n//\tcase PTI_INVALID:\t\t\th10.dxgiformat = 0x4/*DXGI_FORMAT_R32G32B32A32_SINT*/;\t\tbreak;\n//\tcase PTI_INVALID:\t\t\th10.dxgiformat = 0x5/*DXGI_FORMAT_R32G32B32_TYPELESS*/;\t\tbreak;\n\tcase PTI_RGB32F:\t\t\th10.dxgiformat = 0x6/*DXGI_FORMAT_R32G32B32_FLOAT*/;\t\tbreak;\n//\tcase PTI_INVALID:\t\t\th10.dxgiformat = 0x7/*DXGI_FORMAT_R32G32B32_UINT*/;\t\t\tbreak;\n//\tcase PTI_INVALID:\t\t\th10.dxgiformat = 0x8/*DXGI_FORMAT_R32G32B32_SINT*/;\t\t\tbreak;\n//\tcase PTI_INVALID:\t\t\th10.dxgiformat = 0x9/*DXGI_FORMAT_R16G16B16A16_TYPELESS*/;\tbreak;\n\tcase PTI_RGBA16F:\t\t\th10.dxgiformat = 0xa/*DXGI_FORMAT_R16G16B16A16_FLOAT*/;\t\tbreak;\n\tcase PTI_RGBA16:\t\t\th10.dxgiformat = 0xb/*DXGI_FORMAT_R16G16B16A16_UNORM*/;\t\tbreak;\n//\tcase PTI_INVALID:\t\t\th10.dxgiformat = 0xc/*DXGI_FORMAT_R16G16B16A16_UINT*/;\t\tbreak;\n//\tcase PTI_INVALID:\t\t\th10.dxgiformat = 0xd/*DXGI_FORMAT_R16G16B16A16_SNORM*/;\t\tbreak;\n//\tcase PTI_INVALID:\t\t\th10.dxgiformat = 0xe/*DXGI_FORMAT_R16G16B16A16_SINT*/;\t\tbreak;\n//\tcase PTI_INVALID:\t\t\th10.dxgiformat = 0xf/*DXGI_FORMAT_R32G32_TYPELESS*/;\t\tbreak;\n//\tcase PTI_INVALID:\t\t\th10.dxgiformat = 0x10/*DXGI_FORMAT_R32G32_FLOAT*/;\t\t\tbreak;\n//\tcase PTI_INVALID:\t\t\th10.dxgiformat = 0x11/*DXGI_FORMAT_R32G32_UINT*/;\t\t\tbreak;\n//\tcase PTI_INVALID:\t\t\th10.dxgiformat = 0x12/*DXGI_FORMAT_R32G32_SINT*/;\t\t\tbreak;\n//\tcase PTI_INVALID:\t\t\th10.dxgiformat = 0x13/*DXGI_FORMAT_R32G8X24_TYPELESS*/;\t\tbreak;\n//\tcase PTI_INVALID:\t\t\th10.dxgiformat = 0x14/*DXGI_FORMAT_D32_FLOAT_S8X24_UINT*/;\tbreak;\n//\tcase PTI_INVALID:\t\t\th10.dxgiformat = 0x15/*DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS*/;break;\n//\tcase PTI_INVALID:\t\t\th10.dxgiformat = 0x16/*DXGI_FORMAT_X32_TYPELESS_G8X24_UINT*/;break;\n//\tcase PTI_INVALID:\t\t\th10.dxgiformat = 0x17/*DXGI_FORMAT_R10G10B10A2_TYPELESS*/;\tbreak;\n\tcase PTI_A2BGR10:\t\t\th10.dxgiformat = 0x18/*DXGI_FORMAT_R10G10B10A2_UNORM*/;\t\tDX9FMT(32,0x000003ff,0x000ffc00,0x03ff0000,0xc0000000,DX9RGBA);\tbreak;\n//\tcase PTI_INVALID:\t\t\th10.dxgiformat = 0x19/*DXGI_FORMAT_R10G10B10A2_UINT*/;\t\tbreak;\n\tcase PTI_B10G11R11F:\t\th10.dxgiformat = 0x1a/*DXGI_FORMAT_R11G11B10_FLOAT*/;\t\tbreak;\n//\tcase PTI_INVALID:\t\t\th10.dxgiformat = 0x1b/*DXGI_FORMAT_R8G8B8A8_TYPELESS*/;\t\tbreak;\n\tcase PTI_RGBA8:\t\t\t\th10.dxgiformat = 0x1c/*DXGI_FORMAT_R8G8B8A8_UNORM*/;\t\tDX9FMT(32,0x000000ff,0x0000ff00,0x00ff0000,0xff000000,DX9RGBA);\tbreak;\n\tcase PTI_RGBX8_SRGB:\t\t//fall through...\n\tcase PTI_RGBA8_SRGB:\t\th10.dxgiformat = 0x1d/*DXGI_FORMAT_R8G8B8A8_UNORM_SRGB*/;\tbreak;\n//\tcase PTI_INVALID:\t\t\th10.dxgiformat = 0x1e/*DXGI_FORMAT_R8G8B8A8_UINT*/;\t\t\tbreak;\n//\tcase PTI_INVALID:\t\t\th10.dxgiformat = 0x1f/*DXGI_FORMAT_R8G8B8A8_SNORM*/;\t\tbreak;\n//\tcase PTI_INVALID:\t\t\th10.dxgiformat = 0x20/*DXGI_FORMAT_R8G8B8A8_SINT*/;\t\t\tbreak;\n//\tcase PTI_INVALID:\t\t\th10.dxgiformat = 0x21/*DXGI_FORMAT_R16G16_TYPELESS*/;\t\tbreak;\n//\tcase PTI_INVALID:\t\t\th10.dxgiformat = 0x22/*DXGI_FORMAT_R16G16_FLOAT*/;\t\t\tbreak;\n//\tcase PTI_INVALID:\t\t\th10.dxgiformat = 0x23/*DXGI_FORMAT_R16G16_UNORM*/;\t\t\tbreak;\n//\tcase PTI_INVALID:\t\t\th10.dxgiformat = 0x24/*DXGI_FORMAT_R16G16_UINT*/;\t\t\tbreak;\n//\tcase PTI_INVALID:\t\t\th10.dxgiformat = 0x25/*DXGI_FORMAT_R16G16_SNORM*/;\t\t\tbreak;\n//\tcase PTI_INVALID:\t\t\th10.dxgiformat = 0x26/*DXGI_FORMAT_R16G16_SINT*/;\t\t\tbreak;\n//\tcase PTI_INVALID:\t\t\th10.dxgiformat = 0x27/*DXGI_FORMAT_R32_TYPELESS*/;\t\t\tbreak;\n\tcase PTI_DEPTH32:\t\t\th10.dxgiformat = 0x28/*DXGI_FORMAT_D32_FLOAT*/;\t\t\t\tbreak;\n\tcase PTI_R32F:\t\t\t\th10.dxgiformat = 0x29/*DXGI_FORMAT_R32_FLOAT*/;\t\t\t\tbreak;\n//\tcase PTI_INVALID:\t\t\th10.dxgiformat = 0x2a/*DXGI_FORMAT_R32_UINT*/;\t\t\t\tbreak;\n//\tcase PTI_INVALID:\t\t\th10.dxgiformat = 0x2b/*DXGI_FORMAT_R32_SINT*/;\t\t\t\tbreak;\n//\tcase PTI_INVALID:\t\t\th10.dxgiformat = 0x2c/*DXGI_FORMAT_R24G8_TYPELESS*/;\t\tbreak;\n\tcase PTI_DEPTH24_8:\t\t\th10.dxgiformat = 0x2d/*DXGI_FORMAT_D24_UNORM_S8_UINT*/;\t\tbreak;\n//\tcase PTI_INVALID:\t\t\th10.dxgiformat = 0x2e/*DXGI_FORMAT_R24_UNORM_X8_TYPELESS*/;\tbreak;\n//\tcase PTI_INVALID:\t\t\th10.dxgiformat = 0x2f/*DXGI_FORMAT_X24_TYPELESS_G8_UINT*/;\tbreak;\n//\tcase PTI_INVALID:\t\t\th10.dxgiformat = 0x30/*DXGI_FORMAT_R8G8_TYPELESS*/;\t\t\tbreak;\n\tcase PTI_RG8:\t\t\t\th10.dxgiformat = 0x31/*DXGI_FORMAT_R8G8_UNORM*/;\t\t\tbreak;\n//\tcase PTI_INVALID:\t\t\th10.dxgiformat = 0x32/*DXGI_FORMAT_R8G8_UINT*/;\t\t\t\tbreak;\n\tcase PTI_RG8_SNORM:\t\t\th10.dxgiformat = 0x33/*DXGI_FORMAT_R8G8_SNORM*/;\t\t\tbreak;\n//\tcase PTI_INVALID:\t\t\th10.dxgiformat = 0x34/*DXGI_FORMAT_R8G8_SINT*/;\t\t\t\tbreak;\n//\tcase PTI_INVALID:\t\t\th10.dxgiformat = 0x35/*DXGI_FORMAT_R16_TYPELESS*/;\t\t\tbreak;\n\tcase PTI_R16F:\t\t\t\th10.dxgiformat = 0x36/*DXGI_FORMAT_R16_FLOAT*/;\t\t\t\tbreak;\n\tcase PTI_DEPTH16:\t\t\th10.dxgiformat = 0x37/*DXGI_FORMAT_D16_UNORM*/;\t\t\t\tbreak;\n\tcase PTI_R16:\t\t\t\th10.dxgiformat = 0x38/*DXGI_FORMAT_R16_UNORM*/;\t\t\t\tbreak;\n//\tcase PTI_INVALID:\t\t\th10.dxgiformat = 0x39/*DXGI_FORMAT_R16_UINT*/;\t\t\t\tbreak;\n//\tcase PTI_INVALID:\t\t\th10.dxgiformat = 0x3a/*DXGI_FORMAT_R16_SNORM*/;\t\t\t\tbreak;\n//\tcase PTI_INVALID:\t\t\th10.dxgiformat = 0x3b/*DXGI_FORMAT_R16_SINT*/;\t\t\t\tbreak;\n//\tcase PTI_INVALID:\t\t\th10.dxgiformat = 0x3c/*DXGI_FORMAT_R8_TYPELESS*/;\t\t\tbreak;\n\tcase PTI_R8:\t\t\t\th10.dxgiformat = 0x3d/*DXGI_FORMAT_R8_UNORM*/;\t\t\t\tbreak;\n//\tcase PTI_INVALID:\t\t\th10.dxgiformat = 0x3e/*DXGI_FORMAT_R8_UINT*/;\t\t\t\tbreak;\n\tcase PTI_R8_SNORM:\t\t\th10.dxgiformat = 0x3f/*DXGI_FORMAT_R8_SNORM*/;\t\t\t\tbreak;\n//\tcase PTI_INVALID:\t\t\th10.dxgiformat = 0x40/*DXGI_FORMAT_R8_SINT*/;\t\t\t\tbreak;\n//\tcase PTI_INVALID:\t\t\th10.dxgiformat = 0x41/*DXGI_FORMAT_A8_UNORM*/;\t\t\t\tbreak;\n//\tcase PTI_INVALID:\t\t\th10.dxgiformat = 0x42/*DXGI_FORMAT_R1_UNORM*/;\t\t\t\tbreak;\n\tcase PTI_E5BGR9:\t\t\th10.dxgiformat = 0x43/*DXGI_FORMAT_R9G9B9E5_SHAREDEXP*/;\tbreak;\n//\tcase PTI_INVALID:\t\t\th10.dxgiformat = 0x44/*DXGI_FORMAT_R8G8_B8G8_UNORM*/;\t\tbreak;\n//\tcase PTI_INVALID:\t\t\th10.dxgiformat = 0x45/*DXGI_FORMAT_G8R8_G8B8_UNORM*/;\t\tbreak;\n//\tcase PTI_INVALID:\t\t\th10.dxgiformat = 0x46/*DXGI_FORMAT_BC1_TYPELESS*/;\t\t\tbreak;\n\tcase PTI_BC1_RGB:\t\t\t//fall through...\n\tcase PTI_BC1_RGBA:\t\t\th10.dxgiformat = 0x47/*DXGI_FORMAT_BC1_UNORM*/;\t\t\t\tDX9FOURCC('D','X','T','1'); break;\n\tcase PTI_BC1_RGB_SRGB:\t\t//fall through...\n\tcase PTI_BC1_RGBA_SRGB:\t\th10.dxgiformat = 0x48/*DXGI_FORMAT_BC1_UNORM_SRGB*/;\t\tbreak;\n//\tcase PTI_INVALID:\t\t\th10.dxgiformat = 0x49/*DXGI_FORMAT_BC2_TYPELESS*/;\t\t\tbreak;\n\tcase PTI_BC2_RGBA:\t\t\th10.dxgiformat = 0x4a/*DXGI_FORMAT_BC2_UNORM*/;\t\t\t\tDX9FOURCC('D','X','T','3'); break;\n\tcase PTI_BC2_RGBA_SRGB:\t\th10.dxgiformat = 0x4b/*DXGI_FORMAT_BC2_UNORM_SRGB*/;\t\tbreak;\n//\tcase PTI_INVALID:\t\t\th10.dxgiformat = 0x4c/*DXGI_FORMAT_BC3_TYPELESS*/;\t\t\tbreak;\n\tcase PTI_BC3_RGBA:\t\t\th10.dxgiformat = 0x4d/*DXGI_FORMAT_BC3_UNORM*/;\t\t\t\tDX9FOURCC('D','X','T','5'); break;\n\tcase PTI_BC3_RGBA_SRGB:\t\th10.dxgiformat = 0x4e/*DXGI_FORMAT_BC3_UNORM_SRGB*/;\t\tbreak;\n//\tcase PTI_INVALID:\t\t\th10.dxgiformat = 0x4f/*DXGI_FORMAT_BC4_TYPELESS*/;\t\t\tbreak;\n\tcase PTI_BC4_R:\t\t\t\th10.dxgiformat = 0x50/*DXGI_FORMAT_BC4_UNORM*/;\t\t\t\t/*DX9FOURCC('B','C','4','U');*/ DX9FOURCC('A','T','I','1'); break;\n\tcase PTI_BC4_R_SNORM:\t\th10.dxgiformat = 0x51/*DXGI_FORMAT_BC4_SNORM*/;\t\t\t\tDX9FOURCC('B','C','4','S'); break;\n//\tcase PTI_INVALID:\t\t\th10.dxgiformat = 0x52/*DXGI_FORMAT_BC5_TYPELESS*/;\t\t\tbreak;\n\tcase PTI_BC5_RG:\t\t\th10.dxgiformat = 0x53/*DXGI_FORMAT_BC5_UNORM*/;\t\t\t\t/*DX9FOURCC('B','C','5','U');*/ DX9FOURCC('A','T','I','2'); break;\n\tcase PTI_BC5_RG_SNORM:\t\th10.dxgiformat = 0x54/*DXGI_FORMAT_BC5_SNORM*/;\t\t\t\tDX9FOURCC('B','C','5','S'); break;\n\tcase PTI_RGB565:\t\t\th10.dxgiformat = 0x55/*DXGI_FORMAT_B5G6R5_UNORM*/;\t\t\tDX9FMT(16,    0xf800,    0x07e0,    0x001f,    0x0000,DX9RGB);\tbreak;\n\tcase PTI_ARGB1555:\t\t\th10.dxgiformat = 0x56/*DXGI_FORMAT_B5G5R5A1_UNORM*/;\t\tDX9FMT(16,    0x7c00,    0x03e0,    0x001f,    0x8000,DX9RGBA);\tbreak;\n\tcase PTI_BGRA8:\t\t\t\th10.dxgiformat = 0x57/*DXGI_FORMAT_B8G8R8A8_UNORM*/;\t\tDX9FMT(32,0x00ff0000,0x0000ff00,0x000000ff,0xff000000,DX9RGBA);\tbreak;\n\tcase PTI_BGRX8:\t\t\t\th10.dxgiformat = 0x58/*DXGI_FORMAT_B8G8R8X8_UNORM*/;\t\tDX9FMT(32,0x00ff0000,0x0000ff00,0x000000ff,0x00000000,DX9RGB);\tbreak;\t//WARNING: buggy in gimp (ends up alpha=0)\n//\tcase PTI_INVALID:\t\t\th10.dxgiformat = 0x59/*DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORbreak;\n//\tcase PTI_INVALID:\t\t\th10.dxgiformat = 0x5a/*DXGI_FORMAT_B8G8R8A8_TYPELESS*/;\t\tbreak;\n\tcase PTI_BGRA8_SRGB:\t\th10.dxgiformat = 0x5b/*DXGI_FORMAT_B8G8R8A8_UNORM_SRGB*/;\tbreak;\n//\tcase PTI_INVALID:\t\t\th10.dxgiformat = 0x5c/*DXGI_FORMAT_B8G8R8X8_TYPELESS*/;\t\tbreak;\n\tcase PTI_BGRX8_SRGB:\t\th10.dxgiformat = 0x5d/*DXGI_FORMAT_B8G8R8X8_UNORM_SRGB*/;\tbreak;\n//\tcase PTI_INVALID:\t\t\th10.dxgiformat = 0x5e/*DXGI_FORMAT_BC6H_TYPELESS*/;\t\t\tbreak;\n\tcase PTI_BC6_RGB_UFLOAT:\th10.dxgiformat = 0x5f/*DXGI_FORMAT_BC6H_UF16*/;\t\t\t\tbreak;\n\tcase PTI_BC6_RGB_SFLOAT:\th10.dxgiformat = 0x60/*DXGI_FORMAT_BC6H_SF16*/;\t\t\t\tbreak;\n//\tcase PTI_INVALID:\t\t\th10.dxgiformat = 0x61/*DXGI_FORMAT_BC7_TYPELESS*/;\t\t\tbreak;\n\tcase PTI_BC7_RGBA:\t\t\th10.dxgiformat = 0x62/*DXGI_FORMAT_BC7_UNORM*/;\t\t\t\tbreak;\n\tcase PTI_BC7_RGBA_SRGB:\t\th10.dxgiformat = 0x63/*DXGI_FORMAT_BC7_UNORM_SRGB*/;\t\tbreak;\n//\tcase PTI_INVALID:\t\t\th10.dxgiformat = 0x64/*DXGI_FORMAT_AYUV*/;\t\t\t\t\tbreak;\n//\tcase PTI_INVALID:\t\t\th10.dxgiformat = 0x65/*DXGI_FORMAT_Y410*/;\t\t\t\t\tbreak;\n//\tcase PTI_INVALID:\t\t\th10.dxgiformat = 0x66/*DXGI_FORMAT_Y416*/;\t\t\t\t\tbreak;\n//\tcase PTI_INVALID:\t\t\th10.dxgiformat = 0x67/*DXGI_FORMAT_NV12*/;\t\t\t\t\tbreak;\n//\tcase PTI_INVALID:\t\t\th10.dxgiformat = 0x68/*DXGI_FORMAT_P010*/;\t\t\t\t\tbreak;\n//\tcase PTI_INVALID:\t\t\th10.dxgiformat = 0x69/*DXGI_FORMAT_P016*/;\t\t\t\t\tbreak;\n//\tcase PTI_INVALID:\t\t\th10.dxgiformat = 0x6a/*DXGI_FORMAT_420_OPAQUE*/;\t\t\tbreak;\n//\tcase PTI_INVALID:\t\t\th10.dxgiformat = 0x6b/*DXGI_FORMAT_YUY2*/;\t\t\t\t\tbreak;\n//\tcase PTI_INVALID:\t\t\th10.dxgiformat = 0x6c/*DXGI_FORMAT_Y210*/;\t\t\t\t\tbreak;\n//\tcase PTI_INVALID:\t\t\th10.dxgiformat = 0x6d/*DXGI_FORMAT_Y216*/;\t\t\t\t\tbreak;\n//\tcase PTI_INVALID:\t\t\th10.dxgiformat = 0x6e/*DXGI_FORMAT_NV11*/;\t\t\t\t\tbreak;\n//\tcase PTI_INVALID:\t\t\th10.dxgiformat = 0x6f/*DXGI_FORMAT_AI44*/;\t\t\t\t\tbreak;\n//\tcase PTI_INVALID:\t\t\th10.dxgiformat = 0x70/*DXGI_FORMAT_IA44*/;\t\t\t\t\tbreak;\n//\tcase PTI_INVALID:\t\t\th10.dxgiformat = 0x71/*DXGI_FORMAT_P8*/;\t\t\t\t\tbreak;\n//\tcase PTI_INVALID:\t\t\th10.dxgiformat = 0x72/*DXGI_FORMAT_A8P8*/;\t\t\t\t\tbreak;\n\tcase PTI_ARGB4444:\t\t\th10.dxgiformat = 0x73/*DXGI_FORMAT_B4G4R4A4_UNORM*/;\t\tDX9FMT(16,0x00000f00,0x000000f0,0x0000000f,0x0000f000,DX9RGB);\tbreak;\n//\tcase PTI_INVALID:\t\t\th10.dxgiformat = 0x82/*DXGI_FORMAT_P208*/;\t\t\t\t\tbreak;\n//\tcase PTI_INVALID:\t\t\th10.dxgiformat = 0x83/*DXGI_FORMAT_V208*/;\t\t\t\t\tbreak;\n//\tcase PTI_INVALID:\t\t\th10.dxgiformat = 0x84/*DXGI_FORMAT_V408*/;\t\t\t\t\tbreak;\n\tcase PTI_ASTC_4X4_HDR:\t\t//hdr allows more endpoint modes.\n\tcase PTI_ASTC_4X4_LDR:\t\th10.dxgiformat = 0x86/*DXGI_FORMAT_ASTC_4X4_UNORM*/;\t\tbreak;\n\tcase PTI_ASTC_4X4_SRGB:\t\th10.dxgiformat = 0x87/*DXGI_FORMAT_ASTC_4X4_SRGB*/;\t\t\tbreak;\n\tcase PTI_ASTC_5X4_HDR:\t\t//hdr allows more endpoint modes.\n\tcase PTI_ASTC_5X4_LDR:\t\th10.dxgiformat = 0x8a/*DXGI_FORMAT_ASTC_5X4_UNORM*/;\t\tbreak;\n\tcase PTI_ASTC_5X4_SRGB:\t\th10.dxgiformat = 0x8b/*DXGI_FORMAT_ASTC_5X4_SRGB*/;\t\t\tbreak;\n\tcase PTI_ASTC_5X5_HDR:\t\t//hdr allows more endpoint modes.\n\tcase PTI_ASTC_5X5_LDR:\t\th10.dxgiformat = 0x8e/*DXGI_FORMAT_ASTC_5X5_UNORM*/;\t\tbreak;\n\tcase PTI_ASTC_5X5_SRGB:\t\th10.dxgiformat = 0x8f/*DXGI_FORMAT_ASTC_5X5_SRGB*/;\t\t\tbreak;\n\tcase PTI_ASTC_6X5_HDR:\t\t//hdr allows more endpoint modes.\n\tcase PTI_ASTC_6X5_LDR:\t\th10.dxgiformat = 0x92/*DXGI_FORMAT_ASTC_6X5_UNORM*/;\t\tbreak;\n\tcase PTI_ASTC_6X5_SRGB:\t\th10.dxgiformat = 0x93/*DXGI_FORMAT_ASTC_6X5_SRGB*/;\t\t\tbreak;\n\tcase PTI_ASTC_6X6_HDR:\t\t//hdr allows more endpoint modes.\n\tcase PTI_ASTC_6X6_LDR:\t\th10.dxgiformat = 0x96/*DXGI_FORMAT_ASTC_6X6_UNORM*/;\t\tbreak;\n\tcase PTI_ASTC_6X6_SRGB:\t\th10.dxgiformat = 0x97/*DXGI_FORMAT_ASTC_6X6_SRGB*/;\t\t\tbreak;\n\tcase PTI_ASTC_8X5_HDR:\t\t//hdr allows more endpoint modes.\n\tcase PTI_ASTC_8X5_LDR:\t\th10.dxgiformat = 0x9a/*DXGI_FORMAT_ASTC_8X5_UNORM*/;\t\tbreak;\n\tcase PTI_ASTC_8X5_SRGB:\t\th10.dxgiformat = 0x9b/*DXGI_FORMAT_ASTC_8X5_SRGB*/;\t\t\tbreak;\n\tcase PTI_ASTC_8X6_HDR:\t\t//hdr allows more endpoint modes.\n\tcase PTI_ASTC_8X6_LDR:\t\th10.dxgiformat = 0x9e/*DXGI_FORMAT_ASTC_8X6_UNORM*/;\t\tbreak;\n\tcase PTI_ASTC_8X6_SRGB:\t\th10.dxgiformat = 0x9f/*DXGI_FORMAT_ASTC_8X6_SRGB*/;\t\t\tbreak;\n\tcase PTI_ASTC_8X8_HDR:\t\t//hdr allows more endpoint modes.\n\tcase PTI_ASTC_8X8_LDR:\t\th10.dxgiformat = 0xa2/*DXGI_FORMAT_ASTC_8X8_UNORM*/;\t\tbreak;\n\tcase PTI_ASTC_8X8_SRGB:\t\th10.dxgiformat = 0xa3/*DXGI_FORMAT_ASTC_8X8_SRGB*/;\t\t\tbreak;\n\tcase PTI_ASTC_10X5_HDR:\t\t//hdr allows more endpoint modes.\n\tcase PTI_ASTC_10X5_LDR:\t\th10.dxgiformat = 0xa6/*DXGI_FORMAT_ASTC_10X5_UNORM*/;\t\tbreak;\n\tcase PTI_ASTC_10X5_SRGB:\th10.dxgiformat = 0xa7/*DXGI_FORMAT_ASTC_10X5_SRGB*/;\t\tbreak;\n\tcase PTI_ASTC_10X6_HDR:\t\t//hdr allows more endpoint modes.\n\tcase PTI_ASTC_10X6_LDR:\t\th10.dxgiformat = 0xaa/*DXGI_FORMAT_ASTC_10X6_UNORM*/;\t\tbreak;\n\tcase PTI_ASTC_10X6_SRGB:\th10.dxgiformat = 0xab/*DXGI_FORMAT_ASTC_10X6_SRGB*/;\t\tbreak;\n\tcase PTI_ASTC_10X8_HDR:\t\t//hdr allows more endpoint modes.\n\tcase PTI_ASTC_10X8_LDR:\t\th10.dxgiformat = 0xae/*DXGI_FORMAT_ASTC_10X8_UNORM*/;\t\tbreak;\n\tcase PTI_ASTC_10X8_SRGB:\th10.dxgiformat = 0xaf/*DXGI_FORMAT_ASTC_10X8_SRGB*/;\t\tbreak;\n\tcase PTI_ASTC_10X10_HDR:\t//hdr allows more endpoint modes.\n\tcase PTI_ASTC_10X10_LDR:\th10.dxgiformat = 0xb2/*DXGI_FORMAT_ASTC_10X10_UNORM*/;\t\tbreak;\n\tcase PTI_ASTC_10X10_SRGB:\th10.dxgiformat = 0xb3/*DXGI_FORMAT_ASTC_10X10_SRGB*/;\t\tbreak;\n\tcase PTI_ASTC_12X10_HDR:\t//hdr allows more endpoint modes.\n\tcase PTI_ASTC_12X10_LDR:\th10.dxgiformat = 0xb6/*DXGI_FORMAT_ASTC_12X10_UNORM*/;\t\tbreak;\n\tcase PTI_ASTC_12X10_SRGB:\th10.dxgiformat = 0xb7/*DXGI_FORMAT_ASTC_12X10_SRGB*/;\t\tbreak;\n\tcase PTI_ASTC_12X12_HDR:\t//hdr allows more endpoint modes.\n\tcase PTI_ASTC_12X12_LDR:\th10.dxgiformat = 0xba/*DXGI_FORMAT_ASTC_12X12_UNORM*/;\t\tbreak;\n\tcase PTI_ASTC_12X12_SRGB:\th10.dxgiformat = 0xbb/*DXGI_FORMAT_ASTC_12X12_SRGB*/;\t\tbreak;\n#ifdef ASTC3D\n\tcase PTI_ASTC_3X3X3_HDR:\n\tcase PTI_ASTC_4X3X3_HDR:\n\tcase PTI_ASTC_4X4X3_HDR:\n\tcase PTI_ASTC_4X4X4_HDR:\n\tcase PTI_ASTC_5X4X4_HDR:\n\tcase PTI_ASTC_5X5X4_HDR:\n\tcase PTI_ASTC_5X5X5_HDR:\n\tcase PTI_ASTC_6X5X5_HDR:\n\tcase PTI_ASTC_6X6X5_HDR:\n\tcase PTI_ASTC_6X6X6_HDR:\n\tcase PTI_ASTC_3X3X3_LDR:\n\tcase PTI_ASTC_4X3X3_LDR:\n\tcase PTI_ASTC_4X4X3_LDR:\n\tcase PTI_ASTC_4X4X4_LDR:\n\tcase PTI_ASTC_5X4X4_LDR:\n\tcase PTI_ASTC_5X5X4_LDR:\n\tcase PTI_ASTC_5X5X5_LDR:\n\tcase PTI_ASTC_6X5X5_LDR:\n\tcase PTI_ASTC_6X6X5_LDR:\n\tcase PTI_ASTC_6X6X6_LDR:\n\tcase PTI_ASTC_3X3X3_SRGB:\n\tcase PTI_ASTC_4X3X3_SRGB:\n\tcase PTI_ASTC_4X4X3_SRGB:\n\tcase PTI_ASTC_4X4X4_SRGB:\n\tcase PTI_ASTC_5X4X4_SRGB:\n\tcase PTI_ASTC_5X5X4_SRGB:\n\tcase PTI_ASTC_5X5X5_SRGB:\n\tcase PTI_ASTC_6X5X5_SRGB:\n\tcase PTI_ASTC_6X6X5_SRGB:\n\tcase PTI_ASTC_6X6X6_SRGB:\treturn false;\t//no dxgi format assigned that we know of\n#endif\n\n\tcase PTI_RGBX8:\t\t\t\tDX9FMT(32,0x000000ff,0x0000ff00,0x00ff0000,0x00000000,DX9RGB);\tbreak;\t//WARNING: buggy in gimp (ends up alpha=0)\n\tcase PTI_RGB8:\t\t\t\tDX9FMT(24,0x000000ff,0x0000ff00,0x00ff0000,0x00000000,DX9RGB);\tbreak;\n\tcase PTI_BGR8:\t\t\t\tDX9FMT(24,0x00ff0000,0x0000ff00,0x000000ff,0x00000000,DX9RGB);\tbreak;\n\tcase PTI_L8:\t\t\t\tDX9FMT(8,0x000000ff,0x00000000,0x00000000,0x00000000,DX9LUM);\tbreak;\n\tcase PTI_L8A8:\t\t\t\tDX9FMT(16,0x000000ff,0x00000000,0x00000000,0x0000ff00,DX9LUMALPHA);\tbreak;\n\tcase PTI_RGBA5551:\t\t\tDX9FMT(16,0x0000f800,0x000007c0,0x0000003e,0x00000001,DX9RGBA);\tbreak;\t//WARNING: buggy in gimp (ends up greyscale)\n\tcase PTI_RGBA4444:\t\t\tDX9FMT(16,0x0000f000,0x00000f00,0x000000f0,0x0000000f,DX9RGBA);\tbreak;\t//WARNING: buggy in gimp (ends up greyscale)\n\n\tcase PTI_ETC1_RGB8:\t\t\t//fall through (etc2 is backwards compatible)\n\tcase PTI_ETC2_RGB8:\t\t\tDX9FOURCC('E','T','C','2');\tbreak;\t//not an official format, but we can understand it\n\tcase PTI_ETC2_RGB8_SRGB:\n\tcase PTI_ETC2_RGB8A1:\n\tcase PTI_ETC2_RGB8A1_SRGB:\n\tcase PTI_ETC2_RGB8A8:\n\tcase PTI_ETC2_RGB8A8_SRGB:\n\tcase PTI_EAC_R11:\n\tcase PTI_EAC_R11_SNORM:\n\tcase PTI_EAC_RG11:\n\tcase PTI_EAC_RG11_SNORM:\treturn false;\t//unsupported\n\n\tcase PTI_L8_SRGB:\t\t\treturn false;\t//unsupported\n\tcase PTI_L8A8_SRGB:\t\t\treturn false;\t//unsupported\n\tcase PTI_RGB8_SRGB:\t\t\treturn false;\t//unsupported\n\tcase PTI_BGR8_SRGB:\t\t\treturn false;\t//unsupported\n\tcase PTI_DEPTH24:\t\t\treturn false;\t//unsupported, should fall back on dx9 formats.\n\tcase PTI_P8:\t\t\t\treturn false;\t//unsupported, technically R8_UNORM but would load back in wrongly.\n\n#ifdef FTE_TARGET_WEB\n\tcase PTI_WHOLEFILE:\n#endif\n\tcase PTI_EMULATED:\n\tcase PTI_MAX:\n\t\treturn false;\n\n\tsafedefault:\t//don't enable in debug builds, so we get warnings for any cases being missed.\n\t\treturn false;\n\t}\n\n\t//truncate the mip chain if they're dodgy sizes.\n\tfor (mipnum = 1; mipnum < h9.dwMipMapCount; mipnum++)\n\t{\n\t\tsize_t m = mipnum;\n\t\tsize_t p = (mipnum-1);\n\t\tif (mips->mip[m].width != max(1,(mips->mip[p].width)>>1) ||\n\t\t\tmips->mip[m].height != max(1,(mips->mip[p].height)>>1))\n\t\t{\n\t\t\th9.dwMipMapCount = mipnum;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tif (strchr(filename, '*') || strchr(filename, ':'))\n\t\treturn false;\n\n\tif (h9.ddpfPixelFormat.dwFlags && h10.arraysize == 1)\n\t\th10.dxgiformat = 0;\t//skip the dx10 header if we can express it as bitmasks. this generally gives better support in external tools (especially gimp)\n\telse if (h10.dxgiformat)\n\t{\n\t\th9.ddpfPixelFormat.dwFlags = 0;\t//don't get confused. always one or the other.\n\t\tDX9FOURCC('D','X','1','0');\n\t}\n\telse\n\t\treturn false;\t//legacy-only arrays are not supported.\n\n\tfile = FS_OpenVFS(filename, \"wb\", FS_GAMEONLY);\n\tif (!file)\n\t\treturn false;\n\tVFS_WRITE(file, \"DDS \", 4);\n\tfor (endian = (int*)&h9; endian < (int*)(&h9+1); endian++)\n\t\t*endian = LittleLong(*endian);\n\tVFS_WRITE(file, &h9, sizeof(h9));\n\tif (h10.dxgiformat)\n\t{\n\t\tfor (endian = (int*)&h10; endian < (int*)(&h10+1); endian++)\n\t\t\t*endian = LittleLong(*endian);\n\t\tVFS_WRITE(file, &h10, sizeof(h10));\n\t}\n\n\t//our internal state uses width*height*layers for each mip level (gl-friendly).\n\t//DDS requires a0m0, a0m1, a1m0, a1m1, so reorder with two nested loops\n\tfor (a = 0; a < arraysize; a++)\n\t{\n\t\tfor (mipnum = 0; mipnum < h9.dwMipMapCount; mipnum++)\n\t\t{\n\t\t\tsize_t sz = mips->mip[mipnum].datasize / arraysize;\n\t\t\tVFS_WRITE(file, (qbyte*)mips->mip[mipnum].data + sz*a, sz);\n\t\t}\n\t}\n\n\tVFS_CLOSE(file);\n\treturn true;\n}\n#endif\n\n#ifdef IMAGEFMT_BLP\nstatic struct pendingtextureinfo *Image_ReadBLPFile(unsigned int flags, const char *fname, qbyte *filedata, size_t filesize)\n{\n\t//FIXME: cba with endian.\n\tint miplevel;\n\tint w, h, i;\n\tstruct blp_s\n\t{\n\t\tchar blp2[4];\n\t\tint type;\n\t\tqbyte encoding;\n\t\tqbyte alphadepth;\n\t\tqbyte alphaencoding;\n\t\tqbyte hasmips;\n\t\tunsigned int xres;\n\t\tunsigned int yres;\n\t\tunsigned int mipoffset[16];\n\t\tunsigned int mipsize[16];\n\t\tunsigned int palette[256];\n\t} *blp;\n\tunsigned int *tmpmem = NULL;\n\tunsigned char *in;\n\tunsigned int inlen;\n\n\tstruct pendingtextureinfo *mips;\n\n\tblp = (void*)filedata;\n\n\tif (memcmp(blp->blp2, \"BLP2\", 4) || blp->type != 1)\n\t\treturn NULL;\n\n\tmips = Z_Malloc(sizeof(*mips));\n\tmips->mipcount = 0;\n\tmips->type = PTI_2D;\n\n\tw = LittleLong(blp->xres);\n\th = LittleLong(blp->yres);\n\n\tif (blp->encoding == 2)\n\t{\n\t\t//s3tc/dxt\n\t\tswitch(blp->alphaencoding)\n\t\t{\n\t\tdefault:\n\t\tcase 0: //dxt1\n\t\t\tif (blp->alphadepth)\n\t\t\t\tmips->encoding = PTI_BC1_RGBA;\n\t\t\telse\n\t\t\t\tmips->encoding = PTI_BC1_RGB;\n\t\t\tbreak;\n\t\tcase 1: //dxt2/3\n\t\t\tmips->encoding = PTI_BC2_RGBA;\n\t\t\tbreak;\n\t\tcase 7: //dxt4/5\n\t\t\tmips->encoding = PTI_BC3_RGBA;\n\t\t\tbreak;\n\t\t}\n\t\tfor (miplevel = 0; miplevel < 16; )\n\t\t{\n\t\t\tif (!w && !h)\t//shrunk to no size\n\t\t\t\tbreak;\n\t\t\tif (!w)\n\t\t\t\tw = 1;\n\t\t\tif (!h)\n\t\t\t\th = 1;\n\t\t\tif (!blp->mipoffset[miplevel] || !blp->mipsize[miplevel] || blp->mipoffset[miplevel]+blp->mipsize[miplevel] > filesize)\t//no data\n\t\t\t\tbreak;\n\t\t\tmips->mip[miplevel].width = w;\n\t\t\tmips->mip[miplevel].height = h;\n\t\t\tmips->mip[miplevel].depth = 1;\n\t\t\tmips->mip[miplevel].data = filedata + LittleLong(blp->mipoffset[miplevel]);\n\t\t\tmips->mip[miplevel].datasize = LittleLong(blp->mipsize[miplevel]);\n\n\t\t\tmiplevel++;\n\t\t\tif (!blp->hasmips || (flags & IF_NOMIPMAP))\n\t\t\t\tbreak;\n\t\t\tw >>= 1;\n\t\t\th >>= 1;\n\t\t}\n\t\tmips->mipcount = miplevel;\n\t\tmips->extrafree = filedata;\n\t}\n\telse\n\t{\n\t\tmips->encoding = PTI_BGRA8;\n\t\tfor (miplevel = 0; miplevel < 16; )\n\t\t{\n\t\t\tif (!w && !h)\n\t\t\t\tbreak;\n\t\t\tif (!w)\n\t\t\t\tw = 1;\n\t\t\tif (!h)\n\t\t\t\th = 1;\n\t\t\t//if we ran out of mips to load, give up.\n\t\t\tif (!blp->mipoffset[miplevel] || !blp->mipsize[miplevel] || blp->mipoffset[miplevel]+blp->mipsize[miplevel] > filesize)\n\t\t\t{\n\t\t\t\t//if we got at least one mip, cap the mips. might help save some ram? naaah...\n\t\t\t\t//if this is the first mip, well, its completely fucked.\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tin = filedata + LittleLong(blp->mipoffset[miplevel]);\n\t\t\tinlen = LittleLong(blp->mipsize[miplevel]);\n\n\t\t\tif (inlen != w*h+((w*h*blp->alphadepth+7)>>3))\n\t\t\t{\n\t\t\t\tCon_Printf(\"%s: mip level %i does not contain the correct amount of data\\n\", fname, miplevel);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tmips->mip[miplevel].width = w;\n\t\t\tmips->mip[miplevel].height = h;\n\t\t\tmips->mip[miplevel].depth = 1;\n\t\t\tmips->mip[miplevel].datasize = 4*w*h;\n\t\t\tmips->mip[miplevel].data = tmpmem = BZ_Malloc(4*w*h);\n\t\t\tmips->mip[miplevel].needfree = true;\n\n\t\t\t//load the rgb data first (8-bit paletted)\n\t\t\tfor (i = 0; i < w*h; i++)\n\t\t\t\ttmpmem[i] = blp->palette[*in++] | 0xff000000;\n\n\t\t\t//and then change the alpha bits accordingly.\n\t\t\tswitch(blp->alphadepth)\n\t\t\t{\n\t\t\tcase 0:\n\t\t\t\t//BGRX palette, 8bit\n\t\t\t\tbreak;\n\t\t\tcase 1:\n\t\t\t\t//BGRX palette, 8bit\n\t\t\t\t//1bit trailing alpha\n\t\t\t\tfor (i = 0; i < w*h; i+=8, in++)\n\t\t\t\t{\n\t\t\t\t\ttmpmem[i+0] = (tmpmem[i+0] & 0xffffff) | ((*in&0x01)?0xff000000:0);\n\t\t\t\t\ttmpmem[i+1] = (tmpmem[i+1] & 0xffffff) | ((*in&0x02)?0xff000000:0);\n\t\t\t\t\ttmpmem[i+2] = (tmpmem[i+2] & 0xffffff) | ((*in&0x04)?0xff000000:0);\n\t\t\t\t\ttmpmem[i+3] = (tmpmem[i+3] & 0xffffff) | ((*in&0x08)?0xff000000:0);\n\t\t\t\t\ttmpmem[i+4] = (tmpmem[i+4] & 0xffffff) | ((*in&0x10)?0xff000000:0);\n\t\t\t\t\ttmpmem[i+5] = (tmpmem[i+5] & 0xffffff) | ((*in&0x20)?0xff000000:0);\n\t\t\t\t\ttmpmem[i+6] = (tmpmem[i+6] & 0xffffff) | ((*in&0x40)?0xff000000:0);\n\t\t\t\t\ttmpmem[i+7] = (tmpmem[i+7] & 0xffffff) | ((*in&0x80)?0xff000000:0);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase 4:\n\t\t\t\t//BGRX palette, 8bit\n\t\t\t\t//4bit trailing alpha\n\t\t\t\tfor (i = 0; i < w*h; i++)\n\t\t\t\t\ttmpmem[i] = (tmpmem[i] & 0xffffff) | (*in++*0x11000000);\n\t\t\t\tbreak;\n\t\t\tcase 8:\n\t\t\t\t//BGRX palette, 8bit\n\t\t\t\t//8bit trailing alpha\n\t\t\t\tfor (i = 0; i < w*h; i++)\n\t\t\t\t\ttmpmem[i] = (tmpmem[i] & 0xffffff) | (*in++<<24);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tmiplevel++;\n\t\t\tif (!blp->hasmips || (flags & IF_NOMIPMAP))\n\t\t\t\tbreak;\n\t\t\tw = w>>1;\n\t\t\th = h>>1;\n\t\t}\n\t\tBZ_Free(filedata);\n\t\tmips->mipcount = miplevel;\n\t}\n\treturn mips;\n}\n#endif\n\n#ifdef IMAGEFMT_PVR\n\ntypedef struct gbix {\n\tuint32_t magic;\n\tuint32_t len;\n} gbix_t;\n\ntypedef struct pvr {\n\tuint32_t magic;\n\tuint32_t len_file;\n\tuint32_t type;\n\tuint16_t width;\n\tuint16_t height;\n} pvr_t;\n\ntypedef uint32_t (*pvr_pixel_func_t)(uint16_t color);\ntypedef qbyte *(*pvr_image_func_t)(pvr_t *pvr, int offset, qboolean detwiddle, pvr_pixel_func_t pixel_func);\n\nenum {\n\tPVR_PIXEL_TYPE_ARGB1555 = 0,\n\tPVR_PIXEL_TYPE_RGB565 = 1,\n\tPVR_PIXEL_TYPE_ARGB4444 = 2\n};\n\nenum {\n\tPVR_IMAGE_TYPE_TWIDDLED = 1,\n\tPVR_IMAGE_TYPE_TWIDDLED_MM = 2,\n\tPVR_IMAGE_TYPE_VQ = 3,\n\tPVR_IMAGE_TYPE_VQ_MM = 4,\n\tPVR_IMAGE_TYPE_RECTANGULAR = 9,\n\tPVR_IMAGE_TYPE_RECTANGULAR_MM = 10\n};\n\nstatic int pvr_log2(int x)\n{\n\tswitch (x)\n\t{\n\t\tcase 8: return 3;\n\t\tcase 16: return 4;\n\t\tcase 32: return 5;\n\t\tcase 64: return 6;\n\t\tcase 128: return 7;\n\t\tcase 256: return 8;\n\t\tcase 512: return 9;\n\t\tcase 1024: return 10;\n\t\tdefault: return -1;\n\t}\n}\n\nstatic int pvr_detwiddle(int x, int y, int w, int h)\n{\n\tint wmax, hmax;\n\tint i, idx = 0;\n\n\twmax = pvr_log2(w);\n\thmax = pvr_log2(h);\n\n\tif (wmax < 0 || hmax < 0)\n\t\treturn -1;\n\n\tfor (i = 0; i < 10; i++)\n\t{\n\t\tif (i < wmax && i < hmax)\n\t\t{\n\t\t\tidx |= ((y >> i) & 1) << (i * 2 + 0);\n\t\t\tidx |= ((x >> i) & 1) << (i * 2 + 1);\n\t\t}\n\t\telse if (i < wmax)\n\t\t{\n\t\t\tidx |= ((x >> i) & 1) << (i + hmax);\n\t\t}\n\t\telse if (i < hmax)\n\t\t{\n\t\t\tidx |= ((y >> i) & 1) << (i + wmax);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tbreak;\n\t\t}\n\t}\n\n\treturn idx;\n}\n\nstatic uint32_t pvr_argb1555_to_rgba8888(uint16_t color)\n{\n\tuint8_t r, g, b, a;\n\tr = ((color >> 10) & 31) << 4;\n\tg = ((color >> 5) & 31) << 4;\n\tb = ((color >> 0) & 31) << 4;\n\ta = ((color >> 15) & 1) * 255;\n\treturn (a << 24) | (b << 16) | (g << 8) | r;\n}\n\nstatic uint32_t pvr_rgb565_to_rgba8888(uint16_t color)\n{\n\tuint8_t r, g, b, a;\n\tr = ((color >> 11) & 31) << 3;\n\tg = ((color >> 5) & 63) << 2;\n\tb = ((color >> 0) & 31) << 3;\n\ta = 255;\n\treturn (a << 24) | (b << 16) | (g << 8) | r;\n}\n\nstatic uint32_t pvr_argb4444_to_rgba8888(uint16_t color)\n{\n\tuint8_t r, g, b, a;\n\tr = ((color >> 8) & 15) << 4;\n\tg = ((color >> 4) & 15) << 4;\n\tb = ((color >> 0) & 15) << 4;\n\ta = ((color >> 12) & 15) | 0xF;\n\treturn (a << 24) | (b << 16) | (g << 8) | r;\n}\n\nstatic int pvr_mm_offset(int w)\n{\n\tswitch (w)\n\t{\n\t\tcase 1: return 0x00006;\n\t\tcase 2: return 0x00008;\n\t\tcase 4: return 0x00010;\n\t\tcase 8: return 0x00030;\n\t\tcase 16: return 0x000B0;\n\t\tcase 32: return 0x002B0;\n\t\tcase 64: return 0x00AB0;\n\t\tcase 128: return 0x02AB0;\n\t\tcase 256: return 0x0AAB0;\n\t\tcase 512: return 0x2AAB0;\n\t\tcase 1024: return 0xAAAB0;\n\t\tdefault: return -1;\n\t}\n}\n\nstatic int pvr_mm_offset_vq(int w)\n{\n\tswitch (w)\n\t{\n\t\tcase 1: return 0x00000;\n\t\tcase 2: return 0x00001;\n\t\tcase 4: return 0x00002;\n\t\tcase 8: return 0x00006;\n\t\tcase 16: return 0x00016;\n\t\tcase 32: return 0x00056;\n\t\tcase 64: return 0x00156;\n\t\tcase 128: return 0x00556;\n\t\tcase 256: return 0x01556;\n\t\tcase 512: return 0x05556;\n\t\tcase 1024: return 0x15556;\n\t\tdefault: return -1;\n\t}\n}\n\nstatic qbyte *pvr_decode_mm(pvr_t *pvr, pvr_image_func_t image_func, pvr_pixel_func_t pixel_func, qboolean vq, qboolean detwiddle)\n{\n\tint offset = vq ? pvr_mm_offset_vq(pvr->width) : pvr_mm_offset(pvr->width);\n\treturn image_func(pvr, offset, detwiddle, pixel_func);\n}\n\nstatic qbyte *pvr_decode(pvr_t *pvr, int offset, qboolean detwiddle, pvr_pixel_func_t pixel_func)\n{\n\tint x, y;\n\tuint16_t *rgb16;\n\tuint32_t *rgba32;\n\tqbyte *ret;\n\n\tret = (qbyte *)BZ_Malloc(pvr->width * pvr->height * (pixel_func ? sizeof(uint32_t) : sizeof(uint16_t)));\n\trgb16 = (uint16_t *)ret;\n\trgba32 = (uint32_t *)ret;\n\n\tfor (y = 0; y < pvr->height; y++)\n\t{\n\t\tfor (x = 0; x < pvr->width; x++)\n\t\t{\n\t\t\tuint16_t color;\n\t\t\tint ofs;\n\t\t\tif (detwiddle)\n\t\t\t\tofs = offset + pvr_detwiddle(x, y, pvr->width, pvr->height) * 2;\n\t\t\telse\n\t\t\t\tofs = offset + (y * pvr->width + x) * 2;\n\n\t\t\tif (ofs < 0)\n\t\t\t{\n\t\t\t\tBZ_Free(ret);\n\t\t\t\treturn NULL;\n\t\t\t}\n\n\t\t\tcolor = *(uint16_t *)(((qbyte *)(pvr + 1)) + ofs);\n\n\t\t\tif (pixel_func)\n\t\t\t\trgba32[y * pvr->width + x] = pixel_func(color);\n\t\t\telse\n\t\t\t\trgb16[y * pvr->width + x] = color;\n\t\t}\n\t}\n\n\treturn ret;\n}\n\nstatic qbyte *pvr_decode_vq(pvr_t *pvr, int offset, qboolean detwiddle, pvr_pixel_func_t pixel_func)\n{\n\tint x, y;\n\tuint16_t *rgb16;\n\tuint32_t *rgba32;\n\tuint16_t *codebook;\n\tuint8_t *indices;\n\tqbyte *ret;\n\n\tret = (qbyte *)BZ_Malloc(pvr->width * pvr->height * (pixel_func ? sizeof(uint32_t) : sizeof(uint16_t)));\n\trgb16 = (uint16_t *)ret;\n\trgba32 = (uint32_t *)ret;\n\n\tcodebook = (uint16_t *)(pvr + 1);\n\tindices = ((uint8_t *)(codebook + 1024)) + offset;\n\n\tfor (y = 0; y < pvr->height / 2; y++)\n\t{\n\t\tfor (x = 0; x < pvr->width / 2; x++)\n\t\t{\n\t\t\tuint16_t *colors;\n\t\t\tint a, b, c, d;\n\t\t\tint idx;\n\t\t\tif (detwiddle)\n\t\t\t\tidx = pvr_detwiddle(x, y, pvr->width, pvr->height);\n\t\t\telse\n\t\t\t\tidx = y * (pvr->width / 2) + x;\n\n\t\t\tif (idx < 0)\n\t\t\t{\n\t\t\t\tBZ_Free(ret);\n\t\t\t\treturn NULL;\n\t\t\t}\n\n\t\t\tcolors = &codebook[indices[idx] * 4];\n\n\t\t\ta = ((y * 2) + 0) * pvr->width + ((x * 2) + 0);\n\t\t\tb = ((y * 2) + 1) * pvr->width + ((x * 2) + 0);\n\t\t\tc = ((y * 2) + 0) * pvr->width + ((x * 2) + 1);\n\t\t\td = ((y * 2) + 1) * pvr->width + ((x * 2) + 1);\n\n\t\t\tif (pixel_func)\n\t\t\t{\n\t\t\t\trgba32[a] = pixel_func(colors[0]);\n\t\t\t\trgba32[b] = pixel_func(colors[1]);\n\t\t\t\trgba32[c] = pixel_func(colors[2]);\n\t\t\t\trgba32[d] = pixel_func(colors[3]);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\trgb16[a] = colors[0];\n\t\t\t\trgb16[b] = colors[1];\n\t\t\t\trgb16[c] = colors[2];\n\t\t\t\trgb16[d] = colors[3];\n\t\t\t}\n\t\t}\n\t}\n\n\treturn ret;\n}\n\nqbyte *ReadPVRFile(qbyte *buf, int len, int *width, int *height, uploadfmt_t *format, qboolean force_rgba8)\n{\n\tpvr_t *pvr;\n\tint pixel_type, image_type;\n\tpvr_pixel_func_t pixel_func;\n\tqbyte *ret = NULL;\n\n\t// skip gbix\n\tif (memcmp(buf, \"GBIX\", 4) == 0)\n\t{\n\t\tgbix_t *gbix = (gbix_t *)buf;\n\t\tbuf += sizeof(gbix_t) + LittleLong(gbix->len);\n\t}\n\n\t// check magic identifier\n\tif (memcmp(buf, \"PVRT\", 4) != 0)\n\t\treturn NULL;\n\n\t// fix up header\n\tpvr = (pvr_t *)buf;\n\tpvr->len_file = LittleLong(pvr->len_file);\n\tpvr->type = LittleLong(pvr->type);\n\tpvr->width = LittleShort(pvr->width);\n\tpvr->height = LittleShort(pvr->height);\n\n\t// break out type values\n\tpixel_type = pvr->type & 0xFF;\n\timage_type = (pvr->type & 0xFF00) >> 8;\n\n\t// get pixel function\n\tif (force_rgba8)\n\t{\n\t\tswitch (pixel_type)\n\t\t{\n\t\t\tcase PVR_PIXEL_TYPE_ARGB1555: pixel_func = pvr_argb1555_to_rgba8888; break;\n\t\t\tcase PVR_PIXEL_TYPE_RGB565: pixel_func = pvr_rgb565_to_rgba8888; break;\n\t\t\tcase PVR_PIXEL_TYPE_ARGB4444: pixel_func = pvr_argb4444_to_rgba8888; break;\n\t\t\tdefault: return NULL;\n\t\t}\n\t}\n\telse\n\t{\n\t\tpixel_func = NULL;\n\t}\n\n\t// decompress image\n\tswitch (image_type)\n\t{\n\t\tcase PVR_IMAGE_TYPE_TWIDDLED:\n\t\t{\n\t\t\tret = pvr_decode(pvr, 0, true, pixel_func);\n\t\t\tbreak;\n\t\t}\n\t\tcase PVR_IMAGE_TYPE_TWIDDLED_MM:\n\t\t{\n\t\t\tret = pvr_decode_mm(pvr, pvr_decode, pixel_func, false, true);\n\t\t\tbreak;\n\t\t}\n\t\tcase PVR_IMAGE_TYPE_VQ:\n\t\t{\n\t\t\tret = pvr_decode_vq(pvr, 0, true, pixel_func);\n\t\t\tbreak;\n\t\t}\n\t\tcase PVR_IMAGE_TYPE_VQ_MM:\n\t\t{\n\t\t\tret = pvr_decode_mm(pvr, pvr_decode_vq, pixel_func, true, true);\n\t\t\tbreak;\n\t\t}\n\t\tcase PVR_IMAGE_TYPE_RECTANGULAR:\n\t\t{\n\t\t\tret = pvr_decode(pvr, 0, false, pixel_func);\n\t\t\tbreak;\n\t\t}\n\t\tcase PVR_IMAGE_TYPE_RECTANGULAR_MM:\n\t\t{\n\t\t\tret = pvr_decode_mm(pvr, pvr_decode, pixel_func, false, false);\n\t\t\tbreak;\n\t\t}\n\t\tdefault:\n\t\t{\n\t\t\treturn NULL;\n\t\t}\n\t}\n\n\t// something failed\n\tif (!ret)\n\t\treturn NULL;\n\n\t// return stuff\n\tif (width)\n\t\t*width = pvr->width;\n\tif (height)\n\t\t*height = pvr->height;\n\tif (format)\n\t{\n\t\tif (force_rgba8)\n\t\t{\n\t\t\t*format = PTI_RGBA8;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tswitch (pixel_type)\n\t\t\t{\n\t\t\t\tcase PVR_PIXEL_TYPE_ARGB1555: *format = PTI_ARGB1555; break;\n\t\t\t\tcase PVR_PIXEL_TYPE_RGB565: *format = PTI_RGB565; break;\n\t\t\t\tcase PVR_PIXEL_TYPE_ARGB4444: *format = PTI_ARGB4444; break;\n\t\t\t}\n\t\t}\n\t}\n\treturn ret;\n}\n\n#endif // IMAGEFMT_PVR\n\n//This is for the version command\nvoid Image_PrintInputFormatVersions(void)\n{\n\tint i;\n#ifndef S_COLOR_YELLOW\n\t#define S_COLOR_YELLOW \"\"\n\t#define S_COLOR_WHITE \"\"\n\t#define S_COLOR_TRANS \"\"\n#endif\n\tCon_Printf(S_COLOR_YELLOW\"Image Formats:\"S_COLOR_WHITE);\n\n\t#ifdef IMAGEFMT_DDS\n\t\tCon_Printf(\" dds\");\n\t\t#ifndef DECOMPRESS_S3TC\n\t\t\tCon_Printf(\"(hw-only)\");\n\t\t#endif\n\t#endif\n\t#ifdef IMAGEFMT_KTX\n\t\tCon_Printf(\" ktx\");\n\t#endif\n\t#ifdef IMAGEFMT_TGA\n\t\tCon_Printf(\" tga\");\n\t#endif\n\t#ifdef AVAIL_PNGLIB\n\t\tCon_Printf(\" png\");\n\t\t#ifdef DYNAMIC_LIBPNG\n\t\t\tif (!LIBPNG_LOADED())\n\t\t\t\tCon_Printf(S_COLOR_TRANS\"(unavailable, %s)\", PNG_LIBPNG_VER_STRING);\n\t\t\telse\n\t\t\t\tCon_Printf(S_COLOR_TRANS\"(dynamic, %s)\", PNG_LIBPNG_VER_STRING);\n\t\t#else\n\t\t\tCon_Printf(S_COLOR_TRANS\"(%s)\", PNG_LIBPNG_VER_STRING);\n\t\t#endif\n\t#endif\n\t#ifdef IMAGEFMT_BMP\n\t\tCon_Printf(\" bmp+ico\");\n\t#endif\n\t#ifdef AVAIL_JPEGLIB\n\t\tCon_Printf(\" jpeg\");\n\t\t#ifdef DYNAMIC_LIBJPEG\n\t\t\tif (!LIBJPEG_LOADED())\n\t\t\t\tCon_Printf(S_COLOR_TRANS\"(unavailable, %s)\", PNG_LIBPNG_VER_STRING);\n\t\t\telse\n\t\t\t\tCon_Printf(S_COLOR_TRANS\"(dynamic, %i, %d series)\", JPEG_LIB_VERSION, ( JPEG_LIB_VERSION / 10 ) );\n\t\t#else\n\t\t\tCon_Printf(S_COLOR_TRANS\"(%i, %d series)\", JPEG_LIB_VERSION, ( JPEG_LIB_VERSION / 10 ) );\n\t\t#endif\n\t#endif\n\t#ifdef IMAGEFMT_BLP\n\t\tCon_Printf(\" BLP\");\n\t#endif\n\t#ifdef IMAGEFMT_PBM\n\t\tCon_Printf(\" pfm+pbm+pgm+ppm\"/*\"+pam\"*/);\n\t#endif\n\t#ifdef IMAGEFMT_PSD\n\t\tCon_Printf(\" psd\");\n\t#endif\n\t#ifdef IMAGEFMT_HDR\n\t\tCon_Printf(\" hdr\");\n\t#endif\n\t#ifdef IMAGEFMT_ASTC\n\t\tCon_Printf(\" astc\");\n\t\t#ifndef DECOMPRESS_ASTC\n\t\t\tCon_Printf(\"(hw-only)\");\n\t\t#endif\n\t#endif\n\t#ifdef IMAGEFMT_PKM\n\t\tCon_Printf(\" pkm\");\n\t#endif\n\t#ifdef IMAGEFMT_EXR\n\t\tCon_Printf(\" exr\");\n\t\tif (!exr.handle)\n\t\t\tCon_Printf(S_COLOR_TRANS\"(unavailable)\");\n\t#endif\n\t#ifdef IMAGEFMT_PCX\n\t\tCon_Printf(\" pcx\");\n\t#endif\n\n\t#ifdef STB_IMAGE_IMPLEMENTATION\n\t\t#ifdef STBI_ONLY_PNG\n\t\t\tCon_Printf(\" png\"S_COLOR_TRANS\"(stbi)\");\n\t\t#endif\n\t\t#ifdef STBI_ONLY_JPEG\n\t\t\tCon_Printf(\" jpeg\"S_COLOR_TRANS\"(stbi)\");\n\t\t#endif\n\t\t#ifdef STBI_ONLY_BMP\n\t\t\tCon_Printf(\" bmp\"S_COLOR_TRANS\"(stbi)\");\n\t\t#endif\n\t\t#ifdef STBI_ONLY_PSD\n\t\t\tCon_Printf(\" psd\"S_COLOR_TRANS\"(stbi)\");\n\t\t#endif\n\t\t#ifdef STBI_ONLY_TGA\n\t\t\tCon_Printf(\" tga\"S_COLOR_TRANS\"(stbi)\");\n\t\t#endif\n\t\t#ifdef STBI_ONLY_GIF\n\t\t\tCon_Printf(\" gif\"S_COLOR_TRANS\"(stbi)\");\n\t\t#endif\n\t\t#ifdef STBI_ONLY_HDR\n\t\t\tCon_Printf(\" hdr\"S_COLOR_TRANS\"(stbi)\");\n\t\t#endif\n\t\t#ifdef STBI_ONLY_PIC\n\t\t\tCon_Printf(\" pic\"S_COLOR_TRANS\"(stbi)\");\n\t\t#endif\n\t\t#ifdef STBI_ONLY_PNM\n\t\t\tCon_Printf(\" pnm\"S_COLOR_TRANS\"(stbi)\");\n\t\t#endif\n\t#endif\n\n\t#ifdef IMAGEFMT_LMP\n\t\tCon_Printf(\" lmp\");\n\t#endif\n\n\t#ifdef IMAGEFMT_PVR\n\t\tCon_Printf(\" pvr\");\n\t#endif\n\n\t//now properly registered ones.\n\tfor (i = 0; i < imageloader_count;  i++)\n\t\tCon_Printf(\" ^[%s^]\", imageloader[i].funcs->loadername);\n\n\tCon_Printf(\"\\n\");\n}\n\n//if force_rgba8 then it guarentees rgba8 or rgbx8, otherwise can return l8, etc\nqbyte *ReadRawImageFile(qbyte *buf, int len, int *width, int *height, uploadfmt_t *format, qboolean force_rgba8, const char *fname)\n{\n\tsize_t l, i;\n\tqbyte *data;\n\t*format = PTI_RGBX8;\n#ifdef IMAGEFMT_TGA\n\tif ((data = ReadTargaFile(buf, len, width, height, format, false, force_rgba8?PTI_RGBA8:PTI_INVALID)))\n\t\treturn data;\n#endif\n\n#ifdef AVAIL_PNGLIB\n\tif (len > 4 && (buf[0] == 137 && buf[1] == 'P' && buf[2] == 'N' && buf[3] == 'G') && (data = ReadPNGFile(fname, buf, len, width, height, format, force_rgba8)))\n\t\treturn data;\n#endif\n#ifdef AVAIL_JPEGLIB\n\t//jpeg jfif only.\n\tif (len > 4 && (buf[0] == 0xff && buf[1] == 0xd8 && buf[2] == 0xff /*&& buf[3] == 0xe0*/) && (data = ReadJPEGFile(buf, len, width, height)))\n\t{\n\t\t*format = PTI_RGBX8;\n\t\treturn data;\n\t}\n#endif\n#ifdef IMAGEFMT_PCX\n\tif ((data = ReadPCXFile(buf, len, width, height)))\n\t{\n\t\t*format = PTI_RGBA8;\n\t\tTRACE((\"dbg: ReadRawImageFile: pcx\\n\"));\n\t\treturn data;\n\t}\n#endif\n\n#ifdef IMAGEFMT_BMP\n\tif (len > 2 && (buf[0] == 'B' && buf[1] == 'M') && (data = ReadBMPFile(buf, len, width, height)))\n\t{\n\t\t*format = PTI_RGBA8;\n\t\tTRACE((\"dbg: ReadRawImageFile: bmp\\n\"));\n\t\treturn data;\n\t}\n\n\tif (len > 6 && buf[0]==0&&buf[1]==0 && buf[2]==1&&buf[3]==0 && (data = ReadICOFile(fname, buf, len, width, height, force_rgba8?NULL:format)))\n\t{\n\t\tTRACE((\"dbg: ReadRawImageFile: ico\\n\"));\n\t\treturn data;\n\t}\n#endif\n\n#ifdef IMAGEFMT_PVR\n\tif ((data = ReadPVRFile(buf, len, width, height, format, force_rgba8)))\n\t\treturn data;\n#endif\n\n#ifdef IMAGEFMT_PBM\n\tif (!force_rgba8 && len > 2 && buf[0] == 'P' && ((buf[1] >= '1' && buf[1] <= '7') || buf[1] == 'F' || buf[1] == 'f') && (data = ReadPBMFile(buf, len, fname, width, height, format)))\n\t\treturn data;\n#endif\n#ifdef IMAGEFMT_HDR\n\tif (!force_rgba8 && len > 10 && (!strncmp(buf, \"#?RADIANCE\", 10)||!strncmp(buf, \"#?RGBE\", 6)) && (data = ReadRadianceFile(buf, len, fname, width, height, format)))\n\t\treturn data;\n#endif\n#ifdef IMAGEFMT_PSD\n\tif (!force_rgba8 && len > 26 && !strncmp(buf, \"8BPS\", 4) && (data = ReadPSDFile(buf, len, fname, width, height, format)))\n\t\treturn data;\n#endif\n#ifdef IMAGEFMT_XCF\n\tif (len > 9 && !strncmp(buf, \"gimp xcf \", 9) && (data = ReadXCFFile(buf, len, fname, width, height, force_rgba8?NULL:format)))\n\t\treturn data;\n#endif\n\n#ifdef IMAGEFMT_EXR\n\tif (!force_rgba8 && len > 4 && (buf[0]|(buf[1]<<8)|(buf[2]<<16)|(buf[3]<<24)) == 20000630 && (data = ReadEXRFile(buf, len, fname, width, height, format)))\n\t\treturn data;\n#endif\n\n#ifdef STB_IMAGE_IMPLEMENTATION\n\t{\n\t\tint components;\n\t\t//Note: I don't like passing STBI_default, because it'll return 24bit data which we'll then have to pad anyway.\n\t\t//but there's no other easy way to check how many channels are actually valid.\n\t\tif ((data = stbi_load_from_memory(buf, len, width, height, &components, force_rgba8?4:STBI_default)))\n\t\t{\n\t\t\tswitch(components)\n\t\t\t{\n\t\t\tcase STBI_grey:\n\t\t\t\t*format = PTI_L8;\n\t\t\t\treturn data;\n\t\t\tcase STBI_grey_alpha:\n\t\t\t\t*format = PTI_L8A8;\n\t\t\t\treturn data;\n\t\t\tcase STBI_rgb:\n\t\t\t\t*format = PTI_RGB8;\n\t\t\t\treturn data;\n\t\t\tcase STBI_rgb_alpha:\n\t\t\t\t*format = PTI_RGBA8;\n\t\t\t\treturn data;\n\t\t\t}\n\t\t\t//erk...?\n\t\t\tstbi_image_free(data);\n\t\t}\n\t}\n#endif\n\n\tfor (l = 0; l < imageloader_count; l++)\n\t{\n\t\tstruct pendingtextureinfo *mips = imageloader[l].funcs->ReadImageFile(0, fname, buf, len);\n\t\tif (mips)\n\t\t{\n\t\t\tif (mips->extrafree != buf)\n\t\t\t\tSys_Error(\"Image loader did weird extrafree things.\");\n\t\t\tmips->extrafree = NULL;\n\n\t\t\t//free any excess mips\n\t\t\twhile (mips->mipcount > 1)\n\t\t\t\tif (mips->mip[--mips->mipcount].needfree)\n\t\t\t\t\tBZ_Free(mips->mip[mips->mipcount].data);\n\n\t\t\tif (mips->mipcount > 0 && mips->type == PTI_2D)\n\t\t\t{\n\t\t\t\tif (force_rgba8)\n\t\t\t\t{\n\t\t\t\t\tqboolean rgbx8only[PTI_MAX] = {0};\n\t\t\t\t\trgbx8only[PTI_RGBX8] = true;\n\t\t\t\t\trgbx8only[PTI_RGBA8] = true;\n\t\t\t\t\tImage_ChangeFormat(mips, rgbx8only, mips->encoding, fname);\n\t\t\t\t}\n\n\t\t\t\tif (mips->mip[0].needfree)\n\t\t\t\t{\n\t\t\t\t\tdata = mips->mip[0].data;\n\t\t\t\t\tmips->mip[0].data = NULL;\n\t\t\t\t\tmips->mip[0].needfree = false;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tdata = BZ_Malloc(mips->mip[0].datasize);\n\t\t\t\t\tmemcpy(data, mips->mip[0].data, mips->mip[0].datasize);\n\t\t\t\t}\n\t\t\t\t*width = mips->mip[0].width;\n\t\t\t\t*height = mips->mip[0].height;\n\t\t\t\t*format = mips->encoding;\n\t\t\t}\n\t\t\tfor (i = 0; i < mips->mipcount; i++)\n\t\t\t\tif (mips->mip[i].needfree)\n\t\t\t\t\tBZ_Free(mips->mip[i].data);\n\n\t\t\tif (mips->extrafree && mips->extrafree)\n\t\t\t\tBZ_Free(mips->extrafree);\n\t\t\tBZ_Free(mips);\n\n\t\t\tif (data)\n\t\t\t\treturn data;\n\t\t}\n\t}\n\n#ifdef IMAGEFMT_LMP\n\tif (len >= 8)\t//.lmp has no magic id. guess at it.\n\t{\n\t\tint w = LittleLong(((int*)buf)[0]);\n\t\tint h = LittleLong(((int*)buf)[1]);\n\t\tint i;\n\t\tif (((w >= 3 && h >= 4)\n\t\t\t||(w==26&&h==1) //hack for hexen2. stupid lack of a magic.\n\t\t\t) && w*h+sizeof(int)*2 == len)\n\t\t{\t//quake lmp\n\t\t\tif (force_rgba8)\n\t\t\t{\n\t\t\t\tqboolean foundalpha = false;\n\t\t\t\tqbyte *in = buf+sizeof(int)*2;\n\t\t\t\tdata = BZ_Malloc(w * h * sizeof(int));\n\t\t\t\tfor (i = 0; i < w * h; i++)\n\t\t\t\t{\n\t\t\t\t\tif (in[i] == 255)\n\t\t\t\t\t\tfoundalpha = true;\n\t\t\t\t\t((unsigned int*)data)[i] = d_8to24rgbtable[in[i]];\n\t\t\t\t}\n\t\t\t\t*width = w;\n\t\t\t\t*height = h;\n\t\t\t\t*format = foundalpha?PTI_RGBA8:PTI_RGBX8;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tdata = BZ_Malloc(w * h);\n\t\t\t\tmemcpy(data, buf+8, w*h);\n\t\t\t\t*width = w;\n\t\t\t\t*height = h;\n\t\t\t\t*format = TF_TRANS8;\n\t\t\t}\n\t\t\treturn data;\n\t\t}\n\t\telse if (w >= 3 && h >= 4 && w*h+sizeof(int)*2+768+2 == len)\n\t\t{\t//halflife. should probably verify that those 2 extra bytes read as 256.\n\t\t\tqboolean foundalpha = false;\n\t\t\tqbyte *in = buf+sizeof(int)*2;\n\t\t\tqbyte *palette = in + w*h+2, *p;\n\t\t\tdata = BZ_Malloc(w * h * sizeof(int));\n\t\t\tfor (i = 0; i < w * h; i++)\n\t\t\t{\n\t\t\t\tif (in[i] == 255)\n\t\t\t\t\tfoundalpha = true;\n\t\t\t\tp = palette + 3*in[i];\n\t\t\t\tdata[(i<<2)+0] = p[2];\n\t\t\t\tdata[(i<<2)+1] = p[1];\n\t\t\t\tdata[(i<<2)+2] = p[0];\n\t\t\t\tdata[(i<<2)+3] = 255;\n\t\t\t}\n\t\t\t*width = w;\n\t\t\t*height = h;\n\t\t\t*format = foundalpha?PTI_RGBA8:PTI_RGBX8;\n\t\t\treturn data;\n\t\t}\n\t\telse if (len == 128*128 || len == 128*256)\n\t\t{\t//conchars lump (or h2). 0 is transparent.\n\t\t\tqbyte *in = buf;\n\t\t\th = 128;\n\t\t\tw = len/h;\n\t\t\tdata = BZ_Malloc(w * h * sizeof(int));\n\t\t\tfor (i = 0; i < w * h; i++)\n\t\t\t{\n\t\t\t\t((unsigned int*)data)[i] = d_8to24rgbtable[in[i]];\n\t\t\t\tdata[i*4+3] = (in[i] == 0)?0:255;\n\t\t\t}\n\t\t\t*width = w;\n\t\t\t*height = h;\n\t\t\t*format = PTI_RGBA8;\n\t\t\treturn data;\n\t\t}\n\t}\n#endif\n\n#ifdef Q2BSPS\n\tif (len >= sizeof(q2miptex_t))\t//.lmp has no magic id. guess at it.\n\t{\n\t\tconst q2miptex_t *wal = (const q2miptex_t *)buf;\n\t\tsize_t w = LittleLong(wal->width), h = LittleLong(wal->height);\n\t\tsize_t sz = sizeof(*wal) +\n\t\t\t\t\t(w>>0)*(h>>0) +\n\t\t\t\t\t(w>>1)*(h>>1) +\n\t\t\t\t\t(w>>2)*(h>>2) +\n\t\t\t\t\t(w>>3)*(h>>3);\n\t\tif (sz == len)\n\t\t{\n\t\t\tif (force_rgba8)\n\t\t\t{\n\t\t\t\tqboolean foundalpha = false;\n\t\t\t\tqbyte *in = buf+sizeof(*wal);\n\t\t\t\tdata = BZ_Malloc(w * h * sizeof(int));\n\t\t\t\tfor (i = 0; i < w * h; i++)\n\t\t\t\t{\n//\t\t\t\t\tif (in[i] == 255)\n//\t\t\t\t\t\tfoundalpha = true;\n\t\t\t\t\t((unsigned int*)data)[i] = d_8to24rgbtable[in[i]];\n\t\t\t\t}\n\t\t\t\t*width = w;\n\t\t\t\t*height = h;\n\t\t\t\t*format = foundalpha?PTI_RGBA8:PTI_RGBX8;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tdata = BZ_Malloc(w * h);\n\t\t\t\tmemcpy(data, buf+sizeof(*wal), w*h);\n\t\t\t\t*width = w;\n\t\t\t\t*height = h;\n\t\t\t\t*format = TF_SOLID8;\n\t\t\t}\n\t\t\treturn data;\n\t\t}\n\t}\n#endif\n\n\tTRACE((\"dbg: Read32BitImageFile: life sucks\\n\"));\n\n\treturn NULL;\n}\n\nstatic void Image_MipMap1X8 (qbyte *in, int inwidth, int inheight, qbyte *out, int outwidth, int outheight)\n{\n\tint\t\ti, j;\n\tqbyte\t*inrow;\n\n\tint rowwidth = inwidth;\t//rowwidth is the byte width of the input\n\tinrow = in;\n\n\t//mips round down, except for when the input is 1. which bugs out.\n\tif (inwidth <= 1 && inheight <= 1)\n\t\tout[0] = in[0];\n\telse if (inheight <= 1)\n\t{\n\t\t//single row, don't peek at the next\n\t\tfor (in = inrow, j=0 ; j<outwidth ; j++, out+=1, in+=2)\n\t\t\tout[0] = (in[0] + in[1])>>1;\n\t}\n\telse if (inwidth <= 1)\n\t{\n\t\t//single colum, peek only at this pixel\n\t\tfor (i=0 ; i<outheight ; i++, inrow+=rowwidth*2)\n\t\t\tfor (in = inrow, j=0 ; j<outwidth ; j++, out+=1, in+=2)\n\t\t\t\tout[0] = (in[0] + in[rowwidth+0])>>1;\n\t}\n\telse\n\t{\n\t\tfor (i=0 ; i<outheight ; i++, inrow+=rowwidth*2)\n\t\t\tfor (in = inrow, j=0 ; j<outwidth ; j++, out+=1, in+=2)\n\t\t\t\tout[0] = (in[0] + in[1] + in[rowwidth+0] + in[rowwidth+1])>>2;\n\t}\n}\n\nstatic void Image_MipMap2X8 (qbyte *in, int inwidth, int inheight, qbyte *out, int outwidth, int outheight)\n{\n\tint\t\ti, j;\n\tqbyte\t*inrow;\n\n\tint rowwidth = inwidth*2;\t//rowwidth is the byte width of the input\n\tinrow = in;\n\n\t//mips round down, except for when the input is 1. which bugs out.\n\tif (inwidth <= 1 && inheight <= 1)\n\t{\n\t\tout[0] = in[0];\n\t\tout[1] = in[1];\n\t}\n\telse if (inheight <= 1)\n\t{\n\t\t//single row, don't peek at the next\n\t\tfor (in = inrow, j=0 ; j<outwidth ; j++, out+=2, in+=4)\n\t\t{\n\t\t\tout[0] = (in[0] + in[2])>>1;\n\t\t\tout[1] = (in[1] + in[3])>>1;\n\t\t}\n\t}\n\telse if (inwidth <= 1)\n\t{\n\t\t//single colum, peek only at this pixel\n\t\tfor (i=0 ; i<outheight ; i++, inrow+=rowwidth*2)\n\t\t{\n\t\t\tfor (in = inrow, j=0 ; j<outwidth ; j++, out+=2, in+=4)\n\t\t\t{\n\t\t\t\tout[0] = (in[0] + in[rowwidth+0])>>1;\n\t\t\t\tout[1] = (in[1] + in[rowwidth+1])>>1;\n\t\t\t}\n\t\t}\n\t}\n\telse\n\t{\n\t\tfor (i=0 ; i<outheight ; i++, inrow+=rowwidth*2)\n\t\t{\n\t\t\tfor (in = inrow, j=0 ; j<outwidth ; j++, out+=2, in+=4)\n\t\t\t{\n\t\t\t\tout[0] = (in[0] + in[2] + in[rowwidth+0] + in[rowwidth+2])>>2;\n\t\t\t\tout[1] = (in[1] + in[3] + in[rowwidth+1] + in[rowwidth+3])>>2;\n\t\t\t}\n\t\t}\n\t}\n}\n\nstatic void Image_MipMap3X8 (qbyte *in, int inwidth, int inheight, qbyte *out, int outwidth, int outheight)\n{\n\tint\t\ti, j;\n\tqbyte\t*inrow;\n\n\tint rowwidth = inwidth*3;\t//rowwidth is the byte width of the input\n\tinrow = in;\n\n\t//mips round down, except for when the input is 1. which bugs out.\n\tif (inwidth <= 1 && inheight <= 1)\n\t{\n\t\tout[0] = in[0];\n\t\tout[1] = in[1];\n\t\tout[2] = in[2];\n\t}\n\telse if (inheight <= 1)\n\t{\n\t\t//single row, don't peek at the next\n\t\tfor (in = inrow, j=0 ; j<outwidth ; j++, out+=3, in+=6)\n\t\t{\n\t\t\tout[0] = (in[0] + in[3])>>1;\n\t\t\tout[1] = (in[1] + in[4])>>1;\n\t\t\tout[2] = (in[2] + in[5])>>1;\n\t\t}\n\t}\n\telse if (inwidth <= 1)\n\t{\n\t\t//single colum, peek only at this pixel\n\t\tfor (i=0 ; i<outheight ; i++, inrow+=rowwidth*2)\n\t\t{\n\t\t\tfor (in = inrow, j=0 ; j<outwidth ; j++, out+=3, in+=6)\n\t\t\t{\n\t\t\t\tout[0] = (in[0] + in[rowwidth+0])>>1;\n\t\t\t\tout[1] = (in[1] + in[rowwidth+1])>>1;\n\t\t\t\tout[2] = (in[2] + in[rowwidth+2])>>1;\n\t\t\t}\n\t\t}\n\t}\n\telse\n\t{\n\t\tfor (i=0 ; i<outheight ; i++, inrow+=rowwidth*2)\n\t\t{\n\t\t\tfor (in = inrow, j=0 ; j<outwidth ; j++, out+=3, in+=6)\n\t\t\t{\n\t\t\t\tout[0] = (in[0] + in[3] + in[rowwidth+0] + in[rowwidth+3])>>2;\n\t\t\t\tout[1] = (in[1] + in[4] + in[rowwidth+1] + in[rowwidth+4])>>2;\n\t\t\t\tout[2] = (in[2] + in[5] + in[rowwidth+2] + in[rowwidth+5])>>2;\n\t\t\t}\n\t\t}\n\t}\n}\n\nstatic void Image_MipMap4X8 (qbyte *in, int inwidth, int inheight, qbyte *out, int outwidth, int outheight)\n{\n\tint\t\ti, j;\n\tqbyte\t*inrow;\n\n\tint rowwidth = inwidth*4;\t//rowwidth is the byte width of the input\n\tinrow = in;\n\n\t//mips round down, except for when the input is 1. which bugs out.\n\tif (inwidth <= 1 && inheight <= 1)\n\t{\n\t\tout[0] = in[0];\n\t\tout[1] = in[1];\n\t\tout[2] = in[2];\n\t\tout[3] = in[3];\n\t}\n\telse if (inheight <= 1)\n\t{\n\t\t//single row, don't peek at the next\n\t\tfor (in = inrow, j=0 ; j<outwidth ; j++, out+=4, in+=8)\n\t\t{\n\t\t\tout[0] = (in[0] + in[4])>>1;\n\t\t\tout[1] = (in[1] + in[5])>>1;\n\t\t\tout[2] = (in[2] + in[6])>>1;\n\t\t\tout[3] = (in[3] + in[7])>>1;\n\t\t}\n\t}\n\telse if (inwidth <= 1)\n\t{\n\t\t//single colum, peek only at this pixel\n\t\tfor (i=0 ; i<outheight ; i++, inrow+=rowwidth*2)\n\t\t{\n\t\t\tfor (in = inrow, j=0 ; j<outwidth ; j++, out+=4, in+=8)\n\t\t\t{\n\t\t\t\tout[0] = (in[0] + in[rowwidth+0])>>1;\n\t\t\t\tout[1] = (in[1] + in[rowwidth+1])>>1;\n\t\t\t\tout[2] = (in[2] + in[rowwidth+2])>>1;\n\t\t\t\tout[3] = (in[3] + in[rowwidth+3])>>1;\n\t\t\t}\n\t\t}\n\t}\n\telse\n\t{\n\t\tfor (i=0 ; i<outheight ; i++, inrow+=rowwidth*2)\n\t\t{\n\t\t\tfor (in = inrow, j=0 ; j<outwidth ; j++, out+=4, in+=8)\n\t\t\t{\n\t\t\t\tout[0] = (in[0] + in[4] + in[rowwidth+0] + in[rowwidth+4])>>2;\n\t\t\t\tout[1] = (in[1] + in[5] + in[rowwidth+1] + in[rowwidth+5])>>2;\n\t\t\t\tout[2] = (in[2] + in[6] + in[rowwidth+2] + in[rowwidth+6])>>2;\n\t\t\t\tout[3] = (in[3] + in[7] + in[rowwidth+3] + in[rowwidth+7])>>2;\n\t\t\t}\n\t\t}\n\t}\n}\n\n//oh how I wish I had C++'s template stuff right now\nstatic void Image_MipMap4X16 (unsigned short *in, int inwidth, int inheight, unsigned short *out, int outwidth, int outheight)\n{\n\tint\t\ti, j;\n\tunsigned short\t*inrow;\n\n\tint rowwidth = inwidth*4;\t//rowwidth is the byte width of the input\n\tinrow = in;\n\n\t//mips round down, except for when the input is 1. which bugs out.\n\tif (inwidth <= 1 && inheight <= 1)\n\t{\n\t\tout[0] = in[0];\n\t\tout[1] = in[1];\n\t\tout[2] = in[2];\n\t\tout[3] = in[3];\n\t}\n\telse if (inheight <= 1)\n\t{\n\t\t//single row, don't peek at the next\n\t\tfor (in = inrow, j=0 ; j<outwidth ; j++, out+=4, in+=8)\n\t\t{\n\t\t\tout[0] = (in[0] + in[4])>>1;\n\t\t\tout[1] = (in[1] + in[5])>>1;\n\t\t\tout[2] = (in[2] + in[6])>>1;\n\t\t\tout[3] = (in[3] + in[7])>>1;\n\t\t}\n\t}\n\telse if (inwidth <= 1)\n\t{\n\t\t//single colum, peek only at this pixel\n\t\tfor (i=0 ; i<outheight ; i++, inrow+=rowwidth*2)\n\t\t{\n\t\t\tfor (in = inrow, j=0 ; j<outwidth ; j++, out+=4, in+=8)\n\t\t\t{\n\t\t\t\tout[0] = (in[0] + in[rowwidth+0])>>1;\n\t\t\t\tout[1] = (in[1] + in[rowwidth+1])>>1;\n\t\t\t\tout[2] = (in[2] + in[rowwidth+2])>>1;\n\t\t\t\tout[3] = (in[3] + in[rowwidth+3])>>1;\n\t\t\t}\n\t\t}\n\t}\n\telse\n\t{\n\t\tfor (i=0 ; i<outheight ; i++, inrow+=rowwidth*2)\n\t\t{\n\t\t\tfor (in = inrow, j=0 ; j<outwidth ; j++, out+=4, in+=8)\n\t\t\t{\n\t\t\t\tout[0] = (in[0] + in[4] + in[rowwidth+0] + in[rowwidth+4])>>2;\n\t\t\t\tout[1] = (in[1] + in[5] + in[rowwidth+1] + in[rowwidth+5])>>2;\n\t\t\t\tout[2] = (in[2] + in[6] + in[rowwidth+2] + in[rowwidth+6])>>2;\n\t\t\t\tout[3] = (in[3] + in[7] + in[rowwidth+3] + in[rowwidth+7])>>2;\n\t\t\t}\n\t\t}\n\t}\n}\n\n//unsigned normalised floats, for gl_ext_packed_float.\nstatic float SmallToFloat(unsigned int val, unsigned int size)\n{\n\tunsigned int mantissabits = size-5;\n\tunion\n\t{\n\t\tfloat f;\n\t\tunsigned int u;\n\t} u;\n\tunsigned int b;\n\tunsigned int mmask = (1<<mantissabits)-1;\n\tu.u = (((val>>mantissabits)&0x1f)-15+127)<<23;\t//read exponent, rebias it, and reshift.\n\n\t//fold the mantissa multiple times, to try to preserve as much precision as we can.\n\tfor (b = 23; b >= mantissabits; b-= mantissabits)\n\t\tu.u |= (val&mmask)<<(b-mantissabits);\n\tif (b)\n\t\tu.u |= (val&mmask)>>(mantissabits-b);\n\treturn u.f;\n}\n//unsigned normalised floats, for gl_ext_packed_float.\nstatic unsigned int FloatToSmall(float val, unsigned int size)\n{\n\tunsigned int mantissabits = size-5;\n\tunion\n\t{\n\t\tfloat f;\n\t\tunsigned int u;\n\t} u = {val};\n\tint e = 0;\n\tint m;\n\n\te = ((u.u>>23)&0xff) - 127;\n\tif (e < -15)\n\t\treturn 0; //too small exponent, treat it as a 0 denormal\n\tif (e > 15)\n\t\tm = 0; //infinity instead of a nan\n\telse\n\t\tm = (u.u&((1<<23)-1))>>(23-mantissabits);\n\treturn ((e+15)<<mantissabits) | m;\n}\n\nstatic float HalfToFloat(unsigned short val)\n{\n\tunion\n\t{\n\t\tfloat f;\n\t\tunsigned int u;\n\t} u;\n\tif (val&0x7c00)\n\t\tu.u = (((val&0x7c00)>>10)-15+127)<<23;\t//read exponent, rebias it, and reshift.\n\telse\n\t\tu.u = 0;\t//denormal (or 0).\n\tu.u |= ((val & 0x3ff)<<13);//shift up the mantissa, but don't fold\n\tu.u |= (val&0x8000)<<16;\t//retain the sign bit.\n\treturn u.f;\n}\nstatic unsigned short FloatToHalf(float val)\n{\n\tunion\n\t{\n\t\tfloat f;\n\t\tunsigned int u;\n\t} u = {val};\n\tint e = 0;\n\tint m;\n\n\te = ((u.u>>23)&0xff) - 127;\n\tif (e < -15)\n\t\treturn 0; //too small exponent, treat it as a 0 denormal\n\tif (e > 15)\n\t\tm = 0; //infinity instead of a nan\n\telse\n\t\tm = (u.u&((1<<23)-1))>>13;\n\treturn ((u.u>>16)&0x8000) | ((e+15)<<10) | m;\n}\nfte_inlinestatic unsigned short HalfFloatBlend2(unsigned short a, unsigned short b)\n{\n\treturn FloatToHalf((HalfToFloat(a) + HalfToFloat(b))/2);\n}\nfte_inlinestatic unsigned short HalfFloatBlend4(unsigned short a, unsigned short b, unsigned short c, unsigned short d)\n{\n\treturn FloatToHalf((HalfToFloat(a) + HalfToFloat(b) + HalfToFloat(c) + HalfToFloat(d))/4);\n}\nstatic void Image_MipMap4X16F (unsigned short *in, int inwidth, int inheight, unsigned short *out, int outwidth, int outheight)\n{\n\tint\t\ti, j;\n\tunsigned short\t*inrow;\n\n\tint rowwidth = inwidth*4;\t//rowwidth is the byte width of the input\n\tinrow = in;\n\n\t//mips round down, except for when the input is 1. which bugs out.\n\tif (inwidth <= 1 && inheight <= 1)\n\t{\n\t\tout[0] = in[0];\n\t\tout[1] = in[1];\n\t\tout[2] = in[2];\n\t\tout[3] = in[3];\n\t}\n\telse if (inheight <= 1)\n\t{\n\t\t//single row, don't peek at the next\n\t\tfor (in = inrow, j=0 ; j<outwidth ; j++, out+=4, in+=8)\n\t\t{\n\t\t\tout[0] = HalfFloatBlend2(in[0], in[4]);\n\t\t\tout[1] = HalfFloatBlend2(in[1], in[5]);\n\t\t\tout[2] = HalfFloatBlend2(in[2], in[6]);\n\t\t\tout[3] = HalfFloatBlend2(in[3], in[7]);\n\t\t}\n\t}\n\telse if (inwidth <= 1)\n\t{\n\t\t//single colum, peek only at this pixel\n\t\tfor (i=0 ; i<outheight ; i++, inrow+=rowwidth*2)\n\t\t{\n\t\t\tfor (in = inrow, j=0 ; j<outwidth ; j++, out+=4, in+=8)\n\t\t\t{\n\t\t\t\tout[0] = HalfFloatBlend2(in[0], in[rowwidth+0]);\n\t\t\t\tout[1] = HalfFloatBlend2(in[1], in[rowwidth+1]);\n\t\t\t\tout[2] = HalfFloatBlend2(in[2], in[rowwidth+2]);\n\t\t\t\tout[3] = HalfFloatBlend2(in[3], in[rowwidth+3]);\n\t\t\t}\n\t\t}\n\t}\n\telse\n\t{\n\t\tfor (i=0 ; i<outheight ; i++, inrow+=rowwidth*2)\n\t\t{\n\t\t\tfor (in = inrow, j=0 ; j<outwidth ; j++, out+=4, in+=8)\n\t\t\t{\n\t\t\t\tout[0] = HalfFloatBlend4(in[0], in[4], in[rowwidth+0], in[rowwidth+4]);\n\t\t\t\tout[1] = HalfFloatBlend4(in[1], in[5], in[rowwidth+1], in[rowwidth+5]);\n\t\t\t\tout[2] = HalfFloatBlend4(in[2], in[6], in[rowwidth+2], in[rowwidth+6]);\n\t\t\t\tout[3] = HalfFloatBlend4(in[3], in[7], in[rowwidth+3], in[rowwidth+7]);\n\t\t\t}\n\t\t}\n\t}\n}\n\nstatic void Image_MipMap4X32F (float *in, int inwidth, int inheight, float *out, int outwidth, int outheight)\n{\n\tint\t\ti, j;\n\tfloat\t*inrow;\n\n\tint rowwidth = inwidth*4;\t//rowwidth is the byte width of the input\n\tinrow = in;\n\n\t//mips round down, except for when the input is 1. which bugs out.\n\tif (inwidth <= 1 && inheight <= 1)\n\t{\n\t\tout[0] = in[0];\n\t\tout[1] = in[1];\n\t\tout[2] = in[2];\n\t\tout[3] = in[3];\n\t}\n\telse if (inheight <= 1)\n\t{\n\t\t//single row, don't peek at the next\n\t\tfor (in = inrow, j=0 ; j<outwidth ; j++, out+=4, in+=8)\n\t\t{\n\t\t\tout[0] = (in[0] + in[4])/2;\n\t\t\tout[1] = (in[1] + in[5])/2;\n\t\t\tout[2] = (in[2] + in[6])/2;\n\t\t\tout[3] = (in[3] + in[7])/2;\n\t\t}\n\t}\n\telse if (inwidth <= 1)\n\t{\n\t\t//single colum, peek only at this pixel\n\t\tfor (i=0 ; i<outheight ; i++, inrow+=rowwidth*2)\n\t\t{\n\t\t\tfor (in = inrow, j=0 ; j<outwidth ; j++, out+=4, in+=8)\n\t\t\t{\n\t\t\t\tout[0] = (in[0] + in[rowwidth+0])/2;\n\t\t\t\tout[1] = (in[1] + in[rowwidth+1])/2;\n\t\t\t\tout[2] = (in[2] + in[rowwidth+2])/2;\n\t\t\t\tout[3] = (in[3] + in[rowwidth+3])/2;\n\t\t\t}\n\t\t}\n\t}\n\telse\n\t{\n\t\tfor (i=0 ; i<outheight ; i++, inrow+=rowwidth*2)\n\t\t{\n\t\t\tfor (in = inrow, j=0 ; j<outwidth ; j++, out+=4, in+=8)\n\t\t\t{\n\t\t\t\tout[0] = (in[0] + in[4] + in[rowwidth+0] + in[rowwidth+4])/4;\n\t\t\t\tout[1] = (in[1] + in[5] + in[rowwidth+1] + in[rowwidth+5])/4;\n\t\t\t\tout[2] = (in[2] + in[6] + in[rowwidth+2] + in[rowwidth+6])/4;\n\t\t\t\tout[3] = (in[3] + in[7] + in[rowwidth+3] + in[rowwidth+7])/4;\n\t\t\t}\n\t\t}\n\t}\n}\n\nstatic qbyte Image_BlendPalette_2(qbyte a, qbyte b)\n{\n\treturn a;\n}\nstatic qbyte Image_BlendPalette_4(qbyte a, qbyte b, qbyte c, qbyte d)\n{\n\treturn a;\n}\n//this is expected to be slow, thanks to those two expensive helpers.\nstatic void Image_MipMap8Pal (qbyte *in, int inwidth, int inheight, qbyte *out, int outwidth, int outheight)\n{\n\tint\t\ti, j;\n\tqbyte\t*inrow;\n\n\tint rowwidth = inwidth;\t//rowwidth is the byte width of the input\n\tinrow = in;\n\n\t//mips round down, except for when the input is 1. which bugs out.\n\tif (inwidth <= 1 && inheight <= 1)\n\t\tout[0] = in[0];\n\telse if (inheight <= 1)\n\t{\n\t\t//single row, don't peek at the next\n\t\tfor (in = inrow, j=0 ; j<outwidth ; j++, out+=1, in+=2)\n\t\t\tout[0] = Image_BlendPalette_2(in[0], in[1]);\n\t}\n\telse if (inwidth <= 1)\n\t{\n\t\t//single colum, peek only at this pixel\n\t\tfor (i=0 ; i<outheight ; i++, inrow+=rowwidth*2)\n\t\t\tfor (in = inrow, j=0 ; j<outwidth ; j++, out+=1, in+=2)\n\t\t\t\tout[0] = Image_BlendPalette_2(in[0], in[rowwidth]);\n\t}\n\telse\n\t{\n\t\tfor (i=0 ; i<outheight ; i++, inrow+=rowwidth*2)\n\t\t\tfor (in = inrow, j=0 ; j<outwidth ; j++, out+=1, in+=2)\n\t\t\t\tout[0] = Image_BlendPalette_4(in[0], in[1], in[rowwidth+0], in[rowwidth+1]);\n\t}\n}\n\nvoid Image_GenerateMips(struct pendingtextureinfo *mips, unsigned int flags)\n{\n\tint mip;\n\n\tif (mips->type == PTI_3D)\n\t\treturn;\t//3d mipmaps are more complicated to compute.\n\n\tif (flags & IF_NOMIPMAP)\n\t\treturn;\n\n\tif (sh_config.can_genmips && mips->encoding != PTI_P8)\n\t\treturn;\n\n\tif (mips->mip[0].depth != 1)\n\t\treturn;\t//blurgh. we can't deal with layers.\n\n\tswitch(mips->encoding)\n\t{\n\tcase TF_TRANS8:\n\tcase TF_H2_TRANS8_0:\n\tcase PTI_P8:\n\t\tif (sh_config.can_mipcap)\n\t\t\treturn;\t//if we can cap mips, do that. it'll save lots of expensive lookups and uglyness.\n\t\tfor (mip = mips->mipcount; mip < countof(mips->mip); mip++)\n\t\t{\n\t\t\tmips->mip[mip].width = mips->mip[mip-1].width >> 1;\n\t\t\tmips->mip[mip].height = mips->mip[mip-1].height >> 1;\n\t\t\tmips->mip[mip].depth = 1;\n\t\t\tif (mips->mip[mip].width < 1 && mips->mip[mip].height < 1)\n\t\t\t\tbreak;\n\t\t\tif (mips->mip[mip].width < 1)\n\t\t\t\tmips->mip[mip].width = 1;\n\t\t\tif (mips->mip[mip].height < 1)\n\t\t\t\tmips->mip[mip].height = 1;\n\t\t\tmips->mip[mip].datasize = mips->mip[mip].width * mips->mip[mip].height;\n\t\t\tmips->mip[mip].data = BZ_Malloc(mips->mip[mip].datasize);\n\t\t\tmips->mip[mip].needfree = true;\n\n\t\t\tImage_MipMap8Pal(mips->mip[mip-1].data, mips->mip[mip-1].width, mips->mip[mip-1].height, mips->mip[mip].data, mips->mip[mip].width, mips->mip[mip].height);\n\t\t\tmips->mipcount = mip+1;\n\t\t}\n\t\treturn;\n\tcase PTI_R8:\n\tcase PTI_R8_SNORM:\n\tcase PTI_L8:\n\tcase PTI_L8_SRGB:\n\t\tfor (mip = mips->mipcount; mip < countof(mips->mip); mip++)\n\t\t{\n\t\t\tmips->mip[mip].width = mips->mip[mip-1].width >> 1;\n\t\t\tmips->mip[mip].height = mips->mip[mip-1].height >> 1;\n\t\t\tmips->mip[mip].depth = 1;\n\t\t\tif (mips->mip[mip].width < 1 && mips->mip[mip].height < 1)\n\t\t\t\tbreak;\n\t\t\tif (mips->mip[mip].width < 1)\n\t\t\t\tmips->mip[mip].width = 1;\n\t\t\tif (mips->mip[mip].height < 1)\n\t\t\t\tmips->mip[mip].height = 1;\n\t\t\tmips->mip[mip].datasize = mips->mip[mip].width * mips->mip[mip].height;\n\t\t\tmips->mip[mip].data = BZ_Malloc(mips->mip[mip].datasize);\n\t\t\tmips->mip[mip].needfree = true;\n\n\t\t\tImage_MipMap1X8(mips->mip[mip-1].data, mips->mip[mip-1].width, mips->mip[mip-1].height, mips->mip[mip].data, mips->mip[mip].width, mips->mip[mip].height);\n\t\t\tmips->mipcount = mip+1;\n\t\t}\n\t\treturn;\t\n\tcase PTI_RG8:\n\tcase PTI_RG8_SNORM:\n\tcase PTI_L8A8:\n\tcase PTI_L8A8_SRGB:\n\t\tfor (mip = mips->mipcount; mip < countof(mips->mip); mip++)\n\t\t{\n\t\t\tmips->mip[mip].width = mips->mip[mip-1].width >> 1;\n\t\t\tmips->mip[mip].height = mips->mip[mip-1].height >> 1;\n\t\t\tmips->mip[mip].depth = 1;\n\t\t\tif (mips->mip[mip].width < 1 && mips->mip[mip].height < 1)\n\t\t\t\tbreak;\n\t\t\tif (mips->mip[mip].width < 1)\n\t\t\t\tmips->mip[mip].width = 1;\n\t\t\tif (mips->mip[mip].height < 1)\n\t\t\t\tmips->mip[mip].height = 1;\n\t\t\tmips->mip[mip].datasize = mips->mip[mip].width * mips->mip[mip].height * 2;\n\t\t\tmips->mip[mip].data = BZ_Malloc(mips->mip[mip].datasize);\n\t\t\tmips->mip[mip].needfree = true;\n\n\t\t\tImage_MipMap2X8(mips->mip[mip-1].data, mips->mip[mip-1].width, mips->mip[mip-1].height, mips->mip[mip].data, mips->mip[mip].width, mips->mip[mip].height);\n\t\t\tmips->mipcount = mip+1;\n\t\t}\n\t\treturn;\n\tcase PTI_RGBA32F:\n\t\tfor (mip = mips->mipcount; mip < countof(mips->mip); mip++)\n\t\t{\n\t\t\tmips->mip[mip].width = mips->mip[mip-1].width >> 1;\n\t\t\tmips->mip[mip].height = mips->mip[mip-1].height >> 1;\n\t\t\tmips->mip[mip].depth = 1;\n\t\t\tif (mips->mip[mip].width < 1 && mips->mip[mip].height < 1)\n\t\t\t\tbreak;\n\t\t\tif (mips->mip[mip].width < 1)\n\t\t\t\tmips->mip[mip].width = 1;\n\t\t\tif (mips->mip[mip].height < 1)\n\t\t\t\tmips->mip[mip].height = 1;\n\t\t\tmips->mip[mip].datasize = mips->mip[mip].width * mips->mip[mip].height * sizeof(float)*4;\n\t\t\tmips->mip[mip].data = BZ_Malloc(mips->mip[mip].datasize);\n\t\t\tmips->mip[mip].needfree = true;\n\n\t\t\tImage_MipMap4X32F(mips->mip[mip-1].data, mips->mip[mip-1].width, mips->mip[mip-1].height, mips->mip[mip].data, mips->mip[mip].width, mips->mip[mip].height);\n\t\t\tmips->mipcount = mip+1;\n\t\t}\n\t\tbreak;\n\tcase PTI_RGBA16F:\n\t\tfor (mip = mips->mipcount; mip < countof(mips->mip); mip++)\n\t\t{\n\t\t\tmips->mip[mip].width = mips->mip[mip-1].width >> 1;\n\t\t\tmips->mip[mip].height = mips->mip[mip-1].height >> 1;\n\t\t\tmips->mip[mip].depth = 1;\n\t\t\tif (mips->mip[mip].width < 1 && mips->mip[mip].height < 1)\n\t\t\t\tbreak;\n\t\t\tif (mips->mip[mip].width < 1)\n\t\t\t\tmips->mip[mip].width = 1;\n\t\t\tif (mips->mip[mip].height < 1)\n\t\t\t\tmips->mip[mip].height = 1;\n\t\t\tmips->mip[mip].datasize = mips->mip[mip].width * mips->mip[mip].height * sizeof(unsigned short)*4;\n\t\t\tmips->mip[mip].data = BZ_Malloc(mips->mip[mip].datasize);\n\t\t\tmips->mip[mip].needfree = true;\n\n\t\t\tImage_MipMap4X16F(mips->mip[mip-1].data, mips->mip[mip-1].width, mips->mip[mip-1].height, mips->mip[mip].data, mips->mip[mip].width, mips->mip[mip].height);\n\t\t\tmips->mipcount = mip+1;\n\t\t}\n\t\tbreak;\n\tcase PTI_RGBA16:\n\t\tfor (mip = mips->mipcount; mip < countof(mips->mip); mip++)\n\t\t{\n\t\t\tmips->mip[mip].width = mips->mip[mip-1].width >> 1;\n\t\t\tmips->mip[mip].height = mips->mip[mip-1].height >> 1;\n\t\t\tmips->mip[mip].depth = 1;\n\t\t\tif (mips->mip[mip].width < 1 && mips->mip[mip].height < 1)\n\t\t\t\tbreak;\n\t\t\tif (mips->mip[mip].width < 1)\n\t\t\t\tmips->mip[mip].width = 1;\n\t\t\tif (mips->mip[mip].height < 1)\n\t\t\t\tmips->mip[mip].height = 1;\n\t\t\tmips->mip[mip].datasize = mips->mip[mip].width * mips->mip[mip].height * sizeof(unsigned short)*4;\n\t\t\tmips->mip[mip].data = BZ_Malloc(mips->mip[mip].datasize);\n\t\t\tmips->mip[mip].needfree = true;\n\n\t\t\tImage_MipMap4X16(mips->mip[mip-1].data, mips->mip[mip-1].width, mips->mip[mip-1].height, mips->mip[mip].data, mips->mip[mip].width, mips->mip[mip].height);\n\t\t\tmips->mipcount = mip+1;\n\t\t}\n\t\tbreak;\n\tcase PTI_RGB8_SRGB:\n\tcase PTI_BGR8_SRGB:\n\tcase PTI_RGB8:\n\tcase PTI_BGR8:\n\t\tfor (mip = mips->mipcount; mip < countof(mips->mip); mip++)\n\t\t{\n\t\t\tmips->mip[mip].width = mips->mip[mip-1].width >> 1;\n\t\t\tmips->mip[mip].height = mips->mip[mip-1].height >> 1;\n\t\t\tmips->mip[mip].depth = 1;\n\t\t\tif (mips->mip[mip].width < 1 && mips->mip[mip].height < 1)\n\t\t\t\tbreak;\n\t\t\tif (mips->mip[mip].width < 1)\n\t\t\t\tmips->mip[mip].width = 1;\n\t\t\tif (mips->mip[mip].height < 1)\n\t\t\t\tmips->mip[mip].height = 1;\n\t\t\tmips->mip[mip].datasize = mips->mip[mip].width * mips->mip[mip].height * sizeof(qbyte)*3;\n\t\t\tmips->mip[mip].data = BZ_Malloc(mips->mip[mip].datasize);\n\t\t\tmips->mip[mip].needfree = true;\n\n\t\t\tImage_MipMap3X8(mips->mip[mip-1].data, mips->mip[mip-1].width, mips->mip[mip-1].height, mips->mip[mip].data, mips->mip[mip].width, mips->mip[mip].height);\n\t\t\tmips->mipcount = mip+1;\n\t\t}\n\t\tbreak;\n\tcase PTI_RGBA8_SRGB:\n\tcase PTI_RGBX8_SRGB:\n\tcase PTI_BGRA8_SRGB:\n\tcase PTI_BGRX8_SRGB:\n\tcase PTI_RGBA8:\n\tcase PTI_RGBX8:\n\tcase PTI_BGRA8:\n\tcase PTI_BGRX8:\n\t\tfor (mip = mips->mipcount; mip < countof(mips->mip); mip++)\n\t\t{\n\t\t\tmips->mip[mip].width = mips->mip[mip-1].width >> 1;\n\t\t\tmips->mip[mip].height = mips->mip[mip-1].height >> 1;\n\t\t\tmips->mip[mip].depth = 1;\n\t\t\tif (mips->mip[mip].width < 1 && mips->mip[mip].height < 1)\n\t\t\t\tbreak;\n\t\t\tif (mips->mip[mip].width < 1)\n\t\t\t\tmips->mip[mip].width = 1;\n\t\t\tif (mips->mip[mip].height < 1)\n\t\t\t\tmips->mip[mip].height = 1;\n\t\t\tmips->mip[mip].datasize = mips->mip[mip].width * mips->mip[mip].height * sizeof(qbyte)*4;\n\t\t\tmips->mip[mip].data = BZ_Malloc(mips->mip[mip].datasize);\n\t\t\tmips->mip[mip].needfree = true;\n\n\t\t\tImage_MipMap4X8(mips->mip[mip-1].data, mips->mip[mip-1].width, mips->mip[mip-1].height, mips->mip[mip].data, mips->mip[mip].width, mips->mip[mip].height);\n\t\t\tmips->mipcount = mip+1;\n\t\t}\n\t\tbreak;\n\tcase PTI_RGBA4444:\n\tcase PTI_RGB565:\n\tcase PTI_RGBA5551:\n\t\treturn;\t//convert to 16bit afterwards. always mipmap at 8 bit, to try to preserve what little precision there is.\n\tdefault:\n\t\treturn;\t//not supported.\n\t}\n}\n\n//stolen from DP\n//FIXME: optionally support borders as 0,0,0,0\nstatic void Image_Resample32LerpLine (const qbyte *in, qbyte *out, int inwidth, int outwidth)\n{\n\tint\t\tj, xi, oldx = 0, f, fstep, endx, lerp;\n\tfstep = (int) (inwidth*65536.0f/outwidth);\n\tendx = (inwidth-1);\n\tfor (j = 0,f = 0;j < outwidth;j++, f += fstep)\n\t{\n\t\txi = f >> 16;\n\t\tif (xi != oldx)\n\t\t{\n\t\t\tin += (xi - oldx) * 4;\n\t\t\toldx = xi;\n\t\t}\n\t\tif (xi < endx)\n\t\t{\n\t\t\tlerp = f & 0xFFFF;\n\t\t\t*out++ = (qbyte) ((((in[4] - in[0]) * lerp) >> 16) + in[0]);\n\t\t\t*out++ = (qbyte) ((((in[5] - in[1]) * lerp) >> 16) + in[1]);\n\t\t\t*out++ = (qbyte) ((((in[6] - in[2]) * lerp) >> 16) + in[2]);\n\t\t\t*out++ = (qbyte) ((((in[7] - in[3]) * lerp) >> 16) + in[3]);\n\t\t}\n\t\telse // last pixel of the line has no pixel to lerp to\n\t\t{\n\t\t\t*out++ = in[0];\n\t\t\t*out++ = in[1];\n\t\t\t*out++ = in[2];\n\t\t\t*out++ = in[3];\n\t\t}\n\t}\n}\n\n//yes, this is lordhavok's code too.\n//superblur away!\n//FIXME: optionally support borders as 0,0,0,0\n#define LERPBYTE(i) r = row1[i];out[i] = (qbyte) ((((row2[i] - r) * lerp) >> 16) + r)\nstatic void Image_Resample32Lerp(const void *indata, int inwidth, int inheight, void *outdata, int outwidth, int outheight)\n{\n\tint i, j, r, yi, oldy, f, fstep, lerp, endy = (inheight-1), inwidth4 = inwidth*4, outwidth4 = outwidth*4;\n\tqbyte *out;\n\tconst qbyte *inrow;\n\tqbyte *row1, *row2;\n\n\trow1 = alloca(2*(outwidth*4));\n\trow2 = row1 + (outwidth * 4);\n\n\tout = outdata;\n\tfstep = (int) (inheight*65536.0f/outheight);\n\n\tinrow = indata;\n\toldy = 0;\n\tImage_Resample32LerpLine (inrow, row1, inwidth, outwidth);\n\tImage_Resample32LerpLine (inrow + inwidth4, row2, inwidth, outwidth);\n\tfor (i = 0, f = 0;i < outheight;i++,f += fstep)\n\t{\n\t\tyi = f >> 16;\n\t\tif (yi < endy)\n\t\t{\n\t\t\tlerp = f & 0xFFFF;\n\t\t\tif (yi != oldy)\n\t\t\t{\n\t\t\t\tinrow = (const qbyte *)indata + inwidth4*yi;\n\t\t\t\tif (yi == oldy+1)\n\t\t\t\t\tmemcpy(row1, row2, outwidth4);\n\t\t\t\telse\n\t\t\t\t\tImage_Resample32LerpLine (inrow, row1, inwidth, outwidth);\n\t\t\t\tImage_Resample32LerpLine (inrow + inwidth4, row2, inwidth, outwidth);\n\t\t\t\toldy = yi;\n\t\t\t}\n\t\t\tj = outwidth - 4;\n\t\t\twhile(j >= 0)\n\t\t\t{\n\t\t\t\tLERPBYTE( 0);\n\t\t\t\tLERPBYTE( 1);\n\t\t\t\tLERPBYTE( 2);\n\t\t\t\tLERPBYTE( 3);\n\t\t\t\tLERPBYTE( 4);\n\t\t\t\tLERPBYTE( 5);\n\t\t\t\tLERPBYTE( 6);\n\t\t\t\tLERPBYTE( 7);\n\t\t\t\tLERPBYTE( 8);\n\t\t\t\tLERPBYTE( 9);\n\t\t\t\tLERPBYTE(10);\n\t\t\t\tLERPBYTE(11);\n\t\t\t\tLERPBYTE(12);\n\t\t\t\tLERPBYTE(13);\n\t\t\t\tLERPBYTE(14);\n\t\t\t\tLERPBYTE(15);\n\t\t\t\tout += 16;\n\t\t\t\trow1 += 16;\n\t\t\t\trow2 += 16;\n\t\t\t\tj -= 4;\n\t\t\t}\n\t\t\tif (j & 2)\n\t\t\t{\n\t\t\t\tLERPBYTE( 0);\n\t\t\t\tLERPBYTE( 1);\n\t\t\t\tLERPBYTE( 2);\n\t\t\t\tLERPBYTE( 3);\n\t\t\t\tLERPBYTE( 4);\n\t\t\t\tLERPBYTE( 5);\n\t\t\t\tLERPBYTE( 6);\n\t\t\t\tLERPBYTE( 7);\n\t\t\t\tout += 8;\n\t\t\t\trow1 += 8;\n\t\t\t\trow2 += 8;\n\t\t\t}\n\t\t\tif (j & 1)\n\t\t\t{\n\t\t\t\tLERPBYTE( 0);\n\t\t\t\tLERPBYTE( 1);\n\t\t\t\tLERPBYTE( 2);\n\t\t\t\tLERPBYTE( 3);\n\t\t\t\tout += 4;\n\t\t\t\trow1 += 4;\n\t\t\t\trow2 += 4;\n\t\t\t}\n\t\t\trow1 -= outwidth4;\n\t\t\trow2 -= outwidth4;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tyi = endy;\t//don't read off the end\n\t\t\tif (yi != oldy)\n\t\t\t{\n\t\t\t\tinrow = (const qbyte *)indata + inwidth4*yi;\n\t\t\t\tif (yi == oldy+1)\n\t\t\t\t\tmemcpy(row1, row2, outwidth4);\n\t\t\t\telse\n\t\t\t\t\tImage_Resample32LerpLine (inrow, row1, inwidth, outwidth);\n\t\t\t\toldy = yi;\n\t\t\t}\n\t\t\tmemcpy(out, row1, outwidth4);\n\t\t\tout += outwidth4;\t//Fixes a bug from DP.\n\t\t}\n\t}\n}\n\n\n/*\n================\nGL_ResampleTexture\n================\n*/\nstatic void Image_ResampleTexture32Nearest (const unsigned *in, int inwidth, int inheight, unsigned *out,  int outwidth, int outheight)\n{\n\tint\t\ti, j;\n\tconst unsigned\t*inrow;\n\tunsigned\tfrac, fracstep;\n\n\tfracstep = inwidth*0x10000/outwidth;\n\tfor (i=0 ; i<outheight ; i++, out += outwidth)\n\t{\n\t\tinrow = in + inwidth*(i*inheight/outheight);\n\t\tfrac = outwidth*fracstep;\n\t\tj=outwidth;\n\t\twhile ((j)&3)\n\t\t{\n\t\t\tj--;\n\t\t\tfrac -= fracstep;\n\t\t\tout[j] = inrow[frac>>16];\n\t\t}\n\t\tfor ( ; j>=4 ;)\n\t\t{\n\t\t\tj-=4;\n\t\t\tfrac -= fracstep;\n\t\t\tout[j+3] = inrow[frac>>16];\n\t\t\tfrac -= fracstep;\n\t\t\tout[j+2] = inrow[frac>>16];\n\t\t\tfrac -= fracstep;\n\t\t\tout[j+1] = inrow[frac>>16];\n\t\t\tfrac -= fracstep;\n\t\t\tout[j+0] = inrow[frac>>16];\n\t\t}\n\t}\n}\n\nstatic void Image_ResampleTexture8Nearest (const unsigned char *in, int inwidth, int inheight, unsigned char *out,  int outwidth, int outheight)\n{\n\tint\t\ti, j;\n\tconst unsigned\tchar *inrow;\n\tunsigned\tfrac, fracstep;\n\n\tfracstep = inwidth*0x10000/outwidth;\n\tfor (i=0 ; i<outheight ; i++, out += outwidth)\n\t{\n\t\tinrow = in + inwidth*(i*inheight/outheight);\n\t\tfrac = outwidth*fracstep;\n\t\tj=outwidth;\n\t\twhile ((j)&3)\n\t\t{\n\t\t\tj--;\n\t\t\tfrac -= fracstep;\n\t\t\tout[j] = inrow[frac>>16];\n\t\t}\n\t\tfor ( ; j>=4 ;)\n\t\t{\n\t\t\tj-=4;\n\t\t\tfrac -= fracstep;\n\t\t\tout[j+3] = inrow[frac>>16];\n\t\t\tfrac -= fracstep;\n\t\t\tout[j+2] = inrow[frac>>16];\n\t\t\tfrac -= fracstep;\n\t\t\tout[j+1] = inrow[frac>>16];\n\t\t\tfrac -= fracstep;\n\t\t\tout[j+0] = inrow[frac>>16];\n\t\t}\n\t}\n}\nstatic void Image_ResampleTexture16Nearest (const unsigned short *in, int inwidth, int inheight, unsigned short *out,  int outwidth, int outheight)\n{\n\tint\t\ti, j;\n\tconst unsigned\tshort *inrow;\n\tunsigned\tfrac, fracstep;\n\n\tfracstep = inwidth*0x10000/outwidth;\n\tfor (i=0 ; i<outheight ; i++, out += outwidth)\n\t{\n\t\tinrow = in + inwidth*(i*inheight/outheight);\n\t\tfrac = outwidth*fracstep;\n\t\tj=outwidth;\n\t\twhile ((j)&3)\n\t\t{\n\t\t\tj--;\n\t\t\tfrac -= fracstep;\n\t\t\tout[j] = inrow[frac>>16];\n\t\t}\n\t\tfor ( ; j>=4 ;)\n\t\t{\n\t\t\tj-=4;\n\t\t\tfrac -= fracstep;\n\t\t\tout[j+3] = inrow[frac>>16];\n\t\t\tfrac -= fracstep;\n\t\t\tout[j+2] = inrow[frac>>16];\n\t\t\tfrac -= fracstep;\n\t\t\tout[j+1] = inrow[frac>>16];\n\t\t\tfrac -= fracstep;\n\t\t\tout[j+0] = inrow[frac>>16];\n\t\t}\n\t}\n}\n\n//returns out on success. if out is null then returns a new BZ_Malloced buffer\nvoid *Image_ResampleTexture (uploadfmt_t format, const void *in, int inwidth, int inheight, void *out,  int outwidth, int outheight)\n{\n\tswitch(format)\n\t{\n\tcase PTI_INVALID:\n\tdefault:\n\t\treturn NULL;\n\t//we don't care about byte order etc, just channels+sizes.\n\tcase PTI_LLLX8:\n\tcase PTI_LLLA8:\n\tcase PTI_RGBA8:\n\tcase PTI_RGBX8:\n\tcase PTI_BGRA8:\n\tcase PTI_BGRX8:\n\tcase PTI_RGBA8_SRGB:\n\tcase PTI_RGBX8_SRGB:\n\tcase PTI_BGRA8_SRGB:\n\tcase PTI_BGRX8_SRGB:\n\t\tif (!out)\n\t\t\tout = BZ_Malloc(((outwidth+3)&~3)*outheight*4);\n#ifdef HAVE_CLIENT\n\t\tif (gl_lerpimages.ival)\n#else\n\t\tif (1)\n#endif\n\t\t\tImage_Resample32Lerp(in, inwidth, inheight, out, outwidth, outheight);\t//FIXME: should be sRGB-aware, but probably not a common path on hardware that can actually do srgb.\n\t\telse\n\t\t\tImage_ResampleTexture32Nearest(in, inwidth, inheight, out, outwidth, outheight);\n\t\treturn out;\n\tcase PTI_R32F:\n\tcase PTI_A2BGR10:\n\tcase PTI_E5BGR9:\n\t\tif (!out)\n\t\t\tout = BZ_Malloc(((outwidth+3)&~3)*outheight*4);\n\t\tImage_ResampleTexture32Nearest(in, inwidth, inheight, out, outwidth, outheight);\n\t\treturn out;\n\tcase PTI_P8:\n\tcase PTI_L8:\n\tcase PTI_R8:\n\tcase PTI_L8_SRGB:\n\tcase PTI_R8_SNORM:\n\t\tif (!out)\n\t\t\tout = BZ_Malloc((outwidth+3)*outheight);\n\t\tImage_ResampleTexture8Nearest(in, inwidth, inheight, out, outwidth, outheight);\n\t\treturn out;\n\tcase PTI_RG8:\n\tcase PTI_RG8_SNORM:\n\tcase PTI_L8A8:\n\tcase PTI_L8A8_SRGB:\n\tcase PTI_R16:\n\tcase PTI_R16F:\n\tcase PTI_RGB565:\n\tcase PTI_RGBA4444:\n\tcase PTI_ARGB4444:\n\tcase PTI_RGBA5551:\n\tcase PTI_ARGB1555:\n\t\tif (!out)\n\t\t\tout = BZ_Malloc((outwidth+3)*outheight*2);\n\t\tImage_ResampleTexture16Nearest(in, inwidth, inheight, out, outwidth, outheight);\n\t\treturn out;\n\tcase PTI_RGB8:\n\tcase PTI_BGR8:\n\tcase PTI_RGBA16:\n\tcase PTI_RGBA16F:\n\tcase PTI_RGBA32F:\n\t\treturn NULL;\t//fixmes\n\t}\n}\n\n//ripped from tenebrae\nstatic unsigned int * Image_GenerateNormalMap(qbyte *pixels, unsigned int *nmap, int w, int h, float scale, float offsetscale)\n{\n\tint i, j, wr, hr;\n\tunsigned char r, g, b, height;\n\tfloat sqlen, reciplen, nx, ny, nz;\n\n\tconst float oneOver255 = 1.0f/255.0f;\n\n\tfloat c, cx, cy, dcx, dcy;\n\n\twr = w;\n\thr = h;\n\n\tfor (i=0; i<h; i++)\n\t{\n\t\tfor (j=0; j<w; j++)\n\t\t{\n\t\t\t/* Expand [0,255] texel values to the [0,1] range. */\n\t\t\tc = pixels[i*wr + j] * oneOver255;\n\t\t\t/* Expand the texel to its right. */\n\t\t\tcx = pixels[i*wr + (j+1)%wr] * oneOver255;\n\t\t\t/* Expand the texel one up. */\n\t\t\tcy = pixels[((i+1)%hr)*wr + j] * oneOver255;\n\t\t\tdcx = scale * (c - cx);\n\t\t\tdcy = scale * (c - cy);\n\n\t\t\t/* Normalize the vector. */\n\t\t\tsqlen = dcx*dcx + dcy*dcy + 1;\n\t\t\treciplen = 1.0f/(float)sqrt(sqlen);\n\t\t\tnx = dcx*reciplen;\n\t\t\tny = -dcy*reciplen;\n\t\t\tnz = reciplen;\n\n\t\t\t/* Repack the normalized vector into an RGB unsigned qbyte\n\t\t\t   vector in the normal map image. */\n\t\t\tr = (qbyte) (128 + 127*nx);\n\t\t\tg = (qbyte) (128 + 127*ny);\n\t\t\tb = (qbyte) (128 + 127*nz);\n\n\t\t\t/* The highest resolution mipmap level always has a\n\t\t\t   unit length magnitude. */\n\t\t\theight = bound(0, (pixels[i*wr + j]*offsetscale)+(255*(1-offsetscale)), 255);\n\t\t\tnmap[i*w+j] = LittleLong((height << 24)|(b << 16)|(g << 8)|(r));\t// <AWE> Added support for big endian.\n\t\t}\n\t}\n\n\treturn &nmap[0];\n}\n\nstatic int Image_GetPicMip(unsigned int flags)\n{\n\tint picmip = 0;\n#ifdef HAVE_CLIENT\n\tif (flags & IF_NOMIPMAP)\n\t\tpicmip += gl_picmip2d.ival;\t//2d stuff gets its own picmip cvar.\n\telse\n\t{\n\t\tpicmip += gl_picmip.ival;\n\n\t\tif (flags & IF_WORLDTEX)\n\t\t\tpicmip += gl_picmip_world.ival;\n\t\telse if (flags & IF_SPRITETEX)\n\t\t\tpicmip += gl_picmip_sprites.ival;\n\t\telse\n\t\t\tpicmip += gl_picmip_other.ival;\n\t}\n\tif (picmip < 0)\n\t\tpicmip = 0;\n#endif\n\treturn picmip;\n}\n\nstatic void Image_RoundDimensions(int *scaled_width, int *scaled_height, int *scaled_depth, unsigned int flags)\n{\n\tif (sh_config.texture_non_power_of_two)\t//NPOT is a simple extension that relaxes errors.\n\t{\n\t\t//lax form\n\t\tTRACE((\"dbg: GL_RoundDimensions: GL_ARB_texture_non_power_of_two\\n\"));\n\t}\n\telse if ((flags & IF_CLAMP) && (flags & IF_NOMIPMAP) && sh_config.texture_non_power_of_two_pic)\n\t{\n\t\t//more strict form\n\t\tTRACE((\"dbg: GL_RoundDimensions: GL_OES_texture_npot\\n\"));\n\t}\n\telse\n\t{\n\t\tint width = *scaled_width;\n\t\tint height = *scaled_height;\n\t\tint depth = *scaled_depth;\n\t\tfor (*scaled_width = 1 ; *scaled_width < width ; *scaled_width<<=1)\n\t\t\t;\n\t\tfor (*scaled_height = 1 ; *scaled_height < height ; *scaled_height<<=1)\n\t\t\t;\n\t\tif ((flags&IF_TEXTYPEMASK) == IF_TEXTYPE_3D)\n\t\t\tfor (*scaled_depth = 1 ; *scaled_depth < depth ; *scaled_depth<<=1)\n\t\t\t\t;\n\n\t\t/*round npot textures down if we're running on an embedded system*/\n\t\tif (sh_config.npot_rounddown)\n\t\t{\n\t\t\tif (*scaled_width != width)\n\t\t\t\t*scaled_width >>= 1;\n\t\t\tif (*scaled_height != height)\n\t\t\t\t*scaled_height >>= 1;\n\t\t\tif ((flags&IF_TEXTYPEMASK) == IF_TEXTYPE_3D)\n\t\t\t\tif (*scaled_depth != depth)\n\t\t\t\t\t*scaled_depth >>= 1;\n\t\t}\n\t}\n\n\tif (!(flags & IF_NOPICMIP))\n\t{\n\t\tint picmip = Image_GetPicMip(flags);\n\t\t*scaled_width >>= picmip;\n\t\t*scaled_height >>= picmip;\n\t\tif ((flags&IF_TEXTYPEMASK) == IF_TEXTYPE_3D)\n\t\t\t*scaled_depth >>= picmip;\n\t}\n\n\tTRACE((\"dbg: GL_RoundDimensions: %f\\n\", gl_max_size.value));\n\n\tswitch((flags&IF_TEXTYPEMASK)>>IF_TEXTYPESHIFT)\n\t{\n\tdefault:\n\t\tbreak;\n\tcase PTI_CUBE:\n\tcase PTI_CUBE_ARRAY:\n\t\tif (sh_config.texturecube_maxsize)\n\t\t{\n\t\t\tif (*scaled_width > sh_config.texturecube_maxsize)\n\t\t\t\t*scaled_width = sh_config.texturecube_maxsize;\n\t\t\tif (*scaled_height > sh_config.texturecube_maxsize)\n\t\t\t\t*scaled_height = sh_config.texturecube_maxsize;\n\t\t}\n\t\tif (sh_config.texture2darray_maxlayers)\n\t\t\tif (*scaled_depth > sh_config.texture2darray_maxlayers)\n\t\t\t\t*scaled_depth = sh_config.texture2darray_maxlayers;\n\t\tbreak;\n\tcase PTI_3D:\n\t\tif (sh_config.texture3d_maxsize)\n\t\t{\n\t\t\tif (*scaled_width > sh_config.texture3d_maxsize)\n\t\t\t\t*scaled_width = sh_config.texture3d_maxsize;\n\t\t\tif (*scaled_height > sh_config.texture3d_maxsize)\n\t\t\t\t*scaled_height = sh_config.texture3d_maxsize;\n\t\t\tif (*scaled_depth > sh_config.texture3d_maxsize)\n\t\t\t\t*scaled_depth = sh_config.texture3d_maxsize;\n\t\t}\n\t\tbreak;\n\tcase PTI_2D:\n\tcase PTI_2D_ARRAY:\n\t\tif (sh_config.texture2d_maxsize)\n\t\t{\n\t\t\tif (*scaled_width > sh_config.texture2d_maxsize)\n\t\t\t\t*scaled_width = sh_config.texture2d_maxsize;\n\t\t\tif (*scaled_height > sh_config.texture2d_maxsize)\n\t\t\t\t*scaled_height = sh_config.texture2d_maxsize;\n\t\t}\n\t\tif (sh_config.texture2darray_maxlayers)\n\t\t\tif (*scaled_depth > sh_config.texture2darray_maxlayers)\n\t\t\t\t*scaled_depth = sh_config.texture2darray_maxlayers;\n\n#ifdef HAVE_CLIENT\n\t\tif (!(flags & (IF_UIPIC|IF_RENDERTARGET)))\n\t\t{\n\t\t\tif (gl_max_size.value)\n\t\t\t{\n\t\t\t\tif (*scaled_width > gl_max_size.value)\n\t\t\t\t\t*scaled_width = gl_max_size.value;\n\t\t\t\tif (*scaled_height > gl_max_size.value)\n\t\t\t\t\t*scaled_height = gl_max_size.value;\n\t\t\t\tif (*scaled_height > gl_max_size.value)\n\t\t\t\t\t*scaled_height = gl_max_size.value;\n\t\t\t}\n\t\t}\n#endif\n\t\tbreak;\n\t}\n\n\tif (*scaled_width < 1)\n\t\t*scaled_width = 1;\n\tif (*scaled_height < 1)\n\t\t*scaled_height = 1;\n\tif (*scaled_depth < 1)\n\t\t*scaled_depth = 1;\n}\n\nstatic void Image_Tr_NoTransform(struct pendingtextureinfo *mips, int dummy)\n{\n}\n\nstatic void Image_Tr_PalettedtoRGBX8(struct pendingtextureinfo *mips, int alphapix)\n{\n\tunsigned int mip;\n\tfor (mip = 0; mip < mips->mipcount; mip++)\n\t{\n\t\tqbyte *in = mips->mip[mip].data;\n\t\tunsigned int p = mips->mip[mip].width*mips->mip[mip].height*mips->mip[mip].depth;\n\t\tqbyte *out;\n\t\tsize_t datasize = sizeof(*out)*4*p;\n\t\tvoid *newdata = out = BZ_Malloc(datasize);\n\n\t\twhile(p-->0)\n\t\t{\n\t\t\tunsigned int l = *in++;\n\t\t\t*out++ = host_basepal[l*3+0];\n\t\t\t*out++ = host_basepal[l*3+1];\n\t\t\t*out++ = host_basepal[l*3+2];\n\t\t\t*out++ = (l==alphapix)?0:255;\n\t\t}\n\t\tif (mips->mip[mip].needfree)\n\t\t\tBZ_Free(mips->mip[mip].data);\n\t\tmips->mip[mip].needfree = true;\n\t\tmips->mip[mip].data = newdata;\n\t\tmips->mip[mip].datasize = datasize;\n\t}\n}\n\n//may operate in place\nstatic void Image_Tr_RGBX8toPaletted(struct pendingtextureinfo *mips, int args)\n{\n\tunsigned int mip;\n\tint first=args&0xffff;\n\tint stop=(args>>16)&0xffff;\n\tfor (mip = 0; mip < mips->mipcount; mip++)\n\t{\n\t\tqbyte *in = mips->mip[mip].data;\n\t\tqbyte *out = mips->mip[mip].data;\n\t\tunsigned int p = mips->mip[mip].width*mips->mip[mip].height*mips->mip[mip].depth;\n\t\tunsigned short tmp;\n\t\tif (!mips->mip[mip].needfree && !mips->extrafree)\n\t\t{\n\t\t\tmips->mip[mip].needfree = true;\n\t\t\tmips->mip[mip].data = out = BZ_Malloc(sizeof(tmp)*p);\n\t\t}\n\t\tmips->mip[mip].datasize = p*sizeof(*out);\n\n\t\tfor(; p-->0; in += 4)\n\t\t\t*out++ = GetPaletteIndexRange(first, stop, in[0], in[1], in[2]);\n\t}\n}\nstatic void Image_Tr_RGBA8toPaletted(struct pendingtextureinfo *mips, int args)\n{\n\tunsigned int mip;\n\tint first=args&0xffff;\n\tint stop=(args>>16)&0xffff;\n\tint tr = first?0:255;\n\tfor (mip = 0; mip < mips->mipcount; mip++)\n\t{\n\t\tqbyte *in = mips->mip[mip].data;\n\t\tqbyte *out = mips->mip[mip].data;\n\t\tunsigned int p = mips->mip[mip].width*mips->mip[mip].height*mips->mip[mip].depth;\n\t\tunsigned short tmp;\n\t\tif (!mips->mip[mip].needfree && !mips->extrafree)\n\t\t{\n\t\t\tmips->mip[mip].needfree = true;\n\t\t\tmips->mip[mip].data = out = BZ_Malloc(sizeof(tmp)*p);\n\t\t}\n\t\tmips->mip[mip].datasize = p*sizeof(*out);\n\n\t\tfor(; p-->0; in += 4)\n\t\t{\n\t\t\tif (in[3] < 128)\n\t\t\t\t*out++ = tr;\n\t\t\telse\n\t\t\t\t*out++ = GetPaletteIndexRange(first, stop, in[0], in[1], in[2]);\n\t\t}\n\t}\n}\n\n//may operate in place\nstatic void Image_Tr_8888toLuminence(struct pendingtextureinfo *mips, int channels)\n{\t//channels==1?L8\n\t//channels==2?L8A8\n\tunsigned int mip;\n\tfor (mip = 0; mip < mips->mipcount; mip++)\n\t{\n\t\tqbyte l, a;\n\t\tqbyte *in = mips->mip[mip].data;\n\t\tqbyte *out = mips->mip[mip].data;\n\t\tunsigned int p = mips->mip[mip].width*mips->mip[mip].height*mips->mip[mip].depth;\n\t\tunsigned short tmp;\n\t\tif (!mips->mip[mip].needfree && !mips->extrafree)\n\t\t{\n\t\t\tmips->mip[mip].needfree = true;\n\t\t\tmips->mip[mip].data = out = BZ_Malloc(sizeof(tmp)*p*channels);\n\t\t}\n\t\tmips->mip[mip].datasize = p*sizeof(*out)*channels;\n\n\t\tfor(; p-->0; in += 4)\n\t\t{\n\t\t\tl = (in[0]+in[1]+in[2])/3;\n\t\t\ta = in[3];\n\n\t\t\t*out++ = l;\n\t\t\tif (channels == 2)\n\t\t\t\t*out++ = a;\n\t\t}\n\t}\n}\n//may operate in place\nstatic void Image_Tr_8888to565(struct pendingtextureinfo *mips, int bgra)\n{\n\tunsigned int mip;\n\tfor (mip = 0; mip < mips->mipcount; mip++)\n\t{\n\t\tqbyte *in = mips->mip[mip].data;\n\t\tunsigned short *out = mips->mip[mip].data;\n\t\tunsigned int p = mips->mip[mip].width*mips->mip[mip].height*mips->mip[mip].depth;\n\t\tunsigned short tmp;\n\t\tif (!mips->mip[mip].needfree && !mips->extrafree)\n\t\t{\n\t\t\tmips->mip[mip].needfree = true;\n\t\t\tmips->mip[mip].data = out = BZ_Malloc(sizeof(tmp)*p);\n\t\t}\n\t\tmips->mip[mip].datasize = p*sizeof(*out);\n\n\t\tif (bgra)\n\t\t{\n\t\t\twhile(p-->0)\n\t\t\t{\n\t\t\t\ttmp  = ((*in++>>3) << 0);//b\n\t\t\t\ttmp |= ((*in++>>2) << 5);//g\n\t\t\t\ttmp |= ((*in++>>3) << 11);//r\n\t\t\t\tin++;\n\t\t\t\t*out++ = tmp;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\twhile(p-->0)\n\t\t\t{\n\t\t\t\ttmp  = ((*in++>>3) << 11);//r\n\t\t\t\ttmp |= ((*in++>>2) << 5);//g\n\t\t\t\ttmp |= ((*in++>>3) << 0);//b\n\t\t\t\tin++;\n\t\t\t\t*out++ = tmp;\n\t\t\t}\n\t\t}\n\t}\n}\n\nstatic void Image_Tr_8888to1555(struct pendingtextureinfo *mips, int bgra)\n{\n\tunsigned int mip;\n\tfor (mip = 0; mip < mips->mipcount; mip++)\n\t{\n\t\tqbyte *in = mips->mip[mip].data;\n\t\tunsigned short *out = mips->mip[mip].data;\n\t\tunsigned int p = mips->mip[mip].width*mips->mip[mip].height*mips->mip[mip].depth;\n\t\tunsigned short tmp;\n\t\tif (!mips->mip[mip].needfree && !mips->extrafree)\n\t\t{\n\t\t\tmips->mip[mip].needfree = true;\n\t\t\tmips->mip[mip].data = out = BZ_Malloc(sizeof(tmp)*p);\n\t\t}\n\t\tmips->mip[mip].datasize = p*sizeof(*out);\n\n\t\tif (bgra)\n\t\t{\n\t\t\twhile(p-->0)\n\t\t\t{\n\t\t\t\ttmp  = ((*in++>>3) << 0);//b\n\t\t\t\ttmp |= ((*in++>>3) << 5);//g\n\t\t\t\ttmp |= ((*in++>>3) << 10);//r\n\t\t\t\ttmp |= ((*in++>>7) << 15);//a\n\t\t\t\t*out++ = tmp;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\twhile(p-->0)\n\t\t\t{\n\t\t\t\ttmp  = ((*in++>>3) << 10);//r\n\t\t\t\ttmp |= ((*in++>>3) << 5);//g\n\t\t\t\ttmp |= ((*in++>>3) << 0);//b\n\t\t\t\ttmp |= ((*in++>>7) << 15);//a\n\t\t\t\t*out++ = tmp;\n\t\t\t}\n\t\t}\n\t}\n}\n\nstatic void Image_Tr_8888to5551(struct pendingtextureinfo *mips, int bgra)\t//zomg\n{\n\tunsigned int mip;\n\tfor (mip = 0; mip < mips->mipcount; mip++)\n\t{\n\t\tqbyte *in = mips->mip[mip].data;\n\t\tunsigned short *out = mips->mip[mip].data;\n\t\tunsigned int p = mips->mip[mip].width*mips->mip[mip].height*mips->mip[mip].depth;\n\t\tunsigned short tmp;\n\t\tif (!mips->mip[mip].needfree && !mips->extrafree)\n\t\t{\n\t\t\tmips->mip[mip].needfree = true;\n\t\t\tmips->mip[mip].data = out = BZ_Malloc(sizeof(tmp)*p);\n\t\t}\n\t\tmips->mip[mip].datasize = p*sizeof(*out);\n\n\t\tif (bgra)\n\t\t{\n\t\t\twhile(p-->0)\n\t\t\t{\n\t\t\t\ttmp  = ((*in++>>3) << 1);//b\n\t\t\t\ttmp |= ((*in++>>3) << 6);//g\n\t\t\t\ttmp |= ((*in++>>3) << 11);//r\n\t\t\t\ttmp |= ((*in++>>7) << 0);//a\n\t\t\t\t*out++ = tmp;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\twhile(p-->0)\n\t\t\t{\n\t\t\t\ttmp  = ((*in++>>3) << 11);//r\n\t\t\t\ttmp |= ((*in++>>3) << 6);//g\n\t\t\t\ttmp |= ((*in++>>3) << 1);//b\n\t\t\t\ttmp |= ((*in++>>7) << 0);//a\n\t\t\t\t*out++ = tmp;\n\t\t\t}\n\t\t}\n\t}\n}\n\nstatic void Image_Tr_RGBA5551to8888(struct pendingtextureinfo *mips, int dummy)\n{\t//expands\n\tunsigned int mip;\n\tfor (mip = 0; mip < mips->mipcount; mip++)\n\t{\n\t\tunsigned short *in = mips->mip[mip].data;\n\t\tunsigned char *out = mips->mip[mip].data;\n\t\tvoid *dofree = mips->mip[mip].needfree?in:NULL;\n\t\tunsigned int p = mips->mip[mip].width*mips->mip[mip].height*mips->mip[mip].depth;\n\t\tmips->mip[mip].needfree = true;\n\t\tmips->mip[mip].data = out = BZ_Malloc(sizeof(*out)*p*4);\n\t\tmips->mip[mip].datasize = p*sizeof(*out)*4;\n\t\twhile(p-->0)\n\t\t{\n\t\t\tunsigned short l = *in++;\n\t\t\tunsigned char r = (l>> 11)&0x1f;\n\t\t\tunsigned char g = (l>> 6)&0x1f;\n\t\t\tunsigned char b = (l>> 1)&0x1f;\n\t\t\tunsigned char a = (l>> 0)&0x1;\n\t\t\t*out++ = (r<<3)|(r>>2);\n\t\t\t*out++ = (g<<3)|(g>>2);\n\t\t\t*out++ = (b<<3)|(b>>2);\n\t\t\t*out++ = a * 0xff;\n\t\t}\n\t\tBZ_Free(dofree);\n\t}\n}\nstatic void Image_Tr_ARGB1555to8888(struct pendingtextureinfo *mips, int dummy)\n{\t//expands\n\tunsigned int mip;\n\tfor (mip = 0; mip < mips->mipcount; mip++)\n\t{\n\t\tunsigned short *in = mips->mip[mip].data;\n\t\tunsigned char *out = mips->mip[mip].data;\n\t\tvoid *dofree = mips->mip[mip].needfree?in:NULL;\n\t\tunsigned int p = mips->mip[mip].width*mips->mip[mip].height*mips->mip[mip].depth;\n\t\tmips->mip[mip].needfree = true;\n\t\tmips->mip[mip].data = out = BZ_Malloc(sizeof(*out)*p*4);\n\t\tmips->mip[mip].datasize = p*sizeof(*out)*4;\n\t\twhile(p-->0)\n\t\t{\n\t\t\tunsigned short l = *in++;\n\t\t\tunsigned char r = (l>> 0)&0x1f;\n\t\t\tunsigned char g = (l>> 5)&0x1f;\n\t\t\tunsigned char b = (l>>10)&0x1f;\n\t\t\tunsigned char a = (l>>15)&0x1;\n\t\t\t*out++ = (r<<3)|(r>>2);\n\t\t\t*out++ = (g<<3)|(g>>2);\n\t\t\t*out++ = (b<<3)|(b>>2);\n\t\t\t*out++ = a * 0xff;\n\t\t}\n\t\tBZ_Free(dofree);\n\t}\n}\n\n\n\nstatic void Image_Tr_8888to4444(struct pendingtextureinfo *mips, int bgra)\n{\n\tunsigned int mip;\n\tfor (mip = 0; mip < mips->mipcount; mip++)\n\t{\n\t\tqbyte *in = mips->mip[mip].data;\n\t\tunsigned short *out = mips->mip[mip].data;\n\t\tunsigned int p = mips->mip[mip].width*mips->mip[mip].height*mips->mip[mip].depth;\n\t\tunsigned short tmp;\n\t\tif (!mips->mip[mip].needfree && !mips->extrafree)\n\t\t{\n\t\t\tmips->mip[mip].needfree = true;\n\t\t\tmips->mip[mip].data = out = BZ_Malloc(sizeof(tmp)*p);\n\t\t}\n\t\tmips->mip[mip].datasize = p*sizeof(*out);\n\n\t\tif (bgra)\n\t\t{\n\t\t\twhile(p-->0)\n\t\t\t{\n\t\t\t\ttmp  = ((*in++>>4) << 4);//b\n\t\t\t\ttmp |= ((*in++>>4) << 8);//g\n\t\t\t\ttmp |= ((*in++>>4) << 12);//r\n\t\t\t\ttmp |= ((*in++>>4) << 0);//a\n\t\t\t\t*out++ = tmp;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\twhile(p-->0)\n\t\t\t{\n\t\t\t\ttmp  = ((*in++>>4) << 12);//r\n\t\t\t\ttmp |= ((*in++>>4) << 8);//g\n\t\t\t\ttmp |= ((*in++>>4) << 4);//b\n\t\t\t\ttmp |= ((*in++>>4) << 0);//a\n\t\t\t\t*out++ = tmp;\n\t\t\t}\n\t\t}\n\t}\n}\n//may operate in place\nstatic void Image_Tr_8888toARGB4444(struct pendingtextureinfo *mips, int bgra)\n{\n\tunsigned int mip;\n\tfor (mip = 0; mip < mips->mipcount; mip++)\n\t{\n\t\tqbyte *in = mips->mip[mip].data;\n\t\tunsigned short *out = mips->mip[mip].data;\n\t\tunsigned int p = mips->mip[mip].width*mips->mip[mip].height*mips->mip[mip].depth;\n\t\tunsigned short tmp;\n\t\tif (!mips->mip[mip].needfree && !mips->extrafree)\n\t\t{\n\t\t\tmips->mip[mip].needfree = true;\n\t\t\tmips->mip[mip].data = out = BZ_Malloc(sizeof(tmp)*p);\n\t\t}\n\t\tmips->mip[mip].datasize = p*sizeof(*out);\n\n\t\tif (bgra)\n\t\t{\n\t\t\twhile(p-->0)\n\t\t\t{\n\t\t\t\ttmp  = ((*in++>>4) << 0);//b\n\t\t\t\ttmp |= ((*in++>>4) << 4);//g\n\t\t\t\ttmp |= ((*in++>>4) << 8);//r\n\t\t\t\ttmp |= ((*in++>>4) << 12);//a\n\t\t\t\t*out++ = tmp;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\twhile(p-->0)\n\t\t\t{\n\t\t\t\ttmp  = ((*in++>>4) << 8);//r\n\t\t\t\ttmp |= ((*in++>>4) << 4);//g\n\t\t\t\ttmp |= ((*in++>>4) << 0);//b\n\t\t\t\ttmp |= ((*in++>>4) << 12);//a\n\t\t\t\t*out++ = tmp;\n\t\t\t}\n\t\t}\n\t}\n}\n\nstatic void Image_Tr_4444to8888(struct pendingtextureinfo *mips, int dummy)\n{\t//expands\n\tunsigned int mip;\n\tfor (mip = 0; mip < mips->mipcount; mip++)\n\t{\n\t\tunsigned short *in = mips->mip[mip].data;\n\t\tunsigned char *out = mips->mip[mip].data;\n\t\tvoid *dofree = mips->mip[mip].needfree?in:NULL;\n\t\tunsigned int p = mips->mip[mip].width*mips->mip[mip].height*mips->mip[mip].depth;\n\t\tmips->mip[mip].needfree = true;\n\t\tmips->mip[mip].data = out = BZ_Malloc(sizeof(*out)*p*4);\n\t\tmips->mip[mip].datasize = p*sizeof(*out)*4;\n\t\twhile(p-->0)\n\t\t{\n\t\t\tunsigned short l = *in++;\n\t\t\t*out++ = ((l>>12)&0xf) * 0x11;\n\t\t\t*out++ = ((l>> 8)&0xf) * 0x11;\n\t\t\t*out++ = ((l>> 4)&0xf) * 0x11;\n\t\t\t*out++ = ((l>> 0)&0xf) * 0x11;\n\t\t}\n\t\tBZ_Free(dofree);\n\t}\n}\nstatic void Image_Tr_ARGB4444to8888(struct pendingtextureinfo *mips, int dummy)\n{\t//expands\n\tunsigned int mip;\n\tfor (mip = 0; mip < mips->mipcount; mip++)\n\t{\n\t\tunsigned short *in = mips->mip[mip].data;\n\t\tunsigned char *out = mips->mip[mip].data;\n\t\tvoid *dofree = mips->mip[mip].needfree?in:NULL;\n\t\tunsigned int p = mips->mip[mip].width*mips->mip[mip].height*mips->mip[mip].depth;\n\t\tmips->mip[mip].needfree = true;\n\t\tmips->mip[mip].data = out = BZ_Malloc(sizeof(*out)*p*4);\n\t\tmips->mip[mip].datasize = p*sizeof(*out)*4;\n\t\twhile(p-->0)\n\t\t{\n\t\t\tunsigned short l = *in++;\n\t\t\t*out++ = ((l>> 0)&0xf) * 0x11;\n\t\t\t*out++ = ((l>> 4)&0xf) * 0x11;\n\t\t\t*out++ = ((l>> 8)&0xf) * 0x11;\n\t\t\t*out++ = ((l>>12)&0xf) * 0x11;\n\t\t}\n\t\tBZ_Free(dofree);\n\t}\n}\n\nstatic void Image_Tr_4X16to8888(struct pendingtextureinfo *mips, int unused)\n{\n\tunsigned int mip;\n\tfor (mip = 0; mip < mips->mipcount; mip++)\n\t{\n\t\tunsigned short *in = mips->mip[mip].data;\n\t\tqbyte *out = mips->mip[mip].data;\n\t\tunsigned int p = mips->mip[mip].width*mips->mip[mip].height*mips->mip[mip].depth*4;\n\t\tif (!mips->mip[mip].needfree && !mips->extrafree)\n\t\t{\n\t\t\tmips->mip[mip].needfree = true;\n\t\t\tmips->mip[mip].data = out = BZ_Malloc(sizeof(*out)*p);\n\t\t}\n\t\tmips->mip[mip].datasize = p*sizeof(*out);\n\n\t\twhile(p-->0)\n\t\t\t*out++ = *in++>>8;\n\t}\n}\n\n//in place: E5BGR9->RGBA8\nstatic void Image_Tr_E5BGR9ToByte(struct pendingtextureinfo *mips, int bgr)\n{\n\tunsigned int mip;\n\tint rs = bgr?18:0;\n\tint bs = bgr?0:18;\n\tfor (mip = 0; mip < mips->mipcount; mip++)\n\t{\n\t\tunsigned int *in = mips->mip[mip].data;\n\t\tqbyte *out = mips->mip[mip].data;\n\t\tunsigned int p = mips->mip[mip].width*mips->mip[mip].height*mips->mip[mip].depth;\n\t\tif (!mips->mip[mip].needfree && !mips->extrafree)\n\t\t{\n\t\t\tmips->mip[mip].needfree = true;\n\t\t\tmips->mip[mip].data = out = BZ_Malloc(sizeof(*out)*4*p);\n\t\t}\n\t\tmips->mip[mip].datasize = p*sizeof(*out)*4;\n\n\t\twhile(p-->0)\n\t\t{\n\t\t\tunsigned int l = *in++;\n\t\t\tfloat e = rgb9e5tab[l>>27];\n\t\t\te *= 255; //prescale to bytes.\n\t\t\t*out++ = bound(0, e * ((l>>rs)&0x1ff), 255);\n\t\t\t*out++ = bound(0, e * ((l>> 9)&0x1ff), 255);\n\t\t\t*out++ = bound(0, e * ((l>>bs)&0x1ff), 255);\n\t\t\t*out++ = 255;\n\t\t}\n\t}\n}\n//expands the data so must always realloc\nstatic void Image_Tr_E5BGR9ToFloat(struct pendingtextureinfo *mips, int dummy)\n{\n\tunsigned int mip;\n\tfor (mip = 0; mip < mips->mipcount; mip++)\n\t{\n\t\tunsigned int *in = mips->mip[mip].data;\n\t\tfloat *out = mips->mip[mip].data;\n\t\tunsigned int p = mips->mip[mip].width*mips->mip[mip].height*mips->mip[mip].depth;\n\t\tif (1)//!mips->mip[mip].needfree && !mips->extrafree)\n\t\t{\n\t\t\tmips->mip[mip].needfree = true;\n\t\t\tmips->mip[mip].data = out = BZ_Malloc(sizeof(*out)*4*p);\n\t\t}\n\t\tmips->mip[mip].datasize = p*sizeof(*out)*4;\n\n\t\twhile(p-->0)\n\t\t{\n\t\t\tunsigned int l = *in++;\n\t\t\tfloat e = rgb9e5tab[l>>27];\n\t\t\t*out++ = e * ((l>> 0)&0x1ff);\n\t\t\t*out++ = e * ((l>> 9)&0x1ff);\n\t\t\t*out++ = e * ((l>>18)&0x1ff);\n\t\t\t*out++ = 1.0;\n\t\t}\n\t}\n}\n//always out of place\nstatic void Image_Tr_FloatToE5BGR9(struct pendingtextureinfo *mips, int dummy)\n{\n\tunsigned int mip;\n\tfor (mip = 0; mip < mips->mipcount; mip++)\n\t{\n\t\tfloat *in = mips->mip[mip].data;\n\t\tunsigned int *out = mips->mip[mip].data;\n\t\tfloat *dofree = mips->mip[mip].needfree?in:NULL;\n\t\tunsigned int p = mips->mip[mip].width*mips->mip[mip].height*mips->mip[mip].depth;\n\t\tmips->mip[mip].needfree = true;\n\t\tmips->mip[mip].datasize = p*sizeof(*out);\n\t\tmips->mip[mip].data = out = BZ_Malloc(mips->mip[mip].datasize);\n\t\tfor (; p-->0; out++, in+=4)\n\t\t{\n\t\t\tint e = 0;\n\t\t\tfloat m = max(max(in[0], in[1]), in[2]);\n\t\t\tfloat scale;\n\t\t\tif (m >= 0.5)\n\t\t\t{\t//positive exponent\n\t\t\t\twhile (m >= (1<<(e)) && e < 30-15)\t//don't do nans.\n\t\t\t\t\te++;\n\t\t\t}\n\t\t\telse\n\t\t\t{\t//negative exponent...\n\t\t\t\twhile (m < 1/(1<<-e) && e > -15)\t//don't do denormals.\n\t\t\t\t\te--;\n\t\t\t}\n\t\t\tscale = pow(2, e-9);\n\t\t\t*out = ((e+15)<<27);\n\t\t\t*out |= bound(0, (int)(in[0]/scale + 0.5), 0x1ff)<<0;\n\t\t\t*out |= bound(0, (int)(in[1]/scale + 0.5), 0x1ff)<<9;\n\t\t\t*out |= bound(0, (int)(in[2]/scale + 0.5), 0x1ff)<<18;\n\t\t}\n\t\tBZ_Free(dofree);\n\t}\n}\n\nstatic void Image_Tr_PackedToFloat(struct pendingtextureinfo *mips, int dummy)\n{\n\tunsigned int mip;\n\tfor (mip = 0; mip < mips->mipcount; mip++)\n\t{\n\t\tunsigned int *in = mips->mip[mip].data;\n\t\tfloat *out = mips->mip[mip].data;\n\t\tvoid *dofree = mips->mip[mip].needfree?in:NULL;\n\t\tunsigned int p = mips->mip[mip].width*mips->mip[mip].height*mips->mip[mip].depth;\n\t\tmips->mip[mip].needfree = true;\n\t\tmips->mip[mip].data = out = BZ_Malloc(sizeof(*out)*p*4);\n\t\tmips->mip[mip].datasize = p*sizeof(*out)*4;\n\t\twhile(p-->0)\n\t\t{\n\t\t\tunsigned int l = *in++;\n\t\t\t*out++ = SmallToFloat(l>> 0, 11);\n\t\t\t*out++ = SmallToFloat(l>>11, 11);\n\t\t\t*out++ = SmallToFloat(l>>22, 10);\n\t\t\t*out++ = 1.0;\n\t\t}\n\t\tBZ_Free(dofree);\n\t}\n}\n//always out of place\nstatic void Image_Tr_FloatToPacked(struct pendingtextureinfo *mips, int dummy)\n{\n\tunsigned int mip;\n\tfor (mip = 0; mip < mips->mipcount; mip++)\n\t{\n\t\tfloat *in = mips->mip[mip].data;\n\t\tunsigned int *out = mips->mip[mip].data;\n\t\tfloat *dofree = mips->mip[mip].needfree?in:NULL;\n\t\tunsigned int p = mips->mip[mip].width*mips->mip[mip].height*mips->mip[mip].depth;\n\t\tmips->mip[mip].needfree = true;\n\t\tmips->mip[mip].data = out = BZ_Malloc(sizeof(*out)*p);\n\t\tmips->mip[mip].datasize = p*sizeof(*out);\n\t\tfor (; p-->0; out++, in+=4)\n\t\t{\n\t\t\t*out  = FloatToSmall(in[0], 11)<< 0;\n\t\t\t*out |= FloatToSmall(in[1], 11)<<11;\n\t\t\t*out |= FloatToSmall(in[2], 10)<<22;\n\t\t}\n\t\tBZ_Free(dofree);\n\t}\n}\n\n//in place: RGBA16F->RGBA8, or R16F->R8\nstatic void Image_Tr_HalfToByte(struct pendingtextureinfo *mips, int channels)\n{\n\tunsigned int mip;\n\tqboolean bs = channels == -4;\n\tfor (mip = 0; mip < mips->mipcount; mip++)\n\t{\n\t\tint v;\n\t\tunsigned short *in = mips->mip[mip].data;\n\t\tqbyte *out = mips->mip[mip].data;\n\t\tunsigned int p = mips->mip[mip].width*mips->mip[mip].height*mips->mip[mip].depth*abs(channels);\n\t\tif (!mips->mip[mip].needfree && !mips->extrafree)\n\t\t{\n\t\t\tmips->mip[mip].needfree = true;\n\t\t\tmips->mip[mip].data = out = BZ_Malloc(sizeof(*out)*p);\n\t\t}\n\t\tmips->mip[mip].datasize = p*sizeof(*out);\n\n\t\tif (bs)\n\t\t{\t//to bgra\n\t\t\tfor (; p>0; p-=4, out+=4, in+=4)\n\t\t\t{\n\t\t\t\tv = HalfToFloat(in[2])*255;\n\t\t\t\tout[0] = bound(0, v, 255);\n\t\t\t\tv = HalfToFloat(in[1])*255;\n\t\t\t\tout[1] = bound(0, v, 255);\n\t\t\t\tv = HalfToFloat(in[0])*255;\n\t\t\t\tout[2] = bound(0, v, 255);\n\t\t\t\tv = HalfToFloat(in[3])*255;\n\t\t\t\tout[3] = bound(0, v, 255);\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\twhile(p-->0)\n\t\t\t{\n\t\t\t\tv = HalfToFloat(*in++)*255;\n\t\t\t\t*out++ = bound(0, v, 255);\n\t\t\t}\n\t\t}\n\t}\n}\n\n//always out of place\nstatic void Image_Tr_RGB32FToFloat(struct pendingtextureinfo *mips, int dummy)\n{\n\tunsigned int mip;\n\tfor (mip = 0; mip < mips->mipcount; mip++)\n\t{\n\t\tfloat *in = mips->mip[mip].data;\n\t\tfloat *out = mips->mip[mip].data;\n\t\tfloat *dofree = mips->mip[mip].needfree?in:NULL;\n\t\tunsigned int p = mips->mip[mip].width*mips->mip[mip].height*mips->mip[mip].depth;\n\t\tmips->mip[mip].needfree = true;\n\t\tmips->mip[mip].data = out = BZ_Malloc(sizeof(*out)*p*4);\n\t\tmips->mip[mip].datasize = p*sizeof(*out)*4;\n\t\twhile(p-->0)\n\t\t{\n\t\t\t*out++ = *in++;\n\t\t\t*out++ = *in++;\n\t\t\t*out++ = *in++;\n\t\t\t*out++ = 1.0;\n\t\t}\n\t\tBZ_Free(dofree);\n\t}\n}\n\n//always out of place\nstatic void Image_Tr_HalfToFloat(struct pendingtextureinfo *mips, int channels)\n{\n\tunsigned int mip;\n\tfor (mip = 0; mip < mips->mipcount; mip++)\n\t{\n\t\tunsigned short *in = mips->mip[mip].data;\n\t\tfloat *out = mips->mip[mip].data;\n\t\tunsigned short *dofree = mips->mip[mip].needfree?in:NULL;\n\t\tunsigned int p = mips->mip[mip].width*mips->mip[mip].height*mips->mip[mip].depth*channels;\n\t\tmips->mip[mip].needfree = true;\n\t\tmips->mip[mip].datasize = p*sizeof(*out);\n\t\tmips->mip[mip].data = out = BZ_Malloc(mips->mip[mip].datasize);\n\t\twhile(p-->0)\n\t\t\t*out++ = HalfToFloat(*in++);\n\t\tBZ_Free(dofree);\n\t}\n}\n\n//in place: RGBA32F->RGBA16F\nstatic void Image_Tr_FloatToHalf(struct pendingtextureinfo *mips, int channels)\n{\n\tunsigned int mip;\n\tfor (mip = 0; mip < mips->mipcount; mip++)\n\t{\n\t\tfloat *in = mips->mip[mip].data;\n\t\tunsigned short *out = mips->mip[mip].data;\n\t\tunsigned int p = mips->mip[mip].width*mips->mip[mip].height*mips->mip[mip].depth*channels;\n\t\tif (!mips->mip[mip].needfree && !mips->extrafree)\n\t\t{\n\t\t\tmips->mip[mip].needfree = true;\n\t\t\tmips->mip[mip].data = out = BZ_Malloc(sizeof(*out)*p);\n\t\t}\n\t\tmips->mip[mip].datasize = p*sizeof(*out);\n\n\t\twhile(p-->0)\n\t\t\t*out++ = FloatToHalf(*in++);\n\t}\n}\n\n//in place: RGBA32F->RGBA8, or R32F->R8\nstatic void Image_Tr_FloatToByte(struct pendingtextureinfo *mips, int channels)\n{\n\tunsigned int mip;\n\tint bs = channels == -4;\n\tfor (mip = 0; mip < mips->mipcount; mip++)\n\t{\n\t\tint v;\n\t\tfloat *in = mips->mip[mip].data;\n\t\tqbyte *out = mips->mip[mip].data;\n\t\tunsigned int p = mips->mip[mip].width*mips->mip[mip].height*mips->mip[mip].depth*abs(channels);\n\t\tif (!mips->mip[mip].needfree && !mips->extrafree)\n\t\t{\n\t\t\tmips->mip[mip].needfree = true;\n\t\t\tmips->mip[mip].data = out = BZ_Malloc(sizeof(*out)*p);\n\t\t}\n\t\tmips->mip[mip].datasize = p*sizeof(*out);\n\n\t\tif (bs)\n\t\t{\t//to bgra\n\t\t\tfor (; p>0; p-=4, out+=4, in+=4)\n\t\t\t{\n\t\t\t\tv = in[2]*255;\n\t\t\t\tout[0] = bound(0, v, 255);\n\t\t\t\tv = in[1]*255;\n\t\t\t\tout[1] = bound(0, v, 255);\n\t\t\t\tv = in[0]*255;\n\t\t\t\tout[2] = bound(0, v, 255);\n\t\t\t\tv = in[3]*255;\n\t\t\t\tout[3] = bound(0, v, 255);\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\twhile(p-->0)\n\t\t\t{\n\t\t\t\tv = *in++*255;\n\t\t\t\t*out++ = bound(0, v, 255);\n\t\t\t}\n\t\t}\n\t}\n}\n\nstatic void Image_Tr_ByteToFloat(struct pendingtextureinfo *mips, int channels)\n{\t//expands\n\tint mip;\n\tfor (mip = 0; mip < mips->mipcount; mip++)\n\t{\n\t\tqbyte *in = mips->mip[mip].data;\n\t\tfloat *out = mips->mip[mip].data;\n\t\tvoid *dofree = mips->mip[mip].needfree?in:NULL;\n\t\tunsigned int p = mips->mip[mip].width*mips->mip[mip].height*mips->mip[mip].depth*channels;\n\t\tmips->mip[mip].needfree = true;\n\t\tmips->mip[mip].data = out = BZ_Malloc(sizeof(*out)*p);\n\t\tmips->mip[mip].datasize = p*sizeof(*out);\n\t\twhile(p-->0)\n\t\t\t*out++ = *in++/255.0;\n\t\tBZ_Free(dofree);\n\t}\n}\n\n//in place, drops trailing bytes\nstatic void Image_Tr_DropBytes(struct pendingtextureinfo *mips, int srcbitsdstbits)\n{\n\tint srcbytes=srcbitsdstbits>>16;\n\tint dstbytes=srcbitsdstbits&0xffff;\n\tint mip, b;\n\tfor (mip = 0; mip < mips->mipcount; mip++)\n\t{\n\t\tqbyte *in = mips->mip[mip].data;\n\t\tqbyte *out = mips->mip[mip].data;\n\t\tunsigned int p = mips->mip[mip].width*mips->mip[mip].height*mips->mip[mip].depth;\n\t\tif (!mips->mip[mip].needfree && !mips->extrafree)\n\t\t{\n\t\t\tmips->mip[mip].needfree = true;\n\t\t\tmips->mip[mip].data = out = BZ_Malloc(sizeof(*out)*p*dstbytes);\n\t\t}\n\t\tmips->mip[mip].datasize = p*sizeof(*out)*dstbytes;\n\n\t\twhile(p-->0)\n\t\t{\n\t\t\tfor (b = 0; b < dstbytes; b++)\n\t\t\t\t*out++ = *in++;\n\t\t\tin += srcbytes-dstbytes;\n\t\t}\n\t}\n}\n\nstatic void Image_Tr_RG8ToRGXX8(struct pendingtextureinfo *mips, int dummy)\n{\n\tint mip;\n\tfor (mip = 0; mip < mips->mipcount; mip++)\n\t{\n\t\tqbyte *in = mips->mip[mip].data;\n\t\tqbyte *out = mips->mip[mip].data;\n\t\tvoid *dofree = mips->mip[mip].needfree?in:NULL;\n\t\tunsigned int p = mips->mip[mip].width*mips->mip[mip].height*mips->mip[mip].depth;\n\t\tmips->mip[mip].needfree = true;\n\t\tmips->mip[mip].data = out = BZ_Malloc(sizeof(*out)*p*4);\n\t\tmips->mip[mip].datasize = p*sizeof(*out)*4;\n\t\twhile(p-->0)\n\t\t{\n\t\t\t*out++ = in[0];\n\t\t\t*out++ = in[1];\n\t\t\t*out++ = 0;\n\t\t\t*out++ = 0xff;\n\t\t\tin+=2;\n\t\t}\n\t\tBZ_Free(dofree);\n\t}\n}\n\nstatic void Image_Tr_8To10(struct pendingtextureinfo *mips, int dummy)\n{\n\tint mip;\n\tfor (mip = 0; mip < mips->mipcount; mip++)\n\t{\n\t\tqbyte *in = mips->mip[mip].data;\n\t\tunsigned int *out = mips->mip[mip].data;\n\t\tunsigned int p = mips->mip[mip].width*mips->mip[mip].height*mips->mip[mip].depth;\n\t\tif (!mips->mip[mip].needfree && !mips->extrafree)\n\t\t{\n\t\t\tmips->mip[mip].needfree = true;\n\t\t\tmips->mip[mip].data = out = BZ_Malloc(sizeof(*out)*p);\n\t\t}\n\t\tmips->mip[mip].datasize = p*sizeof(*out);\n\n\t\twhile(p-->0)\n\t\t{\n\t\t\t*out++= (((in[0]<<2)|(in[0]>>6))<< 0) |\n\t\t\t\t\t(((in[1]<<2)|(in[1]>>6))<<10) |\n\t\t\t\t\t(((in[2]<<2)|(in[2]>>6))<<20) |\n\t\t\t\t\t((in[3]>>6)<<30);\n\t\t\tin+=4;\n\t\t}\n\t}\n}\nstatic void Image_Tr_10To8(struct pendingtextureinfo *mips, int dummy)\n{\n\tint mip;\n\tfor (mip = 0; mip < mips->mipcount; mip++)\n\t{\n\t\tunsigned int *in = mips->mip[mip].data, v;\n\t\tqbyte *out = mips->mip[mip].data;\n\t\tunsigned int p = mips->mip[mip].width*mips->mip[mip].height*mips->mip[mip].depth;\n\t\tif (!mips->mip[mip].needfree && !mips->extrafree)\n\t\t{\n\t\t\tmips->mip[mip].needfree = true;\n\t\t\tmips->mip[mip].data = out = BZ_Malloc(sizeof(*out)*p*4);\n\t\t}\n\t\tmips->mip[mip].datasize = p*sizeof(*out)*4;\n\n\t\twhile(p-->0)\n\t\t{\n\t\t\tv = *in++;\n\n\t\t\t*out++ = (v>>2)&0xff;\n\t\t\t*out++ = (v>>12)&0xff;\n\t\t\t*out++ = (v>>22)&0xff;\n\t\t\t*out++ = ((v>>30)&0x3)*0x55;\n\t\t}\n\t}\n}\n\nstatic void Image_Tr_Swap8888(struct pendingtextureinfo *mips, int dummy)\n{\n\tint mip;\n\tfor (mip = 0; mip < mips->mipcount; mip++)\n\t{\n\t\tqbyte rb, g, br, a;\n\t\tqbyte *in = mips->mip[mip].data;\n\t\tqbyte *out = mips->mip[mip].data;\n\t\tunsigned int p = mips->mip[mip].width*mips->mip[mip].height*mips->mip[mip].depth;\n\t\tif (!mips->mip[mip].needfree && !mips->extrafree)\n\t\t{\n\t\t\tmips->mip[mip].needfree = true;\n\t\t\tmips->mip[mip].data = out = BZ_Malloc(sizeof(*out)*p*4);\n\t\t}\n\t\tmips->mip[mip].datasize = p*sizeof(*out)*4;\n\n\t\twhile(p-->0)\n\t\t{\n\t\t\ta = in[3];\n\t\t\trb = in[2];\n\t\t\tg = in[1];\n\t\t\tbr = in[0];\n\t\t\tin+=4;\n\n\t\t\t*out++ = rb;\n\t\t\t*out++ = g;\n\t\t\t*out++ = br;\n\t\t\t*out++ = a;\n\t\t}\n\t}\n}\n\ntypedef union\n{\n\tbyte_vec4_t v;\n\tunsigned int u;\n} pixel32_t;\ntypedef union\n{\n\tunsigned short v[4];\n\tquint64_t u;\n} pixel64_t;\n#define etc_expandv(p,x,y,z) p.v[0]|=p.v[0]>>(x),p.v[1]|=p.v[1]>>(y),p.v[2]|=p.v[2]>>(z)\n#ifdef DECOMPRESS_ETC2\n//FIXME: this is littleendian only...\nstatic void Image_Decode_ETC2_Block_TH_Internal(qbyte *fte_restrict in, pixel32_t *fte_restrict out, int w, pixel32_t base1, pixel32_t base2, int d, qboolean tmode)\n{\n\tpixel32_t painttable[4];\n\tint dtab[] = {3,6,11,16,23,32,41,64};\t//writing that felt like giving out lottery numbers.\n#define etc_addclamptopixel(r,p,d) r.v[0]=bound(0,p.v[0]+d, 255),r.v[1]=bound(0,p.v[1]+d, 255),r.v[2]=bound(0,p.v[2]+d, 255),r.v[3]=0xff\n\td = dtab[d];\n\tif (tmode)\n\t{\n\t\tpainttable[0].u = base1.u;\n\t\tetc_addclamptopixel(painttable[1], base2, d);\n\t\tpainttable[2].u = base2.u;\n\t\tetc_addclamptopixel(painttable[3], base2, -d);\n\t}\n\telse\n\t{\n\t\tetc_addclamptopixel(painttable[0], base1, d);\n\t\tetc_addclamptopixel(painttable[1], base1, -d);\n\t\tetc_addclamptopixel(painttable[2], base2, d);\n\t\tetc_addclamptopixel(painttable[3], base2, -d);\n\t}\n#undef etc_addclamptoint\n\t//yay, we have our painttable. now use it. also, screw that msb/lsb split.\n\n#define etc2_th_pix(r,i)\t\t\t\t\t\t\t\t\\\n\tif (in[5-(i/8)]&(1<<(i&7))) {\t\t\t\t\t\t\\\n\t\tif (in[7-(i/8)]&(1<<(i&7)))\tr.u = painttable[3].u;\t\\\n\t\telse\t\t\t\t\t\tr.u = painttable[2].u;\t\\\n\t} else {\t\t\t\t\t\t\t\t\t\t\t\\\n\t\tif (in[7-(i/8)]&(1<<(i&7)))\tr.u = painttable[1].u;\t\\\n\t\telse\t\t\t\t\t\tr.u = painttable[0].u;\t\\\n\t}\n\n\tetc2_th_pix(out[0], 0);\n\tetc2_th_pix(out[1], 4);\n\tetc2_th_pix(out[2], 8);\n\tetc2_th_pix(out[3], 12);\n\tout += w;\n\tetc2_th_pix(out[0], 1);\n\tetc2_th_pix(out[1], 5);\n\tetc2_th_pix(out[2], 9);\n\tetc2_th_pix(out[3], 13);\n\tout += w;\n\tetc2_th_pix(out[0], 2);\n\tetc2_th_pix(out[1], 6);\n\tetc2_th_pix(out[2], 10);\n\tetc2_th_pix(out[3], 14);\n\tout += w;\n\tetc2_th_pix(out[0], 3);\n\tetc2_th_pix(out[1], 7);\n\tetc2_th_pix(out[2], 11);\n\tetc2_th_pix(out[3], 15);\n#undef etc2_th_pix\n}\nstatic void Image_Decode_ETC2_Block_Internal(qbyte *fte_restrict in, pixel32_t *fte_restrict out0, int w, int alphamode)\n{\n\t//the overflow modes are only valid with ETC2.\n\t//alphamode=1 is used for punchthrough-alpha (which also forces the diff mode)\n\tstatic const char tab[8][2] =\n\t{\n\t\t{2,8},\n\t\t{5,17},\n\t\t{9,29},\n\t\t{13,42},\n\t\t{18,60},\n\t\t{24,80},\n\t\t{33,106},\n\t\t{47,183}\n\t};\n\tint tv;\n\tpixel32_t base1, base2, base3;\n\tconst char *cw1, *cw2;\n\tunsigned char R1,G1,B1;\n\tpixel32_t *out1, *out2, *out3;\n\n\tqboolean opaque;\n\n\tif (alphamode)\n\t\topaque = in[3]&2;\n\telse\n\t\topaque = 1;\n\n\tif (alphamode || (in[3]&2))\t//diffbit, bit 33\n\t{\n\t\tR1=(in[0]>>3)&31;//59+5\n\t\tG1=(in[1]>>3)&31;//51+5\n\t\tB1=(in[2]>>3)&31;//43+5\n\t\tVectorSet(base1.v, (R1<<3)+(R1>>2), (G1<<3)+(G1>>2), (B1<<3)+(B1>>2));\n\t\tR1 += (char)((in[0]&3)|((in[0]&4)*0x3f));\t//56+3\n\t\tif (R1&~0x1f) //R2 overflow = T mode\n\t\t{\n\t\t\tVector4Set(base1.v, ((in[0]&0x18)<<3) | ((in[0]&0x3)<<4), (in[1]&0xf0), ((in[1]&0x0f)<<4), 0xff);\n\t\t\tVector4Set(base2.v, (in[2]&0xf0), ((in[2]&0x0f)<<4), (in[3]&0xf0), 0xff);\n\t\t\ttv = ((in[3]&0x0c)>>1)|(in[3]&0x01);\n\t\t\tetc_expandv(base1,4,4,4);\n\t\t\tetc_expandv(base2,4,4,4);\n\t\t\tImage_Decode_ETC2_Block_TH_Internal(in, out0, w, base1, base2, tv, true);\n\t\t\treturn;\n\t\t}\n\t\tG1 += (char)((in[1]&3)|((in[1]&4)*0x3f));\t//48+3\n\t\tif (G1&~0x1f) //G2 overflow = H mode\n\t\t{\n\t\t\tVector4Set(base1.v, ((in[0]&0x78)<<1), ((in[0]&0x07)<<5)|((in[1]&0x10)<<0), ((in[1]&0x08)<<4)|((in[1]&0x03)<<5)|((in[2]&0x80)>>3), 0xff);\n\t\t\tVector4Set(base2.v, ((in[2]&0x78)<<1), ((in[2]&0x07)<<5)|((in[3]&0x80)>>3), ((in[3]&0x78)<<1), 0xff);\n\t\t\ttv = ((in[3]&0x04)>>1)|(in[3]&0x01);\n\t\t\tetc_expandv(base1,4,4,4);\n\t\t\tetc_expandv(base2,4,4,4);\n\t\t\tImage_Decode_ETC2_Block_TH_Internal(in, out0, w, base1, base2, tv, false);\n\t\t\treturn;\n\t\t}\n\t\tB1 += (char)((in[2]&3)|((in[2]&4)*0x3f));\t//40+3\n\t\tif (B1&~0x1f) //B2 overflow = Planar mode\n\t\t{//origin horizontal, vertical delas, interpolated across the 16 pixels\n\t\t\tVectorSet(base1.v, ((in[0]&0x7f)<<1),((in[0]&0x01)<<7)|((in[1])&0x7e),(in[1]<<7)|((in[2]&0x18)<<2)|((in[2]&0x3)<<3)|((in[3]&0x80)>>5));\n\t\t\tVectorSet(base2.v, ((in[3]&0x7c)<<1)|((in[3]&0x01)<<2),(in[4]&0xfe),((in[4]&1)<<7)|((in[5]&0xf8)>>1));\n\t\t\tVectorSet(base3.v, ((in[5]&0x07)<<5)|((in[6]&0xe0)>>3),((in[6]&0x1f)<<3)|((in[7]&0xc0)>>5),(in[7]&0x3f)<<2);\n\t\t\tetc_expandv(base1,6,7,6);\n\t\t\tetc_expandv(base2,6,7,6);\n\t\t\tetc_expandv(base3,6,7,6);\n#define etc2_planar2(r,x,y)\t\t\t\t\\\n\tr[x].v[0] =\tbound(0,(4*base1.v[0] + x*((short)base2.v[0]-base1.v[0]) + y*((short)base3.v[0]-base1.v[0]) + 2)>>2,0xff),\t\\\n\tr[x].v[1] = bound(0,(4*base1.v[1] + x*((short)base2.v[1]-base1.v[1]) + y*((short)base3.v[1]-base1.v[1]) + 2)>>2,0xff),\t\\\n\tr[x].v[2] = bound(0,(4*base1.v[2] + x*((short)base2.v[2]-base1.v[2]) + y*((short)base3.v[2]-base1.v[2]) + 2)>>2,0xff),\t\\\n\tr[x].v[3] = 0xff\n#define etc2_planar(r,y)\t\t\t\t\\\n\t\t\t\t\tetc2_planar2(r,0,y);\t\t\\\n\t\t\t\t\tetc2_planar2(r,1,y);\t\t\\\n\t\t\t\t\tetc2_planar2(r,2,y);\t\t\\\n\t\t\t\t\tetc2_planar2(r,3,y);\n\t\t\tetc2_planar(out0,0);out0 += w;\n\t\t\tetc2_planar(out0,1);out0 += w;\n\t\t\tetc2_planar(out0,2);out0 += w;\n\t\t\tetc2_planar(out0,3);\n\t\t\treturn;\n\t\t}\n\t\t//they should still be 5 bits.\n\t\tVectorSet(base2.v, (R1<<3)+(R1>>2), (G1<<3)+(G1>>2), (B1<<3)+(B1>>2));\n\t}\n\telse\n\t{\n\t\tVectorSet(base1.v, ((in[0]>>4)&15)*0x11,\t/*60+4*/\n\t\t\t\t\t\t ((in[1]>>4)&15)*0x11,\t/*52+4*/\n\t\t\t\t\t\t ((in[2]>>4)&15)*0x11);\t/*44+4*/\n\t\tVectorSet(base2.v, ((in[0]>>0)&15)*0x11,\t/*56+4*/\n\t\t\t\t\t\t ((in[1]>>0)&15)*0x11,\t/*48+4*/\n\t\t\t\t\t\t ((in[2]>>0)&15)*0x11);\t/*40+4*/\n\t}\n\n\tcw1 = tab[(in[3]>>5)&7];\t//37+3\n\tcw2 = tab[(in[3]>>2)&7];\t//34+3\n\n\tout1 = out0+w*1;\n\tout2 = out0+w*2;\n\tout3 = out0+w*3;\n\n#define etc1_pix(r, base,cw,i)\t\\\n\tif (in[7-(i/8)]&(1<<(i&7)))\t\t\t\t\t\t\\\n\t\ttv = (in[5-(i/8)]&(1<<(i&7)))?-cw[1]:cw[1];\t\\\n\telse if (opaque)\t\t\t\t\t\t\t\t\\\n\t\ttv = (in[5-(i/8)]&(1<<(i&7)))?-cw[0]:cw[0];\t\\\n\telse /*punchthrough alpha mode*/\t\t\t\t\\\n\t\ttv = (in[5-(i/8)]&(1<<(i&7)))?-255:0;\t\t\\\n\tif (tv==-255)\t\t\t\t\t\t\t\t\t\\\n\t\tr.u = 0;\t\t\t\t\t\t\t\t\t\\\n\telse\t\t\t\t\t\t\t\t\t\t\t\\\n\t\tr.v[0] = bound(0,base.v[0]+tv,0xff),\t\t\t\\\n\t\tr.v[1] = bound(0,base.v[1]+tv,0xff),\t\t\t\\\n\t\tr.v[2] = bound(0,base.v[2]+tv,0xff),\t\t\t\\\n\t\tr.v[3] = 0xff\n\n\tetc1_pix(out0[0], base1,cw1,0);\n\tetc1_pix(out0[1], base1,cw1,4);\n\tetc1_pix(out1[0], base1,cw1,1);\n\tetc1_pix(out1[1], base1,cw1,5);\n\n\tetc1_pix(out2[2], base2,cw2,10);\n\tetc1_pix(out2[3], base2,cw2,14);\n\tetc1_pix(out3[2], base2,cw2,11);\n\tetc1_pix(out3[3], base2,cw2,15);\n\tif (in[3]&1)\t//flipbit bit 32 - blocks are vertical\n\t{\n\t\tetc1_pix(out0[2], base1,cw1,8);\n\t\tetc1_pix(out0[3], base1,cw1,12);\n\t\tetc1_pix(out1[2], base1,cw1,9);\n\t\tetc1_pix(out1[3], base1,cw1,13);\n\n\t\tetc1_pix(out2[0], base2,cw2,2);\n\t\tetc1_pix(out2[1], base2,cw2,6);\n\t\tetc1_pix(out3[0], base2,cw2,3);\n\t\tetc1_pix(out3[1], base2,cw2,7);\n\t}\n\telse\n\t{\n\t\tetc1_pix(out0[2], base2,cw2,8);\n\t\tetc1_pix(out0[3], base2,cw2,12);\n\t\tetc1_pix(out1[2], base2,cw2,9);\n\t\tetc1_pix(out1[3], base2,cw2,13);\n\n\t\tetc1_pix(out2[0], base1,cw1,2);\n\t\tetc1_pix(out2[1], base1,cw1,6);\n\t\tetc1_pix(out3[0], base1,cw1,3);\n\t\tetc1_pix(out3[1], base1,cw1,7);\n\t}\n#undef etc1_pix\n}\nstatic void Image_Decode_EAC8U_Block_Internal(qbyte *fte_restrict in, qbyte *fte_restrict out, int stride, qboolean goestoeleven)\n{\n\tstatic const char tabs[16][8] =\n\t{\n\t\t{-3,-6, -9,-15,2,5,8,14},\n\t\t{-3,-7,-10,-13,2,6,9,12},\n\t\t{-2,-5, -8,-13,1,4,7,12},\n\t\t{-2,-4, -6,-13,1,3,5,12},\n\t\t{-3,-6, -8,-12,2,5,7,11},\n\t\t{-3,-7, -9,-11,2,6,8,10},\n\t\t{-4,-7, -8,-11,3,6,7,10},\n\t\t{-3,-5, -8,-11,2,4,7,10},\n\t\t{-2,-6, -8,-10,1,5,7,9},\n\t\t{-3,-5, -8,-10,1,4,7,9},\n\t\t{-2,-4, -8,-10,1,3,7,9},\n\t\t{-2,-5, -7,-10,1,4,6,9},\n\t\t{-3,-4, -7,-10,2,3,6,9},\n\t\t{-1,-2, -3,-10,0,1,2,9},\n\t\t{-4,-6, -8, -9,3,5,7,8},\n\t\t{-3,-5, -7, -9,2,4,6,8},\n\t};\n\n\tconst qbyte base = in[0];\n\tconst qbyte mul = in[1]>>4;\n\tconst char *tab = tabs[in[1]&0xf];\n\tconst quint64_t bits = in[2] | (in[3]<<8) | (in[4]<<16) | (in[5]<<24) | ((quint64_t)in[6]<<32) | ((quint64_t)in[7]<<40);\n\n#define EAC_Pix(r,x,y)\tr = bound(0, base + tab[(bits>>((x*4+y)*3))&7] * mul, 255);\n#define EAC_Row(y)\tEAC_Pix(out[0], 0,y);EAC_Pix(out[1], 1,y);EAC_Pix(out[2], 2,y);EAC_Pix(out[3], 3,y);\n\tEAC_Row(0);out += stride;EAC_Row(1);out += stride;EAC_Row(2);out += stride;EAC_Row(3);\n#undef EAC_Row\n#undef EAC_Pix\n}\nstatic void Image_Decode_ETC2_RGB8_Block(qbyte *fte_restrict in, pixel32_t *fte_restrict out, int w, uploadfmt_t fmt)\n{\n\tImage_Decode_ETC2_Block_Internal(in, out, w, false);\n}\n//punchthrough alpha works by removing interleaved mode releasing a bit that says whether a block can have alpha=0, .\nstatic void Image_Decode_ETC2_RGB8A1_Block(qbyte *fte_restrict in, pixel32_t *fte_restrict out, int w, uploadfmt_t fmt)\n{\n\tImage_Decode_ETC2_Block_Internal(in, out, w, true);\n}\n//ETC2 RGBA's alpha and R11(and RG11) work the same way as each other, but with varying extra blocks with either 8 or 11 bits of valid precision.\nstatic void Image_Decode_ETC2_RGB8A8_Block(qbyte *fte_restrict in, pixel32_t *fte_restrict out, int w, uploadfmt_t fmt)\n{\n\tImage_Decode_ETC2_Block_Internal(in+8, out, w, false);\n\tImage_Decode_EAC8U_Block_Internal(in, out->v+3, w*4, false);\n}\nstatic void Image_Decode_EAC_R11U_Block(qbyte *fte_restrict in, pixel32_t *fte_restrict out, int w, uploadfmt_t fmt)\n{\n\tpixel32_t r;\n\tint i = 0;\n\tVector4Set(r.v, 0, 0, 0, 0xff);\n\tfor (i = 0; i < 4; i++)\n\t\tout[w*0+i] = out[w*1+i] = out[w*2+i] = out[w*3+i] = r;\n\tImage_Decode_EAC8U_Block_Internal(in, out->v, w*4, false);\n}\nstatic void Image_Decode_EAC_RG11U_Block(qbyte *fte_restrict in, pixel32_t *fte_restrict out, int w, uploadfmt_t fmt)\n{\n\tpixel32_t r;\n\tint i = 0;\n\tVector4Set(r.v, 0, 0, 0, 0xff);\n\tfor (i = 0; i < 4; i++)\n\t\tout[w*0+i] = out[w*1+i] = out[w*2+i] = out[w*3+i] = r;\n\tImage_Decode_EAC8U_Block_Internal(in, out->v+0, w*4, false);\n\tImage_Decode_EAC8U_Block_Internal(in+8, out->v+1, w*4, false);\n}\n#endif\n\n#ifdef DECOMPRESS_S3TC\nstatic void Image_Decode_S3TC_Block_Internal(qbyte *fte_restrict in, pixel32_t *fte_restrict out, int w, qbyte blackalpha)\n{\n\tpixel32_t tab[4];\n\tunsigned int bits;\n\n\tVector4Set(tab[0].v, (in[1]&0xf8), ((in[0]&0xe0)>>3)|((in[1]&7)<<5), (in[0]&0x1f)<<3, 0xff);\n\tetc_expandv(tab[0],5,6,5);\n\tVector4Set(tab[1].v, (in[3]&0xf8), ((in[2]&0xe0)>>3)|((in[3]&7)<<5), (in[2]&0x1f)<<3, 0xff);\n\tetc_expandv(tab[1],5,6,5);\n\n#define BC1_Lerp(a,as,b,bs,div,c)\t\t((c)[0]=((a)[0]*(as)+(b)[0]*(bs))/(div),(c)[1]=((a)[1]*(as)+(b)[1]*(bs))/(div), (c)[2]=((a)[2]*(as)+(b)[2]*(bs))/(div), (c)[3] = 0xff)\n\tif ((in[0]|(in[1]<<8)) > (in[2]|(in[3]<<8)))\n\t{\n\t\tBC1_Lerp(tab[0].v,2, tab[1].v,1, 3,tab[2].v);\n\t\tBC1_Lerp(tab[0].v,1, tab[1].v,2, 3,tab[3].v);\n\t}\n\telse\n\t{\n\t\tBC1_Lerp(tab[0].v,1, tab[1].v,1, 2,tab[2].v);\n\t\tVector4Set(tab[3].v, 0, 0, 0, blackalpha);\n\t}\n\n\tbits = in[4] | (in[5]<<8) | (in[6]<<16) | (in[7]<<24);\n\n#define BC1_Pix(r,i)\tr.u = tab[(bits>>((i)*2))&3].u\n#define BC1_Row(r,i)\tBC1_Pix(r[0],i+0);BC1_Pix(r[1],i+1);BC1_Pix(r[2],i+2);BC1_Pix(r[3],i+3)\n\n\tBC1_Row(out, 0);\n\tout += w;\n\tBC1_Row(out, 4);\n\tout += w;\n\tBC1_Row(out, 8);\n\tout += w;\n\tBC1_Row(out, 12);\n}\nstatic void Image_Decode_BC1_Block(qbyte *fte_restrict in, pixel32_t *fte_restrict out, int w, uploadfmt_t fmt)\n{\n\tImage_Decode_S3TC_Block_Internal(in, out, w, 0xff);\n}\nstatic void Image_Decode_BC1A_Block(qbyte *fte_restrict in, pixel32_t *fte_restrict out, int w, uploadfmt_t fmt)\n{\n\tImage_Decode_S3TC_Block_Internal(in, out, w, 0);\n}\nstatic void Image_Decode_BC2_Block(qbyte *fte_restrict in, pixel32_t *fte_restrict out, int w, uploadfmt_t fmt)\n{\n\tImage_Decode_S3TC_Block_Internal(in+8, out, w, 0xff);\n\n\t//BC2 has straight 4-bit alpha.\n#define BC2_AlphaRow()\t\\\n\tout[0].v[3] = in[0]&0x0f; out[0].v[3] |= out[0].v[3]<<4;\t\\\n\tout[1].v[3] = in[0]&0xf0; out[1].v[3] |= out[1].v[3]>>4;\t\\\n\tout[2].v[3] = in[1]&0x0f; out[2].v[3] |= out[2].v[3]<<4;\t\\\n\tout[3].v[3] = in[1]&0xf0; out[3].v[3] |= out[3].v[3]>>4\n\n\tBC2_AlphaRow();\n\tin += 2;out += w;\n\tBC2_AlphaRow();\n\tin += 2;out += w;\n\tBC2_AlphaRow();\n\tin += 2;out += w;\n\tBC2_AlphaRow();\n#undef BC2_AlphaRow\n}\n#endif\n#ifdef DECOMPRESS_RGTC\nstatic void Image_Decode_RGTC_Block_Internal(qbyte *fte_restrict in, qbyte *fte_restrict out, int stride, qboolean issigned)\n{\n\tquint64_t bits;\n\tunion\n\t{\n\t\tqbyte u;\n\t\tchar s;\n\t} tab[8];\n\ttab[0].u = in[0];\n\ttab[1].u = in[1];\n\tif (issigned)\n\t{\n\t\tif (tab[0].s > tab[1].s)\n\t\t{\n\t\t\ttab[2].s = (tab[0].s*6 + tab[1].s*1)/7;\n\t\t\ttab[3].s = (tab[0].s*5 + tab[1].s*2)/7;\n\t\t\ttab[4].s = (tab[0].s*4 + tab[1].s*3)/7;\n\t\t\ttab[5].s = (tab[0].s*3 + tab[1].s*4)/7;\n\t\t\ttab[6].s = (tab[0].s*2 + tab[1].s*5)/7;\n\t\t\ttab[7].s = (tab[0].s*1 + tab[1].s*6)/7;\n\t\t}\n\t\telse\n\t\t{\n\t\t\ttab[2].s = (tab[0].s*4 + tab[1].s*1)/5;\n\t\t\ttab[3].s = (tab[0].s*3 + tab[1].s*2)/5;\n\t\t\ttab[4].s = (tab[0].s*2 + tab[1].s*3)/5;\n\t\t\ttab[5].s = (tab[0].s*1 + tab[1].s*4)/5;\n\t\t\ttab[6].s = -128;\n\t\t\ttab[7].s = 127;\n\t\t}\n\t}\n\telse\n\t{\n\t\tif (tab[0].u > tab[1].u)\n\t\t{\n\t\t\ttab[2].u = (tab[0].u*6 + tab[1].u*1)/7;\n\t\t\ttab[3].u = (tab[0].u*5 + tab[1].u*2)/7;\n\t\t\ttab[4].u = (tab[0].u*4 + tab[1].u*3)/7;\n\t\t\ttab[5].u = (tab[0].u*3 + tab[1].u*4)/7;\n\t\t\ttab[6].u = (tab[0].u*2 + tab[1].u*5)/7;\n\t\t\ttab[7].u = (tab[0].u*1 + tab[1].u*6)/7;\n\t\t}\n\t\telse\n\t\t{\n\t\t\ttab[2].u = (tab[0].u*4 + tab[1].u*1)/5;\n\t\t\ttab[3].u = (tab[0].u*3 + tab[1].u*2)/5;\n\t\t\ttab[4].u = (tab[0].u*2 + tab[1].u*3)/5;\n\t\t\ttab[5].u = (tab[0].u*1 + tab[1].u*4)/5;\n\t\t\ttab[6].u = 0;\n\t\t\ttab[7].u = 0xff;\n\t\t}\n\t}\n\n\tbits = in[2] | (in[3]<<8) | (in[4]<<16) | (in[5]<<24) | ((quint64_t)in[6]<<32) | ((quint64_t)in[7]<<40);\n\n#define BC3AU_Pix(r,i)\tr = tab[(bits>>((i)*3))&7].u;\n#define BC3AU_Row(i)\tBC3AU_Pix(out[0], i+0);BC3AU_Pix(out[4], i+1);BC3AU_Pix(out[8], i+2);BC3AU_Pix(out[12], i+3);\n\tBC3AU_Row(0);out += stride;BC3AU_Row(4);out += stride;BC3AU_Row(8);out += stride;BC3AU_Row(12);\n#undef BC3AU_Pix\n}\n#ifdef DECOMPRESS_S3TC\n//s3tc rgb channel, with an rgtc alpha channel that depends upon both encodings (really the origin of rgtc, but mneh).\nstatic void Image_Decode_BC3_Block(qbyte *fte_restrict in, pixel32_t *fte_restrict out, int w, uploadfmt_t fmt)\n{\n\tImage_Decode_S3TC_Block_Internal(in+8, out, w, 0xff);\n\tImage_Decode_RGTC_Block_Internal(in, out->v+3, w*4, false);\n}\n#endif\nstatic void Image_Decode_BC4_Block(qbyte *fte_restrict in, pixel32_t *fte_restrict out, int w, uploadfmt_t fmt)\n{\t//BC4: BC3's alpha channel but used as red only.\n\tpixel32_t r;\n\tint i = 0;\n\tVector4Set(r.v, 0, 0, 0, 0xff);\n\tfor (i = 0; i < 4; i++)\n\t\tout[w*0+i] = out[w*1+i] = out[w*2+i] = out[w*3+i] = r;\n\tImage_Decode_RGTC_Block_Internal(in, out->v+0, w*4, fmt==PTI_BC4_R_SNORM);\n}\nstatic void Image_Decode_BC5_Block(qbyte *fte_restrict in, pixel32_t *fte_restrict out, int w, uploadfmt_t fmt)\n{\t//BC5: two of BC3's alpha channels but used as red+green only.\n\tpixel32_t r;\n\tint i = 0;\n\tVector4Set(r.v, 0, 0, 0, 0xff);\n\tfor (i = 0; i < 4; i++)\n\t\tout[w*0+i] = out[w*1+i] = out[w*2+i] = out[w*3+i] = r;\n\tImage_Decode_RGTC_Block_Internal(in+0, out->v+0, w*4, fmt==PTI_BC5_RG_SNORM);\n\tImage_Decode_RGTC_Block_Internal(in+8, out->v+1, w*4, fmt==PTI_BC5_RG_SNORM);\n}\n#endif\n\n#ifdef DECOMPRESS_BPTC\nfte_inlinestatic int ReadBits(qbyte *in, int *bit, int n)\n{\n\tint mask = (1<<n)-1;\n\tint second;\n\tint first = *bit;\n\t*bit += n;\n\tin += first>>3;\n\tfirst &= 7;\n\tsecond = max(0,n - (8-first));\n\treturn ((in[0]>>first)&mask) ^ (in[1]<<(n-second)&mask);\n}\nfte_inlinestatic int ReadBitsL(qbyte *in, int *bit, int n)\n{\n\tint r = ReadBits(in, bit, 8);\n\treturn (r)|(ReadBits(in, bit, n-8)<<8);\n}\nstatic void Image_Decode_BC6_Block(qbyte *fte_restrict in, pixel64_t *fte_restrict out, int w, uploadfmt_t fmt)\n{\n\tqboolean signextend = fmt==PTI_BC6_RGB_SFLOAT;\n\tstatic const int anchors[32] =\n\t{\n\t\t15,15,15,15,\n\t\t15,15,15,15,\n\t\t15,15,15,15,\n\t\t15,15,15,15,\n\t\t15, 2, 8, 2,\n\t\t2, 8, 8,15,\n\t\t2, 8, 2, 2,\n\t\t8, 8, 2, 2,\n\t};\n\tstatic const qbyte p2[] = {\n\t\t0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,\n\t\t0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,\n\t\t0,1,1,1,0,1,1,1,0,1,1,1,0,1,1,1,\n\t\t0,0,0,1,0,0,1,1,0,0,1,1,0,1,1,1,\n\t\t0,0,0,0,0,0,0,1,0,0,0,1,0,0,1,1,\n\t\t0,0,1,1,0,1,1,1,0,1,1,1,1,1,1,1,\n\t\t0,0,0,1,0,0,1,1,0,1,1,1,1,1,1,1,\n\t\t0,0,0,0,0,0,0,1,0,0,1,1,0,1,1,1,\n\t\t0,0,0,0,0,0,0,0,0,0,0,1,0,0,1,1,\n\t\t0,0,1,1,0,1,1,1,1,1,1,1,1,1,1,1,\n\t\t0,0,0,0,0,0,0,1,0,1,1,1,1,1,1,1,\n\t\t0,0,0,0,0,0,0,0,0,0,0,1,0,1,1,1,\n\t\t0,0,0,1,0,1,1,1,1,1,1,1,1,1,1,1,\n\t\t0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,\n\t\t0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,\n\t\t0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,\n\t\t0,0,0,0,1,0,0,0,1,1,1,0,1,1,1,1,\n\t\t0,1,1,1,0,0,0,1,0,0,0,0,0,0,0,0,\n\t\t0,0,0,0,0,0,0,0,1,0,0,0,1,1,1,0,\n\t\t0,1,1,1,0,0,1,1,0,0,0,1,0,0,0,0,\n\t\t0,0,1,1,0,0,0,1,0,0,0,0,0,0,0,0,\n\t\t0,0,0,0,1,0,0,0,1,1,0,0,1,1,1,0,\n\t\t0,0,0,0,0,0,0,0,1,0,0,0,1,1,0,0,\n\t\t0,1,1,1,0,0,1,1,0,0,1,1,0,0,0,1,\n\t\t0,0,1,1,0,0,0,1,0,0,0,1,0,0,0,0,\n\t\t0,0,0,0,1,0,0,0,1,0,0,0,1,1,0,0,\n\t\t0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,\n\t\t0,0,1,1,0,1,1,0,0,1,1,0,1,1,0,0,\n\t\t0,0,0,1,0,1,1,1,1,1,1,0,1,0,0,0,\n\t\t0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0,\n\t\t0,1,1,1,0,0,0,1,1,0,0,0,1,1,1,0,\n\t\t0,0,1,1,1,0,0,1,1,0,0,1,1,1,0,0,\n\t\t0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,\n\t\t0,0,0,0,1,1,1,1,0,0,0,0,1,1,1,1,\n\t\t0,1,0,1,1,0,1,0,0,1,0,1,1,0,1,0,\n\t\t0,0,1,1,0,0,1,1,1,1,0,0,1,1,0,0,\n\t\t0,0,1,1,1,1,0,0,0,0,1,1,1,1,0,0,\n\t\t0,1,0,1,0,1,0,1,1,0,1,0,1,0,1,0,\n\t\t0,1,1,0,1,0,0,1,0,1,1,0,1,0,0,1,\n\t\t0,1,0,1,1,0,1,0,1,0,1,0,0,1,0,1,\n\t\t0,1,1,1,0,0,1,1,1,1,0,0,1,1,1,0,\n\t\t0,0,0,1,0,0,1,1,1,1,0,0,1,0,0,0,\n\t\t0,0,1,1,0,0,1,0,0,1,0,0,1,1,0,0,\n\t\t0,0,1,1,1,0,1,1,1,1,0,1,1,1,0,0,\n\t\t0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,\n\t\t0,0,1,1,1,1,0,0,1,1,0,0,0,0,1,1,\n\t\t0,1,1,0,0,1,1,0,1,0,0,1,1,0,0,1,\n\t\t0,0,0,0,0,1,1,0,0,1,1,0,0,0,0,0,\n\t\t0,1,0,0,1,1,1,0,0,1,0,0,0,0,0,0,\n\t\t0,0,1,0,0,1,1,1,0,0,1,0,0,0,0,0,\n\t\t0,0,0,0,0,0,1,0,0,1,1,1,0,0,1,0,\n\t\t0,0,0,0,0,1,0,0,1,1,1,0,0,1,0,0,\n\t\t0,1,1,0,1,1,0,0,1,0,0,1,0,0,1,1,\n\t\t0,0,1,1,0,1,1,0,1,1,0,0,1,0,0,1,\n\t\t0,1,1,0,0,0,1,1,1,0,0,1,1,1,0,0,\n\t\t0,0,1,1,1,0,0,1,1,1,0,0,0,1,1,0,\n\t\t0,1,1,0,1,1,0,0,1,1,0,0,1,0,0,1,\n\t\t0,1,1,0,0,0,1,1,0,0,1,1,1,0,0,1,\n\t\t0,1,1,1,1,1,1,0,1,0,0,0,0,0,0,1,\n\t\t0,0,0,1,1,0,0,0,1,1,1,0,0,1,1,1,\n\t\t0,0,0,0,1,1,1,1,0,0,1,1,0,0,1,1,\n\t\t0,0,1,1,0,0,1,1,1,1,1,1,0,0,0,0,\n\t\t0,0,1,0,0,0,1,0,1,1,1,0,1,1,1,0,\n\t\t0,1,0,0,0,1,0,0,0,1,1,1,0,1,1,1,\n    };\n    static const int w3[] = {0, 9, 18, 27, 37, 46, 55, 64};\n    static const int w4[] = {0, 4, 9, 13, 17, 21, 26, 30, 34, 38, 43, 47, 51, 55, 60, 64};\n    static const int *wsz[] = {w3,w3,w3, w3,w4};\n    const int *weight;\n\n\tunsigned short rgb[4][3] = {{0}};\n\tunsigned short palette[16][3];\n\tint i, j, cb, ib;\n\tint bit = 0, cbits[4];\n\tint ss;\n\tint shapeindex;\n\tint tr;\n\tint mode = ReadBits(in, &bit, 2);\n\tif (mode >= 2)\n\t\tmode |=ReadBits(in, &bit, 3)<<2;\n\n\t//n[a:b]\n\t//n is the output name (number=group, r/g/b=0/1/2 channel)\n\t//a:b is the inclusive bit range, typically little-endian input\n\t//so bit counts are a+1-b, shifted up by b.\n\t//if b is ommitted then its equivelent to a (read as a:a, or in other words a single bit shifted up by b)\n\t//if its backwards then the bits are big-endian for some reason and need to be switched (rare, handled here by reading individual bits)\n\t//bit counts larger than 9 may need to read from more than two bytes, which requires special handling, hence the alternative function.\n\t//it is hoped that the compiler will be able to inline these into trivial mask+shifts for less code than it would take to call ReadBits, at least when its from a single byte.\n\tswitch(mode)\n\t{\n\tcase 0:\t//1\n\t\tVector4Set(cbits, 5,5,5, 10);\n\t\ttr = 1;\n\t\tss = 2;\n\t\trgb[2][1] |= ReadBits(in, &bit, 1)<<4;\t//g2[4],\n\t\trgb[2][2] |= ReadBits(in, &bit, 1)<<4;\t//b2[4],\n\t\trgb[3][2] |= ReadBits(in, &bit, 1)<<4;\t//b3[4],\n\t\trgb[0][0] |= ReadBitsL(in, &bit,10)<<0;\t//r0[9:0],\n\t\trgb[0][1] |= ReadBitsL(in, &bit,10)<<0;\t//g0[9:0],\n\t\trgb[0][2] |= ReadBitsL(in, &bit,10)<<0;\t//b0[9:0],\n\t\trgb[1][0] |= ReadBits(in, &bit, 5)<<0;\t//r1[4:0],\n\t\trgb[3][1] |= ReadBits(in, &bit, 1)<<4;\t//g3[4],\n\t\trgb[2][1] |= ReadBits(in, &bit, 4)<<0;\t//g2[3:0],\n\t\trgb[1][1] |= ReadBits(in, &bit, 5)<<0;\t//g1[4:0],\n\t\trgb[3][2] |= ReadBits(in, &bit, 1)<<0;\t//b3[0],\n\t\trgb[3][1] |= ReadBits(in, &bit, 4)<<0;\t//g3[3:0],\n\t\trgb[1][2] |= ReadBits(in, &bit, 5)<<0;\t//b1[4:0],\n\t\trgb[3][2] |= ReadBits(in, &bit, 1)<<1;\t//b3[1],\n\t\trgb[2][2] |= ReadBits(in, &bit, 4)<<0;\t//b2[3:0],\n\t\trgb[2][0] |= ReadBits(in, &bit, 5)<<0;\t//r2[4:0],\n\t\trgb[3][2] |= ReadBits(in, &bit, 1)<<2;\t//b3[2],\n\t\trgb[3][0] |= ReadBits(in, &bit, 5)<<0;\t//r3[4:0],\n\t\trgb[3][2] |= ReadBits(in, &bit, 1)<<3;\t//b3[3]\n\t\tshapeindex = ReadBits(in, &bit, 5);\n\t\tbreak;\n\tcase 1:\t//2\n\t\tVector4Set(cbits, 6,6,6, 7);\n\t\ttr = 1;\n\t\tss = 2;\n\t\trgb[2][1] |= ReadBits(in, &bit, 1)<<5;\t//g2[5],\n\t\trgb[3][1] |= ReadBits(in, &bit, 1)<<4;\t//g3[4],\n\t\trgb[3][1] |= ReadBits(in, &bit, 1)<<5;\t//g3[5],\n\t\trgb[0][0] |= ReadBits(in, &bit, 7)<<0;\t//r0[6:0],\n\t\trgb[3][2] |= ReadBits(in, &bit, 1)<<0;\t//b3[0],\n\t\trgb[3][2] |= ReadBits(in, &bit, 1)<<1;\t//b3[1],\n\t\trgb[2][2] |= ReadBits(in, &bit, 1)<<4;\t//b2[4],\n\t\trgb[0][1] |= ReadBits(in, &bit, 7)<<0;\t//g0[6:0],\n\t\trgb[2][2] |= ReadBits(in, &bit, 1)<<5;\t//b2[5],\n\t\trgb[3][2] |= ReadBits(in, &bit, 1)<<2;\t//b3[2],\n\t\trgb[2][1] |= ReadBits(in, &bit, 1)<<4;\t//g2[4],\n\t\trgb[0][2] |= ReadBits(in, &bit, 7)<<0;\t//b0[6:0],\n\t\trgb[3][2] |= ReadBits(in, &bit, 1)<<3;\t//b3[3],\n\t\trgb[3][2] |= ReadBits(in, &bit, 1)<<5;\t//b3[5],\n\t\trgb[3][2] |= ReadBits(in, &bit, 1)<<4;\t//b3[4],\n\t\trgb[1][0] |= ReadBits(in, &bit, 6)<<0;\t//r1[5:0],\n\t\trgb[2][1] |= ReadBits(in, &bit, 4)<<0;\t//g2[3:0],\n\t\trgb[1][1] |= ReadBits(in, &bit, 6)<<0;\t//g1[5:0],\n\t\trgb[3][1] |= ReadBits(in, &bit, 4)<<0;\t//g3[3:0],\n\t\trgb[1][2] |= ReadBits(in, &bit, 6)<<0;\t//b1[5:0],\n\t\trgb[2][2] |= ReadBits(in, &bit, 4)<<0;\t//b2[3:0],\n\t\trgb[2][0] |= ReadBits(in, &bit, 6)<<0;\t//r2[5:0],\n\t\trgb[3][0] |= ReadBits(in, &bit, 6)<<0;\t//r3[5:0]\n\t\tshapeindex = ReadBits(in, &bit, 5);\n\t\tbreak;\n\tcase 2:\t//3\n\t\tVector4Set(cbits, 5,4,4, 11);\n\t\ttr = 1;\n\t\tss = 2;\n\t\trgb[0][0] |= ReadBitsL(in, &bit,10)<<0;\t//r0[9:0],\n\t\trgb[0][1] |= ReadBitsL(in, &bit,10)<<0;\t//g0[9:0],\n\t\trgb[0][2] |= ReadBitsL(in, &bit,10)<<0;\t//b0[9:0],\n\t\trgb[1][0] |= ReadBits(in, &bit, 5)<<0;\t//r1[4:0],\n\t\trgb[0][0] |= ReadBits(in, &bit, 1)<<10;\t//r0[10],\n\t\trgb[2][1] |= ReadBits(in, &bit, 4)<<0;\t//g2[3:0],\n\t\trgb[1][1] |= ReadBits(in, &bit, 4)<<0;\t//g1[3:0],\n\t\trgb[0][1] |= ReadBits(in, &bit, 1)<<10;\t//g0[10],\n\t\trgb[3][2] |= ReadBits(in, &bit, 1)<<0;\t//b3[0],\n\t\trgb[3][1] |= ReadBits(in, &bit, 4)<<0;\t//g3[3:0],\n\t\trgb[1][2] |= ReadBits(in, &bit, 4)<<0;\t//b1[3:0],\n\t\trgb[0][2] |= ReadBits(in, &bit, 1)<<10;\t//b0[10],\n\t\trgb[3][2] |= ReadBits(in, &bit, 1)<<1;\t//b3[1],\n\t\trgb[2][2] |= ReadBits(in, &bit, 4)<<0;\t//b2[3:0],\n\t\trgb[2][0] |= ReadBits(in, &bit, 5)<<0;\t//r2[4:0],\n\t\trgb[3][2] |= ReadBits(in, &bit, 1)<<2;\t//b3[2],\n\t\trgb[3][0] |= ReadBits(in, &bit, 5)<<0;\t//r3[4:0],\n\t\trgb[3][2] |= ReadBits(in, &bit, 1)<<3;\t//b3[3]\n\t\tshapeindex = ReadBits(in, &bit, 5);\n\t\tbreak;\n\tcase 6:\t//4\n\t\tVector4Set(cbits, 4,5,4, 11);\n\t\ttr = 1;\n\t\tss = 2;\n\t\trgb[0][0] |= ReadBitsL(in, &bit,10)<<0;\t//r0[9:0],\n\t\trgb[0][1] |= ReadBitsL(in, &bit,10)<<0;\t//g0[9:0],\n\t\trgb[0][2] |= ReadBitsL(in, &bit,10)<<0;\t//b0[9:0],\n\t\trgb[1][0] |= ReadBits(in, &bit, 4)<<0;\t//r1[3:0],\n\t\trgb[0][0] |= ReadBits(in, &bit, 1)<<10;\t//r0[10],\n\t\trgb[3][1] |= ReadBits(in, &bit, 1)<<4;\t//g3[4],\n\t\trgb[2][1] |= ReadBits(in, &bit, 4)<<0;\t//g2[3:0],\n\t\trgb[1][1] |= ReadBits(in, &bit, 5)<<0;\t//g1[4:0],\n\t\trgb[0][1] |= ReadBits(in, &bit, 1)<<10;\t//g0[10],\n\t\trgb[3][1] |= ReadBits(in, &bit, 4)<<0;\t//g3[3:0],\n\t\trgb[1][2] |= ReadBits(in, &bit, 4)<<0;\t//b1[3:0],\n\t\trgb[0][2] |= ReadBits(in, &bit, 1)<<10;\t//b0[10],\n\t\trgb[3][2] |= ReadBits(in, &bit, 1)<<1;\t//b3[1],\n\t\trgb[2][2] |= ReadBits(in, &bit, 4)<<0;\t//b2[3:0],\n\t\trgb[2][0] |= ReadBits(in, &bit, 4)<<0;\t//r2[3:0],\n\t\trgb[3][2] |= ReadBits(in, &bit, 1)<<0;\t//b3[0],\n\t\trgb[3][2] |= ReadBits(in, &bit, 1)<<2;\t//b3[2],\n\t\trgb[3][0] |= ReadBits(in, &bit, 4)<<0;\t//r3[3:0],\n\t\trgb[2][1] |= ReadBits(in, &bit, 1)<<4;\t//g2[4],\n\t\trgb[3][2] |= ReadBits(in, &bit, 1)<<3;\t//b3[3]\n\t\tshapeindex = ReadBits(in, &bit, 5);\n\t\tbreak;\n\tcase 10: //5\n\t\tVector4Set(cbits, 4,4,5, 11);\n\t\ttr = 1;\n\t\tss = 2;\n\t\trgb[0][0] |= ReadBitsL(in, &bit,10)<<0;\t//r0[9:0],\n\t\trgb[0][1] |= ReadBitsL(in, &bit,10)<<0;\t//g0[9:0],\n\t\trgb[0][2] |= ReadBitsL(in, &bit,10)<<0;\t//b0[9:0],\n\t\trgb[1][0] |= ReadBits(in, &bit, 4)<<0;\t//r1[3:0],\n\t\trgb[0][0] |= ReadBits(in, &bit, 1)<<10;\t//r0[10],\n\t\trgb[2][2] |= ReadBits(in, &bit, 1)<<4;\t//b2[4],\n\t\trgb[2][1] |= ReadBits(in, &bit, 4)<<0;\t//g2[3:0],\n\t\trgb[1][1] |= ReadBits(in, &bit, 4)<<0;\t//g1[3:0],\n\t\trgb[0][1] |= ReadBits(in, &bit, 1)<<10;\t//g0[10],\n\t\trgb[3][2] |= ReadBits(in, &bit, 1)<<0;\t//b3[0],\n\t\trgb[3][1] |= ReadBits(in, &bit, 4)<<0;\t//g3[3:0],\n\t\trgb[1][2] |= ReadBits(in, &bit, 5)<<0;\t//b1[4:0],\n\t\trgb[0][2] |= ReadBits(in, &bit, 1)<<10;\t//b0[10],\n\t\trgb[2][2] |= ReadBits(in, &bit, 4)<<0;\t//b2[3:0],\n\t\trgb[2][0] |= ReadBits(in, &bit, 4)<<0;\t//r2[3:0],\n\t\trgb[3][2] |= ReadBits(in, &bit, 1)<<1;\t//b3[1],\n\t\trgb[3][2] |= ReadBits(in, &bit, 1)<<2;\t//b3[2],\n\t\trgb[3][0] |= ReadBits(in, &bit, 4)<<0;\t//r3[3:0],\n\t\trgb[3][2] |= ReadBits(in, &bit, 1)<<4;\t//b3[4],\n\t\trgb[3][2] |= ReadBits(in, &bit, 1)<<3;\t//b3[3]\n\t\tshapeindex = ReadBits(in, &bit, 5);\n\t\tbreak;\n\tcase 14:\t//6\n\t\tVector4Set(cbits, 5,5,5, 9);\n\t\ttr = 1;\n\t\tss = 2;\n\t\trgb[0][0] |= ReadBits(in, &bit, 9)<<0;\t//r0[8:0],\n\t\trgb[2][2] |= ReadBits(in, &bit, 1)<<4;\t//b2[4],\n\t\trgb[0][1] |= ReadBits(in, &bit, 9)<<0;\t//g0[8:0],\n\t\trgb[2][1] |= ReadBits(in, &bit, 1)<<4;\t//g2[4],\n\t\trgb[0][2] |= ReadBits(in, &bit, 9)<<0;\t//b0[8:0],\n\t\trgb[3][2] |= ReadBits(in, &bit, 1)<<4;\t//b3[4],\n\t\trgb[1][0] |= ReadBits(in, &bit, 5)<<0;\t//r1[4:0],\n\t\trgb[3][1] |= ReadBits(in, &bit, 1)<<4;\t//g3[4],\n\t\trgb[2][1] |= ReadBits(in, &bit, 4)<<0;\t//g2[3:0],\n\t\trgb[1][1] |= ReadBits(in, &bit, 5)<<0;\t//g1[4:0],\n\t\trgb[3][2] |= ReadBits(in, &bit, 1)<<0;\t//b3[0],\n\t\trgb[3][1] |= ReadBits(in, &bit, 4)<<0;\t//g3[3:0],\n\t\trgb[1][2] |= ReadBits(in, &bit, 5)<<0;\t//b1[4:0],\n\t\trgb[3][2] |= ReadBits(in, &bit, 1)<<1;\t//b3[1],\n\t\trgb[2][2] |= ReadBits(in, &bit, 4)<<0;\t//b2[3:0],\n\t\trgb[2][0] |= ReadBits(in, &bit, 5)<<0;\t//r2[4:0],\n\t\trgb[3][2] |= ReadBits(in, &bit, 1)<<2;\t//b3[2],\n\t\trgb[3][0] |= ReadBits(in, &bit, 5)<<0;\t//r3[4:0],\n\t\trgb[3][2] |= ReadBits(in, &bit, 1)<<3;\t//b3[3]\n\t\tshapeindex = ReadBits(in, &bit, 5);\n\t\tbreak;\n\tcase 18:\t//7\n\t\tVector4Set(cbits, 6,5,5, 8);\n\t\ttr = 1;\n\t\tss = 2;\n\t\trgb[0][0] |= ReadBits(in, &bit, 8)<<0;\t//r0[7:0],\n\t\trgb[3][1] |= ReadBits(in, &bit, 1)<<4;\t//g3[4],\n\t\trgb[2][2] |= ReadBits(in, &bit, 1)<<4;\t//b2[4],\n\t\trgb[0][1] |= ReadBits(in, &bit, 8)<<0;\t//g0[7:0],\n\t\trgb[3][2] |= ReadBits(in, &bit, 1)<<2;\t//b3[2],\n\t\trgb[2][1] |= ReadBits(in, &bit, 1)<<4;\t//g2[4],\n\t\trgb[0][2] |= ReadBits(in, &bit, 8)<<0;\t//b0[7:0],\n\t\trgb[3][2] |= ReadBits(in, &bit, 1)<<3;\t//b3[3],\n\t\trgb[3][2] |= ReadBits(in, &bit, 1)<<4;\t//b3[4],\n\t\trgb[1][0] |= ReadBits(in, &bit, 6)<<0;\t//r1[5:0],\n\t\trgb[2][1] |= ReadBits(in, &bit, 4)<<0;\t//g2[3:0],\n\t\trgb[1][1] |= ReadBits(in, &bit, 5)<<0;\t//g1[4:0],\n\t\trgb[3][2] |= ReadBits(in, &bit, 1)<<0;\t//b3[0],\n\t\trgb[3][1] |= ReadBits(in, &bit, 4)<<0;\t//g3[3:0],\n\t\trgb[1][2] |= ReadBits(in, &bit, 5)<<0;\t//b1[4:0],\n\t\trgb[3][2] |= ReadBits(in, &bit, 1)<<1;\t//b3[1],\n\t\trgb[2][2] |= ReadBits(in, &bit, 4)<<0;\t//b2[3:0],\n\t\trgb[2][0] |= ReadBits(in, &bit, 6)<<0;\t//r2[5:0],\n\t\trgb[3][0] |= ReadBits(in, &bit, 6)<<0;\t//r3[5:0]\n\t\tshapeindex = ReadBits(in, &bit, 5);\n\t\tbreak;\n\tcase 22:\t//8\n\t\tVector4Set(cbits, 5,6,5, 8);\n\t\ttr = 1;\n\t\tss = 2;\n\t\trgb[0][0] |= ReadBits(in, &bit, 8)<<0;\t//r0[7:0],\n\t\trgb[3][2] |= ReadBits(in, &bit, 1)<<0;\t//b3[0],\n\t\trgb[2][2] |= ReadBits(in, &bit, 1)<<4;\t//b2[4],\n\t\trgb[0][1] |= ReadBits(in, &bit, 8)<<0;\t//g0[7:0],\n\t\trgb[2][1] |= ReadBits(in, &bit, 1)<<5;\t//g2[5],\n\t\trgb[2][1] |= ReadBits(in, &bit, 1)<<4;\t//g2[4],\n\t\trgb[0][2] |= ReadBits(in, &bit, 8)<<0;\t//b0[7:0],\n\t\trgb[3][1] |= ReadBits(in, &bit, 1)<<5;\t//g3[5],\n\t\trgb[3][2] |= ReadBits(in, &bit, 1)<<4;\t//b3[4],\n\t\trgb[1][0] |= ReadBits(in, &bit, 5)<<0;\t//r1[4:0],\n\t\trgb[3][1] |= ReadBits(in, &bit, 1)<<4;\t//g3[4],\n\t\trgb[2][1] |= ReadBits(in, &bit, 4)<<0;\t//g2[3:0],\n\t\trgb[1][1] |= ReadBits(in, &bit, 6)<<0;\t//g1[5:0],\n\t\trgb[3][1] |= ReadBits(in, &bit, 4)<<0;\t//g3[3:0],\n\t\trgb[1][2] |= ReadBits(in, &bit, 5)<<0;\t//b1[4:0],\n\t\trgb[3][2] |= ReadBits(in, &bit, 1)<<1;\t//b3[1],\n\t\trgb[2][2] |= ReadBits(in, &bit, 4)<<0;\t//b2[3:0],\n\t\trgb[2][0] |= ReadBits(in, &bit, 5)<<0;\t//r2[4:0],\n\t\trgb[3][2] |= ReadBits(in, &bit, 1)<<2;\t//b3[2],\n\t\trgb[3][0] |= ReadBits(in, &bit, 5)<<0;\t//r3[4:0],\n\t\trgb[3][2] |= ReadBits(in, &bit, 1)<<3;\t//b3[3]\n\t\tshapeindex = ReadBits(in, &bit, 5);\n\t\tbreak;\n\tcase 26:\t//9\n\t\tVector4Set(cbits, 5,5,6, 8);\n\t\ttr = 1;\n\t\tss = 2;\n\t\trgb[0][0] |= ReadBits(in, &bit, 8)<<0;\t//r0[7:0],\n\t\trgb[3][2] |= ReadBits(in, &bit, 1)<<1;\t//b3[1],\n\t\trgb[2][2] |= ReadBits(in, &bit, 1)<<4;\t//b2[4],\n\t\trgb[0][1] |= ReadBits(in, &bit, 8)<<0;\t//g0[7:0],\n\t\trgb[2][2] |= ReadBits(in, &bit, 1)<<5;\t//b2[5],\n\t\trgb[2][1] |= ReadBits(in, &bit, 1)<<4;\t//g2[4],\n\t\trgb[0][2] |= ReadBits(in, &bit, 8)<<0;\t//b0[7:0],\n\t\trgb[3][2] |= ReadBits(in, &bit, 1)<<5;\t//b3[5],\n\t\trgb[3][2] |= ReadBits(in, &bit, 1)<<4;\t//b3[4],\n\t\trgb[1][0] |= ReadBits(in, &bit, 5)<<0;\t//r1[4:0],\n\t\trgb[3][1] |= ReadBits(in, &bit, 1)<<4;\t//g3[4],\n\t\trgb[2][1] |= ReadBits(in, &bit, 4)<<0;\t//g2[3:0],\n\t\trgb[1][1] |= ReadBits(in, &bit, 5)<<0;\t//g1[4:0],\n\t\trgb[3][2] |= ReadBits(in, &bit, 1)<<0;\t//b3[0],\n\t\trgb[3][1] |= ReadBits(in, &bit, 4)<<0;\t//g3[3:0],\n\t\trgb[1][2] |= ReadBits(in, &bit, 6)<<0;\t//b1[5:0],\n\t\trgb[2][2] |= ReadBits(in, &bit, 4)<<0;\t//b2[3:0],\n\t\trgb[2][0] |= ReadBits(in, &bit, 5)<<0;\t//r2[4:0],\n\t\trgb[3][2] |= ReadBits(in, &bit, 1)<<2;\t//b3[2],\n\t\trgb[3][0] |= ReadBits(in, &bit, 5)<<0;\t//r3[4:0],\n\t\trgb[3][2] |= ReadBits(in, &bit, 1)<<3;\t//b3[3]\n\t\tshapeindex = ReadBits(in, &bit, 5);\n\t\tbreak;\n\tcase 30:\t//10\n\t\tVector4Set(cbits, 6,6,6, 6);\n\t\ttr = 0;\n\t\tss = 2;\n\t\trgb[0][0] |= ReadBits(in, &bit, 6)<<0;\t//r0[5:0],\n\t\trgb[3][1] |= ReadBits(in, &bit, 1)<<4;\t//g3[4],\n\t\trgb[3][2] |= ReadBits(in, &bit, 1)<<0;\t//b3[0],\n\t\trgb[3][2] |= ReadBits(in, &bit, 1)<<1;\t//b3[1],\n\t\trgb[2][2] |= ReadBits(in, &bit, 1)<<4;\t//b2[4],\n\t\trgb[0][1] |= ReadBits(in, &bit, 6)<<0;\t//g0[5:0],\n\t\trgb[2][1] |= ReadBits(in, &bit, 1)<<5;\t//g2[5],\n\t\trgb[2][2] |= ReadBits(in, &bit, 1)<<5;\t//b2[5],\n\t\trgb[3][2] |= ReadBits(in, &bit, 1)<<2;\t//b3[2],\n\t\trgb[2][1] |= ReadBits(in, &bit, 1)<<4;\t//g2[4],\n\t\trgb[0][2] |= ReadBits(in, &bit, 6)<<0;\t//b0[5:0],\n\t\trgb[3][1] |= ReadBits(in, &bit, 1)<<5;\t//g3[5],\n\t\trgb[3][2] |= ReadBits(in, &bit, 1)<<3;\t//b3[3],\n\t\trgb[3][2] |= ReadBits(in, &bit, 1)<<5;\t//b3[5],\n\t\trgb[3][2] |= ReadBits(in, &bit, 1)<<4;\t//b3[4],\n\t\trgb[1][0] |= ReadBits(in, &bit, 6)<<0;\t//r1[5:0],\n\t\trgb[2][1] |= ReadBits(in, &bit, 4)<<0;\t//g2[3:0],\n\t\trgb[1][1] |= ReadBits(in, &bit, 6)<<0;\t//g1[5:0],\n\t\trgb[3][1] |= ReadBits(in, &bit, 4)<<0;\t//g3[3:0],\n\t\trgb[1][2] |= ReadBits(in, &bit, 6)<<0;\t//b1[5:0],\n\t\trgb[2][2] |= ReadBits(in, &bit, 4)<<0;\t//b2[3:0],\n\t\trgb[2][0] |= ReadBits(in, &bit, 6)<<0;\t//r2[5:0],\n\t\trgb[3][0] |= ReadBits(in, &bit, 6)<<0;\t//r3[5:0]\n\t\tshapeindex = ReadBits(in, &bit, 5);\n\t\tbreak;\n\tcase 3: //11\n\t\tVector4Set(cbits, 10,10,10, 10);\n\t\ttr = 0;\n\t\tss = 1;\n\t\trgb[0][0] |= ReadBitsL(in, &bit,10)<<0;\t//r0[9:0]\n\t\trgb[0][1] |= ReadBitsL(in, &bit,10)<<0;\t//g0[9:0]\n\t\trgb[0][2] |= ReadBitsL(in, &bit,10)<<0;\t//b0[9:0]\n\t\trgb[1][0] |= ReadBitsL(in, &bit,10)<<0;\t//r1[9:0]\n\t\trgb[1][1] |= ReadBitsL(in, &bit,10)<<0;\t//g1[9:0]\n\t\trgb[1][2] |= ReadBitsL(in, &bit,10)<<0;\t//b1[9:0]\n\t\tshapeindex = 0;\n\t\tbreak;\n\tcase 7: //12\n\t\tVector4Set(cbits, 9,9,9, 11);\n\t\ttr = 1;\n\t\tss = 1;\n\t\trgb[0][0] |= ReadBitsL(in, &bit,10)<<0;\t//r0[9:0],\n\t\trgb[0][1] |= ReadBitsL(in, &bit,10)<<0;\t//g0[9:0],\n\t\trgb[0][2] |= ReadBitsL(in, &bit,10)<<0;\t//b0[9:0],\n\t\trgb[1][0] |= ReadBits(in, &bit, 9)<<0;\t//r1[8:0],\n\t\trgb[0][0] |= ReadBits(in, &bit, 1)<<10;\t//r0[10],\n\t\trgb[1][1] |= ReadBits(in, &bit, 9)<<0;\t//g1[8:0],\n\t\trgb[0][1] |= ReadBits(in, &bit, 1)<<10;\t//g0[10],\n\t\trgb[1][2] |= ReadBits(in, &bit, 9)<<0;\t//b1[8:0],\n\t\trgb[0][2] |= ReadBits(in, &bit, 1)<<10;\t//b0[10]\n\t\tshapeindex = 0;\n\t\tbreak;\n\tcase 11: //13\n\t\tVector4Set(cbits, 8,8,8, 12);\n\t\ttr = 1;\n\t\tss = 1;\n\t\trgb[0][0] |= ReadBitsL(in, &bit,10)<<0;\t//r0[9:0],\n\t\trgb[0][1] |= ReadBitsL(in, &bit,10)<<0;\t//g0[9:0],\n\t\trgb[0][2] |= ReadBitsL(in, &bit,10)<<0;\t//b0[9:0],\n\t\trgb[1][0] |= ReadBits(in, &bit, 8)<<0;\t//r1[7:0],\n\t\trgb[0][0] |=(ReadBits(in, &bit, 1)<<11)\t//r0[10:11],\n\t\t          | (ReadBits(in, &bit, 1)<<10);\n\t\trgb[1][1] |= ReadBits(in, &bit, 8)<<0;\t//g1[7:0],\n\t\trgb[0][1] |=(ReadBits(in, &bit, 1)<<11)\t//g0[10:11],\n\t\t          | (ReadBits(in, &bit, 1)<<10);\n\t\trgb[1][2] |= ReadBits(in, &bit, 8)<<0;\t//b1[7:0],\n\t\trgb[0][2] |=(ReadBits(in, &bit, 1)<<11)\t//b0[10:11],\n\t\t          | (ReadBits(in, &bit, 1)<<10);\n\t\tshapeindex = 0;\n\t\tbreak;\n\tcase 15: //14\n\t\t//UNTESTED\n\t\tVector4Set(cbits, 4,4,4, 16);\n\t\ttr = 1;\n\t\tss = 1;\n\t\trgb[0][0] |= ReadBitsL(in, &bit,10)<<0;\t//r0[9:0],\n\t\trgb[0][1] |= ReadBitsL(in, &bit,10)<<0;\t//g0[9:0],\n\t\trgb[0][2] |= ReadBitsL(in, &bit,10)<<0;\t//b0[9:0],\n\t\trgb[1][0] |= ReadBits(in, &bit, 4)<<0;\t//r1[3:0],\n\t\trgb[0][0] |=(ReadBits(in, &bit, 1)<<15)\t//r0[10:15],\n\t\t          | (ReadBits(in, &bit, 1)<<14)\n\t\t          | (ReadBits(in, &bit, 1)<<13)\n\t\t          | (ReadBits(in, &bit, 1)<<12)\n\t\t          | (ReadBits(in, &bit, 1)<<11)\n\t\t          | (ReadBits(in, &bit, 1)<<10);\n\t\trgb[1][1] |= ReadBits(in, &bit, 4)<<0;\t//g1[3:0],\n\t\trgb[0][1] |=(ReadBits(in, &bit, 1)<<15)\t//g0[10:15],\n\t\t          | (ReadBits(in, &bit, 1)<<14)\n\t\t          | (ReadBits(in, &bit, 1)<<13)\n\t\t          | (ReadBits(in, &bit, 1)<<12)\n\t\t          | (ReadBits(in, &bit, 1)<<11)\n\t\t          | (ReadBits(in, &bit, 1)<<10);\n\t\trgb[1][2] |= ReadBits(in, &bit, 4)<<0;\t//b1[3:0],\n\t\trgb[0][2] |=(ReadBits(in, &bit, 1)<<15)\t//b0[10:15]\n\t\t          | (ReadBits(in, &bit, 1)<<14)\n\t\t          | (ReadBits(in, &bit, 1)<<13)\n\t\t          | (ReadBits(in, &bit, 1)<<12)\n\t\t          | (ReadBits(in, &bit, 1)<<11)\n\t\t          | (ReadBits(in, &bit, 1)<<10);\n\t\tshapeindex = 0;\n\t\tbreak;\n\n\tdefault:\n\t\t//reserved modes\n\t\tfor (i = 0; i < 16; )\n\t\t{\n\t\t\tVector4Set(out[i].v, 0, 0, 0, 0xf<<10);\n\t\t\ti++;\n\t\t\tif (!(i & 3))\n\t\t\t\tout += w-4;\n\t\t}\n\t\treturn;\n\t}\n\n\tib = (ss==2)?3:4;\n\tcb = 1<<ib;\n\tweight = wsz[ib];\n\tif (signextend)\n\t{\n\t\tint v[2], q, k, g, s;\n\t\tfor (g = 0; g < ss; g++)\t//subsets\n\t\t{\n\t\t\tfor (j = 0; j < 3; j++)\t//channels\n\t\t\t{\n\t\t\t\tfor (k = 0; k < 2; k++)\t//start/end\n\t\t\t\t{\n\t\t\t\t\tv[k] = rgb[k+g*2][j];\n\t\t\t\t\tif ((k || g) && tr)\n\t\t\t\t\t{\t//the specified colour values are deltas from the first colour value\n\t\t\t\t\t\tif (v[k] & (1<<(cbits[j]-1)))\t//high bit set\n\t\t\t\t\t\t\tv[k] |= ~0<<cbits[j];\t\t//sign extend it...\n\t\t\t\t\t\tv[k] = (int)rgb[0][j] + v[k];\t\t//add it to the base\n\t\t\t\t\t\tv[k] &= ((1<<cbits[3])-1);\t\t//and mask it to avoid overflows...\n\t\t\t\t\t}\n\t\t\t\t\tif (v[k] & (1<<(cbits[3]-1)))\t//high bit set\n\t\t\t\t\t\tv[k] |= ~0<<cbits[3];\t\t//sign extend it\n\n\t\t\t\t\tif (cbits[3] >= 16)\n\t\t\t\t\t\t;\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\ts = v[k] < 0;\n\t\t\t\t\t\tif (s)\n\t\t\t\t\t\t\tv[k]=-v[k];\n\t\t\t\t\t\tif (v[k] == 0)\n\t\t\t\t\t\t\tv[k] = 0;\n\t\t\t\t\t\telse if (v[k] >= (1<<(cbits[3]-1))-1)\n\t\t\t\t\t\t\tv[k] = 0x7fff;\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tv[k] = (v[k] * (0x7fff+1) + (0x7fff+1)/2) >> (cbits[3]-1);\n\t\t\t\t\t\tif (s)\n\t\t\t\t\t\t\tv[k] = -v[k];\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tfor (i = 0; i < cb; i++)\t//indexbits\n\t\t\t\t{\n\t\t\t\t\tq = (v[0]*(64-weight[i]) + v[1]*weight[i])>>6;\t//this ignores sign, which seems somewhat dodgy.\n\t\t\t\t\tq = (q < 0) ? -(((-q) * 31) >> 5) : (q * 31) >> 5;\n\t\t\t\t\tpalette[i+g*8][j] = q;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\telse\n\t{\n\t\tint v[2], q, k, g;\n\t\tfor (g = 0; g < ss; g++)\t//subsets\n\t\t{\n\t\t\tfor (j = 0; j < 3; j++)\t//channels\n\t\t\t{\n\t\t\t\tfor (k = 0; k < 2; k++)\t//start/end\n\t\t\t\t{\n\t\t\t\t\tv[k] = rgb[k+g*2][j];\n\t\t\t\t\tif ((k || g) && tr)\n\t\t\t\t\t{\t//the specified colour values are deltas from the first colour value\n\t\t\t\t\t\tif (v[k] & (1<<(cbits[j]-1)))\t//high bit set\n\t\t\t\t\t\t\tv[k] |= ~0<<cbits[j];\t\t//sign extend it...\n\t\t\t\t\t\tv[k] = (int)rgb[0][j] + v[k];\t\t//add it to the base\n\t\t\t\t\t\tv[k] &= ((1<<cbits[3])-1);\t\t//and mask it to avoid overflows...\n//\t\t\t\t\t\tif (v[k] & (1<<(bits-1)))\t//high bit set\n//\t\t\t\t\t\t\tv[k] |= ~0<<bits;\t\t//sign extend it\n\t\t\t\t\t}\n\n\t\t\t\t\tif (cbits[3] >= 15)\n\t\t\t\t\t\t;\n\t\t\t\t\telse if (v[k] == 0)\n\t\t\t\t\t\tv[k] = 0;\n\t\t\t\t\telse if (v[k] == (1<<cbits[3])-1)\n\t\t\t\t\t\tv[k] = 0xffff;\n\t\t\t\t\telse\n\t\t\t\t\t\tv[k] = (v[k] * (0xffff+1) + (0xffff+1)/2) >> cbits[3];\n\t\t\t\t}\n\t\t\t\tfor (i = 0; i < cb; i++)\t//indexbits\n\t\t\t\t{\n\t\t\t\t\tq = (v[0]*(64-weight[i]) + v[1]*weight[i])>>6;\n\t\t\t\t\tq = (q*31)>>6;\n\t\t\t\t\tpalette[i+g*8][j] = q;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tif (ss == 2)\n\t{\n\t\tconst qbyte *p = p2+shapeindex*16;\n\t\tfor (i = 0; i < 16; )\n\t\t{\n\t\t\tint pidx = p[i];\n\t\t\tint idx;\n\t\t\tif (i == 0 || i == anchors[shapeindex])\n\t\t\t\tidx = ReadBits(in, &bit, ib-1);\n\t\t\telse\n\t\t\t\tidx = ReadBits(in, &bit, ib);\n\t\t\tif (pidx)\n\t\t\t\tidx += 8;\n\t\t\tout[i].v[0] = palette[idx][0];\n\t\t\tout[i].v[1] = palette[idx][1];\n\t\t\tout[i].v[2] = palette[idx][2];\n\t\t\tout[i].v[3] = 0xf<<10;\t//must be 1\n\t\t\ti++;\n\t\t\tif (!(i & 3))\n\t\t\t\tout += w-4;\n\t\t}\n\t}\n\telse\n\t{\n\t\tfor (i = 0; i < 16; )\n\t\t{\n\t\t\tint idx;\n\t\t\tif (i == 0)\n\t\t\t\tidx = ReadBits(in, &bit, ib-1);\n\t\t\telse\n\t\t\t\tidx = ReadBits(in, &bit, ib);\n\t\t\tout[i].v[0] = palette[idx][0];\n\t\t\tout[i].v[1] = palette[idx][1];\n\t\t\tout[i].v[2] = palette[idx][2];\n\t\t\tout[i].v[3] = 0xf<<10;\t//must be 1\n\t\t\ti++;\n\t\t\tif (!(i & 3))\n\t\t\t\tout += w-4;\n\t\t}\n\t}\n}\nstatic void Image_Decode_BC7_Block(qbyte *fte_restrict in, pixel32_t *fte_restrict out, int w, uploadfmt_t fmt)\n{\n\tstatic const qbyte p1[] = {\n\t\t0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,\n\t};\n\tstatic const qbyte p2[] = {\n\t\t0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,\n\t\t0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,\n\t\t0,1,1,1,0,1,1,1,0,1,1,1,0,1,1,1,\n\t\t0,0,0,1,0,0,1,1,0,0,1,1,0,1,1,1,\n\t\t0,0,0,0,0,0,0,1,0,0,0,1,0,0,1,1,\n\t\t0,0,1,1,0,1,1,1,0,1,1,1,1,1,1,1,\n\t\t0,0,0,1,0,0,1,1,0,1,1,1,1,1,1,1,\n\t\t0,0,0,0,0,0,0,1,0,0,1,1,0,1,1,1,\n\t\t0,0,0,0,0,0,0,0,0,0,0,1,0,0,1,1,\n\t\t0,0,1,1,0,1,1,1,1,1,1,1,1,1,1,1,\n\t\t0,0,0,0,0,0,0,1,0,1,1,1,1,1,1,1,\n\t\t0,0,0,0,0,0,0,0,0,0,0,1,0,1,1,1,\n\t\t0,0,0,1,0,1,1,1,1,1,1,1,1,1,1,1,\n\t\t0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,\n\t\t0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,\n\t\t0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,\n\t\t0,0,0,0,1,0,0,0,1,1,1,0,1,1,1,1,\n\t\t0,1,1,1,0,0,0,1,0,0,0,0,0,0,0,0,\n\t\t0,0,0,0,0,0,0,0,1,0,0,0,1,1,1,0,\n\t\t0,1,1,1,0,0,1,1,0,0,0,1,0,0,0,0,\n\t\t0,0,1,1,0,0,0,1,0,0,0,0,0,0,0,0,\n\t\t0,0,0,0,1,0,0,0,1,1,0,0,1,1,1,0,\n\t\t0,0,0,0,0,0,0,0,1,0,0,0,1,1,0,0,\n\t\t0,1,1,1,0,0,1,1,0,0,1,1,0,0,0,1,\n\t\t0,0,1,1,0,0,0,1,0,0,0,1,0,0,0,0,\n\t\t0,0,0,0,1,0,0,0,1,0,0,0,1,1,0,0,\n\t\t0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,\n\t\t0,0,1,1,0,1,1,0,0,1,1,0,1,1,0,0,\n\t\t0,0,0,1,0,1,1,1,1,1,1,0,1,0,0,0,\n\t\t0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0,\n\t\t0,1,1,1,0,0,0,1,1,0,0,0,1,1,1,0,\n\t\t0,0,1,1,1,0,0,1,1,0,0,1,1,1,0,0,\n\t\t0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,\n\t\t0,0,0,0,1,1,1,1,0,0,0,0,1,1,1,1,\n\t\t0,1,0,1,1,0,1,0,0,1,0,1,1,0,1,0,\n\t\t0,0,1,1,0,0,1,1,1,1,0,0,1,1,0,0,\n\t\t0,0,1,1,1,1,0,0,0,0,1,1,1,1,0,0,\n\t\t0,1,0,1,0,1,0,1,1,0,1,0,1,0,1,0,\n\t\t0,1,1,0,1,0,0,1,0,1,1,0,1,0,0,1,\n\t\t0,1,0,1,1,0,1,0,1,0,1,0,0,1,0,1,\n\t\t0,1,1,1,0,0,1,1,1,1,0,0,1,1,1,0,\n\t\t0,0,0,1,0,0,1,1,1,1,0,0,1,0,0,0,\n\t\t0,0,1,1,0,0,1,0,0,1,0,0,1,1,0,0,\n\t\t0,0,1,1,1,0,1,1,1,1,0,1,1,1,0,0,\n\t\t0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,\n\t\t0,0,1,1,1,1,0,0,1,1,0,0,0,0,1,1,\n\t\t0,1,1,0,0,1,1,0,1,0,0,1,1,0,0,1,\n\t\t0,0,0,0,0,1,1,0,0,1,1,0,0,0,0,0,\n\t\t0,1,0,0,1,1,1,0,0,1,0,0,0,0,0,0,\n\t\t0,0,1,0,0,1,1,1,0,0,1,0,0,0,0,0,\n\t\t0,0,0,0,0,0,1,0,0,1,1,1,0,0,1,0,\n\t\t0,0,0,0,0,1,0,0,1,1,1,0,0,1,0,0,\n\t\t0,1,1,0,1,1,0,0,1,0,0,1,0,0,1,1,\n\t\t0,0,1,1,0,1,1,0,1,1,0,0,1,0,0,1,\n\t\t0,1,1,0,0,0,1,1,1,0,0,1,1,1,0,0,\n\t\t0,0,1,1,1,0,0,1,1,1,0,0,0,1,1,0,\n\t\t0,1,1,0,1,1,0,0,1,1,0,0,1,0,0,1,\n\t\t0,1,1,0,0,0,1,1,0,0,1,1,1,0,0,1,\n\t\t0,1,1,1,1,1,1,0,1,0,0,0,0,0,0,1,\n\t\t0,0,0,1,1,0,0,0,1,1,1,0,0,1,1,1,\n\t\t0,0,0,0,1,1,1,1,0,0,1,1,0,0,1,1,\n\t\t0,0,1,1,0,0,1,1,1,1,1,1,0,0,0,0,\n\t\t0,0,1,0,0,0,1,0,1,1,1,0,1,1,1,0,\n\t\t0,1,0,0,0,1,0,0,0,1,1,1,0,1,1,1,\n    };\n    static const qbyte p3[] = {\n\t\t0,0,1,1,0,0,1,1,0,2,2,1,2,2,2,2,\n\t\t0,0,0,1,0,0,1,1,2,2,1,1,2,2,2,1,\n\t\t0,0,0,0,2,0,0,1,2,2,1,1,2,2,1,1,\n\t\t0,2,2,2,0,0,2,2,0,0,1,1,0,1,1,1,\n\t\t0,0,0,0,0,0,0,0,1,1,2,2,1,1,2,2,\n\t\t0,0,1,1,0,0,1,1,0,0,2,2,0,0,2,2,\n\t\t0,0,2,2,0,0,2,2,1,1,1,1,1,1,1,1,\n\t\t0,0,1,1,0,0,1,1,2,2,1,1,2,2,1,1,\n\t\t0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,\n\t\t0,0,0,0,1,1,1,1,1,1,1,1,2,2,2,2,\n\t\t0,0,0,0,1,1,1,1,2,2,2,2,2,2,2,2,\n\t\t0,0,1,2,0,0,1,2,0,0,1,2,0,0,1,2,\n\t\t0,1,1,2,0,1,1,2,0,1,1,2,0,1,1,2,\n\t\t0,1,2,2,0,1,2,2,0,1,2,2,0,1,2,2,\n\t\t0,0,1,1,0,1,1,2,1,1,2,2,1,2,2,2,\n\t\t0,0,1,1,2,0,0,1,2,2,0,0,2,2,2,0,\n\t\t0,0,0,1,0,0,1,1,0,1,1,2,1,1,2,2,\n\t\t0,1,1,1,0,0,1,1,2,0,0,1,2,2,0,0,\n\t\t0,0,0,0,1,1,2,2,1,1,2,2,1,1,2,2,\n\t\t0,0,2,2,0,0,2,2,0,0,2,2,1,1,1,1,\n\t\t0,1,1,1,0,1,1,1,0,2,2,2,0,2,2,2,\n\t\t0,0,0,1,0,0,0,1,2,2,2,1,2,2,2,1,\n\t\t0,0,0,0,0,0,1,1,0,1,2,2,0,1,2,2,\n\t\t0,0,0,0,1,1,0,0,2,2,1,0,2,2,1,0,\n\t\t0,1,2,2,0,1,2,2,0,0,1,1,0,0,0,0,\n\t\t0,0,1,2,0,0,1,2,1,1,2,2,2,2,2,2,\n\t\t0,1,1,0,1,2,2,1,1,2,2,1,0,1,1,0,\n\t\t0,0,0,0,0,1,1,0,1,2,2,1,1,2,2,1,\n\t\t0,0,2,2,1,1,0,2,1,1,0,2,0,0,2,2,\n\t\t0,1,1,0,0,1,1,0,2,0,0,2,2,2,2,2,\n\t\t0,0,1,1,0,1,2,2,0,1,2,2,0,0,1,1,\n\t\t0,0,0,0,2,0,0,0,2,2,1,1,2,2,2,1,\n\t\t0,0,0,0,0,0,0,2,1,1,2,2,1,2,2,2,\n\t\t0,2,2,2,0,0,2,2,0,0,1,2,0,0,1,1,\n\t\t0,0,1,1,0,0,1,2,0,0,2,2,0,2,2,2,\n\t\t0,1,2,0,0,1,2,0,0,1,2,0,0,1,2,0,\n\t\t0,0,0,0,1,1,1,1,2,2,2,2,0,0,0,0,\n\t\t0,1,2,0,1,2,0,1,2,0,1,2,0,1,2,0,\n\t\t0,1,2,0,2,0,1,2,1,2,0,1,0,1,2,0,\n\t\t0,0,1,1,2,2,0,0,1,1,2,2,0,0,1,1,\n\t\t0,0,1,1,1,1,2,2,2,2,0,0,0,0,1,1,\n\t\t0,1,0,1,0,1,0,1,2,2,2,2,2,2,2,2,\n\t\t0,0,0,0,0,0,0,0,2,1,2,1,2,1,2,1,\n\t\t0,0,2,2,1,1,2,2,0,0,2,2,1,1,2,2,\n\t\t0,0,2,2,0,0,1,1,0,0,2,2,0,0,1,1,\n\t\t0,2,2,0,1,2,2,1,0,2,2,0,1,2,2,1,\n\t\t0,1,0,1,2,2,2,2,2,2,2,2,0,1,0,1,\n\t\t0,0,0,0,2,1,2,1,2,1,2,1,2,1,2,1,\n\t\t0,1,0,1,0,1,0,1,0,1,0,1,2,2,2,2,\n\t\t0,2,2,2,0,1,1,1,0,2,2,2,0,1,1,1,\n\t\t0,0,0,2,1,1,1,2,0,0,0,2,1,1,1,2,\n\t\t0,0,0,0,2,1,1,2,2,1,1,2,2,1,1,2,\n\t\t0,2,2,2,0,1,1,1,0,1,1,1,0,2,2,2,\n\t\t0,0,0,2,1,1,1,2,1,1,1,2,0,0,0,2,\n\t\t0,1,1,0,0,1,1,0,0,1,1,0,2,2,2,2,\n\t\t0,0,0,0,0,0,0,0,2,1,1,2,2,1,1,2,\n\t\t0,1,1,0,0,1,1,0,2,2,2,2,2,2,2,2,\n\t\t0,0,2,2,0,0,1,1,0,0,1,1,0,0,2,2,\n\t\t0,0,2,2,1,1,2,2,1,1,2,2,0,0,2,2,\n\t\t0,0,0,0,0,0,0,0,0,0,0,0,2,1,1,2,\n\t\t0,0,0,2,0,0,0,1,0,0,0,2,0,0,0,1,\n\t\t0,2,2,2,1,2,2,2,0,2,2,2,1,2,2,2,\n\t\t0,1,0,1,2,2,2,2,2,2,2,2,2,2,2,2,\n\t\t0,1,1,1,2,0,1,1,2,2,0,1,2,2,2,0,\n    };\n\n    static const qbyte *psz[] = {p1,p1,p2,p3};\n\n\tstatic const qbyte anchortable[][64] = {{\n\t\t15,15,15,15,15,15,15,15,\n\t\t15,15,15,15,15,15,15,15,\n\t\t15, 2, 8, 2, 2, 8, 8,15,\n\t\t 2, 8, 2, 2, 8, 8, 2, 2,\n\t\t15,15, 6, 8, 2, 8,15,15,\n\t\t 2, 8, 2, 2, 2,15,15, 6,\n\t\t 6, 2, 6, 8,15,15, 2, 2,\n\t\t15,15,15,15,15, 2, 2,15,\n    },{\n\t\t 3, 3,15,15, 8, 3,15,15,\n\t\t 8, 8, 6, 6, 6, 5, 3, 3,\n\t\t 3, 3, 8,15, 3, 3, 6,10,\n\t\t 5, 8, 8, 6, 8, 5,15,15,\n\t\t 8,15, 3, 5, 6,10, 8,15,\n\t\t15, 3,15, 5,15,15,15,15,\n\t\t 3,15, 5, 5, 5, 8, 5,10,\n\t\t 5,10, 8,13,15,12, 3, 3,\n\t},{\n\t\t15, 8, 8, 3,15,15, 3, 8,\n\t\t15,15,15,15,15,15,15, 8,\n\t\t15, 8,15, 3,15, 8,15, 8,\n\t\t 3,15, 6,10,15,15,10, 8,\n\t\t15, 3,15,10,10, 8, 9,10,\n\t\t 6,15, 8,15, 3, 6, 6, 8,\n\t\t15, 3,15,15,15,15,15,15,\n\t\t15,15,15,15, 3,15,15, 8,\n    }};\n\tint anchor[3];\n\n\tstatic const int w1[] = {0, 64};\n    static const int w2[] = {0, 21, 43, 64};\n    static const int w3[] = {0, 9, 18, 27, 37, 46, 55, 64};\n    static const int w4[] = {0, 4, 9, 13, 17, 21, 26, 30, 34, 38, 43, 47, 51, 55, 60, 64};\n    static const int *wsz[] = {w1,w1, w2,w3,w4};\n    static const struct {\n\t\tint numsubsets;\n\t\tint partitionbits;\n\t\tint rotationbits;\n\t\tint indexselectionbits;\n\t\tint colourbits;\n\t\tint alphabits;\n\t\tint pmode;\n\t\tint indexbits[2];\n    } m[9] =\n    {\n\t\t{3, 4, 0, 0, 4, 0, 1, {3, 0}},\t//mode 0 - 3*rgb4\n\t\t{2, 6, 0, 0, 6, 0, 2, {3, 0}},\t//mode 1 - 2*rgb6\n\t\t{3, 6, 0, 0, 5, 0, 0, {2, 0}},\t//mode 2 - 3*rgb5\n\t\t{2, 6, 0, 0, 7, 0, 1, {2, 0}},\t//mode 3 - 2*rgb7\n\t\t{1, 0, 2, 1, 5, 6, 0, {2, 3}},\t//mode 4 - 1*rgb5+a6\n\t\t{1, 0, 2, 0, 7, 8, 0, {2, 2}},\t//mode 5 - 1*rgb7+a8\n\t\t{1, 0, 0, 0, 7, 7, 1, {4, 0}},\t//mode 6 - 1*rgb7a7\n\t\t{2, 6, 0, 0, 5, 5, 1, {2, 0}},\t//mode 7 - 2*rgb5a5\n\t\t{1, 0, 0, 0, 0, 0, 0, {0, 0}},\t//mode 8 - reserved\n    };\n\n\tpixel32_t palette[3][2];\n\tpixel32_t tab[3][16];\n\tint mode, i, j, bit, partition, ss, cb;\n\tconst int *weight;\n\tconst qbyte *p;\n\tint rot;\n\tint idxsel;\n\tfor (mode = 0; mode < 8; mode++)\n\t\tif (*in & (1u<<mode))\n\t\t\tbreak;\n\tss = m[mode].numsubsets;\n\tbit = mode+1;\n\tpartition = ReadBits(in, &bit, m[mode].partitionbits);\n\trot = ReadBits(in, &bit, m[mode].rotationbits);\n\tidxsel = ReadBits(in, &bit, m[mode].indexselectionbits);\n\tp = psz[ss] + partition*16;\n\tfor (j = 0; j < 3; j++)\n\t\tfor (i = 0; i < ss; i++)\n\t\t{\n\t\t\tpalette[i][0].v[j] = ReadBits(in, &bit, m[mode].colourbits) << (8-m[mode].colourbits);\n\t\t\tpalette[i][1].v[j] = ReadBits(in, &bit, m[mode].colourbits) << (8-m[mode].colourbits);\n\t\t}\n\tfor (i = 0; i < ss; i++)\n\t{\n\t\tif (m[mode].alphabits)\n\t\t{\n\t\t\tpalette[i][0].v[3] = ReadBits(in, &bit, m[mode].alphabits) << (8-m[mode].alphabits);\n\t\t\tpalette[i][1].v[3] = ReadBits(in, &bit, m[mode].alphabits) << (8-m[mode].alphabits);\n\t\t}\n\t\telse palette[i][0].v[3] = palette[i][1].v[3] = 255;\n\t}\n\n\tif(m[mode].pmode)\n\t{\n\t\tfor (i = 0; i < m[mode].numsubsets; i++)\n\t\t{\n\t\t\tqbyte p = ReadBits(in, &bit, 1);\n\t\t\tfor (j = 0; j < 3; j++)\n\t\t\t\tpalette[i][0].v[j] |= p<<(7-m[mode].colourbits);\n\t\t\tpalette[i][0].v[3] |= p<<(7-m[mode].alphabits);\n\n\t\t\tif (m[mode].pmode!=2)\n\t\t\t\tp = ReadBits(in, &bit, 1);\n\t\t\tfor (j = 0; j < 3; j++)\n\t\t\t\tpalette[i][1].v[j] |= p<<(7-m[mode].colourbits);\n\t\t\tpalette[i][1].v[3] |= p<<(7-m[mode].alphabits);\n\t\t}\n\t\tetc_expandv(palette[i][0], m[mode].colourbits+1, m[mode].colourbits+1, m[mode].colourbits+1); palette[i][0].v[3]|=palette[i][0].v[3]>>(m[mode].alphabits+1);\n\t\tetc_expandv(palette[i][1], m[mode].colourbits+1, m[mode].colourbits+1, m[mode].colourbits+1); palette[i][0].v[3]|=palette[i][0].v[3]>>(m[mode].alphabits+1);\n\t}\n\telse\n\t{\n\t\tetc_expandv(palette[i][0], m[mode].colourbits, m[mode].colourbits, m[mode].colourbits); palette[i][0].v[3]|=palette[i][0].v[3]>>m[mode].alphabits;\n\t\tetc_expandv(palette[i][1], m[mode].colourbits, m[mode].colourbits, m[mode].colourbits);\tpalette[i][1].v[3]|=palette[i][0].v[3]>>m[mode].alphabits;\n\t}\n\n\tcb = m[mode].indexbits[idxsel];\n\tweight = wsz[cb];\n\n\t//build lerps table (could do it per pixel?)\n\tfor (i = 0; i < m[mode].numsubsets; i++)\n\t{\n\t\tfor (j = 0; j < (1<<cb); j++)\n\t\t{\n\t\t\ttab[i][j].v[0] = (palette[i][0].v[0]*(64-weight[j]) + palette[i][1].v[0]*weight[j] + 32)>>6;\n\t\t\ttab[i][j].v[1] = (palette[i][0].v[1]*(64-weight[j]) + palette[i][1].v[1]*weight[j] + 32)>>6;\n\t\t\ttab[i][j].v[2] = (palette[i][0].v[2]*(64-weight[j]) + palette[i][1].v[2]*weight[j] + 32)>>6;\n\t\t\ttab[i][j].v[3] = (palette[i][0].v[3]*(64-weight[j]) + palette[i][1].v[3]*weight[j] + 32)>>6;\n\t\t}\n\n\t\t//this stuff is annoying, but saves a bit or two\n\t\tif (!cb)\n\t\t\tanchor[i] = 16;\t//don't allow the cb-1 to read negative bits!\n\t\telse if (i)\n\t\t\tanchor[i] = anchortable[(ss>2)?i:0][partition];\n\t\telse\n\t\t\tanchor[i] = 0;\n\t}\n\n\t//okay, tables are all set up, spew out the pixels\n\tfor (i = 0; i < 16; )\n\t{\n\t\tint pidx = p[i];\n\t\tint idx;\n\t\tif (i == anchor[pidx])\n\t\t\tidx = ReadBits(in, &bit, cb-1);\n\t\telse\n\t\t\tidx = ReadBits(in, &bit, cb);\n\t\tout[i].u = tab[pidx][idx].u;\n\t\ti++;\n\t\tif (!(i & 3))\n\t\t\tout += w-4;\n\t}\n\n\t//mode has separate alpha indexes, spew those out too, clobbering any alpha from dodgy rgb blends\n\tif (m[mode].indexbits[idxsel^1])\n\t{\t//FIXME: untested\n\t\tout -= w*4;\n\t\tcb = m[mode].indexbits[idxsel^1];\n\t\tweight = wsz[cb];\n\t\tfor (i = 0; i < m[mode].numsubsets; i++)\n\t\t{\n\t\t\tfor (j = 0; j < (1<<cb); j++)\n\t\t\t\ttab[i][j].v[3] = (palette[i][0].v[3]*(64-weight[j]) + palette[i][1].v[3]*weight[j] + 32)>>6;\n\t\t}\n\n\t\tfor (i = 0; i < 16; )\n\t\t{\n\t\t\tint idx;\n\t\t\tif (i == 0)\n\t\t\t\tidx = ReadBits(in, &bit, cb-1);\n\t\t\telse\n\t\t\t\tidx = ReadBits(in, &bit, cb);\n\t\t\tout[i].v[3] = tab[p[i]][idx].v[3];\n\t\t\ti++;\n\t\t\tif (!(i & 3))\n\t\t\t\tout += w-4;\n\t\t}\n\t}\n\n\t//some modes allow swapping the alpha with an rgb channel (per block)\n\tif (rot)\n\t{\t//FIXME: untested\n\t\tqbyte t;\n\t\trot--; //0=disable, 1=red, 2=green, 3=blue\n\t\tout -= w*4;\n\t\tfor (i = 0; i < 16; )\n\t\t{\n\t\t\tt = out[i].v[3];\n\t\t\tout[i].v[3] = out[i].v[rot];\n\t\t\tout[i].v[rot] = t;\n\n\t\t\ti++;\n\t\t\tif (!(i & 3))\n\t\t\t\tout += w-4;\n\t\t}\n\t}\n}\n#endif\n\n#ifdef DECOMPRESS_ASTC\n#ifdef ASTC_WITH_LDR\nstatic void Image_Decode_ASTC_LDR_U8_Block(qbyte *fte_restrict in, pixel32_t *fte_restrict out, int stride, uploadfmt_t fmt)\n{\n\tint bw, bh, bd, blockbytes;\n\tImage_BlockSizeForEncoding(fmt, &blockbytes, &bw, &bh, &bd);\n\tASTC_Decode_LDR8(in, out->v, stride, 0/*w*h*/, bw, bh, bd);\n}\n#endif\n#ifdef ASTC_WITH_HDR\nstatic void Image_Decode_ASTC_HDR_HF_Block(qbyte *fte_restrict in, pixel64_t *fte_restrict out, int stride, uploadfmt_t fmt)\n{\n\tint bw, bh, bd, blockbytes;\n\tImage_BlockSizeForEncoding(fmt, &blockbytes, &bw, &bh, &bd);\n\tASTC_Decode_HDR(in, out->v, stride, 0/*w*h*/, bw, bh, bd);\n}\n\n/*static unsigned int RGB16F_to_E5BGR9(unsigned short Cr, unsigned short Cg, unsigned short Cb)\n{\n\tint Re,Ge,Be, Rex,Gex,Bex, Xm, Xe;\n\tquint32_t rshift, gshift, bshift, expo;\n\tint Rm, Gm, Bm;\n\n\tif( Cr > 0x7c00 ) Cr = 0; else if( Cr == 0x7c00 ) Cr = 0x7bff;\n\tif( Cg > 0x7c00 ) Cg = 0; else if( Cg == 0x7c00 ) Cg = 0x7bff;\n\tif( Cb > 0x7c00 ) Cb = 0; else if( Cb == 0x7c00 ) Cb = 0x7bff;\n\tRe = (Cr >> 10) & 0x1F;\n\tGe = (Cg >> 10) & 0x1F;\n\tBe = (Cb >> 10) & 0x1F;\n\tRex = Re == 0 ? 1 : Re;\n\tGex = Ge == 0 ? 1 : Ge;\n\tBex = Be == 0 ? 1 : Be;\n\tXm = ((Cr | Cg | Cb) & 0x200) >> 9;\n\tXe = Re | Ge | Be;\n\n\tif (Xe == 0)\n\t{\n\t\texpo = rshift = gshift = bshift = Xm;\n\t}\n\telse if (Re >= Ge && Re >= Be)\n\t{\n\t\texpo = Rex + 1;\n\t\trshift = 2;\n\t\tgshift = Rex - Gex + 2;\n\t\tbshift = Rex - Bex + 2;\n\t}\n\telse if (Ge >= Be)\n\t{\n\t\texpo = Gex + 1;\n\t\trshift = Gex - Rex + 2;\n\t\tgshift = 2;\n\t\tbshift = Gex - Bex + 2;\n\t}\n\telse\n\t{\n\t\texpo = Bex + 1;\n\t\trshift = Bex - Rex + 2;\n\t\tgshift = Bex - Gex + 2;\n\t\tbshift = 2;\n\t}\n\n\tRm = (Cr & 0x3FF) | (Re == 0 ? 0 : 0x400);\n\tGm = (Cg & 0x3FF) | (Ge == 0 ? 0 : 0x400);\n\tBm = (Cb & 0x3FF) | (Be == 0 ? 0 : 0x400);\n\tRm = (Rm >> rshift) & 0x1FF;\n\tGm = (Gm >> gshift) & 0x1FF;\n\tBm = (Bm >> bshift) & 0x1FF;\n\n\treturn (expo << 27) | (Bm << 18) | (Gm << 9) | (Rm << 0);\n}\nstatic void Image_Decode_ASTC_HDR_E5_Block(qbyte *fte_restrict in, pixel32_t *fte_restrict out, int stride, uploadfmt_t fmt)\n{\n\tunsigned short hdr[12][12][4];\n\tint bw, bh, blockbytes;\n\tint x, y;\n\tImage_BlockSizeForEncoding(fmt, &blockbytes, &bw, &bh);\n\tASTC_Decode_HDR(in, hdr[0][0], 12, bw, bh);\n\n\tfor (y = 0; y < bh; y++, out += stride)\n\t\tfor (x = 0; x < bw; x++)\n\t\t\tout[x].u = RGB16F_to_E5BGR9(hdr[y][x][0], hdr[y][x][1], hdr[y][x][2]);\n}*/\n#endif\n#endif\n\nstatic void Image_Decode_RGB8_Block(qbyte *fte_restrict in, pixel32_t *fte_restrict out, int w, uploadfmt_t srcfmt)\n{\n\tVector4Set(out->v, in[0], in[1], in[2], 0xff);\n}\nstatic void Image_Decode_L8A8_Block(qbyte *fte_restrict in, pixel32_t *fte_restrict out, int w, uploadfmt_t srcfmt)\n{\n\tVector4Set(out->v, in[0], in[0], in[0], in[1]);\n}\nstatic void Image_Decode_L8_Block(qbyte *fte_restrict in, pixel32_t *fte_restrict out, int w, uploadfmt_t srcfmt)\n{\n\tVector4Set(out->v, in[0], in[0], in[0], 0xff);\n}\n\nvoid Image_BlockSizeForEncoding(uploadfmt_t encoding, unsigned int *blockbytes, unsigned int *blockwidth, unsigned int *blockheight, unsigned int *blockdepth)\n{\n\tunsigned int b = 0, w = 1, h = 1, d = 1;\n\tswitch(encoding)\n\t{\n\tcase PTI_RGB565:\n\tcase PTI_RGBA4444:\n\tcase PTI_ARGB4444:\n\tcase PTI_RGBA5551:\n\tcase PTI_ARGB1555:\n\t\tb = 2;\t//16bit formats\n\t\tbreak;\n\tcase PTI_RGBA8:\n\tcase PTI_RGBX8:\n\tcase PTI_BGRA8:\n\tcase PTI_BGRX8:\n\tcase PTI_RGBA8_SRGB:\n\tcase PTI_RGBX8_SRGB:\n\tcase PTI_BGRA8_SRGB:\n\tcase PTI_BGRX8_SRGB:\n\tcase PTI_A2BGR10:\n\tcase PTI_E5BGR9:\n\tcase PTI_B10G11R11F:\n\t\tb = 4;\n\t\tbreak;\n\n\tcase PTI_R16:\n\tcase PTI_R16F:\n\t\tb = 1*2;\n\t\tbreak;\n\tcase PTI_R32F:\n\t\tb = 1*4;\n\t\tbreak;\n\tcase PTI_RGBA16:\n\tcase PTI_RGBA16F:\n\t\tb = 4*2;\n\t\tbreak;\n\tcase PTI_RGBA32F:\n\t\tb = 4*4;\n\t\tbreak;\n\tcase PTI_RGB32F:\n\t\tb = 3*4;\n\t\tbreak;\n\tcase PTI_P8:\n\tcase PTI_R8:\n\tcase PTI_R8_SNORM:\n\t\tb = 1;\n\t\tbreak;\n\tcase PTI_RG8:\n\tcase PTI_RG8_SNORM:\n\t\tb = 2;\n\t\tbreak;\n\n\tcase PTI_DEPTH16:\n\t\tb = 2;\n\t\tbreak;\n\tcase PTI_DEPTH24:\n\t\tb = 3;\n\t\tbreak;\n\tcase PTI_DEPTH32:\n\t\tb = 4;\n\t\tbreak;\n\tcase PTI_DEPTH24_8:\n\t\tb = 4;\n\t\tbreak;\n\n\tcase PTI_RGB8:\n\tcase PTI_BGR8:\n\tcase PTI_RGB8_SRGB:\n\tcase PTI_BGR8_SRGB:\n\t\tb = 3;\n\t\tbreak;\n\tcase PTI_L8:\n\tcase PTI_L8_SRGB:\n\t\tb = 1;\n\t\tbreak;\n\tcase PTI_L8A8:\n\tcase PTI_L8A8_SRGB:\n\t\tb = 2;\n\t\tbreak;\n\n\tcase PTI_BC1_RGB:\n\tcase PTI_BC1_RGB_SRGB:\n\tcase PTI_BC1_RGBA:\n\tcase PTI_BC1_RGBA_SRGB:\n\tcase PTI_BC4_R:\n\tcase PTI_BC4_R_SNORM:\n\tcase PTI_ETC1_RGB8:\n\tcase PTI_ETC2_RGB8:\n\tcase PTI_ETC2_RGB8_SRGB:\n\tcase PTI_ETC2_RGB8A1:\n\tcase PTI_ETC2_RGB8A1_SRGB:\n\tcase PTI_EAC_R11:\n\tcase PTI_EAC_R11_SNORM:\n\t\tw = h = 4;\n\t\tb = 8;\n\t\tbreak;\n\tcase PTI_BC2_RGBA:\n\tcase PTI_BC2_RGBA_SRGB:\n\tcase PTI_BC3_RGBA:\n\tcase PTI_BC3_RGBA_SRGB:\n\tcase PTI_BC5_RG:\n\tcase PTI_BC5_RG_SNORM:\n\tcase PTI_BC6_RGB_UFLOAT:\n\tcase PTI_BC6_RGB_SFLOAT:\n\tcase PTI_BC7_RGBA:\n\tcase PTI_BC7_RGBA_SRGB:\n\tcase PTI_ETC2_RGB8A8:\n\tcase PTI_ETC2_RGB8A8_SRGB:\n\tcase PTI_EAC_RG11:\n\tcase PTI_EAC_RG11_SNORM:\n\t\tw = h = 4;\n\t\tb = 16;\n\t\tbreak;\n\n// ASTC is crazy with its format subtypes... note that all are potentially rgba, selected on a per-block basis\n\tcase PTI_ASTC_4X4_HDR:\n\tcase PTI_ASTC_4X4_SRGB:\n\tcase PTI_ASTC_4X4_LDR:\t\tw = 4; h = 4; b = 16; break;\n\tcase PTI_ASTC_5X4_HDR:\n\tcase PTI_ASTC_5X4_SRGB:\n\tcase PTI_ASTC_5X4_LDR:\t\tw = 5; h = 4; b = 16; break;\n\tcase PTI_ASTC_5X5_HDR:\n\tcase PTI_ASTC_5X5_SRGB:\n\tcase PTI_ASTC_5X5_LDR:\t\tw = 5; h = 5; b = 16; break;\n\tcase PTI_ASTC_6X5_HDR:\n\tcase PTI_ASTC_6X5_SRGB:\n\tcase PTI_ASTC_6X5_LDR:\t\tw = 6; h = 5; b = 16; break;\n\tcase PTI_ASTC_6X6_HDR:\n\tcase PTI_ASTC_6X6_SRGB:\n\tcase PTI_ASTC_6X6_LDR:\t\tw = 6; h = 6; b = 16; break;\n\tcase PTI_ASTC_8X5_HDR:\n\tcase PTI_ASTC_8X5_SRGB:\n\tcase PTI_ASTC_8X5_LDR:\t\tw = 8; h = 5; b = 16; break;\n\tcase PTI_ASTC_8X6_HDR:\n\tcase PTI_ASTC_8X6_SRGB:\n\tcase PTI_ASTC_8X6_LDR:\t\tw = 8; h = 6; b = 16; break;\n\tcase PTI_ASTC_10X5_HDR:\n\tcase PTI_ASTC_10X5_SRGB:\n\tcase PTI_ASTC_10X5_LDR:\t\tw = 10; h = 5; b = 16; break;\n\tcase PTI_ASTC_10X6_HDR:\n\tcase PTI_ASTC_10X6_SRGB:\n\tcase PTI_ASTC_10X6_LDR:\t\tw = 10; h = 6; b = 16; break;\n\tcase PTI_ASTC_8X8_HDR:\n\tcase PTI_ASTC_8X8_SRGB:\n\tcase PTI_ASTC_8X8_LDR:\t\tw = 8; h = 8; b = 16; break;\n\tcase PTI_ASTC_10X8_HDR:\n\tcase PTI_ASTC_10X8_SRGB:\n\tcase PTI_ASTC_10X8_LDR:\t\tw = 10; h = 8; b = 16; break;\n\tcase PTI_ASTC_10X10_HDR:\n\tcase PTI_ASTC_10X10_SRGB:\n\tcase PTI_ASTC_10X10_LDR:\tw = 10; h = 10; b = 16; break;\n\tcase PTI_ASTC_12X10_HDR:\n\tcase PTI_ASTC_12X10_SRGB:\n\tcase PTI_ASTC_12X10_LDR:\tw = 12; h = 10; b = 16; break;\n\tcase PTI_ASTC_12X12_HDR:\n\tcase PTI_ASTC_12X12_SRGB:\n\tcase PTI_ASTC_12X12_LDR:\tw = 12; h = 12; b = 16; break;\n\n#ifdef ASTC3D\n\tcase PTI_ASTC_3X3X3_HDR:\n\tcase PTI_ASTC_3X3X3_SRGB:\n\tcase PTI_ASTC_3X3X3_LDR:\tw = 3; h = 3; d = 3; b = 16; break;\n\tcase PTI_ASTC_4X3X3_HDR:\n\tcase PTI_ASTC_4X3X3_SRGB:\n\tcase PTI_ASTC_4X3X3_LDR:\tw = 4; h = 3; d = 3; b = 16; break;\n\tcase PTI_ASTC_4X4X3_HDR:\n\tcase PTI_ASTC_4X4X3_SRGB:\n\tcase PTI_ASTC_4X4X3_LDR:\tw = 4; h = 4; d = 3; b = 16; break;\n\tcase PTI_ASTC_4X4X4_HDR:\n\tcase PTI_ASTC_4X4X4_SRGB:\n\tcase PTI_ASTC_4X4X4_LDR:\tw = 4; h = 4; d = 4; b = 16; break;\n\tcase PTI_ASTC_5X4X4_HDR:\n\tcase PTI_ASTC_5X4X4_SRGB:\n\tcase PTI_ASTC_5X4X4_LDR:\tw = 5; h = 4; d = 4; b = 16; break;\n\tcase PTI_ASTC_5X5X4_HDR:\n\tcase PTI_ASTC_5X5X4_SRGB:\n\tcase PTI_ASTC_5X5X4_LDR:\tw = 5; h = 5; d = 4; b = 16; break;\n\tcase PTI_ASTC_5X5X5_HDR:\n\tcase PTI_ASTC_5X5X5_SRGB:\n\tcase PTI_ASTC_5X5X5_LDR:\tw = 5; h = 5; d = 5; b = 16; break;\n\tcase PTI_ASTC_6X5X5_HDR:\n\tcase PTI_ASTC_6X5X5_SRGB:\n\tcase PTI_ASTC_6X5X5_LDR:\tw = 6; h = 5; d = 5; b = 16; break;\n\tcase PTI_ASTC_6X6X5_HDR:\n\tcase PTI_ASTC_6X6X5_SRGB:\n\tcase PTI_ASTC_6X6X5_LDR:\tw = 6; h = 6; d = 5; b = 16; break;\n\tcase PTI_ASTC_6X6X6_HDR:\n\tcase PTI_ASTC_6X6X6_SRGB:\n\tcase PTI_ASTC_6X6X6_LDR:\tw = 6; h = 6; d = 6; b = 16; break;\n#endif\n\n\tcase TF_BGR24_FLIP:\n\tcase TF_SOLID8:\n\tcase TF_TRANS8:\n\tcase TF_TRANS8_FULLBRIGHT:\n\tcase TF_HEIGHT8:\n\tcase TF_HEIGHT8PAL:\n\tcase TF_H2_T7G1:\n\tcase TF_H2_TRANS8_0:\n\tcase TF_H2_T4A4:\n\t\tb=1;\n\t\tbreak;\n\tcase PTI_LLLX8:\n\tcase PTI_LLLA8:\n\t\tb=4;\n\t\tbreak;\n\tcase TF_8PAL24:\n\tcase TF_8PAL32:\n\tcase TF_INVALID:\n\tcase TF_MIP4_P8:\n\tcase TF_MIP4_SOLID8:\n\tcase TF_MIP4_8PAL24:\n\tcase TF_MIP4_8PAL24_T255:\n#ifdef FTE_TARGET_WEB\n\tcase PTI_WHOLEFILE: //UNKNOWN!\n#endif\n\tcase PTI_MAX:\n\t\tbreak;\n\t}\n\n\t*blockbytes = b;\n\t*blockwidth = w;\n\t*blockheight = h;\n\t*blockdepth = d;\n}\n\nqboolean Image_FormatHasAlpha(uploadfmt_t encoding)\n{\n\tswitch(encoding)\n\t{\n\tcase PTI_RGB565:\n\tcase PTI_RGBX8:\n\tcase PTI_BGRX8:\n\tcase PTI_RGBX8_SRGB:\n\tcase PTI_BGRX8_SRGB:\n\tcase PTI_E5BGR9:\n\tcase PTI_B10G11R11F:\n\tcase PTI_R16:\n\tcase PTI_R16F:\n\tcase PTI_R32F:\n\tcase PTI_RGB32F:\n\tcase PTI_P8:\n\tcase PTI_R8:\n\tcase PTI_R8_SNORM:\n\tcase PTI_RG8:\n\tcase PTI_RG8_SNORM:\n\tcase PTI_DEPTH16:\n\tcase PTI_DEPTH24:\n\tcase PTI_DEPTH32:\n\tcase PTI_DEPTH24_8:\n\tcase PTI_RGB8:\n\tcase PTI_BGR8:\n\tcase PTI_RGB8_SRGB:\n\tcase PTI_BGR8_SRGB:\n\tcase PTI_L8:\n\tcase PTI_L8_SRGB:\n\tcase PTI_BC1_RGB:\n\tcase PTI_BC1_RGB_SRGB:\n\tcase PTI_BC4_R:\n\tcase PTI_BC4_R_SNORM:\n\tcase PTI_BC5_RG:\n\tcase PTI_BC5_RG_SNORM:\n\tcase PTI_BC6_RGB_UFLOAT:\n\tcase PTI_BC6_RGB_SFLOAT:\n\tcase PTI_ETC1_RGB8:\n\tcase PTI_ETC2_RGB8:\n\tcase PTI_ETC2_RGB8_SRGB:\n\tcase PTI_EAC_R11:\n\tcase PTI_EAC_R11_SNORM:\n\tcase PTI_EAC_RG11:\n\tcase PTI_EAC_RG11_SNORM:\n\t\treturn false;\n\n\tcase PTI_RGBA4444:\n\tcase PTI_ARGB4444:\n\tcase PTI_RGBA5551:\n\tcase PTI_ARGB1555:\n\tcase PTI_RGBA8:\n\tcase PTI_BGRA8:\n\tcase PTI_RGBA8_SRGB:\n\tcase PTI_BGRA8_SRGB:\n\tcase PTI_A2BGR10:\n\tcase PTI_RGBA16:\n\tcase PTI_RGBA16F:\n\tcase PTI_RGBA32F:\n\tcase PTI_L8A8:\n\tcase PTI_L8A8_SRGB:\n\tcase PTI_BC1_RGBA:\n\tcase PTI_BC1_RGBA_SRGB:\n\tcase PTI_ETC2_RGB8A1:\n\tcase PTI_ETC2_RGB8A1_SRGB:\n\tcase PTI_BC2_RGBA:\n\tcase PTI_BC2_RGBA_SRGB:\n\tcase PTI_BC3_RGBA:\n\tcase PTI_BC3_RGBA_SRGB:\n\tcase PTI_BC7_RGBA:\n\tcase PTI_BC7_RGBA_SRGB:\n\tcase PTI_ETC2_RGB8A8:\n\tcase PTI_ETC2_RGB8A8_SRGB:\n\tcase PTI_ASTC_4X4_HDR:\n\tcase PTI_ASTC_4X4_SRGB:\n\tcase PTI_ASTC_4X4_LDR:\n\tcase PTI_ASTC_5X4_HDR:\n\tcase PTI_ASTC_5X4_SRGB:\n\tcase PTI_ASTC_5X4_LDR:\n\tcase PTI_ASTC_5X5_HDR:\n\tcase PTI_ASTC_5X5_SRGB:\n\tcase PTI_ASTC_5X5_LDR:\n\tcase PTI_ASTC_6X5_HDR:\n\tcase PTI_ASTC_6X5_SRGB:\n\tcase PTI_ASTC_6X5_LDR:\n\tcase PTI_ASTC_6X6_HDR:\n\tcase PTI_ASTC_6X6_SRGB:\n\tcase PTI_ASTC_6X6_LDR:\n\tcase PTI_ASTC_8X5_HDR:\n\tcase PTI_ASTC_8X5_SRGB:\n\tcase PTI_ASTC_8X5_LDR:\n\tcase PTI_ASTC_8X6_HDR:\n\tcase PTI_ASTC_8X6_SRGB:\n\tcase PTI_ASTC_8X6_LDR:\n\tcase PTI_ASTC_10X5_HDR:\n\tcase PTI_ASTC_10X5_SRGB:\n\tcase PTI_ASTC_10X5_LDR:\n\tcase PTI_ASTC_10X6_HDR:\n\tcase PTI_ASTC_10X6_SRGB:\n\tcase PTI_ASTC_10X6_LDR:\n\tcase PTI_ASTC_8X8_HDR:\n\tcase PTI_ASTC_8X8_SRGB:\n\tcase PTI_ASTC_8X8_LDR:\n\tcase PTI_ASTC_10X8_HDR:\n\tcase PTI_ASTC_10X8_SRGB:\n\tcase PTI_ASTC_10X8_LDR:\n\tcase PTI_ASTC_10X10_HDR:\n\tcase PTI_ASTC_10X10_SRGB:\n\tcase PTI_ASTC_10X10_LDR:\n\tcase PTI_ASTC_12X10_HDR:\n\tcase PTI_ASTC_12X10_SRGB:\n\tcase PTI_ASTC_12X10_LDR:\n\tcase PTI_ASTC_12X12_HDR:\n\tcase PTI_ASTC_12X12_SRGB:\n\tcase PTI_ASTC_12X12_LDR:\n#ifdef ASTC3D\n\tcase PTI_ASTC_3X3X3_HDR:\n\tcase PTI_ASTC_3X3X3_SRGB:\n\tcase PTI_ASTC_3X3X3_LDR:\n\tcase PTI_ASTC_4X3X3_HDR:\n\tcase PTI_ASTC_4X3X3_SRGB:\n\tcase PTI_ASTC_4X3X3_LDR:\n\tcase PTI_ASTC_4X4X3_HDR:\n\tcase PTI_ASTC_4X4X3_SRGB:\n\tcase PTI_ASTC_4X4X3_LDR:\n\tcase PTI_ASTC_4X4X4_HDR:\n\tcase PTI_ASTC_4X4X4_SRGB:\n\tcase PTI_ASTC_4X4X4_LDR:\n\tcase PTI_ASTC_5X4X4_HDR:\n\tcase PTI_ASTC_5X4X4_SRGB:\n\tcase PTI_ASTC_5X4X4_LDR:\n\tcase PTI_ASTC_5X5X4_HDR:\n\tcase PTI_ASTC_5X5X4_SRGB:\n\tcase PTI_ASTC_5X5X4_LDR:\n\tcase PTI_ASTC_5X5X5_HDR:\n\tcase PTI_ASTC_5X5X5_SRGB:\n\tcase PTI_ASTC_5X5X5_LDR:\n\tcase PTI_ASTC_6X5X5_HDR:\n\tcase PTI_ASTC_6X5X5_SRGB:\n\tcase PTI_ASTC_6X5X5_LDR:\n\tcase PTI_ASTC_6X6X5_HDR:\n\tcase PTI_ASTC_6X6X5_SRGB:\n\tcase PTI_ASTC_6X6X5_LDR:\n\tcase PTI_ASTC_6X6X6_HDR:\n\tcase PTI_ASTC_6X6X6_SRGB:\n\tcase PTI_ASTC_6X6X6_LDR:\n#endif\n\t\treturn true;\n\n\tcase PTI_EMULATED:\n#ifdef FTE_TARGET_WEB\n\tcase PTI_WHOLEFILE: //UNKNOWN!\n#endif\n\tcase PTI_MAX:\n\t\treturn false;\n\t}\n\treturn false;\n}\n\nconst char *Image_FormatName(uploadfmt_t fmt)\n{\n\tswitch(fmt)\n\t{\n\tcase PTI_RGB565:\t\t\treturn \"RGB565\";\n\tcase PTI_RGBA4444:\t\t\treturn \"RGBA4444\";\n\tcase PTI_ARGB4444:\t\t\treturn \"ARGB4444\";\n\tcase PTI_RGBA5551:\t\t\treturn \"RGBA5551\";\n\tcase PTI_ARGB1555:\t\t\treturn \"ARGB1555\";\n\tcase PTI_RGBA8:\t\t\t\treturn \"RGBA8\";\n\tcase PTI_RGBX8:\t\t\t\treturn \"RGBX8\";\n\tcase PTI_BGRA8:\t\t\t\treturn \"BGRA8\";\n\tcase PTI_BGRX8:\t\t\t\treturn \"BGRX8\";\n\tcase PTI_RGBA8_SRGB:\t\treturn \"RGBA8_SRGB\";\n\tcase PTI_RGBX8_SRGB:\t\treturn \"RGBX8_SRGB\";\n\tcase PTI_BGRA8_SRGB:\t\treturn \"BGRA8_SRGB\";\n\tcase PTI_BGRX8_SRGB:\t\treturn \"BGRX8_SRGB\";\n\tcase PTI_A2BGR10:\t\t\treturn \"A2BGR10\";\n\tcase PTI_E5BGR9:\t\t\treturn \"E5BGR9_UF\";\n\tcase PTI_B10G11R11F:\t\treturn \"B10G11R11_UF\";\n\tcase PTI_R16F:\t\t\t\treturn \"R16_SF\";\n\tcase PTI_R32F:\t\t\t\treturn \"R32_SF\";\n\tcase PTI_RGBA16F:\t\t\treturn \"RGBA16_SF\";\n\tcase PTI_RGBA32F:\t\t\treturn \"RGBA32_SF\";\n\tcase PTI_RGB32F:\t\t\treturn \"RGB32_SF\";\n\tcase PTI_R16:\t\t\t\treturn \"R16\";\n\tcase PTI_RGBA16:\t\t\treturn \"RGBA16\";\n\tcase PTI_P8:\t\t\t\treturn \"P8\";\n\tcase PTI_R8:\t\t\t\treturn \"R8\";\n\tcase PTI_R8_SNORM:\t\t\treturn \"R8_SNORM\";\n\tcase PTI_RG8:\t\t\t\treturn \"RG8\";\n\tcase PTI_RG8_SNORM:\t\t\treturn \"RG8_SNORM\";\n\tcase PTI_DEPTH16:\t\t\treturn \"DEPTH16\";\n\tcase PTI_DEPTH24:\t\t\treturn \"DEPTH24\";\n\tcase PTI_DEPTH32:\t\t\treturn \"DEPTH32\";\n\tcase PTI_DEPTH24_8:\t\t\treturn \"DEPTH24_8\";\n\tcase PTI_RGB8:\t\t\t\treturn \"RGB8\";\n\tcase PTI_BGR8:\t\t\t\treturn \"BGR8\";\n\tcase PTI_RGB8_SRGB:\t\t\treturn \"RGB8_SRGB\";\n\tcase PTI_BGR8_SRGB:\t\t\treturn \"BGR8_SRGB\";\n\tcase PTI_L8:\t\t\t\treturn \"L8\";\n\tcase PTI_L8_SRGB:\t\t\treturn \"L8_SRGB\";\n\tcase PTI_L8A8:\t\t\t\treturn \"L8A8\";\n\tcase PTI_L8A8_SRGB:\t\t\treturn \"L8A8_SRGB\";\n\tcase PTI_BC1_RGB:\t\t\treturn \"BC1_RGB\";\n\tcase PTI_BC1_RGB_SRGB:\t\treturn \"BC1_RGB_SRGB\";\n\tcase PTI_BC1_RGBA:\t\t\treturn \"BC1_RGBA\";\n\tcase PTI_BC1_RGBA_SRGB:\t\treturn \"BC1_RGBA_SRGB\";\n\tcase PTI_BC2_RGBA:\t\t\treturn \"BC2_RGBA\";\n\tcase PTI_BC2_RGBA_SRGB:\t\treturn \"BC2_RGBA_SRGB\";\n\tcase PTI_BC3_RGBA:\t\t\treturn \"BC3_RGBA\";\n\tcase PTI_BC3_RGBA_SRGB:\t\treturn \"BC3_RGBA_SRGB\";\n\tcase PTI_BC4_R:\t\t\t\treturn \"BC4_R\";\n\tcase PTI_BC4_R_SNORM:\t\treturn \"BC4_R_SNORM\";\n\tcase PTI_BC5_RG:\t\t\treturn \"BC5_RG\";\n\tcase PTI_BC5_RG_SNORM:\t\treturn \"BC5_RG_SNORM\";\n\tcase PTI_BC6_RGB_UFLOAT:\treturn \"BC6_RGB_UF\";\n\tcase PTI_BC6_RGB_SFLOAT:\treturn \"BC6_RGB_SF\";\n\tcase PTI_BC7_RGBA:\t\t\treturn \"BC7_RGBA\";\n\tcase PTI_BC7_RGBA_SRGB:\t\treturn \"BC7_RGBA_SRGB\";\n\tcase PTI_ETC1_RGB8:\t\t\treturn \"ETC1_RGB8\";\n\tcase PTI_ETC2_RGB8:\t\t\treturn \"ETC2_RGB8\";\n\tcase PTI_ETC2_RGB8_SRGB:\treturn \"ETC2_RGB8_SRGB\";\n\tcase PTI_ETC2_RGB8A1:\t\treturn \"ETC2_RGB8A1\";\n\tcase PTI_ETC2_RGB8A1_SRGB:\treturn \"ETC2_RGB8A1_SRGB\";\n\tcase PTI_EAC_R11:\t\t\treturn \"EAC_R11\";\n\tcase PTI_EAC_R11_SNORM:\t\treturn \"EAC_R11_SNORM\";\n\tcase PTI_ETC2_RGB8A8:\t\treturn \"ETC2_RGB8A8\";\n\tcase PTI_ETC2_RGB8A8_SRGB:\treturn \"ETC2_RGB8A8_SRGB\";\n\tcase PTI_EAC_RG11:\t\t\treturn \"EAC_RG11\";\n\tcase PTI_EAC_RG11_SNORM:\treturn \"EAC_RG11_SNORM\";\n\tcase PTI_ASTC_4X4_HDR:\t\treturn \"ASTC_4X4_HDR\";\n\tcase PTI_ASTC_4X4_SRGB:\t\treturn \"ASTC_4X4_SRGB\";\n\tcase PTI_ASTC_4X4_LDR:\t\treturn \"ASTC_4X4_LDR\";\n\tcase PTI_ASTC_5X4_HDR:\t\treturn \"ASTC_5X4_HDR\";\n\tcase PTI_ASTC_5X4_SRGB:\t\treturn \"ASTC_5X4_SRGB\";\n\tcase PTI_ASTC_5X4_LDR:\t\treturn \"ASTC_5X4_LDR\";\n\tcase PTI_ASTC_5X5_HDR:\t\treturn \"ASTC_5X5_HDR\";\n\tcase PTI_ASTC_5X5_SRGB:\t\treturn \"ASTC_5X5_SRGB\";\n\tcase PTI_ASTC_5X5_LDR:\t\treturn \"ASTC_5X5_LDR\";\n\tcase PTI_ASTC_6X5_HDR:\t\treturn \"ASTC_6X5_HDR\";\n\tcase PTI_ASTC_6X5_SRGB:\t\treturn \"ASTC_6X5_SRGB\";\n\tcase PTI_ASTC_6X5_LDR:\t\treturn \"ASTC_6X5_LDR\";\n\tcase PTI_ASTC_6X6_HDR:\t\treturn \"ASTC_6X6_HDR\";\n\tcase PTI_ASTC_6X6_SRGB:\t\treturn \"ASTC_6X6_SRGB\";\n\tcase PTI_ASTC_6X6_LDR:\t\treturn \"ASTC_6X6_LDR\";\n\tcase PTI_ASTC_8X5_HDR:\t\treturn \"ASTC_8X5_HDR\";\n\tcase PTI_ASTC_8X5_SRGB:\t\treturn \"ASTC_8X5_SRGB\";\n\tcase PTI_ASTC_8X5_LDR:\t\treturn \"ASTC_8X5_LDR\";\n\tcase PTI_ASTC_8X6_HDR:\t\treturn \"ASTC_8X6_HDR\";\n\tcase PTI_ASTC_8X6_SRGB:\t\treturn \"ASTC_8X6_SRGB\";\n\tcase PTI_ASTC_8X6_LDR:\t\treturn \"ASTC_8X6_LDR\";\n\tcase PTI_ASTC_10X5_HDR:\t\treturn \"ASTC_10X5_HDR\";\n\tcase PTI_ASTC_10X5_SRGB:\treturn \"ASTC_10X5_SRGB\";\n\tcase PTI_ASTC_10X5_LDR:\t\treturn \"ASTC_10X5_LDR\";\n\tcase PTI_ASTC_10X6_HDR:\t\treturn \"ASTC_10X6_HDR\";\n\tcase PTI_ASTC_10X6_SRGB:\treturn \"ASTC_10X6_SRGB\";\n\tcase PTI_ASTC_10X6_LDR:\t\treturn \"ASTC_10X6_LDR\";\n\tcase PTI_ASTC_8X8_HDR:\t\treturn \"ASTC_8X8_HDR\";\n\tcase PTI_ASTC_8X8_SRGB:\t\treturn \"ASTC_8X8_SRGB\";\n\tcase PTI_ASTC_8X8_LDR:\t\treturn \"ASTC_8X8_LDR\";\n\tcase PTI_ASTC_10X8_HDR:\t\treturn \"ASTC_10X8_HDR\";\n\tcase PTI_ASTC_10X8_SRGB:\treturn \"ASTC_10X8_SRGB\";\n\tcase PTI_ASTC_10X8_LDR:\t\treturn \"ASTC_10X8_LDR\";\n\tcase PTI_ASTC_10X10_HDR:\treturn \"ASTC_10X10_HDR\";\n\tcase PTI_ASTC_10X10_SRGB:\treturn \"ASTC_10X10_SRGB\";\n\tcase PTI_ASTC_10X10_LDR:\treturn \"ASTC_10X10_LDR\";\n\tcase PTI_ASTC_12X10_HDR:\treturn \"ASTC_12X10_HDR\";\n\tcase PTI_ASTC_12X10_SRGB:\treturn \"ASTC_12X10_SRGB\";\n\tcase PTI_ASTC_12X10_LDR:\treturn \"ASTC_12X10_LDR\";\n\tcase PTI_ASTC_12X12_HDR:\treturn \"ASTC_12X12_HDR\";\n\tcase PTI_ASTC_12X12_SRGB:\treturn \"ASTC_12X12_SRGB\";\n\tcase PTI_ASTC_12X12_LDR:\treturn \"ASTC_12X12_LDR\";\n#ifdef ASTC3D\n\tcase PTI_ASTC_3X3X3_HDR:\treturn \"ASTC_3X3X3_HDR\";\n\tcase PTI_ASTC_3X3X3_SRGB:\treturn \"ASTC_3X3X3_SRGB\";\n\tcase PTI_ASTC_3X3X3_LDR:\treturn \"ASTC_3X3X3_LDR\";\n\tcase PTI_ASTC_4X3X3_HDR:\treturn \"ASTC_4X3X3_HDR\";\n\tcase PTI_ASTC_4X3X3_SRGB:\treturn \"ASTC_4X3X3_SRGB\";\n\tcase PTI_ASTC_4X3X3_LDR:\treturn \"ASTC_4X3X3_LDR\";\n\tcase PTI_ASTC_4X4X3_HDR:\treturn \"ASTC_4X4X3_HDR\";\n\tcase PTI_ASTC_4X4X3_SRGB:\treturn \"ASTC_4X4X3_SRGB\";\n\tcase PTI_ASTC_4X4X3_LDR:\treturn \"ASTC_4X4X3_LDR\";\n\tcase PTI_ASTC_4X4X4_HDR:\treturn \"ASTC_4X4X4_HDR\";\n\tcase PTI_ASTC_4X4X4_SRGB:\treturn \"ASTC_4X4X4_SRGB\";\n\tcase PTI_ASTC_4X4X4_LDR:\treturn \"ASTC_4X4X4_LDR\";\n\tcase PTI_ASTC_5X4X4_HDR:\treturn \"ASTC_5X4X4_HDR\";\n\tcase PTI_ASTC_5X4X4_SRGB:\treturn \"ASTC_5X4X4_SRGB\";\n\tcase PTI_ASTC_5X4X4_LDR:\treturn \"ASTC_5X4X4_LDR\";\n\tcase PTI_ASTC_5X5X4_HDR:\treturn \"ASTC_5X5X4_HDR\";\n\tcase PTI_ASTC_5X5X4_SRGB:\treturn \"ASTC_5X5X4_SRGB\";\n\tcase PTI_ASTC_5X5X4_LDR:\treturn \"ASTC_5X5X4_LDR\";\n\tcase PTI_ASTC_5X5X5_HDR:\treturn \"ASTC_5X5X5_HDR\";\n\tcase PTI_ASTC_5X5X5_SRGB:\treturn \"ASTC_5X5X5_SRGB\";\n\tcase PTI_ASTC_5X5X5_LDR:\treturn \"ASTC_5X5X5_LDR\";\n\tcase PTI_ASTC_6X5X5_HDR:\treturn \"ASTC_6X5X5_HDR\";\n\tcase PTI_ASTC_6X5X5_SRGB:\treturn \"ASTC_6X5X5_SRGB\";\n\tcase PTI_ASTC_6X5X5_LDR:\treturn \"ASTC_6X5X5_LDR\";\n\tcase PTI_ASTC_6X6X5_HDR:\treturn \"ASTC_6X6X5_HDR\";\n\tcase PTI_ASTC_6X6X5_SRGB:\treturn \"ASTC_6X6X5_SRGB\";\n\tcase PTI_ASTC_6X6X5_LDR:\treturn \"ASTC_6X6X5_LDR\";\n\tcase PTI_ASTC_6X6X6_HDR:\treturn \"ASTC_6X6X6_HDR\";\n\tcase PTI_ASTC_6X6X6_SRGB:\treturn \"ASTC_6X6X6_SRGB\";\n\tcase PTI_ASTC_6X6X6_LDR:\treturn \"ASTC_6X6X6_LDR\";\n#endif\n\n#ifdef FTE_TARGET_WEB\n\tcase PTI_WHOLEFILE:\t\t\treturn \"Whole File\";\n#endif\n\tcase TF_INVALID:\t\t\treturn \"INVALID\";\n\tcase TF_BGR24_FLIP:\t\t\treturn \"BGR24_FLIP\";\n\tcase TF_MIP4_P8:\t\t\treturn \"MIP4_P8\";\n\tcase TF_MIP4_SOLID8:\t\treturn \"MIP4_SOLID8\";\n\tcase TF_MIP4_8PAL24:\t\treturn \"MIP4_8PAL24\";\n\tcase TF_MIP4_8PAL24_T255:\treturn \"MIP4_8PAL24_T255\";\n\tcase TF_SOLID8:\t\t\t\treturn \"SOLID8\";\n\tcase TF_TRANS8:\t\t\t\treturn \"TRANS8_255\";\n\tcase TF_TRANS8_FULLBRIGHT:\treturn \"TRANS8_FULLBRIGHT\";\n\tcase TF_HEIGHT8:\t\t\treturn \"HEIGHT8\";\n\tcase TF_HEIGHT8PAL:\t\t\treturn \"HEIGHT8PAL\";\n\tcase TF_H2_T7G1:\t\t\treturn \"H2_T7G1\";\n\tcase TF_H2_TRANS8_0:\t\treturn \"TRANS8_0\";\n\tcase TF_H2_T4A4:\t\t\treturn \"H2_T4A4\";\n\tcase TF_8PAL24:\t\t\t\treturn \"8PAL24\";\n\tcase TF_8PAL32:\t\t\t\treturn \"8PAL32\";\n\tcase PTI_LLLX8:\t\t\t\treturn \"LLLX8\";\n\tcase PTI_LLLA8:\t\t\t\treturn \"LLLA8\";\n\tcase PTI_MAX:\n\t\tbreak;\n\t}\n\treturn \"Unknown\";\n}\n\nstatic pixel32_t *Image_Block_Decode(qbyte *fte_restrict in, size_t insize, int w, int h, int d, void(*decodeblock)(qbyte *fte_restrict in, pixel32_t *fte_restrict out, int w, uploadfmt_t srcfmt), uploadfmt_t encoding)\n{\n#define TMPBLOCKSIZE 16u\n\tpixel32_t *ret, *out;\n\tpixel32_t tmp[TMPBLOCKSIZE*TMPBLOCKSIZE];\n\tint x, y, z, i, j;\n\tint sizediff;\n\tint rows, columns, layers;\n\n\tunsigned int blockbytes, blockwidth, blockheight, blockdepth;\n\tImage_BlockSizeForEncoding(encoding, &blockbytes, &blockwidth, &blockheight, &blockdepth);\n\n\tif (blockwidth > TMPBLOCKSIZE || blockheight > TMPBLOCKSIZE || blockdepth != 1)\n\t\tSys_Error(\"Image_Block_Decode only supports up to %u*%u blocks.\\n\", TMPBLOCKSIZE,TMPBLOCKSIZE);\n\n\tsizediff = insize - blockbytes*((w+blockwidth-1)/blockwidth)*((h+blockheight-1)/blockheight)*d;\n\tif (sizediff)\n\t{\n\t\tCon_Printf(\"Image_Block_Decode: %s data size is %u, expected %u\\n\\n\", Image_FormatName(encoding), (unsigned int)insize, (unsigned int)(insize-sizediff));\n\t\tif (sizediff < 0)\n\t\t\treturn NULL;\n\t}\n\n\tret = out = BZ_Malloc(w*h*d*sizeof(*out));\n\n\trows = h/blockheight;\n\trows *= blockheight;\n\tcolumns = w/blockwidth;\n\tcolumns *= blockwidth;\n\tlayers = d;\n\tfor (z = 0; z < layers; z++)\n\t{\n\t\tfor (y = 0; y < rows; y+=blockheight, out += w*(blockheight-1))\n\t\t{\n\t\t\tfor (x = 0; x < columns; x+=blockwidth, in+=blockbytes, out+=blockwidth)\n\t\t\t\tdecodeblock(in, out, w, encoding);\n\t\t\tif (w%blockwidth)\n\t\t\t{\n\t\t\t\tdecodeblock(in, tmp, TMPBLOCKSIZE, encoding);\n\t\t\t\tfor (i = 0; x < w; x++, out++, i++)\n\t\t\t\t{\n\t\t\t\t\tfor (j = 0; j < blockheight; j++)\n\t\t\t\t\t\tout[w*j] = tmp[i+TMPBLOCKSIZE*j];\n\t\t\t\t}\n\t\t\t\tin+=blockbytes;\n\t\t\t}\n\t\t}\n\t\tif (h%blockheight)\n\t\t{\t//now walk along the bottom of the image\n\t\t\th %= blockheight;\n\t\t\tfor (x = 0; x < w; )\n\t\t\t{\n\t\t\t\tdecodeblock(in, tmp, TMPBLOCKSIZE, encoding);\n\t\t\t\ti = 0;\n\t\t\t\tdo\n\t\t\t\t{\n\t\t\t\t\tif (x == w)\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tfor (y = 0; y < h; y++)\n\t\t\t\t\t\tout[w*y] = tmp[i+TMPBLOCKSIZE*y];\n\t\t\t\t\tout++;\n\t\t\t\t\ti++;\n\t\t\t\t} while (++x % blockwidth);\n\t\t\t\tin+=blockbytes;\n\t\t\t}\n\t\t}\n\t}\n\treturn ret;\n}\nstatic pixel64_t *Image_Block_Decode64(qbyte *fte_restrict in, size_t insize, int w, int h, int d, void(*decodeblock)(qbyte *fte_restrict in, pixel64_t *fte_restrict out, int w, uploadfmt_t srcfmt), uploadfmt_t encoding)\n{\n#define TMPBLOCKSIZE 16u\n\tpixel64_t *ret, *out;\n\tpixel64_t tmp[TMPBLOCKSIZE*TMPBLOCKSIZE];\n\tint x, y, z, i, j;\n\tint sizediff;\n\tint rows, columns, layers;\n\n\tunsigned int blockbytes, blockwidth, blockheight, blockdepth;\n\tImage_BlockSizeForEncoding(encoding, &blockbytes, &blockwidth, &blockheight, &blockdepth);\n\n\tif (blockwidth > TMPBLOCKSIZE || blockheight > TMPBLOCKSIZE || blockdepth != 1)\n\t\tSys_Error(\"Image_Block_Decode only supports up to %u*%u*%u blocks.\\n\", TMPBLOCKSIZE,TMPBLOCKSIZE,1);\n\n\tsizediff = insize - blockbytes*((w+blockwidth-1)/blockwidth)*((h+blockheight-1)/blockheight)*((d+blockdepth-1)/blockdepth);\n\tif (sizediff)\n\t{\n\t\tCon_Printf(\"Image_Block_Decode: %s data size is %u, expected %u\\n\\n\", Image_FormatName(encoding), (unsigned int)insize, (unsigned int)(insize-sizediff));\n\t\tif (sizediff < 0)\n\t\t\treturn NULL;\n\t}\n\n\tret = out = BZ_Malloc(w*h*d*sizeof(*out));\n\n\trows = h/blockheight;\n\trows *= blockheight;\n\tcolumns = w/blockwidth;\n\tcolumns *= blockwidth;\n\tlayers = d/blockdepth;\n\tlayers *= blockdepth;\n\tfor (z = 0; z < layers; z+=blockdepth)\n\t{\n\t\tfor (y = 0; y < rows; y+=blockheight, out += w*(blockheight-1))\n\t\t{\n\t\t\tfor (x = 0; x < columns; x+=blockwidth, in+=blockbytes, out+=blockwidth)\n\t\t\t\tdecodeblock(in, out, w, encoding);\n\t\t\tif (w%blockwidth)\n\t\t\t{\n\t\t\t\tdecodeblock(in, tmp, TMPBLOCKSIZE, encoding);\n\t\t\t\tfor (i = 0; x < w; x++, out++, i++)\n\t\t\t\t{\n\t\t\t\t\tfor (j = 0; j < blockheight; j++)\n\t\t\t\t\t\tout[w*j] = tmp[i+TMPBLOCKSIZE*j];\n\t\t\t\t}\n\t\t\t\tin+=blockbytes;\n\t\t\t}\n\t\t}\n\t\tif (h%blockheight)\n\t\t{\t//now walk along the bottom of the image\n\t\t\th %= blockheight;\n\t\t\tfor (x = 0; x < w; )\n\t\t\t{\n\t\t\t\tdecodeblock(in, tmp, TMPBLOCKSIZE, encoding);\n\t\t\t\ti = 0;\n\t\t\t\tdo\n\t\t\t\t{\n\t\t\t\t\tif (x == w)\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tfor (y = 0; y < h; y++)\n\t\t\t\t\t\tout[w*y] = tmp[i+TMPBLOCKSIZE*y];\n\t\t\t\t\tout++;\n\t\t\t\t\ti++;\n\t\t\t\t} while (++x % blockwidth);\n\t\t\t\tin+=blockbytes;\n\t\t\t}\n\t\t}\n\t}\n\treturn ret;\n}\n\nstatic qboolean Image_DecompressFormat(struct pendingtextureinfo *mips, const char *imagename)\n{\n\t//various compressed formats might not be supported by various gpus/apis.\n\t//sometimes the gpu might only partially support the format (eg: d3d requires mip 0 be a multiple of the block size)\n\t//and sometimes we want the actual rgb data (eg: so that we can palettize it)\n\t//so this is still useful even if every driver ever created supported the format.\n\t//as a general rule, decompressing is fairly straight forward, but not free. yay threads.\n\n\t//iiuc any basic s3tc patents have now expired, so it is legally safe to decode (though fancy compression logic may still have restrictions, but we don't compress).\n\tstatic float throttle;\n\tvoid (*decodefunc)(qbyte *fte_restrict, pixel32_t *fte_restrict, int, uploadfmt_t) = NULL;\n\tvoid (*decodefunc64)(qbyte *fte_restrict, pixel64_t *fte_restrict, int, uploadfmt_t) = NULL;\n\tint rcoding = mips->encoding;\n\tint mip;\n\tswitch(mips->encoding)\n\t{\n\tdefault:\n\t\tbreak;\n\tcase PTI_RGB8:\n\t\tdecodefunc = Image_Decode_RGB8_Block;\n\t\trcoding = PTI_RGBX8;\n\t\tbreak;\n\tcase PTI_L8A8:\n\t\tdecodefunc = Image_Decode_L8A8_Block;\n\t\trcoding = PTI_RGBA8;\n\t\tbreak;\n\tcase PTI_L8:\n\t\tdecodefunc = Image_Decode_L8_Block;\n\t\trcoding = PTI_RGBA8;\n\t\tbreak;\n#ifdef DECOMPRESS_ETC2\n\tcase PTI_ETC1_RGB8:\n\tcase PTI_ETC2_RGB8: //backwards compatible, so we just treat them the same\n\tcase PTI_ETC2_RGB8_SRGB:\n\t\tdecodefunc = Image_Decode_ETC2_RGB8_Block;\n\t\trcoding = (mips->encoding==PTI_ETC2_RGB8_SRGB)?PTI_RGBX8_SRGB:PTI_RGBX8;\n\t\tbreak;\n\tcase PTI_ETC2_RGB8A1:\t//weird hack mode\n\tcase PTI_ETC2_RGB8A1_SRGB:\n\t\tdecodefunc = Image_Decode_ETC2_RGB8A1_Block;\n\t\trcoding = (mips->encoding==PTI_ETC2_RGB8A1_SRGB)?PTI_RGBA8_SRGB:PTI_RGBA8;\n\t\tbreak;\n\tcase PTI_ETC2_RGB8A8:\n\tcase PTI_ETC2_RGB8A8_SRGB:\n\t\tdecodefunc = Image_Decode_ETC2_RGB8A8_Block;\n\t\trcoding = (mips->encoding==PTI_ETC2_RGB8A8_SRGB)?PTI_RGBA8_SRGB:PTI_RGBA8;\n\t\tbreak;\n\tcase PTI_EAC_R11:\n\t\tdecodefunc = Image_Decode_EAC_R11U_Block;\n\t\trcoding = PTI_RGBX8;\n\t\tbreak;\n/*\tcase PTI_EAC_R11_SNORM:\n\t\tdecodefunc = Image_Decode_EAC_R11S_Block;\n\t\trcoding = PTI_RGBX8;\n\t\tbreak;*/\n\tcase PTI_EAC_RG11:\n\t\tdecodefunc = Image_Decode_EAC_RG11U_Block;\n\t\trcoding = PTI_RGBX8;\n\t\tbreak;\n/*\tcase PTI_EAC_RG11_SNORM:\n\t\tdecodefunc = Image_Decode_EAC_RG11S_Block;\n\t\trcoding = PTI_RGBX8;\n\t\tbreak;*/\n#else\n\tcase PTI_ETC1_RGB8:\n\tcase PTI_ETC2_RGB8:\n\tcase PTI_ETC2_RGB8_SRGB:\n\tcase PTI_ETC2_RGB8A1:\n\tcase PTI_ETC2_RGB8A1_SRGB:\n\tcase PTI_ETC2_RGB8A8:\n\tcase PTI_ETC2_RGB8A8_SRGB:\n\tcase PTI_EAC_R11:\n\tcase PTI_EAC_R11_SNORM:\n\tcase PTI_EAC_RG11:\n\tcase PTI_EAC_RG11_SNORM:\n\t\tCon_ThrottlePrintf(&throttle, 0, \"ETC1/ETC2/EAC decompression is not supported in this build\\n\");\n\t\tbreak;\n#endif\n\n\tcase PTI_BC1_RGB:\n\tcase PTI_BC1_RGB_SRGB:\n#ifdef DECOMPRESS_S3TC\n\t\tdecodefunc = Image_Decode_BC1_Block;\n\t\trcoding = (mips->encoding==PTI_BC1_RGB_SRGB)?PTI_RGBX8_SRGB:PTI_RGBX8;\n#else\n\t\tCon_ThrottlePrintf(&throttle, 0, \"BC1 decompression is not supported in this build\\n\");\n#endif\n\t\tbreak;\n\tcase PTI_BC1_RGBA:\n\tcase PTI_BC1_RGBA_SRGB:\n#ifdef DECOMPRESS_S3TC\n\t\tdecodefunc = Image_Decode_BC1A_Block;\n\t\trcoding = (mips->encoding==PTI_BC1_RGBA_SRGB)?PTI_RGBA8_SRGB:PTI_RGBA8;\n#else\n\t\tCon_ThrottlePrintf(&throttle, 0, \"BC1A decompression is not supported in this build\\n\");\n#endif\n\t\tbreak;\n\tcase PTI_BC2_RGBA:\n\tcase PTI_BC2_RGBA_SRGB:\n#ifdef DECOMPRESS_S3TC\n\t\tdecodefunc = Image_Decode_BC2_Block;\n\t\trcoding = (mips->encoding==PTI_BC2_RGBA_SRGB)?PTI_RGBA8_SRGB:PTI_RGBA8;\n#else\n\t\tCon_ThrottlePrintf(&throttle, 0, \"BC2 decompression is not supported in this build\\n\");\n#endif\n\t\tbreak;\n\tcase PTI_BC3_RGBA:\n\tcase PTI_BC3_RGBA_SRGB:\n#if defined(DECOMPRESS_RGTC) && defined(DECOMPRESS_S3TC)\n\t\tdecodefunc = Image_Decode_BC3_Block;\n\t\trcoding = (mips->encoding==PTI_BC3_RGBA_SRGB)?PTI_RGBA8_SRGB:PTI_RGBA8;\n#else\n\t\tCon_ThrottlePrintf(&throttle, 0, \"Fallback BC3 decompression is not supported in this build\\n\");\n#endif\n\t\tbreak;\n#ifdef DECOMPRESS_RGTC\n\tcase PTI_BC4_R_SNORM:\n\tcase PTI_BC4_R:\n\t\tdecodefunc = Image_Decode_BC4_Block;\n\t\trcoding = PTI_RGBX8;\n\t\tbreak;\n\tcase PTI_BC5_RG_SNORM:\n\tcase PTI_BC5_RG:\n\t\tdecodefunc = Image_Decode_BC5_Block;\n\t\trcoding = PTI_RGBX8;\n\t\tbreak;\n#else\n\tcase PTI_BC4_R_SNORM:\n\tcase PTI_BC4_R:\n\tcase PTI_BC5_RG_SNORM:\n\tcase PTI_BC5_RG:\n\t\tCon_ThrottlePrintf(&throttle, 0, \"Fallback BC4/BC5 decompression is not supported in this build\\n\");\n\t\tbreak;\n#endif\n\tcase PTI_BC6_RGB_UFLOAT:\n#ifdef DECOMPRESS_BPTC\n\t\tdecodefunc64 = Image_Decode_BC6_Block;\n\t\trcoding = PTI_RGBA16F;\n#else\n\t\tCon_ThrottlePrintf(&throttle, 0, \"Fallback BC6_UFLOAT decompression is not supported\\n\");\n#endif\n\t\tbreak;\n\tcase PTI_BC6_RGB_SFLOAT:\n#ifdef DECOMPRESS_BPTC\n\t\tdecodefunc64 = Image_Decode_BC6_Block;\n\t\trcoding = PTI_RGBA16F;\n#else\n\t\tCon_ThrottlePrintf(&throttle, 0, \"Fallback BC6_SFLOAT decompression is not supported\\n\");\n#endif\n\t\tbreak;\n\tcase PTI_BC7_RGBA:\n\tcase PTI_BC7_RGBA_SRGB:\n#ifdef DECOMPRESS_BPTC\n\t\tdecodefunc = Image_Decode_BC7_Block;\n\t\trcoding = (mips->encoding==PTI_BC7_RGBA_SRGB)?PTI_RGBA8_SRGB:PTI_RGBA8;\n#else\n\t\tCon_ThrottlePrintf(&throttle, 0, \"Fallback BC7 decompression is not supported\\n\");\n#endif\n\t\tbreak;\n\n\tcase PTI_ASTC_4X4_HDR:\n\tcase PTI_ASTC_5X4_HDR:\n\tcase PTI_ASTC_5X5_HDR:\n\tcase PTI_ASTC_6X5_HDR:\n\tcase PTI_ASTC_6X6_HDR:\n\tcase PTI_ASTC_8X5_HDR:\n\tcase PTI_ASTC_8X6_HDR:\n\tcase PTI_ASTC_10X5_HDR:\n\tcase PTI_ASTC_10X6_HDR:\n\tcase PTI_ASTC_8X8_HDR:\n\tcase PTI_ASTC_10X8_HDR:\n\tcase PTI_ASTC_10X10_HDR:\n\tcase PTI_ASTC_12X10_HDR:\n\tcase PTI_ASTC_12X12_HDR:\n#if defined(DECOMPRESS_ASTC) && defined(ASTC_WITH_HDR)\n//\t\tdecodefunc = Image_Decode_ASTC_HDR_E5_Block;\n//\t\trcoding = PTI_E5BGR9;\n\n\t\tdecodefunc64 = Image_Decode_ASTC_HDR_HF_Block;\n\t\trcoding = PTI_RGBA16F;\n\t\tbreak;\n#endif\n\tcase PTI_ASTC_4X4_LDR:\n\tcase PTI_ASTC_5X4_LDR:\n\tcase PTI_ASTC_5X5_LDR:\n\tcase PTI_ASTC_6X5_LDR:\n\tcase PTI_ASTC_6X6_LDR:\n\tcase PTI_ASTC_8X5_LDR:\n\tcase PTI_ASTC_8X6_LDR:\n\tcase PTI_ASTC_10X5_LDR:\n\tcase PTI_ASTC_10X6_LDR:\n\tcase PTI_ASTC_8X8_LDR:\n\tcase PTI_ASTC_10X8_LDR:\n\tcase PTI_ASTC_10X10_LDR:\n\tcase PTI_ASTC_12X10_LDR:\n\tcase PTI_ASTC_12X12_LDR:\n#ifdef DECOMPRESS_ASTC\n#ifdef ASTC_WITH_LDR\n\t\tdecodefunc = Image_Decode_ASTC_LDR_U8_Block;\n\t\trcoding = PTI_RGBA8;\n#else\n\t\tdecodefunc64 = Image_Decode_ASTC_HDR_HF_Block;\n\t\trcoding = PTI_RGBA16F;\n#endif\n\t\tbreak;\n#endif\n\tcase PTI_ASTC_4X4_SRGB:\n\tcase PTI_ASTC_5X4_SRGB:\n\tcase PTI_ASTC_5X5_SRGB:\n\tcase PTI_ASTC_6X5_SRGB:\n\tcase PTI_ASTC_6X6_SRGB:\n\tcase PTI_ASTC_8X5_SRGB:\n\tcase PTI_ASTC_8X6_SRGB:\n\tcase PTI_ASTC_10X5_SRGB:\n\tcase PTI_ASTC_10X6_SRGB:\n\tcase PTI_ASTC_8X8_SRGB:\n\tcase PTI_ASTC_10X8_SRGB:\n\tcase PTI_ASTC_10X10_SRGB:\n\tcase PTI_ASTC_12X10_SRGB:\n\tcase PTI_ASTC_12X12_SRGB:\n#if defined(DECOMPRESS_ASTC) && defined(ASTC_WITH_LDR)\n\t\tdecodefunc = Image_Decode_ASTC_LDR_U8_Block;\n\t\trcoding = PTI_RGBA8_SRGB;\n#else\n\t\tCon_ThrottlePrintf(&throttle, 0, \"Fallback ASTC decompression is not supported\\n\");\n#endif\n\t\tbreak;\n\tcase PTI_INVALID:\n\t\tCon_ThrottlePrintf(&throttle, 0, \"Attempting to decompress invalid format\\n\");\n\t\tbreak;\n\t}\n\tif (decodefunc || decodefunc64)\n\t{\n#ifndef IMGTOOL\n\t\tif (imagename)\n\t\t\tCon_DPrintf(\"Software-decoding %s (%s)\\r\", imagename, Image_FormatName(mips->encoding));\n#endif\n\t\tfor (mip = 0; mip < mips->mipcount; mip++)\n\t\t{\n\t\t\tsize_t sz;\n\t\t\tvoid *out;\n\t\t\tif (decodefunc64)\n\t\t\t{\n\t\t\t\tsz = sizeof(pixel64_t);\n\t\t\t\tout = Image_Block_Decode64(mips->mip[mip].data, mips->mip[mip].datasize, mips->mip[mip].width, mips->mip[mip].height, mips->mip[mip].depth, decodefunc64, mips->encoding);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tsz = sizeof(pixel32_t);\n\t\t\t\tout = Image_Block_Decode(mips->mip[mip].data, mips->mip[mip].datasize, mips->mip[mip].width, mips->mip[mip].height, mips->mip[mip].depth, decodefunc, mips->encoding);\n\t\t\t}\n\t\t\tif (mips->mip[mip].needfree)\n\t\t\t\tBZ_Free(mips->mip[mip].data);\n\t\t\tmips->mip[mip].data = out;\n\t\t\tmips->mip[mip].needfree = true;\n\t\t\tmips->mip[mip].datasize = mips->mip[mip].width*mips->mip[mip].height*sz;\n\t\t}\n\t\tif (mips->extrafree)\n\t\t\tBZ_Free(mips->extrafree);\t//might as well free this now, as nothing is poking it any more.\n\t\tmips->extrafree = NULL;\n\t\tmips->encoding = rcoding;\n\t\treturn true;\n\t}\n\treturn false;\n}\n\nstatic struct\n{\n\tuploadfmt_t src;\n\tuploadfmt_t dest;\n\tvoid (*dotransform) (struct pendingtextureinfo *mips, int arg);\n\tint arg;\n\tqboolean onebitalpha;\n} formattransforms[] =\n{\t//more preferable ones should be first.\n\t//lossy transforms will hopefully not be automatically used, but are relevant for the imgtool.\n\t{PTI_LLLX8,\t\tPTI_RGBX8,\t\tImage_Tr_NoTransform},\n\t{PTI_LLLA8,\t\tPTI_RGBA8,\t\tImage_Tr_NoTransform},\n\t{PTI_LLLX8,\t\tPTI_BGRX8,\t\tImage_Tr_NoTransform},\n\t{PTI_LLLA8,\t\tPTI_BGRA8,\t\tImage_Tr_NoTransform},\n\t{PTI_RGBA8,\t\tPTI_BGRA8,\t\tImage_Tr_Swap8888},\n\t{PTI_BGRA8,\t\tPTI_RGBA8,\t\tImage_Tr_Swap8888},\n\t{PTI_RGBX8,\t\tPTI_BGRX8,\t\tImage_Tr_Swap8888},\n\t{PTI_BGRX8,\t\tPTI_RGBX8,\t\tImage_Tr_Swap8888},\n\n\t{PTI_RGBA16,\tPTI_RGBA8,\t\tImage_Tr_4X16to8888},\n\n\t//float transforms\n\t{PTI_RGB32F,\tPTI_RGBA32F,\tImage_Tr_RGB32FToFloat},\n\t{PTI_RGBA32F,\tPTI_RGBA16F,\tImage_Tr_FloatToHalf,\t4},\n\t{PTI_R32F,\t\tPTI_R16F,\t\tImage_Tr_FloatToHalf,\t1},\n\t{PTI_RGBA16F,\tPTI_RGBA32F,\tImage_Tr_HalfToFloat,\t4},\n\t{PTI_R16F,\t\tPTI_R32F,\t\tImage_Tr_HalfToFloat,\t1},\n\t{PTI_RGBA16F,\tPTI_BGRA8,\t\tImage_Tr_HalfToByte,\t-4},\n\t{PTI_RGBA16F,\tPTI_RGBA8,\t\tImage_Tr_HalfToByte,\t4},\n\t{PTI_R16F,\t\tPTI_R8,\t\t\tImage_Tr_HalfToByte,\t1},\n\t{PTI_RGBA32F,\tPTI_BGRA8,\t\tImage_Tr_FloatToByte,\t-4},\n\t{PTI_RGBA32F,\tPTI_RGBA8,\t\tImage_Tr_FloatToByte,\t4},\n\t{PTI_R32F,\t\tPTI_R8,\t\t\tImage_Tr_FloatToByte,\t1},\n\t{PTI_E5BGR9,\tPTI_RGBX8,\t\tImage_Tr_E5BGR9ToByte, false},\n\t{PTI_E5BGR9,\tPTI_BGRX8,\t\tImage_Tr_E5BGR9ToByte, true},\n\t{PTI_E5BGR9,\tPTI_RGBA32F,\tImage_Tr_E5BGR9ToFloat},\n\t{PTI_RGBA32F,\tPTI_E5BGR9,\t\tImage_Tr_FloatToE5BGR9},\n\t{PTI_B10G11R11F,PTI_RGBA32F,\tImage_Tr_PackedToFloat},\n\t{PTI_RGBA32F,\tPTI_B10G11R11F,\tImage_Tr_FloatToPacked},\n\n\t{PTI_LLLA8,\t\tPTI_RGBA5551,\tImage_Tr_8888to5551,\tfalse,\ttrue},\n\t{PTI_RGBA8,\t\tPTI_RGBA5551,\tImage_Tr_8888to5551,\tfalse,\ttrue},\n\t{PTI_BGRA8,\t\tPTI_RGBA5551,\tImage_Tr_8888to5551,\ttrue,\ttrue},\n\t{PTI_LLLA8,\t\tPTI_ARGB1555,\tImage_Tr_8888to1555,\tfalse,\ttrue},\n\t{PTI_RGBA8,\t\tPTI_ARGB1555,\tImage_Tr_8888to1555,\tfalse,\ttrue},\n\t{PTI_BGRA8,\t\tPTI_ARGB1555,\tImage_Tr_8888to1555,\ttrue,\ttrue},\n\t{PTI_LLLA8,\t\tPTI_RGBA4444,\tImage_Tr_8888to4444,\tfalse},\n\t{PTI_RGBA8,\t\tPTI_RGBA4444,\tImage_Tr_8888to4444,\tfalse},\n\t{PTI_BGRA8,\t\tPTI_RGBA4444,\tImage_Tr_8888to4444,\ttrue},\n\t{PTI_LLLA8,\t\tPTI_ARGB4444,\tImage_Tr_8888toARGB4444,\tfalse},\n\t{PTI_RGBA8,\t\tPTI_ARGB4444,\tImage_Tr_8888toARGB4444,\tfalse},\n\t{PTI_BGRA8,\t\tPTI_ARGB4444,\tImage_Tr_8888toARGB4444,\ttrue},\n\n\t{PTI_LLLX8,\t\tPTI_RGB565,\t\tImage_Tr_8888to565,\tfalse,\ttrue},\n\t{PTI_RGBX8,\t\tPTI_RGB565,\t\tImage_Tr_8888to565,\tfalse,\ttrue},\n\t{PTI_BGRX8,\t\tPTI_RGB565,\t\tImage_Tr_8888to565,\ttrue,\ttrue},\n\t{PTI_LLLX8,\t\tPTI_RGB565,\t\tImage_Tr_8888to565,\tfalse,\ttrue},\n\t{PTI_RGBX8,\t\tPTI_RGB565,\t\tImage_Tr_8888to565,\tfalse,\ttrue},\n\t{PTI_BGRX8,\t\tPTI_RGB565,\t\tImage_Tr_8888to565,\ttrue,\ttrue},\n\n\t{PTI_RGBA5551,\tPTI_RGBA8,\t\tImage_Tr_RGBA5551to8888,\tfalse},\n\t{PTI_ARGB1555,\tPTI_RGBA8,\t\tImage_Tr_ARGB1555to8888,\tfalse},\n\t{PTI_RGBA4444,\tPTI_RGBA8,\t\tImage_Tr_4444to8888,\t\tfalse},\n\t{PTI_ARGB4444,\tPTI_RGBA8,\t\tImage_Tr_ARGB4444to8888,\tfalse},\n\n\t{PTI_A2BGR10,\tPTI_RGBA8,\t\tImage_Tr_10To8},\n\t{PTI_RGBA8,\t\tPTI_A2BGR10,\tImage_Tr_8To10,\t\tfalse,\ttrue},\n\t{PTI_RGBA8,\t\tPTI_RGBA32F,\tImage_Tr_ByteToFloat,\t4},\n\n\t//24bit formats are probably slow\n\t{PTI_LLLX8,\t\tPTI_RGB8,\t\tImage_Tr_DropBytes, (4<<16)|3},\n\t{PTI_RGBX8,\t\tPTI_RGB8,\t\tImage_Tr_DropBytes, (4<<16)|3},\n\t{PTI_BGRX8,\t\tPTI_BGR8,\t\tImage_Tr_DropBytes, (4<<16)|3},\n\t//these are last-resort. and will result in the alpha channel getting lost.\n\t{PTI_LLLA8,\t\tPTI_RGB8,\t\tImage_Tr_DropBytes, (4<<16)|3},\n\t{PTI_RGBA8,\t\tPTI_RGB8,\t\tImage_Tr_DropBytes, (4<<16)|3},\n\t{PTI_BGRA8,\t\tPTI_BGR8,\t\tImage_Tr_DropBytes, (4<<16)|3},\n\t{PTI_LLLA8,\t\tPTI_RGBX8,\t\tImage_Tr_NoTransform},\n\t{PTI_RGBA8,\t\tPTI_RGBX8,\t\tImage_Tr_NoTransform},\n\t{PTI_BGRA8,\t\tPTI_BGRX8,\t\tImage_Tr_NoTransform},\n\t{PTI_LLLA8,\t\tPTI_RGB565,\t\tImage_Tr_8888to565,\tfalse,\ttrue},\n\t{PTI_RGBA8,\t\tPTI_RGB565,\t\tImage_Tr_8888to565,\tfalse,\ttrue},\n\t{PTI_BGRA8,\t\tPTI_RGB565,\t\tImage_Tr_8888to565,\ttrue,\ttrue},\n\t{PTI_LLLA8,\t\tPTI_RGB565,\t\tImage_Tr_8888to565,\tfalse,\ttrue},\n\t{PTI_RGBA8,\t\tPTI_RGB565,\t\tImage_Tr_8888to565,\tfalse,\ttrue},\n\t{PTI_BGRA8,\t\tPTI_RGB565,\t\tImage_Tr_8888to565,\ttrue,\ttrue},\n\t{PTI_RGBX8,\t\tPTI_L8,\t\t\tImage_Tr_8888toLuminence,\t\t1,\ttrue},\n\t{PTI_RGBA8,\t\tPTI_L8A8,\t\tImage_Tr_8888toLuminence,\t\t2,\ttrue},\n\n\t//FIXME: these don't pad alphas properly.\n\t{PTI_RGBX8,\t\tPTI_RGBA8,\t\tImage_Tr_NoTransform},\n\t{PTI_BGRX8,\t\tPTI_BGRA8,\t\tImage_Tr_NoTransform},\n\t{PTI_LLLX8,\t\tPTI_RGBA5551,\tImage_Tr_8888to5551,\tfalse,\ttrue},\n\t{PTI_RGBX8,\t\tPTI_RGBA5551,\tImage_Tr_8888to5551,\tfalse,\ttrue},\n\t{PTI_BGRX8,\t\tPTI_RGBA5551,\tImage_Tr_8888to5551,\ttrue,\ttrue},\n\t{PTI_LLLX8,\t\tPTI_ARGB1555,\tImage_Tr_8888to1555,\tfalse,\ttrue},\n\t{PTI_RGBX8,\t\tPTI_ARGB1555,\tImage_Tr_8888to1555,\tfalse,\ttrue},\n\t{PTI_BGRX8,\t\tPTI_ARGB1555,\tImage_Tr_8888to1555,\ttrue,\ttrue},\n\t{PTI_LLLX8,\t\tPTI_RGBA4444,\tImage_Tr_8888to4444,\tfalse},\n\t{PTI_RGBX8,\t\tPTI_RGBA4444,\tImage_Tr_8888to4444,\tfalse},\n\t{PTI_BGRX8,\t\tPTI_RGBA4444,\tImage_Tr_8888to4444,\ttrue},\n\t{PTI_LLLX8,\t\tPTI_ARGB4444,\tImage_Tr_8888toARGB4444,\tfalse},\n\t{PTI_RGBX8,\t\tPTI_ARGB4444,\tImage_Tr_8888toARGB4444,\tfalse},\n\t{PTI_BGRX8,\t\tPTI_ARGB4444,\tImage_Tr_8888toARGB4444,\ttrue},\n\n\t{PTI_RGBX8,\t\tPTI_R8,\t\t\tImage_Tr_DropBytes, (4<<16)|1, true},\n\t{PTI_RGBX8,\t\tPTI_RG8,\t\tImage_Tr_DropBytes, (4<<16)|2, true},\t//for small normalmaps (b can be inferred, a isn't available so avoid offsetmapping)\n\t{PTI_RGBA16,\tPTI_R16,\t\tImage_Tr_DropBytes, (8<<16)|2, true},\n\t{PTI_RGBA16F,\tPTI_R16F,\t\tImage_Tr_DropBytes, (8<<16)|2, true},\n\t{PTI_RGBA32F,\tPTI_R32F,\t\tImage_Tr_DropBytes, (16<<16)|4, true},\n\t{PTI_RGBA32F,\tPTI_RGB32F,\t\tImage_Tr_DropBytes, (16<<16)|12, true},\n\n\t{PTI_RG8,\t\tPTI_RGBX8,\t\tImage_Tr_RG8ToRGXX8},\n\t{PTI_RGBX8,\t\tPTI_P8,\t\t\tImage_Tr_RGBX8toPaletted, 0|(256<<16)},\n\t{PTI_RGBX8,\t\tTF_H2_TRANS8_0,\tImage_Tr_RGBX8toPaletted, 1|(256<<16)},\n\t{PTI_RGBA8,\t\tTF_H2_TRANS8_0,\tImage_Tr_RGBA8toPaletted, 1|(256<<16)},\n\t{PTI_RGBX8,\t\tTF_TRANS8,\t\tImage_Tr_RGBX8toPaletted, 0|(255<<16)},\n\t{PTI_RGBA8,\t\tTF_TRANS8,\t\tImage_Tr_RGBA8toPaletted, 0|(255<<16)},\n\t{PTI_P8,\t\tPTI_RGBX8,\t\tImage_Tr_PalettedtoRGBX8, -1},\n\t{TF_SOLID8,\t\tPTI_RGBX8,\t\tImage_Tr_PalettedtoRGBX8, -1},\n\t{TF_H2_TRANS8_0,PTI_RGBA8,\t\tImage_Tr_PalettedtoRGBX8, 0},\n\t{TF_TRANS8,\t\tPTI_RGBA8,\t\tImage_Tr_PalettedtoRGBX8, 255},\n};\nvoid Image_ChangeFormat(struct pendingtextureinfo *mips, qboolean *allowedformats, uploadfmt_t origfmt, const char *imagename)\n{\n\tif (!allowedformats)\n\t\tallowedformats = sh_config.texfmt;\n\n\t//if that format isn't supported/desired, try converting it.\n\tif (allowedformats[mips->encoding])\n\t{\n\t\tif (mips->encoding >= PTI_ASTC_FIRST && mips->encoding <= PTI_ASTC_LAST)\n\t\t\treturn;\t//ignore texture_allow_block_padding for astc.\n\t\telse if (!sh_config.texture_allow_block_padding && mips->mipcount && mips->encoding)\n\t\t{\t//direct3d is annoying, and will reject any block-compressed format with a base mip size that is not a multiple of the block size.\n\t\t\t//its fine with weirdly sized mips though. I have no idea why there's this restriction, but whatever.\n\t\t\t//we need to manually decompress in order to correctly handle such images\n\t\t\tint blockbytes, blockwidth, blockheight, blockdepth;\n\t\t\tImage_BlockSizeForEncoding(mips->encoding, &blockbytes, &blockwidth, &blockheight, &blockdepth);\n\t\t\tif (!(mips->mip[0].width % blockwidth) && !(mips->mip[0].height % blockheight) && !(mips->mip[0].depth % blockdepth))\n\t\t\t\treturn;\n\t\t\t//else encoding isn't supported for this size. fall through.\n\t\t}\n\t\telse\n\t\t\treturn;\n\t}\n\n\t//when the format can't be used, decompress it if its one of those awkward compressed formats.\n\tImage_DecompressFormat(mips, imagename);\n\tif (allowedformats[mips->encoding])\n\t\treturn;\t//okay, that got it.\n\n\n\t{\n\t\tqboolean onebitokay = (origfmt == TF_TRANS8 || origfmt == TF_TRANS8_FULLBRIGHT || origfmt == TF_H2_TRANS8_0 || !(sh_config.texfmt[PTI_RGBA4444] || sh_config.texfmt[PTI_ARGB4444]));\n\t\tuploadfmt_t src = mips->encoding;\n\t\tint i, j, first = -1, sec = -1;\n\t\tfor (i = 0; i < countof(formattransforms); i++)\n\t\t{\n\t\t\tif (formattransforms[i].src == src)\n\t\t\t{\n\t\t\t\tif (allowedformats[formattransforms[i].dest])\n\t\t\t\t{\n\t\t\t\t\tif (formattransforms[i].onebitalpha && !onebitokay)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (first < 0)\t//as a last resort perhaps.\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tfirst = i;\n\t\t\t\t\t\t\tsec = -1;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\t//this is a direct conversion. yay.\n\t\t\t\t\tfirst = i;\n\t\t\t\t\tsec = -1;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\t//check if we can chain it to a second transform\n\t\t\t\tif (first != -1)\n\t\t\t\t\tcontinue;\n\t\t\t\tfor (j = 0; j < countof(formattransforms); j++)\n\t\t\t\t{\n\t\t\t\t\tif (formattransforms[j].src == formattransforms[i].dest)\n\t\t\t\t\t\tif (allowedformats[formattransforms[j].dest])\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tfirst = i;\n\t\t\t\t\t\t\tsec = j;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t//FIXME: add a third transform, for final byteswaps?...\n\t\t\t}\n\t\t}\n\n\t\tif (first >= 0)\n\t\t{\n\t\t\tformattransforms[first].dotransform(mips, formattransforms[first].arg);\n\t\t\tmips->encoding = formattransforms[first].dest;\n\t\t}\n\t\tif (sec >= 0)\n\t\t{\n\t\t\tformattransforms[sec].dotransform(mips, formattransforms[sec].arg);\n\t\t\tmips->encoding = formattransforms[sec].dest;\n\t\t}\n\n\t\tif (allowedformats[mips->encoding])\n\t\t\treturn;\t//okay, that got it.\n\t}\n}\n\nstatic void Image_ChangeFormatFlags(struct pendingtextureinfo *mips, unsigned int flags, uploadfmt_t origfmt, const char *imagename)\n{\n\tif (flags & IF_PALETTIZE)\n\t{\t//paletizing things\n\t\tqboolean p8only[PTI_MAX] = {0};\n\t\tp8only[PTI_P8] = true;\n\t\tImage_ChangeFormat(mips, p8only, origfmt, imagename);\n\t}\n\telse\n\t{\t//don't allow r8-as-indexes if its fed as an input. it won't make sense unless its explicit.\n\t\tqboolean p8 = sh_config.texfmt[PTI_P8];\n\t\tsh_config.texfmt[PTI_P8] = false;\n\t\tImage_ChangeFormat(mips, sh_config.texfmt, origfmt, imagename);\n\t\tsh_config.texfmt[PTI_P8] = p8;\n\t}\n}\n\n//operates in place...\nvoid Image_Premultiply(struct pendingtextureinfo *mips)\n{\n\t//works for rgba or bgra\n\tint i;\n\tswitch(mips->encoding)\n\t{\n\tcase PTI_RGBA32F:\n\t\t{\n\t\t\tfloat *fte_restrict premul = (float*)mips->mip[0].data;\n\t\t\tfor (i = 0; i < mips->mip[0].width*mips->mip[0].height; i++, premul+=4)\n\t\t\t{\n\t\t\t\tpremul[0] = (premul[0] * premul[3]);\n\t\t\t\tpremul[1] = (premul[1] * premul[3]);\n\t\t\t\tpremul[2] = (premul[2] * premul[3]);\n\t\t\t}\n\t\t}\n\t\tbreak;\n\tcase PTI_RGBA16F:\n\t\t{\n\t\t\tunsigned short *fte_restrict premul = (unsigned short*)mips->mip[0].data;\n\t\t\tfor (i = 0; i < mips->mip[0].width*mips->mip[0].height; i++, premul+=4)\n\t\t\t{\n\t\t\t\tfloat a = HalfToFloat(premul[3]);\n\t\t\t\tpremul[0] = FloatToHalf(HalfToFloat(premul[0]) * a);\n\t\t\t\tpremul[1] = FloatToHalf(HalfToFloat(premul[1]) * a);\n\t\t\t\tpremul[2] = FloatToHalf(HalfToFloat(premul[2]) * a);\n\t\t\t}\n\t\t}\n\t\tbreak;\n\tcase PTI_RGBA16:\n\t\t{\n\t\t\tunsigned short *fte_restrict premul = (unsigned short*)mips->mip[0].data;\n\t\t\tfor (i = 0; i < mips->mip[0].width*mips->mip[0].height; i++, premul+=4)\n\t\t\t{\n\t\t\t\tpremul[0] = (premul[0] * premul[3])>>16;\n\t\t\t\tpremul[1] = (premul[1] * premul[3])>>16;\n\t\t\t\tpremul[2] = (premul[2] * premul[3])>>16;\n\t\t\t}\n\t\t}\n\t\tbreak;\n\tcase PTI_A2BGR10:\n\t\t{\n\t\t\tunsigned int *fte_restrict premul = (unsigned int*)mips->mip[0].data, r,g,b,a;\n\t\t\tfor (i = 0; i < mips->mip[0].width*mips->mip[0].height; i++)\n\t\t\t{\n\t\t\t\ta =   (*premul>>30)&0x3;\n\t\t\t\tb = (((*premul>>20)&0x3ff)*a)>>2;\n\t\t\t\tg = (((*premul>>10)&0x3ff)*a)>>2;\n\t\t\t\tr = (((*premul>> 0)&0x3ff)*a)>>2;\n\t\t\t\t*premul++ = (a<<30)|(b<<20)|(g<<20)|(r<<0);\n\t\t\t}\n\t\t}\n\t\tbreak;\n\tcase PTI_LLLX8:\t//FIXME: why the Xs?\n\tcase PTI_LLLA8:\n\tcase PTI_RGBA8:\n\tcase PTI_RGBX8:\n\tcase PTI_BGRA8:\n\tcase PTI_BGRX8:\n\tcase PTI_RGBA8_SRGB:\t//fixme: what's the correct multiplication for srgb?\n\tcase PTI_RGBX8_SRGB:\n\tcase PTI_BGRA8_SRGB:\n\tcase PTI_BGRX8_SRGB:\n\t\t{\n\t\t\tqbyte *fte_restrict premul = (qbyte*)mips->mip[0].data;\n\t\t\tfor (i = 0; i < mips->mip[0].width*mips->mip[0].height; i++, premul+=4)\n\t\t\t{\n\t\t\t\tpremul[0] = (premul[0] * premul[3])>>8;\n\t\t\t\tpremul[1] = (premul[1] * premul[3])>>8;\n\t\t\t\tpremul[2] = (premul[2] * premul[3])>>8;\n\t\t\t}\n\t\t}\n\t\tbreak;\n\tcase PTI_L8A8:\n\tcase PTI_L8A8_SRGB:\n\t\t{\n\t\t\tqbyte *fte_restrict premul = (qbyte*)mips->mip[0].data;\n\t\t\tfor (i = 0; i < mips->mip[0].width*mips->mip[0].height; i++, premul+=2)\n\t\t\t\tpremul[0] = (premul[0] * premul[1])>>8;\n\t\t\tbreak;\n\t\t}\n\tdefault:\n\t\tbreak;\t//format not known, so no idea how to premultiply it. bc2/3 might already be premultiplied or not...\n\t}\n}\n\n//resamples and depalettes as required\n//ALWAYS frees rawdata, even on failure (but never mips).\nstatic qboolean Image_GenMip0(struct pendingtextureinfo *mips, unsigned int flags, void *rawdata, void *palettedata, int imgwidth, int imgheight, int imgdepth, uploadfmt_t fmt, qboolean freedata)\n{\n\tunsigned int *rgbadata = rawdata;\n\tint i;\n\tqboolean valid;\n\tunsigned int bb, bw, bh, bd;\n\n\tmips->mip[0].width = imgwidth;\n\tmips->mip[0].height = imgheight;\n\tmips->mip[0].depth = imgdepth;\n\tmips->mipcount = 1;\n\n\tswitch(fmt)\n\t{\n\tdefault:\n\t\tif (fmt&PTI_FULLMIPCHAIN)\n\t\t{\n\t\t\tfmt = fmt&~PTI_FULLMIPCHAIN;\n\t\t\tImage_RoundDimensions(&mips->mip[0].width, &mips->mip[0].height, &mips->mip[0].depth, flags);\n\t\t\tif (mips->mip[0].width == imgwidth && mips->mip[0].height == imgheight && mips->mip[0].depth == imgdepth)\t//make sure its okay\n\t\t\t{\n\t\t\t\tsize_t sz = 0;\n\t\t\t\tint is3d = (mips->type == PTI_3D)?1:0;\n\t\t\t\tImage_BlockSizeForEncoding(fmt, &bb, &bw, &bh, &bd);\n\t\t\t\tfor (i = 0; i < countof(mips->mip) && (imgwidth || imgheight || (is3d && imgdepth)); i++, imgwidth>>=1, imgheight>>=1, imgdepth>>=is3d)\n\t\t\t\t{\n\t\t\t\t\tmips->mip[i].width = max(1,imgwidth);\n\t\t\t\t\tmips->mip[i].height = max(1,imgheight);\n\t\t\t\t\tmips->mip[i].depth = max(1,imgdepth);\n\t\t\t\t\tmips->mip[i].datasize = bb * ((mips->mip[i].width+bw-1)/bw) * ((mips->mip[i].height+bh-1)/bh) * ((mips->mip[i].depth+bd-1)/bd);\n\t\t\t\t\tmips->mip[i].needfree = false;\n\t\t\t\t\tsz += mips->mip[i].datasize;\n\t\t\t\t}\n\t\t\t\tmips->mipcount = i;\n\t\t\t\tmips->encoding = fmt;\n\t\t\t\tif (!freedata)\n\t\t\t\t{\n\t\t\t\t\trgbadata = BZ_Malloc(sz);\n\t\t\t\t\tmemcpy(rgbadata, rawdata, sz);\n\t\t\t\t}\n\t\t\t\tmips->extrafree = rawdata = rgbadata;\n\t\t\t\tfor (i = 0; i < mips->mipcount; i++)\n\t\t\t\t{\n\t\t\t\t\tmips->mip[i].data = rawdata;\n\t\t\t\t\trawdata = (qbyte*)rawdata+mips->mip[i].datasize;\n\t\t\t\t}\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\tmips->encoding = fmt;\n\t\tbreak;\n\n\tbaddepth:\n\t\tCon_Printf(\"R_LoadRawTexture: bad depth for format\\n\");\n\t\tif (freedata)\n\t\t\tBZ_Free(rawdata);\n\t\treturn false;\n\tcase TF_INVALID:\n\t\tCon_Printf(\"R_LoadRawTexture: bad format\\n\");\n\t\tif (freedata)\n\t\t\tBZ_Free(rawdata);\n\t\treturn false;\n\n\tcase TF_MIP4_P8:\n\t\t//8bit indexed data.\n\t\tImage_RoundDimensions(&mips->mip[0].width, &mips->mip[0].height, &mips->mip[0].depth, flags);\n\t\tflags |= IF_NOPICMIP;\n\t\tif (/*!r_dodgymiptex.ival &&*/ mips->mip[0].width == imgwidth && mips->mip[0].height == imgheight && mips->mip[0].depth == 1)\n\t\t{\n\t\t\tunsigned int pixels =\n\t\t\t\t(imgwidth>>0) * (imgheight>>0) + \n\t\t\t\t(imgwidth>>1) * (imgheight>>1) +\n\t\t\t\t(imgwidth>>2) * (imgheight>>2) +\n\t\t\t\t(imgwidth>>3) * (imgheight>>3);\n\n\t\t\tmips->encoding = PTI_P8;\n\t\t\trgbadata = BZ_Malloc(pixels);\n\t\t\tmemcpy(rgbadata, rawdata, pixels);\n\n\t\t\tfor (i = 0; i < 4; i++)\n\t\t\t{\n\t\t\t\tmips->mip[i].width = imgwidth>>i;\n\t\t\t\tmips->mip[i].height = imgheight>>i;\n\t\t\t\tmips->mip[i].depth = 1;\n\t\t\t\tmips->mip[i].datasize = mips->mip[i].width * mips->mip[i].height;\n\t\t\t\tmips->mip[i].needfree = false;\n\t\t\t}\n\t\t\tmips->mipcount = i;\n\t\t\tmips->mip[0].data = rgbadata;\n\t\t\tmips->mip[1].data = (qbyte*)mips->mip[0].data + mips->mip[0].datasize;\n\t\t\tmips->mip[2].data = (qbyte*)mips->mip[1].data + mips->mip[1].datasize;\n\t\t\tmips->mip[3].data = (qbyte*)mips->mip[2].data + mips->mip[2].datasize;\n\n\t\t\tmips->extrafree = rgbadata;\n\t\t\tif (freedata)\n\t\t\t\tBZ_Free(rawdata);\n\t\t\treturn true;\n\t\t}\n\t\t//fall through\n\tcase PTI_LLLX8:\n\t\tif (sh_config.texfmt[((vid.flags & VID_SRGBAWARE) /*&& (flags & IF_SRGB)*/ && !(flags & IF_NOSRGB))?PTI_L8_SRGB:PTI_L8])\n\t\t{\t//if we can compact it, then do so!\n\t\t\tmips->encoding = PTI_L8;\n\t\t\t//can just do this in-place.\n\t\t\tfor (i = 0; i < imgwidth * imgheight * imgdepth; i++)\n\t\t\t\t((qbyte*)rgbadata)[i] = ((qbyte*)rgbadata)[i*4];\n\t\t}\n\t\t//otherwise treat it as whatever the gpu prefers\n\t\telse if (sh_config.texfmt[PTI_BGRX8] || sh_config.texfmt[PTI_BGRA8])\n\t\t\tmips->encoding = PTI_BGRX8;\n\t\telse\n\t\t\tmips->encoding = PTI_RGBX8;\n\t\tbreak;\n\tcase PTI_LLLA8:\n\t\t//take special care here, because L8A8_SRGB doesn't exist in core gl, nor can it be easily faked.\n\t\tif (sh_config.texfmt[((vid.flags & VID_SRGBAWARE) /*&& (flags & IF_SRGB)*/ && !(flags & IF_NOSRGB))?PTI_L8A8_SRGB:PTI_L8A8])\n\t\t{\t//if we can compact it, then do so!\n\t\t\tmips->encoding = PTI_L8A8;\n\t\t\t//can just do this in-place.\n\t\t\tfor (i = 0; i < imgwidth * imgheight * imgdepth; i++)\n\t\t\t{\n\t\t\t\t((qbyte*)rgbadata)[i*2+0] = ((qbyte*)rgbadata)[i*4+0];\n\t\t\t\t((qbyte*)rgbadata)[i*2+1] = ((qbyte*)rgbadata)[i*4+3];\n\t\t\t}\n\t\t}\n\t\telse if (sh_config.texfmt[PTI_BGRA8])\n\t\t\tmips->encoding = PTI_BGRA8;\n\t\telse\n\t\t\tmips->encoding = PTI_RGBA8;\n\t\tbreak;\n\tcase TF_MIP4_SOLID8:\n\t\t//8bit opaque data\n\t\tImage_RoundDimensions(&mips->mip[0].width, &mips->mip[0].height, &mips->mip[0].depth, flags);\n\t\tflags |= IF_NOPICMIP;\n#ifdef HAVE_CLIENT\n\t\tif (!r_dodgymiptex.ival && mips->mip[0].width == imgwidth && mips->mip[0].height == imgheight && mips->mip[0].depth == 1)\n\t\t{\t//special hack required to preserve the hand-drawn lower mips.\n\t\t\tunsigned int pixels =\n\t\t\t\t(imgwidth>>0) * (imgheight>>0) + \n\t\t\t\t(imgwidth>>1) * (imgheight>>1) +\n\t\t\t\t(imgwidth>>2) * (imgheight>>2) +\n\t\t\t\t(imgwidth>>3) * (imgheight>>3);\n\n\t\t\tmips->encoding = PTI_RGBX8;\n\t\t\trgbadata = BZ_Malloc(pixels*4);\n\t\t\tfor (i = 0; i < pixels; i++)\n\t\t\t\trgbadata[i] = d_8to24rgbtable[((qbyte*)rawdata)[i]];\n\n\t\t\tfor (i = 0; i < 4; i++)\n\t\t\t{\n\t\t\t\tmips->mip[i].width = imgwidth>>i;\n\t\t\t\tmips->mip[i].height = imgheight>>i;\n\t\t\t\tmips->mip[i].depth = 1;\n\t\t\t\tmips->mip[i].datasize = mips->mip[i].width * mips->mip[i].height * 4;\n\t\t\t\tmips->mip[i].needfree = false;\n\t\t\t}\n\t\t\tmips->mipcount = i;\n\t\t\tmips->mip[0].data = rgbadata;\n\t\t\tmips->mip[1].data = (qbyte*)mips->mip[0].data + mips->mip[0].datasize;\n\t\t\tmips->mip[2].data = (qbyte*)mips->mip[1].data + mips->mip[1].datasize;\n\t\t\tmips->mip[3].data = (qbyte*)mips->mip[2].data + mips->mip[2].datasize;\n\n\t\t\tmips->extrafree = rgbadata;\n\t\t\tif (freedata)\n\t\t\t\tBZ_Free(rawdata);\n\t\t\treturn true;\n\t\t}\n#endif\n\t\t//fall through\n\tcase TF_SOLID8:\n\t\trgbadata = BZ_Malloc(imgdepth * imgwidth * imgheight*4);\n\t\tif (sh_config.texfmt[PTI_BGRX8])\n\t\t{\t//bgra8 is typically faster when supported.\n\t\t\tmips->encoding = PTI_BGRX8;\n\t\t\tfor (i = 0; i < imgwidth * imgheight * imgdepth; i++)\n\t\t\t\trgbadata[i] = d_8to24bgrtable[((qbyte*)rawdata)[i]];\n\t\t}\n\t\telse\n\t\t{\n\t\t\tmips->encoding = PTI_RGBX8;\n\t\t\tfor (i = 0; i < imgwidth * imgheight * imgdepth; i++)\n\t\t\t\trgbadata[i] = d_8to24rgbtable[((qbyte*)rawdata)[i]];\n\t\t}\n\t\tif (freedata)\n\t\t\tBZ_Free(rawdata);\n\t\tfreedata = true;\n\t\tbreak;\n\tcase TF_TRANS8:\n\t\t{\n\t\t\tmips->encoding = PTI_RGBX8;\n\t\t\trgbadata = BZ_Malloc(imgdepth * imgwidth * imgheight*4);\n\t\t\tfor (i = 0; i < imgwidth * imgheight * imgdepth; i++)\n\t\t\t{\n\t\t\t\tif (((qbyte*)rawdata)[i] == 0xff)\n\t\t\t\t{//fixme: blend non-0xff neighbours. no, just use premultiplied alpha instead, where it matters.\n\t\t\t\t\trgbadata[i] = 0;\n\t\t\t\t\tmips->encoding = PTI_RGBA8;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\trgbadata[i] = d_8to24rgbtable[((qbyte*)rawdata)[i]];\n\t\t\t}\n\t\t\tif (freedata)\n\t\t\t\tBZ_Free(rawdata);\n\t\t\tfreedata = true;\n\t\t}\n\t\tbreak;\n\tcase TF_H2_TRANS8_0:\n\t\t{\n\t\t\tmips->encoding = PTI_RGBX8;\n\t\t\trgbadata = BZ_Malloc(imgdepth * imgwidth * imgheight*4);\n\t\t\tfor (i = 0; i < imgwidth * imgheight * imgdepth; i++)\n\t\t\t{\n\t\t\t\tqbyte px = ((qbyte*)rawdata)[i];\n\t\t\t\t//Note: The proper value here is 0.\n\t\t\t\t//However, hexen2 has a bug that ALSO treats 255 the same way, but ONLY in the GL version.\n\t\t\t\t//So allow both.\n\t\t\t\tif (px == 0xff || px == 0)\n\t\t\t\t{//fixme: blend opaque neighbours? no, just use premultiplied alpha instead, where it matters.\n\t\t\t\t\trgbadata[i] = 0;\n\t\t\t\t\tmips->encoding = PTI_RGBA8;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\trgbadata[i] = d_8to24rgbtable[px];\n\t\t\t}\n\t\t\tif (freedata)\n\t\t\t\tBZ_Free(rawdata);\n\t\t\tfreedata = true;\n\t\t}\n\t\tbreak;\n\tcase TF_TRANS8_FULLBRIGHT:\n\t\tmips->encoding = PTI_RGBA8;\n\t\trgbadata = BZ_Malloc(imgdepth * imgwidth * imgheight*4);\n\t\tfor (i = 0, valid = false; i < imgwidth * imgheight * imgdepth; i++)\n\t\t{\n\t\t\tif (((qbyte*)rawdata)[i] == 255 || ((qbyte*)rawdata)[i] < 256-vid.fullbright)\n\t\t\t\trgbadata[i] = 0;\n\t\t\telse\n\t\t\t{\n\t\t\t\trgbadata[i] = d_8to24rgbtable[((qbyte*)rawdata)[i]];\n\t\t\t\tvalid = true;\n\t\t\t}\n\t\t}\n\t\tif (freedata)\n\t\t\tBZ_Free(rawdata);\n\t\tfreedata = true;\n\t\tif (!valid)\n\t\t{\n\t\t\tBZ_Free(rgbadata);\n\t\t\treturn false;\n\t\t}\n\t\tbreak;\n\n\tcase TF_HEIGHT8PAL:\n\t\tif (imgdepth != 1)\n\t\t\tgoto baddepth;\n\t\tmips->encoding = PTI_RGBA8;\n\t\trgbadata = BZ_Malloc(imgwidth * imgheight*5);\n\t\t{\n\t\t\tqbyte *heights = (qbyte*)(rgbadata + (imgwidth*imgheight * imgdepth));\n\t\t\tfor (i = 0; i < imgwidth * imgheight; i++)\n\t\t\t{\n\t\t\t\tunsigned int rgb = d_8to24rgbtable[((qbyte*)rawdata)[i]];\n\t\t\t\theights[i] = (((rgb>>16)&0xff) + ((rgb>>8)&0xff) + ((rgb>>0)&0xff))/3;\n\t\t\t}\n#ifndef HAVE_CLIENT\n\t\t\tImage_GenerateNormalMap(heights, rgbadata, imgwidth, imgheight, 4, 0);\n#else\n\t\t\tImage_GenerateNormalMap(heights, rgbadata, imgwidth, imgheight, r_shadow_bumpscale_basetexture.value?r_shadow_bumpscale_basetexture.value:4, r_shadow_heightscale_basetexture.value);\n#endif\n\t\t}\n\t\tif (freedata)\n\t\t\tBZ_Free(rawdata);\n\t\tfreedata = true;\n\t\tbreak;\n\tcase TF_HEIGHT8:\n\t\tif (imgdepth != 1)\n\t\t\tgoto baddepth;\n\t\tmips->encoding = PTI_RGBA8;\n\t\trgbadata = BZ_Malloc(imgwidth * imgheight*4);\n#ifndef HAVE_CLIENT\n\t\tImage_GenerateNormalMap(rawdata, rgbadata, imgwidth, imgheight, 4, 1);\n#else\n\t\tImage_GenerateNormalMap(rawdata, rgbadata, imgwidth, imgheight, r_shadow_bumpscale_bumpmap.value, r_shadow_heightscale_bumpmap.value);\n#endif\n\t\tif (freedata)\n\t\t\tBZ_Free(rawdata);\n\t\tfreedata = true;\n\t\tbreak;\n\n\tcase TF_BGR24_FLIP:\n\t\tif (imgdepth != 1)\n\t\t\tgoto baddepth;\n\t\tmips->encoding = PTI_RGBX8;\n\t\trgbadata = BZ_Malloc(imgwidth * imgheight*4);\n\t\tfor (i = 0; i < imgheight; i++)\n\t\t{\n\t\t\tint x;\n\t\t\tqbyte *in = (qbyte*)rawdata + (imgheight-i-1) * imgwidth * 3;\n\t\t\tqbyte *out = (qbyte*)rgbadata + i * imgwidth * 4;\n\t\t\tfor (x = 0; x < imgwidth; x++, in+=3, out+=4)\n\t\t\t{\n\t\t\t\tout[0] = in[2];\n\t\t\t\tout[1] = in[1];\n\t\t\t\tout[2] = in[0];\n\t\t\t\tout[3] = 0xff;\n\t\t\t}\n\t\t}\n\t\tif (freedata)\n\t\t\tBZ_Free(rawdata);\n\t\tfreedata = true;\n\t\tbreak;\n\n\tcase TF_MIP4_8PAL24_T255:\n\tcase TF_MIP4_8PAL24:\n\t\t//8bit opaque data\n\t\t{\n\t\t\tunsigned int pixels =\n\t\t\t\t\t(imgwidth>>0) * (imgheight>>0) + \n\t\t\t\t\t(imgwidth>>1) * (imgheight>>1) +\n\t\t\t\t\t(imgwidth>>2) * (imgheight>>2) +\n\t\t\t\t\t(imgwidth>>3) * (imgheight>>3);\n\t\t\tpalettedata = (qbyte*)rawdata + pixels;\n\t\t\tImage_RoundDimensions(&mips->mip[0].width, &mips->mip[0].height, &mips->mip[0].depth, flags);\n\t\t\tflags |= IF_NOPICMIP;\n#ifdef HAVE_CLIENT\n\t\t\tif (!r_dodgymiptex.ival && mips->mip[0].width == imgwidth && mips->mip[0].height == imgheight && mips->mip[0].depth == 1)\n\t\t\t{\n\t\t\t\tunsigned int pixels =\n\t\t\t\t\t(imgwidth>>0) * (imgheight>>0) + \n\t\t\t\t\t(imgwidth>>1) * (imgheight>>1) +\n\t\t\t\t\t(imgwidth>>2) * (imgheight>>2) +\n\t\t\t\t\t(imgwidth>>3) * (imgheight>>3);\n\n\t\t\t\trgbadata = BZ_Malloc(pixels*4);\n\t\t\t\tif (fmt == TF_MIP4_8PAL24_T255)\n\t\t\t\t{\n\t\t\t\t\tmips->encoding = PTI_RGBA8;\n\t\t\t\t\tfor (i = 0; i < pixels; i++)\n\t\t\t\t\t{\n\t\t\t\t\t\tqbyte idx = ((qbyte*)rawdata)[i];\n\t\t\t\t\t\tif (idx == 255)\n\t\t\t\t\t\t\trgbadata[i] = 0;\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tqbyte *p = ((qbyte*)palettedata) + idx*3;\n\t\t\t\t\t\t\trgbadata[i] = 0xff000000 | (p[0]<<0) | (p[1]<<8) | (p[2]<<16);\t//FIXME: endian\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tmips->encoding = PTI_RGBX8;\n\t\t\t\t\tfor (i = 0; i < pixels; i++)\n\t\t\t\t\t{\n\t\t\t\t\t\tqbyte *p = ((qbyte*)palettedata) + ((qbyte*)rawdata)[i]*3;\n\t\t\t\t\t\t//FIXME: endian\n\t\t\t\t\t\trgbadata[i] = 0xff000000 | (p[0]<<0) | (p[1]<<8) | (p[2]<<16);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tfor (i = 0; i < 4; i++)\n\t\t\t\t{\n\t\t\t\t\tmips->mip[i].width = imgwidth>>i;\n\t\t\t\t\tmips->mip[i].height = imgheight>>i;\n\t\t\t\t\tmips->mip[i].depth = 1;\n\t\t\t\t\tmips->mip[i].datasize = mips->mip[i].width * mips->mip[i].height * 4;\n\t\t\t\t\tmips->mip[i].needfree = false;\n\t\t\t\t}\n\t\t\t\tmips->mipcount = i;\n\t\t\t\tmips->mip[0].data = rgbadata;\n\t\t\t\tmips->mip[1].data = (qbyte*)mips->mip[0].data + mips->mip[0].datasize;\n\t\t\t\tmips->mip[2].data = (qbyte*)mips->mip[1].data + mips->mip[1].datasize;\n\t\t\t\tmips->mip[3].data = (qbyte*)mips->mip[2].data + mips->mip[2].datasize;\n\n\t\t\t\tmips->extrafree = rgbadata;\n\t\t\t\tif (freedata)\n\t\t\t\t\tBZ_Free(rawdata);\n\t\t\t\treturn true;\n\t\t\t}\n#endif\n\t\t}\n\t\t//fall through\n\tcase TF_8PAL24:\n\t\tif (!palettedata)\n\t\t{\n\t\t\tCon_Printf(\"TF_8PAL24: no palette\");\n\t\t\tif (freedata)\n\t\t\t\tBZ_Free(rawdata);\n\t\t\treturn false;\n\t\t}\n\t\trgbadata = BZ_Malloc(imgdepth * imgwidth * imgheight*4);\n\t\tif (fmt == TF_MIP4_8PAL24_T255)\n\t\t{\n\t\t\tmips->encoding = PTI_RGBA8;\n\t\t\tfor (i = 0; i < imgwidth * imgheight * imgdepth; i++)\n\t\t\t{\n\t\t\t\tqbyte idx = ((qbyte*)rawdata)[i];\n\t\t\t\tif (idx == 255)\n\t\t\t\t\trgbadata[i] = 0;\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tqbyte *p = ((qbyte*)palettedata) + idx*3;\n\t\t\t\t\trgbadata[i] = 0xff000000 | (p[0]<<0) | (p[1]<<8) | (p[2]<<16);\t//FIXME: endian\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tmips->encoding = PTI_RGBX8;\n\t\t\tfor (i = 0; i < imgwidth * imgheight * imgdepth; i++)\n\t\t\t{\n\t\t\t\tqbyte *p = ((qbyte*)palettedata) + ((qbyte*)rawdata)[i]*3;\n\t\t\t\t//FIXME: endian\n\t\t\t\trgbadata[i] = 0xff000000 | (p[0]<<0) | (p[1]<<8) | (p[2]<<16);\n\t\t\t}\n\t\t}\n\t\tif (freedata)\n\t\t\tBZ_Free(rawdata);\n\t\tfreedata = true;\n\t\tbreak;\n\tcase TF_8PAL32:\n\t\tif (!palettedata)\n\t\t{\n\t\t\tCon_Printf(\"TF_8PAL32: no palette\");\n\t\t\tif (freedata)\n\t\t\t\tBZ_Free(rawdata);\n\t\t\treturn false;\n\t\t}\n\t\tmips->encoding = PTI_RGBA8;\n\t\trgbadata = BZ_Malloc(imgdepth * imgwidth * imgheight*4);\n\t\tfor (i = 0; i < imgwidth * imgheight * imgdepth; i++)\n\t\t\trgbadata[i] = ((unsigned int*)palettedata)[((qbyte*)rawdata)[i]];\n\t\tif (freedata)\n\t\t\tBZ_Free(rawdata);\n\t\tfreedata = true;\n\t\tbreak;\n\n#ifdef HEXEN2\n\tcase TF_H2_T7G1: /*8bit data, odd indexes give greyscale transparence*/\n\t\tmips->encoding = PTI_RGBA8;\n\t\trgbadata = BZ_Malloc(imgdepth * imgwidth * imgheight*4);\n\t\tfor (i = 0; i < imgwidth * imgheight * imgdepth; i++)\n\t\t{\n\t\t\tqbyte p = ((qbyte*)rawdata)[i];\n\t\t\trgbadata[i] = d_8to24rgbtable[p] & 0x00ffffff;\n\t\t\tif (p == 0)\n\t\t\t\t;\n\t\t\telse if (p&1)\n\t\t\t\trgbadata[i] |= 0x80000000;\n\t\t\telse\n\t\t\t\trgbadata[i] |= 0xff000000;\n\t\t}\n\t\tif (freedata)\n\t\t\tBZ_Free(rawdata);\n\t\tfreedata = true;\n\t\tbreak;\n\tcase TF_H2_T4A4:     /*8bit data, weird packing*/\n\t\tmips->encoding = PTI_RGBA8;\n\t\trgbadata = BZ_Malloc(imgdepth * imgheight * imgwidth*4);\n\t\tfor (i = 0; i < imgwidth * imgheight * imgdepth; i++)\n\t\t{\n\t\t\tstatic const int ColorIndex[16] = {0x00, 0x1f, 0x2f, 0x3f, 0x4f, 0x5f, 0x6f, 0x7f, 0x8f, 0x9f, 0xaf, 0xbf, 0xc7, 0xcf, 0xdf, 0xe7};\n\t\t\tstatic const unsigned ColorPercent[16] = {25, 51, 76, 102, 114, 127, 140, 153, 165, 178, 191, 204, 216, 229, 237, 247};\n\t\t\tqbyte p = ((qbyte*)rawdata)[i];\n\t\t\trgbadata[i] = d_8to24rgbtable[ColorIndex[p>>4]] & 0x00ffffff;\n\t\t\trgbadata[i] |= ( int )ColorPercent[p&15] << 24;\n\t\t}\n\t\tif (freedata)\n\t\t\tBZ_Free(rawdata);\n\t\tfreedata = true;\n\t\tbreak;\n#endif\n\t}\n\n\tif (flags & IF_NOALPHA)\n\t{\n\t\tsafeswitch(mips->encoding)\n\t\t{\n\t\tcase PTI_RGBA8:\n\t\t\tmips->encoding = PTI_RGBX8;\n\t\t\tbreak;\n\t\tcase PTI_BGRA8:\n\t\t\tmips->encoding = PTI_BGRX8;\n\t\t\tbreak;\n\t\tcase PTI_RGBA8_SRGB:\n\t\t\tmips->encoding = PTI_RGBX8_SRGB;\n\t\t\tbreak;\n\t\tcase PTI_BGRA8_SRGB:\n\t\t\tmips->encoding = PTI_BGRX8_SRGB;\n\t\t\tbreak;\n\t\tcase PTI_RGBA16:\n\t\tcase PTI_RGBA16F:\n\t\tcase PTI_RGBA32F:\n\t\tcase PTI_ARGB4444:\n\t\tcase PTI_ARGB1555:\n\t\tcase PTI_RGBA4444:\n\t\tcase PTI_RGBA5551:\n\t\tcase PTI_A2BGR10:\n\t\tcase PTI_L8A8: //could strip.\n\t\tcase PTI_L8A8_SRGB: //could strip.\n\t\t\tbreak;\t//erk\n\t\tcase PTI_BC1_RGBA:\n\t\t\tmips->encoding = PTI_BC1_RGB;\n\t\t\tbreak;\n\t\tcase PTI_BC1_RGBA_SRGB:\n\t\t\tmips->encoding = PTI_BC1_RGB_SRGB;\n\t\t\tbreak;\n\t\tcase PTI_BC2_RGBA:\t//could strip to PTI_BC1_RGB\n\t\tcase PTI_BC2_RGBA_SRGB:\t//could strip to PTI_BC1_RGB\n\t\tcase PTI_BC3_RGBA:\t//could strip to PTI_BC1_RGB\n\t\tcase PTI_BC3_RGBA_SRGB:\t//could strip to PTI_BC1_RGB\n\t\tcase PTI_BC7_RGBA:\t//much too messy...\n\t\tcase PTI_BC7_RGBA_SRGB:\n\t\tcase PTI_ETC2_RGB8A1: //would need to force the 'opaque' bit in each block and treat as PTI_ETC2_RGB8.\n\t\tcase PTI_ETC2_RGB8A1_SRGB: //would need to force the 'opaque' bit in each block and treat as PTI_ETC2_RGB8.\n\t\tcase PTI_ETC2_RGB8A8: //could strip to PTI_ETC2_RGB8\n\t\tcase PTI_ETC2_RGB8A8_SRGB: //could strip to PTI_ETC2_SRGB8\n\t\tcase PTI_ASTC_4X4_LDR:\n\t\tcase PTI_ASTC_4X4_SRGB:\n\t\tcase PTI_ASTC_4X4_HDR:\n\t\tcase PTI_ASTC_5X4_LDR:\n\t\tcase PTI_ASTC_5X4_SRGB:\n\t\tcase PTI_ASTC_5X4_HDR:\n\t\tcase PTI_ASTC_5X5_LDR:\n\t\tcase PTI_ASTC_5X5_SRGB:\n\t\tcase PTI_ASTC_5X5_HDR:\n\t\tcase PTI_ASTC_6X5_LDR:\n\t\tcase PTI_ASTC_6X5_SRGB:\n\t\tcase PTI_ASTC_6X5_HDR:\n\t\tcase PTI_ASTC_6X6_LDR:\n\t\tcase PTI_ASTC_6X6_SRGB:\n\t\tcase PTI_ASTC_6X6_HDR:\n\t\tcase PTI_ASTC_8X5_LDR:\n\t\tcase PTI_ASTC_8X5_SRGB:\n\t\tcase PTI_ASTC_8X5_HDR:\n\t\tcase PTI_ASTC_8X6_LDR:\n\t\tcase PTI_ASTC_8X6_SRGB:\n\t\tcase PTI_ASTC_8X6_HDR:\n\t\tcase PTI_ASTC_10X5_LDR:\n\t\tcase PTI_ASTC_10X5_SRGB:\n\t\tcase PTI_ASTC_10X5_HDR:\n\t\tcase PTI_ASTC_10X6_LDR:\n\t\tcase PTI_ASTC_10X6_SRGB:\n\t\tcase PTI_ASTC_10X6_HDR:\n\t\tcase PTI_ASTC_8X8_LDR:\n\t\tcase PTI_ASTC_8X8_SRGB:\n\t\tcase PTI_ASTC_8X8_HDR:\n\t\tcase PTI_ASTC_10X8_LDR:\n\t\tcase PTI_ASTC_10X8_SRGB:\n\t\tcase PTI_ASTC_10X8_HDR:\n\t\tcase PTI_ASTC_10X10_LDR:\n\t\tcase PTI_ASTC_10X10_SRGB:\n\t\tcase PTI_ASTC_10X10_HDR:\n\t\tcase PTI_ASTC_12X10_LDR:\n\t\tcase PTI_ASTC_12X10_SRGB:\n\t\tcase PTI_ASTC_12X10_HDR:\n\t\tcase PTI_ASTC_12X12_LDR:\n\t\tcase PTI_ASTC_12X12_SRGB:\n\t\tcase PTI_ASTC_12X12_HDR:\n#ifdef ASTC3D\n\t\tcase PTI_ASTC_3X3X3_HDR:\n\t\tcase PTI_ASTC_3X3X3_SRGB:\n\t\tcase PTI_ASTC_3X3X3_LDR:\n\t\tcase PTI_ASTC_4X3X3_HDR:\n\t\tcase PTI_ASTC_4X3X3_SRGB:\n\t\tcase PTI_ASTC_4X3X3_LDR:\n\t\tcase PTI_ASTC_4X4X3_HDR:\n\t\tcase PTI_ASTC_4X4X3_SRGB:\n\t\tcase PTI_ASTC_4X4X3_LDR:\n\t\tcase PTI_ASTC_4X4X4_HDR:\n\t\tcase PTI_ASTC_4X4X4_SRGB:\n\t\tcase PTI_ASTC_4X4X4_LDR:\n\t\tcase PTI_ASTC_5X4X4_HDR:\n\t\tcase PTI_ASTC_5X4X4_SRGB:\n\t\tcase PTI_ASTC_5X4X4_LDR:\n\t\tcase PTI_ASTC_5X5X4_HDR:\n\t\tcase PTI_ASTC_5X5X4_SRGB:\n\t\tcase PTI_ASTC_5X5X4_LDR:\n\t\tcase PTI_ASTC_5X5X5_HDR:\n\t\tcase PTI_ASTC_5X5X5_SRGB:\n\t\tcase PTI_ASTC_5X5X5_LDR:\n\t\tcase PTI_ASTC_6X5X5_HDR:\n\t\tcase PTI_ASTC_6X5X5_SRGB:\n\t\tcase PTI_ASTC_6X5X5_LDR:\n\t\tcase PTI_ASTC_6X6X5_HDR:\n\t\tcase PTI_ASTC_6X6X5_SRGB:\n\t\tcase PTI_ASTC_6X6X5_LDR:\n\t\tcase PTI_ASTC_6X6X6_HDR:\n\t\tcase PTI_ASTC_6X6X6_SRGB:\n\t\tcase PTI_ASTC_6X6X6_LDR:\n#endif\n#ifdef FTE_TARGET_WEB\n\t\tcase PTI_WHOLEFILE:\n#endif\n\t\t\t//erk. meh.\n\t\t\tbreak;\n\t\tcase PTI_L8:\n\t\tcase PTI_L8_SRGB:\n\t\tcase PTI_R16:\n\t\tcase PTI_P8:\n\t\tcase PTI_R8:\n\t\tcase PTI_R8_SNORM:\n\t\tcase PTI_RG8:\n\t\tcase PTI_RG8_SNORM:\n\t\tcase PTI_RGB565:\n\t\tcase PTI_RGB8:\n\t\tcase PTI_BGR8:\n\t\tcase PTI_RGB8_SRGB:\n\t\tcase PTI_BGR8_SRGB:\n\t\tcase PTI_RGB32F:\n\t\tcase PTI_E5BGR9:\n\t\tcase PTI_B10G11R11F:\n\t\tcase PTI_RGBX8:\n\t\tcase PTI_BGRX8:\n\t\tcase PTI_RGBX8_SRGB:\n\t\tcase PTI_BGRX8_SRGB:\n\t\tcase PTI_BC1_RGB:\n\t\tcase PTI_BC1_RGB_SRGB:\n\t\tcase PTI_BC4_R:\n\t\tcase PTI_BC4_R_SNORM:\n\t\tcase PTI_BC5_RG:\n\t\tcase PTI_BC5_RG_SNORM:\n\t\tcase PTI_BC6_RGB_UFLOAT:\n\t\tcase PTI_BC6_RGB_SFLOAT:\n\t\tcase PTI_ETC1_RGB8:\n\t\tcase PTI_ETC2_RGB8:\n\t\tcase PTI_ETC2_RGB8_SRGB:\n\t\tcase PTI_EAC_R11:\n\t\tcase PTI_EAC_R11_SNORM:\n\t\tcase PTI_EAC_RG11:\n\t\tcase PTI_EAC_RG11_SNORM:\n\t\tcase PTI_R16F:\n\t\tcase PTI_R32F:\n\t\t\tbreak;\t//already no alpha in these formats\n\t\tcase PTI_DEPTH16:\n\t\tcase PTI_DEPTH24:\n\t\tcase PTI_DEPTH32:\n\t\tcase PTI_DEPTH24_8:\n\t\t\tbreak;\n\t\tcase PTI_EMULATED:\n\t\tcase PTI_MAX:\n\t\tsafedefault:\n\t\t\tbreak;\t//stfu\n\t\t}\n\t\t//FIXME: fill alpha channel with 255?\n\t}\n\n\tif ((vid.flags & VID_SRGBAWARE) /*&& (flags & IF_SRGB)*/ && !(flags & IF_NOSRGB))\n\t{\t//most modern editors write srgb images.\n\t\t//however, that might not be supported.\n\t\tuploadfmt_t nf = PTI_MAX;\n\t\tswitch(mips->encoding)\n\t\t{\n\t\tcase PTI_R8:\t\t\tnf = PTI_INVALID; break;\n\t\tcase PTI_L8:\t\t\tnf = PTI_L8_SRGB; break;\n\t\tcase PTI_L8A8:\t\t\tnf = PTI_L8A8_SRGB; break;\n\t\tcase PTI_LLLX8:\t\t\tnf = PTI_RGBX8_SRGB; break;\n\t\tcase PTI_LLLA8:\t\t\tnf = PTI_RGBA8_SRGB; break;\n\t\tcase PTI_RGBA8:\t\t\tnf = PTI_RGBA8_SRGB; break;\n\t\tcase PTI_RGBX8:\t\t\tnf = PTI_RGBX8_SRGB; break;\n\t\tcase PTI_BGRA8:\t\t\tnf = PTI_BGRA8_SRGB; break;\n\t\tcase PTI_BGRX8:\t\t\tnf = PTI_BGRX8_SRGB; break;\n\t\tcase PTI_RGB8:\t\t\tnf = PTI_RGB8_SRGB; break;\n\t\tcase PTI_BGR8:\t\t\tnf = PTI_BGR8_SRGB; break;\n\t\tcase PTI_BC1_RGB:\t\tnf = PTI_BC1_RGB_SRGB; break;\n\t\tcase PTI_BC1_RGBA:\t\tnf = PTI_BC1_RGBA_SRGB; break;\n\t\tcase PTI_BC2_RGBA:\t\tnf = PTI_BC2_RGBA_SRGB; break;\n\t\tcase PTI_BC3_RGBA:\t\tnf = PTI_BC3_RGBA_SRGB; break;\n\t\tcase PTI_BC7_RGBA:\t\tnf = PTI_BC7_RGBA_SRGB; break;\n\t\tcase PTI_ETC1_RGB8:\t\tnf = PTI_ETC2_RGB8_SRGB; break;\n\t\tcase PTI_ETC2_RGB8:\t\tnf = PTI_ETC2_RGB8_SRGB; break;\n\t\tcase PTI_ETC2_RGB8A1:\tnf = PTI_ETC2_RGB8A1_SRGB; break;\n\t\tcase PTI_ETC2_RGB8A8:\tnf = PTI_ETC2_RGB8A8_SRGB; break;\n\t\tcase PTI_ASTC_4X4_LDR:\tnf = PTI_ASTC_4X4_SRGB; break;\n\t\tcase PTI_ASTC_5X4_LDR:\tnf = PTI_ASTC_5X4_SRGB; break;\n\t\tcase PTI_ASTC_5X5_LDR:\tnf = PTI_ASTC_5X5_SRGB; break;\n\t\tcase PTI_ASTC_6X5_LDR:\tnf = PTI_ASTC_6X5_SRGB; break;\n\t\tcase PTI_ASTC_6X6_LDR:\tnf = PTI_ASTC_6X6_SRGB; break;\n\t\tcase PTI_ASTC_8X5_LDR:\tnf = PTI_ASTC_8X5_SRGB; break;\n\t\tcase PTI_ASTC_8X6_LDR:\tnf = PTI_ASTC_8X6_SRGB; break;\n\t\tcase PTI_ASTC_10X5_LDR:\tnf = PTI_ASTC_10X5_SRGB; break;\n\t\tcase PTI_ASTC_10X6_LDR:\tnf = PTI_ASTC_10X6_SRGB; break;\n\t\tcase PTI_ASTC_8X8_LDR:\tnf = PTI_ASTC_8X8_SRGB; break;\n\t\tcase PTI_ASTC_10X8_LDR:\tnf = PTI_ASTC_10X8_SRGB; break;\n\t\tcase PTI_ASTC_10X10_LDR:nf = PTI_ASTC_10X10_SRGB; break;\n\t\tcase PTI_ASTC_12X10_LDR:nf = PTI_ASTC_12X10_SRGB; break;\n\t\tcase PTI_ASTC_12X12_LDR:nf = PTI_ASTC_12X12_SRGB; break;\n\n\t\t//these formats are inherantly linear. oh well.\n\t\tcase PTI_R16:\n\t\tcase PTI_R16F:\n\t\tcase PTI_R32F:\n\t\tcase PTI_RGBA16:\n\t\tcase PTI_RGBA16F:\n\t\tcase PTI_RGBA32F:\n\t\t\tnf = mips->encoding;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tif (freedata)\n\t\t\t\tBZ_Free(rgbadata);\n\t\t\treturn false;\n\t\t}\n\t\tif (sh_config.texfmt[nf])\n\t\t\tmips->encoding = nf;\n\t\telse\n\t\t{\t//srgb->linear\n\t\t\tint m = mips->mip[0].width*mips->mip[0].height*mips->mip[0].depth;\n\n\t\t\tswitch(mips->encoding)\n\t\t\t{\n\t\t\tcase PTI_RGB8:\n\t\t\tcase PTI_BGR8:\n\t\t\t\tm*=3;\n\t\t\t\t//fallthrough\n\t\t\tcase PTI_R8:\n\t\t\tcase PTI_L8:\n\t\t\t\tfor (i = 0; i < m; i++)\n\t\t\t\t\t((qbyte*)rgbadata)[i+0] = 255*Image_LinearFloatFromsRGBFloat(((qbyte*)rgbadata)[i+0] * (1.0/255));\n\t\t\t\tbreak;\n\t\t\tcase PTI_L8A8:\n\t\t\t\tm*=2;\n\t\t\t\tfor (i = 0; i < m; i+=2)\n\t\t\t\t\t((qbyte*)rgbadata)[i+0] = 255*Image_LinearFloatFromsRGBFloat(((qbyte*)rgbadata)[i+0] * (1.0/255));\n\t\t\t\tbreak;\n\t\t\tcase PTI_R16:\n\t\t\t\tfor (i = 0; i < m; i++)\n\t\t\t\t\t((unsigned short*)rgbadata)[i+0] = 0xffff*Image_LinearFloatFromsRGBFloat(((unsigned short*)rgbadata)[i+0] * (1.0/0xffff));\n\t\t\t\tbreak;\n\t\t\tcase PTI_RGBA16:\n\t\t\t\tm*=4;\n\t\t\t\tfor (i = 0; i < m; i+=4)\n\t\t\t\t{\n\t\t\t\t\t((unsigned short*)rgbadata)[i+0] = 0xffff*Image_LinearFloatFromsRGBFloat(((unsigned short*)rgbadata)[i+0] * (1.0/0xffff));\n\t\t\t\t\t((unsigned short*)rgbadata)[i+1] = 0xffff*Image_LinearFloatFromsRGBFloat(((unsigned short*)rgbadata)[i+1] * (1.0/0xffff));\n\t\t\t\t\t((unsigned short*)rgbadata)[i+2] = 0xffff*Image_LinearFloatFromsRGBFloat(((unsigned short*)rgbadata)[i+2] * (1.0/0xffff));\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase PTI_RGBA8:\n\t\t\tcase PTI_RGBX8:\n\t\t\tcase PTI_BGRA8:\n\t\t\tcase PTI_BGRX8:\n\t\t\t\tm*=4;\n\t\t\t\tfor (i = 0; i < m; i+=4)\n\t\t\t\t{\n\t\t\t\t\t((qbyte*)rgbadata)[i+0] = 255*Image_LinearFloatFromsRGBFloat(((qbyte*)rgbadata)[i+0] * (1.0/255));\n\t\t\t\t\t((qbyte*)rgbadata)[i+1] = 255*Image_LinearFloatFromsRGBFloat(((qbyte*)rgbadata)[i+1] * (1.0/255));\n\t\t\t\t\t((qbyte*)rgbadata)[i+2] = 255*Image_LinearFloatFromsRGBFloat(((qbyte*)rgbadata)[i+2] * (1.0/255));\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase PTI_BC1_RGB:\n\t\t\tcase PTI_BC1_RGBA:\n\t\t\tcase PTI_BC2_RGBA:\n\t\t\tcase PTI_BC3_RGBA:\n\t\t\t\t//FIXME: bc1/2/3 has two leading 16bit 565 values per block.\n\t\t\tdefault:\n\t\t\t\t//these formats are weird. we can't just fiddle with the rgbdata\n\t\t\t\t//FIXME: etc2 has all sorts of weird encoding tables...\n\t\t\t\tif (freedata)\n\t\t\t\t\tBZ_Free(rgbadata);\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t}\n\n\n\tImage_RoundDimensions(&mips->mip[0].width, &mips->mip[0].height, &mips->mip[0].depth, flags);\n\tif (rgbadata)\n\t{\n\t\tif (mips->mip[0].width == imgwidth && mips->mip[0].height == imgheight && mips->mip[0].depth == imgdepth)\n\t\t\tmips->mip[0].data = rgbadata;\n\t\telse\n\t\t{\n\t\t\tif (imgdepth == 1 &&\n\t\t\t\t(mips->mip[0].data=Image_ResampleTexture(mips->encoding, rgbadata, imgwidth, imgheight, NULL, mips->mip[0].width, mips->mip[0].height))\t//actually rescale it here.\n\t\t\t\t)\n\t\t\t{\n\t\t\t\tif (freedata)\n\t\t\t\t\tBZ_Free(rgbadata);\n\t\t\t\tfreedata = true;\n\t\t\t}\n\t\t\telse\n\t\t\t{\t//rescaling unsupported\n\t\t\t\tmips->mip[0].data = rgbadata;\n\t\t\t\tmips->mip[0].width = imgwidth;\n\t\t\t\tmips->mip[0].height = imgheight;\n\t\t\t\tmips->mip[0].depth = imgdepth;\n\t\t\t}\n\t\t}\n\t}\n\telse\n\t\tmips->mip[0].data = NULL;\n\tImage_BlockSizeForEncoding(mips->encoding, &bb, &bw, &bh, &bd);\n\tmips->mip[0].datasize = ((mips->mip[0].width+bw-1)/bw) * ((mips->mip[0].height+bh-1)/bh) * ((mips->mip[0].depth+bd-1)/bd) * bb;\n\n\tif (mips->type == PTI_3D)\n\t{\n\t\tqbyte *data2d = mips->mip[0].data, *data3d;\n\t\tmips->mip[0].data = NULL;\n\t\t/*our 2d input image is interlaced as y0z0,y0z1,y1z0,y1z1\n\t\t  however, hardware uses the more logical y0z0,y1z0,y0z1,y1z1 ordering (xis ordered properly already)*/\n\t\tif (mips->mip[0].height*mips->mip[0].height == mips->mip[0].width && mips->mip[0].depth == 1 && (bb==4&&bw==1&&bh==1&&bd==1))\n\t\t{\n\t\t\tint d, r;\n\t\t\tint size = mips->mip[0].height;\n\t\t\tmips->mip[0].width = size;\n\t\t\tmips->mip[0].height = size;\n\t\t\tmips->mip[0].depth = size;\n\t\t\tmips->mip[0].data = data3d = BZ_Malloc(size*size*size);\n\t\t\tfor (d = 0; d < size; d++)\n\t\t\t\tfor (r = 0; r < size; r++)\n\t\t\t\t\tmemcpy(data3d + (r + d*size) * size, data2d + (r*size + d) * size, size*4);\n\t\t\tmips->mip[0].datasize = size*size*size*4;\n\t\t}\n\t\tif (freedata)\n\t\t\tBZ_Free(data2d);\n\t\tif (!mips->mip[0].data)\n\t\t\treturn false;\n\t}\n\n\tif (flags & IF_PREMULTIPLYALPHA)\n\t\tImage_Premultiply(mips);\n\n\tmips->mip[0].needfree = freedata;\n\treturn true;\n}\n\n//for ???X8 formats, replaces the alpha channel with the colours from an additional greyscale _alpha image (typically a jpeg)\n//this stuff exists only for compat with DP. Note that DP reads only the blue channel.\n//writes to rgbdata+format on success\nvoid Image_ReadExternalAlpha(qbyte *rgbadata, size_t imgwidth, size_t imgheight, const char *fname, uploadfmt_t *format)\n{\n#ifdef HAVE_CLIENT\n\tunsigned int alpha_width, alpha_height, p;\n\tchar aname[MAX_QPATH];\n\tqbyte *alphadata, *srcchan;\n\tchar *alph;\n\tsize_t alphsize;\n\tchar ext[8];\n\tuploadfmt_t alphaformat;\n\tint srcstride;\n\n\tswitch(*format)\n\t{\n\tdefault:\n\t\tbreak;\n\tcase PTI_BGRX8_SRGB:\n\tcase PTI_BGRX8:\n\tcase PTI_RGBX8_SRGB:\n\tcase PTI_RGBX8:\n\tcase PTI_LLLX8:\n\t\tCOM_StripExtension(fname, aname, sizeof(aname));\n\t\tCOM_FileExtension(fname, ext, sizeof(ext));\n\t\tQ_strncatz(aname, \"_alpha.\", sizeof(aname));\n\t\tQ_strncatz(aname, ext, sizeof(aname));\n\t\tif (!strchr(aname, ':') && (alph = FS_LoadMallocFile (aname, &alphsize)))\n\t\t{\n\t\t\tif ((alphadata = ReadRawImageFile(alph, alphsize, &alpha_width, &alpha_height, &alphaformat, false, aname)))\n\t\t\t{\n\t\t\t\tif (alpha_width == imgwidth && alpha_height == imgheight)\n\t\t\t\t{\n\t\t\t\t\trgbadata += 3;\t//might as well do this in advance\n\t\t\t\t\tswitch(alphaformat)\n\t\t\t\t\t{\n\t\t\t\t\tcase PTI_RGBA8:\n\t\t\t\t\tcase PTI_RGBX8:\n\t\t\t\t\tcase PTI_LLLA8:\n\t\t\t\t\tcase PTI_LLLX8:\n\t\t\t\t\tcase PTI_RGBA8_SRGB:\n\t\t\t\t\tcase PTI_RGBX8_SRGB:\n\t\t\t\t\t\tsrcstride = 4;\n\t\t\t\t\t\tsrcchan = alphadata+2;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase PTI_RGB8:\n\t\t\t\t\tcase PTI_RGB8_SRGB:\n\t\t\t\t\t\tsrcstride = 4;\n\t\t\t\t\t\tsrcchan = alphadata+2;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase PTI_BGRA8:\n\t\t\t\t\tcase PTI_BGRX8:\n\t\t\t\t\tcase PTI_BGRA8_SRGB:\n\t\t\t\t\tcase PTI_BGRX8_SRGB:\n\t\t\t\t\t\tsrcstride = 4;\n\t\t\t\t\t\tsrcchan = alphadata+0;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase PTI_BGR8:\n\t\t\t\t\tcase PTI_BGR8_SRGB:\n\t\t\t\t\t\tsrcstride = 3;\n\t\t\t\t\t\tsrcchan = alphadata+0;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase PTI_R8:\n\t\t\t\t\tcase PTI_L8:\n\t\t\t\t\tcase PTI_L8_SRGB:\n\t\t\t\t\t\tsrcstride = 1;\n\t\t\t\t\t\tsrcchan = alphadata+0;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase PTI_L8A8:\n\t\t\t\t\tcase PTI_L8A8_SRGB:\n\t\t\t\t\tcase PTI_RG8:\n\t\t\t\t\t\tsrcstride = 2;\n\t\t\t\t\t\tsrcchan = alphadata+0;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tCon_Printf(\"%s: Unable to read luminance (\\\"%s\\\" has unsupported pixelformat)\\n\", fname, aname);\n\t\t\t\t\t\tsrcchan = \"\\xff\";\n\t\t\t\t\t\tsrcstride = 0;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\tfor (p = 0; p < alpha_width*alpha_height; p++)\n\t\t\t\t\t\trgbadata[(p<<2)] = srcchan[p*srcstride];\n\t\t\t\t\tswitch(*format)\n\t\t\t\t\t{\n\t\t\t\t\tcase PTI_LLLX8:\t\t*format = PTI_RGBA8;\t\tbreak;\n\t\t\t\t\tcase PTI_RGBX8:\t\t*format = PTI_RGBA8;\t\tbreak;\n\t\t\t\t\tcase PTI_BGRX8:\t\t*format = PTI_BGRA8;\t\tbreak;\n\t\t\t\t\tcase PTI_RGBX8_SRGB:*format = PTI_RGBA8_SRGB;\tbreak;\n\t\t\t\t\tcase PTI_BGRX8_SRGB:*format = PTI_BGRA8_SRGB;\tbreak;\n\t\t\t\t\tdefault:\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tBZ_Free(alphadata);\n\t\t\t}\n\t\t\tBZ_Free(alph);\n\t\t}\n\t\tbreak;\n\t}\n#endif\n}\n\n//always frees filedata, even on failure.\n//also frees the textures fallback data, but only on success\nstruct pendingtextureinfo *Image_LoadMipsFromMemory(int flags, const char *iname, const char *fname, qbyte *filedata, int filesize)\n{\n\tuploadfmt_t format;\n\tqbyte *rgbadata;\n\n\tint imgwidth, imgheight;\n\tsize_t l;\n\n\tstruct pendingtextureinfo *mips = NULL;\n\n\t//these formats have special handling, because they cannot be implemented via Read32BitImageFile - they don't result in rgba images.\n#ifdef IMAGEFMT_KTX\n\tif (!mips)\n\t\tmips = Image_ReadKTXFile(flags, fname, filedata, filesize);\n#endif\n#ifdef IMAGEFMT_PKM\n\tif (!mips)\n\t\tmips = Image_ReadPKMFile(flags, fname, filedata, filesize);\n#endif\n#ifdef IMAGEFMT_DDS\n\tif (!mips)\n\t\tmips = Image_ReadDDSFile(flags, fname, filedata, filesize);\n#endif\n#ifdef IMAGEFMT_BLP\n\tif (!mips && filedata[0] == 'B' && filedata[1] == 'L' && filedata[2] == 'P' && filedata[3] == '2') \n\t\tmips = Image_ReadBLPFile(flags, fname, filedata, filesize);\n#endif\n\tfor (l = 0; !mips && l < imageloader_count; l++)\n\t\tmips = imageloader[l].funcs->ReadImageFile(flags, fname, filedata, filesize);\n#ifdef IMAGEFMT_ASTC\n\tif (!mips && filesize>= 16 && filedata[0] == 0x13 && filedata[1] == 0xab && filedata[2] == 0xa1 && filedata[3] == 0x5c)\n\t\tmips = Image_ReadASTCFile(flags, fname, filedata, filesize);\n#endif\n\n#ifdef STBI_ONLY_GIF\n\tif (!mips)\n\t{\n\t\tint *delays = NULL;\n\t\tint w, h, d;\n\t\tint comp;\n\t\tstbi_uc *data = stbi_load_gif_from_memory(filedata, filesize, &delays, &w, &h, &d, &comp, 4); //force 4, we're not really ready for other types of 2d arrays.\n\t\tif (data)\n\t\t{\n\t\t\tmips = Z_Malloc(sizeof(*mips));\n\t\t\tmips->mipcount = 1;\t//this format doesn't support mipmaps. so there's only one level.\n\t\t\tmips->type = PTI_2D_ARRAY;\t//2d arrays are basically just a 3d texture with weird mips (meaning they can load with gl's glTexStorage3D\n\t\t\tmips->extrafree = delays;\n\t\t\tswitch(comp)\n\t\t\t{\n\t\t\tcase 1:\t\tmips->encoding = PTI_L8;\t\tbreak;\n\t\t\tcase 2:\t\tmips->encoding = PTI_L8A8;\t\tbreak;\n\t\t\tcase 3:\t\tmips->encoding = PTI_RGB8;\t\tbreak;\n\t\t\tcase 4:\t\tmips->encoding = PTI_RGBA8;\t\tbreak;\n\t\t\tdefault:\tmips->encoding = PTI_INVALID;\tbreak;\n\t\t\t}\n\t\t\tmips->mip[0].data = data;\n\t\t\tmips->mip[0].datasize = comp*w*h*d;\n\t\t\tmips->mip[0].width = w;\n\t\t\tmips->mip[0].height = h;\n\t\t\tmips->mip[0].depth = d;\n\t\t\tmips->mip[0].needfree = true;\n\t\t}\n\t}\n#endif\n\n\t//the above formats are assumed to have consumed filedata somehow (probably storing into mips->extradata)\n\tif (mips)\n\t{\n\t\tunsigned int picmip = min(Image_GetPicMip(flags), mips->mipcount-1), i;\n\t\tif (picmip < mips->mipcount)\n\t\t{\n\t\t\tfor (i = 0; i < picmip; i++)\n\t\t\t\tif (mips->mip[i].needfree)\n\t\t\t\t\tBZ_Free(mips->mip[i].data);\n\t\t\tmips->mipcount -= i;\n\t\t\tmemmove(mips->mip, mips->mip+i, sizeof(*mips->mip)*mips->mipcount);\n\t\t}\n\n\t\tImage_ChangeFormatFlags(mips, flags, TF_INVALID, fname);\n\t\treturn mips;\n\t}\n\n\tif ((rgbadata = ReadRawImageFile(filedata, filesize, &imgwidth, &imgheight, &format, false, fname)))\n\t{\n#ifdef HAVE_CLIENT\n\t\textern cvar_t vid_hardwaregamma;\n\t\tif (!(flags&IF_NOGAMMA) && !vid_hardwaregamma.value)\n\t\t\tBoostGamma(rgbadata, imgwidth, imgheight, format);\n#endif\n\n\t\tswitch(format)\n\t\t{\n\t\tdefault:\n\t\t\tbreak;\n\t\tcase PTI_RGBA32F:\n\t\tcase PTI_RGBA16F:\n\t\tcase PTI_L8A8:\n\t\tcase PTI_RGBA8:\n\t\tcase PTI_RGBA4444:\n\t\tcase PTI_ARGB4444:\n\t\tcase PTI_RGBA5551:\n\t\tcase PTI_ARGB1555:\n\t\t\tflags &= ~IF_NOALPHA;\n\t\t\tbreak;\n\t\tcase PTI_BGRX8_SRGB:\n\t\tcase PTI_BGRX8:\n\t\tcase PTI_RGBX8_SRGB:\n\t\tcase PTI_RGBX8:\n\t\tcase PTI_LLLX8:\n\t\t\tif (!(flags & IF_NOALPHA))\n\t\t\t\tImage_ReadExternalAlpha(rgbadata, imgwidth, imgheight, fname, &format);\n\t\t\tbreak;\n\t\t}\n\n\t\tmips = Z_Malloc(sizeof(*mips));\n\t\tmips->type = (flags & IF_TEXTYPEMASK)>>IF_TEXTYPESHIFT;\n\t\tif (mips->type == PTI_ANY)\n\t\t\tmips->type = PTI_2D;\t//d\n\t\tif (Image_GenMip0(mips, flags, rgbadata, NULL, imgwidth, imgheight, 1, format, true))\n\t\t{\n\t\t\tImage_GenerateMips(mips, flags);\n\t\t\tImage_ChangeFormatFlags(mips, flags, format, fname);\n\t\t\tBZ_Free(filedata);\n\t\t\treturn mips;\n\t\t}\n\t\tZ_Free(mips);\n\t}\n#ifdef FTE_TARGET_WEB\n\telse if (1)\n\t{\n\t\tstruct pendingtextureinfo *mips;\n\t\tmips = Z_Malloc(sizeof(*mips));\n\t\tmips->type = (flags & IF_TEXTYPEMASK)>>IF_TEXTYPESHIFT;\n\t\tmips->mipcount = 1;\n\t\tmips->encoding = PTI_WHOLEFILE;\n\t\tmips->extrafree = NULL;\n\t\t//evil ensues:\n\t\tif (filesize >= 32 && !strncmp(filedata, \"\\x89PNG\", 4) && !strncmp(filedata+12, \"IHDR\", 4))\n\t\t{\t//need to do this to get png sizes working right for the quake rerelease's content\n\t\t\tmips->mip[0].width  = (filedata[0x13]<<0)|(filedata[0x12]<<8)|(filedata[0x11]<<16)|(filedata[0x10]<<24);\n\t\t\tmips->mip[0].height = (filedata[0x17]<<0)|(filedata[0x16]<<8)|(filedata[0x15]<<16)|(filedata[0x14]<<24);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tmips->mip[0].width  = 1;\n\t\t\tmips->mip[0].height = 1;\n\t\t}\n\t\tmips->mip[0].depth = 1;\n\t\tmips->mip[0].data = filedata;\n\t\tmips->mip[0].datasize = filesize;\n\t\tmips->mip[0].needfree = true;\n\t\t//width+height are not yet known. bah.\n\t\treturn mips;\n\t}\n#endif\n\telse\n\t\tCon_TPrintf(\"Unable to load file %s (format unsupported)\\n\", fname);\n\n\tBZ_Free(filedata);\n\treturn NULL;\n}\n\nvoid *Image_FlipImage(const void *inbuffer, void *outbuffer, int *inoutwidth, int *inoutheight, int pixelbytes, qboolean flipx, qboolean flipy, qboolean flipd)\n{\n\tint x, y, b;\n\tqbyte *outb;\n\tconst qbyte *inb, *inr;\n\tint inwidth = *inoutwidth;\n\tint inheight = *inoutheight;\n\tint rowstride = inwidth;\n\tint colstride = 1;\n\n\t//simply return if no operation\n\tif (!flipx && !flipy && !flipd)\n\t\tmemcpy(outbuffer, inbuffer, inwidth*inheight*pixelbytes);\n\telse\n\t{\n\t\tinr = inbuffer;\n\t\toutb = outbuffer;\n\n\t\tif (flipy)\n\t\t{\n\t\t\tinr += (inwidth*inheight-inwidth)*pixelbytes;//start on the bottom row\n\t\t\trowstride *= -1;\t//and we need to move up instead\n\t\t}\n\t\tif (flipx)\n\t\t{\n\t\t\tcolstride *= -1;\t//move backwards\n\t\t\tinr += (inwidth-1)*pixelbytes;\t//start at the end of the row\n\t\t}\n\t\tif (flipd)\n\t\t{\n\t\t\t//switch the dimensions\n\t\t\tint tmp = inwidth;\n\t\t\tinwidth = inheight;\n\t\t\tinheight = tmp;\n\t\t\t//make sure the caller gets the new dimensions\n\t\t\t*inoutwidth = inwidth;\n\t\t\t*inoutheight = inheight;\n\t\t\t//switch the strides\n\t\t\ttmp = colstride;\n\t\t\tcolstride = rowstride;\n\t\t\trowstride = tmp;\n\t\t}\n\n\t\tcolstride *= pixelbytes;\n\t\trowstride *= pixelbytes;\n\n\t\t//rows->rows, columns->columns\n\t\tfor (y = 0; y < inheight; y++)\n\t\t{\n\t\t\tinb = inr;\t//reset the input after each row, so we have truely independant row+column strides\n\t\t\tinr += rowstride;\n\t\t\tfor (x = 0; x < inwidth; x++)\n\t\t\t{\n\t\t\t\tfor (b = 0; b < pixelbytes; b++)\n\t\t\t\t\t*outb++ = inb[b];\n\t\t\t\tinb += colstride;\n\t\t\t}\n\t\t}\n\t}\n\treturn outbuffer;\n}\n#ifdef HAVE_CLIENT\nstatic int tex_extensions_count;\n#define tex_extensions_max 15\nstatic struct\n{\n\tchar name[6];\n} tex_extensions[tex_extensions_max];\nstatic void QDECL R_ImageExtensions_Callback(struct cvar_s *var, char *oldvalue)\n{\n\tchar *v = var->string;\n\ttex_extensions_count = 0;\n\n\twhile (tex_extensions_count < tex_extensions_max)\n\t{\n\t\tv = COM_Parse(v);\n\t\tif (!v)\n\t\t\tbreak;\n\t\tQ_snprintfz(tex_extensions[tex_extensions_count].name, sizeof(tex_extensions[tex_extensions_count].name), \".%s\", com_token);\n\t\ttex_extensions_count++;\n\t}\n\n\tif (tex_extensions_count < tex_extensions_max)\n\t{\n\t\tQ_snprintfz(tex_extensions[tex_extensions_count].name, sizeof(tex_extensions[tex_extensions_count].name), \"\");\n\t\ttex_extensions_count++;\n\t}\n}\nstatic struct pendingtextureinfo *Image_LoadCubemapTextureData(const char *nicename, char *subpath, unsigned int texflags)\n{\n\tstatic struct\n\t{\n\t\tconst char *suffix;\n\t\tqboolean flipx, flipy, flipd;\n\t\tint pad;\n\t} cmscheme[][6] =\n\t{\n\t\t{\n\t\t\t{\"rt\", false,  false, true},\n\t\t\t{\"lf\", true, true,  true},\n\t\t\t{\"bk\", false, true, false},\n\t\t\t{\"ft\", true,  false,  false},\n\t\t\t{\"up\", false,  false, true},\n\t\t\t{\"dn\", false,  false, true}\n\t\t},\n\n\t\t{\n\t\t\t{\"px\", false, false, false},\n\t\t\t{\"nx\", false, false, false},\n\t\t\t{\"py\", false, false, false},\n\t\t\t{\"ny\", false, false, false},\n\t\t\t{\"pz\", false, false, false},\n\t\t\t{\"nz\", false, false, false}\n\t\t},\n\n\t\t{\n\t\t\t{\"posx\", false, false, false},\n\t\t\t{\"negx\", false, false, false},\n\t\t\t{\"posy\", false, false, false},\n\t\t\t{\"negy\", false, false, false},\n\t\t\t{\"posz\", false, false, false},\n\t\t\t{\"negz\", false, false, false}\n\t\t}\n\t};\n\tint i, j, e;\n\tstruct pendingtextureinfo *mips = NULL;\n\tchar fname[MAX_QPATH];\n\tsize_t filesize;\n\tint width, height;\n\tuploadfmt_t format;\n\tchar *nextprefix, *prefixend;\n\tsize_t prefixlen;\n\n\tfor (i = 0; i < 6; i++)\n\t{\n\t\tprefixlen = 0;\n\t\tnextprefix = subpath;\n\t\tfor(;;)\n\t\t{\n\t\t\tfor (e = (texflags & IF_EXACTEXTENSION)?tex_extensions_count-1:0; e < tex_extensions_count; e++)\n\t\t\t{\n\t\t\t\t//try and open one\n\t\t\t\tqbyte *buf = NULL, *data;\n\t\t\t\tfilesize = 0;\n\n\t\t\t\tfor (j = 0; j < countof(cmscheme); j++)\n\t\t\t\t{\n\t\t\t\t\tQ_snprintfz(fname+prefixlen, sizeof(fname)-prefixlen, \"%s_%s%s\", nicename, cmscheme[j][i].suffix, tex_extensions[e].name);\n\t\t\t\t\tbuf = FS_LoadMallocFile(fname, &filesize);\n\t\t\t\t\tif (buf)\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tQ_snprintfz(fname+prefixlen, sizeof(fname)-prefixlen, \"%s%s%s\", nicename, cmscheme[j][i].suffix, tex_extensions[e].name);\n\t\t\t\t\tbuf = FS_LoadMallocFile(fname, &filesize);\n\t\t\t\t\tif (buf)\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\t//now read it\n\t\t\t\tif (buf)\n\t\t\t\t{\n\t\t\t\t\tqboolean needsflipping = cmscheme[j][i].flipx||cmscheme[j][i].flipy||cmscheme[j][i].flipd;\n\t\t\t\t\tif ((data = ReadRawImageFile(buf, filesize, &width, &height, &format, true, fname)))\n\t\t\t\t\t{\n\t\t\t\t\t\textern cvar_t vid_hardwaregamma;\n\t\t\t\t\t\tint bb,bw,bh, bd;\n\t\t\t\t\t\tImage_BlockSizeForEncoding(format, &bb, &bw, &bh, &bd);\n\t\t\t\t\t\tif (needsflipping && (bw!=1 || bh!=1 || bd!=1))\n\t\t\t\t\t\t\tCon_Printf(CON_WARNING\"%s: %s requires flipping, which is unsupported with pixel format %s\\n\", nicename, fname, Image_FormatName(format));\t/*can't do it*/\n\t\t\t\t\t\telse if (width == height && (!mips || width == mips->mip[0].width))\t//cubemaps must be square and all the same size (npot is fine though)\n\t\t\t\t\t\t{\t//(skies have a fallback for invalid sizes, but it'll run a bit slower)\n\n\t\t\t\t\t\t\tif (!mips)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tmips = Z_Malloc(sizeof(*mips));\n\t\t\t\t\t\t\t\tmips->type = PTI_CUBE;\n\t\t\t\t\t\t\t\tmips->mipcount = 1;\n\t\t\t\t\t\t\t\tmips->encoding = format;\n\t\t\t\t\t\t\t\tmips->extrafree = NULL;\n\t\t\t\t\t\t\t\tmips->mip[0].datasize = width*height*bb*6;\n\t\t\t\t\t\t\t\tmips->mip[0].data = BZ_Malloc(mips->mip[0].datasize);\n\t\t\t\t\t\t\t\tmips->mip[0].width = width;\n\t\t\t\t\t\t\t\tmips->mip[0].height = height;\n\t\t\t\t\t\t\t\tmips->mip[0].depth = 6;\n\t\t\t\t\t\t\t\tmips->mip[0].needfree = true;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tif (!(texflags&IF_NOGAMMA) && !vid_hardwaregamma.value)\n\t\t\t\t\t\t\t\tBoostGamma(data, width, height, format);\n\t\t\t\t\t\t\tImage_FlipImage(data, (qbyte*)mips->mip[0].data + i*width*height*bb, &width, &height, bb, cmscheme[j][i].flipx, cmscheme[j][i].flipy, cmscheme[j][i].flipd);\n\t\t\t\t\t\t\tBZ_Free(data);\n\n\t\t\t\t\t\t\tBZ_Free(buf);\n\t\t\t\t\t\t\tgoto nextface;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (mips)\n\t\t\t\t\t\t\t\tCon_Printf(CON_WARNING\"%s: %s has inconsistent dimensions (%i*%i, must be %i*%i)\\n\", nicename, fname, width, height, mips->mip[0].width, mips->mip[0].height);\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\tCon_Printf(CON_WARNING\"%s: %s has inconsistent dimensions (%i*%i, must be square)\\n\", nicename, fname, width, height);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tBZ_Free(data);\n\t\t\t\t\t}\n\t\t\t\t\tBZ_Free(buf);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t//get ready for the next prefix...\n\t\t\tif (!nextprefix || !*nextprefix)\n\t\t\t\tbreak;\t//no more...\n\t\t\tprefixend = strchr(nextprefix, ':');\n\t\t\tif (!prefixend)\n\t\t\t\tprefixend = nextprefix+strlen(nextprefix);\n\n\t\t\tprefixlen = prefixend-nextprefix;\n\t\t\tif (prefixlen >= sizeof(fname)-2)\n\t\t\t\tprefixlen = sizeof(fname)-2;\n\t\t\tmemcpy(fname, nextprefix, prefixlen);\n\t\t\tfname[prefixlen++] = '/';\n\n\t\t\tif (*prefixend)\n\t\t\t\tprefixend++;\n\t\t\tnextprefix = prefixend;\n\t\t}\n\n\t\twhile(i>0)\n\t\t\tBZ_Free(mips->mip[i--].data);\n\t\tZ_Free(mips);\n\t\treturn NULL;\nnextface:;\n\t}\n\treturn mips;\n}\n\n//loads from a single mip. takes ownership of the data.\nstatic qboolean Image_LoadRawTexture(texid_t tex, unsigned int flags, void *rawdata, void *palettedata, int imgwidth, int imgheight, uploadfmt_t fmt)\n{\n\tstruct pendingtextureinfo *mips;\n\tmips = Z_Malloc(sizeof(*mips));\n\tmips->type = (flags&IF_TEXTYPEMASK)>>IF_TEXTYPESHIFT;\n\n\tif (!Image_GenMip0(mips, flags, rawdata, palettedata, imgwidth, imgheight, 1, fmt, true))\n\t{\n\t\tZ_Free(mips);\n\t\tif (flags & IF_NOWORKER)\n\t\t\tImage_LoadTexture_Failed(tex, NULL, 0, 0);\n\t\telse\n\t\t\tCOM_AddWork(WG_MAIN, Image_LoadTexture_Failed, tex, NULL, 0, 0);\n\t\treturn false;\n\t}\n\tfmt &= ~PTI_FULLMIPCHAIN;\n\tImage_GenerateMips(mips, flags);\n\tImage_ChangeFormatFlags(mips, flags, fmt, tex->ident);\n\n\tImage_FixupImageSize(tex, imgwidth, imgheight, mips->mip[0].depth);\n\tif (flags & IF_NOWORKER)\n\t\tImage_LoadTextureMips(tex, mips, 0, 0);\n\telse\n\t\tCOM_AddWork(WG_MAIN, Image_LoadTextureMips, tex, mips, 0, 0);\n\treturn true;\n}\n\n//always frees filedata, even on failure.\n//also frees the textures fallback data, but only on success\nqboolean Image_LoadTextureFromMemory(texid_t tex, int flags, const char *iname, const char *fname, qbyte *filedata, int filesize)\n{\n\tstruct pendingtextureinfo *mips = Image_LoadMipsFromMemory(flags, iname, fname, filedata, filesize);\n\tif (mips)\n\t{\n\t\tBZ_Free(tex->fallbackdata);\n\t\ttex->fallbackdata = NULL;\n\n\t\tImage_FixupImageSize(tex, mips->mip[0].width, mips->mip[0].height, mips->mip[0].depth);\n\t\tif ((flags & IF_NOWORKER) || Sys_IsMainThread())\n\t\t\tImage_LoadTextureMips(tex, mips, 0, 0);\n\t\telse\n\t\t\tCOM_AddWork(WG_MAIN, Image_LoadTextureMips, tex, mips, 0, 0);\n\t\treturn true;\n\t}\n\treturn false;\n}\n\nstatic struct\n{\n\tchar *path;\n\tint args;\n\n\tint enabled;\n} tex_path[] =\n{\n\t/*if three args, first is the subpath*/\n\t/*the last two args are texturename then extension*/\n\t{\"%s%s\",\t\t\t2, 1},\t/*directly named texture*/\n\t{\"textures/%s/%s%s\",3, 1},\t/*fuhquake compatibility*/\n\t{\"%s/%s%s\",\t\t\t3, 1},\t/*fuhquake compatibility*/\n\t{\"textures/%s%s\",\t2, 1},\t/*directly named texture with textures/ prefix*/\n#ifdef HAVE_LEGACY\n\t{\"override/%s%s\",\t2, 1}\t/*tenebrae compatibility*/\n#endif\n};\nqboolean Image_LocateHighResTexture(image_t *tex, flocation_t *bestloc, char *bestname, size_t bestnamesize, unsigned int *bestflags)\n{\n\tchar fname[MAX_QPATH], nicename[MAX_QPATH];\n\tint i, e;\n\tchar *altname;\n\tchar *nextalt;\n\tqboolean exactext = !!(tex->flags & IF_EXACTEXTENSION);\n\tqboolean exactpath = false;\n\n\tint locflags = FSLF_DEPTH_INEXPLICIT|FSLF_DEEPONFAILURE;\n\tint bestdepth = 0x7fffffff, depth;\n\tint firstex = (tex->flags & IF_EXACTEXTENSION)?tex_extensions_count-1:0;\n\n\tflocation_t loc;\n\t\n\tif (strncmp(tex->ident, \"http:\", 5) && strncmp(tex->ident, \"https:\", 6))\n\tfor(altname = tex->ident;altname;altname = nextalt)\n\t{\n\t\tif (!strncmp(altname, \"file:\", 5))\n\t\t{\n\t\t\tnextalt = strchr(altname+5, ':');\n\t\t\texactpath = true;\n\t\t}\n\t\telse\n\t\t\tnextalt = strchr(altname, ':');\n\t\tif (nextalt)\n\t\t{\n\t\t\tnextalt++;\n\t\t\tif (nextalt-altname >= sizeof(fname))\n\t\t\t\tcontinue;\t//too long...\n\t\t\tmemcpy(fname, altname, nextalt-altname-1);\n\t\t\tfname[nextalt-altname-1] = 0;\n\t\t\taltname = fname;\n\t\t}\n\n\t\t//see if we recognise the extension, and only strip it if we do.\n\t\tif (exactext)\n\t\t\te = tex_extensions_count;\n\t\telse\n\t\t{\n\t\t\tCOM_FileExtension(altname, nicename, sizeof(nicename));\n\t\t\te = 0;\n\t\t\tif (Q_strcasecmp(nicename, \"lmp\") && Q_strcasecmp(nicename, \"wal\"))\n\t\t\t\tfor (; e < tex_extensions_count; e++)\n\t\t\t\t{\n\t\t\t\t\tif (!Q_strcasecmp(nicename, (*tex_extensions[e].name=='.')?tex_extensions[e].name+1:tex_extensions[e].name))\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t}\n\n\t\t//strip it and try replacements if we do, otherwise assume that we're meant to be loading progs/foo.mdl_0.tga or whatever\n\t\tif (e == tex_extensions_count || exactext)\n\t\t{\n\t\t\texactext = true;\n\t\t\tQ_strncpyz(nicename, altname, sizeof(nicename));\n\t\t}\n\t\telse\n\t\t\tCOM_StripExtension(altname, nicename, sizeof(nicename));\n\n\t\tif (!tex->fallbackdata || (gl_load24bit.ival && !(tex->flags & IF_NOREPLACE)))\n\t\t{\n#ifdef IMAGEFMT_DDS\n\t\t\tif (!exactpath)\n\t\t\t{\n\t\t\t\tQ_snprintfz(fname, sizeof(fname), \"dds/%s.dds\", nicename);\n\t\t\t\tdepth = FS_FLocateFile(fname, locflags, &loc);\n\t\t\t\tif (depth < bestdepth)\n\t\t\t\t{\n\t\t\t\t\tQ_strncpyz(bestname, fname, bestnamesize);\n\t\t\t\t\tbestdepth = depth;\n\t\t\t\t\t*bestloc = loc;\n\t\t\t\t\t*bestflags = 0;\n\t\t\t\t}\n\t\t\t}\n#endif\n\n\t\t\tif (exactpath || strchr(nicename, '/') || strchr(nicename, '\\\\'))\t//never look in a root dir for the pic\n\t\t\t\ti = 0;\n\t\t\telse\n\t\t\t\ti = 1;\n\n\t\t\tfor (; i < sizeof(tex_path)/sizeof(tex_path[0]); i++)\n\t\t\t{\n\t\t\t\tif (!tex_path[i].enabled)\n\t\t\t\t\tcontinue;\n\t\t\t\tif (exactpath && i)\n\t\t\t\t\tbreak;\n\t\t\t\tif (tex_path[i].args >= 3)\n\t\t\t\t{\t//this is a path that needs subpaths\n\t\t\t\t\tchar subpath[MAX_QPATH];\n\t\t\t\t\tchar basename[MAX_QPATH];\n\t\t\t\t\tchar *s, *n;\n\t\t\t\t\tif (!tex->subpath || !*nicename)\n\t\t\t\t\t\tcontinue;\n\n\t\t\t\t\ts = COM_SkipPath(nicename);\n\t\t\t\t\tif (!*s)\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\tn = basename;\n\t\t\t\t\twhile (*s && (*s != '.'||exactext) && n < basename+sizeof(basename)-5)\n\t\t\t\t\t\t*n++ = *s++;\n\t\t\t\t\ts = strchr(s, '_');\n\t\t\t\t\tif (s)\n\t\t\t\t\t{\n\t\t\t\t\t\twhile (*s && n < basename+sizeof(basename)-5)\n\t\t\t\t\t\t\t*n++ = *s++;\n\t\t\t\t\t}\n\t\t\t\t\t*n = 0;\n\n\t\t\t\t\tfor(s = tex->subpath; s; s = n)\n\t\t\t\t\t{\n\t\t\t\t\t\t//subpath a:b:c tries multiple possible sub paths, for compatibility\n\t\t\t\t\t\tn = strchr(s, ':');\n\t\t\t\t\t\tif (n)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (n-s >= sizeof(subpath))\n\t\t\t\t\t\t\t\t*subpath = 0;\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tmemcpy(subpath, s, n-s);\n\t\t\t\t\t\t\t\tsubpath[n-s] = 0;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tn++;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tQ_strncpyz(subpath, s, sizeof(subpath));\n\t\t\t\t\t\tfor (e = firstex; e < tex_extensions_count; e++)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (tex->flags & IF_NOPCX)\n\t\t\t\t\t\t\t\tif (!Q_strcasecmp(tex_extensions[e].name, \".pcx\"))\n\t\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\tQ_snprintfz(fname, sizeof(fname), tex_path[i].path, subpath, basename, tex_extensions[e].name);\n\t\t\t\t\t\t\tdepth = FS_FLocateFile(fname, locflags, &loc);\n\t\t\t\t\t\t\tif (depth < bestdepth)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tQ_strncpyz(bestname, fname, bestnamesize);\n\t\t\t\t\t\t\t\tbestdepth = depth;\n\t\t\t\t\t\t\t\t*bestloc = loc;\n\t\t\t\t\t\t\t\t*bestflags = 0;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tfor (e = firstex; e < tex_extensions_count; e++)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (tex->flags & IF_NOPCX)\n\t\t\t\t\t\t\tif (!Q_strcasecmp(tex_extensions[e].name, \".pcx\"))\n\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\tQ_snprintfz(fname, sizeof(fname), tex_path[i].path, nicename, tex_extensions[e].name);\n\t\t\t\t\t\tdepth = FS_FLocateFile(fname, locflags, &loc);\n\t\t\t\t\t\tif (depth < bestdepth)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tQ_strncpyz(bestname, fname, bestnamesize);\n\t\t\t\t\t\t\tbestdepth = depth;\n\t\t\t\t\t\t\t*bestloc = loc;\n\t\t\t\t\t\t\t*bestflags = 0;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t//support expansion of _bump textures to _norm textures.\n\t\t\t\tif (tex->flags & IF_TRYBUMP)\n\t\t\t\t{\n\t\t\t\t\tif (tex_path[i].args >= 3)\n\t\t\t\t\t{\n\t\t\t\t\t\t/*no legacy compat needed, hopefully*/\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tchar bumpname[MAX_QPATH], *b;\n\t\t\t\t\t\tconst char *n;\n\t\t\t\t\t\tb = bumpname;\n\t\t\t\t\t\tn = nicename;\n\t\t\t\t\t\twhile(*n)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (*n == '_' && !strcmp(n, \"_norm\"))\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tstrcpy(b, \"_bump\");\n\t\t\t\t\t\t\t\tb += 5;\n\t\t\t\t\t\t\t\tn += 5;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t*b++ = *n++;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (*n)\t//no _norm, give up with that\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t*b = 0;\n\t\t\t\t\t\tfor (e = firstex; e < tex_extensions_count; e++)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (!Q_strcasecmp(tex_extensions[e].name, \".tga\"))\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tQ_snprintfz(fname, sizeof(fname), tex_path[i].path, bumpname, tex_extensions[e].name);\n\t\t\t\t\t\t\t\tdepth = FS_FLocateFile(fname, locflags, &loc);\n\t\t\t\t\t\t\t\tif (depth < bestdepth)\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tQ_strncpyz(bestname, fname, bestnamesize);\n\t\t\t\t\t\t\t\t\tbestdepth = depth;\n\t\t\t\t\t\t\t\t\t*bestloc = loc;\n\t\t\t\t\t\t\t\t\t*bestflags = IF_TRYBUMP;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\n\t\t\t/*still failed? attempt to load quake lmp files, which have no real format id (hence why they're not above)*/\n\t\t\tQ_strncpyz(fname, nicename, sizeof(fname));\n\t\t\tCOM_DefaultExtension(fname, \".lmp\", sizeof(fname));\n\t\t\tif (!(tex->flags & IF_NOPCX))\n\t\t\t{\n\t\t\t\tdepth = FS_FLocateFile(fname, locflags, &loc);\n\t\t\t\tif (depth < bestdepth)\n\t\t\t\t{\n\t\t\t\t\tQ_strncpyz(bestname, fname, bestnamesize);\n\t\t\t\t\tbestdepth = depth;\n\t\t\t\t\t*bestloc = loc;\n\t\t\t\t\t*bestflags = 0;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn bestdepth != 0x7fffffff;\n}\n\nstatic void Image_LoadHiResTextureWorker(void *ctx, void *data, size_t a, size_t b)\n{\n\timage_t *tex = ctx;\n\tchar fname[MAX_QPATH];\n\tchar fname2[MAX_QPATH];\n\tchar *altname;\n\tchar *nextalt;\n\n\tflocation_t loc;\n\tunsigned int locflags = 0;\n\n\tvfsfile_t *f;\n\tsize_t fsize, l;\n\tchar *buf;\n\n\tint i, j;\n\tint imgwidth;\n\tint imgheight;\n\tuploadfmt_t format;\n\tint ttype = (tex->flags & IF_TEXTYPEMASK)>>IF_TEXTYPESHIFT;\n\n\tif (ttype == PTI_CUBE)\n\t{\t//cubemaps require special handling because they are (normally) 6 files instead of 1.\n\t\t//the exception is single-file dds/ktx/etc cubemaps.\n\t\t//FIXME: handle via Image_LocateHighResTexture.\n\t\tfor(altname = tex->ident;altname;altname = nextalt)\n\t\t{\n\t\t\tstruct pendingtextureinfo *mips = NULL;\n\t\t\tstatic const char *cubeexts[] =\n\t\t\t{\n\t\t\t\t\"\"\n#ifdef IMAGEFMT_KTX\n\t\t\t\t, \".ktx\"\n#endif\n#ifdef IMAGEFMT_DDS\n\t\t\t\t, \".dds\"\n#endif\n\t\t\t};\n\n\t\t\tnextalt = strchr(altname, ':');\n\t\t\tif (nextalt)\n\t\t\t{\n\t\t\t\tnextalt++;\n\t\t\t\tif (nextalt-altname >= sizeof(fname))\n\t\t\t\t\tcontinue;\t//too long...\n\t\t\t\tmemcpy(fname, altname, nextalt-altname-1);\n\t\t\t\tfname[nextalt-altname-1] = 0;\n\t\t\t\taltname = fname;\n\t\t\t}\n\n\t\t\tfor (i = 0; i < countof(tex_path) && !mips; i++)\n\t\t\t{\n\t\t\t\tif (!tex_path[i].enabled)\n\t\t\t\t\tcontinue;\n\t\t\t\tbuf = NULL;\n\t\t\t\tfsize = 0;\n\t\t\t\tif (tex_path[i].args == 3 && tex->subpath)\n\t\t\t\t{\n\t\t\t\t\tchar subpath[MAX_QPATH];\n\t\t\t\t\tchar *n = tex->subpath, *e;\n\t\t\t\t\twhile (*n)\n\t\t\t\t\t{\n\t\t\t\t\t\te = strchr(n, ':');\n\t\t\t\t\t\tif (!e)\n\t\t\t\t\t\t\te = n+strlen(n);\n\t\t\t\t\t\tQ_strncpyz(subpath, n, min(sizeof(subpath), (e-n)+1));\n\t\t\t\t\t\tn = e;\n\t\t\t\t\t\twhile(*n == ':')\n\t\t\t\t\t\t\tn++;\n\t\t\t\t\t\tfor (j = 0; !buf && j < countof(cubeexts); j++)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tQ_snprintfz(fname2, sizeof(fname2), tex_path[i].path, subpath, altname, cubeexts[j]);\n\t\t\t\t\t\t\tbuf = FS_LoadMallocFile(fname2, &fsize);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse if (tex_path[i].args == 2)\n\t\t\t\t{\n\t\t\t\t\tfor (j = 0; !buf && j < countof(cubeexts); j++)\n\t\t\t\t\t{\n\t\t\t\t\t\tQ_snprintfz(fname2, sizeof(fname2), tex_path[i].path, altname, cubeexts[j]);\n\t\t\t\t\t\tbuf = FS_LoadMallocFile(fname2, &fsize);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (buf)\n\t\t\t\t{\n#ifdef IMAGEFMT_KTX\n\t\t\t\t\tif (!mips)\n\t\t\t\t\t\tmips = Image_ReadKTXFile(tex->flags, altname, buf, fsize);\n#endif\n#ifdef IMAGEFMT_DDS\n\t\t\t\t\tif (!mips)\n\t\t\t\t\t\tmips = Image_ReadDDSFile(tex->flags, altname, buf, fsize);\n#endif\n\t\t\t\t\tfor (l = 0; !mips && l < imageloader_count; l++)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (!imageloader[l].funcs->canloadcubemaps)\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\tmips = imageloader[l].funcs->ReadImageFile(tex->flags, altname, buf, fsize);\n\t\t\t\t\t}\n\t\t\t\t\tif (!mips)\n\t\t\t\t\t\tBZ_Free(buf);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (!mips)\t//try to load multiple images\n\t\t\t\tmips = Image_LoadCubemapTextureData(altname, tex->subpath, tex->flags);\n\n\t\t\tif (mips)\n\t\t\t{\n\t\t\t\tImage_FixupImageSize(tex, mips->mip[0].width, mips->mip[0].height, mips->mip[0].depth);\n\t\t\t\tif (tex->flags & IF_NOWORKER)\n\t\t\t\t\tImage_LoadTextureMips(tex, mips, 0, 0);\n\t\t\t\telse\n\t\t\t\t\tCOM_AddWork(WG_MAIN, Image_LoadTextureMips, tex, mips, 0, 0);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tif (tex->flags & IF_NOWORKER)\n\t\t\tImage_LoadTexture_Failed(tex, NULL, 0, 0);\n\t\telse\n\t\t\tCOM_AddWork(WG_MAIN, Image_LoadTexture_Failed, tex, NULL, 0, 0);\n\t\treturn;\n\t}\n\n\t\n\n\tif (Image_LocateHighResTexture(tex, &loc, fname, sizeof(fname), &locflags))\n\t{\n\t\tf = FS_OpenReadLocation(fname, &loc);\n\t\tif (f)\n\t\t{\n\t\t\tfsize = VFS_GETLEN(f);\n\t\t\tbuf = BZ_Malloc(fsize);\n\t\t\tif (buf)\n\t\t\t{\n\t\t\t\tVFS_READ(f, buf, fsize);\n\t\t\t\tVFS_CLOSE(f);\n\n#ifdef IMAGEFMT_TGA\n\t\t\t\tif (locflags & IF_TRYBUMP)\n\t\t\t\t{\t//it was supposed to be a heightmap image (that we need to convert to normalmap)\n\t\t\t\t\tqbyte *d;\n\t\t\t\t\tint w, h;\n\t\t\t\t\tuploadfmt_t fmt;\n\t\t\t\t\tif ((d = ReadTargaFile(buf, fsize, &w, &h, &fmt, true, PTI_L8)))\t//Only load a greyscale image.\n\t\t\t\t\t{\n\t\t\t\t\t\tBZ_Free(buf);\n\t\t\t\t\t\tif (Image_LoadRawTexture(tex, tex->flags, d, NULL, w, h, TF_HEIGHT8))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tBZ_Free(tex->fallbackdata);\n\t\t\t\t\t\t\ttex->fallbackdata = NULL;\t\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\tCon_Printf(CON_WARNING \"%s: bumpmaps must be greyscale tga.\\n\", fname);\n\t\t\t\t\t//guess not, fall back to normalmaps\n\t\t\t\t}\n#endif\n\n\t\t\t\tif (Image_LoadTextureFromMemory(tex, tex->flags, tex->ident, fname, buf, fsize))\n\t\t\t\t{\n\t\t\t\t\tBZ_Free(tex->fallbackdata);\n\t\t\t\t\ttex->fallbackdata = NULL;\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t\tVFS_CLOSE(f);\n\t\t}\n\t}\n\n\tif (!tex->fallbackdata)\n\t{\n\t\t//now look in wad files and swap over the fallback. (halflife compatability)\n\t\tCOM_StripExtension(tex->ident, fname, sizeof(fname));\n\t\tbuf = W_GetTexture(fname, &imgwidth, &imgheight, &format);\n\t\tif (buf)\n\t\t{\n\t\t\tBZ_Free(tex->fallbackdata);\n\t\t\ttex->fallbackdata = buf;\n\t\t\ttex->fallbackfmt = format;\n\t\t\ttex->fallbackwidth = imgwidth;\n\t\t\ttex->fallbackheight = imgheight;\n\t\t}\n\t}\n\n\tif (tex->fallbackdata)\n\t{\n\t\tif (tex->fallbackfmt == TF_INVALID)\n\t\t{\n\t\t\tvoid *data = tex->fallbackdata;\n\t\t\ttex->fallbackdata = NULL;\n\t\t\tif (Image_LoadTextureFromMemory(tex, tex->flags, tex->ident, fname, data, tex->fallbackwidth))\n\t\t\t\treturn;\n\t\t}\n\t\telse if (Image_LoadRawTexture(tex, tex->flags, tex->fallbackdata, (char*)tex->fallbackdata+(tex->fallbackwidth*tex->fallbackheight), tex->fallbackwidth, tex->fallbackheight, tex->fallbackfmt))\n\t\t{\n\t\t\ttex->fallbackdata = NULL;\n\t\t\treturn;\n\t\t}\n\t\ttex->fallbackdata = NULL;\t\n\t}\n\n//\tSys_Printf(\"Texture %s failed\\n\", nicename);\n\t//signal the main thread to set the final status instead of just setting it to avoid deadlock (it might already be waiting for it).\n\tif (tex->flags & IF_NOWORKER)\n\t\tImage_LoadTexture_Failed(tex, NULL, 0, 0);\n\telse\n\t\tCOM_AddWork(WG_MAIN, Image_LoadTexture_Failed, tex, NULL, 0, 0);\n}\n\n//returns the pointer if its valid, otherwise null\n//this is to pass pointers via the console\nimage_t *Image_TextureIsValid(qintptr_t address)\n{\n\timage_t *img;\n\tfor (img = imagelist; img; img = img->next)\n\t{\n\t\tif (img == (image_t*)address)\n\t\t\tbreak;\n\t}\n\treturn img;\n}\n\n//find an existing texture\nimage_t *Image_FindTexture(const char *identifier, const char *subdir, unsigned int flags)\n{\n\timage_t *tex;\n\tif (!subdir)\n\t\tsubdir = \"\";\n\ttex = Hash_GetInsensitive(&imagetable, identifier);\n\twhile(tex)\n\t{\n\t\tif (!((tex->flags ^ flags) & (IF_CLAMP|IF_PALETTIZE|IF_PREMULTIPLYALPHA)))\n\t\t{\n\t\t\tif (r_ignoremapprefixes.ival || !Q_strcasecmp(subdir, tex->subpath?tex->subpath:\"\") || ((flags|tex->flags) & IF_INEXACT))\n\t\t\t{\n\t\t\t\ttex->regsequence = r_regsequence;\n\t\t\t\treturn tex;\n\t\t\t}\n\t\t}\n\t\ttex = Hash_GetNextInsensitive(&imagetable, identifier, tex);\n\t}\n\treturn NULL;\n}\n//create a texture, with dupes. you'll need to load something into it too.\nstatic image_t *Image_CreateTexture_Internal (const char *identifier, const char *subdir, unsigned int flags)\n{\n\timage_t *tex;\n\tbucket_t *buck;\n\n\ttex = Z_Malloc(sizeof(*tex) + sizeof(bucket_t) + strlen(identifier)+1 + (subdir?strlen(subdir)+1:0));\n\tbuck = (bucket_t*)(tex+1);\n\ttex->ident = (char*)(buck+1);\n\tstrcpy(tex->ident, identifier);\n#ifdef _DEBUG\n\tQ_strncpyz(tex->dbgident, identifier, sizeof(tex->dbgident));\n#endif\n\tif (subdir && *subdir)\n\t{\n\t\ttex->subpath = tex->ident + strlen(identifier)+1;\n\t\tstrcpy(tex->subpath, subdir);\n\t}\n\n\ttex->next = imagelist;\n\timagelist = tex;\n\n\tif ((vid.flags & VID_SRGBAWARE) && !(flags & IF_NOSRGB))\n\t\ttex->flags = flags | IF_SRGB;\t//guess...\n\telse\n\t\ttex->flags = flags;\n\ttex->width = 0;\n\ttex->height = 0;\n\ttex->regsequence = r_regsequence;\n\ttex->status = TEX_NOTLOADED;\n\ttex->fallbackdata = NULL;\n\ttex->fallbackwidth = 0;\n\ttex->fallbackheight = 0;\n\ttex->fallbackfmt = TF_INVALID;\n\tif (*tex->ident)\n\t\tHash_AddInsensitive(&imagetable, tex->ident, tex, buck);\n\treturn tex;\n}\n\nimage_t *Image_CreateTexture (const char *identifier, const char *subdir, unsigned int flags)\n{\n\timage_t *image;\n#ifdef LOADERTHREAD\n\tSys_LockMutex(com_resourcemutex);\n#endif\n\timage = Image_CreateTexture_Internal(identifier, subdir, flags);\n#ifdef LOADERTHREAD\n\tSys_UnlockMutex(com_resourcemutex);\n#endif\n\treturn image;\n}\n\n#ifdef WEBCLIENT\n//called on main thread. oh well.\nstatic void Image_Downloaded(struct dl_download *dl)\n{\n\tqboolean success = false;\n\timage_t *tex = dl->user_ctx;\n\timage_t *p;\n\n\t//make sure the renderer wasn't restarted mid-download\n\tfor (p = imagelist; p; p = p->next)\n\t\tif (p == tex)\n\t\t\tbreak;\n\tif (p)\n\t{\n\t\tif (dl->status == DL_FINISHED)\n\t\t{\n\t\t\tsize_t fsize = VFS_GETLEN(dl->file);\n\t\t\tchar *buf = BZ_Malloc(fsize);\n\t\t\tif (VFS_READ(dl->file, buf, fsize) == fsize)\n\t\t\t\tif (Image_LoadTextureFromMemory(tex, tex->flags, tex->ident, dl->url, buf, fsize))\n\t\t\t\t\tsuccess = true;\n\t\t}\n\t\tif (!success)\n\t\t\tImage_LoadTexture_Failed(tex, NULL, 0, 0);\n\t}\n}\n#endif\n\n//find a texture. will try to load it from disk, using the fallback if it would fail.\nimage_t *QDECL Image_GetTexture(const char *identifier, const char *subpath, unsigned int flags, void *fallbackdata, void *fallbackpalette, int fallbackwidth, int fallbackheight, uploadfmt_t fallbackfmt)\n{\n\timage_t *tex;\n\n\tqboolean dontposttoworker = (flags & (IF_NOWORKER | IF_LOADNOW));\n\tqboolean lowpri = (flags & IF_LOWPRIORITY);\n\tqboolean highpri = (flags & IF_HIGHPRIORITY);\n\tflags &= ~(IF_LOADNOW | IF_LOWPRIORITY | IF_HIGHPRIORITY);\n\n#ifdef LOADERTHREAD\n\tSys_LockMutex(com_resourcemutex);\n#endif\n\ttex = Image_FindTexture(identifier, subpath, flags);\n\tif (tex)\n\t{\n\t\t//FIXME: race condition is possible here\n\t\t//if a non-replaced texture is given a fallback while a non-fallback version is still loading, it can still fail.\n\t\tif (tex->status == TEX_FAILED && fallbackdata)\n\t\t\ttex->status = TEX_LOADING;\n\t\telse if (tex->status != TEX_NOTLOADED)\n\t\t{\n#ifdef LOADERTHREAD\n\t\t\tSys_UnlockMutex(com_resourcemutex);\n#endif\n\t\t\treturn tex;\t//already exists\n\t\t}\n\t\ttex->flags = flags;\n\t}\n\telse\n\t\ttex = Image_CreateTexture_Internal(identifier, subpath, flags);\n\n\ttex->status = TEX_LOADING;\n\tif (fallbackdata)\n\t{\n\t\tint b = fallbackwidth*fallbackheight, pb = 0;\n\t\tswitch(fallbackfmt)\n\t\t{\n\t\tcase TF_INVALID:\n\t\t\tb = fallbackwidth;\n\t\t\tpb = fallbackheight;\n\t\t\tbreak;\n\t\tcase TF_8PAL24:\n\t\t\tpb = 3*256;\n\t\t\tb *= 1;\n\t\t\tbreak;\n\t\tcase TF_8PAL32:\n\t\t\tpb = 4*256;\n\t\t\tb *= 1;\n\t\t\tbreak;\n\t\tcase PTI_R8:\n\t\tcase PTI_P8:\n\t\tcase TF_SOLID8:\n\t\tcase TF_TRANS8:\n\t\tcase TF_TRANS8_FULLBRIGHT:\n\t\tcase TF_H2_T7G1:\n\t\tcase TF_H2_TRANS8_0:\n\t\tcase TF_H2_T4A4:\n\t\tcase TF_HEIGHT8:\n\t\tcase TF_HEIGHT8PAL:\t//we don't care about the actual palette.\n\t\t\tb *= 1;\n\t\t\tbreak;\n//\t\tcase PTI_LLLX8:\n//\t\tcase PTI_LLLA8:\n\t\tcase TF_RGBX32:\n\t\tcase TF_RGBA32:\n\t\tcase TF_BGRX32:\n\t\tcase TF_BGRA32:\n\t\t\tb *= 4;\n\t\t\tbreak;\n\t\tcase TF_MIP4_8PAL24:\n\t\tcase TF_MIP4_8PAL24_T255:\n\t\t\tpb = 3*256;\n\t\tcase TF_MIP4_P8:\n\t\tcase TF_MIP4_SOLID8:\n\t\t\tb = (fallbackwidth>>0)*(fallbackheight>>0) +\n\t\t\t\t(fallbackwidth>>1)*(fallbackheight>>1) +\n\t\t\t\t(fallbackwidth>>2)*(fallbackheight>>2) +\n\t\t\t\t(fallbackwidth>>3)*(fallbackheight>>3);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\t{\n\t\t\t\tunsigned int bb, bw, bh, bd;\n\t\t\t\tunsigned int lev;\n\t\t\t\tImage_BlockSizeForEncoding(fallbackfmt&~PTI_FULLMIPCHAIN, &bb, &bw, &bh, &bd);\n\t\t\t\tfor (b=0, lev = 0; fallbackwidth>>lev||fallbackheight>>lev; lev++)\n\t\t\t\t{\n\t\t\t\t\tb += bb * (max(1,fallbackwidth>>lev)+bw-1)/bw * (max(1,fallbackheight>>lev)+bh-1)/bh;// * (max(1,fallbackdepth>>lev)+bd-1)/bd;\n\t\t\t\t\tif (!(fallbackfmt&PTI_FULLMIPCHAIN))\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t\ttex->fallbackdata = BZ_Malloc(b + pb);\n\t\tmemcpy(tex->fallbackdata, fallbackdata, b);\n\t\tif (pb)\n\t\t\tmemcpy((qbyte*)tex->fallbackdata + b, fallbackpalette, pb);\n\t\ttex->fallbackwidth = fallbackwidth;\n\t\ttex->fallbackheight = fallbackheight;\n\t\ttex->fallbackfmt = fallbackfmt;\n\t}\n\telse\n\t{\n\t\ttex->fallbackdata = NULL;\n\t\ttex->fallbackwidth = 0;\n\t\ttex->fallbackheight = 0;\n\t\ttex->fallbackfmt = TF_INVALID;\n\t}\n#ifdef LOADERTHREAD\n\tSys_UnlockMutex(com_resourcemutex);\n#endif\n\t//FIXME: pass fallback through this way instead?\n\n\tif (dontposttoworker)\n\t\tImage_LoadHiResTextureWorker(tex, NULL, 0, 0);\n\telse\n\t{\n#ifdef WEBCLIENT\n\t\tif (!strncmp(tex->ident, \"http://\", 7) || !strncmp(tex->ident, \"https://\", 8))\n\t\t{\n\t\t\tstruct dl_download *dl;\n\t\t\tsize_t sizelimit = max(0,r_image_downloadsizelimit.ival);\n\t\t\tif (sizelimit>0 || !*r_image_downloadsizelimit.string)\n\t\t\t\tdl = HTTP_CL_Get(tex->ident, NULL, Image_Downloaded);\n\t\t\telse\n\t\t\t{\n\t\t\t\tCon_Printf(\"r_image_downloadsizelimit: image downloading is blocked\\n\");\n\t\t\t\tdl = NULL;\n\t\t\t}\n\t\t\tif (dl)\n\t\t\t{\n\t\t\t\tif (sizelimit)\n\t\t\t\t\tdl->sizelimit = sizelimit;\n\t\t\t\tdl->user_ctx = tex;\n\t\t\t\tdl->file = VFSPIPE_Open(1, false);\n\t\t\t\tdl->isquery = true;\n\t\t\t}\n#ifdef MULTITHREAD\n\t\t\tDL_CreateThread(dl, NULL, NULL);\n#else\n\t\t\ttex->status = TEX_FAILED;\t//HACK: so nothing waits for it.\n#endif\n\t\t}\n\t\telse\n#endif\n\t\t\tif (highpri)\n\t\t\tCOM_InsertWork(WG_LOADER, Image_LoadHiResTextureWorker, tex, NULL, 0, 0);\n\t\telse if (lowpri)\n\t\t\tCOM_AddWork(WG_LOADER, Image_LoadHiResTextureWorker, tex, NULL, 0, 0);\n\t\telse\n\t\t\tCOM_AddWork(WG_LOADER, Image_LoadHiResTextureWorker, tex, NULL, 0, 0);\n\t}\n\treturn tex;\n}\nvoid Image_Upload\t\t\t(texid_t tex, uploadfmt_t fmt, void *data, void *palette, int width, int height, int depth, unsigned int flags)\n{\n\tstruct pendingtextureinfo mips;\n\tsize_t i;\n\n\t//skip if we're not actually changing the data/size/format.\n\tif (!data && tex->format == fmt && tex->width == width && tex->height == height && tex->depth == depth && tex->status == TEX_LOADED)\n\t\treturn;\n\n\tmips.extrafree = NULL;\n\tmips.type = (flags&IF_TEXTYPEMASK)>>IF_TEXTYPESHIFT;\n\tif (!Image_GenMip0(&mips, flags, data, palette, width, height, depth, fmt, false))\n\t\treturn;\n\tImage_GenerateMips(&mips, flags);\n\tImage_ChangeFormatFlags(&mips, flags, fmt, tex->ident);\n\trf->IMG_LoadTextureMips(tex, &mips);\n\ttex->format = fmt;\n\ttex->width = width;\n\ttex->height = height;\n\ttex->depth = depth;\n\ttex->status = TEX_LOADED;\n\n\tfor (i = 0; i < mips.mipcount; i++)\n\t\tif (mips.mip[i].needfree)\n\t\t\tBZ_Free(mips.mip[i].data);\n\tif (mips.extrafree)\n\t\tBZ_Free(mips.extrafree);\n}\n\ntypedef struct\n{\n\tchar *name;\n\tchar *legacyname;\n\tint\tmaximize, minmip, minimize;\n\tint pad;\n} texmode_t;\nstatic texmode_t texmodes[] = {\n\t{\"n\",\t\"GL_NEAREST\",\t\t\t\t\t0,\t-1,\t0},\n\t{\"l\",\t\"GL_LINEAR\",\t\t\t\t\t1,\t-1,\t1},\n\t{\"nn\",\t\"GL_NEAREST_MIPMAP_NEAREST\",\t0,\t0,\t0},\n\t{\"ln\",\t\"GL_LINEAR_MIPMAP_NEAREST\",\t\t1,\t0,\t1},\n\t{\"nl\",\t\"GL_NEAREST_MIPMAP_LINEAR\",\t\t0,\t1,\t0},\n\t{\"ll\",\t\"GL_LINEAR_MIPMAP_LINEAR\",\t\t1,\t1,\t1},\n\n\t//more explicit names (dupes of the above)\n\t{\"n.n\",\tNULL,\t\t\t\t\t\t\t0,\t-1,\t0},\n\t{\"l.l\",\tNULL,\t\t\t\t\t\t\t1,\t-1,\t1},\n\t{\"nnn\",\tNULL,\t\t\t\t\t\t\t0,\t0,\t0},\n\t{\"lnl\",\tNULL,\t\t\t\t\t\t\t1,\t0,\t1},\n\t{\"nln\",\tNULL,\t\t\t\t\t\t\t0,\t1,\t0},\n\t{\"lll\",\tNULL,\t\t\t\t\t\t\t1,\t1,\t1},\n\n\t//inverted mag filters\n\t{\"n.l\",\tNULL,\t\t\t\t\t\t\t0,\t-1,\t1},\n\t{\"l.n\",\tNULL,\t\t\t\t\t\t\t1,\t-1,\t0},\n\t{\"nnl\",\tNULL,\t\t\t\t\t\t\t0,\t0,\t1},\n\t{\"lnn\",\tNULL,\t\t\t\t\t\t\t1,\t0,\t0},\n\t{\"nll\",\tNULL,\t\t\t\t\t\t\t0,\t1,\t1},\n\t{\"lln\",\tNULL,\t\t\t\t\t\t\t1,\t1,\t0}\n};\nstatic void Image_ParseTextureMode(char *cvarname, char *modename, int modes[3])\n{\n\tint i;\n\tmodes[0] = 1;\n\tmodes[1] = 0;\n\tmodes[2] = 1;\n\tfor (i = 0; i < sizeof(texmodes) / sizeof(texmodes[0]); i++)\n\t{\n\t\tif (!Q_strcasecmp(modename, texmodes[i].name) || (texmodes[i].legacyname && !Q_strcasecmp(modename, texmodes[i].legacyname)))\n\t\t{\n\t\t\tmodes[0] = texmodes[i].minimize;\n\t\t\tmodes[1] = texmodes[i].minmip;\n\t\t\tmodes[2] = texmodes[i].maximize;\n\t\t\treturn;\n\t\t}\n\t}\n\tCon_Printf(\"%s: mode %s was not recognised\\n\", cvarname, modename);\n}\nvoid QDECL Image_TextureMode_Callback (struct cvar_s *var, char *oldvalue)\n{\n\tint mip[3]={1,0,1}, pic[3]={1,-1,1}, mipcap[2] = {0, 1000};\n\tfloat anis = 1, lodbias = 0;\n\tchar *s;\n\textern cvar_t gl_texturemode, gl_texturemode2d, gl_texture_anisotropic_filtering, gl_texture_lodbias, gl_mipcap;\n\n\tImage_ParseTextureMode(gl_texturemode.name, gl_texturemode.string, mip);\n\tImage_ParseTextureMode(gl_texturemode2d.name, gl_texturemode2d.string, pic);\n\tanis = gl_texture_anisotropic_filtering.value;\n\t//parse d_mipcap (two values, nearest furthest)\n\ts = COM_Parse(gl_mipcap.string);\n\tmipcap[0] = *com_token?atoi(com_token):0;\n//\tif (mipcap[0] > 3)\t/*cap it to 3, so no 16*16 textures get bugged*/\n//\t\tmipcap[0] = 3;\n\ts = COM_Parse(s);\n\tmipcap[1] = *com_token?atoi(com_token):1000;\n\tif (mipcap[1] < mipcap[0])\n\t\tmipcap[1] = mipcap[0];\n\tlodbias = gl_texture_lodbias.value;\n\n\tif (rf && rf->IMG_UpdateFiltering)\n\t\trf->IMG_UpdateFiltering(imagelist, mip, pic, mipcap, lodbias, anis);\n}\nqboolean Image_UnloadTexture(image_t *tex)\n{\n\tif (tex->status == TEX_LOADED)\n\t{\n\t\trf->IMG_DestroyTexture(tex);\n\t\ttex->status = TEX_NOTLOADED;\n\t\treturn true;\n\t}\n\treturn false;\n}\n\n//nukes an existing texture, destroying all traces. any lingering references will cause problems, so be careful about how you access these.\nvoid Image_DestroyTexture(image_t *tex)\n{\n\timage_t **link;\n\tif (!tex)\n\t\treturn;\n\tTEXDOWAIT(tex);\t//just in case.\n#ifdef LOADERTHREAD\n\tSys_LockMutex(com_resourcemutex);\n#endif\n\tImage_UnloadTexture(tex);\n\n\tfor (link = &imagelist; *link; link = &(*link)->next)\n\t{\n\t\tif (*link == tex)\n\t\t{\n\t\t\t*link = tex->next;\n\t\t\tbreak;\n\t\t}\n\t}\n#ifdef LOADERTHREAD\n\tSys_UnlockMutex(com_resourcemutex);\n#endif\n\tif (*tex->ident)\n\t\tHash_RemoveDataInsensitive(&imagetable, tex->ident, tex);\n\tZ_Free(tex);\n}\n\nvoid Image_Purge(void)\n{\n\timage_t *tex;\n\tif (r_keepimages.ival)\n\t\treturn;\n\tShader_TouchTextures();\n\tfor (tex = imagelist; tex; tex = tex->next)\n\t{\n\t\tif (tex->flags & IF_NOPURGE)\n\t\t\tcontinue;\n\t\tif (tex->regsequence != r_regsequence)\n\t\t\tImage_UnloadTexture(tex);\n\t}\n}\n\n\nvoid Image_List_f(void)\n{\n\tflocation_t loc;\n\timage_t *tex, *a;\n\tint loaded = 0, aliases = 0, failed = 0, total = 0;\n\tsize_t drivermem = 0;\n\tsize_t aliasedmem = 0;\n\tsize_t imgmem;\n\tunsigned int loadflags;\n\tchar fname[MAX_QPATH];\n\tconst char *filter = Cmd_Argv(1);\n\tqboolean hasdupes;\n\tfor (tex = imagelist; tex; tex = tex->next)\n\t{\n\t\ttotal++;\n\t\tif (*filter && !strstr(tex->ident, filter))\n\t\t\tcontinue;\n\t\tif (tex->status == TEX_FAILED && !developer.ival)\n\t\t{\t//don't show all the failures.\n\t\t\tfailed++;\n\t\t\tcontinue;\n\t\t}\n\n\t\ta = Hash_GetInsensitive(&imagetable, tex->ident);\n\t\thasdupes = (a != tex || Hash_GetNextInsensitive(&imagetable, tex->ident, a));\n\n\t\tif (((tex->flags&IF_TEXTYPEMASK)>>IF_TEXTYPESHIFT) == PTI_2D || ((tex->flags&IF_TEXTYPEMASK)>>IF_TEXTYPESHIFT) == PTI_CUBE)\n\t\t\tCon_Printf(\"^[\\\\imgptr\\\\%#\"PRIxSIZE\"^]\", (size_t)tex);\n\t\tif (tex->subpath)\n\t\t\tCon_Printf(\"^h(%s)^h\", tex->subpath);\n//\t\tCon_DLPrintf(1, \" %x\", tex->flags);\n\n\t\tif (Image_LocateHighResTexture(tex, &loc, fname, sizeof(fname), &loadflags))\n\t\t{\n\t\t\tchar defuck[MAX_OSPATH], *bullshit;\n\t\t\tQ_strncpyz(defuck, loc.search->logicalpath, sizeof(defuck));\n\t\t\twhile((bullshit=strchr(defuck, '\\\\')))\n\t\t\t\t*bullshit = '/';\n\n\t\t\tif (((tex->flags&IF_TEXTYPEMASK)>>IF_TEXTYPESHIFT) == PTI_2D||((tex->flags&IF_TEXTYPEMASK)>>IF_TEXTYPESHIFT) == PTI_CUBE || tex->format == PTI_P8)\n\t\t\t\tCon_Printf(\"^[%s\\\\tip\\\\%s/%s\\n\\n%gkb\\\\tipimgptr\\\\%#\"PRIxSIZE\"^]: \", tex->ident, defuck, fname, loc.len/1024.0f, (size_t)tex);\n\t\t\telse\n\t\t\t\tCon_Printf(\"^[%s\\\\tip\\\\%s/%s\\n\\n%gkb^]: \", tex->ident, defuck, fname, loc.len/1024.0f);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tloc.len = 0;\n\t\t\tCon_Printf(\"^[%s\\\\tipimgptr\\\\%#\"PRIxSIZE\"^]: \", tex->ident, (size_t)tex);\n\t\t}\n\n\t\tfor (a = tex->aliasof; a; a = a->aliasof)\n\t\t{\n\t\t\tif (a->subpath)\n\t\t\t\tCon_Printf(\"^3^h(%s)^h%s: \", a->subpath, a->ident);\n\t\t\telse\n\t\t\t\tCon_Printf(\"^3%s: \", a->ident);\n\t\t}\n\n\t\tif (developer.ival || hasdupes)\n\t\t{\n\t\t\tif (tex->flags & IF_CLAMP)\t\t\tCon_Printf(\"^[^8CLAMP\\\\tip\\\\clamp to edge^] \");\n\t\t\tif (tex->flags & IF_NOMIPMAP)\t\tCon_Printf(\"^[^8NOMIPMAP\\\\tip\\\\disable mipmaps.^] \");\n\t\t\tif (tex->flags & IF_NEAREST)\t\tCon_Printf(\"^[^8NEAREST\\\\tip\\\\force nearest^] \");\n\t\t\tif (tex->flags & IF_LINEAR)\t\t\tCon_Printf(\"^[^8LINEAR\\\\tip\\\\force linear^] \");\n\t\t\tif (tex->flags & IF_UIPIC)\t\t\tCon_Printf(\"^[^8UIPIC\\\\tip\\\\subject to texturemode2d^] \");\n\t\t\tif (tex->flags & IF_SRGB)\t\t\tCon_Printf(\"^[^8SRGB\\\\tip\\\\texture data is srgb (read-as-linear)^] \");\n\t\t\tif (tex->flags & IF_NOPICMIP)\t\tCon_Printf(\"^[^8NOPICMIP\\\\tip\\\\ignores picmip settings^] \");\n\t\t\tif (tex->flags & IF_NOALPHA)\t\tCon_Printf(\"^[^8NOALPHA\\\\tip\\\\hint rather than requirement^] \");\n\t\t\tif (tex->flags & IF_NOGAMMA)\t\tCon_Printf(\"^[^8NOGAMMA\\\\tip\\\\do not apply legacy texture-based gamma^] \");\n\t\t\tswitch((tex->flags&IF_TEXTYPEMASK)>>IF_TEXTYPESHIFT)\n\t\t\t{\n\t\t\tcase PTI_2D:\t\t\t\t\t\tCon_Printf(\"^[^82D\\\\tip\\\\image is a standard 2d image^] \");break;\n\t\t\tcase PTI_3D:\t\t\t\t\t\tCon_Printf(\"^[^83D\\\\tip\\\\image is a 3d image^] \");break;\n\t\t\tcase PTI_CUBE:\t\t\t\t\t\tCon_Printf(\"^[^8CUBE\\\\tip\\\\image is a cubemap^] \");break;\n\t\t\tcase PTI_2D_ARRAY:\t\t\t\t\tCon_Printf(\"^[^82D_ARRAY\\\\tip\\\\image is an array of 2d images^] \");break;\n\t\t\tcase PTI_CUBE_ARRAY:\t\t\t\tCon_Printf(\"^[^8CUBE_ARRAY\\\\tip\\\\image is an array of cubemaps^] \");break;\n\t\t\tcase PTI_ANY:\t\t\t\t\t\tCon_Printf(\"^[^8ANY\\\\tip\\\\image type is unspecified, allowing any^] \");break;\n\t\t\tdefault:\t\t\t\t\t\t\tCon_Printf(\"^[^8<ERROR>\\\\tip\\\\image type not defined^] \");break;\n\t\t\t}\n\t\t\tif (tex->flags & IF_MIPCAP)\t\t\tCon_Printf(\"^[^8MIPCAP\\\\tip\\\\allow the use of d_mipcap^] \");\n\t\t\tif (tex->flags & IF_PREMULTIPLYALPHA)Con_Printf(\"^[^8PREMULTIPLYALPHA\\\\tip\\\\rgb *= alpha^] \");\n\t\t\tif (tex->flags & IF_UNUSED15)\t\tCon_Printf(\"^[^8UNUSED15\\\\tip\\\\...^] \");\n\t\t\tif (tex->flags & IF_UNUSED16)\t\tCon_Printf(\"^[^8UNUSED16\\\\tip\\\\...^] \");\n\t\t\tif (tex->flags & IF_INEXACT)\t\tCon_Printf(\"^[^8INEXACT\\\\tip\\\\subdir info isn't to be used for matching^] \");\n\t\t\tif (tex->flags & IF_WORLDTEX)\t\tCon_Printf(\"^[^8WORLDTEX\\\\tip\\\\gl_picmip_world^] \");\n\t\t\tif (tex->flags & IF_SPRITETEX)\t\tCon_Printf(\"^[^8SPRITETEX\\\\tip\\\\gl_picmip_sprites^] \");\n\t\t\tif (tex->flags & IF_NOSRGB)\t\t\tCon_Printf(\"^[^8NOSRGB\\\\tip\\\\ignore srgb when loading. this is guarenteed to be linear, for normalmaps etc.^] \");\n\t\t\tif (tex->flags & IF_PALETTIZE)\t\tCon_Printf(\"^[^8PALETTIZE\\\\tip\\\\convert+load it as an RTI_P8 texture for the current palette/colourmap^] \");\n\t\t\tif (tex->flags & IF_NOPURGE)\t\tCon_Printf(\"^[^8NOPURGE\\\\tip\\\\texture is not flushed when no more shaders refer to it (for C code that holds a permanant reference to it - still purged on vid_reloads though)^] \");\n\t\t\tif (tex->flags & IF_HIGHPRIORITY)\tCon_Printf(\"^[^8HIGHPRIORITY\\\\tip\\\\pushed to start of worker queue instead of end...^] \");\n\t\t\tif (tex->flags & IF_LOWPRIORITY)\tCon_Printf(\"^[^8LOWPRIORITY\\\\tip\\\\load slowly to favour others instead^] \");\n\t\t\tif (tex->flags & IF_LOADNOW)\t\tCon_Printf(\"^[^8LOADNOW\\\\tip\\\\hit the disk now, and delay the gl load until its actually needed. this is used only so that the width+height are known in advance. valid on worker threads.^] \");\n\t\t\tif (tex->flags & IF_NOPCX)\t\t\tCon_Printf(\"^[^8NOPCX\\\\tip\\\\block pcx format. meaning qw skins can use team colours and cropping^] \");\n\t\t\tif (tex->flags & IF_TRYBUMP)\t\tCon_Printf(\"^[^8TRYBUMP\\\\tip\\\\attempt to load _bump if the specified _norm texture wasn't found^] \");\n\t\t\tif (tex->flags & IF_RENDERTARGET)\tCon_Printf(\"^[^8RENDERTARGET\\\\tip\\\\never loaded from disk, loading can't fail^] \");\n\t\t\tif (tex->flags & IF_EXACTEXTENSION)\tCon_Printf(\"^[^8EXACTEXTENSION\\\\tip\\\\don't mangle extensions, use what is specified and ONLY that^] \");\n\t\t\tif (tex->flags & IF_NOREPLACE)\t\tCon_Printf(\"^[^8NOREPLACE\\\\tip\\\\don't load a replacement, for some reason^] \");\n\t\t\tif (tex->flags & IF_NOWORKER)\t\tCon_Printf(\"^[^8NOWORKER\\\\tip\\\\don't pass the work to a loader thread. this gives fully synchronous loading. only valid from the main thread.^] \");\n\t\t}\n\n\t\tif (tex->status == TEX_LOADED)\n\t\t{\n\t\t\tchar *type;\n\t\t\tunsigned int blockbytes, blockwidth, blockheight, blockdepth;\n\t\t\tImage_BlockSizeForEncoding(tex->format, &blockbytes, &blockwidth, &blockheight, &blockdepth);\n\t\t\timgmem = blockbytes * (tex->width+blockwidth-1)/blockwidth * (tex->height+blockheight-1)/blockheight * (tex->depth+blockdepth-1)/blockdepth;\n\t\t\tswitch((tex->flags & IF_TEXTYPEMASK)>>IF_TEXTYPESHIFT)\n\t\t\t{\n\t\t\tcase PTI_2D:\t\ttype = \"\";\t\t\tbreak;\n\t\t\tcase PTI_3D:\t\ttype = \"3D \";\t\tbreak;\n\t\t\tcase PTI_CUBE:\t\ttype = \"Cube \";\t\tbreak;\n\t\t\tcase PTI_2D_ARRAY:\ttype = \"Array \";\t\tbreak;\n\t\t\tcase PTI_CUBE_ARRAY:type = \"CubeArray \";\tbreak;\n\t\t\tdefault:\t\t\ttype = \"UNKNOWN \";\tbreak;\n\t\t\t}\n\t\t\tif (!(tex->flags & IF_NOMIPMAP))\n\t\t\t\timgmem += imgmem/3;\t//mips take about a third extra mem.\n\n\t\t\t//FIXME: not showing sizes here, because they show the '8bit' size rather than any replacementment's size.\n\n\t\t\tif (tex->depth != 1)\n\t\t\t\tCon_Printf(\"^2loaded (%s%i*%i*%i ^[^4%s\\\\tip\\\\%g bits per texel^])\\n\", type, tex->width, tex->height, tex->depth, Image_FormatName(tex->format), (8.f*blockbytes)/(blockwidth*blockheight*blockdepth));\n\t\t\telse\n\t\t\t\tCon_Printf(\"^2loaded (%s%i*%i ^[^4%s\\\\tip\\\\%g bits per texel^])\\n\", type, tex->width, tex->height, Image_FormatName(tex->format), (8.f*blockbytes)/(blockwidth*blockheight*blockdepth));\n\t\t\tif (tex->aliasof)\n\t\t\t{\n\t\t\t\taliasedmem += imgmem;\n\t\t\t\taliases++;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tdrivermem += imgmem;\n\t\t\t\tloaded++;\n\t\t\t}\n\t\t}\n\t\telse if (tex->status == TEX_FAILED)\n\t\t{\n\t\t\tCon_Printf(\"^1failed\\n\");\n\t\t\tfailed++;\n\t\t}\n\t\telse if (tex->status == TEX_NOTLOADED)\n\t\t\tCon_Printf(\"^5not loaded\\n\");\n\t\telse\n\t\t\tCon_Printf(\"^bloading\\n\");\n\t}\n\n\tCon_Printf(\"%i images total\\n\", total);\n\tCon_Printf(\"%i images loaded (%gMB)\\n\", loaded, drivermem/(1024*1024.0));\n\tCon_Printf(\"%i image alises (%gMB)\\n\", aliases, aliasedmem/(1024*1024.0));\n\tCon_Printf(\"%i images failed\\n\", failed);\n}\n\nvoid Image_Formats_f(void)\n{\n\tsize_t i;\n\tfloat bpp;\n\tint blockbytes, blockwidth, blockheight, blockdepth;\n\n#ifdef GLQUAKE\n\tif (qrenderer == QR_OPENGL)\n\t{\n\t\tCon_Printf(\"OpenGL info:\\n\");\n\t\tCon_Printf(\"OpenGL Version: %s%g\\n\", gl_config.gles?\"ES \":\"\", gl_config.glversion);\n\t\tCon_Printf(\"OpenGLSL Version: %i\\n\", gl_config.maxglslversion);\n\t\tCon_Printf(\"OpenGLSL Attributes: %u\\n\", gl_config.maxattribs);\n\n\t\tCon_Printf(\"arb_texture_env_combine: %u\\n\", gl_config.arb_texture_env_combine);\n\t\tCon_Printf(\"arb_texture_env_dot3: %u\\n\", gl_config.arb_texture_env_dot3);\n\t\tCon_Printf(\"arb_texture_compression: %u\\n\", gl_config.arb_texture_compression);\n\t\tCon_Printf(\"geometryshaders: %u\\n\", gl_config.geometryshaders);\n\t\tCon_Printf(\"ext_framebuffer_objects: %u\\n\", gl_config.ext_framebuffer_objects);\n\t\tCon_Printf(\"arb_framebuffer_srgb: %u\\n\", gl_config.arb_framebuffer_srgb);\n\t\tCon_Printf(\"arb_shader_objects: %u\\n\", gl_config.arb_shader_objects);\n\t\tCon_Printf(\"arb_shadow: %u\\n\", gl_config.arb_shadow);\n\t\tCon_Printf(\"arb_depth_texture: %u\\n\", gl_config.arb_depth_texture);\n\t\tCon_Printf(\"ext_stencil_wrap: %u\\n\", gl_config.ext_stencil_wrap);\n\t\tCon_Printf(\"ext_packed_depth_stencil: %u\\n\", gl_config.ext_packed_depth_stencil);\n\t\tCon_Printf(\"arb_depth_clamp: %u\\n\", gl_config.arb_depth_clamp);\n\t\tCon_Printf(\"ext_texture_filter_anisotropic: %u\\n\", gl_config.ext_texture_filter_anisotropic);\n\t}\n#endif\n\n\tCon_Printf(\t\t\"            Programs: \"S_COLOR_GREEN\"%s\\n\", sh_config.progs_supported?va(sh_config.progpath, \"*\"):S_COLOR_RED\"Unsupported\");\n\tif (sh_config.progs_supported)\n\t{\n\t\tCon_Printf(\t\"     Shader versions: %u - %u\\n\", sh_config.minver, sh_config.maxver);\n\t\tCon_Printf(\t\"       Max GPU Bones: %s%u\\n\", sh_config.max_gpu_bones?S_COLOR_GREEN:S_COLOR_RED, sh_config.max_gpu_bones);\n\t}\n\tCon_Printf(\t\t\"     Legacy Pipeline: %s\\n\", sh_config.progs_required?S_COLOR_RED\"Unsupported\":S_COLOR_GREEN\"Supported\");\n\tif (!sh_config.progs_required)\n\t{\n\t\tCon_Printf(\t\"       Env_Combiners: %s\\n\", sh_config.nv_tex_env_combine4?S_COLOR_GREEN\"Extended\":sh_config.tex_env_combine?S_COLOR_GREEN\"Supported\":S_COLOR_RED\"Unsupported\");\n\t\tCon_Printf(\t\"             Env_Add: %s\\n\", sh_config.env_add?S_COLOR_GREEN\"Supported\":S_COLOR_RED\"Unsupported\");\n\t}\n\tCon_Printf(\t\t\"  Max Texture2d Size: %s%u*%u\\n\", S_COLOR_GREEN, sh_config.texture2d_maxsize, sh_config.texture2d_maxsize);\n\tCon_Printf(\t\t\"Max Texture2d Layers: %s%u\\n\", sh_config.texture2darray_maxlayers?S_COLOR_GREEN:S_COLOR_RED, sh_config.texture2darray_maxlayers);\n\tCon_Printf(\t\t\"  Max Texture3d Size: %s%u*%u*%u\\n\", sh_config.texture3d_maxsize?S_COLOR_GREEN:S_COLOR_RED, sh_config.texture3d_maxsize, sh_config.texture3d_maxsize, sh_config.texture3d_maxsize);\n\tCon_Printf(\t\t\"Max TextureCube Size: %s%u*%u\\n\", sh_config.havecubemaps?S_COLOR_GREEN:S_COLOR_RED, sh_config.texturecube_maxsize, sh_config.texturecube_maxsize);\n\tCon_Printf(\t\t\"    Non-Power-Of-Two: %s%s\\n\", sh_config.texture_non_power_of_two?S_COLOR_GREEN\"Supported\":(sh_config.texture_non_power_of_two_pic?S_COLOR_YELLOW\"Limited\":S_COLOR_RED\"Unsupported\"), sh_config.npot_rounddown?\" (rounded down)\":\"\");\n\tCon_Printf(\t\t\"  Block Size Padding: %s\\n\", sh_config.texture_allow_block_padding?S_COLOR_GREEN\"Supported\":S_COLOR_RED\"Unsupported\");\n\tCon_Printf(\t\t\"              Mipcap: %s\\n\", sh_config.can_mipcap?S_COLOR_GREEN\"Supported\":S_COLOR_RED\"Unsupported\");\n\n\tCon_Printf(\t\t\"\\n      Driver Support:\\n\");\n\tfor (i = 0; i < PTI_MAX; i++)\n\t{\n\t\tswitch(i)\n\t\t{\n\t\tcase PTI_EMULATED:\n\t\t\tcontinue;\n\t\tdefault:\n\t\t\tbreak;\n\t\t}\n\t\tImage_BlockSizeForEncoding(i, &blockbytes, &blockwidth, &blockheight, &blockdepth);\n\t\tbpp = blockbytes*8.0/(blockwidth*blockheight*blockdepth);\n\t\tif (blockbytes)\n\t\t\tCon_Printf(\"%20s: %s\"S_COLOR_GRAY\" (%s%.3g-bpp)\\n\", Image_FormatName(i), sh_config.texfmt[i]?S_COLOR_GREEN\"Enabled\":S_COLOR_RED\"Disabled\", (blockdepth!=1)?\"3d, \":\"\", bpp);\n\t\telse\n\t\t\tCon_Printf(\"%20s: %s\\n\", Image_FormatName(i), sh_config.texfmt[i]?S_COLOR_GREEN\"Enabled\":S_COLOR_RED\"Disabled\");\n\t}\n}\n\n\n//load the named file, without failing.\ntexid_t R_LoadHiResTexture(const char *name, const char *subpath, unsigned int flags)\n{\n\tchar nicename[MAX_QPATH], *data;\n\tif (!*name)\n\t\treturn r_nulltex;\n\tQ_strncpyz(nicename, name, sizeof(nicename));\n\twhile((data = strchr(nicename, '*')))\n\t\t*data = '#';\n\treturn Image_GetTexture(nicename, subpath, flags, NULL, NULL, 0, 0, TF_INVALID);\t//queues the texture creation.\n}\n\n//attempt to load the named texture\n//will not load external textures if gl_load24bit is set (failing instantly if its just going to fail later on anyway)\n//the specified data will be used if the high-res image is blocked/not found.\ntexid_t R_LoadReplacementTexture(const char *name, const char *subpath, unsigned int flags, void *lowres, int lowreswidth, int lowresheight, uploadfmt_t format)\n{\n\tchar nicename[MAX_QPATH], *data;\n\tif (!*name)\n\t\treturn r_nulltex;\n\tif (!gl_load24bit.ival && !lowres)\n\t\treturn r_nulltex;\n\tQ_strncpyz(nicename, name, sizeof(nicename));\n\twhile((data = strchr(nicename, '*')))\n\t\t*data = '#';\n\treturn Image_GetTexture(nicename, subpath, flags, lowres, NULL, lowreswidth, lowresheight, format);\t//queues the texture creation.\n}\n#ifdef RTLIGHTS\nvoid R_LoadNumberedLightTexture(dlight_t *dl, int cubetexnum)\n{\n\tQ_snprintfz(dl->cubemapname, sizeof(dl->cubemapname), \"cubemaps/%i\", cubetexnum);\n\tif (!gl_load24bit.ival)\n\t\tdl->cubetexture = r_nulltex;\n\telse\n\t\tdl->cubetexture = Image_GetTexture(dl->cubemapname, NULL, IF_TEXTYPE_CUBE, NULL, NULL, 0, 0, TF_INVALID);\n}\n#endif\n\n//destroys all textures\nvoid Image_Shutdown(void)\n{\n\timage_t *tex;\n\tint i = 0, j = 0;\n\tCmd_RemoveCommand(\"r_imagelist\");\n\tCmd_RemoveCommand(\"r_imageformats\");\n\twhile (imagelist)\n\t{\n\t\ttex = imagelist;\n\t\tif (*tex->ident)\n\t\t\tHash_RemoveDataInsensitive(&imagetable, tex->ident, tex);\n\t\timagelist = tex->next;\n\t\tif (tex->status == TEX_LOADED)\n\t\t\tj++;\n\t\trf->IMG_DestroyTexture(tex);\n\t\tZ_Free(tex);\n\t\ti++;\n\t}\n\tif (i)\n\t\tCon_DPrintf(\"Destroyed %i/%i images\\n\", j, i);\n\n\tif (wadmutex)\n\t\tSys_DestroyMutex(wadmutex);\n\twadmutex = NULL;\n}\n#endif\n\n//may not create any images yet.\nvoid Image_Init(void)\n{\n\tstatic qboolean initedlibs;\n\tif (!initedlibs)\n\t{\n\t\tinitedlibs = true;\n\t\t#ifdef AVAIL_JPEGLIB\n\t\t\tLibJPEG_Init();\n\t\t#endif\n\t\t#ifdef AVAIL_PNGLIB\n\t\t\tLibPNG_Init();\n\t\t#endif\n\t\t#ifdef IMAGEFMT_EXR\n\t\t\tInitLibrary_OpenEXR();\n\t\t#endif\n\t}\n\n#ifdef HAVE_CLIENT\n\twadmutex = Sys_CreateMutex();\n\tmemset(imagetablebuckets, 0, sizeof(imagetablebuckets));\n\tHash_InitTable(&imagetable, sizeof(imagetablebuckets)/sizeof(imagetablebuckets[0]), imagetablebuckets);\n\n\tCmd_AddCommandD(\"r_imagelist\", Image_List_f, \"Prints out a list of the currently-known textures.\");\n\tCmd_AddCommandD(\"r_imageformats\", Image_Formats_f, \"Prints out a list of the usable hardware pixel formats.\");\n#endif\n}\n\n#ifdef HAVE_CLIENT\n// ocrana led functions\nstatic int ledcolors[8][3] =\n{\n\t// green\n\t{ 0, 255, 0 },\n\t{ 0, 127, 0 },\n\t// red\n\t{ 255, 0, 0 },\n\t{ 127, 0, 0 },\n\t// yellow\n\t{ 255, 255, 0 },\n\t{ 127, 127, 0 },\n\t// blue\n\t{ 0, 0, 255 },\n\t{ 0, 0, 127 }\n};\n\nvoid AddOcranaLEDsIndexed (qbyte *image, int h, int w)\n{\n\tint tridx[8]; // transition indexes\n\tqbyte *point;\n\tint i, idx, x, y, rs;\n\tint r, g, b, rd, gd, bd;\n\n\t// calc row size, character size\n\trs = w;\n\th /= 16;\n\tw /= 16;\n\n\t// generate palettes\n\tfor (i = 0; i < 4; i++)\n\t{\n\t\t// get palette\n\t\tr = ledcolors[i*2][0];\n\t\tg = ledcolors[i*2][1];\n\t\tb = ledcolors[i*2][2];\n\t\trd = (r - ledcolors[i*2+1][0]) / 8;\n\t\tgd = (g - ledcolors[i*2+1][1]) / 8;\n\t\tbd = (b - ledcolors[i*2+1][2]) / 8;\n\t\tfor (idx = 0; idx < 8; idx++)\n\t\t{\n\t\t\ttridx[idx] = GetPaletteIndex(r, g, b);\n\t\t\tr -= rd;\n\t\t\tg -= gd;\n\t\t\tb -= bd;\n\t\t}\n\n\t\t// generate LED into image\n\t\tb = (w * w + h * h) / 16;\n\t\tif (b < 1)\n\t\t\tb = 1;\n\t\trd = w + 1;\n\t\tgd = h + 1;\n\n\t\tpoint = image + (8 * rs * h) + ((6 + i) * w);\n\t\tfor (y = 1; y <= h; y++)\n\t\t{\n\t\t\tfor (x = 1; x <= w; x++)\n\t\t\t{\n\t\t\t\tr = rd - (x*2); r *= r;\n\t\t\t\tg = gd - (y*2); g *= g;\n\t\t\t\tidx = (r + g) / b;\n\n\t\t\t\tif (idx > 7)\n\t\t\t\t\t*point++ = 0;\n\t\t\t\telse\n\t\t\t\t\t*point++ = tridx[idx];\n\t\t\t}\n\t\t\tpoint += rs - w;\n\t\t}\n\t}\n}\n\n/*\nFind closest color in the palette for named color\n*/\nint MipColor(int r, int g, int b)\n{\n\tint i;\n\tfloat dist;\n\tint best=15;\n\tfloat bestdist;\n\tint r1, g1, b1;\n\tstatic int lr = -1, lg = -1, lb = -1;\n\tstatic int lastbest;\n\n\tif (r == lr && g == lg && b == lb)\n\t\treturn lastbest;\n\n\tbestdist = 256*256*3;\n\n\tfor (i = 0; i < 256; i++)\n\t{\n\t\tr1 = host_basepal[i*3] - r;\n\t\tg1 = host_basepal[i*3+1] - g;\n\t\tb1 = host_basepal[i*3+2] - b;\n\t\tdist = r1*r1 + g1*g1 + b1*b1;\n\t\tif (dist < bestdist) {\n\t\t\tbestdist = dist;\n\t\t\tbest = i;\n\t\t}\n\t}\n\tlr = r; lg = g; lb = b;\n\tlastbest = best;\n\treturn best;\n}\n\n#ifdef STB_IMAGE_WRITE_IMPLEMENTATION\nstatic void stbiwritefunc(void *context, void *data, int size)\n{\n\tvfsfile_t *f = context;\n\tVFS_WRITE(f, data, size);\n}\n#endif\n\nqboolean SCR_ScreenShot (char *filename, enum fs_relative fsroot, void **buffer, int numbuffers, qintptr_t bytestride, int width, int height, enum uploadfmt fmt, qboolean writemeta)\n{\n\tchar ext[8];\n\tvoid *nbuffers[2];\n\n\tswitch(fmt)\n\t{\t//nuke any alpha channel...\n\tcase TF_RGBA32: fmt = TF_RGBX32; break;\n\tcase TF_BGRA32: fmt = TF_BGRX32; break;\n\tdefault: break;\n\t}\n\n\tif (!bytestride)\n\t\tbytestride = width*4;\n\tif (bytestride < 0)\n\t{\t//fix up the buffers so callers don't have to.\n\t\tint nb = numbuffers;\n\t\tfor (numbuffers = 0; numbuffers < nb && numbuffers < countof(nbuffers); numbuffers++)\n\t\tnbuffers[numbuffers] = (char*)buffer[numbuffers] - bytestride*(height-1);\n\t\tbuffer = nbuffers;\n\t}\n\n\tCOM_FileExtension(filename, ext, sizeof(ext));\n\n#ifdef STB_IMAGE_WRITE_IMPLEMENTATION\n\tif (numbuffers == 1)\n\t{\n\t\tvfsfile_t *outfile = NULL;\n\t\tqboolean ret = false;\n\t\tint comp = 0;\n\t\tif (comp == PTI_L8)\n\t\t\tcomp = 1;\n\t\telse if (comp == PTI_L8A8)\n\t\t\tcomp = 2;\n\t\telse if (comp == PTI_RGB8)\n\t\t\tcomp = 3;\n\t\telse if (comp == PTI_RGBA8)\n\t\t\tcomp = 4;\n\t\telse if (comp == PTI_RGBA32F)\n\t\t\tcomp = -4;\n\t\telse if (comp == PTI_R32F)\n\t\t\tcomp = -1;\n\n#ifdef STBIW_ONLY_PNG\n\t\tif (!Q_strcasecmp(ext, \"png\") && comp>0)\n\t\t{\t//does NOT support pngs\n\t\t\t//lacks metadata\n\t\t\toutfile=FS_OpenVFS(filename, \"wb\", FS_GAMEONLY);\n\t\t\tret = stbi_write_png_to_func(stbiwritefunc, outfile, width, height, comp, buffer[0], bytestride);\n\t\t}\n#endif\n#ifdef STBIW_ONLY_JPEG\n\t\tif ((!Q_strcasecmp(ext, \"jpg\") || !Q_strcasecmp(ext, \"jpeg\")) && comp>0 && bytestride == width*sizeof(float)*comp)\n\t\t{\n\t\t\t//lacks metadata\n\t\t\toutfile=FS_OpenVFS(filename, \"wb\", FS_GAMEONLY);\n\t\t\tret = stbi_write_jpg_to_func(stbiwritefunc, outfile, width, height, comp, buffer[0], scr_sshot_compression.value/*its in percent*/);\n\t\t}\n#endif\n#ifdef STBIW_ONLY_HDR\n\t\tif (!Q_strcasecmp(ext, \"hdr\") && comp<0 && bytestride == width*sizeof(float)*-comp)\n\t\t{\n\t\t\toutfile=FS_OpenVFS(filename, \"wb\", FS_GAMEONLY);\n\t\t\tret = stbi_write_hdr_to_func(stbiwritefunc, outfile, width, height, -comp, buffer[0]);\n\t\t}\n#endif\n#ifdef STBIW_ONLY_TGA\n\t\tif (!Q_strcasecmp(ext, \"tga\") && comp>0 && bytestride == width*sizeof(float)*comp)\n\t\t{\n\t\t\toutfile=FS_OpenVFS(filename, \"wb\", FS_GAMEONLY);\n\t\t\tret = stbi_write_tga_to_func(stbiwritefunc, outfile, width, height, comp, buffer[0]);\n\t\t}\n#endif\n#ifdef STBIW_ONLY_BMP\n\t\tif (!Q_strcasecmp(ext, \"bmp\") && comp>0 && bytestride == width*sizeof(float)*comp)\n\t\t{\n\t\t\toutfile=FS_OpenVFS(filename, \"wb\", FS_GAMEONLY);\n\t\t\tret = stbi_write_bmp_to_func(stbiwritefunc, outfile, width, height, comp, buffer[0]);\n\t\t}\n#endif\n\n\t\tif (outfile)\n\t\t{\n\t\t\tVFS_CLOSE(outfile);\n\t\t\treturn ret;\n\t\t}\n\t}\n#endif\n#ifdef AVAIL_PNGLIB\n\tif (!Q_strcasecmp(ext, \"png\") || !Q_strcasecmp(ext, \"pns\"))\n\t{\n\t\t//png can do bgr+rgb\n\t\t//rgba bgra will result in an extra alpha chan\n\t\t//actual stereo is also supported. huzzah.\n\t\treturn Image_WritePNG(filename, fsroot, scr_sshot_compression.value, buffer, numbuffers, bytestride, width, height, fmt, writemeta);\n\t}\n#endif\n#ifdef AVAIL_JPEGLIB\n\tif (!Q_strcasecmp(ext, \"jpeg\") || !Q_strcasecmp(ext, \"jpg\") || !Q_strcasecmp(ext, \"jps\"))\n\t{\n\t\treturn screenshotJPEG(filename, fsroot, scr_sshot_compression.value, buffer[0], bytestride, width, height, fmt, writemeta);\n\t}\n#endif\n#ifdef IMAGEFMT_BMP\n\tif (!Q_strcasecmp(ext, \"bmp\"))\n\t{\n\t\treturn WriteBMPFile(filename, fsroot, buffer[0], bytestride, width, height, fmt);\n\t}\n#endif\n#ifdef IMAGEFMT_PCX\n\tif (!Q_strcasecmp(ext, \"pcx\"))\n\t{\n\t\tint y, x, s;\n\t\tqbyte *src, *dest;\n\t\tqbyte *srcbuf = buffer[0], *dstbuf;\n\t\tif (fmt == TF_RGB24 || fmt == TF_RGBA32 || fmt == TF_RGBX32 || fmt == PTI_LLLA8 || fmt == PTI_LLLX8)\n\t\t{\n\t\t\tdstbuf = malloc((intptr_t)width*height);\n\t\t\ts = (fmt == TF_RGB24)?3:4;\n\t\t\t// convert in-place to eight bit\n\t\t\tfor (y = 0; y < height; y++)\n\t\t\t{\n\t\t\t\tsrc = srcbuf + (bytestride * y);\n\t\t\t\tdest = dstbuf + (width * y);\n\n\t\t\t\tfor (x = 0; x < width; x++) {\n\t\t\t\t\t*dest++ = MipColor(src[0], src[1], src[2]);\n\t\t\t\t\tsrc += s;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse if (fmt == TF_BGR24 || fmt == TF_BGRA32 || fmt == TF_BGRX32)\n\t\t{\n\t\t\tdstbuf = malloc((intptr_t)width*height);\n\t\t\ts = (fmt == TF_BGR24)?3:4;\n\t\t\t// convert in-place to eight bit\n\t\t\tfor (y = 0; y < height; y++)\n\t\t\t{\n\t\t\t\tsrc = srcbuf + (bytestride * y);\n\t\t\t\tdest = dstbuf + (width * y);\n\n\t\t\t\tfor (x = 0; x < width; x++) {\n\t\t\t\t\t*dest++ = MipColor(src[2], src[1], src[0]);\n\t\t\t\t\tsrc += s;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse\n\t\t\treturn false;\n\n\t\tWritePCXfile (filename, fsroot, dstbuf, width, height, width, host_basepal, false);\n\t\tfree(dstbuf);\n\t\treturn true;\n\t}\n#endif\n#ifdef IMAGEFMT_TGA\n\tif (!Q_strcasecmp(ext, \"tga\"))\t//tga\n\t\treturn WriteTGA(filename, fsroot, buffer[0], bytestride, width, height, fmt);\n#endif\n#ifdef IMAGEFMT_KTX\n\tif (!Q_strcasecmp(ext, \"ktx\") && bytestride > 0)\t//ktx\n\t{\n\t\tstruct pendingtextureinfo out = {PTI_2D};\n\t\tout.encoding = fmt;\n\t\tout.mipcount = 1;\n\t\tout.mip[0].data = buffer[0];\n\t\tout.mip[0].datasize = bytestride*height;\n\t\tout.mip[0].width = width;\n\t\tout.mip[0].height = height;\n\t\tout.mip[0].depth = 1;\n\t\treturn Image_WriteKTXFile(filename, fsroot, &out);\n\t}\n#endif\n#ifdef IMAGEFMT_DDS\n\tif (!Q_strcasecmp(ext, \"dds\") && bytestride > 0)\t//dds\n\t{\n\t\tstruct pendingtextureinfo out = {PTI_2D};\n\t\tout.encoding = fmt;\n\t\tout.mipcount = 1;\n\t\tout.mip[0].data = buffer[0];\n\t\tout.mip[0].datasize = bytestride*height;\n\t\tout.mip[0].width = width;\n\t\tout.mip[0].height = height;\n\t\tout.mip[0].depth = 1;\n\t\treturn Image_WriteDDSFile(filename, fsroot, &out);\n\t}\n#endif\n\n\t//extension / type not recognised.\n\treturn false;\n}\n#endif\n"
  },
  {
    "path": "engine/client/image_astc.h",
    "content": "//Note: this code does not claim to be bit-correct.\n//It doesn't support volume textures.\n//It doesn't validate block extents (and is generally unaware of more than one block anyway)\n//It doesn't implement all validation checks, either.\n//Do NOT use this code to validate any encoders...\n\n//Based upon documentation here: https://www.khronos.org/registry/OpenGL/extensions/OES/OES_texture_compression_astc.txt\n\n#ifndef ASTC_PUBLIC\n#define ASTC_PUBLIC\n#endif\n\n#define ASTC_WITH_LDR\t\t\t//comment out this line to disable pure-LDR decoding (the hdr code can still be used).\n#define ASTC_WITH_HDR\t\t\t//comment out this line to disable HDR decoding.\n#define ASTC_WITH_HDRTEST\t\t//comment out this line to disable checking for which profile is needed.\n//#define ASTC_WITH_3D\n\n#ifdef ASTC_WITH_LDR\n\tASTC_PUBLIC void ASTC_Decode_LDR8(unsigned char *in, unsigned char *out, int pixstride/*outwidth*/, int layerstride/*outwidth*outheight*/, int bw,int bh,int bd);\t//generates RGBA8 data (gives error colour for hdr blocks!)\n#endif\n#ifdef ASTC_WITH_HDR\n\tASTC_PUBLIC void ASTC_Decode_HDR(unsigned char *in, unsigned short *out, int pixstride/*outwidth*/, int layerstride/*outwidth*outheight*/, int bw,int bh,int bd);\t//generates RGBA16F data.\n#endif\n#ifdef ASTC_WITH_HDRTEST\n\tASTC_PUBLIC int ASTC_BlocksAreHDR(unsigned char *in, size_t datasize, int bw, int bh, int bd);\t\t\t\t//returns true if n consecutive blocks require the HDR profile (ie: detects when you need to soft-decode for drivers with partial support, as opposed to just always decompressing).\n#endif\n\n\n\n#include <math.h>\n#include <stdio.h>\n#include <string.h>\n#ifndef Vector4Set\n\t#define Vector4Set(r,x,y,z,w) {(r)[0] = x; (r)[1] = y;(r)[2] = z;(r)[3]=w;}\n#endif\n#ifndef countof\n\t#define countof(array) (sizeof(array)/sizeof(array[0]))\n#endif\n#if defined(ASTC_WITH_LDR) || defined(ASTC_WITH_HDR)\n\t#define ASTC_WITH_DECODE\n#endif\nenum astc_status_e\n{\n\t//valid blocks\n\tASTC_OKAY,\t\t\t\t//we can decode at least part of this normally (hdr endpoints may still result in per-endpoint errors).\n\tASTC_VOID_LDR,\t\t\t//not an error - the block is a single LDR colour, with an RGBA16 colour in the last 8 bytes.\n\tASTC_VOID_HDR,\t\t\t//not an error - the block is a single HDR colour, with an RGBA16F colour in the last 8 bytes.\n\n\t//invalid blocks\n\tASTC_ERROR,\t\t\t\t//validation errors\n\tASTC_UNSUPPORTED,\t\t//basically just volume textures\n\tASTC_RESERVED,\t\t\t//reserved bits. basically an error but might not be in the future.\n};\nstruct astc_block_info\n{\n\tunsigned char *in;\t\t\t//the 16 bytes of the block\n\tunsigned char blocksize[3];\t//block width, height, depth(1 for 2d).\n\n\tenum astc_status_e status;\t//block status/type.\n\tunsigned char dualplane;\t//two sets of weights instead of one.\n\tunsigned char ccs;\t\t\t//second set applies to this component\n\n\tunsigned char precision;\t//defines the precision of the weights\n\n\tint wcount[4];\t\t\t\t//x,y,z,total weight counts\n\tint weight_bits;\t\t\t//size of weights section.\n\tint config_bits;\t\t\t//size of header before the endpoint bits\n\tint ep_bits;\t\t\t\t//size available to endpoints\n\tunsigned char weights[64];\t//official limit to the number of weights stored\n\n\tunsigned char partitions;\t//number of active partitions to select from (and number of endpoints to read)\n\tunsigned short partindex; //used for deciding which partition each pixel belongs in\n\tstruct astc_part\n\t{\n\t\tunsigned char mode;\t\t//endpoint modes\n#ifdef ASTC_WITH_HDR\n\t\tunsigned char hdr;\t\t//endpoint colour mode - &1=rgb, &2=alpha\n#endif\n\t\tint ep[2][4];\n\t} part[4];\n};\n\nstatic unsigned char ASTC_readbits(unsigned char *in, unsigned int offset, unsigned int count)\n{\t//only reads up to 9 bits, because offset 7 with 10 bits needs to read more than two bytes\n\tunsigned short s;\n\tin += offset>>3;\n\toffset &= 7;\n\ts = in[0];\n\tif (offset+count>8)\n\t\ts |= (in[1]<<8);\n\ts>>=offset;\n\treturn s & ((1u<<count)-1);\n}\nstatic unsigned int ASTC_readmanybits(unsigned char *in, unsigned int offset, unsigned int count)\n{\n\tunsigned int r = 0;\n\twhile(count > 8)\n\t{\n\t\tcount -= 8;\n\t\tr |= ASTC_readbits(in, offset+count, 8)<<count;\n\t}\n\tr |= ASTC_readbits(in, offset, count);\n\treturn r;\n}\n\n//weights cover a range of 0-64 inclusive\n//>32 is +1 (otherwise it would be 0-63)\n//high bits are folded over\nstatic unsigned char dequant_weight_1b[1<<1] = {0x00,0x40};\nstatic unsigned char dequant_weight_2b[1<<2] = {0x00,0x15,0x2b,0x40};\nstatic unsigned char dequant_weight_3b[1<<3] = {0x00,0x09,0x12,0x1b,0x25,0x2e,0x37,0x40};\nstatic unsigned char dequant_weight_4b[1<<4] = {0x00,0x04,0x08,0x0c,0x11,0x15,0x19,0x1d,0x23,0x27,0x2b,0x2f,0x34,0x38,0x3c,0x40};\nstatic unsigned char dequant_weight_5b[1<<5] = {0x00,0x02,0x04,0x06,0x08,0x0a,0x0c,0x0e,0x10,0x12,0x14,0x16,0x18,0x1a,0x1c,0x1e,0x22,0x24,0x26,0x28,0x2a,0x2c,0x2e,0x30,0x32,0x34,0x36,0x38,0x3a,0x3c,0x3e,0x40};\nstatic unsigned char dequant_weight_0t[3] = {0,32,64};\nstatic unsigned char dequant_weight_1t[6] = {0x00,0x40,0x0c,0x34,0x19,0x27};\nstatic unsigned char dequant_weight_2t[12] = {0x00,0x40,0x11,0x2f,0x06,0x3a,0x17,0x29,0x0c,0x34,0x1d,0x23};\nstatic unsigned char dequant_weight_3t[24] = {0x00,0x40,0x08,0x38,0x10,0x30,0x18,0x28,0x02,0x3e,0x0b,0x35,0x13,0x2d,0x1b,0x25,0x05,0x3b,0x0d,0x33,0x16,0x2a,0x1e,0x22};\nstatic unsigned char dequant_weight_0q[5] = {0,16,32,48,64};\nstatic unsigned char dequant_weight_1q[10] = {0x00,0x40,0x05,0x3b,0x0b,0x35,0x11,0x2f,0x17,0x29};\nstatic unsigned char dequant_weight_2q[20] = {0x00,0x40,0x10,0x30,0x03,0x3d,0x13,0x2d,0x06,0x3a,0x17,0x29,0x09,0x37,0x1a,0x26,0x0d,0x33,0x1d,0x23};\nstatic const struct\n{\n\tunsigned char extra, bits, *dequant;\n} astc_weightmode[] =\n{\n\t{0,0, NULL}, //invalid\n\t{0,0, NULL}, //invalid\n\t{0,1, dequant_weight_1b}, //2\n\t{1,0, dequant_weight_0t}, //3\n\t{0,2, dequant_weight_2b}, //4\n\t{2,0, dequant_weight_0q}, //5\n\t{1,1, dequant_weight_1t}, //6\n\t{0,3, dequant_weight_3b}, //8\n\t{0,0, NULL}, //invalid\n\t{0,0, NULL}, //invalid\n\t{2,1, dequant_weight_1q}, //10\n\t{1,2, dequant_weight_2t}, //12\n\t{0,4, dequant_weight_4b}, //16\n\t{2,2, dequant_weight_2q}, //20\n\t{1,3, dequant_weight_3t}, //24\n\t{0,5, dequant_weight_5b}, //32\n};\nstatic unsigned int ASTC_DecodeSize(unsigned int count, unsigned int bits, unsigned char extra)\n{\n\treturn\t((extra==1)?((count*8)+4)/5:0) +\n\t\t\t((extra==2)?((count*7)+2)/3:0) +\n\t\t\t\t\t\t\t count*bits;\n}\n\n\nstatic void ASTC_ReadBlockMode(struct astc_block_info *b)\n{\n\tunsigned char *in = b->in;\n\tunsigned short s = ASTC_readmanybits(in, 0, 13);//in[0] | (in[1]<<8);\n\tb->config_bits = 13;\n\n\tif ((s&0x1ff)==0x1fc)\n\t{\t//void extent\n\t\tif (s&0x200)\n\t\t\tb->status = ASTC_VOID_HDR;\n\t\telse\n\t\t\tb->status = ASTC_VOID_LDR;\n\t\tb->dualplane = b->precision = b->wcount[0] = b->wcount[1] = b->wcount[2] = b->partitions = 0;\n\t\treturn;\n\t}\n\tb->status = ASTC_OKAY;\n\tb->dualplane = (s>>10)&1;\t\t //Dp\n\tb->precision = (s>>(9-3))&(1<<3);//P\n\tb->precision |= (s>>4)&1;\t\t //p0\n\tif (b->blocksize[2] != 1)\n\t{\t//3d blocks have a different header layout\n#ifdef ASTC_WITH_3D\n\t\tif (s&3)\n\t\t{\n\t\t\tb->precision|=(s&3)<<1;\t//p2, p1\n\t\t\tb->wcount[0] = ((s>>5)&3)+2, b->wcount[1] = ((s>>7)&3)+2, b->wcount[2] = ((s>>2)&3)+2;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tb->precision|=(s&0xc)>>1;\t//p2, p1\n\t\t\tif ((s&0x180)!=0x180)\n\t\t\t{\n\t\t\t\tb->dualplane = 0;\t//always single plane.\n\t\t\t\tb->precision &= 7;\t//clear the high precision bit (reused for 'b')\n\t\t\t\tif (!(s&0x180))\n\t\t\t\t\tb->wcount[0] = 6, b->wcount[1] = ((s>>9)&3)+2, b->wcount[2] = ((s>>5)&3)+2;\n\t\t\t\telse if (!(s&0x80))\n\t\t\t\t\tb->wcount[0] = ((s>>5)&3)+2, b->wcount[1] = 6, b->wcount[2] = ((s>>9)&3)+2;\n\t\t\t\telse\n\t\t\t\t\tb->wcount[0] = ((s>>5)&3)+2, b->wcount[1] = ((s>>9)&3)+2, b->wcount[2] = 6;\n\t\t\t}\n\t\t\telse if ((s&0x60)!=0x60)\n\t\t\t{\n\t\t\t\tif (!(s&0x60))\n\t\t\t\t\tb->wcount[0] = 6, b->wcount[1] = 2, b->wcount[2] = 2;\n\t\t\t\telse if (!(s&0x20))\n\t\t\t\t\tb->wcount[0] = 2, b->wcount[1] = 6, b->wcount[2] = 2;\n\t\t\t\telse\t//40\n\t\t\t\t\tb->wcount[0] = 2, b->wcount[1] = 2, b->wcount[2] = 6;\n\t\t\t}\n\t\t\telse\n\t\t\t\tb->status = ASTC_RESERVED; //reserved (or void extent, but those were handled above)\n\t\t}\n#else\n\t\tb->status = ASTC_UNSUPPORTED;\n#endif\n\t}\n\telse\n\t{\n\t\tb->wcount[2] = 1;\n\t\tif (s&3)\n\t\t{\t//one of the first 5 layouts...\n\t\t\tb->precision|=(s&3)<<1;\t//p2, p1\n\t\t\tif (!(s&8))\n\t\t\t{\t//first two layouts...\n\t\t\t\tif (!(s&4))\n\t\t\t\t{\t//layout0\n\t\t\t\t\tb->wcount[0] = ((s>>7)&3)+4;\n\t\t\t\t\tb->wcount[1] = ((s>>5)&3)+2;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\t//layout1\n\t\t\t\t\tb->wcount[0] = ((s>>7)&3)+8;\n\t\t\t\t\tb->wcount[1] = ((s>>5)&3)+2;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (!(s&4))\n\t\t\t{\t//layout2\n\t\t\t\tb->wcount[0] = ((s>>5)&3)+2;\n\t\t\t\tb->wcount[1] = ((s>>7)&3)+8;\n\t\t\t}\n\t\t\telse if (!(s&256))\n\t\t\t{\t//layout3\n\t\t\t\tb->wcount[0] = ((s>>5)&3)+2;\n\t\t\t\tb->wcount[1] = ((s>>7)&1)+6;\n\t\t\t}\n\t\t\telse\n\t\t\t{\t//layout4\n\t\t\t\tb->wcount[0] = ((s>>7)&1)+2;\n\t\t\t\tb->wcount[1] = ((s>>5)&3)+2;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\t//one of the later layouts\n\t\t\tb->precision|=(s&0xc)>>1;\t//p2, p1\n\t\t\tif (!(s&384))\n\t\t\t{\n\t\t\t\tb->wcount[0] = 12;\n\t\t\t\tb->wcount[1] = ((s>>5)&3)+2;\n\t\t\t}\n\t\t\telse if ((s&384)==128)\n\t\t\t{\n\t\t\t\tb->wcount[0] = ((s>>5)&3)+2;\n\t\t\t\tb->wcount[1] = 12;\n\t\t\t}\n\t\t\telse if ((s&480)==384)\n\t\t\t{\n\t\t\t\tb->wcount[0] = 6;\n\t\t\t\tb->wcount[1] = 10;\n\t\t\t}\n\t\t\telse if ((s&480)==416)\n\t\t\t{\n\t\t\t\tb->wcount[0] = 10;\n\t\t\t\tb->wcount[1] = 6;\n\t\t\t}\n\t\t\telse if ((s&384)==256)\n\t\t\t{\n\t\t\t\tb->wcount[0] = ((s>>5)&3)+6;\n\t\t\t\tb->wcount[1] = ((s>>9)&3)+6;\n\t\t\t\tb->dualplane = 0;\t//forget the Dp bit, its reused in this layout\n\t\t\t\tb->precision &= 7;\t//forget the P bit, too\n\t\t\t}\n\t\t\telse\n\t\t\t\tb->status = ASTC_RESERVED; //reserved\n\t\t}\n\t}\n\tb->partitions = ((s>>11)&3)+1;\n\n\tif (b->partitions > 3 && b->dualplane)\n\t\tb->status = ASTC_ERROR;\t//apparently.\n\n\tif (b->wcount[0] > b->blocksize[0] || b->wcount[1] > b->blocksize[1] || b->wcount[2] > b->blocksize[2])\n\t\tb->status = ASTC_ERROR; //invalid weight counts.\n\n\tb->wcount[3] = b->wcount[0] * b->wcount[1] * b->wcount[2];\n\tb->wcount[3]<<=b->dualplane;\t//dual-plane has twice the weights - interleaved.\n\tif (b->wcount[3] > countof(b->weights))\n\t\tb->status = ASTC_ERROR;\t//more than 64 weights are banned, for some reason\n\tb->weight_bits = ASTC_DecodeSize(b->wcount[3], astc_weightmode[b->precision].bits, astc_weightmode[b->precision].extra);\n}\n\nstatic void ASTC_ReadPartitions(struct astc_block_info *b)\n{\n\tint sel;\n\tint i;\n\tunsigned char *in = b->in;\n\tint weight_bits = b->weight_bits;\n\n\tif (b->partitions == 1)\n\t{\t//single-partition mode, simple CEM\n\t\tb->partindex = 0;\n\t\tb->part[0].mode = ASTC_readbits(in, b->config_bits, 4);\n\t\tb->config_bits += 4;\n\t}\n\telse\n\t{\t//multi\n\t\tb->partindex = ASTC_readmanybits(in, b->config_bits, 10);\n\t\tb->config_bits += 10;\n\t\tsel = ASTC_readbits(in, b->config_bits, 6);\n\t\tb->config_bits += 6;\n\t\tif (!(sel&3))\n\t\t{\n\t\t\tsel = (sel>>2)&0xf;\n\t\t\tfor (i = 0; i < b->partitions; i++)\n\t\t\t\tb->part[i].mode = sel;\t//all the same\n\t\t}\n\t\telse\n\t\t{\n\t\t\tint shift = 2;\n\t\t\tint highbits = b->partitions*3 - 4;\n\n\t\t\tweight_bits += highbits;\n\t\t\tsel |= ASTC_readbits(in, 128-weight_bits, highbits)<<6;\t//I don't know why this is separate. it seems like an unnecessary complication to me.\n\n\t\t\tfor (i = 0; i < b->partitions; i++, shift++)\n\t\t\t{\n\t\t\t\tb->part[i].mode = ((sel&3)-1)<<2;\t\t//class groups\n\t\t\t\tb->part[i].mode += ((sel>>shift)&1)<<2;//class\n\t\t\t}\n\t\t\tfor (i = 0; i < b->partitions; i++, shift+=2)\n\t\t\t\tb->part[i].mode += (sel>>shift)&3;\t\t//specific mode info\n\t\t}\n\t}\n\tif (b->dualplane)\n\t{\n\t\tweight_bits += 2;\n\t\tb->ccs = ASTC_readbits(in, 128-weight_bits, 2);\n\t}\n\telse\n\t\tb->ccs = 0;\n\n\tb->ep_bits = 128 - weight_bits - b->config_bits;\n\t//weights are at 128-weight_bits to 128\n\t//epdata is at config_bits to config_bits+ep_bits\n}\n\n#ifdef ASTC_WITH_HDRTEST\nASTC_PUBLIC int ASTC_BlocksAreHDR(unsigned char *in, size_t datasize, int bw, int bh, int bd)\n{\n\tstruct astc_block_info b;\n\tint i;\n\tsize_t blocks = datasize/16;\n\tb.in = in;\n\tb.blocksize[0] = bw;\n\tb.blocksize[1] = bh;\n\tb.blocksize[2] = bd;\n\twhile(blocks --> 0)\n\t{\n\t\tASTC_ReadBlockMode(&b);\n\t\tif (b.status == ASTC_VOID_HDR)\n\t\t\treturn 1;\t//if we're getting hdr blocks then we can decode properly only with hdr\n\t\tif (b.status == ASTC_VOID_LDR)\n\t\t\treturn 0;\t//if we're getting ldr blocks, then its unlikely that there's any hdr blocks in there.\n\t\tif (b.status != ASTC_OKAY)\n\t\t\tcontinue;\n\t\tASTC_ReadPartitions(&b);\n\t\tfor (i = 0; i < b.partitions; i++)\n\t\t{\n\t\t\tswitch(b.part[i].mode)\n\t\t\t{\n\t\t\tcase 2:\n\t\t\tcase 3:\n\t\t\tcase 7:\n\t\t\tcase 11:\n\t\t\tcase 14:\n\t\t\tcase 15:\n\t\t\t\treturn 1;\n\t\t\t }\n\t\t}\n\t\tb.in += 16;\n\t}\n\treturn 0;\n}\n#endif\n\n#ifdef ASTC_WITH_DECODE\nstatic unsigned char ASTC_readbits2(unsigned char *in, unsigned int *offset, unsigned int count)\n{\t//only reads up to 9 bits, because offset 7 with 10 bits needs to read more than two bytes\n\tunsigned char r = ASTC_readbits(in, *offset, count);\n\t*offset += count;\n\treturn r;\n}\nstatic void ASTC_Decode(unsigned char *in, unsigned char *out, int count, unsigned int offset, int bits, int extra, unsigned char *dequant)\n{\n\tunsigned char block[5];\n\tint j;\n\n\t//unfortunately these trits depend upon the values of the later bits in each block.\n\t//if only it were a nice simple modulo...\n\tif (extra==1)\n\t{\n\t\t//read it 5 samples at a time\n\t\twhile(count > 0)\n\t\t{\n\t\t\tunsigned int t, c;\n\n\t\t\tblock[0] = ASTC_readbits2(in, &offset, bits);\n\t\t\tt = ASTC_readbits2(in, &offset, 2);\n\t\t\tif (count > 1)\n\t\t\t{\n\t\t\t\tblock[1] = ASTC_readbits2(in, &offset, bits);\n\t\t\t\tt |= ASTC_readbits2(in, &offset, 2)<<2;\n\t\t\t}\n\t\t\telse\n\t\t\t\tblock[1] = 0;\n\t\t\tif (count > 2)\n\t\t\t{\n\t\t\t\tblock[2] = ASTC_readbits2(in, &offset, bits);\n\t\t\t\tt |= ASTC_readbits2(in, &offset, 1)<<4;\n\t\t\t}\n\t\t\telse\n\t\t\t\tblock[2] = 0;\n\t\t\tif (count > 3)\n\t\t\t{\n\t\t\t\tblock[3] = ASTC_readbits2(in, &offset, bits);\n\t\t\t\tt |= ASTC_readbits2(in, &offset, 2)<<5;\n\t\t\t}\n\t\t\telse\n\t\t\t\tblock[3] = 0;\n\t\t\tif (count > 4)\n\t\t\t{\n\t\t\t\tblock[4] = ASTC_readbits2(in, &offset, bits);\n\t\t\t\tt |= ASTC_readbits2(in, &offset, 1)<<7;\n\t\t\t}\n\t\t\telse\n\t\t\t\tblock[4] = 0;\n\n\t\t\t//okay, we read the block, now figure out the trits and pack them into the high part of the result\n\t\t\tif ((t&0x1c) == 0x1c)\n\t\t\t{\n\t\t\t\tc = ((t>>3)&0x1c) | (t&3);\n\t\t\t\tblock[4] |= 2<<bits;\n\t\t\t\tblock[3] |= 2<<bits;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tc = t&0x1f;\n\t\t\t\tif ((t&0x60) == 0x60)\n\t\t\t\t{\n\t\t\t\t\tblock[4] |= 2<<bits;\n\t\t\t\t\tblock[3] |= (t>>7)<<bits;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tblock[4] |= (t>>7)<<bits;\n\t\t\t\t\tblock[3] |= ((t>>5)&3)<<bits;\n                }\n\t\t\t}\n\t\t\tif ((c&3)==3)\n\t\t\t{\n\t\t\t\tblock[2] |= 2<<bits;\n\t\t\t\tblock[1] |= ((c>>4)&1)<<bits;\n\t\t\t\tblock[0] |= (((c>>2)&2) | ((c>>2)&~(c>>3)&1))<<bits;\n\t\t\t}\n\t\t\telse if ((c&0xc)==0xc)\n\t\t\t{\n\t\t\t\tblock[2] |= 2<<bits;\n\t\t\t\tblock[1] |= 2<<bits;\n\t\t\t\tblock[0] |= (c&3)<<bits;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tblock[2] |= ((c>>4)&1)<<bits;\n\t\t\t\tblock[1] |= ((c>>2)&3)<<bits;\n\t\t\t\tblock[0] |= ((c&2)|(c&1&~(c>>1)))<<bits;\n\t\t\t}\n\n\t\t\t//spit out the result\n\t\t\tfor (j = 0; j < 5 && j < count; j++)\n\t\t\t\t*out++ = dequant[block[j]];\n\t\t\tcount -= 5;\n\t\t}\n\t}\n\telse if (extra == 2)\n\t{\n\t\t//read it 3 samples at a time\n\t\twhile(count > 0)\n\t\t{\n\t\t\tunsigned int t, c;\n\n\t\t\tblock[0] = ASTC_readbits2(in, &offset, bits);\n\t\t\tt = ASTC_readbits2(in, &offset, 3);\n\t\t\tif (count > 1)\n\t\t\t{\n\t\t\t\tblock[1] = ASTC_readbits2(in, &offset, bits);\n\t\t\t\tt |= ASTC_readbits2(in, &offset, 2)<<3;\n\t\t\t}\n\t\t\telse\n\t\t\t\tblock[1] = 0;\n\t\t\tif (count > 2)\n\t\t\t{\n\t\t\t\tblock[2] = ASTC_readbits2(in, &offset, bits);\n\t\t\t\tt |= ASTC_readbits2(in, &offset, 2)<<5;\n\t\t\t}\n\t\t\telse\n\t\t\t\tblock[2] = 0;\n\n\t\t\t//okay, we read the block, now figure out the trits and pack them into the high part of the result\n\t\t\tif ((t&6)==6 && !(t&0x60))\n\t\t\t{\n\t\t\t\tblock[2] |= (((t&1)<<2) | (((t>>4)&~t&1)<<1) | ((t>>3)&~t&1))<<bits;\n\t\t\t\tblock[1] |= 4<<bits;\n\t\t\t\tblock[0] |= 4<<bits;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif ((t&6) == 6)\n\t\t\t\t{\n\t\t\t\t\tblock[2] |= 4<<bits;\n\t\t\t\t\tc = ((t>>3)&3)<<3;\n\t\t\t\t\tc |= (~(t>>5)&3)<<1;\n\t\t\t\t\tc |= t&1;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tblock[2] |= ((t>>5)&3)<<bits;\n\t\t\t\t\tc = t&0x1f;\n\t\t\t\t}\n\n\t\t\t\tif ((c&7) == 5)\n\t\t\t\t{\n\t\t\t\t\tblock[1] |= 4<<bits;\n\t\t\t\t\tblock[0] |= ((c>>3)&3)<<bits;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tblock[1] |= ((c>>3)&3)<<bits;\n\t\t\t\t\tblock[0] |= (c&7)<<bits;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t//spit out the result\n\t\t\tfor (j = 0; j < 3 && j < count; j++)\n\t\t\t\t*out++ = dequant[block[j]];\n\t\t\tcount -= 3;\n\t\t}\n\t}\n\telse while(count --> 0)\t//pure bits, nice and simple\n\t{\n\t\tunsigned char val = ASTC_readbits2(in, &offset, bits);\n\n\t\t*out++ = dequant[val];\n\t}\n}\n\n//endpoints have a logical value between 0 and 255.\n//bit replication is used to fill in missing precision\nstatic unsigned char dequant_ep_1b[1<<1] = {0,255};\nstatic unsigned char dequant_ep_2b[1<<2] = {0x00,0x55,0xaa,0xff};\nstatic unsigned char dequant_ep_3b[1<<3] = {0x00,0x24,0x49,0x6d,0x92,0xb6,0xdb,0xff};\nstatic unsigned char dequant_ep_4b[1<<4] = {\n\t0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88,0x99,0xaa,0xbb,0xcc,0xdd,0xee,0xff};\nstatic unsigned char dequant_ep_5b[1<<5] = {\n\t0x00,0x08,0x10,0x18,0x21,0x29,0x31,0x39,0x42,0x4a,0x52,0x5a,0x63,0x6b,0x73,0x7b,\n\t0x84,0x8c,0x94,0x9c,0xa5,0xad,0xb5,0xbd,0xc6,0xce,0xd6,0xde,0xe7,0xef,0xf7,0xff};\nstatic unsigned char dequant_ep_6b[1<<6] = {\n\t0x00,0x04,0x08,0x0c,0x10,0x14,0x18,0x1c,0x20,0x24,0x28,0x2c,0x30,0x34,0x38,0x3c,\n\t0x41,0x45,0x49,0x4d,0x51,0x55,0x59,0x5d,0x61,0x65,0x69,0x6d,0x71,0x75,0x79,0x7d,\n\t0x82,0x86,0x8a,0x8e,0x92,0x96,0x9a,0x9e,0xa2,0xa6,0xaa,0xae,0xb2,0xb6,0xba,0xbe,\n\t0xc3,0xc7,0xcb,0xcf,0xd3,0xd7,0xdb,0xdf,0xe3,0xe7,0xeb,0xef,0xf3,0xf7,0xfb,0xff};\nstatic unsigned char dequant_ep_7b[1<<7] = {\n\t0x00,0x02,0x04,0x06,0x08,0x0a,0x0c,0x0e,0x10,0x12,0x14,0x16,0x18,0x1a,0x1c,0x1e,\n\t0x20,0x22,0x24,0x26,0x28,0x2a,0x2c,0x2e,0x30,0x32,0x34,0x36,0x38,0x3a,0x3c,0x3e,\n\t0x40,0x42,0x44,0x46,0x48,0x4a,0x4c,0x4e,0x50,0x52,0x54,0x56,0x58,0x5a,0x5c,0x5e,\n\t0x60,0x62,0x64,0x66,0x68,0x6a,0x6c,0x6e,0x70,0x72,0x74,0x76,0x78,0x7a,0x7c,0x7e,\n\t0x81,0x83,0x85,0x87,0x89,0x8b,0x8d,0x8f,0x91,0x93,0x95,0x97,0x99,0x9b,0x9d,0x9f,\n\t0xa1,0xa3,0xa5,0xa7,0xa9,0xab,0xad,0xaf,0xb1,0xb3,0xb5,0xb7,0xb9,0xbb,0xbd,0xbf,\n\t0xc1,0xc3,0xc5,0xc7,0xc9,0xcb,0xcd,0xcf,0xd1,0xd3,0xd5,0xd7,0xd9,0xdb,0xdd,0xdf,\n\t0xe1,0xe3,0xe5,0xe7,0xe9,0xeb,0xed,0xef,0xf1,0xf3,0xf5,0xf7,0xf9,0xfb,0xfd,0xff};\nstatic unsigned char dequant_ep_8b[1<<8] = {\n\t0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,\n\t0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,\n\t0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f,\n\t0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f,\n\t0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f,\n\t0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5a,0x5b,0x5c,0x5d,0x5e,0x5f,\n\t0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x6b,0x6c,0x6d,0x6e,0x6f,\n\t0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x7b,0x7c,0x7d,0x7e,0x7f,\n\t0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8a,0x8b,0x8c,0x8d,0x8e,0x8f,\n\t0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0x9b,0x9c,0x9d,0x9e,0x9f,\n\t0xa0,0xa1,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xab,0xac,0xad,0xae,0xaf,\n\t0xb0,0xb1,0xb2,0xb3,0xb4,0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xbb,0xbc,0xbd,0xbe,0xbf,\n\t0xc0,0xc1,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xcb,0xcc,0xcd,0xce,0xcf,\n\t0xd0,0xd1,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xdb,0xdc,0xdd,0xde,0xdf,\n\t0xe0,0xe1,0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xeb,0xec,0xed,0xee,0xef,\n\t0xf0,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa,0xfb,0xfc,0xfd,0xfe,0xff};\nstatic unsigned char dequant_ep_0t[3]  = {0,128,255};\nstatic unsigned char dequant_ep_1t[6]  = {0x00,0xff,0x33,0xcc,0x66,0x99};\nstatic unsigned char dequant_ep_2t[12] = {0x00,0xff,0x45,0xba,0x17,0xe8,0x5c,0xa3,0x2e,0xd1,0x74,0x8b};\nstatic unsigned char dequant_ep_3t[24] = {0x00,0xff,0x21,0xde,0x42,0xbd,0x63,0x9c,0x0b,0xf4,0x2c,0xd3,0x4d,0xb2,0x6e,0x91,0x16,0xe9,0x37,0xc8,0x58,0xa7,0x79,0x86};\nstatic unsigned char dequant_ep_4t[48] = {0x00,0xff,0x10,0xef,0x20,0xdf,0x30,0xcf,0x41,0xbe,0x51,0xae,0x61,0x9e,0x71,0x8e,0x05,0xfa,0x15,0xea,0x26,0xd9,0x36,0xc9,0x46,0xb9,0x56,0xa9,0x67,0x98,0x77,0x88,0x0b,0xf4,0x1b,0xe4,0x2b,0xd4,0x3b,0xc4,0x4c,0xb3,0x5c,0xa3,0x6c,0x93,0x7c,0x83};\nstatic unsigned char dequant_ep_5t[96] = {0x00,0xff,0x08,0xf7,0x10,0xef,0x18,0xe7,0x20,0xdf,0x28,0xd7,0x30,0xcf,0x38,0xc7,0x40,0xbf,0x48,0xb7,0x50,0xaf,0x58,0xa7,0x60,0x9f,0x68,0x97,0x70,0x8f,0x78,0x87,0x02,0xfd,0x0a,0xf5,0x12,0xed,0x1a,0xe5,0x23,0xdc,0x2b,0xd4,0x33,0xcc,0x3b,0xc4,0x43,0xbc,0x4b,0xb4,0x53,0xac,0x5b,0xa4,0x63,0x9c,0x6b,0x94,0x73,0x8c,0x7b,0x84,0x05,0xfa,0x0d,0xf2,0x15,0xea,0x1d,0xe2,0x25,0xda,0x2d,0xd2,0x35,0xca,0x3d,0xc2,0x46,0xb9,0x4e,0xb1,0x56,0xa9,0x5e,0xa1,0x66,0x99,0x6e,0x91,0x76,0x89,0x7e,0x81};\nstatic unsigned char dequant_ep_6t[192]= {0x00,0xff,0x04,0xfb,0x08,0xf7,0x0c,0xf3,0x10,0xef,0x14,0xeb,0x18,0xe7,0x1c,0xe3,0x20,0xdf,0x24,0xdb,0x28,0xd7,0x2c,0xd3,0x30,0xcf,0x34,0xcb,0x38,0xc7,0x3c,0xc3,0x40,0xbf,0x44,0xbb,0x48,0xb7,0x4c,0xb3,0x50,0xaf,0x54,0xab,0x58,0xa7,0x5c,0xa3,0x60,0x9f,0x64,0x9b,0x68,0x97,0x6c,0x93,0x70,0x8f,0x74,0x8b,0x78,0x87,0x7c,0x83,0x01,0xfe,0x05,0xfa,0x09,0xf6,0x0d,0xf2,0x11,0xee,0x15,0xea,0x19,0xe6,0x1d,0xe2,0x21,0xde,0x25,0xda,0x29,0xd6,0x2d,0xd2,0x31,0xce,0x35,0xca,0x39,0xc6,0x3d,0xc2,0x41,0xbe,0x45,0xba,0x49,0xb6,0x4d,0xb2,0x51,0xae,0x55,0xaa,0x59,0xa6,0x5d,0xa2,0x61,0x9e,0x65,0x9a,0x69,0x96,0x6d,0x92,0x71,0x8e,0x75,0x8a,0x79,0x86,0x7d,0x82,0x02,0xfd,0x06,0xf9,0x0a,0xf5,0x0e,0xf1,0x12,0xed,0x16,0xe9,0x1a,0xe5,0x1e,0xe1,0x22,0xdd,0x26,0xd9,0x2a,0xd5,0x2e,0xd1,0x32,0xcd,0x36,0xc9,0x3a,0xc5,0x3e,0xc1,0x42,0xbd,0x46,0xb9,0x4a,0xb5,0x4e,0xb1,0x52,0xad,0x56,0xa9,0x5a,0xa5,0x5e,0xa1,0x62,0x9d,0x66,0x99,0x6a,0x95,0x6e,0x91,0x72,0x8d,0x76,0x89,0x7a,0x85,0x7e,0x81};\nstatic unsigned char dequant_ep_0q[5]  = {0,64,128,192,255};\nstatic unsigned char dequant_ep_1q[10] = {0x00,0xff,0x1c,0xe3,0x38,0xc7,0x54,0xab,0x71,0x8e};\nstatic unsigned char dequant_ep_2q[20] = {0x00,0xff,0x43,0xbc,0x0d,0xf2,0x50,0xaf,0x1b,0xe4,0x5e,0xa1,0x28,0xd7,0x6b,0x94,0x36,0xc9,0x79,0x86};\nstatic unsigned char dequant_ep_3q[40] = {0x00,0xff,0x20,0xdf,0x41,0xbe,0x61,0x9e,0x06,0xf9,0x27,0xd8,0x47,0xb8,0x68,0x97,0x0d,0xf2,0x2d,0xd2,0x4e,0xb1,0x6e,0x91,0x13,0xec,0x34,0xcb,0x54,0xab,0x75,0x8a,0x1a,0xe5,0x3a,0xc5,0x5b,0xa4,0x7b,0x84};\nstatic unsigned char dequant_ep_4q[80] = {0x00,0xff,0x10,0xef,0x20,0xdf,0x30,0xcf,0x40,0xbf,0x50,0xaf,0x60,0x9f,0x70,0x8f,0x03,0xfc,0x13,0xec,0x23,0xdc,0x33,0xcc,0x43,0xbc,0x53,0xac,0x64,0x9b,0x74,0x8b,0x06,0xf9,0x16,0xe9,0x26,0xd9,0x36,0xc9,0x47,0xb8,0x57,0xa8,0x67,0x98,0x77,0x88,0x09,0xf6,0x19,0xe6,0x2a,0xd5,0x3a,0xc5,0x4a,0xb5,0x5a,0xa5,0x6a,0x95,0x7a,0x85,0x0d,0xf2,0x1d,0xe2,0x2d,0xd2,0x3d,0xc2,0x4d,0xb2,0x5d,0xa2,0x6d,0x92,0x7d,0x82};\nstatic unsigned char dequant_ep_5q[160]= {0x00,0xff,0x08,0xf7,0x10,0xef,0x18,0xe7,0x20,0xdf,0x28,0xd7,0x30,0xcf,0x38,0xc7,0x40,0xbf,0x48,0xb7,0x50,0xaf,0x58,0xa7,0x60,0x9f,0x68,0x97,0x70,0x8f,0x78,0x87,0x01,0xfe,0x09,0xf6,0x11,0xee,0x19,0xe6,0x21,0xde,0x29,0xd6,0x31,0xce,0x39,0xc6,0x41,0xbe,0x49,0xb6,0x51,0xae,0x59,0xa6,0x61,0x9e,0x69,0x96,0x71,0x8e,0x79,0x86,0x03,0xfc,0x0b,0xf4,0x13,0xec,0x1b,0xe4,0x23,0xdc,0x2b,0xd4,0x33,0xcc,0x3b,0xc4,0x43,0xbc,0x4b,0xb4,0x53,0xac,0x5b,0xa4,0x63,0x9c,0x6b,0x94,0x73,0x8c,0x7b,0x84,0x04,0xfb,0x0c,0xf3,0x14,0xeb,0x1c,0xe3,0x24,0xdb,0x2c,0xd3,0x34,0xcb,0x3c,0xc3,0x44,0xbb,0x4c,0xb3,0x54,0xab,0x5c,0xa3,0x64,0x9b,0x6c,0x93,0x74,0x8b,0x7c,0x83,0x06,0xf9,0x0e,0xf1,0x16,0xe9,0x1e,0xe1,0x26,0xd9,0x2e,0xd1,0x36,0xc9,0x3e,0xc1,0x46,0xb9,0x4e,0xb1,0x56,0xa9,0x5e,0xa1,0x66,0x99,0x6e,0x91,0x76,0x89,0x7e,0x81};\nstatic const struct\n{\n\tunsigned char extra, bits, *dequant;\n} astc_epvmode[] =\n{\n\t{0,1, dequant_ep_1b}, //2\n\t{1,0, dequant_ep_0t}, //3\n\t{0,2, dequant_ep_2b}, //4\n\t{2,0, dequant_ep_0q}, //5\n\t{1,1, dequant_ep_1t}, //6\n\t{0,3, dequant_ep_3b}, //8\n\t{2,1, dequant_ep_1q}, //10\n\t{1,2, dequant_ep_2t}, //12\n\t{0,4, dequant_ep_4b}, //16\n\t{2,2, dequant_ep_2q}, //20\n\t{1,3, dequant_ep_3t}, //24\n\t{0,5, dequant_ep_5b}, //32\n\t{2,3, dequant_ep_3q}, //40\n\t{1,4, dequant_ep_4t}, //48\n\t{0,6, dequant_ep_6b}, //64\n\t{2,4, dequant_ep_4q}, //80\n\t{1,5, dequant_ep_5t}, //96\n\t{0,7, dequant_ep_7b}, //128\n\t{2,5, dequant_ep_5q}, //160\n\t{1,6, dequant_ep_6t}, //192\n\t{0,8, dequant_ep_8b}, //256\n\t//other modes don't make any sense\n};\n/*static void ASTC_CalcDequant(void)\n{\n\tint i;\n\n\tint extra = 0;\n\tint bits = 1;\n\tint isweight = 1;\n\tint targbits = isweight?6:8;\n\tint v;\n\n\tstatic qboolean nospam;\n\tif (nospam)\n\t\treturn;\n\tnospam = true;\n\n\t//binary:\n\tif (!extra)\n\t{\n\t\tfor (bits = 1; bits <= (isweight?5:8); bits++)\n\t\t{\n\t\t\tCon_Printf(\"table: %s_%ib\", isweight?\"weight\":\"ep\", bits);\n\t\t\tfor (i = 0; i < (1<<bits); i++)\n\t\t\t{\n\t\t\t\tv = i;\n\t\t\t\tv<<=(targbits-bits);\n\t\t\t\tv|=v>>bits;\n\t\t\t\tv|=v>>bits;\n\t\t\t\tv|=v>>bits;\n\t\t\t\tv|=v>>bits;\n\t\t\t\tv|=v>>bits;\n\t\t\t\tv|=v>>bits;\n\t\t\t\tv|=v>>bits;\n\t\t\t\tv|=v>>bits;\n\n\t\t\t\tif (isweight && v > 32)\n\t\t\t\t\tv++; //0-64 instead of 0-63\n\n\t\t\t\tCon_Printf(\"0x%02x,\", v);\n\t\t\t}\n\t\t\tCon_Printf(\"\\n\");\n\t\t}\n\t}\n\telse if (extra == 1)\n\t{\n\t\tint A,B,C,D;\n\n\t\tfor (bits = 1; bits <= (isweight?3:6); bits++)\n\t\t{\n\t\t\tCon_Printf(\"table: %s_%it:\\n\", isweight?\"weight\":\"ep\", bits);\n\t\t\tfor (i = 0; i < ((2<<bits)|(1<<bits)); i++)\n\t\t\t{\n\t\t\t\tswitch(bits)\n\t\t\t\t{\n\t\t\t\tcase 1:\n\t\t\t\t\tA = (i&1)*(isweight?0x7f:0x1ff);\n\t\t\t\t\tB = 0;\n\t\t\t\t\tC = isweight?50:204;\n\t\t\t\t\tD = i>>bits;\n\t\t\t\t\tbreak;\n\t\t\t\tcase 2:\n\t\t\t\t\tA = (i&1)*(isweight?0x7f:0x1ff);\n\t\t\t\t\tB = ((i>>1)&1) * (isweight?0b1000101:0b100010110);\n\t\t\t\t\tC = isweight?25:93;\n\t\t\t\t\tD = i>>bits;\n\t\t\t\t\tbreak;\n\t\t\t\tcase 3:\n\t\t\t\t\tA = (i&1)*(isweight?0x7f:0x1ff);\n\t\t\t\t\tB = ((i>>1)&1) * (isweight?0b0100001:0b010000101);\t//b\n\t\t\t\t\tB|= ((i>>2)&1) * (isweight?0b1000010:0b100001010);\t//c\n\t\t\t\t\tC = isweight?11:44;\n\t\t\t\t\tD = i>>bits;\n\t\t\t\t\tbreak;\n\t\t\t\tcase 4:\n\t\t\t\t\tA = (i&1)*0x1ff;\n\t\t\t\t\tB = ((i>>1)&1) * 0b001000001;\t//b\n\t\t\t\t\tB|= ((i>>2)&1) * 0b010000010;\t//c\n\t\t\t\t\tB|= ((i>>3)&1) * 0b100000100;\t//d\n\t\t\t\t\tC = 22;\n\t\t\t\t\tD = i>>bits;\n\t\t\t\t\tbreak;\n\t\t\t\tcase 5:\n\t\t\t\t\tA = (i&1)*0x1ff;\n\t\t\t\t\tB = ((i>>1)&1) * 0b000100000;\t//b\n\t\t\t\t\tB|= ((i>>2)&1) * 0b001000000;\t//c\n\t\t\t\t\tB|= ((i>>3)&1) * 0b010000001;\t//d\n\t\t\t\t\tB|= ((i>>4)&1) * 0b100000010;\t//e\n\t\t\t\t\tC = 11;\n\t\t\t\t\tD = i>>bits;\n\t\t\t\t\tbreak;\n\t\t\t\tcase 6:\n\t\t\t\t\tA = (i&1)*0x1ff;\n\t\t\t\t\tB = ((i>>1)&1) * 0b000010000;\t//b\n\t\t\t\t\tB|= ((i>>2)&1) * 0b000100000;\t//c\n\t\t\t\t\tB|= ((i>>3)&1) * 0b001000000;\t//d\n\t\t\t\t\tB|= ((i>>4)&1) * 0b010000000;\t//e\n\t\t\t\t\tB|= ((i>>5)&1) * 0b100000001;\t//f\n\t\t\t\t\tC = 5;\n\t\t\t\t\tD = i>>bits;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tv = D * C + B;\n\t\t\t\tv = v ^ A;\n\t\t\t\tv = (A & (isweight?0x20:0x80)) | (v >> 2);\n\n\t\t\t\tif (isweight && v > 32)\n\t\t\t\t\tv++; //0-64 instead of 0-63\n\n\t\t\t\tCon_Printf(\"0x%02x,\", v);\n\t\t\t}\n\t\t\tCon_Printf(\"\\n\");\n\t\t}\n\t}\n\telse if (extra == 2)\n\t{\n\t\tint A,B,C,D;\n\n\t\tfor (bits = 1; bits <= (isweight?2:5); bits++)\n\t\t{\n\t\t\tCon_Printf(\"table: %s_%iq:\\n\", isweight?\"weight\":\"ep\", bits);\n\t\t\tfor (i = 0; i < ((4<<bits)|(1<<bits)); i++)\n\t\t\t{\n\t\t\t\tswitch(bits)\n\t\t\t\t{\n\t\t\t\tcase 1:\n\t\t\t\t\tA = (i&1)*(isweight?0x7f:0x1ff);\n\t\t\t\t\tB = 0;\n\t\t\t\t\tC = isweight?23:113;\n\t\t\t\t\tD = i>>bits;\n\t\t\t\t\tbreak;\n\t\t\t\tcase 2:\n\t\t\t\t\tA = (i&1)*(isweight?0x7f:0x1ff);\n\t\t\t\t\tB = ((i>>1)&1) * (isweight?0b1000010:0b100001100);\n\t\t\t\t\tC = isweight?13:54;\n\t\t\t\t\tD = i>>bits;\n\t\t\t\t\tbreak;\n\t\t\t\tcase 3:\n\t\t\t\t\tA = (i&1)*0x1ff;\n\t\t\t\t\tB = ((i>>1)&1) * 0b010000010;\t//b\n\t\t\t\t\tB|= ((i>>2)&1) * 0b100000101;\t//c\n\t\t\t\t\tC = 26;\n\t\t\t\t\tD = i>>bits;\n\t\t\t\t\tbreak;\n\t\t\t\tcase 4:\n\t\t\t\t\tA = (i&1)*0x1ff;\n\t\t\t\t\tB = ((i>>1)&1) * 0b001000000;\t//b\n\t\t\t\t\tB|= ((i>>2)&1) * 0b010000001;\t//c\n\t\t\t\t\tB|= ((i>>3)&1) * 0b100000010;\t//d\n\t\t\t\t\tC = 13;\n\t\t\t\t\tD = i>>bits;\n\t\t\t\t\tbreak;\n\t\t\t\tcase 5:\n\t\t\t\t\tA = (i&1)*0x1ff;\n\t\t\t\t\tB = ((i>>1)&1) * 0b000100000;\t//b\n\t\t\t\t\tB|= ((i>>2)&1) * 0b001000000;\t//c\n\t\t\t\t\tB|= ((i>>3)&1) * 0b010000000;\t//d\n\t\t\t\t\tB|= ((i>>4)&1) * 0b100000001;\t//e\n\t\t\t\t\tC = 6;\n\t\t\t\t\tD = i>>bits;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tv = D * C + B;\n\t\t\t\tv = v ^ A;\n\t\t\t\tv = (A & (isweight?0x20:0x80)) | (v >> 2);\n\n\t\t\t\tif (isweight && v > 32)\n\t\t\t\t\tv++; //0-64 instead of 0-63\n\n\t\t\t\tCon_Printf(\"0x%02x,\", v);\n\t\t\t}\n\t\t\tCon_Printf(\"\\n\");\n\t\t}\n\t}\n}*/\n\nstatic void ASTC_blue_contract(int *out, int r, int g, int b, int a)\n{\n    out[0] = (r+b) >> 1;\n    out[1] = (g+b) >> 1;\n    out[2] = b;\n    out[3] = a;\n}\nstatic int ASTC_bit_transfer_signed(int a, unsigned char *b)\t//returns new value for a.\n{\n    *b >>= 1;\n    *b |= a & 0x80;\n    a >>= 1;\n    a &= 0x3F;\n    if((a&0x20)!=0)\n\t\ta=a-0x40;\n\treturn a;\n}\nstatic void ASTC_clamp_unorm8(int *c)\n{\n\tc[0] = bound(0, c[0], 255);\n\tc[1] = bound(0, c[1], 255);\n\tc[2] = bound(0, c[2], 255);\n\tc[3] = bound(0, c[3], 255);\n}\n\n#ifdef ASTC_WITH_HDR\nstatic void ASTC_HDR_Mode_2(struct astc_part *p, unsigned char *v)\n{\n\tint y0,y1;\n\tif(v[1] >= v[0])\n\t{\n\t\ty0 = (v[0] << 4);\n\t\ty1 = (v[1] << 4);\n\t}\n\telse\n\t{\n\t\ty0 = (v[1] << 4) + 8;\n\t\ty1 = (v[0] << 4) - 8;\n\t}\n\tVector4Set(p->ep[0], y0, y0, y0, 0x780);\n\tVector4Set(p->ep[1], y1, y1, y1, 0x780);\n\tp->hdr = 0xf;\n}\nstatic void ASTC_HDR_Mode_3(struct astc_part *p, unsigned char *v)\n{\n\tint y0, y1, d;\n\tif((v[0]&0x80) != 0)\n\t{\n\t\ty0 = ((v[1] & 0xE0) << 4) | ((v[0] & 0x7F) << 2);\n\t\td  =  (v[1] & 0x1F) << 2;\n\t}\n\telse\n\t{\n\t\ty0 = ((v[1] & 0xF0) << 4) | ((v[0] & 0x7F) << 1);\n\t\td  =  (v[1] & 0x0F) << 1;\n\t}\n\n\ty1 = y0 + d;\n\tif(y1 > 0xFFF)\n\t\ty1 = 0xFFF;\n\n\tVector4Set(p->ep[0], y0, y0, y0, 0x780);\n\tVector4Set(p->ep[1], y1, y1, y1, 0x780);\n\tp->hdr = 0xf;\n}\nstatic void ASTC_HDR_Mode_7(struct astc_part *p, unsigned char *v)\n{\n\tint modeval = ((v[0]&0xC0)>>6) | ((v[1]&0x80)>>5) | ((v[2]&0x80)>>4);\n\tint majcomp;\n\tint mode;\n\tstatic const int shamts[6] = { 1,1,2,3,4,5 };\n\tint shamt,t;\n\n\tint red, green, blue, scale;\n\tint x0,x1,x2,x3,x4,x5,x6,ohm;\n\n\tif( (modeval & 0xC ) != 0xC )\n\t{\n\t\tmajcomp = modeval >> 2;\n\t\tmode = modeval & 3;\n\t}\n\telse if( modeval != 0xF )\n\t{\n\t\tmajcomp = modeval & 3;\n\t\tmode = 4;\n\t}\n\telse\n\t{\n\t\tmajcomp = 0; mode = 5;\n\t}\n\n\tred   = v[0] & 0x3f;\n\tgreen = v[1] & 0x1f;\n\tblue  = v[2] & 0x1f;\n\tscale = v[3] & 0x1f;\n\n\tx0 = (v[1] >> 6) & 1; x1 = (v[1] >> 5) & 1;\n\tx2 = (v[2] >> 6) & 1; x3 = (v[2] >> 5) & 1;\n\tx4 = (v[3] >> 7) & 1; x5 = (v[3] >> 6) & 1;\n\tx6 = (v[3] >> 5) & 1;\n\n\tohm = 1 << mode;\n\tif( ohm & 0x30 ) green |= x0 << 6;\n\tif( ohm & 0x3A ) green |= x1 << 5;\n\tif( ohm & 0x30 ) blue |= x2 << 6;\n\tif( ohm & 0x3A ) blue |= x3 << 5;\n\tif( ohm & 0x3D ) scale |= x6 << 5;\n\tif( ohm & 0x2D ) scale |= x5 << 6;\n\tif( ohm & 0x04 ) scale |= x4 << 7;\n\tif( ohm & 0x3B ) red |= x4 << 6;\n\tif( ohm & 0x04 ) red |= x3 << 6;\n\tif( ohm & 0x10 ) red |= x5 << 7;\n\tif( ohm & 0x0F ) red |= x2 << 7;\n\tif( ohm & 0x05 ) red |= x1 << 8;\n\tif( ohm & 0x0A ) red |= x0 << 8;\n\tif( ohm & 0x05 ) red |= x0 << 9;\n\tif( ohm & 0x02 ) red |= x6 << 9;\n\tif( ohm & 0x01 ) red |= x3 << 10;\n\tif( ohm & 0x02 ) red |= x5 << 10;\n\n\tshamt = shamts[mode];\n\tred <<= shamt; green <<= shamt; blue <<= shamt; scale <<= shamt;\n\n\tif( mode != 5 ) { green = red - green; blue = red - blue; }\n\n\tif( majcomp == 1 )\n\t{\n\t\tt = red;\n\t\tred = green;\n\t\tgreen = t;\n\t}\n\tif( majcomp == 2 )\n\t{\n\t\tt = red;\n\t\tred = blue;\n\t\tblue = t;\n\t}\n\n\tp->ep[1][0] = bound( 0, red, 0xFFF );\n\tp->ep[1][1] = bound( 0, green, 0xFFF );\n\tp->ep[1][2] = bound( 0, blue, 0xFFF );\n\n\tp->ep[0][0] = bound( 0, red - scale, 0xFFF );\n\tp->ep[0][1] = bound( 0, green - scale, 0xFFF );\n\tp->ep[0][2] = bound( 0, blue - scale, 0xFFF );\n\n\tp->ep[1][3] = p->ep[0][3] = 0x780;\n\n\tp->hdr = 0xf;\n}\nstatic void ASTC_HDR_Mode_11(struct astc_part *p, unsigned char *v)\n{\n\tstatic const int dbitstab[8] = {7,6,7,6,5,6,5,6};\n\tint shamt;\n\tint majcomp = ((v[4] & 0x80) >> 7) | ((v[5] & 0x80) >> 6);\n\tint mode,va,vb0,vb1,vc,vd0,vd1;\n\tint x0,x1,x2,x3,x4,x5,ohm;\n\n\tif( majcomp == 3 )\n\t{\n\t\tVector4Set(p->ep[0], v[0] << 4, v[2] << 4, (v[4] & 0x7f) << 5, 0x780);\n\t\tVector4Set(p->ep[1], v[1] << 4, v[3] << 4, (v[5] & 0x7f) << 5, 0x780);\n\t\tp->hdr = 0xf;\n\t\treturn;\n\t}\n\n\tmode = ((v[1]&0x80)>>7) | ((v[2]&0x80)>>6) | ((v[3]&0x80)>>5);\n\tva  = v[0] | ((v[1] & 0x40) << 2);\n\tvb0 = v[2] & 0x3f;\n\tvb1 = v[3] & 0x3f;\n\tvc  = v[1] & 0x3f;\n\tvd0 = v[4] & 0x7f;\n\tvd1 = v[5] & 0x7f;\n\n\tif (vd0 & (1<<(dbitstab[mode]-1)))\n\t\tvd0 |= -1 & ~((1u<<dbitstab[mode])-1);\n\tif (vd1 & (1<<(dbitstab[mode]-1)))\n\t\tvd1 |= -1 & ~((1u<<dbitstab[mode])-1);\n\n\tx0 = (v[2] >> 6) & 1;\n\tx1 = (v[3] >> 6) & 1;\n\tx2 = (v[4] >> 6) & 1;\n\tx3 = (v[5] >> 6) & 1;\n\tx4 = (v[4] >> 5) & 1;\n\tx5 = (v[5] >> 5) & 1;\n\n\tohm = 1 << mode;\n\tif( ohm & 0xA4 ) va |= x0 << 9;\n\tif( ohm & 0x08 ) va |= x2 << 9;\n\tif( ohm & 0x50 ) va |= x4 << 9;\n\tif( ohm & 0x50 ) va |= x5 << 10;\n\tif( ohm & 0xA0 ) va |= x1 << 10;\n\tif( ohm & 0xC0 ) va |= x2 << 11;\n\tif( ohm & 0x04 ) vc |= x1 << 6;\n\tif( ohm & 0xE8 ) vc |= x3 << 6;\n\tif( ohm & 0x20 ) vc |= x2 << 7;\n\tif( ohm & 0x5B ) vb0 |= x0 << 6;\n\tif( ohm & 0x5B ) vb1 |= x1 << 6;\n\tif( ohm & 0x12 ) vb0 |= x2 << 7;\n\tif( ohm & 0x12 ) vb1 |= x3 << 7;\n\n\t// Now shift up so that major component is at top of 12-bit value\n\tshamt = (mode >> 1) ^ 3;\n\tva <<= shamt; vb0 <<= shamt; vb1 <<= shamt;\n\tvc <<= shamt; vd0 <<= shamt; vd1 <<= shamt;\n\n\tp->ep[1][0] = bound( 0, va, 0xFFF );\n\tp->ep[1][1] = bound( 0, va - vb0, 0xFFF );\n\tp->ep[1][2] = bound( 0, va - vb1, 0xFFF );\n\n\tp->ep[0][0] = bound( 0, va - vc, 0xFFF );\n\tp->ep[0][1] = bound( 0, va - vb0 - vc - vd0, 0xFFF );\n\tp->ep[0][2] = bound( 0, va - vb1 - vc - vd1, 0xFFF );\n\n\tif( majcomp == 1 )\n\t{\n\t\tp->ep[0][3] = p->ep[0][0];\n\t\tp->ep[0][0] = p->ep[0][1];\n\t\tp->ep[0][1] = p->ep[0][3];\n\t\tp->ep[1][3] = p->ep[1][0];\n\t\tp->ep[1][0] = p->ep[1][1];\n\t\tp->ep[1][1] = p->ep[1][3];\n\t}\n\telse if( majcomp == 2 )\n\t{\n\t\tp->ep[0][3] = p->ep[0][0];\n\t\tp->ep[0][0] = p->ep[0][2];\n\t\tp->ep[0][2] = p->ep[0][3];\n\t\tp->ep[1][3] = p->ep[1][0];\n\t\tp->ep[1][0] = p->ep[1][2];\n\t\tp->ep[1][2] = p->ep[1][3];\n\t}\n\n\tp->ep[0][3] = p->ep[1][3] = 0x780;\n\n\tp->hdr = 0xf;\n}\nstatic void ASTC_HDR_Mode_14(struct astc_part *p, unsigned char *v)\n{\n\tASTC_HDR_Mode_11(p, v);\n\n\tp->ep[0][3] = v[6];\n\tp->ep[1][3] = v[7];\n\tp->hdr &= 0x7;\n}\nstatic void ASTC_HDR_Mode_15(struct astc_part *p, unsigned char *v)\n{\n\tint v6=v[6], v7=v[7];\n\tint mode;\n\tASTC_HDR_Mode_11(p,v);\n\n\tmode = ((v6 >> 7) & 1) | ((v7 >> 6) & 2);\n\tv6 &= 0x7F;\n\tv7 &= 0x7F;\n\n\tif(mode==3)\n\t{\n\t\tp->ep[0][3] = v6 << 5;\n\t\tp->ep[1][3] = v7 << 5;\n\t}\n\telse\n\t{\n\t\tv6 |= (v7 << (mode+1)) & 0x780;\n\t\tv7 &= (0x3F >> mode);\n\t\tv7 ^= 0x20 >> mode;\n\t\tv7 -= 0x20 >> mode;\n\t\tv6 <<= (4-mode);\n\t\tv7 <<= (4-mode);\n\n\t\tv7 += v6;\n\t\tv7 = bound(0, v7, 0xFFF);\n\t\tp->ep[0][3] = v6;\n\t\tp->ep[1][3] = v7;\n\t}\n}\n#endif\n\nstatic void ASTC_DecodeEndpoints(struct astc_block_info *b, unsigned char *v)\n{\n\tint i, t0, t1, t3, t5, t7;\n\n\tfor (i = 0; i < b->partitions; i++)\n\t{\n#ifdef ASTC_WITH_HDR\n\t\tb->part[i].hdr = 0;\n#endif\n\t\tswitch (b->part[i].mode & 15)\n\t\t{\n#ifdef ASTC_WITH_HDR\n\t\tcase 2:\t\t//HDR Luminance, large range\n\t\t\tASTC_HDR_Mode_2(&b->part[i], v);\n\t\t\tbreak;\n\t\tcase 3:\t\t//HDR Luminance, small range\n\t\t\tASTC_HDR_Mode_3(&b->part[i], v);\n\t\t\tbreak;\n\t\tcase 7:\t\t//HDR RGB, base+scale\n\t\t\tASTC_HDR_Mode_7(&b->part[i], v);\n\t\t\tbreak;\n\t\tcase 11:\t//HDR RGB\n\t\t\tASTC_HDR_Mode_11(&b->part[i], v);\n\t\t\tbreak;\n\t\tcase 14:\t//HDR RGB + LDR Alpha\n\t\t\tASTC_HDR_Mode_14(&b->part[i], v);\n\t\t\tbreak;\n\t\tcase 15:\t//HDR RGB + HDR Alpha\n\t\t\tASTC_HDR_Mode_15(&b->part[i], v);\n\t\t\tbreak;\n#endif\n\t\tdefault:\t//the error colour - for unsupported hdr endpoints. unreachable when hdr is enabled. just fill it with the error colour.\n\t\t\tVector4Set(b->part[i].ep[0], 0xff, 0, 0xff, 0xff);\n\t\t\tVector4Set(b->part[i].ep[1], 0xff, 0, 0xff, 0xff);\n\t\t\tbreak;\n\n\t\tcase 0:\t//LDR Luminance, direct\n\t\t\tVector4Set(b->part[i].ep[0], v[0], v[0], v[0], 0xff);\n\t\t\tVector4Set(b->part[i].ep[1], v[1], v[1], v[1], 0xff);\n\t\t\tbreak;\n\t\tcase 1:\t//LDR Luminance, base+offset\n\t\t\tt0 = (v[0]>>2)|(v[1]&0xc0);\n\t\t\tt1 = t0+(v[1]&0x3f);\n\t\t\tif (t1>0xff)\n\t\t\t\tt1=0xff;\n\t\t\tVector4Set(b->part[i].ep[0], t0, t0, t0, 0xff);\n\t\t\tVector4Set(b->part[i].ep[1], t1, t1, t1, 0xff);\n\t\t\tbreak;\n\t\tcase 4:\t//LDR Luminance+Alpha,direct\n\t\t\tVector4Set(b->part[i].ep[0], v[0], v[0], v[0], v[2]);\n\t\t\tVector4Set(b->part[i].ep[1], v[1], v[1], v[1], v[3]);\n\t\t\tbreak;\n\t\tcase 5:\t//LDR Luminance+Alpha, base+offset\n\t\t\tt1 = ASTC_bit_transfer_signed(v[1],&v[0]);\n\t\t\tt3 = ASTC_bit_transfer_signed(v[3],&v[2]);\n\t\t\tVector4Set(b->part[i].ep[0],v[0],v[0],v[0],v[2]);\n\t\t\tVector4Set(b->part[i].ep[1],v[0]+t1,v[0]+t1,v[0]+t1,v[2]+t3);\n\t\t\tASTC_clamp_unorm8(b->part[i].ep[0]);\n\t\t\tASTC_clamp_unorm8(b->part[i].ep[1]);\n\t\t\tbreak;\n\t\tcase 6:\t//LDR RGB, base+scale\n\t\t\tVector4Set(b->part[i].ep[0], ((int)v[0]*(int)v[3])>>8, ((int)v[1]*(int)v[3])>>8, ((int)v[2]*(int)v[3])>>8, 0xff);\n\t\t\tVector4Set(b->part[i].ep[1], v[0], v[1], v[2], 0xff);\n\t\t\tbreak;\n\t\tcase 8:\t//LDR RGB, Direct\n\t\t\tt0 = (int)v[0]+(int)v[2]+(int)v[4];\n\t\t\tt1 = (int)v[1]+(int)v[3]+(int)v[5];\n\t\t\tif (t1>=t0)\n\t\t\t{\n\t\t\t\tVector4Set(b->part[i].ep[0], v[0],v[2],v[4],0xff);\n\t\t\t\tVector4Set(b->part[i].ep[1], v[1],v[3],v[5],0xff);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tASTC_blue_contract(b->part[i].ep[0], v[1],v[3],v[5], 0xff);\n\t\t\t\tASTC_blue_contract(b->part[i].ep[1], v[0],v[2],v[4], 0xff);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 9:\t//LDR RGB, base+offset\n\t\t\tt1 = ASTC_bit_transfer_signed(v[1],&v[0]);\n\t\t\tt3 = ASTC_bit_transfer_signed(v[3],&v[2]);\n\t\t\tt5 = ASTC_bit_transfer_signed(v[5],&v[4]);\n\t\t\tif(t1+t3+t5 >= 0)\n\t\t\t{\n\t\t\t\tVector4Set(b->part[i].ep[0],v[0],v[2],v[4],0xff);\n\t\t\t\tVector4Set(b->part[i].ep[1],v[0]+t1,v[2]+t3,v[4]+t5,0xff);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tASTC_blue_contract(b->part[i].ep[0], v[0]+t1,v[2]+t3,v[4]+t5, 0xff);\n\t\t\t\tASTC_blue_contract(b->part[i].ep[1], v[0],v[2],v[4], 0xff);\n\t\t\t}\n\t\t\tASTC_clamp_unorm8(b->part[i].ep[0]);\n\t\t\tASTC_clamp_unorm8(b->part[i].ep[1]);\n\t\t\tbreak;\n\t\tcase 10:\t//LDR RGB, base+scale plus two A\n\t\t\tVector4Set(b->part[i].ep[0], ((int)v[0]*v[3])>>8, ((int)v[1]*v[3])>>8, ((int)v[2]*v[3])>>8, v[4]);\n\t\t\tVector4Set(b->part[i].ep[1], v[0], v[1], v[2], v[5]);\n\t\t\tbreak;\n\t\tcase 12:\t//LDR RGBA, direct\n\t\t\tif (v[1]+(int)v[3]+v[5]>=v[0]+(int)v[2]+v[4])\n\t\t\t{\n\t\t\t\tVector4Set(b->part[i].ep[0], v[0],v[2],v[4],v[6]);\n\t\t\t\tVector4Set(b->part[i].ep[1], v[1],v[3],v[5],v[7]);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tASTC_blue_contract(b->part[i].ep[0], v[1],v[3],v[5],v[7]);\n\t\t\t\tASTC_blue_contract(b->part[i].ep[1], v[0],v[2],v[4],v[6]);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 13:\t//LDR RGBA, base+offset\n\t\t\tt1 = ASTC_bit_transfer_signed(v[1],&v[0]);\n\t\t\tt3 = ASTC_bit_transfer_signed(v[3],&v[2]);\n\t\t\tt5 = ASTC_bit_transfer_signed(v[5],&v[4]);\n\t\t\tt7 = ASTC_bit_transfer_signed(v[7],&v[6]);\n\t\t\tif(t1+t3+t5>=0)\n\t\t\t{\n\t\t\t\tVector4Set(b->part[i].ep[0], v[0],v[2],v[4],v[6]);\n\t\t\t\tVector4Set(b->part[i].ep[1], v[0]+t1,v[2]+t3,v[4]+t5,v[6]+t7);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tASTC_blue_contract(b->part[i].ep[0], v[0]+t1,v[2]+t3,v[4]+t5,v[6]+t7);\n\t\t\t\tASTC_blue_contract(b->part[i].ep[1], v[0],v[2],v[4],v[6]);\n\t\t\t}\n\t\t\tASTC_clamp_unorm8(b->part[i].ep[0]);\n\t\t\tASTC_clamp_unorm8(b->part[i].ep[1]);\n\t\t\tbreak;\n\t\t}\n\t\tv += ((b->part[i].mode>>2)+1)<<1;\n\t}\n}\nstatic void ASTC_ReadEndpoints(struct astc_block_info *b)\n{\n\tint i;\n\tint cembits;\n\n\tunsigned char epv[18];\t\t//maximum raw endpoint values,\n\tchar epvalues;\n\tunsigned char gahffs[16], t;\n\n\t//figure out how many raw values we need\n\tepvalues = 0;\n\tfor (i = 0; i < b->partitions; i++)\n\t\tepvalues += ((b->part[i].mode>>2)+1)<<1;\n\tif (epvalues > countof(epv))\n\t{\n\t\tb->status = ASTC_ERROR;\n\t\treturn;\n\t}\n\n\t//the endpoint bits are encoded using the largest size available that'll still fit, yielding raw values between 0-255.\n\tfor(i = countof(astc_epvmode)-1; i >= 0; i--)\n\t{\n\t\tcembits = ASTC_DecodeSize(epvalues, astc_epvmode[i].bits, astc_epvmode[i].extra);\n\t\tif(cembits <= b->ep_bits)\n\t\t{\n\t\t\t//read the values.\n\t\t\tASTC_Decode(b->in, epv, epvalues, b->config_bits, astc_epvmode[i].bits, astc_epvmode[i].extra, astc_epvmode[i].dequant);\n\t\t\t//and decode them.\n\t\t\tASTC_DecodeEndpoints(b, epv);\n\n\t\t\t//weight bits are backwards (gah! ffs!)\n\t\t\t//so swap them around so our decode function doesn't need to care\n\t\t\tfor (i = 0; i < countof(gahffs); i++)\n\t\t\t{\n\t\t\t\tt = b->in[i];\n\t\t\t\tt = (t>>4)|(t<<4);\n\t\t\t\tt = ((t&0xcc)>>2)|((t&0x33)<<2);\n\t\t\t\tt = ((t&0xaa)>>1)|((t&0x55)<<1);\n\t\t\t\tgahffs[15-i] = t;\n\t\t\t}\n\t\t\t//weights are aligned at the end... now the start. gah! ffs!\n\t\t\tASTC_Decode(gahffs, b->weights, b->wcount[3], 0, astc_weightmode[b->precision].bits, astc_weightmode[b->precision].extra, astc_weightmode[b->precision].dequant);\n\t\t\treturn;\n\t\t}\n\t}\n\tb->status = ASTC_ERROR;\n}\n\nstatic unsigned int hash52(unsigned int p)\n{\n    p ^= p >> 15;  p -= p << 17;  p += p << 7; p += p <<  4;\n    p ^= p >>  5;  p += p << 16;  p ^= p >> 7; p ^= p >> 3;\n    p ^= p <<  6;  p ^= p >> 17;\n    return p;\n}\nstatic int ASTC_ChoosePartition(int seed, int x, int y, int z, int partitions, int smallblock)\n{\n\tint sh1, sh2, sh3, a,b,c,d;\n\tunsigned int rnum;\n\tunsigned char seed1,seed2,seed3,seed4,seed5,seed6,seed7,seed8,seed9,seed10,seed11,seed12;\n\tif (partitions==1)\n\t\treturn 0;\n\tif (smallblock)\n\t{\n\t\tx <<= 1;\n\t\ty <<= 1;\n\t\tz <<= 1;\n\t}\n\tseed += (partitions-1) * 1024;\n\trnum = hash52(seed);\n\tseed1  =  rnum        & 0xF;\n\tseed2  = (rnum >>  4) & 0xF;\n\tseed3  = (rnum >>  8) & 0xF;\n\tseed4  = (rnum >> 12) & 0xF;\n\tseed5  = (rnum >> 16) & 0xF;\n\tseed6  = (rnum >> 20) & 0xF;\n\tseed7  = (rnum >> 24) & 0xF;\n\tseed8  = (rnum >> 28) & 0xF;\n\tseed9  = (rnum >> 18) & 0xF;\n\tseed10 = (rnum >> 22) & 0xF;\n\tseed11 = (rnum >> 26) & 0xF;\n\tseed12 = ((rnum >> 30) | (rnum << 2)) & 0xF;\n\n\tseed1  *= seed1;    seed2  *= seed2;\n\tseed3  *= seed3;    seed4  *= seed4;\n\tseed5  *= seed5;    seed6  *= seed6;\n\tseed7  *= seed7;    seed8  *= seed8;\n\tseed9  *= seed9;    seed10 *= seed10;\n\tseed11 *= seed11;   seed12 *= seed12;\n\n\n\tif (seed & 1)\n\t{\n\t\tsh1 = ((seed&2) ? 4:5);\n\t\tsh2 = ((partitions==3) ? 6:5);\n\t}\n\telse\n\t{\n\t\tsh1 = ((partitions==3) ? 6:5);\n\t\tsh2 = ((seed&2) ? 4:5);\n\t}\n\tsh3 = (seed & 0x10) ? sh1 : sh2;\n\n\tseed1 >>= sh1; seed2  >>= sh2; seed3  >>= sh1; seed4  >>= sh2;\n\tseed5 >>= sh1; seed6  >>= sh2; seed7  >>= sh1; seed8  >>= sh2;\n\tseed9 >>= sh3; seed10 >>= sh3; seed11 >>= sh3; seed12 >>= sh3;\n\n\ta = seed1*x + seed2*y + seed11*z + (rnum >> 14);\n\tb = seed3*x + seed4*y + seed12*z + (rnum >> 10);\n\tc = seed5*x + seed6*y + seed9 *z + (rnum >>  6);\n\td = seed7*x + seed8*y + seed10*z + (rnum >>  2);\n\n\ta &= 0x3F; b &= 0x3F; c &= 0x3F; d &= 0x3F;\n\n\tif (partitions < 4)\n\t\td = 0;\n\tif (partitions < 3)\n\t\tc = 0;\n\n\tif (a >= b && a >= c && a >= d)\n\t\treturn 0;\n\telse if (b >= c && b >= d)\n\t\treturn 1;\n\telse if (c >= d)\n\t\treturn 2;\n\telse\n\t\treturn 3;\n}\n#endif\n\n#ifdef ASTC_WITH_LDR\n//Spits out 8-bit RGBA data for a single block. Any HDR blocks will result in the error colour.\n//sRGB can be applied by the caller, if needed.\nASTC_PUBLIC void ASTC_Decode_LDR8(unsigned char *in, unsigned char *out, int pixstride, int layerstride, int bw, int bh, int bd)\n{\n\tstruct astc_block_info b;\n\tint x, y;\n\tint stride = pixstride*4;\n#ifdef ASTC_WITH_3D\n\tint z;\n\tlayerstride = layerstride*4-(stride*bh);\n#else\n\tif (bd != 1)\n\t\treturn;\t//error!\n#endif\n\tb.in = in;\n\tb.blocksize[0] = bw;\n\tb.blocksize[1] = bh;\n\tb.blocksize[2] = bd;\n\tASTC_ReadBlockMode(&b);\n\n\tif (b.status == ASTC_VOID_LDR)\n\t{\t//void extent\n\t\t//Note: we don't validate the extents.\n\t\tfor (y = 0; y < bh; y++, out += stride)\n\t\t\tfor (x = 0; x < bw; x++)\n\t\t\t{\n\t\t\t\tout[(x<<2)+0] = in[9];\n\t\t\t\tout[(x<<2)+1] = in[11];\n\t\t\t\tout[(x<<2)+2] = in[13];\n\t\t\t\tout[(x<<2)+3] = in[15];\n\t\t\t}\n\t\treturn;\n\t}\n\n\tif (b.status == ASTC_OKAY)\n\t\tASTC_ReadPartitions(&b);\n\tif (b.status == ASTC_OKAY)\n\t\tASTC_ReadEndpoints(&b);\n\n\tif (b.status == ASTC_OKAY)\n\t{\n\t\t#define N b.wcount[0]\n\t\t#define M b.wcount[1]\n\t\tint s1=1<<b.dualplane,s2=N<<b.dualplane;\t//values for 2d blocks (3d blocks will override)\n\t\tint s3=((bd!=1?N*M:0)+N+1)<<b.dualplane;\t//small variation for 3d blocks.\n\n\t\tint smallblock = (b.blocksize[0]*b.blocksize[1]*b.blocksize[2])<31;\n\t\tint fs, s, ds = (1024+b.blocksize[0]/2)/(b.blocksize[0]-1);\n\t\tint ft, t, dt = (1024+b.blocksize[1]/2)/(b.blocksize[1]-1);\n#ifdef ASTC_WITH_3D\n\t\tint fr, r, dr = (1024+b.blocksize[2]/2)/(b.blocksize[2]-1);\n#endif\n\t\tint v0, w, w00,w01,w10,w11;\n\t\tstruct astc_part *p;\n\n#ifdef ASTC_WITH_HDR\n\t\tfor (x = 0; x < b.partitions; x++)\n\t\t{\t//the LDR profile treats HDR endpoints as the error colour. this is per-partition rather than per-block.\n\t\t\tif (b.part[x].hdr)\n\t\t\t{\n\t\t\t\tVector4Set(b.part[x].ep[0], 0xff, 0, 0xff, 0xff);\n\t\t\t\tVector4Set(b.part[x].ep[1], 0xff, 0, 0xff, 0xff);\n\t\t\t}\n\t\t\t//else FIXME: when spitting out 8bit, we're meant to have an extra 9th bit which is always set, in order to avoid round-to-zero biasing the result of the final 8 bits.\n\t\t}\n#endif\n\n#ifdef ASTC_WITH_3D\n\t\tfor (z = 0; z < bd; z++, out += layerstride-stride*bh)\n#endif\n\t\t{\n#ifdef ASTC_WITH_3D\n\t\t\tr = ((dr*z)*(b.wcount[2]-1)+32)>>6;\n\t\t\tfr=r&0xf;\n#endif\n\t\t\tfor (y = 0; y < bh; y++, out += stride)\n\t\t\t{\n\t\t\t\tt = ((dt*y)*(b.wcount[1]-1)+32)>>6;\n\t\t\t\tft=t&0xf;\n\t\t\t\tfor (x = 0; x < bw; x++)\n\t\t\t\t{\n\t\t\t\t\tp = &b.part[ASTC_ChoosePartition(b.partindex, x,y,0, b.partitions, smallblock)];\n\t\t\t\t\ts = ((ds*x)*(b.wcount[0]-1)+32)>>6;\n\t\t\t\t\tfs=s&0xf;\n#ifdef ASTC_WITH_3D\n\t\t\t\t\tif (bd != 1)\n\t\t\t\t\t{\t//3d blocks use simplex interpolation instead of 8-way interpolation. its easier for hardware but more cycles for us.\n\t\t\t\t\t\tif (fs>fr)\n\t\t\t\t\t\t{\t//figure out which weights/factors to use.\n\t\t\t\t\t\t\tif (ft>fr)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tif (fs>ft)\n\t\t\t\t\t\t\t\t\ts1=1, s2=N, w00=16-fs, w01=fs-ft, w10=ft-fr, w11=fr;\n\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t\ts1=N, s2=1, w00=16-ft, w01=ft-fs, w10=fs-fr, w11=fr;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\ts1=1, s2=N*M, w00=16-fs, w01=fs-fr, w10=fr-ft, w11=ft;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (fs>ft)\n\t\t\t\t\t\t\t\ts1=N*M, s2=1, w00=16-fr, w01=fr-fs, w10=fs-ft, w11=ft;\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tif (ft>fr)\n\t\t\t\t\t\t\t\t\ts1=N, s2=N*M, w00=16-ft, w01=ft-fr, w10=fr-fs, w11=fs;\n\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t\ts1=N*M, s2=N, w00=16-fr, w01=fr-ft, w10=ft-fs, w11=fs;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\ts1 <<= b.dualplane;\n\t\t\t\t\t\ts2 <<= b.dualplane;\n\t\t\t\t\t\ts2+=s1;\n\t\t\t\t\t\t//s3 = (N*M+N+1)<<b.dualplane;\n\t\t\t\t\t\tv0 = ((s>>4)+(t>>4)*N+(r>>4)*N*M) << b.dualplane;\n\t\t\t\t\t}\n\t\t\t\t\telse\n#endif\n\t\t\t\t\t{\n\t\t\t\t\t\t//s1 = 1<<b.dualplane;\n\t\t\t\t\t\t//s2 = (N)<<b.dualplane;\n\t\t\t\t\t\t//s3 = (N+1)<<b.dualplane;\n\t\t\t\t\t\tw11 = (fs*ft+8) >> 4;\n\t\t\t\t\t\tw10 = ft - w11;\n\t\t\t\t\t\tw01 = fs - w11;\n\t\t\t\t\t\tw00 = 16 - fs - ft + w11;\n\t\t\t\t\t\tv0 = ((s>>4)+(t>>4)*N) << b.dualplane;\n\t\t\t\t\t}\n\t\t\t\t\tw =\t(\tw00*b.weights[v0] +\n\t\t\t\t\t\t\tw01*b.weights[v0+s1] +\n\t\t\t\t\t\t\tw10*b.weights[v0+s2] +\n\t\t\t\t\t\t\tw11*b.weights[v0+s3] + 8) >> 4;\n\t\t\t\t\tout[(x<<2)+0] = ((64-w)*p->ep[0][0] + w*p->ep[1][0])>>6;\n\t\t\t\t\tout[(x<<2)+1] = ((64-w)*p->ep[0][1] + w*p->ep[1][1])>>6;\n\t\t\t\t\tout[(x<<2)+2] = ((64-w)*p->ep[0][2] + w*p->ep[1][2])>>6;\n\t\t\t\t\tout[(x<<2)+3] = ((64-w)*p->ep[0][3] + w*p->ep[1][3])>>6;\n\n\t\t\t\t\tif (b.dualplane)\n\t\t\t\t\t{\t//dual planes has a second set of weights that override a single channel\n\t\t\t\t\t\tv0++;\n\t\t\t\t\t\tw =\t(\tw00*b.weights[v0] +\n\t\t\t\t\t\t\t\tw01*b.weights[v0+s1] +\n\t\t\t\t\t\t\t\tw10*b.weights[v0+s2] +\n\t\t\t\t\t\t\t\tw11*b.weights[v0+s3] + 8) >> 4;\n\t\t\t\t\t\tout[(x<<2)+b.ccs] = ((64-w)*p->ep[0][b.ccs] + w*p->ep[1][b.ccs])>>6;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t#undef N\n\t\t#undef M\n\t}\n\telse\n\t{\t//error colour == magenta\n#ifdef ASTC_WITH_3D\n\t\tfor (z = 0; z < bd; z++, out += layerstride)\n#endif\n\t\t\tfor (y = 0; y < bh; y++, out += stride)\n\t\t\t\tfor (x = 0; x < bw; x++)\n\t\t\t\t{\n\t\t\t\t\tout[(x<<2)+0] = 0xff;\n\t\t\t\t\tout[(x<<2)+1] = 0;\n\t\t\t\t\tout[(x<<2)+2] = 0xff;\n\t\t\t\t\tout[(x<<2)+3] = 0xff;\n\t\t\t\t}\n\t}\n}\n#endif\n\n#ifdef ASTC_WITH_HDR\nstatic unsigned short ASTC_GenHalffloat(int hdr, int rawval)\n{\n\tif (hdr)\n\t{\n\t\tint fp16, m;\n\t\tfp16 = (rawval&0xF800) >> 1;\n\t\tm = rawval&0x7FF;\n\t\tif (m < 512)\n\t\t\tfp16 |= (3*m)>>3;\n\t\telse if (m >= 1536)\n\t\t\tfp16 |= (5*m - 2048)>>3;\n\t\telse\n\t\t\tfp16 |= (4*m - 512)>>3;\n\t\treturn fp16;\n\t}\n\telse\n\t{\n\t\tunion\n\t\t{\n\t\t\tfloat f;\n\t\t\tunsigned int u;\n\t\t} u = {rawval/65535.0};\n\t\tint e = 0;\n\t\tint m;\n\n\t\te = ((u.u>>23)&0xff) - 127;\n\t\tif (e < -15)\n\t\t\treturn 0; //too small exponent, treat it as a 0 denormal\n\t\tif (e > 15)\n\t\t\tm = 0; //infinity instead of a nan\n\t\telse\n\t\t\tm = (u.u&((1<<23)-1))>>13;\n\t\treturn ((e+15)<<10) | m;\n\t}\n}\n\n//Spits out half-float RGBA data for a single block.\nASTC_PUBLIC void ASTC_Decode_HDR(unsigned char *in, unsigned short *out, int pixstride, int layerstride, int bw, int bh, int bd)\n{\n\tint x, y;\n\tint stride = pixstride*4;\n\tstruct astc_block_info b;\n#ifdef ASTC_WITH_3D\n\tint z;\n\tlayerstride = layerstride*4-(stride*bh);\n#else\n\tif (bd != 1)\n\t\treturn;\t//error!\n#endif\n   \tb.in = in;\n\tb.blocksize[0] = bw;\n\tb.blocksize[1] = bh;\n\tb.blocksize[2] = bd;\n\n\tASTC_ReadBlockMode(&b);\n\n\tif (b.status == ASTC_VOID_HDR)\n\t{\t//void extent\n\t\t//Note: we don't validate the extents.\n\t\tfor (y = 0; y < bh; y++, out += stride)\n\t\t\tfor (x = 0; x < bw; x++)\n\t\t\t{\t//hdr void extents already use fp16\n\t\t\t\tout[(x<<2)+0] = in[8] | (in[9]<<8);\n\t\t\t\tout[(x<<2)+1] = in[10] | (in[11]<<8);\n\t\t\t\tout[(x<<2)+2] = in[12] | (in[13]<<8);\n\t\t\t\tout[(x<<2)+3] = in[14] | (in[15]<<8);\n\t\t\t}\n\t\treturn;\n\t}\n\tif (b.status == ASTC_VOID_LDR)\n\t{\t//void extent\n\t\t//Note: we don't validate the extents.\n\t\tfor (y = 0; y < bh; y++, out += stride)\n\t\t\tfor (x = 0; x < bw; x++)\n\t\t\t{\n\t\t\t\tout[(x<<2)+0] = ASTC_GenHalffloat(0, in[8] | (in[9]<<8));\n\t\t\t\tout[(x<<2)+1] = ASTC_GenHalffloat(0, in[10] | (in[11]<<8));\n\t\t\t\tout[(x<<2)+2] = ASTC_GenHalffloat(0, in[12] | (in[13]<<8));\n\t\t\t\tout[(x<<2)+3] = ASTC_GenHalffloat(0, in[14] | (in[15]<<8));\n\t\t\t}\n\t\treturn;\n\t}\n\n\tif (b.status == ASTC_OKAY)\n\t\tASTC_ReadPartitions(&b);\n\tif (b.status == ASTC_OKAY)\n\t\tASTC_ReadEndpoints(&b);\n\n\tif (b.status == ASTC_OKAY)\n\t{\n\t\t#define N b.wcount[0]\n\t\t#define M b.wcount[1]\n\t\tint s1=1<<b.dualplane,s2=N<<b.dualplane;\t//values for 2d blocks (3d blocks will override)\n\t\tint s3=((bd!=1?N*M:0)+N+1)<<b.dualplane;\t//small variation for 3d blocks.\n\n\t\tint smallblock = (b.blocksize[0]*b.blocksize[1]*b.blocksize[2])<31;\n\t\tint fs, s, ds = (1024+b.blocksize[0]/2)/(b.blocksize[0]-1);\n\t\tint ft, t, dt = (1024+b.blocksize[1]/2)/(b.blocksize[1]-1);\n#ifdef ASTC_WITH_3D\n\t\tint fr, r, dr = (1024+b.blocksize[2]/2)/(b.blocksize[2]-1);\n#endif\n\t\tint v0, w, w00,w01,w10,w11;\n\t\tstruct astc_part *p;\n\n\t\tfor (x = 0; x < b.partitions; x++)\n\t\t{\t//we need to do a little extra processing here\n\t\t\tfor (y = 0; y < 4; y++)\n\t\t\t{\n\t\t\t\tif (b.part[x].hdr&(1<<y))\n\t\t\t\t{\t//the 12bit endpoint values are shifted up to 16bit...\n\t\t\t\t\tb.part[x].ep[0][y] <<= 4;\n\t\t\t\t\tb.part[x].ep[1][y] <<= 4;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\t//convert to unorm16.\n\t\t\t\t\tb.part[x].ep[0][y] |= b.part[x].ep[0][y] << 8;\n\t\t\t\t\tb.part[x].ep[1][y] |= b.part[x].ep[1][y] << 8;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n#ifdef ASTC_WITH_3D\n\t\tfor (z = 0; z < bd; z++, out += layerstride)\n#endif\n\t\t{\n#ifdef ASTC_WITH_3D\n\t\t\tr = ((dr*z)*(b.wcount[2]-1)+32)>>6;\n\t\t\tfr=r&0xf;\n#endif\n\t\t\tfor (y = 0; y < bh; y++, out += stride)\n\t\t\t{\n\t\t\t\tt = ((dt*y)*(b.wcount[1]-1)+32)>>6;\n\t\t\t\tft=t&0xf;\n\t\t\t\tfor (x = 0; x < bw; x++)\n\t\t\t\t{\n\t\t\t\t\tp = &b.part[ASTC_ChoosePartition(b.partindex, x,y,0, b.partitions, smallblock)];\n\t\t\t\t\ts = ((ds*x)*(b.wcount[0]-1)+32)>>6;\n\t\t\t\t\tfs=s&0xf;\n#ifdef ASTC_WITH_3D\n\t\t\t\t\tif (bd != 1)\n\t\t\t\t\t{\t//3d blocks use simplex interpolation instead of 8-way interpolation. its easier for hardware but more cycles for us.\n\t\t\t\t\t\tif (fs>fr)\n\t\t\t\t\t\t{\t//figure out which weights/factors to use.\n\t\t\t\t\t\t\tif (ft>fr)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tif (fs>ft)\n\t\t\t\t\t\t\t\t\ts1=1, s2=N, w00=16-fs, w01=fs-ft, w10=ft-fr, w11=fr;\n\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t\ts1=N, s2=1, w00=16-ft, w01=ft-fs, w10=fs-fr, w11=fr;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\ts1=1, s2=N*M, w00=16-fs, w01=fs-fr, w10=fr-ft, w11=ft;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (fs>ft)\n\t\t\t\t\t\t\t\ts1=N*M, s2=1, w00=16-fr, w01=fr-fs, w10=fs-ft, w11=ft;\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tif (ft>fr)\n\t\t\t\t\t\t\t\t\ts1=N, s2=N*M, w00=16-ft, w01=ft-fr, w10=fr-fs, w11=fs;\n\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t\ts1=N*M, s2=N, w00=16-fr, w01=fr-ft, w10=ft-fs, w11=fs;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\ts1 <<= b.dualplane;\n\t\t\t\t\t\ts2 <<= b.dualplane;\n\t\t\t\t\t\ts2+=s1;\n\t\t\t\t\t\t//s3 = (N*M+N+1)<<b.dualplane;\n\t\t\t\t\t\tv0 = (((s>>4))+((t>>4)*N)+(r>>4)*N*M) << b.dualplane;\n\t\t\t\t\t}\n\t\t\t\t\telse\n#endif\n\t\t\t\t\t{\n\t\t\t\t\t\t//s1 = 1<<b.dualplane;\n\t\t\t\t\t\t//s2 = (N)<<b.dualplane;\n\t\t\t\t\t\t//s3 = (N+1)<<b.dualplane;\n\t\t\t\t\t\tw11 = (fs*ft+8) >> 4;\n\t\t\t\t\t\tw10 = ft - w11;\n\t\t\t\t\t\tw01 = fs - w11;\n\t\t\t\t\t\tw00 = 16 - fs - ft + w11;\n\n\t\t\t\t\t\tv0 = (((s>>4))+(t>>4)*N) << b.dualplane;\n\t\t\t\t\t}\n\t\t\t\t\tw =\t(\tw00*b.weights[v0] +\n\t\t\t\t\t\t\tw01*b.weights[v0+s1] +\n\t\t\t\t\t\t\tw10*b.weights[v0+s2] +\n\t\t\t\t\t\t\tw11*b.weights[v0+s3] + 8) >> 4;\n\t\t\t\t\tout[(x<<2)+0] = ASTC_GenHalffloat(p->hdr&1, ((64-w)*p->ep[0][0] + w*p->ep[1][0])>>6);\n\t\t\t\t\tout[(x<<2)+1] = ASTC_GenHalffloat(p->hdr&1, ((64-w)*p->ep[0][1] + w*p->ep[1][1])>>6);\n\t\t\t\t\tout[(x<<2)+2] = ASTC_GenHalffloat(p->hdr&1, ((64-w)*p->ep[0][2] + w*p->ep[1][2])>>6);\n\t\t\t\t\tout[(x<<2)+3] = ASTC_GenHalffloat(p->hdr&8, ((64-w)*p->ep[0][3] + w*p->ep[1][3])>>6);\n\n\t\t\t\t\tif (b.dualplane)\n\t\t\t\t\t{\t//dual planes has a second set of weights that override a single channel\n\t\t\t\t\t\tv0++;\n\t\t\t\t\t\tw =\t(\tw00*b.weights[v0] +\n\t\t\t\t\t\t\t\tw01*b.weights[v0+s1] +\n\t\t\t\t\t\t\t\tw10*b.weights[v0+s2] +\n\t\t\t\t\t\t\t\tw11*b.weights[v0+s3] + 8) >> 4;\n\t\t\t\t\t\tout[(x<<2)+b.ccs] = ASTC_GenHalffloat(p->hdr&(1<<b.ccs), ((64-w)*p->ep[0][b.ccs] + w*p->ep[1][b.ccs])>>6);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t#undef N\n\t\t#undef M\n\t}\n\telse\n\t{\t//error colour == magenta\n#ifdef ASTC_WITH_3D\n\t\tfor (z = 0; z < bd; z++, out += layerstride)\n#endif\n\t\t\tfor (y = 0; y < bh; y++, out += stride)\n\t\t\t\tfor (x = 0; x < bw; x++)\n\t\t\t\t{\n\t\t\t\t\tout[(x<<2)+0] = 0xf<<10;\n\t\t\t\t\tout[(x<<2)+1] = 0;\n\t\t\t\t\tout[(x<<2)+2] = 0xf<<10;\n\t\t\t\t\tout[(x<<2)+3] = 0xf<<10;\n\t\t\t\t}\n\t}\n}\n#endif\n"
  },
  {
    "path": "engine/client/in_generic.c",
    "content": "//Generic input code.\r\n//mostly mouse support, but can also handle a few keyboard events.\r\n\r\n//Issues:\r\n\r\n//VirtualBox mouse integration is bugged. X11 code can handle tablets, but VirtualBox sends mouse clicks on the ps/2 device instead.\r\n//  you should be able to fix this with 'in_deviceids * 0 *', remapping both tablet+ps/2 to the same device id. \r\n\r\n//Android touchscreen inputs suck. should have some onscreen buttons, but they're still a bit poo or something. blame mods for not using csqc to do things themselves.\r\n\r\n#include \"quakedef.h\"\r\n\r\nextern qboolean mouse_active;\r\n\r\nstatic cvar_t m_filter = CVARF(\"m_filter\", \"0\", CVAR_ARCHIVE);\r\nstatic cvar_t m_forcewheel = CVARD(\"m_forcewheel\", \"1\", \"0: ignore mousewheels in apis where it is abiguous.\\n1: Use mousewheel when it is treated as a third axis. Motion above a threshold is ignored, to avoid issues with an unknown threshold.\\n2: Like 1, but excess motion is retained. The threshold specifies exact z-axis distance per notice.\");\r\nstatic cvar_t m_forcewheel_threshold = CVARD(\"m_forcewheel_threshold\", \"32\", \"Mousewheel graduations smaller than this will not trigger mousewheel deltas.\");\r\nstatic cvar_t m_touchstrafe = CVARAFD(\"m_touchstrafe\", \"0\", \"m_strafeonright\", CVAR_ARCHIVE, \"0: entire screen changes angles only.\\n1: right hand side controls strafing.\\n2:left hand side strafes.\");\r\n//static cvar_t m_fatpressthreshold = CVARFD(\"m_fatpressthreshold\", \"0.2\", CVAR_ARCHIVE, \"How fat your thumb has to be to register a fat press (touchscreens).\");\r\nstatic cvar_t m_longpressthreshold = CVARFD(\"m_longpressthreshold\", \"1\", CVAR_ARCHIVE, \"How long to press for it to register as a long press (touchscreens).\");\r\nstatic cvar_t m_touchmajoraxis = CVARFD(\"m_touchmajoraxis\", \"1\", CVAR_ARCHIVE, \"When using a touchscreen, use only the major axis for strafing.\");\r\nstatic cvar_t m_slidethreshold = CVARFD(\"m_slidethreshold\", \"10\", CVAR_ARCHIVE, \"How far your finger needs to move to be considered a slide event (touchscreens).\");\r\n\r\nstatic cvar_t m_accel\t\t\t= CVARAFD(\"m_accel\",\t\t\"0\",\t\"cl_mouseAccel\", CVAR_ARCHIVE, \"Values >0 will amplify mouse movement proportional to velocity. Small values have great effect. A lot of good Quake Live players use around the 0.1-0.2 mark, but this depends on your mouse CPI and polling rate.\");\r\nstatic cvar_t m_accel_style\t\t= CVARAD(\"m_accel_style\",\t\"1\",\t\"cl_mouseAccelStyle\",\t\"1 = Quake Live mouse acceleration, 0 = Old style accelertion.\");\r\nstatic cvar_t m_accel_power\t\t= CVARAD(\"m_accel_power\",\t\"2\",\t\"cl_mouseAccelPower\",\t\"Used when m_accel_style is 1.\\nValues 1 or below are dumb. 2 is linear and the default. 99% of accel users use this. Above 2 begins to amplify exponentially and you will get more acceleration at higher velocities. Great if you want low accel for slow movements, and high accel for fast movements. Good in combination with a sensitivity cap.\");\r\nstatic cvar_t m_accel_offset\t= CVARAD(\"m_accel_offset\",\t\"0\",\t\"cl_mouseAccelOffset\",\t\"Used when m_accel_style is 1.\\nAcceleration will not be active until the mouse movement exceeds this speed (counts per millisecond). Negative values are supported, which has the effect of causing higher rates of acceleration to happen at lower velocities.\");\r\nstatic cvar_t m_accel_senscap\t= CVARAD(\"m_accel_senscap\",\t\"0\",\t\"cl_mouseSensCap\",\t\t\"Used when m_accel_style is 1.\\nSets an upper limit on the amplified mouse movement. Great for tuning acceleration around lower velocities while still remaining in control of fast motion such as flicking.\");\r\n\r\nvoid QDECL joyaxiscallback(cvar_t *var, char *oldvalue)\r\n{\r\n\tint sign;\r\n\tchar *end;\r\n\tsign = strtol(var->string, &end, 0);\r\n\tif (!*end)\r\n\t{\r\n\t\t//okay, its missing or an actual number.\r\n\t\tif (sign >= -6  &&  sign <= 6) {\r\n\t\t\tvar->ival = sign;\r\n\t\t}\r\n\t\treturn;\r\n\t}\r\n\r\n\tend = var->string;\r\n\tif (*end == '-')\r\n\t{\r\n\t\tend++;\r\n\t\tsign = -1;\r\n\t}\r\n\telse if (*end == '+')\r\n\t{\r\n\t\tend++;\r\n\t\tsign = 1;\r\n\t}\r\n\telse\r\n\t\tsign = 1;\r\n\tif (!Q_strcasecmp(end, \"forward\") || !Q_strcasecmp(end, \"moveforward\"))\r\n\t\tvar->ival = 1*sign;\r\n\telse if (!Q_strcasecmp(end, \"back\") || !Q_strcasecmp(end, \"moveback\"))\r\n\t\tvar->ival = 1*sign*-1;\r\n\telse if (!Q_strcasecmp(end, \"lookup\") || !Q_strcasecmp(end, \"pitchup\"))\r\n\t\tvar->ival = 2*sign;\r\n\telse if (!Q_strcasecmp(end, \"lookdown\") || !Q_strcasecmp(end, \"pitchdown\"))\r\n\t\tvar->ival = 2*sign*-1;\r\n\telse if (!Q_strcasecmp(end, \"moveright\"))\r\n\t\tvar->ival = 3*sign;\r\n\telse if (!Q_strcasecmp(end, \"moveleft\"))\r\n\t\tvar->ival = 3*sign*-1;\r\n\telse if (!Q_strcasecmp(end, \"right\") || !Q_strcasecmp(end, \"turnright\"))\r\n\t\tvar->ival = 4*sign;\r\n\telse if (!Q_strcasecmp(end, \"left\") || !Q_strcasecmp(end, \"turnleft\"))\r\n\t\tvar->ival = 4*sign*-1;\r\n\telse if (!Q_strcasecmp(end, \"up\") || !Q_strcasecmp(end, \"moveup\"))\r\n\t\tvar->ival = 5*sign;\r\n\telse if (!Q_strcasecmp(end, \"down\") || !Q_strcasecmp(end, \"movedown\"))\r\n\t\tvar->ival = 5*sign*-1;\r\n\telse if (!Q_strcasecmp(end, \"rollright\"))\r\n\t\tvar->ival = 6*sign;\r\n\telse if (!Q_strcasecmp(end, \"rollleft\"))\r\n\t\tvar->ival = 6*sign*-1;\r\n}\r\n\r\nstatic cvar_t\tjoy_advaxis[6] =\r\n{\r\n#define ADVAXISDESC (const char *)\"Provides a way to remap each joystick/controller axis.\\nShould be set to one of: moveforward, moveback, lookup, lookdown, turnleft, turnright, moveleft, moveright, moveup, movedown, rollleft, rollright\"\r\n\tCVARCD(\"joyadvaxisx\", \"moveright\", joyaxiscallback, ADVAXISDESC),\t//left rightwards axis\r\n\tCVARCD(\"joyadvaxisy\", \"moveback\", joyaxiscallback, ADVAXISDESC),\t//left downwards axis\r\n\tCVARCD(\"joyadvaxisz\", \"\", joyaxiscallback, ADVAXISDESC),\t\t\t//typically left trigger (use it as a button)\r\n\tCVARCD(\"joyadvaxisr\", \"turnright\", joyaxiscallback, ADVAXISDESC),\t//right rightwards axis\r\n\tCVARCD(\"joyadvaxisu\", \"lookup\", joyaxiscallback, ADVAXISDESC),\t\t//right downwards axis\r\n\tCVARCD(\"joyadvaxisv\", \"\", joyaxiscallback, ADVAXISDESC)\t\t\t\t//typically right trigger\r\n};\r\nstatic cvar_t\tjoy_advaxisscale[6] =\r\n{\r\n#define ADVAXISSCALEDESC \"Because joyadvaxisx etc can be added together, this provides a way to rescale or invert an individual axis without affecting another with the same action.\"\r\n\tCVARD(\"joyadvaxisx_scale\", \"1.0\", ADVAXISSCALEDESC),\r\n\tCVARD(\"joyadvaxisy_scale\", \"1.0\", ADVAXISSCALEDESC),\r\n\tCVARD(\"joyadvaxisz_scale\", \"1.0\", ADVAXISSCALEDESC),\r\n\tCVARD(\"joyadvaxisr_scale\", \"1.0\", ADVAXISSCALEDESC),\r\n\tCVARD(\"joyadvaxisu_scale\", \"1.0\", ADVAXISSCALEDESC),\r\n\tCVARD(\"joyadvaxisv_scale\", \"1.0\", ADVAXISSCALEDESC)\r\n};\r\nstatic cvar_t\tjoy_anglesens[3] =\r\n{\r\n#define ANGLESENSDESC \"Scaler value for the controller when it is at its most extreme value\"\r\n\tCVARD(\"joypitchsensitivity\", \"0.5\", ANGLESENSDESC),\r\n\tCVARD(\"joyyawsensitivity\", \"1.0\", ANGLESENSDESC),\r\n\tCVARD(\"joyrollsensitivity\", \"1.0\", ANGLESENSDESC)\r\n};\r\nstatic cvar_t\tjoy_movesens[3] =\r\n{\r\n\tCVAR(\"joyforwardsensitivity\", \"1.0\"),\r\n\tCVAR(\"joysidesensitivity\", \"1.0\"),\r\n\tCVAR(\"joyupsensitivity\", \"1.0\")\r\n};\r\n//comments on threshholds comes from microsoft's xinput docs.\r\nstatic cvar_t\tjoy_anglethreshold[3] =\r\n{\r\n#define ANGLETHRESHOLDDESC \"Values reported near the center of the analog joystick/controller are often erroneous and undesired.\\nThe joystick threshholds are how much of the total values to ignore.\"\r\n\tCVARD(\"joypitchthreshold\", \"0.19\", ANGLETHRESHOLDDESC),\t//8689/32767 (right thumb)\r\n\tCVARD(\"joyyawthreshold\", \"0.19\", ANGLETHRESHOLDDESC),\t//8689/32767 (right thumb)\r\n\tCVARD(\"joyrollthreshold\", \"0.118\", ANGLETHRESHOLDDESC),\t//30/255\t (trigger)\r\n};\r\nstatic cvar_t\tjoy_movethreshold[3] =\r\n{\r\n\tCVAR(\"joyforwardthreshold\", \"0.17\"),//7849/32767 (left thumb)\r\n\tCVAR(\"joysidethreshold\", \"0.17\"),\t//7849/32767 (left thumb)\r\n\tCVAR(\"joyupthreshold\", \"0.118\"),\t//30/255\t (trigger)\r\n};\r\n\r\nstatic cvar_t joy_exponent = CVARD(\"joyexponent\", \"1\", \"Scales joystick/controller sensitivity non-linearly to increase precision in the center.\\nA value of 1 is linear.\");\r\n\r\n#if defined(__linux__) && defined(FTE_SDL)\r\n#ifdef FTE_SDL3\r\n#include <SDL3/SDL.h>\r\n#else\r\n#include <SDL.h>\r\n#endif\r\nvoid joy_radialdeadzone_cb(cvar_t *var, char *oldvalue)\r\n{\r\n\tif (!*var->string)\r\n\t{\r\n#if SDL_VERSION_ATLEAST(3,0,0)\r\n\t\tif (SDL_GetHintBoolean(SDL_HINT_JOYSTICK_LINUX_DEADZONES, true))\r\n#else\r\n\t\tif (SDL_GetHintBoolean(SDL_HINT_LINUX_JOYSTICK_DEADZONES, true))\r\n#endif\r\n\t\t\tvar->ival = 2;\t//sdl2 provides its own deadzones on linux, by default.\r\n\t\telse\r\n\t\t\tvar->ival = 1;\r\n\t}\r\n}\r\n#else\r\nvoid joy_radialdeadzone_cb(cvar_t *var, char *oldvalue)\r\n{\r\n\tif (!*var->string)\r\n\t\tvar->ival = 1;\r\n}\r\n#endif\r\nstatic cvar_t joy_radialdeadzone = CVARCD(\"joyradialdeadzone\", \"\", joy_radialdeadzone_cb, \"Treat controller dead zones as a pair, rather than per-axis.\\n0: treat joystick axis independantly (square).\\n1: treat the axis together, radially.\\n2: do not handle deadzones (prefiltered).\");\r\n\r\ncvar_t in_skipplayerone = CVARD(\"in_skipplayerone\", \"1\", \"Do not auto-assign joysticks/game-controllers to the first player. Requires in_restart to take effect.\");\t//FIXME: this needs to be able to change deviceids when changed. until then menus will need to in_restart.\r\n\r\n\r\n#define EVENTQUEUELENGTH 1024\r\nstatic struct eventlist_s\r\n{\r\n\tenum\r\n\t{\r\n\t\tIEV_KEYDOWN,\r\n\t\tIEV_KEYRELEASE,\r\n\t\tIEV_MOUSEABS,\r\n\t\tIEV_MOUSEDELTA,\r\n\t\tIEV_JOYAXIS,\r\n\t\tIEV_ACCELEROMETER,\r\n\t\tIEV_GYROSCOPE,\r\n\t} type;\r\n\tunsigned int devid;\r\n\r\n\tunion\r\n\t{\r\n\t\tstruct\r\n\t\t{\r\n\t\t\tfloat x, y, z;\r\n\t\t\tfloat tsize;\t//the size of the touch\r\n\t\t} mouse;\r\n\t\tstruct\r\n\t\t{\r\n\t\t\tint scancode, unicode;\r\n\t\t} keyboard;\r\n\t\tstruct\r\n\t\t{\r\n\t\t\tint axis;\r\n\t\t\tfloat value;\r\n\t\t} joy;\r\n\t\tstruct\r\n\t\t{\t//metres per second, ish.\r\n\t\t\tfloat x, y, z;\r\n\t\t} accel;\r\n\t\tstruct\r\n\t\t{\t//these are in radians, not degrees.\r\n\t\t\tfloat pitch, yaw, roll;\r\n\t\t} gyro;\r\n\t};\r\n} eventlist[EVENTQUEUELENGTH];\r\nstatic volatile int events_avail; /*volatile to make sure the cc doesn't try leaving these cached in a register*/\r\nstatic volatile int events_used;\r\n\r\nstatic struct eventlist_s *in_newevent(void)\r\n{\r\n\tif (events_avail >= events_used + EVENTQUEUELENGTH)\r\n\t\treturn NULL;\r\n\treturn &eventlist[events_avail & (EVENTQUEUELENGTH-1)];\r\n}\r\n\r\nstatic void in_finishevent(void)\r\n{\r\n\tevents_avail++;\r\n}\r\n\r\n#define MAXPOINTERS 8\r\nstatic struct mouse_s\r\n{\r\n\tenum\r\n\t{\r\n\t\tM_INVALID,\r\n\t\tM_MOUSE,\t//using deltas\r\n\t\tM_TOUCH\t\t//using absolutes\r\n\t\t//the only functional difference between touch and tablets is that tablets should draw the cursor when hovering too.\r\n\t} type;\r\n\tunsigned int qdeviceid;\t//so we can just use pointers.\r\n\tvec2_t oldpos;\t\t//last-known-cursor-position\r\n\tvec2_t heldpos;\t\t//position the cursor was at when the button was held (touch-start pos)\r\n\tfloat moveddist;\t//how far it has moved while held. this provides us with our emulated mouse1 when they release the press\r\n\tvec2_t delta;\t\t//how far its moved recently\r\n\tvec2_t old_delta;\t//how far its moved previously, for mouse smoothing\r\n\tfloat wheeldelta;\r\n\tdouble touchtime;\t//0 when not touching, otherwise start time of touch.\r\n\tunsigned int touchkey;\t//which other key we generated (so it gets released again.\r\n\tunsigned int updates;\t//tracks updates per second\r\n\tqboolean updated;\r\n} ptr[MAXPOINTERS];\r\nstatic int touchcursor = -1;\t//the cursor follows whichever finger was most recently pressed in preference to any mouse also on the same system\r\n\r\n#define MAXJOYAXIS 6\r\n#define MAXJOYSTICKS 8\r\nstatic struct joy_s\r\n{\r\n\tunsigned int qdeviceid;\r\n\tfloat axis[MAXJOYAXIS];\r\n} joy[MAXJOYSTICKS];\r\n\r\nvoid IN_Shutdown(void)\r\n{\r\n\tINS_Shutdown();\r\n}\r\n\r\nvoid IN_ReInit(void)\r\n{\r\n\tint i;\r\n\r\n\tfor (i = 0; i < MAXPOINTERS; i++)\r\n\t{\r\n\t\tmemset(&ptr[i], 0, sizeof(ptr[i]));\r\n\t\tptr[i].type = M_INVALID;\r\n\t\tptr[i].qdeviceid = i;\r\n\t}\r\n\r\n\tfor (i = 0; i < MAXJOYSTICKS; i++)\r\n\t{\r\n\t\tmemset(&joy[i], 0, sizeof(joy[i]));\r\n\t\tjoy[i].qdeviceid = i;\r\n\t}\r\n\r\n\tINS_ReInit();\r\n}\r\n\r\nstruct remapctx\r\n{\r\n\tchar *type;\r\n\tchar *devicename;\r\n\tunsigned int newdevid;\r\n\tunsigned int found;\r\n\tunsigned int failed;\r\n};\r\nstatic void IN_DeviceIDs_DoRemap(void *vctx, const char *type, const char *devicename, unsigned int *qdevid)\r\n{\r\n\tstruct remapctx *ctx = vctx;\r\n\r\n\tif (!strcmp(ctx->type, type) || !strcmp(ctx->type, \"*\"))\r\n\t\tif (!strcmp(ctx->devicename, devicename) || !strcmp(ctx->devicename, \"*\"))\r\n\t\t{\r\n\t\t\tif (qdevid)\r\n\t\t\t\t*qdevid = ctx->newdevid;\r\n\t\t\telse\r\n\t\t\t\tctx->failed = true;\r\n\t\t\tctx->found++;\r\n\t\t}\r\n}\r\nvoid IN_DeviceIDs_Enumerate(void *ctx, const char *type, const char *devicename, unsigned int *qdevid)\r\n{\r\n\tchar buf[8192];\r\n\tdevicename = COM_QuotedString(devicename, buf, sizeof(buf), false);\r\n\tif (!qdevid)\r\n\t\tCon_Printf(\"%s\\t%s\\t%s\\n\", type, \"N/A\", devicename);\r\n\telse if (*qdevid == DEVID_UNSET)\r\n\t\tCon_Printf(\"%s\\t%s\\t%s\\n\", type, \"Unset\", devicename);\r\n\telse\r\n\t\tCon_Printf(\"%s\\t%u\\t%s\\n\", type, *qdevid, devicename);\r\n}\r\n\r\nvoid IN_DeviceIDs_f(void)\r\n{\r\n\tstruct remapctx ctx;\r\n\r\n\tif (Cmd_Argc() > 3)\r\n\t{\r\n\t\tctx.failed = false;\r\n\t\tctx.found = 0;\r\n\t\tctx.type = Cmd_Argv(1);\r\n\t\tctx.newdevid = strtoul(Cmd_Argv(2), NULL, 0);\r\n\t\tctx.devicename = Cmd_Argv(3);\r\n\t\tINS_EnumerateDevices(&ctx, IN_DeviceIDs_DoRemap);\r\n\r\n\t\tif (ctx.failed)\r\n\t\t\tCon_Printf(\"device cannot be remapped\\n\");\r\n\t\telse if (!ctx.found)\r\n\t\t\tCon_Printf(\"%s \\\"%s\\\" not known\\n\", ctx.type, ctx.devicename);\r\n\t\telse if (!cl_warncmd.ival)\r\n\t\t\tCon_Printf(\"device remapped\\n\");\r\n\t}\r\n\telse if (Cmd_Argc() > 1)\r\n\t{\r\n\t\tCon_Printf(\"%s TYPE NEWID DEVICENAME\\n\", Cmd_Argv(0));\r\n\t}\r\n\telse\r\n\t{\r\n\t\tCon_Printf(\"Type\\tMapping\\tName\\n\");\r\n\t\tINS_EnumerateDevices(NULL, IN_DeviceIDs_Enumerate);\r\n\t}\r\n}\r\n\r\nfloat IN_DetermineMouseRate(void)\r\n{\r\n\tdouble time = Sys_DoubleTime();\r\n\tstatic double timer;\r\n\tstatic float last;\r\n\tfloat interval = time - timer;\r\n\tif (fabs(interval) >= 1)\r\n\t{\r\n\t\ttimer = time;\r\n\t\tlast = ptr[0].updates/interval;\r\n\t\tptr[0].updates = 0;\r\n\t}\r\n\treturn last;\r\n}\r\n\r\nvoid IN_Init(void)\r\n{\r\n\tint i;\r\n\tevents_avail = 0;\r\n\tevents_used = 0;\r\n\r\n\tCvar_Register (&m_filter, \"input controls\");\r\n\tCvar_Register (&m_forcewheel, \"Input Controls\");\r\n\tCvar_Register (&m_forcewheel_threshold, \"Input Controls\");\r\n\tCvar_Register (&m_touchstrafe, \"input controls\");\r\n\tCvar_Register (&m_longpressthreshold, \"input controls\");\r\n\tCvar_Register (&m_slidethreshold, \"input controls\");\r\n\tCvar_Register (&m_touchmajoraxis, \"input controls\");\r\n\tCvar_Register (&m_accel, \"input controls\");\r\n\tCvar_Register (&m_accel_style, \"input controls\");\r\n\tCvar_Register (&m_accel_power, \"input controls\");\r\n\tCvar_Register (&m_accel_offset, \"input controls\");\r\n\tCvar_Register (&m_accel_senscap, \"input controls\");\r\n\r\n\tfor (i = 0; i < 6; i++)\r\n\t{\r\n\t\tCvar_Register (&joy_advaxis[i], \"input controls\");\r\n\t\tCvar_Register (&joy_advaxisscale[i], \"input controls\");\r\n\r\n\t\tCvar_ForceCallback(&joy_advaxis[i]);\r\n\t}\r\n\tfor (i = 0; i < 3; i++)\r\n\t{\r\n\t\tCvar_Register (&joy_anglesens[i], \"input controls\");\r\n\t\tCvar_Register (&joy_movesens[i], \"input controls\");\r\n\t\tCvar_Register (&joy_anglethreshold[i], \"input controls\");\r\n\t\tCvar_Register (&joy_movethreshold[i], \"input controls\");\r\n\t}\r\n\tCvar_Register (&joy_exponent, \"input controls\");\r\n\tCvar_Register (&joy_radialdeadzone, \"input controls\");\r\n\tCvar_Register (&in_skipplayerone, \"input controls\");\r\n\r\n\tCmd_AddCommand (\"in_deviceids\", IN_DeviceIDs_f);\r\n\r\n\tINS_Init();\r\n}\r\n\r\n//there was no ui to click on at least...\r\n//translates touch press events into ones that are actually bound, according to touchstrafe and position.\r\nint IN_Touch_Fallback(unsigned int devid)\r\n{\r\n\tint ret;\r\n\tif (devid >= countof(ptr))\r\n\t\tret = 0;\r\n\telse switch(m_touchstrafe.ival)\t//translate touch to mouse2 if its on the strafing side of the screen.\r\n\t{\r\n\tcase 2:\r\n\t\tret = ptr[devid].heldpos[0] < vid.pixelwidth/2;\r\n\t\tbreak;\r\n\tdefault:\r\n\t\tret = ptr[devid].heldpos[0] > vid.pixelwidth/2;\r\n\t\tbreak;\r\n\tcase 0:\r\n\t\tret = false;\r\n\t\tbreak;\r\n\t}\r\n\tret = ret?K_MOUSE2:K_MOUSE1;\r\n\r\n\treturn ret;\r\n}\r\nvoid IN_Touch_BlockGestures(unsigned int devid)\r\n{\t//called via K_TOUCH, blocks K_TOUCHTAP etc gestures\r\n\tif (devid < countof(ptr))\r\n\t\tptr[devid].touchkey = 0;\t//block it all.\r\n}\r\nqboolean IN_Touch_MouseIsAbs(unsigned int devid)\r\n{\t//lets the caller know if a mouse1 down event was abs\r\n\tif (devid < countof(ptr))\r\n\t\treturn ptr[devid].type == M_TOUCH;\r\n\treturn false;\r\n}\r\n\r\n/*a 'pointer' is either a multitouch pointer, or a separate device\r\nnote that mice use the keyboard button api, but separate devices*/\r\nvoid IN_Commands(void)\r\n{\r\n\tstruct eventlist_s *ev;\r\n\r\n\tINS_Commands();\r\n\r\n\twhile (events_used != events_avail)\r\n\t{\r\n\t\tev = &eventlist[events_used & (EVENTQUEUELENGTH-1)];\r\n\r\n\t\tswitch(ev->type)\r\n\t\t{\r\n\t\tcase IEV_KEYRELEASE:\r\n//Con_Printf(\"IEV_KEYDOWN %i: %i '%c'\\n\", ev->devid, ev->keyboard.scancode, ev->keyboard.unicode?ev->keyboard.unicode:' ');\r\n\t\t\tif (ev->keyboard.scancode == -1)\r\n\t\t\t{\r\n\t\t\t\tint i;\r\n\t\t\t\tfor (i = 0; i < K_MAX; i++)\r\n\t\t\t\t\tKey_Event(ev->devid, i, 0, false);\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\t\t\tif ((ev->keyboard.scancode == K_MOUSE1||ev->keyboard.scancode==K_TOUCH) && ev->devid < MAXPOINTERS && (ptr[ev->devid].type == M_TOUCH))\r\n\t\t\t{\t//touch (or abs clicks)\r\n\t\t\t\tstruct mouse_s *m = &ptr[ev->devid];\r\n\t\t\t\tif (touchcursor == ev->devid)\r\n\t\t\t\t\ttouchcursor = -1;\t//revert it to the mouse, or whatever device was 0.\r\n\r\n\t\t\t\tif (ev->keyboard.scancode==K_TOUCH && m->touchtime && m->touchkey==K_TOUCH)\r\n\t\t\t\t{\t//convert to tap/longpress if it wasn't already a gesture. don't ever convert mouse.\r\n\t\t\t\t\tif (Sys_DoubleTime()-m->touchtime > m_longpressthreshold.value)\r\n\t\t\t\t\t\tm->touchkey = K_TOUCHLONG;\r\n\t\t\t\t\telse\r\n\t\t\t\t\t\tm->touchkey = K_TOUCHTAP;\r\n\t\t\t\t\tKey_Event(ev->devid, m->touchkey, 0, true);\r\n\t\t\t\t}\r\n\t\t\t\tif (m->touchkey && m->touchkey!=ev->keyboard.scancode)\r\n\t\t\t\t\tKey_Event(ev->devid, m->touchkey, 0, false);\t//and release...\r\n\r\n\t\t\t\t//reset it.\r\n\t\t\t\tm->touchkey = 0;\r\n\t\t\t\tm->touchtime = 0;\r\n\t\t\t\tm->moveddist = 0;\r\n\t\t\t}\r\n\t\t\tKey_Event(ev->devid, ev->keyboard.scancode, ev->keyboard.unicode, false);\r\n\t\t\tbreak;\r\n\t\tcase IEV_KEYDOWN:\r\n//Con_Printf(\"IEV_KEYDOWN %i: %i '%c'\\n\", ev->devid, ev->keyboard.scancode, ev->keyboard.unicode?ev->keyboard.unicode:' ');\r\n\t\t\tif ((ev->keyboard.scancode == K_MOUSE1||ev->keyboard.scancode==K_TOUCH) && ev->devid < MAXPOINTERS && (ptr[ev->devid].type == M_TOUCH))\r\n\t\t\t{\t//touch (or abs clicks)\r\n\t\t\t\tstruct mouse_s *m = &ptr[ev->devid];\r\n\t\t\t\tfloat fl;\r\n\t\t\t\ttouchcursor = ev->devid;\r\n\t\t\t\tfl = m->oldpos[0] * vid.width / vid.pixelwidth;\t\tmousecursor_x = bound(0, fl, vid.width-1);\r\n\t\t\t\tfl = m->oldpos[1] * vid.height / vid.pixelheight;\tmousecursor_y = bound(0, fl, vid.height-1);\r\n\r\n\t\t\t\tm->touchtime = Sys_DoubleTime();\r\n\t\t\t\tm->moveddist = 0;\r\n\t\t\t\tif (ev->keyboard.scancode==K_TOUCH)\r\n\t\t\t\t\tm->touchkey = K_TOUCH;\r\n\t\t\t\telse\r\n\t\t\t\t\tm->touchkey = 0;\r\n\t\t\t}\r\n\t\t\tKey_Event(ev->devid, ev->keyboard.scancode, ev->keyboard.unicode, true);\r\n\t\t\tbreak;\r\n\t\tcase IEV_JOYAXIS:\r\n\t\t\tif (ev->devid < MAXJOYSTICKS && ev->joy.axis>=0 && ev->joy.axis<MAXJOYAXIS)\r\n\t\t\t{\r\n\t\t\t\tif (topmenu && topmenu->joyaxis && topmenu->joyaxis(topmenu, ev->devid, ev->joy.axis, ev->joy.value))\r\n\t\t\t\t\tjoy[ev->devid].axis[ev->joy.axis] = 0;\r\n#ifdef CSQC_DAT\r\n\t\t\t\telse if (CSQC_JoystickAxis(ev->joy.axis, ev->joy.value, ev->devid))\r\n\t\t\t\t\tjoy[ev->devid].axis[ev->joy.axis] = 0;\r\n#endif\r\n\t\t\t\telse\r\n\t\t\t\t\tjoy[ev->devid].axis[ev->joy.axis] = ev->joy.value;\r\n\t\t\t}\r\n\t\t\tbreak;\r\n\t\tcase IEV_MOUSEDELTA:\r\n//Con_Printf(\"IEV_MOUSEDELTA %i: %f %f\\n\", ev->devid, ev->mouse.x, ev->mouse.y);\r\n\t\t\tif (ev->devid < MAXPOINTERS)\r\n\t\t\t{\r\n\t\t\t\tif (ev->mouse.x || ev->mouse.y)\r\n\t\t\t\t\tptr[ev->devid].updated = true;\r\n\t\t\t\tif (ptr[ev->devid].type != M_MOUSE)\r\n\t\t\t\t{\r\n\t\t\t\t\tptr[ev->devid].type = M_MOUSE;\r\n\t\t\t\t}\r\n\t\t\t\tptr[ev->devid].delta[0] += ev->mouse.x;\r\n\t\t\t\tptr[ev->devid].delta[1] += ev->mouse.y;\r\n\r\n\t\t\t\t//if we're emulating a cursor, make sure that's updated too.\r\n\t\t\t\tif (touchcursor < 0 && !vrui.enabled && Key_MouseShouldBeFree())\r\n\t\t\t\t{\r\n\t\t\t\t\tmousecursor_x += ev->mouse.x;\r\n\t\t\t\t\tmousecursor_y += ev->mouse.y;\r\n\t\t\t\t\tmousecursor_x = bound(0, mousecursor_x, vid.pixelwidth-1);\r\n\t\t\t\t\tmousecursor_y = bound(0, mousecursor_y, vid.pixelheight-1);\r\n\t\t\t\t}\r\n\t\t\t\tptr[ev->devid].oldpos[0] = mousecursor_x;\r\n\t\t\t\tptr[ev->devid].oldpos[1] = mousecursor_y;\r\n\r\n\t\t\t\tif (m_forcewheel.value >= 2)\r\n\t\t\t\t\tptr[ev->devid].wheeldelta -= ev->mouse.z;\r\n\t\t\t\telse if (m_forcewheel.value)\r\n\t\t\t\t{\r\n\t\t\t\t\tint mfwt = (int)m_forcewheel_threshold.value;\r\n\r\n\t\t\t\t\tif (ev->mouse.z > mfwt)\r\n\t\t\t\t\t\tptr[ev->devid].wheeldelta -= mfwt;\r\n\t\t\t\t\telse if (ev->mouse.z < -mfwt)\r\n\t\t\t\t\t\tptr[ev->devid].wheeldelta += mfwt;\r\n\t\t\t\t}\r\n\r\n\t\t\t\tif (ev->mouse.x || ev->mouse.y)\r\n\t\t\t\t\tptr[ev->devid].updates++;\r\n\t\t\t}\r\n\t\t\tbreak;\r\n\t\tcase IEV_MOUSEABS:\r\n//Con_Printf(\"IEV_MOUSEABS %i: %f %f\\n\", ev->devid, ev->mouse.x, ev->mouse.y);\r\n\t\t\t/*mouse cursors only really work with one pointer*/\r\n\t\t\tif (!vrui.enabled)\r\n\t\t\tif (ev->devid == touchcursor || (touchcursor < 0 && ev->devid < MAXPOINTERS && (ptr[ev->devid].oldpos[0] != ev->mouse.x || ptr[ev->devid].oldpos[1] != ev->mouse.y)))\r\n\t\t\t{\r\n\t\t\t\tfloat fl;\r\n\t\t\t\tfl = ev->mouse.x * vid.width / vid.pixelwidth;\r\n\t\t\t\tmousecursor_x = bound(0, fl, vid.width-1);\r\n\t\t\t\tfl = ev->mouse.y * vid.height / vid.pixelheight;\r\n\t\t\t\tmousecursor_y = bound(0, fl, vid.height-1);\r\n\t\t\t}\r\n\r\n\t\t\tif (ev->devid < MAXPOINTERS)\r\n\t\t\t{\r\n\t\t\t\tstruct mouse_s *m = &ptr[ev->devid];\r\n\t\t\t\tif (m->type != M_TOUCH)\r\n\t\t\t\t{\r\n\t\t\t\t\t//if its now become an absolute device, clear stuff so we don't get confused.\r\n\t\t\t\t\tm->type = M_TOUCH;\r\n\t\t\t\t\tm->touchtime = 0;\r\n\t\t\t\t\tm->touchkey = 0;\r\n\t\t\t\t\tm->moveddist = 0;\r\n\t\t\t\t\tm->oldpos[0] = ev->mouse.x;\r\n\t\t\t\t\tm->oldpos[1] = ev->mouse.y;\r\n\t\t\t\t}\r\n\r\n\t\t\t\tif (m->touchtime)\r\n\t\t\t\t{\t//only do this when its actually held in some form...\r\n\t\t\t\t\tm->delta[0] += ev->mouse.x - m->oldpos[0];\r\n\t\t\t\t\tm->delta[1] += ev->mouse.y - m->oldpos[1];\r\n\t\t\r\n\t\t\t\t\tm->moveddist += fabs(ev->mouse.x - m->oldpos[0]) + fabs(ev->mouse.y - m->oldpos[1]);\r\n\t\t\t\t}\r\n\r\n\t\t\t\tif (ev->mouse.x != m->oldpos[0] ||\r\n\t\t\t\t\tev->mouse.y != m->oldpos[1])\r\n\t\t\t\t{\r\n\t\t\t\t\tm->updates++;\r\n\t\t\t\t\tm->updated = true;\r\n\t\t\t\t}\r\n\r\n\t\t\t\tm->oldpos[0] = ev->mouse.x;\r\n\t\t\t\tm->oldpos[1] = ev->mouse.y;\r\n\r\n\t\t\t\tif (m->touchtime && m->touchkey == K_TOUCH)\r\n\t\t\t\t{\r\n\t\t\t\t\tif (Sys_DoubleTime()-m->touchtime > 1)\r\n\t\t\t\t\t\tm->touchkey = K_TOUCHLONG;\t//held for long enough...\r\n\t\t\t\t\telse if (m->moveddist >= m_slidethreshold.value)\r\n\t\t\t\t\t\tm->touchkey = K_TOUCHSLIDE;\t//moved far enough to consitute a slide\r\n\t\t\t\t\telse\r\n\t\t\t\t\t\tbreak;\r\n\t\t\t\t\tKey_Event(ev->devid, m->touchkey, 0, true);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\tbreak;\r\n\r\n\t\tcase IEV_ACCELEROMETER:\r\n\t\t\t//down: x= +9.8\r\n\t\t\t//left: y= -9.8\r\n\t\t\t//up:   z= +9.8\r\n#ifdef CSQC_DAT\r\n\t\t\tCSQC_Accelerometer(ev->accel.x, ev->accel.y, ev->accel.z);\r\n#endif\r\n\t\t\tbreak;\r\n\t\tcase IEV_GYROSCOPE:\r\n#ifdef CSQC_DAT\r\n\t\t\tCSQC_Gyroscope(ev->gyro.pitch * 180.0/M_PI, ev->gyro.yaw * 180.0/M_PI, ev->gyro.roll * 180.0/M_PI);\r\n#endif\r\n\t\t\tbreak;\r\n\t\t}\r\n\t\tevents_used++;\r\n\t}\r\n}\r\n\r\nvoid IN_MoveMouse(struct mouse_s *mouse, float *movements, int pnum, float frametime)\r\n{\r\n\tfloat mx, my;\r\n\tdouble mouse_x, mouse_y, mouse_deltadist;\r\n\tint mfwt;\r\n\tqboolean strafe_x, strafe_y;\r\n\tint wpnum;\r\n#ifdef CSQC_DAT\r\n#ifdef MULTITHREAD\r\n\textern qboolean runningindepphys;\r\n#else\r\n\tconst qboolean runningindepphys = false;\r\n#endif\r\n#endif\r\n\r\n\t//small performance boost\r\n\tif (mouse->type == M_INVALID)\r\n\t\treturn;\r\n\r\n\t/*each device will be processed when its player comes to be processed*/\r\n\twpnum = cl.splitclients;\r\n\tif (wpnum < 1)\r\n\t\twpnum = 1;\r\n\tif (cl_forceseat.ival)\r\n\t\twpnum = (cl_forceseat.ival-1) % wpnum;\r\n\telse\r\n\t\twpnum = mouse->qdeviceid % wpnum;\r\n\tif (wpnum != pnum)\r\n\t\treturn;\r\n\r\n\tif (m_forcewheel.value)\r\n\t{\r\n\t\tmfwt = m_forcewheel_threshold.ival;\r\n\t\tif (mfwt)\r\n\t\t{\r\n\t\t\twhile(mouse->wheeldelta <= -mfwt)\r\n\t\t\t{\r\n\t\t\t\tKey_Event (mouse->qdeviceid, K_MWHEELUP, 0, true);\r\n\t\t\t\tKey_Event (mouse->qdeviceid, K_MWHEELUP, 0, false);\r\n\t\t\t\tmouse->wheeldelta += mfwt;\r\n\t\t\t}\r\n\r\n\t\t\twhile(mouse->wheeldelta >= mfwt)\r\n\t\t\t{\r\n\t\t\t\tKey_Event (mouse->qdeviceid, K_MWHEELDOWN, 0, true);\r\n\t\t\t\tKey_Event (mouse->qdeviceid, K_MWHEELDOWN, 0, false);\r\n\t\t\t\tmouse->wheeldelta -= mfwt;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tif (m_forcewheel.value < 2)\r\n\t\t\tmouse->wheeldelta = 0;\r\n\t}\r\n\r\n\tmx = mouse->delta[0];\r\n\tmouse->delta[0]=0;\r\n\tmy = mouse->delta[1];\r\n\tmouse->delta[1]=0;\r\n\r\n\r\n\tif(in_xflip.value) mx *= -1;\r\n\r\n\tmousemove_x += mx;\r\n\tmousemove_y += my;\r\n\r\n\tif (!vrui.enabled && Key_MouseShouldBeFree())\r\n\t\tmx=my=0;\r\n\r\n\tif (mouse->type == M_TOUCH)\r\n\t{\r\n\t\tqboolean strafing = false;\r\n\r\n\t\tif (mouse->touchtime && mouse->touchkey==K_TOUCH)\r\n\t\t{\t//convert to tap/longpress if it wasn't already a gesture. don't ever convert mouse.\r\n\t\t\tif (Sys_DoubleTime()-mouse->touchtime > m_longpressthreshold.value)\r\n\t\t\t{\t//might as well trigger this here...\r\n\t\t\t\tmouse->touchkey = K_TOUCHLONG;\r\n\t\t\t\tKey_Event(mouse-ptr, mouse->touchkey, 0, true);\r\n\t\t\t}\r\n\t\t}\r\n\r\n\r\n\t\tswitch(m_touchstrafe.ival)\r\n\t\t{\r\n\t\tcase 2:\r\n\t\t\tstrafing = mouse->heldpos[0] < vid.pixelwidth/2;\r\n\t\t\tbreak;\r\n\t\tdefault:\r\n\t\t\tstrafing = mouse->heldpos[0] > vid.pixelwidth/2;\r\n\t\t\tbreak;\r\n\t\tcase 0:\r\n\t\t\tstrafing = false;\r\n\t\t\tbreak;\r\n\t\t}\r\n\t\tif (strafing && movements != NULL && !Key_Dest_Has(~kdm_game))\r\n\t\t{\r\n\t\t\t//if they're strafing, calculate the speed to move at based upon their displacement\r\n\t\t\tif (mouse->touchtime)\r\n\t\t\t{\r\n\t\t\t\tif (m_touchstrafe.ival == 2)\t//left side\r\n\t\t\t\t\tmx = mouse->oldpos[0] - (vid.pixelwidth*1)/4.0;\r\n\t\t\t\telse\t//right side\r\n\t\t\t\t\tmx = mouse->oldpos[0] - (vid.pixelwidth*3)/4.0;\r\n\t\t\t\tmy = mouse->oldpos[1] - (vid.pixelheight*3)/4.0;\r\n\r\n\t\t\t\t//mx = (mouse->oldpos[0] - mouse->heldpos[0])*0.1;\r\n\t\t\t\t//my = (mouse->oldpos[1] - mouse->heldpos[1])*0.1;\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tmx = 0;\r\n\t\t\t\tmy = 0;\r\n\t\t\t}\r\n\r\n\t\t\tif (m_touchmajoraxis.ival)\r\n\t\t\t{\r\n\t\t\t\t//major axis only\r\n\t\t\t\tif (fabs(mx) > fabs(my))\r\n\t\t\t\t\tmy = 0;\r\n\t\t\t\telse\r\n\t\t\t\t\tmx = 0;\r\n\t\t\t}\r\n\r\n\t\t\tstrafe_x = true;\r\n\t\t\tstrafe_y = true;\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tstrafe_x = false;\r\n\t\t\tstrafe_y = false;\r\n\r\n\t\t\t//boost sensitivity so that the default works okay.\r\n\t\t\tmx *= 1.75;\r\n\t\t\tmy *= 1.75;\r\n\r\n#ifdef QUAKESTATS\r\n\t\t\tif (IN_WeaponWheelAccumulate(pnum, mx, my, 0))\r\n\t\t\t\tmx = my = 0;\r\n#endif\r\n\t\t}\r\n\t}\r\n\telse\r\n\t{\r\n\t\tstrafe_x = (in_strafe.state[pnum] & 1) || (lookstrafe.value && (in_mlook.state[pnum] & 1) );\r\n\t\tstrafe_y = !((in_mlook.state[pnum] & 1) && !(in_strafe.state[pnum] & 1));\r\n\t}\r\n\r\n\tif (mouse->type == M_TOUCH || Key_MouseShouldBeFree())\r\n\t{\r\n\t\tif (mouse->updated)\r\n\t\t{\t//many mods assume only a single mouse device.\r\n\t\t\t//when we have multiple active abs devices, we need to avoid sending all of them, because that just confuses everyone. such mods will see only devices that are actually moving, so uni-cursor mods will see only the one that moved most recently.\r\n\t\t\tmouse->updated = false;\r\n\t\t\tif (!runningindepphys)\r\n\t\t\t{\r\n\t\t\t\tif ((promptmenu && promptmenu->mousemove && promptmenu->mousemove(topmenu, true, mouse->qdeviceid, mouse->oldpos[0], mouse->oldpos[1])) ||\r\n\t\t\t\t\t(topmenu && topmenu->mousemove && topmenu->mousemove(topmenu, true, mouse->qdeviceid, mouse->oldpos[0], mouse->oldpos[1])))\r\n\t\t\t\t{\r\n\t\t\t\t\tmx = 0;\r\n\t\t\t\t\tmy = 0;\r\n\t\t\t\t}\r\n#ifdef CSQC_DAT\r\n\t\t\t\tif (!runningindepphys && CSQC_MousePosition(mouse->oldpos[0], mouse->oldpos[1], mouse->qdeviceid))\r\n\t\t\t\t{\r\n\t\t\t\t\tmx = 0;\r\n\t\t\t\t\tmy = 0;\r\n\t\t\t\t}\r\n#endif\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\telse\r\n\t{\r\n\t\tif (Key_Dest_Has(kdm_menu))\r\n\t\tif (mx || my)\r\n\t\tif (!runningindepphys && topmenu && topmenu->mousemove && topmenu->mousemove(topmenu, false, mouse->qdeviceid, mx, my))\r\n\t\t{\r\n\t\t\tmx = 0;\r\n\t\t\tmy = 0;\r\n\t\t}\r\n\r\n#ifdef CSQC_DAT\r\n\t\tif (mx || my)\r\n\t\tif (!runningindepphys && CSQC_MouseMove(mx, my, mouse->qdeviceid))\r\n\t\t{\r\n\t\t\tmx = 0;\r\n\t\t\tmy = 0;\r\n\t\t}\r\n#endif\r\n\r\n\t\t//if game is not focused, kill any mouse look\r\n\t\tif (\r\n#ifdef QUAKESTATS\r\n\t\t\tIN_WeaponWheelAccumulate(pnum, mx, my, 0) ||\r\n#endif\r\n\t\t\tKey_Dest_Has(~kdm_game))\r\n\t\t{\r\n\t\t\tmx = 0;\r\n\t\t\tmy = 0;\r\n\t\t}\r\n\t}\r\n\r\n\tif (m_filter.value)\r\n\t{\r\n\t\tdouble fraction = bound(0, m_filter.value, 2) * 0.5;\r\n\t\tmouse_x = (mx*(1-fraction) + mouse->old_delta[0]*fraction);\r\n\t\tmouse_y = (my*(1-fraction) + mouse->old_delta[1]*fraction);\r\n\t}\r\n\telse\r\n\t{\r\n\t\tmouse_x = mx;\r\n\t\tmouse_y = my;\r\n\t}\r\n\r\n\tmouse->old_delta[0] = mx;\r\n\tmouse->old_delta[1] = my;\r\n\r\n\tif (m_accel.value)\r\n\t{\r\n\t\tif (m_accel_style.ival && frametime)\r\n\t\t{\r\n\t\t\tfloat accelsens = sensitivity.value*in_sensitivityscale;\r\n\t\t\tfloat mousespeed = (sqrt (mx * mx + my * my)) / (1000.0f * (float) frametime);\r\n\t\t\tmousespeed -= m_accel_offset.value;\r\n\t\t\tif (mousespeed > 0)\r\n\t\t\t{\r\n\t\t\t\tmousespeed *= m_accel.value;\r\n\t\t\t\tif (m_accel_power.value > 1)\r\n\t\t\t\t\taccelsens += exp((m_accel_power.value - 1) * log(mousespeed));\r\n\t\t\t\telse\r\n\t\t\t\t\taccelsens = 1;\r\n\t\t\t}\r\n\t\t\tif (m_accel_senscap.value > 0 && accelsens > m_accel_senscap.value)\r\n\t\t\t\taccelsens = m_accel_senscap.value;\r\n\t\t\tmouse_x *= accelsens;\r\n\t\t\tmouse_y *= accelsens;\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tmouse_deltadist = sqrt(mx*mx + my*my);\r\n\t\t\tmouse_x *= (mouse_deltadist*m_accel.value + sensitivity.value*in_sensitivityscale);\r\n\t\t\tmouse_y *= (mouse_deltadist*m_accel.value + sensitivity.value*in_sensitivityscale);\r\n\t\t}\r\n\t}\r\n\telse\r\n\t{\r\n\t\tmouse_x *= sensitivity.value*in_sensitivityscale;\r\n\t\tmouse_y *= sensitivity.value*in_sensitivityscale;\r\n\t}\r\n\r\n/*\r\n#ifdef QUAKESTATS\r\n\tif (cl.playerview[pnum].statsf[STAT_VIEWZOOM])\r\n\t{\r\n\t\tmouse_x *= cl.playerview[pnum].statsf[STAT_VIEWZOOM]/STAT_VIEWZOOM_SCALE;\r\n\t\tmouse_y *= cl.playerview[pnum].statsf[STAT_VIEWZOOM]/STAT_VIEWZOOM_SCALE;\r\n\t}\r\n#endif\r\n*/\r\n\r\n\tif (!movements || cl.disablemouse)\r\n\t{\r\n\t\treturn;\r\n\t}\r\n\r\n\tif (r_xflip.ival)\r\n\t\tmouse_x *= -1;\r\n\r\n// add mouse X/Y movement to cmd\r\n\tif (strafe_x)\r\n\t\tmovements[1] += m_side.value * mouse_x;\r\n\telse\r\n\t{\r\n//\t\tif ((int)((cl.viewangles[pnum][PITCH]+89.99)/180) & 1)\r\n//\t\t\tmouse_x *= -1;\r\n\t\tcl.playerview[pnum].viewanglechange[YAW] -= m_yaw.value * mouse_x;\r\n\t}\r\n\r\n\tif (in_mlook.state[pnum] & 1)\r\n\t\tV_StopPitchDrift (&cl.playerview[pnum]);\r\n\r\n\tif (!strafe_y)\r\n\t{\r\n\t\tcl.playerview[pnum].viewanglechange[PITCH] += m_pitch.value * mouse_y;\r\n\t}\r\n\telse\r\n\t{\r\n\t\tif ((in_strafe.state[pnum] & 1) && noclip_anglehack)\r\n\t\t\tmovements[2] -= m_forward.value * mouse_y;\r\n\t\telse\r\n\t\t\tmovements[0] -= m_forward.value * mouse_y;\r\n\t}\r\n}\r\n\r\n//rescales threshold-1 down 0-1\r\nstatic float joydeadzone(float mag, float deadzone)\r\n{\r\n\tif (joy_radialdeadzone.ival == 2)\t//hacky overload to disable dead zones where the system provides it instead.\r\n\t\tdeadzone = 0;\r\n\r\n\tif (mag > 1)\t//erg?\r\n\t\tmag = 1;\r\n\tif (mag > deadzone)\r\n\t{\r\n\t\tmag -= deadzone;\r\n\t\tmag = mag / (1.f-deadzone);\r\n\r\n\t\tmag = pow(mag, joy_exponent.value);\r\n\t}\r\n\telse\r\n\t\tmag = 0;\r\n\treturn mag;\r\n}\r\n\r\nvoid IN_MoveJoystick(struct joy_s *joy, float *movements, int pnum, float frametime)\r\n{\r\n\tfloat mag;\r\n\tvec3_t jlook, jstrafe;\r\n\r\n\tint wpnum, i;\r\n\tfor (i = 0; i < MAXJOYAXIS; i++)\r\n\t\tif (joy->axis[i])\r\n\t\t\tbreak;\r\n\tif (i == MAXJOYAXIS)\r\n\t\treturn;\r\n\r\n\t/*each device will be processed when its player comes to be processed*/\r\n\twpnum = cl.splitclients;\r\n\tif (wpnum < 1)\r\n\t\twpnum = 1;\r\n\tif (cl_forceseat.ival)\r\n\t\twpnum = (cl_forceseat.ival-1) % wpnum;\r\n\telse\r\n\t\twpnum = joy->qdeviceid % wpnum;\r\n\tif (wpnum != pnum)\r\n\t\treturn;\r\n\r\n\tmemset(jstrafe, 0, sizeof(jstrafe));\r\n\tmemset(jlook, 0, sizeof(jlook));\r\n\r\n\tfor (i = 0; i < 6; i++)\r\n\t{\r\n\t\tint ax = joy_advaxis[i].ival;\r\n\t\tswitch(ax)\r\n\t\t{\r\n\t\tdefault:\r\n\t\tcase 0:\t//dead axis\r\n\t\t\tbreak;\r\n\t\tcase 1:\r\n\t\tcase 3:\r\n\t\tcase 5:\r\n\t\t\tjstrafe[(ax-1)/2] += joy->axis[i] * joy_advaxisscale[i].value;\r\n\t\t\tbreak;\r\n\t\tcase -1:\r\n\t\tcase -3:\r\n\t\tcase -5:\r\n\t\t\tjstrafe[(-ax-1)/2] -= joy->axis[i] * joy_advaxisscale[i].value;\r\n\t\t\tbreak;\r\n\r\n\t\tcase 2:\r\n\t\tcase 4:\r\n\t\tcase 6:\r\n\t\t\tjlook[(ax-2)/2] += joy->axis[i] * joy_advaxisscale[i].value;\r\n\t\t\tbreak;\r\n\t\tcase -2:\r\n\t\tcase -4:\r\n\t\tcase -6:\r\n\t\t\tjlook[(-ax-2)/2] -= joy->axis[i] * joy_advaxisscale[i].value;\r\n\t\t\tbreak;\r\n\t\t}\r\n\t}\r\n\r\n\tif (joy_radialdeadzone.ival)\r\n\t{\r\n\t\t//uses a radial deadzone for x+y axis, and separate out the z axis, just because most controllers are 2d affairs with any 3rd axis being a separate knob.\r\n\t\t//deadzone values are stolen from microsoft's xinput documentation. they seem quite large to me - I guess that means that xbox controllers are just dodgy imprecise crap with excessive amounts of friction and finger grease.\r\n\t\tfloat basemag = sqrt(jlook[0]*jlook[0] + jlook[1]*jlook[1]);\r\n\t\tif (basemag)\r\n\t\t{\r\n\t\t\tmag = joydeadzone(basemag, sqrt(joy_anglethreshold[0].value*joy_anglethreshold[0].value + joy_anglethreshold[1].value*joy_anglethreshold[1].value));\r\n\t\t\tjlook[0] = (jlook[0]/basemag) * mag;\r\n\t\t\tjlook[1] = (jlook[1]/basemag) * mag;\r\n\t\t}\r\n\t\telse\r\n\t\t\tjlook[0] = jlook[1] = 0;\r\n\t\tmag = joydeadzone(fabs(jlook[2]), joy_anglethreshold[2].value);\r\n\t\tjlook[2] = mag;\r\n\r\n\t\tbasemag = sqrt(jstrafe[0]*jstrafe[0] + jstrafe[1]*jstrafe[1]);\r\n\t\tif (basemag)\r\n\t\t{\r\n\t\t\tmag = joydeadzone(basemag, sqrt(joy_movethreshold[0].value*joy_movethreshold[0].value + joy_movethreshold[1].value*joy_movethreshold[1].value));\r\n\t\t\tjstrafe[0] = (jstrafe[0]/basemag) * mag;\r\n\t\t\tjstrafe[1] = (jstrafe[1]/basemag) * mag;\r\n\t\t}\r\n\t\telse\r\n\t\t\tjstrafe[0] = jstrafe[1] = 0;\r\n\t\tmag = joydeadzone(fabs(jstrafe[2]), joy_movethreshold[2].value);\r\n\t\tjstrafe[2] = mag;\r\n\t}\r\n\telse\r\n\t{\r\n\t\tfor (i = 0; i < 3; i++)\r\n\t\t{\r\n\t\t\tmag = joydeadzone(fabs(jlook[i]), joy_anglethreshold[i].value);\r\n\t\t\tjlook[i] = ((jlook[i]<0)?-1:1)*mag;\r\n\r\n\t\t\tmag = joydeadzone(fabs(jstrafe[i]), joy_movethreshold[i].value);\r\n\t\t\tjstrafe[i] = ((jstrafe[i]<0)?-1:1)*mag;\r\n\t\t}\r\n\t}\r\n\r\n#ifdef QUAKESTATS\r\n\tif (IN_WeaponWheelAccumulate(joy->qdeviceid, jstrafe[1]*50, -jstrafe[0]*50, 20))\r\n\t\tjstrafe[0] = jstrafe[1] = 0;\r\n\tif (IN_WeaponWheelAccumulate(joy->qdeviceid, jlook[1]*50, jlook[0]*50, 20))\r\n\t\tjlook[0] = jlook[1] = 0;\r\n#endif\r\n\r\n\tif (Key_Dest_Has(~kdm_game))\r\n\t{\r\n\t\tVectorClear(jlook);\r\n\t\tVectorClear(jstrafe);\r\n\t}\r\n\tif (r_xflip.ival)\r\n\t\tjlook[0] *= -1, jstrafe[0] *= -1;\r\n\r\n\tif (in_speed.state[pnum] & 1)\r\n\t{\r\n\t\tVectorScale(jlook, 360*cl_movespeedkey.value, jlook);\r\n\t\tVectorScale(jstrafe, 360*cl_movespeedkey.value, jstrafe);\r\n\t}\r\n\tVectorScale(jlook, 360*frametime*in_sensitivityscale, jlook);\r\n\r\n\tif (!movements)\t//if this is null, gamecode should still get inputs, just no camera looking or anything.\r\n\t\treturn;\r\n\r\n\t//angle changes\r\n\tcl.playerview[pnum].viewanglechange[PITCH] += joy_anglesens[0].value * jlook[0] * in_sensitivityscale;\r\n\tcl.playerview[pnum].viewanglechange[YAW] -= joy_anglesens[1].value * jlook[1] * in_sensitivityscale;\r\n\tcl.playerview[pnum].viewanglechange[ROLL] += joy_anglesens[2].value * jlook[2] * in_sensitivityscale;\r\n\r\n\tif (in_mlook.state[pnum] & 1)\r\n\t\tV_StopPitchDrift (&cl.playerview[pnum]);\r\n\r\n\t//movement\r\n\tmag = 1;\r\n\tif ((in_speed.state[pnum] & 1) ^ cl_run.ival)\r\n\t\tmag *= cl_movespeedkey.value;\r\n\tmovements[0] += joy_movesens[0].value * mag*cl_forwardspeed.value * jstrafe[0];\r\n\tmovements[1] += joy_movesens[1].value * mag*cl_sidespeed.value * jstrafe[1];\r\n\tmovements[2] += joy_movesens[2].value * mag*cl_upspeed.value * jstrafe[2];\r\n}\r\n\r\nvoid IN_Move (float *nudgemovements, float *absmovements, int pnum, float frametime)\r\n{\r\n\tint i;\r\n\tfor (i = 0; i < MAXPOINTERS; i++)\r\n\t\tIN_MoveMouse(&ptr[i], nudgemovements, pnum, frametime);\r\n\r\n\tfor (i = 0; i < MAXJOYSTICKS; i++)\r\n\t\tIN_MoveJoystick(&joy[i], absmovements, pnum, frametime);\r\n}\r\n\r\nvoid IN_JoystickAxisEvent(unsigned int devid, int axis, float value)\r\n{\r\n\tstruct eventlist_s *ev = in_newevent();\r\n\tif (!ev)\t\r\n\t\treturn;\r\n\tev->type = IEV_JOYAXIS;\r\n\tev->devid = devid;\r\n\tev->joy.axis = axis;\r\n\tev->joy.value = value;\r\n\tin_finishevent();\r\n}\r\n\r\nvoid IN_KeyEvent(unsigned int devid, int down, int keycode, int unicode)\r\n{\r\n\tstruct eventlist_s *ev = in_newevent();\r\n\tif (!ev)\r\n\t\treturn;\r\n\tev->type = down?IEV_KEYDOWN:IEV_KEYRELEASE;\r\n\tev->devid = devid;\r\n\tev->keyboard.scancode = keycode;\r\n\tev->keyboard.unicode = unicode;\r\n\tin_finishevent();\r\n}\r\n\r\n/*\r\ndevid is the mouse device id. generally idependant from keyboards.\r\nfor multitouch, devid might be the touch identifier, which will persist until released.\r\nx is horizontal, y is vertical.\r\nz is height... generally its used as a mousewheel instead, but there are some '3d' mice out there, so its provided in this api.\r\n*/\r\nvoid IN_MouseMove(unsigned int devid, int abs, float x, float y, float z, float size)\r\n{\r\n\tstruct eventlist_s *ev;\r\n\tif (!abs && !x && !y && !z)\r\n\t\treturn;\t//ignore non-movements\r\n\tev = in_newevent();\r\n\tif (!ev)\r\n\t\treturn;\r\n\tev->devid = devid;\r\n\tev->type = abs?IEV_MOUSEABS:IEV_MOUSEDELTA;\r\n\tev->mouse.x = x;\r\n\tev->mouse.y = y;\r\n\tev->mouse.z = z;\r\n\tev->mouse.tsize = size;\r\n\tin_finishevent();\r\n}\r\n\r\nvoid IN_Accelerometer(unsigned int devid, float x, float y, float z)\r\n{\r\n\tstruct eventlist_s *ev = in_newevent();\r\n\tif (!ev)\r\n\t\treturn;\r\n\tev->devid = devid;\r\n\tev->type = IEV_ACCELEROMETER;\r\n\tev->accel.x = x;\r\n\tev->accel.y = y;\r\n\tev->accel.z = z;\r\n\tin_finishevent();\r\n}\r\nvoid IN_Gyroscope(unsigned int devid, float pitch, float yaw, float roll)\r\n{\r\n\tstruct eventlist_s *ev = in_newevent();\r\n\tif (!ev)\r\n\t\treturn;\r\n\tev->devid = devid;\r\n\tev->type = IEV_GYROSCOPE;\r\n\tev->gyro.pitch = pitch;\r\n\tev->gyro.yaw = yaw;\r\n\tev->gyro.roll = roll;\r\n\tin_finishevent();\r\n}\r\n\r\n\r\nextern usercmd_t cl_pendingcmd[MAX_SPLITS];\r\nqboolean IN_SetHandPosition(const char *devname, vec3_t org, vec3_t ang, vec3_t vel, vec3_t avel)\r\n{\r\n\tint dtype;\r\n\tint seat;\r\n\tstruct vrdevinfo_s *dev;\r\n\tif (!Q_strncasecmp(devname, \"left\", 4))\r\n\t{\r\n\t\tseat = atoi(devname+4);\r\n\t\tdtype = VRDEV_LEFT;\r\n\t}\r\n\telse if (!Q_strncasecmp(devname, \"right\", 5))\r\n\t{\r\n\t\tseat = atoi(devname+5);\r\n\t\tdtype = VRDEV_RIGHT;\r\n\t}\r\n\telse if (!Q_strncasecmp(devname, \"head\", 4))\r\n\t{\r\n\t\tseat = atoi(devname+4);\r\n\t\tdtype = VRDEV_HEAD;\r\n\t}\r\n\telse\r\n\t\treturn false;\t//no idea what you're talking about.\r\n\tif (seat < 0 || seat >= MAX_SPLITS)\r\n\t\treturn false;\t//duuuude!\r\n\tdev = &cl.playerview[seat].vrdev[dtype];\r\n\r\n\tif (org)\r\n\t\tVectorCopy(org, dev->origin);\r\n\telse\r\n\t\tVectorClear(dev->origin);\r\n\tif (ang)\r\n\t{\r\n\t\tdev->angles[0] = ANGLE2SHORT(ang[0]),\r\n\t\tdev->angles[1] = ANGLE2SHORT(ang[1]),\r\n\t\tdev->angles[2] = ANGLE2SHORT(ang[2]);\r\n\t}\r\n\telse\r\n\t\tVectorClear(dev->angles);\r\n\tif (vel)\r\n\t\tVectorCopy(vel, dev->velocity);\r\n\telse\r\n\t\tVectorClear(dev->velocity);\r\n\tif (avel)\r\n\t\tdev->avelocity[0] = ANGLE2SHORT(avel[0]),\r\n\t\tdev->avelocity[1] = ANGLE2SHORT(avel[1]),\r\n\t\tdev->avelocity[2] = ANGLE2SHORT(avel[2]);\r\n\telse\r\n\t\tVectorClear(dev->avelocity);\r\n\r\n\tdev->status =\r\n\t\t\t(org ?VRSTATUS_ORG:0)|\r\n\t\t\t(ang ?VRSTATUS_ANG:0)|\r\n\t\t\t(vel ?VRSTATUS_VEL:0)|\r\n\t\t\t(avel?VRSTATUS_AVEL:0);\r\n\treturn true;\r\n}\r\n"
  },
  {
    "path": "engine/client/in_morphos.c",
    "content": "/*\nCopyright (C) 2006-2007 Mark Olsen\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  \n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n*/\n\n#include <exec/exec.h>\n#include <intuition/intuition.h>\n#include <intuition/extensions.h>\n#include <intuition/intuitionbase.h>\n#include <devices/input.h>\n\n#include <proto/exec.h>\n#include <proto/intuition.h>\n\n#include <clib/alib_protos.h>\n\n#include \"quakedef.h\"\n#include \"input.h\"\n\n#include \"in_morphos.h\"\n\nstruct InputEvent imsgs[MAXIMSGS];\nextern struct IntuitionBase *IntuitionBase;\nextern struct Window *window;\nextern struct Screen *screen;\n\nint imsglow = 0;\nint imsghigh = 0;\n\nextern qboolean mouse_active;\n\nstatic struct Interrupt InputHandler;\n\nstatic struct Interrupt InputHandler;\nstatic struct MsgPort *inputport = 0;\nstatic struct IOStdReq *inputreq = 0;\nstatic BYTE inputret = -1;\n\n#define DEBUGRING(x)\n\nvoid INS_Shutdown(void)\n{\n\tif (inputret == 0)\n\t{\n\t\tinputreq->io_Data = (void *)&InputHandler;\n\t\tinputreq->io_Command = IND_REMHANDLER;\n\t\tDoIO((struct IORequest *)inputreq);\n\n\t\tCloseDevice((struct IORequest *)inputreq);\n\n\t\tinputret = -1;\n\t}\n\n\tif (inputreq)\n\t{\n\t\tDeleteStdIO(inputreq);\n\n\t\tinputreq = 0;\n\t}\n\n\tif (inputport)\n\t{\n\t\tDeletePort(inputport);\n\n\t\tinputport = 0;\n\t}\n}\n\nvoid INS_ReInit()\n{\n/*\tCvar_Register (&m_filter, \"input controls\");*/\n\n\tif (inputport)\n\t\treturn;\n\n\tinputport = CreatePort(0, 0);\n\tif (inputport == 0)\n\t{\n\t\tIN_Shutdown();\n\t\tSys_Error(\"Unable to create message port\");\n\t}\n\n\tinputreq = CreateStdIO(inputport);\n\tif (inputreq == 0)\n\t{\n\t\tIN_Shutdown();\n\t\tSys_Error(\"Unable to create IO request\");\n\t}\n\n\tinputret = OpenDevice(\"input.device\", 0, (struct IORequest *)inputreq, 0);\n\tif (inputret != 0)\n\t{\n\t\tIN_Shutdown();\n\t\tSys_Error(\"Unable to open input.device\");\n\t}\n\n\tInputHandler.is_Node.ln_Type = NT_INTERRUPT;\n\tInputHandler.is_Node.ln_Pri = 100;\n\tInputHandler.is_Node.ln_Name = \"FTEQW input handler\";\n\tInputHandler.is_Data = 0;\n\tInputHandler.is_Code = (void(*)())&myinputhandler;\n\tinputreq->io_Data = (void *)&InputHandler;\n\tinputreq->io_Command = IND_ADDHANDLER;\n\tDoIO((struct IORequest *)inputreq);\n}\n\nvoid INS_Init(void)\n{\n\tINS_ReInit();\n}\n\n//IN_KeyEvent is threadsafe (for one other thread, anyway)\nvoid INS_ProcessInputMessage(struct InputEvent *msg, qboolean consumemotion)\n{\n\tint key;\n\tqboolean down;\n\tint code;\n\n\tif ((window->Flags & WFLG_WINDOWACTIVE))\n\t{\n\t\tif (msg->ie_Class == IECLASS_NEWMOUSE)\n\t\t{\n\t\t\tkey = 0;\n\n\t\t\tif (msg->ie_Code == NM_WHEEL_UP)\n\t\t\t\tkey = K_MWHEELUP;\n\t\t\telse if (msg->ie_Code == NM_WHEEL_DOWN)\n\t\t\t\tkey = K_MWHEELDOWN;\n\n\t\t\tif (msg->ie_Code == NM_BUTTON_FOURTH)\n\t\t\t{\n\t\t\t\tIN_KeyEvent(0, true, K_MOUSE4, 0);\n\t\t\t}\n\t\t\telse if (msg->ie_Code == (NM_BUTTON_FOURTH|IECODE_UP_PREFIX))\n\t\t\t{\n\t\t\t\tIN_KeyEvent(0, false, K_MOUSE4, 0);\n\t\t\t}\n\n\t\t\tif (key)\n\t\t\t{\n\t\t\t\tIN_KeyEvent(0, true, key, 0);\n\t\t\t\tIN_KeyEvent(0, false, key, 0);\n\t\t\t}\n\n\t\t}\n\t\telse if (msg->ie_Class == IECLASS_RAWKEY)\n\t\t{\n\t\t\tdown = !(msg->ie_Code&IECODE_UP_PREFIX);\n\t\t\tcode = msg->ie_Code & ~IECODE_UP_PREFIX;\n\n\t\t\tkey = 0;\n\t\t\tif (code <= 255)\n\t\t\t\tkey = keyconv[code];\n\n\t\t\tif (key)\n\t\t\t\tIN_KeyEvent(0, down, key, key);\n\t\t\telse\n\t\t\t{\n//\t\t\t\tif (developer.value)\n//\t\t\t\t\tprintf(\"Unknown key %d\\n\", msg->ie_Code);\n\t\t\t}\n\t\t}\n\n\t\telse if (msg->ie_Class == IECLASS_RAWMOUSE)\n\t\t{\n\t\t\tif (msg->ie_Code == IECODE_LBUTTON)\n\t\t\t\tIN_KeyEvent(0, true, K_MOUSE1, 0);\n\t\t\telse if (msg->ie_Code == (IECODE_LBUTTON|IECODE_UP_PREFIX))\n\t\t\t\tIN_KeyEvent(0, false, K_MOUSE1, 0);\n\t\t\telse if (msg->ie_Code == IECODE_RBUTTON)\n\t\t\t\tIN_KeyEvent(0, true, K_MOUSE2, 0);\n\t\t\telse if (msg->ie_Code == (IECODE_RBUTTON|IECODE_UP_PREFIX))\n\t\t\t\tIN_KeyEvent(0, false, K_MOUSE2, 0);\n\t\t\telse if (msg->ie_Code == IECODE_MBUTTON)\n\t\t\t\tIN_KeyEvent(0, true, K_MOUSE3, 0);\n\t\t\telse if (msg->ie_Code == (IECODE_MBUTTON|IECODE_UP_PREFIX))\n\t\t\t\tIN_KeyEvent(0, false, K_MOUSE3, 0);\n\n\t\t\tif (in_windowed_mouse.ival)\n\t\t\t{\n\t\t\t\tif (consumemotion)\n\t\t\t\t{\n\t\t\t\t\tIN_MouseMove(0, 0, msg->ie_position.ie_xy.ie_x, msg->ie_position.ie_xy.ie_y, 0, 0);\n\n#if 0\n\t\t\t\t\tmsg->ie_Class = IECLASS_NULL;\n#else\n\t\t\t\t\tmsg->ie_position.ie_xy.ie_x = 0;\n\t\t\t\t\tmsg->ie_position.ie_xy.ie_y = 0;\n#endif\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nvoid INS_Commands(void)\n{\n}\nvoid INS_EnumerateDevices(void *ctx, void(*callback)(void *ctx, const char *type, const char *devicename, unsigned int *qdevid))\n{\n}\nvoid INS_Move (void)\n{\n}\n\nchar keyconv[] =\n{\n\t'`', /* 0 */\n\t'1',\n\t'2',\n\t'3',\n\t'4',\n\t'5',\n\t'6',\n\t'7',\n\t'8',\n\t'9',\n\t'0', /* 10 */\n\t'-',\n\t'=',\n\t0,\n\t0,\n\tK_INS,\n\t'q',\n\t'w',\n\t'e',\n\t'r',\n\t't', /* 20 */\n\t'y',\n\t'u',\n\t'i',\n\t'o',\n\t'p',\n\t'[',\n\t']',\n\t0,\n\tK_KP_END,\n\tK_KP_DOWNARROW, /* 30 */\n\tK_KP_PGDN,\n\t'a',\n\t's',\n\t'd',\n\t'f',\n\t'g',\n\t'h',\n\t'j',\n\t'k',\n\t'l', /* 40 */\n\t';',\n\t'\\'',\n\t'\\\\',\n\t0,\n\tK_KP_LEFTARROW,\n\tK_KP_5,\n\tK_KP_RIGHTARROW,\n\t'<',\n\t'z',\n\t'x', /* 50 */\n\t'c',\n\t'v',\n\t'b',\n\t'n',\n\t'm',\n\t',',\n\t'.',\n\t'/',\n\t0,\n\tK_KP_DEL, /* 60 */\n\tK_KP_HOME,\n\tK_KP_UPARROW,\n\tK_KP_PGUP,\n\t' ',\n\tK_BACKSPACE,\n\tK_TAB,\n\tK_KP_ENTER,\n\tK_ENTER,\n\tK_ESCAPE,\n\tK_DEL, /* 70 */\n\tK_INS,\n\tK_PGUP,\n\tK_PGDN,\n\tK_KP_MINUS,\n\tK_F11,\n\tK_UPARROW,\n\tK_DOWNARROW,\n\tK_RIGHTARROW,\n\tK_LEFTARROW,\n\tK_F1, /* 80 */\n\tK_F2,\n\tK_F3,\n\tK_F4,\n\tK_F5,\n\tK_F6,\n\tK_F7,\n\tK_F8,\n\tK_F9,\n\tK_F10,\n\t0, /* 90 */\n\t0,\n\tK_KP_SLASH,\n\t0,\n\tK_KP_PLUS,\n\t0,\n\tK_SHIFT,\n\tK_SHIFT,\n\tK_CAPSLOCK,\n\tK_CTRL,\n\tK_ALT, /* 100 */\n\tK_ALT,\n\t0,\n\t0,\n\t0,\n\t0,\n\t0,\n\t0,\n\t0,\n\t0,\n\tK_PAUSE, /* 110 */\n\tK_F12,\n\tK_HOME,\n\tK_END,\n\t0,\n\t0,\n\t0,\n\t0,\n\t0,\n\t0,\n\t0, /* 120 */\n\t0,\n\t0,\n\t0,\n\t0,\n\t0,\n\t0,\n\t0,\n\t0,\n\t0,\n\t0, /* 130 */\n\t0,\n\t0,\n\t0,\n\t0,\n\t0,\n\t0,\n\t0,\n\t0,\n\t0,\n\t0, /* 140 */\n\t0,\n\t0,\n\t0,\n\t0,\n\t0,\n\t0,\n\t0,\n\t0,\n\t0,\n\t0, /* 150 */\n\t0,\n\t0,\n\t0,\n\t0,\n\t0,\n\t0,\n\t0,\n\t0,\n\t0,\n\t0, /* 160 */\n\t0,\n\t0,\n\t0,\n\t0,\n\t0,\n\t0,\n\t0,\n\t0,\n\t0,\n\t0, /* 170 */\n\t0,\n\t0,\n\t0,\n\t0,\n\t0,\n\t0,\n\t0,\n\t0,\n\t0,\n\t0, /* 180 */\n\t0,\n\t0,\n\t0,\n\t0,\n\t0,\n\t0,\n\t0,\n\t0,\n\t0,\n\t0, /* 190 */\n\t0,\n\t0,\n\t0,\n\t0,\n\t0,\n\t0,\n\t0,\n\t0,\n\t0,\n\t0, /* 200 */\n\t0,\n\t0,\n\t0,\n\t0,\n\t0,\n\t0,\n\t0,\n\t0,\n\t0,\n\t0, /* 210 */\n\t0,\n\t0,\n\t0,\n\t0,\n\t0,\n\t0,\n\t0,\n\t0,\n\t0,\n\t0, /* 220 */\n\t0,\n\t0,\n\t0,\n\t0,\n\t0,\n\t0,\n\t0,\n\t0,\n\t0,\n\t0, /* 230 */\n\t0,\n\t0,\n\t0,\n\t0,\n\t0,\n\t0,\n\t0,\n\t0,\n\t0,\n\t0, /* 240 */\n\t0,\n\t0,\n\t0,\n\t0,\n\t0,\n\t0,\n\t0,\n\t0,\n\t0,\n\t0, /* 250 */\n\t0,\n\t0,\n\t0,\n\t0,\n\t0,\n\t0,\n\t0,\n\t0,\n\t0\n};\n\nstruct InputEvent *myinputhandler_real(void);\n\nstruct EmulLibEntry myinputhandler =\n{\n\tTRAP_LIB, 0, (void(*)(void))myinputhandler_real\n};\n\nstruct InputEvent *myinputhandler_real()\n{\n\tstruct InputEvent *moo = (struct InputEvent *)REG_A0;\n\n\tstruct InputEvent *coin;\n\n\tint screeninfront;\n\n\tcoin = moo;\n\n\tif (screen)\n\t{\n#if 0\n\t\tif (IntuitionBase->LibNode.lib_Version > 50 || (IntuitionBase == 50 && IntuitionBase->LibNode.lib_Revision >= 56))\n\t\t\tGetAttr(screen, SA_Displayed, &screeninfront);\n\t\telse\n#endif\n\t\t\tscreeninfront = screen==IntuitionBase->FirstScreen;\n\t}\n\telse\n\t\tscreeninfront = 1;\n\n\tdo\n\t{\n\t\tif (coin->ie_Class == IECLASS_RAWMOUSE || coin->ie_Class == IECLASS_RAWKEY || coin->ie_Class == IECLASS_NEWMOUSE)\n\t\t{\n\t\t\tINS_ProcessInputMessage(coin, screeninfront && window->MouseX > 0 && window->MouseY > 0);\n\t\t}\n\n\t\tcoin = coin->ie_NextEvent;\n\t} while(coin);\n\n\treturn moo;\n}\n\n"
  },
  {
    "path": "engine/client/in_morphos.h",
    "content": "/*\nCopyright (C) 2006-2007 Mark Olsen\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  \n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n*/\n\n#define MAXIMSGS 32\n\nextern char keyconv[];\nextern struct EmulLibEntry myinputhandler;\n\n"
  },
  {
    "path": "engine/client/in_raw.h",
    "content": "// Raw input includes\n\n#ifndef WINUSERAPI\n#define WINUSERAPI\n#endif\n\n#ifndef RIM_TYPEMOUSE\n#define WM_INPUT 255\n\n#undef QS_INPUT\n#define QS_RAWINPUT 1024\n#define QS_INPUT 1031\n\n#define RIM_INPUT     0x00000000\n#define RIM_INPUTSINK 0x00000001\n#define RIM_TYPEMOUSE    0x00000000\n#define RIM_TYPEKEYBOARD 0x00000001\n#define RIM_TYPEHID      0x00000002\n#define MOUSE_MOVE_RELATIVE      0x00000000\n#define MOUSE_MOVE_ABSOLUTE      0x00000001\n#define MOUSE_VIRTUAL_DESKTOP    0x00000002\n#define MOUSE_ATTRIBUTES_CHANGED 0x00000004\n#define RI_MOUSE_LEFT_BUTTON_DOWN   0x0001\n#define RI_MOUSE_LEFT_BUTTON_UP     0x0002\n#define RI_MOUSE_RIGHT_BUTTON_DOWN  0x0004\n#define RI_MOUSE_RIGHT_BUTTON_UP    0x0008\n#define RI_MOUSE_MIDDLE_BUTTON_DOWN 0x0010\n#define RI_MOUSE_MIDDLE_BUTTON_UP   0x0020\n#define RI_MOUSE_BUTTON_1_DOWN      RI_MOUSE_LEFT_BUTTON_DOWN\n#define RI_MOUSE_BUTTON_1_UP        RI_MOUSE_LEFT_BUTTON_UP\n#define RI_MOUSE_BUTTON_2_DOWN      RI_MOUSE_RIGHT_BUTTON_DOWN\n#define RI_MOUSE_BUTTON_2_UP        RI_MOUSE_RIGHT_BUTTON_UP\n#define RI_MOUSE_BUTTON_3_DOWN      RI_MOUSE_MIDDLE_BUTTON_DOWN\n#define RI_MOUSE_BUTTON_3_UP        RI_MOUSE_MIDDLE_BUTTON_UP\n#define RI_MOUSE_BUTTON_4_DOWN      0x0040\n#define RI_MOUSE_BUTTON_4_UP        0x0080\n#define RI_MOUSE_BUTTON_5_DOWN      0x0100\n#define RI_MOUSE_BUTTON_5_UP        0x0200\n#define RI_MOUSE_WHEEL              0x0400\n#define KEYBOARD_OVERRUN_MAKE_CODE 0x00ff\n#define RI_KEY_MAKE            0x0000\n#define RI_KEY_BREAK           0x0001\n#define RI_KEY_E0              0x0002\n#define RI_KEY_E1              0x0004\n#define RI_KEY_TERMSRV_SET_LED 0x0008\n#define RI_KEY_TERMSRV_SHADOW  0x0010\n#define RID_INPUT  0x10000003\n#define RID_HEADER 0x10000005\n#define RIDI_PREPARSEDDATA 0x20000005\n#define RIDI_DEVICENAME    0x20000007\n#define RIDI_DEVICEINFO    0x2000000b\n#define RIDEV_REMOVE       0x00000001\n#define RIDEV_EXCLUDE      0x00000010\n#define RIDEV_PAGEONLY     0x00000020\n#define RIDEV_NOLEGACY     0x00000030\n#define RIDEV_INPUTSINK    0x00000100\n#define RIDEV_CAPTUREMOUSE 0x00000200\n#define RIDEV_NOHOTKEYS    0x00000200\n#define RIDEV_APPKEYS      0x00000400\n\nDECLARE_HANDLE(HRAWINPUT);\ntypedef struct tagRAWINPUTHEADER {\n\tDWORD dwType;\n\tDWORD dwSize;\n\tHANDLE hDevice;\n\tWPARAM wParam;\n} RAWINPUTHEADER,*PRAWINPUTHEADER;\ntypedef struct tagRAWMOUSE {\n\tUSHORT usFlags;\n\tunion {\n\t\tULONG ulButtons;\n\t\tstruct {\n\t\t\tUSHORT usButtonFlags;\n\t\t\tUSHORT usButtonData;\n\t\t};\n\t};\n\tULONG ulRawButtons;\n\tLONG lLastX;\n\tLONG lLastY;\n\tULONG ulExtraInformation;\n} RAWMOUSE,*PRAWMOUSE,*LPRAWMOUSE;\ntypedef struct tagRAWKEYBOARD {\n\tUSHORT MakeCode;\n\tUSHORT Flags;\n\tUSHORT Reserved;\n\tUSHORT VKey;\n\tUINT Message;\n\tULONG ExtraInformation;\n} RAWKEYBOARD,*PRAWKEYBOARD,*LPRAWKEYBOARD;\ntypedef struct tagRAWHID {\n\tDWORD dwSizeHid;\n\tDWORD dwCount;\n\tBYTE bRawData;\n} RAWHID,*PRAWHID,*LPRAWHID;\ntypedef struct tagRAWINPUT {\n\tRAWINPUTHEADER header;\n\tunion {\n\t\tRAWMOUSE    mouse;\n\t\tRAWKEYBOARD keyboard;\n\t\tRAWHID      hid;\n\t} data;\n} RAWINPUT,*PRAWINPUT,*LPRAWINPUT;\ntypedef struct tagRAWINPUTDEVICE {\n\tUSHORT usUsagePage;\n\tUSHORT usUsage;\n\tDWORD dwFlags;\n\tHWND hwndTarget;\n} RAWINPUTDEVICE,*PRAWINPUTDEVICE,*LPRAWINPUTDEVICE;\ntypedef const RAWINPUTDEVICE *PCRAWINPUTDEVICE;\ntypedef struct tagRAWINPUTDEVICELIST {\n\tHANDLE hDevice;\n\tDWORD dwType;\n} RAWINPUTDEVICELIST,*PRAWINPUTDEVICELIST;\n\nWINUSERAPI LRESULT WINAPI DefRawInputProc(PRAWINPUT*,INT,UINT);\nWINUSERAPI UINT WINAPI GetRawInputBuffer(PRAWINPUT,PUINT,UINT);\nWINUSERAPI UINT WINAPI GetRawInputData(HRAWINPUT,UINT,LPVOID,PUINT,UINT);\nWINUSERAPI UINT WINAPI GetRawInputDeviceInfoA(HANDLE,UINT,LPVOID,PUINT);\nWINUSERAPI UINT WINAPI GetRawInputDeviceInfoW(HANDLE,UINT,LPVOID,PUINT);\nWINUSERAPI UINT WINAPI GetRawInputDeviceList(PRAWINPUTDEVICELIST,PUINT,UINT);\nWINUSERAPI UINT WINAPI GetRegisteredRawInputDevices(PRAWINPUTDEVICE,PUINT,UINT);\nWINUSERAPI BOOL WINAPI RegisterRawInputDevices(PCRAWINPUTDEVICE,UINT,UINT);\n#endif\n"
  },
  {
    "path": "engine/client/in_sdl.c",
    "content": "#include \"quakedef.h\"\n\n#ifdef FTE_SDL3\n#include <SDL3/SDL.h>\n\n#ifdef VKQUAKE\n#include \"../vk/vkrenderer.h\"\n#endif\n#else\n#include <SDL.h>\n\n#if SDL_VERSION_ATLEAST(2,0,6) && defined(VKQUAKE)\n#include <SDL_vulkan.h>\n#include \"../vk/vkrenderer.h\"\n#endif\n#endif\n\n#if SDL_VERSION_ATLEAST(2,0,0)\nextern SDL_Window *sdlwindow;\n#else\nextern SDL_Surface *sdlsurf;\n#endif\n\nqboolean mouseactive;\nextern qboolean mouseusedforgui;\nextern qboolean vid_isfullscreen;\n\n#if SDL_MAJOR_VERSION > 1 || (SDL_MAJOR_VERSION == 1 && SDL_MINOR_VERSION >= 3)\n#define HAVE_SDL_TEXTINPUT\ncvar_t sys_osk = CVARD(\"sys_osk\", \"0\", \"Enables support for text input. This will be ignored when the console has focus, but gamecode may end up with composition boxes appearing.\");\n#endif\n\nvoid IN_ActivateMouse(void)\n{\n\tif (mouseactive)\n\t\treturn;\n\tif (!vid.activeapp)\n\t\treturn;\n\n\tmouseactive = true;\n#if SDL_VERSION_ATLEAST(3,0,0)\n\tSDL_HideCursor();\n\tSDL_SetWindowRelativeMouseMode(sdlwindow, true);\n\tSDL_SetWindowMouseGrab(sdlwindow, true);\n#elif SDL_VERSION_ATLEAST(2,0,0)\n\tSDL_ShowCursor(0);\n\tSDL_SetRelativeMouseMode(SDL_TRUE);\n\tSDL_SetWindowGrab(sdlwindow, SDL_TRUE);\n#else\n\tSDL_ShowCursor(0);\n\tSDL_WM_GrabInput(SDL_GRAB_ON);\n#endif\n}\n\nvoid IN_DeactivateMouse(void)\n{\n\tif (!mouseactive)\n\t\treturn;\n\n\tmouseactive = false;\n#if SDL_VERSION_ATLEAST(3,0,0)\n\tSDL_ShowCursor();\n\tSDL_SetWindowRelativeMouseMode(sdlwindow, false);\n\tSDL_SetWindowMouseGrab(sdlwindow, false);\n#elif SDL_VERSION_ATLEAST(2,0,0)\n\tSDL_ShowCursor(1);\n\tSDL_SetRelativeMouseMode(SDL_FALSE);\n\tSDL_SetWindowGrab(sdlwindow, SDL_FALSE);\n#else\n\tSDL_ShowCursor(1);\n\tSDL_WM_GrabInput(SDL_GRAB_OFF);\n#endif\n}\n\n#if SDL_VERSION_ATLEAST(2,0,0)\n#define MAX_FINGERS 16 //zomg! mutant!\nstatic struct sdlfinger_s\n{\n\tqboolean active;\n\tSDL_JoystickID jid;\n\tSDL_TouchID tid;\n\tSDL_FingerID fid;\n} sdlfinger[MAX_FINGERS];\n//sanitizes sdl fingers into touch events that our engine can eat.\n//we don't really deal with different devices, we just munge the lot into a single thing (allowing fingers to be tracked, at least if splitscreen isn't active).\nstatic uint32_t SDL_GiveFinger(SDL_JoystickID jid, SDL_TouchID tid, SDL_FingerID fid, qboolean fingerraised)\n{\n\tuint32_t f;\n\tfor (f = 0; f < countof(sdlfinger); f++)\n\t{\n\t\tif (sdlfinger[f].active)\n\t\t{\n\t\t\tif (sdlfinger[f].jid == jid && sdlfinger[f].tid == tid && sdlfinger[f].fid == fid)\n\t\t\t{\n\t\t\t\tsdlfinger[f].active = !fingerraised;\n\t\t\t\treturn f;\n\t\t\t}\n\t\t}\n\t}\n\tfor (f = 0; f < countof(sdlfinger); f++)\n\t{\n\t\tif (!sdlfinger[f].active)\n\t\t{\n\t\t\tsdlfinger[f].active = !fingerraised;\n\t\t\tsdlfinger[f].jid = jid;\n\t\t\tsdlfinger[f].tid = tid;\n\t\t\tsdlfinger[f].fid = fid;\n\t\t\treturn f;\n\t\t}\n\t}\n\treturn f;\n}\n#endif\n\n#if SDL_VERSION_ATLEAST(2,0,0)\n#define MAX_JOYSTICKS 16\n#ifndef MAXJOYAXIS\n#define MAXJOYAXIS 6\n#endif\n\nstatic struct\n{\n\tint qaxis;\n\tkeynum_t pos, neg;\n} gpaxismap[] =\n{\n\t{/*SDL_CONTROLLER_AXIS_LEFTX*/\t\t\tGPAXIS_LT_RIGHT,\tK_GP_LEFT_THUMB_RIGHT,\tK_GP_LEFT_THUMB_LEFT},\n\t{/*SDL_CONTROLLER_AXIS_LEFTY*/\t\t\tGPAXIS_LT_DOWN,\t\tK_GP_LEFT_THUMB_DOWN,\tK_GP_LEFT_THUMB_UP},\n\t{/*SDL_CONTROLLER_AXIS_RIGHTX*/\t\t\tGPAXIS_RT_RIGHT,\tK_GP_RIGHT_THUMB_RIGHT,\tK_GP_RIGHT_THUMB_LEFT},\n\t{/*SDL_CONTROLLER_AXIS_RIGHTY*/\t\t\tGPAXIS_RT_DOWN,\t\tK_GP_RIGHT_THUMB_DOWN,\tK_GP_RIGHT_THUMB_UP},\n\t{/*SDL_CONTROLLER_AXIS_TRIGGERLEFT*/\tGPAXIS_LT_TRIGGER,\tK_GP_LEFT_TRIGGER,\t\t0},\n\t{/*SDL_CONTROLLER_AXIS_TRIGGERRIGHT*/\tGPAXIS_RT_TRIGGER,\tK_GP_RIGHT_TRIGGER,\t\t0},\n};\nstatic const int gpbuttonmap[] =\n{\n\tK_GP_A,\n\tK_GP_B,\n\tK_GP_X,\n\tK_GP_Y,\n\tK_GP_BACK,\n\tK_GP_GUIDE,\n\tK_GP_START,\n\tK_GP_LEFT_STICK,\n\tK_GP_RIGHT_STICK,\n\tK_GP_LEFT_SHOULDER,\n\tK_GP_RIGHT_SHOULDER,\n\tK_GP_DPAD_UP,\n\tK_GP_DPAD_DOWN,\n\tK_GP_DPAD_LEFT,\n\tK_GP_DPAD_RIGHT,\n\tK_GP_MISC1,\n\tK_GP_PADDLE1,\n\tK_GP_PADDLE2,\n\tK_GP_PADDLE3,\n\tK_GP_PADDLE4,\n\tK_GP_TOUCHPAD\n};\n\nstatic struct sdljoy_s\n{\n\t//fte doesn't distinguish between joysticks and controllers.\n\t//in sdl, controllers are some glorified version of joysticks apparently.\n\tchar *devname;\n\tSDL_Joystick *joystick;\n#if SDL_VERSION_ATLEAST(3,0,0)\n\tSDL_Gamepad *controller;\n#else\n\tSDL_GameController *controller;\n#endif\n\tSDL_JoystickID id;\n\tunsigned int qdevid;\n\n\t//axis junk\n\tunsigned int axistobuttonp;\n\tunsigned int axistobuttonn;\n\tfloat repeattime[countof(gpaxismap)];\n\n\t//button junk\n\tunsigned int buttonheld;\n\tfloat buttonrepeat[countof(gpbuttonmap)];\n} sdljoy[MAX_JOYSTICKS]; \n\nstatic cvar_t joy_only = CVARD(\"joyonly\", \"0\", \"If true, treats \\\"game controllers\\\" as regular joysticks.\\nMust be set at startup.\");\n\n//the enumid is the value for the open function rather than the working id.\nstatic void J_AllocateDevID(struct sdljoy_s *joy)\n{\n\textern cvar_t in_skipplayerone;\n\tunsigned int id = (in_skipplayerone.ival?1:0), j;\n\tfor (j = 0; j < MAX_JOYSTICKS;)\n\t{\n\t\tif (sdljoy[j++].qdevid == id)\n\t\t{\n\t\t\tj = 0;\n\t\t\tid++;\n\t\t}\n\t}\n\n\tjoy->qdevid = id;\n\n#if SDL_VERSION_ATLEAST(3,0,0)\n\tif (joy->controller)\n\t{\n\t\t//enable some sensors if they're there. because we can.\n\t\tif (SDL_GamepadHasSensor(joy->controller, SDL_SENSOR_ACCEL))\n\t\t\tSDL_SetGamepadSensorEnabled(joy->controller, SDL_SENSOR_ACCEL, true);\n\t\tif (SDL_GamepadHasSensor(joy->controller, SDL_SENSOR_GYRO))\n\t\t\tSDL_SetGamepadSensorEnabled(joy->controller, SDL_SENSOR_GYRO, true);\n\t}\n#elif SDL_VERSION_ATLEAST(2,0,14)\n\tif (joy->controller)\n\t{\n\t\t//enable some sensors if they're there. because we can.\n\t\tif (SDL_GameControllerHasSensor(joy->controller, SDL_SENSOR_ACCEL))\n\t\t\tSDL_GameControllerSetSensorEnabled(joy->controller, SDL_SENSOR_ACCEL, SDL_TRUE);\n\t\tif (SDL_GameControllerHasSensor(joy->controller, SDL_SENSOR_GYRO))\n\t\t\tSDL_GameControllerSetSensorEnabled(joy->controller, SDL_SENSOR_GYRO, SDL_TRUE);\n\t}\n#endif\n}\nstatic void J_ControllerAdded(int enumid)\n{\n\tconst char *cname;\n\tint i;\n\n\tif (joy_only.ival)\n\t\treturn;\n\n\tfor (i = 0; i < MAX_JOYSTICKS; i++)\n\t\tif (sdljoy[i].controller == NULL && sdljoy[i].joystick == NULL)\n\t\t\tbreak;\n\tif (i == MAX_JOYSTICKS)\n\t\treturn;\n\n#if SDL_VERSION_ATLEAST(3,0,0)\n\tsdljoy[i].controller = SDL_OpenGamepad(enumid);\n\tif (!sdljoy[i].controller)\n\t\treturn;\n\tsdljoy[i].joystick = SDL_GetGamepadJoystick(sdljoy[i].controller);\n\tsdljoy[i].id = SDL_GetJoystickID(sdljoy[i].joystick);\n\n\tcname = SDL_GetGamepadName(sdljoy[i].controller);\n#else\n\tsdljoy[i].controller = SDL_GameControllerOpen(enumid);\n\tif (!sdljoy[i].controller)\n\t\treturn;\n\tsdljoy[i].joystick = SDL_GameControllerGetJoystick(sdljoy[i].controller);\n\tsdljoy[i].id = SDL_JoystickInstanceID(sdljoy[i].joystick);\n\n\tcname = SDL_GameControllerName(sdljoy[i].controller);\n#endif\n\tif (!cname)\n\t\tcname = \"Unknown Controller\";\n\tCon_Printf(\"Found new controller (%i): %s\\n\", i, cname);\n\tsdljoy[i].devname = Z_StrDup(cname);\n\tsdljoy[i].qdevid = DEVID_UNSET;\n}\nstatic void J_JoystickAdded(int enumid)\n{\n\tconst char *cname;\n\tint i;\n\tfor (i = 0; i < MAX_JOYSTICKS; i++)\n\t\tif (sdljoy[i].joystick == NULL && sdljoy[i].controller == NULL)\n\t\t\tbreak;\n\tif (i == MAX_JOYSTICKS)\n\t\treturn;\n\n#if SDL_VERSION_ATLEAST(3,0,0)\n\tif (!joy_only.ival  &&  SDL_IsGamepad(enumid))\t//if its reported via the gamecontroller api then use that instead. don't open it twice.\n\t\treturn;\n\n\tsdljoy[i].joystick = SDL_OpenJoystick(enumid);\n\tif (!sdljoy[i].joystick)\n\t\treturn;\n\tsdljoy[i].id = SDL_GetJoystickID(sdljoy[i].joystick);\n\n\tcname = SDL_GetJoystickName(sdljoy[i].joystick);\n#else\n\tif (!joy_only.ival  &&  SDL_IsGameController(enumid))\t//if its reported via the gamecontroller api then use that instead. don't open it twice.\n\t\treturn;\n\n\tsdljoy[i].joystick = SDL_JoystickOpen(enumid);\n\tif (!sdljoy[i].joystick)\n\t\treturn;\n\tsdljoy[i].id = SDL_JoystickInstanceID(sdljoy[i].joystick);\n\n\tcname = SDL_JoystickName(sdljoy[i].joystick);\n#endif\n\tif (!cname)\n\t\tcname = \"Unknown Joystick\";\n\tCon_Printf(\"Found new joystick (%i): %s\\n\", i, cname);\n\tsdljoy[i].qdevid = DEVID_UNSET;\n}\nstatic struct sdljoy_s *J_DevId(SDL_JoystickID jid)\n{\n\tint i;\n\tfor (i = 0; i < MAX_JOYSTICKS; i++)\n\t\tif (sdljoy[i].joystick && sdljoy[i].id == jid)\n\t\t\treturn &sdljoy[i];\n\treturn NULL;\n}\nstatic void J_ControllerAxis(SDL_JoystickID jid, int axis, int value)\n{\n\tstruct sdljoy_s *joy = J_DevId(jid);\n\n\tif (joy->qdevid == DEVID_UNSET)\n\t{\n\t\tif (abs(value) < 0x4000)\n\t\t\treturn;\t//has to be big enough to handle any generous dead zone.\n\t\tJ_AllocateDevID(joy);\n\t}\n\n\tif (joy && axis < countof(gpaxismap) && joy->qdevid != DEVID_UNSET)\n\t{\n\t\tif (value > 0x4000 && gpaxismap[axis].pos)\n\t\t{\n\t\t\tif (!(joy->axistobuttonp & (1u<<axis)))\n\t\t\t{\n\t\t\t\tIN_KeyEvent(joy->qdevid, true, gpaxismap[axis].pos, 0);\n\t\t\t\tjoy->repeattime[axis] = 1.0;\n\t\t\t}\n\t\t\tjoy->axistobuttonp |= 1u<<axis;\n\t\t}\n\t\telse if (joy->axistobuttonp & (1u<<axis))\n\t\t{\n\t\t\tIN_KeyEvent(joy->qdevid, false, gpaxismap[axis].pos, 0);\n\t\t\tjoy->axistobuttonp &= ~(1u<<axis);\n\t\t}\n\n\t\tif (value < -0x4000 && gpaxismap[axis].neg)\n\t\t{\n\t\t\tif (!(joy->axistobuttonn & (1u<<axis)))\n\t\t\t{\n\t\t\t\tIN_KeyEvent(joy->qdevid, true, gpaxismap[axis].neg, 0);\n\t\t\t\tjoy->repeattime[axis] = 1.0;\n\t\t\t}\n\t\t\tjoy->axistobuttonn |= 1u<<axis;\n\t\t}\n\t\telse if (joy->axistobuttonn & (1u<<axis))\n\t\t{\n\t\t\tIN_KeyEvent(joy->qdevid, false, gpaxismap[axis].neg, 0);\n\t\t\tjoy->axistobuttonn &= ~(1u<<axis);\n\t\t}\n\n\t\tIN_JoystickAxisEvent(joy->qdevid, gpaxismap[axis].qaxis, value / 32767.0);\n\t}\n}\nstatic void J_JoystickAxis(SDL_JoystickID jid, int axis, int value)\n{\n\tstruct sdljoy_s *joy = J_DevId(jid);\n\n\tif (joy->qdevid == DEVID_UNSET)\n\t{\n\t\tif (abs(value) < 0x1000)\n\t\t\treturn;\n\t\tJ_AllocateDevID(joy);\n\t}\n\n\tif (joy && !joy->controller && axis < MAXJOYAXIS && joy->qdevid != DEVID_UNSET)\n\t\tIN_JoystickAxisEvent(joy->qdevid, axis, value / 32767.0);\n}\n//we don't do hats and balls and stuff.\nstatic void J_ControllerButton(SDL_JoystickID jid, int button, qboolean pressed)\n{\n\t//controllers have reliable button maps.\n\t//but that doesn't meant that fte has specific k_ names for those buttons, but the mapping should be reliable, at least until they get mapped to proper k_ values.\n\tstruct sdljoy_s *joy = J_DevId(jid);\n\tif (joy && button < countof(gpbuttonmap))\n\t{\n\t\tif (joy->qdevid == DEVID_UNSET)\n\t\t{\n\t\t\tif (!pressed)\n\t\t\t\treturn;\n\t\t\tJ_AllocateDevID(joy);\n\t\t}\n\t\tif (pressed)\n\t\t\tjoy->buttonheld |= (1u<<button);\n\t\telse\n\t\t\tjoy->buttonheld &= ~(1u<<button);\n\t\tIN_KeyEvent(joy->qdevid, pressed, gpbuttonmap[button], 0);\n\t\tjoy->buttonrepeat[button] = 1.0;\n\t}\n}\nstatic void J_JoystickButton(SDL_JoystickID jid, int button, qboolean pressed)\n{\n\t//generic joysticks have no specific mappings. they're really random like that.\n\tstatic const int buttonmap[] = {\n\t\tK_JOY1,\n\t\tK_JOY2,\n\t\tK_JOY3,\n\t\tK_JOY4,\n\t\tK_JOY5,\n\t\tK_JOY6,\n\t\tK_JOY7,\n\t\tK_JOY8,\n\t\tK_JOY9,\n\t\tK_JOY10,\n\t\tK_JOY11,\n\t\tK_JOY12,\n\t\tK_JOY13,\n\t\tK_JOY14,\n\t\tK_JOY15,\n\t\tK_JOY16,\n\t\tK_JOY17,\n\t\tK_JOY18,\n\t\tK_JOY19,\n\t\tK_JOY20,\n\t\tK_JOY21,\n\t\tK_JOY22,\n\t\tK_JOY23,\n\t\tK_JOY24,\n\t\tK_JOY25,\n\t\tK_JOY26,\n\t\tK_JOY27,\n\t\tK_JOY28,\n\t\tK_JOY29,\n\t\tK_JOY30,\n\t\tK_JOY31,\n\t\tK_JOY32,\n\t\tK_AUX1,\n\t\tK_AUX2,\n\t\tK_AUX3,\n\t\tK_AUX4,\n\t\tK_AUX5,\n\t\tK_AUX6,\n\t\tK_AUX7,\n\t\tK_AUX8,\n\t\tK_AUX9,\n\t\tK_AUX10,\n\t\tK_AUX11,\n\t\tK_AUX12,\n\t\tK_AUX13,\n\t\tK_AUX14,\n\t\tK_AUX15,\n\t\tK_AUX16\n\t};\n\n\tstruct sdljoy_s *joy = J_DevId(jid);\n\tif (joy && !joy->controller && button < sizeof(buttonmap)/sizeof(buttonmap[0]))\n\t{\n\t\tif (joy->qdevid == DEVID_UNSET)\n\t\t{\n\t\t\tif (!pressed)\n\t\t\t\treturn;\n\t\t\tJ_AllocateDevID(joy);\n\t\t}\n\t\tIN_KeyEvent(joy->qdevid, pressed, buttonmap[button], 0);\n\t}\n}\n\nenum controllertype_e INS_GetControllerType(int id)\n{\n#if SDL_VERSION_ATLEAST(2,0,12)\n\tint i;\n\tfor (i = 0; i < MAX_JOYSTICKS; i++)\n\t{\n\t\tif (sdljoy[i].qdevid == id)\n\t\t{\n#if SDL_VERSION_ATLEAST(3,0,0)\n\t\t\tswitch(SDL_GetGamepadTypeForID(sdljoy[i].id))\n\t\t\t{\t//sdl reports whether it has touchpads etc. we only really report a value for button icons.\n\t\t\tcase SDL_GAMEPAD_TYPE_STANDARD:\t//I guess microsoft's xinput won.\n\t\t\tcase SDL_GAMEPAD_TYPE_XBOX360:\n\t\t\tcase SDL_GAMEPAD_TYPE_XBOXONE:\n\t\t\t\treturn CONTROLLER_XBOX;\n\t\t\tcase SDL_GAMEPAD_TYPE_PS3:\n\t\t\tcase SDL_GAMEPAD_TYPE_PS4:\n\t\t\tcase SDL_GAMEPAD_TYPE_PS5:\n\t\t\t\treturn CONTROLLER_PLAYSTATION;\n\t\t\tcase SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_PRO:\n\t\t\tcase SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_LEFT:\n\t\t\tcase SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_RIGHT:\n\t\t\tcase SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_PAIR:\n\t\t\t\treturn CONTROLLER_NINTENDO;\n\t\t\tcase SDL_GAMEPAD_TYPE_UNKNOWN:\n\t\t\tcase SDL_GAMEPAD_TYPE_COUNT:\n\t\t\tdefault:\n\t\t\t\treturn CONTROLLER_UNKNOWN;\n\t\t\t}\n#else\n\t\t\tswitch(SDL_GameControllerTypeForIndex(sdljoy[i].id))\n\t\t\t{\n#if SDL_VERSION_ATLEAST(2,0,14)\n\t\t\tcase SDL_CONTROLLER_TYPE_VIRTUAL:\t//don't really know... assume steaminput and thus steamdeck and thus xbox-like.\n\t\t\t\treturn CONTROLLER_VIRTUAL;\n#endif\n\t\t\tcase SDL_CONTROLLER_TYPE_XBOX360:\n\t\t\tcase SDL_CONTROLLER_TYPE_XBOXONE:\n#if SDL_VERSION_ATLEAST(2,0,16)\n\t\t\tcase SDL_CONTROLLER_TYPE_GOOGLE_STADIA:\t//close enough\n\t\t\tcase SDL_CONTROLLER_TYPE_AMAZON_LUNA:\t//it'll do. I guess we're starting to see a standard here.\n#endif\n#if SDL_VERSION_ATLEAST(2,0,24)\n\t\t\tcase SDL_CONTROLLER_TYPE_NVIDIA_SHIELD:\n#endif\n\t\t\t\treturn CONTROLLER_XBOX;\t//a on bottom, b('cancel') to right\n\t\t\tcase SDL_CONTROLLER_TYPE_PS3:\n\t\t\tcase SDL_CONTROLLER_TYPE_PS4:\n#if SDL_VERSION_ATLEAST(2,0,14)\n\t\t\tcase SDL_CONTROLLER_TYPE_PS5:\n#endif\n\t\t\t\treturn CONTROLLER_PLAYSTATION;\t//weird indecipherable shapes.\n\t\t\tcase SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO:\n#if SDL_VERSION_ATLEAST(2,0,24)\n\t\t\tcase SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_LEFT:\n\t\t\tcase SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_RIGHT:\n\t\t\tcase SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_PAIR:\n#endif\n\t\t\t\treturn CONTROLLER_NINTENDO;\t//b on bottom, a('cancel') to right\t\n\t\t\tcase SDL_CONTROLLER_TYPE_UNKNOWN:\n\t\t\tdefault:\t//for the future...\n\t\t\t\treturn CONTROLLER_UNKNOWN;\n\t\t\t}\n#endif\n\t\t}\n\t}\n#endif\n\treturn 0;\n}\nvoid INS_Rumble(int id, quint16_t amp_low, quint16_t amp_high, quint32_t duration)\n{\n#if SDL_VERSION_ATLEAST(2,0,9)\n\tint i;\n\tif (duration > 10000)\n\t\tduration = 10000;\n\n\tfor (i = 0; i < MAX_JOYSTICKS; i++)\n\t{\n\t\tif (sdljoy[i].qdevid == id)\n\t\t{\n#if SDL_VERSION_ATLEAST(3,0,0)\n\t\t\tSDL_RumbleGamepad(SDL_GetGamepadFromID(sdljoy[i].id), amp_low, amp_high, duration);\n#else\n\t\t\tSDL_GameControllerRumble(SDL_GameControllerFromInstanceID(sdljoy[i].id), amp_low, amp_high, duration);\n#endif\n\t\t\treturn;\n\t\t}\n\t}\n#else\n\tCon_DPrintf(CON_WARNING \"Rumble is requires at least SDL 2.0.9\\n\");\n#endif\n}\n\nvoid INS_RumbleTriggers(int id, quint16_t left, quint16_t right, quint32_t duration)\n{\n#if SDL_VERSION_ATLEAST(2,0,14)\n\tint i;\n\tif (duration > 10000)\n\t\tduration = 10000;\n\n\tfor (i = 0; i < MAX_JOYSTICKS; i++)\n\t{\n\t\tif (sdljoy[i].qdevid == id)\n\t\t{\n#if SDL_VERSION_ATLEAST(3,0,0)\n\t\t\tSDL_RumbleGamepadTriggers(SDL_GetGamepadFromID(sdljoy[i].id), left, right, duration);\n#else\n\t\t\tSDL_GameControllerRumbleTriggers(SDL_GameControllerFromInstanceID(sdljoy[i].id), left, right, duration);\n#endif\n\t\t\treturn;\n\t\t}\n\t}\n#else\n\tCon_DPrintf(CON_WARNING \"Trigger rumble is requires at least SDL 2.0.14\\n\");\n#endif\n}\n\nvoid INS_SetLEDColor(int id, vec3_t color)\n{\n#if SDL_VERSION_ATLEAST(2,0,14)\n\tint i;\n\t/* maybe we'll eventually get sRGB LEDs */\n\tcolor[0] *= 255.0f;\n\tcolor[1] *= 255.0f;\n\tcolor[2] *= 255.0f;\n\n\tfor (i = 0; i < MAX_JOYSTICKS; i++)\n\t{\n\t\tif (sdljoy[i].qdevid == id)\n\t\t{\n#if SDL_VERSION_ATLEAST(3,0,0)\n\t\t\tSDL_SetGamepadLED(SDL_GetGamepadFromID(sdljoy[i].id), (uint8_t)color[0], (uint8_t)color[1], (uint8_t)color[2]);\n#else\n\t\t\tSDL_GameControllerSetLED(SDL_GameControllerFromInstanceID(sdljoy[i].id), (uint8_t)color[0], (uint8_t)color[1], (uint8_t)color[2]);\n#endif\n\t\t\treturn;\n\t\t}\n\t}\n#else\n\tCon_DPrintf(CON_WARNING \"Trigger rumble is requires at least SDL 2.0.14\\n\");\n#endif\n}\n\nvoid INS_SetTriggerFX(int id, const void *data, size_t size)\n{\n#if SDL_VERSION_ATLEAST(2,0,15)\n\tint i;\n\tfor (i = 0; i < MAX_JOYSTICKS; i++)\n\t{\n\t\tif (sdljoy[i].qdevid == id)\n\t\t{\n#if SDL_VERSION_ATLEAST(3,0,0)\n\t\t\tSDL_SendGamepadEffect(SDL_GetGamepadFromID(sdljoy[i].id), &data, size);\n#else\n\t\t\tSDL_GameControllerSendEffect(SDL_GameControllerFromInstanceID(sdljoy[i].id), &data, size);\n#endif\n\t\t\treturn;\n\t\t}\n\t}\n#else\n\tCon_DPrintf(CON_WARNING \"Trigger FX is requires at least SDL 2.0.15\\n\");\n#endif\n}\n\n#if SDL_VERSION_ATLEAST(2,0,14)\nstatic void J_ControllerTouchPad(SDL_JoystickID jid, int pad, int finger, int fingerstate, float x, float y, float pressure)\n{\n#if 1\n\t//FIXME: forgets which seat its meant to be controlling.\n\tuint32_t thefinger = SDL_GiveFinger(jid, pad, finger, fingerstate<0);\n#else\n\t//FIXME: conflicts with regular mice (very problematic when using absolute coords)\n\tuint32_t thefinger;\n\tstruct sdljoy_s *joy = J_DevId(jid);\n\tif (!joy)\n\t\treturn;\n\tif (joy->qdevid == DEVID_UNSET)\n\t{\n\t\tif (fingerstate!=1)\n\t\t\treturn;\n\t\tJ_AllocateDevID(joy);\n\t}\n\tthefinger = joy->qdevid;\n#endif\n\tx *= vid.pixelwidth;\n\ty *= vid.pixelheight;\n\tIN_MouseMove(thefinger, true, x, y, 0, pressure);\n\tif (fingerstate)\n\t\tIN_KeyEvent(thefinger, fingerstate>0, K_GP_TOUCHPAD, 0);\n}\nstatic void J_ControllerSensor(SDL_JoystickID jid, SDL_SensorType sensor, float *data)\n{\n\tstruct sdljoy_s *joy = J_DevId(jid);\n\tif (!joy)\n\t\treturn;\n\t//don't assign an id here. wait for a button.\n\tif (joy->qdevid == DEVID_UNSET)\n\t\treturn;\n\n\tswitch(sensor)\n\t{\n\tcase SDL_SENSOR_ACCEL:\n\t\tIN_Accelerometer(joy->qdevid, data[0], data[1], data[2]);\n\t\tbreak;\n\tcase SDL_SENSOR_GYRO:\n\t\tIN_Gyroscope(joy->qdevid, data[0], data[1], data[2]);\n\t\tbreak;\n/*#if SDL_VERSION_ATLEAST(2,25,0)\n\tcase SDL_SENSOR_ACCEL_L:\n\tcase SDL_SENSOR_ACCEL_R:\n\tcase SDL_SENSOR_GYRO_L:\n\tcase SDL_SENSOR_GYRO_R:\n#endif*/\n\tcase SDL_SENSOR_INVALID:\n\tcase SDL_SENSOR_UNKNOWN:\n\tdefault:\n\t\tbreak;\n\t}\n}\n#endif\n\nstatic void J_Kill(SDL_JoystickID jid, qboolean verbose)\n{\n\tint i;\n\tstruct sdljoy_s *joy = J_DevId(jid);\n\n\tif (!joy)\n\t\treturn;\n\n\t//make sure all the axis are nulled out, to avoid surprises.\n\tif (joy->qdevid != DEVID_UNSET)\n\t{\n\t\tfor (i = 0; i < 6; i++)\n\t\t\tIN_JoystickAxisEvent(joy->qdevid, i, 0);\n\t\tif (joy->controller)\n\t\t{\n\t\t\tfor (i = 0; i < 32; i++)\n\t\t\t\tJ_ControllerButton(jid, i, false);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tfor (i = 0; i < 32; i++)\n\t\t\t\tJ_JoystickButton(jid, i, false);\n\t\t}\n\t}\n\n\tif (joy->controller)\n\t{\n\t\tCon_Printf(\"Controller unplugged(%i): %s\\n\", (int)(joy - sdljoy), joy->devname);\n#if SDL_VERSION_ATLEAST(3,0,0)\n\t\tSDL_CloseGamepad(joy->controller);\n#else\n\t\tSDL_GameControllerClose(joy->controller);\n#endif\n\t}\n\telse\n\t{\n\t\tCon_Printf(\"Joystick unplugged(%i): %s\\n\", (int)(joy - sdljoy), joy->devname);\n#if SDL_VERSION_ATLEAST(3,0,0)\n\t\tSDL_CloseJoystick(joy->joystick);\n#else\n\t\tSDL_JoystickClose(joy->joystick);\n#endif\n\t}\n\tjoy->controller = NULL;\n\tjoy->joystick = NULL;\n\tZ_Free(joy->devname);\n\tjoy->devname = NULL;\n\tjoy->qdevid = DEVID_UNSET;\n}\nstatic void J_KillAll(void)\n{\n\tint i;\n\tfor (i = 0; i < MAX_JOYSTICKS; i++)\n\t\tJ_Kill(sdljoy[i].id, false);\n}\n#endif\n\n#if SDL_VERSION_ATLEAST(3,0,0)\n//use SDL_GetKeyName(SDL_GetKeyFromScancode(quaketosdl[qkey])) for keybinds menu\nunsigned int MySDL_MapScancode(SDL_Scancode sdlscancode)\n{\n\tswitch(sdlscancode)\n\t{\n\tcase SDL_SCANCODE_A:\n\tcase SDL_SCANCODE_B:\n\tcase SDL_SCANCODE_C:\n\tcase SDL_SCANCODE_D:\n\tcase SDL_SCANCODE_E:\n\tcase SDL_SCANCODE_F:\n\tcase SDL_SCANCODE_G:\n\tcase SDL_SCANCODE_H:\n\tcase SDL_SCANCODE_I:\n\tcase SDL_SCANCODE_J:\n\tcase SDL_SCANCODE_K:\n\tcase SDL_SCANCODE_L:\n\tcase SDL_SCANCODE_M:\n\tcase SDL_SCANCODE_N:\n\tcase SDL_SCANCODE_O:\n\tcase SDL_SCANCODE_P:\n\tcase SDL_SCANCODE_Q:\n\tcase SDL_SCANCODE_R:\n\tcase SDL_SCANCODE_S:\n\tcase SDL_SCANCODE_T:\n\tcase SDL_SCANCODE_U:\n\tcase SDL_SCANCODE_V:\n\tcase SDL_SCANCODE_W:\n\tcase SDL_SCANCODE_X:\n\tcase SDL_SCANCODE_Y:\n\tcase SDL_SCANCODE_Z:\t\t\treturn 'a' + sdlscancode-SDL_SCANCODE_A;\n\tcase SDL_SCANCODE_1:\n\tcase SDL_SCANCODE_2:\n\tcase SDL_SCANCODE_3:\n\tcase SDL_SCANCODE_4:\n\tcase SDL_SCANCODE_5:\n\tcase SDL_SCANCODE_6:\n\tcase SDL_SCANCODE_7:\n\tcase SDL_SCANCODE_8:\n\tcase SDL_SCANCODE_9:\t\t\treturn '1' + sdlscancode-SDL_SCANCODE_1;\n\tcase SDL_SCANCODE_0:\t\t\treturn '0';\n\tcase SDL_SCANCODE_RETURN:\t\treturn K_ENTER;\n\tcase SDL_SCANCODE_ESCAPE:\t\treturn K_ESCAPE;\n\tcase SDL_SCANCODE_BACKSPACE:\treturn K_BACKSPACE;\n\tcase SDL_SCANCODE_TAB:\t\t\treturn K_TAB;\n\tcase SDL_SCANCODE_SPACE:\t\treturn K_SPACE;\n\tcase SDL_SCANCODE_MINUS:\t\treturn '-';\n\tcase SDL_SCANCODE_EQUALS:\t\treturn '=';\n\tcase SDL_SCANCODE_LEFTBRACKET:\treturn '[';\n\tcase SDL_SCANCODE_RIGHTBRACKET:\treturn ']';\n\tcase SDL_SCANCODE_BACKSLASH:\treturn '#';\n\tcase SDL_SCANCODE_NONUSHASH:\treturn '#';\n\tcase SDL_SCANCODE_SEMICOLON:\treturn ';';\n\tcase SDL_SCANCODE_APOSTROPHE:\treturn '\\'';\n\tcase SDL_SCANCODE_GRAVE:\t\treturn '`';\n\tcase SDL_SCANCODE_COMMA:\t\treturn ',';\n\tcase SDL_SCANCODE_PERIOD:\t\treturn '.';\n\tcase SDL_SCANCODE_SLASH:\t\treturn '/';\n\tcase SDL_SCANCODE_CAPSLOCK:\t\treturn K_CAPSLOCK;\n\tcase SDL_SCANCODE_F1:\t\t\treturn K_F1;\n\tcase SDL_SCANCODE_F2:\t\t\treturn K_F2;\n\tcase SDL_SCANCODE_F3:\t\t\treturn K_F3;\n\tcase SDL_SCANCODE_F4:\t\t\treturn K_F4;\n\tcase SDL_SCANCODE_F5:\t\t\treturn K_F5;\n\tcase SDL_SCANCODE_F6:\t\t\treturn K_F6;\n\tcase SDL_SCANCODE_F7:\t\t\treturn K_F7;\n\tcase SDL_SCANCODE_F8:\t\t\treturn K_F8;\n\tcase SDL_SCANCODE_F9:\t\t\treturn K_F9;\n\tcase SDL_SCANCODE_F10:\t\t\treturn K_F10;\n\tcase SDL_SCANCODE_F11:\t\t\treturn K_F11;\n\tcase SDL_SCANCODE_F12:\t\t\treturn K_F12;\n\tcase SDL_SCANCODE_PRINTSCREEN:\treturn K_PRINTSCREEN;\n\tcase SDL_SCANCODE_SCROLLLOCK:\treturn K_SCRLCK;\n\tcase SDL_SCANCODE_PAUSE:\t\treturn K_PAUSE;\n\tcase SDL_SCANCODE_INSERT:\t\treturn K_INS;\n\tcase SDL_SCANCODE_HOME:\t\t\treturn K_HOME;\n\tcase SDL_SCANCODE_PAGEUP:\t\treturn K_PGUP;\n\tcase SDL_SCANCODE_DELETE:\t\treturn K_DEL;\n\tcase SDL_SCANCODE_END:\t\t\treturn K_END;\n\tcase SDL_SCANCODE_PAGEDOWN:\t\treturn K_PGDN;\n\tcase SDL_SCANCODE_RIGHT:\t\treturn K_RIGHTARROW;\n\tcase SDL_SCANCODE_LEFT:\t\t\treturn K_LEFTARROW;\n\tcase SDL_SCANCODE_DOWN:\t\t\treturn K_DOWNARROW;\n\tcase SDL_SCANCODE_UP:\t\t\treturn K_UPARROW;\n\tcase SDL_SCANCODE_NUMLOCKCLEAR:\treturn K_KP_NUMLOCK;\n\tcase SDL_SCANCODE_KP_DIVIDE:\treturn K_KP_SLASH;\n\tcase SDL_SCANCODE_KP_MULTIPLY:\treturn K_KP_STAR;\n\tcase SDL_SCANCODE_KP_MINUS:\t\treturn K_KP_MINUS;\n\tcase SDL_SCANCODE_KP_PLUS:\t\treturn K_KP_PLUS;\n\tcase SDL_SCANCODE_KP_ENTER:\t\treturn K_KP_ENTER;\n\tcase SDL_SCANCODE_KP_1:\t\t\treturn K_KP_END;\n\tcase SDL_SCANCODE_KP_2:\t\t\treturn K_KP_DOWNARROW;\n\tcase SDL_SCANCODE_KP_3:\t\t\treturn K_KP_PGDN;\n\tcase SDL_SCANCODE_KP_4:\t\t\treturn K_KP_LEFTARROW;\n\tcase SDL_SCANCODE_KP_5:\t\t\treturn K_KP_5;\n\tcase SDL_SCANCODE_KP_6:\t\t\treturn K_KP_RIGHTARROW;\n\tcase SDL_SCANCODE_KP_7:\t\t\treturn K_KP_HOME;\n\tcase SDL_SCANCODE_KP_8:\t\t\treturn K_KP_UPARROW;\n\tcase SDL_SCANCODE_KP_9:\t\t\treturn K_KP_PGUP;\n\tcase SDL_SCANCODE_KP_0:\t\t\treturn K_KP_INS;\n\tcase SDL_SCANCODE_KP_PERIOD:\treturn K_KP_DEL;\n\tcase SDL_SCANCODE_NONUSBACKSLASH:\treturn '\\\\';\n\tcase SDL_SCANCODE_APPLICATION:\t\treturn K_APP;\n\tcase SDL_SCANCODE_POWER:\t\t\treturn K_POWER;\n\tcase SDL_SCANCODE_KP_EQUALS:\t\treturn K_KP_EQUALS;\n\tcase SDL_SCANCODE_F13:\t\t\t\treturn K_F13;\n\tcase SDL_SCANCODE_F14:\t\t\t\treturn K_F14;\n\tcase SDL_SCANCODE_F15:\t\t\t\treturn K_F15;\n//\tcase SDL_SCANCODE_F16:\t\t\t\treturn K_F16;\n//\tcase SDL_SCANCODE_F17:\t\t\t\treturn K_F17;\n//\tcase SDL_SCANCODE_F18:\t\t\t\treturn K_F18;\n//\tcase SDL_SCANCODE_F19:\t\t\t\treturn K_F19;\n//\tcase SDL_SCANCODE_F20:\t\t\t\treturn K_F20;\n//\tcase SDL_SCANCODE_F21:\t\t\t\treturn K_F21;\n//\tcase SDL_SCANCODE_F22:\t\t\t\treturn K_F22;\n//\tcase SDL_SCANCODE_F23:\t\t\t\treturn K_F23;\n//\tcase SDL_SCANCODE_F24:\t\t\t\treturn K_F24;\n//\tcase SDL_SCANCODE_EXECUTE:\t\t\treturn K_;\n//\tcase SDL_SCANCODE_HELP:\t\t\t\treturn K_;\n//\tcase SDL_SCANCODE_MENU:\t\t\t\treturn K_;\n//\tcase SDL_SCANCODE_SELECT:\t\t\treturn K_;\n//\tcase SDL_SCANCODE_STOP:\t\t\t\treturn K_;\n//\tcase SDL_SCANCODE_AGAIN:\t\t\treturn K_;\n//\tcase SDL_SCANCODE_UNDO:\t\t\t\treturn K_;\n//\tcase SDL_SCANCODE_CUT:\t\t\t\treturn K_;\n//\tcase SDL_SCANCODE_COPY:\t\t\t\treturn K_;\n//\tcase SDL_SCANCODE_PASTE:\t\t\treturn K_;\n//\tcase SDL_SCANCODE_FIND:\t\t\t\treturn K_;\n//\tcase SDL_SCANCODE_MUTE:\t\t\t\treturn K_;\n\tcase SDL_SCANCODE_VOLUMEUP:\t\t\treturn K_VOLUP;\n\tcase SDL_SCANCODE_VOLUMEDOWN:\t\treturn K_VOLDOWN;\n//\tcase SDL_SCANCODE_KP_COMMA:\t\t\treturn K_KP_;\n//\tcase SDL_SCANCODE_KP_EQUALSAS400:\treturn K_KP_;\n//\tcase SDL_SCANCODE_INTERNATIONAL1:\treturn K_;\n//\tcase SDL_SCANCODE_INTERNATIONAL2:\treturn K_;\n//\tcase SDL_SCANCODE_INTERNATIONAL3:\treturn K_;\n//\tcase SDL_SCANCODE_INTERNATIONAL4:\treturn K_;\n//\tcase SDL_SCANCODE_INTERNATIONAL5:\treturn K_;\n//\tcase SDL_SCANCODE_INTERNATIONAL6:\treturn K_;\n//\tcase SDL_SCANCODE_INTERNATIONAL7:\treturn K_;\n//\tcase SDL_SCANCODE_INTERNATIONAL8:\treturn K_;\n//\tcase SDL_SCANCODE_INTERNATIONAL9:\treturn K_;\n//\tcase SDL_SCANCODE_LANG1:\t\t\treturn K_;\n//\tcase SDL_SCANCODE_LANG2:\t\t\treturn K_;\n//\tcase SDL_SCANCODE_LANG3:\t\t\treturn K_;\n//\tcase SDL_SCANCODE_LANG4:\t\t\treturn K_;\n//\tcase SDL_SCANCODE_LANG5:\t\t\treturn K_;\n//\tcase SDL_SCANCODE_LANG6:\t\t\treturn K_;\n//\tcase SDL_SCANCODE_LANG7:\t\t\treturn K_;\n//\tcase SDL_SCANCODE_LANG8:\t\t\treturn K_;\n//\tcase SDL_SCANCODE_LANG9:\t\t\treturn K_;\n//\tcase SDL_SCANCODE_ALTERASE:\t\t\treturn K_;\n//\tcase SDL_SCANCODE_SYSREQ:\t\t\treturn K_;\n//\tcase SDL_SCANCODE_CANCEL:\t\t\treturn K_;\n//\tcase SDL_SCANCODE_CLEAR:\t\t\treturn K_;\n//\tcase SDL_SCANCODE_PRIOR:\t\t\treturn K_;\n//\tcase SDL_SCANCODE_RETURN2:\t\t\treturn K_;\n//\tcase SDL_SCANCODE_SEPARATOR:\t\treturn K_;\n//\tcase SDL_SCANCODE_OUT:\t\t\t\treturn K_;\n//\tcase SDL_SCANCODE_OPER:\t\t\t\treturn K_;\n//\tcase SDL_SCANCODE_CLEARAGAIN:\t\treturn K_;\n//\tcase SDL_SCANCODE_CRSEL:\t\t\treturn K_;\n//\tcase SDL_SCANCODE_EXSEL:\t\t\treturn K_;\n//\tcase SDL_SCANCODE_KP_00:\t\t\t\treturn K_KP_;\n//\tcase SDL_SCANCODE_KP_000:\t\t\t\treturn K_KP_;\n//\tcase SDL_SCANCODE_THOUSANDSSEPARATOR:\treturn K_KP_;\n//\tcase SDL_SCANCODE_DECIMALSEPARATOR:\t\treturn K_KP_;\n//\tcase SDL_SCANCODE_CURRENCYUNIT:\t\t\treturn K_KP_;\n//\tcase SDL_SCANCODE_CURRENCYSUBUNIT:\t\treturn K_KP_;\n//\tcase SDL_SCANCODE_KP_LEFTPAREN:\t\t\treturn K_KP_;\n//\tcase SDL_SCANCODE_KP_RIGHTPAREN:\t\treturn K_KP_;\n//\tcase SDL_SCANCODE_KP_LEFTBRACE:\t\t\treturn K_KP_;\n//\tcase SDL_SCANCODE_KP_RIGHTBRACE:\t\treturn K_KP_;\n//\tcase SDL_SCANCODE_KP_TAB:\t\t\t\treturn K_KP_;\n//\tcase SDL_SCANCODE_KP_BACKSPACE:\t\t\treturn K_KP_;\n//\tcase SDL_SCANCODE_KP_A:\t\t\t\t\treturn K_KP_;\n//\tcase SDL_SCANCODE_KP_B:\t\t\t\t\treturn K_KP_;\n//\tcase SDL_SCANCODE_KP_C:\t\t\t\t\treturn K_KP_;\n//\tcase SDL_SCANCODE_KP_D:\t\t\t\t\treturn K_KP_;\n//\tcase SDL_SCANCODE_KP_E:\t\t\t\t\treturn K_KP_;\n//\tcase SDL_SCANCODE_KP_F:\t\t\t\t\treturn K_KP_;\n//\tcase SDL_SCANCODE_KP_XOR:\t\t\t\treturn K_KP_;\n//\tcase SDL_SCANCODE_KP_POWER:\t\t\t\treturn K_KP_;\n//\tcase SDL_SCANCODE_KP_PERCENT:\t\t\treturn K_KP_;\n//\tcase SDL_SCANCODE_KP_LESS:\t\t\t\treturn K_KP_;\n//\tcase SDL_SCANCODE_KP_GREATER:\t\t\treturn K_KP_;\n//\tcase SDL_SCANCODE_KP_AMPERSAND:\t\t\treturn K_KP_;\n//\tcase SDL_SCANCODE_KP_DBLAMPERSAND:\t\treturn K_KP_;\n//\tcase SDL_SCANCODE_KP_VERTICALBAR:\t\treturn K_KP_;\n//\tcase SDL_SCANCODE_KP_DBLVERTICALBAR:\treturn K_KP_;\n//\tcase SDL_SCANCODE_KP_COLON:\t\t\t\treturn K_KP_;\n//\tcase SDL_SCANCODE_KP_HASH:\t\t\t\treturn K_KP_;\n//\tcase SDL_SCANCODE_KP_SPACE:\t\t\t\treturn K_KP_;\n//\tcase SDL_SCANCODE_KP_AT:\t\t\t\treturn K_KP_;\n//\tcase SDL_SCANCODE_KP_EXCLAM:\t\t\treturn K_KP_;\n//\tcase SDL_SCANCODE_KP_MEMSTORE:\t\t\treturn K_KP_;\n//\tcase SDL_SCANCODE_KP_MEMRECALL:\t\t\treturn K_KP_;\n//\tcase SDL_SCANCODE_KP_MEMCLEAR:\t\t\treturn K_KP_;\n//\tcase SDL_SCANCODE_KP_MEMADD:\t\t\treturn K_KP_;\n//\tcase SDL_SCANCODE_KP_MEMSUBTRACT:\t\treturn K_KP_;\n//\tcase SDL_SCANCODE_KP_MEMMULTIPLY:\t\treturn K_KP_;\n//\tcase SDL_SCANCODE_KP_MEMDIVIDE:\t\t\treturn K_KP_;\n//\tcase SDL_SCANCODE_KP_PLUSMINUS:\t\t\treturn K_KP_;\n//\tcase SDL_SCANCODE_KP_CLEAR:\t\t\t\treturn K_KP_;\n//\tcase SDL_SCANCODE_KP_CLEARENTRY:\t\treturn K_KP_;\n//\tcase SDL_SCANCODE_KP_BINARY:\t\t\treturn K_KP_;\n//\tcase SDL_SCANCODE_KP_OCTAL:\t\t\t\treturn K_KP_;\n//\tcase SDL_SCANCODE_KP_DECIMAL:\t\t\treturn K_KP_;\n//\tcase SDL_SCANCODE_KP_HEXADECIMAL:\t\treturn K_KP_;\n\tcase SDL_SCANCODE_LCTRL:\t\t\t\treturn K_LCTRL;\n\tcase SDL_SCANCODE_LSHIFT:\t\t\t\treturn K_LSHIFT;\n\tcase SDL_SCANCODE_LALT:\t\t\t\t\treturn K_LALT;\n\tcase SDL_SCANCODE_LGUI:\t\t\t\t\treturn K_LWIN;\n\tcase SDL_SCANCODE_RCTRL:\t\t\t\treturn K_RCTRL;\n\tcase SDL_SCANCODE_RSHIFT:\t\t\t\treturn K_RSHIFT;\n\tcase SDL_SCANCODE_RALT:\t\t\t\t\treturn K_RALT;\n\tcase SDL_SCANCODE_RGUI:\t\t\t\t\treturn K_RWIN;\n//\tcase SDL_SCANCODE_MODE:\t\t\t\t\treturn K_;\n//\tcase SDL_SCANCODE_SLEEP:\t\t\t\treturn K_;\n//\tcase SDL_SCANCODE_WAKE:\t\t\t\t\treturn K_;\n//\tcase SDL_SCANCODE_CHANNEL_INCREMENT:\treturn K_;\n//\tcase SDL_SCANCODE_CHANNEL_DECREMENT:\treturn K_;\n//\tcase SDL_SCANCODE_MEDIA_PLAY:\t\t\treturn K_;\n//\tcase SDL_SCANCODE_MEDIA_PAUSE:\t\t\treturn K_MM_TRACK_PLAYPAUSE;\n//\tcase SDL_SCANCODE_MEDIA_RECORD:\t\t\treturn K_MM_;\n//\tcase SDL_SCANCODE_MEDIA_FAST_FORWARD:\treturn K_MM_;\n//\tcase SDL_SCANCODE_MEDIA_REWIND:\t\t\treturn K_MM_;\n\tcase SDL_SCANCODE_MEDIA_NEXT_TRACK:\t\treturn K_MM_TRACK_NEXT;\n\tcase SDL_SCANCODE_MEDIA_PREVIOUS_TRACK:\treturn K_MM_TRACK_PREV;\n\tcase SDL_SCANCODE_MEDIA_STOP:\t\t\treturn K_MM_TRACK_STOP;\n//\tcase SDL_SCANCODE_MEDIA_EJECT:\t\t\treturn K_MM_;\n\tcase SDL_SCANCODE_MEDIA_PLAY_PAUSE:\t\treturn K_MM_TRACK_PLAYPAUSE;\n//\tcase SDL_SCANCODE_MEDIA_SELECT:\t\t\treturn K_MM_;\n//\tcase SDL_SCANCODE_AC_NEW:\t\t\t\treturn K_MM_;\n//\tcase SDL_SCANCODE_AC_OPEN:\t\t\t\treturn K_MM_;\n//\tcase SDL_SCANCODE_AC_CLOSE:\t\t\t\treturn K_MM_;\n//\tcase SDL_SCANCODE_AC_EXIT:\t\t\t\treturn K_MM_;\n//\tcase SDL_SCANCODE_AC_SAVE:\t\t\t\treturn K_MM_;\n//\tcase SDL_SCANCODE_AC_PRINT:\t\t\t\treturn K_MM_;\n//\tcase SDL_SCANCODE_AC_PROPERTIES:\t\treturn K_MM_;\n//\tcase SDL_SCANCODE_AC_SEARCH:\t\t\treturn K_MM_;\n\tcase SDL_SCANCODE_AC_HOME:\t\t\t\treturn K_MM_BROWSER_HOME;\n\tcase SDL_SCANCODE_AC_BACK:\t\t\t\treturn K_MM_BROWSER_BACK;\n\tcase SDL_SCANCODE_AC_FORWARD:\t\t\treturn K_MM_BROWSER_FORWARD;\n\tcase SDL_SCANCODE_AC_STOP:\t\t\t\treturn K_MM_BROWSER_STOP;\n\tcase SDL_SCANCODE_AC_REFRESH:\t\t\treturn K_MM_BROWSER_REFRESH;\n\tcase SDL_SCANCODE_AC_BOOKMARKS:\t\t\treturn K_MM_BROWSER_FAVORITES;\n//\tcase SDL_SCANCODE_SOFTLEFT:\n//\tcase SDL_SCANCODE_SOFTRIGHT:\n//\tcase SDL_SCANCODE_CALL:\n//\tcase SDL_SCANCODE_ENDCALL:\n\tcase SDL_SCANCODE_UNKNOWN:\n\tdefault:\t\t\t\treturn 0;\n\t}\n}\n#elif SDL_VERSION_ATLEAST(2,0,0)\n//FIXME: switch to scancodes rather than keysyms\n//use SDL_GetKeyName(SDL_GetKeyFromScancode(quaketosdl[qkey])) for keybinds menu\nunsigned int MySDL_MapKey(SDL_Keycode sdlkey)\n{\n\tswitch(sdlkey)\n\t{\n\tdefault:\t\t\t\treturn 0;\n\t//any ascii chars can be mapped directly to keys, even if they're only ever accessed with shift etc... oh well.\n\tcase SDLK_RETURN:\t\treturn K_ENTER;\n\tcase SDLK_ESCAPE:\t\treturn K_ESCAPE;\n\tcase SDLK_BACKSPACE:\treturn K_BACKSPACE;\n\tcase SDLK_TAB:\t\t\treturn K_TAB;\n\tcase SDLK_SPACE:\t\treturn K_SPACE;\n\tcase SDLK_EXCLAIM:\n\tcase SDLK_HASH:\n\tcase SDLK_PERCENT:\n\tcase SDLK_DOLLAR:\n\tcase SDLK_AMPERSAND:\n\tcase SDLK_LEFTPAREN:\n\tcase SDLK_RIGHTPAREN:\n\tcase SDLK_ASTERISK:\n\tcase SDLK_PLUS:\n\tcase SDLK_COMMA:\n\tcase SDLK_MINUS:\n\tcase SDLK_PERIOD:\n\tcase SDLK_SLASH:\n\tcase SDLK_0:\n\tcase SDLK_1:\n\tcase SDLK_2:\n\tcase SDLK_3:\n\tcase SDLK_4:\n\tcase SDLK_5:\n\tcase SDLK_6:\n\tcase SDLK_7:\n\tcase SDLK_8:\n\tcase SDLK_9:\n\tcase SDLK_COLON:\n\tcase SDLK_SEMICOLON:\n\tcase SDLK_LESS:\n\tcase SDLK_EQUALS:\n\tcase SDLK_GREATER:\n\tcase SDLK_QUESTION:\n\tcase SDLK_AT:\n\tcase SDLK_LEFTBRACKET:\n\tcase SDLK_BACKSLASH:\n\tcase SDLK_RIGHTBRACKET:\n\tcase SDLK_CARET:\n\tcase SDLK_UNDERSCORE:\n\tcase SDLK_QUOTEDBL:\n\tcase SDLK_QUOTE:\n\tcase SDLK_BACKQUOTE:\n\tcase SDLK_a:\n\tcase SDLK_b:\n\tcase SDLK_c:\n\tcase SDLK_d:\n\tcase SDLK_e:\n\tcase SDLK_f:\n\tcase SDLK_g:\n\tcase SDLK_h:\n\tcase SDLK_i:\n\tcase SDLK_j:\n\tcase SDLK_k:\n\tcase SDLK_l:\n\tcase SDLK_m:\n\tcase SDLK_n:\n\tcase SDLK_o:\n\tcase SDLK_p:\n\tcase SDLK_q:\n\tcase SDLK_r:\n\tcase SDLK_s:\n\tcase SDLK_t:\n\tcase SDLK_u:\n\tcase SDLK_v:\n\tcase SDLK_w:\n\tcase SDLK_x:\n\tcase SDLK_y:\n\tcase SDLK_z:\n\t\treturn sdlkey;\n\tcase SDLK_CAPSLOCK:\t\treturn K_CAPSLOCK;\n\tcase SDLK_F1:\t\t\treturn K_F1;\n\tcase SDLK_F2:\t\t\treturn K_F2;\n\tcase SDLK_F3:\t\t\treturn K_F3;\n\tcase SDLK_F4:\t\t\treturn K_F4;\n\tcase SDLK_F5:\t\t\treturn K_F5;\n\tcase SDLK_F6:\t\t\treturn K_F6;\n\tcase SDLK_F7:\t\t\treturn K_F7;\n\tcase SDLK_F8:\t\t\treturn K_F8;\n\tcase SDLK_F9: \t\t\treturn K_F9;\n\tcase SDLK_F10:\t\t\treturn K_F10;\n\tcase SDLK_F11:\t\t\treturn K_F11;\n\tcase SDLK_F12:\t\t\treturn K_F12;\n\tcase SDLK_PRINTSCREEN:\treturn K_PRINTSCREEN;\n\tcase SDLK_SCROLLLOCK:\treturn K_SCRLCK;\n\tcase SDLK_PAUSE:\t\treturn K_PAUSE;\n\tcase SDLK_INSERT:\t\treturn K_INS;\n\tcase SDLK_HOME:\t\t\treturn K_HOME;\n\tcase SDLK_PAGEUP:\t\treturn K_PGUP;\n\tcase SDLK_DELETE:\t\treturn K_DEL;\n\tcase SDLK_END:\t\t\treturn K_END;\n\tcase SDLK_PAGEDOWN:\t\treturn K_PGDN;\n\tcase SDLK_RIGHT:\t\treturn K_RIGHTARROW;\n\tcase SDLK_LEFT:\t\t\treturn K_LEFTARROW;\n\tcase SDLK_DOWN:\t\t\treturn K_DOWNARROW;\n\tcase SDLK_UP:\t\t\treturn K_UPARROW;\n\tcase SDLK_NUMLOCKCLEAR:\treturn K_KP_NUMLOCK;\n\tcase SDLK_KP_DIVIDE:\treturn K_KP_SLASH;\n\tcase SDLK_KP_MULTIPLY:\treturn K_KP_STAR;\n\tcase SDLK_KP_MINUS:\t\treturn K_KP_MINUS;\n\tcase SDLK_KP_PLUS:\t\treturn K_KP_PLUS;\n\tcase SDLK_KP_ENTER:\t\treturn K_KP_ENTER;\n\tcase SDLK_KP_1:\t\t\treturn K_KP_END;\n\tcase SDLK_KP_2:\t\t\treturn K_KP_DOWNARROW;\n\tcase SDLK_KP_3:\t\t\treturn K_KP_PGDN;\n\tcase SDLK_KP_4:\t\t\treturn K_KP_LEFTARROW;\n\tcase SDLK_KP_5:\t\t\treturn K_KP_5;\n\tcase SDLK_KP_6:\t\t\treturn K_KP_RIGHTARROW;\n\tcase SDLK_KP_7:\t\t\treturn K_KP_HOME;\n\tcase SDLK_KP_8:\t\t\treturn K_KP_UPARROW;\n\tcase SDLK_KP_9:\t\t\treturn K_KP_PGDN;\n\tcase SDLK_KP_0:\t\t\treturn K_KP_INS;\n\tcase SDLK_KP_PERIOD:\treturn K_KP_DEL;\n\tcase SDLK_APPLICATION:\treturn K_APP;\n\tcase SDLK_POWER:\t\treturn K_POWER;\n\tcase SDLK_KP_EQUALS:\treturn K_KP_EQUALS;\n\tcase SDLK_F13:\t\t\treturn K_F13;\n\tcase SDLK_F14:\t\t\treturn K_F14;\n\tcase SDLK_F15:\t\t\treturn K_F15;\n/*\n\tcase SDLK_F16:\t\t\treturn K_;\n\tcase SDLK_F17:\t\t\treturn K_;\n\tcase SDLK_F18:\t\t\treturn K_;\n\tcase SDLK_F19:\t\t\treturn K_;\n\tcase SDLK_F20:\t\t\treturn K_;\n\tcase SDLK_F21:\t\t\treturn K_;\n\tcase SDLK_F22:\t\t\treturn K_;\n\tcase SDLK_F23:\t\t\treturn K_;\n\tcase SDLK_F24:\t\t\treturn K_;\n\tcase SDLK_EXECUTE:\t\treturn K_;\n\tcase SDLK_HELP:\t\t\treturn K_;\n\tcase SDLK_MENU:\t\t\treturn K_;\n\tcase SDLK_SELECT:\t\treturn K_;\n\tcase SDLK_STOP:\t\t\treturn K_;\n\tcase SDLK_AGAIN:\t\treturn K_;\n\tcase SDLK_UNDO:\t\t\treturn K_;\n\tcase SDLK_CUT:\t\t\treturn K_;\n\tcase SDLK_COPY:\t\t\treturn K_;\n\tcase SDLK_PASTE:\t\treturn K_;\n\tcase SDLK_FIND:\t\t\treturn K_;\n\tcase SDLK_MUTE:\t\t\treturn K_;\n*/\n\tcase SDLK_VOLUMEUP:\t\treturn K_VOLUP;\n\tcase SDLK_VOLUMEDOWN:\treturn K_VOLDOWN;\n/*\n\tcase SDLK_KP_COMMA:\t\treturn K_;\n\tcase SDLK_KP_EQUALSAS400:\treturn K_;\n\tcase SDLK_ALTERASE:\t\treturn K_;\n\tcase SDLK_SYSREQ:\t\treturn K_;\n\tcase SDLK_CANCEL:\t\treturn K_;\n\tcase SDLK_CLEAR:\t\treturn K_;\n\tcase SDLK_PRIOR:\t\treturn K_;\n\tcase SDLK_RETURN2:\t\treturn K_;\n\tcase SDLK_SEPARATOR:\treturn K_;\n\tcase SDLK_OUT:\t\t\treturn K_;\n\tcase SDLK_OPER:\t\t\treturn K_;\n\tcase SDLK_CLEARAGAIN:\treturn K_;\n\tcase SDLK_CRSEL:\t\treturn K_;\n\tcase SDLK_EXSEL:\t\treturn K_;\n\tcase SDLK_KP_00:\t\treturn K_;\n\tcase SDLK_KP_000:\t\treturn K_;\n\tcase SDLK_THOUSANDSSEPARATOR:\treturn K_;\n\tcase SDLK_DECIMALSEPARATOR:\t\treturn K_;\n\tcase SDLK_CURRENCYUNIT:\t\t\treturn K_;\n\tcase SDLK_CURRENCYSUBUNIT:\t\treturn K_;\n\tcase SDLK_KP_LEFTPAREN:\t\t\treturn K_;\n\tcase SDLK_KP_RIGHTPAREN:\t\treturn K_;\n\tcase SDLK_KP_LEFTBRACE:\t\t\treturn K_;\n\tcase SDLK_KP_RIGHTBRACE:\t\treturn K_;\n\tcase SDLK_KP_TAB:\t\treturn K_;\n\tcase SDLK_KP_BACKSPACE:\treturn K_;\n\tcase SDLK_KP_A:\t\t\treturn K_;\n\tcase SDLK_KP_B:\t\t\treturn K_;\n\tcase SDLK_KP_C:\t\t\treturn K_;\n\tcase SDLK_KP_D:\t\t\treturn K_;\n\tcase SDLK_KP_E:\t\t\treturn K_;\n\tcase SDLK_KP_F:\t\t\treturn K_;\n\tcase SDLK_KP_XOR:\t\treturn K_;\n\tcase SDLK_KP_POWER:\t\treturn K_;\n\tcase SDLK_KP_PERCENT:\treturn K_;\n\tcase SDLK_KP_LESS:\t\treturn K_;\n\tcase SDLK_KP_GREATER:\treturn K_;\n\tcase SDLK_KP_AMPERSAND: return K_;\n\tcase SDLK_KP_DBLAMPERSAND:\t\treturn K_;\n\tcase SDLK_KP_VERTICALBAR:\t\treturn K_;\n\tcase SDLK_KP_DBLVERTICALBAR:\treturn K_;\n\tcase SDLK_KP_COLON:\t\treturn K_;\n\tcase SDLK_KP_HASH:\t\treturn K_;\n\tcase SDLK_KP_SPACE:\t\treturn K_;\n\tcase SDLK_KP_AT:\t\treturn K_;\n\tcase SDLK_KP_EXCLAM:\treturn K_;\n\tcase SDLK_KP_MEMSTORE:\treturn K_;\n\tcase SDLK_KP_MEMRECALL:\treturn K_;\n\tcase SDLK_KP_MEMCLEAR:\treturn K_;\n\tcase SDLK_KP_MEMADD:\treturn K_;\n\tcase SDLK_KP_MEMSUBTRACT:\treturn K_;\n\tcase SDLK_KP_MEMMULTIPLY:\treturn K_;\n\tcase SDLK_KP_MEMDIVIDE:\t\treturn K_;\n\tcase SDLK_KP_PLUSMINUS:\t\treturn K_;\n\tcase SDLK_KP_CLEAR:\t\t\treturn K_;\n\tcase SDLK_KP_CLEARENTRY:\treturn K_;\n\tcase SDLK_KP_BINARY:\t\treturn K_;\n\tcase SDLK_KP_OCTAL:\t\t\treturn K_;\n\tcase SDLK_KP_DECIMAL:\t\treturn K_;\n\tcase SDLK_KP_HEXADECIMAL:\treturn K_;\n*/\n\tcase SDLK_LCTRL:\t\treturn K_LCTRL;\n\tcase SDLK_LSHIFT:\t\treturn K_LSHIFT;\n\tcase SDLK_LALT:\t\t\treturn K_LALT;\n\tcase SDLK_LGUI:\t\t\treturn K_APP;\n\tcase SDLK_RCTRL:\t\treturn K_RCTRL;\n\tcase SDLK_RSHIFT:\t\treturn K_RSHIFT;\n\tcase SDLK_RALT:\t\t\treturn K_RALT;\n/*\n\tcase SDLK_RGUI:\t\t\treturn K_;\n\tcase SDLK_MODE:\t\t\treturn K_;\n\tcase SDLK_AUDIONEXT:\treturn K_;\n\tcase SDLK_AUDIOPREV:\treturn K_;\n\tcase SDLK_AUDIOSTOP:\treturn K_;\n\tcase SDLK_AUDIOPLAY:\treturn K_;\n\tcase SDLK_AUDIOMUTE:\treturn K_;\n\tcase SDLK_MEDIASELECT:\treturn K_;\n\tcase SDLK_WWW:\t\t\treturn K_;\n\tcase SDLK_MAIL:\t\t\treturn K_;\n\tcase SDLK_CALCULATOR:\treturn K_;\n\tcase SDLK_COMPUTER:\t\treturn K_;\n\tcase SDLK_AC_SEARCH:\treturn K_;\n\tcase SDLK_AC_HOME:\t\treturn K_;\n\tcase SDLK_AC_BACK:\t\treturn K_;\n\tcase SDLK_AC_FORWARD:\treturn K_;\n\tcase SDLK_AC_STOP:\t\treturn K_;\n\tcase SDLK_AC_REFRESH:\treturn K_;\n\tcase SDLK_AC_BOOKMARKS:\treturn K_;\n\tcase SDLK_BRIGHTNESSDOWN:\treturn K_;\n\tcase SDLK_BRIGHTNESSUP:\t\treturn K_;\n\tcase SDLK_DISPLAYSWITCH:\treturn K_;\n\tcase SDLK_KBDILLUMTOGGLE:\treturn K_;\n\tcase SDLK_KBDILLUMDOWN:\t\treturn K_;\n\tcase SDLK_KBDILLUMUP:\t\treturn K_;\n\tcase SDLK_EJECT:\t\t\treturn K_;\n\tcase SDLK_SLEEP:\t\t\treturn K_;\n*/\n\t}\n}\n#else\n#define tenoh\t0,0,0,0,0, 0,0,0,0,0\n#define fiftyoh tenoh, tenoh, tenoh, tenoh, tenoh\n#define hundredoh fiftyoh, fiftyoh\nstatic unsigned int tbl_sdltoquake[] =\n{\n\t0,0,0,0,\t\t//SDLK_UNKNOWN\t\t= 0,\n\t0,0,0,0,\t\t//SDLK_FIRST\t\t= 0,\n\tK_BACKSPACE,\t//SDLK_BACKSPACE\t= 8,\n\tK_TAB,\t\t\t//SDLK_TAB\t\t\t= 9,\n\t0,0,\n\t0,\t\t\t\t//SDLK_CLEAR\t\t= 12,\n\tK_ENTER,\t\t//SDLK_RETURN\t\t= 13,\n\t0,0,0,0,0,\n\tK_PAUSE,\t\t//SDLK_PAUSE\t\t= 19,\n\t0,0,0,0,0,0,0,\n\tK_ESCAPE,\t\t//SDLK_ESCAPE\t\t= 27,\n\t0,0,0,0,\n\tK_SPACE,\t\t//SDLK_SPACE\t\t= 32,\n\t'!',\t\t\t//SDLK_EXCLAIM\t\t= 33,\n\t'\"',\t\t\t//SDLK_QUOTEDBL\t\t= 34,\n\t'#',\t\t\t//SDLK_HASH\t\t\t= 35,\n\t'$',\t\t\t//SDLK_DOLLAR\t\t= 36,\n\t0,\n\t'&',\t\t\t//SDLK_AMPERSAND\t= 38,\n\t'\\'',\t\t\t//SDLK_QUOTE\t\t= 39,\n\t'(',\t\t\t//SDLK_LEFTPAREN\t= 40,\n\t')',\t\t\t//SDLK_RIGHTPAREN\t= 41,\n\t'*',\t\t\t//SDLK_ASTERISK\t\t= 42,\n\t'+',\t\t\t//SDLK_PLUS\t\t\t= 43,\n\t',',\t\t\t//SDLK_COMMA\t\t= 44,\n\t'-',\t\t\t//SDLK_MINUS\t\t= 45,\n\t'.',\t\t\t//SDLK_PERIOD\t\t= 46,\n\t'/',\t\t\t//SDLK_SLASH\t\t= 47,\n\t'0',\t\t\t//SDLK_0\t\t\t= 48,\n\t'1',\t\t\t//SDLK_1\t\t\t= 49,\n\t'2',\t\t\t//SDLK_2\t\t\t= 50,\n\t'3',\t\t\t//SDLK_3\t\t\t= 51,\n\t'4',\t\t\t//SDLK_4\t\t\t= 52,\n\t'5',\t\t\t//SDLK_5\t\t\t= 53,\n\t'6',\t\t\t//SDLK_6\t\t\t= 54,\n\t'7',\t\t\t//SDLK_7\t\t\t= 55,\n\t'8',\t\t\t//SDLK_8\t\t\t= 56,\n\t'9',\t\t\t//SDLK_9\t\t\t= 57,\n\t':',\t\t\t//SDLK_COLON\t\t= 58,\n\t';',\t\t\t//SDLK_SEMICOLON\t= 59,\n\t'<',\t\t\t//SDLK_LESS\t\t\t= 60,\n\t'=',\t\t\t//SDLK_EQUALS\t\t= 61,\n\t'>',\t\t\t//SDLK_GREATER\t\t= 62,\n\t'?',\t\t\t//SDLK_QUESTION\t\t= 63,\n\t'@',\t\t\t//SDLK_AT\t\t\t= 64,\n\t0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,\n\t'[',\t\t//SDLK_LEFTBRACKET\t= 91,\n\t'\\\\',\t\t//SDLK_BACKSLASH\t= 92,\n\t']',\t\t//SDLK_RIGHTBRACKET\t= 93,\n\t'^',\t\t//SDLK_CARET\t\t= 94,\n\t'_',\t\t//SDLK_UNDERSCORE\t= 95,\n\t'`',\t\t//SDLK_BACKQUOTE\t= 96,\n\t'a',\t\t//SDLK_a\t\t\t= 97,\n\t'b',\t\t//SDLK_b\t\t\t= 98,\n\t'c',\t\t//SDLK_c\t\t\t= 99,\n\t'd',\t\t//SDLK_d\t\t\t= 100,\n\t'e',\t\t//SDLK_e\t\t\t= 101,\n\t'f',\t\t//SDLK_f\t\t\t= 102,\n\t'g',\t\t//SDLK_g\t\t\t= 103,\n\t'h',\t\t//SDLK_h\t\t\t= 104,\n\t'i',\t\t//SDLK_i\t\t\t= 105,\n\t'j',\t\t//SDLK_j\t\t\t= 106,\n\t'k',\t\t//SDLK_k\t\t\t= 107,\n\t'l',\t\t//SDLK_l\t\t\t= 108,\n\t'm',\t\t//SDLK_m\t\t\t= 109,\n\t'n',\t\t//SDLK_n\t\t\t= 110,\n\t'o',\t\t//SDLK_o\t\t\t= 111,\n\t'p',\t\t//SDLK_p\t\t\t= 112,\n\t'q',\t\t//SDLK_q\t\t\t= 113,\n\t'r',\t\t//SDLK_r\t\t\t= 114,\n\t's',\t\t//SDLK_s\t\t\t= 115,\n\t't',\t\t//SDLK_t\t\t\t= 116,\n\t'u',\t\t//SDLK_u\t\t\t= 117,\n\t'v',\t\t//SDLK_v\t\t\t= 118,\n\t'w',\t\t//SDLK_w\t\t\t= 119,\n\t'x',\t\t//SDLK_x\t\t\t= 120,\n\t'y',\t\t//SDLK_y\t\t\t= 121,\n\t'z',\t\t//SDLK_z\t\t\t= 122,\n\t0,0,0,0,\n\tK_DEL, \t\t//SDLK_DELETE\t\t= 127,\n\thundredoh /*227*/, tenoh, tenoh, 0,0,0,0,0,0,0,0,\n\tK_KP_INS,\t\t//SDLK_KP0\t\t= 256,\n\tK_KP_END,\t\t//SDLK_KP1\t\t= 257,\n\tK_KP_DOWNARROW,\t\t//SDLK_KP2\t\t= 258,\n\tK_KP_PGDN,\t\t//SDLK_KP3\t\t= 259,\n\tK_KP_LEFTARROW,\t\t//SDLK_KP4\t\t= 260,\n\tK_KP_5,\t\t//SDLK_KP5\t\t= 261,\n\tK_KP_RIGHTARROW,\t\t//SDLK_KP6\t\t= 262,\n\tK_KP_HOME,\t\t//SDLK_KP7\t\t= 263,\n\tK_KP_UPARROW,\t\t//SDLK_KP8\t\t= 264,\n\tK_KP_PGUP,\t\t//SDLK_KP9\t\t= 265,\n\tK_KP_DEL,//SDLK_KP_PERIOD\t= 266,\n\tK_KP_SLASH,//SDLK_KP_DIVIDE\t= 267,\n\tK_KP_STAR,//SDLK_KP_MULTIPLY= 268,\n\tK_KP_MINUS,\t//SDLK_KP_MINUS\t\t= 269,\n\tK_KP_PLUS,\t//SDLK_KP_PLUS\t\t= 270,\n\tK_KP_ENTER,\t//SDLK_KP_ENTER\t\t= 271,\n\tK_KP_EQUALS,//SDLK_KP_EQUALS\t= 272,\n\tK_UPARROW,\t//SDLK_UP\t\t= 273,\n\tK_DOWNARROW,//SDLK_DOWN\t\t= 274,\n\tK_RIGHTARROW,//SDLK_RIGHT\t= 275,\n\tK_LEFTARROW,//SDLK_LEFT\t\t= 276,\n\tK_INS,\t\t//SDLK_INSERT\t= 277,\n\tK_HOME,\t\t//SDLK_HOME\t\t= 278,\n\tK_END,\t\t//SDLK_END\t\t= 279,\n\tK_PGUP, \t//SDLK_PAGEUP\t= 280,\n\tK_PGDN,\t\t//SDLK_PAGEDOWN\t= 281,\n\tK_F1,\t\t//SDLK_F1\t\t= 282,\n\tK_F2,\t\t//SDLK_F2\t\t= 283,\n\tK_F3,\t\t//SDLK_F3\t\t= 284,\n\tK_F4,\t\t//SDLK_F4\t\t= 285,\n\tK_F5,\t\t//SDLK_F5\t\t= 286,\n\tK_F6,\t\t//SDLK_F6\t\t= 287,\n\tK_F7,\t\t//SDLK_F7\t\t= 288,\n\tK_F8,\t\t//SDLK_F8\t\t= 289,\n\tK_F9,\t\t//SDLK_F9\t\t= 290,\n\tK_F10,\t\t//SDLK_F10\t\t= 291,\n\tK_F11,\t\t//SDLK_F11\t\t= 292,\n\tK_F12,\t\t//SDLK_F12\t\t= 293,\n\t0,\t\t\t//SDLK_F13\t\t= 294,\n\t0,\t\t\t//SDLK_F14\t\t= 295,\n\t0,\t\t\t//SDLK_F15\t\t= 296,\n\t0,0,0,\n\t0,//K_NUMLOCK,\t//SDLK_NUMLOCK\t= 300,\n\tK_CAPSLOCK,\t//SDLK_CAPSLOCK\t= 301,\n\t0,//K_SCROLLOCK,//SDLK_SCROLLOCK= 302,\n\tK_RSHIFT,\t//SDLK_RSHIFT\t= 303,\n\tK_LSHIFT,\t//SDLK_LSHIFT\t= 304,\n\tK_RCTRL,\t//SDLK_RCTRL\t= 305,\n\tK_LCTRL,\t//SDLK_LCTRL\t= 306,\n\tK_RALT,\t\t//SDLK_RALT\t\t= 307,\n\tK_LALT,\t\t//SDLK_LALT\t\t= 308,\n\t0,\t\t\t//SDLK_RMETA\t= 309,\n\t0,\t\t\t//SDLK_LMETA\t= 310,\n\t0,\t\t\t//SDLK_LSUPER\t= 311,\t\t/* Left \"Windows\" key */\n\t0,\t\t\t//SDLK_RSUPER\t= 312,\t\t/* Right \"Windows\" key */\n\t0,\t\t\t//SDLK_MODE\t\t= 313,\t\t/* \"Alt Gr\" key */\n\t0,\t\t\t//SDLK_COMPOSE\t= 314,\t\t/* Multi-key compose key */\n\t0,\t\t\t//SDLK_HELP\t\t= 315,\n\t0,\t\t\t//SDLK_PRINT\t= 316,\n\t0,\t\t\t//SDLK_SYSREQ\t= 317,\n\tK_PAUSE,\t//SDLK_BREAK\t= 318,\n\t0,\t\t\t//SDLK_MENU\t\t= 319,\n\t0,\t\t\t//SDLK_POWER\t= 320,\t\t/* Power Macintosh power key */\n\t'e',\t\t//SDLK_EURO\t\t= 321,\t\t/* Some european keyboards */\n\t0\t\t\t//SDLK_UNDO\t\t= 322,\t\t/* Atari keyboard has Undo */\n};\n#endif\n\n/* stubbed */\nqboolean INS_KeyToLocalName(int qkey, char *buf, size_t bufsize)\n{\n\tconst char *keyname;\n\n\t//too lazy to make+maintain a reverse MySDL_MapKey, so lets just make a reverse table with it instead.\n\tstatic qboolean stupidtabsetup;\n\tstatic SDL_Keycode stupidtable[K_MAX];\n\tif (!stupidtabsetup)\n\t{\n\t\tint i, k;\n#if SDL_VERSION_ATLEAST(3, 0, 0)\n\t\tfor (i = 0; i < SDL_SCANCODE_COUNT; i++)\n\t\t{\n\t\t\tk = MySDL_MapScancode(i);\n\t\t\tif (k)\n\t\t\t\tstupidtable[k] = SDL_SCANCODE_TO_KEYCODE(i);\n\t\t}\n#else\n\t\tfor (i = 0; i < SDL_NUM_SCANCODES; i++)\n\t\t{\n\t\t\tk = MySDL_MapKey(i);\n\t\t\tif (k)\n\t\t\t\tstupidtable[k] = i;\n\n\t\t\t//grr, oh well, at least we're not scanning 2 billion codes here\n\t\t\tk = MySDL_MapKey(SDL_SCANCODE_TO_KEYCODE(i));\n\t\t\tif (k)\n\t\t\t\tstupidtable[k] = SDL_SCANCODE_TO_KEYCODE(i);\n\t\t}\n#endif\n\t\tstupidtabsetup = true;\n\t}\n\n\tif (stupidtable[qkey])\n\t\tkeyname = SDL_GetKeyName(stupidtable[qkey]);\n\telse if (qkey >= K_GP_DIAMOND_DOWN && qkey < K_GP_TOUCHPAD)\n\t{\n#if SDL_VERSION_ATLEAST(3, 0, 0)\n\t\tSDL_GamepadButton b;\n\t\tswitch(qkey)\n\t\t{\n\t\tcase K_GP_DIAMOND_DOWN:\t\tb = SDL_GAMEPAD_BUTTON_SOUTH;\t\t\tbreak;\n\t\tcase K_GP_DIAMOND_RIGHT:\tb = SDL_GAMEPAD_BUTTON_EAST;\t\t\tbreak;\n\t\tcase K_GP_DIAMOND_LEFT:\t\tb = SDL_GAMEPAD_BUTTON_WEST;\t\t\tbreak;\n\t\tcase K_GP_DIAMOND_UP:\t\tb = SDL_GAMEPAD_BUTTON_NORTH;\t\t\tbreak;\n\t\tcase K_GP_BACK:\t\t\t\tb = SDL_GAMEPAD_BUTTON_BACK;\t\t\tbreak;\n\t\tcase K_GP_GUIDE:\t\t\tb = SDL_GAMEPAD_BUTTON_GUIDE;\t\t\tbreak;\n\t\tcase K_GP_START:\t\t\tb = SDL_GAMEPAD_BUTTON_START;\t\t\tbreak;\n\t\tcase K_GP_LEFT_STICK:\t\tb = SDL_GAMEPAD_BUTTON_LEFT_STICK;\t\tbreak;\n\t\tcase K_GP_RIGHT_STICK:\t\tb = SDL_GAMEPAD_BUTTON_RIGHT_STICK;\t\tbreak;\n\t\tcase K_GP_LEFT_SHOULDER:\tb = SDL_GAMEPAD_BUTTON_LEFT_SHOULDER;\tbreak;\n\t\tcase K_GP_RIGHT_SHOULDER:\tb = SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER;\tbreak;\n\t\tcase K_GP_DPAD_UP:\t\t\tb = SDL_GAMEPAD_BUTTON_DPAD_UP;\t\t\tbreak;\n\t\tcase K_GP_DPAD_DOWN:\t\tb = SDL_GAMEPAD_BUTTON_DPAD_DOWN;\t\tbreak;\n\t\tcase K_GP_DPAD_LEFT:\t\tb = SDL_GAMEPAD_BUTTON_DPAD_LEFT;\t\tbreak;\n\t\tcase K_GP_DPAD_RIGHT:\t\tb = SDL_GAMEPAD_BUTTON_DPAD_RIGHT;\t\tbreak;\n#if SDL_VERSION_ATLEAST(2, 0, 14)\n\t\tcase K_GP_MISC1:\t\t\tb = SDL_GAMEPAD_BUTTON_MISC1;\t\t\tbreak;\n\t\tcase K_GP_PADDLE1:\t\t\tb = SDL_GAMEPAD_BUTTON_RIGHT_PADDLE1;\tbreak;\n\t\tcase K_GP_PADDLE2:\t\t\tb = SDL_GAMEPAD_BUTTON_LEFT_PADDLE1;\tbreak;\n\t\tcase K_GP_PADDLE3:\t\t\tb = SDL_GAMEPAD_BUTTON_RIGHT_PADDLE2;\tbreak;\n\t\tcase K_GP_PADDLE4:\t\t\tb = SDL_GAMEPAD_BUTTON_LEFT_PADDLE2;\tbreak;\n\t\tcase K_GP_TOUCHPAD:\t\t\tb = SDL_GAMEPAD_BUTTON_TOUCHPAD;\t\tbreak;\n#endif\n\t\tdefault:\n\t\t\treturn false;\n\t\t}\n\t\tkeyname = SDL_GetGamepadStringForButton(b);\n#else\n\t\tSDL_GameControllerButton b;\n\t\tswitch(qkey)\n\t\t{\n\t\tcase K_GP_DIAMOND_DOWN:\t\tb = SDL_CONTROLLER_BUTTON_A;\t\t\t\tbreak;\n\t\tcase K_GP_DIAMOND_RIGHT:\tb = SDL_CONTROLLER_BUTTON_B;\t\t\t\tbreak;\n\t\tcase K_GP_DIAMOND_LEFT:\t\tb = SDL_CONTROLLER_BUTTON_X;\t\t\t\tbreak;\n\t\tcase K_GP_DIAMOND_UP:\t\tb = SDL_CONTROLLER_BUTTON_Y;\t\t\t\tbreak;\n\t\tcase K_GP_BACK:\t\t\t\tb = SDL_CONTROLLER_BUTTON_BACK;\t\t\t\tbreak;\n\t\tcase K_GP_GUIDE:\t\t\tb = SDL_CONTROLLER_BUTTON_GUIDE;\t\t\tbreak;\n\t\tcase K_GP_START:\t\t\tb = SDL_CONTROLLER_BUTTON_START;\t\t\tbreak;\n\t\tcase K_GP_LEFT_STICK:\t\tb = SDL_CONTROLLER_BUTTON_LEFTSTICK;\t\tbreak;\n\t\tcase K_GP_RIGHT_STICK:\t\tb = SDL_CONTROLLER_BUTTON_RIGHTSTICK;\t\tbreak;\n\t\tcase K_GP_LEFT_SHOULDER:\tb = SDL_CONTROLLER_BUTTON_LEFTSHOULDER;\t\tbreak;\n\t\tcase K_GP_RIGHT_SHOULDER:\tb = SDL_CONTROLLER_BUTTON_RIGHTSHOULDER;\tbreak;\n\t\tcase K_GP_DPAD_UP:\t\t\tb = SDL_CONTROLLER_BUTTON_DPAD_UP;\t\t\tbreak;\n\t\tcase K_GP_DPAD_DOWN:\t\tb = SDL_CONTROLLER_BUTTON_DPAD_DOWN;\t\tbreak;\n\t\tcase K_GP_DPAD_LEFT:\t\tb = SDL_CONTROLLER_BUTTON_DPAD_LEFT;\t\tbreak;\n\t\tcase K_GP_DPAD_RIGHT:\t\tb = SDL_CONTROLLER_BUTTON_DPAD_RIGHT;\t\tbreak;\n#if SDL_VERSION_ATLEAST(2, 0, 14)\n\t\tcase K_GP_MISC1:\t\t\tb = SDL_CONTROLLER_BUTTON_MISC1;\t\t\tbreak;\n\t\tcase K_GP_PADDLE1:\t\t\tb = SDL_CONTROLLER_BUTTON_PADDLE1;\t\t\tbreak;\n\t\tcase K_GP_PADDLE2:\t\t\tb = SDL_CONTROLLER_BUTTON_PADDLE2;\t\t\tbreak;\n\t\tcase K_GP_PADDLE3:\t\t\tb = SDL_CONTROLLER_BUTTON_PADDLE3;\t\t\tbreak;\n\t\tcase K_GP_PADDLE4:\t\t\tb = SDL_CONTROLLER_BUTTON_PADDLE4;\t\t\tbreak;\n\t\tcase K_GP_TOUCHPAD:\t\t\tb = SDL_CONTROLLER_BUTTON_TOUCHPAD;\t\t\tbreak;\n#endif\n\t\tdefault:\n\t\t\treturn false;\n\t\t}\n\t\tkeyname = SDL_GameControllerGetStringForButton(b);\n#endif\n\t}\n\telse\n\t\treturn false;\n\n\tif (keyname)\n\t{\n\t\tQ_strncpyz(buf, keyname, bufsize);\n\t\treturn true;\n\t}\n\treturn false;\n}\n\nstatic unsigned int tbl_sdltoquakemouse[] =\n{\n\tK_MOUSE1,\n\tK_MOUSE3,\n\tK_MOUSE2,\n#if !SDL_VERSION_ATLEAST(2, 0, 0)\n\tK_MWHEELUP,\n\tK_MWHEELDOWN,\n#endif\n\tK_MOUSE4,\n\tK_MOUSE5,\n\tK_MOUSE6,\n\tK_MOUSE7,\n\tK_MOUSE8,\n\tK_MOUSE9,\n\tK_MOUSE10\n};\n\n#ifdef HAVE_SDL_TEXTINPUT\n#if SDL_VERSION_ATLEAST(3, 0, 0)\n#elif defined(__linux__)\n#include <SDL_misc.h>\nstatic qboolean usesteamosk;\n#endif\n\nvoid INS_SetOSK(int osk)\n{\n\tstatic qboolean active = false;\n\tif (osk && sdlwindow)\n\t{\n\t\tSDL_Rect rect;\n\t\trect.x = 0;\n\t\trect.y = vid.rotpixelheight/2;\n\t\trect.w = vid.rotpixelwidth;\n\t\trect.h = vid.rotpixelheight - rect.y;\n#if SDL_VERSION_ATLEAST(3, 0, 0)\n\t\tSDL_SetTextInputArea(sdlwindow, &rect, 0);\n#else\n\t\tSDL_SetTextInputRect(&rect);\n#endif\n\n\t\tif (!active)\n\t\t{\n#if SDL_VERSION_ATLEAST(3, 0, 0)\n\t\t\tSDL_StartTextInput(sdlwindow);\n#else\n#ifdef __linux__\n\t\t\tif (usesteamosk)\n\t\t\t\tSDL_OpenURL(\"steam://open/keyboard?Mode=1\");\n\t\t\telse\n#endif\n\t\t\t\tSDL_StartTextInput();\n#endif\n\t\t\tactive = true;\n//\t\t\tCon_Printf(\"OSK shown...\\n\");\n\t\t}\n\t}\n\telse\n\t{\n\t\tif (active)\n\t\t{\n#if SDL_VERSION_ATLEAST(3, 0, 0)\n\t\t\tSDL_StopTextInput(sdlwindow);\n#else\n#ifdef __linux__\n\t\t\tif (usesteamosk)\n\t\t\t\tSDL_OpenURL(\"steam://close/keyboard?Mode=1\");\n\t\t\telse\n#endif\n\t\t\t\tSDL_StopTextInput();\n#endif\n\t\t\tactive = false;\n//\t\t\tCon_Printf(\"OSK shown... killed\\n\");\n\t\t}\n\t}\n}\n#else\nvoid INS_SetOSK(int osk)\n{\n}\n#endif\n\n#if SDL_VERSION_ATLEAST(3, 0, 0)\nstatic void INS_MouseAddRem(SDL_MouseID mid, qboolean added)\n{\n\tif (added)\n\t\treturn;\t//let INS_MouseID add it instead. so we're not adding devices that won't be used at low indexes\n\tSDL_GiveFinger(-2, mid, -1, !added);\n}\nstatic int INS_MouseID(SDL_MouseID mid)\n{\n\treturn SDL_GiveFinger(-2, mid, -1, false);\n}\nSDL_AppResult SDLCALL SDL_AppEvent(void *appstate, SDL_Event *event)\n{\n\tint which;\n\tswitch(event->type)\n\t{\n\tcase SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED:\n#if defined(VKQUAKE)\n\t\tif (qrenderer == QR_VULKAN)\n\t\t{\n\t\t\tif (vid.pixelwidth != event->window.data1 || vid.pixelheight != event->window.data2)\n\t\t\t\tvk.neednewswapchain = true;\n\t\t\tbreak;\n\t\t}\n#endif\n\t\tif (qrenderer == QR_OPENGL)\n\t\t{\n\t\t\tvid.pixelwidth = event->window.data1;\n\t\t\tvid.pixelheight = event->window.data2;\n\t\t\t{\n\t\t\t\textern cvar_t vid_conautoscale, vid_conwidth;\t//make sure the screen is updated properly.\n\t\t\t\tCvar_ForceCallback(&vid_conautoscale);\n\t\t\t\tCvar_ForceCallback(&vid_conwidth);\n\t\t\t}\n\t\t}\n\t\tbreak;\n\tcase SDL_EVENT_WINDOW_FOCUS_GAINED:\n\t\tvid.activeapp = true;\n\t\tbreak;\n\tcase SDL_EVENT_WINDOW_FOCUS_LOST:\n\t\tvid.activeapp = false;\n\t\tIN_DeactivateMouse();\n\t\tbreak;\n\tcase SDL_EVENT_WINDOW_CLOSE_REQUESTED:\n\t\tCbuf_AddText(\"quit prompt\\n\", RESTRICT_LOCAL);\n\t\tbreak;\n\n\tcase SDL_EVENT_KEY_UP:\n\tcase SDL_EVENT_KEY_DOWN:\n\t\t{\n\t\t\tint s = event->key.scancode;\n\t\t\tint qs = MySDL_MapScancode(s);\n\n#ifdef HAVE_SDL_TEXTINPUT\n\t\t\tIN_KeyEvent(0, event->key.down, qs, 0);\n#else\n\t\t\tIN_KeyEvent(0, event->key.down, qs, event.key.keysym.unicode);\n#endif\n\t\t}\n\t\tbreak;\n#ifdef HAVE_SDL_TEXTINPUT\n/*\tcase SDL_EVENT_TEXT_EDITING:\n\t\t{\n\t\t\tevent.edit;\n\t\t}\n\t\tbreak;*/\n\tcase SDL_EVENT_TEXT_INPUT:\n\t\t{\n\t\t\tunsigned int uc;\n\t\t\tint err;\n\t\t\tconst char *text = event->text.text;\n\t\t\twhile(*text)\n\t\t\t{\n\t\t\t\tuc = utf8_decode(&err, text, &text);\n\t\t\t\tif (uc && !err)\n\t\t\t\t{\n\t\t\t\t\tIN_KeyEvent(0, true, 0, uc);\n\t\t\t\t\tIN_KeyEvent(0, false, 0, uc);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tbreak;\n#endif\n\n\tcase SDL_EVENT_FINGER_DOWN:\n\tcase SDL_EVENT_FINGER_UP:\n\t\t{\n\t\t\tuint32_t thefinger = SDL_GiveFinger(-1, event->tfinger.touchID, event->tfinger.fingerID, event->type==SDL_EVENT_FINGER_UP);\n\t\t\tIN_MouseMove(thefinger, true, event->tfinger.x * vid.pixelwidth, event->tfinger.y * vid.pixelheight, 0, event->tfinger.pressure);\n\t\t\tIN_KeyEvent(thefinger, event->type==SDL_EVENT_FINGER_DOWN, K_TOUCH, 0);\n\t\t}\n\t\tbreak;\n\tcase SDL_EVENT_FINGER_MOTION:\n\t\t{\n\t\t\tuint32_t thefinger = SDL_GiveFinger(-1, event->tfinger.touchID, event->tfinger.fingerID, false);\n\t\t\tIN_MouseMove(thefinger, true, event->tfinger.x * vid.pixelwidth, event->tfinger.y * vid.pixelheight, 0, event->tfinger.pressure);\n\t\t}\n\t\tbreak;\n\n\tcase SDL_EVENT_DROP_FILE:\n\t\tHost_RunFile(event->drop.data, strlen(event->drop.data), NULL);\n\t\tbreak;\n\n\tcase SDL_EVENT_MOUSE_MOTION:\n\t\tif (event->motion.which == SDL_TOUCH_MOUSEID)\n\t\t\tbreak;\t//ignore legacy touch events.\n\t\twhich = INS_MouseID(event->motion.which);\n\t\tif (!mouseactive)\n\t\t\tIN_MouseMove(which, true, event->motion.x, event->motion.y, 0, 0);\n\t\telse\n\t\t\tIN_MouseMove(which, false, event->motion.xrel, event->motion.yrel, 0, 0);\n\t\tbreak;\n\n\tcase SDL_EVENT_MOUSE_WHEEL:\n\t\tif (event->motion.which == SDL_TOUCH_MOUSEID)\n\t\t\tbreak;\t//ignore legacy touch events.\n\t\twhich = INS_MouseID(event->wheel.which);\n\t\tif (event->wheel.direction == SDL_MOUSEWHEEL_FLIPPED)\n\t\t\tevent->wheel.y *= -1;\n\t\tfor (; event->wheel.y > 0; event->wheel.y--)\n\t\t{\n\t\t\tIN_KeyEvent(which, true, K_MWHEELUP, 0);\n\t\t\tIN_KeyEvent(which, false, K_MWHEELUP, 0);\n\t\t}\n\t\tfor (; event->wheel.y < 0; event->wheel.y++)\n\t\t{\n\t\t\tIN_KeyEvent(which, true, K_MWHEELDOWN, 0);\n\t\t\tIN_KeyEvent(which, false, K_MWHEELDOWN, 0);\n\t\t}\n\t\tfor (; event->wheel.x > 0; event->wheel.x--)\n\t\t{\n\t\t\tIN_KeyEvent(which, true, K_MWHEELRIGHT, 0);\n\t\t\tIN_KeyEvent(which, false, K_MWHEELRIGHT, 0);\n\t\t}\n\t\tfor (; event->wheel.x < 0; event->wheel.x++)\n\t\t{\n\t\t\tIN_KeyEvent(which, true, K_MWHEELLEFT, 0);\n\t\t\tIN_KeyEvent(which, false, K_MWHEELLEFT, 0);\n\t\t}\n\t\tbreak;\n\n\tcase SDL_EVENT_MOUSE_BUTTON_DOWN:\n\tcase SDL_EVENT_MOUSE_BUTTON_UP:\n\t\tif (event->button.which == SDL_TOUCH_MOUSEID)\n\t\t\tbreak;\t//ignore legacy touch events. SDL_FINGER* events above will handle it (for multitouch)\n\t\twhich = INS_MouseID(event->button.which);\n\t\t//Hmm. SDL allows for 255 buttons, but only defines 5...\n\t\tif (event->button.button > sizeof(tbl_sdltoquakemouse)/sizeof(tbl_sdltoquakemouse[0]))\n\t\t\tevent->button.button = sizeof(tbl_sdltoquakemouse)/sizeof(tbl_sdltoquakemouse[0]);\n\t\tIN_KeyEvent(which, event->button.down, tbl_sdltoquakemouse[event->button.button-1], 0);\n\t\tbreak;\n\n\tcase SDL_EVENT_MOUSE_ADDED:\n\t\tINS_MouseAddRem(event->mdevice.which, true);\n\t\tbreak;\n\tcase SDL_EVENT_MOUSE_REMOVED:\n\t\tINS_MouseAddRem(event->mdevice.which, false);\n\t\tbreak;\n\n\tcase SDL_EVENT_TERMINATING:\n\t\tCbuf_AddText(\"quit force\\n\", RESTRICT_LOCAL);\n\t\treturn SDL_APP_SUCCESS;\n\tcase SDL_EVENT_QUIT:\n\t\tCbuf_AddText(\"quit\\n\", RESTRICT_LOCAL);\n\t\treturn SDL_APP_SUCCESS;\n\n\t//actually, joysticks *should* work with sdl1 as well, but there are some differences (like no hot plugging, I think).\n\tcase SDL_EVENT_JOYSTICK_AXIS_MOTION:\n\t\tJ_JoystickAxis(event->jaxis.which, event->jaxis.axis, event->jaxis.value);\n\t\tbreak;\n//\tcase SDL_EVENT_JOYSTICK_BALL_MOTION:\n//\tcase SDL_EVENT_JOYSTICK_HAT_MOTION:\n//\t\tbreak;\n\tcase SDL_EVENT_JOYSTICK_BUTTON_DOWN:\n\tcase SDL_EVENT_JOYSTICK_BUTTON_UP:\n\t\tJ_JoystickButton(event->jbutton.which, event->jbutton.button, event->type==SDL_EVENT_JOYSTICK_BUTTON_DOWN);\n\t\tbreak;\n\tcase SDL_EVENT_JOYSTICK_ADDED:\n\t\tJ_JoystickAdded(event->jdevice.which);\n\t\tbreak;\n\tcase SDL_EVENT_JOYSTICK_REMOVED:\n\t\tJ_Kill(event->jdevice.which, true);\n\t\tbreak;\n\n\tcase SDL_EVENT_GAMEPAD_AXIS_MOTION:\n\t\tJ_ControllerAxis(event->gaxis.which, event->gaxis.axis, event->gaxis.value);\n\t\tbreak;\n\tcase SDL_EVENT_GAMEPAD_BUTTON_DOWN:\n\tcase SDL_EVENT_GAMEPAD_BUTTON_UP:\n\t\tJ_ControllerButton(event->gbutton.which, event->gbutton.button, event->type==SDL_EVENT_GAMEPAD_BUTTON_DOWN);\n\t\tbreak;\n\tcase SDL_EVENT_GAMEPAD_ADDED:\n\t\tJ_ControllerAdded(event->gdevice.which);\n\t\tbreak;\n\tcase SDL_EVENT_GAMEPAD_REMOVED:\n\t\tJ_Kill(event->gdevice.which, true);\n\t\tbreak;\n//\tcase SDL_EVENT_GAMEPAD_REMAPPED:\n//\t\tbreak;\n\tcase SDL_EVENT_GAMEPAD_TOUCHPAD_DOWN:\n\t\tJ_ControllerTouchPad(event->gtouchpad.which, event->gtouchpad.touchpad, event->gtouchpad.finger, 1, event->gtouchpad.x, event->gtouchpad.y, event->gtouchpad.pressure);\n\t\tbreak;\n\tcase SDL_EVENT_GAMEPAD_TOUCHPAD_MOTION:\n\t\tJ_ControllerTouchPad(event->gtouchpad.which, event->gtouchpad.touchpad, event->gtouchpad.finger, 0, event->gtouchpad.x, event->gtouchpad.y, event->gtouchpad.pressure);\n\t\tbreak;\n\tcase SDL_EVENT_GAMEPAD_TOUCHPAD_UP:\n\t\tJ_ControllerTouchPad(event->gtouchpad.which, event->gtouchpad.touchpad, event->gtouchpad.finger, -1, event->gtouchpad.x, event->gtouchpad.y, event->gtouchpad.pressure);\n\t\tbreak;\n\tcase SDL_EVENT_GAMEPAD_SENSOR_UPDATE:\n\t\tJ_ControllerSensor(event->gsensor.which, event->gsensor.sensor, event->gsensor.data);\n\t\tbreak;\n\t}\n\n\treturn SDL_APP_CONTINUE;\n}\n#else\nstatic int INS_MouseID(Uint32 mid)\n{\n\treturn SDL_GiveFinger(-2, mid, -1, false);\n}\n#endif\n\n\nvoid Sys_SendKeyEvents(void)\n{\n\tSDL_Event event;\n\tint axis, j;\n\n#ifdef HAVE_SERVER\n\tif (isDedicated)\n\t{\n\t\tSV_GetConsoleCommands ();\n\t\treturn;\n\t}\n#endif\n\n#ifdef HAVE_SDL_TEXTINPUT\n\t{\n\t\tqboolean osk = !!Key_Dest_Has(kdm_console|kdm_cwindows|kdm_message);\n\t\tif (Key_Dest_Has(kdm_prompt|kdm_menu))\n\t\t{\n\t\t\tj = Menu_WantOSK();\n\t\t\tif (j < 0)\n\t\t\t\tosk |= sys_osk.ival;\n\t\t\telse\n\t\t\t\tosk |= j;\n\t\t}\n\t\telse if (Key_Dest_Has(kdm_game))\n\t\t\tosk |= sys_osk.ival;\n\t\tINS_SetOSK(osk);\n\t}\n#endif\n\n\twhile(SDL_PollEvent(&event))\n\t{\n#if SDL_VERSION_ATLEAST(3, 0, 0)\n\t\tSDL_AppEvent(NULL, &event);\n#else\n\t\tint which;\n\t\tswitch(event.type)\n\t\t{\n#if SDL_VERSION_ATLEAST(2,0,0)\n\t\tcase SDL_WINDOWEVENT:\n\t\t\tswitch(event.window.event)\n\t\t\t{\n\t\t\tdefault:\n\t\t\t\tbreak;\n\t\t\tcase SDL_WINDOWEVENT_SIZE_CHANGED:\n#if SDL_VERSION_ATLEAST(2,0,6) && defined(VKQUAKE)\n\t\t\t\tif (qrenderer == QR_VULKAN)\n\t\t\t\t{\n\t\t\t\t\tunsigned window_width, window_height;\n\t\t\t\t\tSDL_Vulkan_GetDrawableSize(sdlwindow, &window_width, &window_height);\t//get the proper physical size.\n\t\t\t\t\tif (vid.pixelwidth != window_width || vid.pixelheight != window_height)\n\t\t\t\t\t\tvk.neednewswapchain = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n#endif\n\t\t\t\tif (qrenderer == QR_OPENGL)\n\t\t\t\t{\n\t\t\t\t\t#if SDL_VERSION_ATLEAST(2,0,1)\n\t\t\t\t\t\tSDL_GL_GetDrawableSize(sdlwindow, &vid.pixelwidth, &vid.pixelheight);\t//get the proper physical size.\n\t\t\t\t\t#else\n\t\t\t\t\t\tSDL_GetWindowSize(sdlwindow, &vid.pixelwidth, &vid.pixelheight);\n\t\t\t\t\t#endif\n\t\t\t\t\t{\n\t\t\t\t\t\textern cvar_t vid_conautoscale, vid_conwidth;\t//make sure the screen is updated properly.\n\t\t\t\t\t\tCvar_ForceCallback(&vid_conautoscale);\n\t\t\t\t\t\tCvar_ForceCallback(&vid_conwidth);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase SDL_WINDOWEVENT_FOCUS_GAINED:\n\t\t\t\tvid.activeapp = true;\n\t\t\t\tbreak;\n\t\t\tcase SDL_WINDOWEVENT_FOCUS_LOST:\n\t\t\t\tvid.activeapp = false;\n\t\t\t\tbreak;\n\t\t\tcase SDL_WINDOWEVENT_CLOSE:\n\t\t\t\tCbuf_AddText(\"quit prompt\\n\", RESTRICT_LOCAL);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tbreak;\n#else\n\n\t\tcase SDL_ACTIVEEVENT:\n\t\t\tif (event.active.state & SDL_APPINPUTFOCUS)\n\t\t\t{\t//follow keyboard status\n\t\t\t\tvid.activeapp = !!event.active.gain;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase SDL_VIDEORESIZE:\n#ifndef SERVERONLY\n\t\t\tvid.pixelwidth = event.resize.w;\n\t\t\tvid.pixelheight = event.resize.h;\n\t\t\t{\n\t\t\textern cvar_t vid_conautoscale, vid_conwidth;\t//make sure the screen is updated properly.\n\t\t\tCvar_ForceCallback(&vid_conautoscale);\n\t\t\tCvar_ForceCallback(&vid_conwidth);\n\t\t\t}\n#endif\n\t\t\tbreak;\n#endif\n\n\t\tcase SDL_KEYUP:\n\t\tcase SDL_KEYDOWN:\n\t\t\t{\n\t\t\t\tint s = event.key.keysym.sym;\n\t\t\t\tint qs;\n#if SDL_VERSION_ATLEAST(2,0,0)\n\t\t\t\tqs = MySDL_MapKey(s);\n#else\n\t\t\t\tif (s < sizeof(tbl_sdltoquake) / sizeof(tbl_sdltoquake[0]))\n\t\t\t\t\tqs = tbl_sdltoquake[s];\n\t\t\t\telse \n\t\t\t\t\tqs = 0;\n#endif\n\n#ifdef FTE_TARGET_WEB\n\t\t\t\tif (s == 1249)\n\t\t\t\t\tqs = K_SHIFT;\n#endif\n#ifdef HAVE_SDL_TEXTINPUT\n\t\t\t\tIN_KeyEvent(0, event.key.state, qs, 0);\n#else\n\t\t\t\tIN_KeyEvent(0, event.key.state, qs, event.key.keysym.unicode);\n#endif\n\t\t\t}\n\t\t\tbreak;\n#ifdef HAVE_SDL_TEXTINPUT\n\t\t/*case SDL_TEXTEDITING:\n\t\t\t{\n\t\t\t\tevent.edit;\n\t\t\t}\n\t\t\tbreak;*/\n\t\tcase SDL_TEXTINPUT:\n\t\t\t{\n\t\t\t\tunsigned int uc;\n\t\t\t\tint err;\n\t\t\t\tconst char *text = event.text.text;\n\t\t\t\twhile(*text)\n\t\t\t\t{\n\t\t\t\t\tuc = utf8_decode(&err, text, &text);\n\t\t\t\t\tif (uc && !err)\n\t\t\t\t\t{\n\t\t\t\t\t\tIN_KeyEvent(0, true, 0, uc);\n\t\t\t\t\t\tIN_KeyEvent(0, false, 0, uc);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n#endif\n\n#if SDL_VERSION_ATLEAST(2,0,0)\n\t\tcase SDL_FINGERDOWN:\n\t\tcase SDL_FINGERUP:\n\t\t\t{\n\t\t\t\tuint32_t thefinger = SDL_GiveFinger(-1, event.tfinger.touchId, event.tfinger.fingerId, event.type==SDL_FINGERUP);\n\t\t\t\tIN_MouseMove(thefinger, true, event.tfinger.x * vid.pixelwidth, event.tfinger.y * vid.pixelheight, 0, event.tfinger.pressure);\n\t\t\t\tIN_KeyEvent(thefinger, event.type==SDL_FINGERDOWN, K_TOUCH, 0);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase SDL_FINGERMOTION:\n\t\t\t{\n\t\t\t\tuint32_t thefinger = SDL_GiveFinger(-1, event.tfinger.touchId, event.tfinger.fingerId, false);\n\t\t\t\tIN_MouseMove(thefinger, true, event.tfinger.x * vid.pixelwidth, event.tfinger.y * vid.pixelheight, 0, event.tfinger.pressure);\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase SDL_DROPFILE:\n\t\t\tHost_RunFile(event.drop.file, strlen(event.drop.file), NULL);\n\t\t\tSDL_free(event.drop.file);\n\t\t\tbreak;\n#endif\n\n\t\tcase SDL_MOUSEMOTION:\n#if SDL_VERSION_ATLEAST(2,0,0)\n\t\t\tif (event.motion.which == SDL_TOUCH_MOUSEID)\n\t\t\t\tbreak;\t//ignore legacy touch events.\n#endif\n\t\t\twhich = INS_MouseID(event.button.which);\n\t\t\tif (!mouseactive)\n\t\t\t\tIN_MouseMove(which, true, event.motion.x, event.motion.y, 0, 0);\n\t\t\telse\n\t\t\t\tIN_MouseMove(which, false, event.motion.xrel, event.motion.yrel, 0, 0);\n\t\t\tbreak;\n\n#if SDL_VERSION_ATLEAST(2,0,0)\n\t\tcase SDL_MOUSEWHEEL:\n\t\t\tif (event.motion.which == SDL_TOUCH_MOUSEID)\n\t\t\t\tbreak;\t//ignore legacy touch events.\n\t\t\twhich = INS_MouseID(event.button.which);\n\t\t\tif (event.wheel.direction == SDL_MOUSEWHEEL_FLIPPED)\n\t\t\t\tevent.wheel.y *= -1;\n\t\t\tfor (; event.wheel.y > 0; event.wheel.y--)\n\t\t\t{\n\t\t\t\tIN_KeyEvent(which, true, K_MWHEELUP, 0);\n\t\t\t\tIN_KeyEvent(which, false, K_MWHEELUP, 0);\n\t\t\t}\n\t\t\tfor (; event.wheel.y < 0; event.wheel.y++)\n\t\t\t{\n\t\t\t\tIN_KeyEvent(which, true, K_MWHEELDOWN, 0);\n\t\t\t\tIN_KeyEvent(which, false, K_MWHEELDOWN, 0);\n\t\t\t}\n/*\t\t\tfor (; event.wheel.x > 0; event.wheel.x--)\n\t\t\t{\n\t\t\t\tIN_KeyEvent(which, true, K_MWHEELRIGHT, 0);\n\t\t\t\tIN_KeyEvent(which, false, K_MWHEELRIGHT, 0);\n\t\t\t}\n\t\t\tfor (; event.wheel.x < 0; event.wheel.x++)\n\t\t\t{\n\t\t\t\tIN_KeyEvent(which, true, K_MWHEELLEFT, 0);\n\t\t\t\tIN_KeyEvent(which, false, K_MWHEELLEFT, 0);\n\t\t\t}*/\n\t\t\tbreak;\n#endif\n\n\t\tcase SDL_MOUSEBUTTONDOWN:\n\t\tcase SDL_MOUSEBUTTONUP:\n#if SDL_VERSION_ATLEAST(2,0,0)\n\t\t\tif (event.button.which == SDL_TOUCH_MOUSEID)\n\t\t\t\tbreak;\t//ignore legacy touch events. SDL_FINGER* events above will handle it (for multitouch)\n#endif\n\t\t\twhich = INS_MouseID(event.button.which);\n\t\t\t//Hmm. SDL allows for 255 buttons, but only defines 5...\n\t\t\tif (event.button.button > sizeof(tbl_sdltoquakemouse)/sizeof(tbl_sdltoquakemouse[0]))\n\t\t\t\tevent.button.button = sizeof(tbl_sdltoquakemouse)/sizeof(tbl_sdltoquakemouse[0]);\n\t\t\tIN_KeyEvent(which, event.button.state, tbl_sdltoquakemouse[event.button.button-1], 0);\n\t\t\tbreak;\n\n#if SDL_VERSION_ATLEAST(2,0,0)\n\t\tcase SDL_APP_TERMINATING:\n\t\t\tCbuf_AddText(\"quit force\\n\", RESTRICT_LOCAL);\n\t\t\tbreak;\n#endif\n\t\tcase SDL_QUIT:\n\t\t\tCbuf_AddText(\"quit\\n\", RESTRICT_LOCAL);\n\t\t\tbreak;\n\n#if SDL_VERSION_ATLEAST(2,0,0)\n\t\t//actually, joysticks *should* work with sdl1 as well, but there are some differences (like no hot plugging, I think).\n\t\tcase SDL_JOYAXISMOTION:\n\t\t\tJ_JoystickAxis(event.jaxis.which, event.jaxis.axis, event.jaxis.value);\n\t\t\tbreak;\n//\t\tcase SDL_JOYBALLMOTION:\n//\t\tcase SDL_JOYHATMOTION:\n//\t\t\tbreak;\n\t\tcase SDL_JOYBUTTONDOWN:\n\t\tcase SDL_JOYBUTTONUP:\n\t\t\tJ_JoystickButton(event.jbutton.which, event.jbutton.button, event.type==SDL_JOYBUTTONDOWN);\n\t\t\tbreak;\n\t\tcase SDL_JOYDEVICEADDED:\n\t\t\tJ_JoystickAdded(event.jdevice.which);\n\t\t\tbreak;\n\t\tcase SDL_JOYDEVICEREMOVED:\n\t\t\tJ_Kill(event.jdevice.which, true);\n\t\t\tbreak;\n\n\t\tcase SDL_CONTROLLERAXISMOTION:\n\t\t\tJ_ControllerAxis(event.caxis.which, event.caxis.axis, event.caxis.value);\n\t\t\tbreak;\n\t\tcase SDL_CONTROLLERBUTTONDOWN:\n\t\tcase SDL_CONTROLLERBUTTONUP:\n\t\t\tJ_ControllerButton(event.cbutton.which, event.cbutton.button, event.type==SDL_CONTROLLERBUTTONDOWN);\n\t\t\tbreak;\n\t\tcase SDL_CONTROLLERDEVICEADDED:\n\t\t\tJ_ControllerAdded(event.cdevice.which);\n\t\t\tbreak;\n\t\tcase SDL_CONTROLLERDEVICEREMOVED:\n\t\t\tJ_Kill(event.cdevice.which, true);\n\t\t\tbreak;\n//\t\tcase SDL_CONTROLLERDEVICEREMAPPED:\n//\t\t\tbreak;\n#if SDL_VERSION_ATLEAST(2,0,14)\n\t\tcase SDL_CONTROLLERTOUCHPADDOWN:\n\t\t\tJ_ControllerTouchPad(event.cdevice.which, event.ctouchpad.touchpad, event.ctouchpad.finger, 1, event.ctouchpad.x, event.ctouchpad.y, event.ctouchpad.pressure);\n\t\t\tbreak;\n\t\tcase SDL_CONTROLLERTOUCHPADMOTION:\n\t\t\tJ_ControllerTouchPad(event.cdevice.which, event.ctouchpad.touchpad, event.ctouchpad.finger, 0, event.ctouchpad.x, event.ctouchpad.y, event.ctouchpad.pressure);\n\t\t\tbreak;\n\t\tcase SDL_CONTROLLERTOUCHPADUP:\n\t\t\tJ_ControllerTouchPad(event.cdevice.which, event.ctouchpad.touchpad, event.ctouchpad.finger, -1, event.ctouchpad.x, event.ctouchpad.y, event.ctouchpad.pressure);\n\t\t\tbreak;\n\t\tcase SDL_CONTROLLERSENSORUPDATE:\n\t\t\tJ_ControllerSensor(event.csensor.which, event.csensor.sensor, event.csensor.data);\n\t\t\tbreak;\n#endif\n#endif\n\t\t}\n#endif\n\t}\n\n\tfor (j = 0; j < MAX_JOYSTICKS; j++)\n\t{\n\t\tstruct sdljoy_s *joy = &sdljoy[j];\n\t\tif (joy->qdevid == DEVID_UNSET || !joy->controller)\n\t\t\tcontinue;\n\n\t\tif (joy->buttonheld)\n\t\t{\n\t\t\tfor (axis = 0; axis < countof(gpbuttonmap); axis++)\n\t\t\t{\n\t\t\t\tif (joy->buttonheld & (1u<<axis))\n\t\t\t\t{\n\t\t\t\t\tjoy->buttonrepeat[axis] -= host_frametime;\n\t\t\t\t\tif (joy->buttonrepeat[axis] < 0)\n\t\t\t\t\t{\n\t\t\t\t\t\tIN_KeyEvent(joy->qdevid, true, gpbuttonmap[axis], 0);\n\t\t\t\t\t\tjoy->buttonrepeat[axis] = 0.25;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tfor (axis = 0; axis < countof(gpaxismap); axis++)\n\t\t{\n\t\t\tif ((joy->axistobuttonp | joy->axistobuttonn) & (1u<<axis))\n\t\t\t{\n\t\t\t\tjoy->repeattime[axis] -= host_frametime;\n\t\t\t\tif (joy->repeattime[axis] < 0)\n\t\t\t\t{\n\t\t\t\t\tif (joy->axistobuttonp & (1u<<axis))\n\t\t\t\t\t{\n\t\t\t\t\t\tIN_KeyEvent(joy->qdevid, true, gpaxismap[axis].pos, 0);\n\t\t\t\t\t\tjoy->repeattime[axis] = 0.25;\n\t\t\t\t\t}\n\t\t\t\t\tif (joy->axistobuttonn & (1u<<axis))\n\t\t\t\t\t{\n\t\t\t\t\t\tIN_KeyEvent(joy->qdevid, true, gpaxismap[axis].neg, 0);\n\t\t\t\t\t\tjoy->repeattime[axis] = 0.25;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n\n\n\n\n\nvoid INS_Shutdown (void)\n{\n\tIN_DeactivateMouse();\n\n#if SDL_VERSION_ATLEAST(2, 0, 0)\n\tJ_KillAll();\n\t#if SDL_VERSION_ATLEAST(3, 0, 0)\n\t\tSDL_QuitSubSystem(SDL_INIT_JOYSTICK|SDL_INIT_GAMEPAD);\n\t#else\n\t\tSDL_QuitSubSystem(SDL_INIT_JOYSTICK|SDL_INIT_GAMECONTROLLER);\n\t#endif\n#endif\n}\n\nvoid INS_ReInit (void)\n{\n#if SDL_VERSION_ATLEAST(2, 0, 0)\n\tunsigned int i;\n\tmemset(sdljoy, 0, sizeof(sdljoy));\n\tfor (i = 0; i < MAX_JOYSTICKS; i++)\n\t\tsdljoy[i].qdevid = DEVID_UNSET;\n\t#if SDL_VERSION_ATLEAST(3, 0, 0)\n\t\tSDL_InitSubSystem(SDL_INIT_JOYSTICK|SDL_INIT_GAMEPAD);\n\t#else\n\t\tSDL_InitSubSystem(SDL_INIT_JOYSTICK|SDL_INIT_GAMECONTROLLER);\n\t#endif\n#endif\n\n\tIN_ActivateMouse();\n\n#ifndef HAVE_SDL_TEXTINPUT\n\tSDL_EnableUNICODE(SDL_ENABLE);\n#endif\n#if SDL_VERSION_ATLEAST(3, 0, 0)\n\tSDL_SetEventEnabled(SDL_EVENT_DROP_FILE, true);\n#elif SDL_VERSION_ATLEAST(2, 0, 0)\n\tSDL_EventState(SDL_DROPFILE, SDL_ENABLE);\n#endif\n}\n\n//stubs, all the work is done in Sys_SendKeyEvents\nvoid INS_Move(void)\n{\n}\nvoid INS_Init (void)\n{\n#ifdef HAVE_SDL_TEXTINPUT\n\tCvar_Register(&sys_osk, \"input controls\");\n#endif\n\tCvar_Register (&joy_only, \"input controls\");\n\n#ifdef HAVE_SDL_TEXTINPUT\n#if SDL_VERSION_ATLEAST(3, 0, 0)\n#elif defined(__linux__)\n\tusesteamosk = SDL_GetHintBoolean(\"SteamDeck\", false);\t//looks like there's a 'SteamDeck=1' environment setting on the deck (when started via steam itself, at least), so we don't get valve's buggy-as-poop osk on windows etc.\n#endif\n#endif\n}\nvoid INS_Accumulate(void)\t//input polling\n{\n}\nvoid INS_Commands (void)\t//used to Cbuf_AddText joystick button events in windows.\n{\n}\nvoid INS_EnumerateDevices(void *ctx, void(*callback)(void *ctx, const char *type, const char *devicename, unsigned int *qdevid))\n{\n#if SDL_VERSION_ATLEAST(2, 0, 0)\n\tunsigned int i;\n\tfor (i = 0; i < MAX_JOYSTICKS; i++)\n\t\tif (sdljoy[i].controller || sdljoy[i].joystick)\n\t\t\tcallback(ctx, \"joy\", sdljoy[i].devname, &sdljoy[i].qdevid);\n\n#if SDL_VERSION_ATLEAST(3, 0, 0)\n\t//pointer devices...\n\tfor (i = 0; i < MAX_FINGERS; i++)\n\t{\n\t\tunsigned int d = i;\n\t\tif (!sdlfinger[i].active)\n\t\t\tcontinue;\n\t\telse if (sdlfinger[i].jid == -2)\n\t\t\tcallback(ctx, \"mouse\", SDL_GetMouseNameForID(sdlfinger[i].tid), &d);\n\t\telse if (sdlfinger[i].jid == -1)\n\t\t\tcallback(ctx, \"finger\", SDL_GetTouchDeviceName(sdlfinger[i].tid), &d);\n\t\telse\n\t\t\tcallback(ctx, \"joypad\", SDL_GetGamepadNameForID(sdlfinger[i].jid), &d);\n\t}\n#endif\n\n\t//sdl3 allows tracking keyboards. too lazy to deal with that.\n#endif\n}\n\n\n"
  },
  {
    "path": "engine/client/in_win.c",
    "content": "/*\nCopyright (C) 1996-1997 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n*/\n// in_win.c -- windows 95 mouse and joystick code\n// 02/21/97 JCB Added extended DirectInput code to support external controllers.\n\n#include \"quakedef.h\"\n#include \"winquake.h\"\n//#include \"dosisms.h\"\n#ifndef WINRT\n#define USINGRAWINPUT\n\n#ifdef USINGRAWINPUT\n#include \"in_raw.h\"\n#endif\n\nvoid INS_Accumulate (void);\n\n#define AVAIL_XINPUT\n#ifdef AVAIL_XINPUT\ntypedef struct _XINPUT_GAMEPAD {\n\tWORD  wButtons;\n\tBYTE  bLeftTrigger;\n\tBYTE  bRightTrigger;\n\tSHORT sThumbLX;\n\tSHORT sThumbLY;\n\tSHORT sThumbRX;\n\tSHORT sThumbRY;\n} XINPUT_GAMEPAD, *PXINPUT_GAMEPAD;\ntypedef struct _XINPUT_STATE {\n\tDWORD          dwPacketNumber;\n\tXINPUT_GAMEPAD Gamepad;\n} XINPUT_STATE, *PXINPUT_STATE;\ntypedef struct _XINPUT_VIBRATION {\n  WORD wLeftMotorSpeed;\n  WORD wRightMotorSpeed;\n} XINPUT_VIBRATION, *PXINPUT_VIBRATION;\nDWORD (WINAPI *pXInputGetState)(DWORD dwUserIndex, XINPUT_STATE *pState);\nDWORD (WINAPI *pXInputSetState)(DWORD dwUserIndex, XINPUT_VIBRATION *pState);\nDWORD (WINAPI *pXInputGetDSoundAudioDeviceGuids)(DWORD dwUserIndex, GUID* pDSoundRenderGuid, GUID* pDSoundCaptureGuid);\t//xi 1.3\nDWORD (WINAPI *pXInputGetAudioDeviceIds)(DWORD dwUserIndex, LPWSTR pRenderDeviceId, UINT *pRenderCount, LPWSTR pCaptureDeviceId, UINT *pCaptureCount); //xi 1.4\n\nenum\n{\n\tXINPUT_GAMEPAD_DPAD_UP\t\t\t= 0x0001,\n\tXINPUT_GAMEPAD_DPAD_DOWN\t\t= 0x0002,\n\tXINPUT_GAMEPAD_DPAD_LEFT\t\t= 0x0004,\n\tXINPUT_GAMEPAD_DPAD_RIGHT\t\t= 0x0008,\n\tXINPUT_GAMEPAD_START\t\t\t= 0x0010,\n\tXINPUT_GAMEPAD_BACK\t\t\t\t= 0x0020,\n\tXINPUT_GAMEPAD_LEFT_THUMB\t\t= 0x0040,\n\tXINPUT_GAMEPAD_RIGHT_THUMB\t\t= 0x0080,\n\tXINPUT_GAMEPAD_LEFT_SHOULDER\t= 0x0100,\n\tXINPUT_GAMEPAD_RIGHT_SHOULDER\t= 0x0200,\n\tXINPUT_GAMEPAD_A\t\t\t\t= 0x1000,\n\tXINPUT_GAMEPAD_B\t\t\t\t= 0x2000,\n\tXINPUT_GAMEPAD_X\t\t\t\t= 0x4000,\n\tXINPUT_GAMEPAD_Y\t\t\t\t= 0x8000\n};\nstatic qboolean xinput_useaudio;\n#endif\n\n\n#ifdef AVAIL_DINPUT\n\n#ifndef DIRECTINPUT_VERSION\n#define DIRECTINPUT_VERSION 0x0700\n#endif\n\n#include <dinput.h>\n\n#define DINPUT_BUFFERSIZE           16\n#define iDirectInputCreate(a,b,c,d)\tpDirectInputCreate(a,b,c,d)\n\nHRESULT (WINAPI *pDirectInputCreate)(HINSTANCE hinst, DWORD dwVersion,\n\tLPDIRECTINPUT * lplpDirectInput, LPUNKNOWN punkOuter);\n\n#endif\n\n#define DINPUT_VERSION_DX3 0x0300\n#define DINPUT_VERSION_DX7 0x0700\n\n// mouse variables\nstatic cvar_t\tin_dinput = CVARFD(\"in_dinput\",\"0\", CVAR_ARCHIVE, \"Enables the use of dinput for mouse movements\");\nstatic cvar_t\tin_xinput = CVARFD(\"in_xinput\",\"1\", CVAR_ARCHIVE, \"Enables the use of xinput for controllers.\\nNote that if you have a headset plugged in, that headset will be used for audio playback if no specific audio device is configured.\");\nstatic cvar_t\tin_builtinkeymap = CVARF(\"in_builtinkeymap\", \"0\", CVAR_ARCHIVE);\nstatic cvar_t in_simulatemultitouch = CVAR(\"in_simulatemultitouch\", \"0\");\nstatic cvar_t\tin_nonstandarddeadkeys = CVARD(\"in_nonstandarddeadkeys\", \"1\", \"Discard input events that result in multiple keys. Only the last key will be used. This results in behaviour that differs from eg notepad. To use a dead key, press it twice instead of the dead key followed by space.\");\n\nstatic cvar_t\txinput_leftvibrator = CVARF(\"xinput_leftvibrator\",\"0\", CVAR_ARCHIVE);\nstatic cvar_t\txinput_rightvibrator = CVARF(\"xinput_rightvibrator\",\"0\", CVAR_ARCHIVE);\n\nstatic cvar_t\tm_accel_noforce = CVAR(\"m_accel_noforce\", \"0\");\nstatic cvar_t  m_threshold_noforce = CVAR(\"m_threshold_noforce\", \"0\");\n\nstatic cvar_t\tcl_keypad = CVAR(\"cl_keypad\", \"1\");\n\nextern float multicursor_x[8], multicursor_y[8];\nextern qboolean multicursor_active[8];\n\nPOINT\t\tcurrent_mouse_pos;\n\n\nHWND mainwindow;\nint window_center_x, window_center_y;\nRECT\t\twindow_rect;\n\n\ntypedef struct {\n\tunion {\n\t\tHANDLE rawinputhandle;\n\t} handles;\n\tchar sysname[MAX_OSPATH];\n\n\tint qdeviceid;\n} keyboard_t;\n\ntypedef struct {\n\tunion {\n\t\tHANDLE rawinputhandle; // raw input\n\t} handles;\n\tchar sysname[MAX_OSPATH];\n\n\tint numbuttons;\n\tint oldbuttons;\n\tint qdeviceid;\t/*the device id controls which player slot it controls, if splitscreen splits it that way*/\n} mouse_t;\n\nstatic mouse_t sysmouse;\n\nstatic qboolean\trestore_spi;\nstatic int\t\toriginalmouseparms[3], newmouseparms[3] = {0, 0, 0};\nqboolean\t\tmouseinitialized;\nstatic qboolean\tmouseparmsvalid, mouseactivatetoggle;\nqboolean\tmouseshowtoggle = 1;\nunsigned int uiWheelMessage;\n\nqboolean\tmouseactive;\n\n// joystick defines and variables\n// where should defines be moved?\n#define JOY_ABSOLUTE_AXIS\t0x00000000\t\t// control like a joystick\n#define JOY_RELATIVE_AXIS\t0x00000010\t\t// control like a mouse, spinner, trackball\n#define\tJOY_MAX_AXES\t\t6\t\t\t\t// X, Y, Z, R, U, V\n\n\n// none of these cvars are saved over a session\n// this means that advanced controller configuration needs to be executed\n// each time.  this avoids any problems with getting back to a default usage\n// or when changing from one controller to another.  this way at least something\n// works.\nstatic cvar_t\tin_joystick\t\t\t\t= CVARAF(\"joystick\",\"0\", \"in_joystick\", CVAR_ARCHIVE);\nstatic qboolean\tjoy_advancedinit;\n\nstatic DWORD\t\tjoy_flags = JOY_RETURNALL|JOY_RETURNCENTERED;\n#define MAX_JOYSTICKS 8\nstatic struct wjoy_s {\n\tqboolean\t\t\tisxinput;\t//xinput device\n\tenum\n\t{\n\t\tDS_UNKNOWN,\n\t\tDS_PRESENT,\n\t\tDS_NOTPRESENT\n\t} devstate;\n\tunsigned int\t\tid;\t\t//windows id. device id is the index.\n\tunsigned int\t\tdevid;\t//quake id (generally player index)\n\tDWORD\tnumbuttons;\n\tqboolean haspov;\n\tDWORD\tpovstate;\n\tDWORD\toldpovstate;\n\tDWORD\tbuttonstate;\n\tDWORD\toldbuttonstate;\n\tsoundcardinfo_t *audiodev;\n} wjoy[MAX_JOYSTICKS];\nstatic int joy_count;\n\n#ifdef AVAIL_DINPUT\nstatic const GUID fGUID_XAxis\t= {0xA36D02E0, 0xC9F3, 0x11CF, {0xBF, 0xC7, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}};\nstatic const GUID fGUID_YAxis\t= {0xA36D02E1, 0xC9F3, 0x11CF, {0xBF, 0xC7, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}};\nstatic const GUID fGUID_ZAxis\t= {0xA36D02E2, 0xC9F3, 0x11CF, {0xBF, 0xC7, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}};\nstatic const GUID fGUID_SysMouse\t= {0x6F1D2B60, 0xD5A0, 0x11CF, {0xBF, 0xC7, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}};\n\nstatic const GUID fIID_IDirectInputDevice7A\t= {0x57d7c6bc, 0x2356, 0x11d3, {0x8e, 0x9d, 0x00, 0xC0, 0x4f, 0x68, 0x44, 0xae}};\nstatic const GUID fIID_IDirectInput7A\t\t= {0x9a4cb684, 0x236d, 0x11d3, {0x8e, 0x9d, 0x00, 0xc0, 0x4f, 0x68, 0x44, 0xae}};\n\n// devices\nstatic LPDIRECTINPUT\t\tg_pdi;\nstatic LPDIRECTINPUTDEVICE\tg_pMouse;\nstatic qboolean\tdinput_acquired;\n\nstatic HINSTANCE hInstDI;\n\n// current DirectInput version in use, 0 means using no DirectInput\nstatic int dinput;\n\ntypedef struct MYDATA {\n\tLONG  lX;                   // X axis goes here\n\tLONG  lY;                   // Y axis goes here\n\tLONG  lZ;                   // Z axis goes here\n\tBYTE  bButtonA;             // One button goes here\n\tBYTE  bButtonB;             // Another button goes here\n\tBYTE  bButtonC;             // Another button goes here\n\tBYTE  bButtonD;             // Another button goes here\n#if (DIRECTINPUT_VERSION >= DINPUT_VERSION_DX7)\n\tBYTE  bButtonE;             // DX7 buttons\n\tBYTE  bButtonF;\n\tBYTE  bButtonG;\n\tBYTE  bButtonH;\n#endif\n} MYDATA;\n\nstatic DIOBJECTDATAFORMAT rgodf[] = {\n  { &fGUID_XAxis,    FIELD_OFFSET(MYDATA, lX),       DIDFT_AXIS | DIDFT_ANYINSTANCE,   0,},\n  { &fGUID_YAxis,    FIELD_OFFSET(MYDATA, lY),       DIDFT_AXIS | DIDFT_ANYINSTANCE,   0,},\n  { &fGUID_ZAxis,    FIELD_OFFSET(MYDATA, lZ),       0x80000000 | DIDFT_AXIS | DIDFT_ANYINSTANCE,   0,},\n  { 0,              FIELD_OFFSET(MYDATA, bButtonA), DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0,},\n  { 0,              FIELD_OFFSET(MYDATA, bButtonB), DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0,},\n  { 0,              FIELD_OFFSET(MYDATA, bButtonC), 0x80000000 | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0,},\n  { 0,              FIELD_OFFSET(MYDATA, bButtonD), 0x80000000 | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0,},\n#if (DIRECTINPUT_VERSION >= DINPUT_VERSION_DX7)\n  { 0,              FIELD_OFFSET(MYDATA, bButtonE), 0x80000000 | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0,},\n  { 0,              FIELD_OFFSET(MYDATA, bButtonF), 0x80000000 | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0,},\n  { 0,              FIELD_OFFSET(MYDATA, bButtonG), 0x80000000 | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0,},\n  { 0,              FIELD_OFFSET(MYDATA, bButtonH), 0x80000000 | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0,},\n#endif\n};\n\n#define NUM_OBJECTS (sizeof(rgodf) / sizeof(rgodf[0]))\n\nstatic DIDATAFORMAT\tdf = {\n\tsizeof(DIDATAFORMAT),       // this structure\n\tsizeof(DIOBJECTDATAFORMAT), // size of object data format\n\tDIDF_RELAXIS,               // absolute axis coordinates\n\tsizeof(MYDATA),             // device data size\n\tNUM_OBJECTS,                // number of objects\n\trgodf,                      // and here they are\n};\n\n#if (DIRECTINPUT_VERSION >= DINPUT_VERSION_DX7)\n// DX7 devices\nstatic LPDIRECTINPUT7\t\tg_pdi7;\nstatic LPDIRECTINPUTDEVICE7\tg_pMouse7;\n\n// DX7 specific calls\n#define iDirectInputCreateEx(a,b,c,d,e)\tpDirectInputCreateEx(a,b,c,d,e)\n\nstatic HRESULT (WINAPI *pDirectInputCreateEx)(HINSTANCE hinst,\n\t\tDWORD dwVersion, REFIID riidltf, LPVOID *ppvOut, LPUNKNOWN punkOuter);\n#endif\n\n#else\n#define dinput 0\n#endif\n\nstatic JOYINFOEX\tji;\n\n// raw input specific defines\n#ifdef USINGRAWINPUT\n// defines\n#define MAX_RI_DEVICE_SIZE 128\n#define INIT_RIBUFFER_SIZE (sizeof(RAWINPUTHEADER)+sizeof(RAWMOUSE))\n\n#define RI_RAWBUTTON_MASK 0x000003E0\n#define RI_INVALID_POS    0x80000000\n\n// raw input dynamic functions\ntypedef INT (WINAPI *pGetRawInputDeviceList)(OUT PRAWINPUTDEVICELIST pRawInputDeviceList, IN OUT PINT puiNumDevices, IN UINT cbSize);\ntypedef INT(WINAPI *pGetRawInputData)(IN HRAWINPUT hRawInput, IN UINT uiCommand, OUT LPVOID pData, IN OUT PINT pcbSize, IN UINT cbSizeHeader);\ntypedef INT(WINAPI *pGetRawInputDeviceInfoA)(IN HANDLE hDevice, IN UINT uiCommand, OUT LPVOID pData, IN OUT PINT pcbSize);\ntypedef BOOL (WINAPI *pRegisterRawInputDevices)(IN PCRAWINPUTDEVICE pRawInputDevices, IN UINT uiNumDevices, IN UINT cbSize);\n\nstatic pGetRawInputDeviceList _GRIDL;\nstatic pGetRawInputData _GRID;\nstatic pGetRawInputDeviceInfoA _GRIDIA;\nstatic pRegisterRawInputDevices _RRID;\n\nstatic keyboard_t *rawkbd;\nstatic mouse_t *rawmice;\nstatic int rawmicecount;\nstatic int rawkbdcount;\nstatic RAWINPUT *raw;\nstatic int ribuffersize;\n\nstatic cvar_t in_rawinput_mice = CVARD(\"in_rawinput\", \"0\", \"Enables rawinput support for mice in XP onwards. Rawinput permits independant device identification (ie: splitscreen clients can each have their own mouse)\");\nstatic cvar_t in_rawinput_keyboard = CVARD(\"in_rawinput_keyboard\", \"0\", \"Enables rawinput support for keyboards in XP onwards as well as just mice.\");\nstatic cvar_t in_rawinput_rdp = CVARD(\"in_rawinput_rdp\", \"0\", \"Activate Remote Desktop Protocol devices too.\");\n\nvoid INS_RawInput_MouseDeRegister(void);\nint INS_RawInput_MouseRegister(void);\nvoid INS_RawInput_KeyboardDeRegister(void);\nint INS_RawInput_KeyboardRegister(void);\nvoid INS_RawInput_DeInit(void);\n\n#endif\n\n#ifdef AVAIL_XINPUT\nstatic const int xinputjbuttons[] =\n{\t//xinput->fte button mapping table\n\tK_GP_DPAD_UP,\n\tK_GP_DPAD_DOWN,\n\tK_GP_DPAD_LEFT,\n\tK_GP_DPAD_RIGHT,\n\tK_GP_START,\n\tK_GP_BACK,\n\tK_GP_LEFT_STICK,\n\tK_GP_RIGHT_STICK,\n\n\tK_GP_LEFT_SHOULDER,\n\tK_GP_RIGHT_SHOULDER,\n\tK_GP_GUIDE,\t\t//officially, this is 'reserved'\n\tK_GP_UNKNOWN,\t//reserved\n\tK_GP_A,\n\tK_GP_B,\n\tK_GP_X,\n\tK_GP_Y,\n\n\t//not part of xinput specs, but used to treat the various axis as buttons.\n\tK_GP_LEFT_THUMB_UP,\n\tK_GP_LEFT_THUMB_DOWN,\n\tK_GP_LEFT_THUMB_LEFT,\n\tK_GP_LEFT_THUMB_RIGHT,\n\tK_GP_RIGHT_THUMB_UP,\n\tK_GP_RIGHT_THUMB_DOWN,\n\tK_GP_RIGHT_THUMB_LEFT,\n\tK_GP_RIGHT_THUMB_RIGHT,\n\n\tK_GP_LEFT_TRIGGER,\n\tK_GP_RIGHT_TRIGGER,\n};\n#endif\nstatic const int mmjbuttons[32] =\n{\t//winmm->fte joystick button mapping table\n\tK_JOY1,\n\tK_JOY2,\n\tK_JOY3,\n\tK_JOY4,\n\t//yes, aux1-4 skipped for compat with other quake engines.\n\tK_JOY9,\n\tK_JOY10,\n\tK_JOY11,\n\tK_JOY12,\n\tK_JOY13,\n\tK_JOY14,\n\tK_JOY15,\n\tK_JOY16,\n\tK_JOY17,\n\tK_JOY18,\n\tK_JOY19,\n\tK_JOY20,\n\tK_JOY21,\n\tK_JOY22,\n\tK_JOY23,\n\tK_JOY24,\n\tK_JOY25,\n\tK_JOY26,\n\tK_JOY27,\n\tK_JOY28,\n\tK_JOY29,\n\tK_JOY30,\n\tK_JOY31,\n\tK_JOY32,\n\t//29-32 used for the pov stuff, so lets switch back to aux1-4 to avoid wastage\n\tK_JOY5,\n\tK_JOY6,\n\tK_JOY7,\n\tK_JOY8\n};\n\n\nstatic HANDLE\tpowercontext = INVALID_HANDLE_VALUE;\nstatic qboolean\tpowersaveblocked = false;\nstatic HANDLE\t(WINAPI *pPowerCreateRequest)\t(REASON_CONTEXT *Context);\nstatic BOOL\t\t(WINAPI *pPowerSetRequest)\t\t(HANDLE Context,\tPOWER_REQUEST_TYPE RequestType);\nstatic BOOL\t\t(WINAPI *pPowerClearRequest)\t(HANDLE Context,\tPOWER_REQUEST_TYPE RequestType);\nstatic void INS_ScreenSaver_Init(void)\n{\n\tdllfunction_t functable[] =\n\t{\n\t\t{(void*)&pPowerCreateRequest,\t\"PowerCreateRequest\"},\n\t\t{(void*)&pPowerSetRequest,\t\t\"PowerSetRequest\"},\n\t\t{(void*)&pPowerClearRequest,\t\"PowerClearRequest\"},\n\t\t{NULL}\n\t};\n\n\tif (Sys_LoadLibrary(\"kernel32.dll\", functable))\n\t{\t//we don't do microsoft's interpretation of localisation, so this is lame.\n\t\tREASON_CONTEXT reason = {POWER_REQUEST_CONTEXT_VERSION, POWER_REQUEST_CONTEXT_SIMPLE_STRING};\n\t\treason.Reason.SimpleReasonString = L\"Demo Playback\";\n\t\tpowercontext = pPowerCreateRequest(&reason);\n\n\t\t//FIXME: be prepared to regenerate the reason when our lang changes...\n\t}\n}\nstatic void INS_ScreenSaver_UpdateBlock(qboolean block)\n{\n\tif (powercontext != INVALID_HANDLE_VALUE && block != powersaveblocked)\n\t{\n\t\tpowersaveblocked = block;\n\t\tif (block)\n\t\t{\n\t\t\tpPowerSetRequest(powercontext, PowerRequestDisplayRequired);\t//keep the screen on...\n\t\t\tpPowerSetRequest(powercontext, PowerRequestSystemRequired);\t\t//and don't go to sleep mid-video, too...\n\t\t}\n\t\telse\n\t\t{\n\t\t\tpPowerClearRequest(powercontext, PowerRequestSystemRequired);\n\t\t\tpPowerClearRequest(powercontext, PowerRequestDisplayRequired);\n\t\t}\n\t}\n}\n\n\n\n// forward-referenced functions\nvoid INS_StartupJoystick (void);\nvoid INS_JoyMove (void);\n\n/*\n===========\nForce_CenterView_f\n===========\n*/\nvoid Force_CenterView_f (void)\n{\n\tcl.playerview[0].viewangles[PITCH] = 0;\n}\n\n/*\n===========\nINS_UpdateClipCursor\n===========\n*/\nvoid INS_UpdateClipCursor (void)\n{\n\tif (mouseinitialized && mouseactive && !dinput)\n\t{\n\t\tClipCursor (&window_rect);\n\t}\n}\n\n\n/*\n===========\nINS_ShowMouse\n===========\n*/\nstatic void INS_ShowMouse (void)\n{\n\tif (!mouseshowtoggle)\n\t{\t//FIXME: we should be sending this via the window thread.\n\t\tShowCursor (TRUE);\n\t\tmouseshowtoggle = 1;\n\t}\n}\n\n\n/*\n===========\nINS_HideMouse\n===========\n*/\nstatic void INS_HideMouse (void)\n{\n\tif (mouseshowtoggle)\n\t{\t//I'm told that nvidia's null-named 'Geforce Experience' (which is malware in my book) fucks with cursor visibility.\n\t\t//So lets try to ensure that it gets hidden properly in case we've got race conditions and code getting injected into our process.\n\t\t//in modern windows, this is per-thread, so blame code injection if this ever prints.\n\t\t//FIXME: we should be sending this via the window thread.\n\t\twhile (ShowCursor (FALSE) >= 0)\n\t\t\tCon_Printf(CON_WARNING \"Force-hiding mouse cursor...\\n\");\n\t\tmouseshowtoggle = 0;\n\t}\n}\n\n//scan for an unused device id for joysticks (now that something was pressed).\nstatic int Joy_AllocateDevID(void)\n{\n\textern cvar_t cl_splitscreen;\n\textern cvar_t in_skipplayerone;\n\tint id = (in_skipplayerone.ival?1:0), j;\n\tfor ( ; ; id++)\n\t{\n\t\tfor (j = 0; j < joy_count; j++)\n\t\t{\n\t\t\tif (wjoy[j].devid == id)\n\t\t\t\tbreak;\n\t\t}\n\t\tif (j == joy_count)\n\t\t{\n\t\t\tif (id > cl_splitscreen.ival && !*cl_splitscreen.string)\n\t\t\t\tcl_splitscreen.ival = id;\n\t\t\treturn id;\n\t\t}\n\t}\n}\n\nenum controllertype_e INS_GetControllerType(int id)\n{\n\tint j;\n\tfor (j = 0; j < joy_count; j++)\n\t{\n\t\tif (wjoy[j].devid == id)\n\t\t{\n\t\t\tif (wjoy[j].devstate == DS_NOTPRESENT)\n\t\t\t\treturn CONTROLLER_NONE;\n\t\t\tif (wjoy[j].isxinput)\n\t\t\t\treturn CONTROLLER_XBOX;\t//we don't really know, but we're using it through an xbox-specific api, so...\n\t\t\treturn CONTROLLER_UNKNOWN;\t//the legacy joy API.\n\t\t}\n\t}\n\treturn CONTROLLER_NONE;\t//no idea what you're talking about.\n}\n\nvoid INS_Rumble(int id, quint16_t amp_low, quint16_t amp_high, quint32_t duration)\n{\n\t//Con_DPrintf(CON_WARNING \"Rumble is unavailable on this platform\\n\");\n}\n\nvoid INS_RumbleTriggers(int id, quint16_t left, quint16_t right, quint32_t duration)\n{\n\t//Con_DPrintf(CON_WARNING \"Trigger rumble is unavailable on this platform\\n\");\n}\n\nvoid INS_SetLEDColor(int id, vec3_t color)\n{\n\t//Con_DPrintf(CON_WARNING \"Game-Pad LED colors are unavailable on this platform\\n\");\n}\n\nvoid INS_SetTriggerFX(int id, const void *data, size_t size)\n{\n\t//Con_DPrintf(CON_WARNING \"Trigger FX are unavailable on this platform\\n\");\n}\n\n#ifdef USINGRAWINPUT\nstatic int Mouse_AllocateDevID(void)\n{\n\textern cvar_t cl_splitscreen;\n\tint id = 0, j;\n\tfor (id = 0; ; id++)\n\t{\n\t\tfor (j = 0; j < rawmicecount; j++)\n\t\t{\n\t\t\tif (rawmice[j].qdeviceid == id)\n\t\t\t\tbreak;\n\t\t}\n\t\tif (j == rawmicecount)\n\t\t{\n\t\t\tif (id > cl_splitscreen.ival && !*cl_splitscreen.string)\n\t\t\t\tcl_splitscreen.ival = id;\n\t\t\treturn id;\n\t\t}\n\t}\n}\nstatic int Keyboard_AllocateDevID(void)\n{\n\textern cvar_t cl_splitscreen;\n\tint id = 0, j;\n\tfor (id = 0; ; id++)\n\t{\n\t\tfor (j = 0; j < rawkbdcount; j++)\n\t\t{\n\t\t\tif (rawkbd[j].qdeviceid == id)\n\t\t\t\tbreak;\n\t\t}\n\t\tif (j == rawkbdcount)\n\t\t{\n\t\t\tif (id > cl_splitscreen.ival && !*cl_splitscreen.string)\n\t\t\t\tcl_splitscreen.ival = id;\n\t\t\treturn id;\n\t\t}\n\t}\n}\n#endif\n\n/*\n===========\nINS_ActivateMouse\n===========\n*/\nstatic void INS_ActivateMouse (void)\n{\n\tmouseactivatetoggle = true;\n\n\tif (mouseinitialized && !mouseactive)\n\t{\n#ifdef AVAIL_DINPUT\n#if (DIRECTINPUT_VERSION >= DINPUT_VERSION_DX7)\n\t\tif (dinput >= DINPUT_VERSION_DX7)\n\t\t{\n\t\t\tif (g_pMouse7)\n\t\t\t{\n\t\t\t\tif (!dinput_acquired)\n\t\t\t\t{\n\t\t\t\t\tIDirectInputDevice7_Acquire(g_pMouse7);\n\t\t\t\t\tdinput_acquired = true;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\telse\n#endif\n\t\tif (dinput)\n\t\t{\n\t\t\tif (g_pMouse)\n\t\t\t{\n\t\t\t\tif (!dinput_acquired)\n\t\t\t\t{\n\t\t\t\t\tIDirectInputDevice_Acquire(g_pMouse);\n\t\t\t\t\tdinput_acquired = true;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\telse\n#endif\n\t\t{\n#ifdef USINGRAWINPUT\n\t\t\tif (rawkbdcount > 0)\n\t\t\t{\n\t\t\t\tif (INS_RawInput_KeyboardRegister())\n\t\t\t\t{\n\t\t\t\t\tCon_SafePrintf(\"Raw input: unable to register raw input for keyboard, deinitializing\\n\");\n\t\t\t\t\tINS_RawInput_KeyboardDeRegister();\n\t\t\t\t}\n\t\t\t}\n#endif\n\n\t\t\tif (mouseparmsvalid)\n\t\t\t{\n\t\t\t\tSystemParametersInfo (SPI_SETMOUSE, 0, newmouseparms, 0);\n\t\t\t\trestore_spi = true;\n\t\t\t}\n\n\t\t\tSetCursorPos (window_center_x, window_center_y);\n\t\t\tSetCapture (mainwindow);\n\t\t\tClipCursor (&window_rect);\n\t\t}\n\n\t\tmouseactive = true;\n\t}\n}\n\n\n/*\n===========\nINS_DeactivateMouse\n===========\n*/\nstatic void INS_DeactivateMouse (void)\n{\n\tmouseactivatetoggle = false;\n\n\tif (mouseinitialized && mouseactive)\n\t{\n#ifdef AVAIL_DINPUT\n#if (DIRECTINPUT_VERSION >= DINPUT_VERSION_DX7)\n\t\tif (dinput >= DINPUT_VERSION_DX7)\n\t\t{\n\t\t\tif (g_pMouse7)\n\t\t\t{\n\t\t\t\tif (dinput_acquired)\n\t\t\t\t{\n\t\t\t\t\tIDirectInputDevice_Unacquire(g_pMouse7);\n\t\t\t\t\tdinput_acquired = false;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\telse\n#endif\n\t\tif (dinput)\n\t\t{\n\t\t\tif (g_pMouse)\n\t\t\t{\n\t\t\t\tif (dinput_acquired)\n\t\t\t\t{\n\t\t\t\t\tIDirectInputDevice_Unacquire(g_pMouse);\n\t\t\t\t\tdinput_acquired = false;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse\n#endif\n\t\t{\n#ifdef USINGRAWINPUT\n//\t\t\tif (rawmicecount > 0)\n//\t\t\t\tINS_RawInput_MouseDeRegister();\n#endif\n\n\t\t\tif (restore_spi)\n\t\t\t\tSystemParametersInfo (SPI_SETMOUSE, 0, originalmouseparms, 0);\n\n\t\t\tClipCursor (NULL);\n\t\t\tReleaseCapture ();\n\t\t\tif (!vid.forcecursor)\n\t\t\t{\n\t\t\t\tvid.forcecursor = true;\n\t\t\t\tvid.forcecursorpos[0] = mousecursor_x;\n\t\t\t\tvid.forcecursorpos[1] = mousecursor_y;\n\t\t\t}\n\t\t}\n\n\t\tmouseactive = false;\n\t}\n}\n\n/*\n===========\nINS_SetQuakeMouseState\n===========\n*/\nvoid INS_SetQuakeMouseState (void)\n{\n\tif (mouseactivatetoggle)\n\t\tINS_ActivateMouse ();\n\telse\n\t\tINS_DeactivateMouse();\n}\n\n/*\n===========\nINS_RestoreOriginalMouseState\n===========\n*/\nvoid INS_RestoreOriginalMouseState (void)\n{\n\tif (mouseactivatetoggle)\n\t{\n\t\tINS_DeactivateMouse ();\n\t\tmouseactivatetoggle = true;\n\t}\n\n// try to redraw the cursor so it gets reinitialized, because sometimes it\n// has garbage after the mode switch\n\tShowCursor (TRUE);\n\tShowCursor (FALSE);\n}\n\n\n\n\nvoid INS_UpdateGrabs(int fullscreen, int activeapp)\n{\n\tint grabmouse;\n\n\tINS_ScreenSaver_UpdateBlock(cls.demoplayback && activeapp);\n\n\tif (!activeapp)\n\t\tgrabmouse = false;\n\telse if (fullscreen || in_simulatemultitouch.ival || in_windowed_mouse.value)\n\t{\n\t\tif (vrui.enabled || !Key_MouseShouldBeFree())\n\t\t\tgrabmouse = true;\n\t\telse\n\t\t\tgrabmouse = false;\n\t}\n\telse\n\t\tgrabmouse = false;\n\n\tif (activeapp)\n\t{\n\t\tif (\n#ifdef TEXTEDITOR\n\t\t\t!editormodal &&\n#endif\n\t\t\t!SCR_HardwareCursorIsActive() && (fullscreen || in_simulatemultitouch.ival || in_windowed_mouse.value) && (fullscreen || (current_mouse_pos.x >= window_rect.left && current_mouse_pos.y >= window_rect.top && current_mouse_pos.x <= window_rect.right && current_mouse_pos.y <= window_rect.bottom)))\n\t\t{\n\t\t\tINS_HideMouse();\n\t\t}\n\t\telse \n\t\t{\n\t\t\tINS_ShowMouse();\n\t\t\tgrabmouse = false;\n\t\t}\n\t}\n\telse\n\t{\n\t\tINS_ShowMouse();\n\t\tgrabmouse = false;\n\t}\n\n#ifdef HLCLIENT\n\t//halflife gamecode does its own mouse control... yes this is vile.\n\tif (grabmouse)\n\t{\n\t\tif (CLHL_GamecodeDoesMouse())\n\t\t\tgrabmouse = 2;\n\t}\n\n\tif (grabmouse == 2)\n\t{\n\t\tINS_DeactivateMouse();\n\t\tCLHL_SetMouseActive(true);\n\t\treturn;\n\t}\n\n\tCLHL_SetMouseActive(false);\n#endif\n\n\tif (grabmouse)\n\t\tINS_ActivateMouse();\n\telse\n\t\tINS_DeactivateMouse();\n\n\n\tif (vid.forcecursor && !mouseactive)\n\t{\n\t\tvid.forcecursor = false;\n\t\tif (activeapp)\n\t\t\tSetCursorPos(window_rect.left+vid.forcecursorpos[0], window_rect.top+vid.forcecursorpos[1]);\n\t}\n}\n\n\n\n\n\n\n#ifdef AVAIL_DINPUT\nBOOL CALLBACK INS_EnumerateDI7Devices(LPCDIDEVICEINSTANCE inst, LPVOID parm)\n{\n\tCon_DPrintf(\"EnumerateDevices found: %s\\n\", inst->tszProductName);\n\n\treturn DIENUM_CONTINUE;\n}\n/*\n===========\nINS_InitDInput\n===========\n*/\nint INS_InitDInput (void)\n{\n\tHRESULT\t\thr;\n\tDIPROPDWORD\tdipdw = {\n\t\t{\n\t\t\tsizeof(DIPROPDWORD),        // diph.dwSize\n\t\t\tsizeof(DIPROPHEADER),       // diph.dwHeaderSize\n\t\t\t0,                          // diph.dwObj\n\t\t\tDIPH_DEVICE,                // diph.dwHow\n\t\t},\n\t\tDINPUT_BUFFERSIZE,              // dwData\n\t};\n\n\tif (!hInstDI)\n\t{\n\t\thInstDI = Sys_LoadLibrary(\"dinput.dll\", NULL);\n\n\t\tif (hInstDI == NULL)\n\t\t{\n\t\t\tCon_SafePrintf (\"Couldn't load dinput.dll\\n\");\n\t\t\treturn 0;\n\t\t}\n\t}\n\n#if (DIRECTINPUT_VERSION >= DINPUT_VERSION_DX7)\n\tif (!pDirectInputCreateEx)\n\t\tpDirectInputCreateEx = (void *)GetProcAddress(hInstDI,\"DirectInputCreateEx\");\n\n\tif (pDirectInputCreateEx) // use DirectInput 7\n\t{\n\t\t// register with DirectInput and get an IDirectInput to play with.\n\t\thr = iDirectInputCreateEx(global_hInstance, DINPUT_VERSION_DX7, &fIID_IDirectInput7A, (void**)&g_pdi7, NULL);\n\n\t\tif (FAILED(hr))\n\t\t\treturn 0;\n\n\t\tIDirectInput7_EnumDevices(g_pdi7, 0, &INS_EnumerateDI7Devices, NULL, DIEDFL_ATTACHEDONLY);\n\n\t\t// obtain an interface to the system mouse device.\n\t\thr = IDirectInput7_CreateDeviceEx(g_pdi7, &fGUID_SysMouse, &fIID_IDirectInputDevice7A, (void**)&g_pMouse7, NULL);\n\n\t\tif (FAILED(hr)) {\n\t\t\tCon_SafePrintf (\"Couldn't open DI7 mouse device\\n\");\n\t\t\treturn 0;\n\t\t}\n\n\t\t// set the data format to \"mouse format\".\n\t\thr = IDirectInputDevice7_SetDataFormat(g_pMouse7, &df);\n\n\t\tif (FAILED(hr)) {\n\t\t\tCon_SafePrintf (\"Couldn't set DI7 mouse format\\n\");\n\t\t\treturn 0;\n\t\t}\n\n\t\t// set the cooperativity level.\n\t\thr = IDirectInputDevice7_SetCooperativeLevel(g_pMouse7, mainwindow,\n\t\t\tDISCL_EXCLUSIVE | DISCL_FOREGROUND);\n\n\t\tif (FAILED(hr)) {\n\t\t\tCon_SafePrintf (\"Couldn't set DI7 coop level\\n\");\n\t\t\treturn 0;\n\t\t}\n\n\t\t// set the buffer size to DINPUT_BUFFERSIZE elements.\n\t\t// the buffer size is a DWORD property associated with the device\n\t\thr = IDirectInputDevice7_SetProperty(g_pMouse7, DIPROP_BUFFERSIZE, &dipdw.diph);\n\n\t\tif (FAILED(hr)) {\n\t\t\tCon_SafePrintf (\"Couldn't set DI7 buffersize\\n\");\n\t\t\treturn 0;\n\t\t}\n\n\t\treturn DINPUT_VERSION_DX7;\n\t}\n#endif\n\n\tif (!pDirectInputCreate)\n\t{\n\t\tpDirectInputCreate = (void *)GetProcAddress(hInstDI,\"DirectInputCreateA\");\n\n\t\tif (!pDirectInputCreate)\n\t\t{\n\t\t\tCon_SafePrintf (\"Couldn't get DI3 proc addr\\n\");\n\t\t\treturn 0;\n\t\t}\n\t}\n\n// register with DirectInput and get an IDirectInput to play with.\n\thr = iDirectInputCreate(global_hInstance, DINPUT_VERSION_DX3, &g_pdi, NULL);\n\n\tif (FAILED(hr))\n\t{\n\t\treturn 0;\n\t}\n\tIDirectInput_EnumDevices(g_pdi, 0, &INS_EnumerateDI7Devices, NULL, DIEDFL_ATTACHEDONLY);\n\n// obtain an interface to the system mouse device.\n\thr = IDirectInput_CreateDevice(g_pdi, &fGUID_SysMouse, &g_pMouse, NULL);\n\n\tif (FAILED(hr))\n\t{\n\t\tCon_SafePrintf (\"Couldn't open DI3 mouse device\\n\");\n\t\treturn 0;\n\t}\n\n// set the data format to \"mouse format\".\n\thr = IDirectInputDevice_SetDataFormat(g_pMouse, &df);\n\n\tif (FAILED(hr))\n\t{\n\t\tCon_SafePrintf (\"Couldn't set DI3 mouse format\\n\");\n\t\treturn 0;\n\t}\n\n// set the cooperativity level.\n\thr = IDirectInputDevice_SetCooperativeLevel(g_pMouse, mainwindow,\n\t\t\tDISCL_EXCLUSIVE | DISCL_FOREGROUND);\n\n\tif (FAILED(hr))\n\t{\n\t\tCon_SafePrintf (\"Couldn't set DI3 coop level\\n\");\n\t\treturn 0;\n\t}\n\n\n// set the buffer size to DINPUT_BUFFERSIZE elements.\n// the buffer size is a DWORD property associated with the device\n\thr = IDirectInputDevice_SetProperty(g_pMouse, DIPROP_BUFFERSIZE, &dipdw.diph);\n\n\tif (FAILED(hr))\n\t{\n\t\tCon_SafePrintf (\"Couldn't set DI3 buffersize\\n\");\n\t\treturn 0;\n\t}\n\n\treturn DINPUT_VERSION_DX3;\n}\n\nvoid INS_CloseDInput (void)\n{\n#if (DIRECTINPUT_VERSION >= DINPUT_VERSION_DX7)\n\tif (g_pMouse7)\n\t{\n\t\tIDirectInputDevice7_Release(g_pMouse7);\n\t\tg_pMouse7 = NULL;\n\t}\n\tif (g_pdi7)\n\t{\n\t\tIDirectInput7_Release(g_pdi7);\n\t\tg_pdi7 = NULL;\n\t}\n\tpDirectInputCreateEx = NULL;\n#endif\n\tif (g_pMouse)\n\t{\n\t\tIDirectInputDevice_Release(g_pMouse);\n\t\tg_pMouse = NULL;\n\t}\n\tif (g_pdi)\n\t{\n\t\tIDirectInput_Release(g_pdi);\n\t\tg_pdi = NULL;\n\t}\n\tif (hInstDI)\n\t{\n\t\tFreeLibrary(hInstDI);\n\t\thInstDI = NULL;\n\t\tpDirectInputCreate = NULL;\n\t}\n\n}\n#endif\n\n#ifdef USINGRAWINPUT\nvoid INS_RawInput_MouseDeRegister(void)\n{\n\tRAWINPUTDEVICE Rid;\n\n\t// deregister raw input\n\tRid.usUsagePage = 0x01;\n\tRid.usUsage = 0x02;\n\tRid.dwFlags = RIDEV_REMOVE;\n\tRid.hwndTarget = NULL;\n\n\t(*_RRID)(&Rid, 1, sizeof(Rid));\n}\n\nvoid INS_RawInput_KeyboardDeRegister(void)\n{\n\tRAWINPUTDEVICE Rid;\n\n\t// deregister raw input\n\tRid.usUsagePage = 0x01;\n\tRid.usUsage = 0x06;\n\tRid.dwFlags = RIDEV_REMOVE;\n\tRid.hwndTarget = NULL;\n\n\t(*_RRID)(&Rid, 1, sizeof(Rid));\n}\n\nvoid INS_RawInput_DeInit(void)\n{\n\tif (rawmicecount > 0)\n\t\tINS_RawInput_MouseDeRegister();\n\tif (rawkbdcount > 0)\n\t\tINS_RawInput_KeyboardDeRegister();\n\trawmicecount = 0;\n\trawkbdcount = 0;\n\tZ_Free(rawmice);\n\trawmice = NULL;\n\tZ_Free(rawkbd);\n\trawkbd = NULL;\n\tZ_Free(raw);\n\traw = NULL;\n\tmemset(multicursor_active, 0, sizeof(multicursor_active));\n}\n#endif\n\n#ifdef USINGRAWINPUT\n// raw input registration functions\nint INS_RawInput_MouseRegister(void)\n{\n\t// This function registers to receive the WM_INPUT messages\n\tRAWINPUTDEVICE Rid; // Register only for mouse messages from wm_input.\n\n\t//register to get wm_input messages\n\tRid.usUsagePage = 0x01;\n\tRid.usUsage = 0x02;\n\t//note: we don't exclude legacy events any more. while we don't really want them, we also don't want to get confused about click states. this way we can track the states properly without breaking.\n\tRid.dwFlags = 0;//RIDEV_NOLEGACY; // adds HID mouse and also ignores legacy mouse messages\n\tRid.hwndTarget = NULL;\n\n\t// Register to receive the WM_INPUT message for any change in mouse (buttons, wheel, and movement will all generate the same message)\n\tif (!(*_RRID)(&Rid, 1, sizeof(Rid)))\n\t\treturn 1;\n\n\treturn 0;\n}\n\nint INS_RawInput_KeyboardRegister(void)\n{\n\tRAWINPUTDEVICE Rid;\n\n\tRid.usUsagePage = 0x01;\n\tRid.usUsage = 0x06;\n\tRid.dwFlags = RIDEV_NOLEGACY | RIDEV_APPKEYS | RIDEV_NOHOTKEYS; // fetch everything, disable hotkey behavior (should cvar?)\n\tRid.hwndTarget = NULL;\n\n\tif (!(*_RRID)(&Rid, 1, sizeof(Rid)))\n\t\treturn 1;\n\n\treturn 0;\n}\n\nint INS_RawInput_Register(void)\n{\n\tif (INS_RawInput_MouseRegister())\n\t\treturn !in_rawinput_keyboard.ival || INS_RawInput_KeyboardRegister();\n\treturn 0;\n}\n\nint INS_RawInput_IsRDPDevice(char *cDeviceString)\n{\n\t// mouse is \\\\?\\Root#RDP_MOU#, keyboard is \\\\?\\Root#RDP_KBD#\"\n\tchar cRDPString[] = \"\\\\\\\\?\\\\Root#RDP_\";\n\tint i;\n\n\tif (strlen(cDeviceString) < strlen(cRDPString)) {\n\t\treturn 0;\n\t}\n\n\tfor (i = strlen(cRDPString) - 1; i >= 0; i--)\n\t{\n\t\tif (cDeviceString[i] != cRDPString[i])\n\t\t\treturn 0;\n\t}\n\n\treturn 1;\n}\n\nvoid INS_RawInput_Init(void)\n{\n\t  // \"0\" to exclude, \"1\" to include\n\tPRAWINPUTDEVICELIST pRawInputDeviceList;\n\tint inputdevices, i, j, mtemp, ktemp;\n\tchar dname[MAX_RI_DEVICE_SIZE];\n\n\t// Return 0 if rawinput is not available\n\tHMODULE user32 = LoadLibrary(\"user32.dll\");\n\tif (!user32)\n\t{\n\t\tCon_SafePrintf(\"Raw input: unable to load user32.dll\\n\");\n\t\treturn;\n\t}\n\t_RRID = (pRegisterRawInputDevices)GetProcAddress(user32,\"RegisterRawInputDevices\");\n\tif (!_RRID)\n\t{\n\t\tCon_SafePrintf(\"Raw input: function RegisterRawInputDevices is not available\\n\");\n\t\treturn;\n\t}\n\t_GRIDL = (pGetRawInputDeviceList)GetProcAddress(user32,\"GetRawInputDeviceList\");\n\tif (!_GRIDL)\n\t{\n\t\tCon_SafePrintf(\"Raw input: function GetRawInputDeviceList is not available\\n\");\n\t\treturn;\n\t}\n\t_GRIDIA = (pGetRawInputDeviceInfoA)GetProcAddress(user32,\"GetRawInputDeviceInfoA\");\n\tif (!_GRIDIA)\n\t{\n\t\tCon_SafePrintf(\"Raw input: function GetRawInputDeviceInfoA is not available\\n\");\n\t\treturn;\n\t}\n\t_GRID = (pGetRawInputData)GetProcAddress(user32,\"GetRawInputData\");\n\tif (!_GRID)\n\t{\n\t\tCon_SafePrintf(\"Raw input: function GetRawInputData is not available\\n\");\n\t\treturn;\n\t}\n\n\trawmicecount = 0;\n\trawmice = NULL;\n\traw = NULL;\n\tribuffersize = 0;\n\n\t// 1st call to GetRawInputDeviceList: Pass NULL to get the number of devices.\n\tif ((*_GRIDL)(NULL, &inputdevices, sizeof(RAWINPUTDEVICELIST)) != 0)\n\t{\n\t\tCon_SafePrintf(\"Raw input: unable to count raw input devices\\n\");\n\t\treturn;\n\t}\n\n\t// Allocate the array to hold the DeviceList\n\tpRawInputDeviceList = Z_Malloc(sizeof(RAWINPUTDEVICELIST) * inputdevices);\n\n\t// 2nd call to GetRawInputDeviceList: Pass the pointer to our DeviceList and GetRawInputDeviceList() will fill the array\n\tif ((*_GRIDL)(pRawInputDeviceList, &inputdevices, sizeof(RAWINPUTDEVICELIST)) == -1)\n\t{\n\t\tCon_SafePrintf(\"Raw input: unable to get raw input device list\\n\");\n\t\treturn;\n\t}\n\n\t// Loop through all devices and count the mice\n\tfor (i = 0, mtemp = 0, ktemp = 0; i < inputdevices; i++)\n\t{\n\t\tj = MAX_RI_DEVICE_SIZE;\n\n\t\t// Get the device name and use it to determine if it's the RDP Terminal Services virtual device.\n\t\tif ((*_GRIDIA)(pRawInputDeviceList[i].hDevice, RIDI_DEVICENAME, dname, &j) < 0)\n\t\t\tdname[0] = 0;\n\n\t\tif (!(in_rawinput_rdp.value) && INS_RawInput_IsRDPDevice(dname)) // use rdp (cvar)\n\t\t\tcontinue;\n\n\t\tswitch (pRawInputDeviceList[i].dwType)\n\t\t{\n\t\tcase RIM_TYPEMOUSE:\n\t\t\tif (!in_rawinput_mice.ival)\n\t\t\t\tcontinue;\n\n\t\t\tmtemp++;\n\t\t\tbreak;\n\t\tcase RIM_TYPEKEYBOARD:\n\t\t\tif (!in_rawinput_keyboard.ival)\n\t\t\t\tcontinue;\n\n\t\t\tktemp++;\n\t\t\tbreak;\n\t\tdefault: // (RIM_TYPEHID) support joysticks?\n\t\t\tbreak;\n\t\t}\n\t}\n\n\t// exit out if no devices found\n\tif (!mtemp && !ktemp)\n\t{\n\t\tCon_SafePrintf(\"Raw input: no usable device found\\n\");\n\t\treturn;\n\t}\n\n\t// Loop again and bind devices\n\trawmice = (mouse_t *)Z_Malloc(sizeof(mouse_t) * mtemp);\n\trawkbd = (keyboard_t *)Z_Malloc(sizeof(keyboard_t) * ktemp);\n\tfor (i = 0; i < inputdevices; i++)\n\t{\n\t\tj = MAX_RI_DEVICE_SIZE;\n\n\t\t// Get the device name and use it to determine if it's the RDP Terminal Services virtual device.\n\t\tif ((*_GRIDIA)(pRawInputDeviceList[i].hDevice, RIDI_DEVICENAME, dname, &j) < 0)\n\t\t\tdname[0] = 0;\n\n\t\tif (!(in_rawinput_rdp.value) && INS_RawInput_IsRDPDevice(dname)) // use rdp (cvar)\n\t\t\tcontinue;\n\n\t\tswitch (pRawInputDeviceList[i].dwType)\n\t\t{\n\t\tcase RIM_TYPEMOUSE:\n\t\t\tif (!in_rawinput_mice.ival)\n\t\t\t\tcontinue;\n\n\t\t\t// set handle\n\t\t\trawmice[rawmicecount].handles.rawinputhandle = pRawInputDeviceList[i].hDevice;\n\t\t\tQ_strncpyz(rawmice[rawmicecount].sysname, dname, sizeof(rawmice[rawmicecount].sysname));\n\t\t\trawmice[rawmicecount].numbuttons = 16;\n\t\t\trawmice[rawmicecount].oldbuttons = 0;\n\t\t\trawmice[rawmicecount].qdeviceid = DEVID_UNSET;\n\t\t\trawmicecount++;\n\t\t\tbreak;\n\t\tcase RIM_TYPEKEYBOARD:\n\t\t\tif (!in_rawinput_keyboard.ival)\n\t\t\t\tcontinue;\n\n\t\t\trawkbd[rawkbdcount].handles.rawinputhandle = pRawInputDeviceList[i].hDevice;\n\t\t\tQ_strncpyz(rawkbd[rawkbdcount].sysname, dname, sizeof(rawkbd[rawkbdcount].sysname));\n\t\t\trawkbd[rawkbdcount].qdeviceid = DEVID_UNSET;\n\t\t\trawkbdcount++;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tcontinue;\n\t\t}\n\n\t\t// print pretty message about device\n\t\tdname[MAX_RI_DEVICE_SIZE - 1] = 0;\n\t\tfor (mtemp = strlen(dname); mtemp >= 0; mtemp--)\n\t\t{\n\t\t\tif (dname[mtemp] == '#')\n\t\t\t{\n\t\t\t\tdname[mtemp + 1] = 0;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tCon_DPrintf(\"Raw input type %i: [%i] %s\\n\", (int)pRawInputDeviceList[i].dwType, i, dname);\n\t}\n\n\n\t// free the RAWINPUTDEVICELIST\n\tZ_Free(pRawInputDeviceList);\n\n\t// alloc raw input buffer\n\traw = BZ_Malloc(INIT_RIBUFFER_SIZE);\n\tribuffersize = INIT_RIBUFFER_SIZE;\n\n\tCon_DPrintf(\"Raw input: initialized with %i mice and %i keyboards\\n\", rawmicecount, rawkbdcount);\n\n\treturn; // success\n}\n#endif\n\n/*\n===========\nINS_StartupMouse\n===========\n*/\nvoid INS_StartupMouse (void)\n{\n\tif ( COM_CheckParm (\"-nomouse\") )\n\t\treturn;\n\n\tmouseinitialized = true;\n\n\t//make sure it can't get stuck\n\tINS_DeactivateMouse ();\n\n#ifdef AVAIL_DINPUT\n\tif (in_dinput.value)\n\t{\n\t\tdinput = INS_InitDInput ();\n\n\t\tif (dinput)\n\t\t{\n\t\t\tCon_SafePrintf (\"DirectInput initialized, version %i\\n\", (dinput >> 8 & 0xFF));\n\t\t}\n\t\telse\n\t\t{\n\t\t\tCon_SafePrintf (\"DirectInput not initialized\\n\");\n\t\t}\n\t}\n\telse\n\t\tdinput = 0;\n\n\tif (!dinput)\n#endif\n\t{\n\t\tif (!mouseparmsvalid)\n\t\t\tmouseparmsvalid = SystemParametersInfo (SPI_GETMOUSE, 0, originalmouseparms, 0);\n\n\t\tif (mouseparmsvalid)\n\t\t{\n\t\t\tif ( m_accel_noforce.value )\n\t\t\t\tnewmouseparms[2] = originalmouseparms[2];\n\n\t\t\tif ( m_threshold_noforce.value )\n\t\t\t{\n\t\t\t\tnewmouseparms[0] = originalmouseparms[0];\n\t\t\t\tnewmouseparms[1] = originalmouseparms[1];\n\t\t\t}\n\t\t}\n\t}\n\n\tsysmouse.numbuttons = 10;\n\n// if a fullscreen video mode was set before the mouse was initialized,\n// set the mouse state appropriately\n\tif (mouseactivatetoggle)\n\t\tINS_ActivateMouse ();\n}\n\n\n/*\n===========\nINS_Init\n===========\n*/\nvoid INS_ReInit (void)\n{\n#ifdef USINGRAWINPUT\n\tif (in_rawinput_mice.ival || in_rawinput_keyboard.ival)\n\t{\n\t\tINS_RawInput_Init();\n\t}\n#endif\n\n\tINS_StartupMouse ();\n\tINS_StartupJoystick ();\n//\tINS_ActivateMouse();\n\n#ifdef USINGRAWINPUT\n\t//mouse rawinput is always enabled, because its too messy otherwise.\n\tif (rawmicecount > 0)\n\t{\n\t\tif (INS_RawInput_MouseRegister())\n\t\t{\n\t\t\tCon_SafePrintf(\"Raw input: unable to register raw input for mice, deinitializing\\n\");\n\t\t\tINS_RawInput_MouseDeRegister();\n\t\t}\n\t}\n#endif\n}\n\nvoid INS_Init (void)\n{\n\t//keyboard variables\n\tCvar_Register (&cl_keypad, \"Input Controls\");\n\n#ifdef AVAIL_DINPUT\n\tCvar_Register (&in_dinput, \"Input Controls\");\n#endif\n#ifdef AVAIL_XINPUT\n\tCvar_Register (&in_xinput, \"Input Controls\");\n\tCvar_Register (&xinput_leftvibrator, \"Input Controls\");\n\tCvar_Register (&xinput_rightvibrator, \"Input Controls\");\n#endif\n\tCvar_Register (&in_builtinkeymap, \"Input Controls\");\n\tCvar_Register (&in_nonstandarddeadkeys, \"Input Controls\");\n\tCvar_Register (&in_simulatemultitouch, \"Input Controls\");\n\n\tCvar_Register (&m_accel_noforce, \"Input Controls\");\n\tCvar_Register (&m_threshold_noforce, \"Input Controls\");\n\n\t// this looks strange but quake cmdline definitions\n\t// and MS documentation don't agree with each other\n\tif (COM_CheckParm (\"-noforcemspd\"))\n\t\tCvar_Set(&m_accel_noforce, \"1\");\n\n\tif (COM_CheckParm (\"-noforcemaccel\"))\n\t\tCvar_Set(&m_threshold_noforce, \"1\");\n\n\tif (COM_CheckParm (\"-noforcemparms\"))\n\t{\n\t\tCvar_Set(&m_accel_noforce, \"1\");\n\t\tCvar_Set(&m_threshold_noforce, \"1\");\n\t}\n\n\tif (COM_CheckParm (\"-dinput\"))\n\t\tCvar_Set(&in_dinput, \"1\");\n\n\t// joystick variables\n\tCvar_Register (&in_joystick, \"Joystick variables\");\n\n\tCmd_AddCommand (\"force_centerview\", Force_CenterView_f);\n\n\tuiWheelMessage = RegisterWindowMessageA ( \"MSWHEEL_ROLLMSG\" );\n\n#ifdef USINGRAWINPUT\n\tCvar_Register (&in_rawinput_mice, \"Input Controls\");\n\tCvar_Register (&in_rawinput_keyboard, \"Input Controls\");\n\tCvar_Register (&in_rawinput_rdp, \"Input Controls\");\n#endif\n\n\tINS_ScreenSaver_Init();\n}\n\n/*\n===========\nINS_Shutdown\n===========\n*/\nvoid INS_Shutdown (void)\n{\n\tINS_DeactivateMouse ();\n\tINS_ShowMouse ();\n\n\tmouseinitialized = false;\n\tmouseparmsvalid = false;\n\n#ifdef AVAIL_DINPUT\n\tINS_CloseDInput();\n#endif\n#ifdef USINGRAWINPUT\n\tINS_RawInput_DeInit();\n#endif\n}\n\n\n/*\n===========\nINS_MouseEvent\n===========\na mouse button was pressed/released, mstate is the current set of buttons pressed.\n*/\nvoid INS_MouseEvent (int mstate)\n{\n\tint\t\ti;\n\n\tif (dinput && mouseactive)\n\t\treturn;\n\n#ifdef HLCLIENT\n\tif (CLHL_MouseEvent(mstate))\n\t\treturn;\n#endif\n\n\tif (1)//mouseactive || (key_dest != key_game))\n\t{\n\t// perform button actions\n\t\tfor (i=0 ; i<sysmouse.numbuttons ; i++)\n\t\t{\n\t\t\tif ( (mstate & (1<<i)) &&\n\t\t\t\t!(sysmouse.oldbuttons & (1<<i)) )\n\t\t\t{\n\t\t\t\tif (!rawmicecount)\n\t\t\t\t\tIN_KeyEvent (sysmouse.qdeviceid, true, K_MOUSE1 + i, 0);\n\t\t\t\telse\n\t\t\t\t\tmstate &= ~(1<<i);\n\t\t\t}\n\n\t\t\tif ( !(mstate & (1<<i)) &&\n\t\t\t\t(sysmouse.oldbuttons & (1<<i)) )\n\t\t\t{\n\t\t\t\tIN_KeyEvent (sysmouse.qdeviceid, false, K_MOUSE1 + i, 0);\n\t\t\t}\n\t\t}\n\n\t\tsysmouse.oldbuttons = mstate;\n\t}\n}\n\n/*\n===========\nINS_MouseMove\n===========\n*/\nvoid INS_MouseMove (void)\n{\n#ifdef AVAIL_DINPUT\n\tif (dinput && mouseactive)\n\t{\n\t\tDIDEVICEOBJECTDATA\tod;\n\t\tDWORD\t\t\t\tdwElements;\n\t\tHRESULT\t\t\t\thr;\n\t\tint xd = 0, yd = 0, zd = 0;\n\n\t\tfor (;;)\n\t\t{\n\t\t\tdwElements = 1;\n\n#if (DIRECTINPUT_VERSION >= DINPUT_VERSION_DX7)\n\t\t\tif (dinput >= DINPUT_VERSION_DX7)\n\t\t\t{\n\t\t\t\thr = IDirectInputDevice7_GetDeviceData(g_pMouse7,\n\t\t\t\t\t\tsizeof(DIDEVICEOBJECTDATA), &od, &dwElements, 0);\n\n\t\t\t\tif ((hr == DIERR_INPUTLOST) || (hr == DIERR_NOTACQUIRED))\n\t\t\t\t{\n\t\t\t\t\tdinput_acquired = true;\n\t\t\t\t\tIDirectInputDevice7_Acquire(g_pMouse7);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n#endif\n\t\t\t{\n\t\t\t\thr = IDirectInputDevice_GetDeviceData(g_pMouse,\n\t\t\t\t\t\tsizeof(DIDEVICEOBJECTDATA), &od, &dwElements, 0);\n\n\t\t\t\tif ((hr == DIERR_INPUTLOST) || (hr == DIERR_NOTACQUIRED))\n\t\t\t\t{\n\t\t\t\t\tdinput_acquired = true;\n\t\t\t\t\tIDirectInputDevice_Acquire(g_pMouse);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t/* Unable to read data or no data available */\n\t\t\tif (FAILED(hr) || dwElements == 0)\n\t\t\t{\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\t/* Look at the element to see what happened */\n\n\t\t\tswitch (od.dwOfs)\n\t\t\t{\n\t\t\t\tcase DIMOFS_X:\n\t\t\t\t\txd += od.dwData;\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase DIMOFS_Y:\n\t\t\t\t\tyd += od.dwData;\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase DIMOFS_Z:\n\t\t\t\t\tzd += od.dwData;\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase DIMOFS_BUTTON0:\n\t\t\t\tcase DIMOFS_BUTTON1:\n\t\t\t\tcase DIMOFS_BUTTON2:\n\t\t\t\tcase DIMOFS_BUTTON3:\n#if (DIRECTINPUT_VERSION >= DINPUT_VERSION_DX7)\n\t\t\t\tcase DIMOFS_BUTTON4:\n\t\t\t\tcase DIMOFS_BUTTON5:\n\t\t\t\tcase DIMOFS_BUTTON6:\n\t\t\t\tcase DIMOFS_BUTTON7:\n#endif\n\t\t\t\t\tIN_KeyEvent(sysmouse.qdeviceid, (od.dwData & 0x80)?true:false, K_MOUSE1 + ((od.dwOfs - DIMOFS_BUTTON0) / (DIMOFS_BUTTON1-DIMOFS_BUTTON0)), 0);\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (xd || yd || zd)\n\t\t\tIN_MouseMove(sysmouse.qdeviceid, false, xd, yd, zd, 0);\n\t}\n\telse\n#endif\n\t{\n\t\tINS_Accumulate();\n\t}\n}\n\n\n/*\n===========\nINS_Move\n===========\n*/\nvoid INS_Move (void)\n{\n\tif (vid.activeapp && !Minimized)\n\t{\n\t\tINS_MouseMove ();\n\t\tINS_JoyMove ();\n\t}\n\telse\n\t\tINS_Accumulate();\n}\n\n\n/*\n===========\nINS_Accumulate\n===========\npotentially called multiple times per frame.\n*/\nvoid INS_Accumulate (void)\n{\n\tif (mouseactive && !dinput)\n\t{\n\t\t//if you have two programs side by side that both think that they own the mouse cursor then there are certain race conditions when switching between them\n\t\t//when alt+tabbing, windows won't wait for a response, so we may have already lost focus by the time we get here, and none of our internal state will know about it.\n\t\t//fte won't grab the mouse until its actually inside the window, but other programs don't have similar delays.\n\t\t//so when switching to other quake ports, expect fte to detect the oter engine's setcursorpos as a really big mouse move.\n\n\t\tRECT cliprect;\n\t\tif (GetClipCursor (&cliprect) && !(\n\t\t\tcliprect.left >= window_rect.left && \n\t\t\tcliprect.right <= window_rect.right && \n\t\t\tcliprect.top >= window_rect.top && \n\t\t\tcliprect.bottom <= window_rect.bottom\n\t\t\t))\n\t\t\t;\t//cliprect now covers some area outside of where we asked for.\n\t\telse\n#ifdef USINGRAWINPUT\n\t\t//raw input disables the system mouse, to avoid dupes\n\t\tif (!rawmicecount)\n#endif\n\t\t{\n\t\t\tGetCursorPos (&current_mouse_pos);\n\t\t\tSetCursorPos (window_center_x, window_center_y);\n\n\t\t\tif (current_mouse_pos.x - window_center_x || current_mouse_pos.y - window_center_y)\n\t\t\t\tIN_MouseMove(sysmouse.qdeviceid, false, current_mouse_pos.x - window_center_x, current_mouse_pos.y - window_center_y, 0, 0);\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// force the mouse to the center, so there's room to move (rawinput ignore this apparently)\n\t\t\tSetCursorPos (window_center_x, window_center_y);\n\t\t}\n\t}\n\n\tif (!mouseactive)\n\t{\n\t\tGetCursorPos (&current_mouse_pos);\n\n\t\tIN_MouseMove(sysmouse.qdeviceid, true, current_mouse_pos.x-window_rect.left, current_mouse_pos.y-window_rect.top, 0, 0);\n\t\treturn;\n\t}\n}\n\n#ifdef USINGRAWINPUT\nvoid INS_RawInput_MouseRead(void)\n{\n\tint i, tbuttons, j;\n\tmouse_t *mouse;\n\n\t// find mouse in our mouse list\n\tfor (i = 0; i < rawmicecount; i++)\n\t{\n\t\tif (rawmice[i].handles.rawinputhandle == raw->header.hDevice)\n\t\t\tbreak;\n\t}\n\n\tif (i == rawmicecount) // we're not tracking this device\n\t\treturn;\n\tmouse = &rawmice[i];\n\n\tif (mouse->qdeviceid == DEVID_UNSET)\n\t{\n\t\tif ((raw->data.mouse.usButtonFlags & (RI_MOUSE_BUTTON_1_DOWN|RI_MOUSE_BUTTON_2_DOWN|RI_MOUSE_BUTTON_3_DOWN|RI_MOUSE_BUTTON_4_DOWN|RI_MOUSE_BUTTON_5_DOWN|RI_MOUSE_WHEEL))\n\t\t\t|| (raw->data.mouse.ulRawButtons & RI_RAWBUTTON_MASK) || raw->data.mouse.lLastX || raw->data.mouse.lLastY)\n\t\t{\n\t\t\tmouse->qdeviceid = Mouse_AllocateDevID();\n\t\t}\n\t\telse\n\t\t\treturn;\n\t}\n\n\tmulticursor_active[mouse->qdeviceid&7] = 0;\n\n\tif (vid.activeapp)\n\t{\n\t\t// movement\n\t\tif (raw->data.mouse.usFlags & MOUSE_MOVE_ABSOLUTE)\n\t\t{\n\t\t\tif (in_simulatemultitouch.ival)\n\t\t\t{\n\t\t\t\tmulticursor_active[mouse->qdeviceid&7] = true;\n\t\t\t\tmulticursor_x[mouse->qdeviceid&7] = raw->data.mouse.lLastX;\n\t\t\t\tmulticursor_y[mouse->qdeviceid&7] = raw->data.mouse.lLastY;\n\t\t\t}\n\t\t\tIN_MouseMove(mouse->qdeviceid, true, raw->data.mouse.lLastX, raw->data.mouse.lLastY, 0, 0);\n\t\t}\n\t\telse if (mouseactive)// RELATIVE\n\t\t{\n\t\t\tif (in_simulatemultitouch.ival)\n\t\t\t{\n\t\t\t\tmulticursor_active[mouse->qdeviceid&7] = true;\n\t\t\t\tmulticursor_x[mouse->qdeviceid&7] += raw->data.mouse.lLastX;\n\t\t\t\tmulticursor_y[mouse->qdeviceid&7] += raw->data.mouse.lLastY;\n\t\t\t\tmulticursor_x[mouse->qdeviceid&7] = bound(0, multicursor_x[mouse->qdeviceid&7], vid.pixelwidth);\n\t\t\t\tmulticursor_y[mouse->qdeviceid&7] = bound(0, multicursor_y[mouse->qdeviceid&7], vid.pixelheight);\n\t\t\t\tIN_MouseMove(mouse->qdeviceid, true, multicursor_x[mouse->qdeviceid&7], multicursor_y[mouse->qdeviceid&7], 0, 0);\n\t\t\t}\n\t\t\telse\n\t\t\t\tIN_MouseMove(mouse->qdeviceid, false, raw->data.mouse.lLastX, raw->data.mouse.lLastY, 0, 0);\n\t\t}\n\n\t\t// button presses\n\t\tif (raw->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_1_DOWN)\n\t\t\tIN_KeyEvent(mouse->qdeviceid, true, K_MOUSE1, 0);\n\t\tif (raw->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_2_DOWN)\n\t\t\tIN_KeyEvent(mouse->qdeviceid, true, K_MOUSE2, 0);\n\t\tif (raw->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_3_DOWN)\n\t\t\tIN_KeyEvent(mouse->qdeviceid, true, K_MOUSE3, 0);\n\t\tif (raw->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_4_DOWN)\n\t\t\tIN_KeyEvent(mouse->qdeviceid, true, K_MOUSE4, 0);\n\t\tif (raw->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_5_DOWN)\n\t\t\tIN_KeyEvent(mouse->qdeviceid, true, K_MOUSE5, 0);\n\n\n\t\t// mouse wheel\n\t\tif (raw->data.mouse.usButtonFlags & RI_MOUSE_WHEEL)\n\t\t{      // If the current message has a mouse_wheel message\n\t\t\tif ((SHORT)raw->data.mouse.usButtonData > 0)\n\t\t\t{\n\t\t\t\tIN_KeyEvent(mouse->qdeviceid, true, K_MWHEELUP, 0);\n\t\t\t\tIN_KeyEvent(mouse->qdeviceid, false, K_MWHEELUP, 0);\n\t\t\t}\n\t\t\tif ((SHORT)raw->data.mouse.usButtonData < 0)\n\t\t\t{\n\t\t\t\tIN_KeyEvent(mouse->qdeviceid, true, K_MWHEELDOWN, 0);\n\t\t\t\tIN_KeyEvent(mouse->qdeviceid, false, K_MWHEELDOWN, 0);\n\t\t\t}\n\t\t}\n\t}\n\t//button releass\n\tif (raw->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_1_UP)\n\t\tIN_KeyEvent(mouse->qdeviceid, false, K_MOUSE1, 0);\n\tif (raw->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_2_UP)\n\t\tIN_KeyEvent(mouse->qdeviceid, false, K_MOUSE2, 0);\n\tif (raw->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_3_UP)\n\t\tIN_KeyEvent(mouse->qdeviceid, false, K_MOUSE3, 0);\n\tif (raw->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_4_UP)\n\t\tIN_KeyEvent(mouse->qdeviceid, false, K_MOUSE4, 0);\n\tif (raw->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_5_UP)\n\t\tIN_KeyEvent(mouse->qdeviceid, false, K_MOUSE5, 0);\n\n\t// extra buttons\n\ttbuttons = raw->data.mouse.ulRawButtons & RI_RAWBUTTON_MASK;\n\tfor (j=6 ; j<rawmice[i].numbuttons ; j++)\n\t{\n\t\tif ( (tbuttons & (1<<j)) && !(rawmice[i].oldbuttons & (1<<j)) )\n\t\t{\n\t\t\tif (vid.activeapp)\n\t\t\t\tIN_KeyEvent (mouse->qdeviceid, true, K_MOUSE1 + j, 0);\n\t\t}\n\n\t\tif ( !(tbuttons & (1<<j)) && (rawmice[i].oldbuttons & (1<<j)) )\n\t\t{\n\t\t\tIN_KeyEvent (mouse->qdeviceid, false, K_MOUSE1 + j, 0);\n\t\t}\n\t}\n\n\trawmice[i].oldbuttons &= ~RI_RAWBUTTON_MASK;\n\trawmice[i].oldbuttons |= tbuttons;\n}\n\nvoid INS_RawInput_KeyboardRead(void)\n{\n\tint i;\n\tqboolean down;\n\tWPARAM wParam;\n\tLPARAM lParam;\n\n\tfor (i = 0; i < rawkbdcount; i++)\n\t{\n\t\tif (rawkbd[i].handles.rawinputhandle == raw->header.hDevice)\n\t\t\tbreak;\n\t}\n\n\tif (i == rawkbdcount) // not tracking this device\n\t\treturn;\n\n\tif (rawkbd[i].qdeviceid == DEVID_UNSET)\n\t\trawkbd[i].qdeviceid = Keyboard_AllocateDevID();\n\n\tdown = !(raw->data.keyboard.Flags & RI_KEY_BREAK);\n\twParam = raw->data.keyboard.VKey ;//(-down) & 0xC0000000;\n\tlParam = (MapVirtualKey(raw->data.keyboard.VKey, 0)<<16) | ((!!(raw->data.keyboard.Flags & RI_KEY_E0))<<24);\n\n \tINS_TranslateKeyEvent(wParam, lParam, down, rawkbd[i].qdeviceid, true);\n}\n\nvoid INS_RawInput_Read(HANDLE in_device_handle)\n{\n\tint dwSize;\n\n\t// get raw input\n\tif ((*_GRID)((HRAWINPUT)in_device_handle, RID_INPUT, NULL, &dwSize, sizeof(RAWINPUTHEADER)) == -1)\n\t{\n\t\tCon_Printf(\"Raw input: unable to add to get size of raw input header.\\n\");\n\t\treturn;\n\t}\n\n\tif (dwSize > ribuffersize)\n\t{\n\t\tribuffersize = dwSize;\n\t\traw = (RAWINPUT *)BZ_Realloc(raw, dwSize);\n\t}\n\n\tif ((*_GRID)((HRAWINPUT)in_device_handle, RID_INPUT, raw, &dwSize, sizeof(RAWINPUTHEADER)) != dwSize ) {\n\t\tCon_Printf(\"Raw input: unable to add to get raw input header.\\n\");\n\t\treturn;\n\t}\n\n\tINS_RawInput_MouseRead();\n\tINS_RawInput_KeyboardRead();\n}\n#else\nvoid INS_RawInput_Read(HANDLE in_device_handle)\n{\n}\n#endif\n\n/*\n===================\nINS_ClearStates\n===================\n*/\nvoid INS_ClearStates (void)\n{\n\n\tif (mouseactive)\n\t{\n\t\tmemset(&sysmouse, 0, sizeof(sysmouse));\n\t\tsysmouse.numbuttons = 10;\n\t}\n}\n\n/*\n===============\nINS_StartupJoystick\n===============\n*/\nstatic void INS_StartupJoystickId(unsigned int id)\n{\n\tJOYCAPS\t\tjc;\n\tMMRESULT\tmmr;\n\tstruct wjoy_s *joy;\n\tif (joy_count == MAX_JOYSTICKS)\n\t\treturn;\n\tjoy = &wjoy[joy_count];\n\tmemset(joy, 0, sizeof(*joy));\n\tjoy->id = id;\n\tjoy->devid = DEVID_UNSET;//1+joy_count;\t//FIXME: this is a hack. make joysticks 1-based. this means mouse0 controls 1st player, joy0 controls 2nd player (controls wrap so joy1 controls 1st player too.\n\n\t// get the capabilities of the selected joystick\n\t// abort startup if command fails\n\tmemset (&jc, 0, sizeof(jc));\n\tif ((mmr = joyGetDevCaps (joy->id, &jc, sizeof(jc))) != JOYERR_NOERROR)\n\t{\n\t\tCon_Printf (\"joystick %03x not found -- invalid joystick capabilities (%x)\\n\", id, mmr);\n\t\treturn;\n\t}\n\n\t// save the joystick's number of buttons and POV status\n\tjoy->numbuttons = min(countof(mmjbuttons), jc.wNumButtons);\n\tjoy->haspov = jc.wCaps & JOYCAPS_HASPOV;\n\n\t// mark the joystick as available and advanced initialization not completed\n\t// this is needed as cvars are not available during initialization\n\n\tjoy_advancedinit = false;\n\n\tjoy_count++;\n}\nstatic void IN_XInput_SetupAudio(struct wjoy_s *joy)\n{\n#ifdef AVAIL_XINPUT\n\tchar audiodevicename[MAX_QPATH];\n\twchar_t mssuck[MAX_QPATH];\n\tstatic GUID GUID_NULL;\n\tGUID gplayback = GUID_NULL;\n\tGUID gcapture = GUID_NULL;\n\n\tif (joy->audiodev)\n\t{\n\t\tS_ShutdownCard(joy->audiodev);\n\t\tjoy->audiodev = NULL;\n\t}\n\n\tif (!xinput_useaudio)\n\t\treturn;\n\n\tif (!joy->isxinput)\n\t\treturn;\n\tif (joy->devid == DEVID_UNSET)\n\t\treturn;\n\n\tif (pXInputGetDSoundAudioDeviceGuids)\n\t{\n\t\tif (pXInputGetDSoundAudioDeviceGuids(joy->id, &gplayback, &gcapture) != ERROR_SUCCESS)\n\t\t\treturn;\t//probably not plugged in\n\n\t\tif (!memcmp(&gplayback, &GUID_NULL, sizeof(gplayback)))\n\t\t\treturn;\t//we have a controller, but no headset.\n\n\t\tStringFromGUID2(&gplayback, mssuck, sizeof(mssuck)/sizeof(mssuck[0]));\n\t\tnarrowen(audiodevicename, sizeof(audiodevicename), mssuck);\n\t\tCon_Printf(\"Controller %i uses directsound device %s\\n\", joy->id, audiodevicename);\n\t\tjoy->audiodev = S_SetupDeviceSeat(\"DirectSound\", audiodevicename, joy->devid);\n\t}\n\telse if (pXInputGetAudioDeviceIds)\n\t{\n\t\tUINT wccount = countof(mssuck);\n\t\tif (!FAILED(pXInputGetAudioDeviceIds(joy->id, mssuck, &wccount, NULL,NULL/*we don't have separate mics, sadly, which also makes config awkward*/)))\n\t\t{\n\t        narrowen(audiodevicename, sizeof(audiodevicename), mssuck);\n\t\t\tCon_Printf(\"Controller %i uses xaudio2 device %s\\n\", joy->id, audiodevicename);\n\t\t\tjoy->audiodev = S_SetupDeviceSeat(\"XAudio2\", audiodevicename, joy->devid);\n\t\t}\n\t}\n#endif\n}\nvoid INS_SetupControllerAudioDevices(qboolean enabled)\n{\n#ifdef AVAIL_XINPUT\n\tint i;\n\n\txinput_useaudio = enabled;\n\tfor (i = 0; i < joy_count; i++)\n\t\tIN_XInput_SetupAudio(&wjoy[i]);\n#endif\n}\nvoid INS_StartupJoystick (void)\n{\n\tunsigned int\tid, numdevs;\n\tMMRESULT\tmmr;\n\n \t// assume no joysticks\n\tjoy_count = 0;\n\n#ifdef AVAIL_XINPUT\n\tif (in_xinput.ival)\n\t{\n\t\tstatic dllhandle_t *xinput;\n\t\tstatic const char *dllnames[] =\n\t\t{\n\t\t\t\"xinput1_4.dll\",\t//win8+ only. does xaudio2 instead of dsound.\n\t\t\t\"xinput1_3.dll\",\t//dxsdk (vista+). does dsound stuff.\n\t\t\t\"xinput9_1_0.dll\"\t//vista+. doesn't do audio stuff.\n\t\t};\n\t\tif (!xinput)\n\t\t{\n\t\t\tdllfunction_t funcs[] =\n\t\t\t{\n\t\t\t\t{(void**)&pXInputGetState, \"XInputGetState\"},\n\t\t\t\t{(void**)&pXInputSetState, \"XInputSetState\"},\n\t\t\t\t{NULL}\n\t\t\t};\n\t\t\tpXInputGetDSoundAudioDeviceGuids = NULL;\n\t\t\tpXInputGetAudioDeviceIds = NULL;\n\t\t\tfor (id = 0; id < countof(dllnames); id++)\n\t\t\t{\n\t\t\t\txinput = Sys_LoadLibrary(dllnames[id], funcs);\n\t\t\t\tif (xinput)\n\t\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tpXInputGetDSoundAudioDeviceGuids = xinput?Sys_GetAddressForName(xinput, \"XInputGetDSoundAudioDeviceGuids\"):NULL;\n\t\t\tpXInputGetAudioDeviceIds = xinput?Sys_GetAddressForName(xinput, \"XInputGetAudioDeviceIds\"):NULL;\n\t\t}\n\t\tif (pXInputGetState)\n\t\t{\n\t\t\tXINPUT_STATE xistate;\n\t\t\tnumdevs = 0;\n\t\t\tfor (id = 0; id < 4; id++)\n\t\t\t{\n\t\t\t\tif (joy_count == countof(wjoy))\n\t\t\t\t\tbreak;\n\t\t\t\tmemset(&wjoy[id], 0, sizeof(wjoy[id]));\n\t\t\t\twjoy[joy_count].isxinput = true;\n\t\t\t\twjoy[joy_count].id = id;\n\t\t\t\twjoy[joy_count].devid = DEVID_UNSET;//id;\n\t\t\t\twjoy[joy_count].numbuttons = countof(xinputjbuttons);\t//xinput supports 16 buttons, we emulate two more with the two triggers.\n\t\t\t\tjoy_count++;\n\n\t\t\t\tif (ERROR_DEVICE_NOT_AVAILABLE != pXInputGetState(id, &xistate))\n\t\t\t\t\tnumdevs++;\n\t\t\t}\n\t\t\tCon_DPrintf(\"XInput is enabled (%i controllers found)\\n\", numdevs);\n\t\t}\n\t\telse\n\t\t\tCon_Printf(\"XInput (%s) not installed\\n\", dllnames[1]);\n\t}\n#endif\n\n\t// abort startup if user requests no joystick\n\tif (!in_joystick.ival )\n\t\treturn;\n\n\t// verify joystick driver is present\n\tif ((numdevs = joyGetNumDevs ()) == 0)\n\t{\n\t\tCon_Printf (\"joystick not found -- driver not present\\n\");\n\t\treturn;\n\t}\n\n\tmmr = JOYERR_UNPLUGGED;\n\n\t// cycle through the joystick ids for the first valid one\n\tfor (id=0 ; id<numdevs ; id++)\n\t{\n\t\tmemset (&ji, 0, sizeof(ji));\n\t\tji.dwSize = sizeof(ji);\n\t\tji.dwFlags = JOY_RETURNCENTERED;\n\n\t\tif ((mmr = joyGetPosEx (id, &ji)) == JOYERR_NOERROR)\n\t\t{\n\t\t\tINS_StartupJoystickId(id);\n\t\t}\n\t}\n\n\tif (joy_count)\n\t\tCon_Printf (\"found %i joysticks\\n\", joy_count);\n\telse\n\t\tCon_DPrintf (\"found no joysticks\\n\");\n}\n\n/*\n===========\nINS_Commands\n===========\n*/\nvoid INS_Commands (void)\n{\n\tint\t\ti;\n\tDWORD\tbuttonstate, povstate;\n\tunsigned int idx;\n\tstruct wjoy_s *joy;\n\n\tfor (idx = 0; idx < joy_count; idx++)\n\t{\n\t\tjoy = &wjoy[idx];\n\n\t\t// loop through the joystick buttons\n\t\t// key a joystick event or auxillary event for higher number buttons for each state change\n\t\tbuttonstate = joy->buttonstate;\n\t\tif (buttonstate != joy->oldbuttonstate)\n\t\t{\n\t\t\tif (joy->devid == DEVID_UNSET)\n\t\t\t{\n\t\t\t\tjoy->devid = Joy_AllocateDevID();\n\t\t\t\tIN_XInput_SetupAudio(joy);\n\t\t\t}\n\n\t\t\tif (joy->isxinput)\n\t\t\t{\n\t\t\t\tfor (i=0 ; i < joy->numbuttons ; i++)\n\t\t\t\t{\n\t\t\t\t\tif ( (buttonstate & (1<<i)) && !(joy->oldbuttonstate & (1<<i)) )\n\t\t\t\t\t\tKey_Event (joy->devid, xinputjbuttons[i], 0, true);\n\n\t\t\t\t\tif ( !(buttonstate & (1<<i)) && (joy->oldbuttonstate & (1<<i)) )\n\t\t\t\t\t\tKey_Event (joy->devid, xinputjbuttons[i], 0, false);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tfor (i=0 ; i < joy->numbuttons ; i++)\n\t\t\t\t{\n\t\t\t\t\tif ( (buttonstate & (1<<i)) && !(joy->oldbuttonstate & (1<<i)) )\n\t\t\t\t\t\tKey_Event (joy->devid, mmjbuttons[i], 0, true);\n\n\t\t\t\t\tif ( !(buttonstate & (1<<i)) && (joy->oldbuttonstate & (1<<i)) )\n\t\t\t\t\t\tKey_Event (joy->devid, mmjbuttons[i], 0, false);\n\t\t\t\t}\n\t\t\t}\n\t\t\tjoy->oldbuttonstate = buttonstate;\n\t\t}\n\n\t\tif (joy->haspov)\n\t\t{\n\t\t\t// convert POV information into 4 bits of state information\n\t\t\t// this avoids any potential problems related to moving from one\n\t\t\t// direction to another without going through the center position\n\t\t\tpovstate = 0;\n\t\t\tif(joy->povstate != JOY_POVCENTERED)\n\t\t\t{\n\t\t\t\tif (joy->povstate == JOY_POVFORWARD)\n\t\t\t\t\tpovstate |= 0x01;\n\t\t\t\tif (joy->povstate == JOY_POVRIGHT)\n\t\t\t\t\tpovstate |= 0x02;\n\t\t\t\tif (joy->povstate == JOY_POVBACKWARD)\n\t\t\t\t\tpovstate |= 0x04;\n\t\t\t\tif (joy->povstate == JOY_POVLEFT)\n\t\t\t\t\tpovstate |= 0x08;\n\t\t\t}\n\t\t\tif (joy->oldpovstate != povstate)\n\t\t\t{\n\t\t\t\tif (joy->devid == DEVID_UNSET)\n\t\t\t\t\tjoy->devid = Joy_AllocateDevID();\n\n\t\t\t\t// determine which bits have changed and key an auxillary event for each change\n\t\t\t\tfor (i=0 ; i < 4 ; i++)\n\t\t\t\t{\n\t\t\t\t\tif ( (povstate & (1<<i)) && !(joy->oldpovstate & (1<<i)) )\n\t\t\t\t\t{\n\t\t\t\t\t\tKey_Event (joy->devid, K_AUX13 + i, 0, true);\n\t\t\t\t\t}\n\n\t\t\t\t\tif ( !(povstate & (1<<i)) && (joy->oldpovstate & (1<<i)) )\n\t\t\t\t\t{\n\t\t\t\t\t\tKey_Event (joy->devid, K_AUX13 + i, 0, false);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tjoy->oldpovstate = povstate;\n\t\t\t}\n\t\t}\n\t}\n}\n\n\nvoid INS_DeviceChanged(void *ctx, void *data, size_t a ,size_t b)\n{\t//called on WM_DEVICECHANGE\n\tunsigned int idx;\n\tfor (idx = 0; idx < joy_count; idx++)\n\t{\n\t\twjoy[idx].devstate = DS_UNKNOWN;\n\t}\n}\n/*\n===============\nINS_ReadJoystick\n===============\n*/\nstatic qboolean INS_ReadJoystick (struct wjoy_s *joy)\n{\n\tif (joy->devstate != DS_NOTPRESENT)\n\t{\t//xinput samples all basically say to poll, but that gives really shit performance.\n\t\t//instead we wait until our window thread gets a WM_DEVICECHANGE before we restart polling an inactive device.\n\t\t//this can safe us a couple ms each frame.\n\n#ifdef AVAIL_XINPUT\n\t\tif (joy->isxinput)\n\t\t{\n\t\t\tXINPUT_STATE xistate;\n\t\t\tXINPUT_VIBRATION vibrator;\n\t\t\tHRESULT hr;\n\t\t\tmemset(&xistate, 0, sizeof(xistate));\n\t\t\thr = pXInputGetState(joy->id, &xistate);\n\n\t\t\tif (in_xinput.ival == 2)\n\t\t\t{\n\t\t\t\tCon_Printf(\"xi%i %s, b:%#x r:%u,%u l:%u,%u t:%u,%u\\n\", joy->id, (hr==ERROR_SUCCESS)?\"success\":va(\"%lx\", hr), xistate.Gamepad.wButtons,\n\t\t\t\t\txistate.Gamepad.sThumbRX, xistate.Gamepad.sThumbRY, \n\t\t\t\t\txistate.Gamepad.sThumbLX,xistate.Gamepad.sThumbLY,\n\t\t\t\t\txistate.Gamepad.bLeftTrigger,xistate.Gamepad.bRightTrigger);\n\t\t\t}\n\t\t\telse if (in_xinput.ival == 3 && joy->id == 0)\n\t\t\t{\n\t\t\t//I don't have a controller to test this with, so we fake stuff.\n\t\t\t\tPOINT p;\n\t\t\t\tGetCursorPos(&p);\n\t\t\t\thr = ERROR_SUCCESS;\n\t\t\t\txistate.Gamepad.wButtons = rand() & 0xfff0;\n\t\t\t\txistate.Gamepad.sThumbRX = 0;//(p.x/1920.0)*0xffff - 0x8000;\n\t\t\t\txistate.Gamepad.sThumbRY = 0;//(p.y/1080.0)*0xffff - 0x8000;\n\t\t\t\txistate.Gamepad.sThumbLX = (p.x/1920.0)*0xffff - 0x8000;\n\t\t\t\txistate.Gamepad.sThumbLY = (p.y/1080.0)*0xffff - 0x8000;\n\t\t\t\txistate.Gamepad.bLeftTrigger = 0;\n\t\t\t\txistate.Gamepad.bRightTrigger = 0;\n\t\t\t}\n\n\t\t\tif (hr == ERROR_SUCCESS)\n\t\t\t{\t//ERROR_SUCCESS\n\t\t\t\t//do we care about the dwPacketNumber?\n\t\t\t\tjoy->devstate = DS_PRESENT;\n\t\t\t\tjoy->buttonstate = xistate.Gamepad.wButtons & 0xffff;\n\n\t\t\t\tif (xistate.Gamepad.sThumbLY < -16364)\n\t\t\t\t\tjoy->buttonstate |= 0x00010000;\n\t\t\t\tif (xistate.Gamepad.sThumbLY > 16364)\n\t\t\t\t\tjoy->buttonstate |= 0x00020000;\n\t\t\t\tif (xistate.Gamepad.sThumbLX < -16364)\n\t\t\t\t\tjoy->buttonstate |= 0x00040000;\n\t\t\t\tif (xistate.Gamepad.sThumbLX > 16364)\n\t\t\t\t\tjoy->buttonstate |= 0x00080000;\n\n\t\t\t\tif (xistate.Gamepad.sThumbRY < -16364)\n\t\t\t\t\tjoy->buttonstate |= 0x00100000;\n\t\t\t\tif (xistate.Gamepad.sThumbRY > 16364)\n\t\t\t\t\tjoy->buttonstate |= 0x00200000;\n\t\t\t\tif (xistate.Gamepad.sThumbRX < -16364)\n\t\t\t\t\tjoy->buttonstate |= 0x00400000;\n\t\t\t\tif (xistate.Gamepad.sThumbRX > 16364)\n\t\t\t\t\tjoy->buttonstate |= 0x00800000;\n\n\t\t\t\tif (xistate.Gamepad.bLeftTrigger >= 128)\n\t\t\t\t\tjoy->buttonstate |= 0x01000000;\n\t\t\t\tif (xistate.Gamepad.bRightTrigger >= 128)\n\t\t\t\t\tjoy->buttonstate |= 0x02000000;\n\n\t\t\t\tif (joy->devid != DEVID_UNSET)\n\t\t\t\t{\n\t\t\t\t\tIN_JoystickAxisEvent(joy->devid, GPAXIS_LT_RIGHT, xistate.Gamepad.sThumbLX / 32768.0);\n\t\t\t\t\tIN_JoystickAxisEvent(joy->devid, GPAXIS_LT_DOWN, -xistate.Gamepad.sThumbLY / 32768.0);\n\t\t\t\t\tIN_JoystickAxisEvent(joy->devid, GPAXIS_LT_TRIGGER, xistate.Gamepad.bLeftTrigger/255.0);\n\t\t\t\t\tIN_JoystickAxisEvent(joy->devid, GPAXIS_RT_RIGHT, xistate.Gamepad.sThumbRX / 32768.0);\n\t\t\t\t\tIN_JoystickAxisEvent(joy->devid, GPAXIS_RT_DOWN, -xistate.Gamepad.sThumbRY / 32768.0);\n\t\t\t\t\tIN_JoystickAxisEvent(joy->devid, GPAXIS_RT_TRIGGER, xistate.Gamepad.bRightTrigger/255.0);\n\n\t\t\t\t\tvibrator.wLeftMotorSpeed = xinput_leftvibrator.value * 0xffff;\n\t\t\t\t\tvibrator.wRightMotorSpeed = xinput_rightvibrator.value * 0xffff;\n\t\t\t\t\tpXInputSetState(joy->id, &vibrator);\n\t\t\t\t}\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\telse if (hr == ERROR_DEVICE_NOT_CONNECTED)\n\t\t\t\tjoy->devstate = DS_NOTPRESENT;\n\t\t}\n\t\telse\n#endif\n\t\t{\n\t\t\tmemset (&ji, 0, sizeof(ji));\n\t\t\tji.dwSize = sizeof(ji);\n\t\t\tji.dwFlags = joy_flags;\n\n\t\t\tif (joyGetPosEx (joy->id, &ji) == JOYERR_NOERROR)\n\t\t\t{\n\t\t\t\tjoy->devstate = DS_PRESENT;\n\t\t\t\tjoy->povstate = ji.dwPOV;\n\t\t\t\tjoy->buttonstate = ji.dwButtons;\n\t\t\t\tif (joy->devid != DEVID_UNSET)\n\t\t\t\t{\n\t\t\t\t\tIN_JoystickAxisEvent(joy->devid, GPAXIS_LT_RIGHT, (ji.dwXpos - 32768.0) / 32768);\n\t\t\t\t\tIN_JoystickAxisEvent(joy->devid, GPAXIS_LT_DOWN, (ji.dwYpos - 32768.0) / 32768);\n\t\t\t\t\tIN_JoystickAxisEvent(joy->devid, GPAXIS_LT_AUX, (ji.dwZpos - 32768.0) / 32768);\n\t\t\t\t\tIN_JoystickAxisEvent(joy->devid, GPAXIS_RT_RIGHT, (ji.dwRpos - 32768.0) / 32768);\n\t\t\t\t\tIN_JoystickAxisEvent(joy->devid, GPAXIS_RT_DOWN, (ji.dwUpos - 32768.0) / 32768);\n\t\t\t\t\tIN_JoystickAxisEvent(joy->devid, GPAXIS_RT_AUX, (ji.dwVpos - 32768.0) / 32768);\n\t\t\t\t}\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\telse\n\t\t\t\tjoy->devstate = DS_NOTPRESENT;\n\t\t}\n\t}\n\n\tjoy->povstate = 0;\n\tjoy->buttonstate = 0;\n\tif (joy->devid != DEVID_UNSET)\n\t{\n\t\tIN_JoystickAxisEvent(joy->devid, 0, 0);\n\t\tIN_JoystickAxisEvent(joy->devid, 1, 0);\n\t\tIN_JoystickAxisEvent(joy->devid, 2, 0);\n\t\tIN_JoystickAxisEvent(joy->devid, 3, 0);\n\t\tIN_JoystickAxisEvent(joy->devid, 4, 0);\n\t\tIN_JoystickAxisEvent(joy->devid, 5, 0);\n\t}\n\n\t// read error occurred\n\t// turning off the joystick seems too harsh for 1 read error,\n\t// but what should be done?\n\t// Con_Printf (\"INS_ReadJoystick: no response\\n\");\n\t// joy_avail = false;\n\treturn false;\n}\n\n/*\n===========\nINS_JoyMove\n===========\n*/\nvoid INS_JoyMove (void)\n{\n\tunsigned int idx;\n\tfor (idx = 0; idx < joy_count; idx++)\n\t{\n\t\tINS_ReadJoystick(&wjoy[idx]);\n\t}\n}\n\nvoid INS_EnumerateDevices(void *ctx, void(*callback)(void *ctx, const char *type, const char *devicename, unsigned int *qdevid))\n{\n\tint idx;\n\n\tfor (idx = 0; idx < rawkbdcount; idx++)\n\t\tcallback(ctx, \"keyboard\", rawkbd[idx].sysname?rawkbd[idx].sysname:va(\"rawk%i\", idx), &rawkbd[idx].qdeviceid);\n\tcallback(ctx, \"keyboard\", \"system\", NULL);\n\n\tfor (idx = 0; idx < rawmicecount; idx++)\n\t\tcallback(ctx, \"mouse\", rawmice[idx].sysname?rawmice[idx].sysname:va(\"raw%i\", idx), &rawmice[idx].qdeviceid);\n\n#ifdef AVAIL_DINPUT\n#if (DIRECTINPUT_VERSION >= DINPUT_VERSION_DX7)\n\tif (dinput >= DINPUT_VERSION_DX7 && g_pMouse7)\n\t\tcallback(ctx, \"mouse\", \"di7\", NULL);\n\telse\n#endif\n\t\tif (dinput && g_pMouse)\n\t\tcallback(ctx, \"mouse\", \"di\", NULL);\n#endif\n\tcallback(ctx, \"mouse\", \"system\", NULL);\n\n\tfor (idx = 0; idx < joy_count; idx++)\n\t{\n\t\tint odevid = wjoy[idx].devid;\n\t\tif (wjoy[idx].isxinput)\n\t\t\tcallback(ctx, \"joy\", va(\"xi%i\", wjoy[idx].id), &wjoy[idx].devid);\n\t\telse\n\t\t\tcallback(ctx, \"joy\", va(\"mmj%i\", wjoy[idx].id), &wjoy[idx].devid);\n\t\tif (odevid != wjoy[idx].devid)\n\t\t\tIN_XInput_SetupAudio(&wjoy[idx]);\n\t}\n}\n\nstatic unsigned short        scantokey[] =\n{\n//\t0\t\t\t1\t\t\t2\t\t\t3\t\t\t4\t\t\t5\t\t\t6\t\t\t\t7\n//\t8\t\t\t9\t\t\tA\t\t\tB\t\t\tC\t\t\tD\t\t\tE\t\t\t\tF\n\t0,\t\t\t27,\t\t\t'1',\t\t'2',\t\t'3',\t\t'4',\t\t'5',\t\t\t'6',\t\t// 0\n\t'7',\t\t'8',\t\t'9',\t\t'0',\t\t'-',\t\t'=',\t\tK_BACKSPACE,\t9,\t\t\t// 0\n\t'q',\t\t'w',\t\t'e',\t\t'r',\t\t't',\t\t'y',\t\t'u',\t\t\t'i',\t\t// 1\n\t'o',\t\t'p',\t\t'[',\t\t']',\t\tK_ENTER,\tK_LCTRL,\t'a',\t\t\t's',\t\t// 1\n\t'd',\t\t'f',\t\t'g',\t\t'h',\t\t'j',\t\t'k',\t\t'l',\t\t\t';',\t\t// 2\n\t'\\'',\t\t'`',\t\tK_LSHIFT,\t'\\\\',\t\t'z',\t\t'x',\t\t'c',\t\t\t'v',\t\t// 2\n\t'b',\t\t'n',\t\t'm',\t\t',',\t\t'.',\t\t'/',\t\tK_RSHIFT,\t\tK_KP_STAR,\t// 3\n\tK_LALT,\t\t' ',\t\tK_CAPSLOCK,\tK_F1,\t\tK_F2,\t\tK_F3,\t\tK_F4,\t\t\tK_F5,\t\t// 3\n\tK_F6,\t\tK_F7,\t\tK_F8,\t\tK_F9,\t\tK_F10,\t\tK_PAUSE,\tK_SCRLCK,\t\tK_KP_HOME,\t\t// 4\n\tK_KP_UPARROW,K_KP_PGUP,\tK_KP_MINUS,\tK_KP_LEFTARROW,K_KP_5,\tK_KP_RIGHTARROW,K_KP_PLUS,\tK_KP_END,\t\t// 4\n\tK_KP_DOWNARROW,K_KP_PGDN,K_KP_INS,\tK_KP_DEL,\t0,\t\t\t0,\t\t\t0,\t\t\t\tK_F11,\t\t// 5\n\tK_F12,\t\t0,\t\t\t0,\t\t\t0,\t\t\t0,\t\t\t0,\t\t\t0,\t\t\t\t0,\t\t\t// 5\n\t0,\t\t\t0,\t\t\t0,\t\t\t0,\t\t\t0,\t\t\t'\\\\',\t\t0,\t\t\t\t0,\t\t\t// 6\n\t0,\t\t\t0,\t\t\t0,\t\t\t0,\t\t\t0,\t\t\t0,\t\t\t0,\t\t\t\t0,\t\t\t// 6\n\t0,\t\t\t0,\t\t\t0,\t\t\t0,\t\t\t0,\t\t\t0,\t\t\t0,\t\t\t\t0,\t\t\t// 7\n\t0,\t\t\t0,\t\t\t0,\t\t\t0,\t\t\t0,\t\t\t0,\t\t\t0,\t\t\t\t0,\t\t\t// 7\n//\t0\t\t\t1\t\t\t2\t\t\t3\t\t\t4\t\t\t5\t\t\t6\t\t\t\t7\n//\t8\t\t\t9\t\t\tA\t\t\tB\t\t\tC\t\t\tD\t\t\tE\t\t\t\tF\n\t0,\t\t\t0,\t\t\t0,\t\t\t0,\t\t\t0,\t\t\t0,\t\t\t0,\t\t\t\t0,\t\t\t// 8\n\t0,\t\t\t0,\t\t\t0,\t\t\t0,\t\t\t0,\t\t\t0,\t\t\t0,\t\t\t\t0,\t\t\t// 8\n\t0,\t\t\t0,\t\t\t0,\t\t\t0,\t\t\t0,\t\t\t0,\t\t\t0,\t\t\t\t0,\t\t\t// 9\n\t0,\t\t\t0,\t\t\t0,\t\t\t0,\t\t\t0,\t\t\t0,\t\t\t0,\t\t\t\t0,\t\t\t// 9\n\t0,\t\t\t0,\t\t\t0,\t\t\t0,\t\t\t0,\t\t\t0,\t\t\t0,\t\t\t\t0,\t\t\t// a\n\t0,\t\t\t0,\t\t\t0,\t\t\t0,\t\t\t0,\t\t\t0,\t\t\t0,\t\t\t\t0,\t\t\t// a\n\t0,\t\t\t0,\t\t\t0,\t\t\t0,\t\t\t0,\t\t\t0,\t\t\t0,\t\t\t\t0,\t\t\t// b\n\t0,\t\t\t0,\t\t\t0,\t\t\t0,\t\t\t0,\t\t\t0,\t\t\t0,\t\t\t\t0,\t\t\t// b\n\t0,\t\t\t0,\t\t\t0,\t\t\t0,\t\t\t0,\t\t\t0,\t\t\t0,\t\t\t\t0,\t\t\t// c\n\t0,\t\t\t0,\t\t\t0,\t\t\t0,\t\t\t0,\t\t\t0,\t\t\t0,\t\t\t\t0,\t\t\t// c\n\t0,\t\t\t0,\t\t\t0,\t\t\t0,\t\t\t0,\t\t\t0,\t\t\t0,\t\t\t\t0,\t\t\t// d\n\t0,\t\t\t0,\t\t\t0,\t\t\t0,\t\t\t0,\t\t\t0,\t\t\t0,\t\t\t\t0,\t\t\t// d\n\t0,\t\t\t0,\t\t\t0,\t\t\t0,\t\t\t0,\t\t\t0,\t\t\t0,\t\t\t\t0,\t\t\t// e\n\t0,\t\t\t0,\t\t\t0,\t\t\t0,\t\t\t0,\t\t\t0,\t\t\t0,\t\t\t\t0,\t\t\t// e\n\t0,\t\t\t0,\t\t\t0,\t\t\t0,\t\t\t0,\t\t\t0,\t\t\t0,\t\t\t\t0,\t\t\t// f\n\t0,\t\t\t0,\t\t\t0,\t\t\t0,\t\t\t0,\t\t\t0,\t\t\t0,\t\t\t\t0,\t\t\t// f\n//\t0\t\t\t1\t\t\t2\t\t\t3\t\t\t4\t\t\t5\t\t\t6\t\t\t\t7\n//\t8\t\t\t9\t\t\tA\t\t\tB\t\t\tC\t\t\tD\t\t\tE\t\t\t\tF\n\t0,\t\t\t27,\t\t\t'1',\t\t'2',\t\t'3',\t\t'4',\t\t'5',\t\t\t'6',\t\t// 0\n\t'7',\t\t'8',\t\t'9',\t\t'0',\t\t'-',\t\t'=',\t\tK_BACKSPACE,\t9,\t\t\t// 0\n\t'q',\t\t'w',\t\t'e',\t\t'r',\t\t't',\t\t'y',\t\t'u',\t\t\t'i',\t\t// 1\n\t'o',\t\t'p',\t\t'[',\t\t']',\t\tK_KP_ENTER,\tK_RCTRL,\t'a',\t\t\t's',\t\t// 1\n\t'd',\t\t'f',\t\t'g',\t\t'h',\t\t'j',\t\t'k',\t\t'l',\t\t\t';',\t\t// 2\n\t'\\'',\t\t'`',\t\tK_SHIFT,\t'\\\\',\t\t'z',\t\t'x',\t\t'c',\t\t\t'v',\t\t// 2\n\t'b',\t\t'n',\t\t'm',\t\t',',\t\t'.',\t\tK_KP_SLASH,\tK_SHIFT,\t\tK_PRINTSCREEN,// 3\n\tK_RALT,\t\t' ',\t\tK_CAPSLOCK,\tK_F1,\t\tK_F2,\t\tK_F3,\t\tK_F4,\t\t\tK_F5,\t\t// 3\n\tK_F6,\t\tK_F7,\t\tK_F8,\t\tK_F9,\t\tK_F10,\t\tK_KP_NUMLOCK,K_SCRLCK,\t\tK_HOME,\t\t// 4\n\tK_UPARROW,\tK_PGUP,\t\t'-',\t\tK_LEFTARROW,0,\t\t\tK_RIGHTARROW,'+',\t\t\tK_END,\t\t// 4\n\tK_DOWNARROW,K_PGDN,\t\tK_INS,\t\tK_DEL,\t\t0,\t\t\t0,\t\t\t0,\t\t\t\tK_F11,\t\t// 5\n\tK_F12,\t\t0,\t\t\t0,\t\t\t0,\t\t\t0,\t\t\t0,\t\t\t0,\t\t\t\t0,\t\t\t// 5\n\t0,\t\t\t0,\t\t\t0,\t\t\t0,\t\t\t0,\t\t\t'\\\\',\t\t0,\t\t\t\t0,\t\t\t// 6\n\t0,\t\t\t0,\t\t\t0,\t\t\t0,\t\t\t0,\t\t\t0,\t\t\t0,\t\t\t\t0,\t\t\t// 6\n\t0,\t\t\t0,\t\t\t0,\t\t\t0,\t\t\t0,\t\t\t0,\t\t\t0,\t\t\t\t0,\t\t\t// 7\n\t0,\t\t\t0,\t\t\t0,\t\t\t0,\t\t\t0,\t\t\t0,\t\t\t0,\t\t\t\t0\t\t\t// 7\n//\t0\t\t\t1\t\t\t2\t\t\t3\t\t\t4\t\t\t5\t\t\t6\t\t\t\t7\n//\t8\t\t\t9\t\t\tA\t\t\tB\t\t\tC\t\t\tD\t\t\tE\t\t\t\tF\n};\n\n/*\n=======\nMapKey\n\nMap from windows to quake keynums\n=======\n*/\nstatic int MapKey (int vkey)\n{\n\tint key;\n\tkey = (vkey>>16)&511;\n\n\tif (key < sizeof(scantokey) / sizeof(scantokey[0]))\n\t\tkey = scantokey[key];\n\telse\n\t\tkey = 0;\n\tif (!cl_keypad.ival)\n\t{\n\t\tswitch(key)\n\t\t{\n\t\tcase K_KP_HOME:\t\t\treturn '7';\n\t\tcase K_KP_UPARROW:\t\treturn '8';\n\t\tcase K_KP_PGUP:\t\t\treturn '9';\n\t\tcase K_KP_LEFTARROW:\treturn '4';\n\t\tcase K_KP_5:\t\t\treturn '5';\n\t\tcase K_KP_RIGHTARROW:\treturn '6';\n\t\tcase K_KP_END:\t\t\treturn '1';\n\t\tcase K_KP_DOWNARROW:\treturn '2';\n\t\tcase K_KP_PGDN:\t\t\treturn '3';\n\t\tcase K_KP_ENTER:\t\treturn K_ENTER;\n\t\tcase K_KP_INS:\t\t\treturn '0';\n\t\tcase K_KP_DEL:\t\t\treturn '.';\n\t\tcase K_KP_SLASH:\t\treturn '/';\n\t\tcase K_KP_MINUS:\t\treturn '-';\n\t\tcase K_KP_PLUS:\t\t\treturn '+';\n\t\tcase K_KP_STAR:\t\t\treturn '*';\n\t\tcase K_KP_EQUALS:\t\treturn '=';\n\t\t}\n\t}\n\tif (key == 0)\n\t\tCon_DPrintf(\"key 0x%02x has no translation\\n\", key);\n\n\treturn key;\n}\n\nvoid INS_TranslateKeyEvent(WPARAM wParam, LPARAM lParam, qboolean down, int qdeviceid, qboolean genkeystate)\n{\n\textern cvar_t in_builtinkeymap;\n\tint qcode;\n\tint unicode;\n\tint chars;\n\textern int\t\tkeyshift[K_MAX];\n\textern int\t\tshift_down;\n\n\tqcode = MapKey(lParam);\n\n\tif (WinNT && !in_builtinkeymap.value)\n\t{\n\t\tBYTE\tkeystate[256];\n\t\tWCHAR\twchars[2];\n\n\t\tif (genkeystate)\n\t\t{\n\t\t\textern qboolean\tkeydown[K_MAX];\n\t\t\tmemset(keystate, 0, sizeof(keystate));\n\t\t\t//128 for held.\n\t\t\t//1 for toggled (ie: caps / num)\n\t\t\tkeystate[VK_LSHIFT] = 128*!!keydown[K_LSHIFT];\n\t\t\tkeystate[VK_RSHIFT] = 128*!!keydown[K_RSHIFT];\n\t\t\tkeystate[VK_LCONTROL] = 128*!!keydown[K_LCTRL];\n\t\t\tkeystate[VK_RCONTROL] = 128*!!keydown[K_RCTRL];\n\t\t\tkeystate[VK_LMENU] = 128*!!keydown[K_LALT];\n\t\t\tkeystate[VK_RMENU] = 128*!!keydown[K_RALT];\n\n\t\t\t//seems to matter\n\t\t\tkeystate[VK_SHIFT] = keystate[VK_LSHIFT]|keystate[VK_RSHIFT];\n\t\t\tkeystate[VK_CONTROL] = keystate[VK_LCONTROL]|keystate[VK_RCONTROL];\n\t\t\tkeystate[VK_MENU] = keystate[VK_LMENU]|keystate[VK_RMENU];\n\n\t\t\tkeystate[VK_NUMLOCK] = 1;\t//doesn't seem to matter?\n\t\t}\n\t\telse\n\t\t\tGetKeyboardState(keystate);\n\t\tchars = ToUnicode(wParam, HIWORD(lParam), keystate, wchars, sizeof(wchars)/sizeof(wchars[0]), 0);\n\t\n\t\tif (chars > 0)\n\t\t{\n\t\t\tif (!in_nonstandarddeadkeys.ival)\n\t\t\t{\n\t\t\t\tfor (unicode = 0; unicode < chars-1; unicode++)\n\t\t\t\t\tIN_KeyEvent(qdeviceid, down, 0, wchars[unicode]);\n\t\t\t}\n\t\t\tunicode = wchars[chars-1];\n\t\t}\n\t\telse unicode = 0;\n\t}\n\telse\n\t{\n\t\tunicode = (qcode < 128)?qcode:0;\n\t\tif (shift_down && unicode < K_MAX && keyshift[unicode])\n\t\t\tunicode = keyshift[unicode]; \n\t}\n\tIN_KeyEvent(qdeviceid, down, qcode, unicode);\n}\n\nqboolean INS_KeyToLocalName(int qkey, char *buf, size_t bufsize)\n{\n\tint i;\n\t*buf = 0;\t//assume failure\n\tfor (i = 0; i < countof(scantokey); i++)\n\t{\n\t\tif (!scantokey[i])\n\t\t\tcontinue;\t//not a vkey that quake understands\n\t\tif (scantokey[i] == qkey)\n\t\t{\n\t\t\twchar_t tmpbuf[64];\n\t\t\tif (GetKeyNameTextW(i<<16, tmpbuf, sizeof(tmpbuf)))\n\t\t\t{\n\t\t\t\tnarrowen(buf, bufsize, tmpbuf);\t//yay for utf-8\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t}\n\treturn false;\n}\n\n\n#ifndef APPCOMMAND_BROWSER_BACKWARD\t\t\t//added in win2k (but was probably used before that too)\n#define APPCOMMAND_BROWSER_BACKWARD       1\n#define APPCOMMAND_BROWSER_FORWARD        2\n#define APPCOMMAND_BROWSER_REFRESH        3\n#define APPCOMMAND_BROWSER_STOP           4\n#define APPCOMMAND_BROWSER_SEARCH         5\n#define APPCOMMAND_BROWSER_FAVORITES      6\n#define APPCOMMAND_BROWSER_HOME           7\n#define APPCOMMAND_VOLUME_MUTE            8\n#define APPCOMMAND_VOLUME_DOWN            9\n#define APPCOMMAND_VOLUME_UP              10\n#define APPCOMMAND_MEDIA_NEXTTRACK        11\n#define APPCOMMAND_MEDIA_PREVIOUSTRACK    12\n#define APPCOMMAND_MEDIA_STOP             13\n#define APPCOMMAND_MEDIA_PLAY_PAUSE       14\n#define APPCOMMAND_LAUNCH_MAIL            15\n#define APPCOMMAND_LAUNCH_MEDIA_SELECT    16\n#define APPCOMMAND_LAUNCH_APP1            17\n#define APPCOMMAND_LAUNCH_APP2            18\n#define APPCOMMAND_BASS_DOWN              19\n#define APPCOMMAND_BASS_BOOST             20\n#define APPCOMMAND_BASS_UP                21\n#define APPCOMMAND_TREBLE_DOWN            22\n#define APPCOMMAND_TREBLE_UP              23\n#endif\n#ifndef APPCOMMAND_MICROPHONE_VOLUME_MUTE\t//added in winxp\n#define APPCOMMAND_MICROPHONE_VOLUME_MUTE 24\n#define APPCOMMAND_MICROPHONE_VOLUME_DOWN 25\n#define APPCOMMAND_MICROPHONE_VOLUME_UP   26\n#define APPCOMMAND_HELP                   27\n#define APPCOMMAND_FIND                   28\n#define APPCOMMAND_NEW                    29\n#define APPCOMMAND_OPEN                   30\n#define APPCOMMAND_CLOSE                  31\n#define APPCOMMAND_SAVE                   32\n#define APPCOMMAND_PRINT                  33\n#define APPCOMMAND_UNDO                   34\n#define APPCOMMAND_REDO                   35\n#define APPCOMMAND_COPY                   36\n#define APPCOMMAND_CUT                    37\n#define APPCOMMAND_PASTE                  38\n#define APPCOMMAND_REPLY_TO_MAIL          39\n#define APPCOMMAND_FORWARD_MAIL           40\n#define APPCOMMAND_SEND_MAIL              41\n#define APPCOMMAND_SPELL_CHECK            42\n#define APPCOMMAND_DICTATE_OR_COMMAND_CONTROL_TOGGLE    43\n#define APPCOMMAND_MIC_ON_OFF_TOGGLE      44\n#define APPCOMMAND_CORRECTION_LIST        45\n#define APPCOMMAND_MEDIA_PLAY             46\n#define APPCOMMAND_MEDIA_PAUSE            47\n#define APPCOMMAND_MEDIA_RECORD           48\n#define APPCOMMAND_MEDIA_FAST_FORWARD     49\n#define APPCOMMAND_MEDIA_REWIND           50\n#define APPCOMMAND_MEDIA_CHANNEL_UP       51\n#define APPCOMMAND_MEDIA_CHANNEL_DOWN     52\n#endif\n\nint INS_AppCommand(LPARAM lParam)\n{\n\tconst char *b;\n\tint qkey = 0;\n\tswitch(HIWORD(lParam)&0xfff)\n\t{\n\tcase APPCOMMAND_BROWSER_BACKWARD:\tqkey = K_MM_BROWSER_BACK;\t\t\tbreak;\n\tcase APPCOMMAND_BROWSER_FAVORITES:\tqkey = K_MM_BROWSER_FAVORITES;\t\tbreak;\n\tcase APPCOMMAND_BROWSER_FORWARD:\tqkey = K_MM_BROWSER_FORWARD;\t\tbreak;\n\tcase APPCOMMAND_BROWSER_HOME:\t\tqkey = K_MM_BROWSER_HOME;\t\t\tbreak;\n\tcase APPCOMMAND_BROWSER_REFRESH:\tqkey = K_MM_BROWSER_REFRESH;\t\tbreak;\n\tcase APPCOMMAND_BROWSER_SEARCH:\t\tqkey = K_SEARCH;\t\t\t\t\tbreak;\n\tcase APPCOMMAND_BROWSER_STOP:\t\tqkey = K_MM_BROWSER_STOP;\t\t\tbreak;\n\tcase APPCOMMAND_VOLUME_MUTE:\t\tqkey = K_MM_VOLUME_MUTE;\t\t\tbreak;\n\tcase APPCOMMAND_VOLUME_UP:\t\t\tqkey = K_VOLUP;\t\t\t\t\t\tbreak;\n\tcase APPCOMMAND_VOLUME_DOWN:\t\tqkey = K_VOLDOWN;\t\t\t\t\tbreak;\n\tcase APPCOMMAND_MEDIA_NEXTTRACK:\tqkey = K_MM_TRACK_NEXT;\t\t\t\tbreak;\n\tcase APPCOMMAND_MEDIA_PREVIOUSTRACK:qkey = K_MM_TRACK_PREV;\t\t\t\tbreak;\n\tcase APPCOMMAND_MEDIA_STOP:\t\t\tqkey = K_MM_TRACK_STOP;\t\t\t\tbreak;\n\tcase APPCOMMAND_MEDIA_PLAY_PAUSE:\tqkey = K_MM_TRACK_PLAYPAUSE;\t\tbreak;\n\tdefault:\n\t\treturn false;\n\t}\n\tb = Key_GetBinding(qkey, 0, 0);\n\tif (b && *b)\n\t{\t//only take the key if its actually bound to something, otherwise let the system handle it normally.\n\t\tIN_KeyEvent(0, true,  qkey, 0);\n\t\tIN_KeyEvent(0, false, qkey, 0);\n\t\treturn true;\n\t}\n\treturn false;\n}\n#endif\n"
  },
  {
    "path": "engine/client/input.h",
    "content": "/*\nCopyright (C) 1996-1997 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  \n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n*/\n// input.h -- external (non-keyboard) input devices\n\nvoid IN_ReInit (void);\n\nvoid IN_Init (void);\nfloat IN_DetermineMouseRate(void);\n\nvoid IN_Shutdown (void);\n\nvoid IN_Commands (void);\n// oportunity for devices to stick commands on the script buffer\n\nvoid IN_Touch_BlockGestures(unsigned int devid);\t//prevents any gestures from being generated from the same touch event.\nint IN_Touch_Fallback(unsigned int devid);\t\t//decides whether a tap should be attack/jump according to m_touchstrafe\nqboolean IN_Touch_MouseIsAbs(unsigned int devid);\n\nvoid IN_Move (float *nudgemovements, float *absmovements, int pnum, float frametime);\n// add additional movement on top of the keyboard move cmd\n\nextern cvar_t in_xflip;\n\nextern float mousecursor_x, mousecursor_y;\nextern float mousemove_x, mousemove_y;\n\nvoid IN_ActivateMouse(void);\nvoid IN_DeactivateMouse(void);\n\nint CL_TargettedSplit(qboolean nowrap);\n\n//specific events for the system-specific input code to call. may be called outside the main thread (so long as you don't call these simultaneously - ie: use a mutex or only one input thread).\nvoid IN_KeyEvent(unsigned int devid, int down, int keycode, int unicode);\t\t//don't use IN_KeyEvent for mice if you ever use abs mice...\nvoid IN_MouseMove(unsigned int devid, int abs, float x, float y, float z, float size);\nvoid IN_JoystickAxisEvent(unsigned int devid, int axis, float value);\nvoid IN_Accelerometer(unsigned int devid, float x, float y, float z);\nvoid IN_Gyroscope(unsigned int devid, float pitch, float yaw, float roll);\nqboolean IN_SetHandPosition(const char *devname, vec3_t org, vec3_t ang, vec3_t vel, vec3_t avel);\n\n//system-specific functions\nvoid INS_Move (void);\nvoid INS_Accumulate (void);\nvoid INS_ClearStates (void);\nvoid INS_ReInit (void);\nvoid INS_Init (void);\nvoid INS_Shutdown (void);\nvoid INS_Commands (void);\t//final chance to call IN_MouseMove/IN_KeyEvent each frame\nvoid INS_EnumerateDevices(void *ctx, void(*callback)(void *ctx, const char *type, const char *devicename, unsigned int *qdevid));\nvoid INS_SetupControllerAudioDevices(qboolean enabled);\t//creates audio devices for each controller (where controllers have their own audio devices)\n\nenum controllertype_e\n{\n\tCONTROLLER_NONE,\n\tCONTROLLER_UNKNOWN,\t\t//its all just numbers. could be anything\n\tCONTROLLER_XBOX,\t\t//ABXY\n\tCONTROLLER_PLAYSTATION,\t//XOSqareTriangle\n\tCONTROLLER_NINTENDO,\t//BAYX...\n\tCONTROLLER_VIRTUAL\t\t//its fucked. no idea what mappings they're using at all. could be keyboard.\n};\nenum controllertype_e INS_GetControllerType(int id);\nvoid INS_Rumble(int joy, quint16_t amp_low, quint16_t amp_high, quint32_t duration);\nvoid INS_RumbleTriggers(int joy, quint16_t left, quint16_t right, quint32_t duration);\nvoid INS_SetLEDColor(int id, vec3_t color);\nvoid INS_SetTriggerFX(int id, const void *data, size_t size);\nqboolean INS_KeyToLocalName(int qkey, char *buf, size_t bufsize);\t//returns a name for the key, according to their keyboard layout AND system language(hopefully), or false on unsupported/error. result may change at any time (eg: tap alt+shift on windows)\n\n#define DEVID_UNSET ~0u\n\nextern cvar_t\tcl_splitscreen;\nextern cvar_t\tcl_nodelta;\nextern cvar_t\tcl_c2spps;\nextern cvar_t\tcl_c2sImpulseBackup;\nextern cvar_t\tcl_netfps;\nextern cvar_t\tcl_queueimpulses;\nextern cvar_t\tcl_smartjump;\nextern cvar_t\tcl_run;\nextern cvar_t\tcl_fastaccel;\nextern cvar_t\tcl_rollspeed;\nextern cvar_t\tcl_prydoncursor;\nextern cvar_t\tcl_instantrotate;\nextern cvar_t\tin_xflip;\nextern cvar_t\tin_windowed_mouse;\t//if 0, uses absolute mouse coords allowing the mouse to be used for other programs too. ignored when fullscreen (and reliable).\nextern cvar_t\tprox_inmenu;\nextern cvar_t\tcl_forceseat;\n"
  },
  {
    "path": "engine/client/keys.c",
    "content": "/*\nCopyright (C) 1996-1997 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  \n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n*/\n#include \"quakedef.h\"\n#ifdef _WIN32\n#include \"winquake.h\"\n#endif\n#include \"shader.h\"\n/*\n\nkey up events are sent even if in console mode\n\n*/\nqboolean Editor_Key(int key, int unicode);\nvoid Key_ConsoleInsert(const char *instext);\nvoid Key_ClearTyping (void);\n\nunsigned char\t*key_lines[CON_EDIT_LINES_MASK+1];\nint\t\tkey_linepos;\nint\t\tshift_down=false;\n\nint\t\tedit_line=0;\nint\t\thistory_line=0;\n\nunsigned int key_dest_mask;\nqboolean key_dest_console;\nunsigned int key_dest_absolutemouse;\n\nstruct key_cursor_s key_customcursor[kc_max];\n\nint\t\tkey_bindmaps[2];\nchar\t*keybindings[K_MAX][KEY_MODIFIERSTATES];\nqbyte\tbindcmdlevel[K_MAX][KEY_MODIFIERSTATES];\t//should be a struct, but not due to 7 bytes wasted per on 64bit machines\nqboolean\tconsolekeys[K_MAX];\t// if true, can't be rebound while in console\nint\t\tkeyshift[K_MAX];\t\t// key to map to if shift held down in console\nunsigned int\tkeydown[K_MAX];\t//\tbitmask, for each device (to block autorepeat binds per-seat).\n\n#define MAX_INDEVS 8\n\nchar *releasecommand[K_MAX][MAX_INDEVS];\t//this is the console command to be invoked when the key is released. should free it.\nqbyte releasecommandlevel[K_MAX][MAX_INDEVS];\t//and this is the cbuf level it is to be run at.\n\nextern cvar_t con_displaypossibilities;\ncvar_t con_echochat = CVAR(\"con_echochat\", \"0\");\nextern cvar_t cl_chatmode;\n\nstatic int KeyModifier (unsigned int shift, unsigned int alt, unsigned int ctrl, unsigned int devbit)\n{\n\tint stateset = 0;\n\tif (shift&devbit)\n\t\tstateset |= KEY_MODIFIER_SHIFT;\n\tif (alt&devbit)\n\t\tstateset |= KEY_MODIFIER_ALT;\n\tif (ctrl&devbit)\n\t\tstateset |= KEY_MODIFIER_CTRL;\n\n\treturn stateset;\n}\n\nvoid Key_GetBindMap(int *bindmaps)\n{\n\tint i;\n\tfor (i = 0; i < countof(key_bindmaps); i++)\n\t{\n\t\tif (key_bindmaps[i])\n\t\t\tbindmaps[i] = (key_bindmaps[i]&~KEY_MODIFIER_ALTBINDMAP) + 1;\n\t\telse\n\t\t\tbindmaps[i] = 0;\n\t}\n}\n\nvoid Key_SetBindMap(int *bindmaps)\n{\n\tint i;\n\tfor (i = 0; i < countof(key_bindmaps); i++)\n\t{\n\t\tif (bindmaps[i] > 0 && bindmaps[i] <= KEY_MODIFIER_ALTBINDMAP)\n\t\t\tkey_bindmaps[i] = (bindmaps[i]-1)|KEY_MODIFIER_ALTBINDMAP;\n\t\telse\n\t\t\tkey_bindmaps[i] = 0;\n\t}\n}\n\ntypedef struct\n{\n\tchar\t*name;\n\tint\t\tkeynum;\n} keyname_t;\n\nkeyname_t keynames[] =\n{\n\t{\"TAB\",\t\t\tK_TAB},\n\t{\"ENTER\",\t\tK_ENTER},\n\t{\"RETURN\",\t\tK_ENTER},\n\t{\"ESCAPE\",\t\tK_ESCAPE},\n\t{\"SPACE\",\t\tK_SPACE},\n\t{\"BACKSPACE\",\tK_BACKSPACE},\n\t{\"UPARROW\",\t\tK_UPARROW},\n\t{\"DOWNARROW\",\tK_DOWNARROW},\n\t{\"LEFTARROW\",\tK_LEFTARROW},\n\t{\"RIGHTARROW\",\tK_RIGHTARROW},\n\n\t{\"LALT\",\tK_LALT},\n\t{\"RALT\",\tK_RALT},\n\t{\"LCTRL\",\tK_LCTRL},\n\t{\"RCTRL\",\tK_RCTRL},\n\t{\"LSHIFT\",\tK_LSHIFT},\n\t{\"RSHIFT\",\tK_RSHIFT},\n\t{\"ALT\",\t\tK_ALT},\t\t//depricated name\n\t{\"CTRL\",\tK_CTRL},\t//depricated name\n\t{\"SHIFT\",\tK_SHIFT},\t//depricated name\n\t\n\t{\"F1\",\t\tK_F1},\n\t{\"F2\",\t\tK_F2},\n\t{\"F3\",\t\tK_F3},\n\t{\"F4\",\t\tK_F4},\n\t{\"F5\",\t\tK_F5},\n\t{\"F6\",\t\tK_F6},\n\t{\"F7\",\t\tK_F7},\n\t{\"F8\",\t\tK_F8},\n\t{\"F9\",\t\tK_F9},\n\t{\"F10\",\t\tK_F10},\n\t{\"F11\",\t\tK_F11},\n\t{\"F12\",\t\tK_F12},\n\n\t{\"INS\",\t\tK_INS},\n\t{\"DEL\",\t\tK_DEL},\n\t{\"PGDN\",\tK_PGDN},\n\t{\"PGUP\",\tK_PGUP},\n\t{\"HOME\",\tK_HOME},\n\t{\"END\",\t\tK_END},\n\n\t\n\t{\"KP_HOME\",\t\tK_KP_HOME},\n\t{\"KP_UPARROW\",\tK_KP_UPARROW},\n\t{\"KP_PGUP\",\t\tK_KP_PGUP},\n\t{\"KP_LEFTARROW\", K_KP_LEFTARROW},\n\t{\"KP_5\",\t\tK_KP_5},\n\t{\"KP_RIGHTARROW\", K_KP_RIGHTARROW},\n\t{\"KP_END\",\t\tK_KP_END},\n\t{\"KP_DOWNARROW\",\tK_KP_DOWNARROW},\n\t{\"KP_PGDN\",\t\tK_KP_PGDN},\n\t{\"KP_ENTER\",\tK_KP_ENTER},\n\t{\"KP_INS\",\t\tK_KP_INS},\n\t{\"KP_DEL\",\t\tK_KP_DEL},\n\t{\"KP_SLASH\",\tK_KP_SLASH},\n\t{\"KP_MINUS\",\tK_KP_MINUS},\n\t{\"KP_PLUS\",\t\tK_KP_PLUS},\n\t{\"KP_NUMLOCK\",\tK_KP_NUMLOCK},\n\t{\"KP_STAR\",\t\tK_KP_STAR},\n\t{\"KP_MULTIPLY\",\tK_KP_STAR},\n\t{\"KP_EQUALS\",\tK_KP_EQUALS},\n\n\t//fuhquake compatible.\n\t{\"KP_0\",\t\tK_KP_INS},\n\t{\"KP_1\",\t\tK_KP_END},\n\t{\"KP_2\",\t\tK_KP_DOWNARROW},\n\t{\"KP_3\",\t\tK_KP_PGDN},\n\t{\"KP_4\",\t\tK_KP_LEFTARROW},\n\t{\"KP_6\",\t\tK_KP_RIGHTARROW},\n\t{\"KP_7\",\t\tK_KP_HOME},\n\t{\"KP_8\",\t\tK_KP_UPARROW},\n\t{\"KP_9\",\t\tK_KP_PGUP},\n\t//dp compat\n\t{\"KP_PERIOD\",\tK_KP_DEL},\n\t{\"KP_DIVIDE\",\tK_KP_SLASH},\n\t{\"NUMLOCK\",\t\tK_KP_NUMLOCK},\n\n\n\n\t{\"MOUSE1\",\t\tK_MOUSE1},\n\t{\"MOUSE2\",\t\tK_MOUSE2},\n\t{\"MOUSE3\",\t\tK_MOUSE3},\n\t{\"MOUSE4\",\t\tK_MOUSE4},\n\t{\"MOUSE5\",\t\tK_MOUSE5},\n\t{\"MOUSE6\",\t\tK_MOUSE6},\n\t{\"MOUSE7\",\t\tK_MOUSE7},\n\t{\"MOUSE8\",\t\tK_MOUSE8},\n\t{\"MOUSE9\",\t\tK_MOUSE9},\n\t{\"MOUSE10\",\t\tK_MOUSE10},\n\t{\"MWHEELUP\",\tK_MWHEELUP},\n\t{\"MWHEELDOWN\",\tK_MWHEELDOWN},\n\t{\"MWHEELLEFT\",\tK_MWHEELLEFT},\n\t{\"MWHEELRIGHT\",\tK_MWHEELRIGHT},\n\t{\"TOUCH\",\t\tK_TOUCH},\n\t{\"TOUCHSLIDE\",\tK_TOUCHSLIDE},\n\t{\"TOUCHTAP\",\tK_TOUCHTAP},\n\t{\"TOUCHLONG\",\tK_TOUCHLONG},\n\n\t{\"LWIN\",\tK_LWIN},\t//windows name\n\t{\"RWIN\",\tK_RWIN},\t//windows name\n\t{\"WIN\",\t\tK_WIN},\t\t//depricated\n\t{\"RCOMMAND\",K_RWIN},\t//mac name\n\t{\"LCOMMAND\",K_LWIN},\t//mac name\n\t{\"COMMAND\",\tK_WIN},\t\t//quakespasm(mac) compat\n\t{\"LMETA\",\tK_LWIN},\t//linux name\n\t{\"RMETA\",\tK_RWIN},\t//linux name\n\t{\"APP\",\t\tK_APP},\n\t{\"MENU\",\tK_APP},\n\t{\"SEARCH\",\tK_SEARCH},\n\t{\"POWER\",\tK_POWER},\n\t{\"VOLUP\",\tK_VOLUP},\n\t{\"VOLDOWN\",\tK_VOLDOWN},\n\n\t{\"JOY1\",\tK_JOY1},\n\t{\"JOY2\",\tK_JOY2},\n\t{\"JOY3\",\tK_JOY3},\n\t{\"JOY4\",\tK_JOY4},\n\t{\"JOY5\",\tK_JOY5},\n\t{\"JOY6\",\tK_JOY6},\n\t{\"JOY7\",\tK_JOY7},\n\t{\"JOY8\",\tK_JOY8},\n\t{\"JOY9\",\tK_JOY9},\n\t{\"JOY10\",\tK_JOY10},\n\t{\"JOY11\",\tK_JOY11},\n\t{\"JOY12\",\tK_JOY12},\n\t{\"JOY13\",\tK_JOY13},\n\t{\"JOY14\",\tK_JOY14},\n\t{\"JOY15\",\tK_JOY15},\n\t{\"JOY16\",\tK_JOY16},\n\t{\"JOY17\",\tK_JOY17},\n\t{\"JOY18\",\tK_JOY18},\n\t{\"JOY19\",\tK_JOY19},\n\t{\"JOY20\",\tK_JOY20},\n\t{\"JOY21\",\tK_JOY21},\n\t{\"JOY22\",\tK_JOY22},\n\t{\"JOY23\",\tK_JOY23},\n\t{\"JOY24\",\tK_JOY24},\n\t{\"JOY25\",\tK_JOY25},\n\t{\"JOY26\",\tK_JOY26},\n\t{\"JOY27\",\tK_JOY27},\n\t{\"JOY28\",\tK_JOY28},\n\t{\"JOY29\",\tK_JOY29},\n\t{\"JOY30\",\tK_JOY30},\n\t{\"JOY31\",\tK_JOY31},\n\t{\"JOY32\",\tK_JOY32},\n\n\t{\"AUX1\",\tK_AUX1},\n\t{\"AUX2\",\tK_AUX2},\n\t{\"AUX3\",\tK_AUX3},\n\t{\"AUX4\",\tK_AUX4},\n\t{\"AUX5\",\tK_AUX5},\n\t{\"AUX6\",\tK_AUX6},\n\t{\"AUX7\",\tK_AUX7},\n\t{\"AUX8\",\tK_AUX8},\n\t{\"AUX9\",\tK_AUX9},\n\t{\"AUX10\",\tK_AUX10},\n\t{\"AUX11\",\tK_AUX11},\n\t{\"AUX12\",\tK_AUX12},\n\t{\"AUX13\",\tK_AUX13},\n\t{\"AUX14\",\tK_AUX14},\n\t{\"AUX15\",\tK_AUX15},\n\t{\"AUX16\",\tK_AUX16},\n\n\t{\"PAUSE\",\t\tK_PAUSE},\n\n\t{\"PRINTSCREEN\",\tK_PRINTSCREEN},\n\t{\"CAPSLOCK\",\tK_CAPSLOCK},\n\t{\"SCROLLLOCK\",\tK_SCRLCK},\n\n\t{\"SEMICOLON\",\t';'},\t// because a raw semicolon seperates commands\n\t{\"PLUS\",\t\t'+'},\t// because \"shift++\" is inferior to shift+plus\n\t{\"MINUS\",\t\t'-'},\t// because \"shift+-\" is inferior to shift+minus\n\n\t{\"APOSTROPHE\",\t'\\''},\t//can mess up string parsing, unfortunately\n\t{\"QUOTES\",\t\t'\\\"'},\t//can mess up string parsing, unfortunately\n\t{\"TILDE\",\t\t'~'},\n\t{\"BACKQUOTE\",\t'`'},\n\t{\"BACKSLASH\",\t'\\\\'},\n\n\t{\"GP_A\",\t\t\tK_GP_A},\t//note: xbox arrangement, not nintendo arrangement.\n\t{\"GP_B\",\t\t\tK_GP_B},\n\t{\"GP_X\",\t\t\tK_GP_X},\n\t{\"GP_Y\",\t\t\tK_GP_Y},\n\t{\"GP_LSHOULDER\",\tK_GP_LEFT_SHOULDER},\n\t{\"GP_RSHOULDER\",\tK_GP_RIGHT_SHOULDER},\n\t{\"GP_LTRIGGER\",\t\tK_GP_LEFT_TRIGGER},\n\t{\"GP_RTRIGGER\",\t\tK_GP_RIGHT_TRIGGER},\n\t{\"GP_VIEW\",\t\t\tK_GP_VIEW},\n\t{\"GP_MENU\",\t\t\tK_GP_MENU},\n\t{\"GP_LTHUMB\",\t\tK_GP_LEFT_STICK},\n\t{\"GP_RTHUMB\",\t\tK_GP_RIGHT_STICK},\n\t{\"GP_DPAD_UP\",\t\tK_GP_DPAD_UP},\n\t{\"GP_DPAD_DOWN\",\tK_GP_DPAD_DOWN},\n\t{\"GP_DPAD_LEFT\",\tK_GP_DPAD_LEFT},\n\t{\"GP_DPAD_RIGHT\",\tK_GP_DPAD_RIGHT},\n\t{\"GP_GUIDE\",\t\tK_GP_GUIDE},\n\t{\"GP_SHARE\",\t\tK_GP_MISC1},\n\t{\"GP_PADDLE1\",\t\tK_GP_PADDLE1},\n\t{\"GP_PADDLE2\",\t\tK_GP_PADDLE2},\n\t{\"GP_PADDLE3\",\t\tK_GP_PADDLE3},\n\t{\"GP_PADDLE4\",\t\tK_GP_PADDLE4},\n\t{\"GP_TOUCHPAD\",\t\tK_GP_TOUCHPAD},\n\t{\"GP_UNKNOWN\",\t\tK_GP_UNKNOWN},\n\n\t//older xbox names\n\t{\"GP_BACK\",\t\t\tK_GP_BACK},\n\t{\"GP_START\",\t\tK_GP_START},\n\t//names for playstation controllers\n\t{\"GP_CROSS\",\t\tK_GP_PS_CROSS},\n\t{\"GP_CIRCLE\",\t\tK_GP_PS_CIRCLE},\n\t{\"GP_SQUARE\",\t\tK_GP_PS_SQUARE},\n\t{\"GP_TRIANGLE\",\t\tK_GP_PS_TRIANGLE},\n\t{\"GP_MIC\",\t\t\tK_GP_MISC1},\n\t{\"GP_SELECT\",\t\tK_GP_VIEW},\n\t{\"GP_SHARE\",\t\tK_GP_VIEW},\n\t{\"GP_OPTIONS\",\t\tK_GP_START},\n\n\t//axis->button emulation\n\t{\"GP_LTHUMB_UP\",\tK_GP_LEFT_THUMB_UP},\n\t{\"GP_LTHUMB_DOWN\",\tK_GP_LEFT_THUMB_DOWN},\n\t{\"GP_LTHUMB_LEFT\",\tK_GP_LEFT_THUMB_LEFT},\n\t{\"GP_LTHUMB_RIGHT\",\tK_GP_LEFT_THUMB_RIGHT},\n\t{\"GP_RTHUMB_UP\",\tK_GP_RIGHT_THUMB_UP},\n\t{\"GP_RTHUMB_DOWN\",\tK_GP_RIGHT_THUMB_DOWN},\n\t{\"GP_RTHUMB_LEFT\",\tK_GP_RIGHT_THUMB_LEFT},\n\t{\"GP_RTHUMB_RIGHT\",\tK_GP_RIGHT_THUMB_RIGHT},\n\n#ifdef Q2BSPS\n\t//kingpin compat\n\t{\"ESC\",\t\t\t\tK_ESCAPE},\n\t{\"B_SPACE\",\t\t\tK_BACKSPACE},\n\t{\"U_ARROW\",\t\t\tK_UPARROW},\n\t{\"D_ARROW\",\t\t\tK_DOWNARROW},\n\t{\"L_ARROW\",\t\t\tK_LEFTARROW},\n\t{\"R_ARROW\",\t\t\tK_RIGHTARROW},\n#endif\n\n#ifndef QUAKETC\n\t//dp compat\n\t{\"X360_DPAD_UP\",\t\t\tK_GP_DPAD_UP},\n\t{\"X360_DPAD_DOWN\",\t\t\tK_GP_DPAD_DOWN},\n\t{\"X360_DPAD_LEFT\",\t\t\tK_GP_DPAD_LEFT},\n\t{\"X360_DPAD_RIGHT\",\t\t\tK_GP_DPAD_RIGHT},\n\t{\"X360_START\",\t\t\t\tK_GP_START},\n\t{\"X360_BACK\",\t\t\t\tK_GP_BACK},\n\t{\"X360_LEFT_THUMB\",\t\t\tK_GP_LEFT_STICK},\n\t{\"X360_RIGHT_THUMB\",\t\tK_GP_RIGHT_STICK},\n\t{\"X360_LEFT_SHOULDER\",\t\tK_GP_LEFT_SHOULDER},\n\t{\"X360_RIGHT_SHOULDER\",\t\tK_GP_RIGHT_SHOULDER},\n\t{\"X360_A\",\t\t\t\t\tK_GP_A},\n\t{\"X360_B\",\t\t\t\t\tK_GP_B},\n\t{\"X360_X\",\t\t\t\t\tK_GP_X},\n\t{\"X360_Y\",\t\t\t\t\tK_GP_Y},\n\t{\"X360_LEFT_TRIGGER\",\t\tK_GP_LEFT_TRIGGER},\n\t{\"X360_RIGHT_TRIGGER\",\t\tK_GP_RIGHT_TRIGGER},\n\t{\"X360_LEFT_THUMB_UP\",\t\tK_GP_LEFT_THUMB_UP},\n\t{\"X360_LEFT_THUMB_DOWN\",\tK_GP_LEFT_THUMB_DOWN},\n\t{\"X360_LEFT_THUMB_LEFT\",\tK_GP_LEFT_THUMB_LEFT},\n\t{\"X360_LEFT_THUMB_RIGHT\",\tK_GP_LEFT_THUMB_RIGHT},\n\t{\"X360_RIGHT_THUMB_UP\",\t\tK_GP_RIGHT_THUMB_UP},\n\t{\"X360_RIGHT_THUMB_DOWN\",\tK_GP_RIGHT_THUMB_DOWN},\n\t{\"X360_RIGHT_THUMB_LEFT\",\tK_GP_RIGHT_THUMB_LEFT},\n\t{\"X360_RIGHT_THUMB_RIGHT\",\tK_GP_RIGHT_THUMB_RIGHT},\n\n\t//quakespasm compat\n\t{\"LTHUMB\",\t\t\t\t\tK_GP_LEFT_STICK},\n\t{\"RTHUMB\",\t\t\t\t\tK_GP_RIGHT_STICK},\n\t{\"LSHOULDER\",\t\t\t\tK_GP_LEFT_SHOULDER},\n\t{\"RSHOULDER\",\t\t\t\tK_GP_RIGHT_SHOULDER},\n\t{\"ABUTTON\",\t\t\t\t\tK_GP_A},\n\t{\"BBUTTON\",\t\t\t\t\tK_GP_B},\n\t{\"XBUTTON\",\t\t\t\t\tK_GP_X},\n\t{\"YBUTTON\",\t\t\t\t\tK_GP_Y},\n\t{\"LTRIGGER\",\t\t\t\tK_GP_LEFT_TRIGGER},\n\t{\"RTRIGGER\",\t\t\t\tK_GP_RIGHT_TRIGGER},\n#endif\n\n\t/* Steam Controller */\n\t{\"SC_LPADDLE\",\t\t\t\tK_JOY16},\n\t{\"SC_RPADDLE\",\t\t\t\tK_JOY17},\n\n\t{NULL,\t\t\t0}\n};\n\n#if defined(CSQC_DAT) || defined(MENU_DAT)\nint MP_TranslateFTEtoQCCodes(keynum_t code);\nvoid Key_PrintQCDefines(vfsfile_t *f, qboolean defines)\n{\n\tint i, j;\n\tfor (i = 0; keynames[i].name; i++)\n\t{\n\t\tfor (j = 0; j < i; j++)\n\t\t\tif (keynames[j].keynum == keynames[i].keynum)\n\t\t\t\tbreak;\n\t\tif (j == i)\n\t\t{\n\t\t\tif (defines)\n\t\t\t\tVFS_PRINTF(f, \"#define K_%s\\t%i\\n\", keynames[i].name, MP_TranslateFTEtoQCCodes(keynames[j].keynum));\n\t\t\telse\n\t\t\t\tVFS_PRINTF(f, \"const float K_%s = %i;\\n\", keynames[i].name, MP_TranslateFTEtoQCCodes(keynames[j].keynum));\n\t\t}\n\t}\n}\n#endif\n\n/*\n==============================================================================\n\n\t\t\tLINE TYPING INTO THE CONSOLE\n\n==============================================================================\n*/\n\nqboolean Cmd_IsCommand (const char *line)\n{\n\tchar\tcommand[128];\n\tconst char\t*cmd, *s;\n\tint\t\ti;\n\n\ts = line;\n\n\tfor (i=0 ; i<127 ; i++)\n\t\tif (s[i] <= ' ' || s[i] == ';')\n\t\t\tbreak;\n\t\telse\n\t\t\tcommand[i] = s[i];\n\tcommand[i] = 0;\n\n\tcmd = Cmd_CompleteCommand (command, true, false, -1, NULL);\n\tif (!cmd  || strcmp (cmd, command) )\n\t\treturn false;\t\t// just a chat message\n\treturn true;\n}\n\n#define COLUMNWIDTH 20\n#define MINCOLUMNWIDTH 18\n\nint PaddedPrint (char *s, int x)\n{\n\tCon_Printf (\"^4%s\\t\", s);\n\tx+=strlen(s);\n\n\treturn x;\n}\n\nint con_commandmatch;\nvoid Key_UpdateCompletionDesc(void)\n{\n\tconst char *desc;\n\tcmd_completion_t *c;\n\tconst char *s = key_lines[edit_line];\n\tif (*s == ' ' || *s == '\\t')\n\t\ts++;\n\tif (*s == '\\\\' || *s == '/')\n\t\ts++;\n\tif (*s == ' ' || *s == '\\t')\n\t\ts++;\n\n\tc = Cmd_Complete(s, true);\n\tif (!con_commandmatch || con_commandmatch > c->num)\n\t\tcon_commandmatch = 1;\n\n\tif (con_commandmatch <= c->num)\n\t{\n\t\tconst char *cmd;\n\t\tcvar_t *var;\n\t\tif (c->completions[con_commandmatch-1].repl)\n\t\t\tcmd = c->completions[con_commandmatch-1].repl;\n\t\telse\n\t\t\tcmd = c->completions[con_commandmatch-1].text;\n\t\tdesc = c->completions[con_commandmatch-1].desc;\n\t\tvar = Cvar_FindVar(cmd);\n\t\tif (var)\n\t\t{\n\t\t\tif (desc)\n\t\t\t\tCon_Footerf(NULL, false, \"%s %s\\n%s\", cmd, var->string, localtext(desc));\n\t\t\telse\n\t\t\t\tCon_Footerf(NULL, false, \"%s %s\", cmd, var->string);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (desc)\n\t\t\t\tCon_Footerf(NULL, false, \"%s: %s\", cmd, localtext(desc));\n\t\t\telse\n\t\t\t\tCon_Footerf(NULL, false, \"\");\n\t\t}\n\t}\n\telse\n\t{\n\t\tCon_Footerf(NULL, false, \"\");\n\t\tif (con_commandmatch)\n\t\t\tcon_commandmatch = 1;\n\t}\n}\n\nvoid CompleteCommand (qboolean force, int direction)\n{\n\tconst char\t*cmd, *s;\n\tconst char *desc;\n\tcmd_completion_t *c;\n\n\ts = key_lines[edit_line];\n\tif (!*s)\n\t\treturn;\n\tif (*s == ' ' || *s == '\\t')\n\t\ts++;\n\tif (*s == '\\\\' || *s == '/')\n\t\ts++;\n\tif (*s == ' ' || *s == '\\t')\n\t\ts++;\n\n\t//check for singular matches and complete if found\n\tc = Cmd_Complete(s, true);\n\tif (c->num == 1 || force)\n\t{\n\t\tdesc = NULL;\n\t\tif (!force && c->num != 1)\n\t\t\tcmd = c->guessed;\n\t\telse if (c->num)\n\t\t{\n\t\t\tint idx = max(0, con_commandmatch-1);\n\t\t\tif (c->completions[idx].repl)\n\t\t\t\tcmd = c->completions[idx].repl;\n\t\t\telse\n\t\t\t\tcmd = c->completions[idx].text;\n\t\t}\n\t\telse\n\t\t\tcmd = NULL;\n\t\tif (cmd)\n\t\t{\n\t\t\tif (strlen(cmd) < strlen(s))\n\t\t\t\treturn;\n\n\t\t\t//complete to that (maybe partial) cmd.\n\t\t\tKey_ClearTyping();\n\t\t\tif (cl_chatmode.ival)\n\t\t\t\tKey_ConsoleInsert(\"/\");\n\t\t\tKey_ConsoleInsert(cmd);\n\t\t\ts = key_lines[edit_line];\n\t\t\tif (*s == '/')\n\t\t\t\ts++;\n\n\t\t\t//if its the only match, add a space ready for arguments.\n\t\t\tc = Cmd_Complete(s, true);\n\t\t\tcmd = ((c->num >= 1)?(c->completions[0].repl?c->completions[0].repl:c->completions[0].text):NULL);\n\t\t\tdesc = ((c->num >= 1)?c->completions[0].desc:NULL);\n\t\t\tif (c->num == 1)\n\t\t\t\tKey_ConsoleInsert(\" \");\n\n\t\t\tif (!con_commandmatch)\n\t\t\t\tcon_commandmatch = 1;\n\n\t\t\tif (desc)\n\t\t\t\tCon_Footerf(con_current, false, \"%s: %s\", cmd, desc);\n\t\t\telse\n\t\t\t\tCon_Footerf(con_current, false, \"\");\n\t\t\treturn;\n\t\t}\n\t}\n\t//complete to a partial match.\n\tcmd = c->guessed;\n\tif (cmd)\n\t{\n\t\tint i = key_lines[edit_line][0] == '/'?1:0;\n\t\tif (i != 1 || strcmp(key_lines[edit_line]+i, cmd))\n\t\t{\t//if successful, use that instead.\n\t\t\tKey_ClearTyping();\n\t\t\tif (cl_chatmode.ival)\n\t\t\t\tKey_ConsoleInsert(\"/\");\n\t\t\tKey_ConsoleInsert(cmd);\n\n\t\t\ts = key_lines[edit_line];\t//readjust to cope with the insertion of a /\n\t\t\tif (*s == '\\\\' || *s == '/')\n\t\t\t\ts++;\n\t\t}\n\t}\n\n\tcon_commandmatch += direction;\n\tif (con_commandmatch <= 0)\n\t\tcon_commandmatch += c->num;\n\tKey_UpdateCompletionDesc();\n}\n\nint Con_Navigate(console_t *con, const char *line)\n{\n\tif (con->backshader)\n\t{\n#ifdef HAVE_MEDIA_DECODER\n\t\tcin_t *cin = R_ShaderGetCinematic(con->backshader);\n\t\tif (cin)\n\t\t{\n\t\t\tMedia_Send_Command(cin, line);\n\t\t}\n#endif\n\t}\n\tcon->linebuffered = NULL;\n\treturn 2;\n}\n\n//lines typed at the main console enter here\nint Con_ExecuteLine(console_t *con, const char *line)\n{\n\tqboolean waschat = false;\n\tchar *deutf8 = NULL;\n\tif (com_parseutf8.ival <= 0)\n\t{\n\t\tunsigned int unicode;\n\t\tint err;\n\t\tint len = 0;\n\t\tint maxlen = strlen(line)*6+1;\n\t\tdeutf8 = malloc(maxlen);\n\t\twhile(*line)\n\t\t{\n\t\t\tunicode = utf8_decode(&err, line, &line);\n\t\t\tlen += unicode_encode(deutf8+len, unicode, maxlen-1 - len, true);\n\t\t}\n\t\tdeutf8[len] = 0;\n\t\tline = deutf8;\n\t}\n\n\tif (con_commandmatch)\n\t\tcon_commandmatch=1;\n\tCon_Footerf(con, false, \"\");\n\n\tif (cls.state >= ca_connected && cl_chatmode.value == 2)\n\t{\n\t\twaschat = true;\n\t\tif (keydown[K_CTRL])\n\t\t\tCbuf_AddText (\"say_team \", RESTRICT_LOCAL);\n\t\telse if (keydown[K_SHIFT] || *line == ' ')\n\t\t\tCbuf_AddText (\"say \", RESTRICT_LOCAL);\n\t\telse\n\t\t\twaschat = false;\n\t}\n\twhile (*line == ' ')\n\t\tline++;\n\tif (waschat)\n\t\tCbuf_AddText (line, RESTRICT_LOCAL);\n\telse\n\t{\n\t\tconst char *exec = NULL;\n\t\tif (line[0] == '\\\\' || line[0] == '/')\n\t\t\texec = line+1;\t// skip the slash\n\t\telse if (cl_chatmode.value == 2 && Cmd_IsCommand(line))\n\t\t\texec = line;\t// valid command\n\t#ifdef Q2CLIENT\n\t\telse if (cls.protocol == CP_QUAKE2)\n\t\t\texec = line;\t// send the command to the server via console, and let the server convert to chat\n\t#endif\n\t\telse if (*line)\n\t\t{\t// convert to a chat message\n\t\t\tif ((cl_chatmode.value == 1 || ((cls.state >= ca_connected && cl_chatmode.value == 2) && (strncmp(line, \"say \", 4)))))\n\t\t\t{\n\t\t\t\tif (keydown[K_CTRL])\n\t\t\t\t\tCbuf_AddText (\"say_team \", RESTRICT_LOCAL);\n\t\t\t\telse\n\t\t\t\t\tCbuf_AddText (\"say \", RESTRICT_LOCAL);\n\t\t\t\twaschat = true;\n\t\t\t\tCbuf_AddText (line, RESTRICT_LOCAL);\n\t\t\t}\n\t\t\telse\n\t\t\t\texec = line;\t//exec it anyway. let the cbuf give the error message in case its 'INVALID;VALID'\n\t\t}\n\n\t\tif (exec)\n\t\t{\n#ifdef TEXTEDITOR\n\t\t\tif (editormodal)\n\t\t\t{\n\t\t\t\tchar cvarname[128];\n\t\t\t\tCOM_ParseOut(exec, cvarname, sizeof(cvarname));\n\t\t\t\tif (Cvar_FindVar(cvarname) && !strchr(line, ';') && !strchr(line, '\\n'))\n\t\t\t\t{\n\t\t\t\t\tCon_Printf (\"]%s\\n\",line);\n\t\t\t\t\tCmd_ExecuteString(exec, RESTRICT_SERVER);\n\t\t\t\t\tfree(deutf8);\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\n\t\t\t\tCon_Footerf(con, false, \"Commands cannot be execed while debugging QC\");\n\t\t\t}\n#endif\n\t\t\tCbuf_AddText (exec, RESTRICT_LOCAL);\n\t\t}\n\t}\n\n\tCbuf_AddText (\"\\n\", RESTRICT_LOCAL);\n\tif (!waschat || con_echochat.value)\n\t{\n\t\tCon_Printf (\"%s\", con->prompt);\n\t\tCon_Printf (\"%s\\n\",line);\n\t}\n\n//\tif (cls.state == ca_disconnected)\n//\t\tSCR_UpdateScreen ();\t// force an update, because the command\n//\t\t\t\t\t\t\t\t\t// may take some time\n\n\tfree(deutf8);\n\treturn true;\n}\n\nqboolean Key_GetConsoleSelectionBox(console_t *con, int *sx, int *sy, int *ex, int *ey)\n{\n\t*sx = *sy = *ex = *ey = 0;\n\n\tif (con->buttonsdown == CB_SCROLL || con->buttonsdown == CB_SCROLL_R)\n\t{\n\t\tfloat lineheight = Font_CharVHeight(font_console);\n\t\t//left-mouse.\n\t\t//scroll the console with the mouse. trigger links on release.\n\t\tcon->displayscroll += (con->mousecursor[1] - con->mousedown[1])/lineheight;\n\t\tcon->mousedown[1] = con->mousecursor[1];\n\t\twhile (con->displayscroll > con->display->numlines)\n\t\t{\n\t\t\tif (con->display->older)\n\t\t\t{\n\t\t\t\tcon->displayscroll -= con->display->numlines;\n\t\t\t\tcon->display = con->display->older;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tcon->displayscroll = con->display->numlines;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\twhile (con->displayscroll <= 0)\n\t\t{\n\t\t\tif (con->display->newer)\n\t\t\t{\n\t\t\t\tcon->display = con->display->newer;\n\t\t\t\tcon->displayscroll += con->display->numlines;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tcon->displayscroll = 0;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\t/*\n\t\twhile (con->mousecursor[1] - con->mousedown[1] > 8 && con->display->older)\n\t\t{\n\t\t\tcon->mousedown[1] += 8;\n\t\t\tcon->display = con->display->older;\n\t\t}\n\t\twhile (con->mousecursor[1] - con->mousedown[1] < -8 && con->display->newer)\n\t\t{\n\t\t\tcon->mousedown[1] -= 8;\n\t\t\tcon->display = con->display->newer;\n\t\t}\n*/\n\t\t*sx = con->mousecursor[0];\n\t\t*sy = con->mousecursor[1];\n\t\t*ex = con->mousecursor[0];\n\t\t*ey = con->mousecursor[1];\n\t\treturn true;\n\t}\n\telse if (con->buttonsdown == CB_SELECT || con->buttonsdown == CB_SELECTED || con->buttonsdown == CB_TAPPED)\n\t{\n\t\t//right-mouse\n\t\t//select. copy-to-clipboard on release.\n\t\t*sx = con->mousedown[0];\n\t\t*sy = con->mousedown[1];\n\t\t*ex = con->mousecursor[0];\n\t\t*ey = con->mousecursor[1];\n\t\treturn true;\n\t}\n\telse\n\t{\n\t\tif (con_curwindow == con && con->buttonsdown)\n\t\t{\n\t\t\tif (con->buttonsdown == CB_MOVE)\n\t\t\t{\t//move window to track the cursor\n\t\t\t\tcon->wnd_x += con->mousecursor[0] - con->mousedown[0];\n\t\t//\t\tcon->mousedown[0] = con->mousecursor[0];\n\t\t\t\tcon->wnd_y += con->mousecursor[1] - con->mousedown[1];\n\t\t//\t\tcon->mousedown[1] = con->mousecursor[1];\n\t\t\t}\n\t\t\tif (con->buttonsdown & CB_SIZELEFT)\n\t\t\t{\n\t\t\t\tif (con->wnd_w - (con->mousecursor[0] - con->mousedown[0]) >= 64)\n\t\t\t\t{\n\t\t\t\t\tcon->wnd_w -= con->mousecursor[0] - con->mousedown[0];\n\t\t\t\t\tcon->wnd_x += con->mousecursor[0] - con->mousedown[0];\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (con->buttonsdown & CB_SIZERIGHT)\n\t\t\t{\n\t\t\t\tif (con->wnd_w + (con->mousecursor[0] - con->mousedown[0]) >= 64)\n\t\t\t\t{\n\t\t\t\t\tcon->wnd_w += con->mousecursor[0] - con->mousedown[0];\n\t\t\t\t\tcon->mousedown[0] = con->mousecursor[0];\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (con->buttonsdown & CB_SIZEBOTTOM)\n\t\t\t{\n\t\t\t\tif (con->wnd_h + (con->mousecursor[1] - con->mousedown[1]) >= 64)\n\t\t\t\t{\n\t\t\t\t\tcon->wnd_h += con->mousecursor[1] - con->mousedown[1];\n\t\t\t\t\tcon->mousedown[1] = con->mousecursor[1];\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse\n\t\t\tcon->buttonsdown = CB_NONE;\n\n\t\t*sx = con->mousecursor[0];\n\t\t*sy = con->mousecursor[1];\n\t\t*ex = con->mousecursor[0];\n\t\t*ey = con->mousecursor[1];\n\t\treturn false;\n\t}\n}\n\n/*insert the given text at the console input line at the current cursor pos*/\nvoid Key_ConsoleInsert(const char *instext)\n{\n\tint i;\n\tint len, olen;\n\tchar *old;\n\tif (!*instext)\n\t\treturn;\n\n\told = key_lines[edit_line];\n\tlen = strlen(instext);\n\tolen = strlen(old);\n\tkey_lines[edit_line] = BZ_Malloc(olen + len + 1);\n\tmemcpy(key_lines[edit_line], old, key_linepos);\n\tmemcpy(key_lines[edit_line]+key_linepos, instext, len);\n\tmemcpy(key_lines[edit_line]+key_linepos+len, old+key_linepos, olen - key_linepos+1);\n\tZ_Free(old);\n\tfor (i = key_linepos; i < key_linepos+len; i++)\n\t{\n\t\tif (key_lines[edit_line][i] == '\\r')\n\t\t\tkey_lines[edit_line][i] = ' ';\n\t\telse if (key_lines[edit_line][i] == '\\n')\n\t\t\tkey_lines[edit_line][i] = ';';\n\t}\n\tkey_linepos += len;\n}\nvoid Key_ConsoleReplace(const char *instext)\n{\n\tif (!*instext)\n\t\treturn;\n\n\tkey_linepos = 0;\n\tkey_lines[edit_line][key_linepos] = 0;\n\tKey_ConsoleInsert(instext);\n}\n\nvoid Key_DefaultLinkClicked(console_t *con, char *text, char *info)\n{\n\tchar *c;\n\t/*the engine supports specific default links*/\n\t/*we don't support everything. a: there's no point. b: unbindall links are evil.*/\n\tc = Info_ValueForKey(info, \"player\");\n\tif (*c)\n\t{\n\t\tunsigned int player = atoi(c);\n\t\tint i;\n\t\tif (player >= cl.allocated_client_slots || !*cl.players[player].name)\n\t\t\treturn;\n\n\t\tc = Info_ValueForKey(info, \"action\");\n\t\tif (*c)\n\t\t{\n\t\t\tif (!strcmp(c, \"mute\"))\n\t\t\t{\n\t\t\t\tif (!cl.players[player].vignored)\n\t\t\t\t{\n\t\t\t\t\tcl.players[player].vignored = true;\n\t\t\t\t\tCon_Printf(\"^[%s\\\\player\\\\%i^] muted\\n\", cl.players[player].name, player);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tcl.players[player].vignored = false;\n\t\t\t\t\tCon_Printf(\"^[%s\\\\player\\\\%i^] unmuted\\n\", cl.players[player].name, player);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (!strcmp(c, \"ignore\"))\n\t\t\t{\n\t\t\t\tif (!cl.players[player].ignored)\n\t\t\t\t{\n\t\t\t\t\tcl.players[player].ignored = true;\n\t\t\t\t\tcl.players[player].vignored = true;\n\t\t\t\t\tCon_Printf(\"^[%s\\\\player\\\\%i^] ignored\\n\", cl.players[player].name, player);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tcl.players[player].ignored = false;\n\t\t\t\t\tcl.players[player].vignored = false;\n\t\t\t\t\tCon_Printf(\"^[%s\\\\player\\\\%i^] unignored\\n\", cl.players[player].name, player);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (!strcmp(c, \"spec\"))\n\t\t\t{\n\t\t\t\tCam_TrackPlayer(0, \"spectate\", cl.players[player].name);\n\t\t\t}\n\t\t\telse if (!strcmp(c, \"kick\"))\n\t\t\t{\n#ifndef CLIENTONLY\n\t\t\t\tif (sv.active)\n\t\t\t\t{\n\t\t\t\t\t//use the q3 command, because we can.\n\t\t\t\t\tCbuf_AddText(va(\"\\nclientkick %i\\n\", player), RESTRICT_LOCAL);\n\t\t\t\t}\n\t\t\t\telse\n#endif\n\t\t\t\t\tCbuf_AddText(va(\"\\nrcon kick %s\\n\", cl.players[player].name), RESTRICT_LOCAL);\n\t\t\t}\n\t\t\telse if (!strcmp(c, \"ban\"))\n\t\t\t{\n#ifndef CLIENTONLY\n\t\t\t\tif (sv.active)\n\t\t\t\t{\n\t\t\t\t\t//use the q3 command, because we can.\n\t\t\t\t\tCbuf_AddText(va(\"\\nbanname %s QuickBan\\n\", cl.players[player].name), RESTRICT_LOCAL);\n\t\t\t\t}\n\t\t\t\telse\n#endif\n\t\t\t\t\tCbuf_AddText(va(\"\\nrcon banname %s QuickBan\\n\", cl.players[player].name), RESTRICT_LOCAL);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tif (!con)\n\t\t\treturn;\t//can't do footers\n\n\t\tCon_Footerf(con, false, \"^m#^m ^[%s\\\\player\\\\%i^]: %if %ims\", cl.players[player].name, player, cl.players[player].frags, cl.players[player].ping);\n\n\t\tfor (i = 0; i < cl.splitclients; i++)\n\t\t{\n\t\t\tif (cl.playerview[i].playernum == player)\n\t\t\t\tbreak;\n\t\t}\n\t\tif (i == cl.splitclients)\n\t\t{\n\t\t\textern cvar_t rcon_password;\n\t\t\tif (*cl.players[player].ip)\n\t\t\t\tCon_Footerf(con, true, \"\\n%s\", cl.players[player].ip);\n\n\t\t\tif (cl.playerview[0].spectator || cls.demoplayback==DPB_MVD)\n\t\t\t{\n\t\t\t\t//we're spectating, or an mvd\n\t\t\t\tCon_Footerf(con, true, \" ^[Spectate\\\\player\\\\%i\\\\action\\\\spec^]\", player);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t//we're playing.\n\t\t\t\tif (cls.protocol == CP_QUAKEWORLD && strcmp(cl.players[cl.playerview[0].playernum].team, cl.players[player].team))\n\t\t\t\t\tCon_Footerf(con, true, \" ^[[Join Team %s]\\\\cmd\\\\setinfo team %s^]\", cl.players[player].team, cl.players[player].team);\n\t\t\t}\n\t\t\tCon_Footerf(con, true, \" ^[%sgnore\\\\player\\\\%i\\\\action\\\\ignore^]\", cl.players[player].ignored?\"Uni\":\"I\", player);\n\t//\t\tif (cl_voip_play.ival)\n\t\t\t\tCon_Footerf(con, true, \" ^[%sute\\\\player\\\\%i\\\\action\\\\mute^]\", cl.players[player].vignored?\"Unm\":\"M\",  player);\n\n\t\t\tif (!cls.demoplayback && (*rcon_password.string\n#ifndef CLIENTONLY\n\t\t\t\t|| (sv.state && svs.clients[player].netchan.remote_address.type != NA_LOOPBACK)\n#endif\n\t\t\t\t))\n\t\t\t{\n\t\t\t\tCon_Footerf(con, true, \" ^[Kick\\\\player\\\\%i\\\\action\\\\kick^]\", player);\n\t\t\t\tCon_Footerf(con, true, \" ^[Ban\\\\player\\\\%i\\\\action\\\\ban^]\", player);\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tchar cmdprefix[6];\n//\t\t\tif (i == 0)\n\t\t\t\t*cmdprefix = 0;\n//\t\t\telse\n//\t\t\t\tsnprintf(cmdprefix, sizeof(cmdprefix), \"%i \", i+1);\n\n\t\t\t//hey look! its you!\n\n\t\t\tif (i || cl.playerview[i].spectator || cls.demoplayback)\n\t\t\t{\n\t\t\t\t//need join option here or something\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tCon_Footerf(con, true, \" ^[Suicide\\\\cmd\\\\%skill^]\", cmdprefix);\n\t#ifndef CLIENTONLY\n\t\t\t\tif (!sv.state)\n\t\t\t\t\tCon_Footerf(con, true, \" ^[Disconnect\\\\cmd\\\\disconnect^]\");\n\t\t\t\tif (cls.allow_cheats || (sv.state && sv.allocated_client_slots == 1))\n\t#else\n\t\t\t\tCon_Footerf(con, true, \" ^[Disconnect\\\\cmd\\\\disconnect^]\");\n\t\t\t\tif (cls.allow_cheats)\n\t#endif\n\t\t\t\t{\n\t\t\t\t\tCon_Footerf(con, true, \" ^[Noclip\\\\cmd\\\\%snoclip^]\", cmdprefix);\n\t\t\t\t\tCon_Footerf(con, true, \" ^[Fly\\\\cmd\\\\%sfly^]\", cmdprefix);\n\t\t\t\t\tCon_Footerf(con, true, \" ^[God\\\\cmd\\\\%sgod^]\", cmdprefix);\n\t\t\t\t\tCon_Footerf(con, true, \" ^[Give\\\\impulse\\\\9^]\");\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn;\n\t}\n\tc = Info_ValueForKey(info, \"menu\");\n\tif (*c && !strchr(c, ';') && !strchr(c, '\\n'))\n\t{\n\t\tCbuf_AddText(va(\"\\nmenu_cmd conlink %s\\n\", c), RESTRICT_LOCAL);\n\t\treturn;\n\t}\n\tc = Info_ValueForKey(info, \"connect\");\n\tif (*c && !strchr(c, ';') && !strchr(c, '\\n'))\n\t{\n\t\tCbuf_AddText(va(\"\\nconnect \\\"%s\\\"\\n\", c), RESTRICT_LOCAL);\n\t\treturn;\n\t}\n\tc = Info_ValueForKey(info, \"join\");\n\tif (*c && !strchr(c, ';') && !strchr(c, '\\n'))\n\t{\n\t\tCbuf_AddText(va(\"\\njoin \\\"%s\\\"\\n\", c), RESTRICT_LOCAL);\n\t\treturn;\n\t}\n\t/*c = Info_ValueForKey(info, \"url\");\n\tif (*c && !strchr(c, ';') && !strchr(c, '\\n'))\n\t{\n\t\tCbuf_AddText(va(\"\\nplayfilm %s\\n\", c), RESTRICT_LOCAL);\n\t\treturn;\n\t}*/\n\tc = Info_ValueForKey(info, \"observe\");\n\tif (*c && !strchr(c, ';') && !strchr(c, '\\n'))\n\t{\n\t\tCbuf_AddText(va(\"\\nobserve \\\"%s\\\"\\n\", c), RESTRICT_LOCAL);\n\t\treturn;\n\t}\n\tc = Info_ValueForKey(info, \"qtv\");\n\tif (*c && !strchr(c, ';') && !strchr(c, '\\n'))\n\t{\n\t\tCbuf_AddText(va(\"\\nqtvplay \\\"%s\\\"\\n\", c), RESTRICT_LOCAL);\n\t\treturn;\n\t}\n\tc = Info_ValueForKey(info, \"demo\");\n\tif (*c && !strchr(c, ';') && !strchr(c, '\\n'))\n\t{\n\t\tCbuf_AddText(va(\"\\nplaydemo \\\"%s\\\"\\n\", c), RESTRICT_LOCAL);\n\t\treturn;\n\t}\n#ifndef CLIENTONLY\n\tc = Info_ValueForKey(info, \"map\");\n\tif (*c && !strchr(c, ';') && !strchr(c, '\\n'))\n\t{\n\t\tCbuf_AddText(va(\"\\nmap \\\"%s\\\"\\n\", c), RESTRICT_LOCAL);\n\t\treturn;\n\t}\n#endif\n#ifndef NOBUILTINMENUS\n\tc = Info_ValueForKey(info, \"modelviewer\");\n\tif (*c && !strchr(c, ';') && !strchr(c, '\\n'))\n\t{\n\t\tCbuf_AddText(va(\"\\nmodelviewer \\\"%s\\\"\\n\", c), RESTRICT_LOCAL);\n\t\treturn;\n\t}\n#endif\n\tc = Info_ValueForKey(info, \"type\");\n\tif (*c)\n\t{\n\t\tKey_ConsoleReplace(c);\n\t\treturn;\n\t}\n\tc = Info_ValueForKey(info, \"cmd\");\n\tif (*c && !strchr(c, ';') && !strchr(c, '\\n'))\n\t{\n\t\tCbuf_AddText(va(\"\\ncmd %s\\n\", c), RESTRICT_LOCAL);\n\t\treturn;\n\t}\n\tc = Info_ValueForKey(info, \"say\");\n\tif (*c && !strchr(c, ';') && !strchr(c, '\\n'))\n\t{\n\t\tCbuf_AddText(va(\"\\nsay %s\\n\", c), RESTRICT_LOCAL);\n\t\treturn;\n\t}\n\tc = Info_ValueForKey(info, \"echo\");\n\tif (*c)\n\t{\n\t\tCon_Printf(\"%s\\n\", c);\n\t\treturn;\n\t}\n\tc = Info_ValueForKey(info, \"dir\");\n\tif (*c && !strchr(c, ';') && !strchr(c, '\\n'))\n\t{\n\t\tCbuf_AddText(va(\"\\necho Contents of %s:\\ndir \\\"%s\\\"\\n\", c, c), RESTRICT_LOCAL);\n\t\treturn;\n\t}\n#ifdef TEXTEDITOR\n\tc = Info_ValueForKey(info, \"edit\");\n\tif (*c && !strchr(c, ';') && !strchr(c, '\\n'))\n\t{\n\t\tCbuf_AddText(va(\"\\nedit \\\"%s\\\"\\n\", c), RESTRICT_LOCAL);\n\t\treturn;\n\t}\n#endif\n#ifdef SUBSERVERS\n\tc = Info_ValueForKey(info, \"ssv\");\n\tif (*c && !strchr(c, ';') && !strchr(c, '\\n'))\n\t{\n\t\tCbuf_AddText(va(\"\\nssv \\\"%s\\\"\\n\", c), RESTRICT_LOCAL);\n\t\treturn;\n\t}\n#endif\n#ifdef SUPPORT_ICE\n\tc = Info_ValueForKey(info, \"ice\");\n\tif (*c && !strchr(c, ';') && !strchr(c, '\\n'))\n\t{\n\t\tCbuf_AddText(va(\"\\nnet_ice_show \\\"%s\\\"\\n\", c), RESTRICT_LOCAL);\n\t\treturn;\n\t}\n#endif\n\tc = Info_ValueForKey(info, \"impulse\");\n\tif (*c && !strchr(c, ';') && !strchr(c, '\\n'))\n\t{\n\t\tCbuf_AddText(va(\"\\nimpulse %s\\n\", c), RESTRICT_LOCAL);\n\t\treturn;\n\t}\n#ifdef HAVE_MEDIA_DECODER\n\tc = Info_ValueForKey(info, \"film\");\n\tif (*c && !strchr(c, ';') && !strchr(c, '\\n'))\n\t{\n\t\tCbuf_AddText(va(\"\\nplayfilm \\\"%s\\\"\\n\", c), RESTRICT_LOCAL);\n\t\treturn;\n\t}\n#endif\n\tc = Info_ValueForKey(info, \"playaudio\");\n\tif (*c && !strchr(c, ';') && !strchr(c, '\\n'))\n\t{\n\t\tS_StartSound(0, 0x7ffffffe, S_PrecacheSound(c), NULL, NULL, 1, ATTN_NONE, 0, 0, CF_NOSPACIALISE);\n\t\treturn;\n\t}\n\tc = Info_ValueForKey(info, \"desc\");\n\tif (*c)\n\t{\n\t\tif (con)\n\t\t\tCon_Footerf(con, false, \"%s\", c);\n\t\treturn;\n\t}\n\n\t//if there's no info and the text starts with a leading / then insert it as a suggested/completed console command\n\t//skip any leading colour code.\n\tif (text[0] == '^' && text[1] >= '0' && text[1] <= '9')\n\t\ttext+=2;\n\tif (*text == '/')\n\t{\n\t\tint tlen;\n\t\tif (!cl_chatmode.ival)\n\t\t\ttext++;\t//NQ should generally not bother with the / in commands.\n\t\ttlen = info - text;\n\t\tZ_Free(key_lines[edit_line]);\n\t\tkey_lines[edit_line] = BZ_Malloc(tlen + 1);\n\t\tmemcpy(key_lines[edit_line], text, tlen);\n\t\tkey_lines[edit_line][tlen] = 0;\n\t\tkey_linepos = strlen(key_lines[edit_line]);\n\n\t\tKey_UpdateCompletionDesc();\n\t\treturn;\n\t}\n}\n\nvoid Key_HandleConsoleLink(console_t *con, char *buffer)\n{\n\tif (!buffer)\n\t\treturn;\n\tif (buffer[0] == '^' && buffer[1] == '[')\n\t{\n\t\t//looks like it might be a link!\n\t\tchar *end = NULL;\n\t\tchar *info;\n\t\tfor (info = buffer + 2; *info; )\n\t\t{\n\t\t\tif (info[0] == '^' && info[1] == ']')\n\t\t\t\tbreak; //end of tag, with no actual info, apparently\n\t\t\tif (*info == '\\\\')\n\t\t\t\tbreak;\n\t\t\telse if (info[0] == '^' && info[1] == '^')\n\t\t\t\tinfo+=2;\n\t\t\telse\n\t\t\t\tinfo++;\n\t\t}\n\t\tfor(end = info; *end; )\n\t\t{\n\t\t\tif (end[0] == '^' && end[1] == ']')\n\t\t\t{\n\t\t\t\t//okay, its a valid link that they clicked\n\t\t\t\t*end = 0;\n#ifdef PLUGINS\t//plugins can use these window things like popup menus.\n\t\t\t\tif (con && !Plug_ConsoleLink(buffer+2, info, con->name))\n#endif\n#ifdef CSQC_DAT\n\t\t\t\tif (!CSQC_ConsoleLink(buffer+2, info))\n#endif\n\t\t\t\t{\n\t\t\t\t\tKey_DefaultLinkClicked(con, buffer+2, info);\n\t\t\t\t}\n\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (end[0] == '^' && end[1] == '^')\n\t\t\t\tend+=2;\n\t\t\telse\n\t\t\t\tend++;\n\t\t}\n\t}\n}\n\nvoid Key_ConsoleRelease(console_t *con, int key, unsigned int unicode)\n{\n\tchar *buffer;\t\n\tif (key < 0)\n\t\tkey = 0;\n\n\tif ((key == K_TOUCHTAP || key == K_MOUSE1) && con->buttonsdown == CB_SELECT)\n\t{\n\t\tif (fabs(con->mousedown[0] - con->mousecursor[0]) < 5 && fabs(con->mousedown[1] - con->mousecursor[1]) < 5 && realtime < con->mousedowntime + 0.4)\n\t\t\tcon->buttonsdown = CB_TAPPED;\t//don't leave it selected.\n\t\telse\n\t\t\tcon->buttonsdown = CB_SELECTED;\n\t\treturn;\n\t}\n\tif ((key == K_TOUCHSLIDE || key == K_MOUSE1) && con->buttonsdown == CB_SCROLL)// || (key == K_MOUSE2 && con->buttonsdown == CB_SCROLL_R))\n\t{\n\t\tcon->buttonsdown = CB_NONE;\n\t\tif (fabs(con->mousedown[0] - con->mousecursor[0]) < 5 && fabs(con->mousedown[1] - con->mousecursor[1]) < 5)\n\t\t{\n\t\t\tbuffer = Con_CopyConsole(con, false, false, false);\n\t\t\tCon_Footerf(con, false, \"\");\n\t\t\tif (!buffer)\n\t\t\t\treturn;\n\t\t\tif (keydown[K_SHIFT])\n\t\t\t{\n\t\t\t\tint len;\n\t\t\t\tlen = strlen(buffer);\n\t\t\t\t//strip any trailing dots/elipsis\n\t\t\t\twhile (len > 1 && !strcmp(buffer+len-1, \".\"))\n\t\t\t\t{\n\t\t\t\t\tlen-=1;\n\t\t\t\t\tbuffer[len] = 0;\n\t\t\t\t}\n\t\t\t\t//strip any enclosing quotes\n\t\t\t\twhile (*buffer == '\\\"' && len > 2 && !strcmp(buffer+len-1, \"\\\"\"))\n\t\t\t\t{\n\t\t\t\t\tlen-=2;\n\t\t\t\t\tmemmove(buffer, buffer+1, len);\n\t\t\t\t\tbuffer[len] = 0;\n\t\t\t\t}\n\t\t\t\tKey_ConsoleInsert(buffer);\n\t\t\t}\n\t\t\telse\n\t\t\t\tKey_HandleConsoleLink(con, buffer);\n\t\t\tZ_Free(buffer);\n\t\t}\n\t\telse\n\t\t\tCon_Footerf(con, false, \"\");\n\t}\n\t/*if ((key == K_TOUCHLONG || key == K_MOUSE2) && con->buttonsdown == CB_COPY)\n\t{\n\t\tcon->buttonsdown = CB_NONE;\n\t\tbuffer = Con_CopyConsole(con, true, false, true);\t//don't keep markup if we're copying to the clipboard\n\t\tif (!buffer)\n\t\t\treturn;\n\t\tSys_SaveClipboard(CBT_CLIPBOARD,  buffer);\n\t\tZ_Free(buffer);\n\t}*/\n\tif (con->buttonsdown == CB_CLOSE)\n\t{\t//window X (close)\n\t\tif (con->mousecursor[0] > con->wnd_w-16 && con->mousecursor[1] < 8)\n\t\t{\n\t\t\tif (con->close && !con->close(con, false))\n\t\t\t\treturn;\n\t\t\tCon_Destroy (con);\n\t\t\treturn;\n\t\t}\n\t}\n\tif (con->buttonsdown == CB_SELECTED || con->buttonsdown == CB_TAPPED)\n\t\t;\t//will time out in the drawing code.\n\telse\n//\tif (con->buttonsdown == CB_MOVE)\t//window title(move)\n\t\tcon->buttonsdown = CB_NONE;\n\n#ifdef HAVE_MEDIA_DECODER\n\tif (con->backshader)\n\t{\n\t\tcin_t *cin = R_ShaderGetCinematic(con->backshader);\n\t\tif (cin)\n\t\t\tMedia_Send_KeyEvent(cin, key, unicode, 1);\n\t}\n#endif\n}\n\nstatic qbyte *emojidata = NULL;\nstatic const qbyte *builtinemojidata =\n//\t\t\"\\x02\\x03\" \":)\"\t\t\t\t\"\\xE2\\x98\\xBA\"\n\n#ifdef QUAKEHUD\n\t\"\\x04\\x03\" \":sg:\"\t\t\t\"\\xEE\\x84\\x82\"\n\t\"\\x05\\x03\" \":ssg:\"\t\t\t\"\\xEE\\x84\\x83\"\n\t\"\\x04\\x03\" \":ng:\"\t\t\t\"\\xEE\\x84\\x84\"\n\t\"\\x05\\x03\" \":sng:\"\t\t\t\"\\xEE\\x84\\x85\"\n\t\"\\x04\\x03\" \":gl:\"\t\t\t\"\\xEE\\x84\\x86\"\n\t\"\\x04\\x03\" \":rl:\"\t\t\t\"\\xEE\\x84\\x87\"\n\t\"\\x04\\x03\" \":lg:\"\t\t\t\"\\xEE\\x84\\x88\"\n\n\t\"\\x05\\x03\" \":sg2:\"\t\t\t\"\\xEE\\x84\\x92\"\n\t\"\\x06\\x03\" \":ssg2:\"\t\t\t\"\\xEE\\x84\\x93\"\n\t\"\\x05\\x03\" \":ng2:\"\t\t\t\"\\xEE\\x84\\x94\"\n\t\"\\x06\\x03\" \":sng2:\"\t\t\t\"\\xEE\\x84\\x95\"\n\t\"\\x05\\x03\" \":gl2:\"\t\t\t\"\\xEE\\x84\\x96\"\n\t\"\\x05\\x03\" \":rl2:\"\t\t\t\"\\xEE\\x84\\x97\"\n\t\"\\x05\\x03\" \":lg2:\"\t\t\t\"\\xEE\\x84\\x98\"\n\n\t\"\\x08\\x03\" \":shells:\"\t\t\"\\xEE\\x84\\xA0\"\n\t\"\\x07\\x03\" \":nails:\"\t\t\"\\xEE\\x84\\xA1\"\n\t\"\\x08\\x03\" \":rocket:\"\t\t\"\\xEE\\x84\\xA2\"\n\t\"\\x07\\x03\" \":cells:\"\t\t\"\\xEE\\x84\\xA3\"\n\t\"\\x04\\x03\" \":ga:\"\t\t\t\"\\xEE\\x84\\xA4\"\n\t\"\\x04\\x03\" \":ya:\"\t\t\t\"\\xEE\\x84\\xA5\"\n\t\"\\x04\\x03\" \":ra:\"\t\t\t\"\\xEE\\x84\\xA6\"\n\n\t\"\\x06\\x03\" \":key1:\"\t\t\t\"\\xEE\\x84\\xB0\"\n\t\"\\x06\\x03\" \":key2:\"\t\t\t\"\\xEE\\x84\\xB1\"\n\t\"\\x06\\x03\" \":ring:\"\t\t\t\"\\xEE\\x84\\xB2\"\n\t\"\\x06\\x03\" \":pent:\"\t\t\t\"\\xEE\\x84\\xB3\"\n\t\"\\x06\\x03\" \":suit:\"\t\t\t\"\\xEE\\x84\\xB4\"\n\t\"\\x06\\x03\" \":quad:\"\t\t\t\"\\xEE\\x84\\xB5\"\n\t\"\\x08\\x03\" \":sigil1:\"\t\t\"\\xEE\\x84\\xB6\"\n\t\"\\x08\\x03\" \":sigil2:\"\t\t\"\\xEE\\x84\\xB7\"\n\t\"\\x08\\x03\" \":sigil3:\"\t\t\"\\xEE\\x84\\xB8\"\n\t\"\\x08\\x03\" \":sigil4:\"\t\t\"\\xEE\\x84\\xB9\"\n\n\t\"\\x07\\x03\" \":face1:\"\t\t\"\\xEE\\x85\\x80\"\n\t\"\\x09\\x03\" \":face_p1:\"\t\t\"\\xEE\\x85\\x81\"\n\t\"\\x07\\x03\" \":face2:\"\t\t\"\\xEE\\x85\\x82\"\n\t\"\\x09\\x03\" \":face_p2:\"\t\t\"\\xEE\\x85\\x83\"\n\t\"\\x07\\x03\" \":face3:\"\t\t\"\\xEE\\x85\\x84\"\n\t\"\\x09\\x03\" \":face_p3:\"\t\t\"\\xEE\\x85\\x85\"\n\t\"\\x07\\x03\" \":face4:\"\t\t\"\\xEE\\x85\\x86\"\n\t\"\\x09\\x03\" \":face_p4:\"\t\t\"\\xEE\\x85\\x87\"\n\t\"\\x07\\x03\" \":face5:\"\t\t\"\\xEE\\x85\\x88\"\n\t\"\\x09\\x03\" \":face_p5:\"\t\t\"\\xEE\\x85\\x89\"\n\t\"\\x0c\\x03\" \":face_invis:\"\t\"\\xEE\\x85\\x8A\"\n\t\"\\x0d\\x03\" \":face_invul2:\"\t\"\\xEE\\x85\\x8B\"\n\t\"\\x0b\\x03\" \":face_inv2:\"\t\"\\xEE\\x85\\x8C\"\n\t\"\\x0b\\x03\" \":face_quad:\"\t\"\\xEE\\x85\\x8D\"\n#endif\n\t\"\";\nstatic void Key_LoadEmojiList(void)\n{\n\tqbyte line[1024];\n\tvfsfile_t *f;\n\tchar *json = FS_MallocFile(\"data-by-emoji.json\", FS_GAME, NULL);\t//https://unpkg.com/unicode-emoji-json/data-by-emoji.json\n\n\temojidata = Z_StrDup(builtinemojidata);\n\tif (json)\n\t{\n\t\t//eg: {\t\"utf8\":{\"slug\":\"text_for_emoji\"}, ... } (there's a few other keys*/\n\t\tjson_t *root = JSON_Parse(json);\n\t\tjson_t *def;\n\t\tchar nam[64];\n\t\tfor (def = (root?root->child:NULL); def; def = def->sibling)\n\t\t{\n\t\t\tint e;\n\t\t\tconst char *o;\n\t\t\tutf8_decode(&e, def->name, &o);\n\t\t\tif (*o)\n\t\t\t\tcontinue;\t//we can only cope with single codepoints.\n\t\t\tif (JSON_GetString(def, \"slug\", nam+1, sizeof(nam)-2, NULL))\n\t\t\t{\n\t\t\t\tnam[0] = ':';\n\t\t\t\tQ_strncatz(nam, \":\", sizeof(nam));\n\t\t\t\tline[0] = strlen(nam);\n\t\t\t\tline[1] = strlen(def->name);\n\t\t\t\tstrcpy(line+2, nam);\n\t\t\t\tstrcpy(line+2+line[0], def->name);\n\t\t\t\tZ_StrCat((char**)&emojidata, line);\n\t\t\t}\n\t\t}\n\t\tJSON_Destroy(root);\n\t\tFS_FreeFile(json);\n\t}\n\n\tf = FS_OpenVFS(\"emoji.lst\", \"rb\", FS_GAME);\n\tif (f)\n\t{\n\t\tqbyte line[1024];\n\t\tchar nam[64];\n\t\tchar rep[64];\n\t\twhile (VFS_GETS(f, line, sizeof(line)))\n\t\t{\n\t\t\tCOM_ParseTokenOut(COM_ParseTokenOut(line, NULL, nam, sizeof(nam), NULL), NULL, rep, sizeof(rep), NULL);\n\t\t\tif (!*nam || !*rep)\n\t\t\t\tcontinue;\t//next line then, I guess.\n\t\t\tline[0] = strlen(nam);\n\t\t\tline[1] = strlen(rep);\n\t\t\tstrcpy(line+2, nam);\n\t\t\tstrcpy(line+2+line[0], rep);\n\t\t\tZ_StrCat((char**)&emojidata, line);\n\t\t}\n\t\tVFS_CLOSE(f);\n\t}\n}\nvoid Key_EmojiCompletion_c(int argn, const char *partial, struct xcommandargcompletioncb_s *ctx)\n{\n\tchar guess[256];\n\tchar repl[256];\n\tsize_t ofs, len;\n\tif (*partial != ':')\n\t\treturn;\t//don't show annoying completion crap.\n\tif (!emojidata)\n\t\tKey_LoadEmojiList();\n\tlen = strlen(partial);\n\tfor (ofs = 0; emojidata[ofs]; )\n\t{\n\t\tif (len <= emojidata[ofs+0])\n\t\t\tif (!strncmp(partial, &emojidata[ofs+2], len))\n\t\t\t{\n\t\t\t\tmemcpy(guess, &emojidata[ofs+2], emojidata[ofs+0]);\n\t\t\t\tguess[emojidata[ofs+0]] = 0;\n\n\t\t\t\tmemcpy(repl, &emojidata[ofs+2]+emojidata[ofs+0], emojidata[ofs+1]);\n\t\t\t\trepl[emojidata[ofs+1]] = 0;\n\t\t\t\tctx->cb(guess, NULL, NULL, ctx);\n\t\t\t}\n\t\tofs += 2+emojidata[ofs+0]+emojidata[ofs+1];\n\t}\n}\n\nconst char *Key_Demoji(char *buffer, size_t buffersize, const char *in)\n{\n\tchar *estart = strchr(in, ':');\n\tsize_t ofs;\n\tchar *out = buffer, *outend = buffer+buffersize-1;\n\tif (!estart)\n\t\treturn in;\n\n\tif (!emojidata)\n\t\tKey_LoadEmojiList();\n\n\tfor(; estart; )\n\t{\n\t\tif (out + (estart-in) >= outend)\n\t\t\tbreak; //not enough space\n\t\tmemcpy(out, in, estart-in);\n\t\tout += estart-in;\n\t\tin = estart;\n\n\t\tfor (ofs = 0; emojidata[ofs]; )\n\t\t{\n\t\t\tif (!strncmp(in, &emojidata[ofs+2], emojidata[ofs+0]))\n\t\t\t\tbreak;\t//its this one!\n\t\t\tofs += 2+emojidata[ofs+0]+emojidata[ofs+1];\n\t\t}\n\t\tif (emojidata[ofs])\n\t\t{\n\t\t\tif (out + emojidata[ofs+1] >= outend)\n\t\t\t{\n\t\t\t\tin = \"\";\t//no half-emoji\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tin += emojidata[ofs+0];\n\t\t\tmemcpy(out, &emojidata[ofs+2]+emojidata[ofs+0], emojidata[ofs+1]);\n\t\t\tout += emojidata[ofs+1];\n\t\t\testart = strchr(in, ':');\n\t\t}\n\t\telse\n\t\t{\n\t\t\testart = strchr(in+1, ':');\n\t\t}\n\t}\n\twhile (*in && out < outend)\n\t\t*out++ = *in++;\n\t*out = 0;\n\treturn buffer;\n}\n\n//if the referenced (trailing) chevron is doubled up, then it doesn't act as part of any markup and should be ignored for such things.\nstatic qboolean utf_specialchevron(unsigned char *start, unsigned char *chev)\n{\n\tint count = 0;\n\twhile (chev >= start)\n\t{\n\t\tif (*chev-- == '^')\n\t\t\tcount++;\n\t\telse\n\t\t\tbreak;\n\t}\n\treturn count&1;\n}\n//move the cursor one char to the left. cursor must be within the 'start' string.\nstatic unsigned char *utf_left(unsigned char *start, unsigned char *cursor, qboolean skiplink)\n{\n\tif (cursor == start)\n\t\treturn cursor;\n\tif (1)//com_parseutf8.ival>0)\n\t{\n\t\tcursor--;\n\t\twhile ((*cursor & 0xc0) == 0x80 && cursor > start)\n\t\t\tcursor--;\n\t}\n\telse\n\t\tcursor--;\n\n\t//FIXME: should verify that the ^ isn't doubled.\n\tif (*cursor == ']' && cursor > start && skiplink && utf_specialchevron(start, cursor-1))\n\t{\n\t\t//just stepped onto a link\n\t\tunsigned char *linkstart;\n\t\tlinkstart = cursor-1;\n\t\twhile(linkstart >= start)\n\t\t{\n\t\t\t//FIXME: should verify that the ^ isn't doubled.\n\t\t\tif (utf_specialchevron(start, linkstart) && linkstart[1] == '[')\n\t\t\t\treturn linkstart;\n\t\t\tlinkstart--;\n\t\t}\n\t}\n\n\treturn cursor;\n}\n\n//move the cursor one char to the right.\nstatic unsigned char *utf_right(unsigned char *start, unsigned char *cursor, qboolean skiplink)\n{\n\t//FIXME: should make sure this is not doubled.\n\tif (utf_specialchevron(start, cursor) && cursor[1] == '[' && skiplink)\n\t{\n\t\t//just stepped over a link\n\t\tchar *linkend;\n\t\tlinkend = cursor+2;\n\t\twhile(*linkend)\n\t\t{\n\t\t\tif (utf_specialchevron(start, linkend) && linkend[1] == ']')\n\t\t\t\treturn linkend+2;\n\t\t\telse\n\t\t\t\tlinkend++;\n\t\t}\n\t\treturn linkend;\n\t}\n\n\tif (1)//com_parseutf8.ival>0)\n\t{\n\t\tint skip = 1;\n\t\t//figure out the length of the char\n\t\tif ((*cursor & 0xc0) == 0x80)\n\t\t\tskip = 1;\t//error\n\t\telse if ((*cursor & 0xe0) == 0xc0)\n\t\t\tskip = 2;\n\t\telse if ((*cursor & 0xf0) == 0xe0)\n\t\t\tskip = 3;\n\t\telse if ((*cursor & 0xf1) == 0xf0)\n\t\t\tskip = 4;\n\t\telse if ((*cursor & 0xf3) == 0xf1)\n\t\t\tskip = 5;\n\t\telse if ((*cursor & 0xf7) == 0xf3)\n\t\t\tskip = 6;\n\t\telse if ((*cursor & 0xff) == 0xf7)\n\t\t\tskip = 7;\n\t\telse skip = 1;\n\n\t\twhile (*cursor && skip)\n\t\t{\n\t\t\tcursor++;\n\t\t\tskip--;\n\t\t}\n\t}\n\telse if (*cursor)\n\t\tcursor++;\n\n\treturn cursor;\n}\n\nvoid Key_EntryInsert(unsigned char **line, int *linepos, const char *instext)\n{\n\tint i;\n\tint len, olen;\n\tchar *old;\n\n\tif (!*instext)\n\t\treturn;\n\n\told = (*line);\n\tlen = strlen(instext);\n\tolen = strlen(old);\n\t*line = BZ_Malloc(olen + len + 1);\n\tmemcpy(*line, old, *linepos);\n\tmemcpy(*line+*linepos, instext, len);\n\tmemcpy(*line+*linepos+len, old+*linepos, olen - *linepos+1);\n\tZ_Free(old);\n\tfor (i = *linepos; i < *linepos+len; i++)\n\t{\n\t\tif ((*line)[i] == '\\r')\n\t\t\t(*line)[i] = ' ';\n\t\telse if ((*line)[i] == '\\n')\n\t\t\t(*line)[i] = ';';\n\t}\n\t*linepos += len;\n}\n\nstatic void Key_ConsolePaste(void *ctx, const char *utf8)\n{\n\tunsigned char **line = ctx;\n\tint *linepos = ((line == &chat_buffer)?&chat_bufferpos:&key_linepos);\n\tif (utf8)\n\t\tKey_EntryInsert(line, linepos, utf8);\n}\nqboolean Key_EntryLine(console_t *con, unsigned char **line, int lineoffset, int *linepos, int key, unsigned int unicode)\n{\n\tqboolean alt = keydown[K_LALT] || keydown[K_RALT];\n\tqboolean ctrl = keydown[K_LCTRL] || keydown[K_RCTRL];\n\tqboolean shift = keydown[K_LSHIFT] || keydown[K_RSHIFT];\n\tchar utf8[8];\n\n\tif (key == K_LEFTARROW || key == K_KP_LEFTARROW || key == K_GP_DPAD_LEFT)\n\t{\n\t\tif (ctrl)\n\t\t{\n\t\t\t//ignore whitespace if we're at the end of the word\n\t\t\twhile (*linepos > 0 && (*line)[*linepos-1] == ' ')\n\t\t\t\t*linepos = utf_left((*line)+lineoffset, (*line) + *linepos, !alt) - (*line);\n\t\t\t//keep skipping until we find the start of that word\n\t\t\twhile (ctrl && *linepos > lineoffset && (*line)[*linepos-1] != ' ')\n\t\t\t\t*linepos = utf_left((*line)+lineoffset, (*line) + *linepos, !alt) - (*line);\n\t\t}\n\t\telse\n\t\t\t*linepos = utf_left((*line)+lineoffset, (*line) + *linepos, !alt) - (*line);\n\t\treturn true;\n\t}\n\tif (key == K_RIGHTARROW || key == K_KP_RIGHTARROW || key == K_GP_DPAD_RIGHT)\n\t{\n\t\tif ((*line)[*linepos])\n\t\t{\n\t\t\t*linepos = utf_right((*line)+lineoffset, (*line) + *linepos, !alt) - (*line);\n\t\t\tif (ctrl)\n\t\t\t{\n\t\t\t\t//skip over the word\n\t\t\t\twhile ((*line)[*linepos] && (*line)[*linepos] != ' ')\n\t\t\t\t\t*linepos = utf_right((*line)+lineoffset, (*line) + *linepos, !alt) - (*line);\n\t\t\t\t//as well as any trailing whitespace\n\t\t\t\twhile ((*line)[*linepos] == ' ')\n\t\t\t\t\t*linepos = utf_right((*line)+lineoffset, (*line) + *linepos, !alt) - (*line);\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\t\telse\n\t\t\tunicode = ' ';\n\t}\n\n\tif (key == K_DEL || key == K_KP_DEL)\n\t{\n\t\tif ((*line)[*linepos])\n\t\t{\n\t\t\tint charlen = utf_right((*line)+lineoffset, (*line) + *linepos, !alt) - ((*line) + *linepos);\n\t\t\tmemmove((*line)+*linepos, (*line)+*linepos+charlen, strlen((*line)+*linepos+charlen)+1);\n\t\t\treturn true;\n\t\t}\n\t\telse\n\t\t\tkey = K_BACKSPACE;\n\t}\n\n\tif (key == K_BACKSPACE)\n\t{\n\t\tif (*linepos > lineoffset)\n\t\t{\n\t\t\tint charlen = ((*line)+*linepos) - utf_left((*line)+lineoffset, (*line) + *linepos, !alt);\n\t\t\tmemmove((*line)+*linepos-charlen, (*line)+*linepos, strlen((*line)+*linepos)+1);\n\t\t\t*linepos -= charlen;\n\t\t}\n\n\t\tif (con_commandmatch)\n\t\t{\n\t\t\tCon_Footerf(NULL, false, \"\");\n\t\t\tcon_commandmatch = (**line?1:0);\n\t\t}\n\t\treturn true;\n\t}\n\n\n\n\tif (key == K_HOME || key == K_KP_HOME)\n\t{\n\t\t*linepos = lineoffset;\n\t\treturn true;\n\t}\n\n\tif (key == K_END || key == K_KP_END)\n\t{\n\t\t*linepos = strlen(*line);\n\t\treturn true;\n\t}\n\n\t//beware that windows translates ctrl+c and ctrl+v to a control char\n\tif (((unicode=='C' || unicode=='c' || unicode==3) && ctrl) || (ctrl && key == K_INS))\n\t{\n\t\tif (con && (con->flags & CONF_KEEPSELECTION))\n\t\t{\t//copy selected text to the system clipboard\n\t\t\tchar *buffer = Con_CopyConsole(con, true, false, true);\n\t\t\tif (buffer)\n\t\t\t{\n\t\t\t\tSys_SaveClipboard(CBT_CLIPBOARD,  buffer);\n\t\t\t\tZ_Free(buffer);\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\t\t//copy the entire input line if there's nothing selected.\n\t\tSys_SaveClipboard(CBT_CLIPBOARD, *line);\n\t\treturn true;\n\t}\n\n\tif (key == K_MOUSE3)\n\t{\t//middle-click to paste from the unixy primary buffer.\n\t\tSys_Clipboard_PasteText(CBT_SELECTION, Key_ConsolePaste, line);\n\t\treturn true;\n\t}\n\tif (((unicode=='V' || unicode=='v' || unicode==22/*sync*/) && ctrl) || (shift && key == K_INS))\n\t{\t//ctrl+v to paste from the windows-style clipboard.\n\t\tSys_Clipboard_PasteText(CBT_CLIPBOARD, Key_ConsolePaste, line);\n\t\treturn true;\n\t}\n\n\tif ((unicode=='X' || unicode=='x' || unicode==24/*cancel*/) && ctrl)\n\t{\t//cut - copy-to-clipboard-and-delete\n\t\tSys_SaveClipboard(CBT_CLIPBOARD, *line);\n\t\t(*line)[lineoffset] = 0;\n\t\t*linepos = strlen(*line);\n\t\treturn true;\n\t}\n\tif ((unicode=='U' || unicode=='u' || unicode==21/*nak*/) && ctrl)\n\t{\t//clear line\n\t\t(*line)[lineoffset] = 0;\n\t\t*linepos = strlen(*line);\n\t\treturn true;\n\t}\n\n\tif (unicode < ' ')\n\t{\n\t\t//if the user is entering control codes, then the ctrl+foo mechanism is probably unsupported by the unicode input stuff, so give best-effort replacements.\n\t\tswitch(unicode)\n\t\t{\n\t\tcase 27/*'['*/: unicode = 0xe010; break;\n\t\tcase 29/*']'*/: unicode = 0xe011; break;\n\t\tcase 7/*'g'*/: unicode = 0xe086; break;\n\t\tcase 18/*'r'*/: unicode = 0xe087; break;\n\t\tcase 25/*'y'*/: unicode = 0xe088; break;\n\t\tcase 2/*'b'*/: unicode = 0xe089; break;\n\t\tcase 19/*'s'*/: unicode = 0xe080; break;\n\t\tcase 4/*'d'*/: unicode = 0xe081; break;\n\t\tcase 6/*'f'*/: unicode = 0xe082; break;\n\t\tcase 1/*'a'*/: unicode = 0xe083; break;\n\t\tcase 21/*'u'*/: unicode = 0xe01d; break;\n\t\tcase 9/*'i'*/: unicode = 0xe01e; break;\n\t\tcase 15/*'o'*/: unicode = 0xe01f; break;\n\t\tcase 10/*'j'*/: unicode = 0xe01c; break;\n\t\tcase 16/*'p'*/: unicode = 0xe09c; break;\n\t\tcase 13/*'m'*/: unicode = 0xe08b; break;\n\t\tcase 11/*'k'*/: unicode = 0xe08d; break;\n\t\tcase 14/*'n'*/: unicode = '\\r'; break;\n\t\tdefault:\n//\t\t\tif (unicode)\n//\t\t\t\tCon_Printf(\"escape code %i\\n\", unicode);\n\n\t\t\t//even if we don't print these, we still need to cancel them in the caller.\n\t\t\tif (key == K_LALT || key == K_RALT ||\n\t\t\t\tkey == K_LCTRL || key == K_RCTRL ||\n\t\t\t\tkey == K_LSHIFT || key == K_RSHIFT)\n\t\t\t\treturn true;\n\t\t\treturn false;\n\t\t}\n\t}\n#ifndef FTE_TARGET_WEB\t//browser port gets keys stuck down when task switching, especially alt+tab. don't confuse users.\n\telse if (com_parseutf8.ival >= 0)\t//don't do this for iso8859-1. the major user of that is hexen2 which doesn't have these chars.\n\t{\n\t\tif (ctrl && !keydown[K_RALT])\n\t\t{\n\t\t\tif (unicode >= '0' && unicode <= '9')\n\t\t\t\tunicode = unicode - '0' + 0xe012;\t// yellow number\n\t\t\telse switch (unicode)\n\t\t\t{\n\t\t\t\tcase '[': unicode = 0xe010; break;\n\t\t\t\tcase ']': unicode = 0xe011; break;\n\t\t\t\tcase 'g': unicode = 0xe086; break;\n\t\t\t\tcase 'r': unicode = 0xe087; break;\n\t\t\t\tcase 'y': unicode = 0xe088; break;\n\t\t\t\tcase 'b': unicode = 0xe089; break;\n\t\t\t\tcase '(': unicode = 0xe080; break;\n\t\t\t\tcase '=': unicode = 0xe081; break;\n\t\t\t\tcase ')': unicode = 0xe082; break;\n\t\t\t\tcase 'a': unicode = 0xe083; break;\n\t\t\t\tcase '<': unicode = 0xe01d; break;\n\t\t\t\tcase '-': unicode = 0xe01e; break;\n\t\t\t\tcase '>': unicode = 0xe01f; break;\n\t\t\t\tcase ',': unicode = 0xe01c; break;\n\t\t\t\tcase '.': unicode = 0xe09c; break;\n\t\t\t\tcase 'B': unicode = 0xe08b; break;\n\t\t\t\tcase 'C': unicode = 0xe08d; break;\n\t\t\t\tcase 'n': unicode = '\\r'; break;\n\t\t\t}\n\t\t}\n\n\t\tif (keydown[K_LALT] && unicode > 32 && unicode < 128)\n\t\t\tunicode |= 0xe080;\t\t// red char\n\t}\n#endif\n\n\tunicode = utf8_encode(utf8, unicode, sizeof(utf8)-1);\n\tif (unicode)\n\t{\n\t\tutf8[unicode] = 0;\n\t\tKey_EntryInsert(line, linepos, utf8);\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\n/*\n====================\nKey_Console\n\nInteractive line editing and console scrollback\n====================\n*/\nqboolean Key_Console (console_t *con, int key, unsigned int unicode)\n{\n\tqboolean ctrl = keydown[K_LCTRL] || keydown[K_RCTRL];\n\tqboolean shift = keydown[K_LSHIFT] || keydown[K_RSHIFT];\n\tint rkey = key;\n\tchar *buffer;\n\n\t//weirdness for the keypad.\n\tif ((unicode >= '0' && unicode <= '9') || unicode == '.' || key < 0)\n\t\tkey = 0;\n\n\tif (key == K_TAB && !(con->flags & CONF_ISWINDOW) && ctrl&&shift)\n\t{\t// cycle consoles with ctrl+shift+tab.\n\t\t// (ctrl+tab forces tab completion,\n\t\t//\tshift+tab controls completion cycle,\n\t\t//  so it has to be both.)\n\t\tCon_CycleConsole();\n\t\treturn true;\n\t}\n\tif (con->redirect)\n\t{\n\t\tif (key == K_TOUCHTAP || key == K_TOUCHSLIDE || key == K_TOUCHLONG || key == K_MOUSE1 || key == K_MOUSE2)\n\t\t\t;\n\t\telse if (con->redirect(con, unicode, key))\n\t\t\treturn true;\n\t}\n\n\tif (key == K_GP_VIEW || key == K_GP_MENU)\n\t{\n\t\tKey_Dest_Remove(kdm_console);\n\t\tKey_Dest_Remove(kdm_cwindows);\n\t\tif (!cls.state && !Key_Dest_Has(~kdm_game))\n\t\t\tM_ToggleMenu_f ();\n\t\treturn true;\n\t}\n\n\tif (key == K_TOUCHTAP || key == K_TOUCHSLIDE || key == K_TOUCHLONG || key == K_MOUSE1 || key == K_MOUSE2)\n\t{\n\t\tint olddown[2] = {con->mousedown[0],con->mousedown[1]};\n\t\tif (con->flags & CONF_ISWINDOW)\n\t\t\tif (con->mousecursor[0] < -8 || con->mousecursor[1] < 0 || con->mousecursor[0] > con->wnd_w || con->mousecursor[1] > con->wnd_h)\n\t\t\t\treturn true;\n\t\tif (con == con_mouseover)\n\t\t{\n\t\t\tcon->buttonsdown = CB_NONE;\n\n\t\t\tif ((con->flags & CONF_ISWINDOW) && !keydown[K_SHIFT])\n\t\t\t\tCon_SetActive(con);\n\t\t}\n\t\tcon->mousedown[0] = con->mousecursor[0];\n\t\tcon->mousedown[1] = con->mousecursor[1];\n\t\tif (con_mouseover && con->mousedown[1] < 8)//(8.0*vid.height)/vid.pixelheight)\n\t\t{\n\t\t\tif ((key == K_MOUSE2||key==K_TOUCHLONG) && !(con->flags & CONF_ISWINDOW))\n\t\t\t{\n\t\t\t\tif (con->close && !con->close(con, false))\n\t\t\t\t\treturn true;\n\t\t\t\tCon_Destroy (con);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tCon_SetActive(con);\n\t\t\t\tif ((con->flags & CONF_ISWINDOW))\n\t\t\t\t\tcon->buttonsdown = (con->mousedown[0] > con->wnd_w-16)?CB_CLOSE:CB_MOVE;\n\t\t\t}\n\t\t}\n\t\telse if (con_mouseover && con->mousedown[1] < 16)\n\t\t\tcon->buttonsdown = CB_ACTIONBAR;\n\t\telse if (key == K_MOUSE2)\n\t\t{\n\t\t\tif (con->redirect && con->redirect(con, unicode, key))\n\t\t\t\treturn true;\n\n\t\t\tcon->flags &= ~CONF_KEEPSELECTION;\n\t\t\tcon->buttonsdown = CB_SCROLL_R;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tcon->buttonsdown = CB_NONE;\n\t\t\tif ((con->flags & CONF_ISWINDOW) && con->mousedown[0] < 0)\n\t\t\t\tcon->buttonsdown |= CB_SIZELEFT;\n\t\t\tif ((con->flags & CONF_ISWINDOW) && con->mousedown[0] > con->wnd_w-16)\n\t\t\t\tcon->buttonsdown |= CB_SIZERIGHT;\n\t\t\tif ((con->flags & CONF_ISWINDOW) && con->mousedown[1] > con->wnd_h-8)\n\t\t\t\tcon->buttonsdown |= CB_SIZEBOTTOM;\n\t\t\tif (con->buttonsdown == CB_NONE)\n\t\t\t{\n\t\t\t\tif (con->redirect && con->redirect(con, unicode, key))\n\t\t\t\t\treturn true;\n#ifdef HAVE_MEDIA_DECODER\n\t\t\t\tif (con->backshader && R_ShaderGetCinematic(con->backshader))\n\t\t\t\t{\n\t\t\t\t\tMedia_Send_KeyEvent(R_ShaderGetCinematic(con->backshader), rkey, unicode, 0);\n\t\t\t\t\treturn true;\n\t\t\t\t}\n#endif\n\t\t\t\tif (key == K_TOUCHSLIDE || con->mousecursor[0] > ((con->flags & CONF_ISWINDOW)?con->wnd_w-16:vid.width)-8)\n\t\t\t\t{\t//just scroll the console up/down\n\t\t\t\t\tcon->buttonsdown = CB_SCROLL;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\t//selecting text. woo.\n\t\t\t\t\tif (realtime < con->mousedowntime + 0.4\n\t\t\t\t\t\t&& con->mousecursor[0] >= olddown[0]-3 && con->mousecursor[0] <= olddown[0]+3\n\t\t\t\t\t\t&& con->mousecursor[1] >= olddown[1]-3 && con->mousecursor[1] <= olddown[1]+3\n\t\t\t\t\t\t)\n\t\t\t\t\t{\t//this was a double-click... expand the selection to the entire word\n\t\t\t\t\t\t//FIXME: detect tripple-clicks to select the entire line\n\t\t\t\t\t\tCon_ExpandConsoleSelection(con);\n\t\t\t\t\t\tcon->flags |= CONF_KEEPSELECTION;\n\n\t\t\t\t\t\tbuffer = Con_CopyConsole(con, true, false, true);\t//don't keep markup if we're copying to the clipboard\n\t\t\t\t\t\tif (buffer)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tSys_SaveClipboard(CBT_SELECTION,  buffer);\n\t\t\t\t\t\t\tZ_Free(buffer);\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t\tcon->mousedowntime = realtime;\n\n\t\t\t\t\tcon->buttonsdown = CB_SELECT;\n\t\t\t\t\tcon->flags &= ~CONF_KEEPSELECTION;\n\n\t\t\t\t\tif (shift)\n\t\t\t\t\t{\n\t\t\t\t\t\tcon->mousedown[0] = olddown[0];\n\t\t\t\t\t\tcon->mousedown[1] = olddown[1];\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (con->buttonsdown == CB_SCROLL && !con->linecount && (!con->linebuffered || con->linebuffered == Con_Navigate))\n\t\t\tcon->buttonsdown = CB_NONE;\n\t\telse\n\t\t\treturn true;\n\t}\n\tif (key == K_TOUCH)\n\t\treturn true;\t//eat it, so we don't get any kind of mouse emu junk\n\n\tif (key == K_PGUP || key == K_KP_PGUP || key==K_MWHEELUP || key == K_GP_LEFT_THUMB_UP)\n\t{\n\t\tconline_t *l;\n\t\tint i = 2;\n\t\tif (ctrl)\n\t\t\ti = 8;\n\t\tif (!con->display)\n\t\t\treturn true;\n//\t\tif (con->display == con->current)\n//\t\t\ti+=2;\t//skip over the blank input line, and extra so we actually move despite the addition of the ^^^^^ line\n\t\tif (con->display->older != NULL)\n\t\t{\n\t\t\tcon->displayscroll += i;\n\t\t\twhile (con->displayscroll >= con->display->numlines)\n\t\t\t{\n\t\t\t\tif (con->display->older == NULL)\n\t\t\t\t\tbreak;\n\t\t\t\tcon->displayscroll -= con->display->numlines;\n\t\t\t\tcon->display = con->display->older;\n\t\t\t\tcon->display->time = realtime;\n\t\t\t}\n\t\t\tfor (l = con->display; l; l = l->older)\n\t\t\t\tl->time = realtime;\n\t\t\treturn true;\n\t\t}\n\t}\n\tif (key == K_PGDN || key == K_KP_PGDN || key==K_MWHEELDOWN || key == K_GP_LEFT_THUMB_DOWN)\n\t{\n\t\tint i = 2;\n\t\tif (ctrl)\n\t\t\ti = 8;\n\t\tif (!con->display)\n\t\t\treturn true;\n\t\tif (con->display->newer != NULL)\n\t\t{\n\t\t\tcon->displayscroll -= i;\n\t\t\twhile (con->displayscroll < 0)\n\t\t\t{\n\t\t\t\tif (con->display->newer == NULL)\n\t\t\t\t\tbreak;\n\t\t\t\tcon->display = con->display->newer;\n\t\t\t\tcon->display->time = realtime;\n\t\t\t\tcon->displayscroll += con->display->numlines;\n\t\t\t}\n\t\t\tif (con->display->newer && con->display->newer == con->current)\n\t\t\t{\n\t\t\t\tcon->display = con->current;\n\t\t\t\tcon->displayscroll = 0;\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\t}\n\n\tif ((key == K_HOME || key == K_KP_HOME) && ctrl)\n\t{\n\t\tif (con->display != con->oldest)\n\t\t{\n\t\t\tcon->displayscroll = 0;\n\t\t\tcon->display = con->oldest;\n\t\t\treturn true;\n\t\t}\n\t}\n\n\tif ((key == K_END || key == K_KP_END) && ctrl)\n\t{\n\t\tif (con->display != con->current)\n\t\t{\n\t\t\tcon->displayscroll = 0;\n\t\t\tcon->display = con->current;\n\t\t\treturn true;\n\t\t}\n\t}\n\n#ifdef TEXTEDITOR\n\tif (editormodal)\n\t{\n\t\tif (Editor_Key(key, unicode))\n\t\t\treturn true;\n\t}\n#endif\n\n\tif (key == K_MOUSE3)\t//mousewheel click/middle button\n\t{\n\t}\n\n\t//console does not have any way to accept input, so don't try giving it any.\n\tif (!con->linebuffered)\n\t{\n#ifdef HAVE_MEDIA_DECODER\n\t\tif (con->backshader)\n\t\t{\n\t\t\tcin_t *cin = R_ShaderGetCinematic(con->backshader);\n\t\t\tif (cin)\n\t\t\t{\n\t\t\t\tMedia_Send_KeyEvent(cin, rkey, unicode, 0);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n#endif\n\t\treturn false;\n\t}\n\t\n\tif (key == K_ENTER || key == K_KP_ENTER || key == K_GP_DIAMOND_CONFIRM)\n\t{\t// backslash text are commands, else chat\n\t\tchar demoji[8192];\n\t\tconst char *txt = Key_Demoji(demoji, sizeof(demoji), key_lines[edit_line]);\n\n#ifndef FTE_TARGET_WEB\n\t\tif (keydown[K_LALT] || keydown[K_RALT])\n\t\t\tCbuf_AddText(\"\\nvid_toggle\\n\", RESTRICT_LOCAL);\n#endif\n\n\t\tif ((con_commandmatch && !strchr(txt, ' ')) || shift)\n\t\t{\t//if that isn't actually a command, and we can actually complete it to something, then lets try to complete it.\n\t\t\tif (*txt == '/')\n\t\t\t\ttxt++;\n\n\t\t\tif ((shift||!Cmd_IsCommand(txt)) && Cmd_CompleteCommand(txt, true, true, con_commandmatch, NULL))\n\t\t\t{\n\t\t\t\tCompleteCommand (true, 1);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tCon_Footerf(con, false, \"\");\n\t\t\tcon_commandmatch = 0;\n\t\t}\n\n\n\t\tif (con->linebuffered)\n\t\t{\n\t\t\tif (con->linebuffered(con, txt) != 2)\n\t\t\t{\n\t\t\t\tedit_line = (edit_line + 1) & (CON_EDIT_LINES_MASK);\n\t\t\t\thistory_line = edit_line;\n\t\t\t}\n\t\t}\n\n\t\tZ_Free(key_lines[edit_line]);\n\t\tkey_lines[edit_line] = BZ_Malloc(1);\n\t\tkey_lines[edit_line][0] = '\\0';\n\t\tkey_linepos = 0;\n\t\tcon_commandmatch = 0;\n\t\treturn true;\n\t}\n\n\tif (key == K_SPACE && ctrl && con->commandcompletion)\n\t{\n\t\tchar *txt = key_lines[edit_line];\n\t\tif (*txt == '/')\n\t\t\ttxt++;\n\t\tif (Cmd_CompleteCommand(txt, true, true, con->commandcompletion, NULL))\n\t\t{\n\t\t\tCompleteCommand (true, 1);\n\t\t\treturn true;\n\t\t}\n\t}\n\n\tif (key == K_TAB)\n\t{\t// command completion\n\t\tif (con->commandcompletion)\n\t\t\tCompleteCommand (ctrl, shift?-1:1);\n\t\treturn true;\n\t}\n\t\n\tif (key == K_UPARROW || key == K_KP_UPARROW || key == K_GP_DPAD_UP)\n\t{\n\t\tdo\n\t\t{\n\t\t\thistory_line = (history_line - 1) & CON_EDIT_LINES_MASK;\n\t\t} while (history_line != edit_line\n\t\t\t\t&& !key_lines[history_line][0]);\n\t\tif (history_line == edit_line)\n\t\t\thistory_line = (edit_line+1)&CON_EDIT_LINES_MASK;\n\t\tkey_lines[edit_line] = BZ_Realloc(key_lines[edit_line], strlen(key_lines[history_line])+1);\n\t\tQ_strcpy(key_lines[edit_line], key_lines[history_line]);\n\t\tkey_linepos = Q_strlen(key_lines[edit_line]);\n\n\t\tcon_commandmatch = 0;\n\t\treturn true;\n\t}\n\n\tif (key == K_DOWNARROW || key == K_KP_DOWNARROW || key == K_GP_DPAD_DOWN)\n\t{\n\t\tif (history_line == edit_line)\n\t\t{\n\t\t\tkey_lines[edit_line][0] = '\\0';\n\t\t\tkey_linepos=0;\n\t\t\tcon_commandmatch = 0;\n\t\t\treturn true;\n\t\t}\n\t\tdo\n\t\t{\n\t\t\thistory_line = (history_line + 1) & CON_EDIT_LINES_MASK;\n\t\t}\n\t\twhile (history_line != edit_line\n\t\t\t&& !key_lines[history_line][1]);\n\t\tif (history_line == edit_line)\n\t\t{\n\t\t\tkey_lines[edit_line][0] = '\\0';\n\t\t\tkey_linepos = 0;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tkey_lines[edit_line] = BZ_Realloc(key_lines[edit_line], strlen(key_lines[history_line])+1);\n\t\t\tQ_strcpy(key_lines[edit_line], key_lines[history_line]);\n\t\t\tkey_linepos = Q_strlen(key_lines[edit_line]);\n\t\t}\n\t\treturn true;\n\t}\n\n\tif (rkey && !consolekeys[rkey])\n\t{\n\t\tif (rkey != '`' || key_linepos==0)\n\t\t\treturn false;\n\t}\n\n\tif (con_commandmatch)\n\t{\t//if they're typing, try to retain the current completion guess.\n\t\tcmd_completion_t *c;\n\t\tchar *txt = key_lines[edit_line];\n\t\tchar *guess = Cmd_CompleteCommand(*txt=='/'?txt+1:txt, true, true, con_commandmatch, NULL);\n\t\tKey_EntryLine(con, &key_lines[edit_line], 0, &key_linepos, key, unicode);\n\t\tif (!key_linepos)\n\t\t\tcon_commandmatch = 0;\n\t\telse if (guess)\n\t\t{\n\t\t\tguess = Z_StrDup(guess);\n\t\t\ttxt = key_lines[edit_line];\n\t\t\tc = Cmd_Complete(*txt=='/'?txt+1:txt, true);\n\t\t\tfor (con_commandmatch = c->num; con_commandmatch > 1; con_commandmatch--) \n\t\t\t{\n\t\t\t\tif (!strcmp(guess, c->completions[con_commandmatch-1].text))\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tZ_Free(guess);\n\t\t}\n\t}\n\telse\n\t\tKey_EntryLine(con, &key_lines[edit_line], 0, &key_linepos, key, unicode);\n\n\treturn true;\n}\n\n//============================================================================\n\nqboolean\tchat_team;\nunsigned char\t\t*chat_buffer;\nint\t\t\tchat_bufferpos;\n\nvoid Key_Message (int key, int unicode)\n{\n\tif (!chat_buffer)\n\t{\n\t\tchat_buffer = BZ_Malloc(1);\n\t\tchat_buffer[0] = 0;\n\t\tchat_bufferpos = 0;\n\t}\n\n\tif (key == K_ENTER || key == K_KP_ENTER || key == K_GP_DIAMOND_CONFIRM)\n\t{\n\t\tif (chat_buffer && chat_buffer[0])\n\t\t{\t//send it straight into the command.\n\t\t\tconst char *line = chat_buffer;\n\t\t\tchar deutf8[8192];\n\t\t\tchar demoji[8192];\n\t\t\tline = Key_Demoji(demoji, sizeof(demoji), line);\n\n\t\t\tif (com_parseutf8.ival <= 0)\n\t\t\t{\n\t\t\t\tunsigned int unicode;\n\t\t\t\tint err;\n\t\t\t\tint len = 0;\n\t\t\t\twhile(*line)\n\t\t\t\t{\n\t\t\t\t\tunicode = utf8_decode(&err, line, &line);\n\t\t\t\t\tlen += unicode_encode(deutf8+len, unicode, sizeof(deutf8)-1 - len, true);\n\t\t\t\t}\n\t\t\t\tdeutf8[len] = 0;\n\t\t\t\tline = deutf8;\n\t\t\t}\n\n\t\t\tCmd_TokenizeString(va(\"%s %s\", chat_team?\"say_team\":\"say\", line), true, false);\n\t\t\tCL_Say(chat_team, \"\");\n\t\t}\n\n\t\tKey_Dest_Remove(kdm_message);\n\t\tchat_bufferpos = 0;\n\t\tchat_buffer[0] = 0;\n\t\treturn;\n\t}\n\n\tif (key == K_ESCAPE || key == K_TOUCHLONG || key == K_GP_DIAMOND_CANCEL)\n\t{\n\t\tKey_Dest_Remove(kdm_message);\n\t\tchat_bufferpos = 0;\n\t\tchat_buffer[0] = 0;\n\t\treturn;\n\t}\n\n\tKey_EntryLine(NULL, &chat_buffer, 0, &chat_bufferpos, key, unicode);\n}\n\n//============================================================================\n\n//for qc\nconst char *Key_GetBinding(int keynum, int bindmap, int modifier)\n{\n\tchar *key = NULL;\n\tif (keynum < 0 || keynum >= K_MAX)\n\t\t;\n\telse if (bindmap < 0)\n\t{\n\t\tkey = NULL;\n\t\tif (!key)\n\t\t\tkey = keybindings[keynum][key_bindmaps[0]];\n\t\tif (!key)\n\t\t\tkey = keybindings[keynum][key_bindmaps[1]];\n\t}\n\telse\n\t{\n\t\tif (bindmap)\n\t\t\tmodifier = (bindmap-1) + KEY_MODIFIER_ALTBINDMAP;\n\t\tif (modifier >= 0 && modifier < KEY_MODIFIERSTATES)\n\t\t\tkey = keybindings[keynum][modifier];\n\t}\n\treturn key;\n}\n\n/*\n===================\nKey_StringToKeynum\n\nReturns a key number to be used to index keybindings[] by looking at\nthe given string.  Single ascii characters return themselves, while\nthe K_* names are matched up.\n===================\n*/\nint Key_StringToKeynum (const char *str, int *modifier)\n{\n\tint k;\n\tkeyname_t\t*kn;\n\n\tif (!strnicmp(str, \"std_\", 4) || !strnicmp(str, \"std+\", 4))\n\t\t*modifier = 0;\n\telse\n\t{\n\t\tstruct\n\t\t{\n\t\t\tchar *prefix;\n\t\t\tint len;\n\t\t\tint mod;\n\t\t} mods[] =\n\t\t{\n\t\t\t{\"shift\",\t5, KEY_MODIFIER_SHIFT},\n\t\t\t{\"ctrl\",\t4, KEY_MODIFIER_CTRL},\n\t\t\t{\"alt\",\t\t3, KEY_MODIFIER_ALT},\n\t\t};\n\t\tint i;\n\t\t*modifier = 0;\n\t\tfor (i = 0; i < countof(mods); )\n\t\t{\n\t\t\tif (!Q_strncasecmp(mods[i].prefix, str, mods[i].len))\n\t\t\t\tif (/*str[mods[i].len] == '_' ||*/ str[mods[i].len] == '+' || str[mods[i].len] == ' ')\n\t\t\t\tif (str[mods[i].len+1])\n\t\t\t\t{\n\t\t\t\t\t*modifier |= mods[i].mod;\n\t\t\t\t\tstr += mods[i].len+1;\n\t\t\t\t\ti = 0;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\ti++;\n\t\t}\n\t\tif (!*modifier)\n\t\t\t*modifier = ~0;\n\t}\n\t\n\tif (!str || !str[0])\n\t\treturn -1;\n\tif (!str[1])\t//single char.\n\t{\n#if 0//def _WIN32\n\t\treturn VkKeyScan(str[0]);\n#else\n\t\tk = str[0];\n//\t\treturn str[0];\n#endif\n\t}\n\telse\n\t{\n\t\tif (!strncmp(str, \"K_\", 2))\n\t\t\tstr+=2;\n\n\t\tfor (kn=keynames ; kn->name ; kn++)\n\t\t{\n\t\t\tif (!Q_strcasecmp(str,kn->name))\n\t\t\t\treturn kn->keynum;\n\t\t}\n\t\tk = atoi(str);\n\t}\n\tif (k)\t//assume ascii code. (prepend with a 0 if needed)\n\t{\n\t\tif (k >= 'A' && k <= 'Z')\n\t\t\tk += 'a'-'A';\n\t\treturn k;\n\t}\n\treturn -1;\n}\n\n/*\n===================\nKey_KeynumToString\n\nReturns a string (either a single ascii char, or a K_* name) for the\ngiven keynum.\nFIXME: handle quote special (general escape sequence?)\n===================\n*/\nstatic char *Key_KeynumToStringRaw (int keynum)\n{\n\tkeyname_t\t*kn;\t\n\tstatic\tchar\ttinystr[2];\n\t\n\tif (keynum < 0)\n\t\treturn \"<KEY NOT FOUND>\";\n\tif (keynum > 32 && keynum < 127 && keynum != '\\'' && keynum != '\\\"' && keynum != '\\\\')\n\t{\t// printable ascii\n\t\ttinystr[0] = keynum;\n\t\ttinystr[1] = 0;\n\t\treturn tinystr;\n\t}\n\t\n\tfor (kn=keynames ; kn->name ; kn++)\n\t\tif (keynum == kn->keynum)\n\t\t\treturn kn->name;\n\n\t{\n\t\tif (keynum < 10)\t//don't let it be a single character\n\t\t\treturn va(\"0%i\", keynum);\n\t\treturn va(\"%i\", keynum);\n\t}\n\n\treturn \"<UNKNOWN KEYNUM>\";\n}\n\nconst char *Key_KeynumToString (int keynum, int modifier)\n{\n\tconst char *r = Key_KeynumToStringRaw(keynum);\n\tif (r[0] == '<' && r[1])\n\t\tmodifier = 0;\t//would be too weird.\n\tswitch(modifier)\n\t{\n\tcase KEY_MODIFIER_CTRL|KEY_MODIFIER_ALT|KEY_MODIFIER_SHIFT:\n\t\treturn va(\"Ctrl+Alt+Shift+%s\", r);\n\tcase KEY_MODIFIER_ALT|KEY_MODIFIER_SHIFT:\n\t\treturn va(\"Alt+Shift+%s\", r);\n\tcase KEY_MODIFIER_CTRL|KEY_MODIFIER_SHIFT:\n\t\treturn va(\"Ctrl+Shift+%s\", r);\n\tcase KEY_MODIFIER_CTRL|KEY_MODIFIER_ALT:\n\t\treturn va(\"Ctrl+Alt+%s\", r);\n\tcase KEY_MODIFIER_CTRL:\n\t\treturn va(\"Ctrl+%s\", r);\n\tcase KEY_MODIFIER_ALT:\n\t\treturn va(\"Alt+%s\", r);\n\tcase KEY_MODIFIER_SHIFT:\n\t\treturn va(\"Shift+%s\", r);\n\tdefault:\n\t\treturn r;\t//no modifier or a bindmap\n\t}\n}\n\nconst char *Key_KeynumToLocalString (int keynum, int modifier)\n{\n\tconst char *r;\n#if defined(_WIN32)\t|| (defined(__linux__)&&!defined(NO_X11)) //not defined in all targets yet...\n\tstatic char tmp[64];\n\tif (INS_KeyToLocalName(keynum, tmp, sizeof(tmp)))\n\t\tr = tmp;\n\telse\n#endif\n\t\tr = Key_KeynumToStringRaw(keynum);\n\tif (r[0] == '<' && r[1])\n\t\tmodifier = 0;\t//would be too weird.\n\tswitch(modifier)\n\t{\n\tcase KEY_MODIFIER_CTRL|KEY_MODIFIER_ALT|KEY_MODIFIER_SHIFT:\n\t\treturn va(\"Ctrl+Alt+Shift+%s\", r);\n\tcase KEY_MODIFIER_ALT|KEY_MODIFIER_SHIFT:\n\t\treturn va(\"Alt+Shift+%s\", r);\n\tcase KEY_MODIFIER_CTRL|KEY_MODIFIER_SHIFT:\n\t\treturn va(\"Ctrl+Shift+%s\", r);\n\tcase KEY_MODIFIER_CTRL|KEY_MODIFIER_ALT:\n\t\treturn va(\"Ctrl+Alt+%s\", r);\n\tcase KEY_MODIFIER_CTRL:\n\t\treturn va(\"Ctrl+%s\", r);\n\tcase KEY_MODIFIER_ALT:\n\t\treturn va(\"Alt+%s\", r);\n\tcase KEY_MODIFIER_SHIFT:\n\t\treturn va(\"Shift+%s\", r);\n\tdefault:\n\t\treturn r;\t//no modifier or a bindmap\n\t}\n}\n\n/*\n===================\nKey_SetBinding\n===================\n*/\nvoid Key_SetBinding (int keynum, int modifier, const char *binding, int level)\n{\n\tchar\t*newc;\n\tint\t\tl;\n\n\tif (modifier == ~0)\t//all of the possibilities.\n\t{\n\t\tif (binding)\n\t\t{\t//bindmaps are meant to be independant of each other.\n\t\t\tfor (l = 0; l < KEY_MODIFIER_ALTBINDMAP; l++)\n\t\t\t\tKey_SetBinding(keynum, l, binding, level);\n\t\t}\n\t\telse\n\t\t{\t//when unbinding, unbind all bindmaps.\n\t\t\tfor (l = 0; l < KEY_MODIFIERSTATES; l++)\n\t\t\t\tKey_SetBinding(keynum, l, binding, level);\n\t\t}\n\t\treturn;\n\t}\n\n\tif (keynum < 0 || keynum >= K_MAX)\n\t\treturn;\n\n\t//just so the quit menu realises it needs to show something.\n\tCvar_ConfigChanged();\n\n// free old bindings\n\tif (keybindings[keynum][modifier])\n\t{\n\t\tZ_Free (keybindings[keynum][modifier]);\n\t\tkeybindings[keynum][modifier] = NULL;\n\t}\n\n\n\tif (!binding)\n\t{\n\t\tkeybindings[keynum][modifier] = NULL;\n\t\treturn;\n\t}\n// allocate memory for new binding\n\tl = Q_strlen (binding);\t\n\tnewc = Z_Malloc (l+1);\n\tQ_strcpy (newc, binding);\n\tnewc[l] = 0;\n\tkeybindings[keynum][modifier] = newc;\n\tbindcmdlevel[keynum][modifier] = level;\n}\n\n/*\n===================\nKey_Unbind_f\n===================\n*/\nvoid Key_Unbind_f (void)\n{\n\tint\t\tb, modifier;\n\n\tif (Cmd_Argc() != 2)\n\t{\n\t\tCon_Printf (\"unbind <key> : remove commands from a key\\n\");\n\t\treturn;\n\t}\n\t\n\tb = Key_StringToKeynum (Cmd_Argv(1), &modifier);\n\tif (b==-1)\n\t{\n\t\tif (cl_warncmd.ival)\n\t\t\tCon_Printf (\"\\\"%s\\\" isn't a valid key\\n\", Cmd_Argv(1));\n\t\treturn;\n\t}\n\n\tKey_SetBinding (b, modifier, NULL, Cmd_ExecLevel);\n}\n\nvoid Key_Unbindall_f (void)\n{\n\tint\t\ti;\n\t\n\tfor (i=0 ; i<K_MAX ; i++)\n\t\tKey_SetBinding (i, ~0, NULL, Cmd_ExecLevel);\n}\n\nvoid Key_Bind_c(int argn, const char *partial, struct xcommandargcompletioncb_s *ctx)\n{\n\tint key, mf;\n\tkeyname_t *kn;\n\tconst char *n, *m;\n\tsize_t len = strlen(partial), l;\n\tstruct\n\t{\n\t\tconst char *prefix;\n\t\tint mods;\n\t} mtab[] = \n\t{\n\t\t{\"\",0},\n\n\t\t{\"CTRL+ALT+SHIFT+\",KEY_MODIFIER_CTRL|KEY_MODIFIER_ALT|KEY_MODIFIER_SHIFT},\n\t\t{\"ALT+CTRL+SHIFT+\",KEY_MODIFIER_CTRL|KEY_MODIFIER_ALT|KEY_MODIFIER_SHIFT},\n\t\t{\"SHIFT+CTRL+ALT+\",KEY_MODIFIER_CTRL|KEY_MODIFIER_ALT|KEY_MODIFIER_SHIFT},\n\t\t{\"SHIFT+ALT+CTRL+\",KEY_MODIFIER_CTRL|KEY_MODIFIER_ALT|KEY_MODIFIER_SHIFT},\n\t\t{\"CTRL+SHIFT+ALT+\",KEY_MODIFIER_CTRL|KEY_MODIFIER_ALT|KEY_MODIFIER_SHIFT},\n\t\t{\"ALT+SHIFT+CTRL+\",KEY_MODIFIER_CTRL|KEY_MODIFIER_ALT|KEY_MODIFIER_SHIFT},\n\n\t\t{\"CTRL+ALT+\",KEY_MODIFIER_CTRL|KEY_MODIFIER_ALT|KEY_MODIFIER_SHIFT},\n\t\t{\"ALT+CTRL+\",KEY_MODIFIER_CTRL|KEY_MODIFIER_ALT|KEY_MODIFIER_SHIFT},\n\t\t{\"SHIFT+ALT+\",KEY_MODIFIER_CTRL|KEY_MODIFIER_ALT|KEY_MODIFIER_SHIFT},\n\t\t{\"ALT+SHIFT+\",KEY_MODIFIER_CTRL|KEY_MODIFIER_ALT|KEY_MODIFIER_SHIFT},\n\t\t{\"CTRL+SHIFT+\",KEY_MODIFIER_CTRL|KEY_MODIFIER_ALT|KEY_MODIFIER_SHIFT},\n\t\t{\"SHIFT+CTRL+\",KEY_MODIFIER_CTRL|KEY_MODIFIER_ALT|KEY_MODIFIER_SHIFT},\n\n\t\t{\"CTRL+\",KEY_MODIFIER_CTRL},\n\t\t{\"ALT+\",KEY_MODIFIER_ALT},\n\t\t{\"SHIFT+\",KEY_MODIFIER_SHIFT},\n\t};\n\tfor (l = 0; l < countof(mtab); l++)\n\t{\n\t\tm = mtab[l].prefix;\n\t\tmf = strlen(m);\n\t\tmf = min(mf, len);\n\t\tif (Q_strncasecmp(partial, m, mf))\n\t\t\tcontinue;\n\t\tmf = mtab[l].mods;\n\n\t\tfor (kn=keynames ; kn->name ; kn++)\n\t\t{\n\t\t\t//don't suggest shift+shift, because that would be weird.\n\t\t\tif ((mf & KEY_MODIFIER_CTRL) && (kn->keynum == K_LCTRL || kn->keynum == K_RCTRL))\n\t\t\t\tcontinue;\n\t\t\tif ((mf & KEY_MODIFIER_ALT) && (kn->keynum == K_LALT || kn->keynum == K_RALT))\n\t\t\t\tcontinue;\n\t\t\tif ((mf & KEY_MODIFIER_SHIFT) && (kn->keynum == K_LSHIFT || kn->keynum == K_RSHIFT))\n\t\t\t\tcontinue;\n\n\t\t\tn = va(\"%s%s\", m, kn->name);\n\t\t\tif (!Q_strncasecmp(partial,n, len))\n\t\t\t\tctx->cb(n, NULL, NULL, ctx);\n\t\t}\n\t\tfor (key = 32+1; key < 127; key++)\n\t\t{\n\t\t\tif (key >= 'a' && key <= 'z')\n\t\t\t\tcontinue;\n\t\t\tn = va(\"%s%c\", m, key);\n\t\t\tif (!Q_strncasecmp(partial,n, len))\n\t\t\t\tctx->cb(n, NULL, NULL, ctx);\n\t\t}\n\t}\n}\n/*\n===================\nKey_Bind_f\n===================\n*/\nvoid Key_Bind_f (void)\n{\n\tint\t\t\ti, c, b, modifier;\n\tchar\t\tcmd[1024];\n\tint bindmap = 0;\n\tint level = Cmd_ExecLevel;\n\tqboolean isbindlevel = !strcmp(\"bindlevel\", Cmd_Argv(0));\n\tif (!strcmp(\"in_bind\", Cmd_Argv(0)))\n\t{\n\t\tbindmap = atoi(Cmd_Argv(1));\n\t\tCmd_ShiftArgs(1, level==RESTRICT_LOCAL);\n\t}\n\t\n\tc = Cmd_Argc();\n\n\tif (c < 2+isbindlevel)\n\t{\n\t\tCon_Printf (\"%s <key> %s[command] : attach a command to a key\\n\", Cmd_Argv(0), isbindlevel?\"<level> \":\"\");\n\t\treturn;\n\t}\n\tb = Key_StringToKeynum (Cmd_Argv(1), &modifier);\n\tif (b==-1)\n\t{\n\t\tif (cl_warncmd.ival)\n\t\t\tCon_Printf (\"\\\"%s\\\" isn't a valid key\\n\", Cmd_Argv(1));\n\t\treturn;\n\t}\n\tif (isbindlevel)\n\t{\n\t\tlevel = atoi(Cmd_Argv(2));\n\t\tif (Cmd_IsInsecure())\n\t\t\tlevel = Cmd_ExecLevel;\n\t\telse\n\t\t{\n\t\t\tif (level > RESTRICT_MAX)\n\t\t\t\tlevel = RESTRICT_INSECURE;\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (level < RESTRICT_MIN)\n\t\t\t\t\tlevel = RESTRICT_MIN;\n\t\t\t\tif (level > Cmd_ExecLevel)\n\t\t\t\t\tlevel = Cmd_ExecLevel;\t//clamp exec levels, so we don't get more rights than we should.\n\t\t\t}\n\t\t}\n\t}\n\n\tif (bindmap)\n\t{\n\t\tif (bindmap <= 0 || bindmap > KEY_MODIFIER_ALTBINDMAP)\n\t\t{\n\t\t\tif (cl_warncmd.ival)\n\t\t\t\tCon_Printf (\"unsupported bindmap %i\\n\", bindmap);\n\t\t\treturn;\n\t\t}\n\t\tif (modifier != ~0)\n\t\t{\n\t\t\tif (cl_warncmd.ival)\n\t\t\t\tCon_Printf (\"modifiers cannot be combined with bindmaps\\n\");\n\t\t\treturn;\n\t\t}\n\t\tmodifier = (bindmap-1) | KEY_MODIFIER_ALTBINDMAP;\n\t}\n\n\tif (c == 2+isbindlevel)\n\t{\n\t\tconst char *keyname=Key_KeynumToString(b, modifier);\t//pretty it up.\n\t\tif (modifier == ~0)\t//modifier unspecified. default to no modifier\n\t\t\tmodifier = 0;\n\t\tif (keybindings[b][modifier])\n\t\t{\n\t\t\tchar *alias = Cmd_AliasExist(keybindings[b][modifier], RESTRICT_LOCAL);\n\t\t\tchar quotedbind[2048];\n\t\t\tchar quotedalias[2048];\n\t\t\tchar leveldesc[1024];\n\t\t\tif (bindcmdlevel[b][modifier] != level)\n\t\t\t{\n\t\t\t\tif (Cmd_ExecLevel > RESTRICT_SERVER)\n\t\t\t\t\tQ_snprintfz(leveldesc, sizeof(leveldesc), \", for seat %i\", Cmd_ExecLevel - RESTRICT_SERVER);\n\t\t\t\telse if (Cmd_ExecLevel == RESTRICT_SERVER)\n\t\t\t\t\tQ_snprintfz(leveldesc, sizeof(leveldesc), \", bound by server\");\n\t\t\t\telse if (bindcmdlevel[b][modifier]>=RESTRICT_INSECURE)\n\t\t\t\t\tQ_snprintfz(leveldesc, sizeof(leveldesc), \", bound by insecure source\");\n\t\t\t\telse\n\t\t\t\t\tQ_snprintfz(leveldesc, sizeof(leveldesc), \", at level %i\", bindcmdlevel[b][modifier]);\n\t\t\t}\n\t\t\telse\n\t\t\t\t*leveldesc = 0;\n\t\t\tCOM_QuotedString(keybindings[b][modifier], quotedbind, sizeof(quotedbind), false);\n\t\t\tif (alias)\n\t\t\t{\n\t\t\t\tCOM_QuotedString(alias, quotedalias, sizeof(quotedalias), false);\n\t\t\t\tCon_Printf (\"^[\\\"%s\\\"\\\\type\\\\bind %s %s^] = ^[\\\"%s\\\"\\\\type\\\\alias %s %s^]%s\\n\", keyname, Cmd_Argv(1), quotedbind, keybindings[b][modifier], keybindings[b][modifier], quotedalias, leveldesc);\n\t\t\t}\n\t\t\telse\n\t\t\t\tCon_Printf (\"^[\\\"%s\\\"\\\\type\\\\bind %s %s^] = \\\"%s\\\"%s\\n\", keyname, keybindings[b][modifier], Cmd_Argv(1), keybindings[b][modifier], leveldesc);\n\t\t}\n\t\telse\n\t\t\tCon_Printf (\"\\\"%s\\\" is not bound\\n\", keyname);\n\t\treturn;\n\t}\n\n\tif (c > 3+isbindlevel)\n\t{\n\t\tCmd_ShiftArgs(1+isbindlevel, level==RESTRICT_LOCAL);\n\t\tKey_SetBinding (b, modifier, Cmd_Args(), level);\n\t\treturn;\n\t}\n\t\n// copy the rest of the command line\n\tcmd[0] = 0;\t\t// start out with a null string\n\tfor (i=2+isbindlevel ; i< c ; i++)\n\t{\n\t\tQ_strncatz (cmd, Cmd_Argv(i), sizeof(cmd));\n\t\tif (i != (c-1))\n\t\t\tQ_strncatz (cmd, \" \", sizeof(cmd));\n\t}\n\n\tKey_SetBinding (b, modifier, cmd, level);\n}\n\n/*\n============\nKey_WriteBindings\n\nWrites lines containing \"bind key value\"\n============\n*/\nvoid Key_WriteBindings (vfsfile_t *f)\n{\n\tconst char *s;\n\tint\t\ti, m;\n\tchar *binding, *base;\n\n\tchar keybuf[256];\n\tchar commandbuf[2048];\n\n\tfor (i=0 ; i<K_MAX ; i++)\t//we rebind the key with all modifiers to get the standard bind, then change the specific ones.\n\t{\t\t\t\t\t\t//this does two things, it normally allows us to skip 7 of the 8 possibilities\n\t\tbase = keybindings[i][0];\t//plus we can use the config with other clients.\n\t\tif (!base)\n\t\t\tbase = \"\";\n\t\tfor (m = 0; m < KEY_MODIFIER_ALTBINDMAP; m++)\n\t\t{\n\t\t\tbinding = keybindings[i][m];\n\t\t\tif (!binding)\n\t\t\t\tbinding = \"\";\n\t\t\tif (strcmp(binding, base) || (m==0 && keybindings[i][0]) || bindcmdlevel[i][m] != bindcmdlevel[i][0])\n\t\t\t{\n\t\t\t\ts = Key_KeynumToString(i, m);\n\t\t\t\t//quote it as required\n\t\t\t\tif (i == ';' || i <= ' ' || strchr(s, ' ') || strchr(s, '+') || strchr(s, '\\\"'))\n\t\t\t\t\ts = COM_QuotedString(s, keybuf, sizeof(keybuf), false);\n\n\t\t\t\tif (bindcmdlevel[i][m] != RESTRICT_LOCAL)\n\t\t\t\t\ts = va(\"bindlevel %s %i %s\\n\", s, bindcmdlevel[i][m], COM_QuotedString(binding, commandbuf, sizeof(commandbuf), false));\n\t\t\t\telse\n\t\t\t\t\ts = va(\"bind %s %s\\n\", s, COM_QuotedString(binding, commandbuf, sizeof(commandbuf), false));\n\t\t\t\tVFS_WRITE(f, s, strlen(s));\n\t\t\t}\n\t\t}\n\t\t//now generate some special in_binds for bindmaps.\n\t\tfor (m = 0; m < KEY_MODIFIER_ALTBINDMAP; m++)\n\t\t{\n\t\t\tbinding = keybindings[i][m|KEY_MODIFIER_ALTBINDMAP];\n\t\t\tif (binding && *binding)\n\t\t\t{\n\t\t\t\ts = va(\"%s\", Key_KeynumToString(i, 0));\n\t\t\t\t//quote it as required\n\t\t\t\tif (i == ';' || i <= ' ' || i == '\\\"')\n\t\t\t\t\ts = COM_QuotedString(s, keybuf, sizeof(keybuf), false);\n\n\t\t\t\ts = va(\"in_bind %i %s %s\\n\", m+1, s, COM_QuotedString(binding, commandbuf, sizeof(commandbuf), false));\n\t\t\t\tVFS_WRITE(f, s, strlen(s));\n\t\t\t}\n\t\t}\n\t}\n}\n\n/*\n===================\nKey_Init\n===================\n*/\nvoid Key_Init (void)\n{\n\tint\t\ti;\n\n\tfor (i=0 ; i<=CON_EDIT_LINES_MASK ; i++)\n\t{\n\t\tkey_lines[i] = Z_Malloc(1);\n\t\tkey_lines[i][0] = '\\0';\n\t}\n\tkey_linepos = 0;\n\n\tKey_Dest_Add(kdm_game);\n\tkey_dest_absolutemouse = kdm_centerprint | kdm_console | kdm_cwindows | kdm_menu;\n\n//\n// init ascii characters in console mode\n//\n\tfor (i=32 ; i<128 ; i++)\n\t\tconsolekeys[i] = true;\n\tconsolekeys[K_ENTER] = true;\n\tconsolekeys[K_KP_ENTER] = true;\n\tconsolekeys[K_TAB] = true;\n\tconsolekeys[K_LEFTARROW] = true;\n\tconsolekeys[K_RIGHTARROW] = true;\n\tconsolekeys[K_UPARROW] = true;\n\tconsolekeys[K_DOWNARROW] = true;\n\tconsolekeys[K_KP_LEFTARROW] = true;\n\tconsolekeys[K_KP_RIGHTARROW] = true;\n\tconsolekeys[K_KP_UPARROW] = true;\n\tconsolekeys[K_KP_DOWNARROW] = true;\n\tconsolekeys[K_BACKSPACE] = true;\n\tconsolekeys[K_INS] = true;\n\tconsolekeys[K_DEL] = true;\n\tconsolekeys[K_KP_DEL] = true;\n\tconsolekeys[K_HOME] = true;\n\tconsolekeys[K_KP_HOME] = true;\n\tconsolekeys[K_END] = true;\n\tconsolekeys[K_KP_END] = true;\n\tconsolekeys[K_PGUP] = true;\n\tconsolekeys[K_KP_PGUP] = true;\n\tconsolekeys[K_PGDN] = true;\n\tconsolekeys[K_KP_PGDN] = true;\n\tconsolekeys[K_LSHIFT] = true;\n\tconsolekeys[K_RSHIFT] = true;\n\tconsolekeys[K_MWHEELUP] = true;\n\tconsolekeys[K_MWHEELDOWN] = true;\n\tconsolekeys[K_LCTRL] = true;\n\tconsolekeys[K_RCTRL] = true;\n\tconsolekeys[K_LALT] = true;\n\tconsolekeys[K_RALT] = true;\n\tconsolekeys['`'] = false;\n\tconsolekeys['~'] = false;\n\n\t//most gamepad keys are not console keys, just because.\n\tconsolekeys[K_GP_DPAD_UP] = true;\n\tconsolekeys[K_GP_DPAD_DOWN] = true;\n\tconsolekeys[K_GP_DPAD_LEFT] = true;\n\tconsolekeys[K_GP_DPAD_RIGHT] = true;\n\tconsolekeys[K_GP_START] = true;\n\tconsolekeys[K_GP_BACK] = true;\n\n\tfor (i=K_MOUSE1 ; i<K_MOUSE10 ; i++)\n\t{\n\t\tconsolekeys[i] = true;\n\t}\n\tconsolekeys[K_MWHEELUP] = true;\n\tconsolekeys[K_MWHEELDOWN] = true;\n\tconsolekeys[K_TOUCH] = true;\n\n//\n// register our functions\n//\n\tCmd_AddCommandAD (\"bind\",Key_Bind_f, Key_Bind_c, \"Changes the action associated with each keyboard button. Use eg \\\"bind ctrl+shift+alt+k kill\\\" for special modifiers (should be used only after more basic modifiers).\");\n\tCmd_AddCommand (\"in_bind\",Key_Bind_f);\n\tCmd_AddCommand (\"bindlevel\",Key_Bind_f);\n\tCmd_AddCommandAD (\"unbind\",Key_Unbind_f, Key_Bind_c, NULL);\n\tCmd_AddCommandD (\"unbindall\",Key_Unbindall_f, \"A dangerous command that forgets ALL your key settings. For use only in default.cfg.\");\n\n\tCvar_Register (&con_echochat, \"Console variables\");\n}\n\nqboolean Key_MouseShouldBeFree(void)\n{\n\t//returns if the mouse should be a cursor or if it should go to the menu\n\n\t//if true, the input code is expected to return mouse cursor positions rather than deltas\n\textern cvar_t cl_prydoncursor;\n\tif (key_dest_absolutemouse & key_dest_mask)\n\t\treturn true;\n\n\tif (cl_prydoncursor.ival)\n\t\treturn true;\n\n\treturn false;\n}\n\n/*\n===================\nKey_Event\n\nCalled by the system between frames for both key up and key down events\nShould NOT be called during an interrupt!\n\nOn some systems, keys and (uni)char codes will be entirely separate events.\n===================\n*/\nvoid Key_Event (unsigned int devid, int key, unsigned int unicode, qboolean down)\n{\n\tunsigned int devbit = 1u<<devid;\n\tint bl, bkey;\n\tconst char\t*dc;\n\tchar\t*uc;\n\tchar\tp[16];\n\tint modifierstate;\n\tint conkey = consolekeys[key] || ((unicode || key == '`') && (key != '`' || key_linepos>0));\t//if the input line is empty, allow ` to toggle the console, otherwise enter it as actual text.\n\tqboolean wasdown = !!(keydown[key]&devbit);\n\n//\tCon_Printf (\"%i : %i : %i\\n\", key, unicode, down); //@@@\n\n\t//bug: two of my keyboard doesn't fire release events if the other shift is already pressed (so I assume this is a common thing).\n\t//hack around that by just force-releasing eg left if right is pressed, but only on inital press to avoid potential infinite loops if the state got bad.\n\t//ctrl+alt don't seem to have the problem.\n\tif (key == K_LSHIFT && !keydown[K_LSHIFT] && keydown[K_RSHIFT])\n\t\tKey_Event(devid, K_RSHIFT, 0, false);\n\tif (key == K_RSHIFT && !keydown[K_RSHIFT] && keydown[K_LSHIFT])\n\t\tKey_Event(devid, K_LSHIFT, 0, false);\n\n\tmodifierstate = KeyModifier(keydown[K_LSHIFT]|keydown[K_RSHIFT], keydown[K_LALT]|keydown[K_RALT], keydown[K_LCTRL]|keydown[K_RCTRL], devbit);\n\n\tif (down)\n\t\tkeydown[key] |= devbit;\n\telse\n\t\tkeydown[key] &= ~devbit;\n\n\tif (down)\n\t{\n//\t\tif (key >= 200 && !keybindings[key])\t//is this too annoying?\n//\t\t\tCon_Printf (\"%s is unbound, hit F4 to set.\\n\", Key_KeynumToString (key) );\n\t}\n\n\tif (key == K_LSHIFT || key == K_RSHIFT)\n\t{\n\t\tshift_down = keydown[K_LSHIFT]|keydown[K_RSHIFT];\n\t}\n\n\tif ((key == K_ESCAPE  && (shift_down        &devbit)) ||\n\t\t(key == K_GP_MENU && (keydown[K_GP_VIEW]&devbit)))\n\t{\n\t\textern cvar_t con_stayhidden;\n\t\tif (down && con_stayhidden.ival < 2)\n\t\t{\n\t\t\tif (!Key_Dest_Has(kdm_console))\t//don't toggle it when the console is already down. this allows typing blind to not care if its already active.\n\t\t\t\tCon_ToggleConsole_Force();\n\t\t\treturn;\n\t\t}\n\t}\n\n\t//yes, csqc is allowed to steal the escape key (unless shift).\n\t//it is blocked from blocking backtick (unless shift).\n\tif ((key != '`'||shift_down) && (!down || key != K_ESCAPE || (!Key_Dest_Has(~kdm_game) && !shift_down)) &&\n\t\t!Key_Dest_Has(~kdm_game))\n\t{\n#ifdef CSQC_DAT\n\t\tif (CSQC_KeyPress(key, unicode, down, devid))\t//give csqc a chance to handle it.\n\t\t\treturn;\n#endif\n#ifdef VM_CG\n\t\tif (q3 && q3->cg.KeyPressed(key, unicode, down))\n\t\t\treturn;\n#endif\n\t}\n\telse if (!down && key)\n\t{\n#ifdef CSQC_DAT\n\t\t//csqc should still be told of up events. note that there's some filering to prevent notifying about events that it shouldn't receive (like all the up events when typing at the console).\n\t\tCSQC_KeyPress(key, unicode, down, devid);\n#endif\n\t}\n\n//\n// handle escape specialy, so the user can never unbind it\n//\n\tif (key == K_ESCAPE)\n\t{\n\t\tif (!Key_Dest_Has(~kdm_game))\n\t\t{\n#ifdef VM_UI\n//\t\t\tif (UI_KeyPress(key, unicode, down))\t//Allow the UI to see the escape key. It is possible that a developer may get stuck at a menu.\n//\t\t\t\treturn;\n#endif\n\t\t}\n\n\t\tif (!down)\n\t\t{\n\t\t\tif (Key_Dest_Has(kdm_prompt) || (Key_Dest_Has(kdm_menu) && !Key_Dest_Has(kdm_console|kdm_cwindows)))\n\t\t\t\tMenu_KeyEvent (false, devid, key, unicode);\n\t\t\treturn;\n\t\t}\n\n\t\tif (Key_Dest_Has(kdm_prompt))\n\t\t\tMenu_KeyEvent (true, devid, key, unicode);\n\t\telse if (Key_Dest_Has(kdm_console))\n\t\t{\n\t\t\tKey_Dest_Remove(kdm_console);\n\t\t\tKey_Dest_Remove(kdm_cwindows);\n\t\t\tif (!cls.state && !Key_Dest_Has(~kdm_game))\n\t\t\t\tM_ToggleMenu_f ();\n\t\t}\n\t\telse if (Key_Dest_Has(kdm_cwindows))\n\t\t{\n\t\t\tKey_Dest_Remove(kdm_cwindows);\n\t\t\tif (!cls.state && !Key_Dest_Has(~kdm_game))\n\t\t\t\tM_ToggleMenu_f ();\n\t\t}\n\t\telse if (Key_Dest_Has(kdm_menu))\n\t\t\tMenu_KeyEvent (true, devid, key, unicode);\n\t\telse if (Key_Dest_Has(kdm_message))\n\t\t{\n\t\t\tKey_Dest_Remove(kdm_message);\n\t\t\tif (chat_buffer)\n\t\t\t\tchat_buffer[0] = 0;\n\t\t\tchat_bufferpos = 0;\n\t\t}\n\t\telse\n\t\t\tM_ToggleMenu_f ();\n\t\treturn;\n\t}\n\n//\n// key up events only generate commands if the game key binding is\n// a button command (leading + sign).  These will occur even in console mode,\n// to keep the character from continuing an action started before a console\n// switch.  Button commands include the keynum as a parameter, so multiple\n// downs can be matched with ups\n//\n\tif (!down)\n\t{\n\t\tif (Key_Dest_Has(kdm_console|kdm_cwindows))\n\t\t{\n\t\t\tconsole_t *con;\n\t\t\tif (Key_Dest_Has(kdm_console))\n\t\t\t\tcon = con_current;\n\t\t\telse if (Key_Dest_Has(kdm_cwindows))\n\t\t\t\tcon = con_curwindow;\n\t\t\telse\n\t\t\t\tcon = NULL;\n\t\t\tif (con_mouseover && ((key >= K_MOUSE1 && key <= K_MWHEELDOWN) || key == K_TOUCH))\n\t\t\t\tcon = con_mouseover;\n\t\t\tif (con_curwindow && con_curwindow != con)\n\t\t\t\tcon_curwindow->buttonsdown = CB_NONE;\n\t\t\tif (con)\n\t\t\t{\n\t\t\t\tcon->mousecursor[0] = mousecursor_x - ((con->flags & CONF_ISWINDOW)?con->wnd_x+8:0);\n\t\t\t\tcon->mousecursor[1] = mousecursor_y - ((con->flags & CONF_ISWINDOW)?con->wnd_y:0);\n\t\t\t\tKey_ConsoleRelease(con, key, unicode);\n\t\t\t}\n\t\t}\n\t\tif (Key_Dest_Has(kdm_menu|kdm_prompt))\n\t\t\tMenu_KeyEvent (false, devid, key, unicode);\n\n\t\tuc = releasecommand[key][devid%MAX_INDEVS];\n\t\tif (uc)\t//this wasn't down, so don't crash on bad commands.\n\t\t{\n\t\t\treleasecommand[key][devid%MAX_INDEVS] = NULL;\n\t\t\tCbuf_AddText (uc, releasecommandlevel[key][devid%MAX_INDEVS]);\n\t\t\tZ_Free(uc);\n\t\t}\n\t\treturn;\n\t}\n\n//\n// during demo playback, most keys bring up the main menu\n//\n\tif (cls.demoplayback && cls.demoplayback != DPB_MVD && conkey && !Key_Dest_Has(~kdm_game))\n\t{\n\t\tdc = keybindings[key][modifierstate];\n\t\tif (!dc || (strcmp(dc, \"toggleconsole\") && strncmp(dc, \"+show\", 5) && strncmp(dc, \"demo_\", 5)))\n\t\t{\n\t\t\textern cvar_t cl_demospeed;\n\t\t\tswitch (key)\n\t\t\t{\t//these keys don't force the menu to appear while playing the demo reel\n\t\t\tcase K_LSHIFT:\n\t\t\tcase K_RSHIFT:\n\t\t\tcase K_LALT:\n\t\t\tcase K_RALT:\n\t\t\tcase K_LCTRL:\n\t//\t\tcase K_RCTRL:\n\t\t\t\tbreak;\n\t\t\t//demo modifiers...\n\t\t\tcase K_DOWNARROW:\n\t\t\tcase K_GP_DPAD_DOWN:\n\t\t\t\tCvar_SetValue(&cl_demospeed, max(cl_demospeed.value - 0.1, 0));\n\t\t\t\tCon_Printf(\"playback speed: %g%%\\n\", cl_demospeed.value*100);\n\t\t\t\treturn;\n\t\t\tcase K_UPARROW:\n\t\t\tcase K_GP_DPAD_UP:\n\t\t\t\tCvar_SetValue(&cl_demospeed, min(cl_demospeed.value + 0.1, 10));\n\t\t\t\tCon_Printf(\"playback speed: %g%%\\n\", cl_demospeed.value*100);\n\t\t\t\treturn;\n\t\t\tcase K_LEFTARROW:\n\t\t\tcase K_GP_DPAD_LEFT:\n\t\t\t\tCbuf_AddText(\"demo_jump -10\", RESTRICT_LOCAL);\t//expensive.\n\t\t\t\treturn;\n\t\t\tcase K_RIGHTARROW:\n\t\t\tcase K_GP_DPAD_RIGHT:\n\t\t\t\tCbuf_AddText(\"demo_jump +10\", RESTRICT_LOCAL);\n\t\t\t\treturn;\n\t\t\t//any other key\n\t\t\tdefault:\n\t\t\t\tM_ToggleMenu_f ();\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n\n\t//prompts get the first chance\n\tif (Key_Dest_Has(kdm_prompt))\n\t{\n\t\tif (key < K_F1 || key > K_F15)\n\t\t{\t//function keys don't get intercepted by the menu...\n\t\t\tMenu_KeyEvent (true, devid, key, unicode);\n\t\t\treturn;\n\t\t}\n\t}\n\n//\n// if not a consolekey, send to the interpreter no matter what mode is\n//\n\tif (/*conkey &&*/Key_Dest_Has(kdm_console|kdm_cwindows))\n\t{\n\t\tconsole_t *con = Key_Dest_Has(kdm_console)?con_current:con_curwindow;\n\t\tif ((con_mouseover||!Key_Dest_Has(kdm_console)) && ((key >= K_MOUSE1 && key <= K_MWHEELDOWN)||key==K_TOUCH))\n\t\t\tcon = con_mouseover;\n\t\tif (con)\n\t\t{\n\t\t\tcon->mousecursor[0] = mousecursor_x - ((con->flags & CONF_ISWINDOW)?con->wnd_x+8:0);\n\t\t\tcon->mousecursor[1] = mousecursor_y - ((con->flags & CONF_ISWINDOW)?con->wnd_y:0);\n\t\t\tif (Key_Console (con, key, unicode))\n\t\t\t\treturn;\n\t\t}\n\t\telse\n\t\t\tKey_Dest_Remove(kdm_cwindows);\n\n\t}\n\n\t//menus after console stuff\n\tif (Key_Dest_Has(kdm_menu))\n\t{\n\t\tif (key < K_F1 || key > K_F15)\n\t\t{\t//function keys don't get intercepted by the menu...\n\t\t\tMenu_KeyEvent (true, devid, key, unicode);\n\t\t\treturn;\n\t\t}\n\t}\n\tif (Key_Dest_Has(kdm_message))\n\t{\n\t\tKey_Message (key, unicode);\n\t\treturn;\n\t}\n\n\tif (Key_Dest_Has(kdm_centerprint))\n\t\tif (Key_Centerprint(key, unicode, devid))\n\t\t\treturn;\n\n\t//anything else is a key binding.\n\n\t/*don't auto-repeat binds as it breaks too many scripts*/\n\tif (down && wasdown)\n\t\treturn;\n\n\t//first player is normally assumed anyway.\n\tif (cl_forceseat.ival>0)\n\t\tQ_snprintfz (p, sizeof(p), \"p %i \", cl_forceseat.ival);\n\telse if (devid)\n\t\tQ_snprintfz (p, sizeof(p), \"p %i \", devid+1);\n\telse\n\t\t*p = 0;\n\n\t//assume the worst\n\tdc = NULL;\n\tbl = 0;\n\t//try bindmaps if they're set\n\tif (key_bindmaps[0] && (!dc || !*dc))\n\t{\n\t\tdc = keybindings[key][key_bindmaps[0]];\n\t\tbl = bindcmdlevel[key][key_bindmaps[0]];\n\t}\n\tif (key_bindmaps[1] && (!dc || !*dc))\n\t{\n\t\tdc = keybindings[key][key_bindmaps[1]];\n\t\tbl = bindcmdlevel[key][key_bindmaps[1]];\n\t}\n\n\t//regular ctrl_alt_shift_foo binds\n\tif (!dc || !*dc)\n\t{\n\t\tdc = keybindings[key][modifierstate];\n\t\tbl = bindcmdlevel[key][modifierstate];\n\t}\n\n\tbkey = key;\n\tif (!dc || !*dc)\n\t{\n\t\tbl = RESTRICT_LOCAL;\n\t\t//I've split some keys in the past. Some keys won't be known to csqc/menuqc/configs or whatever so we instead remap them to something else to prevent them from being dead-by-default.\n\t\t//csqc key codes may even have no standard keycode so cannot easily be bound from qc.\n\t\tswitch(key)\n\t\t{\n\t\t//left+right alts/etc got split\n\t\t//the generic name maps to the left key, so the right key needs to try the left\n\t\tcase K_RALT:\n\t\t\tbkey = K_LALT;\n\t\t\tbreak;\n\t\tcase K_RCTRL:\n\t\t\tbkey = K_LCTRL;\n\t\t\tbreak;\n\t\tcase K_RSHIFT:\n\t\t\tbkey = K_LSHIFT;\n\t\t\tbreak;\n\t\tcase K_RWIN:\n\t\t\tbkey = K_LWIN;\n\t\t\tbreak;\n\n\t\t//gamepad buttons should get fallbacks out of the box, even if they're not initially listed on the binds menu.\n\t\t//these may be redefined later...\n\t\tcase K_GP_LEFT_SHOULDER:\tdc = \"+jump\";\t\t\tgoto defaultedbind;\t//qs: impulse 12 (should be 11 for qw...)\n\t\tcase K_GP_RIGHT_SHOULDER:\tdc = \"+weaponwheel\";\tgoto defaultedbind;\t//qs: impulse 10\n\t\tcase K_GP_LEFT_TRIGGER:\t\tdc = \"+button3\";\t\tgoto defaultedbind;\t//qs: jump\n\t\tcase K_GP_RIGHT_TRIGGER:\tdc = \"+attack\";\t\t\tgoto defaultedbind;\t//qs: +attack\n\t\tcase K_GP_START:\t\t\tdc = \"togglemenu\";\t\tgoto defaultedbind;\n\t\tcase K_GP_A:\t\t\t\tdc = \"impulse 10\";\t\tgoto defaultedbind;\n\t\tcase K_GP_B:\t\t\t\tdc = \"+button4\";\t\tgoto defaultedbind;\n\t\tcase K_GP_X:\t\t\t\tdc = \"+button5\";\t\tgoto defaultedbind;\n\t\tcase K_GP_Y:\t\t\t\tdc = \"+button6\";\t\tgoto defaultedbind;\n\t\tcase K_GP_BACK:\t\t\t\tdc = \"+showscores\";\t\tgoto defaultedbind;\n\t\tcase K_GP_UNKNOWN:\t\t\tdc = \"+button8\";\t\tgoto defaultedbind;\n\t\tcase K_GP_DPAD_UP:\t\t\tdc = \"+forward\";\t\tgoto defaultedbind;\n\t\tcase K_GP_DPAD_DOWN:\t\tdc = \"+back\";\t\t\tgoto defaultedbind;\n\t\tcase K_GP_DPAD_LEFT:\t\tdc = \"+moveleft\";\t\tgoto defaultedbind;\n\t\tcase K_GP_DPAD_RIGHT:\t\tdc = \"+moveright\";\t\tgoto defaultedbind;\n\t\tcase K_GP_GUIDE:\t\t\tdc = \"togglemenu\";\t\tgoto defaultedbind;\n\n\t\tcase K_GP_LEFT_STICK:\t\tdc = \"+movedown\";\t\tgoto defaultedbind;\n\t\tcase K_GP_RIGHT_STICK:\n\t\tdefault:\n\t\t\tbreak;\n\t\t}\n\n\t\tdc = keybindings[bkey][modifierstate];\n\t\tbl = bindcmdlevel[bkey][modifierstate];\n\t}\n\ndefaultedbind:\n\tif (key == K_TOUCH || (key == K_MOUSE1 && IN_Touch_MouseIsAbs(devid)))\n\t{\n\t\tconst char *button = SCR_ShowPics_ClickCommand(mousecursor_x, mousecursor_y, key == K_TOUCH);\n\t\tif (!button && cl.mouseplayerview && cl.mousenewtrackplayer>=0)\n\t\t\tCam_Lock(cl.mouseplayerview, cl.mousenewtrackplayer);\n\t\tif (button)\n\t\t{\n\t\t\tdc = button;\n\t\t\tbl = RESTRICT_INSECURE;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tint bkey = Sbar_TranslateHudClick();\n\t\t\tif (bkey)\n\t\t\t{\n\t\t\t\tdc = keybindings[bkey][modifierstate];\n\t\t\t\tbl = bindcmdlevel[bkey][modifierstate];\n\t\t\t}\n\t\t\telse if (!Key_MouseShouldBeFree())\n\t\t\t\treturn;\n\t\t}\n\t\tIN_Touch_BlockGestures(devid);\n\t}\n\tif (key == K_TOUCHTAP && !dc)\n\t{\t//convert to mouse1 or mouse2 when touchstrafe is on and touchtap is unbound\n\t\tbkey = IN_Touch_Fallback(devid);\n\t\tif (bkey)\n\t\t{\n\t\t\tdc = keybindings[bkey][modifierstate];\n\t\t\tbl = bindcmdlevel[bkey][modifierstate];\n\t\t}\n\t}\n\n\tif (dc)\n\t{\n\t\tif (dc[0] == '+')\n\t\t{\n\t\t\tuc = va(\"-%s%s %i\\n\", p, dc+1, bkey);\n\t\t\tdc = va(\"+%s%s %i\\n\", p, dc+1, bkey);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tuc = NULL;\n\t\t\tdc = va(\"%s%s\\n\", p, dc);\n\t\t}\n\t}\n\telse\n\t\tuc = NULL;\n\n\t//don't mess up if we ran out of devices, just silently release the one that it conflicted with (and only if its in conflict).\n\tif (releasecommand[key][devid%MAX_INDEVS] && (!uc || strcmp(uc, releasecommand[key][devid%MAX_INDEVS])))\n\t{\n\t\tCbuf_AddText (releasecommand[key][devid%MAX_INDEVS], releasecommandlevel[key][devid%MAX_INDEVS]);\n\t\tZ_Free(releasecommand[key][devid%MAX_INDEVS]);\n\t\treleasecommand[key][devid%MAX_INDEVS] = NULL;\n\t}\n\tif (dc)\n\t\tCbuf_AddText (dc, bl);\n\tif (uc)\n\t\treleasecommand[key][devid%MAX_INDEVS] = Z_StrDup(uc);\n\treleasecommandlevel[key][devid%MAX_INDEVS] = bl;\n}\n\n/*\n===================\nKey_ClearStates\n===================\n*/\nvoid Key_ClearStates (void)\n{\n\tint\t\ti;\n\n\tfor (i=0 ; i<K_MAX ; i++)\n\t\tkeydown[i] = 0;\n}\n\n"
  },
  {
    "path": "engine/client/keys.h",
    "content": "/*\nCopyright (C) 1996-1997 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  \n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n*/\n\n#ifndef __CLIENT_KEYS_H__\n#define __CLIENT_KEYS_H__\n\nenum\n{\t//fte's assumed gamepad axis\n\tGPAXIS_LT_RIGHT\t= 0,\n\tGPAXIS_LT_DOWN\t= 1,\n\tGPAXIS_LT_AUX\t= 2,\n\n\tGPAXIS_RT_RIGHT\t= 3,\n\tGPAXIS_RT_DOWN\t= 4,\n\tGPAXIS_RT_AUX\t= 5,\n\n//gah\n#define GPAXIS_LT_TRIGGER GPAXIS_LT_AUX\n#define GPAXIS_RT_TRIGGER GPAXIS_RT_AUX\n};\n\n//\n// these are the key numbers that should be passed to Key_Event\n//\ntypedef enum {\n\tK_TAB\t\t= 9,\n\tK_ENTER\t\t= 13,\n\tK_ESCAPE\t= 27,\n\tK_SPACE\t\t= 32,\n\n\t// normal keys should be passed as lowercased ascii\n\tK_BACKSPACE\t= 127,\n\n\n\tK_SCRLCK,\n\tK_CAPSLOCK,\n\tK_POWER,\n\tK_PAUSE,\n\n\tK_UPARROW,\n\tK_DOWNARROW,\n\tK_LEFTARROW,\n\tK_RIGHTARROW,\n\n\tK_LALT,\n\tK_LCTRL,\n\tK_LSHIFT,\n\tK_INS,\n\tK_DEL,\n\tK_PGDN,\n\tK_PGUP,\n\tK_HOME,\n\tK_END,\n\n\tK_F1,\n\tK_F2,\n\tK_F3,\n\tK_F4,\n\tK_F5,\n\tK_F6,\n\tK_F7,\n\tK_F8,\n\tK_F9,\n\tK_F10,\n\tK_F11,\n\tK_F12,\n\tK_F13,\n\tK_F14,\n\tK_F15,\n\n\tK_KP_HOME,\n\tK_KP_UPARROW,\n\tK_KP_PGUP,\n\tK_KP_LEFTARROW,\n\tK_KP_5,\n\tK_KP_RIGHTARROW,\n\tK_KP_END,\n\tK_KP_DOWNARROW,\n\tK_KP_PGDN,\n\tK_KP_ENTER,\n\tK_KP_INS,\n\tK_KP_DEL,\n\tK_KP_SLASH,\n\tK_KP_MINUS,\n\tK_KP_PLUS,\n\tK_KP_NUMLOCK,\n\tK_KP_STAR,\n\tK_KP_EQUALS,\n\n\tK_MOUSE1,\t//aka left\n\tK_MOUSE2,\t//aka right\n\tK_MOUSE3,\t//aka middle\n\tK_MOUSE4,\t//aka back\n\tK_MOUSE5,\t//aka forward\n\n\tK_MWHEELDOWN,\n\tK_MWHEELUP,\n\n\tK_JOY1,\n\tK_JOY2,\n\tK_JOY3,\n\tK_JOY4,\n\tK_JOY5,\n\tK_JOY6,\n\tK_JOY7,\n\tK_JOY8,\n\tK_JOY9,\n\tK_JOY10,\n\tK_JOY11,\n\tK_JOY12,\n\tK_JOY13,\n\tK_JOY14,\n\tK_JOY15,\n\tK_JOY16,\n\tK_JOY17,\n\tK_JOY18,\n\tK_JOY19,\n\tK_JOY20,\n\tK_JOY21,\n\tK_JOY22,\n\tK_JOY23,\n\tK_JOY24,\n\tK_JOY25,\n\tK_JOY26,\n\tK_JOY27,\n\tK_JOY28,\n\tK_JOY29,\n\tK_JOY30,\n\tK_JOY31,\n\tK_JOY32,\n\n\tK_AUX1,\n\tK_AUX2,\n\tK_AUX3,\n\tK_AUX4,\n\tK_AUX5,\n\tK_AUX6,\n\tK_AUX7,\n\tK_AUX8,\n\tK_AUX9,\n\tK_AUX10,\n\tK_AUX11,\n\tK_AUX12,\n\tK_AUX13,\n\tK_AUX14,\n\tK_AUX15,\n\tK_AUX16,\n\n\t/* if you change the above order, you _will_ break Trinity!\n\t * only make modifcations below, unless you want to start \n\t * remapping keys for that too */\n\n\t/* Section dedicated to SDL controller definitions */\n\tK_GP_DIAMOND_DOWN,\n\tK_GP_DIAMOND_RIGHT,\n\tK_GP_DIAMOND_LEFT,\n\tK_GP_DIAMOND_UP,\n//for their behaviours in the menus... we may want to put a conditional in here for japanese-style right-for-confirm, but for now I'm lazy and am sticking with western/xbox/steam mappings.\n#define K_GP_DIAMOND_CONFIRM\tK_GP_DIAMOND_DOWN\t//roughly equivelent to k_return for menu behaviours\n#define K_GP_DIAMOND_CANCEL\t\tK_GP_DIAMOND_RIGHT\t//roughly like escape, at least in menus\n#define K_GP_DIAMOND_ALTCONFIRM\tK_GP_DIAMOND_UP\t\t//for more negative confirmations.\n\tK_GP_VIEW, //aka back (near left stick)\n\tK_GP_GUIDE,\n\tK_GP_MENU,\t//aka options/start (near right stick)\n\tK_GP_LEFT_STICK,\t\t//l3\n\tK_GP_RIGHT_STICK,\t\t//r3\n\tK_GP_LEFT_SHOULDER,\t\t//l1\n\tK_GP_RIGHT_SHOULDER,\t//r1\n\tK_GP_DPAD_UP,\n\tK_GP_DPAD_DOWN,\n\tK_GP_DPAD_LEFT,\n\tK_GP_DPAD_RIGHT,\n\tK_GP_MISC1,    /* share/mic-mute button */\n\tK_GP_PADDLE1,\t//r4\n\tK_GP_PADDLE2,\t//l4\n\tK_GP_PADDLE3,\t//r5\n\tK_GP_PADDLE4,\t//l5\n\tK_GP_TOUCHPAD, /* when pressed */\n\n\t/* emulated, we'll trigger these 'buttons' when we reach 50% pressed */\n\tK_GP_LEFT_TRIGGER,\t//l2\n\tK_GP_RIGHT_TRIGGER,\t//r2\n\tK_GP_LEFT_THUMB_UP,\n\tK_GP_LEFT_THUMB_DOWN,\n\tK_GP_LEFT_THUMB_LEFT,\n\tK_GP_LEFT_THUMB_RIGHT,\n\tK_GP_RIGHT_THUMB_UP,\n\tK_GP_RIGHT_THUMB_DOWN,\n\tK_GP_RIGHT_THUMB_LEFT,\n\tK_GP_RIGHT_THUMB_RIGHT,\n\tK_GP_UNKNOWN,\n\n\t/* extra dinput mouse buttons */\n\tK_MOUSE6,\n\tK_MOUSE7,\n\tK_MOUSE8,\n\tK_MOUSE9,\n\tK_MOUSE10,\n\n\t/*FIXME*/\n#define K_MWHEELLEFT\tK_MOUSE9\n#define K_MWHEELRIGHT\tK_MOUSE10\n\n\t/* spare joystick button presses */\n\tK_JOY_UP,\n\tK_JOY_DOWN,\n\tK_JOY_LEFT,\n\tK_JOY_RIGHT,\n\n\t/* extra keys */\n\tK_LWIN,\n\tK_RWIN,\n\tK_APP,\n\tK_SEARCH,\n\tK_VOLUP,\n\tK_VOLDOWN,\n\tK_RALT,\n\tK_RCTRL,\n\tK_RSHIFT,\n\tK_PRINTSCREEN,\n\n\t/* multimedia keyboard */\n\tK_MM_BROWSER_BACK,\n\tK_MM_BROWSER_FAVORITES,\n\tK_MM_BROWSER_FORWARD,\n\tK_MM_BROWSER_HOME,\n\tK_MM_BROWSER_REFRESH,\n\tK_MM_BROWSER_STOP,\n\tK_MM_VOLUME_MUTE,\n\tK_MM_TRACK_NEXT,\n\tK_MM_TRACK_PREV,\n\tK_MM_TRACK_STOP,\n\tK_MM_TRACK_PLAYPAUSE,\n\n\t//touchscreen stuff.\n\tK_TOUCH,\t\t//initial touch\n\t//will be paired with one of...\n\tK_TOUCHSLIDE,\t//existing touch became a slide\n\tK_TOUCHTAP,\t\t//touched briefly without sliding (treat like a left-click, though fired on final release)\n\tK_TOUCHLONG,\t//touch lasted a while and without moving (treat like a right-click)\n\n\tK_MAX,\n\n\t//360 buttons\n\tK_GP_A = K_GP_DIAMOND_DOWN,\n\tK_GP_B = K_GP_DIAMOND_RIGHT,\n\tK_GP_X = K_GP_DIAMOND_LEFT,\n\tK_GP_Y = K_GP_DIAMOND_UP,\n\tK_GP_BACK = K_GP_VIEW,\n\tK_GP_START = K_GP_MENU,\n\n\t//ps buttons\n\tK_GP_PS_CROSS\t\t= K_GP_DIAMOND_DOWN,\n\tK_GP_PS_CIRCLE\t\t= K_GP_DIAMOND_RIGHT,\n\tK_GP_PS_SQUARE\t\t= K_GP_DIAMOND_LEFT,\n\tK_GP_PS_TRIANGLE\t= K_GP_DIAMOND_UP,\n} keynum_t;\n\n#define KEY_MODIFIER_SHIFT\t\t(1<<0)\n#define KEY_MODIFIER_ALT\t\t(1<<1)\n#define KEY_MODIFIER_CTRL\t\t(1<<2)\n//#define KEY_MODIFIER_META\t\t(1<<?) do we want?\n#define KEY_MODIFIER_ALTBINDMAP\t(1<<3)\n#define\tKEY_MODIFIERSTATES\t\t(1<<4)\n\n//legacy aliases, lest we ever forget!\n#define K_SHIFT K_LSHIFT\n#define K_CTRL K_LCTRL\n#define K_ALT K_LALT\n#define K_WIN K_LWIN\n\n#ifdef __QUAKEDEF_H__\ntypedef enum\t//highest has priority\n{\n\tkdm_game\t\t= 1u<<0,\t//should always be set\n\tkdm_centerprint\t= 1u<<1,\t//enabled when there's a centerprint menu with clickable things.\n\tkdm_message\t\t= 1u<<2,\n\tkdm_menu\t\t= 1u<<3,\t//layered menus (engine menus, qc menus, or plugins/etc)\n\tkdm_console\t\t= 1u<<4,\n\tkdm_cwindows\t= 1u<<5,\n\tkdm_prompt\t\t= 1u<<6,\t//highest priority - popups that require user interaction (eg: confirmation from untrusted console commands)\n} keydestmask_t;\n\n//unsigned int Key_Dest_Get(void);\t//returns highest priority destination\n#define Key_Dest_Add(kdm) (key_dest_mask |= (kdm))\n#define Key_Dest_Remove(kdm) (key_dest_mask &= ~(kdm))\n#define Key_Dest_Has(kdm) (key_dest_mask & (kdm))\n#define Key_Dest_Has_Higher(kdm) (key_dest_mask & (~0&~((kdm)|((kdm)-1))))\t//must be a single bit\n#define Key_Dest_Toggle(kdm) do {if (key_dest_mask & kdm) Key_Dest_Remove(kdm); else Key_Dest_Add(kdm);}while(0)\n\nextern unsigned int key_dest_absolutemouse;\t//if the active key dest bit is set, the mouse is absolute.\nextern unsigned int key_dest_mask;\nextern char *keybindings[K_MAX][KEY_MODIFIERSTATES];\n\nextern unsigned int keydown[K_MAX];\t//bitmask of devices.\n\nenum\n{\n\tkc_game,\t\t//csprogs.dat\n\tkc_menuqc,\t\t//\n\tkc_nativemenu,\t//\n\tkc_plugin,\t\t//for plugins\n\tkc_console,\t\t//generic engine-defined cursor\n\tkc_max\n};\nextern struct key_cursor_s\n{\n\tchar name[MAX_QPATH];\n\tfloat hotspot[2];\n\tfloat scale;\n\tqboolean dirty;\n\tvoid *handle;\n} key_customcursor[kc_max];\n\nextern unsigned char *chat_buffer;\nextern\tint chat_bufferpos;\nextern\tqboolean\tchat_team;\n\nvoid Key_Event (unsigned int devid, int key, unsigned int unicode, qboolean down);\nvoid Key_Init (void);\nvoid IN_WriteButtons(vfsfile_t *f, qboolean all);\nvoid Key_WriteBindings (struct vfsfile_s *f);\nvoid Key_SetBinding (int keynum, int modifier, const char *binding, int cmdlevel);\nvoid Key_ClearStates (void);\nqboolean Key_Centerprint(int key, int unicode, unsigned int devid);\nvoid Key_Unbindall_f (void);\t//aka: Key_Shutdown\nvoid Key_ConsoleReplace(const char *instext);\nvoid Key_DefaultLinkClicked(console_t *con, char *text, char *info);\nvoid Key_HandleConsoleLink(console_t *con, char *buffer);\n\nqboolean Key_Console (console_t *con, int key, unsigned int unicode);\nvoid Key_ConsoleRelease(console_t *con, int key, unsigned int unicode);\n\nstruct console_s;\nqboolean Key_GetConsoleSelectionBox(struct console_s *con, int *sx, int *sy, int *ex, int *ey);\nqboolean Key_MouseShouldBeFree(void);\n\nconst char *Key_Demoji(char *buffer, size_t buffersize, const char *in);\t//strips out :smile: stuff.\nvoid Key_EmojiCompletion_c(int argn, const char *partial, struct xcommandargcompletioncb_s *ctx);\n#endif\n#endif\n\n"
  },
  {
    "path": "engine/client/lhfont.h",
    "content": "0x00,0x00,0x0B,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x01,0x08,0x08,0x99,0x00,0x83,0xFF,0xFF,0x00,0xE1,0x00,0x99,0x00,0x83,0xFF,0xFF,0x00,0xE1,0x00,0x81,0x00,0x83,0xFF,0x93,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x8B,0x00,0x89,0xFF,0x8B,0x00,0x85,0xFF,0x85,0x00,0x87,0xFF,0x89,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x81,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x85,0x00,0x83,0xFF,0x85,0x00,0x83,0xFF,0x89,0x00,0x8B,0xFF,0x89,0x00,0x85,0xFF,0x87,0x00,0x83,0xFF,0x87,0x00,0x85,0xFF,0x9D,0x00,0x83,0xFF,0x85,0x00,0x81,0x00,0x83,0xFF,0x93,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x8B,0x00,0x8A,0xFF,0x89,0x00,0x86,0xFF,0x84,0x00,0x89,0xFF,\n0x87,0x00,0x85,0xFF,0x86,0x00,0x83,0xFF,0x81,0x00,0x83,0xFF,0x83,0x00,0x84,0xFF,0x83,0x00,0x84,0xFF,0x85,0x00,0x84,0xFF,0x88,0x00,0x8B,0xFF,0x88,0x00,0x86,0xFF,0x87,0x00,0x83,0xFF,0x87,0x00,0x86,0xFF,0x9B,0x00,0x84,0xFF,0x85,0x00,0x81,0x00,0x89,0xFF,0x87,0x00,0x89,0xFF,0x83,0x00,0x83,0xFF,0x93,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x85,0x00,0x87,0xFF,0x85,0x00,0x89,0xFF,0x84,0x00,0x84,0xFF,0x81,0x00,0x84,0xFF,0x88,0x00,0x83,0xFF,0x88,0x00,0x84,0xFF,0x8D,0x00,0x83,0xFF,0x8B,0x00,0x83,0xFF,0x8B,0x00,0x83,0xFF,0x99,0x00,0x83,0xFF,0x87,0x00,0x81,0x00,0x8A,0xFF,0x85,0x00,0x8A,0xFF,0x83,0x00,0x83,0xFF,\n0x93,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x84,0x00,0x89,0xFF,0x83,0x00,0x8B,0xFF,0x84,0x00,0x89,0xFF,0x89,0x00,0x83,0xFF,0x89,0x00,0x84,0xFF,0x8B,0x00,0x84,0xFF,0x8B,0x00,0x83,0xFF,0x8B,0x00,0x84,0xFF,0x97,0x00,0x84,0xFF,0x87,0x00,0x81,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x8D,0x00,0x88,0xFF,0x88,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x84,0xFF,0x81,0x00,0x84,0xFF,0x81,0x00,0x83,0xFF,0x81,0x00,0x81,0xFF,0x81,0x00,0x83,0xFF,0x84,0x00,0x87,0xFF,0x8A,0x00,0x83,0xFF,0x8A,0x00,0x84,0xFF,0x8A,0x00,0x82,0xFF,0x8D,0x00,\n0x83,0xFF,0x8D,0x00,0x82,0xFF,0x96,0x00,0x8B,0xFF,0x81,0x00,0x81,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x8C,0x00,0x88,0xFF,0x89,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x81,0x00,0x83,0xFF,0x81,0x00,0x81,0xFF,0x81,0x00,0x83,0xFF,0x84,0x00,0x87,0xFF,0x89,0x00,0x85,0xFF,0x8A,0x00,0x84,0xFF,0x87,0x00,0x84,0xFF,0x9F,0x00,0x84,0xFF,0x94,0x00,0x8B,0xFF,0x81,0x00,0x81,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x8D,0x00,0x89,0xFF,0x83,0x00,\n0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x81,0x00,0x83,0xFF,0x81,0x00,0x81,0xFF,0x81,0x00,0x83,0xFF,0x83,0x00,0x89,0xFF,0x87,0x00,0x87,0xFF,0x8A,0x00,0x84,0xFF,0x86,0x00,0x84,0xFF,0x9F,0x00,0x84,0xFF,0x95,0x00,0x84,0xFF,0x87,0x00,0x81,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x82,0x00,0x84,0xFF,0x83,0x00,0x83,0xFF,0x8D,0x00,0x89,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x81,0x00,0x83,0xFF,0x81,0x00,0x81,0xFF,0x81,0x00,0x83,0xFF,0x82,0x00,0x84,0xFF,0x81,0x00,0x84,0xFF,0x85,0x00,0x89,0xFF,0x8A,0x00,0x84,0xFF,\n0x87,0x00,0x82,0xFF,0x8D,0x00,0x83,0xFF,0x8D,0x00,0x82,0xFF,0x98,0x00,0x83,0xFF,0x87,0x00,0x81,0x00,0x8A,0xFF,0x85,0x00,0x8A,0xFF,0x83,0x00,0x8A,0xFF,0x85,0x00,0x8A,0xFF,0x87,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x81,0x00,0x83,0xFF,0x81,0x00,0x81,0xFF,0x81,0x00,0x83,0xFF,0x81,0x00,0x84,0xFF,0x83,0x00,0x84,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x8B,0xFF,0x86,0x00,0x84,0xFF,0x8B,0x00,0x83,0xFF,0x8B,0x00,0x84,0xFF,0x99,0x00,0x84,0xFF,0x85,0x00,0x81,0x00,0x89,0xFF,0x87,0x00,0x89,0xFF,0x83,0x00,0x89,0xFF,0x87,0x00,0x89,0xFF,0x87,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,\n0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x81,0x00,0x83,0xFF,0x81,0x00,0x81,0xFF,0x81,0x00,0x83,0xFF,0x81,0x00,0x83,0xFF,0x85,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x8B,0xFF,0x87,0x00,0x83,0xFF,0x8B,0x00,0x83,0xFF,0x8B,0x00,0x83,0xFF,0x9B,0x00,0x83,0xFF,0x85,0x00,0xFF,0x00,0xB6,0x00,0x86,0xFF,0x87,0x00,0x83,0xFF,0x87,0x00,0x86,0xFF,0x86,0x00,0x83,0xFF,0x81,0x00,0x85,0xFF,0x93,0x00,0xFF,0x00,0xB7,0x00,0x85,0xFF,0x87,0x00,0x83,0xFF,0x87,0x00,0x85,0xFF,0x87,0x00,0x83,0xFF,0x81,0x00,0x86,0xFF,0x92,0x00,0xFF,0x00,0xE0,0x00,0x86,0xFF,0x81,0x00,0x83,0xFF,0x91,0x00,0xFF,0x00,0xE1,0x00,0x85,0xFF,\n0x81,0x00,0x83,0xFF,0x91,0x00,0xF3,0x00,0x85,0xFF,0xFF,0x00,0x85,0x00,0xF3,0x00,0x86,0xFF,0xFF,0x00,0x84,0x00,0x93,0x00,0x89,0xFF,0x83,0x00,0x89,0xFF,0x87,0x00,0x89,0xFF,0x85,0x00,0x89,0xFF,0x85,0x00,0x87,0xFF,0x85,0x00,0x83,0xFF,0x91,0x00,0x83,0xFF,0x85,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x87,0x00,0x85,0xFF,0x89,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x87,0xFF,0x85,0x00,0x83,0xFF,0x81,0x00,0x81,0xFF,0x81,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x85,0x00,0x87,0xFF,0x83,0x00,0x92,0x00,0x8A,0xFF,0x83,0x00,0x8A,0xFF,0x85,0x00,0x8A,0xFF,0x84,0x00,0x8A,0xFF,0x84,0x00,0x88,0xFF,0x85,0x00,0x83,0xFF,\n0x91,0x00,0x83,0xFF,0x85,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x86,0x00,0x87,0xFF,0x88,0x00,0x83,0xFF,0x82,0x00,0x84,0xFF,0x83,0x00,0x87,0xFF,0x85,0x00,0x83,0xFF,0x81,0x00,0x81,0xFF,0x81,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x84,0x00,0x89,0xFF,0x82,0x00,0x91,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x8B,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x8B,0x00,0x83,0xFF,0x8B,0x00,0x89,0xFF,0x85,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x85,0x00,0x83,0xFF,0x81,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x81,0x00,0x84,0xFF,0x86,0x00,\n0x83,0xFF,0x87,0x00,0x83,0xFF,0x81,0x00,0x81,0xFF,0x81,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x81,0x00,0x91,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x8B,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x8B,0x00,0x83,0xFF,0x8A,0x00,0x8A,0xFF,0x85,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x85,0x00,0x83,0xFF,0x81,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x00,0x00,0x84,0xFF,0x87,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x81,0x00,0x81,0xFF,0x81,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,\n0x83,0x00,0x83,0xFF,0x81,0x00,0x92,0x00,0x8A,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x8B,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x8B,0xFF,0x83,0x00,0x83,0xFF,0x89,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x85,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x8B,0x00,0x83,0xFF,0x87,0x00,0x88,0xFF,0x88,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x81,0x00,0x81,0xFF,0x81,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x81,0x00,0x93,0x00,0x89,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x8B,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x8B,0xFF,\n0x83,0x00,0x83,0xFF,0x89,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x85,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x8B,0x00,0x83,0xFF,0x87,0x00,0x88,0xFF,0x88,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x81,0x00,0x81,0xFF,0x81,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x81,0x00,0x83,0x00,0x83,0xFF,0x91,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x8B,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x85,0xFF,0x87,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x85,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x8B,0x00,\n0x83,0xFF,0x87,0x00,0x83,0xFF,0x00,0x00,0x84,0xFF,0x87,0x00,0x83,0xFF,0x87,0x00,0x8D,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x81,0x00,0x83,0x00,0x84,0xFF,0x90,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x8B,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x85,0xFF,0x87,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x85,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x8B,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x81,0x00,0x84,0xFF,0x86,0x00,0x83,0xFF,0x87,0x00,0x8D,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,\n0x83,0x00,0x83,0xFF,0x81,0x00,0x84,0x00,0x84,0xFF,0x89,0x00,0x88,0xFF,0x84,0x00,0x8A,0xFF,0x85,0x00,0x8A,0xFF,0x84,0x00,0x8A,0xFF,0x84,0x00,0x89,0xFF,0x84,0x00,0x83,0xFF,0x8A,0x00,0x8A,0xFF,0x85,0x00,0x8A,0xFF,0xA4,0x00,0x83,0xFF,0x82,0x00,0x84,0xFF,0x85,0x00,0x83,0xFF,0x88,0x00,0x8B,0xFF,0x84,0x00,0x8A,0xFF,0x85,0x00,0x89,0xFF,0x82,0x00,0x85,0x00,0x83,0xFF,0x89,0x00,0x87,0xFF,0x85,0x00,0x89,0xFF,0x87,0x00,0x89,0xFF,0x85,0x00,0x89,0xFF,0x85,0x00,0x87,0xFF,0x85,0x00,0x83,0xFF,0x8B,0x00,0x89,0xFF,0x85,0x00,0x89,0xFF,0xA5,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x85,0x00,0x83,0xFF,0x89,0x00,0x83,0xFF,0x81,0x00,0x83,0xFF,0x85,0x00,0x89,0xFF,\n0x87,0x00,0x87,0xFF,0x83,0x00,0x85,0x00,0x83,0xFF,0x97,0x00,0x83,0xFF,0xA3,0x00,0x83,0xFF,0x94,0x00,0x86,0xFF,0x97,0x00,0x83,0xFF,0x8F,0x00,0x83,0xFF,0x8B,0x00,0x83,0xFF,0x97,0x00,0x85,0xFF,0xB7,0x00,0x85,0x00,0x83,0xFF,0x97,0x00,0x83,0xFF,0xA3,0x00,0x83,0xFF,0x95,0x00,0x85,0xFF,0x97,0x00,0x83,0xFF,0x8F,0x00,0x83,0xFF,0x8B,0x00,0x83,0xFF,0x97,0x00,0x85,0xFF,0xB7,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0x97,0x00,0x85,0xFF,0xFF,0x00,0xE1,0x00,0x96,0x00,0x86,0xFF,0xFF,0x00,0xE1,0x00,0x81,0x00,0x83,0xFF,0x8D,0x00,0x87,0xFF,0x85,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x89,0xFF,0x89,0x00,0x83,0xFF,0x89,0x00,0x87,0xFF,0x89,0x00,\n0x83,0xFF,0x87,0x00,0x83,0xFF,0x81,0x00,0x83,0xFF,0x85,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x87,0x00,0x8B,0xFF,0x85,0x00,0x87,0xFF,0x8F,0x00,0x83,0xFF,0x83,0x00,0x87,0xFF,0x93,0x00,0x8D,0xFF,0x81,0x00,0x81,0x00,0x83,0xFF,0x8C,0x00,0x88,0xFF,0x85,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x8A,0xFF,0x88,0x00,0x83,0xFF,0x88,0x00,0x89,0xFF,0x87,0x00,0x85,0xFF,0x86,0x00,0x89,0xFF,0x85,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x87,0x00,0x8B,0xFF,0x85,0x00,0x87,0xFF,0x8E,0x00,0x84,0xFF,0x83,0x00,0x87,0xFF,0x93,0x00,0x8D,0xFF,0x81,0x00,0x81,0x00,0x83,0xFF,0x8B,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,\n0x83,0xFF,0x83,0x00,0x83,0xFF,0x8C,0x00,0x82,0xFF,0x87,0x00,0x83,0xFF,0x87,0x00,0x84,0xFF,0x81,0x00,0x84,0xFF,0x85,0x00,0x87,0xFF,0x85,0x00,0x89,0xFF,0x85,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x8D,0x00,0x83,0xFF,0x91,0x00,0x84,0xFF,0x88,0x00,0x83,0xFF,0xA3,0x00,0x81,0x00,0x83,0xFF,0x8B,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x8C,0x00,0x82,0xFF,0x87,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x84,0x00,0x89,0xFF,0x83,0x00,0x8B,0xFF,0x84,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x87,0x00,0x84,0xFF,0x8C,0x00,0x83,0xFF,0x90,0x00,0x84,0xFF,\n0x89,0x00,0x83,0xFF,0xA3,0x00,0x81,0x00,0x83,0xFF,0x8B,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x8C,0x00,0x82,0xFF,0x87,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x84,0xFF,0x81,0x00,0x84,0xFF,0x81,0x00,0x83,0xFF,0x81,0x00,0x81,0xFF,0x81,0x00,0x83,0xFF,0x84,0x00,0x89,0xFF,0x88,0x00,0x83,0xFF,0x88,0x00,0x84,0xFF,0x8B,0x00,0x83,0xFF,0x8F,0x00,0x84,0xFF,0x8A,0x00,0x83,0xFF,0xA3,0x00,0x81,0x00,0x83,0xFF,0x8B,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x85,0x00,0x88,0xFF,0x88,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,\n0x83,0x00,0x83,0xFF,0x81,0x00,0x83,0xFF,0x81,0x00,0x81,0xFF,0x81,0x00,0x83,0xFF,0x85,0x00,0x87,0xFF,0x89,0x00,0x83,0xFF,0x89,0x00,0x84,0xFF,0x8A,0x00,0x83,0xFF,0x8E,0x00,0x84,0xFF,0x8B,0x00,0x83,0xFF,0xA3,0x00,0x81,0x00,0x89,0xFF,0x85,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x8A,0xFF,0x85,0x00,0x88,0xFF,0x89,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x81,0x00,0x83,0xFF,0x81,0x00,0x81,0xFF,0x81,0x00,0x83,0xFF,0x86,0x00,0x85,0xFF,0x8A,0x00,0x83,0xFF,0x8A,0x00,0x84,0xFF,0x89,0x00,0x83,0xFF,0x8D,0x00,0x84,0xFF,0x8C,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x85,0x00,0x83,0xFF,0x91,0x00,\n0x81,0x00,0x8A,0xFF,0x84,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x8A,0xFF,0x84,0x00,0x83,0xFF,0x8F,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x81,0x00,0x83,0xFF,0x81,0x00,0x81,0xFF,0x81,0x00,0x83,0xFF,0x86,0x00,0x85,0xFF,0x89,0x00,0x85,0xFF,0x8A,0x00,0x84,0xFF,0x88,0x00,0x83,0xFF,0x8C,0x00,0x84,0xFF,0x8D,0x00,0x83,0xFF,0x83,0x00,0x84,0xFF,0x83,0x00,0x84,0xFF,0x91,0x00,0x81,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x8F,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,\n0x83,0xFF,0x83,0x00,0x83,0xFF,0x81,0x00,0x83,0xFF,0x81,0x00,0x81,0xFF,0x81,0x00,0x83,0xFF,0x85,0x00,0x87,0xFF,0x87,0x00,0x87,0xFF,0x8A,0x00,0x84,0xFF,0x87,0x00,0x83,0xFF,0x8B,0x00,0x84,0xFF,0x8E,0x00,0x83,0xFF,0x84,0x00,0x84,0xFF,0x81,0x00,0x84,0xFF,0x92,0x00,0x81,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x8F,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x81,0x00,0x83,0xFF,0x81,0x00,0x81,0xFF,0x81,0x00,0x83,0xFF,0x84,0x00,0x89,0xFF,0x85,0x00,0x89,0xFF,0x8A,0x00,0x84,0xFF,0x86,0x00,0x83,0xFF,\n0x8A,0x00,0x84,0xFF,0x8F,0x00,0x83,0xFF,0x85,0x00,0x89,0xFF,0x93,0x00,0x81,0x00,0x8A,0xFF,0x85,0x00,0x89,0xFF,0x84,0x00,0x8A,0xFF,0x85,0x00,0x8A,0xFF,0x83,0x00,0x8B,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x81,0x00,0x83,0xFF,0x81,0x00,0x81,0xFF,0x81,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x84,0xFF,0x81,0x00,0x84,0xFF,0x83,0x00,0x8B,0xFF,0x85,0x00,0x87,0xFF,0x85,0x00,0x84,0xFF,0x8C,0x00,0x87,0xFF,0x86,0x00,0x87,0xFF,0x94,0x00,0x81,0x00,0x89,0xFF,0x87,0x00,0x87,0xFF,0x85,0x00,0x89,0xFF,0x87,0x00,0x89,0xFF,0x83,0x00,0x8B,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,\n0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x81,0x00,0x83,0xFF,0x81,0x00,0x81,0xFF,0x81,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x8B,0xFF,0x85,0x00,0x87,0xFF,0x85,0x00,0x83,0xFF,0x8D,0x00,0x87,0xFF,0x87,0x00,0x85,0xFF,0x95,0x00,0xFF,0x00,0xE4,0x00,0x83,0xFF,0x96,0x00,0xFF,0x00,0xE5,0x00,0x81,0xFF,0x97,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0x83,0x00,0x87,0xFF,0x85,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x89,0xFF,0x87,0x00,0x87,0xFF,0x85,0x00,0x89,0xFF,0x85,0x00,0x8B,0xFF,0x83,0x00,0x83,0xFF,0x8B,0x00,0x87,0xFF,0x87,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,\n0x8B,0xFF,0x83,0x00,0x85,0xFF,0x89,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x8B,0xFF,0x81,0x00,0x83,0xFF,0x85,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x85,0x00,0x87,0xFF,0x83,0x00,0x82,0x00,0x88,0xFF,0x85,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x8A,0xFF,0x85,0x00,0x89,0xFF,0x84,0x00,0x8A,0xFF,0x84,0x00,0x8B,0xFF,0x83,0x00,0x83,0xFF,0x8A,0x00,0x89,0xFF,0x86,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x8B,0xFF,0x82,0x00,0x87,0xFF,0x88,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x8B,0xFF,0x81,0x00,0x83,0xFF,0x85,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x84,0x00,0x89,0xFF,0x82,0x00,0x81,0x00,\n0x83,0xFF,0x8B,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x8B,0x00,0x83,0xFF,0x89,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x85,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x85,0x00,0x83,0xFF,0x81,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x89,0x00,0x83,0xFF,0x85,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x81,0x00,0x81,0x00,0x83,0xFF,0x8B,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,\n0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x8B,0x00,0x83,0xFF,0x89,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x85,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x85,0x00,0x83,0xFF,0x81,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x89,0x00,0x83,0xFF,0x85,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x82,0x00,0x84,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x81,0x00,0x81,0x00,0x83,0xFF,0x81,0x00,0x85,0xFF,0x83,0x00,0x8B,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x8B,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x8B,0x00,\n0x83,0xFF,0x89,0x00,0x83,0xFF,0x81,0x00,0x85,0xFF,0x85,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x8B,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x89,0x00,0x83,0xFF,0x81,0x00,0x81,0xFF,0x81,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x81,0x00,0x85,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x81,0x00,0x81,0x00,0x83,0xFF,0x81,0x00,0x85,0xFF,0x83,0x00,0x8B,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x8B,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x8B,0x00,0x83,0xFF,0x89,0x00,0x83,0xFF,0x81,0x00,0x85,0xFF,0x85,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,\n0x8B,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x89,0x00,0x83,0xFF,0x81,0x00,0x81,0xFF,0x81,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x00,0x00,0x86,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x81,0x00,0x81,0x00,0x83,0xFF,0x81,0x00,0x85,0xFF,0x84,0x00,0x82,0xFF,0x81,0x00,0x84,0xFF,0x84,0x00,0x8A,0xFF,0x84,0x00,0x83,0xFF,0x8B,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x87,0xFF,0x87,0x00,0x87,0xFF,0x85,0x00,0x83,0xFF,0x8D,0x00,0x8B,0xFF,0x87,0x00,0x83,0xFF,0x8B,0x00,0x83,0xFF,0x87,0x00,0x89,0xFF,0x85,0x00,0x83,0xFF,0x89,0x00,0x8D,0xFF,0x83,0x00,0x8B,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x81,0x00,\n0x81,0x00,0x83,0xFF,0x81,0x00,0x85,0xFF,0x85,0x00,0x81,0xFF,0x81,0x00,0x83,0xFF,0x85,0x00,0x8A,0xFF,0x84,0x00,0x83,0xFF,0x8B,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x87,0xFF,0x87,0x00,0x87,0xFF,0x85,0x00,0x83,0xFF,0x8D,0x00,0x8B,0xFF,0x87,0x00,0x83,0xFF,0x8B,0x00,0x83,0xFF,0x87,0x00,0x89,0xFF,0x85,0x00,0x83,0xFF,0x89,0x00,0x8D,0xFF,0x83,0x00,0x8B,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x81,0x00,0x81,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x85,0x00,0x87,0xFF,0x85,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x8B,0x00,0x83,0xFF,0x89,0x00,\n0x83,0xFF,0x83,0x00,0x83,0xFF,0x85,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x8B,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x89,0x00,0x8D,0xFF,0x83,0x00,0x86,0xFF,0x00,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x81,0x00,0x81,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x85,0x00,0x87,0xFF,0x85,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x8B,0x00,0x83,0xFF,0x89,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x85,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x8B,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,\n0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x89,0x00,0x85,0xFF,0x81,0x00,0x85,0xFF,0x83,0x00,0x85,0xFF,0x81,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x81,0x00,0x82,0x00,0x89,0xFF,0x87,0x00,0x85,0xFF,0x86,0x00,0x8A,0xFF,0x85,0x00,0x89,0xFF,0x84,0x00,0x8A,0xFF,0x84,0x00,0x8B,0xFF,0x83,0x00,0x8B,0xFF,0x82,0x00,0x89,0xFF,0x86,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x8B,0xFF,0x87,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x89,0x00,0x84,0xFF,0x83,0x00,0x84,0xFF,0x83,0x00,0x84,0xFF,0x82,0x00,0x83,0xFF,0x84,0x00,0x89,0xFF,0x82,0x00,0x83,0x00,0x87,0xFF,0x89,0x00,0x83,0xFF,0x87,0x00,0x89,0xFF,\n0x87,0x00,0x87,0xFF,0x85,0x00,0x89,0xFF,0x85,0x00,0x8B,0xFF,0x83,0x00,0x8B,0xFF,0x83,0x00,0x87,0xFF,0x87,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x8B,0xFF,0x87,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x89,0x00,0x83,0xFF,0x85,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x85,0x00,0x87,0xFF,0x83,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xB3,0x00,0x83,0xFF,0xC7,0x00,0xFF,0x00,0xB3,0x00,0x84,0xFF,0xC6,0x00,0x83,0x00,0x87,0xFF,0x89,0x00,0x83,0xFF,0x87,0x00,0x8B,0xFF,0x83,0x00,0x89,0xFF,0x8B,0x00,0x83,0xFF,0x85,0x00,0x89,0xFF,0x87,0x00,0x87,0xFF,0x89,0x00,0x83,0xFF,0x89,0x00,\n0x87,0xFF,0x87,0x00,0x87,0xFF,0x89,0x00,0x83,0xFF,0x8A,0x00,0x84,0xFF,0x8D,0x00,0x83,0xFF,0x97,0x00,0x83,0xFF,0x8D,0x00,0x83,0xFF,0x85,0x00,0x82,0x00,0x89,0xFF,0x88,0x00,0x83,0xFF,0x87,0x00,0x8B,0xFF,0x83,0x00,0x8A,0xFF,0x8A,0x00,0x83,0xFF,0x85,0x00,0x8A,0xFF,0x85,0x00,0x89,0xFF,0x88,0x00,0x83,0xFF,0x88,0x00,0x89,0xFF,0x86,0x00,0x88,0xFF,0x88,0x00,0x83,0xFF,0x8B,0x00,0x83,0xFF,0x8C,0x00,0x84,0xFF,0x97,0x00,0x84,0xFF,0x8C,0x00,0x83,0xFF,0x85,0x00,0x81,0x00,0x85,0xFF,0x81,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x93,0x00,0x83,0xFF,0x83,0x00,0x8B,0xFF,0x8B,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x87,0x00,\n0x83,0xFF,0x87,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x8B,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x8B,0x00,0x83,0xFF,0x8B,0x00,0x84,0xFF,0x86,0x00,0x8B,0xFF,0x86,0x00,0x84,0xFF,0x95,0x00,0x81,0x00,0x86,0xFF,0x00,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x93,0x00,0x83,0xFF,0x83,0x00,0x8B,0xFF,0x8B,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x8B,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x8B,0x00,0x83,0xFF,0x8A,0x00,0x84,0xFF,0x87,0x00,0x8B,0xFF,0x87,0x00,0x84,0xFF,0x94,0x00,0x81,0x00,0x83,0xFF,0x00,0x00,0x86,0xFF,0x87,0x00,0x83,0xFF,0x88,0x00,0x88,0xFF,0x89,0x00,\n0x86,0xFF,0x84,0x00,0x83,0xFF,0x81,0x00,0x83,0xFF,0x85,0x00,0x8A,0xFF,0x84,0x00,0x8A,0xFF,0x88,0x00,0x83,0xFF,0x88,0x00,0x89,0xFF,0x86,0x00,0x89,0xFF,0xA5,0x00,0x84,0xFF,0x9D,0x00,0x84,0xFF,0x89,0x00,0x83,0xFF,0x85,0x00,0x81,0x00,0x83,0xFF,0x81,0x00,0x85,0xFF,0x87,0x00,0x83,0xFF,0x89,0x00,0x88,0xFF,0x88,0x00,0x86,0xFF,0x84,0x00,0x83,0xFF,0x81,0x00,0x83,0xFF,0x85,0x00,0x89,0xFF,0x85,0x00,0x89,0xFF,0x89,0x00,0x84,0xFF,0x87,0x00,0x89,0xFF,0x85,0x00,0x8A,0xFF,0xA5,0x00,0x83,0xFF,0x9F,0x00,0x83,0xFF,0x89,0x00,0x83,0xFF,0x85,0x00,0x81,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x8F,0x00,0x83,0xFF,0x8B,0x00,0x83,0xFF,0x84,0x00,\n0x88,0xFF,0x85,0x00,0x83,0xFF,0x8B,0x00,0x83,0xFF,0x90,0x00,0x84,0xFF,0x85,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0xA5,0x00,0x83,0xFF,0x9F,0x00,0x83,0xFF,0x89,0x00,0x85,0xFF,0x83,0x00,0x81,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x8F,0x00,0x83,0xFF,0x8B,0x00,0x83,0xFF,0x85,0x00,0x87,0xFF,0x85,0x00,0x83,0xFF,0x8B,0x00,0x83,0xFF,0x91,0x00,0x83,0xFF,0x85,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0xA5,0x00,0x84,0xFF,0x9D,0x00,0x84,0xFF,0x89,0x00,0x86,0xFF,0x82,0x00,0x82,0x00,0x89,0xFF,0x86,0x00,0x85,0xFF,0x87,0x00,0x8A,0xFF,0x84,0x00,0x8A,0xFF,0x87,0x00,\n0x86,0xFF,0x85,0x00,0x8B,0xFF,0x84,0x00,0x88,0xFF,0x85,0x00,0x89,0xFF,0x86,0x00,0x89,0xFF,0x85,0x00,0x89,0xFF,0x88,0x00,0x83,0xFF,0x8B,0x00,0x83,0xFF,0x8A,0x00,0x84,0xFF,0x87,0x00,0x8B,0xFF,0x87,0x00,0x84,0xFF,0x8E,0x00,0x83,0xFF,0x81,0x00,0x83,0x00,0x87,0xFF,0x87,0x00,0x85,0xFF,0x87,0x00,0x89,0xFF,0x85,0x00,0x89,0xFF,0x89,0x00,0x85,0xFF,0x85,0x00,0x8B,0xFF,0x85,0x00,0x87,0xFF,0x85,0x00,0x89,0xFF,0x87,0x00,0x87,0xFF,0x87,0x00,0x87,0xFF,0x89,0x00,0x83,0xFF,0x8B,0x00,0x83,0xFF,0x8B,0x00,0x84,0xFF,0x86,0x00,0x8B,0xFF,0x86,0x00,0x84,0xFF,0x8F,0x00,0x83,0xFF,0x81,0x00,0xFF,0x00,0xA5,0x00,0x83,0xFF,0x8B,0x00,0x83,0xFF,0x8C,0x00,0x84,0xFF,\n0x97,0x00,0x84,0xFF,0x88,0x00,0x8A,0xFF,0x82,0x00,0xFF,0x00,0xA5,0x00,0x83,0xFF,0x8B,0x00,0x83,0xFF,0x8D,0x00,0x83,0xFF,0x97,0x00,0x83,0xFF,0x89,0x00,0x89,0xFF,0x83,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xC5,0x00,0x83,0xFF,0xB5,0x00,0xFF,0x00,0xC5,0x00,0x84,0xFF,0xB4,0x00,0x95,0x00,0x83,0xFF,0x97,0x00,0x83,0xFF,0x81,0x00,0x83,0xFF,0x89,0x00,0x81,0xFF,0x87,0x00,0x83,0xFF,0x85,0x00,0x83,0xFF,0x85,0x00,0x85,0xFF,0x81,0x00,0x83,0xFF,0x97,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x89,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x8C,0x00,0x84,0xFF,0x95,0x00,0x83,0xFF,0x89,0x00,0x83,0xFF,0x8B,0x00,0x95,0x00,0x83,0xFF,\n0x97,0x00,0x83,0xFF,0x81,0x00,0x83,0xFF,0x85,0x00,0x88,0xFF,0x84,0x00,0x84,0xFF,0x84,0x00,0x83,0xFF,0x84,0x00,0x8C,0xFF,0x96,0x00,0x84,0xFF,0x87,0x00,0x84,0xFF,0x88,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x8D,0x00,0x83,0xFF,0x95,0x00,0x83,0xFF,0x89,0x00,0x84,0xFF,0x8A,0x00,0xB1,0x00,0x83,0xFF,0x81,0x00,0x83,0xFF,0x85,0x00,0x89,0xFF,0x84,0x00,0x84,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x84,0xFF,0x96,0x00,0x84,0xFF,0x89,0x00,0x84,0xFF,0x89,0x00,0x87,0xFF,0x89,0x00,0x83,0xFF,0x8D,0x00,0x83,0xFF,0x95,0x00,0x83,0xFF,0x8A,0x00,0x84,0xFF,0x89,0x00,0xB1,0x00,0x83,0xFF,0x81,0x00,0x83,0xFF,0x89,0x00,0x81,0xFF,\n0x81,0x00,0x81,0xFF,0x85,0x00,0x84,0xFF,0x82,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x96,0x00,0x84,0xFF,0x8B,0x00,0x84,0xFF,0x88,0x00,0x87,0xFF,0x89,0x00,0x83,0xFF,0x8D,0x00,0x83,0xFF,0x95,0x00,0x83,0xFF,0x8B,0x00,0x84,0xFF,0x88,0x00,0x95,0x00,0x83,0xFF,0x95,0x00,0x8D,0xFF,0x87,0x00,0x81,0xFF,0x81,0x00,0x81,0xFF,0x86,0x00,0x84,0xFF,0x89,0x00,0x83,0xFF,0x81,0x00,0x85,0xFF,0x95,0x00,0x84,0xFF,0x8D,0x00,0x84,0xFF,0x83,0x00,0x8F,0xFF,0x81,0x00,0x8B,0xFF,0x93,0x00,0x8B,0xFF,0x94,0x00,0x84,0xFF,0x87,0x00,0x95,0x00,0x83,0xFF,0x95,0x00,0x8D,0xFF,0x84,0x00,0x88,0xFF,0x87,0x00,0x84,0xFF,0x88,0x00,0x83,0xFF,0x81,0x00,0x86,0xFF,\n0x94,0x00,0x83,0xFF,0x8F,0x00,0x83,0xFF,0x83,0x00,0x8F,0xFF,0x81,0x00,0x8B,0xFF,0x93,0x00,0x8B,0xFF,0x95,0x00,0x84,0xFF,0x86,0x00,0x95,0x00,0x83,0xFF,0x97,0x00,0x83,0xFF,0x81,0x00,0x83,0xFF,0x85,0x00,0x88,0xFF,0x89,0x00,0x84,0xFF,0x88,0x00,0x88,0xFF,0x00,0x00,0x82,0xFF,0x87,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x8F,0x00,0x83,0xFF,0x87,0x00,0x87,0xFF,0x89,0x00,0x83,0xFF,0xBA,0x00,0x84,0xFF,0x85,0x00,0x95,0x00,0x83,0xFF,0x97,0x00,0x83,0xFF,0x81,0x00,0x83,0xFF,0x85,0x00,0x81,0xFF,0x81,0x00,0x81,0xFF,0x8D,0x00,0x84,0xFF,0x88,0x00,0x86,0xFF,0x82,0x00,0x81,0xFF,0x87,0x00,0x84,0xFF,0x86,0x00,0x83,0xFF,0x8F,0x00,0x83,0xFF,0x87,0x00,0x87,0xFF,\n0x89,0x00,0x83,0xFF,0xBB,0x00,0x84,0xFF,0x84,0x00,0x95,0x00,0x83,0xFF,0x89,0x00,0x83,0xFF,0x81,0x00,0x83,0xFF,0x81,0x00,0x8D,0xFF,0x83,0x00,0x81,0xFF,0x81,0x00,0x81,0xFF,0x87,0x00,0x83,0xFF,0x82,0x00,0x84,0xFF,0x88,0x00,0x86,0xFF,0x8C,0x00,0x84,0xFF,0x85,0x00,0x83,0xFF,0x8F,0x00,0x83,0xFF,0x85,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0xBC,0x00,0x84,0xFF,0x83,0x00,0x95,0x00,0x83,0xFF,0x89,0x00,0x83,0xFF,0x81,0x00,0x83,0xFF,0x81,0x00,0x8D,0xFF,0x83,0x00,0x89,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x84,0xFF,0x87,0x00,0x87,0xFF,0x8C,0x00,0x83,0xFF,0x85,0x00,0x84,0xFF,0x8D,0x00,0x84,0xFF,0x85,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,\n0x87,0x00,0x83,0xFF,0xBD,0x00,0x84,0xFF,0x82,0x00,0x95,0x00,0x83,0xFF,0x89,0x00,0x83,0xFF,0x81,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x81,0x00,0x83,0xFF,0x86,0x00,0x88,0xFF,0x83,0x00,0x83,0xFF,0x84,0x00,0x84,0xFF,0x85,0x00,0x83,0xFF,0x81,0x00,0x83,0xFF,0x8B,0x00,0x83,0xFF,0x86,0x00,0x84,0xFF,0x8B,0x00,0x84,0xFF,0xDD,0x00,0x84,0xFF,0x81,0x00,0x95,0x00,0x83,0xFF,0x89,0x00,0x83,0xFF,0x81,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x81,0x00,0x83,0xFF,0x89,0x00,0x81,0xFF,0x87,0x00,0x83,0xFF,0x85,0x00,0x83,0xFF,0x85,0x00,0x83,0xFF,0x81,0x00,0x83,0xFF,0x8B,0x00,0x83,0xFF,0x87,0x00,0x84,0xFF,0x89,0x00,0x84,0xFF,0xDF,0x00,0x83,0xFF,0x81,0x00,0xA3,0x00,\n0x83,0xFF,0x81,0x00,0x83,0xFF,0xB6,0x00,0x87,0xFF,0x99,0x00,0x84,0xFF,0x87,0x00,0x84,0xFF,0xE6,0x00,0xA3,0x00,0x83,0xFF,0x81,0x00,0x83,0xFF,0xB7,0x00,0x85,0xFF,0x9B,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0xE7,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,\n0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0x99,0x00,0x83,0xFF,0xFF,0x00,0xE1,0x00,0x99,0x00,0x83,0xFF,0xFF,0x00,0xE1,0x00,0x81,0x00,0x83,0xFF,0x93,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x8B,0x00,0x89,0xFF,0x8B,0x00,0x85,0xFF,0x85,0x00,0x87,0xFF,0x89,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x81,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x85,0x00,0x83,0xFF,0x85,0x00,0x83,0xFF,0x89,0x00,0x8B,0xFF,0x89,0x00,0x85,0xFF,0x87,0x00,0x83,0xFF,0x87,0x00,0x85,0xFF,0x9D,0x00,0x83,0xFF,0x85,0x00,\n0x81,0x00,0x83,0xFF,0x93,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x8B,0x00,0x8A,0xFF,0x89,0x00,0x86,0xFF,0x84,0x00,0x89,0xFF,0x87,0x00,0x85,0xFF,0x86,0x00,0x83,0xFF,0x81,0x00,0x83,0xFF,0x83,0x00,0x84,0xFF,0x83,0x00,0x84,0xFF,0x85,0x00,0x84,0xFF,0x88,0x00,0x8B,0xFF,0x88,0x00,0x86,0xFF,0x87,0x00,0x83,0xFF,0x87,0x00,0x86,0xFF,0x9B,0x00,0x84,0xFF,0x85,0x00,0x81,0x00,0x89,0xFF,0x87,0x00,0x89,0xFF,0x83,0x00,0x83,0xFF,0x93,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x85,0x00,0x87,0xFF,0x85,0x00,0x89,0xFF,0x84,0x00,0x84,0xFF,0x81,0x00,0x84,0xFF,0x88,0x00,0x83,0xFF,0x88,0x00,0x84,0xFF,0x8D,0x00,0x83,0xFF,0x8B,0x00,\n0x83,0xFF,0x8B,0x00,0x83,0xFF,0x99,0x00,0x83,0xFF,0x87,0x00,0x81,0x00,0x8A,0xFF,0x85,0x00,0x8A,0xFF,0x83,0x00,0x83,0xFF,0x93,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x84,0x00,0x89,0xFF,0x83,0x00,0x8B,0xFF,0x84,0x00,0x89,0xFF,0x89,0x00,0x83,0xFF,0x89,0x00,0x84,0xFF,0x8B,0x00,0x84,0xFF,0x8B,0x00,0x83,0xFF,0x8B,0x00,0x84,0xFF,0x97,0x00,0x84,0xFF,0x87,0x00,0x81,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x8D,0x00,0x88,0xFF,0x88,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x84,0xFF,0x81,0x00,0x84,0xFF,0x81,0x00,0x83,0xFF,0x81,0x00,\n0x81,0xFF,0x81,0x00,0x83,0xFF,0x84,0x00,0x87,0xFF,0x8A,0x00,0x83,0xFF,0x8A,0x00,0x84,0xFF,0x8A,0x00,0x82,0xFF,0x8D,0x00,0x83,0xFF,0x8D,0x00,0x82,0xFF,0x96,0x00,0x8B,0xFF,0x81,0x00,0x81,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x8C,0x00,0x88,0xFF,0x89,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x81,0x00,0x83,0xFF,0x81,0x00,0x81,0xFF,0x81,0x00,0x83,0xFF,0x84,0x00,0x87,0xFF,0x89,0x00,0x85,0xFF,0x8A,0x00,0x84,0xFF,0x87,0x00,0x84,0xFF,0x9F,0x00,0x84,0xFF,0x94,0x00,0x8B,0xFF,0x81,0x00,0x81,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,\n0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x8D,0x00,0x89,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x81,0x00,0x83,0xFF,0x81,0x00,0x81,0xFF,0x81,0x00,0x83,0xFF,0x83,0x00,0x89,0xFF,0x87,0x00,0x87,0xFF,0x8A,0x00,0x84,0xFF,0x86,0x00,0x84,0xFF,0x9F,0x00,0x84,0xFF,0x95,0x00,0x84,0xFF,0x87,0x00,0x81,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x82,0x00,0x84,0xFF,0x83,0x00,0x83,0xFF,0x8D,0x00,0x89,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x81,0x00,0x83,0xFF,\n0x81,0x00,0x81,0xFF,0x81,0x00,0x83,0xFF,0x82,0x00,0x84,0xFF,0x81,0x00,0x84,0xFF,0x85,0x00,0x89,0xFF,0x8A,0x00,0x84,0xFF,0x87,0x00,0x82,0xFF,0x8D,0x00,0x83,0xFF,0x8D,0x00,0x82,0xFF,0x98,0x00,0x83,0xFF,0x87,0x00,0x81,0x00,0x8A,0xFF,0x85,0x00,0x8A,0xFF,0x83,0x00,0x8A,0xFF,0x85,0x00,0x8A,0xFF,0x87,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x81,0x00,0x83,0xFF,0x81,0x00,0x81,0xFF,0x81,0x00,0x83,0xFF,0x81,0x00,0x84,0xFF,0x83,0x00,0x84,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x8B,0xFF,0x86,0x00,0x84,0xFF,0x8B,0x00,0x83,0xFF,0x8B,0x00,0x84,0xFF,0x99,0x00,0x84,0xFF,0x85,0x00,\n0x81,0x00,0x89,0xFF,0x87,0x00,0x89,0xFF,0x83,0x00,0x89,0xFF,0x87,0x00,0x89,0xFF,0x87,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x81,0x00,0x83,0xFF,0x81,0x00,0x81,0xFF,0x81,0x00,0x83,0xFF,0x81,0x00,0x83,0xFF,0x85,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x8B,0xFF,0x87,0x00,0x83,0xFF,0x8B,0x00,0x83,0xFF,0x8B,0x00,0x83,0xFF,0x9B,0x00,0x83,0xFF,0x85,0x00,0xFF,0x00,0xB6,0x00,0x86,0xFF,0x87,0x00,0x83,0xFF,0x87,0x00,0x86,0xFF,0x86,0x00,0x83,0xFF,0x81,0x00,0x85,0xFF,0x93,0x00,0xFF,0x00,0xB7,0x00,0x85,0xFF,0x87,0x00,0x83,0xFF,0x87,0x00,0x85,0xFF,0x87,0x00,0x83,0xFF,\n0x81,0x00,0x86,0xFF,0x92,0x00,0xFF,0x00,0xE0,0x00,0x86,0xFF,0x81,0x00,0x83,0xFF,0x91,0x00,0xFF,0x00,0xE1,0x00,0x85,0xFF,0x81,0x00,0x83,0xFF,0x91,0x00,0xF3,0x00,0x85,0xFF,0xFF,0x00,0x85,0x00,0xF3,0x00,0x86,0xFF,0xFF,0x00,0x84,0x00,0x93,0x00,0x89,0xFF,0x83,0x00,0x89,0xFF,0x87,0x00,0x89,0xFF,0x85,0x00,0x89,0xFF,0x85,0x00,0x87,0xFF,0x85,0x00,0x83,0xFF,0x91,0x00,0x83,0xFF,0x85,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x87,0x00,0x85,0xFF,0x89,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x87,0xFF,0x85,0x00,0x83,0xFF,0x81,0x00,0x81,0xFF,0x81,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x85,0x00,0x87,0xFF,0x83,0x00,\n0x92,0x00,0x8A,0xFF,0x83,0x00,0x8A,0xFF,0x85,0x00,0x8A,0xFF,0x84,0x00,0x8A,0xFF,0x84,0x00,0x88,0xFF,0x85,0x00,0x83,0xFF,0x91,0x00,0x83,0xFF,0x85,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x86,0x00,0x87,0xFF,0x88,0x00,0x83,0xFF,0x82,0x00,0x84,0xFF,0x83,0x00,0x87,0xFF,0x85,0x00,0x83,0xFF,0x81,0x00,0x81,0xFF,0x81,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x84,0x00,0x89,0xFF,0x82,0x00,0x91,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x8B,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x8B,0x00,0x83,0xFF,0x8B,0x00,0x89,0xFF,0x85,0x00,0x83,0xFF,0x83,0x00,\n0x83,0xFF,0x87,0x00,0x83,0xFF,0x85,0x00,0x83,0xFF,0x81,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x81,0x00,0x84,0xFF,0x86,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x81,0x00,0x81,0xFF,0x81,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x81,0x00,0x91,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x8B,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x8B,0x00,0x83,0xFF,0x8A,0x00,0x8A,0xFF,0x85,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x85,0x00,0x83,0xFF,0x81,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x00,0x00,0x84,0xFF,0x87,0x00,0x83,0xFF,\n0x87,0x00,0x83,0xFF,0x81,0x00,0x81,0xFF,0x81,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x81,0x00,0x92,0x00,0x8A,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x8B,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x8B,0xFF,0x83,0x00,0x83,0xFF,0x89,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x85,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x8B,0x00,0x83,0xFF,0x87,0x00,0x88,0xFF,0x88,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x81,0x00,0x81,0xFF,0x81,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x81,0x00,0x93,0x00,0x89,0xFF,\n0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x8B,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x8B,0xFF,0x83,0x00,0x83,0xFF,0x89,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x85,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x8B,0x00,0x83,0xFF,0x87,0x00,0x88,0xFF,0x88,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x81,0x00,0x81,0xFF,0x81,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x81,0x00,0x83,0x00,0x83,0xFF,0x91,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x8B,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,\n0x85,0xFF,0x87,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x85,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x8B,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x00,0x00,0x84,0xFF,0x87,0x00,0x83,0xFF,0x87,0x00,0x8D,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x81,0x00,0x83,0x00,0x84,0xFF,0x90,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x8B,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x85,0xFF,0x87,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x85,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x8B,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,\n0x81,0x00,0x84,0xFF,0x86,0x00,0x83,0xFF,0x87,0x00,0x8D,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x81,0x00,0x84,0x00,0x84,0xFF,0x89,0x00,0x88,0xFF,0x84,0x00,0x8A,0xFF,0x85,0x00,0x8A,0xFF,0x84,0x00,0x8A,0xFF,0x84,0x00,0x89,0xFF,0x84,0x00,0x83,0xFF,0x8A,0x00,0x8A,0xFF,0x85,0x00,0x8A,0xFF,0xA4,0x00,0x83,0xFF,0x82,0x00,0x84,0xFF,0x85,0x00,0x83,0xFF,0x88,0x00,0x8B,0xFF,0x84,0x00,0x8A,0xFF,0x85,0x00,0x89,0xFF,0x82,0x00,0x85,0x00,0x83,0xFF,0x89,0x00,0x87,0xFF,0x85,0x00,0x89,0xFF,0x87,0x00,0x89,0xFF,0x85,0x00,0x89,0xFF,0x85,0x00,0x87,0xFF,0x85,0x00,0x83,0xFF,0x8B,0x00,0x89,0xFF,0x85,0x00,0x89,0xFF,\n0xA5,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x85,0x00,0x83,0xFF,0x89,0x00,0x83,0xFF,0x81,0x00,0x83,0xFF,0x85,0x00,0x89,0xFF,0x87,0x00,0x87,0xFF,0x83,0x00,0x85,0x00,0x83,0xFF,0x97,0x00,0x83,0xFF,0xA3,0x00,0x83,0xFF,0x94,0x00,0x86,0xFF,0x97,0x00,0x83,0xFF,0x8F,0x00,0x83,0xFF,0x8B,0x00,0x83,0xFF,0x97,0x00,0x85,0xFF,0xB7,0x00,0x85,0x00,0x83,0xFF,0x97,0x00,0x83,0xFF,0xA3,0x00,0x83,0xFF,0x95,0x00,0x85,0xFF,0x97,0x00,0x83,0xFF,0x8F,0x00,0x83,0xFF,0x8B,0x00,0x83,0xFF,0x97,0x00,0x85,0xFF,0xB7,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0x97,0x00,0x85,0xFF,0xFF,0x00,0xE1,0x00,0x96,0x00,0x86,0xFF,0xFF,0x00,0xE1,0x00,0x81,0x00,0x83,0xFF,0x8D,0x00,\n0x87,0xFF,0x85,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x89,0xFF,0x89,0x00,0x83,0xFF,0x89,0x00,0x87,0xFF,0x89,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x81,0x00,0x83,0xFF,0x85,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x87,0x00,0x8B,0xFF,0x85,0x00,0x87,0xFF,0x8F,0x00,0x83,0xFF,0x83,0x00,0x87,0xFF,0x93,0x00,0x8D,0xFF,0x81,0x00,0x81,0x00,0x83,0xFF,0x8C,0x00,0x88,0xFF,0x85,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x8A,0xFF,0x88,0x00,0x83,0xFF,0x88,0x00,0x89,0xFF,0x87,0x00,0x85,0xFF,0x86,0x00,0x89,0xFF,0x85,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x87,0x00,0x8B,0xFF,0x85,0x00,0x87,0xFF,0x8E,0x00,0x84,0xFF,\n0x83,0x00,0x87,0xFF,0x93,0x00,0x8D,0xFF,0x81,0x00,0x81,0x00,0x83,0xFF,0x8B,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x8C,0x00,0x82,0xFF,0x87,0x00,0x83,0xFF,0x87,0x00,0x84,0xFF,0x81,0x00,0x84,0xFF,0x85,0x00,0x87,0xFF,0x85,0x00,0x89,0xFF,0x85,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x8D,0x00,0x83,0xFF,0x91,0x00,0x84,0xFF,0x88,0x00,0x83,0xFF,0xA3,0x00,0x81,0x00,0x83,0xFF,0x8B,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x8C,0x00,0x82,0xFF,0x87,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x84,0x00,0x89,0xFF,0x83,0x00,0x8B,0xFF,\n0x84,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x87,0x00,0x84,0xFF,0x8C,0x00,0x83,0xFF,0x90,0x00,0x84,0xFF,0x89,0x00,0x83,0xFF,0xA3,0x00,0x81,0x00,0x83,0xFF,0x8B,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x8C,0x00,0x82,0xFF,0x87,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x84,0xFF,0x81,0x00,0x84,0xFF,0x81,0x00,0x83,0xFF,0x81,0x00,0x81,0xFF,0x81,0x00,0x83,0xFF,0x84,0x00,0x89,0xFF,0x88,0x00,0x83,0xFF,0x88,0x00,0x84,0xFF,0x8B,0x00,0x83,0xFF,0x8F,0x00,0x84,0xFF,0x8A,0x00,0x83,0xFF,0xA3,0x00,0x81,0x00,0x83,0xFF,0x8B,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,\n0x83,0x00,0x83,0xFF,0x85,0x00,0x88,0xFF,0x88,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x81,0x00,0x83,0xFF,0x81,0x00,0x81,0xFF,0x81,0x00,0x83,0xFF,0x85,0x00,0x87,0xFF,0x89,0x00,0x83,0xFF,0x89,0x00,0x84,0xFF,0x8A,0x00,0x83,0xFF,0x8E,0x00,0x84,0xFF,0x8B,0x00,0x83,0xFF,0xA3,0x00,0x81,0x00,0x89,0xFF,0x85,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x8A,0xFF,0x85,0x00,0x88,0xFF,0x89,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x81,0x00,0x83,0xFF,0x81,0x00,0x81,0xFF,0x81,0x00,0x83,0xFF,0x86,0x00,0x85,0xFF,0x8A,0x00,0x83,0xFF,0x8A,0x00,\n0x84,0xFF,0x89,0x00,0x83,0xFF,0x8D,0x00,0x84,0xFF,0x8C,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x85,0x00,0x83,0xFF,0x91,0x00,0x81,0x00,0x8A,0xFF,0x84,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x8A,0xFF,0x84,0x00,0x83,0xFF,0x8F,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x81,0x00,0x83,0xFF,0x81,0x00,0x81,0xFF,0x81,0x00,0x83,0xFF,0x86,0x00,0x85,0xFF,0x89,0x00,0x85,0xFF,0x8A,0x00,0x84,0xFF,0x88,0x00,0x83,0xFF,0x8C,0x00,0x84,0xFF,0x8D,0x00,0x83,0xFF,0x83,0x00,0x84,0xFF,0x83,0x00,0x84,0xFF,0x91,0x00,0x81,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,\n0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x8F,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x81,0x00,0x83,0xFF,0x81,0x00,0x81,0xFF,0x81,0x00,0x83,0xFF,0x85,0x00,0x87,0xFF,0x87,0x00,0x87,0xFF,0x8A,0x00,0x84,0xFF,0x87,0x00,0x83,0xFF,0x8B,0x00,0x84,0xFF,0x8E,0x00,0x83,0xFF,0x84,0x00,0x84,0xFF,0x81,0x00,0x84,0xFF,0x92,0x00,0x81,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x8F,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x81,0x00,0x83,0xFF,\n0x81,0x00,0x81,0xFF,0x81,0x00,0x83,0xFF,0x84,0x00,0x89,0xFF,0x85,0x00,0x89,0xFF,0x8A,0x00,0x84,0xFF,0x86,0x00,0x83,0xFF,0x8A,0x00,0x84,0xFF,0x8F,0x00,0x83,0xFF,0x85,0x00,0x89,0xFF,0x93,0x00,0x81,0x00,0x8A,0xFF,0x85,0x00,0x89,0xFF,0x84,0x00,0x8A,0xFF,0x85,0x00,0x8A,0xFF,0x83,0x00,0x8B,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x81,0x00,0x83,0xFF,0x81,0x00,0x81,0xFF,0x81,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x84,0xFF,0x81,0x00,0x84,0xFF,0x83,0x00,0x8B,0xFF,0x85,0x00,0x87,0xFF,0x85,0x00,0x84,0xFF,0x8C,0x00,0x87,0xFF,0x86,0x00,0x87,0xFF,0x94,0x00,0x81,0x00,0x89,0xFF,\n0x87,0x00,0x87,0xFF,0x85,0x00,0x89,0xFF,0x87,0x00,0x89,0xFF,0x83,0x00,0x8B,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x81,0x00,0x83,0xFF,0x81,0x00,0x81,0xFF,0x81,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x8B,0xFF,0x85,0x00,0x87,0xFF,0x85,0x00,0x83,0xFF,0x8D,0x00,0x87,0xFF,0x87,0x00,0x85,0xFF,0x95,0x00,0xFF,0x00,0xE4,0x00,0x83,0xFF,0x96,0x00,0xFF,0x00,0xE5,0x00,0x81,0xFF,0x97,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0x83,0x00,0x87,0xFF,0x85,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x89,0xFF,0x87,0x00,0x87,0xFF,0x85,0x00,\n0x89,0xFF,0x85,0x00,0x8B,0xFF,0x83,0x00,0x83,0xFF,0x8B,0x00,0x87,0xFF,0x87,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x8B,0xFF,0x83,0x00,0x85,0xFF,0x89,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x8B,0xFF,0x81,0x00,0x83,0xFF,0x85,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x85,0x00,0x87,0xFF,0x83,0x00,0x82,0x00,0x88,0xFF,0x85,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x8A,0xFF,0x85,0x00,0x89,0xFF,0x84,0x00,0x8A,0xFF,0x84,0x00,0x8B,0xFF,0x83,0x00,0x83,0xFF,0x8A,0x00,0x89,0xFF,0x86,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x8B,0xFF,0x82,0x00,0x87,0xFF,0x88,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x8B,0xFF,\n0x81,0x00,0x83,0xFF,0x85,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x84,0x00,0x89,0xFF,0x82,0x00,0x81,0x00,0x83,0xFF,0x8B,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x8B,0x00,0x83,0xFF,0x89,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x85,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x85,0x00,0x83,0xFF,0x81,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x89,0x00,0x83,0xFF,0x85,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,\n0x83,0xFF,0x81,0x00,0x81,0x00,0x83,0xFF,0x8B,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x8B,0x00,0x83,0xFF,0x89,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x85,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x85,0x00,0x83,0xFF,0x81,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x89,0x00,0x83,0xFF,0x85,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x82,0x00,0x84,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x81,0x00,0x81,0x00,0x83,0xFF,0x81,0x00,0x85,0xFF,0x83,0x00,0x8B,0xFF,0x83,0x00,\n0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x8B,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x8B,0x00,0x83,0xFF,0x89,0x00,0x83,0xFF,0x81,0x00,0x85,0xFF,0x85,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x8B,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x89,0x00,0x83,0xFF,0x81,0x00,0x81,0xFF,0x81,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x81,0x00,0x85,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x81,0x00,0x81,0x00,0x83,0xFF,0x81,0x00,0x85,0xFF,0x83,0x00,0x8B,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x8B,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,\n0x8B,0x00,0x83,0xFF,0x89,0x00,0x83,0xFF,0x81,0x00,0x85,0xFF,0x85,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x8B,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x89,0x00,0x83,0xFF,0x81,0x00,0x81,0xFF,0x81,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x00,0x00,0x86,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x81,0x00,0x81,0x00,0x83,0xFF,0x81,0x00,0x85,0xFF,0x84,0x00,0x82,0xFF,0x81,0x00,0x84,0xFF,0x84,0x00,0x8A,0xFF,0x84,0x00,0x83,0xFF,0x8B,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x87,0xFF,0x87,0x00,0x87,0xFF,0x85,0x00,0x83,0xFF,0x8D,0x00,0x8B,0xFF,0x87,0x00,0x83,0xFF,0x8B,0x00,0x83,0xFF,0x87,0x00,\n0x89,0xFF,0x85,0x00,0x83,0xFF,0x89,0x00,0x8D,0xFF,0x83,0x00,0x8B,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x81,0x00,0x81,0x00,0x83,0xFF,0x81,0x00,0x85,0xFF,0x85,0x00,0x81,0xFF,0x81,0x00,0x83,0xFF,0x85,0x00,0x8A,0xFF,0x84,0x00,0x83,0xFF,0x8B,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x87,0xFF,0x87,0x00,0x87,0xFF,0x85,0x00,0x83,0xFF,0x8D,0x00,0x8B,0xFF,0x87,0x00,0x83,0xFF,0x8B,0x00,0x83,0xFF,0x87,0x00,0x89,0xFF,0x85,0x00,0x83,0xFF,0x89,0x00,0x8D,0xFF,0x83,0x00,0x8B,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x81,0x00,0x81,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x85,0x00,0x87,0xFF,0x85,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,\n0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x8B,0x00,0x83,0xFF,0x89,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x85,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x8B,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x89,0x00,0x8D,0xFF,0x83,0x00,0x86,0xFF,0x00,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x81,0x00,0x81,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x85,0x00,0x87,0xFF,0x85,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x8B,0x00,0x83,0xFF,0x89,0x00,0x83,0xFF,\n0x83,0x00,0x83,0xFF,0x85,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x8B,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x89,0x00,0x85,0xFF,0x81,0x00,0x85,0xFF,0x83,0x00,0x85,0xFF,0x81,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x81,0x00,0x82,0x00,0x89,0xFF,0x87,0x00,0x85,0xFF,0x86,0x00,0x8A,0xFF,0x85,0x00,0x89,0xFF,0x84,0x00,0x8A,0xFF,0x84,0x00,0x8B,0xFF,0x83,0x00,0x8B,0xFF,0x82,0x00,0x89,0xFF,0x86,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x8B,0xFF,0x87,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x89,0x00,0x84,0xFF,0x83,0x00,0x84,0xFF,0x83,0x00,\n0x84,0xFF,0x82,0x00,0x83,0xFF,0x84,0x00,0x89,0xFF,0x82,0x00,0x83,0x00,0x87,0xFF,0x89,0x00,0x83,0xFF,0x87,0x00,0x89,0xFF,0x87,0x00,0x87,0xFF,0x85,0x00,0x89,0xFF,0x85,0x00,0x8B,0xFF,0x83,0x00,0x8B,0xFF,0x83,0x00,0x87,0xFF,0x87,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x8B,0xFF,0x87,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x89,0x00,0x83,0xFF,0x85,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x85,0x00,0x87,0xFF,0x83,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xB3,0x00,0x83,0xFF,0xC7,0x00,0xFF,0x00,0xB3,0x00,0x84,0xFF,0xC6,0x00,0x83,0x00,0x87,0xFF,0x89,0x00,0x83,0xFF,0x87,0x00,\n0x8B,0xFF,0x83,0x00,0x89,0xFF,0x8B,0x00,0x83,0xFF,0x85,0x00,0x89,0xFF,0x87,0x00,0x87,0xFF,0x89,0x00,0x83,0xFF,0x89,0x00,0x87,0xFF,0x87,0x00,0x87,0xFF,0x89,0x00,0x83,0xFF,0x8A,0x00,0x84,0xFF,0x8D,0x00,0x83,0xFF,0x97,0x00,0x83,0xFF,0x8D,0x00,0x83,0xFF,0x85,0x00,0x82,0x00,0x89,0xFF,0x88,0x00,0x83,0xFF,0x87,0x00,0x8B,0xFF,0x83,0x00,0x8A,0xFF,0x8A,0x00,0x83,0xFF,0x85,0x00,0x8A,0xFF,0x85,0x00,0x89,0xFF,0x88,0x00,0x83,0xFF,0x88,0x00,0x89,0xFF,0x86,0x00,0x88,0xFF,0x88,0x00,0x83,0xFF,0x8B,0x00,0x83,0xFF,0x8C,0x00,0x84,0xFF,0x97,0x00,0x84,0xFF,0x8C,0x00,0x83,0xFF,0x85,0x00,0x81,0x00,0x85,0xFF,0x81,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x87,0x00,\n0x83,0xFF,0x93,0x00,0x83,0xFF,0x83,0x00,0x8B,0xFF,0x8B,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x8B,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x8B,0x00,0x83,0xFF,0x8B,0x00,0x84,0xFF,0x86,0x00,0x8B,0xFF,0x86,0x00,0x84,0xFF,0x95,0x00,0x81,0x00,0x86,0xFF,0x00,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x93,0x00,0x83,0xFF,0x83,0x00,0x8B,0xFF,0x8B,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x8B,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x8B,0x00,0x83,0xFF,0x8A,0x00,0x84,0xFF,0x87,0x00,0x8B,0xFF,\n0x87,0x00,0x84,0xFF,0x94,0x00,0x81,0x00,0x83,0xFF,0x00,0x00,0x86,0xFF,0x87,0x00,0x83,0xFF,0x88,0x00,0x88,0xFF,0x89,0x00,0x86,0xFF,0x84,0x00,0x83,0xFF,0x81,0x00,0x83,0xFF,0x85,0x00,0x8A,0xFF,0x84,0x00,0x8A,0xFF,0x88,0x00,0x83,0xFF,0x88,0x00,0x89,0xFF,0x86,0x00,0x89,0xFF,0xA5,0x00,0x84,0xFF,0x9D,0x00,0x84,0xFF,0x89,0x00,0x83,0xFF,0x85,0x00,0x81,0x00,0x83,0xFF,0x81,0x00,0x85,0xFF,0x87,0x00,0x83,0xFF,0x89,0x00,0x88,0xFF,0x88,0x00,0x86,0xFF,0x84,0x00,0x83,0xFF,0x81,0x00,0x83,0xFF,0x85,0x00,0x89,0xFF,0x85,0x00,0x89,0xFF,0x89,0x00,0x84,0xFF,0x87,0x00,0x89,0xFF,0x85,0x00,0x8A,0xFF,0xA5,0x00,0x83,0xFF,0x9F,0x00,0x83,0xFF,0x89,0x00,0x83,0xFF,\n0x85,0x00,0x81,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x8F,0x00,0x83,0xFF,0x8B,0x00,0x83,0xFF,0x84,0x00,0x88,0xFF,0x85,0x00,0x83,0xFF,0x8B,0x00,0x83,0xFF,0x90,0x00,0x84,0xFF,0x85,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0xA5,0x00,0x83,0xFF,0x9F,0x00,0x83,0xFF,0x89,0x00,0x85,0xFF,0x83,0x00,0x81,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x8F,0x00,0x83,0xFF,0x8B,0x00,0x83,0xFF,0x85,0x00,0x87,0xFF,0x85,0x00,0x83,0xFF,0x8B,0x00,0x83,0xFF,0x91,0x00,0x83,0xFF,0x85,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0xA5,0x00,0x84,0xFF,0x9D,0x00,0x84,0xFF,\n0x89,0x00,0x86,0xFF,0x82,0x00,0x82,0x00,0x89,0xFF,0x86,0x00,0x85,0xFF,0x87,0x00,0x8A,0xFF,0x84,0x00,0x8A,0xFF,0x87,0x00,0x86,0xFF,0x85,0x00,0x8B,0xFF,0x84,0x00,0x88,0xFF,0x85,0x00,0x89,0xFF,0x86,0x00,0x89,0xFF,0x85,0x00,0x89,0xFF,0x88,0x00,0x83,0xFF,0x8B,0x00,0x83,0xFF,0x8A,0x00,0x84,0xFF,0x87,0x00,0x8B,0xFF,0x87,0x00,0x84,0xFF,0x8E,0x00,0x83,0xFF,0x81,0x00,0x83,0x00,0x87,0xFF,0x87,0x00,0x85,0xFF,0x87,0x00,0x89,0xFF,0x85,0x00,0x89,0xFF,0x89,0x00,0x85,0xFF,0x85,0x00,0x8B,0xFF,0x85,0x00,0x87,0xFF,0x85,0x00,0x89,0xFF,0x87,0x00,0x87,0xFF,0x87,0x00,0x87,0xFF,0x89,0x00,0x83,0xFF,0x8B,0x00,0x83,0xFF,0x8B,0x00,0x84,0xFF,0x86,0x00,0x8B,0xFF,\n0x86,0x00,0x84,0xFF,0x8F,0x00,0x83,0xFF,0x81,0x00,0xFF,0x00,0xA5,0x00,0x83,0xFF,0x8B,0x00,0x83,0xFF,0x8C,0x00,0x84,0xFF,0x97,0x00,0x84,0xFF,0x88,0x00,0x8A,0xFF,0x82,0x00,0xFF,0x00,0xA5,0x00,0x83,0xFF,0x8B,0x00,0x83,0xFF,0x8D,0x00,0x83,0xFF,0x97,0x00,0x83,0xFF,0x89,0x00,0x89,0xFF,0x83,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xC5,0x00,0x83,0xFF,0xB5,0x00,0xFF,0x00,0xC5,0x00,0x84,0xFF,0xB4,0x00,0x95,0x00,0x83,0xFF,0x97,0x00,0x83,0xFF,0x81,0x00,0x83,0xFF,0x89,0x00,0x81,0xFF,0x87,0x00,0x83,0xFF,0x85,0x00,0x83,0xFF,0x85,0x00,0x85,0xFF,0x81,0x00,0x83,0xFF,0x97,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x89,0x00,0x83,0xFF,0x83,0x00,\n0x83,0xFF,0x87,0x00,0x83,0xFF,0x8C,0x00,0x84,0xFF,0x95,0x00,0x83,0xFF,0x89,0x00,0x83,0xFF,0x8B,0x00,0x95,0x00,0x83,0xFF,0x97,0x00,0x83,0xFF,0x81,0x00,0x83,0xFF,0x85,0x00,0x88,0xFF,0x84,0x00,0x84,0xFF,0x84,0x00,0x83,0xFF,0x84,0x00,0x8C,0xFF,0x96,0x00,0x84,0xFF,0x87,0x00,0x84,0xFF,0x88,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x8D,0x00,0x83,0xFF,0x95,0x00,0x83,0xFF,0x89,0x00,0x84,0xFF,0x8A,0x00,0xB1,0x00,0x83,0xFF,0x81,0x00,0x83,0xFF,0x85,0x00,0x89,0xFF,0x84,0x00,0x84,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x84,0xFF,0x96,0x00,0x84,0xFF,0x89,0x00,0x84,0xFF,0x89,0x00,0x87,0xFF,0x89,0x00,0x83,0xFF,0x8D,0x00,\n0x83,0xFF,0x95,0x00,0x83,0xFF,0x8A,0x00,0x84,0xFF,0x89,0x00,0xB1,0x00,0x83,0xFF,0x81,0x00,0x83,0xFF,0x89,0x00,0x81,0xFF,0x81,0x00,0x81,0xFF,0x85,0x00,0x84,0xFF,0x82,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x96,0x00,0x84,0xFF,0x8B,0x00,0x84,0xFF,0x88,0x00,0x87,0xFF,0x89,0x00,0x83,0xFF,0x8D,0x00,0x83,0xFF,0x95,0x00,0x83,0xFF,0x8B,0x00,0x84,0xFF,0x88,0x00,0x95,0x00,0x83,0xFF,0x95,0x00,0x8D,0xFF,0x87,0x00,0x81,0xFF,0x81,0x00,0x81,0xFF,0x86,0x00,0x84,0xFF,0x89,0x00,0x83,0xFF,0x81,0x00,0x85,0xFF,0x95,0x00,0x84,0xFF,0x8D,0x00,0x84,0xFF,0x83,0x00,0x8F,0xFF,0x81,0x00,0x8B,0xFF,0x93,0x00,0x8B,0xFF,0x94,0x00,0x84,0xFF,0x87,0x00,\n0x95,0x00,0x83,0xFF,0x95,0x00,0x8D,0xFF,0x84,0x00,0x88,0xFF,0x87,0x00,0x84,0xFF,0x88,0x00,0x83,0xFF,0x81,0x00,0x86,0xFF,0x94,0x00,0x83,0xFF,0x8F,0x00,0x83,0xFF,0x83,0x00,0x8F,0xFF,0x81,0x00,0x8B,0xFF,0x93,0x00,0x8B,0xFF,0x95,0x00,0x84,0xFF,0x86,0x00,0x95,0x00,0x83,0xFF,0x97,0x00,0x83,0xFF,0x81,0x00,0x83,0xFF,0x85,0x00,0x88,0xFF,0x89,0x00,0x84,0xFF,0x88,0x00,0x88,0xFF,0x00,0x00,0x82,0xFF,0x87,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0x8F,0x00,0x83,0xFF,0x87,0x00,0x87,0xFF,0x89,0x00,0x83,0xFF,0xBA,0x00,0x84,0xFF,0x85,0x00,0x95,0x00,0x83,0xFF,0x97,0x00,0x83,0xFF,0x81,0x00,0x83,0xFF,0x85,0x00,0x81,0xFF,0x81,0x00,0x81,0xFF,0x8D,0x00,0x84,0xFF,\n0x88,0x00,0x86,0xFF,0x82,0x00,0x81,0xFF,0x87,0x00,0x84,0xFF,0x86,0x00,0x83,0xFF,0x8F,0x00,0x83,0xFF,0x87,0x00,0x87,0xFF,0x89,0x00,0x83,0xFF,0xBB,0x00,0x84,0xFF,0x84,0x00,0x95,0x00,0x83,0xFF,0x89,0x00,0x83,0xFF,0x81,0x00,0x83,0xFF,0x81,0x00,0x8D,0xFF,0x83,0x00,0x81,0xFF,0x81,0x00,0x81,0xFF,0x87,0x00,0x83,0xFF,0x82,0x00,0x84,0xFF,0x88,0x00,0x86,0xFF,0x8C,0x00,0x84,0xFF,0x85,0x00,0x83,0xFF,0x8F,0x00,0x83,0xFF,0x85,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0xBC,0x00,0x84,0xFF,0x83,0x00,0x95,0x00,0x83,0xFF,0x89,0x00,0x83,0xFF,0x81,0x00,0x83,0xFF,0x81,0x00,0x8D,0xFF,0x83,0x00,0x89,0xFF,0x83,0x00,0x83,0xFF,0x83,0x00,0x84,0xFF,\n0x87,0x00,0x87,0xFF,0x8C,0x00,0x83,0xFF,0x85,0x00,0x84,0xFF,0x8D,0x00,0x84,0xFF,0x85,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0xBD,0x00,0x84,0xFF,0x82,0x00,0x95,0x00,0x83,0xFF,0x89,0x00,0x83,0xFF,0x81,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x81,0x00,0x83,0xFF,0x86,0x00,0x88,0xFF,0x83,0x00,0x83,0xFF,0x84,0x00,0x84,0xFF,0x85,0x00,0x83,0xFF,0x81,0x00,0x83,0xFF,0x8B,0x00,0x83,0xFF,0x86,0x00,0x84,0xFF,0x8B,0x00,0x84,0xFF,0xDD,0x00,0x84,0xFF,0x81,0x00,0x95,0x00,0x83,0xFF,0x89,0x00,0x83,0xFF,0x81,0x00,0x83,0xFF,0x83,0x00,0x83,0xFF,0x81,0x00,0x83,0xFF,0x89,0x00,0x81,0xFF,0x87,0x00,0x83,0xFF,0x85,0x00,0x83,0xFF,0x85,0x00,0x83,0xFF,\n0x81,0x00,0x83,0xFF,0x8B,0x00,0x83,0xFF,0x87,0x00,0x84,0xFF,0x89,0x00,0x84,0xFF,0xDF,0x00,0x83,0xFF,0x81,0x00,0xA3,0x00,0x83,0xFF,0x81,0x00,0x83,0xFF,0xB6,0x00,0x87,0xFF,0x99,0x00,0x84,0xFF,0x87,0x00,0x84,0xFF,0xE6,0x00,0xA3,0x00,0x83,0xFF,0x81,0x00,0x83,0xFF,0xB7,0x00,0x85,0xFF,0x9B,0x00,0x83,0xFF,0x87,0x00,0x83,0xFF,0xE7,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,\n0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x54,0x52,0x55,0x45,0x56,0x49,0x53,0x49,0x4F,0x4E,0x2D,0x58,0x46,0x49,0x4C,0x45,0x2E,0x00\n"
  },
  {
    "path": "engine/client/m_download.c",
    "content": "//copyright 'Spike', license gplv2+\n//provides both a package manager and downloads menu.\n//FIXME: block downloads of exe/dll/so/etc if not an https url (even if inside zips). also block such files from package lists over http.\n\n//Note: for a while we didn't have strong hashes, nor signing, so we depended upon known-self-signed tls certificates to prove authenticity\n//we now have sha256 hashes(and sizes) to ensure that the file we wanted hasn't been changed in transit.\n//and we have signature info, to prove that the hash specified was released by a known authority. This means that we should now be able to download such things over http without worries (or at least that we can use an untrustworthy CA that's trusted by insecurity-mafia browsers).\n//WARNING: paks/pk3s may still be installed without signatures, without allowing dlls/exes/etc to be installed.\n//signaturedata+hashes can be generated with 'fteqw -privkey key.priv -pubkey key.pub -certhost MyAuthority -sign pathtofile', but Auth_GetKnownCertificate will need to be updated for any allowed authorities.\n\n#include \"quakedef.h\"\n#include \"shader.h\"\n#include \"netinc.h\"\n\n#define ENABLEPLUGINSBYDEFAULT\t//auto-enable plugins that the user installs. this risks other programs installing dlls (but we can't really protect our listing file so this is probably not any worse in practise).\n\n#ifdef PACKAGEMANAGER\n\t#if !defined(NOBUILTINMENUS) && defined(HAVE_CLIENT)\n\t\t#define DOWNLOADMENU\n\t#endif\n#endif\n\n#ifdef PACKAGEMANAGER\n#include \"fs.h\"\n\n//some extra args for the downloads menu (for the downloads menu to handle engine updates).\n#ifndef SVNREVISION\n#define SVNREVISION -\n#endif\nstatic char enginerevision[256] = STRINGIFY(SVNREVISION);\n\n\n\n#ifdef ENABLEPLUGINSBYDEFAULT\ncvar_t\tpkg_autoupdate = CVARFD(\"pkg_autoupdate\", \"1\", CVAR_NOTFROMSERVER|CVAR_NOSAVE|CVAR_NOSET|CVAR_NORESET, \"Controls autoupdates, can only be changed via the downloads menu.\\n0: off.\\n1: enabled (stable only).\\n2: enabled (unstable).\\nNote that autoupdate will still prompt the user to actually apply the changes.\"); //read from the package list only.\n#else\ncvar_t\tpkg_autoupdate = CVARFD(\"pkg_autoupdate\", \"-1\", CVAR_NOTFROMSERVER|CVAR_NOSAVE|CVAR_NOSET|CVAR_NORESET, \"Controls autoupdates, can only be changed via the downloads menu.\\n0: off.\\n1: enabled (stable only).\\n2: enabled (unstable).\\nNote that autoupdate will still prompt the user to actually apply the changes.\"); //read from the package list only.\n#endif\n\n#define INSTALLEDFILES\t\"installed.lst\"\t//the file that resides in the quakedir (saying what's installed).\n\n//installed native okay [previously manually installed, or has no a qhash]\n//installed cached okay [had a qhash]\n//installed native corrupt [they overwrote it manually]\n//installed cached corrupt [we fucked up, probably]\n//installed native missing (becomes not installed) [deleted]\n//installed cached missing (becomes not installed) [deleted]\n//installed none [meta package with no files]\n\n//!installed native okay [was manually installed, flag as installed now]\n//!installed cached okay [they got it from some other source / previously installed]\n//!installed native corrupt [manually installed conflict]\n//!installed cached corrupt [we fucked up, probably]\n\n//!installed * missing [simply not installed]\n\n#define DPF_ENABLED\t\t\t\t\t(1u<<0)\n#define DPF_NATIVE\t\t\t\t\t(1u<<1)\t//appears to be installed properly\n#define DPF_CACHED\t\t\t\t\t(1u<<2)\t//appears to be installed in their dlcache dir (and has a qhash)\n#define DPF_CORRUPT\t\t\t\t\t(1u<<3)\t//will be deleted before it can be changed\n\n#define DPF_USERMARKED\t\t\t\t(1u<<4)\t//user selected it\n#define DPF_AUTOMARKED\t\t\t\t(1u<<5)\t//selected only to satisfy a dependancy\n#define DPF_MANIMARKED\t\t\t\t(1u<<6)\t//legacy. selected to satisfy packages listed directly in manifests. the filesystem code will load the packages itself, we just need to download (but not enable).\n#define DPF_DISPLAYVERSION\t\t\t(1u<<7)\t//some sort of conflict, the package is listed twice, so show versions so the user knows what's old.\n\n#define DPF_FORGETONUNINSTALL\t\t(1u<<8)\t//for previously installed packages, remove them from the list if there's no current version any more (should really be automatic if there's no known mirrors)\n#define DPF_HIDDEN\t\t\t\t\t(1u<<9)\t//wrong arch, file conflicts, etc. still listed if actually installed.\n#define DPF_PURGE\t\t\t\t\t(1u<<10)\t//package should be completely removed (ie: the dlcache dir too). if its still marked then it should be reinstalled anew. available on cached or corrupt packages, implied by native.\n#define DPF_MANIFEST\t\t\t\t(1u<<11)\t//package was named by the manifest, and should only be uninstalled after a warning.\n\n#define DPF_TESTING\t\t\t\t\t(1u<<12)\t//package is provided on a testing/trial basis, and will only be selected/listed if autoupdates are configured to allow it.\n#define DPF_GUESSED\t\t\t\t\t(1u<<13)\t//package data was guessed from basically just filename+qhash+url. merge aggressively.\n#define DPF_ENGINE\t\t\t\t\t(1u<<14)\t//engine update. replaces old autoupdate mechanism\n#define DPF_PLUGIN\t\t\t\t\t(1u<<15)\t//this is a plugin package, with a dll\n\n#define DPF_TRUSTED\t\t\t\t\t(1u<<16)\t//package was trusted when installed. any pk3s can be flagged appropriately.\n#define DPF_SIGNATUREREJECTED\t\t(1u<<17)\t//signature is bad\n#define DPF_SIGNATUREACCEPTED\t\t(1u<<18)\t//signature is good (required for dll/so/exe files)\n#define DPF_SIGNATUREUNKNOWN\t\t(1u<<19)\t//signature is unknown\n#define DPF_HIDEUNLESSPRESENT\t\t(1u<<20)\t//hidden in the ui, unless present.\n#define DPF_PENDING\t\t\t\t\t(1u<<21)\t//started a download (required due to threading sillyness).\n\n#define DPF_MARKED\t\t\t\t\t(DPF_USERMARKED|DPF_AUTOMARKED)\t//flags that will enable it\n#define DPF_ALLMARKED\t\t\t\t(DPF_USERMARKED|DPF_AUTOMARKED|DPF_MANIMARKED)\t//flags that will download it without necessarily enabling it.\n#define DPF_PRESENT\t\t\t\t\t(DPF_NATIVE|DPF_CACHED)\n#define DPF_DISABLEDINSTALLED\t\t(DPF_ENGINE|DPF_PLUGIN)\t//engines+plugins can be installed without being enabled.\n//pak.lst\n//priories <0\n//pakX\n//manifest packages\n//priority 0-999\n//*.pak\n//priority >=1000\n#define PM_DEFAULTPRIORITY\t\t1000\n\nvoid CL_StartCinematicOrMenu(void);\n\n#if defined(SERVERONLY)\n#\tdefine ENGINE_RENDERER \"sv\"\n#elif defined(GLQUAKE) && (defined(VKQUAKE) || defined(D3DQUAKE) || defined(SWQUAKE))\n#\tdefine ENGINE_RENDERER \"m\"\n#elif defined(GLQUAKE)\n#\tdefine ENGINE_RENDERER \"gl\"\n#elif defined(VKQUAKE)\n#\tdefine ENGINE_RENDERER \"vk\"\n#elif defined(D3DQUAKE)\n#\tdefine ENGINE_RENDERER \"d3d\"\n#else\n#\tdefine ENGINE_RENDERER \"none\"\n#endif\n#if defined(NOCOMPAT)\n#\tdefine ENGINE_CLIENT \"-nc\"\n#elif defined(MINIMAL)\n#\tdefine ENGINE_CLIENT \"-min\"\n#elif defined(CLIENTONLY)\n#\tdefine ENGINE_CLIENT \"-cl\"\n#else\n#\tdefine ENGINE_CLIENT\n#endif\n\n#define THISARCH PLATFORM \"_\" ARCH_CPU_POSTFIX\n#define THISENGINE THISARCH \"-\" DISTRIBUTION \"-\" ENGINE_RENDERER ENGINE_CLIENT\n\n#define MAXMIRRORS 8\ntypedef struct package_s {\n\tchar *name;\n\tchar *category;\t//in path form\n\n\tstruct package_s *alternative;\t//alternative (hidden) forms of this package.\n\n\tchar *mirror[MAXMIRRORS];\t//FIXME: move to two types of dep...\n\tchar gamedir[16];\n\tenum fs_relative fsroot;\t//FS_BINARYPATH or FS_ROOT _ONLY_\n\tchar version[24];\n\tchar *arch;\n\tchar *qhash;\n\tchar *packprefix;\t//extra weirdness to skip embedded gamedirs or force extra maps/ nesting\n\n\tquint64_t filesize;\t//in bytes, as part of verifying the hash.\n\tchar *filesha1;\t\t//this is the hash of the _download_, not the individual files\n\tchar *filesha512;\t//this is the hash of the _download_, not the individual files\n\tchar *signature;\t//signature of the [prefix:]sha512\n\n\tchar *title;\n\tchar *description;\n\tchar *license;\n\tchar *author;\n\tchar *website;\n\tchar *previewimage;\n\tenum\n\t{\n\t\tEXTRACT_COPY,\t//just copy the download over\n\t\tEXTRACT_XZ,\t\t//give the download code a write filter so that it automatically decompresses on the fly\n\t\tEXTRACT_GZ,\t\t//give the download code a write filter so that it automatically decompresses on the fly\n\t\tEXTRACT_EXPLICITZIP,//extract an explicit file list once it completes. kinda sucky.\n\t\tEXTRACT_ZIP,\t//extract stuff once it completes. kinda sucky.\n\t} extract;\n\n\tstruct packagedep_s\n\t{\n\t\tstruct packagedep_s *next;\n\t\tenum\n\t\t{\n\t\t\tDEP_CONFLICT,\t\t//don't install if we have the named package installed.\n\t\t\tDEP_REPLACE,\t\t//obsoletes the specified package (or just acts as a conflict marker for now).\n\t\t\tDEP_FILECONFLICT,\t//don't install if this file already exists.\n\t\t\tDEP_REQUIRE,\t\t//don't install unless we have the named package installed.\n\t\t\tDEP_RECOMMEND,\t\t//like depend, but uninstalling will not bubble.\n\t\t\tDEP_SUGGEST,\t\t//like recommend, but will not force install (ie: only prevents auto-uninstall)\n\t\t\tDEP_NEEDFEATURE,\t//requires a specific feature to be available (typically controlled via a cvar)\n//\t\t\tDEP_MIRROR,\n//\t\t\tDEP_FAILEDMIRROR,\n\t\t\tDEP_MAP,\t\t\t//This package contains this map. woo.\n\n\t\t\tDEP_SOURCE,\t\t\t//which source url we found this package from\n\t\t\tDEP_EXTRACTNAME,\t//a file that will be installed\n\t\t\tDEP_FILE,\t\t\t//a file that will be installed. will be loaded as a package, where appropriate.\n\t\t\tDEP_CACHEFILE\t\t//an installed file that's relative to the downloads/ subdir\n\t\t} dtype;\n\t\tchar name[1];\n\t} *deps;\n\n#ifdef WEBCLIENT\n\tstruct dl_download *curdownload;\n\tunsigned int trymirrors;\n#endif\n\n\tint flags;\n\tint priority;\n\tstruct package_s **link;\n\tstruct package_s *next;\n} package_t;\n\nstatic qboolean loadedinstalled;\nstatic package_t *availablepackages;\nstatic int numpackages;\nstatic char *declinedpackages;\t//metapackage named by the manicfest.\n\n#ifdef WEBCLIENT\nstatic struct\n{\n\tchar *package;\t//package to load. don't forget its dependancies too.\n\tchar *map;\t\t//the map to load.\n} pm_onload;\n\nstatic int allowphonehome = -1;\t//if autoupdates are disabled, make sure we get (temporary) permission before phoning home for available updates. (-1=unknown, 0=no, 1=yes)\nstatic int doautoupdate;\t//updates will be marked (but not applied without the user's actions)\nstatic int pm_pendingprompts; //number of prompts that are pending. don't show apply prompts until these are all cleared.\nstatic qboolean pkg_updating;\t//when flagged, further changes are blocked until completion.\n#else\nstatic const qboolean pkg_updating = false;\n#endif\nstatic qboolean pm_packagesinstalled;\n\n//FIXME: these are allocated for the life of the exe. changing basedir should purge the list.\nstatic size_t pm_numsources = 0;\nstatic struct pm_source_s\n{\n\tchar *url;\t\t\t\t\t//url to query. unique.\n\tchar *prefix;\t\t\t\t//category prefix for packages from this source.\n\tenum\n\t{\n\t\tSRCSTAT_UNTRIED,\t\t//not tried to connect at all.\n\t\tSRCSTAT_FAILED_DNS,\t\t//tried but failed, unresolvable.\n\t\tSRCSTAT_FAILED_NORESP,\t//tried but failed, no response.\n\t\tSRCSTAT_FAILED_REFUSED,\t//tried but failed, refused (no precess).\n\t\tSRCSTAT_FAILED_EOF,\t\t//tried but failed, abrupt termination.\n\t\tSRCSTAT_FAILED_MITM,\t//tried but failed. misc cert problems.\n\t\tSRCSTAT_FAILED_HTTP,\t//tried but failed, misc http failure.\n\t\tSRCSTAT_PENDING,\t\t//waiting for response (or queued). don't show package list yet.\n\t\tSRCSTAT_OBTAINED,\t\t//we got a response.\n\t} status;\n\t#define SRCFL_HISTORIC\t(1u<<0)\t//aka hidden. replaced by the others... used for its enablement. must be parsed first so its enabled-state wins.\n\t#define SRCFL_NESTED\t(1u<<1)\t//discovered from a different source. always disabled.\n\t#define SRCFL_MANIFEST\t(1u<<2)\t//not saved. often default to enabled.\n\t#define SRCFL_USER\t\t(1u<<3)\t//user explicitly added it. included into installed.lst. enabled (if trusted).\n\t#define SRCFL_PLUGIN\t(1u<<4)\t//user explicitly added it. included into installed.lst. enabled (if trusted).\n\t#define SRCFLMASK_FROM\t(SRCFL_HISTORIC|SRCFL_NESTED|SRCFL_MANIFEST|SRCFL_USER|SRCFL_PLUGIN) //mask of flags, forming priority for replacements.\n\t#define SRCFL_DISABLED\t(1u<<5)\t//source was explicitly disabled.\n\t#define SRCFL_ENABLED\t(1u<<6)\t//source was explicitly enabled.\n\t#define SRCFL_PROMPTED\t(1u<<7)\t//source was explicitly enabled.\n\t#define SRCFL_ONCE\t\t(1u<<8)\t//autoupdates are disabled, but the user is viewing packages anyway. enabled via a prompt.\n\t#define SRCFL_UNSAFE\t(1u<<9)\t//ignore signing requirements.\n\tunsigned int flags;\n\tstruct dl_download *curdl;\t//the download context\n\n\tvoid *module;\t//plugins\n\tplugupdatesourcefuncs_t *funcs;\n} *pm_source/*[pm_maxsources]*/;\nstatic int pm_sequence;\t//bumped any time any package is purged\n\nstatic void PM_WriteInstalledPackages(void);\nstatic void PM_PreparePackageList(void);\nstatic void PM_UpdatePackageList(qboolean autoupdate);\nstatic void PM_PromptApplyChanges(void);\nqboolean PM_AreSourcesNew(qboolean doprompt); //prompts to enable sources, or just queries(to do the flashing thing)\n#ifdef DOWNLOADMENU\nstatic qboolean PM_DeclinedPackages(char *out, size_t outsize);\n#endif\n#ifdef WEBCLIENT\nstatic qboolean PM_SignatureOkay(package_t *p);\n#endif\n\nstatic void PM_FreePackage(package_t *p)\n{\n\tstruct packagedep_s *d;\n#ifdef WEBCLIENT\n\tint i;\n#endif\n\n\tCOM_AssertMainThread(\"PM_FreePackage\");\n\n\tif (p->link)\n\t{\n\t\tif (p->alternative)\n\t\t{\t//replace it with its alternative package\n\t\t\t*p->link = p->alternative;\n\t\t\tp->alternative->alternative = p->alternative->next;\n\t\t\tif (p->alternative->alternative)\n\t\t\t\tp->alternative->alternative->link = &p->alternative->alternative;\n\t\t\tp->alternative->next = p->next;\n\t\t\tp->alternative->link = p->link;\n\t\t}\n\t\telse\n\t\t{\t//just remove it from the list.\n\t\t\t*p->link = p->next;\n\t\t\tif (p->next)\n\t\t\t\tp->next->link = p->link;\n\t\t}\n\t}\n\n#ifdef WEBCLIENT\n\tif (p->curdownload)\n\t{\t//if its currently downloading, cancel it.\n\t\tDL_Close(p->curdownload);\n\t\tp->curdownload = NULL;\n\t}\n\n\tfor (i = 0; i < countof(p->mirror); i++)\n\t\tZ_Free(p->mirror[i]);\n#endif\n\n\t//free its data.\n\twhile(p->deps)\n\t{\n\t\td = p->deps;\n\t\tp->deps = d->next;\n\t\tZ_Free(d);\n\t}\n\n\tZ_Free(p->name);\n\tZ_Free(p->category);\n\tZ_Free(p->title);\n\tZ_Free(p->description);\n\tZ_Free(p->author);\n\tZ_Free(p->website);\n\tZ_Free(p->license);\n\tZ_Free(p->previewimage);\n\tZ_Free(p->qhash);\n\tZ_Free(p->arch);\n\tZ_Free(p->packprefix);\n\tZ_Free(p->filesha1);\n\tZ_Free(p->filesha512);\n\tZ_Free(p->signature);\n\tZ_Free(p);\n}\n\nstatic void PM_AddDep(package_t *p, int deptype, const char *depname)\n{\n\tstruct packagedep_s *nd, **link;\n\n\t//no dupes.\n\tfor (link = &p->deps; (nd=*link) ; link = &nd->next)\n\t{\n\t\tif (nd->dtype == deptype && !strcmp(nd->name, depname))\n\t\t\treturn;\n\t}\n\n\t//add it on the end, preserving order.\n\tnd = Z_Malloc(sizeof(*nd) + strlen(depname));\n\tnd->dtype = deptype;\n\tstrcpy(nd->name, depname);\n\tnd->next = *link;\n\t*link = nd;\n}\nstatic const char *PM_GetDepSingle(package_t *p, int deptype)\n{\n\tstruct packagedep_s *d;\n\tconst char *val = NULL;\n\n\t//no dupes.\n\tfor (d = p->deps; d ; d = d->next)\n\t{\n\t\tif (d->dtype == deptype)\n\t\t{\n\t\t\tif (val)\n\t\t\t\treturn NULL;\t//found a second. give up in confusion.\n\t\t\telse\n\t\t\t\tval = d->name;\t//found the first, but continue to make sure there's no second.\n\t\t}\n\t}\n\treturn val;\n}\nstatic qboolean PM_HasDep(package_t *p, int deptype, const char *depname)\n{\n\tstruct packagedep_s *d;\n\n\t//no dupes.\n\tfor (d = p->deps; d ; d = d->next)\n\t{\n\t\tif (d->dtype == deptype && (!depname || !strcmp(d->name, depname)))\n\t\t\treturn true;\n\t}\n\treturn false;\n}\n\nstatic qboolean PM_PurgeOnDisable(package_t *p)\n{\n\t//corrupt packages must be purged\n\tif (p->flags & DPF_CORRUPT)\n\t\treturn true;\n\t//certain updates can be present and not enabled\n\tif (p->flags & DPF_DISABLEDINSTALLED)\n\t\treturn false;\n\t//hashed packages can also be present and not enabled, but only if they're in the cache and not native\n\tif (*p->gamedir && (p->flags & DPF_CACHED))\n\t\treturn false;\n\t//all other packages must be deleted to disable them\n\treturn true;\n}\n\nstatic void PM_ValidateAuthenticity(package_t *p, enum hashvalidation_e validated)\n{\n\tqbyte hashdata[512+MAX_QPATH];\n\tsize_t hashsize = 0;\n\tqbyte signdata[1024];\n\tsize_t signsize = 0;\n\tint r, i;\n\tchar authority[MAX_QPATH], *sig;\n\n\tconst qbyte *pubkey;\n\tsize_t pubkeysize;\n\n#ifndef _DEBUG\n#pragma message(\"Temporary code.\")\n\t//this is temporary code and should be removed once everything else has been fixed.\n\t//ignore the signature (flag as accepted) for any packages with all mirrors on our own update site.\n\t//we can get away with this because we enforce a known certificate for the download.\n\tif (!COM_CheckParm(\"-notlstrust\") && p->mirror[0])\n\t{\n\t\tconchar_t musite[256], *e;\n\t\tchar site[256];\n\t\tchar *oldprefix = \"http://fte.\";\n\t\tchar *newprefix = \"https://updates.\";\n\t\tint m;\n\t\te = COM_ParseFunString(CON_WHITEMASK, ENGINEWEBSITE, musite, sizeof(musite), false);\n\t\tCOM_DeFunString(musite, e, site, sizeof(site)-1, true, true);\n\t\tif (!strncmp(site, oldprefix, strlen(oldprefix)))\n\t\t{\n\t\t\tmemmove(site+strlen(newprefix), site+strlen(oldprefix), strlen(site)-strlen(oldprefix)+1);\n\t\t\tmemcpy(site, newprefix, strlen(newprefix));\n\t\t}\n\t\tQ_strncatz(site, \"/\", sizeof(site));\n\t\tfor (m = 0; m < countof(p->mirror); m++)\n\t\t{\n\t\t\tif (p->mirror[m] && strncmp(p->mirror[m], site, strlen(site)))\n\t\t\t\tbreak;\t//some other host\n\t\t}\n\t\tif (m == countof(p->mirror))\n\t\t{\n\t\t\tp->flags |= DPF_SIGNATUREACCEPTED;\n\t\t\treturn;\n\t\t}\n\t}\n#endif\n\n\t*authority = 0;\n\tif (!p->signature)\n\t\tr = VH_AUTHORITY_UNKNOWN;\n\telse if (!p->filesha512)\n\t\tr = VH_INCORRECT;\n\telse\n\t{\n\t\tsig = strchr(p->signature, ':');\n\t\tif (sig && sig-p->signature<countof(authority)-1)\n\t\t{\n\t\t\tmemcpy(authority, p->signature, sig-p->signature);\n\t\t\tauthority[sig-p->signature] = 0;\n\t\t\tsig++;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tstrcpy(authority, \"Spike\");\t//legacy bollocks\n\t\t\tsig = p->signature;\n\t\t}\n\n\t\t//to validate it, we need a single blob that composits all the parts that might need to be validated. mostly just prefix and file hash.\n\t\thashsize = Base16_DecodeBlock(p->filesha512, hashdata, sizeof(hashdata));\n\n\t\tif (p->packprefix && *p->packprefix)\n\t\t{\t//need to a bit of extra hashing when we have extra data, to get it to fit.\n\t\t\thashfunc_t *h = &hash_sha2_512;\n\t\t\tvoid *ctx = alloca(h->contextsize);\n\t\t\th->init(ctx);\n\t\t\th->process(ctx, p->packprefix, strlen(p->packprefix));\n\t\t\th->process(ctx, \"\\0\", 1);\n\t\t\th->process(ctx, hashdata, hashsize);\n\t\t\th->terminate(hashdata, ctx);\n\t\t\thashsize = h->digestsize;\n\t\t}\n\n\t\t//and the proof...\n\t\tsignsize = Base64_DecodeBlock(sig, NULL, signdata, sizeof(signdata));\n\t\tr = VH_UNSUPPORTED;//preliminary\n\t}\n\n\tpubkey = Auth_GetKnownCertificate(authority, &pubkeysize);\n\tif (!pubkey)\n\t\tr = VH_AUTHORITY_UNKNOWN;\n\n\t//try and get one of our providers to verify it...\n\tfor (i = 0; r==VH_UNSUPPORTED && i < cryptolib_count; i++)\n\t{\n\t\tif (cryptolib[i] && cryptolib[i]->VerifyHash)\n\t\t\tr = cryptolib[i]->VerifyHash(hashdata, hashsize, pubkey, pubkeysize, signdata, signsize);\n\t}\n\n\tp->flags &= ~(DPF_SIGNATUREACCEPTED|DPF_SIGNATUREREJECTED|DPF_SIGNATUREUNKNOWN);\n\tif (r == VH_CORRECT)\n\t\tp->flags |= DPF_SIGNATUREACCEPTED;\n\telse if (r == VH_INCORRECT)\n\t{\n\t\tCon_Printf(\"Signature verification failed\\n\");\n\t\tp->flags |= DPF_SIGNATUREREJECTED;\n\t}\n\telse if (validated == VH_CORRECT && p->filesize && (p->filesha1||p->filesha512))\n\t\tp->flags |= DPF_SIGNATUREACCEPTED;\t//parent validation was okay, expand that to individual packages too.\n\telse if (p->signature)\n\t\tp->flags |= DPF_SIGNATUREUNKNOWN;\n}\n\nstatic qboolean PM_TryGenCachedName(const char *pname, package_t *p, char *local, int llen)\n{\n\tif (!*p->gamedir)\n\t\treturn false;\n\tif (!p->qhash)\n\t{\t//we'll throw paks+pk3s into the cache dir even without a qhash\n\t\tconst char *ext = COM_GetFileExtension(pname, NULL);\n\t\tif (strcmp(ext, \".pak\") && strcmp(ext, \".pk3\"))\n\t\t\treturn false;\n\t}\n\treturn FS_GenCachedPakName(pname, p->qhash, local, llen);\n}\n\n//checks the status of each package\nstatic void PM_ValidatePackage(package_t *p)\n{\n\tpackage_t *o;\n\tstruct packagedep_s *dep;\n\tvfsfile_t *pf;\n\tchar temp[MAX_OSPATH];\n\tp->flags &=~ (DPF_NATIVE|DPF_CACHED|DPF_CORRUPT|DPF_HIDEUNLESSPRESENT);\n\tif (p->flags & DPF_ENABLED)\n\t{\n\t\tfor (dep = p->deps; dep; dep = dep->next)\n\t\t{\n\t\t\tchar *n;\n\t\t\tif (dep->dtype == DEP_CACHEFILE)\n\t\t\t{\n//\t\t\t\tp->flags |= DPF_HIDEUNLESSPRESENT;\n\t\t\t\tn = va(\"downloads/%s\", dep->name);\n\t\t\t\tpf = FS_OpenVFS(n, \"rb\", p->fsroot);\n\t\t\t\tif (pf)\n\t\t\t\t{\n\t\t\t\t\tVFS_CLOSE(pf);\n\t\t\t\t\tp->flags |= DPF_CACHED;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (dep->dtype == DEP_FILE)\n\t\t\t{\n\t\t\t\tif (*p->gamedir)\n\t\t\t\t\tn = va(\"%s/%s\", p->gamedir, dep->name);\n\t\t\t\telse\n\t\t\t\t\tn = dep->name;\n\t\t\t\tpf = FS_OpenVFS(n, \"rb\", p->fsroot);\n\t\t\t\tif (pf)\n\t\t\t\t{\n\t\t\t\t\tVFS_CLOSE(pf);\n\t\t\t\t\tp->flags |= DPF_NATIVE;\n\t\t\t\t}\n\t\t\t\telse if (PM_TryGenCachedName(n, p, temp, sizeof(temp)))\n\t\t\t\t{\n\t\t\t\t\tpf = FS_OpenVFS(temp, \"rb\", p->fsroot);\n\t\t\t\t\tif (pf)\n\t\t\t\t\t{\n\t\t\t\t\t\tVFS_CLOSE(pf);\n\t\t\t\t\t\tp->flags |= DPF_CACHED;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t\tcontinue;\n\t\t\tif (!(p->flags & (DPF_NATIVE|DPF_CACHED)))\n\t\t\t\tCon_Printf(\"WARNING: (%i) %s (%s) no longer exists\\n\", dep->dtype, p->name, n);\n\t\t}\n\n\t\t//if no files were present, unmark it.\n\t\tif (!(p->flags & (DPF_NATIVE|DPF_CACHED)))\n\t\t\tp->flags &= ~DPF_ENABLED;\n\t}\n\telse\n\t{\n\t\tfor (dep = p->deps; dep; dep = dep->next)\n\t\t{\n\t\t\tchar *n;\n\t\t\tstruct packagedep_s *odep;\n\t\t\tunsigned int fl = DPF_NATIVE;\n\t\t\tif (dep->dtype == DEP_FILE)\n\t\t\t{\n\t\t\t\tif (*p->gamedir)\n\t\t\t\t\tn = va(\"%s/%s\", p->gamedir, dep->name);\n\t\t\t\telse\n\t\t\t\t\tn = dep->name;\n\t\t\t\tpf = FS_OpenVFS(n, \"rb\", p->fsroot);\n\t\t\t\tif (!pf && PM_TryGenCachedName(n, p, temp, sizeof(temp)))\n\t\t\t\t{\n\t\t\t\t\tpf = FS_OpenVFS(temp, \"rb\", p->fsroot);\n\t\t\t\t\tfl = DPF_CACHED;\n\t\t\t\t\t//fixme: skip any archive checks\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (dep->dtype == DEP_CACHEFILE)\n\t\t\t{\n//\t\t\t\tp->flags |= DPF_HIDEUNLESSPRESENT;\n\t\t\t\tfl = DPF_CACHED;\n\t\t\t\tn = va(\"downloads/%s\", dep->name);\n\t\t\t\tpf = FS_OpenVFS(n, \"rb\", p->fsroot);\n\t\t\t}\n\t\t\telse\n\t\t\t\tcontinue;\n\n\t\t\tif (pf)\n\t\t\t{\n\t\t\t\tfor (o = availablepackages; o; o = o->next)\n\t\t\t\t{\n\t\t\t\t\tif (o == p)\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\tif (o->flags & DPF_ENABLED)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (!strcmp(p->gamedir, o->gamedir) && p->fsroot == o->fsroot)\n\t\t\t\t\t\t\tif (strcmp(p->name, o->name) || strcmp(p->version, o->version))\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tfor (odep = o->deps; odep; odep = odep->next)\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tif (!strcmp(dep->name, odep->name))\n\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tif (odep)\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif ((o && o->qhash && p->qhash && (o->flags & DPF_CACHED)) || fl == DPF_CACHED)\n\t\t\t\t\tp->flags |= DPF_CACHED;\n\t\t\t\telse if (!o)\n\t\t\t\t{\n\t\t\t\t\tif (!PM_PurgeOnDisable(p) || !strcmp(p->gamedir, \"downloads\"))\n\t\t\t\t\t{\n\t\t\t\t\t\tp->flags |= fl;\n\t\t\t\t\t\tVFS_CLOSE(pf);\n\t\t\t\t\t}\n\t\t\t\t\telse if (p->qhash)\n\t\t\t\t\t{\n\t\t\t\t\t\tsearchpathfuncs_t *archive = FS_OpenPackByExtension(pf, NULL, n, n, p->packprefix);\n\n\t\t\t\t\t\tif (archive)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tunsigned int fqhash;\n\t\t\t\t\t\t\tpf = NULL;\n\t\t\t\t\t\t\tfqhash = archive->GeneratePureCRC(archive, NULL);\n\t\t\t\t\t\t\tarchive->ClosePath(archive);\n\n\t\t\t\t\t\t\tif (fqhash == (unsigned int)strtoul(p->qhash, NULL, 0))\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tp->flags |= fl;\n\t\t\t\t\t\t\t\tif (fl&DPF_NATIVE)\n\t\t\t\t\t\t\t\t\tp->flags |= DPF_MARKED|DPF_ENABLED;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\tpf = NULL;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tVFS_CLOSE(pf);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tp->flags |= DPF_CORRUPT|fl;\n\t\t\t\t\t\tVFS_CLOSE(pf);\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tVFS_CLOSE(pf);\n\t\t\t}\n\t\t}\n\t}\n}\n\nstatic qboolean PM_MergePackage(package_t *oldp, package_t *newp)\n{\n\t//we don't track mirrors for previously-installed packages.\n\t//use the file list of the installed package, zips ignore the file list of the remote package but otherwise they must match to be mergeable\n\t//local installed copies of the package may lack some information, like mirrors.\n\t//the old package *might* be installed, the new won't be. this means we need to use the old's file list rather than the new\n\tif (!oldp->qhash || !strcmp(oldp->qhash?oldp->qhash:\"\", newp->qhash?newp->qhash:\"\"))\n\t{\n\t\tunsigned int om, nm;\n\t\tstruct packagedep_s *od, *nd;\n\t\tqboolean ignorefiles;\n\t\tfor (om = 0; om < countof(oldp->mirror) && oldp->mirror[om]; om++)\n\t\t\t;\n\t\tfor (nm = 0; nm < countof(newp->mirror) && newp->mirror[nm]; nm++)\n\t\t\t;\n//\t\tif (oldp->priority != newp->priority)\n//\t\t\treturn false;\n\n\t\tignorefiles = (oldp->extract==EXTRACT_ZIP);\t//zips ignore the remote file list, its only important if its already installed (so just keep the old file list and its fine).\n\t\tif (oldp->extract != newp->extract)\n\t\t{\t//if both have mirrors of different types then we have some sort of conflict\n\t\t\tif (ignorefiles || (om && nm))\n\t\t\t\treturn false;\n\t\t}\n\n\t\t//packages are merged according to how it should look when its actually installed.\n\t\t//this means we might be merging two packages from different sources with different installation methods/files/etc...\n\t\tif ((newp->signature && oldp->signature && strcmp(newp->signature, oldp->signature)) ||\n\t\t\t(newp->filesha512 && oldp->filesha512 && strcmp(newp->filesha512, oldp->filesha512)) ||\n\t\t\t(newp->filesha1 && oldp->filesha1 && strcmp(newp->filesha1, oldp->filesha1)))\n\t\t\treturn false;\n\n\t\tfor (od = oldp->deps, nd = newp->deps; od && nd; )\n\t\t{\n\t\t\t//if its a zip then the 'remote' file list will be blank while the local list is not (we can just keep the local list).\n\t\t\t//if the file list DOES change, then bump the version.\n\t\t\tif ((od->dtype == DEP_FILE && ignorefiles) || od->dtype == DEP_SOURCE)\n\t\t\t{\n\t\t\t\tod = od->next;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif ((nd->dtype == DEP_FILE && ignorefiles) || nd->dtype == DEP_SOURCE)\n\t\t\t{\n\t\t\t\tnd = nd->next;\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (od->dtype != nd->dtype)\n\t\t\t\treturn false;\t//deps don't match\n\t\t\tif (strcmp(od->name, nd->name))\n\t\t\t\treturn false;\n\t\t\tod = od->next;\n\t\t\tnd = nd->next;\n\t\t}\n\n\t\t//overwrite these. use the 'new' / remote values for each of them\n\t\t//the versions of the two packages will be the same, so the texts should be the same. still favour the new one so that things can be corrected serverside without needing people to redownload everything.\n\t\tif (newp->qhash){Z_Free(oldp->qhash); oldp->qhash = Z_StrDup(newp->qhash);}\n\t\tif (newp->description){Z_Free(oldp->description); oldp->description = Z_StrDup(newp->description);}\n\t\tif (newp->license){Z_Free(oldp->license); oldp->license = Z_StrDup(newp->license);}\n\t\tif (newp->author){Z_Free(oldp->author); oldp->author = Z_StrDup(newp->author);}\n\t\tif (newp->website){Z_Free(oldp->website); oldp->website = Z_StrDup(newp->website);}\n\t\tif (newp->previewimage){Z_Free(oldp->previewimage); oldp->previewimage = Z_StrDup(newp->previewimage);}\n\n\t\t//use the new package's auth settings. this affects downloading rather than reactivation.\n\t\tif (newp->signature || newp->filesha1 || newp->filesha512)\n\t\t{\n\t\t\tZ_Free(oldp->signature); oldp->signature = newp->signature?Z_StrDup(newp->signature):NULL;\n\t\t\tZ_Free(oldp->filesha1); oldp->filesha1 = newp->filesha1?Z_StrDup(newp->filesha1):NULL;\n\t\t\tZ_Free(oldp->filesha512); oldp->filesha512 = newp->filesha512?Z_StrDup(newp->filesha512):NULL;\n\t\t\toldp->filesize = newp->filesize;\n\t\t\toldp->flags &=            ~(DPF_SIGNATUREACCEPTED|DPF_SIGNATUREREJECTED|DPF_SIGNATUREUNKNOWN);\n\t\t\toldp->flags |= newp->flags&(DPF_SIGNATUREACCEPTED|DPF_SIGNATUREREJECTED|DPF_SIGNATUREUNKNOWN);\n\t\t}\n\t\telse\n\t\t\toldp->flags &= ~DPF_SIGNATUREACCEPTED;\n\n\t\toldp->priority = newp->priority;\n\n\t\tif (nm)\n\t\t{\t//copy over the mirrors\n\t\t\toldp->extract = newp->extract;\n\t\t\tfor (; nm --> 0 && om < countof(oldp->mirror); )\n\t\t\t{\n\t\t\t\t//skip it if this mirror was already known.\n\t\t\t\tunsigned int u;\n\t\t\t\tfor (u = 0; u < om; u++)\n\t\t\t\t{\n\t\t\t\t\tif (!strcmp(oldp->mirror[u], newp->mirror[nm]))\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tif (u < om)\n\t\t\t\t\tcontinue;\n\n\t\t\t\t//new mirror! copy it over\n\t\t\t\toldp->mirror[om++] = newp->mirror[nm];\n\t\t\t\tnewp->mirror[nm] = NULL;\n\t\t\t}\n\t\t}\n\t\t//these flags should only remain set if set in both.\n\t\toldp->flags &= ~(DPF_FORGETONUNINSTALL|DPF_TESTING|DPF_MANIFEST|DPF_GUESSED) | (newp->flags & (DPF_FORGETONUNINSTALL|DPF_TESTING|DPF_MANIFEST|DPF_GUESSED));\n\n\t\tfor (nd = newp->deps; nd ; nd = nd->next)\n\t\t{\n\t\t\tif (nd->dtype == DEP_SOURCE)\n\t\t\t{\n\t\t\t\tif (!PM_HasDep(oldp, DEP_SOURCE, nd->name))\n\t\t\t\t\tPM_AddDep(oldp, DEP_SOURCE, nd->name);\n\t\t\t}\n\t\t}\n\n\t\tPM_FreePackage(newp);\n\t\treturn true;\n\t}\n\treturn false;\n}\n\nstatic int QDECL PM_PackageSortOrdering(const void *l, const void *r)\n{\t//for qsort.\n\tconst package_t *a=*(package_t*const*)l, *b=*(package_t*const*)r;\n\tconst char *ac, *bc;\n\tint order;\n\n\t//sort by categories\n\tac = a->category?a->category:\"\";\n\tbc = b->category?b->category:\"\";\n\torder = Q_strcasecmp(ac,bc);\n\tif (order)\n\t\treturn order;\n\n\t//otherwise sort by title.\n\tac = a->title?a->title:a->name;\n\tbc = b->title?b->title:b->name;\n\torder = Q_strcasecmp(ac,bc);\n\treturn order;\n}\nstatic void PM_ResortPackages(void)\n{\n\tint i, count;\n\tpackage_t **sorted;\n\tpackage_t *p;\n\tfor (count = 0, p = availablepackages; p; p=p->next)\n\t\tcount++;\n\tif (!count)\n\t\treturn;\n\tsorted = Z_Malloc(sizeof(*sorted)*count);\n\tfor (count = 0, p = availablepackages; p; p=p->next)\n\t\tsorted[count++] = p;\n\tqsort(sorted, count, sizeof(*sorted), PM_PackageSortOrdering);\n\tavailablepackages = NULL;\n\tfor (i = count; i --> 0; )\n\t{\n\t\tsorted[i]->next = availablepackages;\n\t\tsorted[i]->link = &availablepackages;\n\n\t\tif (availablepackages)\n\t\t\tavailablepackages->link = &sorted[i]->next;\n\t\tavailablepackages = sorted[i];\n\t}\n\tZ_Free(sorted);\n}\n\nstatic package_t *PM_InsertPackage(package_t *p)\n{\n\tpackage_t **link;\n\tint v;\n\n\tfor (link = &availablepackages; *link; )\n\t{\n\t\tpackage_t *prev = *link;\n\t\tif (((prev->flags|p->flags) & DPF_GUESSED) && prev->extract == p->extract && !strcmp(prev->gamedir, p->gamedir) && prev->fsroot == p->fsroot && !strcmp(prev->qhash?prev->qhash:\"\", p->qhash?p->qhash:\"\"))\n\t\t{\t//if one of the packages was guessed then match according to the qhash and file names.\n\t\t\tstruct packagedep_s\t*a = prev->deps, *b = p->deps;\n\t\t\tqboolean differs = false;\n\t\t\tfor (;;)\n\t\t\t{\n\t\t\t\twhile (a && a->dtype != DEP_FILE)\n\t\t\t\t\ta = a->next;\n\t\t\t\twhile (b && b->dtype != DEP_FILE)\n\t\t\t\t\tb = b->next;\n\t\t\t\tif (!a && !b)\n\t\t\t\t\tbreak;\n\t\t\t\tif (a && b && !strcmp(a->name, b->name))\n\t\t\t\t{\n\t\t\t\t\ta = a->next;\n\t\t\t\t\tb = b->next;\n\t\t\t\t\tcontinue;\t//matches...\n\t\t\t\t}\n\t\t\t\tdiffers = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (differs)\n\t\t\t{\n\t\t\t\tlink = &(*link)->next;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (((p->flags & DPF_GUESSED) && (prev->flags & (DPF_USERMARKED|DPF_MANIMARKED|DPF_PENDING))) || prev->curdownload)\n\t\t\t\t{\t//the new one was also guessed. just return the existing package instead.\n\t\t\t\t\tprev->flags |= p->flags&(DPF_PRESENT|DPF_ALLMARKED|DPF_ENABLED);\n\t\t\t\t\tPM_FreePackage(p);\n\t\t\t\t\treturn prev;\n\t\t\t\t}\n\t\t\t\tPM_FreePackage(prev);\n\t\t\t\tlink = &availablepackages;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\t\tv = PM_PackageSortOrdering(&prev, &p);\n\t\tif (v > 0)\n\t\t\tbreak;\t//insert before this one\n\t\telse if (v == 0)\n\t\t{\t//name matches.\n\t\t\t//if (!strcmp(p->fullname),prev->fullname)\n\t\t\tif (!strcmp(p->version, prev->version) && !strcmp(prev->qhash?prev->qhash:\"\", p->qhash?p->qhash:\"\"))\n\t\t\tif (!strcmp(p->gamedir, prev->gamedir))\n\t\t\tif (!strcmp(p->arch?p->arch:\"\", prev->arch?prev->arch:\"\"))\n\t\t\t{ /*package matches, merge them somehow, don't add*/\n\t\t\t\tpackage_t *a;\n\t\t\t\tif (PM_MergePackage(prev, p))\n\t\t\t\t\treturn prev;\n\t\t\t\tfor (a = p->alternative; a; a = a->next)\n\t\t\t\t{\n\t\t\t\t\tif (PM_MergePackage(a, p))\n\t\t\t\t\t\treturn prev;\n\t\t\t\t}\n\t\t\t\tp->next = prev->alternative;\n\t\t\t\tprev->alternative = p;\n\t\t\t\tp->link = &prev->alternative;\n\t\t\t\tif (p->next)\n\t\t\t\t\tp->next->link = &p->next;\n\t\t\t\treturn p;\n\t\t\t}\n\n\t\t\t//something major differs, display both independantly.\n\t\t\tp->flags |= DPF_DISPLAYVERSION;\n\t\t\tprev->flags |= DPF_DISPLAYVERSION;\n\n\t\t\t//there might be more such dupes.\n\t\t}\n\n\t\tlink = &(*link)->next;\n\t}\n\tPM_ValidatePackage(p);\n\n\tif (p->flags & DPF_MANIFEST)\n\t\tif (!(p->flags & (DPF_PRESENT|DPF_CACHED)))\n\t\t{\t//if a manifest wants it then there's only any point listing it if there's an actual mirror listed. otherwise its a hint to the filesystem for ordering and not something that's actually present.\n\t\t\tint i;\n\t\t\tfor (i = 0; i < countof(p->mirror); i++)\n\t\t\t\tif (p->mirror[i])\n\t\t\t\t\tbreak;\n\t\t\tif (i == countof(p->mirror))\n\t\t\t{\n\t\t\t\tPM_FreePackage(p);\n\t\t\t\treturn NULL;\n\t\t\t}\n\t\t}\n\n\tp->next = *link;\n\tp->link = link;\n\t*link = p;\n\tnumpackages++;\n\n\treturn p;\n}\n\nstatic qboolean PM_CheckFeature(const char *feature, const char **featurename, const char **concommand)\n{\n#ifdef HAVE_CLIENT\n\textern cvar_t r_replacemodels;\n#endif\n\t*featurename = NULL;\n\t*concommand = NULL;\n#ifdef HAVE_CLIENT\n\t//check for compressed texture formats, to warn when not supported.\n\tif (!strcmp(feature, \"bc1\") || !strcmp(feature, \"bc2\") || !strcmp(feature, \"bc3\") || !strcmp(feature, \"s3tc\"))\n\t\treturn *featurename=\"S3 Texture Compression\", sh_config.hw_bc>=1;\n\tif (!strcmp(feature, \"bc4\") || !strcmp(feature, \"bc5\") || !strcmp(feature, \"rgtc\"))\n\t\treturn *featurename=\"Red/Green Texture Compression\", sh_config.hw_bc>=2;\n\tif (!strcmp(feature, \"bc6\") || !strcmp(feature, \"bc7\") || !strcmp(feature, \"bptc\"))\n\t\treturn *featurename=\"Block Partitioned Texture Compression\", sh_config.hw_bc>=3;\n\tif (!strcmp(feature, \"etc1\"))\n\t\treturn *featurename=\"Ericson Texture Compression, Original\", sh_config.hw_etc>=1;\n\tif (!strcmp(feature, \"etc2\") || !strcmp(feature, \"eac\"))\n\t\treturn *featurename=\"Ericson Texture Compression, Revision 2\", sh_config.hw_etc>=2;\n\tif (!strcmp(feature, \"astcldr\") || !strcmp(feature, \"astc\"))\n\t\treturn *featurename=\"Adaptive Scalable Texture Compression (LDR)\", sh_config.hw_astc>=1;\n\tif (!strcmp(feature, \"astchdr\"))\n\t\treturn *featurename=\"Adaptive Scalable Texture Compression (HDR)\", sh_config.hw_astc>=2;\n\n\tif (!strcmp(feature, \"24bit\"))\n\t\treturn *featurename=\"24bit Textures\", *concommand=\"seta gl_load24bit 1\\n\", gl_load24bit.ival;\n\tif (!strcmp(feature, \"md3\"))\n\t\treturn *featurename=\"Replacement Models\", *concommand=\"seta r_replacemodels md3 md2 md5mesh\\n\", !!strstr(r_replacemodels.string, \"md3\");\n\tif (!strcmp(feature, \"rtlights\"))\n\t\treturn *featurename=\"Realtime Dynamic Lights\", *concommand=\"seta r_shadow_realtime_dlight 1\\n\", r_shadow_realtime_dlight.ival||r_shadow_realtime_world.ival;\n\tif (!strcmp(feature, \"rtworld\"))\n\t\treturn *featurename=\"Realtime World Lights\", *concommand=\"seta r_shadow_realtime_dlight 1\\nseta r_shadow_realtime_world 1\\n\", r_shadow_realtime_world.ival;\n#endif\n\n\treturn false;\n}\nstatic qboolean PM_CheckPackageFeatures(package_t *p)\n{\n\tstruct packagedep_s *dep;\n\tconst char *featname, *enablecmd;\n\n\tfor (dep = p->deps; dep; dep = dep->next)\n\t{\n\t\tif (dep->dtype == DEP_NEEDFEATURE)\n\t\t{\n\t\t\tif (!PM_CheckFeature(dep->name, &featname, &enablecmd))\n\t\t\t\treturn false;\n\t\t}\n\t}\n\treturn true;\n}\n\nstatic qboolean PM_CheckFile(const char *filename, enum fs_relative base)\n{\n\tvfsfile_t *f = FS_OpenVFS(filename, \"rb\", base);\n\tif (f)\n\t{\n\t\tVFS_CLOSE(f);\n\t\treturn true;\n\t}\n\treturn false;\n}\n\nstatic void PM_Plugin_Source_CacheFinished(void *ctx, vfsfile_t *f);\nstatic void PM_AddSubListModule(void *module, plugupdatesourcefuncs_t *funcs, const char *url, const char *prefix, unsigned int flags)\n{\n\tsize_t i;\n\tif (!prefix)\n\t\tprefix = \"\";\n\tif (!*url)\n\t\treturn;\n\tif (strchr(url, '\\\"') || strchr(url, '\\n'))\n\t\treturn;\n\tif (strchr(prefix, '\\\"') || strchr(prefix, '\\n'))\n\t\treturn;\n\n\tfor (i = 0; i < pm_numsources; i++)\n\t{\n\t\tif (!strcmp(pm_source[i].url, url))\n\t\t{\n\t\t\tunsigned int newpri = flags&SRCFLMASK_FROM;\n\t\t\tunsigned int oldpri = pm_source[i].flags&SRCFLMASK_FROM;\n\t\t\tif (module)\n\t\t\t{\n\t\t\t\tpm_source[i].module = module;\n\t\t\t\tpm_source[i].funcs = funcs;\n\t\t\t}\n\t\t\tif (newpri > oldpri)\n\t\t\t{\t//replacing an historic package should stomp on most of it, retaining only its enablement status.\n\t\t\t\tpm_source[i].flags &= ~SRCFLMASK_FROM;\n\t\t\t\tpm_source[i].flags |= flags&(SRCFLMASK_FROM);\n\n\t\t\t\tZ_Free(pm_source[i].prefix);\n\t\t\t\tpm_source[i].prefix = Z_StrDup(prefix);\n\t\t\t}\n\t\t\tpm_source[i].flags &= ~SRCFL_HISTORIC;\n\t\t\tpm_source[i].flags |= flags & SRCFL_UNSAFE;\n\t\t\tif ((flags & SRCFL_USER) && (flags & (SRCFL_ENABLED|SRCFL_DISABLED)))\n\t\t\t{\n\t\t\t\tpm_source[i].flags &= ~SRCFL_ENABLED|SRCFL_DISABLED;\n\t\t\t\tpm_source[i].flags |= (SRCFL_ENABLED|SRCFL_DISABLED) & flags;\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t}\n\tif (i == pm_numsources)\n\t{\n\t\tZ_ReallocElements((void*)&pm_source, &pm_numsources, i+1, sizeof(*pm_source));\n\n\t\tpm_source[i].module = module;\n\t\tpm_source[i].funcs = funcs;\n\t\tpm_source[i].status = SRCSTAT_UNTRIED;\n\t\tpm_source[i].flags = flags;\n\n\t\tpm_source[i].url = BZ_Malloc(strlen(url)+1);\n\t\tstrcpy(pm_source[i].url, url);\n\n\t\tpm_source[i].prefix = BZ_Malloc(strlen(prefix)+1);\n\t\tstrcpy(pm_source[i].prefix, prefix);\n\n\t\tpm_sequence++;\n\t}\n\n\tif (pm_source[i].funcs && (pm_source[i].status == SRCSTAT_UNTRIED) && (pm_source[i].flags&SRCFL_ENABLED))\t//cache only!\n\t\tpm_source[i].funcs->Update(pm_source[i].url, VFS_OpenPipeCallback(PM_Plugin_Source_CacheFinished, &pm_source[i]), true);\n}\nstatic void PM_AddSubList(const char *url, const char *prefix, unsigned int flags)\n{\n\tPM_AddSubListModule(NULL, NULL, url, prefix, flags);\n}\n#ifdef WEBCLIENT\nstatic void PM_RemSubList(const char *url)\n{\n\tint i;\n\tfor (i = 0; i < pm_numsources; )\n\t{\n\t\tif (!strcmp(pm_source[i].url, url))\n\t\t{\n\t\t\tif (pm_source[i].curdl)\n\t\t\t{\n\t\t\t\tDL_Close(pm_source[i].curdl);\n\t\t\t\tpm_source[i].curdl = NULL;\n\t\t\t}\n\t\t\t//don't actually remove it, that'd mess up the indexes which could break stuff like PM_ListDownloaded callbacks. :(\n\t\t\tpm_source[i].flags = SRCFL_HISTORIC;\t//forget enablement state etc. we won't bother writing it.\n\t\t\tpm_sequence++;\t//make sure any menus hide it.\n\n\t\t\tCon_Printf(\"Source %s removed\\n\", url);\n\t\t\treturn;\n\t\t}\n\t\telse\n\t\t\ti++;\n\t}\n\n\tCon_Printf(\"Source %s not known, cannot remove\\n\", url);\n}\n#endif\n\nstruct packagesourceinfo_s\n{\n\tunsigned int parseflags;\t//0 for a downloadable ones, or DPF_FORGETONUNINSTALL|DPF_ENABLED for the installed package list.\n\tconst char *url;\n\tconst char *categoryprefix;\n\n\tenum hashvalidation_e validated;\n\n\tint version;\n\tchar gamedir[64];\t//when not overridden...\n\tchar mirror[MAXMIRRORS][MAX_OSPATH];\n\tint nummirrors;\n};\nstatic const char *PM_ParsePackage(struct packagesourceinfo_s *source, const char *tokstart, package_t **outpackage, int wantvariation, const char *defaultpkgname)\n{\n\tpackage_t *p;\n\tstruct packagedep_s *dep;\n\tqboolean isauto = false;\n\tconst char *start = tokstart;\n\tint variation = 0;\t//variation we're currently parsing (or skipping...).\n\tqboolean invariation = false;\n\n\n\t{\n\t\tchar pathname[256];\n\t\tchar *fullname = defaultpkgname?Z_StrDup(defaultpkgname):NULL;\n\t\tchar *file = NULL;\n\t\tchar *url = NULL;\n\t\tchar *category = NULL;\n\t\tunsigned int flags = source->parseflags;\n\n\t\tif (source->version > 2)\t//v3 has an 'installed' token to say its enabled. v2 has a 'stale' token to say its old, which was weird and to try to avoid conflicts.\n\t\t\tflags &= ~DPF_ENABLED;\n\n\t\tp = Z_Malloc(sizeof(*p));\n\t\tp->extract = EXTRACT_COPY;\n\t\tp->priority = PM_DEFAULTPRIORITY;\n\t\tp->fsroot = FS_ROOT;\n\t\tQ_strncpyz(p->gamedir, source->gamedir, sizeof(p->gamedir));\n\t\twhile (tokstart)\n\t\t{\n\t\t\tchar *eq;\n\t\t\tchar key[8192];\n\t\t\tchar val[8192];\n\n\t\t\t//skip leading whitespace\n\t\t\twhile (*tokstart>0 && *tokstart <= ' ')\n\t\t\t\ttokstart++;\n\n\t\t\tif (source->version >= 3)\n\t\t\t{\n\t\t\t\ttokstart = COM_ParseCString (tokstart, key, sizeof(key), NULL);\n\t\t\t\tif (!strcmp(key, \"}\"))\n\t\t\t\t{\n\t\t\t\t\tif (invariation)\n\t\t\t\t\t{\n\t\t\t\t\t\tinvariation = false;\n\t\t\t\t\t\tvariation++;\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\tbreak;\t//end of package.\n\t\t\t\t}\n\t\t\t\telse if (!strcmp(key, \"{\"))\n\t\t\t\t{\n\t\t\t\t\tif (invariation)\t//some sort of corruption? stop here.\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tinvariation = true;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\ttokstart = COM_ParseCString (tokstart, val, sizeof(val), NULL);\n\n\t\t\t\tif (invariation && variation != wantvariation)\n\t\t\t\t\tcontinue;\t//we're parsing one of the other variations. ignore this.\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t//the following are [\\][\"]key=[\"]value[\"] parameters, which is definitely messy, yes.\n\t\t\t\t*val = 0;\n\t\t\t\tif (*tokstart == '\\\\' || *tokstart == '\\\"')\n\t\t\t\t{\t//legacy quoting\n\t\t\t\t\ttokstart = COM_StringParse (tokstart, key, sizeof(key), false, false);\n\t\t\t\t\teq = strchr(key, '=');\n\t\t\t\t\tif (eq)\n\t\t\t\t\t{\n\t\t\t\t\t\t*eq = 0;\n\t\t\t\t\t\tQ_strncpyz(val, eq+1, sizeof(val));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\ttokstart = COM_ParseTokenOut(tokstart, \"=\", key, sizeof(key), NULL);\n\t\t\t\t\tif (!*key)\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\tif (tokstart && *tokstart == '=')\n\t\t\t\t\t{\n\t\t\t\t\t\ttokstart++;\n\t\t\t\t\t\tif (!(*tokstart >= 0 && *tokstart <= ' '))\n\t\t\t\t\t\t\ttokstart = COM_ParseCString(tokstart, val, sizeof(val), NULL);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (!strcmp(key, \"package\"))\n\t\t\t\tZ_StrDupPtr(&fullname, val);\n\t\t\telse if (!strcmp(key, \"url\"))\n\t\t\t\tZ_StrDupPtr(&url, val);\n\t\t\telse if (!strcmp(key, \"category\"))\n\t\t\t\tZ_StrDupPtr(&category, val);\n\t\t\telse if (!strcmp(key, \"title\"))\n\t\t\t\tZ_StrDupPtr(&p->title, val);\n\t\t\telse if (!strcmp(key, \"gamedir\"))\n\t\t\t\tQ_strncpyz(p->gamedir, val, sizeof(p->gamedir));\n\t\t\telse if (!strcmp(key, \"ver\") || !strcmp(key, \"v\"))\n\t\t\t\tQ_strncpyz(p->version, val, sizeof(p->version));\n\t\t\telse if (!strcmp(key, \"arch\"))\n\t\t\t\tZ_StrDupPtr(&p->arch, val);\n\t\t\telse if (!strcmp(key, \"priority\"))\n\t\t\t\tp->priority = atoi(val);\n\t\t\telse if (!strcmp(key, \"qhash\"))\n\t\t\t\tZ_StrDupPtr(&p->qhash, val);\n\t\t\telse if (!strcmp(key, \"packprefix\"))\n\t\t\t\tZ_StrDupPtr(&p->packprefix, val);\n\t\t\telse if (!strcmp(key, \"desc\") || !strcmp(key, \"description\"))\n\t\t\t{\n\t\t\t\tif (p->description)\n\t\t\t\t\tZ_StrCat(&p->description, \"\\n\");\n\t\t\t\tZ_StrCat(&p->description, val);\n\t\t\t}\n\t\t\telse if (!strcmp(key, \"license\"))\n\t\t\t\tZ_StrDupPtr(&p->license, val);\n\t\t\telse if (!strcmp(key, \"author\"))\n\t\t\t\tZ_StrDupPtr(&p->author, val);\n\t\t\telse if (!strcmp(key, \"preview\"))\n\t\t\t{\n\t\t\t\tif (source->nummirrors && !(!strncmp(val, \"http://\", 7) || !strncmp(val, \"https://\", 8)))\n\t\t\t\t{\n\t\t\t\t\tZ_Free(p->previewimage);\n\t\t\t\t\tp->previewimage = Z_StrDupf(\"%s%s\", source->mirror[0], val);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tZ_StrDupPtr(&p->previewimage, val);\n\t\t\t}\n\t\t\telse if (!strcmp(key, \"website\"))\n\t\t\t\tZ_StrDupPtr(&p->website, val);\n\t\t\telse if (!strcmp(key, \"unzipfile\"))\n\t\t\t{\t//filename extracted from zip.\n\t\t\t\tp->extract = EXTRACT_EXPLICITZIP;\n\t\t\t\tPM_AddDep(p, DEP_EXTRACTNAME, val);\n\t\t\t}\n\t\t\telse if (!strcmp(key, \"file\"))\n\t\t\t{\t//installed file\n\t\t\t\tif (!file)\n\t\t\t\t\tZ_StrDupPtr(&file, val);\n\t\t\t\tPM_AddDep(p, DEP_FILE, val);\n\t\t\t}\n\t\t\telse if (!strcmp(key, \"cachefile\"))\n\t\t\t{\t//installed file\n\t\t\t\tif (!file)\n\t\t\t\t\tZ_StrDupPtr(&file, val);\n\t\t\t\tPM_AddDep(p, DEP_CACHEFILE, val);\n\t\t\t}\n\t\t\telse if (!strcmp(key, \"extract\"))\n\t\t\t{\n\t\t\t\tif (!strcmp(val, \"xz\"))\n\t\t\t\t\tp->extract = EXTRACT_XZ;\n\t\t\t\telse if (!strcmp(val, \"gz\"))\n\t\t\t\t\tp->extract = EXTRACT_GZ;\n\t\t\t\telse if (!strcmp(val, \"zip\"))\n\t\t\t\t\tp->extract = EXTRACT_ZIP;\n\t\t\t\telse if (!strcmp(val, \"zip_explicit\"))\n\t\t\t\t\tp->extract = EXTRACT_EXPLICITZIP;\n\t\t\t\telse\n\t\t\t\t\tCon_Printf(\"Unknown decompression method: %s\\n\", val);\n\t\t\t}\n\t\t\telse if (!strcmp(key, \"map\"))\n\t\t\t\tPM_AddDep(p, DEP_MAP, val);\n\t\t\telse if (!strcmp(key, \"depend\") || !strcmp(key, \"depends\"))\n\t\t\t\tPM_AddDep(p, DEP_REQUIRE, val);\n\t\t\telse if (!strcmp(key, \"conflict\"))\n\t\t\t\tPM_AddDep(p, DEP_CONFLICT, val);\n\t\t\telse if (!strcmp(key, \"replace\"))\n\t\t\t\tPM_AddDep(p, DEP_REPLACE, val);\n\t\t\telse if (!strcmp(key, \"fileconflict\"))\n\t\t\t\tPM_AddDep(p, DEP_FILECONFLICT, val);\n\t\t\telse if (!strcmp(key, \"recommend\"))\n\t\t\t\tPM_AddDep(p, DEP_RECOMMEND, val);\n\t\t\telse if (!strcmp(key, \"suggest\"))\n\t\t\t\tPM_AddDep(p, DEP_SUGGEST, val);\n\t\t\telse if (!strcmp(key, \"need\"))\n\t\t\t\tPM_AddDep(p, DEP_NEEDFEATURE, val);\n\t\t\telse if (!strcmp(key, \"test\"))\n\t\t\t\tflags |= DPF_TESTING;\n\t\t\telse if (!strcmp(key, \"guessed\"))\n\t\t\t\tflags |= DPF_GUESSED;\n\t\t\telse if (!strcmp(key, \"trusted\") && (source->parseflags&DPF_ENABLED))\n\t\t\t\tflags |= DPF_TRUSTED;\n\t\t\telse if (!strcmp(key, \"stale\") && source->version==2)\n\t\t\t\tflags &= ~DPF_ENABLED;\t//known about, (probably) cached, but not actually enabled.\n\t\t\telse if (!strcmp(key, \"enabled\") && source->version>2)\n\t\t\t\tflags |= source->parseflags & DPF_ENABLED;\n\t\t\telse if (!strcmp(key, \"auto\"))\n\t\t\t\tisauto = true;\t//autoinstalled and NOT user-installed\n\t\t\telse if (!strcmp(key, \"root\") && (source->parseflags&DPF_ENABLED))\n\t\t\t{\n\t\t\t\tif (!Q_strcasecmp(val, \"bin\"))\n\t\t\t\t\tp->fsroot = FS_BINARYPATH;\n\t\t\t\telse if (!Q_strcasecmp(val, \"lib\"))\n\t\t\t\t\tp->fsroot = FS_LIBRARYPATH;\n\t\t\t\telse\n\t\t\t\t\tp->fsroot = FS_ROOT;\n\t\t\t}\n\t\t\telse if (!strcmp(key, \"dlsize\"))\n\t\t\t\tp->filesize = strtoull(val, NULL, 0);\n\t\t\telse if (!strcmp(key, \"sha1\"))\n\t\t\t\tZ_StrDupPtr(&p->filesha1, val);\n\t\t\telse if (!strcmp(key, \"sha512\"))\n\t\t\t\tZ_StrDupPtr(&p->filesha512, val);\n\t\t\telse if (!strcmp(key, \"sign\"))\n\t\t\t\tZ_StrDupPtr(&p->signature, val);\n\t\t\telse\n\t\t\t{\n\t\t\t\tCon_DPrintf(\"Unknown package property \\\"%s\\\"\\n\", key);\n\t\t\t}\n\t\t}\n\n\t\tif (!fullname)\n\t\t\tfullname = Z_StrDup(\"UNKNOWN\");\n//\t\tCon_Printf(\"%s - %s\\n\", source->url, fullname);\n\n\t\tif (category)\n\t\t{\n\t\t\tp->name = fullname;\n\n\t\t\tif (*source->categoryprefix)\n\t\t\t\tQ_snprintfz(pathname, sizeof(pathname), \"%s/%s\", source->categoryprefix, category);\n\t\t\telse\n\t\t\t\tQ_snprintfz(pathname, sizeof(pathname), \"%s\", category);\n\t\t\tif (*pathname)\n\t\t\t{\n\t\t\t\tif (pathname[strlen(pathname)-1] != '/')\n\t\t\t\t\tQ_strncatz(pathname, \"/\", sizeof(pathname));\n\t\t\t}\n\t\t\tp->category = Z_StrDup(pathname);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (*source->categoryprefix)\n\t\t\t\tQ_snprintfz(pathname, sizeof(pathname), \"%s/%s\", source->categoryprefix, fullname);\n\t\t\telse\n\t\t\t\tQ_snprintfz(pathname, sizeof(pathname), \"%s\", fullname);\n\t\t\tZ_Free(fullname);\n\t\t\tp->name = Z_StrDup(COM_SkipPath(pathname));\n\t\t\t*COM_SkipPath(pathname) = 0;\n\t\t\tp->category = Z_StrDup(pathname);\n\t\t}\n\n\t\tif (!p->title)\n\t\t\tp->title = Z_StrDup(p->name);\n\n\t\tp->flags = flags;\n\n\t\tif (url && (!strncmp(url, \"http://\", 7) || !strncmp(url, \"https://\", 8)))\n\t\t{\n\t\t\tp->mirror[0] = Z_StrDup(url);\n\n\t\t\tif (!file && p->extract != EXTRACT_ZIP)\n\t\t\t{\n\t\t\t\tFS_PathURLCache(url, pathname, sizeof(pathname));\n\t\t\t\tPM_AddDep(p, DEP_CACHEFILE, pathname+10);\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tint m;\n\t\t\tchar *ext = \"\";\n\t\t\tchar *relurl = url;\n\t\t\tif (!relurl)\n\t\t\t{\n\t\t\t\tif (p->extract == EXTRACT_XZ)\n\t\t\t\t\text = \".xz\";\n\t\t\t\telse if (p->extract == EXTRACT_GZ)\n\t\t\t\t\text = \".gz\";\n\t\t\t\telse if (p->extract == EXTRACT_ZIP || p->extract == EXTRACT_EXPLICITZIP)\n\t\t\t\t\text = \".zip\";\n\t\t\t\trelurl = file;\n\t\t\t}\n\t\t\tif (relurl)\n\t\t\t{\n\t\t\t\tfor (m = 0; m < source->nummirrors; m++)\n\t\t\t\t\tp->mirror[m] = Z_StrDupf(\"%s%s%s\", source->mirror[m], relurl, ext);\n\n\t\t\t\tif (!file && p->extract != EXTRACT_ZIP && source->nummirrors)\n\t\t\t\t{\n\t\t\t\t\tFS_PathURLCache(p->mirror[0], pathname, sizeof(pathname));\n\t\t\t\t\tPM_AddDep(p, DEP_CACHEFILE, pathname+10);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tPM_ValidateAuthenticity(p, source->validated);\n\n\t\tZ_Free(file);\n\t\tZ_Free(url);\n\t\tZ_Free(category);\n\t}\n\tif (p->arch)\n\t{\n\t\tif (!Q_strcasecmp(p->arch, THISENGINE))\n\t\t{\n\t\t\tif (!Sys_EngineMayUpdate())\n\t\t\t\tp->flags |= DPF_HIDDEN;\n\t\t\telse\n\t\t\t\tp->flags |= DPF_ENGINE;\n\t\t}\n\t\telse if (!Q_strcasecmp(p->arch, THISARCH))\n\t\t{\n\t\t\tif ((p->fsroot == FS_ROOT || p->fsroot == FS_BINARYPATH || p->fsroot == FS_LIBRARYPATH) && !*p->gamedir && p->priority == PM_DEFAULTPRIORITY)\n\t\t\t\tp->flags |= DPF_PLUGIN;\n\t\t}\n\t\telse\n\t\t\tp->flags |= DPF_HIDDEN;\t//other engine builds or other cpus are all hidden\n\t}\n\tfor (dep = p->deps; dep; dep = dep->next)\n\t{\n\t\tif (dep->dtype == DEP_FILECONFLICT)\n\t\t{\n\t\t\tconst char *n;\n\t\t\tif (*p->gamedir)\n\t\t\t\tn = va(\"%s/%s\", p->gamedir, dep->name);\n\t\t\telse\n\t\t\t\tn = dep->name;\n\t\t\tif (PM_CheckFile(n, p->fsroot))\n\t\t\t\tp->flags |= DPF_HIDDEN;\n\t\t}\n\t}\n\tif (p->flags & DPF_ENABLED)\n\t{\n\t\tif (isauto)\n\t\t\tp->flags |= DPF_AUTOMARKED;\t//FIXME: we don't know if this was manual or auto\n\t\telse\n\t\t\tp->flags |= DPF_USERMARKED;\t//FIXME: we don't know if this was manual or auto\n\t}\n\n\tif (source->url)\n\t\tPM_AddDep(p, DEP_SOURCE, source->url);\n\n\tif (outpackage)\n\t\t*outpackage = p;\n\telse\n\t{\n\t\tPM_InsertPackage(p);\n\n\t\tif (wantvariation == 0)\t//only the first!\n\t\t{\n\t\t\twhile (++wantvariation < variation)\n\t\t\t\tif (tokstart != PM_ParsePackage(source, start, NULL, wantvariation, defaultpkgname))\n\t\t\t\t{\n\t\t\t\t\tCon_Printf(CON_ERROR\"%s: Unable to parse package variation...\\n\", source->url);\n\t\t\t\t\tbreak;\t//erk?\n\t\t\t\t}\n\t\t}\n\t}\n\n\treturn tokstart;\n}\n\nstatic qboolean PM_ParsePackageList(const char *f, unsigned int parseflags, const char *url, const char *prefix)\n{\n\tchar line[65536];\n\tsize_t l;\n\tchar *sl;\n\n\tstruct packagesourceinfo_s source = {parseflags, url, prefix};\n\tchar *tokstart;\n\tqboolean forcewrite = false;\n\n\tconst char *filestart = f, *linestart;\n\n\tif (!f)\n\t\treturn forcewrite;\n\n\tsource.validated = (parseflags & (DPF_ENABLED|DPF_SIGNATUREACCEPTED))?VH_CORRECT/*FIXME*/:VH_UNSUPPORTED;\n\tQ_strncpyz(source.gamedir, FS_GetGamedir(false), sizeof(source.gamedir));\n\n\tif (url)\n\t{\n\t\tQ_strncpyz(source.mirror[source.nummirrors], url, sizeof(source.mirror[source.nummirrors]));\n\t\tsl = COM_SkipPath(source.mirror[source.nummirrors]);\n\t\t*sl = 0;\n\t\tsource.nummirrors++;\n\t}\n\n\tdo\n\t{\n\t\tfor (l = 0;*f;)\n\t\t{\n\t\t\tif (*f == '\\r' || *f == '\\n')\n\t\t\t{\n\t\t\t\tif (f[0] == '\\r' && f[1] == '\\n')\n\t\t\t\t\tf++;\n\t\t\t\tf++;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (l < sizeof(line)-1)\n\t\t\t\tline[l++] = *f;\n\t\t\telse\n\t\t\t\tline[0] = 0;\n\t\t\tf++;\n\t\t}\n\t\tline[l] = 0;\n\n\t\tCmd_TokenizeString (line, false, false);\n\t} while (!Cmd_Argc() && *f);\n\n\tif (strcmp(Cmd_Argv(0), \"version\"))\n\t\treturn forcewrite;\t//it's not the right format.\n\n\tsource.version = atoi(Cmd_Argv(1));\n\tif (\n#ifdef HAVE_LEGACY\n\t\tsource.version != 0 && source.version != 1 &&\n#endif\n\t\tsource.version != 2 && source.version != 3)\n\t{\n\t\tCon_Printf(\"Packagelist is of a future or incompatible version\\n\");\n\t\treturn forcewrite;\t//it's not the right version.\n\t}\n\n\tpm_sequence++;\n\n\twhile(*f)\n\t{\n\t\tlinestart = f;\n\t\tfor (l = 0;*f;)\n\t\t{\n\t\t\tif (*f == '\\r' || *f == '\\n')\n\t\t\t{\n\t\t\t\tif (f[0] == '\\r' && f[1] == '\\n')\n\t\t\t\t\tf++;\n\t\t\t\tf++;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (l < sizeof(line)-1)\n\t\t\t\tline[l++] = *f;\n\t\t\telse\n\t\t\t\tline[0] = 0;\n\t\t\tf++;\n\t\t}\n\t\tline[l] = 0;\n\n\t\ttokstart = COM_StringParse (line, com_token, sizeof(com_token), false, false);\n\t\tif (*com_token)\n\t\t{\n\t\t\tif (!strcmp(com_token, \"signature\"))\n\t\t\t{\t//FIXME: local file should hash /etc/machine-id with something, to avoid file-dropping dll-loading hacks.\n\t\t\t\t//signature \"authority\" \"signdata\"\n#if 0\n\t\t\t\t(void)filestart;\n\t\t\t\t(void)linestart;\n#else\n\t\t\t\tif (source.validated == VH_UNSUPPORTED)\n\t\t\t\t{\t//only allow one valid signature line...\n\t\t\t\t\tchar authority[MAX_OSPATH];\n\t\t\t\t\tchar signdata[MAX_OSPATH];\n\t\t\t\t\tchar signature_base64[MAX_OSPATH];\n\t\t\t\t\tqbyte *pubkey;\n\t\t\t\t\tsize_t pubkeysize;\n\t\t\t\t\tsize_t signsize;\n\t\t\t\t\tenum hashvalidation_e r;\n\t\t\t\t\tint i;\n\t\t\t\t\thashfunc_t *hf = &hash_sha2_512;\n\t\t\t\t\tvoid *hashdata = Z_Malloc(hf->digestsize);\n\t\t\t\t\tvoid *hashctx = Z_Malloc(hf->contextsize);\n\t\t\t\t\ttokstart = COM_StringParse (tokstart, authority, sizeof(authority), false, false);\n\t\t\t\t\ttokstart = COM_StringParse (tokstart, signature_base64, sizeof(signature_base64), false, false);\n\n\t\t\t\t\tsignsize = Base64_DecodeBlock(signature_base64, NULL, signdata, sizeof(signdata));\n\t\t\t\t\thf->init(hashctx);\n\t\t\t\t\thf->process(hashctx, filestart, linestart-filestart);\t//hash the text leading up to the signature line\n\t\t\t\t\thf->process(hashctx, f, strlen(f));\t\t\t\t\t\t//and hash the data after it. so the only bit not hashed is the signature itself.\n\t\t\t\t\thf->terminate(hashdata, hashctx);\n\t\t\t\t\tZ_Free(hashctx);\n\t\t\t\t\tr = VH_UNSUPPORTED;//preliminary\n\n\t\t\t\t\tpubkey = Auth_GetKnownCertificate(authority, &pubkeysize);\n\t\t\t\t\tif (!pubkey)\n\t\t\t\t\t{\n\t\t\t\t\t\tr = VH_AUTHORITY_UNKNOWN;\n\t\t\t\t\t\tCon_Printf(CON_ERROR\"%s: Unknown signature authority %s\\n\", url, authority);\n\t\t\t\t\t}\n\n\t\t\t\t\t//try and get one of our providers to verify it...\n\t\t\t\t\tfor (i = 0; r==VH_UNSUPPORTED && i < cryptolib_count; i++)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (cryptolib[i] && cryptolib[i]->VerifyHash)\n\t\t\t\t\t\t\tr = cryptolib[i]->VerifyHash(hashdata, hf->digestsize, pubkey, pubkeysize, signdata, signsize);\n\t\t\t\t\t}\n\n\t\t\t\t\tZ_Free(hashdata);\n\t\t\t\t\tsource.validated = r;\n\t\t\t\t}\n#endif\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (!strcmp(com_token, \"sublist\"))\n\t\t\t{\n\t\t\t\tchar *subprefix;\n\t\t\t\tchar url[MAX_OSPATH];\n\t\t\t\tchar enablement[MAX_OSPATH];\n\t\t\t\ttokstart = COM_StringParse (tokstart, url, sizeof(url), false, false);\n\t\t\t\ttokstart = COM_StringParse (tokstart, com_token, sizeof(com_token), false, false);\n\t\t\t\tif (*prefix)\n\t\t\t\t\tsubprefix = va(\"%s/%s\", prefix, com_token);\n\t\t\t\telse\n\t\t\t\t\tsubprefix = com_token;\n\n\t\t\t\tif (parseflags & DPF_ENABLED)\n\t\t\t\t{\t//local file. a user-defined source that was previously registered (but may have been disabled)\n\t\t\t\t\ttokstart = COM_StringParse (tokstart, enablement, sizeof(enablement), false, false);\n\t\t\t\t\tif (!Q_strcasecmp(enablement, \"enabled\"))\n\t\t\t\t\t\tPM_AddSubList(url, subprefix, SRCFL_USER|SRCFL_ENABLED);\n\t\t\t\t\telse\n\t\t\t\t\t\tPM_AddSubList(url, subprefix, SRCFL_USER|SRCFL_DISABLED);\n\t\t\t\t}\n\t\t\t\telse\t//a nested source. will need to inherit enablement the long way.\n\t\t\t\t\tPM_AddSubList(url, subprefix, SRCFL_NESTED);\t//nested sources should be disabled by default.\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (!strcmp(com_token, \"source\"))\n\t\t\t{\t//`source URL ENABLED|DISABLED` -- valid ONLY in an installed.lst file\n\t\t\t\tchar url[MAX_OSPATH];\n\t\t\t\tchar enablement[MAX_OSPATH];\n\t\t\t\ttokstart = COM_StringParse (tokstart, url, sizeof(url), false, false);\n\t\t\t\ttokstart = COM_StringParse (tokstart, enablement, sizeof(enablement), false, false);\n\t\t\t\tif (parseflags & DPF_ENABLED)\n\t\t\t\t{\n\t\t\t\t\tif (!Q_strcasecmp(enablement, \"enabled\"))\n\t\t\t\t\t\tPM_AddSubList(url, NULL, SRCFL_HISTORIC|SRCFL_ENABLED);\n\t\t\t\t\telse\n\t\t\t\t\t\tPM_AddSubList(url, NULL, SRCFL_HISTORIC|SRCFL_DISABLED);\n\t\t\t\t}\n\t\t\t\t//else ignore possible exploits with sources trying to force-enable themselves.\n\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (!strcmp(com_token, \"set\"))\n\t\t\t{\n\t\t\t\ttokstart = COM_StringParse (tokstart, com_token, sizeof(com_token), false, false);\n\t\t\t\tif (!strcmp(com_token, \"gamedir\"))\n\t\t\t\t{\n\t\t\t\t\ttokstart = COM_StringParse (tokstart, com_token, sizeof(com_token), false, false);\n\t\t\t\t\tif (!*com_token)\n\t\t\t\t\t\tQ_strncpyz(source.gamedir, FS_GetGamedir(false), sizeof(source.gamedir));\n\t\t\t\t\telse\n\t\t\t\t\t\tQ_strncpyz(source.gamedir, com_token, sizeof(source.gamedir));\n\t\t\t\t}\n\t\t\t\telse if (!strcmp(com_token, \"mirrors\"))\n\t\t\t\t{\n\t\t\t\t\tsource.nummirrors = 0;\n\t\t\t\t\twhile (source.nummirrors < countof(source.mirror) && tokstart)\n\t\t\t\t\t{\n\t\t\t\t\t\ttokstart = COM_StringParse (tokstart, com_token, sizeof(com_token), false, false);\n\t\t\t\t\t\tif (*com_token)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tQ_strncpyz(source.mirror[source.nummirrors], com_token, sizeof(source.mirror[source.nummirrors]));\n\t\t\t\t\t\t\tsource.nummirrors++;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse if (!strcmp(com_token, \"updatemode\"))\n\t\t\t\t{\n\t\t\t\t\ttokstart = COM_StringParse (tokstart, com_token, sizeof(com_token), false, false);\n\t\t\t\t\tif (parseflags & DPF_ENABLED)\t//don't use a downloaded file's version of this, only use the local version of it.\n\t\t\t\t\t\tCvar_ForceSet(&pkg_autoupdate, com_token);\n\t\t\t\t}\n\t\t\t\telse if (!strcmp(com_token, \"declined\"))\n\t\t\t\t{\n\t\t\t\t\tif (parseflags & DPF_ENABLED)\t//don't use a downloaded file's version of this, only use the local version of it.\n\t\t\t\t\t{\n\t\t\t\t\t\ttokstart = COM_StringParse (tokstart, com_token, sizeof(com_token), false, false);\n\t\t\t\t\t\tZ_Free(declinedpackages);\n\t\t\t\t\t\tif (*com_token)\n\t\t\t\t\t\t\tdeclinedpackages = Z_StrDup(com_token);\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tdeclinedpackages = NULL;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t//erk\n\t\t\t\t\tCon_Printf(\"%s: unrecognised command - set %s\\n\", source.url, com_token);\n\t\t\t\t}\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (!strcmp(com_token, \"{\"))\n\t\t\t{\n\t\t\t\tlinestart = COM_StringParse (linestart, com_token, sizeof(com_token), false, false);\n\t\t\t\tf = PM_ParsePackage(&source, linestart, NULL, 0, NULL);\n\t\t\t\tif (!f)\n\t\t\t\t\tbreak;\t//erk!\n\t\t\t}\n\t\t\telse if (source.version < 3)\n\t\t\t{\t//old single-line gibberish\n\t\t\t\tPM_ParsePackage(&source, tokstart, NULL, -1, com_token);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tCon_Printf(\"%s: unrecognised command - %s\\n\", source.url, com_token);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn forcewrite;\n}\n\nstatic qboolean PM_NameIsInStrings(const char *strings, const char *match)\n{\n\tchar tok[1024];\n\twhile (strings && *strings)\n\t{\n\t\tstrings = COM_ParseStringSetSep(strings, ';', tok, sizeof(tok));\n\t\tif (!Q_strcasecmp(tok, match))\t//okay its here.\n\t\t\treturn true;\n\t}\n\treturn false;\n}\n#ifdef PLUGINS\nvoid PM_EnumeratePlugins(void (*callback)(const char *name, qboolean blocked))\n{\n\tpackage_t *p;\n\tstruct packagedep_s *d;\n\n\tPM_PreparePackageList();\n\n\tfor (p = availablepackages; p; p = p->next)\n\t{\n\t\tif ((p->flags & DPF_ENABLED) && (p->flags & DPF_PLUGIN))\n\t\t{\n\t\t\tfor (d = p->deps; d; d = d->next)\n\t\t\t{\n\t\t\t\tif (d->dtype == DEP_FILE)\n\t\t\t\t{\n\t\t\t\t\tif (!Q_strncasecmp(d->name, PLUGINPREFIX, strlen(PLUGINPREFIX)))\n\t\t\t\t\t{\n\t\t\t\t\t\tqboolean blocked = PM_NameIsInStrings(fs_manifest?fs_manifest->installupd:NULL, va(\"!%s\", p->name));\n\t\t\t\t\t\tcallback(d->name, blocked);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n#endif\nvoid PM_EnumerateMaps(const char *partial, struct xcommandargcompletioncb_s *ctx)\n{\n\tpackage_t *p;\n\tstruct packagedep_s *d;\n\tsize_t partiallen = strlen(partial);\n\tchar mname[256];\n\tconst char *sep = strchr(partial, ':');\n\tsize_t pkgpartiallen = sep?sep-partial:partiallen;\n\n\tPM_PreparePackageList();\n\n\tfor (p = availablepackages; p; p = p->next)\n\t{\n\t\tif (!Q_strncasecmp(p->name, partial, pkgpartiallen))\n\t\t{\n\t\t\tfor (d = p->deps; d; d = d->next)\n\t\t\t{\n\t\t\t\tif (d->dtype == DEP_MAP)\n\t\t\t\t{\n\t\t\t\t\t/*if (!strchr(partial, ':'))\n\t\t\t\t\t{\t//try to expand to only one...\n\t\t\t\t\t\tQ_snprintfz(mname, sizeof(mname), \"%s:\", p->name);\n\t\t\t\t\t\tctx->cb(mname, NULL, NULL, ctx);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\telse*/\n\t\t\t\t\t{\n\t\t\t\t\t\tQ_snprintfz(mname, sizeof(mname), \"%s:%s\", p->name, d->name);\n\t\t\t\t\t\tif (!Q_strncasecmp(mname, partial, partiallen))\n\t\t\t\t\t\t\tctx->cb(mname, p->description, NULL, ctx);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nstatic qboolean QDECL Host_StubClose (struct vfsfile_s *file)\n{\n\treturn true;\n}\nstatic char *PM_GetMetaTextFromFile(vfsfile_t *file, const char *filename, char *qhash, size_t hashsize)\t//seeks, but does not close.\n{\n\tqboolean (QDECL *OriginalClose) (struct vfsfile_s *file) = file->Close;\t//evilness\n\tsearchpathfuncs_t *archive;\n\tchar *ret = NULL, *line;\n\t*qhash = 0;\n\n\tfile->Close = Host_StubClose;\t//so it doesn't go away without our say\n\tarchive = FS_OpenPackByExtension(file, NULL, filename, filename, \"\");\n\tif (archive)\n\t{\n\t\tint crc;\n\t\tflocation_t loc;\n\t\tvfsfile_t *metafile = NULL;\n\t\tif (archive->FindFile(archive, &loc, \"fte.meta\", NULL))\n\t\t\tmetafile = archive->OpenVFS(archive, &loc, \"rb\");\n\t\telse if (archive->FindFile(archive, &loc, \"-\", NULL))\t//lame.\n\t\t\tmetafile = archive->OpenVFS(archive, &loc, \"rb\");\n\t\tif (metafile)\n\t\t{\n\t\t\tsize_t sz = VFS_GETLEN(metafile);\n\t\t\tret = BZ_Malloc(sz+1);\n\t\t\tif (ret)\n\t\t\t{\n\t\t\t\tVFS_READ(metafile, ret, sz);\n\t\t\t\tret[sz] = 0;\n\t\t\t}\n\t\t\tVFS_CLOSE(metafile);\n\t\t}\n\t\tcrc = archive->GeneratePureCRC(archive, NULL);\n\n\t\tif (!ret)\n\t\t{\t//it'd be really nice if creators were embedding metadata into their packages... but creators suck... especially when their creations predate our engine...\n\t\t\t//anyway, we kinda need this stuff in order to get the gamedirs right when drag+dropping. we also get nicer titles out of it.\n#ifdef HAVE_LEGACY\n\t\t\tif (0)\n\t\t\t\t;\n\t\t\t//quake\n\t\t\telse if (crc == 0x92c8b832 && !Q_strcasecmp(filename, \"pak0.pak\"))\tret = Z_StrDup(\"{\\npackage quake_shareware\\ntitle \\\"Quake Shareware Data\\\"\\ngamedir id1\\nver 1.01\\n}\\n\");\n\t\t\telse if (crc == 0x4f069cac && !Q_strcasecmp(filename, \"pak0.pak\"))\tret = Z_StrDup(\"{\\npackage quake_shareware\\ntitle \\\"Quake Shareware Data\\\"\\ngamedir id1\\nver 1.06\\n}\\n\");\n\t\t\telse if (crc == 0x329050a6 && !Q_strcasecmp(filename, \"pak1.pak\"))\tret = Z_StrDup(\"{\\npackage quake_registered\\ntitle \\\"Quake Registered Data\\\"\\ngamedir id1\\n}\\n\");\n\t\t\telse if (crc == 0x31bef951 && !Q_strcasecmp(filename, \"pak0.pak\"))\tret = Z_StrDup(\"{\\npackage quake_hipnotic\\ntitle \\\"Scourge of Armagon\\\"\\ngamedir hipnotic\\n}\\n\");\n\t\t\telse if (crc == 0x799d60c0 && !Q_strcasecmp(filename, \"pak0.pak\"))\tret = Z_StrDup(\"{\\npackage quake_rogue\\ntitle \\\"Dissolution of Eternity\\\"\\ngamedir rogue\\n}\\n\");\n\t\t\t//quake rerelease\n\t\t\telse if (crc == 0xefdb8a92 && !Q_strcasecmp(filename, \"QuakeEX.kpf\"))ret= Z_StrDup(\"{\\npackage quakeex_data\\ntitle \\\"Quake Rerelease Misc Data\\\"\\ngamedir \\\"\\\"\\n}\\n\");\t//p4\n\t\t\telse if (crc == 0xbb51cd43 && !Q_strcasecmp(filename, \"pak0.pak\"))\tret = Z_StrDup(\"{\\npackage quake_rerel\\ntitle \\\"Quake Rerelease Data\\\"\\ngamedir id1\\nrequire quakeex_data\\n}\\n\");\t//p4\n\t\t\telse if (crc == 0xda8e9bb8 && !Q_strcasecmp(filename, \"pak0.pak\"))\tret = Z_StrDup(\"{\\npackage quake_rerel_hipnotic\\ntitle \\\"Scourge of Armagon (Rerelease)\\\"\\ngamedir hipnotic\\nrequire quakeex_data\\n}\\n\");\n\t\t\telse if (crc == 0x016eac0c && !Q_strcasecmp(filename, \"pak0.pak\"))\tret = Z_StrDup(\"{\\npackage quake_rerel_rogue\\ntitle \\\"Dissolution of Eternity (Rerelease)\\\"\\ngamedir rogue\\nrequire quakeex_data\\n}\\n\");\n\t\t\telse if (crc == 0xbdfb74e2 && !Q_strcasecmp(filename, \"pak0.pak\"))\tret = Z_StrDup(\"{\\npackage quake_rerel_ctf\\ntitle \\\"Capture The Flag (Rerelease)\\\"\\ngamedir ctf\\nrequire quakeex_data\\n}\\n\");\n\t\t\telse if (crc == 0x8751abb3 && !Q_strcasecmp(filename, \"pak0.pak\"))\tret = Z_StrDup(\"{\\npackage quake_rerel_dopa\\ntitle \\\"Dimensions Of The Past (Rerelease)\\\"\\ngamedir dopa\\nrequire quakeex_data\\n}\\n\");\n\t\t\telse if (crc == 0x73f34d24 && !Q_strcasecmp(filename, \"pak0.pak\"))\tret = Z_StrDup(\"{\\npackage quake_rerel_mg1\\ntitle \\\"Dimensions Of The Machine\\\"\\ngamedir mg1\\nrequire quakeex_data\\n}\\n\");\n\t\t\t//hexen2\n\t\t\telse if (crc == 0xae67217a && !Q_strcasecmp(filename, \"pak0.pak\"))\tret = Z_StrDup(\"{\\npackage hexen2_registered_p0\\ntitle \\\"Quake2 Data (pak0)\\\"\\ngamedir data1\\n}\\n\");\n\t\t\telse if (crc == 0xee6fabd1 && !Q_strcasecmp(filename, \"pak1.pak\"))\tret = Z_StrDup(\"{\\npackage hexen2_registered_p1\\ntitle \\\"Quake2 Data (pak1)\\\"\\ngamedir data1\\n}\\n\");\n\t\t\telse if (crc == 0x400848b4 && !Q_strcasecmp(filename, \"pak3.pak\"))\tret = Z_StrDup(\"{\\npackage hexen2_portals\\ntitle \\\"Portals Data\\\"\\ngamedir portals\\n}\\n\");\n\t\t\t//quake2\n\t\t\telse if (crc == 0x03ffdce0 && !Q_strcasecmp(filename, \"pak0.pak\"))\tret = Z_StrDup(\"{\\npackage quake2_p0\\ntitle \\\"Quake2 Data (pak0)\\\"\\ngamedir baseq2\\n}\\n\");\n\t\t\telse if (crc == 0x7d40ac55 && !Q_strcasecmp(filename, \"pak1.pak\"))\tret = Z_StrDup(\"{\\npackage quake2_p1\\ntitle \\\"Quake2 Data (pak1)\\\"\\ngamedir baseq2\\n}\\n\");\n\t\t\telse if (crc == 0xdc6fabf9 && !Q_strcasecmp(filename, \"pak2.pak\"))\tret = Z_StrDup(\"{\\npackage quake2_p2\\ntitle \\\"Quake2 Data (pak2)\\\"\\ngamedir baseq2\\n}\\n\");\n//\t\t\telse if (crc == 0x && !Q_strcasecmp(filename, \"pak0.pak\"))\tret = Z_StrDup(\"{\\npackage quake2_xatrixp2\\ntitle \\\"The Reckoning\\\"\\ngamedir baseq2\\nconflict quake2_release\\n}\\n\");\n//\t\t\telse if (crc == 0x && !Q_strcasecmp(filename, \"pak0.pak\"))\tret = Z_StrDup(\"{\\npackage quake2_rogue\\ntitle \\\"Ground Zero\\\"\\ngamedir baseq2\\nconflict quake2_release\\n}\\n\");\n//\t\t\telse if (crc == 0x && !Q_strcasecmp(filename, \"pak0.pak\"))\tret = Z_StrDup(\"{\\npackage quake2_release\\ntitle \\\"Quake2 Rerelease Data\\\"\\ngamedir baseq2\\nrequire quake2ex_data\\n}\\n\");\n//\t\t\telse if (crc == 0x && !Q_strcasecmp(filename, \"Q2Game.kpf\"))ret = Z_StrDup(\"{\\npackage quake2ex_data\\ntitle \\\"Quake2 Rerelease Misc Data\\\"\\ngamedir \\\"\\\"\\n}\\n\");\n\t\t\t//quake3\n\t\t\telse if (crc == 0xb1f4d354 && !Q_strcasecmp(filename, \"pak0.pk3\"))\tret = Z_StrDup(\"{\\npackage quake3_demo\\ntitle \\\"Quake3 Demo Data\\\"\\ngamedir baseq3\\n}\\n\");\n\t\t\telse if (crc == 0x5d626b5f && !Q_strcasecmp(filename, \"pak0.pk3\"))\tret = Z_StrDup(\"{\\npackage quake3_p0\\ntitle \\\"Quake3 Data (pak0)\\\"\\ngamedir baseq3\\n}\\n\");\n\t\t\telse if (crc == 0x11c4fe9b && !Q_strcasecmp(filename, \"pak1.pk3\"))\tret = Z_StrDup(\"{\\npackage quake3_p1\\ntitle \\\"Quake3 Data (pak1)\\\"\\ngamedir baseq3\\n}\\n\");\n\t\t\telse if (crc == 0x18912474 && !Q_strcasecmp(filename, \"pak2.pk3\"))\tret = Z_StrDup(\"{\\npackage quake3_p2\\ntitle \\\"Quake3 Data (pak2)\\\"\\ngamedir baseq3\\n}\\n\");\n\t\t\telse if (crc == 0xb24e9894 && !Q_strcasecmp(filename, \"pak3.pk3\"))\tret = Z_StrDup(\"{\\npackage quake3_p3\\ntitle \\\"Quake3 Data (pak3)\\\"\\ngamedir baseq3\\n}\\n\");\n\t\t\telse if (crc == 0x476700a6 && !Q_strcasecmp(filename, \"pak4.pk3\"))\tret = Z_StrDup(\"{\\npackage quake3_p4\\ntitle \\\"Quake3 Data (pak4)\\\"\\ngamedir baseq3\\n}\\n\");\n\t\t\telse if (crc == 0xf39bc355 && !Q_strcasecmp(filename, \"pak5.pk3\"))\tret = Z_StrDup(\"{\\npackage quake3_p5\\ntitle \\\"Quake3 Data (pak5)\\\"\\ngamedir baseq3\\n}\\n\");\n\t\t\telse if (crc == 0xdd13d69b && !Q_strcasecmp(filename, \"pak6.pk3\"))\tret = Z_StrDup(\"{\\npackage quake3_p6\\ntitle \\\"Quake3 Data (pak6)\\\"\\ngamedir baseq3\\n}\\n\");\n\t\t\telse if (crc == 0x362c0725 && !Q_strcasecmp(filename, \"pak7.pk3\"))\tret = Z_StrDup(\"{\\npackage quake3_p7\\ntitle \\\"Quake3 Data (pak7)\\\"\\ngamedir baseq3\\n}\\n\");\n\t\t\telse if (crc == 0x3a3dc1a6 && !Q_strcasecmp(filename, \"pak8.pk3\"))\tret = Z_StrDup(\"{\\npackage quake3_p8\\ntitle \\\"Quake3 Data (pak8)\\\"\\ngamedir baseq3\\n}\\n\");\n\t\t\telse if (crc == 0x90dc1501 && !Q_strcasecmp(filename, \"pak0.pk3\"))\tret = Z_StrDup(\"{\\npackage quake3_ta_p0\\ntitle \\\"TeamArena Data (pak0)\\\"\\ngamedir missionpack\\n}\\n\");\n\t\t\telse if (crc == 0x1e757510 && !Q_strcasecmp(filename, \"pak1.pk3\"))\tret = Z_StrDup(\"{\\npackage quake3_ta_p1\\ntitle \\\"TeamArena Data (pak1)\\\"\\ngamedir missionpack\\n}\\n\");\n\t\t\telse if (crc == 0x9eb4a591 && !Q_strcasecmp(filename, \"pak2.pk3\"))\tret = Z_StrDup(\"{\\npackage quake3_ta_p2\\ntitle \\\"TeamArena Data (pak2)\\\"\\ngamedir missionpack\\n}\\n\");\n\t\t\telse if (crc == 0x55c0476a && !Q_strcasecmp(filename, \"pak3.pk3\"))\tret = Z_StrDup(\"{\\npackage quake3_ta_p3\\ntitle \\\"TeamArena Data (pak3)\\\"\\ngamedir missionpack\\n}\\n\");\n\t\t\telse\n#endif\n\t\t\t\tret = Z_StrDup(\"{\\nguessed 1\\n}\\n\");\n\t\t}\n\n\t\t//get the archive's qhash.\n\t\tQ_snprintfz(qhash, hashsize, \"0x%x\", crc);\n\n\t\tline = ret;\n\t\tdo\n\t\t{\n\t\t\tline = (char*)Cmd_TokenizeString (line, false, false);\n\t\t\tif (!strcmp(com_token, \"{\"))\n\t\t\t\tbreak;\t//okay, found the start of it.\n\t\t} while (*line);\n\t\t//and leave it pointing there for easier parsing later. single package only.\n\t\tline = va(\"qhash %s\\n%s\", qhash, line);\n\t\tline = Z_StrDup(line);\n\t\tZ_Free(ret);\n\t\tret = line;\n\n\t\tarchive->ClosePath(archive);\n\t}\n\telse\n\t\tCon_Printf(\"No archive in %s\\n\", filename);\n\tfile->Close = OriginalClose;\n\treturn ret;\n}\n\nvoid *PM_GeneratePackageFromMeta(vfsfile_t *file, char *fname, size_t fnamesize, enum fs_relative *fsroot)\n{\n\tpackage_t *p = NULL;\n\tchar pkgname[MAX_QPATH];\n\tchar qhash[64];\n\tchar *pkgdata = PM_GetMetaTextFromFile(file, fname, qhash, sizeof(qhash));\n\n\tstruct packagesourceinfo_s pkgsrc = {DPF_ENABLED};\n\tpkgsrc.version = 3;\n\tpkgsrc.categoryprefix = \"\";\n\tQ_strncpyz(pkgsrc.gamedir, FS_GetGamedir(true), sizeof(pkgsrc.gamedir));\n\n\tCOM_StripAllExtensions(COM_SkipPath(fname), pkgname,sizeof(pkgname));\n\n\tPM_PreparePackageList();\t//just in case.\n\n\t//see if we can make any sense of it.\n\tPM_ParsePackage(&pkgsrc, pkgdata, &p, 1, fname);\n\tif (p)\n\t{\n\t\t/*if (!(p->fsroot == FS_ROOT && *p->gamedir) && FS_RELATIVE_ISSPECIAL(p->fsroot))\n\t\t{\t//the metadata is useless if it says to put it somewhere unsafe, though signed packages mean this is at least mostly safe...\n\t\t\tZ_Free(pkgdata);\n\t\t\tpkgdata = NULL;\n\t\t\tMenu_PromptOrPrint(va(localtext(\"Refusing to install to requested location\\n%s\\n\"), fname), \"Cancel\", true);\n\t\t}\n\t\telse*/ if (!PM_SignatureOkay(p))\n\t\t{\t//only allow if its trustworthy (or paks/pk3s)\n\t\t\tZ_Free(pkgdata);\n\t\t\tpkgdata = NULL;\n\t\t\tMenu_PromptOrPrint(va(localtext(\"Bad MetaData Signature\\n%s\\n\"), fname), \"Cancel\", true);\n\t\t}\n\t\telse if (p->extract == EXTRACT_COPY)\n\t\t{\n\t\t\tvfsfile_t *vfs;\n\t\t\tconst char *f = PM_GetDepSingle(p, DEP_FILE);\n\t\t\tif (!f)\n\t\t\t\tf = va(\"%s\", fname);\t//erk?\n\n\t\t\tif (*p->gamedir)\n\t\t\t\tf = va(\"%s/%s\", p->gamedir, f);\n\n\t\t\tZ_StrDupPtr(&p->qhash, qhash);\n\n\t\t\tvfs = FS_OpenVFS(f, \"rb\", p->fsroot);\t//if they're dropping pak0.pak, don't overwrite anything.\n\t\t\tif (vfs)\n\t\t\t{\n\t\t\t\tVFS_CLOSE(vfs);\n\t\t\t\tZ_Free(pkgdata);\n\t\t\t\tpkgdata = NULL;\n\t\t\t\tMenu_PromptOrPrint(va(localtext(\"File is already installed\\n%s\\n\"), fname), \"Cancel\", true);\n\t\t\t}\n\t\t\tif (!vfs && PM_TryGenCachedName(f, p, fname, fnamesize))\n\t\t\t\t;\n\t\t\telse\n\t\t\t\tQ_strncpyz(fname, f, fnamesize);\n\t\t\t*fsroot = p->fsroot;\n\t\t}\n\t\telse\n\t\t{\t//we're doing drag+drop single-file logic here, so don't want to handle extracting everything with the risk of overwriting.\n\t\t\tZ_Free(pkgdata);\n\t\t\tpkgdata = NULL;\n\t\t\tMenu_PromptOrPrint(va(localtext(\"Package installation too complex\\n%s\\n\"), fname), \"Cancel\", true);\n\t\t}\n\n\t\t//okay, seems there's something in it.\n\t\tPM_FreePackage(p);\n\t}\n\n\treturn pkgdata;\n}\n\nstatic qboolean PM_FileInstalled_Internal(const char *package, const char *category, const char *title, const char *filename, enum fs_relative fsroot, unsigned pkgflags, void *metainfo, qboolean enable)\n{\n\tpackage_t *p;\n\tchar *dlcache, *end;\n\n\tif (!*filename)\n\t\treturn false;\t//wtf?\n\n\tif (metainfo)\n\t{\n\t\tstruct packagesourceinfo_s pkgsrc = {DPF_ENABLED};\n\t\tpkgsrc.version = 3;\n\t\tpkgsrc.categoryprefix = \"\";\n\n\t\tPM_ParsePackage(&pkgsrc, metainfo, &p, 1, package);\n\t\tif (!p)\n\t\t\treturn false;\n\t}\n\telse\n\t{\n\t\tp = Z_Malloc(sizeof(*p));\n\t\tp->priority = PM_DEFAULTPRIORITY;\n\t\tstrcpy(p->version, \"?\" \"?\" \"?\" \"?\");\n\t}\n\n\tif (PM_HasDep(p, DEP_FILE, filename))\n\t\t;\t//no dupes\n\telse if (PM_GetDepSingle(p, DEP_FILE))\n\t{\t//a filename was specified, but it differs from what we expected...\n\t\tCon_Printf(CON_WARNING\"PM_FileInstalled: filename does not match metadata info\\n\");\n\t\tPM_FreePackage(p);\n\t\treturn false;\n\t}\n\telse\n\t\tPM_AddDep(p, DEP_FILE, filename);\n\n\tdlcache = strstr(p->deps->name+1, \"/dlcache/\");\n\tif (dlcache)\n\t{\n\t\tmemmove(dlcache+1, dlcache+9, strlen(dlcache+9)+1);\n\t\tdlcache = (char*)(COM_GetFileExtension(p->deps->name, NULL));\n\t\tif (dlcache[0] == '.' && dlcache[1] && *COM_GetFileExtension(p->deps->name, dlcache))\n\t\t{\n\t\t\tunsigned int qhash = strtoul(dlcache+1, &end, 16);\n\t\t\tif (!*end)\t//reached the end of the name?...\n\t\t\t{\n\t\t\t\tp->qhash = Z_StrDupf(\"%#x\", qhash);\t//that looks like a qhash!\n\t\t\t\t*dlcache = 0;\t//strip it from the name.\n\t\t\t}\n\t\t }\n\t}\n\n\tif (fsroot == FS_PUBGAMEONLY)\n\t{\n\t\tQ_strncpyz(p->gamedir, FS_GetGamedir(true), sizeof(p->gamedir));\n\t\tp->fsroot = FS_ROOT;\n\t}\n\telse if (fsroot == FS_ROOT && pkgflags)\n\t{\n\t\tdlcache = strchr(p->deps->name+1, '/');\n\t\tif (dlcache)\n\t\t{\n\t\t\t*dlcache++ = 0;\n\t\t\tQ_strncpyz(p->gamedir, p->deps->name, sizeof(p->gamedir));\n\t\t\tmemmove(p->deps->name, dlcache, strlen(dlcache)+1);\n\t\t}\n\t\tp->fsroot = FS_ROOT;\n\t}\n\telse\n\t\tp->fsroot = fsroot;\n\n\tif (pkgflags&DPF_PLUGIN)\n\t{\n\t\tif (strcmp(p->version, STRINGIFY(SVNREVISION)))\n\t\t\tenable = false;\n\t\tif (!p->arch)\n\t\t\tp->arch = Z_StrDup(THISARCH);\n\t\tZ_Free(p->qhash);\t//don't get confused.\n\t\tp->qhash = NULL;\n\t}\n\telse\n\t\tpkgflags |= DPF_FORGETONUNINSTALL;\n\tif (!p->name || !*p->name)\n\t\tp->name = Z_StrDup(package);\n\tif (!p->title || !*p->title)\n\t\tp->title = Z_StrDup(title);\n\tif (!p->category || !*p->category)\n\t\tp->category = Z_StrDup(category);\n\tp->flags = pkgflags|DPF_NATIVE;\n\tif (enable)\n\t\tp->flags |= DPF_USERMARKED|DPF_ENABLED;\n\n\tp = PM_InsertPackage(p);\n\tif (p)\n\t{\n\t\tif (enable && !(p->flags&DPF_ENABLED))\n\t\t\tCon_Printf(CON_WARNING\"PM_FileInstalled: package failed to enable\\n\");\n//\t\t\tCmd_ExecuteString(\"menu_download\\n\", RESTRICT_LOCAL);\n\t\tPM_ResortPackages();\n\t\tPM_WriteInstalledPackages();\n\t}\n\n\treturn true;\n}\nvoid PM_FileInstalled(const char *filename, enum fs_relative fsroot, void *metainfo, qboolean enable)\n{\n\tchar pkgname[MAX_QPATH];\n\tCOM_StripAllExtensions(COM_SkipPath(filename), pkgname,sizeof(pkgname));\n\tPM_FileInstalled_Internal(pkgname, \"\", pkgname, filename, fsroot, /*metainfo?0:*/DPF_GUESSED,  metainfo, enable);\n}\n\n#ifdef PLUGINS\nstatic package_t *PM_FindExactPackage(const char *packagename, const char *arch, const char *version, unsigned int flags);\nstatic package_t *PM_FindPackage(const char *packagename);\nstatic int QDECL PM_EnumeratedPlugin (const char *name, qofs_t size, time_t mtime, void *param, searchpathfuncs_t *spath)\n{\n\tenum fs_relative fsroot = (qintptr_t)param;\n\tstatic const char *knownarch[] =\n\t{\n\t\t\"x32\", \"x64\", \"amd64\", \"x86\",\t//various x86 ABIs\n\t\t\"arm\", \"arm64\", \"armhf\",\t\t//various arm ABIs\n\t\t\"ppc\", \"unk\",\t\t\t\t\t//various misc ABIs\n\t};\n\tpackage_t *p;\n\tstruct packagedep_s *dep;\n\tchar vmname[MAX_QPATH];\n\tint len, l, a;\n\tchar *dot;\n\tchar *pkgname;\n\tchar *metainfo = NULL;\n\tvfsfile_t *f;\n\tif (!strncmp(name, PLUGINPREFIX, strlen(PLUGINPREFIX)))\n\t\tQ_strncpyz(vmname, name+strlen(PLUGINPREFIX), sizeof(vmname));\n\telse\n\t\tQ_strncpyz(vmname, name, sizeof(vmname));\n\tlen = strlen(vmname);\n\tl = strlen(ARCH_CPU_POSTFIX ARCH_DL_POSTFIX);\n\tif (len > l && !strcmp(vmname+len-l, ARCH_CPU_POSTFIX ARCH_DL_POSTFIX))\n\t{\n\t\tlen -= l;\n\t\tvmname[len] = 0;\n\t}\n\telse\n\t{\n\t\tdot = strchr(vmname, '.');\n\t\tif (dot)\n\t\t{\n\t\t\t*dot = 0;\n\t\t\tlen = strlen(vmname);\n\n\t\t\t//if we can find a known cpu arch there then ignore it - its a different cpu arch\n\t\t\tfor (a = 0; a < countof(knownarch); a++)\n\t\t\t{\n\t\t\t\tl = strlen(knownarch[a]);\n\t\t\t\tif (len > l && !Q_strcasecmp(vmname + len-l, knownarch[a]))\n\t\t\t\t\treturn true;\t//wrong arch! ignore it.\n\t\t\t}\n\t\t}\n\t}\n\tif (len > 0 && vmname[len-1] == '_')\n\t\tvmname[len-1] = 0;\n\n\tfor (p = availablepackages; p; p = p->next)\n\t{\n\t\tif (!(p->flags & DPF_PLUGIN))\n\t\t\tcontinue;\n\t\tif (p->fsroot != fsroot)\n\t\t\tcontinue;\n\t\tfor (dep = p->deps; dep; dep = dep->next)\n\t\t{\n\t\t\tif (dep->dtype != DEP_FILE)\n\t\t\t\tcontinue;\n\t\t\tif (!Q_strcasecmp(dep->name, name))\n\t\t\t\treturn true;\n\t\t}\n\t}\n\n\tpkgname = va(\"fteplug_%s\", vmname);\n\n\tif (PM_FindExactPackage(pkgname, NULL, NULL, 0))\n\t\treturn true;\t//don't include it if its a dupe anyway.\n\tif (PM_FindExactPackage(vmname, NULL, NULL, 0))\n\t\treturn true;\t//don't include it if its a dupe anyway.\n\t//FIXME: should be checking whether there's a package that provides the file...\n\n\n\tf = FS_OpenVFS(name, \"rb\", fsroot);\n\tif (f)\n\t{\n\t\tchar qhash[16];\n\t\tmetainfo = PM_GetMetaTextFromFile(f, name, qhash, sizeof(qhash));\n\t\tVFS_CLOSE(f);\n\t}\n\n\treturn PM_FileInstalled_Internal(pkgname, \"Plugins/\", vmname, name, fsroot, DPF_PLUGIN|DPF_TRUSTED, metainfo,\n#ifdef ENABLEPLUGINSBYDEFAULT\n\t\t\ttrue\n#else\n\t\t\tfalse\n#endif\n\t\t);\n}\n#ifndef SERVERONLY\n#ifndef ENABLEPLUGINSBYDEFAULT\nstatic void PM_PluginDetected(void *ctx, int status)\n{\n\tif (status != PROMPT_CANCEL)\n\t\tPM_WriteInstalledPackages();\n\tif (status == PROMPT_YES)\t//'view'...\n\t{\n\t\tCmd_ExecuteString(\"menu_download\\n\", RESTRICT_LOCAL);\n\t\tCmd_ExecuteString(\"menu_download \\\"Plugins/\\\"\\n\", RESTRICT_LOCAL);\n\t}\n}\n#endif\n#endif\n#endif\n\n/*#ifndef SERVERONLY\nstatic void PM_AutoUpdateQuery(void *ctx, promptbutton_t status)\n{\n\tif (status == PROMPT_CANCEL)\n\t\treturn; //'Later'\n\tif (status == PROMPT_YES)\n\t\tCmd_ExecuteString(\"menu_download\\n\", RESTRICT_LOCAL);\n\tMenu_Download_Update();\n}\n#endif*/\n\nstatic void PM_PreparePackageList(void)\n{\n\t//figure out what we've previously installed.\n\tif (fs_manifest && !loadedinstalled)\n\t{\n\t\tint parm;\n\t\tqofs_t sz = 0;\n\t\tchar *f = FS_MallocFile(INSTALLEDFILES, FS_ROOT, &sz);\n\t\tloadedinstalled = true;\n\t\tif (f)\n\t\t{\n\t\t\tif (PM_ParsePackageList(f, DPF_FORGETONUNINSTALL|DPF_ENABLED, NULL, \"\"))\n\t\t\t\tPM_WriteInstalledPackages();\n\t\t\tBZ_Free(f);\n\t\t}\n\n\t\tparm = COM_CheckParm (\"-updatesrc\");\n\t\tif (parm)\n\t\t{\n\t\t\tunsigned int fl = SRCFL_USER;\n\t\t\tif (COM_CheckParm (\"-unsafe\"))\n\t\t\t\tfl |= SRCFL_UNSAFE;\n\t\t\tdo\n\t\t\t{\n\t\t\t\tPM_AddSubList(com_argv[parm+1], NULL, fl);\t//enable it by default. functionality is kinda broken otherwise.\n\t\t\t\tparm = COM_CheckNextParm (\"-updatesrc\", parm);\n\t\t\t} while (parm && parm < com_argc-1);\n\t\t}\n\t\telse if (fs_manifest && fs_manifest->downloadsurl && *fs_manifest->downloadsurl)\n\t\t{\n\t\t\tunsigned int fl = SRCFL_MANIFEST;\n\t\t\tchar *s = fs_manifest->downloadsurl;\n\n\t\t\tif (fs_manifest->security==MANIFEST_SECURITY_NOT)\n\t\t\t\tfl |= SRCFL_DISABLED;\t//don't trust it, don't even prompt.\n\n\t\t\twhile ((s = COM_Parse(s)))\n\t\t\t{\n#ifdef FTE_TARGET_WEB\n\t\t\t\tif (*com_token == '/' && !(fl&SRCFL_DISABLED))\n\t\t\t\t\tPM_AddSubList(com_token, NULL, fl|SRCFL_ENABLED);\t//enable it by default, its too annoying otherwise.\n\t\t\t\telse\n#endif\n\t\t\t\t\tPM_AddSubList(com_token, NULL, fl);\n\t\t\t}\n\t\t}\n\n#ifdef PLUGINS\n\t\t{\n\t\t\tchar sys[MAX_OSPATH];\n\t\t\tchar disp[MAX_OSPATH];\n\t\t\tif (FS_DisplayPath(\"\", FS_BINARYPATH, disp, sizeof(disp))&&FS_SystemPath(\"\", FS_BINARYPATH, sys, sizeof(sys)))\n\t\t\t{\n\t\t\t\tCon_DPrintf(\"Loading plugins from \\\"%s\\\"\\n\", disp);\n\t\t\t\tSys_EnumerateFiles(sys, PLUGINPREFIX\"*\" ARCH_DL_POSTFIX, PM_EnumeratedPlugin, (void*)FS_BINARYPATH, NULL);\n\t\t\t}\n\t\t\tif (FS_DisplayPath(\"\", FS_LIBRARYPATH, disp, sizeof(disp))&&FS_SystemPath(\"\", FS_LIBRARYPATH, sys, sizeof(sys)))\n\t\t\t{\n\t\t\t\tCon_DPrintf(\"Loading plugins from \\\"%s\\\"\\n\", disp);\n\t\t\t\tSys_EnumerateFiles(sys, PLUGINPREFIX\"*\" ARCH_DL_POSTFIX, PM_EnumeratedPlugin, (void*)FS_LIBRARYPATH, NULL);\n\t\t\t}\n\t\t}\n#endif\n\t}\n}\n\nvoid PM_ManifestChanged(ftemanifest_t *man)\n{\t//gamedir or something changed. reset the package manager stuff.\n\tif (!man)\n\t{\t//shutting down... don't reload anything.\n\t\tPM_Shutdown(false);\n\t\treturn;\n\t}\n\tPM_Shutdown(true);\n\tPM_UpdatePackageList(false);\n}\n\nqboolean PM_HandleRedirect(const char *package, /*char *cachename, size_t cachesize,*/ char *url, size_t urlsize)\n{\t//given a \"GAMEDIR/PACKAGEFILE\", swap it out for a real path/url that a client can download.\n\t//eg \"package/GAMEDIR/dlcache/PACKAGEFILE.xxxxxxxx\" or http://whereeverweoriginallydownloadeditfrom\"\n\tchar gamedir[16];\n\tchar *sep;\n\tpackage_t *p;\nchar *cachename = NULL; size_t cachesize=0;\n\tsep = strchr(package, '/');\n\t*cachename = 0;\n\tif (!sep)\n\t\treturn false;\t//no gamedir? that doesn't seem right. tell em to fuck off.\n\tif (sep-package >= sizeof(gamedir))\n\t\treturn false;\t//overflow\n\tmemcpy(gamedir, package, sep-package);\n\tgamedir[sep-package] = 0;\n\tsep++;\n\tfor (p = availablepackages; p; p = p->next)\n\t{\n\t\tif (!(p->flags & DPF_PRESENT))\n\t\t\tcontinue;\t//dunno, shouldn't really be trying to download it if its not even on the server.\n\t\tif (strcmp(p->gamedir, gamedir))\n\t\t\tcontinue;\t//nope, some other gamedir perhaps.\n\t\tif (PM_HasDep(p, DEP_FILE, sep))\n\t\t{\n\t\t\tif (!(p->flags & DPF_CACHED) || !PM_TryGenCachedName(package, p, cachename, cachesize))\n\t\t\t\t*cachename = 0;\n\t\t\tif (p->mirror[0])\n\t\t\t{\n\t\t\t\tQ_strncpyz(url, p->mirror[0], urlsize);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tif (p->flags & DPF_CACHED)\n\t\t\t{\n\t\t\t\tQ_snprintfz(url, urlsize, \"package/%s\", cachename);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\treturn false;\t//native? don't redirect it locally.\n\t\t}\n\t\t/*if (PM_HasDep(p, DEP_CACHEFILE, sep))\n\t\t{\n\t\t\tQ_snprintfz(cachename, sizeof(cachename), \"downloads/%s\", d->name);\n\t\t}*/\n\t}\n\treturn false;\n}\nvoid PM_LoadPackages(searchpath_t **oldpaths, const char *parent_pure, const char *parent_logical, searchpath_t *search, unsigned int loadstuff, int minpri, int maxpri)\n{\n\tpackage_t *p;\n\tstruct packagedep_s *d;\n\tchar temp[MAX_OSPATH];\n\tint pri;\n\n\tdo\n\t{\n\t\t//find the lowest used priority above the previous\n\t\tpri = maxpri;\n\t\tfor (p = availablepackages; p; p = p->next)\n\t\t{\n\t\t\tif ((p->flags & (DPF_ENABLED|DPF_MANIMARKED)) && p->priority>=minpri&&p->priority<pri && !Q_strcasecmp(parent_pure, p->gamedir))\n\t\t\t\tpri = p->priority;\n\t\t}\n\t\tminpri = pri+1;\n\n\t\tfor (p = availablepackages; p; p = p->next)\n\t\t{\n\t\t\tif ((p->flags & (DPF_ENABLED|DPF_MANIMARKED)) && p->priority==pri && !Q_strcasecmp(parent_pure, p->gamedir))\n\t\t\t{\n\t\t\t\tchar *qhash = (p->qhash&&*p->qhash)?p->qhash:NULL;\n\t\t\t\tunsigned int fsfl = 0;\n\t\t\t\tif (p->flags & DPF_CACHED)\n\t\t\t\t\tfsfl |= 0;\t//package is in the dlcache dir... if its downloaded from somewhere then its probably a bit rich to block it from downloading from elsewhere.\n\t\t\t\telse\n\t\t\t\t\tfsfl |= SPF_COPYPROTECTED;\n\t\t\t\tif (qhash && (p->flags&DPF_TRUSTED))\n\t\t\t\t\t;\n\t\t\t\telse\n\t\t\t\t\tfsfl |= SPF_UNTRUSTED;\t//never trust it if we can't provide it\n\n\t\t\t\tfor (d = p->deps; d; d = d->next)\n\t\t\t\t{\n\t\t\t\t\tif (d->dtype == DEP_FILE)\n\t\t\t\t\t{\n\t\t\t\t\t\tQ_snprintfz(temp, sizeof(temp), \"%s/%s\", p->gamedir, d->name);\n\t\t\t\t\t\tFS_AddHashedPackage(oldpaths, parent_pure, parent_logical, search, loadstuff, temp, qhash, p->packprefix, fsfl);\n\t\t\t\t\t}\n\t\t\t\t\telse if (d->dtype == DEP_CACHEFILE)\n\t\t\t\t\t{\n\t\t\t\t\t\tQ_snprintfz(temp, sizeof(temp), \"downloads/%s\", d->name);\n\t\t\t\t\t\tFS_AddHashedPackage(oldpaths, parent_pure, parent_logical, NULL, loadstuff, temp, qhash, p->packprefix, fsfl);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t} while (pri < maxpri);\n}\n\nvoid PM_Shutdown(qboolean soft)\n{\n\tsize_t i, pm_numoldsources = pm_numsources;\n\t//free everything...\n\n\tpm_sequence++;\n\n\tpm_numsources = 0;\n\tfor (i = 0; i < pm_numoldsources; i++)\n\t{\n#ifdef WEBCLIENT\n\t\tif (pm_source[i].curdl)\n\t\t{\n\t\t\tDL_Close(pm_source[i].curdl);\n\t\t\tpm_source[i].curdl = NULL;\n\t\t}\n#endif\n\t\tpm_source[i].status = SRCSTAT_UNTRIED;\n\n\t\tif (pm_source[i].module && soft)\n\t\t{\t//added via a plugin. reset rather than forget.\n\t\t\tpm_source[pm_numsources++] = pm_source[i];\n\t\t}\n\t\telse\n\t\t{\t//forget it, oh noes.\n\t\t\tZ_Free(pm_source[i].url);\n\t\t\tpm_source[i].url = NULL;\n\t\t\tZ_Free(pm_source[i].prefix);\n\t\t\tpm_source[i].prefix = NULL;\n\t\t}\n\t}\n\tif (!pm_numsources)\n\t{\n\t\tZ_Free(pm_source);\n\t\tpm_source = NULL;\n\t}\n\n\tif (!soft)\n\t{\n\t\twhile (availablepackages)\n\t\t\tPM_FreePackage(availablepackages);\n\t}\n\tloadedinstalled = false;\n}\n\n//finds the newest version\nstatic package_t *PM_FindExactPackage(const char *packagename, const char *arch, const char *version, unsigned int flags)\n{\n\tpackage_t *p, *r = NULL;\n\tif (arch && !*arch) arch = NULL;\n\tif (version && !*version) version = NULL;\n\n\tfor (p = availablepackages; p; p = p->next)\n\t{\n\t\tif (!strcmp(p->name, packagename))\n\t\t{\n\t\t\tif (arch && (!p->arch||strcmp(p->arch, arch)))\n\t\t\t\tcontinue;\t//wrong arch.\n\t\t\tif (!arch && p->arch && *p->arch && Q_strcasecmp(p->arch, THISARCH))\n\t\t\t\tcontinue;\t//wrong arch.\n\t\t\tif (flags && !(p->flags & flags))\n\t\t\t\tcontinue;\n\t\t\tif (version)\n\t\t\t{\t//versions are a bit more complex.\n\t\t\t\tif (*version == '=' && strcmp(p->version, version+1))\n\t\t\t\t\tcontinue;\n\t\t\t\tif (*version == '>' && strcmp(p->version, version+1)<=0)\n\t\t\t\t\tcontinue;\n\t\t\t\tif (*version == '<' && strcmp(p->version, version+1)>=0)\n\t\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (!r || strcmp(p->version, r->version)>0)\n\t\t\t\tr = p;\n\t\t}\n\t}\n\treturn r;\n}\nstatic package_t *PM_FindPackage(const char *packagename)\n{\n\tchar *t = strcpy(alloca(strlen(packagename)+2), packagename);\n\tchar *arch = strchr(t, ':');\n\tchar *ver = strchr(t, '=');\n\tif (!ver)\n\t\tver = strchr(t, '>');\n\tif (!ver)\n\t\tver = strchr(t, '<');\n\tif (arch)\n\t\t*arch++ = 0;\n\tif (ver)\n\t{\n\t\t*ver = 0;\n\t\treturn PM_FindExactPackage(t, arch, packagename + (ver-t), 0);\t//weirdness is because the leading char of the version is important.\n\t}\n\telse\n\t\treturn PM_FindExactPackage(t, arch, NULL, 0);\n}\n//returns the marked version of a package, if any.\nstatic package_t *PM_MarkedPackage(const char *packagename, int markflag)\n{\n\tchar *t = strcpy(alloca(strlen(packagename)+2), packagename);\n\tchar *arch = strchr(t, ':');\n\tchar *ver = strchr(t, '=');\n\tif (!markflag)\n\t\treturn NULL;\n\tif (!ver)\n\t\tver = strchr(t, '>');\n\tif (!ver)\n\t\tver = strchr(t, '<');\n\tif (arch)\n\t\t*arch++ = 0;\n\tif (ver)\n\t{\n\t\t*ver = 0;\n\t\treturn PM_FindExactPackage(t, arch, packagename + (ver-t), markflag);\t//weirdness is because the leading char of the version is important.\n\t}\n\telse\n\t\treturn PM_FindExactPackage(t, arch, NULL, markflag);\n}\n\n//just resets all actions, so that a following apply won't do anything.\nstatic void PM_RevertChanges(void)\n{\n\tpackage_t *p;\n\tint us = parse_revision_number(enginerevision, true), them;\n\n\tif (pkg_updating)\n\t\treturn;\n\n\tfor (p = availablepackages; p; p = p->next)\n\t{\n\t\tif (p->flags & DPF_ENGINE)\n\t\t{\n\t\t\tthem = parse_revision_number(p->version, true);\n\t\t\tif (!(p->flags & DPF_HIDDEN) && us && us==them && (p->flags & DPF_PRESENT))\n\t\t\t\tp->flags |= DPF_AUTOMARKED;\n\t\t\telse\n\t\t\t\tp->flags &= ~DPF_MARKED;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (p->flags & DPF_ENABLED)\n\t\t\t\tp->flags |= DPF_USERMARKED;\n\t\t\telse\n\t\t\t\tp->flags &= ~DPF_MARKED;\n\t\t}\n\t\tp->flags &= ~DPF_PURGE;\n\t}\n}\n\nstatic qboolean PM_HasDependant(package_t *package, unsigned int markflag)\n{\n\tpackage_t *o;\n\tstruct packagedep_s *dep;\n\tfor (o = availablepackages; o; o = o->next)\n\t{\n\t\tif (o->flags & markflag)\n\t\t\tfor (dep = o->deps; dep; dep = dep->next)\n\t\t\t\tif (dep->dtype == DEP_REQUIRE || dep->dtype == DEP_RECOMMEND || dep->dtype == DEP_SUGGEST)\n\t\t\t\t\tif (!strcmp(package->name, dep->name))\n\t\t\t\t\t\treturn true;\n\t}\n\treturn false;\n}\n\n//just flags, doesn't delete (yet)\n//markflag should be DPF_AUTOMARKED or DPF_USERMARKED\nstatic void PM_UnmarkPackage(package_t *package, unsigned int markflag)\n{\n\tpackage_t *o;\n\tstruct packagedep_s *dep;\n\n\tCOM_AssertMainThread(\"PM_UnmarkPackage\");\n\n\tif (pkg_updating)\n\t\treturn;\n\n\tif (!(package->flags & markflag))\n\t\treturn;\t//looks like its already deselected.\n\tpackage->flags &= ~(markflag);\n\n\tif (!(package->flags & DPF_MARKED))\n\t{\n#ifdef WEBCLIENT\n\t\t//Is this safe?\n\t\tpackage->trymirrors = 0;\t//if its enqueued, cancel that quickly...\n\t\tif (package->curdownload)\n\t\t{\t\t\t\t\t//if its currently downloading, cancel it.\n\t\t\tDL_Close(package->curdownload);\n\t\t\tpackage->curdownload = NULL;\n\t\t}\n\t\tpackage->flags &= ~DPF_PENDING;\n#endif\n\n\t\t//remove stuff that depends on us\n\t\tfor (o = availablepackages; o; o = o->next)\n\t\t{\n\t\t\tfor (dep = o->deps; dep; dep = dep->next)\n\t\t\t\tif (dep->dtype == DEP_REQUIRE)\n\t\t\t\t\tif (!strcmp(dep->name, package->name))\n\t\t\t\t\t\tPM_UnmarkPackage(o, DPF_MARKED);\n\t\t}\n\t}\n\tif (!(package->flags & DPF_USERMARKED))\n\t{\n\t\t//go through dependancies and unmark any automarked packages if nothing else depends upon them\n\t\tfor (dep = package->deps; dep; dep = dep->next)\n\t\t{\n\t\t\tif (dep->dtype == DEP_REQUIRE || dep->dtype == DEP_RECOMMEND)\n\t\t\t{\n\t\t\t\tpackage_t *d = PM_MarkedPackage(dep->name, DPF_AUTOMARKED);\n\t\t\t\tif (d && !(d->flags & DPF_USERMARKED))\n\t\t\t\t{\n\t\t\t\t\tif (!PM_HasDependant(d, DPF_MARKED))\n\t\t\t\t\t\tPM_UnmarkPackage(d, DPF_AUTOMARKED);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nstatic package_t *PM_RootForAlt(package_t *package)\n{\n\tpackage_t *p, *a;\n\tfor (p = availablepackages; p; p = p->next)\n\t{\n\t\tif (p == package)\n\t\t\treturn p;\n\t\tfor (a = p->alternative; a; a = a->next)\n\t\t{\n\t\t\tif (a == package)\n\t\t\t\treturn p;\n\t\t}\n\t}\n\treturn NULL;\t//some kind of error\n}\n//just flags, doesn't install\n//returns true if it was marked (or already enabled etc), false if we're not allowed.\nstatic qboolean PM_MarkPackage(package_t *package, unsigned int markflag)\n{\n\tpackage_t *o;\n\tstruct packagedep_s *dep, *dep2;\n\tqboolean replacing = false;\n\tpackage_t *root;\t//if its an alt package, we want to do some switcheroos\n\n\tif (pkg_updating)\n\t{\n\t\tCon_Printf(\"PM_MarkPackage: busy...\\n\");\n\t\treturn false;\n\t}\n\n\tif (package->flags & DPF_MARKED)\n\t{\n\t\tpackage->flags |= markflag;\n\t\treturn true;\t//looks like its already picked. marking it again will do no harm.\n\t}\n\n#ifndef WEBCLIENT\n\t//can't mark for download if we cannot download.\n\tif (!(package->flags & DPF_PRESENT))\n\t{\t//though we can at least unmark it for deletion...\n\t\tpackage->flags &= ~DPF_PURGE;\n\t\tCon_Printf(\"PM_MarkPackage: unable to download, and not already present.\\n\");\n\t\treturn false;\n\t}\n#else\n\tif (!PM_SignatureOkay(package))\n\t{\n\t\tCon_Printf(CON_WARNING\"%s: package not trusted, refusing to mark.\\n\", package->name);\n\t\treturn false;\n\t}\n#endif\n\n\t//any file-conflicts prevent the package from being installable.\n\t//this is mostly for pak1.pak\n\tfor (dep = package->deps; dep; dep = dep->next)\n\t{\n\t\tif (dep->dtype == DEP_FILECONFLICT)\n\t\t{\n\t\t\tconst char *n;\n\t\t\tif (*package->gamedir)\n\t\t\t\tn = va(\"%s/%s\", package->gamedir, dep->name);\n\t\t\telse\n\t\t\t\tn = dep->name;\n\t\t\tif (PM_CheckFile(n, package->fsroot))\n\t\t\t{\n\t\t\t\tchar tmp[MAX_OSPATH];\n\t\t\t\tFS_DisplayPath(n, package->fsroot, tmp,sizeof(tmp));\n\t\t\t\tCon_Printf(CON_WARNING\"%s: conflicts with existing file \\\"%s\\\"\\n\", package->name, tmp);\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t}\n\n\troot = PM_RootForAlt(package);\n\tif (root != package && (package->flags&DPF_TRUSTED) >= (root->flags&DPF_TRUSTED))\n\t{\n\t\t//unlink the new.\n\t\t*package->link = package->next;\n\t\tif (package->next)\n\t\t\tpackage->next->link = package->link;\n\n\t\t//replace the old root with the new\n\t\t*root->link = package;\n\t\tpackage->next = root->next;\n\t\tif (package->next)\n\t\t\tpackage->next->link = &package->next;\n\n\t\t//insert the old into the new's alt\n\t\troot->next = package->alternative;\n\t\tif (root->next)\n\t\t\troot->next->link = &root->next;\n\t\troot->link = &package->alternative;\n\t\t*root->link = root;\n\n\t\troot->flags &= ~DPF_ALLMARKED;\n\t}\n\telse if (root != package)\n\t\treturn false;\n\n\tpackage->flags |= markflag;\n\n\t//first check to see if we're replacing a different version of the same package\n\tfor (o = availablepackages; o; o = o->next)\n\t{\n\t\tif (o == package)\n\t\t\tcontinue;\n\n\t\tif (o->flags & DPF_MARKED)\n\t\t{\n\t\t\tif (!strcmp(o->name, package->name))\n\t\t\t{\t//replaces this package\n\t\t\t\to->flags &= ~DPF_MARKED;\n\t\t\t\treplacing = true;\n\t\t\t}\n\t\t\telse if ((package->flags & DPF_ENGINE) && (o->flags & DPF_ENGINE))\n\t\t\t\tPM_UnmarkPackage(o, DPF_MARKED);\t//engine updates are mutually exclusive, unmark the existing one (you might have old ones cached, but they shouldn't be enabled).\n\t\t\telse\n\t\t\t{\t//two packages with the same filename are always mutually incompatible, but with totally separate dependancies etc.\n\t\t\t\tqboolean remove = false;\n\t\t\t\tfor (dep = package->deps; dep; dep = dep->next)\n\t\t\t\t{\n\t\t\t\t\tif (dep->dtype == DEP_FILE)\n\t\t\t\t\tfor (dep2 = o->deps; dep2; dep2 = dep2->next)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (dep2->dtype == DEP_FILE)\n\t\t\t\t\t\tif (!strcmp(dep->name, dep2->name))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tPM_UnmarkPackage(o, DPF_MARKED);\n\t\t\t\t\t\t\tremove = true;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif (remove)\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\t//fixme: zip content conflicts\n\t\t\t}\n\t\t}\n\t}\n\n\t//if we are replacing an existing one, then dependancies are already settled (only because we don't do version deps)\n\tif (replacing)\n\t\treturn true;\n\n\t//satisfy our dependancies.\n\tfor (dep = package->deps; dep; dep = dep->next)\n\t{\n\t\tif (dep->dtype == DEP_REQUIRE || dep->dtype == DEP_RECOMMEND)\n\t\t{\n\t\t\tpackage_t *d = PM_MarkedPackage(dep->name, DPF_MARKED);\n\t\t\tif (!d)\n\t\t\t{\n\t\t\t\td = PM_FindPackage(dep->name);\n\t\t\t\tif (d)\n\t\t\t\t{\n\t\t\t\t\tif (dep->dtype == DEP_RECOMMEND && !PM_CheckPackageFeatures(d))\n\t\t\t\t\t\tCon_DPrintf(\"Skipping recommendation \\\"%s\\\"\\n\", dep->name);\n\t\t\t\t\telse\n\t\t\t\t\t\tPM_MarkPackage(d, DPF_AUTOMARKED);\n\t\t\t\t}\n\t\t\t\telse if (dep->dtype == DEP_REQUIRE)\n\t\t\t\t\tCon_Printf(CON_WARNING\"Couldn't find dependancy \\\"%s\\\"\\n\", dep->name);\n\t\t\t\telse\n\t\t\t\t\tCon_DPrintf(\"Couldn't find dependancy \\\"%s\\\"\\n\", dep->name);\n\t\t\t}\n\t\t}\n\t\tif (dep->dtype == DEP_CONFLICT || dep->dtype == DEP_REPLACE)\n\t\t{\n\t\t\tfor (;;)\n\t\t\t{\n\t\t\t\tpackage_t *d = PM_MarkedPackage(dep->name, DPF_MARKED);\n\t\t\t\tif (!d)\n\t\t\t\t\tbreak;\n\t\t\t\tPM_UnmarkPackage(d, DPF_MARKED);\n\t\t\t}\n\t\t}\n\t}\n\n\t//remove any packages that conflict with us.\n\tfor (o = availablepackages; o; o = o->next)\n\t{\n\t\tfor (dep = o->deps; dep; dep = dep->next)\n\t\t\tif (dep->dtype == DEP_CONFLICT || dep->dtype == DEP_REPLACE)\n\t\t\t\tif (!strcmp(dep->name, package->name))\n\t\t\t\t\tPM_UnmarkPackage(o, DPF_MARKED);\n\t}\n\treturn true;\n}\n\n//just flag stuff as needing updating\n#define UPDAVAIL_UPDATE\t\t1\t//there's regular updates available. show the updates menu.\n#define UPDAVAIL_REQUIRED\t2\t//important updates that require the user to confirm\n#define UPDAVAIL_FORCED\t\t4\t//trusted updates that are insisted on by the manifest.\nunsigned int PM_MarkUpdates (void)\n{\n\tunsigned int ret = 0;\n\tpackage_t *p, *o, *b;\n#ifdef WEBCLIENT\n\tpackage_t *e = NULL;\n\tint bestengine = parse_revision_number(enginerevision, true);\n\tint them;\n#endif\n\n\tdoautoupdate = 0;\n\n\tif (fs_manifest && fs_manifest->installupd)\n\t{\n\t\tchar tok[1024];\n\t\tchar *strings = fs_manifest->installupd;\n\t\twhile (strings && *strings)\n\t\t{\n\t\t\tqboolean isunwanted = (*strings=='!');\n\t\t\tstrings = COM_ParseStringSetSep(strings+isunwanted, ';', tok, sizeof(tok));\n\n\t\t\tp = PM_MarkedPackage(tok, DPF_MARKED);\n\t\t\tif (!p)\n\t\t\t{\n\t\t\t\tif (PM_NameIsInStrings(declinedpackages, tok))\n\t\t\t\t\tcontinue;\n\t\t\t\tp = PM_FindPackage(tok);\n\t\t\t\tif (p)\n\t\t\t\t{\n\t\t\t\t\tif (PM_MarkPackage(p, DPF_AUTOMARKED))\n\t\t\t\t\t\tret |= UPDAVAIL_UPDATE;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (isunwanted)\n\t\t\t{\n\t\t\t\tPM_UnmarkPackage(p, DPF_AUTOMARKED);\t//try and unmark it.\n\t\t\t\tret |= UPDAVAIL_UPDATE;\n\t\t\t}\n\t\t\telse if (!(p->flags & DPF_ENABLED))\n\t\t\t\tret |= UPDAVAIL_UPDATE;\n\t\t}\n\n\t\tif (ret)\n\t\t\tret = (fs_manifest->security==MANIFEST_SECURITY_INSTALLER)?UPDAVAIL_FORCED:UPDAVAIL_REQUIRED;\n\t}\n\n\tfor (p = availablepackages; p; p = p->next)\n\t{\n#ifdef WEBCLIENT\n\t\tif ((p->flags & DPF_ENGINE) && !(p->flags & DPF_HIDDEN) && bestengine>0 && PM_SignatureOkay(p))\n\t\t{\n\t\t\tthem = parse_revision_number(p->version, true);\n\t\t\tif (!(p->flags & DPF_TESTING) || pkg_autoupdate.ival >= UPD_TESTING)\n\t\t\t\tif (them > bestengine)\n\t\t\t\t{\n\t\t\t\t\te = p;\n\t\t\t\t\tbestengine = them;\n\t\t\t\t}\n\t\t}\n#endif\n\t\tif (p->flags & DPF_MARKED)\n\t\t{\n\t\t\tb = NULL;\n\t\t\tfor (o = availablepackages; o; o = o->next)\n\t\t\t{\n\t\t\t\tif (p == o || (o->flags & DPF_HIDDEN))\n\t\t\t\t\tcontinue;\n\t\t\t\tif (!(o->flags & DPF_TESTING) || pkg_autoupdate.ival >= UPD_TESTING)\n\t\t\t\t\tif (!strcmp(o->name, p->name) && !strcmp(o->arch?o->arch:\"\", p->arch?p->arch:\"\") && strcmp(o->version, p->version) > 0)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (!b || strcmp(b->version, o->version) < 0)\n\t\t\t\t\t\t\tb = o;\n\t\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (b)\n\t\t\t{\n\t\t\t\tif (PM_MarkPackage(b, p->flags&DPF_MARKED))\n\t\t\t\t{\n\t\t\t\t\tret |= UPDAVAIL_UPDATE;\n\t\t\t\t\tPM_UnmarkPackage(p, DPF_MARKED);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n#ifdef WEBCLIENT\n\tif (e && !(e->flags & DPF_MARKED))\n\t{\n\t\tif (pkg_autoupdate.ival >= UPD_STABLE)\n\t\t{\n\t\t\tif (PM_MarkPackage(e, DPF_AUTOMARKED))\n\t\t\t\tret |= UPDAVAIL_UPDATE;\n\t\t}\n\t}\n#endif\n\n\treturn ret;\n}\n\n#if defined(M_Menu_Prompt) || defined(SERVERONLY)\n#else\nstatic unsigned int PM_ChangeList(char *out, size_t outsize)\n{\n\tunsigned int changes = 0;\n\tconst char *change;\n\tpackage_t *p;\n\tsize_t l;\n\tsize_t ofs = 0;\n\tif (!outsize)\n\t\tout = NULL;\n\telse\n\t\t*out = 0;\n\tfor (p = availablepackages; p; p=p->next)\n\t{\n\t\tif (!(p->flags & DPF_MARKED) != !(p->flags & DPF_ENABLED) || (p->flags & DPF_PURGE))\n\t\t{\n\t\t\tchanges++;\n\t\t\tif (!out)\n\t\t\t\tcontinue;\n\n\t\t\tif (p->flags & DPF_MARKED)\n\t\t\t{\n\t\t\t\tif (p->flags & DPF_PURGE)\n\t\t\t\t\tchange = va(\" reinstall %s\\n\", p->name);\n\t\t\t\telse if (p->flags & DPF_PRESENT)\n\t\t\t\t\tchange = va(\" enable %s\\n\", p->name);\n\t\t\t\telse\n\t\t\t\t\tchange = va(\" install %s\\n\", p->name);\n\t\t\t}\n\t\t\telse if ((p->flags & DPF_PURGE) || !(p->qhash && (p->flags & DPF_PRESENT)))\n\t\t\t\tchange = va(\" uninstall %s\\n\", p->name);\n\t\t\telse\n\t\t\t\tchange = va(\" disable %s\\n\", p->name);\n\n\t\t\tl = strlen(change);\n\t\t\tif (ofs+l >= outsize)\n\t\t\t{\n\t\t\t\tQ_strncpyz(out, \"Too many changes\\n\", outsize);\n\t\t\t\tout = NULL;\n\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tmemcpy(out+ofs, change, l);\n\t\t\t\tofs += l;\n\t\t\t\tout[ofs] = 0;\n\t\t\t}\n\t\t}\n\t}\n\treturn changes;\n}\n#endif\n\nstatic void PM_PrintChanges(void)\n{\n\tqboolean changes = 0;\n\tpackage_t *p;\n\tfor (p = availablepackages; p; p=p->next)\n\t{\n\t\tif (!(p->flags & DPF_MARKED) != !(p->flags & DPF_ENABLED) || (p->flags & DPF_PURGE))\n\t\t{\n\t\t\tchanges++;\n\t\t\tif (p->flags & DPF_MARKED)\n\t\t\t{\n\t\t\t\tif (p->flags & DPF_PURGE)\n\t\t\t\t\tCon_Printf(\" reinstall %s\\n\", p->name);\n\t\t\t\telse\n\t\t\t\t\tCon_Printf(\" install %s\\n\", p->name);\n\t\t\t}\n\t\t\telse if ((p->flags & DPF_PURGE) || !(p->qhash && (p->flags & DPF_CACHED)))\n\t\t\t\tCon_Printf(\" uninstall %s\\n\", p->name);\n\t\t\telse\n\t\t\t\tCon_Printf(\" disable %s\\n\", p->name);\n\t\t}\n\t}\n\tif (!changes)\n\t\tCon_Printf(\"<no changes>\\n\");\n\telse\n\t\tCon_Printf(\"<%i package(s) changed>\\n\", changes);\n}\n\n#ifdef WEBCLIENT\nstatic void PM_ListDownloaded(struct dl_download *dl)\n{\n\tsize_t listidx = dl->user_num;\n\tsize_t sz = 0;\n\tchar *f = NULL;\n\tif (dl->file && dl->status != DL_FAILED)\n\t{\n\t\tsz = VFS_GETLEN(dl->file);\n\t\tf = BZ_Malloc(sz+1);\n\t\tif (f)\n\t\t{\n\t\t\tf[sz] = 0;\n\t\t\tif (sz != VFS_READ(dl->file, f, sz))\n\t\t\t{\t//err... weird...\n\t\t\t\tBZ_Free(f);\n\t\t\t\tf = NULL;\n\t\t\t}\n\t\t\tif (strlen(f) != sz)\n\t\t\t{\t//don't allow mid-file nulls.\n\t\t\t\tBZ_Free(f);\n\t\t\t\tf = NULL;\n\t\t\t}\n\t\t}\n\t}\n\n\tif (dl != pm_source[listidx].curdl)\n\t{\n\t\t//this request looks stale.\n\t\tBZ_Free(f);\n\t\treturn;\n\t}\n\tpm_source[listidx].curdl = NULL;\n\n\t//FIXME: validate a signature!\n\n\tif (f)\n\t{\n\t\tif (dl->replycode != 100)\n\t\t\tpm_source[listidx].status = SRCSTAT_OBTAINED;\n\t\tpm_sequence++;\n\t\tif (pm_source[listidx].flags & SRCFL_UNSAFE)\n\t\t\tPM_ParsePackageList(f, DPF_SIGNATUREACCEPTED, dl->url, pm_source[listidx].prefix);\n\t\telse\n\t\t\tPM_ParsePackageList(f, 0, dl->url, pm_source[listidx].prefix);\n\t\tPM_ResortPackages();\n\t}\n\telse if (dl->replycode == HTTP_DNSFAILURE)\n\t\tpm_source[listidx].status = SRCSTAT_FAILED_DNS;\n\telse if (dl->replycode == HTTP_NORESPONSE)\n\t\tpm_source[listidx].status = SRCSTAT_FAILED_NORESP;\n\telse if (dl->replycode == HTTP_REFUSED)\n\t\tpm_source[listidx].status = SRCSTAT_FAILED_REFUSED;\n\telse if (dl->replycode == HTTP_EOF)\n\t\tpm_source[listidx].status = SRCSTAT_FAILED_EOF;\n\telse if (dl->replycode == HTTP_MITM || dl->replycode == HTTP_UNTRUSTED)\n\t\tpm_source[listidx].status = SRCSTAT_FAILED_MITM;\n\telse if (dl->replycode && dl->replycode < 900)\n\t\tpm_source[listidx].status = SRCSTAT_FAILED_HTTP;\n\telse\n\t\tpm_source[listidx].status = SRCSTAT_FAILED_EOF;\n\tBZ_Free(f);\n\n\tif (!doautoupdate)\n\t\treturn;\t//don't spam this.\n\tif (pm_pendingprompts)\n\t\treturn;\n\t//check if we're still waiting\n\tfor (listidx = 0; listidx < pm_numsources; listidx++)\n\t{\n\t\tif (pm_source[listidx].status == SRCSTAT_PENDING)\n\t\t\treturn;\n\t}\n\n\t//if our downloads finished and we want to shove it in the user's face then do so now.\n\tif (doautoupdate && listidx == pm_numsources)\n\t{\n\t\tint updates = PM_MarkUpdates();\n\t\tif (updates == UPDAVAIL_FORCED)\n\t\t\tPM_PromptApplyChanges();\n\t\telse if (updates)\n\t\t{\n#ifdef DOWNLOADMENU\n\t\t\tif (!isDedicated)\n\t\t\t{\n\t\t\t\tMenu_PopAll();\n\t\t\t\tCmd_ExecuteString(\"menu_download\\n\", RESTRICT_LOCAL);\n\t\t\t}\n\t\t\telse\n#endif\n\t\t\t\tPM_PrintChanges();\n\t\t}\n\t}\n}\nstatic void PM_Plugin_Source_Finished(void *ctx, vfsfile_t *f)\n{\t//plugin closed its write end.\n\tstruct pm_source_s *src = ctx;\n\tsize_t idx = src-pm_source;\n\tif (idx < pm_numsources && ctx == &pm_source[idx])\n\t{\n\t\tCOM_AssertMainThread(\"PM_Plugin_Source_Finished\");\n\t\tif (!src->curdl)\n\t\t{\n\t\t\tstruct dl_download dl;\n\t\t\tdl.file = f;\n\t\t\tdl.status = DL_FINISHED;\n\t\t\tdl.user_num = src-pm_source;\n\t\t\tdl.url = src->url;\n\t\t\tdl.replycode = 200;\t//okay\n\t\t\tsrc->curdl = &dl;\n\t\t\tPM_ListDownloaded(&dl);\n\t\t}\n\t}\n\tVFS_CLOSE(f);\n}\nstatic void PM_Plugin_Source_CacheFinished(void *ctx, vfsfile_t *f)\n{\t//plugin closed its write end.\n\tstruct pm_source_s *src = ctx;\n\tsize_t idx = src-pm_source;\n\tif (idx < pm_numsources && ctx == &pm_source[idx])\n\t{\n\t\tCOM_AssertMainThread(\"PM_Plugin_Source_CacheFinished\");\n\t\tif (!src->curdl)\n\t\t{\n\t\t\tstruct dl_download dl;\n\t\t\tdl.file = f;\n\t\t\tdl.status = DL_FINISHED;\n\t\t\tdl.user_num = src-pm_source;\n\t\t\tdl.url = src->url;\n\t\t\tdl.replycode = 100;\n\t\t\tsrc->curdl = &dl;\n\t\t\tPM_ListDownloaded(&dl);\n\t\t}\n\t}\n\tVFS_CLOSE(f);\n}\n#endif\n#if defined(HAVE_CLIENT) && defined(WEBCLIENT)\nstatic void PM_AllowPackageListQuery_Callback(void *ctx, promptbutton_t opt)\n{\n\tunsigned int i;\n\t//something changed, let it download now.\n\tif (opt!=PROMPT_CANCEL)\n\t{\n\t\tallowphonehome = (opt==PROMPT_YES);\n\n\t\tfor (i = 0; i < pm_numsources; i++)\n\t\t{\n\t\t\tif ((pm_source[i].flags & SRCFL_MANIFEST) && !(pm_source[i].flags & SRCFL_DISABLED))\n\t\t\t\tpm_source[i].flags |= SRCFL_ONCE;\n\t\t}\n\t}\n\tPM_UpdatePackageList(false);\n}\n#endif\n//retry 1==\nstatic void PM_UpdatePackageList(qboolean autoupdate)\n{\n\tunsigned int i;\n\n\tPM_PreparePackageList();\n\n#ifndef WEBCLIENT\n\tfor (i = 0; i < pm_numsources; i++)\n\t{\n\t\tif (pm_source[i].status == SRCSTAT_PENDING)\n\t\t\tpm_source[i].status = SRCSTAT_FAILED_DNS;\n\t}\n#else\n\tdoautoupdate |= autoupdate;\n\n\tif (COM_CheckParm(\"-noupdate\") || COM_CheckParm(\"-noupdates\"))\n\t\tallowphonehome = false;\n\t#ifdef HAVE_CLIENT\n\telse if (pkg_autoupdate.ival >= 1)\n\t\tallowphonehome = true;\n\telse if (allowphonehome == -1)\n\t{\n\t\tif (doautoupdate)\n\t\t\tMenu_Prompt(PM_AllowPackageListQuery_Callback, NULL, localtext(\"Query updates list?\\n\"), \"Okay\", NULL, \"Nope\", true);\n\t\treturn;\n\t}\n\n\tif (allowphonehome && PM_AreSourcesNew(true))\n\t\treturn;\n\t#else\n\t\tallowphonehome = true; //erk.\n\t#endif\n\n\tif (pm_pendingprompts)\n\t\treturn;\n\tautoupdate = doautoupdate;\n\n\t//kick off the initial tier of list-downloads.\n\tfor (i = 0; i < pm_numsources; i++)\n\t{\n\t\tif (pm_source[i].flags & SRCFL_HISTORIC)\n\t\t\tcontinue;\n\t\tif (!(pm_source[i].flags & (SRCFL_ENABLED|SRCFL_ONCE)))\n\t\t\tcontinue;\t//is not explicitly enabled. might be pending for user confirmation.\n\t\tautoupdate = false;\n\t\tif (pm_source[i].curdl)\n\t\t\tcontinue;\n\n\t\tif (allowphonehome<=0)\n\t\t{\n\t\t\tpm_source[i].status = SRCSTAT_UNTRIED;\n\t\t\tcontinue;\n\t\t}\n\t\tif (pm_source[i].status == SRCSTAT_OBTAINED)\n\t\t\tcontinue;\t//already successful once. no need to do it again.\n\t\tpm_source[i].flags &= ~SRCFL_ONCE;\n\n\t\tif (pm_source[i].funcs)\n\t\t{\n\t\t\tpm_source[i].funcs->Update(pm_source[i].url, VFS_OpenPipeCallback(PM_Plugin_Source_Finished, &pm_source[i]), false);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tpm_source[i].curdl = HTTP_CL_Get(pm_source[i].url, NULL, PM_ListDownloaded);\n\t\t\tif (pm_source[i].curdl)\n\t\t\t{\n\t\t\t\tpm_source[i].curdl->user_num = i;\n\n\t\t\t\tpm_source[i].curdl->file = VFSPIPE_Open(1, false);\n\t\t\t\tpm_source[i].curdl->isquery = true;\n\t\t\t\tDL_CreateThread(pm_source[i].curdl, NULL, NULL);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tCon_Printf(\"Could not contact updates server - %s\\n\", pm_source[i].url);\n\t\t\t\tpm_source[i].status = SRCSTAT_FAILED_DNS;\n\t\t\t}\n\t\t}\n\t}\n#endif\n\n\tif (autoupdate)\n\t{\n\t\tif (PM_MarkUpdates())\n\t\t{\n#ifdef DOWNLOADMENU\n\t\t\tif (!isDedicated)\n\t\t\t\tCbuf_AddText(\"menu_download\\n\", RESTRICT_LOCAL);\n\t\t\telse\n#endif\n\t\t\t\tPM_PrintChanges();\n\t\t}\n\t}\n}\n\nqboolean PM_RegisterUpdateSource(void *module, plugupdatesourcefuncs_t *funcs)\n{\n\tsize_t i;\n\tif (!funcs)\n\t{\n\t\tfor (i = 0; i < pm_numsources; i++)\n\t\t{\n\t\t\tif (pm_source[i].module == module)\n\t\t\t{\n\t\t\t\tpm_source[i].module = NULL;\n\t\t\t\tpm_source[i].funcs = NULL;\n\t\t\t}\n\t\t}\n\t}\n\telse\n\t\tPM_AddSubListModule(module, funcs, va(\"plug:%s\", funcs->description), NULL, SRCFL_PLUGIN|SRCFL_ENABLED);\t//plugin sources default to enabled as you can always just disable the plugin that provides it.\n\treturn true;\n}\n\nstatic void COM_QuotedConcat(const char *cat, char *buf, size_t bufsize)\n{\n\tconst unsigned char *gah;\n\tfor (gah = (const unsigned char*)cat; *gah; gah++)\n\t{\n\t\tif (*gah <= ' ' || *gah == '$' || *gah == '\\\"' || *gah == '\\n' || *gah == '\\r')\n\t\t\tbreak;\n\t}\n\tif (*gah || *cat == '\\\\' ||\n\t\tstrstr(cat, \"//\") || strstr(cat, \"/*\"))\n\t{\t//contains some dodgy stuff.\n\t\tsize_t curlen = strlen(buf);\n\t\tbuf += curlen;\n\t\tbufsize -= curlen;\n\t\tCOM_QuotedString(cat, buf, bufsize, false);\n\t}\n\telse\n\t{\t//okay, no need for quotes.\n\t\tQ_strncatz(buf, cat, bufsize);\n\t}\n}\nstatic void COM_QuotedKeyVal(const char *key, const char *val, char *buf, size_t bufsize)\n{\n\tsize_t curlen;\n\n\tQ_strncatz(buf, \"\\t\\\"\", bufsize);\n\n\tcurlen = strlen(buf);\n\tbuf += curlen;\n\tbufsize -= curlen;\n\tCOM_QuotedString(key, buf, bufsize, true);\n\n\tif (strlen(key) <= 5)\n\t\tQ_strncatz(buf, \"\\\"\\t\\t\\\"\", bufsize);\n\telse\n\t\tQ_strncatz(buf, \"\\\"\\t\\\"\", bufsize);\n\n\tcurlen = strlen(buf);\n\tbuf += curlen;\n\tbufsize -= curlen;\n\tCOM_QuotedString(val, buf, bufsize, true);\n\n\tQ_strncatz(buf, \"\\\"\\n\", bufsize);\n}\nstatic void PM_WriteInstalledPackage_v3(package_t *p, char *buf, size_t bufsize)\n{\n\tstruct packagedep_s *dep;\n\n\tbuf[0] = '{';\n\tbuf[1] = '\\n';\n\tbuf[2] = 0;\n\tCOM_QuotedKeyVal(\"package\", p->name, buf, bufsize);\n\tCOM_QuotedKeyVal(\"category\", p->category, buf, bufsize);\n\tif (p->flags & DPF_ENABLED)\n\t\tCOM_QuotedKeyVal(\"enabled\", \"1\", buf, bufsize);\n\tif (p->flags & DPF_GUESSED)\n\t\tCOM_QuotedKeyVal(\"guessed\", \"1\", buf, bufsize);\n\tif (p->flags & DPF_TRUSTED)\n\t\tCOM_QuotedKeyVal(\"trusted\", \"1\", buf, bufsize);\n\tif (*p->title && strcmp(p->title, p->name))\n\t\tCOM_QuotedKeyVal(\"title\", p->title, buf, bufsize);\n\tif (*p->version)\n\t\tCOM_QuotedKeyVal(\"ver\", p->version, buf, bufsize);\n\tCOM_QuotedKeyVal(\"gamedir\", p->gamedir, buf, bufsize);\n\tif (p->qhash)\n\t\tCOM_QuotedKeyVal(\"qhash\", p->qhash, buf, bufsize);\n\tif (p->priority!=PM_DEFAULTPRIORITY)\n\t\tCOM_QuotedKeyVal(\"priority\", va(\"%i\", p->priority), buf, bufsize);\n\tif (p->arch)\n\t\tCOM_QuotedKeyVal(\"arch\", p->arch, buf, bufsize);\n\n\tif (p->license)\n\t\tCOM_QuotedKeyVal(\"license\", p->license, buf, bufsize);\n\tif (p->website)\n\t\tCOM_QuotedKeyVal(\"website\", p->website, buf, bufsize);\n\tif (p->author)\n\t\tCOM_QuotedKeyVal(\"author\", p->author, buf, bufsize);\n\tif (p->description)\n\t\tCOM_QuotedKeyVal(\"desc\", p->description, buf, bufsize);\n\tif (p->previewimage)\n\t\tCOM_QuotedKeyVal(\"preview\", p->previewimage, buf, bufsize);\n\tif (p->filesize)\n\t\tCOM_QuotedKeyVal(\"filesize\", va(\"%\"PRIu64, p->filesize), buf, bufsize);\n\n\tif (p->fsroot == FS_BINARYPATH)\n\t\tCOM_QuotedKeyVal(\"root\", \"bin\", buf, bufsize);\n\telse if (p->fsroot == FS_LIBRARYPATH)\n\t\tCOM_QuotedKeyVal(\"root\", \"lib\", buf, bufsize);\n\tif (p->packprefix)\n\t\tCOM_QuotedKeyVal(\"packprefix\", p->packprefix, buf, bufsize);\n\n\tfor (dep = p->deps; dep; dep = dep->next)\n\t{\n\t\tsafeswitch(dep->dtype)\n\t\t{\n\t\tcase DEP_FILE:\t\t\tCOM_QuotedKeyVal(\"file\",\t\tdep->name, buf, bufsize); break;\n\t\tcase DEP_CACHEFILE:\t\tCOM_QuotedKeyVal(\"cachefile\",\tdep->name, buf, bufsize); break;\n\t\tcase DEP_MAP:\t\t\tCOM_QuotedKeyVal(\"map\",\t\t\tdep->name, buf, bufsize); break;\n\t\tcase DEP_REQUIRE:\t\tCOM_QuotedKeyVal(\"depend\",\t\tdep->name, buf, bufsize); break;\n\t\tcase DEP_CONFLICT:\t\tCOM_QuotedKeyVal(\"conflict\",\tdep->name, buf, bufsize); break;\n\t\tcase DEP_REPLACE:\t\tCOM_QuotedKeyVal(\"replace\",\t\tdep->name, buf, bufsize); break;\n\t\tcase DEP_FILECONFLICT:\tCOM_QuotedKeyVal(\"fileconflict\",dep->name, buf, bufsize); break;\n\t\tcase DEP_RECOMMEND:\t\tCOM_QuotedKeyVal(\"recommend\",\tdep->name, buf, bufsize); break;\n\t\tcase DEP_NEEDFEATURE:\tCOM_QuotedKeyVal(\"need\",\t\tdep->name, buf, bufsize); break;\n\t\tcase DEP_SUGGEST:\t\tCOM_QuotedKeyVal(\"suggest\",\t\tdep->name, buf, bufsize); break;\n\t\tcase DEP_SOURCE:\t\tCOM_QuotedKeyVal(\"source\",\t\tdep->name, buf, bufsize); break;\n\t\tcase DEP_EXTRACTNAME:\tCOM_QuotedKeyVal(\"unzipfile\",\tdep->name, buf, bufsize); break;\n\t\tsafedefault:\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tif (p->flags & DPF_TESTING)\n\t\tCOM_QuotedKeyVal(\"test\", \"1\", buf, bufsize);\n\n\tif ((p->flags & DPF_AUTOMARKED) && !(p->flags & DPF_USERMARKED))\n\t\tCOM_QuotedKeyVal(\"auto\", \"1\", buf, bufsize);\n\n\tQ_strncatz(buf, \"}\", bufsize);\n\tQ_strncatz(buf, \"\\n\", bufsize);\n}\nstatic void PM_WriteInstalledPackage_v2(package_t *p, char *buf, size_t bufsize)\n{\n\tstruct packagedep_s *dep;\n\n\tbuf[0] = 0;\n\tCOM_QuotedString(va(\"%s%s\", p->category, p->name), buf, bufsize, false);\n\tif (p->flags & DPF_ENABLED)\n\t{\t//v3+\n\t\tQ_strncatz(buf, \" \", bufsize);\n\t\tCOM_QuotedConcat(va(\"enabled=1\"), buf, bufsize);\n\t}\n\telse\n\t{\t//v2\n\t\tQ_strncatz(buf, \" \", bufsize);\n\t\tCOM_QuotedConcat(va(\"stale=1\"), buf, bufsize);\n\t}\n\tif (p->flags & DPF_TRUSTED)\n\t{\t//v3+. was signed when installed.\n\t\tQ_strncatz(buf, \" \", bufsize);\n\t\tCOM_QuotedConcat(va(\"trusted=1\"), buf, bufsize);\n\t}\n\tif (p->flags & DPF_GUESSED)\n\t{\n\t\tQ_strncatz(buf, \" \", bufsize);\n\t\tCOM_QuotedConcat(va(\"guessed=1\"), buf, bufsize);\n\t}\n\tif (*p->title && strcmp(p->title, p->name))\n\t{\n\t\tQ_strncatz(buf, \" \", bufsize);\n\t\tCOM_QuotedConcat(va(\"title=%s\", p->title), buf, bufsize);\n\t}\n\tif (*p->version)\n\t{\n\t\tQ_strncatz(buf, \" \", bufsize);\n\t\tCOM_QuotedConcat(va(\"ver=%s\", p->version), buf, bufsize);\n\t}\n\t//if (*p->gamedir)\n\t{\n\t\tQ_strncatz(buf, \" \", bufsize);\n\t\tCOM_QuotedConcat(va(\"gamedir=%s\", p->gamedir), buf, bufsize);\n\t}\n\tif (p->qhash)\n\t{\n\t\tQ_strncatz(buf, \" \", bufsize);\n\t\tCOM_QuotedConcat(va(\"qhash=%s\", p->qhash), buf, bufsize);\n\t}\n\tif (p->priority!=PM_DEFAULTPRIORITY)\n\t{\n\t\tQ_strncatz(buf, \" \", bufsize);\n\t\tCOM_QuotedConcat(va(\"priority=%i\", p->priority), buf, bufsize);\n\t}\n\tif (p->arch)\n\t{\n\t\tQ_strncatz(buf, \" \", bufsize);\n\t\tCOM_QuotedConcat(va(\"arch=%s\", p->arch), buf, bufsize);\n\t}\n\n\tif (p->license)\n\t{\n\t\tQ_strncatz(buf, \" \", bufsize);\n\t\tCOM_QuotedConcat(va(\"license=%s\", p->license), buf, bufsize);\n\t}\n\tif (p->website)\n\t{\n\t\tQ_strncatz(buf, \" \", bufsize);\n\t\tCOM_QuotedConcat(va(\"website=%s\", p->website), buf, bufsize);\n\t}\n\tif (p->author)\n\t{\n\t\tQ_strncatz(buf, \" \", bufsize);\n\t\tCOM_QuotedConcat(va(\"author=%s\", p->author), buf, bufsize);\n\t}\n\tif (p->description)\n\t{\n\t\tQ_strncatz(buf, \" \", bufsize);\n\t\tCOM_QuotedConcat(va(\"desc=%s\", p->description), buf, bufsize);\n\t}\n\tif (p->previewimage)\n\t{\n\t\tQ_strncatz(buf, \" \", bufsize);\n\t\tCOM_QuotedConcat(va(\"preview=%s\", p->previewimage), buf, bufsize);\n\t}\n\tif (p->filesize)\n\t{\n\t\tQ_strncatz(buf, \" \", bufsize);\n\t\tCOM_QuotedConcat(va(\"filesize=%\"PRIu64, p->filesize), buf, bufsize);\n\t}\n\n\tif (p->fsroot == FS_BINARYPATH)\n\t{\n\t\tQ_strncatz(buf, \" \", bufsize);\n\t\tCOM_QuotedConcat(\"root=bin\", buf, bufsize);\n\t}\n\tif (p->packprefix)\n\t{\n\t\tQ_strncatz(buf, \" \", bufsize);\n\t\tCOM_QuotedConcat(va(\"packprefix=%s\", p->packprefix), buf, bufsize);\n\t}\n\n\tfor (dep = p->deps; dep; dep = dep->next)\n\t{\n\t\tif (dep->dtype == DEP_FILE)\n\t\t{\n\t\t\tQ_strncatz(buf, \" \", bufsize);\n\t\t\tCOM_QuotedConcat(va(\"file=%s\", dep->name), buf, bufsize);\n\t\t}\n\t\telse if (dep->dtype == DEP_CACHEFILE)\n\t\t{\n\t\t\tQ_strncatz(buf, \" \", bufsize);\n\t\t\tCOM_QuotedConcat(va(\"cachefile=%s\", dep->name), buf, bufsize);\n\t\t}\n\t\telse if (dep->dtype == DEP_MAP)\n\t\t{\n\t\t\tQ_strncatz(buf, \" \", bufsize);\n\t\t\tCOM_QuotedConcat(va(\"map=%s\", dep->name), buf, bufsize);\n\t\t}\n\t\telse if (dep->dtype == DEP_REQUIRE)\n\t\t{\n\t\t\tQ_strncatz(buf, \" \", bufsize);\n\t\t\tCOM_QuotedConcat(va(\"depend=%s\", dep->name), buf, bufsize);\n\t\t}\n\t\telse if (dep->dtype == DEP_CONFLICT)\n\t\t{\n\t\t\tQ_strncatz(buf, \" \", bufsize);\n\t\t\tCOM_QuotedConcat(va(\"conflict=%s\", dep->name), buf, bufsize);\n\t\t}\n\t\telse if (dep->dtype == DEP_REPLACE)\n\t\t{\n\t\t\tQ_strncatz(buf, \" \", bufsize);\n\t\t\tCOM_QuotedConcat(va(\"replace=%s\", dep->name), buf, bufsize);\n\t\t}\n\t\telse if (dep->dtype == DEP_FILECONFLICT)\n\t\t{\n\t\t\tQ_strncatz(buf, \" \", bufsize);\n\t\t\tCOM_QuotedConcat(va(\"fileconflict=%s\", dep->name), buf, bufsize);\n\t\t}\n\t\telse if (dep->dtype == DEP_RECOMMEND)\n\t\t{\n\t\t\tQ_strncatz(buf, \" \", bufsize);\n\t\t\tCOM_QuotedConcat(va(\"recommend=%s\", dep->name), buf, bufsize);\n\t\t}\n\t\telse if (dep->dtype == DEP_NEEDFEATURE)\n\t\t{\n\t\t\tQ_strncatz(buf, \" \", bufsize);\n\t\t\tCOM_QuotedConcat(va(\"need=%s\", dep->name), buf, bufsize);\n\t\t}\n\t}\n\n\tif (p->flags & DPF_TESTING)\n\t{\n\t\tQ_strncatz(buf, \" \", bufsize);\n\t\tCOM_QuotedConcat(\"test=1\", buf, bufsize);\n\t}\n\n\tif ((p->flags & DPF_AUTOMARKED) && !(p->flags & DPF_USERMARKED))\n\t{\n\t\tQ_strncatz(buf, \" \", bufsize);\n\t\tCOM_QuotedConcat(\"auto\", buf, bufsize);\n\t}\n\n\tbuf[bufsize-2] = 0;\t//just in case.\n\tQ_strncatz(buf, \"\\n\", bufsize);\n}\nstatic void PM_WriteInstalledPackages(void)\n{\n\tchar buf[65536];\n\tint i;\n\tchar *s;\n\tpackage_t *p;\n\tvfsfile_t *f = FS_OpenVFS(INSTALLEDFILES, \"wbp\", FS_ROOT);\n\tqboolean v3 = false;\n\tif (!f)\n\t{\n\t\tif (FS_DisplayPath(INSTALLEDFILES, FS_ROOT, buf, sizeof(buf)))\n\t\t\tCon_Printf(\"package manager: Can't write %s\\n\", buf);\n\t\telse\n\t\t\tCon_Printf(\"package manager: Can't update installed list\\n\");\n\t\treturn;\n\t}\n\n\tif (v3)\n\t\ts = \"version 3\\n\";\n\telse\n\t\ts = \"version 2\\n\";\n\tVFS_WRITE(f, s, strlen(s));\n\n\ts = va(\"set updatemode %s\\n\", COM_QuotedString(pkg_autoupdate.string, buf, sizeof(buf), false));\n\tVFS_WRITE(f, s, strlen(s));\n\ts = va(\"set declined %s\\n\", COM_QuotedString(declinedpackages?declinedpackages:\"\", buf, sizeof(buf), false));\n\tVFS_WRITE(f, s, strlen(s));\n\n\tfor (i = 0; i < pm_numsources; i++)\n\t{\n\t\tchar *status;\n\t\tif (!(pm_source[i].flags & (SRCFL_DISABLED|SRCFL_ENABLED)))\n\t\t\tcontinue;\t//don't bother saving sources which the user has neither confirmed nor denied.\n\n\t\tif (pm_source[i].flags & SRCFL_ENABLED)\n\t\t\tstatus = \"enabled\";\t//anything else is enabled\n\t\telse\n\t\t\tstatus = \"disabled\";\n\n\t\tif (pm_source[i].flags & SRCFL_USER)\t//make sure it works.\n\t\t\ts = va(\"sublist \\\"%s\\\" \\\"%s\\\" \\\"%s\\\"\\n\", pm_source[i].url, pm_source[i].prefix, status);\n\t\telse\t//will be 'historic' when loaded\n\t\t\ts = va(\"source \\\"%s\\\" \\\"%s\\\"\\n\", pm_source[i].url, status);\n\t\tVFS_WRITE(f, s, strlen(s));\n\t}\n\n\tfor (p = availablepackages; p ; p=p->next)\n\t{\n\t\tif (p->flags & (DPF_PRESENT|DPF_ENABLED))\n\t\t{\n\t\t\tif (v3)\n\t\t\t\tPM_WriteInstalledPackage_v3(p, buf, sizeof(buf));\n\t\t\telse\n\t\t\t\tPM_WriteInstalledPackage_v2(p, buf, sizeof(buf));\n\n\t\t\tVFS_WRITE(f, buf, strlen(buf));\n\t\t}\n\t}\n\n\tVFS_CLOSE(f);\n}\n\n//package has been downloaded and installed, but some packages need to be enabled\n//(plugins might have other dll dependancies, so this can only happen AFTER the entire package was extracted)\nstatic void PM_PackageEnabled(package_t *p)\n{\n\tchar ext[8];\n\tstruct packagedep_s *dep;\n#ifdef HAVEAUTOUPDATE\n\tstruct packagedep_s *ef = NULL;\n#endif\n\tFS_FlushFSHashFull();\n\tfor (dep = p->deps; dep; dep = dep->next)\n\t{\n\t\tif (dep->dtype != DEP_FILE && dep->dtype != DEP_CACHEFILE)\n\t\t\tcontinue;\n\t\tCOM_FileExtension(dep->name, ext, sizeof(ext));\n/*this is now done after all downloads completed, so we don't keep re-execing configs nor forgetting that they changed.\n\t\tif (!pm_packagesinstalled)\n\t\tif (!stricmp(ext, \"pak\") || !stricmp(ext, \"pk3\") || !stricmp(ext, \"zip\"))\n\t\t{\n\t\t\tif (pm_packagesinstalled)\n\t\t\t{\n\t\t\t\tpm_packagesinstalled = false;\n\t\t\t\tFS_ChangeGame(fs_manifest, true, false);\n\t\t\t}\n\t\t\telse\n\t\t\t\tFS_ReloadPackFiles();\n\t\t}*/\n#ifdef PLUGINS\n\t\tif ((p->flags & DPF_PLUGIN) && !Q_strncasecmp(dep->name, PLUGINPREFIX, strlen(PLUGINPREFIX)))\n\t\t\tCmd_ExecuteString(va(\"plug_load %s\\n\", dep->name), RESTRICT_LOCAL);\n#endif\n#ifdef MENU_DAT\n\t\tif (!Q_strcasecmp(dep->name, \"menu.dat\"))\n\t\t\tCmd_ExecuteString(\"menu_restart\\n\", RESTRICT_LOCAL);\n#endif\n#ifdef HAVEAUTOUPDATE\n\t\tif (p->flags & DPF_ENGINE)\n\t\t\tef = dep;\n#endif\n\t}\n\n#ifdef HAVEAUTOUPDATE\n\t//this is an engine update (with installed file) and marked.\n\tif (ef && (p->flags & DPF_MARKED))\n\t{\n\t\tchar native[MAX_OSPATH];\n\t\tpackage_t *othr;\n\t\t//make sure there's no more recent build that's also enabled...\n\t\tfor (othr = availablepackages; othr ; othr=othr->next)\n\t\t{\n\t\t\tif ((othr->flags & DPF_ENGINE) && (othr->flags & DPF_MARKED) && othr->flags & (DPF_PRESENT|DPF_ENABLED) && othr != p)\n\t\t\t\tif (strcmp(p->version, othr->version) >= 0)\n\t\t\t\t\treturn;\n\t\t}\n\n#ifndef HAVE_CLIENT\n#define Menu_Prompt(cb,ctx,msg,yes,no,cancel,highpri) Con_Printf(CON_WARNING \"%s\\n\", msg)\n#endif\n\n\t\tif (FS_SystemPath(ef->name, p->fsroot, native, sizeof(native)) && Sys_SetUpdatedBinary(native))\n\t\t{\n\t\t\tQ_strncpyz(enginerevision, p->version, sizeof(enginerevision));\t//make sure 'revert' picks up the new binary...\n\t\t\tMenu_Prompt(NULL, NULL, localtext(\"Engine binary updated.\\nRestart to use.\"), NULL, NULL, NULL, true);\n\t\t}\n\t\telse\n\t\t\tMenu_Prompt(NULL, NULL, localtext(\"Engine update failed.\\nManual update required.\"), NULL, NULL, NULL, true);\n\t}\n#endif\n}\n\n#ifdef WEBCLIENT\n//callback from PM_Download_Got, extracts each file from an archive\nstatic int QDECL PM_ExtractFiles(const char *fname, qofs_t fsize, time_t mtime, void *parm, searchpathfuncs_t *spath)\n{\t//this is gonna suck. threading would help, but gah.\n\tpackage_t *p = parm;\n\tflocation_t loc;\n\tif (fname[strlen(fname)-1] == '/')\n\t{\t//directory.\n\n\t}\n\telse if (spath->FindFile(spath, &loc, fname, NULL) && loc.len < 0x80000000u)\n\t{\n\t\tchar *f = malloc(loc.len);\n\t\tconst char *n;\n\t\tif (f)\n\t\t{\n\t\t\tspath->ReadFile(spath, &loc, f);\n\t\t\tif (*p->gamedir)\n\t\t\t\tn = va(\"%s/%s\", p->gamedir, fname);\n\t\t\telse\n\t\t\t\tn = fname;\n\t\t\tif (FS_WriteFile(n, f, loc.len, p->fsroot))\n\t\t\t\tp->flags |= DPF_NATIVE|DPF_ENABLED;\n\n\t\t\t//keep track of the installed files, so we can delete them properly after.\n\t\t\tPM_AddDep(p, DEP_FILE, fname);\n\t\t}\n\t\tfree(f);\n\t}\n\treturn 1;\n}\n\nstatic qboolean PM_Download_Got_Extract(package_t *p, searchpathfuncs_t *archive, const char *tempname, enum fs_relative temproot)\n{\t//EXTRACT_EXPLICITZIP is very special.\n\tqboolean success = false;\n\tchar displayname[MAX_OSPATH];\n\tchar ext[8];\n\tchar *destname;\n\tstruct packagedep_s *dep, *srcname = p->deps;\n\n\tfor (dep = p->deps; dep; dep = dep->next)\n\t{\n\t\tunsigned int nfl;\n\t\tif (dep->dtype != DEP_FILE && dep->dtype != DEP_CACHEFILE)\n\t\t\tcontinue;\n\n\t\tCOM_FileExtension(dep->name, ext, sizeof(ext));\n\t\tif (!stricmp(ext, \"pak\") || !stricmp(ext, \"pk3\") || !stricmp(ext, \"zip\"))\n\t\t\tFS_UnloadPackFiles();\t//we reload them after\n#ifdef PLUGINS\n\t\tif ((!stricmp(ext, \"dll\") || !stricmp(ext, \"so\")) && !Q_strncmp(dep->name, PLUGINPREFIX, strlen(PLUGINPREFIX)))\n\t\t\tCmd_ExecuteString(va(\"plug_close %s\\n\", dep->name), RESTRICT_LOCAL);\t//try to purge plugins so there's no files left open\n#endif\n\n\t\tif (dep->dtype == DEP_CACHEFILE)\n\t\t{\n\t\t\tnfl = DPF_CACHED;\n\t\t\tdestname = va(\"downloads/%s\", dep->name);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tnfl = DPF_NATIVE;\n\t\t\tif (!*p->gamedir)\t//basedir\n\t\t\t\tdestname = dep->name;\n\t\t\telse\n\t\t\t{\n\t\t\t\tchar temp[MAX_OSPATH];\n\t\t\t\tdestname = va(\"%s/%s\", p->gamedir, dep->name);\n\t\t\t\tif (PM_TryGenCachedName(destname, p, temp, sizeof(temp)))\n\t\t\t\t{\n\t\t\t\t\tnfl = DPF_CACHED;\n\t\t\t\t\tdestname = va(\"%s\", temp);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (p->flags & DPF_MARKED)\n\t\t\tnfl |= DPF_ENABLED;\n\t\tnfl |= (p->flags & ~(DPF_CACHED|DPF_NATIVE|DPF_CORRUPT));\n\t\tFS_CreatePath(destname, p->fsroot);\n\t\tif (FS_Remove(destname, p->fsroot))\n\t\t\t;\n\t\tif (p->extract == EXTRACT_EXPLICITZIP)\n\t\t{\n\t\t\twhile (srcname && srcname->dtype != DEP_EXTRACTNAME)\n\t\t\t\tsrcname = srcname->next;\n\t\t\tif (!archive)\n\t\t\t\tCon_Printf(CON_ERROR\"archive %s is not an archive!\\n\", tempname);\n\t\t\telse if (!srcname)\n\t\t\t\tCon_Printf(CON_ERROR\"archive %s is EXTRACT_EXPLICITZIP but filename not specified!\\n\", tempname);\n\t\t\telse\n\t\t\t{\n\t\t\t\tflocation_t loc;\n\n\t\t\t\tif (archive->FindFile(archive, &loc, srcname->name, NULL)==FF_FOUND && loc.len < 0x80000000u)\n\t\t\t\t{\n\t\t\t\t\tchar *f = malloc(loc.len);\n\t\t\t\t\tif (f)\n\t\t\t\t\t{\n\t\t\t\t\t\tarchive->ReadFile(archive, &loc, f);\n\t\t\t\t\t\tif (FS_WriteFile(destname, f, loc.len, p->fsroot))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (!FS_DisplayPath(destname, p->fsroot, displayname, sizeof(displayname)))\n\t\t\t\t\t\t\t\tQ_strncpyz(displayname, destname, sizeof(displayname));\n\t\t\t\t\t\t\tCon_Printf(CON_WARNING\"Extracted %s (to %s)\\n\", p->name, displayname);\n\n\t\t\t\t\t\t\tp->flags = nfl;\n\t\t\t\t\t\t\tsuccess = true;\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (!FS_DisplayPath(destname, p->fsroot, displayname, sizeof(displayname)))\n\t\t\t\t\t\t\t\tQ_strncpyz(displayname, destname, sizeof(displayname));\n\t\t\t\t\t\t\tCon_Printf(CON_ERROR\"Failed to write %s (extracting %s)\\n\", displayname, p->name);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tCon_Printf(CON_ERROR\"Failed to find %s in archive %s\\n\", srcname->name, tempname);\n\t\t\t}\n\n\t\t\tif (!FS_DisplayPath(destname, p->fsroot, displayname, sizeof(displayname)))\n\t\t\t\tQ_strncpyz(displayname, destname, sizeof(displayname));\n\t\t\tCon_Printf(CON_ERROR\"Couldn't extract %s/%s to %s. Removed instead.\\n\", tempname, dep->name, displayname);\n\n\t\t\tsuccess = false;\n\t\t}\n\t\telse if (!FS_Rename2(tempname, destname, temproot, p->fsroot))\n\t\t{\n\t\t\t//error!\n\t\t\tif (!FS_DisplayPath(destname, p->fsroot, displayname, sizeof(displayname)))\n\t\t\t\tQ_strncpyz(displayname, destname, sizeof(displayname));\n\t\t\tCon_Printf(CON_ERROR\"Couldn't rename %s to %s. Removed instead.\\n\", tempname, displayname);\n\n\t\t\tsuccess = false;\n\t\t}\n\t\telse\n\t\t{\t//success!\n\t\t\tif (!FS_DisplayPath(destname, p->fsroot, displayname, sizeof(displayname)))\n\t\t\t\tQ_strncpyz(displayname, destname, sizeof(displayname));\n\t\t\tCon_Printf(\"Downloaded %s (to %s)\\n\", p->name, displayname);\n\n\t\t\tp->flags = nfl;\n\n\t\t\tsuccess = true;\n\t\t}\n\t}\n\n\treturn success;\n}\n\nstatic qboolean PM_DownloadSharesSource(package_t *p1, package_t *p2)\n{\n\tif (p1 == p2)\n\t\treturn false;\t//well yes... but no. just no.\n\tif (p1->extract != p2->extract)\n\t\treturn false;\n\tif (p1->extract != EXTRACT_EXPLICITZIP)\n\t\treturn false;\t//others can't handle it, or probably don't make much sense. don't try, too unsafe/special-case.\n\t//these are on the download, not the individual files to extract, awkwardly enough.\n\tif (p1->filesize != p2->filesize)\n\t\treturn false;\n\tif (!p1->filesha1 != !p2->filesha1)\n\t\treturn false;\n\tif (p1->filesha1 && strcmp(p1->filesha1, p2->filesha1))\n\t\treturn false;\n\tif (!p1->filesha512 != !p2->filesha512)\n\t\treturn false;\n\tif (p1->filesha512 && strcmp(p1->filesha512, p2->filesha512))\n\t\treturn false;\n\n\treturn true;\n}\n\nstatic void PM_StartADownload(void);\ntypedef struct\n{\n\tpackage_t *p;\n\tqboolean successful;\n\tchar *tempname;\t//z_strduped string, so needs freeing.\n\tenum fs_relative temproot;\n\tchar localname[256];\n\tchar url[256];\n} pmdownloadedinfo_t;\n//callback from PM_StartADownload\nstatic void PM_Download_Got(int iarg, void *data)\n{\n\tpmdownloadedinfo_t *info = data;\n\tqboolean successful = info->successful;\n\tpackage_t *p, *p2;\n\tchar *tempname = info->tempname;\n\tconst enum fs_relative temproot = info->temproot;\n\n\tfor (p = availablepackages; p ; p=p->next)\n\t{\n\t\tif (p == info->p)\n\t\t\tbreak;\n\t}\n\tpm_packagesinstalled=true;\n\n\tif (p)\n\t{\n\t\tp->flags &= ~DPF_PENDING;\n\t\tp->curdownload = NULL;\t//just in case.\n\n\t\tif (!successful)\n\t\t{\n\t\t\tCon_Printf(CON_ERROR\"Couldn't download %s (from %s)\\n\", p->name, info->url);\n\t\t\tFS_Remove (tempname, temproot);\n\t\t\tZ_Free(tempname);\n\t\t\tPM_StartADownload();\n\t\t\treturn;\n\t\t}\n\n\t\tif (p->extract == EXTRACT_ZIP)\n\t\t{\n\t\t\tsearchpathfuncs_t *archive = NULL;\n#ifdef PACKAGE_PK3\n\t\t\tvfsfile_t *f = FS_OpenVFS(tempname, \"rb\", temproot);\n\t\t\tif (f)\n\t\t\t{\n\t\t\t\tarchive = FSZIP_LoadArchive(f, NULL, tempname, tempname, NULL);\n\t\t\t\tif (!archive)\n\t\t\t\t\tVFS_CLOSE(f);\n\t\t\t}\n#else\n\t\t\tCon_Printf(\"zip format not supported in this build - %s (from %s)\\n\", p->name, dl->url);\n#endif\n\t\t\tif (archive)\n\t\t\t{\n\t\t\t\tp->flags &= ~(DPF_NATIVE|DPF_CACHED|DPF_CORRUPT|DPF_ENABLED);\n\t\t\t\t//FIXME: add an EnumerateAllFiles function or something.\n\t\t\t\t//can't scan for directories - paks don't have em, zips don't guarentee em either.\n\t\t\t\tarchive->EnumerateFiles(archive, \"*\", PM_ExtractFiles, p);\n\t\t\t\tarchive->EnumerateFiles(archive, \"*/*\", PM_ExtractFiles, p);\n\t\t\t\tarchive->EnumerateFiles(archive, \"*/*/*\", PM_ExtractFiles, p);\n\t\t\t\tarchive->EnumerateFiles(archive, \"*/*/*/*\", PM_ExtractFiles, p);\n\t\t\t\tarchive->EnumerateFiles(archive, \"*/*/*/*/*\", PM_ExtractFiles, p);\n\t\t\t\tarchive->EnumerateFiles(archive, \"*/*/*/*/*/*\", PM_ExtractFiles, p);\n\t\t\t\tarchive->EnumerateFiles(archive, \"*/*/*/*/*/*/*\", PM_ExtractFiles, p);\n\t\t\t\tarchive->EnumerateFiles(archive, \"*/*/*/*/*/*/*/*\", PM_ExtractFiles, p);\n\t\t\t\tarchive->EnumerateFiles(archive, \"*/*/*/*/*/*/*/*/*\", PM_ExtractFiles, p);\n\t\t\t\tarchive->ClosePath(archive);\n\n\t\t\t\tPM_WriteInstalledPackages();\n\n//\t\t\t\t\tif (!stricmp(ext, \"pak\") || !stricmp(ext, \"pk3\"))\n//\t\t\t\t\t\tFS_ReloadPackFiles();\n\t\t\t}\n\t\t\tPM_ValidatePackage(p);\n\t\t\tFS_Remove (tempname, temproot);\n\t\t\tZ_Free(tempname);\n\t\t\tp->trymirrors = 0;\t//we're done... don't download it again!\n\t\t\tPM_StartADownload();\n\t\t\treturn;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tqboolean success = false;\n\t\t\tsearchpathfuncs_t *archive = NULL;\n#ifdef PACKAGE_PK3\n\t\t\tif (p->extract == EXTRACT_EXPLICITZIP)\n\t\t\t{\n\t\t\t\tvfsfile_t *f = FS_OpenVFS(tempname, \"rb\", temproot);\n\t\t\t\tif (f)\n\t\t\t\t{\n\t\t\t\t\tarchive = FSZIP_LoadArchive(f, NULL, tempname, tempname, NULL);\n\t\t\t\t\tif (!archive)\n\t\t\t\t\t\tVFS_CLOSE(f);\n\t\t\t\t}\n\t\t\t\tsuccess = PM_Download_Got_Extract(p, archive, tempname, temproot);\n\n\t\t\t\tif (success)\n\t\t\t\tfor (p2 = availablepackages; p2; p2=p2->next)\n\t\t\t\t{\n\t\t\t\t\tif ((p2->flags&DPF_PENDING) ||\t//only if they've not already started downloading separately...\n\t\t\t\t\t\t!p2->trymirrors\t//ignore ones that are not pending.\n\t\t\t\t\t\t)\n\t\t\t\t\t\tcontinue;\n\n\t\t\t\t\tif (PM_DownloadSharesSource(p, p2))\n\t\t\t\t\t\tif (PM_Download_Got_Extract(p2, archive, tempname, temproot))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tp2->trymirrors = false;\t//already did it. mwahaha.\n\t\t\t\t\t\t\tPM_ValidatePackage(p2);\n\t\t\t\t\t\t\tPM_PackageEnabled(p2);\n\t\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n#endif\n\t\t\t\tsuccess = PM_Download_Got_Extract(p, archive, tempname, temproot);\n\t\t\tif (archive)\n\t\t\t\tarchive->ClosePath(archive);\n\t\t\tif (p->extract == EXTRACT_EXPLICITZIP || !success)\n\t\t\t\tFS_Remove (tempname, temproot);\n\t\t\tif (success)\n\t\t\t{\n\t\t\t\tPM_ValidatePackage(p);\n\n\t\t\t\tPM_PackageEnabled(p);\n\t\t\t\tPM_WriteInstalledPackages();\n\n\t\t\t\tZ_Free(tempname);\n\n\t\t\t\tp->trymirrors = 0;\t//we're done with this one... don't download it from another mirror!\n\t\t\t\tPM_StartADownload();\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tCon_Printf(CON_ERROR\"PM_Download_Got: %s has no filename info\\n\", p->name);\n\t}\n\telse\n\t\tCon_Printf(CON_ERROR\"PM_Download_Got: Can't figure out where \\\"%s\\\"(%s) came from (url: %s)\\n\", info->localname, tempname, info->url);\n\n\tFS_Remove (tempname, temproot);\n\tZ_Free(tempname);\n\tPM_StartADownload();\n}\nstatic void PM_Download_PreliminaryGot(struct dl_download *dl)\n{\t//this function is annoying.\n\t//we're on the mainthread, but we might still be waiting for some other thread to complete\n\t//there could be loads of stuff on the callstack. lots of stuff that could get annoyed if we're restarting the entire filesystem, for instance.\n\t//so set up a SECOND callback using a different mechanism...\n\n\tpmdownloadedinfo_t info;\n\tinfo.tempname = dl->user_ctx;\n\tinfo.temproot = dl->user_num;\n\n\tQ_strncpyz(info.url, dl->url, sizeof(info.url));\n\tQ_strncpyz(info.localname, dl->localname, sizeof(info.localname));\n\n\tfor (info.p = availablepackages; info.p ; info.p=info.p->next)\n\t{\n\t\tif (info.p->curdownload == dl)\n\t\t{\n\t\t\tinfo.p->curdownload = NULL;\t//have to clear it here, because this pointer will no longer be valid after (which could result in segfaults)\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tinfo.successful = (dl->status == DL_FINISHED);\n\tif (dl->file)\n\t{\n\t\tif (!VFS_CLOSE(dl->file))\n\t\t\tinfo.successful = false;\n\t\tdl->file = NULL;\n\t}\n\telse\n\t\tinfo.successful = false;\n\n\tCmd_AddTimer(0, PM_Download_Got, 0, &info, sizeof(info));\n}\n\nstatic char *PM_GetTempName(package_t *p)\n{\n\tstruct packagedep_s *dep, *fdep;\n\tchar *destname, *t, *ts;\n\t//always favour the file so that we can rename safely without needing a copy.\n\tfor (dep = p->deps, fdep = NULL; dep; dep = dep->next)\n\t{\n\t\tif (dep->dtype == DEP_FILE || dep->dtype == DEP_CACHEFILE)\n\t\t{\n\t\t\tif (fdep)\n\t\t\t{\n\t\t\t\tfdep = NULL;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tfdep = dep;\n\t\t}\n\t}\n\tif (fdep)\n\t{\n\t\tif (fdep->dtype == DEP_CACHEFILE)\n\t\t\tdestname = va(\"downloads/%s.tmp\", fdep->name);\n\t\telse if (*p->gamedir)\n\t\t\tdestname = va(\"%s/%s.tmp\", p->gamedir, fdep->name);\n\t\telse\n\t\t\tdestname = va(\"%s.tmp\", fdep->name);\n\t\treturn Z_StrDup(destname);\n\t}\n\tts = Z_StrDup(p->name);\n\tfor (t = ts; *t; t++)\n\t{\n\t\tswitch(*t)\n\t\t{\n\t\tcase '/':\n\t\tcase '?':\n\t\tcase '<':\n\t\tcase '>':\n\t\tcase '\\\\':\n\t\tcase ':':\n\t\tcase '*':\n\t\tcase '|':\n\t\tcase '\\\"':\n\t\tcase '.':\n\t\t\t*t = '_';\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tbreak;\n\t\t}\n\t}\n\tif (*ts)\n\t{\n\t\tif (*p->gamedir)\n\t\t\tdestname = va(\"%s/%s.tmp\", p->gamedir, ts);\n\t\telse\n\t\t\tdestname = va(\"%s.tmp\", ts);\n\t}\n\telse\n\t\tdestname = va(\"%x.tmp\", (unsigned int)(quintptr_t)p);\n\tZ_Free(ts);\n\treturn Z_StrDup(destname);\n}\n\n\ntypedef struct {\n\tvfsfile_t pub;\n\tvfsfile_t *f;\n\thashfunc_t *hashfunc;\n\tqofs_t sz;\n\tqofs_t needsize;\n\tqboolean fail;\n\tqbyte need[DIGEST_MAXSIZE];\n\tchar *fname;\n\tqbyte ctx[1];\n} hashfile_t;\nstatic int QDECL HashFile_WriteBytes (struct vfsfile_s *file, const void *buffer, int bytestowrite)\n{\n\thashfile_t *f = (hashfile_t*)file;\n\tf->hashfunc->process(f->ctx, buffer, bytestowrite);\n\tif (bytestowrite != VFS_WRITE(f->f, buffer, bytestowrite))\n\t\tf->fail = true;\t//something went wrong.\n\tif (f->fail)\n\t\treturn -1;\t//error! abort! fail! give up!\n\tf->sz += bytestowrite;\n\treturn bytestowrite;\n}\nstatic void QDECL HashFile_Flush (struct vfsfile_s *file)\n{\n\thashfile_t *f = (hashfile_t*)file;\n\tVFS_FLUSH(f->f);\n}\nstatic qboolean QDECL HashFile_Close (struct vfsfile_s *file)\n{\n\tqbyte digest[DIGEST_MAXSIZE];\n\thashfile_t *f = (hashfile_t*)file;\n\tif (!VFS_CLOSE(f->f))\n\t\tf->fail = true;\t//something went wrong.\n\tf->f = NULL;\n\n\tf->hashfunc->terminate(digest, f->ctx);\n\tif (f->fail)\n\t\tCon_Printf(\"Filesystem problem saving %s during download\\n\", f->fname);\t//don't error if we failed on actual disk problems\n\telse if (f->sz != f->needsize)\n\t{\n\t\tCon_Printf(\"Download truncated: %s\\n\", f->fname);\t//don't error if we failed on actual disk problems\n\t\tf->fail = true;\n\t}\n\telse if (memcmp(digest, f->need, f->hashfunc->digestsize))\n\t{\n\t\tqbyte base64[(DIGEST_MAXSIZE*2)+16];\n\t\tCon_Printf(\"Invalid hash for downloaded file %s, try again later?\\n\", f->fname);\n\n\t\tif (f->hashfunc == &hash_sha1)\n\t\t{\n\t\t\tbase64[Base16_EncodeBlock(digest, f->hashfunc->digestsize, base64, sizeof(base64)-1)] = 0;\n\t\t\tCon_Printf(\"%s vs \", base64);\n\t\t\tbase64[Base16_EncodeBlock(f->need, f->hashfunc->digestsize, base64, sizeof(base64)-1)] = 0;\n\t\t\tCon_Printf(\"%s\\n\", base64);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tbase64[Base64_EncodeBlock(digest, f->hashfunc->digestsize, base64, sizeof(base64)-1)] = 0;\n\t\t\tCon_Printf(\"%s vs \", base64);\n\t\t\tbase64[Base64_EncodeBlock(f->need, f->hashfunc->digestsize, base64, sizeof(base64)-1)] = 0;\n\t\t\tCon_Printf(\"%s\\n\", base64);\n\t\t}\n\t\tf->fail = true;\n\t}\n\n\treturn !f->fail;\t//true if all okay!\n}\nstatic vfsfile_t *FS_Hash_ValidateWrites(vfsfile_t *f, const char *fname, qofs_t needsize, hashfunc_t *hashfunc, const char *hash)\n{\t//wraps a writable file with a layer that'll cause failures when the hash differs from what we expect.\n\tif (f)\n\t{\n\t\thashfile_t *n = Z_Malloc(sizeof(*n) + hashfunc->contextsize + strlen(fname));\n\t\tn->pub.WriteBytes = HashFile_WriteBytes;\n\t\tn->pub.Flush = HashFile_Flush;\n\t\tn->pub.Close = HashFile_Close;\n\t\tn->pub.seekstyle = SS_UNSEEKABLE;\n\t\tn->f = f;\n\t\tn->hashfunc = hashfunc;\n\t\tn->fname = n->ctx+hashfunc->contextsize;\n\t\tstrcpy(n->fname, fname);\n\t\tn->needsize = needsize;\n\t\tBase16_DecodeBlock(hash, n->need, sizeof(n->need));\n\t\tn->fail = false;\n\n\t\tn->hashfunc->init(n->ctx);\n\n\t\tf = &n->pub;\n\t}\n\treturn f;\n}\n\n//function that returns true if the package doesn't look exploity.\n//so either its a versioned package, or its got a trusted signature.\nstatic qboolean PM_SignatureOkay(package_t *p)\n{\n\tstruct packagedep_s *dep;\n\tconst char *ext;\n\n\tif (p->flags & DPF_PRESENT)\n\t\treturn true;\t//we don't know where it came from, but someone manually installed it...\n\tif (p->flags & (DPF_SIGNATUREREJECTED|DPF_SIGNATUREUNKNOWN))\t//the sign key didn't match its sha512 hash\n\t\treturn false;\t//just block it entirely.\n\tif (p->flags & DPF_SIGNATUREACCEPTED)\t//sign value is present and correct\n\t\treturn true;\t//go for it.\n\n\tif (p->flags & DPF_TRUSTED)\n\t\treturn true;\n\n\t//packages without a signature are only allowed under some limited conditions.\n\t//basically we only allow meta packages, pk3s, and paks.\n\n\t//metadata doesn't specify all file names for zips.\n\tif (p->extract == EXTRACT_ZIP)\n\t\treturn false;\n\tif (!*p->gamedir)\n\t\treturn false;\n\n\tfor (dep = p->deps; dep; dep = dep->next)\n\t{\n\t\tif (dep->dtype != DEP_FILE && dep->dtype != DEP_CACHEFILE)\n\t\t\tcontinue;\n\n\t\t//only allow .pak/.pk3/.zip without a signature, and only when they have a qhash specified (or the .fmf specified it without a qhash...).\n\t\text = COM_GetFileExtension(dep->name, NULL);\n\t\tif ((!stricmp(ext, \".kpf\") || !stricmp(ext, \".pak\") || !stricmp(ext, \".pk3\") || !stricmp(ext, \".zip\")) && (p->qhash || dep->dtype == DEP_CACHEFILE || (p->flags&DPF_MANIFEST)))\n\t\t\t;\n\t\telse\n\t\t\treturn false;\n\t}\n\treturn true;\n}\n#endif\n\n/*static void PM_AddDownloadedPackage(const char *filename)\n{\n\tchar pathname[1024];\n\tpackage_t *p;\n\tQ_snprintfz(pathname, sizeof(pathname), \"%s/%s\", \"Cached\", filename);\n\tp->name = Z_StrDup(COM_SkipPath(pathname));\n\t*COM_SkipPath(pathname) = 0;\n\tp->category = Z_StrDup(pathname);\n\n\tQ_strncpyz(p->version, \"\", sizeof(p->version));\n\n\tQ_snprintfz(p->gamedir, sizeof(p->gamedir), \"%s\", \"\");\n\tp->fsroot = FS_ROOT;\n\tp->extract = EXTRACT_COPY;\n\tp->priority = 0;\n\tp->flags = DPF_INSTALLED;\n\n\tp->title = Z_StrDup(p->name);\n\tp->arch = NULL;\n\tp->qhash = NULL; //FIXME\n\tp->description = NULL;\n\tp->license = NULL;\n\tp->author = NULL;\n\tp->previewimage = NULL;\n}*/\n\n#ifdef WEBCLIENT\nstatic size_t PM_AddFilePackage(const char *packagename, struct gamepacks *gp, size_t numgp)\n{\n\tsize_t found = 0;\n\tstruct packagedep_s *dep;\n\tpackage_t *p = PM_FindPackage(packagename);\n\tif (!p)\n\t\treturn 0;\n\n\tif (found < numgp)\n\t{\n\t\tif (p->arch)\n\t\t\tgp[found].package = Z_StrDupf(\"%s:%s=%s\", p->name, p->arch, p->version);\n\t\telse\n\t\t\tgp[found].package = Z_StrDupf(\"%s=%s\", p->name, p->version);\n\t\tgp[found].path = NULL;\n\t\tgp[found].url = p->mirror[0];\t//FIXME: random mirror.\n\t\tfor (dep = p->deps; dep; dep = dep->next)\n\t\t{\n\t\t\tif (dep->dtype == DEP_CACHEFILE)\n\t\t\t\tgp[found].path = Z_StrDupf(\"downloads/%s\", dep->name);\n\t\t}\n\t\tif (gp[found].path)\n\t\t{\n\t\t\tgp[found].subpath = p->packprefix;\n\t\t\tfound++;\n\t\t}\n\t}\n\tfor (dep = p->deps; dep; dep = dep->next)\n\t{\n\t\tif (dep->dtype == DEP_REQUIRE)\n\t\t\tfound += PM_AddFilePackage(dep->name, gp+found, numgp);\n\t}\n\treturn found;\n}\nstatic void PM_DownloadsCompleted(int iarg, void *data)\n{\t//if something installed, then make sure everything is reconfigured properly.\n\tif (pm_packagesinstalled || pm_onload.package || pm_onload.map)\n\t{\n\t\tpm_packagesinstalled = false;\n\n\t\tif (pm_onload.package)\n\t\t{\n\t\t\tpackage_t *p = PM_FindPackage(pm_onload.package);\n\t\t\tchar *map = pm_onload.map;\n\n\t\t\tstruct gamepacks packs[64];\n\t\t\tsize_t usedpacks;\n\t\t\tpm_onload.map = NULL;\n\n\t\t\tusedpacks = PM_AddFilePackage(pm_onload.package, packs, countof(packs)-1);\n\t\t\tmemset(&packs[usedpacks], 0, sizeof(packs[usedpacks]));\n\n\t\t\tZ_Free(pm_onload.package);\n\t\t\tpm_onload.package = NULL;\n\t\t\tCOM_Gamedir(p->gamedir, packs);\n\t\t\tCbuf_InsertText(map, RESTRICT_LOCAL, false);\n\t\t\tZ_Free(map);\n\t\t}\n\t\telse\n\t\t\tFS_ChangeGame(fs_manifest, true, false);\n\t}\n}\n\nstatic unsigned int PM_DownloadingCount(void)\n{\n\tpackage_t *p;\n\tunsigned int count = 0;\n\tfor (p = availablepackages; p ; p=p->next)\n\t{\n\t\tif (p->flags&DPF_PENDING)\n\t\t\tcount++;\n\t}\n\treturn count;\n}\n\n//looks for the next package that needs downloading, and grabs it\nstatic void PM_StartADownload(void)\n{\n\tvfsfile_t *tmpfile;\n\tchar *temp;\n\tenum fs_relative temproot;\n\tpackage_t *p;\n\tconst int simultaneous = 2;\n\tint i;\n\tqboolean downloading = false;\n\n\tfor (p = availablepackages; p && simultaneous > PM_DownloadingCount(); p=p->next)\n\t{\n\t\tif (p->curdownload)\n\t\t\tdownloading = true;\n\t\telse if (p->trymirrors)\n\t\t{\t//flagged for a (re?)download\n\t\t\tchar *mirror = NULL;\n\t\t\tfor (i = 0; i < countof(p->mirror); i++)\n\t\t\t{\n\t\t\t\tif (p->mirror[i] && (p->trymirrors & (1u<<i)))\n\t\t\t\t{\n\t\t\t\t\tmirror = p->mirror[i];\n\t\t\t\t\tp->trymirrors &= ~(1u<<i);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!mirror)\n\t\t\t{\t//erk...\n\t\t\t\tp->trymirrors = 0;\n\n\t\t\t\tfor (i = 0; i < countof(p->mirror); i++)\n\t\t\t\t\tif (p->mirror[i])\n\t\t\t\t\t\tbreak;\n\t\t\t\tif (i == countof(p->mirror))\n\t\t\t\t{\t//this appears to be a meta package with no download\n\t\t\t\t\t//just directly install it.\n\t\t\t\t\tif (PM_HasDep(p, DEP_FILE, NULL))\n\t\t\t\t\t\tCon_Printf(\"Unable to install package %s due to lack of any mirrors\\n\", p->name);\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tp->flags &= ~(DPF_NATIVE|DPF_CACHED|DPF_CORRUPT);\n\t\t\t\t\t\tif (p->flags & DPF_MARKED)\n\t\t\t\t\t\t\tp->flags |= DPF_ENABLED;\n\n\t\t\t\t\t\tCon_Printf(\"Enabled meta package %s\\n\", p->name);\n\t\t\t\t\t\tPM_WriteInstalledPackages();\n\t\t\t\t\t\tPM_PackageEnabled(p);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif ((p->flags & DPF_PRESENT) && !PM_PurgeOnDisable(p))\n\t\t\t{\t//its in our cache directory, so lets just use that\n\t\t\t\tp->trymirrors = 0;\n\t\t\t\tif (p->flags & DPF_MARKED)\n\t\t\t\t\tp->flags |= DPF_ENABLED;\n\n\t\t\t\tCon_Printf(\"Enabled cached package %s\\n\", p->name);\n\t\t\t\tPM_WriteInstalledPackages();\n\t\t\t\tPM_PackageEnabled(p);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (!PM_SignatureOkay(p) || (qofs_t)p->filesize != p->filesize)\n\t\t\t{\n\t\t\t\tp->flags &= ~DPF_MARKED;\t//refusing to do it.\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (p->extract == EXTRACT_EXPLICITZIP)\n\t\t\t{\t//don't allow multiple of these at a time... so we can download a single file and extract two packages from it.\n\t\t\t\tpackage_t *p2;\n\t\t\t\tfor (p2 = availablepackages; p2; p2=p2->next)\n\t\t\t\t\tif (p2->flags&DPF_PENDING)\t//only skip if the other one is already downloading.\n\t\t\t\t\t\tif (PM_DownloadSharesSource(p2, p))\n\t\t\t\t\t\t\tbreak;\n\t\t\t\tif (p2)\n\t\t\t\t\tcontinue;\t//skip downloading it. we'll extract this one when the other is extracted.\n\t\t\t}\n\n\t\t\ttemp = PM_GetTempName(p);\n\t\t\ttemproot = p->fsroot;\n\n\t\t\t//FIXME: we should lock in the temp path, in case the user foolishly tries to change gamedirs.\n\n\t\t\tFS_CreatePath(temp, temproot);\n\t\t\tswitch (p->extract)\n\t\t\t{\n\t\t\tcase EXTRACT_ZIP:\n\t\t\tcase EXTRACT_EXPLICITZIP:\n\t\t\tcase EXTRACT_COPY:\n\t\t\t\ttmpfile = FS_OpenVFS(temp, \"wb\", temproot);\n\t\t\t\tbreak;\n#ifdef AVAIL_XZDEC\n\t\t\tcase EXTRACT_XZ:\n\t\t\t\t{\n\t\t\t\t\tvfsfile_t *raw = FS_OpenVFS(temp, \"wb\", temproot);\n\t\t\t\t\ttmpfile = raw?FS_XZ_DecompressWriteFilter(raw):NULL;\n\t\t\t\t\tif (!tmpfile && raw)\n\t\t\t\t\t\tVFS_CLOSE(raw);\n\t\t\t\t}\n\t\t\t\tbreak;\n#endif\n#ifdef AVAIL_GZDEC\n\t\t\tcase EXTRACT_GZ:\n\t\t\t\t{\n\t\t\t\t\tvfsfile_t *raw = FS_OpenVFS(temp, \"wb\", temproot);\n\t\t\t\t\ttmpfile = raw?FS_GZ_WriteFilter(raw, true, false):NULL;\n\t\t\t\t\tif (!tmpfile && raw)\n\t\t\t\t\t\tVFS_CLOSE(raw);\n\t\t\t\t}\n\t\t\t\tbreak;\n#endif\n\t\t\tdefault:\n\t\t\t\tCon_Printf(\"decompression method not supported\\n\");\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (p->filesha512 && tmpfile)\n\t\t\t\ttmpfile = FS_Hash_ValidateWrites(tmpfile, p->name, p->filesize, &hash_sha2_512, p->filesha512);\n\t\t\telse if (p->filesha1 && tmpfile)\n\t\t\t\ttmpfile = FS_Hash_ValidateWrites(tmpfile, p->name, p->filesize, &hash_sha1, p->filesha1);\n\n\t\t\tif (tmpfile)\n\t\t\t{\n\t\t\t\tp->curdownload = HTTP_CL_Get(mirror, NULL, PM_Download_PreliminaryGot);\n\t\t\t\tif (!p->curdownload)\n\t\t\t\t\tCon_Printf(\"Unable to download %s (%s)\\n\", p->name, mirror);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tchar displaypath[MAX_OSPATH];\n\t\t\t\tCon_Printf(\"Unable to write %s. Fix permissions before trying to download %s\\n\", FS_DisplayPath(temp, temproot, displaypath, sizeof(displaypath))?displaypath:p->name, p->name);\n\t\t\t\tp->trymirrors = 0;\t//don't bother trying other mirrors if we can't write the file or understand its type.\n\t\t\t}\n\t\t\tif (p->curdownload)\n\t\t\t{\n\t\t\t\tCon_Printf(\"Downloading %s\\n\", p->name);\n\t\t\t\tp->flags |= DPF_PENDING;\n\t\t\t\tp->curdownload->file = tmpfile;\n\t\t\t\tp->curdownload->user_ctx = temp;\n\t\t\t\tp->curdownload->user_num = temproot;\n\n\t\t\t\tDL_CreateThread(p->curdownload, NULL, NULL);\n\t\t\t\tdownloading = true;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tp->flags &= ~DPF_PENDING;\n\t\t\t\tp->flags &= ~DPF_MARKED;\t//can't do it.\n\t\t\t\tif (tmpfile)\n\t\t\t\t\tVFS_CLOSE(tmpfile);\n\t\t\t\tFS_Remove(temp, temproot);\n\t\t\t}\n\t\t}\n\t}\n\n\tif (pkg_updating && !downloading)\n\t\tCmd_AddTimer(0, PM_DownloadsCompleted, 0, NULL, 0);\n\n\t//clear the updating flag once there's no more activity needed\n\tpkg_updating = downloading;\n}\nqboolean PM_LoadMapPackage(const char *package)\n{\n\tstruct packagedep_s *dep;\n\tpackage_t *p = PM_FindPackage(package);\n\tif (!p)\n\t\treturn false;\n\n\tif (p->flags & DPF_PRESENT)\n\t\treturn true;\t//okay, already installed.\n\tif (p->extract == EXTRACT_ZIP)\n\t\treturn false;\n\n\t//refuse to use weird packages.\n\tfor (dep = p->deps; dep; dep = dep->next)\n\t{\n\t\tif (dep->dtype == DEP_FILE)\n\t\t\treturn false;\n\t}\n\n\t//make sure its downloaded. fs code will be asked to explicitly use it, so no need to activate it.\n\tp->trymirrors = ~0u;\n\treturn true;\n}\nvoid PM_LoadMap(const char *package, const char *map)\n{\n\tif (!PM_LoadMapPackage(package))\n\t\treturn;\n\tpm_onload.package = Z_StrDup(package);\n\tpm_onload.map = Z_StrDup(map);\n\n\tpkg_updating = true;\n\tPM_StartADownload();\n}\n#else\nvoid PM_LoadMap(const char *package, const char *map)\n{\t//not supported, which is a shame because it might have been downloaded via other means.\n}\n#endif\nunsigned int PM_IsApplying(void)\n{\n\tint ret = 0;\n#ifdef WEBCLIENT\n\tsize_t i;\n\tif (PM_DownloadingCount())\n\t\tret |= 1;\n\tfor (i = 0; i < pm_numsources; i++)\n\t\tif (pm_source[i].curdl)\n\t\t\tret |= 1;\t//some source is downloading.\n#endif\n\tif (pm_pendingprompts)\n\t\tret |= 2;\t//waiting for user action to complete.\n\tif (doautoupdate)\n\t\tret |= 4;\t//will want to trigger a prompt...\n\n\treturn ret;\n}\n//'just' starts doing all the things needed to remove/install selected packages\nvoid PM_ApplyChanges(void)\n{\n\tpackage_t *p, **link;\n\tchar temp[MAX_OSPATH];\n\n#ifdef WEBCLIENT\n\tif (pkg_updating)\n\t\treturn;\n\tpkg_updating = true;\n#endif\n\n//delete any that don't exist\n\tfor (link = &availablepackages; *link ; )\n\t{\n\t\tp = *link;\n#ifdef WEBCLIENT\n\t\tif (p->flags&DPF_PENDING)\n\t\t\t; //erk, dude, don't do two!\n\t\telse\n#endif\n\t\t\tif ((p->flags & DPF_PURGE) || (!(p->flags&DPF_MARKED) && (p->flags&DPF_ENABLED)))\n\t\t{\t//if we don't want it but we have it anyway. don't bother to follow this logic when reinstalling\n\t\t\tqboolean reloadpacks = false;\n\t\t\tstruct packagedep_s *dep;\n\n\n\t\t\tfor (dep = p->deps; dep; dep = dep->next)\n\t\t\t{\n\t\t\t\tif (dep->dtype == DEP_FILE || dep->dtype == DEP_CACHEFILE)\n\t\t\t\t{\n\t\t\t\t\tchar ext[8];\n\t\t\t\t\tCOM_FileExtension(dep->name, ext, sizeof(ext));\n\t\t\t\t\tif (!stricmp(ext, \"pak\") || !stricmp(ext, \"pk3\") || !stricmp(ext, \"zip\"))\n\t\t\t\t\t\treloadpacks = true;\n\n#ifdef PLUGINS\t\t//when disabling/purging plugins, be sure to unload them first (unfortunately there might be some latency before this can actually happen).\n\t\t\t\t\tif ((p->flags & DPF_PLUGIN) && !Q_strncasecmp(dep->name, PLUGINPREFIX, strlen(PLUGINPREFIX)))\n\t\t\t\t\t\tCmd_ExecuteString(va(\"plug_close %s\\n\", dep->name), RESTRICT_LOCAL);\t//try to purge plugins so there's no files left open\n#endif\n\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (reloadpacks)\t//okay, some package was removed, unload all, do the deletions/disables, then reload them. This is kinda shit. Would be better to remove individual packages, which would avoid unnecessary config execs.\n\t\t\t\tFS_UnloadPackFiles();\n\n\t\t\tif ((p->flags & DPF_PURGE) || PM_PurgeOnDisable(p))\n\t\t\t{\n\t\t\t\tCon_Printf(\"Purging package %s\\n\", p->name);\n\t\t\t\tfor (dep = p->deps; dep; dep = dep->next)\n\t\t\t\t{\n\t\t\t\t\tif (dep->dtype == DEP_CACHEFILE)\n\t\t\t\t\t{\n\t\t\t\t\t\tchar *f = va(\"downloads/%s\", dep->name);\n\t\t\t\t\t\tif (!FS_Remove(f, p->fsroot))\n\t\t\t\t\t\t\tp->flags |= DPF_CACHED;\n\t\t\t\t\t}\n\t\t\t\t\telse if (dep->dtype == DEP_FILE)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (*p->gamedir)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tchar *f = va(\"%s/%s\", p->gamedir, dep->name);\n\t\t\t\t\t\t\tif (PM_TryGenCachedName(f, p, temp, sizeof(temp)) && PM_CheckFile(temp, p->fsroot))\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tif (!FS_Remove(temp, p->fsroot))\n\t\t\t\t\t\t\t\t\tp->flags |= DPF_CACHED;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse if (!FS_Remove(va(\"%s/%s\", p->gamedir, dep->name), p->fsroot))\n\t\t\t\t\t\t\t\tp->flags |= DPF_NATIVE;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (!FS_Remove(dep->name, p->fsroot))\n\t\t\t\t\t\t\tp->flags |= DPF_NATIVE;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tfor (dep = p->deps; dep; dep = dep->next)\n\t\t\t\t{\n\t\t\t\t\tif (dep->dtype == DEP_FILE)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (*p->gamedir)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tchar *f = va(\"%s/%s\", p->gamedir, dep->name);\n\t\t\t\t\t\t\tif ((p->flags & DPF_NATIVE) && PM_TryGenCachedName(f, p, temp, sizeof(temp)))\n\t\t\t\t\t\t\t\tFS_Rename(f, temp, p->fsroot);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tCon_Printf(\"Disabling package %s\\n\", p->name);\n\t\t\t}\n\t\t\tp->flags &= ~(DPF_PURGE|DPF_ENABLED);\n\n\t\t\t/* FIXME: windows bug:\n\t\t\t** deleting an exe might 'succeed' but leave the file on disk for a while anyway.\n\t\t\t** the file will eventually disappear, but until then we'll still see it as present,\n\t\t\t**  be unable to delete it again, and trying to open it to see if it still exists\n\t\t\t**  will fail.\n\t\t\t** there's nothing we can do other than wait until whatever part of\n\t\t\t**  windows that's fucking up releases its handles.\n\t\t\t** thankfully this only affects reinstalling exes/engines.\n\t\t\t*/\n\n\t\t\tPM_ValidatePackage(p);\n\t\t\tPM_WriteInstalledPackages();\n\n\t\t\tif (reloadpacks)\n\t\t\t\tFS_ReloadPackFiles();\n\n\t\t\tif ((p->flags & DPF_FORGETONUNINSTALL) && !(p->flags & DPF_PRESENT))\n\t\t\t{\t//packages that have no source to redownload\n#if 1\n\t\t\t\tpm_sequence++;\n\t\t\t\tPM_FreePackage(p);\n#else\n\t\t\t\tif (p->alternative)\n\t\t\t\t{\t//replace it with its alternative package\n\t\t\t\t\t*p->link = p->alternative;\n\t\t\t\t\tp->alternative->alternative = p->alternative->next;\n\t\t\t\t\tif (p->alternative->alternative)\n\t\t\t\t\t\tp->alternative->alternative->link = &p->alternative->alternative;\n\t\t\t\t\tp->alternative->next = p->next;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\t//just remove it from the list.\n\t\t\t\t\t*p->link = p->next;\n\t\t\t\t\tif (p->next)\n\t\t\t\t\t\tp->next->link = p->link;\n\t\t\t\t}\n\n//FIXME: the menu(s) hold references to packages, so its not safe to purge them\n\t\t\t\tp->flags |= DPF_HIDDEN;\n//\t\t\t\t\tBZ_Free(p);\n#endif\n\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\n\t\tlink = &(*link)->next;\n\t}\n\n#ifdef WEBCLIENT\n\t//and flag any new/updated ones for a download\n\tfor (p = availablepackages; p ; p=p->next)\n\t{\n\t\tif (!(p->flags & DPF_PENDING))\n\t\t\tif (((p->flags & DPF_MANIMARKED) && !(p->flags&DPF_PRESENT)) ||\t//satisfying a manifest merely requires that it be present, not actually enabled.\n\t\t\t\t((p->flags&DPF_MARKED) && !(p->flags&DPF_ENABLED)))\t\t\t//actually enabled stuff requires actual enablement\n\t\t\t{\n\t\t\t\tp->trymirrors = ~0u;\n\t\t\t\tif (p->flags & DPF_SIGNATUREACCEPTED)\n\t\t\t\t\tp->flags |= DPF_TRUSTED;\t//user confirmed it, engine trusts it, we're all okay with any exploits it may have...\n\t\t\t}\n\t}\n\tPM_StartADownload();\t//and try to do those downloads.\n#else\n\tfor (p = availablepackages; p; p=p->next)\n\t{\n\t\tif ((p->flags&DPF_MARKED) && !(p->flags&DPF_ENABLED))\n\t\t{\t//flagged for a (re?)download\n\t\t\tint i;\n\t\t\tstruct packagedep_s *dep;\n\t\t\tfor (i = 0; i < countof(p->mirror); i++)\n\t\t\t\tif (p->mirror[i])\n\t\t\t\t\tbreak;\n\t\t\tfor (dep = p->deps; dep; dep=dep->next)\n\t\t\t\tif (dep->dtype == DEP_FILE||dep->dtype == DEP_CACHEFILE)\n\t\t\t\t\tbreak;\n\t\t\tif (!dep && i == countof(p->mirror))\n\t\t\t{\t//this appears to be a meta package with no download\n\t\t\t\t//just directly install it.\n\t\t\t\tp->flags &= ~(DPF_NATIVE|DPF_CACHED|DPF_CORRUPT);\n\t\t\t\tp->flags |= DPF_ENABLED;\n\n\t\t\t\tCon_Printf(\"Enabled meta package %s\\n\", p->name);\n\t\t\t\tPM_WriteInstalledPackages();\n\t\t\t\tPM_PackageEnabled(p);\n\t\t\t}\n\n\t\t\tif ((p->flags & DPF_PRESENT) && !PM_PurgeOnDisable(p))\n\t\t\t{\t//its in our cache directory, so lets just use that\n\t\t\t\tp->flags |= DPF_ENABLED;\n\n\t\t\t\tCon_Printf(\"Enabled cached package %s\\n\", p->name);\n\t\t\t\tPM_WriteInstalledPackages();\n\t\t\t\tPM_PackageEnabled(p);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\telse\n\t\t\t\tp->flags &= ~DPF_MARKED;\n\t\t}\n\t}\n#endif\n}\n\n#ifdef DOWNLOADMENU\nstatic qboolean PM_DeclinedPackages(char *out, size_t outsize)\n{\n\tsize_t ofs = 0;\n\tpackage_t *p;\n\tqboolean ret = false;\n\tif (fs_manifest)\n\t{\n\t\tchar tok[1024];\n\t\tchar *strings = fs_manifest->installupd;\n\t\twhile (strings && *strings)\n\t\t{\n\t\t\tstrings = COM_ParseStringSetSep(strings, ';', tok, sizeof(tok));\n\n\t\t\t//already in the list\n\t\t\tif (PM_NameIsInStrings(declinedpackages, tok))\n\t\t\t\tcontinue;\n\n\t\t\tp = PM_MarkedPackage(tok, DPF_MARKED);\n\t\t\tif (p)\t//don't mark it as declined if it wasn't\n\t\t\t\tcontinue;\n\n\t\t\tp = PM_FindPackage(tok);\n\t\t\tif (p)\n\t\t\t{\t//okay, it was declined\n\t\t\t\tret = true;\n\t\t\t\tif (!out)\n\t\t\t\t{\t//we're confirming that they should be flagged as declined\n\t\t\t\t\tif (declinedpackages)\n\t\t\t\t\t{\n\t\t\t\t\t\tchar *t = declinedpackages;\n\t\t\t\t\t\tdeclinedpackages = Z_StrDupf(\"%s;%s\", declinedpackages, tok);\n\t\t\t\t\t\tZ_Free(t);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\tdeclinedpackages = Z_StrDup(tok);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\t//we're collecting a list of package names\n\t\t\t\t\tchar *change = va(\"%s\\n\", p->name);\n\t\t\t\t\tsize_t l = strlen(change);\n\t\t\t\t\tif (ofs+l >= outsize)\n\t\t\t\t\t{\n\t\t\t\t\t\tQ_strncpyz(out, \"Too many changes\\n\", outsize);\n\t\t\t\t\t\tout = NULL;\n\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tmemcpy(out+ofs, change, l);\n\t\t\t\t\t\tofs += l;\n\t\t\t\t\t\tout[ofs] = 0;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tif (!out && ret)\n\t\tPM_WriteInstalledPackages();\n\treturn ret;\n}\n#endif\n#if defined(M_Menu_Prompt) || defined(SERVERONLY)\n//if M_Menu_Prompt is a define, then its a stub...\nstatic void PM_PromptApplyChanges(void)\n{\n\tPM_ApplyChanges();\n}\n#else\nstatic void PM_PromptApplyChanges_Callback(void *ctx, promptbutton_t opt)\n{\n#ifdef WEBCLIENT\n\tpkg_updating = false;\n#endif\n\tif (opt == PROMPT_YES)\n\t\tPM_ApplyChanges();\n}\nstatic void PM_PromptApplyDecline_Callback(void *ctx, promptbutton_t opt)\n{\n#ifdef WEBCLIENT\n\tpkg_updating = false;\n#endif\n\tif (opt == PROMPT_NO)\n\t{\n\t\tPM_DeclinedPackages(NULL, 0);\n\t\tPM_PromptApplyChanges();\n\t}\n}\nstatic void PM_PromptApplyChanges(void)\n{\n\tunsigned int changes;\n\tchar text[8192];\n#ifdef WEBCLIENT\n\t//lock it down, so noone can make any changes while this prompt is still displayed\n\tif (pkg_updating)\n\t{\n\t\tMenu_Prompt(PM_PromptApplyChanges_Callback, NULL, localtext(\"An update is already in progress\\nPlease wait\\n\"), NULL, NULL, \"Cancel\", true);\n\t\treturn;\n\t}\n\tpkg_updating = true;\n#endif\n\n\tstrcpy(text, localtext(\"Really decline the following\\nrecommended packages?\\n\\n\"));\n\tif (PM_DeclinedPackages(text+strlen(text), sizeof(text)-strlen(text)))\n\t\tMenu_Prompt(PM_PromptApplyDecline_Callback, NULL, text, NULL, \"Confirm\", \"Cancel\", true);\n\telse\n\t{\n\t\tstrcpy(text, localtext(\"Apply the following changes?\\n\\n\"));\n\t\tchanges = PM_ChangeList(text+strlen(text), sizeof(text)-strlen(text));\n\t\tif (!changes)\n\t\t{\n#ifdef WEBCLIENT\n\t\t\tpkg_updating = false;//no changes...\n#endif\n\t\t}\n\t\telse\n\t\t\tMenu_Prompt(PM_PromptApplyChanges_Callback, NULL, text, \"Apply\", NULL, \"Cancel\", true);\n\t}\n}\n#endif\n#if defined(HAVE_CLIENT) && defined(WEBCLIENT)\nstatic void PM_AddSubList_Callback(void *ctx, promptbutton_t opt)\n{\n\tif (opt == PROMPT_YES)\n\t{\n\t\tPM_AddSubList(ctx, \"\", SRCFL_USER|SRCFL_ENABLED);\n\t\tPM_WriteInstalledPackages();\n\t\tPM_UpdatePackageList(false);\n\t}\n\tZ_Free(ctx);\n}\n#endif\n\nqboolean PM_CanInstall(const char *packagename)\n{\n\tint i;\n\tpackage_t *p = PM_FindPackage(packagename);\n\tif (p && !(p->flags&(DPF_ENABLED|DPF_CORRUPT|DPF_HIDDEN)))\n\t{\n\t\tfor (i = 0; i < countof(p->mirror); i++)\n\t\t\tif (p->mirror[i])\n\t\t\t\treturn true;\n\t}\n\treturn false;\n}\n\nstatic void PM_Pkg_ListSources(qboolean onlyenabled)\n{\n\tsize_t i, c=0, e=0;\n\tfor (i = 0; i < pm_numsources; i++)\n\t{\n\t\tif ((pm_source[i].flags & SRCFL_HISTORIC) && !developer.ival)\n\t\t\tcontinue;\t//hidden ones were historically enabled/disabled. remember the state even when using a different fmf, but don't confuse the user.\n\n\t\tc++;\n\t\tif (pm_source[i].flags & SRCFL_ENABLED)\n\t\t\te++;\n\t\telse if (onlyenabled)\n\t\t\tcontinue;\n\n\t\tif (pm_source[i].flags & SRCFL_ENABLED)\n\t\t\tCon_Printf(\"^&02 \");\n\t\telse if (pm_source[i].flags & SRCFL_DISABLED)\n\t\t\tCon_Printf(\"^&04 \");\n\t\telse\n\t\t\tCon_Printf(\"^&0E \");\n\n\t\tif (pm_source[i].flags & SRCFL_DISABLED)\n\t\t\tCon_Printf(\"%s \", pm_source[i].url);\t//enable\n\t\telse\n\t\t\tCon_Printf(\"%s \", pm_source[i].url);\t//disable\n\n\t\tif (pm_source[i].flags & SRCFL_USER)\n\t\t\tCon_Printf(\"- ^[[Delete]\\\\type\\\\pkg remsource \\\"%s\\\"^]\\n\", pm_source[i].url);\n\t\telse\n\t\t\tCon_Printf(\"(implicit)\\n\");\n\t}\n\tif (c != e)\n\t\tCon_Printf(\"<%u sources, %u enabled>\\n\", (unsigned)c, (unsigned)e);\n\telse\n\t\tCon_Printf(\"<%u sources>\\n\", (unsigned)c);\n}\nstatic void PM_Pkg_ListPackage(package_t *p, const char **category)\n{\n\tconst char *newcat;\n\tchar quoted[8192];\n\tconst char *status;\n\tchar *markup;\n\tstruct packagedep_s *dep;\n\n\tif (p->flags & DPF_ENABLED)\n\t\tmarkup = S_COLOR_GREEN;\n\telse if (p->flags & DPF_CORRUPT)\n\t\tmarkup = S_COLOR_RED;\n\telse if (p->flags & (DPF_CACHED))\n\t\tmarkup = S_COLOR_YELLOW;\t//downloaded but not active\n\telse\n\t\tmarkup = S_COLOR_WHITE;\n\n\tif (!(p->flags & DPF_MARKED) != !(p->flags & DPF_ENABLED) || (p->flags & DPF_PURGE))\n\t{\n\t\tif (p->flags & DPF_MARKED)\n\t\t{\n\t\t\tif (p->flags & DPF_PURGE)\n\t\t\t\tstatus = S_COLOR_CYAN\"<to reinstall>\";\n\t\t\telse\n\t\t\t\tstatus = S_COLOR_CYAN\"<to install>\";\n\t\t}\n\t\telse if ((p->flags & DPF_PURGE) || !(p->qhash && (p->flags & DPF_CACHED)))\n\t\t\tstatus = S_COLOR_CYAN\"<to uninstall>\";\n\t\telse\n\t\t\tstatus = S_COLOR_CYAN\"<to disable>\";\n\t}\n\telse if ((p->flags & (DPF_ENABLED|DPF_CACHED)) == DPF_CACHED)\n\t\tstatus = S_COLOR_CYAN\"<disabled>\";\n\telse if (p->flags & DPF_USERMARKED)\n\t\tstatus = S_COLOR_GRAY\"<manual>\";\n\telse if (p->flags & DPF_AUTOMARKED)\n\t\tstatus = S_COLOR_GRAY\"<auto>\";\n\telse\n\t\tstatus = \"\";\n\n\t//show category banner\n\tnewcat = p->category?p->category:\"\";\n\tif (strcmp(*category, newcat))\n\t{\n\t\t*category = newcat;\n\t\tCon_Printf(\"%s\\n\", *category);\n\t}\n\n\t//show quick status display\n\tif (p->flags & DPF_ENABLED)\n\t\tCon_Printf(\"^&02 \");\n\telse if (p->flags & DPF_PRESENT)\n\t\tCon_Printf(\"^&0E \");\n\telse\n\t\tCon_Printf(\"^&04 \");\n\tif (p->flags & DPF_MARKED)\n\t\tCon_Printf(\"^&02 \");\n\telse if (!(p->flags & DPF_PURGE) && (p->flags&DPF_PRESENT))\n\t\tCon_Printf(\"^&0E \");\n\telse\n\t\tCon_Printf(\"^&04 \");\n\n\t//show the package details.\n\tCon_Printf(\"\\t^[\"S_COLOR_GRAY\"%s%s%s%s^] %s\"S_COLOR_GRAY\" %s (%s%s)\", markup, p->name, p->arch?\":\":\"\", p->arch?p->arch:\"\", status, strcmp(p->name, p->title)?p->title:\"\", p->version, (p->flags&DPF_TESTING)?\"-testing\":\"\");\n\n\tfor (dep = p->deps; dep; dep = dep->next)\n\t{\n\t\tif (dep->dtype == DEP_SOURCE)\n\t\t\tCon_Printf(S_COLOR_MAGENTA\" %s\", dep->name);\n\t}\n\n\tif (!(p->flags&DPF_MARKED) && p == PM_FindPackage(p->name))\n\t\tCon_Printf(\" ^[[Add]\\\\type\\\\pkg add %s;pkg apply^]\", COM_QuotedString(p->name, quoted, sizeof(quoted), false));\n\tif ((p->flags&DPF_MARKED) && p == PM_MarkedPackage(p->name, DPF_MARKED))\n\t\tCon_Printf(\" ^[[Remove]\\\\type\\\\pkg rem %s;pkg apply^]\", COM_QuotedString(p->name, quoted, sizeof(quoted), false));\n\n\n\tif (p->flags & DPF_SIGNATUREACCEPTED)\n\t\tCon_Printf(\" ^&02Trusted\");\n\telse if (p->flags & DPF_SIGNATUREREJECTED)\n\t\tCon_Printf(\" ^&04Untrusted\");\n\telse if (p->flags & DPF_SIGNATUREUNKNOWN)\n\t\tCon_Printf(\" ^&0EUnverified\");\n\telse\n\t\tCon_Printf(\" ^&0EUnsigned\");\n\n\tif (p->curdownload)\n\t{\n\t\tif (p->curdownload->totalsize==0)\n\t\t\tCon_Printf(\" Downloading %s/unknown\", FS_AbbreviateSize(quoted,sizeof(quoted), p->curdownload->completed));\n\t\telse\n\t\t\tCon_Printf(\" Downloading %i%% (%s total)\", (int)(100*(double)p->curdownload->completed/(double)p->curdownload->totalsize), FS_AbbreviateSize(quoted,sizeof(quoted), p->curdownload->totalsize));\n\t}\n\telse if (p->flags & DPF_PENDING)\n\t\tCon_Printf(\" Finalising...\");\n\telse if (p->trymirrors)\n\t\tCon_Printf(\" Pending...\");\n\n\tCon_Printf(\"\\n\");\n}\nstatic void PM_Pkg_ListPackages(qboolean onlyenabled)\n{\n\tconst char *category = \"\";\n\tint i, count;\n\tpackage_t **sorted, *p, *a;\n\n\tfor (count = 0, p = availablepackages; p; p=p->next)\n\t\tcount++;\n\tsorted = Z_Malloc(sizeof(*sorted)*count);\n\tfor (count = 0, p = availablepackages; p; p=p->next)\n\t{\n\t\tif ((p->flags & DPF_HIDDEN) && !(p->flags & (DPF_MARKED|DPF_ENABLED|DPF_PURGE|DPF_CACHED)))\n\t\t\tcontinue;\n\n\t\tif (p->flags & DPF_ALLMARKED)\n\t\t\t;\n\t\telse if (onlyenabled)\n\t\t\tcontinue;\n\n\t\tsorted[count++] = p;\n\t}\n\tqsort(sorted, count, sizeof(*sorted), PM_PackageSortOrdering);\n\tfor (i = 0; i < count; i++)\n\t{\n\t\tPM_Pkg_ListPackage(sorted[i], &category);\n\t\tfor(a=sorted[i]->alternative; a; a = a->next)\n\t\t{\n\t\t\tCon_Printf(\" \");\n\t\t\tPM_Pkg_ListPackage(a, &category);\n\t\t}\n\t}\n\tZ_Free(sorted);\n\tCon_Printf(\"<end of list>\\n\");\n}\n\nvoid PM_Command_f(void)\n{\n\tpackage_t *p;\n\tconst char *act = Cmd_Argv(1);\n\tconst char *key;\n\tqboolean quiet = false;\n\n\tif (Cmd_FromGamecode())\n\t{\n\t\tCon_Printf(\"%s may not be used from gamecode\\n\", Cmd_Argv(0));\n\t\treturn;\n\t}\n\n\tif (!strncmp(act, \"quiet_\", 6))\n\t{\n\t\tquiet = true;\t//don't spam so much. for menus to (ab)use.\n\t\tact += 6;\n\t}\n\n\tif (!strcmp(act, \"listenabledsources\"))\n\t\tPM_Pkg_ListSources(true);\n\telse if (!strcmp(act, \"sources\") || !strcmp(act, \"addsource\"))\n\t{\n\t\t#ifdef WEBCLIENT\n\t\t\tif (Cmd_Argc() == 2)\n\t\t\t\tPM_Pkg_ListSources(false);\n\t\t\telse\n\t\t\t{\n\t\t\t\t#ifdef HAVE_CLIENT\n\t\t\t\t\tMenu_Prompt(PM_AddSubList_Callback, Z_StrDup(Cmd_Argv(2)), va(localtext(\"Add updates source?\\n%s\"), Cmd_Argv(2)), \"Confirm\", NULL, \"Cancel\", true);\n\t\t\t\t#else\n\t\t\t\t\tPM_AddSubList(Cmd_Argv(2), \"\", SRCFL_USER|SRCFL_ENABLED);\n\t\t\t\t\tPM_WriteInstalledPackages();\n\t\t\t\t#endif\n\t\t\t}\n\t\t#endif\n\t}\n\telse if (!strcmp(act, \"remsource\"))\n\t{\n\t#ifdef WEBCLIENT\n\t\tPM_RemSubList(Cmd_Argv(2));\n\t\tPM_WriteInstalledPackages();\n\t#endif\n\t}\n\telse\n\t{\n\t\tif (!loadedinstalled)\n\t\t\tPM_UpdatePackageList(false);\n\n\t\tif (!strcmp(act, \"list\"))\n\t\t{\n\t\t\tPM_Pkg_ListSources(false);\n\t\t\tPM_Pkg_ListPackages(false);\n\t\t}\n\t\telse if (!strcmp(act, \"listmarked\"))\n\t\t\tPM_Pkg_ListPackages(true);\n\t\telse if (!strcmp(act, \"internal\"))\n\t\t{\n\t\t\tchar buf[65536];\n\t\t\tkey = Cmd_Argv(2);\n\t\t\tfor (p = availablepackages; p; p=p->next)\n\t\t\t{\n\t\t\t\tif (Q_strcasecmp(p->name, key))\n\t\t\t\t\tcontinue;\n\t\t\t\tPM_WriteInstalledPackage_v3(p, buf, sizeof(buf));\n\t\t\t\tCon_Printf(\"%s\", buf);\n\t\t\t}\n\t\t}\n\t\telse if (!strcmp(act, \"show\"))\n\t\t{\n\t\t\tstruct packagedep_s *dep;\n\t\t\tint found = 0;\n\t\t\tkey = Cmd_Argv(2);\n\t\t\tfor (p = availablepackages; p; p=p->next)\n\t\t\t{\n\t\t\t\tif (Q_strcasecmp(p->name, key))\n\t\t\t\t\tcontinue;\n\n\t\t\t\tif (p->previewimage)\n\t\t\t\t\tCon_Printf(\"^[%s (%s)\\\\tipimg\\\\%s\\\\tip\\\\%s^]\\n\", p->name, p->version, p->previewimage, \"\");\n\t\t\t\telse\n\t\t\t\t\tCon_Printf(\"%s (%s)\\n\", p->name, p->version);\n\t\t\t\tif (p->title)\n\t\t\t\t\tCon_Printf(\"\t^mtitle: ^m%s\\n\", p->title);\n\t\t\t\tif (p->license)\n\t\t\t\t\tCon_Printf(\"\t^mlicense: ^m%s\\n\", p->license);\n\t\t\t\tif (p->author)\n\t\t\t\t\tCon_Printf(\"\t^mauthor: ^m%s\\n\", p->author);\n\t\t\t\tif (p->website)\n\t\t\t\t\tCon_Printf(\"\t^mwebsite: ^m%s\\n\", p->website);\n\t\t\t\tfor (dep = p->deps; dep; dep = dep->next)\n\t\t\t\t{\n\t\t\t\t\tif (dep->dtype == DEP_SOURCE)\n\t\t\t\t\t\tCon_Printf(\"\t^msource: ^m%s\\n\", dep->name);\n\t\t\t\t}\n\t\t\t\tif (p->description)\n\t\t\t\t\tCon_Printf(\"%s\\n\", p->description);\n\t\t\t\tfor (dep = p->deps; dep; dep = dep->next)\n\t\t\t\t{\n\t\t\t\t\tif (dep->dtype == DEP_MAP)\n\t\t\t\t\t\tCon_Printf(\"\t^mmap: ^[[%s]\\\\map\\\\%s^]\\n\", dep->name, dep->name);\n\t\t\t\t}\n\n\t\t\t\tif (p->flags & DPF_MARKED)\n\t\t\t\t{\n\t\t\t\t\tif (p->flags & DPF_ENABLED)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (p->flags & DPF_PURGE)\n\t\t\t\t\t\t\tCon_Printf(\"\tpackage is flagged to be re-installed\\n\");\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tCon_Printf(\"\tpackage is currently installed\\n\");\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\tCon_Printf(\"\tpackage is flagged to be installed\\n\");\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tif (p->flags & DPF_ENABLED)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (p->flags & DPF_PURGE)\n\t\t\t\t\t\t\tCon_Printf(\"\tpackage is flagged to be purged\\n\");\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tCon_Printf(\"\tpackage is flagged to be disabled\\n\");\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\tCon_Printf(\"\tpackage is not installed\\n\");\n\t\t\t\t}\n\t\t\t\tif (p->flags & DPF_NATIVE)\n\t\t\t\t\tCon_Printf(\"\tpackage is native\\n\");\n\t\t\t\tif (p->flags & DPF_CACHED)\n\t\t\t\t\tCon_Printf(\"\tpackage is cached\\n\");\n\t\t\t\tif (p->flags & DPF_CORRUPT)\n\t\t\t\t\tCon_Printf(\"\tpackage is corrupt\\n\");\n\t\t\t\tif (p->flags & DPF_DISPLAYVERSION)\n\t\t\t\t\tCon_Printf(\"\tpackage has a version conflict\\n\");\n\t\t\t\tif (p->flags & DPF_FORGETONUNINSTALL)\n\t\t\t\t\tCon_Printf(\"\tpackage is obsolete\\n\");\n\t\t\t\tif (p->flags & DPF_HIDDEN)\n\t\t\t\t\tCon_Printf(\"\tpackage is hidden\\n\");\n\t\t\t\tif (p->flags & DPF_ENGINE)\n\t\t\t\t\tCon_Printf(\"\tpackage is an engine update\\n\");\n\t\t\t\tif (p->flags & DPF_TESTING)\n\t\t\t\t\tCon_Printf(S_COLOR_YELLOW\"\tpackage is untested\\n\");\n\t\t\t\tif (p->flags & DPF_TRUSTED)\n\t\t\t\t\tCon_Printf(\"\tpackage is trusted\\n\");\n#ifdef WEBCLIENT\n\t\t\t\tif (!PM_SignatureOkay(p))\n\t\t\t\t{\n\t\t\t\t\tif (!p->signature)\n\t\t\t\t\t\tCon_Printf(CON_ERROR\"\tSignature missing\"CON_DEFAULT\"\\n\");\t\t\t//some idiot forgot to include a signature\n\t\t\t\t\telse if (p->flags & DPF_SIGNATUREREJECTED)\n\t\t\t\t\t\tCon_Printf(CON_ERROR\"\tSignature invalid\"CON_DEFAULT\"\\n\");\t\t\t//some idiot got the wrong auth/sig/hash\n\t\t\t\t\telse if (p->flags & DPF_SIGNATUREUNKNOWN)\n\t\t\t\t\t\tCon_Printf(S_COLOR_RED\"\tSignature is not trusted\"CON_DEFAULT\"\\n\");\t//clientside permission.\n\t\t\t\t\telse\n\t\t\t\t\t\tCon_Printf(CON_ERROR\"\tUnable to verify signature\"CON_DEFAULT\"\\n\");\t//clientside problem.\n\t\t\t\t}\n#endif\n\t\t\t\tfound++;\n\t\t\t}\n\t\t\tif (!found)\n\t\t\t\tCon_Printf(\"<package not found>\\n\");\n\t\t}\n\t\telse if (!strcmp(act, \"search\") || !strcmp(act, \"find\"))\n\t\t{\n\t\t\tconst char *key = Cmd_Argv(2);\n\t\t\tfor (p = availablepackages; p; p=p->next)\n\t\t\t{\n\t\t\t\tif (Q_strcasestr(p->name, key) || (p->title && Q_strcasestr(p->title, key)) || (p->description && Q_strcasestr(p->description, key)))\n\t\t\t\t{\n\t\t\t\t\tCon_Printf(\"%s\\n\", p->name);\n\t\t\t\t}\n\t\t\t}\n\t\t\tCon_Printf(\"<end of list>\\n\");\n\t\t}\n\t\telse if (!strcmp(act, \"apply\"))\n\t\t{\n\t\t\tCon_Printf(\"Applying package changes\\n\");\n\t\t\tif (qrenderer != QR_NONE)\n\t\t\t\tPM_PromptApplyChanges();\n\t\t\telse if (Cmd_ExecLevel == RESTRICT_LOCAL)\n\t\t\t\tPM_ApplyChanges();\n\t\t}\n\t\telse if (!strcmp(act, \"changes\"))\n\t\t{\n\t\t\tPM_PrintChanges();\n\t\t}\n\t\telse if (!strcmp(act, \"reset\") || !strcmp(act, \"revert\"))\n\t\t{\n\t\t\tPM_RevertChanges();\n\t\t}\n#ifdef WEBCLIENT\n\t\telse if (!strcmp(act, \"update\"))\n\t\t{\t//query the servers if we've not already done so.\n\t\t\t//FIXME: regrab if more than an hour ago?\n\t\t\tif (!allowphonehome)\n\t\t\t\tallowphonehome = -1;\t//trigger a prompt, instead of ignoring it.\n\t\t\tPM_UpdatePackageList(false);\n\t\t}\n\t\telse if (!strcmp(act, \"refresh\"))\n\t\t{\t//flush package cache, make a new request even if we already got a response from the server.\n\t\t\tint i;\n\t\t\tfor (i = 0; i < pm_numsources; i++)\n\t\t\t{\n\t\t\t\tif (!(pm_source[i].flags & SRCFL_ENABLED))\n\t\t\t\t{\n\t\t\t\t\tif (isDedicated && !(pm_source[i].flags & SRCFL_DISABLED))\n\t\t\t\t\t\tpm_source[i].flags |= SRCFL_ONCE;\n\t\t\t\t\telse\n\t\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tpm_source[i].status = SRCSTAT_PENDING;\n\t\t\t}\n\t\t\tif (!allowphonehome)\n\t\t\t\tallowphonehome = -1;\t//trigger a prompt, instead of ignoring it.\n\t\t\tPM_UpdatePackageList(false);\n\t\t}\n\t\telse if (!strcmp(act, \"upgrade\"))\n\t\t{\t//auto-mark any updated packages.\n\t\t\tunsigned int changes = PM_MarkUpdates();\n\t\t\tif (changes)\n\t\t\t{\n\t\t\t\tif (!quiet)\n\t\t\t\t\tCon_Printf(\"%u packages flagged\\n\", changes);\n\t\t\t\tPM_PromptApplyChanges();\n\t\t\t}\n\t\t\telse if (!quiet)\n\t\t\t\tCon_Printf(\"Already using latest versions of all packages\\n\");\n\t\t}\n#endif\n\t\telse if (!strcmp(act, \"add\") || !strcmp(act, \"get\") || !strcmp(act, \"install\") || !strcmp(act, \"enable\"))\n\t\t{\t//FIXME: make sure this updates.\n\t\t\tint arg = 2;\n\t\t\tfor (arg = 2; arg < Cmd_Argc(); arg++)\n\t\t\t{\n\t\t\t\tconst char *key = Cmd_Argv(arg);\n\t\t\t\tp = PM_FindPackage(key);\n\t\t\t\tif (p)\n\t\t\t\t{\n\t\t\t\t\tif (!PM_MarkPackage(p, DPF_USERMARKED))\n\t\t\t\t\t\tCon_Printf(\"%s: unable to activate/mark %s\\n\", Cmd_Argv(0), key);\n\t\t\t\t\tp->flags &= ~DPF_PURGE;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tCon_Printf(\"%s: package %s not known\\n\", Cmd_Argv(0), key);\n\t\t\t}\n\t\t\tif (!quiet)\n\t\t\t\tPM_PrintChanges();\n\t\t}\n#ifdef WEBCLIENT\n\t\telse if (!strcmp(act, \"reinstall\"))\n\t\t{\t//fixme: favour the current verson.\n\t\t\tint arg = 2;\n\t\t\tfor (arg = 2; arg < Cmd_Argc(); arg++)\n\t\t\t{\n\t\t\t\tconst char *key = Cmd_Argv(arg);\n\t\t\t\tp = PM_FindPackage(key);\n\t\t\t\tif (p)\n\t\t\t\t{\n\t\t\t\t\tPM_MarkPackage(p, DPF_USERMARKED);\n\t\t\t\t\tp->flags |= DPF_PURGE;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tCon_Printf(\"%s: package %s not known\\n\", Cmd_Argv(0), key);\n\t\t\t}\n\t\t\tif (!quiet)\n\t\t\t\tPM_PrintChanges();\n\t\t}\n#endif\n\t\telse if (!strcmp(act, \"disable\") || !strcmp(act, \"rem\") || !strcmp(act, \"remove\"))\n\t\t{\n\t\t\tint arg = 2;\n\t\t\tfor (arg = 2; arg < Cmd_Argc(); arg++)\n\t\t\t{\n\t\t\t\tconst char *key = Cmd_Argv(arg);\n\t\t\t\tp = PM_MarkedPackage(key, DPF_MARKED);\n\t\t\t\tif (!p)\n\t\t\t\t\tp = PM_FindPackage(key);\n\t\t\t\tif (p)\n\t\t\t\t\tPM_UnmarkPackage(p, DPF_MARKED);\n\t\t\t\telse\n\t\t\t\t\tCon_Printf(\"%s: package %s not known\\n\", Cmd_Argv(0), key);\n\t\t\t}\n\t\t\tif (!quiet)\n\t\t\t\tPM_PrintChanges();\n\t\t}\n\t\telse if (!strcmp(act, \"del\") || !strcmp(act, \"purge\") || !strcmp(act, \"delete\") || !strcmp(act, \"uninstall\"))\n\t\t{\n\t\t\tint arg = 2;\n\t\t\tfor (arg = 2; arg < Cmd_Argc(); arg++)\n\t\t\t{\n\t\t\t\tconst char *key = Cmd_Argv(arg);\n\t\t\t\tp = PM_MarkedPackage(key, DPF_MARKED);\n\t\t\t\tif (!p)\n\t\t\t\t\tp = PM_FindPackage(key);\n\t\t\t\tif (p)\n\t\t\t\t{\n\t\t\t\t\tPM_UnmarkPackage(p, DPF_MARKED);\n\t\t\t\t\tif (p->flags & (DPF_PRESENT|DPF_CORRUPT))\n\t\t\t\t\t\tp->flags |=\tDPF_PURGE;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tCon_Printf(\"%s: package %s not known\\n\", Cmd_Argv(0), key);\n\t\t\t}\n\t\t\tif (!quiet)\n\t\t\t\tPM_PrintChanges();\n\t\t}\n\t\telse\n\t\t\tCon_Printf(\"%s: Unknown action %s\\nShould be one of list, show, search, upgrade, revert, add, rem, del, changes, apply, sources, addsource, remsource\\n\", Cmd_Argv(0), act);\n\t}\n}\n\nqboolean PM_FindUpdatedEngine(char *syspath, size_t syspathsize)\n{\n\tstruct packagedep_s *dep;\n\tpackage_t *e = NULL, *p;\n\tchar *pfname;\n\n\tint best = parse_revision_number(enginerevision, true);\n\tint them;\n\tif (best <= 0)\n\t\treturn false;\t//no idea what revision we are, we might be more recent.\n\t//figure out what we've previously installed.\n\tPM_PreparePackageList();\n\n\tfor (p = availablepackages; p; p = p->next)\n\t{\n\t\tif ((p->flags & DPF_ENGINE) && !(p->flags & DPF_HIDDEN) && p->fsroot == FS_ROOT)\n\t\t{\n\t\t\tthem = parse_revision_number(p->version, true);\n\t\t\tif ((p->flags & DPF_ENABLED) && them>best)\n\t\t\t{\n\t\t\t\tfor (dep = p->deps, pfname = NULL; dep; dep = dep->next)\n\t\t\t\t{\n\t\t\t\t\tif (dep->dtype != DEP_FILE)\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\tif (pfname)\n\t\t\t\t\t{\n\t\t\t\t\t\tpfname = NULL;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tpfname = dep->name;\n\t\t\t\t}\n\n\t\t\t\tif (pfname && PM_CheckFile(pfname, p->fsroot))\n\t\t\t\t{\n\t\t\t\t\tif (FS_SystemPath(pfname, p->fsroot, syspath, syspathsize))\n\t\t\t\t\t{\n\t\t\t\t\t\te = p;\n\t\t\t\t\t\tbest = them;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tif (e)\n\t\treturn true;\n\treturn false;\n}\n\n\n\n\n\n\n//called by the filesystem code to make sure needed packages are in the updates system\nstatic const char *FS_RelativeURL(const char *base, const char *file, char *buffer, int bufferlen)\n{\n\t//fixme: cope with windows paths\n\tqboolean baseisurl = base?!!strchr(base, ':'):false;\n\tqboolean fileisurl = !!strchr(file, ':');\n\t//qboolean baseisabsolute = (*base == '/' || *base == '\\\\');\n\tqboolean fileisabsolute = (*file == '/' || *file == '\\\\');\n\tconst char *ebase;\n\n\tif (fileisurl || !base)\n\t\treturn file;\n\tif (fileisabsolute)\n\t{\n\t\tif (baseisurl)\n\t\t{\n\t\t\tebase = strchr(base, ':');\n\t\t\tebase++;\n\t\t\twhile(*ebase == '/')\n\t\t\t\tebase++;\n\t\t\twhile(*ebase && *ebase != '/')\n\t\t\t\tebase++;\n\t\t}\n\t\telse\n\t\t\tebase = base;\n\t}\n\telse\n\t\tebase = COM_SkipPath(base);\n\tmemcpy(buffer, base, ebase-base);\n\tstrcpy(buffer+(ebase-base), file);\n\n\treturn buffer;\n}\n\n//this function is called by the filesystem code to start downloading the packages listed by each manifest.\nvoid PM_AddManifestPackages(ftemanifest_t *man, qboolean mayapply)\n{\n\tpackage_t *p, *m;\n\tsize_t i;\n\tconst char *path, *url;\n\n\tchar buffer[MAX_OSPATH];\n\tint idx;\n\tstruct manpack_s *pack;\n\tconst char *baseurl = man->updateurl;\t//this is a url for updated versions of the fmf itself.\n\tint dtype;\n\n\tfor (p = availablepackages; p; p = p->next)\n\t\tp->flags &= ~DPF_MANIMARKED;\n\n\tPM_RevertChanges();\t//we'll be force-applying, so make sure there's no unconfirmed pending stuff.\n\n\tfor (idx = 0; idx < countof(man->package); idx++)\n\t{\n\t\tpack = &man->package[idx];\n\t\tif (!pack->type)\n\t\t\tcontinue;\n\n\t\t//check this package's conditional\n\t\tif (pack->condition)\n\t\t{\n\t\t\tif (!If_EvaluateBoolean(pack->condition, RESTRICT_LOCAL))\n\t\t\t\tcontinue;\t//ignore it\n\t\t}\n\n\t\tif (pack->packagename)\n\t\t{\n\t\t\tp = PM_FindPackage(pack->packagename);\n\t\t\tif (p)\n\t\t\t{\n\t\t\t\tPM_MarkPackage(p, DPF_MANIMARKED);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\n\t\t/*if (pack->mirrors[0])\n\t\t{\n\t\t\tfor (p = availablepackages; p; p = p->next)\n\t\t\t{\n\t\t\t\tif (p->mirror[0] && !strcmp(p->mirror[0], pack->mirrors[0]))\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (p)\n\t\t\t{\n\t\t\t\tPM_MarkPackage(p, DPF_MANIMARKED);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}*/\n\n\t\tp = Z_Malloc(sizeof(*p));\n\t\tstrcpy(p->version, \"\");\n\t\tif (pack->packagename)\n\t\t{\n\t\t\tchar *arch;\n\t\t\tchar *ver;\n\t\t\tp->name = Z_StrDup(pack->packagename);\n\t\t\tver = strchr(p->name, '=');\n\t\t\tarch = strchr(p->name, ':');\n\t\t\tif (*ver)\n\t\t\t{\n\t\t\t\t*ver++ = 0;\n\t\t\t\tQ_strncpyz(p->version, ver, sizeof(p->version));\n\t\t\t}\n\t\t\tif (*arch)\n\t\t\t{\n\t\t\t\t*arch++ = 0;\n\t\t\t\tp->arch = Z_StrDup(arch);\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tp->name = Z_StrDup(pack->path);\n\t\t\tstrcpy(p->version, \"\");\n\t\t}\n\t\tp->title = Z_StrDup(pack->path);\n\t\tp->category = Z_StrDupf(\"%s/\", man->formalname);\n\t\tp->priority = PM_DEFAULTPRIORITY;\n\t\tp->fsroot = FS_ROOT;\n\t\tp->flags = DPF_FORGETONUNINSTALL|DPF_MANIFEST|DPF_GUESSED;\n\t\tp->qhash = pack->crcknown?Z_StrDupf(\"%#x\", pack->crc):NULL;\n\t\tdtype = DEP_FILE;\n\n\t\tp->packprefix = pack->prefix?Z_StrDup(pack->prefix):NULL;\n\n\t\t//note that this signs the hash(validated with size) with an separately trusted authority and is thus not dependant upon trusting the manifest itself...\n\t\t//that said, we can't necessarily trust any overrides the manifest might include - those parts do not form part of the signature.\n\t\tif (pack->crcknown && strchr(p->name, '/'))\n\t\t{\n\t\t\tp->signature = pack->signature?Z_StrDup(pack->signature):NULL;\n\t\t}\n\t\telse if (pack->signature)\n\t\t\tCon_Printf(CON_WARNING\"Ignoring signature for %s\\n\", p->name);\n\t\tp->filesha512 = pack->sha512?Z_StrDup(pack->sha512):NULL;\n\t\tp->filesize = pack->filesize;\n\n\t\t{\n\t\t\tchar *c = p->name;\n\t\t\tfor (c=p->name; *c; c++)\t//don't get confused.\n\t\t\t\tif (*c == '/') *c = '_';\n\t\t}\n\n\t\tpath = pack->path;\n\t\tif (pack->type != mdt_installation)\n\t\t{\n\t\t\tchar *s = strchr(path, '/');\n\t\t\tif (!s)\n\t\t\t{\n\t\t\t\tPM_FreePackage(p);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t*s = 0;\n\t\t\tQ_strncpyz(p->gamedir, path, sizeof(p->gamedir));\n\t\t\t*s = '/';\n\t\t\tpath=s+1;\n\t\t}\n\n\t\tp->extract = EXTRACT_COPY;\n\t\tfor (i = 0; i < countof(pack->mirrors) && i < countof(p->mirror); i++)\n\t\t{\n\t\t\turl = pack->mirrors[i];\n\t\t\tif (!url)\n\t\t\t\tcontinue;\n\t\t\tif (!strncmp(url, \"gz:\", 3))\n\t\t\t{\n\t\t\t\turl+=3;\n\t\t\t\tp->extract = EXTRACT_GZ;\n\t\t\t}\n\t\t\telse if (!strncmp(url, \"xz:\", 3))\n\t\t\t{\n\t\t\t\turl+=3;\n\t\t\t\tp->extract = EXTRACT_XZ;\n\t\t\t}\n\t\t\telse if (!strncmp(url, \"unzip:\", 6))\n\t\t\t{\n\t\t\t\tchar *comma;\n\t\t\t\turl+=6;\n\t\t\t\tcomma = strchr(url, ',');\n\t\t\t\tif (comma)\n\t\t\t\t{\n\t\t\t\t\tp->extract = EXTRACT_EXPLICITZIP;\n\t\t\t\t\t*comma = 0;\n\t\t\t\t\tPM_AddDep(p, DEP_EXTRACTNAME, url);\n\t\t\t\t\t*comma = ',';\n\t\t\t\t\turl = comma+1;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tp->extract = EXTRACT_ZIP;\n\t\t\t}\n\t\t\t/*else if (!strncmp(url, \"prompt:\", 7))\n\t\t\t{\n\t\t\t\turl+=7;\n\t\t\t\tfspdl_extracttype = X_COPY;\n\t\t\t}*/\n\n\t\t\turl = FS_RelativeURL(baseurl, url, buffer, sizeof(buffer));\n\t\t\tif (url && *url)\n\t\t\t\tp->mirror[i] = Z_StrDup(url);\n\t\t}\n\t\tPM_AddDep(p, dtype, path);\n\n\t\tPM_ValidateAuthenticity(p, (man->security == MANIFEST_SECURITY_INSTALLER)?VH_CORRECT:VH_UNSUPPORTED);\n\t\tif (p->flags & DPF_SIGNATUREACCEPTED)\n\t\t\tp->flags |= DPF_TRUSTED;\t//user confirmed it, engine trusts it, we're all okay with any exploits it may have...\n\n\t\tm = PM_InsertPackage(p);\n\t\tif (!m)\n\t\t\tcontinue;\n\n\t\tPM_MarkPackage(m, DPF_MANIMARKED);\n\n/*\t\t//okay, so we merged it into m. I guess we already had a copy!\n\t\tif (!(m->flags & DPF_PRESENT))\n\t\t\tif (PM_SignatureOkay(m))\n\t\t\t\tm->trymirrors = ~0;\t//FIXME: we should probably mark+prompt...\n*/\n\t\tcontinue;\n\t}\n\n\tPM_ResortPackages();\n\tif (mayapply)\n\t\tPM_ApplyChanges();\t//we reverted changes earlier, so the only packages added should have been from here.\n}\n\n#ifdef HAVE_CLIENT\n#include \"pr_common.h\"\nvoid QCBUILTIN PF_cl_getpackagemanagerinfo(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint packageidx = G_INT(OFS_PARM0);\n\tenum packagemanagerinfo_e fieldidx = G_INT(OFS_PARM1);\n\tpackage_t *p;\n\tG_INT(OFS_RETURN) = 0;\n\tif (packageidx < 0)\n\t\treturn;\n\tif (packageidx == 0)\n\t\tPM_AreSourcesNew(true);\n\n\tfor (p = availablepackages; p; p = p->next)\n\t{\n\t\tif ((p->flags & DPF_HIDDEN) && !(p->flags & (DPF_MARKED|DPF_ENABLED|DPF_PURGE|DPF_CACHED)))\n\t\t\tcontinue;\n\t\tif (packageidx--)\n\t\t\tcontinue;\n\t\tswitch(fieldidx)\n\t\t{\n\t\tcase GPMI_NAME:\n\t\t\tif (p->arch)\n\t\t\t\tRETURN_TSTRING(va(\"%s:%s=%s\", p->name, p->arch, p->version));\n\t\t\telse\n\t\t\t\tRETURN_TSTRING(va(\"%s=%s\", p->name, p->version));\n\t\t\tbreak;\n\t\tcase GPMI_CATEGORY:\n\t\t\tRETURN_TSTRING(p->category);\n\t\t\tbreak;\n\t\tcase GPMI_TITLE:\n\t\t\tif (p->flags & DPF_DISPLAYVERSION)\n\t\t\t\tRETURN_TSTRING(va(\"%s (%s)\", p->title, p->version));\n\t\t\telse\n\t\t\t\tRETURN_TSTRING(p->title);\n\t\t\tbreak;\n\t\tcase GPMI_VERSION:\n\t\t\tRETURN_TSTRING(p->version);\n\t\t\tbreak;\n\t\tcase GPMI_DESCRIPTION:\n\t\t\tRETURN_TSTRING(p->description);\n\t\t\tbreak;\n\t\tcase GPMI_LICENSE:\n\t\t\tRETURN_TSTRING(p->license);\n\t\t\tbreak;\n\t\tcase GPMI_AUTHOR:\n\t\t\tRETURN_TSTRING(p->author);\n\t\t\tbreak;\n\t\tcase GPMI_WEBSITE:\n\t\t\tRETURN_TSTRING(p->website);\n\t\t\tbreak;\n\t\tcase GPMI_FILESIZE:\n\t\t\tif (p->filesize)\n\t\t\t\tRETURN_TSTRING(va(\"%lu\", (unsigned long)p->filesize));\n\t\t\tbreak;\n\t\tcase GPMI_AVAILABLE:\n#ifdef WEBCLIENT\n\t\t\tif (PM_SignatureOkay(p) && !(p->flags&DPF_FORGETONUNINSTALL))\n\t\t\t\tRETURN_SSTRING(\"1\");\n#endif\n\t\t\tbreak;\n\n\t\tcase GPMI_INSTALLED:\n\t\t\tif (p->flags & DPF_CORRUPT)\n\t\t\t\tRETURN_SSTRING(\"corrupt\");\t//some sort of error\n\t\t\telse if (p->flags & DPF_ENABLED)\n\t\t\t\tRETURN_SSTRING(\"enabled\");\t//its there (and in use)\n\t\t\telse if (p->flags & DPF_PRESENT)\n\t\t\t\tRETURN_SSTRING(\"present\");\t//its there (but ignored)\n#ifdef WEBCLIENT\n\t\t\telse if (p->curdownload)\n\t\t\t{\t//we're downloading it\n\t\t\t\tif (p->curdownload->qdownload.sizeunknown&&cls.download->size==0 && p->filesize>0 && p->extract==EXTRACT_COPY)\n\t\t\t\t\tRETURN_TSTRING(va(\"%i%%\", (int)((100*p->curdownload->qdownload.completedbytes)/p->filesize)));\t//server didn't report total size, but we know how big its meant to be.\n\t\t\t\telse\n\t\t\t\t\tRETURN_TSTRING(va(\"%i%%\", (int)p->curdownload->qdownload.percent));\t//we're downloading it.\n\t\t\t}\n\t\t\telse if (p->flags & DPF_PENDING)\n\t\t\t\tRETURN_TSTRING(\"100%\");\t//finalising shouldn't stay in this state long...\n\t\t\telse if (p->trymirrors)\n\t\t\t\tRETURN_SSTRING(\"pending\");\t//its queued.\n#endif\n\t\t\tbreak;\n\t\tcase GPMI_GAMEDIR:\n\t\t\tRETURN_TSTRING(p->gamedir);\n\t\t\tbreak;\n\n\t\tcase GPMI_ACTION:\n\t\t\tif (p->flags & DPF_PURGE)\n\t\t\t{\n\t\t\t\tif (p->flags & DPF_MARKED)\n\t\t\t\t\tRETURN_SSTRING(\"reinstall\");\t//user wants to install it\n\t\t\t\telse\n\t\t\t\t\tRETURN_SSTRING(\"purge\");\t//wiping it out\n\t\t\t}\n\t\t\telse if (p->flags & DPF_USERMARKED)\n\t\t\t\tRETURN_SSTRING(\"user\");\t//user wants to install it\n\t\t\telse if (p->flags & (DPF_AUTOMARKED|DPF_MANIMARKED))\n\t\t\t\tRETURN_SSTRING(\"auto\");\t//enabled to satisfy a dependancy\n\t\t\telse if (p->flags & DPF_ENABLED)\n\t\t\t\tRETURN_SSTRING(\"disable\");\t//change from enabled to cached.\n\t\t\telse if (p->flags & DPF_PRESENT)\n\t\t\t\tRETURN_SSTRING(\"retain\");\t//keep it in cache\n\t\t\t//else not installed and don't want it.\n\t\t\tbreak;\n\t\tcase GPMI_MAPS:\n\t\t\t{\n\t\t\t\tchar buf[256];\n\t\t\t\tchar *maps = NULL;\n\t\t\t\tstruct packagedep_s *d;\n\t\t\t\tfor (d = p->deps; d; d = d->next)\n\t\t\t\t{\n\t\t\t\t\tif (d->dtype == DEP_MAP)\n\t\t\t\t\t{\n\t\t\t\t\t\t*buf = ' ';\n\t\t\t\t\t\tCOM_QuotedString(d->name, buf+1, sizeof(buf)-1, false);\n\t\t\t\t\t\tif (maps)\n\t\t\t\t\t\t\tZ_StrCat(&maps, buf);\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tZ_StrCat(&maps, buf+1);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (maps)\n\t\t\t\t{\t//at least one map found.\n\t\t\t\t\tRETURN_TSTRING(maps);\n\t\t\t\t\tZ_Free(maps);\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\t\tcase GPMI_PREVIEWIMG:\n\t\t\tif (p->previewimage)\n\t\t\t\tRETURN_TSTRING(p->previewimage);\n\t\t\tbreak;\n\t\t}\n\t\treturn;\n\t}\n}\n#endif\n\n#else\nqboolean PM_CanInstall(const char *packagename)\n{\n\treturn false;\n}\nvoid PM_EnumeratePlugins(void (*callback)(const char *name, qboolean blocked))\n{\n}\nint PM_IsApplying(qboolean listsonly)\n{\n\treturn false;\n}\nqboolean PM_FindUpdatedEngine(char *syspath, size_t syspathsize)\n{\n\treturn false;\n}\n#endif\n\n#ifdef DOWNLOADMENU\ntypedef struct {\n\tmenucustom_t *list;\n\tchar intermediatefilename[MAX_QPATH];\n\tchar pathprefix[MAX_QPATH];\n\tint downloadablessequence;\n\tchar titletext[128];\n\tchar applymessage[128];\t//so we can change its text to give it focus\n\tchar filtertext[128];\n\tqboolean filtering;\n\tconst void *expandedpackage;\t//which package we're currently viewing maps for.\n\tqboolean populated;\n} dlmenu_t;\n\nstatic void MD_Draw (int x, int y, struct menucustom_s *c, struct emenu_s *m)\n{\n\tpackage_t *p;\n\tchar *n;\n\tstruct packagedep_s *dep;\n\n\tif (y + 8 < 0 || y >= vid.height)\t//small optimisation.\n\t\treturn;\n\n\tif (c->dint != pm_sequence)\n\t\treturn;\t//probably stale\n\n#ifdef WEBCLIENT\n\tif (allowphonehome == -2)\n\t{\n\t\tallowphonehome = false;\n#ifdef HAVE_CLIENT\n\t\tMenu_Prompt(PM_AllowPackageListQuery_Callback, NULL, localtext(\"Query updates list?\\n\"), \"Okay\", NULL, \"Nope\", true);\n#endif\n\t}\n#endif\n\n\tp = c->dptr;\n\tif (p)\n\t{\n\t\tif (p->alternative && (p->flags & DPF_HIDDEN))\n\t\t\tp = p->alternative;\n\n\t\tfor (dep = p->deps; dep; dep = dep->next)\n\t\t\tif (dep->dtype == DEP_MAP)\n\t\t\t\tbreak;\n\n\t\tif (dep)\n\t\t{\t//map packages are not marked, but cached on demand.\n\t\t\tif (p->flags & DPF_PRESENT)\n\t\t\t{\n\t\t\t\tif (p->flags & DPF_PURGE)\n\t\t\t\t\tDraw_FunStringWidth (x, y, \"DEL\", 48, 2, false);\t//purge\n\t\t\t\telse\n\t\t\t\t\tDraw_FunStringWidth (x, y, \"^&03  \", 48, 2, false);\t//cyan\n\t\t\t}\n\t\t\telse\n\t\t\t\tDraw_FunStringWidth (x, y, \"^&06  \", 48, 2, false);\t//orange\n\t\t}\n\t\telse\n#ifdef WEBCLIENT\n\t\tif (p->curdownload)\n\t\t\tDraw_FunStringWidth (x, y, va(\"%i%%\", (int)p->curdownload->qdownload.percent), 48, 2, false);\n\t\telse if (p->trymirrors || (p->flags&DPF_PENDING))\n\t\t\tDraw_FunStringWidth (x, y, \"PND\", 48, 2, false);\n\t\telse\n#endif\n\t\t{\n\t\t\tif (p->flags & DPF_USERMARKED)\n\t\t\t{\n\t\t\t\tif (!(p->flags & DPF_ENABLED))\n\t\t\t\t{\t//DPF_MARKED|!DPF_ENABLED:\n\t\t\t\t\tif (p->flags & DPF_PURGE)\n\t\t\t\t\t\tDraw_FunStringWidth (x, y, S_COLOR_GREEN\"GET\", 48, 2, false);\n\t\t\t\t\telse if (p->flags & (DPF_PRESENT))\n\t\t\t\t\t\tDraw_FunStringWidth (x, y, S_COLOR_GREEN\"USE\", 48, 2, false);\n\t\t\t\t\telse\n\t\t\t\t\t\tDraw_FunStringWidth (x, y, S_COLOR_GREEN\"GET\", 48, 2, false);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\t//DPF_MARKED|DPF_ENABLED:\n\t\t\t\t\tif (p->flags & DPF_PURGE)\n\t\t\t\t\t\tDraw_FunStringWidth (x, y, S_COLOR_GREEN\"GET\", 48, 2, false);\t//purge and reinstall.\n\t\t\t\t\telse if (p->flags & DPF_CORRUPT)\n\t\t\t\t\t\tDraw_FunStringWidth (x, y, \"?\"\"?\"\"?\", 48, 2, false);\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tDraw_FunStringWidth (x, y, \"^&02  \", 48, 2, false);\t//green\n//\t\t\t\t\t\tDraw_FunStringWidth (x, y, \"^Ue080^Ue082\", 48, 2, false);\n//\t\t\t\t\t\tDraw_FunStringWidth (x, y, \"^Ue083\", 48, 2, false);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (p->flags & DPF_MARKED)\n\t\t\t{\t//auto-use options. draw with half alpha to darken them a little.\n\t\t\t\tif (!(p->flags & DPF_ENABLED))\n\t\t\t\t{\t//DPF_MARKED|!DPF_ENABLED:\n\t\t\t\t\tif (p->flags & DPF_PURGE)\n\t\t\t\t\t\tDraw_FunStringWidth (x, y, S_COLOR_GREEN\"^hGET\", 48, 2, false);\n\t\t\t\t\telse if (p->flags & (DPF_PRESENT))\n\t\t\t\t\t\tDraw_FunStringWidth (x, y, S_COLOR_GREEN\"^hUSE\", 48, 2, false);\n\t\t\t\t\telse\n\t\t\t\t\t\tDraw_FunStringWidth (x, y, S_COLOR_GREEN\"^hGET\", 48, 2, false);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\t//DPF_MARKED|DPF_ENABLED:\n\t\t\t\t\tif (p->flags & DPF_PURGE)\n\t\t\t\t\t\tDraw_FunStringWidth (x, y, S_COLOR_GREEN\"^hGET\", 48, 2, false);\t//purge and reinstall.\n\t\t\t\t\telse if (p->flags & DPF_CORRUPT)\n\t\t\t\t\t\tDraw_FunStringWidth (x, y, \"?\"\"?\"\"?\", 48, 2, false);\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tDraw_FunStringWidth (x, y, \"^&02  \", 48, 2, false);\t//green\n//\t\t\t\t\t\tDraw_FunStringWidth (x, y, \"^Ue080^Ue082\", 48, 2, false);\n//\t\t\t\t\t\tDraw_FunStringWidth (x, y, \"^Ue083\", 48, 2, false);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (!(p->flags & DPF_ENABLED))\n\t\t\t\t{\t//!DPF_MARKED|!DPF_ENABLED:\n\t\t\t\t\tif (p->flags & DPF_PURGE)\n\t\t\t\t\t\tDraw_FunStringWidth (x, y, S_COLOR_RED\"DEL\", 48, 2, false);\t//purge\n\t\t\t\t\telse if (p->flags & DPF_HIDDEN)\n\t\t\t\t\t\tDraw_FunStringWidth (x, y, \"---\", 48, 2, false);\n\t\t\t\t\telse if (p->flags & DPF_CORRUPT)\n\t\t\t\t\t\tDraw_FunStringWidth (x, y, \"!!!\", 48, 2, false);\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tif (p->flags & DPF_PRESENT)\n\t\t\t\t\t\t\tDraw_FunStringWidth (x, y, \"^&0E  \", 48, 2, false);\t//yellow\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tDraw_FunStringWidth (x, y, \"^&04  \", 48, 2, false);\t//red\n\n//\t\t\t\t\t\tDraw_FunStringWidth (x, y, \"^Ue080^Ue082\", 48, 2, false);\n//\t\t\t\t\t\tDraw_FunStringWidth (x, y, \"^Ue081\", 48, 2, false);\n//\t\t\t\t\t\tif (p->flags & DPF_PRESENT)\n//\t\t\t\t\t\t\tDraw_FunStringWidth (x, y, \"-\", 48, 2, false);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\t//!DPF_MARKED|DPF_ENABLED:\n\t\t\t\t\tif ((p->flags & DPF_PURGE) || PM_PurgeOnDisable(p))\n\t\t\t\t\t\tDraw_FunStringWidth (x, y, S_COLOR_RED\"DEL\", 48, 2, false);\n\t\t\t\t\telse\n\t\t\t\t\t\tDraw_FunStringWidth (x, y, S_COLOR_YELLOW\"DIS\", 48, 2, false);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tn = p->title;\n\t\tif (p->flags & DPF_DISPLAYVERSION)\n\t\t\tn = va(\"%s (%s)\", n, *p->version?p->version:\"unversioned\");\n\n\t\tif (p->flags & DPF_TESTING)\t//hide testing updates \n\t\t\tn = va(\"^h%s\", n);\n\n\t\tif (!PM_CheckPackageFeatures(p))\n\t\t\tDraw_FunStringWidth(0, y, \"!\", x+8, true, true);\n#ifdef WEBCLIENT\n\t\tif (!PM_SignatureOkay(p))\n\t\t\tDraw_FunStringWidth(0, y, \"^b!\", x+8, true, true);\n#endif\n\n//\t\tif (!(p->flags & (DPF_ENABLED|DPF_MARKED|DPF_PRESENT))\n//\t\t\tcontinue;\n\n//\t\tif (&m->selecteditem->common == &c->common)\n//\t\t\tDraw_AltFunString (x+48, y, n);\n//\t\telse\n\t\t\tDraw_FunStringU8(CON_WHITEMASK, x+48, y, n);\n\t}\n}\n\nstatic qboolean MD_Key (struct menucustom_s *c, struct emenu_s *m, int key, unsigned int unicode)\n{\n\tqboolean ctrl = keydown[K_LCTRL] || keydown[K_RCTRL];\n\tpackage_t *p, *p2;\n\tstruct packagedep_s *dep, *dep2;\n\tdlmenu_t *info = m->data;\n\tif (c->dint != pm_sequence)\n\t\treturn false;\t//probably stale\n\tp = c->dptr;\n\tif (key == 'c' && ctrl)\n\t\tSys_SaveClipboard(CBT_CLIPBOARD, p->website);\n\telse if (key == K_DEL || key == K_KP_DEL || key == K_BACKSPACE || key == K_GP_DIAMOND_ALTCONFIRM)\n\t{\n\t\tif (!(p->flags & DPF_MARKED))\n\t\t\tp->flags |= DPF_PURGE;\t//purge it when its already not marked (ie: when pressed twice)\n\t\tPM_UnmarkPackage(p, DPF_MARKED);\t//deactivate it\n\t}\n\telse if (key == K_ENTER || key == K_KP_ENTER || key == K_MOUSE1 || key == K_TOUCH || key == K_GP_DIAMOND_CONFIRM)\n\t{\n\t\tif (p->alternative && (p->flags & DPF_HIDDEN))\n\t\t\tp = p->alternative;\n\n\t\tif (info->expandedpackage == p)\n\t\t{\t//close this submenu thing...\n\t\t\tinfo->expandedpackage = NULL;\n\t\t\t//remove the following map items.\n\t\t\tpm_sequence++;\n\t\t\treturn true;\n\t\t}\n\n\t\tfor (dep = p->deps; dep; dep = dep->next)\n\t\t{\n\t\t\tif (dep->dtype == DEP_MAP)\n\t\t\t{\n\t\t\t\tinfo->expandedpackage = p;\n\t\t\t\tpm_sequence++;\n\t\t\t\t//add the map items after (and shift everything)\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\n\t\tif (p->flags & DPF_ENABLED)\n\t\t{\n\t\t\tswitch (p->flags & (DPF_PURGE|DPF_MARKED))\n\t\t\t{\n\t\t\tcase DPF_USERMARKED:\n\t\t\tcase DPF_AUTOMARKED:\n\t\t\tcase DPF_MARKED:\n\t\t\t\tPM_UnmarkPackage(p, DPF_MARKED);\t//deactivate it\n\t\t\t\tbreak;\n\t\t\tcase 0:\n\t\t\t\tp->flags |= DPF_PURGE;\t//purge\n\t\t\t\tif (!PM_PurgeOnDisable(p))\n\t\t\t\t\tbreak;\n\t\t\t\t//fall through\n\t\t\tcase DPF_PURGE:\n\t\t\t\tPM_MarkPackage(p, DPF_USERMARKED);\t\t//reinstall\n//\t\t\t\tif (!(p->flags & DPF_HIDDEN) && !(p->flags & DPF_CACHED))\n//\t\t\t\t\tbreak;\n\t\t\t\t//fall through\n\t\t\tcase DPF_USERMARKED|DPF_PURGE:\n\t\t\tcase DPF_AUTOMARKED|DPF_PURGE:\n\t\t\tcase DPF_MARKED|DPF_PURGE:\n\t\t\t\tp->flags &= ~DPF_PURGE;\t//back to no-change\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tswitch (p->flags & (DPF_PURGE|DPF_MARKED))\n\t\t\t{\n\t\t\tcase 0:\n\t\t\t\tPM_MarkPackage(p, DPF_USERMARKED);\n\t\t\t\t//now: try to install\n\t\t\t\tbreak;\n\t\t\tcase DPF_AUTOMARKED:\t//\n\t\t\t\tp->flags |= DPF_USERMARKED;\n\t\t\t\tbreak;\n\t\t\tcase DPF_USERMARKED:\n\t\t\tcase DPF_MARKED:\n\t\t\t\tp->flags |= DPF_PURGE;\n#ifdef WEBCLIENT\n\t\t\t\t//now: re-get despite already having it.\n\t\t\t\tif ((p->flags & DPF_CORRUPT) || ((p->flags & DPF_PRESENT) && !PM_PurgeOnDisable(p)))\n\t\t\t\t\tbreak;\t//only makes sense if we already have a cached copy that we're not going to use.\n#endif\n\t\t\t\t//fallthrough\n\t\t\tcase DPF_USERMARKED|DPF_PURGE:\n\t\t\tcase DPF_AUTOMARKED|DPF_PURGE:\n\t\t\tcase DPF_MARKED|DPF_PURGE:\n\t\t\t\tPM_UnmarkPackage(p, DPF_MARKED);\n\t\t\t\t//now: delete\n\t\t\t\tif ((p->flags & DPF_CORRUPT) || ((p->flags & DPF_PRESENT) && !PM_PurgeOnDisable(p)))\n\t\t\t\t\tbreak;\t//only makes sense if we have a cached/corrupt copy of it already\n\t\t\t\t//fallthrough\n\t\t\tcase DPF_PURGE:\n\t\t\t\tp->flags &= ~DPF_PURGE;\n\t\t\t\t//now: no change\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tif (p->flags&DPF_MARKED)\n\t\t{\n\t\t\t//any other packages that conflict should be flagged for uninstall now that this one will replace it.\n\t\t\tfor (p2 = availablepackages; p2; p2 = p2->next)\n\t\t\t{\n\t\t\t\tif (p == p2)\n\t\t\t\t\tcontinue;\n\t\t\t\tif (strcmp(p->gamedir, p2->gamedir))\n\t\t\t\t\tcontinue;\t//different gamedirs. don't screw up.\n\t\t\t\tfor (dep = p->deps; dep; dep = dep->next)\n\t\t\t\t{\n\t\t\t\t\tif (dep->dtype == DEP_FILE)\n\t\t\t\t\t{\n\t\t\t\t\t\tfor (dep2 = p2->deps; dep2; dep2 = dep2->next)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (dep2->dtype != DEP_FILE)\n\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\tif (!strcmp(dep->name, dep2->name))\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tPM_UnmarkPackage(p2, DPF_MARKED);\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n#ifdef WEBCLIENT\n\t\telse\n\t\t\tp->trymirrors = 0;\n#endif\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\nstatic void MD_MapDraw (int x, int y, struct menucustom_s *c, struct emenu_s *m)\n{\n\tstruct packagedep_s *map = c->dptr2;\n#if defined(HAVE_SERVER)\n\tconst package_t *p = c->dptr;\n\tstruct packagedep_s *dep;\n\tfloat besttime, fulltime, bestkills, bestsecrets;\n\tchar *package = NULL;\n\tconst char *ext;\n#endif\n\n\tif (y + 8 < 0 || y >= vid.height)\t//small optimisation.\n\t\treturn;\n\n\tif (c->dint != pm_sequence)\n\t\treturn;\t//probably stale\n\n#if defined(HAVE_SERVER)\n\tfor (dep = p->deps; dep; dep = dep->next)\n\t{\n\t\tif (dep->dtype == DEP_CACHEFILE)\n\t\t{\n\t\t\tpackage = va(\"downloads/%s\", dep->name);\n\t\t\tbreak;\n\t\t}\n\t\telse if (dep->dtype == DEP_FILE && !package)\n\t\t\tpackage = dep->name;\n\t}\n\text = COM_GetFileExtension(map->name, NULL);\n\tif (package && Log_CheckMapCompletion(package, va(\"maps/%s%s\", map->name, *ext?\"\":\".bsp\"), &besttime, &fulltime, &bestkills, &bestsecrets))\n\t{\n\t\tif (besttime != fulltime)\n\t\t\tDraw_FunStringU8(CON_WHITEMASK, x+48+8*4, y, va(\"^m%s^m (%g %g in %.1f secs, fastest in %.1f)\", map->name, bestkills,bestsecrets,fulltime, besttime));\n\t\telse\n\t\t\tDraw_FunStringU8(CON_WHITEMASK, x+48+8*4, y, va(\"^m%s^m (%g %g in %.1f secs)\", map->name, bestkills,bestsecrets,fulltime));\n\t}\n\telse\n#endif\n\t\tDraw_FunStringU8(CON_WHITEMASK, x+48+8*4, y, map->name);\n}\nstatic qboolean MD_MapKey (struct menucustom_s *c, struct emenu_s *m, int key, unsigned int unicode)\n{\n\tconst package_t *p = c->dptr;\n\tstruct packagedep_s *map = c->dptr2;\n\tstruct packagedep_s *dep;\n\n\tif (c->dint != pm_sequence)\n\t\treturn false;\t//probably stale\n\n\tif (key == K_ENTER || key == K_KP_ENTER || key == K_GP_START || key == K_MOUSE1 || key == K_TOUCH || key == K_GP_DIAMOND_CONFIRM)\n\t\tfor (dep = p->deps; dep; dep = dep->next)\n\t\t{\n\t\t\tif (dep == map)\n\t\t\t{\n\t\t\t\tchar quoted[MAX_QPATH*2];\n\t\t\t\tCbuf_AddText(va(\"map %s\\n\", COM_QuotedString(va(\"%s:%s\", p->name, map->name), quoted, sizeof(quoted), false)), RESTRICT_LOCAL);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\n\treturn false;\n}\n\n#ifdef WEBCLIENT\nstatic void MD_Source_Draw (int x, int y, struct menucustom_s *c, struct emenu_s *m)\n{\n\tchar *text;\n\tif (!(pm_source[c->dint].flags & SRCFL_ENABLED))\n\t{\n\t\tif (!(pm_source[c->dint].flags & SRCFL_DISABLED))\n\t\t\tDraw_FunStringWidth (x, y, \"??\", 48, 2, false);\n\t\telse\n\t\t\tDraw_FunStringWidth (x, y, \"^&04  \", 48, 2, false);\t//red\n\t}\n\telse switch(pm_source[c->dint].status)\n\t{\n\tcase SRCSTAT_OBTAINED:\n\t\tDraw_FunStringWidth (x, y, \"^&02  \", 48, 2, false);\t//green\n\t\tbreak;\n\tcase SRCSTAT_PENDING:\n\t\tDraw_FunStringWidth (x, y, \"^&0E  \", 48, 2, false);\t//yellow\n\t\tDraw_FunStringWidth (x, y, \"??\", 48, 2, false);\t//this should be fast... so if they get a chance to see the ?? then there's something bad happening, and the ?? is appropriate.\n\t\tbreak;\n\tcase SRCSTAT_UNTRIED:\t//waiting for a refresh.\n\t\tDraw_FunStringWidth (x, y, \"^&0E  \", 48, 2, false);\t//yellow\n\t\tbreak;\n\tcase SRCSTAT_FAILED_DNS:\n\t\tDraw_FunStringWidth (x, y, \"^&0E  \", 48, 2, false);\t//yellow\n#ifdef WEBCLIENT\n\t\tDraw_FunStringWidth (x, y, \"DNS\", 48, 2, false);\n#endif\n\t\tbreak;\n\tcase SRCSTAT_FAILED_NORESP:\n\t\tDraw_FunStringWidth (x, y, \"^&0E  \", 48, 2, false);\t//yellow\n\t\tDraw_FunStringWidth (x, y, \"NR\", 48, 2, false);\n\t\tbreak;\n\tcase SRCSTAT_FAILED_REFUSED:\n\t\tDraw_FunStringWidth (x, y, \"^&0E  \", 48, 2, false);\t//yellow\n\t\tDraw_FunStringWidth (x, y, \"REFUSED\", 48, 2, false);\n\t\tbreak;\n\tcase SRCSTAT_FAILED_EOF:\n\t\tDraw_FunStringWidth (x, y, \"^&0E  \", 48, 2, false);\t//yellow\n\t\tDraw_FunStringWidth (x, y, \"EOF\", 48, 2, false);\n\t\tbreak;\n\tcase SRCSTAT_FAILED_MITM:\n\t\tif ((int)(realtime*4) & 1)\t//flash\n\t\t\tDraw_FunStringWidth (x, y, \"^&04  \", 48, 2, false);\t//red\n\t\telse\n\t\t\tDraw_FunStringWidth (x, y, \"^&0E  \", 48, 2, false);\t//yellow\n\t\tDraw_FunStringWidth (x, y, \"^bMITM\", 48, 2, false);\n\t\tbreak;\n\tcase SRCSTAT_FAILED_HTTP:\n\t\tDraw_FunStringWidth (x, y, \"^&0E  \", 48, 2, false);\t//yellow\n\t\tDraw_FunStringWidth (x, y, \"404\", 48, 2, false);\n\t\tbreak;\n\t}\n\n\ttext = va(\"%s%s\",\t(pm_source[c->dint].flags & (SRCFL_ENABLED|SRCFL_DISABLED))?\"\":\"^b\",\n\t\t\t\t\t\tpm_source[c->dint].url);\n\tDraw_FunStringU8 (CON_WHITEMASK, x+48, y, text);\n}\nstatic qboolean MD_Source_Key (struct menucustom_s *c, struct emenu_s *m, int key, unsigned int unicode)\n{\n\tif (key == K_ENTER || key == K_KP_ENTER || key == K_GP_DIAMOND_CONFIRM || key == K_MOUSE1 || key == K_TOUCHTAP)\n\t{\n\t\tif (pm_source[c->dint].flags & SRCFL_DISABLED)\n\t\t{\n\t\t\tpm_source[c->dint].flags = (pm_source[c->dint].flags&~SRCFL_DISABLED)|SRCFL_ENABLED;\n\t\t\tpm_source[c->dint].status = SRCSTAT_UNTRIED;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tpm_source[c->dint].flags = (pm_source[c->dint].flags&~SRCFL_ENABLED)|SRCFL_DISABLED;\n\t\t\tpm_source[c->dint].status = SRCSTAT_PENDING;\n\t\t}\n\t\tPM_WriteInstalledPackages();\n\t\tif (pm_source[c->dint].flags & SRCFL_DISABLED)\n\t\t\tPM_Shutdown(true);\n\t\tPM_UpdatePackageList(true);\n\t\treturn true;\n\t}\n\tif (key == K_DEL || key == K_BACKSPACE || key == K_GP_DIAMOND_ALTCONFIRM)\n\t{\n\t\tif (pm_source[c->dint].flags & SRCFL_ENABLED)\n\t\t{\n\t\t\tpm_source[c->dint].flags = (pm_source[c->dint].flags&~SRCFL_ENABLED)|SRCFL_DISABLED;\n\t\t\tpm_source[c->dint].status = SRCSTAT_PENDING;\n\t\t}\n\t\telse if (pm_source[c->dint].flags & SRCFL_USER)\n\t\t{\n\t\t\tpm_source[c->dint].flags &= ~(SRCFL_ENABLED|SRCFL_DISABLED);\n\t\t\tpm_source[c->dint].flags |= SRCFL_HISTORIC;\n\t\t}\n\t\telse\n\t\t\treturn false;\t//will just be re-added anyway... :(\n\t\tPM_WriteInstalledPackages();\n\t\tif (pm_source[c->dint].flags & SRCFL_DISABLED)\n\t\t\tPM_Shutdown(true);\n\t\tPM_UpdatePackageList(true);\n\t\treturn true;\n\t}\n\treturn false;\n}\n\nstatic void MD_AutoUpdate_Draw (int x, int y, struct menucustom_s *c, struct emenu_s *m)\n{\n\tchar *settings[] = \n\t{\n\t\t\"Off\",\n\t\t\"Stable Updates\",\n\t\t\"Test Updates\"\n\t};\n\tchar *text;\n\tint setting = bound(0, pkg_autoupdate.ival, 2);\n\n\tsize_t i;\n\tfor (i = 0; i < pm_numsources && !(pm_source[i].flags & SRCFL_ENABLED); i++);\n\n\ttext = va(\"%sAuto Update: ^a%s\", (i<pm_numsources)?\"\":\"^h\", settings[setting]);\n//\tif (&m->selecteditem->common == &c->common)\n//\t\tDraw_AltFunString (x, y, text);\n//\telse\n\t\tDraw_FunString (x, y, text);\n}\nstatic qboolean MD_AutoUpdate_Key (struct menucustom_s *c, struct emenu_s *m, int key, unsigned int unicode)\n{\n\tif (key == K_ENTER || key == K_KP_ENTER || key == K_GP_DIAMOND_CONFIRM || key == K_MOUSE1 || key == K_TOUCHTAP)\n\t{\n\t\tchar nv[8] = \"0\";\n\t\tif (pkg_autoupdate.ival < UPD_TESTING && pkg_autoupdate.ival >= 0)\n\t\t\tQ_snprintfz(nv, sizeof(nv), \"%i\", pkg_autoupdate.ival+1);\n\t\tCvar_ForceSet(&pkg_autoupdate, nv);\n\t\tPM_WriteInstalledPackages();\n\n\t\tPM_UpdatePackageList(true);\n\t}\n\treturn false;\n}\n\nstatic qboolean MD_MarkUpdatesButton (union menuoption_s *mo,struct emenu_s *m,int key)\n{\n\tif (key == K_ENTER || key == K_KP_ENTER || key == K_GP_DIAMOND_CONFIRM || key == K_MOUSE1 || key == K_TOUCHTAP)\n\t{\n\t\tPM_MarkUpdates();\n\t\treturn true;\n\t}\n\treturn false;\n}\n#endif\n\nqboolean MD_PopMenu (union menuoption_s *mo,struct emenu_s *m,int key)\n{\n\tif (key == K_ENTER || key == K_KP_ENTER || key == K_GP_DIAMOND_CONFIRM || key == K_MOUSE1 || key == K_TOUCHTAP)\n\t{\n\t\tM_RemoveMenu(m);\n\t\treturn true;\n\t}\n\treturn false;\n}\n\nstatic qboolean MD_ApplyDownloads (union menuoption_s *mo,struct emenu_s *m,int key)\n{\n\tif (key == K_ENTER || key == K_KP_ENTER || key == K_GP_DIAMOND_CONFIRM || key == K_MOUSE1 || key == K_TOUCHTAP)\n\t{\n\t\tPM_PromptApplyChanges();\n\t\treturn true;\n\t}\n\treturn false;\n}\n\nstatic qboolean MD_RevertUpdates (union menuoption_s *mo,struct emenu_s *m,int key)\n{\n\tif (key == K_ENTER || key == K_KP_ENTER || key == K_GP_DIAMOND_CONFIRM || key == K_MOUSE1 || key == K_TOUCHTAP)\n\t{\n\t\tPM_RevertChanges();\n\t\treturn true;\n\t}\n\treturn false;\n}\n\nstatic int MD_AddMapItems(emenu_t *m, package_t *p, int y, const char *filter)\n{\n\tstruct packagedep_s *dep;\n\tmenucustom_t *c;\n\tfor (dep = p->deps; dep; dep = dep->next)\n\t{\n\t\tif (dep->dtype != DEP_MAP)\n\t\t\tcontinue;\n\t\tif (filter)\n\t\t\tif (!strstr(dep->name, filter))\n\t\t\t\tcontinue;\n\t\tc = MC_AddCustom(m, 0, y, p, pm_sequence, NULL);\n\t\tc->dptr2 = dep;\n\t\tc->draw = MD_MapDraw;\n\t\tc->key = MD_MapKey;\n\t\tc->common.width = 320-16;\n\t\tc->common.height = 8;\n\t\ty += 8;\n\t}\n\treturn y;\n}\nstatic int MD_DownloadMenuFiltered(package_t *p, const char *filter)\n{\n\tstruct packagedep_s *dep;\n\tif ((p->flags & DPF_HIDDEN) && (p->arch || !(p->flags & DPF_ENABLED)))\n\t\treturn true;\n\tif ((p->flags & DPF_HIDEUNLESSPRESENT) && !(p->flags & DPF_PRESENT))\n\t\treturn true;\n\n\tif (filter && *filter)\n\t{\n\t\tif (strstr(p->title, filter) ||\n\t\t\tstrstr(p->name, filter) ||\n\t\t\t(p->description && strstr(p->description, filter)) ||\n\t\t\t(p->author && strstr(p->author, filter)))\n\t\t\t;\n\t\telse\n\t\t{\n\t\t\tfor (dep = p->deps; dep; dep = dep->next)\n\t\t\t{\n\t\t\t\tif (dep->dtype == DEP_MAP)\n\t\t\t\t\tif (strstr(dep->name, filter))\n\t\t\t\t\t\treturn -1;\n\t\t\t}\n\t\t\tif (!dep)\n\t\t\t\treturn true;\n\t\t}\n\t}\n\treturn false;\n}\n\nstatic int MD_AddItemsToDownloadMenu(emenu_t *m, int y, const char *pathprefix, const char *filter, void *selpackage)\n{\n\tchar path[MAX_QPATH];\n\tpackage_t *p;\n\tmenucustom_t *c;\n\tchar *slash;\n\tmenuoption_t *mo;\n\tint prefixlen = strlen(pathprefix);\n\tstruct packagedep_s *dep;\n\tdlmenu_t *info = m->data;\n\tint filtered;\n\n\t//add all packages in this dir\n\tfor (p = availablepackages; p; p = p->next)\n\t{\n\t\tif (strncmp(p->category, pathprefix, prefixlen))\n\t\t\tcontinue;\n\t\tfiltered = MD_DownloadMenuFiltered(p, filter);\n\t\tif (filtered==true)\n\t\t\tcontinue;\n\t\tslash = strchr(p->category+prefixlen, '/');\n\t\tif (!slash)\n\t\t{\n\t\t\tchar *head;\n\t\t\tchar *desc = p->description;\n\t\t\tif (p->author || p->license || p->website)\n\t\t\t\thead = va(\"^aauthor:^U00A0^a%s\\n^alicense:^U00A0^a%s\\n^awebsite:^U00A0^a%s\\n\", p->author?p->author:\"^hUnknown^h\", p->license?p->license:\"^hUnknown^h\", p->website?p->website:\"^hUnknown^h\");\n\t\t\telse\n\t\t\t\thead = NULL;\n\t\t\tif (p->filesize)\n\t\t\t{\n\t\t\t\tchar buf[32];\n\t\t\t\tif (!head)\n\t\t\t\t\thead = \"\";\n\t\t\t\thead = va(\"%s^asize:^U00A0^a%s\\n\", head, FS_AbbreviateSize(buf,sizeof(buf), p->filesize));\n\t\t\t}\n\n\t\t\tfor (dep = p->deps; dep; dep = dep->next)\n\t\t\t{\n\t\t\t\tif (dep->dtype == DEP_NEEDFEATURE)\n\t\t\t\t{\n\t\t\t\t\tconst char *featname, *enablecmd;\n\t\t\t\t\tif (!PM_CheckFeature(dep->name, &featname, &enablecmd))\n\t\t\t\t\t{\n\t\t\t\t\t\tif (enablecmd)\n\t\t\t\t\t\t\thead = va(\"^aDisabled: ^a%s\\n%s\", featname, head?head:\"\");\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\thead = va(\"^aUnavailable: ^a%s\\n%s\", featname, head?head:\"\");\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n#ifdef WEBCLIENT\n\t\t\tif (!PM_SignatureOkay(p))\n\t\t\t{\n\t\t\t\tif (!p->signature)\n\t\t\t\t\thead = va(CON_ERROR\"^bSignature missing^b\"CON_DEFAULT\"\\n%s\", head?head:\"\");\t\t\t//some idiot forgot to include a signature\n\t\t\t\telse if (p->flags & DPF_SIGNATUREREJECTED)\n\t\t\t\t\thead = va(CON_ERROR\"^bSignature invalid^b\"CON_DEFAULT\"\\n%s\", head?head:\"\");\t\t\t//some idiot got the wrong auth/sig/hash\n\t\t\t\telse if (p->flags & DPF_SIGNATUREUNKNOWN)\n\t\t\t\t\thead = va(CON_ERROR\"^bSignature is not trusted^b\"CON_DEFAULT\"\\n%s\", head?head:\"\");\t//clientside permission.\n\t\t\t\telse\n\t\t\t\t\thead = va(CON_ERROR\"^bUnable to verify signature^b\"CON_DEFAULT\"\\n%s\", head?head:\"\");\t//clientside problem.\n\t\t\t}\n#endif\n\n\t\t\tif (head && desc)\n\t\t\t\tdesc = va(U8(\"%s\\n%s\"), head, desc);\n\t\t\telse if (head)\n\t\t\t\tdesc = va(U8(\"%s\"), head);\n\n\t\t\tif (developer.ival)\n\t\t\t{\n\t\t\t\tconst char *root = \"?/\";\n\t\t\t\tswitch(p->fsroot)\n\t\t\t\t{\n\t\t\t\t//valid roots\n\t\t\t\tcase FS_ROOT:\t\t\troot = \"$BASEDIR/\";\t\t\t\tbreak;\n\t\t\t\tcase FS_BINARYPATH:\t\troot = \"$BINDIR/\";\t\t\t\tbreak;\n\t\t\t\tcase FS_LIBRARYPATH:\troot = \"$LIBDIR/\";\t\t\t\tbreak;\n\n\t\t\t\t//invalid roots... (we don't use fs_game etc for packages because that breaks when switching mods within the same basedir)\n\t\t\t\tcase FS_GAME:\t\t\troot = \"$FS_GAME/\";\t\t\t\tbreak;\n\t\t\t\tcase FS_GAMEONLY:\t\troot = \"$FS_GAMEONLY/\";\t\t\tbreak;\n\t\t\t\tcase FS_BASEGAMEONLY:\troot = \"$FS_BASEGAMEONLY/\";\t\tbreak;\n\t\t\t\tcase FS_PUBGAMEONLY:\troot = \"$FS_PUBGAMEONLY/\";\t\tbreak;\n\t\t\t\tcase FS_PUBBASEGAMEONLY:root = \"FS_PUBBASEGAMEONLY://\";\tbreak;\n\t\t\t\tcase FS_SYSTEM:\t\t\troot = \"file://\";\t\t\t\tbreak;\n\t\t\t\tdefault:\t\t\t\troot = \"?/\";\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tif (!desc)\n\t\t\t\t\tdesc = \"\";\n\t\t\t\tfor (dep = p->deps; dep; dep = dep->next)\n\t\t\t\t\tif (dep->dtype == DEP_FILE)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (p->qhash || p->packprefix)\n\t\t\t\t\t\t\tdesc = va(\"%s\\n%s%s%s%s.%s%s\", desc, root, p->gamedir, *p->gamedir?\"/\":\"\", dep->name, p->qhash, p->packprefix);\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tdesc = va(\"%s\\n%s%s%s%s\", desc, root, p->gamedir, *p->gamedir?\"/\":\"\", dep->name);\n\t\t\t\t\t}\n\t\t\t}\n\n\t\t\tc = MC_AddCustom(m, 0, y, p, pm_sequence, desc);\n\t\t\tc->draw = MD_Draw;\n\t\t\tc->key = MD_Key;\n\t\t\tc->common.width = 320-16;\n\t\t\tc->common.height = 8;\n\t\t\ty += 8;\n\n\t\t\tif (info->expandedpackage == p || filtered<0)\n\t\t\t{\n\t\t\t\tm->selecteditem = (menuoption_t*)c;\n\n\t\t\t\ty = MD_AddMapItems(m, p, y, (filtered==-1)?filter:NULL);\n\t\t\t}\n\t\t\tif (!m->selecteditem || p == selpackage)\n\t\t\t\tm->selecteditem = (menuoption_t*)c;\n\t\t}\n\t}\n\n\t//and then try to add any subdirs...\n\tfor (p = availablepackages; p; p = p->next)\n\t{\n\t\tif (strncmp(p->category, pathprefix, prefixlen))\n\t\t\tcontinue;\n\t\tif (MD_DownloadMenuFiltered(p, filter)==true)\n\t\t\tcontinue;\n\n\t\tslash = strchr(p->category+prefixlen, '/');\n\t\tif (slash)\n\t\t{\n\t\t\tQ_strncpyz(path, p->category, MAX_QPATH);\n\t\t\tslash = strchr(path+prefixlen, '/');\n\t\t\tif (slash)\n\t\t\t\t*slash = '\\0';\n\n\t\t\tfor (mo = m->options; mo; mo = mo->common.next)\n\t\t\t\tif (mo->common.type == mt_text/*mt_button*/)\n\t\t\t\t\tif (!strcmp(mo->button.text, path + prefixlen))\n\t\t\t\t\t\tbreak;\n\t\t\tif (!mo)\n\t\t\t{\n\t\t\t\ty += 8;\n\t\t\t\tMC_AddBufferedText(m, 48, 320-16, y, path+prefixlen, false, true);\n\t\t\t\ty += 8;\n\t\t\t\tQ_strncatz(path, \"/\", sizeof(path));\n\t\t\t\ty = MD_AddItemsToDownloadMenu(m, y, path, filter, selpackage);\n\t\t\t}\n\t\t}\n\t}\n\treturn y;\n}\n\n#include \"shader.h\"\nstatic void MD_Download_UpdateStatus(struct emenu_s *m)\n{\n\tdlmenu_t *info = m->data;\n\tint y;\n\tpackage_t *p;\n\tunsigned int totalpackages=0, selectedpackages=0, addpackages=0, rempackages=0;\n\tmenuoption_t *si;\n\tmenubutton_t *b, *d;\n#ifdef WEBCLIENT\n\tint i;\n\tunsigned int downloads=0;\n\tmenucustom_t *c;\n\tqboolean sources;\n#endif\n\tfloat framefrac = 0;\n\tvoid *oldpackage = NULL;\n\n\tif (info->downloadablessequence != pm_sequence || !info->populated)\n\t{\n\t\twhile(m->options)\n\t\t{\n\t\t\tmenuoption_t *op = m->options;\n\t\t\tm->options = op->common.next;\n\t\t\tif (op->common.type == mt_frameend)\n\t\t\t\tframefrac = op->frame.frac;\n\t\t\telse if (m->selecteditem == op && op->common.type == mt_custom)\n\t\t\t\toldpackage = op->custom.dptr;\n\t\t\tif (op->common.iszone)\n\t\t\t\tZ_Free(op);\n\t\t}\n\t\tm->cursoritem = m->selecteditem = m->mouseitem = NULL;\n\t\tinfo->downloadablessequence = pm_sequence;\n\n\t\tinfo->populated = false;\n\t\tMC_AddWhiteText(m, 24, 320, 8, \"Downloads\", false)->text = info->titletext;\n\t\tif (info->filtering || *info->filtertext)\n\t\t\tMC_AddWhiteText(m, 24, 320, 16, va(\"%sFilter: %s\", info->filtering?\"^m\":\"\", info->filtertext), false);\n\t\tMC_AddWhiteText(m, 16, 320, 24, \"^Ue01d^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01f\", false);\n\n\t\t//FIXME: should probably reselect the previous selected item. lets just assume everyone uses a mouse...\n\t}\n\n\tfor (p = availablepackages; p; p = p->next)\n\t{\n\t\tif (p->alternative && (p->flags & DPF_HIDDEN))\n\t\t\tp = p->alternative;\n\n\t\ttotalpackages++;\n#ifdef WEBCLIENT\n\t\tif ((p->flags&DPF_PENDING) || p->trymirrors)\n\t\t\tdownloads++;\t//downloading or pending\n#endif\n\t\tif (p->flags & DPF_MARKED)\n\t\t{\n\t\t\tif (p->flags & DPF_ENABLED)\n\t\t\t{\n\t\t\t\tselectedpackages++;\n\t\t\t\tif (p->flags & DPF_PURGE)\n\t\t\t\t{\n\t\t\t\t\trempackages++;\n\t\t\t\t\taddpackages++;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tselectedpackages++;\n\t\t\t\tif (p->flags & DPF_PURGE)\n\t\t\t\t\trempackages++;\t//adding, but also deleting. how weird is that!\n\t\t\t\taddpackages++;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (p->flags & DPF_ENABLED)\n\t\t\t\trempackages++;\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (p->flags & DPF_PURGE)\n\t\t\t\t\trempackages++;\n\t\t\t}\n\t\t}\n\t}\n\n\t//show status.\n\tif (cls.download)\n\t{\t//we can actually download more than one thing at a time, but that makes the UI messy, so only show one active download here.\n\t\tif (cls.download->sizeunknown && cls.download->size == 0)\n\t\t\tQ_snprintfz(info->titletext, sizeof(info->titletext), \"Downloads (%ukbps - %s)\", CL_DownloadRate()/1000, cls.download->localname);\n\t\telse\n\t\t\tQ_snprintfz(info->titletext, sizeof(info->titletext), \"Downloads (%u%% %ukbps - %s)\", (int)cls.download->percent, CL_DownloadRate()/1000, cls.download->localname);\n\t}\n\telse if (!addpackages && !rempackages)\n\t\tQ_snprintfz(info->titletext, sizeof(info->titletext), \"Downloads (%i of %i)\", selectedpackages, totalpackages);\n\telse\n\t\tQ_snprintfz(info->titletext, sizeof(info->titletext), \"Downloads (+%u -%u)\", addpackages, rempackages);\n\n\tif (pkg_updating)\n\t\tQ_snprintfz(info->applymessage, sizeof(info->applymessage), \"Apply (please wait)\");\n\telse if (addpackages || rempackages)\n\t\tQ_snprintfz(info->applymessage, sizeof(info->applymessage), \"%sApply (+%u -%u)\", ((int)(realtime*4)&3)?\"^a\":\"\", addpackages, rempackages);\n\telse\n\t\tQ_snprintfz(info->applymessage, sizeof(info->applymessage), \"Apply\");\n\n\tif (!info->populated)\n\t{\n\t\ty = 48;\n\n\t\tinfo->populated = true;\n\t\tMC_AddFrameStart(m, 48);\n\t\tif (!info->filtering)\n\t\t{\n#ifdef WEBCLIENT\n\t\t\tfor (i = 0, sources=false; i < pm_numsources; i++)\n\t\t\t{\n\t\t\t\tif (pm_source[i].flags & SRCFL_HISTORIC)\n\t\t\t\t\tcontinue;\t//historic... ignore it.\n\t\t\t\tif (!sources)\n\t\t\t\t\tMC_AddBufferedText(m, 48, 320-16, y, \"Sources\", false, true), y += 8;\n\t\t\t\tsources=true;\n\t\t\t\tc = MC_AddCustom(m, 0, y, p, i, NULL);\n\t\t\t\tc->draw = MD_Source_Draw;\n\t\t\t\tc->key = MD_Source_Key;\n\t\t\t\tc->common.width = 320-48-16;\n\t\t\t\tc->common.height = 8;\n\n\t\t\t\tif (!m->selecteditem)\n\t\t\t\t\tm->selecteditem = (menuoption_t*)c;\n\t\t\t\ty += 8;\n\t\t\t}\n\t\t\ty+=4;\t//small gap\n#endif\n\t\t\tMC_AddBufferedText(m, 48, 320-16, y, \"Options\", false, true), y += 8;\n\t\t\tb = MC_AddCommand(m, 48, 320-16, y, info->applymessage, MD_ApplyDownloads);\n\t\t\tb->rightalign = false;\n\t\t\tb->common.tooltip = \"Enable/Disable/Download/Delete packages to match any changes made (you will be prompted with a list of the changes that will be made).\";\n\t\t\ty+=8;\n\t\t\td = b = MC_AddCommand(m, 48, 320-16, y, \"Back\", MD_PopMenu);\n\t\t\tb->rightalign = false;\n\t\t\ty+=8;\n#ifdef WEBCLIENT\n\t\t\tif (pm_numsources)\n\t\t\t{\n\t\t\t\tb = MC_AddCommand(m, 48, 320-16, y, \"Mark Updates\", MD_MarkUpdatesButton);\n\t\t\t\tb->rightalign = false;\n\t\t\t\tb->common.tooltip = \"Select any updated versions of packages that are already installed.\";\n\t\t\t\ty+=8;\n\t\t\t}\n#endif\n\t\t\tb = MC_AddCommand(m, 48, 320-16, y, \"Undo Changes\", MD_RevertUpdates);\n\t\t\tb->rightalign = false;\n\t\t\tb->common.tooltip = \"Reset selection to only those packages that are currently installed.\";\n\t\t\ty+=8;\n#ifdef WEBCLIENT\n\t\t\tif (pm_numsources)\n\t\t\t{\n\t\t\t\tc = MC_AddCustom(m, 48, y, p, 0, NULL);\n\t\t\t\tc->draw = MD_AutoUpdate_Draw;\n\t\t\t\tc->key = MD_AutoUpdate_Key;\n\t\t\t\tc->common.width = 320-48-16;\n\t\t\t\tc->common.height = 8;\n\t\t\t\ty += 8;\n\t\t\t}\n#endif\n\t\t\ty+=4;\t//small gap\n\t\t}\n\t\telse d = NULL;\n\t\tMC_AddBufferedText(m, 48, 320-16, y, \"Packages\", false, true), y += 8;\n\t\tMD_AddItemsToDownloadMenu(m, y, info->pathprefix, info->filtertext, oldpackage);\n\t\tif (!m->selecteditem)\n\t\t\tm->selecteditem = (menuoption_t*)d;\n\t\tif (info->filtering)\n\t\t\tm->cursoritem = NULL;\n\t\telse\n\t\t\tm->cursoritem = (menuoption_t*)MC_AddWhiteText(m, 40, 0, m->selecteditem->common.posy, NULL, false);\n\t\tMC_AddFrameEnd(m, 48)->frac = framefrac;\n\t}\n\n\tsi = m->mouseitem;\n\tif (!si)\n\t\tsi = m->selecteditem;\n\tif (si && si->common.type == mt_custom && si->custom.dptr)\n\t{\n\t\tpackage_t *p = si->custom.dptr;\n\t\tif (p->previewimage)\n\t\t{\n\t\t\tshader_t *sh = R_RegisterPic(p->previewimage, NULL);\n\t\t\tif (R_GetShaderSizes(sh, NULL, NULL, false) > 0)\n\t\t\t\tR2D_Image(0, 0, vid.width, vid.height, 0, 0, 1, 1, sh);\n\t\t}\n\t}\n}\n\nstatic qboolean MD_Download_Key(struct emenu_s *m, int key, unsigned int unicode)\n{\n\tdlmenu_t *info = m->data;\n\n\tif (info->filtering)\n\t{\n\t\tif (key == K_BACKSPACE)\n\t\t{\n\t\t\tunsigned int l = strlen(info->filtertext);\n\t\t\tif (l)\n\t\t\t{\n\t\t\t\twhile (l > 0 && (info->filtertext[--l]&0xc0) == 0x80)\n\t\t\t\t\t;\n\t\t\t\tinfo->filtertext[l] = 0;\n\t\t\t}\n\t\t\telse\n\t\t\t\tinfo->filtering = false;\n\t\t}\n\t\telse if (key == K_ENTER || key == K_ESCAPE)\n\t\t\tinfo->filtering = false; //done...\n\t\telse if (key>=K_F1 && key!=K_RALT && key!=K_RCTRL && key!=K_RSHIFT)\n\t\t{\t//some other action... don't swallow clicks.\n\t\t\tinfo->filtering = false;\n\t\t\tinfo->populated = false;\n\t\t\treturn false;\n\t\t}\n\t\telse if (unicode)\n\t\t{\n\t\t\tunsigned int l = strlen(info->filtertext);\n\t\t\tl+=utf8_encode(info->filtertext+l, unicode, sizeof(info->filtertext)-1-l);\n\t\t\tinfo->filtertext[l] = 0;\n\t\t}\n\n\t\tinfo->populated = false;\n\t\treturn true;\n\t}\n\telse if (key == '/')\n\t{\n\t\tinfo->filtering = true;\n\t\tinfo->filtertext[0] = 0;\n\n\t\tinfo->populated = false;\n\t\treturn true;\n\t}\n\telse\n\t{\n\t\tinfo->filtering = false;\n\t\treturn false;\n\t}\n}\n\nvoid Menu_DownloadStuff_f (void)\n{\n\temenu_t *menu;\n\tdlmenu_t *info;\n\n\tKey_Dest_Remove(kdm_console|kdm_cwindows);\n\tmenu = M_CreateMenu(sizeof(dlmenu_t));\n\tinfo = menu->data;\n\n\tmenu->menu.persist = true;\n\tmenu->predraw = MD_Download_UpdateStatus;\n\tmenu->key = MD_Download_Key;\n\tinfo->downloadablessequence = pm_sequence;\n\n\n\tQ_strncpyz(info->pathprefix, Cmd_Argv(1), sizeof(info->pathprefix));\n\tif (!*info->pathprefix || !loadedinstalled)\n\t\tPM_UpdatePackageList(false);\n\n\tinfo->populated = false;\t//will add any headers as needed\n}\n\n#ifdef WEBCLIENT\nstatic void PM_ConfirmSource(void *ctx, promptbutton_t button)\n{ //yes='Enable', no='Configure', cancel='Later'...\n\tsize_t i;\n\tpm_pendingprompts--;\n\tif (button == PROMPT_YES)\n\t{\n\t\tfor (i = 0; i < pm_numsources; i++)\n\t\t{\n\t\t\tif (!strcmp(pm_source[i].url, ctx))\n\t\t\t{\n\t\t\t\tpm_source[i].flags |= (button == PROMPT_YES)?SRCFL_ENABLED:SRCFL_DISABLED;\n\t\t\t\tPM_WriteInstalledPackages();\n\t\t\t\tPM_UpdatePackageList(true);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\telse\t//cancel or 'customize'\n\t{\n\t\tfor (i = 0; i < pm_numsources; i++)\n\t\t\tpm_source[i].flags |= SRCFL_PROMPTED;\n\n\t\tif (button == PROMPT_NO)\n\t\t\tCmd_ExecuteString(\"menu_download\\n\", RESTRICT_LOCAL);\n\t\telse\n\t\t\tdoautoupdate = false; //try don't want updates... don't give them any.\n\t}\n}\n\n//given a url, try to chop it down to just a hostname\nstatic const char *PrettyHostFromURL(const char *origurl)\n{\n\tchar *url, *end;\n\turl = va(\"%s\", origurl);\n\tif (!strnicmp(url, \"https://\", 8))\n\t\turl+=8;\n\telse if (!strnicmp(url, \"http://\", 7))\n\t\turl+=7;\n\telse\n\t\treturn origurl;\n\n\turl = va(\"%s\", url);\n\tif (*url == '[')\n\t{\t//ipv6 host\n\t\tend = strchr(url+1, ']');\n\t\tif (!end)\n\t\t\treturn origurl;\n\t\t*end = 0;\n\t}\n\telse\n\t{\t//strip any resource part.\n\t\tend = strchr(url, '/');\n\t\tif (end)\n\t\t\t*end = 0;\n\t\t//strip any explicit port number.\n\t\tend = strrchr(url, ':');\n\t\tif (end)\n\t\t\t*end = 0;\n\t}\n\n\treturn url;\n}\n#endif\n\nqboolean PM_AreSourcesNew(qboolean doprompt)\n{\n\tqboolean ret = false;\n#ifdef WEBCLIENT\n\t//only prompt if autoupdate is actually enabled.\n\tint i;\n\tfor (i = 0; i < pm_numsources; i++)\n\t{\n\t\tif (pm_source[i].flags & SRCFL_HISTORIC)\n\t\t\tcontinue;\t//hidden anyway\n\t\tif (!(pm_source[i].flags & (SRCFL_ENABLED|SRCFL_DISABLED|SRCFL_PROMPTED)) && (pkg_autoupdate.ival > 0 || (pm_source[i].flags&SRCFL_MANIFEST)))\n\t\t{\n\t\t\tret = true;\t//something is dirty.\n\t\t\tif (doprompt)\n\t\t\t{\n\t\t\t\tconst char *msg = va(localtext(\"Enable update source\\n\\n^x66F%s\"), (pm_source[i].flags&SRCFL_MANIFEST)?PrettyHostFromURL(pm_source[i].url):pm_source[i].url);\n\t\t\t\tpm_pendingprompts++;\n\t\t\t\tMenu_Prompt(PM_ConfirmSource, Z_StrDup(pm_source[i].url), msg, \"Enable\", \"Configure\", \"Later\", true);\n\t\t\t\tpm_source[i].flags |= SRCFL_PROMPTED;\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t}\n#endif\n\treturn ret;\n}\n\n//should only be called AFTER the filesystem etc is inited.\nvoid Menu_Download_Update(void)\n{\n\tif (pkg_autoupdate.ival <= 0)\n\t\treturn;\n\n\tPM_UpdatePackageList(true);\n}\n#else\nvoid Menu_Download_Update(void)\n{\n#ifdef PACKAGEMANAGER\n\tPM_UpdatePackageList(true);\n#endif\n}\nvoid Menu_DownloadStuff_f (void)\n{\n\tCon_Printf(\"Download menu not implemented in this build\\n\");\n}\n#endif\n"
  },
  {
    "path": "engine/client/m_items.c",
    "content": "//read menu.h\n\n#include \"quakedef.h\"\n#include \"shader.h\"\n#include \"fs.h\"\n\n//draws the size specified, plus a little extra border (about 8 pixels in each direction, could be more though).\n//size is in vpixels.\nvoid Draw_ApproxTextBox (float x, float y, float width, float height)\n{\n\tmpic_t\t*p;\n\tfloat\tcx, cy;\n\tint\t\tn, lines, columns;\n\n\tx -= 8;\n\ty -= 8;\n\n\tp = R2D_SafeCachePic (\"gfx/box_tl.lmp\");\n\tif (R_GetShaderSizes(p, NULL, NULL, false) != true)\t//assume none exist\n\t{\t//simple fill.\n\t\tR2D_ImageColours(0.1, 0.1, 0.1, 0.9);\n\t\tR2D_FillBlock(x, y, width + 16, height + 16);\n\t\tR2D_ImageColours(1.0, 1.0, 1.0, 1.0);\n\t\treturn;\n\t}\n\n\t//okay, we're drawing it with pics.\n\t//expand the border to keep things centred.\n\tlines = ceil(height/8);\n\ty -= (lines*8-height)/2;\n\n\tcolumns = ceil(width/16)*2;\t//columns must be a multiple of 2.\n\tx -= (columns*8-width)/2;\n\n\t// draw left side\n\tcx = x;\n\tcy = y;\n\tif (p)\n\t\tR2D_ScalePic (cx, cy, 8, 8, p);\n\tp = R2D_SafeCachePic (\"gfx/box_ml.lmp\");\n\tfor (n = 0; n < lines; n++)\n\t{\n\t\tcy += 8;\n\t\tif (p)\n\t\t\tR2D_ScalePic (cx, cy, 8, 8, p);\n\t}\n\tp = R2D_SafeCachePic (\"gfx/box_bl.lmp\");\n\tif (p)\n\t\tR2D_ScalePic (cx, cy+8, 8, 8, p);\n\n\t// draw middle\n\tcx += 8;\n\twhile (columns > 0)\n\t{\n\t\tcy = y;\n\t\tp = R2D_SafeCachePic (\"gfx/box_tm.lmp\");\n\t\tif (p)\n\t\t\tR2D_ScalePic (cx, cy, 16, 8, p);\n\t\tp = R2D_SafeCachePic (\"gfx/box_mm.lmp\");\n\t\tfor (n = 0; n < lines; n++)\n\t\t{\n\t\t\tcy += 8;\n\t\t\tif (n == 1)\n\t\t\t\tp = R2D_SafeCachePic (\"gfx/box_mm2.lmp\");\n\t\t\tif (p)\n\t\t\t\tR2D_ScalePic (cx, cy, 16, 8, p);\n\t\t}\n\t\tp = R2D_SafeCachePic (\"gfx/box_bm.lmp\");\n\t\tif (p)\n\t\t\tR2D_ScalePic (cx, cy+8, 16, 8, p);\n\t\tcolumns -= 2;\n\t\tcx += 16;\n\t}\n\n\t// draw right side\n\tcy = y;\n\tp = R2D_SafeCachePic (\"gfx/box_tr.lmp\");\n\tif (p)\n\t\tR2D_ScalePic (cx, cy, 8, 8, p);\n\tp = R2D_SafeCachePic (\"gfx/box_mr.lmp\");\n\tfor (n = 0; n < lines; n++)\n\t{\n\t\tcy += 8;\n\t\tif (p)\n\t\t\tR2D_ScalePic (cx, cy, 8, 8, p);\n\t}\n\tp = R2D_SafeCachePic (\"gfx/box_br.lmp\");\n\tif (p)\n\t\tR2D_ScalePic (cx, cy+8, 8, 8, p);\n}\n\n#ifndef NOBUILTINMENUS\n\nstatic int omousex;\nstatic int omousey;\nstatic qboolean mousemoved;\nstatic qboolean bindingactive;\nextern cvar_t cl_cursor;\nextern cvar_t cl_cursorsize;\nextern cvar_t cl_cursorbias;\nextern cvar_t m_preset_chosen;\nextern menu_t *topmenu;\nmenuoption_t *M_NextSelectableItem(emenu_t *m, menuoption_t *old, qboolean wrap);\n\n#ifdef HEXEN2\n//this function is so fucked up.\n//firstly, the source image uses 0 for transparent instead of 255. this means we need special handling. *sigh*.\n//secondly we have to avoid sampling too much of the image, because i chars seem to have stray white pixels in them\n//thirdly, we hard-code (by eye) the space between chars, which should be different for any character pair.\n//but we're lazy so we don't consider the next char. italic fonts are annoying like that. feel free to refudge it.\nvoid Draw_Hexen2BigFontString(int x, int y, const char *text)\n{\n\tconchar_t *w, buffer[256];\n\tunsigned int codeflags, oldflags=CON_WHITEMASK, c;\n\tint sx, sy;\n\tmpic_t *p;\n\tp = R_RegisterShader (\"gfx/menu/bigfont.lmp\", SUF_2D,\n\t\t\"{\\n\"\n\t\t\t\"if $nofixed\\n\"\n\t\t\t\t\"program default2d\\n\"\n\t\t\t\"endif\\n\"\n\t\t\t\"affine\\n\"\n\t\t\t\"nomipmaps\\n\"\n\t\t\t\"{\\n\"\n\t\t\t\t\"clampmap $diffuse\\n\"\n\t\t\t\t\"rgbgen vertex\\n\"\n\t\t\t\t\"alphagen vertex\\n\"\n\t\t\t\t\"blendfunc gl_one gl_one_minus_src_alpha\\n\"\n\t\t\t\"}\\n\"\n\t\t\t\"sort additive\\n\"\n\t\t\"}\\n\");\n\tif (!p->defaulttextures->base)\n\t{\n\t\tvoid *file;\n\t\tqofs_t fsize = FS_LoadFile(\"gfx/menu/bigfont.lmp\", &file);\n\t\tif (file)\n\t\t{\n\t\t\tunsigned int w = ((unsigned int*)file)[0];\n\t\t\tunsigned int h = ((unsigned int*)file)[1];\n\t\t\tif (8+w*h==fsize)\n\t\t\t\tp->defaulttextures->base = R_LoadReplacementTexture(\"gfx/menu/bigfont.lmp\", NULL, IF_NOPCX|IF_PREMULTIPLYALPHA|IF_UIPIC|IF_NOPICMIP|IF_NOMIPMAP|IF_CLAMP, (qbyte*)file+8, w, h, TF_H2_TRANS8_0);\n\t\t\tFS_FreeFile(file);\t//got image data\n\t\t}\n\t\tif (!p->defaulttextures->base)\n\t\t\tp->defaulttextures->base = R_LoadHiResTexture(\"gfx/menu/bigfont.lmp\", NULL, IF_PREMULTIPLYALPHA|IF_UIPIC|IF_NOPICMIP|IF_NOMIPMAP|IF_CLAMP);\n\t}\n\n\n\tCOM_ParseFunString(oldflags, text, buffer, sizeof(buffer), false);\n\n\tfor (w = buffer; *w; )\n\t{\n\t\tw = Font_Decode(w, &codeflags, &c);\n\t\tif (codeflags & CON_HIDDEN)\n\t\t\tcontinue;\n\t\tif (c >= 0xe020 && c <= 0xe07f)\n\t\t\tc &= 0x00ff; //convert to quake glyph to unicode/ascii...\n\n\t\tif (codeflags != oldflags)\n\t\t{\n\t\t\tvec4_t rgba;\n\t\t\tunsigned int col;\n\t\t\toldflags = codeflags;\n\n\t\t\tcol = (codeflags&CON_FGMASK)>>CON_FGSHIFT;\n\t\t\trgba[0] = consolecolours[col].fr;\n\t\t\trgba[1] = consolecolours[col].fg;\n\t\t\trgba[2] = consolecolours[col].fb;\n\t\t\tif(codeflags & CON_HALFALPHA)\n\t\t\t\trgba[3] = 0.5;\n\t\t\telse\n\t\t\t\trgba[3] = 1;\n\t\t\tif (vid.flags&VID_SRGBAWARE)\n\t\t\t{\n\t\t\t\trgba[0] = M_SRGBToLinear(rgba[0], 1);\n\t\t\t\trgba[1] = M_SRGBToLinear(rgba[1], 1);\n\t\t\t\trgba[2] = M_SRGBToLinear(rgba[2], 1);\n\t\t\t}\n\t\t\tif (codeflags & CON_BLINKTEXT)\n\t\t\t{\n\t\t\t\tfloat a = (sin(realtime*3)+1)*0.3 + 0.4;\n\t\t\t\tVectorScale(rgba, a, rgba);\n\t\t\t}\n\t\t\tR2D_ImageColours(rgba[0], rgba[1], rgba[2], rgba[3]);\n\t\t}\n\n\t\tif (c >= 'a' && c <= 'z')\n\t\t{\n\t\t\tsx = ((c-'a')%8)*20;\n\t\t\tsy = ((c-'a')/8)*20;\n\t\t}\n\t\telse if (c >= 'A' && c <= 'Z')\n\t\t{\n\t\t\tc = c - 'A' + 'a';\n\t\t\tsx = ((c-'a')%8)*20;\n\t\t\tsy = ((c-'a')/8)*20;\n\t\t}\n\t\telse// if (*text <= ' ')\n\t\t{\n\t\t\tsx=-1;\n\t\t\tsy=-1;\n\t\t}\n\t\tif(sx>=0)\n\t\t\tR2D_SubPic(x, y, 18, 20, p, sx, sy, 20*8, 20*4);\n\n\t\tswitch(c)\n\t\t{\n\t\tcase 'a':\tx+=15; break;\n\t\tcase 'b':\tx+=15; break;\n\t\tcase 'c':\tx+=15; break;\n\t\tcase 'd':\tx+=15; break;\n\t\tcase 'e':\tx+=15; break;\n\t\tcase 'f':\tx+=15; break;\n\t\tcase 'g':\tx+=15; break;\n\t\tcase 'h':\tx+=15; break;\n\t\tcase 'i':\tx+=10; break;\n\t\tcase 'j':\tx+=15; break;\n\t\tcase 'k':\tx+=18; break;\n\t\tcase 'l':\tx+=15; break;\n\t\tcase 'm':\tx+=18; break;\n\t\tcase 'n':\tx+=15; break;\n\t\tcase 'o':\tx+=15; break;\n\t\tcase 'p':\tx+=15; break;\n\t\tcase 'q':\tx+=18; break;\n\t\tcase 'r':\tx+=18; break;\n\t\tcase 's':\tx+=13; break;\n\t\tcase 't':\tx+=15; break;\n\t\tcase 'u':\tx+=15; break;\n\t\tcase 'v':\tx+=12; break;\n\t\tcase 'w':\tx+=15; break;\n\t\tcase 'x':\tx+=18; break;\n\t\tcase 'y':\tx+=15; break;\n\t\tcase 'z':\tx+=18; break;\n\t\tdefault:\tx+=20; break;\n\t\t}\n\t}\n\n\tR2D_ImageColours(1, 1, 1, 1);\n}\n#endif\n\nvoid *QBigFontWorks(void)\n{\n\tmpic_t *p;\n\tint i;\n\tchar *names[] = {\n\t\t\"gfx/mcharset.lmp\",\n\t\t\"mcharset.lmp\",\n\t\t\"textures/gfx/mcharset.lmp\",\n\t\t\"textures/mcharset.lmp\",\n\t\tNULL\n\t};\n\tif (font_menu)\n\t\treturn font_menu;\n\tfor (i = 0; names[i]; i++)\n\t{\n\t\tp = R2D_SafeCachePic (names[i]);\n\t\tif (p && R_GetShaderSizes(p, NULL, NULL, true))\n\t\t\treturn p;\n\t}\n\treturn NULL;\n}\nvoid Draw_BigFontString(int x, int y, const char *text)\n{\n\tconchar_t *w, buffer[256];\n\tunsigned int codeflags, oldflags=CON_WHITEMASK, codepoint;\n\tint sx, sy;\n\tmpic_t *p;\n\tp = QBigFontWorks();\n\tif (!p)\n\t{\n\t\tDraw_AltFunString(x, y + (20-8)/2, text);\n\t\treturn;\n\t}\n\tif (p == (mpic_t*)font_menu)\n\t{\n\t\tDraw_FunStringWidthFont(font_menu, x, y, text, vid.width-x, false, false);\n\t\treturn;\n\t}\n\n\t{\t//a hack for scaling\n\t\tp->width = 20*8;\n\t\tp->height = 20*8;\n\t}\n\n\tCOM_ParseFunString(oldflags, text, buffer, sizeof(buffer), false);\n\n\tfor (w = buffer; *w; )\n\t{\n\t\tw = Font_Decode(w, &codeflags, &codepoint);\n\t\tif (codeflags & CON_HIDDEN)\n\t\t\tcontinue;\n\t\tif (codepoint >= 0xe020 && codepoint <= 0xe07f)\n\t\t\tcodepoint &= 0x00ff; //convert to quake glyph to unicode/ascii...\n\n\t\tif (codeflags != oldflags)\n\t\t{\n\t\t\tvec4_t rgba;\n\t\t\tunsigned int col;\n\t\t\toldflags = codeflags;\n\n\t\t\tcol = (codeflags&CON_FGMASK)>>CON_FGSHIFT;\n\t\t\trgba[0] = consolecolours[col].fr;\n\t\t\trgba[1] = consolecolours[col].fg;\n\t\t\trgba[2] = consolecolours[col].fb;\n\t\t\tif(codeflags & CON_HALFALPHA)\n\t\t\t\trgba[3] = 0.5;\n\t\t\telse\n\t\t\t\trgba[3] = 1;\n\t\t\tif (vid.flags&VID_SRGBAWARE)\n\t\t\t{\n\t\t\t\trgba[0] = M_SRGBToLinear(rgba[0], 1);\n\t\t\t\trgba[1] = M_SRGBToLinear(rgba[1], 1);\n\t\t\t\trgba[2] = M_SRGBToLinear(rgba[2], 1);\n\t\t\t}\n\t\t\tif (codeflags & CON_BLINKTEXT)\n\t\t\t{\n\t\t\t\tfloat a = (sin(realtime*3)+1)*0.3 + 0.4;\n\t\t\t\tVectorScale(rgba, a, rgba);\n\t\t\t}\n\t\t\tR2D_ImageColours(rgba[0], rgba[1], rgba[2], rgba[3]);\n\t\t}\n\n\t\tif (codepoint >= 'A' && codepoint <= 'Z')\n\t\t{\n\t\t\tsx = ((codepoint-'A')%8)*(p->width>>3);\n\t\t\tsy = ((codepoint-'A')/8)*(p->height>>3);\n\t\t}\n\t\telse if (codepoint >= 'a' && codepoint <= 'z')\n\t\t{\n\t\t\tsx = ((codepoint-'a'+26)%8)*(p->width>>3);\n\t\t\tsy = ((codepoint-'a'+26)/8)*(p->height>>3);\n\t\t}\n\t\telse if (codepoint >= '0' && codepoint <= '1')\n\t\t{\n\t\t\tsx = ((codepoint-'0'+26*2)%8)*(p->width>>3);\n\t\t\tsy = ((codepoint-'0'+26*2)/8)*(p->height>>3);\n\t\t}\n\t\telse if (codepoint == ':')\n\t\t{\n\t\t\tsx = ((codepoint-'0'+26*2+10)%8)*(p->width>>3);\n\t\t\tsy = ((codepoint-'0'+26*2+10)/8)*(p->height>>3);\n\t\t}\n\t\telse if (codepoint == '/')\n\t\t{\n\t\t\tsx = ((codepoint-'0'+26*2+11)%8)*(p->width>>3);\n\t\t\tsy = ((codepoint-'0'+26*2+11)/8)*(p->height>>3);\n\t\t}\n\t\telse// if (*text <= ' ')\n\t\t{\n\t\t\tsx=-1;\n\t\t\tsy=-1;\n\t\t}\n\t\tif(sx>=0)\n\t\t\tR2D_SubPic(x, y, 20, 20, p, sx, sy, 20*8, 20*8);\n\t\tx+=(p->width>>3);\n\t}\n\tR2D_ImageColours(1,1,1,1);\n}\n\nstatic char *menudotstyle;\nstatic int maxdots;\nstatic int mindot;\nstatic int dotofs;\n\nstatic void MenuTooltipChange(emenu_t *menu, const char *text)\n{\n\tunsigned int MAX_CHARS=2048;\n\tmenutooltip_t *mtt;\n\tif (menu->tooltip)\n\t{\n\t\tZ_Free(menu->tooltip);\n\t\tmenu->tooltip = NULL;\n\t}\n\n\tif (!text || !text[0] || vid.width < 320 || vid.height < 200)\n\t\treturn;\n\n\t// allocate new tooltip structure, copy text to structure\n\tmtt = (menutooltip_t *)Z_Malloc(sizeof(menutooltip_t) + sizeof(conchar_t)*MAX_CHARS);\n\tmtt->end = COM_ParseFunString(CON_WHITEMASK, text, mtt->text, sizeof(conchar_t)*MAX_CHARS, false);\n\n\tmenu->tooltip = mtt;\n}\n\nstatic qboolean MI_Selectable(menuoption_t *op)\n{\n\tswitch(op->common.type)\n\t{\n\tcase mt_text:\n\t\treturn false;\n\tcase mt_button:\n\t\treturn true;\n#ifdef HEXEN2\n\tcase mt_hexen2buttonbigfont:\n\t\treturn true;\n#endif\n\tcase mt_qbuttonbigfont:\n\t\treturn true;\n\tcase mt_menudot:\n\t\treturn false;\n\tcase mt_picturesel:\n\t\treturn true;\n\tcase mt_picture:\n\t\treturn false;\n\tcase mt_framestart:\n\t\treturn false;\n\tcase mt_frameend:\n\t\treturn true;\n\tcase mt_box:\n\t\treturn false;\n\tcase mt_slider:\n\t\treturn true;\n\tcase mt_checkbox:\n\t\treturn true;\n\tcase mt_edit:\n\t\treturn true;\n\tcase mt_bind:\n\t\treturn true;\n\tcase mt_combo:\n\t\treturn true;\n\tcase mt_custom:\n\t\treturn true;\n\tdefault:\n\t\treturn false;\n\t}\n}\n\nstatic qboolean M_MouseMoved(emenu_t *menu)\n{\n\tint ypos = menu->ypos, framescroll = 0;\n\tmenuoption_t *option;\n\tqboolean havemouseitem = false;\n//\tif (menu->prev && !menu->exclusive)\n//\t\tif (M_MouseMoved(menu->prev))\n//\t\t\treturn true;\n\tif (bindingactive)\n\t\treturn true;\n\tfor(option = menu->options; option; option = option->common.next)\n\t{\n\t\tif (option->common.ishidden)\n\t\t\tcontinue;\n\n\t\tif (mousecursor_x > menu->xpos+option->common.posx-option->common.extracollide && mousecursor_x < menu->xpos+option->common.posx+option->common.width)\n\t\t{\n\t\t\tif (mousecursor_y > ypos+option->common.posy && mousecursor_y < ypos+option->common.posy+option->common.height)\n\t\t\t{\n\t\t\t\tif (MI_Selectable(option))\n\t\t\t\t{\n\t\t\t\t\tif (menu->mouseitem != option)\n\t\t\t\t\t{\n\t\t\t\t\t\tmenu->mouseitem = option;\n\t\t\t\t\t\tif (vrui.enabled)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tmenu->selecteditem = option;\n\t\t\t\t\t\t\tif (menu->cursoritem)\n\t\t\t\t\t\t\t\tmenu->cursoritem->common.posy = menu->selecteditem->common.posy + (menu->selecteditem->common.height-menu->cursoritem->common.height)/2;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tmenu->tooltiptime = realtime + 1;\n\t\t\t\t\t\tMenuTooltipChange(menu, menu->mouseitem->common.tooltip);\n\t\t\t\t\t}\n\t\t\t\t\thavemouseitem = true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tswitch(option->common.type)\n\t\t{\n\t\tdefault:\n\t\t\tbreak;\n\t\tcase mt_framestart:\n\t\t\typos += framescroll;\n\t\t\tframescroll = 0;\n\t\t\tbreak;\n\t\tcase mt_frameend:\n\t\t\t{\n\t\t\t\tmenuoption_t *opt2;\n\t\t\t\tint maxy = option->frame.common.posy;\n\t\t\t\tfor (opt2 = option->common.next; opt2; opt2 = opt2->common.next)\n\t\t\t\t{\n\t\t\t\t\tif (opt2->common.posy + opt2->common.height > maxy)\n\t\t\t\t\t\tmaxy = opt2->common.posy + opt2->common.height;\n\t\t\t\t}\n\t\t\t\tmaxy -= vid.height;\n\t\t\t\tframescroll += option->frame.frac * maxy;\n\t\t\t\typos -= option->frame.frac * maxy;\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t}\n\tif (!havemouseitem && menu->mouseitem)\n\t{\n\t\tmenu->mouseitem = NULL;\n\t\tMenuTooltipChange(menu, NULL);\n\t}\n\treturn true;\n}\n\nstatic void M_CheckMouseMove(emenu_t *m)\n{\n\tif (omousex != (int)mousecursor_x || omousey != (int)mousecursor_y)\n\t\tmousemoved = true;\n\telse\n\t\tmousemoved = false;\n\tomousex = mousecursor_x;\n\tomousey = mousecursor_y;\n\n\tif (mousemoved)\n\t\tM_MouseMoved(m);\n}\n\nstatic float M_DrawScrollbar(int x, int y, int width, int height, float frac, qboolean mgrabbed)\n{\n\tfloat unused = 0;\n\tmpic_t *pic;\n\tint knob=y;\n\n\tR2D_ImageColours(1,1,1,1);\n\n\tpic = R2D_SafeCachePic(\"scrollbars/slidebg.tga\");\n\tif (pic && R_GetShaderSizes(pic, NULL, NULL, false)>0)\n\t{\n\t\tunused = 8*2+64;\t//top+bottom are 8 pixels, knob is 64\n\t\tR2D_ScalePic(x + width - 8, y+8, 8, height-16, pic);\n\n\t\tpic = R2D_SafeCachePic(\"scrollbars/arrow_up.tga\");\n\t\tR2D_ScalePic(x + width - 8, y, 8, 8, pic);\n\n\t\tpic = R2D_SafeCachePic(\"scrollbars/arrow_down.tga\");\n\t\tR2D_ScalePic(x + width - 8, y + height - 8, 8, 8, pic);\n\n\t\tknob += 8;\n\t\tknob += frac * (float)(height-(unused));\n\n\t\tpic = R2D_SafeCachePic(\"scrollbars/slider.tga\");\n\t\tR2D_ScalePic(x + width - 8, knob, 8, 64, pic);\n\t}\n\telse\n\t{\n\t\tunused = width;\t//top+bottom are invisible, knob is square\n\t\tR2D_ImageColours(0.1, 0.1, 0.2, 1.0);\n\t\tR2D_FillBlock(x, y, width, height);\n\n\t\tknob += frac * (height-unused);\n\n\t\tR2D_ImageColours(0.35, 0.35, 0.55, 1.0);\n\t\tR2D_FillBlock(x, knob, width, width);\n\t\tR2D_ImageColours(1,1,1,1);\n\t}\n\n\tif (mgrabbed)\n\t{\n\t\tfloat my;\n\n\t\tmy = mousecursor_y - y;\n\t\tmy -= unused/2;\n\t\tmy /= height-unused;\n\t\tif (my > 1)\n\t\t\tmy = 1;\n\t\tif (my < 0)\n\t\t\tmy = 0;\n\t\tfrac = my;\n\t}\n\treturn frac;\n}\n\nstatic void MenuDrawItems(int xpos, int ypos, menuoption_t *option, emenu_t *menu)\n{\n\tint i;\n\tmpic_t *p;\n\tint pw,ph;\n\tint framescroll = 0;\n\tint framescrollheight = 0;\n\n\tmenuframe_t *framescroller = NULL;\n\n\tfor (; option; option = option->common.next)\n\t{\n\t\tif (option->common.ishidden)\n\t\t\tcontinue;\n\n\t\tif (framescroller && option == menu->selecteditem && framescrollheight)\n\t\t{\n\t\t\tif (ypos+option->common.posy < framescroller->common.posy)\n\t\t\t{\n\t\t\t\tint i = ypos+option->common.posy+framescroll - framescroller->common.posy;\n\t\t\t\ti-=8;\n\t\t\t\tframescroller->frac = (i)/(float)framescrollheight;\n\t\t\t\tif (framescroller->frac < 0)\n\t\t\t\t\tframescroller->frac = 0;\n\t\t\t}\n\t\t\telse if (ypos+option->common.posy+option->common.height > framescroller->common.posy+framescroller->common.height)\n\t\t\t{\n\t\t\t\tint i = ypos+option->common.posy+framescroll - framescroller->common.posy;\n\t\t\t\ti -= vid.height-8-framescroller->common.posy-option->common.height;\n\t\t\t\tframescroller->frac = (i)/(float)framescrollheight;\n\t\t\t\tif (framescroller->frac > 1)\n\t\t\t\t\tframescroller->frac = 1;\n\t\t\t}\n\t\t}\n\n\t\tif (&menu->menu == topmenu && menu->mouseitem == option && option->common.type != mt_frameend && !Key_Dest_Has_Higher(kdm_menu))\n\t\t{\n\t\t\tfloat alphamax = 0.5, alphamin = 0.2;\n\t\t\tR2D_ImageColours(.5,.4,0,(sin(realtime*2)+1)*0.5*(alphamax-alphamin)+alphamin);\n\t\t\tR2D_FillBlock(xpos+menu->mouseitem->common.posx, ypos+menu->mouseitem->common.posy, menu->mouseitem->common.width, menu->mouseitem->common.height);\n\t\t\tR2D_ImageColours(1,1,1,1);\n\t\t}\n\t\tswitch(option->common.type)\n\t\t{\n\t\tcase mt_menucursor:\n\t\t\tif (Key_Dest_Has_Higher(kdm_menu))\n\t\t\t\tbreak;\n\t\t\tif ((int)(realtime*4)&1)\n\t\t\t\tDraw_FunString(xpos+option->common.posx, ypos+option->common.posy, \"^Ue00d\");\n\t\t\tbreak;\n\t\tcase mt_text:\n\t\t\tif (!option->text.text)\n\t\t\t{\t//blinking cursor image hack (FIXME)\n\t\t\t\tif (Key_Dest_Has_Higher(kdm_menu))\n\t\t\t\t\tbreak;\n\t\t\t\tif ((int)(realtime*4)&1)\n\t\t\t\t\tDraw_FunString(xpos+option->common.posx, ypos+option->common.posy, \"^Ue00d\");\n\t\t\t}\n\t\t\telse if (option->common.width)\n\t\t\t\tDraw_FunStringWidth(xpos + option->common.posx, ypos+option->common.posy, option->text.text, option->common.width, option->text.rightalign, option->text.isred);\n\t\t\telse if (option->text.isred)\n\t\t\t\tDraw_AltFunString(xpos+option->common.posx, ypos+option->common.posy, option->text.text);\n\t\t\telse\n\t\t\t\tDraw_FunString(xpos+option->common.posx, ypos+option->common.posy, option->text.text);\n\t\t\tbreak;\n\t\tcase mt_button:\n\t\t\tDraw_FunStringWidth(xpos + option->common.posx, ypos+option->common.posy, option->button.text, option->common.width, option->button.rightalign, !menu->cursoritem && menu->selecteditem == option);\n\t\t\tbreak;\n#ifdef HEXEN2\n\t\tcase mt_hexen2buttonbigfont:\n\t\t\tDraw_Hexen2BigFontString(xpos+option->common.posx, ypos+option->common.posy, option->button.text);\n\t\t\tbreak;\n#endif\n\t\tcase mt_qbuttonbigfont:\n\t\t\tDraw_BigFontString(xpos+option->common.posx, ypos+option->common.posy, option->button.text);\n\t\t\tbreak;\n\t\tcase mt_menudot:\n\t\t\tif (Key_Dest_Has_Higher(kdm_menu))\n\t\t\t\tbreak;\n\t\t\ti = (int)(realtime * 10)%maxdots;\n\t\t\tp = R2D_SafeCachePic(va(menudotstyle, i+mindot ));\n\t\t\tif (R_GetShaderSizes(p, &pw, &ph, false)>0)\n\t\t\t\tR2D_ScalePic(xpos+option->common.posx, ypos+option->common.posy+dotofs, (pw/(float)ph)*option->common.width, option->common.height, p);\n\t\t\telse if ((int)(realtime*4)&1)\n\t\t\t\tDraw_FunString(xpos+option->common.posx, ypos+option->common.posy + (option->common.height-8)/2, \"^a^Ue00d\");\n\t\t\tbreak;\n\t\tcase mt_picturesel:\n\t\t\tp = NULL;\n\t\t\tif (menu->selecteditem && menu->selecteditem->common.posx == option->common.posx && menu->selecteditem->common.posy == option->common.posy && !Key_Dest_Has_Higher(kdm_menu))\n\t\t\t{\n\t\t\t\tchar selname[MAX_QPATH];\n\t\t\t\tQ_strncpyz(selname, option->picture.picturename, sizeof(selname));\n\t\t\t\tCOM_StripExtension(selname, selname, sizeof(selname));\n\t\t\t\tQ_strncatz(selname, \"_sel\", sizeof(selname));\n\t\t\t\tp = R2D_SafeCachePic(selname);\n\t\t\t}\n\t\t\tif (!R_GetShaderSizes(p, &pw, &ph, false))\n\t\t\t\tp = R2D_SafeCachePic(option->picture.picturename);\n\n\t\t\tif (R_GetShaderSizes(p, &pw, &ph, false)>0)\n\t\t\t{\n\t\t\t\tfloat scale = (option->common.height?option->common.height:20.0)/ph;\n\t\t\t\tR2D_ScalePic(xpos+option->common.posx, ypos+option->common.posy, option->common.width?option->common.width:(pw*scale), ph*scale, p);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase mt_picture:\n\t\t\tp = R2D_SafeCachePic(option->picture.picturename);\n\t\t\tif (R_GetShaderSizes(p, NULL, NULL, false)>0) R2D_ScalePic(xpos+option->common.posx, ypos+option->common.posy, option->common.width, option->common.height, p);\n\t\t\tbreak;\n\t\tcase mt_framestart:\n\t\t\typos += framescroll;\n\t\t\tframescroll = 0;\n\t\t\tif (R2D_Flush)\n\t\t\t\tR2D_Flush();\n\t\t\tBE_Scissor(NULL);\n\n\t\t\tframescroller = NULL;\n\t\t\tbreak;\n\t\tcase mt_frameend:\n\t\t\t{\n\t\t\t\tsrect_t srect;\n\t\t\t\tmenuoption_t *opt2;\n\t\t\t\tint maxy = option->frame.common.posy;\n\t\t\t\toption->frame.common.width = 16;\n\t\t\t\toption->frame.common.posx = vid.width - option->frame.common.width - xpos;\n\t\t\t\toption->frame.common.height = vid.height-maxy - ypos;\n\t\t\t\tfor (opt2 = option->common.next; opt2; opt2 = opt2->common.next)\n\t\t\t\t{\n\t\t\t\t\tif (opt2->common.posy + opt2->common.height > maxy)\n\t\t\t\t\t\tmaxy = opt2->common.posy + opt2->common.height;\n\t\t\t\t}\n\t\t\t\tmaxy -= vid.height;\n\t\t\t\tframescrollheight = maxy;\n\n\t\t\t\tif (maxy <= 0)\n\t\t\t\t{\n\t\t\t\t\toption->frame.mousedown = false;\n\t\t\t\t\toption->frame.frac = 0;\n\t\t\t\t\toption->frame.common.width = 0;\n\t\t\t\t\toption->frame.common.height = 0;\n\t\t\t\t\tframescrollheight= 0;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tif (!keydown[K_MOUSE1] && !keydown[K_TOUCHTAP])\n\t\t\t\t\t\toption->frame.mousedown = false;\n\t\t\t\t\toption->frame.frac = M_DrawScrollbar(xpos+option->frame.common.posx, ypos+option->common.posy, option->frame.common.width, option->frame.common.height, option->frame.frac, option->frame.mousedown);\n\n\t\t\t\t\tif (R2D_Flush)\n\t\t\t\t\t\tR2D_Flush();\n\t\t\t\t\tsrect.x = 0;\n\t\t\t\t\tsrect.y = (float)(ypos+option->common.posy) / vid.height;\n\t\t\t\t\tsrect.width = 1;\n\t\t\t\t\tsrect.height = 1 - srect.y;\n\t\t\t\t\tsrect.dmin = -99999;\n\t\t\t\t\tsrect.dmax = 99999;\n\t\t\t\t\tsrect.y = (1-srect.y) - srect.height;\n\t\t\t\t\tBE_Scissor(&srect);\n\n\t\t\t\t\tframescroller = &option->frame;\n\t\t\t\t\tframescroll += framescroller->frac * maxy;\n\t\t\t\t\typos -= framescroller->frac * maxy;\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\t\tcase mt_box:\n\t\t\tDraw_ApproxTextBox(xpos+option->common.posx, ypos+option->common.posy, option->box.width, option->box.height);\n\t\t\tbreak;\n\t\tcase mt_slider:\n\t\t\tif (option->slider.var)\n\t\t\t{\n#define SLIDER_RANGE 10\n\t\t\t\tfloat range;\n\t\t\t\tint\ti;\n\t\t\t\tint x = xpos+option->common.posx;\n\t\t\t\tint y = ypos+option->common.posy;\n\t\t\t\tint s;\n\n\t\t\t\trange = (option->slider.current - option->slider.min)/(option->slider.max-option->slider.min);\n\n\t\t\t\tif (option->slider.text)\n\t\t\t\t{\n\t\t\t\t\tDraw_FunStringWidth(x, y, option->slider.text, option->slider.textwidth, true, !menu->cursoritem && menu->selecteditem == option);\n\t\t\t\t\tx += option->slider.textwidth + 3*8;\n\t\t\t\t}\n\n\t\t\t\tif (range < 0)\n\t\t\t\t\trange = 0;\n\t\t\t\tif (range > 1)\n\t\t\t\t\trange = 1;\n\t\t\t\toption->slider.vx = x;\n\t\t\t\tx -= 8;\n\t\t\t\tFont_BeginString(font_default, x, y, &x, &y);\n\t\t\t\tx = Font_DrawChar(x, y, CON_WHITEMASK, 0xe080);\n\t\t\t\ts = x;\n\t\t\t\tfor (i=0 ; i<SLIDER_RANGE ; i++)\n\t\t\t\t\tx = Font_DrawChar(x, y, CON_WHITEMASK, 0xe081);\n\t\t\t\tFont_DrawChar(x, y, CON_WHITEMASK, 0xe082);\n\t\t\t\tFont_DrawChar(s + (x-s) * range - Font_CharWidth(CON_WHITEMASK, 0xe083)/2, y, CON_WHITEMASK, 0xe083);\n\t\t\t\tFont_EndString(font_default);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase mt_checkbox:\n\t\t\t{\n\t\t\t\tint x = xpos+option->common.posx;\n\t\t\t\tint y = ypos+option->common.posy;\n\t\t\t\tqboolean on;\n\t\t\t\tif (option->check.func)\n\t\t\t\t\ton = option->check.func(&option->check, menu, CHK_CHECKED);\n\t\t\t\telse if (!option->check.var)\n\t\t\t\t\t\ton = option->check.value;\n\t\t\t\telse if (option->check.bits)\t//bits is a bitmask for use with cvars (users can be clumsy, so bittage of 0 uses non-zero as true, but sets only bit 1)\n\t\t\t\t{\n\t\t\t\t\tif (option->check.var->latched_string)\n\t\t\t\t\t\ton = atoi(option->check.var->latched_string)&option->check.bits;\n\t\t\t\t\telse\n\t\t\t\t\t\ton = (int)(option->check.var->value)&option->check.bits;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tif (option->check.var->latched_string)\n\t\t\t\t\t\ton = !!atof(option->check.var->latched_string);\n\t\t\t\t\telse\n\t\t\t\t\t\ton = !!option->check.var->value;\n\t\t\t\t}\n\n\t\t\t\tif (option->check.text)\n\t\t\t\t{\n\t\t\t\t\tDraw_FunStringWidth(x, y, option->check.text, option->check.textwidth, true, (!menu->cursoritem && menu->selecteditem == option) | ((option->check.var && (option->check.var->flags&CVAR_RENDEREROVERRIDE))?4:0));\n\t\t\t\t\tx += option->check.textwidth + 3*8;\n\t\t\t\t}\n#if 0\n\t\t\t\tif (on)\n\t\t\t\t\tDraw_Character (x, y, 0xe083);\n\t\t\t\telse\n\t\t\t\t\tDraw_Character (x, y, 0xe081);\n#endif\n\t\t\t\tif (!menu->cursoritem && menu->selecteditem == option)\n\t\t\t\t\tDraw_AltFunString (x, y, on ? localtext(\"on\") : localtext(\"off\"));\n\t\t\t\telse\n\t\t\t\t\tDraw_FunString (x, y, on ? localtext(\"on\") : localtext(\"off\"));\n\t\t\t}\n\t\t\tbreak;\n\t\tcase mt_edit:\n\t\t\t{\n\t\t\t\tint x = xpos+option->common.posx;\n\t\t\t\tint y = ypos+option->common.posy;\n\n\t\t\t\tif (!option->edit.slim)\n\t\t\t\t\ty += (option->common.height-8)/2;\t//fat ones are twice the height on account of the text box's borders.\n\n\t\t\t\tDraw_FunStringWidth(x, y, option->edit.caption, option->edit.captionwidth, true, !menu->cursoritem && menu->selecteditem == option);\n\t\t\t\tx += option->edit.captionwidth + 3*8;\n\t\t\t\tif (option->edit.slim)\n\t\t\t\t\tx += 8; // more space for cursor\n\t\t\t\telse\n\t\t\t\t\tDraw_ApproxTextBox(x, y, 16*8, 8);\n\t\t\t\tDraw_FunString(x, y, option->edit.text);\n\n\t\t\t\tif (menu->selecteditem == option && (int)(realtime*4) & 1 && !Key_Dest_Has_Higher(kdm_menu))\n\t\t\t\t{\n\t\t\t\t\tvid.ime_allow = true;\n\t\t\t\t\tvid.ime_position[0] = x;\n\t\t\t\t\tvid.ime_position[1] = y+8;\n\n\t\t\t\t\tx += strlen(option->edit.text)*8;\n\t\t\t\t\tDraw_FunString(x, y, \"^Ue00b\");\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\t\tcase mt_bind:\n\t\t\t{\n\t\t\t\tint x = xpos+option->common.posx;\n\t\t\t\tint y = ypos+option->common.posy;\n\t\t\t\tint\t\tkeys[8], keymods[countof(keys)];\n\t\t\t\tint keycount;\n\t\t\t\tconst char *keyname;\n\t\t\t\tint j;\n\n\t\t\t\tDraw_FunStringWidth(x, y, option->bind.caption, option->bind.captionwidth, true, !menu->cursoritem && menu->selecteditem == option);\n\t\t\t\tx += option->bind.captionwidth + 3*8;\n\n\t\t\t\tkeycount = M_FindKeysForCommand (0, cl_forceseat.ival, option->bind.command, keys, keymods, countof(keys));\n\n\t\t\t\tif (bindingactive && menu->selecteditem == option)\n\t\t\t\t\tDraw_FunString (x, y, \"Press key\");\n\t\t\t\telse if (!keycount)\n\t\t\t\t\tDraw_FunString (x, y, \"^8??\"\"?\");\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tfor (j = 0; j < keycount; j++)\n\t\t\t\t\t{\t/*these offsets are wrong*/\n\t\t\t\t\t\tif (j)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tDraw_FunString (x + 8, y, \"^8or\");\n\t\t\t\t\t\t\tx += 32;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tkeyname = Key_KeynumToLocalString (keys[j], keymods[j]);\n\t\t\t\t\t\tDraw_FunString (x, y, keyname);\n\t\t\t\t\t\tx += strlen(keyname) * 8;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase mt_combo:\n\t\t\t{\n\t\t\t\tint x = xpos+option->common.posx;\n\t\t\t\tint y = ypos+option->common.posy;\n\n\t\t\t\tDraw_FunStringWidth(x, y, option->combo.caption, option->combo.captionwidth, true, !menu->cursoritem && menu->selecteditem == option);\n\t\t\t\tx += option->combo.captionwidth + 3*8;\n\n\t\t\t\tif (option->combo.numoptions)\n\t\t\t\t{\n\t\t\t\t\tif (!menu->cursoritem && menu->selecteditem == option)\n\t\t\t\t\t\tDraw_AltFunString(x, y, option->combo.options[option->combo.selectedoption]);\n\t\t\t\t\telse\n\t\t\t\t\t\tDraw_FunString(x, y, option->combo.options[option->combo.selectedoption]);\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\t\tcase mt_custom:\n\t\t\toption->custom.draw(xpos+option->common.posx, ypos+option->common.posy, &option->custom, menu);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tSys_Error(\"Bad item type\\n\");\n\t\t\tbreak;\n\t\t}\n\t}\n}\n\nstatic void MenuDraw(emenu_t *menu)\n{\n\tif (!menu->dontexpand)\n\t{\n\t\tmenu->width = min(vid.width,320);\n\t\tmenu->xpos = ((vid.width - menu->width)>>1);\n\t}\n\tif (menu->predraw)\n\t\tmenu->predraw(menu);\n\tif (menu->selecteditem && menu->selecteditem->common.type == mt_text)\n\t\tmenu->menu.showosk = true;\n\telse\n\t\tmenu->menu.showosk = false;\n\tMenuDrawItems(menu->xpos, menu->ypos, menu->options, menu);\n\t// draw tooltip\n\tif (menu->mouseitem && menu->tooltip && realtime > menu->tooltiptime && !Key_Dest_Has_Higher(kdm_menu))\n\t{\n//\t\tmenuoption_t *option = menu->mouseitem;\n\n//\t\tif (omousex > menu->xpos+option->common.posx && omousex < menu->xpos+option->common.posx+option->common.width)\n//\t\t\tif (omousey > menu->ypos+option->common.posy && omousey < menu->ypos+option->common.posy+option->common.height)\n\t\t\t{\n\t\t\t\tint x = omousex+8;\n\t\t\t\tint y = omousey+8;\n\t\t\t\tint w;\n\t\t\t\tint h;\n\t\t\t\tint l, lines;\n\t\t\t\tconchar_t *line_start[16];\n\t\t\t\tconchar_t *line_end[countof(line_start)];\n\n\t\t\t\t//figure out the line breaks\n\t\t\t\tFont_BeginString(font_default, 0, 0, &l, &l);\n\t\t\t\tlines = Font_LineBreaks(menu->tooltip->text, menu->tooltip->end, min(vid.pixelwidth/2, 30*8*vid.pixelwidth/vid.width), countof(line_start), line_start, line_end);\n\t\t\t\tFont_EndString(font_default);\n\n\t\t\t\t//figure out how wide that makes the tip\n\t\t\t\tw = 0;\n\t\t\t\th = lines*8;\n\t\t\t\tfor (l = 0; l < lines; l++)\n\t\t\t\t{\n\t\t\t\t\tint lw = Font_LineWidth(line_start[l], line_end[l])*vid.width/vid.pixelwidth;\n\t\t\t\t\tif (w < lw)\n\t\t\t\t\t\tw = lw;\n\t\t\t\t}\n\n\t\t\t\t// keep the tooltip within view\n\t\t\t\tif (x + w >= vid.width)\n\t\t\t\t\tx = vid.width - w - 1;\n\t\t\t\tif (y + h >= vid.height)\n\t\t\t\t\ty -= h;\n\n\t\t\t\t// draw the background\n\t\t\t\tDraw_ApproxTextBox(x, y, w, lines*8);\n\n\t\t\t\t//draw the text\n\t\t\t\tFont_BeginString(font_default, x, y, &x, &y);\n\t\t\t\tfor (l = 0; l < lines; l++)\n\t\t\t\t{\n\t\t\t\t\tFont_LineDraw(x, y, line_start[l], line_end[l]);\n\t\t\t\t\ty += Font_CharHeight();\n\t\t\t\t}\n\t\t\t\tFont_EndString(font_default);\n\t\t\t}\n\t}\n\n\tif (menu->postdraw)\n\t\tmenu->postdraw(menu);\n}\n\n\nmenutext_t *MC_AddWhiteText(emenu_t *menu, int lhs, int rhs, int y, const char *text, int rightalign)\n{\n\tmenutext_t *n = Z_Malloc(sizeof(menutext_t) + (text?strlen(text):0)+1);\n\tn->common.type = mt_text;\n\tn->common.iszone = true;\n\tn->common.posx = lhs;\n\tn->common.posy = y;\n\tn->common.width = (rhs)?rhs-lhs:0;\n\tn->common.height = 8;\n\tn->rightalign = rightalign;\n\tif (text)\n\t{\n\t\tn->text = (char*)(n+1);\n\t\tstrcpy((char*)(n+1), (text));\n\t}\n\n\tn->common.next = menu->options;\n\tmenu->options = (menuoption_t *)n;\n\treturn n;\n}\n\nmenutext_t *MC_AddBufferedText(emenu_t *menu, int lhs, int rhs, int y, const char *text, int rightalign, qboolean red)\n{\n\tmenutext_t *n = Z_Malloc(sizeof(menutext_t) + strlen(text)+1);\n\tn->common.type = mt_text;\n\tn->common.iszone = true;\n\tn->common.posx = lhs;\n\tn->common.posy = y;\n\tn->common.width = rhs?rhs-lhs:0;\n\tn->text = (char *)(n+1);\n\tstrcpy((char *)(n+1), text);\n\tn->isred = red;\n\n\tif (rightalign && text)\n\t\tn->common.posx -= strlen(text)*8;\n\n\tn->common.next = menu->options;\n\tmenu->options = (menuoption_t *)n;\n\treturn n;\n}\n\nmenutext_t *MC_AddRedText(emenu_t *menu, int lhs, int rhs, int y, const char *text, int rightalign)\n{\n\tmenutext_t *n;\n\tn = MC_AddWhiteText(menu, lhs, rhs, y, text, rightalign);\n\tn->isred = true;\n\treturn n;\n}\n\nmenubind_t *MC_AddBind(emenu_t *menu, int cx, int bx, int y, const char *caption, char *command, const char *tooltip)\n{\n\tmenubind_t *n = Z_Malloc(sizeof(*n) + strlen(caption)+1 + strlen(command)+1 + (tooltip?strlen(tooltip)+1:0));\n\tn->common.type = mt_bind;\n\tn->common.iszone = true;\n\tn->common.posx = cx;\n\tn->common.posy = y;\n\tn->captionwidth = bx-cx;\n\tn->caption = (char *)(n+1);\n\tstrcpy(n->caption, caption);\n\tn->command = n->caption+strlen(n->caption)+1;\n\tstrcpy(n->command, command);\n\tif (tooltip)\n\t{\n\t\tchar *tip = n->command+strlen(n->command)+1;\n\t\tn->common.tooltip = tip;\n\t\tstrcpy(tip, tooltip);\n\t}\n\tn->common.width = n->captionwidth + 64;\n\tn->common.height = 8;\n\n\tn->common.next = menu->options;\n\tmenu->options = (menuoption_t *)n;\n\treturn n;\n}\n\nmenupicture_t *MC_AddSelectablePicture(emenu_t *menu, int x, int y, int height, const char *picname)\n{\n\tchar selname[MAX_QPATH];\n\tmenupicture_t *n;\n\n\tif (qrenderer == QR_NONE)\n\t\treturn NULL;\n\n\tQ_strncpyz(selname, picname, sizeof(selname));\n\tCOM_StripExtension(selname, selname, sizeof(selname));\n\tQ_strncatz(selname, \"_sel\", sizeof(selname));\n\n\tR2D_SafeCachePic(picname);\n\tR2D_SafeCachePic(selname);\n\n\tn = Z_Malloc(sizeof(menupicture_t) + strlen(picname)+1);\n\tn->common.type = mt_picturesel;\n\tn->common.iszone = true;\n\tn->common.posx = x;\n\tn->common.posy = y;\n\tn->common.height = height;\n\tn->picturename = (char *)(n+1);\n\tstrcpy(n->picturename, picname);\n\n\tn->common.next = menu->options;\n\tmenu->options = (menuoption_t *)n;\n\treturn n;\n}\n\nmenupicture_t *MC_AddPicture(emenu_t *menu, int x, int y, int width, int height, const char *picname)\n{\n\tmenupicture_t *n;\n\tif (qrenderer == QR_NONE)\n\t\treturn NULL;\n\n\tR2D_SafeCachePic(picname);\n\n\tn = Z_Malloc(sizeof(menupicture_t) + strlen(picname)+1);\n\tn->common.type = mt_picture;\n\tn->common.iszone = true;\n\tn->common.posx = x;\n\tn->common.posy = y;\n\tn->common.width = width;\n\tn->common.height = height;\n\tn->picturename = (char *)(n+1);\n\tstrcpy(n->picturename, picname);\n\n\tn->common.next = menu->options;\n\tmenu->options = (menuoption_t *)n;\n\treturn n;\n}\n\nmenupicture_t *MC_AddCenterPicture(emenu_t *menu, int y, int height, const char *picname)\n{\n\tint x;\n\tint width;\n\tmpic_t *p;\n\n\tif (qrenderer == QR_NONE)\n\t\treturn NULL;\n\tp = R2D_SafeCachePic(picname);\n\tif (!p)\n\t{\n\t\tx = 320/2;\n\t\twidth = 64;\n\t}\n\telse\n\t{\n\t\tint pwidth, pheight;\n\t\tif (R_GetShaderSizes(p, &pwidth, &pheight, true))\n\t\t\twidth = (pwidth * (float)height) / pheight;\n\t\telse\n\t\t\twidth = 64;\n\t\tx = (320-(int)width)/2;\n\t}\n\n\treturn MC_AddPicture(menu, x, y, width, height, picname);\n}\n\nmenuoption_t *MC_AddCursorSmall(emenu_t *menu, menuresel_t *reselection, int x)\n{\n\tmenuoption_t *n = Z_Malloc(sizeof(menucommon_t));\n\tif (reselection)\n\t\tmenu->reselection = reselection;\n\tn->common.type = mt_menucursor;\n\tn->common.iszone = true;\n\tn->common.posx = x;\n\tn->common.height = 8;\n\tif (!menu->selecteditem)\n\t\tn->common.posy = -8;\n\telse\n\t\tn->common.posy = menu->selecteditem->common.posy + (menu->selecteditem->common.height-n->common.height)/2;\n\n\tn->common.next = menu->options;\n\tmenu->options = (menuoption_t *)n;\n\n\n\tif (menu->reselection)\n\t{\n\t\tmenuoption_t *sel, *firstsel = M_NextSelectableItem(menu, NULL, false);\n\t\tfor (sel = firstsel; sel; )\n\t\t{\n\t\t\tif (sel->common.posx == menu->reselection->x && sel->common.posy == menu->reselection->y)\n\t\t\t{\n\t\t\t\tmenu->selecteditem = sel;\n\t\t\t\tn->common.posy = sel->common.posy + (sel->common.height-n->common.height)/2;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tsel = M_NextSelectableItem(menu, sel, false);\n\t\t\tif (sel == firstsel)\n\t\t\t\tbreak;\n\t\t}\n\t}\n\treturn n;\n}\n\nmenupicture_t *MC_AddCursor(emenu_t *menu, menuresel_t *reselection, int x, int y)\n{\n\tint i;\n\tmenupicture_t *n = Z_Malloc(sizeof(menupicture_t));\n\tif (reselection)\n\t\tmenu->reselection = reselection;\n\tn->common.type = mt_menudot;\n\tn->common.iszone = true;\n\tn->common.posx = x;\n\tn->common.posy = y;\n\tn->common.width = 20;\n\tn->common.height = 20;\n\n\tn->common.next = menu->options;\n\tmenu->options = (menuoption_t *)n;\n\n\tswitch(M_GameType())\n\t{\n#ifdef Q2CLIENT\n\tcase MGT_QUAKE2:\n\t\t//AND QUAKE 2 WINS!!!\n\t\tmenudotstyle = \"pics/m_cursor%i.pcx\";\n\t\tmindot = 0;\n\t\tmaxdots = 15;\n\t\tdotofs=0;\n\n\t\t//this is *obviously* the correct size... not.\n\t\tn->common.width = 22;\n\t\tn->common.height = 29;\n\t\tbreak;\n#endif\n#ifdef HEXEN2\n\tcase MGT_HEXEN2:\n\t\t//AND THE WINNER IS HEXEN 2!!!\n\t\tmenudotstyle = \"gfx/menu/menudot%i.lmp\";\n\t\tmindot = 1;\n\t\tmaxdots = 8;\n\t\tdotofs=-2;\n\t\tbreak;\n#endif\n\tdefault:\n\t\t//QUAKE 1 WINS BY DEFAULT!\n\t\tmenudotstyle = \"gfx/menudot%i.lmp\";\n\t\tmindot = 1;\n\t\tmaxdots = 6;\n\t\tdotofs=0;\n\t\tbreak;\n\t}\n\n\t//cache them all. this avoids weird flickering as things get dynamically loaded\n\tfor (i = 0; i < maxdots; i++)\n\t\tR2D_SafeCachePic(va(menudotstyle, i+mindot));\n\n\tif (menu->reselection)\n\t{\n\t\tmenuoption_t *sel, *firstsel = M_NextSelectableItem(menu, NULL, false);\n\t\tfor (sel = firstsel; sel; )\n\t\t{\n\t\t\tif (sel->common.posx == menu->reselection->x && sel->common.posy == menu->reselection->y)\n\t\t\t{\n\t\t\t\tmenu->selecteditem = sel;\n\t\t\t\tn->common.posy = sel->common.posy;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tsel = M_NextSelectableItem(menu, sel, false);\n\t\t\tif (sel == firstsel)\n\t\t\t\tbreak;\n\t\t}\n\t}\n\treturn n;\n}\n\nmenuedit_t *MC_AddEdit(emenu_t *menu, int cx, int ex, int y, const char *text, const char *def)\n{\n\tmenuedit_t *n = Z_Malloc(sizeof(menuedit_t)+strlen(text)+1);\n\tn->slim = false;\n\tn->common.type = mt_edit;\n\tn->common.iszone = true;\n\tn->common.posx = cx;\n\tn->common.posy = y;\n\tn->common.width = ex-cx+(17)*8;\n\tn->common.height = 8 + (n->slim?0:(8*2));\t//the 8bit artwork has 8*8 borders - only 4 pixels of that contains any actual data, but replacement images don't stick to that. so just treat them as the full +/- 8 extents here.\n\tn->modified = true;\n\tn->captionwidth = ex-cx;\n\tn->caption = (char *)(n+1);\n\tstrcpy((char *)(n+1), text);\n\tQ_strncpyz(n->text, def, sizeof(n->text));\n\n\tn->common.next = menu->options;\n\tmenu->options = (menuoption_t *)n;\n\treturn n;\n}\n\nmenuedit_t *MC_AddEditCvar(emenu_t *menu, int cx, int ex, int y, const char *text, const char *name, qboolean isslim)\n{\n\tmenuedit_t *n = Z_Malloc(sizeof(menuedit_t)+strlen(text)+1);\n\tcvar_t *cvar;\n\tcvar = Cvar_Get(name, \"\", CVAR_USERCREATED|CVAR_ARCHIVE, NULL);\t//well, this is a menu/\n\tn->slim = isslim;\n\tn->common.type = mt_edit;\n\tn->common.iszone = true;\n\tn->common.posx = cx;\n\tn->common.posy = y;\n\tn->common.width = ex-cx+(17)*8;\n\tn->common.height = n->slim?8:16;\n\tn->common.tooltip = cvar->description;\n\tn->modified = true;\n\tn->captionwidth = ex-cx;\n\tn->caption = (char *)(n+1);\n\tstrcpy((char *)(n+1), text);\n\tn->cvar = cvar;\n#ifdef _DEBUG\n\tif (!(cvar->flags & CVAR_ARCHIVE))\n\t\tCon_Printf(\"Warning: %s is not set for archiving\\n\", cvar->name);\n#endif\n\tQ_strncpyz(n->text, cvar->string, sizeof(n->text));\n\n\tn->common.next = menu->options;\n\tmenu->options = (menuoption_t *)n;\n\treturn n;\n}\n\nmenubox_t *MC_AddBox(emenu_t *menu, int x, int y, int width, int height)\n{\n\tmenubox_t *n = Z_Malloc(sizeof(menubox_t));\n\tn->common.type = mt_box;\n\tn->common.iszone = true;\n\tn->common.posx = x;\n\tn->common.posy = y;\n\tn->width = width;\n\tn->height = height;\n\n\tn->common.next = menu->options;\n\tmenu->options = (menuoption_t *)n;\n\treturn n;\n}\n\nmenucustom_t *MC_AddCustom(emenu_t *menu, int x, int y, void *dptr, int dint, const char *tooltip)\n{\n\tmenucustom_t *n = Z_Malloc(sizeof(menucustom_t) + (tooltip?strlen(tooltip)+1:0));\n\tn->common.type = mt_custom;\n\tn->common.iszone = true;\n\tn->common.posx = x;\n\tn->common.posy = y;\n\tn->dptr = dptr;\n\tn->dint = dint;\n\tn->common.tooltip = tooltip?strcpy((char*)(n+1), tooltip):NULL;\n\n\tn->common.next = menu->options;\n\tmenu->options = (menuoption_t *)n;\n\treturn n;\n}\n\nmenucheck_t *MC_AddCheckBox(emenu_t *menu, int tx, int cx, int y, const char *text, cvar_t *var, int bits)\n{\n\tmenucheck_t *n = Z_Malloc(sizeof(menucheck_t)+strlen(text)+1);\n\tn->common.type = mt_checkbox;\n\tn->common.iszone = true;\n\tn->common.posx = tx;\n\tn->common.posy = y;\n\tn->common.height = 8;\n\tn->textwidth = cx - tx;\n\tn->common.width = cx-tx + 7*8;\n\tn->common.tooltip = var?var->description:NULL;\n\tn->text = (char *)(n+1);\n\tstrcpy((char *)(n+1), text);\n\tn->var = var;\n\tn->bits = bits;\n\n#ifdef _DEBUG\n\tif (var)\n\t{\n\t\tif (!(var->flags & CVAR_ARCHIVE))\n\t\t\tCon_Printf(\"Warning: %s is not set for archiving\\n\", var->name);\n\t\telse if (var->flags & (CVAR_RENDERERLATCH|CVAR_VIDEOLATCH))\n\t\t\tCon_Printf(\"Warning: %s requires a vid_restart\\n\", var->name);\n\t}\n#endif\n\n\tn->common.next = menu->options;\n\tmenu->options = (menuoption_t *)n;\n\treturn n;\n}\nmenuframe_t *MC_AddFrameStart(emenu_t *menu, int y)\n{\n\tmenuframe_t *n = Z_Malloc(sizeof(menuframe_t));\n\tn->common.type = mt_framestart;\n\tn->common.iszone = true;\n\tn->common.posx = 0;\n\tn->common.posy = y;\n\tn->common.height = 0;\n\tn->common.width = 0;\n\n\tn->common.next = menu->options;\n\tmenu->options = (menuoption_t *)n;\n\treturn n;\n}\nmenuframe_t *MC_AddFrameEnd(emenu_t *menu, int y)\n{\n\tmenuframe_t *n = Z_Malloc(sizeof(menuframe_t));\n\tn->common.type = mt_frameend;\n\tn->common.iszone = true;\n\tn->common.posx = 0;\n\tn->common.posy = y;\n\tn->common.height = 0;\n\tn->common.width = 0;\n\n\tn->common.next = menu->options;\n\tmenu->options = (menuoption_t *)n;\n\treturn n;\n}\nmenucheck_t *MC_AddCheckBoxFunc(emenu_t *menu, int tx, int cx, int y, const char *text, qboolean (*func) (menucheck_t *option, emenu_t *menu, chk_set_t set), int bits)\n{\n\tmenucheck_t *n = Z_Malloc(sizeof(menucheck_t)+strlen(text)+1);\n\tn->common.type = mt_checkbox;\n\tn->common.iszone = true;\n\tn->common.posx = tx;\n\tn->common.posy = y;\n\tn->common.height = 8;\n\tn->textwidth = cx - tx;\n\tn->common.width = cx-tx + 7*8;\n\tn->text = (char *)(n+1);\n\tstrcpy((char *)(n+1), text);\n\tn->func = func;\n\tn->bits = bits;\n\n\tn->common.next = menu->options;\n\tmenu->options = (menuoption_t *)n;\n\treturn n;\n}\n\n//delta may be 0\nmenuslider_t *MC_AddSlider(emenu_t *menu, int tx, int sx, int y, const char *text, cvar_t *var, float min, float max, float delta)\n{\n\tmenuslider_t *n = Z_Malloc(sizeof(menuslider_t)+strlen(text)+1);\n\tn->common.type = mt_slider;\n\tn->common.iszone = true;\n\tn->common.posx = tx;\n\tn->common.posy = y;\n\tn->common.height = 8;\n\tn->common.width = sx-tx + (SLIDER_RANGE+5)*8;\n\tn->common.tooltip = var->description;\n\tn->var = var;\n\tn->textwidth = sx-tx;\n\tn->text = (char *)(n+1);\n\tstrcpy((char *)(n+1), text);\n\n\tif (var)\n\t{\n\t\tn->current = var->value;\n\n#ifdef _DEBUG\n\t\tif (!(var->flags & CVAR_ARCHIVE))\n\t\t\tCon_Printf(\"Warning: %s is not set for archiving\\n\", var->name);\n#endif\n\t}\n\n\tn->min = min;\n\tn->max = max;\n\tn->smallchange = delta;\n\tn->largechange = delta*5;\n\n\tn->common.next = menu->options;\n\tmenu->options = (menuoption_t *)n;\n\n\treturn n;\n}\n\nmenucombo_t *MC_AddCombo(emenu_t *menu, int tx, int cx, int y, const char *caption, const char **ops, int initialvalue)\n{\n\tint numopts;\n\tint optlen;\n\tint maxoptlen;\n\tint optbufsize;\n\tmenucombo_t *n;\n\tchar **newops;\n\tchar *optbuf;\n\tconst char *o;\n\tint i;\n\n\tmaxoptlen = 0;\n\toptbufsize = sizeof(char*);\n\tnumopts = 0;\n\toptlen = 0;\n\tif (ops) while(ops[numopts])\n\t{\n\t\to = localtext(ops[numopts]);\n\n\t\toptlen = strlen(o);\n\t\tif (maxoptlen < optlen)\n\t\t\tmaxoptlen = optlen;\n\t\toptbufsize += optlen+1+sizeof(char*);\n\t\tnumopts++;\n\t}\n\n\n\tn = Z_Malloc(sizeof(*n) + optbufsize);\n\tnewops = (char **)(n+1);\n\toptbuf = (char*)(newops + numopts+1);\n\tn->common.type = mt_combo;\n\tn->common.iszone = true;\n\tn->common.posx = tx;\n\tn->common.posy = y;\n\tn->common.height = 8;\n\tn->common.width = cx-tx + maxoptlen*8;\n\tn->captionwidth = cx-tx;\n\tn->caption = caption;\n\tn->options = (const char **)newops;\n\n\tn->common.next = menu->options;\n\tmenu->options = (menuoption_t *)n;\n\n\tn->numoptions = numopts;\n\tfor (i = 0; i < numopts; i++)\n\t{\n\t\to = localtext(ops[i]);\n\n\t\tstrcpy(optbuf, o);\n\t\tnewops[i] = optbuf;\n\t\toptbuf += strlen(optbuf)+1;\n\t}\n\tnewops[i] = NULL;\n\n\tif (initialvalue && initialvalue >= n->numoptions)\n\t{\n\t\tCon_Printf(\"WARNING: Fixed initialvalue for %s\\n\", caption);\n\t\tinitialvalue = n->numoptions-1;\n\t}\n\tn->selectedoption = initialvalue;\n\n\treturn n;\n}\nmenucombo_t *MC_AddCvarCombo(emenu_t *menu, int tx, int cx, int y, const char *caption, cvar_t *cvar, const char **ops, const char **values)\n{\n\tint numopts;\n\tint optlen;\n\tint maxoptlen;\n\tint optbufsize;\n\tmenucombo_t *n;\n\tchar **newops;\n\tchar **newvalues;\n\tchar *optbuf;\n\tconst char *v, *o;\n\tint i;\n\n\tmaxoptlen = 0;\n\toptbufsize = sizeof(char*)*2 + strlen(caption)+1;\n\toptlen = 0;\n\tfor (i = 0; ops[i]; i++)\n\t{\n\t\tv = values?values[i]:va(\"%i\", i);\n\t\to = localtext(ops[i]);\n\n\t\toptlen = strlen(o);\n\t\tif (maxoptlen < optlen)\n\t\t\tmaxoptlen = optlen;\n\t\toptbufsize += optlen+1+sizeof(char*);\n\t\toptbufsize += strlen(v)+1+sizeof(char*);\n\t}\n\tnumopts = i;\n\n\n\n\tn = Z_Malloc(sizeof(*n) + optbufsize);\n\tnewops = (char **)(n+1);\n\tnewvalues = (char**)(newops + numopts+1);\n\toptbuf = (char*)(newvalues + numopts+1);\n\tn->common.type = mt_combo;\n\tn->common.iszone = true;\n\tn->common.posx = tx;\n\tn->common.posy = y;\n\tn->common.height = 8;\n\tn->common.width = cx-tx + maxoptlen*8;\n\tn->common.tooltip = localtext(cvar->description);\n\tn->captionwidth = cx-tx;\n\n\tstrcpy(optbuf, caption);\n\tn->caption = optbuf;\n\toptbuf += strlen(optbuf)+1;\n\n\tn->options = (char const*const*)newops;\n\tn->values = (char const*const*)newvalues;\n\tn->cvar = cvar;\n\n//\tif (!(cvar->flags & CVAR_ARCHIVE))\n//\t\tCon_Printf(\"Warning: %s is not set for archiving\\n\", cvar->name);\n\n\tn->selectedoption = 0;\n\n\tn->common.next = menu->options;\n\tmenu->options = (menuoption_t *)n;\n\n\tn->numoptions = numopts;\n\tfor (i = 0; i < numopts; i++)\n\t{\n\t\tv = values?values[i]:va(\"%i\", i);\n\t\to = localtext(ops[i]);\n\n\t\tif (!strcmp(v, cvar->string))\n\t\t\tn->selectedoption = i;\n\n\t\tstrcpy(optbuf, o);\n\t\tnewops[i] = optbuf;\n\t\toptbuf += strlen(optbuf)+1;\n\n\t\tstrcpy(optbuf, v);\n\t\tnewvalues[i] = optbuf;\n\t\toptbuf += strlen(optbuf)+1;\n\t}\n\tnewops[i] = NULL;\n\tnewvalues[i] = NULL;\n\n\treturn n;\n}\n\nmenubutton_t *MC_AddConsoleCommand(emenu_t *menu, int lhs, int rhs, int y, const char *text, const char *command)\n{\n\tmenubutton_t *n = Z_Malloc(sizeof(menubutton_t)+strlen(text)+1+strlen(command)+1);\n\tn->common.type = mt_button;\n\tn->common.iszone = true;\n\tn->common.posx = lhs;\n\tn->common.posy = y;\n\tn->common.height = 8;\n\tn->common.width = rhs?rhs - lhs:strlen(text)*8;\n\tn->rightalign = true;\n\tn->text = (char *)(n+1);\n\tstrcpy((char *)(n+1), text);\n\tn->command = n->text + strlen(n->text)+1;\n\tstrcpy((char *)n->command, command);\n\n\tn->common.next = menu->options;\n\tmenu->options = (menuoption_t *)n;\n\treturn n;\n}\n\nmenubutton_t *MC_AddConsoleCommandQBigFont(emenu_t *menu, int x, int y, const char *text, const char *command)\n{\n\tmenubutton_t *n = Z_Malloc(sizeof(menubutton_t)+strlen(text)+1+strlen(command)+1);\n\tn->common.type = mt_qbuttonbigfont;\n\tn->common.iszone = true;\n\tn->common.posx = x;\n\tn->common.posy = y;\n\tn->common.height = 20;\n\tn->common.width = strlen(text)*20;\n\tn->text = (char *)(n+1);\n\tstrcpy((char *)(n+1), text);\n\tn->command = n->text + strlen(n->text)+1;\n\tstrcpy((char *)n->command, command);\n\n\tn->common.next = menu->options;\n\tmenu->options = (menuoption_t *)n;\n\treturn n;\n}\n#ifdef HEXEN2\nmenubutton_t *MC_AddConsoleCommandHexen2BigFont(emenu_t *menu, int x, int y, const char *text, const char *command)\n{\n\tmenubutton_t *n = Z_Malloc(sizeof(menubutton_t)+strlen(text)+1+strlen(command)+1);\n\tn->common.type = mt_hexen2buttonbigfont;\n\tn->common.iszone = true;\n\tn->common.posx = x;\n\tn->common.posy = y;\n\tn->common.height = 20;\n\tn->common.width = strlen(text)*20;\n\tn->text = (char *)(n+1);\n\tstrcpy((char *)(n+1), text);\n\tn->command = n->text + strlen(n->text)+1;\n\tstrcpy((char *)n->command, command);\n\n\tn->common.next = menu->options;\n\tmenu->options = (menuoption_t *)n;\n\treturn n;\n}\n#endif\nmenubutton_t *MC_AddCommand(emenu_t *menu, int lhs, int rhs, int y, const char *text, qboolean (*command) (union menuoption_s *,struct emenu_s *,int))\n{\n\tmenubutton_t *n = Z_Malloc(sizeof(menubutton_t)+strlen(text)+1);\n\tn->common.type = mt_button;\n\tn->common.iszone = true;\n\tn->common.posx = lhs;\n\tn->common.posy = y;\n\tn->rightalign = true;\n\tn->text = strcpy((char*)(n+1), text);\n\tn->command = NULL;\n\tn->key = command;\n\tn->common.height = 8;\n\tn->common.width = rhs?rhs-lhs:strlen(text)*8;\n\n\tn->common.next = menu->options;\n\tmenu->options = (menuoption_t *)n;\n\treturn n;\n}\n\nmenubutton_t *VARGS MC_AddConsoleCommandf(emenu_t *menu, int lhs, int rhs, int y, int rightalign, const char *text, char *command, ...)\n{\n\tva_list\t\targptr;\n\tstatic char\t\tstring[1024];\n\tmenubutton_t *n;\n\n\tva_start (argptr, command);\n\tvsnprintf (string,sizeof(string)-1, command,argptr);\n\tva_end (argptr);\n\n\tn = Z_Malloc(sizeof(menubutton_t) + strlen(string)+1);\n\tn->common.type = mt_button;\n\tn->common.iszone = true;\n\tn->common.posx = lhs;\n\tn->common.posy = y;\n\tn->common.width = rhs-lhs;\n\tn->rightalign = rightalign;\n\tn->text = text;\n\tn->command = (char *)(n+1);\n\tstrcpy((char *)(n+1), string);\n\n\tn->common.next = menu->options;\n\tmenu->options = (menuoption_t *)n;\n\treturn n;\n}\n\nvoid MC_Slider_Key(menuslider_t *option, int key)\n{\n\tfloat range = option->current;\n\tfloat delta;\n\n\tfloat ix = option->vx;\n\tfloat ex = ix + 10*8;\n\n\tif (option->smallchange)\n\t\tdelta = option->smallchange;\n\telse\n\t\tdelta = 0.1;\n\n\tif (key == K_LEFTARROW || key == K_KP_LEFTARROW || key == K_GP_DPAD_LEFT || key == K_GP_LEFT_THUMB_LEFT || key == K_GP_DIAMOND_CONFIRM || key == K_MWHEELDOWN)\n\t{\n\t\trange -= delta;\n\t\tif (option->min > option->max)\n\t\t\trange = bound(option->max, range, option->min);\n\t\telse\n\t\t\trange = bound(option->min, range, option->max);\n\t\toption->current = range;\n\t}\n\telse if (key == K_RIGHTARROW || key == K_KP_RIGHTARROW || key == K_GP_DPAD_RIGHT || key == K_GP_LEFT_THUMB_RIGHT || key == K_GP_DIAMOND_ALTCONFIRM || key == K_MWHEELUP)\n\t{\n\t\trange += delta;\n\t\tif (option->min > option->max)\n\t\t\trange = bound(option->max, range, option->min);\n\t\telse\n\t\t\trange = bound(option->min, range, option->max);\n\t\toption->current = range;\n\t}\n\telse if ((key == K_TOUCHTAP || key == K_MOUSE1) && mousecursor_x >= ix-8 && mousecursor_x < ex+8)\n\t{\n\t\trange = (mousecursor_x - ix) / (ex - ix);\n\t\trange = option->min + range*(option->max-option->min);\n\t\tif (option->min > option->max)\n\t\t\trange = bound(option->max, range, option->min);\n\t\telse\n\t\t\trange = bound(option->min, range, option->max);\n\t\toption->current = range;\n\t}\n\telse if (key == K_ENTER || key == K_KP_ENTER || key == K_GP_START || key == K_MOUSE1 || key == K_TOUCHTAP)\n\t{\n\t\tif (range == option->max)\n\t\t\trange = option->min;\n\t\telse\n\t\t{\n\t\t\trange += delta;\n\t\t\tif (option->min > option->max)\n\t\t\t{\n\t\t\t\tif (range < option->max-delta/2)\n\t\t\t\t\trange = option->max;\n\t\t\t}\n\t\t\telse\n\t\t\t\tif (range > option->max-delta/2)\n\t\t\t\t\trange = option->max;\n\t\t}\n\t\toption->current = range;\n\t}\n\telse if (key == K_DEL || key == K_BACKSPACE)\n\t{\n\t\tif (option->var && option->var->defaultstr)\n\t\t\toption->current = atof(option->var->defaultstr);\n\t\telse\n\t\t\toption->current = (option->max-option->min)/2;\n\t}\n\telse\n\t\treturn;\n\n\tS_LocalSound (\"misc/menu2.wav\");\n\tif (option->var)\n\t\tCvar_SetValue(option->var, option->current);\n}\n\nvoid MC_CheckBox_Key(menucheck_t *option, emenu_t *menu, int key)\n{\n\tif (key == K_DEL && option->var && !option->func)\n\t\tCvar_Set(option->var, option->var->defaultstr);\n\tif (key != K_ENTER && key != K_KP_ENTER && key != K_GP_START && key != K_GP_DIAMOND_CONFIRM && key != K_GP_DIAMOND_ALTCONFIRM && key != K_LEFTARROW && key != K_KP_LEFTARROW && key != K_GP_DPAD_LEFT && key != K_GP_LEFT_THUMB_LEFT && key != K_RIGHTARROW && key != K_KP_LEFTARROW && key != K_GP_DPAD_RIGHT && key != K_GP_LEFT_THUMB_RIGHT && key != K_MWHEELUP && key != K_MWHEELDOWN && key != K_MOUSE1 && key != K_TOUCHTAP)\n\t\treturn;\n\tif (option->func)\n\t\toption->func(option, menu, CHK_TOGGLE);\n\telse if (!option->var)\n\t\toption->value = !option->value;\n\telse\n\t{\n\t\tif (option->bits)\n\t\t{\n\t\t\tint old;\n\t\t\tif (option->var->latched_string)\n\t\t\t\told = atoi(option->var->latched_string);\n\t\t\telse\n\t\t\t\told = option->var->value;\n\n\t\t\tif (old & option->bits)\n\t\t\t\tCvar_SetValue(option->var, old&~option->bits);\n\t\t\telse\n\t\t\t\tCvar_SetValue(option->var, old|option->bits);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (option->var->latched_string)\n\t\t\t\tCvar_SetValue(option->var, !atof(option->var->latched_string));\n\t\t\telse\n\t\t\t\tCvar_SetValue(option->var, !option->var->value);\n\t\t}\n\t}\n\tS_LocalSound (\"misc/menu2.wav\");\n}\n\nvoid MC_EditBox_Key(menuedit_t *edit, int key, unsigned int unicode)\n{\n\tint len = strlen(edit->text);\n\tif (key == K_DEL || key == K_BACKSPACE)\n\t{\n\t\tif (!len)\n\t\t\treturn;\n\t\tedit->text[len-1] = '\\0';\n\t}\n\telse if (!unicode)\n\t\treturn;\n\telse\n\t{\n\t\tif (unicode < 128)\n\t\t{\n\t\t\tif (len < sizeof(edit->text))\n\t\t\t{\n\t\t\t\tedit->text[len] = unicode;\n\t\t\t\tedit->text[len+1] = '\\0';\n\t\t\t}\n\t\t}\n\t}\n\n\tedit->modified = true;\n\n\tif (edit->cvar)\n\t{\n\t\tCvar_Set(edit->cvar, edit->text);\n\t\tS_LocalSound (\"misc/menu2.wav\");\n\t}\n}\n\nvoid MC_Combo_Key(menucombo_t *combo, int key)\n{\n\tif (key == K_DEL && combo->cvar)\n\t\tCvar_Set(combo->cvar, combo->cvar->defaultstr);\n\telse if (key == K_ENTER || key == K_KP_ENTER || key == K_GP_START || key == K_RIGHTARROW || key == K_KP_RIGHTARROW || key == K_GP_DPAD_RIGHT || key == K_GP_LEFT_THUMB_RIGHT ||key == K_GP_DIAMOND_ALTCONFIRM || key == K_MWHEELDOWN || key == K_MOUSE1 || key == K_TOUCHTAP)\n\t{\n\t\tcombo->selectedoption++;\n\t\tif (combo->selectedoption >= combo->numoptions)\n\t\t\tcombo->selectedoption = 0;\n\nchanged:\n\t\tif (combo->cvar && combo->numoptions)\n\t\t\tCvar_Set(combo->cvar, (char *)combo->values[combo->selectedoption]);\n\t\tS_LocalSound (\"misc/menu2.wav\");\n\t}\n\telse if (key == K_LEFTARROW || key == K_KP_LEFTARROW || key == K_GP_DPAD_LEFT || key == K_GP_LEFT_THUMB_LEFT || key == K_GP_DIAMOND_CONFIRM || key == K_MWHEELUP)\n\t{\n\t\tcombo->selectedoption--;\n\t\tif (combo->selectedoption < 0)\n\t\t\tcombo->selectedoption = combo->numoptions?combo->numoptions-1:0;\n\t\tgoto changed;\n\t}\n}\n\nstatic qboolean menu_mousedown;\nextern emenu_t *menu_script;\nstatic void M_Draw (menu_t *menu)\n{\n\temenu_t *m = (emenu_t*)menu;\n\tqboolean stillactive = false;\n\n\tif (!Key_Dest_Has(kdm_menu))\n\t{\n\t\tM_RemoveAllMenus(false);\n\t\tmenu_mousedown = false;\n\t\treturn;\n\t}\n\tif (!vrui.enabled && (!menu_script || scr_con_current))\n\t{\n\t\tif (m->nobacktint || (m->selecteditem && m->selecteditem->common.type == mt_slider && (m->selecteditem->slider.var == &v_gamma || m->selecteditem->slider.var == &v_contrast)))\n\t\t\t/*no menu tint if we're trying to adjust gamma*/;\n\t\telse\n\t\t\tR2D_FadeScreen ();\n\t}\n\n\tR2D_ImageColours(1, 1, 1, 1);\n\n\tif (m)\n\t{\n\t\tM_CheckMouseMove(m);\n\n\t\tMenuDraw(m);\n\t\tstillactive = true;\n\t}\n\n\tif (!stillactive)\n\t\tKey_Dest_Remove(kdm_menu);\n}\n\nstatic qboolean M_KeyEvent(menu_t *m, qboolean isdown, unsigned int devid, int key, int unicode)\n{\n\temenu_t *menu = (emenu_t*)m;\n\tif (isdown)\n\t{\n\t\tif (key == K_MOUSE1 || key == K_TOUCHTAP)\t//mouse clicks are deferred until the release event. this is for touch screens and aiming.\n\t\t{\n\n\t\t\tif (menu->mouseitem && menu->selecteditem != menu->mouseitem)\n\t\t\t{\n\t\t\t\tmenu->selecteditem = menu->mouseitem;\n#ifdef HEXEN2\n\t\t\t\tif (M_GameType() == MGT_HEXEN2)\n\t\t\t\t\tS_LocalSound (\"raven/menu1.wav\");\n\t\t\t\telse\n#endif\n\t\t\t\t\tS_LocalSound (\"misc/menu1.wav\");\n\n\t\t\t\tif (menu->cursoritem)\n\t\t\t\t\tmenu->cursoritem->common.posy = menu->selecteditem->common.posy + (menu->selecteditem->common.height-menu->cursoritem->common.height)/2;\n\t\t\t}\n\n\t\t\tif (menu->mouseitem && menu->mouseitem->common.type == mt_frameend)\n\t\t\t\tmenu->mouseitem->frame.mousedown = true;\n\t\t\telse\n\t\t\t\tmenu_mousedown = true;\n\t\t}\n\t\telse if (key == K_LSHIFT || key == K_RSHIFT || key == K_LALT || key == K_RALT || key == K_LCTRL || key == K_RCTRL)\n\t\t\t;\t//modifiers are sent on up events instead.\n\t\telse\n\t\t\tM_Complex_Key (menu, key, unicode);\n\t\treturn true; //eat all keys...\n\t}\n\telse\n\t{\n\t\tif ((key == K_MOUSE1 || key == K_TOUCHTAP) && menu_mousedown)\n\t\t\tM_Complex_Key (menu, key, unicode);\n\t\telse if (key == K_LSHIFT || key == K_RSHIFT || key == K_LALT || key == K_RALT || key == K_LCTRL || key == K_RCTRL)\n\t\t\tM_Complex_Key (menu, key, unicode);\n\t\tmenu_mousedown = false;\n\t\treturn false;\n\t}\n}\n\nvoid M_Release (menu_t *m, qboolean forced)\n{\n\temenu_t *menu = (emenu_t*)m;\n\tmenuoption_t *op, *oop;\n\tif (menu->reselection)\n\t{\n\t\tmenu->reselection->x = menu->selecteditem->common.posx;\n\t\tmenu->reselection->y = menu->selecteditem->common.posy;\n\t}\n\n\tif (menu->remove)\n\t\tmenu->remove(menu);\n\n\top = menu->options;\n\twhile(op)\n\t{\n\t\toop = op;\n\t\top = op->common.next;\n\t\tif (oop->common.iszone)\n\t\t\tZ_Free(oop);\n\t}\n\tmenu->options=NULL;\n\n\tif (menu->tooltip)\n\t{\n\t\tZ_Free(menu->tooltip);\n\t\tmenu->tooltip = NULL;\n\t}\n\n\tif (menu->iszone)\n\t{\n\t\tmenu->iszone=false;\n\t\tZ_Free(menu);\n\t}\n}\n\nemenu_t *M_CreateMenu (int extrasize)\n{\n\temenu_t *menu;\n\tmenu = Z_Malloc(sizeof(emenu_t)+extrasize);\n\tmenu->iszone=true;\n\tmenu->data = menu+1;\n\n\tmenu->menu.cursor = &key_customcursor[kc_console];\n\t/*void (*videoreset)\t(struct menu_s *);\t//called after a video mode switch / shader reload.\n\tvoid (*release)\t\t(struct menu_s *);\t//\n\tqboolean (*keyevent)(struct menu_s *, qboolean isdown, unsigned int devid, int key, int unicode);\t//true if key was handled\n\tqboolean (*mousemove)(struct menu_s *, qboolean abs, unsigned int devid, float x, float y);\n\tqboolean (*joyaxis)\t(struct menu_s *, unsigned  int devid, int axis, float val);\n\tvoid (*drawmenu)\t(struct menu_s *);\n\t*/\n\tmenu->menu.drawmenu = M_Draw;\n\tmenu->menu.keyevent = M_KeyEvent;\n\tmenu->menu.release = M_Release;\n\tMenu_Push(&menu->menu, false);\n\n\treturn menu;\n}\nvoid M_RemoveMenu (emenu_t *menu)\n{\n\tMenu_Unlink((menu_t*)menu, false);\n}\n\nvoid M_ReloadMenus(void)\n{\n\tmenu_t *m;\n\n\tfor (m = topmenu; m; m = m->prev)\n\t{\n\t\tif (m->videoreset)\n\t\t\tm->videoreset(m);\n\t}\n}\n\nvoid M_RemoveAllMenus (qboolean leaveprompts)\n{\t//certain menuqc mods are evil and force themselves open again each time we ask them to close, which means we get into an infinite loop trying to ask them to kindly fuck off.\n\t//so only kill the current ones.\n\tmenu_t **list, *m;\n\tint count = 0;\n\tfor (m = topmenu; m; m = m->prev)\n\t\tcount++;\n\tlist = BZ_Malloc(count * sizeof(list));\n\n\tfor (count = 0, m = topmenu; m; m = m->prev)\n\t{\n\t\tif (m->persist && leaveprompts)\n\t\t\tcontinue;\n\t\tlist[count++] = m;\n\t}\n\twhile(count --> 0)\n\t\tMenu_Unlink(list[count], true);\n\tBZ_Free(list);\n}\nvoid M_MenuPop_f (void)\n{\n\tif (!topmenu)\n\t\treturn;\n\tMenu_Unlink(topmenu, false);\n}\n\nmenubutton_t *M_FindButton(emenu_t *menu, const char *command)\n{\n\tmenuoption_t *o;\n\tfor (o = menu->options; o; o = o->common.next)\n\t{\n\t\tif (( o->common.type == mt_button\n\t\t\t||o->common.type == mt_qbuttonbigfont\n\t\t\t||o->common.type == mt_hexen2buttonbigfont)\n\t\t\t&& !strcmp(o->button.command, command))\n\t\t\treturn (menubutton_t*)o;\n\t}\n\treturn NULL;\n}\nstatic menuoption_t *M_NextItem(emenu_t *m, menuoption_t *old)\n{\n\tmenuoption_t *op = m->options;\n\twhile(op->common.next)\n\t{\n\t\tif (op->common.next == old)\n\t\t\treturn op;\n\n\t\top = op->common.next;\n\t}\n\treturn op;\n}\nmenuoption_t *M_NextSelectableItem(emenu_t *m, menuoption_t *old, qboolean wrap)\n{\n\tmenuoption_t *op;\n\n\tif (!m->options)\n\t\treturn NULL;\t//erk!\n\n\tif (!old)\n\t\told = M_NextItem(m, old);\n\n\top = old;\n\n\twhile (1)\n\t{\n\t\tif (!op)\n\t\t\top = m->options;\n\n\t\tif (op == m->options && !wrap)\n\t\t\treturn NULL;\n\t\top = M_NextItem(m, op);\n\t\tif (!op)\n\t\t\top = m->options;\n\n\t\tif (op == old)\n\t\t{\n\t\t\tif (op->common.type == mt_slider || op->common.type == mt_checkbox || op->common.type == mt_button || op->common.type == mt_hexen2buttonbigfont || op->common.type == mt_qbuttonbigfont || op->common.type == mt_edit || op->common.type == mt_combo || op->common.type == mt_bind || (op->common.type == mt_custom && op->custom.key))\n\t\t\t\treturn op;\n\t\t\treturn NULL;\t//whoops.\n\t\t}\n\n\t\tif (op->common.type == mt_slider || op->common.type == mt_checkbox || op->common.type == mt_button || op->common.type == mt_hexen2buttonbigfont || op->common.type == mt_qbuttonbigfont || op->common.type == mt_edit || op->common.type == mt_combo || op->common.type == mt_bind || (op->common.type == mt_custom && op->custom.key))\n\t\t\tif (!op->common.ishidden)\n\t\t\t\treturn op;\n\t}\n}\n\nmenuoption_t *M_PrevSelectableItem(emenu_t *m, menuoption_t *old, qboolean wrap)\n{\n\tmenuoption_t *op;\n\n\tif (!m->options)\n\t\treturn NULL;\t//erk!\n\n\tif (!old)\n\t\told = m->options;\n\n\top = old;\n\n\twhile (1)\n\t{\n\t\tif (!op)\n\t\t\top = m->options;\n\n\t\top = op->common.next;\n\t\tif (!op)\n\t\t{\n\t\t\tif (!wrap)\n\t\t\t\treturn NULL;\n\t\t\top = m->options;\n\t\t}\n\n\t\tif (op == old)\n\t\t\treturn old;\t//whoops.\n\n\t\tif (op->common.type == mt_slider || op->common.type == mt_checkbox || op->common.type == mt_button || op->common.type == mt_hexen2buttonbigfont || op->common.type == mt_qbuttonbigfont || op->common.type == mt_edit || op->common.type == mt_combo || op->common.type == mt_bind || (op->common.type == mt_custom && op->custom.key))\n\t\t\tif (!op->common.ishidden)\n\t\t\t\treturn op;\n\t}\n}\n\nvoid M_Complex_Key(emenu_t *currentmenu, int key, int unicode)\n{\n\tmenuoption_t *mi;\n\tif (!currentmenu)\n\t\treturn;\t//erm...\n\n\tM_CheckMouseMove(currentmenu);\n\n\tif (currentmenu->key)\n\t\tif (currentmenu->key(currentmenu, key, unicode))\n\t\t\treturn;\n\n\tif (currentmenu->selecteditem && currentmenu->selecteditem->common.type == mt_custom && (key == K_DOWNARROW || key == K_KP_DOWNARROW || key == K_GP_DPAD_DOWN || key == K_GP_LEFT_THUMB_DOWN || key == K_GP_DIAMOND_CONFIRM || key == K_GP_DIAMOND_ALTCONFIRM || key == K_UPARROW || key == K_KP_UPARROW || key == K_GP_DPAD_UP || key == K_GP_LEFT_THUMB_UP || key == K_TAB || key == K_MWHEELUP || key == K_MWHEELDOWN || key == K_PGUP || key == K_PGDN))\n\t\tif (currentmenu->selecteditem->custom.key)\n\t\t\tif (currentmenu->selecteditem->custom.key(&currentmenu->selecteditem->custom, currentmenu, key, unicode))\n\t\t\t\treturn;\n\n\tif (currentmenu->selecteditem && currentmenu->selecteditem->common.type == mt_bind)\n\t{\n\t\tif (bindingactive)\n\t\t{\n\t\t\t//don't let key 0 be bound here. unicode-only keys are also not bindable.\n\t\t\tif (key == 0)\n\t\t\t\treturn;\n\n#ifdef HEXEN2\n\t\t\tif (M_GameType() == MGT_HEXEN2)\n\t\t\t\tS_LocalSound (\"raven/menu1.wav\");\n\t\t\telse\n#endif\n\t\t\t\tS_LocalSound (\"misc/menu1.wav\");\n\n\t\t\tif (key != K_ESCAPE && key != '`')\n\t\t\t{\n\t\t\t\tint modifiers = 0;\n\t\t\t\tif (keydown[K_LSHIFT] && key != K_LSHIFT)\n\t\t\t\t\tmodifiers |= 1;\n\t\t\t\tif (keydown[K_RSHIFT] && key != K_RSHIFT)\n\t\t\t\t\tmodifiers |= 1;\n\t\t\t\tif (keydown[K_LALT] && key != K_LALT)\n\t\t\t\t\tmodifiers |= 2;\n\t\t\t\tif (keydown[K_RALT] && key != K_RALT)\n\t\t\t\t\tmodifiers |= 2;\n\t\t\t\tif (keydown[K_LCTRL] && key != K_LCTRL)\n\t\t\t\t\tmodifiers |= 4;\n\t\t\t\tif (keydown[K_RCTRL] && key != K_RCTRL)\n\t\t\t\t\tmodifiers |= 4;\n\n\t\t\t\tCbuf_InsertText (va(\"bind \\\"%s\\\" \\\"%s\\\"\\n\", Key_KeynumToString (key, modifiers), currentmenu->selecteditem->bind.command), RESTRICT_LOCAL, false);\n\t\t\t}\n\t\t\tbindingactive = false;\n\t\t\treturn;\n\t\t}\n\t}\n\n\tswitch(key)\n\t{\n\tcase K_MOUSE2:\t//right\n\tcase K_MOUSE4:\t//back\n\tcase K_ESCAPE:\n\tcase K_TOUCHLONG:\n\tcase K_GP_BACK:\n\tcase K_GP_START:\n\tcase K_GP_DIAMOND_CANCEL:\n\t\t//remove\n\t\tM_RemoveMenu(currentmenu);\n#ifdef HEXEN2\n\t\tif (M_GameType() == MGT_HEXEN2)\n\t\t\tS_LocalSound (\"raven/menu3.wav\");\n\t\telse\n#endif\n\t\t\tS_LocalSound (\"misc/menu3.wav\");\n\t\tbreak;\n\tcase K_TAB:\n\tcase K_DOWNARROW:\n\tcase K_KP_DOWNARROW:\n\tcase K_GP_DPAD_DOWN:\n\tcase K_GP_LEFT_THUMB_DOWN:\n\tgodown:\n\t\tcurrentmenu->selecteditem = M_NextSelectableItem(currentmenu, currentmenu->selecteditem, true);\n\t\tgoto gone;\n\n\tcase K_UPARROW:\n\tcase K_KP_UPARROW:\n\tcase K_GP_DPAD_UP:\n\tcase K_GP_LEFT_THUMB_UP:\n\tgoup:\n\t\tcurrentmenu->selecteditem = M_PrevSelectableItem(currentmenu, currentmenu->selecteditem, true);\n\t\tgoto gone;\n\n\tcase K_PGDN:\n\t\tfor (key = 0; key < 10; key++)\n\t\t{\n\t\t\tmenuoption_t *op = M_NextSelectableItem(currentmenu, currentmenu->selecteditem, false);\n\t\t\tif (op)\n\t\t\t\tcurrentmenu->selecteditem = op;\n\t\t\telse\n\t\t\t\tbreak;\n\t\t}\n\t\tgoto gone;\n\tcase K_PGUP:\n\t\tfor (key = 0; key < 10; key++)\n\t\t{\n\t\t\tmenuoption_t *op = M_PrevSelectableItem(currentmenu, currentmenu->selecteditem, false);\n\t\t\tif (op)\n\t\t\t\tcurrentmenu->selecteditem = op;\n\t\t\telse\n\t\t\t\tbreak;\n\t\t}\n\t\tgoto gone;\n\n\tgone:\n\t\tif (currentmenu->selecteditem)\n\t\t{\n#ifdef HEXEN2\n\t\t\tif (M_GameType() == MGT_HEXEN2)\n\t\t\t\tS_LocalSound (\"raven/menu1.wav\");\n\t\t\telse\n#endif\n\t\t\t\tS_LocalSound (\"misc/menu1.wav\");\n\n\t\t\tif (currentmenu->cursoritem)\n\t\t\t\tcurrentmenu->cursoritem->common.posy = currentmenu->selecteditem->common.posy + (currentmenu->selecteditem->common.height-currentmenu->cursoritem->common.height)/2;\n\t\t}\n\t\tbreak;\n\n\tcase K_MWHEELUP:\n\tcase K_MWHEELDOWN:\n\t\tmi = currentmenu->mouseitem;\n\t\tif (!mi)\n\t\t\tmi = currentmenu->selecteditem;\n\t\tif (mi)\n\t\t{\n\t\t\tqboolean handled = false;\n\t\t\tswitch(mi->common.type)\n\t\t\t{\n\t\t\tcase mt_combo:\n\t\t\t\tif (mousecursor_x >= currentmenu->xpos + mi->common.posx + mi->combo.captionwidth + 3*8)\n\t\t\t\t{\n\t\t\t\t\tMC_Combo_Key(&mi->combo, key);\n\t\t\t\t\thandled = true;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase mt_checkbox:\n\t\t\t\tif (mousecursor_x >= currentmenu->xpos + mi->common.posx + mi->check.textwidth + 3*8)\n\t\t\t\t{\n\t\t\t\t\tMC_CheckBox_Key(&mi->check, currentmenu, key);\n\t\t\t\t\thandled = true;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase mt_custom:\n\t\t\t\tif (mi->custom.key)\n\t\t\t\t\thandled = mi->custom.key(&mi->custom, currentmenu, key, unicode);\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tif (handled)\n\t\t\t{\n\t\t\t\tcurrentmenu->selecteditem = mi;\n\t\t\t\tif (currentmenu->cursoritem)\n\t\t\t\t\tcurrentmenu->cursoritem->common.posy = currentmenu->selecteditem->common.posy + (currentmenu->selecteditem->common.height-currentmenu->cursoritem->common.height)/2;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\telse if (key == K_MWHEELUP)\n\t\t\t\tgoto goup;\n\t\t\telse goto godown;\n\t\t}\n\tcase K_TOUCHTAP:\n\tcase K_MOUSE1:\n\tcase K_MOUSE3:\n\tcase K_MOUSE5:\n\tcase K_MOUSE6:\n\tcase K_MOUSE7:\n\tcase K_MOUSE8:\n\tcase K_MOUSE9:\n\tcase K_MOUSE10:\n\t\tif (!currentmenu->mouseitem)\n\t\t\tbreak;\n\t\tif (currentmenu->mouseitem && currentmenu->selecteditem != currentmenu->mouseitem)\n\t\t{\n/*\t\t\tcurrentmenu->selecteditem = currentmenu->mouseitem;\n#ifdef HEXEN2\n\t\t\tif (M_GameType() == MGT_HEXEN2)\n\t\t\t\tS_LocalSound (\"raven/menu1.wav\");\n\t\t\telse\n#endif\n\t\t\t\tS_LocalSound (\"misc/menu1.wav\");\n\n\t\t\tif (currentmenu->cursoritem)\n\t\t\t\tcurrentmenu->cursoritem->common.posy = currentmenu->selecteditem->common.posy + (currentmenu->selecteditem->common.height-currentmenu->cursoritem->common.height)/2;\n*/\t\t\tbreak;\t//require a double-click when selecting... too easy to miss, and a touble-tap at least makes it easier to clarify what you meant.\n\t\t}\n\t\t//fall through\n\tdefault:\n\t\tif (!currentmenu->selecteditem)\n\t\t{\n\t\t\tif (!currentmenu->options)\n\t\t\t\treturn;\n\t\t\tcurrentmenu->selecteditem = currentmenu->options;\n\t\t}\n\t\tswitch(currentmenu->selecteditem->common.type)\n\t\t{\n\t\tcase mt_slider:\n\t\t\tMC_Slider_Key(&currentmenu->selecteditem->slider, key);\n\t\t\tbreak;\n\t\tcase mt_checkbox:\n\t\t\tMC_CheckBox_Key(&currentmenu->selecteditem->check, currentmenu, key);\n\t\t\tbreak;\n\t\tcase mt_button:\n\t\tcase mt_hexen2buttonbigfont:\n\t\tcase mt_qbuttonbigfont:\n\t\t\tif (!currentmenu->selecteditem->button.command)\n\t\t\t\tcurrentmenu->selecteditem->button.key(currentmenu->selecteditem, currentmenu, key);\n\t\t\telse if (key == K_ENTER || key == K_KP_ENTER || key == K_GP_DIAMOND_CONFIRM || key == K_GP_DIAMOND_ALTCONFIRM || key == K_MOUSE1 || key == K_TOUCHTAP)\n\t\t\t{\n\t\t\t\tCbuf_AddText(currentmenu->selecteditem->button.command, RESTRICT_LOCAL);\n#ifdef HEXEN2\n\t\t\t\tif (M_GameType() == MGT_HEXEN2)\n\t\t\t\t\tS_LocalSound (\"raven/menu2.wav\");\n\t\t\t\telse\n#endif\n\t\t\t\t\tS_LocalSound (\"misc/menu2.wav\");\n\t\t\t}\n\t\t\tbreak;\n\t\tcase mt_custom:\n\t\t\tif (currentmenu->selecteditem->custom.key)\n\t\t\t\tcurrentmenu->selecteditem->custom.key(&currentmenu->selecteditem->custom, currentmenu, key, unicode);\n\t\t\tbreak;\n\t\tcase mt_edit:\n\t\t\tMC_EditBox_Key(&currentmenu->selecteditem->edit, key, unicode);\n\t\t\tbreak;\n\t\tcase mt_combo:\n\t\t\tMC_Combo_Key(&currentmenu->selecteditem->combo, key);\n\t\t\tbreak;\n\t\tcase mt_bind:\n\t\t\tif (key == K_ENTER || key == K_KP_ENTER || key == K_GP_DIAMOND_CONFIRM || key == K_MOUSE1 || key == K_TOUCHTAP)\n\t\t\t\tbindingactive = true;\n\t\t\telse if (key == K_BACKSPACE || key == K_DEL || key == K_GP_DIAMOND_ALTCONFIRM)\n\t\t\t\tM_UnbindCommand (currentmenu->selecteditem->bind.command);\n\t\tdefault:\n\t\t\tbreak;\n\t\t}\n\t\tbreak;\n\t}\n}\n\n\n\n\nqboolean MC_Main_Key (emenu_t *menu, int key, unsigned int unicode)\t//here purly to restart demos.\n{\n\tif (key == K_ESCAPE || key == K_GP_BACK || key == K_GP_DIAMOND_CANCEL || key == K_MOUSE2)\n\t{\n\t\textern cvar_t con_stayhidden;\n\n\t\t//don't spam menu open+close events if we're not going to be allowing the console to appear\n\t\tif (con_stayhidden.ival && cls.state == ca_disconnected)\n\t\t\tif (!CL_TryingToConnect())\n\t\t\t{\n\t\t\t\textern cvar_t cl_demoreel;\n\t\t\t\tif (cl_demoreel.ival)\n\t\t\t\t{\t//start a demo instead. this should probably be on a timer...\n\t\t\t\t\tcls.demonum = MAX_DEMOS;\n\t\t\t\t\tCL_NextDemo();\n\t\t\t\t\tif (cls.state)\n\t\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\treturn true;\n\t\t\t}\n\t}\n\treturn false;\n}\n\nstatic int M_Main_AddExtraOptions(emenu_t *mainm, int y)\n{\n\tif (Cmd_AliasExist(\"mod_menu\", RESTRICT_LOCAL))\n\t\t{MC_AddConsoleCommandQBigFont(mainm, 72, y,\tva(\"%-14s\", Cvar_Get(\"mod_menu\", \"Mod Menu\", 0, NULL)->string), \"mod_menu\\n\");\t\t\ty += 20;}\n\tif (Cmd_Exists(\"xmpp\"))\n\t\t{MC_AddConsoleCommandQBigFont(mainm, 72, y,\tlocaltext(\"Social        \"), \"xmpp\\n\");\t\t\ty += 20;}\n\tif (Cmd_Exists(\"irc\"))\n\t\t{MC_AddConsoleCommandQBigFont(mainm, 72, y,\tlocaltext(\"IRC           \"), \"irc\\n\");\t\t\t\ty += 20;}\n\tif (Cmd_Exists(\"qi\"))\n\t\t{MC_AddConsoleCommandQBigFont(mainm, 72, y,\tlocaltext(\"Quake Injector\"), \"qi\\n\");\t\t\t\ty += 20;}\n\telse if (PM_CanInstall(\"qi\"))\n\t\t{MC_AddConsoleCommandQBigFont(mainm, 72, y,\tlocaltext(\"Get Quake Injector\"), \"pkg reset; pkg add qi; pkg apply\\n\");\ty += 20;}\n\tif (Cmd_Exists(\"menu_download\"))\n\t{\n#ifdef WEBCLIENT\n\t\tMC_AddConsoleCommandQBigFont(mainm, 72, y,\tlocaltext(\"^bUpdates       \"), \"menu_download\\n\");\ty += 20;\n#else\n\t\tMC_AddConsoleCommandQBigFont(mainm, 72, y,\tlocaltext(\"^bPackages      \"), \"menu_download\\n\");\ty += 20;\n#endif\n\t}\n\tif (Cmd_Exists(\"menu_mods\"))\n\t\t{MC_AddConsoleCommandQBigFont(mainm, 72, y,\tlocaltext(\"Mods          \"), \"menu_mods\\n\");\ty += 20;}\n\n\tif (Cmd_Exists(\"sys_openfile\"))\n\t\t{MC_AddConsoleCommandQBigFont(mainm, 72, y,\tlocaltext(\"Open File     \"), \"sys_openfile\\n\");\ty += 20;}\n\n#ifdef FTE_TARGET_WEB\n\tif (Cmd_Exists(\"xr_toggle\"))\n\t\t{MC_AddConsoleCommandQBigFont(mainm, 72, y,\tlocaltext(\"Toggle WebXR  \"), \"xr_toggle\\n\");\ty += 20;}\n#endif\n\n\treturn y;\n}\n\nvoid MC_Main_Predraw(emenu_t *menu)\n{\n\textern cvar_t m_preset_chosen;\n\tmenubutton_t *b;\n\n\tb = M_FindButton(menu, \"menu_options\\n\");\n\tif (b && b->text[0] && b->text[1])\n\t{\n\t\tqboolean flash =\n#ifdef PACKAGEMANAGER\n\t\t\tPM_AreSourcesNew(false)||\n#endif\n\t\t\t!m_preset_chosen.ival;\n\t\tb->text = (char*)(b+1);\n\t\tif (b->text[0] == '^' && b->text[1] == 'b' && !flash)\n\t\t\tb->text += 2;\n\t}\n\n#ifdef PACKAGEMANAGER\n\tb = M_FindButton(menu, \"menu_download\\n\");\n\tif (b && b->text[0] && b->text[1])\n\t{\n\t\tqboolean flash = PM_AreSourcesNew(false);\n\t\tb->text = (char*)(b+1);\n\t\tif (b->text[0] == '^' && b->text[1] == 'b' && !flash)\n\t\t\tb->text += 2;\n\t}\n#endif\n}\n\nvoid M_Menu_Main_f (void)\n{\n\textern cvar_t m_helpismedia;\n\tmenubutton_t *b;\n\temenu_t *mainm = NULL;\n\tmpic_t *p;\n\tstatic menuresel_t resel;\n\tint y;\n\n#ifndef SERVERONLY\n\tif (isDedicated || !Renderer_Started())\n\t\treturn;\n#endif\n\n#ifdef CSQC_DAT\n\tif (CSQC_ConsoleCommand(-1, va(\"%s %s\", Cmd_Argv(0), Cmd_Args())))\n\t\treturn;\n#endif\n\n/*\tif (cls.demoplayback)\n\t{\n\t\tm_save_demonum = cls.demonum;\n\t\tcls.demonum = -1;\n\t}\n*/\n\tSCR_EndLoadingPlaque();\t//just in case...\n\n\tif (!FS_GameIsInitialised())\n\t{\t//if you canceled the mods menu, quit instead.\n\t\tif (!Key_Dest_Has(kdm_prompt) && !Key_Dest_Has(kdm_menu))\n\t\t{\n\t\t\tM_Menu_Mods_f();\t//bring back the mods menu... THERE'S NO ESCAPE!!! (no basedir, so options etc is ponitless)\n\t\t\tM_Menu_Quit_f();\t//and a quit prompt, cos they probably hit escape or something.\n\t\t}\n\t\treturn;\n\t}\n\n/*\n\tif (0)\n\t{\n\t\tint x, i;\n\t\tguiinfo_t *gui;\n\t\tm_state = m_complex;\n\t\tkey_dest = key_menu;\n\t\tm_entersound = true;\n\n\t\tmainm = M_CreateMenu(sizeof(guiinfo_t));\n\t\tmainm->key = MC_GuiKey;\n\t\tmainm->xpos=0;\n\t\tgui = (guiinfo_t *)mainm->data;\n\t\tgui->text[0] = \"Single\";\n\t\tgui->text[1] = \"Multiplayer\";\n\t\tgui->text[2] = \"Quit\";\n\t\tfor (x = 0, i = 0; gui->text[i]; i++)\n\t\t{\n\t\t\tgui->op[i] = MC_AddRedText(mainm, x, 0, gui->text[i], false);\n\t\t\tx+=(strlen(gui->text[i])+1)*8;\n\t\t}\n\t\treturn;\n\t}\n*/\n\n\tS_LocalSound (\"misc/menu2.wav\");\n\n#ifdef Q2CLIENT\n\tif (M_GameType() == MGT_QUAKE2)\t//quake2 main menu.\n\t{\n\t\tif (R_GetShaderSizes(R2D_SafeCachePic(\"pics/m_main_quit\"), NULL, NULL, true) > 0)\n\t\t{\n\t\t\tint itemheight = 32;\n\n\t\t\tmainm = M_CreateMenu(0);\n\t\t\tmainm->key = MC_Main_Key;\n\n\t\t\tMC_AddPicture(mainm, 0, 4, 38, 166, \"pics/m_main_plaque\");\n\t\t\tMC_AddPicture(mainm, 0, 173, 36, 42, \"pics/m_main_logo\");\n#if defined(HAVE_SERVER) && defined(Q2SERVER)\n\t\t\tMC_AddSelectablePicture(mainm, 68, 13, itemheight, \"pics/m_main_game\");\n#endif\n\t\t\tMC_AddSelectablePicture(mainm, 68, 53, itemheight, \"pics/m_main_multiplayer\");\n\t\t\tMC_AddSelectablePicture(mainm, 68, 93, itemheight, \"pics/m_main_options\");\n\t\t\tMC_AddSelectablePicture(mainm, 68, 133, itemheight, \"pics/m_main_video\");\n\t\t\tMC_AddSelectablePicture(mainm, 68, 173, itemheight, \"pics/m_main_quit\");\n\n#if defined(HAVE_SERVER) && defined(Q2SERVER)\n\t\t\tb = MC_AddConsoleCommand\t(mainm, 68, 320, 13,\t\"\", \"menu_single\\n\");\n\t\t\tb->common.tooltip = localtext(\"Singleplayer.\");\n\t\t\tmainm->selecteditem = (menuoption_t *)b;\n\t\t\tb->common.width = 12*20;\n\t\t\tb->common.height = itemheight;\n#endif\n\t\t\tb = MC_AddConsoleCommand\t(mainm, 68, 320, 53,\t\"\", \"menu_multi\\n\");\n\t\t\tb->common.tooltip = localtext(\"Multiplayer.\");\n\t\t\tif (!mainm->selecteditem)\n\t\t\t\tmainm->selecteditem = (menuoption_t *)b;\n\t\t\tb->common.width = 12*20;\n\t\t\tb->common.height = itemheight;\n\t\t\tb = MC_AddConsoleCommand\t(mainm, 68, 320, 93,\t\"\", \"menu_options\\n\");\n\t\t\tb->common.tooltip = localtext(\"Options.\");\n\t\t\tb->common.width = 12*20;\n\t\t\tb->common.height = itemheight;\n\t\t\tb = MC_AddConsoleCommand\t(mainm, 68, 320, 133,\t\"\", \"menu_video\\n\");\n\t\t\tb->common.tooltip = localtext(\"Video Options.\");\n\t\t\tb->common.width = 12*20;\n\t\t\tb->common.height = itemheight;\n\t\t\tb = MC_AddConsoleCommand\t(mainm, 68, 320, 173,\t\"\", \"menu_quit\\n\");\n\t\t\tb->common.tooltip = localtext(\"Quit to DOS.\");\n\t\t\tb->common.width = 12*20;\n\t\t\tb->common.height = itemheight;\n\n\t\t\tmainm->cursoritem = (menuoption_t *)MC_AddCursor(mainm, &resel, 42, mainm->selecteditem->common.posy);\n\t\t}\n\t}\n\telse\n#endif\n#ifdef HEXEN2\n\t\tif (M_GameType() == MGT_HEXEN2)\n\t{\n\t\tp = R2D_SafeCachePic(\"gfx/menu/title0.lmp\");\n\t\tif (R_GetShaderSizes(p, NULL, NULL, true) > 0)\n\t\t{\n\t\t\tint y = 64;\n\t\t\tmainm = M_CreateMenu(0);\n\t\t\tmainm->key = MC_Main_Key;\n\n\t\t\tMC_AddPicture(mainm, 16, 0, 35, 176, \"gfx/menu/hplaque.lmp\");\n\t\t\tMC_AddCenterPicture(mainm, 0, 60, \"gfx/menu/title0.lmp\");\n\n#ifndef CLIENTONLY\n\t\t\tb=MC_AddConsoleCommandHexen2BigFont\t(mainm, 80, y,\tlocaltext(\"Single Player\"), \"menu_single\\n\");\n\t\t\tmainm->selecteditem = (menuoption_t *)b;\n\t\t\tb->common.width = 12*20;\n\t\t\tb->common.height = 20;\n\t\t\ty += 20;\n#endif\n\t\t\tb=MC_AddConsoleCommandHexen2BigFont\t(mainm, 80, y,\tlocaltext(\"MultiPlayer\"), \"menu_multi\\n\");\n#ifdef CLIENTONLY\n\t\t\tmainm->selecteditem = (menuoption_t *)b;\n#endif\n\t\t\tb->common.width = 12*20;\n\t\t\tb->common.height = 20;\n\t\t\ty += 20;\n\t\t\tb=MC_AddConsoleCommandHexen2BigFont\t(mainm, 80, y,\tlocaltext(\"^bOptions\"), \"menu_options\\n\");\n\t\t\tb->common.width = 12*20;\n\t\t\tb->common.height = 20;\n\t\t\ty += 20;\n\t\t\tif (m_helpismedia.value)\n\t\t\t\tb=MC_AddConsoleCommandHexen2BigFont\t(mainm, 80, y,\tlocaltext(\"Media\"), \"menu_media\\n\");\n\t\t\telse\n\t\t\t\tb=MC_AddConsoleCommandHexen2BigFont\t(mainm, 80, y,\tlocaltext(\"Help\"), \"help\\n\");\n\t\t\tb->common.width = 12*20;\n\t\t\tb->common.height = 20;\n\t\t\ty += 20;\n\n\t\t\tb=MC_AddConsoleCommandHexen2BigFont\t(mainm, 80, y,\tlocaltext(\"Mods\"), \"menu_mods\\n\");\n\t\t\tb->common.width = 12*20;\n\t\t\tb->common.height = 20;\n\t\t\ty += 20;\n\n\t\t\tb=MC_AddConsoleCommandHexen2BigFont\t(mainm, 80, y,\tlocaltext(\"Quit\"), \"menu_quit\\n\");\n\t\t\tb->common.width = 12*20;\n\t\t\tb->common.height = 20;\n\t\t\ty += 20;\n\n\t\t\tmainm->cursoritem = (menuoption_t *)MC_AddCursor(mainm, &resel, 56, mainm->selecteditem->common.posy);\n\t\t}\n\t}\n\telse\n#endif\n\t\tif (QBigFontWorks())\n\t{\n\t\tp = R2D_SafeCachePic(\"gfx/ttl_main.lmp\");\n\t\tif (R_GetShaderSizes(p, NULL, NULL, true) > 0)\n\t\t{\n\t\t\tmainm = M_CreateMenu(0);\n\t\t\tmainm->key = MC_Main_Key;\n\t\t\tMC_AddPicture(mainm, 16, 4, 32, 144, \"gfx/qplaque.lmp\");\n\n\t\t\tMC_AddCenterPicture(mainm, 4, 24, \"gfx/ttl_main.lmp\");\n\n\t\t\ty = 32;\n\t\t\tmainm->selecteditem = (menuoption_t *)\n#ifndef CLIENTONLY\n\t\t\tMC_AddConsoleCommandQBigFont\t(mainm, 72, y,\tlocaltext(\"Single        \"), \"menu_single\\n\");\t\ty += 20;\n#endif\n\t\t\tMC_AddConsoleCommandQBigFont\t(mainm, 72, y,\tlocaltext(\"Multiplayer   \"), \"menu_multi\\n\");\t\ty += 20;\n\t\t\tMC_AddConsoleCommandQBigFont\t(mainm, 72, y,localtext(\"^bOptions       \"), \"menu_options\\n\");\ty += 20;\n\t\t\tif (m_helpismedia.value)\n\t\t\t\t{MC_AddConsoleCommandQBigFont(mainm, 72, y,\tlocaltext(\"Media         \"), \"menu_media\\n\");\t\ty += 20;}\n\t\t\telse\n\t\t\t\t{MC_AddConsoleCommandQBigFont(mainm, 72, y,\tlocaltext(\"Help          \"), \"help\\n\");\t\t\ty += 20;}\n\t\t\ty = M_Main_AddExtraOptions(mainm, y);\n#ifdef FTE_TARGET_WEB\n\t\t\tMC_AddConsoleCommandQBigFont\t(mainm, 72, y,\tlocaltext(\"Save Settings \"), \"menu_quit\\n\");\t\ty += 20;\n#else\n\t\t\tMC_AddConsoleCommandQBigFont\t(mainm, 72, y,\tlocaltext(\"Quit          \"), \"menu_quit\\n\");\t\ty += 20;\n#endif\n\n\t\t\tmainm->cursoritem = (menuoption_t *)MC_AddCursor(mainm, &resel, 54, mainm->selecteditem->common.posy);\n\t\t}\n\t}\n\telse\n\t{\n\t\tint width;\n\t\tp = R2D_SafeCachePic(\"gfx/mainmenu.lmp\");\n\t\tR2D_SafeCachePic(\"gfx/ttl_main.lmp\");\n\t\tif (R_GetShaderSizes(p, &width, NULL, true) > 0)\n\t\t{\n\t\t\tmainm = M_CreateMenu(0);\n\n\t\t\tmainm->key = MC_Main_Key;\n\t\t\tMC_AddPicture(mainm, 16, 4, 32, 144, \"gfx/qplaque.lmp\");\n\n\t\t\tMC_AddCenterPicture(mainm, 4, 24, \"gfx/ttl_main.lmp\");\n\t\t\tMC_AddPicture(mainm, 72, 32, 240, 112, \"gfx/mainmenu.lmp\");\n\n\t\t\tb=MC_AddConsoleCommand\t(mainm, 72, 312, 32,\t\"\", \"menu_single\\n\");\n\t\t\tb->common.tooltip = localtext(\"Start singleplayer Quake game.\");\n\t\t\tmainm->selecteditem = (menuoption_t *)b;\n\t\t\tb->common.width = width;\n\t\t\tb->common.height = 20;\n\t\t\tb=MC_AddConsoleCommand\t(mainm, 72, 312, 52,\t\"\", \"menu_multi\\n\");\n\t\t\tb->common.tooltip = localtext(\"Multiplayer menu.\");\n\t\t\tb->common.width = width;\n\t\t\tb->common.height = 20;\n\t\t\tb=MC_AddConsoleCommand\t(mainm, 72, 312, 72,\t\"\", \"menu_options\\n\");\n\t\t\tb->common.tooltip = localtext(\"Options menu.\");\n\t\t\tb->common.width = width;\n\t\t\tb->common.height = 20;\n\t\t\tif (m_helpismedia.value)\n\t\t\t{\n\t\t\t\tb=MC_AddConsoleCommand(mainm, 72, 312, 92,\t\"\", \"menu_media\\n\");\n\t\t\t\tb->common.tooltip = localtext(\"Media menu.\");\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tb=MC_AddConsoleCommand(mainm, 72, 312, 92,\t\"\", \"help\\n\");\n\t\t\t\tb->common.tooltip = localtext(\"Help menu.\");\n\t\t\t}\n\t\t\tb->common.width = width;\n\t\t\tb->common.height = 20;\n\t\t\tb=MC_AddConsoleCommand\t(mainm, 72, 312, 112,\t\"\", \"menu_quit\\n\");\n#ifdef FTE_TARGET_WEB\n\t\t\tb->common.tooltip = localtext(\"Save settings to local storage.\");\n#else\n\t\t\tb->common.tooltip = localtext(\"Exit to DOS.\");\n#endif\n\t\t\tb->common.width = width;\n\t\t\tb->common.height = 20;\n\n\t\t\tM_Main_AddExtraOptions(mainm, 112+20);\n\n\t\t\tmainm->cursoritem = (menuoption_t *)MC_AddCursor(mainm, &resel, 54, mainm->selecteditem->common.posy);\n\t\t}\n\t}\n\n\tif (!mainm)\n\t{\n\t\tmainm = M_CreateMenu(0);\n\t\tMC_AddRedText(mainm, 72, 320, 0,\t\t\t\t\"Main Menu\", false);\n\n\t\ty = 36;\n\t\tmainm->selecteditem = (menuoption_t *)\n\t\t//skip menu_single if we don't seem to have any content.\n#ifdef CL_MASTER\n\t\tMC_AddConsoleCommandQBigFont\t(mainm, 72, y,\tlocaltext(\"Join server\"),\t\"menu_servers\\n\");\ty += 20;\n#endif\n\t\tMC_AddConsoleCommandQBigFont\t(mainm, 72, y,\tlocaltext(\"^bOptions\"),\t\t\"menu_options\\n\");\ty += 20;\n\t\ty = M_Main_AddExtraOptions(mainm, y);\n\t\tMC_AddConsoleCommandQBigFont\t(mainm, 72, y,\tlocaltext(\"Quit\"),\t\t\t\"menu_quit\\n\");\t\ty += 20;\n\n\t\tmainm->cursoritem = (menuoption_t *)MC_AddCursor(mainm, &resel, 54, mainm->selecteditem->common.posy);\n\t}\n\n\tmainm->predraw = MC_Main_Predraw;\t//disable flashes as appropriate.\n\t//pick a better default option...\n\tb = NULL;\n\tif (!b && !m_preset_chosen.ival)\n\t\tb = M_FindButton(mainm, \"menu_options\\n\");\n#ifdef PACKAGEMANAGER\n\tif (!b && PM_AreSourcesNew(false))\n\t\tb = M_FindButton(mainm, \"menu_download\\n\");\n#endif\n\tif (b)\n\t{\n\t\tmainm->selecteditem = (menuoption_t*)b;\n\t\tmainm->cursoritem->common.posy = mainm->selecteditem->common.posy + (mainm->selecteditem->common.height-mainm->cursoritem->common.height)/2;\n\t}\n}\n\nint MC_AddBulk(struct emenu_s *menu, menuresel_t *resel, menubulk_t *bulk, int xstart, int xtextend, int y)\n{\n\tint last_y = y;\n\tmenuoption_t *selected = NULL;\n\n\twhile (bulk)\n\t{\n\t\tmenuoption_t *control;\n\t\tint x = xtextend;\n\t\tint xleft;\n\t\tint spacing = 8;\n\n\t\tif (bulk->text)\n\t\t{\t//lots of fancy code just to figure out the correct width of the string. yay. :(\n\t\t\tint px, py;\n\t\t\tconchar_t buffer[2048], *end;\n\t\t\tif (font_default)\n\t\t\t{\n\t\t\t\tend = COM_ParseFunString(CON_WHITEMASK, bulk->text, buffer, sizeof(buffer), false);\n\t\t\t\tFont_BeginString(font_default, 0, 0, &px, &py);\n\t\t\t\tpx = Font_LineWidth(buffer, end);\n\t\t\t\tFont_EndString(NULL);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tCon_DPrintf(\"MC_AddBulk: default font not initialised yet\\n\");\n\t\t\t\tpx = strlen(bulk->text)*8;\n\t\t\t}\n\n\t\t\tx -= ((float)px * vid.width) / vid.rotpixelwidth;\n\t\t}\n\t\txleft = x - xstart;\n\n\t\tswitch (bulk->type)\n\t\t{\n\t\tcase mt_text:\n\t\t\tswitch (bulk->variant)\n\t\t\t{\n\t\t\tcase -1: // end of menu\n\t\t\tdefault:\n\t\t\t\tbulk = NULL;\n\t\t\t\tcontrol = NULL;\n\t\t\t\tcontinue;\n\t\t\tcase 0: // white text\n\t\t\t\tcontrol = (union menuoption_s *)MC_AddWhiteText(menu, xleft, xtextend, y, bulk->text, bulk->rightalign);\n\t\t\t\tbreak;\n\t\t\tcase 1: // red text\n\t\t\t\tcontrol = (union menuoption_s *)MC_AddRedText(menu, xleft, xtextend, y, bulk->text, bulk->rightalign);\n\t\t\t\tbreak;\n\t\t\tcase 2: // spacing\n\t\t\t\tspacing = bulk->spacing;\n\t\t\t\tcontrol = NULL;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase mt_button:\n\t\t\tswitch (bulk->variant)\n\t\t\t{\n\t\t\tdefault:\n\t\t\tcase 0: // console command\n\t\t\t\tcontrol = (union menuoption_s *)MC_AddConsoleCommand(menu, xleft, xtextend, y, bulk->text, bulk->consolecmd);\n\t\t\t\tbreak;\n\t\t\tcase 1: // function command\n\t\t\t\tcontrol = (union menuoption_s *)MC_AddCommand(menu, xleft, xtextend, y, bulk->text, bulk->command);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase mt_checkbox:\n\t\t\tcontrol = (union menuoption_s *)MC_AddCheckBox(menu, xleft, xtextend, y, bulk->text, bulk->cvar, bulk->flags);\n\t\t\tcontrol->check.func = bulk->func;\n\t\t\tbreak;\n\t\tcase mt_slider:\n\t\t\tcontrol = (union menuoption_s *)MC_AddSlider(menu, xleft, xtextend, y, bulk->text, bulk->cvar, bulk->min, bulk->max, bulk->delta);\n\t\t\tbreak;\n\t\tcase mt_combo:\n\t\t\tswitch (bulk->variant)\n\t\t\t{\n\t\t\tdefault:\n\t\t\tcase 0: // cvar combo\n\t\t\t\tcontrol = (union menuoption_s *)MC_AddCvarCombo(menu, xleft, xtextend, y, bulk->text, bulk->cvar, bulk->options, bulk->values);\n\t\t\t\tbreak;\n\t\t\tcase 1: // combo with return value\n\t\t\t\tif (bulk->selectedoption < 0)\n\t\t\t\t{\t//invalid...\n\t\t\t\t\tcontrol = NULL;\n\t\t\t\t\tspacing = 0;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcontrol = (union menuoption_s *)MC_AddCombo(menu, xleft, xtextend, y, bulk->text, bulk->options, bulk->selectedoption);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase mt_edit:\n\t\t\tswitch (bulk->variant)\n\t\t\t{\n\t\t\tdefault:\n\t\t\tcase 0:\n\t\t\t\tcontrol = (union menuoption_s *)MC_AddEditCvar(menu, xleft, xtextend, y, bulk->text, bulk->cvarname, false);\n\t\t\t\tbreak;\n\t\t\tcase 1:\n\t\t\t\tcontrol = (union menuoption_s *)MC_AddEditCvar(menu, xleft, xtextend, y, bulk->text, bulk->cvarname, true);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tspacing = control->common.height;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tCon_Printf(CON_ERROR \"Invalid type in bulk menu!\\n\");\n\t\t\tbulk = NULL;\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (bulk->ret)\n\t\t\t*bulk->ret = control;\n\t\tif (control)\n\t\t{\n\t\t\tcontrol->common.grav_y = y-last_y;\n\t\t\tlast_y = y;\n\t\t}\n\t\tif (control && MI_Selectable(control) && !selected)\n\t\t\tselected = control;\n\t\tif (control && bulk->tooltip)\n\t\t\tcontrol->common.tooltip = bulk->tooltip;\n\t\tif (control && xleft > 0)\n\t\t\tcontrol->common.extracollide = xleft;\n\t\ty += spacing;\n\n\t\tbulk++;\n\t}\n\n\tmenu->selecteditem = selected;\n\tmenu->cursoritem = (menuoption_t*)MC_AddCursorSmall(menu, resel, xtextend + 8);\n\treturn y;\n}\n#endif\n"
  },
  {
    "path": "engine/client/m_master.c",
    "content": "#include \"quakedef.h\"\n\n#if defined(CL_MASTER) && !defined(NOBUILTINMENUS)\n#include \"cl_master.h\"\n#include \"shader.h\"\n\n//filtering\nstatic cvar_t\tsb_sortcolumn\t\t= CVARF(\"sb_sortcolumn\",\t\"0\",\tCVAR_ARCHIVE);\nstatic cvar_t\tsb_filtertext\t\t= CVARF(\"sb_filtertext\",\t\"\",\t\tCVAR_NOSAVE);\nstatic cvar_t\tsb_hideempty\t\t= CVARF(\"sb_hideempty\",\t\t\"0\",\tCVAR_ARCHIVE);\nstatic cvar_t\tsb_hidenotempty\t\t= CVARF(\"sb_hidenotempty\",\t\"0\",\tCVAR_ARCHIVE);\nstatic cvar_t\tsb_hidefull\t\t\t= CVARF(\"sb_hidefull\",\t\t\"0\",\tCVAR_ARCHIVE);\nstatic cvar_t\tsb_hidedead\t\t\t= CVARF(\"sb_hidedead\",\t\t\"1\",\tCVAR_ARCHIVE);\nstatic cvar_t\tsb_hidenetquake\t\t= CVARF(\"sb_hidenetquake\",\t\"0\",\tCVAR_ARCHIVE);\nstatic cvar_t\tsb_hidequakeworld\t= CVARF(\"sb_hidequakeworld\",\"0\",\tCVAR_ARCHIVE);\nstatic cvar_t\tsb_hideproxies\t\t= CVARF(\"sb_hideproxies\",\t\"1\",\tCVAR_ARCHIVE);\n\n#ifdef FTE_TARGET_WEB\nstatic cvar_t\tsb_showping\t\t\t= CVARF(\"sb_showping\",\t\t\"0\",\tCVAR_ARCHIVE);\t//not really much point showing pings.\n#else\nstatic cvar_t\tsb_showping\t\t\t= CVARF(\"sb_showping\",\t\t\"1\",\tCVAR_ARCHIVE);\n#endif\nstatic cvar_t\tsb_showaddress\t\t= CVARF(\"sb_showaddress\",\t\"0\",\tCVAR_ARCHIVE);\nstatic cvar_t\tsb_showmap\t\t\t= CVARF(\"sb_showmap\",\t\t\"0\",\tCVAR_ARCHIVE);\nstatic cvar_t\tsb_showgamedir\t\t= CVARF(\"sb_showgamedir\",\t\"0\",\tCVAR_ARCHIVE);\nstatic cvar_t\tsb_showplayers\t\t= CVARF(\"sb_showplayers\",\t\"1\",\tCVAR_ARCHIVE);\nstatic cvar_t\tsb_showfraglimit\t= CVARF(\"sb_showfraglimit\",\t\"0\",\tCVAR_ARCHIVE);\nstatic cvar_t\tsb_showtimelimit\t= CVARF(\"sb_showtimelimit\",\t\"0\",\tCVAR_ARCHIVE);\n\nstatic cvar_t\tsb_alpha\t= CVARF(\"sb_alpha\",\t\"0.7\",\tCVAR_ARCHIVE);\n\nvrect_t joinbutton, streambutton, specbutton;\nstatic float refreshedtime;\nstatic int isrefreshing;\nstatic enum\n{\n\tSVPV_NO,\n#ifdef HAVE_PACKET\n\tSVPV_PLAYERS,\n#endif\n\tSVPV_RULES,\n#ifdef HAVE_PACKET\n\tSVPV_HELP,\n\tSVPV_ROUTE,\n\tSVPV_LAST=SVPV_ROUTE,\n#else\n\tSVPV_LAST=SVPV_RULES,\n#endif\n} serverpreview;\nextern cvar_t slist_writeserverstxt;\nextern cvar_t slist_cacheinfo;\n\nstatic void CalcFilters(emenu_t *menu);\n\nvoid M_Serverlist_Init(void)\n{\n\tchar *grp = \"Server Browser Vars\";\n\n\tCvar_Register(&sb_alpha, grp);\n\tCvar_Register(&sb_hideempty, grp);\n\tCvar_Register(&sb_hidenotempty, grp);\n\tCvar_Register(&sb_hidefull, grp);\n\tCvar_Register(&sb_hidedead, grp);\n\tCvar_Register(&sb_hidenetquake, grp);\n\tCvar_Register(&sb_hidequakeworld, grp);\n\tCvar_Register(&sb_hideproxies, grp);\n\tCvar_Register(&sb_filtertext, grp);\n\tCvar_Register(&sb_sortcolumn, grp);\n\n\tCvar_Register(&sb_showping, grp);\n\tCvar_Register(&sb_showaddress, grp);\n\tCvar_Register(&sb_showmap, grp);\n\tCvar_Register(&sb_showgamedir, grp);\n\tCvar_Register(&sb_showplayers, grp);\n\tCvar_Register(&sb_showfraglimit, grp);\n\tCvar_Register(&sb_showtimelimit, grp);\n\n\tCvar_Register(&slist_writeserverstxt, grp);\n\tCvar_Register(&slist_cacheinfo, grp);\n}\n\nenum\n{\n\tSLFILTER_HIDENETQUAKE,\n\tSLFILTER_HIDEQUAKEWORLD,\n\tSLFILTER_HIDEPROXIES,\n\tSLFILTER_ONLYFAVOURITES,\n\tSLFILTER_HIDEEMPTY,\n\tSLFILTER_HIDEFULL,\n\tSLFILTER_MAX\n};\ntypedef struct {\n\tint servers_top;\n\tint visibleslots;\n\tint scrollpos;\n\tint selectedpos;\n\tint filtermodcount;\n\n\tint numslots;\n\tqboolean stillpolling;\n\tqbyte filter[SLFILTER_MAX];\n\n\tmenuedit_t *filtertext;\n\n\tchar refreshtext[64];\n\n\tqboolean sliderpressed;\n\n\tmenupicture_t *mappic;\n} serverlist_t;\n\nstatic void SL_DrawColumnTitle (int *x, int y, int xlen, int mx, char *str, qboolean recolor, qbyte clr, qboolean *filldraw)\n{\n\tint xmin;\n\n\tif (x == NULL)\n\t\txmin = 0;\n\telse\n\t\txmin = (*x - xlen);\n\n\tif (recolor)\n\t\tstr = va(\"^&%c-%s\", clr, str);\n\tif (mx >= xmin && !(*filldraw))\n\t{\n\t\t*filldraw = true;\n\t\tR2D_ImageColours((sin(realtime*4.4)*0.25)+0.5, (sin(realtime*4.4)*0.25)+0.5, 0.08, 1.0);\n\t\tR2D_FillBlock(xmin, y, xlen, 8);\n\t\tR2D_ImageColours(1,1,1,1);\n\t}\n\tDraw_FunStringWidth(xmin, y, str, xlen, false, false);\n\n\tif (x != NULL)\n\t\t*x -= xlen + 8;\n}\n\nstatic void SL_TitlesDraw (int x, int y, menucustom_t *ths, emenu_t *menu)\n{\n\tint sf = Master_GetSortField();\n\tint mx = mousecursor_x;\n\tqboolean filldraw = false;\n\tqbyte clr;\n\n\tif (Master_GetSortDescending())\n\t\tclr = 'D';\n\telse\n\t\tclr = 'B';\n\tx = ths->common.width;\n\tif ((mx > x || mousecursor_y < y || mousecursor_y >= y+8) && !serverpreview)\n\t\tfilldraw = true;\n\tif (sb_showtimelimit.value)\t{SL_DrawColumnTitle(&x, y, 3*8, mx, \"tl\", (sf==SLKEY_TIMELIMIT), clr, &filldraw);}\n\tif (sb_showfraglimit.value)\t{SL_DrawColumnTitle(&x, y, 3*8, mx, \"fl\", (sf==SLKEY_FRAGLIMIT), clr, &filldraw);}\n\tif (sb_showplayers.value)\t{SL_DrawColumnTitle(&x, y, 5*8, mx, \"plyrs\", (sf==SLKEY_NUMHUMANS), clr, &filldraw);}\n\tif (sb_showmap.value)\t\t{SL_DrawColumnTitle(&x, y, 8*8, mx, \"map\", (sf==SLKEY_MAP), clr, &filldraw);}\n\tif (sb_showgamedir.value)\t{SL_DrawColumnTitle(&x, y, 8*8, mx, \"gamedir\", (sf==SLKEY_GAMEDIR), clr, &filldraw);}\n\tif (sb_showping.value)\t\t{SL_DrawColumnTitle(&x, y, 3*8, mx, \"png\", (sf==SLKEY_PING), clr, &filldraw);}\n\tif (sb_showaddress.value)\t{SL_DrawColumnTitle(&x, y, 21*8, mx, \"address\", (sf==SLKEY_ADDRESS), clr, &filldraw);}\n\tSL_DrawColumnTitle(NULL, y, x, mx, \"hostname \", (sf==SLKEY_NAME), clr, &filldraw);\n}\n\nstatic qboolean SL_TitlesKey (menucustom_t *ths, emenu_t *menu, int key, unsigned int unicode)\n{\n\tint x;\n\tint mx = mousecursor_x/8;\n\tint sortkey;\n\tqboolean descending;\n\tchar sortchar = 0;\n\n\tif (key != K_MOUSE1)\n\t\treturn false;\n\n\tdo {\n\t\tx = ths->common.width/8;\n\t\tif (mx > x) return false;\t//out of bounds\n\t\tif (sb_showtimelimit.value)\t{x-=4;if (mx > x) {sortkey = SLKEY_TIMELIMIT;\tsortchar='t';\tbreak;}}\n\t\tif (sb_showfraglimit.value)\t{x-=4;if (mx > x) {sortkey = SLKEY_FRAGLIMIT;\tsortchar='f';\tbreak;}}\n\t\tif (sb_showplayers.value)\t{x-=6;if (mx > x) {sortkey = SLKEY_NUMHUMANS;\tsortchar='p';\tbreak;}}\n\t\tif (sb_showmap.value)\t\t{x-=9;if (mx > x) {sortkey = SLKEY_MAP;\t\t\tsortchar='m';\tbreak;}}\n\t\tif (sb_showgamedir.value)\t{x-=9;if (mx > x) {sortkey = SLKEY_GAMEDIR;\t\tsortchar='g';\tbreak;}}\n\t\tif (sb_showping.value)\t\t{x-=4;if (mx > x) {sortkey = SLKEY_PING;\t\tsortchar='l';\tbreak;}}\n\t\tif (sb_showaddress.value)\t{x-=22;if (mx > x) {sortkey = SLKEY_ADDRESS;\tsortchar='a';\tbreak;}}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tsortkey = SLKEY_NAME;\t\tsortchar='n';\tbreak;\n\t} while (0);\n\n//\tif (sortkey == SLKEY_ADDRESS)\n//\t\treturn true;\n\n\tswitch(sortkey)\n\t{\n\tcase SLKEY_NUMPLAYERS:\n\tcase SLKEY_NUMHUMANS:\n\t\t//favour descending order (low first)\n\t\tdescending = Master_GetSortField()!=sortkey||!Master_GetSortDescending();\n\t\tbreak;\n\tdefault:\n\t\t//favour ascending order (low first)\n\t\tdescending = Master_GetSortField()==sortkey&&!Master_GetSortDescending();\n\t\tbreak;\n\t}\n\tif (descending)\n\t\tCvar_Set(&sb_sortcolumn, va(\"-%c\", sortchar));\n\telse\n\t\tCvar_Set(&sb_sortcolumn, va(\"+%c\", sortchar));\n\tMaster_SetSortField(sortkey, descending);\n\tMaster_SortServers();\n\treturn true;\n}\n\ntypedef enum {\n\tST_NORMALQW,\n\tST_FTESERVER,\n\tST_QUAKE2,\n\tST_QUAKE3,\n\tST_NETQUAKE,\n\tST_QTV,\n\tST_PROXY,\n\tST_FAVORITE,\n\tMAX_SERVERTYPES\n} servertypes_t;\n\nstatic float serverbackcolor[MAX_SERVERTYPES * 2][3] =\n{\n\t{0.08, 0.08, 0.08}, // default\n\t{0.16, 0.16, 0.16},\n\t{0.14, 0.07, 0.07}, // FTE server\n\t{0.28, 0.14, 0.14},\n\t{0.04, 0.09, 0.04}, // Quake 2\n\t{0.08, 0.18, 0.08},\n\t{0.05, 0.05, 0.12}, // Quake 3\n\t{0.10, 0.10, 0.24},\n\t{0.12, 0.08, 0.02}, // NetQuake\n\t{0.24, 0.16, 0.04},\n\t{0.10, 0.05, 0.10}, // FTEQTV\n\t{0.20, 0.10, 0.20},\n\t{0.10, 0.05, 0.10}, // qizmo\n\t{0.20, 0.10, 0.20},\n\t{0.01, 0.13, 0.13}, // Favorite\n\t{0.02, 0.26, 0.26}\n};\n\nstatic float serverhighlight[MAX_SERVERTYPES][3] =\n{\n\t{0.35, 0.35, 0.45}, // Default\n\t{0.60, 0.30, 0.30}, // FTE Server\n\t{0.25, 0.45, 0.25}, // Quake 2\n\t{0.20, 0.20, 0.60}, // Quake 3\n\t{0.40, 0.40, 0.25}, // NetQuake\n\t{0.45, 0.20, 0.45}, // FTEQTV\n\t{0.45, 0.20, 0.45}, // qizmo\n\t{0.10, 0.60, 0.60}  // Favorite\n};\n\nstatic servertypes_t flagstoservertype(int flags)\n{\n\tif (flags & SS_FAVORITE)\n\t\treturn ST_FAVORITE;\n\tif (flags & SS_PROXY)\n\t{\n\t\tif (flags & SS_FTESERVER)\n\t\t\treturn ST_QTV;\n\t\telse\n\t\t\treturn ST_PROXY;\n\t}\n#ifdef _DEBUG\n\tif (flags & SS_FTESERVER)\n\t\treturn ST_FTESERVER;\n#endif\n\n\n\tswitch(flags & SS_PROTOCOLMASK)\n\t{\n\tcase SS_QEPROT:\n\t\treturn ST_NETQUAKE;\n\tcase SS_NETQUAKE:\n\t\treturn ST_NETQUAKE;\n\tcase SS_QUAKE2:\n\t\treturn ST_QUAKE2;\n\tcase SS_QUAKE3:\n\t\treturn ST_QUAKE3;\n\tcase SS_QUAKEWORLD:\n\t\treturn ST_NORMALQW;\n\tcase SS_UNKNOWN:\n\t\treturn ST_NORMALQW;\n\tdefault:\n\t\treturn ST_FTESERVER;\t//bug\n\t}\n}\n\nstatic void SL_ServerDraw (int x, int y, menucustom_t *ths, emenu_t *menu)\n{\n\tserverlist_t *info = (serverlist_t*)(menu + 1);\n\tserverinfo_t *si;\n\tint thisone = ths->dint + info->scrollpos;\n\tservertypes_t stype;\n\tchar adr[MAX_ADR_SIZE];\n\n\tif (sb_filtertext.modifiedcount != info->filtermodcount)\n\t\tCalcFilters(menu);\n\n\tsi = Master_SortedServer(thisone);\n\tif (si)\n\t{\n\t\tx = ths->common.width;\n\t\tstype = flagstoservertype(si->special);\n\t\tif (thisone == info->selectedpos)\n\t\t{\n\t\t\tR2D_ImageColours(SRGBA(\n\t\t\t\tserverhighlight[(int)stype][0],\n\t\t\t\tserverhighlight[(int)stype][1],\n\t\t\t\tserverhighlight[(int)stype][2],\n\t\t\t\t1.0));\n\t\t}\n\t\telse if (thisone == info->scrollpos + (int)(mousecursor_y-info->servers_top)/8 && mousecursor_x < x && !serverpreview)\n\t\t\tR2D_ImageColours(SRGBA((sin(realtime*4.4)*0.25)+0.5, (sin(realtime*4.4)*0.25)+0.5, 0.08, sb_alpha.value));\n\t\telse if (selectedserver.inuse && NET_CompareAdr(&si->adr, &selectedserver.adr) && !strcmp(si->brokerid, selectedserver.brokerid))\n\t\t\tR2D_ImageColours(SRGBA(((sin(realtime*4.4)*0.25)+0.5) * 0.5, ((sin(realtime*4.4)*0.25)+0.5)*0.5, 0.08*0.5, sb_alpha.value));\n\t\telse\n\t\t{\n\t\t\tR2D_ImageColours(SRGBA(\n\t\t\t\tserverbackcolor[(int)stype * 2 + (thisone & 1)][0],\n\t\t\t\tserverbackcolor[(int)stype * 2 + (thisone & 1)][1],\n\t\t\t\tserverbackcolor[(int)stype * 2 + (thisone & 1)][2],\n\t\t\t\tsb_alpha.value));\n\t\t}\n\t\tR2D_FillBlock(0, y, ths->common.width, 8);\n\n\t\tR2D_ImageColours(1,1,1,1);\n\t\tif (sb_showtimelimit.value)\t{Draw_FunStringWidth((x-3*8), y, va(\"%i\", si->tl), 3*8, false, false); x-=4*8;}\n\t\tif (sb_showfraglimit.value)\t{Draw_FunStringWidth((x-3*8), y, va(\"%i\", si->fl), 3*8, false, false); x-=4*8;}\n\t\tif (sb_showplayers.value)\t{Draw_FunStringWidth((x-5*8), y, va(\"%2i/%2i\", si->numhumans, si->maxplayers), 5*8, false, false); x-=6*8;}\n\t\tif (sb_showmap.value)\t\t{Draw_FunStringWidth((x-8*8), y, si->map, 8*8, false, false); x-=9*8;}\n\t\tif (sb_showgamedir.value)\t{Draw_FunStringWidth((x-8*8), y, si->gamedir, 8*8, false, false); x-=9*8;}\n\t\tif (sb_showping.value)\t\t{Draw_FunStringWidth((x-3*8), y, *si->brokerid?\"---\":va(\"%i\", si->ping), 3*8, false, false); x-=4*8;}\n\t\tif (sb_showaddress.value)\t{Draw_FunStringWidth((x-21*8), y, Master_ServerToString(adr, sizeof(adr), si), 21*8, false, false); x-=22*8;}\n\t\tDraw_FunStringWidth(0, y, si->name, x, false, false);\n\t}\n}\nvoid MC_EditBox_Key(menuedit_t *edit, int key, unsigned int unicode);\nstatic qboolean SL_ServerKey (menucustom_t *ths, emenu_t *menu, int key, unsigned int unicode)\n{\n\tstatic int lastclick;\n\tint curtime;\n\tint oldselection;\n\tserverlist_t *info = (serverlist_t*)(menu + 1);\n\tserverinfo_t *server;\n\tqboolean ctrl = keydown[K_LCTRL] || keydown[K_RCTRL];\n\n\tif (key == K_MOUSE1 || key == K_TOUCH)\n\t{\n\t\toldselection = info->selectedpos;\n\t\tinfo->selectedpos = info->scrollpos + (mousecursor_y-info->servers_top)/8;\n\t\tserver = Master_SortedServer(info->selectedpos);\n\n\t\tselectedserver.inuse = true;\n\t\tSListOptionChanged(server);\n\n\t\tif (server)\n\t\t{\n\t\t\tsnprintf(info->mappic->picturename, 32, \"levelshots/%s\", server->map);\n\t\t\tif (!*server->map || !R2D_SafeCachePic(info->mappic->picturename))\n\t\t\t\tsnprintf(info->mappic->picturename, 32, \"levelshots/nomap\");\n\t\t}\n\t\telse\n\t\t{\n\t\t\tsnprintf(info->mappic->picturename, 32, \"levelshots/nomap\");\n\t\t\treturn true;\n\t\t}\n\n\t\tcurtime = Sys_Milliseconds();\n\t\tif (lastclick > curtime || lastclick < curtime-250)\n\t\t{\t//shouldn't happen, or too old a click\n\t\t\tlastclick = curtime;\n\t\t\treturn true;\n\t\t}\n\t\tif (oldselection == info->selectedpos)\n\t\t\tserverpreview = (server->adr.prot>=NP_STREAM)?SVPV_RULES:1;\n\t\treturn true;\n\t}\n\n\telse if (ctrl && key == 'f')\n\t{\n\t\tserver = Master_SortedServer(info->selectedpos);\n\t\tif (server)\n\t\t{\n\t\t\tserver->special ^= SS_FAVORITE;\n\t\t\tsb_favouriteschanged = true;\n\t\t}\n\t}\n\n\telse if (key == K_ENTER || key == K_KP_ENTER || key == K_GP_DIAMOND_CONFIRM || (ctrl && (key == 's' || key == 'j')) || key == K_SPACE)\n\t{\n\t\tserver = Master_SortedServer(info->selectedpos);\n\t\tif (server)\n\t\t{\n\t\t\tserverpreview = 1;\n\t\t\tselectedserver.inuse = true;\n\t\t\tSListOptionChanged(server);\n\t\t}\n\t\treturn true;\n\t}\n\telse\n\t{\n\t\tMC_EditBox_Key(info->filtertext, key, unicode);\n\t\treturn true;\n\t}\n\treturn false;\n}\nstatic void SL_PreDraw\t(emenu_t *menu)\n{\n\tserverlist_t *info = (serverlist_t*)(menu + 1);\n\tMaster_CheckPollSockets();\n\n\tif (isrefreshing)\n\t{\n\t\tif (!CL_QueryServers())\n\t\t{\n\t\t\t//extra second, to ensure we got replies\n\t\t\tif (isrefreshing != 2)\n\t\t\t{\n\t\t\t\tisrefreshing = 2;\n\t\t\t\trefreshedtime = Sys_DoubleTime()+1;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t\tisrefreshing = 1;\t//something new came up\n\n\t\tif (isrefreshing == 2)\n\t\t{\n\t\t\tif (refreshedtime < Sys_DoubleTime())\n\t\t\t{\n\t\t\t\tisrefreshing = false;\n\t\t\t\tMaster_SortServers();\n\t\t\t}\n\t\t}\n\n\t}\n\n\tinfo->numslots = Master_NumSorted();\n\tsnprintf(info->refreshtext, sizeof(info->refreshtext), localtext(\"Refresh - %u/%u/%u\\n\"), info->numslots, Master_NumAlive(), Master_TotalCount());\n}\nqboolean NET_SendPollPacket(int len, void *data, netadr_t to);\nstatic void SL_PostDraw\t(emenu_t *menu)\n{\n#ifdef HAVE_PACKET\n\tstatic char *helpstrings[] =\n\t{\n\t\t\"rmb: cancel\",\n\t\t\"j: join\",\n\t\t\"o: observe\",\n\t\t\"b: join with automatic best route\",\n\t\t\"v: say server info\",\n\t\t\"ctrl-v: say_team server info\",\n\t\t\"c: copy server info to clipboard\",\n\t\t\"ctrl-c: copy server info only to clipboard\",\n\t\t\"i: view serverinfo\",\n\t\t\"k: toggle this info\"\n\t};\n\tint skins = 0;\n#endif\n\n\tchar buf[64];\n\tserverlist_t *info = (serverlist_t*)(menu + 1);\n\tMaster_CheckPollSockets();\n\n\tif (serverpreview != SVPV_NO)\n\t{\n\t\tserverinfo_t *server = selectedserver.inuse?Master_InfoForServer(&selectedserver.adr, selectedserver.brokerid):NULL;\n\t\tint h = 0;\n\t\tint w = 240;\n\t\tchar *qtv;\n#ifdef HAVE_PACKET\n\t\tif (server && selectedserver.refreshtime < realtime)\n\t\t{\n\t\t\tselectedserver.refreshtime = realtime + 4;\n\t\t\tserver->sends++;\n#ifdef NQPROT\n\t\t\t//we might have gotten stuck. reset the poll\n\t\t\tif ((server->special&SS_PROTOCOLMASK) == SS_NETQUAKE)\n\t\t\t{\t//start spamming the server to get all of its details. silly protocols.\n\t\t\t\tselectedserver.lastplayer = 0;\n\t\t\t\t*selectedserver.lastrule = 0;\n\n\t\t\t\tSZ_Clear(&net_message);\n\t\t\t\tnet_message.packing = SZ_RAWBYTES;\n\t\t\t\tnet_message.currentbit = 0;\n\t\t\t\tMSG_WriteLong(&net_message, 0);// save space for the header, filled in later\n\t\t\t\tMSG_WriteByte(&net_message, CCREQ_PLAYER_INFO);\n\t\t\t\tMSG_WriteByte(&net_message, selectedserver.lastplayer);\n\t\t\t\t*((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));\n\t\t\t\tNET_SendPollPacket(net_message.cursize, net_message.data, server->adr);\n\t\t\t\tSZ_Clear(&net_message);\n\t\t\t\tMSG_WriteLong(&net_message, 0);// save space for the header, filled in later\n\t\t\t\tMSG_WriteByte(&net_message, CCREQ_RULE_INFO);\n\t\t\t\tMSG_WriteString(&net_message, selectedserver.lastrule);\n\t\t\t\t*((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));\n\t\t\t\tNET_SendPollPacket(net_message.cursize, net_message.data, server->adr);\n\t\t\t\tSZ_Clear(&net_message);\n\t\t\t}\n\t\t\telse\n#endif\n\t\t\t\tMaster_QueryServer(server);\n\t\t}\n#endif\n\t\tR2D_ImageColours(1,1,1,1);\n\t\tif (server && server->moreinfo)\n\t\t{\n\t\t\tint lx, x, y, i;\n\t\t\tif (serverpreview == SVPV_RULES)\n\t\t\t{\n\t\t\t\tfor (i = 0; ; i++)\n\t\t\t\t{\n\t\t\t\t\tchar *key = Info_KeyForNumber(server->moreinfo->info, i);\n\t\t\t\t\tif (!strcmp(key, \"hostname\") || !strcmp(key, \"status\"))\t//these are part of the header\n\t\t\t\t\t\t;\n\t\t\t\t\telse if (*key)\n\t\t\t\t\t\th++;\n\t\t\t\t\telse\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n#ifdef HAVE_PACKET\n\t\t\telse if (serverpreview == SVPV_HELP)\n\t\t\t\th = countof(helpstrings);\n\t\t\telse if (serverpreview == SVPV_ROUTE)\n\t\t\t{\n\t\t\t\t//count the number of proxies the best route will need\n\t\t\t\tserverinfo_t *prox;\n\t\t\t\tfor (h = 1, prox = server; prox; h++, prox = prox->prevpeer)\n\t\t\t\t\t;\n\t\t\t\tw += 120;\n\t\t\t}\n\t\t\telse if (serverpreview == SVPV_PLAYERS)\n\t\t\t{\n\t\t\t\th += server->moreinfo->numplayers+2;\n\n\t\t\t\tfor (i = 0; i < server->moreinfo->numplayers; i++)\n\t\t\t\t{\n\t\t\t\t\tif (*server->moreinfo->players[i].skin && strcmp(server->moreinfo->players[i].skin, \"base\"))\n\t\t\t\t\t{\n\t\t\t\t\t\tskins = true;\n\t\t\t\t\t\tw += 8*8+8;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n#endif\n\t\t\th += 4;\n\t\t\th *= 8;\n\n\t\t\tDraw_ApproxTextBox(vid.width/2.0f - w/2-4, vid.height/2.0f - h/2 - 8, w+8, h+8);\n\n\t\t\tlx = vid.width/2 - w/2;\n\t\t\ty = vid.height/2 - h/2 - 4;\n\n\t\t\tx = lx;\n\t\t\tDraw_FunStringWidth (x, y, Info_ValueForKey(server->moreinfo->info, \"hostname\"), w, 2, false);\n\t\t\ty += 8;\n\t\t\tDraw_FunStringWidth (x, y, Info_ValueForKey(server->moreinfo->info, \"status\"), w, 2, false);\n\t\t\ty += 8;\n\t\t\tDraw_FunStringWidth (x, y, Master_ServerToString(buf, sizeof(buf), server), w, 2, false);\n\t\t\ty += 8;\n\n\t\t\tDraw_FunStringWidth (x, y, \"^Ue01d^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01f\", w, 2, false);\n\t\t\ty+=8;\n\n\t\t\tif (serverpreview == SVPV_RULES)\n\t\t\t{\n\t\t\t\tfor (i = 0; ; i++)\n\t\t\t\t{\n\t\t\t\t\tchar *key = Info_KeyForNumber(server->moreinfo->info, i);\n\t\t\t\t\tif (!strcmp(key, \"hostname\") || !strcmp(key, \"status\"))\n\t\t\t\t\t\t;\n\t\t\t\t\telse if (*key)\n\t\t\t\t\t{\n\t\t\t\t\t\tchar *value = Info_ValueForKey(server->moreinfo->info, key);\n\t\t\t\t\t\tx = lx;\n\t\t\t\t\t\tDraw_FunStringWidth (x, y, key, w/2 - 8, true, true);\n\t\t\t\t\t\tx+=w/2;\n\t\t\t\t\t\tDraw_FunStringWidth (x, y, value, w/2, false, false);\n\t\t\t\t\t\ty += 8;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n#ifdef HAVE_PACKET\n\t\t\telse if (serverpreview == SVPV_HELP)\n\t\t\t{\n\t\t\t\tx = lx;\n\t\t\t\tfor (i = 0; i < countof(helpstrings); i++)\n\t\t\t\t{\n\t\t\t\t\tDraw_FunStringWidth (x, y, localtext(helpstrings[i]), w, false, false);\n\t\t\t\t\ty += 8;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (serverpreview == SVPV_ROUTE)\n\t\t\t{\n\t\t\t\tserverinfo_t *prox;\n\t\t\t\tfor (prox = server; prox; prox = prox->prevpeer)\n\t\t\t\t{\n\t\t\t\t\tDraw_FunStringWidth (x, y, va(\"%i\", prox->cost), 32-8, true, false);\n\t\t\t\t\tDraw_FunStringWidth (x + 32, y, Master_ServerToString(buf, sizeof(buf), prox), w/2 - 8 - 32, true, false);\n\t\t\t\t\tDraw_FunStringWidth (x + w/2, y, prox->name, w/2, false, false);\n\t\t\t\t\ty += 8;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (serverpreview == SVPV_PLAYERS)\n\t\t\t{\n\t\t\t\tint teamplay = atoi(Info_ValueForKey(server->moreinfo->info, \"teamplay\"));\n\t\t\t\tx = lx;\n\t\t\t\tDraw_FunStringWidth (x, y, \"^mFrgs\", 28, true, false);\n\t\t\t\tx += 32+8;\n\t\t\t\tDraw_FunStringWidth (x, y, \"^mPng\", 28, true, false);\n\t\t\t\tx += 3*8+8;\n\n\t\t\t\tif (teamplay)\n\t\t\t\t{\n\t\t\t\t\tDraw_FunStringWidth (x, y, \"^mTeam\", 4*8, false, false);\n\t\t\t\t\tx += 4*8+8;\n\t\t\t\t\tDraw_FunStringWidth (x, y, \"^mName\", 12*8, false, false);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tDraw_FunStringWidth (x, y, \"^mName\", 16*8, false, false);\n\t\t\t\t}\n\t\t\t\tif (skins)\n\t\t\t\t{\n\t\t\t\t\tDraw_FunStringWidth (lx+w-(8*8+8), y, \"^mSkin\", 8*8, false, false);\n\t\t\t\t\tx = lx+w;\n\t\t\t\t}\n\n\t\t\t\ty+=8;\n\t\t\t\tfor (i = 0; i < server->moreinfo->numplayers; i++)\n\t\t\t\t{\n\t\t\t\t\tx = lx;\n\t\t\t\t\tif (server->moreinfo->players[i].isspec&1)\n\t\t\t\t\t\tDraw_FunStringWidth (x, y, \"spec\", 32, true, false);\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tR2D_ImagePaletteColour (Sbar_ColorForMap(server->moreinfo->players[i].topc), 1.0);\n\t\t\t\t\t\tR2D_FillBlock (x, y+1, 32, 3);\n\t\t\t\t\t\tR2D_ImagePaletteColour (Sbar_ColorForMap(server->moreinfo->players[i].botc), 1.0);\n\t\t\t\t\t\tR2D_FillBlock (x, y+4, 32, 4);\n\t\t\t\t\t\tR2D_ImageColours (1.0, 1.0, 1.0, 1.0);\n\t\t\t\t\t\tDraw_FunStringWidth (x, y, va(\"%3i\", server->moreinfo->players[i].frags), 32-4, true, false);\n\t\t\t\t\t}\n\t\t\t\t\tx += 32+8;\n\t\t\t\t\tif (server->moreinfo->players[i].isspec&2)\n\t\t\t\t\t\tDraw_FunStringWidth (x-8, y, \"bot\", 3*8+8, true, false);\n\t\t\t\t\telse\n\t\t\t\t\t\tDraw_FunStringWidth (x-8, y, va(\"%3i\", server->moreinfo->players[i].ping), 3*8+8, true, false);\n\t\t\t\t\tx += 3*8+8;\n\n\t\t\t\t\tif (teamplay)\n\t\t\t\t\t{\n\t\t\t\t\t\tDraw_FunStringWidth (x, y, server->moreinfo->players[i].team, 4*8, false, false);\n\t\t\t\t\t\tx += 4*8+8;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (skins)\n\t\t\t\t\t{\n\t\t\t\t\t\tDraw_FunStringWidth (x, y, server->moreinfo->players[i].name, lx+w-(8*8+8)-x, false, false);\n\t\t\t\t\t\tx += lx+w-(8*8+8)-x;\n\t\t\t\t\t\tDraw_FunStringWidth (x, y, server->moreinfo->players[i].skin, 8*8, false, false);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\tDraw_FunStringWidth (x, y, server->moreinfo->players[i].name, lx+w-x, false, false);\n\n\t\t\t\t\ty += 8;\n\t\t\t\t}\n\n\t\t\t\tDraw_FunStringWidth (lx, y, localtext(\"^h(left/rightarrow for different info)\"), w, false, false);\n\t\t\t}\n#endif\n\t\t}\n\t\telse\n\t\t{\n\t\t\tDraw_ApproxTextBox(vid.width/2 - 100, vid.height/2 - 16, 200, 16*3);\n\t\t\tDraw_FunStringWidth(vid.width/2 - 100, vid.height/2 - 8, localtext(\"Querying server\"), 200, 2, false);\n\t\t\tDraw_FunStringWidth(vid.width/2 - 100, vid.height/2 + 0, localtext(\"Please wait\"), 200, 2, false);\n\t\t}\n\n\t\tif (server && (server->special & SS_PROTOCOLMASK) == SS_QUAKEWORLD)\n\t\t{\n\t\t\tint lx = vid.width/2 - w/2;\n\t\t\tint y = vid.height/2 - h/2 - 4 + h;\n\t\t\tint bh, bw;\n\t\t\tqboolean active = false;\n\t\t\tbw = w+16+12;\n\t\t\tbh = 24;\n//\t\t\tlx += bw-12;\n\t\t\tbw = strlen(localtext(\"Observe\"))*8 + 24;\n\t\t\tbw = ((bw+15)/16) * 16;\t//width must be a multiple of 16\n//\t\t\tlx -= bw;\n\n\t\t\tspecbutton.x = lx;\n\t\t\tspecbutton.y = y;\n\t\t\tspecbutton.width = bw + 16;\n\t\t\tspecbutton.height = bh + 16;\n\t\t\tR2D_ImageColours(1,1,1,1);\n\t\t\ty += 8;\n\t\t\tDraw_ApproxTextBox(lx, y, bw, bh);\n\n\t\t\tif (mousecursor_x >= specbutton.x && mousecursor_x < specbutton.x+specbutton.width)\n\t\t\t\tif (mousecursor_y >= specbutton.y && mousecursor_y < specbutton.y+specbutton.height)\n\t\t\t\t\tactive = true;\n\n\t\t\tDraw_FunStringWidth(lx, y + (bh-8)/2, localtext(\"Observe\"), bw, 2, active);y+=8;\n\t\t}\n\n\t\tqtv = Info_ValueForKey(server->moreinfo->info, \"qtvstream\");\n\t\tif (server && *qtv)\n\t\t{\n\t\t\tint lx = vid.width/2 - w/2;\n\t\t\tint y = vid.height/2 - h/2 - 4 + h;\n\t\t\tint bh, bw;\n\t\t\tqboolean active = false;\n\t\t\tbw = w+16+12;\n\t\t\tbh = 24;\n//\t\t\tlx += bw-12;\n\t\t\tbw = strlen(localtext(\"Stream\"))*8 + 24;\n\t\t\tbw = ((bw+15)/16) * 16;\t//width must be a multiple of 16\n//\t\t\tlx -= bw;\n\n\t\t\tstreambutton.x = lx;\n\t\t\tstreambutton.y = y;\n\t\t\tstreambutton.width = bw + 16;\n\t\t\tstreambutton.height = bh + 16;\n\t\t\tR2D_ImageColours(1,1,1,1);\n\t\t\ty += 8;\n\t\t\tDraw_ApproxTextBox(lx, y, bw, bh);\n\n\t\t\tif (mousecursor_x >= streambutton.x && mousecursor_x <  streambutton.x+streambutton.width)\n\t\t\t\tif (mousecursor_y >= streambutton.y && mousecursor_y < streambutton.y+streambutton.height)\n\t\t\t\t\tactive = true;\n\n\t\t\tDraw_FunStringWidth(lx, y + (bh-8)/2, localtext(\"Stream\"), bw, 2, active);y+=8;\n\t\t}\n\n\t\t{\n\t\t\tint lx = vid.width/2 - w/2;\n\t\t\tint y = vid.height/2 - h/2 - 4 + h;\n\t\t\tint bw, bh;\n\t\t\tqboolean active = false;\n\t\t\tbw = w+16;\n\t\t\tbh = 24;\n\t\t\tlx += w-12;\n\t\t\tbw = strlen(localtext(\"Join\"))*8 + 24;\n\t\t\tbw = ((bw+15)/16) * 16;\t//width must be a multiple of 16\n\t\t\tlx -= bw;\n\n\t\t\tjoinbutton.x = lx;\n\t\t\tjoinbutton.y = y;\n\t\t\tjoinbutton.width = bw + 16;\n\t\t\tjoinbutton.height = bh + 16;\n\t\t\tR2D_ImageColours(1,1,1,1);\n\t\t\ty += 8;\n\t\t\tlx += 8;\n\t\t\tDraw_ApproxTextBox(lx, y, bw, bh);\n\n\t\t\tif (mousecursor_x >= joinbutton.x && mousecursor_x < joinbutton.x+joinbutton.width)\n\t\t\t\tif (mousecursor_y >= joinbutton.y && mousecursor_y < joinbutton.y+joinbutton.height)\n\t\t\t\t\tactive = true;\n\n\t\t\tDraw_FunStringWidth(lx, y + (bh-8)/2, localtext(\"Join\"), bw, 2, active);y+=8;\n\t\t}\n\t}\n\telse if (isrefreshing)\n\t{\n\t\tR2D_ImageColours(1,1,1,1);\n\t\tDraw_ApproxTextBox(vid.width/2 - 100-4, vid.height/2 - 24, 200, 64);\n\t\tDraw_FunStringWidth(vid.width/2 - 100, vid.height/2 - 8, localtext(\"Refreshing, please wait\"), 200, 2, false);\n\t\tDraw_FunStringWidth(vid.width/2 - 100, vid.height/2 + 0, va(localtext(\"polling %i of %i\"), Master_NumPolled(), Master_TotalCount()), 200, 2, false);\n\t}\n\telse if (!info->numslots)\n\t{\n\t\tR2D_ImageColours(1,1,1,1);\n\t\tif (!Master_TotalCount())\n\t\t{\n\t\t\tDraw_FunStringWidth(0, vid.height/2 - 8, localtext(\"No servers found\"), vid.width, 2, false);\n#ifdef HAVE_PACKET\n\t\t\tDraw_FunStringWidth(0, vid.height/2 + 0, localtext(\"Check internet connection\"), vid.width, 2, false);\n#endif\n\t\t}\n\t\telse if (!Master_NumAlive())\n\t\t{\n\t\t\tDraw_FunStringWidth(0, vid.height/2 - 8, localtext(\"No servers responding\"), vid.width, 2, false);\n#ifdef HAVE_PACKET\n\t\t\tDraw_FunStringWidth(0, vid.height/2 + 0, localtext(\"Check udp internet connection\"), vid.width, 2, false);\n#endif\n\t\t}\n\t\telse\n\t\t{\n\t\t\tDraw_FunStringWidth(0, vid.height/2 - 8, localtext(\"All servers were filtered out\"), vid.width, 2, false);\n\t\t\tDraw_FunStringWidth(0, vid.height/2 + 0, localtext(\"Change filter settings\"), vid.width, 2, false);\n\t\t}\n\t}\n}\nstatic qboolean SL_Key\t(emenu_t *menu, int key, unsigned int unicode)\n{\n\tserverlist_t *info = (serverlist_t*)(menu + 1);\n\n\tif (serverpreview != SVPV_NO)\n\t{\n\t\tchar buf[64];\n\t\tserverinfo_t *server = selectedserver.inuse?Master_InfoForServer(&selectedserver.adr, selectedserver.brokerid):NULL;\n\t\tqboolean ctrldown = keydown[K_LCTRL] || keydown[K_RCTRL];\n\n\t\tif (key == K_ESCAPE || key == K_GP_DIAMOND_CANCEL || key == K_MOUSE2 || key == K_MOUSE4)\n\t\t{\n\t\t\tserverpreview = SVPV_NO;\n\t\t\treturn true;\n\t\t}\n\t\telse if (key == K_MOUSE1 || key == K_TOUCH)\n\t\t{\n\t\t\tif (mousecursor_x >= joinbutton.x && mousecursor_x < joinbutton.x+joinbutton.width)\n\t\t\t\tif (mousecursor_y >= joinbutton.y && mousecursor_y < joinbutton.y+joinbutton.height)\n\t\t\t\t{\n\t\t\t\t\tserverpreview = SVPV_NO;\n\t\t\t\t\tgoto dojoin;\n\t\t\t\t}\n\t\t\tif (mousecursor_x >= specbutton.x && mousecursor_x < specbutton.x+joinbutton.width)\n\t\t\t\tif (mousecursor_y >= specbutton.y && mousecursor_y < specbutton.y+joinbutton.height)\n\t\t\t\t{\n\t\t\t\t\tserverpreview = SVPV_NO;\n\t\t\t\t\tgoto dospec;\n\t\t\t\t}\n\t\t\tif (mousecursor_x >= streambutton.x && mousecursor_x < streambutton.x+streambutton.width)\n\t\t\t\tif (mousecursor_y >= streambutton.y && mousecursor_y < streambutton.y+streambutton.height)\n\t\t\t\t{\n\t\t\t\t\tserverpreview = SVPV_NO;\n\t\t\t\t\tgoto dostream;\n\t\t\t\t}\n\t\t\treturn true;\n\t\t}\n#ifdef HAVE_PACKET\n\t\telse if (key == 'i')\n\t\t{\n\t\t\tserverpreview = ((serverpreview==SVPV_RULES)?1:SVPV_RULES);\n\t\t\treturn true;\n\t\t}\n\t\telse if (key == 'k')\n\t\t{\n\t\t\tserverpreview = ((serverpreview==SVPV_HELP)?1:SVPV_HELP);\n\t\t\treturn true;\n\t\t}\n#endif\n\t\telse if (key == K_LEFTARROW || key == K_KP_LEFTARROW || key == K_GP_DPAD_LEFT)\n\t\t{\n\t\t\tif (--serverpreview < 1)\n\t\t\t\tserverpreview = SVPV_LAST;\n\n#ifdef HAVE_PACKET\n\t\t\tif (serverpreview == SVPV_ROUTE && server)\n\t\t\t\tMaster_FindRoute(server->adr);\n#endif\n\t\t\treturn true;\n\t\t}\n\t\telse if (key == K_RIGHTARROW || key == K_KP_RIGHTARROW || key == K_GP_DPAD_RIGHT)\n\t\t{\n\t\t\tif (++serverpreview > SVPV_LAST)\n\t\t\t\tserverpreview = 1;\n\n#ifdef HAVE_PACKET\n\t\t\tif (serverpreview == SVPV_ROUTE && server)\n\t\t\t\tMaster_FindRoute(server->adr);\n#endif\n\t\t\treturn true;\n\t\t}\n#ifdef HAVE_PACKET\n\t\telse if (key == 'b' && serverpreview != SVPV_ROUTE)\n\t\t{\n\t\t\tif (server)\n\t\t\t\tMaster_FindRoute(server->adr);\n\t\t\tserverpreview = SVPV_ROUTE;\n\t\t\treturn true;\n\t\t}\n#endif\n\t\telse if (key == 't')\n\t\t{\ndostream:\n\t\t\tCbuf_AddText(va(\"qtvplay \\\"%s\\\"\\n\", Info_ValueForKey(server->moreinfo->info, \"qtvstream\")), RESTRICT_LOCAL);\n\t\t\tM_RemoveAllMenus(true);\n\t\t\treturn true;\n\t\t}\n\t\telse if (key == 'b' || key == 'o' || key == 'j' || key == K_ENTER || key == K_KP_ENTER || key == K_GP_DIAMOND_CONFIRM || key == K_GP_DIAMOND_ALTCONFIRM)\t//join\n\t\t{\n\t\t\tif (key == 's' || key == 'o' || key == K_GP_DIAMOND_ALTCONFIRM)\n\t\t\t{\ndospec:\n\t\t\t\tCbuf_AddText(\"spectator 1\\n\", RESTRICT_LOCAL);\n\t\t\t}\n\t\t\telse if (key == 'j' || key == K_GP_DIAMOND_CONFIRM)\n\t\t\t{\ndojoin:\n\t\t\t\tCbuf_AddText(\"spectator 0\\n\", RESTRICT_LOCAL);\n\t\t\t}\n\n\t\t\t//which connect command are we using?\n#ifdef NQPROT\n\t\t\tif ((server->special & SS_PROTOCOLMASK) == SS_QEPROT)\n\t\t\t\tCbuf_AddText(\"connectqe \", RESTRICT_LOCAL);\n\t\t\telse\n#endif\n\t\t\t\tCbuf_AddText(\"connect \", RESTRICT_LOCAL);\n\n\t\t\t//output the server's address\n\t\t\tCbuf_AddText(va(\"%s\", Master_ServerToString(buf, sizeof(buf), server)), RESTRICT_LOCAL);\n#ifdef HAVE_PACKET\n\t\t\tif (serverpreview == SVPV_ROUTE || key == 'b')\n\t\t\t{\t//and postfix it with routing info if we're going for a proxied route.\n\t\t\t\tif (serverpreview != SVPV_ROUTE)\n\t\t\t\t\tMaster_FindRoute(server->adr);\n\t\t\t\tfor (server = server->prevpeer; server; server = server->prevpeer)\n\t\t\t\t\tCbuf_AddText(va(\"@%s\", Master_ServerToString(buf, sizeof(buf), server)), RESTRICT_LOCAL);\n\t\t\t}\n#endif\n\t\t\tCbuf_AddText(\"\\n\", RESTRICT_LOCAL);\n\n\n\t\t\tM_RemoveAllMenus(true);\n\t\t\treturn true;\n\t\t}\n\t\telse if (server && key == 'c' && ctrldown)\t//copy to clip\n\t\t{\n\t\t\tSys_SaveClipboard(CBT_CLIPBOARD, Master_ServerToString(buf, sizeof(buf), server));\n\t\t\treturn true;\n\t\t}\n\t\telse if (server && (key == 'v' || key == 'c'))\t//say to current server\n\t\t{\n\t\t\tchar *s;\n\t\t\tchar safename[128];\n\t\t\tQ_strncpyz(safename, server->name, sizeof(safename));\n\t\t\t//ALWAYS sanitize your inputs.\n\t\t\twhile((s = strchr(safename, ';')))\n\t\t\t\t*s = ' ';\n\t\t\twhile((s = strchr(safename, '\\n')))\n\t\t\t\t*s = ' ';\n\t\t\tif (key == 'c')\n\t\t\t\tSys_SaveClipboard(CBT_CLIPBOARD, va(\"%s - %s\\n\", server->name, Master_ServerToString(buf, sizeof(buf), server)));\n\t\t\telse if (ctrldown)\n\t\t\t\tCbuf_AddText(va(\"say_team %s - %s\\n\", server->name, Master_ServerToString(buf, sizeof(buf), server)), RESTRICT_LOCAL);\n\t\t\telse\n\t\t\t\tCbuf_AddText(va(\"say %s - %s\\n\", server->name, Master_ServerToString(buf, sizeof(buf), server)), RESTRICT_LOCAL);\n\t\t\treturn true;\n\t\t}\n\t\t//eat (nearly) all keys\n\t\telse if (!(key == K_UPARROW || key == K_KP_UPARROW || key == K_GP_DPAD_UP || key == K_DOWNARROW || key == K_KP_DOWNARROW || key == K_GP_DPAD_DOWN))\n\t\t\treturn true;\n\t}\n\tif (key == K_HOME)\n\t{\n\t\tinfo->scrollpos = 0;\n\t\tinfo->selectedpos = 0;\n\t}\n\telse if (key == K_END)\n\t{\n\t\tinfo->selectedpos = info->numslots-1;\n\t\tinfo->scrollpos = info->selectedpos - (vid.height-16-7)/8+8;\n\t}\n\telse if (key == K_PGDN)\n\t\tinfo->selectedpos += 10;\n\telse if (key == K_PGUP)\n\t\tinfo->selectedpos -= 10;\n\telse if (key == K_DOWNARROW || key == K_KP_DOWNARROW || key == K_GP_DPAD_DOWN)\n\t\tinfo->selectedpos += 1;\n\telse if (key == K_UPARROW || key == K_KP_UPARROW || key == K_GP_DPAD_UP)\n\t\tinfo->selectedpos -= 1;\n\telse if (key == K_MWHEELUP)\n\t\tinfo->selectedpos -= 3;\n\telse if (key == K_MWHEELDOWN)\n\t\tinfo->selectedpos += 3;\n\telse\n\t\treturn false;\n\n\t{\n\t\tserverinfo_t *server;\n\t\tserver = Master_SortedServer(info->selectedpos);\n\n//\t\tselectedserver.inuse = true;\n//\t\tSListOptionChanged(server);\n\n\t\tif (server)\n\t\t{\n\t\t\tsnprintf(info->mappic->picturename, 32, \"levelshots/%s\", server->map);\n\t\t\tif (!*server->map || !R2D_SafeCachePic(info->mappic->picturename))\n\t\t\t\tsnprintf(info->mappic->picturename, 32, \"levelshots/nomap\");\n\t\t}\n\t\telse\n\t\t{\n\t\t\tsnprintf(info->mappic->picturename, 32, \"levelshots/nomap\");\n\t\t}\n\n\t\tif (/*serverpreview &&*/ server)\n\t\t{\n\t\t\tselectedserver.inuse = true;\n\t\t\tSListOptionChanged(server);\n\n#ifdef HAVE_PACKET\n\t\t\tif (serverpreview == SVPV_ROUTE)\n\t\t\t\tMaster_FindRoute(server->adr);\n#endif\n\t\t}\n\t}\n\n\tif (info->selectedpos < 0)\n\t\tinfo->selectedpos = 0;\n\tif (info->selectedpos > info->numslots-1)\n\t\tinfo->selectedpos = info->numslots-1;\n\tif (info->scrollpos < info->selectedpos - info->visibleslots)\n\t\tinfo->scrollpos = info->selectedpos - info->visibleslots;\n\tif (info->selectedpos < info->scrollpos)\n\t\tinfo->scrollpos = info->selectedpos;\n\n\treturn true;\n}\n\nstatic void SL_ServerPlayer (int x, int y, menucustom_t *ths, emenu_t *menu)\n{\n\tif (selectedserver.inuse)\n\t{\n\t\tif (selectedserver.detail)\n\t\t\tif (ths->dint < selectedserver.detail->numplayers)\n\t\t\t{\n\t\t\t\tint i = ths->dint;\n\t\t\t\tif (selectedserver.detail->players[i].isspec&1)\n\t\t\t\t\tDraw_FunStringWidth (x, y, localtext(\"spectator\"), 32, false, false);\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tR2D_ImagePaletteColour (Sbar_ColorForMap(selectedserver.detail->players[i].topc), 1.0);\n\t\t\t\t\tR2D_FillBlock (x, y, 32, 4);\n\t\t\t\t\tR2D_ImagePaletteColour (Sbar_ColorForMap(selectedserver.detail->players[i].botc), 1.0);\n\t\t\t\t\tR2D_FillBlock (x, y+4, 32, 4);\n\t\t\t\t\tR2D_ImageColours (1.0, 1.0, 1.0, 1.0);\n\t\t\t\t\tDraw_FunStringWidth (x, y, va(\"%3i\", selectedserver.detail->players[i].frags), 32-4, true, false);\n\t\t\t\t}\n\n\t\t\t\tDraw_FunStringWidth (x+36, y, selectedserver.detail->players[i].name, 128-36, false, false);\n\t\t\t}\n\t}\n}\n\nstatic void SL_SliderDraw (int x, int y, menucustom_t *ths, emenu_t *menu)\n{\n\tserverlist_t *info = (serverlist_t*)(menu + 1);\n\n\tmpic_t *pic;\n\n\tR2D_ImageColours(1,1,1,1);\n\n\tpic = R2D_SafeCachePic(\"scrollbars/slidebg.tga\");\n\tif (pic && R_GetShaderSizes(pic, NULL, NULL, false)>0)\n\t{\n\t\tR2D_ScalePic(x + ths->common.width - 8, y+8, 8, ths->common.height-16, pic);\n\n\t\tpic = R2D_SafeCachePic(\"scrollbars/arrow_up.tga\");\n\t\tR2D_ScalePic(x + ths->common.width - 8, y, 8, 8, pic);\n\n\t\tpic = R2D_SafeCachePic(\"scrollbars/arrow_down.tga\");\n\t\tR2D_ScalePic(x + ths->common.width - 8, y + ths->common.height - 8, 8, 8, pic);\n\n\t\ty += ((info->scrollpos) / ((float)info->numslots - info->visibleslots)) * (float)(ths->common.height-(64+16-1));\n\n\t\ty += 8;\n\n\t\tpic = R2D_SafeCachePic(\"scrollbars/slider.tga\");\n\t\tR2D_ScalePic(x + ths->common.width - 8, y, 8, 64, pic);\n\t}\n\telse\n\t{\n\t\tR2D_ImageColours(SRGBA(0.1, 0.1, 0.2, 1.0));\n\t\tR2D_FillBlock(x, y, ths->common.width, ths->common.height);\n\n\t\ty += ((info->scrollpos) / ((float)info->numslots - info->visibleslots)) * (ths->common.height-8);\n\n\t\tR2D_ImageColours(SRGBA(0.35, 0.35, 0.55, 1.0));\n\t\tR2D_FillBlock(x, y, 8, 8);\n\t\tR2D_ImageColours(1,1,1,1);\n\t}\n\n\tif (keydown[K_MOUSE1] || keydown[K_TOUCH])\n\t\tif (mousecursor_x >= ths->common.posx && mousecursor_x < ths->common.posx + ths->common.width)\n\t\t\tif (mousecursor_y >= ths->common.posy && mousecursor_y < ths->common.posy + ths->common.height)\n\t\t\t\tinfo->sliderpressed = true;\n\tif (info->sliderpressed)\n\t{\n\t\tif (keydown[K_MOUSE1] || keydown[K_TOUCH])\n\t\t{\n\t\t\tfloat my;\n\t\t\tserverlist_t *info = (serverlist_t*)(menu + 1);\n\n\t\t\tmy = mousecursor_y;\n\t\t\tmy -= ths->common.posy;\n\t\t\tif (R_GetShaderSizes(R2D_SafeCachePic(\"scrollbars/slidebg.tga\"), NULL, NULL, false)>0)\n\t\t\t{\n\t\t\t\tmy -= 32+8;\n\t\t\t\tmy /= ths->common.height - (64+16);\n\t\t\t}\n\t\t\telse\n\t\t\t\tmy /= ths->common.height;\n\t\t\tmy *= (info->numslots-info->visibleslots);\n\n\t\t\tif (my > info->numslots-info->visibleslots-1)\n\t\t\t\tmy = info->numslots-info->visibleslots-1;\n\t\t\tif (my < 0)\n\t\t\t\tmy = 0;\n\n\t\t\tinfo->scrollpos = my;\n\t\t}\n\t\telse\n\t\t\tinfo->sliderpressed = false;\n\t}\n}\nstatic qboolean SL_SliderKey (menucustom_t *ths, emenu_t *menu, int key, unsigned int unicode)\n{\n\tif (key == K_MOUSE1 || key == K_TOUCH)\n\t{\n\t\tfloat my;\n\t\tserverlist_t *info = (serverlist_t*)(menu + 1);\n\n\t\tmy = mousecursor_y;\n\t\tmy -= ths->common.posy;\n\t\tif (R_GetShaderSizes(R2D_SafeCachePic(\"scrollbars/slidebg.tga\"), NULL, NULL, false)>0)\n\t\t{\n\t\t\tmy -= 32+8;\n\t\t\tmy /= ths->common.height - (64+16);\n\t\t}\n\t\telse\n\t\t\tmy /= ths->common.height;\n\t\tmy *= (info->numslots-info->visibleslots);\n\n\t\tif (my > info->numslots-info->visibleslots-1)\n\t\t\tmy = info->numslots-info->visibleslots-1;\n\t\tif (my < 0)\n\t\t\tmy = 0;\n\n\t\tinfo->scrollpos = my;\n\t\tinfo->sliderpressed = true;\n\t\treturn true;\n\t}\n\treturn false;\n}\n\nstatic void CalcFilters(emenu_t *menu)\n{\n\tserverlist_t *info = (serverlist_t*)(menu + 1);\n\tinfo->filtermodcount = sb_filtertext.modifiedcount;\n\n\tMaster_ClearMasks();\n\n//\tMaster_SetMaskInteger(false, SLKEY_PING, 0, SLIST_TEST_GREATEREQUAL);\n//\tMaster_SetMaskInteger(false, SLKEY_BASEGAME, SS_UNKNOWN, SLIST_TEST_NOTEQUAL);\n\tif (info->filter[SLFILTER_HIDENETQUAKE] && info->filter[SLFILTER_HIDEQUAKEWORLD])\n\t\tMaster_SetMaskInteger(false, SLKEY_FLAGS, SS_PROXY, SLIST_TEST_CONTAINS);\t//show only proxies\n\telse\n\t{\n\t\tif (info->filter[SLFILTER_HIDENETQUAKE]) Master_SetMaskInteger(false, SLKEY_BASEGAME, SS_NETQUAKE, SLIST_TEST_NOTEQUAL);\n\t\tif (info->filter[SLFILTER_HIDEQUAKEWORLD]) Master_SetMaskInteger(false, SLKEY_BASEGAME, SS_QUAKEWORLD, SLIST_TEST_NOTEQUAL);\n\t}\n\tif (info->filter[SLFILTER_HIDEPROXIES]) Master_SetMaskInteger(false, SLKEY_FLAGS, SS_PROXY, SLIST_TEST_NOTCONTAIN);\n\tif (!info->filter[SLFILTER_ONLYFAVOURITES]) Master_SetMaskInteger(false, SLKEY_FLAGS, SS_FAVORITE, SLIST_TEST_CONTAINS);\n\tif (info->filter[SLFILTER_HIDEEMPTY]) Master_SetMaskInteger(false, SLKEY_NUMHUMANS, 0, SLIST_TEST_NOTEQUAL);\n\tif (info->filter[SLFILTER_HIDEFULL]) Master_SetMaskInteger(false, SLKEY_FREEPLAYERS, 0, SLIST_TEST_NOTEQUAL);\n\n\tif (*sb_filtertext.string) Master_SetMaskString(false, SLKEY_NAME, sb_filtertext.string, SLIST_TEST_CONTAINS);\n\n\tMaster_SortServers();\n}\n\nstatic qboolean SL_ReFilter (menucheck_t *option, emenu_t *menu, chk_set_t set)\n{\n\tserverlist_t *info = (serverlist_t*)(menu + 1);\n\tswitch(set)\n\t{\n\tcase CHK_CHECKED:\n\t\treturn !info->filter[option->bits];\n\tcase CHK_TOGGLE:\n\t\tinfo->filter[option->bits] ^= 1;\n\t\tCvar_Set(&sb_hidenetquake, info->filter[SLFILTER_HIDENETQUAKE]?\"1\":\"0\");\n\t\tCvar_Set(&sb_hidequakeworld, info->filter[SLFILTER_HIDEQUAKEWORLD]?\"1\":\"0\");\n\t\tCvar_Set(&sb_hideproxies, info->filter[SLFILTER_HIDEPROXIES]?\"1\":\"0\");\n\n\t\tCvar_Set(&sb_hideempty, info->filter[SLFILTER_HIDEEMPTY]?\"1\":\"0\");\n\t\tCvar_Set(&sb_hidefull, info->filter[SLFILTER_HIDEFULL]?\"1\":\"0\");\n\n\t\tCalcFilters(menu);\n\n\t\treturn true;\n\t}\n\n\treturn true;\n}\n\nstatic void SL_Remove\t(emenu_t *menu)\n{\n\tserverlist_t *info = (serverlist_t*)(menu + 1);\n\n\tCvar_Set(&sb_hidenetquake, info->filter[SLFILTER_HIDENETQUAKE]?\"1\":\"0\");\n\tCvar_Set(&sb_hidequakeworld, info->filter[SLFILTER_HIDEQUAKEWORLD]?\"1\":\"0\");\n\tCvar_Set(&sb_hideproxies, info->filter[SLFILTER_HIDEPROXIES]?\"1\":\"0\");\n\tCvar_Set(&sb_hideempty, info->filter[SLFILTER_HIDEEMPTY]?\"1\":\"0\");\n\tCvar_Set(&sb_hidefull, info->filter[SLFILTER_HIDEFULL]?\"1\":\"0\");\n}\n\nstatic qboolean SL_DoRefresh (menuoption_t *opt, emenu_t *menu, int key)\n{\n\tif (key == K_MOUSE1 || key == K_TOUCH || key == K_ENTER || key == K_KP_ENTER || key == K_GP_DIAMOND_CONFIRM)\n\t{\n\t\tMasterInfo_Refresh(false);\n\t\tisrefreshing = true;\n\t\treturn true;\n\t}\n\treturn false;\n}\n\nvoid M_Menu_ServerList2_f(void)\n{\n\tint i, y, x;\n\temenu_t *menu;\n\tmenucustom_t *cust;\n\tserverlist_t *info;\n\tqboolean descending;\n\tint sortkey;\n\tchar *sc;\n\n\tif (!qrenderer)\n\t{\n\t\tCbuf_AddText(\"wait; menu_servers\\n\", Cmd_ExecLevel);\n\t\treturn;\n\t}\n\n\tserverpreview = SVPV_NO;\t//in case it was lingering.\n\n\tKey_Dest_Remove(kdm_console);\n\n\tmenu = M_CreateMenu(sizeof(serverlist_t));\n\tmenu->predraw = SL_PreDraw;\n\tmenu->postdraw = SL_PostDraw;\n\tmenu->key = SL_Key;\n\tmenu->remove = SL_Remove;\n\n\tinfo = (serverlist_t*)(menu + 1);\n\n\ty = 16;\n\tcust = MC_AddCustom(menu, 0, y, NULL, 0, NULL);\n\tcust->draw = SL_TitlesDraw;\n\tcust->key = SL_TitlesKey;\n\tcust->common.height = 8;\n\tcust->common.width = vid.width-8;\n\ty+=8;\n\n\tinfo->servers_top = y;\n\tinfo->visibleslots = (vid.height-info->servers_top - 64);\n\n\tcust = MC_AddCustom(menu, vid.width-8, 16, NULL, 0, NULL);\n\tcust->draw = SL_SliderDraw;\n\tcust->key = SL_SliderKey;\n\tcust->common.height = info->visibleslots;\n\tcust->common.width = 8;\n\n\tinfo->visibleslots = (info->visibleslots-8)/8;\n\tfor (i = 0, y = info->servers_top; i <= info->visibleslots; y +=8, i++)\n\t{\n\t\tcust = MC_AddCustom(menu, 0, y, NULL, i, NULL);\n\t\tif (i==0)\n\t\t\tmenu->selecteditem = (menuoption_t*)&cust->common;\n\t\tcust->draw = SL_ServerDraw;\n\t\tcust->key = SL_ServerKey;\n\t\tcust->common.height = 8;\n\t\tcust->common.width = vid.width-8;\n\t\tcust->common.noselectionsound = true;\n\t}\n\tmenu->dontexpand = true;\n\n\ti = 0;\n\tfor (x = 256; x < vid.width-64; x += 128)\n\t{\n\t\tfor (y = vid.height-64+8; y < vid.height; y += 8, i++)\n\t\t{\n\t\t\tcust = MC_AddCustom(menu, x+16, y, NULL, i, NULL);\n\t\t\tcust->draw = SL_ServerPlayer;\n\t\t\tcust->key = NULL;\n\t\t\tcust->common.height = 8;\n\t\t\tcust->common.width = 0;\n\t\t}\n\t}\n\n\tstrcpy(info->refreshtext, localtext(\"Refresh Server List\"));\n\n\tMC_AddCheckBox(menu, 0, 72, vid.height - 64+8*1, localtext(\"Ping     \"), &sb_showping, 1);\n\tMC_AddCheckBox(menu, 0, 72, vid.height - 64+8*2, localtext(\"Address  \"), &sb_showaddress, 1);\n\tMC_AddCheckBox(menu, 0, 72, vid.height - 64+8*3, localtext(\"Map      \"), &sb_showmap, 1);\n\tMC_AddCheckBox(menu, 0, 72, vid.height - 64+8*4, localtext(\"Gamedir  \"), &sb_showgamedir, 1);\n\tMC_AddCheckBox(menu, 0, 72, vid.height - 64+8*5, localtext(\"Players  \"), &sb_showplayers, 1);\n\tMC_AddCheckBox(menu, 0, 72, vid.height - 64+8*6, localtext(\"Fraglimit\"), &sb_showfraglimit, 1);\n\tMC_AddCheckBox(menu, 0, 72, vid.height - 64+8*7, localtext(\"Timelimit\"), &sb_showtimelimit, 1);\n\n#ifdef NQPROT\n\tif (M_GameType() == MGT_QUAKE1)\n\t{\n\t\tMC_AddCheckBoxFunc(menu, 128, 208, vid.height - 64+8*1, localtext(\"Show NQ   \"), SL_ReFilter, SLFILTER_HIDENETQUAKE);\n\t\tMC_AddCheckBoxFunc(menu, 128, 208, vid.height - 64+8*2, localtext(\"Show QW   \"), SL_ReFilter, SLFILTER_HIDEQUAKEWORLD);\n\t}\n#endif\n\tMC_AddCheckBoxFunc(menu, 128, 208, vid.height - 64+8*3, localtext(\"Show Proxies\"), SL_ReFilter, SLFILTER_HIDEPROXIES);\n\tinfo->filtertext =\n\tMC_AddEditCvar    (menu, 128, 200, vid.height - 64+8*4, localtext(\"Filter   \"),\tsb_filtertext.name, true);\n\tMC_AddCheckBoxFunc(menu, 128, 208, vid.height - 64+8*5, localtext(\"Only Favs \"), SL_ReFilter, SLFILTER_ONLYFAVOURITES);\n\tMC_AddCheckBoxFunc(menu, 128, 208, vid.height - 64+8*6, localtext(\"Show Empty\"), SL_ReFilter, SLFILTER_HIDEEMPTY);\n\tMC_AddCheckBoxFunc(menu, 128, 208, vid.height - 64+8*7, localtext(\"Show Full \"), SL_ReFilter, SLFILTER_HIDEFULL);\n\n\tMC_AddCommand(menu, 64, 320, 0, info->refreshtext, SL_DoRefresh);\n\n\tinfo->filter[SLFILTER_HIDENETQUAKE] = !!sb_hidenetquake.value;\n\tinfo->filter[SLFILTER_HIDEQUAKEWORLD] = !!sb_hidequakeworld.value;\n\tinfo->filter[SLFILTER_HIDEPROXIES] = !!sb_hideproxies.value;\n\tinfo->filter[SLFILTER_ONLYFAVOURITES] = true;//!sb_showonlyfavourites.value;\n\tinfo->filter[SLFILTER_HIDEEMPTY] = !!sb_hideempty.value;\n\tinfo->filter[SLFILTER_HIDEFULL] = !!sb_hidefull.value;\n\n\tinfo->mappic = (menupicture_t *)MC_AddPicture(menu, vid.width - 64, vid.height - 64, 64, 64, \"012345678901234567890123456789012\");\n\n\tdescending = false;\n\n\tsc = sb_sortcolumn.string;\n\tif (*sc == '-')\n\t\tdescending = true;\n\telse if (*sc == '+')\n\t\tdescending = false;\n\telse\n\t\tsc--;\n\tsc++;\n\tswitch(*sc)\n\t{\n\tcase 't':\tsortkey = SLKEY_TIMELIMIT;\tbreak;\n\tcase 'f':\tsortkey = SLKEY_FRAGLIMIT;\tbreak;\n\tcase 'p':\tsortkey = SLKEY_NUMHUMANS;\tbreak;\n\tcase 'm':\tsortkey = SLKEY_MAP;\t\tbreak;\n\tcase 'g':\tsortkey = SLKEY_GAMEDIR;\tbreak;\n\tcase 'l':\tsortkey = SLKEY_PING;\t\tbreak;\n\tcase 'a':\tsortkey = SLKEY_ADDRESS;\tbreak;\n\tcase 'n':\tsortkey = SLKEY_NAME;\t\tbreak;\n\tdefault:\tsortkey = SLKEY_PING;\t\tbreak;\n\t}\n\tMaster_SetSortField(sortkey, descending);\n\n\tif (!Master_TotalCount())\n\t{\n\t\tMasterInfo_Refresh(true);\n\t\tisrefreshing = true;\n\t}\n\n\tCalcFilters(menu);\n}\n\n#ifdef HAVE_PACKET\nstatic float quickconnecttimeout;\n\nstatic void M_QuickConnect_PreDraw(emenu_t *menu)\n{\n\tserverinfo_t *best = NULL;\n\tserverinfo_t *s;\n\tchar adr[MAX_ADR_SIZE];\n\tint ping;\n\n\tMaster_CheckPollSockets();\t//see if we were told something important.\n\tCL_QueryServers();\n\n\tif (Sys_DoubleTime() > quickconnecttimeout)\n\t{\n\t\tquickconnecttimeout = Sys_DoubleTime() + 15;\n\n\t\tfor (ping = 50; ping < 200 && !best; ping += 50)\n\t\t{\n\t\t\tfor (s = firstserver; s; s = s->next)\n\t\t\t{\n\t\t\t\tif (!s->maxplayers)\t//no response?\n\t\t\t\t\tcontinue;\n\t\t\t\tif (s->players == s->maxplayers)\n\t\t\t\t\tcontinue;\t//server is full already\n\t\t\t\tif (s->special & SS_PROXY)\n\t\t\t\t\tcontinue;\t//don't quickconnect to a proxy. their player counts are often wrong (especially with qtv)\n\t\t\t\tif (s->ping < ping)\t//don't like servers with too high a ping\n\t\t\t\t{\n\t\t\t\t\tif (s->numhumans > 0)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (best)\n\t\t\t\t\t\t\tif (best->numhumans > s->numhumans)\n\t\t\t\t\t\t\t\tcontinue;\t//go for the one with most players\n\t\t\t\t\t\tbest = s;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (best)\n\t\t{\n\t\t\tCon_TPrintf(\"Quick connect found %s (gamedir %s, players %i/%i/%i, ping %ims)\\n\", best->name, best->gamedir, best->numhumans, best->players, best->maxplayers, best->ping);\n\n#ifdef NQPROT\n\t\t\tif ((best->special & SS_PROTOCOLMASK) == SS_QEPROT)\n\t\t\t\tCbuf_AddText(va(\"connectqe %s\\n\", Master_ServerToString(adr, sizeof(adr), best)), RESTRICT_LOCAL);\n\t\t\telse\n#endif\n\t\t\t\tCbuf_AddText(va(\"join %s\\n\", Master_ServerToString(adr, sizeof(adr), best)), RESTRICT_LOCAL);\n\n\t\t\tM_ToggleMenu_f();\n\t\t\treturn;\n\t\t}\n\n\t\t//retry\n\t\tMasterInfo_Refresh(false);\n\t\tisrefreshing = true;\n\t}\n}\n\nstatic qboolean M_QuickConnect_Key\t(emenu_t *menu, int key, unsigned int unicode)\n{\n\treturn false;\n}\n\nstatic void M_QuickConnect_Remove\t(emenu_t *menu)\n{\n}\n\nstatic qboolean M_QuickConnect_Cancel (menuoption_t *opt, emenu_t *menu, int key)\n{\n\treturn false;\n}\n\nstatic void M_QuickConnect_DrawStatus (int x, int y, menucustom_t *ths, emenu_t *menu)\n{\n\tDraw_FunString(x, y, va(\"Polling, %i secs\\n\", (int)(quickconnecttimeout - Sys_DoubleTime() + 0.9)));\n}\n\nvoid M_QuickConnect_f(void)\n{\n\tmenucustom_t *cust;\n\temenu_t *menu;\n\n\tMasterInfo_Refresh(false);\n\tisrefreshing = true;\n\n\tquickconnecttimeout = Sys_DoubleTime() + 5;\n\n\tmenu = M_CreateMenu(sizeof(serverlist_t));\n\tmenu->predraw = M_QuickConnect_PreDraw;\n\tmenu->key = M_QuickConnect_Key;\n\tmenu->remove = M_QuickConnect_Remove;\n\n\tcust = MC_AddCustom(menu, 64, 64, NULL, 0, NULL);\n\tcust->draw = M_QuickConnect_DrawStatus;\n\tcust->common.height = 8;\n\tcust->common.width = vid.width-8;\n\n\tMC_AddCommand(menu, 64, 0, 128, localtext(\"Refresh\"), SL_DoRefresh);\n\tMC_AddCommand(menu, 64, 0, 136, localtext(\"Cancel\"), M_QuickConnect_Cancel);\n}\n#endif\n\n\n\n\n#endif\n"
  },
  {
    "path": "engine/client/m_mp3.c",
    "content": "//mp3 menu and track selector.\n//was origonally an mp3 track selector, now handles lots of media specific stuff - like q3 films!\n//should rename to m_media.c\n#include \"quakedef.h\"\n\n#ifdef GLQUAKE\n#include \"glquake.h\"\n#endif\n#ifdef VKQUAKE\n#include \"../vk/vkrenderer.h\"\n#endif\n#include \"shader.h\"\n#include \"gl_draw.h\"\n\n#if defined(HAVE_JUKEBOX)\n\t#if defined(_WIN32) && !defined(WINRT) && !defined(NOMEDIAMENU)\n\t\t//#define WINAMP\n\t#endif\n#endif\n#if (defined(HAVE_MEDIA_DECODER) || defined(HAVE_MEDIA_ENCODER)) && defined(_WIN32) && !defined(WINRT)\n\t#define HAVE_API_VFW\n#endif\n\n#ifdef _WIN32\n\t#include \"winquake.h\"\n#endif\n#if defined(AVAIL_MP3_ACM) || defined(HAVE_API_VFW)\n//equivelent to\n//#include <msacm.h>\n#undef CDECL\t//windows is stupid at times.\n#define CDECL __cdecl\n\n#if defined(_MSC_VER) && (_MSC_VER < 1300)\n\t#define DWORD_PTR DWORD\n#endif\n\nDECLARE_HANDLE(HACMSTREAM);\ntypedef HACMSTREAM *LPHACMSTREAM;\nDECLARE_HANDLE(HACMDRIVER);\ntypedef struct {\n\tDWORD     cbStruct;\n\tDWORD     fdwStatus;\n\tDWORD_PTR dwUser;\n\tLPBYTE    pbSrc;\n\tDWORD     cbSrcLength;\n\tDWORD     cbSrcLengthUsed;\n\tDWORD_PTR dwSrcUser;\n\tLPBYTE    pbDst;\n\tDWORD     cbDstLength;\n\tDWORD     cbDstLengthUsed;\n\tDWORD_PTR dwDstUser;\n\tDWORD     dwReservedDriver[10];\n} ACMSTREAMHEADER, *LPACMSTREAMHEADER;\n#define ACM_STREAMCONVERTF_BLOCKALIGN   0x00000004\n\n\n\n\n//mingw workarounds\n#define LPWAVEFILTER void *\n#include <objbase.h>\n\nMMRESULT (WINAPI *qacmStreamUnprepareHeader) (HACMSTREAM has, LPACMSTREAMHEADER pash, DWORD fdwUnprepare);\nMMRESULT (WINAPI *qacmStreamConvert) (HACMSTREAM has, LPACMSTREAMHEADER pash, DWORD fdwConvert);\nMMRESULT (WINAPI *qacmStreamPrepareHeader) (HACMSTREAM has, LPACMSTREAMHEADER pash, DWORD fdwPrepare);\nMMRESULT (WINAPI *qacmStreamOpen) (LPHACMSTREAM phas, HACMDRIVER had, LPWAVEFORMATEX pwfxSrc, LPWAVEFORMATEX pwfxDst, LPWAVEFILTER pwfltr, DWORD_PTR dwCallback, DWORD_PTR dwInstance, DWORD fdwOpen);\nMMRESULT (WINAPI *qacmStreamClose) (HACMSTREAM has, DWORD fdwClose);\n\nstatic qboolean qacmStartup(void)\n{\n\tstatic int inited;\n\tstatic dllhandle_t *module;\n\tif (!inited)\n\t{\n\t\tdllfunction_t funcs[] =\n\t\t{\n\t\t\t{(void*)&qacmStreamUnprepareHeader,\t\"acmStreamUnprepareHeader\"},\n\t\t\t{(void*)&qacmStreamConvert,\t\t\t\"acmStreamConvert\"},\n\t\t\t{(void*)&qacmStreamPrepareHeader,\t\"acmStreamPrepareHeader\"},\n\t\t\t{(void*)&qacmStreamOpen,\t\t\t\"acmStreamOpen\"},\n\t\t\t{(void*)&qacmStreamClose,\t\t\t\"acmStreamClose\"},\n\t\t\t{NULL,NULL}\n\t\t};\n\t\tinited = true;\n\t\tmodule = Sys_LoadLibrary(\"msacm32.dll\", funcs);\n\t}\n\n\treturn module?true:false;\n}\n#endif\n\nstatic char media_currenttrack[MAX_QPATH];\nstatic cvar_t music_fade = CVAR(\"music_fade\", \"1\");\n\n//higher bits have priority (if they have something to play).\n#define MEDIA_GAMEMUSIC (1u<<0)\t//cd music. also music command etc.\n#ifdef HAVE_JUKEBOX\n#define MEDIA_CVARLIST\t(1u<<1)\t//cvar abuse. handy for preserving times when switching tracks.\n#define MEDIA_PLAYLIST\t(1u<<2)\t//\nstatic unsigned int media_playlisttypes;\nstatic unsigned int media_playlistcurrent;\n\n//cvar abuse\nstatic int music_playlist_last;\nstatic cvar_t music_playlist_index = CVAR(\"music_playlist_index\", \"-1\");\n//\tcreated dynamically: CVAR(\"music_playlist_list0+\", \"\"),\n//\tcreated dynamically: CVAR(\"music_playlist_sampleposition0+\", \"-1\"),\n\n\ntypedef struct mediatrack_s{\n\tchar filename[MAX_QPATH];\n\tchar nicename[MAX_QPATH];\n\tint length;\n\tstruct mediatrack_s *next;\n} mediatrack_t;\n\n//info about the current stuff that is playing.\nstatic char media_friendlyname[MAX_QPATH];\n\n\n\nint lasttrackplayed;\n\ncvar_t media_shuffle = CVAR(\"media_shuffle\", \"1\");\ncvar_t media_repeat = CVAR(\"media_repeat\", \"1\");\n#ifdef WINAMP\ncvar_t media_hijackwinamp = CVAR(\"media_hijackwinamp\", \"0\");\n#endif\n\nint selectedoption=-1;\nint numtracks;\nint nexttrack=-1;\nmediatrack_t *tracks;\n\nchar media_iofilename[MAX_OSPATH]=\"\";\n\n#if !defined(NOMEDIAMENU) && !defined(NOBUILTINMENUS)\nstatic int mouseselectedoption=-1;\nvoid Media_LoadTrackNames (char *listname);\nvoid Media_SaveTrackNames (char *listname);\nint loadedtracknames;\n#endif\nqboolean Media_EvaluateNextTrack(void);\n\n#else\n#define NOMEDIAMENU\t//the media menu requires us to be able to queue up random tracks and stuff.\n#endif\n\n#ifdef HAVE_CDPLAYER\nstatic int cdplayingtrack;\t//currently playing cd track (becomes 0 when paused)\nstatic int cdpausedtrack;\t//currently paused cd track\n\n//info about (fake)cd tracks that we want to play\nint cdplaytracknum;\n\n//info about (fake)cd tracks that we could play if asked.\n#define REMAPPED_TRACKS 256\nstatic struct\n{\n\tchar fname[MAX_QPATH];\n} cdremap[REMAPPED_TRACKS];\nstatic qboolean cdenabled;\nstatic int cdnumtracks;\t\t//maximum cd track we can play.\n#endif\n\n\nstatic char media_playtrack[MAX_QPATH];\t\t//name of track to play next/now\nstatic char media_loopingtrack[MAX_QPATH];\t//name of track to loop afterwards\nqboolean media_fadeout;\nfloat media_fadeouttime;\n\nqboolean Media_CleanupTrackName(const char *track, int *out_track, char *result, size_t resultsize);\n\n//whatever music track was previously playing has terminated.\n//return value is the new sample to start playing.\n//*starttime says the time into the track that we should resume playing at\n//this is on the main thread with the mixer locked, its safe to do stuff, but try not to block\nsfx_t *Media_NextTrack(int musicchannelnum, float *starttime)\n{\n\tsfx_t *s = NULL;\n\tif (bgmvolume.value <= 0 || mastervolume.value <= 0)\n\t\treturn NULL;\n\tmedia_fadeout = false;\t//it has actually ended now, at least on one device. don't fade the new track too...\n\n\tQ_strncpyz(media_currenttrack, \"\", sizeof(media_currenttrack));\n#ifdef HAVE_JUKEBOX\n\tQ_strncpyz(media_friendlyname, \"\", sizeof(media_friendlyname));\n\n\tmusic_playlist_index.modified = false;\n\tmusic_playlist_last = -1;\n\tmedia_playlistcurrent = 0;\n\n\tif (!media_playlistcurrent && (media_playlisttypes & MEDIA_PLAYLIST))\n\t{\n#if !defined(NOMEDIAMENU) && !defined(NOBUILTINMENUS)\n\t\tif (!loadedtracknames)\n\t\t\tMedia_LoadTrackNames(\"sound/media.m3u\");\n#endif\n\t\tif (Media_EvaluateNextTrack())\n\t\t{\n\t\t\tmedia_playlistcurrent = MEDIA_PLAYLIST;\n\t\t\tif (*media_currenttrack == '#')\n\t\t\t\treturn S_PrecacheSound2(media_currenttrack+1, true);\n\t\t\telse\n\t\t\t\treturn S_PrecacheSound(media_currenttrack);\n\t\t}\n\t}\n\tif (!media_playlistcurrent && (media_playlisttypes & MEDIA_CVARLIST))\n\t{\n\t\tif (music_playlist_index.ival >= 0)\n\t\t{\n\t\t\tcvar_t *list = Cvar_Get(va(\"music_playlist_list%i\", music_playlist_index.ival), \"\", 0, \"compat\");\n\t\t\tif (list)\n\t\t\t{\n\t\t\t\tcvar_t *sampleposition = Cvar_Get(va(\"music_playlist_sampleposition%i\", music_playlist_index.ival), \"-1\", 0, \"compat\");\n\t\t\t\tQ_snprintfz(media_currenttrack, sizeof(media_currenttrack), \"sound/cdtracks/%s\", list->string);\n\t\t\t\tQ_strncpyz(media_friendlyname, \"\", sizeof(media_friendlyname));\n\t\t\t\tmedia_playlistcurrent = MEDIA_CVARLIST;\n\t\t\t\tmusic_playlist_last = music_playlist_index.ival;\n\t\t\t\tif (sampleposition)\n\t\t\t\t{\n\t\t\t\t\t*starttime = sampleposition->value;\n\t\t\t\t\tif (*starttime == -1)\n\t\t\t\t\t\t*starttime = 0;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\t*starttime = 0;\n\n\t\t\t\ts = S_PrecacheSound(media_currenttrack);\n\t\t\t}\n\t\t}\n\t}\n\n\tif (!media_playlistcurrent && (media_playlisttypes & MEDIA_GAMEMUSIC))\n#endif\n\t{\n#ifdef HAVE_CDPLAYER\n\t\tif (cdplaytracknum)\n\t\t{\n\t\t\tif (cdplayingtrack != cdplaytracknum && cdpausedtrack != cdplaytracknum)\n\t\t\t{\n\t\t\t\tCDAudio_Play(cdplaytracknum);\n\t\t\t\tcdplayingtrack = cdplaytracknum;\n\t\t\t}\n#ifdef HAVE_JUKEBOX\n\t\t\tmedia_playlistcurrent = MEDIA_GAMEMUSIC;\n#endif\n\t\t\treturn NULL;\n\t\t}\n#endif\n\t\tif (*media_playtrack || *media_loopingtrack)\n\t\t{\n\t\t\tif (*media_playtrack)\n\t\t\t{\n\t\t\t\tQ_strncpyz(media_currenttrack, media_playtrack, sizeof(media_currenttrack));\n\t\t\t\t*media_playtrack = 0;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (!Media_CleanupTrackName(media_loopingtrack, NULL, media_currenttrack, sizeof(media_currenttrack)))\n\t\t\t\t\tQ_strncpyz(media_currenttrack, \"\", sizeof(media_currenttrack));\n\t\t\t}\n#ifdef HAVE_JUKEBOX\n\t\t\tQ_strncpyz(media_friendlyname, \"\", sizeof(media_friendlyname));\n\t\t\tmedia_playlistcurrent = MEDIA_GAMEMUSIC;\n#endif\n\t\t\ts = S_PrecacheSound(media_currenttrack);\n\t\t}\n\t}\n\treturn s;\n}\n\n//begin cross-fading\nstatic qboolean Media_Changed (unsigned int mediatype)\n{\n#ifdef HAVE_JUKEBOX\n\t//something changed, but it has a lower priority so we don't care\n\tif (mediatype < media_playlistcurrent)\n\t\treturn false;\n#endif\n\n#ifdef HAVE_CDPLAYER\n\t//make sure we're not playing any cd music.\n\tif (cdplayingtrack || cdpausedtrack)\n\t{\n\t\tcdplayingtrack = 0;\n\t\tcdpausedtrack = 0;\n\t\tCDAudio_Stop();\n\t}\n#endif\n\tmedia_fadeout = music_fade.ival;\n\tmedia_fadeouttime = realtime;\n\treturn true;\n}\n\n//returns the new volume the sample should be at, to support fading.\n//if we return < 0, the mixer will know to kill whatever is currently playing, ready for a new track.\n//this is on the main thread with the mixer locked, its safe to do stuff, but try not to block\nfloat Media_CrossFade(int musicchanel, float vol, float time)\n{\n\tif (media_fadeout)\n\t{\n\t\tfloat fadetime = 1;\n\t\tfloat frac = (fadetime + media_fadeouttime - realtime)/fadetime;\n\t\tvol *= frac;\n\t}\n#ifdef HAVE_JUKEBOX\n\telse if (music_playlist_index.modified)\n\t{\n\t\tif (Media_Changed(MEDIA_CVARLIST))\n\t\t{\n\t\t\tif (music_playlist_last >= 0)\n\t\t\t{\n\t\t\t\tcvar_t *sampleposition = Cvar_Get(va(\"music_playlist_sampleposition%i\", music_playlist_last), \"-1\", 0, \"compat\");\n\t\t\t\tif (sampleposition && sampleposition->value != -1)\n\t\t\t\t\tCvar_SetValue(sampleposition, time);\n\t\t\t}\n\t\t\tvol = -1;\t//kill it NOW\n\t\t}\n\t}\n#endif\n\treturn vol;\n}\n\nvoid Media_WriteCurrentTrack(sizebuf_t *buf)\n{\n\t//fixme: for demo playback\n\tMSG_WriteByte (buf, svc_cdtrack);\n\tMSG_WriteByte (buf, 0);\n}\n\nqboolean Media_CleanupTrackName(const char *track, int *out_track, char *result, size_t resultsize)\n{\n\t//FIXME: for q2, gog uses ../music/Track%02i.ogg, with various remapping requirements for the mission packs.\n\tstatic char *path[] =\n\t{\n\t\t\"music/\",\n\t\t\"sound/cdtracks/\",\n\t\t\"\",\n\t\tNULL\n\t};\n\tstatic char *ext[] =\n\t{\n\t\t\"\",\n#if defined(AVAIL_OGGOPUS) || defined(FTE_TARGET_WEB)\n\t\t\".opus\",\t//opus might be the future, but ogg is the present\n#endif\n#if defined(AVAIL_OGGVORBIS) || defined(FTE_TARGET_WEB)\n\t\t\".ogg\",\n#endif\n#if defined(AVAIL_MP3_ACM) || defined(FTE_TARGET_WEB)\n\t\t\".mp3\",\n#endif\n\t\t\".wav\",\n#if defined(PLUGINS)\t//ffmpeg plugin? woo.\n\t#if !(defined(AVAIL_OGGOPUS) || defined(FTE_TARGET_WEB))\n\t\t\".opus\",\t//opus might be the future, but ogg is the present\n\t#endif\n\t#if !(defined(AVAIL_OGGVORBIS) || defined(FTE_TARGET_WEB))\n\t\t\".ogg\",\n\t#endif\n\t#if !(defined(AVAIL_MP3_ACM) || defined(FTE_TARGET_WEB))\n\t\t\".mp3\",\n\t#endif\n\t\t\".flac\",\t//supported by QS at least.\n\t\t//\".s3m\",\t//some variant of mod that noone cares about. listed because of qs.\n\t\t//\".umx\",\t//wtf? qs is weird.\n#endif\n\t\tNULL\n\t};\n\tunsigned int tracknum;\n\tchar *trackend;\n\tunsigned int ip, ie;\n\tint bestdepth = 0x7fffffff, d;\n\tchar tryname[MAX_QPATH];\n\n\t//check if its a proper number (0123456789 without any other weird stuff. if so, we can use fake track paths or actual cd tracks)\n\ttracknum = strtoul(track, &trackend, 10);\n\tif (*trackend)\n\t\ttracknum = 0;\t//not a single integer\n#ifdef HAVE_CDPLAYER\n\tif (tracknum > 0 && tracknum < REMAPPED_TRACKS && *cdremap[tracknum].fname)\n\t{\t//remap the track if its remapped.\n\t\ttrack = cdremap[tracknum].fname;\n\t\ttracknum = strtoul(track, &trackend, 0);\n\t\tif (*trackend)\n\t\t\ttracknum = 0;\n\t}\n#endif\n\n#ifndef HAVE_LEGACY\n\tif (!tracknum)\t//might as well require exact file\n\t{\n\t\tQ_snprintfz(result, resultsize, \"%s\", track);\n\t\td = COM_FCheckExists(result);\n\t}\n\telse\n#endif\n\tfor(ip = 0; path[ip]; ip++)\n\t{\n\t\tif (tracknum)\n\t\t{\n\t\t\tif (*path[ip])\n\t\t\t{\n\t\t\t\tif (tracknum <= 999)\n\t\t\t\t{\n\t\t\t\t\tfor(ie = 0; ext[ie]; ie++)\n\t\t\t\t\t{\n\t\t\t\t\t\tQ_snprintfz(tryname, sizeof(tryname), \"%strack%03u%s\", path[ip], tracknum, ext[ie]);\n\t\t\t\t\t\td = COM_FDepthFile(tryname, false);\n\t\t\t\t\t\tif (d < bestdepth)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tbestdepth = d;\n\t\t\t\t\t\t\tQ_strncpy(result, tryname, resultsize);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (tracknum <= 99)\n\t\t\t\t{\n\t\t\t\t\tfor(ie = 0; ext[ie]; ie++)\n\t\t\t\t\t{\n\t\t\t\t\t\tQ_snprintfz(tryname, sizeof(tryname), \"%strack%02u%s\", path[ip], tracknum, ext[ie]);\n\t\t\t\t\t\td = COM_FDepthFile(tryname, false);\n\t\t\t\t\t\tif (d < bestdepth)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tbestdepth = d;\n\t\t\t\t\t\t\tQ_strncpy(result, tryname, resultsize);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tfor(ie = 0; ext[ie]; ie++)\n\t\t\t{\n\t\t\t\tQ_snprintfz(tryname, sizeof(tryname), \"%s%s%s\", path[ip], track, ext[ie]);\n\t\t\t\td = COM_FDepthFile(tryname, false);\n\t\t\t\tif (d < bestdepth)\n\t\t\t\t{\n\t\t\t\t\tbestdepth = d;\n\t\t\t\t\tQ_strncpy(result, tryname, resultsize);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tif (out_track)\n\t\t*out_track = tracknum;\n\tif (bestdepth < 0x7fffffff)\n\t\treturn true;\n\treturn false;\n}\n\n//controls which music track should be playing right now\n//track and looptrack will usually be the same thing, track is what to play NOW, looptrack is what to keep re-playing after, or \"-\" for stop.\nqboolean Media_NamedTrack(const char *track, const char *looptrack)\n{\n\tunsigned int tracknum;\n\tchar trackname[MAX_QPATH];\n\n\tif (!track && !looptrack)\n\t{\n\t\t*media_playtrack = *media_loopingtrack = 0;\n\t\tMedia_Changed(MEDIA_GAMEMUSIC);\n\t\treturn true;\n\t}\n\n\tif (!track || !*track)\t\t\t//ignore calls if the primary track is invalid. whatever is already playing will continue to play.\n\t\treturn false;\n\tif (!looptrack || !*looptrack)\t//null or empty looptrack loops using the primary track, for compat with q3.\n\t\tlooptrack = track;\n\n\tif (!strcmp(looptrack, \"-\"))\t//- for the looptrack argument can be used to prevent looping.\n\t\tlooptrack = \"\";\n\n\t//okay, do that faketrack thing if we got one.\n\tif (Media_CleanupTrackName(track, &tracknum, trackname, sizeof(trackname)))\n\t{\n#ifdef HAVE_CDPLAYER\n\t\tcdplaytracknum = 0;\n#endif\n\t\tQ_strncpyz(media_playtrack, trackname, sizeof(media_playtrack));\n\t\tQ_strncpyz(media_loopingtrack, looptrack, sizeof(media_loopingtrack));\n\t\tMedia_Changed(MEDIA_GAMEMUSIC);\n\t\treturn true;\n\t}\n\n#ifdef HAVE_CDPLAYER\n\t//couldn't do a faketrack, resort to actual cd tracks, if we're allowed\n\tif (tracknum && cdenabled)\n\t{\n\t\tQ_strncpyz(media_loopingtrack, looptrack, sizeof(media_loopingtrack));\n\n\t\tif (!CDAudio_Startup())\n\t\t\treturn false;\n\t\tif (cdnumtracks <= 0)\n\t\t\tcdnumtracks = CDAudio_GetAudioDiskInfo();\n\n\t\tif (tracknum > cdnumtracks)\n\t\t{\n\t\t\tCon_DPrintf(\"CDAudio: Bad track number %u.\\n\", tracknum);\n\t\t\treturn false;\t//can't play that, sorry. its not an available track\n\t\t}\n\n\t\tif (cdplayingtrack == tracknum)\n\t\t\treturn true;\t//already playing, don't need to do anything\n\n\t\tcdplaytracknum = tracknum;\n\t\tQ_strncpyz(media_playtrack, \"\", sizeof(media_playtrack));\n\t\tMedia_Changed(MEDIA_GAMEMUSIC);\n\t\treturn true;\n\t}\n#endif\n\treturn false;\n}\n\n//for q3\nvoid Media_NamedTrack_f(void)\n{\n\tif (Cmd_Argc() == 3)\n\t\tMedia_NamedTrack(Cmd_Argv(1), Cmd_Argv(2));\n\telse\n\t\tMedia_NamedTrack(Cmd_Argv(1), Cmd_Argv(1));\n}\nvoid Media_StopTrack_f(void)\n{\n\t*media_playtrack = *media_loopingtrack = 0;\n\tMedia_Changed(MEDIA_GAMEMUSIC);\n}\n\nvoid Media_NumberedTrack(unsigned int initialtrack, unsigned int looptrack)\n{\n\tchar *init = initialtrack?va(\"%u\", initialtrack):NULL;\n\tchar *loop = looptrack?va(\"%u\", looptrack):NULL;\n\n\tMedia_NamedTrack(init, loop);\n}\n\nvoid Media_EndedTrack(void)\n{\n#ifdef HAVE_CDPLAYER\n\tcdplayingtrack = 0;\n\tcdpausedtrack = 0;\n#endif\n\n\tif (*media_loopingtrack)\n\t\tMedia_NamedTrack(media_loopingtrack, media_loopingtrack);\n}\n\n\n\n\nvoid Media_SetPauseTrack(qboolean paused)\n{\n#ifdef HAVE_CDPLAYER\n\tif (paused)\n\t{\n\t\tif (!cdplayingtrack)\n\t\t\treturn;\n\t\tcdpausedtrack = cdplayingtrack;\n\t\tcdplayingtrack = 0;\n\t\tCDAudio_Pause();\n\t}\n\telse\n\t{\n\t\tif (!cdpausedtrack)\n\t\t\treturn;\n\t\tcdplayingtrack = cdpausedtrack;\n\t\tcdpausedtrack = 0;\n\t\tCDAudio_Resume();\n\t}\n#endif\n}\n\n#ifdef HAVE_CDPLAYER\nvoid Media_SaveTracks(vfsfile_t *outcfg)\n{\n\tunsigned int n;\n\tchar buf[MAX_QPATH*4];\n\tfor (n = 1; n < REMAPPED_TRACKS; n++)\n\t\tif (*cdremap[n].fname)\n\t\t\tCon_Printf(\"cd remap %u %s\\n\", n, COM_QuotedString(cdremap[n].fname, buf, sizeof(buf), false));\n}\nvoid CD_f (void)\n{\n\tchar\t*command;\n\tint\t\tret;\n\tint\t\tn;\n\n\tif (Cmd_Argc() < 2)\n\t\treturn;\n\n\tcommand = Cmd_Argv (1);\n\n\tif (Q_strcasecmp(command, \"play\") == 0)\n\t{\n\t\tMedia_NamedTrack(Cmd_Argv(2), \"-\");\n\t\treturn;\n\t}\n\n\tif (Q_strcasecmp(command, \"loop\") == 0)\n\t{\n\t\tif (Cmd_Argc() < 4)\n\t\t\tMedia_NamedTrack(Cmd_Argv(2), NULL);\n\t\telse\n\t\t\tMedia_NamedTrack(Cmd_Argv(2), Cmd_Argv(3));\n\t\treturn;\n\t}\n\n\tif (Q_strcasecmp(command, \"remap\") == 0)\n\t{\n\t\tret = Cmd_Argc() - 2;\n\t\tif (ret <= 0)\n\t\t{\n\t\t\tfor (n = 1; n < REMAPPED_TRACKS; n++)\n\t\t\t\tif (*cdremap[n].fname)\n\t\t\t\t\tCon_Printf(\"  %u -> %s\\n\", n, cdremap[n].fname);\n\t\t\treturn;\n\t\t}\n\t\tfor (n = 1; n <= ret && n < REMAPPED_TRACKS; n++)\n\t\t\tQ_strncpyz(cdremap[n].fname, Cmd_Argv (n+1), sizeof(cdremap[n].fname));\n\t\treturn;\n\t}\n\n\tif (Q_strcasecmp(command, \"stop\") == 0)\n\t{\n\t\t*media_playtrack = *media_loopingtrack = 0;\n\t\tcdplaytracknum = 0;\n\t\tMedia_Changed(MEDIA_GAMEMUSIC);\n\t\treturn;\n\t}\n\n\tif (!bgmvolume.value)\n\t{\n\t\tCon_Printf(\"Background music is disabled: %s is 0\\n\", bgmvolume.name);\n\t\treturn;\n\t}\n\n\tif (!CDAudio_Startup())\n\t{\n\t\tCon_Printf(\"No cd drive detected\\n\");\n\t\treturn;\n\t}\n\n\tif (Q_strcasecmp(command, \"on\") == 0)\n\t{\n\t\tcdenabled = true;\n\t\treturn;\n\t}\n\n\tif (Q_strcasecmp(command, \"off\") == 0)\n\t{\n\t\tif (cdplayingtrack || cdpausedtrack)\n\t\t\tCDAudio_Stop();\n\t\tcdenabled = false;\n\n\t\t*media_playtrack = *media_loopingtrack = 0;\n\t\tcdplaytracknum = 0;\n\t\tMedia_Changed(MEDIA_GAMEMUSIC);\n\t\treturn;\n\t}\n\n\tif (Q_strcasecmp(command, \"reset\") == 0)\n\t{\n\t\tcdenabled = true;\n\t\tif (cdplayingtrack || cdpausedtrack)\n\t\t\tCDAudio_Stop();\n\t\tfor (n = 0; n < REMAPPED_TRACKS; n++)\n\t\t\tstrcpy(cdremap[n].fname, \"\");\n\t\tcdnumtracks = CDAudio_GetAudioDiskInfo();\n\t\treturn;\n\t}\n\n\tif (Q_strcasecmp(command, \"close\") == 0)\n\t{\n\t\tCDAudio_CloseDoor();\n\t\treturn;\n\t}\n\n\tif (cdnumtracks <= 0)\n\t{\n\t\tcdnumtracks = CDAudio_GetAudioDiskInfo();\n\t\tif (cdnumtracks < 0)\n\t\t{\n\t\t\tCon_Printf(\"No CD in player.\\n\");\n\t\t\treturn;\n\t\t}\n\t}\n\n\tif (Q_strcasecmp(command, \"pause\") == 0)\n\t{\n\t\tMedia_SetPauseTrack(true);\n\t\treturn;\n\t}\n\n\tif (Q_strcasecmp(command, \"resume\") == 0)\n\t{\n\t\tMedia_SetPauseTrack(false);\n\t\treturn;\n\t}\n\n\tif (Q_strcasecmp(command, \"eject\") == 0)\n\t{\n\t\tif (Cmd_IsInsecure())\n\t\t\treturn;\n\n\t\tif (cdplayingtrack || cdpausedtrack)\n\t\t\tCDAudio_Stop();\n\t\tCDAudio_Eject();\n\t\tcdnumtracks = -1;\n\t\treturn;\n\t}\n\n\tif (Q_strcasecmp(command, \"info\") == 0)\n\t{\n\t\tCon_Printf(\"%u tracks\\n\", cdnumtracks);\n\t\tif (cdplayingtrack > 0)\n\t\t\tCon_Printf(\"Currently %s track %u\\n\", *media_loopingtrack ? \"looping\" : \"playing\", cdplayingtrack);\n\t\telse if (cdpausedtrack > 0)\n\t\t\tCon_Printf(\"Paused %s track %u\\n\", *media_loopingtrack ? \"looping\" : \"playing\", cdpausedtrack);\n\t\treturn;\n\t}\n}\n#endif\n\n\n\n\n#ifdef HAVE_JUKEBOX\n\n#ifdef WINAMP\n\n#include \"winamp.h\"\nHWND hwnd_winamp;\n\n#endif\n\nqboolean Media_EvaluateNextTrack(void);\n\n\n#ifdef WINAMP\nqboolean WinAmp_GetHandle (void)\n{\n\tif ((hwnd_winamp = FindWindowW(L\"Winamp\", NULL)))\n\t\treturn true;\n\tif ((hwnd_winamp = FindWindowW(L\"Winamp v1.x\", NULL)))\n\t\treturn true;\n\n\t*currenttrack.nicename = '\\0';\n\n\treturn false;\n}\n\nqboolean WinAmp_StartTune(char *name)\n{\n\tint trys;\n\tint pos;\n\tCOPYDATASTRUCT cds;\n\tif (!WinAmp_GetHandle())\n\t\treturn false;\n\n\t//FIXME: extract from fs if it's in a pack.\n\t//FIXME: always give absolute path\n\tcds.dwData = IPC_PLAYFILE;\n\tcds.lpData = (void *) name;\n\tcds.cbData = strlen((char *) cds.lpData)+1; // include space for null char\n\tSendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_DELETE);\n\tSendMessage(hwnd_winamp,WM_COPYDATA,(WPARAM)NULL,(LPARAM)&cds);\n\tSendMessage(hwnd_winamp,WM_WA_IPC,(WPARAM)0,IPC_STARTPLAY );\n\n\tfor (trys = 1000; trys; trys--)\n\t{\n\t\tpos = SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_GETOUTPUTTIME);\n\t\tif (pos>100 && SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_GETOUTPUTTIME)>=0)\t//tune has started\n\t\t\tbreak;\n\n\t\tSleep(10);\t//give it a chance.\n\t\tif (!WinAmp_GetHandle())\n\t\t\tbreak;\n\t}\n\n\treturn true;\n}\n\nvoid WinAmp_Think(void)\n{\n\tint pos;\n\tint len;\n\n\tif (!WinAmp_GetHandle())\n\t\treturn;\n\n\tpos = bgmvolume.value*255;\n\tif (pos > 255) pos = 255;\n\tif (pos < 0) pos = 0;\n\tPostMessage(hwnd_winamp, WM_WA_IPC,pos,IPC_SETVOLUME);\n\n//optimise this to reduce calls?\n\tpos = SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_GETOUTPUTTIME);\n\tlen = SendMessage(hwnd_winamp,WM_WA_IPC,1,IPC_GETOUTPUTTIME)*1000;\n\n\tif ((pos > len || pos <= 100) && len != -1)\n\tif (Media_EvaluateNextTrack())\n\t\tWinAmp_StartTune(currenttrack.filename);\n}\n#endif\nvoid Media_Seek (float time)\n{\n#ifdef WINAMP\n\tif (media_hijackwinamp.value)\n\t{\n\t\tint pos;\n\t\tif (WinAmp_GetHandle())\n\t\t{\n\t\t\tpos = SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_GETOUTPUTTIME);\n\t\t\tpos += time*1000;\n\t\t\tPostMessage(hwnd_winamp,WM_WA_IPC,pos,IPC_JUMPTOTIME);\n\n\t\t\tWinAmp_Think();\n\t\t}\n\t}\n#endif\n\tS_Music_Seek(time);\n}\n\nvoid Media_FForward_f(void)\n{\n\tfloat time = atoi(Cmd_Argv(1));\n\tif (!time)\n\t\ttime = 15;\n\tMedia_Seek(time);\n}\nvoid Media_Rewind_f (void)\n{\n\tfloat time = atoi(Cmd_Argv(1));\n\tif (!time)\n\t\ttime = 15;\n\tMedia_Seek(-time);\n}\n\nqboolean Media_EvaluateNextTrack(void)\n{\n\tmediatrack_t *track;\n\tint trnum;\n\tif (!tracks)\n\t\treturn false;\n\tif (nexttrack>=0)\n\t{\n\t\ttrnum = nexttrack;\n\t\tfor (track = tracks; track; track=track->next)\n\t\t{\n\t\t\tif (!trnum)\n\t\t\t{\n\t\t\t\tQ_strncpyz(media_currenttrack, track->filename, sizeof(media_currenttrack));\n\t\t\t\tQ_strncpyz(media_friendlyname, track->nicename, sizeof(media_friendlyname));\n\t\t\t\tlasttrackplayed = nexttrack;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\ttrnum--;\n\t\t}\n\t\tnexttrack = -1;\n\t}\n\telse\n\t{\n\t\tif (media_shuffle.value)\n\t\t\tnexttrack=((float)(rand()&0x7fff)/0x7fff)*numtracks;\n\t\telse\n\t\t{\n\t\t\tnexttrack = lasttrackplayed+1;\n\t\t\tif (nexttrack >= numtracks)\n\t\t\t{\n\t\t\t\tif (media_repeat.value)\n\t\t\t\t\tnexttrack = 0;\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t*media_currenttrack='\\0';\n\t\t\t\t\t*media_friendlyname='\\0';\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\ttrnum = nexttrack;\n\t\tfor (track = tracks; track; track=track->next)\n\t\t{\n\t\t\tif (!trnum)\n\t\t\t{\n\t\t\t\tQ_strncpyz(media_currenttrack, track->filename, sizeof(media_currenttrack));\n\t\t\t\tQ_strncpyz(media_friendlyname, track->nicename, sizeof(media_friendlyname));\n\t\t\t\tlasttrackplayed = nexttrack;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\ttrnum--;\n\t\t}\n\t\tnexttrack = -1;\n\t}\n\n\treturn true;\n}\n\n//actually, this func just flushes and states that it should be playing. the ambientsound func actually changes the track.\nvoid Media_Next_f (void)\n{\n\tMedia_Changed(MEDIA_PLAYLIST);\n\n#ifdef WINAMP\n\tif (media_hijackwinamp.value)\n\t{\n\t\tif (WinAmp_GetHandle())\n\t\tif (Media_EvaluateNextTrack())\n\t\t\tWinAmp_StartTune(currenttrack.filename);\n\t}\n#endif\n}\n\n\n\n\n\nvoid Media_AddTrack(const char *fname)\n{\n\tmediatrack_t *newtrack;\n\tif (!*fname)\n\t\treturn;\n\tfor (newtrack = tracks; newtrack; newtrack = newtrack->next)\n\t{\n\t\tif (!strcmp(newtrack->filename, fname))\n\t\t\treturn;\t//already added. ho hum\n\t}\n\tnewtrack = Z_Malloc(sizeof(mediatrack_t));\n\tQ_strncpyz(newtrack->filename, fname, sizeof(newtrack->filename));\n\tQ_strncpyz(newtrack->nicename, COM_SkipPath(fname), sizeof(newtrack->nicename));\n\tnewtrack->length = 0;\n\tnewtrack->next = tracks;\n\ttracks = newtrack;\n\tnumtracks++;\n\n\tif (numtracks == 1)\n\t\tMedia_Changed(MEDIA_PLAYLIST);\n}\nvoid Media_RemoveTrack(const char *fname)\n{\n\tmediatrack_t **link, *newtrack;\n\tif (!*fname)\n\t\treturn;\n\tfor (link = &tracks; *link; link = &(*link)->next)\n\t{\n\t\tnewtrack = *link;\n\t\tif (!strcmp(newtrack->filename, fname))\n\t\t{\n\t\t\t*link = newtrack->next;\n\t\t\tnumtracks--;\n\n\t\t\tif (!strcmp(media_currenttrack, newtrack->filename))\n\t\t\t\tMedia_Changed(MEDIA_PLAYLIST);\n\t\t\tZ_Free(newtrack);\n\t\t\treturn;\n\t\t}\n\t}\n}\n\n#if !defined(NOMEDIAMENU) && !defined(NOBUILTINMENUS)\nvoid M_Media_Add_f (void)\n{\n\tchar *fname = Cmd_Argv(1);\n\n\tif (!loadedtracknames)\n\t\tMedia_LoadTrackNames(\"sound/media.m3u\");\n\n\tif (Cmd_Argc() == 1)\n\t\tCon_Printf(\"%s <track>\\n\", Cmd_Argv(0));\n\telse\n\t\tMedia_AddTrack(fname);\n\n\tMedia_SaveTrackNames(\"sound/media.m3u\");\n}\nvoid M_Media_Remove_f (void)\n{\n\tchar *fname = Cmd_Argv(1);\n\n\tif (Cmd_Argc() == 1)\n\t\tCon_Printf(\"%s <track>\\n\", Cmd_Argv(0));\n\telse\n\t\tMedia_RemoveTrack(fname);\n\n\tMedia_SaveTrackNames(\"sound/media.m3u\");\n}\n\n\nvoid Media_LoadTrackNames (char *listname);\n\n#define MEDIA_MIN\t-7\n#define MEDIA_VOLUME -7\n#define MEDIA_FASTFORWARD -6\n#define MEDIA_CLEARLIST -5\n#define MEDIA_ADDTRACK -4\n#define MEDIA_ADDLIST -3\n#define MEDIA_SHUFFLE -2\n#define MEDIA_REPEAT -1\n\nvoid M_Media_Draw (emenu_t *menu)\n{\n\tmediatrack_t *track;\n\tint y;\n\tint op, i, mop;\n\tqboolean playing;\n\n\tfloat time, duration;\n\tchar title[256];\n\tplaying = S_GetMusicInfo(0, &time, &duration, title, sizeof(title));\n\n#define MP_Hightlight(x,y,text,hl) (hl?M_PrintWhite(x, y, text):M_Print(x, y, text))\n\n\tif (!bgmvolume.value)\n\t\tM_Print (12, 32, \"Not playing - no volume\");\n\telse if (!*media_currenttrack)\n\t{\n\t\tif (!tracks)\n\t\t\tM_Print (12, 32, \"Not playing - no track to play\");\n\t\telse\n\t\t{\n#ifdef WINAMP\n\t\t\tif (!WinAmp_GetHandle())\n\t\t\t\tM_Print (12, 32, \"Please start WinAmp 2\");\n\t\t\telse\n#endif\n\t\t\t\tM_Print (12, 32, \"Not playing - switched off\");\n\t\t}\n\t}\n\telse\n\t{\n\t\tM_Print (12, 32, \"Currently playing:\");\n\t\tif (*title)\n\t\t{\n\t\t\tM_Print (12, 40, title);\n\t\t\tM_Print (12, 48, media_currenttrack);\n\t\t}\n\t\telse\n\t\t\tM_Print (12, 40, *media_friendlyname?media_friendlyname:media_currenttrack);\n\t}\n\n\ty=60;\n\top = selectedoption - (vid.height-y)/16;\n\tif (op + (vid.height-y)/8>numtracks)\n\t\top = numtracks - (vid.height-y)/8;\n\tif (op < MEDIA_MIN)\n\t\top = MEDIA_MIN;\n\tmop = (mousecursor_y - y)/8;\n\tmop += MEDIA_MIN;\n\tmouseselectedoption = MEDIA_MIN-1;\n\tif (mousecursor_x < 12 + ((vid.width - 320)>>1) || mousecursor_x > 320-24 + ((vid.width - 320)>>1))\n\t\tmop = mouseselectedoption;\n\twhile(op < 0)\n\t{\n\t\tif (op == mop)\n\t\t{\n\t\t\tfloat alphamax = 0.5, alphamin = 0.2;\n\t\t\tmouseselectedoption = op;\n\t\t\tR2D_ImageColours(.5,.4,0,(sin(realtime*2)+1)*0.5*(alphamax-alphamin)+alphamin);\n\t\t\tR2D_FillBlock(12 + ((vid.width - 320)>>1), y, 320-24, 8);\n\t\t\tR2D_ImageColours(1.0, 1.0, 1.0, 1.0);\n\t\t}\n\t\tswitch(op)\n\t\t{\n\t\tcase MEDIA_VOLUME:\n\t\t\tMP_Hightlight (12, y, va(\"<< Volume %2i%%    >>\", (int)(100*bgmvolume.value)), op == selectedoption);\n\t\t\ty+=8;\n\t\t\tbreak;\n\t\tcase MEDIA_CLEARLIST:\n\t\t\tMP_Hightlight (12, y, \"Clear all\", op == selectedoption);\n\t\t\ty+=8;\n\t\t\tbreak;\n\t\tcase MEDIA_FASTFORWARD:\n\t\t\t{\n\t\t\t\tif (playing)\n\t\t\t\t{\n\t\t\t\t\tint itime = time;\n\t\t\t\t\tint iduration = duration;\n\t\t\t\t\tif (iduration)\n\t\t\t\t\t{\n\t\t\t\t\t\tint pct = (int)((100*time)/duration);\n\t\t\t\t\t\tMP_Hightlight (12, y, va(\"<< %i:%02i / %i:%02i - %i%% >>\", itime/60, itime%60, iduration/60, iduration%60, pct), op == selectedoption);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\tMP_Hightlight (12, y, va(\"<< %i:%02i >>\", itime/60, itime%60), op == selectedoption);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tMP_Hightlight (12, y, \"<<    skip\t\t >>\", op == selectedoption);\n\t\t\t}\n\t\t\ty+=8;\n\t\t\tbreak;\n\t\tcase MEDIA_ADDTRACK:\n\t\t\tMP_Hightlight (12, y, \"Add Track\", op == selectedoption);\n\t\t\tif (op == selectedoption)\n\t\t\t\tM_PrintWhite (12+9*8, y, media_iofilename);\n\t\t\ty+=8;\n\t\t\tbreak;\n\t\tcase MEDIA_ADDLIST:\n\t\t\tMP_Hightlight (12, y, \"Add List\", op == selectedoption);\n\t\t\tif (op == selectedoption)\n\t\t\t\tM_PrintWhite (12+9*8, y, media_iofilename);\n\t\t\ty+=8;\n\t\t\tbreak;\n\t\tcase MEDIA_SHUFFLE:\n\t\t\tif (media_shuffle.value)\n\t\t\t\tMP_Hightlight (12, y, \"Shuffle on\", op == selectedoption);\n\t\t\telse\n\t\t\t\tMP_Hightlight (12, y, \"Shuffle off\", op == selectedoption);\n\t\t\ty+=8;\n\t\t\tbreak;\n\t\tcase MEDIA_REPEAT:\n\t\t\tif (!media_shuffle.value)\n\t\t\t{\n\t\t\t\tif (media_repeat.value)\n\t\t\t\t\tMP_Hightlight (12, y, \"Repeat on\", op == selectedoption);\n\t\t\t\telse\n\t\t\t\t\tMP_Hightlight (12, y, \"Repeat off\", op == selectedoption);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (media_repeat.value)\n\t\t\t\t\tMP_Hightlight (12, y, \"(Repeat on)\", op == selectedoption);\n\t\t\t\telse\n\t\t\t\t\tMP_Hightlight (12, y, \"(Repeat off)\", op == selectedoption);\n\t\t\t}\n\t\t\ty+=8;\n\t\t\tbreak;\n\t\t}\n\t\top++;\n\t}\n\n\tfor (track = tracks, i=0; track && i<op; track=track->next, i++);\n\tfor (; track; track=track->next, y+=8, op++)\n\t{\n\t\tif (track->length != (int)duration && *title && !strcmp(track->filename, media_currenttrack))\n\t\t{\n\t\t\tQ_strncpyz(track->nicename, title, sizeof(track->nicename));\n\t\t\ttrack->length = duration;\n\t\t\tMedia_SaveTrackNames(\"sound/media.m3u\");\n\t\t}\n\n\t\tif (op == mop)\n\t\t{\n\t\t\tfloat alphamax = 0.5, alphamin = 0.2;\n\t\t\tmouseselectedoption = op;\n\t\t\tR2D_ImageColours(.5,.4,0,(sin(realtime*2)+1)*0.5*(alphamax-alphamin)+alphamin);\n\t\t\tR2D_FillBlock(12 + ((vid.width - 320)>>1), y, 320-24, 8);\n\t\t\tR2D_ImageColours(1.0, 1.0, 1.0, 1.0);\n\t\t}\n\t\tif (op == selectedoption)\n\t\t\tM_PrintWhite (12, y, track->nicename);\n\t\telse\n\t\t\tM_Print (12, y, track->nicename);\n\t}\n}\n\nchar compleatenamepath[MAX_OSPATH];\nchar compleatenamename[MAX_OSPATH];\nqboolean compleatenamemultiple;\nint QDECL Com_CompleatenameCallback(const char *name, qofs_t size, time_t mtime, void *data, searchpathfuncs_t *spath)\n{\n\tif (*compleatenamename)\n\t\tcompleatenamemultiple = true;\n\tQ_strncpyz(compleatenamename, name, sizeof(compleatenamename));\n\n\treturn true;\n}\nvoid Com_CompleateOSFileName(char *name)\n{\n\tchar *ending;\n\tcompleatenamemultiple = false;\n\n\tstrcpy(compleatenamepath, name);\n\tending = COM_SkipPath(compleatenamepath);\n\tif (compleatenamepath!=ending)\n\t\tending[-1] = '\\0';\t//strip a slash\n\t*compleatenamename='\\0';\n\n\tSys_EnumerateFiles(NULL, va(\"%s*\", name), Com_CompleatenameCallback, NULL, NULL);\n\tSys_EnumerateFiles(NULL, va(\"%s*.*\", name), Com_CompleatenameCallback, NULL, NULL);\n\n\tif (*compleatenamename)\n\t\tstrcpy(name, compleatenamename);\n}\n\nqboolean M_Media_Key (emenu_t *menu, int key, unsigned int unicode)\n{\n\tint dir;\n\tif (key == K_ESCAPE || key == K_GP_BACK || key == K_MOUSE2)\n\t{\n\t\treturn false;\n\t}\n\telse if (key == K_RIGHTARROW || key == K_KP_RIGHTARROW || key == K_GP_DPAD_RIGHT || key == K_LEFTARROW || key == K_KP_LEFTARROW || key == K_GP_DPAD_LEFT)\n\t{\n\t\tif (key == K_RIGHTARROW || key == K_KP_RIGHTARROW || key == K_GP_DPAD_RIGHT)\n\t\t\tdir = 1;\n\t\telse dir = -1;\n\t\tswitch(selectedoption)\n\t\t{\n\t\tcase MEDIA_VOLUME:\n\t\t\tbgmvolume.value += dir * 0.1;\n\t\t\tif (bgmvolume.value < 0)\n\t\t\t\tbgmvolume.value = 0;\n\t\t\tif (bgmvolume.value > 1)\n\t\t\t\tbgmvolume.value = 1;\n\t\t\tCvar_SetValue (&bgmvolume, bgmvolume.value);\n\t\t\tbreak;\n\t\tcase MEDIA_FASTFORWARD:\n\t\t\tMedia_Seek(15*dir);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tif (selectedoption >= 0)\n\t\t\t\tMedia_Next_f();\n\t\t\tbreak;\n\t\t}\n\t}\n\telse if (key == K_DOWNARROW || key == K_KP_DOWNARROW || key == K_GP_DPAD_DOWN)\n\t{\n\t\tselectedoption++;\n\t\tif (selectedoption>=numtracks)\n\t\t\tselectedoption = numtracks-1;\n\t}\n\telse if (key == K_PGDN)\n\t{\n\t\tselectedoption+=10;\n\t\tif (selectedoption>=numtracks)\n\t\t\tselectedoption = numtracks-1;\n\t}\n\telse if (key == K_UPARROW || key == K_KP_UPARROW || key == K_GP_DPAD_UP)\n\t{\n\t\tselectedoption--;\n\t\tif (selectedoption < MEDIA_MIN)\n\t\t\tselectedoption = MEDIA_MIN;\n\t}\n\telse if (key == K_PGUP)\n\t{\n\t\tselectedoption-=10;\n\t\tif (selectedoption < MEDIA_MIN)\n\t\t\tselectedoption = MEDIA_MIN;\n\t}\n\telse if (key == K_DEL|| key == K_GP_DIAMOND_ALTCONFIRM)\n\t{\n\t\tif (selectedoption>=0)\n\t\t{\n\t\t\tmediatrack_t *prevtrack=NULL, *tr;\n\t\t\tint num=0;\n\t\t\ttr=tracks;\n\t\t\twhile(tr)\n\t\t\t{\n\t\t\t\tif (num == selectedoption)\n\t\t\t\t{\n\t\t\t\t\tif (prevtrack)\n\t\t\t\t\t\tprevtrack->next = tr->next;\n\t\t\t\t\telse\n\t\t\t\t\t\ttracks = tr->next;\n\t\t\t\t\tnumtracks--;\n\t\t\t\t\tif (!strcmp(media_currenttrack, tr->filename))\n\t\t\t\t\t\tMedia_Changed(MEDIA_PLAYLIST);\n\t\t\t\t\tZ_Free(tr);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tprevtrack = tr;\n\t\t\t\ttr=tr->next;\n\t\t\t\tnum++;\n\t\t\t}\n\t\t}\n\t}\n\telse if (key == K_ENTER || key == K_KP_ENTER || key == K_GP_DIAMOND_CONFIRM || key == K_MOUSE1 || key == K_TOUCHTAP)\n\t{\n\t\tif (key == K_MOUSE1)\n\t\t{\n\t\t\tif (mouseselectedoption < MEDIA_MIN)\n\t\t\t\treturn false;\n\t\t\tselectedoption = mouseselectedoption;\n\t\t}\n\t\tswitch(selectedoption)\n\t\t{\n\t\tcase MEDIA_FASTFORWARD:\n\t\t\tMedia_Seek(15);\n\t\t\tbreak;\n\t\tcase MEDIA_CLEARLIST:\n\t\t\t{\n\t\t\t\tmediatrack_t *prevtrack;\n\t\t\t\twhile(tracks)\n\t\t\t\t{\n\t\t\t\t\tprevtrack = tracks;\n\t\t\t\t\ttracks=tracks->next;\n\t\t\t\t\tZ_Free(prevtrack);\n\t\t\t\t\tnumtracks--;\n\t\t\t\t}\n\t\t\t\tif (numtracks!=0)\n\t\t\t\t{\n\t\t\t\t\tnumtracks=0;\n\t\t\t\t\tCon_SafePrintf(\"numtracks should be 0\\n\");\n\t\t\t\t}\n\t\t\t\tMedia_Changed(MEDIA_PLAYLIST);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase MEDIA_ADDTRACK:\n\t\t\tif (*media_iofilename)\n\t\t\t\tMedia_AddTrack(media_iofilename);\n\t\t\telse\n\t\t\t\tCmd_ExecuteString(\"menu_mediafiles\", RESTRICT_LOCAL);\n\t\t\tbreak;\n\t\tcase MEDIA_ADDLIST:\n\t\t\tif (*media_iofilename)\n\t\t\t\tMedia_LoadTrackNames(media_iofilename);\n\t\t\tbreak;\n\t\tcase MEDIA_SHUFFLE:\n\t\t\tCvar_Set(&media_shuffle, media_shuffle.value?\"0\":\"1\");\n\t\t\tbreak;\n\t\tcase MEDIA_REPEAT:\n\t\t\tCvar_Set(&media_repeat, media_repeat.value?\"0\":\"1\");\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tif (selectedoption>=0)\n\t\t\t{\n\t\t\t\tnexttrack = selectedoption;\n\t\t\t\tMedia_Next_f();\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\t\treturn true;\n\t}\n\telse\n\t{\n\t\tif (selectedoption == MEDIA_ADDLIST || selectedoption == MEDIA_ADDTRACK)\n\t\t{\n\t\t\tif (key == K_TAB)\n\t\t\t{\n\t\t\t\tCom_CompleateOSFileName(media_iofilename);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\telse if (key == K_BACKSPACE)\n\t\t\t{\n\t\t\t\tdir = strlen(media_iofilename);\n\t\t\t\tif (dir)\n\t\t\t\t\tmedia_iofilename[dir-1] = '\\0';\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\telse if ((key >= 'a' && key <= 'z') || (key >= '0' && key <= '9') || key == '/' || key == '_' || key == '.' || key == ':')\n\t\t\t{\n\t\t\t\tdir = strlen(media_iofilename);\n\t\t\t\tmedia_iofilename[dir] = key;\n\t\t\t\tmedia_iofilename[dir+1] = '\\0';\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\telse if (selectedoption>=0)\n\t\t{\n\t\t\tmediatrack_t *tr;\n\t\t\tint num=0;\n\t\t\ttr=tracks;\n\t\t\twhile(tr)\n\t\t\t{\n\t\t\t\tif (num == selectedoption)\n\t\t\t\t\tbreak;\n\n\t\t\t\ttr=tr->next;\n\t\t\t\tnum++;\n\t\t\t}\n\t\t\tif (!tr)\n\t\t\t\treturn false;\n\n\t\t\tif (key == K_BACKSPACE)\n\t\t\t{\n\t\t\t\tdir = strlen(tr->nicename);\n\t\t\t\tif (dir)\n\t\t\t\t\ttr->nicename[dir-1] = '\\0';\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\telse if ((key >= 'a' && key <= 'z') || (key >= '0' && key <= '9') || key == '/' || key == '_' || key == '.' || key == ':'  || key == '&' || key == '|' || key == '#' || key == '\\'' || key == '\\\"' || key == '\\\\' || key == '*' || key == '@' || key == '!' || key == '(' || key == ')' || key == '%' || key == '^' || key == '?' || key == '[' || key == ']' || key == ';' || key == ':' || key == '+' || key == '-' || key == '=')\n\t\t\t{\n\t\t\t\tdir = strlen(tr->nicename);\n\t\t\t\ttr->nicename[dir] = key;\n\t\t\t\ttr->nicename[dir+1] = '\\0';\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t}\n\treturn false;\n}\n\nvoid M_Menu_Media_f (void)\n{\n\temenu_t *menu;\n\tif (!loadedtracknames)\n\t\tMedia_LoadTrackNames(\"sound/media.m3u\");\n\n\tmenu = M_CreateMenu(0);\n\n//\tMC_AddPicture(menu, 16, 4, 32, 144, \"gfx/qplaque.lmp\");\n\tMC_AddCenterPicture(menu, 4, 24, \"gfx/p_option.lmp\");\n\n\tmenu->key = M_Media_Key;\n\tmenu->postdraw = M_Media_Draw;\n}\n\n\n\nvoid Media_SaveTrackNames (char *listname)\n{\n\tmediatrack_t *tr;\n\tvfsfile_t *f = FS_OpenVFS(listname, \"wb\", FS_GAMEONLY);\n\tif (!f)\n\t\treturn;\n\tVFS_PRINTF(f, \"#EXTM3U\\n\");\n\tfor (tr = tracks; tr; tr = tr->next)\n\t{\n\t\tVFS_PRINTF(f, \"#EXTINF:%i,%s\\n%s\\n\", tr->length, tr->nicename, tr->filename);\n\t}\n\tVFS_CLOSE(f);\n}\n\n//safeprints only.\nvoid Media_LoadTrackNames (char *listname)\n{\n\tchar *lineend;\n\tchar *len;\n\tchar *filename;\n\tchar *trackname;\n\tmediatrack_t *newtrack;\n\tsize_t fsize;\n\tchar *data = COM_LoadTempFile(listname, FSLF_IGNOREPURE, &fsize);\n\n\tloadedtracknames=true;\n\n\tif (!data)\n\t\treturn;\n\n\tif (!Q_strncasecmp(data, \"#extm3u\", 7))\n\t{\n\t\tdata = strchr(data, '\\n')+1;\n\t\tfor(;;)\n\t\t{\n\t\t\tlineend = strchr(data, '\\n');\n\n\t\t\tif (Q_strncasecmp(data, \"#extinf:\", 8))\n\t\t\t{\n\t\t\t\tif (!lineend)\n\t\t\t\t\treturn;\n\t\t\t\tCon_SafePrintf(\"Bad m3u file\\n\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tlen = data+8;\n\t\t\ttrackname = strchr(data, ',')+1;\n\t\t\tif (!trackname)\n\t\t\t\treturn;\n\n\t\t\tif (lineend > data && lineend[-1] == '\\r')\n\t\t\t\tlineend[-1]='\\0';\n\t\t\telse\n\t\t\t\tlineend[0]='\\0';\n\n\t\t\tfilename = data = lineend+1;\n\n\t\t\tlineend = strchr(data, '\\n');\n\n\t\t\tif (lineend)\n\t\t\t{\n\t\t\t\tif (lineend > data && lineend[-1] == '\\r')\n\t\t\t\t\tlineend[-1]='\\0';\n\t\t\t\telse\n\t\t\t\t\tlineend[0]='\\0';\n\t\t\t\tdata = lineend+1;\n\t\t\t}\n\n\t\t\tnewtrack = Z_Malloc(sizeof(mediatrack_t));\n\t\t\tQ_strncpyz(newtrack->filename, filename, sizeof(newtrack->filename));\n\t\t\tQ_strncpyz(newtrack->nicename, trackname, sizeof(newtrack->nicename));\n\t\t\tnewtrack->length = atoi(len);\n\t\t\tnewtrack->next = tracks;\n\t\t\ttracks = newtrack;\n\t\t\tnumtracks++;\n\n\t\t\tif (!lineend)\n\t\t\t\treturn;\n\t\t}\n\t}\n\telse\n\t{\n\t\tfor(;;)\n\t\t{\n\t\t\ttrackname = filename = data;\n\t\t\tlineend = strchr(data, '\\n');\n\n\t\t\tif (!lineend && !*data)\n\t\t\t\tbreak;\n\t\t\tlineend[-1]='\\0';\n\t\t\tdata = lineend+1;\n\n\t\t\tnewtrack = Z_Malloc(sizeof(mediatrack_t));\n\t\t\tQ_strncpyz(newtrack->filename, filename, sizeof(newtrack->filename));\n\t\t\tQ_strncpyz(newtrack->nicename, COM_SkipPath(trackname), sizeof(newtrack->nicename));\n\t\t\tnewtrack->length = 0;\n\t\t\tnewtrack->next = tracks;\n\t\t\ttracks = newtrack;\n\t\t\tnumtracks++;\n\n\t\t\tif (!lineend)\n\t\t\t\tbreak;\n\t\t}\n\t}\n}\n#endif\n\n#endif\n\n\n\n\n\n///temporary residence for media handling\n\n#ifdef HAVE_API_VFW\n#if 0\n#include <vfw.h>\n#else\ntypedef struct \n{\n\tDWORD fccType;\n\tDWORD fccHandler;\n\tDWORD dwFlags;\n\tDWORD dwCaps;\n\tWORD  wPriority;\n\tWORD  wLanguage;\n\tDWORD dwScale;\n\tDWORD dwRate;\n\tDWORD dwStart;\n\tDWORD dwLength;\n\tDWORD dwInitialFrames;\n\tDWORD dwSuggestedBufferSize;\n\tDWORD dwQuality;\n\tDWORD dwSampleSize;\n\tRECT  rcFrame;\n\tDWORD dwEditCount;\n\tDWORD dwFormatChangeCount;\n\tTCHAR szName[64];\n} AVISTREAMINFOA, *LPAVISTREAMINFOA;\ntypedef struct AVISTREAM *PAVISTREAM;\ntypedef struct AVIFILE *PAVIFILE;\ntypedef struct GETFRAME *PGETFRAME;\ntypedef struct\t \n{\n\tDWORD  fccType;\n\tDWORD  fccHandler;\n\tDWORD  dwKeyFrameEvery;\n\tDWORD  dwQuality;\n\tDWORD  dwBytesPerSecond;\n\tDWORD  dwFlags;\n\tLPVOID lpFormat;\n\tDWORD  cbFormat;\n\tLPVOID lpParms;\n\tDWORD  cbParms;\n\tDWORD  dwInterleaveEvery;\n} AVICOMPRESSOPTIONS;\n#define streamtypeVIDEO         mmioFOURCC('v', 'i', 'd', 's')\n#define streamtypeAUDIO         mmioFOURCC('a', 'u', 'd', 's')\n#define AVISTREAMREAD_CONVENIENT\t(-1L)\n#define AVIIF_KEYFRAME\t0x00000010L\n#endif\n\nULONG\t(WINAPI *qAVIStreamRelease)\t\t\t(PAVISTREAM pavi);\nHRESULT\t(WINAPI *qAVIStreamEndStreaming)\t(PAVISTREAM pavi);\nHRESULT\t(WINAPI *qAVIStreamGetFrameClose)\t(PGETFRAME pg);\nHRESULT\t(WINAPI *qAVIStreamRead)\t\t\t(PAVISTREAM pavi, LONG lStart, LONG lSamples, LPVOID lpBuffer, LONG cbBuffer, LONG FAR * plBytes, LONG FAR * plSamples);\nLPVOID\t(WINAPI *qAVIStreamGetFrame)\t\t(PGETFRAME pg, LONG lPos);\nHRESULT\t(WINAPI *qAVIStreamReadFormat)\t\t(PAVISTREAM pavi, LONG lPos,LPVOID lpFormat,LONG FAR *lpcbFormat);\nLONG\t(WINAPI *qAVIStreamStart)\t\t\t(PAVISTREAM pavi);\nPGETFRAME(WINAPI*qAVIStreamGetFrameOpen)\t(PAVISTREAM pavi, LPBITMAPINFOHEADER lpbiWanted);\nHRESULT\t(WINAPI *qAVIStreamBeginStreaming)\t(PAVISTREAM pavi, LONG lStart, LONG lEnd, LONG lRate);\nLONG\t(WINAPI *qAVIStreamSampleToTime)\t(PAVISTREAM pavi, LONG lSample);\nLONG\t(WINAPI *qAVIStreamLength)\t\t\t(PAVISTREAM pavi);\nHRESULT\t(WINAPI *qAVIStreamInfoA)\t\t\t(PAVISTREAM pavi, LPAVISTREAMINFOA psi, LONG lSize);\nULONG\t(WINAPI *qAVIFileRelease)\t\t\t(PAVIFILE pfile);\nHRESULT\t(WINAPI *qAVIFileGetStream)\t\t\t(PAVIFILE pfile, PAVISTREAM FAR * ppavi, DWORD fccType, LONG lParam);\nHRESULT\t(WINAPI *qAVIFileOpenA)\t\t\t\t(PAVIFILE FAR *ppfile, LPCSTR szFile, UINT uMode, LPCLSID lpHandler);\nvoid\t(WINAPI *qAVIFileInit)\t\t\t\t(void);\nHRESULT\t(WINAPI *qAVIStreamWrite)\t\t\t(PAVISTREAM pavi, LONG lStart, LONG lSamples, LPVOID lpBuffer, LONG cbBuffer, DWORD dwFlags, LONG FAR *plSampWritten, LONG FAR *plBytesWritten);\nHRESULT\t(WINAPI *qAVIStreamSetFormat)\t\t(PAVISTREAM pavi, LONG lPos,LPVOID lpFormat,LONG cbFormat);\nHRESULT\t(WINAPI *qAVIMakeCompressedStream)\t(PAVISTREAM FAR * ppsCompressed, PAVISTREAM ppsSource, AVICOMPRESSOPTIONS FAR * lpOptions, CLSID FAR *pclsidHandler);\nHRESULT\t(WINAPI *qAVIFileCreateStreamA)\t\t(PAVIFILE pfile, PAVISTREAM FAR *ppavi, AVISTREAMINFOA FAR * psi);\n\nstatic qboolean qAVIStartup(void)\n{\n\tstatic int aviinited;\n\tstatic dllhandle_t *avimodule;\n\tif (!aviinited)\n\t{\n\t\tdllfunction_t funcs[] =\n\t\t{\n\t\t\t{(void*)&qAVIFileInit,\t\t\t\t\"AVIFileInit\"},\n\t\t\t{(void*)&qAVIStreamRelease,\t\t\t\"AVIStreamRelease\"},\n\t\t\t{(void*)&qAVIStreamEndStreaming,\t\"AVIStreamEndStreaming\"},\n\t\t\t{(void*)&qAVIStreamGetFrameClose,\t\"AVIStreamGetFrameClose\"},\n\t\t\t{(void*)&qAVIStreamRead,\t\t\t\"AVIStreamRead\"},\n\t\t\t{(void*)&qAVIStreamGetFrame,\t\t\"AVIStreamGetFrame\"},\n\t\t\t{(void*)&qAVIStreamReadFormat,\t\t\"AVIStreamReadFormat\"},\n\t\t\t{(void*)&qAVIStreamStart,\t\t\t\"AVIStreamStart\"},\n\t\t\t{(void*)&qAVIStreamGetFrameOpen,\t\"AVIStreamGetFrameOpen\"},\n\t\t\t{(void*)&qAVIStreamBeginStreaming,\t\"AVIStreamBeginStreaming\"},\n\t\t\t{(void*)&qAVIStreamSampleToTime,\t\"AVIStreamSampleToTime\"},\n\t\t\t{(void*)&qAVIStreamLength,\t\t\t\"AVIStreamLength\"},\n\t\t\t{(void*)&qAVIStreamInfoA,\t\t\t\"AVIStreamInfoA\"},\n\t\t\t{(void*)&qAVIFileRelease,\t\t\t\"AVIFileRelease\"},\n\t\t\t{(void*)&qAVIFileGetStream,\t\t\t\"AVIFileGetStream\"},\n\t\t\t{(void*)&qAVIFileOpenA,\t\t\t\t\"AVIFileOpenA\"},\n\t\t\t{(void*)&qAVIStreamWrite,\t\t\t\"AVIStreamWrite\"},\n\t\t\t{(void*)&qAVIStreamSetFormat,\t\t\"AVIStreamSetFormat\"},\n\t\t\t{(void*)&qAVIMakeCompressedStream,\t\"AVIMakeCompressedStream\"},\n\t\t\t{(void*)&qAVIFileCreateStreamA,\t\t\"AVIFileCreateStreamA\"},\n\t\t\t{NULL,NULL}\n\t\t};\n\t\taviinited = true;\n\t\tavimodule = Sys_LoadLibrary(\"avifil32.dll\", funcs);\n\n\t\tif (avimodule)\n\t\t\tqAVIFileInit();\n\t}\n\n\treturn avimodule?true:false;\n}\n#endif\n\n#ifdef HAVE_MEDIA_DECODER\n\nstatic char *cin_prop_buf;\nstatic size_t cin_prop_bufsize;\nstruct cin_s\n{\n\tqboolean (*decodeframe)(cin_t *cin, qboolean nosound, qboolean forcevideo, double mediatime, void (QDECL *uploadtexture)(void *ctx, uploadfmt_t fmt, int width, int height, void *data, void *palette), void *ctx);\n\tvoid (*shutdown)(cin_t *cin);\t//warning: doesn't free cin_t\n\tvoid (*rewind)(cin_t *cin);\n\t//these are any interactivity functions you might want...\n\tvoid (*cursormove) (struct cin_s *cin, float posx, float posy);\t//pos is 0-1\n\tvoid (*key) (struct cin_s *cin, int code, int unicode, int event);\n\tqboolean (*setsize) (struct cin_s *cin, int width, int height);\n\tvoid (*getsize) (struct cin_s *cin, int *width, int *height, float *aspect);\n\tvoid (*changestream) (struct cin_s *cin, const char *streamname);\n\tqboolean (*getproperty) (struct cin_s *cin, const char *key, char *buffer, size_t *buffersize);\n\n\n\n\t//these are the outputs (not always power of two!)\n\tcinstates_t playstate;\n\tqboolean forceframe;\n\tfloat filmpercentage;\n\n\ttexid_t texture;\n\n\n#ifdef HAVE_API_VFW\n\tstruct {\n\t\tAVISTREAMINFOA\t\tvidinfo;\n\t\tPAVISTREAM\t\t\tpavivideo;\n\t\tAVISTREAMINFOA\t\taudinfo;\n\t\tPAVISTREAM\t\t\tpavisound;\n\t\tPAVIFILE\t\t\tpavi;\n\t\tPGETFRAME\t\t\tpgf;\n\n\t\tHACMSTREAM\t\t\taudiodecoder;\n\n\t\tLPWAVEFORMATEX pWaveFormat;\n\n\t\t//sound stuff\n\t\tint soundpos;\n\t\tint currentframe;\t//previously displayed frame, to avoid excess decoding\n\n\t\t//source info\n\t\tfloat filmfps;\n\t\tint num_frames;\n\t} avi;\n#endif\n\n#ifdef PLUGINS\n\tstruct {\n\t\tvoid *ctx;\n\t\tstruct plugin_s *plug;\n\t\tmedia_decoder_funcs_t *funcs;\t/*fixme*/\n\t\tstruct cin_s *next;\n\t\tstruct cin_s *prev;\n\t} plugin;\n#endif\n\n\tstruct {\n\t\tqbyte\t\t*data;\n\t\tuploadfmt_t\tformat;\n\t\tint\t\t\twidth;\n\t\tint\t\t\theight;\n\t} image;\n\n\tstruct {\n\t\tstruct roq_info_s *roqfilm;\n//\t\tfloat lastmediatime;\n\t\tfloat nextframetime;\n\t} roq;\n\n\tstruct {\n\t\tstruct cinematics_s *cin;\n\t} q2cin;\n\n\tfloat filmstarttime;\n\n\tqbyte *framedata;\t//Z_Malloced buffer\n};\n\nstatic menu_t videomenu;\t//to capture keys+draws+etc. a singleton - multiple videos will be queued instead of simultaneous.\nstatic shader_t *videoshader;\n\n//////////////////////////////////////////////////////////////////////////////////\n//AVI Support (windows)\n#ifdef HAVE_API_VFW\nstatic void Media_WinAvi_Shutdown(struct cin_s *cin)\n{\n\tqAVIStreamGetFrameClose(cin->avi.pgf);\n\tqAVIStreamEndStreaming(cin->avi.pavivideo);\n\tqAVIStreamRelease(cin->avi.pavivideo);\n\t//we don't need to free the file (we freed it immediatly after getting the stream handles)\n}\nstatic void Media_WinAvi_Rewind (cin_t *cin)\n{\n\tcin->avi.soundpos = 0;\n\tcin->avi.currentframe = -1;\n}\nstatic qboolean Media_WinAvi_DecodeFrame (cin_t *cin, qboolean nosound, qboolean forcevideo, double mediatime, void (QDECL *uploadtexture)(void *ctx, uploadfmt_t fmt, int width, int height, void *data, void *palette), void *ctx)\n{\n\tLPBITMAPINFOHEADER lpbi;\t\t\t\t\t\t\t\t\t// Holds The Bitmap Header Information\n\tfloat newframe;\n\tint newframei;\n\tint wantsoundtime;\n\textern cvar_t _snd_mixahead;\n\n\n\tnewframe = ((mediatime * cin->avi.vidinfo.dwRate) / cin->avi.vidinfo.dwScale) + cin->avi.vidinfo.dwInitialFrames;\n\tnewframei = newframe;\n\n\tif (newframei>=cin->avi.num_frames)\n\t\treturn false;\n\n\tif (newframei == cin->avi.currentframe && !forcevideo)\n\t\treturn true;\n\n\tif (cin->avi.currentframe < newframei-1)\n\t\tCon_DPrintf(\"Dropped %i frame(s)\\n\", (newframei - cin->avi.currentframe)-1);\n\n\tcin->avi.currentframe = newframei;\n\tcin->filmpercentage = newframe / cin->avi.num_frames;\n\n\tif (newframei>=cin->avi.num_frames)\n\t\treturn false;\n\t\n\tlpbi = (LPBITMAPINFOHEADER)qAVIStreamGetFrame(cin->avi.pgf, newframei);\t// Grab Data From The AVI Stream\n\tif (!lpbi || lpbi->biBitCount != 24)//oops\n\t\treturn false;\n\n\tuploadtexture(ctx, TF_BGR24_FLIP, lpbi->biWidth, lpbi->biHeight, (char*)lpbi+lpbi->biSize, NULL);\n\n\tif(nosound)\n\t\twantsoundtime = 0;\n\telse\n\t\twantsoundtime = (((mediatime + _snd_mixahead.value + 0.02) * cin->avi.audinfo.dwRate) / cin->avi.audinfo.dwScale) + cin->avi.audinfo.dwInitialFrames;\n\n\twhile (cin->avi.pavisound && cin->avi.soundpos < wantsoundtime)\n\t{\n\t\tLONG lSize;\n\t\tLPBYTE pBuffer;\n\t\tLONG samples;\n\n\t\t/*if the audio skipped more than a second, drop it all and start at a sane time, so our raw audio playing code doesn't buffer too much*/\n\t\tif (cin->avi.soundpos + (1*cin->avi.audinfo.dwRate / cin->avi.audinfo.dwScale) < wantsoundtime)\n\t\t{\n\t\t\tcin->avi.soundpos = wantsoundtime;\n\t\t\tbreak;\n\t\t}\n\n\t\tqAVIStreamRead(cin->avi.pavisound, cin->avi.soundpos, AVISTREAMREAD_CONVENIENT, NULL, 0, &lSize, &samples);\n\t\tpBuffer = cin->framedata;\n\t\tqAVIStreamRead(cin->avi.pavisound, cin->avi.soundpos, AVISTREAMREAD_CONVENIENT, pBuffer, lSize, NULL, &samples);\n\n\t\tcin->avi.soundpos+=samples;\n\n\t\t/*if no progress, stop!*/\n\t\tif (!samples)\n\t\t\tbreak;\n\n\t\tif (cin->avi.audiodecoder)\n\t\t{\n\t\t\tACMSTREAMHEADER strhdr;\n\t\t\tchar buffer[1024*256];\n\n\t\t\tmemset(&strhdr, 0, sizeof(strhdr));\n\t\t\tstrhdr.cbStruct = sizeof(strhdr);\n\t\t\tstrhdr.pbSrc = pBuffer;\n\t\t\tstrhdr.cbSrcLength = lSize;\n\t\t\tstrhdr.pbDst = buffer;\n\t\t\tstrhdr.cbDstLength = sizeof(buffer);\n\n\t\t\tqacmStreamPrepareHeader(cin->avi.audiodecoder, &strhdr, 0);\n\t\t\tqacmStreamConvert(cin->avi.audiodecoder, &strhdr, ACM_STREAMCONVERTF_BLOCKALIGN);\n\t\t\tqacmStreamUnprepareHeader(cin->avi.audiodecoder, &strhdr, 0);\n\n\t\t\tS_RawAudio(SOURCEID_CINEMATIC, strhdr.pbDst, cin->avi.pWaveFormat->nSamplesPerSec, strhdr.cbDstLengthUsed/4, cin->avi.pWaveFormat->nChannels, 2, volume.value);\n\t\t}\n\t\telse\n\t\t\tS_RawAudio(SOURCEID_CINEMATIC, pBuffer, cin->avi.pWaveFormat->nSamplesPerSec, samples, cin->avi.pWaveFormat->nChannels, 2, volume.value);\n\t}\n\treturn true;\n}\n\nstatic cin_t *Media_WinAvi_TryLoad(char *name)\n{\n\tcin_t *cin;\n\tPAVIFILE\t\t\tpavi;\n\tflocation_t loc;\n\n\tif (strchr(name, ':'))\n\t\treturn NULL;\n\n\tif (!qAVIStartup())\n\t\treturn NULL;\n\n\n\tFS_FLocateFile(name, FSLF_IFFOUND, &loc);\n\n\tif (!loc.offset && *loc.rawname && !qAVIFileOpenA(&pavi, loc.rawname, OF_READ, NULL))//!AVIStreamOpenFromFile(&pavi, name, streamtypeVIDEO, 0, OF_READ, NULL))\n\t{\n\t\tint filmwidth;\n\t\tint filmheight;\n\n\t\tcin = Z_Malloc(sizeof(cin_t));\n\t\tcin->avi.pavi = pavi;\n\n\t\tif (qAVIFileGetStream(cin->avi.pavi, &cin->avi.pavivideo, streamtypeVIDEO, 0))\t//retrieve video stream\n\t\t{\n\t\t\tqAVIFileRelease(pavi);\n\t\t\tCon_Printf(\"%s contains no video stream\\n\", name);\n\t\t\treturn NULL;\n\t\t}\n\t\tif (qAVIFileGetStream(cin->avi.pavi, &cin->avi.pavisound, streamtypeAUDIO, 0))\t//retrieve audio stream\n\t\t{\n\t\t\tCon_DPrintf(\"%s contains no audio stream\\n\", name);\n\t\t\tcin->avi.pavisound=NULL;\n\t\t}\n\t\tqAVIFileRelease(cin->avi.pavi);\n\n//play with video\n\t\tqAVIStreamInfoA(cin->avi.pavivideo, &cin->avi.vidinfo, sizeof(cin->avi.vidinfo));\n\t\tfilmwidth=cin->avi.vidinfo.rcFrame.right-cin->avi.vidinfo.rcFrame.left;\t\t\t\t\t// Width Is Right Side Of Frame Minus Left\n\t\tfilmheight=cin->avi.vidinfo.rcFrame.bottom-cin->avi.vidinfo.rcFrame.top;\t\t\t\t\t// Height Is Bottom Of Frame Minus Top\n\t\tcin->framedata = BZ_Malloc(filmwidth*filmheight*4);\n\n\t\tcin->avi.num_frames=qAVIStreamLength(cin->avi.pavivideo);\t\t\t\t\t\t\t// The Last Frame Of The Stream\n\t\tcin->avi.filmfps=1000.0f*(float)cin->avi.num_frames/(float)qAVIStreamSampleToTime(cin->avi.pavivideo,cin->avi.num_frames);\t\t// Calculate Rough Milliseconds Per Frame\n\n\t\tqAVIStreamBeginStreaming(cin->avi.pavivideo, 0, cin->avi.num_frames, 100);\n\n\t\tcin->avi.pgf=qAVIStreamGetFrameOpen(cin->avi.pavivideo, NULL);\n\n\t\tif (!cin->avi.pgf)\n\t\t{\n\t\t\tCon_Printf(\"AVIStreamGetFrameOpen failed. Please install a vfw codec for '%c%c%c%c'. Try ffdshow.\\n\", \n\t\t\t\t((unsigned char*)&cin->avi.vidinfo.fccHandler)[0],\n\t\t\t\t((unsigned char*)&cin->avi.vidinfo.fccHandler)[1],\n\t\t\t\t((unsigned char*)&cin->avi.vidinfo.fccHandler)[2],\n\t\t\t\t((unsigned char*)&cin->avi.vidinfo.fccHandler)[3]\n\t\t\t\t);\n\t\t}\n\n\t\tcin->avi.currentframe=-1;\n\t\tcin->filmstarttime = Sys_DoubleTime();\n\n\t\tcin->avi.soundpos=0;\n\n\n//play with sound\n\t\tif (cin->avi.pavisound)\n\t\t{\n\t\t\tLONG lSize;\n\t\t\tLPBYTE pChunk;\n\t\t\tqAVIStreamInfoA(cin->avi.pavisound, &cin->avi.audinfo, sizeof(cin->avi.audinfo));\n\n\t\t\tqAVIStreamRead(cin->avi.pavisound, 0, AVISTREAMREAD_CONVENIENT, NULL, 0, &lSize, NULL);\n\n\t\t\tif (!lSize)\n\t\t\t\tcin->avi.pWaveFormat = NULL;\n\t\t\telse\n\t\t\t{\n\t\t\t\tpChunk = BZ_Malloc(sizeof(qbyte)*lSize);\n\n\t\t\t\tif(qAVIStreamReadFormat(cin->avi.pavisound, qAVIStreamStart(cin->avi.pavisound), pChunk, &lSize))\n\t\t\t\t{\n\t\t\t\t   // error\n\t\t\t\t\tCon_Printf(\"Failure reading sound info\\n\");\n\t\t\t\t}\n\t\t\t\tcin->avi.pWaveFormat = (LPWAVEFORMATEX)pChunk;\n\t\t\t}\n\n\t\t\tif (!cin->avi.pWaveFormat)\n\t\t\t{\n\t\t\t\tCon_Printf(\"VFW: unable to decode audio\\n\");\n\t\t\t\tqAVIStreamRelease(cin->avi.pavisound);\n\t\t\t\tcin->avi.pavisound=NULL;\n\t\t\t}\n\t\t\telse if (cin->avi.pWaveFormat->wFormatTag != 1)\n\t\t\t{\n\t\t\t\tWAVEFORMATEX pcm_format;\n\t\t\t\tHACMDRIVER drv = NULL;\n\n\t\t\t\tmemset (&pcm_format, 0, sizeof(pcm_format));\n\t\t\t\tpcm_format.wFormatTag = WAVE_FORMAT_PCM;\n\t\t\t\tpcm_format.nChannels = cin->avi.pWaveFormat->nChannels;\n\t\t\t\tpcm_format.nSamplesPerSec = cin->avi.pWaveFormat->nSamplesPerSec;\n\t\t\t\tpcm_format.nBlockAlign = 4;\n\t\t\t\tpcm_format.nAvgBytesPerSec = pcm_format.nSamplesPerSec*4;\n\t\t\t\tpcm_format.wBitsPerSample = 16;\n\t\t\t\tpcm_format.cbSize = 0;\n\n\t\t\t\tif (!qacmStartup() || 0!=qacmStreamOpen(&cin->avi.audiodecoder, drv, cin->avi.pWaveFormat, &pcm_format, NULL, 0, 0, 0))\n\t\t\t\t{\n\t\t\t\t\tCon_Printf(\"Failed to open audio decoder\\n\");\t//FIXME: so that it no longer is...\n\t\t\t\t\tqAVIStreamRelease(cin->avi.pavisound);\n\t\t\t\t\tcin->avi.pavisound=NULL;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tcin->decodeframe = Media_WinAvi_DecodeFrame;\n\t\tcin->rewind = Media_WinAvi_Rewind;\n\t\tcin->shutdown = Media_WinAvi_Shutdown;\n\t\treturn cin;\n\t}\n\treturn NULL;\n}\n#endif\n\n//AVI Support (windows)\n//////////////////////////////////////////////////////////////////////////////////\n//Plugin Support\n#ifdef PLUGINS\nstatic media_decoder_funcs_t *plugindecodersfunc[8];\nstatic struct plugin_s *plugindecodersplugin[8];\nstatic cin_t *active_cin_plugins;\n\nqboolean Media_RegisterDecoder(struct plugin_s *plug, media_decoder_funcs_t *funcs)\n{\n\tint i;\n\tif (!funcs || funcs->structsize != sizeof(*funcs))\n\t\treturn false;\n\n\tfor (i = 0; i < sizeof(plugindecodersfunc)/sizeof(plugindecodersfunc[0]); i++)\n\t{\n\t\tif (plugindecodersfunc[i] == NULL)\n\t\t{\n\t\t\tplugindecodersfunc[i] = funcs;\n\t\t\tplugindecodersplugin[i] = plug;\n\t\t\treturn true;\n\t\t}\n\t}\n\treturn false;\n}\n/*funcs==null closes ALL decoders from this plugin*/\nqboolean Media_UnregisterDecoder(struct plugin_s *plug, media_decoder_funcs_t *funcs)\n{\n\tqboolean success = true;\n\tint i;\n\tstatic media_decoder_funcs_t deadfuncs;\n\tstruct plugin_s *oldplug = currentplug;\n\tcin_t *cin, *next;\n\n\tfor (i = 0; i < sizeof(plugindecodersfunc)/sizeof(plugindecodersfunc[0]); i++)\n\t{\n\t\tif (plugindecodersfunc[i] == funcs || (!funcs && plugindecodersplugin[i] == plug))\n\t\t{\n\t\t\t//kill any cinematics currently using that decoder\n\t\t\tfor (cin = active_cin_plugins; cin; cin = next)\n\t\t\t{\n\t\t\t\tnext = cin->plugin.next;\n\n\t\t\t\tif (cin->plugin.plug == plug && cin->plugin.funcs == plugindecodersfunc[i])\n\t\t\t\t{\n\t\t\t\t\t//we don't kill the engine's side of it, not just yet anyway.\n\t\t\t\t\tcurrentplug = cin->plugin.plug;\n\t\t\t\t\tif (cin->plugin.funcs->shutdown)\n\t\t\t\t\t\tcin->plugin.funcs->shutdown(cin->plugin.ctx);\n\t\t\t\t\tcin->plugin.funcs = &deadfuncs;\n\t\t\t\t\tcin->plugin.plug = NULL;\n\t\t\t\t\tcin->plugin.ctx = NULL;\n\t\t\t\t}\n\t\t\t}\n\t\t\tcurrentplug = oldplug;\n\n\n\t\t\tplugindecodersfunc[i] = NULL;\n\t\t\tplugindecodersplugin[i] = NULL;\n\t\t\tif (funcs)\n\t\t\t\treturn success;\n\t\t}\n\t}\n\n\tif (!funcs)\n\t{\n\t\tstatic media_decoder_funcs_t deadfuncs;\n\t\tstruct plugin_s *oldplug = currentplug;\n\t\tcin_t *cin, *next;\n\n\t\tfor (cin = active_cin_plugins; cin; cin = next)\n\t\t{\n\t\t\tnext = cin->plugin.next;\n\n\t\t\tif (cin->plugin.plug == plug)\n\t\t\t{\n\t\t\t\t//we don't kill the engine's side of it, not just yet anyway.\n\t\t\t\tcurrentplug = cin->plugin.plug;\n\t\t\t\tif (cin->plugin.funcs->shutdown)\n\t\t\t\t\tcin->plugin.funcs->shutdown(cin->plugin.ctx);\n\t\t\t\tcin->plugin.funcs = &deadfuncs;\n\t\t\t\tcin->plugin.plug = NULL;\n\t\t\t\tcin->plugin.ctx = NULL;\n\t\t\t}\n\t\t}\n\t\tcurrentplug = oldplug;\n\t}\n\treturn success;\n}\n\nstatic qboolean Media_Plugin_DecodeFrame (cin_t *cin, qboolean nosound, qboolean forcevideo, double mediatime, void (QDECL *uploadtexture)(void *ctx, uploadfmt_t fmt, int width, int height, void *data, void *palette), void *ctx)\n{\n\tqboolean okay;\n\tstruct plugin_s *oldplug = currentplug;\n\tif (!cin->plugin.funcs->decodeframe)\n\t\treturn false;\t//plugin closed or something\n\n\tcurrentplug = cin->plugin.plug;\n\tokay = cin->plugin.funcs->decodeframe(cin->plugin.ctx, nosound, forcevideo, mediatime, uploadtexture, ctx);\n\tcurrentplug = oldplug;\n\treturn okay;\n}\nstatic void Media_Plugin_Shutdown(cin_t *cin)\n{\n\tstruct plugin_s *oldplug = currentplug;\n\tcurrentplug = cin->plugin.plug;\n\tif (cin->plugin.funcs->shutdown)\n\t\tcin->plugin.funcs->shutdown(cin->plugin.ctx);\n\tcurrentplug = oldplug;\n\n\tif (cin->plugin.prev)\n\t\tcin->plugin.prev->plugin.next = cin->plugin.next;\n\telse\n\t\tactive_cin_plugins = cin->plugin.next;\n\tif (cin->plugin.next)\n\t\tcin->plugin.next->plugin.prev = cin->plugin.prev;\n}\nstatic void Media_Plugin_Rewind(cin_t *cin)\n{\n\tstruct plugin_s *oldplug = currentplug;\n\tcurrentplug = cin->plugin.plug;\n\tif (cin->plugin.funcs->rewind)\n\t\tcin->plugin.funcs->rewind(cin->plugin.ctx);\n\tcurrentplug = oldplug;\n}\n\nvoid Media_Plugin_MoveCursor(cin_t *cin, float posx, float posy)\n{\n\tstruct plugin_s *oldplug = currentplug;\n\tcurrentplug = cin->plugin.plug;\n\tif (cin->plugin.funcs->cursormove)\n\t\tcin->plugin.funcs->cursormove(cin->plugin.ctx, posx, posy);\n\tcurrentplug = oldplug;\n}\nvoid Media_Plugin_KeyPress(cin_t *cin, int code, int unicode, int event)\n{\n\tstruct plugin_s *oldplug = currentplug;\n\tcurrentplug = cin->plugin.plug;\n\tif (cin->plugin.funcs->key)\n\t\tcin->plugin.funcs->key(cin->plugin.ctx, code, unicode, event);\n\tcurrentplug = oldplug;\n}\nqboolean Media_Plugin_SetSize(cin_t *cin, int width, int height)\n{\n\tqboolean result = false;\n\tstruct plugin_s *oldplug = currentplug;\n\tcurrentplug = cin->plugin.plug;\n\tif (cin->plugin.funcs->setsize)\n\t\tresult = cin->plugin.funcs->setsize(cin->plugin.ctx, width, height);\n\tcurrentplug = oldplug;\n\treturn result;\n}\nvoid Media_Plugin_GetSize(cin_t *cin, int *width, int *height, float *aspect)\n{\n\tstruct plugin_s *oldplug = currentplug;\n\tcurrentplug = cin->plugin.plug;\n\tif (cin->plugin.funcs->getsize)\n\t\tcin->plugin.funcs->getsize(cin->plugin.ctx, width, height);\n\tcurrentplug = oldplug;\n}\nvoid Media_Plugin_ChangeStream(cin_t *cin, const char *streamname)\n{\n\tstruct plugin_s *oldplug = currentplug;\n\tcurrentplug = cin->plugin.plug;\n\tif (cin->plugin.funcs->changestream)\n\t\tcin->plugin.funcs->changestream(cin->plugin.ctx, streamname);\n\tcurrentplug = oldplug;\n}\nqboolean Media_Plugin_GetProperty(cin_t *cin, const char *propname, char *data, size_t *datasize)\n{\n\tqboolean ret = false;\n\tstruct plugin_s *oldplug = currentplug;\n\tcurrentplug = cin->plugin.plug;\n\tif (cin->plugin.funcs->getproperty)\n\t\tret = cin->plugin.funcs->getproperty(cin->plugin.ctx, propname, data, datasize);\n\tcurrentplug = oldplug;\n\treturn ret;\n}\n\ncin_t *Media_Plugin_TryLoad(char *name)\n{\n\tcin_t *cin;\n\tint i;\n\tmedia_decoder_funcs_t *funcs = NULL;\n\tstruct plugin_s *plug = NULL;\n\tvoid *ctx = NULL;\n\tstruct plugin_s *oldplug = currentplug;\n\tfor (i = 0; i < sizeof(plugindecodersfunc)/sizeof(plugindecodersfunc[0]); i++)\n\t{\n\t\tfuncs = plugindecodersfunc[i];\n\t\tif (funcs)\n\t\t{\n\t\t\tplug = plugindecodersplugin[i];\n\t\t\tcurrentplug = plug;\n\t\t\tctx = funcs->createdecoder(name);\n\t\t\tif (ctx)\n\t\t\t\tbreak;\n\t\t}\n\t}\n\tcurrentplug = oldplug;\n\n\tif (ctx)\n\t{\n\t\tcin = Z_Malloc(sizeof(cin_t));\n\t\tcin->plugin.funcs = funcs;\n\t\tcin->plugin.plug = plug;\n\t\tcin->plugin.ctx = ctx;\n\t\tcin->plugin.next = active_cin_plugins;\n\t\tcin->plugin.prev = NULL;\n\t\tif (cin->plugin.next)\n\t\t\tcin->plugin.next->plugin.prev = cin;\n\t\tactive_cin_plugins = cin;\n\t\tcin->decodeframe = Media_Plugin_DecodeFrame;\n\t\tcin->shutdown = Media_Plugin_Shutdown;\n\t\tcin->rewind = Media_Plugin_Rewind;\n\n\t\tif (funcs->cursormove)\n\t\t\tcin->cursormove\t\t= Media_Plugin_MoveCursor;\n\t\tif (funcs->key)\n\t\t\tcin->key\t\t\t= Media_Plugin_KeyPress;\n\t\tif (funcs->setsize)\n\t\t\tcin->setsize\t\t= Media_Plugin_SetSize;\n\t\tif (funcs->getsize)\n\t\t\tcin->getsize\t\t= Media_Plugin_GetSize;\n\t\tif (funcs->changestream)\n\t\t\tcin->changestream\t= Media_Plugin_ChangeStream;\n\t\tif (funcs->getproperty)\n\t\t\tcin->getproperty\t= Media_Plugin_GetProperty;\n\n\t\treturn cin;\n\t}\n\treturn NULL;\n}\n#endif\n//Plugin Support\n//////////////////////////////////////////////////////////////////////////////////\n//Quake3 RoQ Support\n\n#ifdef Q3CLIENT\n#include \"roq.h\"\nstatic void Media_Roq_Shutdown(struct cin_s *cin)\n{\n\troq_close(cin->roq.roqfilm);\n\tcin->roq.roqfilm=NULL;\n}\n\nstatic void Media_Roq_Rewind(struct cin_s *cin)\n{\n\troq_rewind(cin->roq.roqfilm);\n\tcin->roq.nextframetime = 0;\n}\n\nstatic qboolean Media_Roq_DecodeFrame (cin_t *cin, qboolean nosound, qboolean forcevideo, double mediatime, void (QDECL *uploadtexture)(void *ctx, uploadfmt_t fmt, int width, int height, void *data, void *palette), void *ctx)\n{\n\tqboolean doupdate = forcevideo;\n\n\t//seeking could probably be done a bit better...\n\t//key frames or something, I dunno\n\twhile (mediatime >= cin->roq.nextframetime)\n\t{\n\t\tswitch (roq_read_frame(cin->roq.roqfilm)) //0 if end, -1 if error, 1 if success\n\t\t{\n\t\tdefault:\t//error\n\t\tcase 0:\t\t//eof\n\t\t\tif (!doupdate)\n\t\t\t\treturn false;\n\t\t\tbreak;\n\t\tcase 1:\n\t\t\tbreak;\t//success\n\t\t}\n\t\tcin->roq.nextframetime += 1.0/30;\n\t\tdoupdate = true;\n\t}\n\n\tif (doupdate)\n\t{\n\t\tif (cin->roq.roqfilm->num_frames)\n\t\t\tcin->filmpercentage = cin->roq.roqfilm->frame_num / cin->roq.roqfilm->num_frames;\n\t\telse\n\t\t\tcin->filmpercentage = 0;\n\n\t\tuploadtexture(ctx, TF_RGBX32, cin->roq.roqfilm->width, cin->roq.roqfilm->height, cin->roq.roqfilm->rgba[0], NULL);\n\n\t\tif (!nosound)\n\t\t{\n\t\t\twhile (cin->roq.roqfilm->audio_channels && S_HaveOutput() && cin->roq.roqfilm->aud_pos < cin->roq.roqfilm->vid_pos)\n\t\t\t{\n\t\t\t\tif (roq_read_audio(cin->roq.roqfilm)>0)\t\t\n\t\t\t\t\tS_RawAudio(SOURCEID_CINEMATIC, cin->roq.roqfilm->audio, 22050, cin->roq.roqfilm->audio_size/cin->roq.roqfilm->audio_channels, cin->roq.roqfilm->audio_channels, 2, volume.value );\n\t\t\t\telse\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\treturn true;\n}\n\nvoid Media_Roq_GetSize(struct cin_s *cin, int *width, int *height, float *aspect)\n{\n\t*width = cin->roq.roqfilm->width;\n\t*height = cin->roq.roqfilm->height;\n\t*aspect = (float)cin->roq.roqfilm->width/cin->roq.roqfilm->height;\n}\n\nstatic cin_t *Media_RoQ_TryLoad(char *name)\n{\n\tcin_t *cin;\n\troq_info *roqfilm;\n\tif (strchr(name, ':'))\n\t\treturn NULL;\n\n\tif ((roqfilm = roq_open(name)))\n\t{\n\t\tcin = Z_Malloc(sizeof(cin_t));\n\t\tcin->decodeframe = Media_Roq_DecodeFrame;\n\t\tcin->rewind = Media_Roq_Rewind;\n\t\tcin->shutdown = Media_Roq_Shutdown;\n\t\tcin->getsize = Media_Roq_GetSize;\n\n\t\tcin->roq.roqfilm = roqfilm;\n\n\t\treturn cin;\n\t}\n\treturn NULL;\n}\n#endif\n\n//Quake3 RoQ Support\n//////////////////////////////////////////////////////////////////////////////////\n//Static Image Support\n\n#ifndef MINIMAL\nstatic void Media_Static_Shutdown(struct cin_s *cin)\n{\n\tBZ_Free(cin->image.data);\n\tcin->image.data = NULL;\n}\n\nstatic qboolean Media_Static_DecodeFrame (cin_t *cin, qboolean nosound, qboolean forcevideo, double mediatime, void (QDECL *uploadtexture)(void *ctx, uploadfmt_t fmt, int width, int height, void *data, void *palette), void *ctx)\n{\n\tif (forcevideo)\n\t\tuploadtexture(ctx, cin->image.format, cin->image.width, cin->image.height, cin->image.data, NULL);\n\treturn true;\n}\n\nstatic cin_t *Media_Static_TryLoad(char *name)\n{\n\tcin_t *cin;\n\tchar *dot = strrchr(name, '.');\n\n\tif (dot && (!strcmp(dot, \".pcx\") || !strcmp(dot, \".tga\") || !strcmp(dot, \".png\") || !strcmp(dot, \".jpg\")))\n\t{\n\t\tqbyte *staticfilmimage;\n\t\tint imagewidth;\n\t\tint imageheight;\n\t\tuploadfmt_t format = PTI_RGBA8;\n\n\t\tint fsize;\n\t\tchar fullname[MAX_QPATH];\n\t\tqbyte *file;\n\n\t\tQ_snprintfz(fullname, sizeof(fullname), \"%s\", name);\n\t\tfsize = FS_LoadFile(fullname, (void **)&file);\n\t\tif (!file)\n\t\t{\n\t\t\tQ_snprintfz(fullname, sizeof(fullname), \"pics/%s\", name);\n\t\t\tfsize = FS_LoadFile(fullname, (void **)&file);\n\t\t\tif (!file)\n\t\t\t\treturn NULL;\n\t\t}\n\n\t\tstaticfilmimage = ReadRawImageFile(file, fsize, &imagewidth, &imageheight, &format, false, fullname);\n\t\tFS_FreeFile(file); //done with the file now\n\t\tif (!staticfilmimage)\n\t\t{\n\t\t\tCon_Printf(\"Static cinematic format not supported.\\n\");\t//not supported format\n\t\t\treturn NULL;\n\t\t}\n\n\t\tcin = Z_Malloc(sizeof(cin_t));\n\t\tcin->decodeframe = Media_Static_DecodeFrame;\n\t\tcin->shutdown = Media_Static_Shutdown;\n\n\t\tcin->image.data = staticfilmimage;\n\t\tcin->image.format = format;\n\t\tcin->image.width = imagewidth;\n\t\tcin->image.height = imageheight;\n\n\t\treturn cin;\n\t}\n\treturn NULL;\n}\n#endif\n\n//Static Image Support\n//////////////////////////////////////////////////////////////////////////////////\n//Quake2 CIN Support\n\n#ifdef Q2CLIENT\n//fixme: this code really shouldn't be in this file.\nstatic void Media_Cin_Shutdown(struct cin_s *cin)\n{\n\tCIN_StopCinematic(cin->q2cin.cin);\n}\nstatic void Media_Cin_Rewind(struct cin_s *cin)\n{\n\tCIN_Rewind(cin->q2cin.cin);\n}\n\nstatic qboolean Media_Cin_DecodeFrame(cin_t *cin, qboolean nosound, qboolean forcevideo, double mediatime, void (QDECL *uploadtexture)(void *ctx, uploadfmt_t fmt, int width, int height, void *data, void *palette), void *ctx)\n{\n\tqbyte *outdata;\n\tint outwidth;\n\tint outheight;\n\tqbyte *outpalette;\n\n\tswitch (CIN_RunCinematic(cin->q2cin.cin, mediatime, &outdata, &outwidth, &outheight, &outpalette))\n\t{\n\tdefault:\n\tcase 0:\n\t\treturn false;\n\tcase 1:\n\t\tbreak;\n\tcase 2:\n\t\tif (!forcevideo)\n\t\t\treturn true;\n\t\tbreak;\n\t}\n\n\tuploadtexture(ctx, TF_8PAL24, outwidth, outheight, outdata, outpalette);\n\treturn true;\n}\n\nstatic cin_t *Media_Cin_TryLoad(char *name)\n{\n\tstruct cinematics_s *q2cin;\n\tcin_t *cin;\n\tchar *dot = strrchr(name, '.');\n\n\tif (dot && (!strcmp(dot, \".cin\")))\n\t{\n\t\tq2cin = CIN_PlayCinematic(name);\n\t\tif (q2cin)\n\t\t{\n\t\t\tcin = Z_Malloc(sizeof(cin_t));\n\t\t\tcin->q2cin.cin = q2cin;\n\t\t\tcin->decodeframe = Media_Cin_DecodeFrame;\n\t\t\tcin->shutdown = Media_Cin_Shutdown;\n\t\t\tcin->rewind = Media_Cin_Rewind;\n\t\t\treturn cin;\n\t\t}\n\t}\n\n\treturn NULL;\n}\n#endif\n\n//Quake2 CIN Support\n//////////////////////////////////////////////////////////////////////////////////\n\nvoid Media_ShutdownCin(cin_t *cin)\n{\n\tif (!cin)\n\t\treturn;\n\n\tif (cin->shutdown)\n\t\tcin->shutdown(cin);\n\n\tif (TEXVALID(cin->texture))\n\t\tImage_UnloadTexture(cin->texture);\n\n\tif (cin->framedata)\n\t{\n\t\tBZ_Free(cin->framedata);\n\t\tcin->framedata = NULL;\n\t}\n\n\tZ_Free(cin);\n\n\tZ_Free(cin_prop_buf);\n\tcin_prop_buf = NULL;\n\tcin_prop_bufsize = 0;\n}\n\ncin_t *Media_StartCin(char *name)\n{\n\tcin_t *cin = NULL;\n\n\tif (!name || !*name)\t//clear only.\n\t\treturn NULL;\n\n#ifndef MINIMAL\n\tif (!cin)\n\t\tcin = Media_Static_TryLoad(name);\n#endif\n#ifdef Q2CLIENT\n\tif (!cin)\n\t\tcin = Media_Cin_TryLoad(name);\n#endif\n#ifdef Q3CLIENT\n\tif (!cin)\n\t\tcin = Media_RoQ_TryLoad(name);\n#endif\n#ifdef HAVE_API_VFW\n\tif (!cin)\n\t\tcin = Media_WinAvi_TryLoad(name);\n#endif\n#ifdef PLUGINS\n\tif (!cin)\n\t\tcin = Media_Plugin_TryLoad(name);\n#endif\n\tif (cin)\n\t\tcin->filmstarttime = realtime;\n\treturn cin;\n}\n\nstatic struct pendingfilms_s\n{\n\tstruct pendingfilms_s *next;\n\tchar name[1];\n} *pendingfilms;\n\nstatic void MediaView_DrawFilm(menu_t *m)\n{\n\tif (videoshader)\n\t{\n\t\tcin_t *cin = R_ShaderGetCinematic(videoshader);\n\t\tif (cin && cin->playstate == CINSTATE_INVALID)\n\t\t\tMedia_SetState(cin, CINSTATE_PLAY);\t//err... wot? must have vid_reloaded or something\n\t\tif (cin && cin->playstate == CINSTATE_ENDED)\n\t\t\tMedia_StopFilm(false);\n\t\telse if (cin)\n\t\t{\n\t\t\tint cw, ch;\n\t\t\tfloat aspect;\n\t\t\tif (cin->cursormove)\n\t\t\t\tcin->cursormove(cin, mousecursor_x/(float)vid.width, mousecursor_y/(float)vid.height);\n\t\t\tif (cin->setsize)\n\t\t\t\tcin->setsize(cin, vid.pixelwidth, vid.pixelheight);\n\n\t\t\t//FIXME: should have a proper aspect ratio setting. RoQ files are always power of two, which makes things ugly.\n\t\t\tif (cin->getsize)\n\t\t\t\tcin->getsize(cin, &cw, &ch, &aspect);\n\t\t\telse\n\t\t\t{\n\t\t\t\tcw = 4;\n\t\t\t\tch = 3;\n\t\t\t}\n\n\t\t\tR2D_Letterbox(0, 0, vid.fbvwidth, vid.fbvheight, videoshader, cw, ch);\n\n\t\t\tSCR_SetUpToDrawConsole();\n\t\t\tif  (scr_con_current)\n\t\t\t\tSCR_DrawConsole (false);\n\t\t}\n\t}\n\telse if (!videoshader)\n\t\tMenu_Unlink(m, true);\n}\nstatic qboolean MediaView_KeyEvent(menu_t *m, qboolean isdown, unsigned int devid, int key, int unicode)\n{\n\tif (isdown && (key == K_ESCAPE || key == K_GP_GUIDE || key == K_GP_DIAMOND_CANCEL || key == K_TOUCHLONG))\n\t{\n\t\tMedia_StopFilm(false);\t//skip to the next.\n\t\treturn true;\n\t}\n\tMedia_Send_KeyEvent(NULL, key, unicode, !isdown);\n\treturn true;\n}\nstatic qboolean MediaView_MouseMove(menu_t *m, qboolean isabs, unsigned int devid, float x, float y)\n{\n\tcin_t *cin;\n\tcin = R_ShaderGetCinematic(videoshader);\n\tif (!cin || !cin->cursormove)\n\t\treturn false;\n\tif (isabs)\n\t\tcin->cursormove(cin, x, y);\n\treturn true;\n}\nstatic void MediaView_StopFilm(menu_t *m, qboolean forced)\n{\t//display is going away for some reason. might as well kill them all\n\tMedia_StopFilm(true);\n}\n\nstatic qboolean Media_BeginNextFilm(void)\n{\n\tcin_t *cin;\n\tchar sname[MAX_QPATH];\n\tstruct pendingfilms_s *p;\n\n\tif (!pendingfilms)\n\t\treturn false;\n\tp = pendingfilms;\n\tpendingfilms = p->next;\n\tsnprintf(sname, sizeof(sname), \"cinematic/%s\", p->name);\n\n\tif (!qrenderer)\n\t{\n\t\tZ_Free(p);\n\t\treturn false;\n\t}\n\n\tvideoshader = R_RegisterCustom(NULL, sname, SUF_NONE, Shader_DefaultCinematic, p->name);\n\n\tcin = R_ShaderGetCinematic(videoshader);\n\tif (cin)\n\t{\n\t\tMedia_SetState(cin, CINSTATE_PLAY);\n\t\tMedia_Send_Reset(cin);\n\t\tif (cin->changestream)\n\t\t\tcin->changestream(cin, \"cmd:focus\");\n\t}\n\telse\n\t{\n\t\tCon_Printf(\"Unable to play cinematic %s\\n\", p->name);\n\t\tR_UnloadShader(videoshader);\n\t\tvideoshader = NULL;\n\t}\n\tZ_Free(p);\n\n\tif (videoshader)\n\t{\n\t\tvideomenu.cursor = NULL;\n\t\tvideomenu.release = MediaView_StopFilm;\n\t\tvideomenu.drawmenu = MediaView_DrawFilm;\n\t\tvideomenu.mousemove = MediaView_MouseMove;\n\t\tvideomenu.keyevent = MediaView_KeyEvent;\n\t\tvideomenu.isopaque = true;\n\t\tMenu_Push(&videomenu, false);\n\t}\n\telse\n\t\tMenu_Unlink(&videomenu, true);\n\treturn !!videoshader;\n}\nqboolean Media_StopFilm(qboolean all)\n{\n\tif (!pendingfilms && !videoshader)\n\t\treturn false;\t//nothing to do\n\n\tif (all)\n\t{\n\t\tstruct pendingfilms_s *p;\n\t\twhile(pendingfilms)\n\t\t{\n\t\t\tp = pendingfilms;\n\t\t\tpendingfilms = p->next;\n\t\t\tZ_Free(p);\n\t\t}\n\t}\n\tif (videoshader)\n\t{\n\t\tR_UnloadShader(videoshader);\n\t\tvideoshader = NULL;\n\n\t\tS_RawAudio(SOURCEID_CINEMATIC, NULL, 0, 0, 0, 0, 0);\n\t}\n\n\twhile (pendingfilms && !videoshader)\n\t{\n\t\tif (Media_BeginNextFilm())\n\t\t\tbreak;\n\t}\n\n\t//for q2 cinematic-maps.\n\t//q2 sends 'nextserver' when the client's cinematics end so that the game can progress to the next map.\n\t//qc might want to make use of it too, but its probably best to just send a playfilm command at the start of the map and then just wait it out. maybe treat nextserver as an unpause request.\n\tif (!videoshader && cls.state == ca_active)\n\t{\n\t\tCL_SendClientCommand(true, \"nextserver %i\", cl.servercount);\n\t}\n\treturn true;\n}\nqboolean Media_PlayFilm(char *name, qboolean enqueue)\n{\n\tif (!enqueue || !*name)\n\t\tMedia_StopFilm(true);\n\n\tif (*name)\n\t{\n\t\tstruct pendingfilms_s **p;\n\t\tfor (p = &pendingfilms; *p; p = &(*p)->next)\n\t\t\t;\n\t\t(*p) = Z_Malloc(sizeof(**p) + strlen(name));\n\t\tstrcpy((*p)->name, name);\n\n\t\twhile (pendingfilms && !videoshader)\n\t\t{\n\t\t\tif (Media_BeginNextFilm())\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\tif (videoshader)\n\t{\n#ifdef HAVE_CDPLAYER\n\t\tCDAudio_Stop();\n#endif\n\t\tSCR_EndLoadingPlaque();\n\n\t\tif (!Key_Dest_Has(kdm_console))\n\t\t\tscr_con_current=0;\n\t\treturn true;\n\t}\n\telse\n\t\treturn false;\n}\n\n#ifndef SERVERONLY\nvoid QDECL Media_UpdateTexture(void *ctx, uploadfmt_t fmt, int width, int height, void *data, void *palette)\n{\n\tcin_t *cin = ctx;\n\tif (!TEXVALID(cin->texture))\n\t\tTEXASSIGN(cin->texture, Image_CreateTexture(\"***cin***\", NULL, IF_CLAMP|IF_NOMIPMAP));\n\tImage_Upload(cin->texture, fmt, data, palette, width, height, 1, IF_CLAMP|IF_NOMIPMAP|IF_NOGAMMA);\n}\ntexid_tf Media_UpdateForShader(cin_t *cin)\n{\n\tfloat mediatime;\n\tqboolean force = false;\n\tif (!cin)\t//caller fucked up.\n\t\treturn r_nulltex;\n\n\tif (cin->playstate == CINSTATE_FLUSHED)\n\t{\n\t\tcin->playstate = CINSTATE_PLAY;\n\t\tmediatime = 0;\n\t\tcin->filmstarttime = realtime;\n\t}\n\telse if (cin->playstate == CINSTATE_INVALID)\n\t{\n\t\tcin->playstate = CINSTATE_LOOP;\n\t\tmediatime = 0;\n\t\tcin->filmstarttime = realtime;\n\t}\n\telse if (cin->playstate == CINSTATE_ENDED)\n\t\tmediatime = 0;\t//if we reach the end of the video, we give up and just show the first frame for subsequent frames\n\telse if (cin->playstate == CINSTATE_PAUSE)\n\t\tmediatime = cin->filmstarttime;\n\telse\n\t\tmediatime = realtime - cin->filmstarttime;\n\n\tif (!TEXVALID(cin->texture))\n\t{\n\t\tforce = true;\n\t}\n\n\tif (!cin->decodeframe(cin, false, force, mediatime, Media_UpdateTexture, cin))\n\t{\n\t\t//we got eof / error\n\t\tif (cin->playstate != CINSTATE_LOOP)\n\t\t\tMedia_SetState(cin, CINSTATE_ENDED);\n\t\tif (cin->rewind)\n\t\t{\n\t\t\tMedia_Send_Reset(cin);\n\t\t\tmediatime = 0;\n\t\t\tif (!cin->decodeframe(cin, true, force, mediatime, Media_UpdateTexture, cin))\n\t\t\t\treturn r_nulltex;\n\t\t}\n\t}\n\n\treturn cin->texture;\n}\n#endif\n\nvoid QDECL Media_Send_KeyEvent(cin_t *cin, int button, int unicode, int event)\n{\n\tif (!cin)\n\t\tcin = R_ShaderGetCinematic(videoshader);\n\tif (!cin)\n\t\treturn;\n\tif (cin->key)\n\t\tcin->key(cin, button, unicode, event);\n\telse if ((button == K_SPACE || button == K_GP_DIAMOND_ALTCONFIRM) && !event)\n\t{\n\t\tif (cin->playstate == CINSTATE_PAUSE)\n\t\t\tMedia_SetState(cin, CINSTATE_PLAY);\n\t\telse\n\t\t\tMedia_SetState(cin, CINSTATE_PAUSE);\n\t}\n\telse if (button == K_BACKSPACE && !event)\n\t{\n\t\tMedia_Send_Reset(cin);\n\t}\n\telse if ((button == K_LEFTARROW || button == K_KP_LEFTARROW || button == K_GP_DPAD_LEFT) && !event)\n\t\tcin->filmstarttime += (cin->playstate == CINSTATE_PAUSE)?-10:10;\n\telse if ((button == K_RIGHTARROW || button == K_KP_RIGHTARROW || button == K_GP_DPAD_RIGHT) && !event)\n\t\tcin->filmstarttime -= (cin->playstate == CINSTATE_PAUSE)?-10:10;\n}\nvoid QDECL Media_Send_MouseMove(cin_t *cin, float x, float y)\n{\n\tif (!cin)\n\t\tcin = R_ShaderGetCinematic(videoshader);\n\tif (!cin || !cin->cursormove)\n\t\treturn;\n\tcin->cursormove(cin, x, y);\n}\nvoid QDECL Media_Send_Resize(cin_t *cin, int x, int y)\n{\n\tif (!cin)\n\t\tcin = R_ShaderGetCinematic(videoshader);\n\tif (!cin || !cin->setsize)\n\t\treturn;\n\tcin->setsize(cin, x, y);\n}\nvoid QDECL Media_Send_GetSize(cin_t *cin, int *x, int *y, float *aspect)\n{\n\t*x = 0;\n\t*y = 0;\n\t*aspect = 0;\n\tif (!cin)\n\t\tcin = R_ShaderGetCinematic(videoshader);\n\tif (!cin || !cin->getsize)\n\t\treturn;\n\tcin->getsize(cin, x, y, aspect);\n}\nvoid QDECL Media_Send_Reset(cin_t *cin)\n{\n\tif (!cin)\n\t\treturn;\n\tif (cin->playstate == CINSTATE_PAUSE)\n\t\tcin->filmstarttime = 0;\t//fixed on resume\n\telse\n\t\tcin->filmstarttime = realtime;\n\tif (cin->rewind)\n\t\tcin->rewind(cin);\n}\nvoid QDECL Media_Send_Command(cin_t *cin, const char *command)\n{\n\tif (!cin)\n\t\tcin = R_ShaderGetCinematic(videoshader);\n\tif (cin && cin->changestream)\n\t\tcin->changestream(cin, command);\n\telse if (cin && !strcmp(command, \"cmd:rewind\"))\n\t\tMedia_Send_Reset(cin);\n}\nconst char *QDECL Media_Send_GetProperty(cin_t *cin, const char *key)\n{\n\tif (!cin)\n\t\tcin = R_ShaderGetCinematic(videoshader);\n\tif (cin && cin->getproperty)\n\t{\n\t\tsize_t needsize = 0;\n\t\tif (cin->getproperty(cin, key, NULL, &needsize))\n\t\t{\n\t\t\tif (needsize+1 > cin_prop_bufsize)\n\t\t\t{\n\t\t\t\tcin_prop_buf = BZ_Realloc(cin_prop_buf, needsize+1);\n\t\t\t\tcin_prop_bufsize = needsize+1;\n\t\t\t}\n\t\t\tcin_prop_buf[needsize] = 0;\n\t\t\tif (cin->getproperty(cin, key, cin_prop_buf, &needsize))\n\t\t\t\treturn cin_prop_buf;\n\t\t}\n\t}\n\n\treturn NULL;\n}\nvoid Media_SetState(cin_t *cin, cinstates_t newstate)\n{\n\tif (cin->playstate == newstate)\n\t\treturn;\n\n\tif (newstate == CINSTATE_FLUSHED || newstate == CINSTATE_ENDED)\n\t\tcin->filmstarttime = realtime;\t//these technically both rewind it.\n\telse if (newstate == CINSTATE_PAUSE)\n\t\tcin->filmstarttime = realtime - cin->filmstarttime;\n\telse if (cin->playstate == CINSTATE_PAUSE)\n\t\tcin->filmstarttime = realtime - cin->filmstarttime;\n\n\tcin->playstate = newstate;\n}\ncinstates_t Media_GetState(cin_t *cin)\n{\n\tif (!cin)\n\t\tcin = R_ShaderGetCinematic(videoshader);\n\tif (!cin)\n\t\treturn CINSTATE_INVALID;\n\treturn cin->playstate;\n}\n\nvoid Media_PlayFilm_f (void)\n{\n\tint i;\n\tif (Cmd_Argc() < 2)\n\t{\n\t\tCon_Printf(\"playfilm <filename>\");\n\t}\n\tif (!strcmp(Cmd_Argv(0), \"cinematic\"))\n\t\tMedia_PlayFilm(va(\"video/%s\", Cmd_Argv(1)), false);\n\telse\n\t{\n\t\tMedia_PlayFilm(Cmd_Argv(1), false);\n\t\tfor (i = 2; i < Cmd_Argc(); i++)\n\t\t\tMedia_PlayFilm(Cmd_Argv(i), true);\n\t}\n}\n\nvoid Media_PlayVideoWindowed_f (void)\n{\n\tchar *videomap = Cmd_Argv(1);\n\tshader_t *s;\n\tconsole_t *con;\n\tif (!qrenderer)\n\t\treturn;\n\ts = R_RegisterCustom(NULL, va(\"consolevid_%s\", videomap), SUF_NONE, Shader_DefaultCinematic, videomap);\n\tif (!R_ShaderGetCinematic(s))\n\t{\n\t\tR_UnloadShader(s);\n\t\tCon_Printf(\"Unable to load video %s\\n\", videomap);\n\t\treturn;\n\t}\n\n\tcon = Con_Create(videomap, 0);\n\tif (!con)\n\t\treturn;\n\tcon->parseflags = PFS_FORCEUTF8;\n\tcon->flags = CONF_ISWINDOW;\n\tcon->wnd_x = vid.width/4;\n\tcon->wnd_y = vid.height/4;\n\tcon->wnd_w = vid.width/2;\n\tcon->wnd_h = vid.height/2;\n\tcon->linebuffered = NULL;\n\n\tQ_strncpyz(con->backimage, \"\", sizeof(con->backimage));\n\tif (con->backshader)\n\t\tR_UnloadShader(con->backshader);\n\tcon->backshader = s;\n\n\tCon_SetActive(con);\n}\n#endif\t//HAVE_MEDIA_DECODER\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n#ifdef HAVE_MEDIA_ENCODER\n\nsoundcardinfo_t *capture_fakesounddevice;\n\ndouble captureframeinterval;\t//interval between video frames\ndouble capturelastvideotime;\t//time of last video frame\nint captureframe;\nfbostate_t capturefbo;\nint captureoldfbo;\nqboolean capturingfbo;\ntexid_t\tcapturetexture;\nqboolean captureframeforce;\n\n#if defined(GLQUAKE) && !defined(GLESONLY)\n#define GLQUAKE_PBOS\n#endif\nstruct\n{\n#ifdef GLQUAKE_PBOS\n\tint pbo_handle;\n#endif\n\tenum uploadfmt format;\n\tint stride;\n\tint width;\n\tint height;\n} offscreen_queue[4];\t//ringbuffer of offscreen_captureframe...captureframe\nint offscreen_captureframe;\nenum uploadfmt offscreen_format;\n\n#define CAPTURECODECDESC_DEFAULT\t\"tga\"\n#ifdef HAVE_API_VFW\n#define CAPTUREDRIVERDESC_AVI \"avi: capture directly to avi (capturecodec should be a fourcc value).\\n\"\n#define CAPTURECODECDESC_AVI \"With (win)avi, this should be a fourcc code like divx or xvid, may be blank for raw video.\\n\"\n#else\n#define CAPTUREDRIVERDESC_AVI\n#define CAPTURECODECDESC_AVI\n#endif\n\nqboolean capturepaused;\nextern cvar_t vid_conautoscale;\ncvar_t capturerate = CVARD(\"capturerate\", \"30\", \"The framerate of the video to capture\");\ncvar_t capturewidth = CVARD(\"capturedemowidth\", \"0\", \"When using capturedemo, this specifies the width of the FBO image used. Can be larger than your physical monitor.\");\ncvar_t captureheight = CVARD(\"capturedemoheight\", \"0\", \"When using capturedemo, this specifies the height of the FBO image used.\");\ncvar_t capturedriver = CVARD(\"capturedriver\", \"\", \"The driver to use to capture the demo. Use the capture command with no arguments for a list of drivers.\");\ncvar_t capturecodec = CVARD(\"capturecodec\", CAPTURECODECDESC_DEFAULT, \"the compression/encoding codec to use.\\n\"CAPTURECODECDESC_AVI\"With raw capturing, this should be one of tga,png,jpg,pcx (ie: screenshot extensions).\");\ncvar_t capturesound = CVARD(\"capturesound\", \"1\", \"Enables the capturing of game voice. If not using capturedemo, this can be combined with cl_voip_test to capture your own voice.\");\ncvar_t capturesoundchannels = CVAR(\"capturesoundchannels\", \"2\");\ncvar_t capturesoundbits = CVAR(\"capturesoundbits\", \"16\");\ncvar_t capturemessage = CVAR(\"capturemessage\", \"\");\ncvar_t capturethrottlesize = CVARD(\"capturethrottlesize\", \"0\", \"If set, capturing will slow down significantly if there is less disk space available than this cvar (in mb). This is useful when recording a stream to (ram)disk while another program is simultaneously consuming frames.\");\nqboolean recordingdemo;\n\nmedia_encoder_funcs_t *currentcapture_funcs;\nvoid *currentcapture_ctx;\n\n\n#if 1\n/*screenshot capture*/\nstruct capture_raw_ctx\n{\n\tint frames;\n\tenum fs_relative fsroot;\n\tchar videonameprefix[MAX_QPATH];\n\tchar videonameextension[16];\n\tvfsfile_t *audio;\n};\n\nqboolean FS_FixPath(char *path, size_t pathsize);\nstatic void *QDECL capture_raw_begin (char *streamname, int videorate, int width, int height, int *sndkhz, int *sndchannels, int *sndbits)\n{\n\tchar filename[MAX_OSPATH];\n\tstruct capture_raw_ctx *ctx = Z_Malloc(sizeof(*ctx));\n\n\tif (!strcmp(capturecodec.string, \"png\") || !strcmp(capturecodec.string, \"jpeg\") || !strcmp(capturecodec.string, \"jpg\") || !strcmp(capturecodec.string, \"bmp\") || !strcmp(capturecodec.string, \"pcx\") || !strcmp(capturecodec.string, \"tga\"))\n\t\tQ_strncpyz(ctx->videonameextension, capturecodec.string, sizeof(ctx->videonameextension));\n\telse\n\t\tQ_strncpyz(ctx->videonameextension, \"tga\", sizeof(ctx->videonameextension));\n\n\tif (!FS_SystemPath(va(\"%s\", streamname), FS_GAMEONLY, ctx->videonameprefix, sizeof(ctx->videonameprefix)))\n\t{\n\t\tZ_Free(ctx);\n\t\treturn NULL;\n\t}\n\tif (!FS_FixPath(ctx->videonameprefix, sizeof(ctx->videonameprefix)))\n\t{\n\t\tZ_Free(ctx);\n\t\treturn NULL;\n\t}\n\tctx->fsroot = FS_SYSTEM;\n\n\tFS_CreatePath(ctx->videonameprefix, ctx->fsroot);\n\n\tctx->audio = NULL;\n\tif (*sndkhz)\n\t{\n\t\tif (*sndbits < 8)\n\t\t\t*sndbits = 8;\n\t\tif (*sndbits != 8)\n\t\t\t*sndbits = 16;\n\t\tif (*sndchannels > 6)\n\t\t\t*sndchannels = 6;\n\t\tif (*sndchannels < 1)\n\t\t\t*sndchannels = 1;\n\t\tQ_snprintfz(filename, sizeof(filename), \"%saudio_%ichan_%ikhz_%ib.raw\", ctx->videonameprefix, *sndchannels, *sndkhz/1000, *sndbits);\n\t\tctx->audio = FS_OpenVFS(filename, \"wb\", ctx->fsroot);\n\t}\n\tif (!ctx->audio)\n\t{\n\t\t*sndkhz = 0;\n\t\t*sndchannels = 0;\n\t\t*sndbits = 0;\n\t}\n\treturn ctx;\n}\nstatic void QDECL capture_raw_video (void *vctx, int frame, void *data, int stride, int width, int height, enum uploadfmt fmt)\n{\n\tstruct capture_raw_ctx *ctx = vctx;\n\tchar filename[MAX_OSPATH];\n\tctx->frames = frame+1;\n\tQ_snprintfz(filename, sizeof(filename), \"%s%8.8i.%s\", ctx->videonameprefix, frame, ctx->videonameextension);\n\tif (!SCR_ScreenShot(filename, ctx->fsroot, &data, 1, stride, width, height, fmt, true))\n\t{\n\t\tSys_Sleep(1);\n\t\tif (!SCR_ScreenShot(filename, ctx->fsroot, &data, 1, stride, width, height, fmt, true))\n\t\t\tCon_DPrintf(\"Error writing frame %s\\n\", filename);\n\t}\n\n\tif (capturethrottlesize.value)\n\t{\n\t\tchar base[MAX_QPATH];\n\t\tQ_strncpyz(base, ctx->videonameprefix, sizeof(base));\n\t\tif (FS_SystemPath(base, ctx->fsroot, filename, sizeof(filename)))\n\t\t{\n\t\t\tquint64_t diskfree = 0;\n\t\t\tif (Sys_GetFreeDiskSpace(filename, &diskfree))\n\t\t\t{\n\t\t\t\tCon_DLPrintf(2, \"Free Space: %\"PRIu64\", threshhold %\"PRIu64\"\\n\", diskfree, (quint64_t)(1024*1024*capturethrottlesize.value));\n\t\t\t\tif (diskfree < (quint64_t)(1024*1024*capturethrottlesize.value))\n\t\t\t\t\tSys_Sleep(1);\t//throttle\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tCon_Printf(\"%s: unable to query free disk space for %s. Disabling\\n\", capturethrottlesize.name, filename);\n\t\t\t\tcapturethrottlesize.ival = capturethrottlesize.value = 0;\n\t\t\t}\n\t\t}\n\t}\n}\nstatic void QDECL capture_raw_audio (void *vctx, void *data, int bytes)\n{\n\tstruct capture_raw_ctx *ctx = vctx;\n\n\tif (ctx->audio)\n\t\tVFS_WRITE(ctx->audio, data, bytes);\n}\nstatic void QDECL capture_raw_end (void *vctx)\n{\n\tstruct capture_raw_ctx *ctx = vctx;\n\tif (ctx->audio)\n\t\tVFS_CLOSE(ctx->audio);\n\tCon_Printf(\"%d video frames captured\\n\", ctx->frames);\n\tZ_Free(ctx);\n}\nstatic media_encoder_funcs_t capture_raw =\n{\n\tsizeof(media_encoder_funcs_t),\n\t\"raw\",\n\t\"Saves the video as a series of image files within the named sub directory, one for each frame. Audio is recorded as raw pcm.\",\n\tNULL,\n\tcapture_raw_begin,\n\tcapture_raw_video,\n\tcapture_raw_audio,\n\tcapture_raw_end\n};\n#endif\n#if defined(HAVE_API_VFW)\n\n/*screenshot capture*/\nstruct capture_avi_ctx\n{\n\tPAVIFILE file;\n\t#define avi_video_stream(ctx) (ctx->codec_fourcc?ctx->compressed_video_stream:ctx->uncompressed_video_stream)\n\tPAVISTREAM uncompressed_video_stream;\n\tPAVISTREAM compressed_video_stream;\n\tPAVISTREAM uncompressed_audio_stream;\n\tWAVEFORMATEX wave_format;\n\tunsigned long codec_fourcc;\n\n\tint audio_frame_counter;\n};\n\nstatic void QDECL capture_avi_end(void *vctx)\n{\n\tstruct capture_avi_ctx *ctx = vctx;\n\n\tif (ctx->uncompressed_video_stream)\tqAVIStreamRelease(ctx->uncompressed_video_stream);\n\tif (ctx->compressed_video_stream)\tqAVIStreamRelease(ctx->compressed_video_stream);\n\tif (ctx->uncompressed_audio_stream)\tqAVIStreamRelease(ctx->uncompressed_audio_stream);\n\tif (ctx->file)\t\t\t\t\t\tqAVIFileRelease(ctx->file);\n\tZ_Free(ctx);\n}\n\nstatic void *QDECL capture_avi_begin (char *streamname, int videorate, int width, int height, int *sndkhz, int *sndchannels, int *sndbits)\n{\n\tstruct capture_avi_ctx *ctx = Z_Malloc(sizeof(*ctx));\n\tHRESULT hr;\n\tBITMAPINFOHEADER bitmap_info_header;\n\tAVISTREAMINFOA stream_header;\n\tFILE *f;\n\tchar aviname[256];\n\tchar nativepath[256];\n\n\tchar *fourcc = capturecodec.string;\n\n\tif (strlen(fourcc) == 4)\n\t\tctx->codec_fourcc = mmioFOURCC(*(fourcc+0), *(fourcc+1), *(fourcc+2), *(fourcc+3));\n\telse\n\t\tctx->codec_fourcc = 0;\n\n\tif (!qAVIStartup())\n\t{\n\t\tCon_Printf(\"vfw support not available.\\n\");\n\t\tcapture_avi_end(ctx);\n\t\treturn NULL;\n\t}\n\n\t/*convert to foo.avi*/\n\tCOM_StripExtension(streamname, aviname, sizeof(aviname));\n\tCOM_DefaultExtension (aviname, \".avi\", sizeof(aviname));\n\t/*find the system location of that*/\n\tFS_SystemPath(aviname, FS_GAMEONLY, nativepath, sizeof(nativepath));\n\n\t//wipe it.\n\tf = fopen(nativepath, \"rb\");\n\tif (f)\n\t{\n\t\tfclose(f);\n\t\tunlink(nativepath);\n\t}\n\n\thr = qAVIFileOpenA(&ctx->file, nativepath, OF_WRITE | OF_CREATE, NULL);\n\tif (FAILED(hr))\n\t{\n\t\tCon_Printf(\"Failed to open %s\\n\", nativepath);\n\t\tcapture_avi_end(ctx);\n\t\treturn NULL;\n\t}\n\n\n\tmemset(&bitmap_info_header, 0, sizeof(BITMAPINFOHEADER));\n\tbitmap_info_header.biSize = 40;\n\tbitmap_info_header.biWidth = width;\n\tbitmap_info_header.biHeight = height;\n\tbitmap_info_header.biPlanes = 1;\n\tbitmap_info_header.biBitCount = 24;\n\tbitmap_info_header.biCompression = BI_RGB;\n\tbitmap_info_header.biSizeImage = width*height * 3;\n\n\n\tmemset(&stream_header, 0, sizeof(stream_header));\n\tstream_header.fccType = streamtypeVIDEO;\n\tstream_header.fccHandler = ctx->codec_fourcc;\n\tstream_header.dwScale = 100;\n\tstream_header.dwRate = (unsigned long)(0.5 + 100.0/captureframeinterval);\n\tSetRect(&stream_header.rcFrame, 0, 0, width, height);\n\n\thr = qAVIFileCreateStreamA(ctx->file, &ctx->uncompressed_video_stream, &stream_header);\n\tif (FAILED(hr))\n\t{\n\t\tCon_Printf(\"Couldn't initialise the stream, check codec\\n\");\n\t\tcapture_avi_end(ctx);\n\t\treturn NULL;\n\t}\n\n\tif (ctx->codec_fourcc)\n\t{\n\t\tAVICOMPRESSOPTIONS opts;\n\t\tmemset(&opts, 0, sizeof(opts));\n\t\topts.fccType = stream_header.fccType;\n\t\topts.fccHandler = ctx->codec_fourcc;\n\t\t// Make the stream according to compression\n\t\thr = qAVIMakeCompressedStream(&ctx->compressed_video_stream, ctx->uncompressed_video_stream, &opts, NULL);\n\t\tif (FAILED(hr))\n\t\t{\n\t\t\tCon_Printf(\"AVIMakeCompressedStream failed. check video codec.\\n\");\n\t\t\tcapture_avi_end(ctx);\n\t\t\treturn NULL;\n\t\t}\n\t}\n\n\n\thr = qAVIStreamSetFormat(avi_video_stream(ctx), 0, &bitmap_info_header, sizeof(BITMAPINFOHEADER));\n\tif (FAILED(hr))\n\t{\n\t\tCon_Printf(\"AVIStreamSetFormat failed\\n\");\n\t\tcapture_avi_end(ctx);\n\t\treturn NULL;\n\t}\n\n\tif (*sndbits != 8 && *sndbits != 16)\n\t\t*sndbits = 8;\n\tif (*sndchannels < 1 && *sndchannels > 6)\n\t\t*sndchannels = 1;\n\n\tif (*sndkhz)\n\t{\n\t\tmemset(&ctx->wave_format, 0, sizeof(WAVEFORMATEX));\n\t\tctx->wave_format.wFormatTag = WAVE_FORMAT_PCM;\n\t\tctx->wave_format.nChannels = *sndchannels;\n\t\tctx->wave_format.nSamplesPerSec = *sndkhz;\n\t\tctx->wave_format.wBitsPerSample = *sndbits;\n\t\tctx->wave_format.nBlockAlign = ctx->wave_format.wBitsPerSample/8 * ctx->wave_format.nChannels;\n\t\tctx->wave_format.nAvgBytesPerSec = ctx->wave_format.nSamplesPerSec * ctx->wave_format.nBlockAlign;\n\t\tctx->wave_format.cbSize = 0;\n\n\n\t\tmemset(&stream_header, 0, sizeof(stream_header));\n\t\tstream_header.fccType = streamtypeAUDIO;\n\t\tstream_header.dwScale = ctx->wave_format.nBlockAlign;\n\t\tstream_header.dwRate = stream_header.dwScale * (unsigned long)ctx->wave_format.nSamplesPerSec;\n\t\tstream_header.dwSampleSize = ctx->wave_format.nBlockAlign;\n\n\t\t//FIXME: be prepared to capture audio to mp3.\n\n\t\thr = qAVIFileCreateStreamA(ctx->file, &ctx->uncompressed_audio_stream, &stream_header);\n\t\tif (FAILED(hr))\n\t\t{\n\t\t\tcapture_avi_end(ctx);\n\t\t\treturn NULL;\n\t\t}\n\n\t\thr = qAVIStreamSetFormat(ctx->uncompressed_audio_stream, 0, &ctx->wave_format, sizeof(WAVEFORMATEX));\n\t\tif (FAILED(hr))\n\t\t{\n\t\t\tcapture_avi_end(ctx);\n\t\t\treturn NULL;\n\t\t}\n\t}\n\treturn ctx;\n}\n\nstatic void QDECL capture_avi_video(void *vctx, int frame, void *vdata, int stride, int width, int height, enum uploadfmt fmt)\n{\n\t//vfw api is bottom up.\n\tstruct capture_avi_ctx *ctx = vctx;\n\tqbyte *data, *in, *out;\n\tint x, y;\n\n\tif (stride < 0)\t//if stride is negative, then its bottom-up, but the data pointer is at the start of the buffer (rather than 'first row')\n\t\tvdata = (char*)vdata - stride*(height-1);\n\n\t//we need to output a packed bottom-up bgr image.\n\n\t//switch the input from logical-top-down to bottom-up (regardless of the physical ordering of its rows)\n\tin = (qbyte*)vdata + stride*(height-1);\n\tstride = -stride;\n\n\tif (fmt == TF_BGR24 && stride == width*-3)\n\t{\t//no work needed!\n\t\tdata = in;\n\t}\n\telse\n\t{\n\t\tint ipx = (fmt == TF_BGR24||fmt == TF_RGB24)?3:4;\n\t\tdata = out = Hunk_TempAlloc(width*height*3);\n\t\tif (fmt == TF_RGB24 || fmt == TF_RGBX32 || fmt == TF_RGBA32)\n\t\t{\t//byteswap + strip alpha\n\t\t\tfor (y = height; y --> 0; out += width*3, in += stride)\n\t\t\t{\n\t\t\t\tfor (x = 0; x < width; x++)\n\t\t\t\t{\n\t\t\t\t\tout[x*3+0] = in[x*ipx+2];\n\t\t\t\t\tout[x*3+1] = in[x*ipx+1];\n\t\t\t\t\tout[x*3+2] = in[x*ipx+0];\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse if (fmt == TF_BGR24 || fmt == TF_BGRX32 || fmt == TF_BGRA32)\n\t\t{\t//just strip alpha (or just flip)\n\t\t\tfor (y = height; y --> 0; out += width*3, in += stride)\n\t\t\t{\n\t\t\t\tfor (x = 0; x < width; x++)\n\t\t\t\t{\n\t\t\t\t\tout[x*3+0] = in[x*ipx+0];\n\t\t\t\t\tout[x*3+1] = in[x*ipx+1];\n\t\t\t\t\tout[x*3+2] = in[x*ipx+2];\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\t//probably spammy, but oh well\n\t\t\tCon_Printf(\"capture_avi_video: Unsupported image format\\n\");\n\t\t\treturn;\n\t\t}\n\t}\n\n\t//FIXME: if we're allocating memory anyway, can we not push this to a thread?\n\n\t//write it\n\tif (FAILED(qAVIStreamWrite(avi_video_stream(ctx), frame, 1, data, width*height * 3, ((frame%15) == 0)?AVIIF_KEYFRAME:0, NULL, NULL)))\n\t\tCon_DPrintf(\"Recoring error\\n\");\n}\n\nstatic void QDECL capture_avi_audio(void *vctx, void *data, int bytes)\n{\n\tstruct capture_avi_ctx *ctx = vctx;\n\tif (ctx->uncompressed_audio_stream)\n\t\tqAVIStreamWrite(ctx->uncompressed_audio_stream, ctx->audio_frame_counter++, 1, data, bytes, AVIIF_KEYFRAME, NULL, NULL);\n}\n\nstatic media_encoder_funcs_t capture_avi =\n{\n\tsizeof(media_encoder_funcs_t),\n\t\"avi\",\n\t\"Uses Windows' VideoForWindows API to record avi files. Codecs are specified with a fourcc, for instance 'xvid' or 'x264', depending on which ones you have installed.\",\n\t\".avi\",\n\tcapture_avi_begin,\n\tcapture_avi_video,\n\tcapture_avi_audio,\n\tcapture_avi_end\n};\n#endif\n\n#ifdef _DEBUG\nstruct capture_null_context\n{\n\tfloat starttime;\n\tint frames;\n};\nstatic void QDECL capture_null_end(void *vctx)\n{\n\tstruct capture_null_context *ctx = vctx;\n\tfloat duration = Sys_DoubleTime() - ctx->starttime;\n\tCon_Printf(\"%d video frames ignored, %g secs, %gfps\\n\", ctx->frames, duration, ctx->frames/duration);\n\tZ_Free(ctx);\n}\nstatic void *QDECL capture_null_begin (char *streamname, int videorate, int width, int height, int *sndkhz, int *sndchannels, int *sndbits)\n{\n\tstruct capture_null_context *ctx = Z_Malloc(sizeof(*ctx));\n\t*sndkhz = 11025;\n\t*sndchannels = 2;\n\t*sndbits = 32;\t//floats!\n\tctx->starttime = Sys_DoubleTime();\n\treturn ctx;\n}\nstatic void QDECL capture_null_video(void *vctx, int frame, void *vdata, int stride, int width, int height, enum uploadfmt fmt)\n{\n\tstruct capture_null_context *ctx = vctx;\n\tctx->frames = frame+1;\n}\nstatic void QDECL capture_null_audio(void *vctx, void *data, int bytes)\n{\n}\nstatic media_encoder_funcs_t capture_null =\n{\n\tsizeof(media_encoder_funcs_t),\n\t\"null\",\n\t\"An encoder that doesn't actually encode or write anything, for debugging.\",\n\t\".null\",\n\tcapture_null_begin,\n\tcapture_null_video,\n\tcapture_null_audio,\n\tcapture_null_end\n};\n#endif\n\nstatic media_encoder_funcs_t *pluginencodersfunc[8];\nstatic struct plugin_s *pluginencodersplugin[8];\nqboolean Media_RegisterEncoder(struct plugin_s *plug, media_encoder_funcs_t *funcs)\n{\n\tint i;\n\tif (!funcs || funcs->structsize != sizeof(*funcs))\n\t\treturn false;\n\tfor (i = 0; i < sizeof(pluginencodersfunc)/sizeof(pluginencodersfunc[0]); i++)\n\t{\n\t\tif (pluginencodersfunc[i] == NULL)\n\t\t{\n\t\t\tpluginencodersfunc[i] = funcs;\n\t\t\tpluginencodersplugin[i] = plug;\n\t\t\treturn true;\n\t\t}\n\t}\n\treturn false;\n}\nvoid Media_StopRecordFilm_f(void);\n/*funcs==null closes ALL decoders from this plugin*/\nqboolean Media_UnregisterEncoder(struct plugin_s *plug, media_encoder_funcs_t *funcs)\n{\n\tqboolean success = false;\n\tint i;\n\n\tfor (i = 0; i < sizeof(pluginencodersfunc)/sizeof(pluginencodersfunc[0]); i++)\n\t{\n\t\tif (pluginencodersplugin[i])\n\t\tif (pluginencodersfunc[i] == funcs || (!funcs && pluginencodersplugin[i] == plug))\n\t\t{\n\t\t\tif (currentcapture_funcs == pluginencodersfunc[i])\n\t\t\t\tMedia_StopRecordFilm_f();\n\t\t\tsuccess = true;\n\t\t\tpluginencodersfunc[i] = NULL;\n\t\t\tpluginencodersplugin[i] = NULL;\n\t\t\tif (funcs)\n\t\t\t\treturn success;\n\t\t}\n\t}\n\treturn success;\n}\n \n//returns 0 if not capturing. 1 if capturing live. 2 if capturing a demo (where frame timings are forced).\nint Media_Capturing (void)\n{\n\tif (!currentcapture_funcs)\n\t\treturn 0;\n\treturn captureframeforce?2:1;\n}\n\nvoid Media_CapturePause_f (void)\n{\n\tcapturepaused = !capturepaused;\n}\n\nqboolean Media_PausedDemo (qboolean fortiming)\n{\n\t//if fortiming is set, then timing might need to advance if we still need to parse the demo to get the first valid data out of it.\n\n\tif (capturepaused)\n\t\treturn true;\n\n\t//capturedemo doesn't record any frames when the console is visible\n\t//but that's okay, as we don't load any demo frames either.\n\tif ((cls.demoplayback && Media_Capturing()))\n\t\tif (Key_Dest_Has(~kdm_game) || scr_con_current > 0 || (!fortiming&&!cl.validsequence))\n\t\t\treturn true;\n\n\treturn false;\n}\n\nstatic qboolean Media_ForceTimeInterval(void)\n{\n\treturn (cls.demoplayback && Media_Capturing() && captureframeinterval>0 && !Media_PausedDemo(false));\n}\n\ndouble Media_TweekCaptureFrameTime(double oldtime, double time)\n{\n\tif (Media_ForceTimeInterval())\n\t{\n\t\tcaptureframeforce = true;\n\t\t//if we're forcing time intervals, then we use fixed time increments and generate a new video frame for every single frame.\n\t\treturn capturelastvideotime;\n\t}\n\treturn oldtime + time;\n}\n\n#ifdef VKQUAKE\nstatic void Media_CapturedFrame (void *data, qintptr_t bytestride, size_t width, size_t height, enum uploadfmt fmt)\n{\n\tif (currentcapture_funcs)\n\t\tcurrentcapture_funcs->capture_video(currentcapture_ctx, offscreen_captureframe, data, bytestride, width, height, fmt);\n\toffscreen_captureframe++;\n}\n#endif\n\nvoid Media_RecordFrame (void)\n{\n\tchar *buffer;\n\tint bytestride, truewidth, trueheight;\n\tenum uploadfmt fmt;\n\n\tif (!currentcapture_funcs)\n\t\treturn;\n\n/*\tif (*capturecutoff.string && captureframe * captureframeinterval > capturecutoff.value*60)\n\t{\n\t\tcurrentcapture_funcs->capture_end(currentcapture_ctx);\n\t\tcurrentcapture_ctx = currentcapture_funcs->capture_begin(Cmd_Argv(1), capturerate.value, vid.pixelwidth, vid.pixelheight, &sndkhz, &sndchannels, &sndbits);\n\t\tif (!currentcapture_ctx)\n\t\t{\n\t\t\tcurrentcapture_funcs = NULL;\n\t\t\treturn;\n\t\t}\n\t\tcaptureframe = 0;\n\t}\n*/\n\tif (Media_PausedDemo(false))\n\t{\n\t\tint y = vid.height -32-16;\n\t\tif (y < scr_con_current) y = scr_con_current;\n\t\tif (y > vid.height-8)\n\t\t\ty = vid.height-8;\n\n#ifdef GLQUAKE\n\t\tif (capturingfbo && qrenderer == QR_OPENGL)\n\t\t{\n\t\t\tshader_t *pic;\n\t\t\tGLBE_FBO_Pop(captureoldfbo);\n\t\t\tvid.framebuffer = NULL;\n\t\t\tGL_Set2D(false);\n\n\t\t\tpic = R_RegisterShader(\"capturdemofeedback\", SUF_NONE,\n\t\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\t\"program default2d\\n\"\n\t\t\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\t\t\"map $diffuse\\n\"\n\t\t\t\t\t\t\t\"}\\n\"\n\t\t\t\t\t\t\"}\\n\");\n\t\t\tpic->defaulttextures->base = capturetexture;\n\t\t\t//pulse green slightly, so its a bit more obvious\n\t\t\tR2D_ImageColours(1, 1+0.2*sin(realtime), 1, 1);\n\t\t\tR2D_Image(0, 0, vid.width, vid.height, 0, 1, 1, 0, pic);\n\t\t\tR2D_ImageColours(1, 1, 1, 1);\n\t\t\tDraw_FunString(0, 0, S_COLOR_RED\"PAUSED\");\n\t\t\tif (R2D_Flush)\n\t\t\t\tR2D_Flush();\n\n\t\t\tcaptureoldfbo = GLBE_FBO_Push(&capturefbo);\n\t\t\tvid.framebuffer = capturetexture;\n\t\t\tGL_Set2D(false);\n\t\t}\n\t\telse\n#endif\n\t\t\tDraw_FunString((strlen(capturemessage.string)+1)*8, y, S_COLOR_RED \"PAUSED\");\n\n\t\tif (captureframeforce)\n\t\t\tcapturelastvideotime += captureframeinterval;\n\t\treturn;\n\t}\n\n\t//don't capture frames while we're loading.\n\tif (cl.sendprespawn || (cls.state < ca_active && COM_HasWork()))\n\t{\n\t\tcapturelastvideotime += captureframeinterval;\n\t\treturn;\n\t}\n\n//overlay this on the screen, so it appears in the film\n\tif (*capturemessage.string)\n\t{\n\t\tint y = vid.height -32-16;\n\t\tif (y < scr_con_current) y = scr_con_current;\n\t\tif (y > vid.height-8)\n\t\t\ty = vid.height-8;\n\t\tDraw_FunString(0, y, capturemessage.string);\n\t}\n\n\t//time for another frame?\n\tif (!captureframeforce)\n\t{\n\t\tif (capturelastvideotime > realtime+1)\n\t\t\tcapturelastvideotime = realtime;\t//urm, wrapped?..\n\t\tif (capturelastvideotime > realtime)\n\t\t\tgoto skipframe;\n\t}\n\t\n\tif (cls.findtrack)\n\t{\n\t\tcapturelastvideotime += captureframeinterval;\n\t\treturn;\t//skip until we're tracking the right player.\n\t}\n\n\tif (R2D_Flush)\n\t\tR2D_Flush();\n\n#ifdef GLQUAKE_PBOS\n\tif (offscreen_format != TF_INVALID && qrenderer == QR_OPENGL)\n\t{\n\t\tint frame;\n\t\t//encode the frame if we're about to stomp on it\n\t\twhile (offscreen_captureframe + countof(offscreen_queue) <= captureframe)\n\t\t{\n\t\t\tframe = offscreen_captureframe%countof(offscreen_queue);\n\t\t\tqglBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, offscreen_queue[frame].pbo_handle);\n\t\t\tbuffer = qglMapBufferARB(GL_PIXEL_PACK_BUFFER_ARB, GL_READ_ONLY_ARB);\n\t\t\tif (buffer)\n\t\t\t{\n\t\t\t\t//FIXME: thread these (with audio too, to avoid races)\n\t\t\t\tcurrentcapture_funcs->capture_video(currentcapture_ctx, offscreen_captureframe, buffer, offscreen_queue[frame].stride, offscreen_queue[frame].width, offscreen_queue[frame].height, offscreen_queue[frame].format);\n\t\t\t\tqglUnmapBufferARB(GL_PIXEL_PACK_BUFFER_ARB);\n\t\t\t}\n\t\t\toffscreen_captureframe++;\n\t\t}\n\n\t\tframe = captureframe%countof(offscreen_queue);\n\t\t//if we have no pbo yet, create one.\n\t\tif (!offscreen_queue[frame].pbo_handle || offscreen_queue[frame].width != vid.fbpwidth || offscreen_queue[frame].height != vid.fbpheight)\n\t\t{\n\t\t\tint imagesize = 0;\n\t\t\tif (offscreen_queue[frame].pbo_handle)\n\t\t\t\tqglDeleteBuffersARB(1, &offscreen_queue[frame].pbo_handle);\n\t\t\toffscreen_queue[frame].format = offscreen_format;\n\t\t\toffscreen_queue[frame].width = vid.fbpwidth;\n\t\t\toffscreen_queue[frame].height = vid.fbpheight;\n\t\t\tswitch(offscreen_queue[frame].format)\n\t\t\t{\n\t\t\tcase TF_BGR24:\n\t\t\tcase TF_RGB24:\n\t\t\t\timagesize = 3;\n\t\t\t\tbreak;\n\t\t\tcase TF_BGRA32:\n\t\t\tcase TF_RGBA32:\n\t\t\t\timagesize = 4;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\toffscreen_queue[frame].stride = vid.fbpwidth*-imagesize;//gl is upside down\n\n\t\t\timagesize *= offscreen_queue[frame].width * offscreen_queue[frame].height;\n\n\t\t\tqglGenBuffersARB(1, &offscreen_queue[frame].pbo_handle);\n\t\t\tqglBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, offscreen_queue[frame].pbo_handle);\n\t\t\tqglBufferDataARB(GL_PIXEL_PACK_BUFFER_ARB, imagesize, NULL, GL_STREAM_READ_ARB);\n\t\t}\n\n\t\t//get the gpu to copy the texture into the pbo. the driver should pipeline this read until we actually map the pbo, hopefully avoiding stalls\n\t\tqglBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, offscreen_queue[frame].pbo_handle);\n\t\tswitch(offscreen_queue[frame].format)\n\t\t{\n\t\tcase TF_BGR24:\n\t\t\tqglReadPixels(0, 0, offscreen_queue[frame].width, offscreen_queue[frame].height, GL_BGR_EXT, GL_UNSIGNED_BYTE, 0);\n\t\t\tbreak;\n\t\tcase TF_BGRA32:\n\t\t\tqglReadPixels(0, 0, offscreen_queue[frame].width, offscreen_queue[frame].height, GL_BGRA_EXT, GL_UNSIGNED_INT_8_8_8_8_REV, 0);\n\t\t\tbreak;\n\t\tcase TF_RGB24:\n\t\t\tqglReadPixels(0, 0, offscreen_queue[frame].width, offscreen_queue[frame].height, GL_RGB, GL_UNSIGNED_BYTE, 0);\n\t\t\tbreak;\n\t\tcase TF_RGBA32:\n\t\t\tqglReadPixels(0, 0, offscreen_queue[frame].width, offscreen_queue[frame].height, GL_RGBA, GL_UNSIGNED_BYTE, 0);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tbreak;\n\t\t}\n\t\tqglBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0);\n\t}\n\telse\n#endif\n#ifdef VKQUAKE\n\tif (qrenderer == QR_VULKAN)\n\t\tVKVID_QueueGetRGBData(Media_CapturedFrame);\n\telse\n#endif\n\t{\n\t\toffscreen_captureframe = captureframe+1;\n\t\t//submit the current video frame. audio will be mixed to match.\n\t\tbuffer = VID_GetRGBInfo(&bytestride, &truewidth, &trueheight, &fmt);\n\t\tif (buffer)\n\t\t{\n\t\t\tqbyte *firstrow = (bytestride<0)?buffer - bytestride*(trueheight-1):buffer;\n\t\t\tcurrentcapture_funcs->capture_video(currentcapture_ctx, captureframe, firstrow, bytestride, truewidth, trueheight, fmt);\n\t\t\tBZ_Free (buffer);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tCon_DPrintf(\"Unable to grab video image\\n\");\n\t\t\tcurrentcapture_funcs->capture_video(currentcapture_ctx, captureframe, NULL, 0, 0, 0, TF_INVALID);\n\t\t}\n\t}\n\tcaptureframe++;\n\tcapturelastvideotime += captureframeinterval;\n\n\tcaptureframeforce = false;\n\n\t//this is drawn to the screen and not the film\nskipframe:\n{\n\tint y = vid.height -32-16;\n\tif (y < scr_con_current) y = scr_con_current;\n\tif (y > vid.height-8)\n\t\ty = vid.height-8;\n\n#ifdef GLQUAKE\n\tif (capturingfbo && qrenderer == QR_OPENGL)\n\t{\n\t\tshader_t *pic;\n\t\tGLBE_FBO_Pop(captureoldfbo);\n\t\tvid.framebuffer = NULL;\n\t\tGL_Set2D(false);\n\n\t\tpic = R_RegisterShader(\"capturdemofeedback\", SUF_NONE,\n\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\"program default2d\\n\"\n\t\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\t\"map $diffuse\\n\"\n\t\t\t\t\t\t\"}\\n\"\n\t\t\t\t\t\"}\\n\");\n\t\tpic->defaulttextures->base = capturetexture;\n\t\t//pulse green slightly, so its a bit more obvious\n\t\tR2D_ImageColours(1, 1+0.2*sin(realtime), 1, 1);\n\t\tR2D_Image(0, 0, vid.width, vid.height, 0, 1, 1, 0, pic);\n\t\tR2D_ImageColours(1, 1, 1, 1);\n\t\tDraw_FunString(0, 0, va(S_COLOR_RED\"RECORDING OFFSCREEN %g\", capturelastvideotime));\n\t\tif (R2D_Flush)\n\t\t\tR2D_Flush();\n\n\t\tcaptureoldfbo = GLBE_FBO_Push(&capturefbo);\n\t\tvid.framebuffer = capturetexture;\n\t\tGL_Set2D(false);\n\t}\n\telse\n#endif\n\t\tDraw_FunString((strlen(capturemessage.string)+1)*8, y, S_COLOR_RED\"RECORDING\");\n}\n}\n\nstatic void *MSD_Lock (soundcardinfo_t *sc, unsigned int *sampidx)\n{\n\treturn sc->sn.buffer;\n}\nstatic void MSD_Unlock (soundcardinfo_t *sc, void *buffer)\n{\n}\n\nstatic unsigned int MSD_GetDMAPos(soundcardinfo_t *sc)\n{\n\tint\t\ts;\n\n\ts = captureframe*(sc->sn.speed*captureframeinterval);\n\n\n//\ts >>= sc->sn.samplebytes - 1;\n\ts *= sc->sn.numchannels;\n\treturn s;\n}\n\nstatic void MSD_Submit(soundcardinfo_t *sc, int start, int end)\n{\n\t//Fixme: support outputting to wav\n\t//http://www.borg.com/~jglatt/tech/wave.htm\n\n\n\tint lastpos;\n\tint newpos;\n\tint framestosubmit;\n\tint offset;\n\tint bytesperframe;\n\tint minframes = 576;\t//mp3 requires this (which we use for vfw)\n\tint maxframes = 576;\n\n\tnewpos = sc->paintedtime;\n\n\twhile(1)\n\t{\n\t\tlastpos = sc->snd_completed;\n\t\tframestosubmit = newpos - lastpos;\n\t\tif (framestosubmit < minframes)\n\t\t\treturn;\n\t\tif (framestosubmit > maxframes)\n\t\t\tframestosubmit = maxframes;\n\n\t\tbytesperframe = sc->sn.numchannels*sc->sn.samplebytes;\n\n\t\toffset = (lastpos % (sc->sn.samples/sc->sn.numchannels));\n\n\t\t//we could just use a buffer size equal to the number of samples in each frame\n\t\t//but that isn't as robust when it comes to floating point imprecisions\n\t\t//namly: that it would loose a sample each frame with most framerates.\n\n\t\tif ((sc->snd_completed % (sc->sn.samples/sc->sn.numchannels)) < offset)\n\t\t{\n\t\t\tframestosubmit = ((sc->sn.samples/sc->sn.numchannels)) - offset;\n\t\t\toffset = 0;\n\t\t}\n\t\tcurrentcapture_funcs->capture_audio(currentcapture_ctx, sc->sn.buffer+offset*bytesperframe, framestosubmit*bytesperframe);\n\t\tsc->snd_completed += framestosubmit;\n\t}\n}\n\nstatic void MSD_Shutdown (soundcardinfo_t *sc)\n{\n\tZ_Free(sc->sn.buffer);\n\tcapture_fakesounddevice = NULL;\n}\n\nvoid Media_InitFakeSoundDevice (int speed, int channels, int samplebits)\n{\n\tsoundcardinfo_t *sc;\n\n\tif (capture_fakesounddevice)\n\t\treturn;\n\n\t//when we're recording a demo, we'll be timedemoing it as it were.\n\t//this means that the actual sound devices and the fake device will be going at different rates\n\t//which really confuses any stream decoding, like music.\n\t//so just kill all actual sound devices.\n\tif (recordingdemo)\n\t{\n\t\tsoundcardinfo_t *next;\n\t\tfor (sc = sndcardinfo; sc; sc=next)\n\t\t{\n\t\t\tnext = sc->next;\n\t\t\tsc->Shutdown(sc);\n\t\t\tZ_Free(sc);\n\t\t\tsndcardinfo = next;\n\t\t}\n\t}\n\n\tif (!snd_speed)\n\t\tsnd_speed = speed;\n\n\tsc = Z_Malloc(sizeof(soundcardinfo_t));\n\n\tsc->seat = -1;\n\tsc->snd_sent = 0;\n\tsc->snd_completed = 0;\n\n\tsc->sn.samples = speed*0.5;\n\tsc->sn.speed = speed;\n\tswitch(samplebits)\n\t{\n\tcase 32:\n\t\tsc->sn.samplebytes = 4;\n\t\tsc->sn.sampleformat = QSF_F32;\n\t\tbreak;\n\tdefault:\n\tcase 16:\n\t\tsc->sn.samplebytes = 2;\n\t\tsc->sn.sampleformat = QSF_S16;\n\t\tbreak;\n\tcase 8:\n\t\tsc->sn.samplebytes = 1;\n\t\tsc->sn.sampleformat = QSF_U8;\n\t\tbreak;\n\t}\n\tsc->sn.samplepos = 0;\n\tsc->sn.numchannels = channels;\n\tsc->inactive_sound = true;\n\n\tsc->sn.samples -= sc->sn.samples%1152;\t//truncate slightly to keep vfw happy.\n\tsc->samplequeue = -1;\n\n\tsc->sn.buffer = (unsigned char *) BZ_Malloc(sc->sn.samples*sc->sn.numchannels*sc->sn.samplebytes);\n\n\tZ_ReallocElements((void**)&sc->channel, &sc->max_chans, NUM_AMBIENTS+NUM_MUSICS, sizeof(*sc->channel));\n\n\tsc->Lock\t\t= MSD_Lock;\n\tsc->Unlock\t\t= MSD_Unlock;\n\tsc->Submit\t\t= MSD_Submit;\n\tsc->Shutdown\t= MSD_Shutdown;\n\tsc->GetDMAPos\t= MSD_GetDMAPos;\n\n\tsc->next = sndcardinfo;\n\tsndcardinfo = sc;\n\n\tcapture_fakesounddevice = sc;\n\n\tS_DefaultSpeakerConfiguration(sc);\n}\n\n//stops capturing and destroys everything.\nstatic void Media_FlushCapture(void)\n{\n#ifdef GLQUAKE_PBOS\n\tif (offscreen_format && qrenderer == QR_OPENGL)\n\t{\n\t\tint frame;\n\t\tqbyte *buffer;\n\t\twhile (offscreen_captureframe < captureframe)\n\t\t{\n\t\t\tframe = offscreen_captureframe%countof(offscreen_queue);\n\t\t\tqglBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, offscreen_queue[frame].pbo_handle);\n\t\t\tbuffer = qglMapBufferARB(GL_PIXEL_PACK_BUFFER_ARB, GL_READ_ONLY_ARB);\n\t\t\tif (buffer)\n\t\t\t{\n\t\t\t\tcurrentcapture_funcs->capture_video(currentcapture_ctx, offscreen_captureframe, buffer, offscreen_queue[frame].stride, offscreen_queue[frame].width, offscreen_queue[frame].height, offscreen_queue[frame].format);\n\t\t\t\tqglUnmapBufferARB(GL_PIXEL_PACK_BUFFER_ARB);\n\t\t\t}\n\t\t\toffscreen_captureframe++;\n\t\t}\n\n\t\tfor (frame = 0; frame < countof(offscreen_queue); frame++)\n\t\t{\n\t\t\tif (offscreen_queue[frame].pbo_handle)\n\t\t\t\tqglDeleteBuffersARB(1, &offscreen_queue[frame].pbo_handle);\n\t\t\tmemset(&offscreen_queue[frame], 0, sizeof(offscreen_queue[frame]));\n\t\t}\n\t}\n#endif\n#if 0//def VKQUAKE\n\tif (pbo_format && qrenderer == QR_VULKAN)\n\t{\n\t\tint frame;\n\t\twhile (offscreen_captureframe < captureframe)\n\t\t{\n\t\t\tframe = offscreen_captureframe%countof(offscreen_queue);\n\t\t\tqbyte *buffer;\n\t\t\tqglBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, offscreen_queue[frame].pbo_handle);\n\t\t\tbuffer = qglMapBufferARB(GL_PIXEL_PACK_BUFFER_ARB, GL_READ_ONLY_ARB);\n\t\t\tif (buffer)\n\t\t\t{\n\t\t\t\tcurrentcapture_funcs->capture_video(currentcapture_ctx, buffer, offscreen_captureframe, offscreen_queue[frame].width, offscreen_queue[frame].height, offscreen_queue[frame].format);\n\t\t\t\tqglUnmapBufferARB(GL_PIXEL_PACK_BUFFER_ARB);\n\t\t\t}\n\t\t\toffscreen_captureframe++;\n\t\t}\n\n\t\tfor (frame = 0; frame < countof(offscreen_queue); frame++)\n\t\t{\n\t\t\tif (offscreen_queue[frame].pbo_handle)\n\t\t\t\tqglDeleteBuffersARB(1, &offscreen_queue[frame].pbo_handle);\n\t\t\tmemset(&offscreen_queue[frame], 0, sizeof(offscreen_queue[frame]));\n\t\t}\n\t}\n#endif\n\n#ifdef GLQUAKE\n\tif (capturingfbo && qrenderer == QR_OPENGL)\n\t{\n\t\tGLBE_FBO_Pop(captureoldfbo);\n\t\tGLBE_FBO_Destroy(&capturefbo);\n\t\tvid.framebuffer = NULL;\n\t}\n#endif\n}\n\nvoid Media_StopRecordFilm_f (void)\n{\n\tMedia_FlushCapture();\n\tcapturingfbo = false;\n\n\tif (capture_fakesounddevice)\n\t\tS_ShutdownCard(capture_fakesounddevice);\n\tcapture_fakesounddevice = NULL;\n\n\tif (recordingdemo)\t//start up their regular audio devices again.\n\t\tS_DoRestart(false);\n\n\trecordingdemo=false;\n\n\tif (currentcapture_funcs)\n\t\tcurrentcapture_funcs->capture_end(currentcapture_ctx);\n\tcurrentcapture_ctx = NULL;\n\tcurrentcapture_funcs = NULL;\n\n\tCvar_ForceCallback(&vid_conautoscale);\n}\nvoid Media_VideoRestarting(void)\n{\n\tMedia_FlushCapture();\n}\nvoid Media_VideoRestarted(void)\n{\n#ifdef GLQUAKE\n\tif (capturingfbo && qrenderer == QR_OPENGL && gl_config.ext_framebuffer_objects)\n\t{\t//restore it how it was, if we can.\n\t\tint w = capturefbo.rb_size[0], h = capturefbo.rb_size[1];\n\t\tcapturingfbo = true;\n\t\tcapturetexture = R2D_RT_Configure(\"$democapture\", w, h, TF_BGRA32, RT_IMAGEFLAGS);\n\t\tcaptureoldfbo = GLBE_FBO_Update(&capturefbo, FBO_RB_DEPTH|(Sh_StencilShadowsActive()?FBO_RB_STENCIL:0), &capturetexture, 1, r_nulltex, capturetexture->width, capturetexture->height, 0);\n\t\tvid.fbpwidth = capturetexture->width;\n\t\tvid.fbpheight = capturetexture->height;\n\t\tvid.framebuffer = capturetexture;\n\t}\n\telse\n#endif\n\t\tcapturingfbo = false;\n\n\tif (capturingfbo)\n\t\tCvar_ForceCallback(&vid_conautoscale);\n}\nstatic void Media_RecordFilm (char *recordingname, qboolean demo)\n{\n\tint sndkhz, sndchannels, sndbits;\n\tint i;\n\n\tMedia_StopRecordFilm_f();\n\n\tif (capturerate.value<=0)\n\t{\n\t\tCon_Printf(\"Invalid capturerate\\n\");\n\t\tcapturerate.value = 15;\n\t}\n\n\tcaptureframeinterval = 1/capturerate.value;\n\tif (captureframeinterval < 0.001)\n\t\tcaptureframeinterval = 0.001;\t//no more than 1000 images per second.\n\tcapturelastvideotime = realtime = 0;\n\n\tCon_ClearNotify();\n\n\tcaptureframe = offscreen_captureframe = 0;\n\tfor (i = 0; i < sizeof(pluginencodersfunc)/sizeof(pluginencodersfunc[0]); i++)\n\t{\n\t\tif (pluginencodersfunc[i])\n\t\t\tif (!strcmp(pluginencodersfunc[i]->drivername, capturedriver.string))\n\t\t\t\tcurrentcapture_funcs = pluginencodersfunc[i];\n\t}\n\tif (!currentcapture_funcs)\n\t{\t//try to find one based upon the explicit extension given.\n\t\tchar captext[8];\n\t\tconst char *outext = COM_GetFileExtension(recordingname, NULL);\n\n\t\tfor (i = 0; i < countof(pluginencodersfunc); i++)\n\t\t{\n\t\t\tif (pluginencodersfunc[i] && pluginencodersfunc[i]->extensions)\n\t\t\t{\n\t\t\t\tconst char *t = pluginencodersfunc[i]->extensions;\n\t\t\t\twhile (*t)\n\t\t\t\t{\n\t\t\t\t\tt = COM_ParseStringSetSep (t, ';', captext, sizeof(captext));\n\t\t\t\t\tif (wildcmp(captext, outext))\n\t\t\t\t\t{\t//matches the wildcard...\n\t\t\t\t\t\tcurrentcapture_funcs = pluginencodersfunc[i];\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tif (!currentcapture_funcs)\n\t{\t//otherwise just find the first valid one that's in a plugin.\n\t\tfor (i = 0; i < countof(pluginencodersfunc); i++)\n\t\t{\n\t\t\tif (pluginencodersfunc[i] && pluginencodersfunc[i]->extensions && pluginencodersplugin[i])\n\t\t\t\tcurrentcapture_funcs = pluginencodersfunc[i];\n\t\t}\n\t}\n\tif (!currentcapture_funcs)\n\t{\t//otherwise just find the first valid one that's from anywhere...\n\t\tfor (i = 0; i < countof(pluginencodersfunc); i++)\n\t\t{\n\t\t\tif (pluginencodersfunc[i] && pluginencodersfunc[i]->extensions)\n\t\t\t\tcurrentcapture_funcs = pluginencodersfunc[i];\n\t\t}\n\t}\n\tif (capturesound.ival && !nosound.ival)\n\t{\n\t\tsndkhz = snd_speed?snd_speed:48000;\n\t\tsndchannels = capturesoundchannels.ival;\n\t\tsndbits = capturesoundbits.ival;\n\t}\n\telse\n\t{\n\t\tsndkhz = 0;\n\t\tsndchannels = 0;\n\t\tsndbits = 0;\n\t}\n\n\tvid.fbpwidth = vid.pixelwidth;\n\tvid.fbpheight = vid.pixelheight;\n\tif (demo && capturewidth.ival && captureheight.ival)\n\t{\n#ifdef GLQUAKE\n\t\tif (qrenderer == QR_OPENGL && gl_config.ext_framebuffer_objects)\n\t\t{\n\t\t\tcapturingfbo = true;\n\t\t\tcapturetexture = R2D_RT_Configure(\"$democapture\", capturewidth.ival, captureheight.ival, TF_BGRA32, RT_IMAGEFLAGS);\n\t\t\tcaptureoldfbo = GLBE_FBO_Update(&capturefbo, FBO_RB_DEPTH|(Sh_StencilShadowsActive()?FBO_RB_STENCIL:0), &capturetexture, 1, r_nulltex, capturewidth.ival, captureheight.ival, 0);\n\t\t\tvid.fbpwidth = capturetexture->width;\n\t\t\tvid.fbpheight = capturetexture->height;\n\t\t\tvid.framebuffer = capturetexture;\n\t\t}\n#endif\n\t}\n\n\toffscreen_format = TF_INVALID;\n#ifdef GLQUAKE_PBOS\n\tif (qrenderer == QR_OPENGL && !gl_config.gles && gl_config.glversion >= 2.1)\n\t{\t//both tgas and vfw favour bgr24, so lets get the gl drivers to suffer instead of us, where possible.\n\t\tif (vid.fbpwidth & 3)\n\t\t\toffscreen_format = TF_BGRA32;\t//don't bother changing pack alignment, just use something that is guarenteed to not need anything.\n\t\telse\n\t\t\toffscreen_format = TF_BGR24;\n\t}\n#endif\n#ifdef VKQUAKE\n//\tif (qrenderer == QR_VULKAN)\n//\t\toffscreen_format = TF_BGRA32;\t//use the native format, the driver won't do byteswapping for us.\n#endif\n\n\trecordingdemo = demo;\n\t\n\tif (!currentcapture_funcs->capture_begin)\n\t\tcurrentcapture_ctx = NULL;\n\telse\n\t{\n\t\tchar fname[MAX_QPATH];\n\t\tchar ext[8];\n\t\tCOM_FileExtension(recordingname, ext, sizeof(ext));\n\t\tif (!strcmp(ext, \"dem\") || !strcmp(ext, \"qwd\") || !strcmp(ext, \"mvd\") || !strcmp(ext, \"dm2\") || !strcmp(ext, \"gz\") || !strcmp(ext, \"xz\") || !*ext)\n\t\t{\t//extensions are evil, but whatever.\n\t\t\t//make sure there is actually an extension there, and don't break if they try overwriting a known demo format...\n\t\t\tCOM_StripExtension(recordingname, fname, sizeof(fname));\n\t\t\tif (currentcapture_funcs->extensions)\n\t\t\t{\n\t\t\t\tCOM_ParseStringSetSep (currentcapture_funcs->extensions, ';', ext, sizeof(ext));\n\t\t\t\tQ_strncatz(fname, ext, sizeof(fname));\n\t\t\t}\n\t\t\trecordingname = fname;\n\t\t}\n\n\t\tcurrentcapture_ctx = currentcapture_funcs->capture_begin(recordingname, capturerate.value, vid.fbpwidth, vid.fbpheight, &sndkhz, &sndchannels, &sndbits);\n\t}\n\tif (!currentcapture_ctx)\n\t{\n\t\trecordingdemo = false;\n\t\tcurrentcapture_funcs = NULL;\n\t\tCon_Printf(\"Unable to initialise capture driver\\n\");\n\t\tMedia_StopRecordFilm_f();\n\t}\n\telse if (sndkhz)\n\t\tMedia_InitFakeSoundDevice(sndkhz, sndchannels, sndbits);\n\n\tCvar_ForceCallback(&vid_conautoscale);\n}\nstatic void Media_RecordFilm_f (void)\n{\n\tif (Cmd_Argc() != 2)\n\t{\n\t\tint i;\n\t\tCon_Printf(\"capture <filename>\\nRecords video output in an avi file.\\nUse capturerate and capturecodec to configure.\\n\\n\");\n\t\tfor (i = 0; i < countof(pluginencodersfunc); i++)\n\t\t{\n\t\t\tif (pluginencodersfunc[i])\n\t\t\t\tCon_Printf(\"%s%s^7: %s\\n\", !strcmp(pluginencodersfunc[i]->drivername, capturedriver.string)?\"^2\":\"^3\", pluginencodersfunc[i]->drivername, pluginencodersfunc[i]->description);\n\t\t}\n\t\tCon_Printf(\"\\n\");\n\n\t\tCon_Printf(\"Current capture settings:\\n\");\n\t\tCon_Printf(\" ^[/capturedriver %s^]\\n\", capturedriver.string);\n\t\tCon_Printf(\" ^[/capturecodec %s^]\\n\", capturecodec.string);\n\t\tCon_Printf(\" ^[/capturedemowidth %s^]\\n\", capturewidth.string);\n\t\tCon_Printf(\" ^[/capturedemoheight %s^]\\n\", captureheight.string);\n\t\tCon_Printf(\" ^[/capturerate %s^]\\n\", capturerate.string);\n\t\tCon_Printf(\" ^[/capturesound %s^]\\n\", capturesound.string);\n\t\tif (capturesound.value)\n\t\t{\n\t\t\tCon_Printf(\" ^[/capturesoundchannels %s^]\\n\", capturesoundchannels.string);\n\t\t\tCon_Printf(\" ^[/capturesoundbits %s^]\\n\", capturesoundbits.string);\n\t\t}\n\t\treturn;\n\t}\n\tif (Cmd_IsInsecure())\t//err... don't think so sonny.\n\t\treturn;\n\n\tMedia_RecordFilm(Cmd_Argv(1), false);\n}\nvoid Media_CaptureDemoEnd(void)\n{\n\tif (recordingdemo)\n\t\tMedia_StopRecordFilm_f();\n}\nvoid CL_PlayDemo(char *demoname, qboolean usesystempath);\nvoid Media_RecordDemo_f(void)\n{\n\tchar *demoname = Cmd_Argv(1);\n\tchar *videoname = Cmd_Argv(2);\n\n\tif (Cmd_FromGamecode())\n\t\treturn;\n\n\tif (Cmd_Argc()<=1)\n\t{\n\t\tCon_Printf(\t\"capturedemo demoname outputname\\n\"\n\t\t\t\t\t\"captures a demo to video frames using offline rendering for smoother results\\n\"\n\t\t\t\t\t\"see also: apropos capture\\n\");\n\t\treturn;\n\t}\n\n\tif (!Renderer_Started() && !isDedicated)\n\t{\n\t\tCbuf_AddText(va(\"wait;%s %s\\n\", Cmd_Argv(0), Cmd_Args()), Cmd_ExecLevel);\n\t\treturn;\n\t}\n\n\tif (Cmd_Argc()<=2)\n\t\tvideoname = demoname;\n\n\tCL_Stopdemo_f();\t//capturing failed for some reason\n\n\tCL_PlayDemo(demoname, false);\n\tif (!cls.demoplayback)\n\t{\n\t\tCon_Printf(\"unable to play demo, not capturing\\n\");\n\t\treturn;\n\t}\n\t//FIXME: make sure it loaded okay\n\tMedia_RecordFilm(videoname, true);\n\tscr_con_current=0;\n\n\tMenu_PopAll();\n\tKey_Dest_Remove(kdm_console);\n\n\tif (!currentcapture_funcs)\n\t\tCL_Stopdemo_f();\t//capturing failed for some reason\n}\n#else\n\nvoid Media_CaptureDemoEnd(void) {}\nqboolean Media_PausedDemo(qboolean fortiming) {return false;}\nint Media_Capturing (void) { return 0; }\ndouble Media_TweekCaptureFrameTime(double oldtime, double time) { return oldtime+time ; }\nvoid Media_RecordFrame (void) {}\nvoid Media_VideoRestarting(void) {}\nvoid Media_VideoRestarted(void) {}\n\n#endif\n\n\n\n\n\n#ifdef HAVE_SPEECHTOTEXT\ntypedef struct ISpNotifySink ISpNotifySink;\ntypedef void *ISpNotifyCallback;\ntypedef void __stdcall SPNOTIFYCALLBACK(WPARAM wParam, LPARAM lParam);\ntypedef struct SPEVENT\n{\n    WORD        eEventId : 16;\n    WORD  elParamType : 16;\n    ULONG       ulStreamNum;\n    ULONGLONG   ullAudioStreamOffset;\n    WPARAM      wParam;\n    LPARAM      lParam;\n} SPEVENT;\n\n#define SPEVENTSOURCEINFO void\n#define ISpObjectToken void\n#define ISpStreamFormat void\n#define SPVOICESTATUS void\n#define SPVPRIORITY int\n#define SPEVENTENUM int\n\ntypedef struct ISpVoice ISpVoice;\ntypedef struct ISpVoiceVtbl\n{\n    HRESULT ( STDMETHODCALLTYPE *QueryInterface )(\n        ISpVoice * This,\n        /* [in] */ REFIID riid,\n        /* [iid_is][out] */ void **ppvObject);\n\n    ULONG ( STDMETHODCALLTYPE *AddRef )(\n        ISpVoice * This);\n\n    ULONG ( STDMETHODCALLTYPE *Release )(\n        ISpVoice * This);\n\n    HRESULT ( STDMETHODCALLTYPE *SetNotifySink )(\n        ISpVoice * This,\n        /* [in] */ ISpNotifySink *pNotifySink);\n\n    /* [local] */ HRESULT ( STDMETHODCALLTYPE *SetNotifyWindowMessage )(\n        ISpVoice * This,\n        /* [in] */ HWND hWnd,\n        /* [in] */ UINT Msg,\n        /* [in] */ WPARAM wParam,\n        /* [in] */ LPARAM lParam);\n\n    /* [local] */ HRESULT ( STDMETHODCALLTYPE *SetNotifyCallbackFunction )(\n        ISpVoice * This,\n        /* [in] */ SPNOTIFYCALLBACK *pfnCallback,\n        /* [in] */ WPARAM wParam,\n        /* [in] */ LPARAM lParam);\n\n    /* [local] */ HRESULT ( STDMETHODCALLTYPE *SetNotifyCallbackInterface )(\n        ISpVoice * This,\n        /* [in] */ ISpNotifyCallback *pSpCallback,\n        /* [in] */ WPARAM wParam,\n        /* [in] */ LPARAM lParam);\n\n    /* [local] */ HRESULT ( STDMETHODCALLTYPE *SetNotifyWin32Event )(\n        ISpVoice * This);\n\n    /* [local] */ HRESULT ( STDMETHODCALLTYPE *WaitForNotifyEvent )(\n        ISpVoice * This,\n        /* [in] */ DWORD dwMilliseconds);\n\n    /* [local] */ HANDLE ( STDMETHODCALLTYPE *GetNotifyEventHandle )(\n        ISpVoice * This);\n\n    HRESULT ( STDMETHODCALLTYPE *SetInterest )(\n        ISpVoice * This,\n        /* [in] */ ULONGLONG ullEventInterest,\n        /* [in] */ ULONGLONG ullQueuedInterest);\n\n    HRESULT ( STDMETHODCALLTYPE *GetEvents )(\n        ISpVoice * This,\n        /* [in] */ ULONG ulCount,\n        /* [size_is][out] */ SPEVENT *pEventArray,\n        /* [out] */ ULONG *pulFetched);\n\n    HRESULT ( STDMETHODCALLTYPE *GetInfo )(\n        ISpVoice * This,\n        /* [out] */ SPEVENTSOURCEINFO *pInfo);\n\n    HRESULT ( STDMETHODCALLTYPE *SetOutput )(\n        ISpVoice * This,\n        /* [in] */ IUnknown *pUnkOutput,\n        /* [in] */ BOOL fAllowFormatChanges);\n\n    HRESULT ( STDMETHODCALLTYPE *GetOutputObjectToken )(\n        ISpVoice * This,\n        /* [out] */ ISpObjectToken **ppObjectToken);\n\n    HRESULT ( STDMETHODCALLTYPE *GetOutputStream )(\n        ISpVoice * This,\n        /* [out] */ ISpStreamFormat **ppStream);\n\n    HRESULT ( STDMETHODCALLTYPE *Pause )(\n        ISpVoice * This);\n\n    HRESULT ( STDMETHODCALLTYPE *Resume )(\n        ISpVoice * This);\n\n    HRESULT ( STDMETHODCALLTYPE *SetVoice )(\n        ISpVoice * This,\n        /* [in] */ ISpObjectToken *pToken);\n\n    HRESULT ( STDMETHODCALLTYPE *GetVoice )(\n        ISpVoice * This,\n        /* [out] */ ISpObjectToken **ppToken);\n\n    HRESULT ( STDMETHODCALLTYPE *Speak )(\n        ISpVoice * This,\n        /* [string][in] */ const WCHAR *pwcs,\n        /* [in] */ DWORD dwFlags,\n        /* [out] */ ULONG *pulStreamNumber);\n\n    HRESULT ( STDMETHODCALLTYPE *SpeakStream )(\n        ISpVoice * This,\n        /* [in] */ IStream *pStream,\n        /* [in] */ DWORD dwFlags,\n        /* [out] */ ULONG *pulStreamNumber);\n\n    HRESULT ( STDMETHODCALLTYPE *GetStatus )(\n        ISpVoice * This,\n        /* [out] */ SPVOICESTATUS *pStatus,\n        /* [string][out] */ WCHAR **ppszLastBookmark);\n\n    HRESULT ( STDMETHODCALLTYPE *Skip )(\n        ISpVoice * This,\n        /* [string][in] */ WCHAR *pItemType,\n        /* [in] */ long lNumItems,\n        /* [out] */ ULONG *pulNumSkipped);\n\n    HRESULT ( STDMETHODCALLTYPE *SetPriority )(\n        ISpVoice * This,\n        /* [in] */ SPVPRIORITY ePriority);\n\n    HRESULT ( STDMETHODCALLTYPE *GetPriority )(\n        ISpVoice * This,\n        /* [out] */ SPVPRIORITY *pePriority);\n\n    HRESULT ( STDMETHODCALLTYPE *SetAlertBoundary )(\n        ISpVoice * This,\n        /* [in] */ SPEVENTENUM eBoundary);\n\n    HRESULT ( STDMETHODCALLTYPE *GetAlertBoundary )(\n        ISpVoice * This,\n        /* [out] */ SPEVENTENUM *peBoundary);\n\n    HRESULT ( STDMETHODCALLTYPE *SetRate )(\n        ISpVoice * This,\n        /* [in] */ long RateAdjust);\n\n    HRESULT ( STDMETHODCALLTYPE *GetRate )(\n        ISpVoice * This,\n        /* [out] */ long *pRateAdjust);\n\n    HRESULT ( STDMETHODCALLTYPE *SetVolume )(\n        ISpVoice * This,\n        /* [in] */ USHORT usVolume);\n\n    HRESULT ( STDMETHODCALLTYPE *GetVolume )(\n        ISpVoice * This,\n        /* [out] */ USHORT *pusVolume);\n\n    HRESULT ( STDMETHODCALLTYPE *WaitUntilDone )(\n        ISpVoice * This,\n        /* [in] */ ULONG msTimeout);\n\n    HRESULT ( STDMETHODCALLTYPE *SetSyncSpeakTimeout )(\n        ISpVoice * This,\n        /* [in] */ ULONG msTimeout);\n\n    HRESULT ( STDMETHODCALLTYPE *GetSyncSpeakTimeout )(\n        ISpVoice * This,\n        /* [out] */ ULONG *pmsTimeout);\n\n    /* [local] */ HANDLE ( STDMETHODCALLTYPE *SpeakCompleteEvent )(\n        ISpVoice * This);\n\n    /* [local] */ HRESULT ( STDMETHODCALLTYPE *IsUISupported )(\n        ISpVoice * This,\n        /* [in] */ const WCHAR *pszTypeOfUI,\n        /* [in] */ void *pvExtraData,\n        /* [in] */ ULONG cbExtraData,\n        /* [out] */ BOOL *pfSupported);\n\n    /* [local] */ HRESULT ( STDMETHODCALLTYPE *DisplayUI )(\n        ISpVoice * This,\n        /* [in] */ HWND hwndParent,\n        /* [in] */ const WCHAR *pszTitle,\n        /* [in] */ const WCHAR *pszTypeOfUI,\n        /* [in] */ void *pvExtraData,\n        /* [in] */ ULONG cbExtraData);\n\n    END_INTERFACE\n} ISpVoiceVtbl;\n\nstruct ISpVoice\n{\n    struct ISpVoiceVtbl *lpVtbl;\n};\nvoid TTS_SayUnicodeString(wchar_t *stringtosay)\n{\n\tstatic CLSID CLSID_SpVoice = {0x96749377, 0x3391, 0x11D2,\n\t\t\t\t\t\t\t\t{0x9E,0xE3,0x00,0xC0,0x4F,0x79,0x73,0x96}};\n\tstatic GUID IID_ISpVoice = {0x6C44DF74,0x72B9,0x4992,\n\t\t\t\t\t\t\t\t{0xA1,0xEC,0xEF,0x99,0x6E,0x04,0x22,0xD4}};\n\tstatic ISpVoice *sp = NULL;\n\n\tif (!sp)\n\t\tCoCreateInstance(\n\t\t\t\t&CLSID_SpVoice,\n\t\t\t\tNULL,\n\t\t\t\tCLSCTX_SERVER,\n\t\t\t\t&IID_ISpVoice,\n\t\t\t\t(void*)&sp);\n\n\tif (sp)\n\t{\n\t\tsp->lpVtbl->Speak(sp, stringtosay, 1, NULL);\n\t}\n}\nvoid TTS_SayAsciiString(char *stringtosay)\n{\n\twchar_t bigbuffer[8192];\n\tmbstowcs(bigbuffer, stringtosay, sizeof(bigbuffer)/sizeof(bigbuffer[0]) - 1);\n\tbigbuffer[sizeof(bigbuffer)/sizeof(bigbuffer[0]) - 1] = 0;\n\tTTS_SayUnicodeString(bigbuffer);\n}\n\ncvar_t tts_mode = CVARD(\"tts_mode\", \"1\", \"Text to speech\\n0: off\\n1: Read only chat messages with a leading 'tts ' prefix.\\n2: Read all chat messages\\n3: Read every single console print.\");\nvoid TTS_SayChatString(char **stringtosay)\n{\n\tif (!strncmp(*stringtosay, \"tts \", 4))\n\t{\n\t\t*stringtosay += 4;\n\t\tif (tts_mode.ival != 1 && tts_mode.ival != 2)\n\t\t\treturn;\n\t}\n\telse\n\t{\n\t\tif (tts_mode.ival != 2)\n\t\t\treturn;\n\t}\n\n\tTTS_SayAsciiString(*stringtosay);\n}\nvoid TTS_SayConString(conchar_t *stringtosay)\n{\n\twchar_t bigbuffer[8192];\n\tint i;\n\n\tif (tts_mode.ival < 3)\n\t\treturn;\n\t\n\tfor (i = 0; i < 8192-1 && *stringtosay; i++, stringtosay++)\n\t{\n\t\tif ((*stringtosay & 0xff00) == 0xe000)\n\t\t\tbigbuffer[i] = *stringtosay & 0x7f;\n\t\telse\n\t\t\tbigbuffer[i] = *stringtosay & CON_CHARMASK;\n\t}\n\tbigbuffer[i] = 0;\n\tif (i)\n\t\tTTS_SayUnicodeString(bigbuffer);\n}\nvoid TTS_Say_f(void)\n{\n\tTTS_SayAsciiString(Cmd_Args());\n}\n\n#define ISpRecognizer void\n#define SPPHRASE void\n#define SPSERIALIZEDPHRASE void\n#define SPSTATEHANDLE void*\n#define SPGRAMMARWORDTYPE int\n#define SPPROPERTYINFO void\n#define SPLOADOPTIONS void*\n#define SPBINARYGRAMMAR void*\n#define SPRULESTATE int\n#define SPTEXTSELECTIONINFO void\n#define SPWORDPRONOUNCEABLE void\n#define SPGRAMMARSTATE int\ntypedef struct ISpRecoResult ISpRecoResult;\ntypedef struct ISpRecoContext ISpRecoContext;\ntypedef struct ISpRecoGrammar ISpRecoGrammar;\n\ntypedef struct ISpRecoContextVtbl\n{\n\tHRESULT ( STDMETHODCALLTYPE *QueryInterface )( \n\t\tISpRecoContext * This,\n\t\t/* [in] */ REFIID riid,\n\t\t/* [iid_is][out] */ void **ppvObject);\n\n\tULONG ( STDMETHODCALLTYPE *AddRef )( \n\t\tISpRecoContext * This);\n\n\tULONG ( STDMETHODCALLTYPE *Release )( \n\t\tISpRecoContext * This);\n\n    HRESULT ( STDMETHODCALLTYPE *SetNotifySink )( \n        ISpRecoContext * This,\n        /* [in] */ ISpNotifySink *pNotifySink);\n    \n    /* [local] */ HRESULT ( STDMETHODCALLTYPE *SetNotifyWindowMessage )( \n        ISpRecoContext * This,\n        /* [in] */ HWND hWnd,\n        /* [in] */ UINT Msg,\n        /* [in] */ WPARAM wParam,\n        /* [in] */ LPARAM lParam);\n    \n    /* [local] */ HRESULT ( STDMETHODCALLTYPE *SetNotifyCallbackFunction )( \n        ISpRecoContext * This,\n        /* [in] */ SPNOTIFYCALLBACK *pfnCallback,\n        /* [in] */ WPARAM wParam,\n        /* [in] */ LPARAM lParam);\n    \n    /* [local] */ HRESULT ( STDMETHODCALLTYPE *SetNotifyCallbackInterface )( \n        ISpRecoContext * This,\n        /* [in] */ ISpNotifyCallback *pSpCallback,\n        /* [in] */ WPARAM wParam,\n        /* [in] */ LPARAM lParam);\n    \n    /* [local] */ HRESULT ( STDMETHODCALLTYPE *SetNotifyWin32Event )( \n        ISpRecoContext * This);\n    \n    /* [local] */ HRESULT ( STDMETHODCALLTYPE *WaitForNotifyEvent )( \n        ISpRecoContext * This,\n        /* [in] */ DWORD dwMilliseconds);\n    \n    /* [local] */ HANDLE ( STDMETHODCALLTYPE *GetNotifyEventHandle )( \n        ISpRecoContext * This);\n    \n    HRESULT ( STDMETHODCALLTYPE *SetInterest )( \n        ISpRecoContext * This,\n        /* [in] */ ULONGLONG ullEventInterest,\n        /* [in] */ ULONGLONG ullQueuedInterest);\n    \n    HRESULT ( STDMETHODCALLTYPE *GetEvents )( \n        ISpRecoContext * This,\n        /* [in] */ ULONG ulCount,\n        /* [size_is][out] */ SPEVENT *pEventArray,\n        /* [out] */ ULONG *pulFetched);\n\n    HRESULT ( STDMETHODCALLTYPE *GetInfo )( \n        ISpRecoContext * This,\n        /* [out] */ SPEVENTSOURCEINFO *pInfo);\n    \n    HRESULT ( STDMETHODCALLTYPE *GetRecognizer )( \n        ISpRecoContext * This,\n        /* [out] */ ISpRecognizer **ppRecognizer);\n    \n    HRESULT ( STDMETHODCALLTYPE *CreateGrammar )( \n        ISpRecoContext * This,\n        /* [in] */ ULONGLONG ullGrammarId,\n        /* [out] */ ISpRecoGrammar **ppGrammar);\n} ISpRecoContextVtbl;\nstruct ISpRecoContext\n{\n    struct ISpRecoContextVtbl *lpVtbl;\n};\n\ntypedef struct ISpRecoResultVtbl\n{\n    HRESULT ( STDMETHODCALLTYPE *QueryInterface )( \n        ISpRecoResult * This,\n        /* [in] */ REFIID riid,\n        /* [iid_is][out] */ void **ppvObject);\n    \n    ULONG ( STDMETHODCALLTYPE *AddRef )( \n        ISpRecoResult * This);\n    \n    ULONG ( STDMETHODCALLTYPE *Release )( \n        ISpRecoResult * This);\n    \n    HRESULT ( STDMETHODCALLTYPE *GetPhrase )( \n        ISpRecoResult * This,\n        /* [out] */ SPPHRASE **ppCoMemPhrase);\n    \n    HRESULT ( STDMETHODCALLTYPE *GetSerializedPhrase )( \n        ISpRecoResult * This,\n        /* [out] */ SPSERIALIZEDPHRASE **ppCoMemPhrase);\n    \n    HRESULT ( STDMETHODCALLTYPE *GetText )( \n        ISpRecoResult * This,\n        /* [in] */ ULONG ulStart,\n        /* [in] */ ULONG ulCount,\n        /* [in] */ BOOL fUseTextReplacements,\n        /* [out] */ WCHAR **ppszCoMemText,\n        /* [out] */ BYTE *pbDisplayAttributes);\n    \n    HRESULT ( STDMETHODCALLTYPE *Discard )( \n        ISpRecoResult * This,\n        /* [in] */ DWORD dwValueTypes);\n#if 0\n    HRESULT ( STDMETHODCALLTYPE *GetResultTimes )( \n        ISpRecoResult * This,\n        /* [out] */ SPRECORESULTTIMES *pTimes);\n    \n    HRESULT ( STDMETHODCALLTYPE *GetAlternates )( \n        ISpRecoResult * This,\n        /* [in] */ ULONG ulStartElement,\n        /* [in] */ ULONG cElements,\n        /* [in] */ ULONG ulRequestCount,\n        /* [out] */ ISpPhraseAlt **ppPhrases,\n        /* [out] */ ULONG *pcPhrasesReturned);\n    \n    HRESULT ( STDMETHODCALLTYPE *GetAudio )( \n        ISpRecoResult * This,\n        /* [in] */ ULONG ulStartElement,\n        /* [in] */ ULONG cElements,\n        /* [out] */ ISpStreamFormat **ppStream);\n    \n    HRESULT ( STDMETHODCALLTYPE *SpeakAudio )( \n        ISpRecoResult * This,\n        /* [in] */ ULONG ulStartElement,\n        /* [in] */ ULONG cElements,\n        /* [in] */ DWORD dwFlags,\n        /* [out] */ ULONG *pulStreamNumber);\n    \n    HRESULT ( STDMETHODCALLTYPE *Serialize )( \n        ISpRecoResult * This,\n        /* [out] */ SPSERIALIZEDRESULT **ppCoMemSerializedResult);\n    \n    HRESULT ( STDMETHODCALLTYPE *ScaleAudio )( \n        ISpRecoResult * This,\n        /* [in] */ const GUID *pAudioFormatId,\n        /* [in] */ const WAVEFORMATEX *pWaveFormatEx);\n    \n    HRESULT ( STDMETHODCALLTYPE *GetRecoContext )( \n        ISpRecoResult * This,\n        /* [out] */ ISpRecoContext **ppRecoContext);\n    \n#endif\n} ISpRecoResultVtbl;\nstruct ISpRecoResult\n{\n    struct ISpRecoResultVtbl *lpVtbl;\n};\n\ntypedef struct ISpRecoGrammarVtbl\n{\n    HRESULT ( STDMETHODCALLTYPE *QueryInterface )( \n        ISpRecoGrammar * This,\n        /* [in] */ REFIID riid,\n        /* [iid_is][out] */ void **ppvObject);\n    \n    ULONG ( STDMETHODCALLTYPE *AddRef )( \n        ISpRecoGrammar * This);\n    \n    ULONG ( STDMETHODCALLTYPE *Release )( \n        ISpRecoGrammar * This);\n    \n    HRESULT ( STDMETHODCALLTYPE *ResetGrammar )( \n        ISpRecoGrammar * This,\n        /* [in] */ WORD NewLanguage);\n    \n    HRESULT ( STDMETHODCALLTYPE *GetRule )( \n        ISpRecoGrammar * This,\n        /* [in] */ const WCHAR *pszRuleName,\n        /* [in] */ DWORD dwRuleId,\n        /* [in] */ DWORD dwAttributes,\n        /* [in] */ BOOL fCreateIfNotExist,\n        /* [out] */ SPSTATEHANDLE *phInitialState);\n    \n    HRESULT ( STDMETHODCALLTYPE *ClearRule )( \n        ISpRecoGrammar * This,\n        SPSTATEHANDLE hState);\n    \n    HRESULT ( STDMETHODCALLTYPE *CreateNewState )( \n        ISpRecoGrammar * This,\n        SPSTATEHANDLE hState,\n        SPSTATEHANDLE *phState);\n    \n    HRESULT ( STDMETHODCALLTYPE *AddWordTransition )( \n        ISpRecoGrammar * This,\n        SPSTATEHANDLE hFromState,\n        SPSTATEHANDLE hToState,\n        const WCHAR *psz,\n        const WCHAR *pszSeparators,\n        SPGRAMMARWORDTYPE eWordType,\n        float Weight,\n        const SPPROPERTYINFO *pPropInfo);\n    \n    HRESULT ( STDMETHODCALLTYPE *AddRuleTransition )( \n        ISpRecoGrammar * This,\n        SPSTATEHANDLE hFromState,\n        SPSTATEHANDLE hToState,\n        SPSTATEHANDLE hRule,\n        float Weight,\n        const SPPROPERTYINFO *pPropInfo);\n    \n    HRESULT ( STDMETHODCALLTYPE *AddResource )( \n        ISpRecoGrammar * This,\n        /* [in] */ SPSTATEHANDLE hRuleState,\n        /* [in] */ const WCHAR *pszResourceName,\n        /* [in] */ const WCHAR *pszResourceValue);\n    \n    HRESULT ( STDMETHODCALLTYPE *Commit )( \n        ISpRecoGrammar * This,\n        DWORD dwReserved);\n    \n    HRESULT ( STDMETHODCALLTYPE *GetGrammarId )( \n        ISpRecoGrammar * This,\n        /* [out] */ ULONGLONG *pullGrammarId);\n    \n    HRESULT ( STDMETHODCALLTYPE *GetRecoContext )( \n        ISpRecoGrammar * This,\n        /* [out] */ ISpRecoContext **ppRecoCtxt);\n    \n    HRESULT ( STDMETHODCALLTYPE *LoadCmdFromFile )( \n        ISpRecoGrammar * This,\n        /* [string][in] */ const WCHAR *pszFileName,\n        /* [in] */ SPLOADOPTIONS Options);\n    \n    HRESULT ( STDMETHODCALLTYPE *LoadCmdFromObject )( \n        ISpRecoGrammar * This,\n        /* [in] */ REFCLSID rcid,\n        /* [string][in] */ const WCHAR *pszGrammarName,\n        /* [in] */ SPLOADOPTIONS Options);\n    \n    HRESULT ( STDMETHODCALLTYPE *LoadCmdFromResource )( \n        ISpRecoGrammar * This,\n        /* [in] */ HMODULE hModule,\n        /* [string][in] */ const WCHAR *pszResourceName,\n        /* [string][in] */ const WCHAR *pszResourceType,\n        /* [in] */ WORD wLanguage,\n        /* [in] */ SPLOADOPTIONS Options);\n    \n    HRESULT ( STDMETHODCALLTYPE *LoadCmdFromMemory )( \n        ISpRecoGrammar * This,\n        /* [in] */ const SPBINARYGRAMMAR *pGrammar,\n        /* [in] */ SPLOADOPTIONS Options);\n    \n    HRESULT ( STDMETHODCALLTYPE *LoadCmdFromProprietaryGrammar )( \n        ISpRecoGrammar * This,\n        /* [in] */ REFGUID rguidParam,\n        /* [string][in] */ const WCHAR *pszStringParam,\n        /* [in] */ const void *pvDataPrarm,\n        /* [in] */ ULONG cbDataSize,\n        /* [in] */ SPLOADOPTIONS Options);\n    \n    HRESULT ( STDMETHODCALLTYPE *SetRuleState )( \n        ISpRecoGrammar * This,\n        /* [string][in] */ const WCHAR *pszName,\n        void *pReserved,\n        /* [in] */ SPRULESTATE NewState);\n    \n    HRESULT ( STDMETHODCALLTYPE *SetRuleIdState )( \n        ISpRecoGrammar * This,\n        /* [in] */ ULONG ulRuleId,\n        /* [in] */ SPRULESTATE NewState);\n    \n    HRESULT ( STDMETHODCALLTYPE *LoadDictation )( \n        ISpRecoGrammar * This,\n        /* [string][in] */ const WCHAR *pszTopicName,\n        /* [in] */ SPLOADOPTIONS Options);\n    \n    HRESULT ( STDMETHODCALLTYPE *UnloadDictation )( \n        ISpRecoGrammar * This);\n    \n    HRESULT ( STDMETHODCALLTYPE *SetDictationState )( \n        ISpRecoGrammar * This,\n        /* [in] */ SPRULESTATE NewState);\n    \n    HRESULT ( STDMETHODCALLTYPE *SetWordSequenceData )( \n        ISpRecoGrammar * This,\n        /* [in] */ const WCHAR *pText,\n        /* [in] */ ULONG cchText,\n        /* [in] */ const SPTEXTSELECTIONINFO *pInfo);\n    \n    HRESULT ( STDMETHODCALLTYPE *SetTextSelection )( \n        ISpRecoGrammar * This,\n        /* [in] */ const SPTEXTSELECTIONINFO *pInfo);\n    \n    HRESULT ( STDMETHODCALLTYPE *IsPronounceable )( \n        ISpRecoGrammar * This,\n        /* [string][in] */ const WCHAR *pszWord,\n        /* [out] */ SPWORDPRONOUNCEABLE *pWordPronounceable);\n    \n    HRESULT ( STDMETHODCALLTYPE *SetGrammarState )( \n        ISpRecoGrammar * This,\n        /* [in] */ SPGRAMMARSTATE eGrammarState);\n    \n    HRESULT ( STDMETHODCALLTYPE *SaveCmd )( \n        ISpRecoGrammar * This,\n        /* [in] */ IStream *pStream,\n        /* [optional][out] */ WCHAR **ppszCoMemErrorText);\n    \n    HRESULT ( STDMETHODCALLTYPE *GetGrammarState )( \n        ISpRecoGrammar * This,\n        /* [out] */ SPGRAMMARSTATE *peGrammarState);\n} ISpRecoGrammarVtbl;\nstruct ISpRecoGrammar\n{\n\tstruct ISpRecoGrammarVtbl *lpVtbl;\n};\n\nstatic ISpRecoContext *stt_recctx = NULL;\nstatic ISpRecoGrammar *stt_gram = NULL;\nvoid STT_Event(void)\n{\n\tWCHAR *wstring, *i;\n\tstruct SPEVENT ev;\n\tISpRecoResult *rr;\n\tHRESULT hr;\n\tchar asc[2048], *o;\n\tint l;\n\tunsigned short c;\n\tchar *nib = \"0123456789abcdef\";\n\tif (!stt_gram)\n\t\treturn;\n\n\twhile (SUCCEEDED(hr = stt_recctx->lpVtbl->GetEvents(stt_recctx, 1, &ev, NULL)) && hr != S_FALSE)\n\t{\n\t\trr = (ISpRecoResult*)ev.lParam;\n\t\trr->lpVtbl->GetText(rr, -1, -1, TRUE, &wstring, NULL);\n\t\tfor (l = sizeof(asc)-1, o = asc, i = wstring; l > 0 && *i; )\n\t\t{\n\t\t\tc = *i++;\n\t\t\tif (c == '\\n' || c == ';')\n\t\t\t{\n\t\t\t}\n\t\t\telse if (c < 128)\n\t\t\t{\n\t\t\t\t*o++ = c;\n\t\t\t\tl--;\n\t\t\t}\n\t\t\telse if (l > 6)\n\t\t\t{\n\t\t\t\t*o++ = '^';\n\t\t\t\t*o++ = 'U';\n\t\t\t\t*o++ = nib[(c>>12)&0xf];\n\t\t\t\t*o++ = nib[(c>>8)&0xf];\n\t\t\t\t*o++ = nib[(c>>4)&0xf];\n\t\t\t\t*o++ = nib[(c>>0)&0xf];\n\t\t\t}\n\t\t\telse\n\t\t\t\tbreak;\n\t\t}\n\t\t*o = 0;\n\t\tCoTaskMemFree(wstring);\n\t\tCbuf_AddText(\"say tts \", RESTRICT_LOCAL);\n\t\tCbuf_AddText(asc, RESTRICT_LOCAL);\n\t\tCbuf_AddText(\"\\n\", RESTRICT_LOCAL);\n\t\trr->lpVtbl->Release(rr);\n\t}\n}\nvoid STT_Init_f(void)\n{\n\tstatic CLSID CLSID_SpSharedRecoContext\t=\t{0x47206204, 0x5ECA, 0x11D2, {0x96, 0x0F, 0x00, 0xC0, 0x4F, 0x8E, 0xE6, 0x28}};\n\tstatic CLSID IID_SpRecoContext\t\t\t=\t{0xF740A62F, 0x7C15, 0x489E, {0x82, 0x34, 0x94, 0x0A, 0x33, 0xD9, 0x27, 0x2D}};\n\n\tif (stt_gram)\n\t{\n\t\tstt_gram->lpVtbl->Release(stt_gram);\n\t\tstt_recctx->lpVtbl->Release(stt_recctx);\n\t\tstt_gram = NULL;\n\t\tstt_recctx = NULL;\n\t\tCon_Printf(\"Speech-to-text disabled\\n\");\n\t\treturn;\n\t}\n\n\tif (SUCCEEDED(CoCreateInstance(&CLSID_SpSharedRecoContext, NULL, CLSCTX_SERVER, &IID_SpRecoContext, (void*)&stt_recctx)))\n\t{\n\t\tULONGLONG ev = (((ULONGLONG)1) << 38) | (((ULONGLONG)1) << 30) | (((ULONGLONG)1) << 33);\n\t\tif (SUCCEEDED(stt_recctx->lpVtbl->SetNotifyWindowMessage(stt_recctx, mainwindow, WM_USER_SPEECHTOTEXT, 0, 0)))\n\t\tif (SUCCEEDED(stt_recctx->lpVtbl->SetInterest(stt_recctx, ev, ev)))\n\t\tif (SUCCEEDED(stt_recctx->lpVtbl->CreateGrammar(stt_recctx, 0, &stt_gram)))\n\t\t{\n\t\t\tif (SUCCEEDED(stt_gram->lpVtbl->LoadDictation(stt_gram, NULL, 0)))\n\t\t\tif (SUCCEEDED(stt_gram->lpVtbl->SetDictationState(stt_gram, 1)))\n\t\t\t{\n\t\t\t\t//success!\n\t\t\t\tCon_Printf(\"Speech-to-text active\\n\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tstt_gram->lpVtbl->Release(stt_gram);\n\t\t}\n\t\tstt_recctx->lpVtbl->Release(stt_recctx);\n\t}\n\tstt_gram = NULL;\n\tstt_recctx = NULL;\n\n\tCon_Printf(\"Speech-to-text unavailable\\n\");\n}\n#endif\n\n\n\n\n\n\n#ifdef AVAIL_MP3_ACM\ntypedef struct\n{\n\tHACMSTREAM acm;\n\n\tunsigned int dstbuffer; /*in frames*/\n\tunsigned int dstcount; /*in frames*/\n\tunsigned int dststart; /*in frames*/\n\tqbyte *dstdata;\n\n\tunsigned int srcspeed;\n\tqaudiofmt_t  srcformat;\n\tunsigned int srcchannels;\n\tunsigned int srcoffset; /*in bytes*/\n\tunsigned int srclen;\t/*in bytes*/\n\tqbyte srcdata[1];\n\n\tchar title[256];\n} mp3decoder_t;\n\nstatic void QDECL S_MP3_Purge(sfx_t *sfx)\n{\n\tmp3decoder_t *dec = sfx->decoder.buf;\n\n\tsfx->decoder.buf = NULL;\n\tsfx->decoder.ended = NULL;\n\tsfx->decoder.purge = NULL;\n\tsfx->decoder.decodedata = NULL;\n\n\tqacmStreamClose(dec->acm, 0);\n\n\tif (dec->dstdata)\n\t\tBZ_Free(dec->dstdata);\n\tBZ_Free(dec);\n\n\tsfx->loadstate = SLS_NOTLOADED;\n}\n\nfloat QDECL S_MP3_Query(sfx_t *sfx, sfxcache_t *buf, char *title, size_t titlesize)\n{\n\tmp3decoder_t *dec = sfx->decoder.buf;\n\t//we don't know unless we decode it all\n\tif (buf)\n\t{\n\t}\n\tif (titlesize && dec->srclen >= 128)\n\t{\t//id3v1 is a 128 byte blob at the end of the file.\n\t\tchar trimartist[31];\n\t\tchar trimtitle[31];\n\t\tchar *p;\n\t\tstruct\n\t\t{\n\t\t\tchar tag[3];\t//TAG\n\t\t\tchar title[30];\n\t\t\tchar artist[30];\n\t\t\tchar album[30];\n\t\t\tchar year[4];\n\t\t\tchar comment[30];//[28]+null+track\n\t\t\tqbyte genre;\n\t\t} *id3v1 = (void*)(dec->srcdata + dec->srclen-128);\n\t\tif (id3v1->tag[0] == 'T' && id3v1->tag[1] == 'A' && id3v1->tag[2] == 'G')\n\t\t{\t//yup, there's an id3v1 tag there\n\t\t\tmemcpy(trimartist, id3v1->artist, 30);\n\t\t\tfor(p = trimartist+30; p>trimartist && p[-1] == ' '; )\n\t\t\t\tp--;\n\t\t\t*p = 0;\n\t\t\tmemcpy(trimtitle, id3v1->title, 30);\n\t\t\tfor(p = trimtitle+30; p>trimtitle && p[-1] == ' '; )\n\t\t\t\tp--;\n\t\t\t*p = 0;\n\t\t\tif (*trimartist && *trimtitle)\n\t\t\t\tQ_snprintfz(title, titlesize, \"%.30s - %.30s\", trimartist, trimtitle);\n\t\t\telse if (*id3v1->title)\n\t\t\t\tQ_snprintfz(title, titlesize, \"%.30s\", trimtitle);\n\t\t}\n\t\treturn 1;//no real idea.\n\t}\n\treturn 0;\n}\n\n/*must be thread safe*/\nsfxcache_t *QDECL S_MP3_Locate(sfx_t *sfx, sfxcache_t *buf, ssamplepos_t start, int length)\n{\n\tint newlen;\n\tif (buf)\n\t{\n\t\tmp3decoder_t *dec = sfx->decoder.buf;\n\t\tACMSTREAMHEADER strhdr;\n\t\tchar buffer[8192];\n\t\textern cvar_t snd_linearresample_stream;\n\t\tint framesz = (QAF_BYTES(dec->srcformat) * dec->srcchannels);\n\n\t\tif (length)\n\t\t{\n\t\t\tif (dec->dststart > start)\n\t\t\t{\n\t\t\t\t/*I don't know where the compressed data is for each sample. acm doesn't have a seek. so reset to start, for music this should be the most common rewind anyway*/\n\t\t\t\tdec->dststart = 0;\n\t\t\t\tdec->dstcount = 0;\n\t\t\t\tdec->srcoffset = 0;\n\t\t\t}\n\n\t\t\tif (dec->dstcount > snd_speed*6)\n\t\t\t{\n\t\t\t\tint trim = dec->dstcount - snd_speed; //retain a second of buffer in case we have multiple sound devices\n\t\t\t\tif (dec->dststart + trim > start)\n\t\t\t\t{\n\t\t\t\t\ttrim = start - dec->dststart;\n\t\t\t\t\tif (trim < 0)\n\t\t\t\t\t\ttrim = 0;\n\t\t\t\t}\n\t//\t\t\tif (trim < 0)\n\t//\t\t\t\ttrim = 0;\n\t///\t\t\tif (trim > dec->dstcount)\n\t//\t\t\t\ttrim = dec->dstcount;\n\t\t\t\tmemmove(dec->dstdata, dec->dstdata + trim*framesz, (dec->dstcount - trim)*framesz);\n\t\t\t\tdec->dststart += trim;\n\t\t\t\tdec->dstcount -= trim;\n\t\t\t}\n\n\t\t\twhile(start+length >= dec->dststart+dec->dstcount)\n\t\t\t{\n\t\t\t\tmemset(&strhdr, 0, sizeof(strhdr));\n\t\t\t\tstrhdr.cbStruct = sizeof(strhdr);\n\t\t\t\tstrhdr.pbSrc = dec->srcdata + dec->srcoffset;\n\t\t\t\tstrhdr.cbSrcLength = dec->srclen - dec->srcoffset;\n\t\t\t\tif (!strhdr.cbSrcLength)\n\t\t\t\t\tbreak;\n\t\t\t\tstrhdr.pbDst = buffer;\n\t\t\t\tstrhdr.cbDstLength = sizeof(buffer);\n\n\t\t\t\tqacmStreamPrepareHeader(dec->acm, &strhdr, 0);\n\t\t\t\tqacmStreamConvert(dec->acm, &strhdr, ACM_STREAMCONVERTF_BLOCKALIGN);\n\t\t\t\tqacmStreamUnprepareHeader(dec->acm, &strhdr, 0);\n\t\t\t\tdec->srcoffset += strhdr.cbSrcLengthUsed;\n\t\t\t\tif (!strhdr.cbDstLengthUsed)\n\t\t\t\t{\n\t\t\t\t\tif (strhdr.cbSrcLengthUsed)\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tnewlen = dec->dstcount + (strhdr.cbDstLengthUsed * ((float)snd_speed / dec->srcspeed))/framesz;\n\t\t\t\tif (dec->dstbuffer < newlen+64)\n\t\t\t\t{\n\t\t\t\t\tdec->dstbuffer = newlen+64 + snd_speed;\n\t\t\t\t\tdec->dstdata = BZ_Realloc(dec->dstdata, dec->dstbuffer*framesz);\n\t\t\t\t}\n\n\t\t\t\tSND_ResampleStream(strhdr.pbDst, \n\t\t\t\t\tdec->srcspeed, \n\t\t\t\t\tdec->srcformat,\n\t\t\t\t\tdec->srcchannels, \n\t\t\t\t\tstrhdr.cbDstLengthUsed / framesz,\n\t\t\t\t\tdec->dstdata+dec->dstcount*framesz,\n\t\t\t\t\tsnd_speed,\n\t\t\t\t\tdec->srcformat,\n\t\t\t\t\tdec->srcchannels,\n\t\t\t\t\tsnd_linearresample_stream.ival);\n\t\t\t\tdec->dstcount = newlen;\n\t\t\t}\n\t\t}\n\n\t\tbuf->data = dec->dstdata;\n\t\tbuf->length = dec->dstcount;\n\t\tbuf->numchannels = dec->srcchannels;\n\t\tbuf->soundoffset = dec->dststart;\n\t\tbuf->speed = snd_speed;\n\t\tbuf->format = dec->srcformat;\n\n\t\tif (dec->srclen == dec->srcoffset && start >= dec->dststart+dec->dstcount)\n\t\t\treturn NULL;\t//once we reach the EOF, start reporting errors.\n\t}\n\treturn buf;\n}\n\n#ifndef WAVE_FORMAT_MPEGLAYER3\n#define WAVE_FORMAT_MPEGLAYER3 0x0055\ntypedef struct\n{\n\tWAVEFORMATEX  wfx;\n\tWORD          wID;\n\tDWORD         fdwFlags;\n\tWORD          nBlockSize;\n\tWORD          nFramesPerBlock;\n\tWORD          nCodecDelay;\n} MPEGLAYER3WAVEFORMAT;\n#endif\n#ifndef MPEGLAYER3_ID_MPEG\n#define MPEGLAYER3_WFX_EXTRA_BYTES 12\n#define MPEGLAYER3_FLAG_PADDING_OFF 2\n#define MPEGLAYER3_ID_MPEG 1\n#endif\n\nstatic qboolean QDECL S_LoadMP3Sound (sfx_t *s, qbyte *data, size_t datalen, int sndspeed, qboolean forcedecode)\n{\n\tWAVEFORMATEX pcm_format;\n\tMPEGLAYER3WAVEFORMAT mp3format;\n\tHACMDRIVER drv = NULL;\n\tmp3decoder_t *dec;\n\n\tchar ext[8];\n\tCOM_FileExtension(s->name, ext, sizeof(ext));\n\tif (stricmp(ext, \"mp3\"))\n\t\treturn false;\n\n\tdec = BZF_Malloc(sizeof(*dec) + datalen);\n\tif (!dec)\n\t\treturn false;\n\tmemcpy(dec->srcdata, data, datalen);\n\tdec->srclen = datalen;\n\ts->decoder.buf = dec;\n\ts->decoder.ended = S_MP3_Purge;\n\ts->decoder.purge = S_MP3_Purge;\n\ts->decoder.decodedata = S_MP3_Locate;\n\ts->decoder.querydata = S_MP3_Query;\n\ts->loopstart = -1;\n\t\n\tdec->dstdata = NULL;\n\tdec->dstcount = 0;\n\tdec->dststart = 0;\n\tdec->dstbuffer = 0;\n\tdec->srcoffset = 0;\n\n\tdec->srcspeed = 44100;\n\tdec->srcchannels = 2;\n\tdec->srcformat = QAF_S16;\n\n\tmemset (&pcm_format, 0, sizeof(pcm_format));\n\tpcm_format.wFormatTag = WAVE_FORMAT_PCM;\n\tpcm_format.nChannels = dec->srcchannels;\n\tpcm_format.nSamplesPerSec = dec->srcspeed;\n\tpcm_format.nBlockAlign = QAF_BYTES(dec->srcformat)*dec->srcchannels;\n\tpcm_format.nAvgBytesPerSec = pcm_format.nSamplesPerSec*QAF_BYTES(dec->srcformat)*dec->srcchannels;\n\tpcm_format.wBitsPerSample = QAF_BYTES(dec->srcformat)*8;\n\tpcm_format.cbSize = 0;\n\n\tmp3format.wfx.cbSize = MPEGLAYER3_WFX_EXTRA_BYTES;\n\tmp3format.wfx.wFormatTag = WAVE_FORMAT_MPEGLAYER3;\n\tmp3format.wfx.nChannels = dec->srcchannels;\n\tmp3format.wfx.nAvgBytesPerSec = 128 * (1024 / 8);  // not really used but must be one of 64, 96, 112, 128, 160kbps\n\tmp3format.wfx.wBitsPerSample = 0;                  // MUST BE ZERO\n\tmp3format.wfx.nBlockAlign = 1;                     // MUST BE ONE\n\tmp3format.wfx.nSamplesPerSec = dec->srcspeed;       // 44.1kHz\n\tmp3format.fdwFlags = MPEGLAYER3_FLAG_PADDING_OFF;\n\tmp3format.nBlockSize = 522;\t\t\t\t\t       // voodoo value #1 - 144 x (bitrate / sample rate) + padding\n\tmp3format.nFramesPerBlock = 1;                     // MUST BE ONE\n\tmp3format.nCodecDelay = 0;//1393;                      // voodoo value #2\n\tmp3format.wID = MPEGLAYER3_ID_MPEG;\n\n\tif (!qacmStartup() || 0!=qacmStreamOpen(&dec->acm, drv, (WAVEFORMATEX*)&mp3format, &pcm_format, NULL, 0, 0, 0))\n\t{\n\t\tCon_Printf(\"Couldn't init decoder\\n\");\n\t\treturn false;\n\t}\n\n\tS_MP3_Locate(s, NULL, 0, 100);\n\n\n\treturn true;\n}\n#endif\n\n\n\nvoid Media_Init(void)\n{\n#ifdef HAVE_JUKEBOX\n\tint i;\n\tCmd_AddCommand(\"music_fforward\", Media_FForward_f);\n\tCmd_AddCommand(\"music_rewind\", Media_Rewind_f);\n\tCmd_AddCommand(\"music_next\", Media_Next_f);\n\tCmd_AddCommand(\"media_next\", Media_Next_f);\n\n\tCvar_Register(&music_playlist_index, \"compat\");\n\tfor (i = 0; i < 6; i++)\n\t{\n\t\tCvar_Get(va(\"music_playlist_list%i\", i), \"\", 0, \"compat\");\n\t\tCvar_Get(va(\"music_playlist_sampleposition%i\", i), \"-1\", 0, \"compat\");\n\t}\n\tmusic_playlist_last = -1;\n\tmedia_playlisttypes = MEDIA_PLAYLIST | MEDIA_GAMEMUSIC | MEDIA_CVARLIST;\n\n\t#ifdef WINAMP\n\t\tCvar_Register(&media_hijackwinamp,\t\"Media player things\");\n\t#endif\n\tCvar_Register(&media_shuffle,\t\"Media player things\");\n\tCvar_Register(&media_repeat,\t\"Media player things\");\n\t#if !defined(NOMEDIAMENU) && !defined(NOBUILTINMENUS)\n\t\tCmd_AddCommand (\"media_add\", M_Media_Add_f);\n\t\tCmd_AddCommand (\"media_remove\", M_Media_Remove_f);\n\t\tCmd_AddCommand (\"menu_media\", M_Menu_Media_f);\n\t#endif\n#endif\n\tCvar_Register(&music_fade,\t\"Media player things\");\n\n#ifdef HAVE_SPEECHTOTEXT\n\tCmd_AddCommand(\"tts\", TTS_Say_f);\n\tCmd_AddCommand(\"stt\", STT_Init_f);\n\tCvar_Register(&tts_mode, \"Gimmicks\");\n#endif\n#ifdef AVAIL_MP3_ACM\n\tS_RegisterSoundInputPlugin(NULL, S_LoadMP3Sound);\n#endif\n\n\n#ifdef HAVE_CDPLAYER\n\tCmd_AddCommand(\"cd\", CD_f);\n\tcdenabled = false;\n\tif (COM_CheckParm(\"-nocdaudio\"))\n\t\tcdenabled = false;\n\tif (COM_CheckParm(\"-cdaudio\"))\n\t\tcdenabled = true;\n#endif\n\n#ifdef HAVE_MEDIA_ENCODER\n\t#ifdef _DEBUG\n\t\tMedia_RegisterEncoder(NULL, &capture_null);\n\t#endif\n\tMedia_RegisterEncoder(NULL, &capture_raw);\n\t#if defined(HAVE_API_VFW)\n\t\tMedia_RegisterEncoder(NULL, &capture_avi);\n\t#endif\n\n\tCmd_AddCommandD(\"capture\", Media_RecordFilm_f, \"Captures realtime action to a named video file. Check the capture* cvars to control driver/codecs/rates.\");\n\tCmd_AddCommandD(\"capturedemo\", Media_RecordDemo_f, \"capturedemo foo.dem foo.avi - Captures a named demo to a named video file.\\nDemo capturing is performed offscreen when possible, allowing arbitrary video sizes or smooth captures on underpowered hardware.\");\n\tCmd_AddCommandD(\"capturestop\", Media_StopRecordFilm_f, \"Aborts the current video capture.\");\n\tCmd_AddCommandD(\"capturepause\", Media_CapturePause_f, \"Pauses the video capture, allowing you to avoid capturing uninteresting parts. This is a toggle, so reuse the same command to resume capturing again.\");\n\n\tCvar_Register(&capturemessage,\t\t\t\"Video Capture Controls\");\n\tCvar_Register(&capturesound,\t\t\t\"Video Capture Controls\");\n\tCvar_Register(&capturerate,\t\t\t\t\"Video Capture Controls\");\n\tCvar_Register(&capturewidth,\t\t\t\"Video Capture Controls\");\n\tCvar_Register(&captureheight,\t\t\t\"Video Capture Controls\");\n\tCvar_Register(&capturedriver,\t\t\t\"Video Capture Controls\");\n\tCvar_Register(&capturecodec,\t\t\t\"Video Capture Controls\");\n\tCvar_Register(&capturethrottlesize,\t\t\"Video Capture Controls\");\n\tCvar_Register(&capturesoundbits,\t\t\"Video Capture Controls\");\n\tCvar_Register(&capturesoundchannels,\t\"Video Capture Controls\");\n#endif\n\n#ifdef HAVE_MEDIA_DECODER\n\tCmd_AddCommand(\"playclip\", Media_PlayVideoWindowed_f);\n\tCmd_AddCommand(\"playvideo\", Media_PlayFilm_f);\n\tCmd_AddCommand(\"playfilm\", Media_PlayFilm_f);\n\tCmd_AddCommand(\"cinematic\", Media_PlayFilm_f);\t//q3: name <1:hold, 2:loop>\n#endif\n\n\tCmd_AddCommand(\"music\", Media_NamedTrack_f);\n\tCmd_AddCommand(\"stopmusic\", Media_StopTrack_f);\n}\n"
  },
  {
    "path": "engine/client/m_multi.c",
    "content": "//read menu.h\n\n#include \"quakedef.h\"\n#include \"winquake.h\"\n#include \"shader.h\"\n\n#ifndef NOBUILTINMENUS\n\nextern cvar_t maxclients;\n\n/* MULTIPLAYER MENU */\nvoid M_Menu_MultiPlayer_f (void)\n{\n\tmenubutton_t *b;\n\temenu_t *menu;\n\tmpic_t *p;\n\tint mgt;\n\tstatic menuresel_t resel;\n\n\tp = NULL;\n\n\tmgt = M_GameType();\n\n\tmenu = M_CreateMenu(0);\n\n#ifdef Q2CLIENT\n\tif (mgt == MGT_QUAKE2)\n\t{\n\t\tMC_AddCenterPicture(menu, 4, 24, \"pics/m_banner_multiplayer\");\n\n\t\tmenu->selecteditem = (menuoption_t*)\n\t\tMC_AddConsoleCommand\t(menu, 64, 170, 40,\t\"Join network server\",\t\"menu_slist\\n\");\n#ifdef HAVE_PACKET\n\t\tMC_AddConsoleCommand\t(menu, 64, 170, 48,\t\"Quick Connect\",\t\t\"quickconnect qw\\n\");\n#endif\n#ifdef Q2SERVER\n\t\tMC_AddConsoleCommand\t(menu, 64, 170, 56,\t\"Start network server\",\t\"menu_newmulti\\n\");\n#endif\n\t\tMC_AddConsoleCommand\t(menu, 64, 170, 64,\t\"Player setup\",\t\t\t\"menu_setup\\n\");\n\t\tMC_AddConsoleCommand\t(menu, 64, 170, 72,\t\"Demos\",\t\t\t\t\"menu_demo\\n\");\n\n\t\tmenu->cursoritem = (menuoption_t*)MC_AddWhiteText(menu, 48, 0, 40, NULL, false);\n\t\treturn;\n\t}\n\telse\n#endif\n#ifdef HEXEN2\n\t\tif (mgt == MGT_HEXEN2)\n\t{\n\t\tMC_AddCenterPicture(menu, 0, 60, \"gfx/menu/title4.lmp\");\n\n\t\tmgt=64;\n\t\tmenu->selecteditem = (menuoption_t*)\n\t\tMC_AddConsoleCommandHexen2BigFont\t(menu, 80, mgt,\t\"Server List \",\t\"menu_slist\\n\");mgt+=20;\n\t\tMC_AddConsoleCommandHexen2BigFont\t(menu, 80, mgt,\t\"New Server  \",\t\"menu_newmulti\\n\");mgt+=20;\n\t\tMC_AddConsoleCommandHexen2BigFont\t(menu, 80, mgt,\t\"Player Setup\",\t\"menu_setup\\n\");mgt+=20;\n\t\tMC_AddConsoleCommandHexen2BigFont\t(menu, 80, mgt,\t\"Demos       \",\t\"menu_demo\\n\");mgt+=20;\n\n\t\tmenu->cursoritem = (menuoption_t *)MC_AddCursor(menu, &resel, 48, 64);\n\t\treturn;\n\t}\n\telse\n#endif\n\t\tif (QBigFontWorks())\n\t{\n\t\tMC_AddPicture(menu, 16, 4, 32, 144, \"gfx/qplaque.lmp\");\n\t\tMC_AddCenterPicture(menu, 4, 24, \"gfx/p_multi.lmp\");\n\n\t\tmgt=32;\n\t\tmenu->selecteditem = (menuoption_t*)\n\t\tMC_AddConsoleCommandQBigFont\t(menu, 72, mgt,\tlocaltext(\"Server List \"),\t\"menu_slist\\n\");mgt+=20;\n#ifdef HAVE_PACKET\n\t\tMC_AddConsoleCommandQBigFont\t(menu, 72, mgt,\tlocaltext(\"Quick Connect\"), \"quickconnect qw\\n\");mgt+=20;\n#endif\n\t\tMC_AddConsoleCommandQBigFont\t(menu, 72, mgt,\tlocaltext(\"New Server  \"),\t\"menu_newmulti\\n\");mgt+=20;\n\t\tMC_AddConsoleCommandQBigFont\t(menu, 72, mgt,\tlocaltext(\"Player Setup\"),\t\"menu_setup\\n\");mgt+=20;\n\t\tMC_AddConsoleCommandQBigFont\t(menu, 72, mgt,\tlocaltext(\"Demos       \"),\t\"menu_demo\\n\");mgt+=20;\n\n\t\tmenu->cursoritem = (menuoption_t*)MC_AddCursor(menu, &resel, 54, 32);\n\t\treturn;\n\t}\n\telse\n\t{\n\t\tint width;\n\n\t\tp = R2D_SafeCachePic(\"gfx/mp_menu.lmp\");\n\t\tif (p)\n\t\t{\n\t\t\tMC_AddPicture(menu, 16, 4, 32, 144, \"gfx/qplaque.lmp\");\n\t\t\tMC_AddCenterPicture(menu, 4, 24, \"gfx/p_multi.lmp\");\n\t\t\tMC_AddPicture(menu, 72, 32, 232, 64, \"gfx/mp_menu.lmp\");\n\t\t}\n\n\t\tif (R_GetShaderSizes(p, &width, NULL, true) <= 0)\n\t\t\twidth = 232;\n\n\t\tb = MC_AddConsoleCommand(menu, 72, 320, 32, \"\", \"menu_slist\\n\");\n\t\tmenu->selecteditem = (menuoption_t*)b;\n\t\tb->common.height = 20;\n\t\tb->common.width = width;\n\t\tb = MC_AddConsoleCommand(menu, 72, 320, 52, \"\", \"menu_newmulti\\n\");\n\t\tb->common.height = 20;\n\t\tb->common.width = width;\n\t\tb = MC_AddConsoleCommand(menu, 72, 320, 72, \"\", \"menu_setup\\n\");\n\t\tb->common.height = 20;\n\t\tb->common.width = width;\n\n\t\tb = MC_AddConsoleCommand(menu, 72, 320, 92, \"\", \"menu_demo\\n\");\n\t\tMC_AddWhiteText(menu, 72, 0, 92+20/2-6, localtext(\"^aDemos\"), false);\n\t\tb->common.height = 20;\n\t\tb->common.width = width;\n\n#ifdef HAVE_PACKET\n\t\tb = MC_AddConsoleCommand(menu, 72, 320, 112, \"\", \"quickconnect qw\\n\");\n\t\tMC_AddWhiteText(menu, 72, 0, 112+20/2-6, localtext(\"^aQuick Connect\"), false);\n\t\tb->common.height = 20;\n\t\tb->common.width = width;\n#endif\n\t}\n\n\tmenu->cursoritem = (menuoption_t*)MC_AddCursor(menu, &resel, 54, 32);\n}\n\nextern cvar_t\tteam, skin;\nextern cvar_t\ttopcolor;\nextern cvar_t\tbottomcolor;\nextern cvar_t skill;\ntypedef struct {\n\tmenuedit_t *nameedit;\n\tmenuedit_t *teamedit;\n\tmenuedit_t *skinedit;\n#ifdef HEXEN2\n\tmenucombo_t *classedit;\n\tint ticlass;\n#endif\n\tmenucombo_t *modeledit;\n\tunsigned int topcolour;\n\tunsigned int lowercolour;\n\n\tint tiwidth, tiheight;\n\tqbyte translationimage[128*128];\n} setupmenu_t;\nvoid MSetup_Removed(emenu_t *menu)\n{\n\tchar bot[64], top[64];\n\tsetupmenu_t *info = menu->data;\n\n\tif (info->nameedit)\n\t\tCvar_Set(&name, info->nameedit->text);\n\tif (info->teamedit)\n\t\tCvar_Set(&team, info->teamedit->text);\n\tif (info->skinedit)\n\t\tCvar_Set(&skin, info->skinedit->text);\n#ifdef HEXEN2\n\tif (info->classedit)\n\t\tCvar_SetValue(Cvar_FindVar(\"cl_playerclass\"), info->classedit->selectedoption+1);\n#endif\n\tif (info->lowercolour >= 16)\n\t\tQ_snprintfz(bot, sizeof(bot), \"0x%x\", info->lowercolour&0xffffff);\n\telse\n\t\tQ_snprintfz(bot, sizeof(bot), \"%i\", info->lowercolour);\n\tif (info->topcolour >= 16)\n\t\tQ_snprintfz(top, sizeof(top), \"0x%x\", info->topcolour&0xffffff);\n\telse\n\t\tQ_snprintfz(top, sizeof(top), \"%i\", info->topcolour);\n\tCbuf_AddText(va(\"color %s %s\\n\", top, bot), RESTRICT_LOCAL);\n}\n\n//http://axonflux.com/handy-rgb-to-hsl-and-rgb-to-hsv-color-model-c\nstatic void rgbtohsv(unsigned int rgb, vec3_t result)\n{\n\tint r = (rgb>>16)&0xff, g = (rgb>>8)&0xff, b = (rgb>>0)&0xff;\n\tfloat maxc = max(r, max(g, b)), minc = min(r, min(g, b));\n    float h, s, l = (maxc + minc) / 2;\n\n\tfloat d = maxc - minc;\n\tif (maxc)\n\t\ts = d / maxc;\n\telse\n\t\ts = 0;\n\n\tif(maxc == minc)\n\t{\n\t\th = 0; // achromatic\n\t}\n\telse\n\t{\n\t\tif (maxc == r)\n\t\t\th = (g - b) / d + ((g < b) ? 6 : 0);\n\t\telse if (maxc == g)\n\t\t\th = (b - r) / d + 2;\n\t\telse\n\t\t\th = (r - g) / d + 4;\n\t\th /= 6;\n    }\n\n\tresult[0] = h;\n\tresult[1] = s;\n\tresult[2] = l;\n};\n\n//http://axonflux.com/handy-rgb-to-hsl-and-rgb-to-hsv-color-model-c\nstatic unsigned int hsvtorgb(float inh, float s, float v)\n{\n\tint r, g, b;\n\tfloat h = inh - (int)floor(inh);\n\tint i = h * 6;\n\tfloat f = h * 6 - i;\n\tfloat p = v * (1 - s);\n\tfloat q = v * (1 - f * s);\n\tfloat t = v * (1 - (1 - f) * s);\n\tswitch(i)\n\t{\n\tdefault:\n\tcase 0: r = v*0xff, g = t*0xff, b = p*0xff; break;\n\tcase 1: r = q*0xff, g = v*0xff, b = p*0xff; break;\n\tcase 2: r = p*0xff, g = v*0xff, b = t*0xff; break;\n\tcase 3: r = p*0xff, g = q*0xff, b = v*0xff; break;\n\tcase 4: r = t*0xff, g = p*0xff, b = v*0xff; break;\n\tcase 5: r = v*0xff, g = p*0xff, b = q*0xff; break;\n\t}\n\n\treturn 0xff000000 | (r<<16)|(g<<8)|(b<<0);\n};\n\nqboolean SetupMenuColour (union menuoption_s *option,struct emenu_s *menu, int key)\n{\n\tsetupmenu_t *info = menu->data;\n\tunsigned int *ptr = (*option->button.text == 'T')?&info->topcolour:&info->lowercolour;\n\n\t//okay, this is a bit weird.\n\t//fte supports rgb colours, but we only allow hue to be chosen via the menu (people picking pure black are annoying, also conversions and precisions limit us)\n\t//for NQ compat, we stick to old-skool values (so we don't end up with far too many teams)\n\t//but we give the top free reign.\n\t//unless they hold shift, in which case it switches around\n\t//this allows for whatever you want\n\tif (key == K_ENTER || key == K_KP_ENTER || key == K_GP_DIAMOND_CONFIRM || key == K_RIGHTARROW || key == K_KP_RIGHTARROW || key == K_MOUSE1 || key == K_TOUCHTAP || key == K_GP_DPAD_RIGHT)\n\t{\n\t\tif ((keydown[K_LSHIFT] || keydown[K_RSHIFT]) ^ (ptr == &info->topcolour))\n\t\t{\n\t\t\tvec3_t hsv;\n\t\t\trgbtohsv(*ptr, hsv);\n\t\t\t*ptr = hsvtorgb(hsv[0]+((key == K_MOUSE1)?5:1)/128.0, 1, 1);//hsv[1], hsv[2]);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (*ptr >= 13 || *ptr >= 16)\n\t\t\t\t*ptr = 0;\n\t\t\telse\n\t\t\t\t*ptr += 1;\n\t\t}\n\t\tS_LocalSound (\"misc/menu2.wav\");\n\t\treturn true;\n\t}\n\tif (key == K_DEL)\n\t{\n\t\t*ptr = 0;\n\t\tS_LocalSound (\"misc/menu2.wav\");\n\t\treturn true;\n\t}\n\tif (key == K_LEFTARROW || key == K_KP_LEFTARROW || key == K_GP_DPAD_LEFT)\n\t{\n\t\tif ((keydown[K_LSHIFT] || keydown[K_RSHIFT]) ^ (ptr == &info->topcolour))\n\t\t{\n\t\t\tvec3_t hsv;\n\t\t\trgbtohsv(*ptr, hsv);\n\t\t\t*ptr = hsvtorgb(hsv[0]-1/128.0, 1, 1);//hsv[1], hsv[2]);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (*ptr==0 || *ptr >= 16)\n\t\t\t\t*ptr=12;\n\t\t\telse\n\t\t\t\t*ptr -= 1;\n\t\t}\n\t\tS_LocalSound (\"misc/menu2.wav\");\n\t\treturn true;\n\t}\n\treturn false;\n}\n\n\ntypedef struct {\n\tchar **names;\n\tint entries;\n\tint match;\n} q2skinsearch_t;\n\nint QDECL q2skin_enumerate(const char *name, qofs_t fsize, time_t mtime, void *parm, searchpathfuncs_t *spath)\n{\n\tchar blah[MAX_QPATH];\n\tq2skinsearch_t *s = parm;\n\n\tCOM_StripExtension(name+8, blah, sizeof(blah));\n\tif (strlen(blah) < 2)\n\t\treturn false;\t//this should never happen\n\tblah[strlen(blah)-2] = 0;\n\n\ts->names = BZ_Realloc(s->names, ((s->entries+64)&~63) * sizeof(char*));\n\ts->names[s->entries] = BZ_Malloc(strlen(blah)+1);\n\tstrcpy(s->names[s->entries], blah);\n\n\tif (!strcmp(blah, skin.string))\n\t\ts->match = s->entries;\n\n\ts->entries++;\n\treturn true;\n}\nvoid q2skin_destroy(q2skinsearch_t *s)\n{\n\tint i;\n\tfor (i = 0; i < s->entries; i++)\n\t{\n\t\tBZ_Free(s->names[i]);\n\t}\n\tBZ_Free(s);\n}\n\nqboolean MSetupQ2_ChangeSkin (struct menucustom_s *option,struct emenu_s *menu, int key, unsigned int unicode)\n{\n\tsetupmenu_t *info = menu->data;\n\tq2skinsearch_t *s = Z_Malloc(sizeof(*s));\n\tCOM_EnumerateFiles(va(\"players/%s/*_i.*\", info->modeledit->values[info->modeledit->selectedoption]), q2skin_enumerate, s);\n\tif (key == K_ENTER || key == K_KP_ENTER || key == K_RIGHTARROW || key == K_KP_RIGHTARROW || key == K_GP_DPAD_RIGHT || key == K_GP_DIAMOND_CONFIRM)\n\t{\n\t\ts->match ++;\n\t\tif (s->match>=s->entries)\n\t\t\ts->match=0;\n\t}\n\telse if (key == K_LEFTARROW || key == K_KP_LEFTARROW || key == K_GP_DPAD_LEFT || key == K_GP_DIAMOND_ALTCONFIRM)\n\t{\n\t\ts->match --;\n\t\tif (s->match<=0)\n\t\t\ts->match=s->entries-1;\n\t}\n\telse\n\t{\n\t\tq2skin_destroy(s);\n\t\treturn false;\n\t}\n\tif (s->entries)\n\t\tCvar_Set(&skin, s->names[s->match]);\n\tS_LocalSound (\"misc/menu2.wav\");\n\tq2skin_destroy(s);\n\treturn true;\n}\nvoid MSetupQ2_TransDraw (int x, int y, menucustom_t *option, emenu_t *menu)\n{\n\tsetupmenu_t *info = menu->data;\n\tmpic_t\t*p;\n\tint w, h;\n\n\tDraw_FunStringWidth(x, y, \"Skin\", 160-64, true, !menu->cursoritem && menu->selecteditem == (menuoption_t*)option);\n\tx += 160-40;//172-16\n\n\tp = R2D_SafeCachePic (va(\"players/%s_i\", skin.string));\n\tif (!R_GetShaderSizes(p, &w, &h, false))\n\t{\n\t\tq2skinsearch_t *s = Z_Malloc(sizeof(*s));\n\t\tCOM_EnumerateFiles(va(\"players/%s/*_i.*\", info->modeledit->values[info->modeledit->selectedoption]), q2skin_enumerate, s);\n\t\tif (s->entries)\n\t\t\tCvar_Set(&skin, s->names[rand()%s->entries]);\n\t\tq2skin_destroy(s);\n\n\t\tp = R2D_SafeCachePic (va(\"players/%s_i\", skin.string));\n\t}\n\tif (R_GetShaderSizes(p, &w, &h, false)>0)\n\t\tR2D_ScalePic (x, y-8, w, h, p);\n}\n\nvoid MSetup_TransDraw (int x, int y, menucustom_t *option, emenu_t *menu)\n{\n\tunsigned int translationTable[256];\n\tsetupmenu_t *info = menu->data;\n\tmpic_t\t*p;\n\tvoid *f;\n\tqboolean reloadtimage = false;\n\tunsigned int pc = 0;\n\n\tif (info->skinedit && info->skinedit->modified)\n\t{\n\t\tinfo->skinedit->modified = false;\n\t\treloadtimage = true;\n\t}\n#ifdef HEXEN2\n\tif (info->classedit)\n\t{\n\t\tif (info->classedit->selectedoption != info->ticlass)\n\t\t{\n\t\t\tinfo->ticlass = info->classedit->selectedoption;\n\t\t\treloadtimage = true;\n\t\t}\n\t\tpc = info->ticlass+1;\n\t}\n#endif\n\n\tif (reloadtimage)\n\t{\n#ifdef HEXEN2\n\t\tif (info->classedit)\t//quake2 main menu.\n\t\t{\n\t\t\tFS_LoadFile(va(\"gfx/menu/netp%i.lmp\", info->ticlass+1), &f);\n\t\t}\n\t\telse\n#endif\n\t\t{\n\t\t\tif (*info->skinedit->text)\n\t\t\t\tFS_LoadFile(va(\"gfx/player/%s.lmp\", info->skinedit->text), &f);\n\t\t\telse\n\t\t\t\tf = NULL;\n\t\t\tif (!f)\n\t\t\t\tFS_LoadFile(\"gfx/menuplyr.lmp\", &f);\n\t\t}\n\n\t\tif (f)\n\t\t{\n\t\t\tinfo->tiwidth = ((int*)f)[0];\n\t\t\tinfo->tiheight = ((int*)f)[1];\n\t\t\tif (info->tiwidth * info->tiheight > sizeof(info->translationimage))\n\t\t\t\tinfo->tiwidth = info->tiheight = 0;\n\t\t\tmemcpy(info->translationimage, (char*)f+8, info->tiwidth*info->tiheight);\n\t\t\tFS_FreeFile(f);\n\t\t}\n\t}\n\n\tR2D_ImageColours(1,1,1,1);\n\tp = R2D_SafeCachePic (\"gfx/bigbox.lmp\");\n\tif (R_GetShaderSizes(p, NULL, NULL, false)>0)\n\t\tR2D_ScalePic (x-12, y-8, 72, 72, p);\n\n\tM_BuildTranslationTable(pc, info->topcolour, info->lowercolour, translationTable);\n\tR2D_TransPicTranslate (x, y, info->tiwidth, info->tiheight, info->translationimage, translationTable);\n}\n\nvoid M_Menu_Setup_f (void)\n{\n\tsetupmenu_t *info;\n\temenu_t *menu;\n\tmenucustom_t *ci;\n\tmenubutton_t *b;\n\tstatic menuresel_t resel;\n\tint y;\n\n#ifdef Q2CLIENT\n\tif (M_GameType() == MGT_QUAKE2)\t//quake2 main menu.\n\t{\n\t\tstatic const char *modeloptions[] =\n\t\t{\n\t\t\t\"male\",\n\t\t\t\"female\",\n\t\t\tNULL\n\t\t};\n\t\tmenucustom_t *cu;\n\n\t\tmenu = M_CreateMenu(sizeof(setupmenu_t));\n\t\tmenu->remove = MSetup_Removed;\n\t\tinfo = menu->data;\n//\t\tmenu->key = MC_Main_Key;\n\n\t\tMC_AddPicture(menu, 0, 4, 38, 166, \"pics/m_main_plaque\");\n\t\tMC_AddPicture(menu, 0, 173, 36, 42, \"pics/m_main_logo\");\n\n\t\tMC_AddCenterPicture(menu, 4, 24, \"pics/m_banner_player_setup\");\n\n\t\tmenu->selecteditem = (menuoption_t*)\n\t\t(info->nameedit = MC_AddEdit(menu, 64, 160, 40, localtext(\"Your name\"), name.string));\n\t\t(info->modeledit = MC_AddCvarCombo(menu, 64, 160,72, localtext(\"model\"), &skin, (const char **)modeloptions, (const char **)modeloptions));\n\t\tinfo->modeledit->selectedoption = !strncmp(skin.string, \"female\", 6);\n\t\tcu = MC_AddCustom(menu, 64, 88+16, NULL, 0, NULL);\n\t\tcu->draw = MSetupQ2_TransDraw;\n\t\tcu->key = MSetupQ2_ChangeSkin;\n\n\t\tmenu->cursoritem = (menuoption_t*)MC_AddCursorSmall(menu, &resel, 54);\n\t\treturn;\n\t}\n#endif\n\n\tmenu = M_CreateMenu(sizeof(setupmenu_t));\n\tmenu->remove = MSetup_Removed;\n\tinfo = menu->data;\n\n//\tMC_AddPicture(menu, 72, 32, Draw_CachePic (\"gfx/mp_menu.lmp\") );\n\n\ty = 40;\n\tmenu->selecteditem = (menuoption_t*)\n\t(info->nameedit = MC_AddEdit(menu, 64, 160, y, localtext(\"Your name\"), name.string)); y+= info->nameedit->common.height;\n\t(info->teamedit = MC_AddEdit(menu, 64, 160, y, localtext(\"Your team\"), team.string)); y+= info->teamedit->common.height;\n#ifdef HEXEN2\n\tinfo->ticlass = -1;\n\tif (M_GameType() == MGT_HEXEN2)\n\t{\n\t\tstatic const char *classnames[] =\n\t\t{\n\t\t\t\"Paladin\",\n\t\t\t\"Crusader\",\n\t\t\t\"Necromancer\",\n\t\t\t\"Assasin\",\n\t\t\t\"Demoness\",\n\t\t\tNULL\n\t\t};\n\t\tcvar_t *pc = Cvar_Get(\"cl_playerclass\", \"1\", CVAR_USERINFO|CVAR_ARCHIVE, \"Hexen2\");\n\t\t(info->classedit = MC_AddCombo(menu, 64, 160, y, localtext(\"Your class\"), (const char **)classnames, pc->ival-1)); y+= info->classedit->common.height;\n\n\t\t//trim options if the artwork is missing.\n\t\twhile (info->classedit->numoptions && !COM_FCheckExists(va(\"gfx/menu/netp%i.lmp\", info->classedit->numoptions)))\n\t\t\tinfo->classedit->numoptions--;\n\t}\n\telse\n#endif\n\t{\n\t\tMC_AddPicture(menu, 16, 4, 32, 144, \"gfx/qplaque.lmp\");\n\t\tMC_AddCenterPicture(menu, 4, 24, \"gfx/p_multi.lmp\");\n\n\t\t(info->skinedit = MC_AddEdit(menu, 64, 160, y, localtext(\"Your skin\"), skin.string)); y+= info->skinedit->common.height;\n\t}\n\n\tci = MC_AddCustom(menu, 172+32, y, NULL, 0, NULL);\n\tci->draw = MSetup_TransDraw;\n\tci->key = NULL;\n\n\tMC_AddCommand(menu, 64, 160, y+8, localtext(\"Top colour\"), SetupMenuColour);\n\tMC_AddCommand(menu, 64, 160, y+32, localtext(\"Lower colour\"), SetupMenuColour);\n\ty+= 16;\n\ty+=4;\n\n\tb = MC_AddConsoleCommand(menu, 64, 204, 168, localtext(\"Network Settings\"), \"menu_network\\n\");\n\tb->common.tooltip = localtext(\"Change network and client prediction settings.\");\n\ty += b->common.height;\n\tb = MC_AddConsoleCommand(menu, 64, 204, 176, localtext(\"Teamplay Settings\"), \"menu_teamplay\\n\");\n\tb->common.tooltip = localtext(\"Change teamplay macro settings.\");\n\ty += b->common.height;\n\tmenu->cursoritem = (menuoption_t*)MC_AddCursorSmall(menu, &resel, 54);\n\n\n\tinfo->lowercolour = bottomcolor.value;\n\tinfo->topcolour = topcolor.value;\n\tif (info->skinedit)\n\t\tinfo->skinedit->modified = true;\n}\n\n\n\n\n#ifdef CLIENTONLY\nvoid M_Menu_GameOptions_f (void)\n{\n}\n#else\n\ntypedef struct {\n\tmenuedit_t *hostnameedit;\n\tmenucombo_t *publicgame;\n\tmenucombo_t *deathmatch;\n\tmenucombo_t *numplayers;\n\tmenucombo_t *teamplay;\n\tmenucombo_t *skill;\n\tmenucombo_t *timelimit;\n\tmenucombo_t *fraglimit;\n\tmenucombo_t *mapname;\n\tmenucheck_t *rundedicated;\n\n\tint topcolour;\n\tint lowercolour;\n} newmultimenu_t;\n\nstatic const char *numplayeroptions[] = {\n\t\"2\",\n\t\"3\",\n\t\"4\",\n\t\"8\",\n\t\"12\",\n\t\"16\",\n\t\"20\",\n\t\"24\",\n\t\"32\",\n\tNULL\n};\n\nqboolean MultiBeginGame (union menuoption_s *option,struct emenu_s *menu, int key)\n{\n\tnewmultimenu_t *info = menu->data;\n\tchar quoted[1024];\n\tif (key != K_ENTER && key != K_KP_ENTER && key != K_GP_DIAMOND_CONFIRM && key != K_MOUSE1 && key != K_TOUCHTAP)\n\t\treturn false;\n\n\tif (cls.state)\n\t\tCbuf_AddText(\"disconnect\\n\", RESTRICT_LOCAL);\n\n\tCbuf_AddText(\"sv_playerslots \\\"\\\"\\n\", RESTRICT_LOCAL);\t//just in case.\n\tCbuf_AddText(va(\"maxclients \\\"%s\\\"\\n\", numplayeroptions[info->numplayers->selectedoption]), RESTRICT_LOCAL);\n\tif (info->rundedicated->value)\n\t\tCbuf_AddText(\"setrenderer dedicated\\n\", RESTRICT_LOCAL);\n\tCbuf_AddText(va(\"hostname %s\\n\", COM_QuotedString(info->hostnameedit->text, quoted, sizeof(quoted), false)), RESTRICT_LOCAL);\n\tCbuf_AddText(va(\"deathmatch %i\\n\", info->deathmatch->selectedoption), RESTRICT_LOCAL);\n\tif (!info->deathmatch->selectedoption)\n\t\tCbuf_AddText(\"coop 1\\n\", RESTRICT_LOCAL);\n\telse\n\t\tCbuf_AddText(\"coop 0\\n\", RESTRICT_LOCAL);\n\tCbuf_AddText(va(\"teamplay %i\\n\", info->teamplay->selectedoption), RESTRICT_LOCAL);\n\tCbuf_AddText(va(\"skill %i\\n\", info->skill->selectedoption), RESTRICT_LOCAL);\n\tCbuf_AddText(va(\"timelimit %i\\n\", info->timelimit->selectedoption*5), RESTRICT_LOCAL);\n\tCbuf_AddText(va(\"fraglimit %i\\n\", info->fraglimit->selectedoption*10), RESTRICT_LOCAL);\n\n\tif (info->publicgame->selectedoption-1 == 2)\n\t{\n\t\tconst char *hostname = info->hostnameedit->text;\n\t\tconst char *shn;\n\t\tfor (shn = hostname; *shn; shn++)\n\t\t{\n\t\t\tif (*shn >= 'a' && *shn <= 'z')\n\t\t\t\tcontinue;\n\t\t\tif (*shn >= 'A' && *shn <= 'Z')\n\t\t\t\tcontinue;\n\t\t\tif (*shn >= '0' && *shn <= '9')\n\t\t\t\tcontinue;\n\t\t\tif (*shn == '-' || *shn <= '_')\n\t\t\t\tcontinue;\n\t\t\tbreak;\n\t\t}\n\t\tif (*shn || !*hostname || !Q_strcasecmp(hostname, \"player\") || !Q_strcasecmp(hostname, \"unnamed\"))\t//not simple enough...\n\t\t\tCbuf_AddText(va(\"sv_public \\\"/\\\"\\n\"), RESTRICT_LOCAL);\n\t\telse\n\t\t\tCbuf_AddText(va(\"sv_public \\\"/%s\\\"\\n\", info->hostnameedit->text), RESTRICT_LOCAL);\n\t}\n\telse\n\t\tCbuf_AddText(va(\"sv_public %i\\n\", info->publicgame->selectedoption-1), RESTRICT_LOCAL);\n\n\tCbuf_AddText(va(\"map \\\"%s\\\"\\n\", info->mapname->options[info->mapname->selectedoption]), RESTRICT_LOCAL);\n\n\tif (info->rundedicated->value)\n\t{\n\t\tCbuf_AddText(\"echo You can use the setrenderer command to return to a graphical interface at any time\\n\", RESTRICT_LOCAL);\n\t}\n\n\tM_RemoveAllMenus(true);\n\n\treturn true;\n}\n\nstruct mapopts_s\n{\n\tsize_t max, count;\n\tconst char **maps;\n};\nstatic int QDECL M_Menu_GameOptions_AddMap(const char *fname, qofs_t fsize, time_t mtime, void *parm, searchpathfuncs_t *spath)\n{\n\tstruct mapopts_s *ctx = parm;\n\tsize_t i;\n\tchar *ext;\n\tchar trimmedfname[MAX_QPATH];\n\tif (Q_strncasecmp(fname, \"maps/\", 5))\n\t\treturn true; //o.O\n\tfname += 5;\n\tif (fname[0] == 'b' && fname[1] == '_')\n\t\treturn true;\t//stoopid ammo boxes.\n\text = strrchr(fname, '.');\n\tif (ext && !strcmp(ext, \".bsp\") && ext-fname<sizeof(trimmedfname))\n\t{\n\t\tmemcpy(trimmedfname, fname, ext-fname);\n\t\ttrimmedfname[ext-fname] = 0;\n\t\tfname = trimmedfname;\n\t}\n\n\tfor (i = 0; i < ctx->count; i++)\n\t\tif (!Q_strcasecmp(ctx->maps[i], fname))\n\t\t\treturn true; //don't do dupes.\n\tif (ctx->count+1 >= ctx->max)\n\t\tZ_ReallocElements((void**)&ctx->maps, &ctx->max, ctx->count + 64, sizeof(char*));\n\tctx->maps[ctx->count++] = Z_StrDup(fname);\n\treturn true;\n}\n\n\nvoid M_Menu_GameOptions_f (void)\n{\n\tstatic const char *deathmatchoptions[] = {\n\t\t\"Cooperative\",\n\t\t\"Deathmatch 1\",\n\t\t\"Deathmatch 2\",\n\t\t\"Deathmatch 3\",\n\t\t\"Deathmatch 4\",\n\t\t\"Deathmatch 5\",\n\t\tNULL\n\t};\n\tstatic const char *teamplayoptions[] = {\n\t\t\"off\",\t\t\t\t//no teams at all\n\t\t\"no self+team fire\",\t//don't hurt same-team (with bugs in coop)\n\t\t\"friendly fire\",\t//scoreboard shows teams, gamecode doesn't care\n\t\t\"no team fire (qw-only)\",\t//like 1, except you still hurt yourself.\n\t\tNULL\n\t};\n\tstatic const char *teamplayoptions_rogue[] = {\n\t\t\"off\",\t\t\t\t//no teams at all\n\t\t\"no self+team fire\",\t//don't hurt same-team (with bugs in coop)\n\t\t\"friendly fire\",\t//scoreboard shows teams, gamecode doesn't care\n\t\t\"tag\",\n\t\t\"Capture The Flag\",\n\t\t\"One Flag CTF\",\n\t\t\"Three Team CTF\",\n\t\tNULL\n\t};\n\tstatic const char *skilloptions[] = {\n\t\t\"Easy\",\n\t\t\"Medium\",\n\t\t\"Hard\",\n\t\t\"NIGHTMARE\",\n\t\tNULL\n\t};\n\tstatic const char *timelimitoptions[] = {\n\t\t\"no limit\",\n\t\t\"5 minutes\",\n\t\t\"10 minutes\",\n\t\t\"15 minutes\",\n\t\t\"20 minutes\",\n\t\t\"25 minutes\",\n\t\t\"30 minutes\",\n\t\t\"35 minutes\",\n\t\t\"40 minutes\",\n\t\t\"45 minutes\",\n\t\t\"50 minutes\",\n\t\t\"55 minutes\",\n\t\t\"1 hour\",\n\t\tNULL\n\t};\n\tstatic const char *fraglimitoptions[] = {\n\t\t\"no limit\",\n\t\t\"10 frags\",\n\t\t\"20 frags\",\n\t\t\"30 frags\",\n\t\t\"40 frags\",\n\t\t\"50 frags\",\n\t\t\"60 frags\",\n\t\t\"70 frags\",\n\t\t\"80 frags\",\n\t\t\"90 frags\",\n\t\t\"100 frags\",\n\t\tNULL\n\t};\n\tstatic const char *publicoptions[] = {\n\t\t\"Splitscreen Only\",\n\t\t\"Private/LAN\",\n\t\t\"Public (Manual)\",\n\t\t\"Public (Holepunch)\",\n\t\tNULL\n\t};\n\textern cvar_t sv_public;\n\tnewmultimenu_t *info;\n\temenu_t *menu;\n\tint y = 40;\n\tint mgt;\n\tint players;\n\tstruct mapopts_s mapopts = {0};\n\n\tmenu = M_CreateMenu(sizeof(newmultimenu_t));\n\tinfo = menu->data;\n\n\tmgt = M_GameType();\n\n\tif (mgt == MGT_QUAKE2)\n\t{\n\t\tMC_AddCenterPicture(menu, 4, 24, \"pics/m_banner_start_server\");\n\t\ty += 8;\n\t}\n\telse if (mgt == MGT_HEXEN2)\n\t{\n\t}\n\telse\n\t{\n\t\tMC_AddPicture(menu, 16, 4, 32, 144, \"gfx/qplaque.lmp\");\n\t\tMC_AddCenterPicture(menu, 4, 24, \"gfx/p_multi.lmp\");\n\t}\n\n//\tMC_AddPicture(menu, 72, 32, (\"gfx/mp_menu.lmp\") );\n\n\tmenu->selecteditem = (menuoption_t*)\n\tMC_AddCommand\t\t\t\t\t\t(menu, 64, 160, y,\tlocaltext(\"Start game\"), MultiBeginGame);y+=16;\n\n\ty+=4;\n\tinfo->hostnameedit\t= MC_AddEdit\t(menu, 64, 160, y,\t\t\tlocaltext(\"Hostname\"), name.string);y+=info->hostnameedit->common.height;\n\tinfo->publicgame\t= MC_AddCombo\t(menu, 64, 160, y,\t\t\tlocaltext(\"Public\"), publicoptions, bound(0, sv_public.ival+1, 4));y+=8;\n#if !defined(FTE_TARGET_WEB) && defined(HAVE_DTLS)\n\t{\n\t\tstatic const char *encoptions[] =\n\t\t{\n\t\t\t\"Disabled\",\n\t\t\t\"Accept\",\n\t\t\t\"Request\",\n\t\t\t\"Require\",\n\t\t\tNULL\n\t\t};\n\t\tMC_AddCvarCombo (menu, 64, 160, y,\t\tlocaltext(\"DTLS Encryption\"), &net_enable_dtls, encoptions, NULL);y+=8;\n\t}\n#endif\n\ty+=4;\n\n\tfor (players = 0; players < sizeof(numplayeroptions)/ sizeof(numplayeroptions[0]); players++)\n\t{\n\t\tif (atoi(numplayeroptions[players]) >= maxclients.value)\n\t\t\tbreak;\n\t}\n\n\tinfo->numplayers\t= MC_AddCombo\t(menu, 64, 160, y,\t\t\tlocaltext(\"Max players\"), (const char **)numplayeroptions,\tplayers);y+=8;\n\n\tinfo->deathmatch\t= MC_AddCombo\t(menu, 64, 160, y,\t\t\tlocaltext(\"Deathmatch\"), (const char **)deathmatchoptions,\tdeathmatch.value);y+=8;\n\tinfo->teamplay\t\t= MC_AddCombo\t(menu, 64, 160, y,\t\t\tlocaltext(\"Teamplay\"), (!Q_strcasecmp(FS_GetGamedir(true), \"rogue\")?(const char **)teamplayoptions_rogue:(const char **)teamplayoptions),\t\tteamplay.value);y+=8;\n\tinfo->skill\t\t\t= MC_AddCombo\t(menu, 64, 160, y,\t\t\tlocaltext(\"Skill\"), (const char **)skilloptions,\t\t\tskill.value);y+=8;\n\tinfo->rundedicated\t= MC_AddCheckBox(menu, 64, 160, y,\t\t\tlocaltext(\"dedicated\"), NULL, 0);y+=8;\n\ty+=8;\n\tinfo->timelimit\t\t= MC_AddCombo\t(menu, 64, 160, y,\t\t\tlocaltext(\"Time Limit\"), (const char **)timelimitoptions,\t\ttimelimit.value/5);y+=8;\n\tinfo->fraglimit\t\t= MC_AddCombo\t(menu, 64, 160, y,\t\t\tlocaltext(\"Frag Limit\"), (const char **)fraglimitoptions,\t\tfraglimit.value/10);y+=8;\n\ty+=8;\n\n\t//populate it with an appropriate default. its a shame it won't change with the deathmatch/coop options\n\tswitch(mgt)\n\t{\n\tcase MGT_QUAKE2:\n\t\tM_Menu_GameOptions_AddMap(\"maps/base1.bsp\", 0, 0, &mapopts, NULL);\n\t\tbreak;\n\tcase MGT_HEXEN2:\n\t\tM_Menu_GameOptions_AddMap(\"maps/demo1.bsp\", 0, 0, &mapopts, NULL);\n\t\tbreak;\n\tcase MGT_QUAKE1:\n\t\tM_Menu_GameOptions_AddMap(\"maps/start.bsp\", 0, 0, &mapopts, NULL);\n\t\tbreak;\n\tdefault:\n\t\tbreak;\n\t}\n\tCOM_EnumerateFiles(\"maps/*.bsp\",\tM_Menu_GameOptions_AddMap, &mapopts);\n\tCOM_EnumerateFiles(\"maps/*.bsp.gz\",\tM_Menu_GameOptions_AddMap, &mapopts);\n\tCOM_EnumerateFiles(\"maps/*.bsp.xz\",\tM_Menu_GameOptions_AddMap, &mapopts);\n\tCOM_EnumerateFiles(\"maps/*.map\",\tM_Menu_GameOptions_AddMap, &mapopts);\n\tCOM_EnumerateFiles(\"maps/*.map.gz\",\tM_Menu_GameOptions_AddMap, &mapopts);\n\tCOM_EnumerateFiles(\"maps/*.cm\",\t\tM_Menu_GameOptions_AddMap, &mapopts);\n\tCOM_EnumerateFiles(\"maps/*.hmp\",\tM_Menu_GameOptions_AddMap, &mapopts);\n\tinfo->mapname\t\t= MC_AddCombo\t(menu, 64, 160, y,\t\t\tlocaltext(\"Map\"), (const char **)mapopts.maps,\t\t0);y+=8;\n\ty += 16;\n\n\tmenu->cursoritem = (menuoption_t*)MC_AddWhiteText(menu, 54, 0, menu->selecteditem->common.posy, NULL, false);\n\n\n\tinfo->lowercolour = bottomcolor.value;\n\tinfo->topcolour = topcolor.value;\n}\n#endif\n\nvoid M_Menu_Teamplay_f (void)\n{\n#ifdef QWSKINS\n\tstatic const char *noskinsoptions[] =\n\t{\n\t\t\"Enabled\",\n\t\t\"Disabled\",\n\t\t\"No Download\",\n\t\tNULL\n\t};\n\tstatic const char *noskinsvalues[] =\n\t{\n\t\t\"0\",\n\t\t\"1\",\n\t\t\"2\",\n\t\tNULL\n\t};\n#endif\n\n\textern cvar_t cl_parseSay, cl_triggers, tp_forceTriggers, tp_loadlocs, cl_parseFunChars, cl_noblink, noskins;\n\tint y;\n\tmenubulk_t bulk[] =\n\t{\n\t\tMB_REDTEXT(\"Teamplay Options\", true),\n\t\tMB_TEXT(\"^Ue080^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue082\", true),\n#ifdef QWSKINS\n\t\tMB_COMBOCVAR(\"Skins\", noskins, noskinsoptions, noskinsvalues, \"Enable or disable player skin usage. No download will use skins but will not download them from the server.\"),\n#endif\n\t\tMB_EDITCVARTIP(\"Enemy Skin\", \"cl_enemyskin\", \"Override enemy skin with this.\"),\n\t\tMB_EDITCVARTIP(\"Team Skin\", \"cl_teamskin\", \"Override teammate skin with this.\"),\n\t\tMB_EDITCVARTIP(\"Fake Name\", \"cl_fakename\", \"Name that appears in teamplay messages\"),\n\t\tMB_CHECKBOXCVARTIP(\"Parse Fun Chars\", cl_parseFunChars, 0, \"Whether to parse fun characters\"),\n\t\tMB_CHECKBOXCVARTIP(\"Parse Macros\", cl_parseSay, 0, \"Whether to parse teamplay macros like %l etc.\"),\n\t\tMB_CHECKBOXCVARTIP(\"Load Locs\", tp_loadlocs, 0, \"Whether to load teamplay locations from .loc files\"),\n\t\tMB_CHECKBOXCVARTIP(\"No Blink\", cl_noblink, 0, \"No blinking characters\"),\n\t\tMB_EDITCVARTIP(\"Sound Trigger\", \"tp_soundtrigger\", \"Character that indicates the following text is a wav file.\\nExample:\\nsay_team ~location.wav$\\\\me: I'm at %l #a\"),\n\t\tMB_EDITCVARTIP(\"Weapon Order\", \"tp_weapon_order\",\"Weapon preference order:\\n8 = Lightning Gun\\n7 = Rocket Launcher\\n6 = Grenade Launcher\\n5 = Super Nailgun\\n4 = Nailgun\\n3 = Super Shotgun\\n2 = Shotgun\\n1 = Axe\"),\n\t\tMB_CHECKBOXCVARTIP(\"Teamplay Triggers\", cl_triggers, 0, \"Enable or disable teamplay triggers\"),\n\t\tMB_CHECKBOXCVARTIP(\"Force Triggers\", tp_forceTriggers, 0, \"Whether to force teamplay triggers in non-teamplay play like in a 1 on 1 situation\"),\n\t\tMB_SPACING(4),\n\t\tMB_CONSOLECMD(\"Location Names\", \"menu_teamplay_locations\\n\", \"Modify team play location settings.\"),\n\t\tMB_CONSOLECMD(\"Item Needs\", \"menu_teamplay_needs\\n\", \"Modify messages for item needs in team play macros.\"),\n\t\tMB_CONSOLECMD(\"Item Names\", \"menu_teamplay_items\\n\", \"Modify messages for items in team play macros.\"),\n\t\tMB_END()\n\t};\n\tstatic menuresel_t resel;\n\temenu_t *menu = M_Options_Title(&y, 0);\n\tMC_AddBulk(menu, &resel, bulk, 16, 200, y);\n}\n\nvoid M_Menu_Teamplay_Locations_f (void)\n{\n\tint y;\n\tmenubulk_t bulk[] =\n\t{\n\t\tMB_REDTEXT(\"Teamplay Location Names\", true),\n\t\tMB_TEXT(\"^Ue080^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue082\", true),\n\t\tMB_EDITCVARSLIM(\"Separator\", \"loc_name_separator\", \"Location name seperator character(s)\"),\n\t\tMB_SPACING(4),\n\t\tMB_EDITCVARSLIM(\"Super Shotgun\", \"loc_name_ssg\", \"Short name for Super Shotgun in teamplay location 'reports'\"),\n\t\tMB_EDITCVARSLIM(\"Nailgun\", \"loc_name_ng\", \"Short name for Nailgun in teamplay location 'reports'\"),\n\t\tMB_EDITCVARSLIM(\"Super Nailgun\", \"loc_name_sng\", \"Short name for Super Nailgun in teamplay location 'reports'\"),\n\t\tMB_EDITCVARSLIM(\"Grenade Launcher\", \"loc_name_gl\", \"Short name for Grenade Launcher in teamplay location 'reports'\"),\n\t\tMB_EDITCVARSLIM(\"Rocket Launcher\", \"loc_name_rl\", \"Short name for Rocket Launcher in teamplay location 'reports'\"),\n\t\tMB_EDITCVARSLIM(\"Lightning Gun\", \"loc_name_lg\", \"Short name for Lightning Gun in teamplay location 'reports'\"),\n\t\tMB_SPACING(4),\n\t\tMB_EDITCVARSLIM(\"Quad Damage\", \"loc_name_quad\", \"Short name for Quad Damage in teamplay location 'reports'\"),\n\t\tMB_EDITCVARSLIM(\"Pentagram\", \"loc_name_pent\", \"Short name for Pentagram of Protection in teamplay location 'reports'\"),\n\t\tMB_EDITCVARSLIM(\"Ring of Invis\", \"loc_name_ring\", \"Short name for Ring of Invisibility in teamplay location 'reports'\"),\n\t\tMB_EDITCVARSLIM(\"Suit\", \"loc_name_suit\", \"Short name for Environment Suit in teamplay location 'reports'\"),\n\t\tMB_SPACING(4),\n\t\tMB_EDITCVARSLIM(\"Green Armor\", \"loc_name_ga\", \"Short name for Green Armor in teamplay location 'reports'\"),\n\t\tMB_EDITCVARSLIM(\"Yellow Armor\", \"loc_name_ya\", \"Short name for Yellow Armor in teamplay location 'reports'\" ),\n\t\tMB_EDITCVARSLIM(\"Red Armor\", \"loc_name_ra\", \"Short name for Red Armor in teamplay location 'reports'\"),\n\t\t// TODO: we probably need an actual back button or some such\n\t\t//MB_SPACING(4),\n\t\t//MB_CONSOLECMD(\"\\x7f Teamplay\", \"menu_teamplay\\n\", \"Return to the teamplay menu.\"),\n\t\tMB_END()\n\t};\n\tstatic menuresel_t resel;\n\temenu_t *menu = M_Options_Title(&y, 0);\n\tMC_AddBulk(menu, &resel, bulk, 16, 200, y);\n}\n\nvoid M_Menu_Teamplay_Needs_f (void)\n{\n\tint y;\n\tmenubulk_t bulk[] =\n\t{\n\t\tMB_REDTEXT(\"Teamplay Needed Items\", true),\n\t\tMB_TEXT(\"^Ue080^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue082\", true),\n\t\tMB_EDITCVARSLIM(\"Shells\", \"tp_need_shells\", \"Short name for Shotgun Shells in teamplay 'need' reports\"),\n\t\tMB_EDITCVARSLIM(\"Nails\", \"tp_need_nails\", \"Short name for Nails in teamplay 'need' reports\"),\n\t\tMB_EDITCVARSLIM(\"Rockets\", \"tp_need_rockets\", \"Short name for Rockets/Grenades in teamplay 'need' reports\"),\n\t\tMB_EDITCVARSLIM(\"Cells\", \"tp_need_cells\", \"Short name for Power Cells in teamplay 'need' reports\"),\n\t\tMB_EDITCVARSLIM(\"Rocket Launcher\", \"tp_need_rl\", \"Short name for Rocket Launcher in teamplay 'need' reports\"),\n\t\tMB_SPACING(4),\n\t\tMB_EDITCVARSLIM(\"Green Armor\", \"tp_need_ga\", \"Short name for Green Armor in teamplay 'need' reports\"),\n\t\tMB_EDITCVARSLIM(\"Yellow Armor\", \"tp_need_ya\", \"Short name for Yellow Armor in teamplay 'need' reports\"),\n\t\tMB_EDITCVARSLIM(\"Red Armor\", \"tp_need_ra\", \"Short name for Red Armor in teamplay 'need' reports\"),\n\t\tMB_SPACING(4),\n\t\tMB_EDITCVARSLIM(\"Health\", \"tp_need_health\", \"Short name for Health in teamplay 'need' reports\"),\n\t\tMB_EDITCVARSLIM(\"Weapon\", \"tp_need_weapon\", \"Need weapon preference order:\\n8 = Lightning Gun\\n7 = Rocket Launcher\\n6 = Grenade Launcher\\n5 = Super Nailgun\\n4 = Nailgun\\n3 = Super Shotgun\\n2 = Shotgun\\n1 = Axe\"),\n\t\tMB_END()\n\t};\n\tstatic menuresel_t resel;\n\temenu_t *menu = M_Options_Title(&y, 0);\n\tMC_AddBulk(menu, &resel, bulk, 16, 200, y);\n}\n\nvoid M_Menu_Teamplay_Items_f (void)\n{\n\tint y;\n\tmenubulk_t bulk[] =\n\t{\n\t\tMB_REDTEXT(\"Teamplay Item Names\", true),\n\t\tMB_TEXT(\"^Ue080^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue082\", true),\n\t\tMB_CONSOLECMD(\"Armor\", \"menu_teamplay_armor\\n\", \"Modify team play macro armor names.\"),\n\t\tMB_CONSOLECMD(\"Weapon\", \"menu_teamplay_weapons\\n\", \"Modify team play macro weapon names.\"),\n\t\tMB_CONSOLECMD(\"Powerups\", \"menu_teamplay_powerups\\n\", \"Modify team play macro powerup names.\"),\n\t\tMB_CONSOLECMD(\"Ammo/Health\", \"menu_teamplay_ammo_health\\n\", \"Modify team play macro ammo and health names.\"),\n\t\tMB_CONSOLECMD(\"Team Fortress\", \"menu_teamplay_team_fortress\\n\", \"Modify Team Fortress exclusive team play macro names.\"),\n\t\tMB_CONSOLECMD(\"Status/Location/Misc\", \"menu_teamplay_status_location_misc\\n\", \"Modify status, location, and miscellaneous team play macro names.\"),\n\t\tMB_END()\n\t};\n\tstatic menuresel_t resel;\n\temenu_t *menu = M_Options_Title(&y, 0);\n\tMC_AddBulk(menu, &resel, bulk, 16, 224, y);\n}\n\nvoid M_Menu_Teamplay_Items_Armor_f (void)\n{\n\tint y;\n\tmenubulk_t bulk[] =\n\t{\n\t\tMB_REDTEXT(\"Teamplay Armor Names\", true),\n\t\tMB_TEXT(\"^Ue080^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue082\", true),\n\t\tMB_EDITCVARSLIM(\"Armor\", \"tp_name_armor\", \"Short name for Armor type\"),\n\t\tMB_EDITCVARSLIM(\"Green Type -\", \"tp_name_armortype_ga\", \"Short name for Green Armor type\"),\n\t\tMB_EDITCVARSLIM(\"Yellow Type -\", \"tp_name_armortype_ya\", \"Short name for Yellow Armor type\"),\n\t\tMB_EDITCVARSLIM(\"Red Type -\", \"tp_name_armortype_ra\", \"Short name for Red Armor type\"),\n\t\tMB_SPACING(4),\n\t\tMB_EDITCVARSLIM(\"Green Armor\", \"tp_name_ga\", \"Short name for Green Armor\"),\n\t\tMB_EDITCVARSLIM(\"Yellow Armor\", \"tp_name_ya\", \"Short name for Yellow Armor\"),\n\t\tMB_EDITCVARSLIM(\"Red Armor\", \"tp_name_ra\", \"Short name for Red Armor\"),\n\t\tMB_END()\n\t};\n\tstatic menuresel_t resel;\n\temenu_t *menu = M_Options_Title(&y, 0);\n\tMC_AddBulk(menu, &resel, bulk, 16, 200, y);\n}\n\nvoid M_Menu_Teamplay_Items_Weapons_f (void)\n{\n\tint y;\n\tmenubulk_t bulk[] =\n\t{\n\t\tMB_REDTEXT(\"Teamplay Weapon Names\", true),\n\t\tMB_TEXT(\"^Ue080^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue082\", true),\n\t\tMB_EDITCVARSLIM(\"Weapon\", \"tp_name_weapon\", \"Short name for Weapon\"),\n\t\tMB_SPACING(4),\n\t\tMB_EDITCVARSLIM(\"Axe\", \"tp_name_axe\", \"Short name for Weapon\"),\n\t\tMB_EDITCVARSLIM(\"Shotgun\", \"tp_name_sg\", \"Short name for Shotgun\"),\n\t\tMB_EDITCVARSLIM(\"Super Shotgun\", \"tp_name_ssg\", \"Short name for Super Shotgun\"),\n\t\tMB_EDITCVARSLIM(\"Nailgun\", \"tp_name_ng\", \"Short name for Nailgun\"),\n\t\tMB_EDITCVARSLIM(\"Super Nailgun\", \"tp_name_sng\", \"Short name for Super Nailgun\"),\n\t\tMB_EDITCVARSLIM(\"Grenade Launcher\", \"tp_name_gl\", \"Short name for Grenade Launcher\"),\n\t\tMB_EDITCVARSLIM(\"Rocket Launcher\", \"tp_name_rl\", \"Short name for Rocket Launcher\"),\n\t\tMB_EDITCVARSLIM(\"Lightning Gun\", \"tp_name_lg\", \"Short name for Lightning Gun\"),\n\t\tMB_END()\n\t};\n\tstatic menuresel_t resel;\n\temenu_t *menu = M_Options_Title(&y, 0);\n\tMC_AddBulk(menu, &resel, bulk, 16, 200, y);\n}\n\nvoid M_Menu_Teamplay_Items_Powerups_f (void)\n{\n\tint y;\n\tmenubulk_t bulk[] =\n\t{\n\t\tMB_REDTEXT(\"Teamplay Powerup Names\", true),\n\t\tMB_TEXT(\"^Ue080^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue082\", true),\n\t\tMB_EDITCVARSLIM(\"Quad Damage\", \"tp_name_quad\", \"Short name for Quad Damage\"),\n\t\tMB_EDITCVARSLIM(\"Pentagram\", \"tp_name_pent\", \"Short name for Pentgram of Protection\"),\n\t\tMB_EDITCVARSLIM(\"Ring of Invis\", \"tp_name_ring\", \"Short name for Ring Of Invisibilty\"),\n\t\tMB_EDITCVARSLIM(\"Suit\", \"tp_name_suit\", \"Short name for Environment Suit\"),\n\t\tMB_SPACING(4),\n\t\tMB_EDITCVARSLIM(\"Quaded\", \"tp_name_quaded\", \"Short name for reporting being 'Quaded'. Dying by another player who has Quad Damage\"),\n\t\tMB_EDITCVARSLIM(\"Pented\", \"tp_name_pented\", \"Short name for reporting being 'Pented'. Dying by another player who has the Pentagram\"),\n\t\tMB_EDITCVARSLIM(\"Eyes (Ringed)\", \"tp_name_eyes\", \"Short name for reporting being 'Ringed', Dying by another player who has Eyes (Invisibility)\"),\n\t\tMB_SPACING(4),\n\t\tMB_EDITCVARSLIM(\"Resistance Rune\", \"tp_name_rune_1\", \"Short name for Resistance Rune\"),\n\t\tMB_EDITCVARSLIM(\"Strength Rune\", \"tp_name_rune_2\", \"Short name for Strength Rune\"),\n\t\tMB_EDITCVARSLIM(\"Haste Rune\", \"tp_name_rune_3\", \"Short name for Haste Rune\"),\n\t\tMB_EDITCVARSLIM(\"Regen Rune\", \"tp_name_rune_4\", \"Short name for Regeneration Rune\"),\n\t\tMB_END()\n\t};\n\tstatic menuresel_t resel;\n\temenu_t *menu = M_Options_Title(&y, 0);\n\tMC_AddBulk(menu, &resel, bulk, 16, 200, y);\n}\n\nvoid M_Menu_Teamplay_Items_Ammo_Health_f (void)\n{\n\tint y;\n\tmenubulk_t bulk[] =\n\t{\n\t\tMB_REDTEXT(\"Teamplay Ammo/Health\", true),\n\t\tMB_TEXT(\"^Ue080^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue082\", true),\n\t\tMB_EDITCVARSLIM(\"Shells\", \"tp_name_shells\", \"Short name for Shells\"),\n\t\tMB_EDITCVARSLIM(\"Nails\", \"tp_name_nails\", \"Short name for Nails\"),\n\t\tMB_EDITCVARSLIM(\"Rockets\", \"tp_name_rockets\", \"Short name for Rockets\"),\n\t\tMB_EDITCVARSLIM(\"Cells\", \"tp_name_cells\", \"Short name for Cells\"),\n\t\tMB_SPACING(4),\n\t\tMB_EDITCVARSLIM(\"Backpack\", \"tp_name_backpack\", \"Short name for Backpack\"),\n\t\tMB_EDITCVARSLIM(\"Health\", \"tp_name_health\", \"Short name for Health\"),\n\t\tMB_EDITCVARSLIM(\"Mega Health\", \"tp_name_mh\", \"Short name for Mega Health\"),\n\t\tMB_END()\n\t};\n\tstatic menuresel_t resel;\n\temenu_t *menu = M_Options_Title(&y, 0);\n\tMC_AddBulk(menu, &resel, bulk, 16, 200, y);\n}\n\nvoid M_Menu_Teamplay_Items_Team_Fortress_f (void)\n{\n\tint y;\n\tmenubulk_t bulk[] =\n\t{\n\t\tMB_REDTEXT(\"Teamplay Team Fortress\", true),\n\t\tMB_TEXT(\"^Ue080^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue082\", true),\n\t\tMB_EDITCVARSLIM(\"Sentry Gun\", \"tp_name_sentry\", \"Short name for the Engineer's Sentry Gun\"),\n\t\tMB_EDITCVARSLIM(\"Dispenser\", \"tp_name_disp\", \"Short name for the Engineer's Ammo Dispenser\"),\n\t\tMB_EDITCVARSLIM(\"Flag\", \"tp_name_flag\", \"Short name for Flag\"),\n\t\tMB_END()\n\t};\n\tstatic menuresel_t resel;\n\temenu_t *menu = M_Options_Title(&y, 0);\n\tMC_AddBulk(menu, &resel, bulk, 16, 200, y);\n}\n\nvoid M_Menu_Teamplay_Items_Status_Location_Misc_f (void)\n{\n\tint y;\n\tmenubulk_t bulk[] =\n\t{\n\t\tMB_REDTEXT(\"Teamplay Misc\", true),\n\t\tMB_TEXT(\"^Ue080^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue082\", true),\n\t\tMB_EDITCVARSLIM(\"Enemy\", \"tp_name_enemy\", \"Short for Enemy in teamplay 'status' & 'location' reports\"),\n\t\tMB_EDITCVARSLIM(\"Teammate\", \"tp_name_teammate\", \"Short for Enemy in teamplay 'status' & 'location' reports\"),\n\t\tMB_SPACING(4),\n\t\tMB_EDITCVARSLIM(\"At (Location)\", \"tp_name_at\", \"Short for @ (Location) in teamplay 'status' & 'location' reports\"),\n\t\tMB_EDITCVARSLIM(\"None\", \"tp_name_none\", \"Short for None in teamplay 'status' & 'location' reports\"),\n\t\tMB_EDITCVARSLIM(\"Nothing\", \"tp_name_nothing\", \"Short for Nothing in teamplay 'status' & 'location' reports\"),\n\t\tMB_EDITCVARSLIM(\"Separator\", \"tp_name_separator\", \"Seperator character(s) in teamplay 'status' & 'location' reports\"),\n\t\tMB_EDITCVARSLIM(\"Some place\", \"tp_name_someplace\", \"Short for Someplace in teamplay 'status' & 'location' reports\"),\n\t\tMB_SPACING(4),\n\t\tMB_EDITCVARSLIM(\"Red Status\", \"tp_name_status_red\", \"Macro for Status Red in teamplay 'status' & 'location' reports\"),\n\t\tMB_EDITCVARSLIM(\"Green Status\", \"tp_name_status_green\", \"Macro for Status Green in teamplay 'status' & 'location' reports\"),\n\t\tMB_EDITCVARSLIM(\"Blue Status\", \"tp_name_status_blue\", \"Macro for Status Blue in teamplay 'status' & 'location' reports\"),\n\t\tMB_EDITCVARSLIM(\"Yellow Status\", \"tp_name_status_yellow\", \"Macro for Status Yellow in teamplay 'status' & 'location' reports\"),\n\t\tMB_END()\n\t};\n\tstatic menuresel_t resel;\n\temenu_t *menu = M_Options_Title(&y, 0);\n\tMC_AddBulk(menu, &resel, bulk, 16, 200, y);\n}\n\nvoid M_Menu_Network_f (void)\n{\n#if MAX_SPLITS > 1\n\tstatic const char *splitopts[] = {\n\t\t\"Disabled\",\n\t\t\"2 Screens\",\n#if MAX_SPLITS > 2\n\t\t\"3 Screens\",\n#endif\n#if MAX_SPLITS > 3\n\t\t\"4 Screens\",\n#endif\n\t\tNULL\n\t};\n\tstatic const char *splitvalues[] =\n\t{\n\t\t\"0\",\n\t\t\"1\",\n#if MAX_SPLITS > 2\n\t\t\"2\",\n#endif\n#if MAX_SPLITS > 3\n\t\t\"3\",\n#endif\n\t\tNULL\n\t};\n#endif\n\tstatic const char *smoothingopts[] = {\n\t\t\"Lower Latency\",\n\t\t\"Smoother\",\n\t\t\"Smooth Demos Only\",\n\t\tNULL\n\t};\n#ifdef HAVE_DTLS\n\tstatic const char *dtlsopts[] = {\n\t\t\"Disabled\",\n\t\t\"Accept\",\n\t\t\"Request\",\n\t\t\"Require\",\n\t\tNULL\n\t};\n#endif\n\tstatic const char *smoothingvalues[] = {\"0\", \"1\", \"2\", NULL};\n\textern cvar_t cl_download_csprogs, cl_download_redirection, requiredownloads, cl_solid_players;\n\textern cvar_t cl_predict_players, cl_lerp_smooth, cl_predict_extrapolate;\n\tstatic menuresel_t resel;\n\tint y;\n\tmenubulk_t bulk[] =\n\t{\n\t\tMB_REDTEXT(\"Network Settings\", true),\n\t\tMB_TEXT(\"^Ue080^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue082\", true),\n\t\tMB_EDITCVARSLIM(\"Network FPS\", \"cl_netfps\", \"Sets ammount of FPS used to communicate with server (sent and received)\"),\n\t\tMB_EDITCVARSLIM(\"Rate\", \"rate\", \"Maximum bytes per second that the server should send to the client\"),\n\t\tMB_EDITCVARSLIM(\"Download Rate\", \"drate\", \"Maximum bytes per second that the server should send maps and demos to the client\"),\n#ifdef HAVE_DTLS\n\t\tMB_COMBOCVAR(\"DTLS Encryption\", net_enable_dtls, dtlsopts, NULL, \"Use this to avoid snooping. Certificates will be pinned.\"),\n#endif\n\t\tMB_SPACING(4),\n\t\tMB_CHECKBOXCVARTIP(\"Wait for Downloads\", requiredownloads, 0, \"Ignore downloaded content sent to the client and connect immediately\"),\n\t\tMB_CHECKBOXCVARTIP(\"Redirect Download\", cl_download_redirection, 0, \"Whether the client will ignore download redirection from servers\"),\n\t\tMB_CHECKBOXCVARTIP(\"Download CSQC\", cl_download_csprogs, 0, \"Whether to allow the client to download CSQC (client-side QuakeC) progs from servers\"),\n\t\tMB_SPACING(4),\n\t\tMB_COMBOCVAR(\"Network Smoothing\", cl_lerp_smooth, smoothingopts, smoothingvalues, \"Smoother gameplay comes at the cost of higher latency. Which do you favour?\"),\n\t\tMB_CHECKBOXCVARTIP(\"Extrapolate Prediction\", cl_predict_extrapolate, 0, \"Extrapolate local player movement beyond the frames already sent to the server\"),\n\t\tMB_CHECKBOXCVARTIP(\"Predict Other Players\", cl_predict_players, 0, \"Toggle player prediction\"),\n\t\tMB_CHECKBOXCVARTIP(\"Solid Players\", cl_solid_players, 0, \"When running/clipping into other players, ON make it appear they are solid, OFF will make it appear like running into a marshmellon.\"),\n#if MAX_SPLITS > 1\n\t\tMB_COMBOCVAR(\"Split-screen\", cl_splitscreen, splitopts, splitvalues, \"Enables split screen with a number of clients. This feature requires server support.\"),\n#endif\n\t\tMB_END()\n\t};\n\temenu_t *menu = M_Options_Title(&y, 0);\n\tMC_AddBulk(menu, &resel, bulk, 16, 200, y);\n}\n#endif\n"
  },
  {
    "path": "engine/client/m_native.c",
    "content": "#include \"quakedef.h\"\r\n#ifdef MENU_NATIVECODE\r\nstatic dllhandle_t *libmenu;\r\nmenu_export_t *mn_entry;\r\n\r\nextern unsigned int r2d_be_flags;\r\n#include \"pr_common.h\"\r\n#include \"shader.h\"\r\n#include \"cl_master.h\"\r\n\r\n//static void MN_Menu_VideoReset(struct menu_s *m);\r\n//static void MN_Menu_Released(struct menu_s *m);\r\nstatic qboolean MN_Menu_KeyEvent(struct menu_s *m, qboolean isdown, unsigned int devid, int key, int unicode)\r\n{\r\n\tif (mn_entry && mn_entry->InputEvent)\r\n\t{\r\n\t\tvoid *nctx = (m->ctx==libmenu)?NULL:m->ctx;\r\n\t\tstruct menu_inputevent_args_s ev = {isdown?MIE_KEYDOWN:MIE_KEYUP, devid};\r\n\t\tev.key.scancode = key;\r\n\t\tev.key.charcode = unicode;\r\n\t\treturn mn_entry->InputEvent(nctx, ev);\r\n\t}\r\n\treturn false;\r\n}\r\nstatic qboolean MN_Menu_MouseMove(struct menu_s *m, qboolean abs, unsigned int devid, float x, float y)\r\n{\r\n\tif (mn_entry && mn_entry->InputEvent)\r\n\t{\r\n\t\tvoid *nctx = (m->ctx==libmenu)?NULL:m->ctx;\r\n\t\tstruct menu_inputevent_args_s ev = {abs?MIE_MOUSEABS:MIE_MOUSEDELTA, devid};\r\n\t\tev.mouse.delta[0] = x;\r\n\t\tev.mouse.delta[1] = y;\r\n\t\tev.mouse.screen[0] = mousecursor_x;\r\n\t\tev.mouse.screen[1] = mousecursor_y;\r\n\t\treturn mn_entry->InputEvent(nctx, ev);\r\n\t}\r\n\treturn false;\r\n}\r\nstatic qboolean MN_Menu_JoyAxis(struct menu_s *m, unsigned int devid, int axis, float val)\r\n{\r\n\tif (mn_entry && mn_entry->InputEvent)\r\n\t{\r\n\t\tvoid *nctx = (m->ctx==libmenu)?NULL:m->ctx;\r\n\t\tstruct menu_inputevent_args_s ev = {MIE_JOYAXIS, devid};\r\n\t\tev.axis.axis = axis;\r\n\t\tev.axis.val = val;\r\n\t\treturn mn_entry->InputEvent(nctx, ev);\r\n\t}\r\n\treturn false;\r\n}\r\nstatic void MN_Menu_DrawMenu(struct menu_s *m)\r\n{\r\n\tvoid *nctx = (m->ctx==libmenu)?NULL:m->ctx;\r\n\tif (mn_entry && mn_entry->Draw)\r\n\t\tmn_entry->Draw(nctx, host_frametime);\r\n\telse\r\n\t\tMenu_Unlink(m);\r\n}\r\n\r\nstatic int MN_CheckExtension(const char *extname)\r\n{\r\n\tunsigned int i;\r\n\tfor (i = 0; i < QSG_Extensions_count; i++)\r\n\t{\r\n\t\tif (!strcmp(QSG_Extensions[i].name, extname))\r\n\t\t\treturn true;\r\n\t}\r\n\treturn false;\r\n}\r\nstatic void MN_LocalCmd(const char *text)\r\n{\r\n\tCbuf_AddText(text, RESTRICT_LOCAL);\t//menus are implicitly trusted. latching and other stuff would be a nightmare otherwise.\r\n}\r\nstatic const char *MN_Cvar_String(const char *cvarname, qboolean effective)\r\n{\r\n\tcvar_t *cv = Cvar_FindVar(cvarname);\r\n\tif (cv)\r\n\t{\t//some cvars don't change instantly, giving them (temporary) effective values that are different from their intended values.\r\n\t\tif (cv->latched_string && !effective)\r\n\t\t\treturn cv->latched_string;\r\n\t\treturn cv->string;\r\n\t}\r\n\telse\r\n\t\treturn NULL;\r\n}\r\nstatic const char *MN_Cvar_GetDefault(const char *cvarname)\r\n{\r\n\tcvar_t *cv = Cvar_FindVar(cvarname);\r\n\tif (cv)\r\n\t\treturn cv->defaultstr?cv->defaultstr:\"\";\r\n\telse\r\n\t\treturn NULL;\r\n}\r\nstatic void MN_RegisterCvar(const char *cvarname, const char *defaulttext, unsigned int flags, const char *description)\r\n{\r\n\tCvar_Get2(cvarname, defaulttext, flags, description, NULL);\r\n}\r\nstatic void MN_RegisterCommand(const char *commandname, const char *description)\r\n{\r\n\tif (!Cmd_Exists(commandname)) {\r\n\t\tCmd_AddCommandD(commandname, NULL, description);\r\n\t}\r\n}\r\nstatic int MN_GetServerState(void)\r\n{\r\n\tif (!sv.active)\r\n\t\treturn 0;\r\n\tif (svs.allocated_client_slots <= 1)\r\n\t\treturn 1;\r\n\treturn 2;\r\n}\r\nstatic int MN_GetClientState(char const ** disconnect_reason)\r\n{\r\n\textern cvar_t cl_disconnectreason;\r\n\t*disconnect_reason = NULL;\r\n\tif (cls.state >= ca_active)\r\n\t\treturn 2;\r\n\tif (cls.state != ca_disconnected)\r\n\t\treturn 1;\r\n\t*disconnect_reason = (const char*)cl_disconnectreason.string;\r\n\treturn 0;\r\n}\r\nstatic void MN_fclose(vfsfile_t *f)\r\n{\r\n\tVFS_CLOSE(f);\r\n}\r\nstatic shader_t *MN_CachePic(const char *picname)\r\n{\r\n\treturn R2D_SafeCachePic(picname);\r\n}\r\nstatic qboolean MN_DrawGetImageSize(struct shader_s *pic, int *w, int *h)\r\n{\r\n\treturn R_GetShaderSizes(pic, w, h, true)>0;\r\n}\r\nstatic void MN_DrawQuad(const vec2_t position[4], const vec2_t texcoords[4], shader_t *pic, const vec4_t rgba, unsigned int be_flags)\r\n{\r\n\textern shader_t *shader_draw_fill, *shader_draw_fill_trans;\r\n\tr2d_be_flags = be_flags;\r\n\tif (!pic)\r\n\t\tpic = rgba[3]==1?shader_draw_fill:shader_draw_fill_trans;\r\n\tR2D_ImageColours(rgba[0], rgba[1], rgba[2], rgba[3]);\r\n\tR2D_Image2dQuad(position, texcoords, NULL, pic);\r\n\tr2d_be_flags = 0;\r\n}\r\nstatic float MN_DrawString(const vec2_t position, const char *text, struct font_s *font, float height, const vec4_t rgba, unsigned int be_flags)\r\n{\r\n\tfloat px, py, ix;\r\n\tunsigned int codeflags, codepoint;\r\n\tconchar_t buffer[2048], *str = buffer;\r\n\tif (!font)\r\n\t\tfont = font_default;\r\n\r\n\tCOM_ParseFunString(CON_WHITEMASK, text, buffer, sizeof(buffer), false);\r\n\r\n\tR2D_ImageColours(rgba[0], rgba[1], rgba[2], rgba[3]);\r\n\tFont_BeginScaledString(font, position[0], position[1], height, height, &px, &py);\r\n\tix=px;\r\n\twhile(*str)\r\n\t{\r\n\t\tstr = Font_Decode(str, &codeflags, &codepoint);\r\n\t\tpx = Font_DrawScaleChar(px, py, codeflags, codepoint);\r\n\t}\r\n\tFont_EndString(font);\r\n\treturn ((px-ix)*(float)vid.width)/(float)vid.rotpixelwidth;\r\n}\r\nstatic float MN_StringWidth(const char *text, struct font_s *font, float height)\r\n{\r\n\tfloat px, py;\r\n\tconchar_t buffer[2048], *end;\r\n\tif (!font)\r\n\t\tfont = font_default;\r\n\r\n\tend = COM_ParseFunString(CON_WHITEMASK, text, buffer, sizeof(buffer), false);\r\n\r\n\tFont_BeginScaledString(font, 0, 0, height, height, &px, &py);\r\n\tpx = Font_LineScaleWidth(buffer, end);\r\n\tFont_EndString(font);\r\n\treturn (px * (float)vid.width) / (float)vid.rotpixelwidth;\r\n}\r\nstatic void MN_DrawSetClipArea(float x, float y, float width, float height)\r\n{\r\n\tsrect_t srect;\r\n\tif (R2D_Flush)\r\n\t\tR2D_Flush();\r\n\r\n\tsrect.x = x / (float)vid.fbvwidth;\r\n\tsrect.y = y / (float)vid.fbvheight;\r\n\tsrect.width = width / (float)vid.fbvwidth;\r\n\tsrect.height = height / (float)vid.fbvheight;\r\n\tsrect.dmin = -99999;\r\n\tsrect.dmax = 99999;\r\n\tsrect.y = (1-srect.y) - srect.height;\r\n\tBE_Scissor(&srect);\r\n}\r\nstatic void MN_DrawResetClipArea(void)\r\n{\r\n\tif (R2D_Flush)\r\n\t\tR2D_Flush();\r\n\tBE_Scissor(NULL);\r\n}\r\nstatic void MN_PushMenu(void *ctx)\r\n{\r\n\tmenu_t *m;\r\n\tif (!ctx)\r\n\t\tctx = libmenu;\t//to match kill\r\n\tm = Menu_FindContext(ctx);\r\n\tif (!m)\r\n\t{\t//not created yet.\r\n\t\tm = Z_Malloc(sizeof(*m));\r\n\t\tm->ctx = ctx;\r\n\r\n//\t\tm->videoreset\t= MN_Menu_VideoReset;\r\n//\t\tm->release\t\t= MN_Menu_Released;\r\n\t\tm->keyevent\t\t= MN_Menu_KeyEvent;\r\n\t\tm->mousemove\t= MN_Menu_MouseMove;\r\n\t\tm->joyaxis\t\t= MN_Menu_JoyAxis;\r\n\t\tm->drawmenu\t\t= MN_Menu_DrawMenu;\r\n\t}\r\n\tm->cursor = &key_customcursor[kc_nativemenu];\r\n\tMenu_Push(m, false);\r\n\r\n\tif (ctx == libmenu)\r\n\t\tctx = NULL;\r\n\tif (m->cursor)\r\n\t{\t//we're activating the mouse cursor now... make sure the position is actually current.\r\n\t\t//FIXME: we should probably get the input code to do this for us when switching cursor modes.\r\n\t\tstruct menu_inputevent_args_s ev = {MIE_MOUSEABS, -1};\r\n\t\tev.mouse.screen[0] = mousecursor_x;\r\n\t\tev.mouse.screen[1] = mousecursor_y;\r\n\t\tmn_entry->InputEvent(ctx, ev);\r\n\t}\r\n}\r\nstatic qboolean MN_IsMenuPushed(void *ctx)\r\n{\r\n\tmenu_t *m;\r\n\tif (!ctx)\r\n\t\tctx = libmenu;\t//to match kill\r\n\tm = Menu_FindContext(ctx);\r\n\treturn !!m;\r\n}\r\nstatic void MN_KillMenu(void *ctx)\r\n{\r\n\tmenu_t *m;\r\n\tif (!ctx)\r\n\t\tctx = libmenu;\t//don't allow null contexts, because that screws up any other menus.\r\n\tm = Menu_FindContext(ctx);\r\n\tif (m)\r\n\t\tMenu_Unlink(m);\r\n}\r\nstatic int MN_SetMouseTarget(const char *cursorname, float hot_x, float hot_y, float scale)\r\n{\r\n\tif (cursorname)\r\n\t{\r\n\t\tstruct key_cursor_s *m = &key_customcursor[kc_nativemenu];\r\n\t\tif (scale <= 0)\r\n\t\t\tscale = 1;\r\n\t\tif (!strcmp(m->name, cursorname) || m->hotspot[0] != hot_x || m->hotspot[1] != hot_y || m->scale != scale)\r\n\t\t{\r\n\t\t\tQ_strncpyz(m->name, cursorname, sizeof(m->name));\r\n\t\t\tm->hotspot[0] = hot_x;\r\n\t\t\tm->hotspot[1] = hot_y;\r\n\t\t\tm->scale = scale;\r\n\t\t\tm->dirty = true;\r\n\t\t}\r\n\t}\r\n\treturn true;\r\n}\r\n\r\nstatic model_t *MN_CacheModel(const char *name)\r\n{\r\n\treturn Mod_ForName(name, MLV_SILENT);\r\n}\r\nstatic qboolean MN_GetModelSize(model_t *model, vec3_t out_mins, vec3_t out_maxs)\r\n{\r\n\tif (model)\r\n\t{\r\n\t\twhile(model->loadstate == MLS_LOADING)\r\n\t\t\tCOM_WorkerPartialSync(model, &model->loadstate, MLS_LOADING);\r\n\r\n\t\tVectorCopy(model->mins, out_mins);\r\n\t\tVectorCopy(model->maxs, out_maxs);\r\n\t\treturn model->loadstate == MLS_LOADED;\r\n\t}\r\n\tVectorClear(out_mins);\r\n\tVectorClear(out_maxs);\r\n\treturn false;\r\n}\r\nstatic void MN_RenderScene(menuscene_t *scene)\r\n{\r\n\tint i;\r\n\tentity_t ent;\r\n\tmenuentity_t *e;\r\n\tif (R2D_Flush)\r\n\t\tR2D_Flush();\r\n\r\n\tCL_ClearEntityLists();\r\n\tmemset(&ent, 0, sizeof(ent));\r\n\tfor (i = 0; i < scene->numentities; i++)\r\n\t{\r\n\t\te = &scene->entlist[i];\r\n\t\tent.keynum = i;\r\n\t\tent.model = scene->entlist[i].model;\r\n\t\tVectorCopy(e->matrix[0], ent.axis[0]); ent.origin[0] = e->matrix[0][3];\r\n\t\tVectorCopy(e->matrix[1], ent.axis[1]); ent.origin[1] = e->matrix[1][3];\r\n\t\tVectorCopy(e->matrix[2], ent.axis[2]); ent.origin[2] = e->matrix[2][3];\r\n\r\n\t\tent.scale = 1;\r\n\t\tent.framestate.g[FS_REG].frame[0] = e->frame[0];\r\n\t\tent.framestate.g[FS_REG].frame[1] = e->frame[1];\r\n\t\tent.framestate.g[FS_REG].lerpweight[1] = e->frameweight[0];\r\n\t\tent.framestate.g[FS_REG].lerpweight[0] = e->frameweight[1];\r\n\t\tent.framestate.g[FS_REG].frametime[0] = e->frametime[0];\r\n\t\tent.framestate.g[FS_REG].frametime[1] = e->frametime[1];\r\n\r\n\t\tent.playerindex = -1;\r\n\t\tent.topcolour = TOP_DEFAULT;\r\n\t\tent.bottomcolour = BOTTOM_DEFAULT;\r\n\t\tVector4Set(ent.shaderRGBAf, 1, 1, 1, 1);\r\n\t\tVectorSet(ent.glowmod, 1, 1, 1);\r\n#ifdef HEXEN2\r\n\t\tent.drawflags = SCALE_ORIGIN_ORIGIN;\r\n\t\tent.abslight = 0;\r\n#endif\r\n\t\tent.skinnum = 0;\r\n\t\tent.fatness = 0;\r\n\t\tent.forcedshader = NULL;\r\n\t\tent.customskin = 0;\r\n\r\n\t\tV_AddAxisEntity(&ent);\r\n\t}\r\n\r\n\tVectorCopy(scene->viewmatrix[0], r_refdef.viewaxis[0]); r_refdef.vieworg[0] = scene->viewmatrix[0][3];\r\n\tVectorCopy(scene->viewmatrix[1], r_refdef.viewaxis[1]); r_refdef.vieworg[1] = scene->viewmatrix[1][3];\r\n\tVectorCopy(scene->viewmatrix[2], r_refdef.viewaxis[2]); r_refdef.vieworg[2] = scene->viewmatrix[2][3];\r\n\t\r\n\tr_refdef.viewangles[0] = -(atan2(r_refdef.viewaxis[0][2], sqrt(r_refdef.viewaxis[0][1]*r_refdef.viewaxis[0][1]+r_refdef.viewaxis[0][0]*r_refdef.viewaxis[0][0])) * 180 / M_PI);\r\n\tr_refdef.viewangles[1] = (atan2(r_refdef.viewaxis[0][1], r_refdef.viewaxis[0][0]) * 180 / M_PI);\r\n\tr_refdef.viewangles[2] = 0;\r\n\r\n\tr_refdef.flags = 0;\r\n\tif (scene->worldmodel && scene->worldmodel == cl.worldmodel)\r\n\t\tr_refdef.flags &= ~RDF_NOWORLDMODEL;\r\n\telse\r\n\t\tr_refdef.flags |= RDF_NOWORLDMODEL;\r\n\tr_refdef.fovv_x = r_refdef.fov_x = scene->fov[0];\r\n\tr_refdef.fovv_y = r_refdef.fov_y = scene->fov[1];\r\n\tr_refdef.vrect.x = scene->pos[0];\r\n\tr_refdef.vrect.y = scene->pos[1];\r\n\tr_refdef.vrect.width = scene->size[0];\r\n\tr_refdef.vrect.height = scene->size[1];\r\n\tr_refdef.time = scene->time;\r\n\tr_refdef.useperspective = true;\r\n\tr_refdef.mindist = bound(0.1, gl_mindist.value, 4);\r\n\tr_refdef.maxdist = gl_maxdist.value;\r\n\tr_refdef.playerview = &cl.playerview[0];\r\n\r\n\tmemset(&r_refdef.globalfog, 0, sizeof(r_refdef.globalfog));\r\n\tr_refdef.areabitsknown = false;\r\n\r\n\tR_RenderView();\r\n\tr_refdef.playerview = NULL;\r\n\tr_refdef.time = 0;\r\n}\r\n\r\nvoid MN_Shutdown(void)\r\n{\r\n\tif (mn_entry)\r\n\t{\r\n\t\tmn_entry->Shutdown(MI_INIT);\r\n\t\tmn_entry = NULL;\r\n\t}\r\n\tif (libmenu)\r\n\t{\r\n\t\tSys_CloseLibrary(libmenu);\r\n\t\tlibmenu = NULL;\r\n\t}\r\n}\r\nqboolean MN_Init(void)\r\n{\r\n\tmenu_export_t *(QDECL *pGetMenuAPI) ( menu_import_t *import );\r\n\tstatic menu_import_t imports =\r\n\t{\r\n\t\tNATIVEMENU_API_VERSION_MAX,\r\n\t\tNULL,\r\n\r\n\t\tMN_CheckExtension,\r\n\t\tHost_Error,\r\n\t\tCon_Printf,\r\n\t\tCon_DPrintf,\r\n\t\tMN_LocalCmd,\r\n\t\tCvar_VariableValue,\r\n\t\tMN_Cvar_String,\r\n\t\tMN_Cvar_GetDefault,\r\n\t\tCvar_SetNamed,\r\n\t\tMN_RegisterCvar,\r\n\t\tMN_RegisterCommand,\r\n\r\n\t\tCOM_ParseType,\r\n\r\n\t\tMN_GetServerState,\r\n\t\tMN_GetClientState,\r\n\t\tS_LocalSound2,\r\n\t\r\n\t\t// file input / search crap\r\n\t\tFS_OpenVFS,\r\n\t\tMN_fclose,\r\n\t\tVFS_GETS,\r\n\t\tVFS_PRINTF,\r\n\t\tCOM_EnumerateFiles,\r\n\t\tFS_SystemPath,\r\n\r\n\t\t// Drawing stuff\r\n\t\tMN_DrawSetClipArea,\r\n\t\tMN_DrawResetClipArea,\r\n\r\n\t\t//pics\r\n\t\tMN_CachePic,\r\n\t\tMN_DrawGetImageSize,\r\n\t\tMN_DrawQuad,\r\n\r\n\t\t//strings\r\n\t\tMN_DrawString,\r\n\t\tMN_StringWidth,\r\n\t\tFont_LoadFont,\r\n\t\tFont_Free,\r\n\r\n\t\t//3d stuff\r\n\t\tMN_CacheModel,\r\n\t\tMN_GetModelSize,\r\n\t\tMN_RenderScene,\r\n\r\n\t\t// Menu specific stuff\r\n\t\tMN_PushMenu,\r\n\t\tMN_IsMenuPushed,\r\n\t\tMN_KillMenu,\r\n\t\tMN_SetMouseTarget,\r\n\t\tKey_KeynumToString,\r\n\t\tKey_StringToKeynum,\r\n\t\tM_FindKeysForBind,\r\n\r\n\t\t// Server browser stuff\r\n\t\tMaster_KeyForName,\r\n\t\tMaster_SortedServer,\r\n\t\tMaster_ReadKeyString,\r\n\t\tMaster_ReadKeyFloat,\r\n\r\n\t\tMaster_ClearMasks,\r\n\t\tMaster_SetMaskString,\r\n\t\tMaster_SetMaskInteger,\r\n\t\tMaster_SetSortField,\r\n\t\tMaster_SortServers,\r\n\t\tMasterInfo_Refresh,\r\n\t\tCL_QueryServers,\r\n\t};\r\n\tdllfunction_t funcs[] =\r\n\t{\r\n\t\t{(void*)&pGetMenuAPI, \"GetMenuAPI\"},\r\n\t\t{NULL}\r\n\t};\r\n\tvoid *iterator = NULL;\r\n\tchar syspath[MAX_OSPATH];\r\n\tchar gamepath[MAX_QPATH];\r\n\r\n\twhile(COM_IteratePaths(&iterator, syspath, sizeof(syspath), gamepath, sizeof(gamepath)))\r\n\t{\r\n\t\tif (com_gamedirnativecode.ival)\r\n\t\t\tlibmenu = Sys_LoadLibrary(va(\"%smenu_\"ARCH_CPU_POSTFIX ARCH_DL_POSTFIX, syspath), funcs);\r\n\t\tif (libmenu)\r\n\t\t\tbreak;\r\n\r\n\t\tif (host_parms.binarydir && !strchr(gamepath, '/') && !strchr(gamepath, '\\\\'))\r\n\t\t\tlibmenu = Sys_LoadLibrary(va(\"%smenu_\"ARCH_CPU_POSTFIX ARCH_DL_POSTFIX, host_parms.binarydir), funcs);\r\n\t\tif (libmenu)\r\n\t\t\tbreak;\r\n\r\n\t\t//some build systems don't really know the cpu type.\r\n\t\tif (host_parms.binarydir && !strchr(gamepath, '/') && !strchr(gamepath, '\\\\'))\r\n\t\t\tlibmenu = Sys_LoadLibrary(va(\"%smenu_%s\" ARCH_DL_POSTFIX, host_parms.binarydir, gamepath), funcs);\r\n\t\tif (libmenu)\r\n\t\t\tbreak;\r\n\t}\r\n\r\n\tif (libmenu)\r\n\t{\r\n\t\timports.engine_version = version_string();\r\n\r\n\t\tmn_entry = pGetMenuAPI (&imports); \r\n\t\tif (mn_entry && mn_entry->api_version >= NATIVEMENU_API_VERSION_MIN && mn_entry->api_version <= NATIVEMENU_API_VERSION_MAX)\r\n\t\t{\r\n\t\t\tmn_entry->Init(0, vid.width, vid.height, vid.pixelwidth, vid.pixelheight);\r\n\t\t\treturn true;\r\n\t\t}\r\n\t\telse\r\n\t\t\tmn_entry = NULL;\r\n\t\tMN_Shutdown();\r\n\t\tSys_CloseLibrary(libmenu);\r\n\t\tlibmenu = NULL;\r\n\t}\r\n\r\n\treturn false;\r\n}\r\n#endif\r\n"
  },
  {
    "path": "engine/client/m_options.c",
    "content": "//read menu.h\n\n#include \"quakedef.h\"\n#include \"winquake.h\"\n#include \"fs.h\"\n\n\nstatic const char *res4x3[] =\n{\n\t\"640x480\",\n\t\"800x600\",\n\t\"960x720\",\n\t\"1024x768\",\n\t\"1152x864\",\n\t\"1280x960\",\n\t\"1440x1080\",\n\t\"1600x1200\",\n//\t\"1792x1344\",\n//\t\"1856x1392\",\n\t\"1920x1440\",\n\t\"2048x1536\",\n\tNULL\n};\nstatic const char *res5x4[] =\n{\n\t\"1280x1024\",\n\t\"1800x1440\",\n\t\"2560x2048\",\n\tNULL\n};\nstatic const char *res16x9[] =\n{\n\t\"856x480\",\n\t\"1024x576\",\n\t\"1280x720\",\n\t\"1366x768\",\n\t\"1600x900\",\n\t\"1920x1080\",\n\t\"2048x1152\",\n\t\"2560x1440\",\n\t\"3840x2160\",\n\t\"4096x2304\",\n\tNULL\n};\nstatic const char *res16x10[] =\n{\n\t\"1024x640\",\n\t\"1152x720\",\n\t\"1280x800\",\n\t\"1440x900\",\n\t\"1680x1050\",\n\t\"1920x1200\",\n\t\"2304x1440\",\n\t\"2560x1600\",\n\tNULL\n};\n#define ASPECT_RATIOS 4\nstatic const char **resaspects[ASPECT_RATIOS] =\n{\n\tres4x3,\n\tres5x4,\n\tres16x9,\n\tres16x10\n};\n#define ASPECT_LIST \"4:3\", \"5:4\", \"16:9\", \"16:10\",\nenum\n{\n\tASPECT3D_DESKTOP = ASPECT_RATIOS,\n\tASPECT3D_CUSTOM,\n};\nenum\n{\n\tASPECT2D_AUTO = ASPECT_RATIOS,\n\tASPECT2D_HEIGHT,\n\tASPECT2D_SCALED,\n\tASPECT2D_CUSTOM,\n};\n\n\ntypedef struct {\n\tunsigned int w, h;\n} ftevidmode_t;\nstatic ftevidmode_t *vidmodes;\nstatic size_t nummodes;\nvoid VidMode_Clear(void)\n{\n\tBZ_Free(vidmodes);\n\tvidmodes = NULL;\n\tnummodes = 0;\n}\nvoid VidMode_Add(int w, int h)\n{\n\textern cvar_t vid_minsize;\n\tsize_t m;\n\n\tif (w < vid_minsize.vec4[0] || h < vid_minsize.vec4[1])\n\t\treturn;\t//would be too small. ignore it.\n\tfor (m = 0; m < nummodes; m++)\n\t{\n\t\tif (vidmodes[m].w == w && vidmodes[m].h == h)\n\t\t\treturn;\n\t}\n\tZ_ReallocElements((void**)&vidmodes, &nummodes, nummodes+1, sizeof(*vidmodes));\n\tvidmodes[m].w = w;\n\tvidmodes[m].h = h;\n}\nstatic int QDECL VidMode_Sort(const void *v1, const void *v2)\n{\n\tconst ftevidmode_t *m1 = v1, *m2 = v2;\n\tint n1 = m1->w, n2=m2->w;\t//sort by width\n\tif (n1 == n2)\n\t\tn1 = m1->h, n2=m2->h;\t//then by height\n\tif (n1 == n2)\n\t\treturn 0;\t\t\t\t//then give up and consider equal... which shouldn't happen.\n\tif (n1 > n2)\n\t\treturn 1;\n\treturn -1;\n}\nqboolean M_Vid_GetMode(qboolean forfullscreen, int num, int *w, int *h)\n{\n\tint a;\n\textern cvar_t vid_devicename;\n\tif (!nummodes)\n\t{\n\t\tVidMode_Clear();\n\n\t\tif (rf->VID_EnumerateVideoModes)\n\t\t\trf->VID_EnumerateVideoModes(\"\", vid_devicename.string, VidMode_Add);\n\n\t\tif (!nummodes)\n\t\t{\t//failed to query, use internal fallbacks (may be too big).\n\t\t\tfor (a = 0; a < countof(resaspects); a++)\n\t\t\t{\n\t\t\t\tconst char **v = resaspects[a];\n\t\t\t\tfor (; *v; v++)\n\t\t\t\t{\n\t\t\t\t\tconst char *c = *v;\n\t\t\t\t\tconst char *s = strchr(c, 'x');\n\t\t\t\t\tif (s)\n\t\t\t\t\t\tVidMode_Add(atoi(c), atoi(s+1));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tqsort(vidmodes, nummodes, sizeof(*vidmodes), VidMode_Sort);\n\t}\n\n\tif (num < 0 || num >= nummodes)\n\t\treturn false;\n\t*w = vidmodes[num].w;\n\t*h = vidmodes[num].h;\n\treturn true;\n}\n\n\n\n#ifndef NOBUILTINMENUS\n\nextern qboolean forcesaveprompt;\nextern cvar_t pr_debugger;\n\nemenu_t *M_Options_Title(int *y, int infosize)\n{\n\tstruct emenu_s *menu;\n\t*y = 32;\n\n\tmenu = M_CreateMenu(infosize);\n\n\tswitch(M_GameType())\n\t{\n\tcase MGT_QUAKE2:\t//q2...\n\t\tMC_AddCenterPicture(menu, 4, 24, \"pics/m_banner_options\");\n\t\tbreak;\n#ifdef HEXEN2\n\tcase MGT_HEXEN2://h2\n\t\tMC_AddPicture(menu, 16, 0, 35, 176, \"gfx/menu/hplaque.lmp\");\n\t\tMC_AddCenterPicture(menu, 0, 60, \"gfx/menu/title3.lmp\");\n\t\t*y += 32;\n\t\tbreak;\n#endif\n\tdefault: //q1\n\t\tMC_AddPicture(menu, 16, 4, 32, 144, \"gfx/qplaque.lmp\");\n\t\tMC_AddCenterPicture(menu, 4, 24, \"gfx/p_option.lmp\");\n\t\tbreak;\n\t}\n\treturn menu;\n}\n\n//these are awkward/strange\nstatic qboolean M_Options_AlwaysRun (menucheck_t *option, struct emenu_s *menu, chk_set_t set)\n{\n\textern cvar_t cl_run;\n\tswitch (M_GameType())\n\t{\n\tcase MGT_HEXEN2:\n\t\t//hexen2 uses forwardspeed's magnitude as an 'isrunning' boolean check, with all else hardcoded.\n\t\tif (set != CHK_CHECKED)\n\t\t{\n\t\t\tif (cl_forwardspeed.value > 200 || cl_run.ival)\n\t\t\t\tCvar_SetValue(&cl_forwardspeed, 200);\n\t\t\telse\n\t\t\t\tCvar_SetValue(&cl_forwardspeed, 400);\n\t\t\tCvar_Set(&cl_backspeed, \"\");\n\t\t\tCvar_SetValue(&cl_run, 0);\n\t\t}\n\t\treturn cl_forwardspeed.value > 200;\n\tdefault:\n\tcase MGT_QUAKE2:\n\t\t//quake2 mods have a nasty tendancy to hack at the various cvars, which breaks everything\n\t\tif (set != CHK_CHECKED)\n\t\t\tCvar_SetValue(&cl_run, !cl_run.ival);\n\t\treturn cl_run.ival;\n\tcase MGT_QUAKE1:\n\t\t//for better compat with other quake engines, we just ignore the cl_run cvar, at least for the menu.\n\t\tif (set == CHK_CHECKED)\n\t\t\treturn cl_forwardspeed.value > 200;\n\t\telse if (cl_forwardspeed.value > 200)\n\t\t{\n\t\t\tCvar_SetValue (&cl_forwardspeed, 200);\n\t\t\tif (*cl_backspeed.string)\n\t\t\t\tCvar_SetValue (&cl_backspeed, 200);\n\t\t\treturn false;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tCvar_SetValue (&cl_forwardspeed, 400);\n\t\t\tif (*cl_backspeed.string)\n\t\t\t\tCvar_SetValue (&cl_backspeed, 400);\n\t\t\treturn true;\n\t\t}\n\t}\n}\nstatic qboolean M_Options_InvertMouse (menucheck_t *option, struct emenu_s *menu, chk_set_t set)\n{\n\tif (set == CHK_CHECKED)\n\t\treturn m_pitch.value < 0;\n\telse\n\t{\n\t\tCvar_SetValue (&m_pitch, -m_pitch.value);\n\t\treturn m_pitch.value < 0;\n\t}\n}\n\nstatic void M_Options_Predraw(emenu_t *menu)\n{\n\textern cvar_t m_preset_chosen;\n\tmenubutton_t *b;\n\tb = M_FindButton(menu, \"fps_preset\\n\");\n\tb->text = (char*)(b+1) + (m_preset_chosen.ival?2:0);\n\n#ifdef PACKAGEMANAGER\n\tb = M_FindButton(menu, \"menu_download\\n\");\n\tb->text = (char*)(b+1) + (PM_AreSourcesNew(false)?0:2);\n#endif\n}\n\n//options menu.\nvoid M_Menu_Options_f (void)\n{\n\textern cvar_t m_preset_chosen;\n\textern cvar_t crosshair, r_projection;\n\tint y;\n\n\tstatic const char *projections[] = {\n\t\t\"Regular\",\n\t\t\"Stereographic\",\n\t\t\"Fisheye\",\n\t\t\"Panoramic\",\n\t\t\"Lambert Azimuthal Equal-Area\",\n\t\t\"Equirectangular\",\n\t\tNULL\n\t};\n\tstatic const char *projectionvalues[] = {\n\t\t\"0\",\n\t\t\"1\",\n\t\t\"2\",\n\t\t\"3\",\n\t\t\"4\",\n\t\t\"5\",\n\t\tNULL\n\t};\n\n#if !defined(CLIENTONLY) && defined(SAVEDGAMES)\n\textern cvar_t sv_autosave;\n\tstatic const char *autosaveopts[] = {\n\t\t\"Off\",\n\t\t\"30 secs\",\n\t\t\"60 secs\",\n\t\t\"90 secs\",\n\t\t\"120 secs\",\n\t\t\"5 mins\",\n\t\tNULL\n\t};\n\tstatic const char *autosavevals[] = {\n\t\t\"0\",\n\t\t\"0.5\",\n\t\t\"1\",\n\t\t\"1.5\",\n\t\t\"2\",\n\t\t\"5\",\n\t\tNULL\n\t};\n#endif\n#if !defined(CLIENTONLY) && defined(MVD_RECORDING)\n\textern cvar_t sv_demoAutoRecord;\n\tstatic const char *autorecordopts[] = {\n\t\t\"Off\",\n\t\t\"Clientside Only\",\n\t\t\"Prefer Serverside\",\n\t\tNULL\n\t};\n\tstatic const char *autorecordvals[] = {\n\t\t\"0\",\n\t\t\"-1\",\n\t\t\"1\",\n\t\tNULL\n\t};\n\textern cvar_t cl_loopbackprotocol;\n\tstatic const char *lprotopts[] = {\n\t\t\"Vanilla QW\",\n\t\t\"FTE QW (recommended)\",\n#ifdef NQPROT\n\t\t\"FTE NQ\",\n\t\t\"666\",\n\t\t\"BJP3\",\n//\t\t\"DP6\",\n//\t\t\"DP7\",\n\t\t\"Automatic (FTE NQ/QW)\",\n\t\t\"Vanilla NQ\",\n#endif\n\t\tNULL\n\t};\n\tstatic const char *lprotvals[] = {\n\t\t\"qwid\",\n\t\t\"qw\",\n#ifdef NQPROT\n\t\t\"nq\",\n\t\t\"fitz\",\n\t\t\"bjp3\",\n//\t\t\"dp6\",\n//\t\t\"dp7\",\n\t\t\"auto\",\n\t\t\"nqid\",\n#endif\n\t\tNULL\n\t};\n#endif\n\n\textern cvar_t scr_fov_mode;\n\t\tstatic const char *fovmodes[] = {\n\t\t\"Major-\",\n\t\t\"Minor+\",\n\t\t\"Horizontal\",\n\t\t\"Vertical\",\n\t\t\"4:3 stretched\",\n\t\tNULL\n\t};\n\tstatic const char *fovmodevalues[] = {\n\t\t\"0\",\n\t\t\"1\",\n\t\t\"2\",\n\t\t\"3\",\n\t\t\"4\",\n\t\tNULL\n\t};\n\n\textern cvar_t cfg_save_auto;\n\tmenubulk_t bulk[] = {\n\t\tMB_CONSOLECMD(\"Save all settings\", \"cfg_save\\n\", \"Writes changed settings out to a config file.\"),\n\t\tMB_CHECKBOXCVARTIP(\"Auto-save Settings\", cfg_save_auto, 1, \"If this is disabled, you will need to explicitly save your settings.\"),\n\t\tMB_CONSOLECMD(\"Reset to defaults\", \"cvarreset *\\nexec default.cfg\\nplay misc/menu2.wav\\n\", \"Reloads the default configuration.\"),\n\t\tMB_SPACING(4),\n\t\tMB_CONSOLECMD(\"Controls\", \"menu_keys\\n\", \"Modify keyboard and mouse inputs.\"),\n#ifdef PACKAGEMANAGER\n\t\tMB_CONSOLECMD(\"^bUpdates and Packages\", \"menu_download\\n\", \"Configure additional content and plugins.\"),\n#endif\n\t\tMB_CONSOLECMD(\"Go to console\", \"toggleconsole\\nplay misc/menu2.wav\\n\", \"Open up the engine console.\"),\n\t\tMB_COMBOCVAR(\"View Projection\", r_projection, projections, projectionvalues, NULL),\n\t\tMB_COMBOCVAR(\"FOV Mode\", scr_fov_mode, fovmodes, fovmodevalues, NULL),\n\t\tMB_SLIDER(\"Field of View\", scr_fov, 70, 360, 5, NULL),\n\t\tMB_SLIDER(\"Mouse Speed\", sensitivity, 1, 10, 0.2, NULL),\n\t\tMB_SLIDER(\"Crosshair\", crosshair, 0, 22, 1, NULL), // move this to hud setup?\n\t\tMB_CHECKBOXFUNC(\"Always Run\", M_Options_AlwaysRun, 0, \"Set movement to run at fastest speed by default.\"),\n\t\tMB_CHECKBOXFUNC(\"Invert Mouse\", M_Options_InvertMouse, 0, \"Invert vertical mouse movement.\"),\n\t\tMB_CHECKBOXCVAR(\"Lookspring\", lookspring, 0),\n\t\tMB_CHECKBOXCVAR(\"Lookstrafe\", lookstrafe, 0),\n\t\tMB_CHECKBOXCVAR(\"Windowed Mouse\", in_windowed_mouse, 0),\n#if !defined(CLIENTONLY) && defined(SAVEDGAMES)\n\t\tMB_COMBOCVAR(\"Auto Save\", sv_autosave, autosaveopts, autosavevals, NULL),\n#endif\n#if !defined(CLIENTONLY) && defined(MVD_RECORDING)\n\t\tMB_COMBOCVAR(\"Auto Record\", sv_demoAutoRecord, autorecordopts, autorecordvals, NULL),\n\t\tMB_COMBOCVAR(\"Force Protocol\", cl_loopbackprotocol, lprotopts, lprotvals, \"Some protocols may impose additional limitations/breakages, and are listed only for potential demo-recording compat.\"),\n#endif\n\t\tMB_SPACING(4),\n\t\t// removed hud options (cl_sbar, cl_hudswap, old-style chat, old-style msg)\n\t\tMB_CONSOLECMD(\"Audio Options\", \"menu_audio\\n\", \"Set audio quality and speaker setup options.\"),\n\t\tMB_CONSOLECMD(\"^bGraphics Presets\", \"fps_preset\\n\", \"Choose a different graphical preset to use.\"),\n\t\tMB_CONSOLECMD(\"Video Options\", \"menu_video\\n\", \"Set video resolution, color depth, refresh rate, and anti-aliasing options.\"),\n#ifdef TEXTEDITOR\n\t\t//this option is a bit strange in q2.\n//\t\tMB_CHECKBOXCVAR(\"QC Debugger\", pr_debugger, 0),\n#endif\n\t\t// removed downloads (is this still appropriate?)\n\t\t// removed teamplay\n\t\t// removed singleplayer cheats (move this to single player menu)\n\t\tMB_END()\n\t};\n\temenu_t *menu = M_Options_Title(&y, 0);\n\tstatic menuresel_t resel;\n\tint framey = y;\n\tmenubutton_t *o;\n\n\tMC_AddFrameStart(menu, framey);\n\ty = MC_AddBulk(menu, &resel, bulk, 16, 216, y);\n\n#ifdef PLUGINS\n\tif (Cmd_Exists(\"ezhud_nquake\"))\n\t{\n\t\textern cvar_t plug_sbar;\n\t\tstatic const char *hudplugopts[] = {\n\t\t\t\"Never\",\n\t\t\t\"Deathmatch\",\n\t\t\t\"Single Player/Coop\",\n\t\t\t\"Always\",\n\t\t\tNULL\n\t\t};\n\t\tstatic const char *hudplugvalues[] = {\n\t\t\t\"0\",\n\t\t\t\"1\",\n\t\t\t\"2\",\n\t\t\t\"3\",\n\t\t\tNULL\n\t\t};\n\t\tMC_AddCvarCombo(menu, 16, 216, y, localtext(\"Use Hud Plugin\"), &plug_sbar, hudplugopts, hudplugvalues);\t\t\ty += 8;\n\t}\n#endif\n\tMC_AddFrameEnd(menu, framey);\n\n\tmenu->predraw = M_Options_Predraw;\n\tif (!resel.x)\n\t{\n\t\to = NULL;\n\t\tif (!o && !m_preset_chosen.ival)\n\t\t\to = M_FindButton(menu, \"fps_preset\\n\");\n#ifdef PACKAGEMANAGER\n\t\tif (!o && PM_AreSourcesNew(false))\n\t\t\to = M_FindButton(menu, \"menu_download\\n\");\n#endif\n\t\tif (o)\n\t\t{\n\t\t\tmenu->selecteditem = (menuoption_t*)o;\n\t\t\tmenu->cursoritem->common.posy = o->common.posy + (o->common.height-menu->cursoritem->common.height)/2;\n\t\t}\n\t}\n}\n\n#ifndef __CYGWIN__\ntypedef struct {\n\tint cursorpos;\n\tmenuoption_t *cursoritem;\n\n\tmenutext_t *speaker[MAXSOUNDCHANNELS];\n\tmenutext_t *testsoundsource;\n\n\tsoundcardinfo_t *card;\n} audiomenuinfo_t;\n\nqboolean M_Audio_Key (struct emenu_s *menu, int key, unsigned int unicode)\n{\n\tint i;\n\taudiomenuinfo_t *info = menu->data;\n\tsoundcardinfo_t *sc;\n\tfor (sc = sndcardinfo; sc; sc = sc->next)\n\t{\n\t\tif (sc == info->card)\n\t\t\tbreak;\n\t}\n\tif (!sc)\n\t{\n\t\tM_RemoveMenu(menu);\n\t\treturn true;\n\t}\n\n\n\tif (key == K_DOWNARROW || key == K_KP_DOWNARROW || key == K_GP_DPAD_DOWN)\n\t{\n\t\tinfo->testsoundsource->common.posy+=10;\n\t}\n\tif (key == K_UPARROW || key == K_KP_UPARROW || key == K_GP_DPAD_UP)\n\t{\n\t\tinfo->testsoundsource->common.posy-=10;\n\t}\n\tif (key == K_RIGHTARROW || key == K_KP_RIGHTARROW || key == K_GP_DPAD_RIGHT)\n\t{\n\t\tinfo->testsoundsource->common.posx+=10;\n\t}\n\tif (key == K_LEFTARROW || key == K_KP_LEFTARROW || key == K_GP_DPAD_LEFT)\n\t{\n\t\tinfo->testsoundsource->common.posx-=10;\n\t}\n\tif (key >= '0' && key <= '5')\n\t{\n\t\ti = key - '0';\n\n\t\tsc->speakerdir[i][0] = (info->testsoundsource->common.posy-200/2)/-50.0;\n\t\tsc->speakerdir[i][1] = (info->testsoundsource->common.posx-320/2)/-50.0;\n\t\tsc->speakerdir[i][2] = 0;\n\n\t\tsc->dist[i] = VectorLength(sc->speakerdir[i]);\n\t}\n\n\tmenu->selecteditem = NULL;\n\n\treturn false;\n}\n\nvoid M_Audio_StartSound (struct emenu_s *menu)\n{\n\tint i;\n\tvec3_t org;\n\taudiomenuinfo_t *info = menu->data;\n\tsoundcardinfo_t *sc;\n\tvec3_t mat[4];\n\n\tstatic float lasttime;\n\n\tfor (sc = sndcardinfo; sc; sc = sc->next)\n\t{\n\t\tif (sc == info->card)\n\t\t\tbreak;\n\t}\n\tif (!sc)\n\t{\n\t\tM_RemoveMenu(menu);\n\t\treturn;\n\t}\n\n\tfor (i = 0; i < sc->sn.numchannels; i++)\n\t{\n\t\tinfo->speaker[i]->common.posx = 320/2 + sc->speakerdir[i][1] * 50;\n\t\tinfo->speaker[i]->common.posy = 200/2 - sc->speakerdir[i][0] * 50;\n\t}\n\tfor (; i < 6; i++)\n\t\tinfo->speaker[i]->common.posy = -100;\n\n\tif (lasttime+0.5 < Sys_DoubleTime())\n\t{\n\t\tS_GetListenerInfo(0, mat[0], mat[1], mat[2], mat[3]);\n\n\t\tlasttime = Sys_DoubleTime();\n\t\torg[0] = mat[0][0] + 2*(mat[2][0]*(info->testsoundsource->common.posx-320/2) - mat[1][0]*(info->testsoundsource->common.posy-200/2));\n\t\torg[1] = mat[0][1] + 2*(mat[2][1]*(info->testsoundsource->common.posx-320/2) - mat[1][1]*(info->testsoundsource->common.posy-200/2));\n\t\torg[2] = mat[0][2] + 2*(mat[2][2]*(info->testsoundsource->common.posx-320/2) - mat[1][2]*(info->testsoundsource->common.posy-200/2));\n\t\tS_StartSound(0, 0, S_PrecacheSound(\"player/pain3.wav\"), org, NULL, 1, 4, 0, 0, 0);\n\t}\n}\n\nvoid M_Menu_Audio_Speakers_f (void)\n{\n\tint i;\n\taudiomenuinfo_t *info;\n\temenu_t *menu;\n\n\tmenu = M_CreateMenu(sizeof(audiomenuinfo_t));\n\tinfo = menu->data;\n\tmenu->key = M_Audio_Key;\n\tmenu->predraw = M_Audio_StartSound;\n\n\tfor (i = 0; i < 6; i++)\n\t\tinfo->speaker[i] = MC_AddBufferedText(menu, 0, 0, 0, va(\"%i\", i), false, true);\n\n\tinfo->testsoundsource = MC_AddBufferedText(menu, 320/2, 320/2, 200/2, \"X\", false, true);\n\n\tinfo->card = sndcardinfo;\n\n\tmenu->selecteditem = NULL;\n}\n\nstruct audiomenuinfo\n{\n\tchar **outdevnames;\n\tchar **outdevdescs;\n#ifdef VOICECHAT\n\tchar **capdevnames;\n\tchar **capdevdescs;\n#endif\n};\nvoid M_Menu_Audio_Remove(emenu_t *menu)\n{\n\tint i;\n\tstruct audiomenuinfo *info = menu->data;\n\tfor (i = 0; info->outdevnames[i]; i++)\n\t\tZ_Free(info->outdevnames[i]);\n\tZ_Free(info->outdevnames);\n\tfor (i = 0; info->outdevdescs[i]; i++)\n\t\tZ_Free(info->outdevdescs[i]);\n\tZ_Free(info->outdevdescs);\n#ifdef VOICECHAT\n\tfor (i = 0; info->capdevnames[i]; i++)\n\t\tZ_Free(info->capdevnames[i]);\n\tZ_Free(info->capdevnames);\n\tfor (i = 0; info->capdevdescs[i]; i++)\n\t\tZ_Free(info->capdevdescs[i]);\n\tZ_Free(info->capdevdescs);\n#endif\n}\nstruct audiomenuinfo *M_Menu_Audio_Setup(emenu_t *menu)\n{\n#ifdef VOICECHAT\n\textern cvar_t snd_voip_capturedevice_opts;\n#endif\n\textern cvar_t snd_device_opts;\n\tint pairs, i;\n\tstruct audiomenuinfo *info = menu->data;\n\tchar buf[8192];\n\tmenu->remove = M_Menu_Audio_Remove;\n\n\tCmd_TokenizeString(snd_device_opts.string?snd_device_opts.string:\"\", false, false);\n\tpairs = Cmd_Argc()/2;\n\tinfo->outdevnames = BZ_Malloc((pairs+1)*sizeof(char*));\n\tinfo->outdevdescs = BZ_Malloc((pairs+1)*sizeof(char*));\n\tfor (i = 0; i < pairs; i++)\n\t{\n\t\tinfo->outdevnames[i] = Z_StrDup(COM_QuotedString(Cmd_Argv(i*2+0), buf, sizeof(buf), false));\n\t\tinfo->outdevdescs[i] = Z_StrDup(Cmd_Argv(i*2+1));\n\t}\n\tinfo->outdevnames[i] = NULL;\n\tinfo->outdevdescs[i] = NULL;\n#ifdef VOICECHAT\n\tCmd_TokenizeString(snd_voip_capturedevice_opts.string?snd_voip_capturedevice_opts.string:\"\", false, false);\n\tpairs = Cmd_Argc()/2;\n\tinfo->capdevnames = BZ_Malloc((pairs+1)*sizeof(char*));\n\tinfo->capdevdescs = BZ_Malloc((pairs+1)*sizeof(char*));\n\tfor (i = 0; i < pairs; i++)\n\t{\n\t\tinfo->capdevnames[i] = Z_StrDup(COM_QuotedString(Cmd_Argv(i*2+0), buf, sizeof(buf), false));\n\t\tinfo->capdevdescs[i] = Z_StrDup(Cmd_Argv(i*2+1));\n\t}\n\tinfo->capdevnames[i] = NULL;\n\tinfo->capdevdescs[i] = NULL;\n#endif\n\treturn info;\n}\n\nvoid M_Menu_Audio_f (void)\n{\n\tint y;\n\temenu_t *menu = M_Options_Title(&y, sizeof(struct audiomenuinfo));\n\tstruct audiomenuinfo *info = M_Menu_Audio_Setup(menu);\n\textern cvar_t nosound, snd_leftisright, snd_device, snd_khz, snd_speakers, ambient_level, bgmvolume, snd_playersoundvolume, ambient_fade, cl_staticsounds, snd_inactive, _snd_mixahead, snd_doppler;\n//\textern cvar_t snd_noextraupdate, snd_eax, precache;\n#ifdef VOICECHAT\n\textern cvar_t snd_voip_capturedevice, snd_voip_play, snd_voip_send, snd_voip_test, snd_voip_micamp, snd_voip_vad_threshhold, snd_voip_ducking, snd_voip_codec;\n#ifdef HAVE_SPEEX\n\textern cvar_t snd_voip_noisefilter;\n#endif\n#endif\n\n\tstatic const char *soundqualityoptions[] = {\n\t\t\"11025 Hz\",\n\t\t\"22050 Hz\",\n\t\t\"44100 Hz\",\n\t\t\"48000 Hz\",\n\t\tNULL\n\t};\n\n\tstatic const char *soundqualityvalues[] = {\n\t\t\"11\",\n\t\t\"22\",\n\t\t\"44\",\n\t\t\"48\",\n\t\tNULL\n\t};\n\n\tstatic const char *speakeroptions[] = {\n\t\t\"Mono\",\n\t\t\"Stereo\",\n\t\t\"Quad\",\n\t\t\"5.1\",\n\t\tNULL\n\t};\n\n\tstatic const char *speakervalues[] = {\n\t\t\"1\",\n\t\t\"2\",\n\t\t\"4\",\n\t\t\"6\",\n\t\tNULL\n\t};\n#ifdef VOICECHAT\n\tstatic const char *voipcodecoptions[] = {\n#ifdef HAVE_OPUS\n\t\t\"Opus\",\n#endif\n#ifdef HAVE_SPEEX\n#ifdef HAVE_LEGACY\n\t\t\"Speex (ez-compat)\",\n#endif\n\t\t\"Speex (Narrow)\",\n\t\t\"Speex (Wide)\",\n//\t\t\"Speex (UltraWide)\",\n#endif\n//\t\t\"Raw16 (11025)\",\n//\t\t\"PCM A-Law\",\n//\t\t\"PCM U-Law\",\n\t\tNULL\n\t};\n\tstatic const char *voipcodecvalue[] = {\n#ifdef HAVE_OPUS\n\t\t\"2\",\t//opus\n#endif\n#ifdef HAVE_SPEEX\n#ifdef HAVE_LEGACY\n\t\t\"0\",\t//speex non-standard (outdated)\n#endif\n\t\t\"3\",\t//speex narrow\n\t\t\"4\",\t//speex wide\n//\t\t\"5\",\t//speex UW\n#endif\n//\t\t\"1\",\t//pcm16 sucks\n//\t\t\"6\",\t//pcma\n//\t\t\"7\",\t//pcmu\n\t\tNULL\n\t};\n\n\tstatic const char *voipsendoptions[] = {\n\t\t\"Push To Talk\",\n\t\t\"Voice Activation\",\n\t\t\"Continuous\",\n\t\tNULL\n\t};\n\tstatic const char *voipsendvalue[] = {\n\t\t\"0\",\n\t\t\"1\",\n\t\t\"2\",\n\t\tNULL\n\t};\n#endif\n\tmenubulk_t bulk[] = {\n\t\tMB_REDTEXT(\"Sound Options\", true),\n\t\tMB_TEXT(\"^Ue080^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue082\", true),\n\t\tMB_CONSOLECMD(\"Restart Sound\", \"snd_restart\\n\", \"Restart audio systems and apply set options.\"),\n\t\tMB_SPACING(4),\n\t\tMB_COMBOCVAR(\"Output Device\", snd_device, (const char**)info->outdevdescs, (const char**)info->outdevnames, \"Choose which audio driver and device to use.\"),\n\t\tMB_SLIDER(\"Volume\", volume, 0, 1, 0.1, NULL),\n\t\tMB_COMBOCVAR(\"Speaker Setup\", snd_speakers, speakeroptions, speakervalues, NULL),\n\t\tMB_COMBOCVAR(\"Frequency\", snd_khz, soundqualityoptions, soundqualityvalues, NULL),\n\t\tMB_CHECKBOXCVAR(\"Low Quality (8-bit)\", snd_loadas8bit, 0),\n\t\tMB_CHECKBOXCVAR(\"Flip Speakers\", snd_leftisright, 0),\n\t\tMB_SLIDER(\"Mixahead\", _snd_mixahead, 0, 1, 0.05, NULL),\n\t\tMB_CHECKBOXCVAR(\"Disable All Sounds\", nosound, 0),\n\t\tMB_SPACING(4),\n\n\t\tMB_SLIDER(\"Player Sound Volume\", snd_playersoundvolume, 0, 1, 0.1, NULL),\n\t\tMB_SLIDER(\"Ambient Volume\", ambient_level, 0, 1, 0.1, NULL),\n\t\tMB_SLIDER(\"Ambient Fade\", ambient_fade, 0, 1000, 1, NULL),\n\t\tMB_CHECKBOXCVAR(\"Static Sounds\", cl_staticsounds, 0),\n\t\tMB_SLIDER(\"Music Volume\", bgmvolume, 0, 1, 0.1, NULL),\n\t\tMB_SLIDER(\"Doppler Factor\", snd_doppler, 0, 10, 0.1, NULL),\n\t\t// removed music buffer\n\t\t// removed precache\n\t\t// removed eax2\n\t\t// remove no extra update\n\t\tMB_CHECKBOXCVAR(\"Sound While Inactive\", snd_inactive, 0),\n\t\tMB_SPACING(4),\n\n#ifdef VOICECHAT\n\t\tMB_REDTEXT(\"Voice Options\", true),\n\t\tMB_TEXT(\"^Ue080^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue082\", true),\n\t\tMB_COMBOCVAR(\"Microphone Device\", snd_voip_capturedevice, (const char**)info->capdevdescs, (const char**)info->capdevnames, NULL),\n\t\tMB_SLIDER(\"Voice Volume\", snd_voip_play, 0, 2, 0.1, NULL),\n\t\tMB_CHECKBOXCVAR(\"Microphone Test\", snd_voip_test, 0),\n\t\tMB_SLIDER(\"Microphone Volume\", snd_voip_micamp, 0, 2, 0.1, NULL),\n\t\tMB_COMBOCVAR(\"Activation Mode\", snd_voip_send, voipsendoptions, voipsendvalue, NULL),\n\t\tMB_SLIDER(\"Act. Threshhold\", snd_voip_vad_threshhold, 0, 30, 1, NULL),\n\t\tMB_CHECKBOXCVAR(\"Audio Ducking\", snd_voip_ducking, 0),\n#ifdef HAVE_SPEEX\n\t\tMB_CHECKBOXCVAR(\"Noise Cancelation\", snd_voip_noisefilter, 0),\n#endif\n\t\tMB_COMBOCVAR(\"Codec\", snd_voip_codec, voipcodecoptions, voipcodecvalue, NULL),\n#endif\n\n\t\t//MB_CONSOLECMD(\"Speaker Test\", \"menu_speakers\\n\", \"Test speaker setup output.\"),\n\t\tMB_END()\n\t};\n\tstatic menuresel_t resel;\n\tMC_AddFrameStart(menu, y);\n\tMC_AddBulk(menu, &resel, bulk, 16, 216, y);\n\tMC_AddFrameEnd(menu, y);\n}\n\n#else\nvoid M_Menu_Audio_f (void)\n{\n\tCon_Printf(\"No sound in cygwin\\n\");\n}\n#endif\n\n\n\nvoid M_Menu_Particles_f (void)\n{\n\temenu_t *menu;\n\textern cvar_t r_bouncysparks, r_part_rain, gl_part_flame, r_grenadetrail, r_rockettrail, r_part_rain_quantity, r_particledesc, r_particle_tracelimit, r_part_contentswitch, r_bloodstains;\n//\textern cvar_t r_part_sparks_trifan, r_part_sparks_textured, r_particlesystem;\n\n/*\tstatic const char *psystemopts[] =\n\t{\n\t\t\"Classic\",\n\t\t\"Script\",\n\t\t\"None\",\n\t\tNULL\n\t};\n\tstatic const char *psystemvals[] =\n\t{\n\t\t\"classic\",\n\t\t\"script\",\n\t\t\"null\",\n\t\tNULL\n\t};\n*/\n\tstatic const char *pdescopts[] =\n\t{\n\t\t\"Classic\",\n\t\t\"Faithful\",\n\t\t\"High FPS\",\n\t\t\"Fancy\",\n\t\t\"Fancy+LG\",\n\t\t\"Snazzy\",\n\t\t\"Bare bones\",\n\t\tNULL\n\t};\n\tstatic const char *pdescvals[] =\n\t{\n\t\t\"classic\",\n\t\t\"faithful\",\n\t\t\"highfps\",\n\t\t\"spikeset\",\n\t\t\"spikeset tsshaft\",\n\t\t\"high tsshaft\",\n\t\t\"minimal\",\n\t\tNULL\n\t};\n\n\tstatic const char *trailopts[] =\n\t{\n\t\t\"Disable\",\n\t\t\"Default\",\n\t\t\"Swap\",\n\t\t\"Alternate\",\n\t\t\"Blood\",\n\t\t\"Zombie\",\n\t\t\"Scrag\",\n\t\t\"Knight\",\n\t\t\"Vore\",\n\t\t\"Rail\",\n\t\tNULL\n\t};\n\tstatic const char *trailvals[] = { \"0\", \"1\", \"2\", \"3\", \"4\", \"5\", \"6\", \"7\", \"8\", \"9\", NULL };\n\n\tint y;\n\tmenubulk_t bulk[] = {\n\t\tMB_REDTEXT(\"Particle Options\", true),\n\t\tMB_TEXT(\"^Ue080^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue082\", true),\n//\t\tMB_COMBOCVAR(\"Particle System\", r_particlesystem, psystemopts, psystemvals, \"Selects particle system to use. Classic is standard Quake particles, script is FTE style scripted particles, and none disables particles entirely.\"),\n\t\tMB_COMBOCVAR(\"Particle Set\", r_particledesc, pdescopts, pdescvals, \"Selects particle set to use with the scripted particle system.\"),\n\t\tMB_SPACING(4),\n\t\tMB_COMBOCVAR(\"Rocket Trail\", r_rockettrail, trailopts, trailvals, \"Chooses effect to replace rocket trails.\"),\n\t\tMB_COMBOCVAR(\"Grenade Trail\", r_grenadetrail, trailopts, trailvals, \"Chooses effect to replace grenade trails.\"),\n\t\tMB_SPACING(4),\n\t\t// removed texture sparks\n\t\t// removed trifan sparks\n\t\tMB_CHECKBOXCVAR(\"Particle Physics\", r_bouncysparks, 0),\n\t\tMB_CHECKBOXCVAR(\"Particle Stains\", r_bloodstains, 0),\n\t\tMB_CHECKBOXCVAR(\"Content Switching\", r_part_contentswitch, 0),\n\t\tMB_CHECKBOXCVAR(\"Surface Emitting\", r_part_rain, 0),\n\t\tMB_SLIDER(\"Surface Quantity\", r_part_rain_quantity, 0, 10, 1, NULL),\n\t\tMB_CHECKBOXCVAR(\"Model Emitting\", gl_part_flame, 0),\n\t\tMB_SLIDER(\"Trace Limit\", r_particle_tracelimit, 0, 2000, 100, NULL),\n\t\t// removed particle beams\n\t\tMB_END()\n\t};\n\tstatic menuresel_t resel;\n\n\tmenu = M_Options_Title(&y, 0);\n\tMC_AddFrameStart(menu, y);\n\tMC_AddBulk(menu, &resel, bulk, 16, 200, y);\n\tMC_AddFrameEnd(menu, y);\n}\n\nconst char *presetname[] =\n{\n\t\"286\",\t\t//everything turned off to make it as fast as possible, even if you're crippled without it\n\t\"Fast\",\t\t//typical deathmatch settings.\n\t\"Spasm\",\t//faithful NQ aesthetics (fairly strict, but not all out).\n\t\"Vanilla\",\t//vanilla effects enabled, no content replacement.\n\t\"Normal\",\t//content replacement enabled\n\t\"Nice\",\t\t//potentially expensive, but not painful\n\t\"Realtime\",\t//everything on\n\tNULL\n};\n#define PRESET_NUM (countof(presetname)-1)\n\n// this is structured like this for a possible future feature\n// also don't include cvars that need a restart here\nconst char *presetexec[] =\n{\n\t// 286 options (also the first commands to be execed in the chain)\n\t\"seta m_preset_chosen 1;\"\n\t\"seta gl_texturemode nn;\"\n\t\"seta gl_texturemode2d n;\"\n\t\"seta gl_blendsprites 0;\"\n\t\"seta r_particlesystem null;\"\n\t\"seta r_particledesc \\\"\\\";\"\n\t\"seta r_part_classic_square 0;\"\n\t\"seta r_part_classic_expgrav 10;\"\n\t\"seta r_part_classic_opaque 0;\"\n\t\"seta cl_expsprite 1;\"\n\t\"seta r_stains 0;\"\n\t\"seta r_drawflat 1;\"\n\t\"seta r_lightmap 0;\"\n\t\"seta r_nolerp 1;\"\n\t\"seta r_noframegrouplerp 1;\"\n\t\"seta r_nolightdir 1;\"\n\t\"seta r_dynamic 0;\"\n\t\"seta r_bloom 0;\"\n\t\"seta r_softwarebanding 0;\"\n\t\"seta d_mipcap 0 1000;\"\n\t\"seta gl_affinemodels 0;\"\n\t\"seta gl_polyblend 0;\"\n\t\"seta gl_flashblend 0;\"\n\t\"seta gl_specular 0;\"\n\t\"seta r_deluxemapping 0;\"\n\t\"seta r_loadlit 0;\"\n\t\"seta r_fastsky 1;\"\n\t\"seta r_drawflame 0;\"\n\t\"seta r_waterstyle 1;\"\n\t\"seta r_lavastyle 1;\"\t\t//defer to water\n\t\"seta r_fog_cullentities 2;\"\n//\t\"seta r_slimestyle \\\"\\\";\"\t//defer to water\n\t\"seta r_coronas 0;\"\n\t\"seta r_shadow_realtime_dlight 0;\"\n\t\"seta r_shadow_realtime_world 0;\"\n\t\"seta r_shadow_realtime_dlight_shadows 1;\"\n\t\"seta r_glsl_offsetmapping 0;\"\n\t\"seta vid_hardwaregamma 3;\"\t//people benchmarking against other engines with fte using glsl gamma and the other not is annoying as fuck.\n//\t\"seta gl_detail 0;\"\n\t\"seta gl_load24bit 0;\"\n\t\"seta r_replacemodels \\\"\\\";\"\n\t\"seta r_waterwarp 0;\"\n\t\"seta r_lightstylesmooth 0;\"\n\t\"seta r_lightstylespeed 0;\"\n\t\"seta r_part_density 0.25;\"\n\t\"seta cl_nolerp 1;\"\n\t\"seta r_lerpmuzzlehack 0;\"\n\t\"seta v_gunkick 0;\"\n\t\"seta r_shadows 0;\"\n\t\"seta v_viewmodel_quake 0;\"\n\t\"seta cl_rollangle 0;\"\n\t\"seta cl_bob 0;\"\n\t\"seta cl_sbar 0;\"\n//\t\"cvarreset sv_nqplayerphysics;\"\t//server settings in a preset might be bad.\n\t\"seta cl_demoreel 0;\"\n\t\"seta cl_gibfilter 1;\"\n\t\"if cl_deadbodyfilter == 0 then seta cl_deadbodyfilter 1;\"\t\t//as useful as 2 is, some mods use death frames for crouching etc.\n\t\"seta gl_simpleitems 1;\"\n\t\"seta cl_fullpitch 1;seta maxpitch \\\"\\\";seta minpitch \\\"\\\";\"\t//mimic quakespasm where possible.\n\t\"seta r_graphics 1;\"\n\t\"seta r_renderscale 1;\"\n\t\"seta gl_texture_anisotropic_filtering 0;\"\n\t// end '286'\n\n\t, // fast options (for deathmatch)\n\t\"gl_texturemode ln;\"\n\t\"gl_texturemode2d n;\"\n#ifdef MINIMAL\n\t\"r_particlesystem classic;\"\n#else\n\t\"r_particlesystem script;\"\n#endif\n\t\"r_particledesc classic;\"\n\t\"r_drawflat 0;\"\n\t\"r_nolerp 0;\"\n\t\"gl_flashblend 1;\"\n\t\"r_loadlit 1;\"\n\t\"r_fastsky 0;\"\n\t\"r_waterstyle 1;\"\n\t\"r_lavastyle 1;\"\n\t\"r_nolightdir 0;\"\n\t\"seta gl_simpleitems 0;\"\n\t// end fast\n\n\t, //quakespasm-esque options (for singleplayer faithful).\n\t\"gl_texturemode2d l.l;\"\n\t\"r_part_density 1;\"\n\t\"gl_polyblend 1;\"\n\t\"r_dynamic 2;\"\n\t\"gl_flashblend 0;\"\n\t\"cl_nolerp 0;\"\t//projectiles lerped at least.\n\t\"r_noframegrouplerp 1;\" //flames won't lerp\n\t\"r_waterwarp 1;\"\n\t\"r_drawflame 1;\"\n\t\"v_gunkick 1;\"\n\t\"cl_rollangle 2.0;\"\n\t\"cl_bob 0.02;\"\n\t\"r_fog_cullentities 1;\"\n\t\"r_lightstylespeed 10;\"\n\t\"vid_hardwaregamma 1;\"\t\t//auto hardware gamma, for fast fullscreen and usable windowed.\n\t\"r_part_classic_expgrav 1;\"\t//vanillaery\n\t\"r_part_classic_opaque 1;\"\n//\t\"r_particlesystem script;\"\t//q2 or hexen2 particle effects need to be loadable\n\t\"cl_sbar 2;\"\t\t\t\t//its a style thing\n//\t\"sv_nqplayerphysics 1;\"\t\t//gb wanted this, should give nq physics to people who want nq settings. note that this disables prediction. disabled again because 'auto' matches the mod, which generally works out better.\n\t\"cl_demoreel 1;\"\t\t\t//yup, arcadey\n\t//\"d_mipcap \\\"0 3\\\";\"\t\t//logically correct, but will fuck up on ATI drivers if increased mid-map, because ATI will just ignore any levels that are not currently enabled.\n\t\"cl_gibfilter 0;\"\n\t\"seta cl_deadbodyfilter 0;\"\n\t\"gl_texture_anisotropic_filtering 4;\"\n\t\"cl_fullpitch 1;maxpitch 90;seta minpitch -90;\"\t//QS has cheaty viewpitch range. some maps require it.\n\t// end spasm\n\n\t, //vanilla-esque options (for purists).\n\t\"cl_fullpitch 0;maxpitch \\\"\\\";seta minpitch \\\"\\\";\"\t//quakespasm is not vanilla\n\t\"gl_texturemode nll;\"\t\t//yup, we went there.\n\t\"gl_texturemode2d n.l;\"\t\t//yeah, 2d too.\n\t\"r_nolerp 1;\"\n\t\"cl_sbar 1;\"\n\t\"d_mipcap 0 2;\"\t\t\t\t//gl without anisotropic filtering favours too-distant mips too often, so lets just pretend it doesn't exist. should probably mess with lod instead or something\n\t\"v_viewmodel_quake 1;\"\n\t\"r_loadlit 0;\"\n\t\"gl_affinemodels 1;\"\n\t\"r_softwarebanding 1;\"\t\t//ugly software banding.\n\t\"r_part_classic_square 1;\"\t//blocky baby!\n\t// end vanilla\n\n\t, // normal (faithful) options, but with content replacement thrown in\n//#ifdef MINIMAL\n//\t\"r_particlesystem classic;\"\n//#else\n//\t\"r_particlesystem script;\"\n//\t\"r_particledesc classic;\"\n//#endif\n\t\"r_part_classic_square 0;\"\n\t\"r_part_classic_expgrav 10;\"\t//gives a slightly more dynamic feel to them\n\t\"r_part_classic_opaque 0;\"\n\t\"gl_load24bit 1;\"\n\t\"r_replacemodels \\\"md3 md2 md5mesh\\\";\"\n\t\"r_coronas 1;\"\n\t\"r_dynamic 1;\"\n\t\"r_softwarebanding 0;\"\n\t\"d_mipcap 0 1000;\"\n\t\"gl_affinemodels 0;\"\n\t\"r_lerpmuzzlehack 1;\"\n\t\"gl_texturemode ln;\"\n\t\"gl_texturemode2d l;\"\n\t\"cl_sbar 0;\"\n\t\"v_viewmodel_quake 0;\"\t//don't move the gun around weirdly.\n//\t\"cvarreset sv_nqplayerphysics;\"\n\t\"cl_demoreel 0;\"\n\t\"r_loadlit 1;\"\n\t\"r_nolerp 0;\"\n\t\"r_noframegrouplerp 0;\"\n\t\"cl_fullpitch 1;maxpitch 90;seta minpitch -90;\"\n\t//end normal\n\n\t, // nice options\n//\t\"r_stains 0.75;\"\n\t\"gl_texturemode lll;\"\n#ifndef MINIMAL\n//\t\"r_particlesystem script;\"\n\t\"r_particledesc \\\"high tsshaft\\\";\"\n#endif\n\t\"gl_specular 1;\"\n//\t\"r_loadlit 3;\"\n\t\"r_waterstyle 2;\"\n\t\"gl_blendsprites 1;\"\n//\t\"r_fastsky -1;\"\n\t\"r_dynamic 0;\"\n\t\"r_shadow_realtime_dlight 1;\"\n//\t\"gl_detail 1;\"\n\t\"r_lightstylesmooth 1;\"\n\t\"r_deluxemapping 2;\"\n\t//end 'nice'\n\n\t, // realtime options\n\t\"r_bloom 1;\"\n\t\"r_deluxemapping 1;\"\t//won't be seen anyway\n\t\"r_particledesc \\\"high tsshaft\\\";\"\n//\t\"r_waterstyle 3;\"\t//too expensive.\n\t\"r_glsl_offsetmapping 1;\"\n\t\"r_shadow_realtime_world 1;\"\n\t\"gl_texture_anisotropic_filtering 16;\"\n\t\"vid_hardwaregamma 4;\"\t//scene gamma\n\t//end 'realtime'\n};\n\nstruct\n{\n\tconst char *name;\n\tqboolean dorestart;\n\tconst char *desc;\n\tconst char *settings;\n} builtinpresets[] =\n{\n\t{\t\"hdr\", true,\n\t\t\"Don't let colour depth stop you!\",\n\n\t\t\"set vid_srgb 2\\n\"\n\t\t\"set r_hdr_irisadaptation 1\\n\"\n\t},\n\t{\t\"shib\", true,\n\t\t\"Performance optimisations for large/detailed maps.\",\n\n\t\t\"set r_temporalscenecache 1\\n\"\t//the main speedup.\n\t\t\"set r_lightstylespeed 0\\n\"\t\t//FIXME: we shouldn't need this, but its too stuttery without.\n\t\t\"set sv_autooffload 1\\n\"\t\t//Needs polish still.\n\t\t\"set gl_pbolightmaps 1\\n\"\t\t//FIXME: this needs to be the default eventually.\n\t},\n\t{\t\"dm\", false,\n\t\t\"Various settings to make you more competitive.\"\n\n\t\t\"set cl_yieldcpu 0\\n\"\n\t\t\"set v_kickroll 0\\n\"\t\t//roll change when taking damage\n\t\t\"set v_kickpitch 0\\n\"\t\t//pitch change when taking damage\n\t\t\"set v_damagecshift 0\\n\"\t//colour change when taking damage\n\t\t\"set v_gunkick 0\\n\"\t\t\t//recoil when firing\n\t\t\"set cl_rollangle 0\\n\"\t\t//rolling when strafing\n\t\t\"set cl_bob 0\\n\"\t\t\t//view bobbing when moving.\n#ifdef _WIN32\n\t\t\"set sys_clockprecision 1\\n\"\t//windows kinda sucks otherwise\n#endif\n\t},\n\t{\t\"qw\", false,\n\t\t\"Enable QuakeWorld physics, for better gameplay.\",\n\n\t\t\"set sv_nqplayerphysics 0\\n\"\n\t\t\"set sv_gameplayfix_multiplethinks 1\\n\"\n\t\t\"cvarreset pm_bunnyfriction\\n\"\n\t\t\"cvarreset pm_edgefriction\\n\"\n\t\t\"cvarreset pm_slidefix\\n\"\n\t\t\"cvarreset pm_slidyslopes\\n\"\n\t},\n\t{\t\"hybridphysics\", false,\n\t\t\"Tweak QuakeWorld player physics to feel like nq physics, while still supporting prediction.\",\n\n\t\t\"set sv_nqplayerphysics 0\\n\"\n\t\t\"set sv_gameplayfix_multiplethinks 1\\n\"\n\t\t\"set pm_bunnyfriction 1\\n\"\t//don't need bunnyspeedcap with this.\n\t\t\"set pm_edgefriction 2\\n\"\t//forces traceline instead of tracebox, to match nq (applies earlier, making it more aggressive)\n\t\t\"set pm_slidefix 1\\n\"\t\t//smoother running down slopes\n\t\t\"set pm_slidyslopes 1\\n\"\t//*sigh*\n\t\t\"set pm_noround 1\\n\"\t\t//lame\n\t\t\"set sv_maxtic 0\\n\"\t\t\t//fixed tick rates.\n\t},\n\t{\t\"nq\", false,\n\t\t\"Disable QuakeWorld physics, for nq mod compat.\",\n\n\t\t\"set sv_nqplayerphysics 1\\n\"\n\t\t\"set sv_gameplayfix_multiplethinks 0\\n\"\n\t\t//*also* set these, in case they use nqplayerphysics 2 after, which should give better hints.\n\t\t\"set pm_bunnyfriction 1\\n\"\t//don't need bunnyspeedcap with this.\n\t\t\"set pm_edgefriction 2\\n\"\t//forces traceline instead of tracebox, to match nq (applies earlier, making it more aggressive)\n\t\t\"set pm_slidefix 1\\n\"\t\t//smoother running down slopes\n\t\t\"set pm_slidyslopes 1\\n\"\t//*sigh*\n\t\t\"set pm_noround 1\\n\"\t\t//lame\n\t},\n\n\t{\t\"dp\", false,\n\t\t\"Reconfigures FTE to mimic DP for compat reasons.\",\n\n\t\t\"if $server then echo Be sure to restart your server\\n\"\n\n\t\t\"fps_preset nq\\n\"\n\t\t//these are for smc+derived mods\n\t\t\"sv_listen_dp 1\\n\"\t\t\t\t\t//awkward, but forces the server to load the effectinfo.txt in advance.\n\t\t\"sv_bigcoords 1\\n\"\t\t\t\t\t//for viewmodel lep precision (would be better to use csqc)\n\t\t\"r_particledesc \\\"effectinfo high\\\"\\n\" //blurgh.\n\t\t\"dpcompat_noretouchground 1\\n\"\t\t//don't call touch functions on entities that already appear onground. this also changes the order that the onground flag is set relative to touch functions.\n\t\t\"cl_nopred 1\\n\"\t\t\t\t\t\t//DP doesn't predict by default, and DP mods have a nasty habit of clearing .solid values during prethinks, which screws up prediction. so play safe.\n\t\t\"r_dynamic 0\\nr_shadow_realtime_dlight 1\\n\" //fte has separate cvars for everything. which kinda surprises people and makes stuff twice as bright as it should be.\n\t\t\"r_coronas_intensity 0.25\\n\"\n\t\t\"con_logcenterprint 0\\n\"\t\t\t//kinda annoying....\n\t\t\"scr_fov_mode 4\\n\"\t\t\t\t\t//for fairer framerate comparisons\n\n\t\t//general compat stuff\n\t\t\"dpcompat_console 1\\n\"\t\t\t\t//\n\t\t\"dpcompat_findradiusarealinks 1\\n\"\t//faster findradiuses (but that require things are setorigined properly)\n\t\t\"dpcompat_makeshitup 2\\n\"\t\t\t//flatten shaders to a single pass, then add new specular etc passes.\n\t\t//\"dpcompat_nopremulpics 1\\n\"\t\t\t//don't use premultiplied alpha (solving issues with compressed image formats)\n\t\t\"dpcompat_psa_ungroup 1\\n\"\t\t\t//don't use framegroups with psk models at all.\n\t\t\"dpcompat_set 1\\n\"\t\t\t\t\t//handle 3-arg sets differently\n\t\t\"dpcompat_stats 1\\n\"\t\t\t\t//truncate float stats\n\t\t\"dpcompat_strcat_limit 16383\\n\"\t\t//xonotic compat. maximum length of strcat strings.\n\n//\t\t\"sv_listen_dp 1\\nsv_listen_nq 0\\nsv_listen_qw 0\\ncl_loopbackprotocol dpp7\\ndpcompat_nopreparse 1\\n\"\n\t},\n\n\t{\t\"tenebrae\", true,\n\t\t\"Reconfigures FTE to mimic Tenebrae for compat/style reasons.\",\n\t\t//for the luls. combine with the tenebrae mod for maximum effect.\n\t\t\"fps_preset nq\\n\"\n\t\t\"set r_shadow_realtime_world 1\\n\"\n\t\t\"set r_shadow_realtime_dlight 1\\n\"\n\t\t\"set r_shadow_bumpscale_basetexture 4\\n\"\n\t\t\"set r_shadow_shadowmapping 0\\n\"\n\t\t\"set gl_specular 1\\n\"\n\t\t\"set gl_specular_power 16\\n\"\n\t\t\"set gl_specular_fallback 1\\n\"\n\t\t\"set mod_litsprites_force 1\\n\"\n\t\t\"set gl_blendsprites 2\\n\"\n\t\t\"set r_nolerp 1\\n\"\t//well, that matches tenebrae. for the luls, right?\n\t},\n\n\t{\t\"timedemo\", false,\n\t\t\"Reconfigure some stuff to get through timedemos really fast. Some people might consider this cheating.\",\n\t\t//some extra things to pwn timedemos.\n\t\t\"fps_preset fast\\n\"\n\t\t\"set r_renderscale 1\\n\"\n\t\t\"set contrast 1\\n\"\n\t\t\"set gamma 1\\n\"\n\t\t\"set brightness 0\\n\"\n\t\t\"set scr_autoid 0\\n\"\n\t\t\"set scr_autoid_team 0\\n\"\n\t\t\"set r_dynamic 0\\n\"\n\t\t\"set sbar_teamstatus 2\\n\"\n\t\t\"set gl_polyblend 0\\n\"\n#if 1\n\t\t//these are cheaty settings.\n\t\t\"set gl_flashblend 0\\n\"\n\t\t\"set cl_predict_players 0\\n\"\t//very cheaty. you won't realise its off, but noone would disable it for actual play.\n#else\n\t\t//to make things fair\n\t\t\"set gl_flashblend 1\\n\"\n\t\t\"set r_part_density 1\\n\"\n#endif\n\t},\n};\n\nstatic void ApplyPreset (int presetnum, qboolean doreload)\n{\n\tint i;\n\t//this function is written backwards, to ensure things work properly in configs etc.\n\n\t// TODO: work backwards and only set cvars once\n\tif (doreload)\n\t{\n\t\tforcesaveprompt = true;\n\t\tCbuf_InsertText(\"\\nfs_restart\\nvid_reload\\ncl_warncmd 1\\n\", RESTRICT_LOCAL, true);\n\t}\n\tfor (i = presetnum; i >= 0; i--)\n\t{\n\t\tCbuf_InsertText(presetexec[i], RESTRICT_LOCAL, true);\n\t}\n\tif (doreload)\n\t{\n\t\tCbuf_InsertText(\"\\ncl_warncmd 0\\n\", RESTRICT_LOCAL, true);\n\t}\n}\n\nstatic int M_Menu_Preset_GetActive(void)\n{\n\textern cvar_t r_drawflat;\n\n\t//bottoms up!\n#ifdef RTLIGHTS\n\tif (r_shadow_realtime_world.ival)\n\t\treturn 6;\t//realtime\n\telse\n#endif\n\t\tif (r_deluxemapping_cvar.ival)\n\t\treturn 5;\t//nice\n\telse if (gl_load24bit.ival)\n\t\treturn 4;\t//normal\n\telse if (r_softwarebanding_cvar.ival)\n\t\treturn 3;\t//vanilla\n\telse if (cl_sbar.ival == 2)\n\t\treturn 2;\t//spasm\n\telse if (!r_drawflat.ival)\n\t\treturn 1;\t//fast\n\telse\n\t\treturn 0;\t//simple\n}\n\nstatic int M_Menu_ApplyGravity(menuoption_t *op)\n{\t//menu is bottom-up, so we return the y pos of its parent...\n\tif (!op)\n\t\treturn 0;\n\n\tif (op->common.ishidden)\n\t\treturn M_Menu_ApplyGravity(op->common.next);\n\n\tif (op->common.grav_y)\n\t\top->common.posy = M_Menu_ApplyGravity(op->common.next)+op->common.grav_y;\n\telse\t//not moving this one, but make sure others move properly.\n\t\tM_Menu_ApplyGravity(op->common.next);\n\n\treturn op->common.posy;\n}\nstatic void M_Menu_Preset_Predraw(emenu_t *menu)\n{\n\textern cvar_t m_preset_chosen;\n\tmenuoption_t *op;\n\tint preset = M_Menu_Preset_GetActive();\n\tint i;\n\tqboolean forcereload = false;\n\tqboolean filtering = false;\n\textern cvar_t cfg_save_auto;\n\n#ifdef RTLIGHTS\n\tpreset = 6-preset;\n#else\n\tpreset = 5-preset;\n#endif\n\n\tfor (op = menu->options; op; op = op->common.next)\n\t{\n\t\tif (op->common.type == mt_button)\n\t\t{\n\t\t\tif (!strcmp(op->button.command, \"menupop\\n\"))\n\t\t\t{\n\t\t\t\tif (m_preset_chosen.ival)\n\t\t\t\t\top->button.text = localtext(\"^sAccept\");\n\t\t\t}\n\t\t\telse if (!strncmp(op->button.command, \"fps_preset \", 11))\n\t\t\t{\n\t\t\t\tif (((char*)op->button.text)[0] == '^' && ((char*)op->button.text)[1] == ((preset!=0)?'m':'7'))\n\t\t\t\t\t((char*)op->button.text)[1] = (preset==0)?'m':'7';\n\t\t\t\tpreset--;\n\t\t\t}\n#if defined(WEBCLIENT) && defined(PACKAGEMANAGER)\n\t\t\telse if (!strcmp(op->button.command, \"menu_download\\n\"))\n\t\t\t{\n\t\t\t\top->button.text = PM_AreSourcesNew(false)?localtext(\"^bPackages (New!)\"):localtext(\"Packages\");\n\t\t\t\top->common.posx = op->common.next->common.posx;\n\t\t\t\top->common.width = 216-op->common.posx;\n\t\t\t}\n#endif\n\t\t}\n\t\telse if (!filtering)\n\t\t{\n\t\t\tif (op->common.type == mt_checkbox && op->check.var == &cfg_save_auto)\n\t\t\t\tfiltering = true;\n\t\t}\n\t\telse if(op->common.type == mt_checkbox||\n\t\t\t\top->common.type == mt_slider||\n\t\t\t\top->common.type == mt_combo) op->common.ishidden = preset!=0;\n\n\t\tif (op->common.type == mt_combo && op->combo.cvar)\n\t\t{\t//make sure combo items are updated properly if their cvar value is changed via some other mechanism...\n\t\t\tconst char *curval = op->combo.cvar->latched_string?op->combo.cvar->latched_string:op->combo.cvar->string;\n\t\t\tif (strcmp(curval, op->combo.values[op->combo.selectedoption]))\n\t\t\t{\n\t\t\t\tfor (i = 0; i < op->combo.numoptions; i++)\n\t\t\t\t{\n\t\t\t\t\tif (!strcmp(curval, op->combo.values[i]))\n\t\t\t\t\t{\n\t\t\t\t\t\top->combo.selectedoption = i;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (op->combo.cvar->latched_string && (op->combo.cvar->flags&CVAR_LATCHMASK) == CVAR_RENDERERLATCH)\n\t\t\t\tforcereload = true;\n\t\t}\n\t}\n\tM_Menu_ApplyGravity(menu->options);\n\tmenu->cursoritem->common.posy = menu->selecteditem->common.posy + (menu->selecteditem->common.height-menu->cursoritem->common.height)/2;\n\n\tif (forcereload)\n\t\tCbuf_InsertText(\"\\nfs_restart\\nvid_reload\\n\", RESTRICT_LOCAL, true);\n}\n\nvoid M_Menu_Preset_f (void)\n{\n\textern cvar_t cfg_save_auto;\n\temenu_t *menu;\n\tint y;\n\tmenuoption_t *presetoption[7];\n\textern cvar_t r_nolerp, r_loadlits;\n#ifdef HAVE_SERVER\n\textern cvar_t sv_nqplayerphysics;\n#endif\n#if defined(RTLIGHTS) && (defined(GLQUAKE) || defined(VKQUAKE))\n\textern cvar_t r_bloom, r_shadow_realtime_world_importlightentitiesfrommap;\n#endif\n\n\tstatic const char *deluxeopts[] = {\n\t\t\"Off\",\n\t\t\"Auto\",\n\t\t\"Force\",\n\t\tNULL\n\t};\n\tstatic const char *litopts[] = {\n\t\t\"Off\",\n\t\t\"Auto\",\n\t\tNULL\n\t};\n\tstatic const char *litvals[] = {\n\t\t\"1\",\n\t\t\"3\",\n\t\tNULL\n\t};\n\tmenubulk_t bulk[] =\n\t{\n\t\tMB_REDTEXT(\"Please Choose Preset\", true),\n\t\tMB_TEXT(\"^Ue080^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue082\", true),\n\t\tMB_CONSOLECMDRETURN(\"^7simple  (untextured)\",\t\"fps_preset 286\\n\",\t\t\t\"Lacks textures, particles, pretty much everything.\", presetoption[0]),\n\t\t\tMB_CHECKBOXCVAR(\"anim snapping\", r_nolerp, 0),\n\t\tMB_CONSOLECMDRETURN(\"^7fast (qw deathmatch)\",\t\"fps_preset fast\\n\",\t\t\"Fullscreen effects off to give consistant framerates\", presetoption[1]),\n\t\tMB_CONSOLECMDRETURN(\"^7spasm    (nq compat)\",\t\"fps_preset spasm\\n\",\t\t\"Aims for visual compatibility with common NQ engines. Also affects mods slightly.\", presetoption[2]),\n#ifdef HAVE_SERVER\n\t\t\tMB_CHECKBOXCVAR(\"nq physics\", sv_nqplayerphysics, 0),\n#endif\n\t\tMB_CONSOLECMDRETURN(\"^7vanilla  (softwarey)\",\t\"fps_preset vanilla\\n\",\t\t\"This is for purists! Party like its 1995! No sanity spared!\", presetoption[3]),\n\t\t\tMB_CHECKBOXCVAR(\"anim snapping\", r_nolerp, 1),\n#if defined(GLQUAKE)\n\t\t\tMB_CHECKBOXCVAR(\"model swimming\", gl_affinemodels, 0),\n#endif\n\t\tMB_CONSOLECMDRETURN(\"^7normal    (faithful)\",\t\"fps_preset normal\\n\",\t\t\"An updated but still faithful appearance, using content replacements where applicable\", presetoption[4]),\n\t\tMB_CONSOLECMDRETURN(\"^7nice       (dynamic)\",\t\"fps_preset nice\\n\",\t\t\"For people who like nice things, but still want to actually play\", presetoption[5]),\n\t\t\tMB_COMBOCVAR(\"rgb lighting\", r_loadlits, litopts, litvals, NULL),\n\t\t\tMB_COMBOCVAR(\"deluxemaps\", r_deluxemapping_cvar, deluxeopts, NULL, NULL),\n#if defined(RTLIGHTS) && (defined(GLQUAKE) || defined(VKQUAKE))\n\t\tMB_CONSOLECMDRETURN(\"^7realtime    (all on)\",\t\"fps_preset realtime\\n\",\t\"For people who value pretty over fast/smooth. Not viable for deathmatch.\", presetoption[6]),\n\t\t\tMB_CHECKBOXCVAR(\"bloom\", r_bloom, 1),\n\t\t\tMB_CHECKBOXCVAR(\"force rtlights\", r_shadow_realtime_world_importlightentitiesfrommap, 1),\n#endif\n\t\tMB_SPACING(16),\n\t\tMB_CHECKBOXCVARTIP(\"Auto-save Settings\", cfg_save_auto, 1, \"If this is disabled, you will need to explicitly save your settings.\"),\n#if defined(WEBCLIENT) && defined(PACKAGEMANAGER)\n\t\tMB_CONSOLECMD(\"Packages\",\t\t\t\t\"menu_download\\n\",\t\"Configure sources and packages.\"),\n#endif\n\t\tMB_SPACING(16),\n\t\tMB_CONSOLECMD(\"Accept\",\t\t\t\t\t\"menupop\\n\",\t\"Continue with selected settings.\"),\n\t\tMB_END()\n\t};\n\tstatic menuresel_t resel;\n\tint item;\n\textern cvar_t r_drawflat;\n\tmenu = M_Options_Title(&y, 0);\n\tMC_AddBulk(menu, &resel, bulk, 16, 216, y);\n\tmenu->selecteditem = menu->options;\n\t//bottoms up!\n#ifdef RTLIGHTS\n\tif (r_shadow_realtime_world.ival)\n\t\titem = 6;\t//realtime\n\telse\n#endif\n\t\tif (r_deluxemapping_cvar.ival)\n\t\titem = 5;\t//nice\n\telse if (gl_load24bit.ival)\n\t\titem = 4;\t//normal\n\telse if (r_softwarebanding_cvar.ival)\n\t\titem = 3;\t//vanilla\n\telse if (cl_sbar.ival == 2)\n\t\titem = 2;\t//spasm\n\telse if (!r_drawflat.ival)\n\t\titem = 1;\t//fast\n\telse\n\t\titem = 0;\t//simple\n\tif (presetoption[item])\n\t{\n\t\tmenu->selecteditem = presetoption[item];\n\t\tmenu->cursoritem->common.posy = menu->selecteditem->common.posy + (menu->selecteditem->common.height-menu->cursoritem->common.height)/2;\n\t}\n\n\t//so they can actually see the preset they're picking.\n\tmenu->nobacktint = true;\n\tmenu->predraw = M_Menu_Preset_Predraw;\n\n\tCbuf_InsertText(\"\\ndemos idle\\n\", RESTRICT_LOCAL, false);\n}\n\nvoid FPS_Preset_f (void)\n{\n\tchar *presetfname;\n\tchar *arg = Cmd_Argv(1);\n\tint i;\n\tqboolean doreload = true;\n\n\tif (!*arg)\n\t{\n\t\tM_Menu_Preset_f();\n\t\treturn;\n\t}\n\n\tif (!strncmp(arg, \"builtin_\", 8))\n\t{\n\t\targ += 8;\n\t\tdoreload = false;\n\t}\n\telse\n\t{\n\t\tpresetfname = va(\"configs/preset_%s.cfg\", arg);\n\t\tif (COM_FCheckExists(presetfname))\n\t\t{\n\t\t\tchar buffer[MAX_OSPATH];\n\t\t\tCOM_QuotedString(presetfname, buffer, sizeof(buffer), false);\n\t\t\tCbuf_InsertText(va(\"\\nexec %s\\nfs_restart\\n\", buffer), RESTRICT_LOCAL, false);\n\t\t\treturn;\n\t\t}\n\t}\n\n\tfor (i = 0; i < PRESET_NUM; i++)\n\t{\n\t\tif (!stricmp(presetname[i], arg))\n\t\t{\n\t\t\tApplyPreset(i, doreload);\n\t\t\treturn;\n\t\t}\n\t}\n\n\tfor (i = 0; i < countof(builtinpresets); i++)\n\t{\n\t\tif (!stricmp(builtinpresets[i].name, arg))\n\t\t{\n\t\t\tif (doreload && builtinpresets[i].dorestart)\n\t\t\t\tCbuf_InsertText(\"\\nfs_restart\\nvid_reload\\n\", RESTRICT_LOCAL, false);\n\t\t\tCbuf_InsertText(builtinpresets[i].settings, RESTRICT_LOCAL, false);\n\t\t\treturn;\n\t\t}\n\t}\n\n\tCon_Printf(\"Preset %s not recognised\\n\", arg);\n\tCon_Printf(\"Valid presests:\\n\");\n\tfor (i = 0; i < PRESET_NUM; i++)\n\t\tCon_Printf(\"%s\\n\", presetname[i]);\n\tfor (i = 0; i < countof(builtinpresets); i++)\n\t\tCon_DPrintf(\"%s\\n\", builtinpresets[i].name);\n}\n\nstatic int QDECL CompletePresetList (const char *name, qofs_t flags, time_t mtime, void *parm, searchpathfuncs_t *spath)\n{\n\tstruct xcommandargcompletioncb_s *ctx = parm;\n\tif (!Q_strncasecmp(name, \"configs/preset_\", 15))\n\t{\n\t\tchar preset[MAX_QPATH];\n\t\tCOM_StripExtension(name+15, preset, sizeof(preset));\n\t\tctx->cb(preset, NULL, NULL, ctx);\n\t}\n\treturn true;\n}\nvoid FPS_Preset_c(int argn, const char *partial, struct xcommandargcompletioncb_s *ctx)\n{\n\tif (argn == 1)\n\t{\n\t\tint i;\n\t\tsize_t partiallen = strlen(partial);\n\n\t\tCOM_EnumerateFiles(va(\"configs/preset_%s*.cfg\", partial), CompletePresetList, ctx);\n\n\t\tfor (i = 0; i < PRESET_NUM; i++)\n\t\t\tif (!Q_strncasecmp(partial, presetname[i], partiallen))\n\t\t\t\tctx->cb(presetname[i], NULL, NULL, ctx);\n\n\t\tfor (i = 0; i < countof(builtinpresets); i++)\n\t\t\tif (!Q_strncasecmp(partial, builtinpresets[i].name, partiallen))\n\t\t\t\tctx->cb(builtinpresets[i].name, builtinpresets[i].desc, NULL, ctx);\n\t}\n}\n\n/*typedef struct fpsmenuinfo_s\n{\n\tmenucombo_t *preset;\n} fpsmenuinfo_t;\nqboolean M_PresetApply (union menuoption_s *op, struct emenu_s *menu, int key)\n{\n\tfpsmenuinfo_t *info = (fpsmenuinfo_t*)menu->data;\n\n\tif (key != K_ENTER && key != K_KP_ENTER && key != K_GP_DIAMOND_CONFIRM && key != K_MOUSE1 && key != K_TOUCHTAP)\n\t\treturn false;\n\n\tCbuf_AddText(\"fps_preset \", RESTRICT_LOCAL);\n\tCbuf_AddText(info->preset->options[info->preset->selectedoption], RESTRICT_LOCAL);\n\tCbuf_AddText(\"\\n\", RESTRICT_LOCAL);\n\n\treturn true;\n}*/\n\nvoid M_Menu_FPS_f (void)\n{\n\tstatic const char *fpsopts[] =\n\t{\n\t\t\"Disabled\",\n\t\t\"Average FPS\",\n\t\t\"Timing Graph\",\n\t\tNULL\n\t};\n\tstatic const char *fpsvalues[] = {\"0\", \"1\", \"2\", NULL};\n\tstatic const char *entlerpopts[] =\n\t{\n\t\t\"Enabled (always)\",\n\t\t\"Disabled\",\n\t\t\"Enabled (SP only)\",\n\t\tNULL\n\t};\n\tstatic const char *playerlerpopts[] =\n\t{\n\t\t\"Disabled\",\n\t\t\"Enabled\",\n\t\tNULL\n\t};\n\tstatic const char *bodyopts[] =\n\t{\n\t\t\"Disabled\",\n\t\t\"Ground\",\n\t\t\"All\",\n\t\tNULL\n\t};\n\tstatic const char *values_0_1_2[] = {\"0\", \"1\", \"2\", NULL};\n\tstatic const char *values_0_1[] = {\"0\", \"1\", NULL};\n\n\temenu_t *menu;\n//\tfpsmenuinfo_t *info;\n\n\textern cvar_t v_contentblend, show_fps, cl_r2g, cl_gibfilter, cl_expsprite, cl_deadbodyfilter, cl_lerp_players, cl_nolerp, cl_maxfps, cl_yieldcpu, r_halfrate;\n\tstatic menuresel_t resel;\n\tint y;\n\tmenu = M_Options_Title(&y, 0);\n//\tmenu = M_Options_Title(&y, sizeof(fpsmenuinfo_t));\n//\tinfo = (fpsmenuinfo_t *)menu->data;\n\n\t/*lerping is here because I'm not sure where else to put it. if they care about framerate that much then they'll want to disable interpolation to get as up-to-date stuff as possible*/\n\n\t{\n\t\tmenubulk_t bulk[] =\n\t\t{\n\t\t\tMB_REDTEXT(\"FPS Options\", true),\n\t\t\tMB_TEXT(\"^Ue080^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue082\", true),\n//\t\t\tMB_COMBORETURN(\"Preset\", presetname, 2, info->preset, \"Select a builtin configuration of graphical settings.\"),\n//\t\t\tMB_CMD(\"Apply\", M_PresetApply, \"Applies selected preset.\"),\n//\t\t\tMB_SPACING(4),\n\t\t\tMB_COMBOCVAR(\"Show FPS\", show_fps, fpsopts, fpsvalues, \"Display FPS or frame millisecond values on screen. Settings except immediate are for values across 1 second.\"),\n\t\t\tMB_EDITCVARSLIM(\"Framerate Limiter\", cl_maxfps.name, \"Limits the maximum framerate. Set to 0 for none.\"),\n\t\t\tMB_CHECKBOXCVARTIP(\"Yield CPU\", cl_yieldcpu, 1, \"Reduce CPU usage between frames.\\nShould probably be off when using vsync.\"),\n\t\t\tMB_CHECKBOXCVARTIP(\"Half-Rate Shading\", r_halfrate, 0, \"Reduce the number of shader invocations to save gpu time (doesn't harm edges).\"),\n\t\t\tMB_COMBOCVAR(\"Player lerping\", cl_lerp_players, playerlerpopts, values_0_1, \"Smooth movement of other players, but will increase effective latency. Does not affect all network protocols.\"),\n\t\t\tMB_COMBOCVAR(\"Entity lerping\", cl_nolerp, entlerpopts, values_0_1_2, \"Smooth movement of entities, but will increase effective latency.\"),\n\t\t\tMB_CHECKBOXCVAR(\"Content Blend\", v_contentblend, 0),\n\t\t\tMB_CHECKBOXCVAR(\"Gib Filter\", cl_gibfilter, 0),\n\t\t\tMB_COMBOCVAR(\"Dead Body Filter\", cl_deadbodyfilter, bodyopts, values_0_1_2, \"Selects which dead player frames to filter out in rendering. Ground frames are those of the player lying on the ground, and all frames include all used in the player dying animation.\"),\n\t\t\tMB_CHECKBOXCVAR(\"Explosion Sprite\", cl_expsprite, 0),\n\t\t\tMB_CHECKBOXCVAR(\"Rockets to Grenades\", cl_r2g, 0),\n\t\t\tMB_EDITCVAR(\"Skybox\", \"r_skybox\"),\n\t\t\tMB_END()\n\t\t};\n\t\tMC_AddFrameStart(menu, y);\n\t\tMC_AddBulk(menu, &resel, bulk, 16, 216, y);\n\t\tMC_AddFrameEnd(menu, y);\n\t}\n}\n\nvoid M_Menu_Render_f (void)\n{\n\tstatic const char *warpopts[] =\n\t{\n\t\t\"Disabled\",\n\t\t\"FOV Warp\",\n\t\t\"Shader\",\n\t\tNULL\n\t};\n\tstatic const char *warpvalues[] =\n\t{\n\t\t\"0\",\n\t\t\"-1\",\n\t\t\"1\",\n\t\tNULL\n\t};\n\n\tstatic const char *logcenteropts[] = {\"Off\", \"Singleplayer\", \"Always\", NULL};\n\tstatic const char *logcentervalues[] = {\"0\", \"1\", \"2\", NULL};\n\n\tstatic const char *cshiftopts[] = {\"Off\", \"Fullscreen\", \"Edges\", NULL};\n\tstatic const char *cshiftvalues[] = {\"0\", \"1\", \"2\", NULL};\n\n\tstatic const char *scenecacheopts[] = {\"Auto\", \"Force Off\", \"Force On\", NULL};\n\tstatic const char *scenecachevalues[] = {\"\", \"0\", \"1\", NULL};\n\n\temenu_t *menu;\n\textern cvar_t r_novis, cl_item_bobbing, r_waterwarp, r_nolerp, r_noframegrouplerp, r_fastsky, gl_nocolors, gl_lerpimages, r_wateralpha, r_drawviewmodel, gl_cshiftenabled, r_hdr_irisadaptation, scr_logcenterprint, r_fxaa, r_graphics;\n#if defined(GLQUAKE) || defined(VKQUAKE)\n\textern cvar_t r_bloom;\n#endif\n\tstatic menuresel_t resel;\n\n\tint y;\n\tmenubulk_t bulk[] =\n\t{\n\t\tMB_REDTEXT(\"Rendering Options\", true),\n\t\tMB_TEXT(\"^Ue080^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue082\", true),\n\t\tMB_CHECKBOXCVARTIP(\"Graphics\", r_graphics, 0, \"The only option console games have. Yes, this is a joke cvar. Try toggling it!\"),\t//graphics on / off. Its a general dig at modern games not having any real options.\n\t\tMB_CHECKBOXCVARTIP(\"Disable VIS\", r_novis, 0, \"You shouldn't normally set this. Its better to use vispatches for your maps in order to support transparent water properly.\"),\n\t\tMB_CHECKBOXCVARTIP(\"Fast Sky\", r_fastsky, 0, \"Use black skies. On modern machines this is more of a stylistic choice that a perfomance helper.\"),\n\t\tMB_CHECKBOXCVAR(\"Lerp Images\", gl_lerpimages, 0),\n\t\tMB_CHECKBOXCVAR(\"Disable Model Lerp\", r_nolerp, 0),\n\t\tMB_CHECKBOXCVAR(\"Disable Framegroup Lerp\", r_noframegrouplerp, 0),\n\t\tMB_CHECKBOXCVAR(\"Model Bobbing\", cl_item_bobbing, 0),\n\t\tMB_COMBOCVAR(\"Scene Cache\", r_temporalscenecache, scenecacheopts,scenecachevalues, \"Cache scene data to significantly optimise highly complex scenes or unvised maps.\\nThis may result in offscreen surfaces getting rendered.\"),\n\t\tMB_COMBOCVAR(\"Water Warp\", r_waterwarp, warpopts, warpvalues, NULL),\n\t\tMB_SLIDER(\"Water Alpha\", r_wateralpha, 0, 1, 0.1, NULL),\n\t\tMB_SLIDER(\"Viewmodel Alpha\", r_drawviewmodel, 0, 1, 0.1, NULL),\n\t\tMB_COMBOCVAR(\"Screen Tints\", gl_cshiftenabled, cshiftopts, cshiftvalues, \"Changes how screen flashes should be displayed (otherwise known as polyblends).\"),\n#ifdef QWSKINS\n\t\tMB_CHECKBOXCVAR(\"Ignore Player Colors\", gl_nocolors, 0),\n#endif\n\t\tMB_COMBOCVAR(\"Log Centerprints\", scr_logcenterprint, logcenteropts, logcentervalues, \"Display centerprints in the console also.\"),\n\t\tMB_CHECKBOXCVAR(\"FXAA\", r_fxaa, 0),\n#if defined(GLQUAKE) || defined(VKQUAKE)\n\t\tMB_CHECKBOXCVAR(\"Bloom\", r_bloom, 0),\n#endif\n\t\tMB_CHECKBOXCVARTIP(\"HDR\", r_hdr_irisadaptation, 0, \"Adjust scene brightness to compensate for lighting levels.\"),\n\t\tMB_END()\n\t};\n\tmenu = M_Options_Title(&y, 0);\n\tMC_AddFrameStart(menu, y);\n\tMC_AddBulk(menu, &resel, bulk, 16, 216, y);\n\tMC_AddFrameEnd(menu, y);\n}\n\nvoid M_Menu_Textures_f (void)\n{\n\tstatic const char *texturefilternames[] =\n\t{\n\t\t\"Nearest (noise)\",\n\t\t\"Nearest (harsh)\",\n\t\t\"Nearest (soft)\",\n\t\t\"Bilinear\",\n\t\t\"Trilinear\",\n\t\tNULL\n\t};\n\tstatic const char *texturefiltervalues[] =\n\t{\n\t\t\"GL_NEAREST\",\n\t\t\"GL_NEAREST_MIPMAP_NEAREST\",\n\t\t\"nll\",\n\t\t\"GL_LINEAR_MIPMAP_NEAREST\",\n\t\t\"GL_LINEAR_MIPMAP_LINEAR\",\n\t\tNULL\n\t};\n\n\tstatic const char *texture2dfilternames[] =\n\t{\n\t\t\"Nearest\",\n\t\t\"Linear\",\n\t\tNULL\n\t};\n\tstatic const char *texture2dfiltervalues[] =\n\t{\n\t\t\"GL_NEAREST\",\n\t\t\"GL_LINEAR\",\n\t\tNULL\n\t};\n\n\n\tstatic const char *anisotropylevels[] =\n\t{\n\t\t\"Off\",\n\t\t\"2x\",\n\t\t\"4x\",\n\t\t\"8x\",\n\t\t\"16x\",\n\t\tNULL\n\t};\n\tstatic const char *anisotropyvalues[] =\n\t{\n\t\t\"1\",\n\t\t\"2\",\n\t\t\"4\",\n\t\t\"8\",\n\t\t\"16\",\n\t\tNULL\n\t};\n\n\tstatic const char *texturesizeoptions[] =\n\t{\n\t\t\"128\",\n//\t\t\"192\",\n\t\t\"256\",\n//\t\t\"384\",\n\t\t\"512\",\n//\t\t\"768\",\n\t\t\"1024\",\n\t\t\"2048\",\n\t\t\"4096\",\n\t\t\"8192\",\n\t\tNULL\n\t};\n\n\textern cvar_t gl_load24bit, gl_specular, gl_compress, gl_picmip, gl_picmip2d, gl_max_size, r_drawflat, r_glsl_offsetmapping;\n\textern cvar_t gl_texture_anisotropic_filtering, gl_texturemode, gl_texturemode2d, gl_mipcap;\n\tint y;\n\tmenubulk_t bulk[] =\n\t{\n\t\tMB_REDTEXT(\"Texture Options\", true),\n\t\tMB_TEXT(\"^Ue080^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue082\", true),\n\t\tMB_CHECKBOXCVAR(\"Load Replacements\", gl_load24bit, 0),\n\t\tMB_CHECKBOXCVAR(\"Simple Texturing\", r_drawflat, 0),\n\t\tMB_COMBOCVAR(\"3D Filter Mode\", gl_texturemode, texturefilternames, texturefiltervalues, \"Chooses the texture filtering method used for 3D objects.\"),\n\t\tMB_COMBOCVAR(\"2D Filter Mode\", gl_texturemode2d, texture2dfilternames, texture2dfiltervalues, \"Chooses the texture filtering method used for HUD, menus, and other 2D assets.\"),\n\t\tMB_COMBOCVAR(\"Anisotropy\", gl_texture_anisotropic_filtering, anisotropylevels, anisotropyvalues, NULL),\n\t\tMB_SPACING(4),\n#ifdef GLQUAKE\n\t\tMB_CHECKBOXCVAR(\"Software-style Rendering\", r_softwarebanding_cvar, 0),\n#endif\n\t\tMB_CHECKBOXCVAR(\"Specular Highlights\", gl_specular, 0),\n//\t\tMB_CHECKBOXCVAR(\"Detail Textures\", gl_detail, 0),\n\t\tMB_CHECKBOXCVAR(\"offsetmapping\", r_glsl_offsetmapping, 0),\n\t\tMB_SPACING(4),\n\t\tMB_CHECKBOXCVAR(\"Texture Compression\", gl_compress, 0), // merge the save compressed tex options into here?\n\t\tMB_SLIDER(\"3D Picmip\", gl_picmip, 0, 16, 1, NULL),\n\t\tMB_SLIDER(\"2D Picmip\", gl_picmip2d, 0, 16, 1, NULL),\n\t\tMB_SLIDER(\"World Mipcap\", gl_mipcap, 0, 3, 1, NULL),\n\t\tMB_COMBOCVAR(\"Max Texture Size\", gl_max_size, texturesizeoptions, texturesizeoptions, NULL),\n\t\tMB_END()\n\t};\n\temenu_t *menu = M_Options_Title(&y, 0);\n\tstatic menuresel_t resel;\n\tMC_AddFrameStart(menu, y);\n\tMC_AddBulk(menu, &resel, bulk, 16, 216, y);\n\tMC_AddFrameEnd(menu, y);\n}\n\ntypedef struct {\n\tmenucombo_t *lightcombo;\n\tmenucombo_t *dlightcombo;\n} lightingmenuinfo_t;\n\nqboolean M_VideoApplyShadowLighting (union menuoption_s *op,struct emenu_s *menu,int key)\n{\n\tlightingmenuinfo_t *info = (lightingmenuinfo_t*)menu->data;\n\n\tif (key != K_ENTER && key != K_KP_ENTER && key != K_GP_DIAMOND_CONFIRM && key != K_MOUSE1 && key != K_TOUCHTAP)\n\t\treturn false;\n\n#ifdef RTLIGHTS\n\t{\n\t\tchar *cvarsrw = \"0\";\n\t\tchar *cvarsrws = \"0\";\n\t\tchar *cvarv = \"0\";\n\t\tswitch (info->lightcombo->selectedoption)\n\t\t{\n\t\tcase 1:\n\t\t\tcvarsrw = \"1\";\n\t\t\tbreak;\n\t\tcase 2:\n\t\t\tcvarsrw = \"1\";\n\t\t\tcvarsrws = \"1\";\n\t\t\tbreak;\n\t\tcase 3:\n\t\t\tcvarv = \"1\";\n\t\t\tbreak;\n\t\t}\n#ifdef MINIMAL\n\t\t(void)cvarv;\n\t\tCbuf_AddText(va(\"r_shadow_realtime_world %s;r_shadow_realtime_world_shadows %s\\n\", cvarsrw, cvarsrws), RESTRICT_LOCAL);\n#else\n\t\tCbuf_AddText(va(\"r_vertexlight %s;r_shadow_realtime_world %s;r_shadow_realtime_world_shadows %s\\n\", cvarv, cvarsrw, cvarsrws), RESTRICT_LOCAL);\n#endif\n\t}\n#endif\n\n\t{\n\t\tchar *cvard = \"0\";\n\t\tchar *cvarvd = \"0\";\n\t\tchar *cvarsrd = \"0\";\n\t\tchar *cvarsrds = \"0\";\n\t\tswitch (info->dlightcombo->selectedoption)\n\t\t{\n\t\tcase 1:\n\t\t\tcvard = \"1\";\n\t\t\tbreak;\n\t\tcase 2:\n\t\t\tcvarsrd = \"1\";\n\t\t\tbreak;\n\t\tcase 3:\n\t\t\tcvarsrd = \"1\";\n\t\t\tcvarsrds = \"1\";\n\t\t\tbreak;\n\t\tcase 4:\n\t\t\tcvard = \"1\";\n\t\t\tcvarvd = \"1\";\n\t\t\tbreak;\n\t\t}\n#ifdef RTLIGHTS\n#ifdef MINIMAL\n\t\tCbuf_AddText(va(\"r_shadow_realtime_dlight %s;r_shadow_realtime_dlight_shadows %s;r_dynamic %s\\n\", cvarsrd, cvarsrds, cvard), RESTRICT_LOCAL);\n#else\n\t\tCbuf_AddText(va(\"r_shadow_realtime_dlight %s;r_shadow_realtime_dlight_shadows %s;r_dynamic %s;r_vertexdlight %s\\n\", cvarsrd, cvarsrds, cvard, cvarvd), RESTRICT_LOCAL);\n#endif\n#else\n#ifdef MINIMAL\n\t\tCbuf_AddText(va(\"r_dynamic %s\\n\", cvard), RESTRICT_LOCAL);\n#else\n\t\tCbuf_AddText(va(\"r_dynamic %s;r_vertexdlight %s\\n\", cvard, cvarvd), RESTRICT_LOCAL);\n#endif\n#endif\n\t\t(void)cvarsrd, (void)cvarsrds, (void)cvard, (void)cvarvd;\n\t}\n\n\tCbuf_AddText(\"vid_restart\\n\", RESTRICT_LOCAL);\n\n\tM_RemoveMenu(menu);\n\tCbuf_AddText(\"menu_lighting\\n\", RESTRICT_LOCAL);\n\treturn true;\n}\n\nvoid M_Menu_Lighting_f (void)\n{\n#ifndef MINIMAL\n\textern cvar_t r_vertexlight;\n#endif\n\textern cvar_t r_stains, r_shadows, r_loadlits, r_lightmap_format;\n\textern cvar_t r_lightstylesmooth, r_nolightdir;\n#ifdef RTLIGHTS\n\textern cvar_t r_dynamic, r_shadow_realtime_world, r_shadow_realtime_dlight, r_shadow_realtime_dlight_shadows;\n#ifndef MINIMAL\n\textern cvar_t r_vertexdlights;\n#endif\n#endif\n\textern cvar_t r_fb_models, r_rocketlight, r_powerupglow;\n\textern cvar_t v_powerupshell, r_explosionlight;\n\t//extern cvar_t r_fb_bmodels, r_shadow_realtime_world_lightmaps, r_lightstylespeed;\n\n\tstatic const char *lightingopts[] =\n\t{\n\t\t\"Standard\",\n#ifdef RTLIGHTS\n\t\t\"Realtime\",\n\t\t\"RT+Shadows\",\n#ifndef MINIMAL\n\t\t\"Vertex\",\n#endif\n#endif\n\t\tNULL\n\t};\n\tstatic const char *dlightopts[] =\n\t{\n\t\t\"None\",\n\t\t\"Standard\",\n#ifdef RTLIGHTS\n\t\t\"Realtime\",\n\t\t\"RT+Shadows\",\n#ifndef MINIMAL\n\t\t\"Vertex\",\n#endif\n#endif\n\t\tNULL\n\t};\n\n\tstatic const char *loaddeluxeopts[] =\n\t{\n\t\t\"Disabled\",\n\t\t\"Enabled\",\n\t\t\"Generate\",\n\t\tNULL\n\t};\n\n\tstatic const char *loadlitopts[] =\n\t{\n\t\t\"Disabled\",\n\t\t\"Enabled\",\n\t\t\"Generate LDR\",\n\t\t\"Generate HDR\",\n\t\tNULL\n\t};\n\n\tstatic const char *fbopts[] =\n\t{\n\t\t\"Disabled\",\n\t\t\"Enabled\",\n\t\t\"Traced\",\n\t\tNULL\n\t};\n\tstatic const char *fbvalues[] =\n\t{\n\t\t\"0\",\n\t\t\"1\",\n\t\t\"2\",\n\t\tNULL\n\t};\n\n\tstatic const char *powerupopts[] =\n\t{\n\t\t\"Disabled\",\n\t\t\"Enabled\",\n\t\t\"Non-Self\",\n\t\tNULL\n\t};\n\tstatic const char *powerupvalues[] =\n\t{\n\t\t\"0\",\n\t\t\"1\",\n\t\t\"2\",\n\t\tNULL\n\t};\n\n\tstatic const char *fb_models_opts[] =\n\t{\n\t\t\"Disabled\",\n\t\t\"Entire model\",\n\t\t\"If textured\",\n\t\tNULL\n\t};\n\tstatic const char *fb_models_values[] =\n\t{\n\t\t\"0\",\n\t\t\"1\",\n\t\t\"2\",\n\t\tNULL\n\t};\n\n\tstatic const char *lightmapformatopts[] =\n\t{\n\t\t\"Automatic\",\n\t\t\"4bit\",\n\t\t\"5bit (5551)\",\n\t\t\"5bit (565)\",\n\t\t\"8bit (Greyscale)\",\n\t\t\"8bit (Misaligned)\",\n\t\t\"8bit (Aligned)\",\n\t\t\"10bit\",\n\t\t\"9bit (HDR)\",\n\t\t\"Half-Float (HDR)\",\n\t\t\"Float (HDR)\",\n\t\tNULL\n\t};\n\tstatic const char *lightmapformatvalues[] =\n\t{\n\t\t\"\",\n\t\t\"rgba4\",\n\t\t\"rgb5a1\",\n\t\t\"rgb565\",\n\t\t\"l8\",\n\t\t\"rgb8\",\n\t\t\"bgrx8\",\n\t\t\"rgb10\",\n\t\t\"rgb9e5\",\n\t\t\"rgba16f\",\n\t\t\"rgba32f\",\n\t\tNULL\n\t};\n\n\tint y;\n\temenu_t *menu = M_Options_Title(&y, sizeof(lightingmenuinfo_t));\n\n\tint lightselect, dlightselect;\n\n#ifdef RTLIGHTS\n\tif (r_shadow_realtime_world.ival)\n\t{\n\t\tif (r_shadow_realtime_world_shadows.ival)\n\t\t\tlightselect = 2;\n\t\telse\n\t\t\tlightselect = 1;\n\t}\n\telse\n#endif\n#ifndef MINIMAL\n\tif (r_vertexlight.ival)\n\t\tlightselect = 3;\n\telse\n#endif\n\t\tlightselect = 0;\n\n#ifdef RTLIGHTS\n\tif (r_shadow_realtime_dlight.ival)\n\t{\n\t\tif (r_shadow_realtime_dlight_shadows.ival)\n\t\t\tdlightselect = 3;\n\t\telse\n\t\t\tdlightselect = 2;\n\t}\n#ifndef MINIMAL\n\telse if (r_vertexdlights.ival)\n\t\tdlightselect = 4;\n#endif\n\telse\n#endif\n\tif (r_dynamic.ival > 0)\n\t\tdlightselect = 1;\n\telse\n\t\tdlightselect = 0;\n\n\t{\n\t\tlightingmenuinfo_t *info = menu->data;\n\t\tmenubulk_t bulk[] =\n\t\t{\n\t\t\tMB_REDTEXT(\"Lighting Options\", true),\n\t\t\tMB_TEXT(\"^Ue080^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue082\", true),\n\t\t\tMB_COMBORETURN(\"Lighting Mode\", lightingopts, lightselect, info->lightcombo, \"Selects method used for world lighting. Realtime lighting requires appropriate realtime lighting files for maps.\"),\n\t\t\tMB_COMBORETURN(\"Dynamic Lighting Mode\", dlightopts, dlightselect, info->dlightcombo, \"Selects method used for dynamic lighting such as explosion lights and muzzle flashes.\"),\n#ifdef RTLIGHTS\n#ifdef VKQUAKE\n\t\t\tMB_CHECKBOXCVARTIP(\"Raytrace Shadows\", r_shadow_raytrace, 0, \"Enables raytraced shadows when supported by hardware+drivers. Consider combining with half-rate shading.\"),\n#endif\n\t\t\tMB_CHECKBOXCVARTIP(\"Soft Shadows\", r_shadow_shadowmapping, 0, \"Enables softer shadows instead of course-edged pixelated shadows.\"),\n\t\t\tMB_CMD(\"Apply Lighting\", M_VideoApplyShadowLighting, \"Applies set lighting modes and restarts video.\"),\n\t\t\tMB_SPACING(4),\n#endif\n\t\t\tMB_COMBOCVAR(\"Lightmap Format\", r_lightmap_format, lightmapformatopts, lightmapformatvalues, \"Selects which format to use for lightmaps.\"),\n\t\t\tMB_COMBOCVAR(\"LIT Loading\", r_loadlits, loadlitopts, NULL, \"Determines if the engine should use external colored lighting for maps. The generated setting will cause the engine to generate colored lighting for maps that don't have the associated data.\"),\n\t\t\tMB_COMBOCVAR(\"Deluxemapping\", r_deluxemapping_cvar, loaddeluxeopts, NULL, \"Controls whether static lighting should respond to lighting directions.\"),\n\t\t\tMB_CHECKBOXCVAR(\"Lightstyle Lerp\", r_lightstylesmooth, 0),\n\t\t\tMB_SPACING(4),\n\t\t\tMB_COMBOCVAR(\"Flash Blend\", r_flashblend, fbopts, fbvalues, \"Disables or enables the spherical light effect for dynamic lights. Traced means the sphere effect will be line of sight checked before displaying the effect.\"),\n\t\t\tMB_SLIDER(\"Explosion Light\", r_explosionlight, 0, 1, 0.1, NULL),\n\t\t\tMB_SLIDER(\"Rocket Light\", r_rocketlight, 0, 1, 0.1, NULL),\n\t\t\tMB_COMBOCVAR(\"Powerup Glow\", r_powerupglow, powerupopts, powerupvalues, \"Disables or enables the dynamic light effect for powerups. Non-self will disable the light only for the current player.\"),\n\t\t\tMB_CHECKBOXCVAR(\"Powerup Shell\", v_powerupshell, 0),\n\t\t\tMB_SPACING(4),\n\t\t\tMB_SLIDER(\"Blob Shadows\", r_shadows, 0, 1, 0.05, \"Small blobs underneath monsters and players, to add depth to the scene without excessive rendering.\"),\n\t\t\tMB_SLIDER(\"Stains\", r_stains, 0, 1, 0.05, \"Allows discolouration of world surfaces, commonly used for blood trails.\"),\n\t\t\tMB_CHECKBOXCVARTIP(\"No Light Direction\", r_nolightdir, 0, \"Disables shading calculations for uniform light levels on models from all directions.\"),\n\t\t\tMB_COMBOCVAR(\"Model Fullbrights\", r_fb_models, fb_models_opts, fb_models_values, \"Affects loading of fullbrights on models/polymeshes.\"),\n\t\t\tMB_END()\n\t\t};\n\t\tstatic menuresel_t resel;\n\t\tMC_AddFrameStart(menu, y);\n\t\tMC_AddBulk(menu, &resel, bulk, 16, 216, y);\n\t\tMC_AddFrameEnd(menu, y);\n\t}\n}\n\n\ntypedef struct {\nmenucombo_t *skillcombo;\nmenucombo_t *mapcombo;\n} singleplayerinfo_t;\n\n#ifdef HAVE_SERVER\nstatic const char *maplist_q1[] =\n{\n\t\"start\",\n\t\"e1m1\",\n\t\"e1m2\",\n\t\"e1m3\",\n\t\"e1m4\",\n\t\"e1m5\",\n\t\"e1m6\",\n\t\"e1m7\",\n\t\"e1m8\",\n\t\"e2m1\",\n\t\"e2m2\",\n\t\"e2m3\",\n\t\"e2m4\",\n\t\"e2m5\",\n\t\"e2m6\",\n\t\"e2m7\",\n\t\"e3m1\",\n\t\"e3m2\",\n\t\"e3m3\",\n\t\"e3m4\",\n\t\"e3m5\",\n\t\"e3m6\",\n\t\"e3m7\",\n\t\"e4m1\",\n\t\"e4m2\",\n\t\"e4m3\",\n\t\"e4m4\",\n\t\"e4m5\",\n\t\"e4m6\",\n\t\"e4m7\",\n\t\"e4m8\",\n\t\"end\"\n};\nstatic const char *mapoptions_q1[] =\n{\n\t\"Start (Introduction)\",\n\t\"E1M1 (The Slipgate Complex)\",\n\t\"E1M2 (Castle Of The Damned)\",\n\t\"E1M3 (The Necropolis)\",\n\t\"E1M4 (The Grisly Grotto)\",\n\t\"E1M5 (Gloom Keep)\",\n\t\"E1M6 (The Door To Chthon)\",\n\t\"E1M7 (The House Of Chthon)\",\n\t\"E1M8 (Ziggarat Vertigo)\",\n\t\"E2M1 (The Installation)\",\n\t\"E2M2 (The Ogre Citadel)\",\n\t\"E2M3 (The Crypt Of Decay)\",\n\t\"E2M4 (The Ebon Fortress)\",\n\t\"E2M5 (The Wizard's Manse)\",\n\t\"E2M6 (The Dismal Oubliette\",\n\t\"E2M7 (The Underearth)\",\n\t\"E3M1 (Termination Central)\",\n\t\"E3M2 (The Vaults Of Zin)\",\n\t\"E3M3 (The Tomb Of Terror)\",\n\t\"E3M4 (Satan's Dark Delight)\",\n\t\"E3M5 (The Wind Tunnels)\",\n\t\"E3M6 (Chambers Of Torment)\",\n\t\"E3M7 (Tha Haunted Halls)\",\n\t\"E4M1 (The Sewage System)\",\n\t\"E4M2 (The Tower Of Despair)\",\n\t\"E4M3 (The Elder God Shrine)\",\n\t\"E4M4 (The Palace Of Hate)\",\n\t\"E4M5 (Hell's Atrium)\",\n\t\"E4M6 (The Pain Maze)\",\n\t\"E4M7 (Azure Agony)\",\n\t\"E4M8 (The Nameless City)\",\n\t\"End (Shub-Niggurath's Pit)\",\n\tNULL\n};\n\n#if defined(Q2CLIENT) && defined(Q2SERVER)\nstatic const char *maplist_q2[] =\n{\n\t\"base1\",\n\t\"base2\",\n\t\"base3\",\n\t\"train\",\n\t\"bunk1\",\n\t\"ware1\",\n\t\"ware2\",\n\t\"jail1\",\n\t\"jail2\",\n\t\"jail3\",\n\t\"jail4\",\n\t\"jail5\",\n\t\"security\",\n\t\"mintro\",\n\t\"mine1\",\n\t\"mine2\",\n\t\"mine3\",\n\t\"mine4\",\n\t\"fact1\",\n\t\"fact3\",\n\t\"fact2\",\n\t\"power1\",\n\t\"power2\",\n\t\"cool1\",\n\t\"waste1\",\n\t\"waste2\",\n\t\"waste3\",\n\t\"biggun\",\n\t\"hangar1\",\n\t\"space\",\n\t\"lab\",\n\t\"hangar2\",\n\t\"command\",\n\t\"strike\",\n\t\"city1\",\n\t\"city2\",\n\t\"city3\",\n\t\"boss1\",\n\t\"boss2\",\n};\nstatic const char *mapoptions_q2[] =\n{\n\t\"base1 (Unit 1 Base Unit: Outer Base)\",\n\t\"base2 (Unit 1 Base Unit: Installation)\",\n\t\"base3 (Unit 1 Base Unit: Comm Center)\",\n\t\"train (Unit 1 Base Unit: Lost Station)\",\n\t\"bunk1 (Unit 2 Warehouse Unit: Ammo Depot)\",\n\t\"ware1 (Unit 2 Warehouse Unit: Supply Station)\",\n\t\"ware2 (Unit 2 Warehouse Unit: Warehouse)\",\n\t\"jail1 (Unit 3 Jail Unit: Main Gate)\",\n\t\"jail2 (Unit 3 Jail Unit: Destination Center)\",\n\t\"jail3 (Unit 3 Jail Unit: Security Compex)\",\n\t\"jail4 (Unit 3 Jail Unit: Torture Chambers)\",\n\t\"jail5 (Unit 3 Jail Unit: Guard House)\",\n\t\"security (Unit 3 Jail Unit: Grid Control)\",\n\t\"mintro (Unit 4 Mine Unit: Mine Entrance)\",\n\t\"mine1 (Unit 4 Mine Unit: Upper Mines)\",\n\t\"mine2 (Unit 4 Mine Unit: Borehole)\",\n\t\"mine3 (Unit 4 Mine Unit: Drilling Area)\",\n\t\"mine4 (Unit 4 Mine Unit: Lower Mines)\",\n\t\"fact1 (Unit 5 Factory Unit: Receiving Center)\",\n\t\"fact3 (Unit 5 Factory Unit: Sudden Death)\",\n\t\"fact2 (Unit 5 Factory Unit: Processing Plant)\",\n\t\"power1 (Unit 6 Power Unit/Big Gun: Power Plant)\",\n\t\"power2 (Unit 6 Power Unit/Big Gun: The Reactor)\",\n\t\"cool1 (Unit 6 Power Unit/Big Gun: Cooling Facility)\",\n\t\"waste1 (Unit 6 Power Unit/Big Gun: Toxic Waste Dump)\",\n\t\"waste2 (Unit 6 Power Unit/Big Gun: Pumping Station 1)\",\n\t\"waste3 (Unit 6 Power Unit/Big Gun: Pumping Station 2)\",\n\t\"biggun (Unit 6 Power Unit/Big Gun: Big Gun)\",\n\t\"hangar1 (Unit 7 Hangar Unit: Outer Hangar)\",\n\t\"space (Unit 7 Hangar Unit: Comm Satelite)\",\n\t\"lab (Unit 7 Hangar Unit: Research Lab)\",\n\t\"hangar2 (Unit 7 Hangar Unit: Inner Hangar)\",\n\t\"command (Unit 7 Hangar Unit: Launch Command)\",\n\t\"strike (Unit 7 Hangar Unit: Outlands)\",\n\t\"city1 (Unit 8 City Unit: Outer Courts)\",\n\t\"city2 (Unit 8 City Unit: Lower Palace)\",\n\t\"city3 (Unit 8 City Unit: Upper Palace)\",\n\t\"boss1 (Unit 9 Boss Levels: Inner Chamber)\",\n\t\"boss2 (Unit 9 Boss Levels: Final Showdown)\",\n\tNULL\n};\n#endif\n#endif\n\nqboolean M_Apply_SP_Cheats (union menuoption_s *op,struct emenu_s *menu,int key)\n{\n\tsingleplayerinfo_t *info = menu->data;\n\n\tif (key != K_ENTER && key != K_KP_ENTER && key != K_GP_DIAMOND_CONFIRM && key != K_MOUSE1 && key != K_TOUCHTAP)\n\t\treturn false;\n\n\tswitch(info->skillcombo->selectedoption)\n\t{\n\tcase 0:\n\t\tCbuf_AddText(\"skill 0\\n\", RESTRICT_LOCAL);\n\t\tbreak;\n\tcase 1:\n\t\tCbuf_AddText(\"skill 1\\n\", RESTRICT_LOCAL);\n\t\tbreak;\n\tcase 2:\n\t\tCbuf_AddText(\"skill 2\\n\", RESTRICT_LOCAL);\n\t\tbreak;\n\tcase 3:\n\t\tCbuf_AddText(\"skill 3\\n\", RESTRICT_LOCAL);\n\t\tbreak;\n\t}\n\n#ifdef HAVE_SERVER\n\tif ((unsigned int)info->mapcombo->selectedoption < countof(maplist_q1)-1)\n\t\tCbuf_AddText(va(\"map %s\\n\", maplist_q1[info->mapcombo->selectedoption]), RESTRICT_LOCAL);\n#endif\n\n\tM_RemoveMenu(menu);\n\tCbuf_AddText(\"menu_spcheats\\n\", RESTRICT_LOCAL);\n\treturn true;\n}\n\n\nvoid M_Menu_Singleplayer_Cheats_Quake (void)\n{\n\t#ifdef HAVE_SERVER\n\tstatic const char *skilloptions[] =\n\t{\n\t\t\"Easy\",\n\t\t\"Normal\",\n\t\t\"Hard\",\n\t\t\"Nightmare\",\n\t\t\"None Set\",\n\t\tNULL\n\t};\n\tint currentskill;\n\tint currentmap;\n\textern cvar_t sv_gravity, sv_cheats, sv_maxspeed, skill;\n\t#endif\n\tsingleplayerinfo_t *info;\n\tint cursorpositionY;\n\tint y;\n\temenu_t *menu = M_Options_Title(&y, sizeof(*info));\n\tinfo = menu->data;\n\n\tcursorpositionY = (y + 24);\n\n\t#ifdef HAVE_SERVER\n\tif ( !*skill.string )\n\t\tcurrentskill = 4; // no skill selected\n\telse\n\t\tcurrentskill = skill.value;\n\n\tfor (currentmap = countof(maplist_q1); currentmap --> 0; )\n\t\tif (!strcmp(host_mapname.string, maplist_q1[currentmap]))\n\t\t\tbreak;\n\t/*anything that doesn't match will end up with 0*/\n\t#endif\n\n\tMC_AddRedText(menu, 16, 170, y, \t\t\t\"     Quake Singleplayer Cheats\", false); y+=8;\n\tMC_AddWhiteText(menu, 16, 170, y,\t\t\"     ^Ue080^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue082 \", false); y+=8;\n\ty+=8;\n\t#ifdef HAVE_SERVER\n\tinfo->skillcombo = MC_AddCombo(menu,16,170, y,\t\"Difficulty\", skilloptions, currentskill);\ty+=8;\n\tinfo->mapcombo = MC_AddCombo(menu,16,170, y,\t\"Map\", mapoptions_q1, currentmap);\ty+=8;\n\tMC_AddCheckBox(menu,\t16, 170, y,\t\t\"Cheats\", &sv_cheats,0);\ty+=8;\n\t#endif\n\t#ifdef TEXTEDITOR\n\tMC_AddCheckBox(menu,\t16, 170, y,\t\t\"Debugger\", &pr_debugger, 0); y+=8;\n\t#endif\n\tMC_AddConsoleCommand(menu, 16, 170, y,\t\"     Toggle Godmode\", \"god\\n\"); y+=8;\n\tMC_AddConsoleCommand(menu, 16, 170, y,\t\"     Toggle Flymode\", \"fly\\n\"); y+=8;\n\tMC_AddConsoleCommand(menu, 16, 170, y,\t\"      Toggle Noclip\", \"noclip\\n\"); y+=8;\n\tMC_AddConsoleCommand(menu, 16, 170, y,\t\"        Quad Damage\", \"impulse 255\\n\"); y+=8;\n\t#ifdef HAVE_SERVER\n\tMC_AddSlider(menu,\t16, 170, y,\t\t\t\"Gravity\", &sv_gravity,0,800,25);\ty+=8;\n\t#endif\n\tMC_AddSlider(menu,\t16, 170, y,\t\t\t\"Forward Speed\", &cl_forwardspeed,0,1000,50);\ty+=8;\n\tMC_AddSlider(menu,\t16, 170, y,\t\t\t\"Side Speed\", &cl_sidespeed,0,1000,50);\ty+=8;\n\tMC_AddSlider(menu,\t16, 170, y,\t\t\t\"Back Speed\", &cl_backspeed,0,1000,50);\ty+=8;\n\t#ifdef HAVE_SERVER\n\tMC_AddSlider(menu,\t16, 170, y,\t\t\t\"Max Movement Speed\", &sv_maxspeed,0,1000,50);\ty+=8;\n\t#endif\n\tMC_AddConsoleCommand(menu, 16, 170, y,\t\" Silver & Gold Keys\", \"impulse 13\\nimpulse 14\\n\"); y+=8;\n\tMC_AddConsoleCommand(menu, 16, 170, y,\t\"All Weapons & Items\", \"impulse 9\\n\"); y+=8;\n\tMC_AddConsoleCommand(menu, 16, 170, y,\t\"No Enemy Targetting\", \"notarget\\n\"); y+=8;\n\t#ifdef HAVE_SERVER\n\tMC_AddConsoleCommand(menu, 16, 170, y,   \"Restart Map\", \"restart\\n\"); y+=8;\n\t#else\n\tMC_AddConsoleCommand(menu, 16, 170, y,   \"Suicide\", \"kill\\n\"); y+=8;\n\t#endif\n\n\ty+=8;\n\tMC_AddCommand(menu,\t16, 170, y,\t\t\t\"Apply Changes\", M_Apply_SP_Cheats);\ty+=8;\n\n\tmenu->selecteditem = (union menuoption_s *)info->skillcombo;\n\tmenu->cursoritem = (menuoption_t*)MC_AddWhiteText(menu, 170, 0, cursorpositionY, NULL, false);\n}\n\n#ifdef Q2SERVER\n// Quake 2\n\ntypedef struct {\nmenucombo_t *skillcombo;\nmenucombo_t *mapcombo;\n} singleplayerq2info_t;\n\nqboolean M_Apply_SP_Cheats_Q2 (union menuoption_s *op,struct emenu_s *menu,int key)\n{\n\tsingleplayerq2info_t *info = menu->data;\n\n\tif (key != K_ENTER && key != K_KP_ENTER && key != K_GP_DIAMOND_CONFIRM && key != K_MOUSE1 && key != K_TOUCHTAP)\n\t\treturn false;\n\n\tswitch(info->skillcombo->selectedoption)\n\t{\n\tcase 0:\n\t\tCbuf_AddText(\"skill 0\\n\", RESTRICT_LOCAL);\n\t\tbreak;\n\tcase 1:\n\t\tCbuf_AddText(\"skill 1\\n\", RESTRICT_LOCAL);\n\t\tbreak;\n\tcase 2:\n\t\tCbuf_AddText(\"skill 2\\n\", RESTRICT_LOCAL);\n\t\tbreak;\n\t}\n\n\tif ((unsigned int)info->mapcombo->selectedoption < countof(maplist_q2)-1)\n\t\tCbuf_AddText(va(\"map %s\\n\", maplist_q2[info->mapcombo->selectedoption]), RESTRICT_LOCAL);\n\n\tM_RemoveMenu(menu);\n\tCbuf_AddText(\"menu_spcheats\\n\", RESTRICT_LOCAL);\n\treturn true;\n}\n\nvoid M_Menu_Singleplayer_Cheats_Quake2 (void)\n{\n\n\tstatic const char *skilloptions[] =\n\t{\n\t\t\"Easy\",\n\t\t\"Normal\",\n\t\t\"Hard\",\n\t\t\"None Set\",\n\t\tNULL\n\t};\n\n\tsingleplayerq2info_t *info;\n\tint cursorpositionY;\n\t#ifdef HAVE_SERVER\n\tint currentskill;\n\tint currentmap;\n\textern cvar_t sv_gravity, sv_cheats, sv_maxspeed, skill;\n\t#endif\n\tint y;\n\temenu_t *menu = M_Options_Title(&y, sizeof(*info));\n\tinfo = menu->data;\n\n\tcursorpositionY = (y + 24);\n\n\t#ifdef HAVE_SERVER\n\tif ( !*skill.string )\n\t\tcurrentskill = 3; // no skill selected\n\telse\n\t\tcurrentskill = skill.value;\n\n\tfor (currentmap = countof(maplist_q2); currentmap --> 0; )\n\t\tif (!Q_strcasecmp(host_mapname.string, maplist_q2[currentmap]))\n\t\t\tbreak;\n\t/*anything that doesn't match will end up with 0*/\n\t#endif\n\n\tMC_AddRedText(menu, 16, 170, y, \t\t\"Quake2 Singleplayer Cheats\", false); y+=8;\n\tMC_AddWhiteText(menu, 16, 170, y,\t\t\"^Ue080^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue082\", false); y+=8;\n\ty+=8;\n\t#ifdef HAVE_SERVER\n\tinfo->skillcombo = MC_AddCombo(menu,16,170, y,\t\"Difficulty\", skilloptions, currentskill);\ty+=8;\n\tinfo->mapcombo = MC_AddCombo(menu,16,170, y,\t\"Map\", mapoptions_q2, currentmap);\ty+=8;\n\tMC_AddCheckBox(menu,\t16, 170, y,\t\t\"Cheats\", &sv_cheats,0);\ty+=8;\n\t#endif\n\tMC_AddConsoleCommand(menu, 16, 170, y,\t\"Toggle Godmode\", \"god\\n\"); y+=8;\n\tMC_AddConsoleCommand(menu, 16, 170, y,\t\"Toggle Noclip\", \"noclip\\n\"); y+=8;\n\t#ifdef HAVE_SERVER\n\tMC_AddSlider(menu,\t16, 170, y,\t\t\t\"Gravity\", &sv_gravity,0,850,25);\ty+=8;\n\t#endif\n\tMC_AddSlider(menu,\t16, 170, y,\t\t\t\"Forward Speed\", &cl_forwardspeed,0,1000,50);\ty+=8;\n\tMC_AddSlider(menu,\t16, 170, y,\t\t\t\"Side Speed\", &cl_sidespeed,0,1000,50);\ty+=8;\n\tMC_AddSlider(menu,\t16, 170, y,\t\t\t\"Back Speed\", &cl_backspeed,0,1000,50);\ty+=8;\n\t#ifdef HAVE_SERVER\n\tMC_AddSlider(menu,\t16, 170, y,\t\t\t\"Max Movement Speed\", &sv_maxspeed,0,1000,50);\ty+=8;\n\t#endif\n\tMC_AddConsoleCommand(menu, 16, 170, y,\t\"Unlimited Ammo\", \"dmflags 8192\\n\"); y+=8;\n\tMC_AddConsoleCommand(menu, 16, 170, y,\t\"Quad Damage\", \"give quad damage\\n\"); y+=8;\n\tMC_AddConsoleCommand(menu, 16, 170, y,\t\"Blue & Red Key\", \"give blue key\\ngive red key\\n\"); y+=8;\n\tMC_AddConsoleCommand(menu, 16, 170, y,\t\"Pyramid Key\", \"give pyramid key\\n\"); y+=8;\n\tMC_AddConsoleCommand(menu, 16, 170, y,\t\"All Weapons & Items\", \"give all\\n\"); y+=8;\n\tMC_AddConsoleCommand(menu, 16, 170, y,\t\"Data Spinner\", \"give data spinner\\n\"); y+=8;\n\tMC_AddConsoleCommand(menu, 16, 170, y,\t\"Power Cube\", \"give power cube\\n\"); y+=8;\n\tMC_AddConsoleCommand(menu, 16, 170, y,\t\"Data CD\", \"give data cd\\n\"); y+=8;\n\tMC_AddConsoleCommand(menu, 16, 170, y,\t\"Ammo Pack\", \"give ammo pack\\n\"); y+=8;\n\tMC_AddConsoleCommand(menu, 16, 170, y,\t\"Bandolier\", \"give bandolier\\n\"); y+=8;\n\tMC_AddConsoleCommand(menu, 16, 170, y,\t\"Adrenaline\", \"give adrenaline\\n\"); y+=8;\n\tMC_AddConsoleCommand(menu, 16, 170, y,\t\"Ancient Head\", \"give ancient head\\n\"); y+=8;\n\tMC_AddConsoleCommand(menu, 16, 170, y,\t\"Environment Suit\", \"give environment suit\\n\"); y+=8;\n\tMC_AddConsoleCommand(menu, 16, 170, y,\t\"Rebreather\", \"give rebreather\\n\"); y+=8;\n\tMC_AddConsoleCommand(menu, 16, 170, y,\t\"Invulnerability\", \"give invulnerability\\n\"); y+=8;\n\tMC_AddConsoleCommand(menu, 16, 170, y,\t\"Silencer\", \"give silencer\\n\"); y+=8;\n\tMC_AddConsoleCommand(menu, 16, 170, y,\t\"Power Shield\", \"give power shield\\n\"); y+=8;\n\tMC_AddConsoleCommand(menu, 16, 170, y,\t\"Commander's Head\", \"give commander's head\\n\"); y+=8;\n\tMC_AddConsoleCommand(menu, 16, 170, y,\t\"Security Pass\", \"give security pass\\n\"); y+=8;\n\tMC_AddConsoleCommand(menu, 16, 170, y,\t\"Airstrike Marker\", \"give airstrike marker\\n\"); y+=8;\n\t#ifdef HAVE_SERVER\n\tMC_AddConsoleCommand(menu, 16, 170, y,   \"Restart Map\", va(\"restart\\n\")); y+=8;\n\t#endif\n\n\ty+=8;\n\tMC_AddCommand(menu,\t16, 170, y,\t\t\t\"Apply Changes\", M_Apply_SP_Cheats_Q2);\ty+=8;\n\n\tmenu->selecteditem = (union menuoption_s *)info->skillcombo;\n\tmenu->cursoritem = (menuoption_t*)MC_AddWhiteText(menu, 170, 0, cursorpositionY, NULL, false);\n}\n#endif\t// Quake 2\n\n#ifdef HEXEN2 // Hexen 2\ntypedef struct {\nmenucombo_t *skillcombo;\nmenucombo_t *mapcombo;\n} singleplayerh2info_t;\n\n#ifdef HAVE_SERVER\nstatic const char *maplist_h2[] =\n{\n\t\"demo1\",\n\t\"demo2\",\n\t\"demo3\",\n\t\"village1\",\n\t\"village3\",\n\t\"village2\",\n\t\"village4\",\n\t\"village5\",\n\t\"rider1a\",\n\t\"meso1\",\n\t\"meso2\",\n\t\"meso3\",\n\t\"meso4\",\n\t\"meso5\",\n\t\"meso6\",\n\t\"meso8\",\n\t\"meso9\",\n\t\"egypt1\",\n\t\"egypt2\",\n\t\"egypt3\",\n\t\"egypt4\",\n\t\"egypt5\",\n\t\"egypt6\",\n\t\"egypt7\",\n\t\"rider2c\",\n\t\"romeric1\",\n\t\"romeric2\",\n\t\"romeric3\",\n\t\"romeric4\",\n\t\"romeric5\",\n\t\"romeric6\",\n\t\"romeric7\",\n\t\"castle4\",\n\t\"castle5\",\n\t\"cath\",\n\t\"tower\",\n\t\"eidolon\",\n};\nstatic const char *mapoptions_h2[] =\n{\n\t\"demo1 (Blackmarsh: Hub 1 Blackmarsh)\",\n\t\"demo2 (Barbican: Hub 1 Blackmarsh)\",\n\t\"demo3 (The Mill: Hub 1 Blackmarsh)\",\n\t\"village1 (King's Court: Hub 1 Blackmarsh)\",\n\t\"village3 (Stables: Hub 1 Blackmarsh)\",\n\t\"village2 (Inner Courtyard: Hub 1 Blackmarsh)\",\n\t\"village4 (Palance Entrance: Hub 1 Blackmarsh)\",\n\t\"village5 (The Forgotten Chapel: Hub 1 Blackmarsh)\",\n\t\"rider1a (Famine's Domain: Hub 1 Blackmarsh)\",\n\t\"meso1 (Palance of Columns: Hub 2 Mazaera)\",\n\t\"meso2 (Plaza of the Sun: Hub 2 Mazaera)\",\n\t\"meso3 (Square of the Stream: Hub 2 Mazaera)\",\n\t\"meso4 (Tomb of the High Priest: Hub 2 Mazaera)\",\n\t\"meso5 (Obelisk of the Moon: Hub 2 Mazaera)\",\n\t\"meso6 (Court of 1000 Warriors: Hub 2 Mazaera)\",\n\t\"meso8 (Bridge of Stars: Hub 2 Mazaera)\",\n\t\"meso9 (Well of Souls: Hub 2 Mazaera)\",\n\t\"egypt1 (Temple of Horus: Hub 3 Thysis)\",\n\t\"egypt2 (Ancient Tempor of Nefertum: Hub 3 Thysis)\",\n\t\"egypt3 (Tempor of Nefertum: Hub 3 Thysis)\",\n\t\"egypt4 (Palace of the Pharaoh: Hub 3 Thysis\",\n\t\"egypt5 (Pyramid of Anubus: Hub 3 Thysis)\",\n\t\"egypt6 (Temple of Light: Hub 3 Thysis)\",\n\t\"egypt7 (Shrine of Naos: Hub 3 Thysis)\",\n\t\"rider2c (Pestilence's Lair: Hub 3 Thysis)\",\n\t\"romeric1 (The Hall of Heroes: Hub 4 Septimus)\",\n\t\"romeric2 (Gardens of Athena: Hub 4 Septimus)\",\n\t\"romeric3 (Forum of Zeus: Hub 4 Septimus)\",\n\t\"romeric4 (Baths of Demetrius: Hub 4 Septimus)\",\n\t\"romeric5 (Temple of Mars: Hub 4 Septimus)\",\n\t\"romeric6 (Coliseum of War: Hub 4 Septimus)\",\n\t\"romeric7 (Reflecting Pool: Hub 4 Septimus)\",\n\t\"castle4 (The Underhalls: Hub 5 Return to Blackmarsh)\",\n\t\"castle5 (Eidolon's Ordeal: Hub 5 Return to Blackmarsh)\",\n\t\"cath (Cathedral: Hub 5 Return to Blackmarsh)\",\n\t\"tower (Tower of the Dark Mage: Hub 5 Return to Blackmarsh)\",\n\t\"eidolon (Eidolon's Lair: Hub 5 Return to Blackmarsh)\",\n\tNULL\n};\n#endif\n\nqboolean M_Apply_SP_Cheats_H2 (union menuoption_s *op,struct emenu_s *menu,int key)\n{\n#ifdef HAVE_SERVER\n\tsingleplayerh2info_t *info = menu->data;\n#endif\n\n\tif (key != K_ENTER && key != K_KP_ENTER && key != K_GP_DIAMOND_CONFIRM && key != K_MOUSE1 && key != K_TOUCHTAP)\n\t\treturn false;\n\n#ifdef HAVE_SERVER\n\tswitch(info->skillcombo->selectedoption)\n\t{\n\tcase 0:\n\t\tCbuf_AddText(\"skill 0\\n\", RESTRICT_LOCAL);\n\t\tbreak;\n\tcase 1:\n\t\tCbuf_AddText(\"skill 1\\n\", RESTRICT_LOCAL);\n\t\tbreak;\n\tcase 2:\n\t\tCbuf_AddText(\"skill 2\\n\", RESTRICT_LOCAL);\n\t\tbreak;\n\tcase 3:\n\t\tCbuf_AddText(\"skill 3\\n\", RESTRICT_LOCAL);\n\t\tbreak;\n\t}\n\n\tif ((unsigned)info->mapcombo->selectedoption < countof(maplist_h2))\n\t\tCbuf_AddText(va(\"map %s\\n\", maplist_h2[info->mapcombo->selectedoption]), RESTRICT_LOCAL);\n#endif\n\n\tM_RemoveMenu(menu);\n\tCbuf_AddText(\"menu_spcheats\\n\", RESTRICT_LOCAL);\n\treturn true;\n}\n\n\nvoid M_Menu_Singleplayer_Cheats_Hexen2 (void)\n{\n\tsingleplayerh2info_t *info;\n\tint cursorpositionY;\n\t#ifdef HAVE_SERVER\n\t\tint currentmap;\n\t\tint currentskill;\n\t\tstatic const char *skilloptions[] =\n\t\t{\n\t\t\t\"Easy\",\n\t\t\t\"Normal\",\n\t\t\t\"Hard\",\n\t\t\t\"Nightmare\",\n\t\t\t\"None Set\",\n\t\t\tNULL\n\t\t};\n\t\textern cvar_t sv_gravity, sv_cheats, sv_maxspeed, skill;\n\t#endif\n\tint y;\n\temenu_t *menu = M_Options_Title(&y, sizeof(*info));\n\tinfo = menu->data;\n\n\tcursorpositionY = (y + 24);\n\n\t#ifdef HAVE_SERVER\n\tif ( !*skill.string )\n\t\tcurrentskill = 4; // no skill selected\n\telse\n\t\tcurrentskill = skill.value;\n\n\tfor (currentmap = countof(maplist_h2); currentmap --> 0; )\n\t\tif (!Q_strcasecmp(host_mapname.string, maplist_h2[currentmap]))\n\t\t\tbreak;\n\t#endif\n\n\tMC_AddRedText(menu, 16, 170, y, \t\t\"Hexen2 Singleplayer Cheats\", false); y+=8;\n\tMC_AddWhiteText(menu, 16, 170, y,\t\t\"^Ue080^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue082 \", false); y+=8;\n\ty+=8;\n\t#ifdef HAVE_SERVER\n\tinfo->skillcombo = MC_AddCombo(menu,16,170, y,\t\"Difficulty\", skilloptions, currentskill);\ty+=8;\n\tinfo->mapcombo = MC_AddCombo(menu,16,170, y,\t\"Map\", mapoptions_h2, currentmap);\ty+=8;\n\t#endif\n\t#ifdef HAVE_SERVER\n\tMC_AddCheckBox(menu,\t16, 170, y,\t\t\"Cheats\", &sv_cheats,0);\ty+=8;\n\t#endif\n\tMC_AddConsoleCommand(menu, 16, 170, y,\t\"Toggle Godmode\", \"god\\n\"); y+=8;\n\tMC_AddConsoleCommand(menu, 16, 170, y,\t\"Toggle Flymode\", \"fly\\n\"); y+=8;\n\tMC_AddConsoleCommand(menu, 16, 170, y,\t\"Toggle Noclip\", \"noclip\\n\"); y+=8;\n\t#ifdef HAVE_SERVER\n\tMC_AddSlider(menu,\t16, 170, y,\t\t\t\"Gravity\", &sv_gravity,0,800,25);\ty+=8;\n\t#endif\n\tMC_AddSlider(menu,\t16, 170, y,\t\t\t\"Forward Speed\", &cl_forwardspeed,0,1000,50);\ty+=8;\n\tMC_AddSlider(menu,\t16, 170, y,\t\t\t\"Side Speed\", &cl_sidespeed,0,1000,50);\ty+=8;\n\tMC_AddSlider(menu,\t16, 170, y,\t\t\t\"Back Speed\", &cl_backspeed,0,1000,50);\ty+=8;\n\t#ifdef HAVE_SERVER\n\tMC_AddSlider(menu,\t16, 170, y,\t\t\t\"Max Movement Speed\", &sv_maxspeed,0,1000,50);\ty+=8;\n\t#endif\n\tMC_AddConsoleCommand(menu, 16, 170, y,\t\"Sheep Transformation\", \"impulse 14\\n\"); y+=8;\n\tMC_AddConsoleCommand(menu, 16, 170, y,\t\"Change To Paladin (lvl3+)\", \"impulse 171\\n\"); y+=8;\n\tMC_AddConsoleCommand(menu, 16, 170, y,\t\"Change To Crusader (lvl3+)\", \"impulse 172\\n\"); y+=8;\n\tMC_AddConsoleCommand(menu, 16, 170, y,\t\"Change to Necromancer (lvl3+)\", \"impulse 173\\n\"); y+=8;\n\tMC_AddConsoleCommand(menu, 16, 170, y,\t\"Change to Assassin (lvl3+)\", \"impulse 174\\n\"); y+=8;\n\t//demoness?\n\tMC_AddConsoleCommand(menu, 16, 170, y,\t\"Remove Monsters\", \"impulse 35\\n\"); y+=8;\n\tMC_AddConsoleCommand(menu, 16, 170, y,\t\"Freeze Monsters\", \"impulse 36\\n\"); y+=8;\n\tMC_AddConsoleCommand(menu, 16, 170, y,\t\"Unfreeze Monsters\", \"impulse 37\\n\"); y+=8;\n\tMC_AddConsoleCommand(menu, 16, 170, y,\t\"Increase Level By 1\", \"impulse 40\\n\"); y+=8;\n\tMC_AddConsoleCommand(menu, 16, 170, y,\t\"Increase Experience\", \"impulse 41\\n\"); y+=8;\n\tMC_AddConsoleCommand(menu, 16, 170, y,\t\"Display Co-ordinates\", \"impulse 42\\n\"); y+=8;\n\tMC_AddConsoleCommand(menu, 16, 170, y,\t\"All Weapons & Mana\", \"impulse 9\\n\"); y+=8;\n\tMC_AddConsoleCommand(menu, 16, 170, y,\t\"All Weapons & Mana & Items\", \"impulse 43\\n\"); y+=8;\n\tMC_AddConsoleCommand(menu, 16, 170, y,\t\"No Enemy Targetting\", \"notarget\\n\"); y+=8;\n\tMC_AddConsoleCommand(menu, 16, 170, y,\t\"Enable Crosshair\", \"crosshair 1\\n\"); y+=8;\n\tMC_AddConsoleCommand(menu, 16, 170, y,\t\"20 Of Each Artifact\", \"impulse 299\\n\"); y+=8;\n\tMC_AddConsoleCommand(menu, 16, 170, y,  \"Restart Map\", \"impulse 99\\n\"); y+=8;\n\n\ty+=8;\n\tMC_AddCommand(menu,\t16, 170, y,\t\t\t\"Apply Changes\", M_Apply_SP_Cheats_H2);\ty+=8;\n\n\tmenu->selecteditem = (union menuoption_s *)info->skillcombo;\n\tmenu->cursoritem = (menuoption_t*)MC_AddWhiteText(menu, 250, 0, cursorpositionY, NULL, false);\n}\n#endif\n\nvoid M_Menu_Singleplayer_Cheats_f (void)\n{\n\tswitch(M_GameType())\n\t{\n\tcase MGT_QUAKE1:\n\t\tM_Menu_Singleplayer_Cheats_Quake();\n\t\tbreak;\n#ifdef Q2SERVER\n\tcase MGT_QUAKE2:\n\t\tM_Menu_Singleplayer_Cheats_Quake2();\n\t\tbreak;\n#endif\n#ifdef HEXEN2\n\tcase MGT_HEXEN2:\n\t\tM_Menu_Singleplayer_Cheats_Hexen2();\n\t\tbreak;\n#endif\n\t}\n}\n\n// video mode options\n#if defined(D3DQUAKE) && defined(GLQUAKE)\n#define MULTIRENDERER // allow options for selecting renderer\n#endif\n\ntypedef struct {\n\tmenucombo_t *dispmode;\n\tmenucombo_t *resmode;\n\tmenuedit_t *width;\n\tmenuedit_t *height;\n\tmenuedit_t *bpp;\n\tmenuedit_t *hz;\n\tmenucombo_t *bppfixed;\n\tmenucombo_t *hzfixed;\n\tmenucombo_t *res2dmode;\n\tmenucombo_t *scale;\n\tmenuedit_t *width2d;\n\tmenuedit_t *height2d;\n\tmenucombo_t *ressize[ASPECT_RATIOS];\n\tmenucombo_t *res2dsize[ASPECT_RATIOS];\n} videomenuinfo_t;\n\nvoid CheckCustomMode(struct emenu_s *menu)\n{\n\tint i, sel;\n\tvideomenuinfo_t *info = (videomenuinfo_t*)menu->data;\n\n\t// hide all display controls\n\tinfo->resmode->common.ishidden = true;\n\tinfo->width->common.ishidden = true;\n\tinfo->height->common.ishidden = true;\n\tinfo->bpp->common.ishidden = true;\n\tinfo->hz->common.ishidden = true;\n\tinfo->bppfixed->common.ishidden = true;\n\tinfo->hzfixed->common.ishidden = true;\n\tfor (i = 0; i < ASPECT_RATIOS; i++)\n\t\tinfo->ressize[i]->common.ishidden = true;\n\tif (!info->dispmode || info->dispmode->selectedoption != 2)\n\t{\n\t\tinfo->resmode->common.ishidden = false;\n\t\tsel = info->resmode->selectedoption;\n\t\tif (sel == ASPECT3D_CUSTOM)\n\t\t{ // unhide custom entries for custom option\n\t\t\tinfo->width->common.ishidden = false;\n\t\t\tinfo->height->common.ishidden = false;\n\t\t\tinfo->bpp->common.ishidden = false;\n\t\t\tinfo->hz->common.ishidden = false;\n\t\t}\n\t\telse if (sel != ASPECT3D_DESKTOP)\n\t\t{\n\t\t\t// unhide appropriate aspect ratio combo and restricted bpp/hz combos\n\t\t\tinfo->bppfixed->common.ishidden = false;\n\t\t\tinfo->hzfixed->common.ishidden = false;\n\t\t\tinfo->ressize[sel]->common.ishidden = false;\n\t\t}\n\t}\n\t// hide all 2d display controls\n\tinfo->width2d->common.ishidden = true;\n\tinfo->height2d->common.ishidden = true;\n\tinfo->scale->common.ishidden = true;\n\tfor (i = 0; i < ASPECT_RATIOS; i++)\n\t\tinfo->res2dsize[i]->common.ishidden = true;\n\tsel = info->res2dmode->selectedoption;\n\tif (sel == ASPECT2D_SCALED) // unhide scale option\n\t\tinfo->scale->common.ishidden = false;\n\telse if (sel == ASPECT2D_HEIGHT) // unhide custom entries for height-only option\n\t\tinfo->height2d->common.ishidden = false;\n\telse if (sel == ASPECT2D_CUSTOM) // unhide custom entries for custom option\n\t{\n\t\tinfo->width2d->common.ishidden = false;\n\t\tinfo->height2d->common.ishidden = false;\n\t}\n\telse if (sel != ASPECT2D_AUTO) // unhide appropriate aspect ratio combo\n\t\tinfo->res2dsize[sel]->common.ishidden = false;\n}\n\n//return value is aspect group, *outres is the mode index inside that aspect.\nstatic int M_MatchModes(int width, int height, int *outres)\n{\n\tint i;\n\tint ratio = -1;\n\n\t// find closest resolution for each ratio\n\tfor (i = 0; i < ASPECT_RATIOS; i++)\n\t{\n\t\tconst char **v = resaspects[i];\n\t\toutres[i] = 0;\n\t\t// search through each string in ratio array\n\t\twhile (*v)\n\t\t{\n\t\t\tconst char *c = *v;\n\t\t\tint w = atoi(c);\n\t\t\tif (width <= w)\n\t\t\t{\n\t\t\t\tif (width == w)\n\t\t\t\t{\n\t\t\t\t\t// if we match height as well we have a direct resolution match\n\t\t\t\t\t// so record ratio index\n\t\t\t\t\tconst char *s = strchr(c, 'x');\n\t\t\t\t\tif (s)\n\t\t\t\t\t{\n\t\t\t\t\t\tint h = atoi(s + 1);\n\t\t\t\t\t\tif (height == h)\n\t\t\t\t\t\t\tratio = i;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\toutres[i]++;\n\t\t\tv++;\n\t\t}\n\t}\n\n\treturn ratio;\n}\n\nqboolean M_VideoApply (union menuoption_s *op, struct emenu_s *menu, int key)\n{\n\textern cvar_t vid_desktopsettings;\n\tvideomenuinfo_t *info = (videomenuinfo_t*)menu->data;\n\n\tif (key != K_ENTER && key != K_KP_ENTER && key != K_GP_DIAMOND_CONFIRM && key != K_MOUSE1 && key != K_TOUCHTAP)\n\t\treturn false;\n\n\t// force update display options\n\t{\n\t\tint w = 0, h = 0;\n\t\tconst char *wc = NULL;\n\t\tconst char *hc = NULL;\n\t\tconst char *bc = \"32\";\n\t\tconst char *fc = \"0\";\n\t\tconst char *dc = \"0\";\n\n\t\tswitch (info->resmode->selectedoption)\n\t\t{\n\t\tcase ASPECT3D_DESKTOP: // Desktop\n\t\t\tdc = \"1\";\n\t\t\tbreak;\n\t\tcase ASPECT3D_CUSTOM: // Custom\n\t\t\twc = info->width->text;\n\t\t\thc = info->height->text;\n\t\t\tbc = info->bpp->text;\n\t\t\tfc = info->hz->text;\n\t\t\tbreak;\n\t\tdefault: // Aspects\n\t\t\t{\n\t\t\t\tmenucombo_t *c = info->ressize[info->resmode->selectedoption];\n\t\t\t\tconst char *res = c->options[c->selectedoption];\n\t\t\t\tconst char *x = strchr(res, 'x');\n\n\t\t\t\tw = atoi(res);\n\t\t\t\th = atoi(x + 1);\n\n\t\t\t\tbc = info->bppfixed->values[info->bppfixed->selectedoption];\n\t\t\t\tfc = info->hzfixed->values[info->hzfixed->selectedoption];\n\t\t\t}\n\t\t}\n\n\t\tif (!wc)\n\t\t\tCvar_SetValue(info->width->cvar, w);\n\t\telse\n\t\t\tCvar_Set(info->width->cvar, wc);\n\t\tif (!hc)\n\t\t\tCvar_SetValue(info->height->cvar, h);\n\t\telse\n\t\t\tCvar_Set(info->height->cvar, hc);\n\t\tCvar_Set(info->bpp->cvar, bc);\n\t\tCvar_Set(info->hz->cvar, fc);\n\t\tCvar_Set(&vid_desktopsettings, dc);\n\t}\n\n\t// force update 2d options\n\t{\n\t\tint w = 0, h = 0;\n\t\tconst char *wc = NULL;\n\t\tconst char *hc = NULL;\n\t\tconst char *sc = \"0\";\n\n\t\tswitch (info->res2dmode->selectedoption)\n\t\t{\n\t\tcase ASPECT_RATIOS: // Default\n\t\t\tbreak;\n\t\tcase ASPECT2D_SCALED: // Scale\n\t\t\tsc = info->scale->values[info->scale->selectedoption];\n\t\t\tbreak;\n\t\tcase ASPECT2D_HEIGHT:\n\t\t\twc = \"0\";\n\t\t\thc = info->height2d->text;\n\t\t\tbreak;\n\t\tcase ASPECT2D_CUSTOM: // Custom\n\t\t\twc = info->width2d->text;\n\t\t\thc = info->height2d->text;\n\t\t\tbreak;\n\t\tdefault: // Aspects\n\t\t\t{\n\t\t\t\tmenucombo_t *c = info->res2dsize[info->res2dmode->selectedoption];\n\t\t\t\tconst char *res = c->options[c->selectedoption];\n\t\t\t\tconst char *x = strchr(res, 'x');\n\n\t\t\t\tw = atoi(res);\n\t\t\t\th = atoi(x + 1);\n\t\t\t}\n\t\t}\n\n\t\tif (!wc)\n\t\t\tCvar_SetValue(info->width2d->cvar, w);\n\t\telse\n\t\t\tCvar_Set(info->width2d->cvar, wc);\n\t\tif (!hc)\n\t\t\tCvar_SetValue(info->height2d->cvar, h);\n\t\telse\n\t\t\tCvar_Set(info->height2d->cvar, hc);\n\t\tCvar_Set(info->scale->cvar, sc);\n\t}\n\n\t// restart video to apply latched cvars\n\tM_RemoveMenu(menu);\n\tCbuf_AddText(\"vid_restart\\nmenu_video\\n\", RESTRICT_LOCAL);\n\treturn true;\n}\n\nvoid M_Menu_Video_f (void)\n{\n\textern cvar_t v_contrast, vid_conwidth, vid_conheight;\n//\textern cvar_t vid_width, vid_height, vid_preservegamma, vid_hardwaregamma, vid_desktopgamma;\n\textern cvar_t vid_desktopsettings, vid_conautoscale;\n\textern cvar_t vid_bpp, vid_refreshrate, vid_multisample, vid_srgb;\n\n\tstatic const char *gammamodeopts[] = {\n\t\t\"Off\",\n\t\t\"Auto\",\n\t\t\"GLSL\",\n\t\t\"Hardware\",\n\t\t\"Scene-Only\",\n\t\tNULL\n\t};\n\tstatic const char *gammamodevalues[] = {\n\t\t\"0\",\n\t\t\"1\",\n\t\t\"2\",\n\t\t\"3\",\n\t\t\"4\",\n\t\tNULL\n\t};\n\n\tstatic const char *srgbopts[] = {\n\t\t\"Non-Linear\",\t//0 (legacy buggy linear->srgb non-transforms)\n\t\t\"sRGB-Aware\",\t//1 (linear->srgb transforms)\n\t\t\"Linear (HDR)\",\t//2 (try to use a float framebuffer, otherwise fall back on srgb framebuffer)\n\t\t\"Linearised\",\t//-1\n\t\tNULL\n\t};\n\tstatic const char *srgbvalues[] = { \"0\", \"1\", \"2\", \"-1\", NULL};\n\n\n#if defined(ANDROID) && !defined(FTE_SDL)\n\textern cvar_t sys_orientation;\n\tstatic const char *orientationopts[] = {\n\t\t\"Auto\",\n\t\t\"Landscape\",\n\t\t\"Portrait\",\n\t\t\"Reverse Landscape\",\n\t\t\"Reverse Portrait\",\n\t\tNULL\n\t};\n\tstatic const char *orientationvalues[] = {\n\t\t\"\",\n\t\t\"landscape\",\n\t\t\"portrait\",\n\t\t\"reverselandscape\",\n\t\t\"reverseportrait\",\n\t\tNULL\n\t};\n#else\n\textern cvar_t vid_fullscreen;\n\tstatic const char *fullscreenopts[] = {\n\t\t\"Windowed\",\n\t\t\"Fullscreen\",\n\t\t\"Borderless Windowed\",\n\t\tNULL\n\t};\n\tstatic const char *fullscreenvalues[] = {\"0\", \"1\", \"2\", NULL};\n#endif\n\textern cvar_t vid_renderer;\n\tstatic const char *rendererops[] =\n\t{\n#ifdef GLQUAKE\n\t\t\"OpenGL\",\n#endif\n#ifdef VKQUAKE\n\t\t\"Vulkan\",\n#endif\n#ifdef D3D8QUAKE\n\t\t\"Direct3D 8 (limited)\",\n#endif\n#ifdef D3D9QUAKE\n\t\t\"Direct3D 9 (limited)\",\n#endif\n#ifdef D3D11QUAKE\n\t\t\"Direct3D 11 Hardware (Experimental)\",\n\t\t\"Direct3D 11 Software (Experimental)\",\n#endif\n#ifdef SWQUAKE\n\t\t\"Software Rendering (unusable)\",\n#endif\n#if defined(_WIN32) && !defined(CLIENTONLY)\t//win32 because other systems probably won't display a console if started via some shortcut\n\t\t\"Dedicated Server\",\n#endif\n#if 0\n\t\t\"Headless\",\n#endif\n\t\tNULL\n\t};\n\tstatic const char *renderervalues[] =\n\t{\n#ifdef GLQUAKE\n\t\t\"gl\",\n#endif\n#ifdef VKQUAKE\n\t\t\"vk\",\n#endif\n#ifdef D3D8QUAKE\n\t\t\"d3d8\",\n#endif\n#ifdef D3D9QUAKE\n\t\t\"d3d9\",\n#endif\n#ifdef D3D11QUAKE\n\t\t\"d3d11\",\n\t\t\"d3d11 warp\",\n#endif\n#ifdef SWQUAKE\n\t\t\"sw\",\n#endif\n#if defined(_WIN32) && !defined(CLIENTONLY)\n\t\t\"sv\",\n#endif\n#if 0\n\t\t\"headless\",\n#endif\n\t\tNULL\n\t};\n\n\tstatic const char *aaopts[] = {\n\t\t\"1x\",\n\t\t\"2x\",\n\t\t\"4x\",\n\t\t\"6x\",\n\t\t\"8x\",\n\t\tNULL\n\t};\n\tstatic const char *aavalues[] = {\"0\", \"2\", \"4\", \"6\", \"8\", NULL};\n\n\tstatic const char *resmodeopts[] = {\n\t\tASPECT_LIST\n\t\t\"Desktop\",\n\t\t\"Custom\",\n\t\tNULL\n\t};\n\n\tstatic const char *bppopts[] =\n\t{\n\t\t\"16-bit\",\n\t\t\"24-bit\",\n\t\tNULL\n\t};\n\tstatic const char *bppvalues[] = {\"16\", \"24\", NULL};\n#if defined(_WIN32) && !defined(FTE_SDL)\n\textern int qwinvermaj, qwinvermin;\n\t//on win8+, hide the 16bpp option - windows would just reject it.\n\tint bppbias = ((qwinvermaj == 6 && qwinvermin >= 2) || qwinvermaj>6)?1:0;\n#else\n\tconst int bppbias = 0;\n#endif\n\n\tstatic const char *refreshopts[] =\n\t{\n\t\t\"Default\",\n\t\t\"59Hz\",\n\t\t\"60Hz\",\n\t\t\"70Hz\",\n\t\t\"72Hz\",\n\t\t\"75Hz\",\n\t\t\"85Hz\",\n\t\t\"100Hz\",\n\t\t\"120Hz\",\n\t\t\"144Hz\",\n\t\tNULL\n\t};\n\tstatic const char *refreshvalues[] = {\"\", \"59\", \"60\", \"70\", \"72\", \"75\", \"85\", \"100\", \"120\", \"144\", NULL};\n\n\tstatic const char *res2dmodeopts[] = {\n\t\tASPECT_LIST\n\t\t\"Default\",\n\t\t\"Square (by height)\",\n\t\t\"Scale\",\n\t\t\"Custom\",\n\t\tNULL\n\t};\n\n\tstatic const char *scaleopts[] = {\n\t\t\"1x\",\n\t\t\"1.5x\",\n\t\t\"2x\",\n\t\t\"2.5x\",\n\t\t\"3x\",\n\t\t\"4x\",\n\t\t\"5x\",\n\t\t\"6x\",\n\t\tNULL\n\t};\n\tstatic const char *scalevalues[] = { \"1\", \"1.5\", \"2\", \"2.5\", \"3\", \"4\", \"5\", \"6\", NULL};\n\n\tstatic const char *vsyncopts[] =\n\t{\n\t\t\"Off\",\n\t\t\"Strict\",\n\t\t\"Lax\",\n\t\t\"Alternate Frames\",\n\t\tNULL\n\t};\n\tstatic const char *vsyncvalues[] = { \"0\", \"1\", \"-1\", \"2\", NULL};\n\textern cvar_t vid_vsync;\n\textern cvar_t cl_maxfps;\n\textern cvar_t cl_yieldcpu;\n\n/*\n\tstatic const char *vsyncoptions[] =\n\t{\n\t\t\"Off\",\n\t\t\"Wait for Vertical Sync\",\n\t\t\"Wait for Display Enable\",\n\t\tNULL\n\t};\n\textern cvar_t vid_vsync;\n*/\n\tvideomenuinfo_t *info;\n\tstatic char current3dres[32]; // enough to fit 1920x1200\n\n\tstatic menuresel_t resel;\n\tint y;\n\tint resmodechoice, res2dmodechoice;\n\tint reschoices[ASPECT_RATIOS], res2dchoices[ASPECT_RATIOS];\n\temenu_t *menu;\n\n\t//not calling M_Options_Title because of quake2's different banner.\n\ty = 32;\n\tmenu = M_CreateMenu(sizeof(videomenuinfo_t));\n\tswitch(M_GameType())\n\t{\n\tcase MGT_QUAKE2:\t//q2...\n\t\tMC_AddCenterPicture(menu, 4, 24, \"pics/m_banner_video\");\n\t\tbreak;\n#ifdef HEXEN2\n\tcase MGT_HEXEN2://h2\n\t\tMC_AddPicture(menu, 16, 0, 35, 176, \"gfx/menu/hplaque.lmp\");\n\t\tMC_AddCenterPicture(menu, 0, 60, \"gfx/menu/title3.lmp\");\n\t\ty += 32;\n\t\tbreak;\n#endif\n\tdefault: //q1\n\t\tMC_AddPicture(menu, 16, 4, 32, 144, \"gfx/qplaque.lmp\");\n\t\tMC_AddCenterPicture(menu, 4, 24, \"gfx/p_option.lmp\");\n\t\tbreak;\n\t}\n\n\tinfo = (videomenuinfo_t*)menu->data;\n\n\tsnprintf(current3dres, sizeof(current3dres), \"Current: %ix%i\", vid.pixelwidth, vid.pixelheight);\n\tresmodechoice = M_MatchModes(vid.pixelwidth, vid.pixelheight, reschoices);\n\tif (vid_desktopsettings.ival)\n\t\tresmodechoice = ASPECT3D_DESKTOP;\n\telse if (resmodechoice < 0)\n\t\tresmodechoice = ASPECT3D_CUSTOM;\n\tres2dmodechoice = M_MatchModes(vid.pixelwidth, vid.pixelheight, res2dchoices);\n\tif (vid_conautoscale.value > 0)\n\t\tres2dmodechoice = ASPECT2D_SCALED;\n\telse if (!vid_conwidth.ival && !vid_conheight.ival)\n\t\tres2dmodechoice = ASPECT2D_AUTO;\n\telse if (!vid_conwidth.ival)\n\t\tres2dmodechoice = ASPECT2D_HEIGHT;\n\telse if (res2dmodechoice < 0)\n\t\tres2dmodechoice = ASPECT2D_CUSTOM;\n\n\t{\n\t\tmenubulk_t bulk[] =\n\t\t{\n\t\t\tMB_REDTEXT(\"Video Options\", true),\n\t\t\tMB_TEXT(\"^Ue080^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue082\", true),\n\t\t\tMB_CMD(\"Apply Settings\", M_VideoApply, \"Restart video and apply renderer, display, and 2D resolution options.\"),\n\t\t\tMB_SPACING(4),\n\t\t\tMB_COMBOCVAR(\"Renderer\", vid_renderer, rendererops, renderervalues, NULL),\n#if defined(ANDROID) && !defined(FTE_SDL)\n\t\t\tMB_COMBOCVAR(\"Orientation\", sys_orientation, orientationopts, orientationvalues, NULL),\n#else\n\t\t\tMB_COMBOCVARRETURN(\"Display Mode\", vid_fullscreen, fullscreenopts, fullscreenvalues, info->dispmode, vid_fullscreen.description),\n#endif\n\t\t\tMB_COMBOCVAR(\"MSAA\", vid_multisample, aaopts, aavalues, NULL),\n\t\t\tMB_REDTEXT(current3dres, true),\n\t\t\tMB_COMBORETURN(\"Aspect\", resmodeopts, resmodechoice, info->resmode, \"Select method for determining or configuring display options. The desktop option will attempt to use the width, height, color depth, and refresh from your operating system's desktop environment.\"),\n\t\t\t// aspect entries\n\t\t\tMB_COMBORETURN(\"Size\", resaspects[0], reschoices[0], info->ressize[0], \"Select resolution for display.\"),\n\t\t\tMB_SPACING(-8),\n\t\t\tMB_COMBORETURN(\"Size\", resaspects[1], reschoices[1], info->ressize[1], \"Select resolution for display.\"),\n\t\t\tMB_SPACING(-8),\n\t\t\tMB_COMBORETURN(\"Size\", resaspects[2], reschoices[2], info->ressize[2], \"Select resolution for display.\"),\n\t\t\tMB_SPACING(-8),\n\t\t\tMB_COMBORETURN(\"Size\", resaspects[3], reschoices[3], info->ressize[3], \"Select resolution for display.\"),\n\t\t\tMB_COMBOCVARRETURN(\"Color Depth\", vid_bpp, bppopts+bppbias, bppvalues+bppbias, info->bppfixed, vid_bpp.description),\n\t\t\tMB_COMBOCVARRETURN(\"Refresh Rate\", vid_refreshrate, refreshopts, refreshvalues, info->hzfixed, vid_refreshrate.description),\n\t\t\tMB_SPACING(-24), // really hacky...\n\t\t\t// custom entries\n\t\t\tMB_EDITCVARSLIMRETURN(\"Width\", \"vid_width\", info->width),\n\t\t\tMB_EDITCVARSLIMRETURN(\"Height\", \"vid_height\", info->height),\n\t\t\tMB_EDITCVARSLIMRETURN(\"Color Depth\", \"vid_bpp\", info->bpp),\n\t\t\tMB_EDITCVARSLIMRETURN(\"Refresh Rate\", \"vid_displayfrequency\", info->hz),\n\n//\t\t\tMB_SPACING(4),\n\t\t\tMB_COMBORETURN(\"2D Mode\", res2dmodeopts, res2dmodechoice, info->res2dmode, \"Select method for determining or configuring 2D resolution and scaling. The default option matches the current display resolution, and the scale option scales by a factor of the display resolution.\"),\n\t\t\t// scale entry\n\t\t\tMB_COMBOCVARRETURN(\"Amount\", vid_conautoscale, scaleopts, scalevalues, info->scale, NULL),\n\t\t\tMB_SPACING(-8),\n\t\t\t// 2d aspect entries\n\t\t\tMB_COMBORETURN(\"Size\", resaspects[0], res2dchoices[0], info->res2dsize[0], \"Select resolution for 2D rendering.\"),\n\t\t\tMB_SPACING(-8),\n\t\t\tMB_COMBORETURN(\"Size\", resaspects[1], res2dchoices[1], info->res2dsize[1], \"Select resolution for 2D rendering.\"),\n\t\t\tMB_SPACING(-8),\n\t\t\tMB_COMBORETURN(\"Size\", resaspects[2], res2dchoices[2], info->res2dsize[2], \"Select resolution for 2D rendering.\"),\n\t\t\tMB_SPACING(-8),\n\t\t\tMB_COMBORETURN(\"Size\", resaspects[3], res2dchoices[3], info->res2dsize[3], \"Select resolution for 2D rendering.\"),\n\t\t\tMB_SPACING(-8),\n\t\t\t// 2d custom entries\n\t\t\tMB_EDITCVARSLIMRETURN(\"Width\", \"vid_conwidth\", info->width2d),\n\t\t\tMB_EDITCVARSLIMRETURN(\"Height\", \"vid_conheight\", info->height2d),\n\t\t\tMB_SPACING(4),\n\t\t\tMB_COMBOCVAR(\"sRGB\", vid_srgb, srgbopts, srgbvalues, \"Controls the colour space to try to use.\"),\n\t\t\tMB_COMBOCVAR(\"Gamma Mode\", vid_hardwaregamma, gammamodeopts, gammamodevalues, \"Controls how gamma is applied\"),\n\t\t\tMB_SLIDER(\"Gamma\", v_gamma, 1.5, 0.25, -0.05, NULL),\n\t\t\tMB_SLIDER(\"Contrast\", v_contrast, 0.8, 3, 0.05, NULL),\n\t\t\tMB_SLIDER(\"Brightness\", v_brightness, 0.0, 0.5, 0.05, NULL),\n\t\t\tMB_SPACING(4),\n\t\t\tMB_SLIDER(\"View Size\", scr_viewsize, 30, 120, 10, NULL),\n\t\t\tMB_COMBOCVAR(\"VSync\", vid_vsync, vsyncopts, vsyncvalues, \"Controls whether to wait for rendering to finish.\"),\n//\t\t\tMB_EDITCVARSLIM(\"Framerate Limiter\", cl_maxfps.name, \"Limits the maximum framerate. Set to 0 for none.\"),\n//\t\t\tMB_CHECKBOXCVARTIP(\"Yield CPU\", cl_yieldcpu, 1, \"Reduce CPU usage between frames.\\nShould probably be off when using vsync.\"),\n\n\t\t\tMB_SPACING(4),\n\t\t\tMB_CONSOLECMD(\"FPS Options\", \"menu_fps\\n\", \"Set model filtering and graphical profile options.\"),\n\t\t\tMB_CONSOLECMD(\"Rendering Options\", \"menu_render\\n\", \"Set rendering options such as water warp and tinting effects.\"),\n\t\t\tMB_CONSOLECMD(\"Lighting Options\", \"menu_lighting\\n\", \"Set options for level lighting and dynamic lights.\"),\n\t\t\tMB_CONSOLECMD(\"Texture Options\", \"menu_textures\\n\", \"Set options for texture detail and effects.\"),\n#ifndef MINIMAL\n\t\t\tMB_CONSOLECMD(\"Particle Options\", \"menu_particles\\n\", \"Set particle effect options.\"),\n#endif\n\t\t\t\n\t\t\tMB_END()\n\t\t};\n\t\tMC_AddFrameStart(menu, y);\n\t\tMC_AddBulk(menu, &resel, bulk, 16, 200, y);\n\t\tMC_AddFrameEnd(menu, y);\n\t}\n\n\t/*\n\ty += 8;\n\tMC_AddRedText(menu, 200, y, current3dres, false); y+=8;\n\n \ty+=8;\n\tMC_AddRedText(menu, 0, y,\t\t\t\t\t\t\t\t\"      ^Ue080^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue082 \", false); y+=8;\n\ty+=8;\n\tinfo->renderer = MC_AddCombo(menu,\t16, y,\t\t\t\t\"         Renderer\", rendererops, i);\ty+=8;\n\tinfo->bppcombo = MC_AddCombo(menu,\t16, y,\t\t\t\t\"      Color Depth\", bppnames, currentbpp); y+=8;\n\tinfo->refreshratecombo = MC_AddCombo(menu,\t16, y,\t\t\"     Refresh Rate\", refreshrates, currentrefreshrate); y+=8;\n\tinfo->modecombo = MC_AddCombo(menu,\t16, y,\t\t\t\t\"       Video Size\", modenames, prefabmode+1);\ty+=8;\n\tMC_AddWhiteText(menu, 16, y, \t\t\t\t\t\t\t\"  3D Aspect Ratio\", false); y+=8;\n\tinfo->conscalecombo = MC_AddCombo(menu,\t16, y,\t\t\t\"          2D Size\", modenames, prefab2dmode+1);\ty+=8;\n\tMC_AddWhiteText(menu, 16, y, \t\t\t\t\t\t\t\"  2D Aspect Ratio\", false); y+=8;\n\tMC_AddCheckBox(menu,\t16, y,\t\t\t\t\t\t\t\"       Fullscreen\", &vid_fullscreen,0);\ty+=8;\n\ty+=4;info->customwidth = MC_AddEdit(menu, 16, y,\t\t\"     Custom width\", vid_width.string);\ty+=8;\n\ty+=4;info->customheight = MC_AddEdit(menu, 16, y,\t\t\"    Custom height\", vid_height.string);\ty+=12;\n\tinfo->vsynccombo = MC_AddCombo(menu,\t16, y,\t\t\t\"            VSync\", vsyncoptions, currentvsync); y+=8;\n\t//MC_AddCheckBox(menu,\t16, y,\t\t\t\t\t\t\t\"   Override VSync\", &vid_vsync,0);\ty+=8;\n\tMC_AddCheckBox(menu,\t16, y,\t\t\t\t\t\t\t\" Desktop Settings\", &vid_desktopsettings,0);\ty+=8;\n\ty+=8;\n\tMC_AddCommand(menu,\t16, y,\t\t\t\t\t\t\t\t\"= Apply Changes =\", M_VideoApply);\ty+=8;\n\ty+=8;\n\tMC_AddSlider(menu,\t16, y,\t\t\t\t\t\t\t\t\"      Screen size\", &scr_viewsize,\t30,\t\t120, 1);y+=8;\n\tMC_AddSlider(menu,\t16, y,\t\t\t\t\t\t\t\t\"Console Autoscale\",&vid_conautoscale, 0, 6, 0.25);\ty+=8;\n\tMC_AddSlider(menu,\t16, y,\t\t\t\t\t\t\t\t\"            Gamma\", &v_gamma, 0.3, 1, 0.05);\ty+=8;\n\tMC_AddCheckBox(menu,\t16, y,\t\t\t\t\t\t\t\"    Desktop Gamma\", &vid_desktopgamma,0);\ty+=8;\n\tMC_AddCheckBox(menu,\t16, y,\t\t\t\t\t\t\t\"   Hardware Gamma\", &vid_hardwaregamma,0);\ty+=8;\n\tMC_AddCheckBox(menu,\t16, y,\t\t\t\t\t\t\t\"   Preserve Gamma\", &vid_preservegamma,0);\ty+=8;\n\tMC_AddSlider(menu,\t16, y,\t\t\t\t\t\t\t\t\"         Contrast\", &v_contrast, 1, 3, 0.05);\ty+=8;\n\ty+=8;\n\tMC_AddCheckBox(menu,\t16, y,\t\t\t\t\t\t\t\"   Windowed Mouse\", &in_windowed_mouse,0);\ty+=8;\n\n\tmenu->selecteditem = (union menuoption_s *)info->renderer;\n\tmenu->cursoritem = (menuoption_t*)MC_AddWhiteText(menu, 152, menu->selecteditem->common.posy, NULL, false);\n\t*/\n\tmenu->predraw = CheckCustomMode;\n}\n\n#ifndef MINIMAL\n\n#ifdef RAGDOLL\n#include \"pr_common.h\"\n#endif\n\ntypedef struct\n{\n\tenum {\n\t\tMV_NONE,\n\t\tMV_BONES,\n\t\tMV_SHADER,\n\t\tMV_TEXTURE,\n\t\tMV_COLLISION,\n\t\tMV_EVENTS,\n\t\tMV_NORMALS,\n\t} mode;\n\tint surfaceidx;\n\tint skingroup;\n\tint framegroup;\n\tint boneidx;\n\tint bonebias;\t//shift the bones menu down to ensure the boneidx stays visible\n\tint textype;\n\tdouble frametime;\n\tdouble skintime;\n\n\tfloat pitch;\n\tfloat yaw;\n\tvec3_t cameraorg;\n\tvec2_t mousepos;\n\tqboolean mousedown;\n\tfloat dist;\n\n\tchar modelname[MAX_QPATH];\n\tchar skinname[MAX_QPATH];\n\tchar animname[MAX_QPATH];\n\n\tchar shaderfile[MAX_QPATH];\n\tchar *shadertext;\n\n\tqboolean paused;\n#ifdef RAGDOLL\n\tlerpents_t ragent;\n\tworld_t ragworld;\n\twedict_t ragworldedict;\n\tcomentvars_t ragworldvars;\n#ifdef VM_Q1\n\tcomextentvars_t ragworldextvars;\n#endif\n\tpubprogfuncs_t ragfuncs;\n\tqboolean flop;\t//ragdoll flopping enabled.\n\tfloat fixedrate;\n#endif\n} modelview_t;\n\nstatic unsigned int genhsv(float h_, float s, float v)\n{\n\tfloat r=0, g=1, b=0;\n\n\tfloat h = h_ - floor(h_);\n\n\tint i = floor(h * 6);\n\tfloat f = h * 6 - i;\n\tfloat p = v * (1 - s);\n\tfloat q = v * (1 - f * s);\n\tfloat t = v * (1 - (1 - f) * s);\n\tswitch(i)\n\t{\n\t\tcase 0: r = v, g = t, b = p; break;\n\t\tcase 1: r = q, g = v, b = p; break;\n\t\tcase 2: r = p, g = v, b = t; break;\n\t\tcase 3: r = p, g = q, b = v; break;\n\t\tcase 4: r = t, g = p, b = v; break;\n\t\tcase 5: r = v, g = p, b = q; break;\n\t}\n\n\treturn 0xff000000 |\n\t\t((int)(r*255)<<16) |\n\t\t((int)(g*255)<<8) |\n\t\t((int)(b*255)<<0);\n}\n\n#include \"com_mesh.h\"\n#ifdef SKELETALMODELS\nstatic int M_BoneDisplayLame(modelview_t *mods, entity_t *e, int topy, int y, int depth, int parent, int first, int last)\n{\n\tint i;\n\tfor (i = first; i < last;  i++)\n\t{\n\t\tint p = Mod_GetBoneParent(e->model, i+1);\n\t\tif (p == parent)\n\t\t{\n\t\t\tconst char *bname = Mod_GetBoneName(e->model, i+1);\n\t\t\tfloat result[12];\n\t\t\tif (!bname)\n\t\t\t\tbname = \"NULL\";\n\t\t\tmemset(result, 0, sizeof(result));\n\t\t\tif (i == mods->boneidx)\n\t\t\t{\n\t\t\t\tif (y < 0)\n\t\t\t\t\tmods->bonebias+=8;\n\t\t\t\telse if (topy+y+8 >= vid.height)\n\t\t\t\t\tmods->bonebias-=8;\n\t\t\t}\n\t\t\tif (y >= 0 && y+8 < vid.height)\n\t\t\t{\n\t\t\t\tif (Mod_GetTag(e->model, i+1, &e->framestate, result))\n\t\t\t\t{\n\t\t\t\t\tif (developer.ival)\n\t\t\t\t\t{\n\t\t\t\t\t\tfloat scale = sqrt(result[0]*result[0] + result[1]*result[1] + result[2]*result[2]);\n\t\t\t\t\t\tfloat scale2 = sqrt(result[0]*result[0] + result[4]*result[4] + result[8]*result[8]);\n\t\t\t\t\t\tDraw_FunString(depth*16, topy+y, va(\"%s%i: %s (%g %g %g, %g %g)\", (i==mods->boneidx)?\"^1\":\"\", i+1, bname, result[3], result[7], result[11], scale, scale2));\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\tDraw_FunString(depth*16, topy+y, va(\"%s%i: %s\", (i==mods->boneidx)?\"^1\":\"\", i+1, bname));\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tDraw_FunString(depth*16, topy+y, va(\"%s%i: %s (err)\", (i==mods->boneidx)?\"^1\":\"\", i, bname));\n\t\t\t}\n\t\t\ty += 8;\n\t\t\ty += M_BoneDisplayLame(mods, e, topy, y, depth+1, i+1, i+1, last)-y;\n\t\t}\n\t}\n\treturn y;\n}\n#endif\n\nstatic unsigned int tobit(unsigned int bitmask)\n{\n\tunsigned int b;\n\tfor (b = 0; b < 32; b++)\n\t{\n\t\tif (bitmask & (1<<b))\n\t\t\treturn b;\n\t}\n\treturn 0;\n}\nstatic void M_ModelViewerDraw(int x, int y, struct menucustom_s *c, struct emenu_s *m)\n{\n\tstatic playerview_t pv;\n\tentity_t ent;\n\tvec3_t fwd, rgt, up;\n\tconst char *fname;\n\tshader_t *shader;\n\tvec2_t fs = {8,8};\n//\tfloat bones[12*MAX_BONES];\n\tvec3_t lightpos = {0, 1, 0};\n\n\tmodelview_t *mods = c->dptr;\n\tskinfile_t *skin;\n\ttexnums_t *texnums;\n#ifdef SKELETALMODELS\n\tqboolean boneanimsonly;\n#endif\n\tmodel_t *animmodel = NULL;\n\n\tif (R2D_Flush)\n\t\tR2D_Flush();\n\n\tmemset(&pv, 0, sizeof(pv));\n\n\tAlias_FlushCache();\t//doesn't like us using stack...\n\tCL_DecayLights ();\n\tCL_ClearEntityLists();\n\tV_ClearRefdef(&pv);\n\tr_refdef.drawsbar = false;\n\tV_CalcRefdef(&pv);\n\tr_refdef.grect.width = vid.width;\n\tr_refdef.grect.height = vid.height;\n\tr_refdef.grect.x = 0;\n\tr_refdef.grect.y = 0;\n\tr_refdef.time = mods->skintime;\n\n\tr_refdef.flags = RDF_NOWORLDMODEL;\n\n\tr_refdef.afov = 60;\n\tr_refdef.fov_x = 0;\n\tr_refdef.fov_y = 0;\n\tr_refdef.dirty |= RDFD_FOV;\n\n\tVectorClear(r_refdef.viewangles);\n\tr_refdef.viewangles[0] = mods->pitch;\n\tr_refdef.viewangles[1] = mods->yaw;\n\tAngleVectors(r_refdef.viewangles, fwd, rgt, up);\n\tVectorScale(fwd, -mods->dist, r_refdef.vieworg);\n\n\tif (keydown[K_MOUSE1] && mods->mousedown&1)\n\t{\n\t\tmods->pitch += (mousecursor_y-mods->mousepos[1]) * m_pitch.value * sensitivity.value;\n\t\tmods->yaw -= (mousecursor_x-mods->mousepos[0]) * m_yaw.value * sensitivity.value;\n\n\t\tif (keydown['w'] || keydown['s'] || keydown['a'] || keydown['d'])\n\t\t{\n\t\t\tVectorAdd(mods->cameraorg, r_refdef.vieworg, mods->cameraorg);\n\t\t\tmods->dist = 0;\n\n\t\t\tif (keydown['w'])\n\t\t\t\tVectorMA(mods->cameraorg, host_frametime*cl_forwardspeed.value, fwd, mods->cameraorg);\n\t\t\tif (keydown['s'])\n\t\t\t\tVectorMA(mods->cameraorg, host_frametime*-(cl_backspeed.value?cl_backspeed.value:cl_forwardspeed.value), fwd, mods->cameraorg);\n\t\t\tif (keydown['a'])\n\t\t\t\tVectorMA(mods->cameraorg, host_frametime*-cl_sidespeed.value, rgt, mods->cameraorg);\n\t\t\tif (keydown['d'])\n\t\t\t\tVectorMA(mods->cameraorg, host_frametime*cl_sidespeed.value, rgt, mods->cameraorg);\n\t\t}\n\t}\n\telse if (keydown[K_MOUSE3] && (mods->mousedown&2))\n\t{\n\t\tfloat r = (mousecursor_x-mods->mousepos[0]);\n\t\tfloat u = (mousecursor_y-mods->mousepos[1]);\n\t\tVectorMA(mods->cameraorg, r*cl_sidespeed.value/4000, rgt, mods->cameraorg);\n\t\tVectorMA(mods->cameraorg, u*cl_upspeed.value/4000, up, mods->cameraorg);\n\t}\n\tmods->mousedown = (!!keydown[K_MOUSE1])<<0;\n\tmods->mousedown|= (!!keydown[K_MOUSE3])<<1;\n\tmods->mousepos[0] = mousecursor_x;\n\tmods->mousepos[1] = mousecursor_y;\n\n\tVectorAdd(r_refdef.vieworg, mods->cameraorg, r_refdef.vieworg);\n\n\tmemset(&ent, 0, sizeof(ent));\n//\tent.angles[1] = realtime*45;//mods->yaw;\n//\tent.angles[0] = realtime*23.4;//mods->pitch;\n\n\tent.model = Mod_ForName(mods->modelname, MLV_WARN);\n\tif (!ent.model)\n\t\treturn;\t//panic!\n\n\tif (ent.model->type == mod_alias)\t//should we even bother with this here?\n\t{\n\t\tif (*mods->animname)\n\t\t\tanimmodel = Mod_ForName(mods->animname, MLV_WARN);\n\n\t\tAngleVectorsMesh(ent.angles, ent.axis[0], ent.axis[1], ent.axis[2]);\n\t}\n\telse\n\t\tAngleVectors(ent.angles, ent.axis[0], ent.axis[1], ent.axis[2]);\n\tVectorInverse(ent.axis[1]);\n\n\tent.scale = max(max(fabs(ent.model->maxs[0]-ent.model->mins[0]), fabs(ent.model->maxs[1]-ent.model->mins[1])), fabs(ent.model->maxs[2]-ent.model->mins[2]));\n\tent.scale = ent.scale?64.0/ent.scale:1;\n//\tent.scale = 1;\n\tent.origin[2] -= ent.model->mins[2] + (ent.model->maxs[2]-ent.model->mins[2]) * 0.5;\n\tent.origin[2] *= ent.scale;\n\tVector4Set(ent.shaderRGBAf, 1, 1, 1, 1);\n\tVectorSet(ent.glowmod, 1, 1, 1);\n\n//\tVectorScale(ent.axis[0], ent.scale, ent.axis[0]);\n//\tVectorScale(ent.axis[1], ent.scale, ent.axis[1]);\n//\tVectorScale(ent.axis[2], ent.scale, ent.axis[2]);\n//\tent.scale = 1;\n\n\tif (strstr(mods->modelname, \"player\"))\n\t{\n\t\tent.bottomcolour\t= genhsv(realtime*0.1 + 0, 1, 1);\n\t\tent.topcolour\t\t= genhsv(realtime*0.1 + 0.5, 1, 1);\n\t}\n\telse\n\t{\n\t\tent.topcolour = TOP_DEFAULT;\n\t\tent.bottomcolour = BOTTOM_DEFAULT;\n\t}\n//\tent.fatness = sin(realtime)*5;\n\tent.playerindex = -1;\n\tent.skinnum = mods->skingroup;\n\tent.shaderTime = 0;//realtime;\n\tent.framestate.g[FS_REG].lerpweight[0] = 1;\n\tent.framestate.g[FS_REG].frame[0] = mods->framegroup;\n\tent.framestate.g[FS_REG].frametime[0] = ent.framestate.g[FS_REG].frametime[1] = mods->frametime;\n\tent.framestate.g[FS_REG].endbone = 0x7fffffff;\n\tif (*mods->skinname)\n\t{\n\t\tent.customskin = Mod_RegisterSkinFile(mods->skinname);\t//explicit .skin file to use\n\t\tif (!ent.customskin)\n\t\t{\n\t\t\tCon_Printf(CON_WARNING\"Named skinfile not loaded\\n\");\n\t\t\t*mods->skinname = 0;\t//don't spam.\n\t\t}\n\t}\n\telse\n\t{\n\t\tent.customskin = Mod_RegisterSkinFile(va(\"%s_%i.skin\", mods->modelname, ent.skinnum));\n\t\tif (ent.customskin == 0)\n\t\t{\n\t\t\tchar haxxor[MAX_QPATH];\n\t\t\tCOM_StripExtension(mods->modelname, haxxor, sizeof(haxxor));\n\t\t\tent.customskin = Mod_RegisterSkinFile(va(\"%s_default.skin\", haxxor));\t//fall back to some default\n\t\t}\n\t}\n\tskin = Mod_LookupSkin(ent.customskin);\n\n\tent.light_avg[0] = ent.light_avg[1] = ent.light_avg[2] = 0.66;\n\tent.light_range[0] = ent.light_range[1] = ent.light_range[2] = 0.33;\n\n#ifdef HEXEN2\n\tent.drawflags = SCALE_ORIGIN_ORIGIN;\n#endif\n\n\tV_ApplyRefdef();\n\n\tif (!mods->paused)\n\t{\n\t\tmods->frametime += host_frametime;\n\t\tmods->skintime += host_frametime;\n\t}\n/*\n\t{\n\t\ttrace_t tr;\n\t\tvec3_t worldmouse;\n\t\tvec3_t mouse = {mousecursor_x/vid.width, 1-mousecursor_y/vid.height, 0.5};\n\t\tfloat d;\n\t\tMatrix4x4_CM_UnProject(mouse, worldmouse, r_refdef.viewangles, r_refdef.vieworg, r_refdef.fov_x, r_refdef.fov_y);\n\n\t\td = DotProduct(worldmouse, fwd);\n\t\tVectorMA(worldmouse, -d, fwd, worldmouse);\n\n\t\tif (ent.model->funcs.NativeTrace && ent.model->funcs.NativeTrace(ent.model, 0, &ent.framestate, ent.axis, r_refdef.vieworg, worldmouse, vec3_origin, vec3_origin, false, ~0, &tr))\n\t\t\t;\n\t\telse\n\t\t\tVectorCopy(worldmouse, tr.endpos);\n\n\t\tVectorCopy(tr.endpos, lightpos);\n\t}\n*/\n\tlightpos[0] = sin(realtime*0.1);\n\tlightpos[1] = cos(realtime*0.1);\n\tlightpos[2] = 0;\n\n\tVectorNormalize(lightpos);\n\tent.light_dir[0] = DotProduct(lightpos, ent.axis[0]);\n\tent.light_dir[1] = DotProduct(lightpos, ent.axis[1]);\n\tent.light_dir[2] = DotProduct(lightpos, ent.axis[2]);\n\n\tent.light_known = 2;\n\n\tif (ent.model->type == mod_dummy)\n\t{\n\t\tDraw_FunString(0, 0, va(\"model \\\"%s\\\" not loaded\", ent.model->name));\n\t\treturn;\n\t}\n\tswitch(ent.model->loadstate)\n\t{\n\tcase MLS_LOADED:\n\t\tbreak;\n\tcase MLS_NOTLOADED:\n\t\tDraw_FunString(0, 0, va(\"\\\"%s\\\" not loaded\", ent.model->name));\n\t\treturn;\n\tcase MLS_LOADING:\n\t\tDraw_FunString(0, 0, va(\"\\\"%s\\\" still loading\", ent.model->name));\n\t\treturn;\n\tcase MLS_FAILED:\n\t\tDraw_FunString(0, 0, va(\"Unable to load \\\"%s\\\"\", ent.model->name));\n\t\treturn;\n\t}\n\n\n#ifdef RAGDOLL\n\tif (mods->flop)\n\t\tent.framestate.g[FS_REG].frame[0] |= 0x8000;\n\tif (ent.model->dollinfo && mods->ragworld.rbe)\n\t{\n\t\tfloat rate = 1.0/60;\t//try a fixed tick rate...\n\t\trag_doallanimations(&mods->ragworld);\n\t\tif (mods->fixedrate > 1)\n\t\t\tmods->fixedrate = 1;\n\t\twhile (mods->fixedrate >= rate)\n\t\t{\n\t\t\tmods->ragworld.rbe->RunFrame(&mods->ragworld, rate, 800);\n\t\t\tmods->fixedrate -= rate;\n\t\t}\n\t\tif (!mods->paused)\n\t\t\tmods->fixedrate += host_frametime;\n\n\t\trag_updatedeltaent(&mods->ragworld, &ent, &mods->ragent);\n\t}\n#endif\n\n#ifdef SKELETALMODELS\n\tif (animmodel)// && Mod_GetNumBones(ent.model, false)==Mod_GetNumBones(animmodel, false))\n\t{\n\t\tint numbones = Mod_GetNumBones(ent.model, false);\n\t\tgaliasbone_t *boneinfo = Mod_GetBoneInfo(ent.model, &numbones);\n\t\tfloat *bonematrix = alloca(numbones*sizeof(*bonematrix)*12);\n\t\tent.framestate.bonecount = Mod_GetBoneRelations(animmodel, 0, numbones, boneinfo, &ent.framestate, bonematrix);\n\t\tent.framestate.bonestate = bonematrix;\n\t\tent.framestate.skeltype = SKEL_RELATIVE;\n\t}\n\telse\n#endif\n\t\tanimmodel = ent.model;\t//not using it. sorry. warn?\n\n\n\tif (mods->mode == MV_NORMALS)\n\t{\n\t\tshader_t *s;\n\t\tif (1)\n\t\t{\n\t\t\ts = R_RegisterShader(\"hitbox_nodepth\", SUF_NONE,\n\t\t\t\t\"{\\n\"\n\t\t\t\t\t\"polygonoffset\\n\"\n\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\"map $whiteimage\\n\"\n\t\t\t\t\t\t\"blendfunc gl_src_alpha gl_one\\n\"\n\t\t\t\t\t\t\"rgbgen vertex\\n\"\n\t\t\t\t\t\t\"alphagen vertex\\n\"\n\t\t\t\t\t\t\"nodepthtest\\n\"\n\t\t\t\t\t\"}\\n\"\n\t\t\t\t\"}\\n\");\n\t\t\tMod_AddSingleSurface(&ent, mods->surfaceidx, s, true);\n\t\t}\n\t}\n\tif (mods->mode == MV_COLLISION)\n\t{\n\t\tshader_t *s;\n\n#ifdef HALFLIFEMODELS\n\t\tif (ent.model->type == mod_halflife)\n\t\t\tHLMDL_DrawHitBoxes(&ent);\n\t\telse\n#endif\n\t\tif (1)\n\t\t{\n\t\t\ts = R_RegisterShader(\"hitbox_nodepth\", SUF_NONE,\n\t\t\t\t\"{\\n\"\n\t\t\t\t\t\"polygonoffset\\n\"\n\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\"map $whiteimage\\n\"\n\t\t\t\t\t\t\"blendfunc gl_src_alpha gl_one\\n\"\n\t\t\t\t\t\t\"rgbgen vertex\\n\"\n\t\t\t\t\t\t\"alphagen vertex\\n\"\n\t\t\t\t\t\t\"nodepthtest\\n\"\n\t\t\t\t\t\"}\\n\"\n\t\t\t\t\"}\\n\");\n\t\t\tMod_AddSingleSurface(&ent, mods->surfaceidx, s, false);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tvec3_t mins, maxs;\n\t\t\tVectorAdd(ent.model->mins, ent.origin, mins);\n\t\t\tVectorAdd(ent.model->maxs, ent.origin, maxs);\n\n\t\t\ts = R_RegisterShader(\"bboxshader_nodepth\", SUF_NONE,\n\t\t\t\t\"{\\n\"\n\t\t\t\t\t\"polygonoffset\\n\"\n\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\"map $whiteimage\\n\"\n\t\t\t\t\t\t\"blendfunc gl_src_alpha gl_one\\n\"\n\t\t\t\t\t\t\"rgbgen vertex\\n\"\n\t\t\t\t\t\t\"alphagen vertex\\n\"\n\t\t\t\t\t\t\"nodepthtest\\n\"\n\t\t\t\t\t\"}\\n\"\n\t\t\t\t\"}\\n\");\n\t\t\tCLQ1_AddOrientedCube(s, mins, maxs, NULL, 1, 1, 1, 0.2);\n\t\t}\n\n#ifdef _DEBUG\n\t\t{\n\t\t\ttrace_t tr;\n\t\t\tvec3_t v1, v2;\n\t\t\tVectorSet(v1, 1000*sin(realtime*2*M_PI/180), 1000*cos(realtime*2*M_PI/180), -ent.origin[2]);\n\t\t\tVectorScale(v1, -1, v2);\n\t\t\tv2[2] = v1[2];\n\t\t\ts = R_RegisterShader(\"bboxshader\", SUF_NONE,\n\t\t\t\t\"{\\n\"\n\t\t\t\t\t\"polygonoffset\\n\"\n\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\"map $whiteimage\\n\"\n\t\t\t\t\t\t\"blendfunc gl_src_alpha gl_one\\n\"\n\t\t\t\t\t\t\"rgbgen vertex\\n\"\n\t\t\t\t\t\t\"alphagen vertex\\n\"\n\t\t\t\t\t\"}\\n\"\n\t\t\t\t\"}\\n\");\n\n\t\t\tif (ent.model->funcs.NativeTrace && ent.model->funcs.NativeTrace(ent.model, 0, &ent.framestate, ent.axis, v1, v2, vec3_origin, vec3_origin, false, ~0, &tr))\n\t\t\t{\n\t\t\t\tvec3_t dir;\n\t\t\t\tfloat f;\n\n\t\t\t\tVectorMA(ent.origin, ent.scale, v1, v1);\n\t\t\t\tVectorMA(ent.origin, ent.scale, v2, v2);\n\t\t\t\tVectorMA(ent.origin, ent.scale, tr.endpos, tr.endpos);\n\n\t\t\t\tVectorSubtract(tr.endpos, v1, dir);\n\t\t\t\tf = DotProduct(dir, tr.plane.normal) * -2;\n\t\t\t\tVectorMA(dir, f, tr.plane.normal, v2);\n\t\t\t\tVectorAdd(v2, tr.endpos, v2);\n\n\t\t\t\tCLQ1_DrawLine(s, v1, tr.endpos, 0, 1, 0, 1);\n\t\t\t\tCLQ1_DrawLine(s, tr.endpos, v2, 1, 0, 0, 1);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tVectorAdd(v1, ent.origin, v1);\n\t\t\t\tVectorAdd(v2, ent.origin, v2);\n\t\t\t\tCLQ1_DrawLine(s, v1, v2, 0, 1, 0, 1);\n\t\t\t}\n\t\t}\n#endif\n\t}\n#ifdef SKELETALMODELS\n\tboneanimsonly = false;\n\tif (ent.model && ent.model->loadstate == MLS_LOADED && ent.model->type == mod_alias)\n\t{\t//some models don't actually contain any mesh data, but exist as containers for skeletal animations that can be skel_built into a different model's anims.\n\t\t//always show their bones, so users don't think its an engine bug.\n\t\tgaliasinfo_t *inf = Mod_Extradata(ent.model);\n\t\tif (inf && !inf->nextsurf && !inf->numindexes && inf->numbones)\n\t\t\tboneanimsonly = true;\n\t}\n\tif (mods->mode == MV_BONES || boneanimsonly)\n\t{\n\t\tshader_t *lineshader;\n\t\tint tags = Mod_GetNumBones(ent.model, true);\n\t\tint b;\n\t\t//if (ragdoll)\n\t\t//\tent->frame[] |= 0x8000;\n\t\t//rag_updatedeltaent(&ent, le);\n\n\t\tlineshader = R_RegisterShader(\"lineshader_nodepth\", SUF_NONE,\n\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\"polygonoffset\\n\"\n\t\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\t\"map $whiteimage\\n\"\n\t\t\t\t\t\t\t\"blendfunc add\\n\"\n\t\t\t\t\t\t\t\"rgbgen vertex\\n\"\n\t\t\t\t\t\t\t\"alphagen vertex\\n\"\n\t\t\t\t\t\t\t\"nodepthtest\\n\"\n\t\t\t\t\t\t\"}\\n\"\n\t\t\t\t\t\"}\\n\");\n\t\tfor (b = 1; b <= tags; b++)\n\t\t{\n\t\t\tint p = Mod_GetBoneParent(ent.model, b);\n\t\t\tvec3_t start, end;\n\t\t\tfloat boneinfo[12];\n\n\t\t\tMod_GetTag(ent.model, b, &ent.framestate, boneinfo);\n\t\t\t//fixme: no axis transform\n\t\t\tVectorSet(start, boneinfo[3], boneinfo[7], boneinfo[11]);\n\t\t\tVectorMA(ent.origin, ent.scale, start, start);\n\n\t\t\tif (p)\n\t\t\t{\n\t\t\t\tMod_GetTag(ent.model, p, &ent.framestate, boneinfo);\n\t\t\t\t//fixme: no axis transform\n\t\t\t\tVectorSet(end, boneinfo[3], boneinfo[7], boneinfo[11]);\n\t\t\t\tVectorMA(ent.origin, ent.scale, end, end);\n\t\t\t\tCLQ1_DrawLine(lineshader, start, end, 1, (b-1 == mods->boneidx)?0:1, 1, 1);\n\t\t\t}\n\t\t\tif (b-1 == mods->boneidx)\n\t\t\t{\n\t\t\t\tVectorSet(end, start[0]+boneinfo[0], start[1]+boneinfo[4], start[2]+boneinfo[8]);\n\t\t\t\tCLQ1_DrawLine(lineshader, start, end, 1, 0, 0, 1);\n\t\t\t\tVectorSet(end, start[0]+boneinfo[1], start[1]+boneinfo[5], start[2]+boneinfo[9]);\n\t\t\t\tCLQ1_DrawLine(lineshader, start, end, 0, 1, 0, 1);\n\t\t\t\tVectorSet(end, start[0]+boneinfo[2], start[1]+boneinfo[6], start[2]+boneinfo[10]);\n\t\t\t\tCLQ1_DrawLine(lineshader, start, end, 0, 0, 1, 1);\n\t\t\t}\n\t\t}\n\t}\n#endif\n\n\tV_AddAxisEntity(&ent);\n\n\tR_RenderView();\n\n\ty = 0;\n\t{\n\t\tfname = Mod_SurfaceNameForNum(ent.model, mods->surfaceidx);\n\t\tif (!fname)\n\t\t\tfname = \"Unknown Surface\";\n\t\tDraw_FunString(0, y, va(\"Surf %i: %s\", mods->surfaceidx, fname));\n\t\ty+=8;\n\t}\n\t{\n\t\tfname = Mod_SkinNameForNum(ent.model, mods->surfaceidx, mods->skingroup);\n\t\tif (!fname)\n\t\t{\n\t\t\tDraw_FunString(0, y, va(\"Skin %i: <invalid skin>\", mods->skingroup));\n\t\t}\n\t\telse\n\t\t{\n\t\t\tshader = Mod_ShaderForSkin(ent.model, mods->surfaceidx, mods->skingroup, r_refdef.time, &texnums);\n\t\t\tDraw_FunString(0, y, va(\"Skin %i: \\\"%s\\\", material \\\"%s\\\"\", mods->skingroup, fname, shader?shader->name:\"NO SHADER\"));\n\t\t}\n\t\ty+=8;\n\t}\n\t{\n\t\tchar *fname;\n\t\tint numframes = 0;\n\t\tfloat duration = 0;\n\t\tqboolean loop = false;\n\t\tint act = -1;\n\t\tif (!Mod_FrameInfoForNum(animmodel, mods->surfaceidx, mods->framegroup, &fname, &numframes, &duration, &loop, &act))\n\t\t\tfname = \"Unknown Sequence\";\n\t\tif (animmodel != ent.model)\n\t\t\tfname = va(\"^[%s^] %s\", animmodel->name, fname);\t//tag it properly if its from our animmodel\n\t\tif (act != -1)\n\t\t\tDraw_FunString(0, y, va(\"Frame%i[%i]: %s (%i poses, %f of %f secs, %s)\", mods->framegroup, act, fname, numframes, ent.framestate.g[FS_REG].frametime[0], duration, loop?\"looped\":\"unlooped\"));\n\t\telse\n\t\t\tDraw_FunString(0, y, va(\"Frame%i: %s (%i poses, %f of %f secs, %s)\", mods->framegroup, fname, numframes, ent.framestate.g[FS_REG].frametime[0], duration, loop?\"looped\":\"unlooped\"));\n\t\ty+=8;\n\t}\n\n\tswitch(mods->mode)\n\t{\n\tcase MV_NONE:\n\t\tR_DrawTextField(r_refdef.grect.x, r_refdef.grect.y+y, r_refdef.grect.width, r_refdef.grect.height-y, \n\t\t\tva(\"Help:\\narrows: pitch/rotate\\n\"\n\t\t\t\"w: zoom in\\n\"\n\t\t\t\"s: zoom out\\n\"\n\t\t\t\"m: mode\\n\"\n\t\t\t\"r: reset times\\n\"\n\t\t\t\"home: skin+=1\\n\"\n\t\t\t\"end: skin-=1\\n\"\n\t\t\t\"pgup: frame+=1\\n\"\n\t\t\t\"pgdn: frame-=1\\n\"\n\t\t\t\"mins: %g %g %g, maxs: %g %g %g\\n\"\n\t\t\t\"flags: %#x %#x\\n\", \n\t\t\t\tent.model->mins[0], ent.model->mins[1], ent.model->mins[2], ent.model->maxs[0], ent.model->maxs[1], ent.model->maxs[2],\n\t\t\t\tent.model->flags, ent.model->engineflags),\n\t\t\tCON_WHITEMASK, CPRINT_TALIGN|CPRINT_LALIGN, font_default, fs);\n\t\tbreak;\n\tcase MV_COLLISION:\n\t\tif (!ent.model)\n\t\t\t;\n\t\telse if (ent.model->type == mod_alias)\n\t\t{\n\t\t\tgaliasinfo_t *inf = Mod_Extradata(ent.model);\n\t\t\tint surfnum = mods->surfaceidx;\n\t\t\twhile(inf && surfnum-->0)\n\t\t\t\tinf = inf->nextsurf;\n\t\t\tif (inf)\n\t\t\t{\n\t\t\t\tchar contents[512];\n\t\t\t\tunsigned int i;\n\t\t\t\tchar *contentnames[32] = {NULL};\n\t\t\t\tcontentnames[tobit(FTECONTENTS_SOLID)] = \"solid\";\n\t\t\t\tcontentnames[tobit(FTECONTENTS_LAVA)] = \"lava\";\n\t\t\t\tcontentnames[tobit(FTECONTENTS_SLIME)] = \"slime\";\n\t\t\t\tcontentnames[tobit(FTECONTENTS_WATER)] = \"water\";\n\t\t\t\tcontentnames[tobit(FTECONTENTS_LADDER)] = \"ladder\";\n\t\t\t\tcontentnames[tobit(FTECONTENTS_PLAYERCLIP)] = \"playerclip\";\n\t\t\t\tcontentnames[tobit(FTECONTENTS_MONSTERCLIP)] = \"monsterclip\";\n\t\t\t\tcontentnames[tobit(FTECONTENTS_BODY)] = \"body\";\n\t\t\t\tcontentnames[tobit(FTECONTENTS_CORPSE)] = \"corpse\";\n\t\t\t\tcontentnames[tobit(FTECONTENTS_SKY)] = \"sky\";\n\t\t\t\tfor (*contents = 0, i = 0; i < 32; i++)\n\t\t\t\t{\n\t\t\t\t\tif (inf->contents & (1<<i))\n\t\t\t\t\t{\n\t\t\t\t\t\tif (*contents)\n\t\t\t\t\t\t\tQ_strncatz(contents, \"|\", sizeof(contents));\n\t\t\t\t\t\tif (contentnames[i])\n\t\t\t\t\t\t\tQ_strncatz(contents, contentnames[i], sizeof(contents));\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tQ_strncatz(contents, va(\"%#x\", 1<<i), sizeof(contents));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (!*contents)\n\t\t\t\t\tQ_strncatz(contents, \"non-solid\", sizeof(contents));\n\t\t\t\tR_DrawTextField(r_refdef.grect.x, r_refdef.grect.y+y, r_refdef.grect.width, r_refdef.grect.height-y, \n\t\t\t\t\tva(\t\"Collision:\\n\"\n\t\t\t\t\t\t\"mins: %g %g %g, maxs: %g %g %g\\n\"\n\t\t\t\t\t\t\"contents: %s\\n\"\n\t\t\t\t\t\t\"surfflags: %#x\\n\"\n\t\t\t\t\t\t\"body: %i\\n\"\n\t\t\t\t\t\t\"geomset: %i %i%s\\n\"\n\t\t\t\t\t\t\"numverts: %i\\nnumtris: %i\\n\"\n#ifdef SKELETALMODELS\n\t\t\t\t\t\t\"numbones: %i\\n\"\n#endif\n\t\t\t\t\t\t\"minlod: %g\\n\"\n\t\t\t\t\t\t\"maxlod: %g%s\\n\"\n\t\t\t\t\t\t, ent.model->mins[0], ent.model->mins[1], ent.model->mins[2], ent.model->maxs[0], ent.model->maxs[1], ent.model->maxs[2],\n\t\t\t\t\t\tcontents,\n\t\t\t\t\t\tinf->csurface.flags,\n\t\t\t\t\t\tinf->surfaceid,\n\t\t\t\t\t\tinf->geomset>=MAX_GEOMSETS?-1:inf->geomset, inf->geomid,\n\t\t\t\t\t\t\t\tinf->geomset>=MAX_GEOMSETS?\" (always)\":\n\t\t\t\t\t\t\t\t((skin?skin->geomset[inf->geomset]:0)!=inf->geomid)?\" (hidden)\":\n\t\t\t\t\t\t\t\t\"\",\n\t\t\t\t\t\tinf->numverts, inf->numindexes/3\n#ifdef SKELETALMODELS\n\t\t\t\t\t\t,inf->numbones\n#endif\n\t\t\t\t\t\t,inf->mindist,inf->maxdist,inf->maxdist?\"\":\" (infinite)\"\n\t\t\t\t\t\t)\n\t\t\t\t\t, CON_WHITEMASK, CPRINT_TALIGN|CPRINT_LALIGN, font_default, fs);\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tR_DrawTextField(r_refdef.grect.x, r_refdef.grect.y+y, r_refdef.grect.width, r_refdef.grect.height-y, \n\t\t\t\tva(\t\"Collision info not available\\n\"\n\t\t\t\t\t\"mins: %g %g %g, maxs: %g %g %g\\n\", ent.model->mins[0], ent.model->mins[1], ent.model->mins[2], ent.model->maxs[0], ent.model->maxs[1], ent.model->maxs[2])\n\t\t\t\t, CON_WHITEMASK, CPRINT_TALIGN|CPRINT_LALIGN, font_default, fs);\n\t\t}\n\t\tbreak;\n\tcase MV_NORMALS:\n\t\tDraw_FunString(0, y, va(\"Normals\"));\n\t\tbreak;\n\tcase MV_BONES:\n#ifdef SKELETALMODELS\n\t\t{\n\t\t\tint bonecount = Mod_GetNumBones(ent.model, true);\n\t\t\tif (bonecount)\n\t\t\t{\n\t\t\t\tif (mods->boneidx >= bonecount)\n\t\t\t\t\tmods->boneidx = bonecount-1;\n\t\t\t\tif (mods->boneidx < 0)\n\t\t\t\t\tmods->boneidx = 0;\n\t\t\t\tDraw_FunString(0, y, va(\"Bones: \"));\n\t\t\t\ty+=8;\n\t\t\t\tM_BoneDisplayLame(mods, &ent, y, mods->bonebias, 0, 0, 0, bonecount);\n\t\t\t}\n\t\t\telse\n\t\t\t\tR_DrawTextField(r_refdef.grect.x, r_refdef.grect.y+y, r_refdef.grect.width, r_refdef.grect.height-y, \"No bones in model\", CON_WHITEMASK, CPRINT_TALIGN|CPRINT_LALIGN, font_default, fs);\n\t\t}\n#endif\n\t\tbreak;\n\tcase MV_EVENTS:\n\t\t{\n\t\t\tint i;\n\t\t\tfloat timestamp = 0;\n\t\t\tint code = 0;\n\t\t\tchar *data = NULL;\n\t\t\tDraw_FunString(0, y, va(\"Events: \"));\n\t\t\ty+=8;\n\t\t\tfor (i = 0; Mod_GetModelEvent(animmodel, mods->framegroup, i, &timestamp, &code, &data); y+=8, i++)\n\t\t\t{\n\t\t\t\tDraw_FunString(0, y, va(\"%i %f: %i %s\", i, timestamp, code, data));\n\t\t\t}\n\t\t\tDraw_FunString(0, y, va(\"%f: <end of animation>\", Mod_GetFrameDuration(animmodel, 0, mods->framegroup)));\n\t\t}\n\t\tbreak;\n\tcase MV_SHADER:\n\t\t{\n\t\t\tif (!mods->shadertext)\n\t\t\t{\n\t\t\t\tchar *cr;\n\t\t\t\tchar *body = Shader_GetShaderBody(Mod_ShaderForSkin(ent.model, mods->surfaceidx, mods->skingroup, r_refdef.time, &texnums), mods->shaderfile, sizeof(mods->shaderfile));\n\t\t\t\tif (!body)\n\t\t\t\t{\n\t\t\t\t\tDraw_FunString(0, y, \"Shader info not available\");\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tif (*mods->shaderfile)\n\t\t\t\t\tmods->shadertext = Z_StrDupf(\"\\n\\nPress space to view+edit the shader\\n\\n%s\", body);\n\t\t\t\telse\n\t\t\t\t\tmods->shadertext = Z_StrDupf(\"{ %s\",body);\n\n\t\t\t\twhile ((cr = strchr(mods->shadertext, '\\r')))\n\t\t\t\t\t*cr = ' ';\n\t\t\t}\n\t\t\tR_DrawTextField(r_refdef.grect.x, r_refdef.grect.y+24, r_refdef.grect.width, r_refdef.grect.height-16, mods->shadertext, CON_WHITEMASK, CPRINT_TALIGN|CPRINT_LALIGN, font_default, fs);\n\n\t\t\t//fixme: draw the shader's textures.\n\t\t}\n\t\tbreak;\n\tcase MV_TEXTURE:\n\t\t{\n\t\t\tshader_t *shader = Mod_ShaderForSkin(ent.model, mods->surfaceidx, mods->skingroup, r_refdef.time, &texnums);\n\t\t\tif (shader)\n\t\t\t{\n\t\t\t\tchar *t;\n\t\t\t\ttexnums_t *skin = (texnums&&TEXVALID(texnums->base))?texnums:shader->defaulttextures;\n\t\t\t\tshader = R_RegisterShader(\"modelviewertexturepreview\", SUF_2D, \"{\\nprogram default2d\\n{\\nmap $diffuse\\n}\\n}\\n\");\n\n\t\t\t\tswitch(mods->textype)\n\t\t\t\t{\n\t\t\t\tcase 1:\n\t\t\t\t\tt = \"Normalmap\";\n\t\t\t\t\tshader->defaulttextures->base = skin->bump;\n\t\t\t\t\tbreak;\n\t\t\t\tcase 2:\n\t\t\t\t\tt = \"SpecularMap\";\n\t\t\t\t\tshader->defaulttextures->base = skin->specular;\t\t//specular lighting values.\n\t\t\t\t\tbreak;\n\t\t\t\tcase 3:\n\t\t\t\t\tt = \"UpperMap\";\n\t\t\t\t\tshader->defaulttextures->base = skin->upperoverlay;\t//diffuse texture for the upper body(shirt colour). no alpha channel. added to base.rgb\n\t\t\t\t\tbreak;\n\t\t\t\tcase 4:\n\t\t\t\t\tt = \"LowerMap\";\n\t\t\t\t\tshader->defaulttextures->base = skin->loweroverlay;\t//diffuse texture for the lower body(trouser colour). no alpha channel. added to base.rgb\n\t\t\t\t\tbreak;\n\t\t\t\tcase 5:\n\t\t\t\t\tt = \"PalettedMap\";\n\t\t\t\t\tshader->defaulttextures->base = skin->paletted;\t\t//8bit paletted data, just because.\n\t\t\t\t\tbreak;\n\t\t\t\tcase 6:\n\t\t\t\t\tt = \"FullbrightMap\";\n\t\t\t\t\tshader->defaulttextures->base = skin->fullbright;\n\t\t\t\t\tbreak;\n\t\t\t\tcase 7:\n\t\t\t\t\tt = \"ReflectCube\";\n\t\t\t\t\tshader->defaulttextures->base = skin->reflectcube;\n\t\t\t\t\tbreak;\n\t\t\t\tcase 8:\n\t\t\t\t\tt = \"ReflectMask\";\n\t\t\t\t\tshader->defaulttextures->base = skin->reflectmask;\n\t\t\t\t\tbreak;\n\t\t\t\tcase 9:\n\t\t\t\t\tt = \"DisplacementMap\";\n\t\t\t\t\tshader->defaulttextures->base = skin->displacement;\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tmods->textype = 0;\n\t\t\t\t\tt = \"Diffusemap\";\n\t\t\t\t\tshader->defaulttextures->base = skin->base;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tif (shader->defaulttextures->base)\n\t\t\t\t{\n\t\t\t\t\tfloat w, h;\n\t\t\t\t\tDraw_FunString(0, y, va(\"%s: %s  (%s), %s %u*%u*%u\",\n\t\t\t\t\t\t\tt, shader->defaulttextures->base->ident, shader->defaulttextures->base->subpath?shader->defaulttextures->base->subpath:\"\",\n\t\t\t\t\t\t\tImage_FormatName(shader->defaulttextures->base->format), shader->defaulttextures->base->width, shader->defaulttextures->base->height, shader->defaulttextures->base->depth));\n\t\t\t\t\ty+=8;\n\n\t\t\t\t\tw = (float)vid.width / shader->defaulttextures->base->width;\n\t\t\t\t\th = (float)(vid.height-y) / shader->defaulttextures->base->height;\n\t\t\t\t\th = min(w,h);\n\t\t\t\t\tw = h*shader->defaulttextures->base->width;\n\t\t\t\t\th = h*shader->defaulttextures->base->height;\n\t\t\t\t\tR2D_Image(0, y, w, h, 0, 0, 1, 1, shader);\n\n\n\t\t\t\t\t{\n\t\t\t\t\t\tshader_t *s = R_RegisterShader(\"hitbox_nodepth\", SUF_NONE,\n\t\t\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\t\t\"polygonoffset\\n\"\n\t\t\t\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\t\t\t\"map $whiteimage\\n\"\n\t\t\t\t\t\t\t\t\t\"blendfunc gl_src_alpha gl_one\\n\"\n\t\t\t\t\t\t\t\t\t\"rgbgen vertex\\n\"\n\t\t\t\t\t\t\t\t\t\"alphagen vertex\\n\"\n\t\t\t\t\t\t\t\t\t\"nodepthtest\\n\"\n\t\t\t\t\t\t\t\t\"}\\n\"\n\t\t\t\t\t\t\t\"}\\n\");\n\t\t\t\t\t\tint oldtris = cl_numstris;\n\t\t\t\t\t\tint oldidx = cl_numstrisidx;\n\t\t\t\t\t\tint oldvert = cl_numstrisvert;\n\t\t\t\t\t\tmesh_t mesh;\n\t\t\t\t\t\tmemset(&mesh, 0, sizeof(mesh));\n\n\t\t\t\t\t\tAngleVectors(vec3_origin, ent.axis[0], ent.axis[1], ent.axis[2]);\n\t\t\t\t\t\tVectorInverse(ent.axis[1]);\n\t\t\t\t\t\tVectorScale(ent.axis[0], w, ent.axis[0]);\n\t\t\t\t\t\tVectorScale(ent.axis[1], h, ent.axis[1]);\n\t\t\t\t\t\tVectorSet(ent.origin, 0, y, 0);\n\t\t\t\t\t\tent.scale = 1;\n\n\t\t\t\t\t\tMod_AddSingleSurface(&ent, mods->surfaceidx, s, 2);\n\n\t\t\t\t\t\tmesh.xyz_array = cl_strisvertv + oldvert;\n\t\t\t\t\t\tmesh.st_array = cl_strisvertt + oldvert;\n\t\t\t\t\t\tmesh.colors4f_array[0] = cl_strisvertc + oldvert;\n\t\t\t\t\t\tmesh.indexes = cl_strisidx + oldidx;\n\t\t\t\t\t\tmesh.numindexes = cl_numstrisidx - oldidx;\n\t\t\t\t\t\tmesh.numvertexes = cl_numstrisvert-oldvert;\n\n\t\t\t\t\t\tcl_numstris = oldtris;\n\t\t\t\t\t\tcl_numstrisidx = oldidx;\n\t\t\t\t\t\tcl_numstrisvert = oldvert;\n\n\t\t\t\t\t\tif (R2D_Flush)\n\t\t\t\t\t\t\tR2D_Flush();\n\t\t\t\t\t\tBE_DrawMesh_Single(s, &mesh, NULL, BEF_LINES);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tDraw_FunString(0, y, va(\"%s: <NO TEXTURE>\", t));\n\t\t\t}\n\t\t\telse\n\t\t\t\tDraw_FunString(0, y, \"Texture info not available\");\n\t\t}\n\t\tbreak;\n\tdefault:\n\t\tDraw_FunString(0, y, \"Unknown display mode\");\n\t\tbreak;\n\t}\n}\nstatic qboolean M_ModelViewerKey(struct menucustom_s *c, struct emenu_s *m, int key, unsigned int unicode)\n{\n\tmodelview_t *mods = c->dptr;\n\n\tif ((key == 'w' && !keydown[K_MOUSE1]) || key == K_MWHEELUP)\n\t{\n\t\tmods->dist *= 0.9;\n\t\tif (mods->dist < 1)\n\t\t\tmods->dist = 1;\n\t}\n\telse if ((key == 's' && !keydown[K_MOUSE1]) || key == K_MWHEELDOWN)\n\t\tmods->dist /= 0.9;\n\telse if (key == 'm')\n\t{\n\t\tZ_Free(mods->shadertext);\n\t\tmods->shadertext = NULL;\n\t\tswitch (mods->mode)\n\t\t{\n\t\tcase MV_NONE:\tmods->mode = MV_BONES;\t\tbreak; \n\t\tcase MV_BONES:\tmods->mode = MV_SHADER;\t\tbreak;\n\t\tcase MV_SHADER:\tmods->mode = MV_TEXTURE;\tbreak;\n\t\tcase MV_TEXTURE: mods->mode = MV_COLLISION;\tbreak;\n\t\tcase MV_COLLISION: mods->mode = MV_EVENTS;\tbreak;\n\t\tcase MV_EVENTS: mods->mode = MV_NORMALS;\tbreak;\n\t\tcase MV_NORMALS: mods->mode = MV_NONE;\t\tbreak;\n\t\t}\n\t}\n\telse if (key >= '1' && key <= '7')\n\t{\n\t\tZ_Free(mods->shadertext);\n\t\tmods->shadertext = NULL;\n\t\tmods->mode = MV_NONE + (key - '1');\n\t}\n\telse if (key == 'p')\n\t\tmods->paused = !mods->paused;\n\telse if (key == 'r')\n\t{\n\t\tmods->frametime = 0;\n\t\tmods->skintime = 0;\n\t}\n#ifdef RAGDOLL\n\telse if (key == 'f')\n\t{\n\t\tmods->flop ^= 1;\n\t\tif (!mods->flop)\n\t\t\trag_removedeltaent(&mods->ragent);\n\t}\n#endif\n\telse if (key == '[')\n\t{\n\t\tmods->boneidx--;\n\t\tif (mods->boneidx < 0)\n\t\t\tmods->boneidx = 0;\n\t}\n\telse if (key == ']')\n\t\tmods->boneidx++;\n\telse if (key == K_UPARROW || key == K_KP_UPARROW || key == K_GP_DPAD_UP)\n\t\tmods->pitch += 5;\n\telse if (key == K_DOWNARROW || key == K_KP_DOWNARROW || key == K_GP_DPAD_DOWN)\n\t\tmods->pitch -= 5;\n\telse if (key == K_LEFTARROW || key == K_KP_LEFTARROW || key == K_GP_DPAD_LEFT)\n\t\tmods->yaw -= 5;\n\telse if (key == K_RIGHTARROW || key == K_KP_RIGHTARROW || key == K_GP_DPAD_RIGHT)\n\t\tmods->yaw += 5;\n\telse if (key == 't')\n\t{\n\t\tif (mods->mode == MV_TEXTURE)\n\t\t\tmods->textype += 1;\n\t\telse\n\t\t\tmods->textype = 0;\n\t\tmods->mode = MV_TEXTURE;\n\t}\n\telse if (key == K_END)\n\t{\n\t\tmods->skingroup = max(0, mods->skingroup-1);\n\t\tmods->skintime = 0;\n\t\tZ_Free(mods->shadertext);\n\t\tmods->shadertext = NULL;\n\t}\n\telse if (key == K_HOME)\n\t{\n\t\tmods->skingroup += 1;\n\t\tmods->skintime = 0;\n\t\tZ_Free(mods->shadertext);\n\t\tmods->shadertext = NULL;\n\t}\n\telse if (key == K_PGDN)\n\t{\n\t\tmods->framegroup = max(0, mods->framegroup-1);\n\t\tmods->frametime = 0;\n\t\tZ_Free(mods->shadertext);\n\t\tmods->shadertext = NULL;\n\t}\n\telse if (key == K_PGUP)\n\t{\n\t\tmods->framegroup += 1;\n\t\tmods->frametime = 0;\n\t}\n\telse if (key == K_DEL)\n\t{\n\t\tmods->surfaceidx = max(0, mods->surfaceidx-1);\n\t\tZ_Free(mods->shadertext);\n\t\tmods->shadertext = NULL;\n\t}\n\telse if (key == K_INS)\n\t{\n\t\tmods->surfaceidx += 1;\n\t\tZ_Free(mods->shadertext);\n\t\tmods->shadertext = NULL;\n\t}\n\telse if (key == K_SPACE)\n\t{\n\t\tif (*mods->shaderfile)\n\t\t\tCbuf_AddText(va(\"\\nedit %s\\n\", mods->shaderfile), RESTRICT_LOCAL);\n\t\treturn true;\n\t}\n\telse\n\t\treturn false;\n\treturn true;\n}\n\n#ifdef RAGDOLL\nvoid M_Modelviewer_Reset(struct menu_s *cmenu)\n{\n\temenu_t *menu = (emenu_t*)cmenu;\n\tmodelview_t *mv = menu->data;\n\tmv->ragworld.worldmodel = NULL;\t//already went away\n\trag_removedeltaent(&mv->ragent);\n\tskel_reset(&mv->ragworld);\n\tWorld_RBE_Shutdown(&mv->ragworld);\n\t//we still want it.\n\tmv->ragworld.worldmodel = NULL;//Mod_ForName(\"\", MLV_SILENT);\n//\tWorld_RBE_Start(&mv->ragworld);\n}\nstatic void M_Modelviewer_Shutdown(struct emenu_s *menu)\n{\n\tmodelview_t *mv = menu->data;\n\trag_removedeltaent(&mv->ragent);\n\tskel_reset(&mv->ragworld);\n\tWorld_RBE_Shutdown(&mv->ragworld);\n}\n//haxors, for skeletal objects+RBE\nstatic char\t*PDECL M_Modelviewer_AddString(pubprogfuncs_t *prinst, const char *val, int minlength, pbool demarkup)\n{\n\treturn Z_Malloc(minlength);\n}\nstatic struct edict_s\t*PDECL M_Modelviewer_ProgsToEdict(pubprogfuncs_t *prinst, int num)\n{\n\treturn (struct edict_s*)prinst->edicttable[num];\n}\n#endif\n\nvoid M_Menu_ModelViewer_f(void)\n{\n\tmodelview_t *mv;\n\tmenucustom_t *c;\n\temenu_t *menu;\n\n\tif (!*Cmd_Argv(1))\n\t{\n\t\tCon_Printf(\"modelviewer <MODELNAME> [SKINFILE] [ANIMATIONFILE]\\n\");\n\t\treturn;\n\t}\n\n\tmenu = M_CreateMenu(sizeof(*mv));\n\tmenu->menu.persist = true;\n\tmv = menu->data;\n\tc = MC_AddCustom(menu, 64, 32, mv, 0, NULL);\n\tmenu->selecteditem = menu->cursoritem = (menuoption_t*)c;\n\tc->draw = M_ModelViewerDraw;\n\tc->key = M_ModelViewerKey;\n\n\tmv->yaw = 180;// + crandom()*45;\n\tmv->dist = 150;\n\tQ_strncpyz(mv->modelname, Cmd_Argv(1), sizeof(mv->modelname));\n\tQ_strncpyz(mv->skinname, Cmd_Argv(2), sizeof(mv->skinname));\n\tQ_strncpyz(mv->animname, Cmd_Argv(3), sizeof(mv->animname));\n\n\tmv->frametime = 0;\n\tmv->skintime = 0;\n#ifdef RAGDOLL\n\tmenu->menu.videoreset = M_Modelviewer_Reset;\n\tmenu->remove = M_Modelviewer_Shutdown;\n\tmv->ragworld.progs = &mv->ragfuncs;\n\tmv->ragfuncs.AddString = M_Modelviewer_AddString;\n\tmv->ragfuncs.ProgsToEdict = M_Modelviewer_ProgsToEdict;\n\tmv->ragfuncs.edicttable = (edict_t**)&mv->ragworld.edicts;\n\tmv->ragworld.edicts = &mv->ragworldedict;\n\tmv->ragworld.edicts->v = &mv->ragworldvars;\n#ifdef VM_Q1\n\tmv->ragworld.edicts->xv = &mv->ragworldextvars;\n#endif\n\tmv->ragworld.num_edicts = 1;\n\tmv->ragworld.edicts->v->solid = SOLID_BBOX;\n\tVectorSet(mv->ragworld.edicts->v->mins, -1000, -1000, -1000);\n\tVectorSet(mv->ragworld.edicts->v->maxs, 1000, 1000, -100);\n\tmv->ragworld.edicts->xv->dimension_hit = mv->ragworld.edicts->xv->dimension_solid = 0xff;\n\n\tmv->ragworld.worldmodel = Mod_ForName(\"\", MLV_SILENT);\n\tWorld_RBE_Start(&mv->ragworld);\n#endif\n}\nstatic int QDECL CompleteModelViewerList (const char *name, qofs_t flags, time_t mtime, void *parm, searchpathfuncs_t *spath)\n{\n\tstruct xcommandargcompletioncb_s *ctx = parm;\n\tconst char *ext = COM_GetFileExtension(name, NULL);\n\tif (!strcmp(ext, \".mdl\") || !strcmp(ext, \".md2\") || !strcmp(ext, \".md3\")\n\t\t|| !strcmp(ext, \".iqm\") || !strcmp(ext, \".dpm\") || !strcmp(ext, \".zym\")\n\t\t|| !strcmp(ext, \".psk\") || !strcmp(ext, \".md5mesh\") || !strcmp(ext, \".md5anim\")\n\t\t|| !strcmp(ext, \".bsp\") || !strcmp(ext, \".map\") || !strcmp(ext, \".hmp\")\n\t\t|| !strcmp(ext, \".spr\") || !strcmp(ext, \".sp2\") || !strcmp(ext, \".spr32\")\n\t\t|| !strcmp(ext, \".gltf\") || !strcmp(ext, \".glb\") || !strcmp(ext, \".ase\") || !strcmp(ext, \".lwo\") || !strcmp(ext, \".obj\")\n\t\t|| !strncmp(name, \"xmodel/\", 7) || !strncmp(name, \"xanim/\", 6))\t//urgh!\n\t{\n\t\tctx->cb(name, NULL, NULL, ctx);\n\t}\n\treturn true;\n}\nvoid M_Menu_ModelViewer_c(int argn, const char *partial, struct xcommandargcompletioncb_s *ctx)\n{\n\tif (argn == 1)\n\t{\n\t\tCOM_EnumerateFiles(va(\"%s*\", partial), CompleteModelViewerList, ctx);\n\t\tCOM_EnumerateFiles(va(\"%s*/*\", partial), CompleteModelViewerList, ctx);\n\t\tCOM_EnumerateFiles(va(\"%s*/*\", partial), CompleteModelViewerList, ctx);\n\t\tCOM_EnumerateFiles(va(\"%s*/*/*\", partial), CompleteModelViewerList, ctx);\n\t}\n}\n#else\nvoid M_Menu_ModelViewer_f(void)\n{\n\tCon_Printf(\"modelviewer: not in this build\\n\");\n}\n#endif\n\nstatic void Mods_Draw(int x, int y, struct menucustom_s *c, struct emenu_s *m)\n{\n\tint i = c->dint;\n\tstruct modlist_s *mod = Mods_GetMod(i);\n\n\tif (!mod && !i)\n\t{\n\t\tfloat scale[] = {8,8};\n\n\t\tm->height = vid.height;\n\n\t\tif (y==0)\n\t\t{\t//just take the full screen.\n\t\t\ty = m->ypos;\n\t\t\tc->common.posy = 0;\n\t\t\tc->common.height = m->height;\n\n\t\t\tm->dontexpand = true;\n\t\t\tm->xpos = x = 0;\n\t\t\tm->width = vid.width;\n\t\t}\n\t\telse\n\t\t{\t//at least expand it.\n\t\t\tc->common.height = m->height - c->common.posy;\n\t\t}\n\n\t\t//take the full width of the menu\n\t\tx = m->xpos;\n\t\tc->common.posx = 0;\n\t\tc->common.width = m->width;\n\n\t\tR_DrawTextField(x, y, c->common.width, c->common.height,\n\t\t\t\t\tva(\n\t\t\t\t\t\"No games or mods known.\\n\"\n#ifdef FTE_TARGET_WEB\n\t\t\t\t\t\"Try providing packages/gamedirs via drag+drop.\\n\"\n\t\t\t\t\t\"%s\", Cmd_Exists(\"sys_openfile\")?\"Or click to add a package\\n\":\"\"\n#else\n\t#ifndef ANDROID\n\t\t\t\t\t\"You may need to use -basedir $PATHTOGAME on the commandline.\\n\"\n\t#endif\n\t\t\t\t\t\"\\nExpected data path:\\n^a%s\", com_gamepath\n#endif\n\t\t\t\t\t), CON_WHITEMASK, 0, font_default, scale);\n\t\treturn;\n\t}\n\tc->common.height = 8;\n\tc->common.width = vid.width - x - 16;\n\n\tif (!mod)\n\t\treturn;\n\tif (mod->manifest)\n\t\tDraw_FunStringWidth(x, y, mod->manifest->formalname, c->common.width, 0, m->selecteditem == (menuoption_t*)c);\n\telse\n\t\tDraw_FunStringWidth(x, y, mod->gamedir, c->common.width, 0, m->selecteditem == (menuoption_t*)c);\n}\nstatic qboolean Mods_Key(struct menucustom_s *c, struct emenu_s *m, int key, unsigned int unicode)\n{\n\tint gameidx = c->dint;\n\tif (key == K_ENTER || key == K_KP_ENTER || key == K_GP_DIAMOND_CONFIRM || key == K_MOUSE1 || key == K_TOUCHTAP)\n\t{\n\t\tqboolean wasgameless = !*FS_GetGamedir(false);\n\t\tif (!Mods_GetMod(c->dint))\n\t\t{\n\t\t\tif (Cmd_Exists(\"sys_openfile\"))\n\t\t\t\tCbuf_AddText(\"sys_openfile\\n\", RESTRICT_LOCAL);\n\t\t\treturn false;\n\t\t}\n\t\tM_RemoveMenu(m);\n\n\t\tCbuf_AddText(va(\"\\nfs_changegame %u\\n\", gameidx+1), RESTRICT_LOCAL);\n\t\tif (wasgameless && !!*FS_GetGamedir(false))\n\t\t{\n\t\t\t//starting to a blank state generally means that the current(engine-default) config settings are utterly useless and windowed by default.\n\t\t\t//so generally when switching to a *real* game, we want to restart video just so things like fullscreen etc are saved+used properly.\n\t\t\tCbuf_AddText(\"\\nvid_restart\\n\", RESTRICT_LOCAL);\n\t\t}\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\nvoid M_Menu_Mods_f (void)\n{\n\tmenucustom_t *c;\n\temenu_t *menu;\n\tsize_t i;\n\tint y;\n\n\t//FIXME: sort by mtime?\n\n\tmenu = M_CreateMenu(0);\n\tif (COM_FCheckExists(\"gfx/p_option.lmp\"))\n\t{\n\t\tMC_AddPicture(menu, 16, 4, 32, 144, \"gfx/qplaque.lmp\");\n\t\tMC_AddCenterPicture(menu, 0, 24, \"gfx/p_option.lmp\");\n\t\ty = 32;\n\t}\n\telse\n\t{\n\t\tMC_AddRedText(menu, 64, 320, 4, \"Mod List\", false);\n\t\ty = 32;\n\t}\n\n\tMC_AddFrameStart(menu, y);\n\tfor (i = 0; i<1 || Mods_GetMod(i); i++)\n\t{\n\t\tstruct modlist_s *mod = Mods_GetMod(i);\n\t\tc = MC_AddCustom(menu, 64, y+i*8, menu->data, i, (mod&&mod->manifest)?mod->manifest->basedir:NULL);\n//\t\tif (!menu->selecteditem)\n//\t\t\tmenu->selecteditem = (menuoption_t*)c;\n\t\tc->common.height = 8;\n\t\tc->draw = Mods_Draw;\n\t\tc->key = Mods_Key;\n\t}\n\tMC_AddFrameEnd(menu, y);\n}\n\n#if 0\nextern ftemanifest_t\t*fs_manifest;\t//currently active manifest.\nstruct installermenudata\n{\n\tmenuedit_t *syspath;\n};\nstatic void Installer_Remove\t(struct menu_s *m)\n{\n\tCbuf_AddText(\"quit force\\n\", RESTRICT_LOCAL);\n}\n\n\nvoid FS_CreateBasedir(const char *path);\n#include <process.h>\nstatic qboolean Installer_Go(menuoption_t *opt, menu_t *menu, int key)\n{\n\tstruct installermenudata *md = menu->data;\n\t\n\tif (key == K_ENTER || key == K_KP_ENTER || key == K_GP_DIAMOND_CONFIRM || key == K_MOUSE1 || key == K_TOUCHTAP)\n\t{\n\t\textern int startuppending;\n\t\tvfsfile_t *f;\n\t\tchar path[MAX_OSPATH];\n\t\tchar exepath[MAX_OSPATH];\n\t\tchar newexepath[MAX_OSPATH];\n\n\t\tQ_snprintfz(path, sizeof(path), \"%s/\", md->syspath->text);\n\n\t\tCon_Printf(\"path %s\\n\", path);\n\n\t\tmenu->remove = NULL;\n\t\tM_RemoveMenu(menu);\n\n\t\tFS_CreateBasedir(path);\n\n#ifdef _WIN32\n\t\tGetModuleFileNameW(NULL, exepath, sizeof(exepath));\n\t\tFS_SystemPath(va(\"%s.exe\", fs_manifest->installation), FS_ROOT, newexepath, sizeof(newexepath));\n\t\tCopyFileW(exepath, newexepath, FALSE);\n\n//\t\tSetHookState(false);\n\t\tHost_Shutdown ();\n//\t\tCloseHandle (qwclsemaphore);\n//\t\tSetHookState(false);\n//\t\t_execv(newexepath, host_parms.argv);\n\t\t{\n\t\t\tPROCESS_INFORMATION childinfo;\n\t\t\tSTARTUPINFOW startinfo = {sizeof(startinfo)};\n\t\t\tmemset(&childinfo, 0, sizeof(childinfo));\n\t\t\tif (CreateProcessW(newexepath, va(\"\\\"%s\\\" +sys_register_file_associations %s\", newexepath, COM_Parse(GetCommandLineW())), NULL, NULL, FALSE, 0, NULL, path, &startinfo, &childinfo))\n\t\t\t{\n\t\t\t\tCloseHandle(childinfo.hProcess);\n\t\t\t\tCloseHandle(childinfo.hThread);\n\t\t\t}\n\t\t}\n\t\texit(1);\n#elif 0\n#ifdef __linux__\n\t\tif (readlink(\"/proc/self/exe\", exepath, sizeof(exepath)-1) <= 0)\n#endif\n\t\t\tQ_strncpyz(exepath, host_parms.argv[0], sizeof(exepath));\n\n\t\tint fd = creat(newexepath, S_IRWXU | S_IRGRP|S_IXGRP);\n\t\twrite(fd);\n\t\tclose(fd);\n#endif\n\n\t\tstartuppending = 2;\n\t\treturn TRUE;\n\t}\n\n\treturn FALSE;\n}\n\nvoid M_Menu_Installer(void)\n{\n\tmenu_t *menu;\n\tstruct installermenudata *md;\n\n\tKey_Dest_Add(kdm_menu);\n\n\tmenu = M_CreateMenu(sizeof(*md));\n\tmd = menu->data = (menu+1);\n\n\tmd->syspath = MC_AddEdit(menu, 0, 160, 64, \"Path\", \"C:/Games/AfterQuake/testinstall/base\");//va(\"c:/%s\", fs_manifest->installation));\n\n\t//FIXME: add path check display\n\n\tMC_AddCommand\t\t(menu, 0, 160, 128, \"Install\", Installer_Go);\n\tMC_AddConsoleCommand(menu, 0, 160, 136,\t\"Cancel\", \"menu_quit\\n\");\n\n\tmenu->selecteditem = (menuoption_t*)md->syspath;\n\tmenu->cursoritem = (menuoption_t*)MC_AddWhiteText(menu, 250, 0, menu->selecteditem->common.posy, NULL, false);\n\tmenu->remove = Installer_Remove;\n}\n#endif\n#endif\n"
  },
  {
    "path": "engine/client/m_script.c",
    "content": "//read menu.h\n\n#include \"quakedef.h\"\n#include \"shader.h\"\n\n#ifndef NOBUILTINMENUS\n\nint selectitem;\nemenu_t *menu_script;\n\nvoid M_Script_Option (emenu_t *menu, char *optionvalue, qboolean isexplicit)\n{\n\tmenuoption_t *mo;\n\tchar *scriptname = menu->data;\n\n\tchar buf[8192];\n\t//FIXME: not sure about these, as the user typically can't see what they'll do.\n\tint expandlevel = RESTRICT_SERVER;\n\tint execlevel = RESTRICT_LOCAL;\n\n\tCbuf_AddText(\"wait\\n\", execlevel);\n\n\tif (!scriptname || !*scriptname)\n\t{\n\t\tif (isexplicit)\n\t\t\tCbuf_AddText(va(\"%s\\n\", optionvalue), execlevel);\n\t\treturn;\n\t}\n\n\t//update the option\n\tCbuf_AddText(va(\"set option %s\\n\", COM_QuotedString(optionvalue, buf, sizeof(buf), false)), execlevel);\n\n\t//expand private arguments\n\tfor (mo = menu->options, *buf = 0; mo; mo = mo->common.next)\n\t{\n\t\tif (mo->common.type == mt_edit)\n\t\t{\n\t\t\tif (strlen(buf) + strlen(mo->edit.text) + 2 >= sizeof(buf))\n\t\t\t\tbreak;\n\t\t\tmemmove(buf+strlen(mo->edit.text)+1, buf, strlen(buf)+1);\n\t\t\tmemcpy(buf, mo->edit.text, strlen(mo->edit.text));\n\t\t\tbuf[strlen(mo->edit.text)] = ' ';\n\t\t}\n\t}\n\tCmd_TokenizeString(buf, false, false);\n\tCmd_ExpandString(scriptname, buf, sizeof(buf), &expandlevel, true, false, false);\n\n\t//and execute it as-is\n\tCbuf_AddText(buf, execlevel);\n\tCbuf_AddText(\"\\n\", execlevel);\n}\n\nvoid M_Script_Remove (emenu_t *menu)\n{\n\tif (menu == menu_script)\n\t\tmenu_script = NULL;\n\n\tM_Script_Option(menu, \"cancel\", false);\n}\nqboolean M_Script_Key (struct emenu_s *menu, int key, unsigned int unicode)\n{\n\tif (menu->selecteditem && menu->selecteditem->common.type == mt_edit)\n\t\treturn false;\n\tif (key >= '0' && key <= '9' && menu->data)\n\t{\n\t\tif (key == '0')\t//specal case so that \"hello\" < \"0\"... (plus matches common impulses)\n\t\t\tM_Script_Option(menu, \"10\", false);\n\t\telse\n\t\t\tM_Script_Option(menu, va(\"%i\", key-'0'), false);\n\t\treturn true;\n\t}\n\treturn false;\n}\n\nvoid M_MenuS_Callback_f (void)\n{\n\tif (menu_script)\n\t{\n\t\tM_Script_Option(menu_script, Cmd_Argv(1), true);\n\t}\n}\nvoid M_MenuS_Clear_f (void)\n{\n\tif (menu_script)\n\t{\n\t\tM_RemoveMenu(menu_script);\n\t}\n}\n\nvoid M_MenuS_Script_f (void)\t//create a menu.\n{\n\tint items;\n\tchar *alias = Cmd_Argv(1);\n\n\tselectitem = 0;\n\titems=0;\n\n\tif (menu_script)\n\t{\n\t\tmenuoption_t *option;\n\t\tfor (option = menu_script->options; option; option = option->common.next)\n\t\t{\n\t\t\tif (option->common.type == mt_button)\n\t\t\t{\n\t\t\t\tif (menu_script->selecteditem == option)\n\t\t\t\t\tselectitem = items;\n\t\t\t\titems++;\n\t\t\t}\n\t\t}\n\t\tselectitem = items - selectitem-1;\n\t\tM_MenuS_Clear_f();\n\t}\n\n\tmenu_script = M_CreateMenu(0);\n\tmenu_script->remove = M_Script_Remove;\n\tmenu_script->key = M_Script_Key;\n\t\n\tKey_Dest_Remove(kdm_console);\n\n\tif (Cmd_Argc() == 1)\n\t\tmenu_script->data = Cmd_ParseMultiline(true);\n\telse\n\t\tmenu_script->data = Z_StrDup(alias);\n}\n\nvoid M_MenuS_Box_f (void)\n{\n\tint x = atoi(Cmd_Argv(1));\n\tint y = atoi(Cmd_Argv(2));\n\tint width = atoi(Cmd_Argv(3));\n\tint height = atoi(Cmd_Argv(4));\n\n\tif (!menu_script)\n\t{\n\t\tCon_Printf(\"%s with no active menu\\n\", Cmd_Argv(0));\n\t\treturn;\n\t}\n\n\tMC_AddBox(menu_script, x, y, width, height);\n}\n\nvoid M_MenuS_CheckBox_f (void)\n{\n\tint x = atoi(Cmd_Argv(1));\n\tint y = atoi(Cmd_Argv(2));\n\tchar *text = Cmd_Argv(3);\n\tchar *cvarname = Cmd_Argv(4);\n\tint bitmask = atoi(Cmd_Argv(5));\n\tcvar_t *cvar;\n\n\tif (!menu_script)\n\t{\n\t\tCon_Printf(\"%s with no active menu\\n\", Cmd_Argv(0));\n\t\treturn;\n\t}\n\tcvar = Cvar_Get(cvarname, text, 0, \"User variables\");\n\tif (!cvar)\n\t\treturn;\n\tMC_AddCheckBox(menu_script, x, x+160, y, text, cvar, bitmask);\n}\n\nvoid M_MenuS_Slider_f (void)\n{\n\tint x = atoi(Cmd_Argv(1));\n\tint y = atoi(Cmd_Argv(2));\n\tchar *text = Cmd_Argv(3);\n\tchar *cvarname = Cmd_Argv(4);\n\tfloat min = atof(Cmd_Argv(5));\n\tfloat max = atof(Cmd_Argv(6));\n\tcvar_t *cvar;\n\n\tif (!menu_script)\n\t{\n\t\tCon_Printf(\"%s with no active menu\\n\", Cmd_Argv(0));\n\t\treturn;\n\t}\n\tcvar = Cvar_Get(cvarname, text, 0, \"User variables\");\n\tif (!cvar)\n\t\treturn;\n\tMC_AddSlider(menu_script, x, x+160, y, text, cvar, min, max, 0);\n}\n\nvoid M_MenuS_Picture_f (void)\n{\n\tint x = atoi(Cmd_Argv(1));\n\tint y = atoi(Cmd_Argv(2));\n\tchar *picname = Cmd_Argv(3);\n\tmpic_t *p;\n\n\tif (!menu_script)\n\t{\n\t\tCon_Printf(\"%s with no active menu\\n\", Cmd_Argv(0));\n\t\treturn;\n\t}\n\n\tp = R2D_SafeCachePic(picname);\n\tif (!p)\n\t\treturn;\n\n\tif (!strcmp(Cmd_Argv(1), \"-\"))\n\t\tMC_AddCenterPicture(menu_script, y, p->height, picname);\n\telse\n\t\tMC_AddPicture(menu_script, x, y, p->width, p->height, picname);\n}\n\nvoid M_MenuS_Edit_f (void)\n{\n\tint x = atoi(Cmd_Argv(1));\n\tint y = atoi(Cmd_Argv(2));\n\tchar *text = Cmd_Argv(3);\n\tchar *def = Cmd_Argv(4);\n\n\tif (!menu_script)\n\t{\n\t\tCon_Printf(\"%s with no active menu\\n\", Cmd_Argv(0));\n\t\treturn;\n\t}\n\n\tMC_AddEditCvar(menu_script, x, x+160, y, text, def, false);\n}\nvoid M_MenuS_EditPriv_f (void)\n{\n\tint x = atoi(Cmd_Argv(1));\n\tint y = atoi(Cmd_Argv(2));\n\tchar *text = Cmd_Argv(3);\n\tchar *def = Cmd_Argv(4);\n\n\tif (!menu_script)\n\t{\n\t\tCon_Printf(\"%s with no active menu\\n\", Cmd_Argv(0));\n\t\treturn;\n\t}\n\n\tMC_AddEdit(menu_script, x, x+160, y, text, def);\n}\n\nvoid M_MenuS_Text_f (void)\n{\n\tmenuoption_t *option;\n\tint x = atoi(Cmd_Argv(1));\n\tint y = atoi(Cmd_Argv(2));\n\tchar *text = Cmd_Argv(3);\n\tchar *command = Cmd_Argv(4);\n\n\tif (!menu_script)\n\t{\n\t\tCon_Printf(\"%s with no active menu\\n\", Cmd_Argv(0));\n\t\treturn;\n\t}\n\tif (Cmd_Argc() == 4)\n\t\tMC_AddBufferedText(menu_script, x, 0, y, text, false, false);\n\telse\n\t{\n\t\toption = (menuoption_t *)MC_AddConsoleCommand(menu_script, x, 0, y, text, va(\"menucallback \\\"%s\\\"\\n\", command));\n\t\tif (selectitem-- == 0)\n\t\t\tmenu_script->selecteditem = option;\n\t}\n}\n\nvoid M_MenuS_TextBig_f (void)\n{\n\tmenuoption_t *option;\n\tint x = atoi(Cmd_Argv(1));\n\tint y = atoi(Cmd_Argv(2));\n\tchar *text = Cmd_Argv(3);\n\tchar *command = Cmd_Argv(4);\n\n\tif (!menu_script)\n\t{\n\t\tCon_Printf(\"%s with no active menu\\n\", Cmd_Argv(0));\n\t\treturn;\n\t}\n\tif (!*command)\n\t\tMC_AddConsoleCommandQBigFont(menu_script, x, y, text, command);\n\telse\n\t{\n\t\toption = (menuoption_t *)MC_AddConsoleCommandQBigFont(menu_script, x, y, text, va(\"menucallback %s\\n\", command));\n\t\tif (selectitem-- == 0)\n\t\t\tmenu_script->selecteditem = option;\n\t}\n}\n\nvoid M_MenuS_Bind_f (void)\n{\n\tint x = atoi(Cmd_Argv(1));\n\tint y = atoi(Cmd_Argv(2));\n\tchar *caption = Cmd_Argv(3);\n\tchar *command = Cmd_Argv(4);\n\n\tif (!menu_script)\n\t{\n\t\tCon_Printf(\"%s with no active menu\\n\", Cmd_Argv(0));\n\t\treturn;\n\t}\n\n\tif (!*caption)\n\t\tcaption = command;\n\n\tMC_AddBind(menu_script, x, x+160, y, command, caption, NULL);\n}\n\nvoid M_MenuS_Comboi_f (void)\n{\n\tint opt;\n\tchar *opts[64];\n\tchar *values[64];\n\tchar valuesb[64][8];\n\tint x = atoi(Cmd_Argv(1));\n\tint y = atoi(Cmd_Argv(2));\n\tchar *caption = Cmd_Argv(3);\n\tchar *command = Cmd_Argv(4);\n\tchar *line;\n\n\tcvar_t *var;\n\n\tif (!menu_script)\n\t{\n\t\tCon_Printf(\"%s with no active menu\\n\", Cmd_Argv(0));\n\t\treturn;\n\t}\n\n\tvar = Cvar_Get(command, \"0\", 0, \"custom cvars\");\n\tif (!var)\n\t\treturn;\n\n\tif (!*caption)\n\t\tcaption = command;\n\n\tfor (opt = 0; opt < sizeof(opts)/sizeof(opts[0])-2 && *(line=Cmd_Argv(5+opt)); opt++)\n\t{\n\t\topts[opt] = line;\n\t\tQ_snprintfz(valuesb[opt], sizeof(valuesb[opt]), \"%i\", opt);\n\t\tvalues[opt] = valuesb[opt];\n\t}\n\topts[opt] = NULL;\n\n\tMC_AddCvarCombo(menu_script, x, x+160, y, caption, var, (const char **)opts, (const char **)values);\n}\n\nchar *Hunk_TempString(char *s)\n{\n\tchar *h;\n\th = Hunk_TempAllocMore(strlen(s)+1);\n\tstrcpy(h, s);\n\treturn h;\n}\n\nvoid M_MenuS_Combos_f (void)\n{\n\tint opt;\n\tchar *opts[64];\n\tchar *values[64];\n\tint x = atoi(Cmd_Argv(1));\n\tint y = atoi(Cmd_Argv(2));\n\tchar *caption = Cmd_Argv(3);\n\tchar *command = Cmd_Argv(4);\n\tchar *line;\n\n\tcvar_t *var;\n\n\tif (!menu_script)\n\t{\n\t\tCon_Printf(\"%s with no active menu\\n\", Cmd_Argv(0));\n\t\treturn;\n\t}\n\n\tvar = Cvar_Get(command, \"0\", 0, \"custom cvars\");\n\tif (!var)\n\t\treturn;\n\n\tif (!*caption)\n\t\tcaption = command;\n\n\tline = Cmd_Argv(5);\n\tif (!*line)\n\t{\n\t\tline = Cbuf_GetNext(Cmd_ExecLevel, true);\n\t\tif (*line != '{')\n\t\t\tCbuf_InsertText(line, Cmd_ExecLevel, true);\t//whoops. Stick the trimmed string back in to the cbuf.\n\t\telse\n\t\t\tline = \"{\";\n\t}\n\tif (!strcmp(line, \"{\"))\n\t{\n\t\tchar *line;\n\t\tHunk_TempAlloc(4);\n\t\tfor (opt = 0; opt < sizeof(opts)/sizeof(opts[0])-2; opt++)\n\t\t{\n\t\t\tline = Cbuf_GetNext(Cmd_ExecLevel, true);\n\t\t\tline = COM_Parse(line);\n\t\t\tif (!strcmp(com_token, \"}\"))\n\t\t\t\tbreak;\n\t\t\topts[opt] = Hunk_TempString(com_token);\n\t\t\tline = COM_Parse(line);\n\t\t\tvalues[opt] = Hunk_TempString(com_token);\n\t\t}\n\t}\n\telse\n\t{\n\t\tfor (opt = 0; opt < sizeof(opts)/sizeof(opts[0])-2; opt++)\n\t\t{\n\t\t\tline = Cmd_Argv(5+opt*2);\n\t\t\tif (!*line)\n\t\t\t\tbreak;\n\t\t\topts[opt] = line;\n\t\t\tvalues[opt] = Cmd_Argv(5+opt*2 + 1);\n\t\t}\n\t}\n\topts[opt] = NULL;\n\n\tMC_AddCvarCombo(menu_script, x, x+160, y, caption, var, (const char **)opts, (const char **)values);\n}\n\n/*\nmenuclear\nconmenu menucallback\n\nmenubox 0 0 320 8\nmenutext 0 0 \"GO GO GO!!!\" \t\t\"radio21\"\nmenutext 0 8 \"Fall back\" \t\t\"radio22\"\nmenutext 0 8 \"Stick together\" \t\t\"radio23\"\nmenutext 0 16 \"Get in position\"\t\t\"radio24\"\nmenutext 0 24 \"Storm the front\"\t \t\"radio25\"\nmenutext 0 24 \"Report in\"\t \t\"radio26\"\nmenutext 0 24 \"Cancel\"\t\n*/\nvoid M_Script_Init(void)\n{\n\tCmd_AddCommandD(\"menuclear\",\tM_MenuS_Clear_f,\t\"Pop the currently scripted menu.\");\n\tCmd_AddCommandD(\"menucallback\",\tM_MenuS_Callback_f,\t\"Explicitly invoke the active script menu's callback function with the given option set.\");\n\tCmd_AddCommandD(\"conmenu\",\t\tM_MenuS_Script_f,\t\"conmenu <callback>\\nCreates a new (built-in) scripted menu. any following commands that define scipted menu items will add their items to this new menu. The callback will be called with argument 'cancel' when the menu is closed.\");\n\tCmd_AddCommandD(\"menubox\",\t\tM_MenuS_Box_f,\t\t\"x y width height\");\n\tCmd_AddCommandD(\"menuedit\",\t\tM_MenuS_Edit_f,\t\t\"x y caption cvarname\");\n\tCmd_AddCommandD(\"menueditpriv\",\tM_MenuS_EditPriv_f, \"x y caption def\");\n\tCmd_AddCommandD(\"menutext\",\t\tM_MenuS_Text_f,\t\t\"x y caption cbcommand\");\n\tCmd_AddCommandD(\"menutextbig\",\tM_MenuS_TextBig_f,\t\"x y caption cbcommand\");\n\tCmd_AddCommandD(\"menupic\",\t\tM_MenuS_Picture_f,\t\"x y picname\");\n\tCmd_AddCommandD(\"menucheck\",\tM_MenuS_CheckBox_f,\t\"x y caption cvarname bitmask\");\n\tCmd_AddCommandD(\"menuslider\",\tM_MenuS_Slider_f,\t\"x y caption cvarname min max\");\n\tCmd_AddCommandD(\"menubind\",\t\tM_MenuS_Bind_f,\t\t\"x y caption bindcommand\");\n\tCmd_AddCommandD(\"menucomboi\",\tM_MenuS_Comboi_f,\t\"x y caption cvarname [caption0] [caption1] ...\");\n\tCmd_AddCommandD(\"menucombos\",\tM_MenuS_Combos_f,\t\"x y caption cvarname [caption0] [value0] [caption1] [value1] ...\\nif 'caption0' is { then the options will be parsed from trailing lines\\n\");\n}\n#endif\n"
  },
  {
    "path": "engine/client/m_single.c",
    "content": "//read menu.h\n\n#include \"quakedef.h\"\n#include \"winquake.h\"\n#include \"shader.h\"\n#ifndef NOBUILTINMENUS\n#if !defined(CLIENTONLY) && defined(SAVEDGAMES)\n//=============================================================================\n/* LOAD/SAVE MENU */\n\ntypedef struct {\n\tint issave;\n\tint cursorpos;\n\tmenutext_t *cursoritem;\n\n\tint picslot;\n\tshader_t *picshader;\n} loadsavemenuinfo_t;\n\n#define SAVEFIRST_AUTO 1\n#define SAVECOUNT_AUTO 3\n#define SAVEFIRST_STANDARD (SAVEFIRST_AUTO + SAVECOUNT_AUTO)\n#define SAVECOUNT_STANDARD 20\n#define\tMAX_SAVEGAMES\t\t(1+SAVECOUNT_AUTO+SAVECOUNT_STANDARD)\nstatic struct\n{\n\tqboolean loadable;\n\tqbyte\tsaveable; //0=autosave, 1=regular, 2=quick\n\tchar\tsname[9];\n\tchar\tdesc[22+1];\n\tchar\tkills[39-22+1];\n\tchar\ttime[64];\n\tchar\tmap[32];\n} m_saves[MAX_SAVEGAMES];\n\nstatic void M_ScanSave(unsigned int slot, const char *name, qbyte savable)\n{\n\tchar\t*in, *out, *end;\n\tint\t\tj;\n\tchar\tline[MAX_OSPATH];\n\tflocation_t loc;\n\ttime_t\tmtime;\n\tvfsfile_t\t*f;\n\tint\t\tversion;\n\n\tm_saves[slot].saveable = savable;\n\tm_saves[slot].loadable = false;\n\tQ_strncpyz (m_saves[slot].sname, name, sizeof(m_saves[slot].sname));\n\tQ_strncpyz (m_saves[slot].desc, \"--- UNUSED SLOT ---\", sizeof(m_saves[slot].desc));\n\tQ_strncpyz (m_saves[slot].kills, \"\", sizeof(m_saves[slot].kills));\n\tQ_strncpyz (m_saves[slot].time, \"\", sizeof(m_saves[slot].time));\n\n\tsnprintf (line, sizeof(line), \"saves/%s/info.fsv\", m_saves[slot].sname);\n\tif (!FS_FLocateFile(line, FSLF_DONTREFERENCE|FSLF_IGNOREPURE, &loc))\n\t{\t//legacy saved games from some other engine\n\t\tsnprintf (line, sizeof(line), \"%s.sav\", m_saves[slot].sname);\n\t\tif (!FS_FLocateFile(line, FSLF_DONTREFERENCE|FSLF_IGNOREPURE, &loc))\n\t\t\treturn;\t//not found\n\t}\n\tf = FS_OpenReadLocation(line, &loc);\n\tif (f)\n\t{\n\t\tVFS_GETS(f, line, sizeof(line));\n\t\tversion = atoi(line);\n\t\tif (version != SAVEGAME_VERSION_NQ && version != SAVEGAME_VERSION_QW && version != SAVEGAME_VERSION_FTE_LEG && (version < SAVEGAME_VERSION_FTE_HUB || version >= SAVEGAME_VERSION_FTE_HUB+GT_MAX))\n\t\t{\n\t\t\tQ_strncpyz (m_saves[slot].desc, \"Incompatible version\", sizeof(m_saves[slot].desc));\n\t\t\tVFS_CLOSE (f);\n\t\t\treturn;\n\t\t}\n\n\t\t// read the desc, change _ back to space, fill the separate fields\n\t\tVFS_GETS(f, line, sizeof(line));\n\t\tfor (j=0 ; line[j] ; j++)\n\t\t\tif (line[j] == '_')\n\t\t\t\tline[j] = ' ';\n\t\tfor (; j < sizeof(line[j]); j++)\n\t\t\tline[j] = '\\0';\n\t\tmemcpy(m_saves[slot].desc, line, 22);\n\t\tm_saves[slot].desc[22] = 0;\n\n\t\tfor (in = line+22, out = m_saves[slot].kills, end = line+39; in < end && *in == ' '; )\n\t\t\tin++;\n\t\tfor (out = m_saves[slot].kills; in < end; )\n\t\t\t*out++ = *in++;\n\t\tfor (end = m_saves[slot].kills; out > end && out[-1] == ' '; )\n\t\t\tout--;\n\t\t*out = 0;\n\n\t\tif (strlen(line) > 39)\n\t\t\tQ_strncpyz(m_saves[slot].time, line+39, sizeof(m_saves[slot].time));\n\t\telse if (FS_GetLocMTime(&loc, &mtime))\n\t\t\tstrftime(m_saves[slot].time, sizeof(m_saves[slot].time), \"%Y-%m-%d %H:%M:%S\", localtime( &mtime ));\n\t\t// else time unknown, just leave it blank\n\n\t\tif (version == 5 || version == 6)\n\t\t{\n\t\t\tfor (j = 0; j < 16; j++)\n\t\t\t\tVFS_GETS(f, line, sizeof(line));\t//16 parms\n\t\t\tVFS_GETS(f, line, sizeof(line));\t//skill\n\t\t\tVFS_GETS(f, m_saves[slot].map, sizeof(m_saves[slot].map));\n\t\t}\n\n\t\tm_saves[slot].loadable = true;\n\t\tVFS_CLOSE (f);\n\t}\n}\n\nconst char *M_ChooseAutoSave(void)\n{\n\tint i, j;\n\n\tfor (i = SAVEFIRST_AUTO; i < SAVEFIRST_AUTO+SAVECOUNT_AUTO; i++)\n\t{\n\t\tM_ScanSave(i, va(\"a%i\", i-SAVEFIRST_AUTO), false);\n\t\tif (!m_saves[i].loadable)\n\t\t\treturn m_saves[i].sname;\n\t}\n\n\tfor (i = SAVEFIRST_AUTO; i < SAVEFIRST_AUTO+SAVECOUNT_AUTO; i++)\n\t{\n\t\tfor (j = SAVEFIRST_AUTO; j < SAVEFIRST_AUTO+SAVECOUNT_AUTO; j++)\n\t\t\tif (strcmp(m_saves[i].time, m_saves[j].time) > 0)\n\t\t\t\tbreak;\n\t\tif (j == SAVEFIRST_AUTO+SAVECOUNT_AUTO)\n\t\t\treturn m_saves[i].sname;\t\t\n\t}\n\n\treturn m_saves[SAVEFIRST_AUTO].sname;\n}\n\nstatic void M_ScanSaves (void)\n{\n\tint i;\n\tM_ScanSave(0, \"quick\", 2);\n\tfor (i=SAVEFIRST_AUTO ; i<SAVEFIRST_AUTO+SAVECOUNT_AUTO ; i++)\n\t\tM_ScanSave(i, va(\"a%i\", i-SAVEFIRST_AUTO), false);\n\tfor (i=SAVEFIRST_STANDARD ; i<SAVEFIRST_STANDARD+SAVECOUNT_STANDARD ; i++)\n\t\tM_ScanSave(i, va(\"s%i\", i-SAVEFIRST_STANDARD), true);\n}\n\nstatic void M_Menu_LoadSave_UnloadShaders(emenu_t *menu)\n{\n\tloadsavemenuinfo_t *info = menu->data;\n\tif (info->picshader)\n\t{\n\t\tImage_UnloadTexture(info->picshader->defaulttextures->base);\n\t\tR_UnloadShader(info->picshader);\n\t\tinfo->picshader = NULL;\n\t}\n}\n\nstatic void M_Menu_LoadSave_Preview_Draw(int x, int y, menucustom_t *item, emenu_t *menu)\n{\n\tloadsavemenuinfo_t *info = menu->data;\n\tint slot;\n\tif (!menu->selecteditem)\n\t\treturn;\n\tslot = (menu->selecteditem->common.posy - 32)/8;\n\tif (slot >= 0 && slot < MAX_SAVEGAMES)\n\t{\n\t\tint width, height;\n\t\tif (slot != info->picslot || !info->picshader)\n\t\t{\n\t\t\tinfo->picslot = slot;\n\t\t\tif (info->picshader)\n\t\t\t{\n\t\t\t\tImage_UnloadTexture(info->picshader->defaulttextures->base);\n\t\t\t\tR_UnloadShader(info->picshader);\n\t\t\t}\n\t\t\tinfo->picshader = R_RegisterPic(va(\"saves/%s/screeny.tga\", m_saves[slot].sname), NULL);\n\t\t}\n\t\tif (info->picshader)\n\t\t{\n\t\t\tshader_t *pic = NULL;\n\t\t\tswitch(R_GetShaderSizes(info->picshader, &width, &height, false))\n\t\t\t{\n\t\t\tcase 1:\n\t\t\t\tpic = info->picshader;\n\t\t\t\tbreak;\n\t\t\tcase 0:\n\t\t\t\tif (*m_saves[slot].map)\n\t\t\t\t\tpic = R_RegisterPic(va(\"levelshots/%s\", m_saves[slot].map), NULL);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (pic)\n\t\t\t{\n\t\t\t\tint w = 160;\n\t\t\t\tint h = 120;\n\t\t\t\tif (R_GetShaderSizes(pic, &width, &height, false) <= 0)\n\t\t\t\t{\n\t\t\t\t\twidth = 64;\n\t\t\t\t\theight = 64;\n\t\t\t\t}\n\t\n\t\t\t\tif ((float)width/height > (float)w/h)\n\t\t\t\t{\n\t\t\t\t\tw = 160;\n\t\t\t\t\th = ((float)w*height) / width;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\th = 120;\n\t\t\t\t\tw = ((float)h*width) / height;\n\t\t\t\t}\n\t\t\t\tR2D_ScalePic (x + (160-w)/2, y + (120-h)/2, w, h, pic);\n\t\t\t}\n\t\t}\n\t\tDraw_FunStringWidth(x, y+120+0, m_saves[slot].time, 160, 2, false);\n\t\tDraw_FunStringWidth(x, y+120+8, m_saves[slot].kills, 160, 2, false);\n\n\t\tswitch(m_saves[slot].saveable)\n\t\t{\n\t\tcase 2:\n\t\t\tDraw_FunStringWidth(x, y+120+16, \"Quick Save\", 160, 2, false);\n\t\t\tbreak;\n\t\tcase 0:\n\t\t\tDraw_FunStringWidth(x, y+120+16, \"Autosave\", 160, 2, false);\n\t\t\tbreak;\n\t\t}\n\t\tDraw_FunStringWidth(x, y+120+24, m_saves[slot].sname, 160, 2, false);\n\t}\n}\n\nvoid M_Menu_Save_f (void)\n{\n\tmenuoption_t *op = NULL;\n\temenu_t *menu;\n\tint\t\ti;\n\n\tif (!sv.state)\n\t\treturn;\n\n\tif (cl.intermissionmode != IM_NONE)\n\t\treturn;\n\n\tmenu = M_CreateMenu(sizeof(loadsavemenuinfo_t));\n\tmenu->data = menu+1;\n\tmenu->remove = M_Menu_LoadSave_UnloadShaders;\n\tmenu->reset\t = M_Menu_LoadSave_UnloadShaders;\n\t\n\tswitch(M_GameType())\n\t{\n#ifdef Q2CLIENT\n\tcase MGT_QUAKE2:\n\t\tMC_AddCenterPicture(menu, 4, 24, \"pics/m_banner_save_game.pcx\");\n\t\tbreak;\n#endif\n\tdefault:\n\t\tMC_AddCenterPicture(menu, 4, 24, \"gfx/p_save.lmp\");\n\t\tbreak;\n\t}\n\n\tmenu->cursoritem = (menuoption_t *)MC_AddRedText(menu, 8, 0, 32, NULL, false);\t\n\n\tM_ScanSaves ();\n\n\tfor (i=0 ; i< MAX_SAVEGAMES; i++)\n\t{\n\t\tif (m_saves[i].saveable)\n\t\t\top = (menuoption_t *)MC_AddConsoleCommandf(menu, 16, 192, 32+8*i, false, m_saves[i].desc, \"savegame %s\\nclosemenu\\n\", m_saves[i].sname);\n\t\telse\n\t\t\tMC_AddWhiteText(menu, 16, 170, 32+8*i, m_saves[i].desc, false);\n\t\tif (!menu->selecteditem)\n\t\t\tmenu->selecteditem = op;\n\t}\n\n\tMC_AddCustom(menu, 192, 60-16, NULL, 0, NULL)->draw = M_Menu_LoadSave_Preview_Draw;\n}\nvoid M_Menu_Load_f (void)\n{\n\tmenuoption_t *op = NULL;\n\temenu_t *menu;\n\tint\t\ti;\n\tchar time[64];\n\n\tmenu = M_CreateMenu(sizeof(loadsavemenuinfo_t));\n\tmenu->data = menu+1;\n\tmenu->remove = M_Menu_LoadSave_UnloadShaders;\n\tmenu->reset\t = M_Menu_LoadSave_UnloadShaders;\n\n\tswitch(M_GameType())\n\t{\n#ifdef Q2CLIENT\n\tcase MGT_QUAKE2:\n\t\tMC_AddCenterPicture(menu, 4, 24, \"pics/m_banner_load_game.pcx\");\n\t\tbreak;\n#endif\n\tdefault:\n\t\tMC_AddCenterPicture(menu, 4, 24, \"gfx/p_load.lmp\");\n\t\tbreak;\n\t}\n\n\tM_ScanSaves ();\n\n\tfor (i=0 ; i< MAX_SAVEGAMES; i++)\n\t{\n\t\tif (m_saves[i].loadable)\n\t\t\top = (menuoption_t *)MC_AddConsoleCommandf(menu, 16, 170, 32+8*i, false, m_saves[i].desc, \"loadgame %s\\nclosemenu\\n\", m_saves[i].sname);\n\t\telse\n\t\t\tMC_AddWhiteText(menu, 16, 170, 32+8*i, m_saves[i].desc, false);\n\t\tif (op)\n\t\t\tif (!menu->selecteditem || (*m_saves[i].time && strcmp(time, m_saves[i].time) < 0))\n\t\t\t{\n\t\t\t\tmenu->selecteditem = op;\n\t\t\t\tQ_strncpyz(time, m_saves[i].time, sizeof(time));\n\t\t\t}\n\t}\n\n\tif (menu->selecteditem)\n\t\tmenu->cursoritem = (menuoption_t *)MC_AddRedText(menu, 8, 0, menu->selecteditem->common.posy, NULL, false);\t\n\n\tMC_AddCustom(menu, 192, 60-16, NULL, 0, NULL)->draw = M_Menu_LoadSave_Preview_Draw;\n}\n\n\n#endif\n\nstatic qboolean M_SingleParseMapDBEpisodes(emenu_t *menu, int *y, qboolean bigfont)\n{\t//use the remaster's episode selection lists.\n\tsize_t sz;\n\tchar *file = FS_LoadMallocFile(\"mapdb.json\", &sz);\n\tif (file)\n\t{\n\t\tjson_t *j = JSON_Parse(file);\n\t\tjson_t *episodes = JSON_FindChild(j, \"episodes\"), *e;\n\t\tint i = 0;\n\t\twhile ((e=JSON_GetIndexed(episodes, i++)))\n\t\t{\n\t\t\tchar namebuf[MAX_QPATH];\n\t\t\tchar cmdbuf[MAX_QPATH];\n\t\t\tconst char *command = JSON_GetString(e, \"command\", cmdbuf,sizeof(cmdbuf), NULL);\n\t\t\tconst char *name = JSON_GetString(e, \"name\", namebuf,sizeof(namebuf), NULL);\n\t\t\tif (!command)\n\t\t\t{\n\t\t\t\tcommand = JSON_GetString(e, \"dir\", cmdbuf,sizeof(cmdbuf), NULL);\n\t\t\t\tif (command)\n\t\t\t\t\tcommand = va(\"gamedir %s; map start\", command);\n\t\t\t}\n\t\t\tif (!command)\n\t\t\t\tcontinue;\n\t\t\tname = TL_Translate(com_language, name);\n\t\t\tif (name && command)\n\t\t\t{\n\t\t\t\tmenubutton_t *b;\n\t\t\t\tif (bigfont)\n\t\t\t\t\tb = MC_AddConsoleCommandQBigFont(menu, 72, *y,\tname,  va(\"closemenu;disconnect;maxclients 1;spectator \\\"\\\";samelevel \\\"\\\";deathmatch \\\"\\\";set_calc coop ($cl_splitscreen>0);%s\\n\", command)), *y += 20-8;\n\t\t\t\telse if (JSON_GetInteger(e, \"needsClassSelect\", false))\n\t\t\t\t\tb = MC_AddConsoleCommand\t\t(menu, 64, 260, *y,\tname,\t\tva(\"menu_single class %s\\n\", command));\n\t\t\t\telse if (JSON_GetInteger(e, \"needsSkillSelect\", false))\n\t\t\t\t\tb = MC_AddConsoleCommand\t\t(menu, 64, 260, *y,\tname,\t\tva(\"menu_single skill %s\\n\", command));\n\t\t\t\telse\n\t\t\t\t\tb = MC_AddConsoleCommand\t\t(menu, 64, 260, *y,\tname,\t\tva(\"closemenu; skill 0;deathmatch 0; set_calc coop ($cl_splitscreen>0);%s\\n\",command));\n\t\t\t\t*y+=8;\n\t\t\t\tif (!menu->selecteditem)\n\t\t\t\t\tmenu->selecteditem = (menuoption_t*)b;\n\t\t\t}\n\t\t}\n\t\tJSON_Destroy(j);\n\t\tFS_FreeFile(file);\n\t\treturn true;\n\t}\n\treturn false;\n}\n\nvoid M_Menu_SinglePlayer_f (void)\n{\n\temenu_t *menu;\n#ifdef HAVE_SERVER\n\tmenubutton_t *b;\n\tmpic_t *p;\n\tstatic menuresel_t resel;\n\n#if MAX_SPLITS > 1\n\tstatic const char *splitopts[] =\n\t\t{\n\t\t\t\"Single\",\n\t\t\t\"Dual\",\n\t\t\t\"Tripple\",\n\t\t\t\"QUAD\",\n\t\t\tNULL\n\t\t};\n\tstatic const char *splitvals[] =\n\t\t{\n\t\t\t\"0\",\n\t\t\t\"1\",\n\t\t\t\"2\",\n\t\t\t\"3\",\n\t\t\tNULL\n\t\t};\n#endif\n\n\tswitch(M_GameType())\n\t{\n#ifdef Q2CLIENT\n\tcase MGT_QUAKE2:\n\t\t{\n\t\t\tint y = 40;\n\t\t\tconst char *command = \"newgame\";\n\t\t\tmenu = M_CreateMenu(0);\n\n\t\t\tMC_AddCenterPicture(menu, 4, 24, \"pics/m_banner_game\");\n\n\t\t\tif (!strncmp(Cmd_Argv(1), \"skill\", 5))\n\t\t\t\tcommand = Cmd_Argv(2);\n\t\t\telse\n\t\t\t\tM_SingleParseMapDBEpisodes(menu, &y, false);\n\n\t\t\tif (!menu->selecteditem)\n\t\t\t{ \t//quake2 uses the 'newgame' alias, which controls the intro video and then start map.\n\t\t\t\tmenu->selecteditem = (menuoption_t*)\n\t\t\t\tMC_AddConsoleCommand\t(menu, 64, 170, y,\t\"Easy\",\t\tva(\"closemenu; skill 0;deathmatch 0; set_calc coop ($cl_splitscreen>0);%s\\n\",command)); y+=8;\n\t\t\t\tMC_AddConsoleCommand\t(menu, 64, 170, y,\t\"Medium\",\tva(\"closemenu; skill 1;deathmatch 0; set_calc coop ($cl_splitscreen>0);%s\\n\",command)); y+=8;\n\t\t\t\tMC_AddConsoleCommand\t(menu, 64, 170, y,\t\"Hard\",\t\tva(\"closemenu; skill 2;deathmatch 0; set_calc coop ($cl_splitscreen>0);%s\\n\",command)); y+=8;\n\t\t\t}\n\t\t\tif (strncmp(Cmd_Argv(1), \"skill\", 5))\n\t\t\t{\n#ifdef SAVEDGAMES\n\t\t\t\ty+=8;\n\t\t\t\tMC_AddConsoleCommand\t(menu, 64, 170, y,\t\"Load Game\", \"menu_load\\n\"); y+=8;\n\t\t\t\tMC_AddConsoleCommand\t(menu, 64, 170, y,\t\"Save Game\", \"menu_save\\n\"); y+=8;\n#endif\n\n#if MAX_SPLITS > 1\n\t\t\t\ty+=8;\n\t\t\t\tMC_AddCvarCombo(menu, 72, 170, y, localtext(\"Splitscreen\"), &cl_splitscreen, splitopts, splitvals);\n#endif\n\t\t\t}\n\n\t\t\tmenu->cursoritem = (menuoption_t*)MC_AddWhiteText(menu, 48, 0, 40, NULL, false);\n\t\t}\n\t\treturn;\n#endif\n#ifdef HEXEN2\n\tcase MGT_HEXEN2:\n\t\t{\n\t\t\tint y;\n\t\t\tint i;\n\t\t\tcvar_t *pc;\n\t\t\tqboolean havemp;\n\t\t\tstatic char *classlistmp[] = {\n\t\t\t\t\"Random\",\n\t\t\t\t\"Paladin\",\n\t\t\t\t\"Crusader\",\n\t\t\t\t\"Necromancer\",\n\t\t\t\t\"Assasin\",\n\t\t\t\t\"Demoness\"\n\t\t\t};\n\t\t\tmenubutton_t *b;\n\t\t\thavemp = !!COM_FCheckExists(\"maps/keep1.bsp\");\n\t\t\tmenu = M_CreateMenu(0);\n\t\t\tMC_AddPicture(menu, 16, 0, 35, 176, \"gfx/menu/hplaque.lmp\");\n\n\t\t\tCvar_Get(\"cl_playerclass\", \"1\", CVAR_USERINFO|CVAR_ARCHIVE, \"Hexen2\");\n\n\t\t\ty = 64-20;\n\n\t\t\tif (!strncmp(Cmd_Argv(1), \"class\", 5))\n\t\t\t{\n\t\t\t\tunsigned taken = 0;\n\t\t\t\tint oldclass;\n\t\t\t\tint pnum;\n\t\t\t\tpnum = atoi(Cmd_Argv(1)+5);\n\t\t\t\tcl_splitscreen.ival = bound(0, cl_splitscreen.ival, MAX_SPLITS-1);\n\t\t\t\tpnum = bound(1, pnum, cl_splitscreen.ival+1);\n\n\t\t\t\tMC_AddCenterPicture(menu, 0, 60, \"gfx/menu/title2.lmp\");\n\n\t\t\t\tif (cl_splitscreen.ival)\n\t\t\t\t\tMC_AddBufferedText(menu, 80, 0, (y+=8)+12, va(localtext(\"Player %i\\n\"), pnum), false, true);\n\n\t\t\t\tfor (i = 0; i < pnum-1 && i < countof(cls.userinfo); i++)\n\t\t\t\t\ttaken |= 1<<atoi(InfoBuf_ValueForKey(&cls.userinfo[i], \"cl_playerclass\"));\n\t\t\t\toldclass = atoi(InfoBuf_ValueForKey(&cls.userinfo[pnum-1], \"cl_playerclass\"));\n\n\t\t\t\tfor (i = 0; i <= 4+havemp; i++)\n\t\t\t\t{\n\t\t\t\t\tb = MC_AddConsoleCommandHexen2BigFont(menu, 80, y+=20,\t\t(!i || !(taken&(1<<i)))?classlistmp[i]:(va(S_COLOR_GRAY\"%s\", classlistmp[i])),\n\t\t\t\t\t\t\tva(\"p%i setinfo cl_playerclass %i; menu_single %s %s\\n\",\n\t\t\t\t\t\t\t\tpnum,\n\t\t\t\t\t\t\t\ti?i:((rand()%(4+havemp))+1),\n\t\t\t\t\t\t\t\t((pnum+1 > cl_splitscreen.ival+1)?\"skill\":va(\"class%i\",pnum+1)),\n\t\t\t\t\t\t\t\tCmd_Argv(2)));\n\t\t\t\t\tif (!menu->selecteditem || i == oldclass)\n\t\t\t\t\t\tmenu->selecteditem = (menuoption_t*)b;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (!strncmp(Cmd_Argv(1), \"skill\", 5))\n\t\t\t{\n\t\t\t\textern cvar_t skill;\n\t\t\t\t//yes, hexen2 has per-class names for the skill levels. because being weird and obtuse is kinda its forte\n\t\t\t\tstatic char *skillnames[6][4] =\n\t\t\t\t{\n\t\t\t\t\t{\t//generic/random\n\t\t\t\t\t\t\"Easy\",\n\t\t\t\t\t\t\"Medium\",\n\t\t\t\t\t\t\"Hard\",\n\t\t\t\t\t\t\"Nightmare\"\n\t\t\t\t\t},\n\t\t\t\t\t{\t//barbarian\n\t\t\t\t\t\t\"Servant\",\t//string changed, because somehow the original is malicious. was: \"Apprentice\",\n\t\t\t\t\t\t\"Squire\",\n\t\t\t\t\t\t\"Adept\",\n\t\t\t\t\t\t\"Lord\"\n\t\t\t\t\t},\n\t\t\t\t\t{\t//paladin\n\t\t\t\t\t\t\"Gallant\",\n\t\t\t\t\t\t\"Holy Avenger\",\n\t\t\t\t\t\t\"Divine Hero\",\n\t\t\t\t\t\t\"Legend\"\n\t\t\t\t\t},\n\t\t\t\t\t{\t//necromancer\n\t\t\t\t\t\t\"Sorcerer\",\n\t\t\t\t\t\t\"Dark Servant\",\n\t\t\t\t\t\t\"Warlock\",\n\t\t\t\t\t\t\"Lich King\"\n\t\t\t\t\t},\n\t\t\t\t\t{\t//assassin\n\t\t\t\t\t\t\"Rogue\",\n\t\t\t\t\t\t\"Cutthroat\",\n\t\t\t\t\t\t\"Executioner\",\n\t\t\t\t\t\t\"Widow Maker\"\n\t\t\t\t\t},\n\t\t\t\t\t{\t//demoness\n\t\t\t\t\t\t\"Larva\",\n\t\t\t\t\t\t\"Spawn\",\n\t\t\t\t\t\t\"Fiend\",\n\t\t\t\t\t\t\"She Bitch\"\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t\t\tchar **sn = skillnames[0];\n\t\t\t\tpc = Cvar_Get(\"cl_playerclass\", \"1\", CVAR_USERINFO|CVAR_ARCHIVE, \"Hexen2\");\n\t\t\t\tif (pc && (unsigned)pc->ival <= 5)\n\t\t\t\t\tsn = skillnames[pc->ival];\n\n\t\t\t\tMC_AddCenterPicture(menu, 0, 60, \"gfx/menu/title5.lmp\");\n\t\t\t\tfor (i = 0; i < 4; i++)\n\t\t\t\t{\n\t\t\t\t\tb = MC_AddConsoleCommandHexen2BigFont(menu, 80, y+=20,\tsn[i],\tva(\"skill %i; closemenu; disconnect; deathmatch 0; coop %i;wait;map %s\\n\", i, cl_splitscreen.ival>0, Cmd_Argv(2)));\n\t\t\t\t\tif (!menu->selecteditem || i == skill.ival)\n\t\t\t\t\t\tmenu->selecteditem = (menuoption_t*)b;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tMC_AddCenterPicture(menu, 0, 60, \"gfx/menu/title1.lmp\");\n\t\t\t\t//startmap selection in hexen2 is nasty.\n\t\t\t\tif (havemp)\n\t\t\t\t{\n\t\t\t\t\tmenu->selecteditem = (menuoption_t*)\n\t\t\t\t\tMC_AddConsoleCommandHexen2BigFont(menu, 80, y+=20,\t\"New Mission\",\t\"menu_single class keep1\\n\");\n\t\t\t\t\tMC_AddConsoleCommandHexen2BigFont(menu, 80, y+=20,\t\"Old Mission\",\t\"menu_single class demo1\\n\");\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tmenu->selecteditem = (menuoption_t*)\n\t\t\t\t\tMC_AddConsoleCommandHexen2BigFont(menu, 80, y+=20,\t\"New Game\",\t\t\"menu_single class demo1\\n\");\n\t\t\t\t}\n#ifdef SAVEDGAMES\n\t\t\t\tMC_AddConsoleCommandHexen2BigFont(menu, 80, y+=20,\t\t\"Save Game\",\t\"menu_save\\n\");\n\t\t\t\tMC_AddConsoleCommandHexen2BigFont(menu, 80, y+=20,\t\t\"Load Game\",\t\"menu_load\\n\");\n#endif\n\n\t\t\t\tMC_AddCvarCombo(menu, 72, 170, y+=20, localtext(\"Splitscreen\"), &cl_splitscreen, splitopts, splitvals);\n\t\t\t}\n\n\t\t\tmenu->cursoritem = (menuoption_t *)MC_AddCursor(menu, menu->selecteditem?NULL:&resel, 56, menu->selecteditem?menu->selecteditem->common.posy:0);\n\n\t\t\treturn;\n\t\t}\n\t\tbreak;\n#endif\n\tdefault:\n\t\tif (QBigFontWorks())\n\t\t{\n\t\t\tint y = 32;\n\t\t\tmenu = M_CreateMenu(0);\n\t\t\tMC_AddPicture(menu, 16, 4, 32, 144, \"gfx/qplaque.lmp\");\n\t\t\tMC_AddCenterPicture(menu, 4, 24, \"gfx/ttl_sgl.lmp\");\n\n\t\t\tif (M_SingleParseMapDBEpisodes(menu, &y, true))\n\t\t\t\ty += 20;\n\t\t\telse\n\t\t\t{\n\t\t\t\tmenu->selecteditem = (menuoption_t*)\n\t\t\t\tMC_AddConsoleCommandQBigFont\t(menu, 72, y,\t\"New Game\",  \"closemenu;disconnect;maxclients 1;spectator \\\"\\\";samelevel \\\"\\\";deathmatch \\\"\\\";set_calc coop ($cl_splitscreen>0);startmap_sp\\n\"); y += 20;\n\t\t\t}\n#ifdef SAVEDGAMES\n\t\t\tMC_AddConsoleCommandQBigFont\t(menu, 72, y,\t\"Load Game\", \"menu_load\\n\"); y+=20;\n\t\t\tMC_AddConsoleCommandQBigFont\t(menu, 72, y,\t\"Save Game\", \"menu_save\\n\"); y+=20;\n#endif\n\n\t\t\tmenu->cursoritem = (menuoption_t*)MC_AddCursor(menu, &resel, 54, 32);\n\t\t\treturn;\n\t\t}\n\t\telse\n\t\t{\t//q1\n\t\t\tmenu = M_CreateMenu(0);\n\t\t\tMC_AddPicture(menu, 16, 4, 32, 144, \"gfx/qplaque.lmp\");\n\t\t\tMC_AddCenterPicture(menu, 4, 24, \"gfx/ttl_sgl.lmp\");\n\t\t}\n\t\tbreak;\n\t}\n\n\tp = R2D_SafeCachePic(\"gfx/sp_menu.lmp\");\n\tif (!p)\n\t{\n\t\tMC_AddWhiteText(menu, 92, 0, 12*8, \"Couldn't find file\", false);\n\t\tMC_AddWhiteText(menu, 92, 0, 13*8, \"gfx/sp_menu.lmp\", false);\n\t\tMC_AddBox (menu, 72, 11*8, 23*8, 4*8);\n\t}\n\telse\n\t{\n\t\tint width;\n\t\tif (R_GetShaderSizes(p, &width, NULL, true) <= 0)\n\t\t\twidth = 232;\n\n\t\tMC_AddPicture(menu, 72, 32, 232, 64, \"gfx/sp_menu.lmp\");\n\n\t\tb = MC_AddConsoleCommand\t(menu, 72, 304, 32,\t\"\", \"closemenu;disconnect;maxclients 1;spectator \\\"\\\";samelevel \\\"\\\";deathmatch \\\"\\\";set_calc coop ($cl_splitscreen>0);startmap_sp\\n\");\n\t\tmenu->selecteditem = (menuoption_t *)b;\n\t\tb->common.width = width;\n\t\tb->common.height = 20;\n#ifdef SAVEDGAMES\n\t\tb = MC_AddConsoleCommand\t(menu, 72, 304, 52,\t\"\", \"menu_load\\n\");\n\t\tb->common.width = width;\n\t\tb->common.height = 20;\n\t\tb = MC_AddConsoleCommand\t(menu, 72, 304, 72,\t\"\", \"menu_save\\n\");\n\t\tb->common.width = width;\n\t\tb->common.height = 20;\n#endif\n\n#if MAX_SPLITS > 1\n\t\tb = (menubutton_t*)MC_AddCvarCombo(menu, 72, 72+width/2, 92, \"\", &cl_splitscreen, splitopts, splitvals);\n\t\tMC_AddRedText(menu, 72, 0, 92, localtext(\"Splitscreen\"), false);\n\t\tb->common.height = 20;\n\t\tb->common.width = width;\n#endif\n\n\t\tmenu->cursoritem = (menuoption_t*)MC_AddCursor(menu, &resel, 54, 32);\n\t}\n\n#else\n\tmenu = M_CreateMenu(0);\n\n\tMC_AddWhiteText(menu, 84, 0, 12*8, \"This build is unable\", false);\n\tMC_AddWhiteText(menu, 84, 0, 13*8, \"to start a local game\", false);\n\n\tMC_AddBox (menu, 60, 11*8, 25*8, 4*8);\n#endif\n}\n\n\ntypedef struct demoitem_s {\n\tqboolean isdir;\n\tint size;\n\tstruct demoitem_s *next;\n\tstruct demoitem_s *prev;\n\tchar name[1];\n} demoitem_t;\n\ntypedef struct {\n\tint fsroot;\t//FS_SYSTEM, FS_GAME, FS_GAMEONLY. if FS_SYSTEM, executed command will have a leading #\n\tchar path[MAX_OSPATH];\n\tchar selname[MAX_OSPATH];\n} demoloc_t;\n\ntypedef struct {\n\tmenucustom_t *list;\n\tdemoitem_t *selected;\n\tdemoitem_t *firstitem;\n\n\tdemoloc_t *fs; \n\tint pathlen;\n\n\t//for the basedir picker...\n\tftemanifest_t *man;\n\n\tchar *command[64];\t//these let the menu be used for nearly any sort of file browser.\n\tchar *ext[64];\n\tint numext;\n\n\tint dragscroll;\n\tint mousedownpos;\n\n\tdemoitem_t *items;\n} demomenu_t;\n\nstatic void M_DemoDraw(int x, int y, menucustom_t *control, emenu_t *menu)\n{\n\tchar *text;\n\tdemomenu_t *info = menu->data;\n\tdemoitem_t *item, *lostit;\n\tint ty;\n\n\tchar displaypath[MAX_OSPATH];\n\tif (FS_DisplayPath(info->fs->path, (info->fs->fsroot==FS_GAME)?FS_GAMEONLY:info->fs->fsroot, displaypath, sizeof(displaypath)))\n\t\tDraw_FunString(x, y-16, displaypath);\n\n\tty = vid.height-24;\n\titem = info->selected;\n\twhile(item)\n\t{\n\t\tif (info->firstitem == item)\n\t\t\tbreak;\n\t\tif (ty < y)\n\t\t{\n\t\t\t//we couldn't find it\n\t\t\tfor (lostit = info->firstitem; lostit; lostit = lostit->prev)\n\t\t\t{\n\t\t\t\tif (info->selected == lostit)\n\t\t\t\t{\n\t\t\t\t\titem = lostit;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tinfo->firstitem = item;\n\t\t\tbreak;\n\t\t}\n\t\titem = item->prev;\n\t\tty-=8;\n\t}\n\tif (!item)\n\t\tinfo->firstitem = info->items;\n\n\tif (keydown[K_MOUSE1] || keydown[K_TOUCHSLIDE])\n\t{\n\t\tif (!info->dragscroll)\n\t\t{\n\t\t\tinfo->dragscroll = 1;\n\t\t\tinfo->mousedownpos = mousecursor_y-y;\n\t\t}\n\t\tif (info->dragscroll)\n\t\t{\n\t\t\tif (info->mousedownpos >= mousecursor_y-y+8)\n\t\t\t{\n\t\t\t\tinfo->dragscroll = 2;\n\t\t\t\tinfo->mousedownpos -= 8;\n\t\t\t\tif (info->firstitem->next)\n\t\t\t\t{\n\t\t\t\t\tif (info->firstitem == info->selected)\n\t\t\t\t\t\tinfo->selected = info->firstitem->next;\n\t\t\t\t\tinfo->firstitem = info->firstitem->next;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (info->mousedownpos+8 <= mousecursor_y-y)\n\t\t\t{\n\t\t\t\tinfo->dragscroll = 2;\n\t\t\t\tinfo->mousedownpos += 8;\n\t\t\t\tif (info->firstitem->prev)\n\t\t\t\t{\n\t\t\t\t\tif (ty <= 24)\n\t\t\t\t\t\tinfo->selected = info->selected->prev;\n\t\t\t\t\tinfo->firstitem = info->firstitem->prev;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\telse\n\t\tinfo->dragscroll = 0;\n\n\tcontrol->common.height = vid.height-y;\n\n\titem = info->firstitem;\n\twhile(item)\n\t{\n\t\tif (y >= y+control->common.height)\n\t\t\treturn;\n\t\tif (!item->isdir)\n\t\t\ttext = va(\"%-32.32s%6iKB\", item->name+info->pathlen, item->size/1024);\n\t\telse\n\t\t\ttext = item->name+info->pathlen;\n\t\tif (item == info->selected)\n\t\t\tDraw_AltFunString(x, y, text);\n\t\telse\n\t\t\tDraw_FunString(x, y, text);\n\t\ty+=8;\n\t\titem = item->next;\n\t}\n}\nstatic void ShowDemoMenu (emenu_t *menu, const char *path);\nstatic qboolean M_DemoKey(menucustom_t *control, emenu_t *menu, int key, unsigned int unicode)\n{\n\tdemomenu_t *info = menu->data;\n\tdemoitem_t *it;\n\tint i;\n\n\tswitch (key)\n\t{\n\tcase K_MWHEELUP:\n\tcase K_UPARROW:\n\tcase K_KP_UPARROW:\n\tcase K_GP_DPAD_UP:\n\t\tif (info->selected && info->selected->prev)\n\t\t\tinfo->selected = info->selected->prev;\n\t\tbreak;\n\tcase K_MWHEELDOWN:\n\tcase K_DOWNARROW:\n\tcase K_KP_DOWNARROW:\n\tcase K_GP_DPAD_DOWN:\n\t\tif (info->selected && info->selected->next)\n\t\t\tinfo->selected = info->selected->next;\n\t\tbreak;\n\tcase K_HOME:\n\t\tinfo->selected = info->items;\n\t\tbreak;\n\tcase K_END:\n\t\tinfo->selected = info->items;\n\t\twhile(info->selected->next)\n\t\t\tinfo->selected = info->selected->next;\n\t\tbreak;\n\tcase K_PGUP:\n\t\tfor (i = 0; i < 10; i++)\n\t\t{\n\t\t\tif (info->selected && info->selected->prev)\n\t\t\t\tinfo->selected = info->selected->prev;\n\t\t}\n\t\tbreak;\n\tcase K_PGDN:\n\t\tfor (i = 0; i < 10; i++)\n\t\t{\n\t\t\tif (info->selected && info->selected->next)\n\t\t\t\tinfo->selected = info->selected->next;\n\t\t}\n\t\tbreak;\n\tcase K_MOUSE1:\t//this is on release\n\t\tif (info->dragscroll == 2)\n\t\t\tbreak;\n\tcase K_TOUCHTAP:\n\t\tit = info->firstitem;\n\t\ti = (mousecursor_y - control->common.posy) / 8;\n\t\twhile(i > 0 && it && it->next)\n\t\t{\n\t\t\tit = it->next;\n\t\t\ti--;\n\t\t}\n\t\tif (info->selected != it)\n\t\t{\n\t\t\tinfo->selected = it;\n\t\t\tinfo->dragscroll = 0;\n\t\t\tbreak;\n\t\t}\n\t\t//fallthrough\n\tcase K_ENTER:\n\tcase K_KP_ENTER:\n\tcase K_GP_DIAMOND_CONFIRM:\n\t\tif (info->selected)\n\t\t{\n\t\t\tif (info->selected->isdir)\n\t\t\t\tShowDemoMenu(menu, info->selected->name);\n\t\t\telse if (info->numext)\n\t\t\t{\n\t\t\t\textern int\t\tshift_down;\n\t\t\t\tint extnum;\n\t\t\t\tconst char *ext = COM_GetFileExtension(info->selected->name, NULL);\n\t\t\t\tif (!Q_strcasecmp(ext, \".gz\") || !Q_strcasecmp(ext, \".xz\"))\n\t\t\t\t\text = COM_GetFileExtension(info->selected->name, ext);\n\t\t\t\tfor (extnum = 0; extnum < info->numext; extnum++)\n\t\t\t\t\tif (!Q_strcasecmp(ext, info->ext[extnum]))\n\t\t\t\t\t\tbreak;\n\n\t\t\t\tif (extnum == info->numext)\t//wasn't on our list of extensions.\n\t\t\t\t\textnum = 0;\n\n\t\t\t\tif (!info->command[extnum])\n\t\t\t\t{\t//acceptable archive formats\n\t\t\t\t\tShowDemoMenu(menu, va(\"%s/\", info->selected->name));\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\n\t\t\t\tCbuf_AddText(va(\"%s \\\"%s%s\\\"\\n\", info->command[extnum], (info->fs->fsroot==FS_SYSTEM)?\"#\":\"\", info->selected->name), RESTRICT_LOCAL);\n\t\t\t\tif (!shift_down)\n\t\t\t\t\tM_RemoveMenu(menu);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\tbreak;\n\tdefault:\n\t\treturn false;\n\t}\n\tif (info->selected)\n\t\tQ_strncpyz(info->fs->selname, info->selected->name, sizeof(info->fs->selname));\n\telse\n\t\tQ_strncpyz(info->fs->selname, \"\", sizeof(info->fs->selname));\n\treturn true;\n}\n\nstatic int QDECL DemoAddItem(const char *filename, qofs_t size, time_t modified, void *parm, searchpathfuncs_t *spath)\n{\n\tint extnum;\n\tdemomenu_t *menu = parm;\n\tdemoitem_t *link, *newi;\n\tint side;\n\tqboolean isdir;\n\tchar tempfname[MAX_QPATH];\n\n\tchar *i;\n\n\ti = strchr(filename+menu->pathlen, '/');\n\tif (i == NULL)\n\t{\n\t\tconst char *ext = COM_GetFileExtension(filename, NULL);\n\t\tif (!Q_strcasecmp(ext, \".gz\") || !Q_strcasecmp(ext, \".xz\"))\n\t\t\text = COM_GetFileExtension(filename, ext);\n\t\tfor (extnum = 0; extnum < menu->numext; extnum++)\n\t\t\tif (!Q_strcasecmp(ext, menu->ext[extnum]))\n\t\t\t\tbreak;\n\n\t\tif (extnum == menu->numext)\t//wasn't on our list of extensions.\n\t\t\treturn true;\n\t\tisdir = false;\n\t}\n\telse\n\t{\n\t\ti++;\n\t\tif (i-filename > sizeof(tempfname)-2)\n\t\t\treturn true;\t//too long to fit in our buffers anyway\n\t\tstrncpy(tempfname, filename, i-filename);\n\t\ttempfname[i-filename] = 0;\n\t\tfilename = tempfname;\n\n\t\tsize = 0;\n\t\tisdir = true;\n\t}\n\n\tif (!menu->items)\n\t\tmenu->items = newi = BZ_Malloc(sizeof(*newi) + strlen(filename));\n\telse\n\t{\n\t\tlink = menu->items;\n\t\tfor(;;)\n\t\t{\n\t\t\tif (link->isdir != isdir)\t//bias directories, so they sink\n\t\t\t\tside = (link->isdir > isdir)?1:-1;\n\t\t\telse\n\t\t\t\tside = Q_strcasecmp(link->name, filename);\n\t\t\tif (side == 0)\n\t\t\t\treturn true;\t//already got this file\n\t\t\telse if (side > 0)\n\t\t\t{\n\t\t\t\tif (!link->prev)\n\t\t\t\t{\n\t\t\t\t\tlink->prev = newi = BZ_Malloc(sizeof(*newi) + strlen(filename));\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tlink = link->prev;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (!link->next)\n\t\t\t\t{\n\t\t\t\t\tlink->next = newi = BZ_Malloc(sizeof(*newi) + strlen(filename));\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tlink = link->next;\n\t\t\t}\n\t\t}\n\t}\n\t\n\tstrcpy(newi->name, filename);\n\tnewi->size = size;\n\tnewi->isdir = isdir;\n\tnewi->prev = NULL;\n\tnewi->next = NULL;\n\n\treturn true;\n}\n\n//converts the binary tree into sorted linked list\nstatic void M_Demo_Flatten(demomenu_t *info)\n{\n\tdemoitem_t *btree = info->items, *item, *lastitem;\n\tdemoitem_t *listhead = NULL, *listlast = NULL;\n\n\twhile(btree)\n\t{\n\t\tif (!btree->prev)\n\t\t{\t//none on left side, descend down right removing head node\n\t\t\titem = btree;\n\t\t\tbtree = btree->next;\n\t\t}\n\t\telse\n\t\t{\n\t\t\titem = btree;\n\t\t\tlastitem = item;\n\t\t\tfor (;;)\n\t\t\t{\n\t\t\t\tif (!item->prev)\n\t\t\t\t{\n\t\t\t\t\tlastitem->prev = item->next;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tlastitem = item;\n\t\t\t\titem = lastitem->prev;\n\t\t\t}\n\t\t}\n\t\tif (listlast)\n\t\t{\n\t\t\tlistlast->next = item;\n\t\t\titem->prev = listlast;\n\t\t\tlistlast = item;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tlisthead = listlast = item;\n\t\t\titem->prev = NULL;\n\t\t}\n\t}\n\tif (listlast)\n\t\tlistlast->next = NULL;\n\tinfo->items = listhead;\n\tinfo->selected = listhead;\n\tinfo->firstitem = listhead;\n}\n\nstatic void M_Demo_Flush (demomenu_t *info)\n{\n\tdemoitem_t *item;\n\twhile (info->items)\n\t{\n\t\titem = info->items;\n\t\tinfo->items = item->next;\n\t\tBZ_Free(item);\n\t}\n\tinfo->items = NULL;\n\tinfo->selected = NULL;\n\tinfo->firstitem = NULL;\n}\n\nstatic void M_Demo_Remove (emenu_t *menu)\n{\n\tdemomenu_t *info = menu->data;\n\tM_Demo_Flush(info);\n\n\tFS_Manifest_Free(info->man);\n\tinfo->man = NULL;\n}\n\nstatic void FS_GameDirPrompted(void *ctx, promptbutton_t btn)\n{\n\temenu_t *menu = ctx;\n\tif (Menu_IsLinked(&menu->menu))\n\t{\n\t\tdemomenu_t *info = menu->data;\n\t\tftemanifest_t *man = info->man;\n\t\tif (!man || info->fs->fsroot != FS_SYSTEM)\n\t\t\treturn;\t//erk? no exploits!\n\n\t\tswitch(btn)\n\t\t{\n\t\tcase PROMPT_CANCEL:\n\t\t\treturn;\n\t\tcase PROMPT_YES:\n\t\t\tinfo->man = NULL;\n\t\t\tMenu_Unlink(&menu->menu, true);\t//try to kill the dialog menu.\n\t\t\tFS_ChangeGame(man, true, true);\t//switch to that new gamedir\n\t\t\tbreak;\n\t\tcase PROMPT_NO:\n\t\t\treturn;\n\t\t}\n\t}\n}\n\nstatic void ShowDemoMenu (emenu_t *menu, const char *path)\n{\n\tdemomenu_t *info = menu->data;\n\n\tint c;\n\tchar *s;\n\tchar match[256];\n\n\tif (path != info->fs->path)\n\t{\n\t\tif (*path == '/' && info->fs->fsroot != FS_SYSTEM)\n\t\t\tpath++;\n\t\tQ_strncpyz(info->fs->path, path, sizeof(info->fs->path));\n\t}\n\n\tif (info->fs->fsroot == FS_GAME)\n\t{\n\t\tif (!strcmp(path, \"../\"))\n\t\t{\n\t\t\tQ_strncpyz(info->fs->path, \"\", sizeof(info->fs->path));\n\t\t\tinfo->fs->fsroot = FS_ROOT;\n\t\t}\n\t}\n\telse if (info->fs->fsroot == FS_ROOT)\n\t{\n\t\tif (!strcmp(path, \"../\"))\n\t\t{\n\t\t\tFS_SystemPath(\"\", FS_ROOT, info->fs->path, sizeof(info->fs->path));\n\t\t\tQ_strncatz(info->fs->path, \"../\", sizeof(info->fs->path));\n\t\t\tinfo->fs->fsroot = FS_SYSTEM;\n\t\t\twhile((s = strchr(info->fs->path, '\\\\')))\n\t\t\t\t*s = '/';\n\t\t}\n\t}\n\twhile (!strcmp(info->fs->path+strlen(info->fs->path)-3, \"../\"))\n\t{\n\t\tc = 0;\n\t\tfor (s = info->fs->path+strlen(info->fs->path)-3; s >= info->fs->path; s--)\n\t\t{\n\t\t\tif (*s == '/')\n\t\t\t{\n\t\t\t\tc++;\n\t\t\t\ts[1] = '\\0';\n\t\t\t\tif (c == 2)\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (c<2)\n\t\t\t*info->fs->path = '\\0';\n\t}\n\tinfo->selected = NULL;\n\tinfo->pathlen = strlen(info->fs->path);\n\n\tM_Demo_Flush(menu->data);\n\tif (info->fs->fsroot == FS_SYSTEM)\n\t{\n\t\ts = strchr(info->fs->path, '/');\n\t\tif (s && strchr(s+1, '/'))\n\t\t{\n\t\t\tQ_snprintfz(match, sizeof(match), \"%s../\", info->fs->path);\n\t\t\tDemoAddItem(match, 0, 0, info, NULL);\n\t\t}\n\t}\n\telse if (*info->fs->path)\n\t{\n\t\tQ_snprintfz(match, sizeof(match), \"%s../\", info->fs->path);\n\t\tDemoAddItem(match, 0, 0, info, NULL);\n\t}\n\telse if (info->fs->fsroot == FS_GAME || info->fs->fsroot == FS_ROOT)\n\t{\n\t\tQ_snprintfz(match, sizeof(match), \"../\");\n\t\tDemoAddItem(match, 0, 0, info, NULL);\n\t}\n\tif (info->fs->fsroot == FS_SYSTEM)\n\t{\n\t\tif (*info->fs->path)\n\t\t\tQ_snprintfz(match, sizeof(match), \"%s*\", info->fs->path);\n\t\telse\n\t\t\tQ_snprintfz(match, sizeof(match), \"/*\");\n\t\tSys_EnumerateFiles(\"\", match, DemoAddItem, info, NULL);\n\t}\n\telse if (info->fs->fsroot == FS_ROOT)\n\t{\n\t\tQ_snprintfz(match, sizeof(match), \"%s*\", info->fs->path);\n\t\tif (*com_homepath)\n\t\t\tSys_EnumerateFiles(com_homepath, match, DemoAddItem, info, NULL);\n\t\tSys_EnumerateFiles(com_gamepath, match, DemoAddItem, info, NULL);\n\t}\n\telse\n\t{\n\t\tQ_snprintfz(match, sizeof(match), \"%s*\", info->fs->path);\n\t\tCL_ListFilesInPackage(NULL, match, DemoAddItem, info, NULL);\n//\t\tCOM_EnumerateFiles(match, DemoAddItem, info);\n\t}\n\tM_Demo_Flatten(info);\n\n\tif (info->man && FS_DirHasAPackage(info->fs->path, info->man))\n\t{\n\t\tif (promptmenu)\n\t\t\treturn\t//wut? don't confuse basedirs here...\n\t\tZ_Free(info->man->basedir);\n\t\tinfo->man->basedir = Z_StrDup(info->fs->path);\n\t\tMenu_Prompt(FS_GameDirPrompted, &menu->menu, va(localtext(\"Use this directory?\\n%s\"), info->fs->path), \"Yes!\", NULL, \"No\", true);\n\t}\n}\nvoid M_Demo_Reselect(demomenu_t *info, const char *name)\n{\n\tdemoitem_t *item;\n\tfor(item = info->items; item; item = item->next)\n\t{\n\t\tif (!strcmp(item->name, name))\n\t\t{\n\t\t\tinfo->selected = item;\n\t\t\treturn;\n\t\t}\n\t}\n}\n\nvoid M_Menu_Demos_f (void)\n{\n\tchar *demoexts[] = {\n\t\t\".mvd\", \".mvd.gz\",\n\t\t\".qwd\", \".qwd.gz\",\n\t\t//\".qwz\", \".qwz.gz\",\n#ifdef NQPROT\n\t\t\".dem\", \".dem.gz\",\n#endif\n#ifdef Q2CLIENT\n\t\t\".dm2\", \".dm2.gz\"\n#endif\n\t\t//there are also qizmo demos (.qwz) out there...\n\t\t//we don't support them, but if we were to ask qizmo to decode them for us, we could do.\n\t};\n\tchar *archiveexts[] = {\n#ifdef PACKAGE_PK3\n\t\t\".zip\", \".pk3\", \".pk4\",\n#endif\n#ifdef PACKAGE_Q1PAK\n\t\t\".pak\",\n#endif\n#ifdef PACKAGE_DZIP\n\t\t\".dz\",\n#endif\n\t\tNULL\t//in case none of the above are defined. compilers don't much like 0-length arrays.\n\t};\n\tsize_t u;\n\tdemomenu_t *info;\n\temenu_t *menu;\n\tstatic demoloc_t mediareenterloc = {FS_GAME, \"demos/\"};\n\n\tKey_Dest_Remove(kdm_console);\n\n\tmenu = M_CreateMenu(sizeof(demomenu_t));\n\tmenu->remove = M_Demo_Remove;\n\tinfo = menu->data;\n\n\tinfo->fs = &mediareenterloc;\n\n\tif (Cmd_Argc()>1)\n\t{\n\t\tchar *startdemo = Cmd_Argv(1);\n\t\tif (*startdemo == '#')\n\t\t{\n\t\t\tstartdemo++;\n\t\t\tinfo->fs->fsroot = FS_SYSTEM;\n\t\t}\n\t\telse\n\t\t\tinfo->fs->fsroot = FS_GAME;\n\t\tQ_strncpyz(info->fs->path, startdemo, sizeof(info->fs->path));\n\t\t*COM_SkipPath(info->fs->path) = 0;\n\t\tQ_strncpyz(info->fs->selname, startdemo, sizeof(info->fs->selname));\n\t}\n\n\tinfo->numext = 0;\n\tfor (u = 0; u < countof(demoexts); u++)\n\t{\n\t\tinfo->command[info->numext] = \"closemenu;playdemo\";\n\t\tinfo->ext[info->numext++] = demoexts[u];\n\t}\n\n\t//and some archive formats... for the luls\n\tfor (u = 0; u < countof(archiveexts); u++)\n\t{\n\t\tif (!archiveexts[u])\n\t\t\tcontinue;\n\t\tinfo->command[info->numext] = NULL;\n\t\tinfo->ext[info->numext++] = archiveexts[u];\n\t}\n\n\tMC_AddWhiteText(menu, 24, 170, 8, localtext(\"Choose a Demo\"), false);\n\tMC_AddWhiteText(menu, 16, 170, 24, \"^Ue01d^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01f\", false);\n\n\tinfo->list = MC_AddCustom(menu, 0, 32, NULL, 0, NULL);\n\tinfo->list->draw = M_DemoDraw;\n\tinfo->list->key = M_DemoKey;\n\n\tmenu->selecteditem = (menuoption_t*)info->list;\n\n\tShowDemoMenu(menu, info->fs->path);\n\tM_Demo_Reselect(info, info->fs->selname);\n}\n\n#ifdef HAVE_JUKEBOX\nvoid M_Menu_MediaFiles_f (void)\n{\n\tdemomenu_t *info;\n\temenu_t *menu;\n\tstatic demoloc_t mediareenterloc = {FS_GAME};\n\n\tmenu = M_CreateMenu(sizeof(demomenu_t));\n\tmenu->remove = M_Demo_Remove;\n\tinfo = menu->data;\n\n\tinfo->fs = &mediareenterloc;\n\tinfo->numext = 0;\n\n#ifdef HAVE_JUKEBOX\n//\tinfo->ext[info->numext] = \".m3u\";\n//\tinfo->command[info->numext] = \"mediaplaylist\";\n//\tinfo->numext++;\n\tinfo->ext[info->numext] = \".wav\";\n\tinfo->command[info->numext] = \"media_add\";\n\tinfo->numext++;\n#if defined(AVAIL_OGGOPUS) || defined(FTE_TARGET_WEB) || defined(PLUGINS)\n\tinfo->ext[info->numext] = \".opus\";\n\tinfo->command[info->numext] = \"media_add\";\n\tinfo->numext++;\n#endif\n#if defined(AVAIL_OGGVORBIS) || defined(FTE_TARGET_WEB) || defined(PLUGINS)\n\tinfo->ext[info->numext] = \".ogg\";\n\tinfo->command[info->numext] = \"media_add\";\n\tinfo->numext++;\n#endif\n#if defined(AVAIL_MP3_ACM) || defined(FTE_TARGET_WEB) || defined(PLUGINS)\n\tinfo->ext[info->numext] = \".mp3\";\n\tinfo->command[info->numext] = \"media_add\";\n\tinfo->numext++;\n#endif\n#if defined(PLUGINS)\n\tinfo->ext[info->numext] = \".flac\";\n\tinfo->command[info->numext] = \"media_add\";\n\tinfo->numext++;\n#endif\n#endif\n\n#ifdef HAVE_MEDIA_DECODER\n\tinfo->ext[info->numext] = \".roq\";\n\tinfo->command[info->numext] = \"playfilm\";\n\tinfo->numext++;\n#ifdef _WIN32\t//avis are only playable on windows due to a windows dll being used to decode them.\n\tinfo->ext[info->numext] = \".avi\";\n\tinfo->command[info->numext] = \"playfilm\";\n\tinfo->numext++;\n#endif\n#endif\n\n\tMC_AddWhiteText(menu, 24, 170, 8, localtext(\"Media List\"), false);\n\tMC_AddWhiteText(menu, 16, 170, 24, \"^Ue01d^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01f\", false);\n\n\tinfo->list = MC_AddCustom(menu, 0, 32, NULL, 0, NULL);\n\tinfo->list->draw = M_DemoDraw;\n\tinfo->list->key = M_DemoKey;\n\n\tmenu->selecteditem = (menuoption_t*)info->list;\n\n\tShowDemoMenu(menu, info->fs->path);\n\tM_Demo_Reselect(info, info->fs->selname);\n}\n#endif\n\n#include <stdlib.h>\nvoid M_Menu_BasedirPrompt(ftemanifest_t *man)\n{\n\tdemomenu_t *info;\n\temenu_t *menu;\n\tchar *start = getenv(\"HOME\");\n\tsize_t l;\n\n\tKey_Dest_Remove(kdm_console);\n\n\tmenu = M_CreateMenu(sizeof(demomenu_t) + sizeof(demoloc_t));\n\tmenu->remove = M_Demo_Remove;\n\tinfo = menu->data;\n\n\tinfo->man = man;\n\n\tinfo->fs = (demoloc_t*)(info+1);\n\tinfo->fs->fsroot = FS_SYSTEM;\n\tif (!start || !*start || (l = strlen(info->fs->path))>=sizeof(info->fs->path))\n\t\tstrcpy(info->fs->path, \"/\");\n\telse\n\t\tstrcpy(info->fs->path, start);\n\t//make sure it has a trailing slash.\n\tl = strlen(info->fs->path);\n#ifdef _WIN32\n\tif (info->fs->path[l-1] == '\\\\')\n\t\tinfo->fs->path[l-1] = '/';\n#endif\n\tif (info->fs->path[l-1] != '/')\n\t{\n\t\tinfo->fs->path[l] = '/';\n\t\tinfo->fs->path[l+1] = 0;\n\t}\n\n\tinfo->numext = 0;\n\n\tMC_AddWhiteText(menu, 24, 170, 8, va(localtext(\"Where is %s installed?\"), man->formalname), false);\n\tMC_AddWhiteText(menu, 16, 170, 24, \"^Ue01d^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01f\", false);\n\n\tinfo->list = MC_AddCustom(menu, 0, 32, NULL, 0, NULL);\n\tinfo->list->common.width = 320;\n\tinfo->list->draw = M_DemoDraw;\n\tinfo->list->key = M_DemoKey;\n\n\tmenu->selecteditem = (menuoption_t*)info->list;\n\n\tShowDemoMenu(menu, info->fs->path);\n\tM_Demo_Reselect(info, info->fs->selname);\n}\n\n#endif\n"
  },
  {
    "path": "engine/client/menu.c",
    "content": "/*\nCopyright (C) 1996-1997 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n*/\n#include \"quakedef.h\"\n#include \"winquake.h\"\n#include \"shader.h\"\n#include \"cl_master.h\"\n#ifdef FTE_TARGET_WEB\n#include <emscripten/emscripten.h>\n#endif\n\nmenu_t *topmenu;\nmenu_t *promptmenu;\n\nvoid Menu_KeyEvent(qboolean isdown, int deviceid, int key, int unicode)\n{\n\tif (promptmenu && promptmenu->keyevent)\n\t\tpromptmenu->keyevent(promptmenu, isdown, deviceid, key, unicode);\n\telse if (topmenu && topmenu->keyevent)\n\t\ttopmenu->keyevent(topmenu, isdown, deviceid, key, unicode);\n\telse if (isdown)\n\t\tKey_Dest_Remove(kdm_menu);\t//it doesn't want it then...\n}\nstatic void Menu_UpdateFocus(void)\n{\n\tif (!promptmenu || !promptmenu->keyevent)\n\t\tKey_Dest_Remove(kdm_prompt);\t//can't take key presses, so don't take keys.\n\tif (promptmenu && promptmenu->cursor)\n\t\tkey_dest_absolutemouse |= kdm_prompt;\n\telse\n\t\tkey_dest_absolutemouse &= ~kdm_prompt;\n\n\tif (!topmenu || !topmenu->keyevent)\n\t\tKey_Dest_Remove(kdm_menu);\t//can't take key presses, so don't take keys.\n\tif (topmenu && topmenu->cursor)\n\t\tkey_dest_absolutemouse |= kdm_menu;\n\telse\n\t\tkey_dest_absolutemouse &= ~kdm_menu;\n}\nqboolean Menu_IsLinked(menu_t *menu)\n{\n\tmenu_t *link;\n\tfor (link = topmenu; link; link = link->prev)\n\t{\n\t\tif (menu == link)\n\t\t\treturn true;\n\t}\n\treturn false;\n}\nmenu_t *Menu_FindContext(void *ctx)\n{\n\tmenu_t *link;\n\tfor (link = promptmenu; link; link = link->prev)\n\t{\n\t\tif (link->ctx == ctx)\n\t\t\treturn link;\n\t}\n\tfor (link = topmenu; link; link = link->prev)\n\t{\n\t\tif (link->ctx == ctx)\n\t\t\treturn link;\n\t}\n\treturn NULL;\n}\nvoid Menu_Unlink(menu_t *menu, qboolean forced)\n{\n\tmenu_t **link;\n\tfor (link = &promptmenu; *link; link = &(*link)->prev)\n\t{\n\t\tif (menu == *link)\n\t\t{\n\t\t\t*link = menu->prev;\n\t\t\tif (menu->release)\n\t\t\t\tmenu->release(menu, forced);\n\n\t\t\tMenu_UpdateFocus();\n\t\t\treturn;\n\t\t}\n\t}\n\tfor (link = &topmenu; *link; link = &(*link)->prev)\n\t{\n\t\tif (menu == *link)\n\t\t{\n\t\t\t*link = menu->prev;\n\t\t\tif (menu->release)\n\t\t\t\tmenu->release(menu, forced);\n\n\t\t\tMenu_UpdateFocus();\n\t\t\treturn;\n\t\t}\n\t}\n}\nvoid Menu_Push(menu_t *menu, qboolean prompt)\n{\n\tif (!Menu_IsLinked(menu))\n\t{\t//only link once.\n\t\t//annoying logic so that persistent menus always appear on top of other stuff.\n\t\tmenu_t **prev = prompt?&promptmenu:&topmenu;\n\t\twhile (menu->lowpriority && *prev && !(*prev)->lowpriority)\n\t\t\tprev = &(*prev)->prev;\n\t\tmenu->prev = *prev;\n\t\t*prev = menu;\n\t}\n\tif (menu == promptmenu)\n\t{\n\t\tif (!Key_Dest_Has(kdm_prompt))\n\t\t\tVRUI_SnapAngle();\n\t\tKey_Dest_Add(kdm_prompt);\n\t\tMenu_UpdateFocus();\n\t}\n\tif (menu == topmenu)\n\t{\n\t\tif (!Key_Dest_Has(kdm_menu))\n\t\t\tVRUI_SnapAngle();\n\t\tKey_Dest_Add(kdm_menu);\n\t\tMenu_UpdateFocus();\n\t}\n}\nvoid Menu_PopAll(void)\n{\n\tmenu_t **menus, *menu;\n\tsize_t count, i;\n\t//first loop to count them\n\tfor (count = 0, menu = topmenu; menu; menu = menu->prev)\n\t{\n\t\tif (menu->persist)\n\t\t\tcontinue;\n\t\tcount++;\n\t}\n\tmenus = alloca(sizeof(*menus)*count);\n\t//second loop to track them\n\tfor (i = 0, menu = topmenu; i < count && menu; menu = menu->prev)\n\t{\n\t\tif (menu->persist)\n\t\t\tcontinue;\n\t\tmenus[i++] = menu;\n\t}\n\t//third link to actually unlink them safely without unlinking multiple times etc (grr menuqc mods re-grabbing focus when closing)\n\tfor (i = 0; i < count; i++)\n\t\tMenu_Unlink(menus[i], true);\n}\n\nint Menu_WantOSK(void)\n{\n\tif (promptmenu)\n\t\treturn promptmenu->showosk;\n\tif (topmenu)\n\t\treturn topmenu->showosk;\n\treturn -1;\n}\n\nvoid Menu_Draw(void)\n{\n#ifdef MENU_DAT\n\t//shitty always-drawn crap\n\tMP_Draw();\n#endif\n\n\t//draw whichever menu has focus\n\tif (topmenu && topmenu->drawmenu)\n\t\ttopmenu->drawmenu(topmenu);\n}\nvoid Prompts_Draw(void)\n{\n\t//prompts always appear over the top of everything else, particuarly other menus.\n\tif (promptmenu && promptmenu->drawmenu)\n\t\tpromptmenu->drawmenu(promptmenu);\n}\n\nvoid M_DrawScalePic (int x, int y, int w, int h, mpic_t *pic)\n{\n\tR2D_ScalePic (x + ((vid.width - 320)>>1), y, w, h, pic);\n}\nvoid M_DrawTextBox (int x, int y, int width, int lines)\n{\n\tmpic_t\t*p;\n\tint\t\tcx, cy;\n\tint\t\tn, w;\n\n\t// draw left side\n\tcx = x;\n\tcy = y;\n\tp = R2D_SafeCachePic (\"gfx/box_tl.lmp\");\n\tswitch(R_GetShaderSizes(p, NULL, NULL, false))\n\t{\n\tcase -1:\n\t\treturn;\t//still pending\n\tcase 0:\n\t\tR2D_ImageColours(0.0, 0.0, 0.0, 1.0);\n\t\tR2D_FillBlock(x + ((vid.width - 320)>>1), y, width*8+16, lines*8+16);\n\t\tR2D_ImageColours(1.0, 1.0, 1.0, 1.0);\n\t\treturn;\n\t}\n\tM_DrawScalePic (cx, cy, 8, 8, p);\n\tp = R2D_SafeCachePic (\"gfx/box_ml.lmp\");\n\tif (p)\n\t\tfor (n = 0; n < lines; n++)\n\t\t{\n\t\t\tcy += 8;\n\t\t\tM_DrawScalePic (cx, cy, 8, 8, p);\n\t\t}\n\tp = R2D_SafeCachePic (\"gfx/box_bl.lmp\");\n\tif (p)\n\t\tM_DrawScalePic (cx, cy+8, 8, 8, p);\n\n\t// draw middle\n\tcy = y;\n\tcx += 8;\n\t//top-strip\n\tp = R2D_SafeCachePic (\"gfx/box_tm.lmp\");\n\tif (p) for (w = 0; w < width; w+=2)\n\t\tM_DrawScalePic (cx+w*8, cy, 16, 8, p);\n\n\t//just-under-top (shadowed region)\n\tif (lines)\n\t{\n\t\tcy+=8;\n\t\tp = R2D_SafeCachePic (\"gfx/box_mm.lmp\");\n\t\tif (p) for (w = 0; w < width; w+=2)\n\t\t\tM_DrawScalePic (cx+w*8, cy, 16, 8, p);\n\t}\n\n\t//2d body\n\tp = R2D_SafeCachePic (\"gfx/box_mm2.lmp\");\n\tfor (n = 1; n < lines; n++)\n\t{\n\t\tcy+=8;\n\t\tif (p) for (w = 0; w < width; w+=2)\n\t\t\tM_DrawScalePic (cx+w*8, cy, 16, 8, p);\n\t}\n\n\t//bottom strip\n\tcy+=8;\n\tp = R2D_SafeCachePic (\"gfx/box_bm.lmp\");\n\tif (p) for (w = 0; w < width; w+=2)\n\t\tM_DrawScalePic (cx+w*8, cy, 16, 8, p);\n\n\tcx += 8*width;\n\n\t// draw right side\n\tcy = y;\n\tp = R2D_SafeCachePic (\"gfx/box_tr.lmp\");\n\tif (p)\n\t\tM_DrawScalePic (cx, cy, 8, 8, p);\n\tp = R2D_SafeCachePic (\"gfx/box_mr.lmp\");\n\tif (p)\n\t\tfor (n = 0; n < lines; n++)\n\t\t{\n\t\t\tcy += 8;\n\t\t\tM_DrawScalePic (cx, cy, 8, 8, p);\n\t\t}\n\tp = R2D_SafeCachePic (\"gfx/box_br.lmp\");\n\tif (p)\n\t\tM_DrawScalePic (cx, cy+8, 8, 8, p);\n}\n\nint QDECL M_FindKeysForBind (int bindmap, const char *command, int *keylist, int *keymods, int keycount)\n{\n\tint\t\tcount;\n\tint\t\tj, m;\n\tint\t\tl, p;\n\tchar\t*b;\n\tint\t\tfirstmod, lastmod;\n\n\tl = strlen(command);\n\tcount = 0;\n\n\tif (bindmap > 0 && bindmap <= KEY_MODIFIER_ALTBINDMAP)\n\t{\n\t\t//bindmaps don't support modifiers\n\t\tfirstmod = (bindmap-1)|KEY_MODIFIER_ALTBINDMAP;\n\t\tlastmod = firstmod+1;\n\t}\n\telse\n\t{\n\t\tfirstmod = 0;\n\t\tlastmod = KEY_MODIFIER_ALTBINDMAP;\n\t}\n\n\tfor (j=0 ; j<K_MAX ; j++)\n\t{\n\t\tfor (m = firstmod; m < lastmod; m++)\n\t\t{\n\t\t\tb = keybindings[j][m];\n\t\t\tif (!b)\n\t\t\t\tcontinue;\n\t\t\tif (!strncmp (b, command, l) && (!b[l] || b[l] == ' ' || b[l] == ';'))\n\t\t\t{\n\t\t\t\t//if ctrl_a and ctrl_shift_a do the same thing, don't report ctrl_shift_a because its redundant.\n\t\t\t\tfor (p = firstmod; p < m; p++)\n\t\t\t\t{\n\t\t\t\t\tif (p&~m)\t//ignore shift_a if we're checking ctrl_a\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\tif (keybindings[j][p] && !strcmp(keybindings[j][p], b))\n\t\t\t\t\t\tbreak;\t//break+continue\n\t\t\t\t}\n\t\t\t\tif (p != m)\n\t\t\t\t\tcontinue;\n\n\t\t\t\tkeylist[count] = j;\n\t\t\t\tif (keymods)\n\t\t\t\t\tkeymods[count] = m;\n\t\t\t\tcount++;\n\t\t\t\tif (count == keycount)\n\t\t\t\t\treturn count;\n\t\t\t}\n\t\t}\n\t}\n\tfor (j = count; j < keycount; j++)\n\t{\n\t\tkeylist[j] = -1;\n\t\tif (keymods)\n\t\t\tkeymods[j] = 0;\n\t}\n\treturn count;\n}\nint M_FindKeysForCommand (int bindmap, int pnum, const char *command, int *keylist, int *keymods, int keycount)\n{\n\tchar prefix[5];\n\n\tif (*command == '+' || *command == '-')\n\t{\n\t\tprefix[0] = *command;\n\t\tprefix[1] = 0;\n\t\tif (pnum != 0)\n\t\t{\n\t\t\tprefix[1] = 'p';\n\t\t\tprefix[2] = '0'+pnum;\n\t\t\tprefix[3] = ' ';\n\t\t\tprefix[4] = 0;\n\t\t}\n\t\tcommand++;\n\t}\n\telse\n\t{\n\t\tprefix[0] = 0;\n\t\tif (pnum != 0)\n\t\t{\n\t\t\tprefix[0] = 'p';\n\t\t\tprefix[1] = '0'+pnum;\n\t\t\tprefix[2] = ' ';\n\t\t\tprefix[3] = 0;\n\t\t}\n\t}\n\treturn M_FindKeysForBind(bindmap, va(\"%s%s\", prefix, command), keylist, keymods, keycount);\n}\n\n/*\n================\nM_ToggleMenu_f\n================\n*/\nvoid M_ToggleMenu_f (void)\n{\t\n\tif (topmenu)\n\t{\n\t\tKey_Dest_Add(kdm_menu);\n\t\treturn;\n\t}\n\n#ifdef CSQC_DAT\n\tif (CSQC_ConsoleCommand(-1, \"togglemenu\"))\n\t{\n\t\tKey_Dest_Remove(kdm_console|kdm_cwindows);\n\t\treturn;\n\t}\n#endif\n#ifdef MENU_DAT\n\tif (MP_Toggle(1))\n\t{\n\t\tKey_Dest_Remove(kdm_console|kdm_cwindows);\n\t\treturn;\n\t}\n#endif\n#ifdef MENU_NATIVECODE\n\tif (mn_entry)\n\t{\n\t\tmn_entry->Toggle(1);\n\t\tKey_Dest_Remove(kdm_console|kdm_cwindows);\n\t\treturn;\n\t}\n#endif\n#ifdef VM_UI\n\tif (q3 && q3->ui.OpenMenu())\n\t\treturn;\n#endif\n\n#ifndef NOBUILTINMENUS\n\t{\n\t\textern cvar_t cl_disconnectreason;\n\t\tif (*cl_disconnectreason.string)\n\t\t{\n\t\t\tMenu_Prompt(NULL, NULL, cl_disconnectreason.string, NULL, NULL, \"Okay\", true);\n\t\t\tCvar_Set(&cl_disconnectreason, \"\");\n\t\t}\n\t}\n\n\tM_Menu_Main_f ();\n\tKey_Dest_Remove(kdm_console|kdm_cwindows);\n#endif\n}\n\n/*\n================\nM_Restart_f\n================\n*/\nvoid M_Init_Internal (void);\nvoid M_Restart_f(void)\n{\n\tM_Shutdown(false);\n\n\tif (!strcmp(Cmd_Argv(1), \"off\"))\n\t{\t//explicitly restart the engine's menu. not the menuqc crap\n\t\t//don't even start csqc menus.\n\t\tM_Init_Internal();\n\t}\n\telse\n\t\tM_Reinit();\n\n\t//start up the ui now we have a renderer\n#ifdef VM_UI\n\tif (q3)\n\t\tq3->ui.Start();\n#endif\n}\n\n\n//=============================================================================\n/* Various callback-based prompts */\n\ntypedef struct\n{\n\tmenu_t m;\n\tvoid (*callback)(void *, promptbutton_t);\n\tvoid *ctx;\n\n\tconchar_t *msg;\n\tsize_t msglen;\n\tconst char *buttons[3];\n\tint kbutton, mbutton;\n\tqboolean mousedown;\n} promptmenu_t;\nstatic qboolean Prompt_MenuKeyEvent(struct menu_s *gm, qboolean isdown, unsigned int devid, int key, int unicode)\n{\n\tpromptmenu_t *m = (promptmenu_t*)gm;\n\tpromptbutton_t action;\n\tvoid (*callback)(void *, promptbutton_t) = m->callback;\n\tvoid *ctx = m->ctx;\n\n\tif (key == K_MOUSE1 || key == K_TOUCHTAP)\n\t{\t//mouse events fire their action on release.\n\t\tif (isdown)\n\t\t{\n\t\t\tm->mousedown = true;\t//so we don't respond to stray release events.\n\t\t\treturn true;\n\t\t}\n\t\telse if (!m->mousedown)\n\t\t\treturn false;\t\t\t//looks like a stray release event. ignore it.\n\t}\n\telse\n\t{\t//keyboard events fire on press.\n\t\tif (!isdown)\n\t\t\treturn false;\n\t}\n\n\tif (key == 'n' || key == 'N')\n\t\taction = PROMPT_NO;\n\telse if (key == 'y' || key == 'Y')\n\t\taction = PROMPT_YES;\n\telse if (key==K_RIGHTARROW || key==K_GP_DPAD_RIGHT || key==K_GP_LEFT_THUMB_RIGHT || key==K_DOWNARROW || key==K_GP_DPAD_DOWN || key==K_GP_LEFT_THUMB_DOWN || key == K_GP_DIAMOND_ALTCONFIRM || (key == K_TAB && !keydown[K_LSHIFT] && !keydown[K_RSHIFT]))\n\t{\n\t\tint start = m->kbutton;\n\t\tfor(;;)\n\t\t{\n\t\t\tm->kbutton++;\n\t\t\tif (start == m->kbutton)\n\t\t\t\tbreak;\n\t\t\tif (m->kbutton >= 3)\n\t\t\t\tm->kbutton -= 3;\n\t\t\tif (m->buttons[m->kbutton])\n\t\t\t\tbreak;\n\t\t}\n\t\treturn true;\n\t}\n\telse if (key == K_LEFTARROW || key == K_GP_DPAD_LEFT || key==K_GP_LEFT_THUMB_LEFT || key==K_UPARROW || key==K_GP_DPAD_UP || key==K_GP_LEFT_THUMB_UP || key==K_TAB)\n\t{\n\t\tint start = m->kbutton;\n\t\tfor(;;)\n\t\t{\n\t\t\tm->kbutton--;\n\t\t\tif (start == m->kbutton)\n\t\t\t\tbreak;\n\t\t\tif (m->kbutton < 0)\n\t\t\t\tm->kbutton += 3;\n\t\t\tif (m->buttons[m->kbutton])\n\t\t\t\tbreak;\n\t\t}\n\t\treturn true;\n\t}\n\telse if (key == K_ESCAPE || key == K_GP_BACK || key == K_MOUSE2 || key == K_MOUSE4 || key == K_GP_DIAMOND_CANCEL)\n\t\taction = PROMPT_CANCEL;\n\telse if (key == K_ENTER || key == K_KP_ENTER || key == K_MOUSE1 || key == K_TOUCHTAP || key == K_GP_DIAMOND_CONFIRM)\n\t{\n\t\tint button;\n\t\tif (key == K_MOUSE1 || key == K_TOUCHTAP)\n\t\t\tbutton = m->mbutton;\n\t\telse\n\t\t\tbutton = m->kbutton;\n\n\t\tswitch(button)\n\t\t{\n\t\tcase 0:\n\t\t\taction = PROMPT_YES;\n\t\t\tbreak;\n\t\tcase 1:\n\t\t\taction = PROMPT_NO;\n\t\t\tbreak;\n\t\tcase 2:\n\t\t\taction = PROMPT_CANCEL;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\treturn false;\t//nothing focused.\n\t\t}\n\t}\n\telse\n\t\treturn false; // no idea what that is\n\n\tm->callback = NULL;\t//so the remove handler can't fire.\n\tMenu_Unlink(&m->m, false);\n\tif (callback)\n\t\tcallback(ctx, action);\n\n\treturn true;\n}\nstatic void Prompt_Draw(struct menu_s *g)\n{\n\tpromptmenu_t *m = (promptmenu_t*)g;\n\tint x = 64;\n\tint y = 76;\n\tint px, py;\n\tfloat scale = Font_CharVHeight(font_console);\n\tint w = 320*scale/8;\n\tint h, lines;\n\tint i;\n\tint bx[4];\n\tconchar_t *linestart[16];\n\tconchar_t *lineend[countof(linestart)];\n\n\tx = (((int)vid.width-w)>>1);\n\n\tFont_BeginString(font_console, w, 0, &px, &py);\n\tlines = Font_LineBreaks(m->msg, m->msg+m->msglen, px, countof(linestart), linestart, lineend);\n\th = (lines+3)*scale;\n\tFont_EndString(font_console);\n\n\tDraw_ApproxTextBox(x, y, w, h);\n\ty+=scale;\n\n\tFont_BeginString(font_console, x, y, &px, &py);\n\tfor (i = 0; i < lines; i++)\n\t{\n\t\tint xoffset = (w*vid.rotpixelwidth/vid.width) - Font_LineWidth(linestart[i], lineend[i]);\n\t\tFont_LineDraw(px + xoffset/2, py, linestart[i], lineend[i]);\n\t\tpy+=Font_CharHeight();\n\t}\n\tFont_EndString(font_console);\n\n\ty+=scale*lines;\n\ty+=scale;\n\tm->mbutton = -1;\n\tbx[0] = x;\n\tbx[1] = x+w/3;\n\tbx[2] = x+w-w/3;\n\tbx[3] = x+w;\n\tif (mousecursor_y >= y && mousecursor_y <= y+scale)\n\t{\n\t\tfor (i = 0; i < 3; i++)\n\t\t{\n\t\t\tif (m->buttons[i] && mousecursor_x >= bx[i] && mousecursor_x < bx[i+1])\n\t\t\t\tm->mbutton = i;\n\t\t}\n\t}\n\tfor (i = 0; i < 3; i++)\n\t{\n\t\tif (m->buttons[i])\n\t\t{\n\t\t\tif (m->mbutton == i)\n\t\t\t{\n\t\t\t\tfloat alphamax = 0.5, alphamin = 0.2;\n\t\t\t\tR2D_ImageColours(.5,.4,0,(sin(realtime*2)+1)*0.5*(alphamax-alphamin)+alphamin);\n\t\t\t\tR2D_FillBlock(bx[i], y, bx[i+1]-bx[i], scale);\n\t\t\t\tR2D_ImageColours(1,1,1,1);\n\t\t\t}\n\t\t\tDraw_FunStringWidthFont(font_console, bx[i], y, m->buttons[i], bx[i+1]-bx[i], 2, m->kbutton==i);\n\t\t}\n\t}\n}\nstatic void Prompt_Release(struct menu_s *gm, qboolean forced)\n{\n\tpromptmenu_t *m = (promptmenu_t*)gm;\n\tvoid (*callback)(void *, promptbutton_t) = m->callback;\n\tvoid *ctx = m->ctx;\n\tm->callback = NULL;\n\n\tif (callback)\n\t\tcallback(ctx, PROMPT_CANCEL);\n\tZ_Free(m);\n}\nvoid Menu_Prompt (void (*callback)(void *, promptbutton_t), void *ctx, const char *messages, const char *optionyes, const char *optionno, const char *optioncancel, qboolean highpri)\n{\n\tpromptmenu_t *m;\n\tchar *t;\n\tconchar_t message[8192], *e;\n\n\tif (optionyes)\n\t\toptionyes = localtext(optionyes);\n\tif (optionno)\n\t\toptionno = localtext(optionno);\n\tif (optioncancel)\n\t\toptioncancel = localtext(optioncancel);\n\n\te = COM_ParseFunString(CON_WHITEMASK, messages, message, sizeof(message)-sizeof(conchar_t), false);\n\n\tm = (promptmenu_t*)Z_Malloc(sizeof(*m) + (e-message)*sizeof(conchar_t)+(optionyes?strlen(optionyes):0)+(optionno?strlen(optionno):0)+(optioncancel?strlen(optioncancel):0)+7);\n\n\tm->m.cursor = &key_customcursor[kc_console];\n\t/*void (*videoreset)\t(struct menu_s *);\t//called after a video mode switch / shader reload.\n\tvoid (*release)\t\t(struct menu_s *);\t//\n\tqboolean (*keyevent)(struct menu_s *, qboolean isdown, unsigned int devid, int key, int unicode);\t//true if key was handled\n\tqboolean (*mousemove)(struct menu_s *, qboolean abs, unsigned int devid, float x, float y);\n\tqboolean (*joyaxis)\t(struct menu_s *, unsigned  int devid, int axis, float val);\n\tvoid (*drawmenu)\t(struct menu_s *);\n\t*/\n\tm->m.drawmenu = Prompt_Draw;\n\tm->m.keyevent = Prompt_MenuKeyEvent;\n\tm->m.release = Prompt_Release;\n\tm->mbutton = -1;\n\tm->kbutton = -1;\n\tm->m.persist = true;\n\tMenu_Push(&m->m, highpri);\n\n\tm->callback = callback;\n\tm->ctx = ctx;\n\n\tt = (char*)(m+1);\n\tif (optionyes)\n\t{\n\t\tm->buttons[0] = t;\n\t\tstrcpy(t, optionyes);\n\t\tt += strlen(t)+1;\n\t}\n\tif (optionno)\n\t{\n\t\tm->buttons[1] = t;\n\t\tstrcpy(t, optionno);\n\t\tt += strlen(t)+1;\n\t}\n\tif (optioncancel)\n\t{\n\t\tm->buttons[2] = t;\n\t\tstrcpy(t, optioncancel);\n\t\tt += strlen(t)+1;\n\t}\n\n\tm->msglen = e-message;\n\tm->msg = memcpy(t, message, (m->msglen)*sizeof(conchar_t));\n}\n\n#ifndef NOBUILTINMENUS\n\nvoid M_Menu_Audio_f (void);\nvoid M_Menu_Demos_f (void);\nvoid M_Menu_Mods_f (void);\nvoid M_Menu_ModelViewer_f(void);\nvoid M_Menu_ModelViewer_c(int argn, const char *partial, struct xcommandargcompletioncb_s *ctx);\n\nvoid M_ConfigureNetSubsystem(void);\n\ncvar_t m_helpismedia = CVAR(\"m_helpismedia\", \"0\");\ncvar_t m_preset_chosen = CVARF(\"m_preset_chosen\", \"0\", CVAR_ARCHIVE);\n\n//=============================================================================\n/* Support Routines */\n\nvoid M_Print (int cx, int cy, qbyte *str)\n{\n\tDraw_AltFunString(cx + ((vid.width - 320)>>1), cy, str);\n}\nvoid M_PrintWhite (int cx, int cy, qbyte *str)\n{\n\tDraw_FunString(cx + ((vid.width - 320)>>1), cy, str);\n}\n\nvoid M_BuildTranslationTable(unsigned int pc, unsigned int top, unsigned int bottom, unsigned int *translationTable)\n{\n\tint\t\tj;\n#ifdef HEXEN2\n\tif (h2playertranslations && pc)\n\t{\n\t\tint i;\n\t\tunsigned int color_offsets[5] = {2*14*256,0,1*14*256,2*14*256,2*14*256};\n\t\tunsigned char *colorA, *colorB, *sourceA, *sourceB;\n\t\tcolorA = h2playertranslations + 256 + color_offsets[pc-1];\n\t\tcolorB = colorA + 256;\n\t\tsourceA = colorB + (top * 256);\n\t\tsourceB = colorB + (bottom * 256);\n\t\tfor(i=0;i<255;i++)\n\t\t{\n\t\t\tif (bottom > 0 && (colorB[i] != 255))\n\t\t\t{\n\t\t\t\tif (bottom >= 16)\n\t\t\t\t{\n\t\t\t\t\tunsigned int v = d_8to24rgbtable[colorB[i]];\n\t\t\t\t\tv = max(max((v>>0)&0xff, (v>>8)&0xff), (v>>16)&0xff);\n\t\t\t\t\t*((unsigned char*)&translationTable[i]+0) = (((bottom&0xff0000)>>16)*v)>>8;\n\t\t\t\t\t*((unsigned char*)&translationTable[i]+1) = (((bottom&0x00ff00)>> 8)*v)>>8;\n\t\t\t\t\t*((unsigned char*)&translationTable[i]+2) = (((bottom&0x0000ff)>> 0)*v)>>8;\n\t\t\t\t\t*((unsigned char*)&translationTable[i]+3) = 0xff;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\ttranslationTable[i] = d_8to24rgbtable[sourceB[i]] | 0xff000000;\n\t\t\t}\n\t\t\telse if (top > 0 && (colorA[i] != 255))\n\t\t\t{\n\t\t\t\tif (top >= 16)\n\t\t\t\t{\n\t\t\t\t\tunsigned int v = d_8to24rgbtable[colorA[i]];\n\t\t\t\t\tv = max(max((v>>0)&0xff, (v>>8)&0xff), (v>>16)&0xff);\n\t\t\t\t\t*((unsigned char*)&translationTable[i]+0) = (((top&0xff0000)>>16)*v)>>8;\n\t\t\t\t\t*((unsigned char*)&translationTable[i]+1) = (((top&0x00ff00)>> 8)*v)>>8;\n\t\t\t\t\t*((unsigned char*)&translationTable[i]+2) = (((top&0x0000ff)>> 0)*v)>>8;\n\t\t\t\t\t*((unsigned char*)&translationTable[i]+3) = 0xff;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\ttranslationTable[i] = d_8to24rgbtable[sourceA[i]] | 0xff000000;\n\t\t\t}\n\t\t\telse\n\t\t\t\ttranslationTable[i] = d_8to24rgbtable[i] | 0xff000000;\n\t\t}\n\t}\n\telse\n#endif\n\t{\n\t\tfor(j=0;j<255;j++)\n\t\t{\n\t\t\tif (j >= TOP_RANGE && j < TOP_RANGE + (1<<4))\n\t\t\t{\n\t\t\t\tif (top >= 16)\n\t\t\t\t{\n\t\t\t\t\t*((unsigned char*)&translationTable[j]+0) = (((top&0xff0000)>>16)**((unsigned char*)&d_8to24rgbtable[j&15]+0))>>8;\n\t\t\t\t\t*((unsigned char*)&translationTable[j]+1) = (((top&0x00ff00)>> 8)**((unsigned char*)&d_8to24rgbtable[j&15]+1))>>8;\n\t\t\t\t\t*((unsigned char*)&translationTable[j]+2) = (((top&0x0000ff)>> 0)**((unsigned char*)&d_8to24rgbtable[j&15]+2))>>8;\n\t\t\t\t\t*((unsigned char*)&translationTable[j]+3) = 0xff;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\ttranslationTable[j] = d_8to24rgbtable[top<8?j-TOP_RANGE+(top<<4):(top<<4)+15-(j-TOP_RANGE)] | 0xff000000;\n\t\t\t}\n\t\t\telse if (j >= BOTTOM_RANGE && j < BOTTOM_RANGE + (1<<4))\n\t\t\t{\n\t\t\t\tif (bottom >= 16)\n\t\t\t\t{\n\t\t\t\t\t*((unsigned char*)&translationTable[j]+0) = (((bottom&0xff0000)>>16)**((unsigned char*)&d_8to24rgbtable[j&15]+0))>>8;\n\t\t\t\t\t*((unsigned char*)&translationTable[j]+1) = (((bottom&0x00ff00)>> 8)**((unsigned char*)&d_8to24rgbtable[j&15]+1))>>8;\n\t\t\t\t\t*((unsigned char*)&translationTable[j]+2) = (((bottom&0x0000ff)>> 0)**((unsigned char*)&d_8to24rgbtable[j&15]+2))>>8;\n\t\t\t\t\t*((unsigned char*)&translationTable[j]+3) = 0xff;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\ttranslationTable[j] = d_8to24rgbtable[bottom<8?j-BOTTOM_RANGE+(bottom<<4):(bottom<<4)+15-(j-BOTTOM_RANGE)] | 0xff000000;\n\t\t\t}\n\t\t\telse\n\t\t\t\ttranslationTable[j] = d_8to24rgbtable[j] | 0xff000000;\n\t\t}\n\t}\n\ttranslationTable[255] = 0;\t//alpha\n}\n\n//=============================================================================\n\nvoid M_CloseMenu_f (void)\n{\n\tif (!Key_Dest_Has(kdm_menu))\n\t\treturn;\n\tM_RemoveAllMenus(false);\n}\n\n//=============================================================================\n/* KEYS MENU */\n\ntypedef struct {\n\tchar *command;\n\tchar *name;\n} bindnames_t;\n\nbindnames_t qwbindnames[] =\n{\n{\"+attack\", \t\t\"attack        \"},\n{\"impulse 10\", \t\t\"change weapon \"},\n{\"impulse 12\", \t\t\"prev weapon   \"},\n{\"+jump\", \t\t\t\"jump / swim up\"},\n{\"+forward\", \t\t\"walk forward  \"},\n{\"+back\", \t\t\t\"backpedal     \"},\n{\"+left\", \t\t\t\"turn left     \"},\n{\"+right\", \t\t\t\"turn right    \"},\n{\"+speed\", \t\t\t\"run           \"},\n{\"+moveleft\", \t\t\"step left     \"},\n{\"+moveright\", \t\t\"step right    \"},\n{\"+strafe\", \t\t\"sidestep      \"},\n{\"+lookup\", \t\t\"look up       \"},\n{\"+lookdown\", \t\t\"look down     \"},\n{\"centerview\", \t\t\"center view   \"},\n{\"+mlook\", \t\t\t\"mouse look    \"},\n{\"+klook\", \t\t\t\"keyboard look \"},\n{\"+moveup\",\t\t\t\"swim up       \"},\n{\"+movedown\",\t\t\"swim down     \"},\n#ifdef VOICECHAT\n{\"+voip\",\t\t\t\"voice chat    \"},\n#endif\n{NULL}\n};\n\n#ifdef Q2CLIENT\nbindnames_t q2bindnames[] =\n{\n{\"+attack\", \t\t\"attack        \"},\n{\"cmd weapnext\", \t\"next weapon   \"},\n{\"cmd weapprev\", \t\"prev weapon   \"},\n{\"+forward\", \t\t\"walk forward  \"},\n{\"+back\", \t\t\t\"backpedal     \"},\n{\"+left\", \t\t\t\"turn left     \"},\n{\"+right\", \t\t\t\"turn right    \"},\n{\"+speed\", \t\t\t\"run           \"},\n{\"+moveleft\", \t\t\"step left     \"},\n{\"+moveright\", \t\t\"step right    \"},\n{\"+strafe\", \t\t\"sidestep      \"},\n{\"+lookup\", \t\t\"look up       \"},\n{\"+lookdown\", \t\t\"look down     \"},\n{\"centerview\", \t\t\"center view   \"},\n{\"+mlook\", \t\t\t\"mouse look    \"},\n{\"+klook\", \t\t\t\"keyboard look \"},\n{\"+moveup\",\t\t\t\"up / jump     \"},\n{\"+movedown\",\t\t\"down / crouch \"},\n\n{\"cmd inven\",\t\t\"inventory     \"},\n{\"cmd invuse\",\t\t\"use item      \"},\n{\"cmd invdrop\",\t\t\"drop item     \"},\n{\"cmd invprev\",\t\t\"prev item     \"},\n{\"cmd invnext\",\t\t\"next item     \"},\n\n{\"cmd help\", \t\t\"help computer \"},\n{NULL}\n};\n#endif\n\n\nbindnames_t h2bindnames[] =\n{\n{\"+attack\", \t\t\"attack        \"},\n{\"impulse 10\", \t\t\"change weapon \"},\n{\"+jump\", \t\t\t\"jump / swim up\"},\n{\"+forward\", \t\t\"walk forward  \"},\n{\"+back\", \t\t\t\"backpedal     \"},\n{\"+left\", \t\t\t\"turn left     \"},\n{\"+right\", \t\t\t\"turn right    \"},\n{\"+speed\", \t\t\t\"run           \"},\n{\"+moveleft\", \t\t\"step left     \"},\n{\"+moveright\", \t\t\"step right    \"},\n{\"+strafe\", \t\t\"sidestep      \"},\n{\"+crouch\",\t\t\t\"crouch        \"},\n{\"+lookup\", \t\t\"look up       \"},\n{\"+lookdown\", \t\t\"look down     \"},\n{\"centerview\", \t\t\"center view   \"},\n{\"+mlook\", \t\t\t\"mouse look    \"},\n{\"+klook\", \t\t\t\"keyboard look \"},\n{\"+moveup\",\t\t\t\"swim up       \"},\n{\"+movedown\",\t\t\"swim down     \"},\n{\"impulse 13\", \t\t\"lift object   \"},\n{\"invuse\",\t\t\t\"use inv item  \"},\n{\"impulse 44\",\t\t\"drop inv item \"},\n{\"+showinfo\",\t\t\"full inventory\"},\n{\"+showdm\",\t\t\t\"info / frags  \"},\n//{\"toggle_dm\",\t\t\"toggle frags  \"},\n{\"+infoplaque\",\t\t\"objectives    \"},\t//requires pulling info out of the mod... on the client.\n{\"invleft\",\t\t\t\"inv move left \"},\n{\"invright\",\t\t\"inv move right\"},\n{\"impulse 100\",\t\t\"inv:torch     \"},\n{\"impulse 101\",\t\t\"inv:qrtz flask\"},\n{\"impulse 102\",\t\t\"inv:mystic urn\"},\n{\"impulse 103\",\t\t\"inv:krater    \"},\n{\"impulse 104\",\t\t\"inv:chaos devc\"},\n{\"impulse 105\",\t\t\"inv:tome power\"},\n{\"impulse 106\",\t\t\"inv:summon stn\"},\n{\"impulse 107\",\t\t\"inv:invisiblty\"},\n{\"impulse 108\",\t\t\"inv:glyph     \"},\n{\"impulse 109\",\t\t\"inv:boots     \"},\n{\"impulse 110\",\t\t\"inv:repulsion \"},\n{\"impulse 111\",\t\t\"inv:bo peep   \"},\n{\"impulse 112\",\t\t\"inv:flight    \"},\n{\"impulse 113\",\t\t\"inv:force cube\"},\n{\"impulse 114\",\t\t\"inv:icon defn \"},\n#ifdef VOICECHAT\n{\"+voip\",\t\t\t\"voice chat    \"},\n#endif\n{NULL}\n};\n\nbindnames_t *bindnames;\nint numbindnames;\n\nint\t\tkeys_cursor;\nint\t\tbind_grab;\n\nvoid M_Menu_Keys_f (void)\n{\n\tint y;\n\temenu_t *menu;\n\tvfsfile_t *bindslist;\n\n\tmenu = M_CreateMenu(0);\n\tswitch(M_GameType())\n\t{\n#ifdef Q2CLIENT\n\tcase MGT_QUAKE2:\n\t\tMC_AddCenterPicture(menu, 0, 24, \"pics/m_banner_customize.pcx\");\n\t\ty = 48;\n\t\tbindnames = q2bindnames;\n\t\tbreak;\n#endif\n#ifdef HEXEN2\n\tcase MGT_HEXEN2:\n\t\tMC_AddCenterPicture(menu, 0, 60, \"gfx/menu/title6.lmp\");\n\t\ty = 64;\n\t\tbindnames = h2bindnames;\n\t\tbreak;\n#endif\n\tdefault:\n\t\tMC_AddCenterPicture(menu, 4, 24, \"gfx/ttl_cstm.lmp\");\n\t\ty = 48;\n\t\tbindnames = qwbindnames;\n\t\tbreak;\n\t}\n\n#if MAX_SPLITS > 1\n\tif (cl.splitclients || cl_splitscreen.ival || cl_forceseat.ival)\n\t{\n\t\tstatic char *texts[MAX_SPLITS+2] =\n\t\t{\n\t\t\t\"Depends on device\",\n\t\t\t\"Player 1\",\n\t\t\t\"Player 2\",\n#if MAX_SPLITS >= 3\n\t\t\t\"Player 3\",\n#endif\n#if MAX_SPLITS >= 4\n\t\t\t\"Player 4\",\n#endif\n\t\t\tNULL\n\t\t};\n\t\tstatic char *values[MAX_SPLITS+1] =\n\t\t{\n\t\t\t\"0\",\n\t\t\t\"1\",\n\t\t\t\"2\",\n#if MAX_SPLITS >= 3\n\t\t\t\"3\",\n#endif\n#if MAX_SPLITS >= 4\n\t\t\t\"4\"\n#endif\n\t\t};\n\t\tMC_AddCvarCombo(menu, 16, 170, y, localtext(\"Force client\"), &cl_forceseat, (const char **)texts, (const char **)values);\n\t\ty+=8;\n\t}\n#endif\n\n\tbindslist = FS_OpenVFS(\"bindlist.lst\", \"rb\", FS_GAME);\n\tif (bindslist)\n\t{\n\t\tchar line[1024];\n\t\twhile(VFS_GETS(bindslist, line, sizeof(line)))\n\t\t{\n\t\t\tchar *cmd, *desc, *tip;\n\t\t\tCmd_TokenizeString(line, false, false);\n\t\t\tcmd = Cmd_Argv(0);\n\t\t\tdesc = Cmd_Argv(1);\n\t\t\ttip = Cmd_Argv(2);\n\t\t\tif (*cmd)\n\t\t\t{\n\t\t\t\tif (strcmp(cmd, \"-\"))\t//lines with a command of \"-\" are spacers/comments.\n\t\t\t\t\tMC_AddBind(menu, (320-(int)vid.width)/2, 170, y, desc, cmd, tip);\n\t\t\t\telse if (*desc)\n\t\t\t\t\tMC_AddRedText(menu, (320-(int)vid.width)/2, 170, y, desc, true);\n\t\t\t\ty += 8;\n\t\t\t}\n\t\t}\n\t\tVFS_CLOSE(bindslist);\n\t\treturn;\n\t}\n\n\tMC_AddFrameStart(menu, 48+8);\n\twhile (bindnames->name)\n\t{\n\t\tMC_AddBind(menu, 16, 170, y, localtext(bindnames->name), bindnames->command, NULL);\n\t\ty += 8;\n\t\tbindnames++;\n\t}\n\tMC_AddFrameEnd(menu, 48+8);\n}\n\nvoid M_UnbindCommand (const char *command)\n{\n\tint\t\tj;\n\tint\t\tl;\n\tchar\t*b;\n\tint m;\n\n\tl = strlen(command);\n\n\tfor (j=0 ; j<K_MAX ; j++)\n\t{\t//FIXME: not sure what to do about bindmaps here. oh well.\n\t\tfor (m = 0; m < KEY_MODIFIERSTATES; m++)\n\t\t{\n\t\t\tb = keybindings[j][m];\n\t\t\tif (!b)\n\t\t\t\tcontinue;\n\t\t\tif (!strncmp (b, command, l) )\n\t\t\t\tKey_SetBinding (j, m, \"\", RESTRICT_LOCAL);\n\t\t}\n\t}\n}\n\n//=============================================================================\n/* HELP MENU */\n\nstatic int\t\thelp_page;\nstatic int\t\tnum_help_pages;\n\nstatic struct\n{\n\tchar *pattern;\n\tint base;\n} helpstyles[] =\n{\n\t{\"gfx/help%i.dxt\",0},\t\t\t//quake extended\n\t{\"gfx/help%i.tga\",0},\t\t\t//quake extended\n\t{\"gfx/help%i.png\",0},\t\t\t//quake extended\n\t{\"gfx/help%i.jpeg\",0},\t\t\t//quake extended\n\t{\"gfx/help%i.lmp\",0},\t\t\t//quake\n\t{\"gfx/menu/help%02i.lmp\",1}\t\t//hexen2\n};\n\nvoid M_Help_Draw (emenu_t *m)\n{\n\tint i;\n\tmpic_t *pic = NULL;\n\tfor (i = 0; i < sizeof(helpstyles)/sizeof(helpstyles[0]) && !pic; i++)\n\t{\n\t\tpic = R2D_SafeCachePic(va(helpstyles[i].pattern, help_page+helpstyles[i].base));\n\t\tif (R_GetShaderSizes(pic, NULL, NULL, true) <= 0)\n\t\t\tpic = NULL;\n\t}\n\tif (!pic)\n\t{\n\t\tm->postdraw = M_RemoveMenu;\n\t\tM_Menu_Main_f ();\n\t}\n\telse\n\t{\n\t\t//define default aspect ratio\n\t\tint width = 320;\n\t\tint height = 200;\n\n\t\t//figure out which axis we're meeting.\n\t\tif (vid.width/(float)width > vid.height/(float)height)\n\t\t{\n\t\t\twidth = width * (vid.height/(float)height);\n\t\t\theight = vid.height;\n\t\t}\n\t\telse\n\t\t{\n\t\t\theight = height * (vid.width/(float)width);\n\t\t\twidth = vid.width;\n\t\t}\n\t\tR2D_ScalePic ((vid.width-width)/2, (vid.height-height)/2, width, height, pic);\n\t}\n}\nqboolean M_Help_Key (struct emenu_s *m, int key, unsigned int unicode)\n{\n\tswitch (key)\n\t{\n\tcase K_ESCAPE:\n\tcase K_GP_DIAMOND_CANCEL:\n\tcase K_GP_START:\n\tcase K_MOUSE2:\n\tcase K_MOUSE4:\n\t\tM_RemoveMenu(m);\n\t\treturn true;\n\n\tcase K_UPARROW:\n\tcase K_RIGHTARROW:\n\tcase K_KP_RIGHTARROW:\n\tcase K_GP_DPAD_RIGHT:\n\tcase K_MOUSE1:\n\tcase K_GP_DIAMOND_CONFIRM:\n\t\tS_LocalSound (\"misc/menu2.wav\");\n\t\tif (++help_page >= num_help_pages)\n\t\t\thelp_page = 0;\n\t\treturn true;\n\n\tcase K_DOWNARROW:\n\tcase K_LEFTARROW:\n\tcase K_KP_LEFTARROW:\n\tcase K_GP_DPAD_LEFT:\n\tcase K_GP_DIAMOND_ALTCONFIRM:\n\t\tS_LocalSound (\"misc/menu2.wav\");\n\t\tif (--help_page < 0)\n\t\t\thelp_page = num_help_pages-1;\n\t\treturn true;\n\tdefault:\n\t\treturn false;\n\t}\n}\n\nvoid M_Menu_Help_f (void)\n{\n\tint i;\n\temenu_t *helpmenu;\n#ifdef CSQC_DAT\n\tif (CSQC_ConsoleCommand(CL_TargettedSplit(false), Cmd_Argv(0)))\n\t\t;//return;\n#endif\n\n\thelpmenu = M_CreateMenu(0);\n\n\thelpmenu->predraw = M_Help_Draw;\n\thelpmenu->key = M_Help_Key;\n\n\thelp_page = 0;\n\n\tnum_help_pages = 1;\n\twhile(num_help_pages < 100)\n\t{\n\t\tfor (i = 0; i < sizeof(helpstyles)/sizeof(helpstyles[0]); i++)\n\t\t{\n\t\t\tif (COM_FCheckExists(va(helpstyles[i].pattern, num_help_pages+helpstyles[i].base)))\n\t\t\t\tbreak;\n\t\t}\n\t\tif (i == sizeof(helpstyles)/sizeof(helpstyles[0]))\n\t\t\tbreak;\n\t\tnum_help_pages++;\n\t}\n}\n\n//=============================================================================\n/* QUIT MENU */\n\nstatic char *quitMessage [] =\n{\n/* .........1.........2.... */\n\t\"Are you gonna quit\\n\"\n\t\"this game just like\\n\"\n\t\"everything else?\",\n\n\t\"Milord, methinks that\\n\"\n\t\"thou art a lowly\\n\"\n\t\"quitter. Is this true?\",\n\n\t\"Do I need to bust your\\n\"\n\t\"face open for trying\\n\"\n\t\"to quit?\",\n\n\t\"Man, I oughta smack you\\n\"\n\t\"for trying to quit!\\n\"\n\t\"Press Y to get\\n\"\n\t\"smacked out.\",\n\n\t\"Press Y to quit like a\\n\"\n\t\"big loser in life.\\n\"\n\t\"Press N to stay proud\\n\"\n\t\"and successful!\",\n\n\t//this is a vanilla message... but I'm having enough issues with false-positives in malware scanners right now that I really don't want to risk an already-suspicious actual hunam seeing stuff like this... :)\n/*\t\"If you press Y to\\n\"\n\t\"quit, I will summon\\n\"\n\t\"Satan all over your\\n\"\n\t\"hard drive!\",\n*/\n\t\"Um, Asmodeus dislikes\\n\"\n\t\"his children trying to\\n\"\n\t\"quit. Press Y to return\\n\"\n\t\"to your Tinkertoys.\",\n\n\t\"If you quit now, I'll\\n\"\n\t\"throw a blanket-party\\n\"\n\t\"for you next time!\",\n\n\n\n\n//\t\"Only cowards press Y here.\\n\",\n\n//\t\"Is your life empty and\\n\"\n//\t\"devoid of enjoyment?\\n\",\n\n//\t\"Are you off to play with\\n\"\n//\t\"your joy stick now?\\n\",\n};\n\n\n\n/*\tchar *cmsg[] = {\n//   0123456789012345678901234567890123456789\n\t\"0            QuakeWorld\",\n\t\"1          version \" VSTR2(VERSION),\n\t\"1modified by Forethought Entertainment\",\n\t\"0Based on QuakeWorld Version 2.40\",\n\t\"1\",\n\t\"0Additional Programming\",\n\t\"1 David Walton\",\n\t\"1\",\n\t\"0Id Software is not responsible for\",\n    \"0providing technical support for\",\n\t\"0QUAKEWORLD(tm). (c)1996 Id Software,\",\n\t\"0Inc.  All Rights Reserved.\",\n\t\"0QUAKEWORLD(tm) is a trademark of Id\",\n\t\"0Software, Inc.\",\n\t\"1NOTICE: THE COPYRIGHT AND TRADEMARK\",\n\t\"1NOTICES APPEARING  IN YOUR COPY OF\",\n\t\"1QUAKE(r) ARE NOT MODIFIED BY THE USE\",\n\t\"1OF QUAKEWORLD(tm) AND REMAIN IN FULL\",\n\t\"1FORCE.\",\n\t\"0NIN(r) is a registered trademark\",\n\t\"0licensed to Nothing Interactive, Inc.\",\n\t\"0All rights reserved. Press y to exit\",\n\tNULL };*/\n\nvoid Cmd_WriteConfig_f(void);\nstatic void M_Menu_DoQuit(void *ctx, promptbutton_t option)\n{\n\tif (option == PROMPT_YES)\t//'yes - quit'\n\t\tCmd_ExecuteString(\"menu_quit force\\n\", RESTRICT_LOCAL);\n//\telse if (option == PROMPT_NO)\t//'no - don't quit'\n//\telse if (option == PROMPT_CANCEL)\t//'cancel - don't quit'\n}\nstatic void M_Menu_DoQuitSave(void *ctx, promptbutton_t option)\n{\n\tif (option == PROMPT_YES)\t//'yes - save-and-quit'\n\t\tCmd_ExecuteString(\"menu_quit forcesave\\n\", RESTRICT_LOCAL);\n\telse if (option == PROMPT_NO)\t//'no - nosave-and-quit'\n\t\tCmd_ExecuteString(\"menu_quit force\\n\", RESTRICT_LOCAL);\n//\telse if (option == PROMPT_CANCEL)\t//'cancel - don't quit'\n}\n\n//quit menu\nvoid M_Menu_Quit_f (void)\n{\n\tint mode;\n\textern cvar_t cfg_save_auto;\n\tchar *arg = Cmd_Argv(1);\n\n#ifdef CL_MASTER\n\tMasterInfo_WriteServers();\n#endif\n\n\tif (!strcmp(arg, \"force\"))\n\t\tmode = 0;\n\telse if (!strcmp(arg, \"forcesave\") || cfg_save_auto.ival)\n\t{\n\t\tCmd_ExecuteString(\"cfg_save\", RESTRICT_LOCAL);\n\t\tif (!strcmp(arg, \"prompt\"))\n\t\t\tmode = 1;\n\t\telse\n\t\t\tmode = 0;\n\t}\n\telse if (!strcmp(arg, \"save\"))\n\t\tmode = 2;\n\telse\n\t{\t//prompt to save, but not otherwise.\n\t\tif (Cvar_UnsavedArchive())\n\t\t\tmode = 2;\n\t\telse\n\t\t{\n\t\t\tif (!strcmp(arg, \"prompt\"))\n\t\t\t\tmode = 1;\n\t\t\telse if (!strcmp(arg, \"noprompt\"))\n\t\t\t\tmode = 0;\n\t\t\telse\n\t\t\t\tmode = 1;\n\t\t}\n\t}\n\n\tswitch(mode)\n\t{\n\tcase 0:\n\t\tCL_Disconnect (NULL);\n\t\tSys_Quit ();\n\t\tbreak;\n\tcase 2:\n\t\tMenu_Prompt (M_Menu_DoQuitSave, NULL, localtext(\"You have unsaved settings\\nWould you like to\\nsave them now?\"), \"Yes\", \"No\", \"Cancel\", true);\n\t\tbreak;\n\tcase 1:\n\t\tMenu_Prompt (M_Menu_DoQuit, NULL, localtext(quitMessage[rand()%countof(quitMessage)]), \"Quit\", NULL, \"Cancel\", true);\n\t\tbreak;\n\t}\n}\n\n#ifdef HAVE_LEGACY\nvoid M_Menu_Credits_f (void)\n{\n\tMenu_Prompt (NULL, NULL, localtext(\"That's all folks!\\nTry a different mod now.\"), NULL, NULL, \"Sure!\", false);\n}\n#endif\n\n//=============================================================================\n/* Menu Subsystem */\n\nvoid M_Menu_ServerList2_f(void);\nvoid M_QuickConnect_f(void);\n\nvoid M_Menu_MediaFiles_f (void);\nvoid M_Menu_FPS_f (void);\nvoid M_Menu_Lighting_f (void);\nvoid M_Menu_Render_f (void);\nvoid M_Menu_Textures_f (void);\nvoid M_Menu_Teamplay_f (void);\nvoid M_Menu_Teamplay_Locations_f (void);\nvoid M_Menu_Teamplay_Needs_f (void);\nvoid M_Menu_Teamplay_Items_f (void);\nvoid M_Menu_Teamplay_Items_Armor_f (void);\nvoid M_Menu_Teamplay_Items_Weapons_f (void);\nvoid M_Menu_Teamplay_Items_Powerups_f (void);\nvoid M_Menu_Teamplay_Items_Ammo_Health_f (void);\nvoid M_Menu_Teamplay_Items_Team_Fortress_f (void);\nvoid M_Menu_Teamplay_Items_Status_Location_Misc_f (void);\nvoid M_Menu_Network_f(void);\nvoid M_Menu_Singleplayer_Cheats_f (void);\nvoid M_Menu_Particles_f (void);\nvoid M_Menu_Audio_Speakers_f (void);\nvoid Menu_DownloadStuff_f (void);\nstatic qboolean internalmenusregistered;\nvoid M_Init_Internal (void)\n{\n#ifdef MENU_DAT\n\tMP_Shutdown();\n#endif\n\n\tif (internalmenusregistered)\n\t\treturn;\n\tinternalmenusregistered = true;\n\n#if !defined(CLIENTONLY) && defined(SAVEDGAMES)\n\tCmd_AddCommand (\"menu_save\", M_Menu_Save_f);\n\tCmd_AddCommand (\"menu_load\", M_Menu_Load_f);\n\tCmd_AddCommand (\"menu_loadgame\", M_Menu_Load_f);\t//q2...\n#endif\n\tCmd_AddCommand (\"menu_single\", M_Menu_SinglePlayer_f);\n\tCmd_AddCommand (\"menu_multi\", M_Menu_MultiPlayer_f);\n\n\tCmd_AddCommand (\"menu_keys\", M_Menu_Keys_f);\n\tCmd_AddCommand (\"help\", M_Menu_Help_f);\n\tCmd_AddCommand (\"menu_quit\", M_Menu_Quit_f);\n\tCmd_AddCommand (\"menu_mods\", M_Menu_Mods_f);\n\n#ifdef CL_MASTER\n\tCmd_AddCommand (\"menu_slist\", M_Menu_ServerList2_f);\n#endif\n\tCmd_AddCommand (\"menu_setup\", M_Menu_Setup_f);\n\tCmd_AddCommand (\"menu_newmulti\", M_Menu_GameOptions_f);\n\n\tCmd_AddCommand (\"menu_main\", M_Menu_Main_f);\t//I've moved main to last because that way tab give us main and not quit.\n\n\tCmd_AddCommand (\"menu_options\", M_Menu_Options_f);\n\tCmd_AddCommand (\"menu_video\", M_Menu_Video_f);\n\tCmd_AddCommand (\"menu_audio\", M_Menu_Audio_f);\n#ifndef __CYGWIN__\n\tCmd_AddCommand (\"menu_speakers\", M_Menu_Audio_Speakers_f);\n#endif\n\tCmd_AddCommand (\"menu_spcheats\", M_Menu_Singleplayer_Cheats_f);\n\tCmd_AddCommand (\"menu_fps\", M_Menu_FPS_f);\n\tCmd_AddCommand (\"menu_render\" , M_Menu_Render_f);\n\tCmd_AddCommand (\"menu_lighting\", M_Menu_Lighting_f);\n\tCmd_AddCommand (\"menu_textures\", M_Menu_Textures_f);\n\tCmd_AddCommand (\"menu_teamplay\", M_Menu_Teamplay_f);\n\tCmd_AddCommand (\"menu_teamplay_locations\", M_Menu_Teamplay_Locations_f);\n\tCmd_AddCommand (\"menu_teamplay_needs\", M_Menu_Teamplay_Needs_f);\n\tCmd_AddCommand (\"menu_teamplay_items\", M_Menu_Teamplay_Items_f);\n\tCmd_AddCommand (\"menu_teamplay_armor\", M_Menu_Teamplay_Items_Armor_f);\n\tCmd_AddCommand (\"menu_teamplay_weapons\", M_Menu_Teamplay_Items_Weapons_f);\n\tCmd_AddCommand (\"menu_teamplay_powerups\", M_Menu_Teamplay_Items_Powerups_f);\n\tCmd_AddCommand (\"menu_teamplay_ammo_health\", M_Menu_Teamplay_Items_Ammo_Health_f);\n\tCmd_AddCommand (\"menu_teamplay_team_fortress\", M_Menu_Teamplay_Items_Team_Fortress_f);\n\tCmd_AddCommand (\"menu_teamplay_status_location_misc\", M_Menu_Teamplay_Items_Status_Location_Misc_f);\n\tCmd_AddCommand (\"menu_particles\", M_Menu_Particles_f);\n\tCmd_AddCommand (\"menu_network\", M_Menu_Network_f);\n\n#ifdef HAVE_LEGACY\n\tCmd_AddCommand (\"menu_credits\", M_Menu_Credits_f);\n#endif\n\n#if defined(CL_MASTER) && defined(HAVE_PACKET)\n\tCmd_AddCommand (\"quickconnect\", M_QuickConnect_f);\n#endif\n}\n\nvoid M_DeInit_Internal (void)\n{\n\tM_RemoveAllMenus(true);\n\n\tif (!internalmenusregistered)\n\t\treturn;\n\tinternalmenusregistered = false;\n\n#ifndef CLIENTONLY\n\tCmd_RemoveCommand (\"menu_save\");\n\tCmd_RemoveCommand (\"menu_load\");\n\tCmd_RemoveCommand (\"menu_loadgame\");\t//q2...\n#endif\n\tCmd_RemoveCommand (\"menu_single\");\n\tCmd_RemoveCommand (\"menu_multi\");\n\n\tCmd_RemoveCommand (\"menu_keys\");\n\tCmd_RemoveCommand (\"help\");\n\tCmd_RemoveCommand (\"menu_quit\");\n\tCmd_RemoveCommand (\"menu_mods\");\n\n#ifdef CL_MASTER\n\tCmd_RemoveCommand (\"menu_slist\");\n#endif\n\tCmd_RemoveCommand (\"menu_setup\");\n\tCmd_RemoveCommand (\"menu_newmulti\");\n\n\tCmd_RemoveCommand (\"menu_options\");\n\tCmd_RemoveCommand (\"menu_video\");\n\tCmd_RemoveCommand (\"menu_audio\");\n\tCmd_RemoveCommand (\"menu_speakers\");\n\tCmd_RemoveCommand (\"menu_teamplay\");\n\tCmd_RemoveCommand (\"menu_teamplay_locations\");\n\tCmd_RemoveCommand (\"menu_teamplay_needs\");\n\tCmd_RemoveCommand (\"menu_teamplay_items\");\n\tCmd_RemoveCommand (\"menu_teamplay_armor\");\n\tCmd_RemoveCommand (\"menu_teamplay_weapons\");\n\tCmd_RemoveCommand (\"menu_teamplay_powerups\");\n\tCmd_RemoveCommand (\"menu_teamplay_ammo_health\");\n\tCmd_RemoveCommand (\"menu_teamplay_team_fortress\");\n\tCmd_RemoveCommand (\"menu_teamplay_status_location_misc\");\n\tCmd_RemoveCommand (\"menu_spcheats\");\n\tCmd_RemoveCommand (\"menu_fps\");\n\tCmd_RemoveCommand (\"menu_render\");\n\tCmd_RemoveCommand (\"menu_lighting\");\n\tCmd_RemoveCommand (\"menu_textures\");\n\tCmd_RemoveCommand (\"menu_particles\");\n\tCmd_RemoveCommand (\"menu_network\");\n\n#ifdef HAVE_LEGACY\n\tCmd_RemoveCommand (\"menu_credits\");\n#endif\n\n\tCmd_RemoveCommand (\"menu_main\");\t//I've moved main to last because that way tab gives us main and not quit.\n\tCmd_RemoveCommand (\"quickconnect\");\n}\n\nvoid M_Shutdown(qboolean total)\n{\n#ifdef MENU_NATIVECODE\n\tMN_Shutdown();\n#endif\n#ifdef MENU_DAT\n\tMP_Shutdown();\n#endif\n\tM_RemoveAllMenus(!total);\n\tM_DeInit_Internal();\n}\n\nvoid M_Reinit(void)\n{\n#ifdef MENU_NATIVECODE\n\tif (!MN_Init())\n#endif\n#ifdef MENU_DAT\n\tif (!MP_Init())\n#endif\n\t{\n\t\tM_Init_Internal();\n\n\t\t(void)CSQC_UnconnectedInit();\n\t}\n}\n\nvoid FPS_Preset_f(void);\nvoid FPS_Preset_c(int argn, const char *partial, struct xcommandargcompletioncb_s *ctx);\nvoid M_MenuPop_f(void);\n\n//menu.dat is loaded later... after the video and everything is up.\nvoid M_Init (void)\n{\n\tCmd_AddCommand(\"menu_restart\", M_Restart_f);\n\tCmd_AddCommand(\"togglemenu\", M_ToggleMenu_f);\n\tCmd_AddCommand(\"closemenu\", M_CloseMenu_f);\n\tCmd_AddCommandAD(\"fps_preset\", FPS_Preset_f, FPS_Preset_c, \"Apply a preset\");\n\tCmd_AddCommand(\"menupop\", M_MenuPop_f);\n\n\t//server browser is kinda complex, and has clipboard integration which we need to sandbox a little\n#ifdef CL_MASTER\n\tCmd_AddCommand (\"menu_servers\", M_Menu_ServerList2_f);\n#endif\n\t//downloads menu needs sandboxing, so cannot be provided by qc.\n#ifdef PACKAGEMANAGER\n\tCmd_AddCommand (\"menu_download\", Menu_DownloadStuff_f);\n#endif\n#ifndef MINIMAL\n\tCmd_AddCommandAD (\"modelviewer\", M_Menu_ModelViewer_f, M_Menu_ModelViewer_c, \"View a model...\");\n#endif\n\t//demo menu is allowed to see outside of the quakedir. you can't replicate that in qc's sandbox.\n\tCmd_AddCommand (\"menu_demo\", M_Menu_Demos_f);\n#ifdef HAVE_JUKEBOX\n\tCmd_AddCommand (\"menu_mediafiles\", M_Menu_MediaFiles_f);\n#endif\n\n\n\n\tCvar_Register(&m_preset_chosen, \"Menu thingumiebobs\");\n\tCvar_Register(&m_helpismedia, \"Menu thingumiebobs\");\n\n\tMedia_Init();\n#ifdef CL_MASTER\n\tM_Serverlist_Init();\n#endif\n\tM_Script_Init();\n\n\tM_Reinit();\n}\n//end builtin-menu code.\n#else\nvoid M_Init_Internal (void){}\nvoid M_DeInit_Internal (void){}\nvoid M_Shutdown(qboolean total)\n{\n#ifdef MENU_NATIVECODE\n\tMN_Shutdown();\n#endif\n#ifdef MENU_DAT\n\tMP_Shutdown();\n#endif\n}\nvoid M_Reinit(void)\n{\n#ifdef MENU_NATIVECODE\n\tif (!MN_Init())\n#endif\n#ifdef MENU_DAT\n\tif (!MP_Init())\n#endif\n\t{\n\t\t(void)CSQC_UnconnectedInit();\n\t}\n}\nvoid M_Init (void)\n{\n\tCmd_AddCommand(\"menu_restart\", M_Restart_f);\n\tCmd_AddCommand(\"togglemenu\", M_ToggleMenu_f);\n\n\tMedia_Init();\n\tM_Reinit();\n}\n#endif\n\nvoid M_Window_ClosePrompt(void)\n{\t//someone clicked our window's 'close' button or system menu or alt+f4 or etc. we blocked it for now, but don't just ignore it...\n\tCOM_AssertMainThread(\"M_Window_ClosePrompt\");\n\tKey_Dest_Remove(kdm_console);\n\tif (Cmd_Exists(\"menu_quit\") || Cmd_AliasExist(\"menu_quit\", RESTRICT_LOCAL))\n\t\tCmd_ExecuteString(\"menu_quit prompt\", RESTRICT_LOCAL);\t//our builtin menus use this form\n\telse if (Cmd_Exists(\"m_quit\") || Cmd_AliasExist(\"m_quit\", RESTRICT_LOCAL))\n\t\tCmd_ExecuteString(\"m_quit\", RESTRICT_LOCAL);\t//some menuqc mods use a different name for the command to avoid conflicts.\n\telse\n\t\tCmd_ExecuteString(\"quit\", RESTRICT_LOCAL);\t//fall back to the engine's version\n}\n\n\n// Generic function to choose which game menu to draw\nint M_GameType (void)\n{\n\tstatic int cached;\n\tstatic unsigned int cachedrestarts;\n\n\tif (FS_Restarted(&cachedrestarts))\n\t{\n\t\tstruct\n\t\t{\n\t\t\tint gametype;\n\t\t\tchar *path;\n\t\t} configs[] =\n\t\t{\n\t\t\t{MGT_QUAKE1, \"gfx/sp_menu.lmp\"},\n#ifdef Q2CLIENT\n\t\t\t{MGT_QUAKE2, \"pics/m_banner_game.pcx\"},\n#endif\n#ifdef HEXEN2\n\t\t\t{MGT_HEXEN2, \"gfx/menu/title2.lmp\"},\n#endif\n\t\t\t{0, NULL}\n\t\t};\n\t\tint bd = COM_FDepthFile(configs[0].path, true);\n\t\tint i;\n\t\tcached = configs[0].gametype;\n\t\tfor (i = 1; configs[i].path; i++)\n\t\t{\n\t\t\tint gd = COM_FDepthFile(configs[i].path, true);\n\t\t\tif (bd > gd)\n\t\t\t{\n\t\t\t\tbd = gd;\n\t\t\t\tcached = configs[i].gametype;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn cached;\n}\n\n\n"
  },
  {
    "path": "engine/client/menu.h",
    "content": "/*\nCopyright (C) 1996-1997 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n*/\n\n//There are still menu states, which say which menu gets absolute control.\n//Some of the destinations are healthier than others. :)\n\n//m_none\t- menu is disabled\n//m_complex\t- hirachy of item types\n//m_help\t- old q1 style help menu (fixme: make m_complex)\n//m_plugin\t- A QVM based or DLL based plugin.\n\n\n//the m_complex menu state is the most advanced, and drives the bulk of FTE's menus in an event driven way.\n//It consists of menus and items. Fairly basic really.\n//Each item type has a structure (or shares a structure).\n//Each of these structures contain a menucommon_t.\n//The backend of this system lives in m_items.c.\n//If you're creating your own quake menu, there should be little need to go in there.\n//These are the item types:\n\n//mt_childwindow\t-\n//mt_button\t\t\t- Executes a console command or callback on enter. Uses conchars.\n//mt_buttonbigfont\t- Used by hexen2's menus. Uses gfx/menu/bigfont.lmp as it's characters.\n//mt_box\t\t\t- A 2d box. The same one as the quit dialog from q1, but resized.\n//mt_colouredbox\t- Not used.\n//mt_line\t\t\t- Not used.\n//mt_edit\t\t\t- A one row edit box, either attached to a cvar, or an apply button.\n//mt_text\t\t\t- unselectable. Otherwise like mt_button\n//mt_slider\t\t\t- a horizontal slider, like quake's gamma option, attached to a cvar.\n//mt_combo\t\t\t- multiple specific options. Created with specifically structured info.\n//mt_bind\t\t\t- a key binding option.\n//mt_checkbox\t\t- a yes/no toggle, attached to a cvar.\n//mt_picture\t\t- Just draws a lmp from it's x/y.\n//mt_menudot\t\t- The 24*24 rotating quake menudot. Should be specified as the cursoritem, and should be placed to match the selecteditem's y position.\n//mt_custom\t\t\t- Just an option with callbacks. This is the basis of the top/bottom color seletion, and could be used for all sorts of things.\n\n\n//Sample menu creation, entirly within / * and * /\n//Note that most of FTE's menus are more complicated, as FTE runs on Q1/Q2/H2 data, and it's choice of menu images reflects this.\n//Most of the normal menus also have more items too, of course.\n\n//FTE's menu console commands are registered from M_Init_Internal instead of M_Init as implied here. Why?\n//FTE's menu.dat support unregisters the menu console commands so the menu.dat can use those commands instead.\n//This results in more user friendliness for mods but makes the code a little more confusing.\n//If you make the menu name unique enough, then there's no need to follow the standard menu code.\n/*\n//M_SomeMenuConsoleCommand_f\n//Spawns a sample menu.\nvoid M_SomeMenuConsoleCommand_f (void)\n{\n\tmenu_t *m = M_CreateMenu(0);\n\tint y = 32;\n\n\t//add the title\n\tMC_AddCenterPicture(m, 4, 24, \"gfx/p_option.lmp\");\n\n\t//add the blinking > thingie\n\t//(note NULL instead of a valid string, this should really be a variant of mt_menudot instead)\n\tmenu->cursoritem = (menuoption_t*)MC_AddWhiteText(menu, 200, y, NULL, false);\n\n\t//Set up so the first item is selected. :)\n\tm->selecteditem = (menuoption_t*)\n\n\t//Add the items.\n\tMC_AddConsoleCommand(menu, 16, y,\t\"    Customize controls\", \"menu_keys\\n\"); y+=8;\n\tMC_AddSlider(menu, 16, y,\t\t\t\"           Mouse Speed\", &sensitivity,\t\t1,\t\t10); y+=8;\n\tMC_AddCheckBox(menu, 16, y,\t\t\t\"            Lookstrafe\", &lookstrafe,0); y+=8;\n}\n\n//eg: M_Init\nvoid M_SomeInitialisationFunctionCalledAtStartup(void)\n{\n\tCmd_AddCommand(\"menu_somemenu\", M_SomeMenuConsoleCommand_f);\n}\n*/\n\nvoid M_DrawTextBox (int x, int y, int width, int lines);\n\n//menus can have all sorts of implementation.\n//however, to avoid a cacophony of ugly blends, these are always mutually exclusive - only one type of menu will be at the top and only that one will be drawn.\n//if you want windows etc you can implement that inside a single one of these.\n//each menu subsystem can implement this and then provide its own widgets.\ntypedef struct menu_s {\n\tstruct menu_s *prev;\n\n\tvoid *ctx;\t//for finding a specific menu\n\tvoid (*videoreset)\t(struct menu_s *);\t//called after a video mode switch / shader reload.\n\tvoid (*release)\t\t(struct menu_s *, qboolean forced);\t//forced says 'dont load any other menus'.\n\tqboolean (*keyevent)(struct menu_s *, qboolean isdown, unsigned int devid, int key, int unicode);\t//true if key was handled\n\tqboolean (*mousemove)(struct menu_s *, qboolean abs, unsigned int devid, float x, float y);\n\tqboolean (*joyaxis)\t(struct menu_s *, unsigned int devid, int axis, float val);\n\tvoid (*drawmenu)\t(struct menu_s *);\n\tstruct key_cursor_s *cursor; //NULL for relative motion\n\tqboolean lowpriority;\t//appears underneath other menus.\n\tqboolean isopaque;\t//guarentees an opaque background\n\tqboolean persist;\t//try really hard to not kill this.\n\tqboolean showosk;\n} menu_t;\nextern menu_t *topmenu;\t\t//the currently visible menu.\nextern menu_t *promptmenu;\t//the currently visible prompt (separate from menus, so they always appear over the top of consoles too, they also always show the menu underneath)\nvoid Menu_KeyEvent(qboolean down, int qdeviceid, int key, int unicode);\nint Menu_WantOSK(void);\nvoid Menu_Draw(void);\nvoid Prompts_Draw(void);\nvoid Menu_PopAll(void); //attempts to pop all menus (this is for map starts, some might linger)\nvoid Menu_Unlink(menu_t *menu, qboolean forced);\nvoid Menu_Push(menu_t *menu, qboolean prompt);\nmenu_t *Menu_FindContext(void *ctx);\nqboolean Menu_IsLinked(menu_t *menu);\n\ntypedef enum\n{\n\tPROMPT_YES\t\t= 0,\n\tPROMPT_NO\t\t= 1,\n\tPROMPT_CANCEL\t= -1,\n} promptbutton_t;\n#ifdef HAVE_CLIENT\nvoid Menu_Prompt (void (*callback)(void *, promptbutton_t), void *ctx, const char *messages, const char *optionyes, const char *optionno, const char *optioncancel, qboolean highpri);\n#define Menu_PromptOrPrint(messages,optioncancel,highpri) Menu_Prompt(NULL, NULL, messages, NULL, NULL, optioncancel, highpri)\n#else\n#define Menu_PromptOrPrint(messages,optioncancel,highpri) Con_Printf(\"%s\", messages)\n#endif\n\nvoid M_Window_ClosePrompt (void);\t//called when the window was requested to be closed. displays whatever quit menu is appropriate.\n#ifndef NOBUILTINMENUS\n\n//\n// menus\n//\nvoid M_Init (void);\nvoid M_Reinit(void);\nvoid M_Shutdown(qboolean total);\nvoid M_Menu_Mods_f (void);\t//used at startup if the current gamedirs look dodgy.\nvoid M_Menu_Installer (void);\t//given an embedded manifest, this displays an install menu for said game.\nmpic_t\t*M_CachePic (char *path);\nvoid M_Menu_Quit_f (void);\ntypedef struct emenu_s emenu_t;\n\n\ntypedef enum {\n\tmt_framestart,\n\tmt_frameend,\n\tmt_button,\n\tmt_qbuttonbigfont,\n\tmt_hexen2buttonbigfont,\n\tmt_box,\n\tmt_colouredbox,\n\tmt_line,\n\tmt_edit,\n\tmt_text,\n\tmt_slider,\n\tmt_combo,\n\tmt_bind,\n\tmt_checkbox,\n\tmt_picture,\n\tmt_picturesel,\n\tmt_menudot,\n\tmt_menucursor,\n\tmt_custom\n} menutype_t;\n\ntypedef struct {\t//must be first of each structure type.\n\tmenutype_t type;\n\tint posx;\n\tint posy;\n\tint width;\t\t//total width\n\tint height;\t\t//total height\n\tint extracollide; // dirty hack to stretch collide box left (the real fix is to have separate collide/render rects)\n\t//unsigned int grav_type;\n\t//int grav_x;\t//\n\tint\tgrav_y;\t\t//\n\t//int grav_size[2];\n\tconst char *tooltip;\n\tqboolean noselectionsound:1;\n\tqboolean iszone:1;\n\tqboolean ishidden:1;\n\tunion menuoption_s *next;\n} menucommon_t;\n\n\ntypedef struct {\n\tmenucommon_t common;\n\tconst char *text;\n\tconst char *command;\n\tqboolean rightalign;\n\tqboolean (*key) (union menuoption_s *option, struct emenu_s *, int key);\n} menubutton_t;\n\n#define MAX_EDIT_LENGTH 256\ntypedef struct {\n\tmenucommon_t common;\n\tint captionwidth;\n\tconst char *caption;\n\tcvar_t *cvar;\n\tchar text[MAX_EDIT_LENGTH];\n\tint cursorpos;\n\tqboolean modified;\n\tqboolean slim;\n} menuedit_t;\ntypedef struct {\n\tmenucommon_t common;\n\tfloat min;\n\tfloat max;\n\tfloat current;\n\tfloat smallchange;\n\tfloat largechange;\n\tfloat vx;\n\tcvar_t *var;\n\tint textwidth;\n\tconst char *text;\n} menuslider_t;\n\ntypedef enum {CHK_CHECKED, CHK_TOGGLE} chk_set_t;\ntypedef struct menucheck_s {\n\tmenucommon_t common;\n\tconst char *text;\n\tint textwidth;\n\tcvar_t *var;\n\tint bits;\n\tfloat value;\n\tqboolean (*func) (struct menucheck_s *option, struct emenu_s *menu, chk_set_t set);\n} menucheck_t;\n\ntypedef struct {\n\tmenucommon_t common;\n\tconst char *text;\n\tint rightalign;\n\tqboolean isred;\n} menutext_t;\n\ntypedef struct menucustom_s {\n\tmenucommon_t common;\n\tvoid *dptr;\n\tvoid *dptr2;\n\tint dint;\n\tvoid (*draw) (int x, int y, struct menucustom_s *, struct emenu_s *);\n\tqboolean (*key) (struct menucustom_s *, struct emenu_s *, int key, unsigned int unicode);\n} menucustom_t;\n\ntypedef struct {\n\tmenucommon_t common;\n\tchar *picturename;\n} menupicture_t;\n\ntypedef struct {\n\tmenucommon_t common;\n\tint width;\n\tint height;\n} menubox_t;\n\ntypedef struct {\n\tmenucommon_t common;\n\n\tint captionwidth;\n\tchar const*caption;\n\tchar const*const*options;\n\tchar const*const*values;\n\tcvar_t *cvar;\n\tint numoptions;\n\tint selectedoption;\n} menucombo_t;\n\ntypedef struct {\n\tmenucommon_t common;\n\tint captionwidth;\n\tchar *caption;\n\tchar *command;\n} menubind_t;\n\ntypedef struct {\n\tmenucommon_t common;\n\tqboolean mousedown;\n\tfloat frac;\n\tunion menuoption_s *suboptions;\n} menuframe_t;\n\ntypedef union menuoption_s {\n\tmenucommon_t\tcommon;\n\tmenubutton_t\tbutton;\n\tmenuedit_t\t\tedit;\n\tmenucombo_t\t\tcombo;\n\tmenuslider_t\tslider;\n\tmenutext_t\t\ttext;\n\tmenucustom_t\tcustom;\n\tmenupicture_t\tpicture;\n\tmenubox_t\t\tbox;\n\tmenucheck_t\t\tcheck;\n\tmenubind_t\t\tbind;\n\tmenuframe_t\t\tframe;\n} menuoption_t;\n\ntypedef struct menutooltip_s {\n\tconchar_t *end;\n\tconchar_t text[1];\n} menutooltip_t;\n\ntypedef struct menuresel_s\t//THIS STRUCT MUST BE STATICALLY ALLOCATED.\n{\n\tint x, y;\n} menuresel_t;\n\nstruct emenu_s {\n\tmenu_t menu;\n\n\tint xpos;\n\tint ypos;\n\tint width;\n\tint height;\n\tqboolean dontexpand;\t\t//false=recenter xpos\n\tint numoptions;\n\tmenuresel_t *reselection;\t//stores some info to restore selection properly.\n\n\tqboolean\tiszone;\n\tqboolean\tnobacktint;\n\n\tvoid *data;\t//typecast\n\n\tvoid (*reset)\t\t(struct emenu_s *);\t//called after a video mode switch / shader reload.\n\tvoid (*remove)\t\t(struct emenu_s *);\n\tqboolean (*key)\t\t(struct emenu_s *, int key, unsigned int unicode);\t//true if key was handled\n\tvoid (*predraw)\t\t(struct emenu_s *);\n\tvoid (*postdraw)\t(struct emenu_s *);\n\tmenuoption_t *options;\n\n\tmenuoption_t *selecteditem;\n\tmenuoption_t *mouseitem;\n\n\tmenutooltip_t *tooltip;\n\tdouble tooltiptime;\n\n\tint cursorpos;\n\tmenuoption_t *cursoritem;\n};\n\nmenutext_t *MC_AddBufferedText(emenu_t *menu, int lhs, int rhs, int y, const char *text, int rightalign, qboolean red);\nmenutext_t *MC_AddRedText(emenu_t *menu, int lhs, int rhs, int y, const char *text, int rightalign);\nmenutext_t *MC_AddWhiteText(emenu_t *menu, int lhs, int rhs, int y, const char *text, int rightalign);\nmenubind_t *MC_AddBind(emenu_t *menu, int cx, int bx, int y, const char *caption, char *command, const char *tooltip);\nmenubox_t *MC_AddBox(emenu_t *menu, int x, int y, int width, int height);\nmenupicture_t *MC_AddPicture(emenu_t *menu, int x, int y, int width, int height, const char *picname);\nmenupicture_t *MC_AddSelectablePicture(emenu_t *menu, int x, int y, int height, const char *picname);\nmenupicture_t *MC_AddCenterPicture(emenu_t *menu, int y, int height, const char *picname);\nmenupicture_t *MC_AddCursor(emenu_t *menu, menuresel_t *resel, int x, int y);\nmenuoption_t *MC_AddCursorSmall(emenu_t *menu, menuresel_t *reselection, int x);\nmenuslider_t *MC_AddSlider(emenu_t *menu, int tx, int sx, int y, const char *text, cvar_t *var, float min, float max, float delta);\nmenucheck_t *MC_AddCheckBox(emenu_t *menu, int tx, int cx, int y, const char *text, cvar_t *var, int cvarbitmask);\nmenucheck_t *MC_AddCheckBoxFunc(emenu_t *menu, int tx, int cx, int y, const char *text, qboolean (*func) (menucheck_t *option, emenu_t *menu, chk_set_t set), int bits);\nmenubutton_t *MC_AddConsoleCommand(emenu_t *menu, int lhs, int rhs, int y, const char *text, const char *command);\nmenubutton_t *MC_AddConsoleCommandQBigFont(emenu_t *menu, int x, int y, const char *text, const char *command);\nvoid *QBigFontWorks(void);\t//treat as a boolean.\nmenubutton_t *MC_AddConsoleCommandHexen2BigFont(emenu_t *menu, int x, int y, const char *text, const char *command);\nmenubutton_t *VARGS MC_AddConsoleCommandf(emenu_t *menu, int lhs, int rhs, int y, int rightalign, const char *text, char *command, ...);\nmenubutton_t *MC_AddCommand(emenu_t *menu, int lhs, int rhs, int y, const char *text, qboolean (*command) (union menuoption_s *,struct emenu_s *,int));\nmenucombo_t *MC_AddCombo(emenu_t *menu, int tx, int cx, int y, const char *caption, const char **ops, int initialvalue);\nmenucombo_t *MC_AddCvarCombo(emenu_t *menu, int tx, int cx, int y, const char *caption, cvar_t *cvar, const char **ops, const char **values);\nmenuedit_t *MC_AddEdit(emenu_t *menu, int cx, int ex, int y, const char *text, const char *def);\nmenuedit_t *MC_AddEditCvar(emenu_t *menu, int cx, int ex, int y, const char *text, const char *name, qboolean slim);\nmenucustom_t *MC_AddCustom(emenu_t *menu, int x, int y, void *dptr, int dint, const char *tooltip);\nmenuframe_t *MC_AddFrameStart(emenu_t *menu, int y);\t//call before items are added\nmenuframe_t *MC_AddFrameEnd(emenu_t *menu, int y);\t//and call AFTER that stuff with the same y.\n\ntypedef struct menubulk_s {\n\tmenutype_t type;\n\tint variant;\n\tconst char *text;\n\tconst char *tooltip;\n\tchar *consolecmd; // console command\n\tcvar_t *cvar; // check box, slider\n\tint flags; // check box\n\tqboolean (*func) (struct menucheck_s *option, struct emenu_s *menu, chk_set_t set); // check box\n\tfloat min; // slider\n\tfloat max; // slider\n\tfloat delta; // slider\n\tqboolean rightalign; // text\n\tqboolean (*command) (union menuoption_s *, struct emenu_s *, int); // command\n\tchar *cvarname; // edit cvar\n\tconst char **options; // combo\n\tconst char **values; // cvar combo\n\tint selectedoption; // other combo\n\tunion menuoption_s **ret; // other combo\n\tint spacing; // spacing\n} menubulk_t;\n\n#define MB_CONSOLECMD(text, cmd, tip) \t\t\t\t\t\t\t\t{mt_button,\t\t0, localtext(text), localtext(tip), cmd}\n#define MB_CONSOLECMDRETURN(text, cmd, tip, ret)\t\t\t\t\t{mt_button,\t\t0, localtext(text), localtext(tip), cmd, NULL, 0, NULL, 0, 0, 0, false, NULL, NULL, NULL, NULL, 0, (union menuoption_s **)&ret}\n#define MB_CHECKBOXCVAR(text, cvar, cvarflag) \t\t\t\t\t\t{mt_checkbox,\t0, localtext(text), NULL, NULL, &cvar, cvarflag}\n#define MB_CHECKBOXCVARTIP(text, cvar, cvarflag, tip) \t\t\t\t{mt_checkbox,\t0, localtext(text), localtext(tip), NULL, &cvar, cvarflag}\n#define MB_CHECKBOXCVARRETURN(text, cvar, cvarflag, ret) \t\t\t{mt_checkbox,\t0, localtext(text), NULL, NULL, &cvar, cvarflag, NULL, 0, 0, 0, false, NULL, NULL, NULL, NULL, 0, (union menuoption_s **)&ret}\n#define MB_CHECKBOXFUNC(text, func, flags, tip) \t\t\t\t\t{mt_checkbox,\t0, localtext(text), localtext(tip), NULL, NULL, flags, func}\n#define MB_SLIDER(text, cvar, min, max, delta, tip) \t\t\t\t{mt_slider,\t\t0, localtext(text), localtext(tip), NULL, &cvar, 0, NULL, min, max, delta}\n#define MB_TEXT(text, align) \t\t\t\t\t\t\t\t\t\t{mt_text,\t\t0, localtext(text), NULL, NULL, NULL, 0, NULL, 0, 0, 0, align}\n#define MB_REDTEXT(text, align) \t\t\t\t\t\t\t\t\t{mt_text,\t\t1, localtext(text), NULL, NULL, NULL, 0, NULL, 0, 0, 0, align}\n#define MB_CMD(text, cmdfunc, tip) \t\t\t\t\t\t\t\t\t{mt_button,\t\t1, localtext(text), localtext(tip), NULL, NULL, 0, NULL, 0, 0, 0, false, cmdfunc}\n#define MB_EDITCVARTIP(text, cvarname, tip) \t\t\t\t\t\t{mt_edit,\t\t0, localtext(text), localtext(tip), NULL, NULL, 0, NULL, 0, 0, 0, false, NULL, cvarname}\n#define MB_EDITCVAR(text, cvarname) \t\t\t\t\t\t\t\t{mt_edit,\t\t0, localtext(text), NULL, NULL, NULL, 0, NULL, 0, 0, 0, false, NULL, cvarname}\n#define MB_EDITCVARSLIM(text, cvarname, tip) \t\t\t\t\t\t{mt_edit,\t\t1, localtext(text), localtext(tip), NULL, NULL, 0, NULL, 0, 0, 0, false, NULL, cvarname}\n#define MB_EDITCVARSLIMRETURN(text, cvarname, ret) \t\t\t\t\t{mt_edit,\t\t1, localtext(text), NULL, NULL, NULL, 0, NULL, 0, 0, 0, false, NULL, cvarname, NULL, NULL, 0, (union menuoption_s **)&ret}\n#define MB_COMBOCVAR(text, cvar, options, values, tip) \t\t\t\t{mt_combo,\t\t0, localtext(text), localtext(tip), NULL, &cvar, 0, NULL, 0, 0, 0, false, NULL, NULL, options, values}\n#define MB_COMBORETURN(text, options, selected, ret, tip) \t\t\t{mt_combo,\t\t1, localtext(text), localtext(tip), NULL, NULL, 0, NULL, 0, 0, 0, false, NULL, NULL, options, NULL, selected, (union menuoption_s **)&ret}\n#define MB_COMBOCVARRETURN(text, cvar, options, values, ret, tip) \t{mt_combo,\t\t0, localtext(text), localtext(tip), NULL, &cvar, 0, NULL, 0, 0, 0, false, NULL, NULL, options, values, 0, (union menuoption_s **)&ret}\n#define MB_SPACING(space) \t\t\t\t\t\t\t\t\t\t\t{mt_text,\t\t2, NULL, NULL, NULL, NULL, 0, NULL, 0, 0, 0, false, NULL, NULL, NULL, NULL, 0, NULL, space}\n#define MB_END() \t\t\t\t\t\t\t\t\t\t\t\t\t{mt_text, -1}\n\nint MC_AddBulk(emenu_t *menu, menuresel_t *resel, menubulk_t *bulk, int xstart, int xtextend, int y);\n\n\n\nemenu_t *M_Options_Title(int *y, int infosize);\t/*Create a menu with the default options titlebar*/\nemenu_t *M_CreateMenu (int extrasize);\nvoid M_RemoveMenu (emenu_t *menu);\nvoid M_RemoveAllMenus (qboolean leaveprompts);\nvoid M_ReloadMenus(void);\nmenubutton_t *M_FindButton(emenu_t *menu, const char *command);\n\nvoid M_Complex_Key(emenu_t *currentmenu, int key, int unicode);\nvoid M_Script_Init(void);\nvoid M_Serverlist_Init(void);\n\nvoid M_Menu_BasedirPrompt(ftemanifest_t *man);\n\nconst char *M_ChooseAutoSave(void);\nvoid M_Menu_Main_f (void);\n\tvoid M_Menu_SinglePlayer_f (void);\n\t\tvoid M_Menu_Load_f (void);\n\t\tvoid M_Menu_Save_f (void);\n\tvoid M_Menu_MultiPlayer_f (void);\n\t\tvoid M_Menu_Setup_f (void);\n\t\tvoid M_Menu_Net_f (void);\n\tvoid M_Menu_Options_f (void);\n\t\tvoid M_Menu_Keys_f (void);\n\t\tvoid M_Menu_Video_f (void);\n\tvoid M_Menu_Help_f (void);\n\tvoid M_Menu_Quit_f (void);\n\tvoid M_Menu_Preset_f (void);\nvoid M_Menu_LanConfig_f (void);\nvoid M_Menu_GameOptions_f (void);\nvoid M_Menu_Search_f (void);\nvoid M_Menu_ServerList_f (void);\n\n/*\nvoid M_Main_Draw (void);\n\tvoid M_SinglePlayer_Draw (void);\n\t\tvoid M_Load_Draw (void);\n\t\tvoid M_Save_Draw (void);\n\tvoid M_MultiPlayer_Draw (void);\n\t\tvoid M_Setup_Draw (void);\n\t\tvoid M_Net_Draw (void);\n\tvoid M_Options_Draw (void);\n\t\tvoid M_Video_Draw (void);\n\tvoid M_Help_Draw (void);\n\tvoid M_Quit_Draw (void);\nvoid M_LanConfig_Draw (void);\nvoid M_GameOptions_Draw (void);\nvoid M_Search_Draw (void);\nvoid M_ServerList_Draw (void);\n\nvoid M_Main_Key (int key);\n\tvoid M_SinglePlayer_Key (int key);\n\t\tvoid M_Load_Key (int key);\n\t\tvoid M_Save_Key (int key);\n\tvoid M_MultiPlayer_Key (int key);\n\t\tvoid M_Setup_Key (int key);\n\t\tvoid M_Net_Key (int key);\n\tvoid M_Options_Key (int key);\n\t\tvoid M_Video_Key (int key);\n\tvoid M_Help_Key (int key);\n\tvoid M_Quit_Key (int key);\nvoid M_LanConfig_Key (int key);\nvoid M_GameOptions_Key (int key);\nvoid M_Search_Key (int key);\nvoid M_ServerList_Key (int key);\n*/\n\n//drawing funcs\nvoid M_BuildTranslationTable(unsigned int pc, unsigned int top, unsigned int bottom, unsigned int *translationTable);\nvoid M_DrawCharacter (int cx, int line, unsigned int num);\nvoid M_Print (int cx, int cy, qbyte *str);\nvoid M_PrintWhite (int cx, int cy, qbyte *str);\nvoid M_DrawScalePic (int x, int y, int w, int h, mpic_t *pic);\n\n\nvoid M_UnbindCommand (const char *command);\n\n#else\n//no builtin menu code.\n//stubs\n//#define M_Shutdown(t) MP_Shutdown()\n\nvoid M_Init (void);\nvoid M_Reinit(void);\nvoid M_Shutdown(qboolean total);\n#endif\nint M_FindKeysForCommand (int bindmap, int pnum, const char *command, int *keylist, int *keymods, int keycount);\nint QDECL M_FindKeysForBind (int bindmap, const char *command, int *keylist, int *keymods, int keycount);\nvoid M_ToggleMenu_f (void);\n\n#ifdef MENU_DAT\nvoid MP_CvarChanged(cvar_t *var);\nqboolean MP_Init (void);\nvoid MP_Shutdown (void);\nqboolean MP_Toggle(int mode);\nvoid MP_RendererRestarted(void);\nvoid MP_Draw(void);\nqboolean MP_UsingGamecodeLoadingScreen(void);\nvoid MP_RegisterCvarsAndCmds(void);\nint MP_BuiltinValid(const char *name, int num);\nqboolean MP_ConsoleCommand(const char *cmdtext);\nint MP_GetServerCategory(int index);\n#else\n#define MP_UsingGamecodeLoadingScreen() false\n#endif\n\n#ifdef MENU_NATIVECODE\n#ifdef HAVE_CLIENT\n#include \"api_menu.h\"\nextern menu_export_t *mn_entry;\nvoid MN_Shutdown(void);\nqboolean MN_Init(void);\n#endif\n#endif\n\n#define MGT_BAD    ~0\n#define MGT_QUAKE1 0\n#define MGT_HEXEN2 1\n#define MGT_QUAKE2 2\nint M_GameType(void);\n\n\n\n\n\n//plugin functions\n#ifdef PLUGINS\nqboolean\tPlug_CenterPrintMessage(const char *buffer, int clientnum);\nqboolean\tPlug_ChatMessage(char *buffer, int talkernum, int tpflags);\nvoid\t\tPlug_Command_f(void);\nint\t\t\tPlug_ConnectionlessClientPacket(char *buffer, int size);\nqboolean\tPlug_ConsoleLink(char *text, char *info, const char *consolename);\nqboolean\tPlug_ConsoleLinkMouseOver(float x, float y, char *text, char *info);\nvoid\t\tPlug_DrawReloadImages(void);\nvoid\t\tPlug_Initialise(qboolean fromgamedir);\nvoid\t\tPlug_Shutdown(qboolean preliminary);\nqboolean\tPlug_Menu_Event(int eventtype, int keyparam, int unicodeparam);\nvoid\t\tPlug_ResChanged(qboolean restarted);\nvoid\t\tPlug_SBar(playerview_t *pv);\nqboolean\tPlug_ServerMessage(char *buffer, int messagelevel);\nvoid\t\tPlug_Tick(void);\nqboolean\tPlugin_ExecuteString(void);\nvoid\t\tPlug_FreeAllImages(void);\n\n#ifdef ANDROID\n#define PLUGINPREFIX \"libplug_\" //android is kinda annoying and only extracts specific files.\n#else\n#define PLUGINPREFIX \"fteplug_\" //this string defines what consitutes a plugin, as opposed to some other dll\n#endif\n#endif\n"
  },
  {
    "path": "engine/client/merged.h",
    "content": "#ifndef MERGED_H\n#define MERGED_H\n\n#ifdef VKQUAKE\n//we need some types available elsewhere, but don't really want to have to include the entire vulkan api everywhere.\n//unfortunately, vulkan's handle types are not well defined.\n#if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__) ) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__)\n#define VulkanAPIRandomness void*\n#elif defined(_MSC_VER) && _MSC_VER < 1300\n#define VulkanAPIRandomness __int64\n#else\n#define VulkanAPIRandomness long long\n#endif\n#define qVkDescriptorSet VulkanAPIRandomness\n#define qVkShaderModule VulkanAPIRandomness\n#define qVkPipelineLayout VulkanAPIRandomness\n#define qVkDescriptorSetLayout VulkanAPIRandomness\n#define qVkBuffer VulkanAPIRandomness\n#define qVkDeviceMemory VulkanAPIRandomness\n#endif\n\n//These are defined later in the source tree. This file should probably be moved to a later spot.\nstruct pubprogfuncs_s;\nstruct globalvars_s;\nstruct texture_s;\nstruct texnums_s;\nstruct vbo_s;\nstruct mesh_s;\nstruct batch_s;\nstruct entity_s;\nstruct dlight_s;\nstruct galiasbone_s;\nstruct dlight_s;\nstruct font_s;\n\ntypedef enum\n{\n\tSKEL_RELATIVE,\t//relative to parent.\n\tSKEL_ABSOLUTE,\t//relative to model. doesn't blend very well.\n\tSKEL_INVERSE_RELATIVE,\t//pre-inverted. faster than regular relative but has weirdness with skeletal objects. blends okay.\n\tSKEL_INVERSE_ABSOLUTE,\t//final renderable type.\n\tSKEL_IDENTITY,\t//PANIC\n\tSKEL_QUATS\t\t//quat+org, 7 floats rather than 12. better lerping.\n} skeltype_t;\n\n#ifdef HALFLIFEMODELS\n\t#define MAX_BONE_CONTROLLERS 5\n#endif\n\n#ifdef HAVE_LEGACY\n#define FRAME_BLENDS 4\t//for compat with DP (for mods that want 4-way blending yet refuse to use framegroups properly). real mods should be using skeletal objects allowing for N-way blending.\n#else\n#define FRAME_BLENDS 2\n#endif\n\n#define FST_BASE 0\t//base frames\n#define FS_REG 1\t//regular frames\n#define FS_COUNT 2\t//regular frames\ntypedef struct framestate_s {\n\tstruct framestateregion_s {\n\t\tint frame[FRAME_BLENDS];\n\t\tfloat frametime[FRAME_BLENDS];\n\t\tfloat lerpweight[FRAME_BLENDS];\n\n#ifdef HALFLIFEMODELS\n\t\tfloat subblendfrac;\t\t//hl models are weird\n\t\tfloat subblend2frac;\t//very weird.\n#endif\n\n\t\tint endbone;\n\t} g[FS_COUNT];\n\n#ifdef SKELETALOBJECTS\n\tfloat *bonestate;\n\tint bonecount;\n\tskeltype_t skeltype;\n#endif\n\n#ifdef HALFLIFEMODELS\n\tfloat bonecontrols[MAX_BONE_CONTROLLERS];\t//hl special bone controllers\n#endif\n} framestate_t;\n#define NULLFRAMESTATE (framestate_t*)NULL\n\n\n\n\n//function prototypes\n\n#if defined(SERVERONLY)\n#define qrenderer QR_NONE\n#define FNC(n) (n)\t\t\t//FNC is defined as 'pointer if client build, direct if dedicated server'\n\n#else\n#define FNC(n) (*n)\nextern r_qrenderer_t qrenderer;\nextern char *q_renderername;\n\napic_t *R2D_LoadAtlasedPic(const char *name);\nvoid R2D_ImageAtlas(float x, float y, float w, float h, float s1, float t1, float s2, float t2, apic_t *pic);\n#define R2D_ScalePicAtlas(x,y,w,h,p) R2D_ImageAtlas(x,y,w,h,0,0,1,1,p)\n\nmpic_t *R2D_SafeCachePic (const char *path);\nmpic_t *R2D_SafePicFromWad (const char *name);\nvoid R2D_DrawCrosshair (void);\nvoid R2D_ScalePic (float x, float y, float width, float height, mpic_t *pic);\nvoid R2D_SubPic(float x, float y, float width, float height, mpic_t *pic, float srcx, float srcy, float srcwidth, float srcheight);\nvoid R2D_TransPicTranslate (float x, float y, int width, int height, qbyte *pic, unsigned int *palette);\nvoid R2D_TileClear (float x, float y, float w, float h);\nvoid R2D_FadeScreen (void);\n\nvoid R2D_Font_Changed(void);\nvoid R2D_ConsoleBackground (int firstline, int lastline, qboolean forceopaque);\nvoid R2D_EditorBackground (void);\nqboolean R2D_DrawLevelshot(void);\n\nvoid R2D_Image(float x, float y, float w, float h, float s1, float t1, float s2, float t2, mpic_t *pic);\nvoid R2D_Image2dQuad(vec2_t const*points, vec2_t const*texcoords, vec4_t const*rgba, mpic_t *pic);\n\nvoid R2D_ImageColours(float r, float g, float b, float a);\nvoid R2D_ImagePaletteColour(unsigned int i, float a);\nvoid R2D_FillBlock(float x, float y, float w, float h);\nvoid R2D_Line(float x1, float y1, float x2, float y2, mpic_t *pic);\nextern void (*R2D_Flush)(void);\n\nextern void\t(*Draw_Init)\t\t\t\t\t\t\t(void);\n\nextern void\t(*R_Init)\t\t\t\t\t\t\t\t(void);\nextern void\t(*R_DeInit)\t\t\t\t\t\t\t\t(void);\nextern void\t(*R_RenderView)\t\t\t\t\t\t\t(void);\t\t// must set r_refdef first\n\nextern qboolean\t(*VID_Init)\t\t\t\t\t\t\t(rendererstate_t *info, unsigned char *palette);\nextern void\t(*VID_DeInit)\t\t\t\t\t\t\t(void);\nextern char *(*VID_GetRGBInfo)\t\t\t\t\t\t(int *stride, int *truevidwidth, int *truevidheight, enum uploadfmt *fmt); //if stride is negative, then the return value points to the last line intead of the first. this allows it to be freed normally.\nextern void\t(*VID_SetWindowCaption)\t\t\t\t\t(const char *msg);\n\nextern void *SCR_ScreenShot_Capture\t\t\t\t\t(int fbwidth, int fbheight, int *stride, enum uploadfmt *fmt, qboolean no2d, qboolean hdr);\nextern void SCR_Init\t\t\t\t\t\t\t\t(void);\nextern void SCR_DeInit\t\t\t\t\t\t\t\t(void);\nextern qboolean (*SCR_UpdateScreen)\t\t\t\t\t(void);\nextern void SCR_BeginLoadingPlaque\t\t\t\t\t(void);\nextern void SCR_EndLoadingPlaque\t\t\t\t\t(void);\nextern void SCR_DrawConsole\t\t\t\t\t\t\t(qboolean noback);\nextern void SCR_SetUpToDrawConsole\t\t\t\t\t(void);\nextern void SCR_CenterPrint\t\t\t\t\t\t\t(int pnum, const char *str, qboolean skipgamecode);\n\nint R_DrawTextField(int x, int y, int w, int h, const char *text, unsigned int defaultmask, unsigned int fieldflags, struct font_s *font, vec2_t fontscale);\n#define CPRINT_LALIGN\t\t(1<<0)\t//L\n#define CPRINT_TALIGN\t\t(1<<1)\t//T\n#define CPRINT_RALIGN\t\t(1<<2)\t//R\n#define CPRINT_BALIGN\t\t(1<<3)\t//B\n#define CPRINT_BACKGROUND\t(1<<4)\t//P\n#define CPRINT_NOWRAP\t\t(1<<5)\n\n#define CPRINT_OBITUARTY\t(1<<16)\t//O (show at 2/3rds from top)\n#define CPRINT_PERSIST\t\t(1<<17)\t//P (doesn't time out)\n#define CPRINT_TYPEWRITER\t(1<<18)\t//  (char at a time)\n#define CPRINT_CURSOR\t\t(1<<19)\t//C (use a mouse cursor, also enabled by the presence of a (auto) link)\n\n#endif\n\n//mod_purge flags\nenum mod_purge_e\n{\n\tMP_MAPCHANGED,\t//new map. old stuff no longer needed, can skip stuff if it'll be expensive.\n\tMP_FLUSH,\t\t//user flush command. anything flushable goes.\n\tMP_RESET\t\t//*everything* is destroyed. renderer is going down, or at least nothing depends upon it.\n};\nenum mlverbosity_e\n{\n\tMLV_SILENT,\n\tMLV_SILENTSYNC,\n\tMLV_WARN,\n\tMLV_WARNSYNC,\n\tMLV_ERROR\n};\n\nextern struct model_s *mod_known; //for evil people that want to do evil indexing.\nconst char *Mod_GetEntitiesString(struct model_s *mod);\nvoid Mod_SetEntitiesStringLen(struct model_s *mod, const char *str, size_t strsize);\nvoid Mod_SetEntitiesString(struct model_s *mod, const char *str, qboolean docopy);\nqboolean Mod_LoadEntitiesBlob(struct model_s *mod, const char *entdata, size_t entdatasize);\t//initial read, with .ent file replacement etc\nvoid Mod_ParseEntities(struct model_s *mod);\nvoid Mod_LoadMapArchive(struct model_s *mod, void *archivedata, size_t archivesize);\nextern void\tMod_ClearAll\t\t\t\t\t\t(void);\nextern void Mod_Purge\t\t\t\t\t\t\t(enum mod_purge_e type);\nextern void Mod_SetModifier\t\t\t\t\t\t(const char *modifier);\nextern char mod_modifier[];\nextern qboolean Mod_PurgeModel\t\t\t\t\t(struct model_s\t*mod, enum mod_purge_e ptype);\nextern struct model_s *Mod_FindName\t\t\t\t(const char *name);\t//find without loading. needload should be set.\nextern struct model_s *Mod_ForName\t\t\t\t(const char *name, enum mlverbosity_e verbosity);\t//finds+loads\nextern struct model_s *Mod_LoadModel\t\t\t(struct model_s *mod, enum mlverbosity_e verbose);\t//makes sure a model is loaded\nextern void Mod_FileWritten\t\t\t\t\t\t(const char *filename);\nextern void\t*Mod_Extradata\t\t\t\t\t\t(struct model_s *mod);\t// handles caching\nextern void\tMod_TouchModel\t\t\t\t\t\t(const char *name);\nextern const char *Mod_FixName\t\t\t\t\t(const char *modname, const char *worldname);\t//remaps the name appropriately\nconst char *Mod_ParseWorldspawnKey\t\t\t\t(struct model_s *mod, const char *key, char *buffer, size_t sizeofbuffer);\n\nextern qboolean Mod_GetModelEvent\t\t\t\t(struct model_s *model, int animation, int eventidx, float *timestamp, int *eventcode, char **eventdata);\nextern int Mod_SkinNumForName\t\t\t\t\t(struct model_s *model, int surfaceidx, const char *name);\nextern int Mod_FrameNumForName\t\t\t\t\t(struct model_s *model, int surfaceidx, const char *name);\nextern float Mod_GetFrameDuration\t\t\t\t(struct model_s *model, int surfaceidx, int framenum);\nextern int Mod_GetFrameCount\t\t\t\t\t(struct model_s *model);\n\n#undef FNC\n\nextern qboolean\tMod_GetTag\t\t\t\t\t\t(struct model_s *model, int tagnum, framestate_t *framestate, float *transforms);\nextern int Mod_TagNumForName\t\t\t\t\t(struct model_s *model, const char *name, int firsttag);\n\nvoid Mod_AddSingleSurface(struct entity_s *ent, int surfaceidx, shader_t *shader, int mode);\nint Mod_GetNumBones(struct model_s *model, qboolean allowtags);\nint Mod_GetBoneRelations(struct model_s *model, int firstbone, int lastbone, const struct galiasbone_s *boneinfo, const framestate_t *fstate, float *result);\nint Mod_GetBoneParent(struct model_s *model, int bonenum);\nstruct galiasbone_s *Mod_GetBoneInfo(struct model_s *model, int *numbones);\nconst char *Mod_GetBoneName(struct model_s *model, int bonenum);\n\nvoid Draw_FunString(float x, float y, const void *str);\nvoid Draw_FunStringU8(unsigned int flags, float x, float y, const void *str);\nvoid Draw_AltFunString(float x, float y, const void *str);\nvoid Draw_FunStringWidthFont(struct font_s *font, float x, float y, const void *str, int width, int rightalign, qboolean highlight);\n#define Draw_FunStringWidth(x,y,str,width,rightalign,highlight) Draw_FunStringWidthFont(font_default,x,y,str,width,rightalign,highlight)\n\nextern int r_regsequence;\n\nenum\n{\n\tTEX_NOTLOADED,\n\tTEX_LOADING,\n\tTEX_LOADED,\n\tTEX_FAILED\n};\ntypedef struct image_s\n{\n#ifdef _DEBUG\n\tchar dbgident[32];\n#endif\n\tchar *ident;\t//allocated on end\n\tchar *subpath;\t//allocated on end\n\tint regsequence;\n\tint width;\t//this is the logical size. the physical size is not considered important (except for render targets, which should not be loaded from disk).\n\tint height;\n\tint depth;\n\tuploadfmt_t format;\n\tint status;\t//TEX_\n\tunsigned int flags;\n\tstruct image_s *next;\n//\tstruct image_s *prev;\n\tstruct image_s *aliasof;\n\tunion\n\t{\n\t\tint nosyntaxerror;\n#if defined(D3DQUAKE) || defined(SWQUAKE)\n\t\tstruct\n\t\t{\n\t\t\tvoid *ptr;\t//texture\n\t\t\tvoid *ptr2;\t//view\n\t\t};\n#endif\n#ifdef GLQUAKE\n\t\tint num;\n#endif\n#ifdef VKQUAKE\n\t\tstruct\n\t\t{\n\t\t\tqVkDescriptorSet vkdescriptor;\n\t\t\tstruct vk_image_s *vkimage;\n\t\t};\n#endif\n\t};\n\n\tvoid *fallbackdata;\n\tint fallbackwidth;\n\tint fallbackheight;\n\tuploadfmt_t fallbackfmt;\n} image_t;\n\n#if 1\ntypedef image_t *texid_t;\n#define texid_tf texid_t\n#define TEXASSIGN(d,s) d=s\n#define TEXASSIGNF(d,s) d=s\n#define TEXVALID(t) ((t))\n#define TEXLOADED(tex) ((tex) && (tex)->status == TEX_LOADED)\n#define TEXDOWAIT(tex)\tdo{if ((tex) && (tex)->status == TEX_LOADING)\tCOM_WorkerPartialSync((tex), &(tex)->status, TEX_LOADING);}while(0)\n#else\ntypedef struct texid_s texid_t[1];\ntypedef struct texid_s texid_tf;\n#define TEXASSIGN(d,s) memcpy(&d,&s,sizeof(d))\n#define TEXASSIGNF(d,s) memcpy(&d,&s,sizeof(d))\n#define TEXVALID(t) 1\n#endif\n\n#define Image_LinearFloatFromsRGBFloat(c) (((c) <= 0.04045f) ? (c) * (1.0f / 12.92f) : (float)pow(((c) + 0.055f)*(1.0f/1.055f), 2.4f))\n#define Image_sRGBFloatFromLinearFloat(c) (((c) < 0.0031308f) ? (c) * 12.92f : 1.055f * (float)pow((c), 1.0f/2.4f) - 0.055f)\n\nstruct pendingtextureinfo\n{\n\tenum imgtype_e\n\t{\n\t\t//formats are all w*h*d (where depth has limitations according to type)\n\t\tPTI_2D,\t\t\t//w*h*1 - depth MUST be 1\n\t\tPTI_3D,\t\t\t//w*h*d - we can't generate 3d mips\n\t\tPTI_CUBE,\t\t//w*h*6 - depth MUST be 6 (faces must be tightly packed)\n\t\tPTI_2D_ARRAY,\t//w*h*layers - depth is =layers\n\t\tPTI_CUBE_ARRAY,\t//w*h*(layers*6) - depth is =(layers*6).\n\n\t\tPTI_ANY\t\t\t//says we don't care.\n\t} type;\n\n\tuploadfmt_t encoding;\t//PTI_* formats\n\tvoid *extrafree;\t\t//avoids some memcpys\n\tunsigned int mipcount;\n\tstruct\n\t{\n\t\tvoid *data;\n\t\tsize_t datasize; //ceil(width/blockwidth)*ceil(height/blockheight)*ceil(depth/blockdepth)*blocksize - except that blockdepth is always considered 1 for now.\n\t\tunsigned int width;\n\t\tunsigned int height;\n\t\tint depth;\n\t\tqboolean needfree;\n\t} mip[72];\t//enough for a 4096 cubemap. or a really smegging big 2d texture...\n\t//mips are ordered as in arrayindex THEN mip order, allowing easy truncation of mip levels.\n\t//cubemaps are just arrayindex*6\n};\n\n//small context for easy vbo creation.\ntypedef struct\n{\n\tsize_t maxsize;\n\tsize_t pos;\n\tint vboid[2];\n\tvoid *vboptr[2];\n\tvoid *fallback;\n} vbobctx_t;\n\ntypedef union vboarray_s\n{\n\tvoid *sysptr;\n#ifdef GLQUAKE\n\tstruct\n\t{\n\t\tint vbo;\n\t\tvoid *addr;\n\t} gl;\n#endif\n#if defined(D3D8QUAKE) || defined(D3D9QUAKE) || defined(D3D11QUAKE)\n\tstruct\n\t{\n\t\tvoid *buff;\n\t\tunsigned int offs;\n\t} d3d;\n#endif\n#ifdef VKQUAKE\n\tstruct\n\t{\n\t\tqVkBuffer buff;\n\t\tunsigned int offs;\n\t} vk;\n#endif\n\tstruct\n\t{\t//matches the biggest version. currently vulkan. this ensures that plugins can allocate model data without caring about renderers.\n\t\tqint64_t buff;\n\t\tunsigned int offs;\n\t} pad;\n} vboarray_t;\n\n//scissor rects\ntypedef struct\n{\n\tfloat x;\n\tfloat y;\n\tfloat width;\n\tfloat height;\n\tdouble dmin;\n\tdouble dmax;\n} srect_t;\n\ntypedef struct texnums_s {\n\tchar\tmapname[MAX_QPATH];\t//the 'official' name of the diffusemap. used to generate filenames for other textures.\n\ttexid_t base;\t\t\t//2d,\tregular diffuse texture. may have alpha if surface is transparent.\n\ttexid_t bump;\t\t\t//2d,\tnormalmap. height values packed in alpha.\n\ttexid_t specular;\t\t//2d,\tspecular lighting values. alpha contains exponent multiplier\n\ttexid_t upperoverlay;\t//2d,\tdiffuse texture for the upper body(shirt colour). no alpha channel. added to base.rgb. ideally an l8 texture\n\ttexid_t loweroverlay;\t//2d,\tdiffuse texture for the lower body(trouser colour). no alpha channel. added to base.rgb. ideally an l8 texture\n\ttexid_t paletted;\t\t//2d,\t8bit paletted data, just because. only red is used.\n\ttexid_t fullbright;\t\t//2d,\temissive texture. alpha should be 1.\n\ttexid_t reflectcube;\t//cube,\tfor fake reflections\n\ttexid_t reflectmask;\t//2d,\tdefines how reflective it is (for cubemap reflections)\n\ttexid_t displacement;\t//2d,\talternative to bump.a, eg R16[F] for offsetmapping or tessellation\n\ttexid_t occlusion;\t\t//2d,\tocclusion map... only red is used.\n\ttexid_t transmission;\t//2d,\tmultiplier for transmissionfactor... only red is used.\n\ttexid_t thickness;\t\t//2d,\tmultiplier for thicknessfactor... only green is used.\n\n\t//the material's pushconstants. vulkan guarentees only 128 bytes. so 8 vec4s. note that lmscales should want 4 of them...\n\t/*struct\n\t{\n\t\tvec4_t basefactors;\n\t\tvec4_t specfactors;\n\t\tvec4_t fullbrightfactors;\n\n\t\t//FIXME: envmap index, lightmap index, etc.\n\t} factors;*/\n} texnums_t;\n\n//not all modes accept meshes - STENCIL(intentional) and DEPTHONLY(not implemented)\ntypedef enum backendmode_e\n{\n\tBEM_STANDARD,\t\t\t//regular mode to draw surfaces akin to q3 (aka: legacy mode). lightmaps+delux+ambient\n\tBEM_DEPTHONLY,\t\t\t//just a quick depth pass. textures used only for alpha test (shadowmaps).\n\tBEM_WIREFRAME,\t\t\t//for debugging or something\n\tBEM_STENCIL,\t\t\t//used for drawing shadow volumes to the stencil buffer.\n\tBEM_DEPTHDARK,\t\t\t//a quick depth pass. textures used only for alpha test. additive textures still shown as normal.\n\tBEM_CREPUSCULAR,\t\t//sky is special, everything else completely black\n\tBEM_GBUFFER,\t\t\t//\n\tBEM_FOG,\t\t\t\t//drawing a fog volume\n\tBEM_LIGHT,\t\t\t\t//we have a valid light\n} backendmode_t;\n\ntypedef struct rendererinfo_s {\n\tchar *description;\n\tchar *name[5];\n\tr_qrenderer_t rtype;\n\t//FIXME: all but the vid stuff really should be filled in by the video code, simplifying system-specific stuff.\n\n//FIXME: remove these...\n\tvoid\t(*Draw_Init)\t\t\t\t(void);\n\tvoid\t(*Draw_Shutdown)\t\t\t(void);\n\n\tvoid\t (*IMG_UpdateFiltering)\t\t(image_t *imagelist, int filtermip[3], int filterpic[3], int mipcap[2], float lodbias, float anis);\n\tqboolean (*IMG_LoadTextureMips)\t\t(texid_t tex, const struct pendingtextureinfo *mips);\n\tvoid\t (*IMG_DestroyTexture)\t\t(texid_t tex);\n\n\tvoid\t (*R_Init)\t\t\t\t\t(void); //FIXME - merge implementations\n\tvoid\t (*R_DeInit)\t\t\t\t\t(void);\t//FIXME - merge implementations\n\tvoid\t (*R_RenderView)\t\t\t\t(void);\t// must set r_refdef first\n\n//FIXME: keep these...\n\tqboolean (*VID_Init)\t\t\t\t(rendererstate_t *info, unsigned char *palette);\n\tvoid\t (*VID_DeInit)\t\t\t\t(void);\n\tvoid\t (*VID_SwapBuffers)\t\t\t(void);\t//force a buffer swap, regardless of what's displayed.\n\tqboolean (*VID_ApplyGammaRamps)\t\t(unsigned int size, unsigned short *ramps);\n\n\tvoid\t*(*VID_CreateCursor)\t\t\t(const qbyte *imagedata, int width, int height, uploadfmt_t format, float hotx, float hoty, float scale);\t//may be null, stub returns null\n\tqboolean (*VID_SetCursor)\t\t\t(void *cursor);\t//may be null\n\tvoid\t (*VID_DestroyCursor)\t\t\t(void *cursor);\t//may be null\n\n\tvoid\t (*VID_SetWindowCaption)\t\t(const char *msg);\n\n//FIXME: add clipboard stuff...\n\n//FIXME: remove these...\n\tchar\t*(*VID_GetRGBInfo)\t\t\t(int *bytestride, int *truevidwidth, int *truevidheight, enum uploadfmt *fmt);\n\n\tqboolean (*SCR_UpdateScreen)\t\t\t(void);\n\n\t\n\t//Select the current render mode and modifier flags\n\tvoid\t(*BE_SelectMode)(backendmode_t mode);\n\t/*Draws an entire mesh list from a VBO. vbo can be null, in which case the chain may be drawn without batching.\n\t  Rules for using a list: Every mesh must be part of the same VBO, shader, lightmap, and must have the same pointers set*/\n\tvoid\t(*BE_DrawMesh_List)(shader_t *shader, int nummeshes, struct mesh_s **mesh, struct vbo_s *vbo, struct texnums_s *texnums, unsigned int be_flags);\n\tvoid\t(*BE_DrawMesh_Single)(shader_t *shader, struct mesh_s *meshchain, struct vbo_s *vbo, unsigned int be_flags);\n\tvoid\t(*BE_SubmitBatch)(struct batch_s *batch);\n\tstruct batch_s *(*BE_GetTempBatch)(void);\n\t//Asks the backend to invoke DrawMeshChain for each surface, and to upload lightmaps as required\n\tvoid\t(*BE_DrawWorld) (struct batch_s **worldbatches);\n\t//called at init, force the display to the right defaults etc\n\tvoid\t(*BE_Init)(void);\n\t//Generates an optimised VBO, one for each texture on the map\n\tvoid\t(*BE_GenBrushModelVBO)(struct model_s *mod);\n\t//Destroys the given vbo\n\tvoid\t(*BE_ClearVBO)(struct vbo_s *vbo, qboolean dataonly);\n\t//Uploads all modified lightmaps\n\tvoid\t(*BE_UploadAllLightmaps)(void);\n\tvoid\t(*BE_SelectEntity)(struct entity_s *ent);\n\tqboolean (*BE_SelectDLight)(struct dlight_s *dl, vec3_t colour, vec3_t axis[3], unsigned int lmode);\n\tvoid\t(*BE_Scissor)(srect_t *rect);\n\t/*check to see if an ent should be drawn for the selected light*/\n\tqboolean (*BE_LightCullModel)(vec3_t org, struct model_s *model);\n\tvoid\t(*BE_VBO_Begin)(vbobctx_t *ctx, size_t maxsize);\n\tvoid\t(*BE_VBO_Data)(vbobctx_t *ctx, void *data, size_t size, vboarray_t *varray);\n\tvoid\t(*BE_VBO_Finish)(vbobctx_t *ctx, void *edata, size_t esize, vboarray_t *earray, void **vbomem, void **ebomem);\n\tvoid\t(*BE_VBO_Destroy)(vboarray_t *vearray, void *mem);\n\tvoid\t(*BE_RenderToTextureUpdate2d)(qboolean destchanged);\n\n\tchar *alignment;\t//just to make sure that added functions cause compile warnings.\n\n\tint\t\t(*VID_GetPriority)\t(void);\t//so that eg x11 or wayland can be prioritised depending on environment settings. assumed to be 1.\n\tvoid\t(*VID_EnumerateVideoModes) (const char *driver, const char *output, void (*cb) (int w, int h));\n\tqboolean(*VID_EnumerateDevices) (void *usercontext, void(*callback)(void *context, const char *devicename, const char *outputname, const char *desc));\n\tqboolean(*VID_MayRefresh)\t(void);\n\n//FIXME: add getdestopres\n//FIXME: add clipboard handling\n} rendererinfo_t;\n\n#define rf currentrendererstate.renderer\n\n#define VID_SwapBuffers\t\trf->VID_SwapBuffers\n#define VID_MayRefresh\t\trf->VID_MayRefresh\n\n#define BE_Init\t\t\t\t\trf->BE_Init\n#define BE_SelectMode\t\t\trf->BE_SelectMode\n#define BE_GenBrushModelVBO\t\trf->BE_GenBrushModelVBO\n#define BE_ClearVBO\t\t\t\trf->BE_ClearVBO\n#define BE_UploadAllLightmaps\trf->BE_UploadAllLightmaps\n#define BE_LightCullModel\t\trf->BE_LightCullModel\n#define BE_SelectEntity\t\t\trf->BE_SelectEntity\n#define BE_SelectDLight\t\t\trf->BE_SelectDLight\n#define BE_GetTempBatch\t\t\trf->BE_GetTempBatch\n#define BE_SubmitBatch\t\t\trf->BE_SubmitBatch\n#define BE_DrawMesh_List\t\trf->BE_DrawMesh_List\n#define BE_DrawMesh_Single\t\trf->BE_DrawMesh_Single\n#define BE_SubmitMeshes\t\t\trf->BE_SubmitMeshes\n#define BE_DrawWorld\t\t\trf->BE_DrawWorld\n#define BE_VBO_Begin \t\t\trf->BE_VBO_Begin\n#define BE_VBO_Data\t\t\t\trf->BE_VBO_Data\n#define BE_VBO_Finish\t\t\trf->BE_VBO_Finish\n#define BE_VBO_Destroy\t\t\trf->BE_VBO_Destroy\n#define BE_Scissor\t\t\t\trf->BE_Scissor\n\n#define BE_RenderToTextureUpdate2d rf->BE_RenderToTextureUpdate2d\n\n#define RT_IMAGEFLAGS (IF_NOMIPMAP|IF_CLAMP|IF_LINEAR|IF_RENDERTARGET)\ntexid_t R2D_RT_Configure(const char *id, int width, int height, uploadfmt_t rtfmt, unsigned int imageflags);\ntexid_t R2D_RT_GetTexture(const char *id, unsigned int *width, unsigned int *height);\n\n#endif //MERGED_H\n"
  },
  {
    "path": "engine/client/modelgen.h",
    "content": "/*\nCopyright (C) 1996-1997 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  \n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n*/\n//\n// modelgen.h: header file for model generation program\n//\n\n// *********************************************************\n// * This file must be identical in the modelgen directory *\n// * and in the Quake directory, because it's used to      *\n// * pass data from one to the other via model files.      *\n// *********************************************************\n\n#ifdef INCLUDELIBS\n\n#include <stdlib.h>\n#include <stdio.h>\n#include <math.h>\n#include <string.h>\n\n#include \"cmdlib.h\"\n#include \"scriplib.h\"\n#include \"trilib.h\"\n#include \"lbmlib.h\"\n#include \"mathlib.h\"\n\n#endif\n\n#define ALIAS_VERSION\t6\n#define QTESTALIAS_VERSION 3\n\n#define ALIAS_ONSEAM\t\t\t\t0x0020\n\n// must match definition in spritegn.h\n#ifndef SYNCTYPE_T\n#define SYNCTYPE_T\ntypedef enum {ST_SYNC=0, ST_RAND } synctype_t;\n#endif\n\ntypedef enum { ALIAS_SINGLE=0, ALIAS_GROUP, ALIAS_GROUP_SWAPPED=0x01000000 } aliasframetype_t;\n\ntypedef enum { ALIAS_SKIN_SINGLE=0, ALIAS_SKIN_GROUP } aliasskintype_t;\n\ntypedef struct {\n\tint\t\t\tident;\n\tint\t\t\tversion;\n\tvec3_t\t\tscale;\n\tvec3_t\t\tscale_origin;\n\tfloat\t\tboundingradius;\n\tvec3_t\t\teyeposition;\n\tint\t\t\tnumskins;\n\tint\t\t\tskinwidth;\n\tint\t\t\tskinheight;\n\tint\t\t\tnumverts;\n\tint\t\t\tnumtris;\n\tint\t\t\tnumframes;\n\tsynctype_t\tsynctype;\n//qtest stops here\n\tint\t\t\tflags;\t\t//offset 0x4c\n\tfloat\t\tsize;\n//quake stops here\n\tint\t\t\tnum_st;\n//rapo stops here\n} dmdl_t;\n\n// TODO: could be shorts\n\ntypedef struct {\n\tint\t\tonseam;\n\tint\t\ts;\n\tint\t\tt;\n} dstvert_t;\n\ntypedef struct {\n\tshort\t\ts;\n\tshort\t\tt;\n} dmd2stvert_t;\n\ntypedef struct dtriangle_s {\n\tint\t\t\t\t\tfacesfront;\n\tint\t\t\t\t\tvertindex[3];\n} dtriangle_t;\n\ntypedef struct dh2triangle_s {\n\tint\t\t\t\t\tfacesfront;\n\tunsigned short\t\tvertindex[3];\n\tunsigned short      stindex[3];\n} dh2triangle_t;\n\ntypedef struct dmd2triangle_s {\n\tshort\t\t\t\t\txyz_index[3];\n\tshort\t\t\t\t\tst_index[3];\n} dmd2triangle_t;\n\n#define DT_FACES_FRONT\t\t\t\t0x0010\n\n// This mirrors trivert_t in trilib.h, is present so Quake knows how to\n// load this data\n\ntypedef struct {\n\tqbyte\tv[3];\n\tqbyte\tlightnormalindex;\n} dtrivertx_t;\n\ntypedef struct {\n\tdtrivertx_t\tbboxmin;\t// lightnormal isn't used\n\tdtrivertx_t\tbboxmax;\t// lightnormal isn't used\n// qtest stops here\n\tchar\t\tname[16];\t// frame name from grabbing\n} daliasframe_t;\n\ntypedef struct {\n\tint\t\t\tnumframes;\n\tdtrivertx_t\tbboxmin;\t// lightnormal isn't used\n\tdtrivertx_t\tbboxmax;\t// lightnormal isn't used\n} daliasgroup_t;\n\ntypedef struct {\n\tint\t\t\tnumskins;\n} daliasskingroup_t;\n\ntypedef struct {\n\tfloat\tinterval;\n} daliasinterval_t;\n\ntypedef struct {\n\tfloat\tinterval;\n} daliasskininterval_t;\n\ntypedef struct {\n\taliasframetype_t\ttype;\n} daliasframetype_t;\n\ntypedef struct {\n\taliasskintype_t\ttype;\n} daliasskintype_t;\n\n#define IDPOLYHEADER\t\"IDPO\",4 /*little-endian \"IDPO\"*/\n#define RAPOLYHEADER\t\"RAPO\",4 /*used by hexen2 mp*/\n#define MD3_IDENT\t\t\"IDP3\",4 /*quake3, duh*/\n\n\n"
  },
  {
    "path": "engine/client/net_master.c",
    "content": "/*\nserverside master heartbeat code\nclientside master queries and server ping/polls\n*/\n\n#include \"quakedef.h\"\n#include \"cl_master.h\"\n#include \"netinc.h\"\n\n#define FAVOURITESFILE \"favourites.txt\"\n\nqboolean\tsb_favouriteschanged;\t//some server's favourite flag got changed. we'll need to resave the list.\nqboolean\tsb_enablequake2;\nqboolean\tsb_enablequake3;\nqboolean\tsb_enablenetquake;\nqboolean\tsb_enabledarkplaces;\nqboolean\tsb_enablequakeworld;\n\nvoid Master_DetermineMasterTypes(void)\n{\n\tif (com_protocolname.modified)\n\t{\n\t\tchar tok[MAX_QPATH];\n\t\tchar *prot = com_protocolname.string;\n\t\tchar *game;\n\t\tcom_protocolname.modified = 0;\n\n\t\tsb_enabledarkplaces = true;\t//dpmaster is not specific to any single game/mod, so can be left enabled even when running q2 etc, for extra redundancy.\n\t\tsb_enablequake2 = false;\n\t\tsb_enablequake3 = false;\n\t\tsb_enablenetquake = false;\n\t\tsb_enablequakeworld = false;\n\n\t\tfor (prot = com_protocolname.string; *prot;)\n\t\t{\n\t\t\tprot = COM_ParseOut(prot, tok, sizeof(tok));\n\t\t\tgame = tok;\n\t\t\t//this is stupid, but hey\n\t\t\tif (!Q_strncasecmp(game, \"FTE-\", 4))\n\t\t\t\tgame += 4;\n\t\t\telse if (!Q_strncasecmp(game, \"DarkPlaces-\", 11))\n\t\t\t\tgame += 11;\n\n\t\t\tif (!strcmp(game, \"Quake2\"))\n\t\t\t\tsb_enablequake2 = true;\n\t\t\tif (!strcmp(game, \"Quake3\"))\n\t\t\t\tsb_enablequake3 = true;\n\t\t\t//for DP compatibility, we consider these separate(ish) games.\n\t\t\tif (!strcmp(game, \"Quake\") || !strcmp(game, \"Hipnotic\") || !strcmp(game, \"Rogue\"))\n\t\t\t\tsb_enablenetquake = sb_enablequakeworld = true;\n\t\t}\n\t}\n}\nqboolean Master_MasterProtocolIsEnabled(enum masterprotocol_e protocol)\n{\n\tswitch (protocol)\n\t{\n\tcase MP_DPMASTER:\n\t\treturn sb_enabledarkplaces;\n\t#ifdef Q2SERVER\n\tcase MP_QUAKE2:\n\t\treturn sb_enablequake2;\n\t#endif\n\t#ifdef Q3SERVER\n\tcase MP_QUAKE3:\n\t\treturn sb_enablequake3;\n\t#endif\n\tcase MP_QUAKEWORLD:\n\t\treturn sb_enablequakeworld;\n\tdefault:\n\t\treturn false;\n\t}\n}\n\n#define MAX_MASTER_ADDRESSES 4\t//each master might have multiple dns addresses, typically both ipv4+ipv6. we want to report to both address families so we work with remote single-stack hosts.\n\n#ifdef HAVE_SERVER\nstatic void QDECL Net_Masterlist_Callback(struct cvar_s *var, char *oldvalue);\n#ifdef HAVE_LEGACY\nstatic void SV_SetMaster_f (void);\n#endif\n#else\n#define Net_Masterlist_Callback NULL\n#endif\n\nextern cvar_t sv_public;\nextern cvar_t sv_reportheartbeats;\nextern cvar_t sv_heartbeat_interval;\nextern cvar_t sv_heartbeat_checks;\n\nextern cvar_t sv_listen_qw;\nextern cvar_t sv_listen_nq;\nextern cvar_t sv_listen_dp;\nextern cvar_t sv_listen_q3;\n\ntypedef struct {\n\tenum masterprotocol_e protocol;\n\tcvar_t\t\tcv;\n\tchar\t\t*comment;\n\n\tqboolean\tannounced;\t\t//when set, hide when sv_reportheartbeats 2\n\tqboolean\tneedsresolve;\t//set any time the cvar is modified\n\tqboolean\tresolving;\t//set any time the cvar is modified\n\tnetadr_t\tadr[MAX_MASTER_ADDRESSES];\n} net_masterlist_t;\nstatic net_masterlist_t net_masterlist[] = {\n#if 0\t//for debugging\n\t{MP_DPMASTER,\tCVARFC(\"net_masterextra1\",\t\t\"localhost:27950\",\t\t\t\t\t\t\t\t\t\tCVAR_NOSAVE, Net_Masterlist_Callback)}, //admin: the reader...\n#else\n\n#ifndef QUAKETC\n\t//user-specified master lists.\n\t{MP_QUAKEWORLD, CVARC(\"net_qwmaster1\", \"\", Net_Masterlist_Callback)},\n\t{MP_QUAKEWORLD, CVARC(\"net_qwmaster2\", \"\", Net_Masterlist_Callback)},\n\t{MP_QUAKEWORLD, CVARC(\"net_qwmaster3\", \"\", Net_Masterlist_Callback)},\n\t{MP_QUAKEWORLD, CVARC(\"net_qwmaster4\", \"\", Net_Masterlist_Callback)},\n\t{MP_QUAKEWORLD, CVARC(\"net_qwmaster5\", \"\", Net_Masterlist_Callback)},\n\t{MP_QUAKEWORLD, CVARC(\"net_qwmaster6\", \"\", Net_Masterlist_Callback)},\n\t{MP_QUAKEWORLD, CVARC(\"net_qwmaster7\", \"\", Net_Masterlist_Callback)},\n\t{MP_QUAKEWORLD, CVARC(\"net_qwmaster8\", \"\", Net_Masterlist_Callback)},\n#endif\n\n\t//dpmaster is the generic non-quake-specific master protocol that we use for custom stand-alone mods.\n\t{MP_DPMASTER,\tCVARAFC(\"net_master1\", \"\", \"sv_master1\", 0, Net_Masterlist_Callback)},\n\t{MP_DPMASTER,\tCVARAFC(\"net_master2\", \"\", \"sv_master2\", 0, Net_Masterlist_Callback)},\n\t{MP_DPMASTER,\tCVARAFC(\"net_master3\", \"\", \"sv_master3\", 0, Net_Masterlist_Callback)},\n\t{MP_DPMASTER,\tCVARAFC(\"net_master4\", \"\", \"sv_master4\", 0, Net_Masterlist_Callback)},\n\t{MP_DPMASTER,\tCVARAFC(\"net_master5\", \"\", \"sv_master5\", 0, Net_Masterlist_Callback)},\n\t{MP_DPMASTER,\tCVARAFC(\"net_master6\", \"\", \"sv_master6\", 0, Net_Masterlist_Callback)},\n\t{MP_DPMASTER,\tCVARAFC(\"net_master7\", \"\", \"sv_master7\", 0, Net_Masterlist_Callback)},\n\t{MP_DPMASTER,\tCVARAFC(\"net_master8\", \"\", \"sv_master8\", 0, Net_Masterlist_Callback)},\n\n#ifdef Q2CLIENT\n\t{MP_QUAKE2,\t\tCVARC(\"net_q2master1\", \"\", Net_Masterlist_Callback)},\n\t{MP_QUAKE2,\t\tCVARC(\"net_q2master2\", \"\", Net_Masterlist_Callback)},\n\t{MP_QUAKE2,\t\tCVARC(\"net_q2master3\", \"\", Net_Masterlist_Callback)},\n\t{MP_QUAKE2,\t\tCVARC(\"net_q2master4\", \"\", Net_Masterlist_Callback)},\n#endif\n\n#ifdef Q3CLIENT\n\t{MP_QUAKE3,\t\tCVARC(\"net_q3master1\", \"\", Net_Masterlist_Callback)},\n\t{MP_QUAKE3,\t\tCVARC(\"net_q3master2\", \"\", Net_Masterlist_Callback)},\n\t{MP_QUAKE3,\t\tCVARC(\"net_q3master3\", \"\", Net_Masterlist_Callback)},\n\t{MP_QUAKE3,\t\tCVARC(\"net_q3master4\", \"\", Net_Masterlist_Callback)},\n#endif\n\n#ifdef HAVE_PACKET\n#ifndef QUAKETC\n\t//engine-specified/maintained master lists (so users can be lazy and update the engine without having to rewrite all their configs).\n\t{MP_QUAKEWORLD, CVARFC(\"net_qwmasterextra1\", \"\"/*\"qwmaster.ocrana.de:27000\" not responding*/,\tCVAR_NOSAVE, Net_Masterlist_Callback),\t\"Ocrana(2nd)\"},\t//german. admin unknown\n\t{MP_QUAKEWORLD, CVARFC(\"net_qwmasterextra2\", \"\"/*\"masterserver.exhale.de:27000\" dns dead*/,\t\tCVAR_NOSAVE, Net_Masterlist_Callback)},\t//german. admin unknown\n//\t{MP_QUAKEWORLD, CVARFC(\"net_qwmasterextra3\", \"asgaard.morphos-team.net:27000\",\t\t\t\t\tCVAR_NOSAVE, Net_Masterlist_Callback),\t\"Germany, admin: bigfoot\"},\n\t{MP_QUAKEWORLD, CVARFC(\"net_qwmasterextra4\", \"master.quakeservers.net:27000\",\t\t\t\t\tCVAR_NOSAVE, Net_Masterlist_Callback),\t\"Germany, admin: raz0?\"},\n\t{MP_QUAKEWORLD, CVARFC(\"net_qwmasterextra5\", \"qwmaster.fodquake.net:27000\",\t\t\t\t\t\tCVAR_NOSAVE, Net_Masterlist_Callback),\t\"admin: bigfoot\"},\n//\t{MP_QUAKEWORLD, CVARFC(\"net_qwmasterextraHistoric\",\t\"satan.idsoftware.com:27000\",\t\t\t\tCVAR_NOSAVE, Net_Masterlist_Callback),\t\"Official id Master\"},\n//\t{MP_QUAKEWORLD, CVARFC(\"net_qwmasterextraHistoric\",\t\"satan.idsoftware.com:27002\",\t\t\t\tCVAR_NOSAVE, Net_Masterlist_Callback),\t\"Official id Master For CTF Servers\"},\n//\t{MP_QUAKEWORLD, CVARFC(\"net_qwmasterextraHistoric\",\t\"satan.idsoftware.com:27003\",\t\t\t\tCVAR_NOSAVE, Net_Masterlist_Callback),\t\"Official id Master For TeamFortress Servers\"},\n//\t{MP_QUAKEWORLD, CVARFC(\"net_qwmasterextraHistoric\",\t\"satan.idsoftware.com:27004\",\t\t\t\tCVAR_NOSAVE, Net_Masterlist_Callback),\t\"Official id Master For Miscilaneous Servers\"},\n//\t{MP_QUAKEWORLD, CVARFC(\"net_qwmasterextraHistoric\",\t\"satan.idsoftware.com:27006\",\t\t\t\tCVAR_NOSAVE, Net_Masterlist_Callback),\t\"Official id Master For Deathmatch Servers\"},\n//\t{MP_QUAKEWORLD, CVARFC(\"net_qwmasterextraHistoric\",\t\"150.254.66.120:27000\",\t\t\t\t\t\tCVAR_NOSAVE, Net_Masterlist_Callback),\t\"Poland\"},\n//\t{MP_QUAKEWORLD, CVARFC(\"net_qwmasterextraHistoric\",\t\"62.112.145.129:27000\",\t\t\t\t\t\tCVAR_NOSAVE, Net_Masterlist_Callback),\t\"Ocrana (original)\"},\n//\t{MP_QUAKEWORLD, CVARFC(\"net_qwmasterextraHistoric\",\t\"master.edome.net\",\t\t\t\t\t\t\tCVAR_NOSAVE, Net_Masterlist_Callback),\t\"edome\"},\n//\t{MP_QUAKEWORLD, CVARFC(\"net_qwmasterextraHistoric\",\t\"qwmaster.barrysworld.com\",\t\t\t\t\tCVAR_NOSAVE, Net_Masterlist_Callback),\t\"barrysworld\"},\n//\t{MP_QUAKEWORLD, CVARFC(\"net_qwmasterextraHistoric\",\t\"213.221.174.165:27000\",\t\t\t\t\tCVAR_NOSAVE, Net_Masterlist_Callback),\t\"unknown1\"},\n//\t{MP_QUAKEWORLD, CVARFC(\"net_qwmasterextraHistoric\",\t\"195.74.0.8\",\t\t\t\t\t\t\t\tCVAR_NOSAVE, Net_Masterlist_Callback),\t\"unknown2\"},\n//\t{MP_QUAKEWORLD, CVARFC(\"net_qwmasterextraHistoric\",\t\"204.182.161.2\",\t\t\t\t\t\t\tCVAR_NOSAVE, Net_Masterlist_Callback),\t\"unknown5\"},\n//\t{MP_QUAKEWORLD, CVARFC(\"net_qwmasterextraHistoric\",\t\"kubus.rulez.pl:27000\",\t\t\t\t\t\tCVAR_NOSAVE, Net_Masterlist_Callback),\t\"kubus.rulez.pl\"},\n//\t{MP_QUAKEWORLD, CVARFC(\"net_qwmasterextraHistoric\",\t\"telefrag.me:27000\",\t\t\t\t\t\tCVAR_NOSAVE, Net_Masterlist_Callback),\t\"telefrag.me\"},\n//\t{MP_QUAKEWORLD, CVARFC(\"net_qwmasterextraHistoric\",\t\"master.teamdamage.com:27000\",\t\t\t\tCVAR_NOSAVE, Net_Masterlist_Callback),\t\"master.teamdamage.com\"},\n\n\t//Total conversions will need to define their own in defaults.cfg or whatever.\n\t{MP_DPMASTER,\tCVARFC(\"net_masterextra1\",\t\t\"master.frag-net.com:27950\",\t\t\t\t\tCVAR_NOSAVE, Net_Masterlist_Callback)}, //admin: Eukara\n//\t{MP_DPMASTER,\tCVARFC(\"net_masterextra1\",\t\t\"\"/*\"ghdigital.com:27950\"*/,\t\t\t\t\tCVAR_NOSAVE, Net_Masterlist_Callback)}, //(was 69.59.212.88) admin: LordHavoc\n\t{MP_DPMASTER,\tCVARFC(\"net_masterextra2\",\t\t\"dpmaster.deathmask.net:27950\",\t\t\t\t\tCVAR_NOSAVE, Net_Masterlist_Callback)}, //admin: Willis\n\t{MP_DPMASTER,\tCVARFC(\"net_masterextra3\",\t\t\"dpmaster.tchr.no:27950\",\t\t\t\t\t\tCVAR_NOSAVE, Net_Masterlist_Callback)}, //admin: tChr\n#else\n\t{MP_DPMASTER,\tCVARFC(\"net_masterextra1\",\t\t\"\",\t\t\t\t\t\t\t\t\t\t\t\tCVAR_NOSAVE, Net_Masterlist_Callback)},\n\t{MP_DPMASTER,\tCVARFC(\"net_masterextra2\",\t\t\"\",\t\t\t\t\t\t\t\t\t\t\t\tCVAR_NOSAVE, Net_Masterlist_Callback)},\n\t{MP_DPMASTER,\tCVARFC(\"net_masterextra3\",\t\t\"\",\t\t\t\t\t\t\t\t\t\t\t\tCVAR_NOSAVE, Net_Masterlist_Callback)},\n#endif\n\t{MP_DPMASTER,\tCVARFC(\"net_masterextra4\",\t\t\"\",\t\t\t\t\t\t\t\t\t\t\t\tCVAR_NOSAVE, Net_Masterlist_Callback)},\n\t{MP_DPMASTER,\tCVARFC(\"net_masterextra5\",\t\t\"\",\t\t\t\t\t\t\t\t\t\t\t\tCVAR_NOSAVE, Net_Masterlist_Callback)},\n\t{MP_DPMASTER,\tCVARFC(\"net_masterextra6\",\t\t\"\",\t\t\t\t\t\t\t\t\t\t\t\tCVAR_NOSAVE, Net_Masterlist_Callback)},\n\t{MP_DPMASTER,\tCVARFC(\"net_masterextra7\",\t\t\"\",\t\t\t\t\t\t\t\t\t\t\t\tCVAR_NOSAVE, Net_Masterlist_Callback)},\n\t{MP_DPMASTER,\tCVARFC(\"net_masterextra8\",\t\t\"\",\t\t\t\t\t\t\t\t\t\t\t\tCVAR_NOSAVE, Net_Masterlist_Callback)},\n\n#ifdef Q2CLIENT\n//\t{MP_QUAKE2,\t\tCVARFC(\"net_q2masterextra1\",\t\"satan.idsoftware.com:27900\",\t\t\t\t\tCVAR_NOSAVE, Net_Masterlist_Callback),\t\"Official Quake2 master server\"},\n//\t{MP_QUAKE2,\t\tCVARFC(\"net_q2masterextra1\",\t\"master.planetgloom.com:27900\",\t\t\t\t\tCVAR_NOSAVE, Net_Masterlist_Callback)},\t//?\n\t{MP_QUAKE2,\t\tCVARFC(\"net_q2masterextra1\",\t\"netdome.biz:27900\",\t\t\t\t\t\t\tCVAR_NOSAVE, Net_Masterlist_Callback)},\t//?\n\t{MP_QUAKE2,\t\tCVARFC(\"net_q2masterextra2\",\t\"master.quakeservers.net:27900\",\t\t\t\tCVAR_NOSAVE, Net_Masterlist_Callback)},\t//?\n\t{MP_QUAKE2,\t\tCVARFC(\"net_q2masterextra3\",\t\"master.q2servers.com:27900\",\t\t\t\t\tCVAR_NOSAVE, Net_Masterlist_Callback)},\t//fucked. server doesn't give responses... but we DO want to send it heartbeats so we can at least get listed to eg q2rtx. maybe they'll fix their bugs.\n#endif\n\n#ifdef Q3CLIENT\n//\t{MP_QUAKE3,\t\tCVARFC(\"net_q3masterextra1\",\t\"masterserver.exhale.de:27950\",\t\t\t\t\tCVAR_NOSAVE, Net_Masterlist_Callback),\t\"Official Quake3 master server\"},\n\t{MP_QUAKE3,\t\tCVARFC(\"net_q3masterextra1\",\t\"master.quake3arena.com:27950\",\t\t\t\t\tCVAR_NOSAVE, Net_Masterlist_Callback),\t\"Official Quake3 master server\"},\n\t{MP_QUAKE3,\t\tCVARFC(\"net_q3masterextra2\",\t\"master0.excessiveplus.net:27950\",\t\t\t\tCVAR_NOSAVE, Net_Masterlist_Callback),\t\"DE: Excessive Plus\"},\n\t{MP_QUAKE3,\t\tCVARFC(\"net_q3masterextra3\",\t\"master.ioquake3.org:27950\",\t\t\t\t\tCVAR_NOSAVE, Net_Masterlist_Callback),\t\"DE: ioquake3\"},\n\t{MP_QUAKE3,\t\tCVARFC(\"net_q3masterextra4\",\t\"master.huxxer.de:27950\",\t\t\t\t\t\tCVAR_NOSAVE, Net_Masterlist_Callback),\t\"DE: BMA Team\"},\n\t{MP_QUAKE3,\t\tCVARFC(\"net_q3masterextra5\",\t\"master.maverickservers.com:27950\",\t\t\t\tCVAR_NOSAVE, Net_Masterlist_Callback),\t\"US: Maverickservers.com\"},\n\t{MP_QUAKE3,\t\tCVARFC(\"net_q3masterextra8\",\t\"master3.idsoftware.com:27950\",\t\t\t\t\tCVAR_NOSAVE, Net_Masterlist_Callback),\t\"US: id Software Quake III Master\"},\n#endif\n#endif\n#endif\n\n\t{MP_UNSPECIFIED, CVAR(NULL, NULL)}\n};\n#ifdef HAVE_SERVER\nqboolean SV_Master_AddressIsMaster(netadr_t *adr)\n{\n\tsize_t i, j;\n\t//never throttle packets from master servers. we don't want to go missing.\n\tfor (i = 0; net_masterlist[i].cv.name; i++)\n\t{\n\t\tif (!net_masterlist[i].protocol || net_masterlist[i].resolving || net_masterlist[i].needsresolve)\n\t\t\tcontinue;\n\t\tfor (j = 0; j < MAX_MASTER_ADDRESSES; j++)\n\t\t\tif (net_masterlist[i].adr[j].type != NA_INVALID)\n\t\t\t\tif (NET_CompareBaseAdr(&net_from, &net_masterlist[i].adr[j]))\n\t\t\t\t\treturn true;\n\t}\n\treturn false;\n}\nvoid SV_Master_HeartbeatResponse(netadr_t *adr, const char *challenge)\n{\n//ftemaster responds from two different ports. one direct, one indirect.\n//if there's a NAT/firewall issue then the indirect response is lost.\n//thus if we get more than X direct responses without any indirect ones in that time, start warning because the server is unreachable and thus probably pointless.\n\tqboolean directresponse = (*challenge=='?');\t//this is a fallback (direct) response.\n\tstatic size_t directresponse_count;\n\tstatic double okaytimestamp;\t//timer throttle, in case there's spoofing going on. we won't get any DoS attacks but its still annoying.\n\n\t//'?' denotes the master trying to send a direct response.\n\t//length>0&&length<12 denotes a dpmaster query\n\t//no challengedenotes a client (if they get our address then its all good)\n\tif (*challenge && strlen(challenge) <= 12)\n\t\treturn;\t//outdated dpmaster. these are (probably) direct responses that don't really mean anything.\n\n\tif (NET_ClassifyAddress(adr, NULL) != ASCOPE_NET)\n\t\treturn;\t//ignore any broadcast lan probes.\n\n\t//if we're getting fake-direct responses without typical indirect ones then someone's probably being obnoxious and trying to trigger some false positives.\n\t//FIXME: spoofed fake-direct responses can still be an annoyance, but this is informative only, so not a real issue.\n\tif (directresponse && !SV_Master_AddressIsMaster(adr))\n\t\tdirectresponse = false;\n\n\tif (directresponse && sv_public.ival == 1)\n\t\tdirectresponse_count++;\t\t//bad...\n\telse\n\t\tdirectresponse_count = 0;\t//yay! we're reachable!... for now...\n\n\tif (directresponse_count >= 4)\n\t{\n\t\tif ((realtime - okaytimestamp) > 60)\n\t\t{\n\t\t\tCon_Printf(CON_ERROR\"WARNING: 'sv_public %s' is ineffective, this server appears unreachable due to NAT/Firewall issues\\n\", sv_public.string);\n\t\t\tokaytimestamp = realtime;\n\t\t\tdirectresponse_count = 0;\n\t\t}\n\t}\n\telse\n\t\tokaytimestamp = realtime;\n}\n\nstatic void QDECL Net_Masterlist_Callback(struct cvar_s *var, char *oldvalue)\n{\n\tint i;\n\n\tfor (i = 0; net_masterlist[i].cv.name; i++)\n\t{\n\t\tif (var == &net_masterlist[i].cv)\n\t\t\tbreak;\n\t}\n\n\tif (!net_masterlist[i].cv.name)\n\t\treturn;\n\n\tnet_masterlist[i].needsresolve = true;\n}\n\nstatic void SV_Master_SingleHeartbeat(net_masterlist_t *master)\n{\n\tchar\t\tstring[2048];\n\tqboolean\tmadeqwstring = false;\n\tchar\t\tadr[MAX_ADR_SIZE];\n\tnetadr_t\t*na;\n\tint i;\n\tint e;\n\n\tfor (i = 0; i < MAX_MASTER_ADDRESSES; i++)\n\t{\n\t\tna = &master->adr[i];\n\t\tif (na->port)\n\t\t{\n\t\t\te = -1;\n\t\t\tswitch(master->protocol)\n\t\t\t{\n\t\t\tcase MP_QUAKEWORLD:\n\t\t\t\tif ((svs.gametype == GT_PROGS || svs.gametype == GT_Q1QVM\n#ifdef VM_LUA\n\t\t\t\t\t|| svs.gametype == GT_LUA\n#endif\n\t\t\t\t\t) && sv_listen_qw.value && na->type == NA_IP)\n\t\t\t\t{\n\t\t\t\t\tif (!madeqwstring)\n\t\t\t\t\t{\n\t\t\t\t\t\tint active, j;\n\n\t\t\t\t\t\t// count active users\n\t\t\t\t\t\tactive = 0;\n\t\t\t\t\t\tfor (j=0 ; j<svs.allocated_client_slots ; j++)\n\t\t\t\t\t\t\tif (svs.clients[j].state == cs_connected ||\n\t\t\t\t\t\t\tsvs.clients[j].state == cs_spawned )\n\t\t\t\t\t\t\t\tactive++;\n\n\t\t\t\t\t\tsprintf (string, \"%c\\n%i\\n%i\\n\", S2M_HEARTBEAT,\n\t\t\t\t\t\t\tsvs.heartbeat_sequence, active);\n\n\t\t\t\t\t\tmadeqwstring = true;\n\t\t\t\t\t}\n\n\t\t\t\t\te = NET_SendPacket (svs.sockets, strlen(string), string, na);\n\t\t\t\t}\n\t\t\t\tbreak;\n#ifdef Q2SERVER\n\t\t\tcase MP_QUAKE2:\n\t\t\t\tif (svs.gametype == GT_QUAKE2 && sv_listen_qw.value && na->type == NA_IP)\t//sv_listen==sv_listen_qw, yes, weird.\n\t\t\t\t{\n\t\t\t\t\tchar *str = \"\\377\\377\\377\\377\"\"heartbeat\\n%s\\n%s\";\n\t\t\t\t\tchar info[8192];\n\t\t\t\t\tchar q2users[8192];\n\t\t\t\t\tsize_t i;\n\t\t\t\t\tconst char *infos[] = {\"hostname\", \"*version\", \"deathmatch\", \"fraglimit\", \"timelimit\", \"gamedir\", \"mapname\", \"maxclients\", \"dmflags\", NULL};\n\t\t\t\t\tInfoBuf_ToString(&svs.info, info, sizeof(info), NULL, NULL, infos, NULL, NULL);\n\t\t\t\t\tq2users[0] = 0;\n\t\t\t\t\tfor (i = 0; i < sv.allocated_client_slots; i++)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (svs.clients[i].state >= cs_connected)\n\t\t\t\t\t\t\tQ_strncatz(q2users, va(\"%i %i \\\"%s\\\"\\n\", svs.clients[i].old_frags, SV_CalcPing(&svs.clients[i], false), svs.clients[i].name), sizeof(q2users));\n\t\t\t\t\t}\n\t\t\t\t\te = NET_SendPacket (svs.sockets, strlen(str), va(str, info, q2users), na);\n\t\t\t\t}\n\t\t\t\tbreak;\n#endif\n\t\t\tcase MP_DPMASTER:\n\t\t\t\t//fte normally uses quakeworld masters for clients that support qw protocols, and dpmaster for clients that support nq protocols.\n\t\t\t\t//unfortunately qwmasters don't support ipv6, and total conversions don't want to use qwmasters.\n\t\t\t\t//we default to FTE-Quake when running quake so at least that part is fair.\n\t\t\t\t//however, I made QSS also look for FTE-Quake servers too, so that's messy with listen_nq 0, but that's true if listen_dp is set.\n\t\t\t\t//so we want to be quite permissive here, at least with custom builds that will default to these cvars set to 0.\n\t\t\t\t//Note that Darkplaces clients are supposed to be able to use the qw protocol, so it should be okay to heartbeat as Darkplaces-Quake here even when not doing any nq protocols.\n\t\t\t\t//either way, custom protocols tend to require ftemaster/dpmaster so we want to heartbeat regardless.\n#if defined(NQPROT) && !defined(QUAKETC)\n//\t\t\t\tif (sv_listen_dp.value || sv_listen_nq.value || strcasecmp(com_protocolname.string, \"FTE-Quake\"))\n#endif\n\t\t\t\t{\n\t\t\t\t\t//darkplaces here refers to the master server protocol, rather than the game protocol\n\t\t\t\t\t//(specifies that the server responds to infoRequest packets from the master)\n\t\t\t\t\tchar *str = \"\\377\\377\\377\\377\"\"heartbeat DarkPlaces\\n\";\n\t\t\t\t\te = NET_SendPacket (svs.sockets, strlen(str), str, na);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\te = -2;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tswitch(e)\n\t\t\t{\n\t\t\tcase -1:\t//master not enabled for this game type\n\t\t\t\tbreak;\n\t\t\tcase NETERR_SENT:\n\t\t\t\tif (sv_reportheartbeats.value)\n\t\t\t\t{\n\t\t\t\t\tif (sv_reportheartbeats.ival != 2 || !master->announced)\n\t\t\t\t\t{\n\t\t\t\t\t\tCOM_Parse(master->cv.string);\n\t\t\t\t\t\tCon_TPrintf (S_COLOR_GRAY\"Sending heartbeat to %s (%s)\\n\", NET_AdrToString (adr, sizeof(adr), na), com_token);\n\t\t\t\t\t\tmaster->announced = true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase NETERR_NOROUTE:\n\t\t\t\tif (sv_reportheartbeats.value)\n\t\t\t\t{\n\t\t\t\t\tif (sv_reportheartbeats.ival != 2 || !master->announced)\n\t\t\t\t\t{\n\t\t\t\t\t\tCOM_Parse(master->cv.string);\n\t\t\t\t\t\tCon_TPrintf (CON_WARNING\"No route for heartbeat to %s (%s)\\n\", NET_AdrToString (adr, sizeof(adr), na), com_token);\n\t\t\t\t\t\tmaster->announced = true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\tcase NETERR_DISCONNECTED:\n\t\t\tcase NETERR_MTU:\n\t\t\tcase NETERR_CLOGGED:\n\t\t\t\tif (sv_reportheartbeats.value)\n\t\t\t\t{\n\t\t\t\t\tif (sv_reportheartbeats.ival != 2 || !master->announced)\n\t\t\t\t\t{\n\t\t\t\t\t\tCOM_Parse(master->cv.string);\n\t\t\t\t\t\tCon_TPrintf (CON_ERROR\"Failed to send heartbeat to %s (%s)\\n\", NET_AdrToString (adr, sizeof(adr), na), com_token);\n\t\t\t\t\t\tmaster->announced = true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n}\n\n//main thread\nstruct thr_res\n{\n\tqboolean success;\n\tnetadr_t na[MAX_MASTER_ADDRESSES];\n\tchar str[1];\t//trailing\n};\nstatic void SV_Master_Worker_Resolved(void *ctx, void *data, size_t a, size_t b)\n{\n\tchar adr[256];\n\tint i, j;\n\tstruct thr_res *work = data;\n\tnetadr_t *na;\n\tnet_masterlist_t *master = &net_masterlist[a];\n\n\tmaster->resolving = false;\n\n\t//only accept the result if the master wasn't changed while we were still resolving it. no race conditions please.\n\tif (!strcmp(master->cv.string, work->str))\n\t{\n\t\tmaster->needsresolve = false;\n\t\tfor (i = 0; i < MAX_MASTER_ADDRESSES; i++)\n\t\t{\n\t\t\tna = &master->adr[i];\n\t\t\t*na = work->na[i];\n\t\t\tmaster->needsresolve = false;\n\n\t\t\tswitch (master->protocol)\n\t\t\t{\n#ifdef Q2SERVER\n\t\t\tcase MP_QUAKE2:\n#endif\n\t\t\tcase MP_QUAKEWORLD:\n\t\t\t\t//these masters have no ipv6 results query, so don't sent ipv6 heartbeats\n\t\t\t\t//(its possible that a router will convert to ipv4, but such a router is probably natted and its not really worth it)\n\t\t\t\tif (na->type != NA_IP)\n\t\t\t\t\tna->type = NA_INVALID;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\t//these masters should do ipv4+ipv6, but not others.\n\t\t\t\tif (na->type != NA_IP && na->type != NA_IPV6)\n\t\t\t\t\tna->type = NA_INVALID;\n\t\t\t\tbreak;\t//protocol\n\t\t\t}\n\t\t\tif (na->type != NA_INVALID)\n\t\t\t{\n\t\t\t\tfor (j = 0; j < i; j++)\n\t\t\t\t{\n\t\t\t\t\tif (NET_CompareAdr(&master->adr[j], na))\n\t\t\t\t\t{\t//a dupe of a previous one...\n\t\t\t\t\t\tna->type = NA_INVALID;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (na->type == NA_INVALID)\n\t\t\t\tmemset(na, 0, sizeof(*na));\n\t\t\telse\n\t\t\t{\n\t\t\t\tCon_DPrintf (\"Resolved master \\\"%s\\\" to %s\\n\", master->cv.string, NET_AdrToString(adr, sizeof(adr), na));\n\t\t\t\t//fix up default ports if not specified\n\t\t\t\tif (!na->port)\n\t\t\t\t{\n\t\t\t\t\tsafeswitch (master->protocol)\n\t\t\t\t\t{\n\t\t\t\t\tcase MP_UNSPECIFIED:\n#ifdef NQPROT\n\t\t\t\t\tcase MP_NETQUAKE:\n#endif\n\t\t\t\t\tcase MP_DPMASTER:\tna->port = BigShort (27950);\tbreak;\n#if defined(Q2CLIENT) || defined(Q2SERVER)\n\t\t\t\t\tcase MP_QUAKE2:\t\tna->port = BigShort (27900);\tbreak;\t//FIXME: verify\n#endif\n#ifdef Q3SERVER\n\t\t\t\t\tcase MP_QUAKE3:\t\tna->port = BigShort (27950);\tbreak;\n#endif\n\t\t\t\t\tcase MP_QUAKEWORLD:\tna->port = BigShort (27000);\tbreak;\n\t\t\t\t\tsafedefault:\t\tna->port = BigShort (27950);\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t//some master servers require a ping to get them going or so\n\t\t\t\tif (sv.state)\n\t\t\t\t{\n\t\t\t\t\t//tcp masters require a route\n\t\t\t\t\tif (NET_AddrIsReliable(na))\n\t\t\t\t\t{\n\t\t\t\t\t\tstruct dtlspeercred_s cred = {master->cv.string};\n\t\t\t\t\t\tNET_EnsureRoute(svs.sockets, master->cv.name, &cred, NULL, na, true);\n\t\t\t\t\t}\n\n\t\t\t\t\t//q2+qw masters are given a ping to verify that they're still up\n\t\t\t\t\tswitch (master->protocol)\n\t\t\t\t\t{\n#ifdef Q2SERVER\n\t\t\t\t\tcase MP_QUAKE2:\n\t\t\t\t\t\tNET_SendPacket (svs.sockets, 8, \"\\xff\\xff\\xff\\xffping\", na);\n\t\t\t\t\t\tbreak;\n#endif\n\t\t\t\t\tcase MP_QUAKEWORLD:\n\t\t\t\t\t\t//qw does this for some reason, keep the behaviour even though its unreliable thus pointless\n\t\t\t\t\t\tNET_SendPacket (svs.sockets, 2, \"k\\0\", na);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (!work->success)\n\t\t\tCon_TPrintf (\"Couldn't resolve master \\\"%s\\\"\\n\", master->cv.string);\n\t\telse\n\t\t\tSV_Master_SingleHeartbeat(master);\n\t}\n\tZ_Free(work);\n}\n\n#if defined(SUPPORT_ICE)\nstruct stunheader_s\n{\n\tunsigned short msgtype;\n\tunsigned short msglen;\n\tunsigned int magiccookie;\n\tunsigned int transactid[3];\n};\nstatic void SV_Master_Worker_Resolved_Broker(void *ctx, void *data, size_t a, size_t b)\n{\n\tstruct thr_res *work = data;\n\tif (svs.sockets && work->na[0].type != NA_INVALID)\t//something resolved...\n\t{\n\t\tstruct stunheader_s msg = {htons(1), htons(sizeof(msg)-20), BigLong(0x2112a442), {42,42,42}};\n\n\t\t//randomize the transaction id to avoid poisoning.\n\t\tif (!Sys_RandomBytes((qbyte*)msg.transactid, sizeof(msg.transactid)))\n\t\t{ \t//FIXME: not really random enough to avoid hacks. oh well.\n\t\t\tmsg.transactid[0] = rand();\n\t\t\tmsg.transactid[1] = rand();\n\t\t\tmsg.transactid[2] = rand();\n\t\t}\n\n\t\tsvs.sockets->srflx_tid[0] = msg.transactid[0];\n\t\tsvs.sockets->srflx_tid[1] = msg.transactid[1];\n\t\tsvs.sockets->srflx_tid[2] = msg.transactid[2];\n\n\t\tNET_SendPacket(svs.sockets, sizeof(msg), &msg, &work->na[0]);\n\t}\n\tZ_Free(work);\n}\n#endif\n//worker thread\nstatic void SV_Master_Worker_Resolve(void *ctx, void *data, size_t a, size_t b)\n{\n\tchar token[1024];\n\tint found = 0;\n\tqboolean first = true;\n\tchar *str;\n\tstruct thr_res *work = data;\n\tstr = work->str;\n\twhile (str && *str)\n\t{\n\t\tstr = COM_ParseOut(str, token, sizeof(token));\n\t\tif (*token)\n\t\t\tfound += NET_StringToAdr2(token, 0, &work->na[found], countof(work->na)-found, NULL);\n\t\tif (first && found)\n\t\t\tbreak;\t//if we found one by name, don't try any fallback ip addresses.\n\t\tfirst = false;\n\t}\n\twork->success = !!found;\n\n#if defined(SUPPORT_ICE)\n\tif (a==~(size_t)0)\n\t\tCOM_AddWork(WG_MAIN, SV_Master_Worker_Resolved_Broker, NULL, work, a, b);\n\telse\n#endif\n\t\tCOM_AddWork(WG_MAIN, SV_Master_Worker_Resolved, NULL, work, a, b);\n}\n\n/*\n================\nMaster_Heartbeat\n\nSend a message to the master every few minutes to\nlet it know we are alive, and log information\n================\n*/\nvoid SV_Master_Heartbeat (void)\n{\n\tint\t\t\ti;\n\tint interval = bound(85, sv_heartbeat_interval.ival, 600);\n\n\tif (sv_public.ival<=0 || SSV_IsSubServer())\n\t\treturn;\n#ifdef SUPPORT_ICE\n\tif (sv_public.ival == 2)\n\t\treturn;\t//using our broker service. we're configured as behind a nat so these addresses won't work anyway.\n#endif\n\n\tif (realtime-interval - svs.last_heartbeat < interval)\n\t\treturn;\t\t// not time to send yet\n\n\tif ((sv.allocated_client_slots == 1) && !isDedicated)\n\t\treturn;\t\t//don't heartbeat in single-player, we don't even have a public socket open!\n\n\tsvs.last_heartbeat = realtime-interval;\n\n\tsvs.heartbeat_sequence++;\n\n\tMaster_DetermineMasterTypes();\n\n\t// send to group master\n\tfor (i = 0; net_masterlist[i].cv.name; i++)\n\t{\n\t\tif (!Master_MasterProtocolIsEnabled(net_masterlist[i].protocol))\n\t\t\tcontinue;\n\n\t\tif (net_masterlist[i].resolving)\n\t\t\tcontinue;\n\n\t\tif (net_masterlist[i].needsresolve)\n\t\t{\n\t\t\tif (!*net_masterlist[i].cv.string || *net_masterlist[i].cv.string == '*')\n\t\t\t\tmemset(net_masterlist[i].adr, 0, sizeof(net_masterlist[i].adr));\n\t\t\telse\n\t\t\t{\n\t\t\t\tstruct thr_res *work = Z_Malloc(sizeof(*work) + strlen(net_masterlist[i].cv.string));\n\t\t\t\tstrcpy(work->str, net_masterlist[i].cv.string);\n\t\t\t\tnet_masterlist[i].resolving = true;\t//don't spam work\n\t\t\t\tCOM_AddWork(WG_MAIN, SV_Master_Worker_Resolve, NULL, work, i, 0);\n\t\t\t}\n\t\t}\n\t\telse\n\t\t\tSV_Master_SingleHeartbeat(&net_masterlist[i]);\n\t}\n\n#if defined(SUPPORT_ICE)\n\tif (*net_ice_broker.string)\n\t{\n\t\tconst char *s = net_ice_broker.string;\n\t\tstruct thr_res *work = Z_Malloc(sizeof(*work) + strlen(s));\n\t\tif (!strncmp(s, \"tls://\", 6) || !strncmp(s, \"tcp://\", 6) || !strncmp(s, \"wss://\", 6))\n\t\t\ts+=6;\t//ignore weird prefixes here\n\t\telse if (!strncmp(s, \"ws://\", 5))\n\t\t\ts+=5;\t//ignore dumb prefixes here\n\t\tstrcpy(work->str, s);\n\t\tCOM_AddWork(WG_MAIN, SV_Master_Worker_Resolve, NULL, work, ~(size_t)0, 0);\n\t}\n#endif\n}\n\n#ifdef HAVE_LEGACY\nstatic void SV_Master_Add(int type, char *stringadr)\n{\n\tint i;\n\n\t//don't do dupes...\n\tfor (i = 0; net_masterlist[i].cv.name; i++)\n\t{\n\t\tif (net_masterlist[i].protocol == type)\n\t\t\tif (!strcmp(net_masterlist[i].cv.string, stringadr))\n\t\t\t{\n\t\t\t\tsvs.last_heartbeat = -99999;\n\t\t\t\treturn;\n\t\t\t}\n\t}\n\n\tfor (i = 0; net_masterlist[i].cv.name; i++)\n\t{\n\t\tif (net_masterlist[i].protocol != type)\n\t\t\tcontinue;\n\t\tif (net_masterlist[i].cv.flags & CVAR_NOSAVE)\n\t\t\tcontinue;\t//ignore our extras\n\t\tif (!*net_masterlist[i].cv.string)\n\t\t\tbreak;\n\t}\n\n\tif (!net_masterlist[i].cv.name)\n\t{\n\t\tCon_Printf (\"Too many masters\\n\");\n\t\treturn;\n\t}\n\n\tCvar_Set(&net_masterlist[i].cv, stringadr);\n\tCon_Printf(CON_WARNING\"setting %s to \\\"%s\\\"\\n\", net_masterlist[i].cv.name, stringadr);\n\n\tsvs.last_heartbeat = -99999;\n}\n\nstatic void SV_Master_ClearType(int type)\n{\n\tint i;\n\tfor (i = 0; net_masterlist[i].cv.name; i++)\n\t{\n\t\tif (net_masterlist[i].protocol == type)\n\t\t{\n\t\t\tif (net_masterlist[i].cv.flags & CVAR_NOSAVE)\n\t\t\t\tcontinue;\t//ignore our extras\n\t\t\tif (*net_masterlist[i].cv.string)\n\t\t\t\tCon_Printf(CON_WARNING\"clearing %s (was \\\"%s\\\")\\n\", net_masterlist[i].cv.name, net_masterlist[i].cv.string);\n\t\t\tCvar_Set(&net_masterlist[i].cv, \"\");\n\t\t}\n\t}\n}\nstatic void SV_Master_ClearAll(void)\n{\n\tint i;\n\tfor (i = 0; net_masterlist[i].cv.name; i++)\n\t{\n\t\tCvar_Set(&net_masterlist[i].cv, \"\");\n\t}\n}\n\n/*\n====================\nSV_SetMaster_f\n\nMake a master server current. deprecated in favour of setting numbered masters via configs/engine source code.\nonly supports qw masters.\n====================\n*/\nstatic void SV_SetMaster_f (void)\n{\n\tint\t\ti;\n\n\tif (!strcmp(Cmd_Argv(1), \"none\"))\n\t{\n\t\tCvar_Set(&sv_public, \"0\");\t//go private.\n\n\t\tSV_Master_ClearAll();\n\t\tif (cl_warncmd.ival)\n\t\t\tCon_Printf (\"Entering no-master mode\\n\");\n\t\treturn;\n\t}\n\tif (!strcmp(Cmd_Argv(1), \"clear\"))\n\t{\n\t\tSV_Master_ClearType(MP_QUAKEWORLD);\n\t\treturn;\n\t}\n\n\tif (sv_public.ival < 1)\n\t\tCon_Printf(CON_WARNING\"%s used on private server (sv_public is \\\"%s\\\")\\n\", Cmd_Argv(0), sv_public.string);\n\tif (!strcmp(Cmd_Argv(1), \"default\"))\n\t{\n\t\tfor (i = 0; net_masterlist[i].cv.name; i++)\n\t\t\tCvar_Set(&net_masterlist[i].cv, net_masterlist[i].cv.enginevalue);\n\t\treturn;\n\t}\n\n\ti = 1;\n\tif (!strcmp(Cmd_Argv(1), \"add\"))\n\t\ti++;\n\telse\n\t\tSV_Master_ClearType(MP_QUAKEWORLD);\n\tfor ( ; i<Cmd_Argc() ; i++)\n\t{\n\t\tSV_Master_Add(MP_QUAKEWORLD, Cmd_Argv(i));\n\t}\n\n\tsvs.last_heartbeat = -99999;\n}\n#endif\n\nvoid SV_Master_ReResolve(void)\n{\n\tint i;\n\tfor (i = 0; net_masterlist[i].cv.name; i++)\n\t{\n\t\tnet_masterlist[i].needsresolve = true;\n\t}\n\t//trigger a heartbeat at the next available opportunity.\n\tsvs.last_heartbeat = -9999;\n}\n\n/*\n=================\nMaster_Shutdown\n\nInforms all masters that this server is going down\n=================\n*/\nvoid SV_Master_Shutdown (void)\n{\n\tchar\t\tstring[2048];\n\tchar\t\tadr[MAX_ADR_SIZE];\n\tint\t\t\ti, j;\n\tnetadr_t\t*na;\n\n\t//note that if a master server actually blindly listens to this then its exploitable.\n\t//we send it out anyway as for us its all good.\n\t//master servers ought to try and check up on the status of the server first, if they listen to this.\n\n\tsprintf (string, \"%c\\n\", S2M_SHUTDOWN);\n\n\t// send to group master\n\tfor (i = 0; net_masterlist[i].cv.name; i++)\n\t{\n\t\tfor (j = 0; j < MAX_MASTER_ADDRESSES; j++)\n\t\t{\n\t\t\tna = &net_masterlist[i].adr[j];\n\t\t\tif (na->port)\n\t\t\t{\n\t\t\t\tswitch(net_masterlist[i].protocol)\n\t\t\t\t{\n\t\t\t\tcase MP_QUAKEWORLD:\n\t\t\t\t\tif (sv_reportheartbeats.value)\n\t\t\t\t\t\tCon_TPrintf (\"Sending shutdown to %s\\n\", NET_AdrToString (adr, sizeof(adr), na));\n\n\t\t\t\t\tNET_SendPacket (svs.sockets, strlen(string), string, na);\n\t\t\t\t\tbreak;\n\t\t\t\t//dp has no shutdown\n\t\t\t\tdefault:\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n#endif\n\n\n\n\n#if defined(CL_MASTER) && !defined(SERVERONLY)\n\n#define NET_GAMENAME_NQ\t\t\"QUAKE\"\n\n//rename to cl_master.c sometime\n\n//the networking operates seperatly from the main code. This is so we can have full control over all parts of the server sending prints.\n//when we send status to the server, it replys with a print command. The text to print contains the serverinfo.\n//Q2's print command is a compleate 'print', while qw is just a 'p', thus we can distinguish the two easily.\n\n//save favorites and allow addition of new ones from game?\n//add filters some time\n\n//remove dead servers.\n//master was polled a minute ago and server was not on list - server on multiple masters would be awkward.\n\n#include \"netinc.h\"\n\n//the number of servers should be limited only by memory.\n\ncvar_t slist_cacheinfo = CVAR(\"slist_cacheinfo\", \"0\");\t//this proves dangerous, memory wise.\ncvar_t slist_writeserverstxt = CVAR(\"slist_writeservers\", \"1\");\n\nstatic void MasterInfo_RemoveAllPlayers(void);\n\nmaster_t *master;\nplayer_t *mplayers;\nserverinfo_t *firstserver;\nstruct selectedserver_s selectedserver;\n\nstatic serverinfo_t **visibleservers;\nstatic int numvisibleservers;\nstatic int maxvisibleservers;\n\nstatic hostcachekey_t sortfield;\nstatic qboolean sort_decreasing;\nstatic qboolean sort_favourites;\nstatic qboolean sort_categories;\nstatic serverinfo_t *categorisingserver;\t//returned for sorted server -1 (hacky)\n\n\n\n\ntypedef struct {\n\thostcachekey_t fieldindex;\n\n\tfloat operandi;\n\tconst char *operands;\n\n\tqboolean or;\n\tint compareop;\n} visrules_t;\n#define MAX_VISRULES 8\nvisrules_t visrules[MAX_VISRULES];\nint numvisrules;\n\n\n\n\n#define SLIST_MAXKEYS 64\nchar slist_keyname[SLIST_MAXKEYS][MAX_INFO_KEY];\nint slist_customkeys;\n\n\n#ifndef INVALID_SOCKET\n#define INVALID_SOCKET -1\n#endif\n\n#ifdef HAVE_IPV4\n#define POLLUDP4SOCKETS 64\t//it's big so we can have lots of messages when behind a firewall. Basically if a firewall only allows replys, and only remembers 3 servers per socket, we need this big cos it can take a while for a packet to find a fast optimised route and we might be waiting for a few secs for a reply the first time around.\nint lastpollsockUDP4;\n#else\n#define POLLUDP4SOCKETS 0\n#endif\n\n#ifdef HAVE_IPV6\n#define POLLUDP6SOCKETS 4\t//it's non-zero so we can have lots of messages when behind a firewall. Basically if a firewall only allows replys, and only remembers 3 servers per socket, we need this big cos it can take a while for a packet to find a fast optimised route and we might be waiting for a few secs for a reply the first time around.\nint lastpollsockUDP6;\n#else\n#define POLLUDP6SOCKETS 0\n#endif\n\n#ifdef HAVE_IPX\n#define POLLIPXSOCKETS\t2\t//ipx isn't used as much. In fact, we only expect local servers to be using it. I'm not sure why I implemented it anyway. You might see a q2 server using it. Rarely.\nint lastpollsockIPX;\n#else\n#define POLLIPXSOCKETS 0\n#endif\n\n#define FIRSTIPXSOCKET (0)\n#define FIRSTUDP4SOCKET (FIRSTIPXSOCKET+POLLIPXSOCKETS)\n#define FIRSTUDP6SOCKET (FIRSTUDP4SOCKET+POLLUDP4SOCKETS)\n#define POLLTOTALSOCKETS (FIRSTUDP6SOCKET+POLLUDP6SOCKETS)\n#if POLLTOTALSOCKETS>0\nSOCKET pollsocketsList[POLLTOTALSOCKETS];\nchar pollsocketsBCast[POLLTOTALSOCKETS];\n\nvoid Master_SetupSockets(void)\n{\n\tint i;\n\tfor (i = 0; i < POLLTOTALSOCKETS; i++)\n\t\tpollsocketsList[i] = INVALID_SOCKET;\n}\n\nstatic void CL_MasterListParse(netadrtype_t adrtype, int type, qboolean slashpad);\nstatic int CL_ReadServerInfo(char *msg, enum masterprotocol_e prototype, qboolean favorite);\nstatic void CL_ReadPingList(void);\n#else\nvoid Master_SetupSockets(void)\n{\n}\nvoid Master_CheckPollSockets(void)\n{\n}\n#endif\n\n\nunsigned int Master_TotalCount(void)\n{\n\tunsigned int count=0;\n\tserverinfo_t *info;\n\n\tfor (info = firstserver; info; info = info->next)\n\t{\n\t\tcount++;\n\t}\n\treturn count;\n}\n\nunsigned int Master_NumPolled(void)\n{\n\tunsigned int count=0;\n\tserverinfo_t *info;\n\n\tfor (info = firstserver; info; info = info->next)\n\t{\n\t\tif (!info->sends)\n\t\t\tcount++;\n\t}\n\treturn count;\n}\nunsigned int Master_NumAlive(void)\n{\n\tunsigned int count=0;\n\tserverinfo_t *info;\n\n\tfor (info = firstserver; info; info = info->next)\n\t{\n\t\tif (info->status&SRVSTATUS_ALIVE)\n\t\t\tcount++;\n\t}\n\treturn count;\n}\n\n//true if server is on a different master's list.\nserverinfo_t *Master_InfoForServer (netadr_t *addr, const char *brokerid)\n{\n\tserverinfo_t *info;\n\tif (!brokerid)\n\t\tbrokerid=\"\";\n\n\tfor (info = firstserver; info; info = info->next)\n\t{\n\t\tif (!strcmp(info->brokerid, brokerid) && NET_CompareAdr(&info->adr, addr))\n\t\t\treturn info;\n\t}\n\treturn NULL;\n}\nserverinfo_t *Master_InfoForNum (int num)\n{\n\tserverinfo_t *info;\n\n\tfor (info = firstserver; info; info = info->next)\n\t{\n\t\tif (num-- <=0)\n\t\t\treturn info;\n\t}\n\treturn NULL;\n}\n\nstatic void Master_HideServer(serverinfo_t *server)\n{\n\tint i, j;\n\tfor (i = 0; i < numvisibleservers;)\n\t{\n\t\tif (visibleservers[i] == server)\n\t\t{\n\t\t\tfor (j = i; j < numvisibleservers-1; j++)\n\t\t\t\tvisibleservers[j] = visibleservers[j+1];\n\t\t\tnumvisibleservers--;\n\t\t}\n\t\telse\n\t\t\t i++;\n\t}\n\tserver->status &= ~SRVSTATUS_DISPLAYED;\n}\n\nstatic void Master_InsertAt(serverinfo_t *server, int pos)\n{\n\tint i;\n\tif (numvisibleservers >= maxvisibleservers)\n\t{\n\t\tmaxvisibleservers = maxvisibleservers+10;\n\t\tvisibleservers = BZ_Realloc(visibleservers, maxvisibleservers*sizeof(serverinfo_t*));\n\t}\n\tfor (i = numvisibleservers; i > pos; i--)\n\t{\n\t\tvisibleservers[i] = visibleservers[i-1];\n\t}\n\tvisibleservers[pos] = server;\n\tnumvisibleservers++;\n\n\tserver->status |= SRVSTATUS_DISPLAYED;\n}\n\nstatic qboolean Master_CompareInteger(int a, int b, slist_test_t rule)\n{\n\tswitch(rule)\n\t{\n\tcase SLIST_TEST_CONTAINS:\n\t\treturn !!(a&b);\n\tcase SLIST_TEST_NOTCONTAIN:\n\t\treturn !(a&b);\n\tcase SLIST_TEST_LESSEQUAL:\n\t\treturn a<=b;\n\tcase SLIST_TEST_LESS:\n\t\treturn a<b;\n\tcase SLIST_TEST_STARTSWITH:\n\tcase SLIST_TEST_EQUAL:\n\t\treturn a==b;\n\tcase SLIST_TEST_GREATER:\n\t\treturn a>b;\n\tcase SLIST_TEST_GREATEREQUAL:\n\t\treturn a>=b;\n\tcase SLIST_TEST_NOTSTARTSWITH:\n\tcase SLIST_TEST_NOTEQUAL:\n\t\treturn a!=b;\n\t}\n\treturn false;\n}\nstatic qboolean Master_CompareString(const char *a, const char *b, slist_test_t rule)\n{\n\tswitch(rule)\n\t{\n\tcase SLIST_TEST_STARTSWITH:\n\t\treturn Q_strncasecmp(a, b, strlen(b))==0;\n\tcase SLIST_TEST_NOTSTARTSWITH:\n\t\treturn Q_strncasecmp(a, b, strlen(b))!=0;\n\tcase SLIST_TEST_CONTAINS:\n\t\treturn !!Q_strcasestr(a, b);\n\tcase SLIST_TEST_NOTCONTAIN:\n\t\treturn !Q_strcasestr(a, b);\n\tcase SLIST_TEST_LESSEQUAL:\n\t\treturn Q_strcasecmp(a, b)<=0;\n\tcase SLIST_TEST_LESS:\n\t\treturn Q_strcasecmp(a, b)<0;\n\tcase SLIST_TEST_EQUAL:\n\t\treturn Q_strcasecmp(a, b)==0;\n\tcase SLIST_TEST_GREATER:\n\t\treturn Q_strcasecmp(a, b)>0;\n\tcase SLIST_TEST_GREATEREQUAL:\n\t\treturn Q_strcasecmp(a, b)>=0;\n\tcase SLIST_TEST_NOTEQUAL:\n\t\treturn Q_strcasecmp(a, b)!=0;\n\t}\n\treturn false;\n}\n\nchar\t*Master_ServerToString (char *s, int len, serverinfo_t *a)\n{\n\tif (*a->brokerid)\n\t{\n\t\tif (a->adr.type==NA_INVALID)\n\t\t\t*s = 0;\t//default broker... skip it for brevity.\n\t\telse\n\t\t\tNET_AdrToString(s, len, &a->adr);\n//\t\tQ_strncatz(s, \"/\", len);\n\t\tQ_strncatz(s, a->brokerid, len);\n\t\treturn s;\n\t}\n\treturn NET_AdrToString(s, len, &a->adr);\n}\n\nstatic int Master_BaseGame(serverinfo_t *a)\n{\n\tint prot = a->special&SS_PROTOCOLMASK;\n\treturn prot;\n}\n\nstatic qboolean Master_ServerIsGreater(serverinfo_t *a, serverinfo_t *b)\n{\n\tif (sort_categories)\n\t\tif (a->qccategory != b->qccategory)\n\t\t\treturn Master_CompareInteger(a->qccategory, b->qccategory, SLIST_TEST_LESS);\n\n\tif (sort_favourites)\n\t\tif ((a->special & SS_FAVORITE) != (b->special & SS_FAVORITE))\n\t\t\treturn Master_CompareInteger(a->special & SS_FAVORITE, b->special & SS_FAVORITE, SLIST_TEST_LESS);\n\n\tswitch(sortfield)\n\t{\n\tcase SLKEY_ADDRESS:\n\t\tif (a->adr.type != b->adr.type)\n\t\t\treturn a->adr.type < b->adr.type;\n\t\tif (a->adr.type == NA_IP)\n\t\t{\n\t\t\tint i;\n\t\t\tfor (i = 0; i < 4; i++)\n\t\t\t{\n\t\t\t\tif (a->adr.address.ip[i] != b->adr.address.ip[i])\n\t\t\t\t\treturn a->adr.address.ip[i] < b->adr.address.ip[i];\n\t\t\t}\n\t\t}\n\t\tif (a->adr.type == NA_IPV6)\n\t\t{\n\t\t\tint i;\n\t\t\tfor (i = 0; i < 16; i++)\n\t\t\t{\n\t\t\t\tif (a->adr.address.ip6[i] != b->adr.address.ip6[i])\n\t\t\t\t\treturn a->adr.address.ip6[i] < b->adr.address.ip6[i];\n\t\t\t}\n\t\t}\n\t\treturn false;\n\n\tcase SLKEY_BASEGAME:\n\t\treturn Master_CompareInteger(Master_BaseGame(a), Master_BaseGame(b), SLIST_TEST_LESS);\n\tcase SLKEY_FLAGS:\n\t\treturn Master_CompareInteger(a->special&~SS_PROTOCOLMASK, b->special&~SS_PROTOCOLMASK, SLIST_TEST_LESS);\n\tcase SLKEY_CUSTOM:\n\t\tbreak;\n\tcase SLKEY_FRAGLIMIT:\n\t\treturn Master_CompareInteger(a->fl, b->fl, SLIST_TEST_LESS);\n\tcase SLKEY_FREEPLAYERS:\n\t\treturn Master_CompareInteger(a->maxplayers - a->players, b->maxplayers - b->players, SLIST_TEST_LESS);\n\tcase SLKEY_GAMEDIR:\n\t\treturn Master_CompareString(a->gamedir, b->gamedir, SLIST_TEST_LESS);\n\tcase SLKEY_MAP:\n\t\treturn Master_CompareString(a->map, b->map, SLIST_TEST_LESS);\n\tcase SLKEY_MAXPLAYERS:\n\t\treturn Master_CompareInteger(a->maxplayers, b->maxplayers, SLIST_TEST_LESS);\n\tcase SLKEY_NAME:\n\t\treturn Master_CompareString(a->name, b->name, SLIST_TEST_LESS);\n\tcase SLKEY_NUMPLAYERS:\n\t\treturn Master_CompareInteger(a->players, b->players, SLIST_TEST_LESS);\n\tcase SLKEY_NUMHUMANS:\n\t\treturn Master_CompareInteger(a->numhumans, b->numhumans, SLIST_TEST_LESS);\n\tcase SLKEY_NUMSPECTATORS:\n\t\treturn Master_CompareInteger(a->numspectators, b->numspectators, SLIST_TEST_LESS);\n\tcase SLKEY_NUMBOTS:\n\t\treturn Master_CompareInteger(a->numbots, b->numbots, SLIST_TEST_LESS);\n\tcase SLKEY_PING:\n\t\treturn Master_CompareInteger(a->ping, b->ping, SLIST_TEST_LESS);\n\tcase SLKEY_TIMELIMIT:\n\t\treturn Master_CompareInteger(a->tl, b->tl, SLIST_TEST_LESS);\n\tcase SLKEY_TOOMANY:\n\t\tbreak;\n\n\tcase SLKEY_ISPROXY:\n\t\treturn Master_CompareInteger(a->special & SS_PROXY, b->special & SS_PROXY, SLIST_TEST_LESS);\n\tcase SLKEY_ISLOCAL:\n\t\treturn Master_CompareInteger(a->special & SS_LOCAL, b->special & SS_LOCAL, SLIST_TEST_LESS);\n\tcase SLKEY_ISFAVORITE:\n\t\treturn Master_CompareInteger(a->special & SS_FAVORITE, b->special & SS_FAVORITE, SLIST_TEST_LESS);\n\n\tcase SLKEY_CATEGORY:\n\t\treturn Master_CompareInteger(a->qccategory, b->qccategory, SLIST_TEST_LESS);\n\n\tcase SLKEY_MOD:\n\tcase SLKEY_PROTOCOL:\n\tcase SLKEY_QCSTATUS:\n\tcase SLKEY_SERVERINFO:\n\tcase SLKEY_PLAYER0:\n\tdefault:\n\t\tbreak;\n\n\t}\n\treturn false;\n}\n\nqboolean Master_PassesMasks(serverinfo_t *a)\n{\n\tint i;\n\tqboolean val, res;\n//\tqboolean enabled;\n\n\t//always filter out dead/unresponsive servers.\n\tif (!(a->status & SRVSTATUS_ALIVE))\n\t\treturn false;\n\n\tval = 1;\n\n\tfor (i = 0; i < numvisrules; i++)\n\t{\n\t\tsafeswitch(visrules[i].fieldindex)\n\t\t{\n\t\tcase SLKEY_PING:\n\t\t\tres = Master_CompareInteger(a->ping, visrules[i].operandi, visrules[i].compareop);\n\t\t\tbreak;\n\t\tcase SLKEY_NUMPLAYERS:\n\t\t\tres = Master_CompareInteger(a->players, visrules[i].operandi, visrules[i].compareop);\n\t\t\tbreak;\n\t\tcase SLKEY_MAXPLAYERS:\n\t\t\tres = Master_CompareInteger(a->maxplayers, visrules[i].operandi, visrules[i].compareop);\n\t\t\tbreak;\n\t\tcase SLKEY_FREEPLAYERS:\n\t\t\tres = Master_CompareInteger(a->freeslots, visrules[i].operandi, visrules[i].compareop);\n\t\t\tbreak;\n\t\tcase SLKEY_NUMBOTS:\n\t\t\tres = Master_CompareInteger(a->numbots, visrules[i].operandi, visrules[i].compareop);\n\t\t\tbreak;\n\t\tcase SLKEY_NUMHUMANS:\n\t\t\tres = Master_CompareInteger(a->numhumans, visrules[i].operandi, visrules[i].compareop);\n\t\t\tbreak;\n\t\tcase SLKEY_NUMSPECTATORS:\n\t\t\tres = Master_CompareInteger(a->numspectators, visrules[i].operandi, visrules[i].compareop);\n\t\t\tbreak;\n\t\tcase SLKEY_TIMELIMIT:\n\t\t\tres = Master_CompareInteger(a->tl, visrules[i].operandi, visrules[i].compareop);\n\t\t\tbreak;\n\t\tcase SLKEY_FRAGLIMIT:\n\t\t\tres = Master_CompareInteger(a->fl, visrules[i].operandi, visrules[i].compareop);\n\t\t\tbreak;\n\t\tcase SLKEY_PROTOCOL:\n\t\t\tres = Master_CompareInteger(a->fl, visrules[i].operandi, visrules[i].compareop);\n\t\t\tbreak;\n\n\t\tcase SLKEY_MAP:\n\t\t\tres = Master_CompareString(a->map, visrules[i].operands, visrules[i].compareop);\n\t\t\tbreak;\n\t\tcase SLKEY_NAME:\n\t\t\tres = Master_CompareString(a->name, visrules[i].operands, visrules[i].compareop);\n\t\t\tbreak;\n\t\tcase SLKEY_GAMEDIR:\n\t\t\tres = Master_CompareString(a->gamedir, visrules[i].operands, visrules[i].compareop);\n\t\t\tbreak;\n\n\t\tcase SLKEY_BASEGAME:\n\t\t\tres = Master_CompareInteger(Master_BaseGame(a), visrules[i].operandi, visrules[i].compareop);\n\t\t\tbreak;\n\t\tcase SLKEY_FLAGS:\n\t\t\tres = Master_CompareInteger(a->special&~SS_PROTOCOLMASK, visrules[i].operandi, visrules[i].compareop);\n\t\t\tbreak;\n\t\tcase SLKEY_MOD:\n\t\t\tres = Master_CompareString(a->modname, visrules[i].operands, visrules[i].compareop);\n\t\t\tbreak;\n\t\tcase SLKEY_QCSTATUS:\n\t\t\tres = Master_CompareString(a->qcstatus, visrules[i].operands, visrules[i].compareop);\n\t\t\tbreak;\n\t\tcase SLKEY_CATEGORY:\n\t\t\tres = Master_CompareInteger(a->qccategory, visrules[i].operandi, visrules[i].compareop);\n\t\t\tbreak;\n\t\tcase SLKEY_ISFAVORITE:\n\t\t\tres = Master_CompareInteger(a->special&SS_FAVORITE, visrules[i].operandi, visrules[i].compareop);\n\t\t\tbreak;\n\t\tcase SLKEY_ISLOCAL:\n\t\t\tres = Master_CompareInteger(a->special&SS_LOCAL, visrules[i].operandi, visrules[i].compareop);\n\t\t\tbreak;\n\t\tcase SLKEY_ISPROXY:\n\t\t\tres = Master_CompareInteger(a->special&SS_PROXY, visrules[i].operandi, visrules[i].compareop);\n\t\t\tbreak;\n\t\tcase SLKEY_ADDRESS:\t\t//not convinced its useful, but hey...\n\t\tcase SLKEY_SERVERINFO:\t//not really the sort of thing you sort.\n\t\tcase SLKEY_TOOMANY:\t\t//not real\n\t\tcase SLKEY_PLAYER0://...SLKEY_CUSTOM\n\t\tcase SLKEY_CUSTOM:\n\t\tsafedefault:\n\t\t\tres = Master_CompareString(Master_ReadKeyString(a, visrules[i].fieldindex), visrules[i].operands, visrules[i].compareop);\n\t\t\tbreak;\n\t\t}\n\t\tif (visrules[i].or)\n\t\t\tval |= res;\n\t\telse\n\t\t\tval &= res;\n\t}\n\n\treturn val;\n}\n\nvoid Master_ClearMasks(void)\n{\n\tnumvisrules = 0;\n}\n\nvoid Master_SetMaskString(qboolean or, hostcachekey_t field, const char *param, slist_test_t testop)\n{\n\tif (numvisrules == MAX_VISRULES)\n\t\treturn;\t//just don't add it.\n\n\tvisrules[numvisrules].fieldindex = field;\n\tvisrules[numvisrules].compareop = testop;\n\tvisrules[numvisrules].operands = param;\n\tvisrules[numvisrules].operandi = atof(param);\n\tvisrules[numvisrules].or = or;\n\tnumvisrules++;\n}\nvoid Master_SetMaskInteger(qboolean or, hostcachekey_t field, int param, slist_test_t testop)\n{\n\tif (numvisrules == MAX_VISRULES)\n\t\treturn;\t//just don't add it.\n\n\tvisrules[numvisrules].fieldindex = field;\n\tvisrules[numvisrules].compareop = testop;\n\tvisrules[numvisrules].operandi = param;\n\tvisrules[numvisrules].operands = \"\";\t//don't crash if they used the wrong arg type.\n\tvisrules[numvisrules].or = or;\n\tnumvisrules++;\n}\nvoid Master_SetSortField(hostcachekey_t field, unsigned int sortflags)\n{\n\tsortfield = field;\n\tsort_decreasing = sortflags & 1;\n\tsort_favourites = sortflags & 2;\n\tsort_categories = sortflags & 4;\n}\nhostcachekey_t Master_GetSortField(void)\n{\n\treturn sortfield;\n}\nqboolean Master_GetSortDescending(void)\n{\n\treturn sort_decreasing;\n}\n\nvoid Master_ShowServer(serverinfo_t *server)\n{\n\tint i;\n\tif (!numvisibleservers)\n\t{\n\t\tMaster_InsertAt(server, 0);\n\t\treturn;\n\t}\n\n\tif (sort_decreasing)\n\t{\n\t\tfor (i = 0; i < numvisibleservers; i++)\n\t\t{\n\t\t\tif (!Master_ServerIsGreater(server, visibleservers[i]))\n\t\t\t{\n\t\t\t\tMaster_InsertAt(server, i);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t}\n\telse\n\t{\n\t\tfor (i = 0; i < numvisibleservers; i++)\n\t\t{\n\t\t\tif (Master_ServerIsGreater(server, visibleservers[i]))\n\t\t\t{\n\t\t\t\tMaster_InsertAt(server, i);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n\n\tMaster_InsertAt(server, numvisibleservers);\n}\n\nvoid Master_ResortServer(serverinfo_t *server)\n{\n\tif (server->status&SRVSTATUS_DISPLAYED)\n\t{\n\t\tif (!Master_PassesMasks(server))\n\t\t\tMaster_HideServer(server);\n\t}\n\telse\n\t{\n\t\tif (Master_PassesMasks(server))\n\t\t\tMaster_ShowServer(server);\n\t}\n}\n\nint Master_SortServers(void)\n{\n\tserverinfo_t *server;\n\n\tint total = Master_TotalCount();\n\tif (maxvisibleservers < total)\n\t{\n\t\tmaxvisibleservers = total;\n\t\tvisibleservers = BZ_Realloc(visibleservers, maxvisibleservers*sizeof(serverinfo_t*));\n\t}\n\n\t{\n\t\tnumvisibleservers = 0;\n\t\tfor (server = firstserver; server; server = server->next)\n\t\t\tserver->status &= ~SRVSTATUS_DISPLAYED;\n\t}\n\n\tfor (server = firstserver; server; server = server->next)\n\t{\n\t\tMaster_ResortServer(server);\n\t}\n\n\treturn numvisibleservers;\n}\n\nserverinfo_t *Master_SortedServer(int idx)\n{\n\tif (idx < 0 || idx >= numvisibleservers)\n\t{\n\t\tif (idx == -1)\n\t\t\treturn categorisingserver;\n\t\treturn NULL;\n\t}\n\n\treturn visibleservers[idx];\n}\n\nint Master_NumSorted(void)\n{\n\treturn numvisibleservers;\n}\n\n\nfloat Master_ReadKeyFloat(serverinfo_t *server, unsigned int keynum)\n{\n\tif (!server)\n\t\treturn -1;\n\telse if (keynum < SLKEY_CUSTOM)\n\t{\n\t\tswitch((hostcachekey_t)keynum)\n\t\t{\n\t\tcase SLKEY_PING:\n\t\t\treturn server->ping;\n\t\tcase SLKEY_NUMPLAYERS:\n\t\t\treturn server->players;\n\t\tcase SLKEY_MAXPLAYERS:\n\t\t\treturn server->maxplayers;\n\t\tcase SLKEY_FREEPLAYERS:\n\t\t\treturn server->maxplayers - server->players;\n\t\tcase SLKEY_BASEGAME:\n\t\t\treturn Master_BaseGame(server);\n\t\tcase SLKEY_FLAGS:\n\t\t\treturn server->special&~SS_PROTOCOLMASK;\n\t\tcase SLKEY_TIMELIMIT:\n\t\t\treturn server->tl;\n\t\tcase SLKEY_FRAGLIMIT:\n\t\t\treturn server->fl;\n\t\tcase SLKEY_PROTOCOL:\n\t\t\treturn server->protocol;\n\t\tcase SLKEY_NUMBOTS:\n\t\t\treturn server->numbots;\n\t\tcase SLKEY_NUMHUMANS:\n\t\t\treturn server->numhumans;\n\t\tcase SLKEY_NUMSPECTATORS:\n\t\t\treturn server->numspectators;\n\t\tcase SLKEY_ISFAVORITE:\n\t\t\treturn !!(server->special & SS_FAVORITE);\n\t\tcase SLKEY_ISLOCAL:\n\t\t\treturn !!(server->special & SS_LOCAL);\n\t\tcase SLKEY_ISPROXY:\n\t\t\treturn !!(server->special & SS_PROXY);\n\t\tcase SLKEY_CATEGORY:\n\t\t\treturn server->qccategory;\n\n\t\tdefault:\n\t\t\treturn atof(Master_ReadKeyString(server, keynum));\n\t\t}\n\t}\n\telse if (server->moreinfo)\n\t\treturn atof(Info_ValueForKey(server->moreinfo->info, slist_keyname[keynum-SLKEY_CUSTOM]));\n\n\treturn 0;\n}\n\nvoid Master_DecodeColour(vec3_t ret, int col)\n{\n\tif (col < 16)\n\t{\n\t\tcol = Sbar_ColorForMap(col);\n\t\tVectorSet(ret, host_basepal[col*3+0]/255.0, host_basepal[col*3+1]/255.0, host_basepal[col*3+2]/255.0);\n\t}\n\telse\n\t\tVectorSet(ret, ((col&0xff0000)>>16)/255.0, ((col&0x00ff00)>>8)/255.0, ((col&0x0000ff)>>0)/255.0);\n}\n\nchar *Master_ReadKeyString(serverinfo_t *server, unsigned int keynum)\n{\n\tstatic char adr[MAX_ADR_SIZE];\n\n\tif (!server)\n\t\treturn \"\";\n\n\tif (keynum >= SLKEY_CUSTOM)\n\t{\n\t\tif (server->moreinfo)\n\t\t{\n\t\t\tkeynum -= SLKEY_CUSTOM;\n\t\t\tif (keynum < sizeof(slist_keyname)/sizeof(slist_keyname[0]))\n\t\t\t\treturn Info_ValueForKey(server->moreinfo->info, slist_keyname[keynum]);\n\t\t}\n\t\telse if (!(server->special & SS_KEEPINFO))\n\t\t{\n\t\t\tserver->special |= SS_KEEPINFO;\n\t\t\tserver->sends++;\n\t\t}\n\t}\n\telse if (keynum >= SLKEY_PLAYER0)\n\t{\n\t\tif (server->moreinfo)\n\t\t{\n\t\t\tkeynum -= SLKEY_PLAYER0;\n\t\t\tif (keynum < server->moreinfo->numplayers)\n\t\t\t{\n\t\t\t\tvec3_t top, bot;\n\t\t\t\tMaster_DecodeColour(top, server->moreinfo->players[keynum].topc);\n\t\t\t\tMaster_DecodeColour(bot, server->moreinfo->players[keynum].botc);\n\n\t\t\t\treturn va(\"%i %i %g %i \\\"%s\\\" \\\"%s\\\" '%g %g %g' '%g %g %g'\", server->moreinfo->players[keynum].userid, server->moreinfo->players[keynum].frags, server->moreinfo->players[keynum].time, server->moreinfo->players[keynum].ping, server->moreinfo->players[keynum].name, server->moreinfo->players[keynum].skin, top[0],top[1],top[2], bot[0], bot[1], bot[2]);\n\t\t\t}\n\t\t}\n\t\telse if (!(server->special & SS_KEEPINFO))\n\t\t{\n\t\t\tserver->special |= SS_KEEPINFO;\n\t\t\tserver->sends++;\n\t\t}\n\t}\n\telse\n\t{\n\t\tswitch((hostcachekey_t)keynum)\n\t\t{\n\t\tcase SLKEY_MAP:\n\t\t\treturn server->map;\n\t\tcase SLKEY_NAME:\n\t\t\treturn server->name;\n\t\tcase SLKEY_ADDRESS:\n\t\t\treturn Master_ServerToString(adr, sizeof(adr), server);\n\t\tcase SLKEY_GAMEDIR:\n\t\t\treturn server->gamedir;\n\n\t\tcase SLKEY_MOD:\n\t\t\treturn server->modname;\n\t\tcase SLKEY_QCSTATUS:\n\t\t\treturn server->qcstatus;\n\t\tcase SLKEY_SERVERINFO:\n\t\t\tif (server->moreinfo)\n\t\t\t\treturn server->moreinfo->info;\n\t\t\treturn \"\";\n\n\t\tdefault:\n\t\t\t{\n\t\t\t\tstatic char s[64];\n\t\t\t\tsprintf(s, \"%f\", Master_ReadKeyFloat(server, keynum));\n\t\t\t\treturn s;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn \"\";\n}\n\nhostcachekey_t Master_KeyForName(const char *keyname)\n{\n\tint i;\n\tif (!strcmp(keyname, \"map\"))\n\t\treturn SLKEY_MAP;\n\telse if (!strcmp(keyname, \"ping\"))\n\t\treturn SLKEY_PING;\n\telse if (!strcmp(keyname, \"name\") || !strcmp(keyname, \"hostname\"))\n\t\treturn SLKEY_NAME;\n\telse if (!strcmp(keyname, \"address\") || !strcmp(keyname, \"cname\"))\n\t\treturn SLKEY_ADDRESS;\n\telse if (!strcmp(keyname, \"maxplayers\"))\n\t\treturn SLKEY_MAXPLAYERS;\n\telse if (!strcmp(keyname, \"numplayers\"))\n\t\treturn SLKEY_NUMPLAYERS;\n\telse if (!strcmp(keyname, \"freeplayers\") || !strcmp(keyname, \"freeslots\"))\n\t\treturn SLKEY_FREEPLAYERS;\n\telse if (!strcmp(keyname, \"gamedir\") || !strcmp(keyname, \"game\") || !strcmp(keyname, \"*gamedir\"))\n\t\treturn SLKEY_GAMEDIR;\n\telse if (!strcmp(keyname, \"basegame\"))\n\t\treturn SLKEY_BASEGAME;\n\telse if (!strcmp(keyname, \"flags\"))\n\t\treturn SLKEY_FLAGS;\n\telse if (!strcmp(keyname, \"mod\"))\n\t\treturn SLKEY_MOD;\n\telse if (!strcmp(keyname, \"protocol\"))\n\t\treturn SLKEY_PROTOCOL;\n\telse if (!strcmp(keyname, \"numbots\"))\n\t\treturn SLKEY_NUMBOTS;\n\telse if (!strcmp(keyname, \"numhumans\"))\n\t\treturn SLKEY_NUMHUMANS;\n\telse if (!strcmp(keyname, \"numspectators\"))\n\t\treturn SLKEY_NUMSPECTATORS;\n\telse if (!strcmp(keyname, \"qcstatus\"))\n\t\treturn SLKEY_QCSTATUS;\n\telse if (!strcmp(keyname, \"isfavorite\"))\n\t\treturn SLKEY_ISFAVORITE;\n\telse if (!strcmp(keyname, \"islocal\"))\n\t\treturn SLKEY_ISLOCAL;\n\telse if (!strcmp(keyname, \"isproxy\"))\n\t\treturn SLKEY_ISPROXY;\n\telse if (!strcmp(keyname, \"category\"))\n\t\treturn SLKEY_CATEGORY;\n\telse if (!strcmp(keyname, \"serverinfo\"))\n\t\treturn SLKEY_SERVERINFO;\n\telse if (!strncmp(keyname, \"player\", 6))\n\t\treturn SLKEY_PLAYER0 + atoi(keyname+6);\n\n\telse if (slist_customkeys == SLIST_MAXKEYS)\n\t\treturn SLKEY_TOOMANY;\n\telse\n\t{\n\t\tfor (i = 0; i < slist_customkeys; i++)\n\t\t{\n\t\t\tif (!strcmp(slist_keyname[i], keyname))\n\t\t\t{\n\t\t\t\treturn i + SLKEY_CUSTOM;\n\t\t\t}\n\t\t}\n\t\tQ_strncpyz(slist_keyname[slist_customkeys], keyname, MAX_INFO_KEY);\n\n\t\tslist_customkeys++;\n\n\t\treturn slist_customkeys-1 + SLKEY_CUSTOM;\n\t}\n}\n\n\n#ifdef HAVE_PACKET\nstatic void Master_FloodRoute(serverinfo_t *node)\n{\n\tunsigned int i;\n\tstruct peers_s *peer = node->peers;\n\tfor (i = 0; i < node->numpeers; i++, peer++)\n\t{\n\t\tif (peer->ping && peer->ping != PING_DEAD)\n\t\tif ((unsigned int)(peer->peer->cost) > (unsigned int)(node->cost + peer->ping))\n\t\t{\t//we found a shorter route. flood into it.\n\t\t\tpeer->peer->prevpeer = node;\n\t\t\tpeer->peer->cost = node->cost + peer->ping;\n\t\t\tMaster_FloodRoute(peer->peer);\n\t\t}\n\t}\n}\nserverinfo_t *Master_FindRoute(netadr_t target)\n{\n\tserverinfo_t *info, *targ, *prox;\n\textern cvar_t cl_proxyaddr;\n\ttarg = Master_InfoForServer(&target, NULL);\n\tif (!targ)\t//you wot?\n\t\treturn NULL;\n\n\t//never flood into a peer if its just going to be more expensive than a direct connection\n\tif (*cl_proxyaddr.string && NET_ClassifyAddress(&target, NULL) >= ASCOPE_NET)\n\t{\n\t\t//fixme: we don't handle chained proxies properly, as we assume we can directly hop to the named final proxy.\n\t\t//fixme: we'll find the same route, we just won't display the correct expected ping.\n\t\tnetadr_t pa;\n\t\tchar *chain = strchr(cl_proxyaddr.string, '@');\n\t\tif (chain)\n\t\t\t*chain = 0;\n\n\t\tif (NET_StringToAdr(cl_proxyaddr.string, 0, &pa))\n\t\t\tprox = Master_InfoForServer(&pa, NULL);\n\t\telse\n\t\t\tprox = NULL;\n\t\tif (chain)\n\t\t\t*chain = '@';\n\t}\n\telse\n\t\tprox = NULL;\n\n\tif (prox)\n\t{\n\t\tfor (info = firstserver; info; info = info->next)\n\t\t{\n\t\t\tinfo->cost = info->ping;\n\t\t\tinfo->prevpeer = prox;\n\t\t}\n\t\tprox->cost = prox->ping;\n\t\tprox->prevpeer = NULL;\n\t\tMaster_FloodRoute(prox);\n\t}\n\telse\n\t{\n\t\tfor (info = firstserver; info; info = info->next)\n\t\t{\n\t\t\tinfo->cost = info->ping;\n\t\t\tinfo->prevpeer = NULL;\n\t\t}\n\n\t\t//flood through all proxies\n\t\tfor (info = firstserver; info; info = info->next)\n\t\t\tMaster_FloodRoute(info);\n\t}\n\n\tif (targ->prevpeer)\n\t\treturn targ;\n\treturn NULL;\n}\n\nint Master_FindBestRoute(char *server, char *out, size_t outsize, int *directcost, int *chainedcost)\n{\n\tserverinfo_t *route;\n\tnetadr_t adr;\n\tint ret = 0;\n\tchar buf[256];\n\t*out = 0;\n\t*directcost = 0;\n\t*chainedcost = 0;\n\tif (!NET_StringToAdr(server, 0, &adr))\n\t\treturn -1;\n\n\tif (!firstserver)\n\t{\t//routing isn't initialised. you do actually need to refresh the serverbrowser for this junk\n\t\tQ_strncpyz(out, server, outsize);\n\t\treturn -1;\n\t}\n\n\troute = Master_FindRoute(adr);\n\n\tif (!route)\n\t{\t//routing didn't find anything, just go directly.\n\t\tQ_strncpyz(out, server, outsize);\n\t\treturn 0;\n\t}\n\n\t*directcost = route->ping;\n\t*chainedcost = route->cost;\n\n\tQ_strncatz(out, Master_ServerToString(buf, sizeof(buf), route), outsize);\n\tfor (ret = 0, route = route->prevpeer; route; route = route->prevpeer, ret++)\n\t\tQ_strncatz(out, va(\"@%s\", Master_ServerToString(buf, sizeof(buf), route)), outsize);\n\n\treturn ret;\n}\n\n\n\n\n\n\n\n//main thread\nvoid CLMaster_AddMaster_Worker_Resolved(void *ctx, void *data, size_t a, size_t b)\n{\n\tmaster_t *mast = data;\n\tmaster_t *oldmast;\n\n\tif (mast->adr.type == NA_INVALID)\n\t{\n\t\tif (b)\n\t\t\tCon_Printf(\"Failed to resolve master address \\\"%s\\\"\\n\", mast->address);\n\t\t//else master not enabled anyway. the lookup was skipped.\n\t}\n\telse if (mast->adr.type != NA_IP && mast->adr.type != NA_IPV6 && mast->adr.type != NA_IPX)\n\t{\n\t\tCon_Printf(\"Fixme: unable to poll address family for \\\"%s\\\"\\n\", mast->address);\n\t}\n\telse\n\t{\n\t\tif (mast->mastertype == MT_BCAST)\t//broadcasts\n\t\t{\n\t\t\tif (mast->adr.type == NA_IP)\n\t\t\t\tmemset(mast->adr.address.ip+4, 0xff, sizeof(mast->adr.address.ip));\n\t\t\tif (mast->adr.type == NA_IPX)\n\t\t\t{\n\t\t\t\tmemset(mast->adr.address.ipx+0, 0, 4);\n\t\t\t\tmemset(mast->adr.address.ipx+4, 0xff, 6);\n\t\t\t}\n\t\t\tif (mast->adr.type == NA_IPV6)\n\t\t\t{\n\t\t\t\tmemset(mast->adr.address.ip6, 0, sizeof(mast->adr.address.ip6));\n\t\t\t\tmast->adr.address.ip6[0]\t= 0xff;\n\t\t\t\tmast->adr.address.ip6[1]\t= 0x02;\n\t\t\t\tmast->adr.address.ip6[15]\t= 0x01;\n\t\t\t}\n\t\t}\n\n\t\t//fix up default ports if not specified\n\t\tif (!mast->adr.port)\n\t\t{\n\t\t\tswitch (mast->protocoltype)\n\t\t\t{\n\t\t\tcase MP_DPMASTER:\tmast->adr.port = BigShort (27950);\tbreak;\n#ifdef Q2CLIENT\n\t\t\tcase MP_QUAKE2:\t\tmast->adr.port = BigShort (27900);\tbreak;\t//FIXME: verify\n#endif\n#ifdef Q3CLIENT\n\t\t\tcase MP_QUAKE3:\t\tmast->adr.port = BigShort (27950);\tbreak;\n#endif\n\t\t\tcase MP_QUAKEWORLD:\tmast->adr.port = BigShort (PORT_QWMASTER);\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tfor (oldmast = master; oldmast; oldmast = oldmast->next)\n\t\t{\n\t\t\tif (NET_CompareAdr(&oldmast->adr, &mast->adr) && oldmast->mastertype == mast->mastertype && oldmast->protocoltype == mast->protocoltype)\t//already exists.\n\t\t\t\tbreak;\n\t\t}\n\t\tif (!oldmast)\n\t\t{\n\t\t\tmast->next = master;\n\t\t\tmaster = mast;\n\t\t\treturn;\n\t\t}\n\t\telse if (oldmast->nosave && !mast->nosave)\n\t\t\toldmast->nosave = false;\n\t}\n\tZ_Free(mast);\n}\n//worker thread\nvoid CLMaster_AddMaster_Worker_Resolve(void *ctx, void *data, size_t a, size_t b)\n{\n\tnetadr_t adrs[MAX_MASTER_ADDRESSES];\n\tchar token[1024];\n\tint found = 0, j, i;\n//\tqboolean first = true;\n\tchar *str;\n\tmaster_t *work = data;\n\n\tif (!b)\n\t\t;\n\telse\n\t{\n\t\t//resolve all the addresses\n\t\tstr = work->address;\n\t\twhile (str && *str)\n\t\t{\n\t\t\tstr = COM_ParseOut(str, token, sizeof(token));\n\t\t\tif (*token)\n\t\t\t\tfound += NET_StringToAdr2(token, 0, &adrs[found], MAX_MASTER_ADDRESSES-found, NULL);\n\t\t\t//we don't do this logic because windows doesn't look up ipv6 names if it only has teredo\n\t\t\t//this means an ipv4+teredo client cannot see ivp6-only servers. and that sucks.\n//\t\t\tif (first && found)\n//\t\t\t\tbreak;\t//if we found one by name, don't try any fallback ip addresses.\n//\t\t\tfirst = false;\n\t\t}\n\t}\n\n\t//add the main ip address\n\tif (found)\n\t\twork->adr = adrs[0];\n\telse\n\t\tmemset(&work->adr, 0, sizeof(work->adr));\n\n\t//add dupes too (eg: ipv4+ipv6)\n\tfor (i = 1; i < found; i++)\n\t{\n//\t\tmaster_t *alt;\n\t\tif (adrs[i].type == NA_INVALID)\n\t\t\tcontinue;\n\n\t\t//don't add the same ip twice, because that's silly\n\t\tfor (j = 0; j < i; j++)\n\t\t{\n\t\t\tif (NET_CompareAdr(&adrs[j], &adrs[i]))\n\t\t\t\tbreak;\n\t\t}\n\t\tif (j == i)\n\t\t{\t//not already added, hurrah\n\t\t\tmaster_t *alt = Z_Malloc(sizeof(master_t)+strlen(work->name)+1+strlen(work->address)+1);\n\t\t\talt->address = alt->name + strlen(work->name)+1;\n\t\t\talt->mastertype = work->mastertype;\n\t\t\talt->protocoltype = work->protocoltype;\n\t\t\tstrcpy(alt->name, work->name);\n\t\t\tstrcpy(alt->address, work->address);\n\t\t\talt->sends = 1;\n\t\t\talt->nosave = true;\n\t\t\talt->adr = adrs[i];\n\n\t\t\tCOM_AddWork(WG_MAIN, CLMaster_AddMaster_Worker_Resolved, NULL, work, a, b);\n\t\t\twork = alt;\n\t\t}\n\t}\n\n\tCOM_AddWork(WG_MAIN, CLMaster_AddMaster_Worker_Resolved, NULL, work, a, b);\n}\n\nvoid Master_AddMaster (char *address, enum mastertype_e mastertype, enum masterprotocol_e protocol, char *description)\n{\n\tmaster_t *mast;\n\n\tif (!address || !*address)\n\t\treturn;\n\n\tif (!description)\n\t\tdescription = address;\n\n\tmast = Z_Malloc(sizeof(master_t)+strlen(description)+1+strlen(address)+1);\n\tmast->address = mast->name + strlen(description)+1;\n\tmast->mastertype = mastertype;\n\tmast->protocoltype = protocol;\n\tstrcpy(mast->name, description);\n\tstrcpy(mast->address, address);\n\tmast->sends = 1;\n\n\tCOM_AddWork(WG_LOADER, CLMaster_AddMaster_Worker_Resolve, NULL, mast, 0, true/*Master_MasterProtocolIsEnabled(protocol)*/);\n}\n#else\nvoid Master_AddMaster (char *address, enum mastertype_e mastertype, enum masterprotocol_e protocol, char *description)\n{\t//pretend it didn't resolve.\n}\n#endif\n\nstatic void MasterInfo_RemoveAllPlayers(void)\n{\n\tplayer_t *p;\n\twhile(mplayers)\n\t{\n\t\tp = mplayers;\n\t\tmplayers = p->next;\n\t\tZ_Free(p);\n\t}\n}\nvoid MasterInfo_Shutdown(void)\n{\n\tmaster_t *mast;\n\tserverinfo_t *sv;\n\tMasterInfo_RemoveAllPlayers();\n\twhile(firstserver)\n\t{\n\t\tsv = firstserver;\n\t\tfirstserver = sv->next;\n\t\tif (sv->peers)\n\t\t\tZ_Free(sv->peers);\n\t\tZ_Free(sv);\n\t}\n\twhile(master)\n\t{\n\t\tmast = master;\n\t\tmaster = mast->next;\n#ifdef WEBCLIENT\n\t\tif (mast->dl)\n\t\t\tDL_Close(mast->dl);\n#endif\n\t\tZ_Free(mast);\n\t}\n\n\tmaxvisibleservers = 0;\n\tnumvisibleservers = 0;\n\tZ_Free(visibleservers);\n}\n\nvoid Master_AddMasterHTTP (char *address, int mastertype, int protocoltype, char *description)\n{\n\tmaster_t *mast;\n/*\tint servertype;\n\n\tif (protocoltype == MP_DP)\n\t\tservertype = SS_DARKPLACES;\n\telse if (protocoltype == MP_Q2)\n\t\tservertype = SS_QUAKE2;\n\telse if (protocoltype == MP_Q3)\n\t\tservertype = SS_QUAKE3;\n\telse if (protocoltype == MP_NQ)\n\t\tservertype = SS_NETQUAKE;\n\telse\n\t\tservertype = 0;\n*/\n\tfor (mast = master; mast; mast = mast->next)\n\t{\n\t\tif (!strcmp(mast->address, address) && mast->mastertype == mastertype && mast->protocoltype == protocoltype)\t//already exists.\n\t\t\treturn;\n\t}\n\tmast = Z_Malloc(sizeof(master_t)+strlen(description)+1+strlen(address)+1);\n\tmast->address = mast->name + strlen(description)+1;\n\tmast->mastertype = mastertype;\n\tmast->protocoltype = protocoltype;\n//\tmast->servertype = servertype;\n\tstrcpy(mast->name, description);\n\tstrcpy(mast->address, address);\n\n\tmast->next = master;\n\tmaster = mast;\n}\n\n//build a linked list of masters.\tDoesn't duplicate addresses.\nqboolean Master_LoadMasterList (char *filename, qboolean withcomment, int defaulttype, int defaultprotocol, int depth)\n{\n\tvfsfile_t *f;\n\tchar line[1024];\n\tchar name[1024];\n\tchar entry[1024];\n\tchar *next, *sep;\n\tint servertype;\n\tint protocoltype;\n\tqboolean favourite;\n\n\tif (depth <= 0)\n\t\treturn false;\n\tdepth--;\n\n\tf = FS_OpenVFS(filename, \"rb\", FS_ROOT);\n\tif (!f)\n\t\treturn false;\n\n\twhile(VFS_GETS(f, line, sizeof(line)-1))\n\t{\n\t\tif (*line == '#')\t//comment\n\t\t\tcontinue;\n\n\t\t*name = 0;\n\t\tfavourite = false;\n\t\tservertype = defaulttype;\n\t\tprotocoltype = defaultprotocol;\n\n\t\tnext = COM_ParseOut(line, entry, sizeof(entry));\n\t\tif (!*com_token)\n\t\t\tcontinue;\n\n\t\t//special cases. Add a port if you have a host named 'file'... (unlikly)\n\t\tif (!strcmp(entry, \"file\"))\n\t\t{\n\t\t\tif (withcomment)\n\t\t\t\tnext = COM_ParseOut(next, name, sizeof(name));\n\t\t\tnext = COM_ParseOut(next, entry, sizeof(entry));\n\t\t\tif (!next)\n\t\t\t\tcontinue;\n\t\t\tservertype = MT_BAD;\n\t\t}\n\t\telse if (!strcmp(entry, \"master\"))\n\t\t{\n\t\t\tif (withcomment)\n\t\t\t\tnext = COM_ParseOut(next, name, sizeof(name));\n\t\t\tnext = COM_ParseOut(next, entry, sizeof(entry));\n\t\t\tif (!next)\n\t\t\t\tcontinue;\n\t\t\tservertype = MT_MASTERUDP;\n\t\t}\n\t\telse if (!strcmp(entry, \"url\"))\n\t\t{\n\t\t\tif (withcomment)\n\t\t\t\tnext = COM_ParseOut(next, name, sizeof(name));\n\t\t\tnext = COM_ParseOut(next, entry, sizeof(entry));\n\t\t\tservertype = MT_MASTERHTTP;\n\t\t}\n\n\t\tnext = COM_Parse(next);\n\n\t\tfor(sep = com_token; sep; sep = next)\n\t\t{\n\t\t\tnext = strchr(sep, ':');\n\t\t\tif (next)\n\t\t\t\t*next = 0;\n\n\t\t\tif (!strcmp(sep, \"single\"))\n\t\t\t\tservertype = MT_SINGLE;\n\t\t\telse if (!strcmp(sep, \"master\"))\n\t\t\t\tservertype = MT_MASTERUDP;\n\t\t\telse if (!strcmp(sep, \"masterhttp\"))\n\t\t\t\tservertype = MT_MASTERHTTP;\n\t\t\telse if (!strcmp(sep, \"bcast\"))\n\t\t\t\tservertype = MT_BCAST;\n\n#ifndef QUAKETC\n\t\t\telse if (!strcmp(com_token, \"qw\"))\n\t\t\t\tprotocoltype = MP_QUAKEWORLD;\n#endif\n#ifdef Q2CLIENT\n\t\t\telse if (!strcmp(com_token, \"q2\"))\n\t\t\t\tprotocoltype = MP_QUAKE2;\n#endif\n#ifdef Q3CLIENT\n\t\t\telse if (!strcmp(com_token, \"q3\"))\n\t\t\t\tprotocoltype = MP_QUAKE3;\n#endif\n#ifdef NQPROT\n\t\t\telse if (!strcmp(com_token, \"nq\"))\n\t\t\t\tprotocoltype = MP_NETQUAKE;\n#endif\n\t\t\telse if (!strcmp(com_token, \"dp\"))\n\t\t\t\tprotocoltype = MP_DPMASTER;\n\n\t\t\t//legacy compat\n#ifdef NQPROT\n\t\t\telse if (!strcmp(com_token, \"httpnq\"))\n\t\t\t{\n\t\t\t\tservertype = MT_MASTERHTTP;\n\t\t\t\tprotocoltype = MP_NETQUAKE;\n\t\t\t}\n#endif\n#ifndef QUAKETC\n\t\t\telse if (!strcmp(com_token, \"httpqw\"))\n\t\t\t{\n\t\t\t\tservertype = MT_MASTERHTTP;\n\t\t\t\tprotocoltype = MP_QUAKEWORLD;\n\t\t\t}\n#endif\n\n\t\t\telse if (!strcmp(com_token, \"favourite\") || !strcmp(com_token, \"favorite\"))\n\t\t\t\tfavourite = true;\n\t\t}\n\n\t\tif (!*name && next)\n\t\t{\n\t\t\tsep = name;\n\t\t\twhile(*next == ' ' || *next == '\\t')\n\t\t\t\tnext++;\n\t\t\twhile (*next && sep < name+sizeof(name)-1)\n\t\t\t\t*sep++ = *next++;\n\t\t\t*sep = 0;\n\t\t}\n\n\t\tif (servertype == MT_BAD)\n\t\t\tMaster_LoadMasterList(entry, false, servertype, protocoltype, depth);\n\t\telse\n\t\t{\n#if POLLTOTALSOCKETS>0\n\t\t\t//favourites are added explicitly, with their name and stuff\n\t\t\tif (favourite && servertype == MT_SINGLE)\n\t\t\t{\n\t\t\t\tif (NET_StringToAdr(entry, 0, &net_from))\n\t\t\t\t\tCL_ReadServerInfo(va(\"\\\\hostname\\\\%s\", name), -servertype, true);\n\t\t\t\telse\n\t\t\t\t\tCon_Printf(\"Failed to resolve address - \\\"%s\\\"\\n\", entry);\n\t\t\t}\n#endif\n\n\t\t\tswitch (servertype)\n\t\t\t{\n\t\t\tcase MT_MASTERHTTP:\n\t\t\t\tMaster_AddMasterHTTP(entry, servertype, protocoltype, name);\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tMaster_AddMaster(entry, servertype, protocoltype, name);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\tVFS_CLOSE(f);\n\n\treturn true;\n}\n\n#if POLLTOTALSOCKETS>0\nqboolean NET_SendPollPacket(int len, void *data, netadr_t to)\n{\n\tunsigned long bcast;\n\tint ret;\n\tstruct sockaddr_qstorage\taddr;\n\tchar buf[128];\n\n\tNetadrToSockadr (&to, &addr);\n#ifdef HAVE_IPX\n\tif (((struct sockaddr*)&addr)->sa_family == AF_IPX)\n\t{\n\t\tlastpollsockIPX++;\n\t\tif (lastpollsockIPX>=POLLIPXSOCKETS)\n\t\t\tlastpollsockIPX=0;\n\t\tif (pollsocketsList[FIRSTIPXSOCKET+lastpollsockIPX]==INVALID_SOCKET)\n\t\t{\n\t\t\tpollsocketsList[FIRSTIPXSOCKET+lastpollsockIPX] = IPX_OpenSocket(PORT_ANY);\n\t\t\tpollsocketsBCast[FIRSTIPXSOCKET+lastpollsockIPX] = false;\n\t\t}\n\t\tif (pollsocketsList[FIRSTIPXSOCKET+lastpollsockIPX]==INVALID_SOCKET)\n\t\t\treturn true;\t//bother\n\n\t\tbcast = !memcmp(to.address.ipx, \"\\0\\0\\0\\0\\xff\\xff\\xff\\xff\\xff\\xff\", sizeof(to.address.ipx));\n\t\tif (pollsocketsBCast[FIRSTIPXSOCKET+lastpollsockIPX] != bcast)\n\t\t{\n\t\t\tif (setsockopt(pollsocketsList[FIRSTIPXSOCKET+lastpollsockIPX], SOL_SOCKET, SO_BROADCAST, (char *)&bcast, sizeof(bcast)) == -1)\n\t\t\t\treturn true;\n\t\t\tpollsocketsBCast[FIRSTIPXSOCKET+lastpollsockIPX] = bcast;\n\t\t}\n\t\tret = sendto (pollsocketsList[FIRSTIPXSOCKET+lastpollsockIPX], data, len, 0, (struct sockaddr *)&addr, sizeof(addr) );\n\t}\n\telse\n#endif\n#ifdef HAVE_IPV6\n\tif (((struct sockaddr*)&addr)->sa_family == AF_INET6)\n\t{\n\t\tlastpollsockUDP6++;\n\t\tif (lastpollsockUDP6>=POLLUDP6SOCKETS)\n\t\t\tlastpollsockUDP6=0;\n\t\tif (pollsocketsList[FIRSTUDP6SOCKET+lastpollsockUDP6]==INVALID_SOCKET)\n\t\t{\n\t\t\tpollsocketsList[FIRSTUDP6SOCKET+lastpollsockUDP6] = UDP6_OpenSocket(PORT_ANY);\n\t\t\tpollsocketsBCast[FIRSTUDP6SOCKET+lastpollsockUDP6] = false;\n\t\t}\n\t\tif (pollsocketsList[FIRSTUDP6SOCKET+lastpollsockUDP6]==INVALID_SOCKET)\n\t\t\treturn true;\t//bother\n\t\tret = sendto (pollsocketsList[FIRSTUDP6SOCKET+lastpollsockUDP6], data, len, 0, (struct sockaddr *)&addr, sizeof(addr) );\n\t}\n\telse\n#endif\n#ifdef HAVE_IPV4\n\t\tif (((struct sockaddr*)&addr)->sa_family == AF_INET)\n\t{\n\t\tlastpollsockUDP4++;\n\t\tif (lastpollsockUDP4>=POLLUDP4SOCKETS)\n\t\t\tlastpollsockUDP4=0;\n\t\tif (pollsocketsList[FIRSTUDP4SOCKET+lastpollsockUDP4]==INVALID_SOCKET)\n\t\t{\n\t\t\tpollsocketsList[FIRSTUDP4SOCKET+lastpollsockUDP4] = UDP_OpenSocket(PORT_ANY);\n\t\t\tpollsocketsBCast[FIRSTUDP4SOCKET+lastpollsockUDP4] = false;\n\t\t}\n\t\tif (pollsocketsList[FIRSTUDP4SOCKET+lastpollsockUDP4]==INVALID_SOCKET)\n\t\t\treturn true;\t//bother\n\n\t\tbcast = !memcmp(to.address.ip, \"\\xff\\xff\\xff\\xff\", sizeof(to.address.ip));\n\t\tif (pollsocketsBCast[FIRSTUDP4SOCKET+lastpollsockUDP4] != bcast)\n\t\t{\n\t\t\tif (setsockopt(pollsocketsList[FIRSTUDP4SOCKET+lastpollsockUDP4], SOL_SOCKET, SO_BROADCAST, (char *)&bcast, sizeof(bcast)) == -1)\n\t\t\t\treturn true;\n\t\t\tpollsocketsBCast[FIRSTUDP4SOCKET+lastpollsockUDP4] = bcast;\n\t\t}\n\t\tret = sendto (pollsocketsList[FIRSTUDP4SOCKET+lastpollsockUDP4], data, len, 0, (struct sockaddr *)&addr, sizeof(struct sockaddr_in) );\n\t}\n\telse\n#endif\n\t\treturn true;\n\n\tif (ret == -1)\n\t{\n\t\tint er = neterrno();\n// wouldblock is silent\n\t\tif (er == NET_EWOULDBLOCK)\n\t\t\treturn false;\n\n\t\tif (er == NET_ECONNREFUSED)\n\t\t\treturn true;\n\n\t\tif (er == NET_ENETUNREACH)\n\t\t\tCon_DPrintf(\"NET_SendPollPacket Warning: unreachable: %s\\n\", NET_AdrToString(buf, sizeof(buf), &to));\n\t\telse\n#ifdef _WIN32\n\t\tif (er == NET_EADDRNOTAVAIL)\n\t\t\tCon_DPrintf(\"NET_SendPollPacket Warning: %i\\n\", er);\n\t\telse\n\t\t\tCon_Printf (\"NET_SendPollPacket ERROR: %i\\n\", er);\n#else\n\t\tif (er == NET_EADDRNOTAVAIL)\n\t\t\tCon_DPrintf(\"NET_SendPollPacket Warning: %s\\n\", strerror(er));\n\t\telse\n\t\t\tCon_Printf (\"NET_SendPollPacket ERROR: %s\\n\", strerror(er));\n#endif\n\t}\n\treturn true;\n}\n\nvoid Master_CheckPollSockets(void)\n{\n\tint sock;\n\tSOCKET usesocket;\n\tchar adr[MAX_ADR_SIZE];\n\n\tfor (sock = 0; sock < POLLTOTALSOCKETS; sock++)\n\t{\n\t\tint \tret;\n\t\tstruct sockaddr_qstorage\tfrom;\n\t\tint\t\tfromlen;\n\n\t\tusesocket = pollsocketsList[sock];\n\n\t\tif (usesocket == INVALID_SOCKET)\n\t\t\tcontinue;\n\t\tfromlen = sizeof(from);\n\t\tret = recvfrom (usesocket, (char *)net_message_buffer, sizeof(net_message_buffer), 0, (struct sockaddr *)&from, &fromlen);\n\n\t\tif (ret == -1)\n\t\t{\n\t\t\tint e = neterrno();\n\t\t\tif (e == NET_EWOULDBLOCK)\n\t\t\t\tcontinue;\n\t\t\tif (e == NET_EMSGSIZE)\n\t\t\t{\n\t\t\t\tSockadrToNetadr (&from, fromlen, &net_from);\n\t\t\t\tCon_Printf (\"Warning:  Oversize packet from %s\\n\",\n\t\t\t\t\tNET_AdrToString (adr, sizeof(adr), &net_from));\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (e == NET_ECONNABORTED || e == NET_ECONNRESET)\n\t\t\t{\n//\t\t\t\tCon_Printf (\"Connection lost or aborted\\n\");\n\t\t\t\tcontinue;\n\t\t\t}\n\n\n\t\t\tCon_Printf (\"NET_CheckPollSockets: %i, %s\\n\", e, strerror(e));\n\t\t\tcontinue;\n\t\t}\n\t\tSockadrToNetadr (&from, fromlen, &net_from);\n\n\t\tnet_message.cursize = ret;\n\t\tif (ret >= sizeof(net_message_buffer) )\n\t\t{\n\t\t\tCon_Printf (\"Oversize packet from %s\\n\", NET_AdrToString (adr, sizeof(adr), &net_from));\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (*(int *)net_message.data == -1)\n\t\t{\n\t\t\tint c;\n\t\t\tchar *s;\n\n\t\t\tMSG_BeginReading (&net_message, msg_nullnetprim);\n\t\t\tMSG_ReadLong ();        // skip the -1\n\n\t\t\tc = net_message.currentbit;\n\t\t\ts = MSG_ReadStringLine();\t//peek for q2 messages.\n#ifdef Q2CLIENT\n\t\t\tif (!strcmp(s, \"print\"))\n\t\t\t{\n\t\t\t\tCL_ReadServerInfo(MSG_ReadString(), MP_QUAKE2, false);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (!strcmp(s, \"info\"))\t//parse a bit more...\n\t\t\t{\n\t\t\t\tCL_ReadServerInfo(MSG_ReadString(), MP_QUAKE2, false);\n\t\t\t\tcontinue;\n\t\t\t}\n#ifdef HAVE_IPV6\n\t\t\tif (!strncmp(s, \"server6\", 7))\t//parse a bit more...\n\t\t\t{\n\t\t\t\tnet_message.currentbit = c+(7<<3);\n\t\t\t\tCL_MasterListParse(NA_IPV6, SS_QUAKE2, false);\n\t\t\t\tcontinue;\n\t\t\t}\n#endif\n\t\t\tif (!strncmp(s, \"servers\", 7))\t//parse a bit more...\n\t\t\t{\n\t\t\t\tnet_message.currentbit = c+(7<<3);\n\t\t\t\tCL_MasterListParse(NA_IP, SS_QUAKE2, false);\n\t\t\t\tcontinue;\n\t\t\t}\n#endif\n\t\t\t//q3/dpm server responses\n\t\t\tif (!strcmp(s, \"statusResponse\"))\n\t\t\t{\t//originally q3, but oh well.\n\t\t\t\tCL_ReadServerInfo(MSG_ReadString(), MP_DPMASTER, false);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (!strcmp(s, \"infoResponse\"))\t//parse a bit more...\n\t\t\t{\t//originally q3, but oh well.\n\t\t\t\tCL_ReadServerInfo(MSG_ReadString(), MP_DPMASTER, false);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t//q3/dpm master responses\n#ifdef HAVE_IPV6\n\t\t\tif (!strncmp(s, \"getserversResponse6\", 19) && (s[19] == '\\\\' || s[19] == '/'))\t//parse a bit more...\n\t\t\t{\n\t\t\t\tnet_message.currentbit = (c+19-1)<<3;\n\t\t\t\tCL_MasterListParse(NA_IPV6, SS_GETINFO, true);\n\t\t\t\tcontinue;\n\t\t\t}\n#endif\n\t\t\tif (!strncmp(s, \"getserversExtResponse\", 21) && (s[21] == '\\\\' || s[21] == '/'))\t//parse a bit more...\n\t\t\t{\n\t\t\t\tnet_message.currentbit = (c+21-1)<<3;\n\t\t\t\tCL_MasterListParse(NA_IP, SS_GETINFO, true);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (!strncmp(s, \"getserversResponse\", 18) && (s[18] == '\\\\' || s[18] == '/'))\t//parse a bit more...\n\t\t\t{\n\t\t\t\tnet_message.currentbit = (c+18-1)<<3;\n\t\t\t\tCL_MasterListParse(NA_IP, SS_GETINFO, true);\n\t\t\t\tcontinue;\n\t\t\t}\n\n#ifdef HAVE_IPV6\n\t\t\tif (!strncmp(s, \"qw_slist6\\\\\", 10))\t//parse a bit more...\n\t\t\t{\n\t\t\t\tnet_message.currentbit = (c+9-1)<<3;\n\t\t\t\tCL_MasterListParse(NA_IPV6, SS_QUAKEWORLD, false);\n\t\t\t\tcontinue;\n\t\t\t}\n#endif\n\t\t\tif (!strncmp(s, \"pinglist\", 8))\t//parse a bit more...\n\t\t\t{\n\t\t\t\tnet_message.currentbit = (c+8-1)<<3;\n\t\t\t\tCL_ReadPingList();\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tnet_message.currentbit = c;\n\n\t\t\tc = MSG_ReadByte ();\n\n\t\t\tif (c == A2C_PRINT)\t//qw server reply.\n\t\t\t{\n\t\t\t\tCL_ReadServerInfo(MSG_ReadString(), MP_QUAKEWORLD, false);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (c == M2C_MASTER_REPLY)\t//qw master reply.\n\t\t\t{\n\t\t\t\tCL_MasterListParse(NA_IP, SS_QUAKEWORLD, false);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n#ifdef NQPROT\n\t\telse\n\t\t{\t//connected packet? Must be a NQ packet.\n\t\t\tchar name[32];\n\t\t\tchar map[16];\n\t\t\tint users, maxusers;\n\n\t\t\tint control;\n\t\t\tint ccrep;\n\n\t\t\tMSG_BeginReading (&net_message, msg_nullnetprim);\n\t\t\tcontrol = BigLong(*((int *)net_message.data));\n\t\t\tMSG_ReadLong();\n\t\t\tif (control == -1)\n\t\t\t\tcontinue;\n\t\t\tif ((control & (~NETFLAG_LENGTH_MASK)) !=  NETFLAG_CTL)\n\t\t\t\tcontinue;\n\t\t\tif ((control & NETFLAG_LENGTH_MASK) != ret)\n\t\t\t\tcontinue;\n\n\t\t\tccrep = MSG_ReadByte();\n\n\t\t\tif (ccrep == CCREP_PLAYER_INFO)\n\t\t\t{\n\t\t\t\tserverinfo_t *selserver = selectedserver.inuse?Master_InfoForServer(&selectedserver.adr, selectedserver.brokerid):NULL;\n\t\t\t\tserverinfo_t *info = Master_InfoForServer(&net_from, NULL);\n\t\t\t\tinfo = Master_InfoForServer(&net_from, NULL);\n\t\t\t\tif (selserver == info)\n\t\t\t\t{\n\t\t\t\t\tchar playeraddrbuf[256];\n\t\t\t\t\tint playernum = MSG_ReadByte();\n\t\t\t\t\tchar *playername = MSG_ReadString();\n\t\t\t\t\tint playercolor = MSG_ReadLong();\n\t\t\t\t\tint playerfrags = MSG_ReadLong();\n\t\t\t\t\tint secsonserver = MSG_ReadLong();\n\t\t\t\t\tchar *playeraddr = MSG_ReadStringBuffer(playeraddrbuf, sizeof(playeraddrbuf));\n\t\t\t\t\tif (msg_badread)\n\t\t\t\t\t\tcontinue;\n\n\t\t\t\t\t//might as well\n\t\t\t\t\tIPLog_Add(playeraddr, playername);\n\n\t\t\t\t\tselectedserver.lastplayer = playernum+1;\n\n\t\t\t\t\tmemset(&info->moreinfo->players[playernum], 0, sizeof(info->moreinfo->players[playernum]));\n\t\t\t\t\tinfo->moreinfo->players[playernum].userid = 0;\n\t\t\t\t\tinfo->moreinfo->players[playernum].frags = playerfrags;\n\t\t\t\t\tinfo->moreinfo->players[playernum].time = secsonserver;\n\t\t\t\t\tinfo->moreinfo->players[playernum].ping = 0;\t//*sigh*\n\t\t\t\t\tQ_strncpyz(info->moreinfo->players[playernum].name, playername, sizeof(info->moreinfo->players[playernum].name));\n\t\t\t\t\tQ_strncpyz(info->moreinfo->players[playernum].skin, \"\", sizeof(info->moreinfo->players[playernum].skin));\n\t\t\t\t\tQ_strncpyz(info->moreinfo->players[playernum].team, \"\", sizeof(info->moreinfo->players[playernum].team));\n\t\t\t\t\tinfo->moreinfo->players[playernum].topc = playercolor>>4;\n\t\t\t\t\tinfo->moreinfo->players[playernum].botc = playercolor&15;\n\t\t\t\t\tinfo->moreinfo->players[playernum].isspec = false;\n\t\t\t\t\tinfo->moreinfo->numplayers = max(info->moreinfo->numplayers, playernum+1);\n\n\t\t\t\t\t//... and now try to query the next one... because everyone gives up after the first, right?... dude... I hate this shit.\n\t\t\t\t\tSZ_Clear(&net_message);\n\t\t\t\t\tMSG_WriteLong(&net_message, 0);// save space for the header, filled in later\n\t\t\t\t\tMSG_WriteByte(&net_message, CCREQ_PLAYER_INFO);\n\t\t\t\t\tMSG_WriteByte(&net_message, selectedserver.lastplayer);\n\t\t\t\t\t*((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));\n\t\t\t\t\tNET_SendPollPacket(net_message.cursize, net_message.data, info->adr);\n\t\t\t\t\tSZ_Clear(&net_message);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (ccrep == CCREP_RULE_INFO)\n\t\t\t{\n\t\t\t\tserverinfo_t *selserver = selectedserver.inuse?Master_InfoForServer(&selectedserver.adr, selectedserver.brokerid):NULL;\n\t\t\t\tserverinfo_t *info = Master_InfoForServer(&net_from, NULL);\n\t\t\t\tchar *s, *old;\n\t\t\t\tinfo = Master_InfoForServer(&net_from, NULL);\n\t\t\t\tif (selserver == info)\n\t\t\t\t{\n\t\t\t\t\ts = MSG_ReadString();\n\t\t\t\t\tif (msg_badread)\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\tQ_strncpyz(selectedserver.lastrule, s, sizeof(selectedserver.lastrule));\n\t\t\t\t\ts = MSG_ReadString();\n\t\t\t\t\t\n\t\t\t\t\told = Info_ValueForKey(info->moreinfo->info, selectedserver.lastrule);\n\t\t\t\t\tif (strcmp(s, old) && !strchr(s, '\\\"') && !strchr(s, '\\\\'))\n\t\t\t\t\t\tInfo_SetValueForStarKey(info->moreinfo->info, selectedserver.lastrule, s, sizeof(info->moreinfo->info));\n\n\t\t\t\t\t//... and now try to query the next one... because everyone gives up after the first, right?... dude... I hate this shit.\n\t\t\t\t\tSZ_Clear(&net_message);\n\t\t\t\t\tMSG_WriteLong(&net_message, 0);// save space for the header, filled in later\n\t\t\t\t\tMSG_WriteByte(&net_message, CCREQ_RULE_INFO);\n\t\t\t\t\tMSG_WriteString(&net_message, selectedserver.lastrule);\n\t\t\t\t\t*((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));\n\t\t\t\t\tNET_SendPollPacket(net_message.cursize, net_message.data, info->adr);\n\t\t\t\t\tSZ_Clear(&net_message);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (ccrep == CCREP_SERVER_INFO)\n\t\t\t{\n\t\t\t\t/*this is an address string sent from the server. its not usable. if its replying to serverinfos, its possible to send it connect requests, while the address that it claims is 50% bugged*/\n\t\t\t\tMSG_ReadString();\n\n\t\t\t\tQ_strncpyz(name, MSG_ReadString(), sizeof(name));\n\t\t\t\tQ_strncpyz(map, MSG_ReadString(), sizeof(map));\n\t\t\t\tusers = MSG_ReadByte();\n\t\t\t\tmaxusers = MSG_ReadByte();\n\t\t\t\tif (MSG_ReadByte() != NQ_NETCHAN_VERSION)\n\t\t\t\t{\n//\t\t\t\t\tQ_strcpy(name, \"*\");\n//\t\t\t\t\tQ_strcat(name, name);\n\t\t\t\t}\n\n\t\t\t\tCL_ReadServerInfo(va(\"\\\\hostname\\\\%s\\\\map\\\\%s\\\\maxclients\\\\%i\\\\clients\\\\%i\", name, map, maxusers, users), MP_NETQUAKE, false);\n\t\t\t}\n\t\t}\n#endif\n\t\tcontinue;\n\t}\n}\n#endif\n\nvoid Master_RemoveKeepInfo(serverinfo_t *sv)\n{\n\tsv->special &= ~SS_KEEPINFO;\n\tif (sv->moreinfo)\n\t{\n\t\tZ_Free(sv->moreinfo);\n\t\tsv->moreinfo = NULL;\n\t}\n}\n\nvoid SListOptionChanged(serverinfo_t *newserver)\n{\n\tif (selectedserver.inuse)\n\t{\n\t\tserverinfo_t *oldserver;\n\n\t\tselectedserver.detail = NULL;\n\n\t\tif (!slist_cacheinfo.value)\t//we have to flush it. That's the rules.\n\t\t{\n\t\t\tfor (oldserver = firstserver; oldserver; oldserver=oldserver->next)\n\t\t\t{\n\t\t\t\tif (NET_CompareAdr(&selectedserver.adr, &oldserver->adr) && !strcmp(selectedserver.brokerid, oldserver->brokerid))\n\t\t\t\t{\n\t\t\t\t\tif (oldserver->moreinfo && oldserver->ping!=PING_UNKNOWN)\n\t\t\t\t\t{\n\t\t\t\t\t\tZ_Free(oldserver->moreinfo);\n\t\t\t\t\t\toldserver->moreinfo = NULL;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (!newserver)\n\t\t\treturn;\n\n\t\tselectedserver.adr = newserver->adr;\n\t\tstrcpy(selectedserver.brokerid, newserver->brokerid);\n\n\t\tif (newserver->moreinfo)\t//we cached it.\n\t\t{\n\t\t\tselectedserver.detail = newserver->moreinfo;\n\t\t\treturn;\n\t\t}\n//we don't know all the info, so send a request for it.\n\t\tselectedserver.detail = newserver->moreinfo = Z_Malloc(sizeof(serverdetailedinfo_t));\n\n\t\tnewserver->moreinfo->numplayers = newserver->players;\n\t\tstrcpy(newserver->moreinfo->info, \"\");\n\t\tInfo_SetValueForKey(newserver->moreinfo->info, \"hostname\", newserver->name, sizeof(newserver->moreinfo->info));\n\n\n\t\tselectedserver.refreshtime = realtime+4;\n#if POLLTOTALSOCKETS>0\n\t\tnewserver->sends++;\n\t\tMaster_QueryServer(newserver);\n\n#if defined(NQPROT)\n\t\tselectedserver.lastplayer = 0;\n\t\t*selectedserver.lastrule = 0;\n\t\tif ((newserver->special&(SS_PROTOCOLMASK|SS_GETINFO)) == SS_NETQUAKE)\n\t\t{\t//start spamming the server to get all of its details. silly protocols.\n\t\t\tSZ_Clear(&net_message);\n\t\t\tnet_message.packing = SZ_RAWBYTES;\n\t\t\tnet_message.currentbit = 0;\n\t\t\tMSG_WriteLong(&net_message, 0);// save space for the header, filled in later\n\t\t\tMSG_WriteByte(&net_message, CCREQ_PLAYER_INFO);\n\t\t\tMSG_WriteByte(&net_message, selectedserver.lastplayer);\n\t\t\t*((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));\n\t\t\tNET_SendPollPacket(net_message.cursize, net_message.data, newserver->adr);\n\t\t\tSZ_Clear(&net_message);\n\t\t\tMSG_WriteLong(&net_message, 0);// save space for the header, filled in later\n\t\t\tMSG_WriteByte(&net_message, CCREQ_RULE_INFO);\n\t\t\tMSG_WriteString(&net_message, selectedserver.lastrule);\n\t\t\t*((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));\n\t\t\tNET_SendPollPacket(net_message.cursize, net_message.data, newserver->adr);\n\t\t\tSZ_Clear(&net_message);\n\t\t}\n#endif\n#endif\n\t}\n}\n\nstatic qboolean MasterInfo_ReadProtocol(serverinfo_t *info, const char *infostring)\n{\n\tchar *token = Info_ValueForKey(infostring, \"protocol\");\n\tif (*token)\n\t{\n\t\t//read the protocol number\n\t\tinfo->protocol = strtoul(token, &token, 0);\n\n\t\t//and try to figure out which filter it should be under.\n\t\tinfo->special &= ~SS_PROTOCOLMASK;\n\t\tif (*token)\n\t\t{\n\t\t\twhile (*token)\n\t\t\t{\n\t\t\t\tif (*token == 'w')\n\t\t\t\t\tinfo->special |= SS_QUAKEWORLD;\n\t\t\t\telse if (*token == 'n' || *token == 'd')\n\t\t\t\t\tinfo->special |= SS_NETQUAKE;\n\t\t\t\telse if (*token == 'x')\n\t\t\t\t\tinfo->special |= SS_QEPROT;\n\n\t\t\t\telse if (*token == 't')\n\t\t\t\t\tinfo->special |= SS_PROXY;\t//qtv\n\t\t\t\telse if (*token == 'r')\n\t\t\t\t{\n#if POLLTOTALSOCKETS>0\n\t\t\t\t\tchar *msg = \"\\xff\\xff\\xff\\xffpingstatus ext\";\n\t\t\t\t\tif (info->peers)\n\t\t\t\t\t{\t//forget the old, to let them timeout.\n\t\t\t\t\t\tZ_Free(info->peers);\n\t\t\t\t\t\tinfo->peers = NULL;\n\t\t\t\t\t\tinfo->numpeers = 0;\n\t\t\t\t\t}\n\t\t\t\t\tNET_SendPollPacket(strlen(msg), msg, info->adr);\n#endif\n\t\t\t\t\tinfo->special |= SS_PROXY|SS_RELAY;\t//qwfwd relay, ask it for its pinglist\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tcontinue;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\telse switch(info->protocol)\n\t\t{\n\t\tcase PROTOCOL_VERSION_QW:\tinfo->special |= SS_QUAKEWORLD;\tbreak;\n#ifdef NQPROT\n\t\tcase PROTOCOL_VERSION_NQ:\tinfo->special |= SS_NETQUAKE;\tbreak;\n\t\tcase PROTOCOL_VERSION_H2:\tinfo->special |= SS_NETQUAKE;\tbreak;\t//erk\n\t\tcase PROTOCOL_VERSION_NEHD:\tinfo->special |= SS_NETQUAKE;\tbreak;\n\t\tcase PROTOCOL_VERSION_FITZ:\tinfo->special |= SS_NETQUAKE;\tbreak;\n\t\tcase PROTOCOL_VERSION_RMQ:\tinfo->special |= SS_NETQUAKE;\tbreak;\n\t\tcase PROTOCOL_VERSION_DP5:\tinfo->special |= SS_NETQUAKE;\tbreak;\t//dp actually says 3... but hey, that's dp being WEIRD.\n\t\tcase PROTOCOL_VERSION_DP6:\tinfo->special |= SS_NETQUAKE;\tbreak;\n\t\tcase PROTOCOL_VERSION_DP7:\tinfo->special |= SS_NETQUAKE;\tbreak;\n\t\tcase NQ_NETCHAN_VERSION_QEX:info->special |= SS_QEPROT;\t\tbreak;\n\t\tcase NQ_NETCHAN_VERSION:\n#endif\n\t\tdefault:\n\t\t\tif ((info->special&SS_PROTOCOLMASK) == SS_UNKNOWN)\n\t\t\t{\t//guesses...\n\t\t\t\tif (PROTOCOL_VERSION_Q2 >= info->protocol && info->protocol >= PROTOCOL_VERSION_Q2_MIN)\n\t\t\t\t\tinfo->special |= SS_QUAKE2;\t//q2 has a range!\n\t\t\t\telse if (info->protocol > 60)\n\t\t\t\t\tinfo->special |= SS_QUAKE3;\n\t\t\t\telse if (!strcmp(Info_ValueForKey(infostring, \"gamename\"), \"DarkPlaces-Quake\") || *Info_ValueForKey(infostring, \"nqprotocol\"))\n\t\t\t\t\tinfo->special |= SS_NETQUAKE;\n\t\t\t\telse\n\t\t\t\t\tinfo->special |= SS_QUAKEWORLD;\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t\treturn true;\n\t}\n\tinfo->protocol = 0;\n\treturn false;\n}\n\n#ifdef WEBCLIENT\nstatic void MasterInfo_ProcessHTTPInfo(serverinfo_t *srv, const char *info)\n{\n\tchar adrbuf[MAX_ADR_SIZE];\n\tif (info && (!(srv->status & SRVSTATUS_ALIVE) || srv->ping == PING_UNKNOWN))\n\t{\n\t\tif (srv->adr.prot != NP_DGRAM)\n\t\t{\n\t\t\tsrv->sends = 0;\t//no point pinging it, it won't work.\n\t\t\tsrv->ping = PING_UNKNOWN;\n\t\t\tsrv->status |= SRVSTATUS_ALIVE;\t//or at least wouldn't have been reported this time around.\n\t\t}\n\n\t\tQ_strncpyz(srv->name, Info_ValueForKey(info, \"hostname\"), sizeof(srv->name));\n\t\tQ_strncpyz(srv->gamedir, Info_ValueForKey(info, \"modname\"), sizeof(srv->gamedir));\n\t\tQ_strncpyz(srv->map, Info_ValueForKey(info, \"mapname\"), sizeof(srv->map));\n\t\tsrv->players = atoi(Info_ValueForKey(info, \"clients\"));\n\t\tsrv->maxplayers = atoi(Info_ValueForKey(info, \"maxclients\"));\n\n\t\tif (!MasterInfo_ReadProtocol(srv, info))\n\t\t\tsrv->special = (srv->special&~SS_PROTOCOLMASK)|SS_UNKNOWN;\t//assume its an older fteqw server.\n\n\t\tsrv->numbots = 0;\n\t\tsrv->numhumans = srv->players - srv->numbots;\n\t\tsrv->freeslots = srv->maxplayers - srv->players;\n\n\n\t\tif (!srv->moreinfo)// && ((slist_cacheinfo.value == 2 || (NET_CompareAdr(&srv->adr, &selectedserver.adr)&&!strcmp(srv->brokerid,selectedserver.brokerid))) || (srv->special & SS_KEEPINFO)))\n\t\t\tsrv->moreinfo = Z_Malloc(sizeof(serverdetailedinfo_t));\n\t\tif (srv->moreinfo)\n\t\t\tQ_strncpyz(srv->moreinfo->info, info, sizeof(srv->moreinfo->info));\n\t}\n\tif (!*srv->name)\n\t\tQ_snprintfz(srv->name, sizeof(srv->name), \"%s h\", Master_ServerToString(adrbuf, sizeof(adrbuf), srv));\n}\nstatic void MasterInfo_ProcessHTTP(struct dl_download *dl)\n{\n\tmaster_t *mast = dl->user_ctx;\n\tvfsfile_t *file = dl->file;\n\tint protocoltype;\n\tnetadr_t adr;\n\tchar *s;\n\tchar *el;\n\tserverinfo_t *info;\n\tchar linebuffer[2048];\n\tconst char *brokerid;\n\tchar *infostring;\n\tnetadr_t brokeradr;\n\n\tif (mast)\n\t{\n\t\tbrokeradr = mast->adr;\n\t\tmast->dl = NULL;\n\t\tprotocoltype = mast->protocoltype;\n\t}\n\telse\n\t{\n#ifdef Q3CLIENT\n\t\tNET_StringToAdr(\"/\", PORT_ICEBROKER, &brokeradr);\n\t\tprotocoltype = MP_QUAKE3;\n#else\n\t\treturn;\n#endif\n\t}\n\n\tif (!file)\n\t\treturn;\n\n\tbrokeradr.type = NA_INVALID;\t//should be the default broker...\n\tbrokeradr.prot = NP_RTC_TLS;\n\tfor (info = firstserver; info; info = info->next)\n\t{\n\t\tif (NET_CompareAdr(&info->adr, &brokeradr))\n\t\t{\n\t\t\tinfo->ping = PING_DEAD;\n\t\t\tinfo->status &= ~SRVSTATUS_ALIVE;\n\t\t}\n\t}\n\n\twhile(VFS_GETS(file, linebuffer, sizeof(linebuffer)))\n\t{\n\t\ts = linebuffer;\n\t\twhile (*s == '\\t' || *s == ' ')\n\t\t\ts++;\n\n\t\tel = s + strlen(s);\n\t\tif (el>s && el[-1] == '\\r')\n\t\t\tel[-1] = '\\0';\n\n\t\tif (*s == '#')\t//hash is a comment, apparently.\n\t\t\tcontinue;\n\n\t\tfor (infostring = s; *infostring && *infostring != ' ' && *infostring != '\\t'; )\n\t\t\tinfostring++;\n\t\tif (*infostring == ' ' || *infostring == '\\t')\n\t\t{\n\t\t\t*infostring++ = 0;\t//null terminate the address\n\t\t\twhile(*infostring == ' ' || *infostring == '\\t')\n\t\t\t\tinfostring++;\t//skip over any whitespace...\n\t\t\tif (*infostring != '\\\\')\n\t\t\t\tinfostring = NULL;\t//err... no. not an info string. probably a comment.\n\t\t}\n\t\telse\n\t\t\tinfostring = NULL;\n\n\t\tif (!strncmp(s, \"ice:///\", 7) || !strncmp(s, \"ices:///\", 8) || !strncmp(s, \"rtc:///\", 7) || !strncmp(s, \"rtcs:///\", 8))\n\t\t{\t//implicitly using the ip:port of the responder, instead of that being specified (giving a consistent route to it instead of it having to guess what hostname we used).\n\t\t\tbrokerid = s+((s[4]==':')?7:6);\n\t\t\tadr = brokeradr;\n\t\t\tif (!*brokerid)\n\t\t\t\tcontinue;\t//invalid...\n\t\t}\n\t\telse if (*s == '/')\n\t\t{\n\t\t\tbrokerid = s;\n\t\t\tadr = brokeradr;\n\t\t}\n#ifndef HAVE_PACKET\n\t\telse if ((*s=='[' || (*s >= '0' && *s <= '9')) && infostring)\n\t\t{\t//if we don't have support for udp packets here, convert any raw address to an rtc:///udp/ADDRESS one instead, via this master's brokering services... hopefully.\n\t\t\tbrokerid = va(\"/udp/%s\", s);\n\t\t\tadr = brokeradr;\n\t\t}\n#endif\n\t\telse\n\t\t{\n\t\t\tif (!NET_StringToAdr2(s, 80, &adr, 1, &brokerid))\n\t\t\t\tcontinue;\n\t\t}\n\n\t\tif ((info = Master_InfoForServer(&adr, brokerid)))\t//remove if the server already exists.\n\t\t{\n\t\t\tinfo->sends = 1;\t//reset.\n\t\t\tMasterInfo_ProcessHTTPInfo(info, infostring);\n\n\t\t\tMaster_ResortServer(info);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tinfo = Z_Malloc(sizeof(serverinfo_t));\n\t\t\tinfo->adr = adr;\n\t\t\tinfo->sends = 1;\n\t\t\tQ_strncpyz(info->brokerid, brokerid?brokerid:\"\", sizeof(info->brokerid));\n\n\t\t\tinfo->special = 0;\n\t\t\tif (protocoltype == MP_QUAKEWORLD)\n\t\t\t\tinfo->special |= SS_QUAKEWORLD;\n\t\t\telse if (protocoltype == MP_DPMASTER)\t//actually ftemaster... so assume fteqw servers not ftenq ones unless otherwise indicated.\n\t\t\t\tinfo->special |= SS_UNKNOWN|SS_GETINFO;\n#if defined(Q2CLIENT) || defined(Q2SERVER)\n\t\t\telse if (protocoltype == MP_QUAKE2)\n\t\t\t\tinfo->special |= SS_QUAKE2;\n#endif\n#if defined(Q3CLIENT) || defined(Q3SERVER)\n\t\t\telse if (protocoltype == MP_QUAKE3)\n\t\t\t\tinfo->special |= SS_QUAKE3;\n#endif\n#ifdef NQPROT\n\t\t\telse if (protocoltype == MP_NETQUAKE)\n\t\t\t\tinfo->special |= SS_NETQUAKE;\n#endif\n\n\t\t\tinfo->refreshtime = 0;\n\t\t\tinfo->ping = PING_DEAD;\n\n\t\t\tMasterInfo_ProcessHTTPInfo(info, infostring);\n\n\t\t\tinfo->next = firstserver;\n\t\t\tfirstserver = info;\n\n\t\t\tMaster_ResortServer(info);\n\t\t}\n\t\tinfo->status |= SRVSTATUS_GLOBAL;\n\t}\n}\n#endif\n\n//don't try sending to servers we don't support\nstatic void MasterInfo_Request(master_t *mast)\n{\n\tif (!mast)\n\t\treturn;\n\n\tif (mast->sends)\n\t\tmast->sends--;\n\n\t//these are generic requests\n\tswitch(mast->mastertype)\n\t{\n#ifdef WEBCLIENT\n\tcase MT_MASTERHTTP:\n\t\tif (!mast->dl)\n\t\t{\n\t\t\tmast->dl = HTTP_CL_Get(mast->address, NULL, MasterInfo_ProcessHTTP);\n\t\t\tif (mast->dl)\n\t\t\t{\n\t\t\t\tmast->dl->user_ctx = mast;\n\t\t\t\tmast->dl->isquery = true;\n\t\t\t}\n\t\t}\n\t\tbreak;\n#endif\n#if POLLTOTALSOCKETS>0\n\tcase MT_MASTERUDP:\n\t\tswitch(mast->protocoltype)\n\t\t{\n#ifdef Q3CLIENT\n\t\tcase MP_QUAKE3:\n\t\t\t{\n\t\t\t\tchar *str;\n\t\t\t\tif (mast->adr.type == NA_IPV6)\n\t\t\t\t\tstr = va(\"%c%c%c%cgetserversExt %u empty full ipv6\\n\", 255, 255, 255, 255, 68);\n\t\t\t\telse\n\t\t\t\t\tstr = va(\"%c%c%c%cgetservers %u empty full\\n\", 255, 255, 255, 255, 68);\n\t\t\t\tNET_SendPollPacket (strlen(str), str, mast->adr);\n\t\t\t}\n\t\t\tbreak;\n#endif\n#ifdef Q2CLIENT\n\t\tcase MP_QUAKE2:\n\t\t\tif (mast->adr.type == NA_IP)\t//qw masters have no ipx/ipv6 reporting, so its a bit pointless\n\t\t\t\tNET_SendPollPacket (6, \"query\", mast->adr);\n\t\t\tbreak;\n#endif\n\t\tcase MP_QUAKEWORLD:\n\t\t\tif (mast->adr.type == NA_IP)\t//qw masters have no ipx/ipv6 reporting, so its a bit pointless\n\t\t\t\tNET_SendPollPacket (3, \"c\\n\", mast->adr);\n\t\t\tbreak;\n#ifdef NQPROT\n\t\tcase MP_NETQUAKE:\n\t\t\t//there is no nq udp master protocol\n\t\t\tbreak;\n#endif\n\t\tcase MP_DPMASTER:\n\t\t\t{\n\t\t\t\tchar *str;\n\t\t\t\tchar game[MAX_QPATH];\n\t\t\t\tchar *games = com_protocolname.string;\n\t\t\t\twhile(*games)\n\t\t\t\t{\t//send a request for each game listed.\n\t\t\t\t\tgames = COM_ParseOut(games, game, sizeof(game));\n\n\t\t\t\t\t//for compat with dp, we use the nq netchan version. which is stupid, but whatever\n\t\t\t\t\t//we ask for ipv6 addresses from ipv6 masters (assuming it resolved okay)\n\t\t\t\t\tif (mast->adr.type == NA_IPV6)\n\t\t\t\t\t\tstr = va(\"%c%c%c%cgetserversExt %s %u empty full ipv6\", 255, 255, 255, 255, game, com_protocolversion.ival);\n\t\t\t\t\telse\n\t\t\t\t\t\tstr = va(\"%c%c%c%cgetservers %s %u empty full\", 255, 255, 255, 255, game, com_protocolversion.ival);\n\t\t\t\t\tNET_SendPollPacket (strlen(str), str, mast->adr);\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t\tbreak;\n\tcase MT_BCAST:\n\tcase MT_SINGLE:\t//FIXME: properly add the server and flag it for resending instead of directly pinging it\n\t\tswitch(mast->protocoltype)\n\t\t{\n#ifdef Q3CLIENT\n\t\tcase MP_QUAKE3:\n\t\t\tNET_SendPollPacket (14, va(\"%c%c%c%cgetstatus\\n\", 255, 255, 255, 255), mast->adr);\n\t\t\tbreak;\n#endif\n#ifdef Q2CLIENT\n\t\tcase MP_QUAKE2:\n\t\t\tNET_SendPollPacket (11, va(\"%c%c%c%cstatus\\n\", 255, 255, 255, 255), mast->adr);\n\t\t\tbreak;\n#endif\n\t\tcase MP_QUAKEWORLD:\n\t\t\tNET_SendPollPacket (14, va(\"%c%c%c%cstatus %i\\n\", 255, 255, 255, 255, STATUS_QTVLIST|STATUS_SHOWTEAMS|STATUS_SPECTATORS|STATUS_PLAYERS|STATUS_SERVERINFO), mast->adr);\n\t\t\tbreak;\n#ifdef NQPROT\n\t\tcase MP_NETQUAKE:\n\t\t\tSZ_Clear(&net_message);\n\t\t\tnet_message.packing = SZ_RAWBYTES;\n\t\t\tnet_message.currentbit = 0;\n\t\t\tMSG_WriteLong(&net_message, 0);// save space for the header, filled in later\n\t\t\tMSG_WriteByte(&net_message, CCREQ_SERVER_INFO);\n\t\t\tMSG_WriteString(&net_message, NET_GAMENAME_NQ);\t//look for either sort of server\n\t\t\tMSG_WriteByte(&net_message, NQ_NETCHAN_VERSION);\n\t\t\t*((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));\n\t\t\tNET_SendPollPacket(net_message.cursize, net_message.data, mast->adr);\n\t\t\tSZ_Clear(&net_message);\n\t\t\tbreak;\n#endif\n\t\tcase MP_DPMASTER:\t//fixme\n\t\t\t{\n\t\t\t\tchar *str;\n\t\t\t\tstr = va(\"%c%c%c%cgetinfo\", 255, 255, 255, 255);\n\t\t\t\tNET_SendPollPacket (strlen(str), str, mast->adr);\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t\tbreak;\n#endif\n\t}\n}\n\n\nvoid MasterInfo_WriteServers(void)\n{\n\tserverinfo_t *server;\n\tvfsfile_t *qws;\n\tchar adr[MAX_ADR_SIZE];\n\n\tif (slist_writeserverstxt.ival && sb_favouriteschanged)\n\t{\n\t\tqws = FS_OpenVFS(FAVOURITESFILE, \"wt\", FS_ROOT);\n\t\tif (qws)\n\t\t{\n\t\t\tsb_favouriteschanged = false;\n\t\t\tfor (server = firstserver; server; server = server->next)\n\t\t\t{\n\t\t\t\tif (server->special & SS_FAVORITE)\n\t\t\t\t{\n\t\t\t\t\tswitch(server->special & SS_PROTOCOLMASK)\n\t\t\t\t\t{\n\t\t\t\t\tcase SS_QUAKE3:\n\t\t\t\t\t\tVFS_PUTS(qws, va(\"%s\\t%s\\t%s\\n\", Master_ServerToString(adr, sizeof(adr), server), \"favorite:q3\", server->name));\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase SS_QUAKE2:\n\t\t\t\t\t\tVFS_PUTS(qws, va(\"%s\\t%s\\t%s\\n\", Master_ServerToString(adr, sizeof(adr), server), \"favorite:q2\", server->name));\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase SS_NETQUAKE:\n\t\t\t\t\t\tVFS_PUTS(qws, va(\"%s\\t%s\\t%s\\n\", Master_ServerToString(adr, sizeof(adr), server), \"favorite:nq\", server->name));\n\t\t\t\t\t\tbreak;\n//\t\t\t\t\tcase SS_DARKPLACES:\n//\t\t\t\t\t\tVFS_PUTS(qws, va(\"%s\\t%s\\t%s\\n\", Master_ServerToString(adr, sizeof(adr), server), \"favorite:dp\", server->name));\n//\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase SS_QUAKEWORLD:\n\t\t\t\t\t\tVFS_PUTS(qws, va(\"%s\\t%s\\t%s\\n\", Master_ServerToString(adr, sizeof(adr), server), \"favorite:qw\", server->name));\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tVFS_CLOSE(qws);\n\t\t}\n\t}\n}\n\n//poll master servers for server lists.\nvoid MasterInfo_Refresh(qboolean doreset)\n{\n\tmaster_t *mast;\n\tserverinfo_t *info;\n\tqboolean loadedone;\n\n\tif (doreset)\n\t{\n\t\tfor (info = firstserver; info; info = info->next)\n\t\t\tinfo->status &= ~SRVSTATUS_ALIVE;\t//hide until we get a new response from it.\n\t}\n\n\tloadedone = false;\n\tloadedone |= Master_LoadMasterList(\"masters.txt\", false, MT_MASTERUDP, MP_QUAKEWORLD, 5);\t//fte listing\n\n\tMaster_LoadMasterList(FAVOURITESFILE, false, MT_MASTERUDP, MP_QUAKEWORLD, 1);\n\n\tif (!loadedone)\n\t{\n\t\tint i;\n\n\t\tMaster_LoadMasterList(\"sources.txt\", true, MT_MASTERUDP, MP_QUAKEWORLD, 5);\t//merge with ezquake compat listing\n\n\t\tMaster_LoadMasterList(\"servers.txt\", false, MT_MASTERUDP, MP_QUAKEWORLD, 1);\n\n\t\tMaster_AddMaster(\"255.255.255.255:\"STRINGIFY(PORT_DEFAULTSERVER),\t\t\tMT_BCAST,\t\t\tMP_DPMASTER, \"Nearby Game Servers.\");\n#ifndef QUAKETC\n\t\tMaster_AddMaster(\"255.255.255.255:\"STRINGIFY(PORT_QWSERVER),\t\t\t\tMT_BCAST,\t\t\tMP_QUAKEWORLD, \"Nearby QuakeWorld UDP servers.\");\n//\t\tMaster_AddMasterHTTP(\"http://www.gameaholic.com/servers/qspy-quakeworld\",\tMT_MASTERHTTP,\t\tMP_QUAKEWORLD, \"gameaholic's QW master\");\n//\t\tMaster_AddMasterHTTP(\"https://www.quakeservers.net/lists/servers/global.txt\",MT_MASTERHTTP,\t\tMP_QUAKEWORLD, \"QuakeServers.net (http)\");\n#endif\n#ifdef NQPROT\n//\t\tMaster_AddMasterHTTP(\"http://www.gameaholic.com/servers/qspy-quake\",\t\tMT_MASTERHTTP,\t\tMP_NETQUAKE, \"gameaholic's NQ master\");\n//\t\tMaster_AddMasterHTTP(\"http://servers.quakeone.com/index.php?format=json\",\tMT_MASTERHTTPJSON,\tMP_NETQUAKE, \"quakeone's server listing\");\n\t\tMaster_AddMaster(\"255.255.255.255:\"STRINGIFY(PORT_NQSERVER),\t\t\t\tMT_BCAST,\t\t\tMP_NETQUAKE, \"Nearby Quake1 servers\");\n\t\tMaster_AddMaster(\"255.255.255.255:\"STRINGIFY(PORT_NQSERVER),\t\t\t\tMT_BCAST,\t\t\tMP_DPMASTER, \"Nearby DarkPlaces servers\");\t//only responds to one type, depending on active protocol.\n#endif\n#ifdef Q2CLIENT\n//\t\tMaster_AddMasterHTTP(\"http://www.gameaholic.com/servers/qspy-quake2\",\t\tMT_MASTERHTTP,\t\tMP_QUAKE2, \"gameaholic's Q2 master\");\n\t\tMaster_AddMasterHTTP(\"http://q2servers.com/?raw=1\",\t\t\t\t\t\t\tMT_MASTERHTTP,\t\tMP_QUAKE2, \"q2servers.com\");\t//https is fucked. binary version is defective as it has no way to represent ipv6, so don't use that.\n\t\tMaster_AddMaster(\"255.255.255.255:27910\",\t\t\t\t\t\t\t\t\tMT_BCAST,\t\t\tMP_QUAKE2, \"Nearby Quake2 UDP servers.\");\n#endif\n#ifdef Q3CLIENT\n//\t\tMaster_AddMasterHTTP(\"http://www.gameaholic.com/servers/qspy-quake3\",\t\tMT_MASTERHTTP,\t\tMP_QUAKE3, \"gameaholic's Q3 master\");\n\t\tMaster_AddMaster(\"255.255.255.255:\"STRINGIFY(PORT_Q3SERVER),\t\t\t\tMT_BCAST,\t\t\tMP_QUAKE3, \"Nearby Quake3 UDP servers.\");\n#endif\n\n\t\tif (!*net_ice_broker.string)\n\t\t\t;\t//nope, sorry, not configured.\n\t\telse\n\t\t{\n\t\t\tchar *url;\n\t\t\tCOM_Parse(com_protocolname.string);\n\t\t\tif (!strncmp(net_ice_broker.string, \"tls://\", 6))\n\t\t\t\turl = va(\"https://%s/raw/%s\", net_ice_broker.string+6, com_token);\n\t\t\telse if (!strncmp(net_ice_broker.string, \"tcp://\", 6))\n\t\t\t\turl = va(\"http://%s/raw/%s\", net_ice_broker.string+6, com_token);\n\t\t\telse\n\t\t\t\turl = va(\"http://%s/raw/%s\", net_ice_broker.string, com_token);\n\t\t\tMaster_AddMasterHTTP(url,\t\t\t\tMT_MASTERHTTP,\t\tMP_DPMASTER, \"Public Servers Potentially Behind A NAT.\");\n\t\t}\n\n\t\tfor (i = 0; net_masterlist[i].cv.name; i++)\n\t\t{\n\t\t\tMaster_AddMaster(net_masterlist[i].cv.string, MT_MASTERUDP, net_masterlist[i].protocol, net_masterlist[i].comment);\n\t\t}\n\t}\n\n\n\tfor (mast = master; mast; mast=mast->next)\n\t{\n\t\tmast->sends = 1;\n\t}\n\n\tMaster_SortServers();\n}\n\n#if POLLTOTALSOCKETS>0\nvoid Master_QueryServer(serverinfo_t *server)\n{\n\tchar\tdata[2048];\n\tserver->sends--;\n\tif (*server->brokerid)\n\t\treturn;\t//don't even try. we have no direct route.\n\tserver->refreshtime = Sys_DoubleTime();\n\n\tif (server->special & SS_GETINFO)\n\t{\n\t\tif (server->moreinfo)\n\t\t\tQ_snprintfz(data, sizeof(data), \"%c%c%c%cgetstatus\", 255, 255, 255, 255);\n\t\telse\n\t\t\tQ_snprintfz(data, sizeof(data), \"%c%c%c%cgetinfo\", 255, 255, 255, 255);\n\t}\n\telse switch(server->special & SS_PROTOCOLMASK)\n\t{\n\tcase SS_QUAKE3:\n\t\tQ_snprintfz(data, sizeof(data), \"%c%c%c%cgetstatus\", 255, 255, 255, 255);\n\t\tbreak;\n#ifdef NQPROT\n\tcase SS_NETQUAKE:\n\t\tSZ_Clear(&net_message);\n\t\tnet_message.packing = SZ_RAWBYTES;\n\t\tnet_message.currentbit = 0;\n\t\tMSG_WriteLong(&net_message, 0);// save space for the header, filled in later\n\t\tMSG_WriteByte(&net_message, CCREQ_SERVER_INFO);\n\t\tMSG_WriteString(&net_message, NET_GAMENAME_NQ);\t//look for either sort of server\n\t\tMSG_WriteByte(&net_message, NQ_NETCHAN_VERSION);\n\t\t*((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));\n\t\tNET_SendPollPacket(net_message.cursize, net_message.data, server->adr);\n\t\tSZ_Clear(&net_message);\n\t\treturn;\n#endif\n\tcase SS_QUAKEWORLD:\n\t\tQ_snprintfz(data, sizeof(data), \"%c%c%c%cstatus %i\\n\", 255, 255, 255, 255, STATUS_QTVLIST|STATUS_SHOWTEAMS|STATUS_SPECTATORS|STATUS_PLAYERS|STATUS_SERVERINFO);\n\t\tbreak;\n\tcase SS_QUAKE2:\n\t\tQ_snprintfz(data, sizeof(data), \"%c%c%c%cstatus\\n\", 255, 255, 255, 255);\n\t\tbreak;\n\tdefault:\n\t\treturn;\n\t}\n\tif (!NET_SendPollPacket (strlen(data), data, server->adr))\n\t\tserver->sends++; //if we failed, just try again later\n}\n//send a packet to each server in sequence.\nqboolean CL_QueryServers(void)\n{\n\tstatic int poll;\n\tint op;\n\tserverinfo_t *server;\n\tmaster_t *mast;\n\n\tMaster_DetermineMasterTypes();\n\n\top = poll;\n\n\tfor (mast = master; mast; mast=mast->next)\n\t{\n\t\tswitch (mast->protocoltype)\n\t\t{\n\t\tcase MP_UNSPECIFIED:\n\t\t\tcontinue;\n\t\tcase MP_DPMASTER:\t//dpmaster allows the client to specify the protocol to query. this means it always matches the current game type, so don't bother allowing the user to disable it.\n\t\t\tif (!sb_enabledarkplaces)\n\t\t\t\tcontinue;\n\t\t\tbreak;\n#ifdef NQPROT\n\t\tcase MP_NETQUAKE:\n\t\t\tif (!sb_enablenetquake)\n\t\t\t\tcontinue;\n\t\t\tbreak;\n#endif\n\t\tcase MP_QUAKEWORLD:\n\t\t\tif (!sb_enablequakeworld)\n\t\t\t\tcontinue;\n\t\t\tbreak;\n#ifdef Q2CLIENT\n\t\tcase MP_QUAKE2:\n\t\t\tif (!sb_enablequake2)\n\t\t\t\tcontinue;\n\t\t\tbreak;\n#endif\n#ifdef Q3CLIENT\n\t\tcase MP_QUAKE3:\n\t\t\tif (!sb_enablequake3)\n\t\t\t\tcontinue;\n\t\t\tbreak;\n#endif\n\t\t}\n\n\t\tif (mast->sends > 0)\n\t\t\tMasterInfo_Request(mast);\n\t}\n\n\n\tfor (server = firstserver; op>0 && server; server=server->next, op--);\n\n\tif (!server)\n\t{\n\t\tpoll = 0;\n\t\treturn false;\n\t}\n\n\tif (op == 0)\n\t{\n\n\t\t//we only want to send poll packets to servers which will not be filtered (otherwise it's pointless)\n\t\twhile(server)\n\t\t{\n\t\t\tqboolean enabled;\n\t\t\tswitch(Master_BaseGame(server))\n\t\t\t{\n\t\t\tcase SS_UNKNOWN: enabled = true; break;\n\t\t\tcase SS_QUAKE3: enabled = sb_enablequake3; break;\n\t\t\tcase SS_QUAKE2: enabled = sb_enablequake2; break;\n\t\t\tcase SS_NETQUAKE: enabled = sb_enablenetquake; break;\n\t\t\tcase SS_QUAKEWORLD: enabled = sb_enablequakeworld; break;\n\t\t\tdefault: enabled = false; break;\n\t\t\t}\n\t\t\tif (enabled)\n\t\t\t{\n\t\t\t\tif (server && server->sends > 0)\n\t\t\t\t{\n\t\t\t\t\tMaster_QueryServer(server);\n\t\t\t\t\tpoll++;\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t\tserver = server->next;\n\t\t\tpoll++;\n\t\t}\n\t\tif (!server)\n\t\t{\n\t\t\tpoll = 0;\n\t\t\tserver = firstserver;\n\t\t\twhile (server)\n\t\t\t{\n\t\t\t\tqboolean enabled;\n\t\t\t\tswitch(Master_BaseGame(server))\n\t\t\t\t{\n\t\t\t\tcase SS_UNKNOWN: enabled = true; break;\n\t\t\t\tcase SS_QUAKE3: enabled = sb_enablequake3; break;\n\t\t\t\tcase SS_QUAKE2: enabled = sb_enablequake2; break;\n\t\t\t\tcase SS_NETQUAKE: enabled = sb_enablenetquake; break;\n\t\t\t\tcase SS_QUAKEWORLD: enabled = sb_enablequakeworld; break;\n\t\t\t\tdefault: enabled = false; break;\n\t\t\t\t}\n\t\t\t\tif (enabled)\n\t\t\t\t{\n\t\t\t\t\tif (server && server->sends > 0)\n\t\t\t\t\t{\n\t\t\t\t\t\tMaster_QueryServer(server);\n\t\t\t\t\t\tpoll++;\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tserver = server->next;\n\t\t\t\tpoll++;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn false;\n}\n\nvoid MasterInfo_RemovePlayers(netadr_t *adr)\n{\n\tplayer_t *p, *prev;\n\tprev = NULL;\n\tfor (p = mplayers; p; )\n\t{\n\t\tif (NET_CompareAdr(&p->adr, adr))\n\t\t{\n\t\t\tif (prev)\n\t\t\t\tprev->next = p->next;\n\t\t\telse\n\t\t\t\tmplayers = p->next;\n\t\t\tZ_Free(p);\n\t\t\tp=prev;\n\n\t\t\tcontinue;\n\t\t}\n\t\telse\n\t\t\tprev = p;\n\n\t\tp = p->next;\n\t}\n}\n\nvoid MasterInfo_AddPlayer(netadr_t *serveradr, char *name, int ping, int frags, int colours, char *skin, char *team)\n{\n\tplayer_t *p;\n\tp = Z_Malloc(sizeof(player_t));\n\tp->next = mplayers;\n\tp->adr = *serveradr;\n\tp->colour = colours;\n\tp->frags = frags;\n\tQ_strncpyz(p->team, team, sizeof(p->team));\n\tQ_strncpyz(p->name, name, sizeof(p->name));\n\tQ_strncpyz(p->skin, skin, sizeof(p->skin));\n\tmplayers = p;\n}\n\nstatic void CL_ReadPingListEntry(serverinfo_t *info, netadrtype_t type, size_t *maxpeers)\n{\n\tserverinfo_t *peer;\n\tunsigned short ping;\n\tint i;\n\tnetadr_t pa;\n\tchar adr[MAX_ADR_SIZE];\n\tmemset(&pa, 0, sizeof(pa));\n\n\tpa.type = type;\n\tif (type == NA_IP)\n\t{\n\t\tfor (i = 0; i < countof(pa.address.ip); i++)\n\t\t\tpa.address.ip[i] = MSG_ReadByte();\n\t}\n\telse if (type == NA_IPV6)\n\t{\n\t\tfor (i = 0; i < countof(pa.address.ip6); i++)\n\t\t\tpa.address.ip6[i] = MSG_ReadByte();\n\t}\n\telse\n\t{\n\t\tSys_Error(\"CL_ReadPingListEntry: Unsupported netadrtype_t\\n\");\n\t\treturn;\t//error...\n\t}\n\tpa.port = htons(MSG_ReadShort());\t//little endian... stored into a network-endian variable...\n\tping  = MSG_ReadShort();\n\n\tif (NET_ClassifyAddress(&pa, NULL) >= ASCOPE_NET)\n\t{\n\t\tpeer = Master_InfoForServer(&pa, NULL);\n\t\tif (!peer)\n\t\t{\n\t\t\t//generate some lame peer node that we can use.\n\t\t\tpeer = Z_Malloc(sizeof(serverinfo_t));\n\t\t\tpeer->adr = pa;\n\t\t\tpeer->sends = 1;\n\t\t\tpeer->special = SS_QUAKEWORLD;\n\t\t\tpeer->refreshtime = 0;\n\t\t\tpeer->ping = PING_DEAD;\n\t\t\tQ_snprintfz(peer->name, sizeof(peer->name), \"%s p\", Master_ServerToString(adr, sizeof(adr), peer));\n\t\t\tpeer->next = firstserver;\n\t\t\tfirstserver = peer;\n\t\t}\n\n\t\tfor (i = 0; i < info->numpeers; i++)\n\t\t{\n\t\t\tif (info->peers[i].peer == peer)\n\t\t\t\tbreak;\n\t\t}\n\t\tif (i == *maxpeers)\n\t\t{\t//need a new one\n\t\t\tinfo->numpeers = i+1;\n\t\t\tZ_ReallocElements((void**)&info->peers, maxpeers, info->numpeers+64, sizeof(*info->peers));\n\t\t}\n\t\tinfo->peers[i].peer = peer;\n\t\tinfo->peers[i].ping = ping;\n\t}\n}\nstatic void CL_ReadPingList(void)\n{\n\tserverinfo_t *info = Master_InfoForServer(&net_from, NULL);\n\tsize_t count = info->numpeers;\n\tfor(;;)\n\t{\n\t\tint type = MSG_ReadByte();\n\t\tif (type == '\\\\')\n\t\t\tCL_ReadPingListEntry(info, NA_IP, &count);\n\t\telse if (type == '/')\n\t\t\tCL_ReadPingListEntry(info, NA_IPV6, &count);\n\t\telse\n\t\t\tbreak;\t//don't know, don't corrupt it.\n\t}\n\tif (count > info->numpeers)\n\t{\t//trim it...\n\t\tZ_ReallocElements((void**)&info->peers, &count, info->numpeers, sizeof(*info->peers));\n\t\tinfo->numpeers = count;\n\t}\n}\n\n//we got told about a server, parse it's info\nstatic int CL_ReadServerInfo(char *msg, enum masterprotocol_e prototype, qboolean favorite)\n{\n\tserverdetailedinfo_t details;\n\n\tchar *token;\n\tchar *nl;\n\tchar *name;\n\tint ping;\n\tint len, j, k;\n\tserverinfo_t *info;\n\tchar adr[MAX_ADR_SIZE];\n\n\tinfo = Master_InfoForServer(&net_from, NULL);\n\n\tif (!info)\t//not found...\n\t{\n\t\tif (atoi(Info_ValueForKey(msg, \"sv_punkbuster\")))\n\t\t\treturn false;\t//never add servers that require punkbuster. :(\n//\t\tif (atoi(Info_ValueForKey(msg, \"sv_pure\")))\n//\t\t\treturn false;\t//we don't support the filesystem hashing. :(\n\n\t\tinfo = Z_Malloc(sizeof(serverinfo_t));\n\n\t\tinfo->adr = net_from;\n\n\t\tQ_snprintfz(info->name, sizeof(info->name), \"%s ?\", Master_ServerToString(adr, sizeof(adr), info));\n\n\t\tinfo->next = firstserver;\n\t\tfirstserver = info;\n\n\t\t//server replied from a broadcast message, make sure we ping it to retrieve its actual ping\n\t\tinfo->sends = 1;\n\t\tinfo->ping = PING_DEAD;\t//not known\n\t\tinfo->special |= SS_LOCAL;\n\t}\n\telse\n\t{\n\t\t//determine the ping\n\t\tif (info->refreshtime)\n\t\t{\n\t\t\tping = (Sys_DoubleTime() - info->refreshtime)*1000;\n\t\t\tif (ping > PING_MAX)\n\t\t\t\tinfo->ping = PING_MAX;\t//highest (that is known)\n\t\t\telse\n\t\t\t\tinfo->ping = ping;\n\t\t}\n\t\tinfo->refreshtime = 0;\n\t}\n\n\tinfo->status |= SRVSTATUS_ALIVE;\n\n\tnl = strchr(msg, '\\n');\n\tif (nl)\n\t{\n\t\t*nl = '\\0';\n\t\tnl++;\n\t}\n\n\tif (info->special & SS_PROXY)\n\t{\n\t\tif (!*Info_ValueForKey(msg, \"hostname\"))\n\t\t{\t//qq, you suck\n\t\t\t//this is a proxy peer list, not an actual serverinfo update.\n\t\t\tsize_t count = info->numpeers;\n\t\t\tint remaining = (net_message.cursize - 5) / 8;\n\t\t\tnet_message.currentbit = (5)<<3;\n\t\t\twhile (remaining --> 0)\n\t\t\t\tCL_ReadPingListEntry(info, NA_IP, &count);\n\t\t\tinfo->numpeers = count;\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tname = Info_ValueForKey(msg, \"hostname\");\n\tif (!*name)\n\t\tname = Info_ValueForKey(msg, \"sv_hostname\");\n\tQ_strncpyz(info->name, name, sizeof(info->name));\n\tinfo->special = info->special & (SS_FAVORITE | SS_KEEPINFO | SS_LOCAL | SS_GETINFO);\t//favorite+local is never cleared\n\tif (!strcmp(DISTRIBUTION, Info_ValueForKey(msg, \"*distrib\")))\t//outdated\n\t\tinfo->special |= SS_FTESERVER;\n\telse if (!strncmp(DISTRIBUTION, Info_ValueForKey(msg, \"*version\"), strlen(DISTRIBUTION)))\n\t\tinfo->special |= SS_FTESERVER;\n\n\tif (!MasterInfo_ReadProtocol(info, msg))\n\t{\t//try and guess.\n\t\tif (0)\n\t\t\t;\n#ifdef Q2CLIENT\n\t\telse if (prototype == MP_QUAKE2)\n\t\t\tinfo->special |= SS_QUAKE2;\n#endif\n#ifdef Q3CLIENT\n\t\telse if (prototype == MP_QUAKE3 || prototype == MP_DPMASTER/*if no protocol, assume q3 behaviours*/)\n\t\t\tinfo->special |= SS_QUAKE3;\n#endif\n#ifdef NQPROT\n\t\telse if (prototype == MP_NETQUAKE)\n\t\t\tinfo->special |= SS_NETQUAKE;\n#endif\n\t\telse\n\t\t\tinfo->special |= SS_QUAKEWORLD;\n\t}\n\tif (favorite)\t//was specifically named, not retrieved from a master.\n\t\tinfo->special |= SS_FAVORITE;\n\n\tinfo->players = 0;\n\tping = atoi(Info_ValueForKey(msg, \"maxclients\"));\n\tif (!ping)\n\t\tping = atoi(Info_ValueForKey(msg, \"sv_maxclients\"));\n\tinfo->maxplayers = bound(0, ping, 255);\n\n\tping = atoi(Info_ValueForKey(msg, \"timelimit\"));\n\tinfo->tl = bound(-32768, ping, 32767);\n\tping = atoi(Info_ValueForKey(msg, \"fraglimit\"));\n\tinfo->fl = bound(-32768, ping, 32767);\n\n\tif (*Info_ValueForKey(msg, \"*qtv\") || *Info_ValueForKey(msg, \"*QTV\"))\n\t\tinfo->special |= SS_PROXY|SS_FTESERVER;\t//qtv\n\telse if (!strcmp(Info_ValueForKey(msg, \"*progs\"), \"666\") && !strcmp(Info_ValueForKey(msg, \"*version\"), \"2.91\"))\n\t\tinfo->special |= SS_PROXY;\t//qizmo\n\telse if (!Q_strncmp(Info_ValueForKey(msg, \"*version\"), \"qwfwd\", 5))\n\t{\n\t\tchar *msg = \"\\xff\\xff\\xff\\xffpingstatus ext\";\n\t\tNET_SendPollPacket(strlen(msg), msg, info->adr);\n\t\tif (info->peers)\n\t\t{\t//let em time out\n\t\t\tZ_Free(info->peers);\n\t\t\tinfo->peers = NULL;\n\t\t\tinfo->numpeers = 0;\n\t\t}\n\t\tinfo->special |= SS_PROXY|SS_RELAY;\t//qwfwd\n\t}\n\telse if (!Q_strncasecmp(Info_ValueForKey(msg, \"*version\"), \"qtv \", 4))\n\t\tinfo->special |= SS_PROXY;\t//eztv\n\n\ttoken = Info_ValueForKey(msg, \"map\");\n\tif (!*token)\n\t\ttoken = Info_ValueForKey(msg, \"mapname\");\n\tQ_strncpyz(info->map,\t\ttoken,\tsizeof(info->map));\n\n\ttoken = Info_ValueForKey(msg, \"*gamedir\");\n\tif (!*token)\n\t\ttoken = Info_ValueForKey(msg, \"gamedir\");\n\tif (!*token)\n\t\ttoken = Info_ValueForKey(msg, \"modname\");\n\tQ_strncpyz(info->gamedir,\ttoken,\tsizeof(info->gamedir));\n\tQ_strncpyz(info->qcstatus,\t\tInfo_ValueForKey(msg, \"qcstatus\"),\tsizeof(info->qcstatus));\n\tQ_strncpyz(info->modname,\t\tInfo_ValueForKey(msg, \"modname\"),\tsizeof(info->modname));\n\n//\tinfo->gameversion = atoi(Info_ValueForKey(msg, \"gameversion\"));\n\n\tinfo->numbots = 0;//atoi(Info_ValueForKey(msg, \"bots\"));\n\tinfo->numhumans = info->players - info->numbots;\n\tinfo->freeslots = info->maxplayers - info->players;\n\n\tstrcpy(details.info, msg);\n\tmsg = msg+strlen(msg)+1;\n\n\t//clear player info. unless its an NQ server, which have some really annoying protocol to find out the players.\n\tif ((info->special & (SS_PROTOCOLMASK|SS_GETINFO)) == SS_NETQUAKE)\n\t{\n\t\tif (!info->moreinfo && ((slist_cacheinfo.value == 2 || NET_CompareAdr(&info->adr, &selectedserver.adr)) || (info->special & SS_KEEPINFO)))\n\t\t\tinfo->moreinfo = Z_Malloc(sizeof(serverdetailedinfo_t));\n\t\tinfo->numhumans = info->players = atoi(Info_ValueForKey(details.info, \"clients\"));\n\t}\n\telse\n\t{\n\t\tMasterInfo_RemovePlayers(&info->adr);\n\t\tinfo->players=details.numplayers = 0;\n\t\tif (!strchr(msg, '\\n'))\n\t\t\tinfo->numhumans = info->players = atoi(Info_ValueForKey(details.info, \"clients\"));\n\t\telse\n\t\t{\n\t\t\tint clnum;\n\n\t\t\tfor (clnum=0; ; clnum++)\n\t\t\t{\n\t\t\t\tnl = strchr(msg, '\\n');\n\t\t\t\tif (!nl)\n\t\t\t\t\tbreak;\n\t\t\t\t*nl = '\\0';\n\n\t\t\t\tif (!strncmp(msg, \"qtv \", 4))\n\t\t\t\t{\t//qtv destnum \"proxyname\" \"stream@host:port\" viewercount\n\t\t\t\t\tchar proxstream[128];\n\t\t\t\t\tchar tokval[256];\n\t\t\t\t\ttoken = msg+4;\n\n\t\t\t\t\ttoken = COM_ParseOut(token, tokval,sizeof(tokval));\n\t\t\t\t\t//destnum = atoi(tokval);\n\t\t\t\t\ttoken = COM_ParseOut(token, tokval,sizeof(tokval));\n\t\t\t\t\t//proxy name...\n\t\t\t\t\ttoken = COM_ParseOut(token, proxstream,sizeof(proxstream));\n\t\t\t\t\ttoken = COM_ParseOut(token, tokval,sizeof(tokval));\n\t\t\t\t\t//viewercount = atoi(tokval);\n\n\t\t\t\t\tif (*proxstream)\n\t\t\t\t\t\tInfo_SetValueForKey(details.info, \"qtvstream\", proxstream, sizeof(details.info));\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tif (clnum == MAX_CLIENTS)\n\t\t\t\t\tbreak;\n\n\t\t\t\tdetails.players[clnum].isspec = 0;\n\t\t\t\tdetails.players[clnum].team[0] = 0;\n\t\t\t\tdetails.players[clnum].skin[0] = 0;\n\n\t\t\t\ttoken = msg;\n\t\t\t\tif (!token)\n\t\t\t\t\tbreak;\n\t\t\t\tdetails.players[clnum].userid = atoi(token);\n\t\t\t\ttoken = strchr(token+1, ' ');\n\t\t\t\tif (!token)\n\t\t\t\t\tbreak;\n\t\t\t\tdetails.players[clnum].frags = atoi(token);\n\t\t\t\ttoken = strchr(token+1, ' ');\n\t\t\t\tif (!token)\n\t\t\t\t\tbreak;\n\t\t\t\tdetails.players[clnum].time = atoi(token);\n\t\t\t\tmsg = token;\n\t\t\t\ttoken = COM_Parse(token);\n\t\t\t\tif (!*token)\t//probably q2 response\n\t\t\t\t{\n\t\t\t\t\t//see if this is actually a Quake2 server.\n\t\t\t\t\ttoken = strchr(msg+1, '\\\"');\n\t\t\t\t\tif (!token)\t//it wasn't.\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tdetails.players[clnum].ping = details.players[clnum].frags;\n\t\t\t\t\tdetails.players[clnum].frags = details.players[clnum].userid;\n\n\t\t\t\t\tmsg = strchr(token+1, '\\\"');\n\t\t\t\t\tif (!msg)\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tlen = msg - token;\n\t\t\t\t\tif (len >= sizeof(details.players[clnum].name))\n\t\t\t\t\t\tlen = sizeof(details.players[clnum].name);\n\t\t\t\t\tQ_strncpyz(details.players[clnum].name, token+1, len);\n\n\t\t\t\t\tdetails.players[clnum].skin[0] = '\\0';\n\n\t\t\t\t\tdetails.players[clnum].topc = 0;\n\t\t\t\t\tdetails.players[clnum].botc = 0;\n\t\t\t\t\tdetails.players[clnum].time = 0;\n\t\t\t\t}\n\t\t\t\telse\t//qw response\n\t\t\t\t{\n\t\t\t\t\tdetails.players[clnum].ping = atoi(token);\n\t\t\t\t\tmsg = token;\n\t\t\t\t\ttoken = strchr(msg+1, ' ');\n\t\t\t\t\tif (!token)\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\ttoken = strchr(token+1, '\\\"');\n\t\t\t\t\tif (!token)\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tmsg = strchr(token+1, '\\\"');\n\t\t\t\t\tif (!msg)\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tlen = msg - token;\n\t\t\t\t\tif (len >= sizeof(details.players[clnum].name))\n\t\t\t\t\t\tlen = sizeof(details.players[clnum].name);\n\t\t\t\t\tif (!strncmp(token, \"\\\"\\\\s\\\\\", 4))\n\t\t\t\t\t{\n\t\t\t\t\t\tdetails.players[clnum].isspec |= 1;\n\t\t\t\t\t\tQ_strncpyz(details.players[clnum].name, token+4, len-3);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\tQ_strncpyz(details.players[clnum].name, token+1, len);\n\t\t\t\t\tdetails.players[clnum].name[len] = '\\0';\n\n\t\t\t\t\ttoken = strchr(msg+1, '\\\"');\n\t\t\t\t\tif (!token)\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tmsg = strchr(token+1, '\\\"');\n\t\t\t\t\tif (!msg)\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tlen = msg - token;\n\t\t\t\t\tif (len >= sizeof(details.players[clnum].skin))\n\t\t\t\t\t\tlen = sizeof(details.players[clnum].skin);\n\t\t\t\t\tQ_strncpyz(details.players[clnum].skin, token+1, len);\n\t\t\t\t\tdetails.players[clnum].skin[len] = '\\0';\n\n\t\t\t\t\ttoken = strchr(msg+1, ' ');\n\t\t\t\t\tif (!token)\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdetails.players[clnum].topc = atoi(token);\n\t\t\t\t\ttoken = strchr(token+1, ' ');\n\t\t\t\t\tif (!token)\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdetails.players[clnum].botc = atoi(token);\n\n\t\t\t\t\ttoken = strchr(msg+1, '\\\"');\n\t\t\t\t\tQ_strncpyz(details.players[clnum].team, \"\", sizeof(details.players[clnum].team));\n\t\t\t\t\tif (token)\n\t\t\t\t\t{\n\t\t\t\t\t\tmsg = strchr(token+1, '\\\"');\n\t\t\t\t\t\tif (msg)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tlen = msg - token;\n\t\t\t\t\t\t\tif (len >= sizeof(details.players[clnum].team))\n\t\t\t\t\t\t\t\tlen = sizeof(details.players[clnum].team);\n\t\t\t\t\t\t\tQ_strncpyz(details.players[clnum].team, token+1, len);\n\t\t\t\t\t\t\tdetails.players[clnum].team[len] = '\\0';\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tMasterInfo_AddPlayer(&info->adr, details.players[clnum].name, details.players[clnum].ping, details.players[clnum].frags, details.players[clnum].topc*4 | details.players[clnum].botc, details.players[clnum].skin, details.players[clnum].team);\n\n\t\t\t\t//WallFly is some q2 bot\n\t\t\t\t//[ServeMe] is some qw bot\n\t\t\t\tif (!strncmp(details.players[clnum].name, \"WallFly\", 7) || !strcmp(details.players[clnum].name, \"[ServeMe]\"))\n\t\t\t\t{\n\t\t\t\t\t//not players nor real people. they don't count towards any metric\n\t\t\t\t\tdetails.players[clnum].isspec |= 3;\n\t\t\t\t}\n\t\t\t\t//807 excludes the numerous bot names on some annoying qwtf server\n\t\t\t\t//BOT: excludes fte's botclients (which always have a bot: prefix)\n\t\t\t\telse if (details.players[clnum].ping == 807 || !strncmp(details.players[clnum].name, \"BOT:\", 4))\n\t\t\t\t{\n\t\t\t\t\tinfo->numbots++;\n\t\t\t\t\tdetails.players[clnum].isspec |= 2;\n\t\t\t\t}\n\t\t\t\telse if (details.players[clnum].isspec & 1)\n\t\t\t\t{\n\t\t\t\t\tinfo->numspectators++;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tinfo->numhumans++;\n\n\t\t\t\tfor (k = clnum, j = clnum-1; j >= 0; j--)\n\t\t\t\t{\n\t\t\t\t\tif ((details.players[k].isspec != details.players[j].isspec && !details.players[k].isspec) ||\n\t\t\t\t\t\tdetails.players[k].frags > details.players[j].frags)\n\t\t\t\t\t{\n\t\t\t\t\t\tstruct serverdetailedplayerinfo_s t = details.players[j];\n\t\t\t\t\t\tdetails.players[j] = details.players[k];\n\t\t\t\t\t\tdetails.players[k] = t;\n\t\t\t\t\t\tk = j;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tdetails.numplayers++;\n\n\t\t\t\tinfo->players++;\n\n\t\t\t\tmsg = nl;\n\t\t\t\tif (!msg)\n\t\t\t\t\tbreak;\t//erm...\n\t\t\t\tmsg++;\n\t\t\t}\n\t\t}\n\t\tif (!info->numbots)\n\t\t{\n\t\t\tinfo->numbots = atoi(Info_ValueForKey(details.info, \"bots\"));\n\t\t\tif (info->numbots > info->players)\n\t\t\t\tinfo->numbots = info->players;\n\t\t\tinfo->numhumans -= info->numbots;\n\t\t}\n\n\n\t\tif (!info->moreinfo && ((slist_cacheinfo.value == 2 || (NET_CompareAdr(&info->adr, &selectedserver.adr)&&!strcmp(info->brokerid,selectedserver.brokerid))) || (info->special & SS_KEEPINFO)))\n\t\t\tinfo->moreinfo = Z_Malloc(sizeof(serverdetailedinfo_t));\n\t\tif (NET_CompareAdr(&info->adr, &selectedserver.adr)&&!strcmp(info->brokerid,selectedserver.brokerid))\n\t\t\tselectedserver.detail = info->moreinfo;\n\n\t\tif (info->moreinfo)\n\t\t\tmemcpy(info->moreinfo, &details, sizeof(serverdetailedinfo_t));\n\t}\n\n\tinfo->qccategory = 0;\n#ifdef MENU_DAT\n\tcategorisingserver = info;\n\tinfo->qccategory = MP_GetServerCategory(-1);\n\tcategorisingserver = NULL;\n#endif\n\n\treturn true;\n}\n\n//rewrite to scan for existing server instead of wiping all.\nvoid CL_MasterListParse(netadrtype_t adrtype, int type, qboolean slashpad)\n{\n\tserverinfo_t *info;\n\tserverinfo_t *last, *old;\n\tint adrlen;\n\n\tint p1, p2;\n\tchar adr[MAX_ADR_SIZE];\n\tint i;\n\n\tchar madr[MAX_ADR_SIZE];\n\n\tswitch(adrtype)\n\t{\n\tcase NA_IP:\n\t\tadrlen = 4;\n\t\tbreak;\n\tcase NA_IPV6:\n\t\tadrlen = 16;\n\t\tbreak;\n\tcase NA_IPX:\n\t\tadrlen = 10;\n\t\tbreak;\n\tdefault:\n\t\treturn;\n\t}\n\n\tNET_AdrToString(madr, sizeof(madr), &net_from);\n\n\tMSG_ReadByte ();\t//should be \\n\n\n\tlast = firstserver;\n\n\twhile((net_message.currentbit>>3)+1+2 < net_message.cursize)\n\t{\n\t\tif (slashpad)\n\t\t{\n\t\t\tswitch(MSG_ReadByte())\n\t\t\t{\n\t\t\tcase '\\\\':\n\t\t\t\tadrtype = NA_IP;\n\t\t\t\tadrlen = 4;\n\t\t\t\tbreak;\n\t\t\tcase '/':\n\t\t\t\tadrtype = NA_IPV6;\n\t\t\t\tadrlen = 16;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tfirstserver = last;\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\tinfo = Z_Malloc(sizeof(serverinfo_t));\n\t\tinfo->adr.type = adrtype;\n\t\tswitch(adrtype)\n\t\t{\n\t\tcase NA_IP:\n\t\tcase NA_IPV6:\n\t\tcase NA_IPX:\n\t\t\t//generic fixed-length addresses\n\t\t\tfor (i = 0; i < adrlen; i++)\n\t\t\t\t((qbyte *)&info->adr.address)[i] = MSG_ReadByte();\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tbreak;\n\t\t}\n\t\tinfo->ping = PING_DEAD;\n\n\t\tp1 = MSG_ReadByte();\n\t\tp2 = MSG_ReadByte();\n\t\tinfo->adr.port = htons((unsigned short)((p1<<8)|p2));\n\t\tif (!info->adr.port)\n\t\t{\n\t\t\tZ_Free(info);\n\t\t\tbreak;\n\t\t}\n\t\tif ((old = Master_InfoForServer(&info->adr, NULL)))\t//remove if the server already exists.\n\t\t{\n\t\t\tif ((old->special & (SS_PROTOCOLMASK|SS_GETINFO)) != (type & (SS_PROTOCOLMASK|SS_GETINFO)))\n\t\t\t\told->special = type | (old->special & (SS_FAVORITE|SS_LOCAL));\n\t\t\told->sends = 1;\t//reset.\n\t\t\told->status |= SRVSTATUS_GLOBAL;\n\t\t\tZ_Free(info);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tinfo->sends = 1;\n\n\t\t\tinfo->special = type;\n\t\t\tinfo->refreshtime = 0;\n\t\t\tinfo->status |= SRVSTATUS_GLOBAL;\n\n\t\t\tQ_snprintfz(info->name, sizeof(info->name), \"%s (via %s)\", Master_ServerToString(adr, sizeof(adr), info), madr);\n\n\t\t\tinfo->next = last;\n\t\t\tlast = info;\n\n\t\t\tMaster_ResortServer(info);\n\t\t}\n\t}\n\n\tfirstserver = last;\n}\n#else\nvoid Master_QueryServer(serverinfo_t *server){}\nqboolean CL_QueryServers(void)\n{\n\tmaster_t *mast;\n\n\tMaster_DetermineMasterTypes();\n\n\tfor (mast = master; mast; mast=mast->next)\n\t{\n\t\tswitch (mast->protocoltype)\n\t\t{\n\t\tcase MP_UNSPECIFIED:\n\t\t\tcontinue;\n\t\tcase MP_DPMASTER:\t//dpmaster allows the client to specify the protocol to query. this means it always matches the current game type, so don't bother allowing the user to disable it.\n\t\t\tif (!sb_enabledarkplaces)\n\t\t\t\tcontinue;\n\t\t\tbreak;\n#ifdef NQPROT\n\t\tcase MP_NETQUAKE:\n\t\t\tif (!sb_enablenetquake)\n\t\t\t\tcontinue;\n\t\t\tbreak;\n#endif\n\t\tcase MP_QUAKEWORLD:\n\t\t\tif (!sb_enablequakeworld)\n\t\t\t\tcontinue;\n\t\t\tbreak;\n#ifdef Q2CLIENT\n\t\tcase MP_QUAKE2:\n\t\t\tif (!sb_enablequake2)\n\t\t\t\tcontinue;\n\t\t\tbreak;\n#endif\n#ifdef Q3CLIENT\n\t\tcase MP_QUAKE3:\n\t\t\tif (!sb_enablequake3)\n\t\t\t\tcontinue;\n\t\t\tbreak;\n#endif\n\t\t}\n\n\t\tif (mast->sends > 0)\n\t\t\tMasterInfo_Request(mast);\n\t}\n\n\treturn false;\t//false to say 'done'.\n}\n#endif\n\n\nvoid CL_Connect_c(int argn, const char *partial, struct xcommandargcompletioncb_s *ctx)\n{\n\tserverinfo_t *info;\n\tchar buf[512];\n\tint len;\n\tif (argn == 1)\n\t{\n\t\tlen = strlen(partial);\n\t\tif (len > 1 && partial[len-1] == '\\\"')\n\t\t\tlen--;\n\t\tfor (info = firstserver; info; info = info->next)\n\t\t{\n\t\t\tif (info->ping != PING_DEAD)\n\t\t\t{\n\t\t\t\tif (len && !Q_strncasecmp(partial, info->name, len))\n\t\t\t\t{\n\t\t\t\t\tif (info->ping == PING_UNKNOWN)\n\t\t\t\t\t\tctx->cb(info->name, va(\"^[%s^], %i players, unknown ping\", info->name, info->players), Master_ServerToString(buf, sizeof(buf), info), ctx);\n\t\t\t\t\telse\n\t\t\t\t\t\tctx->cb(info->name, va(\"^[%s^], %i players, %i ping\", info->name, info->players, info->ping), Master_ServerToString(buf, sizeof(buf), info), ctx);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tMaster_ServerToString(buf, sizeof(buf), info);\n\t\t\t\t//there are too many meaningless servers out there, so only suggest IP addresses if those servers are actually significant (ie: active, or favourite)\n\t\t\t\tif (!strncmp(partial, buf, len))\n\t\t\t\t{\n\t\t\t\t\tif (info->players || (info->special & SS_FAVORITE) || NET_ClassifyAddress(&info->adr, NULL)<=ASCOPE_LAN || len == strlen(buf))\n\t\t\t\t\t{\n\t\t\t\t\t\tif (info->ping == PING_UNKNOWN)\n\t\t\t\t\t\t\tctx->cb(buf, va(\"^[%s^], %i players, unknown ping\", info->name, info->players), NULL, ctx);\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tctx->cb(buf, va(\"^[%s^], %i players, %i ping\", info->name, info->players, info->ping), NULL, ctx);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n#else\nvoid CL_Connect_c(int argn, const char *partial, struct xcommandargcompletioncb_s *ctx)\n{\n}\n#endif\n\n#ifdef Q3CLIENT\n#if defined(CL_MASTER)\nstatic void NetQ3_LocalServers_f(void)\n{\n\tMasterInfo_Refresh(true);\n\n#if POLLTOTALSOCKETS>0\n\t{\n\t\tnetadr_t na;\n\t\tif (NET_StringToAdr(\"255.255.255.255\", PORT_Q3SERVER, &na))\n\t\t\tNET_SendPollPacket (14, va(\"%c%c%c%cgetstatus\\n\", 255, 255, 255, 255), na);\n\t}\n#endif\n}\nstatic void NetQ3_GlobalServers_Request(size_t masternum, int protocol, const char *keywords)\n{\n#ifdef WEBCLIENT\n\tif (masternum == countof(net_masterlist))\n\t{\n\t\tconst char *url;\n\t\tstruct dl_download *dl;\n\t\tCOM_Parse(com_protocolname.string);\n\t\tif (*net_ice_broker.string)\n\t\t{\n\t\t\tif (!strncmp(net_ice_broker.string, \"tls://\", 6))\n\t\t\t\turl = va(\"https://%s/raw/%s\", net_ice_broker.string+6, com_token);\n\t\t\telse if (!strncmp(net_ice_broker.string, \"tcp://\", 6))\n\t\t\t\turl = va(\"http://%s/raw/%s\", net_ice_broker.string+6, com_token);\n\t\t\telse\n\t\t\t\turl = va(\"http://%s/raw/%s\", net_ice_broker.string, com_token);\n\n\t\t\tdl = HTTP_CL_Get(url, NULL, MasterInfo_ProcessHTTP);\n\t\t\tif (dl)\n\t\t\t\tdl->isquery = true;\n\t\t}\n\t}\n#endif\n#if POLLTOTALSOCKETS>0\n\tif (masternum >= countof(net_masterlist))\n\t\treturn; //erk\n\tif (net_masterlist[masternum].protocol == MP_QUAKE3)\n\t{\n\t\tnetadr_t adr[16];\n\t\tchar *str;\n\t\tsize_t i, n;\n\t\tCOM_Parse(net_masterlist[masternum].cv.string);\t//only want the first one\n\t\tn = NET_StringToAdr2(com_token, 0, adr, countof(adr), NULL);\n\t\tstr = va(\"%c%c%c%cgetservers %u %s\\n\", 255, 255, 255, 255, protocol, keywords);\n\t\tfor (i = 0; i < n; i++)\n\t\t\tNET_SendPollPacket (strlen(str), str, adr[i]);\n\t}\n#endif\n}\nstatic void NetQ3_GlobalServers_f(void)\n{\n\tsize_t masternum = atoi(Cmd_Argv(1));\n\tint protocol = atoi(Cmd_Argv(2));\n\tchar *keywords;\n\tMasterInfo_Refresh(true);\n\n\tCmd_ShiftArgs(2, false);\n\tkeywords = Cmd_Args();\n\n\tif (!masternum)\n\t{\n\t\tfor (masternum = 0; masternum <= countof(net_masterlist); masternum++)\n\t\t\tNetQ3_GlobalServers_Request(masternum, protocol, keywords);\n\t}\n\telse\n\t\tNetQ3_GlobalServers_Request(masternum-1, protocol, keywords);\n}\n#else\nstatic void NetQ3_LocalServers_f(void){}\nstatic void NetQ3_GlobalServers_f(void){}\n#endif\n#endif\nvoid Net_Master_Init(void)\n{\n\tint i;\n\tfor (i = 0; net_masterlist[i].cv.name; i++)\n\t\tCvar_Register(&net_masterlist[i].cv, \"master servers\");\n#if defined(HAVE_SERVER) && defined(HAVE_LEGACY)\n\tCmd_AddCommand (\"setmaster\", SV_SetMaster_f);\n#endif\n\n#ifdef Q3CLIENT\n\tCmd_AddCommand (\"localservers\", NetQ3_LocalServers_f);\n\tCmd_AddCommand (\"globalservers\", NetQ3_GlobalServers_f);\n#endif\n}\n\n"
  },
  {
    "path": "engine/client/p_classic.c",
    "content": "/*\nCopyright (C) 1996-1997 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  \n\nSee the included (GNU.txt) GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n*/\n#include \"quakedef.h\"\n\n#ifdef PSET_CLASSIC\n\n#include \"glquake.h\"\n#include \"shader.h\"\n#include \"renderque.h\"\n\n#define POLYS\n\n#ifdef FTE_TARGET_WEB\n#define rand myrand\t//emscripten's libc is doing a terrible job of this.\nstatic int rand(void)\n{\t//ripped from glibc\n\tstatic int state = 0xdeadbeef;\n\tint val = ((state * 1103515245) + 12345) & 0x7fffffff;\n\tstate = val;\n\treturn val;\n}\n#endif\n\n\ntypedef enum {\n\tDODGY,\n\n\tROCKET_TRAIL,\n\tALT_ROCKET_TRAIL,\n\tBLOOD_TRAIL,\n\tGRENADE_TRAIL,\n\tBIG_BLOOD_TRAIL,\n\tTRACER1_TRAIL,\n\tTRACER2_TRAIL,\n\tVOOR_TRAIL,\n\n\tBRIGHTFIELD_POINT,\n\n\tBLOBEXPLOSION_POINT,\n\tLAVASPLASH_POINT,\n\tEXPLOSION_POINT,\n\tEXPLOSION2_POINT,\n\tTELEPORTSPLASH_POINT,\n\tMUZZLEFLASH_POINT,\n\tQWGUNSHOT_POINT,\t//not actually the same as nq, to deal with higher counts better\n\tQWSTDBLOOD_POINT,\t//same\n\tQWLGBLOOD_POINT,\t//same\n\n\tEFFECTTYPE_MAX\n} effect_type_t;\n\n\ntypedef struct cparticle_s\n{\n\tavec3_t org;\n\tfloat die;\n\tavec3_t vel;\n\tfloat ramp;\n\tenum\n\t{\n\t\tpt_static,\n\t\tpt_fire,\n\t\tpt_explode,\n\t\tpt_explode2,\n\t\tpt_blob,\n\t\tpt_blob2,\n\t\tpt_grav,\n\t\tpt_slowgrav,\n\n\t\tpt_oneframe\n\t} type;\n\tunsigned int rgb;\n\tstruct cparticle_s *next;\n} cparticle_t;\n\n#define DEFAULT_NUM_PARTICLES\t2048\n#define ABSOLUTE_MIN_PARTICLES\t512\n#define ABSOLUTE_MAX_PARTICLES\t8192\nstatic int r_numparticles;\nstatic cparticle_t\t*particles, *active_particles, *free_particles;\nextern cvar_t r_part_density, r_part_classic_expgrav, r_part_classic_opaque;\n\nstatic unsigned int particleframe;\n\nstatic int\tramp1[8] = {0x6f, 0x6d, 0x6b, 0x69, 0x67, 0x65, 0x63, 0x61};\nstatic int\tramp2[8] = {0x6f, 0x6e, 0x6d, 0x6c, 0x6b, 0x6a, 0x68, 0x66};\nstatic int\tramp3[8] = {0x6d, 0x6b, 6, 5, 4, 3};\n\n#ifndef POLYS\n#define BUFFERVERTS 2048*3\nstatic vecV_t classicverts[BUFFERVERTS];\nstatic union c\n{\n\tbyte_vec4_t b;\n\tunsigned int i;\n} classiccolours[BUFFERVERTS];\nstatic vec2_t classictexcoords[BUFFERVERTS];\nstatic index_t classicindexes[BUFFERVERTS];\nstatic mesh_t classicmesh;\n#endif\nstatic shader_t *classicshader;\n\n\n\n//obtains an index for the name, even if it is unknown (one can be loaded after. will only fail if the effect limit is reached)\n//technically this function is not meant to fail often, but thats fine so long as the other functions are meant to safely reject invalid effect numbers.\nstatic int PClassic_FindParticleType(const char *name)\n{\n\tif (!stricmp(\"tr_rocket\", name))\n\t\treturn ROCKET_TRAIL;\n\tif (!stricmp(\"tr_altrocket\", name))\n\t\treturn ALT_ROCKET_TRAIL;\n\tif (!stricmp(\"tr_slightblood\", name))\n\t\treturn BLOOD_TRAIL;\n\tif (!stricmp(\"tr_grenade\", name))\n\t\treturn GRENADE_TRAIL;\n\tif (!stricmp(\"tr_blood\", name))\n\t\treturn BIG_BLOOD_TRAIL;\n\tif (!stricmp(\"tr_wizspike\", name))\n\t\treturn TRACER1_TRAIL;\n\tif (!stricmp(\"tr_knightspike\", name))\n\t\treturn TRACER2_TRAIL;\n\tif (!stricmp(\"tr_vorespike\", name))\n\t\treturn VOOR_TRAIL;\n\n\tif (!stricmp(\"te_tarexplosion\", name))\n\t\treturn BLOBEXPLOSION_POINT;\n\tif (!stricmp(\"te_lavasplash\", name))\n\t\treturn LAVASPLASH_POINT;\n\tif (!stricmp(\"te_explosion\", name))\n\t\treturn EXPLOSION_POINT;\n\tif (!strnicmp(\"te_explosion2_\", name, 14))\n\t{\n\t\tchar *e;\n\t\tint start = strtoul(name+14, &e, 10);\n\t\tint len = strtoul((*e == '_')?e+1:e, &e, 10);\n\t\tif (!*e && start >= 0 && start <= 255 && len >= 0 && len <= 255)\n\t\t\treturn EXPLOSION2_POINT | (start<<8)|(len<<16);\n\t}\n\tif (!stricmp(\"te_teleport\", name))\n\t\treturn TELEPORTSPLASH_POINT;\n\tif (!stricmp(\"te_muzzleflash\", name))\n\t\treturn MUZZLEFLASH_POINT;\n\tif (!stricmp(\"ef_brightfield\", name))\n\t\treturn BRIGHTFIELD_POINT;\n\n\tif (!stricmp(\"te_qwgunshot\", name))\n\t\treturn QWGUNSHOT_POINT;\n\tif (!stricmp(\"te_qwblood\", name))\n\t\treturn QWSTDBLOOD_POINT;\n\tif (!stricmp(\"te_lightningblood\", name))\n\t\treturn QWLGBLOOD_POINT;\n\n\treturn P_INVALID;\n}\n\nstatic qboolean PClassic_Query(int type, int body, char *outstr, int outstrlen)\n{\n\tchar *n = NULL;\n\tswitch(type&0xff)\n\t{\n\tcase ROCKET_TRAIL:\n\t\tn = \"tr_rocket\";\n\t\tbreak;\n\tcase ALT_ROCKET_TRAIL:\n\t\tn = \"tr_altrocket\";\n\t\tbreak;\n\tcase BLOOD_TRAIL:\n\t\tn = \"tr_slightblood\";\n\t\tbreak;\n\tcase GRENADE_TRAIL:\n\t\tn = \"tr_grenade\";\n\t\tbreak;\n\tcase BIG_BLOOD_TRAIL:\n\t\tn = \"tr_blood\";\n\t\tbreak;\n\tcase TRACER1_TRAIL:\n\t\tn = \"tr_wizspike\";\n\t\tbreak;\n\tcase TRACER2_TRAIL:\n\t\tn = \"tr_knightspike\";\n\t\tbreak;\n\tcase VOOR_TRAIL:\n\t\tn = \"tr_vorespike\";\n\t\tbreak;\n\n\tcase BLOBEXPLOSION_POINT:\n\t\tn = \"te_tarexplosion\";\n\t\tbreak;\n\tcase LAVASPLASH_POINT:\n\t\tn = \"te_lavasplash\";\n\t\tbreak;\n\tcase EXPLOSION_POINT:\n\t\tn = \"te_explosion\";\n\t\tbreak;\n\tcase EXPLOSION2_POINT:\n\t\tn = va(\"te_explosion2_%i_%i\", (type>>8)&0xff, (type>>16)&0xff);\n\t\tbreak;\n\tcase TELEPORTSPLASH_POINT:\n\t\tn = \"te_teleport\";\n\t\tbreak;\n\tcase BRIGHTFIELD_POINT:\n\t\tn = \"ef_brightfield\";\n\t\tbreak;\n\t}\n\n\tif (!n)\n\t\treturn false;\n\t\n\tif (body == 0)\n\t{\n\t\tQ_strncpyz(outstr, n, outstrlen);\n\t\treturn true;\n\t}\n\treturn false;\n}\n\n//a convienience function.\nstatic int PClassic_RunParticleEffectTypeString (vec3_t org, vec3_t dir, float count, char *name)\n{\n\tint efnum = P_FindParticleType(name);\n\treturn P_RunParticleEffectState(org, dir, count, efnum, NULL);\n}\n\n//DP extension: add particles within a box that look like rain or snow.\nstatic void PClassic_RunParticleWeather(vec3_t minb, vec3_t maxb, vec3_t dir, float count, int colour, char *efname)\n{\n}\n\n//DP extension: add particles within a box.\nstatic void PClassic_RunParticleCube(int ptype, vec3_t minb, vec3_t maxb, vec3_t dir_min, vec3_t dir_max, float count, int colour, qboolean gravity, float jitter)\n{\n}\n\n//hexen2 support: add particles flying out from a point with a randomized speed\nstatic void PClassic_RunParticleEffect2 (vec3_t org, vec3_t dmin, vec3_t dmax, int color, int effect, int count)\n{\n}\n\n//hexen2 support: add particles within a box.\nstatic void PClassic_RunParticleEffect3 (vec3_t org, vec3_t box, int color, int effect, int count)\n{\n}\n\n//hexen2 support: add particles around the spot in a radius. no idea what the 'effect' field is.\nstatic void PClassic_RunParticleEffect4 (vec3_t org, float radius, int color, int effect, int count)\n{\n}\n\n//this function is used as a fallback in case a trail effect is unknown.\nstatic void PClassic_ParticleTrailIndex (vec3_t start, vec3_t end, int type, float timestep, int color, int crnd, trailkey_t *tk)\n{\n}\n\n//the one-time initialisation function, called no mater which renderer is active.\nstatic qboolean PClassic_InitParticles (void)\n{\n\tint i;\n\n\tif ((i = COM_CheckParm (\"-particles\")) && i + 1 < com_argc)\n\t{\n\t\tr_numparticles = (int) (Q_atoi(com_argv[i + 1]));\n\t\tr_numparticles = bound(ABSOLUTE_MIN_PARTICLES, r_numparticles, ABSOLUTE_MAX_PARTICLES);\n\t}\n\telse\n\t{\n\t\tr_numparticles = DEFAULT_NUM_PARTICLES;\n\t}\n\n\tparticles = (cparticle_t *) BZ_Malloc (r_numparticles * sizeof(cparticle_t));\n\n#ifndef POLYS\n\tfor (i = 0; i < BUFFERVERTS; i += 3)\n\t{\n\t\tclassictexcoords[i+1][0] = 1;\n\t\tclassictexcoords[i+2][1] = 1;\n\n\t\tclassicindexes[i+0] = i+0;\n\t\tclassicindexes[i+1] = i+1;\n\t\tclassicindexes[i+2] = i+2;\n\t}\n\tclassicmesh.xyz_array = classicverts;\n\tclassicmesh.st_array = classictexcoords;\n\tclassicmesh.colors4b_array = (byte_vec4_t*)classiccolours;\n\tclassicmesh.indexes = classicindexes;\n#endif\n\tclassicshader = R_RegisterShader(\"particles_classic\", SUF_NONE,\n\t\t\"{\\n\"\n\t\t\t\"program defaultsprite\\n\"\n\t\t\t\"nomipmaps\\n\"\n\t\t\t\"surfaceparm nodlight\\n\"\n\t\t\t\"{\\n\"\n\t\t\t\t\"if r_part_classic_square\\n\"\n\t\t\t\t\t\"clampmap classicparticle_square\\n\"\n\t\t\t\t\"else\\n\"\n\t\t\t\t\t\"clampmap classicparticle\\n\"\n\t\t\t\t\"endif\\n\"\n\t\t\t\t\"rgbgen vertex\\n\"\n\t\t\t\t\"alphagen vertex\\n\"\n\t\t\t\t\"blendfunc blend\\n\"\n\t\t\t\"}\\n\"\n\t\t\"}\\n\"\n\t\t);\n\n\treturn true;\n}\n\nstatic void PClassic_ShutdownParticles(void)\n{\n\tBZ_Free(particles);\n\tparticles = NULL;\n}\n\n// a classic trailstate key is really just a float\n// assuming float alignment/size is more strict than our key type \nstatic float Classic_GetLeftover(trailkey_t *tk)\n{\n\tfloat *f = (float *)tk;\n\n\tif (!f)\n\t\treturn 0;\n\n\treturn *f;\n}\n\nstatic void Classic_SetLeftover(trailkey_t *tk, float leftover)\n{\n\tfloat *f = (float *)tk;\n\n\tif (f)\n\t\t*f = leftover;\n}\n\n//called when an entity is removed from the world, taking its trailstate with it.\nstatic void PClassic_DelinkTrailstate(trailkey_t *tk)\n{\n\t*tk = 0;\n}\n\n//wipes all the particles ready for the next map.\nstatic void PClassic_ClearParticles (void)\n{\n\tint\t\ti;\n\t\n\tfree_particles = &particles[0];\n\tactive_particles = NULL;\n\n\tfor (i = 0;i < r_numparticles; i++)\n\t\tparticles[i].next = &particles[i+1];\n\tparticles[r_numparticles - 1].next = NULL;\n}\n\n//some particles (brightfield) must last only one frame\nstatic void PClassic_ClearPerFrame(void)\n{\n\tif (particleframe != -1 && particleframe != cl_framecount)\n\t{\n\t\tcparticle_t **link, *kill;\n\t\tfor (link = &active_particles; *link; )\n\t\t{\n\t\t\tif ((*link)->type == pt_oneframe)\n\t\t\t{\n\t\t\t\tkill = *link;\n\t\t\t\t*link = kill->next;\n\t\t\t\tkill->next = free_particles;\n\t\t\t\tfree_particles = kill;\n\t\t\t}\n\t\t\telse\n\t\t\t\tlink = &(*link)->next;\n\t\t}\n\t}\n}\n\n//draws all the active particles.\nstatic void PClassic_DrawParticles(void)\n{\n\tcparticle_t *p, *kill;\n\tint i;\n\tfloat time2, time3, time1, dvel, frametime, grav;\n\tvec3_t up, right;\n\tfloat dist, scale, r_partscale=0;\n#ifdef POLYS\n\tscenetris_t *scenetri;\n#else\n\tunion c usecolours;\n#endif\n\tstatic float oldtime;\n\tRSpeedMark();\n\n\tif (!active_particles)\n\t{\n\t\toldtime = cl.time;\n\t\treturn;\n\t}\n\n\tif (particleframe != -1 && particleframe != cl_framecount)\n\t{\n\t\tPClassic_ClearPerFrame();\n\t\tparticleframe = -1;\n\t}\n\n\tif (r_refdef.useperspective)\n\t\tr_partscale = 0.004 * tan (r_refdef.fov_x * (M_PI / 180) * 0.5f);\n\telse\n\t\tr_partscale = 0;\n\tVectorScale (vup, 1.5, up);\n\tVectorScale (vright, 1.5, right);\n\n\tframetime = cl.time - oldtime;\n\toldtime = cl.time;\n\tframetime = bound(0, frametime, 1);\n\tif (cl.paused || r_secondaryview || r_refdef.recurse)\n\t\tframetime = 0;\n\ttime3 = frametime * 15;\n\ttime2 = frametime * 10; // 15;\n\ttime1 = frametime * 5;\n\tgrav = frametime * 800 * 0.05;\n\tdvel = 4 * frametime;\n\n#ifdef POLYS\n//\tif (cl_numstris && cl_stris[cl_numstris-1].shader == classicshader && cl_stris[cl_numstris-1].numvert + 8 <= MAX_INDICIES)\n//\t\tscenetri = &cl_stris[cl_numstris-1];\n//\telse\n\t{\n\t\tif (cl_numstris == cl_maxstris)\n\t\t{\n\t\t\tcl_maxstris+=8;\n\t\t\tcl_stris = BZ_Realloc(cl_stris, sizeof(*cl_stris)*cl_maxstris);\n\t\t}\n\t\tscenetri = &cl_stris[cl_numstris++];\n\t\tscenetri->shader = classicshader;\n\t\tscenetri->flags = BEF_NODLIGHT|BEF_NOSHADOWS;\n\t\tscenetri->firstidx = cl_numstrisidx;\n\t\tscenetri->firstvert = cl_numstrisvert;\n\t\tscenetri->numvert = 0;\n\t\tscenetri->numidx = 0;\n\t}\n#endif\n\n\twhile(1)\n\t{\n\t\tkill = active_particles;\n\t\tif (kill && kill->die < cl.time)\n\t\t{\n\t\t\tactive_particles = kill->next;\n\t\t\tkill->next = free_particles;\n\t\t\tfree_particles = kill;\n\t\t\tcontinue;\n\t\t}\n\t\tbreak;\n\t}\n\n\tfor (p = active_particles; p ; p = p->next)\n\t{\n\t\twhile (1)\n\t\t{\n\t\t\tkill = p->next;\n\t\t\tif (kill && kill->die < cl.time)\n\t\t\t{\n\t\t\t\tp->next = kill->next;\n\t\t\t\tkill->next = free_particles;\n\t\t\t\tfree_particles = kill;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\n\t\t// hack a scale up to keep particles from disapearing\n\t\tdist = (p->org[0] - r_origin[0]) * vpn[0] + (p->org[1] - r_origin[1]) * vpn[1] + (p->org[2] - r_origin[2]) * vpn[2];\n\t\tscale = 1 + dist * r_partscale;\n\n#ifdef POLYS\n\t\tif (cl_numstrisvert+3 > cl_maxstrisvert)\n\t\t\tcl_stris_ExpandVerts(cl_numstrisvert+1024*3);\n\n//\t\tVector4Set(cl_strisvertc[cl_numstrisvert+0],1,1,1,1);\n//\t\tVector4Set(cl_strisvertc[cl_numstrisvert+1],1,1,1,1);\n//\t\tVector4Set(cl_strisvertc[cl_numstrisvert+2],1,1,1,1);\n\n\t\tVector4Set(cl_strisvertc[cl_numstrisvert+0], ((p->rgb&0xff)>>0)/255.0, ((p->rgb&0xff00)>>8)/255.0, ((p->rgb&0xff0000)>>16)/255.0, ((p->type == pt_fire && !r_part_classic_opaque.ival)?((6 - p->ramp) *0.166666):1.0));\n\t\tVector4Copy(cl_strisvertc[cl_numstrisvert+0], cl_strisvertc[cl_numstrisvert+1]);\n\t\tVector4Copy(cl_strisvertc[cl_numstrisvert+0], cl_strisvertc[cl_numstrisvert+2]);\n\n\t\tVector2Set(cl_strisvertt[cl_numstrisvert+0], 0, 0);\n\t\tVector2Set(cl_strisvertt[cl_numstrisvert+1], 1, 0);\n\t\tVector2Set(cl_strisvertt[cl_numstrisvert+2], 0, 1);\n\n\t\tVectorCopy(p->org, cl_strisvertv[cl_numstrisvert+0]);\n\t\tVectorMA(p->org, scale, up, cl_strisvertv[cl_numstrisvert+1]);\n\t\tVectorMA(p->org, scale, right, cl_strisvertv[cl_numstrisvert+2]);\n\n\t\tif (cl_numstrisidx+3 > cl_maxstrisidx)\n\t\t{\n\t\t\tcl_maxstrisidx += 1024*3;\n\t\t\tcl_strisidx = BZ_Realloc(cl_strisidx, sizeof(*cl_strisidx)*cl_maxstrisidx);\n\t\t}\n\t\tcl_strisidx[cl_numstrisidx++] = (cl_numstrisvert - scenetri->firstvert) + 0;\n\t\tcl_strisidx[cl_numstrisidx++] = (cl_numstrisvert - scenetri->firstvert) + 1;\n\t\tcl_strisidx[cl_numstrisidx++] = (cl_numstrisvert - scenetri->firstvert) + 2;\n\n\t\tcl_numstrisvert += 3;\n\n\t\tscenetri->numvert += 3;\n\t\tscenetri->numidx += 3;\n#else\n\t\tif (classicmesh.numvertexes >= BUFFERVERTS-3)\n\t\t{\n\t\t\tclassicmesh.numindexes = classicmesh.numvertexes;\n\t\t\tBE_DrawMesh_Single(classicshader, &classicmesh, NULL, &classicshader->defaulttextures, 0);\n\t\t\tclassicmesh.numvertexes = 0;\n\t\t}\n\n\t\tusecolours.i = p->rgb;\n\t\tif (p->type == pt_fire)\n\t\t\tusecolours.b[3] = 255 * (6 - p->ramp) / 6;\n\t\telse\n\t\t\tusecolours.b[3] = 255;\n\t\tclassiccolours[classicmesh.numvertexes].i = usecolours.i;\n\t\tVectorCopy(p->org, classicverts[classicmesh.numvertexes]);\n\t\tclassicmesh.numvertexes++;\n\t\tclassiccolours[classicmesh.numvertexes].i = usecolours.i;\n\t\tVectorMA(p->org, scale, up, classicverts[classicmesh.numvertexes]);\n\t\tclassicmesh.numvertexes++;\n\t\tclassiccolours[classicmesh.numvertexes].i = usecolours.i;\n\t\tVectorMA(p->org, scale, right, classicverts[classicmesh.numvertexes]);\n\t\tclassicmesh.numvertexes++;\n#endif\n\n\t\tp->org[0] += p->vel[0] * frametime;\n\t\tp->org[1] += p->vel[1] * frametime;\n\t\tp->org[2] += p->vel[2] * frametime;\n\t\t\n\t\tswitch (p->type)\n\t\t{\n\t\tcase pt_oneframe:\n\t\tcase pt_static:\n\t\t\tbreak;\n\t\tcase pt_fire:\n\t\t\tp->ramp += time1;\n\t\t\tif (p->ramp >= 6)\n\t\t\t\tp->die = -1;\n\t\t\telse\n\t\t\t\tp->rgb = d_quaketo24srgbtable[ramp3[(int) p->ramp]];\n\t\t\tp->vel[2] += grav;\n\t\t\tbreak;\n\t\tcase pt_explode:\n\t\t\tp->ramp += time2;\n\t\t\tif (p->ramp >=8)\n\t\t\t\tp->die = -1;\n\t\t\telse\n\t\t\t\tp->rgb = d_quaketo24srgbtable[ramp1[(int) p->ramp]];\n\t\t\tfor (i = 0; i < 3; i++)\n\t\t\t\tp->vel[i] += p->vel[i] * dvel;\n\t\t\tp->vel[2] -= grav*r_part_classic_expgrav.value;\n\t\t\tbreak;\n\t\tcase pt_explode2:\n\t\t\tp->ramp += time3;\n\t\t\tif (p->ramp >=8)\n\t\t\t\tp->die = -1;\n\t\t\telse\n\t\t\t\tp->rgb = d_quaketo24srgbtable[ramp2[(int) p->ramp]];\n\t\t\tfor (i = 0; i < 3; i++)\n\t\t\t\tp->vel[i] -= p->vel[i] * frametime;\n\t\t\tp->vel[2] -= grav*r_part_classic_expgrav.value;\n\t\t\tbreak;\n\t\tcase pt_blob:\n\t\t\tfor (i = 0; i < 3; i++)\n\t\t\t\tp->vel[i] += p->vel[i] * dvel;\n\t\t\tp->vel[2] -= grav;\n\t\t\tbreak;\n\t\tcase pt_blob2:\n\t\t\tfor (i = 0; i < 2; i++)\n\t\t\t\tp->vel[i] -= p->vel[i] * dvel;\n\t\t\tp->vel[2] -= grav;\n\t\t\tbreak;\n\t\tcase pt_slowgrav:\n\t\tcase pt_grav:\n\t\t\tp->vel[2] -= grav;\n\t\t\tbreak;\n\t\t}\n\t}\n#ifndef POLYS\n\tif (classicmesh.numvertexes)\n\t{\n\t\tclassicmesh.numindexes = classicmesh.numvertexes;\n\t\tBE_DrawMesh_Single(classicshader, &classicmesh, NULL, &classicshader->defaulttextures, 0);\n\t\tclassicmesh.numvertexes = 0;\n\t}\n#endif\n\tRSpeedEnd(RSPEED_PARTICLESDRAW);\n}\n\n\nstatic void Classic_ParticleExplosion (vec3_t org)\n{\n\tint\ti, j;\n\tcparticle_t\t*p;\n\tint count;\n\n\tcount = 1024 * r_part_density.value;\n\t\n\tfor (i = 0; i < count; i++)\n\t{\n\t\tif (!free_particles)\n\t\t\treturn;\n\t\tp = free_particles;\n\t\tfree_particles = p->next;\n\t\tp->next = active_particles;\n\t\tactive_particles = p;\n\n\t\tp->die = cl.time + 5;\n\t\tp->rgb = d_8to24srgbtable[ramp1[0]];\n\t\tp->ramp = rand() & 3;\n\t\tif (i & 1)\n\t\t{\n\t\t\tp->type = pt_explode;\n\t\t\tfor (j = 0; j < 3; j++)\n\t\t\t{\n\t\t\t\tp->org[j] = org[j] + ((rand() % 32) - 16);\n\t\t\t\tp->vel[j] = (rand() % 512) - 256;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tp->type = pt_explode2;\n\t\t\tfor (j = 0; j < 3; j++)\n\t\t\t{\n\t\t\t\tp->org[j] = org[j] + ((rand() % 32) - 16);\n\t\t\t\tp->vel[j] = (rand()%512) - 256;\n\t\t\t}\n\t\t}\n\t}\n}\n\nstatic void Classic_ParticleExplosion2 (vec3_t org, int colorStart, int colorLength)\n{\n\tint\t\t\ti, j;\n\tcparticle_t\t*p;\n\tint\t\t\tcolorMod = 0;\n\n\tfor (i=0; i<512; i++)\n\t{\n\t\tif (!free_particles)\n\t\t\treturn;\n\t\tp = free_particles;\n\t\tfree_particles = p->next;\n\t\tp->next = active_particles;\n\t\tactive_particles = p;\n\n\t\tp->die = cl.time + 0.3;\n\t\tp->rgb = d_8to24srgbtable[(colorStart + (colorMod % colorLength)) & 255];\n\t\tcolorMod++;\n\n\t\tp->type = pt_blob;\n\t\tfor (j=0 ; j<3 ; j++)\n\t\t{\n\t\t\tp->org[j] = org[j] + ((rand()%32)-16);\n\t\t\tp->vel[j] = (rand()%512)-256;\n\t\t}\n\t}\n}\n\nstatic void Classic_BlobExplosion (vec3_t org)\n{\n\tint i, j;\n\tcparticle_t *p;\n\tint count;\n\n\tcount = 1024 * r_part_density.value;\n\t\n\tfor (i = 0; i < count; i++)\n\t{\n\t\tif (!free_particles)\n\t\t\treturn;\n\t\tp = free_particles;\n\t\tfree_particles = p->next;\n\t\tp->next = active_particles;\n\t\tactive_particles = p;\n\n\t\tp->die = cl.time + 1 + (rand() & 8) * 0.05;\n\n\t\tif (i & 1)\n\t\t{\n\t\t\tp->type = pt_blob;\n\t\t\tp->rgb = d_8to24srgbtable[66 + rand() % 6];\n\t\t\tfor (j = 0; j < 3; j++)\n\t\t\t{\n\t\t\t\tp->org[j] = org[j] + ((rand() % 32) - 16);\n\t\t\t\tp->vel[j] = (rand() % 512) - 256;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tp->type = pt_blob2;\n\t\t\tp->rgb = d_8to24srgbtable[150 + rand() % 6];\n\t\t\tfor (j = 0; j < 3; j++)\n\t\t\t{\n\t\t\t\tp->org[j] = org[j] + ((rand() % 32) - 16);\n\t\t\t\tp->vel[j] = (rand() % 512) - 256;\n\t\t\t}\n\t\t}\n\t}\n}\n\nstatic void Classic_RunParticleEffect (vec3_t org, vec3_t dir, int color, int count, qboolean qwstyle)\n{\n\tint i, j, scale;\n\tcparticle_t *p;\n\n\tif (!dir)\n\t\tdir = vec3_origin;\n\n\tif (qwstyle)\n\t\tscale = (count > 130) ? 3 : (count > 20) ? 2  : 1;\t//QW\n\telse\n\t\tscale = 1;\t//NQ\n\n\tcount = ceil(count*r_part_density.value);\t//round-to-0 was resulting in blood being far too hard to see, especially when blood is often spawned with multiple points all rounded down\n\n\tfor (i = 0; i < count; i++)\n\t{\n\t\tif (!free_particles)\n\t\t\treturn;\n\t\tp = free_particles;\n\t\tfree_particles = p->next;\n\t\tp->next = active_particles;\n\t\tactive_particles = p;\n\n\t\tp->die = cl.time + 0.1 * (rand() % 5);\n\t\tp->rgb = d_8to24srgbtable[(color & ~7) + (rand() & 7)];\n\t\tif (qwstyle)\n\t\t\tp->type = pt_grav;\t//QW\n\t\telse\n\t\t\tp->type = pt_slowgrav;\t//NQ\n\t\tfor (j = 0; j < 3; j++)\n\t\t{\n\t\t\tp->org[j] = org[j] + scale * ((rand() & 15) - 8);\n\t\t\tp->vel[j] = dir[j] * 15;\n\t\t}\n\t}\n}\n\nstatic void Classic_LavaSplash (vec3_t org)\n{\n\tint i, j, k;\n\tcparticle_t *p;\n\tfloat vel;\n\tvec3_t dir;\n\n\tfor (i = -16; i < 16; i++)\n\t{\n\t\tfor (j = -16; j < 16; j++)\n\t\t{\n\t\t\tfor (k = 0; k < 1; k++)\n\t\t\t{\n\t\t\t\tif (!free_particles)\n\t\t\t\t\treturn;\n\t\t\t\tp = free_particles;\n\t\t\t\tfree_particles = p->next;\n\t\t\t\tp->next = active_particles;\n\t\t\t\tactive_particles = p;\n\n\t\t\t\tp->die = cl.time + 2 + (rand() & 31) * 0.02;\n\t\t\t\tp->rgb = d_8to24srgbtable[224 + (rand() & 7)];\n\t\t\t\tp->type = pt_grav;\n\n\t\t\t\tdir[0] = j * 8 + (rand() & 7);\n\t\t\t\tdir[1] = i * 8 + (rand() & 7);\n\t\t\t\tdir[2] = 256;\n\n\t\t\t\tp->org[0] = org[0] + dir[0];\n\t\t\t\tp->org[1] = org[1] + dir[1];\n\t\t\t\tp->org[2] = org[2] + (rand() & 63);\n\n\t\t\t\tVectorNormalizeFast (dir);\n\t\t\t\tvel = 50 + (rand() & 63);\n\t\t\t\tVectorScale (dir, vel, p->vel);\n\t\t\t}\n\t\t}\n\t}\n}\n\nstatic void Classic_TeleportSplash (vec3_t org)\n{\n\tint i, j, k;\n\tcparticle_t *p;\n\tfloat vel;\n\tvec3_t dir;\n\n\tint st = 4 / r_part_density.value;\n\tif (st == 0)\n\t\tst = 1;\n\n\tfor (i = -16; i < 16; i += st)\n\t{\n\t\tfor (j = -16; j < 16; j += st)\n\t\t{\n\t\t\tfor (k = -24; k < 32; k += st)\n\t\t\t{\n\t\t\t\tif (!free_particles)\n\t\t\t\t\treturn;\n\t\t\t\tp = free_particles;\n\t\t\t\tfree_particles = p->next;\n\t\t\t\tp->next = active_particles;\n\t\t\t\tactive_particles = p;\n\n\t\t\t\tp->die = cl.time + 0.2 + (rand() & 7) * 0.02;\n\t\t\t\tp->rgb = d_8to24srgbtable[7 + (rand() & 7)];\n\t\t\t\tp->type = pt_grav;\n\n\t\t\t\tdir[0] = j * 8;\n\t\t\t\tdir[1] = i * 8;\n\t\t\t\tdir[2] = k * 8;\n\n\t\t\t\tp->org[0] = org[0] + i + (rand() & 3);\n\t\t\t\tp->org[1] = org[1] + j + (rand() & 3);\n\t\t\t\tp->org[2] = org[2] + k + (rand() & 3);\n\n\t\t\t\tVectorNormalizeFast (dir);\n\t\t\t\tvel = 50 + (rand() & 63);\n\t\t\t\tVectorScale (dir, vel, p->vel);\n\t\t\t}\n\t\t}\n\t}\n}\n\n#define NUMVERTEXNORMALS\t162\n//vec3_t\tavelocity = {23, 7, 3};\n//float\tpartstep = 0.01;\n//float\ttimescale = 0.01;\nstatic\tvec3_t\tavelocities[NUMVERTEXNORMALS];\nstatic void Classic_BrightField (vec3_t org)\n{\n\textern\tfloat\tr_avertexnormals[NUMVERTEXNORMALS][3];\n\tfloat\tbeamlength = 16;\n\n\tint\t\t\ti;\n\tcparticle_t\t*p;\n\tfloat\t\tangle;\n\tfloat\t\tsp, sy, cp, cy;\n\tvec3_t\t\tforward;\n\tfloat\t\tdist;\n\n\tPClassic_ClearPerFrame();\n\tparticleframe = cl_framecount;\n\n\tdist = 64;\n\n\tif (!avelocities[0][0])\n\t{\n\t\tfor (i=0 ; i<NUMVERTEXNORMALS ; i++)\n\t\t{\n\t\t\tavelocities[i][0] = (rand()&255) * 0.01;\n\t\t\tavelocities[i][1] = (rand()&255) * 0.01;\n\t\t\tavelocities[i][2] = (rand()&255) * 0.01;\n\t\t}\n\t}\n\n\tfor (i=0 ; i<NUMVERTEXNORMALS ; i++)\n\t{\n\t\tif (!free_particles)\n\t\t\treturn;\n\t\tp = free_particles;\n\t\tfree_particles = p->next;\n\t\tp->next = active_particles;\n\t\tactive_particles = p;\n\n\t\tangle = cl.time * avelocities[i][0];\n\t\tsy = sin(angle);\n\t\tcy = cos(angle);\n\t\tangle = cl.time * avelocities[i][1];\n\t\tsp = sin(angle);\n\t\tcp = cos(angle);\n\n\t\t//fixme: is roll important?\n\n\t\tforward[0] = cp*cy;\n\t\tforward[1] = cp*sy;\n\t\tforward[2] = -sp;\n\n\t\tp->die = cl.time;// + 0.01;\n\t\tp->rgb = d_8to24srgbtable[0x6f];\n\t\tp->type = pt_oneframe;\n\n\t\tp->org[0] = org[0] + r_avertexnormals[i][0]*dist + forward[0]*beamlength;\t\t\t\n\t\tp->org[1] = org[1] + r_avertexnormals[i][1]*dist + forward[1]*beamlength;\t\t\t\n\t\tp->org[2] = org[2] + r_avertexnormals[i][2]*dist + forward[2]*beamlength;\t\t\t\n\t}\n}\n\n//svc_tempentity support: this is the function that handles 'special' point effects.\n//use the trail state so fast/slow frames keep the correct particle counts on certain every-frame effects\nstatic int PClassic_RunParticleEffectState (vec3_t org, vec3_t dir, float count, int typenum, trailkey_t *tk)\n{\n\tswitch(typenum&0xff)\n\t{\n\tcase BRIGHTFIELD_POINT:\n\t\tClassic_BrightField(org);\n\t\tbreak;\n\tcase BLOBEXPLOSION_POINT:\n\t\tClassic_BlobExplosion(org);\n\t\tbreak;\n\tcase LAVASPLASH_POINT:\n\t\tClassic_LavaSplash(org);\n\t\tbreak;\n\tcase EXPLOSION_POINT:\n\t\tClassic_ParticleExplosion(org);\n\t\tbreak;\n\tcase EXPLOSION2_POINT:\n\t\tClassic_ParticleExplosion2(org, (typenum>>8)&0xff, (typenum>>16)&0xff);\n\t\tbreak;\n\tcase TELEPORTSPLASH_POINT:\n\t\tClassic_TeleportSplash(org);\n\t\tbreak;\n\tcase MUZZLEFLASH_POINT:\n\t\t{\n\t\t\tdlight_t *dl = CL_AllocDlight (0);\n\t\t\tif (dir)\n\t\t\t\tVectorCopy(dir, dl->axis[0]);\n\t\t\telse\n\t\t\t\tVectorSet(dl->axis[0], 0, 0, 1);\n\t\t\tVectorVectors(dl->axis[0], dl->axis[1], dl->axis[2]);\n\t\t\tVectorInverse(dl->axis[1]);\n\t\t\tif (dir)\n\t\t\t\tVectorMA (org, 15, dl->axis[0], dl->origin);\n\t\t\telse\n\t\t\t\tVectorCopy (org, dl->origin);\n\n\t\t\tdl->radius = 200 + (rand()&31);\n\t\t\tdl->minlight = 32;\n\t\t\tdl->die = cl.time + 0.1;\n\t\t\tdl->color[0] = 1.5;\n\t\t\tdl->color[1] = 1.3;\n\t\t\tdl->color[2] = 1.0;\n\n\t\t\tdl->channelfade[0] = 1.5;\n\t\t\tdl->channelfade[1] = 0.75;\n\t\t\tdl->channelfade[2] = 0.375;\n\t\t\tdl->decay = 1000;\n#ifdef RTLIGHTS\n\t\t\tdl->lightcolourscales[2] = 4;\n#endif\n\t\t}\n\t\tbreak;\n\tcase QWGUNSHOT_POINT:\n\t\tClassic_RunParticleEffect(org, dir, 0, count*20, true);\n\t\tbreak;\n\tcase QWSTDBLOOD_POINT:\n\t\tClassic_RunParticleEffect(org, dir, 73, count*20, true);\n\t\tbreak;\n\tcase QWLGBLOOD_POINT:\n\t\tClassic_RunParticleEffect(org, dir, 225, count*50, true);\n\t\tbreak;\n\tdefault:\n\t\treturn 1;\n\t}\n\treturn 0;\n}\n\nstatic float Classic_ParticleTrail (vec3_t start, vec3_t end, float leftover, effect_type_t type)\n{\n\tvec3_t point, delta, dir, step;\n\tfloat len, rlen, scale;\n\tint i, j, num_particles;\n\tcparticle_t *p;\n\tstatic int tracercount;\n\n\tif (type >= BRIGHTFIELD_POINT)\n\t{\n\t\tPClassic_RunParticleEffectState(end, vec3_origin, 1, type, NULL);\n\t\treturn 0;\n\t}\n\n\tVectorCopy (start, point);\n\tVectorSubtract (end, start, delta);\n\tif (!(len = VectorLength (delta)))\n\t\tgoto done;\n\tVectorScale(delta, 1 / len, dir);\t//unit vector in direction of trail\n\n\tVectorMA(point, -leftover, dir, point);\n\tlen += leftover;\n\trlen = len;\n\n\tswitch (type)\n\t{\n\tcase ALT_ROCKET_TRAIL:\n\t\tscale = 1.5; break;\n\tcase BLOOD_TRAIL:\n\t\tscale = 6; break;\n\tdefault:\n\t\tscale = 3; break;\n\tcase TRACER1_TRAIL:\n\tcase TRACER2_TRAIL:\n\t\tscale = (r_part_density.value < 0.5)?6*r_part_density.value:3;\n\t\tbreak;\n\t}\n\n\tscale /= r_part_density.value;\n\n\tVectorScale (dir, scale, step);\n\n\tlen /= scale;\n\tleftover = rlen - ((int)(len) * scale);\n\n\tnum_particles = (int) len;\n\n\tfor (i = 0; i < num_particles && free_particles; i++)\n\t{\n\t\tp = free_particles;\n\t\tfree_particles = p->next;\n\t\tp->next = active_particles;\n\t\tactive_particles = p;\n\n\t\tVectorClear (p->vel);\n\t\tp->die = cl.time + 2;\n\n\t\tswitch(type)\n\t\t{\t\t\n\t\tcase GRENADE_TRAIL:\n\t\t\tp->ramp = (rand() & 3) + 2;\n\t\t\tp->rgb = d_8to24srgbtable[ramp3[(int) p->ramp]];\n\t\t\tp->type = pt_fire;\n\t\t\tfor (j = 0; j < 3; j++)\n\t\t\t\tp->org[j] = point[j] + ((rand() % 6) - 3);\n\t\t\tbreak;\n\t\tcase BLOOD_TRAIL:\n\t\t\tp->type = pt_slowgrav;\n\t\t\tp->rgb = d_8to24srgbtable[67 + (rand() & 3)];\n\t\t\tfor (j = 0; j < 3; j++)\n\t\t\t\tp->org[j] = point[j] + ((rand() % 6) - 3);\n\t\t\tbreak;\n\t\tcase BIG_BLOOD_TRAIL:\n\t\t\tp->type = pt_slowgrav;\n\t\t\tp->rgb = d_8to24srgbtable[67 + (rand() & 3)];\n\t\t\tfor (j = 0; j < 3; j++)\n\t\t\t\tp->org[j] = point[j] + ((rand() % 6) - 3);\n\t\t\tbreak;\n\t\tcase TRACER1_TRAIL:\n\t\tcase TRACER2_TRAIL:\n\t\t\tp->die = cl.time + 0.5;\n\t\t\tp->type = pt_static;\n\t\t\tif (type == TRACER1_TRAIL)\n\t\t\t\tp->rgb = d_8to24srgbtable[52 + ((tracercount & 4) << 1)];\n\t\t\telse\n\t\t\t\tp->rgb = d_8to24srgbtable[230 + ((tracercount & 4) << 1)];\n\n\t\t\ttracercount++;\n\n\t\t\tVectorCopy (point, p->org);\n\t\t\tif (tracercount & 1)\n\t\t\t{\t//the addition of /scale here counters dir being rescaled\n\t\t\t\tp->vel[0] = 30 * dir[1];\n\t\t\t\tp->vel[1] = 30 * -dir[0];\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tp->vel[0] = 30 * -dir[1];\n\t\t\t\tp->vel[1] = 30 * dir[0];\n\t\t\t}\n\t\t\tbreak;\n\t\tcase VOOR_TRAIL:\n\t\t\tp->rgb = d_8to24srgbtable[9 * 16 + 8 + (rand() & 3)];\n\t\t\tp->type = pt_static;\n\t\t\tp->die = cl.time + 0.3;\n\t\t\tfor (j = 0; j < 3; j++)\n\t\t\t\tp->org[j] = point[j] + ((rand() & 15) - 8);\n\t\t\tbreak;\n\t\tcase ALT_ROCKET_TRAIL:\n\t\t\tp->ramp = (rand() & 3);\n\t\t\tp->rgb = d_8to24srgbtable[ramp3[(int) p->ramp]];\n\t\t\tp->type = pt_fire;\n\t\t\tfor (j = 0; j < 3; j++)\n\t\t\t\tp->org[j] = point[j] + ((rand() % 6) - 3);\n\t\t\tbreak;\n\t\tcase ROCKET_TRAIL:\n\t\tdefault:\t\t\n\t\t\tp->ramp = (rand() & 3);\n\t\t\tp->rgb = d_8to24srgbtable[ramp3[(int) p->ramp]];\n\t\t\tp->type = pt_fire;\n\t\t\tfor (j = 0; j < 3; j++)\n\t\t\t\tp->org[j] = point[j] + ((rand() % 6) - 3);\n\t\t\tbreak;\n\t\t}\n\t\tVectorAdd (point, step, point);\n\t}\ndone:\n\treturn leftover;\n}\n\nint PClassic_PointFile(int c, vec3_t point)\n{\n\tcparticle_t *p;\n\n\tif (!free_particles)\n\t\treturn 0;\n\tp = free_particles;\n\tfree_particles = p->next;\n\tp->next = active_particles;\n\tactive_particles = p;\n\n\tVectorClear (p->vel);\n\tp->die = 99999;\n\tp->rgb = d_8to24srgbtable[(-c) & 0xff];\n\tp->type = pt_static;\n\tVectorCopy(point, p->org);\n\n\treturn 1;\n}\n\n//builds a trail from here to there. The trail state can be used to remember how far you got last frame.\nstatic int PClassic_ParticleTrail (vec3_t startpos, vec3_t end, int type, float timestep, int dlkey, vec3_t dlaxis[3], trailkey_t *tk)\n{\n\tfloat leftover;\n\n\tif (type == P_INVALID)\n\t\treturn 1;\n\n\tleftover = Classic_ParticleTrail(startpos, end, Classic_GetLeftover(tk), type);\n\tClassic_SetLeftover(tk, leftover);\n\treturn 0;\n}\n\n//svc_particle support: add X particles with the given colour, velocity, and aproximate origin.\nstatic void PClassic_RunParticleEffect (vec3_t org, vec3_t dir, int color, int count)\n{\n\tClassic_RunParticleEffect(org, dir, color, count, false);\n}\nstatic void PClassic_RunParticleEffectPalette (const char *nameprefix, vec3_t org, vec3_t dir, int color, int count)\n{\n\tClassic_RunParticleEffect(org, dir, color, count, false);\n}\n\nparticleengine_t pe_classic =\n{\n\t\"Classic\",\n\tNULL,\n\n\tPClassic_FindParticleType,\n\tPClassic_Query,\n\n\tPClassic_RunParticleEffectTypeString,\n\tPClassic_ParticleTrail,\n\tPClassic_RunParticleEffectState,\n\tPClassic_RunParticleWeather,\n\tPClassic_RunParticleCube,\n\tPClassic_RunParticleEffect,\n\tPClassic_RunParticleEffect2,\n\tPClassic_RunParticleEffect3,\n\tPClassic_RunParticleEffect4,\n\tPClassic_RunParticleEffectPalette,\n\n\tPClassic_ParticleTrailIndex,\n\tPClassic_InitParticles,\n\tPClassic_ShutdownParticles,\n\tPClassic_DelinkTrailstate,\n\tPClassic_ClearParticles,\n\tPClassic_DrawParticles\n};\n\n#endif\n"
  },
  {
    "path": "engine/client/p_null.c",
    "content": "#include \"quakedef.h\"\r\n#include \"glquake.h\"\r\n\r\n#include \"particles.h\"\r\n#include \"renderque.h\"\r\n\r\n//returns a valid effect if its existance is known.\r\nstatic int PNULL_FindParticleType(const char *name)\r\n{\r\n//\tCon_DPrintf(\"P_FindParticleType %s\\n\", name);\r\n\treturn P_INVALID;\r\n}\r\n\r\nstatic int PNULL_RunParticleEffectTypeString (vec3_t org, vec3_t dir, float count, char *name){return 1;}\r\nstatic int PNULL_ParticleTrail (vec3_t startpos, vec3_t end, int type, float timestep, int dlkey, vec3_t dlaxis[3], trailkey_t *tk){return 1;}\r\nstatic int PNULL_RunParticleEffectState (vec3_t org, vec3_t dir, float count, int typenum, trailkey_t *tk){return 1;}\r\nstatic void PNULL_RunParticleWeather(vec3_t minb, vec3_t maxb, vec3_t dir, float count, int colour, char *efname){}\r\nstatic void PNULL_RunParticleCube(int typenum, vec3_t minb, vec3_t maxb, vec3_t dir_min, vec3_t dir_max, float count, int colour, qboolean gravity, float jitter){}\r\nstatic void PNULL_RunParticleEffect (vec3_t org, vec3_t dir, int color, int count){}\r\nstatic void PNULL_RunParticleEffect2 (vec3_t org, vec3_t dmin, vec3_t dmax, int color, int effect, int count){}\r\nstatic void PNULL_RunParticleEffect3 (vec3_t org, vec3_t box, int color, int effect, int count){}\r\nstatic void PNULL_RunParticleEffect4 (vec3_t org, float radius, int color, int effect, int count){}\r\nstatic void PNULL_RunParticleEffectPalette (const char *nameprefix, vec3_t org, vec3_t dir, int color, int count){}\r\n\r\nstatic void PNULL_ParticleTrailIndex (vec3_t start, vec3_t end, int type, float timestep, int color, int crnd, trailkey_t *tk){}\r\n\r\nstatic qboolean PNULL_InitParticles (void)\r\n{\r\n\treturn true;\r\n}\r\n\r\nstatic void PNULL_ShutdownParticles(void)\r\n{\r\n}\r\n\r\nstatic void PNULL_DelinkTrailstate(trailkey_t *tk)\r\n{\r\n\t*tk = 0;\r\n}\r\nstatic void PNULL_ClearParticles (void){}\r\nstatic void PNULL_DrawParticles(void)\r\n{\r\n\tRSpeedLocals();\r\n\r\n\tRSpeedRemark();\r\n\tRQ_RenderBatchClear();\r\n\tRSpeedEnd(RSPEED_PARTICLESDRAW);\r\n}\r\n\r\n\r\nparticleengine_t pe_null =\r\n{\r\n\t\"null\",\r\n\t\"none\",\r\n\r\n\tPNULL_FindParticleType,\r\n\tNULL,\r\n\r\n\tPNULL_RunParticleEffectTypeString,\r\n\tPNULL_ParticleTrail,\r\n\tPNULL_RunParticleEffectState,\r\n\tPNULL_RunParticleWeather,\r\n\tPNULL_RunParticleCube,\r\n\tPNULL_RunParticleEffect,\r\n\tPNULL_RunParticleEffect2,\r\n\tPNULL_RunParticleEffect3,\r\n\tPNULL_RunParticleEffect4,\r\n\tPNULL_RunParticleEffectPalette,\r\n\r\n\tPNULL_ParticleTrailIndex,\r\n\tPNULL_InitParticles,\r\n\tPNULL_ShutdownParticles,\r\n\tPNULL_DelinkTrailstate,\r\n\tPNULL_ClearParticles,\r\n\tPNULL_DrawParticles\r\n};\r\n"
  },
  {
    "path": "engine/client/p_script.c",
    "content": "/*\nCopyright (C) 1996-1997 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n*/\n\n/*\nThe aim of this particle system is to have as much as possible configurable.\nSome parts still fail here, and are marked FIXME\nEffects are flushed on new maps.\nThe engine has a few builtins.\n*/\n\n#include \"quakedef.h\"\n\n#ifdef PSET_SCRIPT\n\n\n#ifdef FTE_TARGET_WEB\n#define rand myrand\t//emscripten's libc is doing a terrible job of this.\nstatic int rand(void)\n{\t//ripped from glibc\n\tstatic int state = 0xdeadbeef;\n\tint val = ((state * 1103515245) + 12345) & 0x7fffffff;\n\tstate = val;\n\treturn val;\n}\n#endif\n\n#ifdef GLQUAKE\n#include \"glquake.h\"//hack\n#endif\n#include \"shader.h\"\n\n#include \"renderque.h\"\n\n#ifdef HAVE_LEGACY\n#include \"r_partset.h\"\n#else\n#define R_PARTSET_BUILTINS\n#endif\n\n#include \"pr_common.h\"\nextern world_t csqc_world;\n\nstruct\n{\n\tchar *name;\n\tchar **data;\n} partset_list[] =\n{\n\t{\"none\", NULL},\n\tR_PARTSET_BUILTINS\n\t{NULL}\n};\n\nextern qbyte *host_basepal;\n\nstatic float throttle;\n\nextern int PClassic_PointFile(int c, vec3_t point);\nextern particleengine_t pe_classic;\nparticleengine_t *fallback = NULL; //does this really need to be 'extern'?\n#define FALLBACKBIAS 0x1000000\n\n#define PART_VALID(part) ((part) >= 0 && (part) < FALLBACKBIAS)\t//copes with fallback indexes\n\nstatic int pe_default = P_INVALID;\nstatic int pe_size2 = P_INVALID;\nstatic int pe_size3 = P_INVALID;\nstatic int pe_defaulttrail = P_INVALID;\nstatic qboolean pe_script_enabled;\n\nstatic float psintable[256];\n\nstatic qboolean P_LoadParticleSet(char *name, qboolean implicit, qboolean showwarning);\nstatic void R_Particles_KillAllEffects(void);\n\nstatic void buildsintable(void)\n{\n\tint i;\n\tfor (i = 0; i < 256; i++)\n\t\tpsintable[i] = sin((i*M_PI)/128);\n}\n#define sin(x) (psintable[(int)((x)*(128/M_PI)) & 255])\n#define cos(x) (psintable[((int)((x)*(128/M_PI)) + 64) & 255])\n\ntypedef struct particle_s\n{\n\tstruct particle_s\t*next;\n\tfloat\t\tdie;\n\n// driver-usable fields\n\tvec3_t\t\torg;\n\tvec4_t\t\trgba;\n\tfloat\t\tscale;\n\tfloat\t\ts1, t1, s2, t2;\n\n\tvec3_t\t\toldorg;\t//to throttle traces\n\tvec3_t\t\tvel;\t//renderer uses for sparks\n\tfloat\t\tangle;\n\tunion {\n\t\tfloat nextemit;\n\t\ttrailkey_t trailstate;\n\t} state;\n// drivers never touch the following fields\n\tfloat\t\trotationspeed;\n} particle_t;\n\ntypedef struct clippeddecal_s\n{\n\tstruct clippeddecal_s\t*next;\n\tfloat\t\tdie;\n\n\tint entity;\t\t//>0 is a lerpentity, <0 is a csqc ent. 0 is world. woot.\n//\tmodel_t *model;\t//just for paranoia\n\n\tvec3_t\t\tvertex[3];\n\tvec2_t\t\ttexcoords[3];\n\tfloat\t\tvalpha[3];\n\n\tvec4_t\t\trgba;\n} clippeddecal_t;\n\n#define BS_LASTSEG 0x1 // no draw to next, no delete\n#define BS_DEAD    0x2 // segment is dead\n#define BS_NODRAW  0x4 // only used for lerp switching\n\ntypedef struct beamseg_s\n{\n\tstruct beamseg_s *next;  // next in beamseg list\n\n\tparticle_t *p;\n\tint    flags;            // flags for beamseg\n\tvec3_t dir;\n\n\tfloat texture_s;\n} beamseg_t;\n\ntypedef struct trailstate_s {\n\ttrailkey_t key;  // key to check if ts has been overwriten\n\ttrailkey_t assoc; // assoc linked trail\n\tstruct beamseg_s* lastbeam; // last beam pointer (flagged with BS_LASTSEG)\n\tunion {\n\t\tstruct {\n\t\t\tfloat lastdist;\t\t\t// last distance used with particle effect\n\t\t\tfloat laststop;\t\t\t// last stopping point for particle effect\n\t\t} trail;\n\t\tstruct {\n\t\t\tfloat statetime;\t\t// time to emit effect again (used by spawntime field)\n\t\t\tfloat emittime;\t\t\t// used by r_effect emitters\n\t\t} effect;\n\t\ttrailkey_t fallbackkey; // passed to fallback system\n\t};\n} trailstate_t;\n\n\ntypedef struct skytris_s {\n\tstruct skytris_s\t*next;\n\tvec3_t\torg;\n\tvec3_t\tx;\n\tvec3_t\ty;\n\tfloat\tarea;\n\tfloat\tnexttime;\n\tint\t\tptype;\n\tstruct msurface_s\t*face;\n} skytris_t;\n\ntypedef struct skytriblock_s\n{\n\tstruct skytriblock_s *next;\n\tint count;\n\tskytris_t tris[1024];\n} skytriblock_t;\n\n//this is the required render state for each particle\n//dynamic per-particle stuff isn't important. only static state.\ntypedef struct {\n\tenum {PT_NORMAL, PT_SPARK, PT_SPARKFAN, PT_TEXTUREDSPARK, PT_BEAM, PT_VBEAM, PT_CDECAL, PT_UDECAL, PT_INVISIBLE} type;\n\n\tblendmode_t blendmode;\n\tshader_t *shader;\n\tqboolean\tnearest;\n\n\tfloat scalefactor;\n\tfloat invscalefactor;\n\tfloat stretch;\n\tfloat minstretch;\t//limits the particle's length to a multiple of its width.\n\tint premul;\t//0: direct rgba. 1: rgb*a,a (blend). 2: rgb*a,0 (add).\n} plooks_t;\n\n//these could be deltas or absolutes depending on ramping mode.\ntypedef struct {\n\tvec3_t rgb;\n\tfloat alpha;\n\tfloat scale;\n\tfloat rotation;\n} ramp_t;\ntypedef struct {\n\tchar name[MAX_QPATH];\n\tmodel_t *model;\n\tfloat framestart;\n\tfloat framecount;\n\tfloat framerate;\n\tfloat alpha;\n\tvec3_t rgb;\n\tfloat scalemin, scalemax;\n\tint skin;\n\tint traileffect;\n\tunsigned int rflags;\n#define RF_USEORIENTATION Q2RF_CUSTOMSKIN\t//private flag\n} partmodels_t;\ntypedef struct {\n\tchar name[MAX_QPATH];\n\tfloat vol;\n\tfloat atten;\n\tfloat delay;\n\tfloat pitch;\n\tfloat weight;\n} partsounds_t;\n// TODO: merge in alpha with rgb to gain benefit of vector opts\ntypedef struct part_type_s {\n\tchar name[MAX_QPATH];\n\tchar config[MAX_QPATH];\n\tchar texname[MAX_QPATH];\n\n\tint nummodels;\n\tpartmodels_t *models;\n\n\tint numsounds;\n\tpartsounds_t *sounds;\n\n\tvec3_t rgb;\t//initial colour\n\tfloat alpha;\n\tvec3_t rgbchange;\t//colour delta (per second)\n\tfloat alphachange;\n\tvec3_t rgbrand;\t\t//random rgb colour to start with\n\tfloat alpharand;\n\tint colorindex;\t\t//get colour from a palette\n\tint colorrand;\t\t//and add up to this amount\n\tfloat rgbchangetime;//colour stops changing at this time\n\tvec3_t rgbrandsync;\t//like rgbrand, but a single random value instead of separate (can mix)\n\tfloat scale;\t\t//initial scale\n\tfloat scalerand;\t//with up to this much extra\n\tfloat die, randdie;\t//how long it lasts (plus some rand)\n\tfloat veladd, randomveladd;\t\t//scale the incoming velocity by this much\n\tfloat orgadd, randomorgadd;\t\t//spawn the particle this far along its velocity direction\n\tfloat spawnvel, spawnvelvert; //spawn the particle with a velocity based upon its spawn type (generally so it flies outwards)\n\tvec3_t orgbias;\t\t//static 3d world-coord bias\n\tvec3_t velbias;\n\tvec3_t orgwrand;\t//3d world-coord randomisation without relation to spawn mode\n\tvec3_t velwrand;\t//3d world-coord randomisation without relation to spawn mode\n\tfloat viewspacefrac;\n\tfloat flurry;\n\tint surfflagmatch;\t//this decal only spawns on these surfaces\n\tint surfflagmask;\t//this decal only spawns on these surfaces\n\n\tfloat s1, t1, s2, t2;\t//texture coords\n\tfloat texsstride;\t//addition for s for each random slot.\n\tint randsmax;\t//max times the stride can be added\n\n\tplooks_t *slooks;\t//shared looks, so state switches don't apply between particles so much.\n\tplooks_t looks;\t\t//\n\n\tfloat spawntime;\t//time limit for trails\n\tfloat spawnchance;\t//if < 0, particles might not spawn so many\n\n\tfloat rotationstartmin, rotationstartrand;\n\tfloat rotationmin, rotationrand;\n\n\tfloat scaledelta;\n\tfloat countextra;\n\tfloat count;\n\tfloat countrand;\n\tfloat countspacing; //for trails.\n\tfloat countoverflow; //for badly-designed effects, instead of depending on trail state.\n\tfloat rainfrequency;\n\n\tint assoc;\n\tint cliptype;\n\tint inwater;\n\tfloat clipcount;\n\tint emit;\n\tfloat emittime;\n\tfloat emitrand;\n\tfloat emitstart;\n\n\tfloat areaspread;\n\tfloat areaspreadvert;\n\n\tfloat spawnparam1;\n\tfloat spawnparam2;\n/*\tfloat spawnparam3; */\n\n\tenum {\n\t\tSM_BOX, //box = even spread within the area\n\t\tSM_CIRCLE, //circle = around edge of a circle\n\t\tSM_BALL, //ball = filled sphere\n\t\tSM_SPIRAL, //spiral = spiral trail\n\t\tSM_TRACER, //tracer = tracer trail\n\t\tSM_TELEBOX, //telebox = q1-style telebox\n\t\tSM_LAVASPLASH, //lavasplash = q1-style lavasplash\n\t\tSM_UNICIRCLE, //unicircle = uniform circle\n\t\tSM_FIELD, //field = synced field (brightfield, etc)\n\t\tSM_DISTBALL, // uneven distributed ball\n\t\tSM_MESHSURFACE, //distributed roughly evenly over the surface of the mesh\n\t\tSM_FIXMEWARNING,\t//for people to use to mark placeholder effects.\n\t} spawnmode;\n\n\tfloat gravity;\n\tvec3_t friction;\n\tfloat clipbounce;\n\tfloat stainonimpact;\n\n\tvec3_t dl_rgb;\n\tfloat dl_radius[2];\n\tfloat dl_time;\n\tvec4_t dl_decay;\n\tfloat dl_corona_intensity;\n\tfloat dl_corona_scale;\n\tvec3_t dl_scales;\n\t//PT_NODLSHADOW\n\tint dl_cubemapnum;\n\tint dl_lightstyle;\n\tvec3_t stain_rgb;\n\tfloat stain_radius;\n\n\tenum {RAMP_NONE, RAMP_DELTA, RAMP_NEAREST, RAMP_LERP} rampmode;\n\tint rampindexes;\n\tramp_t *ramp;\n\n\tint loaded;\t//0 if not loaded, 1 if automatically loaded, 2 if user loaded\n\tparticle_t\t*particles;\n\tclippeddecal_t *clippeddecals;\n\tbeamseg_t *beams;\n\tstruct part_type_s *nexttorun;\n\tstruct part_type_s **runlink;\n\n\tunsigned int flags;\n#define PT_VELOCITY\t\t\t0x0001\t// has velocity modifiers\n#define PT_FRICTION\t\t\t0x0002\t// has friction modifiers\n#define PT_CHANGESCOLOUR\t0x0004\n#define PT_CITRACER\t\t\t0x0008\t// Q1-style tracer behavior for colorindex\n#define PT_INVFRAMETIME\t\t0x0010\t// apply inverse frametime to count (causes emits to be per frame)\n#define PT_AVERAGETRAIL\t\t0x0020\t// average trail points from start to end, useful with t_lightning, etc\n#define PT_NOSTATE\t\t\t0x0040\t// don't use trailstate for this emitter (careful with assoc...)\n#define PT_NOSPREADFIRST\t0x0080\t// don't randomize org/vel for first generated particle\n#define PT_NOSPREADLAST\t\t0x0100\t// don't randomize org/vel for last generated particle\n#define PT_TROVERWATER\t\t0x0200\t// don't spawn if underwater\n#define PT_TRUNDERWATER\t\t0x0400\t// don't spawn if overwater\n#define PT_NODLSHADOW\t\t0x0800\t// dlights from this effect don't cast shadows.\n#define PT_WORLDSPACERAND\t0x1000\t// effect has orgwrand or velwrand properties\n\tunsigned int fluidmask;\n\n\tunsigned int state;\n#define PS_INRUNLIST 0x1 // particle type is currently in execution list\n} part_type_t;\n\ntypedef struct pcfg_s\n{\n\tstruct pcfg_s *next;\n\tchar name[1];\n} pcfg_t;\nstatic pcfg_t *loadedconfigs;\n\n#ifndef TYPESONLY\n\n//triangle fan sparks use these. // defined but not used\n//static double sint[7] = {0.000000, 0.781832,  0.974928,  0.433884, -0.433884, -0.974928, -0.781832};\n//static double cost[7] = {1.000000, 0.623490, -0.222521, -0.900969, -0.900969, -0.222521,  0.623490};\n\n#define crand() (rand()%32767/16383.5f-1)\n\nstatic void P_ReadPointFile_f (void);\n#ifdef HAVE_LEGACY\nstatic void P_ExportBuiltinSet_f(void);\n#endif\n\n#define MAX_BEAMSEGS\t\t\t(1<<11)\t// default max # of beam segments\n#define MAX_PARTICLES\t\t\t(1<<18)\t// max # of particles at one time\n#define MAX_DECALS\t\t\t\t(1<<18)\t// max # of decal fragments at one time\n#define MAX_TRAILSTATES\t\t\t(1<<10)\t// default max # of trailstates\n\n//int\t\tramp1[8] = {0x6f, 0x6d, 0x6b, 0x69, 0x67, 0x65, 0x63, 0x61};\n//int\t\tramp2[8] = {0x6f, 0x6e, 0x6d, 0x6c, 0x6b, 0x6a, 0x68, 0x66};\n//int\t\tramp3[8] = {0x6d, 0x6b, 6,\t  5,    4,    3,    2,    1};\n\nparticle_t\t*free_particles;\nparticle_t\t*particles;\t//contains the initial list of alloced particles.\nint\t\t\tr_numparticles;\nint\t\t\tr_particlerecycle;\n\nbeamseg_t   *free_beams;\nbeamseg_t   *beams;\nint\t\t\tr_numbeams;\n\nclippeddecal_t\t*free_decals;\nclippeddecal_t\t*decals;\nint\t\t\tr_numdecals;\nint\t\t\tr_decalrecycle;\n\ntrailstate_t *trailstates;\nint\t\t\tts_cycle; // current cyclic index of trailstates\nint\t\t\tr_numtrailstates;\n\nstatic\t\tqboolean r_plooksdirty;\t//a particle effect was changed, reevaluate shared looks.\n\nextern cvar_t r_bouncysparks;\nextern cvar_t r_part_rain;\nextern cvar_t r_bloodstains;\nextern cvar_t gl_part_flame;\nextern cvar_t r_decal_noperpendicular;\n\nstatic void PScript_EmitSkyEffectTris(model_t *mod, msurface_t \t*fa, int ptype);\nstatic void FinishParticleType(part_type_t *ptype);\n// callbacks\nstatic void QDECL R_ParticleDesc_Callback(struct cvar_s *var, char *oldvalue);\n\nextern cvar_t r_particledesc;\nextern cvar_t r_part_rain_quantity;\nextern cvar_t r_particle_tracelimit;\nextern cvar_t r_part_sparks;\nextern cvar_t r_part_sparks_trifan;\nextern cvar_t r_part_sparks_textured;\nextern cvar_t r_part_beams;\nextern cvar_t r_part_contentswitch;\nextern cvar_t r_part_density;\nextern cvar_t r_part_maxparticles;\nextern cvar_t r_part_maxdecals;\n\nstatic float particletime;\n\n#define BUFFERVERTS 2048*4\nstatic vecV_t pscriptverts[BUFFERVERTS];\nstatic avec4_t pscriptcolours[BUFFERVERTS];\nstatic vec2_t pscripttexcoords[BUFFERVERTS];\nstatic index_t pscriptquadindexes[(BUFFERVERTS/4)*6];\nstatic index_t pscripttriindexes[BUFFERVERTS];\nstatic mesh_t pscriptmesh;\nstatic mesh_t pscripttmesh;\n\nstatic int numparticletypes;\nstatic part_type_t *part_type;\nstatic part_type_t *part_run_list;\n\nextern char part_parsenamespace[MAX_QPATH];\nstatic qboolean part_parseweak;\n\nstatic struct {\n\tchar *oldn;\n\tchar *newn;\n} legacynames[] =\n{\n\t{\"t_rocket\",\t\"TR_ROCKET\"},\n\n\t//{\"t_blastertrail\", \"TR_BLASTERTRAIL\"},\n\t{\"t_grenade\",\t\"TR_GRENADE\"},\n\t{\"t_gib\",\t\t\"TR_BLOOD\"},\n\n\t{\"te_plasma\",\t\"TE_TEI_PLASMAHIT\"},\n\t{\"te_smoke\",\t\"TE_TEI_SMOKE\"},\n\n\t{NULL}\n};\n\nstatic part_type_t *P_GetParticleType(const char *config, const char *name)\n{\n\tint i;\n\tpart_type_t *ptype;\n\tpart_type_t *oldlist = part_type;\n\tchar cfgbuf[MAX_QPATH];\n\tchar *dot = strchr(name, '.');\n\tif (dot && (dot - name) < MAX_QPATH-1)\n\t{\n\t\tconfig = cfgbuf;\n\t\tmemcpy(cfgbuf, name, dot - name);\n\t\tcfgbuf[dot - name] = 0;\n\t\tname = dot+1;\n\t}\n\n\tfor (i = 0; legacynames[i].oldn; i++)\n\t{\n\t\tif (!strcmp(name, legacynames[i].oldn))\n\t\t{\n\t\t\tname = legacynames[i].newn;\n\t\t\tbreak;\n\t\t}\n\t}\n\tfor (i = 0; i < numparticletypes; i++)\n\t{\n\t\tptype = &part_type[i];\n\t\tif (!stricmp(ptype->name, name))\n\t\t\tif (!stricmp(ptype->config, config))\t//must be an exact match.\n\t\t\t\treturn ptype;\n\t}\n\tpart_type = BZ_Realloc(part_type, sizeof(part_type_t)*(numparticletypes+1));\n\tptype = &part_type[numparticletypes++];\n\tmemset(ptype, 0, sizeof(*ptype));\n\tQ_strncpyz(ptype->name, name, sizeof(ptype->name));\n\tQ_strncpyz(ptype->config, config, sizeof(ptype->config));\n\tptype->assoc = P_INVALID;\n\tptype->inwater = P_INVALID;\n\tptype->cliptype = P_INVALID;\n\tptype->emit = P_INVALID;\n\n\tif (oldlist)\n\t{\n\t\tpart_type_t **link;\n\t\tif (part_run_list)\n\t\t\tpart_run_list = (part_type_t*)((char*)part_run_list - (char*)oldlist + (char*)part_type);\n\n\t\tfor (i = 0; i < numparticletypes; i++)\n\t\t{\n\t\t\tif (part_type[i].nexttorun)\n\t\t\t\tpart_type[i].nexttorun = (part_type_t*)((char*)part_type[i].nexttorun - (char*)oldlist + (char*)part_type);\n\t\t\tpart_type[i].runlink = NULL;\n\t\t}\n\t\tfor (link = &part_run_list; *link;)\n\t\t{\n\t\t\t(*link)->runlink = link;\n\t\t\tlink = &(*link)->nexttorun;\n\t\t}\n\t}\n\n\tptype->loaded = 0;\n\tptype->ramp = NULL;\n\tptype->particles = NULL;\n\tptype->beams = NULL;\n\n\tr_plooksdirty = true;\n\treturn ptype;\n}\n\n//unconditionally allocates a particle object. this allows out-of-order allocations.\nstatic int P_AllocateParticleType(const char *config, const char *name)\t//guarentees that the particle type exists, returning it's index.\n{\n\tpart_type_t *pt = P_GetParticleType(config, name);\n\treturn pt - part_type;\n}\n\nstatic void PScript_RetintEffect(part_type_t *to, part_type_t *from, const char *colourcodes)\n{\n\tchar name[sizeof(to->name)];\n\tchar config[sizeof(to->config)];\n\n\tQ_strncpyz(name, to->name, sizeof(to->name));\n\tQ_strncpyz(config, to->config, sizeof(to->config));\n\n\t//'to' was already purged, so we don't need to care about that.\n\tmemcpy(to, from, sizeof(*to));\n\n\tQ_strncpyz(to->name, name, sizeof(to->name));\n\tQ_strncpyz(to->config, config, sizeof(to->config));\n\n\t//make sure 'to' has its own copy of any lists, so that we don't have issues when freeing this memory again.\n\tif (to->models)\n\t{\n\t\tto->models = BZ_Malloc(to->nummodels * sizeof(*to->models));\n\t\tmemcpy(to->models, from->models, to->nummodels * sizeof(*to->models));\n\t}\n\tif (to->sounds)\n\t{\n\t\tto->sounds = BZ_Malloc(to->numsounds * sizeof(*to->sounds));\n\t\tmemcpy(to->sounds, from->sounds, to->numsounds * sizeof(*to->sounds));\n\t}\n\tif (to->ramp)\n\t{\n\t\tto->ramp = BZ_Malloc(to->rampindexes * sizeof(*to->ramp));\n\t\tmemcpy(to->ramp, from->ramp, to->rampindexes * sizeof(*to->ramp));\n\t}\n\n\t//'from' might still have some links so we need to clear those out.\n\tto->nexttorun = NULL;\n\tto->runlink = NULL;\n\tto->particles = NULL;\n\tto->clippeddecals = NULL;\n\tto->beams = NULL;\n\tto->slooks = &to->looks;\n\tr_plooksdirty = true;\n\n\tto->colorindex = strtoul(colourcodes, (char**)&colourcodes, 10);\n\tif (*colourcodes == '_')\n\t\tcolourcodes++;\n\tto->colorrand = strtoul(colourcodes, (char**)&colourcodes, 10);\n}\n\n//public interface. get without creating.\nstatic int PScript_FindParticleType(const char *fullname)\n{\n\tint i;\n\tpart_type_t *ptype = NULL;\n\tchar cfg[MAX_QPATH];\n\tchar *dot;\n\tconst char *name = fullname;\n\tdot = strchr(name, '.');\n\tif (dot && (dot - name) < MAX_QPATH-1)\n\t{\n\t\tmemcpy(cfg, name, dot - name);\n\t\tcfg[dot-name] = 0;\n\t\tname = dot+1;\n\t}\n\telse\n\t\t*cfg = 0;\n\n\tfor (i = 0; legacynames[i].oldn; i++)\n\t{\n\t\tif (!strcmp(name, legacynames[i].oldn))\n\t\t{\n\t\t\tname = legacynames[i].newn;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tif (*cfg)\n\t{\t//favour the namespace if one is specified\n\t\tfor (i = 0; i < numparticletypes; i++)\n\t\t{\n\t\t\tif (!stricmp(part_type[i].name, name))\n\t\t\t{\n\t\t\t\tif (!stricmp(part_type[i].config, cfg))\n\t\t\t\t{\n\t\t\t\t\tptype = &part_type[i];\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\telse\n\t{\n\t\t//but be prepared to load it from any namespace if its not got a namespace specified.\n\t\tfor (i = 0; i < numparticletypes; i++)\n\t\t{\n\t\t\tif (!stricmp(part_type[i].name, name))\n\t\t\t{\n\t\t\t\tptype = &part_type[i];\n\t\t\t\tif (ptype->loaded)\t//(mostly) ignore ones that are not currently loaded\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\tif (!ptype || !ptype->loaded)\n\t{\n\t\tif (!strnicmp(name, \"te_explosion2_\", 14))\n\t\t{\n\t\t\tint from = PScript_FindParticleType(va(\"%s.te_explosion2\", cfg));\n\t\t\tif (from != P_INVALID)\n\t\t\t{\n\t\t\t\tint to = P_AllocateParticleType(part_type[from].config, name);\n\t\t\t\tPScript_RetintEffect(&part_type[to], &part_type[from], name+14);\n\t\t\t\treturn to;\n\t\t\t}\n\t\t}\n\t\tif (*cfg)\n\t\t\tP_LoadParticleSet(cfg, true, true);\n\n\t\tif (fallback)\n\t\t{\n\t\t\tif (!strncmp(name, \"classic_\", 8))\n\t\t\t\ti = fallback->FindParticleType(name+8);\n\t\t\telse\n\t\t\t\ti = fallback->FindParticleType(name);\n\t\t\tif (i != P_INVALID)\n\t\t\t\treturn i+FALLBACKBIAS;\n\t\t}\n\t\treturn P_INVALID;\n\t}\n\treturn i;\n}\n\nstatic void P_SetModified(void)\t//called when the particle system changes (from console).\n{\n\tif (Cmd_IsInsecure())\n\t\treturn;\t//server stuffed particle descriptions don't count.\n\n\tf_modified_particles = true;\n\n\tif (care_f_modified)\n\t{\n\t\tcare_f_modified = false;\n\t\tCbuf_AddText(\"say particles description has changed\\n\", RESTRICT_LOCAL);\n\t}\n}\nstatic int CheckAssosiation(char *config, char *name, int from)\n{\n\tint to, orig;\n\n\torig = to = P_AllocateParticleType(config, name);\n\n\twhile(to != P_INVALID)\n\t{\n\t\tif (to == from)\n\t\t{\n\t\t\tCon_Printf(\"Assosiation of %s would cause infinate loop\\n\", name);\n\t\t\treturn P_INVALID;\n\t\t}\n\t\tto = part_type[to].assoc;\n\t}\n\treturn orig;\n}\n\nstatic void P_LoadTexture(part_type_t *ptype, qboolean warn)\n{\n\ttexnums_t tn;\n\tchar *defaultshader;\n\tchar *namepostfix;\n\tint i;\n\tif (qrenderer == QR_NONE)\n\t\treturn;\n\n\tfor (i = 0; i < ptype->nummodels; i++)\n\t\tptype->models[i].model = NULL;\n\n\tif (*ptype->texname)\n\t{\n\t\tchar *bmpostfix;\n\t\tswitch(ptype->looks.blendmode)\n\t\t{\t//we typically need the blendmode as part of the shader name, so that we don't end up with collisions with default shaders and different particle blend modes.\n\t\t\t//shader blend modes still override, although I guess this way the shader itself can contain conditionals to use different blend modes... if needed.\n\t\tdefault:\t\t\tbmpostfix = \"#BLEND\"; break;\n\t\tcase BM_BLEND:\t\tbmpostfix = \"\"; break;\n\t\tcase BM_BLENDCOLOUR:bmpostfix = \"#BLENDCOLOUR\"; break;\n\t\tcase BM_ADDA:\t\tbmpostfix = \"#ADDA\"; break;\n\t\tcase BM_ADDC:\t\tbmpostfix = \"#ADDC\"; break;\n\t\tcase BM_SUBTRACT:\tbmpostfix = \"#SUBTRACT\"; break;\n\t\tcase BM_INVMODA:\tbmpostfix = \"#INVMODA\"; break;\n\t\tcase BM_INVMODC:\tbmpostfix = \"#INVMODC\"; break;\n\t\tcase BM_PREMUL:\t\tbmpostfix = \"#PREMUL\"; break;\n\t\tcase BM_RTSMOKE:\tbmpostfix = \"#RTSMOKE\"; break;\n\t\t}\n\t\t/*try and load the shader, fail if we would need to generate one*/\n\t\tptype->looks.shader = R_RegisterCustom(NULL, va(\"%s%s\", ptype->texname, bmpostfix), SUF_NONE, NULL, NULL);\n\t}\n\telse\n\t\tptype->looks.shader = NULL;\n\n\tif (!ptype->looks.shader)\n\t{\n\t\t/*okay, so no shader, generate a shader that matches the legacy/shaderless mode*/\n\t\tswitch(ptype->looks.blendmode)\n\t\t{\n\t\tcase BM_BLEND:\n\t\tdefault:\n\t\t\tnamepostfix = \"_blend\";\n\t\t\tdefaultshader =\n\t\t\t\t\"{\\n\"\n\t\t\t\t\t\"program defaultsprite\\n\"\n\t\t\t\t\t\"nomipmaps\\n\"\n\t\t\t\t\t\"sort unlitdecal\\n\"\n\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\"map $diffuse\\n\"\n\t\t\t\t\t\t\"blendfunc blend\\n\"\n\t\t\t\t\t\t\"rgbgen vertex\\n\"\n\t\t\t\t\t\t\"alphagen vertex\\n\"\n\t\t\t\t\t\t\"nodepth\\n\"\n\t\t\t\t\t\"}\\n\"\n\t\t\t\t\t\"polygonoffset\\n\"\n\t\t\t\t\t\"surfaceparm noshadows\\n\"\n\t\t\t\t\t\"surfaceparm nodlight\\n\"\n\t\t\t\t\"}\\n\"\n\t\t\t\t;\n\t\t\tbreak;\n\t\tcase BM_BLENDCOLOUR:\n\t\t\tnamepostfix = \"_bc\";\n\t\t\tdefaultshader =\n\t\t\t\t\"{\\n\"\n\t\t\t\t\t\"program defaultsprite\\n\"\n\t\t\t\t\t\"nomipmaps\\n\"\n\t\t\t\t\t\"sort unlitdecal\\n\"\n\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\"map $diffuse\\n\"\n\t\t\t\t\t\t\"blendfunc GL_SRC_COLOR GL_ONE_MINUS_SRC_COLOR\\n\"\n\t\t\t\t\t\t\"rgbgen vertex\\n\"\n\t\t\t\t\t\t\"alphagen vertex\\n\"\n\t\t\t\t\t\t\"nodepth\\n\"\n\t\t\t\t\t\"}\\n\"\n\t\t\t\t\t\"polygonoffset\\n\"\n\t\t\t\t\t\"surfaceparm noshadows\\n\"\n\t\t\t\t\t\"surfaceparm nodlight\\n\"\n\t\t\t\t\"}\\n\"\n\t\t\t\t;\n\t\t\tbreak;\n\t\tcase BM_ADDA:\n\t\t\tnamepostfix = \"_add\";\n\t\t\tdefaultshader =\n\t\t\t\t\"{\\n\"\n\t\t\t\t\t\"program defaultsprite\\n\"\n\t\t\t\t\t\"nomipmaps\\n\"\n\t\t\t\t\t\"sort additive\\n\"\n\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\"map $diffuse\\n\"\n\t\t\t\t\t\t\"blendfunc GL_SRC_ALPHA GL_ONE\\n\"\n\t\t\t\t\t\t\"rgbgen vertex\\n\"\n\t\t\t\t\t\t\"alphagen vertex\\n\"\n\t\t\t\t\t\t\"nodepth\\n\"\n\t\t\t\t\t\"}\\n\"\n\t\t\t\t\t\"polygonoffset\\n\"\n\t\t\t\t\t\"surfaceparm noshadows\\n\"\n\t\t\t\t\t\"surfaceparm nodlight\\n\"\n\t\t\t\t\"}\\n\"\n\t\t\t\t;\n\t\t\tbreak;\n\t\tcase BM_ADDC:\n\t\t\tnamepostfix = \"_add\";\n\t\t\tdefaultshader =\n\t\t\t\t\"{\\n\"\n\t\t\t\t\t\"program defaultsprite\\n\"\n\t\t\t\t\t\"nomipmaps\\n\"\n\t\t\t\t\t\"sort additive\\n\"\n\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\"map $diffuse\\n\"\n\t\t\t\t\t\t\"blendfunc GL_SRC_COLOR GL_ONE\\n\"\n\t\t\t\t\t\t\"rgbgen vertex\\n\"\n\t\t\t\t\t\t\"alphagen vertex\\n\"\n\t\t\t\t\t\t\"nodepth\\n\"\n\t\t\t\t\t\"}\\n\"\n\t\t\t\t\t\"polygonoffset\\n\"\n\t\t\t\t\t\"surfaceparm noshadows\\n\"\n\t\t\t\t\t\"surfaceparm nodlight\\n\"\n\t\t\t\t\"}\\n\"\n\t\t\t\t;\n\t\t\tbreak;\n\t\tcase BM_PREMUL:\n\t\t\tnamepostfix = \"_premul\";\n\t\t\tdefaultshader =\n\t\t\t\t\"{\\n\"\n\t\t\t\t\t\"program defaultsprite\\n\"\n\t\t\t\t\t\"nomipmaps\\n\"\n\t\t\t\t\t\"sort additive\\n\"\n\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\"map $diffuse\\n\"\n\t\t\t\t\t\t\"blendfunc GL_ONE GL_ONE_MINUS_SRC_ALPHA\\n\"\n\t\t\t\t\t\t\"rgbgen vertex\\n\"\n\t\t\t\t\t\t\"alphagen vertex\\n\"\n\t\t\t\t\t\t\"nodepth\\n\"\n\t\t\t\t\t\"}\\n\"\n\t\t\t\t\t\"polygonoffset\\n\"\n\t\t\t\t\t\"surfaceparm noshadows\\n\"\n\t\t\t\t\t\"surfaceparm nodlight\\n\"\n\t\t\t\t\"}\\n\"\n\t\t\t\t;\n\t\t\tbreak;\n\t\tcase BM_INVMODA:\n\t\t\tnamepostfix = \"_invmoda\";\n\t\t\tdefaultshader =\n\t\t\t\t\"{\\n\"\n\t\t\t\t\t\"program defaultsprite\\n\"\n\t\t\t\t\t\"nomipmaps\\n\"\n\t\t\t\t\t\"sort unlitdecal\\n\"\n\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\"map $diffuse\\n\"\n\t\t\t\t\t\t\"blendfunc GL_ZERO GL_ONE_MINUS_SRC_ALPHA\\n\"\n\t\t\t\t\t\t\"rgbgen vertex\\n\"\n\t\t\t\t\t\t\"alphagen vertex\\n\"\n\t\t\t\t\t\t\"nodepth\\n\"\n\t\t\t\t\t\"}\\n\"\n\t\t\t\t\t\"polygonoffset\\n\"\n\t\t\t\t\t\"surfaceparm noshadows\\n\"\n\t\t\t\t\t\"surfaceparm nodlight\\n\"\n\t\t\t\t\"}\\n\"\n\t\t\t\t;\n\t\t\tbreak;\n\t\tcase BM_INVMODC:\n\t\t\tnamepostfix = \"_invmodc\";\n\t\t\tdefaultshader =\n\t\t\t\t\"{\\n\"\n\t\t\t\t\t\"program defaultsprite\\n\"\n\t\t\t\t\t\"nomipmaps\\n\"\n\t\t\t\t\t\"sort unlitdecal\\n\"\n\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\"map $diffuse\\n\"\n\t\t\t\t\t\t\"blendfunc GL_ZERO GL_ONE_MINUS_SRC_COLOR\\n\"\n\t\t\t\t\t\t\"rgbgen vertex\\n\"\n\t\t\t\t\t\t\"alphagen vertex\\n\"\n\t\t\t\t\t\t\"nodepth\\n\"\n\t\t\t\t\t\"}\\n\"\n\t\t\t\t\t\"polygonoffset\\n\"\n\t\t\t\t\t\"surfaceparm noshadows\\n\"\n\t\t\t\t\t\"surfaceparm nodlight\\n\"\n\t\t\t\t\"}\\n\"\n\t\t\t\t;\n\t\t\tbreak;\n\t\tcase BM_SUBTRACT:\n\t\t\tnamepostfix = \"_sub\";\n\t\t\tdefaultshader =\n\t\t\t\t\"{\\n\"\n\t\t\t\t\t\"program defaultsprite\\n\"\n\t\t\t\t\t\"nomipmaps\\n\"\n\t\t\t\t\t\"sort unlitdecal\\n\"\n\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\"map $diffuse\\n\"\n\t\t\t\t\t\t\"blendfunc GL_SRC_ALPHA GL_ONE_MINUS_SRC_COLOR\\n\"\n\t\t\t\t\t\t\"rgbgen vertex\\n\"\n\t\t\t\t\t\t\"alphagen vertex\\n\"\n\t\t\t\t\t\t\"nodepth\\n\"\n\t\t\t\t\t\"}\\n\"\n\t\t\t\t\t\"polygonoffset\\n\"\n\t\t\t\t\t\"surfaceparm noshadows\\n\"\n\t\t\t\t\t\"surfaceparm nodlight\\n\"\n\t\t\t\t\"}\\n\"\n\t\t\t\t;\n\t\t\tbreak;\n\t\tcase BM_RTSMOKE:\n\t\t\tnamepostfix = \"_rts\";\n\t\t\tdefaultshader =\n\t\t\t\t\"{\\n\"\n\t\t\t\t\t\"program defaultsprite#LIGHT\\n\"\n\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\"map $diffuse\\n\"\n\t\t\t\t\t\t\"blendfunc GL_SRC_ALPHA GL_ONE_MINUS_SRC_ALPHA\\n\"\n\t\t\t\t\t\t\"rgbgen const vertex\\n\"\n\t\t\t\t\t\t\"alphagen vertex\\n\"\n\t\t\t\t\t\"}\\n\"\n\t\t\t\t\t\"surfaceparm noshadows\\n\"\n\t\t\t\t\t\"sort seethrough\\n\"\t//needs to be low enough that its subject to rtlights\n\t\t\t\t\t\"bemode rtlight\\n\"\n\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\"program rtlight#NOBUMP#VERTEXCOLOURS\\n\"\n\t\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\t\"map $diffuse\\n\"\n\t\t\t\t\t\t\t\"blendfunc add\\n\"\n\t\t\t\t\t\t\"}\\n\"\n\t\t\t\t\t\"}\\n\"\n\t\t\t\t\"}\\n\";\n\t\t\tbreak;\n\t\t}\n\n\t\tmemset(&tn, 0, sizeof(tn));\n\t\tif (*ptype->texname)\n\t\t{\n\t\t\ttn.base = R_LoadHiResTexture(ptype->texname, \"particles\", IF_LOADNOW | IF_NOMIPMAP|(ptype->looks.nearest?IF_NEAREST:IF_LINEAR)|(ptype->looks.premul?IF_PREMULTIPLYALPHA:0));\t//mipmapping breaks particlefont stuff\n\t\t\tif (tn.base && tn.base->status == TEX_LOADING)\n\t\t\t\tCOM_WorkerPartialSync(tn.base, &tn.base->status, TEX_LOADING);\n\t\t\tif (!TEXLOADED(tn.base))\n\t\t\t\ttn.base = R_LoadHiResTexture(ptype->texname, \"particles\", IF_CLAMP|IF_NOREPLACE|IF_LOADNOW | IF_NOMIPMAP|(ptype->looks.nearest?IF_NEAREST:IF_LINEAR)|(ptype->looks.premul?IF_PREMULTIPLYALPHA:0));\t//mipmapping breaks particlefont stuff\n\t\t}\n\t\telse\n\t\t\ttn.base = NULL;\n\t\tif (!TEXLOADED(tn.base))\n\t\t{\n\t\t\t/*okay, so the texture they specified wasn't valid either. use a fully default one*/\n\n\t\t\t//note that this could get messy if you depend upon vid_restart to reload your effect without re-execing it after.\n\t\t\tptype->s1 = 0;\n\t\t\tptype->t1 = 0;\n\t\t\tptype->s2 = 1;\n\t\t\tptype->t2 = 1;\n\t\t\tptype->randsmax = 1;\n\t\t\tif (ptype->looks.type == PT_SPARK)\n\t\t\t{\n\t\t\t\textern texid_t r_whiteimage;\n\t\t\t\tptype->looks.shader = R_RegisterShader(va(\"line%s\", namepostfix), SUF_NONE, defaultshader);\n\t\t\t\tTEXASSIGNF(tn.base, r_whiteimage);\n\t\t\t}\n\t\t\telse if (ptype->looks.type == PT_BEAM)\n\t\t\t{\n\t\t\t\t/*untextured beams get a single continuous blob*/\n\t\t\t\tptype->looks.shader = R_RegisterShader(va(\"beam%s\", namepostfix), SUF_NONE, defaultshader);\n\t\t\t\tTEXASSIGNF(tn.base, beamtexture);\n\t\t\t}\n\t\t\telse if (ptype->looks.type == PT_VBEAM)\n\t\t\t{\n\t\t\t\t/*untextured beams get a single continuous blob*/\n\t\t\t\tptype->looks.shader = R_RegisterShader(va(\"vbeam%s\", namepostfix), SUF_NONE, defaultshader);\n\t\t\t\tTEXASSIGNF(tn.base, beamtexture);\n\t\t\t}\n\t\t\telse if (ptype->looks.type == PT_SPARKFAN)\n\t\t\t{\n\t\t\t\t/*untextured beams get a single continuous blob*/\n\t\t\t\tptype->looks.shader = R_RegisterShader(va(\"fan%s\", namepostfix), SUF_NONE, defaultshader);\n\t\t\t\tTEXASSIGNF(tn.base, ptritexture);\n\t\t\t}\n\t\t\telse if (strstr(ptype->texname, \"glow\") || strstr(ptype->texname, \"ball\") || ptype->looks.type == PT_TEXTUREDSPARK)\n\t\t\t{\n\t\t\t\t/*sparks and special names get a nice circular texture.\n\t\t\t\tas these are fully default, we can basically discard the texture name in the shader, and get better batching*/\n\t\t\t\tptype->looks.shader = R_RegisterShader(va(\"ball%s\", namepostfix), SUF_NONE, defaultshader);\n\t\t\t\tTEXASSIGNF(tn.base, balltexture);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t/*anything else gets a fuzzy texture*/\n\t\t\t\tptype->looks.shader = R_RegisterShader(va(\"default%s\", namepostfix), SUF_NONE, defaultshader);\n\t\t\t\tTEXASSIGNF(tn.base, explosiontexture);\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\t/*texture looks good, make a shader, and give it the texture as a diffuse stage*/\n\t\t\tptype->looks.shader = R_RegisterShader(va(\"%s%s\", ptype->texname, namepostfix), SUF_NONE, defaultshader);\n\t\t}\n\t\tR_BuildDefaultTexnums(&tn, ptype->looks.shader, 0);\n\t}\n\telse\n\t\tR_BuildDefaultTexnums(NULL, ptype->looks.shader, 0);\n}\n\nstatic void P_ResetToDefaults(part_type_t *ptype)\n{\n\tparticle_t *parts;\n\tchar tnamebuf[sizeof(ptype->name)];\n\tchar tconfbuf[sizeof(ptype->config)];\n\n\t// go with a lazy clear of list.. mark everything as DEAD and let\n\t// the beam rendering handle removing nodes\n\tbeamseg_t *beamsegs = ptype->beams;\n\twhile (beamsegs)\n\t{\n\t\tbeamsegs->flags |= BS_DEAD;\n\t\tbeamsegs = beamsegs->next;\n\t}\n\n\t// forget any particles before its wiped\n\twhile (ptype->particles)\n\t{\n\t\tparts = ptype->particles->next;\n\t\tptype->particles->next = free_particles;\n\t\tfree_particles = ptype->particles;\n\t\tptype->particles = parts;\n\t}\n\n\t// if we're in the runstate loop through and remove from linked list\n\tif (ptype->state & PS_INRUNLIST)\n\t{\n\t\t*ptype->runlink = ptype->nexttorun;\n\t\tif (ptype->nexttorun)\n\t\t\tptype->nexttorun->runlink = ptype->runlink;\n\t}\n\n\t//some things need to be preserved before we clear everything.\n\tbeamsegs = ptype->beams;\n\tstrcpy(tnamebuf, ptype->name);\n\tstrcpy(tconfbuf, ptype->config);\n\n\t//free uneeded info\n\tif (ptype->ramp)\n\t\tBZ_Free(ptype->ramp);\n\tif (ptype->models)\n\t\tBZ_Free(ptype->models);\n\tif (ptype->sounds)\n\t\tBZ_Free(ptype->sounds);\n\n\t//reset everything we're too lazy to specifically set\n\tmemset(ptype, 0, sizeof(*ptype));\n\n\t//now set any non-0 defaults.\n\n\tptype->beams = beamsegs;\n\tptype->rainfrequency = 1;\n\tstrcpy(ptype->name, tnamebuf);\n\tstrcpy(ptype->config, tconfbuf);\n\tptype->assoc=P_INVALID;\n\tptype->inwater = P_INVALID;\n\tptype->cliptype = P_INVALID;\n\tptype->emit = P_INVALID;\n\tptype->fluidmask = FTECONTENTS_FLUID;\n\tptype->alpha = 1;\n\tptype->alphachange = 1;\n\tptype->clipbounce = 0.8;\n\tptype->clipcount = 1;\n\tptype->colorindex = -1;\n\tptype->rotationstartmin = -M_PI;\t//start with a random angle\n\tptype->rotationstartrand = M_PI-ptype->rotationstartmin;\n\tptype->spawnchance = 1;\n\tptype->dl_time = 0;\n\tptype->dl_lightstyle = -1;\n\tVectorSet(ptype->dl_rgb, 1, 1, 1);\n\tptype->dl_corona_intensity = 0.25;\n\tptype->dl_corona_scale = 0.5;\n\tVectorSet(ptype->dl_scales, 0, 1, 1);\n\tptype->looks.stretch = 0.05;\n\n\tptype->randsmax = 1;\n\tptype->s2 = 1;\n\tptype->t2 = 1;\n}\n\nvoid Cmd_if_f(void);\n\n//Uses FTE's multiline console stuff.\n//This is the function that loads the effect descriptions (via console).\nvoid P_ParticleEffect_f(void)\n{\n\tchar *var, *value;\n\tchar *buf;\n\tqboolean settype = false;\n\tqboolean setalphadelta = false;\n\tqboolean setbeamlen = false;\n\n\tpart_type_t *ptype;\n\tint pnum, assoc;\n\tchar *config = part_parsenamespace;\n\n\tif (Cmd_Argc()!=2)\n\t{\n\t\tif (!strcmp(Cmd_Argv(1), \"namespace\"))\n\t\t{\n\t\t\tQ_strncpyz(part_parsenamespace, Cmd_Argv(2), sizeof(part_parsenamespace));\n\t\t\tif (Cmd_Argc() >= 4)\n\t\t\t\tpart_parseweak = atoi(Cmd_Argv(3));\n\t\t\treturn;\n\t\t}\n\t\tCon_Printf(\"No name for particle effect\\n\");\n\t\treturn;\n\t}\n\n\tbuf = Cbuf_GetNext(Cmd_ExecLevel, false);\n\twhile (*buf && *buf <= ' ')\n\t\tbuf++;\t//no whitespace please.\n\tif (*buf != '{')\n\t{\n\t\tCbuf_InsertText(buf, Cmd_ExecLevel, true);\n\t\tCon_Printf(\"This is a multiline command and should be used within config files\\n\");\n\t\treturn;\n\t}\n\n\tvar = Cmd_Argv(1);\n\tif (!pe_script_enabled)\n\t\tptype = NULL;\n\telse if (*var == '+')\n\t\tptype = P_GetParticleType(config, var+1);\n\telse\n\t\tptype = P_GetParticleType(config, var);\n\n\t//'weak' configs do not replace 'strong' configs\n\t//we allow weak to replace weak as a solution to the +assoc chain thing (to add, we effectively need to 'replace').\n\tif (!pe_script_enabled || (part_parseweak && ptype->loaded==2))\n\t{\n\t\tint depth = 1;\n\t\twhile(1)\n\t\t{\n\t\t\tbuf = Cbuf_GetNext(Cmd_ExecLevel, false);\n\t\t\tif (!*buf)\n\t\t\t\treturn;\n\n\t\t\twhile (*buf && *buf <= ' ')\n\t\t\t\tbuf++;\t//no whitespace please.\n\t\t\tif (*buf == '{')\n\t\t\t\tdepth++;\n\t\t\telse if (*buf == '}')\n\t\t\t{\n\t\t\t\tif (--depth == 0)\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\treturn;\n\t}\n\n\tif (*var == '+')\n\t{\n\t\tif (ptype->loaded)\n\t\t{\n\t\t\tint i, parenttype;\n\t\t\tchar newname[256];\n\t\t\tfor (i = 0; i < 64; i++)\n\t\t\t{\n\t\t\t\tparenttype = ptype - part_type;\n\t\t\t\tsnprintf(newname, sizeof(newname), \"+%i%s\", i, ptype->name);\n\t\t\t\tptype = P_GetParticleType(config, newname);\n\t\t\t\tif (!ptype->loaded)\n\t\t\t\t{\n\t\t\t\t\tif (part_type[parenttype].assoc != P_INVALID)\n\t\t\t\t\t\tCon_Printf(\"warning: assoc on particle chain \\\"%s.%s\\\" overridden\\n\", part_type[parenttype].config, part_type[parenttype].name);\n\t\t\t\t\tpart_type[parenttype].assoc = ptype - part_type;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (i == 64)\n\t\t\t{\n\t\t\t\tCon_Printf(\"Too many duplicate names, gave up\\n\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n\telse\n\t{\n\t\tif (ptype->loaded)\n\t\t{\n\t\t\tassoc = ptype->assoc;\n\t\t\twhile (assoc != P_INVALID && assoc < FALLBACKBIAS)\n\t\t\t{\n\t\t\t\tif (*part_type[assoc].name == '+')\n\t\t\t\t{\n\t\t\t\t\tpart_type[assoc].loaded = false;\n\t\t\t\t\tassoc = part_type[assoc].assoc;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\tif (!ptype)\n\t{\n\t\tCon_Printf(\"Bad name\\n\");\n\t\treturn;\n\t}\n\n\tP_SetModified();\n\n\tpnum = ptype-part_type;\n\n\tP_ResetToDefaults(ptype);\n\n\twhile(1)\n\t{\n\t\tbuf = Cbuf_GetNext(Cmd_ExecLevel, false);\n\t\tif (!*buf)\n\t\t{\n\t\t\tCon_Printf(\"Unexpected end of buffer with effect %s\\n\", ptype->name);\n\t\t\treturn;\n\t\t}\n\n\t\twhile (*buf && *buf <= ' ')\n\t\t\tbuf++;\t//no whitespace please.\n\t\tif (*buf == '}')\n\t\t\tbreak;\n\n\t\tCmd_TokenizeString(buf, true, true);\n\t\tvar = Cmd_Argv(0);\n\t\tvalue = Cmd_Argv(1);\n\n\t\t// TODO: switch this mess to some sort of binary tree to increase parse speed\n\t\tif (!strcmp(var, \"if\"))\n\t\t{\n\t\t\t//cheesy way to handle if statements inside particle configs.\n\t\t\tCmd_if_f();\n\t\t}\n\t\telse if (!strcmp(var, \"shader\"))\n\t\t{\n\t\t\tif (*value)\n\t\t\t\tQ_strncpyz(ptype->texname, value, sizeof(ptype->texname));\n\t\t\telse\n\t\t\t\tQ_strncpyz(ptype->texname, ptype->name, sizeof(ptype->texname));\n\t\t\tbuf = Cbuf_GetNext(Cmd_ExecLevel, true);\n\t\t\twhile (*buf && *buf <= ' ')\n\t\t\t\tbuf++;\t//no leading whitespace please.\n\t\t\tif (*buf == '{')\n\t\t\t{\n\t\t\t\tint nest = 1;\n\t\t\t\tchar *str = BZ_Malloc(3);\n\t\t\t\tint slen = 2;\n\t\t\t\tstr[0] = '{';\n\t\t\t\tstr[1] = '\\n';\n\t\t\t\tstr[2] = 0;\n\t\t\t\twhile(nest)\n\t\t\t\t{\n\t\t\t\t\tbuf = Cbuf_GetNext(Cmd_ExecLevel, true);\n\t\t\t\t\tif (!*buf)\n\t\t\t\t\t{\n\t\t\t\t\t\tCon_Printf(\"Unexpected end of buffer with effect %s\\n\", ptype->name);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\twhile (*buf && *buf <= ' ')\n\t\t\t\t\t\tbuf++;\t//no leading whitespace please.\n\t\t\t\t\tif (*buf == '}')\n\t\t\t\t\t\t--nest;\n\t\t\t\t\tif (*buf == '{')\n\t\t\t\t\t\tnest++;\n\t\t\t\t\tstr = BZ_Realloc(str, slen + strlen(buf) + 2);\n\t\t\t\t\tstrcpy(str + slen, buf);\n\t\t\t\t\tslen += strlen(str + slen);\n\t\t\t\t\tstr[slen++] = '\\n';\n\t\t\t\t}\n\t\t\t\tstr[slen] = 0;\n\t\t\t\tR_RegisterShader(ptype->texname, SUF_NONE, str);\n\t\t\t\tBZ_Free(str);\n\t\t\t}\n\t\t\telse\n\t\t\t\tCbuf_InsertText(buf, Cmd_ExecLevel, true);\n\t\t}\n\t\telse if (!strcmp(var, \"texture\") || !strcmp(var, \"linear_texture\") || !strcmp(var, \"nearest_texture\") || !strcmp(var, \"nearesttexture\"))\n\t\t{\n\t\t\tQ_strncpyz(ptype->texname, value, sizeof(ptype->texname));\n\t\t\tptype->looks.nearest = !strncmp(var, \"nearest\", 7);\n\t\t}\n\t\telse if (!strcmp(var, \"tcoords\"))\n\t\t{\n\t\t\tfloat tscale;\n\n\t\t\ttscale = atof(Cmd_Argv(5));\n\t\t\tif (tscale <= 0)\n\t\t\t\ttscale = 1;\n\n\t\t\tptype->s1 = atof(value)/tscale;\n\t\t\tptype->t1 = atof(Cmd_Argv(2))/tscale;\n\t\t\tptype->s2 = atof(Cmd_Argv(3))/tscale;\n\t\t\tptype->t2 = atof(Cmd_Argv(4))/tscale;\n\n\t\t\tptype->randsmax = atoi(Cmd_Argv(6));\n\t\t\tif (Cmd_Argc()>7)\n\t\t\t\tptype->texsstride = atof(Cmd_Argv(7));/*FIXME: divide-by-tscale missing */\n\t\t\telse\n\t\t\t\tptype->texsstride = 1/tscale;\n\n\t\t\tif (ptype->randsmax < 1 || ptype->texsstride == 0)\n\t\t\t\tptype->randsmax = 1;\n\t\t}\n\t\telse if (!strcmp(var, \"atlas\"))\n\t\t{\t//atlas countineachaxis first [last]\n\t\t\tint dims;\n\t\t\tint i;\n\t\t\tint m;\n\n\t\t\tdims = atof(Cmd_Argv(1));\n\t\t\ti = atoi(Cmd_Argv(2));\n\t\t\tm = atoi(Cmd_Argv(3));\n\t\t\tif (dims < 1)\n\t\t\t\tdims = 1;\n\n\t\t\tif (m > (m/dims)*dims+dims-1)\n\t\t\t{\n\t\t\t\tm = (m/dims)*dims+dims-1;\n\t\t\t\tCon_Printf(\"effect %s wraps across an atlased line\\n\", ptype->name);\n\t\t\t}\n\t\t\tif (m < i)\n\t\t\t\tm = i;\n\n\t\t\tptype->s1 = 1.0/dims * (i%dims);\n\t\t\tptype->s2 = 1.0/dims * (1+(i%dims));\n\t\t\tptype->t1 = 1.0/dims * (i/dims);\n\t\t\tptype->t2 = 1.0/dims * (1+(i/dims));\n\n\t\t\tptype->randsmax = m-i;\n\t\t\tptype->texsstride = ptype->s2-ptype->s1;\n\n\t\t\t//its modulo\n\t\t\tptype->randsmax++;\n\t\t}\n\t\telse if (!strcmp(var, \"rotation\"))\n\t\t{\n\t\t\tptype->rotationstartmin = atof(value)*M_PI/180;\n\t\t\tif (Cmd_Argc()>2)\n\t\t\t\tptype->rotationstartrand = atof(Cmd_Argv(2))*M_PI/180-ptype->rotationstartmin;\n\t\t\telse\n\t\t\t\tptype->rotationstartrand = 0;\n\n\t\t\tptype->rotationmin = atof(Cmd_Argv(3))*M_PI/180;\n\t\t\tif (Cmd_Argc()>4)\n\t\t\t\tptype->rotationrand = atof(Cmd_Argv(4))*M_PI/180-ptype->rotationmin;\n\t\t\telse\n\t\t\t\tptype->rotationrand = 0;\n\t\t}\n\t\telse if (!strcmp(var, \"rotationstart\"))\n\t\t{\n\t\t\tptype->rotationstartmin = atof(value)*M_PI/180;\n\t\t\tif (Cmd_Argc()>2)\n\t\t\t\tptype->rotationstartrand = atof(Cmd_Argv(2))*M_PI/180-ptype->rotationstartmin;\n\t\t\telse\n\t\t\t\tptype->rotationstartrand = 0;\n\t\t}\n\t\telse if (!strcmp(var, \"rotationspeed\"))\n\t\t{\n\t\t\tptype->rotationmin = atof(value)*M_PI/180;\n\t\t\tif (Cmd_Argc()>2)\n\t\t\t\tptype->rotationrand = atof(Cmd_Argv(2))*M_PI/180-ptype->rotationmin;\n\t\t\telse\n\t\t\t\tptype->rotationrand = 0;\n\t\t}\n\t\telse if (!strcmp(var, \"beamtexstep\"))\n\t\t{\n\t\t\tptype->rotationstartmin = 1/atof(value);\n\t\t\tptype->rotationstartrand = 0;\n\t\t\tsetbeamlen = true;\n\t\t}\n\t\telse if (!strcmp(var, \"beamtexspeed\"))\n\t\t{\n\t\t\tptype->rotationmin = atof(value);\n\t\t}\n\t\telse if (!strcmp(var, \"scale\"))\n\t\t{\n\t\t\tptype->scale = atof(value);\n\t\t\tif (Cmd_Argc()>2)\n\t\t\t\tptype->scalerand = atof(Cmd_Argv(2)) - ptype->scale;\n\t\t}\n\t\telse if (!strcmp(var, \"scalerand\"))\n\t\t\tptype->scalerand = atof(value);\n\n\t\telse if (!strcmp(var, \"scalefactor\"))\n\t\t\tptype->looks.scalefactor = atof(value);\n\t\telse if (!strcmp(var, \"scaledelta\"))\n\t\t\tptype->scaledelta = atof(value);\n\t\telse if (!strcmp(var, \"stretchfactor\"))\t//affects sparks\n\t\t{\n\t\t\tptype->looks.stretch = atof(value);\n\t\t\tptype->looks.minstretch = (Cmd_Argc()>2)?atof(Cmd_Argv(2)):0;\n\t\t}\n\n\t\telse if (!strcmp(var, \"step\"))\n\t\t{\n\t\t\tptype->countspacing = atof(value);\n\t\t\tptype->count = 1/atof(value);\n\t\t\tif (Cmd_Argc()>2)\n\t\t\t\tptype->countrand = 1/atof(Cmd_Argv(2));\n\t\t\tif (Cmd_Argc()>3)\n\t\t\t\tptype->countextra = atof(Cmd_Argv(3));\n\t\t}\n\t\telse if (!strcmp(var, \"count\"))\n\t\t{\n\t\t\tptype->countspacing = 0;\n\t\t\tptype->count = atof(value);\n\t\t\tif (Cmd_Argc()>2)\n\t\t\t\tptype->countrand = atof(Cmd_Argv(2));\n\t\t\tif (Cmd_Argc()>3)\n\t\t\t\tptype->countextra = atof(Cmd_Argv(3));\n\t\t}\n\t\telse if (!strcmp(var, \"rainfrequency\"))\n\t\t{\t//multiplier to ramp up the effect or whatever (without affecting spawn patterns).\n\t\t\tptype->rainfrequency = atof(value);\n\t\t}\n\n\t\telse if (!strcmp(var, \"alpha\"))\n\t\t{\n\t\t\tptype->alpha = atof(value);\n\t\t\tif (Cmd_Argc()>2)\n\t\t\t\tptype->alpharand = atof(Cmd_Argv(2)) - ptype->alpha;\n\t\t\tif (Cmd_Argc()>3)\n\t\t\t{\n\t\t\t\tptype->alphachange = atof(Cmd_Argv(3));\n\t\t\t\tsetalphadelta = true;\n\t\t\t}\n\t\t}\n\t\telse if (!strcmp(var, \"alpharand\"))\n\t\t\tptype->alpharand = atof(value);\n#ifdef HAVE_LEGACY\n\t\telse if (!strcmp(var, \"alphachange\"))\n\t\t{\n\t\t\tCon_DPrintf(\"%s.%s: alphachange is deprecated, use alphadelta\\n\", ptype->config, ptype->name);\n\t\t\tptype->alphachange = atof(value);\n\t\t}\n#endif\n\t\telse if (!strcmp(var, \"alphadelta\"))\n\t\t{\n\t\t\tptype->alphachange = atof(value);\n\t\t\tsetalphadelta = true;\n\t\t}\n\t\telse if (!strcmp(var, \"die\"))\n\t\t{\n\t\t\tptype->die = atof(value);\n\t\t\tif (Cmd_Argc()>2)\n\t\t\t{\n\t\t\t\tfloat mn=ptype->die,mx=atof(Cmd_Argv(2));\n\t\t\t\tif (mn > mx)\n\t\t\t\t{\n\t\t\t\t\tmn = mx;\n\t\t\t\t\tmx = ptype->die;\n\t\t\t\t}\n\t\t\t\tptype->die = mx;\n\t\t\t\tptype->randdie = mx-mn;\n\t\t\t}\n\t\t}\n#ifdef HAVE_LEGACY\n\t\telse if (!strcmp(var, \"diesubrand\"))\n\t\t{\n\t\t\tCon_DPrintf(\"%s.%s: diesubrand is deprecated, use die with two arguments\\n\", ptype->config, ptype->name);\n\t\t\tptype->randdie = atof(value);\n\t\t}\n#endif\n\n\t\telse if (!strcmp(var, \"randomvel\"))\n\t\t{\t//shortcut for velwrand (and velbias for z bias)\n\t\t\tptype->velbias[0] = ptype->velbias[1] = 0;\n\t\t\tptype->velwrand[0] = ptype->velwrand[1] = atof(value);\n\t\t\tif (Cmd_Argc()>3)\n\t\t\t{\n\t\t\t\tptype->velbias[2] = atof(Cmd_Argv(2));\n\t\t\t\tptype->velwrand[2] = atof(Cmd_Argv(3));\n\t\t\t\tptype->velwrand[2] -= ptype->velbias[2]; /*make vert be the total range*/\n\t\t\t\tptype->velwrand[2] /= 2; /*vert is actually +/- 1, not 0 to 1, so rescale it*/\n\t\t\t\tptype->velbias[2] += ptype->velwrand[2]; /*and bias must be centered to the range*/\n\t\t\t}\n\t\t\telse if (Cmd_Argc()>2)\n\t\t\t{\n\t\t\t\tptype->velwrand[2] = atof(Cmd_Argv(2));\n\t\t\t\tptype->velbias[2] = 0;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tptype->velwrand[2] = ptype->velwrand[0];\n\t\t\t\tptype->velbias[2] = 0;\n\t\t\t}\n\t\t}\n\t\telse if (!strcmp(var, \"veladd\"))\n\t\t{\n\t\t\tptype->veladd = atof(value);\n\t\t\tptype->randomveladd = 0;\n\t\t\tif (Cmd_Argc()>2)\n\t\t\t\tptype->randomveladd = atof(Cmd_Argv(2)) - ptype->veladd;\n\t\t}\n\t\telse if (!strcmp(var, \"orgadd\"))\n\t\t{\n\t\t\tptype->orgadd = atof(value);\n\t\t\tptype->randomorgadd = 0;\n\t\t\tif (Cmd_Argc()>2)\n\t\t\t\tptype->randomorgadd = atof(Cmd_Argv(2)) - ptype->orgadd;\n\t\t}\n\n\t\telse if (!strcmp(var, \"orgbias\"))\n\t\t{\n\t\t\tptype->orgbias[0] = atof(value);\n\t\t\tptype->orgbias[1] = atof(Cmd_Argv(2));\n\t\t\tptype->orgbias[2] = atof(Cmd_Argv(3));\n\t\t}\n\t\telse if (!strcmp(var, \"velbias\"))\n\t\t{\n\t\t\tptype->velbias[0] = atof(value);\n\t\t\tptype->velbias[1] = atof(Cmd_Argv(2));\n\t\t\tptype->velbias[2] = atof(Cmd_Argv(3));\n\t\t}\n\t\telse if (!strcmp(var, \"orgwrand\"))\n\t\t{\n\t\t\tptype->orgwrand[0] = atof(value);\n\t\t\tptype->orgwrand[1] = atof(Cmd_Argv(2));\n\t\t\tptype->orgwrand[2] = atof(Cmd_Argv(3));\n\t\t}\n\t\telse if (!strcmp(var, \"velwrand\"))\n\t\t{\n\t\t\tptype->velwrand[0] = atof(value);\n\t\t\tptype->velwrand[1] = atof(Cmd_Argv(2));\n\t\t\tptype->velwrand[2] = atof(Cmd_Argv(3));\n\t\t}\n\n\t\telse if (!strcmp(var, \"friction\"))\n\t\t{\n\t\t\tptype->friction[2] = ptype->friction[1] = ptype->friction[0] = atof(value);\n\n\t\t\tif (Cmd_Argc()>3)\n\t\t\t{\n\t\t\t\tptype->friction[2] = atof(Cmd_Argv(3));\n\t\t\t\tptype->friction[1] = atof(Cmd_Argv(2));\n\t\t\t}\n\t\t\telse if (Cmd_Argc()>2)\n\t\t\t{\n\t\t\t\tptype->friction[2] = atof(Cmd_Argv(2));\n\t\t\t}\n\t\t}\n\t\telse if (!strcmp(var, \"gravity\"))\n\t\t\tptype->gravity = atof(value);\n\t\telse if (!strcmp(var, \"flurry\"))\n\t\t\tptype->flurry = atof(value);\n\n\t\telse if (!strcmp(var, \"assoc\"))\n\t\t{\n\t\t\tassoc = CheckAssosiation(config, value, pnum);\t//careful - this can realloc all the particle types\n\t\t\tptype = &part_type[pnum];\n\t\t\tptype->assoc = assoc;\n\t\t}\n\t\telse if (!strcmp(var, \"inwater\"))\n\t\t{\n\t\t\t// the underwater effect switch should only occur for\n\t\t\t// 1 level so the standard assoc check works\n\t\t\tassoc = CheckAssosiation(config, value, pnum);\n\t\t\tptype = &part_type[pnum];\n\t\t\tptype->inwater = assoc;\n\t\t}\n\t\telse if (!strcmp(var, \"underwater\"))\n\t\t{\n\t\t\tptype->flags |= PT_TRUNDERWATER;\n\nparsefluid:\n\t\t\tif ((ptype->flags & (PT_TRUNDERWATER|PT_TROVERWATER)) == (PT_TRUNDERWATER|PT_TROVERWATER))\n\t\t\t{\n\t\t\t\tptype->flags &= ~PT_TRUNDERWATER;\n\t\t\t\tCon_Printf(\"%s.%s: both over and under water\\n\", ptype->config, ptype->name);\n\t\t\t}\n\t\t\tif (Cmd_Argc() == 1)\n\t\t\t\tptype->fluidmask = FTECONTENTS_FLUID;\n\t\t\telse\n\t\t\t{\n\t\t\t\tint i = Cmd_Argc();\n\t\t\t\tptype->fluidmask = 0;\n\t\t\t\twhile (i --> 1)\n\t\t\t\t{\n\t\t\t\t\tchar *value = Cmd_Argv(i);\n\t\t\t\t\tif (!strcmp(value, \"water\"))\n\t\t\t\t\t\tptype->fluidmask |= FTECONTENTS_WATER;\n\t\t\t\t\telse if (!strcmp(value, \"slime\"))\n\t\t\t\t\t\tptype->fluidmask |= FTECONTENTS_SLIME;\n\t\t\t\t\telse if (!strcmp(value, \"lava\"))\n\t\t\t\t\t\tptype->fluidmask |= FTECONTENTS_LAVA;\n\t\t\t\t\telse if (!strcmp(value, \"sky\"))\n\t\t\t\t\t\tptype->fluidmask |= FTECONTENTS_SKY;\n\t\t\t\t\telse if (!strcmp(value, \"fluid\"))\n\t\t\t\t\t\tptype->fluidmask |= FTECONTENTS_FLUID;\n\t\t\t\t\telse if (!strcmp(value, \"solid\"))\n\t\t\t\t\t\tptype->fluidmask |= FTECONTENTS_SOLID;\n\t\t\t\t\telse if (!strcmp(value, \"playerclip\"))\n\t\t\t\t\t\tptype->fluidmask |= FTECONTENTS_PLAYERCLIP;\n\t\t\t\t\telse if (!strcmp(value, \"none\"))\n\t\t\t\t\t\tptype->fluidmask |= 0;\n\t\t\t\t\telse\n\t\t\t\t\t\tCon_Printf(\"%s.%s: unknown contents: %s\\n\", ptype->config, ptype->name, value);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse if (!strcmp(var, \"notunderwater\"))\n\t\t{\n\t\t\tptype->flags |= PT_TROVERWATER;\n\t\t\tgoto parsefluid;\n\t\t}\n\t\telse if (!strcmp(var, \"model\"))\n\t\t{\n\t\t\tpartmodels_t *mod;\n\t\t\tchar *e;\n\n\t\t\tptype->models = BZ_Realloc(ptype->models, sizeof(partmodels_t)*(ptype->nummodels+1));\n\t\t\tQ_strncpyz(ptype->models[ptype->nummodels].name, Cmd_Argv(1), sizeof(ptype->models[ptype->nummodels].name));\n\t\t\tmod = &ptype->models[ptype->nummodels++];\n\n\t\t\tmod->framestart = 0;\n\t\t\tmod->framecount = 1;\n\t\t\tmod->framerate = 10;\n\t\t\tmod->alpha = 1;\n\t\t\tmod->skin = 0;\n\t\t\tmod->traileffect = P_INVALID;\n\t\t\tmod->rflags = RF_NOSHADOW;\n\t\t\tmod->scalemin = mod->scalemax = 1;\n\t\t\tmod->rgb[0] = 1.0f;\n\t\t\tmod->rgb[1] = 1.0f;\n\t\t\tmod->rgb[2] = 1.0f;\n\n\t\t\tstrtoul(Cmd_Argv(2), &e, 0);\n\t\t\twhile(*e == ' ' || *e == '\\t')\n\t\t\t\te++;\n\t\t\tif (*e)\n\t\t\t{\n\t\t\t\tint p;\n\t\t\t\tfor(p = 2; p < Cmd_Argc(); p++)\n\t\t\t\t{\n\t\t\t\t\te = Cmd_Argv(p);\n\n\t\t\t\t\tif (!Q_strncasecmp(e, \"frame=\", 6))\n\t\t\t\t\t{\n\t\t\t\t\t\tmod->framestart = atof(e+6);\n\t\t\t\t\t\tmod->framecount = 1;\n\t\t\t\t\t}\n\t\t\t\t\telse if (!Q_strncasecmp(e, \"framestart=\", 11))\n\t\t\t\t\t\tmod->framestart = atof(e+11);\n\t\t\t\t\telse if (!Q_strncasecmp(e, \"framecount=\", 11))\n\t\t\t\t\t\tmod->framecount = atof(e+11);\n\t\t\t\t\telse if (!Q_strncasecmp(e, \"frameend=\", 9))\t//misnomer.\n\t\t\t\t\t\tmod->framecount = atof(e+9);\n\t\t\t\t\telse if (!Q_strncasecmp(e, \"frames=\", 7))\n\t\t\t\t\t\tmod->framecount = atof(e+7);\n\t\t\t\t\telse if (!Q_strncasecmp(e, \"framerate=\", 10))\n\t\t\t\t\t\tmod->framerate = atof(e+10);\n\t\t\t\t\telse if (!Q_strncasecmp(e, \"skin=\", 5))\n\t\t\t\t\t\tmod->skin = atoi(e+5);\n\t\t\t\t\telse if (!Q_strncasecmp(e, \"alpha=\", 6))\n\t\t\t\t\t\tmod->alpha = atof(e+6);\n\t\t\t\t\telse if (!Q_strncasecmp(e, \"scalemin=\", 9))\n\t\t\t\t\t\tmod->scalemin = atof(e+9);\n\t\t\t\t\telse if (!Q_strncasecmp(e, \"scalemax=\", 9))\n\t\t\t\t\t\tmod->scalemax = atof(e+9);\n\t\t\t\t\telse if (!Q_strncasecmp(e, \"r=\", 2))\n\t\t\t\t\t\tmod->rgb[0] = atof(e+2);\n\t\t\t\t\telse if (!Q_strncasecmp(e, \"g=\", 2))\n\t\t\t\t\t\tmod->rgb[1] = atof(e+2);\n\t\t\t\t\telse if (!Q_strncasecmp(e, \"b=\", 2))\n\t\t\t\t\t\tmod->rgb[2] = atof(e+2);\n\t\t\t\t\telse if (!Q_strncasecmp(e, \"trail=\", 6))\n\t\t\t\t\t{\n\t\t\t\t\t\tmod->traileffect = P_AllocateParticleType(config, e+6);//careful - this can realloc all the particle types\n\t\t\t\t\t\tptype = &part_type[pnum];\n\t\t\t\t\t}\n\t\t\t\t\telse if (!Q_strncasecmp(e, \"orient\", 6))\n\t\t\t\t\t\tmod->rflags |= RF_USEORIENTATION;\t//use the dir to orient the model, instead of always facing up.\n\t\t\t\t\telse if (!Q_strncasecmp(e, \"additive\", 8))\n\t\t\t\t\t\tmod->rflags |= RF_ADDITIVE;\t\t\t//additive blend\n\t\t\t\t\telse if (!Q_strncasecmp(e, \"transparent\", 11))\n\t\t\t\t\t\tmod->rflags |= RF_TRANSLUCENT;\t\t//force blend\n\t\t\t\t\telse if (!Q_strncasecmp(e, \"fullbright\", 10))\n\t\t\t\t\t\tmod->rflags |= RF_FULLBRIGHT;\t\t//fullbright, woo\n\t\t\t\t\telse if (!Q_strncasecmp(e, \"shadow\", 6))\n\t\t\t\t\t\tmod->rflags &= ~RF_NOSHADOW;\t\t//clear noshadow\n\t\t\t\t\telse if (!Q_strncasecmp(e, \"noshadow\", 8))\n\t\t\t\t\t\tmod->rflags |= RF_NOSHADOW;\t\t\t//set noshadow (cos... you know...)\n\t\t\t\t\telse\n\t\t\t\t\t\tCon_Printf(\"Bad named argument: %s\\n\", e);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tmod->framestart = atof(Cmd_Argv(2));\n\t\t\t\tmod->framecount = atof(Cmd_Argv(3));\n\t\t\t\tmod->framerate = atof(Cmd_Argv(4));\n\t\t\t\tmod->alpha = atof(Cmd_Argv(5));\n\t\t\t\tif (*Cmd_Argv(6))\n\t\t\t\t{\n\t\t\t\t\tmod->traileffect = P_AllocateParticleType(config, Cmd_Argv(6));//careful - this can realloc all the particle types\n\t\t\t\t\tptype = &part_type[pnum];\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tmod->traileffect = P_INVALID;\n\t\t\t}\n\t\t}\n\t\telse if (!strcmp(var, \"sound\"))\n\t\t{\n\t\t\tchar *e;\n\t\t\tptype->sounds = BZ_Realloc(ptype->sounds, sizeof(partsounds_t)*(ptype->numsounds+1));\n\t\t\tQ_strncpyz(ptype->sounds[ptype->numsounds].name, Cmd_Argv(1), sizeof(ptype->sounds[ptype->numsounds].name));\n\t\t\tif (*ptype->sounds[ptype->numsounds].name)\n\t\t\t\tS_PrecacheSound(ptype->sounds[ptype->numsounds].name);\n\n\t\t\tptype->sounds[ptype->numsounds].vol = 1;\n\t\t\tptype->sounds[ptype->numsounds].atten = 1;\n\t\t\tptype->sounds[ptype->numsounds].pitch = 1;\n\t\t\tptype->sounds[ptype->numsounds].delay = 0;\n\t\t\tptype->sounds[ptype->numsounds].weight = 0;\n\n\t\t\tstrtoul(Cmd_Argv(2), &e, 0);\n\t\t\twhile(*e == ' ' || *e == '\\t')\n\t\t\t\te++;\n\t\t\tif (*e)\n\t\t\t{\n\t\t\t\tint p;\n\t\t\t\tfor(p = 2; p < Cmd_Argc(); p++)\n\t\t\t\t{\n\t\t\t\t\te = Cmd_Argv(p);\n\n\t\t\t\t\tif (!Q_strncasecmp(e, \"vol=\", 4) || !Q_strncasecmp(e, \"volume=\", 7))\n\t\t\t\t\t\tptype->sounds[ptype->numsounds].vol = atof(strchr(e, '=')+1);\n\t\t\t\t\telse if (!Q_strncasecmp(e, \"attn=\", 5) || !Q_strncasecmp(e, \"atten=\", 6) || !Q_strncasecmp(e, \"attenuation=\", 12))\n\t\t\t\t\t{\n\t\t\t\t\t\te = strchr(e, '=')+1;\n\t\t\t\t\t\tif (!strcmp(e, \"none\"))\n\t\t\t\t\t\t\tptype->sounds[ptype->numsounds].atten = 0;\n\t\t\t\t\t\telse if (!strcmp(e, \"normal\"))\n\t\t\t\t\t\t\tptype->sounds[ptype->numsounds].atten = 1;\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tptype->sounds[ptype->numsounds].atten = atof(e);\n\t\t\t\t\t}\n\t\t\t\t\telse if (!Q_strncasecmp(e, \"pitch=\", 6))\n\t\t\t\t\t\tptype->sounds[ptype->numsounds].pitch = atof(strchr(e, '=')+1)*0.01;\n\t\t\t\t\telse if (!Q_strncasecmp(e, \"delay=\", 6))\n\t\t\t\t\t\tptype->sounds[ptype->numsounds].delay = atof(strchr(e, '=')+1);\n\t\t\t\t\telse if (!Q_strncasecmp(e, \"weight=\", 7))\n\t\t\t\t\t\tptype->sounds[ptype->numsounds].weight = atof(strchr(e, '=')+1);\n\t\t\t\t\telse\n\t\t\t\t\t\tCon_Printf(\"Bad named argument: %s\\n\", e);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tptype->sounds[ptype->numsounds].vol = atof(Cmd_Argv(2));\n\t\t\t\tif (!ptype->sounds[ptype->numsounds].vol)\n\t\t\t\t\tptype->sounds[ptype->numsounds].vol = 1;\n\t\t\t\tptype->sounds[ptype->numsounds].atten = atof(Cmd_Argv(3));\n\t\t\t\tif (!ptype->sounds[ptype->numsounds].atten)\n\t\t\t\t\tptype->sounds[ptype->numsounds].atten = 1;\n\t\t\t\tptype->sounds[ptype->numsounds].pitch = atof(Cmd_Argv(4))*0.01;\n\t\t\t\tif (!ptype->sounds[ptype->numsounds].pitch)\n\t\t\t\t\tptype->sounds[ptype->numsounds].pitch = 1;\n\t\t\t\tptype->sounds[ptype->numsounds].delay = atof(Cmd_Argv(5));\n\t\t\t\tif (!ptype->sounds[ptype->numsounds].delay)\n\t\t\t\t\tptype->sounds[ptype->numsounds].delay = 0;\n\t\t\t\tptype->sounds[ptype->numsounds].weight = atof(Cmd_Argv(6));\n\t\t\t}\n\t\t\tif (!ptype->sounds[ptype->numsounds].weight)\n\t\t\t\tptype->sounds[ptype->numsounds].weight = 1;\n\t\t\tptype->numsounds++;\n\t\t}\n\t\telse if (!strcmp(var, \"colorindex\"))\n\t\t{\n\t\t\tif (Cmd_Argc()>2)\n\t\t\t\tptype->colorrand = strtoul(Cmd_Argv(2), NULL, 0);\n\t\t\tptype->colorindex = strtoul(value, NULL, 0);\n\t\t}\n\t\telse if (!strcmp(var, \"colorrand\"))\n\t\t\tptype->colorrand = atoi(value); // now obsolete\n\t\telse if (!strcmp(var, \"citracer\"))\n\t\t\tptype->flags |= PT_CITRACER;\n\n\t\telse if (!strcmp(var, \"red\"))\n\t\t\tptype->rgb[0] = atof(value)/255;\n\t\telse if (!strcmp(var, \"green\"))\n\t\t\tptype->rgb[1] = atof(value)/255;\n\t\telse if (!strcmp(var, \"blue\"))\n\t\t\tptype->rgb[2] = atof(value)/255;\n\t\telse if (!strcmp(var, \"rgb\"))\n\t\t{\t//byte version\n\t\t\tptype->rgb[0] = ptype->rgb[1] = ptype->rgb[2] = atof(value)/255;\n\t\t\tif (Cmd_Argc()>3)\n\t\t\t{\n\t\t\t\tptype->rgb[1] = atof(Cmd_Argv(2))/255;\n\t\t\t\tptype->rgb[2] = atof(Cmd_Argv(3))/255;\n\t\t\t}\n\t\t}\n\t\telse if (!strcmp(var, \"rgbf\"))\n\t\t{\t//float version\n\t\t\tptype->rgb[0] = ptype->rgb[1] = ptype->rgb[2] = atof(value);\n\t\t\tif (Cmd_Argc()>3)\n\t\t\t{\n\t\t\t\tptype->rgb[1] = atof(Cmd_Argv(2));\n\t\t\t\tptype->rgb[2] = atof(Cmd_Argv(3));\n\t\t\t}\n\t\t}\n\n\t\telse if (!strcmp(var, \"reddelta\"))\n\t\t{\n\t\t\tptype->rgbchange[0] = atof(value)/255;\n\t\t\tif (!ptype->rgbchangetime)\n\t\t\t\tptype->rgbchangetime = ptype->die;\n\t\t}\n\t\telse if (!strcmp(var, \"greendelta\"))\n\t\t{\n\t\t\tptype->rgbchange[1] = atof(value)/255;\n\t\t\tif (!ptype->rgbchangetime)\n\t\t\t\tptype->rgbchangetime = ptype->die;\n\t\t}\n\t\telse if (!strcmp(var, \"bluedelta\"))\n\t\t{\n\t\t\tptype->rgbchange[2] = atof(value)/255;\n\t\t\tif (!ptype->rgbchangetime)\n\t\t\t\tptype->rgbchangetime = ptype->die;\n\t\t}\n\t\telse if (!strcmp(var, \"rgbdelta\"))\n\t\t{\t//byte version\n\t\t\tptype->rgbchange[0] = ptype->rgbchange[1] = ptype->rgbchange[2] = atof(value)/255;\n\t\t\tif (Cmd_Argc()>3)\n\t\t\t{\n\t\t\t\tptype->rgbchange[1] = atof(Cmd_Argv(2))/255;\n\t\t\t\tptype->rgbchange[2] = atof(Cmd_Argv(3))/255;\n\t\t\t}\n\t\t\tif (!ptype->rgbchangetime)\n\t\t\t\tptype->rgbchangetime = ptype->die;\n\t\t}\n\t\telse if (!strcmp(var, \"rgbdeltaf\"))\n\t\t{\t//float version\n\t\t\tptype->rgbchange[0] = ptype->rgbchange[1] = ptype->rgbchange[2] = atof(value);\n\t\t\tif (Cmd_Argc()>3)\n\t\t\t{\n\t\t\t\tptype->rgbchange[1] = atof(Cmd_Argv(2));\n\t\t\t\tptype->rgbchange[2] = atof(Cmd_Argv(3));\n\t\t\t}\n\t\t\tif (!ptype->rgbchangetime)\n\t\t\t\tptype->rgbchangetime = ptype->die;\n\t\t}\n\t\telse if (!strcmp(var, \"rgbdeltatime\"))\n\t\t\tptype->rgbchangetime = atof(value);\n\n\t\telse if (!strcmp(var, \"redrand\"))\n\t\t\tptype->rgbrand[0] = atof(value)/255;\n\t\telse if (!strcmp(var, \"greenrand\"))\n\t\t\tptype->rgbrand[1] = atof(value)/255;\n\t\telse if (!strcmp(var, \"bluerand\"))\n\t\t\tptype->rgbrand[2] = atof(value)/255;\n\t\telse if (!strcmp(var, \"rgbrand\"))\n\t\t{\t//byte version\n\t\t\tptype->rgbrand[0] = ptype->rgbrand[1] = ptype->rgbrand[2] = atof(value)/255;\n\t\t\tif (Cmd_Argc()>3)\n\t\t\t{\n\t\t\t\tptype->rgbrand[1] = atof(Cmd_Argv(2))/255;\n\t\t\t\tptype->rgbrand[2] = atof(Cmd_Argv(3))/255;\n\t\t\t}\n\t\t}\n\t\telse if (!strcmp(var, \"rgbrandf\"))\n\t\t{\t//float version\n\t\t\tptype->rgbrand[0] = ptype->rgbrand[1] = ptype->rgbrand[2] = atof(value);\n\t\t\tif (Cmd_Argc()>3)\n\t\t\t{\n\t\t\t\tptype->rgbrand[1] = atof(Cmd_Argv(2));\n\t\t\t\tptype->rgbrand[2] = atof(Cmd_Argv(3));\n\t\t\t}\n\t\t}\n\n\t\telse if (!strcmp(var, \"rgbrandsync\"))\n\t\t{\n\t\t\tptype->rgbrandsync[0] = ptype->rgbrandsync[1] = ptype->rgbrandsync[2] = atof(value);\n\t\t\tif (Cmd_Argc()>3)\n\t\t\t{\n\t\t\t\tptype->rgbrandsync[1] = atof(Cmd_Argv(2));\n\t\t\t\tptype->rgbrandsync[2] = atof(Cmd_Argv(3));\n\t\t\t}\n\t\t}\n\t\telse if (!strcmp(var, \"redrandsync\"))\n\t\t\tptype->rgbrandsync[0] = atof(value);\n\t\telse if (!strcmp(var, \"greenrandsync\"))\n\t\t\tptype->rgbrandsync[1] = atof(value);\n\t\telse if (!strcmp(var, \"bluerandsync\"))\n\t\t\tptype->rgbrandsync[2] = atof(value);\n\n\t\telse if (!strcmp(var, \"stains\"))\n\t\t\tptype->stainonimpact = atof(value);\n\t\telse if (!strcmp(var, \"blend\"))\n\t\t{\n\t\t\tptype->looks.premul = false;\n\t\t\tif (!strcmp(value, \"adda\") || !strcmp(value, \"add\"))\n\t\t\t\tptype->looks.blendmode = BM_ADDA;\n\t\t\telse if (!strcmp(value, \"addc\"))\n\t\t\t\tptype->looks.blendmode = BM_ADDC;\n\t\t\telse if (!strcmp(value, \"subtract\"))\n\t\t\t\tptype->looks.blendmode = BM_SUBTRACT;\n\t\t\telse if (!strcmp(value, \"invmoda\") || !strcmp(value, \"invmod\"))\n\t\t\t\tptype->looks.blendmode = BM_INVMODA;\n\t\t\telse if (!strcmp(value, \"invmodc\"))\n\t\t\t\tptype->looks.blendmode = BM_INVMODC;\n\t\t\telse if (!strcmp(value, \"blendcolour\") || !strcmp(value, \"blendcolor\"))\n\t\t\t\tptype->looks.blendmode = BM_BLENDCOLOUR;\n\t\t\telse if (!strcmp(value, \"blendalpha\"))\n\t\t\t\tptype->looks.blendmode = BM_BLEND;\n\t\t\telse if (!strcmp(value, \"premul_subtract\"))\n\t\t\t{\n\t\t\t\tptype->looks.premul = 1;\n\t\t\t\tptype->looks.blendmode = BM_INVMODC;\n\t\t\t}\n\t\t\telse if (!strcmp(value, \"premul_add\"))\n\t\t\t{\n\t\t\t\tptype->looks.premul = 2;\n\t\t\t\tptype->looks.blendmode = BM_PREMUL;\n\t\t\t}\n\t\t\telse if (!strcmp(value, \"premul_blend\"))\n\t\t\t{\n\t\t\t\tptype->looks.premul = 1;\n\t\t\t\tptype->looks.blendmode = BM_PREMUL;\n\t\t\t}\n\t\t\telse if (!strcmp(value, \"rtsmoke\"))\n\t\t\t{\n\t\t\t\tptype->looks.premul = 1;\n\t\t\t\tptype->looks.blendmode = BM_RTSMOKE;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tCon_DPrintf(\"%s.%s: uses unknown blend type '%s', assuming legacy 'blendalpha'\\n\", ptype->config, ptype->name, value);\n\t\t\t\tptype->looks.blendmode = BM_BLEND;\t//fallback\n\t\t\t}\n\t\t}\n\t\telse if (!strcmp(var, \"placeholder\"))\n\t\t\tptype->spawnmode = SM_FIXMEWARNING;\n\t\telse if (!strcmp(var, \"spawnmode\"))\n\t\t{\n\t\t\tif (!strcmp(value, \"circle\"))\n\t\t\t\tptype->spawnmode = SM_CIRCLE;\n\t\t\telse if (!strcmp(value, \"ball\"))\n\t\t\t\tptype->spawnmode = SM_BALL;\n\t\t\telse if (!strcmp(value, \"spiral\"))\n\t\t\t\tptype->spawnmode = SM_SPIRAL;\n\t\t\telse if (!strcmp(value, \"tracer\"))\n\t\t\t\tptype->spawnmode = SM_TRACER;\n\t\t\telse if (!strcmp(value, \"telebox\"))\n\t\t\t\tptype->spawnmode = SM_TELEBOX;\n\t\t\telse if (!strcmp(value, \"lavasplash\"))\n\t\t\t\tptype->spawnmode = SM_LAVASPLASH;\n\t\t\telse if (!strcmp(value, \"uniformcircle\"))\n\t\t\t\tptype->spawnmode = SM_UNICIRCLE;\n\t\t\telse if (!strcmp(value, \"syncfield\"))\n\t\t\t{\n\t\t\t\tptype->spawnmode = SM_FIELD;\n#ifdef HAVE_LEGACY\n\t\t\t\tptype->spawnparam1 = 16;\n\t\t\t\tptype->spawnparam2 = 0;\n#endif\n\t\t\t}\n\t\t\telse if (!strcmp(value, \"distball\"))\n\t\t\t\tptype->spawnmode = SM_DISTBALL;\n\t\t\telse if (!strcmp(value, \"box\"))\n\t\t\t\tptype->spawnmode = SM_BOX;\n\t\t\telse\n\t\t\t{\n\t\t\t\tCon_DPrintf(\"%s.%s: uses unknown spawn type '%s', assuming 'box'\\n\", ptype->config, ptype->name, value);\n\t\t\t\tptype->spawnmode = SM_BOX;\t//fallback\n\t\t\t}\n\n\t\t\tif (Cmd_Argc()>2)\n\t\t\t{\n\t\t\t\tif (Cmd_Argc()>3)\n\t\t\t\t\tptype->spawnparam2 = atof(Cmd_Argv(3));\n\t\t\t\tptype->spawnparam1 = atof(Cmd_Argv(2));\n\t\t\t}\n\t\t}\n\t\telse if (!strcmp(var, \"type\"))\n\t\t{\n\t\t\tif (!strcmp(value, \"beam\"))\n\t\t\t\tptype->looks.type = PT_BEAM;\n\t\t\telse if (!strcmp(value, \"vbeam\"))\n\t\t\t\tptype->looks.type = PT_BEAM;\n\t\t\telse if (!strcmp(value, \"spark\") || !strcmp(value, \"linespark\"))\n\t\t\t\tptype->looks.type = PT_SPARK;\n\t\t\telse if (!strcmp(value, \"sparkfan\") || !strcmp(value, \"trianglefan\"))\n\t\t\t\tptype->looks.type = PT_SPARKFAN;\n\t\t\telse if (!strcmp(value, \"texturedspark\"))\n\t\t\t\tptype->looks.type = PT_TEXTUREDSPARK;\n\t\t\telse if (!strcmp(value, \"decal\") || !strcmp(value, \"cdecal\"))\n\t\t\t\tptype->looks.type = PT_CDECAL;\n\t\t\telse if (!strcmp(value, \"udecal\"))\n\t\t\t\tptype->looks.type = PT_UDECAL;\n\t\t\telse if (!strcmp(value, \"normal\"))\n\t\t\t\tptype->looks.type = PT_NORMAL;\n\t\t\telse\n\t\t\t{\n\t\t\t\tCon_DPrintf(\"%s.%s: uses unknown (render) type '%s', assuming 'normal'\\n\", ptype->config, ptype->name, value);\n\t\t\t\tptype->looks.type = PT_NORMAL;\t//fallback\n\t\t\t}\n\t\t\tsettype = true;\n\t\t}\n\t\telse if (!strcmp(var, \"clippeddecal\"))\t//mask, match\n\t\t{\n\t\t\tif (Cmd_Argc()>=2)\n\t\t\t{//decal only appears where: (surfflags&mask)==match\n\t\t\t\tptype->surfflagmatch = ptype->surfflagmask = strtoul(Cmd_Argv(1), NULL, 0);\n\t\t\t\tif (Cmd_Argc()>=3)\n\t\t\t\t\tptype->surfflagmatch = strtoul(Cmd_Argv(2), NULL, 0);\n\t\t\t}\n\t\t\tptype->looks.type = PT_CDECAL;\n\t\t\tsettype = true;\n\t\t}\n#ifdef HAVE_LEGACY\n\t\telse if (!strcmp(var, \"isbeam\"))\n\t\t{\n\t\t\tCon_DPrintf(\"%s.%s: isbeam is deprecated, use type beam\\n\", ptype->config, ptype->name);\n\t\t\tptype->looks.type = PT_BEAM;\n\t\t\tsettype = true;\n\t\t}\n#endif\n\t\telse if (!strcmp(var, \"spawntime\"))\n\t\t\tptype->spawntime = atof(value);\n\t\telse if (!strcmp(var, \"spawnchance\"))\n\t\t\tptype->spawnchance = atof(value);\n\t\telse if (!strcmp(var, \"cliptype\"))\n\t\t{\n\t\t\tassoc = P_AllocateParticleType(config, value);//careful - this can realloc all the particle types\n\t\t\tptype = &part_type[pnum];\n\t\t\tptype->cliptype = assoc;\n\t\t}\n\t\telse if (!strcmp(var, \"clipcount\"))\n\t\t\tptype->clipcount = atof(value);\n\t\telse if (!strcmp(var, \"clipbounce\"))\n\t\t{\n\t\t\tptype->clipbounce = atof(value);\n\t\t\tif (ptype->clipbounce < 0 && ptype->cliptype == P_INVALID)\n\t\t\t\tptype->cliptype = pnum;\n\t\t}\n\t\telse if (!strcmp(var, \"bounce\"))\n\t\t{\n\t\t\tptype->cliptype = pnum;\n\t\t\tptype->clipbounce = atof(value);\n\t\t}\n\n\t\telse if (!strcmp(var, \"emit\"))\n\t\t{\n\t\t\tassoc = P_AllocateParticleType(config, value);//careful - this can realloc all the particle types\n\t\t\tptype = &part_type[pnum];\n\t\t\tptype->emit = assoc;\n\t\t}\n\t\telse if (!strcmp(var, \"emitinterval\"))\n\t\t\tptype->emittime = atof(value);\n\t\telse if (!strcmp(var, \"emitintervalrand\"))\n\t\t\tptype->emitrand = atof(value);\n\t\telse if (!strcmp(var, \"emitstart\"))\n\t\t\tptype->emitstart = atof(value);\n\n#ifdef HAVE_LEGACY\n\t\t// old names\n\t\telse if (!strcmp(var, \"areaspread\"))\n\t\t{\n\t\t\tCon_DPrintf(\"%s.%s: areaspread is deprecated, use spawnorg\\n\", ptype->config, ptype->name);\n\t\t\tptype->areaspread = atof(value);\n\t\t}\n\t\telse if (!strcmp(var, \"areaspreadvert\"))\n\t\t{\n\t\t\tCon_DPrintf(\"%s.%s: areaspreadvert is deprecated, use spawnorg\\n\", ptype->config, ptype->name);\n\t\t\tptype->areaspreadvert = atof(value);\n\t\t}\n\t\telse if (!strcmp(var, \"offsetspread\"))\n\t\t{\n\t\t\tCon_DPrintf(\"%s.%s: offsetspread is deprecated, use spawnvel\\n\", ptype->config, ptype->name);\n\t\t\tptype->spawnvel = atof(value);\n\t\t}\n\t\telse if (!strcmp(var, \"offsetspreadvert\"))\n\t\t{\n\t\t\tCon_DPrintf(\"%s.%s: offsetspreadvert is deprecated, use spawnvel\\n\", ptype->config, ptype->name);\n\t\t\tptype->spawnvelvert  = atof(value);\n\t\t}\n#endif\n\n\t\t// current names\n\t\telse if (!strcmp(var, \"spawnorg\"))\n\t\t{\n\t\t\tptype->areaspreadvert = ptype->areaspread = atof(value);\n\n\t\t\tif (Cmd_Argc()>2)\n\t\t\t\tptype->areaspreadvert = atof(Cmd_Argv(2));\n\t\t}\n\t\telse if (!strcmp(var, \"spawnvel\"))\n\t\t{\n\t\t\tptype->spawnvelvert = ptype->spawnvel = atof(value);\n\n\t\t\tif (Cmd_Argc()>2)\n\t\t\t\tptype->spawnvelvert = atof(Cmd_Argv(2));\n\t\t}\n\n#ifdef HAVE_LEGACY\n\t\t// spawn mode param fields\n\t\telse if (!strcmp(var, \"spawnparam1\"))\n\t\t{\n\t\t\tptype->spawnparam1 = atof(value);\n\t\t\tCon_DPrintf(\"%s.%s: 'spawnparam1' is deprecated, use 'spawnmode foo X'\\n\", ptype->config, ptype->name);\n\t\t}\n\t\telse if (!strcmp(var, \"spawnparam2\"))\n\t\t{\n\t\t\tptype->spawnparam2 = atof(value);\n\t\t\tCon_DPrintf(\"%s.%s: 'spawnparam2' is deprecated, use 'spawnmode foo X Y'\\n\", ptype->config, ptype->name);\n\t\t}\n/*\t\telse if (!strcmp(var, \"spawnparam3\"))\n\t\t\tptype->spawnparam3 = atof(value); */\n\t\telse if (!strcmp(var, \"up\"))\n\t\t\tptype->orgbias[2] = atof(value);\n#endif\n\n\t\telse if (!strcmp(var, \"rampmode\"))\n\t\t{\n\t\t\tif (!strcmp(value, \"none\"))\n\t\t\t\tptype->rampmode = RAMP_NONE;\n#ifdef HAVE_LEGACY\n\t\t\telse if (!strcmp(value, \"absolute\"))\n\t\t\t{\n\t\t\t\tCon_DPrintf(\"%s.%s: 'rampmode absolute' is deprecated, use 'rampmode nearest'\\n\", ptype->config, ptype->name);\n\t\t\t\tptype->rampmode = RAMP_NEAREST;\n\t\t\t}\n#endif\n\t\t\telse if (!strcmp(value, \"nearest\"))\n\t\t\t\tptype->rampmode = RAMP_NEAREST;\n\t\t\telse if (!strcmp(value, \"lerp\"))\t//don't use the name 'linear'. ramps are there to avoid linear...\n\t\t\t\tptype->rampmode = RAMP_LERP;\n\t\t\telse if (!strcmp(value, \"delta\"))\n\t\t\t\tptype->rampmode = RAMP_DELTA;\n\t\t\telse\n\t\t\t{\n\t\t\t\tCon_DPrintf(\"%s.%s: uses unknown ramp mode '%s', assuming 'delta'\\n\", ptype->config, ptype->name, value);\n\t\t\t\tptype->rampmode = RAMP_DELTA;\n\t\t\t}\n\t\t}\n\t\telse if (!strcmp(var, \"rampindexlist\"))\n\t\t{ // better not use this with delta ramps...\n\t\t\tint cidx, i;\n\n\t\t\ti = 1;\n\t\t\twhile (i < Cmd_Argc())\n\t\t\t{\n\t\t\t\tptype->ramp = BZ_Realloc(ptype->ramp, sizeof(ramp_t)*(ptype->rampindexes+1));\n\n\t\t\t\tcidx = atoi(Cmd_Argv(i));\n\t\t\t\tptype->ramp[ptype->rampindexes].alpha = cidx > 255 ? 0.5 : 1;\n\n\t\t\t\tcidx = (cidx & 0xff) * 3;\n\t\t\t\tptype->ramp[ptype->rampindexes].rgb[0] = host_basepal[cidx] * (1/255.0);\n\t\t\t\tptype->ramp[ptype->rampindexes].rgb[1] = host_basepal[cidx+1] * (1/255.0);\n\t\t\t\tptype->ramp[ptype->rampindexes].rgb[2] = host_basepal[cidx+2] * (1/255.0);\n\n\t\t\t\tptype->ramp[ptype->rampindexes].scale = ptype->scale;\n\n\t\t\t\tptype->rampindexes++;\n\t\t\t\ti++;\n\t\t\t}\n\t\t}\n\t\telse if (!strcmp(var, \"rampindex\"))\n\t\t{\n\t\t\tint cidx;\n\t\t\tptype->ramp = BZ_Realloc(ptype->ramp, sizeof(ramp_t)*(ptype->rampindexes+1));\n\n\t\t\tcidx = atoi(value);\n\t\t\tptype->ramp[ptype->rampindexes].alpha = cidx > 255 ? 0.5 : 1;\n\n\t\t\tif (Cmd_Argc() > 2) // they gave alpha\n\t\t\t\tptype->ramp[ptype->rampindexes].alpha *= atof(Cmd_Argv(2));\n\n\t\t\tcidx = (cidx & 0xff) * 3;\n\t\t\tptype->ramp[ptype->rampindexes].rgb[0] = host_basepal[cidx] * (1/255.0);\n\t\t\tptype->ramp[ptype->rampindexes].rgb[1] = host_basepal[cidx+1] * (1/255.0);\n\t\t\tptype->ramp[ptype->rampindexes].rgb[2] = host_basepal[cidx+2] * (1/255.0);\n\n\t\t\tif (Cmd_Argc() > 3) // they gave scale\n\t\t\t\tptype->ramp[ptype->rampindexes].scale = atof(Cmd_Argv(3));\n\t\t\telse\n\t\t\t\tptype->ramp[ptype->rampindexes].scale = ptype->scale;\n\n\n\t\t\tptype->rampindexes++;\n\t\t}\n\t\telse if (!strcmp(var, \"ramp\"))\n\t\t{\n\t\t\tptype->ramp = BZ_Realloc(ptype->ramp, sizeof(ramp_t)*(ptype->rampindexes+1));\n\n\t\t\tptype->ramp[ptype->rampindexes].rgb[0] = atof(value)/255;\n\t\t\tif (Cmd_Argc()>3)\t//seperate rgb\n\t\t\t{\n\t\t\t\tptype->ramp[ptype->rampindexes].rgb[1] = atof(Cmd_Argv(2))/255;\n\t\t\t\tptype->ramp[ptype->rampindexes].rgb[2] = atof(Cmd_Argv(3))/255;\n\n\t\t\t\tif (Cmd_Argc()>4)\t//have we alpha and scale changes?\n\t\t\t\t{\n\t\t\t\t\tptype->ramp[ptype->rampindexes].alpha = atof(Cmd_Argv(4));\n\t\t\t\t\tif (Cmd_Argc()>5)\t//have we scale changes?\n\t\t\t\t\t\tptype->ramp[ptype->rampindexes].scale = atof(Cmd_Argv(5));\n\t\t\t\t\telse\n\t\t\t\t\t\tptype->ramp[ptype->rampindexes].scale = ptype->scaledelta;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tptype->ramp[ptype->rampindexes].alpha = ptype->alpha;\n\t\t\t\t\tptype->ramp[ptype->rampindexes].scale = ptype->scaledelta;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\t//they only gave one value\n\t\t\t{\n\t\t\t\tptype->ramp[ptype->rampindexes].rgb[1] = ptype->ramp[ptype->rampindexes].rgb[0];\n\t\t\t\tptype->ramp[ptype->rampindexes].rgb[2] = ptype->ramp[ptype->rampindexes].rgb[0];\n\n\t\t\t\tptype->ramp[ptype->rampindexes].alpha = ptype->alpha;\n\t\t\t\tptype->ramp[ptype->rampindexes].scale = ptype->scaledelta;\n\t\t\t}\n\n\t\t\tptype->rampindexes++;\n\t\t}\n\t\telse if (!strcmp(var, \"viewspace\"))\n\t\t\tptype->viewspacefrac = (Cmd_Argc()>1)?atof(value):1;\n\t\telse if (!strcmp(var, \"perframe\"))\n\t\t\tptype->flags |= PT_INVFRAMETIME;\n\t\telse if (!strcmp(var, \"averageout\"))\n\t\t\tptype->flags |= PT_AVERAGETRAIL;\n\t\telse if (!strcmp(var, \"nostate\"))\n\t\t\tptype->flags |= PT_NOSTATE;\n\t\telse if (!strcmp(var, \"nospreadfirst\"))\n\t\t\tptype->flags |= PT_NOSPREADFIRST;\n\t\telse if (!strcmp(var, \"nospreadlast\"))\n\t\t\tptype->flags |= PT_NOSPREADLAST;\n\n\t\telse if (!strcmp(var, \"lightradius\"))\n\t\t{\t//float version\n\t\t\tptype->dl_radius[0] = ptype->dl_radius[1] = atof(value);\n\t\t\tif (Cmd_Argc()>2)\n\t\t\t\tptype->dl_radius[1] = atof(Cmd_Argv(2));\n\t\t\tptype->dl_radius[1] -= ptype->dl_radius[0];\n\t\t}\n\t\telse if (!strcmp(var, \"lightradiusfade\"))\n\t\t\tptype->dl_decay[3] = atof(value);\n\t\telse if (!strcmp(var, \"lightrgb\"))\n\t\t{\n\t\t\tptype->dl_rgb[0] = atof(value);\n\t\t\tptype->dl_rgb[1] = atof(Cmd_Argv(2));\n\t\t\tptype->dl_rgb[2] = atof(Cmd_Argv(3));\n\t\t}\n\t\telse if (!strcmp(var, \"lightrgbfade\"))\n\t\t{\n\t\t\tptype->dl_decay[0] = atof(value);\n\t\t\tptype->dl_decay[1] = atof(Cmd_Argv(2));\n\t\t\tptype->dl_decay[2] = atof(Cmd_Argv(3));\n\t\t}\n\t\telse if (!strcmp(var, \"lightcorona\"))\n\t\t{\n\t\t\tptype->dl_corona_intensity = atof(value);\n\t\t\tptype->dl_corona_scale = atof(Cmd_Argv(2));\n\t\t}\n\t\telse if (!strcmp(var, \"lighttime\"))\n\t\t\tptype->dl_time = atof(value);\n\t\telse if (!strcmp(var, \"lightshadows\"))\n\t\t\tptype->flags = (ptype->flags & ~PT_NODLSHADOW) | (atof(value)?0:PT_NODLSHADOW);\n\t\telse if (!strcmp(var, \"lightcubemap\"))\n\t\t\tptype->dl_cubemapnum = atoi(value);\n\t\telse if (!strcmp(var, \"lightstyle\"))\n\t\t\tptype->dl_lightstyle = atoi(value);\n\t\telse if (!strcmp(var, \"lightscales\"))\n\t\t{\t//ambient diffuse specular\n\t\t\tptype->dl_scales[0] = atof(value);\n\t\t\tptype->dl_scales[1] = atof(Cmd_Argv(2));\n\t\t\tptype->dl_scales[2] = atof(Cmd_Argv(3));\n\t\t}\n\t\telse if (!strcmp(var, \"spawnstain\"))\n\t\t{\n\t\t\tptype->stain_radius = atof(value);\n\t\t\tptype->stain_rgb[0] = atof(Cmd_Argv(2));\n\t\t\tptype->stain_rgb[1] = atof(Cmd_Argv(3));\n\t\t\tptype->stain_rgb[2] = atof(Cmd_Argv(4));\n\t\t}\n\t\telse if (Cmd_Argc())\n\t\t\tCon_DPrintf(\"%s.%s: %s is not a recognised particle type field\\n\", ptype->config, ptype->name, var);\n\t}\n\tptype->loaded = part_parseweak?1:2;\n\tif (ptype->clipcount < 1)\n\t\tptype->clipcount = 1;\n\n\tif (!settype)\n\t{\n\t\tif (ptype->looks.type == PT_NORMAL && !*ptype->texname)\n\t\t{\n\t\t\tif (ptype->scale)\n\t\t\t{\n\t\t\t\tptype->looks.type = PT_SPARKFAN;\n\t\t\t\tif (ptype->countextra || ptype->count || ptype->countrand)\n\t\t\t\t\tCon_DPrintf(\"%s.%s: effect lacks a texture. assuming type sparkfan.\\n\", ptype->config, ptype->name);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tptype->looks.type = PT_SPARK;\n\t\t\t\tif (ptype->countextra || ptype->count || ptype->countrand)\n\t\t\t\t\tCon_DPrintf(\"%s.%s: effect lacks a texture. assuming type spark.\\n\", ptype->config, ptype->name);\n\t\t\t}\n\t\t}\n\t\telse if (ptype->looks.type == PT_SPARK)\n\t\t{\n\t\t\tif (*ptype->texname)\n\t\t\t\tptype->looks.type = PT_TEXTUREDSPARK;\n\t\t\telse if (ptype->scale)\n\t\t\t\tptype->looks.type = PT_SPARKFAN;\n\t\t}\n\t}\n\n\t// use old behavior if not using alphadelta\n\tif (!setalphadelta)\n\t\tptype->alphachange = (-ptype->alphachange / ptype->die) * ptype->alpha;\n\n\tFinishParticleType(ptype);\n\n\tif ((ptype->looks.type == PT_BEAM||ptype->looks.type == PT_VBEAM) && !setbeamlen)\n\t\tptype->rotationstartmin = 1/128.0;\n}\n\nqboolean PScript_Query(int typenum, int body, char *outstr, int outstrlen)\n{\n#ifndef QUAKETC\n\tint i;\n\tpart_type_t *ptype = &part_type[typenum];\n\tif (typenum < 0 || typenum >= numparticletypes)\n\t\treturn false;\n\n\tif (body == 0)\n\t{\n\t\tQ_snprintfz(outstr, outstrlen, \"%s.%s\", ptype->config, ptype->name);\n\t\treturn true;\n\t}\n\n\tif (body == 1 || body == 2)\n\t{\n\t\tqboolean all = body==2;\n\t\t*outstr = 0;\n\t\tif (!ptype->loaded)\n\t\t\treturn true;\n\n//\t\tQ_strncatz(outstr, va(\"//this functionality is incomplete\\n\"), outstrlen);\n\n\t\tswitch (ptype->looks.type)\n\t\t{\n\t\tdefault:\n\t\tcase PT_NORMAL:\n\t\t\tQ_strncatz(outstr, \"type normal\\n\", outstrlen);\n\t\t\tbreak;\n\t\tcase PT_SPARK:\n\t\t\tQ_strncatz(outstr, \"type linespark\\n\", outstrlen);\n\t\t\tbreak;\n\t\tcase PT_SPARKFAN:\n\t\t\tQ_strncatz(outstr, \"type sparkfan\\n\", outstrlen);\n\t\t\tbreak;\n\t\tcase PT_TEXTUREDSPARK:\n\t\t\tQ_strncatz(outstr, \"type texturedspark\\n\", outstrlen);\n\t\t\tbreak;\n\t\tcase PT_BEAM:\n\t\t\tQ_strncatz(outstr, \"type beam\\n\", outstrlen);\n\t\t\tbreak;\n\t\tcase PT_VBEAM:\n\t\t\tQ_strncatz(outstr, \"type vbeam\\n\", outstrlen);\n\t\t\tbreak;\n\t\tcase PT_CDECAL:\n\t\t\tif (ptype->surfflagmatch || ptype->surfflagmask)\n\t\t\t\tQ_strncatz(outstr, va(\"clippeddecal %#x %#x\\n\", ptype->surfflagmask, ptype->surfflagmatch), outstrlen);\n\t\t\telse\n\t\t\t\tQ_strncatz(outstr, \"type cdecal\\n\", outstrlen);\n\t\t\tbreak;\n\t\tcase PT_UDECAL:\n\t\t\tQ_strncatz(outstr, \"type udecal\\n\", outstrlen);\n\t\t\tbreak;\n\t\t}\n\t\tswitch (ptype->looks.blendmode)\n\t\t{\n\t\tcase BM_BLEND:\n\t\t\tQ_strncatz(outstr, \"blend blendalpha\\n\", outstrlen);\n\t\t\tbreak;\n\t\tcase BM_BLENDCOLOUR:\n\t\t\tQ_strncatz(outstr, \"blend blendcolour\\n\", outstrlen);\n\t\t\tbreak;\n\t\tcase BM_ADDA:\n\t\t\tQ_strncatz(outstr, \"blend adda\\n\", outstrlen);\n\t\t\tbreak;\n\t\tcase BM_ADDC:\n\t\t\tQ_strncatz(outstr, \"blend addc\\n\", outstrlen);\n\t\t\tbreak;\n\t\tcase BM_SUBTRACT:\n\t\t\tQ_strncatz(outstr, \"blend subtract\\n\", outstrlen);\n\t\t\tbreak;\n\t\tcase BM_INVMODA:\n\t\t\tQ_strncatz(outstr, \"blend invmoda\\n\", outstrlen);\n\t\t\tbreak;\n\t\tcase BM_INVMODC:\n\t\t\tif (ptype->looks.premul)\n\t\t\t\tQ_strncatz(outstr, \"blend premul_subtract\\n\", outstrlen);\n\t\t\telse\n\t\t\t\tQ_strncatz(outstr, \"blend invmodc\\n\", outstrlen);\n\t\t\tbreak;\n\t\tcase BM_PREMUL:\n\t\t\tif (ptype->looks.premul == 2)\n\t\t\t\tQ_strncatz(outstr, \"blend premul_add\\n\", outstrlen);\n\t\t\telse\n\t\t\t\tQ_strncatz(outstr, \"blend premul_blend\\n\", outstrlen);\n\t\t\tbreak;\n\t\tcase BM_RTSMOKE:\n\t\t\tQ_strncatz(outstr, \"blend rtsmoke\\n\", outstrlen);\n\t\t\tbreak;\n\t\t}\n\n\t\tfor (i = 0; i < ptype->nummodels; i++)\n\t\t{\n\t\t\tQ_strncatz(outstr, va(\"model \\\"%s\\\" framestart=%g frames=%g framerate=%g alpha=%g skin=%i\",\n\t\t\t\tptype->models[i].name, ptype->models[i].framestart, ptype->models[i].framecount, ptype->models[i].framerate, ptype->models[i].alpha, ptype->models[i].skin\n\t\t\t\t), outstrlen);\n\t\t\tif (ptype->models[i].traileffect!=P_INVALID)\n\t\t\t\tQ_strncatz(outstr, va(\" trail=%s\", part_type[ptype->models[i].traileffect].name), outstrlen);\n\t\t\tif (ptype->models[i].scalemin != 1 || ptype->models[i].scalemax != 1)\n\t\t\t\tQ_strncatz(outstr, va(\" scalemin=%g scalemax=%g\", ptype->models[i].scalemin, ptype->models[i].scalemax), outstrlen);\n\t\t\tif (ptype->models[i].rflags&RF_USEORIENTATION)\n\t\t\t\tQ_strncatz(outstr, \" orient\", outstrlen);\n\t\t\tif (ptype->models[i].rflags&RF_ADDITIVE)\n\t\t\t\tQ_strncatz(outstr, \" additive\", outstrlen);\n\t\t\tif (ptype->models[i].rflags&RF_TRANSLUCENT)\n\t\t\t\tQ_strncatz(outstr, \" transparent\", outstrlen);\n\t\t\tif (ptype->models[i].rflags&RF_FULLBRIGHT)\n\t\t\t\tQ_strncatz(outstr, \" fullbright\", outstrlen);\n\t\t\tif (ptype->models[i].rflags&RF_NOSHADOW)\n\t\t\t\tQ_strncatz(outstr, \" noshadow\", outstrlen);\n\t\t\telse\n\t\t\t\tQ_strncatz(outstr, \" shadow\", outstrlen);\n\t\t\tQ_strncatz(outstr, \"\\n\", outstrlen);\n\t\t}\n\t\tfor (i = 0; i < ptype->numsounds; i++)\n\t\t{\n\t\t\tQ_strncatz(outstr, va(\"sound \\\"%s\\\" %g %g %g %g %g\\n\", ptype->sounds[i].name, ptype->sounds[i].vol, ptype->sounds[i].atten, ptype->sounds[i].pitch, ptype->sounds[i].delay, ptype->sounds[i].weight), outstrlen);\n\t\t}\n\n\t\tif (*ptype->texname || all)\n\t\t{\t//note: particles don't really know if the shader was embedded or not. the shader system handles all that.\n\t\t\t//this means that you'll really need to use external shaders for this to work.\n\t\t\tQ_strncatz(outstr, va(\"%stexture \\\"%s\\\"\\n\", ptype->looks.nearest?\"nearest_\":\"\", ptype->texname), outstrlen);\n\t\t\tQ_strncatz(outstr, va(\"tcoords %g %g %g %g %g %i %g\\n\", ptype->s1, ptype->t1, ptype->s2, ptype->t2, 1.0f, ptype->randsmax, ptype->texsstride), outstrlen);\n\t\t}\n\n\t\tif (ptype->count || ptype->countrand || ptype->countextra || all)\n\t\t\tQ_strncatz(outstr, va(\"count %g %g %g\\n\", ptype->count, ptype->countrand, ptype->countextra), outstrlen);\n\t\tif (ptype->rainfrequency != 1 || all)\n\t\t\tQ_strncatz(outstr, va(\"rainfrequency %g\\n\", ptype->rainfrequency), outstrlen);\n\n\t\tif (ptype->rgb[0] || ptype->rgb[1] || ptype->rgb[2] || all)\n\t\t\tQ_strncatz(outstr, va(\"rgbf %g %g %g\\n\", ptype->rgb[0], ptype->rgb[1], ptype->rgb[2]), outstrlen);\n\t\tif (ptype->rgbrand[0] || ptype->rgbrand[1] || ptype->rgbrand[2] || all)\n\t\t\tQ_strncatz(outstr, va(\"rgbrandf %g %g %g\\n\", ptype->rgbrand[0], ptype->rgbrand[1], ptype->rgbrand[2]), outstrlen);\n\t\tif (ptype->rgbrandsync[0] || ptype->rgbrandsync[1] || ptype->rgbrandsync[2] || all)\n\t\t\tQ_strncatz(outstr, va(\"rgbrandsync %g %g %g\\n\", ptype->rgbrandsync[0], ptype->rgbrandsync[1], ptype->rgbrandsync[2]), outstrlen);\n\t\tif (ptype->rgbchange[0] || ptype->rgbchange[1] || ptype->rgbchange[2] || all)\n\t\t\tQ_strncatz(outstr, va(\"rgbdeltaf %g %g %g\\n\", ptype->rgbchange[0], ptype->rgbchange[1], ptype->rgbchange[2]), outstrlen);\n\t\tif (ptype->rgbchangetime || all)\n\t\t\tQ_strncatz(outstr, va(\"rgbchangetime %g\\n\", ptype->rgbchangetime), outstrlen);\n\n\t\tif (ptype->colorindex != -1 || all)\n\t\t\tQ_strncatz(outstr, va(\"colorindex %i\\n\", ptype->colorindex), outstrlen);\n\t\tif (ptype->colorrand || all)\n\t\t\tQ_strncatz(outstr, va(\"colorrand %i\\n\", ptype->colorrand), outstrlen);\n\n//\t\tif (ptype->alpha || all)\n\t\t\tQ_strncatz(outstr, va(\"alpha %g\\n\", ptype->alpha), outstrlen);\n\t\tif (ptype->alpharand || all)\n\t\t\tQ_strncatz(outstr, va(\"alpharand %g\\n\", ptype->alpharand), outstrlen);\n//\t\tif (ptype->alphachange || all)\n\t\t\tQ_strncatz(outstr, va(\"alphadelta %g\\n\", ptype->alphachange), outstrlen);\n\n\t\tif (ptype->scale || ptype->scalerand || all)\n\t\t\tQ_strncatz(outstr, va(\"scale %g %g\\n\", ptype->scale, ptype->scale+ptype->scalerand), outstrlen);\n//\t\tif (ptype->looks.scalefactor || all)\t//always write scalefactor, to avoid issues with defaults.\n\t\t\tQ_strncatz(outstr, va(\"scalefactor %g\\n\", ptype->looks.scalefactor), outstrlen);\n\t\tif (ptype->scaledelta || all)\n\t\t\tQ_strncatz(outstr, va(\"scaledelta %g\\n\", ptype->scaledelta), outstrlen);\n\t\tif (ptype->looks.stretch!=0.05 || ptype->looks.minstretch || all)\n\t\t{\n\t\t\tif (ptype->looks.type != PT_TEXTUREDSPARK)\n\t\t\t\tQ_strncatz(outstr, \"//\", outstrlen);\n\t\t\tQ_strncatz(outstr, va(\"stretchfactor %g %g\\n\", ptype->looks.stretch, ptype->looks.minstretch), outstrlen);\n\t\t}\n\n\t\tif (ptype->die || ptype->randdie || all)\n\t\t\tQ_strncatz(outstr, va(\"die %g %g\\n\", ptype->die, ptype->die+ptype->randdie), outstrlen);\n\n\t\tif (ptype->cliptype != P_INVALID && ptype->cliptype != typenum)\n\t\t\tQ_strncatz(outstr, va(\"cliptype \\\"%s.%s\\\"\\n\", part_type[ptype->cliptype].config, part_type[ptype->cliptype].name), outstrlen);\n\t\tif (ptype->clipbounce != 0.8)\n\t\t\tQ_strncatz(outstr, va(\"clipbounce %g\\n\", ptype->clipbounce), outstrlen);\n\t\tif (ptype->clipcount > 1)\n\t\t\tQ_strncatz(outstr, va(\"clipcount %g\\n\", ptype->clipcount), outstrlen);\n\n\t\tif (ptype->spawnmode != SM_BOX || ptype->spawnparam1 || ptype->spawnparam2 || all)\n\t\tswitch(ptype->spawnmode)\n\t\t{\n\t\tcase SM_CIRCLE:\n\t\t\tQ_strncatz(outstr, va(\"spawnmode circle %g %g\\n\", ptype->spawnparam1, ptype->spawnparam2), outstrlen);\n\t\t\tbreak;\n\t\tcase SM_BALL:\n\t\t\tQ_strncatz(outstr, va(\"spawnmode ball %g %g\\n\", ptype->spawnparam1, ptype->spawnparam2), outstrlen);\n\t\t\tbreak;\n\t\tcase SM_SPIRAL:\n\t\t\tQ_strncatz(outstr, va(\"spawnmode spiral %g %g\\n\", ptype->spawnparam1, ptype->spawnparam2), outstrlen);\n\t\t\tbreak;\n\t\tcase SM_TRACER:\n\t\t\tQ_strncatz(outstr, va(\"spawnmode tracer %g %g\\n\", ptype->spawnparam1, ptype->spawnparam2), outstrlen);\n\t\t\tbreak;\n\t\tcase SM_TELEBOX:\n\t\t\tQ_strncatz(outstr, va(\"spawnmode telebox %g %g\\n\", ptype->spawnparam1, ptype->spawnparam2), outstrlen);\n\t\t\tbreak;\n\t\tcase SM_LAVASPLASH:\n\t\t\tQ_strncatz(outstr, va(\"spawnmode lavasplash %g %g\\n\", ptype->spawnparam1, ptype->spawnparam2), outstrlen);\n\t\t\tbreak;\n\t\tcase SM_UNICIRCLE:\n\t\t\tQ_strncatz(outstr, va(\"spawnmode uniformcircle %g %g\\n\", ptype->spawnparam1, ptype->spawnparam2), outstrlen);\n\t\t\tbreak;\n\t\tcase SM_FIELD:\n\t\t\tQ_strncatz(outstr, va(\"spawnmode syncfield %g %g\\n\", ptype->spawnparam1, ptype->spawnparam2), outstrlen);\n\t\t\tbreak;\n\t\tcase SM_DISTBALL:\n\t\t\tQ_strncatz(outstr, va(\"spawnmode distball %g %g\\n\", ptype->spawnparam1, ptype->spawnparam2), outstrlen);\n\t\t\tbreak;\n\t\tcase SM_BOX:\n\t\t\tQ_strncatz(outstr, va(\"spawnmode box %g %g\\n\", ptype->spawnparam1, ptype->spawnparam2), outstrlen);\n\t\t\tbreak;\n\t\tcase SM_MESHSURFACE:\n\t\t\tQ_strncatz(outstr, va(\"spawnmode meshsurface\\n\"), outstrlen);\n\t\t\tbreak;\n\t\tcase SM_FIXMEWARNING:\n\t\t\tQ_strncatz(outstr, va(\"placeholder\\n\"), outstrlen);\n\t\t\tbreak;\n\t\t}\n\t\tif (ptype->spawnvel || ptype->spawnvelvert || all)\n\t\t\tQ_strncatz(outstr, va(\"spawnvel %g %g\\n\", ptype->spawnvel, ptype->spawnvelvert), outstrlen);\n\t\tif (ptype->areaspread || ptype->areaspreadvert || all)\n\t\t\tQ_strncatz(outstr, va(\"spawnorg %g %g\\n\", ptype->areaspread, ptype->areaspreadvert), outstrlen);\n\n\t\tif (ptype->viewspacefrac || all)\n\t\t\tQ_strncatz(outstr, ((ptype->viewspacefrac==1)?\"viewspace\\n\":va(\"viewspace %g\\n\", ptype->viewspacefrac)), outstrlen);\n\n\t\tif (ptype->veladd || ptype->randomveladd || all)\n\t\t{\n\t\t\tif (ptype->randomveladd)\n\t\t\t\tQ_strncatz(outstr, va(\"veladd %g %g\\n\", ptype->veladd, ptype->veladd+ptype->randomveladd), outstrlen);\n\t\t\telse\n\t\t\t\tQ_strncatz(outstr, va(\"veladd %g\\n\", ptype->veladd), outstrlen);\n\t\t}\n\t\tif (ptype->orgadd || ptype->randomorgadd || all)\n\t\t{\n\t\t\tif (ptype->randomorgadd)\n\t\t\t\tQ_strncatz(outstr, va(\"orgadd %g %g\\n\", ptype->orgadd, ptype->orgadd+ptype->randomorgadd), outstrlen);\n\t\t\telse\n\t\t\t\tQ_strncatz(outstr, va(\"orgadd %g\\n\", ptype->orgadd), outstrlen);\n\t\t}\n\n\t\tif (ptype->gravity || all)\n\t\t\tQ_strncatz(outstr, va(\"gravity %g\\n\", ptype->gravity), outstrlen);\n\t\tif (ptype->flurry)\n\t\t\tQ_strncatz(outstr, va(\"flurry %g\\n\", ptype->flurry), outstrlen);\n\n\t\t//these are more for dp-compat than anything else.\n\t\tif (DotProduct(ptype->orgbias,ptype->orgbias) || all)\n\t\t\tQ_strncatz(outstr, va(\"orgbias %g %g %g\\n\", ptype->orgbias[0], ptype->orgbias[1], ptype->orgbias[2]), outstrlen);\n\t\tif (DotProduct(ptype->orgwrand,ptype->orgwrand) || all)\n\t\t\tQ_strncatz(outstr, va(\"orgwrand %g %g %g\\n\", ptype->orgwrand[0], ptype->orgwrand[1], ptype->orgwrand[2]), outstrlen);\n\t\tif (ptype->velbias[0] == 0 && ptype->velbias[1] == 0 && ptype->velwrand[0] == ptype->velwrand[1] && !all)\n\t\t\tQ_strncatz(outstr, va(\"randomvel %g %g %g\\n\", ptype->velwrand[0], ptype->velbias[2] - ptype->velwrand[2], ptype->velbias[2]*2-(ptype->velbias[2]- ptype->velwrand[2])), outstrlen);\n\t\telse\n\t\t{\n\t\t\tif (DotProduct(ptype->velbias,ptype->velbias) || all)\n\t\t\t\tQ_strncatz(outstr, va(\"velbias %g %g %g\\n\", ptype->velbias[0], ptype->velbias[1], ptype->velbias[2]), outstrlen);\n\t\t\tif (DotProduct(ptype->velwrand,ptype->velwrand) || all)\n\t\t\t\tQ_strncatz(outstr, va(\"velwrand %g %g %g\\n\", ptype->velwrand[0], ptype->velwrand[1], ptype->velwrand[2]), outstrlen);\n\t\t}\n\n\t\tif (ptype->assoc != P_INVALID)\n\t\t\tQ_strncatz(outstr, va(\"assoc \\\"%s\\\"\\n\", part_type[ptype->assoc].name), outstrlen);\n\n\t\tif (ptype->rotationstartmin != -M_PI || ptype->rotationstartrand != 2*M_PI || all)\n\t\t\tQ_strncatz(outstr, va(\"rotationstart %g %g\\n\", ptype->rotationstartmin*180/M_PI, (ptype->rotationstartmin+ptype->rotationstartrand)*180/M_PI), outstrlen);\n\t\tif (ptype->rotationmin || ptype->rotationrand || all)\n\t\t\tQ_strncatz(outstr, va(\"rotationspeed %g %g\\n\", ptype->rotationmin*180/M_PI, (ptype->rotationmin+ptype->rotationrand)*180/M_PI), outstrlen);\n\n\t\tif (ptype->flags & (PT_TROVERWATER | PT_TRUNDERWATER))\n\t\t{\n\t\t\tif (ptype->flags & PT_TRUNDERWATER)\n\t\t\t\tQ_strncatz(outstr, \"underwater\", outstrlen);\n\t\t\telse\n\t\t\t\tQ_strncatz(outstr, \"notunderwater\", outstrlen);\n\t\t\tif (ptype->fluidmask == 0)\n\t\t\t\tQ_strncatz(outstr, \" none\", outstrlen);\n\t\t\telse if (ptype->fluidmask != FTECONTENTS_FLUID)\n\t\t\t{\n\t\t\t\tif ((ptype->fluidmask & FTECONTENTS_FLUID) == FTECONTENTS_FLUID)\n\t\t\t\t\tQ_strncatz(outstr, \" fluid\", outstrlen);\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tif (ptype->fluidmask & FTECONTENTS_WATER)\n\t\t\t\t\t\tQ_strncatz(outstr, \" water\", outstrlen);\n\t\t\t\t\tif (ptype->fluidmask & FTECONTENTS_SLIME)\n\t\t\t\t\t\tQ_strncatz(outstr, \" slime\", outstrlen);\n\t\t\t\t\tif (ptype->fluidmask & FTECONTENTS_LAVA)\n\t\t\t\t\t\tQ_strncatz(outstr, \" lava\", outstrlen);\n\t\t\t\t\tif (ptype->fluidmask & FTECONTENTS_SKY)\n\t\t\t\t\t\tQ_strncatz(outstr, \" sky\", outstrlen);\n\t\t\t\t}\n\t\t\t\tif (ptype->fluidmask & FTECONTENTS_SOLID)\n\t\t\t\t\tQ_strncatz(outstr, \" solid\", outstrlen);\n\t\t\t\tif (ptype->fluidmask & FTECONTENTS_PLAYERCLIP)\n\t\t\t\t\tQ_strncatz(outstr, \" playerclip\", outstrlen);\n\t\t\t}\n\t\t\tQ_strncatz(outstr, \"\\n\", outstrlen);\n\t\t}\n\n\t\tif (ptype->dl_radius[0] || ptype->dl_radius[1] || all)\n\t\t{\n\t\t\tQ_strncatz(outstr, va(\"lightradius %g %g\\n\", ptype->dl_radius[0], ptype->dl_radius[0]+ptype->dl_radius[1]), outstrlen);\n\t\t\tQ_strncatz(outstr, va(\"lightradiusfade %g\\n\", ptype->dl_decay[3]), outstrlen);\n\t\t\tQ_strncatz(outstr, va(\"lightrgb %g %g %g\\n\", ptype->dl_rgb[0], ptype->dl_rgb[1], ptype->dl_rgb[2]), outstrlen);\n\t\t\tQ_strncatz(outstr, va(\"lightrgbfade %g %g %g\\n\", ptype->dl_decay[0], ptype->dl_decay[1], ptype->dl_decay[2]), outstrlen);\n\t\t\tQ_strncatz(outstr, va(\"lighttime %g\\n\", ptype->dl_time), outstrlen);\n\t\t\tQ_strncatz(outstr, va(\"lightshadows %g\\n\", (ptype->flags & PT_NODLSHADOW)?0.0f:1.0f), outstrlen);\n\t\t\tQ_strncatz(outstr, va(\"lightcubemap %i\\n\", ptype->dl_cubemapnum), outstrlen);\n\t\t\tQ_strncatz(outstr, va(\"lightstyle %i\\n\", ptype->dl_lightstyle), outstrlen);\n\t\t\tQ_strncatz(outstr, va(\"lightcorona %g %g\\n\", ptype->dl_corona_intensity, ptype->dl_corona_scale), outstrlen);\n\t\t\tQ_strncatz(outstr, va(\"lightscales %g %g %g\\n\", ptype->dl_scales[0], ptype->dl_scales[1], ptype->dl_scales[2]), outstrlen);\n\t\t}\n\t\tif (ptype->stain_radius || all)\n\t\t\tQ_strncatz(outstr, va(\"spawnstain %g %g %g %g\\n\", ptype->stain_radius, ptype->stain_rgb[0], ptype->stain_rgb[1], ptype->stain_rgb[2]), outstrlen);\n\t\tif (ptype->stainonimpact || all)\n\t\t\tQ_strncatz(outstr, va(\"stains %g\\n\", ptype->stainonimpact), outstrlen);\n\n\t\treturn true;\n\t}\n#endif\n\treturn false;\n}\n\n#ifdef HAVE_LEGACY\nstatic void P_ExportAllEffects_f(void)\n{\n\tchar effect[8192];\n\tint i, assoc, n;\n\tvfsfile_t *outf;\n\tchar fname[64] = \"particles/export.cfg\";\n\tFS_CreatePath(\"particles/\", FS_GAMEONLY);\n\toutf = FS_OpenVFS(fname, \"wb\", FS_GAMEONLY);\n\tif (!outf)\n\t{\n\t\tFS_DisplayPath(fname, FS_GAMEONLY, effect, sizeof(effect));\n\t\tCon_TPrintf(\"Unable to open file %s\\n\", effect);\n\t\treturn;\n\t}\n\tfor (i = 0; i < numparticletypes; i++)\n\t{\n\t\tPScript_Query(i, 0, effect, sizeof(effect));\n\t\tif (strchr(effect, '+'))\n\t\t\tcontinue;\n\t\tassoc = i;\n\t\twhile(assoc != P_INVALID)\n\t\t{\n\t\t\tn = part_type[assoc].assoc;\n\t\t\tpart_type[assoc].assoc = P_INVALID;\n\t\t\tVFS_PUTS(outf, \"r_part \");\n\t\t\tVFS_PUTS(outf, effect);\n\t\t\tVFS_PUTS(outf, \"\\n{\\n\");\n\t\t\tPScript_Query(assoc, 1, effect, sizeof(effect));\n\t\t\tVFS_PUTS(outf, effect);\n\t\t\tVFS_PUTS(outf, \"}\\n\");\n\t\t\tpart_type[assoc].assoc = n;\n\t\t\tassoc = n;\n\n\t\t\tQ_snprintfz(effect, sizeof(effect), \"%s.+%s\", part_type[i].config, part_type[i].name);\n\t\t}\n\t}\n\tVFS_CLOSE(outf);\n\n\tFS_DisplayPath(fname, FS_GAMEONLY, effect, sizeof(effect));\n\tCon_Printf(\"Written %s\\n\", effect);\n}\n#endif\n\n#if 1//_DEBUG\n// R_BeamInfo_f - debug junk\nstatic void P_BeamInfo_f (void)\n{\n\tbeamseg_t *bs;\n\tint i, j, k, l, m;\n\n\ti = 0;\n\n\tfor (bs = free_beams; bs; bs = bs->next)\n\t\ti++;\n\n\tCon_Printf(\"%i free beams\\n\", i);\n\n\tfor (i = 0; i < numparticletypes; i++)\n\t{\n\t\tm = l = k = j = 0;\n\t\tfor (bs = part_type[i].beams; bs; bs = bs->next)\n\t\t{\n\t\t\tif (!bs->p)\n\t\t\t\tk++;\n\n\t\t\tif (bs->flags & BS_DEAD)\n\t\t\t\tl++;\n\n\t\t\tif (bs->flags & BS_LASTSEG)\n\t\t\t\tm++;\n\n\t\t\tj++;\n\t\t}\n\n\t\tif (j)\n\t\t\tCon_Printf(\"Type %i = %i NULL p, %i DEAD, %i LASTSEG, %i total\\n\", i, k, l, m, j);\n\t}\n}\n\nstatic void P_PartInfo_f (void)\n{\n\tparticle_t *p;\n\tclippeddecal_t *d;\n\tpart_type_t *ptype;\n\tint totalp = 0, totald = 0, freep, freed, runningp=0, runningd=0, runninge=0, runningt=0;\n\n\tint i, j, k;\n\n\tCon_DPrintf(\"Full list of  effects:\\n\");\n\tfor (i = 0; i < numparticletypes; i++)\n\t{\n\t\tj = 0;\n\t\tfor (p = part_type[i].particles; p; p = p->next)\n\t\t\tj++;\n\t\ttotalp += j;\n\n\t\tk = 0;\n\t\tfor (d = part_type[i].clippeddecals; d; d = d->next)\n\t\t\tk++;\n\t\ttotald += k;\n\n\t\tif (j||k)\n\t\t{\n\t\t\tCon_DPrintf(\"Type %s.%s = %i+%i total\\n\", part_type[i].config, part_type[i].name, j,k);\n\t\t\tif (!(part_type[i].state & PS_INRUNLIST))\n\t\t\t\tCon_Printf(CON_WARNING \"%s.%s NOT RUNNING\\n\", part_type[i].config, part_type[i].name);\n\t\t}\n\t}\n\n\tCon_Printf(\"Running effects:\\n\");\n\t// maintain run list\n\tfor (ptype = part_run_list; ptype; ptype = ptype->nexttorun)\n\t{\n\t\tCon_Printf(\"Type %s.%s\", ptype->config, ptype->name);\n\n\t\tj = 0;\n\t\tfor (p = ptype->particles; p; p = p->next)\n\t\t\tj++;\n\t\tif (j)\n\t\t{\n\t\t\tCon_Printf(\"\\t%i particles\", j);\n\t\t\tif (ptype->cliptype >= 0 || ptype->stainonimpact)\n\t\t\t{\n\t\t\t\tCon_Printf(\"(+traceline)\");\n\t\t\t\trunningt += j;\n\t\t\t}\n\t\t}\n\t\trunningp += j;\n\n\t\tk = 0;\n\t\tfor (d = ptype->clippeddecals; d; d = d->next)\n\t\t\tk++;\n\t\tif (k)\n\t\t\tCon_Printf(\"%s%i decals\", ptype->particles?\", \":\"\\t\", k);\n\t\trunningd += k;\n\n\t\tCon_Printf(\"\\n\");\n\t\trunninge++;\n\t}\n\tCon_Printf(\"End of list\\n\");\n\n\tfor (p = free_particles, freep = 0; p; p = p->next)\n\t\tfreep++;\n\tfor (d = free_decals, freed = 0; d; d = d->next)\n\t\tfreed++;\n\n\tCon_DPrintf(\"%i running effects.\\n\", runninge);\n\tCon_Printf(\"%i particles, %i free, %i traces.\\n\", runningp, freep, runningt);\n\tCon_Printf(\"%i decals, %i free.\\n\", runningd, freed);\n\n\tif (totalp != runningp)\n\t\tCon_Printf(\"%i particles unaccounted for\\n\", totalp - runningp);\n\tif (totald != runningd)\n\t\tCon_Printf(\"%i decals unaccounted for\\n\", totald - runningd);\n}\n#endif\n\nstatic void FinishParticleType(part_type_t *ptype)\n{\n\t//if there is a chance that it moves\n\tif (ptype->gravity || ptype->veladd || ptype->spawnvel || ptype->spawnvelvert || DotProduct(ptype->velwrand,ptype->velwrand) || DotProduct(ptype->velbias,ptype->velbias) || ptype->flurry)\n\t\tptype->flags |= PT_VELOCITY;\n\tif (DotProduct(ptype->velbias,ptype->velbias) || DotProduct(ptype->velwrand,ptype->velwrand) || DotProduct(ptype->orgwrand,ptype->orgwrand))\n\t\tptype->flags |= PT_WORLDSPACERAND;\n\t//if it has friction\n\tif (ptype->friction[0] || ptype->friction[1] || ptype->friction[2])\n\t\tptype->flags |= PT_FRICTION;\n\n\tP_LoadTexture(ptype, true);\n\tif (ptype->dl_decay[3] && !ptype->dl_time)\n\t\tptype->dl_time = ptype->dl_radius[0] / ptype->dl_decay[3];\n\tif (ptype->looks.scalefactor > 1 && !ptype->looks.invscalefactor)\n\t{\n\t\tptype->scale *= ptype->looks.scalefactor;\n\t\tptype->scalerand *= ptype->looks.scalefactor;\n\t\t/*too lazy to go through ramps*/\n\t\tptype->looks.scalefactor = 1;\n\t}\n\tptype->looks.invscalefactor = 1-ptype->looks.scalefactor;\n\n\tif (ptype->looks.type == PT_TEXTUREDSPARK && !ptype->looks.stretch)\n\t\tptype->looks.stretch = 0.05;\t//the old default.\n\n\tif (ptype->looks.type == PT_SPARK && r_part_sparks.ival<0)\n\t\tptype->looks.type = PT_INVISIBLE;\n\tif (ptype->looks.type == PT_TEXTUREDSPARK && !r_part_sparks_textured.ival)\n\t\tptype->looks.type = PT_SPARK;\n\tif (ptype->looks.type == PT_SPARKFAN && !r_part_sparks_trifan.ival)\n\t\tptype->looks.type = PT_SPARK;\n\tif (ptype->looks.type == PT_SPARK && !r_part_sparks.ival)\n\t\tptype->looks.type = PT_INVISIBLE;\n\tif ((ptype->looks.type == PT_BEAM||ptype->looks.type == PT_VBEAM) && r_part_beams.ival <= 0)\n\t\tptype->looks.type = PT_INVISIBLE;\n\t\n\tif (ptype->rampmode && !ptype->ramp)\n\t{\n\t\tptype->rampmode = RAMP_NONE;\n\t\tCon_Printf(\"%s.%s: Particle has a ramp mode but no ramp\\n\", ptype->config, ptype->name);\n\t}\n\telse if (ptype->ramp && !ptype->rampmode)\n\t{\n\t\tCon_Printf(\"%s.%s: Particle has a ramp but no ramp mode\\n\", ptype->config, ptype->name);\n\t}\n\tr_plooksdirty = true;\n}\n\n#ifdef HAVE_LEGACY\nstatic void FinishEffectinfoParticleType(part_type_t *ptype, qboolean blooddecalonimpact)\n{\n\tif (ptype->looks.type == PT_CDECAL)\n\t{\n\t\tif (ptype->die == 9999)\n\t\t\tptype->die = 20;\n\t\tptype->alphachange = -(ptype->alpha / ptype->die);\n\t}\n\telse if (ptype->looks.type == PT_UDECAL)\n\t{\n\t\t//dp's decals have a size as a radius. fte's udecals are 'just' quads.\n\t\t//also, dp uses 'stretch'.\n\t\tptype->looks.stretch *= 1/1.414213562373095;\n\t\tptype->scale *= ptype->looks.stretch;\n\t\tptype->scalerand *= ptype->looks.stretch;\n\t\tptype->scaledelta *= ptype->looks.stretch;\n\t\tptype->looks.stretch = 1;\n\t}\n\telse if (ptype->looks.type == PT_NORMAL)\n\t{\n\t\t//fte's textured particles are *0.25 for some reason.\n\t\t//but fte also uses radiuses, while dp uses total size so we only need to double it here..\n\t\tptype->looks.stretch = 1;//affects billboarding in dp\n\t\tptype->scale *= 2*ptype->looks.stretch;\n\t\tptype->scalerand *= 2*ptype->looks.stretch;\n\t\tptype->scaledelta *= 2*2*ptype->looks.stretch;\t//fixme: this feels wrong, the results look correct though. hrmph.\n\t\tptype->looks.stretch = 1;\n\t}\n\telse if (ptype->looks.type == PT_BEAM || ptype->looks.type == PT_VBEAM)\n\t{\t//ignore what the particle says and just spawn it from a to b. don't accept mid-segment randomness.\n\t\tif (ptype->t1 == 0 && ptype->t2 == 1)\n\t\t\tptype->looks.type = PT_VBEAM;\n\t\telse\n\t\t\tptype->looks.type = PT_BEAM;\n\t\tptype->count = 0;\n\t\tptype->countextra = 2;\n\t\tptype->countrand = 0;\n\t\tptype->countspacing = 10000;\n\t\tptype->scale *= 0.5;\n\t}\n\n\tif (blooddecalonimpact)\t//DP blood particles generate decals unconditionally (and prevent blood from bouncing)\n\t\tptype->clipbounce = -2;\n\tif (ptype->looks.type == PT_TEXTUREDSPARK)\n\t{\n\t\tptype->scale *= 2;\n\t\tptype->scalerand *= 2;\n\t\tptype->scaledelta *= 2;\n\t\tptype->looks.stretch *= 0.04;\n//\t\tif (ptype->looks.stretch < 0)\n//\t\t\tptype->looks.stretch = 0.000001;\n\t}\n\t\n\tif (ptype->die == 9999)\t//internal: means unspecified.\n\t{\n\t\tif (ptype->alphachange)\n\t\t\tptype->die = (ptype->alpha+ptype->alpharand)/-ptype->alphachange;\n\t\telse\n\t\t\tptype->die = 15;\n\t}\n\tptype->looks.minstretch = 0.5;\n\tFinishParticleType(ptype);\n}\nstatic void P_ImportEffectInfo(char *config, char *line)\n{\n\tfloat printtimer = 0;\n\tpart_type_t *ptype = NULL;\n\tint parenttype;\n\tchar arg[8][1024];\n\tint args;\n\tqboolean blooddecalonimpact = false;\t//tracked separately because it needs to override another field\n\n\n\tstruct\n\t{\n\t\tfloat tc[4];\n\t\tchar imgname[128];\n\t} teximages[256];\n\n\t{\n\t\tint i;\n\t\tvfsfile_t *file;\n\t\tchar *line, linebuf[1024];\n\t\t//default assumes 8*8 grid, but we allow more\n\t\tfor (i = 0; i < 256; i++)\n\t\t{\n\t\t\tteximages[i].tc[0] = 1/8.0 * (i & 7);\n\t\t\tteximages[i].tc[1] = 1/8.0 * (1+(i & 7));\n\t\t\tteximages[i].tc[2] = 1/8.0 * (1+(i>>3));\n\t\t\tteximages[i].tc[3] = 1/8.0 * (i>>3);\n\t\t\tstrcpy(teximages[i].imgname, \"particles/particlefont\");\n\t\t}\n\n\t\t//and this one needs to be subject to a hack. ho hum.\n\t\tteximages[60].tc[0] = 0;\n\t\tteximages[60].tc[1] = 1;\n\t\tteximages[60].tc[2] = 0;\n\t\tteximages[60].tc[3] = 1;\n\t\tstrcpy(teximages[60].imgname, \"particles/nexbeam\");\n\n\t\tfile = FS_OpenVFS(\"particles/particlefont.txt\", \"rb\", FS_GAME);\n\t\tif (file)\n\t\t{\n\t\t\twhile (VFS_GETS(file, linebuf, sizeof(linebuf)))\n\t\t\t{\n\t\t\t\targs = 0;\n\t\t\t\tline = COM_StringParse(linebuf, arg[args], sizeof(arg[args]), false, false);\n\t\t\t\tif (line)\n\t\t\t\t{\n\t\t\t\t\tfor (args++; args < countof(arg); args++)\n\t\t\t\t\t{\n\t\t\t\t\t\tline = COM_StringParse(line, arg[args], sizeof(arg[args]), false, false);\n\t\t\t\t\t\tif (!line)\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\ti = atoi(arg[0]);\n\t\t\t\tif (i >= countof(teximages))\n\t\t\t\t\tCon_Printf(\"particles/particlefont.txt: index too high - %i>=%u\\n\", i, (unsigned)countof(teximages));\n\t\t\t\telse if (args == 2)\n\t\t\t\t{\n\t\t\t\t\tteximages[i].tc[0] = 0;\n\t\t\t\t\tteximages[i].tc[1] = 1;\n\t\t\t\t\tteximages[i].tc[2] = 0;\n\t\t\t\t\tteximages[i].tc[3] = 1;\n\t\t\t\t\tQ_strncpyz(teximages[i].imgname, arg[1], sizeof(teximages[i].imgname));\n\t\t\t\t}\n\t\t\t\telse if (args >= 5 && args <= 6)\n\t\t\t\t{\n\t\t\t\t\tteximages[i].tc[0] = atof(arg[1]);\t//s1\n\t\t\t\t\tteximages[i].tc[1] = atof(arg[3]);\t//s2\n\t\t\t\t\tteximages[i].tc[2] = atof(arg[4]);\t//t1\n\t\t\t\t\tteximages[i].tc[3] = atof(arg[2]);\t//t2\n\t\t\t\t\tQ_strncpyz(teximages[i].imgname, (args>=6)?arg[5]:\"particles/particlefont\", sizeof(teximages[i].imgname));\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tCon_Printf(\"particles/particlefont.txt: unsupported argument count - %i\\n\", args);\n\t\t\t}\n\t\t\tVFS_CLOSE(file);\n\t\t}\n\t}\n\n\tfor (args = 0;;)\n\t{\n\t\tif (!*line)\n\t\t\tbreak;\n\t\tif (args == 8)\n\t\t{\n\t\t\tCon_Printf(\"Too many args!\\n\");\n\t\t\targs--;\n\t\t}\n\t\tline = COM_StringParse(line, com_token, sizeof(com_token), false, false);\n\t\tif (!line)\n\t\t\tbreak;\n\t\tQ_strncpyz(arg[args], com_token, sizeof(arg[args]));\n\t\targs++;\n\t\tif (*com_token == '\\n')\n\t\t\targs--;\n\t\telse if (*line)\n\t\t\tcontinue;\n\n\t\tif (args <= 0)\n\t\t\tcontinue;\n\n\t\tif (!strcmp(arg[0], \"effect\"))\n\t\t{\n\t\t\tchar newname[64];\n\t\t\tint i;\n\n\t\t\tif (ptype)\n\t\t\t\tFinishEffectinfoParticleType(ptype, blooddecalonimpact);\n\t\t\tblooddecalonimpact = false;\n\n\t\t\tptype = P_GetParticleType(config, arg[1]);\n\t\t\tif (ptype->loaded)\n\t\t\t{\n\t\t\t\tfor (i = 0; i < 64; i++)\n\t\t\t\t{\n\t\t\t\t\tparenttype = ptype - part_type;\n\t\t\t\t\tQ_snprintfz(newname, sizeof(newname), \"%i+%s\", i, arg[1]);\n\t\t\t\t\tptype = P_GetParticleType(config, newname);\n\t\t\t\t\tif (!ptype->loaded)\n\t\t\t\t\t{\n\t\t\t\t\t\tpart_type[parenttype].assoc = ptype - part_type;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (i == 64)\n\t\t\t\t{\n\t\t\t\t\tCon_Printf(\"Too many duplicate names, gave up\\n\");\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tP_ResetToDefaults(ptype);\n\t\t\tptype->loaded = part_parseweak?1:2;\n\t\t\tptype->scale = 1;\n\t\t\tptype->alpha = 0;\n\t\t\tptype->alpharand = 1;\n\t\t\tptype->alphachange = -1;\n\t\t\tptype->die = 9999;\n\t\t\tptype->rgb[0] = 1;\n\t\t\tptype->rgb[1] = 1;\n\t\t\tptype->rgb[2] = 1;\n\n//\t\t\tptype->spawnmode = SM_BALL;\n\n\t\t\tptype->colorindex = -1;\n\t\t\tptype->spawnchance = 1;\n\t\t\tptype->looks.scalefactor = 2;\n\t\t\tptype->looks.invscalefactor = 0;\n\t\t\tptype->looks.type = PT_NORMAL;\n\t\t\tptype->looks.blendmode = BM_PREMUL;\n\t\t\tptype->looks.premul = 1;\n\t\t\tptype->looks.stretch = 1;\n\n\t\t\tptype->dl_time = 0;\n\n\t\t\ti = 63; //default texture is 63.\n\t\t\tQ_strncpyz(ptype->texname, teximages[i].imgname, sizeof(ptype->texname));\n\t\t\tptype->s1 = teximages[i].tc[0];\n\t\t\tptype->s2 = teximages[i].tc[1];\n\t\t\tptype->t1 = teximages[i].tc[2];\n\t\t\tptype->t2 = teximages[i].tc[3];\n\t\t\tptype->texsstride = 0;\n\t\t\tptype->randsmax = 1;\n\t\t}\n\t\telse if (!ptype)\n\t\t{\n\t\t\tCon_Printf(\"Bad effectinfo file\\n\");\n\t\t\tbreak;\n\t\t}\n\t\telse if (!strcmp(arg[0], \"countabsolute\") && args == 2)\n\t\t\tptype->countextra = atof(arg[1]);\n\t\telse if (!strcmp(arg[0], \"count\") && args == 2)\n\t\t\tptype->count = atof(arg[1]);\n\t\telse if (!strcmp(arg[0], \"type\") && args == 2)\n\t\t{\n\t\t\tif (!strcmp(arg[1], \"decal\") || !strcmp(arg[1], \"cdecal\"))\n\t\t\t{\n\t\t\t\tptype->looks.type = PT_CDECAL;\n\t\t\t\tptype->looks.blendmode = BM_INVMODC;\n\t\t\t\tptype->looks.premul = 2;\n\t\t\t}\n\t\t\telse if (!strcmp(arg[1], \"udecal\"))\n\t\t\t{\n\t\t\t\tptype->looks.type = PT_UDECAL;\n\t\t\t\tptype->looks.blendmode = BM_INVMODC;\n\t\t\t\tptype->looks.premul = 2;\n\t\t\t}\n\t\t\telse if (!strcmp(arg[1], \"alphastatic\"))\n\t\t\t{\n\t\t\t\tptype->looks.type = PT_NORMAL;\n\t\t\t\tptype->looks.blendmode = BM_PREMUL;//BM_BLEND;\n\t\t\t\tptype->looks.premul = 1;\n\t\t\t}\n\t\t\telse if (!strcmp(arg[1], \"static\"))\n\t\t\t{\n\t\t\t\tptype->looks.type = PT_NORMAL;\n\t\t\t\tptype->looks.blendmode = BM_PREMUL;//BM_ADDA;\n\t\t\t\tptype->looks.premul = 2;\n\t\t\t}\n\t\t\telse if (!strcmp(arg[1], \"smoke\"))\n\t\t\t{\n\t\t\t\tptype->looks.type = PT_NORMAL;\n\t\t\t\tptype->looks.blendmode = BM_PREMUL;//BM_ADDA;\n\t\t\t\tptype->looks.premul = 2;\n\t\t\t}\n\t\t\telse if (!strcmp(arg[1], \"spark\"))\n\t\t\t{\n\t\t\t\tptype->looks.type = PT_TEXTUREDSPARK;\n\t\t\t\tptype->looks.blendmode = BM_PREMUL;//BM_ADDA;\n\t\t\t\tptype->looks.premul = 2;\n\t\t\t}\n\t\t\telse if (!strcmp(arg[1], \"bubble\"))\n\t\t\t{\n\t\t\t\tptype->looks.type = PT_NORMAL;\n\t\t\t\tptype->looks.blendmode = BM_PREMUL;//BM_ADDA;\n\t\t\t\tptype->looks.premul = 2;\n\t\t\t}\n\t\t\telse if (!strcmp(arg[1], \"blood\"))\n\t\t\t{\n\t\t\t\tptype->looks.type = PT_NORMAL;\n\t\t\t\tptype->looks.blendmode = BM_INVMODC;\n\t\t\t\tptype->looks.premul = 2;\n\t\t\t\tptype->gravity = 800*1;\n\t\t\t\tblooddecalonimpact = true;\n\t\t\t}\n\t\t\telse if (!strcmp(arg[1], \"beam\"))\n\t\t\t{\n\t\t\t\tptype->looks.type = PT_BEAM;\n\t\t\t\tptype->looks.blendmode = BM_PREMUL;//BM_ADDA;\n\t\t\t\tptype->looks.premul = 2;\n\t\t\t}\n\t\t\telse if (!strcmp(arg[1], \"snow\"))\n\t\t\t{\n\t\t\t\tptype->looks.type = PT_NORMAL;\n\t\t\t\tptype->looks.blendmode = BM_PREMUL;//BM_ADDA;\n\t\t\t\tptype->looks.premul = 2;\n\t\t\t\tptype->flurry = 32;\t//may not still be valid later, but at least it would be an obvious issue with the original.\n\t\t\t}\n\t\t\telse if (!strcmp(arg[1], \"entityparticle\"))\n\t\t\t{\n\t\t\t\tptype->die = 0;\n\t\t\t\tptype->looks.type = PT_NORMAL;\n\t\t\t\tptype->looks.blendmode = BM_PREMUL;//BM_BLEND;\n\t\t\t\tptype->looks.premul = 1;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tCon_Printf(\"effectinfo type %s not supported\\n\", arg[1]);\n\t\t\t}\n\t\t}\n\t\telse if (!strcmp(arg[0], \"tex\") && args == 3)\n\t\t{\t//assume that all textures will be packed into the same texture and on the same line...\n\t\t\tint mini = atoi(arg[1]);\n\t\t\tint maxi = atoi(arg[2]);\n\t\t\tQ_strncpyz(ptype->texname, teximages[mini].imgname, sizeof(ptype->texname));\n\t\t\tptype->s1 = teximages[mini].tc[0];\n\t\t\tptype->s2 = teximages[mini].tc[1];\n\t\t\tptype->t1 = teximages[mini].tc[2];\n\t\t\tptype->t2 = teximages[mini].tc[3];\n\t\t\tptype->texsstride = teximages[(mini+1)&(sizeof(teximages)/sizeof(teximages[0])-1)].tc[0] - teximages[mini].tc[0];\n\t\t\tptype->randsmax = (maxi - mini);\n\t\t\tif (ptype->randsmax < 1)\n\t\t\t\tptype->randsmax = 1;\n\t\t}\n\t\telse if (!strcmp(arg[0], \"size\") && args == 3)\n\t\t{\n\t\t\tfloat s1 = atof(arg[1]), s2 = atof(arg[2]);\n\t\t\tptype->scale = s1;\n\t\t\tptype->scalerand = (s2-s1);\n\t\t}\n\t\telse if (!strcmp(arg[0], \"sizeincrease\") && args == 2)\n\t\t\tptype->scaledelta = atof(arg[1]);\n\t\telse if (!strcmp(arg[0], \"color\") && args == 3)\n\t\t{\n\t\t\tunsigned int rgb1 = strtoul(arg[1], NULL, 0), rgb2 = strtoul(arg[2], NULL, 0);\n\t\t\tint i;\n\t\t\tfor (i = 0; i < 3; i++)\n\t\t\t{\n\t\t\t\tptype->rgb[i] = ((rgb1>>(16-i*8)) & 0xff)/255.0;\n\t\t\t\tptype->rgbrand[i] = (int)(((rgb2>>(16-i*8)) & 0xff) - ((rgb1>>(16-i*8)) & 0xff))/255.0;\n\t\t\t\tptype->rgbrandsync[i] = 1;\n\t\t\t}\n\t\t}\n\t\telse if (!strcmp(arg[0], \"alpha\") && args == 4)\n\t\t{\n\t\t\tfloat a1 = atof(arg[1]), a2 = atof(arg[2]), f = atof(arg[3]);\n\t\t\tif (a1 > a2)\n\t\t\t{\t//backwards\n\t\t\t\tptype->alpha = a2/256;\n\t\t\t\tptype->alpharand = (a1-a2)/256;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tptype->alpha = a1/256;\n\t\t\t\tptype->alpharand = (a2-a1)/256;\n\t\t\t}\n\t\t\tptype->alphachange = -f/256;\n\t\t}\n\t\telse if (!strcmp(arg[0], \"velocityoffset\") && args == 4)\n\t\t{\t/*a 3d world-coord addition*/\n\t\t\tptype->velbias[0] = atof(arg[1]);\n\t\t\tptype->velbias[1] = atof(arg[2]);\n\t\t\tptype->velbias[2] = atof(arg[3]);\n\t\t}\n\t\telse if (!strcmp(arg[0], \"velocityjitter\") && args == 4)\n\t\t{\n\t\t\tptype->velwrand[0] = atof(arg[1]);\n\t\t\tptype->velwrand[1] = atof(arg[2]);\n\t\t\tptype->velwrand[2] = atof(arg[3]);\n\t\t}\n\t\telse if (!strcmp(arg[0], \"originoffset\") && args == 4)\n\t\t{\t/*a 3d world-coord addition*/\n\t\t\tptype->orgbias[0] = atof(arg[1]);\n\t\t\tptype->orgbias[1] = atof(arg[2]);\n\t\t\tptype->orgbias[2] = atof(arg[3]);\n\t\t}\n\t\telse if (!strcmp(arg[0], \"originjitter\") && args == 4)\n\t\t{\n\t\t\tptype->orgwrand[0] = atof(arg[1]);\n\t\t\tptype->orgwrand[1] = atof(arg[2]);\n\t\t\tptype->orgwrand[2] = atof(arg[3]);\n\t\t}\n\t\telse if (!strcmp(arg[0], \"gravity\") && args == 2)\n\t\t{\n\t\t\tptype->gravity = 800*atof(arg[1]);\n\t\t}\n\t\telse if (!strcmp(arg[0], \"bounce\") && args == 2)\n\t\t{\n\t\t\tptype->clipbounce = atof(arg[1]);\n\t\t\tif (ptype->clipbounce < 0)\n\t\t\t\tptype->cliptype = ptype - part_type;\n\t\t}\n\t\telse if (!strcmp(arg[0], \"airfriction\") && args == 2)\n\t\t\tptype->friction[2] = ptype->friction[1] = ptype->friction[0] = atof(arg[1]);\n\t\telse if (!strcmp(arg[0], \"liquidfriction\") && args == 2)\n\t\t\t;\n\t\telse if (!strcmp(arg[0], \"underwater\") && args == 1)\n\t\t\tptype->flags |= PT_TRUNDERWATER;\n\t\telse if (!strcmp(arg[0], \"notunderwater\") && args == 1)\n\t\t\tptype->flags |= PT_TROVERWATER;\n\t\telse if (!strcmp(arg[0], \"velocitymultiplier\") && args == 2)\n\t\t\tptype->veladd = atof(arg[1]);\n\t\telse if (!strcmp(arg[0], \"trailspacing\") && args == 2)\n\t\t{\n\t\t\tptype->countspacing = atof(arg[1]);\n\t\t\tptype->count = 1 / ptype->countspacing;\n\t\t}\n\t\telse if (!strcmp(arg[0], \"time\") && args == 3)\n\t\t{\n\t\t\tptype->die = atof(arg[1]);\n\t\t\tptype->randdie = atof(arg[2]) - ptype->die;\n\t\t\tif (ptype->randdie < 0)\n\t\t\t{\n\t\t\t\tptype->die = atof(arg[2]);\n\t\t\t\tptype->randdie = atof(arg[1]) - ptype->die;\n\t\t\t}\n\t\t}\n\t\telse if (!strcmp(arg[0], \"stretchfactor\") && args == 2)\n\t\t\tptype->looks.stretch = atof(arg[1]);\n\t\telse if (!strcmp(arg[0], \"blend\") && args == 2)\n\t\t{\n\t\t\tif (!strcmp(arg[1], \"invmod\"))\n\t\t\t{\n\t\t\t\tptype->looks.blendmode = BM_INVMODC;\n\t\t\t\tptype->looks.premul = 2;\n\t\t\t}\n\t\t\telse if (!strcmp(arg[1], \"alpha\"))\n\t\t\t{\n\t\t\t\tptype->looks.blendmode = BM_PREMUL;\n\t\t\t\tptype->looks.premul = 1;\n\t\t\t}\n\t\t\telse if (!strcmp(arg[1], \"add\"))\n\t\t\t{\n\t\t\t\tptype->looks.blendmode = BM_PREMUL;\n\t\t\t\tptype->looks.premul = 2;\n\t\t\t}\n\t\t\telse\n\t\t\t\tCon_Printf(\"effectinfo 'blend %s' not supported\\n\", arg[1]);\n\t\t}\n\t\telse if (!strcmp(arg[0], \"orientation\") && args == 2)\n\t\t{\n\t\t\tif (!strcmp(arg[1], \"billboard\"))\n\t\t\t\tptype->looks.type = PT_NORMAL;\n\t\t\telse if (!strcmp(arg[1], \"spark\"))\n\t\t\t\tptype->looks.type = PT_TEXTUREDSPARK;\n\t\t\telse if (!strcmp(arg[1], \"oriented\"))\t//FIXME: not sure this points the right way. also, its double-sided in dp.\n\t\t\t{\n\t\t\t\tif (ptype->looks.type != PT_CDECAL)\n\t\t\t\t\tptype->looks.type = PT_UDECAL;\n\t\t\t}\n\t\t\telse if (!strcmp(arg[1], \"beam\"))\n\t\t\t\tptype->looks.type = PT_BEAM;\n\t\t\telse\n\t\t\t\tCon_Printf(\"effectinfo 'orientation %s' not supported\\n\", arg[1]);\n\t\t}\n\t\telse if (!strcmp(arg[0], \"lightradius\") && args == 2)\n\t\t{\n\t\t\tptype->dl_radius[0] = atof(arg[1]);\n\t\t\tptype->dl_radius[1] = 0;\n\t\t}\n\t\telse if (!strcmp(arg[0], \"lightradiusfade\") && args == 2)\n\t\t\tptype->dl_decay[3] = atof(arg[1]);\n\t\telse if (!strcmp(arg[0], \"lightcolor\") && args == 4)\n\t\t{\n\t\t\tptype->dl_rgb[0] = atof(arg[1]);\n\t\t\tptype->dl_rgb[1] = atof(arg[2]);\n\t\t\tptype->dl_rgb[2] = atof(arg[3]);\n\t\t}\n\t\telse if (!strcmp(arg[0], \"lighttime\") && args == 2)\n\t\t\tptype->dl_time = atof(arg[1]);\n\t\telse if (!strcmp(arg[0], \"lightshadow\") && args == 2)\n\t\t\tptype->flags = (ptype->flags & ~PT_NODLSHADOW) | (!atoi(arg[1])?PT_NODLSHADOW:0);\n\t\telse if (!strcmp(arg[0], \"lightcubemapnum\") && args == 2)\n\t\t\tptype->dl_cubemapnum = atoi(arg[1]);\n\t\telse if (!strcmp(arg[0], \"lightcorona\") && args == 3)\n\t\t{\n\t\t\tptype->dl_corona_intensity = atof(arg[1])*0.25;\t//dp scales them by 0.25\n\t\t\tptype->dl_corona_scale = atof(arg[2]);\n\t\t}\n#if 1\n\t\telse if (!strcmp(arg[0], \"staincolor\") && args == 3)\t//stainmaps multiplier\n\t\t\tCon_ThrottlePrintf(&printtimer, 1, \"%s.%s: Particle effect token %s not supported\\n\", *ptype->config?ptype->config:\"<NONE>\", ptype->name,arg[0]);\n\t\telse if (!strcmp(arg[0], \"stainalpha\") && args == 3)\t//affects stainmaps AND stain-decals.\n\t\t\tCon_ThrottlePrintf(&printtimer, 1, \"%s.%s: Particle effect token %s not supported\\n\", *ptype->config?ptype->config:\"<NONE>\", ptype->name,arg[0]);\n\t\telse if (!strcmp(arg[0], \"stainsize\") && args == 3)\t\t//affects stainmaps AND stain-decals.\n\t\t\tCon_ThrottlePrintf(&printtimer, 1, \"%s.%s: Particle effect token %s not supported\\n\", *ptype->config?ptype->config:\"<NONE>\", ptype->name,arg[0]);\n\t\telse if (!strcmp(arg[0], \"staintex\") && args == 3)\t\t//actually spawns a decal\n\t\t\tCon_ThrottlePrintf(&printtimer, 1, \"%s.%s: Particle effect token %s not supported\\n\", *ptype->config?ptype->config:\"<NONE>\", ptype->name,arg[0]);\n\t\telse if (!strcmp(arg[0], \"stainless\") && args == 2)\n\t\t\tCon_ThrottlePrintf(&printtimer, 1, \"%s.%s: Particle effect token %s not supported\\n\", *ptype->config?ptype->config:\"<NONE>\", ptype->name,arg[0]);\n\t\telse if (!strcmp(arg[0], \"relativeoriginoffset\") && args == 4)\n\t\t\tCon_ThrottlePrintf(&printtimer, 1, \"%s.%s: Particle effect token %s not supported\\n\", *ptype->config?ptype->config:\"<NONE>\", ptype->name,arg[0]);\n\t\telse if (!strcmp(arg[0], \"relativevelocityoffset\") && args == 4)\n\t\t\tCon_ThrottlePrintf(&printtimer, 1, \"%s.%s: Particle effect token %s not supported\\n\", *ptype->config?ptype->config:\"<NONE>\", ptype->name, arg[0]);\n#endif\n\t\telse if (!strcmp(arg[0], \"rotate\") && args == 5)\n\t\t{\n\t\t\tptype->rotationstartmin\t\t= atof(arg[1]);\n\t\t\tptype->rotationstartrand\t= atof(arg[2]) - ptype->rotationstartmin;\n\t\t\tptype->rotationmin\t\t\t= atof(arg[3]);\n\t\t\tptype->rotationrand\t\t\t= atof(arg[4]) - ptype->rotationmin;\n\t\t\tptype->rotationstartmin\t\t*= M_PI/180;\n\t\t\tptype->rotationstartrand\t*= M_PI/180;\n\t\t\tptype->rotationmin\t\t\t*= M_PI/180;\n\t\t\tptype->rotationrand\t\t\t*= M_PI/180;\n\t\t\tptype->rotationstartmin += M_PI/4;\n\t\t}\n\t\telse\n\t\t\tCon_ThrottlePrintf(&printtimer, 0, \"%s.%s: Particle effect token not recognised, or invalid args: %s %s %s %s %s %s\\n\", *ptype->config?ptype->config:\"<NONE>\", ptype->name, arg[0], args<2?\"\":arg[1], args<3?\"\":arg[2], args<4?\"\":arg[3], args<5?\"\":arg[4], args<6?\"\":arg[5]);\n\t\targs = 0;\n\t}\n\n\tif (ptype)\n\t\tFinishEffectinfoParticleType(ptype, blooddecalonimpact);\n\n\tr_plooksdirty = true;\n}\n\nstatic qboolean P_ImportEffectInfo_Name(char *config)\n{\n\tchar *file;\n\n\tFS_LoadFile(va(\"%s.txt\", config), (void**)&file);\n\n\tif (!file)\n\t{\n\t\tCon_Printf(\"%s not found\\n\", config);\n\t\treturn false;\n\t}\n\tP_ImportEffectInfo(config, file);\n\tFS_FreeFile(file);\n\treturn true;\n}\nstatic void P_ConvertEffectInfo_f(void)\n{\n\tint i, assoc, n;\n\tvfsfile_t *outf;\n\tchar effect[1024];\n\tchar fname[64] = \"particles/effectinfo.cfg\";\n\n\tR_Particles_KillAllEffects();\n\tP_ImportEffectInfo_Name(\"effectinfo\");\n\n\tFS_CreatePath(\"particles/\", FS_GAMEONLY);\n\toutf = FS_OpenVFS(fname, \"wb\", FS_GAMEONLY);\n\tif (!outf)\n\t{\n\t\tFS_DisplayPath(fname, FS_GAMEONLY, effect, sizeof(effect));\n\t\tCon_TPrintf(\"Unable to open file %s\\n\", effect);\n\t\treturn;\n\t}\n\tfor (i = 0; i < numparticletypes; i++)\n\t{\n\t\tpart_type_t *ptype = &part_type[i];\n\t\tif (strcmp(ptype->config, \"effectinfo\"))\n\t\t\tcontinue;\t//skip any which are not relevant.\n\n\t\tif (strchr(ptype->name, '+'))\n\t\t\tcontinue;\t//skip assoc chains\n\t\tQ_strncpyz(effect, ptype->name, sizeof(effect));\n\n\t\tassoc = i;\n\t\tfor(;;)\n\t\t{\n\t\t\tn = part_type[assoc].assoc;\n\t\t\tpart_type[assoc].assoc = P_INVALID;\n\t\t\tVFS_PUTS(outf, \"r_part \");\n\t\t\tVFS_PUTS(outf, effect);\n\t\t\tVFS_PUTS(outf, \"\\n{\\n\");\n\t\t\tPScript_Query(assoc, 1, effect, sizeof(effect));\n\t\t\tVFS_PUTS(outf, effect);\n\t\t\tVFS_PUTS(outf, \"}\\n\");\n\t\t\tpart_type[assoc].assoc = n;\n\t\t\tassoc = n;\n\n\t\t\tif (assoc == P_INVALID)\n\t\t\t\tbreak;\n\n\t\t\tif (strchr(part_type[assoc].name, '+'))\n\t\t\t\tQ_snprintfz(effect, sizeof(effect), \"+%s\", part_type[i].name);\n\t\t}\n\t}\n\tVFS_CLOSE(outf);\n\n\tFS_DisplayPath(fname, FS_GAMEONLY, effect, sizeof(effect));\n\tCon_Printf(\"Written %s\\n\", effect);\n}\n#endif\n\n/*\n===============\nR_InitParticles\n===============\n*/\nstatic qboolean PScript_InitParticles (void)\n{\n\tint\t\ti;\n\n\tif (r_numparticles)\t//already inited\n\t\treturn true;\n\n\tbuildsintable();\n\n\tr_numparticles = bound(1, r_part_maxparticles.ival, MAX_PARTICLES);\n\tr_numdecals = bound(1, r_part_maxdecals.ival, MAX_DECALS);\n\n\tr_numbeams = MAX_BEAMSEGS;\n\tr_numtrailstates = MAX_TRAILSTATES;\n\n\tparticles = (particle_t *)\n\t\t\tBZ_Malloc (r_numparticles * sizeof(particle_t));\n\n\tbeams = (beamseg_t *)\n\t\t\tBZ_Malloc (r_numbeams * sizeof(beamseg_t));\n\n\tdecals = (clippeddecal_t *)\n\t\t\tBZ_Malloc (r_numdecals * sizeof(clippeddecal_t));\n\n\ttrailstates = (trailstate_t *)\n\t\t\tBZ_Malloc (r_numtrailstates * sizeof(trailstate_t));\n\tmemset(trailstates, 0, r_numtrailstates * sizeof(trailstate_t));\n\tts_cycle = 0;\n\n\tCmd_AddCommand(\"pointfile\", P_ReadPointFile_f);\t//load the leak info produced from qbsp into the particle system to show a line. :)\n\n\tpe_script_enabled = true;\n\n#ifdef HAVE_LEGACY\n\tCmd_AddCommand(\"r_exportbuiltinparticles\", P_ExportBuiltinSet_f);\n\tCmd_AddCommandD(\"r_converteffectinfo\", P_ConvertEffectInfo_f, \"Attempt to convert particle effects made for a certain other engine.\");\n\tCmd_AddCommand(\"r_exportalleffects\", P_ExportAllEffects_f);\n#endif\n\n//#if _DEBUG\n\tCmd_AddCommand(\"r_partinfo\", P_PartInfo_f);\n\tCmd_AddCommand(\"r_beaminfo\", P_BeamInfo_f);\n//#endif\n\n\tCvar_Hook(&r_particledesc, R_ParticleDesc_Callback);\n\tCvar_ForceCallback(&r_particledesc);\n\n\n\tfor (i = 0; i < (BUFFERVERTS>>2)*6; i += 6)\n\t{\n\t\tpscriptquadindexes[i+0] = ((i/6)<<2)+0;\n\t\tpscriptquadindexes[i+1] = ((i/6)<<2)+1;\n\t\tpscriptquadindexes[i+2] = ((i/6)<<2)+2;\n\t\tpscriptquadindexes[i+3] = ((i/6)<<2)+0;\n\t\tpscriptquadindexes[i+4] = ((i/6)<<2)+2;\n\t\tpscriptquadindexes[i+5] = ((i/6)<<2)+3;\n\t}\n\tpscriptmesh.xyz_array = pscriptverts;\n\tpscriptmesh.st_array = pscripttexcoords;\n\tpscriptmesh.colors4f_array[0] = pscriptcolours;\n\tpscriptmesh.indexes = pscriptquadindexes;\n\tfor (i = 0; i < BUFFERVERTS; i++)\n\t{\n\t\tpscripttriindexes[i] = i;\n\t}\n\tpscripttmesh.xyz_array = pscriptverts;\n\tpscripttmesh.st_array = pscripttexcoords;\n\tpscripttmesh.colors4f_array[0] = pscriptcolours;\n\tpscripttmesh.indexes = pscripttriindexes;\n\treturn true;\n}\n\nstatic void PScript_Shutdown (void)\n{\n\tpe_script_enabled = false;\n\n\tif (fallback)\n\t\tfallback->ShutdownParticles();\n\n\tCvar_Unhook(&r_particledesc);\n\n\tCmd_RemoveCommand(\"pointfile\");\t//load the leak info produced from qbsp into the particle system to show a line. :)\n\n\t\n\tCmd_RemoveCommand(\"r_converteffectinfo\");\n\tCmd_RemoveCommand(\"r_exportalleffects\");\n\tCmd_RemoveCommand(\"r_exportbuiltinparticles\");\n\tCmd_RemoveCommand(\"r_importeffectinfo\");\n\n//#if _DEBUG\n\tCmd_RemoveCommand(\"r_partinfo\");\n\tCmd_RemoveCommand(\"r_beaminfo\");\n//#endif\n\n\tpe_default\t\t\t= P_INVALID;\n\tpe_size2\t\t\t= P_INVALID;\n\tpe_size3\t\t\t= P_INVALID;\n\tpe_defaulttrail\t\t= P_INVALID;\n\n\twhile(loadedconfigs)\n\t{\n\t\tpcfg_t *cfg;\n\t\tcfg = loadedconfigs;\n\t\tloadedconfigs = cfg->next;\n\t\tZ_Free(cfg);\n\t}\n\n\twhile (numparticletypes > 0)\n\t{\n\t\tnumparticletypes--;\n\t\tif (part_type[numparticletypes].models)\n\t\t\tBZ_Free(part_type[numparticletypes].models);\n\t\tif (part_type[numparticletypes].sounds)\n\t\t\tBZ_Free(part_type[numparticletypes].sounds);\n\t\tif (part_type[numparticletypes].ramp)\n\t\t\tBZ_Free(part_type[numparticletypes].ramp);\n\t}\n\tBZ_Free (part_type);\n\tpart_type = NULL;\n\tpart_run_list = NULL;\n\tfallback = NULL;\n\n\tBZ_Free (particles);\n\tBZ_Free (beams);\n\tBZ_Free (decals);\n\tBZ_Free (trailstates);\n\n\tr_numparticles = 0;\n}\n\n\n/*\n===============\nP_ClearParticles\n===============\n*/\nstatic void PScript_ClearParticles (void)\n{\n\tint\t\ti;\n\n\tif (fallback)\n\t\tfallback->ClearParticles();\n\n\tfree_particles = &particles[0];\n\tfor (i=0 ;i<r_numparticles ; i++)\n\t\tparticles[i].next = &particles[i+1];\n\tparticles[r_numparticles-1].next = NULL;\n\n\tfree_decals = &decals[0];\n\tfor (i=0 ;i<r_numdecals ; i++)\n\t\tdecals[i].next = &decals[i+1];\n\tdecals[r_numdecals-1].next = NULL;\n\n\tfree_beams = &beams[0];\n\tfor (i=0 ;i<r_numbeams ; i++)\n\t{\n\t\tbeams[i].p = NULL;\n\t\tbeams[i].flags = BS_DEAD;\n\t\tbeams[i].next = &beams[i+1];\n\t}\n\tbeams[r_numbeams-1].next = NULL;\n\n\tparticletime = cl.time;\n\n\tfor (i = 0; i < numparticletypes; i++)\n\t{\n\t\tP_LoadTexture(&part_type[i], false);\n\t}\n\n\tfor (i = 0; i < numparticletypes; i++)\n\t{\n\t\tpart_type[i].clippeddecals = NULL;\n\t\tpart_type[i].particles = NULL;\n\t\tpart_type[i].beams = NULL;\n\t}\n}\n\n#ifdef HAVE_LEGACY\nstatic void P_ExportBuiltinSet_f(void)\n{\n\tchar *efname = Cmd_Argv(1);\n\tchar *file = NULL;\n\tint i;\n\n\tif (!*efname)\n\t{\n\t\tCon_Printf(\"Please name the built in effect (faithful, spikeset, tsshaft, minimal or highfps)\\n\");\n\t\treturn;\n\t}\n\n\tfor (i = 0; partset_list[i].name; i++)\n\t{\n\t\tif (!stricmp(efname, partset_list[i].name))\n\t\t{\n\t\t\tfile = *partset_list[i].data;\n\t\t\tif (file)\n\t\t\t{\n\t\t\t\tCOM_WriteFile(va(\"particles/%s.cfg\", efname), FS_GAMEONLY, file, strlen(file));\n\t\t\t\tCon_Printf(\"Written particles/%s.cfg\\n\", efname);\n\t\t\t}\n\t\t\telse\n\t\t\t\tCon_Printf(\"nothing to export\\n\");\n\t\t\treturn;\n\t\t}\n\t}\n\n\tCon_Printf(\"'%s' is not a built in particle set\\n\", efname);\n}\n#endif\n\nstatic qboolean P_LoadParticleSet(char *name, qboolean implicit, qboolean showwarning)\n{\n\tchar *file;\n\tint i;\n\tint restrictlevel = Cmd_FromGamecode() ? RESTRICT_SERVER : RESTRICT_LOCAL;\n\tpcfg_t *cfg;\n\n\tif (!*name)\n\t\treturn false;\n\n\t//protect against configs being loaded multiple times. this can easily happen with namespaces (especially if an effect is missing).\n\tfor (cfg = loadedconfigs; cfg; cfg = cfg->next)\n\t{\n\t\t//already loaded?\n\t\tif (!strcmp(cfg->name, name))\n\t\t\treturn false;\n\t}\n\tcfg = Z_Malloc(sizeof(*cfg) + strlen(name));\n\tstrcpy(cfg->name, name);\n\tcfg->next = loadedconfigs;\n\tloadedconfigs = cfg;\n\tname = cfg->name;\n\n#ifdef PSET_CLASSIC\n\tif (!strcmp(name, \"classic\"))\n\t{\n\t\tif (fallback)\n\t\t\tfallback->ShutdownParticles();\n\t\tfallback = &pe_classic;\n\t\tif (fallback)\n\t\t{\n\t\t\tfallback->InitParticles();\n\t\t\tfallback->ClearParticles();\n\t\t}\n\t\treturn true;\n\t}\n#endif\n\n\t//built-in particle effects are always favoured. This is annoying, but means the cvar value alone is enough to detect cheats.\n\tfor (i = 0; partset_list[i].name; i++)\n\t{\n\t\tif (!stricmp(name, partset_list[i].name))\n\t\t{\n\t\t\tif (partset_list[i].data)\n\t\t\t{\n\t\t\t\tCbuf_AddText(va(\"\\nr_part namespace %s %i\\n\", name, implicit), restrictlevel);\n\t\t\t\tCbuf_AddText(*partset_list[i].data, restrictlevel);\n\t\t\t\tCbuf_AddText(\"\\nr_part namespace \\\"\\\" 0\\n\", restrictlevel);\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\t}\n\n\tif (!strncmp(name, \"./\", 2))\n\t\tFS_LoadFile(va(\"%s.cfg\", name+2), (void**)&file);\n\telse\n\t\tFS_LoadFile(va(\"particles/%s.cfg\", name), (void**)&file);\n\tif (!file && strchr(name, '/'))\n\t\tFS_LoadFile(va(\"%s.cfg\", name), (void**)&file);\n\tif (file)\n\t{\n\t\tCbuf_AddText(va(\"\\nr_part namespace %s %i\\n\", name, implicit), restrictlevel);\n\t\tCbuf_AddText(file, restrictlevel);\n\t\tCbuf_AddText(\"\\nr_part namespace \\\"\\\" 0\\n\", restrictlevel);\n\t\tFS_FreeFile(file);\n\t}\n\telse\n\t{\n#ifdef HAVE_LEGACY\n\t\tif (!strcmp(name, \"effectinfo\"))\n\t\t{\n\t\t\t//FIXME: we're loading this too early to deal with per-map stuff.\n\t\t\t//FIXME: wait until after particle precache info has been received, and only reload if the loaded configs actually changed.\n\t\t\tP_ImportEffectInfo_Name(name);\n\t\t\treturn true;\n\t\t}\n#endif\n\t\tif (showwarning)\n\t\t\tCon_Printf(CON_WARNING \"Couldn't find particle description %s\\n\", name);\n\t\treturn false;\n\t}\n\treturn true;\n}\n\nstatic void R_Particles_KillAllEffects(void)\n{\n\tint i;\n\tpcfg_t *cfg;\n\n\tfor (i = 0; i < numparticletypes; i++)\n\t{\n\t\t*part_type[i].texname = '\\0';\n\t\tpart_type[i].scale = 0;\n\t\tpart_type[i].loaded = 0;\n\t\tif (part_type->ramp)\n\t\t\tBZ_Free(part_type->ramp);\n\t\tpart_type->ramp = NULL;\n\t}\n//\tnumparticletypes = 0;\n//\tBZ_Free(part_type);\n//\tpart_type = NULL;\n\n\tf_modified_particles = false;\n\n\tif (fallback)\n\t{\n\t\tfallback->ShutdownParticles();\n\t\tfallback = NULL;\n\t}\n\n\twhile(loadedconfigs)\n\t{\n\t\tcfg = loadedconfigs;\n\t\tloadedconfigs = cfg->next;\n\t\tZ_Free(cfg);\n\t}\n}\n\nstatic void QDECL R_ParticleDesc_Callback(struct cvar_s *var, char *oldvalue)\n{\n\tchar token[256];\n\tint count;\n\tqboolean\t\tfailure;\n\n\tchar *c;\n\n\tif (qrenderer == QR_NONE)\n\t\treturn; // don't bother parsing early\n\n\tR_Particles_KillAllEffects();\n\n\tif (cls.state != ca_connected)\n\t{\n\t\tfailure = false;\n\t\tcount = 0;\n\t\tfor (c = COM_ParseStringSet(var->string, token, sizeof(token)); token[0]; c = COM_ParseStringSet(c, token, sizeof(token)))\n\t\t{\n\t\t\tif (*token)\n\t\t\t{\n\t\t\t\tif (!P_LoadParticleSet(token, false, false))\n\t\t\t\t\tfailure = true;\n\t\t\t\tcount++;\n\t\t\t}\n\t\t}\n\n\t\t//if we didn't manage to load any, make sure SOMETHING got loaded...\n#ifdef PSET_CLASSIC\n\t\tif (!count)\n\t\t\tP_LoadParticleSet(\"classic\", true, true);\n\t\telse\n#endif\n\t\t\tif (failure)\n\t\t\tP_LoadParticleSet(\"high\", true, true);\n\n\t\tif (cls.state && cl.model_name[1])\n\t\t{\n\t\t\t//per-map configs. because we can.\n\t\t\tmemcpy(token, \"map_\", 4);\n\t\t\tCOM_FileBase(cl.model_name[1], token+4, sizeof(token)-4);\n\t\t\tP_LoadParticleSet(token, false, false);\n\t\t}\n\t}\n\n//\tCbuf_AddText(\"r_effect\\n\", RESTRICT_LOCAL);\n\n\t//make sure nothing is stale.\n\tCL_RegisterParticles();\n}\n\nstatic void P_ReadPointFile_f (void)\n{\n\tvfsfile_t\t*f;\n\tvec3_t\torg;\n\t//int\t\tr; //unreferenced\n\tint\t\tc;\n\tchar\tname[MAX_OSPATH];\n\tchar line[1024];\n\tchar *s;\n\tint pt_pointfile;\n\tunsigned int spawned = 0, total = 0;\n\n\tCOM_StripExtension(cl.worldmodel->name, name, sizeof(name));\n\tstrcat(name, \".pts\");\n\n\tf = FS_OpenVFS(name, \"rb\", FS_GAME);\n\tif (!f)\n\t{\n\t\tCon_Printf (\"couldn't open %s\\n\", name);\n\t\treturn;\n\t}\n\n\tP_ClearParticles();\t//so overflows arn't as bad.\n\n\tCon_Printf (\"Reading %s...\\n\", name);\n\tc = 0;\n\tpt_pointfile\t\t= PScript_FindParticleType(\"PT_POINTFILE\");\n\n\tif (pt_pointfile == P_INVALID)\n\t{\n\t\tif (!fallback)\n\t\t{\n#ifdef PSET_CLASSIC\n\t\t\tfallback = &pe_classic;\n#endif\n\t\t\tif (fallback)\n\t\t\t{\n\t\t\t\tfallback->InitParticles();\n\t\t\t\tfallback->ClearParticles();\n\t\t\t}\n\t\t}\n\t}\n\twhile(VFS_GETS(f, line, sizeof(line)))\n\t{\n\t\ts = COM_Parse(line);\n\t\torg[0] = atof(com_token);\n\n\t\ts = COM_Parse(s);\n\t\tif (!s)\n\t\t\tcontinue;\n\t\torg[1] = atof(com_token);\n\n\t\ts = COM_Parse(s);\n\t\tif (!s)\n\t\t\tcontinue;\n\t\torg[2] = atof(com_token);\n\t\tif (COM_Parse(s))\n\t\t\tcontinue;\n\n\t\tc++;\n\n\t\tif (c%8)\n\t\t\tcontinue;\n\n\t\ttotal++;\n\t\tif (pt_pointfile == P_INVALID)\n\t\t{\n#ifdef PSET_CLASSIC\n\t\t\tspawned += PClassic_PointFile(c, org);\n#endif\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (free_particles)\n\t\t\t\tspawned+= 0<P_RunParticleEffectType(org, NULL, 1, pt_pointfile);\n\t\t}\n\t}\n\n\tVFS_CLOSE (f);\n\tCon_Printf (\"spawned %i of %i points\\n\", spawned, c);\n}\n\nvoid PScript_ClearSurfaceParticles(model_t *mod)\n{\n\tmod->skytime = 0;\n\tmod->skytris = NULL;\n\twhile(mod->skytrimem)\n\t{\n\t\tvoid *f = mod->skytrimem;\n\t\tmod->skytrimem = mod->skytrimem->next;\n\t\tZ_Free(f);\n\t}\n\tmod->skytime = 0;\n\tmod->engineflags |= MDLF_RECALCULATERAIN;\n}\n\nstatic void R_Part_SkyTri(model_t *mod, float *v1, float *v2, float *v3, msurface_t *surf, int ptype)\n{\n\tfloat dot;\n\tfloat xm;\n\tfloat ym;\n\tfloat theta;\n\tvec3_t xd;\n\tvec3_t yd;\n\n\tskytris_t *st;\n\n\tskytriblock_t *mem = mod->skytrimem;\n\tif (!mem || mem->count >= sizeof(mem->tris)/sizeof(mem->tris[0]))\n\t{\n\t\tmod->skytrimem = BZ_Malloc(sizeof(*mod->skytrimem));\n\t\tmod->skytrimem->next = mem;\n\t\tmod->skytrimem->count = 0;\n\t\tmem = mod->skytrimem;\n\t}\n\n\tst = &mem->tris[mem->count];\n\tVectorCopy(v1, st->org);\n\tVectorSubtract(v2, st->org, st->x);\n\tVectorSubtract(v3, st->org, st->y);\n\n\tVectorCopy(st->x, xd);\n\tVectorCopy(st->y, yd);\n/*\n\txd[2] = 0;\t//prevent area from being valid on vertical surfaces\n\tyd[2] = 0;\n*/\n\txm = Length(xd);\n\tym = Length(yd);\n\n\tdot = DotProduct(xd, yd);\n\ttheta = acos(dot/(xm*ym));\n\tst->area = sin(theta)*xm*ym;\n\tst->nexttime = particletime;\n\tst->face = surf;\n\tst->ptype = ptype;\n\n\tif (st->area<=0)\n\t\treturn;//bummer.\n\n\tmem->count++;\n\tst->next = mod->skytris;\n\tmod->skytris = st;\n}\n\n\n\nstatic void PScript_EmitSkyEffectTris(model_t *mod, msurface_t \t*fa, int ptype)\n{\n\tvec3_t\t\tverts[64];\n\tint v1;\n\tint v2;\n\tint v3;\n\tint numverts;\n\tint i, lindex;\n\tfloat *vec;\n\n\tif (ptype < 0 || ptype >= numparticletypes)\n\t\treturn;\n\n\t//\n\t// convert edges back to a normal polygon\n\t//\n\tnumverts = 0;\n\tfor (i=0 ; i<fa->numedges ; i++)\n\t{\n\t\tlindex = mod->surfedges[fa->firstedge + i];\n\n\t\tif (lindex > 0)\n\t\t\tvec = mod->vertexes[mod->edges[lindex].v[0]].position;\n\t\telse\n\t\t\tvec = mod->vertexes[mod->edges[-lindex].v[1]].position;\n\t\tVectorCopy (vec, verts[numverts]);\n\t\tnumverts++;\n\n\t\tif (numverts>=64)\n\t\t{\n\t\t\tCon_Printf(\"Too many verts on sky surface\\n\");\n\t\t\treturn;\n\t\t}\n\t}\n\n\tv1 = 0;\n\tv2 = 1;\n\tfor (v3 = 2; v3 < numverts; v3++)\n\t{\n\t\tR_Part_SkyTri(mod, verts[v1], verts[v2], verts[v3], fa, ptype);\n\n\t\tv2 = v3;\n\t}\n}\nstatic void P_AddRainParticles(model_t *mod, vec3_t axis[3], vec3_t eorg, float contribution)\n{\n\tfloat x;\n\tfloat y;\n\tpart_type_t *type;\n\n\tvec3_t org, vdist, worg, wnorm;\n\n\tskytris_t *st;\n\tsize_t nc,oc;\n\tfloat ot;\n\tint area;\n\tint cluster;\n\tunsigned int contentbits;\n\n\tif (mod->engineflags & MDLF_RECALCULATERAIN)\n\t{\n\t\tPScript_ClearSurfaceParticles(mod);\n\t\tmod->engineflags &= ~MDLF_RECALCULATERAIN;\n\n\t\tif (mod->type == mod_brush)\n\t\t{\n\t\t\tint t, i;\n\t\t\tint ptype;\n\t\t\tmsurface_t *surf;\n\n\t\t\t/*particle emision based upon texture. this is lazy code*/\n\t\t\tfor (t = mod->numtextures-1; t >= 0; t--)\n\t\t\t{\n\t\t\t\t/*FIXME: we should read the shader for the particle names here*/\n\t\t\t\t/*FIXME: if the paticle system changes mid-map, we should be prepared to reload things*/\n\t\t\t\tchar *pn;\n\t\t\t\tchar *h;\n\t\t\t\tif (mod->textures[t]->partname)\n\t\t\t\t\tpn = mod->textures[t]->partname;\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tpn = va(\"tex_%s\", mod->textures[t]->name);\n\t\t\t\t\t//strip any trailing data after #, so textures can have shader args\n\t\t\t\t\th = strchr(pn, '#');\n\t\t\t\t\tif (h)\n\t\t\t\t\t\t*h = 0;\n\t\t\t\t}\n\t\t\t\tptype = P_FindParticleType(pn);\n\n\t\t\t\tif (ptype != P_INVALID)\n\t\t\t\t{\n\t\t\t\t\tfor (i=0; i<mod->nummodelsurfaces; i++)\n\t\t\t\t\t{\n\t\t\t\t\t\tsurf = mod->surfaces + i + mod->firstmodelsurface;\n\t\t\t\t\t\tif (surf->texinfo->texture == mod->textures[t])\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t/*FIXME: it would be a good idea to determine the surface's (midpoint) pvs cluster so that we're not spamming for the entire map*/\n\t\t\t\t\t\t\tPScript_EmitSkyEffectTris(mod, surf, ptype);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tif (!mod->skytris)\n\t\treturn;\n\n\tot = mod->skytime;\n\tmod->skytime += contribution;\n\n\tfor (st = mod->skytris; st; st = st->next)\n\t{\n\t\tif ((unsigned int)st->ptype >= (unsigned int)numparticletypes)\n\t\t\tcontinue;\n\t\ttype = &part_type[st->ptype];\n\t\tif (!type->loaded)\t//woo, batch skipping.\n\t\t\tcontinue;\n\n\t\tnc = (mod->skytime*st->area*r_part_rain_quantity.value*type->rainfrequency)/10000.0;\n\t\toc = (ot*st->area*r_part_rain_quantity.value*type->rainfrequency)/10000.0;\n\n\t\twhile (oc < nc)\n\t\t{\n\t\t\toc++;\n\t\t\tif (!free_particles)\n\t\t\t\treturn;\n\n//\t\t\tst->nexttime += 10000.0/(st->area*r_part_rain_quantity.value*type->rainfrequency);\n\n\t\t\tx = frandom()*frandom();\n\t\t\ty = frandom() * (1-x);\n\t\t\tVectorMA(st->org, x, st->x, org);\n\t\t\tVectorMA(org, y, st->y, org);\n\n\t\t\tworg[0] = DotProduct(org, axis[0]) + eorg[0];\n\t\t\tworg[1] = DotProduct(org, axis[1]) + eorg[1];\n\t\t\tworg[2] = DotProduct(org, axis[2]) + eorg[2];\n\n\t\t\t//ignore it if its too far away\n\t\t\tVectorSubtract(worg, r_refdef.vieworg, vdist);\n\t\t\tif (VectorLength(vdist) > (1024+512)*frandom())\n\t\t\t\tcontinue;\n\n\t\t\tif (cl.worldmodel->funcs.InfoForPoint)\n\t\t\t{\n\t\t\t\tcl.worldmodel->funcs.InfoForPoint(cl.worldmodel, worg, &area, &cluster, &contentbits);\n\t\t\t\tif (contentbits & FTECONTENTS_SOLID)\n\t\t\t\t\tcontinue;\n\t\t\t\tif (r_refdef.scenevis && !(r_refdef.scenevis[cluster>>3] & (1<<(cluster&7))))\n\t\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (st->face->flags & SURF_PLANEBACK)\n\t\t\t\tVectorScale(st->face->plane->normal, -1, vdist);\n\t\t\telse\n\t\t\t\tVectorCopy(st->face->plane->normal, vdist);\n\n\t\t\twnorm[0] = DotProduct(vdist, axis[0]);\n\t\t\twnorm[1] = DotProduct(vdist, axis[1]);\n\t\t\twnorm[2] = DotProduct(vdist, axis[2]);\n\n\t\t\tVectorMA(worg, 0.5, wnorm, worg);\n\n\t\t\tP_RunParticleEffectType(worg, wnorm, 1, st->ptype);\n\t\t}\n\t}\n}\n\n// Trailstate functions\nstatic void P_CleanTrailstate(trailstate_t *ts)\n{\n\t// clear LASTSEG flag from lastbeam so it can be reused\n\tif (ts->lastbeam)\n\t{\n\t\tts->lastbeam->flags &= ~BS_LASTSEG;\n\t\tts->lastbeam->flags |= BS_NODRAW;\n\t}\n\n\t// clean structure\n\tmemset(ts, 0, sizeof(trailstate_t));\n}\n\nstatic void PScript_DelinkTrailstate(trailkey_t *tk)\n{\n\ttrailkey_t key;\n\ttrailstate_t *ts;\n\n\tkey = *tk;\n\t*tk = 0;\n\n\twhile (key && key <= r_numtrailstates)\n\t{\n\t\tts = trailstates + (key - 1);\n\n\t\tif (ts->key != key)\n\t\t\tbreak; // prevent overwrite\n\n\t\tkey = ts->assoc; // next to clean\n\t\tP_CleanTrailstate(ts);\n\t}\n}\n\nstatic trailstate_t *P_NewTrailstate(void)\n{\n\ttrailstate_t *ts;\n\n\t// bounds check here in case r_numtrailstates changed\n\tif (ts_cycle >= r_numtrailstates)\n\t\tts_cycle = 0;\n\n\tts = trailstates + ts_cycle;\n\tP_CleanTrailstate(ts);\n\tts_cycle++;\n\tts->key = ts_cycle; // key is 1 above index, allows 0 to be invalid\n\n\treturn ts;\n}\n\nstatic trailstate_t* P_FetchTrailstate(trailkey_t* tk)\n{\n\ttrailstate_t* ts;\n\n\t// trailstate allocation/deallocation\n\tif (tk)\n\t{\n\t\ttrailkey_t key = *tk;\n\t\tif (key == 0 || key > r_numtrailstates)\n\t\t{\n\t\t\tts = P_NewTrailstate();\n\t\t\t*tk = ts->key;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tts = trailstates + (key - 1);\n\t\t\tif (ts->key != key) // trailstate was overwritten\n\t\t\t{\n\t\t\t\tts = P_NewTrailstate(); // so get a new one\n\t\t\t\t*tk = ts->key;\n\t\t\t}\n\t\t}\n\t}\n\telse\n\t\tts = NULL;\n\n\treturn ts;\n}\n\n#define NUMVERTEXNORMALS\t162\nstatic float\tr_avertexnormals[NUMVERTEXNORMALS][3] = {\n#include \"anorms.h\"\n};\nstatic vec2_t\tavelocities[NUMVERTEXNORMALS];\n#define BEAMLENGTH 16\n// vec3_t\tavelocity = {23, 7, 3};\n// float\tpartstep = 0.01;\n// float\ttimescale = 0.01;\n\n\n\nstatic void PScript_ApplyOrgVel(vec3_t oorg, vec3_t ovel, vec3_t eforg, vec3_t axis[3], int pno, int pmax, part_type_t *ptype)\n{\n\tvec3_t ofsvec, arsvec;\n\n\tfloat k,l,m;\n\tint spawnspc, i=pno, j;\n\n\n\tl=0;\n\tj=0;\n\tk=0;\n\tm=0;\n\n\tspawnspc = 8;\n\tswitch (ptype->spawnmode)\n\t{\n\tcase SM_UNICIRCLE:\n\t\tm = pmax;\n\t\tif (ptype->looks.type == PT_BEAM||ptype->looks.type == PT_VBEAM)\n\t\t\tm--;\n\n\t\tif (m < 1)\n\t\t\tm = 0;\n\t\telse\n\t\t\tm = (M_PI*2)/m;\n\n\t\tif (ptype->spawnparam1) /* use for weird shape hacks */\n\t\t\tm *= ptype->spawnparam1;\n\t\tbreak;\n\tcase SM_TELEBOX:\n\t\tspawnspc = 4;\n\t\tl = -ptype->areaspreadvert;\n\tcase SM_LAVASPLASH:\n\t\tj = k = -ptype->areaspread;\n\t\tif (ptype->spawnparam1)\n\t\t\tm = ptype->spawnparam1;\n\t\telse\n\t\t\tm = 0.55752; /* default weird number for tele/lavasplash used in vanilla Q1 */\n\n\t\tif (ptype->spawnparam2)\n\t\t\tspawnspc = (int)ptype->spawnparam2;\n\t\tbreak;\n\tcase SM_FIELD:\n\t\tif (!avelocities[0][0])\n\t\t{\n\t\t\tfor (j=0 ; j<NUMVERTEXNORMALS ; j++)\n\t\t\t{\n\t\t\t\tavelocities[j][0] = (rand()&255) * 0.01;\n\t\t\t\tavelocities[j][1] = (rand()&255) * 0.01;\n\t\t\t}\n\t\t}\n\n\t\tj = pno%NUMVERTEXNORMALS;\n\t\tm = pno/NUMVERTEXNORMALS;\n\t\tbreak;\n\tdefault:\t//others don't need intitialisation\n\t\tbreak;\n\t}\n\n\n\tovel[0] = 0;\n\tovel[1] = 0;\n\tovel[2] = 0;\n\n\t// handle spawn modes (org/vel)\n\tswitch (ptype->spawnmode)\n\t{\n\tcase SM_BOX:\n\t\tofsvec[0] = crandom();\n\t\tofsvec[1] = crandom();\n\t\tofsvec[2] = crandom();\n\n\t\tarsvec[0] = ofsvec[0]*ptype->areaspread;\n\t\tarsvec[1] = ofsvec[1]*ptype->areaspread;\n\t\tarsvec[2] = ofsvec[2]*ptype->areaspreadvert;\n\t\tbreak;\n\tcase SM_TELEBOX:\n\t\tofsvec[0] = k;\n\t\tofsvec[1] = j;\n\t\tofsvec[2] = l+4;\n\t\tVectorNormalize(ofsvec);\n\t\tVectorScale(ofsvec, 1.0-(frandom())*m, ofsvec);\n\n\t\t// org is just like the original\n\t\tarsvec[0] = j + (rand()%spawnspc);\n\t\tarsvec[1] = k + (rand()%spawnspc);\n\t\tarsvec[2] = l + (rand()%spawnspc);\n\n\t\t// advance telebox loop\n\t\tj += spawnspc;\n\t\tif (j >= ptype->areaspread)\n\t\t{\n\t\t\tj = -ptype->areaspread;\n\t\t\tk += spawnspc;\n\t\t\tif (k >= ptype->areaspread)\n\t\t\t{\n\t\t\t\tk = -ptype->areaspread;\n\t\t\t\tl += spawnspc;\n\t\t\t\tif (l >= ptype->areaspreadvert)\n\t\t\t\t\tl = -ptype->areaspreadvert;\n\t\t\t}\n\t\t}\n\t\tbreak;\n\tcase SM_LAVASPLASH:\n\t\t// calc directions, org with temp vector\n\t\tofsvec[0] = k + (rand()%spawnspc);\n\t\tofsvec[1] = j + (rand()%spawnspc);\n\t\tofsvec[2] = 256;\n\n\t\tarsvec[0] = ofsvec[0];\n\t\tarsvec[1] = ofsvec[1];\n\t\tarsvec[2] = frandom()*ptype->areaspreadvert;\n\n\t\tVectorNormalize(ofsvec);\n\t\tVectorScale(ofsvec, 1.0-(frandom())*m, ofsvec);\n\n\t\t// advance splash loop\n\t\tj += spawnspc;\n\t\tif (j >= ptype->areaspread)\n\t\t{\n\t\t\tj = -ptype->areaspread;\n\t\t\tk += spawnspc;\n\t\t\tif (k >= ptype->areaspread)\n\t\t\t\tk = -ptype->areaspread;\n\t\t}\n\t\tbreak;\n\tcase SM_UNICIRCLE:\n\t\tofsvec[0] = cos(m*i);\n\t\tofsvec[1] = sin(m*i);\n\t\tofsvec[2] = 0;\n\t\tVectorScale(ofsvec, ptype->areaspread, arsvec);\n\t\tbreak;\n\tcase SM_FIELD:\n\t\tarsvec[0] = (cl.time * avelocities[i][0]) + m;\n\t\tarsvec[1] = (cl.time * avelocities[i][1]) + m;\n\t\tarsvec[2] = cos(arsvec[1]);\n\n\t\tofsvec[0] = arsvec[2]*cos(arsvec[0]);\n\t\tofsvec[1] = arsvec[2]*sin(arsvec[0]);\n\t\tofsvec[2] = -sin(arsvec[1]);\n\n//\t\tarsvec[0] = r_avertexnormals[j][0]*ptype->areaspread + ofsvec[0]*BEAMLENGTH;\n//\t\tarsvec[1] = r_avertexnormals[j][1]*ptype->areaspread + ofsvec[1]*BEAMLENGTH;\n//\t\tarsvec[2] = r_avertexnormals[j][2]*ptype->areaspreadvert + ofsvec[2]*BEAMLENGTH;\n\n\t\tl = ptype->spawnparam2 * sin(cl.time+j+m);\n\t\tarsvec[0] = r_avertexnormals[j][0]*(ptype->areaspread+l) + ofsvec[0]*ptype->spawnparam1;\n\t\tarsvec[1] = r_avertexnormals[j][1]*(ptype->areaspread+l) + ofsvec[1]*ptype->spawnparam1;\n\t\tarsvec[2] = r_avertexnormals[j][2]*(ptype->areaspreadvert+l) + ofsvec[2]*ptype->spawnparam1;\n\n\t\tVectorNormalize(ofsvec);\n\n\t\tj++;\n\t\tif (j >= NUMVERTEXNORMALS)\n\t\t{\n\t\t\tj = 0;\n\t\t\tm += 0.1762891; // some BS number to try to \"randomize\" things\n\t\t}\n\t\tbreak;\n\tcase SM_DISTBALL:\n\t\t{\n\t\t\tfloat rdist;\n\n\t\t\trdist = ptype->spawnparam2 - crandom()*(1-(crandom() * ptype->spawnparam1));\n\n\t\t\t// this is a strange spawntype, which is based on the fact that\n\t\t\t// crandom()*crandom() provides something similar to an exponential\n\t\t\t// probability curve\n\t\t\tofsvec[0] = hrandom();\n\t\t\tofsvec[1] = hrandom();\n\t\t\tif (ptype->areaspreadvert)\n\t\t\t\tofsvec[2] = hrandom();\n\t\t\telse\n\t\t\t\tofsvec[2] = 0;\n\n\t\t\tVectorNormalize(ofsvec);\n\t\t\tVectorScale(ofsvec, rdist, ofsvec);\n\n\t\t\tarsvec[0] = ofsvec[0]*ptype->areaspread;\n\t\t\tarsvec[1] = ofsvec[1]*ptype->areaspread;\n\t\t\tarsvec[2] = ofsvec[2]*ptype->areaspreadvert;\n\t\t}\n\t\tbreak;\n\tdefault: // SM_BALL, SM_CIRCLE\n\t\tofsvec[0] = hrandom();\n\t\tofsvec[1] = hrandom();\n\t\tif (ptype->areaspreadvert)\n\t\t\tofsvec[2] = hrandom();\n\t\telse\n\t\t\tofsvec[2] = 0;\n\n\t\tVectorNormalize(ofsvec);\n\t\tif (ptype->spawnmode != SM_CIRCLE)\n\t\t\tVectorScale(ofsvec, frandom(), ofsvec);\n\n\t\tarsvec[0] = ofsvec[0]*ptype->areaspread;\n\t\tarsvec[1] = ofsvec[1]*ptype->areaspread;\n\t\tarsvec[2] = ofsvec[2]*ptype->areaspreadvert;\n\t\tbreak;\n\t}\n\n\tk = ptype->orgadd + frandom()*ptype->randomorgadd;\n\tl = ptype->veladd + frandom()*ptype->randomveladd;\n\n#if 1\n\tVectorMA(ovel, ofsvec[0]*ptype->spawnvel, axis[0], ovel);\n\tVectorMA(ovel, ofsvec[1]*ptype->spawnvel, axis[1], ovel);\n\tVectorMA(ovel, l+ofsvec[2]*ptype->spawnvelvert, axis[2], ovel);\n\n\tVectorMA(eforg, arsvec[0], axis[0], oorg);\n\tVectorMA(oorg, arsvec[1], axis[1], oorg);\n\tVectorMA(oorg, k+arsvec[2], axis[2], oorg);\n#else\n\toorg[0] = eforg[0] + arsvec[0];\n\toorg[1] = eforg[1] + arsvec[1];\n\toorg[2] = eforg[2] + arsvec[2];\n\n\t// apply arsvec+ofsvec\n\tif (efdir)\n\t{\n\t\tovel[0] += efdir[0]*l+ofsvec[0]*ptype->spawnvel;\n\t\tovel[1] += efdir[1]*l+ofsvec[1]*ptype->spawnvel;\n\t\tovel[2] += efdir[2]*l+ofsvec[2]*ptype->spawnvelvert;\n\n\t\toorg[0] += efdir[0]*k;\n\t\toorg[1] += efdir[1]*k;\n\t\toorg[2] += efdir[2]*k;\n\t}\n\telse\n\t{//efdir is effectively up - '0 0 -1'\n\t\tovel[0] += ofsvec[0]*ptype->spawnvel;\n\t\tovel[1] += ofsvec[1]*ptype->spawnvel;\n\t\tovel[2] += ofsvec[2]*ptype->spawnvelvert - l;\n\n\t\toorg[2] -= k;\n\t}\n#endif\n\n\tif (ptype->flags & PT_WORLDSPACERAND)\n\t{\n\t\tdo\n\t\t{\n\t\t\tofsvec[0] = crand();\n\t\t\tofsvec[1] = crand();\n\t\t\tofsvec[2] = crand();\n\t\t} while(DotProduct(ofsvec,ofsvec)>1);\t//crap, but I'm trying to mimic dp\n\t\toorg[0] += ofsvec[0] * ptype->orgwrand[0];\n\t\toorg[1] += ofsvec[1] * ptype->orgwrand[1];\n\t\toorg[2] += ofsvec[2] * ptype->orgwrand[2];\n\t\tovel[0] += ofsvec[0] * ptype->velwrand[0];\n\t\tovel[1] += ofsvec[1] * ptype->velwrand[1];\n\t\tovel[2] += ofsvec[2] * ptype->velwrand[2];\n\t\tVectorAdd(ovel, ptype->velbias, ovel);\n\t}\n\n\tVectorAdd(oorg, ptype->orgbias, oorg);\n}\n\nstatic void PScript_EffectSpawned(part_type_t *ptype, vec3_t org, vec3_t axis[3], int dlkey, float countscale)\n{\n\textern cvar_t r_lightflicker;\n\tif (ptype->dl_radius[0] || ptype->dl_radius[1])// && r_rocketlight.value)\n\t{\n\t\tfloat radius;\n\t\tdlight_t *dl;\n\n\t\tstatic int flickertime;\n\t\tstatic int flicker;\n\t\tint i = realtime*20;\n\t\tif (flickertime != i)\n\t\t{\n\t\t\tflickertime = i;\n\t\t\tflicker = rand();\n\t\t}\n\t\tradius = ptype->dl_radius[0] + (r_lightflicker.ival?((flicker + dlkey*2000)&0xffff)*(1.0f/0xffff):0.5)*ptype->dl_radius[1];\n\n\t\tdl = CL_NewDlight(dlkey, org, radius, ptype->dl_time, ptype->dl_rgb[0], ptype->dl_rgb[1], ptype->dl_rgb[2]);\n\t\tdl->channelfade[0]\t= ptype->dl_decay[0];\n\t\tdl->channelfade[1]\t= ptype->dl_decay[1];\n\t\tdl->channelfade[2]\t= ptype->dl_decay[2];\n\t\tdl->decay\t\t\t= ptype->dl_decay[3];\n\t\tdl->corona\t\t\t= ptype->dl_corona_intensity;\n\t\tdl->coronascale\t\t= ptype->dl_corona_scale;\n#ifdef RTLIGHTS\n\t\tdl->lightcolourscales[0] = ptype->dl_scales[0];\n\t\tdl->lightcolourscales[1] = ptype->dl_scales[1];\n\t\tdl->lightcolourscales[2] = ptype->dl_scales[2];\n#endif\n\t\tif (ptype->flags & PT_NODLSHADOW)\n\t\t\tdl->flags |= LFLAG_NOSHADOWS;\n\t\tif (ptype->dl_cubemapnum)\n\t\t\tQ_snprintfz(dl->cubemapname, sizeof(dl->cubemapname), \"cubemaps/%i\", ptype->dl_cubemapnum);\n\t\tdl->style = ptype->dl_lightstyle;\n\t}\n\tif (ptype->numsounds)\n\t{\n\t\tint i;\n\t\tfloat w,tw;\n\t\tfor (i = 0, tw = 0; i < ptype->numsounds; i++)\n\t\t\ttw += ptype->sounds[i].weight;\n\t\tw = frandom() * tw;\t//select the sound by weight\n\t\t//and figure out which one that weight corresponds to\n\t\tfor (i = 0, tw = 0; i < ptype->numsounds; i++)\n\t\t{\n\t\t\ttw += ptype->sounds[i].weight;\n\t\t\tif (w <= tw)\n\t\t\t{\n\t\t\t\tif (*ptype->sounds[i].name && ptype->sounds[i].vol > 0)\n\t\t\t\t\tS_StartSound(0, 0, S_PrecacheSound(ptype->sounds[i].name), org, NULL, ptype->sounds[i].vol, ptype->sounds[i].atten, -ptype->sounds[i].delay, ptype->sounds[i].pitch, 0);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\tif (ptype->stain_radius)\n\t\tSurf_AddStain(org, ptype->stain_rgb[0], ptype->stain_rgb[1], ptype->stain_rgb[2], ptype->stain_radius);\n}\n\ntypedef struct\n{\n\tpart_type_t *ptype;\n\tint entity;\n\tmodel_t *model;\n\tvec3_t center;\n\tvec3_t normal;\n\tvec3_t tangent1;\n\tvec3_t tangent2;\n\n\tfloat scale0;\n\tfloat scale1;\n\tfloat scale2;\n\n\tfloat bias1;\n\tfloat bias2;\n} decalctx_t;\nstatic void PScript_AddDecals(void *vctx, vec3_t *fte_restrict points, size_t numtris, shader_t *surfshader)\n{\n\tdecalctx_t *ctx = vctx;\n\tpart_type_t *ptype = ctx->ptype;\n\tclippeddecal_t *d;\n\tunsigned int i;\n\tvec3_t vec;\n\twhile(numtris-->0)\n\t{\n\t\tif (!free_decals)\n\t\t\tbreak;\n\n\t\td = free_decals;\n\t\tfree_decals = d->next;\n\t\td->next = ptype->clippeddecals;\n\t\tptype->clippeddecals = d;\n\n\t\tfor (i = 0; i < 3; i++)\n\t\t{\n\t\t\tVectorCopy(points[i], d->vertex[i]);\n\t\t\tVectorSubtract(d->vertex[i], ctx->center, vec);\n\t\t\td->texcoords[i][0] = (DotProduct(vec, ctx->tangent1)*ctx->scale1)+ctx->bias1;\n\t\t\td->texcoords[i][1] = (DotProduct(vec, ctx->tangent2)*ctx->scale2)+ctx->bias2;\n\t\t\tif (r_decal_noperpendicular.ival)\n\t\t\t{\n\t\t\t\t//the decal code is already making sure the surfaces are mostly aligned, which should solve some issues.\n\t\t\t\t//this means we can make sure that there's NO fading at all, so no issues if the center of the effect is not actually aligned with any surface (yay inprecision).\n\t\t\t\td->valpha[i] = 1;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t//fade the alpha depending on the distance from the center)\n\t\t\t\t//FIXME: should be fabsed by glsl so that linear interpolation works correctly\n\t\t\t\td->valpha[i] = 1 - fabs((DotProduct(vec, ctx->normal)*ctx->scale0));\n\t\t\t}\n\t\t}\n\t\tpoints += 3;\n\n\t\td->entity = ctx->entity;\n//\t\td->model = ctx->model;\n\t\td->die = ptype->randdie*frandom();\n\n\t\tif (ptype->die)\n\t\t\td->rgba[3] = ptype->alpha + d->die*ptype->alphachange;\n\t\telse\n\t\t\td->rgba[3] = ptype->alpha;\n\t\td->rgba[3] += ptype->alpharand*frandom();\n\n\t\tif (ptype->colorindex >= 0)\n\t\t{\n\t\t\tint cidx;\n\t\t\tcidx = ptype->colorrand > 0 ? rand() % ptype->colorrand : 0;\n\t\t\tcidx = ptype->colorindex + cidx;\n\t\t\tif (cidx > 255)\n\t\t\t\td->rgba[3] = d->rgba[3] / 2; // Hexen 2 style transparency\n\t\t\tcidx = (cidx & 0xff) * 3;\n\t\t\td->rgba[0] = host_basepal[cidx] * (1/255.0);\n\t\t\td->rgba[1] = host_basepal[cidx+1] * (1/255.0);\n\t\t\td->rgba[2] = host_basepal[cidx+2] * (1/255.0);\n\t\t}\n\t\telse\n\t\t\tVectorCopy(ptype->rgb, d->rgba);\n\n\t\tvec[2] = frandom();\n\t\tvec[0] = vec[2]*ptype->rgbrandsync[0] + frandom()*(1-ptype->rgbrandsync[0]);\n\t\tvec[1] = vec[2]*ptype->rgbrandsync[1] + frandom()*(1-ptype->rgbrandsync[1]);\n\t\tvec[2] = vec[2]*ptype->rgbrandsync[2] + frandom()*(1-ptype->rgbrandsync[2]);\n\t\td->rgba[0] += vec[0]*ptype->rgbrand[0] + ptype->rgbchange[0]*d->die;\n\t\td->rgba[1] += vec[1]*ptype->rgbrand[1] + ptype->rgbchange[1]*d->die;\n\t\td->rgba[2] += vec[2]*ptype->rgbrand[2] + ptype->rgbchange[2]*d->die;\n\n\t\td->die = particletime + ptype->die - d->die;\n\n\t\tif (ptype->looks.type != PT_CDECAL)\n\t\t\td->die += 20;\n\n\t\t// maintain run list\n\t\tif (!(ptype->state & PS_INRUNLIST))\n\t\t{\n\t\t\tptype->runlink = &part_run_list;\n\t\t\tptype->nexttorun = part_run_list;\n\t\t\tif (part_run_list)\n\t\t\t\tpart_run_list->runlink = &ptype->nexttorun;\n\t\t\t*ptype->runlink = ptype;\n\t\t\tptype->state |= PS_INRUNLIST;\n\t\t}\n\t}\n}\n\nstatic int PScript_RunParticleEffectState (vec3_t org, vec3_t dir, float count, int typenum, trailkey_t *tk)\n{\n\tpart_type_t *ptype = &part_type[typenum];\n\tint i, j, k, l, spawnspc;\n\tfloat m, pcount;//, orgadd, veladd;\n\tvec3_t axis[3]={{1,0,0},{0,1,0},{0,0,-1}};\n\tparticle_t\t*p;\n\tbeamseg_t *b, *bfirst;\n\tvec3_t ofsvec, arsvec; // offsetspread vec, areaspread vec\n\n\tfloat orgadd, veladd;\n\ttrailstate_t *ts;\n\n\tif (typenum >= FALLBACKBIAS && fallback)\n\t\treturn fallback->RunParticleEffectState(org, dir, count, typenum-FALLBACKBIAS, NULL);\n\n\tif (typenum < 0 || typenum >= numparticletypes)\n\t\treturn 1;\n\n\tif (!ptype->loaded)\n\t\treturn 1;\n\n\t// inwater check, switch only once\n\tif (r_part_contentswitch.ival && ptype->inwater >= 0 && cl.worldmodel && cl.worldmodel->loadstate == MLS_LOADED)\n\t{\n\t\tint cont;\n\t\tcont = cl.worldmodel->funcs.PointContents(cl.worldmodel, NULL, org);\n\n\t\tif (cont & FTECONTENTS_FLUID)\n\t\t\tptype = &part_type[ptype->inwater];\n\t}\n\n\t// eliminate trailstate if flag set\n\tif (ptype->flags & PT_NOSTATE)\n\t\ttk = NULL;\n\n\tts = P_FetchTrailstate(tk);\n\n\t// get msvc to shut up\n\tj = k = l = 0;\n\tm = 0;\n\n\twhile(ptype)\n\t{\n\t\tif (r_part_contentswitch.ival && (ptype->flags & (PT_TRUNDERWATER | PT_TROVERWATER)) && cl.worldmodel && cl.worldmodel->loadstate==MLS_LOADED)\n\t\t{\n\t\t\tint cont;\n\t\t\tcont = cl.worldmodel->funcs.PointContents(cl.worldmodel, NULL, org);\n\n\t\t\tif ((ptype->flags & PT_TROVERWATER) && (cont & ptype->fluidmask))\n\t\t\t\tgoto skip;\n\t\t\tif ((ptype->flags & PT_TRUNDERWATER) && !(cont & ptype->fluidmask))\n\t\t\t\tgoto skip;\n\t\t}\n\n\t\tif (dir && (dir[0] || dir[1] || dir[2]))\n\t\t{\n\t\t\tvoid PerpendicularVector( vec3_t dst, const vec3_t src );\n\t\t\tVectorCopy(dir, axis[2]);\n\t\t\tVectorNormalize(axis[2]);\n\t\t\tPerpendicularVector(axis[0], axis[2]);\n\t\t\tVectorNormalize(axis[0]);\n\t\t\tCrossProduct(axis[2], axis[0], axis[1]);\n\t\t\tVectorNormalize(axis[1]);\n\t\t}\n\t\tPScript_EffectSpawned(ptype, org, axis, 0, count);\n\n\t\tif (ptype->looks.type == PT_CDECAL)\n\t\t{\n\t\t\tvec3_t vec={0.5, 0.5, 0.5};\n\t\t\tint i;\n\t\t\tdecalctx_t ctx;\n\t\t\tvec3_t bestdir;\n\n\t\t\tif (!free_decals)\n\t\t\t\treturn 0;\n\n\t\t\tctx.entity = 0;\n\t\t\tctx.model = cl.worldmodel;\n\t\t\tif (!ctx.model)\n\t\t\t\treturn 0;\n\n\t\t\tVectorCopy(org, ctx.center);\n\t\t\tif (!dir || (dir[0] == 0 && dir[1] == 0 && dir[2] == 0))\n\t\t\t{\n\t\t\t\tfloat bestfrac = 1;\n\t\t\t\tfloat frac;\n\t\t\t\tvec3_t impact, normal;\n\t\t\t\tint what;\n\t\t\t\tbestdir[0] = 0;\n\t\t\t\tbestdir[1] = 0.73;\n\t\t\t\tbestdir[2] = 0.73;\n\t\t\t\tVectorNormalize(bestdir);\n\t\t\t\tfor (i = 0; i < 6; i++)\n\t\t\t\t{\n\t\t\t\t\tif (i >= 3)\n\t\t\t\t\t{\n\t\t\t\t\t\tctx.tangent1[0] = (i==3)*16;\n\t\t\t\t\t\tctx.tangent1[1] = (i==4)*16;\n\t\t\t\t\t\tctx.tangent1[2] = (i==5)*16;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tctx.tangent1[0] = -(i==0)*16;\n\t\t\t\t\t\tctx.tangent1[1] = -(i==1)*16;\n\t\t\t\t\t\tctx.tangent1[2] = -(i==2)*16;\n\t\t\t\t\t}\n\t\t\t\t\tVectorSubtract(org, ctx.tangent1, ctx.tangent2);\n\t\t\t\t\tVectorAdd(org, ctx.tangent1, ctx.tangent1);\n\n\n\t\t\t\t\tfrac = CL_TraceLine(ctx.tangent2, ctx.tangent1, impact, normal, &what);\n\t\t\t\t\tif (bestfrac > frac)\n\t\t\t\t\t{\n\t\t\t\t\t\tbestfrac = frac;\n\t\t\t\t\t\tVectorCopy(normal, bestdir);\n\t\t\t\t\t\tVectorCopy(impact, ctx.center);\n\t\t\t\t\t\tctx.entity = what;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tdir = bestdir;\n\t\t\t}\n\t\t\telse\n\t\t\t{\t//the dir arg is generally assumed to be facing away from the surface.\n\t\t\t\tVectorMA(org, 16, dir, ctx.tangent2);\n\t\t\t\tVectorMA(org, -16, dir, ctx.tangent1);\n\t\t\t\tCL_TraceLine(ctx.tangent2, ctx.tangent1, ctx.center, bestdir, &ctx.entity);\n\t\t\t}\n\n\t\t\tif (ctx.entity)\n\t\t\t{\n\t\t\t\tif (ctx.entity>0 && (unsigned)ctx.entity < (unsigned)cl.maxlerpents)\n\t\t\t\t{\n\t\t\t\t\tlerpents_t *le = cl.lerpents+ctx.entity;\n\t\t\t\t\tctx.model = cl.model_precache[le->entstate->modelindex];\n\t\t\t\t\tif (le->entstate)\n\t\t\t\t\t\tVectorSubtract(ctx.center, le->origin, ctx.center);\n\t\t\t\t\telse\n\t\t\t\t\t\tctx.entity = 0;\n\t\t\t\t}\n\t\t\t\telse if (ctx.entity<0 && (unsigned)-ctx.entity < (unsigned)csqc_world.num_edicts)\n\t\t\t\t{\n\t\t\t\t\twedict_t *e = WEDICT_NUM_UB(csqc_world.progs, -ctx.entity);\n\t\t\t\t\tif (e)\n\t\t\t\t\t{\n\t\t\t\t\t\tctx.model = csqc_world.Get_CModel(&csqc_world, e->v->modelindex);\n\t\t\t\t\t\tif (!ctx.model)\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\tVectorSubtract(ctx.center, e->v->origin, ctx.center);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tctx.entity = 0;\n\t\t\t}\n\n\t\t\tVectorNegate(dir, ctx.normal);\n\t\t\tVectorNormalize(ctx.normal);\n\n\t\t\tVectorNormalize(vec);\n\t\t\tCrossProduct(ctx.normal, vec, ctx.tangent1);\n\t\t\tMatrix4x4_CM_Transform3(Matrix4x4_CM_NewRotation(frandom()*360, ctx.normal[0], ctx.normal[1], ctx.normal[2]), ctx.tangent1, ctx.tangent2);\n\t\t\tCrossProduct(ctx.normal, ctx.tangent2, ctx.tangent1);\n\n\t\t\tVectorNormalize(ctx.tangent1);\n\t\t\tVectorNormalize(ctx.tangent2);\n\n\t\t\tctx.ptype = ptype;\n\t\t\tctx.scale1 = ptype->s2 - ptype->s1;\n\t\t\tctx.bias1 = ptype->s1 + ctx.scale1/2;\n\t\t\tctx.scale2 = ptype->t2 - ptype->t1;\n\t\t\tctx.bias2 = ptype->t1 + ctx.scale2/2;\n\t\t\tm = ptype->scale + frandom() * ptype->scalerand;\n\t\t\tctx.scale0 = 2.0 / m;\n\t\t\tctx.scale1 /= m;\n\t\t\tctx.scale2 /= m;\n\n\t\t\tif (ptype->randsmax!=1)\n\t\t\t\tctx.bias1 += ptype->texsstride * (rand()%ptype->randsmax);\n\n\t\t\t//inserts decals through a callback.\n\t\t\tMod_ClipDecal(ctx.model, ctx.center, ctx.normal, ctx.tangent2, ctx.tangent1, m, ptype->surfflagmask, ptype->surfflagmatch, PScript_AddDecals, &ctx);\n\n\t\t\tif (ptype->assoc < 0)\n\t\t\t\tbreak;\n\n\t\t\tptype = &part_type[ptype->assoc];\n\t\t\tcontinue;\n\t\t}\n\n\t\t// init spawn specific variables\n\t\tb = bfirst = NULL;\n\t\tspawnspc = 8;\n\t\tpcount = ptype->countextra + count*(ptype->count+ptype->countrand*frandom());\n\t\tif (ptype->flags & PT_INVFRAMETIME)\n\t\t\tpcount /= host_frametime;\n\t\tif (ts)\n\t\t\tpcount += ts->effect.emittime;\n\n\t\tpcount *= r_part_density.value;\n\n\t\tswitch (ptype->spawnmode)\n\t\t{\n\t\tcase SM_UNICIRCLE:\n\t\t\tm = pcount;\n\t\t\tif (ptype->looks.type == PT_BEAM||ptype->looks.type == PT_VBEAM)\n\t\t\t\tm--;\n\n\t\t\tif (m < 1)\n\t\t\t\tm = 0;\n\t\t\telse\n\t\t\t\tm = (M_PI*2)/m;\n\n\t\t\tif (ptype->spawnparam1) /* use for weird shape hacks */\n\t\t\t\tm *= ptype->spawnparam1;\n\t\t\tbreak;\n\t\tcase SM_TELEBOX:\n\t\t\tspawnspc = 4;\n\t\t\tl = -ptype->areaspreadvert;\n\t\tcase SM_LAVASPLASH:\n\t\t\tj = k = -ptype->areaspread;\n\t\t\tif (ptype->spawnparam1)\n\t\t\t\tm = ptype->spawnparam1;\n\t\t\telse\n\t\t\t\tm = 0.55752; /* default weird number for tele/lavasplash used in vanilla Q1 */\n\n\t\t\tif (ptype->spawnparam2)\n\t\t\t\tspawnspc = (int)ptype->spawnparam2;\n\t\t\tbreak;\n\t\tcase SM_FIELD:\n\t\t\tif (!avelocities[0][0])\n\t\t\t{\n\t\t\t\tfor (j=0 ; j<NUMVERTEXNORMALS ; j++)\n\t\t\t\t{\n\t\t\t\t\tavelocities[j][0] = (rand()&255) * 0.01;\n\t\t\t\t\tavelocities[j][1] = (rand()&255) * 0.01;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tj = 0;\n\t\t\tm = 0;\n\t\t\tbreak;\n//\t\tcase SM_MESHSURFACE:\n//\t\t\tmeshsurface = querymesh;\n//\t\t\ttotalarea = gah;\n//\t\t\tdensity = count / totalarea;\n//\t\t\tarea = 0;\n//\t\t\ttri = -1;\n//\t\t\tbreak;\n\t\tcase SM_FIXMEWARNING:\n\t\t\tCon_ThrottlePrintf(&throttle, 0, CON_WARNING\"Particle effect %s.%s is marked as a placeholder\\n\", ptype->config, ptype->name);\n\t\t\treturn 1;\n\t\tdefault:\t//others don't need intitialisation\n\t\t\tbreak;\n\t\t}\n\n\t\t// time limit (for completeness)\n\t\tif (ptype->spawntime && ts)\n\t\t{\n\t\t\tif (ts->effect.statetime > particletime)\n\t\t\t\treturn 0; // timelimit still in effect\n\n\t\t\tts->effect.statetime = particletime + ptype->spawntime; // record old time\n\t\t}\n\n\t\t// random chance for point effects\n\t\tif (ptype->spawnchance < frandom())\n\t\t{\n\t\t\ti = ceil(pcount);\n\t\t\tbreak;\n\t\t}\n\n\t\tif (ptype->nummodels)\n\t\t{\t//model spawning loop\n\t\t\tpartmodels_t *mod;\n\t\t\tif (ptype->count == 0 && ptype->countrand == 0 && ptype->countextra == 0)\n\t\t\t\tpcount = count;\t//if they just gave some models with no counts at all, assume they meant count=1.\n\t\t\tfor (i = 0; i < pcount; i++)\n\t\t\t{\n\t\t\t\tmod = &ptype->models[rand() % ptype->nummodels];\n\t\t\t\tif (!mod->model)\n\t\t\t\t\tmod->model = Mod_ForName(mod->name, MLV_WARN);\n\t\t\t\tif (mod->model)\n\t\t\t\t{\n\t\t\t\t\tvec3_t morg, mdir;\n\t\t\t\t\tfloat scale = frandom() * (mod->scalemax-mod->scalemin) + mod->scalemin;\n\t\t\t\t\tPScript_ApplyOrgVel(morg, mdir, org, axis, i, count, ptype);\n\t\t\t\t\tCL_SpawnSpriteEffect(morg, mdir, (mod->rflags&RF_USEORIENTATION)?axis[2]:NULL, mod->model, mod->framestart, mod->framecount, mod->framerate?mod->framerate:10, mod->alpha?mod->alpha:1, scale, ptype->rotationmin*180/M_PI, ptype->gravity, mod->traileffect, (mod->rflags & ~RF_USEORIENTATION) | RF_FORCECOLOURMOD, mod->skin, mod->rgb[0], mod->rgb[1], mod->rgb[2]);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\t/*this is a hack, use countextra=1, count=0*/\n\t\t\tif (!ptype->die && ptype->count == 1 && ptype->countrand == 0 && pcount < 1)\n\t\t\t\tpcount = 1;\n\t\t\tfor (i = 0; i < pcount; i++)\n\t\t\t{\t// particle spawning loop\n\n\t\t\t\tif (!free_particles)\n\t\t\t\t\tbreak;\n\t\t\t\tp = free_particles;\n\t\t\t\tif (ptype->looks.type == PT_BEAM||ptype->looks.type == PT_VBEAM)\n\t\t\t\t{\n\t\t\t\t\tif (!free_beams)\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tif (b)\n\t\t\t\t\t{\n\t\t\t\t\t\tb = b->next = free_beams;\n\t\t\t\t\t\tfree_beams = free_beams->next;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tb = bfirst = free_beams;\n\t\t\t\t\t\tfree_beams = free_beams->next;\n\t\t\t\t\t}\n\t\t\t\t\tb->texture_s = i; // TODO: FIX THIS NUMBER\n\t\t\t\t\tb->flags = 0;\n\t\t\t\t\tb->p = p;\n\t\t\t\t\tVectorClear(b->dir);\n\t\t\t\t}\n\t\t\t\tfree_particles = p->next;\n\t\t\t\tp->next = ptype->particles;\n\t\t\t\tptype->particles = p;\n\n\t\t\t\tp->die = ptype->randdie*frandom();\n\t\t\t\tp->scale = ptype->scale+ptype->scalerand*frandom();\n\t\t\t\tif (ptype->die)\n\t\t\t\t\tp->rgba[3] = ptype->alpha+p->die*ptype->alphachange;\n\t\t\t\telse\n\t\t\t\t\tp->rgba[3] = ptype->alpha;\n\t\t\t\tp->rgba[3] += ptype->alpharand*frandom();\n\t\t\t\t// p->color = 0;\n\t\t\t\tif (ptype->emittime < 0)\n\t\t\t\t\tp->state.trailstate = trailkey_null;\n\t\t\t\telse\n\t\t\t\t\tp->state.nextemit = particletime + ptype->emitstart - p->die;\n\n\t\t\t\tp->rotationspeed = ptype->rotationmin + frandom()*ptype->rotationrand;\n\t\t\t\tp->angle = ptype->rotationstartmin + frandom()*ptype->rotationstartrand;\n\t\t\t\tp->s1 = ptype->s1;\n\t\t\t\tp->t1 = ptype->t1;\n\t\t\t\tp->s2 = ptype->s2;\n\t\t\t\tp->t2 = ptype->t2;\n\t\t\t\tif (ptype->randsmax!=1)\n\t\t\t\t{\n\t\t\t\t\tm = ptype->texsstride * (rand()%ptype->randsmax);\n\t\t\t\t\tp->s1 += m;\n\t\t\t\t\tp->s2 += m;\n\t\t\t\t}\n\n\t\t\t\tif (ptype->colorindex >= 0)\n\t\t\t\t{\n\t\t\t\t\tint cidx;\n\t\t\t\t\tcidx = ptype->colorrand > 0 ? rand() % ptype->colorrand : 0;\n\t\t\t\t\tcidx = ptype->colorindex + cidx;\n\t\t\t\t\tif (cidx > 255)\n\t\t\t\t\t\tp->rgba[3] = p->rgba[3] / 2; // Hexen 2 style transparency\n\t\t\t\t\tcidx = (cidx & 0xff) * 3;\n\t\t\t\t\tp->rgba[0] = host_basepal[cidx] * (1/255.0);\n\t\t\t\t\tp->rgba[1] = host_basepal[cidx+1] * (1/255.0);\n\t\t\t\t\tp->rgba[2] = host_basepal[cidx+2] * (1/255.0);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tVectorCopy(ptype->rgb, p->rgba);\n\n\t\t\t\t// use org temporarily for rgbsync\n\t\t\t\tp->org[2] = frandom();\n\t\t\t\tp->org[0] = p->org[2]*ptype->rgbrandsync[0] + frandom()*(1-ptype->rgbrandsync[0]);\n\t\t\t\tp->org[1] = p->org[2]*ptype->rgbrandsync[1] + frandom()*(1-ptype->rgbrandsync[1]);\n\t\t\t\tp->org[2] = p->org[2]*ptype->rgbrandsync[2] + frandom()*(1-ptype->rgbrandsync[2]);\n\n\t\t\t\tp->rgba[0] += p->org[0]*ptype->rgbrand[0] + ptype->rgbchange[0]*p->die;\n\t\t\t\tp->rgba[1] += p->org[1]*ptype->rgbrand[1] + ptype->rgbchange[1]*p->die;\n\t\t\t\tp->rgba[2] += p->org[2]*ptype->rgbrand[2] + ptype->rgbchange[2]*p->die;\n\n#if 0\n\t\t\t\tPScript_ApplyOrgVel(p->org, p->vel, org, axis, i, pcount, ptype);\n#else\n\t\t\t\tp->vel[0] = 0;\n\t\t\t\tp->vel[1] = 0;\n\t\t\t\tp->vel[2] = 0;\n\n\t\t\t\t// handle spawn modes (org/vel)\n\t\t\t\tswitch (ptype->spawnmode)\n\t\t\t\t{\n/*\t\t\t\tcase SM_MESHSURFACE:\n\t\t\t\t\tif (area <= 0)\n\t\t\t\t\t{\n\t\t\t\t\t\ttri++;\n\t\t\t\t\t\tarea += calcarea(tri);\n\t\t\t\t\t\tarsvec[] = calcnormal(tri);\n\t\t\t\t\t}\n\n\t\t\t\t\tofsvec[] = randompointintriangle(tri);\n\n\t\t\t\t\tarea -= density;\n\t\t\t\t\tbreak;\n*/\n\t\t\t\tcase SM_BOX:\n\t\t\t\t\tofsvec[0] = crandom();\n\t\t\t\t\tofsvec[1] = crandom();\n\t\t\t\t\tofsvec[2] = crandom();\n\n\t\t\t\t\tarsvec[0] = ofsvec[0]*ptype->areaspread;\n\t\t\t\t\tarsvec[1] = ofsvec[1]*ptype->areaspread;\n\t\t\t\t\tarsvec[2] = ofsvec[2]*ptype->areaspreadvert;\n\t\t\t\t\tbreak;\n\t\t\t\tcase SM_TELEBOX:\n\t\t\t\t\tofsvec[0] = k;\n\t\t\t\t\tofsvec[1] = j;\n\t\t\t\t\tofsvec[2] = l+4;\n\t\t\t\t\tVectorNormalize(ofsvec);\n\t\t\t\t\tVectorScale(ofsvec, 1.0-(frandom())*m, ofsvec);\n\n\t\t\t\t\t// org is just like the original\n\t\t\t\t\tarsvec[0] = j + (rand()%spawnspc);\n\t\t\t\t\tarsvec[1] = k + (rand()%spawnspc);\n\t\t\t\t\tarsvec[2] = l + (rand()%spawnspc);\n\n\t\t\t\t\t// advance telebox loop\n\t\t\t\t\tj += spawnspc;\n\t\t\t\t\tif (j >= ptype->areaspread)\n\t\t\t\t\t{\n\t\t\t\t\t\tj = -ptype->areaspread;\n\t\t\t\t\t\tk += spawnspc;\n\t\t\t\t\t\tif (k >= ptype->areaspread)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tk = -ptype->areaspread;\n\t\t\t\t\t\t\tl += spawnspc;\n\t\t\t\t\t\t\tif (l >= ptype->areaspreadvert)\n\t\t\t\t\t\t\t\tl = -ptype->areaspreadvert;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase SM_LAVASPLASH:\n\t\t\t\t\t// calc directions, org with temp vector\n\t\t\t\t\tofsvec[0] = k + (rand()%spawnspc);\n\t\t\t\t\tofsvec[1] = j + (rand()%spawnspc);\n\t\t\t\t\tofsvec[2] = 256;\n\n\t\t\t\t\tarsvec[0] = ofsvec[0];\n\t\t\t\t\tarsvec[1] = ofsvec[1];\n\t\t\t\t\tarsvec[2] = frandom()*ptype->areaspreadvert;\n\n\t\t\t\t\tVectorNormalize(ofsvec);\n\t\t\t\t\tVectorScale(ofsvec, 1.0-(frandom())*m, ofsvec);\n\n\t\t\t\t\t// advance splash loop\n\t\t\t\t\tj += spawnspc;\n\t\t\t\t\tif (j >= ptype->areaspread)\n\t\t\t\t\t{\n\t\t\t\t\t\tj = -ptype->areaspread;\n\t\t\t\t\t\tk += spawnspc;\n\t\t\t\t\t\tif (k >= ptype->areaspread)\n\t\t\t\t\t\t\tk = -ptype->areaspread;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase SM_UNICIRCLE:\n\t\t\t\t\tofsvec[0] = cos(m*i);\n\t\t\t\t\tofsvec[1] = sin(m*i);\n\t\t\t\t\tofsvec[2] = 0;\n\t\t\t\t\tVectorScale(ofsvec, ptype->areaspread, arsvec);\n\t\t\t\t\tbreak;\n\t\t\t\tcase SM_FIELD:\n\t\t\t\t\tarsvec[0] = (cl.time * avelocities[i][0]) + m;\n\t\t\t\t\tarsvec[1] = (cl.time * avelocities[i][1]) + m;\n\t\t\t\t\tarsvec[2] = cos(arsvec[1]);\n\n\t\t\t\t\tofsvec[0] = arsvec[2]*cos(arsvec[0]);\n\t\t\t\t\tofsvec[1] = arsvec[2]*sin(arsvec[0]);\n\t\t\t\t\tofsvec[2] = -sin(arsvec[1]);\n\n//\t\t\t\t\tarsvec[0] = r_avertexnormals[j][0]*ptype->areaspread + ofsvec[0]*BEAMLENGTH;\n//\t\t\t\t\tarsvec[1] = r_avertexnormals[j][1]*ptype->areaspread + ofsvec[1]*BEAMLENGTH;\n//\t\t\t\t\tarsvec[2] = r_avertexnormals[j][2]*ptype->areaspreadvert + ofsvec[2]*BEAMLENGTH;\n\n\t\t\t\t\torgadd = ptype->spawnparam2 * sin(cl.time+j+m);\n\t\t\t\t\tarsvec[0] = r_avertexnormals[j][0]*(ptype->areaspread+orgadd) + ofsvec[0]*ptype->spawnparam1;\n\t\t\t\t\tarsvec[1] = r_avertexnormals[j][1]*(ptype->areaspread+orgadd) + ofsvec[1]*ptype->spawnparam1;\n\t\t\t\t\tarsvec[2] = r_avertexnormals[j][2]*(ptype->areaspreadvert+orgadd) + ofsvec[2]*ptype->spawnparam1;\n\n\t\t\t\t\tVectorNormalize(ofsvec);\n\n\t\t\t\t\tj++;\n\t\t\t\t\tif (j >= NUMVERTEXNORMALS)\n\t\t\t\t\t{\n\t\t\t\t\t\tj = 0;\n\t\t\t\t\t\tm += 0.1762891; // some BS number to try to \"randomize\" things\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase SM_DISTBALL:\n\t\t\t\t\t{\n\t\t\t\t\t\tfloat rdist;\n\n\t\t\t\t\t\trdist = ptype->spawnparam2 - crandom()*(1-(crandom() * ptype->spawnparam1));\n\n\t\t\t\t\t\t// this is a strange spawntype, which is based on the fact that\n\t\t\t\t\t\t// crandom()*crandom() provides something similar to an exponential\n\t\t\t\t\t\t// probability curve\n\t\t\t\t\t\tofsvec[0] = hrandom();\n\t\t\t\t\t\tofsvec[1] = hrandom();\n\t\t\t\t\t\tif (ptype->areaspreadvert)\n\t\t\t\t\t\t\tofsvec[2] = hrandom();\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tofsvec[2] = 0;\n\n\t\t\t\t\t\tVectorNormalize(ofsvec);\n\t\t\t\t\t\tVectorScale(ofsvec, rdist, ofsvec);\n\n\t\t\t\t\t\tarsvec[0] = ofsvec[0]*ptype->areaspread;\n\t\t\t\t\t\tarsvec[1] = ofsvec[1]*ptype->areaspread;\n\t\t\t\t\t\tarsvec[2] = ofsvec[2]*ptype->areaspreadvert;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tdefault: // SM_BALL, SM_CIRCLE\n\t\t\t\t\t{\n\t\t\t\t\t\tofsvec[0] = hrandom();\n\t\t\t\t\t\tofsvec[1] = hrandom();\n\t\t\t\t\t\tif (ptype->areaspreadvert)\n\t\t\t\t\t\t\tofsvec[2] = hrandom();\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tofsvec[2] = 0;\n\n\t\t\t\t\t\tVectorNormalize(ofsvec);\n\t\t\t\t\t\tif (ptype->spawnmode != SM_CIRCLE)\n\t\t\t\t\t\t\tVectorScale(ofsvec, frandom(), ofsvec);\n\n\t\t\t\t\t\tarsvec[0] = ofsvec[0]*ptype->areaspread;\n\t\t\t\t\t\tarsvec[1] = ofsvec[1]*ptype->areaspread;\n\t\t\t\t\t\tarsvec[2] = ofsvec[2]*ptype->areaspreadvert;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\t// apply arsvec+ofsvec\n\t\t\t\torgadd = ptype->orgadd + frandom()*ptype->randomorgadd;\n\t\t\t\tveladd = ptype->veladd + frandom()*ptype->randomveladd;\n#if 1\n\t\t\t\tif (dir)\n\t\t\t\t\tveladd *= VectorLength(dir);\n\t\t\t\tVectorMA(p->vel, ofsvec[0]*ptype->spawnvel, axis[0], p->vel);\n\t\t\t\tVectorMA(p->vel, ofsvec[1]*ptype->spawnvel, axis[1], p->vel);\n\t\t\t\tVectorMA(p->vel, veladd+ofsvec[2]*ptype->spawnvelvert, axis[2], p->vel);\n\n\t\t\t\tVectorMA(org, arsvec[0], axis[0], p->org);\n\t\t\t\tVectorMA(p->org, arsvec[1], axis[1], p->org);\n\t\t\t\tVectorMA(p->org, orgadd+arsvec[2], axis[2], p->org);\n#else\n\t\t\t\tp->org[0] = org[0] + arsvec[0];\n\t\t\t\tp->org[1] = org[1] + arsvec[1];\n\t\t\t\tp->org[2] = org[2] + arsvec[2];\n\t\t\t\tif (dir)\n\t\t\t\t{\n\t\t\t\t\tp->vel[0] += dir[0]*veladd+ofsvec[0]*ptype->spawnvel;\n\t\t\t\t\tp->vel[1] += dir[1]*veladd+ofsvec[1]*ptype->spawnvel;\n\t\t\t\t\tp->vel[2] += dir[2]*veladd+ofsvec[2]*ptype->spawnvelvert;\n\n\t\t\t\t\tp->org[0] += dir[0]*orgadd;\n\t\t\t\t\tp->org[1] += dir[1]*orgadd;\n\t\t\t\t\tp->org[2] += dir[2]*orgadd;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tp->vel[0] += ofsvec[0]*ptype->spawnvel;\n\t\t\t\t\tp->vel[1] += ofsvec[1]*ptype->spawnvel;\n\t\t\t\t\tp->vel[2] += ofsvec[2]*ptype->spawnvelvert - veladd;\n\n\t\t\t\t\tp->org[2] -= orgadd;\n\t\t\t\t}\n#endif\n\t\t\t\tif (ptype->flags & PT_WORLDSPACERAND)\n\t\t\t\t{\n\t\t\t\t\tdo\n\t\t\t\t\t{\n\t\t\t\t\t\tofsvec[0] = crand();\n\t\t\t\t\t\tofsvec[1] = crand();\n\t\t\t\t\t\tofsvec[2] = crand();\n\t\t\t\t\t} while(DotProduct(ofsvec,ofsvec)>1);\t//crap, but I'm trying to mimic dp\n\t\t\t\t\tp->org[0] += ofsvec[0] * ptype->orgwrand[0];\n\t\t\t\t\tp->org[1] += ofsvec[1] * ptype->orgwrand[1];\n\t\t\t\t\tp->org[2] += ofsvec[2] * ptype->orgwrand[2];\n\t\t\t\t\tp->vel[0] += ofsvec[0] * ptype->velwrand[0];\n\t\t\t\t\tp->vel[1] += ofsvec[1] * ptype->velwrand[1];\n\t\t\t\t\tp->vel[2] += ofsvec[2] * ptype->velwrand[2];\n\t\t\t\t\tVectorAdd(p->vel, ptype->velbias, p->vel);\n\t\t\t\t}\n\t\t\t\tVectorAdd(p->org, ptype->orgbias, p->org);\n#endif\n\n\t\t\t\tp->die = particletime + ptype->die - p->die;\n\n\t\t\t\tVectorCopy(p->org, p->oldorg);\n\t\t\t}\n\t\t}\n\n\t\t// update beam list\n\t\tif (ptype->looks.type == PT_BEAM||ptype->looks.type == PT_VBEAM)\n\t\t{\n\t\t\tif (b)\n\t\t\t{\n\t\t\t\t// update dir for bfirst for certain modes since it will never get updated\n\t\t\t\tswitch (ptype->spawnmode)\n\t\t\t\t{\n\t\t\t\tcase SM_UNICIRCLE:\n\t\t\t\t\t// kinda hackish here, assuming ofsvec contains the point at i-1\n\t\t\t\t\tarsvec[0] = cos(m*(i-2));\n\t\t\t\t\tarsvec[1] = sin(m*(i-2));\n\t\t\t\t\tarsvec[2] = 0;\n\t\t\t\t\tVectorSubtract(b->p->org, arsvec, bfirst->dir);\n\t\t\t\t\tVectorNormalize(bfirst->dir);\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tb->flags |= BS_NODRAW;\n\t\t\t\tb->next = ptype->beams;\n\t\t\t\tptype->beams = bfirst;\n\t\t\t}\n\t\t}\n\n\t\t// save off emit times in trailstate\n\t\tif (ts)\n\t\t\tts->effect.emittime = pcount - i;\n\n\t\t// maintain run list\n\t\tif (!(ptype->state & PS_INRUNLIST) && (ptype->particles || ptype->clippeddecals))\n\t\t{\n\t\t\tptype->runlink = &part_run_list;\n\t\t\tptype->nexttorun = part_run_list;\n\t\t\tif (part_run_list)\n\t\t\t\tpart_run_list->runlink = &ptype->nexttorun;\n\t\t\t*ptype->runlink = ptype;\n\t\t\tptype->state |= PS_INRUNLIST;\n\t\t}\n\nskip:\n\n\t\t// go to next associated effect\n\t\tif (ptype->assoc < 0)\n\t\t\tbreak;\n\n\t\t// new trailstate\n\t\tif (ts)\n\t\t{\n\t\t\ttk = &(ts->assoc);\n\t\t\tts = P_FetchTrailstate(tk);\n\t\t}\n\n\t\tptype = &part_type[ptype->assoc];\n\t}\n\n\treturn 0;\n}\n\nstatic int PScript_RunParticleEffectTypeString (vec3_t org, vec3_t dir, float count, char *name)\n{\n\tint type = P_FindParticleType(name);\n\tif (type < 0)\n\t\treturn 1;\n\n\treturn P_RunParticleEffectType(org, dir, count, type);\n}\n\n/*\n===============\nP_RunParticleEffect\n\n===============\n*/\nstatic void PScript_RunParticleEffect (vec3_t org, vec3_t dir, int color, int count)\n{\n\tint ptype;\n\n\tptype = P_FindParticleType(va(\"pe_%i\", color));\n\tif (P_RunParticleEffectType(org, dir, count, ptype))\n\t{\n\t\tif (count > 130 && PART_VALID(pe_size3))\n\t\t{\n\t\t\tpart_type[pe_size3].colorindex = color & ~0x7;\n\t\t\tpart_type[pe_size3].colorrand = 8;\n\t\t\tP_RunParticleEffectType(org, dir, count, pe_size3);\n\t\t}\n\t\telse if (count > 20 && PART_VALID(pe_size2))\n\t\t{\n\t\t\tpart_type[pe_size2].colorindex = color & ~0x7;\n\t\t\tpart_type[pe_size2].colorrand = 8;\n\t\t\tP_RunParticleEffectType(org, dir, count, pe_size2);\n\t\t}\n\t\telse if (PART_VALID(pe_default))\n\t\t{\n\t\t\tpart_type[pe_default].colorindex = color & ~0x7;\n\t\t\tpart_type[pe_default].colorrand = 8;\n\t\t\tP_RunParticleEffectType(org, dir, count, pe_default);\n\t\t}\n\t\telse if (fallback)\n\t\t\tfallback->RunParticleEffect(org, dir, color, count);\n\t}\n}\n\n//h2 stylie\nstatic void PScript_RunParticleEffect2 (vec3_t org, vec3_t dmin, vec3_t dmax, int color, int effect, int count)\n{\n\tint\t\t\ti, j;\n\tfloat\t\tnum;\n\tfloat invcount;\n\tvec3_t\tnvel;\n\n\tint ptype = P_FindParticleType(va(\"pe2_%i_%i\", effect, color));\n\tif (!PART_VALID(ptype))\n\t{\n\t\tptype = P_FindParticleType(va(\"pe2_%i\", effect));\n\t\tif (!PART_VALID(ptype))\n\t\t{\n\t\t\tif (fallback)\n\t\t\t{\n\t\t\t\tfallback->RunParticleEffect2(org, dmin, dmax, color, effect, count);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tptype = pe_default;\n\t\t}\n\t\tif (!PART_VALID(ptype))\n\t\t\treturn;\n\t\tpart_type[ptype].colorindex = color;\n\t}\n\n\tinvcount = 1/part_type[ptype].count; // using this to get R_RPET to always spawn 1\n\tcount = count * part_type[ptype].count;\n\n\tfor (i=0 ; i<count ; i++)\n\t{\n\t\tif (!free_particles)\n\t\t\treturn;\n\n\t\tfor (j=0 ; j<3 ; j++)\n\t\t{\n\t\t\tnum = rand() / (float)RAND_MAX;\n\t\t\tnvel[j] = dmin[j] + ((dmax[j] - dmin[j]) * num);\n\t\t}\n\t\tP_RunParticleEffectType(org, nvel, invcount, ptype);\n\n\t}\n}\n\n/*\n===============\nP_RunParticleEffect3\n\n===============\n*/\n//h2 stylie\nstatic void PScript_RunParticleEffect3 (vec3_t org, vec3_t box, int color, int effect, int count)\n{\n\tint\t\t\ti, j;\n\tvec3_t\tnvel;\n\tfloat\t\tnum;\n\tfloat invcount;\n\n\tint ptype = P_FindParticleType(va(\"pe3_%i_%i\", effect, color));\n\tif (!PART_VALID(ptype))\n\t{\n\t\tptype = P_FindParticleType(va(\"pe3_%i\", effect));\n\t\tif (!PART_VALID(ptype))\n\t\t{\n//\t\t\tif (fallback)\n//\t\t\t{\n//\t\t\t\tfallback->RunParticleEffect3(org, box, color, effect, count);\n//\t\t\t\treturn;\n//\t\t\t}\n\t\t\tptype = pe_default;\n\t\t}\n\t\tif (!PART_VALID(ptype))\n\t\t\treturn;\n\t\tpart_type[ptype].colorindex = color;\n\t}\n\n\tinvcount = 1/part_type[ptype].count; // using this to get R_RPET to always spawn 1\n\tcount = count * part_type[ptype].count;\n\n\tfor (i=0 ; i<count ; i++)\n\t{\n\t\tif (!free_particles)\n\t\t\treturn;\n\n\t\tfor (j=0 ; j<3 ; j++)\n\t\t{\n\t\t\tnum = rand() / (float)RAND_MAX;\n\t\t\tnvel[j] = (box[j] * num * 2) - box[j];\n\t\t}\n\n\t\tP_RunParticleEffectType(org, nvel, invcount, ptype);\n\t}\n}\n\n/*\n===============\nP_RunParticleEffect4\n\n===============\n*/\n//h2 stylie\nstatic void PScript_RunParticleEffect4 (vec3_t org, float radius, int color, int effect, int count)\n{\n\tint\t\t\ti, j;\n\tvec3_t\tnvel;\n\tfloat\t\tnum;\n\tfloat invcount;\n\n\tint ptype = P_FindParticleType(va(\"pe4_%i_%i\", effect, color));\n\tif (!PART_VALID(ptype))\n\t{\n\t\tptype = P_FindParticleType(va(\"pe4_%i\", effect));\n\t\tif (!PART_VALID(ptype))\n\t\t{\n//\t\t\tif (fallback)\n//\t\t\t{\n//\t\t\t\tfallback->RunParticleEffect4(org, radius, color, effect, count);\n//\t\t\t\treturn;\n//\t\t\t}\n\t\t\tptype = pe_default;\n\t\t}\n\t\tif (!PART_VALID(ptype))\n\t\t\treturn;\n\t\tpart_type[ptype].colorindex = color;\n\t}\n\n\tinvcount = 1/part_type[ptype].count; // using this to get R_RPET to always spawn 1\n\tcount = count * part_type[ptype].count;\n\n\tfor (i=0 ; i<count ; i++)\n\t{\n\t\tif (!free_particles)\n\t\t\treturn;\n\n\t\tfor (j=0 ; j<3 ; j++)\n\t\t{\n\t\t\tnum = rand() / (float)RAND_MAX;\n\t\t\tnvel[j] = (radius * num * 2) - radius;\n\t\t}\n\t\tP_RunParticleEffectType(org, nvel, invcount, ptype);\n\t}\n}\n\nstatic void PScript_RunParticleCube(int ptype, vec3_t minb, vec3_t maxb, vec3_t dir_min, vec3_t dir_max, float count, int colour, qboolean gravity, float jitter)\n{\n\tvec3_t org;\n\tint\t\t\ti, j;\n\tfloat\t\tnum;\n\tfloat invcount;\n\n\tif (!PART_VALID(ptype))\n\t\tptype = P_FindParticleType(va(\"te_cube%s_%i\", gravity?\"_g\":\"\", colour));\n\tif (!PART_VALID(ptype))\n\t{\n\t\tptype = P_FindParticleType(va(\"te_cube%s\", gravity?\"_g\":\"\"));\n\t\tif (!PART_VALID(ptype))\n\t\t\tptype = pe_default;\n\t\tif (!PART_VALID(ptype))\n\t\t\treturn;\n\t\tpart_type[ptype].colorindex = colour;\n\t}\n\n\tinvcount = 1/part_type[ptype].count; // using this to get R_RPET to always spawn 1\n\tcount = count * part_type[ptype].count;\n\n\tfor (i=0 ; i<count ; i++)\n\t{\n\t\tif (!free_particles)\n\t\t\treturn;\n\n\t\tfor (j=0 ; j<3 ; j++)\n\t\t{\n\t\t\tnum = rand() / (float)RAND_MAX;\n\t\t\torg[j] = minb[j] + num*(maxb[j]-minb[j]);\n\t\t}\n\t\tP_RunParticleEffectType(org, dir_min, invcount, ptype);\n\t}\n}\n\nstatic void PScript_RunParticleWeather(vec3_t minb, vec3_t maxb, vec3_t dir, float count, int colour, char *efname)\n{\n\tvec3_t org;\n\tint\t\t\ti, j;\n\tfloat\t\tnum;\n\tfloat invcount;\n\n\tint ptype = P_FindParticleType(va(\"te_%s_%i\", efname, colour));\n\tif (!PART_VALID(ptype))\n\t{\n\t\tptype = P_FindParticleType(va(\"te_%s\", efname));\n\t\tif (!PART_VALID(ptype))\n\t\t\tptype = pe_default;\n\t\tif (!PART_VALID(ptype))\n\t\t\treturn;\n\t\tpart_type[ptype].colorindex = colour;\n\t}\n\n\tinvcount = 1/part_type[ptype].count; // using this to get R_RPET to always spawn 1\n\tcount = count * part_type[ptype].count;\n\n\tfor (i=0 ; i<count ; i++)\n\t{\n\t\tif (!free_particles)\n\t\t\treturn;\n\n\t\tfor (j=0 ; j<3 ; j++)\n\t\t{\n\t\t\tnum = rand() / (float)RAND_MAX;\n\t\t\torg[j] = minb[j] + num*(maxb[j]-minb[j]);\n\t\t}\n\t\tP_RunParticleEffectType(org, dir, invcount, ptype);\n\t}\n}\n\nstatic void PScript_RunParticleEffectPalette (const char *nameprefix, vec3_t org, vec3_t dir, int color, int count)\n{\n\tint ptype;\n\n\tptype = P_FindParticleType(va(\"%s_%i\", nameprefix, color));\n\tif (ptype != P_INVALID)\n\t\tP_RunParticleEffectType(org, dir, count, ptype);\n\telse\n\t{\n\t\tptype = P_FindParticleType(nameprefix);\n\t\tif (!PART_VALID(ptype))\n\t\t{\n\t\t\tpart_type[ptype].colorindex = color;\n\t\t\tP_RunParticleEffectType(org, dir, count, ptype);\n\t\t}\n\t}\n}\n\nstatic void P_ParticleTrailSpawn (vec3_t startpos, vec3_t end, part_type_t *ptype, float timeinterval, trailkey_t* tk, int dlkey, vec3_t dlaxis[3])\n{\n\tvec3_t\tvec, vstep, right, up, start;\n\tfloat\tlen;\n\tint\t\t\ttcount;\n\tparticle_t\t*p;\n\tbeamseg_t   *b;\n\tbeamseg_t   *bfirst;\n\ttrailstate_t *ts;\n\tint count;\n\n\tfloat veladd = -ptype->veladd;\n\tfloat step;\n\tfloat stop;\n\tfloat tdegree = 2.0*M_PI/256; /* MSVC whine */\n\tfloat sdegree = 0;\n\tfloat nrfirst, nrlast;\n\n\tVectorCopy(startpos, start);\n\n\t// eliminate trailstate if flag set\n\tif (ptype->flags & PT_NOSTATE)\n\t\ttk = NULL;\n\n\tts = P_FetchTrailstate(tk);\n\n\tPScript_EffectSpawned(ptype, start, dlaxis, dlkey, 1);\n\n\tif (ptype->assoc>=0)\n\t{\n\t\tif (ts)\n\t\t\tP_ParticleTrail(start, end, ptype->assoc, timeinterval, dlkey, NULL, &(ts->assoc));\n\t\telse\n\t\t\tP_ParticleTrail(start, end, ptype->assoc, timeinterval, dlkey, NULL, NULL);\n\t}\n\n\tif (r_part_contentswitch.ival && (ptype->flags & (PT_TRUNDERWATER | PT_TROVERWATER)) && cl.worldmodel)\n\t{\n\t\tint cont;\n\t\tcont = cl.worldmodel->funcs.PointContents(cl.worldmodel, NULL, startpos);\n\n\t\tif ((ptype->flags & PT_TROVERWATER) && (cont & ptype->fluidmask))\n\t\t\treturn;\n\t\tif ((ptype->flags & PT_TRUNDERWATER) && !(cont & ptype->fluidmask))\n\t\t\treturn;\n\t}\n\n\t// time limit for trails\n\tif (ptype->spawntime && ts)\n\t{\n\t\tif (ts->effect.statetime > particletime)\n\t\t\treturn; // timelimit still in effect\n\n\t\tts->effect.statetime = particletime + ptype->spawntime; // record old time\n\t\tts = NULL; // clear trailstate so we don't save length/lastseg\n\t}\n\n\tif (ptype->nummodels)\n\t\treturn;\t//counts are too screwy.\n\n\t// random chance for trails\n\tif (ptype->spawnchance < frandom())\n\t\treturn; // don't spawn but return success\n\n\tif (!ptype->die)\n\t\tts = NULL;\n\n\tVectorSubtract (end, start, vec);\n\tlen = VectorNormalize (vec);\n\n\t// use ptype step to calc step vector and step size\n\n\t//(fractional) extra count\n\tstep = (cl.paused&&(ptype->die||ptype->randdie))?0:ptype->countextra;\n\tstep += ptype->count * r_part_density.value * timeinterval;\n\n\t//round it with overflow tracking\n\tstep += ptype->countoverflow;\n\tcount = step;\n\tptype->countoverflow = step-count;\n\n\tstep = ptype->countspacing;\t\t\t\t\t//particles per qu\n\tstep /= r_part_density.value;\t\t\t\t//scaled...\n\n\tif (ptype->flags & PT_AVERAGETRAIL)\n\t{\n\t\tfloat tavg;\n\t\t// mangle len/step to get last point to be at end\n\t\ttavg = len / step;\n\t\ttavg = tavg / ceil(tavg);\n\t\tstep *= tavg;\n\t\tlen += step;\n\t}\n\n\tVectorScale(vec, step, vstep);\n\n\t// store last stop here for lack of a better solution besides vectors\n\tif (ts)\n\t{\n\t\tts->trail.laststop = stop = ts->trail.laststop + len;\t//when to stop\n\t\tlen = ts->trail.lastdist;\n\t}\n\telse\n\t{\n\t\tstop = len;\n\t\tlen = 0;\n\t}\n\n\tif (step && len < stop)\n\t{\n\t\tif (count && len)\n\t\t{\n\t\t\t//recalculate step to cover the entire distance.\n\t\t\tcount += (stop-len) / step;\n\t\t\tstep = (stop-len)/count;\n\t\t}\n\t\telse\n\t\t\tcount += (stop-len) / step;\n\t}\n\telse\n\t{\n\t\tstep = 0;\n\t\tVectorClear(vstep);\n\t}\n\n// add offset\n//\tVectorAdd(start, ptype->orgbias, start);\n\n\t// spawn mode precalculations\n\tswitch(ptype->spawnmode)\n\t{\n\tcase SM_SPIRAL:\n\t\tVectorVectors(vec, right, up);\n\n\t\t// precalculate degree of rotation\n\t\tif (ptype->spawnparam1)\n\t\t\ttdegree = 2.0*M_PI/ptype->spawnparam1; /* distance per rotation inversed */\n\t\tsdegree = ptype->spawnparam2*(M_PI/180);\n\t\tbreak;\n\tcase SM_CIRCLE:\n\t\tVectorVectors(vec, right, up);\n\t\tbreak;\n\tcase SM_FIXMEWARNING:\n\t\tCon_ThrottlePrintf(&throttle, 0, CON_WARNING\"Particle effect %s.%s is marked as a placeholder\\n\", ptype->config, ptype->name);\n\t\treturn;\n\tdefault:\n\t\tbreak;\n\t}\n\n\tif (ptype->flags & PT_NOSPREADFIRST)\n\t\tnrfirst = len + step*1.5;\n\telse\n\t\tnrfirst = len;\n\n\tif (ptype->flags & PT_NOSPREADLAST)\n\t\tnrlast = stop;\n\telse\n\t\tnrlast = stop + step;\n\n\tb = bfirst = NULL;\n\n\tif (ptype->nummodels)\n\t{\t//model spawning loop\n\t\tpartmodels_t *mod;\n\t\tint i;\n\t\tfor (i = 0; i < count; i++)\n\t\t{\n\t\t\tmod = &ptype->models[rand() % ptype->nummodels];\n\t\t\tif (!mod->model)\n\t\t\t\tmod->model = Mod_ForName(mod->name, MLV_WARN);\n\t\t\tif (mod->model)\n\t\t\t{\n\t\t\t\tvec3_t morg, mdir;\n\t\t\t\tfloat scale = frandom() * (mod->scalemax-mod->scalemin) + mod->scalemin;\n\t\t\t\tPScript_ApplyOrgVel(morg, mdir, start, dlaxis, i, count, ptype);\n\t\t\t\tCL_SpawnSpriteEffect(morg, mdir, (mod->rflags&RF_USEORIENTATION)?dlaxis[2]:NULL, mod->model, mod->framestart, mod->framecount, mod->framerate?mod->framerate:10, mod->alpha?mod->alpha:1, scale, ptype->rotationmin*180/M_PI, ptype->gravity, mod->traileffect, (mod->rflags & ~RF_USEORIENTATION) | RF_FORCECOLOURMOD, mod->skin, mod->rgb[0], mod->rgb[1], mod->rgb[2]);\n\t\t\t}\n\t\t\tVectorAdd (start, vstep, start);\n\t\t}\n\t}\n\telse while (count-->0)//len < stop)\n\t{\n\t\tlen += step;\n\n\t\tif (!free_particles)\n\t\t{\n\t\t\tlen = stop;\n\t\t\tbreak;\n\t\t}\n\n\t\tp = free_particles;\n\t\tif (ptype->looks.type == PT_BEAM||ptype->looks.type == PT_VBEAM)\n\t\t{\n\t\t\tif (!free_beams)\n\t\t\t{\n\t\t\t\tlen = stop;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (b)\n\t\t\t{\n\t\t\t\tb = b->next = free_beams;\n\t\t\t\tfree_beams = free_beams->next;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tb = bfirst = free_beams;\n\t\t\t\tfree_beams = free_beams->next;\n\t\t\t}\n\t\t\tb->texture_s = len; // not sure how to calc this\n\t\t\tb->flags = 0;\n\t\t\tb->p = p;\n\t\t\tVectorCopy(vec, b->dir);\n\t\t}\n\n\t\tfree_particles = p->next;\n\t\tp->next = ptype->particles;\n\t\tptype->particles = p;\n\n\t\tp->die = ptype->randdie*frandom();\n\t\tp->scale = ptype->scale+ptype->scalerand*frandom();\n\t\tif (ptype->die)\n\t\t\tp->rgba[3] = ptype->alpha+p->die*ptype->alphachange;\n\t\telse\n\t\t\tp->rgba[3] = ptype->alpha;\n\t\tp->rgba[3] += ptype->alpharand*frandom();\n//\t\tp->color = 0;\n\n//\t\tif (ptype->spawnmode == SM_TRACER)\n\t\tif (ptype->spawnparam1)\n\t\t\ttcount = (int)(len * ptype->count / ptype->spawnparam1);\n\t\telse\n\t\t\ttcount = (int)(len * ptype->count);\n\n\t\tif (ptype->colorindex >= 0)\n\t\t{\n\t\t\tint cidx;\n\t\t\tcidx = ptype->colorrand > 0 ? rand() % ptype->colorrand : 0;\n\t\t\tif (ptype->flags & PT_CITRACER) // colorindex behavior as per tracers in std Q1\n\t\t\t\tcidx += ((tcount & 4) << 1);\n\n\t\t\tcidx = ptype->colorindex + cidx;\n\t\t\tif (cidx > 255)\n\t\t\t\tp->rgba[3] = p->rgba[3] / 2;\n\t\t\tcidx = (cidx & 0xff) * 3;\n\t\t\tp->rgba[0] = host_basepal[cidx] * (1/255.0);\n\t\t\tp->rgba[1] = host_basepal[cidx+1] * (1/255.0);\n\t\t\tp->rgba[2] = host_basepal[cidx+2] * (1/255.0);\n\t\t}\n\t\telse\n\t\t\tVectorCopy(ptype->rgb, p->rgba);\n\n\t\t// use org temporarily for rgbsync\n\t\tp->org[2] = frandom();\n\t\tp->org[0] = p->org[2]*ptype->rgbrandsync[0] + frandom()*(1-ptype->rgbrandsync[0]);\n\t\tp->org[1] = p->org[2]*ptype->rgbrandsync[1] + frandom()*(1-ptype->rgbrandsync[1]);\n\t\tp->org[2] = p->org[2]*ptype->rgbrandsync[2] + frandom()*(1-ptype->rgbrandsync[2]);\n\n\t\tp->rgba[0] += p->org[0]*ptype->rgbrand[0] + ptype->rgbchange[0]*p->die;\n\t\tp->rgba[1] += p->org[1]*ptype->rgbrand[1] + ptype->rgbchange[1]*p->die;\n\t\tp->rgba[2] += p->org[2]*ptype->rgbrand[2] + ptype->rgbchange[2]*p->die;\n\n\t\tVectorClear (p->vel);\n\t\tif (ptype->emittime < 0)\n\t\t\tp->state.trailstate = trailkey_null; // init trailstate\n\t\telse\n\t\t\tp->state.nextemit = particletime + ptype->emitstart - p->die;\n\n\t\tp->rotationspeed = ptype->rotationmin + frandom()*ptype->rotationrand;\n\t\tp->angle = ptype->rotationstartmin + frandom()*ptype->rotationstartrand;\n\t\tp->s1 = ptype->s1;\n\t\tp->t1 = ptype->t1;\n\t\tp->s2 = ptype->s2;\n\t\tp->t2 = ptype->t2;\n\t\tif (ptype->randsmax!=1)\n\t\t{\n\t\t\tfloat offs;\n\t\t\toffs = ptype->texsstride * (rand()%ptype->randsmax);\n\t\t\tp->s1 += offs;\n\t\t\tp->s2 += offs;\n\t\t\twhile (p->s1 >= 1)\n\t\t\t{\n\t\t\t\tp->s1 -= 1;\n\t\t\t\tp->s2 -= 1;\n\t\t\t\tp->t1 += ptype->texsstride;\n\t\t\t\tp->t2 += ptype->texsstride;\n\t\t\t}\n\t\t}\n\n\t\tif (len < nrfirst || len >= nrlast)\n\t\t{\n\t\t\t// no offset or areaspread for these particles...\n\t\t\tp->vel[0] = vec[0]*veladd;\n\t\t\tp->vel[1] = vec[1]*veladd;\n\t\t\tp->vel[2] = vec[2]*veladd;\n\n\t\t\tVectorCopy(start, p->org);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tswitch(ptype->spawnmode)\n\t\t\t{\n\t\t\tcase SM_TRACER:\n\t\t\t\tif (tcount & 1)\n\t\t\t\t{\n\t\t\t\t\tp->vel[0] = vec[1]*ptype->spawnvel;\n\t\t\t\t\tp->vel[1] = -vec[0]*ptype->spawnvel;\n\t\t\t\t\tp->org[0] = vec[1]*ptype->areaspread;\n\t\t\t\t\tp->org[1] = -vec[0]*ptype->areaspread;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tp->vel[0] = -vec[1]*ptype->spawnvel;\n\t\t\t\t\tp->vel[1] = vec[0]*ptype->spawnvel;\n\t\t\t\t\tp->org[0] = -vec[1]*ptype->areaspread;\n\t\t\t\t\tp->org[1] = vec[0]*ptype->areaspread;\n\t\t\t\t}\n\n\t\t\t\tp->vel[0] += vec[0]*veladd;\n\t\t\t\tp->vel[1] += vec[1]*veladd;\n\t\t\t\tp->vel[2] = vec[2]*veladd;\n\n\t\t\t\tp->org[0] += start[0];\n\t\t\t\tp->org[1] += start[1];\n\t\t\t\tp->org[2] = start[2];\n\t\t\t\tbreak;\n\t\t\tcase SM_SPIRAL:\n\t\t\t\t{\n\t\t\t\t\tfloat tsin, tcos;\n\t\t\t\t\tfloat tright, tup;\n\n\t\t\t\t\ttcos = cos(len*tdegree+sdegree);\n\t\t\t\t\ttsin = sin(len*tdegree+sdegree);\n\n\t\t\t\t\ttright = tcos*ptype->areaspread;\n\t\t\t\t\ttup = tsin*ptype->areaspread;\n\n\t\t\t\t\tp->org[0] = start[0] + right[0]*tright + up[0]*tup;\n\t\t\t\t\tp->org[1] = start[1] + right[1]*tright + up[1]*tup;\n\t\t\t\t\tp->org[2] = start[2] + right[2]*tright + up[2]*tup;\n\n\t\t\t\t\ttright = tcos*ptype->spawnvel;\n\t\t\t\t\ttup = tsin*ptype->spawnvel;\n\n\t\t\t\t\tp->vel[0] = vec[0]*veladd + right[0]*tright + up[0]*tup;\n\t\t\t\t\tp->vel[1] = vec[1]*veladd + right[1]*tright + up[1]*tup;\n\t\t\t\t\tp->vel[2] = vec[2]*veladd + right[2]*tright + up[2]*tup;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t// TODO: directionalize SM_BALL/SM_CIRCLE/SM_DISTBALL\n\t\t\tcase SM_BALL:\n\t\t\t\tp->org[0] = crandom();\n\t\t\t\tp->org[1] = crandom();\n\t\t\t\tp->org[2] = crandom();\n\t\t\t\tVectorNormalize(p->org);\n\t\t\t\tVectorScale(p->org, frandom(), p->org);\n\n\t\t\t\tp->vel[0] = vec[0]*veladd + p->org[0]*ptype->spawnvel;\n\t\t\t\tp->vel[1] = vec[1]*veladd + p->org[1]*ptype->spawnvel;\n\t\t\t\tp->vel[2] = vec[2]*veladd + p->org[2]*ptype->spawnvelvert;\n\n\t\t\t\tp->org[0] = p->org[0]*ptype->areaspread + start[0];\n\t\t\t\tp->org[1] = p->org[1]*ptype->areaspread + start[1];\n\t\t\t\tp->org[2] = p->org[2]*ptype->areaspreadvert + start[2];\n\t\t\t\tbreak;\n\n\t\t\tcase SM_CIRCLE:\n\t\t\t\t{\n\t\t\t\t\tfloat tsin, tcos;\n\n\t\t\t\t\ttcos = cos(len*tdegree)*ptype->areaspread;\n\t\t\t\t\ttsin = sin(len*tdegree)*ptype->areaspread;\n\n\t\t\t\t\tp->org[0] = start[0] + right[0]*tcos + up[0]*tsin + vstep[0] * (len*tdegree);\n\t\t\t\t\tp->org[1] = start[1] + right[1]*tcos + up[1]*tsin + vstep[1] * (len*tdegree);\n\t\t\t\t\tp->org[2] = start[2] + right[2]*tcos + up[2]*tsin + vstep[2] * (len*tdegree)*50;\n\n\t\t\t\t\ttcos = cos(len*tdegree)*ptype->spawnvel;\n\t\t\t\t\ttsin = sin(len*tdegree)*ptype->spawnvel;\n\n\t\t\t\t\tp->vel[0] = vec[0]*veladd + right[0]*tcos + up[0]*tsin;\n\t\t\t\t\tp->vel[1] = vec[1]*veladd + right[1]*tcos + up[1]*tsin;\n\t\t\t\t\tp->vel[2] = vec[2]*veladd + right[2]*tcos + up[2]*tsin;\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase SM_DISTBALL:\n\t\t\t\t{\n\t\t\t\t\tfloat rdist;\n\n\t\t\t\t\trdist = ptype->spawnparam2 - crandom()*(1-(crandom() * ptype->spawnparam1));\n\n\t\t\t\t\t// this is a strange spawntype, which is based on the fact that\n\t\t\t\t\t// crandom()*crandom() provides something similar to an exponential\n\t\t\t\t\t// probability curve\n\t\t\t\t\tp->org[0] = crandom();\n\t\t\t\t\tp->org[1] = crandom();\n\t\t\t\t\tp->org[2] = crandom();\n\n\t\t\t\t\tVectorNormalize(p->org);\n\t\t\t\t\tVectorScale(p->org, rdist, p->org);\n\n\t\t\t\t\tp->vel[0] = vec[0]*veladd + p->org[0]*ptype->spawnvel;\n\t\t\t\t\tp->vel[1] = vec[1]*veladd + p->org[1]*ptype->spawnvel;\n\t\t\t\t\tp->vel[2] = vec[2]*veladd + p->org[2]*ptype->spawnvelvert;\n\n\t\t\t\t\tp->org[0] = p->org[0]*ptype->areaspread + start[0];\n\t\t\t\t\tp->org[1] = p->org[1]*ptype->areaspread + start[1];\n\t\t\t\t\tp->org[2] = p->org[2]*ptype->areaspreadvert + start[2];\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tp->org[0] = crandom();\n\t\t\t\tp->org[1] = crandom();\n\t\t\t\tp->org[2] = crandom();\n\n\t\t\t\tp->vel[0] = vec[0]*veladd + p->org[0]*ptype->spawnvel;\n\t\t\t\tp->vel[1] = vec[1]*veladd + p->org[1]*ptype->spawnvel;\n\t\t\t\tp->vel[2] = vec[2]*veladd + p->org[2]*ptype->spawnvelvert;\n\n\t\t\t\tp->org[0] = p->org[0]*ptype->areaspread + start[0];\n\t\t\t\tp->org[1] = p->org[1]*ptype->areaspread + start[1];\n\t\t\t\tp->org[2] = p->org[2]*ptype->areaspreadvert + start[2];\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tif (ptype->orgadd)\n\t\t\t{\n\t\t\t\tp->org[0] += vec[0]*ptype->orgadd;\n\t\t\t\tp->org[1] += vec[1]*ptype->orgadd;\n\t\t\t\tp->org[2] += vec[2]*ptype->orgadd;\n\t\t\t}\n\t\t}\n\t\tif (ptype->flags & PT_WORLDSPACERAND)\n\t\t{\n\t\t\tvec3_t vtmp;\n\t\t\tdo\n\t\t\t{\n\t\t\t\tvtmp[0] = crand();\n\t\t\t\tvtmp[1] = crand();\n\t\t\t\tvtmp[2] = crand();\n\t\t\t} while(DotProduct(vtmp,vtmp)>1);\t//crap, but I'm trying to mimic dp\n\t\t\tp->org[0] += vtmp[0] * ptype->orgwrand[0];\n\t\t\tp->org[1] += vtmp[1] * ptype->orgwrand[1];\n\t\t\tp->org[2] += vtmp[2] * ptype->orgwrand[2];\n\t\t\tp->vel[0] += vtmp[0] * ptype->velwrand[0];\n\t\t\tp->vel[1] += vtmp[1] * ptype->velwrand[1];\n\t\t\tp->vel[2] += vtmp[2] * ptype->velwrand[2];\n\t\t\tVectorAdd(p->vel, ptype->velbias, p->vel);\n\t\t}\n\t\tVectorAdd(p->org, ptype->orgbias, p->org);\n\n\t\tVectorAdd (start, vstep, start);\n\n\t\tif (ptype->countrand)\n\t\t{\n\t\t\tfloat rstep = frandom() / ptype->countrand;\n\t\t\tVectorMA(start, rstep, vec, start);\n\t\t\tstep += rstep;\n\t\t}\n\n\t\tp->die = particletime + ptype->die - p->die;\n\t\tVectorCopy(p->org, p->oldorg);\n\t}\n\n\tif (ts)\n\t{\n\t\tts->trail.lastdist = len;\n\n\t\t// update beamseg list\n\t\tif (ptype->looks.type == PT_BEAM||ptype->looks.type == PT_VBEAM)\n\t\t{\n\t\t\tif (b)\n\t\t\t{\n\t\t\t\tif (ptype->beams)\n\t\t\t\t{\n\t\t\t\t\tif (ts->lastbeam)\n\t\t\t\t\t{\n\t\t\t\t\t\tb->next = ts->lastbeam->next;\n\t\t\t\t\t\tts->lastbeam->next = bfirst;\n\t\t\t\t\t\tts->lastbeam->flags &= ~BS_LASTSEG;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tb->next = ptype->beams;\n\t\t\t\t\t\tptype->beams = bfirst;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tptype->beams = bfirst;\n\t\t\t\t\tb->next = NULL;\n\t\t\t\t}\n\n\t\t\t\tb->flags |= BS_LASTSEG;\n\t\t\t\tts->lastbeam = b;\n\t\t\t}\n\n\t\t\tif ((!free_particles || !free_beams) && ts->lastbeam)\n\t\t\t{\n\t\t\t\tts->lastbeam->flags &= ~BS_LASTSEG;\n\t\t\t\tts->lastbeam->flags |= BS_NODRAW;\n\t\t\t\tts->lastbeam = NULL;\n\t\t\t}\n\t\t}\n\t}\n\telse if (ptype->looks.type == PT_BEAM||ptype->looks.type == PT_VBEAM)\n\t{\n\t\tif (b)\n\t\t{\n\t\t\tb->flags |= BS_NODRAW;\n\t\t\tb->next = ptype->beams;\n\t\t\tptype->beams = bfirst;\n\t\t}\n\t}\n\n\t// maintain run list\n\tif (!(ptype->state & PS_INRUNLIST))\n\t{\n\t\tptype->runlink = &part_run_list;\n\t\tptype->nexttorun = part_run_list;\n\t\tif (part_run_list)\n\t\t\tpart_run_list->runlink = &ptype->nexttorun;\n\t\t*ptype->runlink = ptype;\n\t\tptype->state |= PS_INRUNLIST;\n\t}\n\n\treturn;\n}\n\nstatic int PScript_ParticleTrail (vec3_t startpos, vec3_t end, int type, float timeinterval, int dlkey, vec3_t axis[3], trailkey_t *tk)\n{\n\tpart_type_t *ptype = &part_type[type];\n\n\tif (type >= FALLBACKBIAS && fallback)\n\t{\n\t\t// this will cause problems if dealing with fallbacks that actually \n\t\t// allocate their own trail state, but for now this should suffice\n\t\t//\n\t\t// also reusing fallback space for emit/trail info will cause some\n\t\t// issues with entities in action during particle reconfiguration \n\t\t// but that shouldn't be happening too often\n\t\ttrailstate_t* ts = P_FetchTrailstate(tk);\n\t\treturn fallback->ParticleTrail(startpos, end, type - FALLBACKBIAS, timeinterval, dlkey, axis, ts?&(ts->fallbackkey):NULL);\n\t}\n\n\tif (type < 0 || type >= numparticletypes)\n\t\treturn 1;\t//bad value\n\n\tif (!ptype->loaded)\n\t\treturn 1;\n\n\t// inwater check, switch only once\n\tif (r_part_contentswitch.ival && ptype->inwater >= 0 && cl.worldmodel && cl.worldmodel->loadstate == MLS_LOADED)\n\t{\n\t\tint cont;\n\t\tcont = cl.worldmodel->funcs.PointContents(cl.worldmodel, NULL, startpos);\n\n\t\tif (cont & FTECONTENTS_FLUID)\n\t\t\tptype = &part_type[ptype->inwater];\n\t}\n\n\tP_ParticleTrailSpawn (startpos, end, ptype, timeinterval, tk, dlkey, axis);\n\treturn 0;\n}\n\nstatic void PScript_ParticleTrailIndex (vec3_t start, vec3_t end, int type, float timeinterval, int color, int crnd, trailkey_t *tk)\n{\n\tif (type == P_INVALID)\n\t\ttype = pe_defaulttrail;\n\tif (PART_VALID(type))\n\t{\n\t\tpart_type[type].colorindex = color;\n\t\tpart_type[type].colorrand = crnd;\n\t\tP_ParticleTrail(start, end, type, timeinterval, 0, NULL, tk);\n\t}\n}\n\nstatic vec3_t pright, pup;\nstatic float pframetime;\n\nstatic void GL_DrawTexturedParticle(int count, particle_t **plist, plooks_t *type)\n{\n\tparticle_t *p;\n\tfloat x,y;\n\tfloat scale;\n\n\twhile (count--)\n\t{\n\t\tp = *plist++;\n\n\t\tif (pscriptmesh.numvertexes >= BUFFERVERTS-4)\n\t\t{\n\t\t\tpscriptmesh.numindexes = pscriptmesh.numvertexes/4*6;\n\t\t\tBE_DrawMesh_Single(type->shader, &pscriptmesh, NULL, 0);\n\t\t\tpscriptmesh.numvertexes = 0;\n\t\t}\n\n\t\tif (type->scalefactor == 1)\n\t\t\tscale = p->scale*0.25;\n\t\telse\n\t\t{\n\t\t\tscale = (p->org[0] - r_origin[0])*vpn[0] + (p->org[1] - r_origin[1])*vpn[1]\n\t\t\t\t+ (p->org[2] - r_origin[2])*vpn[2];\n\t\t\tscale = (scale*p->scale)*(type->invscalefactor) + p->scale * (type->scalefactor*250);\n\t\t\tif (scale < 20)\n\t\t\t\tscale = 0.25;\n\t\t\telse\n\t\t\t\tscale = 0.25 + scale * 0.001;\n\t\t}\n\n\t\tVector4Copy(p->rgba, pscriptcolours[pscriptmesh.numvertexes+0]);\n\t\tVector4Copy(p->rgba, pscriptcolours[pscriptmesh.numvertexes+1]);\n\t\tVector4Copy(p->rgba, pscriptcolours[pscriptmesh.numvertexes+2]);\n\t\tVector4Copy(p->rgba, pscriptcolours[pscriptmesh.numvertexes+3]);\n\n\t\tVector2Set(pscripttexcoords[pscriptmesh.numvertexes+0], p->s1, p->t1);\n\t\tVector2Set(pscripttexcoords[pscriptmesh.numvertexes+1], p->s1, p->t2);\n\t\tVector2Set(pscripttexcoords[pscriptmesh.numvertexes+2], p->s2, p->t2);\n\t\tVector2Set(pscripttexcoords[pscriptmesh.numvertexes+3], p->s2, p->t1);\n\n\t\tif (p->angle)\n\t\t{\n\t\t\tx = sin(p->angle)*scale;\n\t\t\ty = cos(p->angle)*scale;\n\n\t\t\tpscriptverts[pscriptmesh.numvertexes+0][0] = p->org[0] - x*pright[0] - y*pup[0];\n\t\t\tpscriptverts[pscriptmesh.numvertexes+0][1] = p->org[1] - x*pright[1] - y*pup[1];\n\t\t\tpscriptverts[pscriptmesh.numvertexes+0][2] = p->org[2] - x*pright[2] - y*pup[2];\n\t\t\tpscriptverts[pscriptmesh.numvertexes+1][0] = p->org[0] - y*pright[0] + x*pup[0];\n\t\t\tpscriptverts[pscriptmesh.numvertexes+1][1] = p->org[1] - y*pright[1] + x*pup[1];\n\t\t\tpscriptverts[pscriptmesh.numvertexes+1][2] = p->org[2] - y*pright[2] + x*pup[2];\n\t\t\tpscriptverts[pscriptmesh.numvertexes+2][0] = p->org[0] + x*pright[0] + y*pup[0];\n\t\t\tpscriptverts[pscriptmesh.numvertexes+2][1] = p->org[1] + x*pright[1] + y*pup[1];\n\t\t\tpscriptverts[pscriptmesh.numvertexes+2][2] = p->org[2] + x*pright[2] + y*pup[2];\n\t\t\tpscriptverts[pscriptmesh.numvertexes+3][0] = p->org[0] + y*pright[0] - x*pup[0];\n\t\t\tpscriptverts[pscriptmesh.numvertexes+3][1] = p->org[1] + y*pright[1] - x*pup[1];\n\t\t\tpscriptverts[pscriptmesh.numvertexes+3][2] = p->org[2] + y*pright[2] - x*pup[2];\n\t\t}\n\t\telse\n\t\t{\n\t\t\tVectorMA(p->org, -scale, pup, pscriptverts[pscriptmesh.numvertexes+0]);\n\t\t\tVectorMA(p->org, -scale, pright, pscriptverts[pscriptmesh.numvertexes+1]);\n\t\t\tVectorMA(p->org, scale, pup, pscriptverts[pscriptmesh.numvertexes+2]);\n\t\t\tVectorMA(p->org, scale, pright, pscriptverts[pscriptmesh.numvertexes+3]);\n\t\t}\n\t\tpscriptmesh.numvertexes += 4;\n\t}\n\n\tif (pscriptmesh.numvertexes)\n\t{\n\t\tpscriptmesh.numindexes = pscriptmesh.numvertexes/4*6;\n\t\tBE_DrawMesh_Single(type->shader, &pscriptmesh, NULL, 0);\n\t\tpscriptmesh.numvertexes = 0;\n\t}\n}\n\nstatic void GL_DrawTrifanParticle(int count, particle_t **plist, plooks_t *type)\n{\n\tparticle_t *p;\n\tvec3_t v, cr, o2;\n\tfloat scale;\n\n\twhile (count--)\n\t{\n\t\tp = *plist++;\n\n\t\tif (pscripttmesh.numvertexes >= BUFFERVERTS-3)\n\t\t{\n\t\t\tpscripttmesh.numindexes = pscripttmesh.numvertexes;\n\t\t\tBE_DrawMesh_Single(type->shader, &pscripttmesh, NULL, 0);\n\t\t\tpscripttmesh.numvertexes = 0;\n\t\t}\n\n\t\tscale = (p->org[0] - r_origin[0])*vpn[0] + (p->org[1] - r_origin[1])*vpn[1]\n\t\t\t+ (p->org[2] - r_origin[2])*vpn[2];\n\t\tscale = (scale*p->scale)*(type->invscalefactor) + p->scale * (type->scalefactor*250);\n\t\tif (scale < 20)\n\t\t\tscale = 0.05;\n\t\telse\n\t\t\tscale = 0.05 + scale * 0.0001;\n\n\t\tVector4Copy(p->rgba, pscriptcolours[pscripttmesh.numvertexes+0]);\n\t\tVector4Copy(p->rgba, pscriptcolours[pscripttmesh.numvertexes+1]);\n\t\tVector4Copy(p->rgba, pscriptcolours[pscripttmesh.numvertexes+2]);\n\n\t\tVector2Set(pscripttexcoords[pscripttmesh.numvertexes+0], p->s1, p->t1);\n\t\tVector2Set(pscripttexcoords[pscripttmesh.numvertexes+1], p->s1, p->t2);\n\t\tVector2Set(pscripttexcoords[pscripttmesh.numvertexes+2], p->s2, p->t1);\n\n\n\t\tVectorMA(p->org, -scale, p->vel, o2);\n\t\tVectorSubtract(r_refdef.vieworg, o2, v);\n\t\tCrossProduct(v, p->vel, cr);\n\t\tVectorNormalize(cr);\n\n\t\tVectorCopy(p->org, pscriptverts[pscripttmesh.numvertexes+0]);\n\t\tVectorMA(o2, -p->scale, cr, pscriptverts[pscripttmesh.numvertexes+1]);\n\t\tVectorMA(o2, p->scale, cr, pscriptverts[pscripttmesh.numvertexes+2]);\n\n\t\tpscripttmesh.numvertexes += 3;\n\t}\n\n\tif (pscripttmesh.numvertexes)\n\t{\n\t\tpscripttmesh.numindexes = pscripttmesh.numvertexes;\n\t\tBE_DrawMesh_Single(type->shader, &pscripttmesh, NULL, 0);\n\t\tpscripttmesh.numvertexes = 0;\n\t}\n}\n\n//static void R_AddLineSparkParticle(int count, particle_t **plist, plooks_t *type)\nstatic void R_AddLineSparkParticle(scenetris_t *t, particle_t *p, plooks_t *type)\n{\n\tif (cl_numstrisvert+2 > cl_maxstrisvert)\n\t\tcl_stris_ExpandVerts(cl_maxstrisvert+64*2);\n\n\tVector4Copy(p->rgba, cl_strisvertc[cl_numstrisvert+0]);\n\tVectorCopy(p->rgba, cl_strisvertc[cl_numstrisvert+1]);\n\tcl_strisvertc[cl_numstrisvert+1][3] = 0;\n\tVector2Set(cl_strisvertt[cl_numstrisvert+0], p->s1, p->t1);\n\tVector2Set(cl_strisvertt[cl_numstrisvert+1], p->s2, p->t2);\n\n\tVectorCopy(p->org, cl_strisvertv[cl_numstrisvert+0]);\n\tVectorMA(p->org, -1.0/10, p->vel, cl_strisvertv[cl_numstrisvert+1]);\n\t\n\tif (cl_numstrisidx+2 > cl_maxstrisidx)\n\t{\n\t\tcl_maxstrisidx += 64*2;\n\t\tcl_strisidx = BZ_Realloc(cl_strisidx, sizeof(*cl_strisidx)*cl_maxstrisidx);\n\t}\n\tcl_strisidx[cl_numstrisidx++] = (cl_numstrisvert - t->firstvert) + 0;\n\tcl_strisidx[cl_numstrisidx++] = (cl_numstrisvert - t->firstvert) + 1;\n\n\tcl_numstrisvert += 2;\n\n\tt->numvert += 2;\n\tt->numidx += 2;\n}\n\nstatic void R_AddTSparkParticle(scenetris_t *t, particle_t *p, plooks_t *type)\n{\n\tvec3_t v, cr, o2;\n//\tfloat scale;\n\n\tif (cl_numstrisvert+4 > cl_maxstrisvert)\n\t\tcl_stris_ExpandVerts(cl_maxstrisvert+64*4);\n\n/*\tif (type->scalefactor == 1)\n\t\tscale = p->scale*0.25;\n\telse\n\t{\n\t\tscale = (p->org[0] - r_origin[0])*vpn[0] + (p->org[1] - r_origin[1])*vpn[1]\n\t\t\t+ (p->org[2] - r_origin[2])*vpn[2];\n\t\tscale = (scale*p->scale)*(type->invscalefactor) + p->scale * (type->scalefactor*250);\n\t\tif (scale < 20)\n\t\t\tscale = 0.25;\n\t\telse\n\t\t\tscale = 0.25 + scale * 0.001;\n\t}\n*/\n\tif (type->premul)\n\t{\n\t\tvec4_t rgba;\n\t\tfloat a = p->rgba[3];\n\t\tif (a > 1)\n\t\t\ta = 1;\n\t\trgba[0] = p->rgba[0] * a;\n\t\trgba[1] = p->rgba[1] * a;\n\t\trgba[2] = p->rgba[2] * a;\n\t\trgba[3] = (type->premul==2)?0:a;\n\t\tVector4Copy(rgba, cl_strisvertc[cl_numstrisvert+0]);\n\t\tVector4Copy(rgba, cl_strisvertc[cl_numstrisvert+1]);\n\t\tVector4Copy(rgba, cl_strisvertc[cl_numstrisvert+2]);\n\t\tVector4Copy(rgba, cl_strisvertc[cl_numstrisvert+3]);\n\t}\n\telse\n\t{\n\t\tVector4Copy(p->rgba, cl_strisvertc[cl_numstrisvert+0]);\n\t\tVector4Copy(p->rgba, cl_strisvertc[cl_numstrisvert+1]);\n\t\tVector4Copy(p->rgba, cl_strisvertc[cl_numstrisvert+2]);\n\t\tVector4Copy(p->rgba, cl_strisvertc[cl_numstrisvert+3]);\n\t}\n\n\tVector2Set(cl_strisvertt[cl_numstrisvert+0], p->s1, p->t1);\n\tVector2Set(cl_strisvertt[cl_numstrisvert+1], p->s1, p->t2);\n\tVector2Set(cl_strisvertt[cl_numstrisvert+2], p->s2, p->t2);\n\tVector2Set(cl_strisvertt[cl_numstrisvert+3], p->s2, p->t1);\n\n\n/*\tif (1)\n\t{\n\t\tvec3_t movedir;\n\t\tfloat length = VectorNormalize2(p->vel, movedir);\n\t\tlength *= type->stretch;\n\t\tif (length < p->scale * 0.25)\n\t\t\tlength = p->scale * 0.25;\n\t\tVectorMA(p->org, -length, movedir, o2);\n\n\t\tVectorSubtract(r_refdef.vieworg, o2, v);\n\t\tCrossProduct(v, p->vel, cr);\n\t\tVectorNormalize(cr);\n\t\tVectorMA(o2, -p->scale*0.5, cr, cl_strisvertv[cl_numstrisvert+0]);\n\t\tVectorMA(o2, p->scale*0.5, cr, cl_strisvertv[cl_numstrisvert+1]);\n\n\t\tVectorMA(p->org, length, movedir, o2);\n\t}\n\telse*/\n\tif (1/*type->stretch < 0*/)\n\t{\n\t\tvec3_t movedir;\n\t\tfloat halfscale = p->scale*0.5;\n\t\tfloat length = VectorNormalize2(p->vel, movedir);\n\t\tif (type->stretch < 0)\n\t\t\tlength = -type->stretch;\t//fixed lengths\n\t\telse if (type->stretch)\n\t\t\tlength *= type->stretch;\t//velocity multiplier\n\t\telse\n\t\t\tSys_Error(\"type->stretch should be 0.05\\n\");\n//\t\t\tlength *= 0.05;\t\t\t\t//fallback\n\n\t\tif (length < halfscale * type->minstretch)\n\t\t\tlength = halfscale * type->minstretch;\n\n\t\tVectorMA(p->org, -length, movedir, o2);\n\t\tVectorSubtract(r_refdef.vieworg, o2, v);\n\t\tCrossProduct(v, p->vel, cr);\n\t\tVectorNormalize(cr);\n\n\t\thalfscale = fabs(p->scale);\t//gah, I hate dp.\n\t\tVectorMA(o2, -halfscale/2, cr, cl_strisvertv[cl_numstrisvert+0]);\n\t\tVectorMA(o2, halfscale/2, cr, cl_strisvertv[cl_numstrisvert+1]);\n\n\t\tVectorMA(p->org, length, movedir, o2);\n\t}\n\telse if (type->stretch)\n\t{\n\t\tVectorMA(p->org, -type->stretch, p->vel, o2);\n\t\tVectorSubtract(r_refdef.vieworg, o2, v);\n\t\t\n\t\tCrossProduct(v, p->vel, cr);\n\t\tVectorNormalize(cr);\n\n\t\tVectorMA(o2, -p->scale/2, cr, cl_strisvertv[cl_numstrisvert+0]);\n\t\tVectorMA(o2, p->scale/2, cr, cl_strisvertv[cl_numstrisvert+1]);\n\n\t\tVectorMA(p->org, type->stretch, p->vel, o2);\n\t}\n\telse\n\t{\n\t\tVectorMA(p->org, 0.1, p->vel, o2);\n\t\tVectorSubtract(r_refdef.vieworg, p->org, v);\n\n\n\t\tCrossProduct(v, p->vel, cr);\n\t\tVectorNormalize(cr);\n\n\t\tVectorMA(p->org, -p->scale/2, cr, cl_strisvertv[cl_numstrisvert+0]);\n\t\tVectorMA(p->org, p->scale/2, cr, cl_strisvertv[cl_numstrisvert+1]);\n\t}\n\tVectorSubtract(r_refdef.vieworg, o2, v);\n\tCrossProduct(v, p->vel, cr);\n\tVectorNormalize(cr);\n\n\tVectorMA(o2, p->scale*0.5, cr, cl_strisvertv[cl_numstrisvert+2]);\n\tVectorMA(o2, -p->scale*0.5, cr, cl_strisvertv[cl_numstrisvert+3]);\n\n\n\n\tif (cl_numstrisidx+6 > cl_maxstrisidx)\n\t{\n\t\tcl_maxstrisidx += 64*6;\n\t\tcl_strisidx = BZ_Realloc(cl_strisidx, sizeof(*cl_strisidx)*cl_maxstrisidx);\n\t}\n\tcl_strisidx[cl_numstrisidx++] = (cl_numstrisvert - t->firstvert) + 0;\n\tcl_strisidx[cl_numstrisidx++] = (cl_numstrisvert - t->firstvert) + 1;\n\tcl_strisidx[cl_numstrisidx++] = (cl_numstrisvert - t->firstvert) + 2;\n\tcl_strisidx[cl_numstrisidx++] = (cl_numstrisvert - t->firstvert) + 0;\n\tcl_strisidx[cl_numstrisidx++] = (cl_numstrisvert - t->firstvert) + 2;\n\tcl_strisidx[cl_numstrisidx++] = (cl_numstrisvert - t->firstvert) + 3;\n\n\tcl_numstrisvert += 4;\n\n\tt->numvert += 4;\n\tt->numidx += 6;\n}\n\nstatic void GL_DrawTexturedSparkParticle(int count, particle_t **plist, plooks_t *type)\n{\n\tparticle_t *p;\n\tvec3_t v, cr, o2;\n\n\twhile (count--)\n\t{\n\t\tp = *plist++;\n\n\t\tif (pscriptmesh.numvertexes >= BUFFERVERTS-4)\n\t\t{\n\t\t\tpscriptmesh.numindexes = pscriptmesh.numvertexes/4*6;\n\t\t\tBE_DrawMesh_Single(type->shader, &pscriptmesh, NULL, 0);\n\t\t\tpscriptmesh.numvertexes = 0;\n\t\t}\n\n\t\tVector4Copy(p->rgba, pscriptcolours[pscriptmesh.numvertexes+0]);\n\t\tVector4Copy(p->rgba, pscriptcolours[pscriptmesh.numvertexes+1]);\n\t\tVector4Copy(p->rgba, pscriptcolours[pscriptmesh.numvertexes+2]);\n\t\tVector4Copy(p->rgba, pscriptcolours[pscriptmesh.numvertexes+3]);\n\n\t\tVector2Set(pscripttexcoords[pscriptmesh.numvertexes+0], p->s1, p->t1);\n\t\tVector2Set(pscripttexcoords[pscriptmesh.numvertexes+1], p->s1, p->t2);\n\t\tVector2Set(pscripttexcoords[pscriptmesh.numvertexes+2], p->s2, p->t2);\n\t\tVector2Set(pscripttexcoords[pscriptmesh.numvertexes+3], p->s2, p->t1);\n\n\n\t\tif (type->stretch)\n\t\t{\n\t\t\tVectorMA(p->org, type->stretch, p->vel, o2);\n\t\t\tVectorMA(p->org, -type->stretch, p->vel, v);\n\t\t\tVectorSubtract(r_refdef.vieworg, v, v);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tVectorMA(p->org, 0.1, p->vel, o2);\n\t\t\tVectorSubtract(r_refdef.vieworg, p->org, v);\n\t\t}\n\n\t\tCrossProduct(v, p->vel, cr);\n\t\tVectorNormalize(cr);\n\n\t\tVectorMA(p->org, -p->scale/2, cr, pscriptverts[pscriptmesh.numvertexes+0]);\n\t\tVectorMA(p->org, p->scale/2, cr, pscriptverts[pscriptmesh.numvertexes+1]);\n\n\t\tVectorSubtract(r_refdef.vieworg, o2, v);\n\t\tCrossProduct(v, p->vel, cr);\n\t\tVectorNormalize(cr);\n\n\t\tVectorMA(o2, p->scale/2, cr, pscriptverts[pscriptmesh.numvertexes+2]);\n\t\tVectorMA(o2, -p->scale/2, cr, pscriptverts[pscriptmesh.numvertexes+3]);\n\n\t\tpscriptmesh.numvertexes += 4;\n\t}\n\n\tif (pscriptmesh.numvertexes)\n\t{\n\t\tpscriptmesh.numindexes = pscriptmesh.numvertexes/4*6;\n\t\tBE_DrawMesh_Single(type->shader, &pscriptmesh, NULL, 0);\n\t\tpscriptmesh.numvertexes = 0;\n\t}\n}\n\n\nstatic void GL_DrawParticleVBeam(int count, beamseg_t **blist, plooks_t *type)\n{\n\tbeamseg_t *b;\n\tvec3_t v;\n\tvec3_t cr;\n\tbeamseg_t *c;\n\tparticle_t *p;\n\tparticle_t *q;\n\tfloat ts;\n\n\twhile(count--)\n\t{\n\t\tb = *blist++;\n\n\t\tif (pscriptmesh.numvertexes >= BUFFERVERTS-4)\n\t\t{\n\t\t\tpscriptmesh.numindexes = pscriptmesh.numvertexes/4*6;\n\t\t\tBE_DrawMesh_Single(type->shader, &pscriptmesh, NULL, 0);\n\t\t\tpscriptmesh.numvertexes = 0;\n\t\t}\n\n\t\tc = b->next;\n\n\t\tq = c->p;\n\t\tif (!q)\n\t\t\tcontinue;\n\t\tp = b->p;\n\n//\t\tq->rgba[3] = 1;\n//\t\tp->rgba[3] = 1;\n\n\t\tVectorSubtract(r_refdef.vieworg, q->org, v);\n\t\tVectorNormalize(v);\n\t\tCrossProduct(c->dir, v, cr);\n\t\tVectorNormalize(cr);\n\t\tts = c->texture_s*q->angle + particletime*q->rotationspeed;\n\t\tVector4Copy(q->rgba, pscriptcolours[pscriptmesh.numvertexes+0]);\n\t\tVector4Copy(q->rgba, pscriptcolours[pscriptmesh.numvertexes+1]);\n\t\tVector2Set(pscripttexcoords[pscriptmesh.numvertexes+0], p->s1, p->t1);\n\t\tVector2Set(pscripttexcoords[pscriptmesh.numvertexes+1], p->s2, p->t2);\n\t\tVectorMA(q->org, -q->scale, cr, pscriptverts[pscriptmesh.numvertexes+0]);\n\t\tVectorMA(q->org, q->scale, cr, pscriptverts[pscriptmesh.numvertexes+1]);\n\n\t\tVectorSubtract(r_refdef.vieworg, p->org, v);\n\t\tVectorNormalize(v);\n\t\tCrossProduct(b->dir, v, cr); // replace with old p->dir?\n\t\tVectorNormalize(cr);\n\t\tts = b->texture_s*p->angle + particletime*p->rotationspeed;\n\t\t(void)ts;\n\t\tVector4Copy(p->rgba, pscriptcolours[pscriptmesh.numvertexes+2]);\n\t\tVector4Copy(p->rgba, pscriptcolours[pscriptmesh.numvertexes+3]);\n\t\tVector2Set(pscripttexcoords[pscriptmesh.numvertexes+2], p->s1, p->t1);\n\t\tVector2Set(pscripttexcoords[pscriptmesh.numvertexes+3], p->s2, p->t2);\n\t\tVectorMA(p->org, p->scale, cr, pscriptverts[pscriptmesh.numvertexes+2]);\n\t\tVectorMA(p->org, -p->scale, cr, pscriptverts[pscriptmesh.numvertexes+3]);\n\n\t\tpscriptmesh.numvertexes += 4;\n\t}\n\n\tif (pscriptmesh.numvertexes)\n\t{\n\t\tpscriptmesh.numindexes = pscriptmesh.numvertexes/4*6;\n\t\tBE_DrawMesh_Single(type->shader, &pscriptmesh, NULL, 0);\n\t\tpscriptmesh.numvertexes = 0;\n\t}\n}\nstatic void GL_DrawParticleBeam(int count, beamseg_t **blist, plooks_t *type)\n{\n\tbeamseg_t *b;\n\tvec3_t v;\n\tvec3_t cr;\n\tbeamseg_t *c;\n\tparticle_t *p;\n\tparticle_t *q;\n\tfloat ts;\n\n\twhile(count--)\n\t{\n\t\tb = *blist++;\n\n\t\tif (pscriptmesh.numvertexes >= BUFFERVERTS-4)\n\t\t{\n\t\t\tpscriptmesh.numindexes = pscriptmesh.numvertexes/4*6;\n\t\t\tBE_DrawMesh_Single(type->shader, &pscriptmesh, NULL, 0);\n\t\t\tpscriptmesh.numvertexes = 0;\n\t\t}\n\n\t\tc = b->next;\n\n\t\tq = c->p;\n\t\tif (!q)\n\t\t\tcontinue;\n\t\tp = b->p;\n\n//\t\tq->rgba[3] = 1;\n//\t\tp->rgba[3] = 1;\n\n\t\tVectorSubtract(r_refdef.vieworg, q->org, v);\n\t\tVectorNormalize(v);\n\t\tCrossProduct(c->dir, v, cr);\n\t\tVectorNormalize(cr);\n\t\tts = c->texture_s*q->angle + particletime*q->rotationspeed;\n\t\tVector4Copy(q->rgba, pscriptcolours[pscriptmesh.numvertexes+0]);\n\t\tVector4Copy(q->rgba, pscriptcolours[pscriptmesh.numvertexes+1]);\n\t\tVector2Set(pscripttexcoords[pscriptmesh.numvertexes+0], ts, p->t1);\n\t\tVector2Set(pscripttexcoords[pscriptmesh.numvertexes+1], ts, p->t2);\n\t\tVectorMA(q->org, -q->scale, cr, pscriptverts[pscriptmesh.numvertexes+0]);\n\t\tVectorMA(q->org, q->scale, cr, pscriptverts[pscriptmesh.numvertexes+1]);\n\n\t\tVectorSubtract(r_refdef.vieworg, p->org, v);\n\t\tVectorNormalize(v);\n\t\tCrossProduct(b->dir, v, cr); // replace with old p->dir?\n\t\tVectorNormalize(cr);\n\t\tts = b->texture_s*p->angle + particletime*p->rotationspeed;\n\t\tVector4Copy(p->rgba, pscriptcolours[pscriptmesh.numvertexes+2]);\n\t\tVector4Copy(p->rgba, pscriptcolours[pscriptmesh.numvertexes+3]);\n\t\tVector2Set(pscripttexcoords[pscriptmesh.numvertexes+2], ts, p->t2);\n\t\tVector2Set(pscripttexcoords[pscriptmesh.numvertexes+3], ts, p->t1);\n\t\tVectorMA(p->org, p->scale, cr, pscriptverts[pscriptmesh.numvertexes+2]);\n\t\tVectorMA(p->org, -p->scale, cr, pscriptverts[pscriptmesh.numvertexes+3]);\n\n\t\tpscriptmesh.numvertexes += 4;\n\t}\n\n\tif (pscriptmesh.numvertexes)\n\t{\n\t\tpscriptmesh.numindexes = pscriptmesh.numvertexes/4*6;\n\t\tBE_DrawMesh_Single(type->shader, &pscriptmesh, NULL, 0);\n\t\tpscriptmesh.numvertexes = 0;\n\t}\n}\n\nstatic void R_AddClippedDecal(scenetris_t *t, clippeddecal_t *d, plooks_t *type)\n{\n\tif (cl_numstrisvert+4 > cl_maxstrisvert)\n\t\tcl_stris_ExpandVerts(cl_maxstrisvert+64*4);\n\n\tif (d->entity > 0)\n\t{\n\t\tlerpents_t *le = cl.lerpents+d->entity;\n\t\tif (le->sequence != cl.lerpentssequence)\n\t\t\treturn;\t//hide until its visible again.\n\t\tif (le->angles[0] || le->angles[1] || le->angles[2])\n\t\t{\t//FIXME: deal with rotated entities.\n\t\t\td->die = -1;\n\t\t\treturn;\n\t\t}\n\t\tVectorAdd(d->vertex[0], le->origin, cl_strisvertv[cl_numstrisvert+0]);\n\t\tVectorAdd(d->vertex[1], le->origin, cl_strisvertv[cl_numstrisvert+1]);\n\t\tVectorAdd(d->vertex[2], le->origin, cl_strisvertv[cl_numstrisvert+2]);\n\t}\n\telse if (d->entity < 0)\n\t{\n\t\twedict_t *e = WEDICT_NUM_UB(csqc_world.progs, -d->entity);\n\t\tif (!e)\n\t\t\treturn;\n\t\tif (e->ereftype!=ER_ENTITY ||\t//kill decals attached to removed ents.\n\t\t\te->v->angles[0] || e->v->angles[1] || e->v->angles[2])\t//FIXME: deal with rotated entities.\n\t\t{\n\t\t\td->die = -1;\n\t\t\treturn;\n\t\t}\n\t\tVectorAdd(d->vertex[0], e->v->origin, cl_strisvertv[cl_numstrisvert+0]);\n\t\tVectorAdd(d->vertex[1], e->v->origin, cl_strisvertv[cl_numstrisvert+1]);\n\t\tVectorAdd(d->vertex[2], e->v->origin, cl_strisvertv[cl_numstrisvert+2]);\n\t}\n\telse\n\t{\n\t\tVectorCopy(d->vertex[0], cl_strisvertv[cl_numstrisvert+0]);\n\t\tVectorCopy(d->vertex[1], cl_strisvertv[cl_numstrisvert+1]);\n\t\tVectorCopy(d->vertex[2], cl_strisvertv[cl_numstrisvert+2]);\n\t}\n\n\tif (type->premul)\n\t{\n\t\tvec4_t rgba;\n\t\tfloat a = d->rgba[3];\n\t\tif (a > 1)\n\t\t\ta = 1;\n\t\trgba[0] = d->rgba[0] * a;\n\t\trgba[1] = d->rgba[1] * a;\n\t\trgba[2] = d->rgba[2] * a;\n\t\trgba[3] = (type->premul==2)?0:a;\n\t\tVector4Scale(rgba, d->valpha[0], cl_strisvertc[cl_numstrisvert+0]);\n\t\tVector4Scale(rgba, d->valpha[1], cl_strisvertc[cl_numstrisvert+1]);\n\t\tVector4Scale(rgba, d->valpha[2], cl_strisvertc[cl_numstrisvert+2]);\n\t}\n\telse\n\t{\n\t\tVector4Copy(d->rgba, cl_strisvertc[cl_numstrisvert+0]);\n\t\tcl_strisvertc[cl_numstrisvert+0][3] *= d->valpha[0];\n\t\tVector4Copy(d->rgba, cl_strisvertc[cl_numstrisvert+1]);\n\t\tcl_strisvertc[cl_numstrisvert+1][3] *= d->valpha[1];\n\t\tVector4Copy(d->rgba, cl_strisvertc[cl_numstrisvert+2]);\n\t\tcl_strisvertc[cl_numstrisvert+2][3] *= d->valpha[2];\n\t}\n\n\tVector2Copy(d->texcoords[0], cl_strisvertt[cl_numstrisvert+0]);\n\tVector2Copy(d->texcoords[1], cl_strisvertt[cl_numstrisvert+1]);\n\tVector2Copy(d->texcoords[2], cl_strisvertt[cl_numstrisvert+2]);\n\n\tif (cl_numstrisidx+3 > cl_maxstrisidx)\n\t{\n\t\tcl_maxstrisidx += 64*3;\n\t\tcl_strisidx = BZ_Realloc(cl_strisidx, sizeof(*cl_strisidx)*cl_maxstrisidx);\n\t}\n\tcl_strisidx[cl_numstrisidx++] = (cl_numstrisvert - t->firstvert) + 0;\n\tcl_strisidx[cl_numstrisidx++] = (cl_numstrisvert - t->firstvert) + 1;\n\tcl_strisidx[cl_numstrisidx++] = (cl_numstrisvert - t->firstvert) + 2;\n\n\tcl_numstrisvert += 3;\n\n\tt->numvert += 3;\n\tt->numidx += 3;\n}\n\nstatic void R_AddUnclippedDecal(scenetris_t *t, particle_t *p, plooks_t *type)\n{\n\tfloat x, y;\n\tvec3_t sdir, tdir;\n\n\tif (cl_numstrisvert+4 > cl_maxstrisvert)\n\t\tcl_stris_ExpandVerts(cl_maxstrisvert+64*4);\n\n\tif (type->premul)\n\t{\n\t\tvec4_t rgba;\n\t\tfloat a = p->rgba[3];\n\t\tif (a > 1)\n\t\t\ta = 1;\n\t\trgba[0] = p->rgba[0] * a;\n\t\trgba[1] = p->rgba[1] * a;\n\t\trgba[2] = p->rgba[2] * a;\n\t\trgba[3] = (type->premul==2)?0:a;\n\t\tVector4Copy(rgba, cl_strisvertc[cl_numstrisvert+0]);\n\t\tVector4Copy(rgba, cl_strisvertc[cl_numstrisvert+1]);\n\t\tVector4Copy(rgba, cl_strisvertc[cl_numstrisvert+2]);\n\t\tVector4Copy(rgba, cl_strisvertc[cl_numstrisvert+3]);\n\t}\n\telse\n\t{\n\t\tVector4Copy(p->rgba, cl_strisvertc[cl_numstrisvert+0]);\n\t\tVector4Copy(p->rgba, cl_strisvertc[cl_numstrisvert+1]);\n\t\tVector4Copy(p->rgba, cl_strisvertc[cl_numstrisvert+2]);\n\t\tVector4Copy(p->rgba, cl_strisvertc[cl_numstrisvert+3]);\n\t}\n\n\tVector2Set(cl_strisvertt[cl_numstrisvert+0], p->s1, p->t1);\n\tVector2Set(cl_strisvertt[cl_numstrisvert+1], p->s1, p->t2);\n\tVector2Set(cl_strisvertt[cl_numstrisvert+2], p->s2, p->t2);\n\tVector2Set(cl_strisvertt[cl_numstrisvert+3], p->s2, p->t1);\n\n//\tif (p->vel[1] == 1)\n\t{\n\t\tVectorSet(sdir, 1, 0, 0);\n\t\tVectorSet(tdir, 0, 1, 0);\n\t}\n\n\tif (p->angle)\n\t{\n\t\tx = sin(p->angle)*p->scale;\n\t\ty = cos(p->angle)*p->scale;\n\n\t\tcl_strisvertv[cl_numstrisvert+0][0] = p->org[0] - x*sdir[0] - y*tdir[0];\n\t\tcl_strisvertv[cl_numstrisvert+0][1] = p->org[1] - x*sdir[1] - y*tdir[1];\n\t\tcl_strisvertv[cl_numstrisvert+0][2] = p->org[2] - x*sdir[2] - y*tdir[2];\n\t\tcl_strisvertv[cl_numstrisvert+1][0] = p->org[0] - y*sdir[0] + x*tdir[0];\n\t\tcl_strisvertv[cl_numstrisvert+1][1] = p->org[1] - y*sdir[1] + x*tdir[1];\n\t\tcl_strisvertv[cl_numstrisvert+1][2] = p->org[2] - y*sdir[2] + x*tdir[2];\n\t\tcl_strisvertv[cl_numstrisvert+2][0] = p->org[0] + x*sdir[0] + y*tdir[0];\n\t\tcl_strisvertv[cl_numstrisvert+2][1] = p->org[1] + x*sdir[1] + y*tdir[1];\n\t\tcl_strisvertv[cl_numstrisvert+2][2] = p->org[2] + x*sdir[2] + y*tdir[2];\n\t\tcl_strisvertv[cl_numstrisvert+3][0] = p->org[0] + y*sdir[0] - x*tdir[0];\n\t\tcl_strisvertv[cl_numstrisvert+3][1] = p->org[1] + y*sdir[1] - x*tdir[1];\n\t\tcl_strisvertv[cl_numstrisvert+3][2] = p->org[2] + y*sdir[2] - x*tdir[2];\n\t}\n\telse\n\t{\n\t\tVectorMA(p->org, -p->scale, tdir, cl_strisvertv[cl_numstrisvert+0]);\n\t\tVectorMA(p->org, -p->scale, sdir, cl_strisvertv[cl_numstrisvert+1]);\n\t\tVectorMA(p->org, p->scale, tdir, cl_strisvertv[cl_numstrisvert+2]);\n\t\tVectorMA(p->org, p->scale, sdir, cl_strisvertv[cl_numstrisvert+3]);\n\t}\n\n\tif (cl_numstrisidx+6 > cl_maxstrisidx)\n\t{\n\t\tcl_maxstrisidx += 64*6;\n\t\tcl_strisidx = BZ_Realloc(cl_strisidx, sizeof(*cl_strisidx)*cl_maxstrisidx);\n\t}\n\tcl_strisidx[cl_numstrisidx++] = (cl_numstrisvert - t->firstvert) + 0;\n\tcl_strisidx[cl_numstrisidx++] = (cl_numstrisvert - t->firstvert) + 1;\n\tcl_strisidx[cl_numstrisidx++] = (cl_numstrisvert - t->firstvert) + 2;\n\tcl_strisidx[cl_numstrisidx++] = (cl_numstrisvert - t->firstvert) + 0;\n\tcl_strisidx[cl_numstrisidx++] = (cl_numstrisvert - t->firstvert) + 2;\n\tcl_strisidx[cl_numstrisidx++] = (cl_numstrisvert - t->firstvert) + 3;\n\n\tcl_numstrisvert += 4;\n\n\tt->numvert += 4;\n\tt->numidx += 6;\n}\n\nstatic void R_AddTexturedParticle(scenetris_t *t, particle_t *p, plooks_t *type)\n{\n\tfloat scale, x, y;\n\n\tif (cl_numstrisvert+4 > cl_maxstrisvert)\n\t\tcl_stris_ExpandVerts(cl_maxstrisvert+64*4);\n\n\tif (type->scalefactor == 1)\n\t\tscale = p->scale*0.25;\n\telse\n\t{\n\t\tscale = (p->org[0] - r_origin[0])*vpn[0] + (p->org[1] - r_origin[1])*vpn[1]\n\t\t\t+ (p->org[2] - r_origin[2])*vpn[2];\n\t\tscale = (scale*p->scale)*(type->invscalefactor) + p->scale * (type->scalefactor*250);\n\t\tif (scale < 20)\n\t\t\tscale = 0.25;\n\t\telse\n\t\t\tscale = 0.25 + scale * 0.001;\n\t}\n\n\tif (type->premul)\n\t{\n\t\tvec4_t rgba;\n\t\tfloat a = p->rgba[3];\n\t\tif (a > 1)\n\t\t\ta = 1;\n\t\trgba[0] = p->rgba[0] * a;\n\t\trgba[1] = p->rgba[1] * a;\n\t\trgba[2] = p->rgba[2] * a;\n\t\trgba[3] = (type->premul==2)?0:a;\n\t\tVector4Copy(rgba, cl_strisvertc[cl_numstrisvert+0]);\n\t\tVector4Copy(rgba, cl_strisvertc[cl_numstrisvert+1]);\n\t\tVector4Copy(rgba, cl_strisvertc[cl_numstrisvert+2]);\n\t\tVector4Copy(rgba, cl_strisvertc[cl_numstrisvert+3]);\n\t}\n\telse\n\t{\n\t\tVector4Copy(p->rgba, cl_strisvertc[cl_numstrisvert+0]);\n\t\tVector4Copy(p->rgba, cl_strisvertc[cl_numstrisvert+1]);\n\t\tVector4Copy(p->rgba, cl_strisvertc[cl_numstrisvert+2]);\n\t\tVector4Copy(p->rgba, cl_strisvertc[cl_numstrisvert+3]);\n\t}\n\n\tVector2Set(cl_strisvertt[cl_numstrisvert+0], p->s1, p->t1);\n\tVector2Set(cl_strisvertt[cl_numstrisvert+1], p->s1, p->t2);\n\tVector2Set(cl_strisvertt[cl_numstrisvert+2], p->s2, p->t2);\n\tVector2Set(cl_strisvertt[cl_numstrisvert+3], p->s2, p->t1);\n\n\tif (p->angle)\n\t{\n\t\tx = sin(p->angle)*scale;\n\t\ty = cos(p->angle)*scale;\n\n\t\tcl_strisvertv[cl_numstrisvert+0][0] = p->org[0] - x*pright[0] - y*pup[0];\n\t\tcl_strisvertv[cl_numstrisvert+0][1] = p->org[1] - x*pright[1] - y*pup[1];\n\t\tcl_strisvertv[cl_numstrisvert+0][2] = p->org[2] - x*pright[2] - y*pup[2];\n\t\tcl_strisvertv[cl_numstrisvert+1][0] = p->org[0] - y*pright[0] + x*pup[0];\n\t\tcl_strisvertv[cl_numstrisvert+1][1] = p->org[1] - y*pright[1] + x*pup[1];\n\t\tcl_strisvertv[cl_numstrisvert+1][2] = p->org[2] - y*pright[2] + x*pup[2];\n\t\tcl_strisvertv[cl_numstrisvert+2][0] = p->org[0] + x*pright[0] + y*pup[0];\n\t\tcl_strisvertv[cl_numstrisvert+2][1] = p->org[1] + x*pright[1] + y*pup[1];\n\t\tcl_strisvertv[cl_numstrisvert+2][2] = p->org[2] + x*pright[2] + y*pup[2];\n\t\tcl_strisvertv[cl_numstrisvert+3][0] = p->org[0] + y*pright[0] - x*pup[0];\n\t\tcl_strisvertv[cl_numstrisvert+3][1] = p->org[1] + y*pright[1] - x*pup[1];\n\t\tcl_strisvertv[cl_numstrisvert+3][2] = p->org[2] + y*pright[2] - x*pup[2];\n\t}\n\telse\n\t{\n\t\tVectorMA(p->org, -scale, pup, cl_strisvertv[cl_numstrisvert+0]);\n\t\tVectorMA(p->org, -scale, pright, cl_strisvertv[cl_numstrisvert+1]);\n\t\tVectorMA(p->org, scale, pup, cl_strisvertv[cl_numstrisvert+2]);\n\t\tVectorMA(p->org, scale, pright, cl_strisvertv[cl_numstrisvert+3]);\n\t}\n\n\tif (cl_numstrisidx+6 > cl_maxstrisidx)\n\t{\n\t\tcl_maxstrisidx += 64*6;\n\t\tcl_strisidx = BZ_Realloc(cl_strisidx, sizeof(*cl_strisidx)*cl_maxstrisidx);\n\t}\n\tcl_strisidx[cl_numstrisidx++] = (cl_numstrisvert - t->firstvert) + 0;\n\tcl_strisidx[cl_numstrisidx++] = (cl_numstrisvert - t->firstvert) + 1;\n\tcl_strisidx[cl_numstrisidx++] = (cl_numstrisvert - t->firstvert) + 2;\n\tcl_strisidx[cl_numstrisidx++] = (cl_numstrisvert - t->firstvert) + 0;\n\tcl_strisidx[cl_numstrisidx++] = (cl_numstrisvert - t->firstvert) + 2;\n\tcl_strisidx[cl_numstrisidx++] = (cl_numstrisvert - t->firstvert) + 3;\n\n\tcl_numstrisvert += 4;\n\n\tt->numvert += 4;\n\tt->numidx += 6;\n}\n\nstatic void PScript_DrawParticleTypes (void)\n{\n\tfloat viewtranslation[16];\n\tstatic float lastviewmatrix[16];\n//\tvoid (*sparklineparticles)(int count, particle_t **plist, plooks_t *type)=R_AddLineSparkParticle;\n\tvoid (*sparkfanparticles)(int count, particle_t **plist, plooks_t *type)=GL_DrawTrifanParticle;\n\tvoid (*sparktexturedparticles)(int count, particle_t **plist, plooks_t *type)=GL_DrawTexturedSparkParticle;\n\n\tvoid *pdraw, *bdraw;\n\tvoid (*tdraw)(scenetris_t *t, particle_t *p, plooks_t *type);\n\n\tvec3_t oldorg;\n\tvec3_t stop, normal;\n\tpart_type_t *type;\n\tparticle_t\t\t*p, *kill;\n\tclippeddecal_t *d, *dkill;\n\tramp_t *ramp;\n\tfloat grav;\n\tvec3_t friction;\n\tscenetris_t *scenetri;\n\tfloat dist;\n\tparticle_t *kill_list, *kill_first;\t//the kill list is to stop particles from being freed and reused whilst still in this loop\n\t\t\t\t\t\t\t\t\t\t//which is bad because beams need to find out when particles died. Reuse can do wierd things.\n\t\t\t\t\t\t\t\t\t\t//remember that they're not drawn instantly either.\n\tbeamseg_t *b, *bkill;\n\n\tint traces=r_particle_tracelimit.ival;\n\tint rampind;\n\tstatic float oldtime;\n\tstatic float flurrytime;\n\tqboolean doflurry;\n\tint batchflags;\n\tint i;\n\tRSpeedMark();\n\n\tif (r_plooksdirty)\n\t{\n\t\tint i, j;\n\t\t{\n\t\t\tparticleengine_t *tmp = fallback; fallback = NULL;\n\n\t\t\tpe_default\t\t\t= PScript_FindParticleType(\"PE_DEFAULT\");\n\t\t\tpe_size2\t\t\t= PScript_FindParticleType(\"PE_SIZE2\");\n\t\t\tpe_size3\t\t\t= PScript_FindParticleType(\"PE_SIZE3\");\n\t\t\tpe_defaulttrail\t\t= PScript_FindParticleType(\"PE_DEFAULTTRAIL\");\n\n\t\t\tif (pe_default == P_INVALID)\n\t\t\t{\n\t\t\t//pe_default\t\t\t= PScript_FindParticleType(\"SVC_PARTICLE\");\n\t\t\t}\n\n\t\t\tfallback = tmp;\n\t\t}\n\n\t\tfor (i = 0; i < numparticletypes; i++)\n\t\t{\n\t\t\t//set the fallback\n\t\t\tpart_type[i].slooks = &part_type[i].looks;\n\t\t\tfor (j = i-1; j-- > 0;)\n\t\t\t{\n\t\t\t\tif (!memcmp(&part_type[i].looks, &part_type[j].looks, sizeof(plooks_t)))\n\t\t\t\t{\n\t\t\t\t\tpart_type[i].slooks = part_type[j].slooks;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tr_plooksdirty = false;\n\t\tCL_RegisterParticles();\n\t}\n#if 1\n\tpframetime = cl.time - oldtime;\n\tif (pframetime < 0)\n\t\tpframetime = 0;\n\toldtime = cl.time;\n#else\n\tpframetime = host_frametime;\n\tif (cl.paused || r_secondaryview || r_refdef.recurse)\n\t\tpframetime = 0;\n#endif\n\tVectorScale (vup, 1.5, pup);\n\tVectorScale (vright, 1.5, pright);\n\n\tkill_list = kill_first = NULL;\n\n\tflurrytime -= pframetime;\n\tif (flurrytime < 0)\n\t{\n\t\tdoflurry = true;\n\t\tflurrytime = 0.1+frandom()*0.3;\n\t}\n\telse\n\t\tdoflurry = false;\n\n\n\tif (!free_decals)\n\t{\n\t\t//mark some as dead, so we can keep spawning new ones next frame.\n\t\tfor (i = 0; i < 256; i++)\n\t\t{\n\t\t\tdecals[r_decalrecycle].die = -1;\n\t\t\tif (++r_decalrecycle >= r_numdecals)\n\t\t\t\tr_decalrecycle = 0;\n\t\t}\n\t}\n\tif (!free_particles)\n\t{\n\t\t//mark some as dead.\n\t\tfor (i = 0; i < 256; i++)\n\t\t{\n\t\t\tparticles[r_particlerecycle].die = -1;\n\t\t\tif (++r_particlerecycle >= r_numparticles)\n\t\t\t\tr_particlerecycle = 0;\n\t\t}\n\t}\n\n\t{\n\t\tfloat tmp[16];\n\t\tMatrix4_Invert(r_refdef.m_view, tmp);\n\t\tMatrix4_Multiply(tmp, lastviewmatrix, viewtranslation);\n\t\tmemcpy(lastviewmatrix, r_refdef.m_view, sizeof(tmp));\n\t}\n\n\tfor (type = part_run_list; type != NULL; type = type->nexttorun)\n\t{\n\t\tif (type->clippeddecals)\n\t\t{\n\t\t\tif (cl_numstris && cl_stris[cl_numstris-1].shader == type->looks.shader && cl_stris[cl_numstris-1].flags == 0)\n\t\t\t\tscenetri = &cl_stris[cl_numstris-1];\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (cl_numstris == cl_maxstris)\n\t\t\t\t{\n\t\t\t\t\tcl_maxstris+=8;\n\t\t\t\t\tcl_stris = BZ_Realloc(cl_stris, sizeof(*cl_stris)*cl_maxstris);\n\t\t\t\t}\n\t\t\t\tscenetri = &cl_stris[cl_numstris++];\n\t\t\t\tscenetri->shader = type->looks.shader;\n\t\t\t\tscenetri->flags = 0;\n\t\t\t\tscenetri->firstidx = cl_numstrisidx;\n\t\t\t\tscenetri->firstvert = cl_numstrisvert;\n\t\t\t\tscenetri->numvert = 0;\n\t\t\t\tscenetri->numidx = 0;\n\t\t\t}\n\n\t\t\tfor ( ;; )\n\t\t\t{\n\t\t\t\tdkill = type->clippeddecals;\n\t\t\t\tif (dkill && dkill->die < particletime)\n\t\t\t\t{\n\t\t\t\t\ttype->clippeddecals = dkill->next;\n\t\t\t\t\tdkill->next = free_decals;\n\t\t\t\t\tfree_decals = dkill;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tfor (d=type->clippeddecals ; d ; d=d->next)\n\t\t\t{\n\t\t\t\tfor ( ;; )\n\t\t\t\t{\n\t\t\t\t\tdkill = d->next;\n\t\t\t\t\tif (dkill && dkill->die < particletime)\n\t\t\t\t\t{\n\t\t\t\t\t\td->next = dkill->next;\n\t\t\t\t\t\tdkill->next = free_decals;\n\t\t\t\t\t\tfree_decals = dkill;\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\n\t\t\t\tif (d->die - particletime <= type->die)\n\t\t\t\t{\n\t\t\t\t\tswitch (type->rampmode)\n\t\t\t\t\t{\n\t\t\t\t\tcase RAMP_NEAREST:\n\t\t\t\t\t\trampind = (int)(type->rampindexes * (type->die - (d->die - particletime)) / type->die);\n\t\t\t\t\t\tif (rampind >= type->rampindexes)\n\t\t\t\t\t\t\trampind = type->rampindexes - 1;\n\t\t\t\t\t\tramp = type->ramp + rampind;\n\t\t\t\t\t\tVectorCopy(ramp->rgb, d->rgba);\n\t\t\t\t\t\td->rgba[3] = ramp->alpha;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase RAMP_LERP:\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tfloat frac = (type->rampindexes * (type->die - (d->die - particletime)) / type->die);\n\t\t\t\t\t\t\tint s1, s2;\n\t\t\t\t\t\t\ts1 = min(type->rampindexes-1, frac);\n\t\t\t\t\t\t\ts2 = min(type->rampindexes-1, s1+1);\n\t\t\t\t\t\t\tfrac -= s1;\n\t\t\t\t\t\t\tVectorInterpolate(type->ramp[s1].rgb, frac, type->ramp[s2].rgb, d->rgba);\n\t\t\t\t\t\t\tFloatInterpolate(type->ramp[s1].alpha, frac, type->ramp[s2].alpha, d->rgba[3]);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase RAMP_DELTA:\t//particle ramps\n\t\t\t\t\t\tramp = type->ramp + (int)(type->rampindexes * (type->die - (d->die - particletime)) / type->die);\n\t\t\t\t\t\tVectorMA(d->rgba, pframetime, ramp->rgb, d->rgba);\n\t\t\t\t\t\td->rgba[3] -= pframetime*ramp->alpha;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase RAMP_NONE:\t//particle changes acording to it's preset properties.\n\t\t\t\t\t\tif (particletime < (d->die-type->die+type->rgbchangetime))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\td->rgba[0] += pframetime*type->rgbchange[0];\n\t\t\t\t\t\t\td->rgba[1] += pframetime*type->rgbchange[1];\n\t\t\t\t\t\t\td->rgba[2] += pframetime*type->rgbchange[2];\n\t\t\t\t\t\t}\n\t\t\t\t\t\td->rgba[3] += pframetime*type->alphachange;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (cl_numstrisvert - scenetri->firstvert >= MAX_INDICIES-6)\n\t\t\t\t{\n\t\t\t\t\t//generate a new mesh if the old one overflowed. yay smc...\n\t\t\t\t\tif (cl_numstris == cl_maxstris)\n\t\t\t\t\t{\n\t\t\t\t\t\tcl_maxstris+=8;\n\t\t\t\t\t\tcl_stris = BZ_Realloc(cl_stris, sizeof(*cl_stris)*cl_maxstris);\n\t\t\t\t\t}\n\t\t\t\t\tscenetri = &cl_stris[cl_numstris++];\n\t\t\t\t\tscenetri->shader = scenetri[-1].shader;\n\t\t\t\t\tscenetri->firstidx = cl_numstrisidx;\n\t\t\t\t\tscenetri->firstvert = cl_numstrisvert;\n\t\t\t\t\tscenetri->flags = scenetri[-1].flags;\n\t\t\t\t\tscenetri->numvert = 0;\n\t\t\t\t\tscenetri->numidx = 0;\n\t\t\t\t}\n\t\t\t\tR_AddClippedDecal(scenetri, d, type->slooks);\n\t\t\t}\n\t\t}\n\n\t\tbdraw = NULL;\n\t\tpdraw = NULL;\n\t\ttdraw = NULL;\n\t\tbatchflags = 0;\n\n\t\t// set drawing methods by type and cvars and hope branch\n\t\t// prediction takes care of the rest\n\t\tswitch(type->looks.type)\n\t\t{\n\t\tcase PT_INVISIBLE:\n\t\t\tbreak;\n\t\tcase PT_BEAM:\n\t\t\tbdraw = GL_DrawParticleBeam;\n\t\t\tbreak;\n\t\tcase PT_VBEAM:\n\t\t\tbdraw = GL_DrawParticleVBeam;\n\t\t\tbreak;\n\t\tcase PT_CDECAL:\n\t\t\tbreak;\n\t\tcase PT_UDECAL:\n\t\t\ttdraw = R_AddUnclippedDecal;\n\t\t\tbreak;\n\t\tcase PT_NORMAL:\n\t\t\tpdraw = GL_DrawTexturedParticle;\n\t\t\ttdraw = R_AddTexturedParticle;\n\t\t\tbreak;\n\t\tcase PT_SPARK:\n\t\t\ttdraw = R_AddLineSparkParticle;\n\t\t\tbatchflags = BEF_LINES;\n\t\t\tbreak;\n\t\tcase PT_SPARKFAN:\n\t\t\tpdraw = sparkfanparticles;\n\t\t\tbreak;\n\t\tcase PT_TEXTUREDSPARK:\n\t\t\tpdraw = sparktexturedparticles;\n\t\t\ttdraw = R_AddTSparkParticle;\n\t\t\tbreak;\n\t\t}\n\n\t\tif (!tdraw || (type->looks.shader->sort == SHADER_SORT_BLEND && pdraw))\n\t\t\tscenetri = NULL;\n\t\telse if (cl_numstris && cl_stris[cl_numstris-1].shader == type->looks.shader && cl_stris[cl_numstris-1].flags == batchflags)\n\t\t\tscenetri = &cl_stris[cl_numstris-1];\n\t\telse\n\t\t{\n\t\t\tif (cl_numstris == cl_maxstris)\n\t\t\t{\n\t\t\t\tcl_maxstris+=8;\n\t\t\t\tcl_stris = BZ_Realloc(cl_stris, sizeof(*cl_stris)*cl_maxstris);\n\t\t\t}\n\t\t\tscenetri = &cl_stris[cl_numstris++];\n\t\t\tscenetri->shader = type->looks.shader;\n\t\t\tscenetri->firstidx = cl_numstrisidx;\n\t\t\tscenetri->firstvert = cl_numstrisvert;\n\t\t\tscenetri->flags = batchflags;\n\t\t\tscenetri->numvert = 0;\n\t\t\tscenetri->numidx = 0;\n\t\t}\n\n\t\tif (!type->die)\n\t\t{\n\t\t\twhile ((p=type->particles))\n\t\t\t{\n\t\t\t\tif (scenetri)\n\t\t\t\t{\n\t\t\t\t\tif (cl_numstrisvert - scenetri->firstvert >= MAX_INDICIES-6)\n\t\t\t\t\t{\n\t\t\t\t\t\t//generate a new mesh if the old one overflowed. yay smc...\n\t\t\t\t\t\tif (cl_numstris == cl_maxstris)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcl_maxstris+=8;\n\t\t\t\t\t\t\tcl_stris = BZ_Realloc(cl_stris, sizeof(*cl_stris)*cl_maxstris);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tscenetri = &cl_stris[cl_numstris++];\n\t\t\t\t\t\tscenetri->shader = scenetri[-1].shader;\n\t\t\t\t\t\tscenetri->firstidx = cl_numstrisidx;\n\t\t\t\t\t\tscenetri->firstvert = cl_numstrisvert;\n\t\t\t\t\t\tscenetri->flags = scenetri[-1].flags;\n\t\t\t\t\t\tscenetri->numvert = 0;\n\t\t\t\t\t\tscenetri->numidx = 0;\n\t\t\t\t\t}\n\t\t\t\t\ttdraw(scenetri, p, type->slooks);\n\t\t\t\t}\n\t\t\t\telse if (pdraw)\n\t\t\t\t\tRQ_AddDistReorder(pdraw, p, type->slooks, p->org);\n\n\t\t\t\t// make sure emitter runs at least once\n\t\t\t\tif (type->emit >= 0 && type->emitstart <= 0 && pframetime)\n\t\t\t\t\tP_RunParticleEffectType(p->org, p->vel, pframetime, type->emit);\n\n\t\t\t\t// make sure stain effect runs\n\t\t\t\tif (type->stainonimpact && r_bloodstains.value)\n\t\t\t\t{\n\t\t\t\t\tif (traces-->0&&CL_TraceLine(oldorg, p->org, stop, normal, NULL)<1)\n\t\t\t\t\t{\n\t\t\t\t\t\tSurf_AddStain(stop,\t(p->rgba[1]*-10+p->rgba[2]*-10),\n\t\t\t\t\t\t\t\t\t\t\t(p->rgba[0]*-10+p->rgba[2]*-10),\n\t\t\t\t\t\t\t\t\t\t\t(p->rgba[0]*-10+p->rgba[1]*-10),\n\t\t\t\t\t\t\t\t\t\t\t30*p->rgba[3]*type->stainonimpact*r_bloodstains.value);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\ttype->particles = p->next;\n//\t\t\t\tp->next = free_particles;\n//\t\t\t\tfree_particles = p;\n\t\t\t\tp->next = kill_list;\n\t\t\t\tkill_list = p;\n\t\t\t\tif (!kill_first) // branch here is probably faster than list traversal later\n\t\t\t\t\tkill_first = p;\n\t\t\t}\n\n\t\t\tif (type->beams)\n\t\t\t{\n\t\t\t\tb = type->beams;\n\t\t\t}\n\n\t\t\twhile ((b=type->beams) && (b->flags & BS_DEAD))\n\t\t\t{\n\t\t\t\ttype->beams = b->next;\n\t\t\t\tb->next = free_beams;\n\t\t\t\tfree_beams = b;\n\t\t\t}\n\n\t\t\twhile (b)\n\t\t\t{\n\t\t\t\tif (!(b->flags & BS_NODRAW))\n\t\t\t\t{\n\t\t\t\t\t// no BS_NODRAW implies b->next != NULL\n\t\t\t\t\t// BS_NODRAW should imply b->next == NULL or b->next->flags & BS_DEAD\n\t\t\t\t\tVectorCopy(b->next->p->org, stop);\n\t\t\t\t\tVectorCopy(b->p->org, oldorg);\n\t\t\t\t\tVectorSubtract(stop, oldorg, b->next->dir);\n\t\t\t\t\tVectorNormalize(b->next->dir);\n\t\t\t\t\tif (bdraw)\n\t\t\t\t\t{\n\t\t\t\t\t\tVectorAdd(stop, oldorg, stop);\n\t\t\t\t\t\tVectorScale(stop, 0.5, stop);\n\n\t\t\t\t\t\tRQ_AddDistReorder(bdraw, b, type->slooks, stop);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// clean up dead entries ahead of current\n\t\t\t\tfor ( ;; )\n\t\t\t\t{\n\t\t\t\t\tbkill = b->next;\n\t\t\t\t\tif (bkill && (bkill->flags & BS_DEAD))\n\t\t\t\t\t{\n\t\t\t\t\t\tb->next = bkill->next;\n\t\t\t\t\t\tbkill->next = free_beams;\n\t\t\t\t\t\tfree_beams = bkill;\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tb->flags |= BS_DEAD;\n\t\t\t\tb = b->next;\n\t\t\t}\n\n\t\t\tgoto endtype;\n\t\t}\n\n\t\t//kill off early ones.\n\t\tif (type->emittime < 0)\n\t\t{\n\t\t\tfor ( ;; )\n\t\t\t{\n\t\t\t\tkill = type->particles;\n\t\t\t\tif (kill && kill->die < particletime)\n\t\t\t\t{\n\t\t\t\t\tP_DelinkTrailstate(&kill->state.trailstate);\n\t\t\t\t\ttype->particles = kill->next;\n\t\t\t\t\tkill->next = kill_list;\n\t\t\t\t\tkill_list = kill;\n\t\t\t\t\tif (!kill_first)\n\t\t\t\t\t\tkill_first = kill;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tfor ( ;; )\n\t\t\t{\n\t\t\t\tkill = type->particles;\n\t\t\t\tif (kill && kill->die < particletime)\n\t\t\t\t{\n\t\t\t\t\ttype->particles = kill->next;\n\t\t\t\t\tkill->next = kill_list;\n\t\t\t\t\tkill_list = kill;\n\t\t\t\t\tif (!kill_first)\n\t\t\t\t\t\tkill_first = kill;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tgrav = type->gravity*pframetime;\n\t\tfriction[0] = 1 - type->friction[0]*pframetime;\n\t\tfriction[1] = 1 - type->friction[1]*pframetime;\n\t\tfriction[2] = 1 - type->friction[2]*pframetime;\n\n\t\tfor (p=type->particles ; p ; p=p->next)\n\t\t{\n\t\t\tif (type->emittime < 0)\n\t\t\t{\n\t\t\t\tfor ( ;; )\n\t\t\t\t{\n\t\t\t\t\tkill = p->next;\n\t\t\t\t\tif (kill && kill->die < particletime)\n\t\t\t\t\t{\n\t\t\t\t\t\tP_DelinkTrailstate(&kill->state.trailstate);\n\t\t\t\t\t\tp->next = kill->next;\n\t\t\t\t\t\tkill->next = kill_list;\n\t\t\t\t\t\tkill_list = kill;\n\t\t\t\t\t\tif (!kill_first)\n\t\t\t\t\t\t\tkill_first = kill;\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tfor ( ;; )\n\t\t\t\t{\n\t\t\t\t\tkill = p->next;\n\t\t\t\t\tif (kill && kill->die < particletime)\n\t\t\t\t\t{\n\t\t\t\t\t\tp->next = kill->next;\n\t\t\t\t\t\tkill->next = kill_list;\n\t\t\t\t\t\tkill_list = kill;\n\t\t\t\t\t\tif (!kill_first)\n\t\t\t\t\t\t\tkill_first = kill;\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tVectorCopy(p->org, oldorg);\n\t\t\tif (type->flags & PT_VELOCITY)\n\t\t\t{\n\t\t\t\tp->org[0] += p->vel[0]*pframetime;\n\t\t\t\tp->org[1] += p->vel[1]*pframetime;\n\t\t\t\tp->org[2] += p->vel[2]*pframetime;\n\t\t\t\tif (type->flags & PT_FRICTION)\n\t\t\t\t{\n\t\t\t\t\tp->vel[0] *= friction[0];\n\t\t\t\t\tp->vel[1] *= friction[1];\n\t\t\t\t\tp->vel[2] *= friction[2];\n\t\t\t\t}\n\t\t\t\tif (type->flurry && doflurry)\n\t\t\t\t{\t//these should probably be partially synced, \n\t\t\t\t\tp->vel[0] += crandom() * type->flurry;\n\t\t\t\t\tp->vel[1] += crandom() * type->flurry;\n\t\t\t\t}\n\t\t\t\tp->vel[2] -= grav;\n\t\t\t}\n\n\t\t\tif (type->viewspacefrac)\n\t\t\t{\n\t\t\t\tvec3_t tmp;\n\t\t\t\tMatrix4x4_CM_Transform3(viewtranslation, p->org, tmp);\n\t\t\t\tVectorInterpolate(p->org, type->viewspacefrac, tmp, p->org);\n\t\t\t\tMatrix4x4_CM_Transform3x3(viewtranslation, p->vel, tmp);\n\t\t\t\tVectorInterpolate(p->vel, type->viewspacefrac, tmp, p->vel);\n\t\t\t}\n\n\t\t\tp->angle += p->rotationspeed*pframetime;\n\n\t\t\tswitch (type->rampmode)\n\t\t\t{\n\t\t\tcase RAMP_NEAREST:\n\t\t\t\trampind = (int)(type->rampindexes * (type->die - (p->die - particletime)) / type->die);\n\t\t\t\tif (rampind >= type->rampindexes)\n\t\t\t\t\trampind = type->rampindexes - 1;\n\t\t\t\tramp = type->ramp + rampind;\n\t\t\t\tVectorCopy(ramp->rgb, p->rgba);\n\t\t\t\tp->rgba[3] = ramp->alpha;\n\t\t\t\tp->scale = ramp->scale;\n\t\t\t\tbreak;\n\t\t\tcase RAMP_LERP:\n\t\t\t\t{\n\t\t\t\t\tfloat frac = (type->rampindexes * (type->die - (p->die - particletime)) / type->die);\n\t\t\t\t\tint s1, s2;\n\t\t\t\t\ts1 = min(type->rampindexes-1, frac);\n\t\t\t\t\ts2 = min(type->rampindexes-1, s1+1);\n\t\t\t\t\tfrac -= s1;\n\t\t\t\t\tVectorInterpolate(type->ramp[s1].rgb, frac, type->ramp[s2].rgb, p->rgba);\n\t\t\t\t\tFloatInterpolate(type->ramp[s1].alpha, frac, type->ramp[s2].alpha, p->rgba[3]);\n\t\t\t\t\tFloatInterpolate(type->ramp[s1].scale, frac, type->ramp[s2].scale, p->scale);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase RAMP_DELTA:\t//particle ramps\n\t\t\t\trampind = (int)(type->rampindexes * (type->die - (p->die - particletime)) / type->die);\n\t\t\t\tif (rampind >= type->rampindexes)\n\t\t\t\t\trampind = type->rampindexes - 1;\n\t\t\t\tramp = type->ramp + rampind;\n\t\t\t\tVectorMA(p->rgba, pframetime, ramp->rgb, p->rgba);\n\t\t\t\tp->rgba[3] -= pframetime*ramp->alpha;\n\t\t\t\tp->scale += pframetime*ramp->scale;\n\t\t\t\tbreak;\n\t\t\tcase RAMP_NONE:\t//particle changes acording to it's preset properties.\n\t\t\t\tif (particletime < (p->die-type->die+type->rgbchangetime))\n\t\t\t\t{\n\t\t\t\t\tp->rgba[0] += pframetime*type->rgbchange[0];\n\t\t\t\t\tp->rgba[1] += pframetime*type->rgbchange[1];\n\t\t\t\t\tp->rgba[2] += pframetime*type->rgbchange[2];\n\t\t\t\t}\n\t\t\t\tp->rgba[3] += pframetime*type->alphachange;\n\t\t\t\tp->scale += pframetime*type->scaledelta;\n\t\t\t}\n\n\t\t\tif (type->emit >= 0)\n\t\t\t{\n\t\t\t\tif (type->emittime < 0)\n\t\t\t\t\tP_ParticleTrail(oldorg, p->org, type->emit, pframetime, 0, NULL, &p->state.trailstate);\n\t\t\t\telse if (p->state.nextemit < particletime)\n\t\t\t\t{\n\t\t\t\t\tp->state.nextemit = particletime + type->emittime + frandom()*type->emitrand;\n\t\t\t\t\tP_RunParticleEffectType(p->org, p->vel, 1, type->emit);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (type->cliptype>=0 && r_bouncysparks.ival)\n\t\t\t{\n\t\t\t\tVectorSubtract(p->org, p->oldorg, stop);\n\t\t\t\tif (DotProduct(stop,stop) > 10*10)\n\t\t\t\t{\n\t\t\t\t\tint e;\n\t\t\t\t\tif (traces-->0&&CL_TraceLine(p->oldorg, p->org, stop, normal, &e)<1)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (type->stainonimpact && r_bloodstains.value)\n\t\t\t\t\t\t\tSurf_AddStain(stop,\tp->rgba[1]*-10+p->rgba[2]*-10,\n\t\t\t\t\t\t\t\t\t\t\t\tp->rgba[0]*-10+p->rgba[2]*-10,\n\t\t\t\t\t\t\t\t\t\t\t\tp->rgba[0]*-10+p->rgba[1]*-10,\n\t\t\t\t\t\t\t\t\t\t\t\t30*p->rgba[3]*r_bloodstains.value);\n\n\t\t\t\t\t\tif (type->clipbounce < 0)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tp->die = -1;\n\t\t\t\t\t\t\tif (type->clipbounce == -2)\n\t\t\t\t\t\t\t{\t//this type of particle splatters itself as a decal when it hits a wall.\n\t\t\t\t\t\t\t\tdecalctx_t ctx;\n\t\t\t\t\t\t\t\tfloat m;\n\t\t\t\t\t\t\t\tvec3_t vec={0.5, 0.5, 0.431};\n\t\t\t\t\t\t\t\tmodel_t *model;\n\t\t\t\t\t\t\t\tfloat rotangle;\n\n\t\t\t\t\t\t\t\tctx.entity = e;\n\t\t\t\t\t\t\t\tif (!ctx.entity)\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tmodel = cl.worldmodel;\n\t\t\t\t\t\t\t\t\tVectorCopy(p->org, ctx.center);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\telse if ((unsigned)ctx.entity < (unsigned)cl.maxlerpents)\n\t\t\t\t\t\t\t\t{\t//this trace hit a door or something.\n\t\t\t\t\t\t\t\t\tlerpents_t *le = cl.lerpents+ctx.entity;\n\t\t\t\t\t\t\t\t\tmodel = cl.model_precache[le->entstate->modelindex];\n\t\t\t\t\t\t\t\t\tVectorSubtract(p->org, le->origin, ctx.center);\n\t\t\t\t\t\t\t\t\t//FIXME: rotate center+normal around entity.\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t\tcontinue;\t//err, no idea.\n\n\t\t\t\t\t\t\t\tVectorNegate(normal, ctx.normal);\n\t\t\t\t\t\t\t\tVectorNormalize(ctx.normal);\n\n\t\t\t\t\t\t\t\tVectorNormalize(vec);\n\t\t\t\t\t\t\t\tCrossProduct(ctx.normal, vec, ctx.tangent1);\n\t\t\t\t\t\t\t\trotangle = type->rotationstartmin+frandom()*type->rotationstartrand;\n\t\t\t\t\t\t\t\trotangle *= 180/M_PI; //gah! Matrix4x4_CM_NewRotation takes degrees but we already converted to radians.\n\t\t\t\t\t\t\t\tMatrix4x4_CM_Transform3(Matrix4x4_CM_NewRotation(rotangle, ctx.normal[0], ctx.normal[1], ctx.normal[2]), ctx.tangent1, ctx.tangent2);\n\t\t\t\t\t\t\t\tCrossProduct(ctx.normal, ctx.tangent2, ctx.tangent1);\n\n\t\t\t\t\t\t\t\tVectorNormalize(ctx.tangent1);\n\t\t\t\t\t\t\t\tVectorNormalize(ctx.tangent2);\n\n\t\t\t\t\t\t\t\tctx.ptype = type;\n\t\t\t\t\t\t\t\tctx.scale1 = type->s2 - type->s1;\n\t\t\t\t\t\t\t\tctx.bias1 = type->s1 + ctx.scale1/2;\n\t\t\t\t\t\t\t\tctx.scale2 = type->t2 - type->t1;\n\t\t\t\t\t\t\t\tctx.bias2 = type->t1 + ctx.scale2/2;\n\t\t\t\t\t\t\t\tm = p->scale*(0.5+frandom()*0.5);\t//decals should be a little bigger, for some reason.\n\t\t\t\t\t\t\t\tctx.scale0 = 2.0 / m;\n\t\t\t\t\t\t\t\tctx.scale1 /= m;\n\t\t\t\t\t\t\t\tctx.scale2 /= m;\n\n\t\t\t\t\t\t\t\t//inserts decals through a callback.\n\t\t\t\t\t\t\t\tMod_ClipDecal(model, ctx.center, ctx.normal, ctx.tangent2, ctx.tangent1, m, type->surfflagmask, type->surfflagmatch, PScript_AddDecals, &ctx);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (part_type + type->cliptype == type)\n\t\t\t\t\t\t{\t//bounce\n\t\t\t\t\t\t\tdist = DotProduct(p->vel, normal);// * (-1-(rand()/(float)0x7fff)/2);\n\t\t\t\t\t\t\tdist *= -type->clipbounce;\n\t\t\t\t\t\t\tVectorMA(p->vel, dist, normal, p->vel);\n\t\t\t\t\t\t\tVectorCopy(stop, p->org);\n\n\t\t\t\t\t\t\tif (!*type->texname && Length(p->vel)<1000*pframetime && type->looks.type == PT_NORMAL)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tp->die = -1;\n\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tp->die = -1;\n\t\t\t\t\t\t\tVectorNormalize(p->vel);\n\n\t\t\t\t\t\t\tif (type->clipbounce)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tVectorScale(normal, type->clipbounce, normal);\n\t\t\t\t\t\t\t\tP_RunParticleEffectType(stop, normal, type->clipcount/part_type[type->cliptype].count, type->cliptype);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\tP_RunParticleEffectType(stop, p->vel, type->clipcount/part_type[type->cliptype].count, type->cliptype);\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tVectorCopy(p->org, p->oldorg);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (type->stainonimpact && r_bloodstains.value)\n\t\t\t{\n\t\t\t\tVectorSubtract(p->org, p->oldorg, stop);\n\t\t\t\tif (DotProduct(stop,stop) > 10*10)\n\t\t\t\t{\n\t\t\t\t\tif (traces-->0&&CL_TraceLine(p->oldorg, p->org, stop, normal, NULL)<1)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (type->stainonimpact < 0)\n\t\t\t\t\t\t\tSurf_AddStain(stop,\t(p->rgba[0]*-1),\n\t\t\t\t\t\t\t\t\t\t\t\t(p->rgba[1]*-1),\n\t\t\t\t\t\t\t\t\t\t\t\t(p->rgba[2]*-1),\n\t\t\t\t\t\t\t\t\t\t\t\tp->scale*-type->stainonimpact*r_bloodstains.value);\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tSurf_AddStain(stop,\t(p->rgba[1]*-10+p->rgba[2]*-10),\n\t\t\t\t\t\t\t\t\t\t\t\t(p->rgba[0]*-10+p->rgba[2]*-10),\n\t\t\t\t\t\t\t\t\t\t\t\t(p->rgba[0]*-10+p->rgba[1]*-10),\n\t\t\t\t\t\t\t\t\t\t\t\t30*p->rgba[3]*type->stainonimpact*r_bloodstains.value);\n\t\t\t\t\t\tp->die = -1;\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tVectorCopy(p->org, p->oldorg);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (scenetri)\n\t\t\t{\n\t\t\t\tif (cl_numstrisvert - scenetri->firstvert >= MAX_INDICIES-6)\n\t\t\t\t{\n\t\t\t\t\t//generate a new mesh if the old one overflowed. yay smc...\n\t\t\t\t\tif (cl_numstris == cl_maxstris)\n\t\t\t\t\t{\n\t\t\t\t\t\tcl_maxstris+=8;\n\t\t\t\t\t\tcl_stris = BZ_Realloc(cl_stris, sizeof(*cl_stris)*cl_maxstris);\n\t\t\t\t\t}\n\t\t\t\t\tscenetri = &cl_stris[cl_numstris++];\n\t\t\t\t\tscenetri->shader = scenetri[-1].shader;\n\t\t\t\t\tscenetri->firstidx = cl_numstrisidx;\n\t\t\t\t\tscenetri->firstvert = cl_numstrisvert;\n\t\t\t\t\tscenetri->flags = scenetri[-1].flags;\n\t\t\t\t\tscenetri->numvert = 0;\n\t\t\t\t\tscenetri->numidx = 0;\n\t\t\t\t}\n\t\t\t\ttdraw(scenetri, p, type->slooks);\n\t\t\t}\n\t\t\telse if (pdraw)\n\t\t\t\tRQ_AddDistReorder((void*)pdraw, p, type->slooks, p->org);\n\t\t}\n\n\t\t// beams are dealt with here\n\n\t\t// kill early entries\n\t\tfor ( ;; )\n\t\t{\n\t\t\tbkill = type->beams;\n\t\t\tif (bkill && (bkill->flags & BS_DEAD || bkill->p->die < particletime) && !(bkill->flags & BS_LASTSEG))\n\t\t\t{\n\t\t\t\ttype->beams = bkill->next;\n\t\t\t\tbkill->next = free_beams;\n\t\t\t\tfree_beams = bkill;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\n\n\t\tb = type->beams;\n\t\tif (b)\n\t\t{\n\t\t\tfor ( ;; )\n\t\t\t{\n\t\t\t\tif (b->next)\n\t\t\t\t{\n\t\t\t\t\t// mark dead entries\n\t\t\t\t\tif (b->flags & (BS_LASTSEG|BS_DEAD|BS_NODRAW))\n\t\t\t\t\t{\n\t\t\t\t\t\t// kill some more dead entries\n\t\t\t\t\t\tfor ( ;; )\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tbkill = b->next;\n\t\t\t\t\t\t\tif (bkill && (bkill->flags & BS_DEAD) && !(bkill->flags & BS_LASTSEG))\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tb->next = bkill->next;\n\t\t\t\t\t\t\t\tbkill->next = free_beams;\n\t\t\t\t\t\t\t\tfree_beams = bkill;\n\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (!bkill) // have to check so we don't hit NULL->next\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tif (!(b->next->flags & BS_DEAD))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tVectorCopy(b->next->p->org, stop);\n\t\t\t\t\t\t\tVectorCopy(b->p->org, oldorg);\n\t\t\t\t\t\t\tVectorSubtract(stop, oldorg, b->next->dir);\n\t\t\t\t\t\t\tVectorNormalize(b->next->dir);\n\t\t\t\t\t\t\tif (bdraw)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tVectorAdd(stop, oldorg, stop);\n\t\t\t\t\t\t\t\tVectorScale(stop, 0.5, stop);\n\n\t\t\t\t\t\t\t\tRQ_AddDistReorder(bdraw, b, type->slooks, stop);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (b->p->die < particletime)\n\t\t\t\t\t\t\tb->flags |= BS_DEAD;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tif (b->p->die < particletime) // end of the list check\n\t\t\t\t\t\tb->flags |= BS_DEAD;\n\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tif (b->p->die < particletime)\n\t\t\t\t\tb->flags |= BS_DEAD;\n\n\t\t\t\tb = b->next;\n\t\t\t}\n\t\t}\n\nendtype:\n\n\t\t// delete from run list if necessary\n\t\tif (!type->particles && !type->beams && !type->clippeddecals)\n\t\t{\n\t\t\tif (type->nexttorun)\n\t\t\t\ttype->nexttorun->runlink = type->runlink;\n\t\t\t*type->runlink = type->nexttorun;\n\t\t\ttype->runlink = NULL;\n\t\t\ttype->state &= ~PS_INRUNLIST;\n\t\t}\n\t}\n\n\tRSpeedEnd(RSPEED_PARTICLES);\n\n\t// lazy delete for particles is done here\n\tif (kill_list)\n\t{\n\t\tkill_first->next = free_particles;\n\t\tfree_particles = kill_list;\n\t}\n\n\tparticletime += pframetime;\n}\n\n/*\n===============\nR_DrawParticles\n===============\n*/\nstatic void PScript_DrawParticles (void)\n{\n\tif (r_part_rain.value)\n\t{\n\t\tentity_t *ent;\n\t\tint i;\n\t\t\n\t\tP_AddRainParticles(cl.worldmodel, r_worldentity.axis, r_worldentity.origin, pframetime);\n\n\t\tfor (i = 0; i < cl_numvisedicts ; i++)\n\t\t{\n\t\t\tent = &cl_visedicts[i];\n\t\t\tif (!ent->model || ent->model->loadstate != MLS_LOADED)\n\t\t\t\tcontinue;\n\n\t\t\t//this timer, as well as the per-tri timer, are unable to deal with certain rates+sizes. it would be good to fix that...\n\t\t\t//it would also be nice to do mdls too...\n\t\t\tP_AddRainParticles(ent->model, ent->axis, ent->origin, pframetime);\n\t\t}\n\t}\n\n\tPScript_DrawParticleTypes();\n\n\tif (fallback)\n\t\tfallback->DrawParticles();\n}\n\n\nparticleengine_t pe_script =\n{\n\t\"script\",\n\t\"fte\",\n\n\tPScript_FindParticleType,\n\tPScript_Query,\n\n\tPScript_RunParticleEffectTypeString,\n\tPScript_ParticleTrail,\n\tPScript_RunParticleEffectState,\n\tPScript_RunParticleWeather,\n\tPScript_RunParticleCube,\n\tPScript_RunParticleEffect,\n\tPScript_RunParticleEffect2,\n\tPScript_RunParticleEffect3,\n\tPScript_RunParticleEffect4,\n\tPScript_RunParticleEffectPalette,\n\n\tPScript_ParticleTrailIndex,\n\tPScript_InitParticles,\n\tPScript_Shutdown,\n\tPScript_DelinkTrailstate,\n\tPScript_ClearParticles,\n\tPScript_DrawParticles\n};\n\n#endif\n#endif\n\n"
  },
  {
    "path": "engine/client/pr_clcmd.c",
    "content": "//file for builtin implementations relevent to only clientside VMs (menu+csqc).\n#include \"quakedef.h\"\n\n#include \"pr_common.h\"\n#include \"shader.h\"\n\n#if defined(CSQC_DAT) || defined(MENU_DAT)\n\n//these two global qcinput variables are the current scan code being passed to qc, if valid. this protects against protected apis where the qc just passes stuff through.\nint qcinput_scan;\nint qcinput_unicode;\n\n//QC key codes are based upon DP's keycode constants. This is on account of menu.dat coming first.\nint MP_TranslateFTEtoQCCodes(keynum_t code)\n{\n\tsafeswitch(code)\n\t{\n\tcase K_TAB:\t\t\t\treturn 9;\n\tcase K_ENTER:\t\t\treturn 13;\n\tcase K_ESCAPE:\t\t\treturn 27;\n\tcase K_SPACE:\t\t\treturn 32;\n\tcase K_BACKSPACE:\t\treturn 127;\n\tcase K_UPARROW:\t\t\treturn 128;\n\tcase K_DOWNARROW:\t\treturn 129;\n\tcase K_LEFTARROW:\t\treturn 130;\n\tcase K_RIGHTARROW:\t\treturn 131;\n\tcase K_LALT:\t\t\treturn 132;\n\tcase K_RALT:\t\t\treturn -K_RALT;\n\tcase K_LCTRL:\t\t\treturn 133;\n\tcase K_RCTRL:\t\t\treturn -K_RCTRL;\n\tcase K_LSHIFT:\t\t\treturn 134;\n\tcase K_RSHIFT:\t\t\treturn -K_RSHIFT;\n\tcase K_F1:\t\t\t\treturn 135;\n\tcase K_F2:\t\t\t\treturn 136;\n\tcase K_F3:\t\t\t\treturn 137;\n\tcase K_F4:\t\t\t\treturn 138;\n\tcase K_F5:\t\t\t\treturn 139;\n\tcase K_F6:\t\t\t\treturn 140;\n\tcase K_F7:\t\t\t\treturn 141;\n\tcase K_F8:\t\t\t\treturn 142;\n\tcase K_F9:\t\t\t\treturn 143;\n\tcase K_F10:\t\t\t\treturn 144;\n\tcase K_F11:\t\t\t\treturn 145;\n\tcase K_F12:\t\t\t\treturn 146;\n\tcase K_INS:\t\t\t\treturn 147;\n\tcase K_DEL:\t\t\t\treturn 148;\n\tcase K_PGDN:\t\t\treturn 149;\n\tcase K_PGUP:\t\t\treturn 150;\n\tcase K_HOME:\t\t\treturn 151;\n\tcase K_END:\t\t\t\treturn 152;\n\tcase K_PAUSE:\t\t\treturn 153;\n\tcase K_KP_NUMLOCK:\t\treturn 154;\n\tcase K_CAPSLOCK:\t\treturn 155;\n\tcase K_SCRLCK:\t\t\treturn 156;\n\tcase K_KP_INS:\t\t\treturn 157;\n\tcase K_KP_END:\t\t\treturn 158;\n\tcase K_KP_DOWNARROW:\treturn 159;\n\tcase K_KP_PGDN:\t\t\treturn 160;\n\tcase K_KP_LEFTARROW:\treturn 161;\n\tcase K_KP_5:\t\t\treturn 162;\n\tcase K_KP_RIGHTARROW:\treturn 163;\n\tcase K_KP_HOME:\t\t\treturn 164;\n\tcase K_KP_UPARROW:\t\treturn 165;\n\tcase K_KP_PGUP:\t\t\treturn 166;\n\tcase K_KP_DEL:\t\t\treturn 167;\n\tcase K_KP_SLASH:\t\treturn 168;\n\tcase K_KP_STAR:\t\t\treturn 169;\n\tcase K_KP_MINUS:\t\treturn 170;\n\tcase K_KP_PLUS:\t\t\treturn 171;\n\tcase K_KP_ENTER:\t\treturn 172;\n\tcase K_KP_EQUALS:\t\treturn 173;\n\tcase K_PRINTSCREEN:\t\treturn 174;\n\n\tcase K_MOUSE1:\t\t\treturn 512;\n\tcase K_MOUSE2:\t\t\treturn 513;\n\tcase K_MOUSE3:\t\t\treturn 514;\n\tcase K_MWHEELUP:\t\treturn 515;\n\tcase K_MWHEELDOWN:\t\treturn 516;\n\tcase K_MOUSE4:\t\t\treturn 517;\n\tcase K_MOUSE5:\t\t\treturn 518;\n\tcase K_MOUSE6:\t\t\treturn 519;\n\tcase K_MOUSE7:\t\t\treturn 520;\n\tcase K_MOUSE8:\t\t\treturn 521;\n\tcase K_MOUSE9:\t\t\treturn 522;\n\tcase K_MOUSE10:\t\t\treturn 523;\n//\tcase K_MOUSE11:\t\t\treturn 524;\n//\tcase K_MOUSE12:\t\t\treturn 525;\n//\tcase K_MOUSE13:\t\t\treturn 526;\n//\tcase K_MOUSE14:\t\t\treturn 527;\n//\tcase K_MOUSE15:\t\t\treturn 528;\n//\tcase K_MOUSE16:\t\t\treturn 529;\n\tcase K_TOUCH:\t\t\treturn 600;\n\tcase K_TOUCHSLIDE:\t\treturn 601;\n\tcase K_TOUCHTAP:\t\treturn 602;\n\tcase K_TOUCHLONG:\t\treturn 603;\n\n\tcase K_JOY1:\t\t\treturn 768;\n\tcase K_JOY2:\t\t\treturn 769;\n\tcase K_JOY3:\t\t\treturn 770;\n\tcase K_JOY4:\t\t\treturn 771;\n\tcase K_JOY5:\t\t\treturn 772;\n\tcase K_JOY6:\t\t\treturn 773;\n\tcase K_JOY7:\t\t\treturn 774;\n\tcase K_JOY8:\t\t\treturn 775;\n\tcase K_JOY9:\t\t\treturn 776;\n\tcase K_JOY10:\t\t\treturn 777;\n\tcase K_JOY11:\t\t\treturn 778;\n\tcase K_JOY12:\t\t\treturn 779;\n\tcase K_JOY13:\t\t\treturn 780;\n\tcase K_JOY14:\t\t\treturn 781;\n\tcase K_JOY15:\t\t\treturn 782;\n\tcase K_JOY16:\t\t\treturn 783;\n\tcase K_JOY17:\t\t\treturn 784;\n\tcase K_JOY18:\t\t\treturn 785;\n\tcase K_JOY19:\t\t\treturn 786;\n\tcase K_JOY20:\t\t\treturn 787;\n\tcase K_JOY21:\t\t\treturn 788;\n\tcase K_JOY22:\t\t\treturn 789;\n\tcase K_JOY23:\t\t\treturn 790;\n\tcase K_JOY24:\t\t\treturn 791;\n\tcase K_JOY25:\t\t\treturn 792;\n\tcase K_JOY26:\t\t\treturn 793;\n\tcase K_JOY27:\t\t\treturn 794;\n\tcase K_JOY28:\t\t\treturn 795;\n\tcase K_JOY29:\t\t\treturn 796;\n\tcase K_JOY30:\t\t\treturn 797;\n\tcase K_JOY31:\t\t\treturn 798;\n\tcase K_JOY32:\t\t\treturn 799;\n\n\tcase K_AUX1:\t\t\treturn 800;\n\tcase K_AUX2:\t\t\treturn 801;\n\tcase K_AUX3:\t\t\treturn 802;\n\tcase K_AUX4:\t\t\treturn 803;\n\tcase K_AUX5:\t\t\treturn 804;\n\tcase K_AUX6:\t\t\treturn 805;\n\tcase K_AUX7:\t\t\treturn 806;\n\tcase K_AUX8:\t\t\treturn 807;\n\tcase K_AUX9:\t\t\treturn 808;\n\tcase K_AUX10:\t\t\treturn 809;\n\tcase K_AUX11:\t\t\treturn 810;\n\tcase K_AUX12:\t\t\treturn 811;\n\tcase K_AUX13:\t\t\treturn 812;\n\tcase K_AUX14:\t\t\treturn 813;\n\tcase K_AUX15:\t\t\treturn 814;\n\tcase K_AUX16:\t\t\treturn 815;\n\n\tcase K_GP_DPAD_UP:\t\t\treturn 816;\n\tcase K_GP_DPAD_DOWN:\t\treturn 817;\n\tcase K_GP_DPAD_LEFT:\t\treturn 818;\n\tcase K_GP_DPAD_RIGHT:\t\treturn 819;\n\tcase K_GP_START:\t\t\treturn 820;\n\tcase K_GP_BACK:\t\t\t\treturn 821;\n\tcase K_GP_LEFT_STICK:\t\treturn 822;\n\tcase K_GP_RIGHT_STICK:\t\treturn 823;\n\tcase K_GP_LEFT_SHOULDER:\treturn 824;\n\tcase K_GP_RIGHT_SHOULDER:\treturn 825;\n\tcase K_GP_A:\t\t\t\treturn 826;\n\tcase K_GP_B:\t\t\t\treturn 827;\n\tcase K_GP_X:\t\t\t\treturn 828;\n\tcase K_GP_Y:\t\t\t\treturn 829;\n\tcase K_GP_LEFT_TRIGGER:\t\treturn 830;\n\tcase K_GP_RIGHT_TRIGGER:\treturn 831;\n\tcase K_GP_LEFT_THUMB_UP:\treturn 832;\n\tcase K_GP_LEFT_THUMB_DOWN:\treturn 833;\n\tcase K_GP_LEFT_THUMB_LEFT:\treturn 834;\n\tcase K_GP_LEFT_THUMB_RIGHT:\treturn 835;\n\tcase K_GP_RIGHT_THUMB_UP:\treturn 836;\n\tcase K_GP_RIGHT_THUMB_DOWN:\treturn 837;\n\tcase K_GP_RIGHT_THUMB_LEFT:\treturn 838;\n\tcase K_GP_RIGHT_THUMB_RIGHT:return 839;\n\tcase K_JOY_UP:\t\t\t\treturn 840;\n\tcase K_JOY_DOWN:\t\t\treturn 841;\n\tcase K_JOY_LEFT:\t\t\treturn 842;\n\tcase K_JOY_RIGHT:\t\t\treturn 843;\n\n\tcase K_GP_MISC1:\n\tcase K_GP_PADDLE1:\n\tcase K_GP_PADDLE2:\n\tcase K_GP_PADDLE3:\n\tcase K_GP_PADDLE4:\n\tcase K_GP_TOUCHPAD:\n\tcase K_GP_GUIDE:\n\tcase K_GP_UNKNOWN:\n\tcase K_MM_BROWSER_FAVORITES:\n\tcase K_MM_BROWSER_FORWARD:\n\tcase K_MM_BROWSER_BACK:\n\tcase K_MM_BROWSER_HOME:\n\tcase K_MM_BROWSER_REFRESH:\n\tcase K_MM_BROWSER_STOP:\n\tcase K_MM_VOLUME_MUTE:\n\tcase K_MM_TRACK_NEXT:\n\tcase K_MM_TRACK_PREV:\n\tcase K_MM_TRACK_STOP:\n\tcase K_MM_TRACK_PLAYPAUSE:\n\tcase K_F13:\n\tcase K_F14:\n\tcase K_F15:\n\tcase K_POWER:\n\tcase K_LWIN:\n\tcase K_RWIN:\n\tcase K_VOLUP:\n\tcase K_VOLDOWN:\n\tcase K_APP:\n\tcase K_SEARCH:\t\t\treturn -code;\n\n\tcase K_MAX:\n\tsafedefault:\n\t\tif (code == -1)\t//mod bug\n\t\t\treturn code;\n\t\tif (code < 0)\t//negative values are 'qc-native' keys, for stuff that the api lacks.\n\t\t\treturn -code;\n\t\tif (code >= 0 && code < 128)\t//ascii codes identical\n\t\t\treturn code;\n\t\treturn -code;\t//unknown key.\n\t}\n}\n\nkeynum_t MP_TranslateQCtoFTECodes(int code)\n{\n\tswitch(code)\n\t{\n\tcase 9:\t\t\treturn K_TAB;\n\tcase 13:\t\treturn K_ENTER;\n\tcase 27:\t\treturn K_ESCAPE;\n\tcase 32:\t\treturn K_SPACE;\n\tcase 127:\t\treturn K_BACKSPACE;\n\tcase 128:\t\treturn K_UPARROW;\n\tcase 129:\t\treturn K_DOWNARROW;\n\tcase 130:\t\treturn K_LEFTARROW;\n\tcase 131:\t\treturn K_RIGHTARROW;\n\tcase 132:\t\treturn K_LALT;\n\tcase 133:\t\treturn K_LCTRL;\n\tcase 134:\t\treturn K_LSHIFT;\n\tcase 135:\t\treturn K_F1;\n\tcase 136:\t\treturn K_F2;\n\tcase 137:\t\treturn K_F3;\n\tcase 138:\t\treturn K_F4;\n\tcase 139:\t\treturn K_F5;\n\tcase 140:\t\treturn K_F6;\n\tcase 141:\t\treturn K_F7;\n\tcase 142:\t\treturn K_F8;\n\tcase 143:\t\treturn K_F9;\n\tcase 144:\t\treturn K_F10;\n\tcase 145:\t\treturn K_F11;\n\tcase 146:\t\treturn K_F12;\n\tcase 147:\t\treturn K_INS;\n\tcase 148:\t\treturn K_DEL;\n\tcase 149:\t\treturn K_PGDN;\n\tcase 150:\t\treturn K_PGUP;\n\tcase 151:\t\treturn K_HOME;\n\tcase 152:\t\treturn K_END;\n\tcase 153:\t\treturn K_PAUSE;\n\tcase 154:\t\treturn K_KP_NUMLOCK;\n\tcase 155:\t\treturn K_CAPSLOCK;\n\tcase 156:\t\treturn K_SCRLCK;\n\tcase 157:\t\treturn K_KP_INS;\n\tcase 158:\t\treturn K_KP_END;\n\tcase 159:\t\treturn K_KP_DOWNARROW;\n\tcase 160:\t\treturn K_KP_PGDN;\n\tcase 161:\t\treturn K_KP_LEFTARROW;\n\tcase 162:\t\treturn K_KP_5;\n\tcase 163:\t\treturn K_KP_RIGHTARROW;\n\tcase 164:\t\treturn K_KP_HOME;\n\tcase 165:\t\treturn K_KP_UPARROW;\n\tcase 166:\t\treturn K_KP_PGUP;\n\tcase 167:\t\treturn K_KP_DEL;\n\tcase 168:\t\treturn K_KP_SLASH;\n\tcase 169:\t\treturn K_KP_STAR;\n\tcase 170:\t\treturn K_KP_MINUS;\n\tcase 171:\t\treturn K_KP_PLUS;\n\tcase 172:\t\treturn K_KP_ENTER;\n\tcase 173:\t\treturn K_KP_EQUALS;\n\tcase 174:\t\treturn K_PRINTSCREEN;\n\n\tcase 512:\t\treturn K_MOUSE1;\n\tcase 513:\t\treturn K_MOUSE2;\n\tcase 514:\t\treturn K_MOUSE3;\n\tcase 515:\t\treturn K_MWHEELUP;\n\tcase 516:\t\treturn K_MWHEELDOWN;\n\tcase 517:\t\treturn K_MOUSE4;\n\tcase 518:\t\treturn K_MOUSE5;\n\tcase 519:\t\treturn K_MOUSE6;\n\tcase 520:\t\treturn K_MOUSE7;\n\tcase 521:\t\treturn K_MOUSE8;\n\tcase 522:\t\treturn K_MOUSE9;\n\tcase 523:\t\treturn K_MOUSE10;\n//\tcase 524:\t\treturn K_MOUSE11;\n//\tcase 525:\t\treturn K_MOUSE12;\n//\tcase 526:\t\treturn K_MOUSE13;\n//\tcase 527:\t\treturn K_MOUSE14;\n//\tcase 528:\t\treturn K_MOUSE15;\n//\tcase 529:\t\treturn K_MOUSE16;\n\tcase 600:\t\treturn K_TOUCH;\n\tcase 601:\t\treturn K_TOUCHSLIDE;\n\tcase 602:\t\treturn K_TOUCHTAP;\n\tcase 603:\t\treturn K_TOUCHLONG;\n\n\tcase 768:\t\treturn K_JOY1;\n\tcase 769:\t\treturn K_JOY2;\n\tcase 770:\t\treturn K_JOY3;\n\tcase 771:\t\treturn K_JOY4;\n\tcase 772:\t\treturn K_JOY5;\n\tcase 773:\t\treturn K_JOY6;\n\tcase 774:\t\treturn K_JOY7;\n\tcase 775:\t\treturn K_JOY8;\n\tcase 776:\t\treturn K_JOY9;\n\tcase 777:\t\treturn K_JOY10;\n\tcase 778:\t\treturn K_JOY11;\n\tcase 779:\t\treturn K_JOY12;\n\tcase 780:\t\treturn K_JOY13;\n\tcase 781:\t\treturn K_JOY14;\n\tcase 782:\t\treturn K_JOY15;\n\tcase 783:\t\treturn K_JOY16;\n\tcase 784:\t\treturn K_JOY17;\n\tcase 785:\t\treturn K_JOY18;\n\tcase 786:\t\treturn K_JOY19;\n\tcase 787:\t\treturn K_JOY20;\n\tcase 788:\t\treturn K_JOY21;\n\tcase 789:\t\treturn K_JOY22;\n\tcase 790:\t\treturn K_JOY23;\n\tcase 791:\t\treturn K_JOY24;\n\tcase 792:\t\treturn K_JOY25;\n\tcase 793:\t\treturn K_JOY26;\n\tcase 794:\t\treturn K_JOY27;\n\tcase 795:\t\treturn K_JOY28;\n\tcase 796:\t\treturn K_JOY29;\n\tcase 797:\t\treturn K_JOY30;\n\tcase 798:\t\treturn K_JOY31;\n\tcase 799:\t\treturn K_JOY32;\n\n\tcase 800:\t\treturn K_AUX1;\n\tcase 801:\t\treturn K_AUX2;\n\tcase 802:\t\treturn K_AUX3;\n\tcase 803:\t\treturn K_AUX4;\n\tcase 804:\t\treturn K_AUX5;\n\tcase 805:\t\treturn K_AUX6;\n\tcase 806:\t\treturn K_AUX7;\n\tcase 807:\t\treturn K_AUX8;\n\tcase 808:\t\treturn K_AUX9;\n\tcase 809:\t\treturn K_AUX10;\n\tcase 810:\t\treturn K_AUX11;\n\tcase 811:\t\treturn K_AUX12;\n\tcase 812:\t\treturn K_AUX13;\n\tcase 813:\t\treturn K_AUX14;\n\tcase 814:\t\treturn K_AUX15;\n\tcase 815:\t\treturn K_AUX16;\n\n\tcase 816:\t\treturn K_GP_DPAD_UP;\n\tcase 817:\t\treturn K_GP_DPAD_DOWN;\n\tcase 818:\t\treturn K_GP_DPAD_LEFT;\n\tcase 819:\t\treturn K_GP_DPAD_RIGHT;\n\tcase 820:\t\treturn K_GP_START;\n\tcase 821:\t\treturn K_GP_BACK;\n\tcase 822:\t\treturn K_GP_LEFT_STICK;\n\tcase 823:\t\treturn K_GP_RIGHT_STICK;\n\tcase 824:\t\treturn K_GP_LEFT_SHOULDER;\n\tcase 825:\t\treturn K_GP_RIGHT_SHOULDER;\n\tcase 826:\t\treturn K_GP_A;\n\tcase 827:\t\treturn K_GP_B;\n\tcase 828:\t\treturn K_GP_X;\n\tcase 829:\t\treturn K_GP_Y;\n\tcase 830:\t\treturn K_GP_LEFT_TRIGGER;\n\tcase 831:\t\treturn K_GP_RIGHT_TRIGGER;\n\tcase 832:\t\treturn K_GP_LEFT_THUMB_UP;\n\tcase 833:\t\treturn K_GP_LEFT_THUMB_DOWN;\n\tcase 834:\t\treturn K_GP_LEFT_THUMB_LEFT;\n\tcase 835:\t\treturn K_GP_LEFT_THUMB_RIGHT;\n\tcase 836:\t\treturn K_GP_RIGHT_THUMB_UP;\n\tcase 837:\t\treturn K_GP_RIGHT_THUMB_DOWN;\n\tcase 838:\t\treturn K_GP_RIGHT_THUMB_LEFT;\n\tcase 839:\t\treturn K_GP_RIGHT_THUMB_RIGHT;\n\tcase 840:\t\treturn K_JOY_UP;\n\tcase 841:\t\treturn K_JOY_DOWN;\n\tcase 842:\t\treturn K_JOY_LEFT;\n\tcase 843:\t\treturn K_JOY_RIGHT;\n\tdefault:\n\t\tif (code == -1)\t//mod bug\n\t\t\treturn -1;\n\t\tif (code < 0)\t//negative values are 'fte-native' keys, for stuff that the api lacks.\n\t\t\treturn -code;\n\t\tif (code >= 0 && code < 128)\n\t\t\treturn code;\n\t\treturn -code;\t//these keys are not supported in fte. use negatives so that they can be correctly mapped back to qc codes if the need arises. no part of the engine will recognise them.\n\t}\n}\n\n//string\tfindkeysforcommand(string command) = #610;\nvoid QCBUILTIN PF_cl_findkeysforcommand (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char *cmdname = PR_GetStringOfs(prinst, OFS_PARM0);\n\tint bindmap = (prinst->callargc > 1)?G_FLOAT(OFS_PARM1):0;\n\tint keynums[16];\n\tchar keyname[512];\n\tsize_t u;\n\n\tM_FindKeysForCommand(bindmap, 0, cmdname, keynums, NULL, countof(keynums));\n\n\tkeyname[0] = '\\0';\n\tfor (u = 0; u < countof(keynums); u++)\n\t{\n\t\tif (keynums[u] >= 0)\n\t\t\tkeynums[u] = MP_TranslateFTEtoQCCodes(keynums[u]);\n\t\telse if (u >= 2)\t//would ideally be 0, but nexuiz would bug out then.\n\t\t\tbreak;\n\t\tQ_strncatz (keyname, va(\" \\'%i\\'\", keynums[u]), sizeof(keyname));\n\t}\n\n\tRETURN_TSTRING(keyname);\n}\nvoid QCBUILTIN PF_cl_findkeysforcommandex (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char *cmdname = PR_GetStringOfs(prinst, OFS_PARM0);\n\tint bindmap = (prinst->callargc > 1)?G_FLOAT(OFS_PARM1):0;\n\tint keynums[256];\n\tint keymods[countof(keynums)];\n\tchar keyname[512];\n\tint i, count = M_FindKeysForBind(bindmap, cmdname, keynums, keymods, countof(keynums));\n\n\tkeyname[0] = '\\0';\n\n\tfor (i = 0; i < count; i++)\n\t{\n\t\tif (i)\n\t\t\tQ_strncatz (keyname, \" \", sizeof(keyname));\n\t\tQ_strncatz (keyname, Key_KeynumToString(keynums[i], keymods[i]), sizeof(keyname));\n\t}\n\n\tRETURN_TSTRING(keyname);\n}\n\nvoid QCBUILTIN PF_cl_getkeybind (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint bindmap = (prinst->callargc > 1)?G_FLOAT(OFS_PARM1):0;\n\tint modifier = (prinst->callargc > 2)?G_FLOAT(OFS_PARM2):0;\n\tconst char *binding = Key_GetBinding(MP_TranslateQCtoFTECodes(G_FLOAT(OFS_PARM0)), bindmap, modifier);\n\tRETURN_TSTRING(binding);\n}\nvoid QCBUILTIN PF_cl_setkeybind (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint keynum = MP_TranslateQCtoFTECodes(G_FLOAT(OFS_PARM0));\n\tconst char *binding = PR_GetStringOfs(prinst, OFS_PARM1);\n\tint bindmap = (prinst->callargc > 2)?G_FLOAT(OFS_PARM2):0;\n\tint modifier = (prinst->callargc > 3)?G_FLOAT(OFS_PARM3):~0;\n\n\tif (bindmap > 0 && bindmap <= KEY_MODIFIER_ALTBINDMAP)\n\t\tmodifier = (bindmap-1) | KEY_MODIFIER_ALTBINDMAP;\t//ignore the modifier if we're setting into a bindmap...\n\n\tKey_SetBinding(keynum, modifier, binding, RESTRICT_INSECURE);\n}\n\nvoid QCBUILTIN PF_cl_stringtokeynum(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint i;\n\tint modifier;\n\tconst char *s;\n\n\ts = PR_GetStringOfs(prinst, OFS_PARM0);\n\ti = Key_StringToKeynum(s, &modifier);\n\tif (i < 0 || modifier != ~0)\n\t{\n\t\tG_FLOAT(OFS_RETURN) = -1;\n\t\treturn;\n\t}\n\ti = MP_TranslateFTEtoQCCodes(i);\n\tG_FLOAT(OFS_RETURN) = i;\n}\n\n//string\tkeynumtostring(float keynum) = #609;\nvoid QCBUILTIN PF_cl_keynumtostring (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint code = G_FLOAT(OFS_PARM0);\n\n\tcode = MP_TranslateQCtoFTECodes (code);\n\n\tRETURN_TSTRING(Key_KeynumToString(code, 0));\n}\n\nvoid QCBUILTIN PF_cl_setwindowcaption(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char *newcaption = PR_GetStringOfs(prinst, OFS_PARM0);\n\tif (!cl.windowtitle || strcmp(cl.windowtitle, newcaption))\n\t{\n\t\tZ_Free(cl.windowtitle);\n\t\tcl.windowtitle = NULL;\n\t\tif (*newcaption)\n\t\t\tcl.windowtitle = Z_StrDup(newcaption);\n\t\tCL_UpdateWindowTitle();\n\t}\n}\n\nvoid QCBUILTIN PF_cl_setmousepos (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tworld_t *world = prinst->parms->user;\n\tfloat *pos = G_VECTOR(OFS_PARM0);\n\n\tif (key_dest_absolutemouse & world->keydestmask)\n\t{\n\t\tvid.forcecursor = true;\n\t\tvid.forcecursorpos[0] = (pos[0] * vid.pixelwidth) / vid.width;\n\t\tvid.forcecursorpos[1] = (pos[1] * vid.pixelheight) / vid.height;\n\t}\n}\n\n//#343\nvoid QCBUILTIN PF_cl_setcursormode (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tworld_t *world = prinst->parms->user;\n\tif (G_FLOAT(OFS_PARM0))\n\t\tkey_dest_absolutemouse |= world->keydestmask;\n\telse\n\t\tkey_dest_absolutemouse &= ~world->keydestmask;\n\n\tif (prinst->callargc>1)\n\t{\n\t\tstruct key_cursor_s *m = &key_customcursor[(world->keydestmask==kdm_game)?kc_game:kc_menuqc];\n\t\tQ_strncpyz(m->name, PR_GetStringOfs(prinst, OFS_PARM1), sizeof(m->name));\n\t\tm->hotspot[0] = (prinst->callargc>2)?G_FLOAT(OFS_PARM2+0):0;\n\t\tm->hotspot[1] = (prinst->callargc>2)?G_FLOAT(OFS_PARM2+1):0;\n\t\tm->scale = (prinst->callargc>2)?G_FLOAT(OFS_PARM2+2):0;\n\t\tif (m->scale <= 0)\n\t\t\tm->scale = 1;\n\t\tm->dirty = true;\n\t}\n}\n\nvoid QCBUILTIN PF_cl_getcursormode (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tworld_t *world = prinst->parms->user;\n\tif (G_FLOAT(OFS_PARM0))\n\t\tG_FLOAT(OFS_RETURN) = Key_MouseShouldBeFree();\n\telse if (key_dest_absolutemouse & world->keydestmask)\n\t\tG_FLOAT(OFS_RETURN) = true;\n\telse\n\t\tG_FLOAT(OFS_RETURN) = false;\n}\n\nvoid QCBUILTIN PF_cl_playingdemo (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tswitch(cls.demoplayback)\n\t{\n\tcase DPB_NONE:\n\t\tG_FLOAT(OFS_RETURN) = 0;\n\t\tbreak;\n\tcase DPB_MVD:\n\t\tG_FLOAT(OFS_RETURN) = 2;\n\t\tbreak;\n\tdefault:\n\t\tG_FLOAT(OFS_RETURN) = 1;\n\t\tbreak;\n\t}\n}\n\nvoid QCBUILTIN PF_cl_runningserver (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n#ifdef CLIENTONLY\n\tG_FLOAT(OFS_RETURN) = false;\n#else\n\tif (sv.state != ss_dead)\n\t{\n\t\tif (sv.allocated_client_slots > 1)\n\t\t\tG_FLOAT(OFS_RETURN) = true;\n\t\telse\n\t\t\tG_FLOAT(OFS_RETURN) = 0.5;\t//give some half-way value if we're singleplayer. NOTE: DP returns 0 in this case, which is kinda useless for things like deciding whether a 'save' menu option can be used.\n\t}\n\telse\n\t\tG_FLOAT(OFS_RETURN) = false;\n#endif\n}\n\nvoid QCBUILTIN PF_cl_addprogs (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char *s = PR_GetStringOfs(prinst, OFS_PARM0);\n\tint newp;\n\tif (!s || !*s)\n\t\tnewp = -1;\n\telse\n\t{\n\t\tnewp = PR_LoadProgs(prinst, s);\n\t\tif (newp >= 0)\n\t\t\tPR_ProgsAdded(prinst, newp, s);\n\t}\n\tG_FLOAT(OFS_RETURN) = newp;\n}\n\n\n#ifdef HAVE_MEDIA_DECODER\n\n// #487 float(string name, string video=\"http:\") gecko_create\nvoid QCBUILTIN PF_cs_media_create (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char *shadername = PR_GetStringOfs(prinst, OFS_PARM0);\n\tconst char *videoname = (prinst->callargc > 1)?PR_GetStringOfs(prinst, OFS_PARM1):\"http:\";\n\tcin_t *cin;\n\tcin = R_ShaderGetCinematic(R_RegisterShader(shadername, SUF_2D, va(\n\t\t\t\t\"{\\n\"\n\t\t\t\t\t\"program default2d\\n\"\n\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\"videomap %s\\n\"\n\t\t\t\t\t\t\"rgbgen vertex\\n\"\n\t\t\t\t\t\t\"alphagen vertex\\n\"\n\t\t\t\t\t\t\"blendfunc gl_one gl_one_minus_src_alpha\\n\"\n\t\t\t\t\t\t\"nodepth\\n\"\n\t\t\t\t\t\"}\\n\"\n\t\t\t\t\"}\\n\",\t\t\n\t\t\tvideoname)));\n\n\tif (cin)\n\t{\n\t\tG_FLOAT(OFS_RETURN) = 1;\n\t\tMedia_Send_Reset(cin);\n\t}\n\telse\n\t\tG_FLOAT(OFS_RETURN) = 0;\n}\n// #488 void(string name) gecko_destroy\nvoid QCBUILTIN PF_cs_media_destroy (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char *shadername = PR_GetStringOfs(prinst, OFS_PARM0);\n\tshader_t *shader = R_ShaderFind(shadername);\n\tcin_t *cin;\n\tif (!shader)\n\t\treturn;\n\tcin = R_ShaderGetCinematic(shader);\n\tif (cin && shader->uses > 1)\n\t{\n\t\tif (shader->uses > 1)\n\t\t\tMedia_Send_Reset(cin);\t//will still be active afterwards.\n\t}\n\tR_UnloadShader(shader);\n}\n// #489 void(string name, string URI) gecko_navigate\nvoid QCBUILTIN PF_cs_media_command (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char *shader = PR_GetStringOfs(prinst, OFS_PARM0);\n\tconst char *command = PR_GetStringOfs(prinst, OFS_PARM1);\n\tcin_t *cin;\n\tcin = R_ShaderFindCinematic(shader);\n\tif (!cin)\n\t\treturn;\n\tMedia_Send_Command(cin, command);\n}\n// #490 float(string name, float key, float eventtype, optional float charcode) gecko_keyevent\nvoid QCBUILTIN PF_cs_media_keyevent (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char *shader = PR_GetStringOfs(prinst, OFS_PARM0);\n\tint key = G_FLOAT(OFS_PARM1);\n\tint eventtype = G_FLOAT(OFS_PARM2);\n\tint charcode = (prinst->callargc>3)?G_FLOAT(OFS_PARM3):((key>127)?0:key);\n\tcin_t *cin;\n\tcin = R_ShaderFindCinematic(shader);\n\tG_FLOAT(OFS_RETURN) = 0;\n\tif (!cin)\n\t\treturn;\n\tMedia_Send_KeyEvent(cin, MP_TranslateQCtoFTECodes(key), charcode, eventtype);\n\tG_FLOAT(OFS_RETURN) = 1;\n}\n// #491 void(string name, float x, float y) gecko_mousemove\nvoid QCBUILTIN PF_cs_media_mousemove (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char *shader = PR_GetStringOfs(prinst, OFS_PARM0);\n\tfloat posx = G_FLOAT(OFS_PARM1);\n\tfloat posy = G_FLOAT(OFS_PARM2);\n\tcin_t *cin;\n\tcin = R_ShaderFindCinematic(shader);\n\tif (!cin)\n\t\treturn;\n\tMedia_Send_MouseMove(cin, posx, posy);\n}\n// #492 void(string name, float w, float h) gecko_resize\nvoid QCBUILTIN PF_cs_media_resize (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char *shader = PR_GetStringOfs(prinst, OFS_PARM0);\n\tfloat sizex = G_FLOAT(OFS_PARM1);\n\tfloat sizey = G_FLOAT(OFS_PARM2);\n\tcin_t *cin;\n\tcin = R_ShaderFindCinematic(shader);\n\tif (!cin)\n\t\treturn;\n\tMedia_Send_Resize(cin, sizex, sizey);\n}\n// #493 vector(string name) gecko_get_texture_extent\nvoid QCBUILTIN PF_cs_media_get_texture_extent (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char *shader = PR_GetStringOfs(prinst, OFS_PARM0);\n\tfloat *ret = G_VECTOR(OFS_RETURN);\n\tint sx = 0, sy = 0;\n\tfloat aspect = 0;\n\tcin_t *cin;\n\tcin = R_ShaderFindCinematic(shader);\n\tif (cin)\n\t\tMedia_Send_GetSize(cin, &sx, &sy, &aspect);\n\tret[0] = sx;\n\tret[1] = sy;\n\tret[2] = aspect;\n}\nvoid QCBUILTIN PF_cs_media_getproperty (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char *shader = PR_GetStringOfs(prinst, OFS_PARM0);\n\tconst char *propname = PR_GetStringOfs(prinst, OFS_PARM1);\n\tconst char *ret = NULL;\n\tcin_t *cin;\n\tcin = R_ShaderFindCinematic(shader);\n\tif (cin)\n\t\tret = Media_Send_GetProperty(cin, propname);\n\n\tG_INT(OFS_RETURN) = ret?PR_TempString(prinst, ret):0;\n}\nvoid QCBUILTIN PF_cs_media_getstate (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tcinstates_t ret = CINSTATE_INVALID;\n\tif (prinst->callargc>0)\n\t{\n\t\tconst char *shader = PR_GetStringOfs(prinst, OFS_PARM0);\n\t\tcin_t *cin;\n\t\tcin = R_ShaderFindCinematic(shader);\n\t\tif (cin)\n\t\t\tret = Media_GetState(cin);\n\t}\n\telse\n\t\tret = Media_GetState(NULL);\n\n\tG_FLOAT(OFS_RETURN) = ret;\n}\nvoid QCBUILTIN PF_cs_media_setstate (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char *shader = PR_GetStringOfs(prinst, OFS_PARM0);\n\tcinstates_t state = G_FLOAT(OFS_PARM1);\n\tcin_t *cin;\n\tcin = R_ShaderFindCinematic(shader);\n\tif (cin)\n\t\tMedia_SetState(cin, state);\n}\n\nvoid QCBUILTIN PF_cs_media_restart (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char *shader = PR_GetStringOfs(prinst, OFS_PARM0);\n\tcin_t *cin;\n\tcin = R_ShaderFindCinematic(shader);\n\tif (cin)\n\t\tMedia_Send_Reset(cin);\n}\n#endif\n\nvoid QCBUILTIN PF_soundlength (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char *sample = PR_GetStringOfs(prinst, OFS_PARM0);\n\n\tsfx_t *sfx = S_PrecacheSound(sample);\n\tif (sfx && sfx->loadstate == SLS_LOADING)\n\t\tCOM_WorkerPartialSync(sfx, &sfx->loadstate, SLS_LOADING);\n\tif (!sfx || sfx->loadstate != SLS_LOADED)\n\t\tG_FLOAT(OFS_RETURN) = 0;\n\telse\n\t{\n\t\tsfxcache_t cachebuf, *cache;\n\t\tif (sfx->decoder.querydata)\n\t\t{\n\t\t\tG_FLOAT(OFS_RETURN) = sfx->decoder.querydata(sfx, NULL, NULL, 0);\n\t\t\treturn;\n\t\t}\n\t\telse if (sfx->decoder.decodedata)\n\t\t\tcache = sfx->decoder.decodedata(sfx, &cachebuf, 0x7ffffffe, 0);\n\t\telse\n\t\t\tcache = sfx->decoder.buf;\n\t\tif (!cache)\n\t\t\tG_FLOAT(OFS_RETURN) = 0;\n\t\telse\n\t\t\tG_FLOAT(OFS_RETURN) = (cache->soundoffset+cache->length) / (float)snd_speed;\n\t}\n}\n\nqboolean M_Vid_GetMode(qboolean forfullscreen, int num, int *w, int *h);\n//a bit pointless really\nvoid QCBUILTIN PF_cl_getresolution (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tfloat mode = G_FLOAT(OFS_PARM0);\n\tqboolean forfullscreen = (prinst->callargc >= 2)?G_FLOAT(OFS_PARM1):true; //if true, we should return queried video modes... or the mod could make up its own, but whatever.\n\tfloat *ret = G_VECTOR(OFS_RETURN);\n\tint w, h;\n\tfloat pixelheight = 0;\n\n\tw=h=0;\n\tif (mode == -1)\n\t{\n\t\tint bpp, rate;\n\t\tSys_GetDesktopParameters(&w, &h, &bpp, &rate);\n\t}\n\telse\n\t\tM_Vid_GetMode(forfullscreen, mode, &w, &h);\n\n\tret[0] = w;\n\tret[1] = h;\n\tret[2] = pixelheight?pixelheight:((w&&h)?1:0);\t//pixelheight\n}\n\n#ifdef CL_MASTER\n#include \"cl_master.h\"\n\nvoid QCBUILTIN PF_cl_gethostcachevalue (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\thostcacheglobal_t hcg = G_FLOAT(OFS_PARM0);\n\tG_FLOAT(OFS_RETURN) = 0;\n\tswitch(hcg)\n\t{\n\tcase SLIST_HOSTCACHEVIEWCOUNT:\n\t\tCL_QueryServers();\n\t\tMaster_CheckPollSockets();\n\t\tG_FLOAT(OFS_RETURN) = Master_NumSorted();\n\t\treturn;\n\tcase SLIST_HOSTCACHETOTALCOUNT:\n\t\tCL_QueryServers();\n\t\tMaster_CheckPollSockets();\n\t\tG_FLOAT(OFS_RETURN) = Master_TotalCount();\n\t\treturn;\n\n\tcase SLIST_MASTERQUERYCOUNT:\n\tcase SLIST_MASTERREPLYCOUNT:\n\tcase SLIST_SERVERQUERYCOUNT:\n\tcase SLIST_SERVERREPLYCOUNT:\n\t\tG_FLOAT(OFS_RETURN) = 0;\n\t\treturn;\n\n\tcase SLIST_SORTFIELD:\n\t\tG_FLOAT(OFS_RETURN) = Master_GetSortField();\n\t\treturn;\n\tcase SLIST_SORTDESCENDING:\n\t\tG_FLOAT(OFS_RETURN) = Master_GetSortDescending();\n\t\treturn;\n\tdefault:\n\t\treturn;\n\t}\n}\n\n//void \tresethostcachemasks(void) = #615;\nvoid QCBUILTIN PF_cl_resethostcachemasks(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tMaster_ClearMasks();\n}\n//void \tsethostcachemaskstring(float mask, float fld, string str, float op) = #616;\nvoid QCBUILTIN PF_cl_sethostcachemaskstring(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint mask = G_FLOAT(OFS_PARM0);\n\tint field = G_FLOAT(OFS_PARM1);\n\tconst char *str = PR_GetStringOfs(prinst, OFS_PARM2);\n\tint op = G_FLOAT(OFS_PARM3);\n\n\tMaster_SetMaskString((mask&512)?true:false, field, str, op);\n}\n//void\tsethostcachemasknumber(float mask, float fld, float num, float op) = #617;\nvoid QCBUILTIN PF_cl_sethostcachemasknumber(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint mask = G_FLOAT(OFS_PARM0);\n\tint field = G_FLOAT(OFS_PARM1);\n\tint str = G_FLOAT(OFS_PARM2);\n\tint op = G_FLOAT(OFS_PARM3);\n\n\tMaster_SetMaskInteger((mask&512)?true:false, field, str, op);\n}\n//void \tresorthostcache(void) = #618;\nvoid QCBUILTIN PF_cl_resorthostcache(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tMaster_SortServers();\n}\n//void\tsethostcachesort(float fld, float descending) = #619;\nvoid QCBUILTIN PF_cl_sethostcachesort(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tMaster_SetSortField(G_FLOAT(OFS_PARM0), G_FLOAT(OFS_PARM1));\n}\n//void\trefreshhostcache(void) = #620;\nvoid QCBUILTIN PF_cl_refreshhostcache(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tqboolean doreset = (prinst->callargc>=1)?G_FLOAT(OFS_PARM0):false;\n\tMasterInfo_Refresh(doreset);\n}\n//float\tgethostcachenumber(float fld, float hostnr) = #621;\nvoid QCBUILTIN PF_cl_gethostcachenumber(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tfloat ret = 0;\n\tint keynum = G_FLOAT(OFS_PARM0);\n\tint svnum = G_FLOAT(OFS_PARM1);\n\tserverinfo_t *sv;\n\tsv = Master_SortedServer(svnum);\n\n\tret = Master_ReadKeyFloat(sv, keynum);\n\n\tG_FLOAT(OFS_RETURN) = ret;\n}\nvoid QCBUILTIN PF_cl_gethostcachestring (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tchar *ret;\n\tint keynum = G_FLOAT(OFS_PARM0);\n\tint svnum = G_FLOAT(OFS_PARM1);\n\tserverinfo_t *sv;\n\n\tsv = Master_SortedServer(svnum);\n\tret = Master_ReadKeyString(sv, keynum);\n\n\tRETURN_TSTRING(ret);\n}\n\n//float\tgethostcacheindexforkey(string key) = #622;\nvoid QCBUILTIN PF_cl_gethostcacheindexforkey(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char *keyname = PR_GetStringOfs(prinst, OFS_PARM0);\n\n\tG_FLOAT(OFS_RETURN) = Master_KeyForName(keyname);\n}\n//void\taddwantedhostcachekey(string key) = #623;\nvoid QCBUILTIN PF_cl_addwantedhostcachekey(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tPF_cl_gethostcacheindexforkey(prinst, pr_globals);\n}\n\nvoid QCBUILTIN PF_cl_getextresponse(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\t//this does something weird\n\tG_INT(OFS_RETURN) = 0;\n}\n#else\nvoid QCBUILTIN PF_cl_gethostcachevalue (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals){G_FLOAT(OFS_RETURN) = 0;}\nvoid QCBUILTIN PF_cl_gethostcachestring (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) {G_INT(OFS_RETURN) = 0;}\n//void \tresethostcachemasks(void) = #615;\nvoid QCBUILTIN PF_cl_resethostcachemasks(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals){}\n//void \tsethostcachemaskstring(float mask, float fld, string str, float op) = #616;\nvoid QCBUILTIN PF_cl_sethostcachemaskstring(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals){}\n//void\tsethostcachemasknumber(float mask, float fld, float num, float op) = #617;\nvoid QCBUILTIN PF_cl_sethostcachemasknumber(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals){}\n//void \tresorthostcache(void) = #618;\nvoid QCBUILTIN PF_cl_resorthostcache(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals){}\n//void\tsethostcachesort(float fld, float descending) = #619;\nvoid QCBUILTIN PF_cl_sethostcachesort(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals){}\n//void\trefreshhostcache(void) = #620;\nvoid QCBUILTIN PF_cl_refreshhostcache(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) {}\n//float\tgethostcachenumber(float fld, float hostnr) = #621;\nvoid QCBUILTIN PF_cl_gethostcachenumber(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals){G_FLOAT(OFS_RETURN) = 0;}\n//float\tgethostcacheindexforkey(string key) = #622;\nvoid QCBUILTIN PF_cl_gethostcacheindexforkey(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals){G_FLOAT(OFS_RETURN) = 0;}\n//void\taddwantedhostcachekey(string key) = #623;\nvoid QCBUILTIN PF_cl_addwantedhostcachekey(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals){}\n#endif\n\n\nvoid QCBUILTIN PF_shaderforname (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char *str = PR_GetStringOfs(prinst, OFS_PARM0);\n\tconst char *defaultbody = PF_VarString(prinst, 1, pr_globals);\n\n\tshader_t *shad;\n\n\tif (*defaultbody)\n\t\tshad = R_RegisterShader(str, SUF_NONE, defaultbody);\n\telse\n\t\tshad = R_RegisterSkin(NULL, str);\n\tif (shad)\n\t\tG_FLOAT(OFS_RETURN) = shad->id+1;\n\telse\n\t\tG_FLOAT(OFS_RETURN) = 0;\n}\n\nvoid QCBUILTIN PF_remapshader (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char *shadername = PR_GetStringOfs(prinst, OFS_PARM0);\n\tconst char *replacement = PR_GetStringOfs(prinst, OFS_PARM1);\n\tfloat timeoffset = G_FLOAT(OFS_PARM2);\n\n\tR_RemapShader(shadername, replacement, timeoffset);\n}\n\nvoid QCBUILTIN PF_cl_GetBindMap (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint bm[2];\n\tKey_GetBindMap(bm);\n\tG_VECTOR(OFS_RETURN)[0] = bm[0];\n\tG_VECTOR(OFS_RETURN)[1] = bm[1];\n\tG_VECTOR(OFS_RETURN)[2] = 0;\n}\nvoid QCBUILTIN PF_cl_SetBindMap (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint bm[2] =\n\t{\n\t\tG_VECTOR(OFS_PARM0)[0],\n\t\tG_VECTOR(OFS_PARM0)[1]\n\t};\n\tKey_SetBindMap(bm);\n\tG_FLOAT(OFS_RETURN) = 1;\n}\n\n//void\tsetmousetarget(float trg) = #603;\nvoid QCBUILTIN PF_cl_setmousetarget (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tworld_t *world = prinst->parms->user;\n\tunsigned int target = world->keydestmask;\n\tswitch ((int)G_FLOAT(OFS_PARM0))\n\t{\n\tcase 1:\t//1 is delta-based (mt_menu).\n\t\tkey_dest_absolutemouse &= ~target;\n\t\tbreak;\n\tcase 2:\t//2 is absolute (mt_client).\n\t\tkey_dest_absolutemouse |= target;\n\t\tbreak;\n\tdefault:\n\t\tPR_BIError(prinst, \"PF_setmousetarget: not a valid destination\\n\");\n\t}\n}\n\n//float\tgetmousetarget(void)\t  = #604;\nvoid QCBUILTIN PF_cl_getmousetarget (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tworld_t *world = prinst->parms->user;\n\tunsigned int target = world->keydestmask;\n\tG_FLOAT(OFS_RETURN) = (key_dest_absolutemouse&target)?2:1;\n}\n\n//evil builtins to pretend to be a server.\nvoid QCBUILTIN PF_cl_sprint (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\t//this is a bit pointless for menus as it doesn't know player names or anything.\n#ifndef CLIENTONLY\n\tint clientnum = G_FLOAT(OFS_PARM0);\n\tconst char *str = PF_VarString(prinst, 1, pr_globals);\n\tif (sv.active && clientnum < sv.allocated_client_slots && svs.clients[clientnum].state >= cs_connected)\n\t\tSV_PrintToClient(&svs.clients[clientnum], PRINT_HIGH, str);\n#endif\n}\nvoid QCBUILTIN PF_cl_bprint (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n#ifndef CLIENTONLY\n\tconst char *str = PF_VarString(prinst, 0, pr_globals);\n\tif (sv.active)\n\t\tSV_BroadcastPrintf(PRINT_HIGH, \"%s\", str);\n#endif\n}\nvoid QCBUILTIN PF_cl_clientcount (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n#ifndef CLIENTONLY\n\tif (sv.active)\n\t\tG_FLOAT(OFS_RETURN) = sv.allocated_client_slots;\n\telse\n\t\tG_FLOAT(OFS_RETURN) = 0;\n#endif\n}\n\n/*static void PF_cl_clipboard_got(void *ctx, char *utf8)\n{\n\tif (\n}\nvoid QCBUILTIN PF_cl_clipboard_get(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tclipboardtype_t cliptype = G_FLOAT(OFS_PARM0);\n\tSys_Clipboard_PasteText(cliptype, PF_cl_clipboard_got, prinst);\n}*/\nvoid QCBUILTIN PF_cl_clipboard_set(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tclipboardtype_t cliptype = G_FLOAT(OFS_PARM0);\n\tconst char *str = PR_GetStringOfs(prinst, OFS_PARM1);\n\tSys_SaveClipboard(cliptype, str);\n}\n\nvoid QCBUILTIN PF_cl_localsound(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char * s = PR_GetStringOfs(prinst, OFS_PARM0);\n\tfloat chan = (prinst->callargc>1)?G_FLOAT(OFS_PARM1):0;\n\tfloat vol = (prinst->callargc>2)?G_FLOAT(OFS_PARM2):1;\n\n\tS_LocalSound2(s, chan, vol);\n}\nvoid QCBUILTIN PF_cl_queueaudio(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tworld_t *world = prinst->parms->user;\n\tint sourceid\t= (world->keydestmask == kdm_menu)?SOURCEID_MENUQC:SOURCEID_CSQC;\n\tint hz\t\t\t= G_INT(OFS_PARM0);\n\tint channels\t= G_INT(OFS_PARM1);\n\tint type\t\t= G_INT(OFS_PARM2);\n\tint qcptr\t\t= G_INT(OFS_PARM3);\n\tint numframes\t= G_INT(OFS_PARM4);\n\tint i;\n\tconst float volume = 1;\n\tqaudiofmt_t fmt;\n\tconst void *data;\n\tvoid *ndata;\n\n\tif (hz < 1 || (channels != 1 && channels != 2) || numframes <= 0)\n\t{\t//dumb arg.\n\t\tG_FLOAT(OFS_RETURN) = -1;\n\t\treturn;\n\t}\n\tswitch(type)\n\t{\n\tcase 8:\t\t\tfmt = QAF_S8;\tdata = PR_GetReadQCPtr(prinst, qcptr, numframes*channels*QAF_BYTES(fmt));\n\t\tndata = alloca(sizeof(char)*numframes*channels);\n\t\tfor (i = numframes*channels; i --> 0; )\n\t\t\t((char*)ndata)[i] = ((const qbyte*)data)[i]-128;\n\t\tdata = ndata;\tbreak;\n\tcase -8:\t\tfmt = QAF_S8;\tdata = PR_GetReadQCPtr(prinst, qcptr, numframes*channels*QAF_BYTES(fmt));\tbreak;\n\tcase -16:\t\tfmt = QAF_S16;\tdata = PR_GetReadQCPtr(prinst, qcptr, numframes*channels*QAF_BYTES(fmt));\tbreak;\n//\tcase 16:\t\tfmt = QAF_U16:\tdata = PR_GetReadQCPtr(prinst, qcptr, numframes*channels*QAF_BYTES(fmt));\tbreak;\n#ifdef MIXER_F32\n\tcase 256|32:\tfmt = QAF_F32;\tdata = PR_GetReadQCPtr(prinst, qcptr, numframes*channels*QAF_BYTES(fmt));\tbreak;\n#endif\n\tdefault:\n\t\tG_FLOAT(OFS_RETURN) = -2;\t//unsupported width.\n\t\treturn;\n\t}\n\n\tS_RawAudio(sourceid, data, hz, numframes*channels, channels, fmt, volume);\n\n\tG_FLOAT(OFS_RETURN) = 1;\n}\nvoid QCBUILTIN PF_cl_getqueuedaudiotime(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tworld_t *world = prinst->parms->user;\n\tint sourceid\t= (world->keydestmask == kdm_menu)?SOURCEID_MENUQC:SOURCEID_CSQC;\n\n\tG_FLOAT(OFS_RETURN) = S_RawAudioQueued(sourceid);\n}\n\nvoid QCBUILTIN PF_cl_getlocaluserinfoblob (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint seat = G_FLOAT(OFS_PARM0);\n\tconst char *keyname = PR_GetStringOfs(prinst, OFS_PARM1);\n\tint qcptr = G_INT(OFS_PARM2);\n\tint qcsize = G_INT(OFS_PARM3);\n\tvoid *ptr;\n\tconst char *blob;\n\tsize_t blobsize = 0;\n\tinfobuf_t *info;\n\tif (seat < 0 || seat >= MAX_SPLITS)\n\t{\n\t\tPR_BIError(prinst, \"PF_cs_getlocaluserinfoblob: invalid seat\\n\");\n\t\treturn;\n\t}\n\tinfo = &cls.userinfo[seat];\n\n\tif (qcptr < 0 || qcptr+qcsize >= prinst->stringtablesize)\n\t{\n\t\tPR_BIError(prinst, \"PF_cs_getplayerkeyblob: invalid pointer\\n\");\n\t\treturn;\n\t}\n\tptr = (struct reverbproperties_s*)(prinst->stringtable + qcptr);\n\n\tblob = InfoBuf_BlobForKey(info, keyname, &blobsize, NULL);\n\tif (qcptr)\n\t{\n\t\tblobsize = min(blobsize, qcsize);\n\t\tmemcpy(ptr, blob, blobsize);\n\t\tG_INT(OFS_RETURN) = blobsize;\n\t}\n\telse\n\t\tG_INT(OFS_RETURN) = blobsize;\n}\nvoid QCBUILTIN PF_cl_getlocaluserinfostring (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint seat = G_FLOAT(OFS_PARM0);\n\tconst char *keyname = PR_GetStringOfs(prinst, OFS_PARM1);\n\tinfobuf_t *info;\n\tif (seat < 0 || seat >= MAX_SPLITS)\n\t{\n\t\tPR_BIError(prinst, \"PF_cs_getlocaluserinfoblob: invalid seat\\n\");\n\t\treturn;\n\t}\n\tinfo = &cls.userinfo[seat];\n\tRETURN_TSTRING(InfoBuf_ValueForKey(info, keyname));\n}\nvoid QCBUILTIN PF_cl_setlocaluserinfo (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint seat = G_FLOAT(OFS_PARM0);\n\tconst char *keyname = PR_GetStringOfs(prinst, OFS_PARM1);\n\tif (seat < 0 || seat >= MAX_SPLITS)\n\t{\n\t\tPR_BIError(prinst, \"PF_cs_getlocaluserinfoblob: invalid seat\\n\");\n\t\treturn;\n\t}\n\n\tif (prinst->callargc < 4)\n\t\tCL_SetInfo(seat, keyname, PR_GetStringOfs(prinst, OFS_PARM2));\n\telse\n\t{\n\t\tint qcptr = G_INT(OFS_PARM2);\n\t\tint qcsize = G_INT(OFS_PARM3);\n\t\tconst void *ptr;\n\t\tif (qcptr < 0 || qcptr+qcsize >= prinst->stringtablesize)\n\t\t{\n\t\t\tPR_BIError(prinst, \"PF_cs_getplayerkeyblob: invalid pointer\\n\");\n\t\t\treturn;\n\t\t}\n\t\tptr = (struct reverbproperties_s*)(prinst->stringtable + qcptr);\n\t\tCL_SetInfoBlob(seat, keyname, ptr, qcsize);\n\t}\n}\n\n#include \"fs.h\"\nvoid QCBUILTIN PF_cl_getgamedirinfo(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tsize_t diridx = G_FLOAT(OFS_PARM0);\n\tenum getgamedirinfo_e propidx = G_FLOAT(OFS_PARM1);\n\n\tstruct modlist_s *mod, current;\n\tif (G_FLOAT(OFS_PARM0) == -1)\n\t{\n\t\tcurrent.description = fs_manifest->formalname;\n\t\tcurrent.gamedir = FS_GetGamedir(true);\n\t\tcurrent.manifest = fs_manifest;\n\t\tmod = &current;\n\t}\n\telse\n\t\tmod = Mods_GetMod(diridx);\n\n\tG_INT(OFS_RETURN) = 0;\n\tif (mod)\n\t{\n\t\tswitch(propidx)\n\t\t{\n\t\tcase GGDI_GAMEDIR:\t//name\n\t\t\tRETURN_TSTRING(mod->gamedir);\n\t\t\tbreak;\n\t\tcase GGDI_ALLGAMEDIRS:\n\t\t\t{\n\t\t\t\tchar *dirs = NULL;\n\t\t\t\tsize_t d;\n\t\t\t\tftemanifest_t *man = mod->manifest?mod->manifest:fs_manifest;\n\t\t\t\t//the basedirs\n\t\t\t\tfor (d = 0; d < countof(man->gamepath); d++)\n\t\t\t\t{\n\t\t\t\t\tif (man->gamepath[d].path && man->gamepath[d].flags&GAMEDIR_BASEGAME)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (dirs)\n\t\t\t\t\t\t\tZ_StrCat(&dirs, \";\");\n\t\t\t\t\t\tZ_StrCat(&dirs, man->gamepath[d].path);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (mod->manifest)\n\t\t\t\t{\t//the manifest's mod dirs\n\t\t\t\t\tfor (d = 0; d < countof(man->gamepath); d++)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (man->gamepath[d].path && !(man->gamepath[d].flags&GAMEDIR_BASEGAME))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (dirs)\n\t\t\t\t\t\t\t\tZ_StrCat(&dirs, \";\");\n\t\t\t\t\t\t\tZ_StrCat(&dirs, man->gamepath[d].path);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\t//the specified gamedir\n\t\t\t\t{\n\t\t\t\t\tif (dirs)\n\t\t\t\t\t\tZ_StrCat(&dirs, \";\");\n\t\t\t\t\tZ_StrCat(&dirs, mod->gamedir);\n\t\t\t\t}\n\t\t\t\tRETURN_TSTRING(dirs?dirs:\"\");\n\t\t\t\tZ_Free(dirs);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase GGDI_DESCRIPTION:\t//description (contents of modinfo.txt)\n\t\t\tif (mod->description)\n\t\t\t\tRETURN_TSTRING(mod->description);\n\t\t\tbreak;\n\t\tcase GGDI_OVERRIDES:\t//cvars\n\t\t\tif (mod->manifest)\n\t\t\tif (mod->manifest->defaultexec)\n\t\t\t\tRETURN_TSTRING(mod->manifest->defaultexec);\n\t\t\tbreak;\n\t\tcase GGDI_LOADCOMMAND:\t//load command\n\t\t\tRETURN_TSTRING(va(\"fs_changegame %u\\n\", (unsigned int)diridx+1u));\n\t\t\tbreak;\n\t\tcase GGDI_ICON: //icon\n\t\t\t{\n\t\t\t\tchar iname[MAX_QPATH];\n\t\t\t\tshader_t *shader;\n\t\t\t\tQ_snprintfz(iname, sizeof(iname), \"gamedir/%u\", (unsigned) diridx);\n\t\t\t\tshader = R_RegisterShader(iname, SUF_2D,\n\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\"affine\\n\"\n\t\t\t\t\t\t\"nomipmaps\\n\"\n\t\t\t\t\t\t\"program default2d#PREMUL\\n\"\n\t\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\t\"clampmap $diffuse\\n\"\n\t\t\t\t\t\t\t\"blendfunc gl_one gl_one_minus_src_alpha\\n\"\n\t\t\t\t\t\t\"}\\n\"\n\t\t\t\t\t\t\"sort additive\\n\"\n\t\t\t\t\t\"}\\n\"\n\t\t\t\t\t);\n\t\t\t\tif (shader && !shader->defaulttextures->base)\n\t\t\t\t{\t//no textures yet? do something about it!\n\t\t\t\t\tvoid *data = NULL;\n\t\t\t\t\tsize_t i;\n\t\t\t\t\tqofs_t sz;\n\t\t\t\t\tconst char *extensions[] = {\n#ifdef IMAGEFMT_PNG\n\t\t\t\t\t\t\".png\",\n#endif\n\t\t\t\t\t\t\".tga\",\n#ifdef IMAGEFMT_BMP\n\t\t\t\t\t\t\".ico\",\n#endif\n\t\t\t\t\t};\n\t\t\t\t\tif (mod->manifest && mod->manifest->iconname)\n\t\t\t\t\t{\n\t\t\t\t\t\tfor (i = 0; i < countof(extensions) && !data; i++)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tCOM_StripExtension(mod->manifest->filename, iname, sizeof(iname));\n\t\t\t\t\t\t\tCOM_RequireExtension(iname, extensions[i], sizeof(iname));\n\t\t\t\t\t\t\tdata = FS_MallocFile(iname, FS_SYSTEM, &sz);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tfor (i = 0; i < countof(extensions) && !data; i++)\n\t\t\t\t\t\tdata = FS_MallocFile(va(\"%s/icon%s\", mod->gamedir, extensions[i]), FS_ROOT, &sz);\n\t\t\t\t\tQ_snprintfz(iname, sizeof(iname), \"gamedir/%u\", (unsigned) diridx);\n\t\t\t\t\tshader->defaulttextures->base = Image_CreateTexture(iname, NULL, IF_PREMULTIPLYALPHA);\n\t\t\t\t\tif (data)\n\t\t\t\t\t\tImage_LoadTextureFromMemory(shader->defaulttextures->base, shader->defaulttextures->base->flags, iname, iname, data, sz);\n\t\t\t\t}\n\t\t\t\tif (shader && TEXLOADED(shader->defaulttextures->base))\n\t\t\t\t\tRETURN_TSTRING(shader->name);\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t}\n}\n\n//This is consistent with vanilla quakeworld's 'packet' console command.\nvoid QCBUILTIN PF_cl_SendPacket(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tnetadr_t to;\n\tconst char *address = PR_GetStringOfs(prinst, OFS_PARM0);\n\tconst char *contents = PF_VarString(prinst, 1, pr_globals);\n\n\tG_FLOAT(OFS_RETURN) = NETERR_NOROUTE;\n\tif (NET_StringToAdr(address, 0, &to))\n\t{\n\t\tchar *send = Z_Malloc(4+strlen(contents));\n\t\tsend[0] = send[1] = send[2] = send[3] = 0xff;\n\t\tmemcpy(send+4, contents, strlen(contents));\n\t\t//FIXME: this is likely to change its port randomly...\n\t\tG_FLOAT(OFS_RETURN) = NET_SendPacket(cls.sockets, 4+strlen(contents), send, &to);\n\t\tZ_Free(send);\n\t}\n}\n\n\nvoid QCBUILTIN PF_cl_gp_querywithcb(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\t//for wrath compat, which isn't allowed to just return it for some reason.\n\tint device = G_FLOAT(OFS_PARM0);\n\tenum controllertype_e type = INS_GetControllerType(device);\n\n\tfunc_t f = PR_FindFunction (prinst, \"Controller_Type\", PR_CURRENT);\n\tif (f)\n\t{\n\t\tG_FLOAT(OFS_PARM0) = device;\n\t\tG_FLOAT(OFS_PARM1) = type;\n\t\tPR_ExecuteProgram (prinst, f);\n\t}\n}\nvoid QCBUILTIN PF_cl_gp_getbuttontype(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint device = G_FLOAT(OFS_PARM0);\n\tG_FLOAT(OFS_RETURN) = INS_GetControllerType(device);\n}\nvoid QCBUILTIN PF_cl_gp_rumble(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint device = G_FLOAT(OFS_PARM0);\n\tquint16_t amp_low = G_FLOAT(OFS_PARM1);\n\tquint16_t amp_high = G_FLOAT(OFS_PARM2);\n\tquint32_t duration = G_FLOAT(OFS_PARM3);\n\tINS_Rumble(device, amp_low, amp_high, duration);\n}\nvoid QCBUILTIN PF_cl_gp_rumbletriggers(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint device = G_FLOAT(OFS_PARM0);\n\tquint16_t left = G_FLOAT(OFS_PARM1);\n\tquint16_t right = G_FLOAT(OFS_PARM2);\n\tquint32_t duration = G_FLOAT(OFS_PARM3);\n\tINS_RumbleTriggers(device, left, right, duration);\n}\nvoid QCBUILTIN PF_cl_gp_setledcolor(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint device = G_FLOAT(OFS_PARM0);\n\tINS_SetLEDColor(device, G_VECTOR(OFS_PARM1));\n}\nvoid QCBUILTIN PF_cl_gp_settriggerfx(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint device = G_FLOAT(OFS_PARM0);\n\tint size = G_INT(OFS_PARM2);\n\tconst void *fxptr = PR_GetReadQCPtr(prinst, G_INT(OFS_PARM1), size);\n\n\tif (!fxptr)\n\t\tPR_BIError(prinst, \"PF_cl_gp_settriggerfx: invalid pointer/size\\n\");\n\telse\n\t\tINS_SetTriggerFX(device, fxptr, size);\n}\n\nconst char *PF_cl_serverkey_internal(const char *keyname)\n{\n\tchar *ret;\n\tstatic char adr[MAX_ADR_SIZE];\n\n\tif (!strcmp(keyname, \"ip\"))\n\t{\n\t\tif (cls.demoplayback)\n\t\t\tret = cls.lastdemoname;\n\t\telse\n\t\t\tret = NET_AdrToString(adr, sizeof(adr), &cls.netchan.remote_address);\n\t}\n\telse if (!strcmp(keyname, \"servername\"))\n\t\tret = cls.servername;\n\telse if (!strcmp(keyname, \"constate\"))\n\t{\n\t\tif (cls.state == ca_disconnected\n#ifndef CLIENTONLY\n\t\t\t&& !sv.state\n#endif\n\t\t\t\t)\n\t\t\tret = \"disconnected\";\n\t\telse if (cls.state == ca_active)\n\t\t\tret = \"active\";\n\t\telse\n\t\t\tret = \"connecting\";\n\t}\n\telse if (!strcmp(keyname, \"loadstate\"))\n\t{\n\t\textern int\t\t\ttotal_loading_size, current_loading_size, loading_stage;\n\t\textern char\t\t\tlevelshotname[MAX_QPATH];\n\t\tret = va(\"%i %u %u \\\"%s\\\"\", loading_stage, current_loading_size, total_loading_size, levelshotname);\n\t}\n\telse if (!strcmp(keyname, \"transferring\"))\n\t{\n\t\tret = CL_TryingToConnect();\n\t\tif (!ret)\n\t\t\tret = \"\";\n\t}\n\telse if (!strcmp(keyname, \"maxplayers\"))\n\t{\n\t\tret = va(\"%i\", cl.allocated_client_slots);\n\t}\n\telse if (!strcmp(keyname, \"dlstate\"))\n\t{\n\t\tif (!cl.downloadlist && !cls.download)\n\t\t\tret = \"\";\t//nothing being downloaded right now\n\t\telse\n\t\t{\n\t\t\tunsigned int fcount;\n\t\t\tqofs_t tsize;\n\t\t\tqboolean sizeextra;\n\t\t\tCL_GetDownloadSizes(&fcount, &tsize, &sizeextra);\n\t\t\tif (cls.download)\t//downloading something\n\t\t\t\tret = va(\"%u %g %u \\\"%s\\\" \\\"%s\\\" %g %i %g %g\", fcount, (float)tsize, sizeextra?1u:0u, cls.download->localname, cls.download->remotename, cls.download->percent, cls.download->rate, (float)cls.download->completedbytes, (float)cls.download->size);\n\t\t\telse\t//not downloading anything right now\n\t\t\t\tret = va(\"%u %g %u\", fcount, (float)tsize, sizeextra?1u:0u);\n\t\t}\n\t}\n\telse if (!strcmp(keyname, \"pausestate\"))\n\t\tret = cl.paused?\"1\":\"0\";\n\telse if (!strcmp(keyname, \"protocol\"))\n\t{\t//using this is pretty acedemic, really. Not particuarly portable.\n\t\tswitch (cls.protocol)\n\t\t{\t//a tokenizable string\n\t\t\t//first is the base game qw/nq\n\t\t\t//second is branch (custom engine name)\n\t\t\t//third is protocol version.\n\t\tdefault:\n\t\tcase CP_UNKNOWN:\n\t\t\tret = \"Unknown\";\n\t\t\tbreak;\n\t\tcase CP_QUAKEWORLD:\n\t\t\tif (cls.fteprotocolextensions||cls.fteprotocolextensions2)\n\t\t\t\tret = \"QuakeWorld FTE\";\n\t\t\telse if (cls.z_ext)\n\t\t\t\tret = \"QuakeWorld ZQuake\";\n\t\t\telse\n\t\t\t\tret = \"QuakeWorld\";\n\t\t\tbreak;\n\t\tcase CP_NETQUAKE:\n\t\t\tswitch (cls.protocol_nq)\n\t\t\t{\n\t\t\tdefault:\n\t\t\t\tret = \"NetQuake\";\n\t\t\t\tbreak;\n\t\t\tcase CPNQ_FITZ666:\n\t\t\t\tret = \"Fitz666\";\n\t\t\t\tbreak;\n\t\t\tcase CPNQ_DP5:\n\t\t\t\tret = \"NetQuake DarkPlaces 5\";\n\t\t\t\tbreak;\n\t\t\tcase CPNQ_DP6:\n\t\t\t\tret = \"NetQuake DarkPlaces 6\";\n\t\t\t\tbreak;\n\t\t\tcase CPNQ_DP7:\n\t\t\t\tret = \"NetQuake DarkPlaces 7\";\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase CP_QUAKE2:\n\t\t\tret = \"Quake2\";\n\t\t\tbreak;\n\t\tcase CP_QUAKE3:\n\t\t\tret = \"Quake3\";\n\t\t\tbreak;\n\t\tcase CP_PLUGIN:\n\t\t\tret = \"External\";\n\t\t\tbreak;\n\t\t}\n\t}\n\telse if (!strcmp(keyname, \"challenge\"))\n\t{\n\t\tret = va(\"%u\", cls.challenge);\n\t}\n\telse\n\t{\n#ifndef CLIENTONLY\n\t\tif (sv.state >= ss_loading)\n\t\t{\n\t\t\tret = InfoBuf_ValueForKey(&svs.info, keyname);\n\t\t\tif (!*ret)\n\t\t\t\tret = InfoBuf_ValueForKey(&svs.localinfo, keyname);\n\t\t}\n\t\telse\n#endif\n\t\t\tret = InfoBuf_ValueForKey(&cl.serverinfo, keyname);\n\t}\n\treturn ret;\n}\n//string(string keyname)\nvoid QCBUILTIN PF_cl_serverkey (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char *keyname = PF_VarString(prinst, 0, pr_globals);\n\tconst char *ret = PF_cl_serverkey_internal(keyname);\n\tif (*ret)\n\t\tRETURN_TSTRING(ret);\n\telse\n\t\tG_INT(OFS_RETURN) = 0;\n}\nvoid QCBUILTIN PF_cl_serverkeyfloat (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char *keyname = PR_GetStringOfs(prinst, OFS_PARM0);\n\tconst char *ret = PF_cl_serverkey_internal(keyname);\n\tif (*ret)\n\t\tG_FLOAT(OFS_RETURN) = strtod(ret, NULL);\n\telse\n\t\tG_FLOAT(OFS_RETURN) = (prinst->callargc >= 2)?G_FLOAT(OFS_PARM1):0;\n}\nvoid QCBUILTIN PF_cl_serverkeyblob (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char *keyname = PR_GetStringOfs(prinst, OFS_PARM0);\n\tint qcptr = G_INT(OFS_PARM1);\n\tint qcsize = G_INT(OFS_PARM2);\n\tvoid *ptr;\n\tsize_t blobsize = 0;\n\tconst char *blob;\n\n\tif (qcptr < 0 || qcptr+qcsize >= prinst->stringtablesize)\n\t{\n\t\tPR_BIError(prinst, \"PF_cs_serverkeyblob: invalid pointer\\n\");\n\t\treturn;\n\t}\n\tptr = (prinst->stringtable + qcptr);\n\n\tblob = InfoBuf_BlobForKey(&cl.serverinfo, keyname, &blobsize, NULL);\n\n\tif (qcptr)\n\t{\n\t\tblobsize = min(blobsize, qcsize);\n\t\tmemcpy(ptr, blob, blobsize);\n\t\tG_INT(OFS_RETURN) = blobsize;\n\t}\n\telse\n\t\tG_INT(OFS_RETURN) = blobsize;\n}\n\n#endif\n"
  },
  {
    "path": "engine/client/pr_csqc.c",
    "content": "/*\nCopyright (C) 2011 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n*/\n\n#include \"quakedef.h\"\n\n/*\n\n  EXT_CSQC is the 'root' extension\n  EXT_CSQC_1 are a collection of additional features to cover omissions in the original spec\n\n  simplecsqc lacks CSQC_UpdateView and has CSQC_DrawHud+CSQC_DrawScores instead.\n  if we're running arbitrary csqc, we block things that require too much game interaction...\n*/\n\n#ifdef CSQC_DAT\n\n#include \"glquake.h\"\t//evil to include this\n#include \"shader.h\"\n\n#include \"pr_common.h\"\n\nextern usercmd_t cl_pendingcmd[MAX_SPLITS];\nextern cvar_t sv_demo_write_csqc;\n\n\nstatic pubprogfuncs_t *csqcprogs;\n\nstatic qboolean csprogs_promiscuous;\nstatic unsigned int csprogs_checksum;\nstatic size_t csprogs_checksize;\nstatic char csprogs_checkname[MAX_QPATH];\nqboolean csqc_resortfrags;\nworld_t csqc_world;\n\nint\tcsqc_playerseat;\t//can be negative.\nstatic playerview_t *csqc_playerview;\nqboolean csqc_dp_lastwas3d;\t//to emulate DP correctly, we need to track whether drawpic/drawfill or clearscene was called last. blame 515.\n#ifndef HAVE_LEGACY\n#define csqc_isdarkplaces false\t//hopefully this will allow a smart enough compiler to optimise it out cleanly\n#else\nstatic qboolean csqc_isdarkplaces;\n#endif\nstatic qboolean csqc_nogameaccess;\t/*the module is not trusted by the server, so isn't allowed to access origins+stuffcmds+etc*/\nstatic qboolean csqc_singlecheats;\t/*single player or cheats active, allowing custom addons*/\nstatic qboolean csqc_mayread;\t\t/*csqc is allowed to ReadByte();*/\nstatic qboolean csqc_worldchanged;\t/*make sure any caches are rebuilt properly before the next renderscene*/\n\nstatic char csqc_printbuffer[8192];\n\n#define CSQCPROGSGROUP \"CSQC progs control\"\ncvar_t\tpr_csqc_maxedicts = CVAR(\"pr_csqc_maxedicts\", \"65536\");\t//not tied to protocol nor server. can be set arbitrarily high, except for memory allocations.\ncvar_t\tpr_csqc_memsize = CVAR(\"pr_csqc_memsize\", \"-1\");\ncvar_t\tcl_csqcdebug = CVAR(\"cl_csqcdebug\", \"0\");\t//prints entity numbers which arrive (so I can tell people not to apply it to players...)\nstatic cvar_t\tcl_csqc_nodeprecate = CVARAD(\"cl_csqc_nodeprecate\", \"0\", \"dpcompat_nocsqcwarnings\", \"When set, disables deprecation warnings.\");\ncvar_t  cl_nocsqc = CVAR(\"cl_nocsqc\", \"0\");\ncvar_t  pr_csqc_coreonerror = CVAR(\"pr_csqc_coreonerror\", \"1\");\n#if defined(NOBUILTINMENUS) && !defined(MENU_DAT)\ncvar_t  pr_csqc_formenus = CVARF(\"pr_csqc_formenus\", \"1\", CVAR_NOSET);\n#else\ncvar_t  pr_csqc_formenus = CVAR(\"pr_csqc_formenus\", \"0\");\n#endif\nstatic cvar_t\tdpcompat_csqcinputeventtypes = CVARD(\"dpcompat_csqcinputeventtypes\", \"999999\", \"Specifies the first csqc input event that the mod does not recognise. This should never have been a thing, but some mods are simply too buggy.\");\nextern cvar_t dpcompat_stats;\nextern cvar_t\tin_vraim;\n\n// standard effect cvars/sounds\nextern cvar_t r_explosionlight;\nextern sfx_t\t\t\t*cl_sfx_wizhit;\nextern sfx_t\t\t\t*cl_sfx_knighthit;\nextern sfx_t\t\t\t*cl_sfx_tink1;\nextern sfx_t\t\t\t*cl_sfx_ric1;\nextern sfx_t\t\t\t*cl_sfx_ric2;\nextern sfx_t\t\t\t*cl_sfx_ric3;\nextern sfx_t\t\t\t*cl_sfx_r_exp3;\n\ntypedef struct {\n#define globalfloatdep(name,dep) globalfloat(name)\n#define globalfloat(name) float *name;\n#define globalint(name) int *name;\n#define globaluint(name) unsigned int *name;\n#define globaluint64(name) puint64_t *name;\n#define globalvector(name) float *name;\n#define globalentity(name) int *name;\n#define globalstring(name) string_t *name;\n#define globalfunction(name,type) func_t name;\n//These are the functions the engine will call to, found by name.\n\n\tcsqcglobals\n\n#undef globalfloat\n#undef globalint\n#undef globaluint\n#undef globaluint64\n#undef globalvector\n#undef globalentity\n#undef globalstring\n#undef globalfunction\n} csqcglobals_t;\nstatic csqcglobals_t csqcg;\n\nplayerview_t csqc_nullview;\n\nstatic void VARGS CSQC_Abort (const char *format, ...);\t//an error occured.\nstatic void cs_set_input_state (usercmd_t *cmd);\n\n//fixme: we should be using entity numbers, not view numbers.\nstatic void CSQC_ChangeLocalPlayer(int seat)\n{\n\tif (seat < 0 || seat >= MAX_SPLITS)\n\t{\n\t\tcsqc_playerseat = -1;\n\t\tcsqc_playerview = &csqc_nullview;\n\t}\n\telse\n\t{\n\t\tcsqc_playerseat = seat;\n\t\tcsqc_playerview = &cl.playerview[seat];\n\t}\n\tif (csqcg.player_localentnum)\n\t{\n\t\tif (csqc_playerview->viewentity)\n\t\t\t*csqcg.player_localentnum = csqc_playerview->viewentity;\n\t\telse if (csqc_playerview->spectator && Cam_TrackNum(csqc_playerview) >= 0)\n\t\t\t*csqcg.player_localentnum = Cam_TrackNum(csqc_playerview) + 1;\n\t\telse if (csqc_playerview == &csqc_nullview)\n\t\t\t*csqcg.player_localentnum = 0;\n\t\telse\n\t\t\t*csqcg.player_localentnum = csqc_playerview->playernum+1;\n\t}\n\tif (csqcg.player_localnum)\n\t\t*csqcg.player_localnum = csqc_playerview->playernum;\n\n\tif (csqc_nogameaccess)\n\t\treturn;\t//don't give much info otherwise.\n\n\tif (csqcg.view_angles)\n\t{\n\t\tcsqcg.view_angles[0] = csqc_playerview->viewangles[0];\n\t\tcsqcg.view_angles[1] = csqc_playerview->viewangles[1];\n\t\tcsqcg.view_angles[2] = csqc_playerview->viewangles[2];\n\t}\n\tif ((unsigned int)seat < MAX_SPLITS)\n\t{\n//\t\tint i;\n\t\tusercmd_t *cmd = &cl_pendingcmd[seat];\n//\t\tusercmd_t tmp;\n//\t\tfor (i=0 ; i<3 ; i++)\n//\t\t\tcmd->angles[i] = ((int)(csqc_playerview->viewangles[i]*65536.0/360)&65535);\n//\t\tif (!cmd->msec)\n//\t\t\tCL_BaseMove (cmd, seat, cmd->msec, newtime);\n//\t\ttmp = *cmd;\n//\t\tcmd = &tmp;\n//\t\tcmd->msec = (realtime - cl.outframes[(cl.movesequence-1)&UPDATE_MASK].senttime)*1000;\n\t\tcs_set_input_state(cmd);\n\n\t\tif (csqcg.pmove_org)\n\t\t\tVectorCopy(csqc_playerview->simorg, csqcg.pmove_org);\n\t\tif (csqc_isdarkplaces)\n\t\t{\t//dp mods tend to require these to be totally unlerped\n\t\t\tif (csqcg.pmove_vel)\n\t\t\t\tVectorCopy(cl.inframes[cl.validsequence&UPDATE_MASK].playerstate[csqc_playerview->playernum].velocity, csqcg.pmove_vel);\n\t\t\tif (csqcg.pmove_onground)\n\t\t\t\t*csqcg.pmove_onground = cl.inframes[cl.validsequence&UPDATE_MASK].playerstate[csqc_playerview->playernum].onground;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (csqcg.pmove_vel)\n\t\t\t\tVectorCopy(csqc_playerview->simvel, csqcg.pmove_vel);\n\t\t\tif (csqcg.pmove_onground)\n\t\t\t\t*csqcg.pmove_onground = csqc_playerview->onground;\n\t\t}\n\t}\n}\n\nstatic void CSQC_FindGlobals(qboolean nofuncs)\n{\n\tstatic eval_t junk;\n\tstatic float csphysicsmode = 0;\n\tstatic float dimension_default = 255;\n\tstatic vec3_t defaultgravity = {0, 0, -1};\n\tetype_t typ;\n#define globalfloat(name) csqcg.name = (float*)PR_FindGlobal(csqcprogs, #name, 0, NULL);\n#define globalint(name) csqcg.name = (int*)PR_FindGlobal(csqcprogs, #name, 0, NULL);\n#define globaluint(name) csqcg.name = (unsigned int*)PR_FindGlobal(csqcprogs, #name, 0, NULL);\n#define globaluint64(name) csqcg.name = (puint64_t*)PR_FindGlobal(csqcprogs, #name, 0, &typ); if ((typ&0xfff) != ev_uint64 && csqcg.name) {Con_Printf(CON_WARNING\"csqc global \"#name\" wrongly defined as type %i\\n\", typ&0xfff); csqcg.name = NULL;}\n#define globalvector(name) csqcg.name = (float*)PR_FindGlobal(csqcprogs, #name, 0, NULL);\n#define globalentity(name) csqcg.name = (int*)PR_FindGlobal(csqcprogs, #name, 0, NULL);\n#define globalstring(name) csqcg.name = (string_t*)PR_FindGlobal(csqcprogs, #name, 0, NULL);\n#define globalfunction(name,typestr) csqcg.name = nofuncs?0:PR_FindFunction(csqcprogs,#name,PR_ANY);\n\n\tcsqcglobals\n\n#undef globalfloat\n#undef globalint\n#undef globaluint\n#undef globaluint64\n#undef globalvector\n#undef globalentity\n#undef globalstring\n#undef globalfunction\n\n#define ensurefloat(name)\tdo{if (!csqcg.name) csqcg.name = &junk._float;}while(0)\n#define ensureint(name)\t\tdo{if (!csqcg.name) csqcg.name = &junk._int;  }while(0)\n#define ensurevector(name)\tdo{if (!csqcg.name) csqcg.name = junk._vector;}while(0)\n#define ensureentity(name)\tdo{if (!csqcg.name) csqcg.name = &junk.edict; }while(0)\n\n#define ensureprivfloat(name)\tdo{if (!csqcg.name) {static pvec_t f; csqcg.name = &f;}}while(0)\n#define ensureprivint(name)\t\tdo{if (!csqcg.name) {static pint_t i; csqcg.name = &i;}}while(0)\n#define ensureprivvector(name)\tdo{if (!csqcg.name) {static pvec3_t v; csqcg.name = v;}}while(0)\n#define ensurepriventity(name)\tdo{if (!csqcg.name) {static pint_t e; csqcg.name = &e;}}while(0)\n\n\tif (csqc_nogameaccess)\n\t{\n\t\tcsqcg.CSQC_UpdateView = 0;\t//would fail\n\t\tcsqcg.CSQC_UpdateViewLoading = 0;\t//would fail\n\t\tcsqcg.CSQC_Parse_StuffCmd = 0;\t//could block cvar changes, thus allow cheats\n\t\tcsqcg.CSQC_Parse_SetAngles = 0;\t//too evil\n\t\tcsqcg.CSQC_Input_Frame = 0;\t//no aimbot writing\n\t\tcsqcg.CSQC_Event_Sound = csqcg.CSQC_ServerSound = 0; //no sound snooping\n\t\tcsqcg.CSQC_Parse_TempEntity = 0; //compat nightmare\n\t\tcsqcg.view_angles = NULL;\n\t\tcsqcg.physics_mode = NULL;\t//no thinks stuff\n\t\tcsqcg.pmove_org = NULL;\t//can't make aimbots if you don't know where you're aiming from.\n\t\tcsqcg.pmove_vel = NULL;\t//no dead reckoning please\n\t\tcsqcg.pmove_mins = csqcg.pmove_maxs = csqcg.pmove_jump_held = csqcg.pmove_waterjumptime = csqcg.pmove_onground = NULL; //I just want to kill theses\n\t\tcsqcg.input_sequence = NULL;\n\t\tcsqcg.input_angles = csqcg.input_movevalues = csqcg.input_buttons = csqcg.input_impulse = csqcg.input_lightlevel = csqcg.input_servertime = NULL;\n\t\tcsqcg.input_weapon = NULL;\n\t\tcsqcg.input_clienttime = csqcg.input_cursor_screen = csqcg.input_cursor_trace_start = csqcg.input_cursor_trace_endpos = csqcg.input_cursor_entitynumber = NULL;\n\t\tcsqcg.input_head_status = csqcg.input_left_status = csqcg.input_right_status = NULL;\n\t\tcsqcg.input_head_angles = csqcg.input_left_angles = csqcg.input_right_angles = NULL;\n\t\tcsqcg.input_head_origin = csqcg.input_left_origin = csqcg.input_right_origin = NULL;\n\t\tcsqcg.input_head_weapon = csqcg.input_left_weapon = csqcg.input_right_weapon = NULL;\n\t}\n\telse if (csqcg.CSQC_UpdateView || csqcg.CSQC_UpdateViewLoading)\n\t{\t//full csqc AND simplecsqc's entry points at the same time are a bad idea that just result in confusion.\n\t\t//full csqc mods should just disable engine hud drawing\n\t\tcsqcg.CSQC_DrawHud = 0;\n\t\tcsqcg.CSQC_DrawScores = 0;\n\t}\n\n#ifndef HAVE_LEGACY\n\t{\n\t\tetype_t etype = ev_void;\n\t\tif (!csqcg.trace_surfaceflagsi)\n\t\t\tcsqcg.trace_surfaceflagsi = (int*)PR_FindGlobal(csqcprogs, \"trace_surfaceflags\", 0, &etype);\n\t\tif (!csqcg.trace_endcontentsi)\n\t\t\tcsqcg.trace_endcontentsi = (int*)PR_FindGlobal(csqcprogs, \"trace_endcontents\", 0, &etype);\n\t}\n#else\n\tif (!csqcg.trace_surfaceflagsf && !csqcg.trace_surfaceflagsi)\n\t{\n\t\tetype_t etype = ev_void;\n\t\teval_t *v = PR_FindGlobal(csqcprogs, \"trace_surfaceflags\", 0, &etype);\n\t\tif (etype == ev_float)\n\t\t\tcsqcg.trace_surfaceflagsf = &v->_float;\n\t\telse if (etype == ev_integer)\n\t\t\tcsqcg.trace_surfaceflagsi = &v->_int;\n\t}\n\tif (!csqcg.trace_endcontentsf && !csqcg.trace_endcontentsi)\n\t{\n\t\tetype_t etype = ev_void;\n\t\teval_t *v = PR_FindGlobal(csqcprogs, \"trace_endcontents\", 0, &etype);\n\t\tif (etype == ev_float)\n\t\t\tcsqcg.trace_endcontentsf = &v->_float;\n\t\telse if (etype == ev_integer)\n\t\t\tcsqcg.trace_endcontentsi = &v->_int;\n\t}\n\tensurefloat(trace_surfaceflagsf);\n\tensurefloat(trace_endcontentsf);\n#endif\n\n\tensurefloat(trace_allsolid);\n\tensurefloat(trace_startsolid);\n\tensurefloat(trace_fraction);\n\tensurefloat(trace_inwater);\n\tensurefloat(trace_inopen);\n\tensurevector(trace_endpos);\n\tensurevector(trace_plane_normal);\n\tensurefloat(trace_plane_dist);\n\tensureint(trace_surfaceflagsi);\n\tensureint(trace_endcontentsi);\n\tensureint(trace_brush_id);\n\tensureint(trace_brush_faceid);\n\tensureint(trace_surface_id);\n\tensureint(trace_bone_id);\n\tensureint(trace_triangle_id);\n\tensurefloat(trace_networkentity);\n\tensureentity(trace_ent);\n\n\tensureprivfloat(clientcommandframe);\n\tensureprivfloat(input_timelength);\n\tensureprivvector(input_angles);\n\tensureprivvector(input_movevalues);\n\tensureprivfloat(input_buttons);\n//\tensureprivfloat(input_impulse);\n\n\tif (csqcg.time)\n\t\t*csqcg.time = cl.servertime;\n\tif (csqcg.cltime)\n\t\t*csqcg.cltime = realtime-cl.mapstarttime;\n\n\tif (!csqcg.global_gravitydir)\n\t\tcsqcg.global_gravitydir = defaultgravity;\n\n\tCSQC_ChangeLocalPlayer(cl_forceseat.ival?(cl_forceseat.ival - 1) % cl.splitclients:0);\n\n\tcsqc_world.g.self = csqcg.self;\n\tcsqc_world.g.other = csqcg.other;\n\tcsqc_world.g.force_retouch = (float*)PR_FindGlobal(csqcprogs, \"force_retouch\", 0, NULL);\n\tcsqc_world.g.physics_mode = csqcg.physics_mode;\n\tcsqc_world.g.frametime = csqcg.frametime;\n\tcsqc_world.g.newmis = (int*)PR_FindGlobal(csqcprogs, \"newmis\", 0, NULL);\n\tcsqc_world.g.time = csqcg.time;\n\tcsqc_world.g.v_forward = csqcg.v_forward;\n\tcsqc_world.g.v_right = csqcg.v_right;\n\tcsqc_world.g.v_up = csqcg.v_up;\n\tcsqc_world.g.defaultgravitydir = csqcg.global_gravitydir;\n\tcsqc_world.g.drawfont = (float*)PR_FindGlobal(csqcprogs, \"drawfont\", 0, NULL);\n\tcsqc_world.g.drawfontscale = (float*)PR_FindGlobal(csqcprogs, \"drawfontscale\", 0, NULL);\n\n\tif (!csqc_world.g.physics_mode)\n\t{\n\t\tcsphysicsmode = csqc_isdarkplaces?1:0;\t/*note: dp handles think functions as part of addentity rather than elsewhere. if we're in a compat mode, we don't want to have to duplicate work*/\n\t\tcsqc_world.g.physics_mode = &csphysicsmode;\n\t}\n\n\tif (!csqcg.dimension_default)\n\t\tcsqcg.dimension_default = &dimension_default;\n\n\tif (csqcg.maxclients)\n\t\t*csqcg.maxclients = cl.allocated_client_slots;\n}\n\nstatic void CSQC_InitFields(void)\n{\t//CHANGING THIS FUNCTION REQUIRES CHANGES TO csqcentvars_t\n#define comfieldfloat(name,desc) PR_RegisterFieldVar(csqcprogs, ev_float, #name, (size_t)&((csqcentvars_t*)0)->name, -1);\n#define comfieldint(name,desc) PR_RegisterFieldVar(csqcprogs, ev_integer, #name, (size_t)&((csqcentvars_t*)0)->name, -1);\n#define comfieldvector(name,desc) PR_RegisterFieldVar(csqcprogs, ev_vector, #name, (size_t)&((csqcentvars_t*)0)->name, -1);\n#define comfieldentity(name,desc) PR_RegisterFieldVar(csqcprogs, ev_entity, #name, (size_t)&((csqcentvars_t*)0)->name, -1);\n#define comfieldstring(name,desc) PR_RegisterFieldVar(csqcprogs, ev_string, #name, (size_t)&((csqcentvars_t*)0)->name, -1);\n#define comfieldfunction(name, typestr,desc) PR_RegisterFieldVar(csqcprogs, ev_function, #name, (size_t)&((csqcentvars_t*)0)->name, -1);\ncomqcfields\n#undef comfieldfloat\n#undef comfieldint\n#undef comfieldvector\n#undef comfieldentity\n#undef comfieldstring\n#undef comfieldfunction\n\n#ifdef VM_Q1\n#define comfieldfloat(name,desc) PR_RegisterFieldVar(csqcprogs, ev_float, #name, sizeof(csqcentvars_t) + (size_t)&((csqcextentvars_t*)0)->name, -1);\n#define comfieldint(name,desc) PR_RegisterFieldVar(csqcprogs, ev_integer, #name, sizeof(csqcentvars_t) + (size_t)&((csqcextentvars_t*)0)->name, -1);\n#define comfieldvector(name,desc) PR_RegisterFieldVar(csqcprogs, ev_vector, #name, sizeof(csqcentvars_t) + (size_t)&((csqcextentvars_t*)0)->name, -1);\n#define comfieldentity(name,desc) PR_RegisterFieldVar(csqcprogs, ev_entity, #name, sizeof(csqcentvars_t) + (size_t)&((csqcextentvars_t*)0)->name, -1);\n#define comfieldstring(name,desc) PR_RegisterFieldVar(csqcprogs, ev_string, #name, sizeof(csqcentvars_t) + (size_t)&((csqcextentvars_t*)0)->name, -1);\n#define comfieldfunction(name, typestr,desc) PR_RegisterFieldVar(csqcprogs, ev_function, #name, sizeof(csqcentvars_t) + (size_t)&((csqcextentvars_t*)0)->name, -1);\n#else\n#define comfieldfloat(name,desc) PR_RegisterFieldVar(csqcprogs, ev_float, #name, (size_t)&((csqcentvars_t*)0)->name, -1);\n#define comfieldint(name,desc) PR_RegisterFieldVar(csqcprogs, ev_integer, #name, (size_t)&((csqcentvars_t*)0)->name, -1);\n#define comfieldvector(name,desc) PR_RegisterFieldVar(csqcprogs, ev_vector, #name, (size_t)&((csqcentvars_t*)0)->name, -1);\n#define comfieldentity(name,desc) PR_RegisterFieldVar(csqcprogs, ev_entity, #name, (size_t)&((csqcentvars_t*)0)->name, -1);\n#define comfieldstring(name,desc) PR_RegisterFieldVar(csqcprogs, ev_string, #name, (size_t)&((csqcentvars_t*)0)->name, -1);\n#define comfieldfunction(name, typestr,desc) PR_RegisterFieldVar(csqcprogs, ev_function, #name, (size_t)&((csqcentvars_t*)0)->name, -1);\n#endif\ncomextqcfields\ncsqcextfields\n#undef fieldfloat\n#undef fieldint\n#undef fieldvector\n#undef fieldentity\n#undef fieldstring\n#undef fieldfunction\n\n\tPR_RegisterFieldVar(csqcprogs, ev_void, NULL, pr_fixbrokenqccarrays.ival, -1);\n}\n\nstatic csqcedict_t **csqcent;\nstatic int maxcsqcentities;\n\nstatic int csqcentsize;\n\nstatic const char *csqcmapentitydata;\nstatic qboolean csqcmapentitydataloaded;\n\nstatic unsigned int csqc_deprecated_warned;\n#define csqc_deprecated(s) do {if (!csqc_deprecated_warned++){Con_Printf(CON_WARNING\"csqc deprecation warning: %s\\n\", s); PR_StackTrace (prinst, false);}}while(0)\n\nstatic model_t *CSQC_GetModelForIndex(int index);\n\nstatic void CS_CheckVelocity(csqcedict_t *ent)\n{\n}\n\n\n\n\n\n\nstatic void cs_getframestate(csqcedict_t *in, unsigned int rflags, framestate_t *fte_restrict out)\n{\n\t//FTE_CSQC_HALFLIFE_MODELS\n#ifdef HALFLIFEMODELS\n\tout->bonecontrols[0] = in->xv->bonecontrol1;\n\tout->bonecontrols[1] = in->xv->bonecontrol2;\n\tout->bonecontrols[2] = in->xv->bonecontrol3;\n\tout->bonecontrols[3] = in->xv->bonecontrol4;\n\tout->bonecontrols[4] = in->xv->bonecontrol5;\n\tout->g[FS_REG].subblendfrac = in->xv->subblendfrac;\n\tout->g[FS_REG].subblend2frac = in->xv->subblend2frac;\n\tout->g[FST_BASE].subblendfrac = in->xv->subblendfrac;\n\tout->g[FST_BASE].subblend2frac = in->xv->subblend2frac;\n#endif\n\n\t//FTE_CSQC_BASEFRAME\n\tout->g[FST_BASE].endbone = in->xv->basebone;\n\tif (out->g[FST_BASE].endbone)\n\t{\t//small optimisation.\n\t\tout->g[FST_BASE].endbone -= 1;\n\n\t\tout->g[FST_BASE].frame[0] = in->xv->baseframe;\n\t\tout->g[FST_BASE].frame[1] = in->xv->baseframe2;\n\t\tout->g[FST_BASE].lerpweight[1] = in->xv->baselerpfrac;\n\t\t//out->g[FST_BASE].frame[3] = in->xv->baseframe4;\n\n#if 0//FRAME_BLENDS >= 4\n//\t\tout->g[FST_BASE].frame[2] = in->xv->baseframe3;\n//\t\tout->g[FST_BASE].lerpweight[2] = in->xv->baselerpfrac3;\n//\t\tout->g[FST_BASE].frame[3] = in->xv->baseframe3;\n//\t\tout->g[FST_BASE].lerpweight[3] = in->xv->baselerpfrac4;\n\t\tout->g[FST_BASE].lerpweight[0] = 1-(out->g[FST_BASE].lerpweight[1]+out->g[FST_BASE].lerpweight[2]+out->g[FST_BASE].lerpweight[3]);\n#else\n\t\tout->g[FST_BASE].lerpweight[0] = 1-(out->g[FST_BASE].lerpweight[1]);\n#endif\n\t\tif (rflags & CSQCRF_FRAMETIMESARESTARTTIMES)\n\t\t{\n\t\t\tout->g[FST_BASE].frametime[0] = *csqcg.time - in->xv->baseframe1time;\n\t\t\tout->g[FST_BASE].frametime[1] = *csqcg.time - in->xv->baseframe2time;\n\t\t\t//out->g[FST_BASE].frametime[2] = *csqcg.simtime - in->xv->baseframe3time;\n\t\t\t//out->g[FST_BASE].frametime[3] = *csqcg.simtime - in->xv->baseframe4time;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tout->g[FST_BASE].frametime[0] = in->xv->baseframe1time;\n\t\t\tout->g[FST_BASE].frametime[1] = in->xv->baseframe2time;\n\t\t\t//out->g[FST_BASE].frametime[2] = in->xv->baseframe3time;\n\t\t\t//out->g[FST_BASE].frametime[3] = in->xv->baseframe4time;\n\t\t}\n\t}\n\n\t//and the normal frames.\n\tout->g[FS_REG].endbone = 0x7fffffff;\n\tout->g[FS_REG].frame[0] = in->v->frame;\n\tout->g[FS_REG].frame[1] = in->xv->frame2;\n\tout->g[FS_REG].lerpweight[1] = in->xv->lerpfrac;\n#if FRAME_BLENDS >= 4\n\tout->g[FS_REG].frame[2] = in->xv->frame3;\n\tout->g[FS_REG].lerpweight[2] = in->xv->lerpfrac3;\n\tout->g[FS_REG].frame[3] = in->xv->frame4;\n\tout->g[FS_REG].lerpweight[3] = in->xv->lerpfrac4;\n\tout->g[FS_REG].lerpweight[0] = 1-(out->g[FS_REG].lerpweight[1]+out->g[FS_REG].lerpweight[2]+out->g[FS_REG].lerpweight[3]);\n#else\n\tout->g[FS_REG].lerpweight[0] = 1-(out->g[FS_REG].lerpweight[1]);\n#endif\n\tif ((rflags & CSQCRF_FRAMETIMESARESTARTTIMES) || csqc_isdarkplaces)\n\t{\n\t\tout->g[FS_REG].frametime[0] = *csqcg.time - in->xv->frame1time;\n\t\tout->g[FS_REG].frametime[1] = *csqcg.time - in->xv->frame2time;\n#if FRAME_BLENDS >= 4\n\t\tout->g[FS_REG].frametime[2] = *csqcg.time - in->xv->frame3time;\n\t\tout->g[FS_REG].frametime[3] = *csqcg.time - in->xv->frame4time;\n#endif\n\t}\n\telse\n\t{\n\t\tout->g[FS_REG].frametime[0] = in->xv->frame1time;\n\t\tout->g[FS_REG].frametime[1] = in->xv->frame2time;\n#if FRAME_BLENDS >= 4\n\t\tout->g[FS_REG].frametime[2] = in->xv->frame3time;\n\t\tout->g[FS_REG].frametime[3] = in->xv->frame4time;\n#endif\n\t}\n\n\n#if defined(SKELETALOBJECTS) || defined(RAGDOLL)\n\tout->bonecount = 0;\n\tout->bonestate = NULL;\n\tif (in->xv->skeletonindex)\n\t\tskel_lookup(&csqc_world, in->xv->skeletonindex, out);\n#endif\n}\n\n\nstatic void QCBUILTIN PF_cs_remove (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tcsqcedict_t *ed;\n\n\ted = (csqcedict_t*)G_EDICT(prinst, OFS_PARM0);\n\n\tif (ED_ISFREE(ed))\n\t{\n\t\tcsqc_deprecated(\"Tried removing free entity\");\n\t\treturn;\n\t}\n\n\tif (!ed->entnum)\n\t{\n\t\tCon_Printf(\"Unable to remove the world.\\n\");\n\t\tPR_StackTrace (prinst, false);\n\t\treturn;\n\t}\n\tif (ed->readonly)\n\t{\n\t\tCon_Printf(\"Entity %i is readonly.\\n\", ed->entnum);\n\t\treturn;\n\t}\n\n\tif (pe)\n\t\tpe->DelinkTrailstate(&ed->trailstate);\n\tWorld_UnlinkEdict((wedict_t*)ed);\n\tED_Free (prinst, (void*)ed);\n}\nstatic void QCBUILTIN PF_cs_removeinstant (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tcsqcedict_t *ed;\n\n\ted = (csqcedict_t*)G_EDICT(prinst, OFS_PARM0);\n\n\tif (ED_ISFREE(ed))\n\t{\n\t\tcsqc_deprecated(\"Tried removing free entity\");\n\t\treturn;\n\t}\n\n\tif (!ed->entnum)\n\t{\n\t\tCon_Printf(\"Unable to remove the world. Try godmode.\\n\");\n\t\tPR_StackTrace (prinst, false);\n\t\treturn;\n\t}\n\tif (ed->readonly)\n\t{\n\t\tCon_Printf(\"Entity %i is readonly.\\n\", ed->entnum);\n\t\treturn;\n\t}\n\n\tif (pe)\n\t\tpe->DelinkTrailstate(&ed->trailstate);\n\tWorld_UnlinkEdict((wedict_t*)ed);\n\tprinst->EntFree(prinst, (void*)ed, true);\n}\n\nstatic void QCBUILTIN PF_cvar (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tcvar_t\t*var;\n\tconst char\t*str;\n\n\tstr = PR_GetStringOfs(prinst, OFS_PARM0);\n\n\tif (!strcmp(str, \"vid_conwidth\"))\n\t{\n\t\tif (!csqc_isdarkplaces)\t//don't warn when its unfixable...\n\t\t\tcsqc_deprecated(\"vid_conwidth - use (vector)getviewprop(VF_SCREENVSIZE)\");\n\t\tG_FLOAT(OFS_RETURN) = vid.width;\n\t}\n\telse if (!strcmp(str, \"vid_conheight\"))\n\t{\n\t\tif (!csqc_isdarkplaces)\n\t\t\tcsqc_deprecated(\"vid_conheight - use (vector)getviewprop(VF_SCREENVSIZE)\");\n\t\tG_FLOAT(OFS_RETURN) = vid.height;\n\t}\n\telse\n\t{\n\t\tvar = PF_Cvar_FindOrGet (str);\n\t\tif (var && !(var->flags & CVAR_NOUNSAFEEXPAND))\n\t\t\tG_FLOAT(OFS_RETURN) = var->value;\n\t\telse\n\t\t\tG_FLOAT(OFS_RETURN) = 0;\n\t}\n}\n\n//too specific to the prinst's builtins.\nstatic void QCBUILTIN PF_Fixme (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint binum;\n\tchar fname[MAX_QPATH];\n\tif (!prinst->GetBuiltinCallInfo(prinst, &binum, fname, sizeof(fname)))\n\t{\n\t\tbinum = 0;\n\t\tstrcpy(fname, \"?unknown?\");\n\t}\n\n\tCon_Printf(\"\\n\");\n\n\tprinst->RunError(prinst, \"\\nBuiltin %i:%s not implemented.\\nCSQC is not compatible.\", binum, fname);\n\tPR_BIError (prinst, \"bulitin not implemented\");\n}\nstatic void QCBUILTIN PF_Ignore (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tG_INT(OFS_RETURN) = 0;\n}\nstatic void QCBUILTIN PF_NoCSQC (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint binum;\n\tchar fname[MAX_QPATH];\n\tif (!prinst->GetBuiltinCallInfo(prinst, &binum, fname, sizeof(fname)))\n\t{\n\t\tbinum = 0;\n\t\tstrcpy(fname, \"?unknown?\");\n\t}\n\n\tCon_Printf(\"\\n\");\n\n\tprinst->RunError(prinst, \"\\nBuiltin %i:%s does not make sense in csqc.\\nCSQC is not compatible.\", binum, fname);\n\tPR_BIError (prinst, \"bulitin not implemented\");\n}\nstatic void QCBUILTIN PF_checkbuiltin (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tfunc_t funcref = G_INT(OFS_PARM0);\n\tchar *funcname = NULL;\n\tint args;\n\tint builtinno;\n\tif (prinst->GetFunctionInfo(prinst, funcref, &args, NULL, &builtinno, funcname, sizeof(funcname)))\n\t{\t//qc defines the function at least. nothing weird there...\n\t\tif (builtinno > 0 && builtinno < prinst->parms->numglobalbuiltins)\n\t\t{\n\t\t\tif (!prinst->parms->globalbuiltins[builtinno] || prinst->parms->globalbuiltins[builtinno] == PF_Fixme || prinst->parms->globalbuiltins[builtinno] == PF_Ignore || prinst->parms->globalbuiltins[builtinno] == PF_NoCSQC)\n\t\t\t\tG_FLOAT(OFS_RETURN) = false;\t//the builtin with that number isn't defined.\n\t\t\telse\n\t\t\t{\n\t\t\t\tG_FLOAT(OFS_RETURN) = true;\t\t//its defined, within the sane range, mapped, everything. all looks good.\n\t\t\t\t//we should probably go through the available builtins and validate that the qc's name matches what would be expected\n\t\t\t\t//this is really intended more for builtins defined as #0 though, in such cases, mismatched assumptions are impossible.\n\t\t\t}\n\t\t}\n\t\telse\n\t\t\tG_FLOAT(OFS_RETURN) = false;\t//not a valid builtin (#0 builtins get remapped according to the function name)\n\t}\n\telse\n\t{\t//not valid somehow.\n\t\tG_FLOAT(OFS_RETURN) = false;\n\t}\n}\n\nstatic void QCBUILTIN PF_cl_cprint (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char *str = PF_VarString(prinst, 0, pr_globals);\n\tif (csqc_playerseat >= 0)\n\t\tSCR_CenterPrint(csqc_playerseat, str, true);\n}\n\nstatic void QCBUILTIN PF_cs_makevectors (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tif (!csqcg.v_forward || !csqcg.v_right || !csqcg.v_up)\n\t\tHost_EndGame(\"PF_makevectors: one of v_forward, v_right or v_up was not defined\\n\");\n\tAngleVectors (G_VECTOR(OFS_PARM0), csqcg.v_forward, csqcg.v_right, csqcg.v_up);\n}\n\nstatic int CS_FindModel(const char *name, int *free)\n{\n\tint i;\n\tconst char *fixedname;\n\n\t*free = 0;\n\n\tif (!name || !*name)\n\t\treturn 0;\n\n\tfixedname = Mod_FixName(name, csqc_world.worldmodel->publicname);\n\n\tfor (i = 1; i < MAX_CSMODELS; i++)\n\t{\n\t\tif (!*cl.model_csqcname[i])\n\t\t{\n\t\t\t*free = -i;\n\t\t\tbreak;\n\t\t}\n\t\tif (!strcmp(cl.model_csqcname[i], fixedname))\n\t\t\treturn -i;\n\t}\n\tfor (i = 1; i < MAX_PRECACHE_MODELS; i++)\n\t{\n\t\tif (!cl.model_name[i])\n\t\t\tbreak;\n\t\tif (!strcmp(cl.model_name[i], name))\n\t\t\treturn i;\n\t}\n\treturn 0;\n}\n\nstatic model_t *CSQC_GetModelForIndex(int index)\n{\n\tif (index == 0)\n\t\treturn NULL;\n\telse if (index > 0 && index < MAX_PRECACHE_MODELS)\n\t\treturn cl.model_precache[index];\n\telse if (index < 0 && index > -MAX_CSMODELS)\n\t{\n\t\tif (!cl.model_csqcprecache[-index])\n\t\t\tcl.model_csqcprecache[-index] = Mod_ForName(Mod_FixName(cl.model_csqcname[-index], csqc_world.worldmodel?csqc_world.worldmodel->publicname:NULL), MLV_WARN);\n\t\treturn cl.model_csqcprecache[-index];\n\t}\n\telse\n\t\treturn NULL;\n}\n\nstatic model_t *CSQC_GetModelForName(const char *modelname)\n{\n\tint modelindex, freei;\n\tif (!modelname && *modelname)\n\t\treturn NULL;\t//zomg, no name\n\tmodelindex = CS_FindModel(modelname, &freei);\n\n\t//make sure it has an index.\n\tif (!modelindex)\n\t{\n\t\tif (!freei)\n\t\t\tHost_EndGame(\"CSQC ran out of model slots\\n\");\n\t\tCon_DPrintf(\"Late caching model \\\"%s\\\"\\n\", modelname);\n\t\tQ_strncpyz(cl.model_csqcname[-freei], modelname, sizeof(cl.model_csqcname[-freei]));\t//allocate a slot now\n\t\tmodelindex = freei;\n\n\t\tcl.model_csqcprecache[-freei] = NULL;\n\t}\n\treturn CSQC_GetModelForIndex(modelindex);\n}\n\nstatic float CSQC_PitchScaleForModelIndex(int index)\n{\n\tmodel_t *mod = CSQC_GetModelForIndex(index);\n\tif (mod && (mod->type == mod_alias || mod->type == mod_halflife))\n\t\treturn r_meshpitch.value;\t//these are buggy.\n\treturn 1;\n}\n\n#ifdef SKELETALOBJECTS\nwedict_t *skel_gettaginfo_args (pubprogfuncs_t *prinst, vec3_t axis[3], vec3_t origin, int tagent, int tagnum);\n#endif\n\nstatic qboolean CopyCSQCEdictToEntity(csqcedict_t *fte_restrict in, entity_t *fte_restrict out)\n{\n\tint ival;\n\tmodel_t *model;\n\tunsigned int rflags;\n\tunsigned int effects;\n\n\tival = in->v->modelindex;\n\tmodel = CSQC_GetModelForIndex(ival);\n\tif (!model)\n\t\treturn false; //there might be other ent types later as an extension that stop this.\n\n\tmemset(out, 0, sizeof(*out));\n\tout->model = model;\n\tout->pvscache = in->pvsinfo;\n\n\trflags = in->xv->renderflags;\n\tif (csqc_isdarkplaces)\n\t\trflags ^= CSQCRF_FRAMETIMESARESTARTTIMES;\n\tif (rflags)\n\t{\n\t\trflags = in->xv->renderflags;\n\t\tif (rflags & CSQCRF_VIEWMODEL)\n\t\t{\n\t\t\tout->flags |= RF_DEPTHHACK|RF_WEAPONMODEL;\n\t\t\tout->pvscache.num_leafs = -1;\n\t\t}\n\t\tif (rflags & CSQCRF_EXTERNALMODEL)\n\t\t\tout->flags |= RF_EXTERNALMODEL;\n\t\tif (rflags & CSQCRF_FIRSTPERSON)\n\t\t\tout->flags |= RF_FIRSTPERSON;\n\t\tif (rflags & CSQCRF_DEPTHHACK)\n\t\t\tout->flags |= RF_DEPTHHACK;\n\t\tif (rflags & CSQCRF_ADDITIVE)\n\t\t\tout->flags |= RF_ADDITIVE;\n\t\t//CSQCRF_USEAXIS is below\n\t\tif (rflags & CSQCRF_NOSHADOW)\n\t\t\tout->flags |= RF_NOSHADOW;\n\t\t//CSQCRF_FRAMETIMESARESTARTTIMES is handled by cs_getframestate below\n\n//\t\tif (rflags & CSQCRF_REMOVED)\n//\t\t\tCon_Printf(\"Warning: CSQCRF_NOAUTOADD is no longer supported\\n\");\n\t}\n\n\teffects = in->v->effects;\n\tif (effects & EF_FULLBRIGHT)\n\t\tout->flags |= RF_FULLBRIGHT;\n\tif (effects & NQEF_ADDITIVE)\n\t\tout->flags |= RF_ADDITIVE;\n\tif (effects & EF_NOSHADOW)\n\t\tout->flags |= RF_NOSHADOW;\n\tif (effects & EF_NODEPTHTEST)\n\t\tout->flags |= RF_NODEPTHTEST;\n\tif ((effects & DPEF_NOGUNBOB) && (out->flags & RF_WEAPONMODEL))\n\t\tout->flags |= RF_WEAPONMODELNOBOB;\n\n\tcs_getframestate(in, rflags, &out->framestate);\n\n\tVectorCopy(in->v->origin, out->origin);\n\tVectorCopy(in->v->oldorigin, out->oldorigin);\n\tif (in->v->enemy)\n\t{\n\t\tcsqcedict_t *ed = (csqcedict_t*)PROG_TO_EDICT(csqcprogs, in->v->enemy);\n\t\tVectorSubtract(out->oldorigin, ed->v->oldorigin, out->oldorigin);\n\t}\n\n\tif (rflags & CSQCRF_USEAXIS)\n\t{\n\t\tVectorCopy(csqcg.v_forward, out->axis[0]);\n\t\tVectorNegate(csqcg.v_right, out->axis[1]);\n\t\tVectorCopy(csqcg.v_up, out->axis[2]);\n\t\tout->scale = 1;\n\t}\n\telse\n\t{\n\t\tVectorCopy(in->v->angles, out->angles);\n\t\tif (model && model->type == mod_alias)\n\t\t{\n\t\t\tout->angles[0] *= r_meshpitch.value;\n\t\t\tout->angles[2] *= r_meshroll.value;\n\t\t}\n\t\tAngleVectors(out->angles, out->axis[0], out->axis[1], out->axis[2]);\n\t\tVectorInverse(out->axis[1]);\n\n\t\tif (!in->xv->scale)\n\t\t\tout->scale = 1;\n\t\telse\n\t\t\tout->scale = in->xv->scale;\n\t}\n\n\tif (csqc_isdarkplaces && in->xv->tag_entity)\n\t{\n#ifdef SKELETALOBJECTS\n\t\tcsqcedict_t *p = (csqcedict_t*)skel_gettaginfo_args(csqcprogs, out->axis, out->origin, in->xv->tag_entity, in->xv->tag_index);\n\t\tif (p && (int)p->xv->renderflags & CSQCRF_VIEWMODEL)\n\t\t\tout->flags |= RF_DEPTHHACK|RF_WEAPONMODEL;\n\t\tout->pvscache = p->pvsinfo; //for the areas.\n\t\tout->pvscache.num_leafs = -1;\t//make visible globally\n#if defined(Q2BSPS) || defined(Q3BSPS) || defined(TERRAIN)\n\t\tout->pvscache.headnode = 0;\n#endif\n#endif\n\t}\n\n\tival = in->v->colormap;\n\tout->playerindex = -1;\n\tif (ival > 0 && ival <= MAX_CLIENTS)\n\t{\n\t\tout->playerindex = ival - 1;\n\t\tout->topcolour = cl.players[ival-1].dtopcolor;\n\t\tout->bottomcolour = cl.players[ival-1].dbottomcolor;\n\t}\n\telse if (ival /*>= 1024*/)\n\t{\n\t\t//DP COLORMAP extension\n\t\tout->topcolour = (ival>>4) & 0x0f;\n\t\tout->bottomcolour = ival & 0xf;\n\t}\n\telse\n\t{\n\t\tout->topcolour = TOP_DEFAULT;\n\t\tout->bottomcolour = BOTTOM_DEFAULT;\n\t}\n\n\tif (!in->xv->colormod[0] && !in->xv->colormod[1] && !in->xv->colormod[2])\n\t\tVectorSet(out->shaderRGBAf, 1, 1, 1);\n\telse\n\t{\n\t\tout->flags |= RF_FORCECOLOURMOD;\n\t\tVectorCopy(in->xv->colormod, out->shaderRGBAf);\n\t}\n\tif (!in->xv->alpha || in->xv->alpha == 1)\n\t\tout->shaderRGBAf[3] = 1.0f;\n\telse\n\t{\n\t\tout->flags |= RF_TRANSLUCENT;\n\t\tout->shaderRGBAf[3] = in->xv->alpha;\n\t}\n\n\tif (!in->xv->glowmod[0] && !in->xv->glowmod[1] && !in->xv->glowmod[2])\n\t\tVectorSet(out->glowmod, 1, 1, 1);\n\telse\n\t\tVectorCopy(in->xv->glowmod, out->glowmod);\n\n#ifdef HEXEN2\n\tout->drawflags = in->xv->drawflags;\n\tout->abslight = in->xv->abslight;\n#endif\n\tout->skinnum = in->v->skin;\n\tout->fatness = in->xv->fatness;\n\tival = in->xv->forceshader;\n\tif (ival >= 1 && ival <= r_numshaders)\n\t\tout->forcedshader = r_shaders[(ival-1)];\n\telse\n\t\tout->forcedshader = NULL;\n\tout->customskin = (in->skinobject<0)?-in->skinobject:in->skinobject;\n\n\tif (in->xv->entnum && !in->xv->camera_transform)\t//yes, camera_transform is this hacky\n\t\tout->keynum = in->xv->entnum;\n\telse\n\t\tout->keynum = -in->entnum;\n\n\treturn true;\n}\n\nconst char *CSQC_GetExtraFieldInfo(void *went, char *out, size_t outsize)\n{\n\tcsqcedict_t *ent = went;\n\tchar *r = out;\n\tchar *e = out+outsize;\n\tint i;\n\tskinfile_t *sk = Mod_LookupSkin((ent->skinobject<0)?-ent->skinobject:ent->skinobject);\n\tif (sk)\n\t{\n\t\tQ_snprintfz(out, e-out, \"skin %s\\nrefs %i\\n\", sk->skinname, sk->refcount);\n\t\tout+=strlen(out);\n\t\tfor (i = 0; i < sk->nummappings; i++)\n\t\t{\n\t\t\tQ_snprintfz(out, e-out, \"replace \\\"%s\\\" \\\"%s\\\"\\n\", sk->mappings[i].surface, sk->mappings[i].shader?sk->mappings[i].shader->name:\"NULL SHADER\");\n\t\t\tout+=strlen(out);\n\t\t}\n\t\tfor (i = 0; i < MAX_GEOMSETS; i++)\n\t\t{\n\t\t\tif (sk->geomset[i])\n\t\t\t{\n\t\t\t\tQ_snprintfz(out, e-out, \"geomset %i %i\\n\", i, sk->geomset[i]);\n\t\t\t\tout+=strlen(out);\n\t\t\t}\n\t\t}\n#ifdef QWSKINS\n\t\tif (*sk->qwskinname)\n\t\t{\n\t\t\tQ_snprintfz(out, e-out, \"qwskin %s\\n\", sk->qwskinname);\n\t\t\tout+=strlen(out);\n\t\t}\n\t\tif (sk->q1upper != Q1UNSPECIFIED)\n\t\t{\n\t\t\tQ_snprintfz(out, e-out, \"q1upper %#x\\n\", sk->q1upper);\n\t\t\tout+=strlen(out);\n\t\t}\n\t\tif (sk->q1lower != Q1UNSPECIFIED)\n\t\t{\n\t\t\tQ_snprintfz(out, e-out, \"q1lower %#x\\n\", sk->q1lower);\n\t\t\tout+=strlen(out);\n\t\t}\n#ifdef HEXEN2\n\t\tif (sk->h2class != Q1UNSPECIFIED)\n\t\t{\n\t\t\tQ_snprintfz(out, e-out, \"h2class %i\\n\", sk->h2class);\n\t\t\tout+=strlen(out);\n\t\t}\n#endif\n#endif\n\n\t\treturn r;\n\t}\n\treturn r==out?NULL:r;\n}\n\nstatic void QCBUILTIN PF_cs_makestatic (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\t//still does a remove.\n\tcsqcedict_t *in = (void*)G_EDICT(prinst, OFS_PARM0);\n\tentity_t *ent;\n\n\tif (cl.num_statics == cl_max_static_entities)\n\t{\n\t\tcl_max_static_entities += 16;\n\t\tcl_static_entities = BZ_Realloc(cl_static_entities, sizeof(*cl_static_entities) * cl_max_static_entities);\n\t}\n\n\tent = &cl_static_entities[cl.num_statics].ent;\n\tif (CopyCSQCEdictToEntity(in, ent))\n\t{\n\t\tentity_state_t *state = &cl_static_entities[cl.num_statics].state;\n\t\tmemset(state, 0, sizeof(*state));\n\t\tcl_static_entities[cl.num_statics].emit = trailkey_null;\n\t\tcl_static_entities[cl.num_statics].mdlidx = in->v->modelindex;\n\t\tif (cl.worldmodel && cl.worldmodel->funcs.FindTouchedLeafs)\n\t\t\tcl.worldmodel->funcs.FindTouchedLeafs(cl.worldmodel, &cl_static_entities[cl.num_statics].ent.pvscache, in->v->absmin, in->v->absmax);\n\t\telse\n\t\t\tmemset(&cl_static_entities[cl.num_statics].ent.pvscache, 0, sizeof(cl_static_entities[cl.num_statics].ent.pvscache));\n\t\tcl.num_statics++;\n\n\t\t//rtlights kinda need all this junk\n\t\tVectorCopy(ent->origin, state->origin);\n\t\tVectorCopy(ent->angles, state->angles);\n\t\tstate->modelindex = in->v->modelindex;\n\t\tstate->light[3] = in->xv->light_lev;\n\t\tVectorCopy(in->xv->color, state->light);\n\t\tstate->lightpflags = in->xv->pflags;\n\t\tstate->lightstyle = in->xv->style;\n\t\tstate->skinnum = in->v->skin;\n\t}\n\n\tPF_cs_remove(prinst, pr_globals);\n}\n\nstatic void QCBUILTIN PF_R_AddEntity(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tcsqcedict_t *in = (void*)G_EDICT(prinst, OFS_PARM0);\n\tentity_t ent;\n\tif (ED_ISFREE(in) || in->entnum == 0)\n\t{\n\t\tcsqc_deprecated(\"Tried drawing a free/removed/world entity\\n\");\n\t\treturn;\n\t}\n\n\tif (CopyCSQCEdictToEntity(in, &ent))\n\t{\n\t\tCLQ1_AddShadow(&ent);\n\t\tV_AddAxisEntity(&ent);\n\t}\n}\n\nstatic void QCBUILTIN PF_R_AddEntityLighting(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tcsqcedict_t *in = (void*)G_EDICT(prinst, OFS_PARM0);\n\tentity_t ent;\n\tif (ED_ISFREE(in) || in->entnum == 0)\n\t{\n\t\tcsqc_deprecated(\"Tried drawing a free/removed/world entity\\n\");\n\t\treturn;\n\t}\n\n\tif (CopyCSQCEdictToEntity(in, &ent))\n\t{\n\t\tent.light_known = true;\n\t\tVectorCopy(G_VECTOR(OFS_PARM1), ent.light_dir);\n\t\tVectorCopy(G_VECTOR(OFS_PARM2), ent.light_avg);\n\t\tVectorCopy(G_VECTOR(OFS_PARM3), ent.light_range);\n\n\t\tCLQ1_AddShadow(&ent);\n\t\tV_AddAxisEntity(&ent);\n\t}\n}\n\nstatic void QCBUILTIN PF_R_RemoveEntity(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tcsqcedict_t *in = (void*)G_EDICT(prinst, OFS_PARM0);\n\tint keynum, i;\n\tif (ED_ISFREE(in) || in->entnum == 0)\n\t{\n\t\tcsqc_deprecated(\"Tried drawing a free/removed/world entity\\n\");\n\t\treturn;\n\t}\n\n\t//work out the internal key that relates to the given ent. we'll remove all ents with the same key.\n\tif (in->xv->entnum && !in->xv->camera_transform)\t//yes, camera_transform is this hacky\n\t\tkeynum = in->xv->entnum;\n\telse\n\t\tkeynum = -in->entnum;\n\n\tfor (i = 0; i < cl_numvisedicts; )\n\t{\n\t\tif (cl_visedicts[i].keynum == keynum)\n\t\t{\n\t\t\tcl_numvisedicts--;\n\t\t\tmemmove(&cl_visedicts[i], &cl_visedicts[i+1], sizeof(*cl_visedicts)*(cl_numvisedicts-i));\n\t\t}\n\t\telse\n\t\t\ti++;\n\t}\n}\nvoid CL_AddDecal(shader_t *shader, vec3_t origin, vec3_t up, vec3_t side, vec3_t rgbvalue, float alphavalue);\nstatic void QCBUILTIN PF_R_AddDecal(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tshader_t *shader = R_RegisterShader(PR_GetStringOfs(prinst, OFS_PARM0), SUF_NONE, \n\t\t\"{\\n\"\n\t\t\t\"polygonOffset\\n\"\n\t\t\t\"surfaceparms nodlight\\n\"\n\t\t\t\"{\\n\"\n\t\t\t\t\"map $diffuse\\n\"\n\t\t\t\t\"rgbgen vertex\\n\"\n\t\t\t\t\"alphagen vertex\\n\"\n\t\t\t\t\"blendfunc gl_one gl_one_minus_src_alpha\\n\"\n\t\t\t\"}\\n\"\n\t\t\"}\\n\");\n\tfloat *org = G_VECTOR(OFS_PARM1);\n\tfloat *up = G_VECTOR(OFS_PARM2);\n\tfloat *side = G_VECTOR(OFS_PARM3);\n\tfloat *rgb = G_VECTOR(OFS_PARM4);\n\tfloat alpha = G_FLOAT(OFS_PARM5);\n\tif (shader)\n\t\tCL_AddDecal(shader, org, up, side, rgb, alpha);\n}\n\nstatic void QCBUILTIN PF_R_DynamicLight_Set(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char *s;\n\tdlight_t *l;\n\tsize_t lno = G_FLOAT(OFS_PARM0);\n\tint field = G_FLOAT(OFS_PARM1);\n\twhile (lno >= cl_maxdlights)\n\t{\n\t\tif (lno > 1000)\n\t\t\treturn;\n\t\tCL_AllocSlight();\n\t}\n\tl = cl_dlights+lno;\n\tswitch (field)\n\t{\n\tcase lfield_origin:\n\t\tVectorCopy(G_VECTOR(OFS_PARM2), l->origin);\n\t\tl->rebuildcache = true;\n\t\tbreak;\n\tcase lfield_colour:\n\t\tVectorCopy(G_VECTOR(OFS_PARM2), l->color);\n\t\tbreak;\n\tcase lfield_radius:\n\t\tl->radius = G_FLOAT(OFS_PARM2);\n\t\tl->rebuildcache = true;\n\t\tif (lno >= rtlights_max)\n\t\t\trtlights_max = lno+1;\n\t\tbreak;\n\tcase lfield_flags:\n\t\tl->flags = G_FLOAT(OFS_PARM2);\n\t\tl->rebuildcache = true;\n\t\tbreak;\n\tcase lfield_style:\n\t\tl->style = G_FLOAT(OFS_PARM2);\n\t\tbreak;\n\tcase lfield_angles:\n\t\tVectorCopy(G_VECTOR(OFS_PARM2), l->angles);\n\t\tAngleVectors(l->angles, l->axis[0], l->axis[1], l->axis[2]);\n\t\tVectorInverse(l->axis[1]);\n\t\tbreak;\n\tcase lfield_fov:\n\t\tl->fov = G_FLOAT(OFS_PARM2);\n\t\tbreak;\n\tcase lfield_nearclip:\n\t\tl->nearclip = G_FLOAT(OFS_PARM2);\n\t\tbreak;\n\tcase lfield_corona:\n\t\tl->corona = G_FLOAT(OFS_PARM2);\n\t\tbreak;\n\tcase lfield_coronascale:\n\t\tl->coronascale = G_FLOAT(OFS_PARM2);\n\t\tbreak;\n\tcase lfield_cubemapname:\n\t\ts = PR_GetStringOfs(prinst, OFS_PARM2);\n\t\tQ_strncpyz(l->cubemapname, s, sizeof(l->cubemapname));\n\t\tif (*l->cubemapname)\n\t\t\tl->cubetexture = R_LoadReplacementTexture(l->cubemapname, \"\", IF_TEXTYPE_CUBE, NULL, 0, 0, TF_INVALID);\n\t\telse\n\t\t\tl->cubetexture = r_nulltex;\n\t\tbreak;\n#ifdef RTLIGHTS\n\tcase lfield_stylestring:\n\t\ts = PR_GetStringOfs(prinst, OFS_PARM2);\n\t\tZ_Free(l->customstyle);\n\t\tl->customstyle = (s&&*s)?Z_StrDup(s):NULL;\n\t\tbreak;\n\tcase lfield_ambientscale:\n\t\tl->lightcolourscales[0] = G_FLOAT(OFS_PARM2);\n\t\tbreak;\n\tcase lfield_diffusescale:\n\t\tl->lightcolourscales[1] = G_FLOAT(OFS_PARM2);\n\t\tbreak;\n\tcase lfield_specularscale:\n\t\tl->lightcolourscales[2] = G_FLOAT(OFS_PARM2);\n\t\tbreak;\n\tcase lfield_rotation:\n\t\tl->rotation[0] = G_FLOAT(OFS_PARM2+0);\n\t\tl->rotation[1] = G_FLOAT(OFS_PARM2+1);\n\t\tl->rotation[2] = G_FLOAT(OFS_PARM2+2);\n\t\tbreak;\n#endif\n\tcase lfield_dietime:\n\t\tl->die = G_FLOAT(OFS_PARM2);\n\t\tbreak;\n\tcase lfield_rgbdecay:\n\t\tl->channelfade[0] = G_FLOAT(OFS_PARM2+0);\n\t\tl->channelfade[1] = G_FLOAT(OFS_PARM2+1);\n\t\tl->channelfade[2] = G_FLOAT(OFS_PARM2+2);\n\t\tbreak;\n\tcase lfield_radiusdecay:\n\t\tl->decay = G_FLOAT(OFS_PARM2);\n\t\tbreak;\n\tcase lfield_owner:\n\t\t{\n\t\t\tcsqcedict_t *ed = (csqcedict_t*)G_EDICT(prinst, OFS_PARM2);\n\t\t\tif (ed->xv->entnum > 0)\n\t\t\t\tl->key = ed->xv->entnum;\t//attach it to the indicated ssqc ent.\n\t\t\telse\n\t\t\t\tl->key = -ed->entnum;\t\t//use the csqc index for it.\n\t\t}\n\t\tbreak;\n\tdefault:\n\t\tbreak;\n\t}\n}\nstatic void QCBUILTIN PF_R_DynamicLight_Get(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tdlight_t *l;\n\tunsigned int lno = G_FLOAT(OFS_PARM0);\n\tenum lightfield_e field = G_FLOAT(OFS_PARM1);\n\tif (lno >= rtlights_max)\n\t{\n\t\tif ((int)field == -1)\n\t\t\tG_FLOAT(OFS_RETURN) = rtlights_max;\n\t\telse\n\t\t\tG_INT(OFS_RETURN) = 0;\n\t\treturn;\n\t}\n\tl = cl_dlights+lno;\n\tswitch (field)\n\t{\n\tcase lfield_origin:\n\t\tVectorCopy(l->origin, G_VECTOR(OFS_RETURN));\n\t\tbreak;\n\tcase lfield_colour:\n\t\tVectorCopy(l->color, G_VECTOR(OFS_RETURN));\n\t\tbreak;\n\tcase lfield_radius:\n\t\tG_FLOAT(OFS_RETURN) = l->radius;\n\t\tbreak;\n\tcase lfield_flags:\n\t\tG_FLOAT(OFS_RETURN) = l->flags;\n\t\tbreak;\n\tcase lfield_style:\n\t\tG_FLOAT(OFS_RETURN) = l->style;\n\t\tbreak;\n\tcase lfield_angles:\n\t\tVectorCopy(l->angles, G_VECTOR(OFS_RETURN));\n\t\tbreak;\n\tcase lfield_fov:\n\t\tG_FLOAT(OFS_RETURN) = l->fov;\n\t\tbreak;\n\tcase lfield_nearclip:\n\t\tG_FLOAT(OFS_PARM2) = l->nearclip;\n\t\tbreak;\n\tcase lfield_corona:\n\t\tG_FLOAT(OFS_RETURN) = l->corona;\n\t\tbreak;\n\tcase lfield_coronascale:\n\t\tG_FLOAT(OFS_RETURN) = l->coronascale;\n\t\tbreak;\n\tcase lfield_cubemapname:\n\t\tRETURN_TSTRING(l->cubemapname);\n\t\tbreak;\n#ifdef RTLIGHTS\n\tcase lfield_stylestring:\n\t\tif (l->customstyle)\n\t\t\tRETURN_TSTRING(l->customstyle);\n\t\telse\n\t\t\tRETURN_TSTRING(\"\");\n\t\tbreak;\n\tcase lfield_ambientscale:\n\t\tG_FLOAT(OFS_RETURN) = l->lightcolourscales[0];\n\t\tbreak;\n\tcase lfield_diffusescale:\n\t\tG_FLOAT(OFS_RETURN) = l->lightcolourscales[1];\n\t\tbreak;\n\tcase lfield_specularscale:\n\t\tG_FLOAT(OFS_RETURN) = l->lightcolourscales[2];\n\t\tbreak;\n\tcase lfield_rotation:\n\t\tG_FLOAT(OFS_RETURN+0) = l->rotation[0];\n\t\tG_FLOAT(OFS_RETURN+1) = l->rotation[1];\n\t\tG_FLOAT(OFS_RETURN+2) = l->rotation[2];\n\t\tbreak;\n#endif\n\tcase lfield_dietime:\n\t\tG_FLOAT(OFS_RETURN) = l->die;\n\t\tbreak;\n\tcase lfield_rgbdecay:\n\t\tG_FLOAT(OFS_RETURN+0) = l->channelfade[0];\n\t\tG_FLOAT(OFS_RETURN+1) = l->channelfade[1];\n\t\tG_FLOAT(OFS_RETURN+2) = l->channelfade[2];\n\t\tbreak;\n\tcase lfield_radiusdecay:\n\t\tG_FLOAT(OFS_RETURN) = l->decay;\n\t\tbreak;\n\n\tcase lfield_owner:\n\t\tif (l->key < 0 && -l->key < prinst->edicttable_length && prinst->edicttable[-l->key])\t//csqc ent\n\t\t\tRETURN_EDICT(prinst, prinst->edicttable[-l->key]);\n\t\telse if (l->key > 0 && l->key < maxcsqcentities && csqcent[l->key]) \t//ssqc ent\n\t\t\tRETURN_EDICT(prinst, csqcent[l->key]);\n\t\telse\n\t\t\tRETURN_EDICT(prinst, prinst->edicttable[0]);\t//probably an ssqc ent not known to the csqc, so we can't report this info.\n\t\tbreak;\n\tdefault:\n\t\tG_INT(OFS_RETURN) = 0;\n\t\tbreak;\n\t}\n}\n\nstatic void PF_R_DynamicLight_AddInternal(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals, qboolean isstatic)\n{\n\tfloat *org = G_VECTOR(OFS_PARM0);\n\tfloat radius = G_FLOAT(OFS_PARM1);\n\tfloat *rgb = G_VECTOR(OFS_PARM2);\n\tfloat style = (prinst->callargc > 3)?G_FLOAT(OFS_PARM3):0;\n\tconst char *cubemapname = (prinst->callargc > 4)?PR_GetStringOfs(prinst, OFS_PARM4):\"\";\n\tint pflags = (prinst->callargc > 5)?G_FLOAT(OFS_PARM5):PFLAGS_CORONA;\n\n#ifdef RTLIGHTS\n//\tfloat *ambientdiffusespec = (prinst->callargc > 5)?{0,1,1}:G_VECTOR(OFS_PARM6);\n//\tfov, orientation, corona, coronascale, rotation, die, etc, just use dynamiclight_set\n#endif\n\n\twedict_t *self;\n\tdlight_t *dl;\n\tint dlkey;\n\t\n\tif (prinst == csqc_world.progs)\n\t{\n\t\tself = PROG_TO_WEDICT(prinst, *csqcg.self);\n\t\tdlkey = VectorCompare(self->v->origin, org)?-self->entnum:0;\n\t}\n\telse\n\t\tdlkey = 0;\n\n\tif (isstatic)\n\t{\n\t\tdl = CL_AllocSlight();\n\t\tdl->die = 0;\n\t\tdl->flags = LFLAG_NORMALMODE|LFLAG_REALTIMEMODE;\n\t}\n\telse\n\t{\n\t\tdl = CL_AllocDlight(dlkey);\n\t\tdl->die = cl.time - 0.1;\n\t\tdl->flags = LFLAG_DYNAMIC;\n\t}\n\n\tVectorCopy(org, dl->origin);\n\tdl->radius = radius;\n\tVectorCopy(rgb, dl->color);\n\n\tif (*dl->cubemapname)\n\t{\n\t\tVectorCopy(csqcg.v_forward,\tdl->axis[0]);\n\t\tVectorCopy(csqcg.v_right,\t\tdl->axis[1]);\n\t\tVectorCopy(csqcg.v_up,\t\tdl->axis[2]);\n\t}\n\n\tif (pflags & PFLAGS_NOSHADOW)\n\t\tdl->flags |= LFLAG_NOSHADOWS;\n\tif (pflags & PFLAGS_CORONA)\n\t\tdl->corona = 1;\n\telse\n\t\tdl->corona = 0;\n\tdl->style = style;\n\tQ_strncpyz(dl->cubemapname, cubemapname, sizeof(dl->cubemapname));\n\tif (*dl->cubemapname)\n\t\tdl->cubetexture = R_LoadReplacementTexture(dl->cubemapname, \"\", IF_TEXTYPE_CUBE, NULL, 0, 0, TF_INVALID);\n\telse\n\t\tdl->cubetexture = r_nulltex;\n\n\tG_FLOAT(OFS_RETURN) = dl - cl_dlights;\n}\n\nvoid QCBUILTIN PF_R_DynamicLight_AddStatic(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tPF_R_DynamicLight_AddInternal(prinst, pr_globals, true);\n}\nvoid QCBUILTIN PF_R_DynamicLight_AddDynamic(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tPF_R_DynamicLight_AddInternal(prinst, pr_globals, false);\n}\n\nstatic void QCBUILTIN PF_R_AddEntityMask(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint mask = G_FLOAT(OFS_PARM0);\n\tcsqcedict_t *ent;\n\tentity_t rent;\n\tint e;\n\tint maxe;\n\n\tint oldself = *csqcg.self;\n\tRSpeedMark();\n\n\tif (cl.worldmodel)\n\t{\n\t\tif (mask & MASK_DELTA)\n\t\t{\n#ifdef Q2CLIENT\n\t\t\tif (cls.protocol == CP_QUAKE2)\n\t\t\t\tCLQ2_AddEntities();\n\t\t\telse\n#endif\n\t\t\t{\n\t\t\t\tCL_LinkPlayers ();\n\t\t\t\tCL_LinkPacketEntities ();\n\t\t\t}\n\t\t}\n\t}\n\n\tif (csqc_isdarkplaces)\n\t{\n\t\t//hopelessly inefficient version for compat with DP.\n\t\tmaxe = *prinst->parms->num_edicts;\n\t\tfor (e=1; e < maxe; e++)\n\t\t{\n\t\t\tent = (void*)EDICT_NUM_PB(prinst, e);\n\t\t\tif (ED_ISFREE(ent))\n\t\t\t\tcontinue;\n\t\t\tif (ent->v->think)\n\t\t\t{\n\t\t\t\tWPhys_RunThink (&csqc_world, (wedict_t*)ent);\n\t\t\t\tif (ED_ISFREE(ent))\n\t\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (ent->xv->predraw)\n\t\t\t{\n\t\t\t\t*csqcg.self = EDICT_TO_PROG(prinst, (void*)ent);\n\t\t\t\tPR_ExecuteProgram(prinst, ent->xv->predraw);\n\t\t\t\tif (ED_ISFREE(ent))\n\t\t\t\t\tcontinue;\t//bummer...\n\t\t\t}\n\t\t\tif ((int)ent->xv->drawmask & mask)\n\t\t\t{\n\t\t\t\tif (CopyCSQCEdictToEntity(ent, &rent))\n\t\t\t\t{\n\t\t\t\t\tCLQ1_AddShadow(&rent);\n\t\t\t\t\tV_AddAxisEntity(&rent);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\telse\n\t{\n\t\tmaxe = *prinst->parms->num_edicts;\n\t\tfor (e=1; e < maxe; e++)\n\t\t{\n\t\t\tent = (void*)EDICT_NUM_PB(prinst, e);\n\t\t\tif (ED_ISFREE(ent))\n\t\t\t\tcontinue;\n\n\t\t\tif ((int)ent->xv->drawmask & mask)\n\t\t\t{\n\t\t\t\tif (ent->xv->predraw)\n\t\t\t\t{\n\t\t\t\t\t*csqcg.self = EDICT_TO_PROG(prinst, (void*)ent);\n\t\t\t\t\tPR_ExecuteProgram(prinst, ent->xv->predraw);\n\n\t\t\t\t\tif (ED_ISFREE(ent) || G_FLOAT(OFS_RETURN))\n\t\t\t\t\t\tcontinue;\t//bummer...\n\t\t\t\t}\n\t\t\t\tif (CopyCSQCEdictToEntity(ent, &rent))\n\t\t\t\t{\n\t\t\t\t\tCLQ1_AddShadow(&rent);\n\t\t\t\t\tV_AddAxisEntity(&rent);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\t*csqcg.self = oldself;\n\n\tif (cl.worldmodel)\n\t{\n\t\tif (mask & MASK_STDVIEWMODEL)\n\t\t{\n\t\t\tCL_LinkViewModel ();\n\t\t}\n\t\tif (mask & MASK_DELTA)\n\t\t{\n\t\t\tCL_LinkProjectiles ();\n\t\t\tCL_UpdateTEnts ();\n\t\t}\n\t}\n\n\tRSpeedEnd(RSPEED_LINKENTITIES);\n}\n\n//enum {vb_vertexcoord, vb_texcoord, vb_rgba, vb_normal, vb_sdir, vb_tdir, vb_indexes, vb_rgb, vb_alpha};\n//vboidx = vbuff_create(numverts, numidx, flags)\n//vbuff_updateptr(vboidx, datatype, ptr, firstvert, numverts)\n//vbuff_updateone(vboidx, datatype, index, __variant data)\n//vbuff_render(vboidx, shaderid, uniforms, uniformssize)\n//vbuff_delete(vboidx), vboidx=0\n\nstatic shader_t *csqc_poly_shader;\nstatic int csqc_poly_origvert;\nstatic int csqc_poly_origidx;\nstatic int csqc_poly_startvert;\nstatic int csqc_poly_startidx;\nstatic int csqc_poly_flags;\nstatic int csqc_poly_2d;\n\nstatic void CSQC_PolyFlush(void)\n{\n\tmesh_t mesh;\n\tR2D_Flush = NULL;\n\n\t//make sure there's actually something there...\n\tif (cl_numstrisvert == csqc_poly_origvert)\n\t\treturn;\n\n\tif (!csqc_poly_2d)\n\t{\n\t\tscenetris_t *t;\n\t\tif (cl_numstris == cl_maxstris)\n\t\t{\n\t\t\tcl_maxstris+=8;\n\t\t\tcl_stris = BZ_Realloc(cl_stris, sizeof(*cl_stris)*cl_maxstris);\n\t\t}\n\t\tt = &cl_stris[cl_numstris++];\n\t\tt->shader = csqc_poly_shader;\n\t\tt->flags = csqc_poly_flags;\n\t\tt->firstidx = csqc_poly_origidx;\n\t\tt->firstvert = csqc_poly_origvert;\n\n\t\tt->numidx = cl_numstrisidx - t->firstidx;\n\t\tt->numvert = cl_numstrisvert-t->firstvert;\n\t}\n\telse\n\t{\n\t\t/*2d polys need to be flushed now*/\n\t\tmemset(&mesh, 0, sizeof(mesh));\n\n\t\tmesh.istrifan = (csqc_poly_origvert == csqc_poly_startvert);\n\t\tmesh.xyz_array = cl_strisvertv + csqc_poly_origvert;\n\t\tmesh.st_array = cl_strisvertt + csqc_poly_origvert;\n\t\tmesh.colors4f_array[0] = cl_strisvertc + csqc_poly_origvert;\n\t\tmesh.indexes = cl_strisidx + csqc_poly_origidx;\n\t\tmesh.numindexes = cl_numstrisidx - csqc_poly_origidx;\n\t\tmesh.numvertexes = cl_numstrisvert-csqc_poly_origvert;\n\t\t/*undo the positions so we don't draw the same verts more than once*/\n\t\tcl_numstrisvert = csqc_poly_origvert;\n\t\tcl_numstrisidx = csqc_poly_origvert;\n\n\t\tBE_DrawMesh_Single(csqc_poly_shader, &mesh, NULL, csqc_poly_flags);\n\t}\n\n\t//must call begin before the next poly\n\tcsqc_poly_shader = NULL;\n}\n\nstatic shader_t *PR_R_PolygonShader(const char *shadername, qboolean twod)\n{\n\textern shader_t *shader_draw_fill_trans;\n\tshader_t *shader;\n\tif (!*shadername)\n\t\tshader = shader_draw_fill_trans;\t//dp compat...\n\telse if (twod)\n\t\tshader = R_RegisterPic(shadername, NULL);\n\telse\n\t\tshader = R_RegisterCustom(NULL, shadername, 0, Shader_PolygonShader, NULL);\n\treturn shader;\n}\n\n// #306 void(string texturename) R_BeginPolygon (EXT_CSQC_???)\nvoid QCBUILTIN PF_R_PolygonBegin(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char *shadername = PR_GetStringOfs(prinst, OFS_PARM0);\n\tint qcflags = (prinst->callargc > 1)?G_FLOAT(OFS_PARM1):0;\n\tshader_t *shader;\n\tint beflags;\n\tqboolean twod;\n\n\tif (prinst->callargc > 2)\n\t\ttwod = G_FLOAT(OFS_PARM2);\n\telse if (csqc_isdarkplaces)\n\t{\n\t\ttwod = !csqc_dp_lastwas3d;\n\t\tcsqc_deprecated(\"guessing 2d mode based upon random builtin calls\");\n\t}\n\telse\n\t\ttwod = qcflags & DRAWFLAG_2D;\n\n\tif ((qcflags & 3) == DRAWFLAG_ADD)\n\t\tbeflags = BEF_NOSHADOWS|BEF_FORCEADDITIVE;\n\telse if ((qcflags & 3) == DRAWFLAG_MODULATE)\n\t\tbeflags = BEF_NOSHADOWS|BEF_FORCETRANSPARENT;\n\telse\n\t\tbeflags = BEF_NOSHADOWS;\n\tif (csqc_isdarkplaces || (qcflags & DRAWFLAG_TWOSIDED))\n\t\tbeflags |= BEF_FORCETWOSIDED;\n\n\tshader = PR_R_PolygonShader(shadername, twod);\n\n\tif (R2D_Flush && (R2D_Flush != CSQC_PolyFlush || csqc_poly_shader != shader || csqc_poly_flags != beflags || csqc_poly_2d != twod))\n\t\tR2D_Flush();\n\tif (!R2D_Flush)\n\t{\t//this is where our current (2d) batch starts\n\t\tcsqc_poly_origvert = cl_numstrisvert;\n\t\tcsqc_poly_origidx = cl_numstrisidx;\n\t}\n\tR2D_Flush = CSQC_PolyFlush;\n\tcsqc_poly_shader = shader;\n\tcsqc_poly_flags = beflags;\n\tcsqc_poly_2d = twod;\n\n\t//this is where our current poly starts\n\tcsqc_poly_startvert = cl_numstrisvert;\n\tcsqc_poly_startidx = cl_numstrisidx;\n}\n\n// #307 void(vector org, vector texcoords, vector rgb, float alpha) R_PolygonVertex (EXT_CSQC_???)\nvoid QCBUILTIN PF_R_PolygonVertex(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tif (cl_numstrisvert == cl_maxstrisvert)\n\t\tcl_stris_ExpandVerts(cl_numstrisvert+64);\n\n\tVectorCopy(G_VECTOR(OFS_PARM0), cl_strisvertv[cl_numstrisvert]);\n\tVector2Copy(G_VECTOR(OFS_PARM1), cl_strisvertt[cl_numstrisvert]);\n\tVectorCopy(G_VECTOR(OFS_PARM2), cl_strisvertc[cl_numstrisvert]);\n\tcl_strisvertc[cl_numstrisvert][3] = G_FLOAT(OFS_PARM3);\n\tcl_numstrisvert++;\n}\n\n// #308 void() R_EndPolygon (EXT_CSQC_???)\nvoid QCBUILTIN PF_R_PolygonEnd(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint i;\n\tint nv;\n\tint flags = csqc_poly_flags;\n\tint first;\n\n\tif (!csqc_poly_shader)\n\t\treturn;\n\n\tnv = cl_numstrisvert-csqc_poly_startvert;\n\tif (nv == 2)\n\t\tflags |= BEF_LINES;\n\telse\n\t\tflags &= ~BEF_LINES;\n\n\tif (flags != csqc_poly_flags || (cl_numstrisvert-csqc_poly_origvert) >= 32768)\n\t{\n\t\tint sv = cl_numstrisvert - nv;\n\t\tcl_numstrisvert -= nv;\n\t\tCSQC_PolyFlush();\n\n\t\tcsqc_poly_origvert = cl_numstrisvert;\n\t\tcsqc_poly_origidx = cl_numstrisidx;\n\t\tR2D_Flush = CSQC_PolyFlush;\n\t\tcsqc_poly_flags = flags;\n\t\tcsqc_poly_startvert = cl_numstrisvert;\n\t\tcsqc_poly_startidx = cl_numstrisidx;\n\n\t\tmemcpy(cl_strisvertv+cl_numstrisvert, cl_strisvertv + sv, sizeof(*cl_strisvertv) * nv);\n\t\tmemcpy(cl_strisvertt+cl_numstrisvert, cl_strisvertt + sv, sizeof(*cl_strisvertt) * nv);\n\t\tmemcpy(cl_strisvertc+cl_numstrisvert, cl_strisvertc + sv, sizeof(*cl_strisvertc) * nv);\n\t\tcl_numstrisvert += nv;\n\t}\n\n\tif (flags & BEF_LINES)\n\t{\n\t\tnv = cl_numstrisvert-csqc_poly_startvert;\n\t\tif (cl_numstrisidx+nv > cl_maxstrisidx)\n\t\t{\n\t\t\tcl_maxstrisidx=cl_numstrisidx+nv + 64;\n\t\t\tcl_strisidx = BZ_Realloc(cl_strisidx, sizeof(*cl_strisidx)*cl_maxstrisidx);\n\t\t}\n\n\t\tfirst = csqc_poly_startvert - csqc_poly_origvert;\n\t\t/*build the line list fan out of triangles*/\n\t\tfor (i = 1; i < nv; i++)\n\t\t{\n\t\t\tcl_strisidx[cl_numstrisidx++] = first + i-1;\n\t\t\tcl_strisidx[cl_numstrisidx++] = first + i;\n\t\t}\n\t}\n\telse\n\t{\n\t\tnv = cl_numstrisvert-csqc_poly_startvert;\n\t\tif (cl_numstrisidx+(nv-2)*3 > cl_maxstrisidx)\n\t\t{\n\t\t\tcl_maxstrisidx=cl_numstrisidx+(nv-2)*3 + 64;\n\t\t\tcl_strisidx = BZ_Realloc(cl_strisidx, sizeof(*cl_strisidx)*cl_maxstrisidx);\n\t\t}\n\n\t\tfirst = csqc_poly_startvert - csqc_poly_origvert;\n\t\t/*build the triangle fan out of triangles*/\n\t\tfor (i = 2; i < nv; i++)\n\t\t{\n\t\t\tcl_strisidx[cl_numstrisidx++] = first + 0;\n\t\t\tcl_strisidx[cl_numstrisidx++] = first + i-1;\n\t\t\tcl_strisidx[cl_numstrisidx++] = first + i;\n\t\t}\n\t}\n\n\t/*set up ready for the next poly*/\n\tcsqc_poly_startvert = cl_numstrisvert;\n\tcsqc_poly_startidx = cl_numstrisidx;\n}\n\n//input is a line of verts, output is a quad strip\nvoid QCBUILTIN PF_R_PolygonEndRibbon(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint i;\n\tint nv;\n\tint flags = csqc_poly_flags;\n\tint first;\n\n\tvec3_t tang;\n\tvec3_t dir;\n\tvec3_t eyedir;\n\n\tfloat separation = G_FLOAT(OFS_PARM0);\n\tfloat sseparation = G_FLOAT(OFS_PARM1+0);\n\tfloat tseparation = G_FLOAT(OFS_PARM1+1);\n\n\tif (!csqc_poly_shader)\n\t\treturn;\n\n\tnv = cl_numstrisvert-csqc_poly_startvert;\n\tif (nv < 2)\n\t{\n\t\tcsqc_poly_startvert = cl_numstrisvert;\n\t\tcsqc_poly_startidx = cl_numstrisidx;\n\t\treturn; //sod off.\n\t}\n\tflags &= ~BEF_LINES;\n\n\tif (flags != csqc_poly_flags || (cl_numstrisvert-csqc_poly_origvert) >= 32768)\n\t{\n\t\tint sv = cl_numstrisvert - nv;\n\t\tcl_numstrisvert -= nv;\n\t\tCSQC_PolyFlush();\n\n\t\tcsqc_poly_origvert = cl_numstrisvert;\n\t\tcsqc_poly_origidx = cl_numstrisidx;\n\t\tR2D_Flush = CSQC_PolyFlush;\n\t\tcsqc_poly_flags = flags;\n\t\tcsqc_poly_startvert = cl_numstrisvert;\n\t\tcsqc_poly_startidx = cl_numstrisidx;\n\n\t\tmemcpy(cl_strisvertv+cl_numstrisvert, cl_strisvertv + sv, sizeof(*cl_strisvertv) * nv);\n\t\tmemcpy(cl_strisvertt+cl_numstrisvert, cl_strisvertt + sv, sizeof(*cl_strisvertt) * nv);\n\t\tmemcpy(cl_strisvertc+cl_numstrisvert, cl_strisvertc + sv, sizeof(*cl_strisvertc) * nv);\n\t\tcl_numstrisvert += nv;\n\t}\n\n\tnv = cl_numstrisvert-csqc_poly_startvert;\n\t//dupe the verts\n\tif (cl_numstrisvert+nv < cl_maxstrisvert)\n\t\tcl_stris_ExpandVerts(cl_numstrisvert+nv);\n\tmemcpy(&cl_strisvertv[cl_numstrisvert], &cl_strisvertv[csqc_poly_startvert], sizeof(*cl_strisvertv)*nv);\n\tmemcpy(&cl_strisvertt[cl_numstrisvert], &cl_strisvertt[csqc_poly_startvert], sizeof(*cl_strisvertt)*nv);\n\tmemcpy(&cl_strisvertc[cl_numstrisvert], &cl_strisvertc[csqc_poly_startvert], sizeof(*cl_strisvertc)*nv);\n\n\t//apply separation\n\tVectorSubtract(cl_strisvertv[csqc_poly_startvert+0+1], cl_strisvertv[csqc_poly_startvert+0], dir);\n\tVectorSubtract(cl_strisvertv[csqc_poly_startvert+0], r_refdef.vieworg, eyedir);\n\tVectorNormalize(dir); VectorNormalize(eyedir);\n\tCrossProduct(dir, eyedir, tang);\n\tVectorMA(cl_strisvertv[csqc_poly_startvert+0], separation, tang, cl_strisvertv[csqc_poly_startvert+0]);\n\tVectorMA(cl_strisvertv[cl_numstrisvert+0], -separation, tang, cl_strisvertv[cl_numstrisvert+0]);\n\tcl_strisvertt[csqc_poly_startvert+0][0] += sseparation; //and update the generated s coord.\n\tcl_strisvertt[csqc_poly_startvert+0][1] += tseparation; //and update the generated t coord.\n\tfor (i = 1; i < nv-1; i++)\n\t{\t//direction comes from its two neighbours, rather than itself and one of those neighbours\n\t\tVectorSubtract(cl_strisvertv[csqc_poly_startvert+i+1], cl_strisvertv[csqc_poly_startvert+i-1], dir);\n\t\tVectorSubtract(cl_strisvertv[csqc_poly_startvert+i], r_refdef.vieworg, eyedir);\n\t\tVectorNormalize(dir); VectorNormalize(eyedir);\n\t\tCrossProduct(dir, eyedir, tang);\n\t\tVectorMA(cl_strisvertv[csqc_poly_startvert+i], separation, tang, cl_strisvertv[csqc_poly_startvert+i]);\n\t\tVectorMA(cl_strisvertv[cl_numstrisvert+i], -separation, tang, cl_strisvertv[cl_numstrisvert+i]);\n\t\tcl_strisvertt[csqc_poly_startvert+i][0] += sseparation; //and update the generated s coord.\n\t\tcl_strisvertt[csqc_poly_startvert+i][1] += tseparation; //and update the generated t coord.\n\t}\n\t//don't wrap over\n\tVectorSubtract(cl_strisvertv[csqc_poly_startvert+i], cl_strisvertv[csqc_poly_startvert+i-1], dir);\n\tVectorSubtract(cl_strisvertv[csqc_poly_startvert+i], r_refdef.vieworg, eyedir);\n\tVectorNormalize(dir); VectorNormalize(eyedir);\n\tCrossProduct(dir, eyedir, tang);\n\tVectorMA(cl_strisvertv[csqc_poly_startvert+i], separation, tang, cl_strisvertv[csqc_poly_startvert+i]);\n\tVectorMA(cl_strisvertv[cl_numstrisvert+i], -separation, tang, cl_strisvertv[cl_numstrisvert+i]);\n\tcl_strisvertt[csqc_poly_startvert+i][0] += sseparation; //and update the generated s coord.\n\tcl_strisvertt[csqc_poly_startvert+i][1] += tseparation; //and update the generated t coord.\n\t//verts are all set up right\n\tcl_numstrisvert += nv;\n\n\tif (cl_numstrisidx+(nv-1)*6 > cl_maxstrisidx)\n\t{\n\t\tcl_maxstrisidx=cl_numstrisidx+(nv-1)*6 + 64;\n\t\tcl_strisidx = BZ_Realloc(cl_strisidx, sizeof(*cl_strisidx)*cl_maxstrisidx);\n\t}\n\n\tfirst = csqc_poly_startvert - csqc_poly_origvert;\n\t/*build a double-triangle strip out of our lined verts*/\n\tfor (i = 1; i < nv; i++)\n\t{\n\t\tcl_strisidx[cl_numstrisidx++] = first + i-1;\n\t\tcl_strisidx[cl_numstrisidx++] = first + i;\n\t\tcl_strisidx[cl_numstrisidx++] = first + i+nv;\n\n\t\tcl_strisidx[cl_numstrisidx++] = first + i+nv;\n\t\tcl_strisidx[cl_numstrisidx++] = first + i-1+nv;\n\t\tcl_strisidx[cl_numstrisidx++] = first + i-1;\n\t}\n\n\t/*set up ready for the next poly*/\n\tcsqc_poly_startvert = cl_numstrisvert;\n\tcsqc_poly_startidx = cl_numstrisidx;\n}\n\ntypedef struct\n{\n\tvec3_t xyz;\n\tvec2_t st;\n\tvec4_t rgba;\n//\tvec3_t norm;\n//\tvec3_t sdir;\n//\tvec3_t tdir;\n} qcvertex_t;\nvoid QCBUILTIN PF_R_AddTrisoup_Simple(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tshader_t *shader;\t\t//parm 0\n\tconst char *shadername = PR_GetStringOfs(prinst, OFS_PARM0);\n\tunsigned int qcflags\t= G_INT(OFS_PARM1);\n\tunsigned int vertsptr\t= G_INT(OFS_PARM2);\n\tunsigned int indexesptr\t= G_INT(OFS_PARM3);\n\tunsigned int numindexes\t= G_INT(OFS_PARM4);\n\tqboolean twod = qcflags & DRAWFLAG_2D;\n\tunsigned int beflags;\n\tunsigned int maxverts;\n\tconst qcvertex_t *fte_restrict vert;\n\tconst unsigned int *fte_restrict idx;\n\tunsigned int i, j, first;\n\n\tif ((qcflags & 3) == DRAWFLAG_ADD)\n\t\tbeflags = BEF_NOSHADOWS|BEF_FORCEADDITIVE;\n\telse if ((qcflags & 3) == DRAWFLAG_MODULATE)\n\t\tbeflags = BEF_NOSHADOWS|BEF_FORCETRANSPARENT;\n\telse\n\t\tbeflags = BEF_NOSHADOWS;\n\tif (qcflags & DRAWFLAG_TWOSIDED)\n\t\tbeflags |= BEF_FORCETWOSIDED;\n\tif (qcflags & DRAWFLAG_LINES)\n\t\tbeflags |= BEF_LINES;\n\n\tshader = PR_R_PolygonShader(shadername, twod);\n\n\tif (R2D_Flush && (R2D_Flush != CSQC_PolyFlush || csqc_poly_shader != shader || csqc_poly_flags != beflags || csqc_poly_2d != twod))\n\t\tR2D_Flush();\n\tif (!R2D_Flush)\n\t{\t//this is where our current (2d) batch starts\n\t\tcsqc_poly_origvert = cl_numstrisvert;\n\t\tcsqc_poly_origidx = cl_numstrisidx;\n\t}\n\n\t//validates the pointer.\n\tmaxverts = (prinst->stringtablesize - vertsptr) / sizeof(qcvertex_t);\n\tif (maxverts < 1 || vertsptr <= 0 || vertsptr+maxverts*sizeof(qcvertex_t) > prinst->stringtablesize)\n\t{\n\t\tPR_BIError(prinst, \"PF_R_AddTrisoup: invalid vertexes pointer\\n\");\n\t\treturn;\n\t}\n\tvert = (const qcvertex_t*)(prinst->stringtable + vertsptr);\n\tif (indexesptr <= 0 || indexesptr+numindexes*sizeof(int) > prinst->stringtablesize)\n\t{\n\t\tPR_BIError(prinst, \"PF_R_AddTrisoup: invalid indexes pointer\\n\");\n\t\treturn;\n\t}\n\tidx = (const int*)(prinst->stringtable + indexesptr);\n\n\tfirst = cl_numstrisvert - csqc_poly_origvert;\n\tif (first + numindexes > MAX_INDICIES)\n\t{\n\t\tif (numindexes > MAX_INDICIES)\n\t\t{\n\t\t\tPR_BIError(prinst, \"PF_R_AddTrisoup: single batch exceeds MAX_INDICIES\\n\");\n\t\t\treturn;\n\t\t}\n\t\telse if (R2D_Flush)\t//should always be true\n\t\t{\n\t\t\tR2D_Flush();\n\t\t\tfirst = 0;\n\t\t\tcsqc_poly_origvert = cl_numstrisvert;\n\t\t\tcsqc_poly_origidx = cl_numstrisidx;\n\t\t}\n\t}\n\n\tR2D_Flush = CSQC_PolyFlush;\n\tcsqc_poly_shader = shader;\n\tcsqc_poly_flags = beflags;\n\tcsqc_poly_2d = twod;\n\n\t//hacky crappy solution - make a copy of each used vert rather than copying the entire data out.\n\n\tif (cl_numstrisidx+numindexes > cl_maxstrisidx)\n\t{\n\t\tcl_maxstrisidx=cl_numstrisidx+numindexes;\n\t\tcl_strisidx = BZ_Realloc(cl_strisidx, sizeof(*cl_strisidx)*cl_maxstrisidx);\n\t}\n\tif (cl_numstrisvert+numindexes > cl_maxstrisvert)\n\t\tcl_stris_ExpandVerts(cl_numstrisvert+numindexes);\n\tfor (i = 0; i < numindexes; i++)\n\t{\n\t\tj = *idx++;\n\t\tif (j >= maxverts)\n\t\t\tj = 0;\t//out of bounds.\n\n\t\tVectorCopy(vert[j].xyz, cl_strisvertv[cl_numstrisvert]);\n\t\tVector2Copy(vert[j].st, cl_strisvertt[cl_numstrisvert]);\n\t\tVector4Copy(vert[j].rgba, cl_strisvertc[cl_numstrisvert]);\n\t\tcl_numstrisvert++;\n\n\t\tcl_strisidx[cl_numstrisidx++] = first++;\n\t}\n\n\n\t//in case someone calls polygonvertex+end without beginpolygon\n\tcsqc_poly_startvert = cl_numstrisvert;\n\tcsqc_poly_startidx = cl_numstrisidx;\n}\n\n\nqboolean csqc_rebuildmatricies;\nfloat csqc_proj_matrix[16];\nfloat csqc_proj_matrix_inverse[16];\nfloat csqc_proj_frustum[2];\nvoid V_ApplyAFov(playerview_t *pv);\nstatic void cs_buildmatricies(void)\n{\n\tfloat modelview[16];\n\tfloat proj[16];\n\tfloat ofovx = r_refdef.fov_x,ofovy=r_refdef.fov_y;\n\tfloat ofovvx = r_refdef.fovv_x,ofovvy=r_refdef.fovv_y;\n\n\tV_ApplyAFov(csqc_playerview);\n\n\tif (csqc_isdarkplaces)\n\t{\n\t\t/*doesn't bother to use the projection matrix. it isn't much of a transform.*/\n\t\tMatrix4x4_CM_ModelViewMatrix(csqc_proj_matrix, r_refdef.viewangles, r_refdef.vieworg);\n\n\t\tcsqc_proj_frustum[0] = tan(r_refdef.fov_x * M_PI / 360.0);\n\t\tcsqc_proj_frustum[1] = tan(r_refdef.fov_y * M_PI / 360.0);\n\t}\n\telse\n\t{\n\t\t/*build view and projection matricies*/\n\t\tMatrix4x4_CM_ModelViewMatrix(modelview, r_refdef.viewangles, r_refdef.vieworg);\n\t\tif (r_refdef.useperspective)\n\t\t\tMatrix4x4_CM_Projection2(proj, r_refdef.fov_x, r_refdef.fov_y, 4);\n\t\telse\n\t\t\tMatrix4x4_CM_Orthographic(proj, -r_refdef.fov_x/2, r_refdef.fov_x/2, -r_refdef.fov_y/2, r_refdef.fov_y/2, r_refdef.mindist, r_refdef.maxdist>=1?r_refdef.maxdist:9999);\n\n\t\t/*build the vp matrix*/\n\t\tMatrix4_Multiply(proj, modelview, csqc_proj_matrix);\n\t}\n\n\t/*build the unproject matrix (inverted vp matrix)*/\n\tMatrix4_Invert(csqc_proj_matrix, csqc_proj_matrix_inverse);\n\n\tcsqc_rebuildmatricies = false;\n\n\tr_refdef.fov_x = ofovx,\n\tr_refdef.fov_y = ofovy;\n\tr_refdef.fovv_x = ofovvx,\n\tr_refdef.fovv_y = ofovvy;\n}\nstatic void QCBUILTIN PF_cs_project (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tif (csqc_rebuildmatricies)\n\t\tcs_buildmatricies();\n\n\n\t{\n\t\tfloat *in = G_VECTOR(OFS_PARM0);\n\t\tfloat *out = G_VECTOR(OFS_RETURN);\n\t\tfloat v[4], tempv[4];\n\n\t\tv[0] = in[0];\n\t\tv[1] = in[1];\n\t\tv[2] = in[2];\n\t\tv[3] = 1;\n\n\t\tMatrix4x4_CM_Transform4(csqc_proj_matrix, v, tempv);\n\n\t\ttempv[0] /= tempv[3];\n\t\ttempv[1] /= tempv[3];\n\t\ttempv[2] /= tempv[3];\n\n\t\tif (csqc_isdarkplaces)\n\t\t{\t/*sigh*/\n\t\t\ttempv[0] = -tempv[0]/tempv[2]/csqc_proj_frustum[0];\n\t\t\ttempv[1] = tempv[1]/tempv[2]/csqc_proj_frustum[1];\n\t\t\tout[0] = (1+tempv[0])/2;\n\t\t\tout[1] = (1+tempv[1])/2;\n\t\t\tout[2] = -tempv[2];\n\n\t\t\tout[0] = out[0]*vid.width;\n\t\t\tout[1] = out[1]*vid.height;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tout[0] = (1+tempv[0])/2;\n\t\t\tout[1] = 1-(1+tempv[1])/2;\n\t\t\tout[2] = tempv[2];\n\n\t\t\tout[0] = out[0]*r_refdef.vrect.width + r_refdef.vrect.x;\n\t\t\tout[1] = out[1]*r_refdef.vrect.height + r_refdef.vrect.y;\n\t\t}\n\t\tif (tempv[3] < 0)\n\t\t\tout[2] *= -1;\n\t}\n}\nstatic void QCBUILTIN PF_cs_unproject (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tif (csqc_rebuildmatricies)\n\t\tcs_buildmatricies();\n\n\t{\n\t\tfloat *in = G_VECTOR(OFS_PARM0);\n\t\tfloat *out = G_VECTOR(OFS_RETURN);\n\t\tfloat tx = in[0], ty = in[1];\n\n\t\tfloat v[4], tempv[4];\n\n\t\tif (csqc_isdarkplaces)\n\t\t{\t/*sigh*/\n\t\t\ttx = ((tx)/vid.width);\n\t\t\tty = ((ty)/vid.height);\n\n\t\t\t//this is kinda screwy\n\t\t\tv[2] = -in[2];\n\t\t\tv[0] = -(tx*2-1)*v[2]*csqc_proj_frustum[0];\n\t\t\tv[1] = (ty*2-1)*v[2]*csqc_proj_frustum[1];\n\t\t}\n\t\telse\n\t\t{\n\t\t\ttx = ((tx-r_refdef.vrect.x)/r_refdef.vrect.width);\n\t\t\tty = ((ty-r_refdef.vrect.y)/r_refdef.vrect.height);\n\t\t\tty = 1-ty;\n\t\t\tv[0] = tx*2-1;\n\t\t\tv[1] = ty*2-1;\n\t\t\tv[2] = in[2]*2-1;\t//gl projection matrix scales -1 to 1 (unlike d3d, which is 0 to 1)\n\n\t\t\t//don't use 1, because the far clip plane really is an infinite distance away. and that tends to result division by infinity.\n\t\t\tif (v[2] >= 1)\n\t\t\t\tv[2] = 0.999999;\n\t\t}\n\t\tv[3] = 1;\n\n\t\tMatrix4x4_CM_Transform4(csqc_proj_matrix_inverse, v, tempv);\n\n\t\tout[0] = tempv[0]/tempv[3];\n\t\tout[1] = tempv[1]/tempv[3];\n\t\tout[2] = tempv[2]/tempv[3];\n\t}\n}\n\n//clear scene, and set up the default stuff.\nstatic void QCBUILTIN PF_R_ClearScene (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tif (R2D_Flush)\n\t\tR2D_Flush();\n\n\tif (prinst->callargc > 0)\n\t\tCSQC_ChangeLocalPlayer(G_FLOAT(OFS_PARM0));\n\n\tcsqc_rebuildmatricies = true;\n\tcsqc_dp_lastwas3d = true;\t//cleared by the next drawpic.\n\tcsqc_poly_shader = NULL;\n\n\tCL_DecayLights ();\n\n#if defined(SKELETALOBJECTS) || defined(RAGDOLLS)\n\tskel_dodelete(&csqc_world);\n#endif\n\tCL_ClearEntityLists();\n\n\tV_ClearRefdef(csqc_playerview);\n\tr_refdef.drawsbar = false;\t//csqc defaults to no sbar.\n\tr_refdef.drawcrosshair = false;\n\n\tV_CalcRefdef(csqc_playerview);\t//set up the defaults\n}\n\nvoid QCBUILTIN PF_R_GetViewFlag(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tviewflags parametertype = G_FLOAT(OFS_PARM0);\n\tfloat *r = G_VECTOR(OFS_RETURN);\n\n\tr[0] = 0;\n\tr[1] = 0;\n\tr[2] = 0;\n\n#ifdef HAVE_LEGACY\n\tif (csqc_isdarkplaces && prinst == csqc_world.progs)\n\t{\n\t\tswitch(parametertype)\n\t\t{\n\t\tcase VF_VIEWPORT:\n\t\t\tr[0] = r_refdef.grect.width * (float)vid.pixelwidth / vid.width;;\n\t\t\tr[1] = r_refdef.grect.height * (float)vid.pixelheight / vid.height;\n\t\t\treturn;\n\t\tcase VF_SIZE_X:\n\t\t\t*r = r_refdef.grect.width * (float)vid.pixelwidth / vid.width;\n\t\t\treturn;\n\t\tcase VF_SIZE_Y:\n\t\t\t*r = r_refdef.grect.height * (float)vid.pixelheight / vid.height;\n\t\t\treturn;\n\t\tcase VF_SIZE:\n\t\t\tr[0] = r_refdef.grect.width * (float)vid.pixelwidth / vid.width;;\n\t\t\tr[1] = r_refdef.grect.height * (float)vid.pixelheight / vid.height;\n\t\t\tr[2] = 0;\n\t\t\treturn;\n\t\tcase VF_DP_MAINVIEW:\n\t\t\tr[0] = 1;\n\t\t\treturn;\n\t\tcase VF_DP_MINFPS_QUALITY:\n\t\t\tr[0] = 1;\n\t\t\treturn;\n\t\tcase VF_DP_CLEARSCREEN:\n\t\t\tr[0] = 0;\n\t\t\treturn;\n\t\tcase VF_DP_FOG_DENSITY:\n\t\t\tr[0] = r_refdef.globalfog.density;\n\t\t\treturn;\n\t\tcase VF_DP_FOG_COLOR:\n\t\t\tr[0] = r_refdef.globalfog.colour[0];\n\t\t\tr[1] = r_refdef.globalfog.colour[1];\n\t\t\tr[2] = r_refdef.globalfog.colour[2];\n\t\t\treturn;\n\t\tcase VF_DP_FOG_COLOR_R:\n\t\t\tr[0] = r_refdef.globalfog.colour[0];\n\t\t\treturn;\n\t\tcase VF_DP_FOG_COLOR_G:\n\t\t\tr[0] = r_refdef.globalfog.colour[1];\n\t\t\treturn;\n\t\tcase VF_DP_FOG_COLOR_B:\n\t\t\tr[0] = r_refdef.globalfog.colour[2];\n\t\t\treturn;\n\t\tcase VF_DP_FOG_ALPHA:\n\t\t\tr[0] = r_refdef.globalfog.alpha;\n\t\t\treturn;\n\t\tcase VF_DP_FOG_START:\n\t\tcase VF_DP_FOG_END:\n\t\tcase VF_DP_FOG_HEIGHT:\n\t\tcase VF_DP_FOG_FADEDEPTH:\n\t\t\treturn;\n\t\tdefault:\n\t\t\tbreak;\n\t\t}\n\t}\n#endif\n\n\tswitch(parametertype)\n\t{\nnogameaccess:\n\t\tcsqc_deprecated(\"PF_R_GetViewFlag: game access is blocked\");\n\t\tbreak;\n\tcase VF_ACTIVESEAT:\n\t\tif (prinst == csqc_world.progs)\n\t\t\t*r = csqc_playerseat;\n\t\tbreak;\n\tcase VF_FOV:\n\t\tr[0] = r_refdef.fov_x;\n\t\tr[1] = r_refdef.fov_y;\n\t\tbreak;\n\n\tcase VF_FOV_X:\n\t\t*r = r_refdef.fov_x;\n\t\tbreak;\n\n\tcase VF_FOV_Y:\n\t\t*r = r_refdef.fov_y;\n\t\tbreak;\n\n\tcase VF_AFOV:\n\t\t*r = r_refdef.afov;\n\t\tbreak;\n\n\tcase VF_SKYROOM_CAMERA:\n\t\tif (r_refdef.skyroom_enabled)\n\t\t\tVectorCopy(r_refdef.skyroom_pos, r);\n\t\telse\n\t\t\tVectorClear(r);\t//not really correct, but no other way to really signal this. -0? yuck.\n\t\tbreak;\n\n\tcase VF_ORIGIN:\n\t\tif (csqc_nogameaccess && prinst == csqc_world.progs)\n\t\t\tgoto nogameaccess;\n\t\tVectorCopy(r_refdef.vieworg, r);\n\t\tbreak;\n\n\tcase VF_ORIGIN_Z:\n\tcase VF_ORIGIN_X:\n\tcase VF_ORIGIN_Y:\n\t\tif (csqc_nogameaccess && prinst == csqc_world.progs)\n\t\t\tgoto nogameaccess;\n\t\t*r = r_refdef.vieworg[parametertype-VF_ORIGIN_X];\n\t\tbreak;\n\n\tcase VF_ANGLES:\n\t\tif (csqc_nogameaccess && prinst == csqc_world.progs)\n\t\t\tgoto nogameaccess;\n\t\tVectorCopy(r_refdef.viewangles, r);\n\t\tbreak;\n\tcase VF_ANGLES_X:\n\tcase VF_ANGLES_Y:\n\tcase VF_ANGLES_Z:\n\t\tif (csqc_nogameaccess && prinst == csqc_world.progs)\n\t\t\tgoto nogameaccess;\n\t\t*r = r_refdef.viewangles[parametertype-VF_ANGLES_X];\n\t\tbreak;\n\n\tcase VF_VRBASEORIENTATION:\n\t\tif (csqc_nogameaccess && prinst == csqc_world.progs)\n\t\t\tgoto nogameaccess;\n\t\tVectorCopy(r_refdef.base_angles, r);\n\t\tbreak;\n\n\tcase VF_CL_VIEWANGLES_V:\n\t\tif (csqc_nogameaccess && prinst == csqc_world.progs)\n\t\t\tgoto nogameaccess;\n\t\tif (csqc_playerview)\n\t\t\tVectorCopy(csqc_playerview->viewangles, r);\n\t\tbreak;\n\tcase VF_CL_VIEWANGLES_X:\n\tcase VF_CL_VIEWANGLES_Y:\n\tcase VF_CL_VIEWANGLES_Z:\n\t\tif (csqc_nogameaccess && prinst == csqc_world.progs)\n\t\t\tgoto nogameaccess;\n\t\tif (csqc_playerview)\n\t\t\t*r = csqc_playerview->viewangles[parametertype-VF_CL_VIEWANGLES_X];\n\t\tbreak;\n\n\tcase VF_CARTESIAN_ANGLES:\n\t\tif (csqc_nogameaccess && prinst == csqc_world.progs)\n\t\t\tgoto nogameaccess;\n\t\tCon_Printf(CON_WARNING \"WARNING: CARTESIAN ANGLES ARE NOT YET SUPPORTED!\\n\");\n\t\tbreak;\n\n\tcase VF_VIEWPORT:\n\t\tr[0] = r_refdef.grect.width;\n\t\tr[1] = r_refdef.grect.height;\n\t\tbreak;\n\n\tcase VF_SIZE_X:\n\t\t*r = r_refdef.grect.width;\n\t\tbreak;\n\tcase VF_SIZE_Y:\n\t\t*r = r_refdef.grect.height;\n\t\tbreak;\n\tcase VF_SIZE:\n\t\tr[0] = r_refdef.grect.width;\n\t\tr[1] = r_refdef.grect.height;\n\t\tr[2] = 0;\n\t\tbreak;\n\n\tcase VF_MIN_X:\n\t\t*r = r_refdef.grect.x;\n\t\tbreak;\n\tcase VF_MIN_Y:\n\t\t*r = r_refdef.grect.y;\n\t\tbreak;\n\tcase VF_MIN:\n\t\tr[0] = r_refdef.grect.x;\n\t\tr[1] = r_refdef.grect.y;\n\t\tbreak;\n\n\tcase VF_MINDIST:\n\t\t*r = r_refdef.mindist;\n\t\tbreak;\n\tcase VF_MAXDIST:\n\t\t*r = r_refdef.maxdist;\n\t\tbreak;\n\n\tcase VF_DRAWWORLD:\n\t\t*r = !(r_refdef.flags&RDF_NOWORLDMODEL);\n\t\tbreak;\n\tcase VF_ENGINESBAR:\n\t\t*r = r_refdef.drawsbar;\n\t\tbreak;\n\tcase VF_DRAWCROSSHAIR:\n\t\t*r = r_refdef.drawcrosshair;\n\t\tbreak;\n\n\tcase VF_PERSPECTIVE:\n\t\t*r = r_refdef.useperspective;\n\t\tbreak;\n\n\tcase VF_PROJECTIONOFFSET:\n\t\tr[0] = r_refdef.projectionoffset[0];\n\t\tr[1] = r_refdef.projectionoffset[1];\n\t\tbreak;\n\n\tcase VF_SCREENVSIZE:\n\t\tr[0] = vid.width;\n\t\tr[1] = vid.height;\n\t\tbreak;\n\tcase VF_SCREENPSIZE:\n\t\tr[0] = vid.rotpixelwidth;\n\t\tr[1] = vid.rotpixelheight;\n\t\tbreak;\n\tcase VF_PIXELPSCALE:\n\t\tr[0] = vid.dpi_x;\n\t\tr[1] = vid.dpi_y;\n\t\tr[2] = vid.dpi_y/vid.dpi_x;\t//aspect\n\t\tbreak;\n\n\tdefault:\n\t\tCon_DPrintf(\"GetViewFlag: %i not recognised\\n\", parametertype);\n\t\tbreak;\n\t}\n}\nuploadfmt_t PR_TranslateTextureFormat(int qcformat)\n{\n\tswitch(qcformat)\n\t{\n\tcase 1: return PTI_RGBA8;\n\tcase 2: return PTI_RGBA16F;\n\tcase 3: return PTI_RGBA32F;\n\n\tcase 4: return PTI_DEPTH16;\n\tcase 5: return PTI_DEPTH24;\n\tcase 6: return PTI_DEPTH32;\n\n\tcase 7: return PTI_R8;\n\tcase 8: return PTI_R16F;\n\tcase 9: return PTI_R32F;\n\n\tcase 10: return PTI_A2BGR10;\n\tcase 11: return PTI_RGB565;\n\tcase 12: return PTI_RGBA4444;\n\tcase 13: return PTI_RG8;\n\tcase 14: return PTI_RGB32F;\n\n\tcase 15: return TF_8PAL24;\n\tcase 16: return TF_8PAL32;\n\n\tdefault:\n\t\tqcformat = -qcformat;\n\t\tif ((unsigned int)qcformat < PTI_MAX)\n\t\t\treturn qcformat;\n\t\treturn PTI_INVALID;\n\t}\n}\nint PR_UnTranslateTextureFormat(uploadfmt_t pixelformat)\n{\n\tswitch(pixelformat)\n\t{\n\tcase PTI_RGBA8:\t\treturn 1;\n\tcase PTI_RGBA16F:\treturn 2;\n\tcase PTI_RGBA32F:\treturn 3;\n\n\tcase PTI_DEPTH16:\treturn 4;\n\tcase PTI_DEPTH24:\treturn 5;\n\tcase PTI_DEPTH32:\treturn 6;\n\n\tcase PTI_R8:\t\treturn 7;\n\tcase PTI_R16F:\t\treturn 8;\n\tcase PTI_R32F:\t\treturn 9;\n\n\tcase PTI_A2BGR10:\treturn 10;\n\tcase PTI_RGB565:\treturn 11;\n\tcase PTI_RGBA4444:\treturn 12;\n\tcase PTI_RG8:\t\treturn 13;\n\tcase PTI_RGB32F:\treturn 14;\n\n\tcase TF_8PAL24:\t\treturn 15;\n\tcase TF_8PAL32:\t\treturn 16;\n\n\tdefault:return -pixelformat;\n\t}\n}\nvoid QCBUILTIN PF_R_SetViewFlag(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tviewflags parametertype = G_FLOAT(OFS_PARM0);\n\tfloat *p = G_VECTOR(OFS_PARM1);\n\n\tif (prinst->callargc < 2)\n\t{\n\t\tcsqc_deprecated(\"PF_R_SetViewFlag called with wrong argument count\\n\");\n\t\tPF_R_GetViewFlag(prinst, pr_globals);\n\t\treturn;\n\t}\n\n\tif (R2D_Flush)\n\t\tR2D_Flush();\n\n\tcsqc_rebuildmatricies = true;\n\tG_FLOAT(OFS_RETURN) = 1;\n\n#ifdef HAVE_LEGACY\n\tif (csqc_isdarkplaces && prinst == csqc_world.progs)\n\t{\n\t\tswitch(parametertype)\n\t\t{\n\t\tcase VF_VIEWPORT:\n\t\t\tr_refdef.grect.x = p[0] * (float)vid.width / vid.pixelwidth;\n\t\t\tr_refdef.grect.y = p[1] * (float)vid.height / vid.pixelheight;\n\t\t\tp = G_VECTOR(OFS_PARM2);\n\t\t\tr_refdef.grect.width = p[0] * (float)vid.width / vid.pixelwidth;\n\t\t\tr_refdef.grect.height = p[1] * (float)vid.height / vid.pixelheight;\n\t\t\tr_refdef.dirty |= RDFD_FOV;\n\t\t\treturn;\n\t\tcase VF_SIZE_X:\n\t\t\tr_refdef.grect.width = *p * (float)vid.width / vid.pixelwidth;\n\t\t\tr_refdef.dirty |= RDFD_FOV;\n\t\t\treturn;\n\t\tcase VF_SIZE_Y:\n\t\t\tr_refdef.grect.height = *p * (float)vid.height / vid.pixelheight;\n\t\t\tr_refdef.dirty |= RDFD_FOV;\n\t\t\treturn;\n\t\tcase VF_SIZE:\n\t\t\tr_refdef.grect.width = p[0] * (float)vid.width / vid.pixelwidth;\n\t\t\tr_refdef.grect.height = p[1] * (float)vid.height / vid.pixelheight;\n\t\t\tr_refdef.dirty |= RDFD_FOV;\n\t\t\treturn;\n\t\tcase VF_DP_MAINVIEW:\n\t\tcase VF_DP_MINFPS_QUALITY:\n\t\tcase VF_DP_CLEARSCREEN:\n\t\tcase VF_DP_FOG_DENSITY:\n\t\tcase VF_DP_FOG_COLOR:\n\t\tcase VF_DP_FOG_COLOR_R:\n\t\tcase VF_DP_FOG_COLOR_G:\n\t\tcase VF_DP_FOG_COLOR_B:\n\t\tcase VF_DP_FOG_ALPHA:\n\t\tcase VF_DP_FOG_START:\n\t\tcase VF_DP_FOG_END:\n\t\tcase VF_DP_FOG_HEIGHT:\n\t\tcase VF_DP_FOG_FADEDEPTH:\n\t\t\treturn;\n\t\tdefault:\n\t\t\tbreak;\n\t\t}\n\t}\n#endif\n\n\tswitch(parametertype)\n\t{\n\tcase VF_ACTIVESEAT:\n\t\tif (prinst == csqc_world.progs)\n\t\t{\n\t\t\tif (csqc_playerseat != (int)*p)\n\t\t\t{\n\t\t\t\tCSQC_ChangeLocalPlayer(*p);\n\t\t\t\tif (prinst->callargc < 3 || G_FLOAT(OFS_PARM2))\n\t\t\t\t\tV_CalcRefdef(csqc_playerview);\t//set up the default position+angles for the named player.\n\t\t\t}\n\t\t}\n\t\tbreak;\n\tcase VF_VIEWENTITY:\n\t\t//switches over EXTERNALMODEL flags and clears WEAPONMODEL flagged entities.\n\t\t//FIXME: make affect addentities(MASK_ENGINE) calls too.\n\t\tV_EditExternalModels(*p, NULL, 0);\n\t\tbreak;\n\tcase VF_FOV:\n\t\t//explicit fov overrides aproximate fov\n\t\tr_refdef.afov = 0;\n\t\tr_refdef.fov_x = p[0];\n\t\tr_refdef.fov_y = p[1];\n\t\tr_refdef.fovv_x = r_refdef.fovv_y = 0;\n\t\tr_refdef.dirty |= RDFD_FOV;\n\t\tbreak;\n\n\tcase VF_FOV_X:\n\t\tr_refdef.afov = 0;\n\t\tr_refdef.fov_x = *p;\n\t\tr_refdef.fovv_x = r_refdef.fovv_y = 0;\n\t\tr_refdef.dirty |= RDFD_FOV;\n\t\tbreak;\n\n\tcase VF_FOV_Y:\n\t\tr_refdef.afov = 0;\n\t\tr_refdef.fov_y = *p;\n\t\tr_refdef.fovv_x = r_refdef.fovv_y = 0;\n\t\tr_refdef.dirty |= RDFD_FOV;\n\t\tbreak;\n\n\tcase VF_AFOV:\n\t\tr_refdef.afov = *p;\n\t\tr_refdef.fov_x = r_refdef.fov_y = 0;\n\t\tr_refdef.fovv_x = r_refdef.fovv_y = 0;\n\t\tr_refdef.dirty |= RDFD_FOV;\n\t\tbreak;\n\n\tcase VF_SKYROOM_CAMERA:\n\t\tr_refdef.skyroom_enabled = true;\n\t\tVectorCopy(p, r_refdef.skyroom_pos);\n\t\tif (prinst->callargc >= 4)\n\t\t{\n\t\t\tVectorCopy(G_VECTOR(OFS_PARM2), r_refdef.skyroom_spin);\n\t\t\tr_refdef.skyroom_spin[3] = G_FLOAT(OFS_PARM3);\n\t\t}\n\t\telse\n\t\t\tVector4Set(r_refdef.skyroom_spin, 0, 0, 0, 0);\n\t\tbreak;\n\n\tcase VF_ORIGIN:\n\t\tVectorCopy(p, r_refdef.vieworg);\n\t\tif (csqc_playerview)\n\t\t\tcsqc_playerview->crouch = 0;\n\t\tbreak;\n\n\tcase VF_ORIGIN_Z:\n\t\tif (csqc_playerview)\n\t\t\tcsqc_playerview->crouch = 0;\n\tcase VF_ORIGIN_X:\n\tcase VF_ORIGIN_Y:\n\t\tr_refdef.vieworg[parametertype-VF_ORIGIN_X] = *p;\n\t\tbreak;\n\n\tcase VF_ANGLES:\n\t\tVectorCopy(p, r_refdef.viewangles);\n\t\tbreak;\n\tcase VF_ANGLES_X:\n\tcase VF_ANGLES_Y:\n\tcase VF_ANGLES_Z:\n\t\tr_refdef.viewangles[parametertype-VF_ANGLES_X] = *p;\n\t\tbreak;\n\n\tcase VF_VRBASEORIENTATION:\n\t\tin_vraim.ival = 0;\t//csqc mod with explicit vr stuff.\n\t\tr_refdef.base_known = true;\n\t\tVectorCopy(p, r_refdef.base_angles);\n\t\tVectorCopy(G_VECTOR(OFS_PARM2), r_refdef.base_origin);\n\t\tbreak;\n\n\tcase VF_CL_VIEWANGLES_V:\n\t\tif (csqc_playerview)\n\t\t\tVectorCopy(p, csqc_playerview->viewangles);\n\t\tbreak;\n\tcase VF_CL_VIEWANGLES_X:\n\tcase VF_CL_VIEWANGLES_Y:\n\tcase VF_CL_VIEWANGLES_Z:\n\t\tif (csqc_playerview)\n\t\t\tcsqc_playerview->viewangles[parametertype-VF_CL_VIEWANGLES_X] = *p;\n\t\tbreak;\n\n\tcase VF_CARTESIAN_ANGLES:\n\t\tCon_Printf(CON_WARNING \"WARNING: CARTESIAN ANGLES ARE NOT YET SUPPORTED!\\n\");\n\t\tbreak;\n\n\tcase VF_VIEWPORT:\n\t\tr_refdef.grect.x = p[0];\n\t\tr_refdef.grect.y = p[1];\n\t\tp = G_VECTOR(OFS_PARM2);\n\t\tr_refdef.grect.width = p[0];\n\t\tr_refdef.grect.height = p[1];\n\t\tr_refdef.dirty |= RDFD_FOV;\n\t\tbreak;\n\n\tcase VF_SIZE_X:\n\t\tr_refdef.grect.width = *p;\n\t\tr_refdef.dirty |= RDFD_FOV;\n\t\tbreak;\n\tcase VF_SIZE_Y:\n\t\tr_refdef.grect.height = *p;\n\t\tr_refdef.dirty |= RDFD_FOV;\n\t\tbreak;\n\tcase VF_SIZE:\n\t\tr_refdef.grect.width = p[0];\n\t\tr_refdef.grect.height = p[1];\n\t\tr_refdef.dirty |= RDFD_FOV;\n\t\tbreak;\n\n\tcase VF_MIN_X:\n\t\tr_refdef.grect.x = *p;\n\t\tbreak;\n\tcase VF_MIN_Y:\n\t\tr_refdef.grect.y = *p;\n\t\tbreak;\n\tcase VF_MIN:\n\t\tr_refdef.grect.x = p[0];\n\t\tr_refdef.grect.y = p[1];\n\t\tbreak;\n\tcase VF_MINDIST:\n\t\tr_refdef.mindist = *p;\n\t\tbreak;\n\tcase VF_MAXDIST:\n\t\tr_refdef.maxdist = *p;\n\t\tbreak;\n\n\tcase VF_DRAWWORLD:\n\t\tr_refdef.flags = (r_refdef.flags&~RDF_NOWORLDMODEL) | (*p?0:RDF_NOWORLDMODEL);\n\t\tbreak;\n\tcase VF_ENGINESBAR:\n\t\tr_refdef.drawsbar = !!*p;\n\t\tbreak;\n\tcase VF_DRAWCROSSHAIR:\n\t\tr_refdef.drawcrosshair = *p;\n\t\tbreak;\n\n\tcase VF_PERSPECTIVE:\n\t\tr_refdef.useperspective = *p;\n\t\tbreak;\n\n\tcase VF_PROJECTIONOFFSET:\n\t\tr_refdef.projectionoffset[0] = p[0];\n\t\tr_refdef.projectionoffset[1] = p[1];\n\t\tbreak;\n\n\tcase VF_RT_DESTCOLOUR0:\n\tcase VF_RT_DESTCOLOUR1:\n\tcase VF_RT_DESTCOLOUR2:\n\tcase VF_RT_DESTCOLOUR3:\n\tcase VF_RT_DESTCOLOUR4:\n\tcase VF_RT_DESTCOLOUR5:\n\tcase VF_RT_DESTCOLOUR6:\n\tcase VF_RT_DESTCOLOUR7:\n\t\t{\n\t\t\tint i = parametertype - VF_RT_DESTCOLOUR0;\n\t\t\tQ_strncpyz(r_refdef.rt_destcolour[i].texname, PR_GetStringOfs(prinst, OFS_PARM1), sizeof(r_refdef.rt_destcolour[i].texname));\n\t\t\tif (prinst->callargc >= 4 && *r_refdef.rt_destcolour[i].texname)\n\t\t\t{\n\t\t\t\tint fmt = G_FLOAT(OFS_PARM2);\n\t\t\t\tfloat *size = G_VECTOR(OFS_PARM3);\n\t\t\t\tif (fmt < 0)\n\t\t\t\t\tR2D_RT_Configure(r_refdef.rt_destcolour[i].texname, size[0], size[1], PR_TranslateTextureFormat(-fmt), (RT_IMAGEFLAGS&~IF_LINEAR)|IF_NEAREST);\n\t\t\t\telse\n\t\t\t\t\tR2D_RT_Configure(r_refdef.rt_destcolour[i].texname, size[0], size[1], PR_TranslateTextureFormat(fmt), RT_IMAGEFLAGS);\n\t\t\t}\n\t\t\tBE_RenderToTextureUpdate2d(true);\n\t\t}\n\t\tbreak;\n\tcase VF_RT_SOURCECOLOUR:\n\t\tQ_strncpyz(r_refdef.rt_sourcecolour.texname, PR_GetStringOfs(prinst, OFS_PARM1), sizeof(r_refdef.rt_sourcecolour));\n\t\tif (prinst->callargc >= 4 && *r_refdef.rt_sourcecolour.texname)\n\t\t{\n\t\t\tint fmt = G_FLOAT(OFS_PARM2);\n\t\t\tfloat *size = G_VECTOR(OFS_PARM3);\n\t\t\tif (fmt < 0)\n\t\t\t\tR2D_RT_Configure(r_refdef.rt_sourcecolour.texname, size[0], size[1], PR_TranslateTextureFormat(-fmt), (RT_IMAGEFLAGS&~IF_LINEAR)|IF_NEAREST);\n\t\t\telse\n\t\t\t\tR2D_RT_Configure(r_refdef.rt_sourcecolour.texname, size[0], size[1], PR_TranslateTextureFormat(fmt), RT_IMAGEFLAGS);\n\t\t}\n\t\tBE_RenderToTextureUpdate2d(false);\n\t\tbreak;\n\tcase VF_RT_DEPTH:\n\t\tQ_strncpyz(r_refdef.rt_depth.texname, PR_GetStringOfs(prinst, OFS_PARM1), sizeof(r_refdef.rt_depth.texname));\n\t\tif (prinst->callargc >= 4 && *r_refdef.rt_depth.texname)\n\t\t{\n\t\t\tint fmt = G_FLOAT(OFS_PARM2);\n\t\t\tfloat *size = G_VECTOR(OFS_PARM3);\n\t\t\tif (fmt < 0)\n\t\t\t\tR2D_RT_Configure(r_refdef.rt_depth.texname, size[0], size[1], PR_TranslateTextureFormat(-fmt), (RT_IMAGEFLAGS&~IF_LINEAR)|IF_NEAREST);\n\t\t\telse\n\t\t\t\tR2D_RT_Configure(r_refdef.rt_depth.texname, size[0], size[1], PR_TranslateTextureFormat(fmt), RT_IMAGEFLAGS);\n\t\t}\n\t\tBE_RenderToTextureUpdate2d(false);\n\t\tbreak;\n\tcase VF_RT_RIPPLE:\n\t\tQ_strncpyz(r_refdef.rt_ripplemap.texname, PR_GetStringOfs(prinst, OFS_PARM1), sizeof(r_refdef.rt_ripplemap.texname));\n\t\tif (prinst->callargc >= 4 && *r_refdef.rt_ripplemap.texname)\n\t\t{\n\t\t\tint fmt = G_FLOAT(OFS_PARM2);\n\t\t\tfloat *size = G_VECTOR(OFS_PARM3);\n\t\t\tif (fmt < 0)\n\t\t\t\tR2D_RT_Configure(r_refdef.rt_ripplemap.texname, size[0], size[1], PR_TranslateTextureFormat(-fmt), (RT_IMAGEFLAGS&~IF_LINEAR)|IF_NEAREST);\n\t\t\telse\n\t\t\t\tR2D_RT_Configure(r_refdef.rt_ripplemap.texname, size[0], size[1], PR_TranslateTextureFormat(fmt), RT_IMAGEFLAGS);\n\t\t}\n\t\tBE_RenderToTextureUpdate2d(false);\n\t\tbreak;\n\n\tcase VF_ENVMAP:\n\t\tQ_strncpyz(r_refdef.nearenvmap.texname, PR_GetStringOfs(prinst, OFS_PARM1), sizeof(r_refdef.nearenvmap.texname));\n\t\tBE_RenderToTextureUpdate2d(false);\n\t\tbreak;\n\n\tcase VF_USERDATA:\n\t\t{\n\t\t\tint qcptr = G_INT(OFS_PARM1);\n\t\t\tint size = G_INT(OFS_PARM2);\n\t\t\tvoid *ptr;\n\n\t\t\tif (size > sizeof(r_refdef.userdata))\n\t\t\t\tsize = sizeof(r_refdef.userdata);\n\n\t\t\t//validates the pointer.\n\t\t\tif (qcptr < 0 || qcptr+size >= prinst->stringtablesize || size < 0)\n\t\t\t{\n\t\t\t\tPR_BIError(prinst, \"PF_R_SetViewFlag: invalid pointer\\n\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tptr = (prinst->stringtable + qcptr);\n\t\t\tmemcpy(r_refdef.userdata, ptr, size);\n\t\t}\n\t\tbreak;\n\n\tdefault:\n\t\tCon_DPrintf(\"SetViewFlag: %i not recognised\\n\", parametertype);\n\t\tG_FLOAT(OFS_RETURN) = 0;\n\t\tbreak;\n\t}\n}\n\nvoid R2D_PolyBlend (void);\nvoid R_DrawNameTags(void);\nstatic void QCBUILTIN PF_R_RenderScene(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tqboolean scissored;\n\n\tif (R2D_Flush)\n\t\tR2D_Flush();\n\tcsqc_poly_shader = NULL;\n\n\tif (csqc_worldchanged)\n\t{\n\t\tcsqc_worldchanged = false;\n\t\tcl.worldmodel = r_worldentity.model = csqc_world.worldmodel;\n\t\tif (cl.worldmodel)\n\t\t\tFS_LoadMapPackFile(cl.worldmodel->name, cl.worldmodel->archive);\n\t\tSurf_NewMap(csqc_world.worldmodel);\n\t\tCL_UpdateWindowTitle();\n\n\t\tWorld_RBE_Shutdown(&csqc_world);\n\t\tWorld_RBE_Start(&csqc_world);\n\t}\n\n\tR2D_ImageColours(1,1,1,1);\t//apparently does matter.\n\n\tif (cl.worldmodel)\n\t\tR_PushDlights ();\n\n\tr_refdef.playerview = csqc_playerview;\n\n\tV_ApplyRefdef();\n\tV_CalcGunPositionAngle(csqc_playerview, V_CalcBob(csqc_playerview, true));\n\n\tR_RenderView();\n\tR2D_PolyBlend ();\n\n\tif (r_refdef.grect.x || r_refdef.grect.y || r_refdef.grect.width != vid.fbvwidth || r_refdef.grect.height != vid.fbvheight)\n\t{\n\t\tsrect_t srect;\n\t\tsrect.x = (float)r_refdef.grect.x / vid.fbvwidth;\n\t\tsrect.y = (float)r_refdef.grect.y / vid.fbvheight;\n\t\tsrect.width = (float)r_refdef.grect.width / vid.fbvwidth;\n\t\tsrect.height = (float)r_refdef.grect.height / vid.fbvheight;\n\t\tsrect.dmin = -99999;\n\t\tsrect.dmax = 99999;\n\t\tsrect.y = (1-srect.y) - srect.height;\n\t\tBE_Scissor(&srect);\n\t\tscissored = true;\n\t}\n\telse\n\t\tscissored = false;\n\n\tif (!vrui.enabled)\t//when we're using the vrui, this stuff needs to be part of the scene, drawn seperately for each eye\n\t{\n\t\tR_DrawNameTags();\n#ifdef RTLIGHTS\n\t\tR_EditLights_DrawInfo();\n#endif\n\n\t\tif (r_refdef.drawsbar)\n\t\t{\n#ifdef PLUGINS\n\t\t\tPlug_SBar (r_refdef.playerview);\n#else\n\t\t\tif (Sbar_ShouldDraw(r_refdef.playerview))\n\t\t\t{\n\t\t\t\tSCR_TileClear (sb_lines);\n\t\t\t\tSbar_Draw (r_refdef.playerview);\n\t\t\t\tSbar_DrawScoreboard (r_refdef.playerview);\n\t\t\t}\n\t\t\telse\n\t\t\t\tSCR_TileClear (0);\n#endif\n\n\t\t\tif (!Key_Dest_Has(kdm_menu|kdm_cwindows))\n\t\t\t{\n\t\t\t\tif (cl.intermissionmode == IM_NQFINALE || cl.intermissionmode == IM_NQCUTSCENE || cl.intermissionmode == IM_H2FINALE)\n\t\t\t\t{\n\t\t\t\t\tSCR_CheckDrawCenterString ();\n\t\t\t\t}\n\t\t\t\telse if (cl.intermissionmode != IM_NONE)\n\t\t\t\t{\n\t\t\t\t\tSbar_IntermissionOverlay (r_refdef.playerview);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tSCR_ShowPics_Draw();\n\t\t}\n\n\t\tif (r_refdef.drawcrosshair)\n\t\t\tR2D_DrawCrosshair();\n\t}\n\n\tif (scissored)\n\t{\n\t\tif (R2D_Flush)\n\t\t\tR2D_Flush();\n\n\t\tBE_Scissor(NULL);\n\t}\n}\n\nstatic void QCBUILTIN PF_cs_getstat_int(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint stnum = G_FLOAT(OFS_PARM0);\n\tif (stnum < 0 || stnum >= MAX_CL_STATS)\n\t{\n\t\tG_INT(OFS_RETURN) = 0;\n\t\tPR_RunWarning(prinst, \"PF_cs_getstat_int: invalid stat index (%i)\\n\", stnum);\n\t}\n\telse if (stnum >= 128 && csqc_isdarkplaces && cls.protocol != CP_NETQUAKE && !CPNQ_IS_DP)\n\t{\t//dpp7 stats are fucked.\n\t\tG_FLOAT(OFS_RETURN) = csqc_playerview->statsf[stnum];\n\t\tcsqc_deprecated(\"hacked stat type\");\n\t}\n\telse\n\t\tG_INT(OFS_RETURN) = csqc_playerview->stats[stnum];\n}\nstatic void QCBUILTIN PF_cs_getstat_float(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\t//read a numeric stat into a qc float.\n\t//if bits offsets are specified, reads explicitly the integer version of the stat, allowing high bits to be read for items2/serverflags. the float stat should have the same value, just with lower precision as a float can't hold a 32bit value. maybe we should just use doubles.\n\n\tint stnum = G_FLOAT(OFS_PARM0);\n\tif (stnum < 0 || stnum >= MAX_CL_STATS)\n\t{\n\t\tG_FLOAT(OFS_RETURN) = 0;\n\t\tPR_RunWarning(prinst, \"PF_cs_getstat_float: invalid stat index (%i)\\n\", stnum);\n\t}\n\telse if (prinst->callargc > 1)\n\t{\t//like getstati but with partial itof\n\t\tint val = csqc_playerview->stats[stnum];\n\t\tint first, count;\n\t\tfirst = G_FLOAT(OFS_PARM1);\n\t\tif (prinst->callargc > 2)\n\t\t\tcount = G_FLOAT(OFS_PARM2);\n\t\telse\n\t\t\tcount = 1;\n\t\tG_FLOAT(OFS_RETURN) = (((unsigned int)val)&(((1<<count)-1)<<first))>>first;\n\t}\n\telse if (csqc_isdarkplaces && cls.protocol != CP_NETQUAKE && !CPNQ_IS_DP)\n\t{\n\t\tG_FLOAT(OFS_RETURN) = (int)csqc_playerview->statsf[stnum];\t//stupid. mods like xonotic end up with an ugly hud if they're actually given any precision\n\t\tif (G_FLOAT(OFS_RETURN) != csqc_playerview->statsf[stnum])\n\t\t\tcsqc_deprecated(\"getstat_float stat truncation\");\t//this is a common call. only get pissy if there's a reason to get pissy.\n\t}\n\telse\n\t\tG_FLOAT(OFS_RETURN) = csqc_playerview->statsf[stnum];\n}\nstatic void QCBUILTIN PF_cs_getstat_string(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint stnum = G_FLOAT(OFS_PARM0);\n\n\tif (stnum < 0 || stnum >= MAX_CL_STATS)\n\t{\n\t\tG_INT(OFS_RETURN) = 0;\n\t\tPR_RunWarning(prinst, \"PF_cs_getstats: invalid stat index (%i)\\n\", stnum);\n\t}\n\telse if (cls.fteprotocolextensions & PEXT_CSQC)\n\t\tRETURN_TSTRING(csqc_playerview->statsstr[stnum]);\n\telse if (stnum >= MAX_CL_STATS-3)\t//we'll be reading the following 3 extra stats too.\n\t{\n\t\tG_INT(OFS_RETURN) = 0;\n\t\tPR_RunWarning(prinst, \"PF_cs_getstats: invalid packed-string stat index (%i)\\n\", stnum);\n\t}\n\telse\n\t{\n\t\tchar out[17];\n\n\t\t//the network protocol byteswaps\n\n\t\t((unsigned int*)out)[0] = LittleLong(csqc_playerview->stats[stnum+0]);\n\t\t((unsigned int*)out)[1] = LittleLong(csqc_playerview->stats[stnum+1]);\n\t\t((unsigned int*)out)[2] = LittleLong(csqc_playerview->stats[stnum+2]);\n\t\t((unsigned int*)out)[3] = LittleLong(csqc_playerview->stats[stnum+3]);\n\t\tout[sizeof(out)-1] = 0;\t//make sure it's null terminated\n\n\t\tRETURN_TSTRING(out);\n\t}\n}\n\nstatic void QCBUILTIN PF_cs_SetOrigin(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tworld_t *w = prinst->parms->user;\n\twedict_t *ent = (void*)G_WEDICT(prinst, OFS_PARM0);\n\tfloat *org = G_VECTOR(OFS_PARM1);\n\tif (ent->readonly)\n\t{\n\t\tPR_RunWarning(prinst, \"setorigin on entity %i\\n\", ent->entnum);\n\t\treturn;\n\t}\n\tVectorCopy(org, ent->v->origin);\n\tWorld_LinkEdict(w, (wedict_t*)ent, false);\n}\n\nstatic void QCBUILTIN PF_cs_SetSize (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tworld_t *w = prinst->parms->user;\n\twedict_t\t*e;\n\tfloat\t*min, *max;\n\n\te = G_WEDICT(prinst, OFS_PARM0);\n\tif (ED_ISFREE(e))\n\t{\n\t\tPR_RunWarning(prinst, \"%s edict was free\\n\", \"setsize\");\n\t\treturn;\n\t}\n\tif (e->readonly)\n\t{\n\t\tPR_RunWarning(prinst, \"setsize on entity %i\\n\", e->entnum);\n\t\treturn;\n\t}\n\tmin = G_VECTOR(OFS_PARM1);\n\tmax = G_VECTOR(OFS_PARM2);\n\tVectorCopy (min, e->v->mins);\n\tVectorCopy (max, e->v->maxs);\n\tVectorSubtract (max, min, e->v->size);\n\tWorld_LinkEdict (w, (wedict_t*)e, false);\n}\n\nstatic void cs_settracevars(pubprogfuncs_t *prinst, trace_t *tr)\n{\n\t*csqcg.trace_allsolid = tr->allsolid;\n\t*csqcg.trace_startsolid = tr->startsolid;\n\t*csqcg.trace_fraction = tr->fraction;\n\t*csqcg.trace_inwater = tr->inwater;\n\t*csqcg.trace_inopen = tr->inopen;\n\tVectorCopy (tr->endpos, csqcg.trace_endpos);\n\tVectorCopy (tr->plane.normal, csqcg.trace_plane_normal);\n\t*csqcg.trace_plane_dist =  tr->plane.dist;\n\t*csqcg.trace_surfaceflagsi = tr->surface?tr->surface->flags:0;\n\tif (csqcg.trace_surfacename)\n\t\tprinst->SetStringField(prinst, NULL, csqcg.trace_surfacename, tr->surface?tr->surface->name:NULL, true);\n\t*csqcg.trace_endcontentsi = tr->contents;\n\t*csqcg.trace_brush_id = tr->brush_id;\n\t*csqcg.trace_brush_faceid = tr->brush_face;\n\t*csqcg.trace_surface_id = tr->surface_id;\n\t*csqcg.trace_bone_id = tr->bone_id;\n\t*csqcg.trace_triangle_id = tr->triangle_id;\n\tif (tr->ent)\n\t\t*csqcg.trace_ent = EDICT_TO_PROG(csqcprogs, (void*)tr->ent);\n\telse\n\t\t*csqcg.trace_ent = EDICT_TO_PROG(csqcprogs, (void*)csqc_world.edicts);\n\t*csqcg.trace_networkentity = tr->entnum;\n\n#ifdef HAVE_LEGACY\n\t*csqcg.trace_endcontentsf = tr->contents;\n\t*csqcg.trace_surfaceflagsf = tr->surface?tr->surface->flags:0;\n\n\tif (csqcg.trace_dphittexturename)\n\t\tprinst->SetStringField(prinst, NULL, csqcg.trace_dphittexturename, tr->surface?tr->surface->name:NULL, true);\n\tif (csqcg.trace_dpstartcontents)\n\t\t*csqcg.trace_dpstartcontents = FTEToDPContents(0);\t//fixme, maybe\n\tif (csqcg.trace_dphitcontents)\n\t\t*csqcg.trace_dphitcontents = FTEToDPContents(tr->contents);\n\tif (csqcg.trace_dphitq3surfaceflags)\n\t\t*csqcg.trace_dphitq3surfaceflags = tr->surface?tr->surface->flags:0;\n#endif\n}\n\nstatic void QCBUILTIN PF_cs_traceline(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tfloat\t*v1, *v2, *mins, *maxs;\n\ttrace_t\ttrace;\n\tint\t\tnomonsters;\n\tcsqcedict_t\t*ent;\n\n\tv1 = G_VECTOR(OFS_PARM0);\n\tv2 = G_VECTOR(OFS_PARM1);\n\tnomonsters = G_FLOAT(OFS_PARM2);\n\tent = (csqcedict_t*)G_EDICT(prinst, OFS_PARM3);\n\n//\tif (*prinst->callargc == 6)\n//\t{\n//\t\tmins = G_VECTOR(OFS_PARM4);\n//\t\tmaxs = G_VECTOR(OFS_PARM5);\n//\t}\n//\telse\n\t{\n\t\tmins = vec3_origin;\n\t\tmaxs = vec3_origin;\n\t}\n\n\ttrace = World_Move (&csqc_world, v1, mins, maxs, v2, nomonsters|MOVE_IGNOREHULL, (wedict_t*)ent);\n\n\tcs_settracevars(prinst, &trace);\n}\nstatic void QCBUILTIN PF_cs_tracebox(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tfloat\t*v1, *v2, *mins, *maxs;\n\ttrace_t\ttrace;\n\tint\t\tnomonsters;\n\tcsqcedict_t\t*ent;\n\n\tv1 = G_VECTOR(OFS_PARM0);\n\tmins = G_VECTOR(OFS_PARM1);\n\tmaxs = G_VECTOR(OFS_PARM2);\n\tv2 = G_VECTOR(OFS_PARM3);\n\tnomonsters = G_FLOAT(OFS_PARM4);\n\tent = (csqcedict_t*)G_EDICT(prinst, OFS_PARM5);\n\n\ttrace = World_Move (&csqc_world, v1, mins, maxs, v2, nomonsters|MOVE_IGNOREHULL, (wedict_t*)ent);\n\n\tcs_settracevars(prinst, &trace);\n}\n\nstatic trace_t CS_Trace_Toss (csqcedict_t *tossent, csqcedict_t *ignore)\n{\n\tint i;\n\tfloat gravity;\n\tvec3_t move, end;\n\ttrace_t trace;\n//\tfloat maxvel = Cvar_Get(\"sv_maxvelocity\", \"2000\", 0, \"CSQC physics\")->value;\n\n\tvec3_t origin, velocity;\n\n\t// this has to fetch the field from the original edict, since our copy is truncated\n\tgravity = 1;//tossent->v->gravity;\n\tif (!gravity)\n\t\tgravity = 1.0;\n\tgravity *= Cvar_Get(\"sv_gravity\", \"800\", 0, \"CSQC physics\")->value * 0.05;\n\n\tVectorCopy (tossent->v->origin, origin);\n\tVectorCopy (tossent->v->velocity, velocity);\n\n\tCS_CheckVelocity (tossent);\n\n\tfor (i = 0;i < 200;i++) // LordHavoc: sanity check; never trace more than 10 seconds\n\t{\n\t\tvelocity[2] -= gravity;\n\t\tVectorScale (velocity, 0.05, move);\n\t\tVectorAdd (origin, move, end);\n\t\ttrace = World_Move (&csqc_world, origin, tossent->v->mins, tossent->v->maxs, end, MOVE_NORMAL|MOVE_IGNOREHULL, (wedict_t*)tossent);\n\t\tVectorCopy (trace.endpos, origin);\n\n\t\tCS_CheckVelocity (tossent);\n\n\t\tif (trace.fraction < 1 && trace.ent && (void*)trace.ent != ignore)\n\t\t\tbreak;\n\t}\n\n\ttrace.fraction = 0; // not relevant\n\treturn trace;\n}\nstatic void QCBUILTIN PF_cs_tracetoss (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\ttrace_t\ttrace;\n\tcsqcedict_t\t*ent;\n\tcsqcedict_t\t*ignore;\n\n\tent = (csqcedict_t*)G_EDICT(prinst, OFS_PARM0);\n\tif (ent == (csqcedict_t*)csqc_world.edicts)\n\t\tCon_DPrintf(\"tracetoss: can not use world entity\\n\");\n\tignore = (csqcedict_t*)G_EDICT(prinst, OFS_PARM1);\n\n\ttrace = CS_Trace_Toss (ent, ignore);\n\n\tcs_settracevars(prinst, &trace);\n}\n\nstatic void QCBUILTIN PF_cs_pointcontents(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tworld_t *w = prinst->parms->user;\n\n\tfloat\t*v;\n\tint cont;\n\n\tv = G_VECTOR(OFS_PARM0);\n\n\tcont = w->worldmodel?World_PointContentsWorldOnly(w, v):FTECONTENTS_EMPTY;\n\tif (cont & FTECONTENTS_SOLID)\n\t\tG_FLOAT(OFS_RETURN) = Q1CONTENTS_SOLID;\n\telse if (cont & FTECONTENTS_SKY)\n\t\tG_FLOAT(OFS_RETURN) = Q1CONTENTS_SKY;\n\telse if (cont & FTECONTENTS_LAVA)\n\t\tG_FLOAT(OFS_RETURN) = Q1CONTENTS_LAVA;\n\telse if (cont & FTECONTENTS_SLIME)\n\t\tG_FLOAT(OFS_RETURN) = Q1CONTENTS_SLIME;\n\telse if (cont & FTECONTENTS_WATER)\n\t\tG_FLOAT(OFS_RETURN) = Q1CONTENTS_WATER;\n\telse\n\t\tG_FLOAT(OFS_RETURN) = Q1CONTENTS_EMPTY;\n}\nstatic void QCBUILTIN PF_cs_pointcontentsmask(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tworld_t *w = prinst->parms->user;\n\n\tfloat\t*v;\n\tint cont;\n\n\tv = G_VECTOR(OFS_PARM0);\n\n\tif (!w->worldmodel || w->worldmodel->loadstate != MLS_LOADED)\n\t\tcont = FTECONTENTS_EMPTY;\n\telse if (prinst->callargc < 1 || G_FLOAT(OFS_PARM1))\n\t\tcont = World_PointContentsWorldOnly(w, v);\n\telse\n\t\tcont = World_PointContentsAllBSPs(w, v);\n\tG_UINT(OFS_RETURN) = cont;\n}\n\nstatic model_t *csqc_setmodel(pubprogfuncs_t *prinst, csqcedict_t *ent, int modelindex)\n{\n\tmodel_t *model;\n\t\n\tif (ent->readonly)\n\t{\n\t\tCon_Printf(\"setmodel on readonly entity %i\\n\", ent->entnum);\n\t\treturn NULL;\n\t}\n\n\tent->v->modelindex = modelindex;\n\tif (modelindex < 0)\n\t{\n\t\tif (modelindex <= -MAX_CSMODELS)\n\t\t\treturn NULL;\n\t\tprinst->SetStringField(prinst, (void*)ent, &ent->v->model, cl.model_csqcname[-modelindex], true);\n\t\tif (!cl.model_csqcprecache[-modelindex])\n\t\t\tcl.model_csqcprecache[-modelindex] = Mod_ForName(Mod_FixName(cl.model_csqcname[-modelindex], csqc_world.worldmodel->publicname), MLV_WARN);\n\t\tmodel = cl.model_csqcprecache[-modelindex];\n\t}\n\telse\n\t{\n\t\tif (modelindex >= MAX_PRECACHE_MODELS || !cl.model_name[modelindex])\n\t\t\treturn NULL;\n\t\tprinst->SetStringField(prinst, (void*)ent, &ent->v->model, cl.model_name[modelindex], true);\n\t\tmodel = cl.model_precache[modelindex];\n\t}\n\tif (model)\n\t{\n\t\t//csqc probably needs to know the actual model size for any entity. it might as well.\n\t\twhile(model->loadstate == MLS_LOADING)\n\t\t\tCOM_WorkerPartialSync(model, &model->loadstate, MLS_LOADING);\n\n\t\tVectorCopy(model->mins, ent->v->mins);\n\t\tVectorCopy(model->maxs, ent->v->maxs);\n\t\tVectorSubtract (model->maxs, model->mins, ent->v->size);\n\n\t\tif (!ent->entnum)\n\t\t{\t//setmodel(world, \"maps/foo.bsp\"); may be used to switch the csqc's worldmodel.\n\t\t\tcsqc_world.worldmodel = model;\n\t\t\tcsqc_worldchanged = true;\n\n\t\t\tVectorAdd(ent->v->origin, ent->v->mins, ent->v->absmin);\n\t\t\tVectorAdd(ent->v->origin, ent->v->maxs, ent->v->absmax);\n\n\t\t\tWorld_ClearWorld (&csqc_world, true);\t//make sure any pvs stuff is rebuilt.\n\t\t\tcl.num_statics = 0;\t//has pvs indexes that can cause crashes.\n\t\t\treturn model;\n\t\t}\n\t}\n\telse\n\t{\n\t\tVectorClear(ent->v->mins);\n\t\tVectorClear(ent->v->maxs);\n\t}\n\n\tWorld_LinkEdict(&csqc_world, (wedict_t*)ent, false);\n\n\treturn model;\n}\n\nstatic void QCBUILTIN PF_cs_SetModel(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tcsqcedict_t *ent = (void*)G_EDICT(prinst, OFS_PARM0);\n\tconst char *modelname = PR_GetStringOfs(prinst, OFS_PARM1);\n\tint freei;\n\tint modelindex = CS_FindModel(modelname, &freei);\n\tmodel_t *mod;\n\n\tif (!modelindex && modelname && *modelname)\n\t{\n\t\tif (!freei)\n\t\t\tHost_EndGame(\"CSQC ran out of model slots\\n\");\n\t\tCon_DPrintf(\"Late caching model \\\"%s\\\"\\n\", modelname);\n\t\tQ_strncpyz(cl.model_csqcname[-freei], modelname, sizeof(cl.model_csqcname[-freei]));\t//allocate a slot now\n\t\tmodelindex = freei;\n\n\t\tcl.model_csqcprecache[-freei] = NULL;\n\t}\n\n\tmod = csqc_setmodel(prinst, ent, modelindex);\n\tif (mod)\n\t\tent->xv->modelflags = mod->flags;\n}\nstatic void QCBUILTIN PF_cs_SetModelIndex(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tcsqcedict_t *ent = (void*)G_EDICT(prinst, OFS_PARM0);\n\tint modelindex = G_FLOAT(OFS_PARM1);\n\n\tcsqc_setmodel(prinst, ent, modelindex);\n}\nstatic int PF_cs_PrecacheModel_Internal(pubprogfuncs_t *prinst, const char *modelname, qboolean queryonly)\n{\n\tint modelindex, freei;\n\tconst char *fixedname;\n\tint i;\n\n\tif (!*modelname)\n\t\treturn 0;\n\n\tfor (i = 1; i < MAX_PRECACHE_MODELS; i++)\t//Make sure that the server specified model is loaded..\n\t{\n\t\tif (!cl.model_name[i])\n\t\t\tbreak;\n\t\tif (!strcmp(cl.model_name[i], modelname))\n\t\t{\n\t\t\tif (!cl.model_precache[i])\n\t\t\t\tcl.model_precache[i] = Mod_ForName(Mod_FixName(modelname, csqc_world.worldmodel->publicname), MLV_WARN);\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tmodelindex = CS_FindModel(modelname, &freei);\t//now load it\n\n\tif (!modelindex && !queryonly)\n\t{\n\t\tif (!freei)\n\t\t\tHost_EndGame(\"CSQC ran out of model slots\\n\");\n\t\tfixedname = Mod_FixName(modelname, csqc_world.worldmodel->publicname);\n\t\tQ_strncpyz(cl.model_csqcname[-freei], fixedname, sizeof(cl.model_csqcname[-freei]));\t//allocate a slot now\n\t\tmodelindex = freei;\n\n\t\tCL_CheckOrEnqueDownloadFile(modelname, modelname, 0);\n\t\tcl.model_csqcprecache[-freei] = Mod_ForName(fixedname, MLV_WARN);\n\t}\n\n\treturn modelindex;\n}\nstatic void QCBUILTIN PF_cs_PrecacheModel (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char\t*s = PR_GetStringOfs(prinst, OFS_PARM0);\n\n\tG_INT(OFS_RETURN) = G_INT(OFS_PARM0);\n\tPF_cs_PrecacheModel_Internal(prinst, s, false);\n}\n\nstatic void QCBUILTIN PF_cs_getmodelindex (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char\t*s = PR_GetStringOfs(prinst, OFS_PARM0);\n\tqboolean queryonly = (prinst->callargc >= 2)?G_FLOAT(OFS_PARM1):false;\n\n\tG_FLOAT(OFS_RETURN) = PF_cs_PrecacheModel_Internal(prinst, s, queryonly);\n}\nstatic void QCBUILTIN PF_cs_getsoundindex (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char\t*s = PR_GetStringOfs(prinst, OFS_PARM0);\n\tqboolean queryonly = (prinst->callargc >= 2)?G_FLOAT(OFS_PARM1):false;\n\tint i;\n\n\tG_FLOAT(OFS_RETURN) = 0;\n\t//look for the server's names first...\n\tfor (i = 1; i < MAX_PRECACHE_SOUNDS; i++)\n\t{\n\t\tif (!cl.sound_name[i])\n\t\t\tbreak;\n\t\tif (!strcmp(cl.sound_name[i], s))\n\t\t{\n\t\t\tG_FLOAT(OFS_RETURN) = i;\n\t\t\treturn;\n\t\t}\n\t}\n\n\t//FIXME: we don't track clientside sound precaches (the sound system has its own, but can be flushed at any time forgetting/reordering them)\n\t//can still make sure its cached though.\n\tif (!queryonly)\n\t\tS_PrecacheSound(s);\n}\nstatic void QCBUILTIN PF_cs_precachefile(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char *filename = PR_GetStringOfs(prinst, OFS_PARM0);\n\tG_FLOAT(OFS_RETURN) = CL_CheckOrEnqueDownloadFile(filename, NULL, 0);\t\n}\nstatic void QCBUILTIN PF_cs_PrecacheSound(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char *soundname = PR_GetStringOfs(prinst, OFS_PARM0);\n\tif (!*soundname)\t//invalid\n\t\treturn;\n\tSound_CheckDownload(soundname);\n\tS_PrecacheSound(soundname);\n}\n\nstatic void QCBUILTIN PF_cs_ModelnameForIndex(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint modelindex = G_FLOAT(OFS_PARM0);\n\n\tif (modelindex < 0 && (-modelindex) < MAX_CSMODELS)\n\t\tG_INT(OFS_RETURN) = (int)PR_SetString(prinst, cl.model_csqcname[-modelindex]);\n\telse if (modelindex >= 0 && modelindex < MAX_PRECACHE_MODELS && cl.model_name[modelindex])\n\t\tG_INT(OFS_RETURN) = (int)PR_SetString(prinst, cl.model_name[modelindex]);\n\telse\n\t\tG_INT(OFS_RETURN) = 0;\n}\nstatic void QCBUILTIN PF_cs_SoundnameForIndex(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint soundindex = G_FLOAT(OFS_PARM0);\n\n\t//FIXME: no private indexes. still useful for sending sound names from the ssqc via indexes.\n\tif (soundindex >= 0 && soundindex < MAX_PRECACHE_SOUNDS && cl.sound_name[soundindex])\n\t\tG_INT(OFS_RETURN) = (int)PR_SetString(prinst, cl.sound_name[soundindex]);\n\telse\n\t\tG_INT(OFS_RETURN) = 0;\n}\n\nstatic void QCBUILTIN PF_cs_spriteframe(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char *modelname = PR_GetStringOfs(prinst, OFS_PARM0);\n\tint frame = G_INT(OFS_PARM1);\n\tfloat frametime = G_INT(OFS_PARM2);\n\n\tmodel_t *mod = NULL;\n\n\tG_INT(OFS_RETURN) = 0;\t//default result\n\tmod = CSQC_GetModelForName(modelname);\n\n\tif (!mod)\n\t\treturn;\n\tif (mod->loadstate == MLS_NOTLOADED)\t//pull it back in if it was flushed.\n\t\tMod_LoadModel(mod, MLV_WARN);\n\twhile(mod->loadstate == MLS_LOADING)\t//wait for it if its still loading.\n\t\tCOM_WorkerPartialSync(mod, &mod->loadstate, MLS_LOADING);\n\n\tif (mod->type != mod_sprite)\n\t\treturn;\n\t{\t//okay, its in memory, and its a sprite. actually do what we're here for.\n\t\tmsprite_t\t\t*psprite = mod->meshinfo;\n\t\tmspritegroup_t\t*pspritegroup;\n\t\tmspriteframe_t\t*pspriteframe;\n\t\tint\t\t\t\ti, numframes;\n\t\tfloat\t\t\t*pintervals, fullinterval;\n\n\t\tif ((frame >= psprite->numframes) || (frame < 0))\n\t\t\treturn;\n\n\t\tif (psprite->frames[frame].type == SPR_SINGLE)\n\t\t\tpspriteframe = psprite->frames[frame].frameptr;\n\t\telse if (psprite->frames[frame].type == SPR_ANGLED)\n\t\t{\t//just take frametime as 0-1\n\t\t\tpspritegroup = (mspritegroup_t *)psprite->frames[frame].frameptr;\n\t\t\ti = frametime/pspritegroup->numframes;\n\t\t\tpspriteframe = pspritegroup->frames[i%pspritegroup->numframes];\n\t\t}\n\t\telse\n\t\t{\n\t\t\tpspritegroup = (mspritegroup_t *)psprite->frames[frame].frameptr;\n\t\t\tpintervals = pspritegroup->intervals;\n\t\t\tnumframes = pspritegroup->numframes;\n\t\t\tfullinterval = pintervals[numframes-1];\n\t\t\tframetime = frametime - ((int)(frametime / fullinterval)) * fullinterval;\t//make it loop...\n\t\t\tfor (i=0 ; i<(numframes-1) ; i++)\n\t\t\t{\n\t\t\t\tif (pintervals[i] > frametime)\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tpspriteframe = pspritegroup->frames[i];\n\t\t}\n\n\t\t//and let the caller know which model name they should draw with\n\t\tRETURN_TSTRING(pspriteframe->shader->name);\n\t}\n}\n\nvoid QCBUILTIN PF_cs_setcustomskin (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tcsqcedict_t *ent = (void*)G_EDICT(prinst, OFS_PARM0);\n\tconst char *fname = PR_GetStringOfs(prinst, OFS_PARM1);\n\tconst char *skindata = PF_VarString(prinst, 2, pr_globals);\n\n\tif (ent->skinobject > 0)\n\t\tMod_WipeSkin(ent->skinobject, false);\n\tent->skinobject = 0;\n\n\tif (*fname || *skindata)\n\t{\n\t\tif (*skindata)\n\t\t\tent->skinobject = Mod_ReadSkinFile(fname, skindata);\n\t\telse\n\t\t\tent->skinobject = Mod_RegisterSkinFile(fname);\n\n\t\tif (*fname)\n\t\t\tent->skinobject *= -1;//negative means it doesn't bother refcounting, just leaves it loaded the whole time.\n\t}\n}\nvoid QCBUILTIN PF_cs_loadcustomskin (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char *fname = PR_GetStringOfs(prinst, OFS_PARM0);\n\tconst char *skindata = PF_VarString(prinst, 1, pr_globals);\n\n\tif (*fname || *skindata)\n\t{\n\t\tif (*skindata)\n\t\t\tG_FLOAT(OFS_RETURN) = Mod_ReadSkinFile(fname, skindata);\n\t\telse\n\t\t\tG_FLOAT(OFS_RETURN) = Mod_RegisterSkinFile(fname);\n\t}\n\telse\n\t\tG_FLOAT(OFS_RETURN) = 0;\n}\nvoid QCBUILTIN PF_cs_releasecustomskin (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint oldskin = G_FLOAT(OFS_PARM0);\n\tif (oldskin > 0)\n\t\tMod_WipeSkin(oldskin, false);\n}\nvoid QCBUILTIN PF_cs_applycustomskin (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tcsqcedict_t *ent = (void*)G_EDICT(prinst, OFS_PARM0);\n\tint newskin = G_FLOAT(OFS_PARM1);\n\tint oldskin = ent->skinobject;\n\tskinfile_t *sk;\n\tent->skinobject = -abs(newskin);\n\tif (oldskin > 0)\n\t\tMod_WipeSkin(oldskin, false);\n\tif (ent->skinobject > 0)\n\t{\t//add a ref, so it doesn't get forgotten so fast.\n\t\tsk = Mod_LookupSkin(ent->skinobject);\n\t\tif (sk)\n\t\t\tsk->refcount++;\n\t}\n}\n\nstatic void QCBUILTIN PF_ReadByte(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tif (!csqc_mayread)\n\t{\n\t\tCSQC_Abort(\"PF_ReadByte is not valid at this time\");\n\t\tG_FLOAT(OFS_RETURN) = -1;\n\t\treturn;\n\t}\n\tG_FLOAT(OFS_RETURN) = MSG_ReadByte();\n}\n\nstatic void QCBUILTIN PF_ReadChar(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tif (!csqc_mayread)\n\t{\n\t\tCSQC_Abort(\"PF_ReadChar is not valid at this time\");\n\t\tG_FLOAT(OFS_RETURN) = -1;\n\t\treturn;\n\t}\n\tG_FLOAT(OFS_RETURN) = MSG_ReadChar();\n}\n\nstatic void QCBUILTIN PF_ReadShort(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tif (!csqc_mayread)\n\t{\n\t\tCSQC_Abort(\"PF_ReadShort is not valid at this time\");\n\t\tG_FLOAT(OFS_RETURN) = -1;\n\t\treturn;\n\t}\n\tG_FLOAT(OFS_RETURN) = MSG_ReadShort();\n}\n\nstatic void QCBUILTIN PF_ReadEntityNum(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tunsigned int val;\n\tif (!csqc_mayread)\n\t{\n\t\tCSQC_Abort(\"PF_ReadEntityNum is not valid at this time\");\n\t\tG_FLOAT(OFS_RETURN) = -1;\n\t\treturn;\n\t}\n\tval = MSGCL_ReadEntity();\n\tG_FLOAT(OFS_RETURN) = val;\n}\n\nstatic void QCBUILTIN PF_ReadLong(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tif (!csqc_mayread)\n\t{\n\t\tCSQC_Abort(\"PF_ReadLong is not valid at this time\");\n\t\tG_FLOAT(OFS_RETURN) = -1;\n\t\treturn;\n\t}\n\tG_FLOAT(OFS_RETURN) = MSG_ReadLong();\n}\n\nstatic void QCBUILTIN PF_ReadCoord(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tif (!csqc_mayread)\n\t{\n\t\tCSQC_Abort(\"PF_ReadCoord is not valid at this time\");\n\t\tG_FLOAT(OFS_RETURN) = -1;\n\t\treturn;\n\t}\n\tG_FLOAT(OFS_RETURN) = MSG_ReadCoord();\n}\n\nstatic void QCBUILTIN PF_ReadFloat(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tif (!csqc_mayread)\n\t{\n\t\tCSQC_Abort(\"PF_ReadFloat is not valid at this time\");\n\t\tG_FLOAT(OFS_RETURN) = -1;\n\t\treturn;\n\t}\n\tG_FLOAT(OFS_RETURN) = MSG_ReadFloat();\n}\nstatic void QCBUILTIN PF_ReadDouble(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tif (!csqc_mayread)\n\t{\n\t\tCSQC_Abort(\"PF_ReadDouble is not valid at this time\");\n\t\tG_FLOAT(OFS_RETURN) = -1;\n\t\treturn;\n\t}\n\tG_DOUBLE(OFS_RETURN) = MSG_ReadDouble();\n}\nstatic void QCBUILTIN PF_ReadInt(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tif (!csqc_mayread)\n\t{\n\t\tCSQC_Abort(\"PF_ReadInt is not valid at this time\");\n\t\tG_INT(OFS_RETURN) = -1;\n\t\treturn;\n\t}\n\tG_INT(OFS_RETURN) = MSG_ReadLong();\n}\nstatic void QCBUILTIN PF_ReadInt64(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tif (!csqc_mayread)\n\t{\n\t\tCSQC_Abort(\"PF_ReadInt64 is not valid at this time\");\n\t\tG_INT(OFS_RETURN) = -1;\n\t\treturn;\n\t}\n\tG_INT64(OFS_RETURN) = MSG_ReadInt64();\n}\nstatic void QCBUILTIN PF_ReadUInt64(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tif (!csqc_mayread)\n\t{\n\t\tCSQC_Abort(\"PF_ReadUInt64 is not valid at this time\");\n\t\tG_INT(OFS_RETURN) = -1;\n\t\treturn;\n\t}\n\tG_INT64(OFS_RETURN) = MSG_ReadUInt64();\n}\n\nstatic void QCBUILTIN PF_ReadString(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tchar *read;\n\tif (!csqc_mayread)\n\t{\n\t\tCSQC_Abort(\"PF_ReadString is not valid at this time\");\n\t\tG_INT(OFS_RETURN) = 0;\n\t\treturn;\n\t}\n\tread = MSG_ReadString();\n\tRETURN_TSTRING(read);\n}\n\nstatic void QCBUILTIN PF_ReadAngle(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tif (!csqc_mayread)\n\t{\n\t\tCSQC_Abort(\"PF_ReadAngle is not valid at this time\");\n\t\tG_FLOAT(OFS_RETURN) = -1;\n\t\treturn;\n\t}\n\tG_FLOAT(OFS_RETURN) = MSG_ReadAngle();\n}\n\n//basically acts as a readstring, but with added precache (and download)\nstatic void QCBUILTIN PF_ReadPicture(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tchar *imagename;\n\tunsigned short size;\n\tif (!csqc_mayread)\n\t{\n\t\tCSQC_Abort(\"PF_ReadPicture is not valid at this time\");\n\t\tG_INT(OFS_RETURN) = 0;\n\t\treturn;\n\t}\n\timagename = MSG_ReadString();\n\tsize = MSG_ReadShort();\n\tMSG_ReadSkip(size);\n\n\t//do the precache+download thing\n\t{\n\t\tshader_t *pic = R2D_SafeCachePic(imagename);\n\t\tchar ext[8];\n\n\t\t//fixme: probably shouldn't block here.\n\t\tif ((!pic || !R_GetShaderSizes(pic, NULL, NULL, true)) && cls.state\n#ifndef CLIENTONLY\n\t\t\t&& !sv.active\n#endif\n\t\t\t&& *COM_FileExtension(imagename, ext, sizeof(ext)))\t//only try to download it if it looks as though it contains a path.\n\t\t\tCL_CheckOrEnqueDownloadFile(imagename, imagename, 0);\n\t}\n\n\tRETURN_TSTRING(imagename);\n}\n\n\nstatic void QCBUILTIN PF_objerror (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char\t*s;\n\tstruct edict_s\t*ed;\n\n\ts = PF_VarString(prinst, 0, pr_globals);\n/*\tCon_Printf (\"======OBJECT ERROR in %s:\\n%s\\n\", PR_GetString(pr_xfunction->s_name),s);\n*/\ted = PROG_TO_EDICT(prinst, *csqcg.self);\n/*\tED_Print (ed);\n*/\n\tprinst->ED_Print(prinst, ed);\n\tCon_Printf(\"%s\", s);\n\n\tif (developer.value)\n\t\tprinst->debug_trace = 2;\n\telse\n\t{\n\t\tED_Free (prinst, ed);\n\n\t\tprinst->AbortStack(prinst);\n\n\t\tPR_BIError (prinst, \"Program error: %s\", s);\n\t}\n}\n\nstatic void QCBUILTIN PF_cs_setsensitivityscaler (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tin_sensitivityscale = G_FLOAT(OFS_PARM0);\n}\n\nstatic void QCBUILTIN PF_cs_boxparticles(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint effectnum = CL_TranslateParticleFromServer(G_FLOAT(OFS_PARM0));\n//\tcsqcedict_t *ent\t= (csqcedict_t*)G_EDICT(prinst, OFS_PARM1);\n\tfloat *org_from\t\t= G_VECTOR(OFS_PARM2);\n\tfloat *org_to\t\t= G_VECTOR(OFS_PARM3);\n\tfloat *vel_from\t\t= G_VECTOR(OFS_PARM4);\n\tfloat *vel_to\t\t= G_VECTOR(OFS_PARM5);\n\tfloat count\t\t\t= G_FLOAT(OFS_PARM6);\n\tint flags\t\t\t= (prinst->callargc < 7)?0:G_FLOAT(OFS_PARM7);\n\n/*\tif (flags & 1)\t//PARTICLES_USEALPHA\n\t{\n\t\tfloat *alphamin = (float*)PR_FindGlobal(csqcprogs, \"particles_alphamin\", 0, NULL);\n\t\tfloat *alphamax = (float*)PR_FindGlobal(csqcprogs, \"particles_alphamax\", 0, NULL);\n\t\tif (alphamin && alphamax)\n\t\t\t;\n\t}\n\tif (flags & 2)\t//PARTICLES_USECOLOR\n\t{\t//rgb vectors\n\t\tfloat *colourmin = (float*)PR_FindGlobal(csqcprogs, \"particles_colormin\", 0, NULL);\n\t\tfloat *colourmax = (float*)PR_FindGlobal(csqcprogs, \"particles_colormax\", 0, NULL);\n\t\tif (colourmin && colourmax)\n\t\t\t;\n\t}\n\tif (flags & 4)\t//PARTICLES_USEFADE\n\t{\n\t\tfloat *fade = (float*)PR_FindGlobal(csqcprogs, \"particles_fade\", 0, NULL);\n\t\tif (fade)\n\t\t\t;\n\t}\n*/\n\n\tif (flags & 128)\t//PARTICLES_DRAWASTRAIL\n\t{\n\t\tflags &= ~128;\n\t\tP_ParticleTrail(org_from, org_to, effectnum, 1, 0, NULL, NULL);\n\t}\n\telse\n\t{\n\t\tP_RunParticleCube(effectnum, org_from, org_to, vel_from, vel_to, count, 0, true, 0);\n\t}\n\n\tif (flags & ~128)\n\t{\n\t\tstatic float throttletimer;\n\t\tCon_ThrottlePrintf(&throttletimer, 1, \"PF_cs_boxparticles: flags & %x is not supported\\n\", flags);\n\t}\n}\n\nstatic void QCBUILTIN PF_cs_pointparticles (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint effectnum = G_FLOAT(OFS_PARM0);\n\tfloat *org = G_VECTOR(OFS_PARM1);\n\tfloat *vel = G_VECTOR(OFS_PARM2);\n\tfloat count = G_FLOAT(OFS_PARM3);\n\n\tif (prinst->callargc < 3)\n\t\tvel = vec3_origin;\n\tif (prinst->callargc < 4)\n\t\tcount = 1;\n\n\teffectnum = CL_TranslateParticleFromServer(effectnum);\n\tP_RunParticleEffectType(org, vel, count, effectnum);\n}\n\nstatic void QCBUILTIN PF_cs_trailparticles (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint efnum;\n\tcsqcedict_t *ent;\n\tfloat *start = G_VECTOR(OFS_PARM2);\n\tfloat *end = G_VECTOR(OFS_PARM3);\n\tfloat timestep = host_frametime;\n\n\tif ((unsigned int)G_INT(OFS_PARM1) >= MAX_EDICTS)\n\t{\t//ents can't be negative, nor can they be huge (like floats are if expressed as an integer)\n\t\tefnum = G_FLOAT(OFS_PARM1);\n\t\tent = (csqcedict_t*)G_EDICT(prinst, OFS_PARM0);\n\t}\n\telse\n\t{\n\t\tefnum = G_FLOAT(OFS_PARM0);\n\t\tent = (csqcedict_t*)G_EDICT(prinst, OFS_PARM1);\n\t}\n\tefnum = CL_TranslateParticleFromServer(efnum);\n\n\tif (!ent->entnum)\t//world trails are non-state-based.\n\t\tpe->ParticleTrail(start, end, efnum, timestep, 0, NULL, NULL);\n\telse\n\t\tpe->ParticleTrail(start, end, efnum, timestep, -ent->entnum, NULL, &ent->trailstate);\n}\n\nvoid CSQC_ResetTrails(void)\n{\n\tpubprogfuncs_t *prinst = csqc_world.progs;\n\tint i;\n\tcsqcedict_t\t*ent;\n\n\tif (!prinst)\n\t\treturn;\n\n\tfor (i = 0; i < *prinst->parms->num_edicts; i++)\n\t{\n\t\tent = (csqcedict_t*)EDICT_NUM_PB(prinst, i);\n\t\tent->trailstate = trailkey_null;\n\t}\n}\n\n//0 for error, non-0 for success.\n//>0 match server\n//<0 are client-only.\nstatic void QCBUILTIN PF_cs_particleeffectnum (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint i;\n\tconst char *effectname = PR_GetStringOfs(prinst, OFS_PARM0);\n\n\tG_FLOAT(OFS_RETURN) = 0;\t//default to failure.\n\n\t//use the server's index first.\n\tfor (i = 1; i < MAX_SSPARTICLESPRE && cl.particle_ssname[i]; i++)\n\t{\n\t\tif (!strcmp(cl.particle_ssname[i], effectname))\n\t\t{\n\t\t\tG_FLOAT(OFS_RETURN) = i;\n\t\t\treturn;\n\t\t}\n\t}\n\n\t//then look for an existing client id\n\tfor (i = 1; i < MAX_CSPARTICLESPRE && cl.particle_csname[i]; i++)\n\t{\n\t\tif (!strcmp(cl.particle_csname[i], effectname))\n\t\t{\n\t\t\t//effects can be in the list despite now being stale. they still take up a slot to avoid reuse as the qc can potentially still potentially reference it.\n\t\t\t//csqc needs to be able to detect a now-stale effect\n\t\t\t//if (cl.particle_csprecache[i] != P_INVALID)\n\t\t\t\tG_FLOAT(OFS_RETURN) = -i;\n\t\t\treturn;\n\t\t}\n\t}\n\t//create if new\n\tif (i < MAX_CSPARTICLESPRE)\n\t{\n\t\tfree(cl.particle_csname[i]);\n\t\tcl.particle_csname[i] = NULL;\n\t\tcl.particle_csprecache[i] = P_FindParticleType(effectname);\n\t\t//if (cl.particle_csprecache[i] != P_INVALID)\n\t\t{\n\t\t\t//it exists, allow it.\n\t\t\tcl.particle_csname[i] = strdup(effectname);\n\t\t\tG_FLOAT(OFS_RETURN) = -i;\n\t\t}\n\t\tcl.particle_csprecaches = true;\n\t}\n}\n\nstatic void QCBUILTIN PF_cs_particleeffectquery (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint id = G_FLOAT(OFS_PARM0);\n\tqboolean body = G_FLOAT(OFS_PARM1);\n\tchar retstr[8192];\n\n\tid = CL_TranslateParticleFromServer(id);\n\n\tif (pe->ParticleQuery && pe->ParticleQuery(id, body, retstr, sizeof(retstr)))\n\t{\n\t\tRETURN_TSTRING(retstr);\n\t}\n\telse\n\t\tG_INT(OFS_RETURN) = 0;\n}\n\nstatic void QCBUILTIN PF_cs_sendevent (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tcsqcedict_t *ent;\n\tint i;\n\tconst char *eventname = PR_GetStringOfs(prinst, OFS_PARM0);\n\tconst char *argtypes = PR_GetStringOfs(prinst, OFS_PARM1);\n\n\tif (!cls.state)\n\t\treturn;\n\n\tMSG_WriteByte(&cls.netchan.message, clcfte_qcrequest);\n\n\tfor (i = 0; i < 6; i++)\n\t{\n\t\tif (argtypes[i] == 's')\n\t\t{\n\t\t\tMSG_WriteByte(&cls.netchan.message, ev_string);\n\t\t\tMSG_WriteString(&cls.netchan.message, PR_GetStringOfs(prinst, OFS_PARM2+i*3));\n\t\t}\n\t\telse if (argtypes[i] == 'p' && i>0)\n\t\t{\n\t\t\tunsigned int len = G_UINT(OFS_PARM1+i*3);\n\t\t\tunsigned int qcptr = G_UINT(OFS_PARM2+i*3);\n\t\t\tchar *p = prinst->stringtable + qcptr;\n\t\t\tif (len >= prinst->stringtablesize || qcptr > prinst->stringtablesize-len)\n\t\t\t\tbreak;\n\t\t\tMSG_WriteByte(&cls.netchan.message, ev_pointer);\n\t\t\tSZ_Write(&cls.netchan.message, p, len);\n\t\t}\n\t\telse if (argtypes[i] == 'f')\n\t\t{\n\t\t\tMSG_WriteByte(&cls.netchan.message, ev_float);\n\t\t\tMSG_WriteFloat(&cls.netchan.message, G_FLOAT(OFS_PARM2+i*3));\n\t\t}\n\t\telse if (argtypes[i] == 'F')\n\t\t{\n\t\t\tMSG_WriteByte(&cls.netchan.message, ev_double);\n\t\t\tMSG_WriteDouble(&cls.netchan.message, G_DOUBLE(OFS_PARM2+i*3));\n\t\t}\n\t\telse if (argtypes[i] == 'i')\n\t\t{\n\t\t\tMSG_WriteByte(&cls.netchan.message, ev_integer);\n\t\t\tMSG_WriteLong(&cls.netchan.message, G_INT(OFS_PARM2+i*3));\n\t\t}\n\t\telse if (argtypes[i] == 'u')\n\t\t{\n\t\t\tMSG_WriteByte(&cls.netchan.message, ev_uint);\n\t\t\tMSG_WriteLong(&cls.netchan.message, G_UINT(OFS_PARM2+i*3));\n\t\t}\n\t\telse if (argtypes[i] == 'I')\n\t\t{\n\t\t\tMSG_WriteByte(&cls.netchan.message, ev_int64);\n\t\t\tMSG_WriteInt64(&cls.netchan.message, G_INT64(OFS_PARM2+i*3));\n\t\t}\n\t\telse if (argtypes[i] == 'U')\n\t\t{\n\t\t\tMSG_WriteByte(&cls.netchan.message, ev_uint64);\n\t\t\tMSG_WriteUInt64(&cls.netchan.message, G_UINT64(OFS_PARM2+i*3));\n\t\t}\n\t\telse if (argtypes[i] == 'v')\n\t\t{\n\t\t\tMSG_WriteByte(&cls.netchan.message, ev_vector);\n\t\t\tMSG_WriteFloat(&cls.netchan.message, G_FLOAT(OFS_PARM2+i*3+0));\n\t\t\tMSG_WriteFloat(&cls.netchan.message, G_FLOAT(OFS_PARM2+i*3+1));\n\t\t\tMSG_WriteFloat(&cls.netchan.message, G_FLOAT(OFS_PARM2+i*3+2));\n\t\t}\n\t\telse if (argtypes[i] == 'e')\n\t\t{\n\t\t\tent = (csqcedict_t*)G_EDICT(prinst, OFS_PARM2+i*3);\n\t\t\tMSG_WriteByte(&cls.netchan.message, ev_entity);\n\t\t\tMSG_WriteEntity(&cls.netchan.message, ent->xv->entnum);\n\t\t}\n\t\telse\n\t\t\tbreak;\n\t}\n\tif (csqc_playerseat > 0)\n\t\tMSG_WriteByte(&cls.netchan.message, 200+csqc_playerseat);\n\tMSG_WriteByte(&cls.netchan.message, 0);\n\tMSG_WriteString(&cls.netchan.message, eventname);\n}\n\nstatic void cs_set_input_state (usercmd_t *cmd)\n{\n\tif (csqcg.input_sequence)\n\t\t*csqcg.input_sequence = cmd->sequence;\n\tif (csqcg.input_timelength)\n\t\t*csqcg.input_timelength = cmd->msec/1000.0f * cl.gamespeed;\n\tif (csqcg.input_angles)\n\t{\n\t\tcsqcg.input_angles[0] = SHORT2ANGLE(cmd->angles[0]);\n\t\tcsqcg.input_angles[1] = SHORT2ANGLE(cmd->angles[1]);\n\t\tcsqcg.input_angles[2] = SHORT2ANGLE(cmd->angles[2]);\n\t}\n\tif (csqcg.input_movevalues)\n\t{\n\t\tcsqcg.input_movevalues[0] = cmd->forwardmove;\n\t\tcsqcg.input_movevalues[1] = cmd->sidemove;\n\t\tcsqcg.input_movevalues[2] = cmd->upmove;\n\t}\n\tif (csqcg.input_buttons)\n\t\t*csqcg.input_buttons = cmd->buttons;\n\n\tif (csqcg.input_impulse)\n\t\t*csqcg.input_impulse = cmd->impulse;\n\tif (csqcg.input_lightlevel)\n\t\t*csqcg.input_lightlevel = cmd->lightlevel;\n\tif (csqcg.input_weapon)\n\t\t*csqcg.input_weapon = cmd->weapon;\n\tif (csqcg.input_servertime)\n\t\t*csqcg.input_servertime = cmd->fservertime;\n\tif (csqcg.input_clienttime)\n\t\t*csqcg.input_clienttime = cmd->fclienttime;\n\n\tif (csqcg.input_cursor_screen)\n\t{\n\t\tVector2Copy(cmd->cursor_screen, csqcg.input_cursor_screen);\n\t\tcsqcg.input_cursor_screen[2] = 0;\n\t}\n\tif (csqcg.input_cursor_trace_start)\n\t\tVectorCopy(cmd->cursor_start, csqcg.input_cursor_trace_start);\n\tif (csqcg.input_cursor_trace_endpos)\n\t\tVectorCopy(cmd->cursor_impact, csqcg.input_cursor_trace_endpos);\n\tif (csqcg.input_cursor_entitynumber)\n\t\t*csqcg.input_cursor_entitynumber = cmd->cursor_entitynumber;\n\n\n\tif (csqcg.input_head_status)\n\t\t*csqcg.input_head_status = cmd->vr[VRDEV_HEAD].status;\n\tif (csqcg.input_head_origin)\n\t\tVectorCopy(cmd->vr[VRDEV_HEAD].origin, csqcg.input_head_origin);\n\tif (csqcg.input_head_velocity)\n\t\tVectorCopy(cmd->vr[VRDEV_HEAD].velocity, csqcg.input_head_velocity);\n\tif (csqcg.input_head_angles)\n\t{\n\t\tcsqcg.input_head_angles[0] = SHORT2ANGLE(cmd->vr[VRDEV_HEAD].angles[0]);\n\t\tcsqcg.input_head_angles[1] = SHORT2ANGLE(cmd->vr[VRDEV_HEAD].angles[1]);\n\t\tcsqcg.input_head_angles[2] = SHORT2ANGLE(cmd->vr[VRDEV_HEAD].angles[2]);\n\t}\n\tif (csqcg.input_head_avelocity)\n\t{\n\t\tcsqcg.input_head_avelocity[0] = SHORT2ANGLE(cmd->vr[VRDEV_HEAD].avelocity[0]);\n\t\tcsqcg.input_head_avelocity[1] = SHORT2ANGLE(cmd->vr[VRDEV_HEAD].avelocity[1]);\n\t\tcsqcg.input_head_avelocity[2] = SHORT2ANGLE(cmd->vr[VRDEV_HEAD].avelocity[2]);\n\t}\n\tif (csqcg.input_head_weapon)\n\t\t*csqcg.input_head_weapon = cmd->vr[VRDEV_HEAD].weapon;\n\n\tif (csqcg.input_left_status)\n\t\t*csqcg.input_left_status = cmd->vr[VRDEV_LEFT].status;\n\tif (csqcg.input_left_origin)\n\t\tVectorCopy(cmd->vr[VRDEV_LEFT].origin, csqcg.input_left_origin);\n\tif (csqcg.input_left_velocity)\n\t\tVectorCopy(cmd->vr[VRDEV_LEFT].velocity, csqcg.input_left_velocity);\n\tif (csqcg.input_left_angles)\n\t{\n\t\tcsqcg.input_left_angles[0] = SHORT2ANGLE(cmd->vr[VRDEV_LEFT].angles[0]);\n\t\tcsqcg.input_left_angles[1] = SHORT2ANGLE(cmd->vr[VRDEV_LEFT].angles[1]);\n\t\tcsqcg.input_left_angles[2] = SHORT2ANGLE(cmd->vr[VRDEV_LEFT].angles[2]);\n\t}\n\tif (csqcg.input_left_avelocity)\n\t{\n\t\tcsqcg.input_left_avelocity[0] = SHORT2ANGLE(cmd->vr[VRDEV_LEFT].avelocity[0]);\n\t\tcsqcg.input_left_avelocity[1] = SHORT2ANGLE(cmd->vr[VRDEV_LEFT].avelocity[1]);\n\t\tcsqcg.input_left_avelocity[2] = SHORT2ANGLE(cmd->vr[VRDEV_LEFT].avelocity[2]);\n\t}\n\n\tif (csqcg.input_right_status)\n\t\t*csqcg.input_right_status = cmd->vr[VRDEV_RIGHT].status;\n\tif (csqcg.input_right_origin)\n\t\tVectorCopy(cmd->vr[VRDEV_RIGHT].origin, csqcg.input_right_origin);\n\tif (csqcg.input_right_velocity)\n\t\tVectorCopy(cmd->vr[VRDEV_RIGHT].velocity, csqcg.input_right_velocity);\n\tif (csqcg.input_right_angles)\n\t{\n\t\tcsqcg.input_right_angles[0] = SHORT2ANGLE(cmd->vr[VRDEV_RIGHT].angles[0]);\n\t\tcsqcg.input_right_angles[1] = SHORT2ANGLE(cmd->vr[VRDEV_RIGHT].angles[1]);\n\t\tcsqcg.input_right_angles[2] = SHORT2ANGLE(cmd->vr[VRDEV_RIGHT].angles[2]);\n\t}\n\tif (csqcg.input_right_avelocity)\n\t{\n\t\tcsqcg.input_right_avelocity[0] = SHORT2ANGLE(cmd->vr[VRDEV_RIGHT].avelocity[0]);\n\t\tcsqcg.input_right_avelocity[1] = SHORT2ANGLE(cmd->vr[VRDEV_RIGHT].avelocity[1]);\n\t\tcsqcg.input_right_avelocity[2] = SHORT2ANGLE(cmd->vr[VRDEV_RIGHT].avelocity[2]);\n\t}\n}\n\nstatic void cs_get_input_state (usercmd_t *cmd)\n{\n//\tif (csqcg.input_sequence)\n//\t\tcmd->sequence = *csqcg.input_sequence;\n\tif (csqcg.input_timelength)\n\t\tcmd->msec = *csqcg.input_timelength*1000;\n\tif (csqcg.input_angles)\n\t{\n\t\tcmd->angles[0] = ANGLE2SHORT(csqcg.input_angles[0]);\n\t\tcmd->angles[1] = ANGLE2SHORT(csqcg.input_angles[1]);\n\t\tcmd->angles[2] = ANGLE2SHORT(csqcg.input_angles[2]);\n\t}\n\tif (csqcg.input_movevalues)\n\t{\n\t\tcmd->forwardmove = csqcg.input_movevalues[0];\n\t\tcmd->sidemove = csqcg.input_movevalues[1];\n\t\tcmd->upmove = csqcg.input_movevalues[2];\n\t}\n\tif (csqcg.input_buttons)\n\t\tcmd->buttons = *csqcg.input_buttons;\n\n\tif (csqcg.input_impulse)\n\t\tcmd->impulse = *csqcg.input_impulse;\n\tif (csqcg.input_lightlevel)\n\t\tcmd->lightlevel = *csqcg.input_lightlevel;\n\tif (csqcg.input_weapon)\n\t\tcmd->weapon = *csqcg.input_weapon;\n\tif (csqcg.input_servertime)\n\t{\n\t\tcmd->fservertime = *csqcg.input_servertime;\n\t\tcmd->servertime = *csqcg.input_servertime*1000;\n\t}\n\tif (csqcg.input_clienttime)\n\t\tcmd->fclienttime = *csqcg.input_clienttime;\n\n\tif (csqcg.input_cursor_screen)\n\t\tVector2Copy(csqcg.input_cursor_screen, cmd->cursor_screen);\n\tif (csqcg.input_cursor_trace_start)\n\t\tVectorCopy(csqcg.input_cursor_trace_start, cmd->cursor_start);\n\tif (csqcg.input_cursor_trace_endpos)\n\t\tVectorCopy(csqcg.input_cursor_trace_endpos, cmd->cursor_impact);\n\tif (csqcg.input_cursor_entitynumber)\n\t\tcmd->cursor_entitynumber = *csqcg.input_cursor_entitynumber;\n\n\tif (csqcg.input_head_status)\n\t\tcmd->vr[VRDEV_HEAD].status = *csqcg.input_head_status;\n\tif (csqcg.input_head_origin)\n\t\tVectorCopy(csqcg.input_head_origin, cmd->vr[VRDEV_HEAD].origin);\n\tif (csqcg.input_head_velocity)\n\t\tVectorCopy(csqcg.input_head_velocity, cmd->vr[VRDEV_HEAD].velocity);\n\tif (csqcg.input_head_angles)\n\t{\n\t\tcmd->vr[VRDEV_HEAD].angles[0] = ANGLE2SHORT(csqcg.input_head_angles[0]);\n\t\tcmd->vr[VRDEV_HEAD].angles[1] = ANGLE2SHORT(csqcg.input_head_angles[1]);\n\t\tcmd->vr[VRDEV_HEAD].angles[2] = ANGLE2SHORT(csqcg.input_head_angles[2]);\n\t}\n\tif (csqcg.input_head_avelocity)\n\t{\n\t\tcmd->vr[VRDEV_HEAD].avelocity[0] = ANGLE2SHORT(csqcg.input_head_avelocity[0]);\n\t\tcmd->vr[VRDEV_HEAD].avelocity[1] = ANGLE2SHORT(csqcg.input_head_avelocity[1]);\n\t\tcmd->vr[VRDEV_HEAD].avelocity[2] = ANGLE2SHORT(csqcg.input_head_avelocity[2]);\n\t}\n\tif (csqcg.input_head_weapon)\n\t\tcmd->vr[VRDEV_HEAD].weapon = *csqcg.input_head_weapon;\n\n\tif (csqcg.input_left_status)\n\t\tcmd->vr[VRDEV_LEFT].status = *csqcg.input_left_status;\n\tif (csqcg.input_left_origin)\n\t\tVectorCopy(csqcg.input_left_origin, cmd->vr[VRDEV_LEFT].origin);\n\tif (csqcg.input_left_velocity)\n\t\tVectorCopy(csqcg.input_left_velocity, cmd->vr[VRDEV_LEFT].velocity);\n\tif (csqcg.input_left_angles)\n\t{\n\t\tcmd->vr[VRDEV_LEFT].angles[0] = ANGLE2SHORT(csqcg.input_left_angles[0]);\n\t\tcmd->vr[VRDEV_LEFT].angles[1] = ANGLE2SHORT(csqcg.input_left_angles[1]);\n\t\tcmd->vr[VRDEV_LEFT].angles[2] = ANGLE2SHORT(csqcg.input_left_angles[2]);\n\t}\n\tif (csqcg.input_left_avelocity)\n\t{\n\t\tcmd->vr[VRDEV_LEFT].avelocity[0] = ANGLE2SHORT(csqcg.input_left_avelocity[0]);\n\t\tcmd->vr[VRDEV_LEFT].avelocity[1] = ANGLE2SHORT(csqcg.input_left_avelocity[1]);\n\t\tcmd->vr[VRDEV_LEFT].avelocity[2] = ANGLE2SHORT(csqcg.input_left_avelocity[2]);\n\t}\n\tif (csqcg.input_left_weapon)\n\t\tcmd->vr[VRDEV_LEFT].weapon = *csqcg.input_left_weapon;\n\n\tif (csqcg.input_right_status)\n\t\tcmd->vr[VRDEV_RIGHT].status = *csqcg.input_right_status;\n\tif (csqcg.input_right_origin)\n\t\tVectorCopy(csqcg.input_right_origin, cmd->vr[VRDEV_RIGHT].origin);\n\tif (csqcg.input_right_velocity)\n\t\tVectorCopy(csqcg.input_right_velocity, cmd->vr[VRDEV_RIGHT].velocity);\n\tif (csqcg.input_right_angles)\n\t{\n\t\tcmd->vr[VRDEV_RIGHT].angles[0] = ANGLE2SHORT(csqcg.input_right_angles[0]);\n\t\tcmd->vr[VRDEV_RIGHT].angles[1] = ANGLE2SHORT(csqcg.input_right_angles[1]);\n\t\tcmd->vr[VRDEV_RIGHT].angles[2] = ANGLE2SHORT(csqcg.input_right_angles[2]);\n\t}\n\tif (csqcg.input_right_avelocity)\n\t{\n\t\tcmd->vr[VRDEV_RIGHT].avelocity[0] = ANGLE2SHORT(csqcg.input_right_avelocity[0]);\n\t\tcmd->vr[VRDEV_RIGHT].avelocity[1] = ANGLE2SHORT(csqcg.input_right_avelocity[1]);\n\t\tcmd->vr[VRDEV_RIGHT].avelocity[2] = ANGLE2SHORT(csqcg.input_right_avelocity[2]);\n\t}\n\tif (csqcg.input_right_weapon)\n\t\tcmd->vr[VRDEV_RIGHT].weapon = *csqcg.input_right_weapon;\n}\n\n//sets implicit pause (only works when singleplayer)\nstatic void QCBUILTIN PF_cl_setpause (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tcl.implicitpause = !!G_FLOAT(OFS_PARM0);\n}\n\nstatic void QCBUILTIN PF_cl_RotateMoves (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\t//changes the past...\n\tfloat *angles = G_VECTOR(OFS_PARM0);\n\tint frame;\n\tvec3_t of,ou, nf,nu;\n\tvec4_t mat[3];\n\tvec3_t a;\n\tint seat = ((prinst->callargc>1)?G_FLOAT(OFS_PARM1):csqc_playerseat);\n\tif (seat < 0 || seat >= MAX_SPLITS)\n\t{\n\t\tG_FLOAT(OFS_RETURN) = false;\n\t\treturn;\n\t}\n\tAngleVectorsFLU(angles, mat[0], mat[1], mat[2]);\n\tmat[0][3] = mat[1][3] = mat[2][3] = 0;\n\tfor (frame = 0; frame < countof(cl.outframes); frame++)\n\t{\n\t\tif (cl.outframes[frame].cmd_sequence > cl.ackedmovesequence)\n\t\t{\n\t\t\ta[0] = SHORT2ANGLE(cl.outframes[frame].cmd[seat].angles[0]);\n\t\t\ta[1] = SHORT2ANGLE(cl.outframes[frame].cmd[seat].angles[1]);\n\t\t\ta[2] = SHORT2ANGLE(cl.outframes[frame].cmd[seat].angles[2]);\n\t\t\tAngleVectors(a, of, NULL, ou);\n\t\t\tVectorTransform(of, mat, nf);\n\t\t\tVectorTransform(ou, mat, nu);\n\t\t\tVectorAngles(nf, nu, a, false);\n\t\t\tcl.outframes[frame].cmd[seat].angles[0] = ANGLE2SHORT(a[0]);\n\t\t\tcl.outframes[frame].cmd[seat].angles[1] = ANGLE2SHORT(a[1]);\n\t\t\tcl.outframes[frame].cmd[seat].angles[2] = ANGLE2SHORT(a[2]);\n\t\t}\n\t}\n}\n//get the input commands, and stuff them into some globals.\nstatic void QCBUILTIN PF_cs_getinputstate (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tusercmd_t *cmd;\n\tusercmd_t tmp;\n\textern usercmd_t cl_pendingcmd[MAX_SPLITS];\n\tint f = G_FLOAT(OFS_PARM0);\n\tint seat = ((prinst->callargc>1)?G_FLOAT(OFS_PARM1):csqc_playerseat);\n\n\tif (seat < 0 || seat >= MAX_SPLITS)\n\t{\n\t\tG_FLOAT(OFS_RETURN) = false;\n\t\treturn;\n\t}\n\n\tf = G_FLOAT(OFS_PARM0);\n\tif (cl.paused && f >= cl.ackedmovesequence)\n\t{\n\t\tG_FLOAT(OFS_RETURN) = false;\n\t\treturn;\n\t}\n\n\t/*outgoing_sequence says how many packets have actually been sent, but there's an extra pending packet which has not been sent yet - be warned though, its data will change in the coming frames*/\n\tif (f == cl.movesequence)\n\t{\n\t\tint i;\n\t\tcmd = &cl_pendingcmd[seat];\n\n\t\ttmp = *cmd;\n\t\tcmd = &tmp;\n\t\tfor (i=0 ; i<3 ; i++)\n\t\t\tcmd->angles[i] = ((int)(csqc_playerview->viewangles[i]*65536.0/360)&65535);\n\t\tif (!cmd->msec)\n\t\t\t*cmd = cl.outframes[(f-1)&UPDATE_MASK].cmd[seat];\n\t\tcmd->msec = (realtime - cl.outframes[(f-1)&UPDATE_MASK].senttime)*1000;\n\t\tcmd->sequence = f;\n\n\t\t//make sure we have the latest info...\n\t\tcmd->vr[VRDEV_LEFT] = csqc_playerview->vrdev[VRDEV_LEFT];\n\t\tcmd->vr[VRDEV_RIGHT] = csqc_playerview->vrdev[VRDEV_RIGHT];\n\t\tcmd->vr[VRDEV_HEAD] = csqc_playerview->vrdev[VRDEV_HEAD];\n\t}\n\telse\n\t{\n\t\tif (cl.outframes[f&UPDATE_MASK].cmd_sequence != f)\n\t\t{\n\t\t\tG_FLOAT(OFS_RETURN) = false;\n\t\t\treturn;\n\t\t}\n\t\tcmd = &cl.outframes[f&UPDATE_MASK].cmd[seat];\n\t}\n\n\tcs_set_input_state(cmd);\n\n\tG_FLOAT(OFS_RETURN) = true;\n}\n\n//read lots of globals, run the default player physics, write lots of globals.\n//not intended to affect client state at all\nstatic void QCBUILTIN PF_cs_runplayerphysics (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tfloat msecs;\n\tfloat oldtime = *csqcg.time;\n\n\tcsqcedict_t *ent = (void*)G_EDICT(prinst, OFS_PARM0);\n\tint mt = ent->v->movetype;\n\tif (prinst->callargc < 1)\n\t{\n\t\tcsqc_deprecated(\"runplayerphysics with no ent\");\n\t\treturn;\n\t}\n\tif (ent->readonly)\n\t{\n\t\tcsqc_deprecated(\"runplayerphysics called on read-only entity\");\n\t\treturn;\n\t}\n\n\tif (!csqc_world.worldmodel)\n\t\treturn;\t//urm..\n\n\tVALGRIND_MAKE_MEM_UNDEFINED(&pmove, sizeof(pmove));\n\n\t//debugging field\n\n\tpmove.jump_msec = 0;//(cls.z_ext & Z_EXT_PM_TYPE) ? 0 : from->jump_msec;\n\n//set up the movement command\n\tmsecs = *csqcg.input_timelength*1000;\n\t//precision inaccuracies. :(\n\tpmove.cmd.angles[0] = ANGLE2SHORT(csqcg.input_angles[0]);\n\tpmove.cmd.angles[1] = ANGLE2SHORT(csqcg.input_angles[1]);\n\tpmove.cmd.angles[2] = ANGLE2SHORT(csqcg.input_angles[2]);\n\tVectorCopy(csqcg.input_angles, pmove.angles);\n\n\tpmove.cmd.sequence = *csqcg.clientcommandframe;\n\tpmove.cmd.forwardmove = csqcg.input_movevalues[0];\n\tpmove.cmd.sidemove = csqcg.input_movevalues[1];\n\tpmove.cmd.upmove = csqcg.input_movevalues[2];\n\tpmove.cmd.buttons = *csqcg.input_buttons;\n\tpmove.onladder = false;\n\tpmove.safeorigin_known = false;\n\tpmove.capsule = false;\t//FIXME\n\n\tmovevars.bunnyspeedcap = cl.bunnyspeedcap;\n\tmovevars.coordtype = cls.netchan.message.prim.coordtype;\n\tif (csqc_playerseat >= 0 && cl.playerview[csqc_playerseat].playernum+1 == ent->xv->entnum)\n\t{\n\t\tmovevars.entgravity = cl.playerview[csqc_playerseat].entgravity;\n\t\tmovevars.maxspeed = cl.playerview[csqc_playerseat].maxspeed;\n\t}\n\telse\n\t{\n\t\tmovevars.entgravity = 1;\n\t\tmovevars.maxspeed = cl.playerview[0].maxspeed;\n\t}\n\tif (ent->xv->gravity)\n\t\tmovevars.entgravity = ent->xv->gravity;\n\n\tif (ent->xv->entnum)\n\t\tpmove.skipent = ent->xv->entnum;\n\telse\n\t\tpmove.skipent = -1;\n\tmt &= 255;\n\tswitch(mt)\n\t{\n\tdefault:\n\tcase MOVETYPE_WALK:\n\t\tpmove.pm_type = PM_NORMAL;\n\t\tbreak;\n\tcase MOVETYPE_NOCLIP:\n\t\tpmove.pm_type = PM_SPECTATOR;\n\t\tbreak;\n\tcase MOVETYPE_FLY_WORLDONLY:\n\tcase MOVETYPE_FLY:\n\t\tpmove.pm_type = PM_FLY;\n\t\tbreak;\n\t}\n\tpmove.jump_held = (int)ent->xv->pmove_flags & PMF_JUMP_HELD;\n\tpmove.waterjumptime = 0;\n\tpmove.onground = !!((int)ent->v->flags & FL_ONGROUND);\n\tVectorCopy(ent->v->origin, pmove.origin);\n\tVectorCopy(ent->v->velocity, pmove.velocity);\n\tVectorCopy(ent->v->maxs, pmove.player_maxs);\n\tVectorCopy(ent->v->mins, pmove.player_mins);\n\tVectorCopy(ent->xv->gravitydir, pmove.gravitydir);\n\n\tCL_SetSolidEntities();\n\n\twhile(msecs > 0)\t//break up longer commands\n\t{\n\t\tpmove.cmd.msec = msecs;\n\t\tif (pmove.cmd.msec > 50)\n\t\t\tpmove.cmd.msec = 50;\n\t\tmsecs -= pmove.cmd.msec;\n\t\tPM_PlayerMove(1);\n\t}\n\n\tVectorCopy(pmove.angles, ent->v->angles);\n\tent->v->angles[0] *= r_meshpitch.value * 1/3.0f;\t//FIXME\n\tVectorCopy(pmove.origin, ent->v->origin);\n\tVectorCopy(pmove.velocity, ent->v->velocity);\n\tif (pmove.onground)\n\t\tent->v->flags = (int)ent->v->flags | FL_ONGROUND;\n\telse\n\t\tent->v->flags = (int)ent->v->flags & ~FL_ONGROUND;\n\tent->xv->pmove_flags = 0;\n\tent->xv->pmove_flags += pmove.jump_held ? PMF_JUMP_HELD : 0;\n\tent->xv->pmove_flags += pmove.onladder ? PMF_LADDER : 0;\n\n\t//fixme: touch triggers?\n\tWorld_LinkEdict (&csqc_world, (wedict_t*)ent, true);\n\n\t*csqcg.time = oldtime;\n}\n\nstatic void QCBUILTIN PF_cs_getentitytoken (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tif (prinst->callargc)\n\t{\n\t\tconst char *s = PR_GetStringOfs(prinst, OFS_PARM0);\n\t\tif (*s == 0 && csqc_world.worldmodel)\n\t\t{\n\t\t\tif (csqc_world.worldmodel->loadstate == MLS_LOADING)\n\t\t\t\tCOM_WorkerPartialSync(csqc_world.worldmodel, &csqc_world.worldmodel->loadstate, MLS_LOADING);\n\t\t\ts = Mod_GetEntitiesString(csqc_world.worldmodel);\n\t\t}\n\t\tcsqcmapentitydata = s;\n\t\tG_INT(OFS_RETURN) = 0;\n\t\treturn;\n\t}\n\n\tif (!csqcmapentitydata)\n\t{\n\t\t//nothing more to parse\n\t\tG_INT(OFS_RETURN) = 0;\n\t}\n\telse\n\t{\n\t\tchar *QCC_COM_Parse (const char *data);\n\t\textern char qcc_token[];\n\t\tcsqcmapentitydata = QCC_COM_Parse(csqcmapentitydata);\n\n\t\tif (!csqcmapentitydata)\t//hit the end\n\t\t\tG_INT(OFS_RETURN) = 0;\n\t\telse\n\t\t\tRETURN_TSTRING(qcc_token);\n\t}\n}\n\nstatic void CheckSendPings(void)\n{\t//quakeworld sends a 'pings' client command to retrieve the frequently updating stuff\n\tif (realtime - cl.last_ping_request > 2)\n\t{\n\t\tcl.last_ping_request = realtime;\n\t\tCL_SendClientCommand(true, \"pings\");\n\t}\n}\n\nstatic const char *PF_cs_getplayerkey_internal (unsigned int pnum, const char *keyname)\n{\n\tstatic char buffer[64];\n\tchar *ret;\n\n\tif ((unsigned int)pnum >= (unsigned int)cl.allocated_client_slots)\n\t\tret = \"\";\n\telse if (!strcmp(keyname, \"viewentity\"))\t//compat with DP. Yes, I know this is in the wrong place. This is an evil hack.\n\t{\n\t\tret = buffer;\n\t\tsprintf(ret, \"%i\", pnum+1);\n\t}\n\telse if (!*cl.players[pnum].name)\n\t\tret = \"\";\t//player isn't on the server.\n\telse if (!strcmp(keyname, \"ping\"))\n\t{\n\t\tCheckSendPings();\n\n\t\tret = buffer;\n\t\tsprintf(ret, \"%i\", cl.players[pnum].ping);\n\t}\n\telse if (!strcmp(keyname, \"frags\"))\n\t{\n\t\tret = buffer;\n\t\tsprintf(ret, \"%i\", cl.players[pnum].frags);\n\t}\n\telse if (!strcmp(keyname, \"userid\"))\n\t{\n\t\tret = buffer;\n\t\tsprintf(ret, \"%i\", cl.players[pnum].userid);\n\t}\n\telse if (!strcmp(keyname, \"pl\"))\t//packet loss\n\t{\n\t\tCheckSendPings();\n\n\t\tret = buffer;\n\t\tsprintf(ret, \"%i\", cl.players[pnum].pl);\n\t}\n\telse if (!strcmp(keyname, \"activetime\"))\n\t{\n\t\tret = buffer;\n\t\tsprintf(ret, \"%f\", realtime - cl.players[pnum].realentertime);\n\t}\n//\telse if (!strcmp(keyname, \"entertime\"))\n//\t{\n//\t\tret = buffer;\n//\t\tsprintf(ret, \"%i\", (int)cl.players[pnum].entertime);\n//\t}\n\telse if (!strcmp(keyname, \"topcolor_rgb\"))\n\t{\n\t\tunsigned int col = cl.players[pnum].dtopcolor;\n\t\tret = buffer;\n\t\tif (col < 16)\n\t\t{\n\t\t\tcol = Sbar_ColorForMap(col);\n\t\t\tsprintf(ret, \"'%g %g %g'\", host_basepal[col*3+0]/255.0, host_basepal[col*3+1]/255.0, host_basepal[col*3+2]/255.0);\n\t\t}\n\t\telse\n\t\t\tsprintf(ret, \"'%g %g %g'\", ((col&0xff0000)>>16)/255.0, ((col&0x00ff00)>>8)/255.0, ((col&0x0000ff)>>0)/255.0);\n\t}\n\telse if (!strcmp(keyname, \"bottomcolor_rgb\"))\n\t{\n\t\tunsigned int col = cl.players[pnum].dbottomcolor;\n\t\tret = buffer;\n\t\tif (col < 16)\n\t\t{\n\t\t\tcol = Sbar_ColorForMap(col);\n\t\t\tsprintf(ret, \"'%g %g %g'\", host_basepal[col*3+0]/255.0, host_basepal[col*3+1]/255.0, host_basepal[col*3+2]/255.0);\n\t\t}\n\t\telse\n\t\t\tsprintf(ret, \"'%g %g %g'\", ((col&0xff0000)>>16)/255.0, ((col&0x00ff00)>>8)/255.0, ((col&0x0000ff)>>0)/255.0);\n\t}\n#ifdef HAVE_LEGACY\n\telse if (csqc_isdarkplaces && !strcmp(keyname, \"colors\"))\t//checks to see if a player has locally been set to ignored (for text chat)\n\t{\n\t\tret = buffer;\n\t\tsprintf(ret, \"%i\", cl.players[pnum].dtopcolor + cl.players[pnum].dbottomcolor*16);\n\t}\n#endif\n\telse if (!strcmp(keyname, \"ignored\"))\t//checks to see if a player has locally been set to ignored (for text chat)\n\t{\n\t\tret = buffer;\n\t\tsprintf(ret, \"%i\", (int)cl.players[pnum].ignored);\n\t}\n#ifdef VOICECHAT\n\telse if (!strcmp(keyname, \"vignored\"))\t//checks to see this player's voicechat is ignored.\n\t{\n\t\tret = buffer;\n\t\tsprintf(ret, \"%i\", (int)cl.players[pnum].vignored);\n\t}\n\telse if (!strcmp(keyname, \"voipspeaking\"))\n\t{\n\t\tret = buffer;\n\t\tsprintf(ret, \"%i\", S_Voip_Speaking(pnum));\n\t}\n\telse if (!strcmp(keyname, \"voiploudness\"))\n\t{\n\t\tret = buffer;\n\t\tif (pnum == csqc_playerview->playernum)\n\t\t\tsprintf(ret, \"%i\", S_Voip_Loudness(false));\n\t\telse\n\t\t\tsprintf(ret, \"%i\", S_Voip_ClientLoudness(pnum));\n\t}\n#endif\n\telse\n\t{\n\t\tret = InfoBuf_ValueForKey(&cl.players[pnum].userinfo, keyname);\n\t}\n\treturn ret;\n}\n\n//string(float pnum, string keyname)\nstatic void QCBUILTIN PF_cs_getplayerkeystring (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint pnum = G_FLOAT(OFS_PARM0);\n\tconst char *keyname = PR_GetStringOfs(prinst, OFS_PARM1);\n\tconst char *ret;\n\tif (pnum < 0)\n\t{\n\t\tif (csqc_resortfrags)\n\t\t{\n\t\t\tSbar_SortFrags(true, false);\n\t\t\tcsqc_resortfrags = false;\n\t\t}\n\t\tif (pnum >= -scoreboardlines)\n\t\t{//sort by\n\t\t\tpnum = fragsort[-(pnum+1)];\n\t\t}\n\t}\n\n\tret = PF_cs_getplayerkey_internal(pnum, keyname);\n\tif (*ret)\n\t\tRETURN_TSTRING(ret);\n\telse\n\t\tG_INT(OFS_RETURN) = 0;\n}\nstatic void QCBUILTIN PF_cs_getplayerkeyfloat (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint pnum = G_FLOAT(OFS_PARM0);\n\tconst char *keyname = PR_GetStringOfs(prinst, OFS_PARM1);\n\tconst char *ret;\n\tif (pnum < 0)\n\t{\n\t\tif (csqc_resortfrags)\n\t\t{\n\t\t\tSbar_SortFrags(true, false);\n\t\t\tcsqc_resortfrags = false;\n\t\t}\n\t\tif (pnum >= -scoreboardlines)\n\t\t{//sort by\n\t\t\tpnum = fragsort[-(pnum+1)];\n\t\t}\n\t}\n\n\tret = PF_cs_getplayerkey_internal(pnum, keyname);\n\tif (*ret)\n\t\tG_FLOAT(OFS_RETURN) = strtod(ret, NULL);\n\telse\n\t\tG_FLOAT(OFS_RETURN) = (prinst->callargc >= 3)?G_FLOAT(OFS_PARM2):0;\n}\nstatic void QCBUILTIN PF_cs_getplayerkeyblob (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint pnum = G_FLOAT(OFS_PARM0);\n\tconst char *keyname = PR_GetStringOfs(prinst, OFS_PARM1);\n\tint qcptr = G_INT(OFS_PARM2);\n\tint qcsize = G_INT(OFS_PARM3);\n\tvoid *ptr;\n\tif (pnum < 0)\n\t{\n\t\tif (csqc_resortfrags)\n\t\t{\n\t\t\tSbar_SortFrags(true, false);\n\t\t\tcsqc_resortfrags = false;\n\t\t}\n\t\tif (pnum >= -scoreboardlines)\n\t\t{//sort by\n\t\t\tpnum = fragsort[-(pnum+1)];\n\t\t}\n\t}\n\n\tif (qcptr < 0 || qcptr+qcsize >= prinst->stringtablesize)\n\t{\n\t\tPR_BIError(prinst, \"PF_cs_getplayerkeyblob: invalid pointer\\n\");\n\t\treturn;\n\t}\n\tptr = (prinst->stringtable + qcptr);\n\n\tif ((unsigned int)pnum >= (unsigned int)cl.allocated_client_slots)\n\t\tG_INT(OFS_RETURN) = 0;\n\telse\n\t{\n\t\tsize_t blobsize = 0;\n\t\tconst char *blob = InfoBuf_BlobForKey(&cl.players[pnum].userinfo, keyname, &blobsize, NULL);\n\n\t\tif (qcptr)\n\t\t{\n\t\t\tblobsize = min(blobsize, qcsize);\n\t\t\tmemcpy(ptr, blob, blobsize);\n\t\t\tG_INT(OFS_RETURN) = blobsize;\n\t\t}\n\t\telse\n\t\t\tG_INT(OFS_RETURN) = blobsize;\n\t}\n}\n\nstatic void QCBUILTIN PF_cs_infokey (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tcsqcedict_t *ent = (csqcedict_t*)G_EDICT(prinst, OFS_PARM0);\n\tconst char *keyname = PR_GetStringOfs(prinst, OFS_PARM1);\n\tconst char *ret;\n\tif (!ent->entnum)\n\t\tret = PF_cl_serverkey_internal(keyname);\n\telse\n\t{\n\t\tint pnum = ent->xv->entnum-1;\t//figure out which ssqc entity this is meant to be\n\t\tif (pnum < 0)\n\t\t{\n\t\t\tcsqc_deprecated(\"infokey: entity does not correlate to an ssqc entity\\n\");\n\t\t\tret = \"\";\n\t\t}\n\t\telse if (pnum >= cl.allocated_client_slots)\n\t\t{\n\t\t\tcsqc_deprecated(\"infokey: entity does not correlate to an ssqc player entity\\n\");\n\t\t\tret = \"\";\n\t\t}\n\t\telse\n\t\t\tret = PF_cs_getplayerkey_internal(pnum, keyname);\n\t}\n\tif (*ret)\n\t\tRETURN_TSTRING(ret);\n\telse\n\t\tG_INT(OFS_RETURN) = 0;\n}\n\nstatic int PR_CSQC_NamedBuiltinUnsupported(const char *name);\nstatic void QCBUILTIN PF_checkextension (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char *extname = PR_GetStringOfs(prinst, OFS_PARM0);\n\tint i, ebi;\n\tfor (i = 0; i < QSG_Extensions_count; i++)\n\t{\n\t\tif (!QSG_Extensions[i].name)\n\t\t\tcontinue;\n\n\t\tif (!strcmp(QSG_Extensions[i].name, extname))\n\t\t{\n\t\t\tif (QSG_Extensions[i].extensioncheck)\n\t\t\t{\n\t\t\t\textcheck_t ctx = {prinst->parms->user, cls.fteprotocolextensions, cls.fteprotocolextensions2};\n\t\t\t\tG_FLOAT(OFS_RETURN) = QSG_Extensions[i].extensioncheck(&ctx);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t//make sure all of its builtins are actually supported.\n\t\t\t\t//if any is marked as 'fixme' then we've not implemented it in csqc yet.\n\t\t\t\tfor (ebi = 0; ebi < countof(QSG_Extensions[i].builtinnames) && QSG_Extensions[i].builtinnames[ebi]; ebi++)\n\t\t\t\t\tif (PR_CSQC_NamedBuiltinUnsupported(QSG_Extensions[i].builtinnames[ebi]))\n\t\t\t\t\t{\n\t\t\t\t\t\tG_FLOAT(OFS_RETURN) = false;\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\tG_FLOAT(OFS_RETURN) = true;\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t}\n\tG_FLOAT(OFS_RETURN) = false;\n}\n\nvoid QCBUILTIN PF_soundupdate (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\twedict_t\t\t*entity\t= G_WEDICT(prinst, OFS_PARM0);\n\tint\t\t\t\tchannel\t= G_FLOAT(OFS_PARM1);\n\tconst char\t\t*sample = PR_GetStringOfs(prinst, OFS_PARM2);\n\tfloat\t\t\tvolume = G_FLOAT(OFS_PARM3);\n\tfloat\t\t\tattenuation = G_FLOAT(OFS_PARM4);\n\tfloat\t\t\tpitchpct = (prinst->callargc >= 6)?G_FLOAT(OFS_PARM5)*0.01:0;\n\tunsigned int\tflags = (prinst->callargc>=7)?G_FLOAT(OFS_PARM6):0;\n\tfloat\t\t\tstartoffset = (prinst->callargc>=8)?G_FLOAT(OFS_PARM7):0;\n\n\tsfx_t\t\t*sfx = S_PrecacheSound(sample);\n\tvec3_t\t\torg;\n\n\tVectorCopy(entity->v->origin, org);\n\tif (entity->v->solid == SOLID_BSP)\n\t{\n\t\tVectorMA(org, 0.5, entity->v->mins, org);\n\t\tVectorMA(org, 0.5, entity->v->maxs, org);\n\t}\n\n\tG_FLOAT(OFS_RETURN) = S_UpdateSound(-entity->entnum, channel, sfx, org, entity->v->velocity, volume, attenuation, startoffset, pitchpct, flags);\n}\nvoid QCBUILTIN PF_stopsound (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\twedict_t\t*entity\t= G_WEDICT(prinst, OFS_PARM0);\n\tint\t\t\tchannel\t= G_FLOAT(OFS_PARM1);\n\n\tS_StopSound(-entity->entnum, channel);\n}\nvoid QCBUILTIN PF_getsoundtime (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\twedict_t\t*entity\t= G_WEDICT(prinst, OFS_PARM0);\n\tint\t\t\tchannel\t= G_FLOAT(OFS_PARM1);\n\n\tG_FLOAT(OFS_RETURN) = S_GetSoundTime(-entity->entnum, channel);\n}\nvoid QCBUILTIN PF_getchannellevel (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\twedict_t\t*entity\t= G_WEDICT(prinst, OFS_PARM0);\n\tint\t\t\tchannel\t= G_FLOAT(OFS_PARM1);\n\n\tG_FLOAT(OFS_RETURN) = S_GetChannelLevel(-entity->entnum, channel);\n}\nstatic void QCBUILTIN PF_cs_sound(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char\t\t*sample;\n\tint\t\t\tchannel;\n\tcsqcedict_t\t\t*entity;\n\tfloat volume;\n\tfloat attenuation;\n\tfloat pitchpct;\n\tunsigned int flags;\n\tfloat startoffset;\n\n\tsfx_t *sfx;\n\tvec3_t\t\torg;\n\n\tentity = (csqcedict_t*)G_EDICT(prinst, OFS_PARM0);\n\tchannel = G_FLOAT(OFS_PARM1);\n\tsample = PR_GetStringOfs(prinst, OFS_PARM2);\n\tvolume = G_FLOAT(OFS_PARM3);\n\tattenuation = G_FLOAT(OFS_PARM4);\n\tpitchpct = (prinst->callargc>=6)?G_FLOAT(OFS_PARM5)*0.01:0;\n\tflags = (prinst->callargc>=7)?G_FLOAT(OFS_PARM6):0;\n\tstartoffset = (prinst->callargc>=8)?G_FLOAT(OFS_PARM7):0;\n\n\tsfx = S_PrecacheSound(sample);\n\n\tVectorCopy(entity->v->origin, org);\n\tif (entity->v->solid == SOLID_BSP)\n\t{\n\t\tVectorMA(org, 0.5, entity->v->mins, org);\n\t\tVectorMA(org, 0.5, entity->v->maxs, org);\n\t}\n\n\tif (sfx)\n\t\tS_StartSound(-entity->entnum, channel, sfx, org, entity->v->velocity, volume, attenuation, startoffset, pitchpct, flags);\n}\n\nstatic void QCBUILTIN PF_cs_pointsound(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char *sample;\n\tfloat *origin;\n\tfloat volume;\n\tfloat attenuation;\n\tfloat pitchpct;\n\n\tsfx_t *sfx;\n\n\torigin = G_VECTOR(OFS_PARM0);\n\tsample = PR_GetStringOfs(prinst, OFS_PARM1);\n\tvolume = G_FLOAT(OFS_PARM2);\n\tattenuation = G_FLOAT(OFS_PARM3);\n\tif (prinst->callargc >= 5)\n\t\tpitchpct = G_FLOAT(OFS_PARM4)*0.01;\n\telse\n\t\tpitchpct = 0;\n\n\tsfx = S_PrecacheSound(sample);\n\tif (sfx)\n\t\tS_StartSound(0, 0, sfx, origin, NULL, volume, attenuation, 0, pitchpct, 0);\n}\n\nstatic void QCBUILTIN PF_cs_particle(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tfloat *org = G_VECTOR(OFS_PARM0);\n\tfloat *dir = G_VECTOR(OFS_PARM1);\n\tfloat colour = G_FLOAT(OFS_PARM2);\n\tfloat count = G_FLOAT(OFS_PARM2);\n\n\tpe->RunParticleEffect(org, dir, colour, count);\n}\nstatic void QCBUILTIN PF_cs_particle2(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tfloat\t\t*org, *dmin, *dmax;\n\tfloat\t\tcolour;\n\tfloat\t\tcount;\n\tfloat    effect;\n\n\torg = G_VECTOR(OFS_PARM0);\n\tdmin = G_VECTOR(OFS_PARM1);\n\tdmax = G_VECTOR(OFS_PARM2);\n\tcolour = G_FLOAT(OFS_PARM3);\n\teffect = G_FLOAT(OFS_PARM4);\n\tcount = G_FLOAT(OFS_PARM5);\n\n\tpe->RunParticleEffect2 (org, dmin, dmax, colour, effect, count);\n}\n\nstatic void QCBUILTIN PF_cs_particle3(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tfloat\t\t*org, *box;\n\tfloat\t\tcolour;\n\tfloat\t\tcount;\n\tfloat    effect;\n\n\torg = G_VECTOR(OFS_PARM0);\n\tbox = G_VECTOR(OFS_PARM1);\n\tcolour = G_FLOAT(OFS_PARM2);\n\teffect = G_FLOAT(OFS_PARM3);\n\tcount = G_FLOAT(OFS_PARM4);\n\n\tpe->RunParticleEffect3(org, box, colour, effect, count);\n}\n\nstatic void QCBUILTIN PF_cs_particle4(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tfloat\t\t*org;\n\tfloat\t\tradius;\n\tfloat\t\tcolour;\n\tfloat\t\tcount;\n\tfloat    effect;\n\n\torg = G_VECTOR(OFS_PARM0);\n\tradius = G_FLOAT(OFS_PARM1);\n\tcolour = G_FLOAT(OFS_PARM2);\n\teffect = G_FLOAT(OFS_PARM3);\n\tcount = G_FLOAT(OFS_PARM4);\n\n\tpe->RunParticleEffect4(org, radius, colour, effect, count);\n}\n\n\nvoid QCBUILTIN PF_cl_effect(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tfloat *org = G_VECTOR(OFS_PARM0);\n\tconst char *name = PR_GetStringOfs(prinst, OFS_PARM1);\n\tfloat startframe = G_FLOAT(OFS_PARM2);\n\tfloat endframe = G_FLOAT(OFS_PARM3);\n\tfloat framerate = G_FLOAT(OFS_PARM4);\n\tmodel_t *mdl;\n\n\tmdl = Mod_ForName(name, MLV_WARN);\n\tif (mdl)\n\t\tCL_SpawnSpriteEffect(org, NULL, NULL, mdl, startframe, endframe, framerate, mdl->type==mod_sprite?-1:1, 1, 0, 0, P_INVALID, 0, 0, 1.0f, 1.0f, 1.0f);\n\telse\n\t\tCon_Printf(\"PF_cl_effect: Couldn't load model %s\\n\", name);\n}\n\nvoid QCBUILTIN PF_cl_ambientsound(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char\t\t*samp;\n\tfloat\t\t*pos;\n\tfloat \t\tvol, attenuation;\n\n\tpos = G_VECTOR (OFS_PARM0);\n\tsamp = PR_GetStringOfs(prinst, OFS_PARM1);\n\tvol = G_FLOAT(OFS_PARM2);\n\tattenuation = G_FLOAT(OFS_PARM3);\n\n\tS_StaticSound (S_PrecacheSound (samp), pos, vol, attenuation);\n}\n\nstatic void QCBUILTIN PF_cs_lightstyle (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint stnum = G_FLOAT(OFS_PARM0);\n\tconst char *str = PR_GetStringOfs(prinst, OFS_PARM1);\n\tvec3_t rgb = {1,1,1};\n\t\n\tif (prinst->callargc >= 3)\t//fte is a quakeworld engine\n\t\tVectorCopy(G_VECTOR(OFS_PARM2), rgb);\n\n\tR_UpdateLightStyle(stnum, str, rgb[0],rgb[1],rgb[2]);\n}\n\nstatic void QCBUILTIN PF_getlightstyle (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tunsigned int stnum = G_FLOAT(OFS_PARM0);\n\n\tif (stnum >= cl_max_lightstyles)\n\t{\n\t\tVectorSet(G_VECTOR(OFS_PARM1), 0, 0, 0);\n\t\tG_INT(OFS_RETURN) = 0;\n\t\treturn;\n\t}\n\n\tVectorCopy(cl_lightstyle[stnum].colours, G_VECTOR(OFS_PARM1));\n\tif (!cl_lightstyle[stnum].length)\n\t\tG_INT(OFS_RETURN) = 0;\n\telse\n\t\tRETURN_TSTRING(cl_lightstyle[stnum].map);\n}\nstatic void QCBUILTIN PF_getlightstylergb (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tunsigned int stnum = G_FLOAT(OFS_PARM0);\n\tint value;\t//could be float, but that would exceed the precision of R_AnimateLight\n\n\tif (stnum >= MAX_NET_LIGHTSTYLES)\n\t{\n\t\tCon_Printf (\"PF_getlightstyle: stnum > MAX_LIGHTSTYLES\");\n\t\treturn;\n\t}\n\n\tif (stnum >= cl_max_lightstyles)\n\t{\n\t\tvalue = ('m'-'a')*22 * r_lightstylescale.value;\n\t\tG_VECTOR(OFS_RETURN)[0] = G_VECTOR(OFS_RETURN)[1] = G_VECTOR(OFS_RETURN)[2] = value*(1.0/256);\n\t\treturn;\n\t}\n\telse if (!cl_lightstyle[stnum].length)\n\t\tvalue = ('m'-'a')*22 * r_lightstylescale.value;\n\telse if (cl_lightstyle[stnum].map[0] == '=')\n\t\tvalue = atof(cl_lightstyle[stnum].map+1)*256*r_lightstylescale.value;\n\telse\n\t{\n\t\tint v1, v2, vd, i;\n\t\tfloat f;\n\n\t\tf = (cl.time*r_lightstylespeed.value);\n\t\tif (f < 0)\n\t\t\tf = 0;\n\t\ti = (int)f;\n\t\tf -= i;\t//this can require updates at 1000 times a second.. Depends on your framerate of course\n\n\t\tv1 = i % cl_lightstyle[stnum].length;\n\t\tv1 = cl_lightstyle[stnum].map[v1] - 'a';\n\t\tv2 = (i+1) % cl_lightstyle[stnum].length;\n\t\tv2 = cl_lightstyle[stnum].map[v2] - 'a';\n\t\tvd = v1 - v2;\n\t\tif (!r_lightstylesmooth.ival || vd < -r_lightstylesmooth_limit.ival || vd > r_lightstylesmooth_limit.ival)\n\t\t\tvalue = v1*22*r_lightstylescale.value;\n\t\telse\n\t\t\tvalue = (v1*(1-f) + v2*(f))*22*r_lightstylescale.value;\n\t}\n\n\tVectorScale(cl_lightstyle[stnum].colours, value*(1.0/256), G_VECTOR(OFS_RETURN));\n}\n\nstatic void QCBUILTIN PF_cl_te_gunshot (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tfloat *pos = G_VECTOR(OFS_PARM0);\n\tfloat scaler = 1;\n\tif (prinst->callargc >= 2)\t//fte is a quakeworld engine\n\t\tscaler = G_FLOAT(OFS_PARM1);\n\tif (P_RunParticleEffectType(pos, NULL, scaler, pt_gunshot))\n\t\tP_RunParticleEffect (pos, vec3_origin, 0, 20*scaler);\n}\nstatic void QCBUILTIN PF_cl_te_bloodqw (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tfloat *pos = G_VECTOR(OFS_PARM0);\n\tfloat scaler = 1;\n\tif (prinst->callargc >= 2)\t//fte is a quakeworld engine\n\t\tscaler = G_FLOAT(OFS_PARM1);\n\tif (P_RunParticleEffectType(pos, NULL, scaler, ptqw_blood))\n\t\tif (P_RunParticleEffectType(pos, NULL, scaler, ptdp_blood))\n\t\t\tP_RunParticleEffect (pos, vec3_origin, 73, 20*scaler);\n}\nstatic void QCBUILTIN PF_cl_te_blooddp (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tfloat *pos = G_VECTOR(OFS_PARM0);\n\tfloat *dir = G_VECTOR(OFS_PARM1);\n\tfloat scaler = G_FLOAT(OFS_PARM2);\n\n\tif (P_RunParticleEffectType(pos, dir, scaler, ptdp_blood))\n\t\tif (P_RunParticleEffectType(pos, dir, scaler, ptqw_blood))\n\t\t\tP_RunParticleEffect (pos, dir, 73, 20*scaler);\n}\nstatic void QCBUILTIN PF_cl_te_lightningblood (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tfloat *pos = G_VECTOR(OFS_PARM0);\n\tif (P_RunParticleEffectType(pos, NULL, 1, ptqw_lightningblood))\n\t\tP_RunParticleEffect (pos, vec3_origin, 225, 50);\n}\nstatic void QCBUILTIN PF_cl_te_spike (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tfloat *pos = G_VECTOR(OFS_PARM0);\n\tif (P_RunParticleEffectType(pos, NULL, 1, pt_spike))\n\t\tif (P_RunParticleEffectType(pos, NULL, 10, pt_gunshot))\n\t\t\tP_RunParticleEffect (pos, vec3_origin, 0, 10);\n}\nstatic void QCBUILTIN PF_cl_te_superspike (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tfloat *pos = G_VECTOR(OFS_PARM0);\n\tif (P_RunParticleEffectType(pos, NULL, 1, pt_superspike))\n\t\tif (P_RunParticleEffectType(pos, NULL, 2, pt_spike))\n\t\t\tif (P_RunParticleEffectType(pos, NULL, 20, pt_gunshot))\n\t\t\t\tP_RunParticleEffect (pos, vec3_origin, 0, 20);\n}\nstatic void QCBUILTIN PF_cl_te_explosion (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tfloat *pos = G_VECTOR(OFS_PARM0);\n\n\t// light\n\tif (r_explosionlight.value) {\n\t\tdlight_t *dl;\n\n\t\tdl = CL_AllocDlight (0);\n\t\tVectorCopy (pos, dl->origin);\n\t\tdl->radius = 150 + r_explosionlight.value*200;\n\t\tdl->die = cl.time + 1;\n\t\tdl->decay = 300;\n\n\t\tdl->color[0] = 0.2;\n\t\tdl->color[1] = 0.155;\n\t\tdl->color[2] = 0.05;\n\t\tdl->channelfade[0] = 0.196;\n\t\tdl->channelfade[1] = 0.23;\n\t\tdl->channelfade[2] = 0.12;\n\t}\n\n\tif (P_RunParticleEffectType(pos, NULL, 1, pt_explosion))\n\t\tP_RunParticleEffect(pos, NULL, 107, 1024); // should be 97-111\n\n\tSurf_AddStain(pos, -1, -1, -1, 100);\n\n\tS_StartSound (0, 0, cl_sfx_r_exp3, pos, NULL, 1, 1, 0, 0, 0);\n}\nstatic void QCBUILTIN PF_cl_te_tarexplosion (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tfloat *pos = G_VECTOR(OFS_PARM0);\n\tP_RunParticleEffectType(pos, NULL, 1, pt_tarexplosion);\n\n\tS_StartSound (0, 0, cl_sfx_r_exp3, pos, NULL, 1, 1, 0, 0, 0);\n}\nstatic void QCBUILTIN PF_cl_te_wizspike (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tfloat *pos = G_VECTOR(OFS_PARM0);\n\tif (P_RunParticleEffectType(pos, NULL, 1, pt_wizspike))\n\t\tP_RunParticleEffect (pos, vec3_origin, 20, 30);\n\n\tS_StartSound (0, 0, cl_sfx_knighthit, pos, NULL, 1, 1, 0, 0, 0);\n}\nstatic void QCBUILTIN PF_cl_te_knightspike (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tfloat *pos = G_VECTOR(OFS_PARM0);\n\tif (P_RunParticleEffectType(pos, NULL, 1, pt_knightspike))\n\t\tP_RunParticleEffect (pos, vec3_origin, 226, 20);\n\n\tS_StartSound (0, 0, cl_sfx_knighthit, pos, NULL, 1, 1, 0, 0, 0);\n}\nstatic void QCBUILTIN PF_cl_te_lavasplash (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tfloat *pos = G_VECTOR(OFS_PARM0);\n\tP_RunParticleEffectType(pos, NULL, 1, pt_lavasplash);\n}\nstatic void QCBUILTIN PF_cl_te_teleport (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tfloat *pos = G_VECTOR(OFS_PARM0);\n\tP_RunParticleEffectType(pos, NULL, 1, pt_teleportsplash);\n}\nstatic void QCBUILTIN PF_cl_te_gunshotquad (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tfloat *pos = G_VECTOR(OFS_PARM0);\n\tif (P_RunParticleEffectTypeString(pos, vec3_origin, 1, \"te_gunshotquad\"))\n\t\tif (P_RunParticleEffectType(pos, NULL, 1, pt_gunshot))\n\t\t\tP_RunParticleEffect (pos, vec3_origin, 0, 20);\n}\nstatic void QCBUILTIN PF_cl_te_spikequad (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tfloat *pos = G_VECTOR(OFS_PARM0);\n\tif (P_RunParticleEffectTypeString(pos, vec3_origin, 1, \"te_spikequad\"))\n\t\tif (P_RunParticleEffectType(pos, NULL, 1, pt_spike))\n\t\t\tif (P_RunParticleEffectType(pos, NULL, 10, pt_gunshot))\n\t\t\t\tP_RunParticleEffect (pos, vec3_origin, 0, 10);\n}\nstatic void QCBUILTIN PF_cl_te_superspikequad (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tfloat *pos = G_VECTOR(OFS_PARM0);\n\tif (P_RunParticleEffectTypeString(pos, vec3_origin, 1, \"te_superspikequad\"))\n\t\tif (P_RunParticleEffectType(pos, NULL, 1, pt_superspike))\n\t\t\tif (P_RunParticleEffectType(pos, NULL, 2, pt_spike))\n\t\t\t\tif (P_RunParticleEffectType(pos, NULL, 20, pt_gunshot))\n\t\t\t\t\tP_RunParticleEffect (pos, vec3_origin, 0, 20);\n}\nstatic void QCBUILTIN PF_cl_te_explosionquad (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tfloat *pos = G_VECTOR(OFS_PARM0);\n\tif (P_RunParticleEffectTypeString(pos, vec3_origin, 1, \"te_explosionquad\"))\n\t\tif (P_RunParticleEffectType(pos, NULL, 1, pt_explosion))\n\t\t\tP_RunParticleEffect(pos, NULL, 107, 1024); // should be 97-111\n\n\tSurf_AddStain(pos, -1, -1, -1, 100);\n\n\t// light\n\tif (r_explosionlight.value) {\n\t\tdlight_t *dl;\n\n\t\tdl = CL_AllocDlight (0);\n\t\tVectorCopy (pos, dl->origin);\n\t\tdl->radius = 150 + r_explosionlight.value*200;\n\t\tdl->die = cl.time + 1;\n\t\tdl->decay = 300;\n\n\t\tdl->color[0] = 0.2;\n\t\tdl->color[1] = 0.155;\n\t\tdl->color[2] = 0.05;\n\t\tdl->channelfade[0] = 0.196;\n\t\tdl->channelfade[1] = 0.23;\n\t\tdl->channelfade[2] = 0.12;\n\t}\n\n\tS_StartSound (0, 0, cl_sfx_r_exp3, pos, NULL, 1, 1, 0, 0, 0);\n}\n\n//void(vector org, float radius, float lifetime, vector color) te_customflash\nstatic void QCBUILTIN PF_cl_te_customflash (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tfloat *org = G_VECTOR(OFS_PARM0);\n\tfloat radius = G_FLOAT(OFS_PARM1);\n\tfloat lifetime = G_FLOAT(OFS_PARM2);\n\tfloat *colour = G_VECTOR(OFS_PARM3);\n\n\tdlight_t *dl;\n\t// light\n\tdl = CL_AllocDlight (0);\n\tVectorCopy (org, dl->origin);\n\tdl->radius = radius;\n\tdl->die = cl.time + lifetime;\n\tdl->decay = dl->radius / lifetime;\n\tdl->color[0] = colour[0]*0.5f;\n\tdl->color[1] = colour[1]*0.5f;\n\tdl->color[2] = colour[2]*0.5f;\n}\n\nstatic void QCBUILTIN PF_cl_te_bloodshower (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tfloat *minb = G_VECTOR(OFS_PARM0);\n\tfloat *maxb = G_VECTOR(OFS_PARM1);\n\tvec3_t vel = {0,0,-G_FLOAT(OFS_PARM2)};\n\tfloat howmany = G_FLOAT(OFS_PARM3);\n\t\n\tP_RunParticleCube(P_FindParticleType(\"te_bloodshower\"), minb, maxb, vel, vel, howmany, 0, false, 0);\n}\nstatic void QCBUILTIN PF_cl_te_particlecube (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tfloat *minb = G_VECTOR(OFS_PARM0);\n\tfloat *maxb = G_VECTOR(OFS_PARM1);\n\tfloat *vel = G_VECTOR(OFS_PARM2);\n\tfloat howmany = G_FLOAT(OFS_PARM3);\n\tfloat color = G_FLOAT(OFS_PARM4);\n\tfloat gravity = G_FLOAT(OFS_PARM5);\n\tfloat jitter = G_FLOAT(OFS_PARM6);\n\n\tP_RunParticleCube(P_INVALID, minb, maxb, vel, vel, howmany, color, gravity, jitter);\n}\nstatic void QCBUILTIN PF_cl_te_spark (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tfloat *pos = G_VECTOR(OFS_PARM0);\n\tfloat *pos2 = G_VECTOR(OFS_PARM1);\n\tfloat cnt = G_FLOAT(OFS_PARM2);\n\tP_RunParticleEffectType(pos, pos2, cnt, ptdp_spark);\n}\nstatic void QCBUILTIN PF_cl_te_smallflash (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tfloat *pos = G_VECTOR(OFS_PARM0);\n\tdlight_t *dl = CL_AllocDlight (0);\n\tVectorCopy (pos, dl->origin);\n\tdl->radius = 200;\n\tdl->decay = 1000;\n\tdl->die = cl.time + 0.2;\n\tdl->color[0] = 2.0;\n\tdl->color[1] = 2.0;\n\tdl->color[2] = 2.0;\n}\nstatic void QCBUILTIN PF_cl_te_explosion2 (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tfloat *pos = G_VECTOR(OFS_PARM0);\n\tint colorStart = G_FLOAT(OFS_PARM1);\n\tint colorLength = G_FLOAT(OFS_PARM2);\n\tint ef = P_FindParticleType(va(\"TE_EXPLOSION2_%i_%i\", colorStart, colorLength));\n\tif (ef == P_INVALID)\n\t\tef = pt_explosion;\n\tP_RunParticleEffectType(pos, NULL, 1, ef);\n\tif (r_explosionlight.value)\n\t{\n\t\tdlight_t *dl = CL_AllocDlight (0);\n\t\tVectorCopy (pos, dl->origin);\n\t\tdl->radius = 350;\n\t\tdl->die = cl.time + 0.5;\n\t\tdl->decay = 300;\n\t}\n\tS_StartSound (0, 0, cl_sfx_r_exp3, pos, NULL, 1, 1, 0, 0, 0);\n}\n\nstatic void QCBUILTIN PF_cl_te_flamejet (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tvec3_t pos, vel;\n\tint count;\n\n\t// origin\n\tpos[0] = MSG_ReadCoord ();\n\tpos[1] = MSG_ReadCoord ();\n\tpos[2] = MSG_ReadCoord ();\n\n\t// velocity\n\tvel[0] = MSG_ReadCoord ();\n\tvel[1] = MSG_ReadCoord ();\n\tvel[2] = MSG_ReadCoord ();\n\n\t// count\n\tcount = MSG_ReadByte ();\n\n\tif (P_RunParticleEffectType(pos, vel, count, P_FindParticleType(\"TE_FLAMEJET\")))\n\t\tP_RunParticleEffect (pos, vel, 232, count);\n}\n\nstatic void QCBUILTIN PF_cl_te_lightning1 (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tcsqcedict_t *ent = (csqcedict_t*)G_EDICT(prinst, OFS_PARM0);\n\tfloat *start = G_VECTOR(OFS_PARM1);\n\tfloat *end = G_VECTOR(OFS_PARM2);\n\n\tCL_AddBeam(BT_Q1LIGHTNING1, ent->entnum+(ent->entnum?MAX_EDICTS:0), start, end);\n}\nstatic void QCBUILTIN PF_cl_te_lightning2 (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tcsqcedict_t *ent = (csqcedict_t*)G_EDICT(prinst, OFS_PARM0);\n\tfloat *start = G_VECTOR(OFS_PARM1);\n\tfloat *end = G_VECTOR(OFS_PARM2);\n\n\tCL_AddBeam(BT_Q1LIGHTNING2, ent->entnum+(ent->entnum?MAX_EDICTS:0), start, end);\n}\nstatic void QCBUILTIN PF_cl_te_lightning3 (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tcsqcedict_t *ent = (csqcedict_t*)G_EDICT(prinst, OFS_PARM0);\n\tfloat *start = G_VECTOR(OFS_PARM1);\n\tfloat *end = G_VECTOR(OFS_PARM2);\n\n\tCL_AddBeam(BT_Q1LIGHTNING3, ent->entnum+(ent->entnum?MAX_EDICTS:0), start, end);\n}\nstatic void QCBUILTIN PF_cl_te_beam (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tcsqcedict_t *ent = (csqcedict_t*)G_EDICT(prinst, OFS_PARM0);\n\tfloat *start = G_VECTOR(OFS_PARM1);\n\tfloat *end = G_VECTOR(OFS_PARM2);\n\n\tCL_AddBeam(BT_Q1BEAM, ent->entnum+(ent->entnum?MAX_EDICTS:0), start, end);\n}\nstatic void QCBUILTIN PF_cl_te_plasmaburn (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tfloat *pos = G_VECTOR(OFS_PARM0);\n\n\tif (P_RunParticleEffectType(pos, NULL, 1, P_FindParticleType(\"te_plasmaburn\")))\n\t\tP_RunParticleEffect(pos, vec3_origin, 15, 50);\n}\nstatic void QCBUILTIN PF_cl_te_explosionrgb (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tfloat *org = G_VECTOR(OFS_PARM0);\n\tfloat *colour = G_VECTOR(OFS_PARM1);\n\n\tdlight_t *dl;\n\n\tif (P_RunParticleEffectType(org, NULL, 1, pt_explosion))\n\t\tP_RunParticleEffect(org, NULL, 107, 1024); // should be 97-111\n\n\tSurf_AddStain(org, -1, -1, -1, 100);\n\n\t// light\n\tif (r_explosionlight.value)\n\t{\n\t\tdl = CL_AllocDlight (0);\n\t\tVectorCopy (org, dl->origin);\n\t\tdl->radius = 150 + r_explosionlight.value*200;\n\t\tdl->die = cl.time + 0.5;\n\t\tdl->decay = 300;\n\n\t\tdl->color[0] = 0.4f*colour[0];\n\t\tdl->color[1] = 0.4f*colour[1];\n\t\tdl->color[2] = 0.4f*colour[2];\n\t\tdl->channelfade[0] = 0;\n\t\tdl->channelfade[1] = 0;\n\t\tdl->channelfade[2] = 0;\n\t}\n\n\tS_StartSound (0, 0, cl_sfx_r_exp3, org, NULL, 1, 1, 0, 0, 0);\n}\nstatic void QCBUILTIN PF_cl_te_particlerain (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tfloat *min = G_VECTOR(OFS_PARM0);\n\tfloat *max = G_VECTOR(OFS_PARM1);\n\tfloat *vel = G_VECTOR(OFS_PARM2);\n\tfloat howmany = G_FLOAT(OFS_PARM3);\n\tfloat colour = G_FLOAT(OFS_PARM4);\n\n\tP_RunParticleWeather(min, max, vel, howmany, colour, \"rain\");\n}\nstatic void QCBUILTIN PF_cl_te_particlesnow (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tfloat *min = G_VECTOR(OFS_PARM0);\n\tfloat *max = G_VECTOR(OFS_PARM1);\n\tfloat *vel = G_VECTOR(OFS_PARM2);\n\tfloat howmany = G_FLOAT(OFS_PARM3);\n\tfloat colour = G_FLOAT(OFS_PARM4);\n\n\tP_RunParticleWeather(min, max, vel, howmany, colour, \"snow\");\n}\n\nstatic void QCBUILTIN PF_cs_OpenPortal (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint state\t= G_FLOAT(OFS_PARM1)!=0;\n\twedict_t *ent = G_WEDICT(prinst, OFS_PARM0);\n\tint portal = ent->xv->style;\n\tint area1 = ent->pvsinfo.areanum, area2 = ent->pvsinfo.areanum2;\n\tif (csqc_world.worldmodel->funcs.SetAreaPortalState)\n\t\tcsqc_world.worldmodel->funcs.SetAreaPortalState(csqc_world.worldmodel, portal, area1, area2, state);\n}\n\nstatic void QCBUILTIN PF_cs_droptofloor (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tcsqcedict_t\t\t*ent;\n\tvec3_t\t\tend;\n\tvec3_t\t\tstart;\n\ttrace_t\t\ttrace;\n\n\tent = (csqcedict_t*)PROG_TO_EDICT(prinst, *csqcg.self);\n\n\tVectorCopy (ent->v->origin, end);\n\tend[2] -= 512;\n\n\tVectorCopy (ent->v->origin, start);\n\ttrace = World_Move (&csqc_world, start, ent->v->mins, ent->v->maxs, end, MOVE_NORMAL, (wedict_t*)ent);\n\n\tif (trace.fraction == 1 || trace.allsolid)\n\t\tG_FLOAT(OFS_RETURN) = 0;\n\telse\n\t{\n\t\tVectorCopy (trace.endpos, ent->v->origin);\n\t\tWorld_LinkEdict(&csqc_world, (wedict_t*)ent, false);\n\t\tent->v->flags = (int)ent->v->flags | FL_ONGROUND;\n\t\tent->v->groundentity = EDICT_TO_PROG(prinst, trace.ent);\n\t\tG_FLOAT(OFS_RETURN) = 1;\n\t}\n}\n\nstatic void QCBUILTIN PF_cl_getlight (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tvec3_t ambient, diffuse, dir;\n\n\tif (csqc_world.worldmodel->lightmaps.maxstyle >= cl_max_lightstyles || !csqc_world.worldmodel || csqc_world.worldmodel->loadstate != MLS_LOADED || !csqc_world.worldmodel->funcs.LightPointValues)\n\t\tVectorSet(G_VECTOR(OFS_RETURN), 0, 0, 0);\n\telse\n\t{\n\t\tcsqc_world.worldmodel->funcs.LightPointValues(csqc_world.worldmodel, G_VECTOR(OFS_PARM0), ambient, diffuse, dir);\n\t\tVectorMA(ambient, 0.5, diffuse, G_VECTOR(OFS_RETURN));\n\t}\n}\n\n/*\nstatic void QCBUILTIN PF_Stub (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tCon_Printf(\"Obsolete csqc builtin (%i) executed\\n\", prinst->lastcalledbuiltinnumber);\n}\n*/\n\nstatic void QCBUILTIN PF_rotatevectorsbytag (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tcsqcedict_t *ent = (csqcedict_t*)G_EDICT(prinst, OFS_PARM0);\n\tint tagnum = G_FLOAT(OFS_PARM1);\n\n\tfloat *srcorg = ent->v->origin;\n\tint modelindex = ent->v->modelindex;\n\n\tfloat *retorg = G_VECTOR(OFS_RETURN);\n\n\tmodel_t *mod = CSQC_GetModelForIndex(modelindex);\n\tfloat transforms[12];\n\tfloat src[12];\n\tfloat dest[12];\n\tint i;\n\tframestate_t fstate;\n\n\tcs_getframestate(ent, ent->xv->renderflags, &fstate);\n\n\tif (Mod_GetTag(mod, tagnum, &fstate, transforms))\n\t{\n\t\tVectorCopy(csqcg.v_forward, src+0);\n\t\tsrc[3] = 0;\n\t\tVectorNegate(csqcg.v_right, src+4);\n\t\tsrc[7] = 0;\n\t\tVectorCopy(csqcg.v_up, src+8);\n\t\tsrc[11] = 0;\n\n\t\tif (ent->xv->scale)\n\t\t{\n\t\t\tfor (i = 0; i < 12; i+=4)\n\t\t\t{\n\t\t\t\ttransforms[i+0] *= ent->xv->scale;\n\t\t\t\ttransforms[i+1] *= ent->xv->scale;\n\t\t\t\ttransforms[i+2] *= ent->xv->scale;\n\t\t\t\ttransforms[i+3] *= ent->xv->scale;\n\t\t\t}\n\t\t}\n\n\t\tR_ConcatRotationsPad((void*)transforms, (void*)src, (void*)dest);\n\n\t\tVectorCopy(dest+0, csqcg.v_forward);\n\t\tVectorNegate(dest+4, csqcg.v_right);\n\t\tVectorCopy(dest+8, csqcg.v_up);\n\n\t\tVectorCopy(srcorg, retorg);\n\t\tfor (i = 0 ; i < 3 ; i++)\n\t\t{\n\t\t\tretorg[0] += transforms[i*4+3]*src[4*i+0];\n\t\t\tretorg[1] += transforms[i*4+3]*src[4*i+1];\n\t\t\tretorg[2] += transforms[i*4+3]*src[4*i+2];\n\t\t}\n\t\treturn;\n\t}\n\n\tVectorCopy(srcorg, retorg);\n}\n\n\n\n\nstatic void QCBUILTIN PF_cs_break (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tPR_RunWarning (prinst, \"break statement\\n\");\n}\n\n//fixme merge with ssqc\nstatic void QCBUILTIN PF_cs_walkmove (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\twedict_t\t*ent;\n\tfloat\tyaw, dist;\n\tvec3_t\tmove;\n//\tdfunction_t\t*oldf;\n\tint \toldself;\n\tqboolean settrace;\n\tvec3_t axis[3];\n\tfloat s;\n\n\tent = PROG_TO_WEDICT(prinst, *csqcg.self);\n\tyaw = G_FLOAT(OFS_PARM0);\n\tdist = G_FLOAT(OFS_PARM1);\n\tif (prinst->callargc >= 3 && G_FLOAT(OFS_PARM2))\n\t\tsettrace = true;\n\telse\n\t\tsettrace = false;\n\n\tif ( !( (int)ent->v->flags & (FL_ONGROUND|FL_FLY|FL_SWIM) ) )\n\t{\n\t\tG_FLOAT(OFS_RETURN) = 0;\n\t\treturn;\n\t}\n\n\tWorld_GetEntGravityAxis(ent, axis);\n\n\tyaw = yaw*M_PI*2 / 360;\n\n\ts = cos(yaw)*dist;\n\tVectorScale(axis[0], s, move);\n\ts = sin(yaw)*dist;\n\tVectorMA(move, s, axis[1], move);\n\n// save program state, because CS_movestep may call other progs\n\toldself = *csqcg.self;\n\n\tG_FLOAT(OFS_RETURN) = World_movestep(&csqc_world, (wedict_t*)ent, move, axis, true, false, settrace?cs_settracevars:NULL);\n\n// restore program state\n\t*csqcg.self = oldself;\n}\n\n//fixme merge with ssqc\nstatic void QCBUILTIN PF_cs_movetogoal (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\twedict_t\t*ent;\n\tfloat dist;\n\tent = (wedict_t*)PROG_TO_EDICT(prinst, *csqcg.self);\n\tdist = G_FLOAT(OFS_PARM0);\n\tWorld_MoveToGoal (&csqc_world, ent, dist);\n}\n\nstatic void CS_ConsoleCommand_f(void)\n{\n\tchar cmd[2048];\n\tint seat = CL_TargettedSplit(false);\n\tQ_snprintfz(cmd, sizeof(cmd), \"%s %s\", Cmd_Argv(0), Cmd_Args());\n\tCSQC_ConsoleCommand(seat, cmd);\n}\nstatic void QCBUILTIN PF_cs_registercommand (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char *str = PR_GetStringOfs(prinst, OFS_PARM0);\n\tconst char *desc = (prinst->callargc>1)?PR_GetStringOfs(prinst, OFS_PARM1):NULL;\n\tif (desc && !*desc)\n\t\tdesc = NULL;\n\tif (!Cmd_Exists(str))\n\t\tCmd_AddCommandD(str, CS_ConsoleCommand_f, desc);\n}\n\nstatic void QCBUILTIN PF_cs_setlistener (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tfloat *origin = G_VECTOR(OFS_PARM0);\n\tfloat *forward = G_VECTOR(OFS_PARM1);\n\tfloat *right = G_VECTOR(OFS_PARM2);\n\tfloat *up = G_VECTOR(OFS_PARM3);\n\tsize_t reverbtype = (prinst->callargc>4)?G_FLOAT(OFS_PARM4):0;\n\tfloat *velocity = (prinst->callargc>5)?G_VECTOR(OFS_PARM5):NULL;\n\n\tint i = (csqc_playerseat>=0)?csqc_playerseat:0;\n\n\tcl.playerview[i].audio.defaulted = false;\n\tcl.playerview[i].audio.entnum = csqcg.player_localentnum?*csqcg.player_localentnum:cl.playerview[i].viewentity;\n\tVectorCopy(origin, cl.playerview[i].audio.origin);\n\tVectorCopy(forward, cl.playerview[i].audio.forward);\n\tVectorCopy(right, cl.playerview[i].audio.right);\n\tVectorCopy(up, cl.playerview[i].audio.up);\n\tcl.playerview[i].audio.reverbtype = reverbtype;\n\tif (velocity)\n\t\tVectorCopy(velocity, cl.playerview[i].audio.velocity);\n\telse\n\t\tVectorClear(cl.playerview[i].audio.velocity);\n}\nstatic void QCBUILTIN PF_cs_setupreverb (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint reverbslot = G_FLOAT(OFS_PARM0);\n\tint qcptr = G_INT(OFS_PARM1);\n\tint size = G_INT(OFS_PARM2);\n\tstruct reverbproperties_s *ptr;\n\n\t//validates the pointer.\n\tif (qcptr < 0 || qcptr+size >= prinst->stringtablesize)\n\t{\n\t\tPR_BIError(prinst, \"PF_cs_setupreverb: invalid reverb pointer\\n\");\n\t\treturn;\n\t}\n\tptr = (struct reverbproperties_s*)(prinst->stringtable + qcptr);\n\n\t//let the sound system do its thing.\n\tS_UpdateReverb(reverbslot, ptr, size);\n}\n\n#define RSES_NOLERP 1\n#define RSES_NOROTATE 2\n#define RSES_NOTRAILS 4\n#define RSES_NOLIGHTS 8\n\nstatic void CSQC_LerpStateToCSQC(lerpents_t *le, csqcedict_t *ent, qboolean nolerp)\n{\n\tent->v->frame = le->newframe[FS_REG];\n\tent->xv->frame1time = max(0, cl.servertime - le->newframestarttime[FS_REG]);\n\tent->xv->frame2 = le->oldframe[FS_REG];\n\tent->xv->frame2time = max(0, cl.servertime - le->oldframestarttime[FS_REG]);\n\tent->xv->lerpfrac = bound(0, 1-(ent->xv->frame1time) / le->framelerpdeltatime[FS_REG], 1);\n\n\tent->xv->baseframe = le->newframe[FST_BASE];\n\tent->xv->baseframe1time = max(0, cl.servertime - le->newframestarttime[FST_BASE]);\n\tent->xv->baseframe2 = le->oldframe[FST_BASE];\n\tent->xv->baseframe2time = max(0, cl.servertime - le->oldframestarttime[FST_BASE]);\n\tent->xv->baselerpfrac = bound(0, 1-(ent->xv->baseframe1time) / le->framelerpdeltatime[FST_BASE], 1);\n\tent->xv->basebone = le->basebone;\n\n\n\tif (nolerp)\n\t{\n\t\tent->v->origin[0] = le->neworigin[0];\n\t\tent->v->origin[1] = le->neworigin[1];\n\t\tent->v->origin[2] = le->neworigin[2];\n\t\tent->v->angles[0] = le->newangle[0];\n\t\tent->v->angles[1] = le->newangle[1];\n\t\tent->v->angles[2] = le->newangle[2];\n\t}\n\telse\n\t{\n\t\tent->v->origin[0] = le->origin[0];\n\t\tent->v->origin[1] = le->origin[1];\n\t\tent->v->origin[2] = le->origin[2];\n\t\tent->v->angles[0] = le->angles[0];\n\t\tent->v->angles[1] = le->angles[1];\n\t\tent->v->angles[2] = le->angles[2];\n\t}\n}\n\nvoid CSQC_EntStateToCSQC(unsigned int flags, float lerptime, entity_state_t *src, csqcedict_t *ent)\n{\n\tmodel_t *model;\n\tlerpents_t\t\t*le;\n\n\tle = &cl.lerpents[src->number];\n\n\tCSQC_LerpStateToCSQC(le, ent, flags & RSES_NOLERP);\n\n\n\tmodel = cl.model_precache[src->modelindex];\n\tif (!(flags & RSES_NOTRAILS))\n\t{\n\t\t//use entnum as a test to see if its new (if the old origin isn't usable)\n\t\tif (ent->xv->entnum)\n\t\t{\n\t\t\tif (model->particletrail == P_INVALID || pe->ParticleTrail (ent->v->origin, src->origin, model->particletrail, host_frametime, src->number, NULL, &(le->trailstate)))\n\t\t\t\tif (model->traildefaultindex >= 0)\n\t\t\t\t\tpe->ParticleTrailIndex(ent->v->origin, src->origin, P_INVALID, host_frametime, model->traildefaultindex, 0, &(le->trailstate));\n\t\t}\n\t}\n\n\tent->xv->entnum = src->number;\n\tent->v->modelindex = src->modelindex;\n//\tent->xv->vw_index = src->modelindex2;\n//\tent->v->flags = src->flags;\n\tent->v->effects = src->effects;\n\n//we ignore the q2 state fields\n\n\tent->v->colormap = src->colormap;\n\tent->v->skin = src->skinnum;\n\tent->xv->scale = src->scale/16.0f;\n\tent->xv->fatness = src->fatness/16.0f;\n#ifdef HEXEN2\n\tent->xv->drawflags = src->hexen2flags;\n\tent->xv->abslight = src->abslight;\n#endif\n//\tent->xv->abslight = src->abslight;\n//\tent->v->dpflags = src->dpflags;\n\tent->xv->colormod[0] = src->colormod[0]*(8/256.0f);\n\tent->xv->colormod[1] = src->colormod[1]*(8/256.0f);\n\tent->xv->colormod[2] = src->colormod[2]*(8/256.0f);\n\tent->xv->glowmod[0] = src->glowmod[0]*(8/256.0f);\n\tent->xv->glowmod[1] = src->glowmod[1]*(8/256.0f);\n\tent->xv->glowmod[2] = src->glowmod[2]*(8/256.0f);\n//\tent->xv->glow_size = src->glowsize*4;\n//\tent->xv->glow_color = src->glowcolour;\n//\tent->xv->glow_trail = !!(state->dpflags & RENDER_GLOWTRAIL);\n\tent->xv->alpha = (src->trans==255)?0:src->trans/254.0f;\n//\tent->v->solid = src->solid;\n//\tent->v->color[0] = src->light[0]/255.0;\n//\tent->v->color[1] = src->light[1]/255.0;\n//\tent->v->color[2] = src->light[2]/255.0;\n//\tent->v->light_lev = src->light[3];\n//\tent->xv->style = src->lightstyle;\n//\tent->xv->pflags = src->lightpflags;\n\n\tent->xv->tag_entity = src->tagentity;\n\tent->xv->tag_index = src->tagindex;\n\n\tif (src->solidsize == ES_SOLID_BSP)\n\t{\n\t\tent->v->solid = SOLID_BSP;\n\t\tVectorCopy(model->mins, ent->v->mins);\n\t\tVectorCopy(model->maxs, ent->v->maxs);\n\t}\n\telse if (src->solidsize)\n\t{\n\t\tent->v->solid = SOLID_BBOX;\n\t\tCOM_DecodeSize(src->solidsize, ent->v->mins, ent->v->maxs);\n\t}\n\telse\n\t\tent->v->solid = SOLID_NOT;\n\n\tent->v->movetype = src->u.q1.pmovetype;\n\tent->v->velocity[0] = src->u.q1.velocity[0] * (1/8.0);\n\tent->v->velocity[1] = src->u.q1.velocity[1] * (1/8.0);\n\tent->v->velocity[2] = src->u.q1.velocity[2] * (1/8.0);\n\n\tif (model)\n\t{\n\t\tif (!(flags & RSES_NOROTATE) && (model->flags & MF_ROTATE))\n\t\t{\n\t\t\tent->v->angles[0] = 0;\n\t\t\tent->v->angles[1] = 100*lerptime;\n\t\t\tent->v->angles[2] = 0;\n\t\t}\n\t}\n}\nvoid CSQC_PlayerStateToCSQC(int pnum, player_state_t *srcp, csqcedict_t *ent)\n{\n\tent->xv->entnum = pnum+1;\n\n\tent->v->modelindex = srcp->modelindex;\n//\tent->xv->vw_index = srcp->modelindex2;\n\tent->v->skin = srcp->skinnum;\n\n\tCSQC_LerpStateToCSQC(&cl.lerpplayers[pnum], ent, true);\n\tent->xv->lerpfrac = 1-(ent->xv->frame1time) / cl.lerpplayers[pnum].framelerpdeltatime[FS_REG];\n\tent->xv->lerpfrac = bound(0, ent->xv->lerpfrac, 1);\n\n\n\tVectorCopy(srcp->origin, ent->v->origin);\n\tVectorCopy(srcp->viewangles, ent->v->angles);\n\n\tVectorCopy(srcp->velocity, ent->v->velocity);\n\tent->v->angles[0] *= r_meshpitch.value * 0.333;\n\tent->v->colormap = pnum+1;\n\tent->xv->scale = srcp->scale;\n\t//ent->v->fatness = srcp->fatness;\n\tent->xv->alpha = (srcp->alpha==255)?0:(srcp->alpha/254.0f);\n\n//\tent->v->colormod[0] = srcp->colormod[0]*(8/256.0f);\n//\tent->v->colormod[1] = srcp->colormod[1]*(8/256.0f);\n//\tent->v->colormod[2] = srcp->colormod[2]*(8/256.0f);\n//\tent->v->effects = srcp->effects;\n}\n\nunsigned int deltaflags[MAX_PRECACHE_MODELS];\nfunc_t deltafunction[MAX_PRECACHE_MODELS];\n\ntypedef struct\n{\n\tunsigned int readpos;\t//pos\n\tunsigned int numents;\t//present\n\tunsigned int maxents;\t//buffer size\n\tstruct\n\t{\n\t\tunsigned int n;\t//don't rely on the ent->v->entnum, as csqc can corrupt that\n\t\tcsqcedict_t *e;\t//the csqc ent\n\t} *e;\n} csqcdelta_pack_t;\nstatic csqcdelta_pack_t csqcdelta_pack_new;\nstatic csqcdelta_pack_t csqcdelta_pack_old;\nfloat csqcdelta_time;\n\nstatic csqcedict_t *csqcdelta_playerents[MAX_CLIENTS];\n\nstatic void CSQC_EntRemove(csqcedict_t *ed)\n{\n\tif (csqcg.CSQC_Ent_Remove)\n\t{\n\t\t*csqcg.self = EDICT_TO_PROG(csqcprogs, ed);\n\t\tPR_ExecuteProgram(csqcprogs, csqcg.CSQC_Ent_Remove);\n\t}\n\telse\n\t{\n\t\tpe->DelinkTrailstate(&ed->trailstate);\n\t\tWorld_UnlinkEdict((wedict_t*)ed);\n\t\tED_Free (csqcprogs, (void*)ed);\n\t}\n}\n\nqboolean CSQC_DeltaPlayer(int playernum, player_state_t *state)\n{\n\tfunc_t func;\n\n\tif (!state || state->modelindex <= 0 || state->modelindex >= MAX_PRECACHE_MODELS)\n\t{\n\t\tif (csqcdelta_playerents[playernum])\n\t\t{\n\t\t\tCSQC_EntRemove(csqcdelta_playerents[playernum]);\n\t\t\tcsqcdelta_playerents[playernum] = NULL;\n\t\t}\n\t\treturn false;\n\t}\n\n\tfunc = deltafunction[state->modelindex];\n\tif (func)\n\t{\n\t\tvoid *pr_globals;\n\t\tcsqcedict_t *ent;\n\n\t\tent = csqcdelta_playerents[playernum];\n\t\tif (!ent)\n\t\t{\n\t\t\tent = (csqcedict_t *)ED_Alloc(csqcprogs, false, 0);\n\t\t\tent->xv->drawmask = MASK_DELTA;\n\t\t}\n\n\t\tCSQC_PlayerStateToCSQC(playernum, state, ent);\n\n\t\t*csqcg.self = EDICT_TO_PROG(csqcprogs, (void*)ent);\n\t\tpr_globals = PR_globals(csqcprogs, PR_CURRENT);\n\t\tG_FLOAT(OFS_PARM0) = !csqcdelta_playerents[playernum];\n\t\tPR_ExecuteProgram(csqcprogs, func);\n\n\t\tcsqcdelta_playerents[playernum] = ent;\n\n\t\treturn G_FLOAT(OFS_RETURN);\n\t}\n\telse if (csqcdelta_playerents[playernum])\n\t{\n\t\tCSQC_EntRemove(csqcdelta_playerents[playernum]);\n\t\tcsqcdelta_playerents[playernum] = NULL;\n\t}\n\treturn false;\n}\n\nvoid CSQC_DeltaStart(float time)\n{\n\tcsqcdelta_pack_t tmp;\n\tcsqcdelta_time = time;\n\n\ttmp = csqcdelta_pack_new;\n\tcsqcdelta_pack_new = csqcdelta_pack_old;\n\tcsqcdelta_pack_old = tmp;\n\n\tcsqcdelta_pack_new.numents = 0;\n\n\tcsqcdelta_pack_new.readpos = 0;\n\tcsqcdelta_pack_old.readpos = 0;\n}\nqboolean CSQC_DeltaUpdate(entity_state_t *src)\n{\n\t//FTE ensures that this function is called with increasing ent numbers each time\n\tfunc_t func;\n\tfunc = deltafunction[src->modelindex];\n\tif (func)\n\t{\n\t\tvoid *pr_globals;\n\t\tcsqcedict_t *ent, *oldent;\n\n\t\twhile (csqcdelta_pack_old.readpos < csqcdelta_pack_old.numents && csqcdelta_pack_old.e[csqcdelta_pack_old.readpos].n < src->number)\n\t\t{\n\t\t\t//this entity is stale, remove it.\n\t\t\tCSQC_EntRemove(csqcdelta_pack_old.e[csqcdelta_pack_old.readpos].e);\n\t\t\tcsqcdelta_pack_old.readpos++;\n\t\t}\n\n\t\tif (csqcdelta_pack_old.readpos >= csqcdelta_pack_old.numents)\n\t\t\toldent = NULL;\t//reached the end of the old frame's ents (so we must be new)\n\t\telse if (src->number < csqcdelta_pack_old.e[csqcdelta_pack_old.readpos].n)\n\t\t\toldent = NULL;\t//there's a gap, this one must be new.\n\t\telse\n\t\t{\t//already known.\n\t\t\toldent = csqcdelta_pack_old.e[csqcdelta_pack_old.readpos].e;\n\t\t\tcsqcdelta_pack_old.readpos++;\n\t\t}\n\n\t\tif (src->number < maxcsqcentities && csqcent[src->number])\n\t\t{\n\t\t\t//in the csqc list (don't permit in the delta list too)\n\t\t\tif (oldent)\n\t\t\t\tCSQC_EntRemove(oldent);\n\t\t\treturn false;\n\t\t}\n\n\t\tif (oldent)\n\t\t\tent = oldent;\n\t\telse\n\t\t{\n\t\t\tent = (csqcedict_t *)ED_Alloc(csqcprogs, false, 0);\n\t\t\tif (!csqc_isdarkplaces)\n\t\t\t\tent->xv->drawmask = MASK_DELTA;\n\t\t}\n\n\t\tCSQC_EntStateToCSQC(deltaflags[src->modelindex], csqcdelta_time, src, ent);\n\n\n\t\t*csqcg.self = EDICT_TO_PROG(csqcprogs, (void*)ent);\n\t\tpr_globals = PR_globals(csqcprogs, PR_CURRENT);\n\t\tG_FLOAT(OFS_PARM0) = !oldent;\n\t\tPR_ExecuteProgram(csqcprogs, func);\n\n\n\t\tif (csqcdelta_pack_new.maxents <= csqcdelta_pack_new.numents)\n\t\t{\n\t\t\tcsqcdelta_pack_new.maxents = csqcdelta_pack_new.numents + 64;\n\t\t\tcsqcdelta_pack_new.e = BZ_Realloc(csqcdelta_pack_new.e, sizeof(*csqcdelta_pack_new.e)*csqcdelta_pack_new.maxents);\n\t\t}\n\t\tcsqcdelta_pack_new.e[csqcdelta_pack_new.numents].e = ent;\n\t\tcsqcdelta_pack_new.e[csqcdelta_pack_new.numents].n = src->number;\n\t\tcsqcdelta_pack_new.numents++;\n\n\t\treturn G_FLOAT(OFS_RETURN);\n\t}\n\treturn false;\n}\n\nvoid CSQC_DeltaEnd(void)\n{\n\t//remove any unreferenced ents stuck on the end\n\twhile (csqcdelta_pack_old.readpos < csqcdelta_pack_old.numents)\n\t{\n\t\tCSQC_EntRemove(csqcdelta_pack_old.e[csqcdelta_pack_old.readpos].e);\n\t\tcsqcdelta_pack_old.readpos++;\n\t}\n}\n\nstatic void QCBUILTIN PF_DeltaListen(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint i;\n\tconst char *mname = PR_GetStringOfs(prinst, OFS_PARM0);\n\tfunc_t func = G_INT(OFS_PARM1);\n\tunsigned int flags = G_FLOAT(OFS_PARM2);\n\n\tif (csqc_nogameaccess)\n\t{\n\t\tcsqc_deprecated(\"PF_DeltaListen: game access is blocked\");\n\t\treturn;\n\t}\n\n\tif (!prinst->GetFunctionInfo(prinst, func, NULL, NULL, NULL, NULL, 0))\n\t{\n\t\tCon_Printf(\"PF_DeltaListen: Bad function index\\n\");\n\t\treturn;\n\t}\n\n\tif (!strcmp(mname, \"*\"))\n\t{\n\t\t//yes, even things that are not allocated yet\n\t\tfor (i = 0; i < MAX_PRECACHE_MODELS; i++)\n\t\t{\n\t\t\tdeltafunction[i] = func;\n\t\t\tdeltaflags[i] = flags;\n\t\t}\n\t}\n\telse\n\t{\n\t\tfor (i = 1; i < MAX_PRECACHE_MODELS; i++)\n\t\t{\n\t\t\tif (!cl.model_name[i])\n\t\t\t\tbreak;\n\t\t\tif (!strcmp(cl.model_name[i], mname))\n\t\t\t{\n\t\t\t\tdeltafunction[i] = func;\n\t\t\t\tdeltaflags[i] = flags;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n}\n\nstatic void AngleVectorsIndex (const vec3_t angles, int modelindex, vec3_t forward, vec3_t right, vec3_t up)\n{\n\tvec3_t fixedangles;\n\tfixedangles[0] = angles[0] * CSQC_PitchScaleForModelIndex(modelindex);\n\tfixedangles[1] = angles[1];\n\tfixedangles[2] = angles[2];\n\tAngleVectors(fixedangles, forward, right, up);\n}\nstatic void QCBUILTIN PF_getentity(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint entnum = G_FLOAT(OFS_PARM0);\n\tint fldnum = G_FLOAT(OFS_PARM1);\n\tlerpents_t *le;\n\tentity_state_t *es;\n\n\tif (csqc_nogameaccess)\n\t{\n\t\tG_FLOAT(OFS_RETURN+0) = 0;\n\t\tG_FLOAT(OFS_RETURN+1) = 0;\n\t\tG_FLOAT(OFS_RETURN+2) = 0;\n\t\treturn;\n\t}\n\n\tif (fldnum == GE_MAXENTS)\n\t{\n\t\tG_FLOAT(OFS_RETURN) = cl.maxlerpents;\n\t\treturn;\n\t}\n\n\tif (entnum > 0 && entnum <= cl.allocated_client_slots && cl.lerpplayers[entnum-1].sequence == cl.lerpentssequence)\n\t{\n\t\tplayer_state_t *ps = &cl.inframes[cl.validsequence&UPDATE_MASK].playerstate[entnum-1];\n\t\tle = &cl.lerpplayers[entnum-1];\n\n\t\tswitch(fldnum)\n\t\t{\n\t\tcase GE_ACTIVE:\n\t\t\tG_FLOAT(OFS_RETURN) = 1;\n\t\t\tbreak;\n\n\t\tcase GE_ORIGIN:\n\t\t\t/*lerped position*/\n\t\t\tVectorCopy(le->origin, G_VECTOR(OFS_RETURN));\n\t\t\tbreak;\n\t\tcase GE_SCALE:\n\t\t\tG_FLOAT(OFS_RETURN) = ps->scale / 16.0f;\n\t\t\tbreak;\n\t\tcase GE_ALPHA:\n\t\t\tG_FLOAT(OFS_RETURN) = ps->alpha / 255.0f;\n\t\t\tbreak;\n\t\tcase GE_COLORMOD:\n\t\t\tG_FLOAT(OFS_RETURN+0) = ps->colourmod[0] / 8.0f;\n\t\t\tG_FLOAT(OFS_RETURN+1) = ps->colourmod[1] / 8.0f;\n\t\t\tG_FLOAT(OFS_RETURN+2) = ps->colourmod[2] / 8.0f;\n\t\t\tbreak;\n\t\tcase GE_SKIN:\n\t\t\tG_FLOAT(OFS_RETURN) = ps->skinnum;\n\t\t\tbreak;\n\t\tcase GE_MINS:\n\t\t\tVectorCopy(ps->szmins, G_VECTOR(OFS_RETURN));\n\t\t\tbreak;\n\t\tcase GE_MAXS:\n\t\t\tVectorCopy(ps->szmaxs, G_VECTOR(OFS_RETURN));\n\t\t\tbreak;\n\n\t\tcase GE_ABSMIN:\n\t\t\tVectorAdd(ps->szmins, le->origin, G_VECTOR(OFS_RETURN));\n\t\t\tbreak;\n\t\tcase GE_ABSMAX:\n\t\t\tVectorAdd(ps->szmaxs, le->origin, G_VECTOR(OFS_RETURN));\n\t\t\tbreak;\n\t\tcase GE_ORIGINANDVECTORS:\n\t\t\tVectorCopy(le->origin, G_VECTOR(OFS_RETURN));\n\t\t\tAngleVectorsIndex(le->angles, ps->modelindex, csqcg.v_forward, csqcg.v_right, csqcg.v_up);\n\t\t\tbreak;\n\t\tcase GE_FORWARD:\n\t\t\tAngleVectorsIndex(le->angles, ps->modelindex, G_VECTOR(OFS_RETURN), NULL, NULL);\n\t\t\tbreak;\n\t\tcase GE_RIGHT:\n\t\t\tAngleVectorsIndex(le->angles, ps->modelindex, NULL, G_VECTOR(OFS_RETURN), NULL);\n\t\t\tbreak;\n\t\tcase GE_UP:\n\t\t\tAngleVectorsIndex(le->angles, ps->modelindex, NULL, NULL, G_VECTOR(OFS_RETURN));\n\t\t\tbreak;\n\t\tcase GE_PANTSCOLOR:\n\t\t\tG_FLOAT(OFS_RETURN) = cl.players[entnum-1].dbottomcolor;\n\t\t\tbreak;\n\t\tcase GE_SHIRTCOLOR:\n\t\t\tG_FLOAT(OFS_RETURN) = cl.players[entnum-1].dtopcolor;\n\t\t\tbreak;\n\t\tcase GE_LIGHT:\n\t\t\tG_FLOAT(OFS_RETURN) = 0;\n\t\t\tbreak;\n\n\t\tcase GE_MODELINDEX:\n\t\t\tG_FLOAT(OFS_RETURN) = ps->modelindex;\n\t\t\tbreak;\n\t\tcase GE_MODELINDEX2:\n\t\t\tG_FLOAT(OFS_RETURN) = ps->command.impulse;\t//evil hack\n\t\t\tbreak;\n\t\tcase GE_EFFECTS:\n\t\t\tG_FLOAT(OFS_RETURN) = ps->effects;\n\t\t\tbreak;\n\t\tcase GE_FRAME:\n\t\t\tG_FLOAT(OFS_RETURN) = ps->frame;\n\t\t\tbreak;\n\t\tcase GE_ANGLES:\n\t\t\tVectorCopy(le->angles, G_VECTOR(OFS_RETURN));\n\t\t\tbreak;\n\t\tcase GE_FATNESS:\n\t\t\tG_FLOAT(OFS_RETURN) = ps->fatness;\n\t\t\tbreak;\n\t\tcase GE_DRAWFLAGS:\n\t\t\tG_FLOAT(OFS_RETURN) = SCALE_ORIGIN_ORIGIN;\n\t\t\tbreak;\n\t\tcase GE_ABSLIGHT:\n\t\t\tG_FLOAT(OFS_RETURN) = 0;\n\t\t\tbreak;\n\t\tcase GE_GLOWMOD:\n\t\t\tVectorSet(G_VECTOR(OFS_RETURN), 1, 1, 1);\n\t\t\tbreak;\n\t\tcase GE_GLOWSIZE:\n\t\t\tG_FLOAT(OFS_RETURN) = 0;\n\t\t\tbreak;\n\t\tcase GE_GLOWCOLOUR:\n\t\t\tG_FLOAT(OFS_RETURN) = 0;\n\t\t\tbreak;\n\t\tcase GE_RTSTYLE:\n\t\t\tG_FLOAT(OFS_RETURN) = 0;\n\t\t\tbreak;\n\t\tcase GE_RTPFLAGS:\n\t\t\tG_FLOAT(OFS_RETURN) = 0;\n\t\t\tbreak;\n\t\tcase GE_RTCOLOUR:\n\t\t\tVectorSet(G_VECTOR(OFS_RETURN), 1, 1, 1);\n\t\t\tbreak;\n\t\tcase GE_RTRADIUS:\n\t\t\tG_FLOAT(OFS_RETURN) = 0;\n\t\t\tbreak;\n\t\tcase GE_TAGENTITY:\n\t\t\tG_FLOAT(OFS_RETURN) = 0;\n\t\t\tbreak;\n\t\tcase GE_TAGINDEX:\n\t\t\tG_FLOAT(OFS_RETURN) = 0;\n\t\t\tbreak;\n\t\tcase GE_GRAVITYDIR:\n\t\t\tVectorCopy(ps->gravitydir, G_VECTOR(OFS_RETURN));\n\t\t\tbreak;\n\t\tcase GE_TRAILEFFECTNUM:\n\t\t\tG_FLOAT(OFS_RETURN) = 0;\n\t\t\tbreak;\n\n\t\tdefault:\n\t\t\tVectorCopy(vec3_origin, G_VECTOR(OFS_RETURN));\n\t\t\tbreak;\n\t\t}\n\t\treturn;\n\t}\n\n\tif (entnum >= cl.maxlerpents || !cl.lerpentssequence || cl.lerpents[entnum].sequence != cl.lerpentssequence)\n\t{\n\t\tif (fldnum != GE_ACTIVE)\n\t\t\tCon_DPrintf(\"PF_getentity: entity %i is not valid\\n\", entnum);\n\t\tVectorCopy(vec3_origin, G_VECTOR(OFS_RETURN));\n\t\treturn;\n\t}\n\tle = &cl.lerpents[entnum];\n\tes = le->entstate;\n\tswitch(fldnum)\n\t{\n\tcase GE_ACTIVE:\n\t\tG_FLOAT(OFS_RETURN) = 1;\n\t\tbreak;\n\tcase GE_ORIGIN:\n\t\t/*lerped position*/\n\t\tVectorCopy(le->origin, G_VECTOR(OFS_RETURN));\n\t\tbreak;\n\tcase GE_SCALE:\n\t\tG_FLOAT(OFS_RETURN) = es->scale / 16.0f;\n\t\tbreak;\n\tcase GE_ALPHA:\n\t\tG_FLOAT(OFS_RETURN) = es->trans / 255.0f;\n\t\tbreak;\n\tcase GE_COLORMOD:\n\t\tG_FLOAT(OFS_RETURN+0) = es->colormod[0] / 8.0f;\n\t\tG_FLOAT(OFS_RETURN+1) = es->colormod[1] / 8.0f;\n\t\tG_FLOAT(OFS_RETURN+2) = es->colormod[2] / 8.0f;\n\t\tbreak;\n\tcase GE_SKIN:\n\t\tG_FLOAT(OFS_RETURN) = es->skinnum;\n\t\tbreak;\n\tcase GE_MINS:\n\t\t{\n\t\t\tvec3_t maxs;\n\t\t\tCOM_DecodeSize(es->solidsize, G_VECTOR(OFS_RETURN), maxs);\n\t\t}\n\t\tbreak;\n\tcase GE_MAXS:\n\t\t{\n\t\t\tvec3_t mins;\n\t\t\tCOM_DecodeSize(es->solidsize, mins, G_VECTOR(OFS_RETURN));\n\t\t}\n\t\tbreak;\n\n\tcase GE_ABSMIN:\n\t\t{\n\t\t\tvec3_t maxs;\n\t\t\tCOM_DecodeSize(es->solidsize, G_VECTOR(OFS_RETURN), maxs);\n\t\t\tVectorAdd(G_VECTOR(OFS_RETURN), le->origin, G_VECTOR(OFS_RETURN));\n\t\t}\n\t\tbreak;\n\tcase GE_ABSMAX:\n\t\t{\n\t\t\tvec3_t mins;\n\t\t\tCOM_DecodeSize(es->solidsize, mins, G_VECTOR(OFS_RETURN));\n\t\t\tVectorAdd(G_VECTOR(OFS_RETURN), le->origin, G_VECTOR(OFS_RETURN));\n\t\t}\n\t\tbreak;\n\tcase GE_ORIGINANDVECTORS:\n\t\tVectorCopy(le->origin, G_VECTOR(OFS_RETURN));\n\t\tAngleVectorsIndex(le->angles, es->modelindex, csqcg.v_forward, csqcg.v_right, csqcg.v_up);\n\t\tbreak;\n\tcase GE_FORWARD:\n\t\tAngleVectorsIndex(le->angles, es->modelindex, G_VECTOR(OFS_RETURN), NULL, NULL);\n\t\tbreak;\n\tcase GE_RIGHT:\n\t\tAngleVectorsIndex(le->angles, es->modelindex, NULL, G_VECTOR(OFS_RETURN), NULL);\n\t\tbreak;\n\tcase GE_UP:\n\t\tAngleVectorsIndex(le->angles, es->modelindex, NULL, NULL, G_VECTOR(OFS_RETURN));\n\t\tbreak;\n\tcase GE_PANTSCOLOR:\n\t\tif (es->colormap <= cl.allocated_client_slots && !(es->dpflags & RENDER_COLORMAPPED))\n\t\t\tG_FLOAT(OFS_RETURN) = cl.players[es->colormap].dbottomcolor;\n\t\telse\n\t\t\tG_FLOAT(OFS_RETURN) = es->colormap & 15;\n\t\tbreak;\n\tcase GE_SHIRTCOLOR:\n\t\tif (es->colormap <= cl.allocated_client_slots && !(es->dpflags & RENDER_COLORMAPPED))\n\t\t\tG_FLOAT(OFS_RETURN) = cl.players[es->colormap].dtopcolor;\n\t\telse\n\t\t\tG_FLOAT(OFS_RETURN) = es->colormap>>4;\n\t\tbreak;\n\tcase GE_LIGHT:\n\t\tG_FLOAT(OFS_RETURN) = 0;\n\t\tbreak;\n\n\tcase GE_MODELINDEX:\n\t\tG_FLOAT(OFS_RETURN) = es->modelindex;\n\t\tbreak;\n\tcase GE_MODELINDEX2:\n\t\tG_FLOAT(OFS_RETURN) = es->modelindex2;\n\t\tbreak;\n\tcase GE_EFFECTS:\n\t\tG_FLOAT(OFS_RETURN) = es->effects;\n\t\tbreak;\n\tcase GE_FRAME:\n\t\tG_FLOAT(OFS_RETURN) = es->frame;\n\t\tbreak;\n\tcase GE_ANGLES:\n\t\tVectorCopy(le->angles, G_VECTOR(OFS_RETURN));\n\t\tbreak;\n\tcase GE_FATNESS:\n\t\tG_FLOAT(OFS_RETURN) = es->fatness;\n\t\tbreak;\n\tcase GE_DRAWFLAGS:\n\t\tG_FLOAT(OFS_RETURN) = es->hexen2flags;\n\t\tbreak;\n\tcase GE_ABSLIGHT:\n\t\tG_FLOAT(OFS_RETURN) = es->abslight;\n\t\tbreak;\n\tcase GE_GLOWMOD:\n\t\tVectorScale(es->glowmod, 1/8.0, G_VECTOR(OFS_RETURN));\n\t\tbreak;\n\tcase GE_GLOWSIZE:\n\t\tG_FLOAT(OFS_RETURN) = es->glowsize;\n\t\tbreak;\n\tcase GE_GLOWCOLOUR:\n\t\tG_FLOAT(OFS_RETURN) = es->glowcolour;\n\t\tbreak;\n\tcase GE_RTSTYLE:\n\t\tG_FLOAT(OFS_RETURN) = es->lightstyle;\n\t\tbreak;\n\tcase GE_RTPFLAGS:\n\t\tG_FLOAT(OFS_RETURN) = es->lightpflags;\n\t\tbreak;\n\tcase GE_RTCOLOUR:\n\t\tVectorScale(es->light, 1/1024.0, G_VECTOR(OFS_RETURN));\n\t\tbreak;\n\tcase GE_RTRADIUS:\n\t\tG_FLOAT(OFS_RETURN) = es->light[3];\n\t\tbreak;\n\tcase GE_TAGENTITY:\n\t\tG_FLOAT(OFS_RETURN) = es->tagentity;\n\t\tbreak;\n\tcase GE_TAGINDEX:\n\t\tG_FLOAT(OFS_RETURN) = es->tagindex;\n\t\tbreak;\n\tcase GE_GRAVITYDIR:\n\t\t{\n\t\t\tvec3_t a;\n\t\t\ta[0] = ((192+es->u.q1.gravitydir[0])/256.0f) * 360;\n\t\t\ta[1] = (es->u.q1.gravitydir[1]/256.0f) * 360;\n\t\t\ta[2] = 0;\n\t\t\tAngleVectors(a, G_VECTOR(OFS_RETURN), NULL, NULL);\n\t\t}\n\t\tbreak;\n\tcase GE_TRAILEFFECTNUM:\n\t\tG_FLOAT(OFS_RETURN) = es->u.q1.traileffectnum;\n\t\tbreak;\n\n\tdefault:\n\t\tCon_Printf(\"PF_getentity: field %i is not supported\\n\", fldnum);\n\t\tVectorCopy(vec3_origin, G_VECTOR(OFS_RETURN));\n\t\tbreak;\n\t}\n}\n\n\nstatic void QCBUILTIN PF_cs_getplayerstat(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tunsigned int playernum = G_FLOAT(OFS_PARM0);\n\tunsigned int statnum = G_FLOAT(OFS_PARM1);\n\tunsigned int stattype = G_FLOAT(OFS_PARM2);\n\tunsigned int i, j;\n\tif (playernum >= cl.allocated_client_slots || statnum >= MAX_CL_STATS)\n\t\tstattype = ev_void;\n\n\tswitch(stattype)\n\t{\n\tdefault:\n\tcase ev_void:\n\t\tG_FLOAT(OFS_RETURN+0) = 0;\n\t\tG_FLOAT(OFS_RETURN+1) = 0;\n\t\tG_FLOAT(OFS_RETURN+2) = 0;\n\t\tbreak;\n\n\tcase ev_integer:\n\tcase ev_uint:\n\tcase ev_field:\t\t//Hopefully NOT useful, certainly not reliable\n\tcase ev_function:\t//Hopefully NOT useful\n\tcase ev_pointer:\t//NOT useful in a networked capacity.\n\t\tG_INT(OFS_RETURN) = cl.players[playernum].stats[statnum];\n\t\tbreak;\n\n\tcase ev_int64:\t\t//lamely takes two consecutive stats.\n\tcase ev_uint64:\n\t\tG_UINT64(OFS_RETURN) =   (puint64_t)((statnum+0 >= MAX_CL_STATS)?0:cl.players[playernum].stats[statnum+0]) |\n\t\t\t\t\t\t\t\t((puint64_t)((statnum+1 >= MAX_CL_STATS)?0:cl.players[playernum].stats[statnum+1]) <<32);\n\t\tbreak;\n\n\tcase ev_float:\n\t\tG_FLOAT(OFS_RETURN) = cl.players[playernum].statsf[statnum];\n\t\tbreak;\n\tcase ev_double:\t//lame truncation for network.\n\t\tG_DOUBLE(OFS_RETURN) = cl.players[playernum].statsf[statnum];\n\t\tbreak;\n\tcase ev_vector:\n\t\tG_FLOAT(OFS_RETURN+0) = (statnum+0 >= MAX_CL_STATS)?0:cl.players[playernum].statsf[statnum+0];\n\t\tG_FLOAT(OFS_RETURN+1) = (statnum+1 >= MAX_CL_STATS)?0:cl.players[playernum].statsf[statnum+1];\n\t\tG_FLOAT(OFS_RETURN+2) = (statnum+2 >= MAX_CL_STATS)?0:cl.players[playernum].statsf[statnum+2];\n\t\tbreak;\n\tcase ev_entity:\n\t\tj = cl.players[playernum].stats[statnum];\n\t\tif (j < maxcsqcentities && csqcent[j])\n\t\t\tG_INT(OFS_RETURN) = EDICT_TO_PROG(csqcprogs, csqcent[j]);\n\t\telse if (j <= cl.allocated_client_slots && j > 0 && csqcdelta_playerents[j])\n\t\t\tG_INT(OFS_RETURN) = EDICT_TO_PROG(csqcprogs, csqcdelta_playerents[j]);\n\t\telse\n\t\t{\n\t\t\tG_INT(OFS_RETURN) = 0;\n\t\t\t//scan for the delta entity reference.\n\t\t\tfor (i = 0; i < csqcdelta_pack_new.numents; i++)\n\t\t\t{\n\t\t\t\tif (csqcdelta_pack_old.e[i].n == j && csqcdelta_pack_old.e[i].e)\n\t\t\t\t{\n\t\t\t\t\tG_INT(OFS_RETURN) = EDICT_TO_PROG(csqcprogs, csqcdelta_pack_old.e[i].e);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tbreak;\n\tcase ev_string:\n\t\tG_INT(OFS_RETURN) = 0;\t//FIXME: no info, these are not currently tracked in mvds apparently.\n\t\tbreak;\n\t}\n}\n\nvoid CL_CalcCrouch (playerview_t *pv);\nstatic void QCBUILTIN PF_V_CalcRefdef(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\t//this function is essentially an overcomplicated way to shirk from defining your own view bobbing.\n\tcsqcedict_t *ent = (csqcedict_t*)G_EDICT(prinst, OFS_PARM0);\n\tvec3_t savedvel;\n\t/*enum\n\t{\n\t\tTELEPORTED,\n\t\tJUMPING,\n\t\tDEAD,\n\t\tINTERMISSION\n\t} flags = G_FLOAT(OFS_PARM1);*/\n//\tcsqc_deprecated(\"V_CalcRefdef has too much undefined behaviour.\\n\");\n//\tif (ent->xv->entnum >= 1 && ent->xv->entnum <= MAX_CLIENTS)\n//\t\tCSQC_ChangeLocalPlayer(ent->xv->entnum-1);\n\n\t/* xonotic requires:\n\t   cl_followmodel\n\t   cl_smoothviewheight\n\t   cl_bobfall\n\t*/\n\n\tcsqc_rebuildmatricies = true;\n\n\tCL_DecayLights ();\n\n//\tCL_ClearEntityLists();\n\n\tV_ClearRefdef(csqc_playerview);\n\tr_refdef.drawsbar = false;\t//csqc defaults to no sbar.\n\tr_refdef.drawcrosshair = false;\n\n\tVectorCopy(csqc_playerview->simvel, savedvel);\n\tVectorCopy(ent->v->origin, csqc_playerview->simorg);\n\tVectorCopy (ent->v->velocity, csqc_playerview->simvel);\n\tcsqc_playerview->onground = !!((int)ent->v->flags & FL_ONGROUND);\n\tCL_CalcCrouch (csqc_playerview);\n\tV_CalcRefdef(csqc_playerview);\t//set up the defaults\n\n\tVectorCopy(savedvel, csqc_playerview->simvel);\n}\n\nstatic void QCBUILTIN PF_getlocationname(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tfloat *loc = G_VECTOR(OFS_PARM0);\n\tRETURN_TSTRING(TP_LocationName(loc));\n}\n\n#if 1\n//static void QCBUILTIN PF_ReadServerEntityState(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n//{\n//}\n#else\npacket_entities_t *CL_ProcessPacketEntities(float *servertime, qboolean nolerp);\nstatic void QCBUILTIN PF_ReadServerEntityState(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\t//read the arguments the csqc gave us\n\tunsigned int flags = G_FLOAT(OFS_PARM0);\n\tfloat servertime = G_FLOAT(OFS_PARM1);\n\n\t//locals\n\tpacket_entities_t *pack;\n\tcsqcedict_t *ent;\n\tentity_state_t *src;\n\tunsigned int i;\n\tlerpents_t\t\t*le;\n\tcsqcedict_t *oldent;\n\toldcsqcpack_t *oldlist, *newlist;\n\tint oldidx = 0, newidx = 0;\n\tmodel_t *model;\n\tplayer_state_t *srcp;\n\n\t//setup\n\tservertime += cl.servertime;\n\tpack = CL_ProcessPacketEntities(&servertime, (flags & RSES_NOLERP));\n\tif (!pack)\n\t\treturn;\t//we're lagging. can't do anything, just don't update\n\n\tfor (i = 0; i < cl.allocated_client_slots; i++)\n\t{\n\t\tsrcp = &cl.frames[cl.validsequence&UPDATE_MASK].playerstate[i];\n\t\tent = deltaedplayerents[i];\n\t\tif (srcp->messagenum == cl.validsequence && (i+1 >= maxcsqcentities || !csqcent[i+1]))\n\t\t{\n\t\t\tif (!ent)\n\t\t\t{\n\t\t\t\tent = (csqcedict_t *)ED_Alloc(prinst);\n\t\t\t\tdeltaedplayerents[i] = ent;\n\t\t\t\tG_FLOAT(OFS_PARM0) = true;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tG_FLOAT(OFS_PARM0) = false;\n\t\t\t}\n\n\t\t\tCSQC_PlayerStateToCSQC(i, srcp, ent);\n\n\t\t\tif (csqcg.delta_update)\n\t\t\t{\n\t\t\t\t*csqcg.self = EDICT_TO_PROG(prinst, (void*)ent);\n\t\t\t\tPR_ExecuteProgram(prinst, csqcg.delta_update);\n\t\t\t}\n\t\t}\n\t\telse if (ent)\n\t\t{\n\t\t\t*csqcg.self = EDICT_TO_PROG(prinst, (void*)ent);\n\t\t\tPR_ExecuteProgram(prinst, csqcg.delta_remove);\n\t\t\tdeltaedplayerents[i] = NULL;\n\t\t}\n\t}\n\n\toldlist = &loadedcsqcpack[loadedcsqcpacknum];\n\tloadedcsqcpacknum ^= 1;\n\tnewlist = &loadedcsqcpack[loadedcsqcpacknum];\n\tnewlist->numents = 0;\n\n\tfor (i = 0; i < pack->num_entities; i++)\n\t{\n\t\tsrc = &pack->entities[i];\n// CL_LinkPacketEntities\n\n#ifdef warningmsg\n#pragma warningmsg(\"what to do here?\")\n#endif\n//\t\tif (csqcent[src->number])\n//\t\t\tcontinue;\t//don't add the entity if we have one sent specially via csqc protocols.\n\n\t\tif (oldidx == oldlist->numents)\n\t\t{\t//reached the end of the old frame's ents\n\t\t\toldent = NULL;\n\t\t}\n\t\telse\n\t\t{\n\t\t\twhile (oldidx < oldlist->numents && oldlist->entnum[oldidx] < src->number)\n\t\t\t{\n\t\t\t\t//this entity is stale, remove it.\n\t\t\t\toldent = oldlist->entptr[oldidx];\n\t\t\t\t*csqcg.self = EDICT_TO_PROG(prinst, (void*)oldent);\n\t\t\t\tPR_ExecuteProgram(prinst, csqcg.delta_remove);\n\t\t\t\toldidx++;\n\t\t\t}\n\n\t\t\tif (src->number < oldlist->entnum[oldidx])\n\t\t\t\toldent = NULL;\n\t\t\telse\n\t\t\t{\n\t\t\t\toldent = oldlist->entptr[oldidx];\n\t\t\t\toldidx++;\n\t\t\t}\n\t\t}\n\n\t\tif (src->number < maxcsqcentities && csqcent[src->number])\n\t\t{\n\t\t\t//in the csqc list\n\t\t\tif (oldent)\n\t\t\t{\n\t\t\t\t*csqcg.self = EDICT_TO_PROG(prinst, (void*)oldent);\n\t\t\t\tPR_ExecuteProgram(prinst, csqcg.delta_remove);\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\n\t\t//note: we don't delta the state here. we just replace the old.\n\t\t//its already lerped\n\n\t\tif (oldent)\n\t\t\tent = oldent;\n\t\telse\n\t\t\tent = (csqcedict_t *)ED_Alloc(prinst);\n\n\t\tCSQC_EntStateToCSQC(flags, servertime, src, ent);\n\n\t\tif (csqcg.delta_update)\n\t\t{\n\t\t\t*csqcg.self = EDICT_TO_PROG(prinst, (void*)ent);\n\t\t\tG_FLOAT(OFS_PARM0) = !oldent;\n\t\t\tPR_ExecuteProgram(prinst, csqcg.delta_update);\n\t\t}\n\n\t\tif (newlist->maxents <= newidx)\n\t\t{\n\t\t\tnewlist->maxents = newidx + 64;\n\t\t\tnewlist->entptr = BZ_Realloc(newlist->entptr, sizeof(*newlist->entptr)*newlist->maxents);\n\t\t\tnewlist->entnum = BZ_Realloc(newlist->entnum, sizeof(*newlist->entnum)*newlist->maxents);\n\t\t}\n\t\tnewlist->entptr[newidx] = ent;\n\t\tnewlist->entnum[newidx] = src->number;\n\t\tnewidx++;\n\n\t}\n\n\t//remove any unreferenced ents stuck on the end\n\twhile (oldidx < oldlist->numents)\n\t{\n\t\toldent = oldlist->entptr[oldidx];\n\t\t*csqcg.self = EDICT_TO_PROG(prinst, (void*)oldent);\n\t\tPR_ExecuteProgram(prinst, csqcg.delta_remove);\n\t\toldidx++;\n\t}\n\n\tnewlist->numents = newidx;\n}\n#endif\n//be careful to not touch the resource unless we're meant to, to avoid stalling\nstatic void QCBUILTIN PF_resourcestatus(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint restype = G_FLOAT(OFS_PARM0);\n\tint doload = G_FLOAT(OFS_PARM1);\n\tconst char *resname = PR_GetStringOfs(prinst, OFS_PARM2);\n\tint idx, idx2;\n\tmodel_t *mod;\n\timage_t *img;\n//\tshader_t *sh;\n\tsfx_t *sfx;\n\tG_FLOAT(OFS_RETURN) = RESSTATE_NOTKNOWN;\n\tswitch(restype)\n\t{\n\tcase RESTYPE_MODEL:\n\t\tidx = CS_FindModel(resname, &idx2);\n\t\tif (idx < 0)\n\t\t{\n\t\t\tmod = cl.model_csqcprecache[-idx];\n\t\t\tif (!cl.model_csqcprecache[-idx] && doload && cl.model_csqcname[-idx])\n\t\t\t\tmod = cl.model_csqcprecache[-idx] = Mod_ForName(Mod_FixName(cl.model_csqcname[-idx], cl.model_name[1]), MLV_WARN);\n\t\t}\n\t\telse if (idx > 0)\n\t\t{\n\t\t\tmod = cl.model_precache[idx];\n\t\t\tif (!cl.model_precache[idx] && doload && cl.model_name[idx])\n\t\t\t\tmod = cl.model_precache[idx] = Mod_ForName(Mod_FixName(cl.model_name[idx], cl.model_name[1]), MLV_WARN);\n\t\t}\n\t\telse\n\t\t\treturn;\n\n\t\tif (!mod)\n\t\t\tG_FLOAT(OFS_RETURN) = RESSTATE_NOTLOADED;\n\t\telse\n\t\t{\n\t\t\tif (doload && mod->loadstate == MLS_NOTLOADED)\n\t\t\t\tMod_LoadModel (mod, MLV_SILENT);\t//should avoid blocking.\n\t\t\tswitch(mod->loadstate)\n\t\t\t{\n\t\t\tdefault:\n\t\t\tcase MLS_NOTLOADED:\n\t\t\t\tG_FLOAT(OFS_RETURN) = RESSTATE_NOTLOADED;\n\t\t\t\tbreak;\n\t\t\tcase MLS_LOADING:\n\t\t\t\tG_FLOAT(OFS_RETURN) = RESSTATE_LOADING;\n\t\t\t\tbreak;\n\t\t\tcase MLS_LOADED:\n\t\t\t\tG_FLOAT(OFS_RETURN) = RESSTATE_LOADED;\n\t\t\t\tbreak;\n\t\t\tcase MLS_FAILED:\n\t\t\t\tG_FLOAT(OFS_RETURN) = RESSTATE_FAILED;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\treturn;\n\tcase RESTYPE_SOUND:\n\t\tsfx = NULL;\n\t\tfor (idx=1 ; idx<MAX_PRECACHE_SOUNDS; idx++)\n\t\t{\n\t\t\tif (!strcmp(cl.sound_name[idx], resname))\n\t\t\t{\n\t\t\t\tsfx = cl.sound_precache[idx];\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (!sfx)\n\t\t\tsfx = S_FindName(resname, doload, false);\n\t\tif (!sfx)\n\t\t\tG_FLOAT(OFS_RETURN) = RESSTATE_NOTKNOWN;\n\t\telse\n\t\t{\n\t\t\tif (doload && sfx->loadstate == SLS_NOTLOADED)\n\t\t\t\tS_LoadSound(sfx, true);\n\t\t\tswitch(sfx->loadstate)\n\t\t\t{\n\t\t\tcase SLS_NOTLOADED:\n\t\t\t\tG_FLOAT(OFS_RETURN) = RESSTATE_NOTLOADED;\n\t\t\t\tbreak;\n\t\t\tcase SLS_LOADING:\n\t\t\t\tG_FLOAT(OFS_RETURN) = RESSTATE_LOADING;\n\t\t\t\tbreak;\n\t\t\tcase SLS_LOADED:\n\t\t\t\tG_FLOAT(OFS_RETURN) = RESSTATE_LOADED;\n\t\t\t\tbreak;\n\t\t\tcase SLS_FAILED:\n\t\t\t\tG_FLOAT(OFS_RETURN) = RESSTATE_FAILED;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tbreak;\n/*\n\tcase RESTYPE_PARTICLE:\n\t\tG_FLOAT(OFS_RETURN) = RESSTATE_NOTKNOWN;\n\t\tbreak;\n\tcase RESTYPE_SHADER:\n\t\tsh = R_RegisterCustom(resname, 0, NULL, NULL);\n\t\tif (sh)\n\t\t{\n\t\t\t//FIXME: scan through the images.\n\t\t}\n\t\telse\n\t\t\tG_FLOAT(OFS_RETURN) = RESSTATE_NOTKNOWN;\n\t\tbreak;\n\tcase RESTYPE_SKIN:\n\t\tG_FLOAT(OFS_RETURN) = RESSTATE_NOTKNOWN;\n\t\tbreak;\n*/\n\tcase RESTYPE_TEXTURE:\n\t\tG_FLOAT(OFS_RETURN) = RESSTATE_NOTKNOWN;\n\t\timg = Image_FindTexture(resname, NULL, 0);\n\t\tif (!img)\n\t\t\tG_FLOAT(OFS_RETURN) = RESSTATE_NOTKNOWN;\n\t\telse\n\t\t{\n\t\t\tswitch(img->status)\n\t\t\t{\n\t\t\tcase TEX_NOTLOADED:\n\t\t\t\tG_FLOAT(OFS_RETURN) = RESSTATE_NOTLOADED;\n\t\t\t\tbreak;\n\t\t\tcase TEX_LOADING:\n\t\t\t\tG_FLOAT(OFS_RETURN) = RESSTATE_LOADING;\n\t\t\t\tbreak;\n\t\t\tcase TEX_LOADED:\n\t\t\t\tG_FLOAT(OFS_RETURN) = RESSTATE_LOADED;\n\t\t\t\tbreak;\n\t\t\tcase TEX_FAILED:\n\t\t\t\tG_FLOAT(OFS_RETURN) = RESSTATE_FAILED;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tbreak;\n\tdefault:\n\t\tG_FLOAT(OFS_RETURN) = RESSTATE_UNSUPPORTED;\n\t\tbreak;\n\t}\n}\n\n/*static void PF_cs_clipboard_got(void *ctx, const char *utf8)\n{\n\tvoid *pr_globals;\n\tunsigned int unicode;\n\tint error;\n\n\twhile (*utf8)\n\t{\n\t\tunicode = utf8_decode(&error, utf8, &utf8);\n\t\tif (error)\n\t\t\tunicode = 0xfffdu;\n\n\t\tif (!csqcprogs || !csqcg.input_event || CSIE_PASTE >= dpcompat_csqcinputeventtypes.ival)\n\t\t\treturn;\n\t#ifdef TEXTEDITOR\n\t\tif (editormodal)\n\t\t\treturn;\n\t#endif\n\n\t\tpr_globals = PR_globals(csqcprogs, PR_CURRENT);\n\t\tG_FLOAT(OFS_PARM0) = CSIE_PASTE;\n\t\tG_FLOAT(OFS_PARM1) = 0;\n\t\tG_FLOAT(OFS_PARM2) = unicode;\n\t\tG_FLOAT(OFS_PARM3) = 0;\n\n\t\tqcinput_scan = G_FLOAT(OFS_PARM1);\n\t\tqcinput_unicode = G_FLOAT(OFS_PARM2);\n\t\tPR_ExecuteProgram (csqcprogs, csqcg.input_event);\n\t\tqcinput_scan = 0;\t//and stop replay attacks\n\t\tqcinput_unicode = 0;\n\t}\n}\nstatic void QCBUILTIN PF_cs_clipboard_get(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tclipboardtype_t cliptype = G_FLOAT(OFS_PARM0);\n\tSys_Clipboard_PasteText(cliptype, PF_cs_clipboard_got, prinst);\n}*/\n\nvoid QCBUILTIN PF_CL_DrawTextField (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\n\n//prefixes:\n//PF_ - common, works on any vm\n//PF_cs_ - works in csqc only (dependant upon globals or fields)\n//PF_cl_ - works in csqc and menu (if needed...)\n\n//warning: functions that depend on globals are bad, mkay?\nstatic struct {\n\tchar *name;\n\tbuiltin_t bifunc;\n\tint ebfsnum;\n}  BuiltinList[] = {\n//0\n\t{\"makevectors\",\t\t\t\tPF_cs_makevectors, 1},\t\t// #1 void() makevectors (QUAKE)\n\t{\"setorigin\",\t\t\t\tPF_cs_SetOrigin, 2},\t\t// #2 void(entity e, vector org) setorigin (QUAKE)\n\t{\"setmodel\",\t\t\t\tPF_cs_SetModel, 3},\t\t\t// #3 void(entity e, string modl) setmodel (QUAKE)\n\t{\"setsize\",\t\t\t\t\tPF_cs_SetSize, 4},\t\t\t// #4 void(entity e, vector mins, vector maxs) setsize (QUAKE)\n//5\n\t{\"breakpoint\",\t\t\t\tPF_cs_break, 6},\t\t\t// #6 void() debugbreak (QUAKE)\n\t{\"random\",\t\t\t\t\tPF_random,\t7},\t\t\t\t// #7 float() random (QUAKE)\n\t{\"sound\",\t\t\t\t\tPF_cs_sound,\t8},\t\t\t// #8 void(entity e, float chan, string samp, float vol, float atten) sound (QUAKE)\n\t{\"normalize\",\t\t\t\tPF_normalize,\t9},\t\t\t// #9 vector(vector in) normalize (QUAKE)\n//10\n\t{\"error\",\t\t\t\t\tPF_error,\t10},\t\t\t\t// #10 void(string errortext) error (QUAKE)\n\t{\"objerror\",\t\t\t\tPF_objerror,\t11},\t\t\t// #11 void(string errortext) onjerror (QUAKE)\n\t{\"vlen\",\t\t\t\t\tPF_vlen,\t12},\t\t\t\t// #12 float(vector v) vlen (QUAKE)\n\t{\"vectoyaw\",\t\t\t\tPF_vectoyaw,\t13},\t\t\t// #13 float(vector v) vectoyaw (QUAKE)\n\t{\"spawn\",\t\t\t\t\tPF_Spawn,\t14},\t\t\t\t// #14 entity() spawn (QUAKE)\n\t{\"remove\",\t\t\t\t\tPF_cs_remove,\t15},\t\t\t// #15 void(entity e) remove (QUAKE)\n\t{\"removeinstant\",\t\t\tPF_cs_removeinstant,\t0},\n\t{\"traceline\",\t\t\t\tPF_cs_traceline,\t16},\t\t// #16 void(vector v1, vector v2, float nomonst, entity forent) traceline (QUAKE)\n\t{\"checkclient\",\t\t\t\tPF_NoCSQC,\t17},\t\t\t\t// #17 entity() checkclient (QUAKE) (don't support)\n\t{\"find\",\t\t\t\t\tPF_FindString,\t18},\t\t\t// #18 entity(entity start, .string fld, string match) findstring (QUAKE)\n\t{\"precache_sound\",\t\t\tPF_cs_PrecacheSound,\t19},\t// #19 void(string str) precache_sound (QUAKE)\n//20\n\t{\"precache_model\",\t\t\tPF_cs_PrecacheModel,\t20},\t// #20 void(string str) precache_model (QUAKE)\n\t{\"stuffcmd\",\t\t\t\tPF_NoCSQC,\t21},\t\t// #21 void(entity client, string s) stuffcmd (QUAKE) (don't support)\n\t{\"findradius\",\t\t\t\tPF_findradius,\t22},\t\t// #22 entity(vector org, float rad) findradius (QUAKE)\n#ifdef QCGC\n\t{\"findradius_list\",\t\t\tPF_findradius_list, 0},\n\t{\"find_list\",\t\t\t\tPF_FindList,\t\t\t0},\n#endif\n\t{\"bprint\",\t\t\t\t\tPF_NoCSQC,\t23},\t\t\t\t// #23 void(string s, ...) bprint (QUAKE) (don't support)\n\t{\"sprint\",\t\t\t\t\tPF_NoCSQC,\t24},\t\t\t\t// #24 void(entity e, string s, ...) sprint (QUAKE) (don't support)\n\t{\"dprint\",\t\t\t\t\tPF_dprint,\t25},\t\t\t\t// #25 void(string s, ...) dprint (QUAKE)\n\t{\"ftos\",\t\t\t\t\tPF_ftos,\t26},\t\t\t\t// #26 string(float f) ftos (QUAKE)\n\t{\"vtos\",\t\t\t\t\tPF_vtos,\t27},\t\t\t\t// #27 string(vector f) vtos (QUAKE)\n\t{\"coredump\",\t\t\t\tPF_coredump,\t28},\t\t\t// #28 void(void) coredump (QUAKE)\n\t{\"traceon\",\t\t\t\t\tPF_traceon,\t29},\t\t\t\t// #29 void() traceon (QUAKE)\n//30\n\t{\"traceoff\",\t\t\t\tPF_traceoff,\t30},\t\t\t// #30 void() traceoff (QUAKE)\n\t{\"eprint\",\t\t\t\t\tPF_eprint,\t31},\t\t\t\t// #31 void(entity e) eprint (QUAKE)\n\t{\"walkmove\",\t\t\t\tPF_cs_walkmove,\t32},\t\t\t// #32 float(float yaw, float dist) walkmove (QUAKE)\n\t{\"?\",\t\t\t\t\t\tPF_Fixme,\t33},\t\t\t\t// #33\n\t{\"droptofloor\",\t\t\t\tPF_cs_droptofloor,\t34},\t\t// #34\n\t{\"lightstyle\",\t\t\t\tPF_cs_lightstyle,\t35},\t\t// #35 void(float lightstyle, string stylestring) lightstyle (QUAKE)\n\t{\"rint\",\t\t\t\t\tPF_rint,\t36},\t\t\t\t// #36 float(float f) rint (QUAKE)\n\t{\"floor\",\t\t\t\t\tPF_floor,\t37},\t\t\t\t// #37 float(float f) floor (QUAKE)\n\t{\"ceil\",\t\t\t\t\tPF_ceil,\t38},\t\t\t\t// #38 float(float f) ceil (QUAKE)\n//\t{\"?\",\t\t\t\t\t\tPF_Fixme,\t39},\t\t\t\t// #39\n//40\n\t{\"checkbottom\",\t\t\t\tPF_checkbottom,\t40},\t// #40 float(entity e) checkbottom (QUAKE)\n\t{\"pointcontents\",\t\t\tPF_cs_pointcontents,\t41},\t// #41 float(vector org) pointcontents (QUAKE)\n\t{\"pointcontentsmask\",\t\tPF_cs_pointcontentsmask,\t0},\t// #41 float(vector org) pointcontents (QUAKE)\n//\t{\"?\",\t\t\t\t\t\tPF_Fixme,\t42},\t\t\t\t// #42\n\t{\"fabs\",\t\t\t\t\tPF_fabs,\t43},\t\t\t\t// #43 float(float f) fabs (QUAKE)\n\t{\"aim\",\t\t\t\t\t\tPF_NoCSQC,\t44},\t\t\t\t// #44 vector(entity e, float speed) aim (QUAKE) (don't support)\n\t{\"cvar\",\t\t\t\t\tPF_cvar,\t45},\t\t\t\t// #45 float(string cvarname) cvar (QUAKE)\n\t{\"localcmd\",\t\t\t\tPF_localcmd,\t46},\t\t\t// #46 void(string str) localcmd (QUAKE)\n\t{\"nextent\",\t\t\t\t\tPF_nextent,\t47},\t\t\t\t// #47 entity(entity e) nextent (QUAKE)\n\t{\"particle\",\t\t\t\tPF_cs_particle,\t48},\t\t// #48 void(vector org, vector dir, float colour, float count) particle (QUAKE)\n\t{\"changeyaw\",\t\t\t\tPF_changeyaw,\t49},\t\t\t// #49 void() changeyaw (QUAKE)\n//50\n//\t{\"?\",\tPF_Fixme,\t50},\t\t\t\t// #50\n\t{\"vectoangles\",\t\t\t\tPF_vectoangles,\t51},\t\t\t// #51 vector(vector v) vectoangles (QUAKE)\n//\t{\"WriteByte\",\t\t\t\tPF_Fixme,\t52},\t\t\t\t// #52 void(float to, float f) WriteByte (QUAKE)\n//\t{\"WriteChar\",\t\t\t\tPF_Fixme,\t53},\t\t\t\t// #53 void(float to, float f) WriteChar (QUAKE)\n//\t{\"WriteShort\",\t\t\t\tPF_Fixme,\t54},\t\t\t\t// #54 void(float to, float f) WriteShort (QUAKE)\n\n//\t{\"WriteLong\",\t\t\t\tPF_Fixme,\t55},\t\t\t\t// #55 void(float to, float f) WriteLong (QUAKE)\n//\t{\"WriteCoord\",\t\t\t\tPF_Fixme,\t56},\t\t\t\t// #56 void(float to, float f) WriteCoord (QUAKE)\n//\t{\"WriteAngle\",\t\t\t\tPF_Fixme,\t57},\t\t\t\t// #57 void(float to, float f) WriteAngle (QUAKE)\n//\t{\"WriteString\",\t\t\t\tPF_Fixme,\t58},\t\t\t\t// #58 void(float to, float f) WriteString (QUAKE)\n//\t{\"WriteEntity\",\t\t\t\tPF_Fixme,\t59},\t\t\t\t// #59 void(float to, float f) WriteEntity (QUAKE)\n\n//60\n\t{\"sin\",\t\t\t\t\t\tPF_Sin,\t60},\t\t\t\t\t// #60 float(float angle) sin (DP_QC_SINCOSSQRTPOW)\n\t{\"cos\",\t\t\t\t\t\tPF_Cos,\t61},\t\t\t\t\t// #61 float(float angle) cos (DP_QC_SINCOSSQRTPOW)\n\t{\"sqrt\",\t\t\t\t\tPF_Sqrt,\t62},\t\t\t\t// #62 float(float value) sqrt (DP_QC_SINCOSSQRTPOW)\n\t{\"changepitch\",\t\t\t\tPF_changepitch,\t63},\t\t\t// #63 void(entity ent) changepitch (DP_QC_CHANGEPITCH)\n\t{\"tracetoss\",\t\t\t\tPF_cs_tracetoss,\t64},\t\t// #64 void(entity ent, entity ignore) tracetoss (DP_QC_TRACETOSS)\n\n\t{\"etos\",\t\t\t\t\tPF_etos,\t65},\t\t\t\t// #65 string(entity ent) etos (DP_QC_ETOS)\n\t{\"?\",\t\t\t\t\t\tPF_Fixme,\t66},\t\t\t\t// #66\n\t{\"movetogoal\",\t\t\t\tPF_cs_movetogoal,\t67},\t\t// #67 void(float step) movetogoal (QUAKE)\n\t{\"precache_file\",\t\t\tPF_cs_precachefile,\t68},\t\t// #68 void(string s) precache_file (QUAKE) (don't support)\n\t{\"makestatic\",\t\t\t\tPF_cs_makestatic,\t69},\t\t// #69 void(entity e) makestatic (QUAKE)\n//70\n\t{\"changelevel\",\t\t\t\tPF_NoCSQC,\t70},\t\t\t\t// #70 void(string mapname) changelevel (QUAKE) (don't support)\n//\t{\"?\",\t\t\t\t\t\tPF_Fixme,\t71},\t\t\t\t// #71\n\t{\"cvar_set\",\t\t\t\tPF_cvar_set,\t72},\t\t\t// #72 void(string cvarname, string valuetoset) cvar_set (QUAKE)\n\t{\"centerprint\",\t\t\t\tPF_NoCSQC,\t73},\t\t\t\t// #73 void(entity ent, string text) centerprint (QUAKE) (don't support - cprint is supported instead)\n\t{\"ambientsound\",\t\t\tPF_cl_ambientsound,\t74},\t\t// #74 void (vector pos, string samp, float vol, float atten) ambientsound (QUAKE)\n\n\t{\"precache_model2\",\t\t\tPF_cs_PrecacheModel,\t75},\t// #75 void(string str) precache_model2 (QUAKE)\n\t{\"precache_sound2\",\t\t\tPF_cs_PrecacheSound,\t76},\t// #76 void(string str) precache_sound2 (QUAKE)\n\t{\"precache_file2\",\t\t\tPF_cs_precachefile,\t77},\t\t// #77 void(string str) precache_file2 (QUAKE)\n\t{\"setspawnparms\",\t\t\tPF_NoCSQC,\t78},\t\t\t\t// #78 void() setspawnparms (QUAKE) (don't support)\n\t{\"logfrag\",\t\t\t\t\tPF_NoCSQC,\t79},\t\t\t\t// #79 void(entity killer, entity killee) logfrag (QW_ENGINE) (don't support)\n\n//80\n\t{\"infokey\",\t\t\t\t\tPF_cs_infokey,\t80},\t\t\t// #80 string(entity e, string keyname) infokey (QW_ENGINE) (don't support)\n\t{\"stof\",\t\t\t\t\tPF_stof,\t81},\t\t\t\t// #81 float(string s) stof (FRIK_FILE or QW_ENGINE)\n\t{\"multicast\",\t\t\t\tPF_NoCSQC,\t82},\t\t\t\t// #82 void(vector where, float set) multicast (QW_ENGINE) (don't support)\n\n\t{\"getlightstyle\",\t\t\tPF_getlightstyle,\t\t0},\n\t{\"getlightstylergb\",\t\tPF_getlightstylergb,\t\t0},\n\n//90\n\t{\"tracebox\",\t\t\t\tPF_cs_tracebox,\t90},\n\t{\"randomvec\",\t\t\t\tPF_randomvector,\t91},\t\t// #91 vector() randomvec (DP_QC_RANDOMVEC)\n\t{\"getlight\",\t\t\t\tPF_cl_getlight,\t92},\t\t\t// #92 vector(vector org) getlight (DP_QC_GETLIGHT)\n\t{\"registercvar\",\t\t\tPF_registercvar,\t93},\t\t// #93 void(string cvarname, string defaultvalue) registercvar (DP_QC_REGISTERCVAR)\n\t{\"min\",\t\t\t\t\t\tPF_min,\t94},\t\t\t\t// #94 float(float a, floats) min (DP_QC_MINMAXBOUND)\n\n\t{\"max\",\t\t\t\t\t\tPF_max,\t95},\t\t\t\t\t// #95 float(float a, floats) max (DP_QC_MINMAXBOUND)\n\t{\"bound\",\t\t\t\t\tPF_bound,\t96},\t\t\t\t// #96 float(float minimum, float val, float maximum) bound (DP_QC_MINMAXBOUND)\n\t{\"pow\",\t\t\t\t\t\tPF_pow,\t97},\t\t\t\t\t// #97 float(float value) pow (DP_QC_SINCOSSQRTPOW)\n\t{\"logarithm\",\t\t\t\tPF_Logarithm,\t0},\n\t{\"findfloat\",\t\t\t\tPF_FindFloat,\t98},\t\t\t// #98 entity(entity start, .float fld, float match) findfloat (DP_QC_FINDFLOAT)\n\t{\"findentity\",\t\t\t\tPF_FindFloat,\t98},\t\t\t// #98 entity(entity start, .float fld, float match) findfloat (DP_QC_FINDFLOAT)\n\t{\"checkextension\",\t\t\tPF_checkextension,\t99},\t\t// #99 float(string extname) checkextension (EXT_CSQC)\n\t{\"checkbuiltin\",\t\t\tPF_checkbuiltin,\t0},\n\t{\"anglemod\",\t\t\t\tPF_anglemod,\t\t102},\n\t{\"anglesub\",\t\t\t\tPF_anglesub,\t\t0},\n\n//110\n\t{\"fopen\",\t\t\t\t\tPF_fopen,\t110},\t\t\t\t// #110 float(string strname, float accessmode) fopen (FRIK_FILE)\n\t{\"fclose\",\t\t\t\t\tPF_fclose,\t111},\t\t\t\t// #111 void(float fnum) fclose (FRIK_FILE)\n\t{\"fgets\",\t\t\t\t\tPF_fgets,\t112},\t\t\t\t// #112 string(float fnum) fgets (FRIK_FILE)\n\t{\"fputs\",\t\t\t\t\tPF_fputs,\t113},\t\t\t\t// #113 void(float fnum, string str) fputs (FRIK_FILE)\n\t{\"fread\",\t\t\t\t\tPF_fread,\t0},\n\t{\"fwrite\",\t\t\t\t\tPF_fwrite,\t0},\n\t{\"fseek\",\t\t\t\t\tPF_fseek32,\t0},\n\t{\"fsize\",\t\t\t\t\tPF_fsize32,\t0},\n\t{\"fseek64\",\t\t\t\t\tPF_fseek64,\t0},\n\t{\"fsize64\",\t\t\t\t\tPF_fsize64,\t0},\n\t{\"strlen\",\t\t\t\t\tPF_strlen,\t114},\t\t\t\t// #114 float(string str) strlen (FRIK_FILE)\n\n\t{\"strcat\",\t\t\t\t\tPF_strcat,\t\t115},\t\t\t// #115 string(string str1, string str2, ...) strcat (FRIK_FILE)\n\t{\"substring\",\t\t\t\tPF_substring,\t116},\t\t\t// #116 string(string str, float start, float length) substring (FRIK_FILE)\n\t{\"stov\",\t\t\t\t\tPF_stov,\t\t117},\t\t\t// #117 vector(string str) stov (FRIK_FILE)\n\t{\"strzone\",\t\t\t\t\tPF_strzone,\t\t118},\t\t\t// #118 string(string str) dupstring (FRIK_FILE)\n\t{\"strunzone\",\t\t\t\tPF_strunzone,\t119},\t\t\t// #119 void(string str) freestring (FRIK_FILE)\n#ifdef QCGC\n\t{\"createbuffer\",\t\t\tPF_createbuffer,\t0},\n#endif\n\n\t{\"localsound\",\t\t\t\tPF_cl_localsound,\t177},\n\n//200\n\t{\"getmodelindex\",\t\t\tPF_cs_getmodelindex,\t200},\n\t{\"getsoundindex\",\t\t\tPF_cs_getsoundindex,\t0},\n\t{\"externcall\",\t\t\t\tPF_externcall,\t201},\n\t{\"addprogs\",\t\t\t\tPF_cl_addprogs,\t202},\n\t{\"externvalue\",\t\t\t\tPF_externvalue,\t203},\n\t{\"externset\",\t\t\t\tPF_externset,\t204},\n\n\t{\"externrefcall\",\t\t\tPF_externrefcall,\t205},\n\t{\"instr\",\t\t\t\t\tPF_instr,\t206},\n\t{\"openportal\",\t\t\t\tPF_cs_OpenPortal,\t207},\t//q2bsps\n\t{\"registertempent\",\t\t\tPF_NoCSQC,\t208},//{\"RegisterTempEnt\", PF_RegisterTEnt,\t0,\t\t0,\t\t0,\t\t208},\n\t{\"customtempent\",\t\t\tPF_NoCSQC,\t209},//{\"CustomTempEnt\",\tPF_CustomTEnt,\t\t0,\t\t0,\t\t0,\t\t209},\n//210\n\t{\"fork\",\t\t\t\t\tPF_Fork,\t210},//{\"fork\",\t\t\tPF_Fork,\t\t\t0,\t\t0,\t\t0,\t\t210},\n\t{\"abort\",\t\t\t\t\tPF_Abort,\t211}, //#211 void() abort (FTE_MULTITHREADED)\n\t{\"sleep\",\t\t\t\t\tPF_Sleep,\t212},//{\"sleep\",\t\t\tPF_Sleep,\t\t\t0,\t\t0,\t\t0,\t\t212},\n\t{\"forceinfokey\",\t\t\tPF_NoCSQC,\t213},//{\"forceinfokey\",\tPF_ForceInfoKey,\t0,\t\t0,\t\t0,\t\t213},\n\t{\"forceinfokeyblob\",\t\tPF_NoCSQC,\t0},//{\"forceinfokey\",\tPF_ForceInfoKey,\t0,\t\t0,\t\t0,\t\t213},\n\t{\"chat\",\t\t\t\t\tPF_NoCSQC,\t214},//{\"chat\",\t\t\tPF_chat,\t\t\t0,\t\t0,\t\t0,\t\t214},// #214 void(string filename, float starttag, entity edict) SV_Chat (FTE_NPCCHAT)\n\n\t{\"particle2\",\t\t\t\tPF_cs_particle2,\t215}, //215 (FTE_PEXT_HEXEN2)\n\t{\"particle3\",\t\t\t\tPF_cs_particle3,\t216}, //216 (FTE_PEXT_HEXEN2)\n\t{\"particle4\",\t\t\t\tPF_cs_particle4,\t217}, //217 (FTE_PEXT_HEXEN2)\n\n//EXT_DIMENSION_PLANES\n\t{\"bitshift\",\t\t\t\tPF_bitshift,\t218},\t\t//#218 bitshift (EXT_DIMENSION_PLANES)\n\t{\"te_lightningblood\",\t\tPF_cl_te_lightningblood,\t219},// #219 te_lightningblood void(vector org) (FTE_TE_STANDARDEFFECTBUILTINS)\n\n//220\n//\t{\"map_builtin\",\t\t\t\tPF_Fixme,\t220},\t//like #100 - takes 2 args. arg0 is builtinname, 1 is number to map to.\n\t{\"strstrofs\",\t\t\t\tPF_strstrofs,\t221},\t// #221 float(string s1, string sub) strstrofs (FTE_STRINGS)\n\t{\"str2chr\",\t\t\t\t\tPF_str2chr,\t222},\t\t// #222 float(string str, float index) str2chr (FTE_STRINGS)\n\t{\"chr2str\",\t\t\t\t\tPF_chr2str,\t223},\t\t// #223 string(float chr, ...) chr2str (FTE_STRINGS)\n\t{\"strconv\",\t\t\t\t\tPF_strconv,\t224},\t\t// #224 string(float ccase, float redalpha, float redchars, string str, ...) strconv (FTE_STRINGS)\n\n\t{\"strpad\",\t\t\t\t\tPF_strpad,\t225},\t\t// #225 string strpad(float pad, string str1, ...) strpad (FTE_STRINGS)\n\t{\"infoadd\",\t\t\t\t\tPF_infoadd,\t226},\t\t// #226 string(string old, string key, string value) infoadd\n\t{\"infoget\",\t\t\t\t\tPF_infoget,\t227},\t\t// #227 string(string info, string key) infoget\n\t{\"strcmp\",\t\t\t\t\tPF_strncmp,\t228},\t\t// #228 float(string s1, string s2) strcmp (FTE_STRINGS)\n\t{\"strncmp\",\t\t\t\t\tPF_strncmp,\t228},\t\t// #228 float(string s1, string s2, float len) strncmp (FTE_STRINGS)\n\t{\"strcasecmp\",\t\t\t\tPF_strncasecmp,\t229},\t// #229 float(string s1, string s2) strcasecmp (FTE_STRINGS)\n\n//230\n\t{\"strncasecmp\",\t\t\t\tPF_strncasecmp,\t230},\t// #230 float(string s1, string s2, float len) strncasecmp (FTE_STRINGS)\n\t{\"strtrim\",\t\t\t\t\tPF_strtrim,\t\t0},\n\t{\"calltimeofday\",\t\t\tPF_calltimeofday,\t231},\n\t{\"clientstat\",\t\t\t\tPF_NoCSQC,\t232},\t\t// #231 clientstat\n\t{\"runclientphys\",\t\t\tPF_NoCSQC,\t233},\t\t// #232 runclientphys\n//\t{\"isbackbuffered\",\t\t\tPF_NoCSQC,\t234},\t\t// #233 float(entity ent) isbackbuffered\n//I messed up, 234 is meant to be isbackbuffered. luckily that's not present in csqc, but still, this is messy. Don't document this.\n\t{\"rotatevectorsbytag\",\t\tPF_rotatevectorsbytag,\t234},\t// #234\n\n\t{\"rotatevectorsbyangle\",\tPF_rotatevectorsbyangles,\t235}, // #235\n\t{\"rotatevectorsbyvectors\",\tPF_rotatevectorsbymatrix,\t236}, // #236\n\t{\"skinforname\",\t\t\t\tPF_skinforname,\t237},\t\t// #237\n\t{\"shaderforname\",\t\t\tPF_shaderforname,\t238},\t// #238\n\t{\"remapshader\",\t\t\t\tPF_remapshader,\t\t0},\n\t{\"te_bloodqw\",\t\t\t\tPF_cl_te_bloodqw,\t239},\t// #239 void te_bloodqw(vector org[, float count]) (FTE_TE_STANDARDEFFECTBUILTINS)\n\n\t{\"checkpvs\",\t\t\t\tPF_checkpvs,\t\t240},\n//\t{\"matchclientname\",\t\t\tPF_matchclient,\t\t241},\n\t{\"sendpacket\",\t\t\t\tPF_cl_SendPacket,\t242},\t//void(string dest, string content) sendpacket = #242; (FTE_QC_SENDPACKET)\n\n//\t{\"bulleten\",\t\t\t\tPF_bulleten,\t\t243}, (removed builtin)\n\t{\"rotatevectorsbytag\",\t\tPF_rotatevectorsbytag,\t244},\n\t{\"mod\",\t\t\t\t\t\tPF_mod,\t\t\t\t245},\n\n\t{\"sqlconnect\",\t\t\t\tPF_NoCSQC,\t\t\t250},\t// #250 float([string host], [string user], [string pass], [string defaultdb], [string driver]) sqlconnect (FTE_SQL)\n\t{\"sqldisconnect\",\t\t\tPF_NoCSQC,\t\t\t251},\t// #251 void(float serveridx) sqldisconnect (FTE_SQL)\n\t{\"sqlopenquery\",\t\t\tPF_NoCSQC,\t\t\t252},\t// #252 float(float serveridx, void(float serveridx, float queryidx, float rows, float columns, float eof) callback, float querytype, string query) sqlopenquery (FTE_SQL)\n\t{\"sqlclosequery\",\t\t\tPF_NoCSQC,\t\t\t253},\t// #253 void(float serveridx, float queryidx) sqlclosequery (FTE_SQL)\n\t{\"sqlreadfield\",\t\t\tPF_NoCSQC,\t\t\t254},\t// #254 string(float serveridx, float queryidx, float row, float column) sqlreadfield (FTE_SQL)\n\t{\"sqlerror\",\t\t\t\tPF_NoCSQC,\t\t\t255},\t// #255 string(float serveridx, [float queryidx]) sqlerror (FTE_SQL)\n\t{\"sqlescape\",\t\t\t\tPF_NoCSQC,\t\t\t256},\t// #256 string(float serveridx, string data) sqlescape (FTE_SQL)\n\t{\"sqlversion\",\t\t\t\tPF_NoCSQC,\t\t\t257},\t// #257 string(float serveridx) sqlversion (FTE_SQL)\n\t{\"sqlreadfloat\",\t\t\tPF_NoCSQC,\t\t\t258},\t// #258 float(float serveridx, float queryidx, float row, float column) sqlreadfloat (FTE_SQL)\n\n\t{\"json_parse\",\t\t\t\tPF_json_parse,\t\t\t\t0},\n\t{\"json_free\",\t\t\t\tPF_memfree,\t\t\t\t\t0},\n\t{\"json_get_value_type\",\t\tPF_json_get_value_type,\t\t0},\n\t{\"json_get_integer\",\t\tPF_json_get_integer,\t\t0},\n\t{\"json_get_float\",\t\t\tPF_json_get_float,\t\t\t0},\n\t{\"json_get_string\",\t\t\tPF_json_get_string,\t\t\t0},\n\t{\"json_find_object_child\",\tPF_json_find_object_child,\t0},\n\t{\"json_get_length\",\t\t\tPF_json_get_length,\t\t\t0},\n\t{\"json_get_child_at_index\",\tPF_json_get_child_at_index,\t0},\n\t{\"json_get_name\",\t\t\tPF_json_get_name,\t\t\t0},\n\n\t{\"js_run_script\",\t\t\tPF_js_run_script,\t0},\n\n\t{\"stoi\",\t\t\t\t\tPF_stoi,\t\t\t259},\n\t{\"itos\",\t\t\t\t\tPF_itos,\t\t\t260},\n\t{\"stoh\",\t\t\t\t\tPF_stoh,\t\t\t261},\n\t{\"htos\",\t\t\t\t\tPF_htos,\t\t\t262},\n\t{\"ftoi\",\t\t\t\t\tPF_ftoi,\t\t\t0},\n\t{\"itof\",\t\t\t\t\tPF_itof,\t\t\t0},\n\t{\"ftou\",\t\t\t\t\tPF_ftou,\t\t\t0},\n\t{\"utof\",\t\t\t\t\tPF_utof,\t\t\t0},\n\n\t{\"skel_create\",\t\t\t\tPF_skel_create,\t\t\t263},//float(float modlindex) skel_create = #263; // (FTE_CSQC_SKELETONOBJECTS)\n\t{\"skel_build\",\t\t\t\tPF_skel_build,\t\t\t264},//float(float skel, entity ent, float modelindex, float retainfrac, float firstbone, float lastbone, optional float addition) skel_build = #264; // (FTE_CSQC_SKELETONOBJECTS)\n\t{\"skel_build_ptr\",\t\t\tPF_skel_build_ptr,\t\t0},//float(float skel, int numblends, __variant *blends, int blendsize) skel_build_ptr = #0;\n\t{\"skel_get_numbones\",\t\tPF_skel_get_numbones,\t265},//float(float skel) skel_get_numbones = #265; // (FTE_CSQC_SKELETONOBJECTS)\n\t{\"skel_get_bonename\",\t\tPF_skel_get_bonename,\t266},//string(float skel, float bonenum) skel_get_bonename = #266; // (FTE_CSQC_SKELETONOBJECTS) (returns tempstring)\n\t{\"skel_get_boneparent\",\t\tPF_skel_get_boneparent,\t267},//float(float skel, float bonenum) skel_get_boneparent = #267; // (FTE_CSQC_SKELETONOBJECTS)\n\t{\"skel_find_bone\",\t\t\tPF_skel_find_bone,\t\t268},//float(float skel, string tagname) skel_get_boneidx = #268; // (FTE_CSQC_SKELETONOBJECTS)\n\t{\"skel_get_bonerel\",\t\tPF_skel_get_bonerel,\t269},//vector(float skel, float bonenum) skel_get_bonerel = #269; // (FTE_CSQC_SKELETONOBJECTS) (sets v_forward etc)\n\t{\"skel_get_boneabs\",\t\tPF_skel_get_boneabs,\t270},//vector(float skel, float bonenum) skel_get_boneabs = #270; // (FTE_CSQC_SKELETONOBJECTS) (sets v_forward etc)\n\t{\"skel_set_bone\",\t\t\tPF_skel_set_bone,\t\t271},//void(float skel, float bonenum, vector org) skel_set_bone = #271; // (FTE_CSQC_SKELETONOBJECTS) (reads v_forward etc)\n\t{\"skel_premul_bone\",\t\tPF_skel_premul_bone,\t272},//void(float skel, float bonenum, vector org) skel_mul_bone = #272; // (FTE_CSQC_SKELETONOBJECTS) (reads v_forward etc)\n\t{\"skel_premul_bones\",\t\tPF_skel_premul_bones,\t273},//void(float skel, float startbone, float endbone, vector org) skel_mul_bone = #273; // (FTE_CSQC_SKELETONOBJECTS) (reads v_forward etc)\n\t{\"skel_postmul_bone\",\t\tPF_skel_postmul_bone,\t0},//void(float skel, float bonenum, vector org) skel_mul_bone = #272; // (FTE_CSQC_SKELETONOBJECTS) (reads v_forward etc)\n//\t{\"skel_postmul_bones\",\t\tPF_skel_postmul_bones,\t0},//void(float skel, float startbone, float endbone, vector org) skel_mul_bone = #273; // (FTE_CSQC_SKELETONOBJECTS) (reads v_forward etc)\n\t{\"skel_copybones\",\t\t\tPF_skel_copybones,\t\t274},//void(float skeldst, float skelsrc, float startbone, float entbone) skel_copybones = #274; // (FTE_CSQC_SKELETONOBJECTS)\n\t{\"skel_delete\",\t\t\t\tPF_skel_delete,\t\t\t275},//void(float skel) skel_delete = #275; // (FTE_CSQC_SKELETONOBJECTS)\n\t{\"frameforname\",\t\t\tPF_frameforname,\t\t276},//float(float modidx, string framename) frameforname = #276 (FTE_CSQC_SKELETONOBJECTS)\n\t{\"frameduration\",\t\t\tPF_frameduration,\t\t277},//float(float modidx, float framenum) frameduration = #277 (FTE_CSQC_SKELETONOBJECTS)\n\t{\"frameforaction\",\t\t\tPF_frameforaction,\t\t0},//float(float modidx, int actionid) frameforaction = #0\n\t{\"processmodelevents\",\t\tPF_processmodelevents,\t0},\n\t{\"getnextmodelevent\",\t\tPF_getnextmodelevent,\t0},\n\t{\"getmodeleventidx\",\t\tPF_getmodeleventidx,\t0},\n\n\t{\"getlocationname\",\t\t\tPF_getlocationname,\t\t0},\n\t{\"crossproduct\",\t\t\tPF_crossproduct,\t\t0},\n\t{\"pushmove\", \t\t\t\tPF_pushmove, \t\t\t0},\n#ifdef TERRAIN\n\t{\"terrain_edit\",\t\t\tPF_terrain_edit,\t\t278},//void(float action, vector pos, float radius, float quant) terrain_edit = #278 (??FTE_TERRAIN_EDIT??)\n\t{\"brush_get\",\t\t\t\tPF_brush_get,\t\t\t0},\n\t{\"brush_create\",\t\t\tPF_brush_create,\t\t0},\n\t{\"brush_delete\",\t\t\tPF_brush_delete,\t\t0},\n\t{\"brush_selected\",\t\t\tPF_brush_selected,\t\t0},\n\t{\"brush_getfacepoints\",\t\tPF_brush_getfacepoints,\t0},\n\t{\"brush_calcfacepoints\",\tPF_brush_calcfacepoints,0},\n\t{\"brush_findinvolume\",\t\tPF_brush_findinvolume,\t0},\n\n\t{\"patch_getcp\",\t\t\t\tPF_patch_getcp,\t\t\t0},\n\t{\"patch_getmesh\",\t\t\tPF_patch_getmesh,\t\t0},\n\t{\"patch_create\",\t\t\tPF_patch_create,\t\t0},\n\t{\"patch_evaluate\",\t\t\tPF_patch_evaluate,\t\t0},\n#endif\n\n#ifdef ENGINE_ROUTING\n\t{\"route_calculate\",\t\tPF_route_calculate,\t\t0},\n#endif\n\n\t{\"touchtriggers\",\t\t\tPF_touchtriggers,\t\t279},//void() touchtriggers = #279;\n\t{\"skel_ragupdate\",\t\t\tPF_skel_ragedit,\t\t281},// (FTE_QC_RAGDOLL)\n\t{\"skel_mmap\",\t\t\t\tPF_skel_mmap,\t\t\t282},// (FTE_QC_RAGDOLL)\n\t{\"skel_set_bone_world\",\t\tPF_skel_set_bone_world,\t283},\n\t{\"frametoname\",\t\t\t\tPF_frametoname,\t\t\t284},//string(float modidx, float framenum) frametoname\n\t{\"skintoname\",\t\t\t\tPF_skintoname,\t\t\t285},//string(float modidx, float skin) skintoname\n\t{\"resourcestatus\",\t\t\tPF_resourcestatus,\t\t286},\n\n\t{\"hash_createtab\",\t\t\tPF_hash_createtab,\t\t\t287},\n\t{\"hash_destroytab\",\t\t\tPF_hash_destroytab,\t\t\t288},\n\t{\"hash_add\",\t\t\t\tPF_hash_add,\t\t\t\t289},\n\t{\"hash_get\",\t\t\t\tPF_hash_get,\t\t\t\t290},\n\t{\"hash_delete\",\t\t\t\tPF_hash_delete,\t\t\t\t291},\n\t{\"hash_getkey\",\t\t\t\tPF_hash_getkey,\t\t\t\t292},\n\t{\"hash_getcb\",\t\t\t\tPF_hash_getcb,\t\t\t\t293},\n\t{\"checkcommand\",\t\t\tPF_checkcommand,\t\t\t294},\n\t{\"argescape\",\t\t\t\tPF_argescape,\t\t\t\t295},\n\n\t{\"modelframecount\",\t\t\tPF_modelframecount,\t\t\t0},\n\n//300\n\t{\"clearscene\",\t\t\t\tPF_R_ClearScene,\t300},\t\t\t\t// #300 void() clearscene (EXT_CSQC)\n\t{\"addentities\",\t\t\t\tPF_R_AddEntityMask,\t301},\t\t\t\t// #301 void(float mask) addentities (EXT_CSQC)\n\t{\"addentity\",\t\t\t\tPF_R_AddEntity,\t\t302},\t\t\t\t\t// #302 void(entity ent) addentity (EXT_CSQC)\n\t{\"addentity_lighting\",\t\tPF_R_AddEntityLighting,\t\t0},\n\n\t{\"removeentity\",\t\t\tPF_R_RemoveEntity,\t0},\n\t{\"setproperty\",\t\t\t\tPF_R_SetViewFlag,\t303},\t\t\t\t// #303 float(float property, ...) setproperty (EXT_CSQC)\n\t{\"renderscene\",\t\t\t\tPF_R_RenderScene,\t304},\t\t\t\t// #304 void() renderscene (EXT_CSQC)\n\n\t{\"dynamiclight_add\",\t\tPF_R_DynamicLight_AddDynamic,\t305},\t\t\t// #305 float(vector org, float radius, vector lightcolours) adddynamiclight (EXT_CSQC)\n\n\t{\"R_BeginPolygon\",\t\t\tPF_R_PolygonBegin,\t306},\t\t\t\t// #306 void(string texturename) R_BeginPolygon (EXT_CSQC_???)\n\t{\"R_PolygonVertex\",\t\t\tPF_R_PolygonVertex,\t307},\t\t\t\t// #307 void(vector org, vector texcoords, vector rgb, float alpha) R_PolygonVertex (EXT_CSQC_???)\n\t{\"R_EndPolygon\",\t\t\tPF_R_PolygonEnd,\t308},\t\t\t\t// #308 void() R_EndPolygon (EXT_CSQC_???)\n\t{\"R_EndPolygonRibbon\",\t\tPF_R_PolygonEndRibbon, 0},\n\t{\"addtrisoup_simple\",\t\tPF_R_AddTrisoup_Simple,\t0},\n\n\t{\"getproperty\",\t\t\t\tPF_R_GetViewFlag,\t309},\t\t\t\t// #309 vector/float(float property) getproperty (EXT_CSQC_1)\n\n//310\n//maths stuff that uses the current view settings.\n\t{\"unproject\",\t\t\t\tPF_cs_unproject,\t310},\t\t\t\t// #310 vector (vector v) unproject (EXT_CSQC)\n\t{\"project\",\t\t\t\t\tPF_cs_project,\t\t311},\t\t\t\t// #311 vector (vector v) project (EXT_CSQC)\n\n//\t{\"?\",\tPF_Fixme,\t\t\t312},\t\t\t\t// #312\n//\t{\"?\",\tPF_Fixme,\t\t313},\t\t\t\t\t// #313\n\n//2d (immediate) operations\n\t{\"drawtextfield\",\t\t\tPF_CL_DrawTextField,\t\t\t0/*314*/},\n\t{\"drawline\",\t\t\t\tPF_CL_drawline,\t\t\t\t\t315},\t\t\t// #315 void(float width, vector pos1, vector pos2) drawline (EXT_CSQC)\n\t{\"iscachedpic\",\t\t\t\tPF_CL_is_cached_pic,\t\t\t316},\t\t// #316 float(string name) iscachedpic (EXT_CSQC)\n\t{\"precache_pic\",\t\t\tPF_CL_precache_pic,\t\t\t\t317},\t\t// #317 string(string name, float trywad) precache_pic (EXT_CSQC)\n\t{\"r_uploadimage\",\t\t\tPF_CL_uploadimage,\t\t\t\t0},\n\t{\"r_readimage\",\t\t\t\tPF_CL_readimage,\t\t\t\t0},\n\t{\"drawgetimagesize\",\t\tPF_CL_drawgetimagesize,\t\t\t318},\t\t// #318 vector(string picname) draw_getimagesize (EXT_CSQC)\n\t{\"freepic\",\t\t\t\t\tPF_CL_free_pic,\t\t\t\t\t319},\t\t// #319 void(string name) freepic (EXT_CSQC)\n\t{\"spriteframe\",\t\t\t\tPF_cs_spriteframe,\t\t\t\t0},\n//320\n\t{\"drawcharacter\",\t\t\tPF_CL_drawcharacter,\t\t\t320},\t\t// #320 float(vector position, float character, vector scale, vector rgb, float alpha [, float flag]) drawcharacter (EXT_CSQC, [EXT_CSQC_???])\n\t{\"drawrawstring\",\t\t\tPF_CL_drawrawstring,\t\t\t321},\t// #321 float(vector position, string text, vector scale, vector rgb, float alpha [, float flag]) drawstring (EXT_CSQC, [EXT_CSQC_???])\n\t{\"drawpic\",\t\t\t\t\tPF_CL_drawpic,\t\t\t\t\t322},\t\t// #322 float(vector position, string pic, vector size, vector rgb, float alpha [, float flag]) drawpic (EXT_CSQC, [EXT_CSQC_???])\n\t{\"drawrotpic\",\t\t\t\tPF_CL_drawrotpic,\t\t\t\t0},\n\t{\"drawfill\",\t\t\t\tPF_CL_drawfill,\t\t\t\t\t323},\t\t// #323 float(vector position, vector size, vector rgb, float alpha [, float flag]) drawfill (EXT_CSQC, [EXT_CSQC_???])\n\t{\"drawsetcliparea\",\t\t\tPF_CL_drawsetcliparea,\t\t\t324},\t// #324 void(float x, float y, float width, float height) drawsetcliparea (EXT_CSQC_???)\n\t{\"drawresetcliparea\",\t\tPF_CL_drawresetcliparea,\t\t325},\t\t// #325 void(void) drawresetcliparea (EXT_CSQC_???)\n\n\t{\"drawstring\",\t\t\t\tPF_CL_drawcolouredstring,\t\t326},\t// #326\n\t{\"stringwidth\",\t\t\t\tPF_CL_stringwidth,\t\t\t\t327},\t// #327 EXT_CSQC_'DARKPLACES'\n\t{\"drawsubpic\",\t\t\t\tPF_CL_drawsubpic,\t\t\t\t328},\t// #328 EXT_CSQC_'DARKPLACES'\n\t{\"drawrotsubpic\",\t\t\tPF_CL_drawrotsubpic,\t\t\t0},\n#ifdef HAVE_LEGACY\n\t{\"drawrotpic_dp\",\t\t\tPF_CL_drawrotpic_dp,\t\t\t329},\n#endif\n\n//330\n\t{\"getstati\",\t\t\t\tPF_cs_getstat_int,\t\t\t\t330},\t// #330 int(float stnum) getstati (EXT_CSQC)\n\t{\"getstatf\",\t\t\t\tPF_cs_getstat_float,\t\t\t331},\t// #331 float(float stnum) getstatf (EXT_CSQC)\n\t{\"getstats\",\t\t\t\tPF_cs_getstat_string,\t\t\t332},\t// #332 string(float firststnum) getstats (EXT_CSQC)\n\t{\"getplayerstat\",\t\t\tPF_cs_getplayerstat,\t\t\t0},\t\t// #0 __variant(float playernum, float statnum, float stattype) getplayerstat\n\t{\"setmodelindex\",\t\t\tPF_cs_SetModelIndex,\t\t\t333},\t// #333 void(entity e, float mdlindex) setmodelindex (EXT_CSQC)\n\t{\"modelnameforindex\",\t\tPF_cs_ModelnameForIndex,\t\t334},\t// #334 string(float mdlindex) modelnameforindex (EXT_CSQC)\n\t{\"soundnameforindex\",\t\tPF_cs_SoundnameForIndex,\t\t0},\n\n\t{\"particleeffectnum\",\t\tPF_cs_particleeffectnum,\t\t335},\t// #335 float(string effectname) particleeffectnum (EXT_CSQC)\n\t{\"trailparticles\",\t\t\tPF_cs_trailparticles,\t\t\t336},\t// #336 void(float effectnum, entity ent, vector start, vector end) trailparticles (EXT_CSQC),\n\t{\"trailparticles_dp\",\t\tPF_cs_trailparticles,\t\t\t336},\t// #336 DP sucks\n\t{\"pointparticles\",\t\t\tPF_cs_pointparticles,\t\t\t337},\t// #337 void(float effectnum, vector origin [, vector dir, float count]) pointparticles (EXT_CSQC)\n\n\t{\"cprint\",\t\t\t\t\tPF_cl_cprint,\t\t\t\t\t338},\t// #338 void(string s) cprint (EXT_CSQC)\n\t{\"print\",\t\t\t\t\tPF_print,\t\t\t\t\t\t339},\t// #339 void(string s) print (EXT_CSQC)\n\t{\"setwatchpoint\",\t\t\tPF_setwatchpoint,\t\t\t\t0},\n\n//340\n\t{\"keynumtostring\",\t\t\tPF_cl_keynumtostring,\t\t\t340},\t// #340 string(float keynum) keynumtostring (EXT_CSQC)\n\t{\"stringtokeynum\",\t\t\tPF_cl_stringtokeynum,\t\t\t341},\t// #341 float(string keyname) stringtokeynum (EXT_CSQC)\n\t{\"getkeybind\",\t\t\t\tPF_cl_getkeybind,\t\t\t\t342},\t// #342 string(float keynum) getkeybind (EXT_CSQC)\n\n\t{\"setcursormode\",\t\t\tPF_cl_setcursormode,\t\t\t343},\t// #343 This is originally a DP extension\n\t{\"getcursormode\",\t\t\tPF_cl_getcursormode,\t\t\t0},\t\t//\n\t{\"setmousepos\",\t\t\t\tPF_cl_setmousepos,\t\t\t\t0},\t\t//\n\t{\"getmousepos\",\t\t\t\tPF_cl_getmousepos,\t\t\t\t344},\t// #344 This is a DP extension\n\n//\t{\"clipboard_get\",\t\t\tPF_cs_clipboard_get,\t\t\t0},\t\t//don't let csqc read the clipboard right now. its too risky.\n\t{\"clipboard_set\",\t\t\tPF_cl_clipboard_set,\t\t\t0},\t\t//it can change it though, no real problem there. just kill the program if its filling it with crap.\n\n\t{\"getinputstate\",\t\t\tPF_cs_getinputstate,\t\t\t345},\t// #345 float(float framenum) getinputstate (EXT_CSQC)\n\t{\"setsensitivityscaler\",\tPF_cs_setsensitivityscaler, \t346},\t// #346 void(float sens) setsensitivityscaler (EXT_CSQC)\n\n\t{\"runstandardplayerphysics\",PF_cs_runplayerphysics,\t\t\t347},\t// #347 void() runstandardplayerphysics (EXT_CSQC)\n\n\t{\"getplayerkeyvalue\",\t\tPF_cs_getplayerkeystring,\t\t348},\t// #348 string(float playernum, string keyname) getplayerkeyvalue (EXT_CSQC)\n\t{\"getplayerkeyfloat\",\t\tPF_cs_getplayerkeyfloat,\t\t0},\t\t// #348 string(float playernum, string keyname) getplayerkeyvalue\n\t{\"getplayerkeyblob\",\t\tPF_cs_getplayerkeyblob,\t\t\t0},\t\t// #0   int(float playernum, string keyname, optional void *outptr, int size) getplayerkeyblob\n\t{\"setlocaluserinfo\",\t\tPF_cl_setlocaluserinfo,\t\t\t0},\n\t{\"getlocaluserinfo\",\t\tPF_cl_getlocaluserinfostring,\t0},\n\t{\"setlocaluserinfoblob\",\tPF_cl_setlocaluserinfo,\t\t\t0},\n\t{\"getlocaluserinfoblob\",\tPF_cl_getlocaluserinfoblob,\t\t0},\n\n\t{\"isdemo\",\t\t\t\t\tPF_cl_playingdemo,\t\t\t\t349},\t// #349 float() isdemo (EXT_CSQC)\n//350\n\t{\"isserver\",\t\t\t\tPF_cl_runningserver,\t\t\t350},\t// #350 float() isserver (EXT_CSQC)\n\n\t{\"SetListener\",\t\t\t\tPF_cs_setlistener, \t\t\t\t351},\t// #351 void(vector origin, vector forward, vector right, vector up) SetListener (EXT_CSQC)\n\t{\"setup_reverb\",\t\t\tPF_cs_setupreverb, \t\t\t\t0},\n\t{\"queueaudio\",\t\t\t\tPF_cl_queueaudio,\t\t\t\t0},\n\t{\"getqueuedaudiotime\",\t\tPF_cl_getqueuedaudiotime,\t\t0},\n\t{\"registercommand\",\t\t\tPF_cs_registercommand,\t\t\t352},\t// #352 void(string cmdname) registercommand (EXT_CSQC)\n\t{\"wasfreed\",\t\t\t\tPF_WasFreed,\t\t\t\t\t353},\t// #353 float(entity ent) wasfreed (EXT_CSQC) (should be availabe on server too)\n\n\t{\"serverkey\",\t\t\t\tPF_cl_serverkey,\t\t\t\t354},\t// #354 string(string key) serverkey;\n\t{\"serverkeyfloat\",\t\t\tPF_cl_serverkeyfloat,\t\t\t0},\t\t// #0 float(string key) serverkeyfloat;\n\t{\"serverkeyblob\",\t\t\tPF_cl_serverkeyblob,\t\t\t0},\n\t{\"getentitytoken\",\t\t\tPF_cs_getentitytoken,\t\t\t355},\t// #355 string() getentitytoken;\n\t{\"findfont\",\t\t\t\tPF_CL_findfont,\t\t\t\t\t356},\n\t{\"loadfont\",\t\t\t\tPF_CL_loadfont,\t\t\t\t\t357},\n//\t{\"?\",\t\t\t\t\t\tPF_Fixme,\t\t\t\t\t\t358},\t// #358\n\t{\"sendevent\",\t\t\t\tPF_cs_sendevent,\t\t\t\t359},\t// #359\tvoid(string evname, string evargs, ...) (EXT_CSQC_1)\n\n//360\n//note that 'ReadEntity' is pretty hard to implement reliably. Modders should use a combination of ReadShort, and findfloat, and remember that it might not be known clientside (pvs culled or other reason)\n\t{\"readbyte\",\t\t\t\tPF_ReadByte,\t\t\t\t\t360},\t// #360 float() readbyte (EXT_CSQC)\n\t{\"readchar\",\t\t\t\tPF_ReadChar,\t\t\t\t\t361},\t// #361 float() readchar (EXT_CSQC)\n\t{\"readshort\",\t\t\t\tPF_ReadShort,\t\t\t\t\t362},\t// #362 float() readshort (EXT_CSQC)\n\t{\"readlong\",\t\t\t\tPF_ReadLong,\t\t\t\t\t363},\t// #363 float() readlong (EXT_CSQC)\n\t{\"readcoord\",\t\t\t\tPF_ReadCoord,\t\t\t\t\t364},\t// #364 float() readcoord (EXT_CSQC)\n\n\t{\"readangle\",\t\t\t\tPF_ReadAngle,\t\t\t\t\t365},\t// #365 float() readangle (EXT_CSQC)\n\t{\"readstring\",\t\t\t\tPF_ReadString,\t\t\t\t\t366},\t// #366 string() readstring (EXT_CSQC)\n\t{\"readfloat\",\t\t\t\tPF_ReadFloat,\t\t\t\t\t367},\t// #367 float() readfloat (EXT_CSQC)\n\t{\"readdouble\",\t\t\t\tPF_ReadDouble,\t\t\t\t\t0},\t\t// #367 __double() readdouble (EXT_CSQC)\n\t{\"readint\",\t\t\t\t\tPF_ReadInt,\t\t\t\t\t\t0},\t\t// #0 int() readint\n\t{\"readint64\",\t\t\t\tPF_ReadInt64,\t\t\t\t\t0},\t\t// #0 __int64() readint64\n\t{\"readuint64\",\t\t\t\tPF_ReadUInt64,\t\t\t\t\t0},\t\t// #0 __uint64() readuint64\n\t{\"readentitynum\",\t\t\tPF_ReadEntityNum,\t\t\t\t368},\t// #368 float() readentitynum (EXT_CSQC)\n\n//\t{\"readserverentitystate\",\tPF_ReadServerEntityState,\t\t369},\t// #369 void(float flags, float simtime) readserverentitystate (EXT_CSQC_1)\n//\t{\"readsingleentitystate\",\tPF_ReadSingleEntityState,\t\t370},\n\t{\"deltalisten\",\t\t\t\tPF_DeltaListen,\t\t\t\t\t371},\t\t// #371 float(string modelname, float flags) deltalisten  (EXT_CSQC_1)\n\n\t{\"dynamiclight_spawnstatic\",PF_R_DynamicLight_AddStatic,0},\n\t{\"dynamiclight_get\",\t\tPF_R_DynamicLight_Get,\t\t372},\n\t{\"dynamiclight_set\",\t\tPF_R_DynamicLight_Set,\t\t373},\n\t{\"particleeffectquery\",\t\tPF_cs_particleeffectquery,\t374},\n\t{\"adddecal\",\t\t\t\tPF_R_AddDecal,\t\t\t\t375},\n\t{\"setcustomskin\",\t\t\tPF_cs_setcustomskin,\t\t376},\n\t{\"loadcustomskin\",\t\t\tPF_cs_loadcustomskin,\t\t377},\n\t{\"applycustomskin\",\t\t\tPF_cs_applycustomskin,\t\t378},\n\t{\"releasecustomskin\",\t\tPF_cs_releasecustomskin,\t379},\n\n\t{\"memalloc\",\t\t\t\tPF_memalloc,\t\t\t\t384},\n\t{\"memrealloc\",\t\t\t\tPF_memrealloc,\t\t\t\t0},\n\t{\"memfree\",\t\t\t\t\tPF_memfree,\t\t\t\t\t385},\n\t{\"memcmp\",\t\t\t\t\tPF_memcmp,\t\t\t\t\t0},\n\t{\"memcpy\",\t\t\t\t\tPF_memcpy,\t\t\t\t\t386},\n\t{\"memfill8\",\t\t\t\tPF_memfill8,\t\t\t\t387},\n\t{\"memgetval\",\t\t\t\tPF_memgetval,\t\t\t\t388},\n\t{\"memsetval\",\t\t\t\tPF_memsetval,\t\t\t\t389},\n\t{\"memptradd\",\t\t\t\tPF_memptradd,\t\t\t\t390},\n\t{\"memstrsize\",\t\t\t\tPF_memstrsize,\t\t\t\t0},\n\t{\"base64encode\",\t\t\tPF_base64encode,\t\t\t0},\n\t{\"base64decode\",\t\t\tPF_base64decode,\t\t\t0},\n\n\t{\"con_getset\",\t\t\t\tPF_SubConGetSet,\t\t\t391},\n\t{\"con_printf\",\t\t\t\tPF_SubConPrintf,\t\t\t392},\n\t{\"con_draw\",\t\t\t\tPF_SubConDraw,\t\t\t\t393},\n\t{\"con_input\",\t\t\t\tPF_SubConInput,\t\t\t\t394},\n\n\t{\"setwindowcaption\",\t\tPF_cl_setwindowcaption,\t\t0},\n\n\t{\"cvars_haveunsaved\",\t\tPF_cvars_haveunsaved,\t\t0},\n\t{\"entityprotection\",\t\tPF_entityprotection,\t\t0},\n//400\n\t{\"copyentity\",\t\t\t\tPF_copyentity,\t\t\t\t400},\t// #400 void(entity from, entity to) copyentity (DP_QC_COPYENTITY)\n\t{\"setcolor\",\t\t\t\tPF_NoCSQC,\t\t\t\t\t401},\t// #401 void(entity cl, float colours) setcolors (DP_SV_SETCOLOR) (don't implement)\n\t{\"findchain\",\t\t\t\tPF_findchain,\t\t\t\t402},\t// #402 entity(string field, string match) findchain (DP_QC_FINDCHAIN)\n\t{\"findchainfloat\",\t\t\tPF_findchainfloat,\t\t\t403},\t// #403 entity(float fld, float match) findchainfloat (DP_QC_FINDCHAINFLOAT)\n\t{\"effect\",\t\t\t\t\tPF_cl_effect,\t\t\t\t404},\t\t// #404 void(vector org, string modelname, float startframe, float endframe, float framerate) effect (DP_SV_EFFECT)\n\n\t{\"te_blood\",\t\t\t\tPF_cl_te_blooddp,\t\t\t405},\t// #405 void(vector org, vector velocity, float howmany) te_blood (DP_TE_BLOOD)\n\t{\"te_bloodshower\",\t\t\tPF_cl_te_bloodshower,\t\t406},\t\t// #406 void(vector mincorner, vector maxcorner, float explosionspeed, float howmany) te_bloodshower (DP_TE_BLOODSHOWER)\n\t{\"te_explosionrgb\",\t\t\tPF_cl_te_explosionrgb,\t\t407},\t// #407 void(vector org, vector color) te_explosionrgb (DP_TE_EXPLOSIONRGB)\n\t{\"te_particlecube\",\t\t\tPF_cl_te_particlecube,\t\t408},\t\t// #408 void(vector mincorner, vector maxcorner, vector vel, float howmany, float color, float gravityflag, float randomveljitter) te_particlecube (DP_TE_PARTICLECUBE)\n\t{\"te_particlerain\",\t\t\tPF_cl_te_particlerain,\t\t409},\t// #409 void(vector mincorner, vector maxcorner, vector vel, float howmany, float color) te_particlerain (DP_TE_PARTICLERAIN)\n\n\t{\"te_particlesnow\",\t\t\tPF_cl_te_particlesnow,\t\t410},\t\t// #410 void(vector mincorner, vector maxcorner, vector vel, float howmany, float color) te_particlesnow (DP_TE_PARTICLESNOW)\n\t{\"te_spark\",\t\t\t\tPF_cl_te_spark,\t\t\t\t411},\t\t// #411 void(vector org, vector vel, float howmany) te_spark (DP_TE_SPARK)\n\t{\"te_gunshotquad\",\t\t\tPF_cl_te_gunshotquad,\t\t412},\t// #412 void(vector org) te_gunshotquad (DP_TE_QUADEFFECTS1)\n\t{\"te_spikequad\",\t\t\tPF_cl_te_spikequad,\t\t\t413},\t\t// #413 void(vector org) te_spikequad (DP_TE_QUADEFFECTS1)\n\t{\"te_superspikequad\",\t\tPF_cl_te_superspikequad,\t414},\t// #414 void(vector org) te_superspikequad (DP_TE_QUADEFFECTS1)\n\n\t{\"te_explosionquad\",\t\tPF_cl_te_explosionquad,\t\t415},\t// #415 void(vector org) te_explosionquad (DP_TE_QUADEFFECTS1)\n\t{\"te_smallflash\",\t\t\tPF_cl_te_smallflash,\t\t416},\t// #416 void(vector org) te_smallflash (DP_TE_SMALLFLASH)\n\t{\"te_customflash\",\t\t\tPF_cl_te_customflash,\t\t417},\t// #417 void(vector org, float radius, float lifetime, vector color) te_customflash (DP_TE_CUSTOMFLASH)\n\t{\"te_gunshot\",\t\t\t\tPF_cl_te_gunshot,\t\t\t418},\t\t// #418 void(vector org) te_gunshot (DP_TE_STANDARDEFFECTBUILTINS)\n\t{\"te_spike\",\t\t\t\tPF_cl_te_spike,\t\t\t\t419},\t\t// #419 void(vector org) te_spike (DP_TE_STANDARDEFFECTBUILTINS)\n\n\t{\"te_superspike\",\t\t\tPF_cl_te_superspike,420},\t\t// #420 void(vector org) te_superspike (DP_TE_STANDARDEFFECTBUILTINS)\n\t{\"te_explosion\",\t\t\tPF_cl_te_explosion,\t421},\t\t// #421 void(vector org) te_explosion (DP_TE_STANDARDEFFECTBUILTINS)\n\t{\"te_tarexplosion\",\t\t\tPF_cl_te_tarexplosion,422},\t\t// #422 void(vector org) te_tarexplosion (DP_TE_STANDARDEFFECTBUILTINS)\n\t{\"te_wizspike\",\t\t\t\tPF_cl_te_wizspike,\t423},\t\t// #423 void(vector org) te_wizspike (DP_TE_STANDARDEFFECTBUILTINS)\n\t{\"te_knightspike\",\t\t\tPF_cl_te_knightspike,424},\t\t// #424 void(vector org) te_knightspike (DP_TE_STANDARDEFFECTBUILTINS)\n\n\t{\"te_lavasplash\",\t\t\tPF_cl_te_lavasplash,425},\t\t// #425 void(vector org) te_lavasplash  (DP_TE_STANDARDEFFECTBUILTINS)\n\t{\"te_teleport\",\t\t\t\tPF_cl_te_teleport,\t426},\t\t// #426 void(vector org) te_teleport (DP_TE_STANDARDEFFECTBUILTINS)\n\t{\"te_explosion2\",\t\t\tPF_cl_te_explosion2,427},\t\t// #427 void(vector org, float color, float colorlength) te_explosion2 (DP_TE_STANDARDEFFECTBUILTINS)\n\t{\"te_lightning1\",\t\t\tPF_cl_te_lightning1,\t428},\t// #428 void(entity own, vector start, vector end) te_lightning1 (DP_TE_STANDARDEFFECTBUILTINS)\n\t{\"te_lightning2\",\t\t\tPF_cl_te_lightning2,429},\t\t// #429 void(entity own, vector start, vector end) te_lightning2 (DP_TE_STANDARDEFFECTBUILTINS)\n\n\t{\"te_lightning3\",\t\t\tPF_cl_te_lightning3,\t\t\t\t430},\t\t// #430 void(entity own, vector start, vector end) te_lightning3 (DP_TE_STANDARDEFFECTBUILTINS)\n\t{\"te_beam\",\t\t\t\t\tPF_cl_te_beam,\t\t\t\t\t\t431},\t\t// #431 void(entity own, vector start, vector end) te_beam (DP_TE_STANDARDEFFECTBUILTINS)\n\t{\"vectorvectors\",\t\t\tPF_vectorvectors,\t\t\t\t\t432},\t\t// #432 void(vector dir) vectorvectors (DP_QC_VECTORVECTORS)\n\t{\"te_plasmaburn\",\t\t\tPF_cl_te_plasmaburn,\t\t\t\t433},\t\t// #433 void(vector org) te_plasmaburn (DP_TE_PLASMABURN)\n\t{\"getsurfacenumpoints\",\t\tPF_getsurfacenumpoints,\t\t\t\t434},\t\t// #434 float(entity e, float s) getsurfacenumpoints (DP_QC_GETSURFACE)\n\n\t{\"getsurfacepoint\",\t\t\tPF_getsurfacepoint,\t\t\t\t\t435},\t\t// #435 vector(entity e, float s, float n) getsurfacepoint (DP_QC_GETSURFACE)\n\t{\"getsurfacenormal\",\t\tPF_getsurfacenormal,\t\t\t\t436},\t\t// #436 vector(entity e, float s) getsurfacenormal (DP_QC_GETSURFACE)\n\t{\"getsurfacetexture\",\t\tPF_getsurfacetexture,\t\t\t\t437},\t\t\t// #437 string(entity e, float s) getsurfacetexture (DP_QC_GETSURFACE)\n\t{\"getsurfacenearpoint\",\t\tPF_getsurfacenearpoint,\t\t\t\t438},\t\t// #438 float(entity e, vector p) getsurfacenearpoint (DP_QC_GETSURFACE)\n\t{\"getsurfaceclippedpoint\",\tPF_getsurfaceclippedpoint,\t\t\t439},\t\t\t// #439 vector(entity e, float s, vector p) getsurfaceclippedpoint (DP_QC_GETSURFACE)\n\n\t{\"clientcommand\",\t\t\tPF_NoCSQC,\t\t\t440},\t\t// #440 void(entity e, string s) clientcommand (KRIMZON_SV_PARSECLIENTCOMMAND) (don't implement)\n\t{\"tokenize\",\t\t\t\tPF_Tokenize,\t\t441},\t\t// #441 float(string s) tokenize (KRIMZON_SV_PARSECLIENTCOMMAND)\n\t{\"argv\",\t\t\t\t\tPF_ArgV,\t\t\t442},\t\t// #442 string(float n) argv (KRIMZON_SV_PARSECLIENTCOMMAND)\n\t{\"argc\",\t\t\t\t\tPF_ArgC,\t\t\t0},\t\t\t// #0 float() argc pointless, but whatever\n\t{\"setattachment\",\t\t\tPF_setattachment,\t443},\t\t// #443 void(entity e, entity tagentity, string tagname) setattachment (DP_GFX_QUAKE3MODELTAGS)\n\t{\"search_begin\",\t\t\tPF_search_begin,\t444},\t\t// #444 float\tsearch_begin(string pattern, float caseinsensitive, float quiet) (DP_QC_FS_SEARCH)\n\n\t{\"search_end\",\t\t\t\tPF_search_end,\t\t\t445},\t// #445 void\tsearch_end(float handle) (DP_QC_FS_SEARCH)\n\t{\"search_getsize\",\t\t\tPF_search_getsize,\t446},\t\t// #446 float\tsearch_getsize(float handle) (DP_QC_FS_SEARCH)\n\t{\"search_getfilename\",\t\tPF_search_getfilename,447},\t\t// #447 string\tsearch_getfilename(float handle, float num) (DP_QC_FS_SEARCH)\n\t{\"search_getfilesize\",\t\tPF_search_getfilesize,\t0},\n\t{\"search_getfilemtime\",\t\tPF_search_getfilemtime,\t0},\n\t{\"search_getpackagename\",\tPF_search_getpackagename, 0},\n\t{\"search_fopen\",\t\t\tPF_search_fopen,\t\t\t0},\n\t{\"cvar_string\",\t\t\t\tPF_cvar_string,\t\t448},\t\t// #448 string(float n) cvar_string (DP_QC_CVAR_STRING)\n\t{\"findflags\",\t\t\t\tPF_FindFlags,\t\t449},\t\t// #449 entity(entity start, .entity fld, float match) findflags (DP_QC_FINDFLAGS)\n\n\t{\"findchainflags\",\t\t\tPF_findchainflags,\t450},\t\t// #450 entity(.float fld, float match) findchainflags (DP_QC_FINDCHAINFLAGS)\n\t{\"gettagindex\",\t\t\t\tPF_gettagindex,\t\t451},\t\t// #451 float(entity ent, string tagname) gettagindex (DP_MD3_TAGSINFO)\n\t{\"gettaginfo\",\t\t\t\tPF_gettaginfo,\t\t452},\t\t// #452 vector(entity ent, float tagindex) gettaginfo (DP_MD3_TAGSINFO)\n\t{\"dropclient\",\t\t\t\tPF_NoCSQC,\t\t\t453},\t\t// #453 void(entity player) dropclient (DP_SV_BOTCLIENT) (don't implement)\n\t{\"spawnclient\",\t\t\t\tPF_NoCSQC,\t\t\t454},\t\t// #454\tentity() spawnclient (DP_SV_BOTCLIENT) (don't implement)\n\n\t{\"clienttype\",\t\t\t\tPF_NoCSQC,\t\t\t455},\t\t// #455 float(entity client) clienttype (DP_SV_BOTCLIENT) (don't implement)\n\n\n//\t{\"WriteUnterminatedString\",PF_WriteString2,\t\t456},\t//writestring but without the null terminator. makes things a little nicer.\n\n//DP_TE_FLAMEJET\n\t{\"te_flamejet\",\t\t\t\tPF_cl_te_flamejet,\t\t\t457},\t// #457 void(vector org, vector vel, float howmany) te_flamejet\n\n\t//no 458 documented.\n\n//DP_QC_EDICT_NUM\n\t{\"edict_num\",\t\t\t\tPF_edict_for_num,\t\t459},\t// #459 entity(float entnum) edict_num\n\n//DP_QC_STRINGBUFFERS\n\t{\"buf_create\",\t\t\t\tPF_buf_create,\t\t460},\t// #460 float() buf_create\n\t{\"buf_del\",\t\t\t\t\tPF_buf_del,\t\t\t\t461},\t// #461 void(float bufhandle) buf_del\n\t{\"buf_getsize\",\t\t\t\tPF_buf_getsize,\t\t462},\t// #462 float(float bufhandle) buf_getsize\n\t{\"buf_copy\",\t\t\t\tPF_buf_copy,\t\t463},\t// #463 void(float bufhandle_from, float bufhandle_to) buf_copy\n\t{\"buf_sort\",\t\t\t\tPF_buf_sort,\t\t464},\t// #464 void(float bufhandle, float sortpower, float backward) buf_sort\n\t{\"buf_implode\",\t\t\t\tPF_buf_implode,\t\t465},\t// #465 string(float bufhandle, string glue) buf_implode\n\t{\"bufstr_get\",\t\t\t\tPF_bufstr_get,\t\t466},\t// #466 string(float bufhandle, float string_index) bufstr_get\n\t{\"bufstr_set\",\t\t\t\tPF_bufstr_set,\t\t467},\t// #467 void(float bufhandle, float string_index, string str) bufstr_set\n\t{\"bufstr_add\",\t\t\t\tPF_bufstr_add,\t\t468},\t// #468 float(float bufhandle, string str, float order) bufstr_add\n\t{\"bufstr_free\",\t\t\t\tPF_bufstr_free,\t\t\t469},\t// #469 void(float bufhandle, float string_index) bufstr_free\n\n\t//no 470 documented\n\n//DP_QC_ASINACOSATANATAN2TAN\n\t{\"asin\",\t\t\t\t\tPF_asin,\t\t\t471},\t// #471 float(float s) asin\n\t{\"acos\",\t\t\t\t\tPF_acos,\t\t\t472},\t// #472 float(float c) acos\n\t{\"atan\",\t\t\t\t\tPF_atan,\t\t\t473},\t// #473 float(float t) atan\n\t{\"atan2\",\t\t\t\t\tPF_atan2,\t\t\t474},\t// #474 float(float c, float s) atan2\n\t{\"tan\",\t\t\t\t\t\tPF_tan,\t\t\t\t475},\t// #475 float(float a) tan\n\n\n//DP_QC_STRINGCOLORFUNCTIONS\n\t{\"strlennocol\",\t\t\t\tPF_strlennocol,\t\t476},\t// #476 float(string s) strlennocol\n\t{\"strdecolorize\",\t\t\tPF_strdecolorize,\t477},\t// #477 string(string s) strdecolorize\n\n//DP_QC_STRFTIME\n\t{\"strftime\",\t\t\t\tPF_strftime,\t\t478},\t// #478 string(float uselocaltime, string format, ...) strftime\n\n//DP_QC_TOKENIZEBYSEPARATOR\n\t{\"tokenizebyseparator\",\t\tPF_tokenizebyseparator,\t479},\t// #479 float(string s, string separator1, ...) tokenizebyseparator\n\n//DP_QC_STRING_CASE_FUNCTIONS\n\t{\"strtolower\",\t\t\t\tPF_strtolower,\t\t480},\t// #476 string(string s) strtolower\n\t{\"strtoupper\",\t\t\t\tPF_strtoupper,\t\t481},\t// #476 string(string s) strlennocol\n\n//DP_QC_CVAR_DEFSTRING\n\t{\"cvar_defstring\",\t\t\tPF_cvar_defstring,\t482},\t// #482 string(string s) cvar_defstring\n\n//DP_SV_POINTSOUND\n\t{\"pointsound\",\t\t\t\tPF_cs_pointsound,\t\t483},\t// #483 void(vector origin, string sample, float volume, float attenuation) pointsound\n\n//DP_QC_STRREPLACE\n\t{\"strreplace\",\t\t\t\tPF_strreplace,\t\t484},\t// #484 string(string search, string replace, string subject) strreplace\n\t{\"strireplace\",\t\t\t\tPF_strireplace,\t\t485},\t// #485 string(string search, string replace, string subject) strireplace\n\n\n//DP_QC_GETSURFACEPOINTATTRIBUTE\n\t{\"getsurfacepointattribute\",PF_getsurfacepointattribute,\t486},\t// #486vector(entity e, float s, float n, float a) getsurfacepointattribute\n\n#ifdef HAVE_MEDIA_DECODER\n//DP_GECKO_SUPPORT\n\t{\"gecko_create\",\t\t\tPF_cs_media_create,\t\t\t\t487},\t// #487 float(string name)\n\t{\"gecko_destroy\",\t\t\tPF_cs_media_destroy,\t\t\t488},\t// #488 void(string name)\n\t{\"gecko_navigate\",\t\t\tPF_cs_media_command,\t\t\t489},\t// #489 void(string name, string URI)\n\t{\"gecko_keyevent\",\t\t\tPF_cs_media_keyevent,\t\t\t490},\t// #490 float(string name, float key, float eventtype)\n\t{\"gecko_mousemove\",\t\t\tPF_cs_media_mousemove,\t\t\t491},\t// #491 void(string name, float x, float y)\n\t{\"gecko_resize\",\t\t\tPF_cs_media_resize,\t\t\t\t492},\t// #492 void(string name, float w, float h)\n\t{\"gecko_get_texture_extent\",PF_cs_media_get_texture_extent,\t493},\t// #493 vector(string name)\n\t{\"gecko_getproperty\",\t\tPF_cs_media_getproperty},\n\t{\"cin_open\",\t\t\t\tPF_cs_media_create},\n\t{\"cin_close\",\t\t\t\tPF_cs_media_destroy},\n\t{\"cin_setstate\",\t\t\tPF_cs_media_setstate},\n\t{\"cin_getstate\",\t\t\tPF_cs_media_getstate},\n\t{\"cin_restart\",\t\t\t\tPF_cs_media_restart},\n#endif\n\n//DP_QC_CRC16\n\t{\"crc16\",\t\t\t\t\tPF_crc16,\t\t\t\t\t494},\t// #494 float(float caseinsensitive, string s, ...) crc16\n\n//DP_QC_CVAR_TYPE\n\t{\"cvar_type\",\t\t\t\tPF_cvar_type,\t\t\t\t495},\t// #495 float(string name) cvar_type\n\n//DP_QC_ENTITYDATA\n\t{\"numentityfields\",\t\t\tPF_numentityfields,\t\t\t496},\t// #496 float() numentityfields\n\t{\"findentityfield\",\t\t\tPF_findentityfield,\t\t\t0},\n\t{\"entityfieldref\",\t\t\tPF_entityfieldref,\t\t\t0},\n\t{\"entityfieldname\",\t\t\tPF_entityfieldname,\t\t\t497},\t// #497 string(float fieldnum) entityfieldname\n\t{\"entityfieldtype\",\t\t\tPF_entityfieldtype,\t\t\t498},\t// #498 float(float fieldnum) entityfieldtype\n\t{\"getentityfieldstring\",\tPF_getentityfieldstring,\t499},\t// #499 string(float fieldnum, entity ent) getentityfieldstring\n\t{\"putentityfieldstring\",\tPF_putentityfieldstring,\t500},\t// #500 float(float fieldnum, entity ent, string s) putentityfieldstring\n\n//DP_SV_WRITEPICTURE\n\t{\"ReadPicture\",\t\t\t\tPF_ReadPicture,\t\t\t\t501},\t// #501 void(float to, string s, float sz) WritePicture\n\n\t{\"boxparticles\",\t\t\tPF_cs_boxparticles,\t\t\t502},\n\n//DP_QC_WHICHPACK\n\t{\"whichpack\",\t\t\t\tPF_whichpack,\t\t\t\t503},\t// #503 string(string filename) whichpack\n\n//DP_CSQC_QUERYRENDERENTITY\n\t{\"getentity\",\t\t\t\tPF_getentity,\t\t\t\t504},\t// #504 __variant(float entnum, fload fieldnum) getentity\n\n//DP_QC_URI_ESCAPE\n\t{\"uri_escape\",\t\t\t\tPF_uri_escape,\t\t\t\t510},\t// #510 string(string in) uri_escape\n\t{\"uri_unescape\",\t\t\tPF_uri_unescape,\t\t\t511},\t// #511 string(string in) uri_unescape = #511;\n\n//DP_QC_NUM_FOR_EDICT\n\t{\"num_for_edict\",\t\t\tPF_num_for_edict,\t\t\t512},\t// #512 float(entity ent) num_for_edict\n\n//DP_QC_URI_GET\n\t{\"uri_get\",\t\t\t\t\tPF_uri_get,\t\t\t\t\t513},\t// #513 float(string uril, float id) uri_get\n\t{\"uri_post\",\t\t\t\tPF_uri_get,\t\t\t\t\t513},\t// #513 float(string uril, float id) uri_post\n\n\t{\"tokenize_console\",\t\tPF_tokenize_console,\t\t514},\n\t{\"argv_start_index\",\t\tPF_argv_start_index,\t\t515},\n\t{\"argv_end_index\",\t\t\tPF_argv_end_index,\t\t\t516},\n\t{\"buf_cvarlist\",\t\t\tPF_buf_cvarlist,\t\t\t517},\n\t{\"cvar_description\",\t\tPF_cvar_description,\t\t518},\n\n\t{\"gettimef\",\t\t\t\tPF_gettimef,\t\t\t\t519},\n\t{\"gettimed\",\t\t\t\tPF_gettimed,\t\t\t\t0},\n\n\t{\"keynumtostring_omgwtf\",\tPF_cl_keynumtostring,\t\t520},\n\t{\"findkeysforcommand\",\t\tPF_cl_findkeysforcommand,\t521},\n\t{\"findkeysforcommandex\",\tPF_cl_findkeysforcommandex,\t0},\n\n\t{\"loadfromdata\",\t\t\tPF_loadfromdata,\t\t\t529},\n\t{\"loadfromfile\",\t\t\tPF_loadfromfile,\t\t\t530},\n\t{\"setpause\",\t\t\t\tPF_cl_setpause,\t\t\t\t531},\n\t{\"log\",\t\t\t\t\t\tPF_Logarithm,\t\t\t\t532},\n\n\t{\"stopsound\",\t\t\t\tPF_stopsound,\t\t\t\t0},\n\t{\"soundupdate\",\t\t\t\tPF_soundupdate,\t\t\t\t0},\n\t{\"getsoundtime\",\t\t\tPF_getsoundtime,\t\t\t533},\n\t{\"getchannellevel\",\t\t\tPF_getchannellevel,\t\t\t0},\n\t{\"soundlength\",\t\t\t\tPF_soundlength,\t\t\t\t534},\n\t{\"buf_loadfile\",\t\t\tPF_buf_loadfile,\t\t\t535},\n\t{\"buf_writefile\",\t\t\tPF_buf_writefile,\t\t\t536},\n\t{\"bufstr_find\",\t\t\t\tPF_bufstr_find,\t\t\t\t537},\n//\t{\"matchpattern\",\t\t\tPF_Fixme,\t\t\t\t\t538},\n//\t{\"undefined\",\t\t\t\tPF_Fixme,\t\t\t\t\t539},\n\n#ifdef USERBE\n\t{\"physics_enable\",\t\t\tPF_physics_enable,\t\t\t540},\n\t{\"physics_addforce\",\t\tPF_physics_addforce,\t\t541},\n\t{\"physics_addtorque\",\t\tPF_physics_addtorque,\t\t542},\n#endif\n\n\t{\"setmousetarget\",\t\t\tPF_cl_setmousetarget,\t\t603},\n\t{\"getmousetarget\",\t\t\tPF_cl_getmousetarget,\t\t604},\n\n\t{\"callfunction\",\t\t\tPF_callfunction,\t\t\t605},\n\t{\"writetofile\",\t\t\t\tPF_writetofile,\t\t\t\t606},\n\t{\"isfunction\",\t\t\t\tPF_isfunction,\t\t\t\t607},\n\t{\"getresolution\",\t\t\tPF_cl_getresolution,\t\t608},\n\t{\"keynumtostring_menu\",\t\tPF_cl_keynumtostring,\t\t609},\t//while present in dp's menuqc, dp doesn't actually support keynumtostring=609 in csqc. Which is probably a good thing because csqc would have 3 separate versions if it did.\n\n\t{\"findkeysforcommand_menu\",\tPF_cl_findkeysforcommand,\t610},\n\t{\"gethostcachevalue\",\t\tPF_cl_gethostcachevalue,\t611},\n\t{\"gethostcachestring\",\t\tPF_cl_gethostcachestring,\t612},\n\t{\"parseentitydata\",\t\t\tPF_parseentitydata,\t\t\t613},\n\t{\"generateentitydata\",\t\tPF_generateentitydata,\t\t0},\n\t{\"stringtokeynum_menu\",\t\tPF_cl_stringtokeynum,\t\t614},\n\n\t{\"resethostcachemasks\",\t\tPF_cl_resethostcachemasks,\t\t615},\n\t{\"sethostcachemaskstring\",\tPF_cl_sethostcachemaskstring,\t616},\n\t{\"sethostcachemasknumber\",\tPF_cl_sethostcachemasknumber,\t617},\n\t{\"resorthostcache\",\t\t\tPF_cl_resorthostcache,\t\t\t618},\n\t{\"sethostcachesort\",\t\tPF_cl_sethostcachesort,\t\t\t619},\n\t{\"refreshhostcache\",\t\tPF_cl_refreshhostcache,\t\t\t620},\n\t{\"gethostcachenumber\",\t\tPF_cl_gethostcachenumber,\t\t621},\n\t{\"gethostcacheindexforkey\",\tPF_cl_gethostcacheindexforkey,\t622},\n\t{\"addwantedhostcachekey\",\tPF_cl_addwantedhostcachekey,\t623},\n#ifdef CL_MASTER\n\t{\"getextresponse\",\t\t\tPF_cl_getextresponse,\t\t624},\n#endif\n\t{\"netaddress_resolve\",\t\tPF_netaddress_resolve,\t\t625},\n\t{\"getgamedirinfo\",\t\t\tPF_cl_getgamedirinfo,\t\t626},\n#ifdef PACKAGEMANAGER\n\t{\"getpackagemanagerinfo\",\tPF_cl_getpackagemanagerinfo,0},\n#endif\n\t{\"sprintf\",\t\t\t\t\tPF_sprintf,\t\t\t\t\t627},\n\t{\"getsurfacenumtriangles\",\tPF_getsurfacenumtriangles,\t628},\n\t{\"getsurfacetriangle\",\t\tPF_getsurfacetriangle,\t\t629},\n\n\t{\"setkeybind\",\t\t\t\tPF_cl_setkeybind,\t\t\t630},\n\t{\"getbindmaps\",\t\t\t\tPF_cl_GetBindMap,\t\t\t631},\n\t{\"setbindmaps\",\t\t\t\tPF_cl_SetBindMap,\t\t\t632},\n//\t{NULL,\t\t\t\t\t\tPF_Fixme,\t\t\t\t\t633},\n//\t{NULL,\t\t\t\t\t\tPF_Fixme,\t\t\t\t\t634},\n//\t{NULL,\t\t\t\t\t\tPF_Fixme,\t\t\t\t\t635},\n//\t{NULL,\t\t\t\t\t\tPF_Fixme,\t\t\t\t\t636},\n//\t{NULL,\t\t\t\t\t\tPF_Fixme,\t\t\t\t\t637},\n\t{\"CL_RotateMoves\",\t\t\tPF_cl_RotateMoves,\t\t\t638},\n\t{\"digest_hex\",\t\t\t\tPF_digest_hex,\t\t\t\t639},\n\t{\"digest_ptr\",\t\t\t\tPF_digest_ptr,\t\t\t\t0},\n\t{\"V_CalcRefdef\",\t\t\tPF_V_CalcRefdef,\t\t\t640},\n//\t{NULL,\t\t\t\t\t\tPF_Fixme,\t\t\t\t\t641},\n//\t{NULL,\t\t\t\t\t\tPF_Fixme,\t\t\t\t\t642},\n//\t{NULL,\t\t\t\t\t\tPF_Fixme,\t\t\t\t\t643},\n//\t{NULL,\t\t\t\t\t\tPF_Fixme,\t\t\t\t\t644},\n//\t{NULL,\t\t\t\t\t\tPF_Fixme,\t\t\t\t\t645},\n//\t{NULL,\t\t\t\t\t\tPF_Fixme,\t\t\t\t\t646},\n//\t{NULL,\t\t\t\t\t\tPF_Fixme,\t\t\t\t\t647},\n//\t{NULL,\t\t\t\t\t\tPF_Fixme,\t\t\t\t\t648},\n//\t{NULL,\t\t\t\t\t\tPF_Fixme,\t\t\t\t\t649},\n\t{\"fcopy\",\t\t\t\t\tPF_fcopy,\t\t\t\t\t650},\n\t{\"frename\",\t\t\t\t\tPF_frename,\t\t\t\t\t651},\n\t{\"fremove\",\t\t\t\t\tPF_fremove,\t\t\t\t\t652},\n\t{\"fexists\",\t\t\t\t\tPF_fexists,\t\t\t\t\t653},\n\t{\"rmtree\",\t\t\t\t\tPF_rmtree,\t\t\t\t\t654},\n\n\t{\"stachievement_unlock\",\tPF_Fixme,\t\t\t\t\t730},\t// EXT_STEAM_REKI\n\t{\"stachievement_query\",\t\tPF_Fixme,\t\t\t\t\t731},\t// EXT_STEAM_REKI\n\t{\"ststat_setvalue\",\t\t\tPF_Fixme,\t\t\t\t\t732},\t// EXT_STEAM_REKI\n\t{\"ststat_increment\",\t\tPF_Fixme,\t\t\t\t\t733},\t// EXT_STEAM_REKI\n\t{\"ststat_query\",\t\t\tPF_Fixme,\t\t\t\t\t734},\t// EXT_STEAM_REKI\n\t{\"stachievement_register\",\tPF_Fixme,\t\t\t\t\t735},\t// EXT_STEAM_REKI\n\t{\"ststat_register\",\t\t\tPF_Fixme,\t\t\t\t\t736},\t// EXT_STEAM_REKI\n\t{\"controller_query\",\t\tPF_cl_gp_querywithcb,\t\t740},\t// EXT_CONTROLLER_REKI\n\t{\"controller_rumble\",\t\tPF_cl_gp_rumble,\t\t\t741},\t// EXT_CONTROLLER_REKI\n\t{\"controller_rumbletriggers\",PF_cl_gp_rumbletriggers,\t742},\t// EXT_CONTROLLER_REKI\n\n\t{\"gp_getlayout\",\t\t\tPF_cl_gp_getbuttontype,\t\t0}, // #0 float(float devid) gp_getbuttontype\n\t{\"gp_rumble\",\t\t\t\tPF_cl_gp_rumble,\t\t\t0}, // #0 void(float devid, float amp_low, float amp_high, float duration) gp_rumble\n\t{\"gp_rumbletriggers\",\t\tPF_cl_gp_rumbletriggers,\t0}, // #0 void(float devid, float left, float right, float duration) gp_rumbletriggers\n\t{\"gp_setledcolor\",\t\t\tPF_cl_gp_setledcolor,\t\t0}, // #0 void(float devid, float red, float green, float blue) gp_setledcolor\n\t{\"gp_settriggerfx\",\t\t\tPF_cl_gp_settriggerfx,\t\t0}, // #0 void(float devid, const void *data, int size) gp_settriggerfx\n\n\t{NULL}\n};\n\nstatic int PR_CSQC_NamedBuiltinUnsupported(const char *name)\n{\n\tint i;\n\tfor (i = 0; BuiltinList[i].name; i++)\n\t{\n\t\tif (!strcmp(BuiltinList[i].name, name))\n\t\t\treturn (BuiltinList[i].bifunc == PF_Fixme);\n\t}\n\treturn false;\n}\nint PR_CSQC_BuiltinValid(char *name, int num)\n{\n\tint i;\n\tfor (i = 0; BuiltinList[i].name; i++)\n\t{\n\t\tif (BuiltinList[i].ebfsnum == num)\n\t\t{\n\t\t\tif (!strcmp(BuiltinList[i].name, name))\n\t\t\t{\n\t\t\t\tif (BuiltinList[i].bifunc == PF_NoCSQC || BuiltinList[i].bifunc == PF_Fixme)\n\t\t\t\t\treturn false;\n\t\t\t\telse\n\t\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t}\n\treturn false;\n}\n\nstatic builtin_t csqc_builtin[800];\n\nstatic int PDECL PR_CSQC_MapNamedBuiltin(pubprogfuncs_t *progfuncs, int headercrc, const char *builtinname)\n{\n\tint i, binum;\n\tfor (i = 0;BuiltinList[i].name;i++)\n\t{\n\t\tif (!strcmp(BuiltinList[i].name, builtinname) && BuiltinList[i].bifunc != PF_Fixme)\n\t\t{\n\t\t\tfor (binum = sizeof(csqc_builtin)/sizeof(csqc_builtin[0]); --binum; )\n\t\t\t{\n\t\t\t\tif (csqc_builtin[binum] && csqc_builtin[binum] != PF_Fixme && BuiltinList[i].bifunc)\n\t\t\t\t\tcontinue;\n\t\t\t\tcsqc_builtin[binum] = BuiltinList[i].bifunc;\n\t\t\t\treturn binum;\n\t\t\t}\n\t\t\tCon_Printf(\"No more builtin slots to allocate for %s\\n\", builtinname);\n\t\t\tbreak;\n\t\t}\n\t}\n\tif (!csqc_nogameaccess)\n\t\tCon_DPrintf(\"Unknown csqc builtin: %s\\n\", builtinname);\n\treturn 0;\n}\n\n\n\n\nstatic jmp_buf csqc_abort;\nstatic progparms_t csqcprogparms;\n\n\n//Any menu builtin error or anything like that will come here.\nstatic void VARGS CSQC_Abort (const char *format, ...)\t//an error occured.\n{\n\tva_list\t\targptr;\n\tchar\t\tstring[1024];\n\n\tva_start (argptr, format);\n\tvsnprintf (string,sizeof(string)-1, format,argptr);\n\tva_end (argptr);\n\n\tCon_Printf(\"CSQC_Abort: %s\\nShutting down csqc\\n\", string);\n\n\tif (pr_csqc_coreonerror.value)\n\t{\n\t\tsize_t size = 1024*1024*8;\n\t\tchar *buffer = BZ_Malloc(size);\n\t\tcsqcprogs->save_ents(csqcprogs, buffer, &size, size, 3);\n\t\tCOM_WriteFile(\"csqccore.txt\", FS_GAMEONLY, buffer, size);\n\t\tBZ_Free(buffer);\n\t}\n\n\tHost_EndGame(\"csqc error\");\n}\n\nstatic void PDECL CSQC_EntSpawn (struct edict_s *e, int loading)\n{\n\tstruct csqcedict_s *ent = (csqcedict_t*)e;\n#ifdef VM_Q1\n\tif (!ent->xv)\n\t\tent->xv = (csqcextentvars_t *)(ent->v+1);\n#endif\n\n\tif (1)\n\t{\n//\t\tent->xv->dimension_see = csqc_world.dimension_default;\n//\t\tent->xv->dimension_seen = csqc_world.dimension_default;\n//\t\tent->xv->dimension_ghost = 0;\n\t\tent->xv->dimension_solid = *csqcg.dimension_default;\n\t\tent->xv->dimension_hit = *csqcg.dimension_default;\n\n#ifdef HEXEN2\n\t\tent->xv->drawflags = SCALE_ORIGIN_ORIGIN;\n#endif\n\t}\n}\n\nstatic pbool QDECL CSQC_EntFree (struct edict_s *e)\n{\n\tstruct csqcedict_s *ent = (csqcedict_t*)e;\n\tent->v->solid = SOLID_NOT;\n\tent->v->movetype = 0;\n\tent->v->modelindex = 0;\n\tent->v->think = 0;\n\tent->v->nextthink = 0;\n\tent->xv->predraw = 0;\n\tent->xv->drawmask = 0;\n\tent->xv->renderflags = 0;\n\n\tif (ent->skinobject>0)\n\t\tMod_WipeSkin(ent->skinobject, false);\n\tent->skinobject = 0;\n\n#ifdef USERBE\n\tif (csqc_world.rbe)\n\t{\n\t\tcsqc_world.rbe->RemoveFromEntity(&csqc_world, (wedict_t*)ent);\n\t\tcsqc_world.rbe->RemoveJointFromEntity(&csqc_world, (wedict_t*)ent);\n\t}\n#endif\n\n\treturn true;\n}\n\nstatic void QDECL CSQC_Event_Touch(world_t *w, wedict_t *s, wedict_t *o, trace_t *trace)\n{\n\tint oself = *csqcg.self;\n\tint oother = *csqcg.other;\n\n\t*csqcg.self = EDICT_TO_PROG(w->progs, (edict_t*)s);\n\t*csqcg.other = EDICT_TO_PROG(w->progs, (edict_t*)o);\n\t*csqcg.time = w->physicstime;\n\n\tPR_ExecuteProgram (w->progs, s->v->touch);\n\n\t*csqcg.self = oself;\n\t*csqcg.other = oother;\n}\n\nstatic void QDECL CSQC_Event_Think(world_t *w, wedict_t *s)\n{\n\t*csqcg.self = EDICT_TO_PROG(w->progs, (edict_t*)s);\n\t*csqcg.other = EDICT_TO_PROG(w->progs, (edict_t*)w->edicts);\n\t*csqcg.time = w->physicstime;\n\n\tif (!s->v->think)\n\t\tCon_Printf(\"CSQC entity \\\"%s\\\" has nextthink with no think function\\n\", PR_GetString(w->progs, s->v->classname));\n\telse\n\t\tPR_ExecuteProgram (w->progs, s->v->think);\n}\n\nstatic void QDECL CSQC_Event_Sound (float *origin, wedict_t *wentity, int channel, const char *sample, int volume, float attenuation, float pitchadj, float timeoffset, unsigned int flags)\n{\n\tint i;\n\tvec3_t originbuf;\n\tif (!origin)\n\t{\n\t\tif (wentity->v->solid == SOLID_BSP)\n\t\t{\n\t\t\torigin = originbuf;\n\t\t\tfor (i=0 ; i<3 ; i++)\n\t\t\t\torigin[i] = wentity->v->origin[i]+0.5*(wentity->v->mins[i]+wentity->v->maxs[i]);\n\t\t}\n\t\telse\n\t\t\torigin = wentity->v->origin;\n\t}\n\n\tS_StartSound(NUM_FOR_EDICT(csqcprogs, (edict_t*)wentity), channel, S_PrecacheSound(sample), origin, NULL, volume/255.0, attenuation, timeoffset, pitchadj, flags);\n}\n\nstatic qboolean QDECL CSQC_Event_ContentsTransition(world_t *w, wedict_t *ent, int oldwatertype, int newwatertype)\n{\n\tif (ent->xv->contentstransition)\n\t{\n\t\tvoid *pr_globals = PR_globals(w->progs, PR_CURRENT);\n\t\t*csqcg.self = EDICT_TO_PROG(w->progs, ent);\n\t\t*csqcg.time = w->physicstime;\n\t\tG_FLOAT(OFS_PARM0) = oldwatertype;\n\t\tG_FLOAT(OFS_PARM1) = newwatertype;\n\t\tPR_ExecuteProgram (w->progs, ent->xv->contentstransition);\n\t\treturn true;\n\t}\n\treturn false;\t//do legacy behaviour\n}\n\nstatic model_t *QDECL CSQC_World_ModelForIndex(world_t *w, int modelindex)\n{\n\tmodel_t *mod = CSQC_GetModelForIndex(modelindex);\n\tif (mod && mod->loadstate != MLS_LOADED)\n\t{\n\t\tif (mod->loadstate == MLS_NOTLOADED)\n\t\t\tMod_LoadModel(mod, MLV_SILENT);\n\t\tif (mod->loadstate == MLS_LOADING)\n\t\t\tCOM_WorkerPartialSync(mod, &mod->loadstate, MLS_LOADING);\n\t\tif (mod->loadstate != MLS_LOADED)\n\t\t\tmod = NULL;\t//gah, it failed!\n\t}\n\treturn mod;\n}\nstatic void QDECL CSQC_World_GetFrameState(world_t *w, wedict_t *win, framestate_t *out)\n{\n\tcsqcedict_t *in = (csqcedict_t *)win;\n\tcs_getframestate(in, in->xv->renderflags, out);\n}\n\nstatic qboolean CSQC_GenerateMaterial(struct shaderparsestate_s *ps, const char *materialname, void (*LoadMaterialString)(struct shaderparsestate_s *ps, const char *script))\n{\n\tCOM_AssertMainThread(\"CSQC_GenerateMaterial\");\n\tif (csqcg.CSQC_GenerateMaterial)\n\t{\n\t\tvoid *pr_globals = PR_globals(csqcprogs, PR_CURRENT);\n\t\t(((string_t *)pr_globals)[OFS_PARM0] = PR_TempString(csqcprogs, materialname));\n\t\tPR_ExecuteProgram(csqcprogs, csqcg.CSQC_GenerateMaterial);\n\t\tif (G_INT(OFS_RETURN))\n\t\t{\t//we got the script, now pass it to our material system to parse it.\n\t\t\tLoadMaterialString(ps, PR_GetStringOfs(csqcprogs, OFS_RETURN));\n\t\t\treturn true;\n\t\t}\n\t}\n\treturn false;\n}\nstatic plugmaterialloaderfuncs_t csqcmaterialloader = {\"csqc\", CSQC_GenerateMaterial};\n\nvoid CSQC_Shutdown(void)\n{\n\tint i;\n\tif (csqcprogs)\n\t{\n\t\tif (csqcg.CSQC_Shutdown)\n\t\t\tPR_ExecuteProgram(csqcprogs, csqcg.CSQC_Shutdown);\n\n\t\tMaterial_RegisterLoader(&csqc_world, NULL);\n\n\t\tkey_dest_absolutemouse &= ~kdm_game;\n\t\tPR_ReleaseFonts(kdm_game);\n\t\tPR_Common_Shutdown(csqcprogs, false);\n\t\tWorld_Destroy(&csqc_world);\n\t\tcsqcprogs->Shutdown(csqcprogs);\n\t\tcsqc_world.progs = csqcprogs = NULL;\n\t}\n\telse\n\t{\n\t\tPR_Route_Shutdown (&csqc_world);\n\t\tWorld_Destroy(&csqc_world);\n\t}\n\n\tCmd_RemoveCommands(CS_ConsoleCommand_f);\n\n\n\tZ_Free(csqcdelta_pack_new.e);\n\tmemset(&csqcdelta_pack_new, 0, sizeof(csqcdelta_pack_new));\n\tZ_Free(csqcdelta_pack_old.e);\n\tmemset(&csqcdelta_pack_old, 0, sizeof(csqcdelta_pack_old));\n\n\tmemset(&deltafunction, 0, sizeof(deltafunction));\n\tmemset(csqcdelta_playerents, 0, sizeof(csqcdelta_playerents));\n\n\tZ_Free(csqcent);\n\tcsqcent = NULL;\n\tmaxcsqcentities = 0;\n\n\tfor (i = 0; i < MAX_CSPARTICLESPRE && cl.particle_csname[i]; i++)\n\t{\n\t\tfree(cl.particle_csname[i]);\n\t\tcl.particle_csname[i] = NULL;\n\t}\n\n\tcsqcmapentitydata = NULL;\n\tcsqcmapentitydataloaded = false;\n\n\tin_sensitivityscale = 1;\n\tcsqc_world.num_edicts = 0;\n\tmemset(&csqc_world, 0, sizeof(csqc_world));\n\tmemset(&csqcg, 0, sizeof(csqcg));\n\n\tin_vraim.ival = in_vraim.value;\t//csqc mod with explicit vr stuff.\n\n\tif (csqc_deprecated_warned>1)\n\t{\n\t\tif (!cl_csqc_nodeprecate.ival)\n\t\t\tCon_Printf(\"total %u csqc deprecation warnings suppressed\\n\", csqc_deprecated_warned-1);\n\t\tcsqc_deprecated_warned = 0;\n\t}\n}\n\nstatic qboolean CSQC_ValidateMainCSProgs(void *file, size_t filesize, unsigned int checksum, size_t checksize)\n{\n\tif (!file)\n\t\treturn false;\n\tif (checksize && filesize != checksize)\n\t\treturn false;\n\tif (cls.protocol == CP_NETQUAKE && !(cls.fteprotocolextensions2 & PEXT2_PREDINFO))\n\t{\t//DP uses really lame checksums.\n\t\tif (CalcHashInt(&hash_crc16, file, filesize) != checksum)\n\t\t\treturn false;\n\t}\n\telse\n\t{\t//FTE uses folded-md4. yeah, its broken but at least its still more awkward\n\t\tif (LittleLong(CalcHashInt(&hash_md4, file, filesize)) != checksum)\n\t\t\treturn false;\n\t}\n\treturn true;\n}\nstatic void *CSQC_FindMainProgs(size_t *sz, const char *name, unsigned int checksum, size_t checksize)\n{\t//returns a TempFile\n\tchar newname[MAX_QPATH];\n\tvoid *file = NULL;\n\n\t//the filename we'll cache to\n\tsnprintf(newname, MAX_QPATH, \"csprogsvers/%x.dat\", checksum);\n\n\t//we can use FSLF_IGNOREPURE because we have our own hashes/size checks instead.\n\t//this should make it slightly easier for server admins\n\tif (checksum)\n\t{\n\t\tfile = COM_LoadTempFile (newname, FSLF_IGNOREPURE, sz);\n\t\tif (!CSQC_ValidateMainCSProgs(file, *sz, checksum, checksize))\n\t\t\tfile = NULL;\n\t}\n\n\tif (!file)\n\t{\n\t\tconst char *progsname = cls.state?InfoBuf_ValueForKey(&cl.serverinfo, \"*csprogsname\"):\"csprogs.dat\";\n\t\tflocation_t loc={0};\n\t\tvfsfile_t *f;\n\t\tqboolean found = false;\n\t\tif (!found && *progsname)\n\t\t\tfound = FS_FLocateFile(progsname, FSLF_IGNOREPURE, &loc);\n\t\tif (!found && strcmp(progsname, \"csprogs.dat\"))\n\t\t{\n\t\t\tprogsname = \"csprogs.dat\";\n\t\t\tfound = FS_FLocateFile(progsname, FSLF_IGNOREPURE, &loc);\n\t\t}\n\t\tif (found && (f=FS_OpenReadLocation(progsname, &loc)))\n\t\t{\n\t\t\t*sz = VFS_GETLEN(f);\n\t\t\tfile = Hunk_TempAlloc (*sz);\n\t\t\tVFS_READ(f, file, *sz);\n\t\t\tVFS_CLOSE(f);\n\n\t\t\tif (file && !cls.demoplayback)\t//allow them to use csprogs.dat if playing a demo, and don't care about the checksum\n\t\t\t{\n\t\t\t\tif (checksum && !csprogs_promiscuous)\n\t\t\t\t{\n\t\t\t\t\tif (!CSQC_ValidateMainCSProgs(file, *sz, checksum, checksize))\n\t\t\t\t\t{\t//its not a match... maybe we can get a better one from the pure paths instead...\n\t\t\t\t\t\tfile = COM_LoadTempFile (progsname, 0, sz);\n\t\t\t\t\t\tif (!CSQC_ValidateMainCSProgs(file, *sz, checksum, checksize))\n\t\t\t\t\t\t\tfile = NULL;\n\t\t\t\t\t}\n\n\t\t\t\t\t//we write the csprogs into our archive if it was loaded from outside of there.\n\t\t\t\t\t//this is to ensure that demos will play on the same machine later on...\n\t\t\t\t\t//this is unreliable though, and redundant if we're writing the csqc into the demos themselves.\n\t\t\t\t\t//also kinda irrelevant with sv_pure.\n\t\t\t\t\t//FIXME: don't back up if it was in a package.\n#ifndef FTE_TARGET_WEB\n\t\t\t\t\tif (file && !sv_state && !FS_WhichPackForLocation(&loc, false)\n#if !defined(CLIENTONLY) && defined(MVD_RECORDING)\n\t\t\t\t\t\t&& !sv_demo_write_csqc.ival\n#endif\n\t\t\t\t\t\t)\n\t\t\t\t\t\t//back it up\n\t\t\t\t\t\tCOM_WriteFile(newname, FS_GAMEONLY, file, *sz);\n#endif\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn file;\n}\nqboolean CSQC_CheckDownload(const char *name, unsigned int checksum, size_t checksize)\n{\n\tsize_t sz;\n\tif (CSQC_FindMainProgs(&sz, name, checksum, checksize))\n\t\treturn true;\n\treturn false;\n}\n\n//when the qclib needs a file, it calls out to this function.\nvoid *PDECL CSQC_PRLoadFile (const char *path, unsigned char *(PDECL *buf_get)(void *ctx, size_t len), void *buf_ctx, size_t *sz, pbool issource)\n{\n\tqbyte *file;\n\n\tif (!strcmp(path, csprogs_checkname))\n\t\tfile = CSQC_FindMainProgs(sz, csprogs_checkname, csprogs_checksum, csprogs_checksize);\n\telse\n\t\tfile = COM_LoadTempFile (path, 0, sz);\n\n\tif (file)\n\t{\n\t\tqbyte *buffer = buf_get(buf_ctx, *sz);\n\t\tmemcpy(buffer, file, *sz);\n\t\treturn buffer;\n\t}\n\treturn NULL;\n}\n\nint QDECL CSQC_PRFileSize (const char *path)\n{\n\treturn COM_FileSize(path);\n}\n\nqboolean CSQC_Inited(void)\n{\n\tif (csqcprogs)\n\t\treturn true;\n\treturn false;\n}\n\nqboolean CSQC_UnconnectedOkay(qboolean inprinciple)\n{\n\tif (!pr_csqc_formenus.ival)\n\t\treturn false;\n\n\tif (!inprinciple)\n\t{\n\t\tif (!csqcprogs)\n\t\t\treturn false;\n\t}\n\treturn true;\n}\nqboolean CSQC_UnconnectedInit(void)\n{\n\tif (!CSQC_UnconnectedOkay(true))\n\t\treturn false;\n\n\tif (csqcprogs)\n\t\treturn true;\n\treturn CSQC_Init(true, \"csprogs.dat\", 0, 0);\n}\nvoid ASMCALL CSQC_StateOp(pubprogfuncs_t *prinst, float var, func_t func)\n{\n\tworld_t *w = prinst->parms->user;\n\tstdentvars_t *vars = PROG_TO_EDICT(prinst, *w->g.self)->v;\n\tvars->nextthink = *w->g.time+0.1;\n\tvars->think = func;\n\tvars->frame = var;\n}\nvoid ASMCALL CSQC_CStateOp(pubprogfuncs_t *progs, float first, float last, func_t currentfunc)\n{\n\tfloat min, max;\n\tfloat step;\n\tworld_t *w = progs->parms->user;\n\twedict_t *e = PROG_TO_WEDICT(progs, *w->g.self);\n\tfloat frame = e->v->frame;\n\n//\tif (progstype == PROG_H2)\n//\t\te->v->nextthink = *w->g.time+0.05;\n//\telse\n\t\te->v->nextthink = *w->g.time+0.1;\n\te->v->think = currentfunc;\n\n\tif (csqcg.cycle_wrapped)\n\t\t*csqcg.cycle_wrapped = false;\n\n\tif (first > last)\n\t{\t//going backwards\n\t\tmin = last;\n\t\tmax = first;\n\t\tstep = -1.0;\n\t}\n\telse\n\t{\t//forwards\n\t\tmin = first;\n\t\tmax = last;\n\t\tstep = 1.0;\n\t}\n\tif (frame < min || frame > max)\n\t\tframe = first;\t//started out of range, must have been a different animation\n\telse\n\t{\n\t\tframe += step;\n\t\tif (frame < min || frame > max)\n\t\t{\t//became out of range, must have wrapped\n\t\t\tif (csqcg.cycle_wrapped)\n\t\t\t\t*csqcg.cycle_wrapped = true;\n\t\t\tframe = first;\n\t\t}\n\t}\n\te->v->frame = frame;\n}\nstatic void ASMCALL CSQC_CWStateOp (pubprogfuncs_t *prinst, float first, float last, func_t currentfunc)\n{\n\tfloat min, max;\n\tfloat step;\n\tworld_t *w = prinst->parms->user;\n\twedict_t *e = PROG_TO_WEDICT(prinst, *w->g.self);\n\tfloat frame = e->v->weaponframe;\n\n//\tif (progstype == PROG_H2)\n//\t\te->v->nextthink = *w->g.time+0.05;\n//\telse\n\t\te->v->nextthink = *w->g.time+0.1;\n\te->v->think = currentfunc;\n\n\tif (csqcg.cycle_wrapped)\n\t\t*csqcg.cycle_wrapped = false;\n\n\tif (first > last)\n\t{\t//going backwards\n\t\tmin = last;\n\t\tmax = first;\n\t\tstep = -1.0;\n\t}\n\telse\n\t{\t//forwards\n\t\tmin = first;\n\t\tmax = last;\n\t\tstep = 1.0;\n\t}\n\tif (frame < min || frame > max)\n\t\tframe = first;\t//started out of range, must have been a different animation\n\telse\n\t{\n\t\tframe += step;\n\t\tif (frame < min || frame > max)\n\t\t{\t//became out of range, must have wrapped\n\t\t\tif (csqcg.cycle_wrapped)\n\t\t\t\t*csqcg.cycle_wrapped = true;\n\t\t\tframe = first;\n\t\t}\n\t}\n\te->v->weaponframe = frame;\n}\nvoid ASMCALL CSQC_ThinkTimeOp(pubprogfuncs_t *progs, edict_t *ed, float var)\n{\n\tworld_t *w = progs->parms->user;\n\tstdentvars_t *vars = ed->v;\n\tvars->nextthink = *w->g.time+var;\n}\n\npbool PDECL CSQC_CheckHeaderCrc(pubprogfuncs_t *progs, progsnum_t num, int crc, const char *filename)\n{\n\tif (!num)\n\t{\n\t\tswitch(crc)\n\t\t{\n\t\tcase PROGHEADER_CRC_CSQC:\n\t\t\tbreak;\t//fte's full csqc stuff\n\t\tcase PROGHEADER_CRC_QW:\n\t\tcase PROGHEADER_CRC_NQ:\n\t\tcase PROGHEADER_CRC_PREREL:\n\t\tcase PROGHEADER_CRC_TENEBRAE:\n\t\tcase PROGHEADER_CRC_H2:\n\t\tcase PROGHEADER_CRC_H2MP:\n\t\tcase PROGHEADER_CRC_H2DEMO:\n\t\t\tbreak;\t//simple csqc. but only if it has the right entry points.\n#ifndef csqc_isdarkplaces\n\t\tcase PROGHEADER_CRC_CSQC_DP:\n\t\t\tcsqc_isdarkplaces = true;\n\t\t\tCon_DPrintf(CON_WARNING \"Running darkplaces csprogs.dat version\\n\");\n\t\t\tbreak;\n\t\tcase 23147:\n\t\t\tcsqc_isdarkplaces = true;\n\t\t\tCon_DPrintf(CON_WARNING \"Running ^aINVALID^a csprogs.dat version\\n\");\n\t\t\tbreak;\n#endif\n\t\tdefault:\n\t\t\tCon_Printf(CON_WARNING \"Running unknown csprogs.dat version\\n\");\n\t\t\tbreak;\n\t\t}\n\t}\n\treturn true;\n}\n\ndouble  csqctime;\nqboolean CSQC_Init (qboolean anycsqc, const char *csprogsname, unsigned int checksum, size_t progssize)\n{\n\tint i;\n\tstring_t *str;\n\tcsqcedict_t *worldent;\n\tchar *cheats;\n\tqboolean csdatenabled = true;\n\tif (!csprogsname)\n\t{\n\t\tcsdatenabled = false;\n\t\tcsprogsname = \"csprogs.dat\";\n\t}\n\tif (!*csprogsname)\n\t\tcsprogsname = \"csprogs.dat\";\n\tif (csprogs_promiscuous != anycsqc || csprogs_checksum != checksum || csprogs_checksize != progssize || strcmp(csprogs_checkname,csprogsname))\n\t\tCSQC_Shutdown();\n\tcsprogs_promiscuous = anycsqc;\n\tcsprogs_checksum = checksum;\n\tcsprogs_checksize = progssize;\n\tQ_strncpyz(csprogs_checkname, csprogsname, sizeof(csprogs_checkname));\n\n\tcsqc_mayread = false;\n\n\tcsqc_singlecheats = cls.demoplayback;\n#ifdef HAVE_SERVER\n\tif (sv.state == ss_active)\n\t{\n\t\tcheats = InfoBuf_ValueForKey(&svs.info, \"*cheats\");\n\t\tif (!*cheats && sv.allocated_client_slots == 1)\n\t\t\tcheats = \"ON\";\n\t}\n\telse\n#endif\n\t\tcheats = InfoBuf_ValueForKey(&cl.serverinfo, \"*cheats\");\n\tif (!Q_strcasecmp(cheats, \"ON\") || atoi(cheats))\n\t\tcsqc_singlecheats = true;\n\n\t//its already running...\n\tif (csqcprogs)\n\t\treturn false;\n\n\tif (qrenderer == QR_NONE)\n\t{\n\t\treturn false;\n\t}\n\n\tif (cl_nocsqc.value)\n\t{\n\t\tif (checksum || progssize)\n\t\t\tCon_Printf(CON_WARNING\"Server is using csqc, but its disabled via %s\\n\", cl_nocsqc.name);\n\t\treturn false;\n\t}\n\n\tif (cls.state == ca_disconnected)\n\t{\n\t\tmovevars.gravity = 800;\n\t\tmovevars.entgravity = 1;\n\t\tmovevars.maxspeed = 320;\n\t\tmovevars.bunnyspeedcap = 0;//pm_bunnyspeedcap.value;\n\t\tmovevars.ktjump = false;//pm_ktjump.value;\n\t\tmovevars.slidefix = true;//(pm_slidefix.value != 0);\n\t\tmovevars.airstep = true;//(pm_airstep.value != 0);\n\t\tmovevars.pground = true;\n\t\tmovevars.stepdown = true;\n\t\tmovevars.walljump = false;//(pm_walljump.value);\n\t\tmovevars.slidyslopes = false;//(pm_slidyslopes.value!=0);\n\t\tmovevars.bunnyfriction = false;\n\t\tmovevars.autobunny = false;\t//pm_autobunny.value!=0\n\t\tmovevars.watersinkspeed = 60;//*pm_watersinkspeed.string?pm_watersinkspeed.value:60;\n\t\tmovevars.flyfriction = 4;//*pm_flyfriction.string?pm_flyfriction.value:4;\n\t\tmovevars.edgefriction = 2;//*pm_edgefriction.string?pm_edgefriction.value:2;\n\t\tmovevars.stepheight = PM_DEFAULTSTEPHEIGHT;\n\t\tmovevars.coordtype = COORDTYPE_FLOAT_32;\n\t\tmovevars.flags = MOVEFLAG_NOGRAVITYONGROUND;\n\t}\n\n\tfor (i = 0; i < sizeof(csqc_builtin)/sizeof(csqc_builtin[0]); i++)\n\t\tcsqc_builtin[i] = PF_Fixme;\n\tfor (i = 0; BuiltinList[i].bifunc; i++)\n\t{\n\t\tif (BuiltinList[i].ebfsnum)\n\t\t\tcsqc_builtin[BuiltinList[i].ebfsnum] = BuiltinList[i].bifunc;\n\t}\n\n\tcsqc_deprecated_warned = !!cl_csqc_nodeprecate.ival;\n\tmemset(cl.model_csqcname, 0, sizeof(cl.model_csqcname));\n\tmemset(cl.model_csqcprecache, 0, sizeof(cl.model_csqcprecache));\n\n\tcsqcprogparms.progsversion = PROGSTRUCT_VERSION;\n\tcsqcprogparms.ReadFile = CSQC_PRLoadFile;\n\tcsqcprogparms.FileSize = CSQC_PRFileSize;//int (*FileSize) (char *fname);\t//-1 if file does not exist\n\tcsqcprogparms.WriteFile = QC_WriteFile;//bool (*WriteFile) (char *name, void *data, int len);\n\tcsqcprogparms.Printf = PR_Printf;//Con_Printf;//void (*printf) (char *, ...);\n\tcsqcprogparms.DPrintf = PR_DPrintf;//Con_Printf;//void (*printf) (char *, ...);\n\tcsqcprogparms.Sys_Error = Sys_Error;\n\tcsqcprogparms.Abort = CSQC_Abort;\n\tcsqcprogparms.CheckHeaderCrc = CSQC_CheckHeaderCrc;\n\tcsqcprogparms.edictsize = sizeof(csqcedict_t);\n\n\tcsqcprogparms.entspawn = CSQC_EntSpawn;//void (*entspawn) (struct edict_s *ent);\t//ent has been spawned, but may not have all the extra variables (that may need to be set) set\n\tcsqcprogparms.entcanfree = CSQC_EntFree;//bool (*entcanfree) (struct edict_s *ent);\t//return true to stop ent from being freed\n\tcsqcprogparms.stateop = CSQC_StateOp;//StateOp;//void (*stateop) (float var, func_t func);\n\tcsqcprogparms.cstateop = CSQC_CStateOp;//CStateOp;\n\tcsqcprogparms.cwstateop = CSQC_CWStateOp;//CWStateOp;\n\tcsqcprogparms.thinktimeop = CSQC_ThinkTimeOp;//ThinkTimeOp;\n\n\tcsqcprogparms.MapNamedBuiltin = PR_CSQC_MapNamedBuiltin;\n\tcsqcprogparms.loadcompleate = NULL;//void (*loadcompleate) (int edictsize);\t//notification to reset any pointers.\n\n\tcsqcprogparms.memalloc = PR_CB_Malloc;//void *(*memalloc) (int size);\t//small string allocation\tmalloced and freed randomly\n\tcsqcprogparms.memfree = PR_CB_Free;//void (*memfree) (void * mem);\n\n\n\tcsqcprogparms.globalbuiltins = csqc_builtin;//builtin_t *globalbuiltins;\t//these are available to all progs\n\tcsqcprogparms.numglobalbuiltins = sizeof(csqc_builtin)/sizeof(csqc_builtin[0]);\n\n\tcsqcprogparms.autocompile = PR_COMPILEIGNORE;//enum {PR_NOCOMPILE, PR_COMPILENEXIST, PR_COMPILECHANGED, PR_COMPILEALWAYS} autocompile;\n\n\tcsqcprogparms.gametime = &csqctime;\n#ifdef MULTITHREAD\n\tcsqcprogparms.usethreadedgc = pr_gc_threaded.ival;\n#endif\n\n\tcsqcprogparms.edicts = (struct edict_s **)&csqc_world.edicts;\n\tcsqcprogparms.num_edicts = &csqc_world.num_edicts;\n\n\tcsqcprogparms.useeditor = QCEditor;//void (*useeditor) (char *filename, int line, int nump, char **parms);\n\tcsqcprogparms.user = &csqc_world;\n\tcsqc_world.keydestmask = kdm_game;\n\n\tcsqctime = Sys_DoubleTime();\n\tif (!csqcprogs)\n\t{\n\t\tint csprogsnum = -1;\n\t\tint csaddonnum = -1;\n\t\tin_sensitivityscale = 1;\n\t\tcsqcmapentitydataloaded = true;\n\t\tcl.mapstarttime = realtime;\n\t\tcsqcprogs = InitProgs(&csqcprogparms);\n\t\tcsqc_world.progs = csqcprogs;\n\t\tcsqc_world.usesolidcorpse = true;\n\t\tPR_Configure(csqcprogs, PR_ReadBytesString(pr_csqc_memsize.string), MAX_PROGS, pr_enable_profiling.ival);\n\t\tcsqc_world.worldmodel = cl.worldmodel;\n\t\tcsqc_world.Event_Touch = CSQC_Event_Touch;\n\t\tcsqc_world.Event_Think = CSQC_Event_Think;\n\t\tcsqc_world.Event_Sound = CSQC_Event_Sound;\n\t\tcsqc_world.Event_ContentsTransition = CSQC_Event_ContentsTransition;\n\t\tcsqc_world.Get_CModel = CSQC_World_ModelForIndex;\n\t\tcsqc_world.Get_FrameState = CSQC_World_GetFrameState;\n\t\tWorld_ClearWorld(&csqc_world, false);\n\t\tCSQC_InitFields();\t//let the qclib know the field order that the engine needs.\n\n\t\tif (setjmp(csqc_abort))\n\t\t{\n\t\t\tCSQC_Shutdown();\n\t\t\treturn false;\n\t\t}\n\n#ifndef csqc_isdarkplaces\n\t\tcsqc_isdarkplaces = false;\n#endif\n\t\tif (csdatenabled || csqc_singlecheats || anycsqc)\n\t\t\tcsqc_nogameaccess = false;\n\t\telse\n\t\t\tcsqc_nogameaccess = true;\n\n\t\tif (!csqc_nogameaccess)\n\t\t{\t//only load csprogs if its expected to be able to work without failing for game access reasons\n\t\t\tcsprogsnum = PR_LoadProgs(csqcprogs, csprogs_checkname);\n\t\t\tif (csprogsnum >= 0)\n\t\t\t\tCon_DPrintf(\"Loaded csprogs.dat\\n\");\n\t\t\telse if (csprogs_checksum || csprogs_checksize)\n\t\t\t\tCon_Printf(CON_WARNING\"Unable to load \\\"csprogsvers/%x.dat\\\"\\n\", csprogs_checksum);\n\t\t}\n\t\t\n\t\tif (csprogsnum >= 0 && !Q_strcasecmp(csprogs_checkname, \"csaddon.dat\"))\n\t\t\t;\t//using csaddon directly... map editor mode?\n\t\telse if (csqc_singlecheats || anycsqc)\n\t\t{\n\t\t\tcsaddonnum = PR_LoadProgs(csqcprogs, \"csaddon.dat\");\n\t\t\tif (csaddonnum >= 0)\n\t\t\t\tCon_DPrintf(\"loaded csaddon.dat...\\n\");\n\t\t\telse\n\t\t\t\tCon_DPrintf(\"unable to find csaddon.dat.\\n\");\n\t\t}\n\t\telse\n\t\t\tCon_DPrintf(\"skipping csaddon.dat due to cheat restrictions\\n\");\n\n\t\tif (csprogsnum < 0 && csaddonnum < 0)\t\t//simple csqc optionally uses the nq progs, but its explicitly limited\n\t\t{\n\t\t\tcsqc_nogameaccess = true;\n\t\t\tcsprogsnum = PR_LoadProgs(csqcprogs, \"progs.dat\");\n\t\t}\n\n\t\tif (csprogsnum == -1 && csaddonnum == -1)\n\t\t{\n\t\t\tCSQC_Shutdown();\n\t\t\treturn false;\n\t\t}\n\n\t\tif (csqc_nogameaccess && !PR_FindFunction (csqcprogs, \"CSQC_DrawHud\", PR_ANY) && !PR_FindFunction (csqcprogs, \"CSQC_DrawScores\", PR_ANY))\n\t\t{\t//simple csqc module is not csqc. abort now.\n\t\t\tCSQC_Shutdown();\n\t\t\tCon_DPrintf(\"progs.dat is not suitable for SimpleCSQC - no CSQC_DrawHud\\n\");\n\t\t\treturn false;\n\t\t}\n\t\telse if (csqc_nogameaccess)\n\t\t\tCon_DPrintf(\"Loaded [csqc]progs.dat\\n\");\n\n\t\tPR_ProgsAdded(csqcprogs, csprogsnum, \"csprogs.dat\");\n\t\tPR_ProgsAdded(csqcprogs, csaddonnum, \"csaddon.dat\");\n\n\t\tPF_InitTempStrings(csqcprogs);\n\n\t\tcsqc_world.physicstime = 0;\n\t\tif (!csqc_world.worldmodel)\n\t\t\tcsqc_world.worldmodel = Mod_ForName(\"\", MLV_SILENT); \n\t\tcsqc_worldchanged = false;\n\n\t\tmemset(csqcent, 0, sizeof(*csqcent)*maxcsqcentities);\t//clear the server->csqc entity translations.\n\n\t\tif (csqc_isdarkplaces)\n\t\t\tCSQC_FindGlobals(true);\n\t\telse\n\t\t\tCSQC_FindGlobals(false);\n\n\t\tfor (i = 0; i < csqcprogs->numprogs; i++)\n\t\t{\n\t\t\tfunc_t f = PR_FindFunction (csqcprogs, \"init\", i);\n\t\t\tif (f)\n\t\t\t{\n\t\t\t\tvoid *pr_globals = PR_globals(csqcprogs, PR_CURRENT);\n\t\t\t\tG_PROG(OFS_PARM0) = i-1;\n\t\t\t\tPR_ExecuteProgram(csqcprogs, f);\n\t\t\t}\n\t\t}\n\n\t\tcsqcentsize = PR_InitEnts(csqcprogs, pr_csqc_maxedicts.value);\n\n\t\tif (csqcg.CSQC_GenerateMaterial)\n\t\t\tMaterial_RegisterLoader(&csqc_world, &csqcmaterialloader);\n\n\t\t//world edict becomes readonly\n\t\tworldent = (csqcedict_t *)EDICT_NUM_PB(csqcprogs, 0);\n\t\tworldent->ereftype = ER_ENTITY;\n\n\t\tfor (i = 0; i < csqcprogs->numprogs; i++)\n\t\t{\n\t\t\tfunc_t f = PR_FindFunction (csqcprogs, \"initents\", i);\n\t\t\tif (f)\n\t\t\t{\n\t\t\t\tvoid *pr_globals = PR_globals(csqcprogs, PR_CURRENT);\n\t\t\t\tG_PROG(OFS_PARM0) = i-1;\n\t\t\t\tPR_ExecuteProgram(csqcprogs, f);\n\t\t\t}\n\t\t}\n\n\t\t/*DP compat*/\n\t\tstr = (string_t*)csqcprogs->GetEdictFieldValue(csqcprogs, (edict_t*)worldent, \"message\", ev_string, NULL);\n\t\tif (str)\n\t\t\t*str = PR_NewString(csqcprogs, cl.levelname);\n\n\t\tstr = (string_t*)PR_FindGlobal(csqcprogs, \"mapname\", 0, NULL);\n\t\tif (str)\n\t\t{\n\t\t\tchar *s = InfoBuf_ValueForKey(&cl.serverinfo, \"map\");\n\t\t\tif (!*s)\n\t\t\t\ts = cl.model_name[1];\n\t\t\tif (!s || !*s)\n\t\t\t\ts = \"unknown\";\n\t\t\t*str = PR_NewString(csqcprogs, s);\n\t\t}\n\n\t\tif (csqcg.deathmatch)\n\t\t\t*csqcg.deathmatch = cl.deathmatch;\n\t\tif (csqcg.coop)\n\t\t\t*csqcg.coop = !cl.deathmatch && cl.allocated_client_slots > 1;\n\n\t\tif (csqcg.CSQC_Init)\n\t\t{\n\t\t\tvoid *pr_globals = PR_globals(csqcprogs, PR_CURRENT);\n\t\t\tG_FLOAT(OFS_PARM0) = CSQC_API_VERSION;\t//api version\n\t\t\t(((string_t *)pr_globals)[OFS_PARM1] = PR_TempString(csqcprogs, FULLENGINENAME));\n\t\t\tG_FLOAT(OFS_PARM2) = version_number();\n\t\t\tPR_ExecuteProgram(csqcprogs, csqcg.CSQC_Init);\n\t\t}\n/*\n\t\t{\n\t\t\tchar *watchname = \"something\";\n\t\t\tvoid *dbg = PR_FindGlobal(csqcprogs, watchname, 0, NULL);\n\t\t\tif (!csqcprogs->SetWatchPoint(csqcprogs, watchname))\n\t\t\t\tCon_Printf(\"Unable to watch %s\\n\", watchname);\n\t\t}\n*/\n//\t\tcsqcprogs->ToggleBreak(csqcprogs, \"something\", 0, 2);\n\n\t\tCon_DPrintf(\"Loaded csqc\\n\");\n\t\tcsqcmapentitydataloaded = false;\n\n\t\tcsqc_world.physicstime = 0.1;\n\n\t\tCSQC_RendererRestarted(true);\n\n\t\tif (cls.state == ca_disconnected)\n\t\t\tCSQC_WorldLoaded();\n\t}\n\n\treturn true; //success!\n}\n\nvoid CSQC_RendererRestarted(qboolean initing)\n{\n\tint i;\n\tif (!csqcprogs)\n\t\treturn;\n\n\tif (initing)\n\t{\n\t\t//called at startup\n\t\tif (csqc_worldchanged)\n\t\t{\n\t\t\tcsqc_worldchanged = false;\n\t\t\tcl.worldmodel = r_worldentity.model = csqc_world.worldmodel;\n\t\t\tif (cl.worldmodel)\n\t\t\t\tFS_LoadMapPackFile(cl.worldmodel->name, cl.worldmodel->archive);\n\t\t\tSurf_NewMap(csqc_world.worldmodel);\n\t\t\tCL_UpdateWindowTitle();\n\n\t\t\tWorld_RBE_Shutdown(&csqc_world);\n\t\t\tWorld_RBE_Start(&csqc_world);\n\t\t}\n\t}\n\telse\n\t{\t//FIXME: this might be awkward in the purecsqc case.\n\t\tcsqc_world.worldmodel = cl.worldmodel;\n\n\t\tfor (i = 0; i < MAX_CSMODELS; i++)\n\t\t{\n\t\t\tcl.model_csqcprecache[i] = NULL;\n\t\t}\n\t}\n\n\t//FIXME: registered shaders\n\n\t//let the csqc know that its rendertargets got purged\n\tif (csqcg.CSQC_RendererRestarted)\n\t{\n\t\tvoid *pr_globals = PR_globals(csqcprogs, PR_CURRENT);\n\t\t(((string_t *)pr_globals)[OFS_PARM0] = PR_TempString(csqcprogs, rf->description));\n\t\tPR_ExecuteProgram(csqcprogs, csqcg.CSQC_RendererRestarted);\n\t}\n\t//in case it drew to any render targets.\n\tif (R2D_Flush)\n\t\tR2D_Flush();\n\tif (*r_refdef.rt_destcolour[0].texname)\n\t{\n\t\tQ_strncpyz(r_refdef.rt_destcolour[0].texname, \"\", sizeof(r_refdef.rt_destcolour[0].texname));\n\t\tBE_RenderToTextureUpdate2d(true);\n\t}\n}\n\nvoid CSQC_WorldLoaded(void)\n{\n\tcsqcedict_t *worldent;\n\tint tmp;\n\tint wmodelindex;\n\n\tif (!csqcprogs)\n\t\treturn;\n\tif (csqcmapentitydataloaded)\n\t\treturn;\n\n\tif (csqc_isdarkplaces)\n\t\tCSQC_FindGlobals(false);\n\n\tcsqc_world.worldmodel = cl.worldmodel;\n\n\tcsqcmapentitydataloaded = true;\n\tcsqcmapentitydata = Mod_GetEntitiesString(csqc_world.worldmodel);\n\n\tWorld_RBE_Start(&csqc_world);\n\n\tworldent = (csqcedict_t *)EDICT_NUM_PB(csqcprogs, 0);\n\tworldent->v->solid = SOLID_BSP;\n\twmodelindex = CS_FindModel(csqc_world.worldmodel?csqc_world.worldmodel->name:\"\", &tmp);\n\ttmp = csqc_worldchanged;\n\tcsqc_setmodel(csqcprogs, worldent, wmodelindex);\n\tcsqc_worldchanged = tmp;\n\n\tworldent->readonly = false;\t//just in case\n\n\tWorld_ClearWorld(&csqc_world, true);\n\n\tif (csqc_isdarkplaces)\n\t{\n\t\tif (csqcg.CSQC_Init)\n\t\t{\n\t\t\tvoid *pr_globals = PR_globals(csqcprogs, PR_CURRENT);\n\t\t\tG_FLOAT(OFS_PARM0) = CSQC_API_VERSION;\t//api version\n\t\t\t(((string_t *)pr_globals)[OFS_PARM1] = PR_TempString(csqcprogs, FULLENGINENAME));\n\t\t\tG_FLOAT(OFS_PARM2) = version_number();\n\t\t\tPR_ExecuteProgram(csqcprogs, csqcg.CSQC_Init);\n\t\t}\n\t}\n\n\tif (csqcg.CSQC_WorldLoaded)\n\t\tPR_ExecuteProgram(csqcprogs, csqcg.CSQC_WorldLoaded);\n\tcsqcmapentitydata = NULL;\n\n\tworldent->readonly = true;\n}\n\nvoid CSQC_CoreDump(void)\n{\n\tif (!csqcprogs)\n\t{\n\t\tCon_Printf(\"Can't core dump, you need to be running the CSQC progs first.\");\n\t\treturn;\n\t}\n\n\t{\n\t\tsize_t size = 1024*1024*8;\n\t\tchar *buffer = BZ_Malloc(size);\n\t\tcsqcprogs->save_ents(csqcprogs, buffer, &size, size, 3);\n\t\tCOM_WriteFile(\"csqccore.txt\", FS_GAMEONLY, buffer, size);\n\t\tBZ_Free(buffer);\n\t}\n\n}\n\nvoid PR_CSExtensionList_f(void)\n{\n\tint i;\n\tint ebi;\n\tint bi;\n\tqc_extension_t *extlist;\n\tqboolean inactive;\n\n\tchar biissues[8192];\n\tconst builtin_t *pr_builtin = csqc_builtin;\n\tint num;\n\tqboolean wrongmodule;\n\tint j;\n\tconst char *extname;\n\n\textcheck_t extcheck = {&csqc_world, cls.fteprotocolextensions, cls.fteprotocolextensions2};\n\n\n#define SHOW_ACTIVEEXT 1\n#define SHOW_ACTIVEBI 2\n#define SHOW_NOTSUPPORTEDEXT 4\n#define SHOW_NOTACTIVEEXT 8\n#define SHOW_NOTACTIVEBI 16\n\n\tint showflags = atoi(Cmd_Argv(1));\n\tif (!showflags)\n\t\tshowflags = SHOW_ACTIVEEXT|SHOW_NOTACTIVEEXT;\n\n\t//make sure the info is valid\n\tif (!csqc_builtin[0])\n\t{\n\t\tfor (i = 0; i < sizeof(csqc_builtin)/sizeof(csqc_builtin[0]); i++)\n\t\t\tcsqc_builtin[i] = PF_Fixme;\n\t\tfor (i = 0; BuiltinList[i].bifunc; i++)\n\t\t{\n\t\t\tif (BuiltinList[i].ebfsnum)\n\t\t\t\tcsqc_builtin[BuiltinList[i].ebfsnum] = BuiltinList[i].bifunc;\n\t\t}\n\t}\n\n\n\tif (showflags & (SHOW_ACTIVEBI|SHOW_NOTACTIVEBI))\n\tfor (i = 0; BuiltinList[i].name; i++)\n\t{\n\t\tif (!BuiltinList[i].ebfsnum)\n\t\t\tcontinue;\t//a reserved builtin.\n\t\tif (BuiltinList[i].bifunc == PF_Fixme)\n\t\t\tCon_Printf(\"^1%s:%i needs to be added\\n\", BuiltinList[i].name, BuiltinList[i].ebfsnum);\n\t\telse if (csqc_builtin[BuiltinList[i].ebfsnum] == BuiltinList[i].bifunc)\n\t\t{\n\t\t\tif (showflags & SHOW_ACTIVEBI)\n\t\t\t\tCon_Printf(\"%s is active on %i\\n\", BuiltinList[i].name, BuiltinList[i].ebfsnum);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (showflags & SHOW_NOTACTIVEBI)\n\t\t\t\tCon_Printf(\"^4%s is NOT active (%i)\\n\", BuiltinList[i].name, BuiltinList[i].ebfsnum);\n\t\t}\n\t}\n\n\tif (showflags & (SHOW_NOTSUPPORTEDEXT|SHOW_NOTACTIVEEXT|SHOW_ACTIVEEXT))\n\t{\n\t\textlist = QSG_Extensions;\n\n\t\tfor (i = 0; i < QSG_Extensions_count; i++)\n\t\t{\n\t\t\t*biissues = 0;\n\t\t\tinactive = false;\n\t\t\tif (!extlist[i].name)\n\t\t\t\tcontinue;\n\n\t\t\tfor (ebi = 0; ebi < countof(extlist[i].builtinnames) && extlist[i].builtinnames[ebi]; ebi++)\n\t\t\t{\n\t\t\t\twrongmodule = false;\n\n\t\t\t\tfor (bi = 0; BuiltinList[bi].name; bi++)\n\t\t\t\t{\n\t\t\t\t\tif (!strcmp(BuiltinList[bi].name, extlist[i].builtinnames[ebi]))\n\t\t\t\t\t{\n\t\t\t\t\t\tif (BuiltinList[bi].bifunc == PF_Fixme)\n\t\t\t\t\t\t\twrongmodule=true;\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (!BuiltinList[bi].name)\n\t\t\t\t{\n\t\t\t\t\tif (wrongmodule)\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\tQ_strncatz2(biissues, va(CON_ERROR\"\\t^4#:%s is not known\\n\", extlist[i].builtinnames[ebi]));\n\t\t\t\t\tinactive = true;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\n\t\t\t\tnum = BuiltinList[bi].ebfsnum;\n\t\t\t\tif (!num)\n\t\t\t\t\t;//builtins with no number are handled at load time. there are no number conflicts so no way for the builtin to be inactive. if there is an error then its not because of the extension itself.\n\t\t\t\telse if (pr_builtin[num] != BuiltinList[bi].bifunc)\n\t\t\t\t{\n\t\t\t\t\tif (pr_builtin[num] == PF_Fixme)\n\t\t\t\t\t\tQ_strncatz2(biissues, va(\"\\t^4#%i:%s is not implemented\\n\", num, BuiltinList[bi].name));\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tfor (j = 0; BuiltinList[j].name; j++)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (BuiltinList[j].bifunc == pr_builtin[num])\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tQ_strncatz2(biissues, va(\"\\t#%i:%s is currently %s\\n\", num,BuiltinList[bi].name, BuiltinList[j].name));\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (!BuiltinList[j].name)\n\t\t\t\t\t\t\tQ_strncatz2(biissues, va(\"\\t^4#%i:%s is currently unknown\\n\", num, BuiltinList[bi].name));\n\t\t\t\t\t}\n\t\t\t\t\tinactive = true;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (extlist[i].description)\n\t\t\t\textname = va(\"^[%s\\\\tip\\\\%s^]\", extlist[i].name, extlist[i].description);\n\t\t\telse\n\t\t\t\textname = extlist[i].name;\n\n\t\t\tif (extlist[i].extensioncheck && !extlist[i].extensioncheck(&extcheck))\n\t\t\t{\n\t\t\t\tif (showflags & SHOW_NOTSUPPORTEDEXT)\n\t\t\t\t\tCon_Printf(CON_WARNING\"protocol %s is blocked\\n%s\", extname, biissues);\n\t\t\t}\n\t\t\telse if (inactive)\n\t\t\t{\n\t\t\t\tif (showflags & SHOW_NOTSUPPORTEDEXT)\n\t\t\t\t\tCon_Printf(CON_ERROR\"%s is inactive\\n%s\", extname, biissues);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (showflags & SHOW_ACTIVEEXT)\n\t\t\t\t{\n\t\t\t\t\tif (!extlist[i].builtinnames[0])\n\t\t\t\t\t\tCon_Printf(\"%s is supported\\n%s\", extname, biissues);\n\t\t\t\t\telse\n\t\t\t\t\t\tCon_Printf(\"%s is currently active\\n%s\", extname, biissues);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nvoid CSQC_Breakpoint_f(void)\n{\n\tint wasset;\n\tint isset;\n\tchar *filename = Cmd_Argv(1);\n\tint line = atoi(Cmd_Argv(2));\n\n\tif (!csqcprogs)\n\t{\n\t\tCon_Printf(\"CSQC not running\\n\");\n\t\treturn;\n\t}\n\twasset = csqcprogs->ToggleBreak(csqcprogs, filename, line, 3);\n\tisset = csqcprogs->ToggleBreak(csqcprogs, filename, line, 2);\n\n\tif (wasset == isset)\n\t\tCon_Printf(\"Breakpoint was not valid\\n\");\n\telse if (isset)\n\t\tCon_Printf(\"Breakpoint has been set\\n\");\n\telse\n\t\tCon_Printf(\"Breakpoint has been cleared\\n\");\n\n//\tCvar_Set(Cvar_FindVar(\"pr_debugger\"), \"1\");\n}\n\nstatic void CSQC_Poke_f(void)\n{\n\tif (!csqc_singlecheats && cls.state)\n\t\tCon_Printf(\"%s is a cheat command\\n\", Cmd_Argv(0));\n\telse if (csqcprogs)\n\t\tCon_Printf(\"Result: %s\\n\", csqcprogs->EvaluateDebugString(csqcprogs, Cmd_Args()));\n\telse\n\t\tCon_Printf(\"csqc not running\\n\");\n}\nvoid CSQC_WatchPoint_f(void)\n{\n\tchar *variable = Cmd_Argv(1);\n\tif (!*variable)\n\t\tvariable = NULL;\n\n\tif (!csqc_singlecheats)\n\t\tCon_Printf(\"%s is a cheat command\\n\", Cmd_Argv(0));\n\telse if (!csqcprogs)\n\t{\n\t\tCon_Printf(\"csqc not running\\n\");\n\t\treturn;\n\t}\n\tif (csqcprogs->SetWatchPoint(csqcprogs, variable, variable))\n\t\tCon_Printf(\"Watchpoint set\\n\");\n\telse\n\t\tCon_Printf(\"Watchpoint cleared\\n\");\n}\nvoid PR_CSProfile_f(void)\n{\n\tif (csqcprogs && csqcprogs->DumpProfile)\n\t\tif (!csqcprogs->DumpProfile(csqcprogs, !atof(Cmd_Argv(1))))\n\t\t\tCon_Printf(\"Enabled csqc Profiling.\\n\");\n}\n\nstatic void CSQC_GameCommand_f(void);\nvoid CSQC_RegisterCvarsAndThings(void)\n{\n\tCmd_AddCommand(\"coredump_csqc\", CSQC_CoreDump);\n\tCmd_AddCommand (\"extensionlist_csqc\", PR_CSExtensionList_f);\n\tCmd_AddCommandD(\"cl_cmd\", CSQC_GameCommand_f, \"Calls the csqc's GameCommand function\");\n\tCmd_AddCommand(\"breakpoint_csqc\", CSQC_Breakpoint_f);\n\tCmd_AddCommand (\"watchpoint_csqc\", CSQC_WatchPoint_f);\n\tCmd_AddCommandD(\"poke_csqc\", CSQC_Poke_f, \"Allows you to inspect/debug \");\n\tCmd_AddCommand (\"profile_csqc\", PR_CSProfile_f);\n\n\tCvar_Register(&pr_csqc_formenus, CSQCPROGSGROUP);\n\tCvar_Register(&pr_csqc_memsize, CSQCPROGSGROUP);\n\tCvar_Register(&pr_csqc_maxedicts, CSQCPROGSGROUP);\n\tCvar_Register(&cl_csqcdebug, CSQCPROGSGROUP);\n\tCvar_Register(&cl_nocsqc, CSQCPROGSGROUP);\n\tCvar_Register(&pr_csqc_coreonerror, CSQCPROGSGROUP);\n\tCvar_Register(&cl_csqc_nodeprecate, CSQCPROGSGROUP);\n\tCvar_Register(&dpcompat_csqcinputeventtypes, CSQCPROGSGROUP);\n}\n\nvoid CSQC_CvarChanged(cvar_t *var)\n{\n\tif (csqcprogs)\n\t{\n\t\tPR_AutoCvar(csqcprogs, var);\n\t}\n}\n\nqboolean CSQC_UseGamecodeLoadingScreen(void)\n{\n\treturn csqcprogs && csqcg.CSQC_UpdateViewLoading;\n}\n\n//evil evil function. calling qc from inside the renderer is BAD.\nqboolean CSQC_SetupToRenderPortal(int entkeynum)\n{\n#ifdef TEXTEDITOR\n\tif (editormodal)\n\t\treturn false;\n#endif\n\n\tif (csqcprogs && entkeynum < 0)\n\t{\n\t\tcsqcedict_t *e = (void*)EDICT_NUM_UB(csqcprogs, -entkeynum);\n\t\tif (e->xv->camera_transform)\n\t\t{\n\t\t\tint oself = *csqcg.self;\n\t\t\tvoid *pr_globals = PR_globals(csqcprogs, PR_CURRENT);\n\n\t\t\t*csqcg.self = EDICT_TO_PROG(csqcprogs, e);\n\t\t\tVectorCopy(r_refdef.vieworg, G_VECTOR(OFS_PARM0));\n\t\t\tVectorAngles(vpn, vup, G_VECTOR(OFS_PARM1), true/*FIXME*/);\n\t\t\tVectorCopy(vpn, csqcg.v_forward);\n\t\t\tVectorCopy(vright, csqcg.v_right);\n\t\t\tVectorCopy(vup, csqcg.v_up);\n\t\t\tVectorCopy(r_refdef.vieworg/*r_refdef.pvsorigin*/, csqcg.trace_endpos);\n\n\t\t\tPR_ExecuteProgram (csqcprogs, e->xv->camera_transform);\n\n\t\t\tVectorCopy(csqcg.v_forward, vpn);\n\t\t\tVectorCopy(csqcg.v_right, vright);\n\t\t\tVectorCopy(csqcg.v_up, vup);\n\t\t\tVectorCopy(G_VECTOR(OFS_RETURN), r_refdef.vieworg);\n\t\t\tVectorCopy(csqcg.trace_endpos, r_refdef.pvsorigin);\n\n\t\t\t*csqcg.self = oself;\n\t\t\treturn true;\n\t\t}\n\t}\n\treturn false;\n}\n\nqboolean CSQC_DrawView(void)\n{\n\tint ticlimit = 10;\n\tfloat mintic = 0.01;\n\tfloat maxtic = 0.1;\n\tdouble clframetime = host_frametime;\n\tRSpeedLocals();\n\n\tcsqc_resortfrags = true;\n\tcsqctime = Sys_DoubleTime();\n\n\tif (!csqcg.CSQC_UpdateView || !csqcprogs)\n\t\treturn false;\n\n\tif (cls.state < ca_active && !CSQC_UnconnectedOkay(false) && !CSQC_UseGamecodeLoadingScreen())\n\t\treturn false;\n\n\tr_secondaryview = 0;\n\n\tif (csqcg.frametime)\n\t\t*csqcg.frametime = host_frametime;\n\n\tcsqc_dp_lastwas3d = false;\n\n\tRSpeedRemark();\n\tif (*csqc_world.g.physics_mode == 0)\n\t{\n\t\tcsqc_world.physicstime = cl.servertime;\n\t\t// let the progs know that a new frame has started\n\t\tif (csqcg.StartFrame)\n\t\t{\n\t\t\t*csqc_world.g.self = EDICT_TO_PROG(csqcprogs, csqc_world.edicts);\n\t\t\t*csqc_world.g.other = EDICT_TO_PROG(csqcprogs, csqc_world.edicts);\n\t\t\t*csqc_world.g.time = csqc_world.physicstime;\n\t\t\tPR_ExecuteProgram (csqcprogs, csqcg.StartFrame);\n\t\t}\n\t\tPR_RunThreads(&csqc_world);\n\t\tif (csqcg.EndFrame)\n\t\t{\t//consistency. or something.\n\t\t\t*csqc_world.g.self = EDICT_TO_PROG(csqcprogs, csqc_world.edicts);\n\t\t\t*csqc_world.g.other = EDICT_TO_PROG(csqcprogs, csqc_world.edicts);\n\t\t\t*csqc_world.g.time = csqc_world.physicstime;\n\t\t\tPR_ExecuteProgram (csqcprogs, csqcg.EndFrame);\n\t\t}\n\t}\n\telse if (*csqc_world.g.physics_mode)\n\t{\n#ifdef USERBE\n\t\tif (csqc_world.rbe)\n\t\t\tmaxtic = mintic;\t//physics engines need a fixed tick rate.\n#endif\n\t\twhile(1)\n\t\t{\n\t\t\thost_frametime = cl.servertime - csqc_world.physicstime;\n\t\t\tif (host_frametime < mintic)\n\t\t\t\tbreak;\n\t\t\tif (!--ticlimit)\n\t\t\t{\n\t\t\t\tcsqc_world.physicstime = cl.servertime;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (host_frametime > maxtic)\n\t\t\t\thost_frametime = maxtic;\n\n\t\t\t// let the progs know that a new frame has started\n\t\t\tif (csqcg.StartFrame)\n\t\t\t{\n\t\t\t\t*csqc_world.g.self = EDICT_TO_PROG(csqcprogs, csqc_world.edicts);\n\t\t\t\t*csqc_world.g.other = EDICT_TO_PROG(csqcprogs, csqc_world.edicts);\n\t\t\t\t*csqc_world.g.time = csqc_world.physicstime;\n\t\t\t\tPR_ExecuteProgram (csqcprogs, csqcg.StartFrame);\n\t\t\t}\n\n#ifdef USERBE\n\t\t\tif (csqc_world.rbe)\n\t\t\t{\n#ifdef RAGDOLL\n\t\t\t\trag_doallanimations(&csqc_world);\n#endif\n\t\t\t\tcsqc_world.rbe->RunFrame(&csqc_world, host_frametime, 800);\n\t\t\t}\n#endif\n\n\t\t\tPR_RunThreads(&csqc_world);\n\t\t\tWorld_Physics_Frame(&csqc_world);\n\t\t\tcsqc_world.physicstime += host_frametime;\n\n\t\t\t//all thinks done. for consistency with the ssqc extension.\n\t\t\tif (csqcg.EndFrame)\n\t\t\t{\n\t\t\t\t*csqc_world.g.self = EDICT_TO_PROG(csqcprogs, csqc_world.edicts);\n\t\t\t\t*csqc_world.g.other = EDICT_TO_PROG(csqcprogs, csqc_world.edicts);\n\t\t\t\t*csqc_world.g.time = csqc_world.physicstime;\n\t\t\t\tPR_ExecuteProgram (csqcprogs, csqcg.EndFrame);\n\t\t\t}\n\t\t}\n\t}\n\tRSpeedEnd(RSPEED_CSQCPHYSICS);\n\n\tRSpeedRemark();\n\n\thost_frametime = clframetime;\n\n\tif (csqcg.frametime)\n\t\t*csqcg.frametime = cl.paused?0:bound(0, cl.time - cl.lasttime, 0.1);\n\tif (csqcg.clframetime)\n\t\t*csqcg.clframetime = host_frametime;\n\n\tif (csqcg.numclientseats)\n\t\t*csqcg.numclientseats = cl.splitclients;\n\n\tif (csqcg.intermission)\n\t\t*csqcg.intermission = cl.intermissionmode;\n\tif (csqcg.intermission_time)\n\t\t*csqcg.intermission_time = cl.completed_time;\n\n\t//work out which packet entities are solid\n\tCL_SetSolidEntities ();\n\tCL_TransitionEntities();\n\tif (cl.worldmodel)\n\t\tCL_PredictMove ();\n\n\tif (csqcg.cltime)\n\t\t*csqcg.cltime = realtime-cl.mapstarttime;\n\tif (csqcg.time)\n\t\t*csqcg.time = cl.servertime;\n\tif (csqcg.clientcommandframe)\n\t\t*csqcg.clientcommandframe = cl.movesequence;\n\tif (csqcg.servercommandframe)\n\t\t*csqcg.servercommandframe = cl.ackedmovesequence;\n\tif (csqcg.gamespeed)\n\t\t*csqcg.gamespeed = cl.gamespeed;\n\tif (cl.paused)\n\t{\n\t\tif (csqcg.gamespeed)\n\t\t\t*csqcg.gamespeed = 0;\n\t}\n\tif (cl.currentpackentities && cl.previouspackentities)\n\t{\n\t\tif (csqcg.servertime)\n\t\t\t*csqcg.servertime = cl.currentpackentities->servertime;\n\t\tif (csqcg.serverprevtime)\n\t\t\t*csqcg.serverprevtime = cl.previouspackentities->servertime;\n\t\tif (csqcg.serverdeltatime)\n\t\t\t*csqcg.serverdeltatime = cl.currentpackentities->servertime - cl.previouspackentities->servertime;\n\t}\n\n\t//always revert to a usable default.\n\tCSQC_ChangeLocalPlayer(cl_forceseat.ival?(cl_forceseat.ival - 1) % cl.splitclients:0);\n\tDropPunchAngle (csqc_playerview);\t//FIXME: this seems like the wrong place for this.\n\tif (cl.worldmodel)\n\t\tSurf_LessenStains();\n\n#ifdef HAVE_LEGACY\n\tif (csqcg.autocvar_vid_conwidth)\n\t\t*csqcg.autocvar_vid_conwidth = vid.width;\n\tif (csqcg.autocvar_vid_conheight)\n\t\t*csqcg.autocvar_vid_conheight = vid.height;\n#endif\n\n\t{\n\t\textern qboolean\tscr_drawloading;\n\t\textern int\t\tloading_stage;\n\t\tvoid *pr_globals = PR_globals(csqcprogs, PR_CURRENT);\n\t\tif (csqc_isdarkplaces)\n\t\t{\t//fucked for compatibility.\n\t\t\tG_FLOAT(OFS_PARM0) = vid.pixelwidth;\n\t\t\tG_FLOAT(OFS_PARM1) = vid.pixelheight;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tG_FLOAT(OFS_PARM0) = vid.width;\n\t\t\tG_FLOAT(OFS_PARM1) = vid.height;\n\t\t}\n\t\tG_FLOAT(OFS_PARM2) = !Key_Dest_Has(kdm_menu|kdm_cwindows) && !r_refdef.eyeoffset[0] && !r_refdef.eyeoffset[1];\n\n\t\tif (csqcg.CSQC_UpdateViewLoading && ((cls.state && cls.state < ca_active) || scr_drawloading || loading_stage))\n\t\t\tPR_ExecuteProgram(csqcprogs, csqcg.CSQC_UpdateViewLoading);\n\t\telse\n\t\t\tPR_ExecuteProgram(csqcprogs, csqcg.CSQC_UpdateView);\n\t}\n\n\tif (*r_refdef.rt_destcolour[0].texname)\n\t{\n\t\tif (R2D_Flush)\n\t\t\tR2D_Flush();\n\t\tQ_strncpyz(r_refdef.rt_destcolour[0].texname, \"\", sizeof(r_refdef.rt_destcolour[0].texname));\n\t\tBE_RenderToTextureUpdate2d(true);\n\t}\n\n\tRSpeedEnd(RSPEED_CSQCREDRAW);\n\n\n\treturn true;\n}\n\nqboolean CSQC_DrawHud(playerview_t *pv)\n{\n\tif (csqcg.CSQC_DrawHud && (pv==cl.playerview/* || csqcg.numclientseats*/))\n\t{\n\t\tRSpeedMark();\n\t\tvoid *pr_globals = PR_globals(csqcprogs, PR_CURRENT);\n\n\t\t//set csqc globals\n\t\tCSQC_ChangeLocalPlayer(pv-cl.playerview);\n\t\tif (csqcg.numclientseats)\n\t\t\t*csqcg.numclientseats = cl.splitclients;\n\t\tif (csqcg.time)\n\t\t\t*csqcg.time = cl.time;\n\t\tif (csqcg.frametime)\n\t\t\t*csqcg.frametime = host_frametime;\n\t\tif (csqcg.cltime)\n\t\t\t*csqcg.cltime = realtime-cl.mapstarttime;\n\n\t\tG_FLOAT(OFS_PARM0+0) = r_refdef.grect.width;\n\t\tG_FLOAT(OFS_PARM0+1) = r_refdef.grect.height;\n\t\tG_FLOAT(OFS_PARM0+2) = 0;\n#ifdef QUAKEHUD\n\t\tG_FLOAT(OFS_PARM1) = (pv->sb_showscores?1:0) | (pv->sb_showteamscores?2:0);\n#else\n\t\tG_FLOAT(OFS_PARM1) = false;\t//hmm\n#endif\n\t\tG_FLOAT(OFS_PARM2+0) = 0;//r_refdef.grect.x;\n\t\tG_FLOAT(OFS_PARM2+1) = 0;//r_refdef.grect.y;\n\t\tG_FLOAT(OFS_PARM2+2) = 0;//pv-cl.playerview;\n\t\tPR_ExecuteProgram(csqcprogs, csqcg.CSQC_DrawHud);\n\n\t\tif (*r_refdef.rt_destcolour[0].texname)\n\t\t{\n\t\t\tif (R2D_Flush)\n\t\t\t\tR2D_Flush();\n\t\t\tQ_strncpyz(r_refdef.rt_destcolour[0].texname, \"\", sizeof(r_refdef.rt_destcolour[0].texname));\n\t\t\tBE_RenderToTextureUpdate2d(true);\n\t\t}\n\n\t\tRSpeedEnd(RSPEED_CSQCREDRAW);\n\t\treturn true;\n\t}\n\treturn false;\n}\nqboolean CSQC_DrawScores(playerview_t *pv)\n{\n\tif (csqcg.CSQC_DrawScores && (pv==cl.playerview/* || csqcg.numclientseats*/))\n\t{\n\t\tRSpeedMark();\n\t\tvoid *pr_globals = PR_globals(csqcprogs, PR_CURRENT);\n\n\t\t//set csqc globals (in case CSQC_DrawHud wasn't implemented)\n\t\tCSQC_ChangeLocalPlayer(pv-cl.playerview);\n\t\tif (csqcg.numclientseats)\n\t\t\t*csqcg.numclientseats = cl.splitclients;\n\t\tif (csqcg.time)\n\t\t\t*csqcg.time = cl.time;\n\t\tif (csqcg.frametime)\n\t\t\t*csqcg.frametime = host_frametime;\n\t\tif (csqcg.cltime)\n\t\t\t*csqcg.cltime = realtime-cl.mapstarttime;\n\n\t\tG_FLOAT(OFS_PARM0+0) = r_refdef.grect.width;\n\t\tG_FLOAT(OFS_PARM0+1) = r_refdef.grect.height;\n\t\tG_FLOAT(OFS_PARM0+2) = 0;\n#ifdef QUAKEHUD\n\t\tG_FLOAT(OFS_PARM1) = (pv->sb_showscores?1:0) | (pv->sb_showteamscores?2:0);\n#else\n\t\tG_FLOAT(OFS_PARM1) = false;\t//hmm\n#endif\n\t\tG_FLOAT(OFS_PARM2+0) = 0;//r_refdef.grect.x;\n\t\tG_FLOAT(OFS_PARM2+1) = 0;//r_refdef.grect.y;\n\t\tG_FLOAT(OFS_PARM2+2) = 0;//pv-cl.playerview;\n\t\tPR_ExecuteProgram(csqcprogs, csqcg.CSQC_DrawScores);\n\n\t\tif (*r_refdef.rt_destcolour[0].texname)\n\t\t{\n\t\t\tif (R2D_Flush)\n\t\t\t\tR2D_Flush();\n\t\t\tQ_strncpyz(r_refdef.rt_destcolour[0].texname, \"\", sizeof(r_refdef.rt_destcolour[0].texname));\n\t\t\tBE_RenderToTextureUpdate2d(true);\n\t\t}\n\n\t\tRSpeedEnd(RSPEED_CSQCREDRAW);\n\t\treturn true;\n\t}\n\treturn false;\n}\n\nqboolean CSQC_KeyPress(int key, int unicode, qboolean down, unsigned int devid)\n{\n\tstatic qbyte csqckeysdown[K_MAX];\n\tvoid *pr_globals;\n\tint ie = down?CSIE_KEYDOWN:CSIE_KEYUP;\n\n\tif (!csqcprogs || !csqcg.CSQC_InputEvent || ie >= dpcompat_csqcinputeventtypes.ival)\n\t\treturn false;\n#ifdef TEXTEDITOR\n\tif (editormodal)\n\t\treturn false;\n#endif\n\n\tpr_globals = PR_globals(csqcprogs, PR_CURRENT);\n\tG_FLOAT(OFS_PARM0) = ie;\n\tG_FLOAT(OFS_PARM1) = MP_TranslateFTEtoQCCodes(key);\n\tG_FLOAT(OFS_PARM2) = unicode;\n\tG_FLOAT(OFS_PARM3) = devid;\n\n\t//small sanity check, so things don't break too much if things get big.\n\tif ((unsigned)devid >= sizeof(csqckeysdown[0])*8)\n\t\tdevid = sizeof(csqckeysdown[0])*8-1;\n\tif (key < 0 || key >= K_MAX)\n\t\tkey = 0;\t//panic. everyone panic.\n\n\tif (down)\n\t{\n\t\tqcinput_scan = G_FLOAT(OFS_PARM1);\n\t\tqcinput_unicode = G_FLOAT(OFS_PARM2);\n\n\t\tcsqckeysdown[key] |= (1u<<devid);\n\t}\n\telse\n\t{\n\t\tif (key && !(csqckeysdown[key] & (1u<<devid)))\n\t\t\treturn false;\t//prevent up events being able to leak \n\t\tcsqckeysdown[key] &= ~(1u<<devid);\n\t}\n\tPR_ExecuteProgram (csqcprogs, csqcg.CSQC_InputEvent);\n\tqcinput_scan = 0;\t//and stop replay attacks\n\tqcinput_unicode = 0;\n\n\treturn G_FLOAT(OFS_RETURN);\n}\nqboolean CSQC_MousePosition(float xabs, float yabs, unsigned int devid)\n{\n\tvoid *pr_globals;\n\n\tif (!csqcprogs || !csqcg.CSQC_InputEvent || CSIE_MOUSEABS >= dpcompat_csqcinputeventtypes.ival)\n\t\treturn false;\n\n\tpr_globals = PR_globals(csqcprogs, PR_CURRENT);\n\tG_FLOAT(OFS_PARM0) = CSIE_MOUSEABS;\n\tG_FLOAT(OFS_PARM1) = (xabs * vid.width) / vid.pixelwidth;\n\tG_FLOAT(OFS_PARM2) = (yabs * vid.height) / vid.pixelheight;\n\tG_FLOAT(OFS_PARM3) = devid;\n\n\tPR_ExecuteProgram (csqcprogs, csqcg.CSQC_InputEvent);\n\n\treturn G_FLOAT(OFS_RETURN);\n}\nqboolean CSQC_MouseMove(float xdelta, float ydelta, unsigned int devid)\n{\n\tvoid *pr_globals;\n\n\tif (!csqcprogs || !csqcg.CSQC_InputEvent || CSIE_MOUSEDELTA >= dpcompat_csqcinputeventtypes.ival)\n\t\treturn false;\n\n\tpr_globals = PR_globals(csqcprogs, PR_CURRENT);\n\tG_FLOAT(OFS_PARM0) = CSIE_MOUSEDELTA;\n\tG_FLOAT(OFS_PARM1) = (xdelta * vid.width) / vid.pixelwidth;\n\tG_FLOAT(OFS_PARM2) = (ydelta * vid.height) / vid.pixelheight;\n\tG_FLOAT(OFS_PARM3) = devid;\n\n\tPR_ExecuteProgram (csqcprogs, csqcg.CSQC_InputEvent);\n\n\treturn G_FLOAT(OFS_RETURN);\n}\n\nqboolean CSQC_JoystickAxis(int axis, float value, unsigned int devid)\n{\n\tvoid *pr_globals;\n\tif (!csqcprogs || !csqcg.CSQC_InputEvent || CSIE_JOYAXIS >= dpcompat_csqcinputeventtypes.ival)\n\t\treturn false;\n\tpr_globals = PR_globals(csqcprogs, PR_CURRENT);\n\n\tG_FLOAT(OFS_PARM0) = CSIE_JOYAXIS;\n\tG_FLOAT(OFS_PARM1) = axis;\n\tG_FLOAT(OFS_PARM2) = value;\n\tG_FLOAT(OFS_PARM3) = devid;\n\tPR_ExecuteProgram (csqcprogs, csqcg.CSQC_InputEvent);\n\treturn G_FLOAT(OFS_RETURN);\n}\n\nqboolean CSQC_Accelerometer(float x, float y, float z)\n{\n\tvoid *pr_globals;\n\tif (!csqcprogs || !csqcg.CSQC_InputEvent || CSIE_ACCELEROMETER >= dpcompat_csqcinputeventtypes.ival)\n\t\treturn false;\n\tpr_globals = PR_globals(csqcprogs, PR_CURRENT);\n\n\tG_FLOAT(OFS_PARM0) = CSIE_ACCELEROMETER;\n\tG_FLOAT(OFS_PARM1) = x;\n\tG_FLOAT(OFS_PARM2) = y;\n\tG_FLOAT(OFS_PARM3) = z;\n\tPR_ExecuteProgram (csqcprogs, csqcg.CSQC_InputEvent);\n\treturn G_FLOAT(OFS_RETURN);\n}\nqboolean CSQC_Gyroscope(float x, float y, float z)\n{\n\tvoid *pr_globals;\n\tif (!csqcprogs || !csqcg.CSQC_InputEvent || CSIE_GYROSCOPE >= dpcompat_csqcinputeventtypes.ival)\n\t\treturn false;\n\tpr_globals = PR_globals(csqcprogs, PR_CURRENT);\n\n\tG_FLOAT(OFS_PARM0) = CSIE_GYROSCOPE;\n\tG_FLOAT(OFS_PARM1) = x;\n\tG_FLOAT(OFS_PARM2) = y;\n\tG_FLOAT(OFS_PARM3) = z;\n\tPR_ExecuteProgram (csqcprogs, csqcg.CSQC_InputEvent);\n\treturn G_FLOAT(OFS_RETURN);\n}\n\nqboolean CSQC_ConsoleLink(char *text, char *info)\n{\n\tvoid *pr_globals;\n\tif (!csqcprogs || !csqcg.CSQC_ConsoleLink)\n\t\treturn false;\n\n\tpr_globals = PR_globals(csqcprogs, PR_CURRENT);\n\t(((string_t *)pr_globals)[OFS_PARM1] = PR_TempString(csqcprogs, info));\n\t*info = 0;\n\t(((string_t *)pr_globals)[OFS_PARM0] = PR_TempString(csqcprogs, text));\n\t*info = '\\\\';\n\tPR_ExecuteProgram (csqcprogs, csqcg.CSQC_ConsoleLink);\n\treturn G_FLOAT(OFS_RETURN);\n}\n\nqboolean CSQC_ConsoleCommand(int seat, const char *cmd)\n{\n\tvoid *pr_globals;\n\tif (!csqcprogs || !csqcg.CSQC_ConsoleCommand)\n\t\treturn false;\n#ifdef TEXTEDITOR\n\tif (editormodal)\n\t\treturn false;\n#endif\n\n\tif (seat < 0)\n\t\tseat = CL_TargettedSplit(false);\n\tCSQC_ChangeLocalPlayer(seat);\n\n\tpr_globals = PR_globals(csqcprogs, PR_CURRENT);\n\t(((string_t *)pr_globals)[OFS_PARM0] = PR_TempString(csqcprogs, cmd));\n\n\tPR_ExecuteProgram (csqcprogs, csqcg.CSQC_ConsoleCommand);\n\treturn G_FLOAT(OFS_RETURN);\n}\nstatic void CSQC_GameCommand_f(void)\n{\n\tvoid *pr_globals;\n\tif (!csqcprogs || !csqcg.GameCommand)\n\t\treturn;\n\n\tpr_globals = PR_globals(csqcprogs, PR_CURRENT);\n\t(((string_t *)pr_globals)[OFS_PARM0] = PR_TempString(csqcprogs, Cmd_Args()));\n\n\tPR_ExecuteProgram (csqcprogs, csqcg.GameCommand);\n}\n\nvoid CSQC_PlayerInfoChanged(int player)\n{\n\tvoid *pr_globals;\n\tif (!csqcprogs || !csqcg.CSQC_PlayerInfoChanged)\n\t\treturn;\n\n\tpr_globals = PR_globals(csqcprogs, PR_CURRENT);\n\tG_FLOAT(OFS_PARM0) = player;\n//\t(((string_t *)pr_globals)[OFS_PARM1] = PR_TempString(csqcprogs, keyname));\n\tPR_ExecuteProgram (csqcprogs, csqcg.CSQC_PlayerInfoChanged);\n}\nvoid CSQC_ServerInfoChanged(void)\n{\n//\tvoid *pr_globals;\n\tif (!csqcprogs || !csqcg.CSQC_ServerInfoChanged)\n\t\treturn;\n\n//\tpr_globals = PR_globals(csqcprogs, PR_CURRENT);\n//\t(((string_t *)pr_globals)[OFS_PARM0] = PR_TempString(csqcprogs, keyname));\n\tPR_ExecuteProgram (csqcprogs, csqcg.CSQC_ServerInfoChanged);\n}\n\nqboolean CSQC_ParseTempEntity(void)\n{\n\tint obit;\n\tvoid *pr_globals;\n\tif (!csqcprogs || !csqcg.CSQC_Parse_TempEntity)\n\t\treturn false;\n\n\tpr_globals = PR_globals(csqcprogs, PR_CURRENT);\n\tcsqc_mayread = true;\n\tobit = net_message.currentbit;\n\tPR_ExecuteProgram (csqcprogs, csqcg.CSQC_Parse_TempEntity);\n\tcsqc_mayread = false;\n\tif (G_FLOAT(OFS_RETURN))\n\t\treturn true;\n\t//failed. reset the read position.\n\tnet_message.currentbit = obit;\n\tmsg_badread = false;\n\treturn false;\n}\n\nqboolean CSQC_ParseGamePacket(int seat, qboolean sized)\n{\n\tint parsefnc = csqcg.CSQC_Parse_Event?csqcg.CSQC_Parse_Event:csqcg.CSQC_Parse_TempEntity;\n\n\tif (sized)\n\t{\n\t\tint len = (unsigned short)MSG_ReadShort();\n\t\tint start = MSG_GetReadCount(), end;\n\n\t\tif (!csqcprogs || !parsefnc)\n\t\t{\n\t\t\tMSG_ReadSkip(len);\n\t\t\treturn false;\n\t\t}\n\n\t\tcsqc_mayread = true;\n\t\tCSQC_ChangeLocalPlayer(seat);\n\t\tPR_ExecuteProgram (csqcprogs, parsefnc);\n\n\t\tend = MSG_GetReadCount();\n\t\tif (end != start + len)\n\t\t{\n\t\t\tCon_Printf(\"Gamecode misread a gamecode packet (%i bytes too much)\\n\", end - (start+len));\n\t\t\tMSG_ReadSkip(start + len - end);\t//unread or skip.\n\t\t}\n\t}\n\telse\n\t{\n\t\tif (!csqcprogs || !parsefnc)\n\t\t{\n\t\t\tint next = MSG_ReadByte();\n\t\t\tif (!csqcprogs)\n\t\t\t\tHost_EndGame(\"This server requires CSQC support, but \\\"csprogsvers/%x.dat\\\" wasn't loaded.\\n\", csprogs_checksum);\n\t\t\telse\n\t\t\t\tHost_EndGame(\"Loaded CSQC module is unable to parse events (lead byte %i).\\n\", next);\n\t\t\treturn false;\n\t\t}\n\t\tcsqc_mayread = true;\n\t\tCSQC_ChangeLocalPlayer(seat);\n\t\tPR_ExecuteProgram (csqcprogs, parsefnc);\n\t}\n\tcsqc_mayread = false;\n\treturn true;\n}\n\nvoid CSQC_MapEntityEdited(int modelindex, int idx, const char *newe)\n{\n\tvoid *pr_globals;\n\tif (!csqcprogs || !csqcg.CSQC_MapEntityEdited)\n\t\treturn;\n\n\tif (modelindex != 1)\n\t\treturn;\n\n\tpr_globals = PR_globals(csqcprogs, PR_CURRENT);\n\tG_INT(OFS_PARM0) = idx;\n\t(((string_t *)pr_globals)[OFS_PARM1] = PR_TempString(csqcprogs, newe));\n\tPR_ExecuteProgram (csqcprogs, csqcg.CSQC_MapEntityEdited);\n}\n\n/*qboolean CSQC_LoadResource(char *resname, char *restype)\n{\n\tvoid *pr_globals;\n\tif (!csqcprogs || !csqcg.loadresource)\n\t\treturn true;\n\n\tpr_globals = PR_globals(csqcprogs, PR_CURRENT);\n\t(((string_t *)pr_globals)[OFS_PARM0] = PR_TempString(csqcprogs, resname));\n\t(((string_t *)pr_globals)[OFS_PARM1] = PR_TempString(csqcprogs, restype));\n\n\tPR_ExecuteProgram (csqcprogs, csqcg.loadresource);\n\n\treturn !!G_FLOAT(OFS_RETURN);\n}*/\n\nqboolean CSQC_Parse_Damage(int seat, float save, float take, vec3_t source)\n{\n\tvoid *pr_globals;\n\tif (!csqcprogs || !csqcg.CSQC_Parse_Damage)\n\t\treturn false;\n\n\tCSQC_ChangeLocalPlayer(seat);\n\t\n\tpr_globals = PR_globals(csqcprogs, PR_CURRENT);\n\t((float *)pr_globals)[OFS_PARM0] = save;\n\t((float *)pr_globals)[OFS_PARM1] = take;\n\t((float *)pr_globals)[OFS_PARM2+0] = source[0];\n\t((float *)pr_globals)[OFS_PARM2+1] = source[1];\n\t((float *)pr_globals)[OFS_PARM2+2] = source[2];\n\tPR_ExecuteProgram (csqcprogs, csqcg.CSQC_Parse_Damage);\n\n\treturn G_FLOAT(OFS_RETURN);\n}\n\nqboolean CSQC_ParsePrint(char *message, int printlevel)\n{\n\tvoid *pr_globals;\n\tint bufferpos;\n\tchar *nextline;\n\tqboolean doflush;\n\tif (!csqcprogs || !csqcg.CSQC_Parse_Print)\n\t{\n\t\treturn false;\n\t}\n\n\tbufferpos = strlen(csqc_printbuffer);\n\n\t//fix-up faked bot chat\n\tif (*message == '\\1' && *csqc_printbuffer == '\\1')\n\t\tmessage++;\n\n\twhile(*message)\n\t{\n\t\tnextline = strchr(message, '\\n');\n\t\tif (nextline)\n\t\t{\n\t\t\tnextline+=1;\n\t\t\tdoflush = true;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tnextline = message+strlen(message);\n\t\t\tdoflush = false;\n\t\t}\n\n\t\tif (bufferpos + nextline-message >= sizeof(csqc_printbuffer))\n\t\t{\n\t\t\t//if this would overflow the buffer, cap its length and flush the buffer\n\t\t\t//this copes with too many strings and too long strings.\n\t\t\tnextline = message + sizeof(csqc_printbuffer)-1 - bufferpos;\n\t\t\tdoflush = true;\n\t\t}\n\n\t\tmemcpy(csqc_printbuffer+bufferpos, message, nextline-message);\n\t\tbufferpos += nextline-message;\n\t\tcsqc_printbuffer[bufferpos] = '\\0';\n\t\tmessage = nextline;\n\n\t\tif (doflush)\n\t\t{\n\t\t\tpr_globals = PR_globals(csqcprogs, PR_CURRENT);\n\t\t\t(((string_t *)pr_globals)[OFS_PARM0] = PR_TempString(csqcprogs, csqc_printbuffer));\n\t\t\tG_FLOAT(OFS_PARM1) = printlevel;\n\t\t\tPR_ExecuteProgram (csqcprogs, csqcg.CSQC_Parse_Print);\n\n\t\t\tbufferpos = 0;\n\t\t\tcsqc_printbuffer[bufferpos] = 0;\n\t\t}\n\t}\n\treturn true;\n}\n\nqboolean CSQC_StuffCmd(int lplayernum, char *cmd, char *cmdend)\n{\n\tvoid *pr_globals;\n\tchar tmp[2];\n\tif (!csqcprogs || !csqcg.CSQC_Parse_StuffCmd)\n\t\treturn false;\n\n\tCSQC_ChangeLocalPlayer(lplayernum);\n\n\tpr_globals = PR_globals(csqcprogs, PR_CURRENT);\n\ttmp[0] = cmdend[0];\n\ttmp[1] = cmdend[1];\n\tcmdend[0] = '\\n';\n\tcmdend[1] = 0;\n\t(((string_t *)pr_globals)[OFS_PARM0] = PR_TempString(csqcprogs, cmd));\n\tcmdend[0] = tmp[0];\n\tcmdend[1] = tmp[1];\n\n\tPR_ExecuteProgram (csqcprogs, csqcg.CSQC_Parse_StuffCmd);\n\treturn true;\n}\nqboolean CSQC_CenterPrint(int seat, const char *cmd)\n{\n\tvoid *pr_globals;\n\tif (!csqcprogs || !csqcg.CSQC_Parse_CenterPrint)\n\t\treturn false;\n\n\tCSQC_ChangeLocalPlayer(seat);\n\n\tpr_globals = PR_globals(csqcprogs, PR_CURRENT);\n\t(((string_t *)pr_globals)[OFS_PARM0] = PR_TempString(csqcprogs, cmd));\n\n\tPR_ExecuteProgram (csqcprogs, csqcg.CSQC_Parse_CenterPrint);\n\treturn G_FLOAT(OFS_RETURN) || csqc_isdarkplaces;\n}\n\nqboolean CSQC_Parse_SetAngles(int seat, vec3_t newangles, qboolean wasdelta)\n{\n\tvoid *pr_globals;\n\tif (!csqcprogs || !csqcg.CSQC_Parse_SetAngles)\n\t\treturn false;\n\n\tCSQC_ChangeLocalPlayer(seat);\n\n\tpr_globals = PR_globals(csqcprogs, PR_CURRENT);\n\t((float *)pr_globals)[OFS_PARM0+0] = newangles[0];\n\t((float *)pr_globals)[OFS_PARM0+1] = newangles[1];\n\t((float *)pr_globals)[OFS_PARM0+2] = newangles[2];\n\t((float *)pr_globals)[OFS_PARM1] = wasdelta;\n\n\tPR_ExecuteProgram (csqcprogs, csqcg.CSQC_Parse_SetAngles);\n\treturn G_FLOAT(OFS_RETURN);\n}\n\nvoid CSQC_Input_Frame(int seat, usercmd_t *cmd)\n{\n\tif (!csqcprogs || !csqcg.CSQC_Input_Frame)\n\t\treturn;\n\n\tCSQC_ChangeLocalPlayer(seat);\n\n\tif (csqcg.time)\n\t\t*csqcg.time = cl.servertime;\n\tif (csqcg.cltime)\n\t\t*csqcg.cltime = realtime-cl.mapstarttime;\n\n\tif (csqcg.clientcommandframe)\n\t\t*csqcg.clientcommandframe = cl.movesequence;\n\n\tcs_set_input_state(cmd);\n\tPR_ExecuteProgram (csqcprogs, csqcg.CSQC_Input_Frame);\n\tcs_get_input_state(cmd);\n}\n\n//this protocol allows up to 32767 edicts.\n#ifdef PEXT_CSQC\nstatic void CSQC_EntityCheck(unsigned int entnum)\n{\n\tunsigned int newmax;\n\n\tif (entnum >= maxcsqcentities)\n\t{\n\t\tnewmax = entnum+64;\n\t\tcsqcent = BZ_Realloc(csqcent, sizeof(*csqcent)*newmax);\n\t\tmemset(csqcent + maxcsqcentities, 0, (newmax - maxcsqcentities)*sizeof(csqcent));\n\t\tmaxcsqcentities = newmax;\n\t}\n}\n\nint CSQC_StartSound(int entnum, int channel, char *soundname, vec3_t pos, float vol, float attenuation, float pitchmod, float timeofs, unsigned int flags)\n{\n\tvoid *pr_globals;\n\tcsqcedict_t *ent;\n\n\tif (!csqcprogs)\n\t\treturn false;\n\tif (csqcg.CSQC_Event_Sound)\n\t{\n\t\tpr_globals = PR_globals(csqcprogs, PR_CURRENT);\n\n\t\tCSQC_EntityCheck(entnum);\n\t\tent = csqcent[entnum];\n\t\tif (ent)\n\t\t\t*csqcg.self = EDICT_TO_PROG(csqcprogs, (void*)ent);\n\t\telse\n\t\t\t*csqcg.self = 0;\n\n\t\tG_FLOAT(OFS_PARM0) = entnum;\n\t\tG_FLOAT(OFS_PARM1) = channel;\n\t\tG_INT(OFS_PARM2) = PR_TempString(csqcprogs, soundname);\n\t\tG_FLOAT(OFS_PARM3) = vol;\n\t\tG_FLOAT(OFS_PARM4) = attenuation;\n\t\tVectorCopy(pos, G_VECTOR(OFS_PARM5));\n\t\tG_FLOAT(OFS_PARM6) = pitchmod*100;\n\t\tG_FLOAT(OFS_PARM7) = flags;\n//\t\tG_FLOAT(OFS_PARM8) = timeofs;\n\n\t\tPR_ExecuteProgram(csqcprogs, csqcg.CSQC_Event_Sound);\n\n\t\treturn G_FLOAT(OFS_RETURN);\n\t}\n\telse if (csqcg.CSQC_ServerSound)\n\t{\n\t\tCSQC_EntityCheck(entnum);\n\t\tent = csqcent[entnum];\n\t\tif (!ent)\n\t\t\treturn false;\n\n\t\tpr_globals = PR_globals(csqcprogs, PR_CURRENT);\n\n\t\t*csqcg.self = EDICT_TO_PROG(csqcprogs, (void*)ent);\n\t\tG_FLOAT(OFS_PARM0) = channel;\n\t\tG_INT(OFS_PARM1) = PR_TempString(csqcprogs, soundname);\n\t\tVectorCopy(pos, G_VECTOR(OFS_PARM2));\n\t\tG_FLOAT(OFS_PARM3) = vol;\n\t\tG_FLOAT(OFS_PARM4) = attenuation;\n\t\tG_FLOAT(OFS_PARM5) = flags;\n\t\tG_FLOAT(OFS_PARM6) = timeofs;\n\n\t\tPR_ExecuteProgram(csqcprogs, csqcg.CSQC_ServerSound);\n\n\t\treturn G_FLOAT(OFS_RETURN);\n\t}\n\treturn false;\n}\n\nqboolean CSQC_GetEntityOrigin(unsigned int csqcent, float *out)\n{\n\twedict_t *ent;\n\tif (!csqcprogs)\n\t\treturn false;\n\tent = WEDICT_NUM_UB(csqcprogs, csqcent);\n\tVectorCopy(ent->v->origin, out);\n\treturn true;\n}\nqboolean CSQC_GetSSQCEntityOrigin(unsigned int ssqcent, float *out)\n{\n\tcsqcedict_t *ent;\n\tif (csqcprogs && ssqcent < maxcsqcentities)\n\t{\n\t\tent = csqcent[ssqcent];\n\t\tif (ent)\n\t\t{\n\t\t\tif (out)\n\t\t\t\tVectorCopy(ent->v->origin, out);\n\t\t\treturn true;\n\t\t}\n\t}\n\treturn false;\n}\n\nvoid CSQC_ParseEntities(qboolean sized)\n{\n\tcsqcedict_t *ent;\n\tunsigned int entnum;\n\tvoid *pr_globals;\n\tint packetsize;\n\tint packetstart;\n\tqboolean removeflag;\n\n\tif (!csqcprogs)\n\t{\n\t\tconst char *fname = va(\"csprogsvers/%x.dat\", (unsigned int)strtoul(InfoBuf_ValueForKey(&cl.serverinfo, \"*csprogs\"), NULL, 0));\n\t\tconst char *msg;\n\t\tif (cl_nocsqc.value)\n\t\t\tmsg = \"blocked by cl_nocsqc.\\n\";\n\t\telse if (!COM_FCheckExists(fname))\n\t\t{\n\t\t\textern cvar_t cl_downloads, cl_download_csprogs;\n\t\t\tif (!cl_downloads.ival || !cl_download_csprogs.ival)\n\t\t\t\tmsg = \"downloading blocked by cl_downloads or cl_download_csprogs.\\n\";\n\t\t\telse\n\t\t\t\tmsg = \"unable to download.\\n\";\n\t\t}\n\t\telse\n\t\t\tmsg = \"not initialised.\\n\";\n\t\tHost_EndGame(\"%s required, but %s\", fname, msg);\n\t}\n\n\tif (!csqcg.CSQC_Ent_Update || !csqcg.self)\n\t\tHost_EndGame(\"CSQC has no CSQC_Ent_Update function\\n\");\n\tif (!csqc_world.worldmodel || csqc_world.worldmodel->loadstate != MLS_LOADED)\n\t\tHost_EndGame(\"world is not yet initialised\\n\");\n\n\tpr_globals = PR_globals(csqcprogs, PR_CURRENT);\n\n\tCL_CalcClientTime();\n\tif (csqcg.time)\t\t//estimated server time\n\t\t*csqcg.time = cl.servertime;\n\tif (csqcg.cltime)\t//smooth client time.\n\t\t*csqcg.cltime = realtime-cl.mapstarttime;\n\n\tif (csqcg.servertime)\n\t\t*csqcg.servertime = cl.gametime;\n\tif (csqcg.serverprevtime)\n\t\t*csqcg.serverprevtime = cl.oldgametime;\n\tif (csqcg.serverdeltatime)\n\t\t*csqcg.serverdeltatime = cl.gametime - cl.oldgametime;\n\n\tif (!csqc_isdarkplaces)\n\t{\n\t\tif (csqcg.clientcommandframe)\n\t\t\t*csqcg.clientcommandframe = cl.movesequence;\n\t\tif (csqcg.servercommandframe)\n\t\t\t*csqcg.servercommandframe = cl.ackedmovesequence;\n\t}\n\n\tfor(;;)\n\t{\n\t\t//replacement deltas now also includes 22bit entity num indicies.\n\t\tif (cls.fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS)\n\t\t{\n\t\t\tentnum = (unsigned short)MSG_ReadShort();\n\t\t\tremoveflag = !!(entnum & 0x8000);\n\t\t\tif (entnum & 0x4000)\n\t\t\t\tentnum = (entnum & 0x3fff) | (MSG_ReadByte()<<14);\n\t\t\telse\n\t\t\t\tentnum &= ~0x8000;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tentnum = (unsigned short)MSG_ReadShort();\n\t\t\tremoveflag = !!(entnum & 0x8000);\n\t\t\tentnum &= ~0x8000;\n\t\t}\n\n\t\tif ((!entnum && !removeflag) || msg_badread)\n\t\t\tbreak;\n\n\t\tif (removeflag)\n\t\t{\t//remove\n\t\t\tif (!entnum)\n\t\t\t\tHost_EndGame(\"CSQC cannot remove world!\\n\");\n\n\t\t\tCSQC_EntityCheck(entnum);\n\n\t\t\tif (cl_shownet.ival == 3)\n\t\t\t\tCon_Printf(\"%3i:     Remove %i\\n\", MSG_GetReadCount(), entnum);\n\t\t\telse if (cl_csqcdebug.ival)\n\t\t\t\tCon_Printf(\"Remove %i\\n\", entnum);\n\n\t\t\tent = csqcent[entnum];\n\t\t\tcsqcent[entnum] = NULL;\n\n\t\t\tif (!ent)\t//hrm.\n\t\t\t\tcontinue;\n\n\t\t\tCSQC_EntRemove(ent);\n\t\t\t//the csqc is expected to call the remove builtin.\n\t\t}\n\t\telse\n\t\t{\n\t\t\tCSQC_EntityCheck(entnum);\n\n\t\t\tif (sized)\n\t\t\t{\n\t\t\t\tpacketsize = MSG_ReadShort();\n\t\t\t\tpacketstart = MSG_GetReadCount();\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tpacketsize = 0;\n\t\t\t\tpacketstart = 0;\n\t\t\t}\n\n\t\t\tent = csqcent[entnum];\n\t\t\tif (!ent)\n\t\t\t{\n\t\t\t\tif (csqcg.CSQC_Ent_Spawn)\n\t\t\t\t{\n\t\t\t\t\t*csqcg.self = 0;\n\t\t\t\t\tG_FLOAT(OFS_PARM0) = entnum;\n\t\t\t\t\tPR_ExecuteProgram(csqcprogs, csqcg.CSQC_Ent_Spawn);\n\t\t\t\t\tent = csqcent[entnum] = (csqcedict_t*)PROG_TO_WEDICT(csqcprogs, *csqcg.self);\t//allow the mod to change the ent.\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tent = (csqcedict_t*)ED_Alloc(csqcprogs, false, 0);\n\t\t\t\t\tcsqcent[entnum] = ent;\n\t\t\t\t\tent->xv->entnum = entnum;\n\t\t\t\t}\n\n\t\t\t\tG_FLOAT(OFS_PARM0) = true;\n\n\t\t\t\tif (cl_shownet.ival == 3)\n\t\t\t\t\tCon_Printf(\"%3i:     Added %i (%i)\\n\", MSG_GetReadCount(), entnum, packetsize);\n\t\t\t\telse if (cl_csqcdebug.ival)\n\t\t\t\t\tCon_Printf(\"Add %i\\n\", entnum);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tG_FLOAT(OFS_PARM0) = false;\n\t\t\t\tif (cl_shownet.ival == 3)\n\t\t\t\t\tCon_Printf(\"%3i:     Update %i (%i)\\n\", MSG_GetReadCount(), entnum, packetsize);\n\t\t\t\telse if (cl_csqcdebug.ival)\n\t\t\t\t\tCon_Printf(\"Update %i\\n\", entnum);\n\t\t\t}\n#ifdef QUAKESTATS\n\t\t\tif (entnum-1 < cl.allocated_client_slots && cls.findtrack && cl.players[entnum-1].stats[STAT_HEALTH] > 0)\n\t\t\t{\t//FIXME: is this still needed with the autotrack stuff?\n\t\t\t\tCam_Lock(&cl.playerview[0], entnum-1);\n\t\t\t\tcls.findtrack = false;\n\t\t\t}\n#endif\n\n\t\t\t*csqcg.self = EDICT_TO_PROG(csqcprogs, (void*)ent);\n\t\t\tcsqc_mayread = true;\n\t\t\tPR_ExecuteProgram(csqcprogs, csqcg.CSQC_Ent_Update);\n\t\t\tcsqc_mayread = false;\n\t\t\tif (csqcg.CSQC_Ent_Spawn)\n\t\t\t\tcsqcent[entnum] = (csqcedict_t*)PROG_TO_WEDICT(csqcprogs, *csqcg.self);\t//allow the mod to change the ent.\n\n\t\t\tif (sized)\n\t\t\t{\n\t\t\t\tunsigned int readcount = MSG_GetReadCount();\n\t\t\t\tif (readcount != packetstart+packetsize)\n\t\t\t\t{\n\t\t\t\t\tif (readcount > packetstart+packetsize)\n\t\t\t\t\t\tCon_Printf(\"CSQC overread entity %i. Size %i, read %i\", entnum, packetsize, readcount - packetstart);\n\t\t\t\t\telse\n\t\t\t\t\t\tCon_Printf(\"CSQC underread entity %i. Size %i, read %i\", entnum, packetsize, readcount - packetstart);\n\t\t\t\t\tCon_Printf(\", first byte is %i(%x)\\n\", net_message.data[readcount], net_message.data[readcount]);\n#ifndef CLIENTONLY\n\t\t\t\t\tif (sv.state)\n\t\t\t\t\t{\n\t\t\t\t\t\tCon_Printf(\"Server classname: \\\"%s\\\"\\n\", PR_GetString(svprogfuncs, EDICT_NUM_UB(svprogfuncs, entnum)->v->classname));\n\t\t\t\t\t}\n#endif\n\t\t\t\t}\n\t\t\t\tMSG_ReadSkip(packetstart+packetsize - readcount);\t//unread or skip.\n\t\t\t}\n\t\t}\n\t}\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "engine/client/pr_menu.c",
    "content": "#include \"quakedef.h\"\n\n#include \"pr_common.h\"\n#include \"shader.h\"\n\n#ifdef GLQUAKE\n#include \"glquake.h\"\n#endif\n\n#if defined(MENU_DAT) || defined(CSQC_DAT)\n#include \"cl_master.h\"\n\n//MP_MouseMove(mx, my, mouse->qdeviceid)\n\nstatic qbyte mpkeysdown[K_MAX/8];\nextern qboolean csqc_dp_lastwas3d;\n\nvoid M_Init_Internal (void);\nvoid M_DeInit_Internal (void);\n\nextern unsigned int r2d_be_flags;\nstatic unsigned int PF_SelectDPDrawFlag(pubprogfuncs_t *prinst, int flag)\n{\n\tif (r_refdef.warndraw)\n\t{\n\t\tif (!*r_refdef.rt_destcolour[0].texname)\n\t\t{\n\t\t\tr_refdef.warndraw = false; //don't spam too much\n\t\t\tPR_RunWarning(prinst, \"Detected attempt to draw to framebuffer where framebuffer is not valid\\n\");\n\t\t}\n\t}\n#ifdef CSQC_DAT\n\tcsqc_dp_lastwas3d = false;\t//for compat with dp's stupid beginpolygon\n#endif\n\n\t//flags:\n\t//0 = blend\n\t//1 = add\n\t//2 = modulate\n\t//3 = modulate*2\n\tflag &= 3;\n\tif (flag == DRAWFLAG_ADD)\n\t\treturn BEF_FORCEADDITIVE;\n\telse\n\t\treturn 0;\n}\n\n//float\tdrawfill(vector position, vector size, vector rgb, float alpha, float flag) = #457;\nvoid QCBUILTIN PF_CL_drawfill (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tfloat *pos = G_VECTOR(OFS_PARM0);\n\tfloat *size = G_VECTOR(OFS_PARM1);\n\tfloat *rgb = G_VECTOR(OFS_PARM2);\n\tfloat alpha = G_FLOAT(OFS_PARM3);\n\tint flag = prinst->callargc >= 5?G_FLOAT(OFS_PARM4):0;\n\n\tr2d_be_flags = PF_SelectDPDrawFlag(prinst, flag);\n\tR2D_ImageColours(rgb[0], rgb[1], rgb[2], alpha);\n\tR2D_FillBlock(pos[0], pos[1], size[0], size[1]);\n\tr2d_be_flags = 0;\n\n\tG_FLOAT(OFS_RETURN) = 1;\n}\n//void\tdrawsetcliparea(float x, float y, float width, float height) = #458;\nvoid QCBUILTIN PF_CL_drawsetcliparea (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tsrect_t srect;\n\tif (R2D_Flush)\n\t\tR2D_Flush();\n\n#ifdef CSQC_DAT\n\tcsqc_dp_lastwas3d = false;\n#endif\n\n\tsrect.x = G_FLOAT(OFS_PARM0) / (float)vid.fbvwidth;\n\tsrect.y = G_FLOAT(OFS_PARM1) / (float)vid.fbvheight;\n\tsrect.width = G_FLOAT(OFS_PARM2) / (float)vid.fbvwidth;\n\tsrect.height = G_FLOAT(OFS_PARM3) / (float)vid.fbvheight;\n\tsrect.dmin = -99999;\n\tsrect.dmax = 99999;\n\tsrect.y = (1-srect.y) - srect.height;\n\tBE_Scissor(&srect);\n\n\tG_FLOAT(OFS_RETURN) = 1;\n}\n//void\tdrawresetcliparea(void) = #459;\nvoid QCBUILTIN PF_CL_drawresetcliparea (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tif (R2D_Flush)\n\t\tR2D_Flush();\n\n#ifdef CSQC_DAT\n\tcsqc_dp_lastwas3d = false;\n#endif\n\n\tBE_Scissor(NULL);\n\tG_FLOAT(OFS_RETURN) = 1;\n}\n\n#define FONT_SLOTS 32\n#define FONT_SIZES 16\nstruct {\n\tunsigned int owner;\t//kdm_foo. whoever has an interest in this font. font is purged when this becomes 0.\n\tchar slotname[16];\n\tchar facename[MAX_OSPATH];\n\tfloat scale; //poop\n\tint outline; //argh\n\tunsigned int fontflags; //erk\n\tint sizes;\n\tint size[FONT_SIZES];\n\tstruct font_s *font[FONT_SIZES];\n} fontslot[FONT_SLOTS];\n\nstatic struct font_s *PR_CL_ChooseFont(float *fontsel, int szx, int szy)\n{\n\tint fontidx = 0;\t//default by default...\n\tstruct font_s *font = font_default;\n\n\tif (fontsel)\n\t{\n\t\tfontidx = *fontsel;\n\t}\n\n\tif (fontidx >= 0 && fontidx < FONT_SLOTS)\n\t{\n\t\tint i, j;\n\t\tint fontdiff = 10000;\n\t\tfor (i = 0; i < fontslot[fontidx].sizes; i++)\n\t\t{\n\t\t\tj = abs(szy - fontslot[fontidx].size[i]);\n\t\t\tif (j < fontdiff && fontslot[fontidx].font[i])\n\t\t\t{\n\t\t\t\tfontdiff = j;\n\t\t\t\tfont = fontslot[fontidx].font[i];\n\t\t\t}\n\t\t}\n\t}\n\treturn font;\n}\nvoid PR_CL_BeginString(pubprogfuncs_t *prinst, float vx, float vy, float szx, float szy, float *px, float *py)\n{\n\tworld_t *world = prinst->parms->user;\n\tstruct font_s *font;\n\tif (world->g.drawfontscale && (world->g.drawfontscale[0] || world->g.drawfontscale[1]))\n\t{\n\t\tszx *= world->g.drawfontscale[0];\n\t\tszy *= world->g.drawfontscale[1];\n\t}\n\tfont = PR_CL_ChooseFont(world->g.drawfont, szx, szy);\n\tFont_BeginScaledString(font, vx, vy, szx, szy, px, py);\n}\nint PR_findnamedfont(const char *name, qboolean isslotname)\n{\n\tint i;\n\tif (isslotname)\n\t{\n\t\tfor (i = 0; i < FONT_SLOTS; i++)\n\t\t{\n\t\t\tif (!stricmp(fontslot[i].slotname, name))\n\t\t\t\treturn i;\n\t\t}\n\t}\n\telse\n\t{\n\t\tfor (i = 0; i < FONT_SLOTS; i++)\n\t\t{\n\t\t\tif (!stricmp(fontslot[i].facename, name))\n\t\t\t\treturn i;\n\t\t}\n\t}\n\treturn -1;\n}\nint PR_findunusedfont(void)\n{\n\tint i;\n\t//don't find slot 0.\n\tfor (i = FONT_SLOTS; i-- > 1; )\n\t{\n\t\tif (!*fontslot[i].slotname && !*fontslot[i].facename)\n\t\t\treturn i;\n\t}\n\treturn -1;\n}\n//purgeowner is the bitmask of owners that are getting freed.\n//if purgeowner is 0, fonts will get purged\nvoid PR_ReleaseFonts(unsigned int purgeowner)\n{\n\tint i, j;\n\n\tfor (i = 0; i < FONT_SLOTS; i++)\n\t{\n\t\tif (fontslot[i].owner)\n\t\t\tcontinue;\t//already free\n\t\tfontslot[i].owner &= ~purgeowner;\n\t\tif (fontslot[i].owner)\n\t\t\tcontinue;\t//still owned by someone\n\n\t\tfor (j = 0; j < fontslot[i].sizes; j++)\n\t\t{\n\t\t\tif (fontslot[i].font[j])\n\t\t\t\tFont_Free(fontslot[i].font[j]);\n\t\t\tfontslot[i].font[j] = NULL;\n\t\t}\n\n\t\tfontslot[i].sizes = 0;\n\t\tfontslot[i].slotname[0] = '\\0';\n\t\tfontslot[i].facename[0] = '\\0';\n\t\tfontslot[i].scale = 1;\n\t\tfontslot[i].outline = 0;\n\t}\n}\nvoid PR_ReloadFonts(qboolean reload)\n{\n\tint i, j;\n\n\tif (qrenderer == QR_NONE)\n\t\treload = false;\n\n\tfor (i = 0; i < FONT_SLOTS; i++)\n\t{\n\t\t//already not loaded\n\t\tif (!fontslot[i].owner)\n\t\t\tcontinue;\n\n\t\t//flush it (if loaded)\n\t\tfor (j = 0; j < fontslot[i].sizes; j++)\n\t\t{\n\t\t\tif (fontslot[i].font[j])\n\t\t\t\tFont_Free(fontslot[i].font[j]);\n\t\t\tfontslot[i].font[j] = NULL;\n\t\t}\n\t\t//and reload if needed\n\t\tif (reload)\n\t\t{\t//otherwise load it.\n\t\t\tfor (j = 0; j < fontslot[i].sizes; j++)\n\t\t\t{\n\t\t\t\tfontslot[i].font[j] = Font_LoadFont(fontslot[i].facename, fontslot[i].size[j], fontslot[i].scale, fontslot[i].outline, fontslot[i].fontflags);\n\t\t\t}\n\t\t}\n\t}\n}\nvoid QCBUILTIN PF_CL_findfont (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char *slotname = PR_GetStringOfs(prinst, OFS_PARM0);\n\tG_FLOAT(OFS_RETURN) = PR_findnamedfont(slotname, true) + 1;\t//return default on failure.\n}\nvoid QCBUILTIN PF_CL_loadfont (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\textern cvar_t r_font_postprocess_outline;\n\tconst char *slotname = PR_GetStringOfs(prinst, OFS_PARM0);\n\tconst char *facename = PR_GetStringOfs(prinst, OFS_PARM1);\n\tconst char *sizestr = PR_GetStringOfs(prinst, OFS_PARM2);\n\tint slotnum = (prinst->callargc>3)?G_FLOAT(OFS_PARM3):-1;\n\t//float fix_scale = (prinst->callargc>4)?G_FLOAT(OFS_PARM4):0;\n\t//float fix_voffset = (prinst->callargc>5)G_FLOAT(OFS_PARM5):0;\n\tint i, sz;\n\tworld_t *world = prinst->parms->user;\n\n\tG_FLOAT(OFS_RETURN) = 0;\t//return default on failure.\n\n\tif (slotnum < 0 && *slotname)\n\t\tslotnum = PR_findnamedfont(slotname, true);\n\telse if (slotnum < 0)\n\t\tslotnum = PR_findnamedfont(facename, false);\n\tif (slotnum < 0)\n\t\tslotnum = PR_findunusedfont();\n\tif (slotnum < 0)\n\t\treturn;\t//eep.\n\n\tif ((unsigned)slotnum >= FONT_SLOTS)\n\t\treturn;\n\n\t//if its changed, purge it.\n\tif (stricmp(fontslot[slotnum].slotname, slotname) || stricmp(fontslot[slotnum].facename, facename) || !fontslot[slotnum].sizes)\n\t{\n\t\tQ_strncpyz(fontslot[slotnum].slotname, slotname, sizeof(fontslot[slotnum].slotname));\n\t\tQ_strncpyz(fontslot[slotnum].facename, facename, sizeof(fontslot[slotnum].facename));\n\t\tfor (i = 0; i < fontslot[slotnum].sizes; i++)\n\t\t{\n\t\t\tif (fontslot[slotnum].font[i])\n\t\t\t\tFont_Free(fontslot[slotnum].font[i]);\n\t\t\tfontslot[slotnum].font[i] = NULL;\n\t\t}\n\t\tfontslot[slotnum].sizes = 0;\n\t\tfontslot[slotnum].owner = 0;\n\t\tfontslot[slotnum].scale = 1;\n\t\tfontslot[slotnum].outline = r_font_postprocess_outline.ival;\n\t}\n\tfontslot[slotnum].owner |= world->keydestmask;\n\n\twhile(*sizestr)\n\t{\n\t\tsizestr = COM_Parse(sizestr);\n\t\tif (!strncmp(com_token, \"scale=\", 6))\n\t\t{\n\t\t\tfontslot[slotnum].scale = atof(com_token+6);\n\t\t\tcontinue;\n\t\t}\n\t\tif (!strncmp(com_token, \"outline=\", 8))\n\t\t{\n\t\t\tfontslot[slotnum].outline = atoi(com_token+8);\n\t\t\tcontinue;\n\t\t}\n\t\tif (!strncmp(com_token, \"blur=\", 5))\n\t\t{\n\t\t\t//fontslot[slotnum].blur = atoi(com_token+5);\n\t\t\tcontinue;\n\t\t}\n\t\tif (!strncmp(com_token, \"voffset=\", 8))\n\t\t{\n\t\t\t//com_token+8 unused.\n\t\t\tcontinue;\n\t\t}\n\n\t\tsz = atoi(com_token);\n\t\tif (!sz)\n\t\t\tcontinue;\t//o.O\n\n\t\tfor (i = 0; i < fontslot[slotnum].sizes; i++)\n\t\t{\n\t\t\tif (fontslot[slotnum].size[i] == sz)\n\t\t\t\tbreak;\n\t\t}\n\t\tif (i == fontslot[slotnum].sizes)\n\t\t{\n\t\t\tif (i >= FONT_SIZES)\n\t\t\t\tbreak;\n\t\t\tfontslot[slotnum].size[i] = sz;\n\t\t\tfontslot[slotnum].font[i] = NULL;\n\t\t\tfontslot[slotnum].sizes++;\n\t\t}\n\t}\n\n\tif (qrenderer > QR_NONE)\n\t{\n\t\tfor (i = 0; i < fontslot[slotnum].sizes; i++)\n\t\t\tfontslot[slotnum].font[i] = Font_LoadFont(facename, fontslot[slotnum].size[i], fontslot[slotnum].scale, fontslot[slotnum].outline, fontslot[slotnum].fontflags);\n\t}\n\n\tG_FLOAT(OFS_RETURN) = slotnum;\n}\n\n#ifdef HAVE_LEGACY\nvoid CL_LoadFont_f(void)\n{\n\textern cvar_t r_font_postprocess_outline, r_font_postprocess_mono;\n\t//console command for compat with dp/debug.\n\tif (Cmd_Argc() == 1)\n\t{\n\t\tint i, j;\n\t\tint th;\n\t\tfor (i = 0; i < FONT_SLOTS; i++)\n\t\t{\n\t\t\tif (fontslot[i].sizes)\n\t\t\t{\n\t\t\t\tCon_Printf(\"%s[%i]: %s (\", fontslot[i].slotname, i, fontslot[i].facename);\n\t\t\t\tfor (j = 0; j < fontslot[i].sizes; j++)\n\t\t\t\t{\n\t\t\t\t\tif (j)\n\t\t\t\t\t\tCon_Printf(\", \");\n\t\t\t\t\tCon_Printf(\"%i\", fontslot[i].size[j]);\n\t\t\t\t\tif (fontslot[i].font[j])\n\t\t\t\t\t{\n\t\t\t\t\t\tth = Font_GetTrueHeight(fontslot[i].font[j]);\n\t\t\t\t\t\tif (th != Font_CharPHeight(fontslot[i].font[j]))\n\t\t\t\t\t\t\tCon_Printf(\"[%g]\", ((float)th*vid.height)/vid.pixelheight);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tCon_Printf(\")\\n\");\n\t\t\t}\n\t\t}\n\t}\n\telse\n\t{\n\t\tint i;\n\t\tint slotnum = 0;\n\t\tchar *slotname = Cmd_Argv(1);\n\t\tchar *facename = Cmd_Argv(2);\n\t\tint sizenum = 3;\n\t\textern cvar_t dpcompat_console, gl_font, con_textfont;\n\n\t\t//loadfont slot face size1 size2...\n\n\t\tslotnum = PR_findnamedfont(slotname, true);\n\t\tif (slotnum < 0)\n\t\t{\n\t\t\tchar *dpnames[] = {\"default\", \"console\", \"sbar\", \"notify\", \"chat\", \"centerprint\", \"infobar\", \"menu\", \"user0\", \"user1\", \"user2\", \"user3\", \"user4\", \"user5\", \"user6\", \"user7\", NULL};\n\t\t\tfor (i = 0; dpnames[i]; i++)\n\t\t\t{\n\t\t\t\tif (!strcmp(dpnames[i], slotname))\n\t\t\t\t{\n\t\t\t\t\t//assign it to this slot only if this slot does not already have a face. avoids corrupting already-loaded fonts.\n\t\t\t\t\tif (!*fontslot[i].facename)\n\t\t\t\t\t\tslotnum = i;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (slotnum < 0)\n\t\t\t\tslotnum = PR_findnamedfont(\"\", true);\t//whatever is still free\n\t\t}\n\t\tif (slotnum < 0)\n\t\t{\n\t\t\tCon_Printf(\"out of font slots\\n\");\n\t\t\treturn;\n\t\t}\n\n\t\t//if there's a new font in this slot, purge the old and change the name+face strings\n\t\tif (stricmp(fontslot[slotnum].slotname, slotname) || stricmp(fontslot[slotnum].facename, facename))\n\t\t{\n\t\t\tQ_strncpyz(fontslot[slotnum].slotname, slotname, sizeof(fontslot[slotnum].slotname));\n\t\t\tQ_strncpyz(fontslot[slotnum].facename, facename, sizeof(fontslot[slotnum].facename));\n\t\t\tfor (i = 0; i < fontslot[slotnum].sizes; i++)\n\t\t\t{\n\t\t\t\tif (fontslot[slotnum].font[i])\n\t\t\t\t\tFont_Free(fontslot[slotnum].font[i]);\n\t\t\t\tfontslot[slotnum].font[i] = NULL;\n\t\t\t}\n\t\t\tfontslot[slotnum].owner = 0;\n\t\t\tfontslot[slotnum].scale = 1;\n\t\t\tfontslot[slotnum].sizes = 0;\n\t\t\tfontslot[slotnum].outline = r_font_postprocess_outline.ival;\t//locked in at definition, so different fonts can have different settings even with vid_reload going on.\n\t\t\tfontslot[slotnum].fontflags = 0 |\n\t\t\t\t\t\t\t\t\t\t(r_font_postprocess_mono.ival?FONT_MONO:0);\n\t\t}\n\t\tif (!*facename)\n\t\t\treturn;\n\t\tfontslot[slotnum].owner |= kdm_console;\t//fonts owned by the console are never forgotten.\n\t\t\n\t\twhile(sizenum < Cmd_Argc())\n\t\t{\n\t\t\tconst char *a = Cmd_Argv(sizenum++);\n\t\t\tint sz;\n\t\t\tif (!strcmp(a, \"scale\"))\n\t\t\t{\n\t\t\t\tfontslot[slotnum].scale = atof(Cmd_Argv(sizenum++));\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (!strcmp(a, \"outline\"))\n\t\t\t{\n\t\t\t\tfontslot[slotnum].outline = atoi(Cmd_Argv(sizenum++));\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (!strcmp(a, \"mono\"))\n\t\t\t{\n\t\t\t\tif (atoi(Cmd_Argv(sizenum++)))\n\t\t\t\t\tfontslot[slotnum].fontflags |= FONT_MONO;\n\t\t\t\telse\n\t\t\t\t\tfontslot[slotnum].fontflags &= ~FONT_MONO;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (!strcmp(a, \"blur\"))\n\t\t\t{\n\t\t\t\t//fontslot[slotnum].blur = atoi(Cmd_Argv(sizenum++));\n\t\t\t\tsizenum++;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (!strcmp(a, \"voffset\"))\n\t\t\t{\n//\t\t\t\tfontslot[slotnum].voffset = atof(Cmd_Argv(sizenum++));\n\t\t\t\tsizenum++;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tsz = atoi(a);\n\t\t\tif (sz <= 0)\n\t\t\t\tsz = 8;\n\n\t\t\tfor (i = 0; i < fontslot[slotnum].sizes; i++)\n\t\t\t{\n\t\t\t\tif (fontslot[slotnum].size[i] == sz)\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (i == fontslot[slotnum].sizes)\n\t\t\t{\n\t\t\t\tif (i >= FONT_SIZES)\n\t\t\t\t\tbreak;\n\t\t\t\tfontslot[slotnum].size[i] = sz;\n\t\t\t\tfontslot[slotnum].font[i] = NULL;\n\t\t\t\tfontslot[slotnum].sizes++;\n\t\t\t}\n\t\t}\n\n\t\tif (qrenderer > QR_NONE)\n\t\t{\n\t\t\tfor (i = 0; i < fontslot[slotnum].sizes; i++)\n\t\t\t\tfontslot[slotnum].font[i] = Font_LoadFont(facename, fontslot[slotnum].size[i], fontslot[slotnum].scale, fontslot[slotnum].outline, fontslot[slotnum].fontflags);\n\t\t}\n\n\t\t//FIXME: slotnum0==default is problematic.\n\t\tif (dpcompat_console.ival && slotnum == 1)\n\t\t\tCvar_Set(&con_textfont, facename);\n\t\tif (dpcompat_console.ival && slotnum == 0)\n\t\t\tCvar_Set(&gl_font, facename);\n\t}\n}\n#endif\n\n//scrolling could be done with scissoring.\n//selection could be done with some substrings\nvoid QCBUILTIN PF_CL_DrawTextField (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tfloat *pos = G_VECTOR(OFS_PARM0);\n\tfloat *size = G_VECTOR(OFS_PARM1);\n\tunsigned int flags = G_FLOAT(OFS_PARM2);\n\tconst char *text = PR_GetStringOfs(prinst, OFS_PARM3);\n\n\tworld_t *world = prinst->parms->user;\n\tvec2_t scale = {8, 8};\n\tstruct font_s *font;\n\tif (world->g.drawfontscale && (world->g.drawfontscale[0] || world->g.drawfontscale[1]))\n\t{\n\t\tscale[0] *= world->g.drawfontscale[0];\n\t\tscale[1] *= world->g.drawfontscale[1];\n\t}\n\tfont = PR_CL_ChooseFont(world->g.drawfont, scale[0], scale[1]);\n\n\t// Oversight ~eukara\n\tR2D_ImageColours(1.0f, 1.0f, 1.0f, 1.0f);\n\n\tG_FLOAT(OFS_RETURN) = R_DrawTextField(pos[0], pos[1], size[0], size[1], text, CON_WHITEMASK, flags, font, scale);\n}\n\n//float\tdrawstring(vector position, string text, vector scale, float alpha, float flag) = #455;\nvoid QCBUILTIN PF_CL_drawcolouredstring (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tfloat *pos = G_VECTOR(OFS_PARM0);\n\tconst char *text = PR_GetStringOfs(prinst, OFS_PARM1);\n\tfloat *size = G_VECTOR(OFS_PARM2);\n\tfloat alpha = 0;\n\tfloat flag = 0;\n\tfloat r, g, b;\n\tfloat px, py, ipx;\n\tunsigned int codeflags, codepoint;\n\n\tconchar_t buffer[2048], *str;\n\n\tif (prinst->callargc >= 6)\n\t{\n\t\tr = G_FLOAT(OFS_PARM3 + 0);\n\t\tg = G_FLOAT(OFS_PARM3 + 1);\n\t\tb = G_FLOAT(OFS_PARM3 + 2);\n\t\talpha = G_FLOAT(OFS_PARM4);\n\t\tflag = G_FLOAT(OFS_PARM5);\t//flag is mandatory to distinguish it.\n\t}\n\telse\n\t{\n\t\tr = 1;\n\t\tg = 1;\n\t\tb = 1;\n\t\talpha = G_FLOAT(OFS_PARM3);\n\t\tflag = prinst->callargc >= 5?G_FLOAT(OFS_PARM4):0;\n\t}\n\n\tif (!text)\n\t{\n\t\tG_FLOAT(OFS_RETURN) = -1;\t//was null..\n\t\treturn;\n\t}\n\n\tCOM_ParseFunString(CON_WHITEMASK, text, buffer, sizeof(buffer), false);\n\tstr = buffer;\n\n\tr2d_be_flags = PF_SelectDPDrawFlag(prinst, flag);\n\tPR_CL_BeginString(prinst, pos[0], pos[1], size[0], size[1], &px, &py);\n\tipx = px;\n\tR2D_ImageColours(r, g, b, alpha);\n\twhile(*str)\n\t{\n\t\tstr = Font_Decode(str, &codeflags, &codepoint);\n\t\tif (codeflags & CON_HIDDEN)\n\t\t\tcontinue;\n\t\tif (codepoint == '\\n')\n\t\t\tpy += Font_CharHeight();\n\t\telse if (codepoint == '\\r')\n\t\t\tpx = ipx;\n\t\telse\n\t\t\tpx = Font_DrawScaleChar(px, py, codeflags, codepoint);\n\t}\n\tR2D_ImageColours(1,1,1,1);\n\tFont_EndString(NULL);\n\tr2d_be_flags = 0;\n}\n\nvoid QCBUILTIN PF_CL_stringwidth(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconchar_t buffer[2048], *end;\n\tfloat px, py;\n\tconst char *text = PR_GetStringOfs(prinst, OFS_PARM0);\n\tint usecolours = G_FLOAT(OFS_PARM1);\n\tfloat *size = (prinst->callargc > 2)?G_VECTOR(OFS_PARM2):NULL;\n\n\tif (!qrenderer)\n\t{\n\t\tG_FLOAT(OFS_RETURN) = 0;\n\t\treturn;\n\t}\n\n\tend = COM_ParseFunString(CON_WHITEMASK, text, buffer, sizeof(buffer), !usecolours);\n\n\tPR_CL_BeginString(prinst, 0, 0, size?size[0]:8, size?size[1]:8, &px, &py);\n\tpx = Font_LineScaleWidth(buffer, end);\n\tFont_EndString(NULL);\n\n\tif (!size)\t//for compat with dp, divide by 8 after... because weird.\n\t\tpx /= 8;\n\n\tG_FLOAT(OFS_RETURN) = (px * vid.width) / vid.rotpixelwidth;\n}\n\n//float\tdrawpic(vector position, string pic, vector size, vector rgb, float alpha, float flag) = #456;\nvoid QCBUILTIN PF_CL_drawpic (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tfloat *pos = G_VECTOR(OFS_PARM0);\n\tconst char *picname = PR_GetStringOfs(prinst, OFS_PARM1);\n\tfloat *size = G_VECTOR(OFS_PARM2);\n\tfloat *rgb = G_VECTOR(OFS_PARM3);\n\tfloat alpha = G_FLOAT(OFS_PARM4);\n\tint flag = prinst->callargc >= 6?(int)G_FLOAT(OFS_PARM5):0;\n\n\tmpic_t *p;\n\n\tp = R2D_SafeCachePic(picname);\n\tif (!p || !R_GetShaderSizes(p, NULL, NULL, false))\n\t\tp = R2D_SafePicFromWad(picname);\n\n\tif (!p)\n\t{\n\t\tif (!CL_IsDownloading(picname))\n\t\t\tp = R2D_SafeCachePic(\"no_texture\");\n\t\tG_FLOAT(OFS_RETURN) = 0;\n\t}\n\telse\n\t\tG_FLOAT(OFS_RETURN) = 1;\n\n\tr2d_be_flags = PF_SelectDPDrawFlag(prinst, flag);\n\tR2D_ImageColours(rgb[0], rgb[1], rgb[2], alpha);\n\tif ((size[0] < 0) ^ (size[1] < 0))\n\t\tR2D_Image(pos[0]+size[0], pos[1]+size[1], -size[0], -size[1], 1, 1, 0, 0, p);\n\telse\n\t\tR2D_Image(pos[0], pos[1], size[0], size[1], 0, 0, 1, 1, p);\n\tr2d_be_flags = 0;\n}\n\nvoid QCBUILTIN PF_CL_drawrotpic (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tfloat *pivot = G_VECTOR(OFS_PARM0);\n\tfloat *mins = G_VECTOR(OFS_PARM1);\n\tfloat *maxs = G_VECTOR(OFS_PARM2);\n\tconst char *picname = PR_GetStringOfs(prinst, OFS_PARM3);\n\tfloat *rgb = G_VECTOR(OFS_PARM4);\n\tfloat alpha = G_FLOAT(OFS_PARM5);\n\tfloat angle = (G_FLOAT(OFS_PARM6) * M_PI)/180;\n\tint flag = prinst->callargc >= 8?(int) G_FLOAT(OFS_PARM7):0;\n\n\tvec2_t points[4];\n\tvec2_t tcoords[4];\n\tvec2_t saxis;\n\tvec2_t taxis;\n\n\tmpic_t *p;\n\n\tp = R2D_SafeCachePic(picname);\n\tif (!p)\n\t\tp = R2D_SafePicFromWad(picname);\n\n\tsaxis[0] = cos(angle);\n\tsaxis[1] = sin(angle);\n\ttaxis[0] = -sin(angle);\n\ttaxis[1] = cos(angle);\n\n\tVector2MA(pivot, mins[0], saxis, points[0]); Vector2MA(points[0], mins[1], taxis, points[0]);\n\tVector2MA(pivot, maxs[0], saxis, points[1]); Vector2MA(points[1], mins[1], taxis, points[1]);\n\tVector2MA(pivot, maxs[0], saxis, points[2]); Vector2MA(points[2], maxs[1], taxis, points[2]);\n\tVector2MA(pivot, mins[0], saxis, points[3]); Vector2MA(points[3], maxs[1], taxis, points[3]);\n\n\tVector2Set(tcoords[0], 0, 0);\n\tVector2Set(tcoords[1], 1, 0);\n\tVector2Set(tcoords[2], 1, 1);\n\tVector2Set(tcoords[3], 0, 1);\n\n\tr2d_be_flags = PF_SelectDPDrawFlag(prinst, flag);\n\tR2D_ImageColours(rgb[0], rgb[1], rgb[2], alpha);\n\tR2D_Image2dQuad((const vec2_t*)points, (const vec2_t*)tcoords, NULL, p);\n\tr2d_be_flags = 0;\n\n\tG_FLOAT(OFS_RETURN) = 1;\n}\n\nvoid QCBUILTIN PF_CL_drawsubpic (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tfloat *pos = G_VECTOR(OFS_PARM0);\n\tfloat *size = G_VECTOR(OFS_PARM1);\n\tconst char *picname = PR_GetStringOfs(prinst, OFS_PARM2);\n\tfloat *srcPos = G_VECTOR(OFS_PARM3);\n\tfloat *srcSize = G_VECTOR(OFS_PARM4);\n\tfloat *rgb = G_VECTOR(OFS_PARM5);\n\tfloat alpha = G_FLOAT(OFS_PARM6);\n\tint flag = prinst->callargc >= 8?(int) G_FLOAT(OFS_PARM7):0;\n\n\tmpic_t *p;\n\n\tp = R2D_SafeCachePic(picname);\n\tif (!p || !R_GetShaderSizes(p, NULL, NULL, false))\n\t\tp = R2D_SafePicFromWad(picname);\n\n\tr2d_be_flags = PF_SelectDPDrawFlag(prinst, flag);\n\tR2D_ImageColours(rgb[0], rgb[1], rgb[2], alpha);\n\tif ((size[0] < 0) ^ (size[1] < 0))\n\t\tR2D_Image(pos[0]+size[0], pos[1]+size[1], -size[0], -size[1], srcPos[0]+srcSize[0], srcPos[1]+srcSize[1], srcPos[0], srcPos[1], p);\n\telse\n\t\tR2D_Image(pos[0], pos[1], size[0], size[1], srcPos[0], srcPos[1], srcPos[0]+srcSize[0], srcPos[1]+srcSize[1], p);\n\tr2d_be_flags = 0;\n\n\tG_FLOAT(OFS_RETURN) = 1;\n}\nvoid QCBUILTIN PF_CL_drawrotsubpic (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tfloat *pivot = G_VECTOR(OFS_PARM0);\n\tfloat *mins = G_VECTOR(OFS_PARM1);\n\tfloat *maxs = G_VECTOR(OFS_PARM2);\n\tconst char *picname = PR_GetStringOfs(prinst, OFS_PARM3);\n\tfloat *srcPos = G_VECTOR(OFS_PARM4);\n\tfloat *srcSize = G_VECTOR(OFS_PARM5);\n\tfloat *rgb = G_VECTOR(OFS_PARM6);\n\tfloat alpha = G_FLOAT(OFS_PARM7+0);\n\tfloat angle = (G_FLOAT(OFS_PARM7+1) * M_PI) / 180;\n\tint flag = prinst->callargc >= 8?(int) G_FLOAT(OFS_PARM7+2):0;\n\tvec2_t points[4], tcoords[4];\n\tvec2_t saxis;\n\tvec2_t taxis;\n\n\tmpic_t *p;\n\n\tsaxis[0] = cos(angle);\n\tsaxis[1] = sin(angle);\n\ttaxis[0] = -sin(angle);\n\ttaxis[1] = cos(angle);\n\n\tp = R2D_SafeCachePic(picname);\n\tif (!p)\n\t\tp = R2D_SafePicFromWad(picname);\n\n\tVector2MA(pivot, mins[0], saxis, points[0]); Vector2MA(points[0], mins[1], taxis, points[0]);\n\tVector2MA(pivot, maxs[0], saxis, points[1]); Vector2MA(points[1], mins[1], taxis, points[1]);\n\tVector2MA(pivot, maxs[0], saxis, points[2]); Vector2MA(points[2], maxs[1], taxis, points[2]);\n\tVector2MA(pivot, mins[0], saxis, points[3]); Vector2MA(points[3], maxs[1], taxis, points[3]);\n\n\tVector2Set(tcoords[0], srcPos[0]\t\t\t, srcPos[1]\t\t\t\t);\n\tVector2Set(tcoords[1], srcPos[0]+srcSize[0]\t, srcPos[1]\t\t\t\t);\n\tVector2Set(tcoords[2], srcPos[0]+srcSize[0]\t, srcPos[1]+srcSize[1]\t);\n\tVector2Set(tcoords[3], srcPos[0]\t\t\t, srcPos[1]+srcSize[1]\t);\n\n\tr2d_be_flags = PF_SelectDPDrawFlag(prinst, flag);\n\tR2D_ImageColours(rgb[0], rgb[1], rgb[2], alpha);\n\tR2D_Image2dQuad((const vec2_t*)points, (const vec2_t*)tcoords, NULL, p);\n\tr2d_be_flags = 0;\n\n\tG_FLOAT(OFS_RETURN) = 1;\n}\n\n#ifdef HAVE_LEGACY\n/*fuck sake, why does no one give a shit about existing extension?!? seriously this stuff is pissing me off*/\nvoid QCBUILTIN PF_CL_drawrotpic_dp (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tfloat *pivot = G_VECTOR(OFS_PARM0);\n\tconst char *picname = PR_GetStringOfs(prinst, OFS_PARM1);\n\tfloat *size = G_VECTOR(OFS_PARM2);\n\tfloat *mins = G_VECTOR(OFS_PARM3);\n\tfloat angle = (G_FLOAT(OFS_PARM4) * M_PI)/180;\n\tfloat *rgb = G_VECTOR(OFS_PARM5);\n\tfloat alpha = G_FLOAT(OFS_PARM6);\n\tint flag = prinst->callargc >= 8?(int) G_FLOAT(OFS_PARM7):0;\n\n\tvec3_t maxs;\n\n\tvec2_t points[4];\n\tvec2_t tcoords[4];\n\tvec2_t saxis;\n\tvec2_t taxis;\n\n\tmpic_t *p;\n\n\tVectorSubtract(size, mins, maxs);\n\n\tp = R2D_SafeCachePic(picname);\n\tif (!p)\n\t\tp = R2D_SafePicFromWad(picname);\n\n\tsaxis[0] = cos(angle);\n\tsaxis[1] = sin(angle);\n\ttaxis[0] = -sin(angle);\n\ttaxis[1] = cos(angle);\n\n\tVector2MA(pivot, mins[0], saxis, points[0]); Vector2MA(points[0], mins[1], taxis, points[0]);\n\tVector2MA(pivot, maxs[0], saxis, points[1]); Vector2MA(points[1], mins[1], taxis, points[1]);\n\tVector2MA(pivot, maxs[0], saxis, points[2]); Vector2MA(points[2], maxs[1], taxis, points[2]);\n\tVector2MA(pivot, mins[0], saxis, points[3]); Vector2MA(points[3], maxs[1], taxis, points[3]);\n\n\tVector2Set(tcoords[0], 0, 0);\n\tVector2Set(tcoords[1], 1, 0);\n\tVector2Set(tcoords[2], 1, 1);\n\tVector2Set(tcoords[3], 0, 1);\n\n\tr2d_be_flags = PF_SelectDPDrawFlag(prinst, flag);\n\tR2D_ImageColours(rgb[0], rgb[1], rgb[2], alpha);\n\tR2D_Image2dQuad((const vec2_t*)points, (const vec2_t*)tcoords, NULL, p);\n\tr2d_be_flags = 0;\n\n\tG_FLOAT(OFS_RETURN) = 1;\n}\n#endif\n\n\nvoid QCBUILTIN PF_CL_is_cached_pic (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char\t*str;\n\tstr = PR_GetStringOfs(prinst, OFS_PARM0);\n\tG_FLOAT(OFS_RETURN) = !!R_RegisterCustom(NULL, str, SUF_2D, NULL, NULL);\n}\n\nvoid QCBUILTIN PF_CL_precache_pic (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\textern cvar_t pr_precachepic_slow;\n\tconst char\t*str;\n\tmpic_t\t*pic;\n\tunsigned int flags;\n#define PIC_FROMWAD 1\t\t\t\t/*obsolete, probably better to just use gfx/foo.lmp instead*/\n//#define PIC_TEMPORARY 2\t\t\t/*DP, not meaningful here*/\n//#define PIC_NOCLAMP 4\t\t\t\t/*DP, not useful here - use a shader instead*/\n//#define PIC_MIPMAP 8\t\t\t\t/*DP, not useful here - use a shader instead*/\n#define PRECACHE_PIC_DOWNLOAD 256\t/*block until loaded, downloading if missing*/\n#define PRECACHE_PIC_TEST 512\t\t/*block until loaded*/\n\n\tG_INT(OFS_RETURN) = G_INT(OFS_PARM0);\n\tstr = PR_GetStringOfs(prinst, OFS_PARM0);\n\tif (prinst->callargc > 1)\n\t\tflags = G_FLOAT(OFS_PARM1);\n\telse\n\t\tflags = 0;\n\n\tif (pr_precachepic_slow.ival)\n\t\tflags |= PRECACHE_PIC_DOWNLOAD|PRECACHE_PIC_TEST;\n\n\tif (flags & PIC_FROMWAD)\n\t\tpic = R2D_SafePicFromWad(str);\n\telse\n\t{\n\t\tpic = R2D_SafeCachePic(str);\n\n\t\tif ((flags & PRECACHE_PIC_DOWNLOAD) && cls.state\t//if we're allowed to download it...\n\t\t\t && strchr(str, '.')\t//only try to download it if it looks as though it contains a path.\n#ifndef CLIENTONLY\n\t\t\t&& !sv.active\t\t\t//not if we're already the server...\n#endif\n\t\t\t&& (!pic || !R_GetShaderSizes(pic, NULL, NULL, true)))\t//and it wasn't loaded...\n\t\t\tCL_CheckOrEnqueDownloadFile(str, str, 0);\n\t}\n\n\tif (flags & PRECACHE_PIC_TEST)\n\t\tif (!pic || !R_GetShaderSizes(pic, NULL, NULL, true))\n\t\t\tG_INT(OFS_RETURN) = 0;\n}\n\n#ifdef CSQC_DAT\n//warning: not threaded.\nvoid QCBUILTIN PF_CL_uploadimage (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char *imagename = PR_GetStringOfs(prinst, OFS_PARM0);\n\tint width = G_INT(OFS_PARM1);\n\tint height = G_INT(OFS_PARM2);\n\tint src = G_INT(OFS_PARM3);\t//ptr\n\tint size = (prinst->callargc > 4)?G_INT(OFS_PARM4):(width * height * 4);\n\tuploadfmt_t format = (prinst->callargc > 5)?PR_TranslateTextureFormat(G_INT(OFS_PARM5)):TF_RGBA32;\n\tvoid *imgptr;\n\ttexid_t tid;\n\n\tG_INT(OFS_RETURN) = 0;\t//assume the worst\n\n\tif (width < 0 || height < 0 || width > 16384 || height > 16384)\n\t{\t//this is actually kinda likely when everyone assumes everything is a float.\n\t\tPR_BIError(prinst, \"PF_CL_uploadimage: dimensions are out of range\\n\");\n\t\treturn;\n\t}\n\t//FIXME: this should use a proper qclib function to validate more reliably / reusably\n\tif (src <= 0 || src+size >= prinst->stringtablesize)\n\t{\n\t\tPR_BIError(prinst, \"PF_CL_uploadimage: invalid source\\n\");\n\t\treturn;\n\t}\n\timgptr = prinst->stringtable + src;\n\n\n\ttid = Image_FindTexture(imagename, NULL, RT_IMAGEFLAGS);\n\tif (!TEXVALID(tid))\n\t\ttid = Image_CreateTexture(imagename, NULL, RT_IMAGEFLAGS);\n\n\tif (!format)\n\t{\n\t\tvoid *data = BZ_Malloc(size);\n\t\tmemcpy(data, imgptr, size);\n\t\tG_INT(OFS_RETURN) = Image_LoadTextureFromMemory(tid, tid->flags, tid->ident, imagename, data, size);\n\t}\n\telse\n\t{\n\t\tvoid *palette = NULL;\n\t\tunsigned int blockbytes, blockwidth, blockheight, blockdepth;\n\t\t//get format info\n\t\tImage_BlockSizeForEncoding(format, &blockbytes, &blockwidth, &blockheight, &blockdepth);\n\t\t//round up as appropriate\n\t\tblockwidth = ((width+blockwidth-1)/blockwidth)*blockwidth;\n\t\tblockheight = ((height+blockheight-1)/blockheight)*blockheight;\n\n\t\t//we do allow palettes on the end.\n\t\tif (format == TF_8PAL24)\n\t\t\tsize -= 768, palette = (qbyte*)imgptr+size, blockbytes=1;\n\t\telse if (format == TF_8PAL32)\n\t\t\tsize -= 1024, palette = (qbyte*)imgptr+size, blockbytes=1;\n\n\t\tif (size != blockwidth*blockheight*blockbytes)\n\t\t\tG_INT(OFS_RETURN) = 0;\t//size isn't right. which means the pointer might be invalid too.\n\t\telse\n\t\t{\n\t\t\tImage_Upload(tid, format, imgptr, palette, width, height, 1, RT_IMAGEFLAGS);\n\t\t\ttid->width = width;\n\t\t\ttid->height = height;\n\t\t\tG_INT(OFS_RETURN) = 1;\n\t\t}\n\t}\n}\n#endif\n\n//warning: not threadable. hopefully noone abuses it.\nvoid QCBUILTIN PF_CL_readimage (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tsize_t filesize;\n\tconst char *filename = PR_GetStringOfs(prinst, OFS_PARM0);\n\n\tint imagewidth, imageheight;\n\tuploadfmt_t format;\n\tvoid *filedata;\n\n\tG_INT(OFS_RETURN) = 0;\t//assume the worst\n\tG_INT(OFS_PARM1) = 0;\t//out width\n\tG_INT(OFS_PARM2) = 0;\t//out height\n\tG_INT(OFS_PARM3) = 0;\t//out format\n\n\tfiledata = FS_LoadMallocFile(filename, &filesize);\n\n\tif (filedata)\n\t{\n\t\tqbyte *imagedata = ReadRawImageFile(filedata, filesize, &imagewidth, &imageheight, &format, true, filename);\n\t\tZ_Free(filedata);\n\n\t\tif (imagedata)\n\t\t{\n\t\t\tvoid *ptr = prinst->AddressableAlloc(prinst, imagewidth*imageheight*4);\n\t\t\tif (ptr)\n\t\t\t{\n\t\t\t\tmemcpy(ptr, imagedata, imagewidth*imageheight*4);\n\t\t\t\tG_INT(OFS_RETURN) = (char*)ptr - prinst->stringtable;\n\t\t\t\tG_INT(OFS_PARM1) = imagewidth;\t//out width\n\t\t\t\tG_INT(OFS_PARM2) = imageheight;\t//out height\n\t\t\t\tG_INT(OFS_PARM3) = PR_UnTranslateTextureFormat(format);\t//out format...\n\t\t\t}\n\t\t\tBZ_Free(imagedata);\n\t\t}\n\t}\n}\n\nvoid QCBUILTIN PF_CL_free_pic (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\t//we don't support this, as the shader could be used elsewhere also, and we have pointers to things.\n/*\n\tchar\t*str;\n\tstr = PR_GetStringOfs(prinst, OFS_PARM0);\n\tR_UnloadShader(R_RegisterCustom(str, NULL, NULL));\n*/\n}\n\n//float\tdrawcharacter(vector position, float character, vector scale, vector rgb, float alpha, float flag) = #454;\nvoid QCBUILTIN PF_CL_drawcharacter (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tfloat *pos = G_VECTOR(OFS_PARM0);\n\tint chara = G_FLOAT(OFS_PARM1);\n\tfloat *size = G_VECTOR(OFS_PARM2);\n\tfloat *rgb = G_VECTOR(OFS_PARM3);\n\tfloat alpha = G_FLOAT(OFS_PARM4);\n\tint flag = prinst->callargc >= 6?G_FLOAT(OFS_PARM5):0;\n\n\tfloat x, y;\n\n\tif (!chara)\n\t{\n\t\tG_FLOAT(OFS_RETURN) = -1;\t//was null..\n\t\treturn;\n\t}\n\n\t//no control chars. use quake ones if so\n\tif (!(flag & 4) && !com_parseutf8.ival)\n\t{\n\t\t//ugly quake chars...\n\t\tif (chara >= 32 && chara < 128)\n\t\t\t;\t//ascii-comptaible range\n\t\telse\n\t\t\tchara |= 0xe000;\t//use quake glyphs (including for red text, unfortunately)\n\t}\n\n\tr2d_be_flags = PF_SelectDPDrawFlag(prinst, flag);\n\tPR_CL_BeginString(prinst, pos[0], pos[1], size[0], size[1], &x, &y);\n\tR2D_ImageColours(rgb[0], rgb[1], rgb[2], alpha);\n\tFont_DrawScaleChar(x, y, CON_WHITEMASK, chara);\n\tR2D_ImageColours(1,1,1,1);\n\tFont_EndString(NULL);\n\tr2d_be_flags = 0;\n\n\tG_FLOAT(OFS_RETURN) = 1;\n}\n\n//float\tdrawrawstring(vector position, string text, vector scale, vector rgb, float alpha, float flag) = #455;\nvoid QCBUILTIN PF_CL_drawrawstring (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\t\n\tfloat *pos = G_VECTOR(OFS_PARM0);\n\tconst char *text = PR_GetStringOfs(prinst, OFS_PARM1);\n\tfloat *size = G_VECTOR(OFS_PARM2);\n\tfloat *rgb = G_VECTOR(OFS_PARM3);\n\tfloat alpha = G_FLOAT(OFS_PARM4);\n\tint flag = prinst->callargc >= 6?G_FLOAT(OFS_PARM5):0;\n\tfloat x, y;\n\tunsigned int c;\n\tint error;\n\n\tif (!text)\n\t{\n\t\tG_FLOAT(OFS_RETURN) = -1;\t//was null..\n\t\treturn;\n\t}\n\n\tr2d_be_flags = PF_SelectDPDrawFlag(prinst, flag);\n\tPR_CL_BeginString(prinst, pos[0], pos[1], size[0], size[1], &x, &y);\n\tR2D_ImageColours(rgb[0], rgb[1], rgb[2], alpha);\n\n\twhile(*text)\n\t{\n\t\tif (1)//VMUTF8)\n\t\t\tc = unicode_decode(&error, text, &text, false);\n\t\telse\n\t\t{\n\t\t\t//FIXME: which charset is this meant to be using?\n\t\t\t//quakes? 8859-1? utf8? some weird hacky mixture?\n\t\t\tc = *text++&0xff;\n\t\t\tif ((c&0x7f) < 32)\n\t\t\t\tc |= 0xe000;\t//if its a control char, just use the quake range instead.\n\t\t\telse if (c & 0x80)\n\t\t\t\tc |= 0xe000;\t//if its a high char, just use the quake range instead. we could colour it, but why bother\n\t\t}\n\t\tx = Font_DrawScaleChar(x, y, CON_WHITEMASK, c);\n\t}\n\tR2D_ImageColours(1,1,1,1);\n\tFont_EndString(NULL);\n\tr2d_be_flags = 0;\n}\n\n//void (float width, vector pos1, vector pos2, vector rgb, float alpha, optional float flags) drawline;\nvoid QCBUILTIN PF_CL_drawline (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\t//float width = G_FLOAT(OFS_PARM0);\n\tfloat *point1\t= G_VECTOR(OFS_PARM1);\n\tfloat *point2\t= G_VECTOR(OFS_PARM2);\n\tfloat *rgb\t= G_VECTOR(OFS_PARM3);\n\tfloat alpha = G_FLOAT(OFS_PARM4);\n\tint flags\t= prinst->callargc >= 6?G_FLOAT(OFS_PARM5):0;\n\tshader_t *shader_draw_line;\n\n\t//this shader lookup might get pricy.\n\tshader_draw_line = R_RegisterShader(\"shader_draw_line\", SUF_NONE,\n\t\t\"{\\n\"\n\t\t\t\"program defaultfill\\n\"\n\t\t\t\"{\\n\"\n\t\t\t\t\"map $whiteimage\\n\"\n\t\t\t\t\"rgbgen exactvertex\\n\"\n\t\t\t\t\"alphagen vertex\\n\"\n\t\t\t\t\"blendfunc blend\\n\"\n\t\t\t\"}\\n\"\n\t\t\"}\\n\");\n\n\tr2d_be_flags = PF_SelectDPDrawFlag(prinst, flags);\n\tR2D_ImageColours(rgb[0], rgb[1], rgb[2], alpha);\n\tR2D_Line(point1[0], point1[1], point2[0], point2[1], shader_draw_line);\n\tR2D_ImageColours(1,1,1,1);\n\tr2d_be_flags = 0;\n}\n\n//vector  drawgetimagesize(string pic) = #460;\nvoid QCBUILTIN PF_CL_drawgetimagesize (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char *picname = PR_GetStringOfs(prinst, OFS_PARM0);\n\tmpic_t *p = R2D_SafeCachePic(picname);\n\n\tfloat *ret = G_VECTOR(OFS_RETURN);\n\tint iw, ih;\n\t\n\tif (R_GetShaderSizes(p, &iw, &ih, true) > 0)\n\t{\n\t\tret[0] = iw;\n\t\tret[1] = ih;\n\t\tret[2] = 0;\n\t}\n\telse\n\t{\n\t\tret[0] = 0;\n\t\tret[1] = 0;\n\t\tret[2] = 0;\n\t}\n}\n\n//vector\tgetmousepos(void)  \t= #66;\nvoid QCBUILTIN PF_cl_getmousepos (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tfloat *ret = G_VECTOR(OFS_RETURN);\n\tworld_t *world = prinst->parms->user;\n\tunsigned int target = world->keydestmask;\n\n\tif (key_dest_absolutemouse & target)\n\t{\n\t\tret[0] = mousecursor_x;\n\t\tret[1] = mousecursor_y;\n\t}\n\telse\n\t{\n\t\tret[0] = mousemove_x;\n\t\tret[1] = mousemove_y;\n\t}\n\n\tmousemove_x=0;\n\tmousemove_y=0;\n\n//\textern int mousecursor_x, mousecursor_y;\n//\tret[0] = mousecursor_x;\n//\tret[1] = mousecursor_y;\n\tret[2] = 0;\n}\n\n\nvoid QCBUILTIN PF_SubConGetSet (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char *conname = PR_GetStringOfs(prinst, OFS_PARM0);\n\tconst char *field = PR_GetStringOfs(prinst, OFS_PARM1);\n\tconst char *value = (prinst->callargc>2)?PR_GetStringOfs(prinst, OFS_PARM2):NULL;\n\tconsole_t *con = Con_FindConsole(conname);\n\tG_INT(OFS_RETURN) = 0;\n\tif (!con)\n\t{\n\t\t//null if it doesn't exist\n\t\treturn;\n\t}\n\tif (!strcmp(field, \"title\"))\n\t{\n\t\tRETURN_TSTRING(con->title);\n\t\tif (value)\n\t\t\tQ_strncpyz(con->title, value, sizeof(con->title));\n\t}\n\telse if (!strcmp(field, \"name\"))\n\t{\n\t\tRETURN_TSTRING(con->name);\n\t\tif (value && *value && *con->name)\n\t\t\tQ_strncpyz(con->name, value, sizeof(con->name));\n\t}\n\telse if (!strcmp(field, \"next\"))\n\t{\n\t\tcon = con->next;\n\t\tif (con)\n\t\t\tRETURN_TSTRING(con->name);\n\t}\n\telse if (!strcmp(field, \"unseen\"))\n\t{\n\t\tRETURN_TSTRING(va(\"%i\", con->unseentext));\n\t\tif (value)\n\t\t\tcon->unseentext = atoi(value);\n\t}\n\telse if (!strcmp(field, \"markup\"))\n\t{\n\t\tint cur;\n\t\tif (con->parseflags & PFS_NOMARKUP)\n\t\t\tcur = 0;\n\t\telse if (con->parseflags & PFS_KEEPMARKUP)\n\t\t\tcur = 2;\n\t\telse\n\t\t\tcur = 1;\n\t\tRETURN_TSTRING(va(\"%i\", cur));\n\t\tif (value)\n\t\t{\n\t\t\tcur = atoi(value);\n\t\t\tcon->parseflags &= ~(PFS_NOMARKUP|PFS_KEEPMARKUP);\n\t\t\tif (cur == 0)\n\t\t\t\tcon->parseflags |= PFS_NOMARKUP;\n\t\t\telse if (cur == 2)\n\t\t\t\tcon->parseflags |= PFS_KEEPMARKUP;\n\t\t}\n\t}\n\telse if (!strcmp(field, \"forceutf8\"))\n\t{\n\t\tRETURN_TSTRING((con->parseflags&PFS_FORCEUTF8)?\"1\":\"0\");\n\t\tif (value)\n\t\t{\n\t\t\tcon->parseflags &= ~PFS_FORCEUTF8;\n\t\t\tif (atoi(value))\n\t\t\t\tcon->parseflags |= PFS_FORCEUTF8;\n\t\t}\n\t}\n\telse if (!strcmp(field, \"close\"))\n\t{\n\t\tRETURN_TSTRING(\"0\");\t//meant to return the old state...\n\t\tif (value && atoi(value))\n\t\t{\n\t\t\tif (con->close && atoi(value) != 2 && !con->close(con, true))\n\t\t\t\treturn;\n\t\t\tCon_Destroy(con);\n\t\t}\n\t}\n\telse if (!strcmp(field, \"clear\"))\n\t{\n\t\tRETURN_TSTRING(con->linecount?\"0\":\"1\");\n\t\tif (value && atoi(value))\n\t\t\tCon_ClearCon(con);\n\t}\n\telse if (!strcmp(field, \"hidden\"))\n\t{\n\t\tRETURN_TSTRING((con->flags & CONF_HIDDEN)?\"1\":\"0\");\n\t\tif (value)\n\t\t\tcon->flags = (con->flags & ~CONF_HIDDEN) | (atoi(value)?CONF_HIDDEN:0);\n\t}\n\telse if (!strcmp(field, \"linecount\"))\n\t{\n\t\tRETURN_TSTRING(va(\"%i\", con->linecount));\n\t\tif (value)\n\t\t\tcon->unseentext = atoi(value);\n\t}\n\telse if (!strcmp(field, \"backimage\"))\n\t{\n\t\tRETURN_TSTRING(con->backshader?con->backshader->name:con->backimage);\n\t\tif (value)\n\t\t{\n\t\t\tQ_strncpyz(con->backimage, value, sizeof(con->backimage));\n\t\t\tif (con->backshader)\n\t\t\t\tR_UnloadShader(con->backshader);\n\t\t}\n\t}\n\telse if (!strcmp(field, \"backvideomap\"))\n\t{\n\t\tRETURN_TSTRING(con->backshader?con->backshader->name:con->backimage);\n\t\tif (value)\n\t\t{\n\t\t\tQ_strncpyz(con->backimage, \"\", sizeof(con->backimage));\n\t\t\tif (con->backshader)\n\t\t\t\tR_UnloadShader(con->backshader);\n\t\t\tcon->backshader = R_RegisterCustom(NULL, va(\"consolevid_%s\", con->name), SUF_NONE, Shader_DefaultCinematic, value);\n\t\t}\n\t}\n}\nvoid QCBUILTIN PF_SubConPrintf (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tchar outbuf[4096];\n\tconst char *conname = PR_GetStringOfs(prinst, OFS_PARM0);\n\tconst char *fmt = PR_GetStringOfs(prinst, OFS_PARM1);\n\tconsole_t *con = Con_FindConsole(conname);\n\tif (!con)\n\t{\n\t\tcon = Con_Create(conname, 0);\n\t\tif (!con)\n\t\t\treturn;\n\t}\n\tPF_sprintf_internal(prinst, pr_globals, fmt, 2, outbuf, sizeof(outbuf));\n\tCon_PrintCon(con, outbuf, con->parseflags);\n}\nvoid QCBUILTIN PF_SubConDraw (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char *conname = PR_GetStringOfs(prinst, OFS_PARM0);\n\tfloat *pos = G_VECTOR(OFS_PARM1);\n\tfloat *size = G_VECTOR(OFS_PARM2);\n\tfloat fontsize = G_FLOAT(OFS_PARM3);\n\tconsole_t *con = Con_FindConsole(conname);\n\tworld_t *world = prinst->parms->user;\n\tif (!con)\n\t\treturn;\n\n\tif (world->g.drawfontscale)\n\t{\n//\t\tszx *= world->g.drawfontscale[0];\n\t\tfontsize *= world->g.drawfontscale[1];\n\t}\n\n\tCon_DrawOneConsole(con, con->flags & CONF_KEYFOCUSED, PR_CL_ChooseFont(world->g.drawfont, fontsize, fontsize), pos[0], pos[1], size[0], size[1], 0);\n}\nvoid QCBUILTIN PF_SubConInput (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char *conname = PR_GetStringOfs(prinst, OFS_PARM0);\n\tint ie = G_FLOAT(OFS_PARM1);\n\tfloat pa = G_FLOAT(OFS_PARM2);\n\tfloat pb = G_FLOAT(OFS_PARM3);\n//\tfloat pc = G_FLOAT(OFS_PARM4);\n\tconsole_t *con = Con_FindConsole(conname);\n\tG_FLOAT(OFS_RETURN) = 0;\n\tif (!con)\n\t\treturn;\n\tswitch(ie)\n\t{\n\tcase CSIE_KEYDOWN:\n\t\t//scan, char\n\t\tif ((pa && qcinput_scan != pa) || (pb && pb != qcinput_unicode))\n\t\t\tG_FLOAT(OFS_RETURN) = 0;\n\t\telse\n\t\t\tG_FLOAT(OFS_RETURN) = Key_Console(con, MP_TranslateQCtoFTECodes(pa), pb);\n\t\tbreak;\n\tcase CSIE_KEYUP:\n\t\t//scan, char\n\t\tKey_ConsoleRelease(con, MP_TranslateQCtoFTECodes(pa), pb);\n\t\tG_FLOAT(OFS_RETURN) = 0;\t//does not inhibit\n\t\tbreak;\n\tcase CSIE_MOUSEABS:\n\t\t//x, y\n\t\tif (con == con_current && (key_dest_mask & kdm_console))\n\t\t\tbreak;\t//no interfering with the main console!\n\t\tcon->mousecursor[0] = pa;\n\t\tcon->mousecursor[1] = pb;\n\t\tG_FLOAT(OFS_RETURN) = true;\n\t\tbreak;\n\tcase CSIE_FOCUS:\n\t\t//mouse, key\n\t\tif (pb >= 0)\n\t\t{\n\t\t\tcon->flags = (con->flags & ~CONF_KEYFOCUSED) | (pb?CONF_KEYFOCUSED:0);\n\t\t\tG_FLOAT(OFS_RETURN) = true;\n\t\t}\n\t\tbreak;\n\t}\n}\n#endif\n\n\n\n\n\n\n\n#ifdef MENU_DAT\n\ntypedef struct menuedict_s\n{\n\tenum ereftype_e\tereftype;\n\tfloat\t\t\tfreetime; // sv.time when the object was freed\n\tint\t\t\t\tentnum;\n\tunsigned int\tfieldsize;\n\tpbool\t\t\treadonly;\t//world\n\n\tvoid\t\t\t*fields;\n} menuedict_t;\n\n\n\nstatic struct\n{\n\tevalc_t chain;\n\tevalc_t model;\n\tevalc_t modelindex;\n\tevalc_t mins;\n\tevalc_t maxs;\n\tevalc_t origin;\n\tevalc_t angles;\n\tevalc_t skin;\n\tevalc_t colormap;\n\tevalc_t frame1;\n\tevalc_t frame2;\n\tevalc_t lerpfrac;\n\tevalc_t frame1time;\n\tevalc_t frame2time;\n\tevalc_t renderflags;\n\tevalc_t skinobject;\n\tevalc_t skelobject;\n\tevalc_t colourmod;\n\tevalc_t alpha;\n} menuc_eval;\nstatic playerview_t menuview;\n\n\nstatic menu_t menuqc;\t//this is how the client forwards events etc.\nstatic int inmenuprogs;\nstatic progparms_t menuprogparms;\nstatic menuedict_t *menu_edicts;\nstatic int num_menu_edicts;\nworld_t menu_world;\nstatic int menuentsize;\ndouble  menutime;\nstatic struct\n{\n\tfunc_t init;\n\tfunc_t shutdown;\n\tfunc_t draw;\tqboolean fuckeddrawsizes;\n\tfunc_t drawloading;\n\tfunc_t keydown;\n\tfunc_t keyup;\n\tfunc_t inputevent;\n\tfunc_t toggle;\n\tfunc_t consolecommand;\n\tfunc_t gethostcachecategory;\n\tfunc_t rendererrestarted;\n} mpfuncs;\njmp_buf mp_abort;\n\n\n// cvars\n#define MENUPROGSGROUP \"Menu progs control\"\ncvar_t forceqmenu = CVAR(\"forceqmenu\", \"0\");\ncvar_t pr_menu_coreonerror = CVAR(\"pr_menu_coreonerror\", \"1\");\ncvar_t pr_menu_memsize = CVAR(\"pr_menu_memsize\", \"64m\");\n\n\n//new generic functions.\n\nconst char *RemapCvarNameFromDPToFTE(const char *name)\n{\n\tif (!stricmp(name, \"vid_bitsperpixel\"))\n\t\treturn \"vid_bpp\";\n\tif (!stricmp(name, \"_cl_playermodel\"))\n\t\treturn \"model\";\n\tif (!stricmp(name, \"_cl_playerskin\"))\n\t\treturn \"skin\";\n\tif (!stricmp(name, \"_cl_color\"))\n\t\treturn \"topcolor\";\n\tif (!stricmp(name, \"_cl_name\"))\n\t\treturn \"name\";\n\n\tif (!stricmp(name, \"v_contrast\"))\n\t\treturn \"v_contrast\";\n\tif (!stricmp(name, \"v_hwgamma\"))\n\t\treturn \"vid_hardwaregamma\";\n\tif (!stricmp(name, \"showfps\"))\n\t\treturn \"show_fps\";\n\tif (!stricmp(name, \"sv_progs\"))\n\t\treturn \"progs\";\n\n\treturn name;\n}\n\nstatic void QCBUILTIN PF_menu_cvar (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tcvar_t\t*var;\n\tconst char\t*str;\n\n\tstr = PR_GetStringOfs(prinst, OFS_PARM0);\n\n\tif (!strcmp(str, \"vid_conwidth\"))\n\t\tG_FLOAT(OFS_RETURN) = vid.width;\n\telse if (!strcmp(str, \"vid_conheight\"))\n\t\tG_FLOAT(OFS_RETURN) = vid.height;\n\telse if (!strcmp(str, \"vid_pixwidth\"))\n\t\tG_FLOAT(OFS_RETURN) = vid.pixelwidth;\n\telse if (!strcmp(str, \"vid_pixheight\"))\n\t\tG_FLOAT(OFS_RETURN) = vid.pixelheight;\n\telse\n\t{\n\t\tstr = RemapCvarNameFromDPToFTE(str);\n\t\tvar = PF_Cvar_FindOrGet(str);\n\t\tif (var && !(var->flags & CVAR_NOUNSAFEEXPAND))\n\t\t{\n\t\t\t//menuqc sees desired settings, not latched settings.\n\t\t\tif (var->latched_string)\n\t\t\t\tG_FLOAT(OFS_RETURN) = atof(var->latched_string);\n\t\t\telse\n\t\t\t\tG_FLOAT(OFS_RETURN) = var->value;\n\t\t}\n\t\telse\n\t\t\tG_FLOAT(OFS_RETURN) = 0;\n\t}\n}\nstatic void QCBUILTIN PF_menu_cvar_set (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char\t*var_name, *val;\n\tcvar_t *var;\n\n\tvar_name = PR_GetStringOfs(prinst, OFS_PARM0);\n\tvar_name = RemapCvarNameFromDPToFTE(var_name);\n\tval = PR_GetStringOfs(prinst, OFS_PARM1);\n\n\tvar = PF_Cvar_FindOrGet(var_name);\n\tif (var && var->flags & CVAR_NOTFROMSERVER)\n\t{\n\t\t//fixme: menuqc needs some way to display a prompt to allow it anyway.\n\t\treturn;\n\t}\n\tCvar_Set (var, val);\n}\nstatic void QCBUILTIN PF_menu_cvar_string (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char\t*str = PR_GetStringOfs(prinst, OFS_PARM0);\n\tcvar_t *cv = PF_Cvar_FindOrGet(RemapCvarNameFromDPToFTE(str));\n\tif (!cv)\n\t\tG_INT(OFS_RETURN) = 0;\n\telse if (cv->flags & CVAR_NOUNSAFEEXPAND)\n\t\tG_INT(OFS_RETURN) = 0;\n\telse if (cv->latched_string)\n\t\tG_INT(OFS_RETURN) = (int)PR_TempString(prinst, cv->latched_string);\n\telse\n\t\tG_INT(OFS_RETURN) = (int)PR_TempString(prinst, cv->string);\n}\n\n\n\n\nvoid QCBUILTIN PF_nonfatalobjerror (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char\t*s;\n\tstruct edict_s\t*ed;\n\teval_t *selfp;\n\n\ts = PF_VarString(prinst, 0, pr_globals);\n\n\tPR_StackTrace(prinst, true);\n\n\tselfp = PR_FindGlobal(prinst, \"self\", PR_CURRENT, NULL);\n\tif (selfp && selfp->_int)\n\t{\n\t\ted = PROG_TO_EDICT(prinst, selfp->_int);\n\n\t\tPR_PrintEdict(prinst, ed);\n\n\n\t\tif (developer.value)\n\t\t{\t//enable tracing.\n\t\t\tPR_RunWarning(prinst, \"======OBJECT ERROR======\\n%s\\n\", s);\n\t\t\treturn;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tED_Free (prinst, ed);\n\t\t}\n\t}\n\n\tCon_Printf (\"======OBJECT ERROR======\\n%s\\n\", s);\n}\n\n\n\n\n\n\n//float\tisserver(void)  = #60;\nvoid QCBUILTIN PF_isserver (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n#ifdef CLIENTONLY\n\tG_FLOAT(OFS_RETURN) = false;\n#else\n\tif (sv.state == ss_dead)\n\t\tG_FLOAT(OFS_RETURN) = false;\n\telse if (sv.allocated_client_slots == 1)\n\t\tG_FLOAT(OFS_RETURN) = 0.5;\n\telse\n\t\tG_FLOAT(OFS_RETURN) = true;\n#endif\n}\nvoid QCBUILTIN PF_isdemo (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tG_FLOAT(OFS_RETURN) = !!cls.demoplayback;\n}\n\n//float\tclientstate(void)  = #62;\nvoid QCBUILTIN PF_clientstate (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\t//menuqc was originally implemented in DP, so these return values follow NQ norms.\n\tif (isDedicated)\t//unreachable\n\t\tG_FLOAT(OFS_RETURN) = 0/*nq ca_dedicated*/;\n\telse if (\tcls.state >= ca_connected\t//we're on a server\n\t\t\t||\tCL_TryingToConnect()\t\t//or we're trying to connect (avoids bugs with certain menuqc mods)\n#ifndef CLIENTONLY\n\t\t\t||\tsv.state>=ss_loading\n#endif\n\t\t\t\t)\t//or we're going to connect to ourselves once we get our act together\n\t\tG_FLOAT(OFS_RETURN) = 2/*nq ca_connected*/;\n\telse\n\t\tG_FLOAT(OFS_RETURN) = 1/*nq ca_disconnected*/;\n}\n\nstatic void QCBUILTIN PF_Ignore (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tG_INT(OFS_RETURN) = 0;\n}\n//too specific to the prinst's builtins.\nstatic void QCBUILTIN PF_Fixme (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint binum;\n\tchar fname[MAX_QPATH];\n\tif (!prinst->GetBuiltinCallInfo(prinst, &binum, fname, sizeof(fname)))\n\t{\n\t\tbinum = 0;\n\t\tstrcpy(fname, \"?unknown?\");\n\t}\n\n\tCon_Printf(\"\\n\");\n\tprinst->RunError(prinst, \"\\nBuiltin %i:%s not implemented.\\nMenu is not compatible.\", binum, fname);\n\tPR_BIError (prinst, \"bulitin not implemented\");\n}\nstatic void QCBUILTIN PF_checkbuiltin (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tfunc_t funcref = G_INT(OFS_PARM0);\n\tchar *funcname = NULL;\n\tint args;\n\tint builtinno;\n\tif (prinst->GetFunctionInfo(prinst, funcref, &args, NULL, &builtinno, funcname, sizeof(funcname)))\n\t{\t//qc defines the function at least. nothing weird there...\n\t\tif (builtinno > 0 && builtinno < prinst->parms->numglobalbuiltins)\n\t\t{\n\t\t\tif (!prinst->parms->globalbuiltins[builtinno] || prinst->parms->globalbuiltins[builtinno] == PF_Fixme || prinst->parms->globalbuiltins[builtinno] == PF_Ignore)\n\t\t\t\tG_FLOAT(OFS_RETURN) = false;\t//the builtin with that number isn't defined.\n\t\t\telse\n\t\t\t{\n\t\t\t\tG_FLOAT(OFS_RETURN) = true;\t\t//its defined, within the sane range, mapped, everything. all looks good.\n\t\t\t\t//we should probably go through the available builtins and validate that the qc's name matches what would be expected\n\t\t\t\t//this is really intended more for builtins defined as #0 though, in such cases, mismatched assumptions are impossible.\n\t\t\t}\n\t\t}\n\t\telse\n\t\t\tG_FLOAT(OFS_RETURN) = false;\t//not a valid builtin (#0 builtins get remapped according to the function name)\n\t}\n\telse\n\t{\t//not valid somehow.\n\t\tG_FLOAT(OFS_RETURN) = false;\n\t}\n}\n\n\n\nvoid QCBUILTIN PF_CL_precache_sound (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char\t*str;\n\n\tstr = PR_GetStringOfs(prinst, OFS_PARM0);\n\n\tif (S_PrecacheSound(str))\n\t\tG_INT(OFS_RETURN) = G_INT(OFS_PARM0);\n\telse\n\t\tG_INT(OFS_RETURN) = 0;\n}\n\n//void\tsetkeydest(float dest) \t= #601;\nvoid QCBUILTIN PF_cl_setkeydest (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\t//these arguments are stupid\n\tswitch((int)G_FLOAT(OFS_PARM0))\n\t{\n\tcase 0:\n\t\t// key_game\n\t\tif (Key_Dest_Has(kdm_menu))\n\t\t{\n\t\t\tMenu_Unlink(&menuqc, false);\n\t\t\tKey_Dest_Remove(kdm_menu);\n//\t\t\tKey_Dest_Remove(kdm_message);\n//\t\t\tif (cls.state == ca_disconnected)\n//\t\t\t\tKey_Dest_Add(kdm_console);\n\t\t}\n\t\tbreak;\n\tcase 2:\n\t\t// key_menu\n\t\tKey_Dest_Remove(kdm_message);\n\t\tif (!Key_Dest_Has(kdm_menu))\n\t\t\tKey_Dest_Remove(kdm_console);\n\t\tMenu_Push(&menuqc, false);\n\t\tbreak;\n\tcase 1:\n\t\t// key_message\n\t\t//Key_Dest_Remove(kdm_menu);\n\t\t//Key_Dest_Add(kdm_message);\n\t\t// break;\n\tdefault:\n\t\tPR_BIError (prinst, \"PF_setkeydest: wrong destination %i !\\n\",(int)G_FLOAT(OFS_PARM0));\n\t}\n}\n//float\tgetkeydest(void)\t= #602;\nvoid QCBUILTIN PF_cl_getkeydest (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tif (Key_Dest_Has(kdm_menu))\n\t\tG_FLOAT(OFS_RETURN) = 2;\n//\telse if (Key_Dest_Has(kdm_message))\n//\t\tG_FLOAT(OFS_RETURN) = 1;\n\telse\n\t\tG_FLOAT(OFS_RETURN) = 0;\n}\n\nstatic void QCBUILTIN PF_Remove_ (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tmenuedict_t *ed;\n\n\ted = (void*)G_EDICT(prinst, OFS_PARM0);\n\n\tif (ed->ereftype == ER_FREE)\n\t{\n\t\tCon_DPrintf(\"Tried removing free entity\\n\");\n\t\tPR_StackTrace(prinst, false);\n\t\treturn;\n\t}\n\n\tED_Free (prinst, (void*)ed);\n}\nstatic void QCBUILTIN PF_RemoveInstant (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tmenuedict_t *ed;\n\n\ted = (void*)G_EDICT(prinst, OFS_PARM0);\n\n\tif (ed->ereftype == ER_FREE)\n\t{\n\t\tCon_DPrintf(\"Tried removing free entity\\n\");\n\t\tPR_StackTrace(prinst, false);\n\t\treturn;\n\t}\n\n\tprinst->EntFree(prinst, (void*)ed, true);\n}\n\nstatic void QCBUILTIN PF_CopyEntity (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tmenuedict_t *in, *out;\n\n\tin = (menuedict_t*)G_EDICT(prinst, OFS_PARM0);\n\tout = (menuedict_t*)G_EDICT(prinst, OFS_PARM1);\n\n\tmemcpy(out->fields, in->fields, menuentsize);\n}\n\nvoid QCBUILTIN PF_menu_checkextension (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char *extname = PR_GetStringOfs(prinst, OFS_PARM0);\n\tint i;\n\tG_FLOAT(OFS_RETURN) = 0;\n\n\tfor (i = 0; i < QSG_Extensions_count; i++)\n\t{\n\t\tif (!QSG_Extensions[i].name)\n\t\t\tcontinue;\n\t\tif (!stricmp(extname, QSG_Extensions[i].name))\n\t\t{\n\t\t\tG_FLOAT(OFS_RETURN) = 1;\n\t\t\tbreak;\n\t\t}\n\t}\n}\n\nvoid QCBUILTIN PF_CL_precache_file (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tG_INT(OFS_RETURN) = G_INT(OFS_PARM0);\n}\n\n//entity\tfindchainstring(.string _field, string match) = #26;\nvoid QCBUILTIN PF_menu_findchain (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint i, f;\n\tconst char *s;\n\tstring_t t;\n\tmenuedict_t *ent, *chain;\t//note, all edicts share the common header, but don't use it's fields!\n\teval_t *val;\n\n\tchain = (menuedict_t *) *prinst->parms->edicts;\n\n\tf = G_INT(OFS_PARM0)+prinst->fieldadjust;\n\ts = PR_GetStringOfs(prinst, OFS_PARM1);\n\n\tfor (i = 1; i < *prinst->parms->num_edicts; i++)\n\t{\n\t\tent = (menuedict_t *)EDICT_NUM_PB(prinst, i);\n\t\tif (ent->ereftype == ER_FREE)\n\t\t\tcontinue;\n\t\tt = *(string_t *)&((float*)ent->fields)[f];\n\t\tif (!t)\n\t\t\tcontinue;\n\t\tif (strcmp(PR_GetString(prinst, t), s))\n\t\t\tcontinue;\n\n\t\tval = prinst->GetEdictFieldValue(prinst, (void*)ent, \"chain\", ev_entity, &menuc_eval.chain);\n\t\tif (val)\n\t\t\tval->edict = EDICT_TO_PROG(prinst, (void*)chain);\n\t\tchain = ent;\n\t}\n\n\tRETURN_EDICT(prinst, (void*)chain);\n}\n//entity\tfindchainfloat(.float _field, float match) = #27;\nvoid QCBUILTIN PF_menu_findchainfloat (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint i, f;\n\tfloat s;\n\tmenuedict_t\t*ent, *chain;\t//note, all edicts share the common header, but don't use it's fields!\n\teval_t *val;\n\n\tchain = (menuedict_t *) *prinst->parms->edicts;\n\n\tf = G_INT(OFS_PARM0)+prinst->fieldadjust;\n\ts = G_FLOAT(OFS_PARM1);\n\n\tfor (i = 1; i < *prinst->parms->num_edicts; i++)\n\t{\n\t\tent = (menuedict_t*)EDICT_NUM_PB(prinst, i);\n\t\tif (ent->ereftype == ER_FREE)\n\t\t\tcontinue;\n\t\tif (((float *)ent->fields)[f] != s)\n\t\t\tcontinue;\n\n\t\tval = prinst->GetEdictFieldValue(prinst, (void*)ent, \"chain\", ev_entity, &menuc_eval.chain);\n\t\tif (val)\n\t\t\tval->edict = EDICT_TO_PROG(prinst, (void*)chain);\n\t\tchain = ent;\n\t}\n\n\tRETURN_EDICT(prinst, (void*)chain);\n}\n//entity\tfindchainflags(.float _field, float match);\nvoid QCBUILTIN PF_menu_findchainflags (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint i, f;\n\tint s;\n\tmenuedict_t\t*ent, *chain;\t//note, all edicts share the common header, but don't use it's fields!\n\teval_t *val;\n\n\tchain = (menuedict_t *) *prinst->parms->edicts;\n\n\tf = G_INT(OFS_PARM0)+prinst->fieldadjust;\n\ts = G_FLOAT(OFS_PARM1);\n\n\tfor (i = 1; i < *prinst->parms->num_edicts; i++)\n\t{\n\t\tent = (menuedict_t*)EDICT_NUM_PB(prinst, i);\n\t\tif (ent->ereftype == ER_FREE)\n\t\t\tcontinue;\n\t\tif ((int)((float *)ent->fields)[f] & s)\n\t\t\tcontinue;\n\n\t\tval = prinst->GetEdictFieldValue(prinst, (void*)ent, \"chain\", ev_entity, &menuc_eval.chain);\n\t\tif (val)\n\t\t\tval->edict = EDICT_TO_PROG(prinst, (void*)chain);\n\t\tchain = ent;\n\t}\n\n\tRETURN_EDICT(prinst, (void*)chain);\n}\n\nvoid QCBUILTIN PF_etof(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tG_FLOAT(OFS_RETURN) = G_EDICTNUM(prinst, OFS_PARM0);\n}\nvoid QCBUILTIN PF_ftoe(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint entnum = G_FLOAT(OFS_PARM0);\n\n\tRETURN_EDICT(prinst, EDICT_NUM_UB(prinst, entnum));\n}\n\nvoid QCBUILTIN PF_IsNotNull(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint str = G_INT(OFS_PARM0);\n\tG_FLOAT(OFS_RETURN) = !!str;\n}\n\n//float \taltstr_count(string str) = #82;\n//returns number of single quoted strings in the string.\nvoid QCBUILTIN PF_altstr_count(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char *s;\n\tint count = 0;\n\ts = PR_GetStringOfs(prinst, OFS_PARM0);\n\tfor (;*s;s++)\n\t{\n\t\tif (*s == '\\\\')\n\t\t{\n\t\t\tif (!*++s)\n\t\t\t\tbreak;\n\t\t}\n\t\telse if (*s == '\\'')\n\t\t\tcount++;\n\t}\n\tG_FLOAT(OFS_RETURN) = count/2;\n}\n//string  altstr_prepare(string str) = #83;\nvoid QCBUILTIN PF_altstr_prepare(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tchar outstr[8192], *out;\n\tconst char *instr, *in;\n\tint size;\n\n//\tVM_SAFEPARMCOUNT( 1, VM_altstr_prepare );\n\n\tinstr = PR_GetStringOfs(prinst, OFS_PARM0 );\n\t//VM_CheckEmptyString( instr );\n\n\tfor( out = outstr, in = instr, size = sizeof(outstr) - 1 ; size && *in ; size--, in++, out++ )\n\t{\n\t\tif( *in == '\\'' )\n\t\t{\n\t\t\t*out++ = '\\\\';\n\t\t\t*out = '\\'';\n\t\t\tsize--;\n\t\t}\n\t\telse\n\t\t\t*out = *in;\n\t}\n\t*out = 0;\n\n\tG_INT( OFS_RETURN ) = (int)PR_TempString( prinst, outstr );\n}\n//string  altstr_get(string str, float num) = #84;\nvoid QCBUILTIN PF_altstr_get(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char *altstr, *pos;\n\tchar outstr[8192], *out;\n\tint count, size;\n\n//\tVM_SAFEPARMCOUNT( 2, VM_altstr_get );\n\n\taltstr = PR_GetStringOfs(prinst, OFS_PARM0 );\n\t//VM_CheckEmptyString( altstr );\n\n\tcount = G_FLOAT( OFS_PARM1 );\n\tcount = count * 2 + 1;\n\n\tfor( pos = altstr ; *pos && count ; pos++ )\n\t{\n\t\tif( *pos == '\\\\' && !*++pos )\n\t\t\tbreak;\n\t\telse if( *pos == '\\'' )\n\t\t\tcount--;\n\t}\n\n\tif( !*pos )\n\t{\n\t\tG_INT( OFS_RETURN ) = (int)PR_SetString( prinst, \"\" );\n\t\treturn;\n\t}\n\n\tfor( out = outstr, size = sizeof(outstr) - 1 ; size && *pos ; size--, pos++, out++ )\n\t{\n\t\tif( *pos == '\\\\' )\n\t\t{\n\t\t\tif( !*++pos )\n\t\t\t\tbreak;\n\t\t\t*out = *pos;\n\t\t\tsize--;\n\t\t}\n\t\telse if( *pos == '\\'' )\n\t\t\tbreak;\n\t\telse\n\t\t\t*out = *pos;\n\t}\n\n\t*out = 0;\n\tG_INT( OFS_RETURN ) = (int)PR_TempString( prinst, outstr );\n}\n//string  altstr_set(string str, float num, string set) = #85\nvoid QCBUILTIN PF_altstr_set(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint num;\n\tconst char *altstr, *str;\n\tconst char *in;\n\tchar outstr[8192], *out;\n\n//\tVM_SAFEPARMCOUNT( 3, VM_altstr_set );\n\n\taltstr = PR_GetStringOfs(prinst, OFS_PARM0 );\n\t//VM_CheckEmptyString( altstr );\n\n\tnum = G_FLOAT( OFS_PARM1 );\n\n\tstr = PR_GetStringOfs(prinst, OFS_PARM2 );\n\t//VM_CheckEmptyString( str );\n\n\tout = outstr;\n\tfor( num = num * 2 + 1, in = altstr; *in && num; *out++ = *in++ )\n\t{\n\t\tif( *in == '\\\\' && !*++in )\n\t\t\tbreak;\n\t\telse if( *in == '\\'' )\n\t\t\tnum--;\n\t}\n\n\tif( !in )\n\t{\n\t\tG_INT( OFS_RETURN ) = (int)PR_SetString( prinst, \"\" );\n\t\treturn;\n\t}\n\t// copy set in\n\tfor( ; *str; *out++ = *str++ )\n\t\t;\n\t// now jump over the old contents\n\tfor( ; *in ; in++ )\n\t{\n\t\tif( *in == '\\'' || (*in == '\\\\' && !*++in) )\n\t\t\tbreak;\n\t}\n\n\tif( !in ) {\n\t\tG_INT( OFS_RETURN ) = (int)PR_SetString( prinst, \"\" );\n\t\treturn;\n\t}\n\n\tstrcpy( out, in );\n\tG_INT( OFS_RETURN ) = (int)PR_TempString( prinst, outstr );\n\n}\n\n//string(string serveraddress) crypto_getkeyfp\nvoid QCBUILTIN PF_crypto_getkeyfp(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\t//not supported.\n\tG_INT(OFS_RETURN) = 0;\n}\n//string(string serveraddress) crypto_getidfp\nvoid QCBUILTIN PF_crypto_getidfp(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\t//not supported.\n\tG_INT(OFS_RETURN) = 0;\n}\n//float(string serveraddress) crypto_getidstatus\nvoid QCBUILTIN PF_crypto_getidstatus(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\t//not supported.\n\tG_INT(OFS_RETURN) = 0;\n}\n//string(string serveraddress) crypto_getencryptlevel\nvoid QCBUILTIN PF_crypto_getencryptlevel(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\t//not supported.\n\tG_INT(OFS_RETURN) = 0;\n}\n//string(float i) crypto_getmykeyfp\nvoid QCBUILTIN PF_crypto_getmykeyfp(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\t//not supported.\n\tG_INT(OFS_RETURN) = 0;\n}\n//string(float i) crypto_getmyidfp\nvoid QCBUILTIN PF_crypto_getmyidfp(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\t//not supported.\n\tG_INT(OFS_RETURN) = 0;\n}\n//float(float i) PF_crypto_getmyidstatus\nvoid QCBUILTIN PF_crypto_getmyidstatus(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\t//not supported.\n\tG_INT(OFS_RETURN) = 0;\n}\n\nstatic void QCBUILTIN PF_m_precache_model(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char *modelname = PR_GetStringOfs(prinst, OFS_PARM0);\n\tMod_ForName(modelname, MLV_WARN);\n}\nstatic model_t *QDECL MP_GetCModel(struct world_s *w, int modelindex)\n{\n\textern int\t\tmod_numknown;\n\tmodelindex--;\n\tif (modelindex < 0 || modelindex >= mod_numknown)\n\t\treturn NULL;\n\treturn &mod_known[modelindex];\n}\nstatic void QCBUILTIN PF_m_getmodelindex(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char *modelname = PR_GetStringOfs(prinst, OFS_PARM0);\n\tmodel_t *m = Mod_ForName(modelname, MLV_WARN);\n\tif (m)\n\t\tG_FLOAT(OFS_RETURN) = (m-mod_known)+1;\n\telse\n\t\tG_FLOAT(OFS_RETURN) = 0;\n}\nstatic void QCBUILTIN PF_m_setmodel(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tmenuedict_t *ent = (void*)G_EDICT(prinst, OFS_PARM0);\n\tconst char *modelname = PR_GetStringOfs(prinst, OFS_PARM1);\n\teval_t *modelval = prinst->GetEdictFieldValue(prinst, (void*)ent, \"model\", ev_string, &menuc_eval.model);\n\teval_t *minsval = prinst->GetEdictFieldValue(prinst, (void*)ent, \"mins\", ev_vector, &menuc_eval.mins);\n\teval_t *maxsval = prinst->GetEdictFieldValue(prinst, (void*)ent, \"maxs\", ev_vector, &menuc_eval.maxs);\n\teval_t *modelidxval = prinst->GetEdictFieldValue(prinst, (void*)ent, \"modelindex\", ev_float, &menuc_eval.modelindex);\n\tmodel_t *mod = Mod_ForName(modelname, MLV_WARN);\n\tif (modelval)\n\t\tmodelval->string = G_INT(OFS_PARM1);\t//lets hope garbage collection is enough.\n\telse\n\t\tCon_Printf(\"PF_m_setmodel: no model field!\\n\");\n\n\tif (mod)\n\t\twhile(mod->loadstate == MLS_LOADING)\n\t\t\tCOM_WorkerPartialSync(mod, &mod->loadstate, MLS_LOADING);\n\tif (modelidxval)\n\t\tmodelidxval->_float = mod?(mod-mod_known)+1:0;\n\n\tif (mod && minsval)\n\t\tVectorCopy(mod->mins, minsval->_vector);\n\tif (mod && maxsval)\n\t\tVectorCopy(mod->maxs, maxsval->_vector);\n}\nstatic void QCBUILTIN PF_m_setcustomskin(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tmenuedict_t *ent = (void*)G_EDICT(prinst, OFS_PARM0);\n\tconst char *fname = PR_GetStringOfs(prinst, OFS_PARM1);\n\tconst char *skindata = PF_VarString(prinst, 2, pr_globals);\n\teval_t *val = prinst->GetEdictFieldValue(prinst, (void*)ent, \"skinobject\", ev_string, &menuc_eval.skinobject);\n\tif (!val)\n\t{\n\t\tCon_Printf(\"PF_m_setcustomskin: no skinobject field!\\n\");\n\t\treturn;\n\t}\n\n\tif (val->_float > 0)\n\t{\n\t\tMod_WipeSkin(val->_float, false);\n\t\tval->_float = 0;\n\t}\n\n\tif (*fname || *skindata)\n\t{\n\t\tif (*skindata)\n\t\t\tval->_float = Mod_ReadSkinFile(fname, skindata);\n\t\telse\n\t\t\tval->_float = -(int)Mod_RegisterSkinFile(fname);\n\t}\n}\n//trivially basic\nstatic void QCBUILTIN PF_m_setorigin(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tmenuedict_t *ent = (void*)G_EDICT(prinst, OFS_PARM0);\n\tfloat *org = G_VECTOR(OFS_PARM1);\n\teval_t *val = prinst->GetEdictFieldValue(prinst, (void*)ent, \"origin\", ev_vector, &menuc_eval.origin);\n\tif (val)\n\t\tVectorCopy(org, val->_vector);\n\telse\n\t\tCon_Printf(\"PF_m_setorigin: no origin field!\\n\");\n}\nstatic void QCBUILTIN PF_m_clearscene(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n//\tCL_DecayLights ();\n\n#if defined(SKELETALOBJECTS) || defined(RAGDOLLS)\n\tworld_t *world = prinst->parms->user;\n\tif (world)\n\t\tskel_dodelete(world);\n#endif\n\tCL_ClearEntityLists();\n\n\tV_ClearRefdef(&menuview);\n\tr_refdef.drawsbar = false;\n\tr_refdef.drawcrosshair = false;\n\tV_CalcRefdef(&menuview);\t//set up the defaults\n\tr_refdef.flags |= RDF_NOWORLDMODEL;\n}\nstatic void QDECL MP_Read_FrameState(pubprogfuncs_t *prinst, wedict_t *ent, framestate_t *fstate)\n{\n\teval_t *frame1val = prinst->GetEdictFieldValue(prinst, (void*)ent, \"frame\", ev_float, &menuc_eval.frame1);\n\teval_t *frame2val = prinst->GetEdictFieldValue(prinst, (void*)ent, \"frame2\", ev_float, &menuc_eval.frame2);\n\teval_t *lerpfracval = prinst->GetEdictFieldValue(prinst, (void*)ent, \"lerpfrac\", ev_float, &menuc_eval.lerpfrac);\n\teval_t *frame1timeval = prinst->GetEdictFieldValue(prinst, (void*)ent, \"frame1time\", ev_float, &menuc_eval.frame1time);\n\teval_t *frame2timeval = prinst->GetEdictFieldValue(prinst, (void*)ent, \"frame2time\", ev_float, &menuc_eval.frame2time);\n\teval_t *skelobjectval = prinst->GetEdictFieldValue(prinst, (void*)ent, \"skeletonindex\", ev_float, &menuc_eval.skelobject);\n\n\tfstate->g[FST_BASE].endbone = 0;\n\tfstate->g[FS_REG].endbone = 0x7fffffff;\n\tfstate->g[FS_REG].frame[0] = frame1val?frame1val->_float:0;\n\tfstate->g[FS_REG].frame[1] = frame2val?frame2val->_float:0;\n\tfstate->g[FS_REG].lerpweight[1] = lerpfracval?lerpfracval->_float:0;\n\tfstate->g[FS_REG].frametime[0] = frame1timeval?frame1timeval->_float:0;\n\tfstate->g[FS_REG].frametime[1] = frame2timeval?frame2timeval->_float:0;\n\n#if FRAME_BLENDS >= 4\n\tfstate->g[FS_REG].frame[2] = fstate->g[FS_REG].frame[0];\n\tfstate->g[FS_REG].lerpweight[2] = 0;\n\tfstate->g[FS_REG].frame[3] = fstate->g[FS_REG].frame[0];\n\tfstate->g[FS_REG].lerpweight[3] = 0;\n\tfstate->g[FS_REG].lerpweight[0] = 1-(fstate->g[FS_REG].lerpweight[1]+fstate->g[FS_REG].lerpweight[2]+fstate->g[FS_REG].lerpweight[3]);\n#else\n\tfstate->g[FS_REG].lerpweight[0] = 1-fstate->g[FS_REG].lerpweight[1];\n#endif\n\n#if defined(SKELETALOBJECTS) || defined(RAGDOLL)\n\tfstate->bonecount = 0;\n\tfstate->bonestate = NULL;\n\tif (skelobjectval && skelobjectval->_float)\n\t\tskel_lookup(&menu_world, skelobjectval->_float, fstate);\n#endif\n}\nstatic void QDECL MP_Get_FrameState(struct world_s *w, wedict_t *ent, framestate_t *fstate)\n{\n\tMP_Read_FrameState(w->progs, ent, fstate);\n}\nstatic qboolean CopyMenuEdictToEntity(pubprogfuncs_t *prinst, menuedict_t *in, entity_t *out)\n{\n\teval_t *modelval = prinst->GetEdictFieldValue(prinst, (void*)in, \"model\", ev_string, &menuc_eval.model);\n\teval_t *originval = prinst->GetEdictFieldValue(prinst, (void*)in, \"origin\", ev_vector, &menuc_eval.origin);\n\teval_t *anglesval = prinst->GetEdictFieldValue(prinst, (void*)in, \"angles\", ev_vector, &menuc_eval.angles);\n\teval_t *skinval = prinst->GetEdictFieldValue(prinst, (void*)in, \"skin\", ev_float, &menuc_eval.skin);\n\teval_t *frame1val = prinst->GetEdictFieldValue(prinst, (void*)in, \"frame\", ev_float, &menuc_eval.frame1);\n\teval_t *frame2val = prinst->GetEdictFieldValue(prinst, (void*)in, \"frame2\", ev_float, &menuc_eval.frame2);\n\teval_t *lerpfracval = prinst->GetEdictFieldValue(prinst, (void*)in, \"lerpfrac\", ev_float, &menuc_eval.lerpfrac);\n\teval_t *frame1timeval = prinst->GetEdictFieldValue(prinst, (void*)in, \"frame1time\", ev_float, &menuc_eval.frame1time);\n\teval_t *frame2timeval = prinst->GetEdictFieldValue(prinst, (void*)in, \"frame2time\", ev_float, &menuc_eval.frame2time);\n\teval_t *skelobjectval = prinst->GetEdictFieldValue(prinst, (void*)in, \"skeletonindex\", ev_float, &menuc_eval.skelobject);\n\teval_t *colormapval = prinst->GetEdictFieldValue(prinst, (void*)in, \"colormap\", ev_float, &menuc_eval.colormap);\n\teval_t *renderflagsval = prinst->GetEdictFieldValue(prinst, (void*)in, \"renderflags\", ev_float, &menuc_eval.renderflags);\n\teval_t *skinobjectval = prinst->GetEdictFieldValue(prinst, (void*)in, \"skinobject\", ev_float, &menuc_eval.skinobject);\n\teval_t *colourmodval = prinst->GetEdictFieldValue(prinst, (void*)in, \"colormod\", ev_vector, &menuc_eval.colourmod);\n\teval_t *alphaval = prinst->GetEdictFieldValue(prinst, (void*)in, \"alpha\", ev_float, &menuc_eval.alpha);\n\tint ival;\n\tint rflags;\n\n\trflags = renderflagsval?renderflagsval->_float:0;\n\n\tmemset(out, 0, sizeof(*out));\n\tif (modelval)\n\t\tout->model = Mod_ForName(prinst->StringToNative(prinst, modelval->_int), MLV_WARN);\n\tif (originval)\n\t\tVectorCopy(originval->_vector, out->origin);\n\tif (!anglesval)anglesval = (eval_t*)vec3_origin;\n\tAngleVectors(anglesval->_vector, out->axis[0], out->axis[1], out->axis[2]);\n\tVectorInverse(out->axis[1]);\n\n\tout->scale = 1;\n\tout->skinnum = skinval?skinval->_float:0;\n\tout->framestate.g[FS_REG].frame[0] = frame1val?frame1val->_float:0;\n\tout->framestate.g[FS_REG].frame[1] = frame2val?frame2val->_float:0;\n\tout->framestate.g[FS_REG].lerpweight[1] = lerpfracval?lerpfracval->_float:0;\n\tout->framestate.g[FS_REG].lerpweight[0] = 1-out->framestate.g[FS_REG].lerpweight[1];\n\tout->framestate.g[FS_REG].frametime[0] = frame1timeval?frame1timeval->_float:0;\n\tout->framestate.g[FS_REG].frametime[1] = frame2timeval?frame2timeval->_float:0;\n\n#if defined(SKELETALOBJECTS) || defined(RAGDOLL)\n\tout->framestate.bonecount = 0;\n\tout->framestate.bonestate = NULL;\n\tif (skelobjectval && skelobjectval->_float)\n\t\tskel_lookup(&menu_world, skelobjectval->_float, &out->framestate);\n#endif\n\n\tout->customskin = skinobjectval?skinobjectval->_float:0;\n\n\t//FIXME: colourmap\n\tival = colormapval?colormapval->_float:0;\n\tout->playerindex = -1;\n\tif (ival >= 1024)\n\t{\n\t\t//DP COLORMAP extension\n\t\tout->topcolour = (ival>>4) & 0x0f;\n\t\tout->bottomcolour = ival & 0xf;\n\t}\n/*\telse if (ival > 0 && ival <= MAX_CLIENTS)\n\t{\t//FIXME: tie to the current skin/topcolor/bottomcolor cvars somehow?\n\t\tout->playerindex = ival - 1;\n\t\tout->topcolour = cl.players[ival-1].ttopcolor;\n\t\tout->bottomcolour = cl.players[ival-1].tbottomcolor;\n\t}*/\n\telse\n\t{\n\t\tout->topcolour = TOP_DEFAULT;\n\t\tout->bottomcolour = BOTTOM_DEFAULT;\n\t}\n\n\tVectorSet(out->glowmod, 1,1,1);\n\tif (!colourmodval || (!colourmodval->_vector[0] && !colourmodval->_vector[1] && !colourmodval->_vector[2]))\n\t\tVectorSet(out->shaderRGBAf, 1, 1, 1);\n\telse\n\t{\n\t\tout->flags |= RF_FORCECOLOURMOD;\n\t\tVectorCopy(colourmodval->_vector, out->shaderRGBAf);\n\t}\n\tif (!alphaval || !alphaval->_float || alphaval->_float == 1)\n\t\tout->shaderRGBAf[3] = 1.0f;\n\telse\n\t{\n\t\tout->flags |= RF_TRANSLUCENT;\n\t\tout->shaderRGBAf[3] = alphaval->_float;\n\t}\n\n\tif (rflags & CSQCRF_ADDITIVE)\n\t\tout->flags |= RF_ADDITIVE;\n\tif (rflags & CSQCRF_DEPTHHACK)\n\t\tout->flags |= RF_DEPTHHACK;\n\n\tif (out->model)\n\t\treturn true;\n\treturn false;\n}\nstatic void QCBUILTIN PF_m_addentity(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tmenuedict_t *in = (void*)G_EDICT(prinst, OFS_PARM0);\n\tentity_t ent;\n\tif (in->ereftype == ER_FREE || in->entnum == 0)\n\t{\n\t\tCon_Printf(\"Tried drawing a free/removed/world entity\\n\");\n\t\treturn;\n\t}\n\n\tif (CopyMenuEdictToEntity(prinst, in, &ent))\n\t\tV_AddAxisEntity(&ent);\n}\nstatic void QCBUILTIN PF_m_addentity_lighting(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tmenuedict_t *in = (void*)G_EDICT(prinst, OFS_PARM0);\n\tentity_t ent;\n\tif (in->ereftype == ER_FREE || in->entnum == 0)\n\t{\n\t\tCon_Printf(\"Tried drawing a free/removed/world entity\\n\");\n\t\treturn;\n\t}\n\n\tif (CopyMenuEdictToEntity(prinst, in, &ent))\n\t{\n\t\tent.light_known = true;\n\t\tVectorCopy(G_VECTOR(OFS_PARM1), ent.light_dir);\n\t\tVectorCopy(G_VECTOR(OFS_PARM2), ent.light_avg);\n\t\tVectorCopy(G_VECTOR(OFS_PARM3), ent.light_range);\n\n\t\tV_AddAxisEntity(&ent);\n\t}\n}\nstatic void QCBUILTIN PF_m_renderscene(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tV_ApplyRefdef();\n\tR_RenderView();\n}\nvoid QCBUILTIN PF_R_SetViewFlag(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_R_GetViewFlag(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\n\n\nstatic void QCBUILTIN PF_menu_cprint (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char *str = PF_VarString(prinst, 0, pr_globals);\n\tSCR_CenterPrint(0, str, true);\n}\nstatic void QCBUILTIN PF_cl_changelevel (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n#ifndef CLIENTONLY\n\tconst char *nextmap = PR_GetStringOfs(prinst, OFS_PARM0);\n\tif (sv.active || !cls.state)\n\t{\n\t\tchar buf[1024];\n\t\tCbuf_AddText(va(\"changelevel %s\\n\", COM_QuotedString(nextmap, buf, sizeof(buf), false)), RESTRICT_INSECURE);\n\t}\n#endif\n}\nstatic void QCBUILTIN PF_crash (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint binum;\n\tchar fname[MAX_QPATH];\n\t//allow people to rename it or whatever\n\tif (!prinst->GetBuiltinCallInfo(prinst, &binum, fname, sizeof(fname)))\n\t{\n\t\tbinum = 0;\n\t\tstrcpy(fname, \"?unknown?\");\n\t}\n\n\tprinst->RunError(prinst, \"\\n%s called\", fname);\n}\nstatic void QCBUILTIN PF_stackdump (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tprinst->StackTrace(prinst, true);\n}\n#define PF_cl_clientcommand PF_Fixme\n#define PF_altstr_ins PF_Fixme\t//insert after, apparently\n\nstatic void MP_ConsoleCommand_f(void)\n{\n\tchar cmd[2048];\n\tQ_snprintfz(cmd, sizeof(cmd), \"%s %s\", Cmd_Argv(0), Cmd_Args());\n\tMP_ConsoleCommand(cmd);\n}\nstatic void QCBUILTIN PF_menu_registercommand (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char *str = PR_GetStringOfs(prinst, OFS_PARM0);\n\tconst char *desc = (prinst->callargc>1)?PR_GetStringOfs(prinst, OFS_PARM1):NULL;\n\tif (desc && !*desc)\n\t\tdesc = NULL;\n\tif (!Cmd_Exists(str))\n\t\tCmd_AddCommandD(str, MP_ConsoleCommand_f, desc);\n}\n\nstatic void PF_m_clipboard_got(void *ctx, const char *utf8)\n{\n\tvoid *pr_globals;\n\tunsigned int unicode;\n\tint error;\n\n\twhile (*utf8)\n\t{\n\t\tunicode = utf8_decode(&error, utf8, &utf8);\n\t\tif (error)\n\t\t\tunicode = 0xfffdu;\n\n\t\tif (!menu_world.progs || !mpfuncs.inputevent)\n\t\t\treturn;\n\t#ifdef TEXTEDITOR\n\t\tif (editormodal)\n\t\t\treturn;\n\t#endif\n\n\t\tpr_globals = PR_globals(menu_world.progs, PR_CURRENT);\n\t\tG_FLOAT(OFS_PARM0) = CSIE_PASTE;\n\t\tG_FLOAT(OFS_PARM1) = 0;\n\t\tG_FLOAT(OFS_PARM2) = unicode;\n\t\tG_FLOAT(OFS_PARM3) = 0;\n\n\t\tqcinput_scan = G_FLOAT(OFS_PARM1);\n\t\tqcinput_unicode = G_FLOAT(OFS_PARM2);\n\t\tPR_ExecuteProgram (menu_world.progs, mpfuncs.inputevent);\n\t\tqcinput_scan = 0;\t//and stop replay attacks\n\t\tqcinput_unicode = 0;\n\t}\n}\nstatic void QCBUILTIN PF_m_clipboard_get(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tclipboardtype_t cliptype = G_FLOAT(OFS_PARM0);\n\tSys_Clipboard_PasteText(cliptype, PF_m_clipboard_got, prinst);\n}\n\nstatic struct {\n\tchar *name;\n\tbuiltin_t bifunc;\n\tint ebfsnum;\n}  BuiltinList[] = {\n\t{\"checkextension\",\t\t\tPF_menu_checkextension,\t\t1},\n\t{\"checkbuiltin\",\t\t\tPF_checkbuiltin,\t\t\t0},\n\t{\"error\",\t\t\t\t\tPF_error,\t\t\t\t\t2},\n\t{\"objerror\",\t\t\t\tPF_nonfatalobjerror,\t\t3},\n\t{\"print\",\t\t\t\t\tPF_print,\t\t\t\t\t4},\n\t{\"bprint\",\t\t\t\t\tPF_cl_bprint,\t\t\t\t5},\n\t{\"msprint\",\t\t\t\t\tPF_cl_sprint,\t\t\t\t6},\n\t{\"cprint\",\t\t\t\t\tPF_menu_cprint,\t\t\t\t7},\n\t{\"normalize\",\t\t\t\tPF_normalize,\t\t\t\t8},\n\t{\"vlen\",\t\t\t\t\tPF_vlen,\t\t\t\t\t9},\n\t{\"vectoyaw\",\t\t\t\tPF_vectoyaw,\t\t\t\t10},\n\t{\"vectoangles\",\t\t\t\tPF_vectoangles,\t\t\t\t11},\n\t{\"crossproduct\",\t\t\tPF_crossproduct,\t\t\t0},\n\t{\"random\",\t\t\t\t\tPF_random,\t\t\t\t\t12},\n\t{\"localcmd\",\t\t\t\tPF_localcmd,\t\t\t\t13},\n\t{\"cvar\",\t\t\t\t\tPF_menu_cvar,\t\t\t\t14},\n\t{\"cvar_set\",\t\t\t\tPF_menu_cvar_set,\t\t\t15},\n\t{\"dprint\",\t\t\t\t\tPF_dprint,\t\t\t\t\t16},\n\t{\"ftos\",\t\t\t\t\tPF_ftos,\t\t\t\t\t17},\n\t{\"fabs\",\t\t\t\t\tPF_fabs,\t\t\t\t\t18},\n\t{\"vtos\",\t\t\t\t\tPF_vtos,\t\t\t\t\t19},\n\t{\"etos\",\t\t\t\t\tPF_etos,\t\t\t\t\t20},\n\t{\"stof\",\t\t\t\t\tPF_stof,\t\t\t\t\t21},\n\n\t{\"json_parse\",\t\t\t\tPF_json_parse,\t\t\t\t0},\n\t{\"json_free\",\t\t\t\tPF_memfree,\t\t\t\t\t0},\n\t{\"json_get_value_type\",\t\tPF_json_get_value_type,\t\t0},\n\t{\"json_get_integer\",\t\tPF_json_get_integer,\t\t0},\n\t{\"json_get_float\",\t\t\tPF_json_get_float,\t\t\t0},\n\t{\"json_get_string\",\t\t\tPF_json_get_string,\t\t\t0},\n\t{\"json_find_object_child\",\tPF_json_find_object_child,\t0},\n\t{\"json_get_length\",\t\t\tPF_json_get_length,\t\t\t0},\n\t{\"json_get_child_at_index\",\tPF_json_get_child_at_index,\t0},\n\t{\"json_get_name\",\t\t\tPF_json_get_name,\t\t\t0},\n\n\t{\"js_run_script\",\t\t\tPF_js_run_script,\t\t\t0},\n\t\n\t{\"stoi\",\t\t\t\t\tPF_stoi,\t\t\t\t\t0},\n\t{\"itos\",\t\t\t\t\tPF_itos,\t\t\t\t\t0},\n\t{\"stoh\",\t\t\t\t\tPF_stoh,\t\t\t\t\t0},\n\t{\"htos\",\t\t\t\t\tPF_htos,\t\t\t\t\t0},\n\t{\"ftoi\",\t\t\t\t\tPF_ftoi,\t\t\t\t\t0},\n\t{\"itof\",\t\t\t\t\tPF_itof,\t\t\t\t\t0},\n\t{\"ftou\",\t\t\t\t\tPF_ftou,\t\t\t\t\t0},\n\t{\"utof\",\t\t\t\t\tPF_utof,\t\t\t\t\t0},\n\n\t{\"spawn\",\t\t\t\t\tPF_Spawn,\t\t\t\t\t22},\n\t{\"remove\",\t\t\t\t\tPF_Remove_,\t\t\t\t\t23},\n\t{\"removeinstant\",\t\t\tPF_RemoveInstant,\t\t\t0},\n\t{\"find\",\t\t\t\t\tPF_FindString,\t\t\t\t24},\n\t{\"findfloat\",\t\t\t\tPF_FindFloat,\t\t\t\t25},\n\t{\"findentity\",\t\t\t\tPF_FindFloat,\t\t\t\t25},\n\t{\"findchain\",\t\t\t\tPF_menu_findchain,\t\t\t26},\n\t{\"findchainfloat\",\t\t\tPF_menu_findchainfloat,\t\t27},\n#ifdef QCGC\n\t{\"find_list\",\t\t\t\tPF_FindList,\t\t\t\t0},\n#endif\n\t{\"precache_file\",\t\t\tPF_CL_precache_file,\t\t28},\n\t{\"precache_sound\",\t\t\tPF_CL_precache_sound,\t\t29},\n\t{\"coredump\",\t\t\t\tPF_coredump,\t\t\t\t30},\n\t{\"traceon\",\t\t\t\t\tPF_traceon,\t\t\t\t\t31},\n\t{\"traceoff\",\t\t\t\tPF_traceoff,\t\t\t\t32},\n\t{\"eprint\",\t\t\t\t\tPF_eprint,\t\t\t\t\t33},\n\t{\"rint\",\t\t\t\t\tPF_rint,\t\t\t\t\t34},\n\t{\"floor\",\t\t\t\t\tPF_floor,\t\t\t\t\t35},\n\t{\"ceil\",\t\t\t\t\tPF_ceil,\t\t\t\t\t36},\n\t{\"nextent\",\t\t\t\t\tPF_nextent,\t\t\t\t\t37},\n\t{\"sin\",\t\t\t\t\t\tPF_Sin,\t\t\t\t\t\t38},\n\t{\"cos\",\t\t\t\t\t\tPF_Cos,\t\t\t\t\t\t39},\n\t{\"sqrt\",\t\t\t\t\tPF_Sqrt,\t\t\t\t\t40},\n\t{\"randomvec\",\t\t\t\tPF_randomvector,\t\t\t41},\n\t{\"registercvar\",\t\t\tPF_registercvar,\t\t\t42},\n\t{\"min\",\t\t\t\t\t\tPF_min,\t\t\t\t\t\t43},\n\t{\"max\",\t\t\t\t\t\tPF_max,\t\t\t\t\t\t44},\n\t{\"bound\",\t\t\t\t\tPF_bound,\t\t\t\t\t45},\n\t{\"pow\",\t\t\t\t\t\tPF_pow,\t\t\t\t\t\t46},\n\t{\"logarithm\",\t\t\t\tPF_Logarithm,\t\t\t\t0},\n\t{\"entityprotection\",\t\tPF_entityprotection,\t\t0},\n\t{\"copyentity\",\t\t\t\tPF_CopyEntity,\t\t\t\t47},\n\t{\"fopen\",\t\t\t\t\tPF_fopen,\t\t\t\t\t48},\n\t{\"fclose\",\t\t\t\t\tPF_fclose,\t\t\t\t\t49},\n\t{\"fgets\",\t\t\t\t\tPF_fgets,\t\t\t\t\t50},\n\t{\"fputs\",\t\t\t\t\tPF_fputs,\t\t\t\t\t51},\n\t{\"fread\",\t\t\t\t\tPF_fread,\t\t\t\t\t0},\n\t{\"fwrite\",\t\t\t\t\tPF_fwrite,\t\t\t\t\t0},\n\t{\"fseek\",\t\t\t\t\tPF_fseek32,\t\t\t\t\t0},\n\t{\"fsize\",\t\t\t\t\tPF_fsize32,\t\t\t\t\t0},\n\t{\"fseek64\",\t\t\t\t\tPF_fseek64,\t\t\t\t\t0},\n\t{\"fsize64\",\t\t\t\t\tPF_fsize64,\t\t\t\t\t0},\n\t{\"strlen\",\t\t\t\t\tPF_strlen,\t\t\t\t\t52},\n\t{\"strcat\",\t\t\t\t\tPF_strcat,\t\t\t\t\t53},\n\t{\"substring\",\t\t\t\tPF_substring,\t\t\t\t54},\n\t{\"stov\",\t\t\t\t\tPF_stov,\t\t\t\t\t55},\n\t{\"strzone\",\t\t\t\t\tPF_strzone,\t\t\t\t\t56},\n\t{\"strunzone\",\t\t\t\tPF_strunzone,\t\t\t\t57},\n#ifdef QCGC\n\t{\"createbuffer\",\t\t\tPF_createbuffer,\t\t\t0},\n#endif\n\t{\"tokenize\",\t\t\t\tPF_Tokenize,\t\t\t\t58},\n\t{\"argv\",\t\t\t\t\tPF_ArgV,\t\t\t\t\t59},\n\t{\"isserver\",\t\t\t\tPF_isserver,\t\t\t\t60},\n\t{\"clientcount\",\t\t\t\tPF_cl_clientcount,\t\t\t61},\t\t\t\t\t\t//float\tclientcount(void)  = #61;\n\t{\"clientstate\",\t\t\t\tPF_clientstate,\t\t\t\t62},\n\t{\"clientcommand\",\t\t\tPF_cl_clientcommand,\t\t63},\t\t\t\t\t\t//void\tclientcommand(float client, string s)  = #63;\n\t{\"changelevel\",\t\t\t\tPF_cl_changelevel,\t\t\t64},\t\t\t\t\t\t//void\tchangelevel(string map)  = #64;\n\t{\"localsound\",\t\t\t\tPF_cl_localsound,\t\t\t65},\n\t{\"getmousepos\",\t\t\t\tPF_cl_getmousepos,\t\t\t66},\n\t{\"gettime\",\t\t\t\t\tPF_gettimef,\t\t\t\t67},\n\t{\"gettimed\",\t\t\t\tPF_gettimed,\t\t\t\t0},\n\t{\"loadfromdata\",\t\t\tPF_loadfromdata,\t\t\t68},\n\t{\"loadfromfile\",\t\t\tPF_loadfromfile,\t\t\t69},\n\t{\"mod\",\t\t\t\t\t\tPF_mod,\t\t\t\t\t\t70},\n\t{\"cvar_string\",\t\t\t\tPF_menu_cvar_string,\t\t71},\n\t{\"crash\",\t\t\t\t\tPF_crash,\t\t\t\t\t72},\t\t\t\t//void\tcrash(void)\t= #72;\n\t{\"stackdump\",\t\t\t\tPF_stackdump,\t\t\t\t73},\t\t\t//void\tstackdump(void) = #73;\n\t{\"search_begin\",\t\t\tPF_search_begin,\t\t\t74},\n\t{\"search_end\",\t\t\t\tPF_search_end,\t\t\t\t75},\n\t{\"search_getsize\",\t\t\tPF_search_getsize,\t\t\t76},\n\t{\"search_getfilename\",\t\tPF_search_getfilename,\t\t77},\n\t{\"search_getfilesize\",\t\tPF_search_getfilesize,\t\t0},\n\t{\"search_getfilemtime\",\t\tPF_search_getfilemtime,\t\t0},\n\t{\"search_getpackagename\",\tPF_search_getpackagename,\t0},\n\t{\"search_fopen\",\t\t\tPF_search_fopen,\t\t\t0},\n\t{\"chr2str\",\t\t\t\t\tPF_chr2str,\t\t\t\t\t78},\n\t{\"etof\",\t\t\t\t\tPF_etof,\t\t\t\t\t79},\n\t{\"ftoe\",\t\t\t\t\tPF_ftoe,\t\t\t\t\t80},\n\t{\"validstring\",\t\t\t\tPF_IsNotNull,\t\t\t\t81},\n\t{\"altstr_count\",\t\t\tPF_altstr_count, \t\t\t82},\n\t{\"altstr_prepare\",\t\t\tPF_altstr_prepare, \t\t\t83},\n\t{\"altstr_get\",\t\t\t\tPF_altstr_get,\t\t\t\t84},\n\t{\"altstr_set\",\t\t\t\tPF_altstr_set, \t\t\t\t85},\n\t{\"altstr_ins\",\t\t\t\tPF_altstr_ins,\t\t\t\t86},\n\t{\"findflags\",\t\t\t\tPF_FindFlags,\t\t\t\t87},\n\t{\"findchainflags\",\t\t\tPF_menu_findchainflags,\t\t88},\n\t{\"cvar_defstring\",\t\t\tPF_cvar_defstring,\t\t\t89},\n\t{\"setmodel\",\t\t\t\tPF_m_setmodel,\t\t\t\t90},\n\t{\"precache_model\",\t\t\tPF_m_precache_model,\t\t91},\n\t{\"setorigin\",\t\t\t\tPF_m_setorigin,\t\t\t\t92},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t//gap\n\t{\"getmodelindex\",\t\t\tPF_m_getmodelindex,\t\t\t200},\n\t{\"externcall\",\t\t\t\tPF_externcall,\t\t\t\t201},\n\t{\"addprogs\",\t\t\t\tPF_cl_addprogs,\t\t\t\t202},\n\t{\"externvalue\",\t\t\t\tPF_externvalue,\t\t\t\t203},\n\t{\"externset\",\t\t\t\tPF_externset,\t\t\t\t204},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t//gap\n\t{\"fork\",\t\t\t\t\tPF_Fork,\t\t\t\t\t210},\n\t{\"abort\",\t\t\t\t\tPF_Abort,\t\t\t\t\t211},\n\t{\"sleep\",\t\t\t\t\tPF_Sleep,\t\t\t\t\t212},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t//gap\n\t{\"strstrofs\",\t\t\t\tPF_strstrofs,\t\t\t\t221},\n\t{\"str2chr\",\t\t\t\t\tPF_str2chr,\t\t\t\t\t222},\n\t{\"chr2str\",\t\t\t\t\tPF_chr2str,\t\t\t\t\t223},\n\t{\"strconv\",\t\t\t\t\tPF_strconv,\t\t\t\t\t224},\n\t{\"strpad\",\t\t\t\t\tPF_strpad,\t\t\t\t\t225},\n\t{\"infoadd\",\t\t\t\t\tPF_infoadd,\t\t\t\t\t226},\n\t{\"infoget\",\t\t\t\t\tPF_infoget,\t\t\t\t\t227},\n\t{\"strcmp\",\t\t\t\t\tPF_strncmp,\t\t\t\t\t228},\n\t{\"strncmp\",\t\t\t\t\tPF_strncmp,\t\t\t\t\t228},\n\t{\"strcasecmp\",\t\t\t\tPF_strncasecmp,\t\t\t\t229},\n\t{\"strncasecmp\",\t\t\t\tPF_strncasecmp,\t\t\t\t230},\n\t{\"strtrim\",\t\t\t\t\tPF_strtrim,\t\t\t\t\t0},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t//gap\n\t{\"shaderforname\",\t\t\tPF_shaderforname,\t\t\t238},\n\t{\"sendpacket\",\t\t\t\tPF_cl_SendPacket,\t\t\t242},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t//gap\n\t{\"skel_create\",\t\t\t\tPF_skel_create,\t\t\t\t263},//float(float modlindex) skel_create = #263; // (FTE_CSQC_SKELETONOBJECTS)\n\t{\"skel_build\",\t\t\t\tPF_skel_build,\t\t\t\t264},//float(float skel, entity ent, float modelindex, float retainfrac, float firstbone, float lastbone, optional float addition) skel_build = #264; // (FTE_CSQC_SKELETONOBJECTS)\n\t{\"skel_build_ptr\",\t\t\tPF_skel_build_ptr,\t\t\t0},//float(float skel, int numblends, __variant *blends, int blendsize) skel_build_ptr = #0;\n\t{\"skel_get_numbones\",\t\tPF_skel_get_numbones,\t\t265},//float(float skel) skel_get_numbones = #265; // (FTE_CSQC_SKELETONOBJECTS)\n\t{\"skel_get_bonename\",\t\tPF_skel_get_bonename,\t\t266},//string(float skel, float bonenum) skel_get_bonename = #266; // (FTE_CSQC_SKELETONOBJECTS) (returns tempstring)\n\t{\"skel_get_boneparent\",\t\tPF_skel_get_boneparent,\t\t267},//float(float skel, float bonenum) skel_get_boneparent = #267; // (FTE_CSQC_SKELETONOBJECTS)\n\t{\"skel_find_bone\",\t\t\tPF_skel_find_bone,\t\t\t268},//float(float skel, string tagname) skel_get_boneidx = #268; // (FTE_CSQC_SKELETONOBJECTS)\n\t{\"skel_get_bonerel\",\t\tPF_skel_get_bonerel,\t\t269},//vector(float skel, float bonenum) skel_get_bonerel = #269; // (FTE_CSQC_SKELETONOBJECTS) (sets v_forward etc)\n\t{\"skel_get_boneabs\",\t\tPF_skel_get_boneabs,\t\t270},//vector(float skel, float bonenum) skel_get_boneabs = #270; // (FTE_CSQC_SKELETONOBJECTS) (sets v_forward etc)\n\t{\"skel_set_bone\",\t\t\tPF_skel_set_bone,\t\t\t271},//void(float skel, float bonenum, vector org) skel_set_bone = #271; // (FTE_CSQC_SKELETONOBJECTS) (reads v_forward etc)\n\t{\"skel_premul_bone\",\t\tPF_skel_premul_bone,\t\t272},//void(float skel, float bonenum, vector org) skel_mul_bone = #272; // (FTE_CSQC_SKELETONOBJECTS) (reads v_forward etc)\n\t{\"skel_premul_bones\",\t\tPF_skel_premul_bones,\t\t273},//void(float skel, float startbone, float endbone, vector org) skel_mul_bone = #273; // (FTE_CSQC_SKELETONOBJECTS) (reads v_forward etc)\n\t{\"skel_postmul_bone\",\t\tPF_skel_postmul_bone,\t\t0},//void(float skel, float bonenum, vector org) skel_mul_bone = #272; // (FTE_CSQC_SKELETONOBJECTS) (reads v_forward etc)\n//\t{\"skel_postmul_bones\",\t\tPF_skel_postmul_bones,\t\t0},//void(float skel, float startbone, float endbone, vector org) skel_mul_bone = #273; // (FTE_CSQC_SKELETONOBJECTS) (reads v_forward etc)\n\t{\"skel_copybones\",\t\t\tPF_skel_copybones,\t\t\t274},//void(float skeldst, float skelsrc, float startbone, float entbone) skel_copybones = #274; // (FTE_CSQC_SKELETONOBJECTS)\n\t{\"skel_delete\",\t\t\t\tPF_skel_delete,\t\t\t\t275},//void(float skel) skel_delete = #275; // (FTE_CSQC_SKELETONOBJECTS)\n\t{\"frameforname\",\t\t\tPF_frameforname,\t\t\t276},//float(float modidx, string framename) frameforname = #276 (FTE_CSQC_SKELETONOBJECTS)\n\t{\"frameduration\",\t\t\tPF_frameduration,\t\t\t277},//float(float modidx, float framenum) frameduration = #277 (FTE_CSQC_SKELETONOBJECTS)\n\t{\"frameforaction\",\t\t\tPF_frameforaction,\t\t\t0},//float(float modidx, int actionid) frameforaction = #0\n\t{\"processmodelevents\",\t\tPF_processmodelevents,\t\t0},\n\t{\"getnextmodelevent\",\t\tPF_getnextmodelevent,\t\t0},\n\t{\"getmodeleventidx\",\t\tPF_getmodeleventidx,\t\t0},\n\t{\"frametoname\",\t\t\t\tPF_frametoname,\t\t\t\t0},//string(float modidx, float framenum) frametoname\n\t{\"modelframecount\",\t\t\tPF_modelframecount,\t\t\t0},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t//gap\n\t{\"hash_createtab\",\t\t\tPF_hash_createtab,\t\t\t287},\n\t{\"hash_destroytab\",\t\t\tPF_hash_destroytab,\t\t\t288},\n\t{\"hash_add\",\t\t\t\tPF_hash_add,\t\t\t\t289},\n\t{\"hash_get\",\t\t\t\tPF_hash_get,\t\t\t\t290},\n\t{\"hash_delete\",\t\t\t\tPF_hash_delete,\t\t\t\t291},\n\t{\"hash_getkey\",\t\t\t\tPF_hash_getkey,\t\t\t\t292},\n\t{\"hash_getcb\",\t\t\t\tPF_hash_getcb,\t\t\t\t293},\n\t{\"checkcommand\",\t\t\tPF_checkcommand,\t\t\t294},\n\t{\"argescape\",\t\t\t\tPF_argescape,\t\t\t\t295},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t//gap\n\t{\"clearscene\",\t\t\t\tPF_m_clearscene,\t\t\t300},\n//\t{\"addentities\",\t\t\t\tPF_Fixme,\t\t\t\t\t301},\n\t{\"addentity\",\t\t\t\tPF_m_addentity,\t\t\t\t302},//FIXME: needs setmodel, origin, angles, colormap(eep), frame etc, skin, \n\t{\"addentity_lighting\",\t\tPF_m_addentity_lighting,\t0},\n#ifdef CSQC_DAT\n\t{\"setproperty\",\t\t\t\tPF_R_SetViewFlag,\t\t\t303},//should be okay to share\n#endif\n\t{\"renderscene\",\t\t\t\tPF_m_renderscene,\t\t\t304},//too module-specific\n//\t{\"dynamiclight_add\",\t\tPF_R_DynamicLight_Add,\t\t305},//should be okay to share\n\t{\"R_BeginPolygon\",\t\t\tPF_R_PolygonBegin,\t\t\t306},//useful for 2d stuff\n\t{\"R_PolygonVertex\",\t\t\tPF_R_PolygonVertex,\t\t\t307},\n\t{\"R_EndPolygon\",\t\t\tPF_R_PolygonEnd,\t\t\t308},\n#ifdef CSQC_DAT\n\t{\"getproperty\",\t\t\t\tPF_R_GetViewFlag,\t\t\t309},//should be okay to share\n#endif\n//unproject\t\t\t\t\t\t\t\t\t\t\t\t\t310\n//project\t\t\t\t\t\t\t\t\t\t\t\t\t311\n\n#ifdef CSQC_DAT\n\t{\"r_uploadimage\",\t\t\tPF_CL_uploadimage,\t\t\t0},\n#endif\n\t{\"r_readimage\",\t\t\t\tPF_CL_readimage,\t\t\t0},\n\n\n\t{\"print_csqc\",\t\t\t\tPF_print,\t\t\t\t\t339},\n\t{\"setwatchpoint\",\t\t\tPF_setwatchpoint,\t\t\t0},\n\t{\"keynumtostring_csqc\",\t\tPF_cl_keynumtostring,\t\t340},\n\t{\"stringtokeynum_csqc\",\t\tPF_cl_stringtokeynum,\t\t341},\n\t{\"getkeybind\",\t\t\t\tPF_cl_getkeybind,\t\t\t342},\n\t{\"setcursormode\",\t\t\tPF_cl_setcursormode,\t\t343},\n\t{\"getcursormode\",\t\t\tPF_cl_getcursormode,\t\t0},\t\n\t{\"setmousepos\",\t\t\t\tPF_cl_setmousepos,\t\t\t0},\n\n\n\t{\"clipboard_get\",\t\t\tPF_m_clipboard_get,\t\t\t0},\n\t{\"clipboard_set\",\t\t\tPF_cl_clipboard_set,\t\t0},\n\n//\t{NULL,\t\t\t\t\t\tPF_Fixme,\t\t\t\t\t344},\n//\t{NULL,\t\t\t\t\t\tPF_Fixme,\t\t\t\t\t345},\n//\t{NULL,\t\t\t\t\t\tPF_Fixme,\t\t\t\t\t346},\n//\t{NULL,\t\t\t\t\t\tPF_Fixme,\t\t\t\t\t347},\n//\t{NULL,\t\t\t\t\t\tPF_Fixme,\t\t\t\t\t348},\n\t{\"setlocaluserinfo\",\t\tPF_cl_setlocaluserinfo,\t\t\t0},\n\t{\"getlocaluserinfo\",\t\tPF_cl_getlocaluserinfostring,\t0},\n\t{\"setlocaluserinfoblob\",\tPF_cl_setlocaluserinfo,\t\t\t0},\n\t{\"getlocaluserinfoblob\",\tPF_cl_getlocaluserinfoblob,\t\t0},\n\t{\"isdemo\",\t\t\t\t\tPF_isdemo,\t\t\t\t\t349},\n//\t{NULL,\t\t\t\t\t\tPF_Fixme,\t\t\t\t\t350},\n//\t{NULL,\t\t\t\t\t\tPF_Fixme,\t\t\t\t\t351},\n\t{\"queueaudio\",\t\t\t\tPF_cl_queueaudio,\t\t\t0},\n\t{\"getqueuedaudiotime\",\t\tPF_cl_getqueuedaudiotime,\t0},\n\t{\"registercommand\",\t\t\tPF_menu_registercommand,\t352},\n\t{\"wasfreed\",\t\t\t\tPF_WasFreed,\t\t\t\t353},\n\t{\"serverkey\",\t\t\t\tPF_cl_serverkey,\t\t\t354},\t// #354 string(string key) serverkey;\n\t{\"serverkeyfloat\",\t\t\tPF_cl_serverkeyfloat,\t\t0},\t\t// #0 float(string key) serverkeyfloat;\n\t{\"serverkeyblob\",\t\t\tPF_cl_serverkeyblob,\t\t0},\n#ifdef HAVE_MEDIA_DECODER\n\t{\"videoplaying\",\t\t\tPF_cs_media_getstate,\t\t355},\n#endif\n\t{\"findfont\",\t\t\t\tPF_CL_findfont,\t\t\t\t356},\n\t{\"loadfont\",\t\t\t\tPF_CL_loadfont,\t\t\t\t357},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t//gap\n//\t{\"dynamiclight_get\",\t\tPF_R_DynamicLight_Get,\t\t372},\n//\t{\"dynamiclight_set\",\t\tPF_R_DynamicLight_Set,\t\t373},\n//\t{NULL,\t\t\t\t\t\tPF_Fixme,\t\t\t\t\t374},\n//\t{NULL,\t\t\t\t\t\tPF_Fixme,\t\t\t\t\t375},\n\t{\"setcustomskin\",\t\t\tPF_m_setcustomskin,\t\t\t376},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t//gap\n\t{\"memalloc\",\t\t\t\tPF_memalloc,\t\t\t\t384},\n\t{\"memrealloc\",\t\t\t\tPF_memrealloc,\t\t\t\t0},\n\t{\"memfree\",\t\t\t\t\tPF_memfree,\t\t\t\t\t385},\n\t{\"memcmp\",\t\t\t\t\tPF_memcmp,\t\t\t\t\t0},\n\t{\"memcpy\",\t\t\t\t\tPF_memcpy,\t\t\t\t\t386},\n\t{\"memfill8\",\t\t\t\tPF_memfill8,\t\t\t\t387},\n\t{\"memgetval\",\t\t\t\tPF_memgetval,\t\t\t\t388},\n\t{\"memsetval\",\t\t\t\tPF_memsetval,\t\t\t\t389},\n\t{\"memptradd\",\t\t\t\tPF_memptradd,\t\t\t\t390},\n\t{\"memstrsize\",\t\t\t\tPF_memstrsize,\t\t\t\t0},\n\t{\"con_getset\",\t\t\t\tPF_SubConGetSet,\t\t\t391},\n\t{\"con_printf\",\t\t\t\tPF_SubConPrintf,\t\t\t392},\n\t{\"con_draw\",\t\t\t\tPF_SubConDraw,\t\t\t\t393},\n\t{\"con_input\",\t\t\t\tPF_SubConInput,\t\t\t\t394},\n\t{\"setwindowcaption\",\t\tPF_cl_setwindowcaption,\t\t0},\n\t{\"cvars_haveunsaved\",\t\tPF_cvars_haveunsaved,\t\t0},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t//gap\n//\t{\"writebyte,\t\t\t\tPF_Fixme,\t\t\t\t\t401},\n//\t{\"writechar,\t\t\t\tPF_Fixme,\t\t\t\t\t402},\n//\t{\"writeshort,\t\t\t\tPF_Fixme,\t\t\t\t\t403},\n//\t{\"writelong,\t\t\t\tPF_Fixme,\t\t\t\t\t404},\n//\t{\"writeangle,\t\t\t\tPF_Fixme,\t\t\t\t\t405},\n//\t{\"writecoord,\t\t\t\tPF_Fixme,\t\t\t\t\t406},\n//\t{\"writestring,\t\t\t\tPF_Fixme,\t\t\t\t\t407},\n//\t{\"writeentity,\t\t\t\tPF_Fixme,\t\t\t\t\t408},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t//gap\n\t{\"buf_create\",\t\t\t\tPF_buf_create,\t\t\t\t440},\n\t{\"buf_del\",\t\t\t\t\tPF_buf_del,\t\t\t\t\t441},\n\t{\"buf_getsize\",\t\t\t\tPF_buf_getsize,\t\t\t\t442},\n\t{\"buf_copy\",\t\t\t\tPF_buf_copy,\t\t\t\t443},\n\t{\"buf_sort\",\t\t\t\tPF_buf_sort,\t\t\t\t444},\n\t{\"buf_implode\",\t\t\t\tPF_buf_implode,\t\t\t\t445},\n\t{\"bufstr_get\",\t\t\t\tPF_bufstr_get,\t\t\t\t446},\n\t{\"bufstr_set\",\t\t\t\tPF_bufstr_set,\t\t\t\t447},\n\t{\"bufstr_add\",\t\t\t\tPF_bufstr_add,\t\t\t\t448},\n\t{\"bufstr_free\",\t\t\t\tPF_bufstr_free,\t\t\t\t449},\n//\t{NULL,\t\t\t\t\t\tPF_Fixme,\t\t\t\t\t450},\n\t{\"iscachedpic\",\t\t\t\tPF_CL_is_cached_pic,\t\t451},\n\t{\"precache_pic\",\t\t\tPF_CL_precache_pic,\t\t\t452},\n\t{\"free_pic\",\t\t\t\tPF_CL_free_pic,\t\t\t\t453},\n\t{\"drawcharacter\",\t\t\tPF_CL_drawcharacter,\t\t454},\n\t{\"drawrawstring\",\t\t\tPF_CL_drawrawstring,\t\t455},\n\t{\"drawpic\",\t\t\t\t\tPF_CL_drawpic,\t\t\t\t456},\n\t{\"drawrotpic\",\t\t\t\tPF_CL_drawrotpic,\t\t\t0},\n\t{\"drawfill\",\t\t\t\tPF_CL_drawfill,\t\t\t\t457},\n\t{\"drawsetcliparea\",\t\t\tPF_CL_drawsetcliparea,\t\t458},\n\t{\"drawresetcliparea\",\t\tPF_CL_drawresetcliparea,\t459},\n\t{\"drawgetimagesize\",\t\tPF_CL_drawgetimagesize,\t\t460},\n#ifdef HAVE_MEDIA_DECODER\n\t{\"cin_open\",\t\t\t\tPF_cs_media_create,\t\t\t461},\n\t{\"cin_close\",\t\t\t\tPF_cs_media_destroy,\t\t462},\n\t{\"cin_setstate\",\t\t\tPF_cs_media_setstate,\t\t463},\n\t{\"cin_getstate\",\t\t\tPF_cs_media_getstate,\t\t464},\n\t{\"cin_restart\",\t\t\t\tPF_cs_media_restart, \t\t465},\n#endif\n\t{\"drawline\",\t\t\t\tPF_CL_drawline,\t\t\t\t466},\n\t{\"drawstring\",\t\t\t\tPF_CL_drawcolouredstring,\t467},\n\t{\"stringwidth\",\t\t\t\tPF_CL_stringwidth,\t\t\t468},\n\t{\"drawsubpic\",\t\t\t\tPF_CL_drawsubpic,\t\t\t469},\n\t{\"drawrotsubpic\",\t\t\tPF_CL_drawrotsubpic,\t\t0},\n\t{\"drawtextfield\",\t\t\tPF_CL_DrawTextField,\t\t0},\n\n#ifdef HAVE_LEGACY\n\t{\"drawrotpic_dp\",\t\t\tPF_CL_drawrotpic_dp,\t\t470},\n#endif\n//MERGES WITH CLIENT+SERVER BUILTIN MAPPINGS BELOW\n\t{\"asin\",\t\t\t\t\tPF_asin,\t\t\t\t\t471},\n\t{\"acos\",\t\t\t\t\tPF_acos,\t\t\t\t\t472},\n\t{\"atan\",\t\t\t\t\tPF_atan,\t\t\t\t\t473},\n\t{\"atan2\",\t\t\t\t\tPF_atan2,\t\t\t\t\t474},\n\t{\"tan\",\t\t\t\t\t\tPF_tan,\t\t\t\t\t\t475},\n\t{\"strlennocol\",\t\t\t\tPF_strlennocol,\t\t\t\t476},\n\t{\"strdecolorize\",\t\t\tPF_strdecolorize,\t\t\t477},\n\t{\"strftime\",\t\t\t\tPF_strftime,\t\t\t\t478},\n\t{\"tokenizebyseparator\",\t\tPF_tokenizebyseparator,\t\t479},\n\t{\"strtolower\",\t\t\t\tPF_strtolower,\t\t\t\t480},\n\t{\"strtoupper\",\t\t\t\tPF_strtoupper,\t\t\t\t481},\n\t{\"csqc_cvar_defstring\",\t\tPF_cvar_defstring,\t\t\t482},\n//\t{NULL,\t\t\t\t\t\tPF_Fixme,\t\t\t\t\t483},\n\t{\"strreplace\",\t\t\t\tPF_strreplace,\t\t\t\t484},\n\t{\"strireplace\",\t\t\t\tPF_strireplace,\t\t\t\t485},\n//\t{NULL,\t\t\t\t\t\tPF_Fixme,\t\t\t\t\t486},\n#ifdef HAVE_MEDIA_DECODER\n\t{\"gecko_create\",\t\t\tPF_cs_media_create,\t\t\t487},\n\t{\"gecko_destroy\",\t\t\tPF_cs_media_destroy,\t\t488},\n\t{\"gecko_navigate\",\t\t\tPF_cs_media_command,\t\t489},\n\t{\"gecko_keyevent\",\t\t\tPF_cs_media_keyevent,\t\t490},\n\t{\"gecko_mousemove\",\t\t\tPF_cs_media_mousemove,\t\t491},\n\t{\"gecko_resize\",\t\t\tPF_cs_media_resize,\t\t\t492},\n\t{\"gecko_get_texture_extent\",PF_cs_media_get_texture_extent,493},\n\t{\"gecko_getproperty\",\t\tPF_cs_media_getproperty,\t0},\n#endif\n\t{\"crc16\",\t\t\t\t\tPF_crc16,\t\t\t\t\t494},\n\t{\"cvar_type\",\t\t\t\tPF_cvar_type,\t\t\t\t495},\n\t{\"numentityfields\",\t\t\tPF_numentityfields,\t\t\t496},\n\t{\"findentityfield\",\t\t\tPF_findentityfield,\t\t\t0},\n\t{\"entityfieldref\",\t\t\tPF_entityfieldref,\t\t\t0},\n\t{\"entityfieldname\",\t\t\tPF_entityfieldname,\t\t\t497},\n\t{\"entityfieldtype\",\t\t\tPF_entityfieldtype,\t\t\t498},\n\t{\"getentityfieldstring\",\tPF_getentityfieldstring,\t499},\n\t{\"putentityfieldstring\",\tPF_putentityfieldstring,\t500},\n//\t{NULL,\t\t\t\t\t\tPF_Fixme,\t\t\t\t\t501},\n//\t{NULL,\t\t\t\t\t\tPF_Fixme,\t\t\t\t\t502},\n\t{\"whichpack\",\t\t\t\tPF_whichpack,\t\t\t\t503},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t//gap\n\t{\"uri_escape\",\t\t\t\tPF_uri_escape,\t\t\t\t510},\n\t{\"uri_unescape\",\t\t\tPF_uri_unescape,\t\t\t511},\n\t{\"base64encode\",\t\t\tPF_base64encode,\t\t\t0},\n\t{\"base64decode\",\t\t\tPF_base64decode,\t\t\t0},\n\t{\"num_for_edict\",\t\t\tPF_etof,\t\t\t\t\t512},\n\t{\"uri_get\",\t\t\t\t\tPF_uri_get,\t\t\t\t\t513},\n\t{\"uri_post\",\t\t\t\tPF_uri_get,\t\t\t\t\t513},\n\t{\"tokenize_console\",\t\tPF_tokenize_console,\t\t514},\n\t{\"argv_start_index\",\t\tPF_argv_start_index,\t\t515},\n\t{\"argv_end_index\",\t\t\tPF_argv_end_index,\t\t\t516},\n\t{\"buf_cvarlist\",\t\t\tPF_buf_cvarlist,\t\t\t517},\n\t{\"cvar_description\",\t\tPF_cvar_description,\t\t518},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t//gap\n\t{\"log\",\t\t\t\t\t\tPF_Logarithm,\t\t\t\t532},\n//\t{\"getsoundtime\",\t\t\tPF_Fixme,\t\t\t\t\t533},\n\t{\"soundlength\",\t\t\t\tPF_soundlength,\t\t\t\t534},\n\t{\"buf_loadfile\",\t\t\tPF_buf_loadfile,\t\t\t535},\n\t{\"buf_writefile\",\t\t\tPF_buf_writefile,\t\t\t536},\n\t{\"bufstr_find\",\t\t\t\tPF_bufstr_find,\t\t\t\t537},\n//\t{\"matchpattern\",\t\t\tPF_Fixme,\t\t\t\t\t538},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t//gap\n\t{\"setkeydest\",\t\t\t\tPF_cl_setkeydest,\t\t\t601},\n\t{\"getkeydest\",\t\t\t\tPF_cl_getkeydest,\t\t\t602},\n\t{\"setmousetarget\",\t\t\tPF_cl_setmousetarget,\t\t603},\n\t{\"getmousetarget\",\t\t\tPF_cl_getmousetarget,\t\t604},\n\t{\"callfunction\",\t\t\tPF_callfunction,\t\t\t605},\n\t{\"writetofile\",\t\t\t\tPF_writetofile,\t\t\t\t606},\n\t{\"isfunction\",\t\t\t\tPF_isfunction,\t\t\t\t607},\n\t{\"getresolution\",\t\t\tPF_cl_getresolution,\t\t608},\n\t{\"keynumtostring\",\t\t\tPF_cl_keynumtostring,\t\t609},\n\t{\"findkeysforcommand\",\t\tPF_cl_findkeysforcommand,\t610},\n\t{\"findkeysforcommandex\",\tPF_cl_findkeysforcommandex,\t0},\n\t{\"gethostcachevalue\",\t\tPF_cl_gethostcachevalue,\t611},\n\t{\"gethostcachestring\",\t\tPF_cl_gethostcachestring,\t612},\n\t{\"parseentitydata\",\t\t\tPF_parseentitydata,\t\t\t613},\n\t{\"generateentitydata\",\t\tPF_generateentitydata,\t\t0},\n\n\t{\"stringtokeynum\",\t\t\tPF_cl_stringtokeynum,\t\t614},\n\n\t{\"resethostcachemasks\",\t\tPF_cl_resethostcachemasks,\t615},\n\t{\"sethostcachemaskstring\",\tPF_cl_sethostcachemaskstring,616},\n\t{\"sethostcachemasknumber\",\tPF_cl_sethostcachemasknumber,617},\n\t{\"resorthostcache\",\t\t\tPF_cl_resorthostcache,\t\t618},\n\t{\"sethostcachesort\",\t\tPF_cl_sethostcachesort,\t\t619},\n\t{\"refreshhostcache\",\t\tPF_cl_refreshhostcache,\t\t620},\n\t{\"gethostcachenumber\",\t\tPF_cl_gethostcachenumber,\t621},\n\t{\"gethostcacheindexforkey\",\tPF_cl_gethostcacheindexforkey,622},\n\t{\"addwantedhostcachekey\",\tPF_cl_addwantedhostcachekey,623},\n#ifdef CL_MASTER\n\t{\"getextresponse\",\t\t\tPF_cl_getextresponse,\t\t624},\n#endif\n\t{\"netaddress_resolve\",\t\tPF_netaddress_resolve,\t\t625},\n\t{\"getgamedirinfo\",\t\t\tPF_cl_getgamedirinfo,\t\t626},\n#ifdef PACKAGEMANAGER\n\t{\"getpackagemanagerinfo\",\tPF_cl_getpackagemanagerinfo,0},\n#endif\n\t{\"sprintf\",\t\t\t\t\tPF_sprintf,\t\t\t\t\t627},\n//\t{NULL,\t\t\t\t\t\tPF_Fixme,\t\t\t\t\t628},\n//\t{NULL,\t\t\t\t\t\tPF_Fixme,\t\t\t\t\t629},\n\t{\"setkeybind\",\t\t\t\tPF_cl_setkeybind,\t\t\t630},\n\t{\"getbindmaps\",\t\t\t\tPF_cl_GetBindMap,\t\t\t631},\n\t{\"setbindmaps\",\t\t\t\tPF_cl_SetBindMap,\t\t\t632},\n\t{\"crypto_getkeyfp\",\t\t\tPF_crypto_getkeyfp,\t\t\t633},\n\t{\"crypto_getidfp\",\t\t\tPF_crypto_getidfp,\t\t\t634},\n\t{\"crypto_getencryptlevel\",\tPF_crypto_getencryptlevel,\t635},\n\t{\"crypto_getmykeyfp\",\t\tPF_crypto_getmykeyfp,\t\t636},\n\t{\"crypto_getmyidfp\",\t\tPF_crypto_getmyidfp,\t\t637},\n//\t{NULL,\t\t\t\t\t\tPF_Fixme,\t\t\t\t\t638},\n\t{\"digest_hex\",\t\t\t\tPF_digest_hex,\t\t\t\t639},\n\t{\"digest_ptr\",\t\t\t\tPF_digest_ptr,\t\t\t\t0},\n//\t{NULL,\t\t\t\t\t\tPF_Fixme,\t\t\t\t\t640},\n\t{\"crypto_getmyidstatus\",\tPF_crypto_getmyidstatus,\t641},\n//\t{\"coverage\",\t\t\t\tPF_Fixme,\t\t\t\t\t642},\n\t{\"crypto_getidstatus\",\t\tPF_crypto_getidstatus,\t\t643},\n//\t{NULL,\t\t\t\t\t\tPF_Fixme,\t\t\t\t\t644},\n//\t{NULL,\t\t\t\t\t\tPF_Fixme,\t\t\t\t\t645},\n//\t{NULL,\t\t\t\t\t\tPF_Fixme,\t\t\t\t\t646},\n//\t{NULL,\t\t\t\t\t\tPF_Fixme,\t\t\t\t\t647},\n//\t{NULL,\t\t\t\t\t\tPF_Fixme,\t\t\t\t\t648},\n//\t{NULL,\t\t\t\t\t\tPF_Fixme,\t\t\t\t\t649},\n\t{\"fcopy\",\t\t\t\t\tPF_fcopy,\t\t\t\t\t650},\n\t{\"frename\",\t\t\t\t\tPF_frename,\t\t\t\t\t651},\n\t{\"fremove\",\t\t\t\t\tPF_fremove,\t\t\t\t\t652},\n\t{\"fexists\",\t\t\t\t\tPF_fexists,\t\t\t\t\t653},\n\t{\"rmtree\",\t\t\t\t\tPF_rmtree,\t\t\t\t\t654},\n\n\t{\"stachievement_unlock\",\tPF_Fixme,\t\t\t\t\t730},\t// EXT_STEAM_REKI\n\t{\"stachievement_query\",\t\tPF_Fixme,\t\t\t\t\t731},\t// EXT_STEAM_REKI\n\t{\"ststat_setvalue\",\t\t\tPF_Fixme,\t\t\t\t\t732},\t// EXT_STEAM_REKI\n\t{\"ststat_increment\",\t\tPF_Fixme,\t\t\t\t\t733},\t// EXT_STEAM_REKI\n\t{\"ststat_query\",\t\t\tPF_Fixme,\t\t\t\t\t734},\t// EXT_STEAM_REKI\n\t{\"stachievement_register\",\tPF_Fixme,\t\t\t\t\t735},\t// EXT_STEAM_REKI\n\t{\"ststat_register\",\t\t\tPF_Fixme,\t\t\t\t\t736},\t// EXT_STEAM_REKI\n\t{\"controller_query\",\t\tPF_cl_gp_querywithcb,\t\t740},\t// EXT_CONTROLLER_REKI\n\t{\"controller_rumble\",\t\tPF_cl_gp_rumble,\t\t\t741},\t// EXT_CONTROLLER_REKI\n\t{\"controller_rumbletriggers\",PF_cl_gp_rumbletriggers,\t742},\t// EXT_CONTROLLER_REKI\n\n\t{\"gp_getlayout\",\t\t\tPF_cl_gp_getbuttontype,\t\t0}, // #0 float(float devid) gp_getbuttontype\n\t{\"gp_rumble\",\t\t\t\tPF_cl_gp_rumble,\t\t\t0}, // #0 void(float devid, float amp_low, float amp_high, float duration) gp_rumble\n\t{\"gp_rumbletriggers\",\t\tPF_cl_gp_rumbletriggers,\t0}, // #0 void(float devid, float left, float right, float duration) gp_rumbletriggers\n\t{\"gp_setledcolor\",\t\t\tPF_cl_gp_setledcolor,\t\t0}, // #0 void(float devid, float red, float green, float blue) gp_setledcolor\n\t{\"gp_settriggerfx\",\t\t\tPF_cl_gp_settriggerfx,\t\t0}, // #0 void(float devid, const void *data, int size) gp_settriggerfx\n\n\t{NULL}\n};\nstatic builtin_t menu_builtins[1024];\n\n\nint MP_BuiltinValid(const char *name, int num)\n{\n\tint i;\n\tfor (i = 0; BuiltinList[i].name; i++)\n\t{\n\t\tif (BuiltinList[i].ebfsnum == num)\n\t\t{\n\t\t\tif (!strcmp(BuiltinList[i].name, name))\n\t\t\t{\n\t\t\t\tif (/*BuiltinList[i].bifunc == PF_NoMenu ||*/ BuiltinList[i].bifunc == PF_Fixme)\n\t\t\t\t\treturn false;\n\t\t\t\telse\n\t\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t}\n\treturn false;\n}\n\nstatic void MP_SetupBuiltins(void)\n{\n\tint i;\n\tfor (i = 0; i < sizeof(menu_builtins)/sizeof(menu_builtins[0]); i++)\n\t\tmenu_builtins[i] = PF_Fixme;\n\tfor (i = 0; BuiltinList[i].bifunc; i++)\n\t{\n\t\tif (BuiltinList[i].ebfsnum)\n\t\t\tmenu_builtins[BuiltinList[i].ebfsnum] = BuiltinList[i].bifunc;\n\t}\n}\n\nstatic int PDECL PR_Menu_MapNamedBuiltin(pubprogfuncs_t *progfuncs, int headercrc, const char *builtinname)\n{\n\tint i, binum;\n\tfor (i = 0;BuiltinList[i].name;i++)\n\t{\n\t\tif (!strcmp(BuiltinList[i].name, builtinname) && BuiltinList[i].bifunc != PF_Fixme)\n\t\t{\n\t\t\tfor (binum = sizeof(menu_builtins)/sizeof(menu_builtins[0]); --binum; )\n\t\t\t{\n\t\t\t\tif (menu_builtins[binum] && menu_builtins[binum] != PF_Fixme && BuiltinList[i].bifunc)\n\t\t\t\t\tcontinue;\n\t\t\t\tmenu_builtins[binum] = BuiltinList[i].bifunc;\n\t\t\t\treturn binum;\n\t\t\t}\n\t\t\tCon_Printf(\"No more builtin slots to allocate for %s\\n\", builtinname);\n\t\t\tbreak;\n\t\t}\n\t}\n\tCon_DPrintf(\"Unknown menu builtin: %s\\n\", builtinname);\n\treturn 0;\n}\n\nstatic qboolean MP_MouseMove(menu_t *menu, qboolean isabs, unsigned int devid, float xdelta, float ydelta)\n{\n\tvoid *pr_globals;\n\n\tif (!menu_world.progs || !mpfuncs.inputevent)\n\t\treturn false;\n\n\tif (setjmp(mp_abort))\n\t\treturn false;\n\tinmenuprogs++;\n\tpr_globals = PR_globals(menu_world.progs, PR_CURRENT);\n\tG_FLOAT(OFS_PARM0) = isabs?CSIE_MOUSEABS:CSIE_MOUSEDELTA;\n\tG_FLOAT(OFS_PARM1) = (xdelta * vid.width) / vid.pixelwidth;\n\tG_FLOAT(OFS_PARM2) = (ydelta * vid.height) / vid.pixelheight;\n\tG_FLOAT(OFS_PARM3) = devid;\n\tPR_ExecuteProgram (menu_world.progs, mpfuncs.inputevent);\n\tif (R2D_Flush)\n\t\tR2D_Flush();\n\tinmenuprogs--;\n\treturn G_FLOAT(OFS_RETURN);\n}\n\nstatic qboolean MP_JoystickAxis(menu_t *menu, unsigned int devid, int axis, float value)\n{\n\tvoid *pr_globals;\n\tif (!menu_world.progs || !mpfuncs.inputevent)\n\t\treturn false;\n\tif (setjmp(mp_abort))\n\t\treturn false;\n\tinmenuprogs++;\n\tpr_globals = PR_globals(menu_world.progs, PR_CURRENT);\n\tG_FLOAT(OFS_PARM0) = CSIE_JOYAXIS;\n\tG_FLOAT(OFS_PARM1) = axis;\n\tG_FLOAT(OFS_PARM2) = value;\n\tG_FLOAT(OFS_PARM3) = devid;\n\tPR_ExecuteProgram (menu_world.progs, mpfuncs.inputevent);\n\tif (R2D_Flush)\n\t\tR2D_Flush();\n\tinmenuprogs--;\n\treturn G_FLOAT(OFS_RETURN);\n}\nstatic qboolean MP_KeyEvent(menu_t *menu, qboolean isdown, unsigned int devid, int key, int unicode)\n{\n\tqboolean result;\n\n#ifdef TEXTEDITOR\n\tif (editormodal)\n\t\treturn false;\n#endif\n\n\tif (setjmp(mp_abort))\n\t\treturn true;\n\n\tif (isdown)\n\t{\n#ifndef NOBUILTINMENUS\n#ifdef _DEBUG\n\t\tif (key == 'c')\n\t\t{\n\t\t\tif ((keydown[K_LCTRL] || keydown[K_RCTRL]) && (keydown[K_LSHIFT] || keydown[K_RSHIFT]))\n\t\t\t{\n\t\t\t\tMP_Shutdown();\n\t\t\t\tM_Init_Internal();\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n#endif\n#endif\n\n\t\tmpkeysdown[key>>3] |= (1<<(key&7));\n\t}\n\telse\n\t{\t//don't fire up events if it was not actually pressed.\n\t\tif (key && !(mpkeysdown[key>>3] & (1<<(key&7))))\n\t\t\treturn false;\n\t\tmpkeysdown[key>>3] &= ~(1<<(key&7));\n\t}\n\n\tmenutime = Sys_DoubleTime();\n\tif (menu_world.g.time)\n\t\t*menu_world.g.time = menutime;\n\n\tinmenuprogs++;\n\tif (mpfuncs.inputevent)\n\t{\n\t\tvoid *pr_globals = PR_globals(menu_world.progs, PR_CURRENT);\n\t\tG_FLOAT(OFS_PARM0) = isdown?CSIE_KEYDOWN:CSIE_KEYUP;\n\t\tG_FLOAT(OFS_PARM1) = MP_TranslateFTEtoQCCodes(key);\n\t\tG_FLOAT(OFS_PARM2) = unicode;\n\t\tG_FLOAT(OFS_PARM3) = devid;\n\t\tif (isdown)\n\t\t{\n\t\t\tqcinput_scan = G_FLOAT(OFS_PARM1);\n\t\t\tqcinput_unicode = G_FLOAT(OFS_PARM2);\n\t\t}\n\t\tPR_ExecuteProgram(menu_world.progs, mpfuncs.inputevent);\n\t\tresult = G_FLOAT(OFS_RETURN);\n\t\tif (!result && key == K_TOUCH)\n\t\t{\t//convert touches to mouse, for compat. gestures are untranslated but expected to be ignored.\n\t\t\tG_FLOAT(OFS_PARM0) = isdown?CSIE_KEYDOWN:CSIE_KEYUP;\n\t\t\tG_FLOAT(OFS_PARM1) = MP_TranslateFTEtoQCCodes(K_MOUSE1);\n\t\t\tG_FLOAT(OFS_PARM2) = unicode;\n\t\t\tG_FLOAT(OFS_PARM3) = devid;\n\t\t\tPR_ExecuteProgram(menu_world.progs, mpfuncs.inputevent);\n\t\t\tresult = G_FLOAT(OFS_RETURN);\n\t\t}\n\t\tqcinput_scan = 0;\n\t\tqcinput_unicode = 0;\n\t}\n\telse if (isdown && mpfuncs.keydown)\n\t{\n\t\tvoid *pr_globals = PR_globals(menu_world.progs, PR_CURRENT);\n\t\tif (key == K_TOUCH)\n\t\t\tkey = K_MOUSE1;\t//old api doesn't expect touches nor provides feedback for emulation. make sure stuff works.\n\t\tG_FLOAT(OFS_PARM0) = MP_TranslateFTEtoQCCodes(key);\n\t\tG_FLOAT(OFS_PARM1) = unicode;\n\t\tPR_ExecuteProgram(menu_world.progs, mpfuncs.keydown);\n\t\tresult = true;\t//doesn't have a return value, so if the menu is set up for key events, all events are considered eaten.\n\t}\n\telse if (!isdown && mpfuncs.keyup)\n\t{\n\t\tvoid *pr_globals = PR_globals(menu_world.progs, PR_CURRENT);\n\t\tif (key == K_TOUCH)\n\t\t\tkey = K_MOUSE1;\t//old api doesn't expect touches.\n\t\tG_FLOAT(OFS_PARM0) = MP_TranslateFTEtoQCCodes(key);\n\t\tG_FLOAT(OFS_PARM1) = unicode;\n\t\tPR_ExecuteProgram(menu_world.progs, mpfuncs.keyup);\n\t\tresult = false; // doesn't have a return value, so don't block it\n\t}\n\telse\n\t\tresult = false;\n\tinmenuprogs--;\n\n\tif (R2D_Flush)\t//shouldn't be needed, but in case the mod is buggy.\n\t\tR2D_Flush();\n\treturn result;\n}\n\nvoid MP_Shutdown (void)\n{\n\tfunc_t temp;\n\tif (!menu_world.progs)\n\t\treturn;\n\n\tmenuqc.release = NULL; //don't notify\n\tMenu_Unlink(&menuqc, false);\n/*\n\t{\n\t\tchar *buffer;\n\t\tint size = 1024*1024*8;\n\t\tbuffer = Z_Malloc(size);\n\t\tmenuprogs->save_ents(menuprogs, buffer, &size, 1);\n\t\tCOM_WriteFile(\"menucore.txt\", buffer, size);\n\t\tZ_Free(buffer);\n\t}\n*/\n\ttemp = mpfuncs.shutdown;\n\tmpfuncs.shutdown = 0;\n\tif (temp && !inmenuprogs)\n\t\tPR_ExecuteProgram(menu_world.progs, temp);\n\n\tPR_Common_Shutdown(menu_world.progs, false);\n\tmenu_world.progs->Shutdown(menu_world.progs);\n\tmemset(&menu_world, 0, sizeof(menu_world));\n\tPR_ReleaseFonts(kdm_menu);\n\n#ifdef CL_MASTER\n\tMaster_ClearMasks();\n#endif\n\n\tCmd_RemoveCommands(MP_ConsoleCommand_f);\n\n\tKey_Dest_Remove(kdm_menu);\n\tkey_dest_absolutemouse &= ~kdm_menu;\n}\n\nstatic void MP_TryRelease(menu_t *m, qboolean force)\n{\n\tif (inmenuprogs)\n\t\treturn;\t//if the qc asked for it, the qc probably already knows about it. don't recurse.\n\tMP_Toggle(0);\n\n\tif (force && Menu_IsLinked(m))\n\t{\n\t\tCon_Printf(CON_ERROR\"Menuqc retook key grabs when ordered to yield. Killing.\\n\");\n\t\tMP_Shutdown();\n\t\tM_Init_Internal();\n\t}\n}\n\nvoid *VARGS PR_CB_Malloc(int size);\t//these functions should be tracked by the library reliably, so there should be no need to track them ourselves.\nvoid VARGS PR_CB_Free(void *mem);\n\n//Any menu builtin error or anything like that will come here.\nvoid VARGS Menu_Abort (const char *format, ...)\n{\n\tva_list\t\targptr;\n\tchar\t\tstring[1024];\n\n\tva_start (argptr, format);\n\tvsnprintf (string,sizeof(string)-1, format,argptr);\n\tva_end (argptr);\n\n\tCon_Printf(\"Menu_Abort: %s\\nShutting down menu.dat\\n\", string);\n\n\tif (pr_menu_coreonerror.value)\n\t{\n\t\tchar *buffer;\n\t\tsize_t size = 1024*1024*8;\n\t\tbuffer = Z_Malloc(size);\n\t\tmenu_world.progs->save_ents(menu_world.progs, buffer, &size, size, 3);\n\t\tCOM_WriteFile(\"menucore.txt\", FS_GAMEONLY, buffer, size);\n\t\tZ_Free(buffer);\n\t}\n\n\tMP_Shutdown();\n\tM_Init_Internal();\n\n\tif (inmenuprogs)\t//something in the menu caused the problem, so...\n\t{\n\t\tinmenuprogs = 0;\n\t\tlongjmp(mp_abort, 1);\n\t}\n}\n\nvoid MP_CvarChanged(cvar_t *var)\n{\n\tif (menu_world.progs)\n\t{\n\t\tPR_AutoCvar(menu_world.progs, var);\n\t}\n}\n\npbool PDECL Menu_CheckHeaderCrc(pubprogfuncs_t *inst, progsnum_t idx, int crc, const char *filename)\n{\n\tif (crc == 10020)\n\t\treturn true;\t//its okay\n\tCon_Printf(\"progs crc is invalid for %s\\n\", filename);\n\tif (crc == 12776)\n\t{\t//whoever wrote wrath fucked up.\n\t\tCon_Printf(\"(please correct .src include orders)\\n\");\n\t\treturn true;\n\t}\n\treturn false;\n}\n\nstatic void *PDECL MP_PRReadFile (const char *path, qbyte *(PDECL *buf_get)(void *buf_ctx, size_t size), void *buf_ctx, size_t *size, pbool issource)\n{\n\tflocation_t loc;\n\tif (FS_FLocateFile(path, FSLF_IFFOUND|FSLF_SECUREONLY, &loc))\n\t{\n\t\tqbyte *buffer = NULL;\n\t\tvfsfile_t *file = FS_OpenReadLocation(path, &loc);\n\t\tif (file)\n\t\t{\n\t\t\t*size = loc.len;\n\t\t\tbuffer = buf_get(buf_ctx, *size);\n\t\t\tif (buffer)\n\t\t\t\tVFS_READ(file, buffer, *size);\n\t\t\tVFS_CLOSE(file);\n\t\t}\n\t\treturn buffer;\n\t}\n\telse\n\t{\n\t\tif (FS_FLocateFile(path, FSLF_IFFOUND, &loc))\n\t\t\tCon_Printf(\"Not loading %s because it comes from an untrusted source\\n\", path);\n\t\treturn NULL;\n\t}\n}\nstatic int PDECL MP_PRFileSize (const char *path)\n{\n\tflocation_t loc;\n\tif (FS_FLocateFile(path, FSLF_IFFOUND|FSLF_SECUREONLY, &loc))\n\t\treturn loc.len;\n\telse\n\t\treturn -1;\n}\n\nqboolean MP_Init (void)\n{\n\tstruct key_cursor_s *m = &key_customcursor[kc_menuqc];\n\n\tif (qrenderer == QR_NONE)\n\t{\n\t\treturn false;\n\t}\n\n\tif (forceqmenu.value)\n\t{\n\t\tCon_DPrintf(\"menu.dat disabled\\n\");\n\t\treturn false;\n\t}\n\n\tMP_SetupBuiltins();\n\n\tmemset(&menuc_eval, 0, sizeof(menuc_eval));\n\n\n\tmenuprogparms.progsversion = PROGSTRUCT_VERSION;\n\tmenuprogparms.ReadFile = MP_PRReadFile;//char *(*ReadFile) (char *fname, void *buffer, int *len);\n\tmenuprogparms.FileSize = MP_PRFileSize;//int (*FileSize) (char *fname);\t//-1 if file does not exist\n\tmenuprogparms.WriteFile = QC_WriteFile;//bool (*WriteFile) (char *name, void *data, int len);\n\tmenuprogparms.Printf = PR_Printf;//Con_Printf;//void (*printf) (char *, ...);\n\tmenuprogparms.DPrintf = PR_DPrintf;//Con_DPrintf;//void (*dprintf) (char *, ...);\n\tmenuprogparms.Sys_Error = Sys_Error;\n\tmenuprogparms.Abort = Menu_Abort;\n\tmenuprogparms.CheckHeaderCrc = Menu_CheckHeaderCrc;\n\tmenuprogparms.edictsize = sizeof(menuedict_t);\n\n\tmenuprogparms.entspawn = NULL;//void (*entspawn) (struct edict_s *ent);\t//ent has been spawned, but may not have all the extra variables (that may need to be set) set\n\tmenuprogparms.entcanfree = NULL;//bool (*entcanfree) (struct edict_s *ent);\t//return true to stop ent from being freed\n\tmenuprogparms.stateop = NULL;//StateOp;//void (*stateop) (float var, func_t func);\n\tmenuprogparms.cstateop = NULL;//CStateOp;\n\tmenuprogparms.cwstateop = NULL;//CWStateOp;\n\tmenuprogparms.thinktimeop = NULL;//ThinkTimeOp;\n\tmenuprogparms.MapNamedBuiltin = PR_Menu_MapNamedBuiltin;\n\tmenuprogparms.loadcompleate = NULL;//void (*loadcompleate) (int edictsize);\t//notification to reset any pointers.\n\n\tmenuprogparms.memalloc = PR_CB_Malloc;//void *(*memalloc) (int size);\t//small string allocation\tmalloced and freed randomly\n\tmenuprogparms.memfree = PR_CB_Free;//void (*memfree) (void * mem);\n\n\n\tmenuprogparms.globalbuiltins = menu_builtins;//builtin_t *globalbuiltins;\t//these are available to all progs\n\tmenuprogparms.numglobalbuiltins = sizeof(menu_builtins) / sizeof(menu_builtins[0]);\n\n\tmenuprogparms.autocompile = PR_COMPILEIGNORE;//PR_COMPILEEXISTANDCHANGED;//enum {PR_NOCOMPILE, PR_COMPILENEXIST, PR_COMPILECHANGED, PR_COMPILEALWAYS} autocompile;\n\n\tmenuprogparms.gametime = &menutime;\n#ifdef MULTITHREAD\n\tmenuprogparms.usethreadedgc = pr_gc_threaded.ival;\n#endif\n\n\tmenuprogparms.edicts = (struct edict_s **)&menu_edicts;\n\tmenuprogparms.num_edicts = &num_menu_edicts;\n\n\tmenuprogparms.useeditor = QCEditor;//void (*useeditor) (char *filename, int line, int nump, char **parms);\n\tmenuprogparms.user = &menu_world;\n\tmenu_world.keydestmask = kdm_menu;\n\n\t//default to free mouse+hidden cursor, to match dp's default setting, and because its generally the right thing for a menu.\n\tQ_strncpyz(m->name, \"none\", sizeof(m->name));\n\tm->hotspot[0] = 0;\n\tm->hotspot[1] = 0;\n\tm->scale = 1;\n\tm->dirty = true;\n\n\tmenuqc.cursor = m;\n\tmenuqc.drawmenu = NULL;\t\t//menuqc sucks!\n\tmenuqc.mousemove = MP_MouseMove;\n\tmenuqc.keyevent = MP_KeyEvent;\n\tmenuqc.joyaxis = MP_JoystickAxis;\n\tmenuqc.release = MP_TryRelease;\n\n\tmenutime = Sys_DoubleTime();\n\tif (!menu_world.progs)\n\t{\n\t\tvec3_t fwd,rht,up;\n\t\tint mprogs;\n\t\tCon_DPrintf(\"Initializing menu.dat\\n\");\n\t\tmenu_world.Get_CModel = MP_GetCModel;\n\t\tmenu_world.Get_FrameState = MP_Get_FrameState;\n\n\t\tmenu_world.progs = InitProgs(&menuprogparms);\n\t\tPR_Configure(menu_world.progs, PR_ReadBytesString(pr_menu_memsize.string), 16, pr_enable_profiling.ival);\n\t\tmprogs = PR_LoadProgs(menu_world.progs, \"menu.dat\");\n\t\tif (mprogs < 0) //no per-progs builtins.\n\t\t{\n\t\t\t//failed to load or something\n//\t\t\tCloseProgs(menu_world.progs);\n//\t\t\tmenuprogs = NULL;\n\t\t\treturn false;\n\t\t}\n\t\tif (setjmp(mp_abort))\n\t\t{\n\t\t\tCon_DPrintf(\"Failed to initialize menu.dat\\n\");\n\t\t\tinmenuprogs = false;\n\t\t\treturn false;\n\t\t}\n\t\tinmenuprogs++;\n\n\t\tM_DeInit_Internal();\n\n\t\tPF_InitTempStrings(menu_world.progs);\n\n\t\tmenu_world.g.self = (int*)PR_FindGlobal(menu_world.progs, \"self\", 0, NULL);\n\t\tmenu_world.g.time = (float*)PR_FindGlobal(menu_world.progs, \"time\", 0, NULL);\n\t\tif (menu_world.g.time)\n\t\t\t*menu_world.g.time = Sys_DoubleTime();\n\t\tmenu_world.g.frametime = (float*)PR_FindGlobal(menu_world.progs, \"frametime\", 0, NULL);\n\n\t\tmenu_world.g.v_forward\t= (float*)PR_FindGlobal(menu_world.progs, \"v_forward\",\t0, NULL); if (!menu_world.g.v_forward)\tmenu_world.g.v_forward = fwd;\n\t\tmenu_world.g.v_right\t= (float*)PR_FindGlobal(menu_world.progs, \"v_right\",\t0, NULL); if (!menu_world.g.v_right)\tmenu_world.g.v_right = rht;\n\t\tmenu_world.g.v_up\t\t= (float*)PR_FindGlobal(menu_world.progs, \"v_up\",\t\t0, NULL); if (!menu_world.g.v_up)\t\tmenu_world.g.v_up = up;\n\n\t\tmenu_world.g.drawfont = (float*)PR_FindGlobal(menu_world.progs, \"drawfont\", 0, NULL);\n\t\tmenu_world.g.drawfontscale = (float*)PR_FindGlobal(menu_world.progs, \"drawfontscale\", 0, NULL);\n\n\t\tPR_ProgsAdded(menu_world.progs, mprogs, \"menu.dat\");\n\n\t\t//ensure that there's space for these fields in.\n\t\t//other fields will always be referenced/defined by the qc, or 0.\n\t\tPR_RegisterFieldVar(menu_world.progs, ev_string, \"model\", -1, -1);\n\t\tPR_RegisterFieldVar(menu_world.progs, ev_vector, \"origin\", -1, -1);\n\t\tPR_RegisterFieldVar(menu_world.progs, ev_float, \"skinobject\", -1, -1);\n\n\t\tmenuentsize = PR_InitEnts(menu_world.progs, 8192);\n\n\n\t\t//'world' edict\n//\t\tEDICT_NUM_PB(menu_world.progs, 0)->readonly = true;\n\t\tEDICT_NUM_PB(menu_world.progs, 0)->ereftype = ER_ENTITY;\n\n\n\t\tmpfuncs.init = PR_FindFunction(menu_world.progs, \"m_init\", PR_ANY);\n\t\tmpfuncs.shutdown = PR_FindFunction(menu_world.progs, \"m_shutdown\", PR_ANY);\n\t\t{\n\t\t\tint args = 0;\n\t\t\tqbyte *argsizes = NULL;\n\t\t\tmpfuncs.draw = PR_FindFunction(menu_world.progs, \"m_draw\", PR_ANY);\n\t\t\tmenu_world.progs->GetFunctionInfo(menu_world.progs, mpfuncs.draw, &args, &argsizes, NULL, NULL, 0);\n\t\t\tmpfuncs.fuckeddrawsizes = (args == 2 && argsizes[0] == 1 && argsizes[1] == 1);\n\t\t}\n\t\tmpfuncs.drawloading = PR_FindFunction(menu_world.progs, \"m_drawloading\", PR_ANY);\n\t\tmpfuncs.inputevent = PR_FindFunction(menu_world.progs, \"Menu_InputEvent\", PR_ANY);\n\t\tmpfuncs.keydown = PR_FindFunction(menu_world.progs, \"m_keydown\", PR_ANY);\n\t\tmpfuncs.keyup = PR_FindFunction(menu_world.progs, \"m_keyup\", PR_ANY);\n\t\tmpfuncs.toggle = PR_FindFunction(menu_world.progs, \"m_toggle\", PR_ANY);\n\t\tmpfuncs.consolecommand = PR_FindFunction(menu_world.progs, \"m_consolecommand\", PR_ANY);\n\t\tmpfuncs.gethostcachecategory = PR_FindFunction(menu_world.progs, \"m_gethostcachecategory\", PR_ANY);\n\t\tmpfuncs.rendererrestarted = PR_FindFunction(menu_world.progs, \"Menu_RendererRestarted\", PR_ANY);\n\t\tif (mpfuncs.init)\n\t\t\tPR_ExecuteProgram(menu_world.progs, mpfuncs.init);\n\t\tinmenuprogs--;\n\n\t\tEDICT_NUM_PB(menu_world.progs, 0)->readonly = true;\n\n\t\tCon_DPrintf(\"Initialized menu.dat\\n\");\n\t\treturn true;\n\t}\n\treturn false;\n}\n\nstatic void MP_GameCommand_f(void)\n{\n\tvoid *pr_globals;\n\tfunc_t gamecommand;\n\tif (!menu_world.progs)\n\t\treturn;\n\tgamecommand = PR_FindFunction(menu_world.progs, \"GameCommand\", PR_ANY);\n\tif (!gamecommand)\n\t\treturn;\n\n\tif (setjmp(mp_abort))\n\t\treturn;\n\tinmenuprogs++;\n\tpr_globals = PR_globals(menu_world.progs, PR_CURRENT);\n\t(((string_t *)pr_globals)[OFS_PARM0] = PR_TempString(menu_world.progs, Cmd_Args()));\n\tPR_ExecuteProgram (menu_world.progs, gamecommand);\n\tinmenuprogs--;\n}\n\nqboolean MP_ConsoleCommand(const char *cmdtext)\n{\n\tvoid *pr_globals;\n\tif (!menu_world.progs)\n\t\treturn false;\n\tif (!mpfuncs.consolecommand)\n\t\treturn false;\n\n\tif (setjmp(mp_abort))\n\t\treturn true;\n\tinmenuprogs++;\n\tpr_globals = PR_globals(menu_world.progs, PR_CURRENT);\n\t(((string_t *)pr_globals)[OFS_PARM0] = PR_TempString(menu_world.progs, cmdtext));\n\tPR_ExecuteProgram (menu_world.progs, mpfuncs.consolecommand);\n\tinmenuprogs--;\n\treturn G_FLOAT(OFS_RETURN);\n}\n\nvoid MP_CoreDump_f(void)\n{\n\tif (Cmd_IsInsecure())\n\t{\n\t\tCon_TPrintf(\"Refusing to execute insecure %s\\n\", Cmd_Argv(0));\n\t\treturn;\n\t}\n\tif (!menu_world.progs)\n\t{\n\t\tCon_Printf(\"Can't core dump, you need to be running the MenuQC progs first.\");\n\t\treturn;\n\t}\n\n\t{\n\t\tsize_t size = 1024*1024*8;\n\t\tchar *buffer = BZ_Malloc(size);\n\t\tmenu_world.progs->save_ents(menu_world.progs, buffer, &size, size, 3);\n\t\tCOM_WriteFile(\"menucore.txt\", FS_GAMEONLY, buffer, size);\n\t\tBZ_Free(buffer);\n\t}\n}\n\nstatic void MP_Poke_f(void)\n{\n\tif (Cmd_IsInsecure())\n\t\tCon_TPrintf(\"Refusing to execute insecure %s\\n\", Cmd_Argv(0));\n\t/*else if (!SV_MayCheat())\n\t\tCon_TPrintf (\"Please set sv_cheats 1 and restart the map first.\\n\");*/\n\telse if (menu_world.progs && menu_world.progs->EvaluateDebugString)\n\t\tCon_TPrintf(\"Result: %s\\n\", menu_world.progs->EvaluateDebugString(menu_world.progs, Cmd_Args()));\n\telse\n\t\tCon_TPrintf (\"not supported.\\n\");\n}\n\nstatic void MP_Breakpoint_f(void)\n{\n\tint wasset;\n\tint isset;\n\tchar *filename = Cmd_Argv(1);\n\tint line = atoi(Cmd_Argv(2));\n\n\tif (Cmd_IsInsecure())\n\t{\n\t\tCon_TPrintf(\"Refusing to execute insecure %s\\n\", Cmd_Argv(0));\n\t\treturn;\n\t}\n\tif (!menu_world.progs)\n\t{\n\t\tCon_Printf(\"Menu not running\\n\");\n\t\treturn;\n\t}\n\twasset = menu_world.progs->ToggleBreak(menu_world.progs, filename, line, 3);\n\tisset = menu_world.progs->ToggleBreak(menu_world.progs, filename, line, 2);\n\n\tif (wasset == isset)\n\t\tCon_Printf(\"Breakpoint was not valid\\n\");\n\telse if (isset)\n\t\tCon_Printf(\"Breakpoint has been set\\n\");\n\telse\n\t\tCon_Printf(\"Breakpoint has been cleared\\n\");\n\n\tCvar_Set(Cvar_FindVar(\"pr_debugger\"), \"1\");\n}\nstatic void MP_Watchpoint_f(void)\n{\n\tchar *variable = Cmd_Argv(1);\n\tif (!*variable)\n\t\tvariable = NULL;\n\n\tif (Cmd_IsInsecure())\n\t{\n\t\tCon_TPrintf(\"Refusing to execute insecure %s\\n\", Cmd_Argv(0));\n\t\treturn;\n\t}\n\tif (!menu_world.progs)\n\t{\n\t\tCon_Printf(\"menuqc not running\\n\");\n\t\treturn;\n\t}\n\tif (menu_world.progs->SetWatchPoint(menu_world.progs, variable, variable))\n\t\tCon_Printf(\"Watchpoint set\\n\");\n\telse\n\t\tCon_Printf(\"Watchpoint cleared\\n\");\n}\nstatic void MP_Profile_f(void)\n{\n\tif (Cmd_IsInsecure())\n\t{\n\t\tCon_TPrintf(\"Refusing to execute insecure %s\\n\", Cmd_Argv(0));\n\t\treturn;\n\t}\n\tif (menu_world.progs && menu_world.progs->DumpProfile)\n\t\tif (!menu_world.progs->DumpProfile(menu_world.progs, !atof(Cmd_Argv(1))))\n\t\t\tCon_Printf(\"Enabled menuqc Profiling.\\n\");\n}\n\nvoid MP_RegisterCvarsAndCmds(void)\n{\n\tCmd_AddCommand(\"coredump_menuqc\", MP_CoreDump_f);\n\tCmd_AddCommand(\"menu_cmd\", MP_GameCommand_f);\n\tCmd_AddCommand(\"breakpoint_menuqc\", MP_Breakpoint_f);\n\tCmd_AddCommand(\"watchpoint_menuqc\", MP_Watchpoint_f);\n#ifdef HAVE_LEGACY\n\tCmd_AddCommand(\"loadfont\", CL_LoadFont_f);\n#endif\n\n\tCmd_AddCommand(\"poke_menuqc\", MP_Poke_f);\n\tCmd_AddCommand(\"profile_menuqc\", MP_Profile_f);\n\n\n\tCvar_Register(&forceqmenu, MENUPROGSGROUP);\n\tCvar_Register(&pr_menu_coreonerror, MENUPROGSGROUP);\n\tCvar_Register(&pr_menu_memsize, MENUPROGSGROUP);\n}\n\nqboolean MP_UsingGamecodeLoadingScreen(void)\n{\n\treturn menu_world.progs && mpfuncs.drawloading;\n}\n\nint MP_GetServerCategory(int index)\n{\n\tint category = 0;\n\tif (menu_world.progs && mpfuncs.gethostcachecategory)\n\t{\n\t\tvoid *pr_globals = PR_globals(menu_world.progs, PR_CURRENT);\n\t\tif (!setjmp(mp_abort))\n\t\t{\n\t\t\tinmenuprogs++;\n\t\t\tG_FLOAT(OFS_PARM0) = index;\n\t\t\tPR_ExecuteProgram(menu_world.progs, mpfuncs.gethostcachecategory);\n\t\t\tcategory = G_FLOAT(OFS_RETURN);\n\t\t\tinmenuprogs--;\n\t\t}\n\t}\n\treturn category;\n}\n\nvoid MP_RendererRestarted(void)\n{\n\tint i;\n\tif (!menu_world.progs)\n\t\treturn;\n\n\tmenu_world.worldmodel = cl.worldmodel;\n\n\tfor (i = 0; i < MAX_CSMODELS; i++)\n\t{\n\t\tcl.model_csqcprecache[i] = NULL;\n\t}\n\n\t//FIXME: registered shaders\n\n\t//let the csqc know that its rendertargets got purged\n\tif (mpfuncs.rendererrestarted)\n\t{\n\t\tvoid *pr_globals = PR_globals(menu_world.progs, PR_CURRENT);\n\t\t(((string_t *)pr_globals)[OFS_PARM0] = PR_TempString(menu_world.progs, rf->description));\n\t\tPR_ExecuteProgram(menu_world.progs, mpfuncs.rendererrestarted);\n\t}\n\t//in case it drew to any render targets.\n\tif (R2D_Flush)\n\t\tR2D_Flush();\n\tif (*r_refdef.rt_destcolour[0].texname)\n\t{\n\t\tQ_strncpyz(r_refdef.rt_destcolour[0].texname, \"\", sizeof(r_refdef.rt_destcolour[0].texname));\n\t\tBE_RenderToTextureUpdate2d(true);\n\t}\n}\n\nvoid MP_Draw(void)\n{\n\textern qboolean scr_drawloading;\n\tglobalvars_t *pr_globals;\n\tif (!menu_world.progs)\n\t\treturn;\n\tif (setjmp(mp_abort))\n\t\treturn;\n\n\tmenutime = Sys_DoubleTime();\n\tif (menu_world.g.time)\n\t\t*menu_world.g.time = menutime;\n\tif (menu_world.g.frametime)\n\t\t*menu_world.g.frametime = host_frametime;\n\n\tinmenuprogs++;\n\tPR_RunThreads(&menu_world);\n\n\tpr_globals = PR_globals(menu_world.progs, PR_CURRENT);\n\n\tif (scr_drawloading||scr_disabled_for_loading)\n\t{\t//don't draw the menu if we're meant to be drawing a loading screen\n\t\t//the menu should provide a special function if it wants to draw custom loading screens. this is for compat with old/dp/lazy/crappy menus.\n\t\tif (mpfuncs.drawloading)\n\t\t{\n\t\t\t((float *)pr_globals)[OFS_PARM0+0] = vid.width;\n\t\t\t((float *)pr_globals)[OFS_PARM0+1] = vid.height;\n\t\t\t((float *)pr_globals)[OFS_PARM0+2] = 0;\n\n\t\t\t((float *)pr_globals)[OFS_PARM1] = scr_disabled_for_loading;\n\t\t\tPR_ExecuteProgram(menu_world.progs, mpfuncs.drawloading);\n\t\t}\n\t}\n\telse if (mpfuncs.draw)\n\t{\n\t\tif (mpfuncs.fuckeddrawsizes)\n\t\t{\t//pass useless sizes in two args if its a dp menu\n\t\t\t((float *)pr_globals)[OFS_PARM0] = vid.pixelwidth;\n\t\t\t((float *)pr_globals)[OFS_PARM0+1] = 0;\t//make sure its set, just in case...\n\t\t\t((float *)pr_globals)[OFS_PARM0+2] = 0;\n\t\t\t((float *)pr_globals)[OFS_PARM1] = vid.pixelheight;\n\t\t\t((float *)pr_globals)[OFS_PARM1+1] = 0;\n\t\t\t((float *)pr_globals)[OFS_PARM1+2] = 0;\n\t\t}\n\t\telse\n\t\t{\t//pass useful sizes in a 1-arg vector if its an fte menu.\n\t\t\t((float *)pr_globals)[OFS_PARM0+0] = vid.width;\n\t\t\t((float *)pr_globals)[OFS_PARM0+1] = vid.height;\n\t\t\t((float *)pr_globals)[OFS_PARM0+2] = 0;\n\n\t\t\t//make physical pixel counts available too, because we can.\n\t\t\t((float *)pr_globals)[OFS_PARM1+0] = vid.pixelwidth;\n\t\t\t((float *)pr_globals)[OFS_PARM1+1] = vid.pixelheight;\n\t\t\t((float *)pr_globals)[OFS_PARM1+2] = 0;\n\t\t}\n\n\t\tPR_ExecuteProgram(menu_world.progs, mpfuncs.draw);\n\t}\n\tinmenuprogs--;\n}\n\nqboolean MP_Toggle(int mode)\n{\n\tif (!menu_world.progs)\n\t\treturn false;\n#ifdef TEXTEDITOR\n\tif (editormodal)\n\t\treturn false;\n#endif\n\n\tif (!mode && !Key_Dest_Has(kdm_menu))\n\t\treturn false;\n\n\tif (setjmp(mp_abort))\n\t\treturn false;\n\n\tmenutime = Sys_DoubleTime();\n\tif (menu_world.g.time)\n\t\t*menu_world.g.time = menutime;\n\n\tinmenuprogs++;\n\tif (mpfuncs.toggle)\n\t{\n\t\tvoid *pr_globals = PR_globals(menu_world.progs, PR_CURRENT);\n\t\tG_FLOAT(OFS_PARM0) = mode;\n\t\tPR_ExecuteProgram(menu_world.progs, mpfuncs.toggle);\n\t}\n\tif (R2D_Flush)\n\t\tR2D_Flush();\n\tinmenuprogs--;\n\n\treturn true;\n}\n#endif\n"
  },
  {
    "path": "engine/client/pr_skelobj.c",
    "content": "/*\r\nCopyright (C) 2011 Id Software, Inc.\r\n\r\nThis program is free software; you can redistribute it and/or\r\nmodify it under the terms of the GNU General Public License\r\nas published by the Free Software Foundation; either version 2\r\nof the License, or (at your option) any later version.\r\n\r\nThis program is distributed in the hope that it will be useful,\r\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\r\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\r\n\r\nSee the GNU General Public License for more details.\r\n\r\nYou should have received a copy of the GNU General Public License\r\nalong with this program; if not, write to the Free Software\r\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\r\n\r\n*/\r\n/*\r\nthis file deals with qc builtins to apply custom skeletal blending (skeletal objects extension), as well as the logic required to perform realtime ragdoll, if I ever implement that.\r\n*/\r\n\r\n/*\r\nskeletal objects are just a set of bone poses.\r\nthey are separate from the entity they are attached to, and must be created/destroyed separately.\r\ntypically the bones are all stored relative to their parent.\r\nqc must use skel_build to copy animation data from the file format into the skeletal object for rendering, but can build different bones using different animations or can override explicit bones.\r\n\r\nragdoll file is a description of the joints in the ragdoll.\r\na skeletal object, built from a doll instead of a model, has a series of physics objects created at key points (ie: not face).\r\nthese objects are absolute\r\nqc must build the skeletal object still, which fills the skeletal object from the physics objects instead of animation data, for bones that have solid objects.\r\n*/\r\n\r\n#include \"quakedef.h\"\r\n\r\n#if defined(RAGDOLL) || defined(SKELETALOBJECTS)\r\n\r\n#include \"pr_common.h\"\r\n#include \"com_mesh.h\"\r\n\r\n#define MAX_SKEL_OBJECTS 1024\r\n\r\n#ifdef RAGDOLL\r\n/*this is the description of the ragdoll, it is how the doll flops around*/\r\ntypedef struct doll_s\r\n{\r\n\tchar *name;\r\n\tint uses;\r\n\tmodel_t *model;\r\n\tstruct doll_s *next;\r\n\r\n\tqboolean drawn:1;\r\n\tint refanim; //-1 for skining pose. otherwise the first pose of the specified anim. probaly 0.\r\n\tfloat refanimtime; //usually just 0\r\n\tint numdefaultanimated;\r\n\tint numbodies;\r\n\tint numjoints;\r\n\tint numbones;\r\n\trbebodyinfo_t *body;\r\n\trbejointinfo_t *joint;\r\n\tstruct\r\n\t{\r\n\t\t//easy lookup table for bone->body.\r\n\t\t//most of these will be -1, which means 'import from animation object'\r\n\t\tint bodyidx;\r\n\t} *bone;\r\n} doll_t;\r\n\r\ntypedef struct\r\n{\r\n\trbebody_t odebody;\r\n\r\n//\tint ownerent;\t/*multiple of 12*/\r\n//\tint flags;\r\n\r\n//\tfloat moment[12];\r\n\tfloat animstrength;\r\n\tfloat animmatrix[12];\r\n} body_t;\r\n#endif\r\n\r\n/*this is the skeletal object*/\r\ntypedef struct skelobject_s\r\n{\r\n\tint inuse;\r\n\r\n\tint modelindex; //for vid_restarts\r\n\tmodel_t *model;\r\n\tworld_t *world; /*be it ssqc or csqc*/\r\n\tskeltype_t type;\r\n\r\n\tunsigned int numbones;\r\n\tfloat *bonematrix;\r\n\r\n#ifdef RAGDOLL\r\n\tint numanimated;\r\n\tstruct skelobject_s *animsource;\r\n\tunsigned int numbodies;\r\n\tbody_t *body;\r\n\tint numjoints;\r\n\trbejoint_t *joint;\r\n\tdoll_t *doll;\r\n\twedict_t *entity;\t//only valid for dolls.\r\n#endif\r\n} skelobject_t;\r\n\r\n#ifdef RAGDOLL\r\nstatic doll_t *dolllist;\r\n#endif\r\nstatic skelobject_t skelobjects[MAX_SKEL_OBJECTS];\r\nstatic int numskelobjectsused;\r\n\r\n//copes with src==dst to convert rel->abs\r\nstatic void skel_copy_toabs(skelobject_t *skelobjdst, skelobject_t *skelobjsrc, int startbone, int endbone)\r\n{\r\n\tint maxbones;\r\n\tgaliasbone_t *boneinfo = Mod_GetBoneInfo(skelobjsrc->model, &maxbones);\r\n\tif (!boneinfo)\r\n\t\treturn;\r\n\tendbone = min(endbone, maxbones);\r\n\tif (skelobjsrc->type == SKEL_ABSOLUTE)\r\n\t{\r\n\t\tif (skelobjsrc != skelobjdst)\r\n\t\t{\r\n\t\t\twhile(startbone < endbone)\r\n\t\t\t{\r\n\t\t\t\tVector4Copy(skelobjsrc->bonematrix+12*startbone+0, skelobjdst->bonematrix+12*startbone+0);\r\n\t\t\t\tVector4Copy(skelobjsrc->bonematrix+12*startbone+4, skelobjdst->bonematrix+12*startbone+4);\r\n\t\t\t\tVector4Copy(skelobjsrc->bonematrix+12*startbone+8, skelobjdst->bonematrix+12*startbone+8);\r\n\t\t\t\tstartbone++;\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\telse\r\n\t{\r\n\t\tif (skelobjsrc != skelobjdst)\r\n\t\t{\r\n\t\t\twhile(startbone < endbone)\r\n\t\t\t{\r\n\t\t\t\tif (boneinfo[startbone].parent >= 0)\r\n\t\t\t\t{\r\n\t\t\t\t\tMatrix3x4_Multiply(skelobjsrc->bonematrix+12*startbone, skelobjdst->bonematrix+12*boneinfo[startbone].parent, skelobjdst->bonematrix+12*startbone);\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\tVector4Copy(skelobjsrc->bonematrix+12*startbone+0, skelobjdst->bonematrix+12*startbone+0);\r\n\t\t\t\t\tVector4Copy(skelobjsrc->bonematrix+12*startbone+4, skelobjdst->bonematrix+12*startbone+4);\r\n\t\t\t\t\tVector4Copy(skelobjsrc->bonematrix+12*startbone+8, skelobjdst->bonematrix+12*startbone+8);\r\n\t\t\t\t}\r\n\t\t\t\tstartbone++;\r\n\t\t\t}\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tfloat tmpmat[12];\r\n\t\t\twhile(startbone < endbone)\r\n\t\t\t{\r\n\t\t\t\tif (boneinfo[startbone].parent >= 0)\r\n\t\t\t\t{\r\n\t\t\t\t\tMatrix3x4_Multiply(skelobjsrc->bonematrix+12*startbone, skelobjdst->bonematrix+12*boneinfo[startbone].parent, tmpmat);\r\n\t\t\t\t\tmemcpy(skelobjdst->bonematrix+12*startbone, tmpmat, sizeof(tmpmat));\r\n\t\t\t\t}\r\n\t\t\t\tstartbone++;\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\tskelobjdst->type = SKEL_ABSOLUTE;\r\n}\r\nstatic void bonemat_fromidentity(float *out)\r\n{\r\n\tout[0] = 1;\r\n\tout[1] = 0;\r\n\tout[2] = 0;\r\n\tout[3] = 0;\r\n\r\n\tout[4] = 0;\r\n\tout[5] = 1;\r\n\tout[6] = 0;\r\n\tout[7] = 0;\r\n\r\n\tout[8] = 0;\r\n\tout[9] = 0;\r\n\tout[10] = 1;\r\n\tout[11] = 0;\r\n}\r\nstatic void bonemat_fromqcvectors(float *out, const pvec_t vx[3], const pvec_t vy[3], const pvec_t vz[3], const pvec_t t[3])\r\n{\r\n\tout[0] = vx[0];\r\n\tout[1] = -vy[0];\r\n\tout[2] = vz[0];\r\n\tout[3] = t[0];\r\n\tout[4] = vx[1];\r\n\tout[5] = -vy[1];\r\n\tout[6] = vz[1];\r\n\tout[7] = t[1];\r\n\tout[8] = vx[2];\r\n\tout[9] = -vy[2];\r\n\tout[10] = vz[2];\r\n\tout[11] = t[2];\r\n}\r\nstatic void bonemat_fromaxisorg(float *out, vec3_t axis[3], const float t[3])\r\n{\r\n\tout[0] = axis[0][0];\r\n\tout[1] = axis[1][0];\r\n\tout[2] = axis[2][0];\r\n\tout[3] = t[0];\r\n\tout[4] = axis[0][1];\r\n\tout[5] = axis[1][1];\r\n\tout[6] = axis[2][1];\r\n\tout[7] = t[1];\r\n\tout[8] = axis[0][2];\r\n\tout[9] = axis[1][2];\r\n\tout[10]= axis[2][2];\r\n\tout[11]= t[2];\r\n}\r\nstatic void bonemat_fromentity(world_t *w, wedict_t *ed, float *trans)\r\n{\r\n\tvec3_t d[3], a;\r\n\tmodel_t *mod;\r\n\tmod = w->Get_CModel(w, ed->v->modelindex);\r\n\ta[0] = ed->v->angles[0];\r\n\ta[1] = ed->v->angles[1];\r\n\ta[2] = ed->v->angles[2];\r\n\tif (!mod || mod->type == mod_alias)\r\n\t{\r\n\t\ta[0] *= r_meshpitch.value;\r\n\t\ta[2] *= r_meshroll.value;\r\n\t}\r\n\tAngleVectors(a, d[0], d[1], d[2]);\r\n\tbonemat_fromqcvectors(trans, d[0], d[1], d[2], ed->v->origin);\r\n}\r\nstatic void bonemat_toqcvectors(const float *in, pvec_t vx[3], pvec_t vy[3], pvec_t vz[3], pvec_t t[3])\r\n{\r\n\tvx[0] = in[0];\r\n\tvx[1] = in[4];\r\n\tvx[2] = in[8];\r\n\tvy[0] = -in[1];\r\n\tvy[1] = -in[5];\r\n\tvy[2] = -in[9];\r\n\tvz[0] = in[2];\r\n\tvz[1] = in[6];\r\n\tvz[2] = in[10];\r\n\tt [0] = in[3];\r\n\tt [1] = in[7];\r\n\tt [2] = in[11];\r\n}\r\nstatic void bonemat_toaxisorg(const float *src, vec3_t axis[3], float t[3])\r\n{\r\n\taxis[0][0] = src[0];\r\n\taxis[0][1] = src[4];\r\n\taxis[0][2] = src[8];\r\n\taxis[1][0] = src[1];\r\n\taxis[1][1] = src[5];\r\n\taxis[1][2] = src[9];\r\n\taxis[2][0] = src[2];\r\n\taxis[2][1] = src[6];\r\n\taxis[2][2] = src[10];\r\n\tt[0] = src[3];\r\n\tt[1] = src[7];\r\n\tt[2] = src[11];\r\n}\r\n\r\nstatic void bonematident_toqcvectors(float vx[3], float vy[3], float vz[3], float t[3])\r\n{\r\n\tvx[0] = 1;\r\n\tvx[1] = 0;\r\n\tvx[2] = 0;\r\n\tvy[0] = -0;\r\n\tvy[1] = -1;\r\n\tvy[2] = -0;\r\n\tvz[0] = 0;\r\n\tvz[1] = 0;\r\n\tvz[2] = 1;\r\n\tt [0] = 0;\r\n\tt [1] = 0;\r\n\tt [2] = 0;\r\n}\r\n\r\n\r\nstatic qboolean pendingkill; /*states that there is a skel waiting to be killed*/\r\n#ifdef RAGDOLL\r\nstatic void rag_uninstanciate(skelobject_t *sko);\r\nstatic int rag_finddollbody(doll_t *d, char *bodyname)\r\n{\r\n\tint i;\r\n\tfor (i = 0; i < d->numbodies; i++)\r\n\t{\r\n\t\tif (!strcmp(d->body[i].name, bodyname))\r\n\t\t\treturn i;\r\n\t}\r\n\treturn -1;\r\n}\r\nstatic int rag_finddolljoint(doll_t *d, char *name)\r\n{\r\n\tint i;\r\n\tfor (i = 0; i < d->numjoints; i++)\r\n\t{\r\n\t\tif (!strcmp(d->joint[i].name, name))\r\n\t\t\treturn i;\r\n\t}\r\n\treturn -1;\r\n}\r\n\r\ntypedef struct {\r\n\tqboolean errors;\r\n\tdoll_t *d;\r\n\tint numbones;\r\n\tgaliasbone_t *bones;\r\n\r\n\trbebodyinfo_t *body;\r\n\trbejointinfo_t *joint;\r\n\r\n\trbebodyinfo_t defbody;\r\n\trbejointinfo_t defjoint;\r\n} dollcreatectx_t;\r\nstatic dollcreatectx_t *rag_createdoll(model_t *mod, const char *fname, int numbones)\r\n{\r\n\tint i;\r\n\tdollcreatectx_t *ctx;\r\n\tint nummodbones = Mod_GetNumBones(mod, false);\r\n\tif (nummodbones != numbones || !numbones)\r\n\t\treturn NULL;\r\n\r\n\tctx = malloc(sizeof(*ctx));\r\n\r\n\tctx->bones = Mod_GetBoneInfo(mod, &ctx->numbones);\r\n\tctx->errors = 0;\r\n\r\n\tmemset(&ctx->defbody, 0, sizeof(ctx->defbody));\r\n\tctx->defbody.animate = true;\r\n\tctx->defbody.geomshape = GEOMTYPE_BOX;\r\n\tctx->defbody.dimensions[0] = 4;\r\n\tctx->defbody.dimensions[1] = 4;\r\n\tctx->defbody.dimensions[2] = 4;\r\n\tctx->defbody.mass = 1;\r\n\tctx->defbody.orient = false;\r\n\r\n\tmemset(&ctx->defjoint, 0, sizeof(ctx->defjoint));\r\n\tctx->defjoint.startenabled = true;\r\n\tctx->defjoint.axis[1] = 1;\r\n\tctx->defjoint.axis2[2] = 1;\r\n\tctx->defjoint.ERP = 0.2;\t//use ODE defaults\r\n\tctx->defjoint.ERP2 = 0.2;\r\n\tctx->defjoint.CFM = 1e-5;\r\n\tctx->defjoint.CFM2 = 1e-5;\r\n\r\n\tctx->d = BZ_Malloc(sizeof(*ctx->d));\r\n\tctx->d->next = dolllist;\r\n\tctx->d->name = strdup(fname);\r\n\tctx->d->model = mod;\r\n\tctx->d->refanim = -1;\t//skin pose.\r\n\tctx->d->numbodies = 0;\r\n\tctx->d->body = NULL;\r\n\tctx->d->numjoints = 0;\r\n\tctx->d->uses = 0;\r\n\tctx->d->joint = NULL;\r\n\r\n\tctx->d->numbones = min(numbones, ctx->numbones);\r\n\tctx->d->bone = BZ_Malloc(sizeof(*ctx->d->bone) * ctx->d->numbones);\r\n\tfor (i = 0; i < ctx->d->numbones; i++)\r\n\t\tctx->d->bone[i].bodyidx = -1;\r\n\r\n\treturn ctx;\r\n}\r\n//returns true if the command was recognised. false if the command is for something else.\r\nstatic qboolean rag_dollline(dollcreatectx_t *ctx, int linenum)\r\n{\r\n\tint i;\r\n\tint argc;\r\n\tchar *cmd, *val;\r\n\tdoll_t *d = ctx->d;\r\n\r\n\targc = Cmd_Argc();\r\n\tcmd = Cmd_Argv(0);\r\n\tval = Cmd_Argv(1);\r\n\r\n\tif (!argc)\r\n\t{\r\n\t}\r\n\telse if (argc == 2 && !stricmp(cmd, \"refpose\") && !strcmp(val, \"skin\"))\r\n\t\tctx->d->refanim = -1;\r\n\telse if (argc == 3 && !stricmp(cmd, \"refpose\"))\r\n\t{\r\n\t\tctx->d->refanim = atoi(val);\r\n\t\tctx->d->refanimtime = atoi(Cmd_Argv(2));\r\n\t}\r\n\t//create a new body\r\n\telse if (argc == 3 && !stricmp(cmd, \"body\"))\r\n\t{\r\n\t\tint boneidx = Mod_TagNumForName(d->model, Cmd_Argv(2), 0)-1;\r\n\t\tctx->joint = NULL;\r\n\t\tctx->body = NULL;\r\n\t\tif (boneidx >= 0)\r\n\t\t{\r\n\t\t\td->body = BZ_Realloc(d->body, sizeof(*d->body)*(d->numbodies+1));\r\n\t\t\tctx->body = &d->body[d->numbodies];\r\n\t\t\td->bone[boneidx].bodyidx = d->numbodies;\r\n\t\t\td->numbodies++;\r\n\r\n\t\t\t*ctx->body = ctx->defbody;\r\n\t\t\tQ_strncpyz(ctx->body->name, Cmd_Argv(1), sizeof(ctx->body->name));\r\n\t\t\tctx->body->bone = boneidx;\r\n\t\t}\r\n\t\telse if (!ctx->errors++)\r\n\t\t\tCon_Printf(\"^[Unable to create body \\\"%s\\\" because bone \\\"%s\\\" does not exist in %s\\\\edit\\\\%s:%i^]\\n\", Cmd_Argv(1), Cmd_Argv(2), d->model?d->model->name:\"<NOMODEL>\", d->name, linenum);\r\n\t}\r\n\t//create a new joint\r\n\telse if (argc >= 2 && !stricmp(cmd, \"joint\"))\r\n\t{\r\n\t\tchar *name;\r\n\t\tctx->joint = NULL;\r\n\t\tctx->body = NULL;\r\n\t\td->joint = BZ_Realloc(d->joint, sizeof(*d->joint)*(d->numjoints+1));\r\n\t\tctx->joint = &d->joint[d->numjoints];\r\n\t\t*ctx->joint = ctx->defjoint;\r\n\t\tQ_strncpyz(ctx->joint->name, Cmd_Argv(1), sizeof(ctx->joint->name));\r\n\t\tname = Cmd_Argv(2);\r\n\t\tctx->joint->body1 = *name?rag_finddollbody(d, name):-1;\r\n\t\tif (*name && ctx->joint->body1 < 0)\r\n\t\t{\r\n\t\t\tif (!ctx->errors++)\r\n\t\t\t\tCon_Printf(\"^[Joint \\\"%s\\\" joins invalid body \\\"%s\\\"\\\\edit\\\\%s:%i^]\\n\", ctx->joint->name, name, d->name, linenum);\r\n\t\t\treturn true;\r\n\t\t}\r\n\t\tname = Cmd_Argv(3);\r\n\t\tctx->joint->body2 = *name?rag_finddollbody(d, name):-1;\r\n\t\tif (*name && (ctx->joint->body2 < 0 || ctx->joint->body2 == ctx->joint->body1))\r\n\t\t{\r\n\t\t\tif (!ctx->errors++)\r\n\t\t\t{\r\n\t\t\t\tif (ctx->joint->body2 == ctx->joint->body1)\r\n\t\t\t\t\tCon_Printf(\"^[Joint \\\"%s\\\" joins body \\\"%s\\\" to itself\\\\edit\\\\%s:%i^]\\n\", ctx->joint->name, name, d->name, linenum);\r\n\t\t\t\telse\r\n\t\t\t\t\tCon_Printf(\"^[Joint \\\"%s\\\" joins invalid body \\\"%s\\\"\\\\edit\\\\%s:%i^]\\n\", ctx->joint->name, name, d->name, linenum);\r\n\t\t\t}\r\n\t\t\treturn true;\r\n\t\t}\r\n\t\tctx->joint->bonepivot = d->body[(ctx->joint->body2 >= 0)?ctx->joint->body2:ctx->joint->body1].bone;\t//default the pivot object to the bone of the second object.\r\n\r\n\t\tif (ctx->joint->body1 >= 0 || ctx->joint->body2 >= 0)\r\n\t\t\td->numjoints++;\r\n\t\telse if (!ctx->errors++)\r\n\t\t\tCon_Printf(\"^[Joint property \\\"%s\\\" not recognised\\\\edit\\\\%s:%i^]\\n\", ctx->joint->name, d->name, linenum);\r\n\t}\r\n\telse if (argc == 2 && !stricmp(cmd, \"updatebody\"))\r\n\t{\r\n\t\tctx->joint = NULL;\r\n\t\tctx->body = NULL;\r\n\t\tif (!strcmp(val, \"default\"))\r\n\t\t\tctx->body = &ctx->defbody;\r\n\t\telse\r\n\t\t{\r\n\t\t\ti = rag_finddollbody(d, val);\r\n\t\t\tif (i >= 0)\r\n\t\t\t\tctx->body = &d->body[i];\r\n\t\t\telse if (!ctx->errors++)\r\n\t\t\t\tCon_Printf(\"^[Cannot update body \\\"%s\\\"\\\\edit\\\\%s:%i^]\\n\", ctx->body->name, d->name, linenum);\r\n\t\t}\r\n\t}\r\n\telse if (argc == 2 && !stricmp(cmd, \"updatejoint\"))\r\n\t{\r\n\t\tctx->joint = NULL;\r\n\t\tctx->body = NULL;\r\n\t\tif (!strcmp(val, \"default\"))\r\n\t\t\tctx->joint = &ctx->defjoint;\r\n\t\telse\r\n\t\t{\r\n\t\t\ti = rag_finddolljoint(d, val);\r\n\t\t\tif (i >= 0)\r\n\t\t\t\tctx->joint = &d->joint[i];\r\n\t\t\telse if (!ctx->errors++)\r\n\t\t\t\tCon_Printf(\"^[Cannot update joint \\\"%s\\\"\\\\edit\\\\%s:%i^]\\n\", ctx->joint->name, d->name, linenum);\r\n\t\t}\r\n\t}\r\n\r\n\t//body properties\r\n\telse if (ctx->body && argc == 2 && !stricmp(cmd, \"shape\"))\r\n\t{\r\n\t\tif (!stricmp(val, \"box\"))\r\n\t\t\tctx->body->geomshape = GEOMTYPE_BOX;\r\n\t\telse if (!stricmp(val, \"sphere\"))\r\n\t\t\tctx->body->geomshape = GEOMTYPE_SPHERE;\r\n\t\telse if (!stricmp(val, \"cylinder\"))\r\n\t\t\tctx->body->geomshape = GEOMTYPE_CYLINDER;\r\n\t\telse if (!stricmp(val, \"capsule\"))\r\n\t\t\tctx->body->geomshape = GEOMTYPE_CAPSULE;\r\n\t\telse if (!ctx->errors++)\r\n\t\t\tCon_Printf(\"^[Joint shape \\\"%s\\\" not recognised\\\\edit\\\\%s:%i^]\\n\", val, d->name, linenum);\r\n\t}\r\n\telse if (ctx->body && argc == 2 && !stricmp(cmd, \"animate\"))\r\n\t\tctx->body->animate = atof(val);\r\n\telse if (ctx->body && argc == 2 && !stricmp(cmd, \"draw\"))\r\n\t\tctx->body->draw = atoi(val);\r\n\telse if (ctx->body && argc == 2 && !stricmp(cmd, \"mass\"))\r\n\t\tctx->body->mass = atof(val);\r\n\telse if (ctx->body && argc == 2 && (!stricmp(cmd, \"dimensions\") || !stricmp(cmd, \"size\")))\r\n\t\tctx->body->dimensions[0] = ctx->body->dimensions[1] = ctx->body->dimensions[2] = atof(val);\r\n\telse if (ctx->body && argc == 3 && (!stricmp(cmd, \"dimensions\") || !stricmp(cmd, \"size\")))\r\n\t{\r\n\t\tctx->body->dimensions[0] = ctx->body->dimensions[1] = atof(val);\r\n\t\tctx->body->dimensions[2] = atoi(Cmd_Argv(2));\r\n\t}\r\n\telse if (ctx->body && argc == 4 && (!stricmp(cmd, \"dimensions\") || !stricmp(cmd, \"size\")))\r\n\t{\r\n\t\tctx->body->dimensions[0] = atof(val);\r\n\t\tctx->body->dimensions[1] = atof(Cmd_Argv(2));\r\n\t\tctx->body->dimensions[2] = atof(Cmd_Argv(3));\r\n\t}\r\n\telse if (ctx->body && argc == 4 && !stricmp(cmd, \"offset\"))\r\n\t{\r\n\t\tvec3_t ang;\r\n\t\tctx->body->isoffset = true;\r\n\t\tctx->body->relmatrix[3] = atof(val);\r\n\t\tctx->body->relmatrix[7] = atof(Cmd_Argv(2));\r\n\t\tctx->body->relmatrix[11] = atof(Cmd_Argv(3));\r\n\t\tang[0] = atof(Cmd_Argv(4));\r\n\t\tang[1] = atof(Cmd_Argv(5));\r\n\t\tang[2] = atof(Cmd_Argv(6));\r\n\t\tAngleVectorsFLU(ang, &ctx->body->relmatrix[0], &ctx->body->relmatrix[4], &ctx->body->relmatrix[8]);\r\n\t\tMatrix3x4_Invert_Simple(ctx->body->relmatrix, ctx->body->inverserelmatrix);\r\n\t}\r\n\r\n\t//joint properties\r\n\telse if (ctx->joint && argc == 2 && !stricmp(cmd, \"type\"))\r\n\t{\r\n\t\tif (!stricmp(val, \"fixed\"))\r\n\t\t\tctx->joint->type = JOINTTYPE_FIXED;\r\n\t\telse if (!stricmp(val, \"point\"))\r\n\t\t\tctx->joint->type = JOINTTYPE_POINT;\r\n\t\telse if (!stricmp(val, \"hinge\"))\r\n\t\t\tctx->joint->type = JOINTTYPE_HINGE;\r\n\t\telse if (!stricmp(val, \"slider\"))\r\n\t\t\tctx->joint->type = JOINTTYPE_SLIDER;\r\n\t\telse if (!stricmp(val, \"universal\"))\r\n\t\t\tctx->joint->type = JOINTTYPE_UNIVERSAL;\r\n\t\telse if (!stricmp(val, \"hinge2\"))\r\n\t\t\tctx->joint->type = JOINTTYPE_HINGE2;\r\n\t}\r\n\telse if (ctx->joint && argc == 2 && !stricmp(cmd, \"draw\"))\r\n\t\tctx->joint->draw = atoi(val);\r\n\telse if (ctx->joint && argc == 2 && !stricmp(cmd, \"enabled\"))\r\n\t\tctx->joint->startenabled = atoi(val)?true:false;\r\n\telse if (ctx->joint && argc == 2 && !stricmp(cmd, \"ERP\"))\r\n\t\tctx->joint->ERP = atof(val);\r\n\telse if (ctx->joint && argc == 2 && !stricmp(cmd, \"ERP2\"))\r\n\t\tctx->joint->ERP2 = atof(val);\r\n\telse if (ctx->joint && argc == 2 && !stricmp(cmd, \"CFM\"))\r\n\t\tctx->joint->CFM = atof(val);\r\n\telse if (ctx->joint && argc == 2 && !stricmp(cmd, \"CFM2\"))\r\n\t\tctx->joint->CFM2 = atof(val);\r\n\telse if (ctx->joint && argc == 2 && !stricmp(cmd, \"FMax\"))\r\n\t\tctx->joint->FMax = atof(val);\r\n\telse if (ctx->joint && argc == 2 && !stricmp(cmd, \"FMax2\"))\r\n\t\tctx->joint->FMax2 = atof(val);\r\n\telse if (ctx->joint && argc == 2 && !stricmp(cmd, \"HiStop\"))\r\n\t\tctx->joint->HiStop = atof(val);\r\n\telse if (ctx->joint && argc == 2 && !stricmp(cmd, \"HiStop2\"))\r\n\t\tctx->joint->HiStop2 = atof(val);\r\n\telse if (ctx->joint && argc == 2 && !stricmp(cmd, \"LoStop\"))\r\n\t\tctx->joint->LoStop = atof(val);\r\n\telse if (ctx->joint && argc == 2 && !stricmp(cmd, \"LoStop2\"))\r\n\t\tctx->joint->LoStop2 = atof(val);\r\n\telse if (ctx->joint && argc == 4 && !stricmp(cmd, \"axis\"))\r\n\t{\r\n\t\tctx->joint->axis[0] = atof(val);\r\n\t\tctx->joint->axis[1] = atof(Cmd_Argv(2));\r\n\t\tctx->joint->axis[2] = atof(Cmd_Argv(3));\r\n\t}\r\n\telse if (ctx->joint && argc == 4 && !stricmp(cmd, \"axis2\"))\r\n\t{\r\n\t\tctx->joint->axis2[0] = atof(val);\r\n\t\tctx->joint->axis2[1] = atof(Cmd_Argv(2));\r\n\t\tctx->joint->axis2[2] = atof(Cmd_Argv(3));\r\n\t}\r\n\telse if (ctx->joint && argc == 4 && !stricmp(cmd, \"offset\"))\r\n\t{\r\n\t\tctx->joint->offset[0] = atof(val);\r\n\t\tctx->joint->offset[1] = atof(Cmd_Argv(2));\r\n\t\tctx->joint->offset[2] = atof(Cmd_Argv(3));\r\n\t}\r\n\telse if (ctx->joint && ctx->joint != &ctx->defjoint && (argc == 2 || argc == 5) && !stricmp(cmd, \"pivot\"))\r\n\t{\r\n\t\t//the origin is specified in base-frame model space\r\n\t\t//we need to make it relative to the joint's bodies\r\n\t\tchar *bone = val;\r\n\t\ti = Mod_TagNumForName(d->model, bone, 0)-1;\r\n\t\tif (argc > 2)\r\n\t\t{\r\n\t\t\tctx->joint->offset[0] = atof(Cmd_Argv(2));\r\n\t\t\tctx->joint->offset[1] = atof(Cmd_Argv(3));\r\n\t\t\tctx->joint->offset[2] = atof(Cmd_Argv(4));\r\n\t\t}\r\n\t\tif (i >= 0)\r\n\t\t{\r\n\t\t\tctx->joint->bonepivot = i;\r\n\t\t\t//Matrix3x4_Multiply(omat, bones[i].inverse, joint->orgmatrix);\r\n\t\t}\r\n\t\telse if (!ctx->errors++)\r\n\t\t\tCon_Printf(\"^[Directive \\\"%s\\\" not understood or invalid\\\\edit\\\\%s:%i^]\\n\", cmd, d->name, linenum);\r\n\t}\r\n\telse\r\n\t\treturn false;\r\n\r\n\treturn true;\r\n};\r\nstatic doll_t *rag_finishdoll(dollcreatectx_t *ctx)\r\n{\r\n\tdoll_t *d = ctx->d;\r\n\tint i;\r\n\r\n\td->drawn = false;\r\n\tfor (i = 0; i < d->numbodies; i++)\r\n\t{\r\n\t\tif (d->body[i].draw)\r\n\t\t{\r\n\t\t\td->drawn = true;\r\n\t\t\tbreak;\r\n\t\t}\r\n\t}\r\n\tif (i == d->numbodies)\r\n\tfor (i = 0; i < d->numjoints; i++)\r\n\t{\r\n\t\tif (d->joint[i].draw)\r\n\t\t{\r\n\t\t\td->drawn = true;\r\n\t\t\tbreak;\r\n\t\t}\r\n\t}\r\n\r\n\tfree(ctx);\r\n\r\n\tif (!d->numbodies)\r\n\t{\r\n\t\trag_freedoll(d);\r\n\t\treturn NULL;\r\n\t}\r\n\treturn d;\r\n};\r\n\r\ndoll_t *rag_createdollfromstring(model_t *mod, const char *fname, int numbones, const char *file)\r\n{\r\n\tint linenum = 0;\r\n\tdollcreatectx_t *ctx;\r\n\tctx = rag_createdoll(mod, fname, numbones);\r\n\tif (!ctx)\r\n\t\treturn NULL;\r\n\t\r\n\twhile(file && *file)\r\n\t{\r\n\t\tlinenum++;\r\n\t\tfile = Cmd_TokenizeString(file, false, false);\r\n\r\n\t\tif (!rag_dollline(ctx, linenum))\r\n\t\t\tif (!ctx->errors++)\r\n\t\t\t\tCon_Printf(\"^[Directive \\\"%s\\\" not understood or invalid\\\\edit\\\\%s:%i^]\\n\", Cmd_Argv(0), ctx->d->name, linenum);\r\n\t}\r\n\treturn rag_finishdoll(ctx);\r\n}\r\n\r\nstatic doll_t *rag_loaddoll(model_t *mod, char *fname, int numbones)\r\n{\r\n\tdoll_t *d;\r\n\tvoid *fptr = NULL;\r\n\r\n\tfor (d = dolllist; d; d = d->next)\r\n\t{\r\n\t\tif (d->model == mod)\r\n\t\t\tif (!strcmp(d->name, fname))\r\n\t\t\t\treturn d;\r\n\t}\r\n\r\n\tFS_LoadFile(fname, &fptr);\r\n\tif (!fptr)\r\n\t{\r\n#ifndef SERVERONLY\r\n\t\tCL_CheckOrEnqueDownloadFile(fname, NULL, 0);\r\n#endif\r\n\t\treturn NULL;\r\n\t}\r\n\r\n\td = rag_createdollfromstring(mod, fname, numbones, fptr);\r\n\tFS_FreeFile(fptr);\r\n\r\n\tif (d)\r\n\t{\r\n\t\td->next = dolllist;\r\n\t\tdolllist = d;\r\n\t}\r\n\treturn d;\r\n}\r\nvoid rag_freedoll(doll_t *doll)\r\n{\r\n\tint i;\r\n\tif (doll->uses)\r\n\t{\r\n\t\tfor (i = 0; i < numskelobjectsused; i++)\r\n\t\t{\r\n\t\t\tif (skelobjects[i].doll == doll)\r\n\t\t\t{\r\n\t\t\t\trag_uninstanciate(&skelobjects[i]);\r\n\t\t\t\tif (!doll->uses)\r\n\t\t\t\t\tbreak;\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\tBZ_Free(doll->bone);\r\n\tBZ_Free(doll->body);\r\n\tBZ_Free(doll->joint);\r\n\tfree(doll->name);\r\n\tBZ_Free(doll);\r\n}\r\n\r\nvoid rag_uninstanciateall(void)\r\n{\r\n\tint i;\r\n\tfor (i = 0; i < numskelobjectsused; i++)\r\n\t{\r\n\t\trag_uninstanciate(&skelobjects[i]);\r\n\t}\r\n}\r\nvoid rag_flushdolls(qboolean force)\r\n{\r\n\tdoll_t *d, **link;\r\n\tif (force)\r\n\t\trag_uninstanciateall();\r\n\tfor (link = &dolllist; *link; )\r\n\t{\r\n\t\td = *link;\r\n\t\tif (!d->uses)\r\n\t\t{\r\n\t\t\t*link = d->next;\r\n\t\t\trag_freedoll(d);\r\n\t\t}\r\n\t\telse\r\n\t\t\tlink = &(*link)->next;\r\n\t}\r\n}\r\n\r\n#if 0\r\nstatic void skel_integrate(pubprogfuncs_t *prinst, skelobject_t *sko, skelobject_t *skelobjsrc, float ft, float mmat[12])\r\n{\r\n\ttrace_t t;\r\n\tvec3_t npos, opos, wnpos, wopos;\r\n\tvec3_t move;\r\n\tfloat wantmat[12];\r\n\tworld_t *w = prinst->parms->user;\r\n\tbody_t *b;\r\n\tfloat gravity = 800;\r\n\tint bone, bno;\r\n\tint boffs;\r\n\tgaliasbone_t *boneinfo = Mod_GetBoneInfo(sko->model);\r\n\tif (!boneinfo)\r\n\t\treturn;\r\n\r\n\tfor (bone = 0, bno = 0, b = sko->body; bone < sko->numbones; bone++)\r\n\t{\r\n\t\tboffs = bone*12;\r\n\t\t/*if this bone is positioned using a physical body...*/\r\n\t\tif (bno < sko->numbodies && b->jointo == boffs)\r\n\t\t{\r\n\t\t\tif (skelobjsrc)\r\n\t\t\t{\r\n\t\t\t\t/*attempt to move to target*/\r\n\t\t\t\tif (boneinfo[bone].parent >= 0)\r\n\t\t\t\t{\r\n\t\t\t\t\tMatrix3x4_Multiply(skelobjsrc->bonematrix+boffs, sko->bonematrix+12*boneinfo[bone].parent, wantmat);\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\tVector4Copy(skelobjsrc->bonematrix+boffs+0, wantmat+0);\r\n\t\t\t\t\tVector4Copy(skelobjsrc->bonematrix+boffs+4, wantmat+4);\r\n\t\t\t\t\tVector4Copy(skelobjsrc->bonematrix+boffs+8, wantmat+8);\r\n\t\t\t\t}\r\n\t\t\t\tb->vel[0] = (wantmat[3 ] - sko->bonematrix[b->jointo + 3 ])/ft;\r\n\t\t\t\tb->vel[1] = (wantmat[7 ] - sko->bonematrix[b->jointo + 7 ])/ft;\r\n\t\t\t\tb->vel[2] = (wantmat[11] - sko->bonematrix[b->jointo + 11])/ft;\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\t/*handle gravity*/\r\n\t\t\t\tb->vel[2] = b->vel[2] - gravity * ft / 2;\r\n\t\t\t}\r\n\r\n\t\t\tVectorScale(b->vel, ft, move);\r\n\r\n\t\t\topos[0] = sko->bonematrix[b->jointo + 3 ];\r\n\t\t\topos[1] = sko->bonematrix[b->jointo + 7 ];\r\n\t\t\topos[2] = sko->bonematrix[b->jointo + 11];\r\n\t\t\tnpos[0] = opos[0] + move[0];\r\n\t\t\tnpos[1] = opos[1] + move[1];\r\n\t\t\tnpos[2] = opos[2] + move[2];\r\n\r\n\t\t\tMatrix3x4_RM_Transform3(mmat, opos, wopos);\r\n\t\t\tMatrix3x4_RM_Transform3(mmat, npos, wnpos);\r\n\r\n\t\t\tt = World_Move(w, wopos, vec3_origin, vec3_origin, wnpos, MOVE_NOMONSTERS, w->edicts);\r\n\t\t\tif (t.startsolid)\r\n\t\t\t\tt.fraction = 1;\r\n\t\t\telse if (t.fraction < 1)\r\n\t\t\t{\r\n\t\t\t\t/*clip the velocity*/\r\n\t\t\t\tfloat backoff = -DotProduct (b->vel, t.plane.normal) * 1.9; /*teehee, bouncy corpses*/\r\n\t\t\t\tVectorMA(b->vel, backoff, t.plane.normal, b->vel);\r\n\t\t\t}\r\n\t\t\tif (skelobjsrc)\r\n\t\t\t{\r\n\t\t\t\tVectorCopy(wantmat+0, sko->bonematrix+b->jointo+0);\r\n\t\t\t\tVectorCopy(wantmat+4, sko->bonematrix+b->jointo+4);\r\n\t\t\t\tVectorCopy(wantmat+8, sko->bonematrix+b->jointo+8);\r\n\t\t\t}\r\n\r\n\t\t\tsko->bonematrix[b->jointo + 3 ] += move[0] * t.fraction;\r\n\t\t\tsko->bonematrix[b->jointo + 7 ] += move[1] * t.fraction;\r\n\t\t\tsko->bonematrix[b->jointo + 11] += move[2] * t.fraction;\r\n\r\n\t\t\tif (!skelobjsrc)\r\n\t\t\t\tb->vel[2] = b->vel[2] - gravity * ft / 2;\r\n\r\n\t\t\tb++;\r\n\t\t\tbno++;\r\n\t\t}\r\n\t\telse if (skelobjsrc)\r\n\t\t{\r\n\t\t\t/*directly copy animation skeleton*/\r\n\t\t\tif (boneinfo[bone].parent >= 0)\r\n\t\t\t{\r\n\t\t\t\tMatrix3x4_Multiply(skelobjsrc->bonematrix+boffs, sko->bonematrix+12*boneinfo[bone].parent, sko->bonematrix+boffs);\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tVector4Copy(skelobjsrc->bonematrix+boffs+0, sko->bonematrix+boffs+0);\r\n\t\t\t\tVector4Copy(skelobjsrc->bonematrix+boffs+4, sko->bonematrix+boffs+4);\r\n\t\t\t\tVector4Copy(skelobjsrc->bonematrix+boffs+8, sko->bonematrix+boffs+8);\r\n\t\t\t}\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\t/*retain the old relation*/\r\n\t\t\t/*FIXME*/\r\n\t\t}\r\n\t}\r\n\t/*debugging*/\r\n#if 0\r\n\t/*draw points*/\r\n\tfor (bone = 0; p < sko->numbones; bone++)\r\n\t{\r\n\t\topos[0] = sko->bonematrix[bone*12 + 3 ];\r\n\t\topos[1] = sko->bonematrix[bone*12 + 7 ];\r\n\t\topos[2] = sko->bonematrix[bone*12 + 11];\r\n\t\tMatrix3x4_RM_Transform3(mmat, opos, wopos);\r\n\t\tP_RunParticleEffectTypeString(wopos, vec3_origin, 1, \"ragdolltest\");\r\n\t}\r\n#endif\r\n}\r\n#endif\r\n#endif\r\n\r\nstatic void skel_generateragdoll_f_bones(vfsfile_t *f, galiasbone_t *bones, int numbones, int parent, int indent)\r\n{\r\n\tint i, j;\r\n\tfor (i = 0; i < numbones; i++)\r\n\t{\r\n\t\tif (bones[i].parent == parent)\r\n\t\t{\r\n\t\t\tVFS_PUTS(f, \"//\");\r\n\t\t\tfor (j = 0; j < indent; j++)\r\n\t\t\t\tVFS_PUTS(f, \"\\t\");\r\n\t\t\tVFS_PUTS(f, va(\"%i %s\\n\", i, bones[i].name));\r\n\t\t\tskel_generateragdoll_f_bones(f, bones, numbones, i, indent+1);\r\n\t\t}\r\n\t}\r\n}\r\nvoid skel_generateragdoll_f(void)\r\n{\r\n\tchar *modname = Cmd_Argv(1);\r\n\tchar *outname;\r\n\tmodel_t *mod = Mod_ForName(modname, MLV_SILENT);\r\n\tgaliasbone_t *bones;\r\n\tvfsfile_t *f;\r\n\tint i;\r\n\tint numbones;\r\n\tif (!mod)\r\n\t{\r\n\t\tCon_Printf(\"Cannot open %s\\n\", modname);\r\n\t\treturn;\r\n\t}\r\n\tbones = Mod_GetBoneInfo(mod, &numbones);\r\n\tif (!bones || numbones < 1)\r\n\t{\r\n\t\tCon_Printf(\"Model %s has no bones\\n\", modname);\r\n\t\treturn;\r\n\t}\r\n\toutname = va(\"%s.doll\", mod->name);\r\n\t\r\n\tf = FS_OpenVFS(outname, \"wb\", FS_GAMEONLY);\r\n\tVFS_PUTS(f, va(\"//basic ragdoll info for model %s\\n\", mod->name));\r\n\tVFS_PUTS(f, va(\"//generated with: %s %s\\n\", Cmd_Argv(0), Cmd_Args()));\r\n\tVFS_PUTS(f, \"//this file will need editing by hand\\n\");\r\n\tVFS_PUTS(f, \"//use the flush command to reload this file\\n\");\r\n\r\n\t//print background bone info.\r\n\tVFS_PUTS(f, \"\\n//bones are as follows:\\n\");\r\n\tskel_generateragdoll_f_bones(f, bones, numbones, -1, 0);\r\n\r\n\t//print background frame info.\r\n\tVFS_PUTS(f, \"\\n//frames are as follows:\\n\");\r\n\tfor (i = 0; i < 32768; i++)\r\n\t{\r\n\t\tchar *fname;\r\n\t\tint numframes;\r\n\t\tfloat duration;\r\n\t\tqboolean loop;\r\n\t\tint act;\r\n\t\tif (!Mod_FrameInfoForNum(mod, 0, i, &fname, &numframes, &duration, &loop, &act))\r\n\t\t\tbreak;\r\n\t\tVFS_PUTS(f, va(\"//%i %s (%i frames) (%f secs)%s\", i, fname, numframes, duration, loop?\" (loop)\":\"\"));\r\n\t}\r\n\tif (i == 0)\r\n\t\tVFS_PUTS(f, \"//NO FRAME INFO\\n\");\r\n\r\n\tVFS_PUTS(f, \"\\n//reference pose that offsets are defined in terms of\\n\");\r\n\tif (mod->type == mod_halflife)\r\n\t\tVFS_PUTS(f, \"refpose 0 0.0 //use first anim's first pose\\n\");\r\n\telse\r\n\t\tVFS_PUTS(f, \"refpose skin\\n\");\r\n\r\n\t//print background frame info.\r\n\tVFS_PUTS(f, \"\\n//skins are as follows:\\n\");\r\n\tfor (i = 0; i < 32768; i++)\r\n\t{\r\n\t\tconst char *sname;\r\n\t\tsname = Mod_SkinNameForNum(mod, 0, i);\r\n\t\tif (!sname)\r\n\t\t\tbreak;\r\n\t\tVFS_PUTS(f, va(\"//%i %s\", i, sname));\r\n\t}\r\n\tif (i == 0)\r\n\t\tVFS_PUTS(f, \"//NO SKIN INFO\\n\");\r\n\r\n\tVFS_PUTS(f, \"\\nupdatebody default\\n\");\r\n\tVFS_PUTS(f, \"\\tshape box\t//one of box, sphere, cylinder, capsule\\n\");\r\n\tVFS_PUTS(f, \"\\tdimensions 8 8 8\\n\");\r\n\tVFS_PUTS(f, \"\\tdraw 1\t\t//1 for visualising debug, 0 for release\\n\");\r\n\tVFS_PUTS(f, \"\\tanimate 1\t//0 will always be limp\\n\");\r\n\tVFS_PUTS(f, \"\\n\");\r\n\r\n\t//FIXME: only write out the bodies specified as arguments, and with no //\r\n\tfor (i = 0; i < numbones; i++)\r\n\t\tVFS_PUTS(f, va(\"//body \\\"b_%s\\\" \\\"%s\\\"\\n\", bones[i].name, bones[i].name));\r\n\tVFS_PUTS(f, \"\\n\");\r\n\r\n\tVFS_PUTS(f, \"updatejoint default\\n\");\r\n\tVFS_PUTS(f, \"\\ttype hinge\t//one of fixed, point, hinge, slider, universal, hinge2\\n\");\r\n\tVFS_PUTS(f, \"\\t//histop 1\\n\");\r\n\tVFS_PUTS(f, \"\\t//lostop -1\\n\");\r\n\tVFS_PUTS(f, \"\\t//histop2 1\\n\");\r\n\tVFS_PUTS(f, \"\\t//lostop2 -1\\n\");\r\n\tVFS_PUTS(f, \"\\t//erp\\n\");\r\n\tVFS_PUTS(f, \"\\t//erp2\\n\");\r\n\tVFS_PUTS(f, \"\\t//cfm\\n\");\r\n\tVFS_PUTS(f, \"\\t//cfm2\\n\");\r\n\tVFS_PUTS(f, \"\\t//fmax\\n\");\r\n\tVFS_PUTS(f, \"\\t//fmax2\\n\");\r\n\tVFS_PUTS(f, \"\\n\");\r\n\r\n\tfor (i = 0; i < numbones; i++)\r\n\t{\r\n\t\tif (bones[i].parent >= 0)\t//don't generate joints for root bones. FIXME: also skip omitted bodies from argument list.\r\n\t\t{\r\n\t\t\tVFS_PUTS(f, va(\"//joint j_%s b_%s b_%s\\n\", bones[i].name, bones[bones[i].parent].name, bones[i].name));\r\n\t\t\t//FIXME: calc the extents in each pose and set the hi/lo stops.\r\n\t\t}\r\n\t}\r\n\tVFS_PUTS(f, \"\\n\");\r\n\r\n\tVFS_CLOSE(f);\r\n}\r\nvoid skel_info_f(void)\r\n{\r\n\tint i;\r\n\tfor (i = 0; i < numskelobjectsused; i++)\r\n\t{\r\n\t\tif (skelobjects[i].world)\r\n\t\t{\r\n#if defined(HAVE_CLIENT) && defined(CSQC_DAT)\r\n\t\t\textern world_t csqc_world;\r\n#endif\r\n\t\t\tCon_Printf(\"doll %i:\\n\", i);\r\n#ifndef CLIENTONLY\r\n\t\t\tif (skelobjects[i].world == &sv.world)\r\n\t\t\t\tCon_Printf(\" SSQC\\n\");\r\n#endif\r\n#if defined(HAVE_CLIENT) && defined(CSQC_DAT)\r\n\t\t\tif (skelobjects[i].world == &csqc_world)\r\n\t\t\t\tCon_Printf(\" CSQC\\n\");\r\n#endif\r\n\t\t\tCon_Printf(\" type: %s\\n\", (skelobjects[i].type == SKEL_RELATIVE)?\"parentspace\":\"modelspace\");\r\n\t\t\tif (skelobjects[i].model)\r\n\t\t\t\tCon_Printf(\" model: %s\\n\", skelobjects[i].model->name);\r\n\t\t\tCon_Printf(\" bone count: %i\\n\", skelobjects[i].numbones);\r\n#ifdef RAGDOLL\r\n\t\t\tif (skelobjects[i].doll)\r\n\t\t\t{\r\n\t\t\t\tCon_Printf(\" ragdoll: %s%s\\n\", skelobjects[i].doll->name, ((skelobjects[i].doll == skelobjects[i].model->dollinfo)?\" (model default)\":\"\"));\r\n\t\t\t\tCon_Printf(\" phys bodies: %i\\n\", skelobjects[i].doll->numbodies);\r\n\t\t\t}\r\n\t\t\tif (skelobjects[i].entity)\r\n\t\t\t\tCon_Printf(\" entity: %i (%s)\\n\", skelobjects[i].entity->entnum, skelobjects[i].world->progs->StringToNative(skelobjects[i].world->progs, skelobjects[i].entity->v->classname));\r\n#endif\r\n\t\t}\r\n\t}\r\n}\r\n\r\n/*models were purged. reset any pointers to them*/\r\nvoid skel_reload(void)\r\n{\r\n\tint i;\r\n\tfor (i = 0; i < countof(skelobjects); i++)\r\n\t{\r\n\t\tif (!skelobjects[i].model)\r\n\t\t\tcontinue;\r\n\t\tif (skelobjects[i].modelindex && skelobjects[i].world)\r\n\t\t\tskelobjects[i].model = skelobjects[i].world->Get_CModel(skelobjects[i].world, skelobjects[i].modelindex);\r\n\t\telse\r\n\t\t\tskelobjects[i].model = NULL;\r\n\t}\r\n}\r\n\r\n/*destroys all skeletons*/\r\nvoid skel_reset(world_t *world)\r\n{\r\n\tint i;\r\n\r\n\tfor (i = 0; i < countof(skelobjects); i++)\r\n\t{\r\n\t\tif (skelobjects[i].world == world)\r\n\t\t{\r\n#ifdef RAGDOLL\r\n\t\t\trag_uninstanciate(&skelobjects[i]);\r\n#endif\r\n\t\t\tskelobjects[i].numbones = 0;\r\n\t\t\tskelobjects[i].inuse = false;\r\n\t\t\tskelobjects[i].bonematrix = NULL;\r\n\t\t\tskelobjects[i].world = NULL;\r\n\t\t}\r\n\t}\r\n\r\n\twhile (numskelobjectsused && !skelobjects[numskelobjectsused-1].inuse)\r\n\t\tnumskelobjectsused--;\r\n#ifdef RAGDOLL\r\n\trag_flushdolls(false);\r\n#endif\r\n}\r\n\r\n/*deletes any skeletons marked for deletion*/\r\nvoid skel_dodelete(world_t *world)\r\n{\r\n\tint skelidx;\r\n\tif (!pendingkill)\r\n\t\treturn;\r\n\r\n\tpendingkill = false;\r\n\tfor (skelidx = 0; skelidx < numskelobjectsused; skelidx++)\r\n\t{\r\n\t\tif (skelobjects[skelidx].inuse == 2)\r\n\t\t{\r\n#ifdef RAGDOLL\r\n\t\t\trag_uninstanciate(&skelobjects[skelidx]);\r\n#endif\r\n\t\t\tskelobjects[skelidx].inuse = 0;\r\n\t\t}\r\n\t}\r\n\r\n\twhile (numskelobjectsused && !skelobjects[numskelobjectsused-1].inuse)\r\n\t\tnumskelobjectsused--;\r\n}\r\n\r\nstatic skelobject_t *skel_create(world_t *world, int bonecount)\r\n{\r\n\tunsigned int skelidx;\r\n\t//invalid if the bonecount is not set...\r\n\tif (bonecount <= 0 || bonecount > MAX_BONES)\r\n\t\treturn NULL;\r\n\r\n\tfor (skelidx = 0; skelidx < numskelobjectsused; skelidx++)\r\n\t{\r\n\t\tif (!skelobjects[skelidx].inuse && skelobjects[skelidx].numbones == bonecount && skelobjects[skelidx].world == world)\r\n\t\t{\r\n\t\t\tskelobjects[skelidx].inuse = 1;\r\n\t\t\treturn &skelobjects[skelidx];\r\n\t\t}\r\n\t}\r\n\r\n\tfor (skelidx = 0; skelidx <= MAX_SKEL_OBJECTS; skelidx++)\r\n\t{\r\n\t\tif (!skelobjects[skelidx].inuse &&\r\n\t\t\t(!skelobjects[skelidx].numbones || skelobjects[skelidx].numbones == bonecount) &&\r\n\t\t\t(!skelobjects[skelidx].world || skelobjects[skelidx].world == world))\r\n\t\t{\r\n\t\t\tif (!skelobjects[skelidx].numbones)\r\n\t\t\t{\r\n\t\t\t\tskelobjects[skelidx].numbones = bonecount;\r\n\t\t\t\tskelobjects[skelidx].bonematrix = (float*)PR_AddString(world->progs, \"\", sizeof(float)*12*bonecount, false);\r\n\t\t\t}\r\n\t\t\tskelobjects[skelidx].world = world;\r\n\t\t\tif (numskelobjectsused <= skelidx)\r\n\t\t\t\tnumskelobjectsused = skelidx + 1;\r\n\t\t\tskelobjects[skelidx].modelindex = 0;\r\n\t\t\tskelobjects[skelidx].model = NULL;\r\n\t\t\tskelobjects[skelidx].inuse = 1;\r\n\t\t\treturn &skelobjects[skelidx];\r\n\t\t}\r\n\t}\r\n\r\n\treturn NULL;\r\n}\r\nstatic skelobject_t *skel_get(world_t *world, int skelidx)\r\n{\r\n\tskelidx--;\r\n\tif ((unsigned int)skelidx >= numskelobjectsused)\r\n\t\treturn NULL;\r\n\tif (skelobjects[skelidx].inuse != 1)\r\n\t\treturn NULL;\r\n\treturn &skelobjects[skelidx];\r\n}\r\n\r\nvoid skel_lookup(world_t *world, int skelidx, framestate_t *fte_restrict out)\r\n{\r\n\tskelobject_t *sko = skel_get(world, skelidx);\r\n\tif (sko && sko->inuse)\r\n\t{\r\n\t\tout->skeltype = sko->type;\r\n\t\tout->bonecount = sko->numbones;\r\n\t\tout->bonestate = sko->bonematrix;\r\n\t}\r\n}\r\n\r\n\r\nvoid skel_updateentbounds(world_t *w, wedict_t *ent)\r\n{/*\r\n\tfloat radius[MAX_BONES];\r\n\tfloat maxr = 0;\r\n\tsize_t i, numbones;\r\n\tskelobject_t *skel = skel_get(w, ent->xv->skeletonindex);\r\n\tgaliasbone_t *bones;\r\n\tif (!skel)\r\n\t\treturn;\r\n\tbones = Mod_GetBoneInfo(skel->model, &numbones);\r\n\tif (!skel || numbones != skel->numbones)\r\n\t\treturn;\r\n\tif (skel->type == SKEL_RELATIVE)\r\n\t{\r\n\t\tfor (i = 0; i < skel->numbones; i++)\r\n\t\t{\r\n\t\t\tradius[i] = skel->bonematrix[i*12+3]*skel->bonematrix[i*12+3]+skel->bonematrix[i*12+7]*skel->bonematrix[i*12+7]+skel->bonematrix[i*12+11]*skel->bonematrix[i*12+11];\r\n\t\t\tif (bones[i].parent >= 0)\r\n\t\t\t\tradius[i] += radius[bones[i].parent];\r\n\t\t\tif (maxr < radius[i] + bones[i].radius)\r\n\t\t\t\tmaxr = radius[i] + bones[i].radius;\r\n\t\t}\r\n\t\tfor (i = 0; i < 3; i++)\r\n\t\t{\r\n\t\t\tif (ent->v->absmin[i] > env->v->origin-maxr)\r\n\t\t\t\tent->v->absmin[i] = env->v->origin-maxr;\r\n\t\t\tif (ent->v->absmax[i] < env->v->origin+maxr)\r\n\t\t\t\tent->v->absmax[i] = env->v->origin+maxr;\r\n\t\t}\r\n\t}*/\r\n}\r\n\r\nvoid QCBUILTIN PF_skel_mmap(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\r\n{\r\n\tworld_t *world = prinst->parms->user;\r\n\tint skelidx = G_FLOAT(OFS_PARM0);\r\n\tskelobject_t *sko = skel_get(world, skelidx);\r\n\tif (!sko || sko->world != world)\r\n\t\tG_INT(OFS_RETURN) = 0;\r\n\telse\r\n\t\tG_INT(OFS_RETURN) = (char*)sko->bonematrix - prinst->stringtable;\r\n}\r\n\r\n#ifdef RAGDOLL\r\n//may not poke the skeletal object bone data.\r\nstatic void rag_uninstanciate(skelobject_t *sko)\r\n{\r\n\tint i;\r\n\tif (!sko->doll)\r\n\t\treturn;\r\n\r\n\tif (!sko->world || !sko->world->rbe)\r\n\t{\r\n\t\tsko->numbodies = sko->numjoints = 0;\r\n\t\tCon_Printf(CON_ERROR \"ERROR: Uninstanciating ragdoll from invalid world\\n\");\r\n\t}\r\n\r\n\tfor (i = 0; i < sko->numbodies; i++)\r\n\t{\r\n\t\tsko->world->rbe->RagDestroyBody(sko->world, &sko->body[i].odebody);\r\n\t}\r\n\tBZ_Free(sko->body);\r\n\tsko->body = NULL;\r\n\tsko->numbodies = 0;\r\n\r\n\tfor (i = 0; i < sko->numjoints; i++)\r\n\t{\r\n\t\tsko->world->rbe->RagDestroyJoint(sko->world, &sko->joint[i]);\r\n\t}\r\n\tBZ_Free(sko->joint);\r\n\tsko->joint = NULL;\r\n\tsko->numjoints = 0;\r\n\r\n\tsko->doll->uses--;\r\n\tsko->doll = NULL;\r\n}\r\nstatic void rag_genbodymatrix(skelobject_t *sko, rbebodyinfo_t *dollbody, float *emat, float *result)\r\n{\r\n\tfloat tmp[12];\r\n\tfloat *bmat;\r\n\tint bone = dollbody->bone;\r\n\tbmat = sko->bonematrix + bone*12;\r\n\r\n\tif (dollbody->isoffset)\r\n\t{\r\n\t\tR_ConcatTransforms((void*)dollbody->relmatrix, (void*)bmat, (void*)tmp);\r\n\t\tbmat = tmp;\r\n\t}\r\n\r\n\tR_ConcatTransforms((void*)emat, (void*)bmat, (void*)result);\r\n\r\n\tif (dollbody->orient)\r\n\t{\r\n\t\tfloat peer[12];\r\n\t\tbone = dollbody->orientpeer;\r\n\t\tbmat = sko->bonematrix + bone*12;\r\n\t\t\r\n\t\tR_ConcatTransforms((void*)emat, (void*)bmat, (void*)peer);\r\n\t}\r\n\r\n\t//FIXME: handle biasing it to point away from the parent bone towards an orientation bone\r\n}\r\nstatic qboolean rag_animate(skelobject_t *sko, doll_t *doll, float *emat)\r\n{\r\n\t//drive the various animated bodies to their updated positions\r\n\tint i;\r\n\tfor (i = 0; i < sko->numbodies; i++)\r\n\t{\r\n\t\tif (!sko->body[i].animstrength)\r\n\t\t\tcontinue;\r\n\t\trag_genbodymatrix(sko, &doll->body[i], emat, sko->body[i].animmatrix);\r\n\t}\r\n\treturn true;\r\n}\r\nstatic qboolean rag_instanciate(skelobject_t *sko, doll_t *doll, float *emat, wedict_t *ent)\r\n{\r\n\tint i;\r\n\tfloat *bmat;\r\n\tfloat bodymat[12], worldmat[12];\r\n\tvec3_t aaa2[3];\r\n\tint numbones;\r\n\tgaliasbone_t *bones = Mod_GetBoneInfo(sko->model, &numbones);\r\n\tint bone;\r\n\trbebody_t *body1, *body2;\r\n\trbejointinfo_t *j;\r\n\tfloat *absolutes;\r\n\tsko->numbodies = doll->numbodies;\r\n\tsko->body = BZ_Malloc(sizeof(*sko->body) * sko->numbodies);\r\n\tsko->doll = doll;\r\n\tdoll->uses++;\r\n\tsko->numanimated = 0;\r\n\r\n\tif (bones && doll->refanim)\r\n\t{\r\n\t\tframestate_t fstate = {0};\r\n\t\tfloat *relatives = alloca(sizeof(float)*12*numbones*2);\r\n\t\tfstate.g[FS_REG].frame[0] = doll->refanim;\t\t\t//which anim we're using as the reference\r\n\t\tfstate.g[FS_REG].frametime[0] = doll->refanimtime;\t//first pose of the anim\r\n\t\tfstate.g[FS_REG].lerpweight[0] = 1;\r\n\t\tfstate.g[FS_REG].endbone = numbones;\r\n\r\n\t\tabsolutes = relatives+numbones*12;\r\n\t\tnumbones = Mod_GetBoneRelations(sko->model, 0, numbones, bones, &fstate, relatives);\r\n\t\tfor (i = 0; i < numbones; i++)\r\n\t\t{\t//compute the absolutes. not gonna make a bg3 reference here.\r\n\t\t\tif (bones[i].parent>=0)\r\n\t\t\t\tR_ConcatTransforms((void*)(absolutes+12*bones[i].parent), (void*)(relatives+12*i), (void*)(absolutes+12*i));\r\n\t\t\telse\r\n\t\t\t\tmemcpy(absolutes+12*i, relatives+12*i, sizeof(float)*12);\r\n\t\t}\r\n\t}\r\n\telse absolutes = NULL;\r\n\r\n\tfor (i = 0; i < sko->numbodies; i++)\r\n\t{\r\n\t\tmemset(&sko->body[i], 0, sizeof(sko->body[i]));\r\n\r\n\t\tsko->body[i].animstrength = doll->body[i].animate;\r\n\t\tif (sko->body[i].animstrength)\r\n\t\t\tsko->numanimated++;\r\n\r\n\t\t//spawn the body in the base pose, so we can add joints etc (also ignoring the entity matrix, we'll fix all that up later).\r\n\t\tif (absolutes)\t//we have a reference pose\r\n\t\t\tmemcpy(bodymat, absolutes+12*doll->body[i].bone, sizeof(float)*12);\r\n\t\telse if (1)\r\n\t\t\tMatrix3x4_Invert_Simple(bones[doll->body[i].bone].inverse, bodymat);\r\n\t\telse\r\n\t\t\trag_genbodymatrix(sko, &doll->body[i], emat, bodymat);\r\n\t\tif (!sko->world->rbe->RagCreateBody(sko->world, &sko->body[i].odebody, &doll->body[i], bodymat, ent))\r\n\t\t\treturn false;\r\n\t}\r\n\tsko->numjoints = doll->numjoints;\r\n\tsko->joint = BZ_Malloc(sizeof(*sko->joint) * sko->numjoints);\r\n\tmemset(sko->joint, 0, sizeof(*sko->joint) * sko->numjoints);\r\n\tfor(i = 0; i < sko->numjoints; i++)\r\n\t{\r\n\t\tj = &doll->joint[i];\r\n\t\tbody1 = j->body1>=0?&sko->body[j->body1].odebody:NULL;\r\n\t\tbody2 = j->body2>=0?&sko->body[j->body2].odebody:NULL;\r\n\r\n\t\tbone = j->bonepivot;\r\n\t\tbmat = sko->bonematrix + bone*12;\r\n\r\n\t\tif (absolutes)\t//we have a reference pose\r\n\t\t\tmemcpy(worldmat, absolutes+12*doll->body[i].bone, sizeof(float)*12);\r\n\t\telse if (1)\r\n\t\t{\t//FIXME: j->offset isn't actually used?!?\r\n\t\t\tMatrix3x4_Invert_Simple(bones[j->bonepivot].inverse, worldmat);\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tmemcpy(bodymat, bmat, sizeof(bodymat));\r\n\t\t\tbodymat[3] += j->offset[0];\r\n\t\t\tbodymat[3+4] += j->offset[1];\r\n\t\t\tbodymat[3+8] += j->offset[2];\r\n\t\t\tR_ConcatTransforms((void*)emat, (void*)bodymat, (void*)worldmat);\r\n\t\t}\r\n\t\taaa2[0][0] = worldmat[3];\r\n\t\taaa2[0][1] = worldmat[3+4];\r\n\t\taaa2[0][2] = worldmat[3+8];\r\n\t\tVectorNormalize2(j->axis, aaa2[1]);\r\n\t\tVectorNormalize2(j->axis2, aaa2[2]);\r\n\r\n\t\tsko->world->rbe->RagCreateJoint(sko->world, &sko->joint[i], j, body1, body2, aaa2);\r\n\r\n\t\tsko->world->rbe->RagEnableJoint(&sko->joint[i], j->startenabled);\r\n\t}\r\n\r\n\t//now the joints have all their various properties, move the bones to their real positions.\r\n\t//this might result in the body flying across the room...\r\n\tfor (i = 0; i < sko->numbodies; i++)\r\n\t{\r\n\t\trag_genbodymatrix(sko, &doll->body[i], emat, bodymat);\r\n\t\tsko->world->rbe->RagMatrixToBody(&sko->body[i].odebody, bodymat);\r\n\t}\r\n\r\n\tsko->doll->numdefaultanimated = sko->numanimated;\r\n\treturn true;\r\n}\r\n\r\nvoid CLQ1_AddOrientedCube(shader_t *shader, vec3_t mins, vec3_t maxs, float *matrix, float r, float g, float b, float a);\r\nvoid CLQ1_AddOrientedCylinder(shader_t *shader, float radius, float height, qboolean capsule, float *matrix, float r, float g, float b, float a);\r\nvoid CLQ1_DrawLine(shader_t *shader, vec3_t v1, vec3_t v2, float r, float g, float b, float a);\r\nstatic void rag_derive(skelobject_t *sko, skelobject_t *asko, float *emat)\r\n{\r\n\tdoll_t *doll = sko->doll;\r\n\tfloat *bmat = sko->bonematrix;\r\n\tfloat *amat = asko?asko->bonematrix:NULL;\r\n\tint numbones;\r\n\tgaliasbone_t *bones = Mod_GetBoneInfo(sko->model, &numbones);\r\n\tint i;\r\n\tfloat invemat[12];\r\n\tfloat bodymat[12], rel[12];\r\n\r\n\tMatrix3x4_Invert(emat, invemat);\r\n\r\n#ifndef SERVERONLY\r\n\tif (doll->drawn)\r\n\t{\r\n\t\tfloat rad;\r\n\t\tvec3_t mins, maxs;\r\n\t\tshader_t *debugshader = NULL;\r\n\t\tshader_t *lineshader = NULL;\r\n\t\tvec3_t start, end;\r\n\t\tfor (i = 0; i < sko->numbodies; i++)\r\n\t\t{\r\n\t\t\tif (!doll->body[i].draw)\r\n\t\t\t\tcontinue;\r\n\r\n\t\t\tif (!debugshader)\r\n\t\t\t\tdebugshader = R_RegisterShader(\"boneshader\", SUF_NONE,\r\n\t\t\t\t\t\"{\\n\"\r\n\t\t\t\t\t\"polygonoffset\\n\"\r\n\t\t\t\t\t\"{\\n\"\r\n\t\t\t\t\t\"map $whiteimage\\n\"\r\n\t\t\t\t\t\"blendfunc add\\n\"\r\n\t\t\t\t\t\"rgbgen vertex\\n\"\r\n\t\t\t\t\t\"alphagen vertex\\n\"\r\n\t\t\t\t\t\"}\\n\"\r\n\t\t\t\t\t\"}\\n\");\r\n\t\t\t\r\n\t\t\tsko->world->rbe->RagMatrixFromBody(sko->world, &sko->body[i].odebody, bodymat);\r\n\r\n\t\t\tswitch(doll->body[i].geomshape)\r\n\t\t\t{\r\n\t\t\tdefault:\r\n\t\t\tcase GEOMTYPE_BOX:\r\n\t\t\t\tVectorScale(doll->body[i].dimensions, -0.5, mins);\r\n\t\t\t\tVectorScale(doll->body[i].dimensions, 0.5, maxs);\r\n\t\t\t\tCLQ1_AddOrientedCube(debugshader, mins, maxs, bodymat, 0.2, 0.2, 0.2, 1);\r\n\t\t\t\tbreak;\r\n\t\t\tcase GEOMTYPE_CYLINDER:\r\n\t\t\t\trad = (doll->body[i].dimensions[0] + doll->body[i].dimensions[1])*0.5;\r\n\t\t\t\tCLQ1_AddOrientedCylinder(debugshader, rad, doll->body[i].dimensions[2], false, bodymat, 0.2, 0.2, 0.2, 1);\r\n\t\t\t\tbreak;\r\n\t\t\tcase GEOMTYPE_CAPSULE:\r\n\t\t\t\trad = (doll->body[i].dimensions[0] + doll->body[i].dimensions[1])*0.5;\r\n\t\t\t\tCLQ1_AddOrientedCylinder(debugshader, rad, doll->body[i].dimensions[2], true, bodymat, 0.2, 0.2, 0.2, 1);\r\n\t\t\t\tbreak;\r\n\t\t\tcase GEOMTYPE_SPHERE:\r\n\t\t\t\trad = (doll->body[i].dimensions[0] + doll->body[i].dimensions[1] + doll->body[i].dimensions[2])/3;\r\n\t\t\t\tCLQ1_AddOrientedCylinder(debugshader, rad, rad, true, bodymat, 0.2, 0.2, 0.2, 1);\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\t\t}\r\n\t\tmins[0] = mins[1] = mins[2] = -1;\r\n\t\tmaxs[0] = maxs[1] = maxs[2] = 1;\r\n\t\tfor (i = 0; i < doll->numjoints; i++)\r\n\t\t{\r\n\t\t\tif (!doll->joint[i].draw)\r\n\t\t\t\tcontinue;\r\n\t\t\tsko->world->rbe->RagMatrixFromJoint(&sko->joint[i], &doll->joint[i], bodymat);\r\n\t//\t\tCLQ1_AddOrientedCube(debugshader, mins, maxs, bodymat, 0, 0.2, 0, 1);\r\n\r\n\t\t\tif (!lineshader)\r\n\t\t\t\tlineshader = R_RegisterShader(\"lineshader\", SUF_NONE,\r\n\t\t\t\t\t\"{\\n\"\r\n\t\t\t\t\t\"polygonoffset\\n\"\r\n\t\t\t\t\t\"{\\n\"\r\n\t\t\t\t\t\"map $whiteimage\\n\"\r\n\t\t\t\t\t\"blendfunc add\\n\"\r\n\t\t\t\t\t\"rgbgen vertex\\n\"\r\n\t\t\t\t\t\"alphagen vertex\\n\"\r\n\t\t\t\t\t\"}\\n\"\r\n\t\t\t\t\t\"}\\n\");\r\n\t\t\tstart[0] = bodymat[3];\r\n\t\t\tstart[1] = bodymat[7];\r\n\t\t\tstart[2] = bodymat[11];\r\n\t\t\tend[0] = bodymat[3] + bodymat[8]*4;\r\n\t\t\tend[1] = bodymat[7] + bodymat[9]*4;\r\n\t\t\tend[2] = bodymat[11] + bodymat[10]*4;\r\n\t\t\tCLQ1_DrawLine(lineshader, start, end, 0, 1, 1, 1);\r\n\t\t\tstart[0] = bodymat[3] + bodymat[4]*-2;\r\n\t\t\tstart[1] = bodymat[7] + bodymat[5]*-2;\r\n\t\t\tstart[2] = bodymat[11] + bodymat[6]*-2;\r\n\t\t\tend[0] = bodymat[3] + bodymat[4]*2;\r\n\t\t\tend[1] = bodymat[7] + bodymat[5]*2;\r\n\t\t\tend[2] = bodymat[11] + bodymat[6]*2;\r\n\t\t\tCLQ1_DrawLine(lineshader, start, end, 1, 1, 0, 1);\r\n\t\t}\r\n\t}\r\n#endif\r\n\tfor (i = 0; i < doll->numbones; i++)\r\n\t{\r\n\t\tif (doll->bone[i].bodyidx >= 0)\r\n\t\t{\r\n\t\t\t//bones with a body are given an absolute pose matching that body.\r\n\t\t\tsko->world->rbe->RagMatrixFromBody(sko->world, &sko->body[doll->bone[i].bodyidx].odebody, bodymat);\r\n\t\t\tif (doll->body[doll->bone[i].bodyidx].isoffset)\r\n\t\t\t{\r\n\t\t\t\tfloat tmp[12];\r\n\t\t\t\tR_ConcatTransforms((void*)doll->body[doll->bone[i].bodyidx].inverserelmatrix, (void*)bodymat, (void*)tmp);\r\n\t\t\t\tR_ConcatTransforms((void*)invemat, (void*)tmp, (void*)((float*)bmat+i*12));\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t\t//that body matrix is in world space, so transform to model space for our result\r\n\t\t\t\tR_ConcatTransforms((void*)invemat, (void*)bodymat, (void*)((float*)bmat+i*12));\r\n\t\t}\r\n\t\telse if (amat)\t//FIXME: don't do this when the bone has an unanimated child body.\r\n\t\t{\r\n\t\t\t//this bone has no joint object, use the anim sko's relative pose info instead\r\n\t\t\tif (bones[i].parent >= 0)\r\n\t\t\t\tR_ConcatTransforms((void*)(bmat + bones[i].parent*12), (void*)((float*)amat+i*12), (void*)((float*)bmat+i*12));\r\n\t\t\telse\r\n\t\t\t\tmemcpy((void*)((float*)bmat+i*12), (void*)((float*)amat+i*12), sizeof(float)*12);\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\t//copy from the base pose\r\n\t\t\tMatrix3x4_Invert_Simple(bones[i].inverse, bodymat);\r\n\t\t\t//that's the absolute pose...\r\n\t\t\tif (bones[i].parent >= 0)\r\n\t\t\t{\r\n\t\t\t\tR_ConcatTransforms((void*)(bones[bones[i].parent].inverse), (void*)bodymat, (void*)rel);\r\n\t\t\t\tR_ConcatTransforms((void*)(bmat + bones[i].parent*12), (void*)rel, (void*)((float*)bmat+i*12));\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\t//its all absolute when its the root.\r\n\t\t\t\tmemcpy((void*)((float*)bmat+i*12), bodymat, sizeof(float)*12);\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\t//if it wasn't before, it definitely is now.\r\n\tsko->type = SKEL_ABSOLUTE;\r\n}\r\n\r\n//called each physics frame to update the body velocities for animation\r\nvoid rag_doallanimations(world_t *world)\r\n{\r\n\tint i, j;\r\n\tdoll_t *doll;\r\n\tskelobject_t *sko;\r\n\tfor (i = 0; i < numskelobjectsused; i++)\r\n\t{\r\n\t\tsko = &skelobjects[i];\r\n\t\tif (sko->world != world)\r\n\t\t\tcontinue;\r\n\t\tdoll = sko->doll;\r\n\t\tif (!doll || !sko->numanimated)\r\n\t\t\tcontinue;\r\n\r\n\t\tfor (j = 0; j < sko->numbodies; j++)\r\n\t\t{\r\n\t\t\tif (!sko->body[j].animstrength)\r\n\t\t\t\tcontinue;\r\n\t\t\tsko->world->rbe->RagMatrixToBody(&sko->body[j].odebody, sko->body[j].animmatrix);\r\n\t\t}\r\n\t}\r\n}\r\n\r\n#ifndef SERVERONLY\r\nvoid rag_removedeltaent(lerpents_t *le)\r\n{\r\n\textern world_t csqc_world;\r\n\tint skelidx = le->skeletalobject;\r\n\tskelobject_t *skelobj;\r\n\r\n\tif (!skelidx)\r\n\t\treturn;\r\n\tle->skeletalobject = 0;\r\n\r\n\tskelobj = skel_get(&csqc_world, skelidx);\r\n\tif (skelobj)\r\n\t{\r\n\t\tskelobj->inuse = 2;\t//2 means don't reuse yet.\r\n\t\tskelobj->modelindex = 0;\r\n\t\tskelobj->model = NULL;\r\n\t\tpendingkill = true;\r\n\t}\r\n}\r\n\r\n//we received some pos+quat data from the network\r\n//store it into some skeletal object associated with the entity for the renderer to use as needed\r\nvoid rag_lerpdeltaent(lerpents_t *le, unsigned int bonecount, short *newstate, float frac, short *oldstate)\r\n{\r\n\textern world_t csqc_world;\r\n\tint i;\r\n\tfloat sc;\r\n\tvec3_t pos;\r\n\tvec4_t quat, quat1, quat2;\r\n\tvec3_t scale = {1,1,1};\r\n\tskelobject_t *sko;\r\n\tif (le->skeletalobject)\r\n\t{\r\n\t\tsko = skel_get(&csqc_world, le->skeletalobject);\r\n\t\tif (sko->numbones != bonecount)\r\n\t\t{\t//unusable, discard it and create a new one.\r\n\t\t\tsko->inuse = 2;\t//2 means don't reuse yet.\r\n\t\t\tsko->modelindex = 0;\r\n\t\t\tsko->model = NULL;\r\n\t\t\tpendingkill = true;\r\n\t\t\tsko = NULL;\r\n\t\t}\r\n\t}\r\n\telse\r\n\t\tsko = NULL;\r\n\r\n\tif (!sko || sko->inuse != 1)\r\n\t{\r\n\t\tsko = skel_create(&csqc_world, bonecount);\r\n\t\tif (!sko)\r\n\t\t\treturn;\t//couldn't get one, ran out of memory or something?\r\n\t\tsko->modelindex = 0;\r\n\t\tsko->model = NULL;\r\n\t\tsko->type = SKEL_RELATIVE;\r\n\t\tle->skeletalobject = (sko - skelobjects) + 1;\r\n\t}\r\n\r\n\tif (!newstate)\r\n\t{\t//shouldn't happen\r\n\t\tCon_Printf(\"invalid networked bone state\\n\");\r\n\t\trag_removedeltaent(le);\r\n\t\treturn;\r\n\t}\r\n\tif (frac == 1 || !oldstate)\r\n\t{\r\n\t\tfor (i = 0; i < bonecount; i++, newstate += 7)\r\n\t\t{\r\n\t\t\tsc = (newstate[6] > 0)?-1.0/32767:1.0/32767;\r\n\t\t\tVectorScale(newstate, 1.0/64, pos);\r\n\t\t\tVector4Scale(newstate+3, sc, quat);\r\n\t\t\tGenMatrixPosQuat4Scale(pos, quat, scale, sko->bonematrix+i*12);\r\n\t\t}\r\n\t}\r\n\telse\r\n\t{\r\n\t\tfor (i = 0; i < bonecount; i++, oldstate += 7, newstate += 7)\r\n\t\t{\t//this is annoying because of the quat sign\r\n\t\t\tsc = 1.0/64 * (1-frac);\r\n\t\t\tVectorScale(oldstate, sc, pos);\r\n\t\t\tsc = (oldstate[6] > 0)?-1.0/32767:1.0/32767;\r\n\t\t\tVector4Scale(oldstate+3, sc, quat1);\r\n\r\n\t\t\tsc = 1.0/64 * frac;\r\n\t\t\tVectorMA(pos, sc, newstate, pos);\r\n\t\t\tsc = (newstate[6] > 0)?-1.0/32767:1.0/32767;\r\n\t\t\tVector4Scale(newstate+3, sc, quat2);\r\n\r\n\t\t\tQuaternionSlerp(quat1, quat2, frac, quat);\r\n\r\n\t\t\tGenMatrixPosQuat4Scale(pos, quat, scale, sko->bonematrix+i*12);\r\n\t\t}\r\n\t}\r\n}\r\n\r\nvoid rag_updatedeltaent(world_t *w, entity_t *ent, lerpents_t *le)\r\n{\r\n\tmodel_t *mod = ent->model;\r\n\tskelobject_t *sko;\r\n\tfloat emat[12];\r\n\tskelobject_t skorel = {0};\r\n\tfloat relmat[MAX_BONES*12];\r\n\tskorel.bonematrix = relmat;\r\n\tskorel.type = SKEL_RELATIVE;\r\n\r\n\tif (mod->dollinfo)\r\n\t{\r\n\t\tif (!w->rbe)\r\n\t\t\treturn;\r\n\r\n\t\tif (!le->skeletalobject)\r\n\t\t{\r\n\t\t\tsko = skel_create(w, Mod_GetNumBones(mod, false));\r\n\t\t\tif (!sko)\r\n\t\t\t\treturn;\t//couldn't get one, ran out of memory or something?\r\n\t\t\tsko->modelindex = 0;\r\n\t\t\tsko->model = mod;\r\n\t\t\tsko->type = SKEL_RELATIVE;\r\n\t\t\tle->skeletalobject = (sko - skelobjects) + 1;\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tsko = skel_get(w, le->skeletalobject);\r\n\t\t\tif (!sko)\r\n\t\t\t{\r\n\t\t\t\tle->skeletalobject = 0;\r\n\t\t\t\treturn;\t//couldn't get one, ran out of memory or something?\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tskorel.numbones = sko->numbones;\r\n\r\n\t\t//FIXME: provide some way for the animation to auto-trigger ragdoll (so framegroups can work automagically)\r\n\t\tif ((ent->framestate.g[FS_REG].frame[0] & 0x8000) || (ent->framestate.g[FS_REG].frame[1] & 0x8000))\r\n\t\t\tsko->numanimated = 0;\r\n\t\telse if (sko->doll)\r\n\t\t\tsko->numanimated = sko->doll->numdefaultanimated;\r\n\t\tMod_GetBoneRelations(mod, 0, skorel.numbones, NULL, &ent->framestate, skorel.bonematrix);\r\n\t\tskorel.modelindex = sko->modelindex;\r\n\t\tskorel.model = sko->model;\r\n\t\tif (sko->numanimated || sko->doll != mod->dollinfo)\r\n\t\t{\r\n//\t\t\tsko->type = SKEL_ABSOLUTE;\r\n//\t\t\tAlias_ForceConvertBoneData(skorel.type, skorel.bonematrix, skorel.numbones, bones, sko->type, sko->bonematrix, sko->numbones);\r\n\t\t\tskel_copy_toabs(sko, &skorel, 0, sko->numbones);\r\n\t\t}\r\n\r\n\t\tbonemat_fromaxisorg(emat, ent->axis, ent->origin);\r\n\r\n\t\tif (sko->doll != mod->dollinfo)\r\n\t\t{\r\n\t\t\trag_uninstanciate(sko);\r\n\t\t\trag_instanciate(sko, mod->dollinfo, emat, NULL);\r\n\t\t}\r\n\t\tif (sko->numanimated)\r\n\t\t\trag_animate(sko, sko->doll, emat);\r\n\r\n\t\trag_derive(sko, sko->numanimated?&skorel:NULL, emat);\r\n\r\n\t\tent->framestate.bonestate = sko->bonematrix;\r\n\t\tent->framestate.bonecount = sko->numbones;\r\n\t\tent->framestate.skeltype = sko->type;\r\n\t}\r\n\telse if (le->skeletalobject)\r\n\t{\r\n\t\tsko = skel_get(w, le->skeletalobject);\r\n\t\tif (!sko)\r\n\t\t{\r\n\t\t\tle->skeletalobject = 0;\r\n\t\t\treturn;\t//couldn't get one, ran out of memory or something?\r\n\t\t}\r\n\r\n\t\tent->framestate.bonestate = sko->bonematrix;\r\n\t\tent->framestate.bonecount = sko->numbones;\r\n\t\tent->framestate.skeltype = sko->type;\r\n\t}\r\n}\r\n#endif\r\n#endif\r\n#endif\r\n\r\n#ifdef SKELETALOBJECTS\r\n//update a skeletal object to track its ragdoll/apply a ragdoll to a skeletal object.\r\nvoid QCBUILTIN PF_skel_ragedit(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\r\n{\r\n\t//do we want to be able to generate a ragdoll object with this function too?\r\n#ifdef RAGDOLL\r\n\tworld_t *w = prinst->parms->user;\r\n\twedict_t *wed = (wedict_t*)G_EDICT(prinst, OFS_PARM0);\r\n\tconst char *ragname = PR_GetStringOfs(prinst, OFS_PARM1);\r\n\tint parentskel = G_FLOAT(OFS_PARM2);\r\n\tint skelidx;\r\n\tskelobject_t *sko, *psko;\r\n\tdoll_t *doll;\r\n\tfloat emat[12];\r\n\r\n\tpvec3_t d[3], a;\r\n\t//fixme: respond to renderflags&USEAXIS? scale?\r\n\ta[0] = wed->v->angles[0] * r_meshpitch.value; /*mod_alias bug*/\r\n\ta[1] = wed->v->angles[1];\r\n\ta[2] = wed->v->angles[2] * r_meshroll.value; /*hexen2 bug*/\r\n\tAngleVectors(a, d[0], d[1], d[2]);\r\n\tbonemat_fromqcvectors(emat, d[0], d[1], d[2], wed->v->origin);\r\n\tskelidx = wed->xv->skeletonindex;\r\n\r\n\tG_FLOAT(OFS_RETURN) = 0;\r\n\r\n\t//the parent skeletal object must be relative, if specified.\r\n\tpsko = skel_get(w, parentskel);\r\n\tif (psko && psko->type != SKEL_RELATIVE)\r\n\t\treturn;\r\n\r\n\tsko = skel_get(w, skelidx);\r\n\tif (!sko)\r\n\t{\r\n\t\tCon_DPrintf(\"PF_skel_ragedit: invalid skeletal object\\n\");\r\n\t\treturn;\r\n\t}\r\n\r\n\tif (!sko->world->rbe)\r\n\t{\r\n\t\tCon_DPrintf(\"PF_skel_ragedit: rigid body system not enabled\\n\");\r\n\t\treturn;\r\n\t}\r\n\r\n\tif (sko->doll)\t//use the current doll\r\n\t\tdoll = sko->doll;\r\n\telse if (sko->model) //\r\n\t\tdoll = sko->model->dollinfo;\r\n\telse\r\n\t\tdoll = NULL;\r\n\r\n\tif (*ragname)\r\n\t{\r\n\t\tchar *cmd;\r\n\r\n\t\tragname = Cmd_TokenizeString(ragname, false, false);\r\n\t\tcmd = Cmd_Argv(0);\r\n\t\tif (!stricmp(cmd, \"enablejoint\"))\r\n\t\t{\r\n\t\t\tint idx;\r\n\t\t\tint enable = atoi(Cmd_Argv(2));\r\n\t\t\tif (!sko->doll)\r\n\t\t\t{\r\n\t\t\t\tskel_copy_toabs(sko, psko?psko:sko, 0, sko->numbones);\r\n\t\t\t\tif (!doll || !rag_instanciate(sko, doll, emat, wed))\r\n\t\t\t\t{\r\n\t\t\t\t\tCon_Printf(\"enablejoint: doll not instanciated yet\\n\");\r\n\t\t\t\t\treturn;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\tidx = rag_finddolljoint(sko->doll, Cmd_Argv(1));\r\n\t\r\n\t\t\tif (idx >= 0)\r\n\t\t\t{\r\n\t\t\t\tsko->world->rbe->RagEnableJoint(&sko->joint[idx], enable);\r\n\t\t\t\tG_FLOAT(OFS_RETURN) = 1;\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tCon_Printf(\"enablejoint: %s is not defined as a ragdoll joint\\n\", Cmd_Argv(1));\r\n\t\t\t\tG_FLOAT(OFS_RETURN) = 0;\r\n\t\t\t}\r\n\t\t\treturn;\r\n\t\t}\r\n\t\telse if (!stricmp(cmd, \"animatebody\"))\r\n\t\t{\r\n\t\t\tint body;\r\n\t\t\tfloat strength = atof(Cmd_Argv(2));\r\n\t\t\tif (!sko->doll)\r\n\t\t\t{\r\n\t\t\t\tskel_copy_toabs(sko, psko?psko:sko, 0, sko->numbones);\r\n\t\t\t\tif (!doll || !rag_instanciate(sko, doll, emat, wed))\r\n\t\t\t\t{\r\n\t\t\t\t\tCon_Printf(\"animatebody: doll not instanciated yet\\n\");\r\n\t\t\t\t\treturn;\r\n\t\t\t\t}\r\n\t\t\t}\t\r\n\t\t\tbody = rag_finddollbody(sko->doll, Cmd_Argv(1));\r\n\t\t\tif (body >= 0)\r\n\t\t\t{\r\n\t\t\t\tif (sko->body[body].animstrength)\r\n\t\t\t\t\tsko->numanimated--;\r\n\t\t\t\tsko->body[body].animstrength = strength;\r\n\t\t\t\tif (sko->body[body].animstrength)\r\n\t\t\t\t\tsko->numanimated++;\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t\tCon_Printf(\"animatebody: %s is not defined as a ragdoll body\\n\", Cmd_Argv(1));\r\n\t\t\tG_FLOAT(OFS_RETURN) = sko->numanimated;\r\n\t\t\treturn;\r\n\t\t}\r\n\t\telse if (!stricmp(cmd, \"animate\"))\r\n\t\t{\r\n\t\t\tfloat strength = atof(Cmd_Argv(1));\r\n\t\t\tint i;\r\n\t\t\tif (!sko->doll)\r\n\t\t\t{\r\n\t\t\t\tskel_copy_toabs(sko, psko?psko:sko, 0, sko->numbones);\r\n\t\t\t\tif (!doll || !rag_instanciate(sko, doll, emat, wed))\r\n\t\t\t\t{\r\n\t\t\t\t\tCon_Printf(\"animate: doll not instanciated yet\\n\");\r\n\t\t\t\t\treturn;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\tsko->numanimated = 0;\r\n\r\n\t\t\tfor (i = 0; i < sko->numbodies; i++)\r\n\t\t\t{\r\n\t\t\t\tsko->body[i].animstrength = sko->doll->body[i].animate * strength;\r\n\t\t\t\tif (sko->body[i].animstrength)\r\n\t\t\t\t\tsko->numanimated++;\r\n\t\t\t}\r\n\r\n\t\t\tif (sko->numanimated)\r\n\t\t\t{\r\n\t\t\t\t//make sure the animation target is valid.\r\n\t\t\t\tskel_copy_toabs(sko, psko?psko:sko, 0, sko->numbones);\r\n\t\t\t\trag_animate(sko, sko->doll, emat);\r\n\t\t\t}\r\n\t\t\tG_FLOAT(OFS_RETURN) = 1;\r\n\t\t\treturn;\r\n\t\t}\r\n\t\telse if (!stricmp(cmd, \"doll\"))\r\n\t\t\tdoll = sko->model?rag_loaddoll(sko->model, Cmd_Argv(1), sko->numbones):NULL;\r\n\t\telse if (!stricmp(cmd, \"dollstring\"))\r\n\t\t\tdoll = sko->model?rag_createdollfromstring(sko->model, \"\", sko->numbones, ragname):NULL;\r\n\t\telse if (!stricmp(cmd, \"cleardoll\"))\r\n\t\t\tdoll = NULL;\r\n\t\telse\r\n\t\t{\r\n\t\t\tCon_Printf(\"PF_skel_ragedit: Unsupported command.\\n\");\r\n\t\t\treturn;\r\n\t\t}\r\n\t}\r\n\r\n\tif (sko->doll != doll)\r\n\t{\r\n\t\trag_uninstanciate(sko);\r\n\t\tif (!doll)\r\n\t\t{\r\n\t\t\tG_FLOAT(OFS_RETURN) = 1;\t//technically success.\r\n\t\t\treturn;\r\n\t\t}\r\n\t\tskel_copy_toabs(sko, psko?psko:sko, 0, sko->numbones);\r\n\t\tif (!rag_instanciate(sko, doll, emat, wed))\r\n\t\t{\r\n\t\t\trag_uninstanciate(sko);\r\n\t\t\tCon_DPrintf(\"PF_skel_ragedit: unable to instanciate objects\\n\");\r\n\t\t\tG_FLOAT(OFS_RETURN) = 0;\r\n\t\t\treturn;\r\n\t\t}\r\n\t\tif (sko->numanimated)\r\n\t\t\trag_animate(sko, doll, emat);\r\n\t}\r\n\telse if (!doll)\r\n\t{\r\n\t\tG_FLOAT(OFS_RETURN) = !!*ragname;\t//technically success.\r\n\t\treturn;\r\n\t}\r\n\telse if (sko->numanimated)\r\n\t{\r\n\t\tskel_copy_toabs(sko, psko?psko:sko, 0, sko->numbones);\r\n\t\trag_animate(sko, doll, emat);\r\n\t}\r\n\r\n\tif (psko == sko)\r\n\t{\r\n\t\tCon_Printf(\"PF_skel_ragedit: cannot use the same skeleton for animation source\\n\");\r\n\t\tG_FLOAT(OFS_RETURN) = 0;\r\n\t\treturn;\r\n\t}\r\n\trag_derive(sko, psko, emat);\r\n\tG_FLOAT(OFS_RETURN) = 1;\r\n#endif\r\n}\r\n\r\n//float(float modelindex) skel_create (FTE_CSQC_SKELETONOBJECTS)\r\nvoid QCBUILTIN PF_skel_create (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\r\n{\r\n\tworld_t *w = prinst->parms->user;\r\n\r\n\tint numbones;\r\n\tskelobject_t *skelobj;\r\n\tmodel_t *model;\r\n\tint midx;\r\n\tint type;\r\n\r\n\tmidx = G_FLOAT(OFS_PARM0);\r\n\ttype = (prinst->callargc > 1)?G_FLOAT(OFS_PARM1):SKEL_RELATIVE;\r\n\r\n\t//default to failure\r\n\tG_FLOAT(OFS_RETURN) = 0;\r\n\r\n\tmodel = w->Get_CModel(w, midx);\r\n\tif (!model)\r\n\t\treturn; //no model set, can't get a skeleton\r\n\r\n\tnumbones = Mod_GetNumBones(model, type != SKEL_RELATIVE);\r\n\tif (!numbones)\r\n\t{\r\n//\t\tisabs = true;\r\n//\t\tnumbones = Mod_GetNumBones(model, isabs);\r\n//\t\tif (!numbones)\r\n\t\t\treturn;\t//this isn't a skeletal model.\r\n\t}\r\n\r\n\tskelobj = skel_create(w, numbones);\r\n\tif (!skelobj)\r\n\t\treturn;\t//couldn't get one, ran out of memory or something?\r\n\r\n\tskelobj->modelindex = midx;\r\n\tskelobj->model = model;\r\n\tskelobj->type = type;\r\n\r\n\t/*\r\n\tfor (i = 0; i < numbones; i++)\r\n\t{\r\n\t\tgaliasbone_t *bones = Mod_GetBoneInfo(skelobj->model);\r\n\t\tMatrix3x4_Invert_Simple(bones[i].inverse, skelobj->bonematrix + i*12);\r\n\t}\r\n\tskelobj->type = SKOT_ABSOLUTE;\r\n\t*/\r\n\r\n\tG_FLOAT(OFS_RETURN) = (skelobj - skelobjects) + 1;\r\n}\r\n\r\n//float(float skel, entity ent, float modelindex, float retainfrac, float firstbone, float lastbone) skel_build (FTE_CSQC_SKELETONOBJECTS)\r\nvoid QCBUILTIN PF_skel_build(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\r\n{\r\n\tworld_t *w = prinst->parms->user;\r\n\tint skelidx = G_FLOAT(OFS_PARM0);\r\n\twedict_t *ent = (wedict_t*)G_EDICT(prinst, OFS_PARM1);\r\n\tint midx = G_FLOAT(OFS_PARM2);\r\n\tfloat retainfrac = G_FLOAT(OFS_PARM3);\r\n\tint firstbone = G_FLOAT(OFS_PARM4)-1;\r\n\tint lastbone = G_FLOAT(OFS_PARM5)-1;\r\n\tfloat addition = (prinst->callargc>6)?G_FLOAT(OFS_PARM6):1-retainfrac;\r\n\r\n\tint i, j;\r\n\tint numbones;\r\n\tframestate_t fstate;\r\n\tskelobject_t *skelobj;\r\n\tmodel_t *model;\r\n\tgaliasbone_t *boneinfo;\r\n\r\n\t//default to failure\r\n\tG_FLOAT(OFS_RETURN) = 0;\r\n\r\n\tmodel = w->Get_CModel(w, midx);\r\n\tif (!model)\r\n\t\treturn; //invalid model, can't get a skeleton\r\n\r\n\tw->Get_FrameState(w, ent, &fstate);\r\n\r\n\t//heh... don't copy.\r\n\tfstate.bonecount = 0;\r\n\tfstate.bonestate = NULL;\r\n\r\n\tif (!skelidx)\r\n\t{\r\n\t\tnumbones = Mod_GetNumBones(model, false);\r\n\t\tif (!numbones)\r\n\t\t{\r\n\t\t\treturn;\t//this isn't a skeletal model.\r\n\t\t}\r\n\t\tskelobj = skel_create(w, numbones);\r\n\t}\r\n\telse\r\n\t\tskelobj = skel_get(w, skelidx);\r\n\tif (!skelobj)\r\n\t\treturn;\t//couldn't get one, ran out of memory or something?\r\n\r\n\tif (skelobj->model)\r\n\t\tboneinfo = Mod_GetBoneInfo(skelobj->model, &numbones);\r\n\telse\r\n\t\tboneinfo = NULL;\r\n\tnumbones = skelobj->numbones;\r\n\r\n\r\n\tif (lastbone < 0)\r\n\t\tlastbone = numbones;\r\n\tif (lastbone > numbones)\r\n\t\tlastbone = numbones;\r\n\tif (firstbone < 0)\r\n\t\tfirstbone = 0;\r\n\tif (lastbone < firstbone)\r\n\t\tlastbone = firstbone;\r\n\r\n\tif (skelobj->type != SKEL_RELATIVE)\r\n\t{\r\n\t\tif (firstbone > 0 || lastbone < skelobj->numbones || retainfrac)\r\n\t\t{\r\n\t\t\tCon_Printf(\"skel_build on non-relative skeleton\\n\");\r\n\t\t\treturn;\r\n\t\t}\r\n\t\tskelobj->type = SKEL_RELATIVE;\t//entire model will get replaced, convert it.\r\n\t}\r\n\r\n\tif (retainfrac == 0)\r\n\t{\r\n\t\tif (addition == 0) /*wipe it*/\r\n\t\t\tmemset(skelobj->bonematrix + firstbone*12, 0, sizeof(float)*12*(lastbone-firstbone));\r\n\t\telse if (addition == 1) /*replace everything*/\r\n\t\t\tMod_GetBoneRelations(model, firstbone, lastbone, boneinfo, &fstate, skelobj->bonematrix);\r\n\t\telse\r\n\t\t{\r\n\t\t\t//scale new\r\n\t\t\tfloat relationsbuf[MAX_BONES*12];\r\n\t\t\tMod_GetBoneRelations(model, firstbone, lastbone, boneinfo, &fstate, relationsbuf);\r\n\t\t\tfor (i = firstbone; i < lastbone; i++)\r\n\t\t\t{\r\n\t\t\t\tfor (j = 0; j < 12; j++)\r\n\t\t\t\t\tskelobj->bonematrix[i*12+j] = addition*relationsbuf[i*12+j];\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\telse\r\n\t{\r\n\t\tfloat relationsbuf[MAX_BONES*12];\r\n\r\n\t\tif (retainfrac != 1)\r\n\t\t{\r\n\t\t\t//rescale the existing bones\r\n\t\t\tfor (i = firstbone; i < lastbone; i++)\r\n\t\t\t{\r\n\t\t\t\tfor (j = 0; j < 12; j++)\r\n\t\t\t\t\tskelobj->bonematrix[i*12+j] *= retainfrac;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t//just add\r\n\t\tMod_GetBoneRelations(model, firstbone, lastbone, boneinfo, &fstate, relationsbuf);\r\n\t\tif (addition == 1)\r\n\t\t{\r\n\t\t\tfor (i = firstbone; i < lastbone; i++)\r\n\t\t\t{\r\n\t\t\t\tfor (j = 0; j < 12; j++)\r\n\t\t\t\t\tskelobj->bonematrix[i*12+j] += relationsbuf[i*12+j];\r\n\t\t\t}\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tfor (i = firstbone; i < lastbone; i++)\r\n\t\t\t{\r\n\t\t\t\tfor (j = 0; j < 12; j++)\r\n\t\t\t\t\tskelobj->bonematrix[i*12+j] += addition*relationsbuf[i*12+j];\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\tG_FLOAT(OFS_RETURN) = (skelobj - skelobjects) + 1;\r\n}\r\n\r\ntypedef struct\r\n{\r\n\tint sourcemodelindex;\r\n\tint sourceskel;\r\n\tint firstbone;\r\n\tint lastbone;\r\n\tfloat prescale;\t//0 destroys existing data, 1 retains it.\r\n\tfloat scale[4];\r\n\tint animation[4];\r\n\tfloat animationtime[4];\r\n\r\n\t//halflife models\r\n\tfloat subblend[2];\r\n\tfloat controllers[5];\r\n} skelblend_t;\r\n//float(float skel, int numblends, skelblend_t *blenddata, int structsize) skel_build_ptr\r\nvoid QCBUILTIN PF_skel_build_ptr(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\r\n{\r\n\tworld_t *w = prinst->parms->user;\r\n\tint skelidx = G_FLOAT(OFS_PARM0);\r\n\tint numblends = G_INT(OFS_PARM1);\r\n\tint blends_qcptr = G_INT(OFS_PARM2);\r\n\tint structsize = G_INT(OFS_PARM3);\r\n\r\n\tskelblend_t *fte_restrict blends = (skelblend_t*)(prinst->stringtable+blends_qcptr);\r\n\tint i, j;\r\n\tframestate_t fstate;\r\n\tskelobject_t *skelobj;\r\n\r\n\tfloat relationsbuf[MAX_BONES*12];\r\n\tfloat scale;\r\n\tint numbones, firstbone, lastbone;\r\n\tmodel_t *model;\r\n\tqboolean noadd;\r\n\tconst galiasbone_t *boneinfo = NULL;\r\n\r\n\t//default to failure\r\n\tG_FLOAT(OFS_RETURN) = 0;\r\n\r\n\tmemset(&fstate, 0, sizeof(fstate));\r\n\r\n\tskelobj = skel_get(w, skelidx);\r\n\tif (!skelobj)\r\n\t\treturn;\t//couldn't get one, ran out of memory or something?\r\n\r\n\tif (structsize < sizeof(skelblend_t))\r\n\t\treturn;\t//enforce a minimum size...\r\n\r\n\tfor(; numblends --> 0; blends = (skelblend_t*)((char*)blends + structsize))\r\n\t{\r\n\t\tnoadd = blends->scale[0] == 0 && blends->scale[1] == 0 && blends->scale[2] == 0 && blends->scale[3] == 0;\r\n\t\tif (blends->prescale == 1 && noadd)\r\n\t\t\tcontinue;\t//does nothing to the model, skip it before wasting too much time.\r\n\t\t\t\r\n\t\tif (blends->sourceskel)\r\n\t\t{\r\n\t\t\tskelobject_t *srcskel = skel_get(w, blends->sourceskel);\r\n\t\t\tif (!srcskel)\r\n\t\t\t\tcontinue;\r\n\t\t\tmodel = srcskel->model;\r\n\t\t\tif (!model)\r\n\t\t\t\tcontinue; //invalid model, can't get a skeleton\r\n\t\t\tfstate.bonecount = numbones = srcskel->numbones;\r\n\t\t\tfstate.bonestate = srcskel->bonematrix;\r\n\t\t\tfstate.skeltype = srcskel->type;\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tfstate.bonecount = 0;\r\n\t\t\tfstate.bonestate = NULL;\r\n\t\t\tfstate.skeltype = SKEL_RELATIVE;\r\n\r\n\t\t\tif (blends->sourcemodelindex)\r\n\t\t\t\tmodel = w->Get_CModel(w, blends->sourcemodelindex);\r\n\t\t\telse\r\n\t\t\t\tmodel = w->Get_CModel(w, skelobj->modelindex);\r\n\t\t\tif (!model)\r\n\t\t\t\tcontinue; //invalid model, can't get a skeleton\r\n\r\n\t\t\tnumbones = Mod_GetNumBones(model, false);\r\n\t\t\tif (!numbones)\r\n\t\t\t\tcontinue;\t//this isn't a skeletal model.\r\n\t\t}\r\n\r\n\t\tfirstbone = blends->firstbone-1;\r\n\t\tlastbone = blends->lastbone-1;\r\n\r\n\t\tif (lastbone < 0)\r\n\t\t\tlastbone = numbones;\r\n\t\tif (lastbone > numbones)\r\n\t\t\tlastbone = numbones;\r\n\t\tif (firstbone < 0)\r\n\t\t\tfirstbone = 0;\r\n\t\tif (lastbone < firstbone)\r\n\t\t\tlastbone = firstbone;\r\n\r\n\t\tfstate.g[FS_REG].endbone = 0x7fffffff;\r\n\t\tfor (i = 0; i < FRAME_BLENDS; i++)\r\n\t\t{\r\n\t\t\tfstate.g[FS_REG].frame[i] = blends->animation[i];\r\n\t\t\tfstate.g[FS_REG].frametime[i] = blends->animationtime[i];\r\n\t\t\tfstate.g[FS_REG].lerpweight[i] = blends->scale[i];\r\n\t\t}\r\n#ifdef HALFLIFEMODELS\r\n\t\tfstate.g[FS_REG].subblendfrac = blends->subblend[0];\r\n\t\tfstate.g[FS_REG].subblend2frac = blends->subblend[1];\r\n\t\tfstate.bonecontrols[0] = blends->controllers[0];\r\n\t\tfstate.bonecontrols[1] = blends->controllers[1];\r\n\t\tfstate.bonecontrols[2] = blends->controllers[2];\r\n\t\tfstate.bonecontrols[3] = blends->controllers[3];\r\n\t\tfstate.bonecontrols[4] = blends->controllers[4];\r\n#endif\r\n\r\n\t\tif (skelobj->type != SKEL_RELATIVE)\r\n\t\t{\r\n\t\t\tif (firstbone > 0 || lastbone < skelobj->numbones || blends->prescale)\r\n\t\t\t{\r\n\t\t\t\tCon_Printf(\"skel_build on non-relative skeleton\\n\");\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\t\t\tskelobj->type = SKEL_RELATIVE;\t//entire model will get replaced, convert it.\r\n\t\t}\r\n\t\tif (noadd)\r\n\t\t{\r\n\t\t\tif (blends->prescale == 0)\r\n\t\t\t\tmemset(skelobj->bonematrix+firstbone*12, 0, sizeof(float)*12);\r\n\t\t\telse\r\n\t\t\t{\t//just rescale the existing bones\r\n\t\t\t\tscale = blends->prescale;\r\n\t\t\t\tfor (i = firstbone; i < lastbone; i++)\r\n\t\t\t\t{\r\n\t\t\t\t\tfor (j = 0; j < 12; j++)\r\n\t\t\t\t\t\tskelobj->bonematrix[i*12+j] *= scale;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t\telse if (blends->prescale == 0) //new data only. directly replace the existing data\r\n\t\t\tMod_GetBoneRelations(model, firstbone, lastbone, boneinfo, &fstate, skelobj->bonematrix);\r\n\t\telse\r\n\t\t{\r\n\t\t\tif (blends->prescale != 1)\r\n\t\t\t{\t//rescale the existing bones\r\n\t\t\t\tscale = blends->prescale;\r\n\t\t\t\tfor (i = firstbone; i < lastbone; i++)\r\n\t\t\t\t{\r\n\t\t\t\t\tfor (j = 0; j < 12; j++)\r\n\t\t\t\t\t\tskelobj->bonematrix[i*12+j] *= scale;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\tMod_GetBoneRelations(model, firstbone, lastbone, boneinfo, &fstate, relationsbuf);\r\n\t\t\tfor (i = firstbone; i < lastbone; i++)\r\n\t\t\t{\r\n\t\t\t\tfor (j = 0; j < 12; j++)\r\n\t\t\t\t\tskelobj->bonematrix[i*12+j] += relationsbuf[i*12+j];\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\tG_FLOAT(OFS_RETURN) = (skelobj - skelobjects) + 1;\r\n}\r\n\r\n//float(float skel) skel_get_numbones (FTE_CSQC_SKELETONOBJECTS)\r\nvoid QCBUILTIN PF_skel_get_numbones (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\r\n{\r\n\tworld_t *w = prinst->parms->user;\r\n\tint skelidx = G_FLOAT(OFS_PARM0);\r\n\tskelobject_t *skelobj;\r\n\r\n\tskelobj = skel_get(w, skelidx);\r\n\r\n\tif (!skelobj)\r\n\t\tG_FLOAT(OFS_RETURN) = 0;\r\n\telse\r\n\t\tG_FLOAT(OFS_RETURN) = skelobj->numbones;\r\n}\r\n\r\n//string(float skel, float bonenum) skel_get_bonename (FTE_CSQC_SKELETONOBJECTS) (returns tempstring)\r\nvoid QCBUILTIN PF_skel_get_bonename (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\r\n{\r\n\tworld_t *w = prinst->parms->user;\r\n\tint skelidx = G_FLOAT(OFS_PARM0);\r\n\tint boneidx = G_FLOAT(OFS_PARM1);\r\n\tskelobject_t *skelobj;\r\n\r\n\tskelobj = skel_get(w, skelidx);\r\n\r\n\tif (!skelobj)\r\n\t\tG_INT(OFS_RETURN) = 0;\r\n\telse\r\n\t{\r\n\t\tRETURN_TSTRING(Mod_GetBoneName(skelobj->model, boneidx));\r\n\t}\r\n}\r\n\r\n//float(float skel, float bonenum) skel_get_boneparent (FTE_CSQC_SKELETONOBJECTS)\r\nvoid QCBUILTIN PF_skel_get_boneparent (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\r\n{\r\n\tworld_t *w = prinst->parms->user;\r\n\tint skelidx = G_FLOAT(OFS_PARM0);\r\n\tint boneidx = G_FLOAT(OFS_PARM1);\r\n\tskelobject_t *skelobj;\r\n\r\n\tskelobj = skel_get(w, skelidx);\r\n\r\n\tif (!skelobj)\r\n\t\tG_FLOAT(OFS_RETURN) = 0;\r\n\telse\r\n\t\tG_FLOAT(OFS_RETURN) = Mod_GetBoneParent(skelobj->model, boneidx);\r\n}\r\n\r\n//float(float skel, string tagname) skel_find_bone (FTE_CSQC_SKELETONOBJECTS)\r\nvoid QCBUILTIN PF_skel_find_bone (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\r\n{\r\n\tworld_t *w = prinst->parms->user;\r\n\tint skelidx = G_FLOAT(OFS_PARM0);\r\n\tconst char *bname = PR_GetStringOfs(prinst, OFS_PARM1);\r\n\tskelobject_t *skelobj;\r\n\r\n\tskelobj = skel_get(w, skelidx);\r\n\tif (!skelobj)\r\n\t\tG_FLOAT(OFS_RETURN) = 0;\r\n\telse\r\n\t\tG_FLOAT(OFS_RETURN) = Mod_TagNumForName(skelobj->model, bname, 0);\r\n}\r\n\r\n//vector(float skel, float bonenum) skel_get_bonerel (FTE_CSQC_SKELETONOBJECTS) (sets v_forward etc)\r\nvoid QCBUILTIN PF_skel_get_bonerel (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\r\n{\r\n\tworld_t *w = prinst->parms->user;\r\n\tint skelidx = G_FLOAT(OFS_PARM0);\r\n\tint boneidx = G_FLOAT(OFS_PARM1)-1;\r\n\tskelobject_t *skelobj = skel_get(w, skelidx);\r\n\tif (!skelobj || (unsigned int)boneidx >= skelobj->numbones)\r\n\t\tbonematident_toqcvectors(w->g.v_forward, w->g.v_right, w->g.v_up, G_VECTOR(OFS_RETURN));\r\n\telse if (skelobj->type!=SKEL_RELATIVE)\r\n\t{\r\n\t\tfloat tmp[12];\r\n\t\tfloat invparent[12];\r\n\t\tint parent;\r\n\t\t/*invert the parent, multiply that against the child, we now know the transform required to go from parent to child. woo.*/\r\n\t\tparent = Mod_GetBoneParent(skelobj->model, boneidx+1)-1;\r\n\t\tMatrix3x4_Invert(skelobj->bonematrix+12*parent, invparent);\r\n\t\tMatrix3x4_Multiply(invparent, skelobj->bonematrix+12*boneidx, tmp);\r\n\t\tbonemat_toqcvectors(tmp, w->g.v_forward, w->g.v_right, w->g.v_up, G_VECTOR(OFS_RETURN));\r\n\t}\r\n\telse\r\n\t\tbonemat_toqcvectors(skelobj->bonematrix+12*boneidx, w->g.v_forward, w->g.v_right, w->g.v_up, G_VECTOR(OFS_RETURN));\r\n}\r\n\r\n//vector(float skel, float bonenum) skel_get_boneabs (FTE_CSQC_SKELETONOBJECTS) (sets v_forward etc)\r\nvoid QCBUILTIN PF_skel_get_boneabs (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\r\n{\r\n\tworld_t *w = prinst->parms->user;\r\n\tint skelidx = G_FLOAT(OFS_PARM0);\r\n\tint boneidx = G_FLOAT(OFS_PARM1)-1;\r\n\tfloat workingm[12], tempmatrix[3][4];\r\n\tint i;\r\n\tskelobject_t *skelobj = skel_get(w, skelidx);\r\n\r\n\tif (!skelobj || (unsigned int)boneidx >= skelobj->numbones)\r\n\t\tbonematident_toqcvectors(w->g.v_forward, w->g.v_right, w->g.v_up, G_VECTOR(OFS_RETURN));\r\n\telse if (skelobj->type != SKEL_RELATIVE)\r\n\t{\r\n\t\t//can just copy it out\r\n\t\tbonemat_toqcvectors(skelobj->bonematrix + boneidx*12, w->g.v_forward, w->g.v_right, w->g.v_up, G_VECTOR(OFS_RETURN));\r\n\t}\r\n\telse\r\n\t{\r\n\t\t//we need to work out the abs position\r\n\r\n\t\t//testme\r\n\r\n\t\t//set up an identity matrix\r\n\t\tfor (i = 0;i < 12;i++)\r\n\t\t\tworkingm[i] = 0;\r\n\t\tworkingm[0] = 1;\r\n\t\tworkingm[5] = 1;\r\n\t\tworkingm[10] = 1;\r\n\r\n\t\twhile(boneidx >= 0)\r\n\t\t{\r\n\t\t\t//copy out the previous working matrix, so we don't stomp on it\r\n\t\t\tmemcpy(tempmatrix, workingm, sizeof(tempmatrix));\r\n\t\t\tR_ConcatTransforms((void*)(skelobj->bonematrix + boneidx*12), (void*)tempmatrix, (void*)workingm);\r\n\r\n\t\t\tboneidx = Mod_GetBoneParent(skelobj->model, boneidx+1)-1;\r\n\t\t}\r\n\t\tbonemat_toqcvectors(workingm, w->g.v_forward, w->g.v_right, w->g.v_up, G_VECTOR(OFS_RETURN));\r\n\t}\r\n}\r\n\r\n//void(entity ent, float bonenum, vector org, optional fwd, right, up) skel_set_bone_world (FTE_CSQC_SKELETONOBJECTS2) (reads v_forward etc)\r\nvoid QCBUILTIN PF_skel_set_bone_world (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\r\n{\r\n\tworld_t *w = prinst->parms->user;\r\n\twedict_t *ent = G_WEDICT(prinst, OFS_PARM0);\r\n\tunsigned int boneidx = G_FLOAT(OFS_PARM1)-1;\r\n\tpvec_t *matrix[3];\r\n\tskelobject_t *skelobj;\r\n\tfloat *bone;\r\n\tfloat childworld[12], parentinv[12];\r\n\r\n\t/*sort out the parameters*/\r\n\tif (prinst->callargc == 4)\r\n\t{\r\n\t\tpvec3_t d[3], a;\r\n\t\ta[0] = G_VECTOR(OFS_PARM3)[0] * r_meshpitch.value; /*mod_alias bug*/\r\n\t\ta[1] = G_VECTOR(OFS_PARM3)[1];\r\n\t\ta[2] = G_VECTOR(OFS_PARM3)[2];\r\n\t\tAngleVectors(a, d[0], d[1], d[2]);\r\n\t\tbonemat_fromqcvectors(childworld, d[0], d[1], d[2], G_VECTOR(OFS_PARM2));\r\n\t}\r\n\telse\r\n\t{\r\n\t\tif (prinst->callargc > 5)\r\n\t\t{\r\n\t\t\tmatrix[0] = G_VECTOR(OFS_PARM3);\r\n\t\t\tmatrix[1] = G_VECTOR(OFS_PARM4);\r\n\t\t\tmatrix[2] = G_VECTOR(OFS_PARM5);\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tmatrix[0] = w->g.v_forward;\r\n\t\t\tmatrix[1] = w->g.v_right;\r\n\t\t\tmatrix[2] = w->g.v_up;\r\n\t\t}\r\n\t\tbonemat_fromqcvectors(childworld, matrix[0], matrix[1], matrix[2], G_VECTOR(OFS_PARM2));\r\n\t}\r\n\r\n\t/*make sure the skeletal object is correct*/\r\n\tskelobj = skel_get(w, ent->xv->skeletonindex);\r\n\tif (!skelobj || boneidx >= skelobj->numbones)\r\n\t\treturn;\r\n\r\n\t/*get the inverse of the parent matrix*/\r\n\t{\r\n\t\tfloat parentabs[12];\r\n\t\tfloat parentw[12];\r\n\t\tfloat parentent[12];\r\n\t\tframestate_t fstate;\r\n\t\tw->Get_FrameState(w, ent, &fstate);\r\n\t\tif (skelobj->type == SKEL_ABSOLUTE || !Mod_GetTag(skelobj->model, Mod_GetBoneParent(skelobj->model, boneidx+1), &fstate, parentabs))\r\n\t\t{\r\n\t\t\tbonemat_fromentity(w, ent, parentw);\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tbonemat_fromentity(w, ent, parentent);\r\n\t\t\tMatrix3x4_Multiply(parentabs, parentent, parentw);\r\n\t\t}\r\n\t\tMatrix3x4_Invert(parentw, parentinv);\r\n\t}\r\n\r\n\t/*calc the result*/\r\n\tbone = skelobj->bonematrix+12*boneidx;\r\n\tMatrix3x4_Multiply(childworld, parentinv, bone);\r\n}\r\n\r\n//void(float skel, float bonenum, vector org) skel_set_bone (FTE_CSQC_SKELETONOBJECTS) (reads v_forward etc)\r\nvoid QCBUILTIN PF_skel_set_bone (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\r\n{\r\n\tworld_t *w = prinst->parms->user;\r\n\tint skelidx = G_FLOAT(OFS_PARM0);\r\n\tunsigned int boneidx = G_FLOAT(OFS_PARM1)-1;\r\n\tfloat *matrix[3];\r\n\tskelobject_t *skelobj;\r\n\tfloat *bone;\r\n\r\n\tif (prinst->callargc > 5)\r\n\t{\r\n\t\tmatrix[0] = G_VECTOR(OFS_PARM3);\r\n\t\tmatrix[1] = G_VECTOR(OFS_PARM4);\r\n\t\tmatrix[2] = G_VECTOR(OFS_PARM5);\r\n\t}\r\n\telse\r\n\t{\r\n\t\tmatrix[0] = w->g.v_forward;\r\n\t\tmatrix[1] = w->g.v_right;\r\n\t\tmatrix[2] = w->g.v_up;\r\n\t}\r\n\r\n\tskelobj = skel_get(w, skelidx);\r\n\tif (!skelobj || boneidx >= skelobj->numbones)\r\n\t\treturn;\r\n\r\n\tbone = skelobj->bonematrix+12*boneidx;\r\n\tbonemat_fromqcvectors(bone, matrix[0], matrix[1], matrix[2], G_VECTOR(OFS_PARM2));\r\n}\r\n\r\n//void(float skel, float bonenum, vector org [, vector fwd, vector right, vector up]) skel_mul_bone (FTE_CSQC_SKELETONOBJECTS) (reads v_forward etc)\r\nvoid QCBUILTIN PF_skel_premul_bone (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\r\n{\r\n\tworld_t *w = prinst->parms->user;\r\n\tint skelidx = G_FLOAT(OFS_PARM0);\r\n\tint boneidx = G_FLOAT(OFS_PARM1)-1;\r\n\tfloat temp[3][4];\r\n\tfloat mult[3][4];\r\n\tskelobject_t *skelobj;\r\n\tif (prinst->callargc > 5)\r\n\t\tbonemat_fromqcvectors((float*)mult, G_VECTOR(OFS_PARM3), G_VECTOR(OFS_PARM4), G_VECTOR(OFS_PARM5), G_VECTOR(OFS_PARM2));\r\n\telse\r\n\t\tbonemat_fromqcvectors((float*)mult, w->g.v_forward, w->g.v_right, w->g.v_up, G_VECTOR(OFS_PARM2));\r\n\r\n\tskelobj = skel_get(w, skelidx);\r\n\tif (!skelobj || boneidx >= skelobj->numbones)\r\n\t\treturn;\r\n\r\n\t//this is backwards. it rotates the EXISTING position around the passed position. this makes it hard to work with bones that have existing animation data (even if its a static transform).\r\n\tVector4Copy(skelobj->bonematrix+12*boneidx+0, temp[0]);\r\n\tVector4Copy(skelobj->bonematrix+12*boneidx+4, temp[1]);\r\n\tVector4Copy(skelobj->bonematrix+12*boneidx+8, temp[2]);\r\n\tR_ConcatTransforms((void*)mult, (void*)temp, (float(*)[4])(skelobj->bonematrix+12*boneidx));\r\n}\r\nvoid QCBUILTIN PF_skel_postmul_bone (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\r\n{\r\n\tworld_t *w = prinst->parms->user;\r\n\tint skelidx = G_FLOAT(OFS_PARM0);\r\n\tint boneidx = G_FLOAT(OFS_PARM1)-1;\r\n\tfloat temp[3][4];\r\n\tfloat mult[3][4];\r\n\tskelobject_t *skelobj;\r\n\tif (prinst->callargc > 5)\r\n\t\tbonemat_fromqcvectors((float*)mult, G_VECTOR(OFS_PARM3), G_VECTOR(OFS_PARM4), G_VECTOR(OFS_PARM5), G_VECTOR(OFS_PARM2));\r\n\telse\r\n\t\tbonemat_fromqcvectors((float*)mult, w->g.v_forward, w->g.v_right, w->g.v_up, G_VECTOR(OFS_PARM2));\r\n\r\n\tskelobj = skel_get(w, skelidx);\r\n\tif (!skelobj || boneidx >= skelobj->numbones)\r\n\t\treturn;\r\n\r\n\tVector4Copy(skelobj->bonematrix+12*boneidx+0, temp[0]);\r\n\tVector4Copy(skelobj->bonematrix+12*boneidx+4, temp[1]);\r\n\tVector4Copy(skelobj->bonematrix+12*boneidx+8, temp[2]);\r\n\tR_ConcatTransforms((void*)temp, (void*)mult, (float(*)[4])(skelobj->bonematrix+12*boneidx));\r\n}\r\n\r\n//void(float skel, float startbone, float endbone, vector org) skel_mul_bone (FTE_CSQC_SKELETONOBJECTS) (reads v_forward etc)\r\nvoid QCBUILTIN PF_skel_premul_bones (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\r\n{\r\n\tworld_t *w = prinst->parms->user;\r\n\tint skelidx = G_FLOAT(OFS_PARM0);\r\n\tunsigned int startbone = G_FLOAT(OFS_PARM1)-1;\r\n\tunsigned int endbone = G_FLOAT(OFS_PARM2)-1;\r\n\tfloat temp[3][4];\r\n\tfloat mult[3][4];\r\n\tskelobject_t *skelobj;\r\n\tif (prinst->callargc > 6)\r\n\t\tbonemat_fromqcvectors((float*)mult, G_VECTOR(OFS_PARM4), G_VECTOR(OFS_PARM5), G_VECTOR(OFS_PARM6), G_VECTOR(OFS_PARM3));\r\n\telse\r\n\t\tbonemat_fromqcvectors((float*)mult, w->g.v_forward, w->g.v_right, w->g.v_up, G_VECTOR(OFS_PARM3));\r\n\r\n\tskelobj = skel_get(w, skelidx);\r\n\tif (!skelobj)\r\n\t\treturn;\r\n\r\n\tif (startbone == -1)\r\n\t\tstartbone = 0;\r\n\tif (endbone == -1)\r\n\t\tendbone = skelobj->numbones;\r\n\telse if (endbone > skelobj->numbones)\r\n\t\tendbone = skelobj->numbones;\r\n\r\n\twhile(startbone < endbone)\r\n\t{\r\n\t\tVector4Copy(skelobj->bonematrix+12*startbone+0, temp[0]);\r\n\t\tVector4Copy(skelobj->bonematrix+12*startbone+4, temp[1]);\r\n\t\tVector4Copy(skelobj->bonematrix+12*startbone+8, temp[2]);\r\n\t\tR_ConcatTransforms((void*)mult, (void*)temp, (float(*)[4])(skelobj->bonematrix+12*startbone));\r\n\r\n\t\tstartbone++;\r\n\t}\r\n}\r\n\r\n//void(float skeldst, float skelsrc, float startbone, float entbone) skel_copybones (FTE_CSQC_SKELETONOBJECTS)\r\nvoid QCBUILTIN PF_skel_copybones (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\r\n{\r\n\tworld_t *w = prinst->parms->user;\r\n\tint skeldst = G_FLOAT(OFS_PARM0);\r\n\tint skelsrc = G_FLOAT(OFS_PARM1);\r\n\tint startbone = G_FLOAT(OFS_PARM2)-1;\r\n\tint endbone = G_FLOAT(OFS_PARM3)-1;\r\n\r\n\tskelobject_t *skelobjdst;\r\n\tskelobject_t *skelobjsrc;\r\n\r\n\tskelobjdst = skel_get(w, skeldst);\r\n\tskelobjsrc = skel_get(w, skelsrc);\r\n\tif (!skelobjdst || !skelobjsrc)\r\n\t\treturn;\r\n\tif (startbone == -1)\r\n\t\tstartbone = 0;\r\n\tif (endbone == -1)\r\n\t\tendbone = skelobjdst->numbones;\r\n\tif (endbone > skelobjdst->numbones)\r\n\t\tendbone = skelobjdst->numbones;\r\n\tif (endbone > skelobjsrc->numbones)\r\n\t\tendbone = skelobjsrc->numbones;\r\n\r\n\tif (skelobjsrc->type == skelobjdst->type)\r\n\t{\r\n\t\twhile(startbone < endbone)\r\n\t\t{\r\n\t\t\tVector4Copy(skelobjsrc->bonematrix+12*startbone+0, skelobjdst->bonematrix+12*startbone+0);\r\n\t\t\tVector4Copy(skelobjsrc->bonematrix+12*startbone+4, skelobjdst->bonematrix+12*startbone+4);\r\n\t\t\tVector4Copy(skelobjsrc->bonematrix+12*startbone+8, skelobjdst->bonematrix+12*startbone+8);\r\n\r\n\t\t\tstartbone++;\r\n\t\t}\r\n\t}\r\n\telse if (skelobjsrc->type == SKEL_RELATIVE && skelobjdst->type == SKEL_ABSOLUTE)\r\n\t{\r\n\t\t/*copy from relative to absolute*/\r\n\t\tskel_copy_toabs(skelobjdst, skelobjsrc, startbone, endbone);\r\n\t}\r\n\telse\r\n\t{\r\n\t\t/*copy from absolute to relative*/\r\n\t\t//FIXME\r\n\t}\r\n}\r\n\r\n//void(float skel) skel_delete (FTE_CSQC_SKELETONOBJECTS)\r\nvoid QCBUILTIN PF_skel_delete (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\r\n{\r\n\tworld_t *w = prinst->parms->user;\r\n\tint skelidx = G_FLOAT(OFS_PARM0);\r\n\tskelobject_t *skelobj;\r\n\r\n\tskelobj = skel_get(w, skelidx);\r\n\tif (skelobj)\r\n\t{\r\n\t\tskelobj->inuse = 2;\t//2 means don't reuse yet.\r\n\t\tskelobj->modelindex = 0;\r\n\t\tskelobj->model = NULL;\r\n\t\tpendingkill = true;\r\n\t}\r\n}\r\n\r\n//vector(entity ent, float tag) gettaginfo (DP_MD3_TAGSINFO)\r\nvoid QCBUILTIN PF_gettaginfo (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\r\n{\r\n\tworld_t *w = prinst->parms->user;\r\n\twedict_t *ent = G_WEDICT(prinst, OFS_PARM0);\r\n\tint tagnum = G_FLOAT(OFS_PARM1);\r\n\tint tagent = ent->xv->tag_entity;\r\n\tint chain = 10;\r\n\r\n\tfloat transent[12];\r\n\tfloat transforms[12];\r\n\tfloat result[12];\r\n\tfloat result2[12];\r\n\r\n\tframestate_t fstate;\r\n\r\n\tw->Get_FrameState(w, ent, &fstate);\r\n\tif (!Mod_GetTag(w->Get_CModel(w, ent->v->modelindex), tagnum, &fstate, transforms))\r\n\t\tbonemat_fromidentity(transforms);\r\n\r\n\tbonemat_fromentity(w, ent, transent);\r\n\tR_ConcatTransforms((void*)transent, (void*)transforms, (void*)result);\r\n\r\n\twhile (tagent && chain --> 0)\r\n\t{\r\n\t\tent = PROG_TO_WEDICT(prinst, tagent);\r\n\t\tw->Get_FrameState(w, ent, &fstate);\r\n\t\tif (!Mod_GetTag(w->Get_CModel(w, ent->v->modelindex), tagnum, &fstate, transforms))\r\n\t\t\tbonemat_fromidentity(transforms);\r\n\r\n\t\tbonemat_fromentity(w, ent, transent);\r\n\t\tR_ConcatTransforms((void*)transforms, (void*)result, (void*)result2);\r\n\t\tR_ConcatTransforms((void*)transent, (void*)result2, (void*)result);\r\n\r\n\t\ttagent = ent->xv->tag_entity;\r\n\t\ttagnum = ent->xv->tag_index;\r\n\t}\r\n\r\n\tbonemat_toqcvectors(result, w->g.v_forward, w->g.v_right, w->g.v_up, G_VECTOR(OFS_RETURN));\r\n\r\n/*\t//extra info for dp compat.\r\n\tgettaginfo_parent = parentofbone(tagnum);\r\n\tgettaginfo_name = nameofbone(tagnum);\r\n\tbonemat_toqcvectors(relbonetransform(fstate, tagnum), gettaginfo_forward, gettaginfo_right, gettaginfo_up, gettaginfo_offset);\r\n*/\r\n}\r\n\r\n//writes to axis+origin. returns root entity.\r\nwedict_t *skel_gettaginfo_args (pubprogfuncs_t *prinst, vec3_t axis[3], vec3_t origin, int tagent, int tagnum)\r\n{\r\n\tworld_t *w = prinst->parms->user;\r\n\twedict_t *ent = NULL;\r\n\tint chain = 10;\r\n\r\n\tfloat transent[12];\r\n\tfloat transforms[12];\r\n\tfloat result[12];\r\n\tfloat result2[12];\r\n\tframestate_t fstate;\r\n\r\n\tbonemat_fromaxisorg(result, axis, origin);\r\n\r\n\twhile (tagent && chain --> 0)\r\n\t{\r\n\t\tent = PROG_TO_WEDICT(prinst, tagent);\r\n\t\tw->Get_FrameState(w, ent, &fstate);\r\n\t\tif (!Mod_GetTag(w->Get_CModel(w, ent->v->modelindex), tagnum, &fstate, transforms))\r\n\t\t\tbonemat_fromidentity(transforms);\r\n\r\n\t\tbonemat_fromentity(w, ent, transent);\r\n\t\tR_ConcatTransforms((void*)transforms, (void*)result, (void*)result2);\r\n\t\tR_ConcatTransforms((void*)transent, (void*)result2, (void*)result);\r\n\r\n\t\ttagent = ent->xv->tag_entity;\r\n\t\ttagnum = ent->xv->tag_index;\r\n\t}\r\n\r\n\tbonemat_toaxisorg(result, axis, origin);\r\n\treturn ent;\r\n}\r\n\r\n//vector(entity ent, string tagname) gettagindex (DP_MD3_TAGSINFO)\r\nvoid QCBUILTIN PF_gettagindex (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\r\n{\r\n\tworld_t *w = prinst->parms->user;\r\n\twedict_t *ent = G_WEDICT(prinst, OFS_PARM0);\r\n\tconst char *tagname = PR_GetStringOfs(prinst, OFS_PARM1);\r\n\tmodel_t *mod = *tagname?w->Get_CModel(w, ent->v->modelindex):NULL;\r\n\tif (mod)\r\n\t\tG_FLOAT(OFS_RETURN) = Mod_TagNumForName(mod, tagname, 0);\r\n\telse\r\n\t\tG_FLOAT(OFS_RETURN) = 0;\r\n}\r\n\r\n//string(float modidx, float framenum) frametoname\r\nvoid QCBUILTIN PF_frametoname (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\r\n{\r\n\tworld_t *w = prinst->parms->user;\r\n\tint modelindex = G_FLOAT(OFS_PARM0);\r\n\tunsigned int animnum = G_FLOAT(OFS_PARM1);\r\n\tint surfaceidx = 0;\r\n\tmodel_t *mod = w->Get_CModel(w, modelindex);\r\n\tconst char *n = Mod_FrameNameForNum(mod, surfaceidx, animnum);\r\n\r\n\tif (n)\r\n\t\tRETURN_TSTRING(n);\r\n\telse\r\n\t\tG_INT(OFS_RETURN) = 0;\t//null string (which is also empty in qc)\r\n}\r\n\r\nvoid QCBUILTIN PF_frameforname (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\r\n{\r\n\tworld_t *w = prinst->parms->user;\r\n\tint modelindex = G_FLOAT(OFS_PARM0);\r\n\tint surfaceidx = 0;\r\n\tconst char *str = PF_VarString(prinst, 1, pr_globals);\r\n\tmodel_t *mod = w->Get_CModel(w, modelindex);\r\n\r\n\tif (mod)\r\n\t\tG_FLOAT(OFS_RETURN) = Mod_FrameNumForName(mod, surfaceidx, str);\r\n\telse\r\n\t\tG_FLOAT(OFS_RETURN) = -1;\r\n}\r\nvoid QCBUILTIN PF_frameforaction (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\r\n{\r\n\tworld_t *w = prinst->parms->user;\r\n\tint modelindex = G_FLOAT(OFS_PARM0);\r\n\tint surfaceidx = 0;\r\n\tint actionid = G_INT(OFS_PARM1);\r\n\tmodel_t *mod = w->Get_CModel(w, modelindex);\r\n\r\n\tif (mod)\r\n\t\tG_FLOAT(OFS_RETURN) = Mod_FrameNumForAction(mod, surfaceidx, actionid);\r\n\telse\r\n\t\tG_FLOAT(OFS_RETURN) = -1;\r\n}\r\nvoid QCBUILTIN PF_frameduration (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\r\n{\r\n\tworld_t *w = prinst->parms->user;\r\n\tint modelindex = G_FLOAT(OFS_PARM0);\r\n\tunsigned int framenum = G_FLOAT(OFS_PARM1);\r\n\tint surfaceidx = 0;\r\n\tmodel_t *mod = w->Get_CModel(w, modelindex);\r\n\r\n\tif (mod)\r\n\t\tG_FLOAT(OFS_RETURN) = Mod_GetFrameDuration(mod, surfaceidx, framenum);\r\n\telse\r\n\t\tG_FLOAT(OFS_RETURN) = 0;\r\n}\r\nvoid QCBUILTIN PF_modelframecount (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\r\n{\r\n\tworld_t *w = prinst->parms->user;\r\n\tunsigned int modelindex = G_FLOAT(OFS_PARM0);\r\n\tmodel_t *mod = w->Get_CModel(w, modelindex);\r\n\r\n\tif (mod)\r\n\t\tG_FLOAT(OFS_RETURN) = Mod_GetFrameCount(mod);\r\n\telse\r\n\t\tG_FLOAT(OFS_RETURN) = 0;\r\n}\r\n\r\n//void(float modidx, float framenum, __inout float basetime, float targettime, void(float timestamp, int code, string data) callback)\r\nvoid QCBUILTIN PF_processmodelevents (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\r\n{\r\n\tworld_t *w = prinst->parms->user;\r\n\tint modelindex = G_FLOAT(OFS_PARM0);\r\n\tunsigned int frame = G_FLOAT(OFS_PARM1);\r\n\tfloat basetime = G_FLOAT(OFS_PARM2);\r\n\tfloat targettime = G_FLOAT(OFS_PARM3);\r\n\tfunc_t callback = G_INT(OFS_PARM4);\r\n\tmodel_t *mod = w->Get_CModel(w, modelindex);\r\n\tfloat starttime, timestamp;\r\n\r\n\tif (targettime == basetime)\r\n\t\treturn;\t//don't refire the same event multiple times.\r\n\r\n\t//returns all basetime <= eventtime < targettime\r\n\r\n\tif (mod)\r\n\t{\r\n\t\tif (mod->type == mod_alias)\r\n\t\t{\t//slightly more optimised path that is kinda redundant, but w/e\r\n\t\t\tgaliasinfo_t *ga = Mod_Extradata(mod);\r\n\t\t\tgaliasanimation_t *anim = ga->ofsanimations + frame;\r\n\t\t\tgaliasevent_t *ev;\r\n\t\t\tfloat loopduration;\r\n\t\t\tif (frame < (unsigned int)ga->numanimations && anim->events)\r\n\t\t\t{\r\n\t\t\t\tif (anim->loop)\r\n\t\t\t\t{\r\n\t\t\t\t\tloopduration = anim->rate * anim->numposes;\r\n\t\t\t\t\tstarttime = loopduration*(unsigned int)(basetime/loopduration);\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t\tstarttime = loopduration = 0;\r\n\t\t\t\tfor (ev = anim->events; ; )\r\n\t\t\t\t{\r\n\t\t\t\t\t//be careful to use as consistent timings as we can\r\n\t\t\t\t\ttimestamp = starttime + ev->timestamp;\r\n\t\t\t\t\tif (timestamp >= targettime)\r\n\t\t\t\t\t\tbreak;\t//this is in the future.\r\n\t\t\t\t\tif (timestamp >= basetime)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tG_FLOAT(OFS_PARM0) = timestamp;\r\n\t\t\t\t\t\tG_INT(OFS_PARM1) = ev->code;\r\n\t\t\t\t\t\tG_INT(OFS_PARM2) = PR_TempString(prinst, ev->data);\r\n\t\t\t\t\t\tPR_ExecuteProgram(prinst, callback);\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\tev = ev->next;\r\n\t\t\t\t\tif (!ev)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tif (loopduration)\r\n\t\t\t\t\t\t\tev = anim->events;\r\n\t\t\t\t\t\telse\r\n\t\t\t\t\t\t\tbreak;\t//animation ends here, so no more events possible\r\n\t\t\t\t\t\tstarttime += loopduration;\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n#ifdef HALFLIFEMODELS\r\n\t\telse\t//actually this is a generic version that would work for iqm etc too, but is less efficient due to repeated lookups. oh well.\r\n\t\t{\r\n\t\t\tint ev, code;\r\n\t\t\tchar *data;\r\n\t\t\tfloat loopduration;\r\n\t\t\tqboolean looping;\r\n\t\t\tint act;\r\n\r\n\t\t\tif (Mod_FrameInfoForNum(mod, 0, frame, &data, &code, &loopduration, &looping, &act))\r\n\t\t\t{\r\n\t\t\t\tif (looping && loopduration)\r\n\t\t\t\t\tstarttime = loopduration*(unsigned int)(basetime/loopduration);\r\n\t\t\t\telse\r\n\t\t\t\t\tstarttime = loopduration = 0;\r\n\t\t\t\tfor (ev = 0; ; ev++)\r\n\t\t\t\t{\r\n\t\t\t\t\tif (!Mod_GetModelEvent(mod, frame, ev, &timestamp, &code, &data))\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tif (looping && Mod_GetModelEvent(mod, frame, 0, &timestamp, &code, &data))\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tev = 0;\r\n\t\t\t\t\t\t\tstarttime += loopduration;\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\telse\r\n\t\t\t\t\t\t\tbreak;\t//end of anim\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\t//be careful to use as consistent timings as we can...\r\n\t\t\t\t\ttimestamp += starttime;\r\n\t\t\t\t\tif (timestamp >= targettime)\r\n\t\t\t\t\t\tbreak;\t//this is in the future.\r\n\t\t\t\t\tif (timestamp >= basetime)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tG_FLOAT(OFS_PARM0) = timestamp;\r\n\t\t\t\t\t\tG_INT(OFS_PARM1) = code;\r\n\t\t\t\t\t\tG_INT(OFS_PARM2) = PR_TempString(prinst, data);\r\n\t\t\t\t\t\tPR_ExecuteProgram(prinst, callback);\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n#endif\r\n\t}\r\n\tG_FLOAT(OFS_PARM2) = targettime;\r\n}\r\n\r\n//float(float modidx, float framenum, __inout float basetime, float targettime, __out int code, __out string data)\r\nvoid QCBUILTIN PF_getnextmodelevent (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\r\n{\r\n\tworld_t *w = prinst->parms->user;\r\n\tunsigned int modelindex = G_FLOAT(OFS_PARM0);\r\n\tunsigned int frame = G_FLOAT(OFS_PARM1);\r\n\tfloat basetime = G_FLOAT(OFS_PARM2);\r\n\tfloat targettime = G_FLOAT(OFS_PARM3);\r\n\tmodel_t *mod = w->Get_CModel(w, modelindex);\r\n\tfloat starttime, timestamp;\r\n\t//default return values\r\n\tG_FLOAT(OFS_RETURN) = false;\r\n\tG_FLOAT(OFS_PARM2) = targettime;\r\n\tG_INT(OFS_PARM4) = 0;\r\n\tG_INT(OFS_PARM5) = 0;\r\n\r\n\tif (mod)\r\n\t{\r\n\t\tif (mod->type == mod_alias)\r\n\t\t{\t//slightly more optimised path that is kinda redundant, but w/e\r\n\t\t\tgaliasinfo_t *ga = Mod_Extradata(mod);\r\n\t\t\tgaliasanimation_t *anim = ga->ofsanimations + frame;\r\n\t\t\tgaliasevent_t *ev;\r\n\t\t\tfloat loopduration;\r\n\t\t\tif (frame >= (unsigned int)ga->numanimations || !anim->events)\r\n\t\t\t\treturn;\r\n\t\t\tif (anim->loop)\r\n\t\t\t{\r\n\t\t\t\tloopduration = anim->rate * anim->numposes;\r\n\t\t\t\tstarttime = loopduration*(unsigned int)(basetime/loopduration);\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t\tstarttime = loopduration = 0;\r\n\t\t\tfor (ev = anim->events; ; )\r\n\t\t\t{\r\n\t\t\t\t//be careful to use as consistent timings as we can\r\n\t\t\t\ttimestamp = starttime + ev->timestamp;\r\n\t\t\t\tif (timestamp > targettime)\r\n\t\t\t\t\tbreak;\t//this is in the future.\r\n\t\t\t\tif (timestamp > basetime)\r\n\t\t\t\t{\r\n\t\t\t\t\tG_FLOAT(OFS_RETURN) = true;\r\n\t\t\t\t\tG_FLOAT(OFS_PARM2) = timestamp;\r\n\t\t\t\t\tG_INT(OFS_PARM4) = ev->code;\r\n\t\t\t\t\tG_INT(OFS_PARM5) = PR_TempString(prinst, ev->data);\r\n\t\t\t\t\treturn;\r\n\t\t\t\t}\r\n\r\n\t\t\t\tev = ev->next;\r\n\t\t\t\tif (!ev)\r\n\t\t\t\t{\r\n\t\t\t\t\tif (loopduration)\r\n\t\t\t\t\t\tev = anim->events;\r\n\t\t\t\t\telse\r\n\t\t\t\t\t\treturn;\t//animation ended here, so no more events\r\n\t\t\t\t\tstarttime += loopduration;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n#ifdef HALFLIFEMODELS\r\n\t\telse\t//actually this is a generic version that would work for iqm etc too, but is less efficient due to repeated lookups. oh well.\r\n\t\t{\r\n\t\t\tint ev, code;\r\n\t\t\tchar *data;\r\n\t\t\tfloat loopduration;\r\n\t\t\tqboolean looping;\r\n\t\t\tint act;\r\n\r\n\t\t\tif (!Mod_FrameInfoForNum(mod, 0, frame, &data, &code, &loopduration, &looping, &act))\r\n\t\t\t\treturn; //invalid frame\r\n\r\n\t\t\tif (looping && loopduration)\r\n\t\t\t\tstarttime = loopduration*(unsigned int)(basetime/loopduration);\r\n\t\t\telse\r\n\t\t\t\tstarttime = loopduration = 0;\r\n\t\t\tfor (ev = 0; ; ev++)\r\n\t\t\t{\r\n\t\t\t\tif (!Mod_GetModelEvent(mod, frame, ev, &timestamp, &code, &data))\r\n\t\t\t\t{\r\n\t\t\t\t\tif (looping && Mod_GetModelEvent(mod, frame, 0, &timestamp, &code, &data))\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tev = 0;\r\n\t\t\t\t\t\tstarttime += loopduration;\r\n\t\t\t\t\t}\r\n\t\t\t\t\telse\r\n\t\t\t\t\t\tbreak;\t//end of anim\r\n\t\t\t\t}\r\n\r\n\t\t\t\t//be careful to use as consistent timings as we can...\r\n\t\t\t\ttimestamp += starttime;\r\n\t\t\t\tif (timestamp > targettime)\r\n\t\t\t\t\tbreak;\t//this is in the future.\r\n\t\t\t\tif (timestamp > basetime)\r\n\t\t\t\t{\r\n\t\t\t\t\tG_FLOAT(OFS_RETURN) = true;\r\n\t\t\t\t\tG_FLOAT(OFS_PARM2) = timestamp;\r\n\t\t\t\t\tG_INT(OFS_PARM4) = code;\r\n\t\t\t\t\tG_INT(OFS_PARM5) = PR_TempString(prinst, data);\r\n\t\t\t\t\treturn;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n#endif\r\n\t}\r\n}\r\n//float(float modidx, float framenum, int idx, __out float timestamp, __out int code, __out string data)\r\nvoid QCBUILTIN PF_getmodeleventidx (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\r\n{\r\n\tworld_t *w = prinst->parms->user;\r\n\tunsigned int modelindex = G_FLOAT(OFS_PARM0);\r\n\tunsigned int frame = G_FLOAT(OFS_PARM1);\r\n\tint eventindex = G_INT(OFS_PARM2);\r\n\tmodel_t *mod = w->Get_CModel(w, modelindex);\r\n\t//default return values\r\n\tfloat timestamp = 0;\r\n\tint code = 0;\r\n\tchar *data = NULL;\r\n\r\n\tG_FLOAT(OFS_RETURN) = Mod_GetModelEvent(mod, frame, eventindex, &timestamp, &code, &data);\r\n\tG_FLOAT(OFS_PARM3) = timestamp;\r\n\tG_INT(OFS_PARM4) = code;\r\n\tif (data)\r\n\t\tG_INT(OFS_PARM5) = PR_TempString(prinst, data);\r\n\telse\r\n\t\tG_INT(OFS_PARM5) = 0;\r\n}\r\n\r\n//string(float modidx, float skinnum) skintoname\r\nvoid QCBUILTIN PF_skintoname (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\r\n{\r\n\tworld_t *w = prinst->parms->user;\r\n\tunsigned int modelindex = G_FLOAT(OFS_PARM0);\r\n\tunsigned int skinnum = G_FLOAT(OFS_PARM1);\r\n\tint surfaceidx = 0;\r\n\tmodel_t *mod = w->Get_CModel(w, modelindex);\r\n\tconst char *n = Mod_SkinNameForNum(mod, surfaceidx, skinnum);\r\n\r\n\tif (n)\r\n\t\tRETURN_TSTRING(n);\r\n\telse\r\n\t\tG_INT(OFS_RETURN) = 0;\t//null string (which is also empty in qc)\r\n}\r\nvoid QCBUILTIN PF_skinforname (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\r\n{\r\n#ifndef SERVERONLY\r\n\tworld_t *w = prinst->parms->user;\r\n\tunsigned int modelindex = G_FLOAT(OFS_PARM0);\r\n\tconst char *str = PF_VarString(prinst, 1, pr_globals);\r\n\tint surfaceidx = 0;\r\n\tmodel_t *mod = w->Get_CModel(w, modelindex);\r\n\r\n\tif (mod)\r\n\t\tG_FLOAT(OFS_RETURN) = Mod_SkinNumForName(mod, surfaceidx, str);\r\n\telse\r\n#endif\r\n\t\tG_FLOAT(OFS_RETURN) = -1;\r\n}\r\n#endif\r\n\r\n"
  },
  {
    "path": "engine/client/q2anorms.h",
    "content": "/*\nCopyright (C) 1997-2001 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  \n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n*/\n{-0.525731, 0.000000, 0.850651}, \n{-0.442863, 0.238856, 0.864188}, \n{-0.295242, 0.000000, 0.955423}, \n{-0.309017, 0.500000, 0.809017}, \n{-0.162460, 0.262866, 0.951056}, \n{0.000000, 0.000000, 1.000000}, \n{0.000000, 0.850651, 0.525731}, \n{-0.147621, 0.716567, 0.681718}, \n{0.147621, 0.716567, 0.681718}, \n{0.000000, 0.525731, 0.850651}, \n{0.309017, 0.500000, 0.809017}, \n{0.525731, 0.000000, 0.850651}, \n{0.295242, 0.000000, 0.955423}, \n{0.442863, 0.238856, 0.864188}, \n{0.162460, 0.262866, 0.951056}, \n{-0.681718, 0.147621, 0.716567}, \n{-0.809017, 0.309017, 0.500000}, \n{-0.587785, 0.425325, 0.688191}, \n{-0.850651, 0.525731, 0.000000}, \n{-0.864188, 0.442863, 0.238856}, \n{-0.716567, 0.681718, 0.147621}, \n{-0.688191, 0.587785, 0.425325}, \n{-0.500000, 0.809017, 0.309017}, \n{-0.238856, 0.864188, 0.442863}, \n{-0.425325, 0.688191, 0.587785}, \n{-0.716567, 0.681718, -0.147621}, \n{-0.500000, 0.809017, -0.309017}, \n{-0.525731, 0.850651, 0.000000}, \n{0.000000, 0.850651, -0.525731}, \n{-0.238856, 0.864188, -0.442863}, \n{0.000000, 0.955423, -0.295242}, \n{-0.262866, 0.951056, -0.162460}, \n{0.000000, 1.000000, 0.000000}, \n{0.000000, 0.955423, 0.295242}, \n{-0.262866, 0.951056, 0.162460}, \n{0.238856, 0.864188, 0.442863}, \n{0.262866, 0.951056, 0.162460}, \n{0.500000, 0.809017, 0.309017}, \n{0.238856, 0.864188, -0.442863}, \n{0.262866, 0.951056, -0.162460}, \n{0.500000, 0.809017, -0.309017}, \n{0.850651, 0.525731, 0.000000}, \n{0.716567, 0.681718, 0.147621}, \n{0.716567, 0.681718, -0.147621}, \n{0.525731, 0.850651, 0.000000}, \n{0.425325, 0.688191, 0.587785}, \n{0.864188, 0.442863, 0.238856}, \n{0.688191, 0.587785, 0.425325}, \n{0.809017, 0.309017, 0.500000}, \n{0.681718, 0.147621, 0.716567}, \n{0.587785, 0.425325, 0.688191}, \n{0.955423, 0.295242, 0.000000}, \n{1.000000, 0.000000, 0.000000}, \n{0.951056, 0.162460, 0.262866}, \n{0.850651, -0.525731, 0.000000}, \n{0.955423, -0.295242, 0.000000}, \n{0.864188, -0.442863, 0.238856}, \n{0.951056, -0.162460, 0.262866}, \n{0.809017, -0.309017, 0.500000}, \n{0.681718, -0.147621, 0.716567}, \n{0.850651, 0.000000, 0.525731}, \n{0.864188, 0.442863, -0.238856}, \n{0.809017, 0.309017, -0.500000}, \n{0.951056, 0.162460, -0.262866}, \n{0.525731, 0.000000, -0.850651}, \n{0.681718, 0.147621, -0.716567}, \n{0.681718, -0.147621, -0.716567}, \n{0.850651, 0.000000, -0.525731}, \n{0.809017, -0.309017, -0.500000}, \n{0.864188, -0.442863, -0.238856}, \n{0.951056, -0.162460, -0.262866}, \n{0.147621, 0.716567, -0.681718}, \n{0.309017, 0.500000, -0.809017}, \n{0.425325, 0.688191, -0.587785}, \n{0.442863, 0.238856, -0.864188}, \n{0.587785, 0.425325, -0.688191}, \n{0.688191, 0.587785, -0.425325}, \n{-0.147621, 0.716567, -0.681718}, \n{-0.309017, 0.500000, -0.809017}, \n{0.000000, 0.525731, -0.850651}, \n{-0.525731, 0.000000, -0.850651}, \n{-0.442863, 0.238856, -0.864188}, \n{-0.295242, 0.000000, -0.955423}, \n{-0.162460, 0.262866, -0.951056}, \n{0.000000, 0.000000, -1.000000}, \n{0.295242, 0.000000, -0.955423}, \n{0.162460, 0.262866, -0.951056}, \n{-0.442863, -0.238856, -0.864188}, \n{-0.309017, -0.500000, -0.809017}, \n{-0.162460, -0.262866, -0.951056}, \n{0.000000, -0.850651, -0.525731}, \n{-0.147621, -0.716567, -0.681718}, \n{0.147621, -0.716567, -0.681718}, \n{0.000000, -0.525731, -0.850651}, \n{0.309017, -0.500000, -0.809017}, \n{0.442863, -0.238856, -0.864188}, \n{0.162460, -0.262866, -0.951056}, \n{0.238856, -0.864188, -0.442863}, \n{0.500000, -0.809017, -0.309017}, \n{0.425325, -0.688191, -0.587785}, \n{0.716567, -0.681718, -0.147621}, \n{0.688191, -0.587785, -0.425325}, \n{0.587785, -0.425325, -0.688191}, \n{0.000000, -0.955423, -0.295242}, \n{0.000000, -1.000000, 0.000000}, \n{0.262866, -0.951056, -0.162460}, \n{0.000000, -0.850651, 0.525731}, \n{0.000000, -0.955423, 0.295242}, \n{0.238856, -0.864188, 0.442863}, \n{0.262866, -0.951056, 0.162460}, \n{0.500000, -0.809017, 0.309017}, \n{0.716567, -0.681718, 0.147621}, \n{0.525731, -0.850651, 0.000000}, \n{-0.238856, -0.864188, -0.442863}, \n{-0.500000, -0.809017, -0.309017}, \n{-0.262866, -0.951056, -0.162460}, \n{-0.850651, -0.525731, 0.000000}, \n{-0.716567, -0.681718, -0.147621}, \n{-0.716567, -0.681718, 0.147621}, \n{-0.525731, -0.850651, 0.000000}, \n{-0.500000, -0.809017, 0.309017}, \n{-0.238856, -0.864188, 0.442863}, \n{-0.262866, -0.951056, 0.162460}, \n{-0.864188, -0.442863, 0.238856}, \n{-0.809017, -0.309017, 0.500000}, \n{-0.688191, -0.587785, 0.425325}, \n{-0.681718, -0.147621, 0.716567}, \n{-0.442863, -0.238856, 0.864188}, \n{-0.587785, -0.425325, 0.688191}, \n{-0.309017, -0.500000, 0.809017}, \n{-0.147621, -0.716567, 0.681718}, \n{-0.425325, -0.688191, 0.587785}, \n{-0.162460, -0.262866, 0.951056}, \n{0.442863, -0.238856, 0.864188}, \n{0.162460, -0.262866, 0.951056}, \n{0.309017, -0.500000, 0.809017}, \n{0.147621, -0.716567, 0.681718}, \n{0.000000, -0.525731, 0.850651}, \n{0.425325, -0.688191, 0.587785}, \n{0.587785, -0.425325, 0.688191}, \n{0.688191, -0.587785, 0.425325}, \n{-0.955423, 0.295242, 0.000000}, \n{-0.951056, 0.162460, 0.262866}, \n{-1.000000, 0.000000, 0.000000}, \n{-0.850651, 0.000000, 0.525731}, \n{-0.955423, -0.295242, 0.000000}, \n{-0.951056, -0.162460, 0.262866}, \n{-0.864188, 0.442863, -0.238856}, \n{-0.951056, 0.162460, -0.262866}, \n{-0.809017, 0.309017, -0.500000}, \n{-0.864188, -0.442863, -0.238856}, \n{-0.951056, -0.162460, -0.262866}, \n{-0.809017, -0.309017, -0.500000}, \n{-0.681718, 0.147621, -0.716567}, \n{-0.681718, -0.147621, -0.716567}, \n{-0.850651, 0.000000, -0.525731}, \n{-0.688191, 0.587785, -0.425325}, \n{-0.587785, 0.425325, -0.688191}, \n{-0.425325, 0.688191, -0.587785}, \n{-0.425325, -0.688191, -0.587785}, \n{-0.587785, -0.425325, -0.688191}, \n{-0.688191, -0.587785, -0.425325}, \n"
  },
  {
    "path": "engine/client/q2m_flash.c",
    "content": "/*\nCopyright (C) 1997-2001 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  \n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n*/\n// m_flash.c\n\n\n// this file is included in both the game dll and quake2,\n// the game needs it to source shot locations, the client\n// needs it to position muzzle flashes\nstruct {\n\tconst char *name;\n\tvec3_t offset;\n} monster_flash_offset [] =\n{\n\t{\"MZ2_INVALID\", {0.0, 0.0, 0.0}},\n// MZ2_TANK_BLASTER_1\n\t{\"MZ2_TANK_BLASTER\", {20.7, -18.5, 28.7}},\n// MZ2_TANK_BLASTER_2\n\t{\"MZ2_TANK_BLASTER\", {16.6, -21.5, 30.1}},\n// MZ2_TANK_BLASTER_3\t\t\t\t3\n\t{\"MZ2_TANK_BLASTER\", {11.8, -23.9, 32.1}},\n// MZ2_TANK_MACHINEGUN_1\t\t\t4\n\t{\"MZ2_TANK_MACHINEGUN\", {22.9, -0.7, 25.3}},\n// MZ2_TANK_MACHINEGUN_2\t\t\t5\n\t{\"MZ2_TANK_MACHINEGUN\", {22.2, 6.2, 22.3}},\n// MZ2_TANK_MACHINEGUN_3\t\t\t6\n\t{\"MZ2_TANK_MACHINEGUN\", {19.4, 13.1, 18.6}},\n// MZ2_TANK_MACHINEGUN_4\t\t\t7\n\t{\"MZ2_TANK_MACHINEGUN\", {19.4, 18.8, 18.6}},\n// MZ2_TANK_MACHINEGUN_5\t\t\t8\n\t{\"MZ2_TANK_MACHINEGUN\", {17.9, 25.0, 18.6}},\n// MZ2_TANK_MACHINEGUN_6\t\t\t9\n\t{\"MZ2_TANK_MACHINEGUN\", {14.1, 30.5, 20.6}},\n// MZ2_TANK_MACHINEGUN_7\t\t\t10\n\t{\"MZ2_TANK_MACHINEGUN\", {9.3, 35.3, 22.1}},\n// MZ2_TANK_MACHINEGUN_8\t\t\t11\n\t{\"MZ2_TANK_MACHINEGUN\", {4.7, 38.4, 22.1}},\n// MZ2_TANK_MACHINEGUN_9\t\t\t12\n\t{\"MZ2_TANK_MACHINEGUN\", {-1.1, 40.4, 24.1}},\n// MZ2_TANK_MACHINEGUN_10\t\t\t13\n\t{\"MZ2_TANK_MACHINEGUN\", {-6.5, 41.2, 24.1}},\n// MZ2_TANK_MACHINEGUN_11\t\t\t14\n\t{\"MZ2_TANK_MACHINEGUN\", {3.2, 40.1, 24.7}},\n// MZ2_TANK_MACHINEGUN_12\t\t\t15\n\t{\"MZ2_TANK_MACHINEGUN\", {11.7, 36.7, 26.0}},\n// MZ2_TANK_MACHINEGUN_13\t\t\t16\n\t{\"MZ2_TANK_MACHINEGUN\", {18.9, 31.3, 26.0}},\n// MZ2_TANK_MACHINEGUN_14\t\t\t17\n\t{\"MZ2_TANK_MACHINEGUN\", {24.4, 24.4, 26.4}},\n// MZ2_TANK_MACHINEGUN_15\t\t\t18\n\t{\"MZ2_TANK_MACHINEGUN\", {27.1, 17.1, 27.2}},\n// MZ2_TANK_MACHINEGUN_16\t\t\t19\n\t{\"MZ2_TANK_MACHINEGUN\", {28.5, 9.1, 28.0}},\n// MZ2_TANK_MACHINEGUN_17\t\t\t20\n\t{\"MZ2_TANK_MACHINEGUN\", {27.1, 2.2, 28.0}},\n// MZ2_TANK_MACHINEGUN_18\t\t\t21\n\t{\"MZ2_TANK_MACHINEGUN\", {24.9, -2.8, 28.0}},\n// MZ2_TANK_MACHINEGUN_19\t\t\t22\n\t{\"MZ2_TANK_MACHINEGUN\", {21.6, -7.0, 26.4}},\n// MZ2_TANK_ROCKET_1\t\t\t\t23\n\t{\"MZ2_TANK_ROCKET\", {6.2, 29.1, 49.1}},\n// MZ2_TANK_ROCKET_2\t\t\t\t24\n\t{\"MZ2_TANK_ROCKET\", {6.9, 23.8, 49.1}},\n// MZ2_TANK_ROCKET_3\t\t\t\t25\n\t{\"MZ2_TANK_ROCKET\", {8.3, 17.8, 49.5}},\n\n// MZ2_INFANTRY_MACHINEGUN_1\t\t26\n\t{\"MZ2_INFANTRY_MACHINEGUN\", {26.6, 7.1, 13.1}},\n// MZ2_INFANTRY_MACHINEGUN_2\t\t27\n\t{\"MZ2_INFANTRY_MACHINEGUN\", {18.2, 7.5, 15.4}},\n// MZ2_INFANTRY_MACHINEGUN_3\t\t28\n\t{\"MZ2_INFANTRY_MACHINEGUN\", {17.2, 10.3, 17.9}},\n// MZ2_INFANTRY_MACHINEGUN_4\t\t29\n\t{\"MZ2_INFANTRY_MACHINEGUN\", {17.0, 12.8, 20.1}},\n// MZ2_INFANTRY_MACHINEGUN_5\t\t30\n\t{\"MZ2_INFANTRY_MACHINEGUN\", {15.1, 14.1, 21.8}},\n// MZ2_INFANTRY_MACHINEGUN_6\t\t31\n\t{\"MZ2_INFANTRY_MACHINEGUN\", {11.8, 17.2, 23.1}},\n// MZ2_INFANTRY_MACHINEGUN_7\t\t32\n\t{\"MZ2_INFANTRY_MACHINEGUN\", {11.4, 20.2, 21.0}},\n// MZ2_INFANTRY_MACHINEGUN_8\t\t33\n\t{\"MZ2_INFANTRY_MACHINEGUN\", {9.0, 23.0, 18.9}},\n// MZ2_INFANTRY_MACHINEGUN_9\t\t34\n\t{\"MZ2_INFANTRY_MACHINEGUN\", {13.9, 18.6, 17.7}},\n// MZ2_INFANTRY_MACHINEGUN_10\t\t35\n\t{\"MZ2_INFANTRY_MACHINEGUN\", {15.4, 15.6, 15.8}},\n// MZ2_INFANTRY_MACHINEGUN_11\t\t36\n\t{\"MZ2_INFANTRY_MACHINEGUN\", {10.2, 15.2, 25.1}},\n// MZ2_INFANTRY_MACHINEGUN_12\t\t37\n\t{\"MZ2_INFANTRY_MACHINEGUN\", {-1.9, 15.1, 28.2}},\n// MZ2_INFANTRY_MACHINEGUN_13\t\t38\n\t{\"MZ2_INFANTRY_MACHINEGUN\", {-12.4, 13.0, 20.2}},\n\n// MZ2_SOLDIER_BLASTER_1\t\t\t39\n\t{\"MZ2_SOLDIER_BLASTER\", {10.6 * 1.2, 7.7 * 1.2, 7.8 * 1.2}},\n// MZ2_SOLDIER_BLASTER_2\t\t\t40\n\t{\"MZ2_SOLDIER_BLASTER\", {21.1 * 1.2, 3.6 * 1.2, 19.0 * 1.2}},\n// MZ2_SOLDIER_SHOTGUN_1\t\t\t41\n\t{\"MZ2_SOLDIER_SHOTGUN\", {10.6 * 1.2, 7.7 * 1.2, 7.8 * 1.2}},\n// MZ2_SOLDIER_SHOTGUN_2\t\t\t42\n\t{\"MZ2_SOLDIER_SHOTGUN\", {21.1 * 1.2, 3.6 * 1.2, 19.0 * 1.2}},\n// MZ2_SOLDIER_MACHINEGUN_1\t\t\t43\n\t{\"MZ2_SOLDIER_MACHINEGUN\", {10.6 * 1.2, 7.7 * 1.2, 7.8 * 1.2}},\n// MZ2_SOLDIER_MACHINEGUN_2\t\t\t44\n\t{\"MZ2_SOLDIER_MACHINEGUN\", {21.1 * 1.2, 3.6 * 1.2, 19.0 * 1.2}},\n\n// MZ2_GUNNER_MACHINEGUN_1\t\t\t45\n\t{\"MZ2_GUNNER_MACHINEGUN\", {30.1 * 1.15, 3.9 * 1.15, 19.6 * 1.15}},\n// MZ2_GUNNER_MACHINEGUN_2\t\t\t46\n\t{\"MZ2_GUNNER_MACHINEGUN\", {29.1 * 1.15, 2.5 * 1.15, 20.7 * 1.15}},\n// MZ2_GUNNER_MACHINEGUN_3\t\t\t47\n\t{\"MZ2_GUNNER_MACHINEGUN\", {28.2 * 1.15, 2.5 * 1.15, 22.2 * 1.15}},\n// MZ2_GUNNER_MACHINEGUN_4\t\t\t48\n\t{\"MZ2_GUNNER_MACHINEGUN\", {28.2 * 1.15, 3.6 * 1.15, 22.0 * 1.15}},\n// MZ2_GUNNER_MACHINEGUN_5\t\t\t49\n\t{\"MZ2_GUNNER_MACHINEGUN\", {26.9 * 1.15, 2.0 * 1.15, 23.4 * 1.15}},\n// MZ2_GUNNER_MACHINEGUN_6\t\t\t50\n\t{\"MZ2_GUNNER_MACHINEGUN\", {26.5 * 1.15, 0.6 * 1.15, 20.8 * 1.15}},\n// MZ2_GUNNER_MACHINEGUN_7\t\t\t51\n\t{\"MZ2_GUNNER_MACHINEGUN\", {26.9 * 1.15, 0.5 * 1.15, 21.5 * 1.15}},\n// MZ2_GUNNER_MACHINEGUN_8\t\t\t52\n\t{\"MZ2_GUNNER_MACHINEGUN\", {29.0 * 1.15, 2.4 * 1.15, 19.5 * 1.15}},\n// MZ2_GUNNER_GRENADE_1\t\t\t\t53\n\t{\"MZ2_GUNNER_GRENADE\", {4.6 * 1.15, -16.8 * 1.15, 7.3 * 1.15}},\n// MZ2_GUNNER_GRENADE_2\t\t\t\t54\n\t{\"MZ2_GUNNER_GRENADE\", {4.6 * 1.15, -16.8 * 1.15, 7.3 * 1.15}},\n// MZ2_GUNNER_GRENADE_3\t\t\t\t55\n\t{\"MZ2_GUNNER_GRENADE\", {4.6 * 1.15, -16.8 * 1.15, 7.3 * 1.15}},\n// MZ2_GUNNER_GRENADE_4\t\t\t\t56\n\t{\"MZ2_GUNNER_GRENADE\", {4.6 * 1.15, -16.8 * 1.15, 7.3 * 1.15}},\n\n// MZ2_CHICK_ROCKET_1\t\t\t\t57\n//\t-24.8, -9.0, 39.0,\n\t{\"MZ2_CHICK_ROCKET\", {24.8, -9.0, 39.0}},\t\t\t// PGM - this was incorrect in Q2\n\n// MZ2_FLYER_BLASTER_1\t\t\t\t58\n\t{\"MZ2_FLYER_BLASTER\", {12.1, 13.4, -14.5}},\n// MZ2_FLYER_BLASTER_2\t\t\t\t59\n\t{\"MZ2_FLYER_BLASTER\", {12.1, -7.4, -14.5}},\n\n// MZ2_MEDIC_BLASTER_1\t\t\t\t60\n\t{\"MZ2_MEDIC_BLASTER\", {12.1, 5.4, 16.5}},\n\n// MZ2_GLADIATOR_RAILGUN_1\t\t\t61\n\t{\"MZ2_GLADIATOR_RAILGUN\", {30.0, 18.0, 28.0}},\n\n// MZ2_HOVER_BLASTER_1\t\t\t\t62\n\t{\"MZ2_HOVER_BLASTER\", {32.5, -0.8, 10.0}},\n\n// MZ2_ACTOR_MACHINEGUN_1\t\t\t63\n\t{\"MZ2_ACTOR_MACHINEGUN\", {18.4, 7.4, 9.6}},\n\n// MZ2_SUPERTANK_MACHINEGUN_1\t\t64\n\t{\"MZ2_SUPERTANK_MACHINEGUN\", {30.0, 30.0, 88.5}},\n// MZ2_SUPERTANK_MACHINEGUN_2\t\t65\n\t{\"MZ2_SUPERTANK_MACHINEGUN\", {30.0, 30.0, 88.5}},\n// MZ2_SUPERTANK_MACHINEGUN_3\t\t66\n\t{\"MZ2_SUPERTANK_MACHINEGUN\", {30.0, 30.0, 88.5}},\n// MZ2_SUPERTANK_MACHINEGUN_4\t\t67\n\t{\"MZ2_SUPERTANK_MACHINEGUN\", {30.0, 30.0, 88.5}},\n// MZ2_SUPERTANK_MACHINEGUN_5\t\t68\n\t{\"MZ2_SUPERTANK_MACHINEGUN\", {30.0, 30.0, 88.5}},\n// MZ2_SUPERTANK_MACHINEGUN_6\t\t69\n\t{\"MZ2_SUPERTANK_MACHINEGUN\", {30.0, 30.0, 88.5}},\n// MZ2_SUPERTANK_ROCKET_1\t\t\t70\n\t{\"MZ2_SUPERTANK_ROCKET\", {16.0, -22.5, 91.2}},\n// MZ2_SUPERTANK_ROCKET_2\t\t\t71\n\t{\"MZ2_SUPERTANK_ROCKET\", {16.0, -33.4, 86.7}},\n// MZ2_SUPERTANK_ROCKET_3\t\t\t72\n\t{\"MZ2_SUPERTANK_ROCKET\", {16.0, -42.8, 83.3}},\n\n// --- Start Xian Stuff ---\n// MZ2_BOSS2_MACHINEGUN_L1\t\t\t73\n\t{\"MZ2_BOSS2_MACHINEGUN_L\", {32,\t-40,\t70}},\n// MZ2_BOSS2_MACHINEGUN_L2\t\t\t74\n\t{\"MZ2_BOSS2_MACHINEGUN_L\", {32,\t-40,\t70}},\n// MZ2_BOSS2_MACHINEGUN_L3\t\t\t75\n\t{\"MZ2_BOSS2_MACHINEGUN_L\", {32,\t-40,\t70}},\n// MZ2_BOSS2_MACHINEGUN_L4\t\t\t76\n\t{\"MZ2_BOSS2_MACHINEGUN_L\", {32,\t-40,\t70}},\n// MZ2_BOSS2_MACHINEGUN_L5\t\t\t77\n\t{\"MZ2_BOSS2_MACHINEGUN_L\", {32,\t-40,\t70}},\n// --- End Xian Stuff\n\n// MZ2_BOSS2_ROCKET_1\t\t\t\t78\n\t{\"MZ2_BOSS2_ROCKET\", {22.0, 16.0, 10.0}},\n// MZ2_BOSS2_ROCKET_2\t\t\t\t79\n\t{\"MZ2_BOSS2_ROCKET\", {22.0, 8.0, 10.0}},\n// MZ2_BOSS2_ROCKET_3\t\t\t\t80\n\t{\"MZ2_BOSS2_ROCKET\", {22.0, -8.0, 10.0}},\n// MZ2_BOSS2_ROCKET_4\t\t\t\t81\n\t{\"MZ2_BOSS2_ROCKET\", {22.0, -16.0, 10.0}},\n\n// MZ2_FLOAT_BLASTER_1\t\t\t\t82\n\t{\"MZ2_FLOAT_BLASTER\", {32.5, -0.8, 10}},\n\n// MZ2_SOLDIER_BLASTER_3\t\t\t83\n\t{\"MZ2_SOLDIER_BLASTER\", {20.8 * 1.2, 10.1 * 1.2, -2.7 * 1.2}},\n// MZ2_SOLDIER_SHOTGUN_3\t\t\t84\n\t{\"MZ2_SOLDIER_SHOTGUN\", {20.8 * 1.2, 10.1 * 1.2, -2.7 * 1.2}},\n// MZ2_SOLDIER_MACHINEGUN_3\t\t\t85\n\t{\"MZ2_SOLDIER_MACHINEGUN\", {20.8 * 1.2, 10.1 * 1.2, -2.7 * 1.2}},\n// MZ2_SOLDIER_BLASTER_4\t\t\t86\n\t{\"MZ2_SOLDIER_BLASTER\", {7.6 * 1.2, 9.3 * 1.2, 0.8 * 1.2}},\n// MZ2_SOLDIER_SHOTGUN_4\t\t\t87\n\t{\"MZ2_SOLDIER_SHOTGUN\", {7.6 * 1.2, 9.3 * 1.2, 0.8 * 1.2}},\n// MZ2_SOLDIER_MACHINEGUN_4\t\t\t88\n\t{\"MZ2_SOLDIER_MACHINEGUN\", {7.6 * 1.2, 9.3 * 1.2, 0.8 * 1.2}},\n// MZ2_SOLDIER_BLASTER_5\t\t\t89\n\t{\"MZ2_SOLDIER_BLASTER\", {30.5 * 1.2, 9.9 * 1.2, -18.7 * 1.2}},\n// MZ2_SOLDIER_SHOTGUN_5\t\t\t90\n\t{\"MZ2_SOLDIER_SHOTGUN\", {30.5 * 1.2, 9.9 * 1.2, -18.7 * 1.2}},\n// MZ2_SOLDIER_MACHINEGUN\t\t\t91\n\t{\"MZ2_SOLDIER_MACHINEGUN\", {30.5 * 1.2, 9.9 * 1.2, -18.7 * 1.2}},\n// MZ2_SOLDIER_BLASTER_6\t\t\t92\n\t{\"MZ2_SOLDIER_BLASTER\", {27.6 * 1.2, 3.4 * 1.2, -10.4 * 1.2}},\n// MZ2_SOLDIER_SHOTGUN_6\t\t\t93\n\t{\"MZ2_SOLDIER_SHOTGUN\", {27.6 * 1.2, 3.4 * 1.2, -10.4 * 1.2}},\n// MZ2_SOLDIER_MACHINEGUN_6\t\t\t94\n\t{\"MZ2_SOLDIER_MACHINEGUN\", {27.6 * 1.2, 3.4 * 1.2, -10.4 * 1.2}},\n// MZ2_SOLDIER_BLASTER_7\t\t\t95\n\t{\"MZ2_SOLDIER_BLASTER\", {28.9 * 1.2, 4.6 * 1.2, -8.1 * 1.2}},\n// MZ2_SOLDIER_SHOTGUN_7\t\t\t96\n\t{\"MZ2_SOLDIER_SHOTGUN\", {28.9 * 1.2, 4.6 * 1.2, -8.1 * 1.2}},\n// MZ2_SOLDIER_MACHINEGUN_7\t\t\t97\n\t{\"MZ2_SOLDIER_MACHINEGUN\", {28.9 * 1.2, 4.6 * 1.2, -8.1 * 1.2}},\n// MZ2_SOLDIER_BLASTER_8\t\t\t98\n//\t34.5 * 1.2, 9.6 * 1.2, 6.1 * 1.2,\n\t{\"MZ2_SOLDIER_BLASTER\", {31.5 * 1.2, 9.6 * 1.2, 10.1 * 1.2}},\n// MZ2_SOLDIER_SHOTGUN_8\t\t\t99\n\t{\"MZ2_SOLDIER_SHOTGUN\", {34.5 * 1.2, 9.6 * 1.2, 6.1 * 1.2}},\n// MZ2_SOLDIER_MACHINEGUN_8\t\t\t100\n\t{\"MZ2_SOLDIER_MACHINEGUN\", {34.5 * 1.2, 9.6 * 1.2, 6.1 * 1.2}},\n\n// --- Xian shit below ---\n// MZ2_MAKRON_BFG\t\t\t\t\t101\n\t{\"MZ2_MAKRON_BFG\", {17,\t\t-19.5,\t62.9}},\n// MZ2_MAKRON_BLASTER_1\t\t\t\t102\n\t{\"MZ2_MAKRON_BLASTER\", {-3.6,\t-24.1,\t59.5}},\n// MZ2_MAKRON_BLASTER_2\t\t\t\t103\n\t{\"MZ2_MAKRON_BLASTER\", {-1.6,\t-19.3,\t59.5}},\n// MZ2_MAKRON_BLASTER_3\t\t\t\t104\n\t{\"MZ2_MAKRON_BLASTER\", {-0.1,\t-14.4,\t59.5,\t\t}},\n// MZ2_MAKRON_BLASTER_4\t\t\t\t105\n\t{\"MZ2_MAKRON_BLASTER\", {2.0,\t-7.6,\t59.5,\t}},\n// MZ2_MAKRON_BLASTER_5\t\t\t\t106\n\t{\"MZ2_MAKRON_BLASTER\", {3.4,\t1.3,\t59.5}},\n// MZ2_MAKRON_BLASTER_6\t\t\t\t107\n\t{\"MZ2_MAKRON_BLASTER\", {3.7,\t11.1,\t59.5,\t}},\n// MZ2_MAKRON_BLASTER_7\t\t\t\t108\n\t{\"MZ2_MAKRON_BLASTER\", {-0.3,\t22.3,\t59.5}},\n// MZ2_MAKRON_BLASTER_8\t\t\t\t109\n\t{\"MZ2_MAKRON_BLASTER\", {-6,\t\t33,\t\t59.5}},\n// MZ2_MAKRON_BLASTER_9\t\t\t\t110\n\t{\"MZ2_MAKRON_BLASTER\", {-9.3,\t36.4,\t59.5}},\n// MZ2_MAKRON_BLASTER_10\t\t\t111\n\t{\"MZ2_MAKRON_BLASTER\", {-7,\t\t35,\t\t59.5}},\n// MZ2_MAKRON_BLASTER_11\t\t\t112\n\t{\"MZ2_MAKRON_BLASTER\", {-2.1,\t29,\t\t59.5}},\n// MZ2_MAKRON_BLASTER_12\t\t\t113\n\t{\"MZ2_MAKRON_BLASTER\", {3.9,\t17.3,\t59.5}},\n// MZ2_MAKRON_BLASTER_13\t\t\t114\n\t{\"MZ2_MAKRON_BLASTER\", {6.1,\t5.8,\t59.5}},\n// MZ2_MAKRON_BLASTER_14\t\t\t115\n\t{\"MZ2_MAKRON_BLASTER\", {5.9,\t-4.4,\t59.5}},\n// MZ2_MAKRON_BLASTER_15\t\t\t116\n\t{\"MZ2_MAKRON_BLASTER\", {4.2,\t-14.1,\t59.5,\t\t}},\n// MZ2_MAKRON_BLASTER_16\t\t\t117\n\t{\"MZ2_MAKRON_BLASTER\", {2.4,\t-18.8,\t59.5}},\n// MZ2_MAKRON_BLASTER_17\t\t\t118\n\t{\"MZ2_MAKRON_BLASTER\", {-1.8,\t-25.5,\t59.5}},\n// MZ2_MAKRON_RAILGUN_1\t\t\t\t119\n\t{\"MZ2_MAKRON_RAILGUN\", {-17.3,\t7.8,\t72.4}},\n\n// MZ2_JORG_MACHINEGUN_L1\t\t\t120\n\t{\"MZ2_JORG_MACHINEGUN_L\", {78.5,\t-47.1,\t96,\t\t\t}},\n// MZ2_JORG_MACHINEGUN_L2\t\t\t121\n\t{\"MZ2_JORG_MACHINEGUN_L\", {78.5,\t-47.1,\t96,\t\t\t}},\n// MZ2_JORG_MACHINEGUN_L3\t\t\t122\n\t{\"MZ2_JORG_MACHINEGUN_L\", {78.5,\t-47.1,\t96,\t\t\t}},\n// MZ2_JORG_MACHINEGUN_L4\t\t\t123\n\t{\"MZ2_JORG_MACHINEGUN_L\", {78.5,\t-47.1,\t96,\t\t\t}},\n// MZ2_JORG_MACHINEGUN_L5\t\t\t124\n\t{\"MZ2_JORG_MACHINEGUN_L\", {78.5,\t-47.1,\t96,\t\t\t}},\n// MZ2_JORG_MACHINEGUN_L6\t\t\t125\n\t{\"MZ2_JORG_MACHINEGUN_L\", {78.5,\t-47.1,\t96,\t\t\t}},\n// MZ2_JORG_MACHINEGUN_R1\t\t\t126\n\t{\"MZ2_JORG_MACHINEGUN_R\", {78.5,\t46.7,  96,\t\t\t}},\n// MZ2_JORG_MACHINEGUN_R2\t\t\t127\n\t{\"MZ2_JORG_MACHINEGUN_R\", {78.5,\t46.7,\t96,\t\t\t}},\n// MZ2_JORG_MACHINEGUN_R3\t\t\t128\n\t{\"MZ2_JORG_MACHINEGUN_R\", {78.5,\t46.7,\t96,\t\t\t}},\n// MZ2_JORG_MACHINEGUN_R4\t\t\t129\n\t{\"MZ2_JORG_MACHINEGUN_R\", {78.5,\t46.7,\t96,\t\t\t}},\n// MZ2_JORG_MACHINEGUN_R5\t\t\t130\n\t{\"MZ2_JORG_MACHINEGUN_R\", {78.5,\t46.7,\t96,\t\t\t}},\n// MZ2_JORG_MACHINEGUN_R6\t\t\t131\n\t{\"MZ2_JORG_MACHINEGUN_R\", {78.5,\t46.7,\t96,\t\t\t}},\n// MZ2_JORG_BFG_1\t\t\t\t\t132\n\t{\"MZ2_JORG_BFG_1\", {6.3,\t-9,\t\t111.2}},\n\n// MZ2_BOSS2_MACHINEGUN_R1\t\t\t73\n\t{\"MZ2_BOSS2_MACHINEGUN_R\", {32,\t40,\t70}},\n// MZ2_BOSS2_MACHINEGUN_R2\t\t\t74\n\t{\"MZ2_BOSS2_MACHINEGUN_R\", {32,\t40,\t70}},\n// MZ2_BOSS2_MACHINEGUN_R3\t\t\t75\n\t{\"MZ2_BOSS2_MACHINEGUN_R\", {32,\t40,\t70}},\n// MZ2_BOSS2_MACHINEGUN_R4\t\t\t76\n\t{\"MZ2_BOSS2_MACHINEGUN_R\", {32,\t40,\t70}},\n// MZ2_BOSS2_MACHINEGUN_R5\t\t\t77\n\t{\"MZ2_BOSS2_MACHINEGUN_R\", {32,\t40,\t70}},\n\n// --- End Xian Shit ---\n\n// ROGUE\n// note that the above really ends at 137\n// carrier machineguns\n// MZ2_CARRIER_MACHINEGUN_L1\n\t{\"MZ2_CARRIER_MACHINEGUN_L\", {56,\t-32, 32}},\n// MZ2_CARRIER_MACHINEGUN_R1\n\t{\"MZ2_CARRIER_MACHINEGUN_R\", {56,\t32, 32}},\n// MZ2_CARRIER_GRENADE\n\t{\"MZ2_CARRIER_GRENADE\", {42,\t24, 50}},\n// MZ2_TURRET_MACHINEGUN\t\t\t141\n\t{\"MZ2_TURRET_MACHINEGUN\", {16, 0, 0}},\n// MZ2_TURRET_ROCKET\t\t\t\t142\n\t{\"MZ2_TURRET_ROCKET\", {16, 0, 0}},\n// MZ2_TURRET_BLASTER\t\t\t\t143\n\t{\"MZ2_TURRET_BLASTER\", {16, 0, 0}},\n// MZ2_STALKER_BLASTER\t\t\t\t144\n\t{\"MZ2_STALKER_BLASTER\", {24, 0, 6}},\n// MZ2_DAEDALUS_BLASTER\t\t\t\t145\n\t{\"MZ2_DAEDALUS_BLASTER\", {32.5, -0.8, 10.0}},\n// MZ2_MEDIC_BLASTER_2\t\t\t\t146\n\t{\"MZ2_MEDIC_BLASTER\", {12.1, 5.4, 16.5}},\n// MZ2_CARRIER_RAILGUN\t\t\t\t147\n\t{\"MZ2_CARRIER_RAILGUN\", {32, 0, 6, }},\n// MZ2_WIDOW_DISRUPTOR\t\t\t\t148\n\t{\"MZ2_WIDOW_DISRUPTOR\", {57.72, 14.50, 88.81}},\n// MZ2_WIDOW_BLASTER\t\t\t\t149\n\t{\"MZ2_WIDOW_BLASTER\", {56,\t32, 32}},\n// MZ2_WIDOW_RAIL\t\t\t\t\t150\n\t{\"MZ2_WIDOW_RAIL\", {62, -20, 84, }},\n// MZ2_WIDOW_PLASMABEAM\t\t\t\t151\t\t// PMM - not used!\n\t{\"MZ2_WIDOW_PLASMABEAM\", {32, 0, 6, }},\n// MZ2_CARRIER_MACHINEGUN_L2\t\t152\n\t{\"MZ2_CARRIER_MACHINEGUN_L\", {61,\t-32, 12}},\n// MZ2_CARRIER_MACHINEGUN_R2\t\t153\n\t{\"MZ2_CARRIER_MACHINEGUN_R\", {61,\t32, 12}},\n// MZ2_WIDOW_RAIL_LEFT\t\t\t\t154\n\t{\"MZ2_WIDOW_RAIL_LEFT\", {17, -62, 91, }},\n// MZ2_WIDOW_RAIL_RIGHT\t\t\t\t155\n\t{\"MZ2_WIDOW_RAIL_RIGHT\", {68, 12, 86, }},\n// MZ2_WIDOW_BLASTER_SWEEP1\t\t\t156\t\t\tpmm - the sweeps need to be in sequential order\n\t{\"MZ2_WIDOW_BLASTER_SWEEP\", {47.5, 56, 89}},\n// MZ2_WIDOW_BLASTER_SWEEP2\t\t\t157\n\t{\"MZ2_WIDOW_BLASTER_SWEEP\", {54, 52, 91}},\n// MZ2_WIDOW_BLASTER_SWEEP3\t\t\t158\n\t{\"MZ2_WIDOW_BLASTER_SWEEP\", {58, 40, 91}},\n// MZ2_WIDOW_BLASTER_SWEEP4\t\t\t159\n\t{\"MZ2_WIDOW_BLASTER_SWEEP\", {68, 30, 88}},\n// MZ2_WIDOW_BLASTER_SWEEP5\t\t\t160\n\t{\"MZ2_WIDOW_BLASTER_SWEEP\", {74, 20, 88}},\n// MZ2_WIDOW_BLASTER_SWEEP6\t\t\t161\n\t{\"MZ2_WIDOW_BLASTER_SWEEP\", {73, 11, 87}},\n// MZ2_WIDOW_BLASTER_SWEEP7\t\t\t162\n\t{\"MZ2_WIDOW_BLASTER_SWEEP\", {73, 3, 87}},\n// MZ2_WIDOW_BLASTER_SWEEP8\t\t\t163\n\t{\"MZ2_WIDOW_BLASTER_SWEEP\", {70, -12, 87}},\n// MZ2_WIDOW_BLASTER_SWEEP9\t\t\t164\n\t{\"MZ2_WIDOW_BLASTER_SWEEP\", {67, -20, 90}},\n// MZ2_WIDOW_BLASTER_100\t\t\t165\n\t{\"MZ2_WIDOW_BLASTER\", {-20, 76, 90}},\n// MZ2_WIDOW_BLASTER_90\t\t\t\t166\n\t{\"MZ2_WIDOW_BLASTER\", {-8, 74, 90}},\n// MZ2_WIDOW_BLASTER_80\t\t\t\t167\n\t{\"MZ2_WIDOW_BLASTER\", {0, 72, 90}},\n// MZ2_WIDOW_BLASTER_70\t\t\t\t168\t\td06\n\t{\"MZ2_WIDOW_BLASTER\", {10, 71, 89}},\n// MZ2_WIDOW_BLASTER_60\t\t\t\t169\t\td07\n\t{\"MZ2_WIDOW_BLASTER\", {23, 70, 87}},\n// MZ2_WIDOW_BLASTER_50\t\t\t\t170\t\td08\n\t{\"MZ2_WIDOW_BLASTER\", {32, 64, 85}},\n// MZ2_WIDOW_BLASTER_40\t\t\t\t171\n\t{\"MZ2_WIDOW_BLASTER\", {40, 58, 84}},\n// MZ2_WIDOW_BLASTER_30\t\t\t\t172\t\td10\n\t{\"MZ2_WIDOW_BLASTER\", {48, 50, 83}},\n// MZ2_WIDOW_BLASTER_20\t\t\t\t173\n\t{\"MZ2_WIDOW_BLASTER\", {54, 42, 82}},\n// MZ2_WIDOW_BLASTER_10\t\t\t\t174\t\td12\n\t{\"MZ2_WIDOW_BLASTER\", {56, 34, 82}},\n// MZ2_WIDOW_BLASTER_0\t\t\t\t175\n\t{\"MZ2_WIDOW_BLASTER\", {58, 26, 82}},\n// MZ2_WIDOW_BLASTER_10L\t\t\t176\t\td14\n\t{\"MZ2_WIDOW_BLASTER\", {60, 16, 82}},\n// MZ2_WIDOW_BLASTER_20L\t\t\t177\n\t{\"MZ2_WIDOW_BLASTER\", {59, 6, 81}},\n// MZ2_WIDOW_BLASTER_30L\t\t\t178\t\td16\n\t{\"MZ2_WIDOW_BLASTER\", {58, -2, 80}},\n// MZ2_WIDOW_BLASTER_40L\t\t\t179\n\t{\"MZ2_WIDOW_BLASTER\", {57, -10, 79}},\n// MZ2_WIDOW_BLASTER_50L\t\t\t180\t\td18\n\t{\"MZ2_WIDOW_BLASTER\", {54, -18, 78}},\n// MZ2_WIDOW_BLASTER_60L\t\t\t181\n\t{\"MZ2_WIDOW_BLASTER\", {42, -32, 80}},\n// MZ2_WIDOW_BLASTER_70L\t\t\t182\t\td20\n\t{\"MZ2_WIDOW_BLASTER\", {36, -40, 78}},\n// MZ2_WIDOW_RUN_1\t\t\t\t\t183\n\t{\"MZ2_WIDOW_RUN\", {68.4, 10.88, 82.08}},\n// MZ2_WIDOW_RUN_2\t\t\t\t\t184\n\t{\"MZ2_WIDOW_RUN_2\", {68.51, 8.64, 85.14}},\n// MZ2_WIDOW_RUN_3\t\t\t\t\t185\n\t{\"MZ2_WIDOW_RUN\", {68.66, 6.38, 88.78}},\n// MZ2_WIDOW_RUN_4\t\t\t\t\t186\n\t{\"MZ2_WIDOW_RUN\", {68.73, 5.1, 84.47}},\n// MZ2_WIDOW_RUN_5\t\t\t\t\t187\n\t{\"MZ2_WIDOW_RUN\", {68.82, 4.79, 80.52}},\n// MZ2_WIDOW_RUN_6\t\t\t\t\t188\n\t{\"MZ2_WIDOW_RUN\", {68.77, 6.11, 85.37}},\n// MZ2_WIDOW_RUN_7\t\t\t\t\t189\n\t{\"MZ2_WIDOW_RUN\", {68.67, 7.99, 90.24}},\n// MZ2_WIDOW_RUN_8\t\t\t\t\t190\n\t{\"MZ2_WIDOW_RUN\", {68.55, 9.54, 87.36}},\n// MZ2_CARRIER_ROCKET_1\t\t\t\t191\n\t{\"MZ2_CARRIER_ROCKET\", {0, 0, -5}},\n// MZ2_CARRIER_ROCKET_2\t\t\t\t192\n\t{\"MZ2_CARRIER_ROCKET\", {0, 0, -5}},\n// MZ2_CARRIER_ROCKET_3\t\t\t\t193\n\t{\"MZ2_CARRIER_ROCKET\", {0, 0, -5}},\n// MZ2_CARRIER_ROCKET_4\t\t\t\t194\n\t{\"MZ2_CARRIER_ROCKET\", {0, 0, -5}},\n// MZ2_WIDOW2_BEAMER_1\t\t\t\t195\n//\t72.13, -17.63, 93.77,\n\t{\"MZ2_WIDOW2_BEAMER\", {69.00, -17.63, 93.77}},\n// MZ2_WIDOW2_BEAMER_2\t\t\t\t196\n//\t71.46, -17.08, 89.82,\n\t{\"MZ2_WIDOW2_BEAMER\", {69.00, -17.08, 89.82}},\n// MZ2_WIDOW2_BEAMER_3\t\t\t\t197\n//\t71.47, -18.40, 90.70,\n\t{\"MZ2_WIDOW2_BEAMER\", {69.00, -18.40, 90.70}},\n// MZ2_WIDOW2_BEAMER_4\t\t\t\t198\n//\t71.96, -18.34, 94.32,\n\t{\"MZ2_WIDOW2_BEAMER\", {69.00, -18.34, 94.32}},\n// MZ2_WIDOW2_BEAMER_5\t\t\t\t199\n//\t72.25, -18.30, 97.98,\n\t{\"MZ2_WIDOW2_BEAMER\", {69.00, -18.30, 97.98}},\n// MZ2_WIDOW2_BEAM_SWEEP_1\t\t\t200\n\t{\"MZ2_WIDOW2_BEAM_SWEEP\", {45.04, -59.02, 92.24}},\n// MZ2_WIDOW2_BEAM_SWEEP_2\t\t\t201\n\t{\"MZ2_WIDOW2_BEAM_SWEEP\", {50.68, -54.70, 91.96}},\n// MZ2_WIDOW2_BEAM_SWEEP_3\t\t\t202\n\t{\"MZ2_WIDOW2_BEAM_SWEEP\", {56.57, -47.72, 91.65}},\n// MZ2_WIDOW2_BEAM_SWEEP_4\t\t\t203\n\t{\"MZ2_WIDOW2_BEAM_SWEEP\", {61.75, -38.75, 91.38}},\n// MZ2_WIDOW2_BEAM_SWEEP_5\t\t\t204\n\t{\"MZ2_WIDOW2_BEAM_SWEEP\", {65.55, -28.76, 91.24}},\n// MZ2_WIDOW2_BEAM_SWEEP_6\t\t\t205\n\t{\"MZ2_WIDOW2_BEAM_SWEEP\", {67.79, -18.90, 91.22}},\n// MZ2_WIDOW2_BEAM_SWEEP_7\t\t\t206\n\t{\"MZ2_WIDOW2_BEAM_SWEEP\", {68.60, -9.52, 91.23}},\n// MZ2_WIDOW2_BEAM_SWEEP_8\t\t\t207\n\t{\"MZ2_WIDOW2_BEAM_SWEEP\", {68.08, 0.18, 91.32}},\n// MZ2_WIDOW2_BEAM_SWEEP_9\t\t\t208\n\t{\"MZ2_WIDOW2_BEAM_SWEEP\", {66.14, 9.79, 91.44}},\n// MZ2_WIDOW2_BEAM_SWEEP_10\t\t\t209\n\t{\"MZ2_WIDOW2_BEAM_SWEEP\", {62.77, 18.91, 91.65}},\n// MZ2_WIDOW2_BEAM_SWEEP_11\t\t\t210\n\t{\"MZ2_WIDOW2_BEAM_SWEEP\", {58.29, 27.11, 92.00}}\n};\n"
  },
  {
    "path": "engine/client/quake.manifest",
    "content": "<assembly xmlns=\"urn:schemas-microsoft-com:asm.v1\" manifestVersion=\"1.0\" xmlns:asmv3=\"urn:schemas-microsoft-com:asm.v3\">\r\n  <asmv3:trustInfo>\r\n    <security>\r\n      <requestedPrivileges>\r\n        <requestedExecutionLevel level=\"asInvoker\" uiAccess=\"false\"></requestedExecutionLevel>\r\n      </requestedPrivileges>\r\n    </security>\r\n  </asmv3:trustInfo>\r\n  <asmv3:application>\r\n    <asmv3:windowsSettings xmlns=\"http://schemas.microsoft.com/SMI/2005/WindowsSettings\">\r\n      <dpiAware>true</dpiAware>\r\n    </asmv3:windowsSettings>\r\n  </asmv3:application>\r\n</assembly>"
  },
  {
    "path": "engine/client/quakedef.h",
    "content": "/*\nCopyright (C) 1996-1997 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n*/\n// quakedef.h -- primary header for client\n\n#ifndef __QUAKEDEF_H__\n#define __QUAKEDEF_H__\n\n#include \"../common/bothdefs.h\"\t//first thing included by ALL files.\n\n//for msvc #pragma message lines\n#if defined(_MSC_VER)\n#define MSVC_LINE\t__FILE__\"(\"STRINGIFY(__LINE__)\"):\"\n#define warningmsg(s) message(MSVC_LINE s)\n#elif __GNUC__ >=4\n#define warningmsg(s) message(s)\n#endif\n\n#ifdef MSVCDISABLEWARNINGS\n//#pragma warning( disable : 4244 4127 4201 4214 4514 4305 4115 4018)\n/*#pragma warning( disable : 4244)\t//conversion from const double to float\n#pragma warning( disable : 4305)\t//truncation from const double to float\n#pragma warning( disable : 4018)\t//signed/unsigned mismatch... fix these?\n#pragma warning( disable : 4706)\t//assignment within conditional expression - watch for these in GCC where they can be fixed but still functional.\n#pragma warning( disable : 4100)\t//unreferenced formal parameter\n#pragma warning( disable : 4201)\t//nonstandard extension used : nameless struct/union\n#pragma warning( disable : 4213)\t//nonstandard extension used : cast on l-value\n#pragma warning( disable : 4127)\t//conditional expression is constant - fixme?\n*/\n#pragma warning( 4 : 4244)\t//conversion from const double to float\n#pragma warning( 4 : 4305)\t//truncation from const double to float\n#pragma warning( 4 : 4018)\t//truncation from const double to float\n\n#pragma warning( 2 : 4701)\n#pragma warning(2:4132 4268)// const object not initialized\n\n#pragma warning(2:4032)\t\t// function arg has different type from declaration\n#pragma warning(2:4092)\t\t// 'sizeof' value too big\n#pragma warning(2:4132 4268)// const object not initialized\n//#pragma warning(2:4152)\t// pointer conversion between function and data\n#pragma warning(2:4239)\t\t// standard doesn't allow this conversion\n#pragma warning(2:4701)\t\t// local variable used without being initialized\n//#pragma warning(2:4706)\t// if (a=b) instead of (if a==b)\n#pragma warning(2:4709)\t\t// comma in array subscript\n#pragma warning(3:4061)\t\t// not all enum values tested in switch statement\n#pragma warning(3:4710)\t\t// inline function was not inlined\n#pragma warning(3:4121)\t\t// space added for structure alignment\n#pragma warning(3:4505)\t\t// unreferenced local function removed\n#pragma warning(3:4019)\t\t// empty statement at global scope\n//#pragma warning(3:4057)\t// pointers refer to different base types\n#pragma warning(3:4125)\t\t// decimal digit terminates octal escape\n#pragma warning(2:4131)\t\t// old-style function declarator\n#pragma warning(3:4211)\t\t// extern redefined as static\n//#pragma warning(3:4213)\t// cast on left side of = is non-standard\n#pragma warning(3:4222)\t\t// member function at file scope shouldn't be static\n#pragma warning(3:4234 4235)// keyword not supported or reserved for future\n#pragma warning(3:4504)\t\t// type ambiguous; simplify code\n#pragma warning(3:4507)\t\t// explicit linkage specified after default linkage\n#pragma warning(3:4515)\t\t// namespace uses itself\n#pragma warning(3:4516 4517)// access declarations are deprecated\n#pragma warning(3:4670)\t\t// base class of thrown object is inaccessible\n#pragma warning(3:4671)\t\t// copy ctor of thrown object is inaccessible\n#pragma warning(3:4673)\t\t// thrown object cannot be handled in catch block\n#pragma warning(3:4674)\t\t// dtor of thrown object is inaccessible\n#pragma warning(3:4705)\t\t// statement has no effect (example: a+1;)\n\n#pragma warning(3:4013)\t\t// function undefined, assuming extern returning int\n\n\n#pragma warning( 4 : 4267)\t//truncation from const double to float\n#pragma warning( 4 : 4710)\t//function not inlined\n\n#pragma warning( error : 4020)\n\n#pragma warning(error:4013)\n\n//msvc... shut the fuck up.\n#define _CRT_SECURE_NO_DEPRECATE\n#define _CRT_NONSTDC_NO_DEPRECATE\n#endif\n\n\n#define QUAKEDEF_H__\n\n#ifdef __linux__\n#define PNG_SUCKS_WITH_SETJMP\t//cos it does.\n#endif\n\n//define\tPARANOID\t\t\t// speed sapping error checking\n\n#include <float.h>\n#include <math.h>\n#include <string.h>\n#include <stdarg.h>\n#include <stdio.h>\n#include <stdlib.h>\n#if defined(AVAIL_PNGLIB) && defined(PNG_SUCKS_WITH_SETJMP) && !defined(SERVERONLY)\n\t#if defined(MINGW)\n\t\t#include \"./mingw-libs/png.h\"\n\t#elif defined(_WIN32)\n\t\t#include \"png.h\"\n\t#else\n\t\t#include <png.h>\n\t#endif\n#else\n\t#ifdef FTE_TARGET_WEB\n\t\t#include \"web/ftejslib.h\"\n\t\t//officially, emscripten supports longjmp.\n\t\t//unofficially, this makes firefox crash with memory issues.\n\t\t#define setjmp(x) (x=0,x)\n\t\t#define longjmp(b,r) emscriptenfte_abortmainloop(__func__, false)\n\t\ttypedef int jmp_buf;\n\t#else\n\t\t#include <setjmp.h>\n\t#endif\n#endif\n#include <time.h>\n\n#ifdef USE_MSVCRT_DEBUG\n\t#define _CRTDBG_MAP_ALLOC\n\t#include <crtdbg.h>\n#endif\n#if defined(_WIN32) || defined(__DJGPP__)\n\t#include <malloc.h>\n#elif defined(__unix__) && !defined(__linux__) // quick hack for the bsds and other unix systems\n\t#include<stdlib.h>\n#elif !defined(alloca)\t//alloca.h isn't present on bsd (stdlib.h should define it to __builtin_alloca, and we can check for that here).\n\t#include <alloca.h>\n#endif\n\n#ifdef FTE_TARGET_WEB //emscripten's filesystem is throwing all sorts of exceptions and making it hard to debug real bugs.\n\t#define NOSTDIO\n#endif\n#ifdef NOSTDIO\n\t#define stat stat_nolink\n\t#define fopen fopen_nolink\n\t#define fread fread_nolink\n\t#define fwrite fwrite_nolink\n\t#define fclose fclose_nolink\n\t#define fseek fseek_nolink\n\t#define open open_nolink\n\t#define read read_nolink\n\t#define write write_nolink\n\t#define close close_nolink\n#endif\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include \"../common/common.h\"\n#include \"../common/bspfile.h\"\n#include \"vid.h\"\n#include \"../common/sys.h\"\n#include \"../common/zone.h\"\n#include \"../common/mathlib.h\"\n#include \"../common/cvar.h\"\n#include \"../common/translate.h\"\n#include \"../common/net.h\"\n#ifndef WEBSVONLY\n#include \"../common/protocol.h\"\n#include \"../common/cmd.h\"\n#include \"../common/console.h\"\n#include \"screen.h\"\n#include \"wad.h\"\n#include \"sbar.h\"\n#include \"sound.h\"\n#include \"merged.h\"\n#include \"render.h\"\n#include \"client.h\"\n#include \"../gl/gl_model.h\"\n\n#include \"../common/vm.h\"\n\n#include \"input.h\"\n#include \"keys.h\"\n#include \"view.h\"\n#include \"menu.h\"\n#include \"cdaudio.h\"\n#include \"../common/pmove.h\"\n\n#include \"../qclib/progtype.h\"\n#include \"../server/progdefs.h\"\n#include \"../server/progs.h\"\n#include \"../common/world.h\"\n#include \"../server/q2game.h\"\n#include \"../http/iweb.h\"\n#ifdef CLIENTONLY\n#define SSV_IsSubServer() false\n#else\n#include \"../server/server.h\"\n#endif\n#endif\n\n#if defined(Q3CLIENT) || defined(Q3SERVER)\n#include \"../common/q3api.h\"\n#endif\n\n#ifdef __cplusplus\n\t#define q_max(a,b) ((a) > (b) ? (a) : (b))\n\t#define q_min(a,b) ((a) < (b) ? (a) : (b))\n#else\n\t#ifndef max\n\t\t#define max(a,b) ((a) > (b) ? (a) : (b))\n\t\t#define min(a,b) ((a) < (b) ? (a) : (b))\n\t#endif\n\t#define max3(a,b,c) max(max(a,b),c)\n#endif\n\n//msvcrt lacks any and all c99 support.\n#if defined(_WIN32)\n\t#ifdef __GNUC__\n\t\t#include <inttypes.h>\n\t#else\n\t\t#define PRIxPTR \"p\"\n\t\t//totally different from any other system\n\t\t#define PRIx64 \"I64x\"\n\t\t#define PRIu64 \"I64u\"\n\t\t#define PRIi64 \"I64i\"\n\t#endif\n\n\t#ifdef _WIN64\n\t\t#define PRIxSIZE PRIx64\n\t\t#define PRIuSIZE PRIu64\n\t\t#define PRIiSIZE PRIi64\n\t#else\n\t\t//don't use I, for the sake of older libcs\n\t\t#define PRIxSIZE \"x\"\n\t\t#define PRIuSIZE \"u\"\n\t\t#define PRIiSIZE \"i\"\n\t#endif\n#else\n\t#include <inttypes.h>\n\t//these are non-standard. c99 would expect people to just use %zx etc\n\t#if FTE_WORDSIZE != 32 || __STDC_VERSION__ >= 199901L || defined(__GNUC__)\n\t\t//64bit systems are expected to have an awareness of c99\n\t\t#define PRIxSIZE \"zx\"\n\t\t#define PRIuSIZE \"zu\"\n\t\t#define PRIiSIZE \"zi\"\n\t#else\n\t\t//regular old c89 for 32bit platforms.\n\t\t#define PRIxSIZE PRIxPTR\n\t\t#define PRIuSIZE PRIuPTR\n\t\t#define PRIiSIZE PRIiPTR\n\t#endif\n#endif\n\n\n#ifdef _WIN32\n\t#if (_MSC_VER >= 1900)\n\t\t// MSVC 14 has standardized snprintf functions, hurrah!\n\t#elif (_MSC_VER >= 1400)\n\t\t//with MSVC 8, use microsoft's vsnprintf_s. return values are still wrong though.\n\t\t#define snprintf (void)linuxlike_snprintf_vc8\n\t\tint VARGS linuxlike_snprintf_vc8(char *buffer, int size, const char *format, ...) LIKEPRINTF(3);\n\t\t#define vsnprintf(a, b, c, d) (void)(vsnprintf_s(a, b, _TRUNCATE, c, d))\n\t#else\n\t\t//msvc crap. return values are wrong but at least we can null terminate it safely.\n\t\t#define snprintf (void)linuxlike_snprintf\n\t\tint VARGS linuxlike_snprintf(char *buffer, int size, const char *format, ...) LIKEPRINTF(3);\n\t\t#define vsnprintf (void)linuxlike_vsnprintf\n\t\tint VARGS linuxlike_vsnprintf(char *buffer, int size, const char *format, va_list argptr);\n\t#endif\n\n\t#ifdef _MSC_VER\n\t\t//these are provided so we don't use them\n\t\t//but mingw has some defines elsewhere and makes gcc moan\n//\t\t#define _vsnprintf unsafe_vsnprintf\n//\t\t#define _snprintf unsafe_snprintf\n\n\t\t#ifndef strcasecmp\n\t\t\t#define strcasecmp _stricmp\n\t\t#endif\n\t\t#ifndef strncasecmp\n\t\t\t#define strncasecmp _strnicmp\n\t\t#endif\n\t#endif\n#endif\n\n//=============================================================================\n\n// the host system specifies the base of the directory tree, the\n// command line parms passed to the program, and the amount of memory\n// available for the program to use\n\ntypedef struct quakeparms_s\n{\n\tconst char\t*basedir;\t//working directory\n\tconst char\t*binarydir;\t//exe directory\n\tconst char\t*manifest;\t//linked manifest data (for installer functionality etc)\n\tint\t\t\targc;\n\tconst char\t**argv;\n} quakeparms_t;\n\n\n//=============================================================================\n\n#define MAX_NUM_ARGVS\t128\n\n\nextern qboolean noclip_anglehack;\n\n\n//\n// host\n//\nextern\tquakeparms_t host_parms;\n\nextern\tcvar_t\t\tfs_gamename;\n#ifdef PACKAGEMANAGER\nextern\tcvar_t\t\tpkg_autoupdate;\n#endif\nextern\tcvar_t\t\tcom_protocolname;\nextern\tcvar_t\t\tcom_protocolversion;\nextern\tcvar_t\t\tcom_gamedirnativecode;\nextern\tcvar_t\t\tcom_parseutf8;\n#ifdef HAVE_LEGACY\nextern\tcvar_t\t\tscr_usekfont;\nextern\tcvar_t\t\tezcompat_markup;\n#endif\nextern\tcvar_t\t\tsys_ticrate;\nextern\tcvar_t\t\tsys_nostdout;\nextern\tcvar_t\t\tdeveloper;\nextern\tcvar_t\t\thost_mapname;\n\nextern\tcvar_t\tpassword;\n\nextern\tqboolean\thost_initialized;\t\t// true if into command execution\nextern\tdouble\t\thost_frametime;\nextern\tqbyte\t\t*host_basepal;\nextern\tqbyte\t\t*h2playertranslations;\nextern\tint\t\t\thost_framecount;\t// incremented every frame, never reset\nextern\tdouble\t\trealtime;\t\t\t// not bounded in any way, changed at\n\t\t\t\t\t\t\t\t\t\t// start of every frame, never reset\n\nvoid Host_ServerFrame (void);\nvoid Host_InitCommands (void);\nvoid Host_Init (quakeparms_t *parms);\nvoid Host_FinishInit(void);\nvoid Host_Shutdown(void);\nNORETURN void VARGS Host_Error (const char *error, ...) LIKEPRINTF(1);\nNORETURN void VARGS Host_EndGame (const char *message, ...) LIKEPRINTF(1);\nqboolean Host_SimulationTime(float time);\ndouble Host_Frame (double time);\nqboolean Host_RunFile(const char *fname, int nlen, vfsfile_t *file);\nvoid Host_Quit_f (void);\nvoid VARGS Host_ClientCommands (char *fmt, ...) LIKEPRINTF(1);\nvoid Host_ShutdownServer (qboolean crash);\n\n#ifdef LOADERTHREAD\nextern qboolean com_workererror;\t//supresses shutdown prints+threads\nextern cvar_t worker_flush;\nqboolean COM_DoWork(int thread, qboolean leavelocked);\n#define COM_MainThreadWork() while (COM_DoWork(0, false) && worker_flush.ival) /*called each frame to do any gl uploads or whatever*/\n#define COM_MainThreadFlush() while (COM_DoWork(0, false))\t/*make sure the main thread has done ALL work pending*/\nunsigned int COM_HasWorkers(wgroup_t tg);\nvoid COM_AddWork(wgroup_t thread, void(*func)(void *ctx, void *data, size_t a, size_t b), void *ctx, void *data, size_t a, size_t b);\t//low priority\nvoid COM_InsertWork(wgroup_t tg, void(*func)(void *ctx, void *data, size_t a, size_t b), void *ctx, void *data, size_t a, size_t b);\t//high priority\nqboolean COM_HasWork(void);\nvoid COM_WorkerFullSync(void);\nvoid COM_WorkerLock(void);\t//callable on main thread to temporarily suspend workers (in a safe location)\nvoid COM_WorkerUnlock(void);\nvoid COM_DestroyWorkerThread(void);\nvoid COM_WorkerPartialSync(void *priorityctx, int *address, int value); //aka: while(*address==value)wait();\nextern void *com_resourcemutex;\t//random mutex to simplify resource creation type stuff.\nvoid COM_WorkerAbort(char *message);\t//calls sys_error on the main thread, if running on a worker.\n#ifdef _DEBUG\nvoid COM_AssertMainThread(const char *msg);\n#else\n#define COM_AssertMainThread(msg)\n#endif\n#else\n#define com_workererror false\n#define COM_HasWorkers(t) 0\n#define COM_AddWork(t,f,a,b,c,d) (f)((a),(b),(c),(d))\n#define COM_InsertWork(t,f,a,b,c,d) (f)((a),(b),(c),(d))\n#define COM_WorkerPartialSync(c,a,v)\n#define COM_WorkerFullSync()\n#define COM_WorkerLock()\n#define COM_WorkerUnlock()\n#define COM_HasWork() false\n#define COM_DoWork(t,l) false\n#define COM_AssertMainThread(msg)\n#define COM_MainThreadWork() while(0)\n#define COM_MainThreadFlush()\n#define COM_DestroyWorkerThread()\n#define COM_WorkerAbort(m)\n#endif\n\nextern qboolean\t\tmsg_suppress_1;\t\t// suppresses resolution and cache size console output\n\t\t\t\t\t\t\t\t\t\t//  an fullscreen DIB focus gain/loss\n\n#ifndef HAVE_CLIENT\n#define isDedicated true\n#elif !defined(HAVE_SERVER)\n#define isDedicated false\n#else\nextern qboolean isDedicated;\n#endif\nextern qboolean wantquit;\t//flagged if we want to force a quit, safely breaking out of any modal stuff\n\n\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif //__QUAKEDEF_H__\n"
  },
  {
    "path": "engine/client/r_2d.c",
    "content": "#include \"quakedef.h\"\n#ifndef SERVERONLY\n#include \"shader.h\"\n#include \"gl_draw.h\"\n\n#define WIN32_BLOATED\n#include \"glquake.h\"\n\nqboolean r2d_canhwgamma;\t//says the video code has successfully activated hardware gamma\ntexid_t missing_texture;\ntexid_t missing_texture_gloss;\ntexid_t missing_texture_normal;\n\ntexid_t translate_texture;\nshader_t *translate_shader;\n\ntexid_t ch_int_texture;\nvec3_t ch_color;\nshader_t *shader_crosshair;\n\nstatic mpic_t *conback;\nstatic mpic_t *draw_backtile;\nshader_t *shader_draw_fill, *shader_draw_fill_trans;\nmpic_t\t\t*draw_disc;\n\nshader_t *shader_contrastup;\nshader_t *shader_contrastdown;\nshader_t *shader_brightness;\nshader_t *shader_gammacb;\nshader_t *shader_polyblend;\nshader_t *shader_menutint;\n\n#define DRAW_QUADS 128\nstatic int\t\tdraw_active_flags;\nstatic shader_t *draw_active_shader;\nstatic avec4_t\tdraw_active_colour;\nstatic mesh_t\tdraw_mesh;\nstatic vecV_t\tdraw_mesh_xyz[DRAW_QUADS];\nvec2_t\t\t\tdraw_mesh_st[DRAW_QUADS];\nstatic avec4_t\tdraw_mesh_colors[DRAW_QUADS];\nindex_t\t\t\tr_quad_indexes[DRAW_QUADS*6];\nunsigned int\tr2d_be_flags;\n\nstruct\n{\n\tlmalloc_t allocation;\n\tqboolean dirty;\n\tuploadfmt_t fmt;\n\tint lastid;\n\tunsigned int *data;\n\tshader_t *shader;\n\ttexid_t tex;\n\tapic_t *pics;\n} atlas;\n\nextern cvar_t scr_conalpha;\nextern cvar_t gl_conback;\nextern cvar_t gl_font, con_textfont;\nextern cvar_t r_font_postprocess_outline, r_font_postprocess_mono;\nextern cvar_t gl_screenangle;\nextern cvar_t vid_minsize;\nextern cvar_t vid_conautoscale;\nextern cvar_t vid_baseheight;\nextern cvar_t vid_conheight;\nextern cvar_t vid_conwidth;\nextern cvar_t con_textsize;\nstatic void QDECL R2D_Font_Callback(struct cvar_s *var, char *oldvalue);\nstatic void QDECL R2D_Conautoscale_Callback(struct cvar_s *var, char *oldvalue);\nstatic void QDECL R2D_ScreenAngle_Callback(struct cvar_s *var, char *oldvalue);\nstatic void QDECL R2D_Conheight_Callback(struct cvar_s *var, char *oldvalue);\nstatic void QDECL R2D_Conwidth_Callback(struct cvar_s *var, char *oldvalue);\n\nextern cvar_t crosshair;\nextern cvar_t crosshaircolor;\nextern cvar_t crosshairsize;\nextern cvar_t crosshairimage;\nextern cvar_t crosshairalpha;\nstatic void QDECL R2D_Crosshair_Callback(struct cvar_s *var, char *oldvalue);\nstatic void QDECL R2D_CrosshairImage_Callback(struct cvar_s *var, char *oldvalue);\nstatic void QDECL R2D_CrosshairColor_Callback(struct cvar_s *var, char *oldvalue);\n\nvoid (*R2D_Flush)(void);\n\n//We need this for minor things though, so we'll just use the slow accurate method.\n//this is unlikly to be called too often.\nqbyte GetPaletteIndex(int red, int green, int blue)\n{\n\t//slow, horrible method.\n\t{\n\t\tint i, best=15;\n\t\tint bestdif=256*256*256, curdif;\n\t\textern qbyte *host_basepal;\n\t\tqbyte *pa;\n\n\t#define _abs(x) ((x)*(x))\n\n\t\tpa = host_basepal;\n\t\tfor (i = 0; i < 256; i++, pa+=3)\n\t\t{\n\t\t\tcurdif = _abs(red - pa[0]) + _abs(green - pa[1]) + _abs(blue - pa[2]);\n\t\t\tif (curdif < bestdif)\n\t\t\t{\n\t\t\t\tif (curdif<1)\n\t\t\t\t\treturn i;\n\t\t\t\tbestdif = curdif;\n\t\t\t\tbest = i;\n\t\t\t}\n\t\t}\n\t\treturn best;\n\t}\n}\nqbyte GetPaletteIndexNoFB(int red, int green, int blue)\n{\n\t//slow, horrible method.\n\t{\n\t\tint i, best=15;\n\t\tint bestdif=256*256*256, curdif;\n\t\textern qbyte *host_basepal;\n\t\tqbyte *pa;\n\n\t#define _abs(x) ((x)*(x))\n\n\t\tpa = host_basepal;\n\t\tfor (i = 0; i < 256 - vid.fullbright; i++, pa+=3)\n\t\t{\n\t\t\tcurdif = _abs(red - pa[0]) + _abs(green - pa[1]) + _abs(blue - pa[2]);\n\t\t\tif (curdif < bestdif)\n\t\t\t{\n\t\t\t\tif (curdif<1)\n\t\t\t\t\treturn i;\n\t\t\t\tbestdif = curdif;\n\t\t\t\tbest = i;\n\t\t\t}\n\t\t}\n\t\treturn best;\n\t}\n}\nqbyte GetPaletteIndexRange(int first, int stop, int red, int green, int blue)\n{\n\t//slow, horrible method.\n\t{\n\t\tint i, best=15;\n\t\tint bestdif=256*256*256, curdif;\n\t\textern qbyte *host_basepal;\n\t\tqbyte *pa;\n\n\t#define _abs(x) ((x)*(x))\n\n\t\tpa = host_basepal;\n\t\tfor (i = first; i < stop; i++, pa+=3)\n\t\t{\n\t\t\tcurdif = _abs(red - pa[0]) + _abs(green - pa[1]) + _abs(blue - pa[2]);\n\t\t\tif (curdif < bestdif)\n\t\t\t{\n\t\t\t\tif (curdif<1)\n\t\t\t\t\treturn i;\n\t\t\t\tbestdif = curdif;\n\t\t\t\tbest = i;\n\t\t\t}\n\t\t}\n\t\treturn best;\n\t}\n}\n\nvoid R2D_Shutdown(void)\n{\n\tCvar_Unhook(&con_textfont);\n\tCvar_Unhook(&gl_font);\n\tCvar_Unhook(&r_font_postprocess_outline);\n\tCvar_Unhook(&r_font_postprocess_mono);\n\tCvar_Unhook(&vid_conautoscale);\n\tCvar_Unhook(&gl_screenangle);\n\tCvar_Unhook(&vid_conheight);\n\tCvar_Unhook(&vid_conwidth);\n\tCvar_Unhook(&vid_baseheight);\n\tCvar_Unhook(&vid_minsize);\n\n\tCvar_Unhook(&crosshair);\n\tCvar_Unhook(&crosshairimage);\n\tCvar_Unhook(&crosshaircolor);\n\n\tBZ_Free(cl_stris);\n\tcl_stris = NULL;\n\tBZ_Free(cl_strisvertv);\n\tcl_strisvertv = NULL;\n\tBZ_Free(cl_strisvertc);\n\tcl_strisvertc = NULL;\n\tBZ_Free(cl_strisvertt);\n\tcl_strisvertt = NULL;\n\tBZ_Free(cl_strisidx);\n\tcl_strisidx = NULL;\n\tcl_numstrisidx = 0;\n\tcl_maxstrisidx = 0;\n\tcl_numstrisvert = 0;\n\tcl_maxstrisvert = 0;\n\tcl_numstris = 0;\n\tcl_maxstris = 0;\n\n\tCon_FlushBackgrounds();\n\n\tif (font_console == font_default)\n\t\tfont_console = NULL;\n\n\tif (font_console)\n\t\tFont_Free(font_console);\n\tfont_console = NULL; \n\tif (font_default)\n\t\tFont_Free(font_default);\n\tfont_default = NULL; \n\tif (font_tiny)\n\t\tFont_Free(font_tiny);\n\tfont_tiny = NULL; \n\n#ifndef NOBUILTINMENUS\n\tM_ReloadMenus();\n#endif\n\n#if defined(MENU_DAT) || defined(CSQC_DAT)\n\tPR_ReloadFonts(false);\n#endif\n\n\twhile(atlas.pics)\n\t{\n\t\tapic_t *a = atlas.pics;\n\t\tatlas.pics = a->next;\n\t\tZ_Free(a);\n\t}\n\n\tZ_Free(atlas.data);\n\tmemset(&atlas, 0, sizeof(atlas));\n}\n\n/*\nIniitalise the 2d rendering functions (including font).\nImage loading code must be ready for use at this point.\n*/\nvoid R2D_Init(void)\n{\n\tunsigned int nonorm[4*4];\n\tunsigned int nogloss[4*4];\n\tint i, j;\n\tunsigned int glossval;\n\tunsigned int normval;\n\textern cvar_t gl_specular_fallback, gl_specular_fallbackexp, gl_texturemode;\n\tconback = NULL;\n\n\tCvar_ForceCallback(&gl_texturemode);\n\n\tdraw_mesh.istrifan = true;\n\tdraw_mesh.numvertexes = 4;\n\tdraw_mesh.numindexes = 6;\n\tdraw_mesh.xyz_array = draw_mesh_xyz;\n\tdraw_mesh.st_array = draw_mesh_st;\n\tdraw_mesh.colors4f_array[0] = draw_mesh_colors;\n\tdraw_mesh.indexes = r_quad_indexes;\n\n\tfor (i = 0, j = 0; i < countof(r_quad_indexes); i += 6, j += 4)\n\t{\n\t\tr_quad_indexes[i+0] = j+0;\n\t\tr_quad_indexes[i+1] = j+1;\n\t\tr_quad_indexes[i+2] = j+2;\n\t\tr_quad_indexes[i+3] = j+2;\n\t\tr_quad_indexes[i+4] = j+3;\n\t\tr_quad_indexes[i+5] = j+0;\n\t}\n\n\n\tif (strchr(gl_specular_fallback.string, ' '))\n\t{\n\t\tglossval = bound(0, (int)(gl_specular_fallback.vec4[0]*255), 255)<<0;\n\t\tglossval |= bound(0, (int)(gl_specular_fallback.vec4[1]*255), 255)<<8;\n\t\tglossval |= bound(0, (int)(gl_specular_fallback.vec4[2]*255), 255)<<16;\n\t}\n\telse\n\t{\n\t\tglossval = min(gl_specular_fallback.value*255, 255);\n\t\tglossval *= 0x10101;\n\t}\n\tglossval |= 0x01000000 * bound(0, (int)(gl_specular_fallbackexp.value*255), 255);\n\tglossval = LittleLong(glossval);\n\tnormval = 0xffff8080;\n\tnormval = LittleLong(normval);\n\tfor (i = 0; i < 4*4; i++)\n\t{\n\t\tnogloss[i] = glossval;\n\t\tnonorm[i] = normval;\n\t}\n\tmissing_texture = R_LoadTexture8(\"no_texture\", 16, 16, (unsigned char*)(r_notexture_mip+1), IF_NOALPHA|IF_NOGAMMA|IF_NOPURGE, 0);\n\tmissing_texture_gloss = R_LoadTexture(\"no_texture_gloss\", 4, 4, TF_RGBA32, (unsigned char*)nogloss, IF_NOGAMMA|IF_NOPURGE);\n\tmissing_texture_normal = R_LoadTexture(\"no_texture_normal\", 4, 4, TF_RGBA32, (unsigned char*)nonorm, IF_NOGAMMA|IF_NOPURGE);\n\ttranslate_texture = r_nulltex;\n\tch_int_texture = r_nulltex;\n\n\tShader_Init();\n\tBE_Init();\n\tFont_Init();\n\n\tdraw_backtile = R_RegisterShader(\"gfx/backtile.lmp\", SUF_NONE,\n\t\t\"{\\n\"\n\t\t\t\"if $nofixed\\n\"\n\t\t\t\t\"program default2d\\n\"\n\t\t\t\"endif\\n\"\n\t\t\t\"affine\\n\"\n\t\t\t\"nomipmaps\\n\"\n\t\t\t\"{\\n\"\n\t\t\t\t\"map $diffuse\\n\"\n\t\t\t\"}\\n\"\n\t\t\"}\\n\");\n\tTEXDOWAIT(draw_backtile->defaulttextures->base);\n\tif (!TEXLOADED(draw_backtile->defaulttextures->base))\n\t\tdraw_backtile->defaulttextures->base = R_LoadHiResTexture(\"gfx/backtile\", NULL, IF_UIPIC|IF_NOPICMIP|IF_NOMIPMAP|IF_NOWORKER);\n\tif (!TEXLOADED(draw_backtile->defaulttextures->base))\n\t\tdraw_backtile->defaulttextures->base = R_LoadHiResTexture(\"gfx/menu/backtile\", NULL, IF_UIPIC|IF_NOPICMIP|IF_NOMIPMAP|IF_NOWORKER);\n\tif (!TEXLOADED(draw_backtile->defaulttextures->base))\n\t\tdraw_backtile->defaulttextures->base = R_LoadHiResTexture(\"pics/backtile\", NULL, IF_UIPIC|IF_NOPICMIP|IF_NOMIPMAP|IF_NOWORKER);\n\n\tshader_draw_fill = R_RegisterShader(\"fill_opaque\", SUF_NONE,\n\t\t\"{\\n\"\n\t\t\t\"program defaultfill\\n\"\n\t\t\t\"{\\n\"\n\t\t\t\t\"map $whiteimage\\n\"\n\t\t\t\t\"rgbgen exactvertex\\n\"\n\t\t\t\t\"alphagen vertex\\n\"\n\t\t\t\"}\\n\"\n\t\t\"}\\n\");\n\tshader_draw_fill_trans = R_RegisterShader(\"fill_trans\", SUF_NONE,\n\t\t\"{\\n\"\n\t\t\t\"program defaultfill\\n\"\n\t\t\t\"{\\n\"\n\t\t\t\t\"map $whiteimage\\n\"\n\t\t\t\t\"rgbgen vertex\\n\"\n\t\t\t\t\"alphagen vertex\\n\"\n\t\t\t\t\"blendfunc blend\\n\"\n\t\t\t\t\"maskalpha\\n\"\n\t\t\t\"}\\n\"\n\t\t\"}\\n\");\n\tshader_contrastup = R_RegisterShader(\"contrastupshader\", SUF_NONE,\n\t\t\"{\\n\"\n\t\t\t\"program defaultfill\\n\"\n\t\t\t\"{\\n\"\n\t\t\t\t\"nodepthtest\\n\"\n\t\t\t\t\"map $whiteimage\\n\"\n\t\t\t\t\"blendfunc gl_dst_color gl_one\\n\"\n\t\t\t\t\"rgbgen vertex\\n\"\n\t\t\t\t\"alphagen vertex\\n\"\n\t\t\t\t\"maskalpha\\n\"\n\t\t\t\"}\\n\"\n\t\t\"}\\n\"\n\t);\n\tshader_contrastdown = R_RegisterShader(\"contrastdownshader\", SUF_NONE,\n\t\t\"{\\n\"\n\t\t\t\"program defaultfill\\n\"\n\t\t\t\"{\\n\"\n\t\t\t\t\"nodepthtest\\n\"\n\t\t\t\t\"map $whiteimage\\n\"\n\t\t\t\t\"blendfunc gl_dst_color gl_zero\\n\"\n\t\t\t\t\"rgbgen vertex\\n\"\n\t\t\t\t\"alphagen vertex\\n\"\n\t\t\t\t\"maskalpha\\n\"\n\t\t\t\"}\\n\"\n\t\t\"}\\n\"\n\t);\n\tshader_brightness = R_RegisterShader(\"brightnessshader\", SUF_NONE,\n\t\t\"{\\n\"\n\t\t\t\"program defaultfill\\n\"\n\t\t\t\"{\\n\"\n\t\t\t\t\"nodepthtest\\n\"\n\t\t\t\t\"map $whiteimage\\n\"\n\t\t\t\t\"blendfunc gl_one gl_one\\n\"\n\t\t\t\t\"rgbgen vertex\\n\"\n\t\t\t\t\"alphagen vertex\\n\"\n\t\t\t\t\"maskalpha\\n\"\n\t\t\t\"}\\n\"\n\t\t\"}\\n\"\n\t);\n\tshader_gammacb = R_RegisterShader(\"gammacbshader\", SUF_NONE,\n\t\t\"{\\n\"\n\t\t\t\"program defaultgammacb\\n\"\n\t\t\t\"affine\\n\"\n\t\t\t\"{\\n\"\n\t\t\t\t\"map $currentrender\\n\"\n\t\t\t\t\"nodepthtest\\n\"\n\t\t\t\t\"maskalpha\\n\"\n\t\t\t\"}\\n\"\n\t\t\"}\\n\"\n\t);\n\tshader_polyblend = R_RegisterShader(\"polyblendshader\", SUF_NONE,\n\t\t\"{\\n\"\n\t\t\t\"program defaultfill\\n\"\n\t\t\t\"{\\n\"\n\t\t\t\t\"map $whiteimage\\n\"\n\t\t\t\t\"blendfunc gl_src_alpha gl_one_minus_src_alpha\\n\"\n\t\t\t\t\"rgbgen vertex\\n\"\n\t\t\t\t\"alphagen vertex\\n\"\n\t\t\t\t\"maskalpha\\n\"\n\t\t\t\"}\\n\"\n\t\t\"}\\n\"\n\t);\n\tshader_menutint = R_RegisterShader(\"menutint\", SUF_NONE,\n\t\t\"{\\n\"\n\t\t\t\"affine\\n\"\n#ifdef FTE_TARGET_WEB\n\t\t\t\t//currentrender is problematic here, so avoid using it.\n\t\t\t\t\"program default2d\\n\"\n\t\t\t\t\"{\\n\"\n\t\t\t\t\t\"map $whiteimage\\n\"\n\t\t\t\t\t\"blendfunc gl_dst_color gl_zero\\n\"\n\t\t\t\t\t\"rgbgen srgb $r_menutint\\n\"\n\t\t\t\t\"}\\n\"\n#else\n\t\t\t\"if gl_menutint_shader != 0\\n\"\n\t\t\t\t\"program menutint\\n\"\n\t\t\t\"endif\\n\"\n\t\t\t\"if $haveprogram\\n\"\n\t\t\t\t\"{\\n\"\n\t\t\t\t\t\"map $currentrender\\n\"\n\t\t\t\t\"}\\n\"\n\t\t\t\"else\\n\"\n\t\t\t\t\"{\\n\"\n\t\t\t\t\t\"map $whiteimage\\n\"\n\t\t\t\t\t\"blendfunc gl_dst_color gl_zero\\n\"\n\t\t\t\t\t\"rgbgen srgb $r_menutint\\n\"\n\t\t\t\t\"}\\n\"\n\t\t\t\"endif\\n\"\n#endif\n\t\t\"}\\n\"\n\t);\n\tshader_crosshair = R_RegisterShader(\"crosshairshader\", SUF_NONE,\n\t\t\"{\\n\"\n\t\t\t\"if $nofixed\\n\"\n\t\t\t\t\"program default2d\\n\"\n\t\t\t\"endif\\n\"\n\t\t\t\"affine\\n\"\n\t\t\t\"nomipmaps\\n\"\n\t\t\t\"{\\n\"\n\t\t\t\t\"map $diffuse\\n\"\n\t\t\t\t\"blendfunc blend\\n\"\n\t\t\t\t\"rgbgen vertex\\n\"\n\t\t\t\t\"alphagen vertex\\n\"\n\t\t\t\"}\\n\"\n\t\t\"}\\n\"\n\t\t);\n\n\n\tCvar_Hook(&con_textfont, R2D_Font_Callback);\n\tCvar_Hook(&gl_font, R2D_Font_Callback);\n\tCvar_Hook(&r_font_postprocess_outline, R2D_Font_Callback);\n\tCvar_Hook(&r_font_postprocess_mono, R2D_Font_Callback);\n\tCvar_Hook(&vid_conautoscale, R2D_Conautoscale_Callback);\n\tCvar_Hook(&gl_screenangle, R2D_ScreenAngle_Callback);\n\tCvar_Hook(&vid_conheight, R2D_Conheight_Callback);\n\tCvar_Hook(&vid_conwidth, R2D_Conwidth_Callback);\n\tCvar_Hook(&vid_baseheight, R2D_Conautoscale_Callback);\n\tCvar_Hook(&vid_minsize, R2D_Conautoscale_Callback);\n\n\tCvar_Hook(&crosshair, R2D_Crosshair_Callback);\n\tCvar_Hook(&crosshairimage, R2D_CrosshairImage_Callback);\n\tCvar_Hook(&crosshaircolor, R2D_CrosshairColor_Callback);\n\n\tCvar_ForceCallback(&gl_conback);\n\tCvar_ForceCallback(&vid_conautoscale);\n\tCvar_ForceCallback(&gl_font);\n\n\tCvar_ForceCallback(&crosshair);\n\tCvar_ForceCallback(&crosshaircolor);\n\n\tR2D_Font_Changed();\n\n\tR_NetgraphInit();\n\n\tatlas.lastid = -1;\n\tatlas.shader = NULL;\n\tatlas.data = NULL;\n\tatlas.dirty = false;\n\tMod_LightmapAllocInit(&atlas.allocation, false, min(512, sh_config.texture2d_maxsize), min(512, sh_config.texture2d_maxsize), 0);\n}\n\nmpic_t\t*R2D_SafeCachePic (const char *path)\n{\n\tshader_t *s;\n\tif (!qrenderer)\n\t\treturn NULL;\n\ts = R_RegisterPic(path, NULL);\n\treturn s;\n}\n\n\nmpic_t *R2D_SafePicFromWad (const char *name)\n{\n\tshader_t *s;\n\tif (!qrenderer || strchr(name, ':'))\n\t\treturn NULL;\n\ts = R_RegisterCustom (NULL, va(\"gfx/%s\", name), SUF_2D, Shader_Default2D, \"wad\");\n\treturn s;\n}\n\napic_t *R2D_LoadAtlasedPic(const char *name)\n{\n\tapic_t *apic = Z_Malloc(sizeof(*apic));\n\tqpic_t *qp;\n\tint x,y;\n\tqbyte *indata = NULL;\n\tint atlasid;\n\n\tif (!gl_load24bit.ival)\n\t{\n\t\tsize_t lumpsize;\n\t\tqbyte lumptype;\n\t\tqp = W_GetLumpName(name, &lumpsize, &lumptype);\n\t\tif (qp && lumptype == TYP_QPIC && lumpsize == 8+qp->width*qp->height)\n\t\t{\n\t\t\tapic->width = qp->width;\n\t\t\tapic->height = qp->height;\n\t\t\tindata = qp->data;\n\t\t}\n\t}\n\t\n\tif (!indata || apic->width > atlas.allocation.width || apic->height > atlas.allocation.height)\n\t\tatlasid = -1;\t//can happen on voodoo cards\n\telse\n\t\tMod_LightmapAllocBlock(&atlas.allocation, apic->width+2, apic->height+2, &apic->x, &apic->y, &atlasid);\n\n\tif (atlasid >= 0)\n\t{\n\t\tunsigned int *out;\n\t\tapic->x += 1;\n\t\tapic->y += 1;\n\n\t\t//FIXME: extend the atlas height instead, and keep going with a single atlas?\n\t\tif (atlasid != atlas.lastid)\n\t\t{\n\t\t\tatlas.lastid = atlasid;\n\t\t\tif (atlas.dirty)\n\t\t\t\tImage_Upload(atlas.tex, atlas.fmt, atlas.data, NULL, atlas.allocation.width, atlas.allocation.height, 1, IF_NOMIPMAP);\n\t\t\tatlas.tex = r_nulltex;\n\t\t\tatlas.fmt = sh_config.texfmt[PTI_BGRA8]?PTI_BGRA8:PTI_RGBA8;\n\t\t\tatlas.shader = NULL;\n\t\t\tatlas.dirty = false;\n\t\t\tif (atlas.data)\t//clear atlas data instead of reallocating it.\n\t\t\t\tmemset(atlas.data, 0, sizeof(*atlas.data) * atlas.allocation.width * atlas.allocation.height);\n\t\t}\n\n\t\tif (!atlas.tex)\n\t\t\tatlas.tex = Image_CreateTexture(va(\"fte_atlas%i\", atlasid), NULL, IF_NOMIPMAP|IF_NOMIPMAP);\n\t\tif (!atlas.shader)\n\t\t{\n\t\t\tatlas.shader = R_RegisterShader(va(\"fte_atlas%i\", atlasid), SUF_NONE,\n\t\t\t\t\"{\\n\"\n\t\t\t\t\t\"affine\\n\"\n\t\t\t\t\t\"program default2d\\n\"\n\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\"map $diffuse\\n\"\n\t\t\t\t\t\t\"rgbgen vertex\\n\"\n\t\t\t\t\t\t\"alphagen vertex\\n\"\n\t\t\t\t\t\t\"blendfunc gl_one gl_one_minus_src_alpha\\n\"\n\t\t\t\t\t\"}\\n\"\n\t\t\t\t\"}\\n\"\n\t\t\t);\n\t\t\tatlas.shader->defaulttextures->base = atlas.tex;\n\t\t}\n\n\t\tif (!atlas.data)\n\t\t\tatlas.data = Z_Malloc(sizeof(*atlas.data) * atlas.allocation.width * atlas.allocation.height);\n\n\t\tout = atlas.data;\n\t\tout += apic->x;\n\t\tout += apic->y * atlas.allocation.width;\n\t\tapic->atlas = atlas.shader;\n\n\t\tif (atlas.fmt == PTI_BGRA8)\n\t\t{\n\t\t\t//pad above. extra casts because 64bit msvc is RETARDED.\n\t\t\tout[-1 - (qintptr_t)atlas.allocation.width] = (indata[0] == 255)?0:d_8to24bgrtable[indata[0]];\t//pad left\n\t\t\tfor (x = 0; x < apic->width; x++)\n\t\t\t\tout[x-(qintptr_t)atlas.allocation.width] = (indata[x] == 255)?0:d_8to24bgrtable[indata[x]];\n\t\t\tout[x - (qintptr_t)atlas.allocation.width] = (indata[x-1] == 255)?0:d_8to24bgrtable[indata[x-1]];\t//pad right\n\t\t\tfor (y = 0; y < apic->height; y++)\n\t\t\t{\n\t\t\t\tout[-1] = (indata[0] == 255)?0:d_8to24bgrtable[indata[0]];\t//pad left\n\t\t\t\tfor (x = 0; x < apic->width; x++)\n\t\t\t\t\tout[x] = (indata[x] == 255)?0:d_8to24bgrtable[indata[x]];\n\t\t\t\tout[x] = (indata[x-1] == 255)?0:d_8to24bgrtable[indata[x-1]];\t//pad right\n\t\t\t\tindata += x;\n\t\t\t\tout += atlas.allocation.width;\n\t\t\t}\n\t\t\t//pad below\n\t\t\tout[-1] = (indata[0] == 255)?0:d_8to24bgrtable[indata[0]];\t//pad left\n\t\t\tfor (x = 0; x < apic->width; x++)\n\t\t\t\tout[x] = (indata[x] == 255)?0:d_8to24bgrtable[indata[x]];\n\t\t\tout[x] = (indata[x-1] == 255)?0:d_8to24bgrtable[indata[x-1]];\t//pad right\n\t\t}\n\t\telse\n\t\t{\n\t\t\t//pad above. extra casts because 64bit msvc is RETARDED.\n\t\t\tout[-1 - (qintptr_t)atlas.allocation.width] = (indata[0] == 255)?0:d_8to24rgbtable[indata[0]];\t//pad left\n\t\t\tfor (x = 0; x < apic->width; x++)\n\t\t\t\tout[x-(qintptr_t)atlas.allocation.width] = (indata[x] == 255)?0:d_8to24rgbtable[indata[x]];\n\t\t\tout[x - (qintptr_t)atlas.allocation.width] = (indata[x-1] == 255)?0:d_8to24rgbtable[indata[x-1]];\t//pad right\n\t\t\tfor (y = 0; y < apic->height; y++)\n\t\t\t{\n\t\t\t\tout[-1] = (indata[0] == 255)?0:d_8to24rgbtable[indata[0]];\t//pad left\n\t\t\t\tfor (x = 0; x < apic->width; x++)\n\t\t\t\t\tout[x] = (indata[x] == 255)?0:d_8to24rgbtable[indata[x]];\n\t\t\t\tout[x] = (indata[x-1] == 255)?0:d_8to24rgbtable[indata[x-1]];\t//pad right\n\t\t\t\tindata += x;\n\t\t\t\tout += atlas.allocation.width;\n\t\t\t}\n\t\t\t//pad below\n\t\t\tout[-1] = (indata[0] == 255)?0:d_8to24rgbtable[indata[0]];\t//pad left\n\t\t\tfor (x = 0; x < apic->width; x++)\n\t\t\t\tout[x] = (indata[x] == 255)?0:d_8to24rgbtable[indata[x]];\n\t\t\tout[x] = (indata[x-1] == 255)?0:d_8to24rgbtable[indata[x-1]];\t//pad right\n\t\t}\n\n\t\t//pinch inwards, for linear sampling\n\t\tapic->sl = (apic->x+0.5)/(float)atlas.allocation.width;\n\t\tapic->sh = (apic->width-1.0)/(float)atlas.allocation.width;\n\n\t\tapic->tl = (apic->y+0.5)/(float)atlas.allocation.height;\n\t\tapic->th = (apic->height-1.0)/(float)atlas.allocation.height;\n\n\t\tatlas.dirty = true;\n\n\t\tapic->next = atlas.pics;\n\t\tatlas.pics = apic;\n\t}\n\telse if (1)\n\t{\n\t\tapic->atlas = R_RegisterPic(va(\"gfx/%s\", name), \"wad\");\n\t\tapic->sl = 0;\n\t\tapic->sh = 1;\n\t\tapic->tl = 0;\n\t\tapic->th = 1;\n\n\t\tapic->next = atlas.pics;\n\t\tatlas.pics = apic;\n\t}\n\telse\n\t{\n\t\tZ_Free(apic);\n\t\treturn NULL;\n\t}\n\treturn apic;\n}\n\nvoid R2D_ImageColours(float r, float g, float b, float a)\n{\n\tdraw_active_colour[0] = r;\n\tdraw_active_colour[1] = g;\n\tdraw_active_colour[2] = b;\n\tdraw_active_colour[3] = a;\n\n\tFont_InvalidateColour(draw_active_colour);\n}\nvoid R2D_ImagePaletteColour(unsigned int i, float a)\n{\n\tdraw_active_colour[0] = SRGBf(host_basepal[i*3+0]/255.0);\n\tdraw_active_colour[1] = SRGBf(host_basepal[i*3+1]/255.0);\n\tdraw_active_colour[2] = SRGBf(host_basepal[i*3+2]/255.0);\n\tdraw_active_colour[3] = a;\n\n\tFont_InvalidateColour(draw_active_colour);\n}\n\n//awkward and weird to use\nvoid R2D_ImageAtlas(float x, float y, float w, float h, float s1, float t1, float s2, float t2, apic_t *pic)\n{\n\tfloat newsl, newsh, newtl, newth;\n\tif (!pic)\n\t\treturn;\n\tif (atlas.dirty)\n\t{\n\t\tImage_Upload(atlas.tex, atlas.fmt, atlas.data, NULL, atlas.allocation.width, atlas.allocation.height, 1, IF_NOMIPMAP);\n\t\tatlas.dirty = false;\n\t}\n\n\tnewsl = pic->sl + s1 * pic->sh;\n\tnewsh = newsl + (s2-s1) * pic->sh;\n\n\tnewtl = pic->tl + t1 * pic->th;\n\tnewth = newtl + (t2-t1) * pic->th;\n\n\tR2D_Image(x, y, w, h, newsl, newtl, newsh, newth, pic->atlas);\n}\n\nvoid R2D_ImageFlush(void)\n{\n\tBE_DrawMesh_Single(draw_active_shader, &draw_mesh, NULL, draw_active_flags);\n\n\tR2D_Flush = NULL;\n\tdraw_active_shader = NULL;\n}\n//awkward and weird to use\nvoid R2D_Image(float x, float y, float w, float h, float s1, float t1, float s2, float t2, mpic_t *pic)\n{\n\tint i;\n\tif (!pic)\n\t\treturn;\n\n\t//don't draw pics if they have an image which is still loading.\n\tfor (i = 0; i < pic->numpasses; i++)\n\t{\n\t\tif (pic->passes[i].texgen == T_GEN_SINGLEMAP && pic->passes[i].anim_frames[0] && pic->passes[i].anim_frames[0]->status == TEX_LOADING)\n\t\t\treturn;\n\t\tif (pic->passes[i].texgen == T_GEN_DIFFUSE && pic->defaulttextures->base && pic->defaulttextures->base->status == TEX_LOADING)\n\t\t\treturn;\n\t}\n\n\tif (draw_active_shader != pic || draw_active_flags != r2d_be_flags || R2D_Flush != R2D_ImageFlush || draw_mesh.numvertexes+4 > DRAW_QUADS)\n\t{\n\t\tif (R2D_Flush)\n\t\t\tR2D_Flush();\n\n\t\tdraw_active_shader = pic;\n\t\tdraw_active_flags = r2d_be_flags;\n\t\tR2D_Flush = R2D_ImageFlush;\n\n\t\tdraw_mesh.numindexes = 0;\n\t\tdraw_mesh.numvertexes = 0;\n\t}\n\n\tVector2Set(draw_mesh_xyz[draw_mesh.numvertexes+0],\tx, y);\n\tVector2Set(draw_mesh_st[draw_mesh.numvertexes+0],\ts1, t1);\n\tVector4Copy(draw_active_colour, draw_mesh_colors[draw_mesh.numvertexes+0]);\n\n\tVector2Set(draw_mesh_xyz[draw_mesh.numvertexes+1],\tx+w, y);\n\tVector2Set(draw_mesh_st[draw_mesh.numvertexes+1],\ts2, t1);\n\tVector4Copy(draw_active_colour, draw_mesh_colors[draw_mesh.numvertexes+1]);\n\n\tVector2Set(draw_mesh_xyz[draw_mesh.numvertexes+2],\tx+w, y+h);\n\tVector2Set(draw_mesh_st[draw_mesh.numvertexes+2],\ts2, t2);\n\tVector4Copy(draw_active_colour, draw_mesh_colors[draw_mesh.numvertexes+2]);\n\n\tVector2Set(draw_mesh_xyz[draw_mesh.numvertexes+3],\tx, y+h);\n\tVector2Set(draw_mesh_st[draw_mesh.numvertexes+3],\ts1, t2);\n\tVector4Copy(draw_active_colour, draw_mesh_colors[draw_mesh.numvertexes+3]);\n\n\tdraw_mesh.numvertexes += 4;\n\tdraw_mesh.numindexes += 6;\n}\nvoid R2D_Image2dQuad(vec2_t const*points, vec2_t const*texcoords, vec4_t const*rgba, mpic_t *pic)\n{\n\tint i;\n\tif (!pic)\n\t\treturn;\n\n\t//don't draw pics if they have an image which is still loading.\n\tfor (i = 0; i < pic->numpasses; i++)\n\t{\n\t\tif (pic->passes[i].texgen == T_GEN_SINGLEMAP && pic->passes[i].anim_frames[0] && pic->passes[i].anim_frames[0]->status == TEX_LOADING)\n\t\t\treturn;\n\t\tif (pic->passes[i].texgen == T_GEN_DIFFUSE && pic->defaulttextures->base && pic->defaulttextures->base->status == TEX_LOADING)\n\t\t\treturn;\n\t}\n\n\tif (draw_active_shader != pic || draw_active_flags != r2d_be_flags || R2D_Flush != R2D_ImageFlush || draw_mesh.numvertexes+4 > DRAW_QUADS)\n\t{\n\t\tif (R2D_Flush)\n\t\t\tR2D_Flush();\n\n\t\tdraw_active_shader = pic;\n\t\tdraw_active_flags = r2d_be_flags;\n\t\tR2D_Flush = R2D_ImageFlush;\n\n\t\tdraw_mesh.numindexes = 0;\n\t\tdraw_mesh.numvertexes = 0;\n\t}\n\n\tfor (i = 0; i < 4; i++)\n\t{\n\t\tVector2Copy(points[i], draw_mesh_xyz[draw_mesh.numvertexes+i]);\n\t\tVector2Copy(texcoords[i], draw_mesh_st[draw_mesh.numvertexes+i]);\n\t\tif (rgba)\n\t\t\tVector4Copy(rgba[i], draw_mesh_colors[draw_mesh.numvertexes+i]);\n\t\telse\n\t\t\tVector4Copy(draw_active_colour, draw_mesh_colors[draw_mesh.numvertexes+i]);\n\t}\n\n\tdraw_mesh.numvertexes += 4;\n\tdraw_mesh.numindexes += 6;\n}\n\n/*draws a block of the current colour on the screen*/\nvoid R2D_FillBlock(float x, float y, float w, float h)\n{\n\tmpic_t *pic;\n\tif (draw_active_colour[3] != 1)\n\t\tpic = shader_draw_fill_trans;\n\telse\n\t\tpic = shader_draw_fill;\n\tif (draw_active_shader != pic || draw_active_flags != r2d_be_flags || R2D_Flush != R2D_ImageFlush || draw_mesh.numvertexes+4 > DRAW_QUADS)\n\t{\n\t\tif (R2D_Flush)\n\t\t\tR2D_Flush();\n\n\t\tdraw_active_shader = pic;\n\t\tdraw_active_flags = r2d_be_flags;\n\t\tR2D_Flush = R2D_ImageFlush;\n\n\t\tdraw_mesh.numindexes = 0;\n\t\tdraw_mesh.numvertexes = 0;\n\t}\n\n\tVector2Set(draw_mesh_xyz[draw_mesh.numvertexes+0], x, y);\n\tVector4Copy(draw_active_colour, draw_mesh_colors[draw_mesh.numvertexes+0]);\n\n\tVector2Set(draw_mesh_xyz[draw_mesh.numvertexes+1], x+w, y);\n\tVector4Copy(draw_active_colour, draw_mesh_colors[draw_mesh.numvertexes+1]);\n\n\tVector2Set(draw_mesh_xyz[draw_mesh.numvertexes+2], x+w, y+h);\n\tVector4Copy(draw_active_colour, draw_mesh_colors[draw_mesh.numvertexes+2]);\n\n\tVector2Set(draw_mesh_xyz[draw_mesh.numvertexes+3], x, y+h);\n\tVector4Copy(draw_active_colour, draw_mesh_colors[draw_mesh.numvertexes+3]);\n\n\tdraw_mesh.numvertexes += 4;\n\tdraw_mesh.numindexes += 6;\n}\n\nvoid R2D_Line(float x1, float y1, float x2, float y2, shader_t *shader)\n{\n\tif (R2D_Flush)\n\t{\n\t\tR2D_Flush();\n\t\tR2D_Flush = NULL;\n\t}\n\n\tVectorSet(draw_mesh_xyz[0], x1, y1, 0);\n\tVector2Set(draw_mesh_st[0], 0, 0);\n\tVector4Copy(draw_active_colour, draw_mesh_colors[0]);\n\n\tVectorSet(draw_mesh_xyz[1], x2, y2, 0);\n\tVector2Set(draw_mesh_st[1], 1, 0);\n\tVector4Copy(draw_active_colour, draw_mesh_colors[1]);\n\n\tif (!shader)\n\t{\n\t\tif (draw_active_colour[3] != 1)\n\t\t\tshader = shader_draw_fill_trans;\n\t\telse\n\t\t\tshader = shader_draw_fill;\n\t}\n\n\tdraw_mesh.numvertexes = 2;\n\tdraw_mesh.numindexes = 2;\n\tBE_DrawMesh_Single(shader, &draw_mesh, NULL, BEF_LINES);\n}\n\nvoid R2D_ScalePic (float x, float y, float width, float height, mpic_t *pic)\n{\n\tR2D_Image(x, y, width, height, 0, 0, 1, 1, pic);\n}\n\nvoid R2D_SubPic(float x, float y, float width, float height, mpic_t *pic, float srcx, float srcy, float srcwidth, float srcheight)\n{\n\tfloat newsl, newtl, newsh, newth;\n\n\tnewsl = (srcx)/(float)srcwidth;\n\tnewsh = newsl + (width)/(float)srcwidth;\n\n\tnewtl = (srcy)/(float)srcheight;\n\tnewth = newtl + (height)/(float)srcheight;\n\n\tR2D_Image(x, y, width, height, newsl, newtl, newsh, newth, pic);\n}\n\nvoid R2D_Letterbox(float sx, float sy, float sw, float sh, mpic_t *pic, float pw, float ph)\n{\n\tfloat ratiox = (float)pw / sw;\n\tfloat ratioy = (float)ph / sh;\n\n\tif (pw<=0 || ph<=0)\n\t{\t//no size info...\n\t\tR2D_ImageColours(0, 0, 0, 1);\n\t\tR2D_FillBlock(sx, sy, sw, sh);\n\t\tR2D_ScalePic(sx, sy, 0, 0, pic);\t//in case its a videoshader with audio\n\t}\n\telse if (ratiox > ratioy)\n\t{\t//x ratio is greatest\n\t\tfloat h = (sw * ph) / pw;\n\t\tfloat p = sh - h;\n\n\t\t//letterbox\n\t\tR2D_ImageColours(0, 0, 0, 1);\n\t\tR2D_FillBlock(sx, sy, sw, p/2);\n\t\tR2D_FillBlock(sx, sy + h + (p/2), sw, p/2);\n\n\t\tR2D_ImageColours(1, 1, 1, 1);\n\t\tR2D_ScalePic(sx, sy + p/2, sw, h, pic);\n\t}\n\telse\n\t{\t//y ratio is greatest\n\t\tfloat w = (sh * pw) / ph;\n\t\tfloat p = sw - w;\n\n\t\t//sidethingies\n\t\tR2D_ImageColours(0, 0, 0, 1);\n\t\tR2D_FillBlock(sx, sy, (p/2), sh);\n\t\tR2D_FillBlock(sx + w + (p/2), sy, p/2, sh);\n\n\t\tR2D_ImageColours(1, 1, 1, 1);\n\t\tR2D_ScalePic(sx + p/2, sy, w, sh, pic);\n\t}\n}\n\n/* this is an ugly special case drawing func that's only used for the player color selection menu */\nvoid R2D_TransPicTranslate (float x, float y, int width, int height, qbyte *pic, unsigned int *palette)\n{\n\tint\t\t\t\tv, u;\n\tunsigned\t\ttrans[64*64], *dest;\n\tqbyte\t\t\t*src;\n\n\tdest = trans;\n\tfor (v=0 ; v<64 ; v++, dest += 64)\n\t{\n\t\tsrc = &pic[ ((v*height)>>6) *width];\n\t\tfor (u=0 ; u<64 ; u++)\n\t\t\tdest[u] = palette[src[(u*width)>>6]];\n\t}\n\n\tif (!TEXVALID(translate_texture))\n\t{\n\t\ttranslate_texture = Image_CreateTexture(\"***translatedpic***\", NULL, IF_UIPIC|IF_NOMIPMAP|IF_NOGAMMA);\n\t\ttranslate_shader = R_RegisterShader(\"translatedpic\", SUF_2D,\n\t\t\t\"{\\n\"\n\t\t\t\t\"if $nofixed\\n\"\n\t\t\t\t\t\"program default2d\\n\"\n\t\t\t\t\"endif\\n\"\n\t\t\t\t\"nomipmaps\\n\"\n\t\t\t\t\"{\\n\"\n\t\t\t\t\t\"map $diffuse\\n\"\n\t\t\t\t\t\"blendfunc blend\\n\"\n\t\t\t\t\t\"rgbgen vertex\\n\"\n\t\t\t\t\t\"alphagen vertex\\n\"\n\t\t\t\t\"}\\n\"\n\t\t\t\"}\\n\");\n\t\ttranslate_shader->defaulttextures->base = translate_texture;\n\t}\n\t/* could avoid reuploading already translated textures but this func really isn't used enough anyway */\n\tImage_Upload(translate_texture, TF_RGBA32, trans, NULL, 64, 64, 1, IF_UIPIC|IF_NOMIPMAP|IF_NOGAMMA);\n\tR2D_ScalePic(x, y, width, height, translate_shader);\n}\n\n/*\n================\nDraw_ConsoleBackground\n\n================\n*/\nvoid R2D_ConsoleBackground (int firstline, int lastline, qboolean forceopaque)\n{\n\tfloat a;\n\tint w, h;\n\n\tw = vid.width;\n\th = vid.height;\n\n\tif (forceopaque)\n\t{\n\t\ta = 1; // console background is necessary\n\t}\n\telse\n\t{\n\t\tif (!scr_conalpha.value)\n\t\t\treturn;\n\n\t\ta = scr_conalpha.value;\n\t}\n\n\tif (R_GetShaderSizes(conback, NULL, NULL, false) <= 0)\n\t{\n\t\tR2D_ImageColours(0, 0, 0, a);\n\t\tR2D_FillBlock(0, lastline-(int)vid.height, w, h);\n\t\tR2D_ImageColours(1, 1, 1, 1);\n\t}\n\telse if (a >= 1)\n\t{\n\t\tR2D_ImageColours(1, 1, 1, 1);\n\t\tR2D_ScalePic(0, lastline-(int)vid.height, w, h, conback);\n\t}\n\telse\n\t{\n\t\tR2D_ImageColours(1, 1, 1, a);\n\t\tR2D_ScalePic (0, lastline - (int)vid.height, w, h, conback);\n\t\tR2D_ImageColours(1, 1, 1, 1);\n\t}\n}\n\nvoid R2D_EditorBackground (void)\n{\n\tR2D_ImageColours(0, 0, 0, 1);\n\tR2D_FillBlock(0, 0, vid.width, vid.height);\n//\tR2D_ScalePic(0, 0, vid.width, vid.height, conback);\n}\n\n/*\n=============\nDraw_TileClear\n\nThis repeats a 64*64 tile graphic to fill the screen around a sized down\nrefresh window.\n=============\n*/\nvoid R2D_TileClear (float x, float y, float w, float h)\n{\n\tfloat newsl, newsh, newtl, newth;\n\tnewsl = (x)/(float)64;\n\tnewsh = newsl + (w)/(float)64;\n\n\tnewtl = (y)/(float)64;\n\tnewth = newtl + (h)/(float)64;\n\n\tR2D_ImageColours(1,1,1,1);\n\n\tR2D_Image(x, y, w, h, newsl, newtl, newsh, newth, draw_backtile);\n}\n\nvoid QDECL R2D_Conback_Callback(struct cvar_s *var, char *oldvalue)\n{\n\tif (qrenderer == QR_NONE || !strcmp(var->string, \"none\"))\n\t{\n\t\tconback = NULL;\n\t\treturn;\n\t}\n\n\tif (*var->string)\n\t\tconback = R_RegisterPic(var->string, NULL);\n#ifdef HAVE_LEGACY\n\telse if (Cvar_FindVar(\"scr_conalphafactor\"))\n\t{\t//dp bullshit\n\t\tconback = R_RegisterShader(\"gfx/conback\", SUF_2D,\n\t\t\t\"{\\n\"\n\t\t\t\t\"nomipmaps\\n\"\n\t\t\t\t\"{\\n\"\n\t\t\t\t\t\"map gfx/conback\\n\"\n\t\t\t\t\t\"rgbgen const $scr_conbrightness\\n\"\n\t\t\t\t\t\"alphagen const $scr_conalphafactor\\n\"\n\t\t\t\t\t\"tcmod scroll $scr_conscroll_x $scr_conscroll_y\\n\"\n\t\t\t\t\t\"blendfunc blend\\n\"\n\t\t\t\t\"}\\n\"\n\t\t\t\t\"{\\n\"\n\t\t\t\t\t\"map gfx/conback2\\n\"\n\t\t\t\t\t\"rgbgen const $scr_conbrightness\\n\"\n\t\t\t\t\t\"alphagen const $scr_conalpha2factor\\n\"\n\t\t\t\t\t\"tcmod scroll $scr_conscroll2_x $scr_conscroll2_y\\n\"\n\t\t\t\t\t\"blendfunc blend\\n\"\n\t\t\t\t\"}\\n\"\n\t\t\t\t\"{\\n\"\n\t\t\t\t\t\"map gfx/conback3\\n\"\n\t\t\t\t\t\"rgbgen const $scr_conbrightness\\n\"\n\t\t\t\t\t\"alphagen const $scr_conalpha3factor\\n\"\n\t\t\t\t\t\"tcmod scroll $scr_conscroll3_x $scr_conscroll3_y\\n\"\n\t\t\t\t\t\"blendfunc blend\\n\"\n\t\t\t\t\"}\\n\"\n\t\t\t\"}\\n\");\n\t}\n#endif\n\n\tif (!R_GetShaderSizes(conback, NULL, NULL, true))\n\t{\n\t\tconback = R_RegisterCustom(NULL, \"console\", SUF_2D, NULL, NULL);\t//quake3\n\t\tif (!R_GetShaderSizes(conback, NULL, NULL, true))\n\t\t{\n#ifdef HEXEN2\n\t\t\tif (M_GameType() == MGT_HEXEN2)\n\t\t\t\tconback = R_RegisterPic(\"gfx/menu/conback.lmp\", NULL);\n\t\t\telse\n#endif\n\t\t\t\tif (M_GameType() == MGT_QUAKE2)\n\t\t\t\tconback = R_RegisterPic(\"pics/conback.pcx\", NULL);\n\t\t\telse\n\t\t\t\tconback = R_RegisterPic(\"gfx/conback.lmp\", NULL);\n\t\t}\n\t}\n}\n\n#ifdef AVAIL_FREETYPE\n#if defined(LIBFONTCONFIG_STATIC)\n#include <fontconfig/fontconfig.h>\nstatic int QDECL SortCompareFonts(const void *av, const void *bv)\n{\t//qsort compare\n\tconst FcPattern *af = *(FcPattern *const*const)av, *bf = *(FcPattern *const*const)bv;\n\tFcChar8 *as, *bs;\n\tint r = 0;\n\tif (FcPatternGetString(af, FC_FAMILY, 0, &as) == FcResultMatch && FcPatternGetString(bf, FC_FAMILY, 0, &bs) == FcResultMatch)\n\t{\n\t\tr = strcmp(as, bs);\n\t\tif (!r && FcPatternGetString(af, FC_STYLE, 0, &as) == FcResultMatch && FcPatternGetString(bf, FC_STYLE, 0, &bs) == FcResultMatch)\n\t\t\tr = strcmp(as, bs);\n\t}\n\treturn r;\n}\n#elif defined(_WIN32) && !defined(FTE_SDL) && !defined(WINRT) && !defined(_XBOX)\n#include <windows.h>\nqboolean R2D_Font_WasAdded(char *buffer, char *fontfilename)\n{\n\tchar *match;\n\tif (!fontfilename)\n\t\treturn true;\n\tmatch = strstr(buffer, fontfilename);\n\tif (!match)\n\t\treturn false;\n\tif (!(match == buffer || match[-1] == ','))\n\t\treturn false;\n\tmatch += strlen(fontfilename);\n\tif (*match && *match != ',')\n\t\treturn false;\n\treturn true;\n}\nextern qboolean\tWinNT;\nvoid R2D_Font_AddFontLink(char *buffer, int buffersize, char *fontname)\n{\n\tchar link[1024];\n\tchar *res, *comma, *othercomma, *nl;\n\tif (fontname)\n\tif (MyRegGetStringValueMultiSz(HKEY_LOCAL_MACHINE, WinNT?\"SOFTWARE\\\\Microsoft\\\\Windows NT\\\\CurrentVersion\\\\FontLink\\\\SystemLink\":\"SOFTWARE\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\FontLink\\\\SystemLink\", fontname, link, sizeof(link)))\n\t{\n\t\tres = nl = link;\n\t\twhile (*nl)\n\t\t{\n\t\t\tnl += strlen(nl);\n\t\t\tnl++;\n\t\t\tcomma = strchr(res, ',');\n\t\t\tif (comma)\n\t\t\t{\n\t\t\t\t*comma++ = 0;\n\t\t\t\tothercomma = strchr(comma, ',');\n\t\t\t\tif (othercomma)\n\t\t\t\t\t*othercomma = 0;\n\t\t\t}\n\t\t\telse\n\t\t\t\tcomma = \"\";\n\t\t\tif (!R2D_Font_WasAdded(buffer, res))\n\t\t\t{\n\t\t\t\tQ_strncatz(buffer, \",\", buffersize);\n\t\t\t\tQ_strncatz(buffer, res, buffersize);\n\t\t\t\tR2D_Font_AddFontLink(buffer, buffersize, comma);\n\t\t\t}\n\t\t\tres = nl;\n\t\t}\n\t}\n}\n#else\nint R2D_Font_ListSystemFonts(const char *fname, qofs_t fsize, time_t modtime, void *ctx, searchpathfuncs_t *spath)\n{\n\tchar tmp[MAX_QPATH];\n\tCOM_StripExtension(fname, tmp, sizeof(tmp));\n\tCon_Printf(\"^[/gl_font %s^]\\n\", tmp);\n\treturn true;\n}\n#endif\n#endif\nvoid R2D_Font_Changed(void)\n{\n\tfloat tsize;\n\tconst char *con_font_name = con_textfont.string;\n\tunsigned int flags;\n\tif (!con_textsize.modified)\n\t\treturn;\n\tif (!*con_font_name)\n\t\tcon_font_name = gl_font.string;\n\tcon_textsize.modified = false;\n\n\tif (con_textsize.value < 0)\n\t\ttsize = (-con_textsize.value * vid.height) / vid.pixelheight;\t//size defined in physical pixels\n\telse\n\t\ttsize = con_textsize.value;\t//size defined in virtual pixels.\n\tif (!tsize)\n\t\ttsize = 8;\n\n\tif (font_console == font_default)\n\t\tfont_console = NULL;\n\tif (font_console)\n\t\tFont_Free(font_console);\n\tfont_console = NULL;\n\tif (font_default)\n\t\tFont_Free(font_default);\n\tfont_default = NULL;\n\n\tif (font_tiny)\n\t\tFont_Free(font_tiny);\n\tfont_tiny = NULL; \n\n#if defined(MENU_DAT) || defined(CSQC_DAT)\n\tPR_ReloadFonts(true);\n#endif\n\n\tif (qrenderer == QR_NONE)\n\t\treturn;\n\n\tflags = 0;\n\tif (r_font_postprocess_mono.ival)\n\t\tflags |= FONT_MONO;\n\n\tif (!strcmp(gl_font.string, \"?\"))\n\t{\n#ifndef AVAIL_FREETYPE\n\t\tCvar_Set(&gl_font, \"\");\n#elif defined(LIBFONTCONFIG_STATIC)\n\t\tCvar_Set(&gl_font, \"\");\n\t\t{\n\t\t\tFcConfig *config = FcInitLoadConfigAndFonts();\n\t\t\tFcPattern *pat = FcPatternCreate();\n\t\t\tFcObjectSet *os = FcObjectSetBuild (FC_FAMILY, FC_STYLE, (char *) 0);\n\t\t\tFcFontSet *fs = FcFontList(config, pat, os);\n\n\t\t\tif (fs)\n\t\t\t{\n\t\t\t\tint i;\n\t\t\t\tFcChar8 *oldfam = NULL;\n\t\t\t\tFcPattern **fonts = BZ_Malloc(sizeof(*fonts)*fs->nfont);\n\t\t\t\tmemcpy(fonts, fs->fonts, sizeof(*fonts)*fs->nfont);\n\t\t\t\tqsort(fonts, fs->nfont, sizeof(*fonts), SortCompareFonts);\n\t\t\t\tfor (i=0; fs && i < fs->nfont; i++)\n\t\t\t\t{\n\t\t\t\t\tFcPattern *font = fonts[i];\n\t\t\t\t\tFcChar8 *style, *family;\n\t\t\t\t\tif (FcPatternGetString(font, FC_FAMILY, 0, &family) == FcResultMatch && FcPatternGetString(font, FC_STYLE, 0, &style) == FcResultMatch)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (!oldfam || strcmp(oldfam, family))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (oldfam)\n\t\t\t\t\t\t\t\tCon_Printf(\"\\n\");\n\t\t\t\t\t\t\toldfam = family;\n\t\t\t\t\t\t\tCon_Printf(\"^[\"S_COLOR_WHITE\"%s\\\\type\\\\/gl_font %s^]: \", family, family);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tCon_Printf(\" \\t^[%s\\\\type\\\\/gl_font %s?style=%s^]\", style, family, style);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (oldfam)\n\t\t\t\t\tCon_Printf(\"\\n\");\n\t\t\t\tBZ_Free(fonts);\n\t\t\t\tFcFontSetDestroy(fs);\n\t\t\t}\n\t\t\tFcObjectSetDestroy(os);\n\t\t\tFcPatternDestroy(pat);\n\t\t}\n#elif defined(_WIN32) && !defined(FTE_SDL) && !defined(WINRT) && !defined(_XBOX)\n\t\tBOOL (APIENTRY *pChooseFontW)(LPCHOOSEFONTW) = NULL;\n\t\tdllfunction_t funcs[] =\n\t\t{\n\t\t\t{(void*)&pChooseFontW, \"ChooseFontW\"},\n\t\t\t{NULL}\n\t\t};\n\t\tLOGFONTW lf = {0};\n\t\tCHOOSEFONTW cf = {sizeof(cf)};\n\t\textern HWND\tmainwindow;\n\t\tfont_default = Font_LoadFont(\"\", 8, 1, r_font_postprocess_outline.ival, flags);\n\t\tif (tsize != 8)\n\t\t\tfont_console = Font_LoadFont(\"\", tsize, 1, r_font_postprocess_outline.ival, flags);\n\t\tif (!font_console)\n\t\t\tfont_console = font_default;\n\n\t\tcf.hwndOwner = mainwindow;\n\t\tcf.iPointSize = (8 * vid.rotpixelheight)/vid.height;\n\t\tcf.Flags = CF_FORCEFONTEXIST | CF_TTONLY;\n\t\tcf.lpLogFont = &lf;\n\n\t\tSys_LoadLibrary(\"comdlg32.dll\", funcs);\n\n\t\tif (pChooseFontW && pChooseFontW(&cf))\n\t\t{\n\t\t\tchar fname[MAX_OSPATH*8];\n\t\t\tchar lfFaceName[MAX_OSPATH];\n\t\t\tchar *keyname;\n\t\t\tnarrowen(lfFaceName, sizeof(lfFaceName), lf.lfFaceName);\n\t\t\t*fname = 0;\n\t\t\t//FIXME: should enumerate and split & and ignore sizes and () crap.\n\t\t\tif (!*fname)\n\t\t\t{\n\t\t\t\tkeyname = va(\"%s%s%s (TrueType)\", lfFaceName, lf.lfWeight>=FW_BOLD?\" Bold\":\"\", lf.lfItalic?\" Italic\":\"\");\n\t\t\t\tif (!MyRegGetStringValue(HKEY_LOCAL_MACHINE, WinNT?\"SOFTWARE\\\\Microsoft\\\\Windows NT\\\\CurrentVersion\\\\Fonts\":\"SOFTWARE\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\Fonts\", keyname, fname, sizeof(fname)))\n\t\t\t\t\t*fname = 0;\n\t\t\t}\n\t\t\tif (!*fname)\n\t\t\t{\n\t\t\t\tkeyname = va(\"%s (OpenType)\", lfFaceName);\n\t\t\t\tif (!MyRegGetStringValue(HKEY_LOCAL_MACHINE, WinNT?\"SOFTWARE\\\\Microsoft\\\\Windows NT\\\\CurrentVersion\\\\Fonts\":\"SOFTWARE\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\Fonts\", keyname, fname, sizeof(fname)))\n\t\t\t\t\t*fname = 0;\n\t\t\t}\n\n\t\t\tR2D_Font_AddFontLink(fname, sizeof(fname), lfFaceName);\n\t\t\tCvar_Set(&gl_font, fname);\n\t\t}\n\t\treturn;\n#else\n\t\tSys_EnumerateFiles(\"/usr/share/fonts/truetype/\", \"*/*.ttf\", R2D_Font_ListSystemFonts, NULL, NULL);\n\t\tCOM_EnumerateFiles(\"*.ttf\", R2D_Font_ListSystemFonts, NULL);\n\t\tCvar_Set(&gl_font, \"\");\n#endif\n\t}\n\n\tif (COM_FDepthFile(\"fonts/qfont.kfont\", true) <= COM_FDepthFile(\"gfx/mainmenu.lmp\", true))\n\t\tfont_menu = Font_LoadFont(\"qfont\", 20, 1, r_font_postprocess_outline.ival, flags);\n\telse\n\t\tfont_menu = NULL;\n\n\tfont_default = Font_LoadFont(gl_font.string, 8, 1, r_font_postprocess_outline.ival, flags);\n\tif (!font_default && *gl_font.string)\n\t\tfont_default = Font_LoadFont(\"\", 8, 1, r_font_postprocess_outline.ival, flags);\n\n\tif (tsize != 8 || strcmp(gl_font.string, con_font_name))\n\t{\n\t\tfont_console = Font_LoadFont(con_font_name, tsize, 1, r_font_postprocess_outline.ival, flags);\n\t\tif (!font_console)\n\t\t\tfont_console = Font_LoadFont(\"\", tsize, 1, r_font_postprocess_outline.ival, flags);\n\t}\n\tif (!font_console)\n\t\tfont_console = font_default;\n\n\t//these are here instead of R2D_Console_Resize because this is guarenteed to happen in a sane place, while R2D_Console_Resize can happen during waits.\n#ifdef MENU_NATIVECODE\n\tif (mn_entry)\n\t\tmn_entry->Init(MI_RESOLUTION, vid.width, vid.height, vid.rotpixelwidth, vid.rotpixelheight);\n#endif\n#ifdef PLUGINS\n\tPlug_ResChanged(false);\n#endif\n}\n\nstatic void QDECL R2D_Font_Callback(struct cvar_s *var, char *oldvalue)\n{\n\tcon_textsize.modified = true;\n}\n\n// console size manipulation callbacks\nvoid R2D_Console_Resize(void)\n{\n\textern cvar_t gl_font;\n\textern cvar_t vid_conwidth, vid_conheight;\n\tint cwidth, cheight;\n\tfloat xratio;\n\tfloat yratio=0;\n\tfloat ang, rad;\n\textern cvar_t gl_screenangle;\n\n\tint fbwidth = vid.pixelwidth;\n\tint fbheight = vid.pixelheight;\n\n\tif (vid.framebuffer)\n\t{\n\t\tfbwidth = vid.framebuffer->width;\n\t\tfbheight = vid.framebuffer->height;\n\t}\n\n\tang = (gl_screenangle.value>0?(gl_screenangle.value+45):(gl_screenangle.value-45))/90;\n\tang = (int)ang * 90;\n\tif (ang)\n\t{\n\t\trad = (ang * M_PI) / 180;\n\t\tvid.rotpixelwidth = fabs(cos(rad)) * (fbwidth) + fabs(sin(rad)) * (fbheight);\n\t\tvid.rotpixelheight = fabs(sin(rad)) * (fbwidth) + fabs(cos(rad)) * (fbheight);\n\t}\n\telse\n\t{\n\t\tvid.rotpixelwidth = fbwidth;\n\t\tvid.rotpixelheight = fbheight;\n\t}\n\n\tcwidth = vid_conwidth.value;\n\tcheight = vid_conheight.value;\n\n\txratio = vid_conautoscale.value;\n\tif (xratio > 0)\n\t{\n\t\tchar *s = strchr(vid_conautoscale.string, ' ');\n\t\tif (s)\n\t\t\tyratio = atof(s + 1);\n\n\t\tif (yratio <= 0)\n\t\t\tyratio = xratio;\n\n\t\txratio = 1 / xratio;\n\t\tyratio = 1 / yratio;\n\n\t\t//autoscale overrides conwidth/height (without actually changing them)\n\t\tcwidth = vid.rotpixelwidth;\n\t\tcheight = vid.rotpixelheight;\n\t}\n\telse\n\t{\n\t\txratio = 1;\n\t\tyratio = 1;\n\t}\n\n\tif (!cwidth && !cheight)\n\t{\n\t\tint i = 0, nh;\n\t\tint biggest = 0;\n\t\t//find the biggest artwork size that won't get minified\n\t\tfor (i = 0; i < countof(vid_baseheight.vec4); i++)\n\t\t\tif (biggest < vid_baseheight.vec4[i] && vid_baseheight.vec4[i] < vid.rotpixelheight && vid_baseheight.vec4[i] >= vid_minsize.vec4[1])\n\t\t\t\tbiggest = vid_baseheight.vec4[i];\n\t\tif (biggest)\n\t\t{\n\t\t\tnh = vid.rotpixelheight;\n\t\t\twhile ((nh>>1) >= biggest)\n\t\t\t\tnh >>= 1;\n\t\t\tcheight = nh;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (vid.dpi_y)\n\t\t\t\tcheight = (480 * 96) / vid.dpi_y;\n\t\t\telse\n\t\t\t\tcheight = 480;\n\t\t}\n\t}\n\tif (cheight && !cwidth && vid.rotpixelheight)\n\t\tcwidth = (cheight*vid.rotpixelwidth)/vid.rotpixelheight;\n\tif (cwidth && !cheight && vid.rotpixelwidth)\n\t\tcheight = (cwidth*vid.rotpixelheight)/vid.rotpixelwidth;\n\n\tif (!cwidth)\n\t\tcwidth = vid.rotpixelwidth;\n\tif (!cheight)\n\t\tcheight = vid.rotpixelheight;\n\n\tcwidth*=xratio;\n\tcheight*=yratio;\n\n\t//never go lower than the mod's minimum, UI elements would end up off-screen.\n\tif (cwidth < vid_minsize.vec4[0])\n\t\tcwidth = vid_minsize.vec4[0];\n\tif (cheight < vid_minsize.vec4[1])\n\t\tcheight = vid_minsize.vec4[1];\n\t//the engine has its own requirements too, sorry, though its unlikely for mods go lower.\n\tif (cwidth < 320)\n\t\tcwidth = 320;\n\tif (cheight < 200)\n\t\tcheight = 200;\n\n\tvid.width = cwidth;\n\tvid.height = cheight;\n\n\tCvar_ForceCallback(&gl_font);\n}\n\nstatic void QDECL R2D_Conheight_Callback(struct cvar_s *var, char *oldvalue)\n{\n\tif (var->value > 1536)\t//anything higher is unreadable.\n\t{\n\t\tCvar_ForceSet(var, \"1536\");\n\t\treturn;\n\t}\n\tif (var->value < 200 && var->value)\t//lower would be wrong\n\t{\n\t\tCvar_ForceSet(var, \"200\");\n\t\treturn;\n\t}\n\n\tR2D_Console_Resize();\n}\n\nstatic void QDECL R2D_Conwidth_Callback(struct cvar_s *var, char *oldvalue)\n{\n\t//let let the user be too crazy\n\tif (var->value > 2048)\t//anything higher is unreadable.\n\t{\n\t\tCvar_ForceSet(var, \"2048\");\n\t\treturn;\n\t}\n\tif (var->value < 320 && var->value)\t//lower would be wrong\n\t{\n\t\tCvar_ForceSet(var, \"320\");\n\t\treturn;\n\t}\n\n\tR2D_Console_Resize();\n}\n\nstatic void QDECL R2D_Conautoscale_Callback(struct cvar_s *var, char *oldvalue)\n{\n\tR2D_Console_Resize();\n}\n\nstatic void QDECL R2D_ScreenAngle_Callback(struct cvar_s *var, char *oldvalue)\n{\n\tR2D_Console_Resize();\n}\n\n\n/*\n============\nR_PolyBlend\n============\n*/\n//bright flashes and stuff, game only, doesn't touch sbar\nvoid R2D_PolyBlend (void)\n{\n\tfloat bordersize = gl_cshiftborder.value;\n\n\tif (r_refdef.flags & RDF_NOWORLDMODEL)\n\t\treturn;\n\n\n\tif (r_refdef.playerview->bordertint[3])\n\t{\n\t\tvec2_t points[4];\n\t\tvec2_t tcoords[4];\n\t\tvec4_t colours[4];\n\t\tint i;\n\t\tif (!bordersize)\n\t\t\tbordersize = 64;\n\t\tbordersize = min(bordersize, min(r_refdef.vrect.width, r_refdef.vrect.height)/2);\n\n\t\tfor (i = 0; i < 4; i++)\n\t\t{\n\t\t\tVector4Copy(r_refdef.playerview->bordertint, colours[0]);\n\t\t\tVector4Copy(r_refdef.playerview->bordertint, colours[1]);\n\t\t\tVector4Copy(r_refdef.playerview->bordertint, colours[2]);\n\t\t\tVector4Copy(r_refdef.playerview->bordertint, colours[3]);\n\t\t\tswitch(i)\n\t\t\t{\n\t\t\tcase 0:\t//top\n\t\t\t\tVector2Set(points[0], r_refdef.vrect.x,\t\t\t\t\t\t\t\t\tr_refdef.vrect.y);\n\t\t\t\tVector2Set(points[1], r_refdef.vrect.x+r_refdef.vrect.width,\t\t\tr_refdef.vrect.y);\n\t\t\t\tVector2Set(points[2], r_refdef.vrect.x+r_refdef.vrect.width-bordersize,\tr_refdef.vrect.y+bordersize);\n\t\t\t\tVector2Set(points[3], r_refdef.vrect.x+bordersize,\t\t\t\t\t\tr_refdef.vrect.y+bordersize);\n\n\t\t\t\tcolours[2][3] = colours[3][3] = 0;\n\t\t\t\tbreak;\n\t\t\tcase 1:\t//bottom\n\t\t\t\tVector2Set(points[0], r_refdef.vrect.x+bordersize,\t\t\t\t\t\tr_refdef.vrect.y+r_refdef.vrect.height-bordersize);\n\t\t\t\tVector2Set(points[1], r_refdef.vrect.x+r_refdef.vrect.width-bordersize,\tr_refdef.vrect.y+r_refdef.vrect.height-bordersize);\n\t\t\t\tVector2Set(points[2], r_refdef.vrect.x+r_refdef.vrect.width,\t\t\tr_refdef.vrect.y+r_refdef.vrect.height);\n\t\t\t\tVector2Set(points[3], r_refdef.vrect.x,\t\t\t\t\t\t\t\t\tr_refdef.vrect.y+r_refdef.vrect.height);\n\n\t\t\t\tcolours[0][3] = colours[1][3] = 0;\n\t\t\t\tbreak;\n\t\t\tcase 2:\t//left\n\t\t\t\tVector2Set(points[0], r_refdef.vrect.x,\t\t\t\t\t\t\t\t\tr_refdef.vrect.y);\n\t\t\t\tVector2Set(points[1], r_refdef.vrect.x+bordersize,\t\t\t\t\t\tr_refdef.vrect.y+bordersize);\n\t\t\t\tVector2Set(points[2], r_refdef.vrect.x+bordersize,\t\t\t\t\t\tr_refdef.vrect.y+r_refdef.vrect.height-bordersize);\n\t\t\t\tVector2Set(points[3], r_refdef.vrect.x,\t\t\t\t\t\t\t\t\tr_refdef.vrect.y+r_refdef.vrect.height);\n\n\t\t\t\tcolours[1][3] = colours[2][3] = 0;\n\t\t\t\tbreak;\n\t\t\tcase 3:\t//right\n\t\t\t\tVector2Set(points[0], r_refdef.vrect.x+r_refdef.vrect.width-bordersize,\tr_refdef.vrect.y+bordersize);\n\t\t\t\tVector2Set(points[1], r_refdef.vrect.x+r_refdef.vrect.width,\t\t\tr_refdef.vrect.y);\n\t\t\t\tVector2Set(points[2], r_refdef.vrect.x+r_refdef.vrect.width,\t\t\tr_refdef.vrect.y+r_refdef.vrect.height);\n\t\t\t\tVector2Set(points[3], r_refdef.vrect.x+r_refdef.vrect.width-bordersize,\tr_refdef.vrect.y+r_refdef.vrect.height-bordersize);\n\t\t\t\tcolours[0][3] = colours[3][3] = 0;\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tVector2Set(tcoords[0], points[0][0]/64, points[0][1]/64);\n\t\t\tVector2Set(tcoords[1], points[1][0]/64, points[1][1]/64);\n\t\t\tVector2Set(tcoords[2], points[2][0]/64, points[2][1]/64);\n\t\t\tVector2Set(tcoords[3], points[3][0]/64, points[3][1]/64);\n\n\t\t\tR2D_Image2dQuad((const vec2_t*)points, (const vec2_t*)tcoords, (const vec4_t*)colours, shader_polyblend);\n\t\t}\n\t}\n\n\tif (r_refdef.playerview->screentint[3])\n\t{\n\t\tR2D_ImageColours (SRGBA(r_refdef.playerview->screentint[0], r_refdef.playerview->screentint[1], r_refdef.playerview->screentint[2], r_refdef.playerview->screentint[3]));\n\t\tR2D_ScalePic(r_refdef.vrect.x, r_refdef.vrect.y, r_refdef.vrect.width, r_refdef.vrect.height, shader_polyblend);\n\t\tR2D_ImageColours (1, 1, 1, 1);\n\t}\n}\n\n//for lack of hardware gamma\nvoid R2D_BrightenScreen (void)\n{\n\tfloat f;\n\n\tRSpeedMark();\n\n\tif (fabs(v_contrast.value - 1.0) < 0.05 && fabs(v_contrastboost.value - 1.0) < 0.05 && fabs(v_brightness.value - 0) < 0.05 && fabs(v_gamma.value - 1) < 0.05)\n\t\treturn;\n\n\t//don't go crazy with brightness. that makes it unusable and is thus unsafe - and worse, lots of people assume its based around 1 (like gamma and contrast are). cap to 0.5\n\tif (v_brightness.value > 0.5)\n\t\tv_brightness.value = 0.5;\n\tif (v_contrast.value < 0.5)\n\t\tv_contrast.value = 0.5;\n\n\tif (r2d_canhwgamma || vid_hardwaregamma.ival == 4)\n\t\treturn;\n\n\tTRACE((\"R2D_BrightenScreen: brightening\\n\"));\n\tif ((v_gamma.value != 1 || v_contrast.value > 3 || v_contrastboost.value != 1) && shader_gammacb->prog)\n\t{\n\t\t//this should really be done properly, with render-to-texture\n\t\tR2D_ImageColours (v_gammainverted.ival?v_gamma.value:(1/v_gamma.value), v_contrast.value, v_brightness.value, v_contrastboost.value);\n\t\tR2D_Image(0, 0, vid.width, vid.height, 0, 0, 1, 1, shader_gammacb);\n\t}\n\telse\n\t{\n\t\tf = v_contrast.value;\n\t\tf = min (f, 3);\n\n\t\twhile (f > 1)\n\t\t{\n\t\t\tif (f >= 2)\n\t\t\t{\n\t\t\t\tR2D_ImageColours (1, 1, 1, 1);\n\t\t\t\tf *= 0.5;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tR2D_ImageColours (f - 1, f - 1, f - 1, 1);\n\t\t\t\tf = 1;\n\t\t\t}\n\t\t\tR2D_ScalePic(0, 0, vid.width, vid.height, shader_contrastup);\n\t\t}\n\t\tif (f < 1)\n\t\t{\n\t\t\tR2D_ImageColours (f, f, f, 1);\n\t\t\tR2D_ScalePic(0, 0, vid.width, vid.height, shader_contrastdown);\n\t\t}\n\n\t\tif (v_brightness.value)\n\t\t{\n\t\t\tR2D_ImageColours (v_brightness.value, v_brightness.value, v_brightness.value, 1);\n\t\t\tR2D_ScalePic(0, 0, vid.width, vid.height, shader_brightness);\n\t\t}\n\t}\n\tR2D_ImageColours (1, 1, 1, 1);\n\n\t/*make sure the hud is redrawn after if needed*/\n\tSbar_Changed();\n\n\tRSpeedEnd(RSPEED_PALETTEFLASHES);\n}\n\n//for menus\nvoid R2D_FadeScreen (void)\n{\n\tR2D_ScalePic(0, 0, vid.width, vid.height, shader_menutint);\n\n\tSbar_Changed();\n}\n\nqboolean R2D_DrawLevelshot(void)\n{\n\textern char levelshotname[];\n\textern cvar_t scr_loadingscreen_aspect;\n\tif (*levelshotname)\n\t{\n\t\tshader_t *pic = R2D_SafeCachePic (levelshotname);\n\t\tint w,h;\n\t\tif (!R_GetShaderSizes(pic, &w, &h, true))\n\t\t{\n#ifdef Q3CLIENT\n\t\t\tpic = R2D_SafeCachePic(\"menu/art/unkownmap\");\n\t\t\tif (!R_GetShaderSizes(pic, &w, &h, true))\n#endif\n\t\t\t\tw = h = 1;\n\t\t}\n\t\tswitch(scr_loadingscreen_aspect.ival)\n\t\t{\n\t\tcase 0:\t//use the source image's aspect\n\t\t\tbreak;\n\t\tcase 1:\t//q3 assumed 4:3 resolutions, with power-of-two images. lame, but lets retain the aspect\n\t\t\tw = 640;\n\t\t\th = 480;\n\t\t\tbreak;\n\t\tcase 2:\t//just ignore aspect entirely and stretch the thing in hideous ways\n\t\t\tw = vid.width;\n\t\t\th = vid.height;\n\t\t\tbreak;\n\t\t}\n\t\tR2D_Letterbox(0, 0, vid.width, vid.height, pic, w, h);\n\n\t\t//q3's noise.\n\t\tpic = R2D_SafeCachePic(\"levelShotDetail\");\n\t\tif (R_GetShaderSizes(pic, &w, &h, true))\n\t\t\tR2D_Image(0, 0, vid.width, vid.height, 0, 0, vid.width/256, vid.height/256, pic);\n\t\treturn true;\n\t}\n\treturn false;\n}\n\n//crosshairs\n#define CS_HEIGHT 8\n#define CS_WIDTH 8\nunsigned char crosshair_pixels[] =\n{\n\t// 2 + (spaced)\n\t0x08,\n\t0x00,\n\t0x08,\n\t0x55,\n\t0x08,\n\t0x00,\n\t0x08,\n\t0x00,\n\t// 3 +\n\t0x00,\n\t0x08,\n\t0x08,\n\t0x36,\n\t0x08,\n\t0x08,\n\t0x00,\n\t0x00,\n\t// 4 X\n\t0x00,\n\t0x22,\n\t0x14,\n\t0x00,\n\t0x14,\n\t0x22,\n\t0x00,\n\t0x00,\n\t// 5 X (spaced)\n\t0x41,\n\t0x00,\n\t0x14,\n\t0x00,\n\t0x14,\n\t0x00,\n\t0x41,\n\t0x00,\n\t// 6 diamond (unconnected)\n\t0x00,\n\t0x14,\n\t0x22,\n\t0x00,\n\t0x22,\n\t0x14,\n\t0x00,\n\t0x00,\n\t// 7 diamond\n\t0x00,\n\t0x08,\n\t0x14,\n\t0x22,\n\t0x14,\n\t0x08,\n\t0x00,\n\t0x00,\n\t// 8 four points\n\t0x00,\n\t0x08,\n\t0x00,\n\t0x22,\n\t0x00,\n\t0x08,\n\t0x00,\n\t0x00,\n\t// 9 three points\n\t0x00,\n\t0x00,\n\t0x08,\n\t0x00,\n\t0x22,\n\t0x00,\n\t0x00,\n\t0x00,\n\t// 10\n\t0x08,\n\t0x2a,\n\t0x00,\n\t0x63,\n\t0x00,\n\t0x2a,\n\t0x08,\n\t0x00,\n\t// 11\n\t0x49,\n\t0x2a,\n\t0x00,\n\t0x63,\n\t0x00,\n\t0x2a,\n\t0x49,\n\t0x00,\n\t// 12 horizontal line\n\t0x00,\n\t0x00,\n\t0x00,\n\t0x77,\n\t0x00,\n\t0x00,\n\t0x00,\n\t0x00,\n\t// 13 vertical line\n\t0x08,\n\t0x08,\n\t0x08,\n\t0x00,\n\t0x08,\n\t0x08,\n\t0x08,\n\t0x00,\n\t// 14 larger +\n\t0x08,\n\t0x08,\n\t0x08,\n\t0x77,\n\t0x08,\n\t0x08,\n\t0x08,\n\t0x00,\n\t// 15 angle\n\t0x00,\n\t0x00,\n\t0x00,\n\t0x70,\n\t0x08,\n\t0x08,\n\t0x08,\n\t0x00,\n\t// 16 dot\n\t0x00,\n\t0x00,\n\t0x00,\n\t0x08,\n\t0x00,\n\t0x00,\n\t0x00,\n\t0x00,\n\t// 17 weird angle thing\n\t0x00,\n\t0x00,\n\t0x00,\n\t0x38,\n\t0x48,\n\t0x08,\n\t0x10,\n\t0x00,\n\t// 18 circle w/ dot\n\t0x00,\n\t0x00,\n\t0x00,\n\t0x6b,\n\t0x41,\n\t0x63,\n\t0x3e,\n\t0x08,\n\t// 19 tripoint\n\t0x08,\n\t0x08,\n\t0x08,\n\t0x00,\n\t0x14,\n\t0x22,\n\t0x41,\n\t0x00,\n};\n\nvoid R2D_Crosshair_Update(void)\n{\n\tint crossdata[CS_WIDTH*CS_HEIGHT];\n\tint c;\n\tint w, h;\n\tunsigned char *x;\n\n\tc = crosshair.ival;\n\tif (!crosshairimage.string)\n\t\treturn;\n\telse if (crosshairimage.string[0] && c == 1)\n\t{\n\t\tshader_crosshair->defaulttextures->base = R_LoadHiResTexture (crosshairimage.string, \"crosshairs\", IF_UIPIC|IF_NOMIPMAP|IF_NOGAMMA);\n\t\tif (TEXVALID(shader_crosshair->defaulttextures->base))\n\t\t\treturn;\n\t}\n\telse if (c <= 1)\n\t\treturn;\n\n\tc -= 2;\n\tc = c % (sizeof(crosshair_pixels) / (CS_HEIGHT*sizeof(*crosshair_pixels)));\n\n\tif (!TEXVALID(ch_int_texture))\n\t\tch_int_texture = Image_CreateTexture(\"***crosshair***\", NULL, IF_UIPIC|IF_NOMIPMAP);\n\tshader_crosshair->defaulttextures->base = ch_int_texture;\n\n\tQ_memset(crossdata, 0, sizeof(crossdata));\n\n\tx = crosshair_pixels + (CS_HEIGHT * c);\n\tfor (h = 0; h < CS_HEIGHT; h++)\n\t{\n\t\tint pix = x[h];\n\t\tfor (w = 0; w < CS_WIDTH; w++)\n\t\t{\n\t\t\tif (pix & 0x1)\n\t\t\t\tcrossdata[CS_WIDTH * h + w] = 0xffffffff;\n\t\t\tpix >>= 1;\n\t\t}\n\t}\n\n\tImage_Upload(ch_int_texture, TF_RGBA32, crossdata, NULL, CS_WIDTH, CS_HEIGHT, 1, IF_UIPIC|IF_NOMIPMAP|IF_NOGAMMA);\n\n}\n\nstatic void QDECL R2D_CrosshairImage_Callback(struct cvar_s *var, char *oldvalue)\n{\n\tR2D_Crosshair_Update();\n}\n\nstatic void QDECL R2D_Crosshair_Callback(struct cvar_s *var, char *oldvalue)\n{\n\tR2D_Crosshair_Update();\n}\n\nstatic void QDECL R2D_CrosshairColor_Callback(struct cvar_s *var, char *oldvalue)\n{\n\tSCR_StringToRGB(var->string, ch_color, 255);\n\n\tch_color[0] = bound(0, ch_color[0], 1);\n\tch_color[1] = bound(0, ch_color[1], 1);\n\tch_color[2] = bound(0, ch_color[2], 1);\n}\n\nvoid R2D_DrawCrosshair(void)\n{\n\tint x, y;\n\tint sc;\n\tfloat sx, sy, sizex, sizey;\n\n\tfloat size;\n\n\tif (crosshair.ival < 1)\n\t\treturn;\n\n\t// old style\n\tif (crosshair.ival == 1 && !crosshairimage.string[0])\n\t{\n\t\t// adjust console crosshair scale to match default\n\t\tsize = crosshairsize.value;\n\t\tif (size == 0)\n\t\t\tsize = 8;\n\t\telse if (size < 0)\n\t\t\tsize = -size;\n\t\tfor (sc = 0; sc < cl.splitclients; sc++)\n\t\t{\n\t\t\tSCR_CrosshairPosition(&cl.playerview[sc], &sx, &sy);\n\t\t\tFont_BeginScaledString(font_default, sx, sy, size, size, &sx, &sy);\n\t\t\tsx -= Font_CharScaleWidth(CON_WHITEMASK, '+' | 0xe000)/2;\n\t\t\tsy -= Font_CharScaleHeight()/2;\n\t\t\tR2D_ImageColours(ch_color[0], ch_color[1], ch_color[2], crosshairalpha.value);\n\t\t\tFont_DrawScaleChar(sx, sy, CON_WHITEMASK, '+' | 0xe000);\n\t\t\tFont_EndString(font_default);\n\t\t}\n\t\tR2D_ImageColours(1,1,1,1);\n\t\treturn;\n\t}\n\n\tsize = crosshairsize.value;\n\n\tif (size < 0)\n\t{\n\t\tsize = -size;\n\t\tsizex = size;\n\t\tsizey = size;\n\t}\n\telse\n\t{\n\t\tsizex = (size*vid.rotpixelwidth) / (float)vid.width;\n\t\tsizey = (size*vid.rotpixelheight) / (float)vid.height;\n\t}\n\n\tsizex = (int)sizex;\n\tsizex = ((sizex)*(int)vid.width) / (float)vid.rotpixelwidth;\n\n\tsizey = (int)sizey;\n\tsizey = ((sizey)*(int)vid.height) / (float)vid.rotpixelheight;\n\n\tR2D_ImageColours(ch_color[0], ch_color[1], ch_color[2], crosshairalpha.value);\n\tfor (sc = 0; sc < cl.splitclients; sc++)\n\t{\n\t\tSCR_CrosshairPosition(&cl.playerview[sc], &sx, &sy);\n\n\t\t//translate to pixel coord, for rounding\n\t\tx = ((sx-sizex+(sizex/CS_WIDTH))*vid.rotpixelwidth) / (float)vid.width;\n\t\ty = ((sy-sizey+(sizey/CS_HEIGHT))*vid.rotpixelheight) / (float)vid.height;\n\n\t\t//translate to screen coords\n\t\tsx = ((x)*(int)vid.width) / (float)vid.rotpixelwidth;\n\t\tsy = ((y)*(int)vid.height) / (float)vid.rotpixelheight;\n\n\t\tR2D_Image(sx, sy, sizex*2, sizey*2, 0, 0, 1, 1, shader_crosshair);\n\t}\n\tR2D_ImageColours(1, 1, 1, 1);\n}\n\nstatic texid_t internalrt;\n//resize a texture for a render target and specify the format of it.\n//pass TF_INVALID and sizes=0 to get without configuring (shaders that hardcode an $rt1 etc).\ntexid_t R2D_RT_Configure(const char *id, int width, int height, uploadfmt_t rtfmt, unsigned int imageflags)\n{\n\ttexid_t tid;\n\tif (!strcmp(id, \"-\"))\n\t{\n\t\tinternalrt = tid = Image_CreateTexture(\"\", NULL, imageflags);\n\t}\n\telse\n\t{\n\t\ttid = Image_FindTexture(id, NULL, imageflags);\n\t\tif (!TEXVALID(tid))\n\t\t\ttid = Image_CreateTexture(id, NULL, imageflags);\n\t}\n\n\tif (rtfmt)\n\t{\n\t\tif (tid->flags != ((tid->flags & ~(IF_NEAREST|IF_LINEAR)) | (imageflags & (IF_NEAREST|IF_LINEAR))))\n\t\t{\n\t\t\ttid->flags = ((tid->flags & ~(IF_NEAREST|IF_LINEAR)) | (imageflags & (IF_NEAREST|IF_LINEAR)));\n\t\t\ttid->width = -1;\n\t\t}\n\t\tImage_Upload(tid, rtfmt, NULL, NULL, width, height, 1, imageflags);\n\t\ttid->width = width;\n\t\ttid->height = height;\n\t}\n\treturn tid;\n}\ntexid_t R2D_RT_GetTexture(const char *id, unsigned int *width, unsigned int *height)\n{\n\ttexid_t tid;\n\tif (!strcmp(id, \"-\"))\n\t{\n\t\ttid = internalrt;\n//\t\tinternalrt = r_nulltex;\n\t}\n\telse\n\t\ttid = Image_FindTexture(id, NULL, RT_IMAGEFLAGS);\n\n\tif (tid)\n\t{\n\t\t*width = tid->width;\n\t\t*height = tid->height;\n\t}\n\telse\n\t{\n\t\t*width = 0;\n\t\t*height = 0;\n\t}\n\treturn tid;\n}\n\n#endif\n\n"
  },
  {
    "path": "engine/client/r_d3.c",
    "content": "#include \"quakedef.h\"\r\n#ifdef MAP_PROC\r\n\r\n#ifdef HAVE_CLIENT\r\n#include \"shader.h\"\r\n#endif\r\n#include \"com_mesh.h\"\r\n\r\n//FIXME: shadowmaps should build a cache of the nearby area surfaces and flag those models as RF_NOSHADOW or something\r\n//fixme: merge areas and static ents too somehow.\r\n\r\nvoid Mod_SetParent (mnode_t *node, mnode_t *parent);\r\nstatic int\tD3_ClusterForPoint (struct model_s *model, const vec3_t point, int *areaout);\r\n\r\n#ifdef HAVE_CLIENT\r\nstatic void R_BuildDefaultTexnums_Doom3(shader_t *shader)\r\n{\r\n\textern qboolean\t\tr_loadbumpmapping;\r\n\textern cvar_t gl_specular;\r\n\textern cvar_t r_fb_bmodels;\r\n\r\n\tchar *h;\r\n\tchar imagename[MAX_QPATH];\r\n\tchar mapname[MAX_QPATH];\r\n\tchar *subpath = NULL;\r\n\ttexnums_t *tex;\r\n\tunsigned int a, aframes;\r\n\tunsigned int imageflags = 0;\r\n\tstrcpy(imagename, shader->name);\r\n\th = strchr(imagename, '#');\r\n\tif (h)\r\n\t\t*h = 0;\r\n\tif (*imagename == '/' || strchr(imagename, ':'))\r\n\t{\t//this is not security. this is anti-spam for the verbose security in the filesystem code.\r\n\t\tCon_Printf(\"Warning: shader has absolute path: %s\\n\", shader->name);\r\n\t\t*imagename = 0;\r\n\t}\r\n\r\n\ttex = shader->defaulttextures;\r\n\taframes = max(1, shader->numdefaulttextures);\r\n\t//if any were specified explicitly, replicate that into all.\r\n\t//this means animmap can be used, with any explicit textures overriding all.\r\n\r\n\tfor (a = 1; a < aframes; a++)\r\n\t{\r\n\t\tif (!TEXVALID(tex[a].base))\r\n\t\t\ttex[a].base\t\t\t= tex[0].base;\r\n\t\tif (!TEXVALID(tex[a].bump))\r\n\t\t\ttex[a].bump\t\t\t= tex[0].bump;\r\n\t\tif (!TEXVALID(tex[a].fullbright))\r\n\t\t\ttex[a].fullbright\t= tex[0].fullbright;\r\n\t\tif (!TEXVALID(tex[a].specular))\r\n\t\t\ttex[a].specular\t\t= tex[0].specular;\r\n\t\tif (!TEXVALID(tex[a].loweroverlay))\r\n\t\t\ttex[a].loweroverlay\t= tex[0].loweroverlay;\r\n\t\tif (!TEXVALID(tex[a].upperoverlay))\r\n\t\t\ttex[a].upperoverlay\t= tex[0].upperoverlay;\r\n\t\tif (!TEXVALID(tex[a].reflectmask))\r\n\t\t\ttex[a].reflectmask\t= tex[0].reflectmask;\r\n\t\tif (!TEXVALID(tex[a].reflectcube))\r\n\t\t\ttex[a].reflectcube\t= tex[0].reflectcube;\r\n\t}\r\n\tfor (a = 0; a < aframes; a++, tex++)\r\n\t{\r\n\t\tCOM_StripExtension(tex->mapname, mapname, sizeof(mapname));\r\n\r\n\t\tif (!TEXVALID(tex->base))\r\n\t\t{\r\n\t\t\t/*dlights/realtime lighting needs some stuff*/\r\n\t\t\tif (!TEXVALID(tex->base) && *tex->mapname)// && (shader->flags & SHADER_HASDIFFUSE))\r\n\t\t\t\ttex->base = R_LoadHiResTexture(tex->mapname, NULL, 0);\r\n\r\n\t\t\tif (!TEXVALID(tex->base))\r\n\t\t\t\ttex->base = R_LoadHiResTexture(va(\"%s_d\", imagename), subpath, (*imagename=='{')?0:IF_NOALPHA);\r\n\t\t}\r\n\r\n\t\timageflags |= IF_LOWPRIORITY;\r\n\r\n\t\tCOM_StripExtension(imagename, imagename, sizeof(imagename));\r\n\r\n\t\tif (!TEXVALID(tex->bump))\r\n\t\t{\r\n\t\t\tif ((shader->flags & SHADER_HASNORMALMAP) && r_loadbumpmapping)\r\n\t\t\t{\r\n\t\t\t\tif (!TEXVALID(tex->bump) && *mapname && (shader->flags & SHADER_HASNORMALMAP))\r\n\t\t\t\t\ttex->bump = R_LoadHiResTexture(va(\"%s_local\", mapname), NULL, imageflags|IF_TRYBUMP|IF_NOSRGB);\r\n\t\t\t\tif (!TEXVALID(tex->bump))\r\n\t\t\t\t\ttex->bump = R_LoadHiResTexture(va(\"%s_local\", imagename), subpath, imageflags|IF_TRYBUMP|IF_NOSRGB);\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tif (!TEXVALID(tex->loweroverlay))\r\n\t\t{\r\n\t\t\tif (shader->flags & SHADER_HASTOPBOTTOM)\r\n\t\t\t{\r\n\t\t\t\tif (!TEXVALID(tex->loweroverlay) && *mapname)\r\n\t\t\t\t\ttex->loweroverlay = R_LoadHiResTexture(va(\"%s_pants\", mapname), NULL, imageflags);\r\n\t\t\t\tif (!TEXVALID(tex->loweroverlay))\r\n\t\t\t\t\ttex->loweroverlay = R_LoadHiResTexture(va(\"%s_pants\", imagename), subpath, imageflags);\t/*how rude*/\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tif (!TEXVALID(tex->upperoverlay))\r\n\t\t{\r\n\t\t\tif (shader->flags & SHADER_HASTOPBOTTOM)\r\n\t\t\t{\r\n\t\t\t\tif (!TEXVALID(tex->upperoverlay) && *mapname)\r\n\t\t\t\t\ttex->upperoverlay = R_LoadHiResTexture(va(\"%s_shirt\", mapname), NULL, imageflags);\r\n\t\t\t\tif (!TEXVALID(tex->upperoverlay))\r\n\t\t\t\t\ttex->upperoverlay = R_LoadHiResTexture(va(\"%s_shirt\", imagename), subpath, imageflags);\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tif (!TEXVALID(tex->specular))\r\n\t\t{\r\n\t\t\tif ((shader->flags & SHADER_HASGLOSS) && gl_specular.value)\r\n\t\t\t{\r\n\t\t\t\tif (!TEXVALID(tex->specular) && *mapname)\r\n\t\t\t\t\ttex->specular = R_LoadHiResTexture(va(\"%s_s\", mapname), NULL, imageflags);\r\n\t\t\t\tif (!TEXVALID(tex->specular))\r\n\t\t\t\t\ttex->specular = R_LoadHiResTexture(va(\"%s_s\", imagename), subpath, imageflags);\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tif (!TEXVALID(tex->fullbright))\r\n\t\t{\r\n\t\t\tif ((shader->flags & SHADER_HASFULLBRIGHT) && r_fb_bmodels.value && gl_load24bit.value)\r\n\t\t\t{\r\n\t\t\t\tif (!TEXVALID(tex->fullbright) && *mapname)\r\n\t\t\t\t\ttex->fullbright = R_LoadHiResTexture(va(\"%s_luma:%s_glow\", mapname, mapname), NULL, imageflags);\r\n\t\t\t\tif (!TEXVALID(tex->fullbright))\r\n\t\t\t\t\ttex->fullbright = R_LoadHiResTexture(va(\"%s_luma:%s_glow\", imagename, imagename), subpath, imageflags);\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n}\r\nstatic void ModD3_GenAreaVBO(void *ctx, void *data, size_t a, size_t barg)\r\n{\r\n\tmodel_t *sub = ctx;\r\n\tbatch_t *b = sub->batches[0];\r\n\tint surf;\r\n\tsub->batches[0] = NULL;\r\n\r\n\tfor (surf = 0; surf < sub->numbatches; surf++)\r\n\t\tsub->numsurfaces += b[surf].meshes;\r\n\tsub->texinfo = ZG_Malloc(&sub->memgroup, sizeof(*sub->texinfo)*sub->numsurfaces);\r\n\tsub->surfaces = ZG_Malloc(&sub->memgroup, sizeof(*sub->surfaces)*sub->numsurfaces);\r\n\tsub->firstmodelsurface = sub->nummodelsurfaces = 0;\r\n\r\n\tfor (surf = 0; surf < sub->numbatches; surf++)\r\n\t{\r\n\t\tb[surf].shader = R_RegisterShader_Vertex(sub, b[surf].texture->name);\r\n\t\tR_BuildDefaultTexnums_Doom3(b[surf].shader);\r\n\r\n\t\t//now we know its sort key, we can link it properly. *sigh*\r\n\t\tb[surf].next = sub->batches[b[surf].shader->sort];\r\n\t\tsub->batches[b[surf].shader->sort] = &b[surf];\r\n\r\n\t\t//all this extra stuff so r_showshaders works. *sigh*\r\n\t\tsub->surfaces[sub->nummodelsurfaces].texinfo = &sub->texinfo[sub->nummodelsurfaces];\r\n\t\tsub->surfaces[sub->nummodelsurfaces].texinfo->texture = b[surf].texture;\r\n\t\tsub->surfaces[sub->nummodelsurfaces].mesh = b[surf].mesh[0];\r\n\t\tsub->nummodelsurfaces++;\r\n\t}\r\n\r\n\tBE_GenBrushModelVBO(sub);\r\n}\r\n\r\nstatic qboolean Mod_LoadMap_Proc(model_t *model, char *data)\r\n{\r\n\tchar token[256];\r\n\tint ver = 0;\r\n\tdata = COM_ParseOut(data, token, sizeof(token));\r\n\tif (!strcmp(token, \"mapProcFile003\"))\r\n\t\tver = 3;\r\n\tif (!strcmp(token, \"PROC\"))\r\n\t{\r\n\t\tdata = COM_ParseOut(data, token, sizeof(token));\r\n\t\tver = atoi(token);\r\n\t}\r\n\r\n\tif (ver != 3 && ver != 4)\r\n\t{\r\n\t\tCon_Printf(\"proc format not compatible %s\\n\", token);\r\n\t\treturn false;\r\n\t}\r\n\t/*FIXME: add sanity checks*/\r\n\r\n\tif (ver == 4)\r\n\t{\r\n\t\tdata = COM_ParseOut(data, token, sizeof(token));\r\n\t}\r\n\r\n\twhile(1)\r\n\t{\r\n\t\tdata = COM_ParseOut(data, token, sizeof(token));\r\n\t\tif (!data)\r\n\t\t\tbreak;\r\n\t\telse if (!strcmp(token, \"model\"))\r\n\t\t{\r\n\t\t\tbatch_t *b;\r\n\t\t\tmesh_t *m, **ml;\r\n\t\t\tmodel_t *sub;\r\n\t\t\tfloat f;\r\n\t\t\tint numsurfs, surf;\r\n\t\t\tint numverts, v, j;\r\n\t\t\tint numindicies;\r\n\t\t\tchar *vdata;\r\n\r\n\t\t\tdata = COM_ParseOut(data, token, sizeof(token));\r\n\t\t\tif (strcmp(token, \"{\"))\r\n\t\t\t\treturn false;\r\n\r\n\t\t\t*token = '*';\r\n\t\t\tdata = COM_ParseOut(data, token+1, sizeof(token)-1);\r\n\t\t\tQ_strncatz(token, \":\", sizeof(token));\r\n\t\t\tQ_strncatz(token, model->publicname, sizeof(token));\r\n\t\t\tsub = Mod_FindName(token);\r\n\r\n\t\t\tif (sub->loadstate != MLS_NOTLOADED)\r\n\t\t\t\treturn false;\r\n\r\n\t\t\tdata = COM_ParseOut(data, token, sizeof(token));\r\n\t\t\tnumsurfs = atoi(token);\r\n\t\t\tif (numsurfs < 0 || numsurfs > 10000)\r\n\t\t\t\treturn false;\r\n\t\t\tif (numsurfs)\r\n\t\t\t{\r\n\t\t\t\tb = ZG_Malloc(&model->memgroup, sizeof(*b) * numsurfs);\r\n\t\t\t\tm = ZG_Malloc(&model->memgroup, sizeof(*m) * numsurfs);\r\n\t\t\t\tml = ZG_Malloc(&model->memgroup, sizeof(*ml) * numsurfs);\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tb = NULL;\r\n\t\t\t\tm = NULL;\r\n\t\t\t\tml = NULL;\r\n\t\t\t}\r\n\t\t\tsub->numsurfaces = numsurfs;\r\n\r\n\t\t\t//ver4 may have a 'sky' field here\r\n\t\t\tvdata = COM_ParseOut(data, token, sizeof(token));\r\n\t\t\tif (strcmp(token, \"{\") && strcmp(token, \"}\"))\r\n\t\t\t{\r\n\t\t\t\t//sky = atoi(token);\r\n\t\t\t\tdata = vdata;\r\n\t\t\t}\r\n\r\n\t\t\tsub->mins[0] = 99999999;\r\n\t\t\tsub->mins[1] = 99999999;\r\n\t\t\tsub->mins[2] = 99999999;\r\n\t\t\tsub->maxs[0] = -99999999;\r\n\t\t\tsub->maxs[1] = -99999999;\r\n\t\t\tsub->maxs[2] = -99999999;\r\n\t\t\tfor (surf = 0; surf < numsurfs; surf++)\r\n\t\t\t{\r\n\t\t\t\tdata = COM_ParseOut(data, token, sizeof(token));\r\n\t\t\t\tif (strcmp(token, \"{\"))\r\n\t\t\t\t\tbreak;\r\n\t\t\t\tif (!data)\r\n\t\t\t\t\treturn false;\r\n\t\t\t\tb[surf].maxmeshes = 1;\r\n\t\t\t\tb[surf].mesh = &ml[surf];\r\n\t\t\t\tml[surf] = &m[surf];\r\n\t\t\t\tb[surf].lightmap[0] = -1;\r\n\t\t\t\tb[surf].lightmap[1] = -1;\r\n\t\t\t\tb[surf].lightmap[2] = -1;\r\n\t\t\t\tb[surf].lightmap[3] = -1;\r\n\t\t\t\tb[surf].lmlightstyle[0] = 0;\r\n\t\t\t\tb[surf].lmlightstyle[1] = INVALID_LIGHTSTYLE;\r\n\t\t\t\tb[surf].lmlightstyle[2] = INVALID_LIGHTSTYLE;\r\n\t\t\t\tb[surf].lmlightstyle[3] = INVALID_LIGHTSTYLE;\r\n\r\n\t\t\t\tdata = COM_ParseOut(data, token, sizeof(token));\r\n\t\t\t\tb[surf].texture = ZG_Malloc(&sub->memgroup, sizeof(*b[surf].texture));\r\n\t\t\t\tQ_strncpyz(b[surf].texture->name, token, sizeof(b[surf].texture->name));\r\n\r\n\t\t\t\tdata = COM_ParseOut(data, token, sizeof(token));\r\n\t\t\t\tnumverts = atoi(token);\r\n\t\t\t\tdata = COM_ParseOut(data, token, sizeof(token));\r\n\t\t\t\tnumindicies = atoi(token);\r\n\r\n\t\t\t\tm[surf].numvertexes = numverts;\r\n\t\t\t\tm[surf].numindexes = numindicies;\r\n\t\t\t\tvdata = ZG_Malloc(&sub->memgroup, numverts * (sizeof(vecV_t) + sizeof(vec2_t) + sizeof(vec3_t)*3 + sizeof(vec4_t)) + numindicies * sizeof(index_t));\r\n\r\n\t\t\t\tm[surf].colors4f_array[0] = (vec4_t*)vdata;vdata += sizeof(vec4_t)*numverts;\r\n\t\t\t\tm[surf].xyz_array = (vecV_t*)vdata;vdata += sizeof(vecV_t)*numverts;\r\n\t\t\t\tm[surf].st_array = (vec2_t*)vdata;vdata += sizeof(vec2_t)*numverts;\r\n\t\t\t\tm[surf].normals_array = (vec3_t*)vdata;vdata += sizeof(vec3_t)*numverts;\r\n\t\t\t\tm[surf].snormals_array = (vec3_t*)vdata;vdata += sizeof(vec3_t)*numverts;\r\n\t\t\t\tm[surf].tnormals_array = (vec3_t*)vdata;vdata += sizeof(vec3_t)*numverts;\r\n\t\t\t\tm[surf].indexes = (index_t*)vdata;\r\n\r\n\t\t\t\tfor (v = 0; v < numverts; v++)\r\n\t\t\t\t{\r\n\t\t\t\t\tdata = COM_ParseOut(data, token, sizeof(token));\r\n\t\t\t\t\tif (strcmp(token, \"(\"))\r\n\t\t\t\t\t\treturn false;\r\n\r\n\t\t\t\t\tdata = COM_ParseOut(data, token, sizeof(token));\r\n\t\t\t\t\tm[surf].xyz_array[v][0] = atof(token);\r\n\t\t\t\t\tdata = COM_ParseOut(data, token, sizeof(token));\r\n\t\t\t\t\tm[surf].xyz_array[v][1] = atof(token);\r\n\t\t\t\t\tdata = COM_ParseOut(data, token, sizeof(token));\r\n\t\t\t\t\tm[surf].xyz_array[v][2] = atof(token);\r\n\t\t\t\t\tdata = COM_ParseOut(data, token, sizeof(token));\r\n\t\t\t\t\tm[surf].st_array[v][0] = atof(token);\r\n\t\t\t\t\tdata = COM_ParseOut(data, token, sizeof(token));\r\n\t\t\t\t\tm[surf].st_array[v][1] = atof(token);\r\n\t\t\t\t\tdata = COM_ParseOut(data, token, sizeof(token));\r\n\t\t\t\t\tm[surf].normals_array[v][0] = atof(token);\r\n\t\t\t\t\tdata = COM_ParseOut(data, token, sizeof(token));\r\n\t\t\t\t\tm[surf].normals_array[v][1] = atof(token);\r\n\t\t\t\t\tdata = COM_ParseOut(data, token, sizeof(token));\r\n\t\t\t\t\tm[surf].normals_array[v][2] = atof(token);\r\n\r\n\t\t\t\t\tfor (j = 0; j < 3; j++)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tf = m[surf].xyz_array[v][j];\r\n\t\t\t\t\t\tif (f > sub->maxs[j])\r\n\t\t\t\t\t\t\tsub->maxs[j] = f;\r\n\t\t\t\t\t\tif (f < sub->mins[j])\r\n\t\t\t\t\t\t\tsub->mins[j] = f;\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\tm[surf].colors4f_array[0][v][0] = 1;\r\n\t\t\t\t\tm[surf].colors4f_array[0][v][1] = 1;\r\n\t\t\t\t\tm[surf].colors4f_array[0][v][2] = 1;\r\n\t\t\t\t\tm[surf].colors4f_array[0][v][3] = 1;\r\n\r\n\t\t\t\t\tdata = COM_ParseOut(data, token, sizeof(token));\r\n\t\t\t\t\t/*if its not closed yet, there's an optional colour value*/\r\n\t\t\t\t\tif (strcmp(token, \")\"))\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tm[surf].colors4f_array[0][v][0] = atof(token)/255;\r\n\t\t\t\t\t\tdata = COM_ParseOut(data, token, sizeof(token));\r\n\t\t\t\t\t\tm[surf].colors4f_array[0][v][1] = atof(token)/255;\r\n\t\t\t\t\t\tdata = COM_ParseOut(data, token, sizeof(token));\r\n\t\t\t\t\t\tm[surf].colors4f_array[0][v][2] = atof(token)/255;\r\n\t\t\t\t\t\tdata = COM_ParseOut(data, token, sizeof(token));\r\n\t\t\t\t\t\tm[surf].colors4f_array[0][v][3] = atof(token)/255;\r\n\r\n\t\t\t\t\t\tdata = COM_ParseOut(data, token, sizeof(token));\r\n\t\t\t\t\t\tif (strcmp(token, \")\"))\r\n\t\t\t\t\t\t\treturn false;\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t\tfor (v = 0; v < numindicies; v++)\r\n\t\t\t\t{\r\n\t\t\t\t\tdata = COM_ParseOut(data, token, sizeof(token));\r\n\t\t\t\t\tm[surf].indexes[v] = atoi(token);\r\n\t\t\t\t}\r\n\r\n\t\t\t\t//generate the s+t vectors according to the normals that we just parsed.\r\n\t\t\t\tR_Generate_Mesh_ST_Vectors(&m[surf]);\r\n\r\n\t\t\t\tdata = COM_ParseOut(data, token, sizeof(token));\r\n\t\t\t\tif (strcmp(token, \"}\"))\r\n\t\t\t\t\treturn false;\r\n\t\t\t}\r\n\t\t\tdata = COM_ParseOut(data, token, sizeof(token));\r\n\t\t\tif (strcmp(token, \"}\"))\r\n\t\t\t\treturn false;\r\n//\t\t\tsub->loadstate = MLS_LOADED;\r\n\t\t\tsub->fromgame = fg_new;\r\n\t\t\tsub->type = mod_brush;\r\n\t\t\tsub->lightmaps.surfstyles = 1;\r\n\r\n\t\t\tmemset(sub->batches, 0, sizeof(sub->batches));\r\n\t\t\tsub->batches[0] = b;\r\n\t\t\tsub->numbatches = numsurfs;\r\n\r\n\t\t\tCOM_AddWork(WG_MAIN, ModD3_GenAreaVBO, sub, NULL, MLS_LOADED, 0);\r\n\t\t\tCOM_AddWork(WG_MAIN, Mod_ModelLoaded, sub, NULL, MLS_LOADED, 0);\r\n\t\t}\r\n\t\telse if (!strcmp(token, \"shadowModel\"))\r\n\t\t{\r\n\t\t\tint numverts, v;\r\n\t\t\tint numindexes, i;\r\n\t\t\tdata = COM_ParseOut(data, token, sizeof(token));\r\n\t\t\tif (strcmp(token, \"{\"))\r\n\t\t\t\treturn false;\r\n\r\n\t\t\tdata = COM_ParseOut(data, token, sizeof(token));\r\n\t\t\t//name\r\n\t\t\tdata = COM_ParseOut(data, token, sizeof(token));\r\n\t\t\tnumverts = atoi(token);\r\n\t\t\tdata = COM_ParseOut(data, token, sizeof(token));\r\n\t\t\t//nocaps\r\n\t\t\tdata = COM_ParseOut(data, token, sizeof(token));\r\n\t\t\t//nofrontcaps\r\n\t\t\tdata = COM_ParseOut(data, token, sizeof(token));\r\n\t\t\tnumindexes = atoi(token);\r\n\t\t\tdata = COM_ParseOut(data, token, sizeof(token));\r\n\t\t\t//planebits\r\n\r\n\t\t\tfor (v = 0; v < numverts; v++)\r\n\t\t\t{\r\n\t\t\t\tdata = COM_ParseOut(data, token, sizeof(token));\r\n\t\t\t\tif (strcmp(token, \"(\"))\r\n\t\t\t\t\treturn false;\r\n\r\n\t\t\t\tdata = COM_ParseOut(data, token, sizeof(token));\r\n\t\t\t\t//x\r\n\t\t\t\tdata = COM_ParseOut(data, token, sizeof(token));\r\n\t\t\t\t//y\r\n\t\t\t\tdata = COM_ParseOut(data, token, sizeof(token));\r\n\t\t\t\t//z\r\n\r\n\t\t\t\tdata = COM_ParseOut(data, token, sizeof(token));\r\n\t\t\t\tif (strcmp(token, \")\"))\r\n\t\t\t\t\treturn false;\r\n\t\t\t}\r\n\r\n\t\t\tfor (i = 0; i < numindexes; i++)\r\n\t\t\t{\r\n\t\t\t\tdata = COM_ParseOut(data, token, sizeof(token));\r\n\t\t\t}\r\n\r\n\t\t\tdata = COM_ParseOut(data, token, sizeof(token));\r\n\t\t\tif (strcmp(token, \"}\"))\r\n\t\t\t\treturn false;\r\n\t\t}\r\n\t\telse if (!strcmp(token, \"nodes\"))\r\n\t\t{\r\n\t\t\tint numnodes, n;\r\n\t\t\tdata = COM_ParseOut(data, token, sizeof(token));\r\n\t\t\tif (strcmp(token, \"{\"))\r\n\t\t\t\treturn false;\r\n\r\n\t\t\tdata = COM_ParseOut(data, token, sizeof(token));\r\n\t\t\tnumnodes = atoi(token);\r\n\t\t\tmodel->nodes = ZG_Malloc(&model->memgroup, sizeof(*model->nodes)*numnodes);\r\n\t\t\tmodel->planes = ZG_Malloc(&model->memgroup, sizeof(*model->planes)*numnodes);\r\n\r\n\t\t\tfor (n = 0; n < numnodes; n++)\r\n\t\t\t{\r\n\t\t\t\tdata = COM_ParseOut(data, token, sizeof(token));\r\n\t\t\t\tif (strcmp(token, \"(\"))\r\n\t\t\t\t\treturn false;\r\n\r\n\t\t\t\tmodel->nodes[n].plane = &model->planes[n];\r\n\r\n\t\t\t\tdata = COM_ParseOut(data, token, sizeof(token));\r\n\t\t\t\tmodel->planes[n].normal[0] = atof(token);\r\n\t\t\t\tdata = COM_ParseOut(data, token, sizeof(token));\r\n\t\t\t\tmodel->planes[n].normal[1] = atof(token);\r\n\t\t\t\tdata = COM_ParseOut(data, token, sizeof(token));\r\n\t\t\t\tmodel->planes[n].normal[2] = atof(token);\r\n\t\t\t\tdata = COM_ParseOut(data, token, sizeof(token));\r\n\t\t\t\tmodel->planes[n].dist = atof(token);\r\n\r\n\t\t\t\tdata = COM_ParseOut(data, token, sizeof(token));\r\n\t\t\t\tif (strcmp(token, \")\"))\r\n\t\t\t\t\treturn false;\r\n\r\n\t\t\t\tdata = COM_ParseOut(data, token, sizeof(token));\r\n\t\t\t\tmodel->nodes[n].childnum[0] = atoi(token);\r\n\t\t\t\tdata = COM_ParseOut(data, token, sizeof(token));\r\n\t\t\t\tmodel->nodes[n].childnum[1] = atoi(token);\r\n\t\t\t}\r\n\r\n\t\t\tdata = COM_ParseOut(data, token, sizeof(token));\r\n\t\t\tif (strcmp(token, \"}\"))\r\n\t\t\t\treturn false;\r\n\r\n\t\t\tMod_SetParent(model->nodes, NULL);\r\n\t\t}\r\n\t\telse if (!strcmp(token, \"interAreaPortals\"))\r\n\t\t{\r\n\t\t\t//int numareas;\r\n\t\t\tint pno, v;\r\n\t\t\tportal_t *p;\r\n\r\n\t\t\tdata = COM_ParseOut(data, token, sizeof(token));\r\n\t\t\tif (strcmp(token, \"{\"))\r\n\t\t\t\treturn false;\r\n\r\n\t\t\tdata = COM_ParseOut(data, token, sizeof(token));\r\n\t\t\tmodel->numclusters = atoi(token);\r\n\t\t\tdata = COM_ParseOut(data, token, sizeof(token));\r\n\t\t\tmodel->numportals = atoi(token);\r\n\r\n\t\t\tmodel->portal = p = ZG_Malloc(&model->memgroup, sizeof(*p) * model->numportals);\r\n\r\n\t\t\tfor (pno = 0; pno < model->numportals; pno++, p++)\r\n\t\t\t{\r\n\t\t\t\tdata = COM_ParseOut(data, token, sizeof(token));\r\n\t\t\t\tp->numpoints = atoi(token);\r\n\t\t\t\tdata = COM_ParseOut(data, token, sizeof(token));\r\n\t\t\t\tp->area[0] = atoi(token);\r\n\t\t\t\tdata = COM_ParseOut(data, token, sizeof(token));\r\n\t\t\t\tp->area[1] = atoi(token);\r\n\r\n\t\t\t\tp->points = ZG_Malloc(&model->memgroup, sizeof(*p->points) * p->numpoints);\r\n\r\n\t\t\t\tClearBounds(p->min, p->max);\r\n\t\t\t\tfor (v = 0; v < p->numpoints; v++)\r\n\t\t\t\t{\r\n\t\t\t\t\tdata = COM_ParseOut(data, token, sizeof(token));\r\n\t\t\t\t\tif (strcmp(token, \"(\"))\r\n\t\t\t\t\t\treturn false;\r\n\r\n\t\t\t\t\tdata = COM_ParseOut(data, token, sizeof(token));\r\n\t\t\t\t\tp->points[v][0] = atof(token);\r\n\t\t\t\t\tdata = COM_ParseOut(data, token, sizeof(token));\r\n\t\t\t\t\tp->points[v][1] = atof(token);\r\n\t\t\t\t\tdata = COM_ParseOut(data, token, sizeof(token));\r\n\t\t\t\t\tp->points[v][2] = atof(token);\r\n\t\t\t\t\tp->points[v][3] = 1;\r\n\r\n\t\t\t\t\tAddPointToBounds(p->points[v], p->min, p->max);\r\n\r\n\t\t\t\t\tdata = COM_ParseOut(data, token, sizeof(token));\r\n\t\t\t\t\tif (strcmp(token, \")\"))\r\n\t\t\t\t\t\treturn false;\r\n\t\t\t\t}\r\n\r\n\t\t\t}\r\n\r\n\t\t\tdata = COM_ParseOut(data, token, sizeof(token));\r\n\t\t\tif (strcmp(token, \"}\"))\r\n\t\t\t\treturn false;\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tCon_Printf(\"unexpected token %s\\n\", token);\r\n\t\t\treturn false;\r\n\t\t}\r\n\t}\r\n\r\n\treturn true;\r\n}\r\n\r\nqboolean R_CullBox (vec3_t mins, vec3_t maxs);\r\n\r\nstatic int walkno;\r\n/*fixme: convert each portal to a 2d box, because its much much simpler than true poly clipping*/\r\n/*fixme: use occlusion tests, with temporal coherance (draw the portal as black or something if we think its invisible)*/\r\nstatic void D3_WalkPortal(model_t *mod, int start, vec_t bounds[4], unsigned char *vis)\r\n{\r\n\tint i;\r\n\tportal_t *p;\r\n\tint side;\r\n\tvec_t newbounds[4];\r\n\t\r\n\tvis[start>>3] |= 1<<(start&7);\r\n\r\n\tfor (i = 0; i < mod->numportals; i++)\r\n\t{\r\n\t\tp = mod->portal+i;\r\n\t\tif (p->walkno == walkno)\r\n\t\t\tcontinue;\r\n\t\tif (p->area[0] == start)\r\n\t\t\tside = 0;\r\n\t\telse if (p->area[1] == start)\r\n\t\t\tside = 1;\r\n\t\telse\r\n\t\t\tcontinue;\r\n\r\n\t\tif (R_CullBox(p->min, p->max))\r\n\t\t{\r\n\t\t\tcontinue;\r\n\t\t}\r\n\r\n\t\tp->walkno = walkno;\r\n\t\tD3_WalkPortal(mod, p->area[!side], newbounds, vis);\r\n\t}\r\n}\r\n\r\nstatic void D3_PrepareFrame(model_t *mod, refdef_t *refdef, int inarea, int inclusters[2], pvsbuffer_t *vis, qbyte **entvis_out, qbyte **surfvis_out)\r\n{\r\n\tint start;\r\n\tstatic qbyte visbuf[256];\r\n\tqbyte *usevis;\r\n\tvec_t newbounds[4];\r\n\r\n\tint area;\r\n\tentity_t ent;\r\n\r\n\tstart = D3_ClusterForPoint(mod, refdef->vieworg, NULL);\r\n\t/*figure out which area we're in*/\r\n\tif (start < 0)\r\n\t{\r\n\t\t/*outside the world, just make it all visible, and take the fps hit*/\r\n\t\tmemset(visbuf, 255, 4);\r\n\t\tusevis = visbuf;\r\n\t}\r\n\telse if (r_novis.value)\r\n\t\tusevis = visbuf;\r\n\telse\r\n\t{\r\n\t\tmemset(visbuf, 0, 4);\r\n\t\t/*make a bounds the size of the view*/\r\n\t\tnewbounds[0] = -1;\r\n\t\tnewbounds[1] = 1;\r\n\t\tnewbounds[2] = -1;\r\n\t\tnewbounds[3] = 1;\r\n\t\twalkno++;\r\n\t\tD3_WalkPortal(mod, start, newbounds, visbuf);\r\n//\t\tCon_Printf(\"%x %x %x %x\\n\", vis[0], vis[1], vis[2], vis[3]);\r\n\t\tusevis = visbuf;\r\n\t}\r\n\r\n\t//now generate the various entities for that region.\r\n\tmemset(&ent, 0, sizeof(ent));\r\n\tfor (area = 0; area < 256*8; area++)\r\n\t{\r\n\t\tif (usevis[area>>3] & (1u<<(area&7)))\r\n\t\t{\r\n\t\t\tent.model = Mod_FindName(va(\"*_area%i\", area));\r\n\t\t\tent.scale = 1;\r\n\t\t\tAngleVectors(ent.angles, ent.axis[0], ent.axis[1], ent.axis[2]);\r\n\t\t\tVectorInverse(ent.axis[1]);\r\n\t\t\tent.shaderRGBAf[0] = 1;\r\n\t\t\tent.shaderRGBAf[1] = 1;\r\n\t\t\tent.shaderRGBAf[2] = 1;\r\n\t\t\tent.shaderRGBAf[3] = 1;\r\n\r\n\t\t\tV_AddEntity(&ent);\r\n\t\t}\r\n\t}\r\n\t*entvis_out = *surfvis_out = usevis;\r\n}\r\n\r\nstatic void D3_StainNode\t\t\t(struct model_s *model, float *parms)\r\n{\r\n}\r\n\r\nstatic void D3_LightPointValues (struct model_s *model, const vec3_t point, vec3_t res_diffuse, vec3_t res_ambient, vec3_t res_dir)\r\n{\r\n\t/*basically require rtlighting for any light*/\r\n\tVectorClear(res_diffuse);\r\n\tVectorClear(res_ambient);\r\n\tVectorClear(res_dir);\r\n\tres_dir[2] = 1;\r\n}\r\n#endif\r\n\r\n//edict system as opposed to q2 game dll system.\r\nstatic void D3_FindTouchedLeafs (struct model_s *model, struct pvscache_s *ent, const vec3_t cullmins, const vec3_t cullmaxs)\r\n{\r\n}\r\nstatic qbyte *D3_ClusterPVS (struct model_s *model, int num, pvsbuffer_t *buffer, pvsmerge_t merge)\r\n{\r\n\tmemset(buffer->buffer, 0xff, buffer->buffersize);\r\n\treturn buffer->buffer;\r\n}\r\nstatic int\tD3_ClusterForPoint (struct model_s *model, const vec3_t point, int *areaout)\r\n{\r\n\tfloat p;\r\n\tint c;\r\n\tmnode_t *node;\r\n\tnode = model->nodes;\r\n\tif (areaout)\r\n\t\t*areaout = 0;\r\n\twhile(1)\r\n\t{\r\n\t\tp = DotProduct(point, node->plane->normal) + node->plane->dist;\r\n\t\tc = node->childnum[p<0];\r\n\t\tif (c <= 0)\r\n\t\t\treturn -1-c;\r\n\t\tnode = model->nodes + c;\r\n\t}\r\n\treturn 0;\r\n}\r\nstatic unsigned int D3_FatPVS (struct model_s *model, const vec3_t org, pvsbuffer_t *pvsbuffer, qboolean merge)\r\n{\r\n\treturn 0;\r\n}\r\n\r\nstatic qboolean D3_EdictInFatPVS (struct model_s *model, const struct pvscache_s *edict, const qbyte *pvsbuffer, const int *areas)\r\n{\r\n\tint i;\r\n\tfor (i = 0; i < edict->num_leafs; i++)\r\n\t\tif (pvsbuffer[edict->leafnums[i]>>3] & (1u<<(edict->leafnums[i]&7)))\r\n\t\t\treturn true;\r\n\treturn false;\r\n}\r\n\r\n\r\n\r\n\r\n\r\n\r\ntypedef struct cm_surface_s\r\n{\r\n\tvec3_t mins, maxs;\r\n\tvec4_t plane;\r\n\tint numedges;\r\n\tvec4_t *edge;\r\n\r\n//\tshader_t *shader;\r\n\tstruct cm_surface_s *next;\r\n} cm_surface_t;\r\n\r\ntypedef struct cm_brush_s\r\n{\r\n\tvec3_t mins, maxs;\r\n\tint numplanes;\r\n\tvec4_t *plane;\r\n\tunsigned int contents;\r\n\tstruct cm_brush_s *next;\r\n} cm_brush_t;\r\n\r\ntypedef struct cm_node_s\r\n{\r\n\tint axis; /*0=x,1=y,2=z*/\r\n\tfloat dist;\r\n\tvec3_t mins, maxs;\r\n\tstruct cm_node_s *parent;\r\n\tstruct cm_node_s *child[2];\r\n\r\n\tcm_brush_t *brushlist;\r\n\tcm_surface_t *surfacelist;\r\n} cm_node_t;\r\n\r\nstatic struct\r\n{\r\n\tfloat truefraction;\r\n\r\n\tqboolean ispoint;\r\n\tvec3_t start;\r\n\tvec3_t end;\r\n\tvec3_t absmins, absmaxs;\r\n\tvec3_t szmins, szmaxs;\r\n\tvec3_t extents;\r\n\r\n\tcm_surface_t *surf;\r\n} traceinfo;\r\n\r\n#define\tDIST_EPSILON\t(0.03125)\r\n\r\nstatic void D3_TraceToLeaf (cm_node_t *leaf)\r\n{\r\n\tfloat diststart;\r\n\tfloat distend;\r\n\tfloat frac;\r\n\tvec3_t impactpoint;\r\n\tqboolean back;\r\n\tint i, j;\r\n\tfloat pdist, expand;\r\n\tvec3_t ofs;\r\n\r\n\tcm_surface_t *surf;\r\n\tfor (surf = leaf->surfacelist; surf; surf = surf->next)\r\n\t{\r\n\t\t/*lots of maths in this function, we should check the surf's bbox*/\r\n\t\tif (surf->mins[0] > traceinfo.absmaxs[0] || traceinfo.absmins[0] > surf->maxs[0] ||\r\n\t\t\tsurf->mins[1] > traceinfo.absmaxs[1] || traceinfo.absmins[1] > surf->maxs[1] ||\r\n\t\t\tsurf->mins[2] > traceinfo.absmaxs[2] || traceinfo.absmins[2] > surf->maxs[2])\r\n\t\t\tcontinue;\r\n\r\n\t\tif (!traceinfo.ispoint)\r\n\t\t{\t// general box case\r\n\r\n\t\t\t// push the plane out apropriately for mins/maxs\r\n\r\n\t\t\t// FIXME: use signbits into 8 way lookup for each mins/maxs\r\n\t\t\tfor (i=0 ; i<3 ; i++)\r\n\t\t\t{\r\n\t\t\t\tif (surf->plane[i] < 0)\r\n\t\t\t\t\tofs[i] = traceinfo.szmaxs[i];\r\n\t\t\t\telse\r\n\t\t\t\t\tofs[i] = traceinfo.szmins[i];\r\n\t\t\t}\r\n\t\t\texpand = DotProduct (ofs, surf->plane);\r\n//\t\t\tpdist = surf->plane[3] - expand;\r\n\t\t}\r\n\t\telse\r\n\t\t{\t// special point case\r\n//\t\t\tpdist = surf->plane[3];\r\n\t\t\texpand = 0;\r\n\t\t}\r\n\r\n\t\tdiststart = DotProduct(traceinfo.start, surf->plane);\r\n\t\t/*started behind?*/\r\n\t\tback = diststart < surf->plane[3];\r\n\t\tif (diststart <= surf->plane[3]-expand)\r\n\t\t{\r\n\t\t\t/*the trace started behind our expanded front plane*/\r\n\r\n\t\t\t/*don't stop just because the point is closer than the extended plane*/\r\n\t\t\t/*epsilon here please*/\r\n\t\t\tif (diststart <= surf->plane[3])\r\n\t\t\t\tcontinue;\r\n\r\n\t\t\tdistend = DotProduct(traceinfo.end, surf->plane);\r\n\t\t\tif (distend < diststart)\r\n\t\t\t\tfrac = 0; /*don't let us go further into the wall*/\r\n\t\t\telse\r\n\t\t\t\tcontinue;\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tdistend = DotProduct(traceinfo.end, surf->plane);\r\n\t\t\t/*ended on the other side*/\r\n\t\t\tif (back)\r\n\t\t\t{\r\n\t\t\t\tif (distend+expand > -surf->plane[3])\r\n\t\t\t\t\tcontinue;\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tif (distend+expand > surf->plane[3])\r\n\t\t\t\t\tcontinue;\r\n\t\t\t}\r\n\r\n\t\t\tif (diststart == distend)\r\n\t\t\t\tfrac = 0;\r\n\t\t\telse\r\n\t\t\t\tfrac = (diststart - (surf->plane[3]-expand)) / (diststart-distend);\r\n\t\t}\r\n\r\n\t\t/*give up if we already found a closer plane*/\r\n\t\tif (frac >= traceinfo.truefraction)\r\n\t\t\tcontinue;\r\n\r\n\t\t/*okay, this is where it hits this plane*/\r\n\t\timpactpoint[0] = traceinfo.start[0] + frac*(traceinfo.end[0] - traceinfo.start[0]);\r\n\t\timpactpoint[1] = traceinfo.start[1] + frac*(traceinfo.end[1] - traceinfo.start[1]);\r\n\t\timpactpoint[2] = traceinfo.start[2] + frac*(traceinfo.end[2] - traceinfo.start[2]);\r\n\r\n\t\t/*if the impact was not on the surface*/\r\n\t\tfor (i = 0; i < surf->numedges; i++)\r\n\t\t{\r\n\t\t\tif (!traceinfo.ispoint)\r\n\t\t\t{\t// general box case\r\n\r\n\t\t\t\t// push the plane out apropriately for mins/maxs\r\n\r\n\t\t\t\t// FIXME: use signbits into 8 way lookup for each mins/maxs\r\n\t\t\t\tfor (j=0 ; j<3 ; j++)\r\n\t\t\t\t{\r\n\t\t\t\t\tif (surf->edge[i][j] < 0)\r\n\t\t\t\t\t\tofs[j] = traceinfo.szmaxs[j];\r\n\t\t\t\t\telse\r\n\t\t\t\t\t\tofs[j] = traceinfo.szmins[j];\r\n\t\t\t\t}\r\n\t\t\t\tpdist = DotProduct (ofs, surf->edge[i]);\r\n\t\t\t\tpdist = surf->edge[i][3] - pdist;\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\t// special point case\r\n\t\t\t\tpdist = surf->edge[i][3];\r\n\t\t\t}\r\n\r\n\t\t\tif (DotProduct(impactpoint, surf->edge[i]) > pdist)\r\n\t\t\t{\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\t\t}\r\n\t\t/*if we were inside all edges, we hit the surface*/\r\n\t\tif (i == surf->numedges)\r\n\t\t{\r\n\r\n\t\t\ttraceinfo.truefraction = frac;\r\n\t\t\ttraceinfo.surf = surf;\r\n\t\t\t\r\n\t\t\t/*we can't early out. there are multiple surfs in each leaf, and they could overlap. earlying out will result in errors if we hit a further one before the nearer*/\r\n\t\t}\r\n\t}\r\n}\r\n\r\n/*returns the most distant node which contains the entire box*/\r\nstatic cm_node_t *D3_ChildNodeForBox(cm_node_t *node, vec3_t mins, vec3_t maxs)\r\n{\r\n\tfloat t1, t2;\r\n\tfor(;;)\r\n\t{\r\n\t\tt1 = mins[node->axis] - node->dist;\r\n\t\tt2 = maxs[node->axis] - node->dist;\r\n\r\n\t\t//if its completely to one side, walk down that side\r\n\t\tif (t1 > maxs[node->axis] && t2 > maxs[node->axis])\r\n\t\t{\r\n\t\t\t//if this is a leaf, we can't insert in a child anyway.\r\n\t\t\tif (!node->child[0])\r\n\t\t\t\tbreak;\r\n\t\t\tnode = node->child[0];\r\n\t\t\tcontinue;\r\n\t\t}\r\n\t\tif (t1 < mins[node->axis] && t2 < mins[node->axis])\r\n\t\t{\r\n\t\t\t//if this is a leaf, we can't insert in a child anyway.\r\n\t\t\tif (!node->child[1])\r\n\t\t\t\tbreak;\r\n\t\t\tnode = node->child[1];\r\n\t\t\tcontinue;\r\n\t\t}\r\n\r\n\t\t//the box crosses this node\r\n\t\tbreak;\r\n\t}\r\n\r\n\treturn node;\r\n}\r\n\r\nstatic void D3_InsertClipSurface(cm_node_t *node, cm_surface_t *surf)\r\n{\r\n\tnode = D3_ChildNodeForBox(node, surf->mins, surf->maxs);\r\n\r\n\tsurf->next = node->surfacelist;\r\n\tnode->surfacelist = surf;\r\n}\r\nstatic void D3_InsertClipBrush(cm_node_t *node, cm_brush_t *brush)\r\n{\r\n\tnode = D3_ChildNodeForBox(node, brush->mins, brush->maxs);\r\n\r\n\tbrush->next = node->brushlist;\r\n\tnode->brushlist = brush;\r\n}\r\n\r\nstatic void D3_RecursiveSurfCheck (cm_node_t *node, float p1f, float p2f, const vec3_t p1, const vec3_t p2)\r\n{\r\n\tfloat\t\tt1, t2, offset;\r\n\tfloat\t\tfrac, frac2;\r\n\tfloat\t\tidist;\r\n\tint\t\t\ti;\r\n\tvec3_t\t\tmid;\r\n\tint\t\t\tside;\r\n\tfloat\t\tmidf;\r\n\r\n\tif (traceinfo.truefraction <= p1f)\r\n\t\treturn;\t\t// already hit something nearer\r\n\r\n\t/*err, no child here*/\r\n\tif (!node)\r\n\t\treturn;\r\n\r\n\tD3_TraceToLeaf (node);\r\n\r\n\t//\r\n\t// find the point distances to the seperating plane\r\n\t// and the offset for the size of the box\r\n\t//\r\n\r\n\tt1 = p1[node->axis] - node->dist;\r\n\tt2 = p2[node->axis] - node->dist;\r\n\toffset = traceinfo.extents[node->axis];\r\n\r\n#if 0\r\nD3_RecursiveHullCheck (node->childnum[0], p1f, p2f, p1, p2);\r\nD3_RecursiveHullCheck (node->childnum[1], p1f, p2f, p1, p2);\r\nreturn;\r\n#endif\r\n\r\n\t// see which sides we need to consider\r\n\tif (t1 >= offset && t2 >= offset)\r\n\t{\r\n\t\tD3_RecursiveSurfCheck (node->child[0], p1f, p2f, p1, p2);\r\n\t\treturn;\r\n\t}\r\n\tif (t1 < -offset && t2 < -offset)\r\n\t{\r\n\t\tD3_RecursiveSurfCheck ( node->child[1], p1f, p2f, p1, p2);\r\n\t\treturn;\r\n\t}\r\n\r\n\t// put the crosspoint DIST_EPSILON pixels on the near side\r\n\tif (t1 < t2)\r\n\t{\r\n\t\tidist = 1.0/(t1-t2);\r\n\t\tside = 1;\r\n\t\tfrac2 = (t1 + offset + DIST_EPSILON)*idist;\r\n\t\tfrac = (t1 - offset + DIST_EPSILON)*idist;\r\n\t}\r\n\telse if (t1 > t2)\r\n\t{\r\n\t\tidist = 1.0/(t1-t2);\r\n\t\tside = 0;\r\n\t\tfrac2 = (t1 - offset - DIST_EPSILON)*idist;\r\n\t\tfrac = (t1 + offset + DIST_EPSILON)*idist;\r\n\t}\r\n\telse\r\n\t{\r\n\t\tside = 0;\r\n\t\tfrac = 1;\r\n\t\tfrac2 = 0;\r\n\t}\r\n\r\n\t// move up to the node\r\n\tif (frac < 0)\r\n\t\tfrac = 0;\r\n\tif (frac > 1)\r\n\t\tfrac = 1;\r\n\r\n\tmidf = p1f + (p2f - p1f)*frac;\r\n\tfor (i=0 ; i<3 ; i++)\r\n\t\tmid[i] = p1[i] + frac*(p2[i] - p1[i]);\r\n\r\n\tD3_RecursiveSurfCheck (node->child[side], p1f, midf, p1, mid);\r\n\r\n\r\n\t// go past the node\r\n\tif (frac2 < 0)\r\n\t\tfrac2 = 0;\r\n\tif (frac2 > 1)\r\n\t\tfrac2 = 1;\r\n\r\n\tmidf = p1f + (p2f - p1f)*frac2;\r\n\tfor (i=0 ; i<3 ; i++)\r\n\t\tmid[i] = p1[i] + frac2*(p2[i] - p1[i]);\r\n\r\n\tD3_RecursiveSurfCheck (node->child[side^1], midf, p2f, mid, p2);\r\n}\r\n\r\nstatic qboolean D3_Trace (struct model_s *model, int hulloverride, const framestate_t *framestate, const vec3_t axis[3], const vec3_t p1, const vec3_t p2, const vec3_t mins, const vec3_t maxs, qboolean capsule, unsigned int hitcontentsmask, struct trace_s *trace)\r\n{\r\n\tint i;\r\n\tfloat e1,e2;\r\n\ttraceinfo.truefraction = 1;\r\n\tVectorCopy(p1, traceinfo.start);\r\n\tVectorCopy(p2, traceinfo.end);\r\n\tfor (i = 0; i < 3; i++)\r\n\t{\r\n\t\te1 = fabs(mins[i]);\r\n\t\te2 = fabs(maxs[i]);\r\n\t\ttraceinfo.extents[i] = ((e1>e2)?e1:e2);\r\n\t\ttraceinfo.szmins[i] = mins[i];\r\n\t\ttraceinfo.szmaxs[i] = maxs[i];\r\n\r\n\t\ttraceinfo.absmins[i] = ((p1[i]<p2[i])?p1[i]:p2[i]) + mins[i];\r\n\t\ttraceinfo.absmaxs[i] = ((p1[i]>p2[i])?p1[i]:p2[i]) + maxs[i];\r\n\t}\r\n\ttraceinfo.ispoint = !traceinfo.extents[0] && !traceinfo.extents[1] && !traceinfo.extents[2];\r\n\r\n\ttraceinfo.surf = NULL;\r\n\r\n\tD3_RecursiveSurfCheck(model->cnodes, 0, 1, p1, p2);\r\n\r\n\tmemset(trace, 0, sizeof(*trace));\r\n\tif (!traceinfo.surf)\r\n\t{\r\n\t\ttrace->fraction = 1;\r\n\t\tVectorCopy(p2, trace->endpos);\r\n\t}\r\n\telse\r\n\t{\r\n\t\tfloat frac;\r\n\t\t/*we now know which surface it hit. recalc the impact point, but with an epsilon this time, so we can never get too close to the surface*/\r\n\r\n\t\tVectorCopy(traceinfo.surf->plane, trace->plane.normal);\r\n\t\tif (!traceinfo.ispoint)\r\n\t\t{\t// general box case\r\n\t\t\tvec3_t ofs;\r\n\t\t\t// push the plane out apropriately for mins/maxs\r\n\r\n\t\t\t// FIXME: use signbits into 8 way lookup for each mins/maxs\r\n\t\t\tfor (i=0 ; i<3 ; i++)\r\n\t\t\t{\r\n\t\t\t\tif (traceinfo.surf->plane[i] < 0)\r\n\t\t\t\t\tofs[i] = traceinfo.szmaxs[i];\r\n\t\t\t\telse\r\n\t\t\t\t\tofs[i] = traceinfo.szmins[i];\r\n\t\t\t}\r\n\t\t\te1 = DotProduct (ofs, traceinfo.surf->plane);\r\n\t\t\ttrace->plane.dist = traceinfo.surf->plane[3] - e1;\r\n\t\t}\r\n\t\telse\r\n\t\t{\t// special point case\r\n\t\t\ttrace->plane.dist = traceinfo.surf->plane[3];\r\n\t\t}\r\n\r\n\t\tfrac = traceinfo.truefraction;\r\n\t\t/*\r\n\t\tdiststart = DotProduct(traceinfo.start, trace->plane.normal);\r\n\t\tdistend = DotProduct(traceinfo.end, trace->plane.normal);\r\n\t\tif (diststart == distend)\r\n\t\t\tfrac = 0;\r\n\t\telse\r\n\t\t{\r\n\t\t\tfrac = (diststart - trace->plane.dist) / (diststart-distend);\r\n\t\t\tif (frac < 0)\r\n\t\t\t\tfrac = 0;\r\n\t\t\telse if (frac > 1)\r\n\t\t\t\tfrac = 1;\r\n\t\t}*/\r\n\r\n\t\t/*okay, this is where it hits this plane*/\r\n\t\ttrace->endpos[0] = traceinfo.start[0] + frac*(traceinfo.end[0] - traceinfo.start[0]);\r\n\t\ttrace->endpos[1] = traceinfo.start[1] + frac*(traceinfo.end[1] - traceinfo.start[1]);\r\n\t\ttrace->endpos[2] = traceinfo.start[2] + frac*(traceinfo.end[2] - traceinfo.start[2]);\r\n\t\ttrace->fraction = frac;\r\n\t}\r\n\ttrace->ent = NULL;\r\n\treturn false;\r\n}\r\n\r\nstatic unsigned int D3_PointContents (struct model_s *model, const vec3_t axis[3], const vec3_t p)\r\n{\r\n\tcm_node_t *node = model->cnodes;\r\n\tcm_brush_t *brush;\r\n\tfloat t1;\r\n\tunsigned int contents = 0;\r\n\tint i;\r\n\tvec3_t np;\r\n\r\n\tif (axis)\r\n\t{\r\n\t\tnp[0] = DotProduct(p, axis[0]);\r\n\t\tnp[1] = DotProduct(p, axis[1]);\r\n\t\tnp[2] = DotProduct(p, axis[2]);\r\n\t\tp = np;\r\n\t}\r\n\r\n\twhile(node)\r\n\t{\r\n\t\tfor (brush = node->brushlist; brush; brush = brush->next)\r\n\t\t{\r\n\t\t\tif (brush->mins[0] > p[0] || p[0] > brush->maxs[0] ||\r\n\t\t\t\tbrush->mins[1] > p[1] || p[1] > brush->maxs[1] ||\r\n\t\t\t\tbrush->mins[2] > p[2] || p[2] > brush->maxs[2])\r\n\t\t\t\tcontinue;\r\n\r\n\t\t\tfor (i = 0; i < brush->numplanes; i++)\r\n\t\t\t{\r\n\t\t\t\tif (DotProduct(p, brush->plane[i]) > brush->plane[i][3])\r\n\t\t\t\t\tbreak;\r\n\t\t\t}\r\n\t\t\tif (i == brush->numplanes)\r\n\t\t\t\tcontents |= brush->contents;\r\n\t\t}\r\n\r\n\t\tt1 = p[node->axis] - node->dist;\r\n\r\n\t\t// see which side we need to go down\r\n\t\tif (t1 >= 0)\r\n\t\t{\r\n\t\t\tnode = node->child[0];\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tnode = node->child[1];\r\n\t\t}\r\n\t}\r\n\r\n\treturn contents;\r\n}\r\n\r\n#define ensurenewtoken(t) buf = COM_ParseOut(buf, token, sizeof(token)); if (strcmp(token, t)) break\r\n\r\nstatic int D3_ParseContents(char *str)\r\n{\r\n\tchar *e, *n;\r\n\tunsigned int contents = 0;\r\n\twhile(str)\r\n\t{\r\n\t\te = strchr(str, ',');\r\n\t\tif (e)\r\n\t\t{\r\n\t\t\t*e = 0;\r\n\t\t\tn = e+1;\r\n\t\t}\r\n\t\telse \r\n\t\t\tn = NULL;\r\n\r\n\t\tif (!strcmp(str, \"solid\") || !strcmp(str, \"opaque\"))\r\n\t\t\tcontents |= FTECONTENTS_SOLID;\r\n\t\telse if (!strcmp(str, \"playerclip\"))\r\n\t\t\tcontents |= FTECONTENTS_PLAYERCLIP;\r\n\t\telse if (!strcmp(str, \"monsterclip\"))\r\n\t\t\tcontents |= FTECONTENTS_PLAYERCLIP;\r\n\t\telse\r\n\t\t\tCon_Printf(\"Unknown contents type \\\"%s\\\"\\n\", str);\r\n\t\tstr = n;\r\n\t}\r\n\treturn contents;\r\n}\r\ntypedef struct\r\n{\r\n\tint v[2];\r\n\tint fl[2];\r\n} d3edge_t;\r\nqboolean QDECL D3_LoadMap_CollisionMap(model_t *mod, void *buf, size_t bufsize)\r\n{\r\n\tint pedges[64];\r\n\tcm_surface_t *surf;\r\n\tcm_brush_t *brush;\r\n\tvec3_t *verts;\r\n\td3edge_t *edges;\r\n\tint i, j;\r\n\tint filever = 0;\r\n\tint numverts, numedges, numpedges;\r\n\tmodel_t *cmod;\r\n\tchar token[256];\r\n\tbuf = COM_ParseOut(buf, token, sizeof(token));\r\n\tif (strcmp(token, \"CM\"))\r\n\t\treturn false;\r\n\t\r\n\tbuf = COM_ParseOut(buf, token, sizeof(token));\r\n\tfilever = atof(token);\r\n\tif (filever != 1 && filever != 3)\r\n\t\treturn false;\r\n\r\n\tbuf = COM_ParseOut(buf, token, sizeof(token));\r\n\t/*some number, discard*/\r\n\r\n\twhile(buf)\r\n\t{\r\n\t\tbuf = COM_ParseOut(buf, token, sizeof(token));\r\n\t\tif (!strcmp(token, \"collisionModel\"))\r\n\t\t{\r\n\t\t\tbuf = COM_ParseOut(buf, token, sizeof(token));\r\n\t\t\tif (!strcmp(token, \"worldMap\"))\r\n\t\t\t\tcmod = mod;\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tQ_strncatz(token, \":\", sizeof(token));\r\n\t\t\t\tQ_strncatz(token, mod->publicname, sizeof(token));\r\n\t\t\t\tcmod = Mod_FindName(token);\r\n\t\t\t\tif (cmod->loadstate != MLS_NOTLOADED)\r\n\t\t\t\t\treturn false;\r\n\t\t\t}\r\n\r\n\t\t\tif (filever == 3)\r\n\t\t\t{\r\n\t\t\t\tbuf = COM_ParseOut(buf, token, sizeof(token));\r\n\t\t\t\t/*don't know*/\r\n\t\t\t}\r\n\r\n\t\t\tClearBounds(cmod->mins, cmod->maxs);\r\n\t\t\tensurenewtoken(\"{\");\r\n\t\t\tensurenewtoken(\"vertices\");\r\n\t\t\tensurenewtoken(\"{\");\r\n\t\t\t\tbuf = COM_ParseOut(buf, token, sizeof(token));\r\n\t\t\t\tnumverts = atoi(token);\r\n\t\t\t\tverts = malloc(numverts * sizeof(*verts));\r\n\t\t\t\tfor (i = 0; i < numverts; i++)\r\n\t\t\t\t{\r\n\t\t\t\t\tensurenewtoken(\"(\");\r\n\t\t\t\t\tbuf = COM_ParseOut(buf, token, sizeof(token));\r\n\t\t\t\t\tverts[i][0] = atof(token);\r\n\t\t\t\t\tbuf = COM_ParseOut(buf, token, sizeof(token));\r\n\t\t\t\t\tverts[i][1] = atof(token);\r\n\t\t\t\t\tbuf = COM_ParseOut(buf, token, sizeof(token));\r\n\t\t\t\t\tverts[i][2] = atof(token);\r\n\t\t\t\t\tensurenewtoken(\")\");\r\n\t\t\t\t}\r\n\t\t\tensurenewtoken(\"}\");\r\n\t\t\tensurenewtoken(\"edges\");\r\n\t\t\tensurenewtoken(\"{\");\r\n\t\t\t\tbuf = COM_ParseOut(buf, token, sizeof(token));\r\n\t\t\t\tnumedges = atoi(token);\r\n\t\t\t\tedges = malloc(numedges * sizeof(*edges));\r\n\t\t\t\tfor (i = 0; i < numedges; i++)\r\n\t\t\t\t{\r\n\t\t\t\t\tensurenewtoken(\"(\");\r\n\t\t\t\t\tbuf = COM_ParseOut(buf, token, sizeof(token));\r\n\t\t\t\t\tedges[i].v[0] = atoi(token);\r\n\t\t\t\t\tbuf = COM_ParseOut(buf, token, sizeof(token));\r\n\t\t\t\t\tedges[i].v[1] = atoi(token);\r\n\t\t\t\t\tensurenewtoken(\")\");\r\n\t\t\t\t\tbuf = COM_ParseOut(buf, token, sizeof(token));\r\n\t\t\t\t\tedges[i].fl[0] = atoi(token);\r\n\t\t\t\t\tbuf = COM_ParseOut(buf, token, sizeof(token));\r\n\t\t\t\t\tedges[i].fl[1] = atoi(token);\r\n\t\t\t\t}\r\n\t\t\tensurenewtoken(\"}\");\r\n\t\t\tensurenewtoken(\"nodes\");\r\n\t\t\tensurenewtoken(\"{\");\r\n\t\t\tcmod->cnodes = ZG_Malloc(&mod->memgroup, sizeof(cm_node_t));\r\n\t\t\tfor (;;)\r\n\t\t\t{\r\n\t\t\t\tbuf = COM_ParseOut(buf, token, sizeof(token));\r\n\t\t\t\tif (strcmp(token, \"(\"))\r\n\t\t\t\t\tbreak;\r\n\r\n\t\t\t\tbuf = COM_ParseOut(buf, token, sizeof(token));\r\n\t\t\t\tbuf = COM_ParseOut(buf, token, sizeof(token));\r\n\t\t\t\t//axis, dist\r\n\t\t\t\tensurenewtoken(\")\");\r\n\t\t\t}\r\n\t\t\tif (strcmp(token, \"}\"))\r\n\t\t\t\tbreak;\r\n\r\n\t\t\tensurenewtoken(\"polygons\");\r\n\t\t\tif (filever == 1)\r\n\t\t\t{\r\n\t\t\t\tbuf = COM_ParseOut(buf, token, sizeof(token));\r\n\t\t\t\t/*'polygonMemory', which is unusable for us*/\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tbuf = COM_ParseOut(buf, token, sizeof(token));\r\n\t\t\t\t/*numPolygons*/\r\n\t\t\t\tbuf = COM_ParseOut(buf, token, sizeof(token));\r\n\t\t\t\t/*numPolygonEdges*/\r\n\t\t\t}\r\n\t\t\tensurenewtoken(\"{\");\r\n\t\t\tfor (;;)\r\n\t\t\t{\r\n\t\t\t\tbuf = COM_ParseOut(buf, token, sizeof(token));\r\n\t\t\t\tif (!strcmp(token, \"}\"))\r\n\t\t\t\t\tbreak;\r\n\r\n\t\t\t\tnumpedges = atoi(token);\r\n\t\t\t\tsurf = ZG_Malloc(&mod->memgroup, sizeof(*surf) + sizeof(vec4_t)*numpedges);\r\n\t\t\t\tsurf->numedges = numpedges;\r\n\t\t\t\tsurf->edge = (vec4_t*)(surf+1);\r\n\r\n\t\t\t\tensurenewtoken(\"(\");\r\n\t\t\t\tfor (j = 0; j < numpedges; j++)\r\n\t\t\t\t{\r\n\t\t\t\t\tbuf = COM_ParseOut(buf, token, sizeof(token));\r\n\t\t\t\t\tpedges[j] = atoi(token);\r\n\t\t\t\t}\r\n\t\t\t\tensurenewtoken(\")\");\r\n\t\t\t\tensurenewtoken(\"(\");\r\n\t\t\t\tbuf = COM_ParseOut(buf, token, sizeof(token));\r\n\t\t\t\tsurf->plane[0] = atof(token);\r\n\t\t\t\tbuf = COM_ParseOut(buf, token, sizeof(token));\r\n\t\t\t\tsurf->plane[1] = atof(token);\r\n\t\t\t\tbuf = COM_ParseOut(buf, token, sizeof(token));\r\n\t\t\t\tsurf->plane[2] = atof(token);\r\n\t\t\t\tensurenewtoken(\")\");\r\n\t\t\t\tbuf = COM_ParseOut(buf, token, sizeof(token));\r\n\t\t\t\tsurf->plane[3] = atof(token);\r\n\r\n\t\t\t\tensurenewtoken(\"(\");\r\n\t\t\t\tbuf = COM_ParseOut(buf, token, sizeof(token));\r\n\t\t\t\tsurf->mins[0] = atof(token);\r\n\t\t\t\tbuf = COM_ParseOut(buf, token, sizeof(token));\r\n\t\t\t\tsurf->mins[1] = atof(token);\r\n\t\t\t\tbuf = COM_ParseOut(buf, token, sizeof(token));\r\n\t\t\t\tsurf->mins[2] = atof(token);\r\n\t\t\t\tensurenewtoken(\")\");\r\n\t\t\t\r\n\t\t\t\tensurenewtoken(\"(\");\r\n\t\t\t\tbuf = COM_ParseOut(buf, token, sizeof(token));\r\n\t\t\t\tsurf->maxs[0] = atof(token);\r\n\t\t\t\tbuf = COM_ParseOut(buf, token, sizeof(token));\r\n\t\t\t\tsurf->maxs[1] = atof(token);\r\n\t\t\t\tbuf = COM_ParseOut(buf, token, sizeof(token));\r\n\t\t\t\tsurf->maxs[2] = atof(token);\r\n\t\t\t\tensurenewtoken(\")\");\r\n\r\n\t\t\t\tbuf = COM_ParseOut(buf, token, sizeof(token));\r\n#ifdef HAVE_CLIENT\r\n//\t\t\t\tsurf->shader = R_RegisterShader_Vertex(token);\r\n//\t\t\t\tR_BuildDefaultTexnums_Doom3(NULL, surf->shader);\r\n#endif\r\n\r\n\t\t\t\tif (filever == 3)\r\n\t\t\t\t{\r\n\t\t\t\t\tensurenewtoken(\"(\");\r\n\t\t\t\t\tbuf = COM_ParseOut(buf, token, sizeof(token));\r\n\t\t\t\t\tbuf = COM_ParseOut(buf, token, sizeof(token));\r\n\t\t\t\t\tensurenewtoken(\")\");\r\n\r\n\t\t\t\t\tensurenewtoken(\"(\");\r\n\t\t\t\t\tbuf = COM_ParseOut(buf, token, sizeof(token));\r\n\t\t\t\t\tbuf = COM_ParseOut(buf, token, sizeof(token));\r\n\t\t\t\t\tensurenewtoken(\")\");\r\n\r\n\t\t\t\t\tensurenewtoken(\"(\");\r\n\t\t\t\t\tbuf = COM_ParseOut(buf, token, sizeof(token));\r\n\t\t\t\t\tbuf = COM_ParseOut(buf, token, sizeof(token));\r\n\t\t\t\t\tensurenewtoken(\")\");\r\n\r\n\t\t\t\t\tbuf = COM_ParseOut(buf, token, sizeof(token));\r\n\t\t\t\t}\r\n\r\n\t\t\t\tfor (j = 0; j < numpedges; j++)\r\n\t\t\t\t{\r\n\t\t\t\t\tfloat *v1, *v2;\r\n\t\t\t\t\tvec3_t dir;\r\n\t\t\t\t\tif (pedges[j] < 0)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tv2 = verts[edges[-pedges[j]].v[0]];\r\n\t\t\t\t\t\tv1 = verts[edges[-pedges[j]].v[1]];\r\n\t\t\t\t\t}\r\n\t\t\t\t\telse\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tv1 = verts[edges[pedges[j]].v[0]];\r\n\t\t\t\t\t\tv2 = verts[edges[pedges[j]].v[1]];\r\n\t\t\t\t\t}\r\n\t\t\t\t\tVectorSubtract(v1, v2, dir);\r\n\t\t\t\t\tVectorNormalize(dir);\r\n\t\t\t\t\tCrossProduct(surf->plane, dir, surf->edge[j]);\r\n\t\t\t\t\tsurf->edge[j][3] = DotProduct(v1, surf->edge[j]);\r\n\r\n\t\t\t\t\tsurf->edge[j][3] += DIST_EPSILON;\r\n\t\t\t\t}\r\n\r\n\t\t\t\tD3_InsertClipSurface(cmod->cnodes, surf);\r\n\r\n\t\t\t\tAddPointToBounds(surf->mins, cmod->mins, cmod->maxs);\r\n\t\t\t\tAddPointToBounds(surf->maxs, cmod->mins, cmod->maxs);\r\n\t\t\t}\r\n\t\t\tfree(verts);\r\n\t\t\tfree(edges);\r\n\r\n\t\t\tensurenewtoken(\"brushes\");\r\n\t\t\tif (filever == 1)\r\n\t\t\t{\r\n\t\t\t\tbuf = COM_ParseOut(buf, token, sizeof(token));\r\n\t\t\t\t/*'brushMemory', which is unusable for us*/\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tbuf = COM_ParseOut(buf, token, sizeof(token));\r\n\t\t\t\t/*numBrushes */\r\n\t\t\t\tbuf = COM_ParseOut(buf, token, sizeof(token));\r\n\t\t\t\t/*numBrushPlanes*/\r\n\t\t\t}\r\n\t\t\tensurenewtoken(\"{\");\r\n\t\t\tfor (;;)\r\n\t\t\t{\r\n\t\t\t\tbuf = COM_ParseOut(buf, token, sizeof(token));\r\n\t\t\t\tif (!strcmp(token, \"}\"))\r\n\t\t\t\t\tbreak;\r\n\t\t\t\tj = atoi(token);\r\n\t\t\t\tbrush = ZG_Malloc(&mod->memgroup, j*sizeof(vec4_t) + sizeof(*brush));\r\n\t\t\t\tbrush->numplanes = j;\r\n\t\t\t\tbrush->plane = (vec4_t*)(brush+1);\r\n\t\t\t\tensurenewtoken(\"{\");\r\n\t\t\t\tfor (i = 0; i < brush->numplanes; i++)\r\n\t\t\t\t{\r\n\t\t\t\t\tensurenewtoken(\"(\");\r\n\t\t\t\t\tbuf = COM_ParseOut(buf, token, sizeof(token));\r\n\t\t\t\t\tbrush->plane[i][0] = atof(token);\r\n\t\t\t\t\tbuf = COM_ParseOut(buf, token, sizeof(token));\r\n\t\t\t\t\tbrush->plane[i][1] = atof(token);\r\n\t\t\t\t\tbuf = COM_ParseOut(buf, token, sizeof(token));\r\n\t\t\t\t\tbrush->plane[i][2] = atof(token);\r\n\t\t\t\t\tensurenewtoken(\")\");\r\n\t\t\t\t\tbuf = COM_ParseOut(buf, token, sizeof(token));\r\n\t\t\t\t\tbrush->plane[i][3] = atof(token);\r\n\t\t\t\t}\r\n\t\t\t\tensurenewtoken(\"}\");\r\n\r\n\t\t\t\tensurenewtoken(\"(\");\r\n\t\t\t\tbuf = COM_ParseOut(buf, token, sizeof(token));\r\n\t\t\t\tbrush->mins[0] = atof(token);\r\n\t\t\t\tbuf = COM_ParseOut(buf, token, sizeof(token));\r\n\t\t\t\tbrush->mins[1] = atof(token);\r\n\t\t\t\tbuf = COM_ParseOut(buf, token, sizeof(token));\r\n\t\t\t\tbrush->mins[2] = atof(token);\r\n\t\t\t\tensurenewtoken(\")\");\r\n\r\n\t\t\t\tensurenewtoken(\"(\");\r\n\t\t\t\tbuf = COM_ParseOut(buf, token, sizeof(token));\r\n\t\t\t\tbrush->maxs[0] = atof(token);\r\n\t\t\t\tbuf = COM_ParseOut(buf, token, sizeof(token));\r\n\t\t\t\tbrush->maxs[1] = atof(token);\r\n\t\t\t\tbuf = COM_ParseOut(buf, token, sizeof(token));\r\n\t\t\t\tbrush->maxs[2] = atof(token);\r\n\t\t\t\tensurenewtoken(\")\");\r\n\r\n\t\t\t\tbuf = COM_ParseOut(buf, token, sizeof(token));\r\n\t\t\t\tbrush->contents = D3_ParseContents(token);\r\n\r\n\t\t\t\tif (filever == 3)\r\n\t\t\t\t\tbuf = COM_ParseOut(buf, token, sizeof(token));\r\n\r\n\t\t\t\tD3_InsertClipBrush(cmod->cnodes, brush);\r\n\r\n\t\t\t\tAddPointToBounds(brush->mins, cmod->mins, cmod->maxs);\r\n\t\t\t\tAddPointToBounds(brush->maxs, cmod->mins, cmod->maxs);\r\n\t\t\t}\r\n\t\t}\r\n\t\telse\r\n\t\t\tbreak;\r\n\t}\r\n\r\n\r\n\t/*load up the .map so we can get some entities (anyone going to bother making a qc mod compatible with this?)*/\r\n\tCOM_StripExtension(mod->name, token, sizeof(token));\r\n\tQ_strncatz(token, \".map\", sizeof(token));\r\n\tMod_SetEntitiesString(mod, FS_LoadMallocFile(token, NULL), true);\r\n\r\n\tmod->funcs.FindTouchedLeafs = D3_FindTouchedLeafs;\r\n\tmod->funcs.NativeTrace = D3_Trace;\r\n\tmod->funcs.PointContents = D3_PointContents;\r\n\tmod->funcs.FatPVS = D3_FatPVS;\r\n\tmod->funcs.ClusterForPoint = D3_ClusterForPoint;\r\n\tmod->funcs.EdictInFatPVS = D3_EdictInFatPVS;\r\n\tmod->funcs.ClusterPVS = D3_ClusterPVS;\r\n#ifdef HAVE_CLIENT\r\n\tmod->funcs.StainNode = D3_StainNode;\r\n\tmod->funcs.LightPointValues = D3_LightPointValues;\r\n\tmod->funcs.PrepareFrame = D3_PrepareFrame;\r\n#endif\r\n\r\n\tmod->type = mod_brush;\t//err, kinda, sorta, maybe.\r\n\tmod->fromgame = fg_new;\r\n\r\n\t/*that's the physics sorted*/\r\n#ifdef HAVE_CLIENT\r\n\tif (!isDedicated)\r\n\t{\r\n\t\tCOM_StripExtension(mod->name, token, sizeof(token));\r\n\t\tQ_strncatz(token, \".proc\", sizeof(token));\r\n\t\tbuf = FS_LoadMallocFile(token, NULL);\r\n\t\tMod_LoadMap_Proc(mod, buf);\r\n\t\tBZ_Free(buf);\r\n\t}\r\n#endif\r\n\r\n\treturn true;\r\n}\r\n\r\n#endif\r\n"
  },
  {
    "path": "engine/client/r_part.c",
    "content": "/*\nCopyright (C) 1996-1997 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  \n\nSee the included (GNU.txt) GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n*/\n#include \"quakedef.h\"\n\n#define NUMVERTEXNORMALS\t162\nfloat\tr_avertexnormals[NUMVERTEXNORMALS][3] = {\n#include \"anorms.h\"\n};\n\n\n#include \"shader.h\"\n#include \"com_mesh.h\"\n//FIXME: we're likely going to want to thread the building routine at some point.\n//the alias mesh stuff will need some rework as it uses statics inside.\n#define DESCSPERSHADER 8\ntypedef struct\n{\n\tint x, y, z;\t//rebuilt if changed\n\tint key;\n\n\tmodel_t *loadingmodel;\t//needs rebuilding, but wait till this is loaded.\n\n\tstruct\n\t{\n\t\tshader_t *shader;\n\t\tmesh_t mesh;\n\t\tmesh_t *pmesh;\n\t\tvbo_t vbo;\n\t} soups[64];\n\tsize_t numsoups;\n} cluttersector_t;\nstatic cluttersector_t cluttersector[3*3*3];\ncvar_t r_clutter_density\t\t= CVARD(\"r_clutter_density\", \"0\", \"Scaler for clutter counts. 0 disables clutter completely.\\nClutter requires shaders with 'fte_clutter MODEL SPACING SCALEMIN SCALEMAX ZOFS ANGLEMIN ANGLEMAX' terms\");\ncvar_t r_clutter_distance\t\t= CVARD(\"r_clutter_distance\", \"1024\", \"Distance at which clutter will become invisible.\");\t//should be used by various shaders to fade it out by here\nvoid R_Clutter_Init(void)\n{\n\tCvar_Register(&r_clutter_density, \"Ground Clutter\");\n\tCvar_Register(&r_clutter_distance, \"Ground Clutter\");\n}\ntypedef struct\n{\n\tmodel_t *loadingmodel;\n\tstruct clutter_build_ctx_soup_s\n\t{\n\t\tshader_t *shader;\n\t\tvecV_t *coord;\n\t\tvec2_t *texcoord;\n\t\tvec4_t *colour;\n\t\tvec3_t *normal;\n\t\tvec3_t *sdir;\n\t\tvec3_t *tdir;\n\t\tindex_t *idx;\n\t\tsize_t numverts;\n\t\tsize_t numidx;\n\t\tsize_t maxverts;\n\t\tsize_t maxidx;\n\t} soups[64];\n\tunsigned int numsoups;\n\tfloat area[DESCSPERSHADER];\t//here so it can overflow, so large values with small surfaces actually does something. not evenly perhaps, but not much else we can do\n\n\tunsigned int x, y, z, w;\n} clutter_build_ctx_t;\n\n//to make things repeatable so that people can depend upon placement.\nunsigned int R_Clutter_Random(clutter_build_ctx_t *ctx)\n{\t//ripped from wikipedia (originally called xorshift128)\n\tunsigned int t = ctx->x ^ (ctx->x << 11);\n\tctx->x = ctx->y; ctx->y = ctx->z; ctx->z = ctx->w;\n\treturn ctx->w = ctx->w ^ (ctx->w >> 19) ^ t ^ (t >> 8);\n}\nfloat R_Clutter_FRandom(clutter_build_ctx_t *ctx)\n{\n\tunsigned int r = R_Clutter_Random(ctx);\n\treturn (r & 0xffffff) / (float)0xffffff;\n}\n\nstatic void R_Clutter_Insert_Soup(clutter_build_ctx_t *ctx, shader_t *shader, vecV_t *fte_restrict coord, vec2_t *fte_restrict texcoord, vec3_t *fte_restrict normal, vec3_t *fte_restrict sdir, vec3_t *fte_restrict tdir, vec4_t *fte_restrict colours, size_t numverts, index_t *fte_restrict index, size_t numidx, float scale, vec3_t origin, vec3_t axis[])\n{\n\tvec3_t diffuse, ambient, ldir;\n\tfloat dot;\n\tstruct clutter_build_ctx_soup_s *soup = NULL;\n\tsize_t i;\n\n\t/* useful for debugging mayhaps --eukara */\n\t/*shader_t *os = shader;\n\tshader = R_RegisterShader(va(\"clutter#replace=%s\", os->name), SUF_NONE,\n\t\t\t\"{\\n\"\n\t\t\t\t\"program defaultsprite#MASK=0.666\\n\"\n//\t\t\t\t\"surfaceparm nodlight\\n\"\n\t\t\t\t\"surfaceparm noshadows\\n\"\n//\t\t\t\t\"cull disable\\n\"\n\t\t\t\t\"{\\n\"\n\t\t\t\t\t\"map $diffuse\\n\"\n\t\t\t\t\t\"rgbgen vertex\\n\"\n\t\t\t\t\t\"alphagen vertex\\n\"\n//\t\t\t\t\t\"alphafunc ge128\\n\"\n\t\t\t\t\"}\\n\"\n\t\t\t\"}\\n\"\n\t\t);\n\t*shader->defaulttextures = *os->defaulttextures;*/\n\n\n\tfor (i = 0, soup = ctx->soups; i < ctx->numsoups; i++, soup++)\n\t{\n\t\tif (soup->shader == shader)\n\t\t\tif (soup->numverts + numverts <= MAX_INDICIES)\n\t\t\t\tbreak;\n\t}\n\tif (i == ctx->numsoups)\n\t{\n\t\tif (i == sizeof(ctx->soups)/sizeof(ctx->soups[0]))\n\t\t\treturn;\t//too many different shaders or something\n\t\tsoup->shader = shader;\n\t\tctx->numsoups++;\n\t}\n\n\t//inject the indicies\n\tif (soup->numidx + numidx > soup->maxidx)\n\t{\n\t\tsoup->maxidx = (soup->numidx + numidx) * 2;\n\t\tsoup->idx = BZ_Realloc(soup->idx, sizeof(*soup->idx) * soup->maxidx);\n\t}\n\tfor (i = 0; i < numidx; i++)\n\t\tsoup->idx[soup->numidx++] = soup->numverts+*index++;\n\n\n\tcl.worldmodel->funcs.LightPointValues(cl.worldmodel, origin, diffuse, ambient, ldir);\n\tVectorScale(ambient, 1/255.0, ambient);\n\tVectorScale(diffuse, 1/255.0, diffuse);\n\n\t//inject the verts\n\tif (soup->numverts + numverts > soup->maxverts)\n\t{\n\t\tsoup->maxverts = (soup->numverts + numverts) * 2;\n\t\tsoup->coord = BZ_Realloc(soup->coord, sizeof(*soup->coord) * soup->maxverts);\n\t\tsoup->texcoord = BZ_Realloc(soup->texcoord, sizeof(*soup->texcoord) * soup->maxverts);\n\t\tsoup->colour = BZ_Realloc(soup->colour, sizeof(*soup->colour) * soup->maxverts);\n\t\tsoup->normal = BZ_Realloc(soup->normal, sizeof(*soup->normal) * soup->maxverts);\n\t\tsoup->sdir = BZ_Realloc(soup->sdir, sizeof(*soup->sdir) * soup->maxverts);\n\t\tsoup->tdir = BZ_Realloc(soup->tdir, sizeof(*soup->tdir) * soup->maxverts);\n\t}\n\tfor (i = 0; i < numverts; i++)\n\t{\n\t\tVectorMA(origin,\t\t\t\t\t\tscale*coord[i][0], axis[0], soup->coord[soup->numverts]);\n\t\tVectorMA(soup->coord[soup->numverts],\tscale*coord[i][1], axis[1], soup->coord[soup->numverts]);\n\t\tVectorMA(soup->coord[soup->numverts],\tscale*coord[i][2], axis[2], soup->coord[soup->numverts]);\n\t\tVector2Copy(texcoord[i], soup->texcoord[soup->numverts]);\n\n\t\tVectorMA(vec3_origin,\t\t\t\t\tnormal[i][0], axis[0], soup->normal[soup->numverts]);\n\t\tVectorMA(soup->normal[soup->numverts],\tnormal[i][1], axis[1], soup->normal[soup->numverts]);\n\t\tVectorMA(soup->normal[soup->numverts],\tnormal[i][2], axis[2], soup->normal[soup->numverts]);\n\t\t\n\t\tVectorMA(vec3_origin,\t\t\t\t\tsdir[i][0], axis[0], soup->sdir[soup->numverts]);\n\t\tVectorMA(soup->sdir[soup->numverts],\tsdir[i][1], axis[1], soup->sdir[soup->numverts]);\n\t\tVectorMA(soup->sdir[soup->numverts],\tsdir[i][2], axis[2], soup->sdir[soup->numverts]);\n\n\t\tVectorMA(vec3_origin,\t\t\t\t\ttdir[i][0], axis[0], soup->tdir[soup->numverts]);\n\t\tVectorMA(soup->tdir[soup->numverts],\ttdir[i][1], axis[1], soup->tdir[soup->numverts]);\n\t\tVectorMA(soup->tdir[soup->numverts],\ttdir[i][2], axis[2], soup->tdir[soup->numverts]);\n\n//\t\tVectorCopy(ambient, soup->colour[soup->numverts]);\n\t\tdot = DotProduct(ldir, soup->normal[soup->numverts]);\n\t\tif (dot < 0)\n\t\t\tdot = 0;\n\t\tVectorMA(ambient, dot, diffuse, soup->colour[soup->numverts]);\n\t\tif (colours)\t//most model formats don't have vertex colours\n\t\t\tsoup->colour[soup->numverts][3] = colours[i][3];\n\t\telse\n\t\t\tsoup->colour[soup->numverts][3] = 1;\n\n\t\tsoup->numverts++;\n\t}\n}\nstatic void R_Clutter_Insert_Mesh(clutter_build_ctx_t *ctx, model_t *mod, float scale, vec3_t origin, vec3_t axis[3])\n{\n\tmesh_t mesh;\n\tgaliasinfo_t *inf;\n\tunsigned int surfnum = 0;\n\tentity_t re;\n\tunsigned int randanim = R_Clutter_Random(ctx);\n\tunsigned int randskin = R_Clutter_Random(ctx);\n\n\tif (!mod)\n\t\treturn;\n\n\tif (mod->type == mod_alias)\n\t{\n\t\t//fill in the parts of the entity_t that Alias_GAliasBuildMesh needs.\n\t\tmemset(&re, 0, sizeof(re));\n\t\tre.framestate.g[FS_REG].lerpweight[0] = 1;\n\t\tre.model = mod;\n\n\t\tinf = (galiasinfo_t*)Mod_Extradata (mod);\n\t\twhile(inf)\n\t\t{\n\t\t\tgaliasskin_t *skins = inf->ofsskins;\n\t\t\tif (inf->numanimations >= 1)\n\t\t\t\tre.framestate.g[FS_REG].frame[0] = randanim%inf->numanimations;\n\t\t\telse\n\t\t\t\tre.framestate.g[FS_REG].frame[0] = 0;\n\t\t\tif (skins->numframes)\n\t\t\t{\n\t\t\t\tunsigned int frame = randskin%skins->numframes;\n\t\t\t\tAlias_GAliasBuildMesh(&mesh, NULL, inf, surfnum, &re, false);\n\t\t\t\tsurfnum++;\n\t\t\t\t//fixme: if shares verts, rewind the verts and don't add more somehow, while being careful with shaders\n\t\t\t\tR_Clutter_Insert_Soup(ctx, skins->frame[frame].shader, mesh.xyz_array, mesh.st_array, mesh.normals_array, mesh.snormals_array, mesh.tnormals_array, mesh.colors4f_array[0], mesh.numvertexes, mesh.indexes, mesh.numindexes, scale, origin, axis);\n\t\t\t}\n\t\t\tinf = inf->nextsurf;\n\t\t}\n\t\tAlias_FlushCache();\t//it got built using an entity on the stack, make sure other stuff doesn't get hurt.\n\t}\n}\nstatic void R_Clutter_Insert(void *vctx, vec3_t *fte_restrict points, size_t numtris, shader_t *surface)\n{\n\tstruct shader_clutter_s *clut;\n\tunsigned int obj;\n\tclutter_build_ctx_t *ctx = vctx;\n\tmodel_t *mod[DESCSPERSHADER];\n\tif (!surface || !surface->clutter)\n\t\treturn;\t//nothing to do.\n\n\t//avoid returning on error, so the randomization is dependable when content is still loading.\n\tfor (clut = surface->clutter, obj = 0; clut && obj <= DESCSPERSHADER; clut = clut->next, obj++)\n\t{\n\t\tmod[obj] = Mod_ForName(clut->modelname, MLV_WARN);\n\t\tif (mod[obj]->loadstate == MLS_LOADING)\n\t\t{\n\t\t\tif (!ctx->loadingmodel)\n\t\t\t\tctx->loadingmodel = mod[obj];\n\t\t\tmod[obj] = NULL;\n\t\t}\n\t\telse if (mod[obj]->type != mod_alias)\n\t\t\tmod[obj] = NULL;\n\t}\n\n\twhile(numtris-->0)\n\t{\n\t\tvec3_t xd;\n\t\tvec3_t yd;\n\t\tvec3_t zd;\n\t\tvec3_t norm;\n\t\tvec3_t axis[3];\n\t\tvec3_t org, dir;\n\t\tfloat dot;\n\t\tfloat triarea;\n//\t\tvec3_t discard;\n//\t\tunsigned int subimage;\n\t\tvec_t xm, ym, zm, s;\n\t\tVectorSubtract(points[1], points[0], xd);\n\t\tVectorSubtract(points[2], points[0], yd);\n\t\tVectorSubtract(points[2], points[1], zd);\n\t\tCrossProduct(yd, xd, norm);\n\t\tVectorNormalize(norm);\n\t\tif (norm[2] >= 0.7)\n\t\t{\n\t\t\t//determine area of triangle\n\t\t\txm = Length(xd);\n\t\t\tym = Length(yd);\n\t\t\tzm = Length(zd);\n\t\t\ts = (xm+ym+zm)/2;\n\t\t\ttriarea = sqrt(s*(s-xm)*(s-ym)*(s-zm));\n\n\t\t\tfor (clut = surface->clutter, obj = 0; clut && obj <= DESCSPERSHADER; clut = clut->next, obj++)\n\t\t\t{\n\t\t\t\tfloat spacing = clut->spacing / r_clutter_density.value;\n\t\t\t\tif (spacing < 1)\n\t\t\t\t\tspacing = 1;\n\t\t\t\tctx->area[obj] += triarea;\n\t\t\t\twhile (ctx->area[obj] >= spacing)\n\t\t\t\t{\n\t\t\t\t\tfloat scale = clut->scalemin + R_Clutter_FRandom(ctx) * (clut->scalemax-clut->scalemin);\n\t\t\t\t\tctx->area[obj] -= spacing;\n\n\t\t\t\t\t//pick a random spot\n\t\t\t\t\txm = R_Clutter_FRandom(ctx)*R_Clutter_FRandom(ctx);\n\t\t\t\t\tym = R_Clutter_FRandom(ctx) * (1-xm);\n\t\t\t\t\tVectorMA(points[0], xm, xd, org);\n\t\t\t\t\tVectorMA(org, ym, yd, org);\n\n\t\t\t\t\t//randomize the direction\n\t\t\t\t\tdot = clut->anglemin + R_Clutter_FRandom(ctx) * (clut->anglemax-clut->anglemin);\n\t\t\t\t\tdir[0] = cos(dot);\n\t\t\t\t\tdir[1] = sin(dot);\n\t\t\t\t\tdir[2] = 0;\n\t\t\t\t\t//figure out various directions\n\t\t\t\t\tdot = -DotProduct(dir, norm);\n\t\t\t\t\tVectorMA(dir, dot, norm, dir);\n\t\t\t\t\tVectorNormalize(dir);\n\t\t\t\t\tVectorCopy(norm, axis[2]);\n\t\t\t\t\tCrossProduct(axis[2], dir, axis[1]);\n\t\t\t\t\tCrossProduct(axis[1], axis[2], axis[0]);\n\t\t\t\t\tVectorMA(org, clut->zofs*scale, axis[2], org);\n\t\t\t\t\tR_Clutter_Insert_Mesh(ctx, mod[obj], scale, org, axis);\n\n/*\n\t\t\t\t\tVectorMA(org, r_clutter_size.value/2, dir, vertcoord[numverts]);\n\t\t\t\t\tVectorMA(org, -(r_clutter_size.value/2), dir, vertcoord[numverts+1]);\n\t\t\t\t\tVectorMA(vertcoord[numverts], r_clutter_height.value, norm, vertcoord[numverts+2]);\n\t\t\t\t\tVectorMA(vertcoord[numverts+1], r_clutter_height.value, norm, vertcoord[numverts+3]);\n\t\t\t\t\tsubimage = R_Clutter_Random(ctx);\n\t\t\t\t\tVector2Set(texcoord[numverts], subimage%r_clutter_atlaswidth.ival, (subimage/r_clutter_atlaswidth.ival)%r_clutter_atlasheight.ival);\n\t\t\t\t\ttexcoord[numverts][0] *= 1/r_clutter_atlaswidth.value;\n\t\t\t\t\ttexcoord[numverts][1] *= 1/r_clutter_atlasheight.value;\n\t\t\t\t\tVector2Set(texcoord[numverts+1], texcoord[numverts][0]+(1/r_clutter_atlaswidth.value), texcoord[numverts][1]);\n\t\t\t\t\tVector2Set(texcoord[numverts+2], texcoord[numverts][0]\t\t\t\t\t\t\t\t , texcoord[numverts][1]);\n\t\t\t\t\tVector2Set(texcoord[numverts+3], texcoord[numverts][0]+(1/r_clutter_atlaswidth.value), texcoord[numverts][1]);\n\t\t\t\t\ttexcoord[numverts+0][1]+=(1/r_clutter_atlasheight.value);\n\t\t\t\t\ttexcoord[numverts+1][1]+=(1/r_clutter_atlasheight.value);\n\t\t\t\t\tVector4Set(colours[numverts+0], 1, 1, 1, 1);\n\t\t\t\t\tVectorMA(org, 1/8.0, norm, org);//push away from the surface to avoid precision issues with lighting on slopes\n\t\t\t\t\tcl.worldmodel->funcs.LightPointValues(cl.worldmodel, org, colours[numverts+0], discard, discard);\n\t\t\t\t\tVectorScale(colours[numverts+0], 1/512.0, colours[numverts+0]);\n\t\t\t\t\tVector4Copy(colours[numverts+0], colours[numverts+1]);\n\t\t\t\t\tVector4Copy(colours[numverts+0], colours[numverts+2]);\n\t\t\t\t\tVector4Copy(colours[numverts+1], colours[numverts+3]);\n\t\t\t\t\tindexes[numidx+0] = numverts+0;\n\t\t\t\t\tindexes[numidx+1] = numverts+2;\n\t\t\t\t\tindexes[numidx+2] = numverts+1;\n\t\t\t\t\tindexes[numidx+3] = numverts+2;\n\t\t\t\t\tindexes[numidx+4] = numverts+3;\n\t\t\t\t\tindexes[numidx+5] = numverts+1;\n\t\t\t\t\tnumverts += 4;\n\t\t\t\t\tnumidx += 6;\n*/\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tpoints += 3;\n\t}\n}\nvoid R_Clutter_Emit(batch_t **batches)\n{\n\tconst float cluttersize = r_clutter_distance.value;\n\tint vx, vy, vz;\n\tint x, y, z, key, i, j;\n\tcluttersector_t *sect;\n\tbatch_t *b;\n\tqboolean rebuildlimit = false;\n\n\tif (!cl.worldmodel || cl.worldmodel->loadstate != MLS_LOADED || r_clutter_density.value <= 0 || (r_refdef.flags & RDF_NOWORLDMODEL))\n\t\treturn;\n\n\tif (qrenderer != QR_OPENGL && qrenderer != QR_VULKAN)\t//vbo only!\n\t\treturn;\n\n\t//rebuild if any of the cvars changes.\n\tkey =\tr_clutter_density.modified + r_clutter_distance.modified;\n\n\tvx = floor((r_refdef.vieworg[0] / cluttersize));\n\tvy = floor((r_refdef.vieworg[1] / cluttersize));\n\tvz = floor((r_refdef.vieworg[2] / cluttersize));\n\n\tfor (z = vz-1; z <= vz+1; z++)\n\tfor (y = vy-1; y <= vy+1; y++)\n\tfor (x = vx-1; x <= vx+1; x++)\n\t{\n\t\tint ix = x%3;\n\t\tint iy = y%3;\n\t\tint iz = z%3;\n\t\tif (ix < 0)\n\t\t\tix += 3;\n\t\tif (iy < 0)\n\t\t\tiy += 3;\n\t\tif (iz < 0)\n\t\t\tiz += 3;\n\t\tsect = &cluttersector[ix + (iy*3) + (iz*3*3)];\n\t\tif (sect->loadingmodel && sect->loadingmodel->loadstate != MLS_LOADING)\n\t\t{\n\t\t\tsect->loadingmodel = NULL;\n\t\t\tsect->key-=1;\t//rebuild even if failed, this covers multiple models.\n\t\t}\n\t\tif (sect->x != x || sect->y != y || sect->z != z || sect->key != key)\n\t\t{\n\t\t\tvbobctx_t vctx;\n\t\t\tclutter_build_ctx_t cctx;\n\t\t\tvec3_t org = {x*cluttersize+(cluttersize/2),y*cluttersize+(cluttersize/2),z*cluttersize+(cluttersize/2)};\n\t\t\tvec3_t down = {0, 0, -1};\n\t\t\tvec3_t forward = {1, 0, 0};\n\t\t\tvec3_t right = {0, 1, 0};\n\t\t\tif (r_refdef.recurse)\t//FIXME\n\t\t\t\tcontinue;\n\t\t\tif (rebuildlimit)\n\t\t\t\tcontinue;\n\t\t\trebuildlimit = true;\n\t\t\tsect->x = x;\n\t\t\tsect->y = y;\n\t\t\tsect->z = z;\n\t\t\tsect->key = key;\n\n\t\t\t//make sure any old state is gone\n\t\t\tfor (i = 0; i < sect->numsoups; i++)\n\t\t\t{\n\t\t\t\tBE_VBO_Destroy(&sect->soups[i].vbo.coord, sect->soups[i].vbo.vbomem);\n\t\t\t\tBE_VBO_Destroy(&sect->soups[i].vbo.indicies, sect->soups[i].vbo.ebomem);\n\t\t\t}\n\t\t\tsect->numsoups = 0;\n\t\t\tmemset(&cctx, 0, sizeof(cctx));\n\t\t\tcctx.x = x;\n\t\t\tcctx.y = y;\n\t\t\tcctx.z = z;\n\t\t\tcctx.w = (sect-cluttersector)+1;\n\t\t\tMod_ClipDecal(cl.worldmodel, org, down, forward, right, cluttersize, 0, 0, R_Clutter_Insert, &cctx);\n\t\t\tsect->loadingmodel = cctx.loadingmodel;\n\n\t\t\tfor (i = 0; i < cctx.numsoups; i++)\n\t\t\t{\n\t\t\t\tif (cctx.soups[i].numverts)\n\t\t\t\t{\n\t\t\t\t\tsect->soups[sect->numsoups].shader = cctx.soups[i].shader;\n\t\t\t\t\tsect->soups[sect->numsoups].pmesh = &sect->soups[sect->numsoups].mesh;\n\t\t\t\n\t\t\t\t\tBE_VBO_Begin(&vctx, (sizeof(cctx.soups[i].coord[0]) + sizeof(cctx.soups[i].texcoord[0]) + sizeof(cctx.soups[i].colour[0]) + 3*sizeof(vec3_t))*cctx.soups[i].numverts);\n\t\t\t\t\tBE_VBO_Data(&vctx, cctx.soups[i].coord, sizeof(cctx.soups[i].coord[0])*cctx.soups[i].numverts, &sect->soups[sect->numsoups].vbo.coord);\n\t\t\t\t\tBE_VBO_Data(&vctx, cctx.soups[i].texcoord, sizeof(cctx.soups[i].texcoord[0])*cctx.soups[i].numverts, &sect->soups[sect->numsoups].vbo.texcoord);\n\t\t\t\t\tBE_VBO_Data(&vctx, cctx.soups[i].colour, sizeof(cctx.soups[i].colour[0])*cctx.soups[i].numverts, &sect->soups[sect->numsoups].vbo.colours[0]);\n\t\t\t\t\tBE_VBO_Data(&vctx, cctx.soups[i].normal, sizeof(cctx.soups[i].normal[0])*cctx.soups[i].numverts, &sect->soups[sect->numsoups].vbo.normals);\n\t\t\t\t\tBE_VBO_Data(&vctx, cctx.soups[i].sdir, sizeof(cctx.soups[i].sdir[0])*cctx.soups[i].numverts, &sect->soups[sect->numsoups].vbo.svector);\n\t\t\t\t\tBE_VBO_Data(&vctx, cctx.soups[i].tdir, sizeof(cctx.soups[i].tdir[0])*cctx.soups[i].numverts, &sect->soups[sect->numsoups].vbo.tvector);\n\t\t\t\t\tBE_VBO_Finish(&vctx, cctx.soups[i].idx, sizeof(cctx.soups[i].idx[0])*cctx.soups[i].numidx, &sect->soups[sect->numsoups].vbo.indicies, &sect->soups[sect->numsoups].vbo.vbomem, &sect->soups[sect->numsoups].vbo.ebomem);\n\t\t\t\t\tsect->soups[sect->numsoups].vbo.colours_bytes = false;\n\n\t\t\t\t\tsect->soups[sect->numsoups].mesh.numindexes = sect->soups[sect->numsoups].vbo.indexcount = cctx.soups[i].numidx;\n\t\t\t\t\tsect->soups[sect->numsoups].mesh.numvertexes = sect->soups[sect->numsoups].vbo.vertcount = cctx.soups[i].numverts;\n\t\t\t\t\tsect->numsoups++;\n\t\t\t\t}\n\t\t\t\tBZ_Free(cctx.soups[i].coord);\n\t\t\t\tBZ_Free(cctx.soups[i].texcoord);\n\t\t\t\tBZ_Free(cctx.soups[i].colour);\n\t\t\t\tBZ_Free(cctx.soups[i].normal);\n\t\t\t\tBZ_Free(cctx.soups[i].sdir);\n\t\t\t\tBZ_Free(cctx.soups[i].tdir);\n\t\t\t\tBZ_Free(cctx.soups[i].idx);\n\t\t\t}\n\t\t}\n\n\t\t//emit a batch if we have grassy surfaces in this block\n\t\tfor (i = 0; i < sect->numsoups; i++)\n\t\t{\n\t\t\tb = BE_GetTempBatch();\n\t\t\tif (!b)\n\t\t\t\treturn;\n\t\t\tmemset(b, 0, sizeof(*b));\n\t\t\tfor (j = 0; j < MAXRLIGHTMAPS; j++)\n\t\t\t\tb->lightmap[j] = -1;\n\t\t\tb->ent = &r_worldentity;\n\t\t\tb->meshes = 1;\n\t\t\tb->mesh = &sect->soups[i].pmesh;\n\t\t\tb->vbo = &sect->soups[i].vbo;\n\t\t\tb->shader = sect->soups[i].shader;\n\t\t\tb->next = batches[b->shader->sort];\n\t\t\tbatches[b->shader->sort] = b;\n\t\t}\n\t}\n}\n\nvoid R_Clutter_Purge(void)\n{\n\tsize_t i, j;\n\tcluttersector_t *sect;\n\tif (!qrenderer)\n\t\treturn;\n\tfor (i = 0; i < sizeof(cluttersector)/sizeof(cluttersector[0]); i++)\n\t{\n\t\tsect = &cluttersector[i];\n\t\tfor (j = 0; j < sect->numsoups; j++)\n\t\t{\n\t\t\tBE_VBO_Destroy(&sect->soups[j].vbo.coord, sect->soups[j].vbo.vbomem);\n\t\t\tBE_VBO_Destroy(&sect->soups[j].vbo.indicies, sect->soups[j].vbo.ebomem);\n\t\t}\n\t\tmemset(sect, 0, sizeof(*sect));\n\t}\n}\n\n\n\n\nstatic void QDECL R_Rockettrail_Callback(struct cvar_s *var, char *oldvalue)\n{\n\tint i;\n\tmodel_t *mod;\n\textern model_t\t*mod_known;\n\textern int\t\tmod_numknown;\n\n\tif (cls.state == ca_disconnected)\n\t\treturn; // don't bother parsing while disconnected\n\n\tfor (i=0 , mod=mod_known ; i<mod_numknown ; i++, mod++)\n\t{\n\t\tif (mod->loadstate == MLS_LOADED)\n\t\t\tif (mod->flags & MF_ROCKET)\n\t\t\t\tP_LoadedModel(mod);\n\t}\n}\n\nstatic void QDECL R_Grenadetrail_Callback(struct cvar_s *var, char *oldvalue)\n{\n\tint i;\n\tmodel_t *mod;\n\textern model_t\t*mod_known;\n\textern int\t\tmod_numknown;\n\n\tif (cls.state == ca_disconnected)\n\t\treturn; // don't bother parsing while disconnected\n\n\tfor (i=0 , mod=mod_known ; i<mod_numknown ; i++, mod++)\n\t{\n\t\tif (mod->loadstate == MLS_LOADED)\n\t\t\tif (mod->flags & MF_GRENADE)\n\t\t\t\tP_LoadedModel(mod);\n\t}\n}\n\nextern particleengine_t pe_null;\n#ifdef PSET_CLASSIC\nextern particleengine_t pe_classic;\n#endif\nparticleengine_t pe_darkplaces;\nparticleengine_t pe_qmb;\n#ifdef PSET_SCRIPT\nextern particleengine_t pe_script;\n#endif\n\nparticleengine_t *particlesystem[] =\n{\n#ifdef PSET_SCRIPT\n\t&pe_script,\n#endif\n\t&pe_darkplaces,\n\t&pe_qmb,\n#ifdef PSET_CLASSIC\n\t&pe_classic,\n#endif\n\t&pe_null,\n\tNULL,\n};\n\nstatic void QDECL R_ParticleSystem_Callback(struct cvar_s *var, char *oldvalue)\n{\n\tint i;\n\tif (pe)\n\t{\n\t\tCL_ClearTEntParticleState();\n\t\tCL_ClearLerpEntsParticleState();\n#ifdef Q2CLIENT\n\t\tCLQ2_ClearParticleState();\n#endif\n\n\t\tpe->ShutdownParticles();\n\t}\n\n\tif (!qrenderer)\n\t{\n\t\tpe = &pe_null;\n\t}\n\telse\n\t{\n\t\tpe = NULL;\n\t\tfor (i = 0; particlesystem[i]; i++)\n\t\t{\n\t\t\tif (   (particlesystem[i]->name1 && !stricmp(var->string, particlesystem[i]->name1))\n\t\t\t\t|| (particlesystem[i]->name2 && !stricmp(var->string, particlesystem[i]->name2)))\n\t\t\t{\n\t\t\t\tpe = particlesystem[i];\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (!pe)\n\t\t\t\tif (particlesystem[i]->name1)\n\t\t\t\t\tpe = particlesystem[i];\n\t\t}\n\t}\n\tif (!pe)\n\t\tSys_Error(\"No particle system available. Please recompile.\");\n\n\tif (!pe->InitParticles())\n\t{\n\t\tCon_Printf(\"Particlesystem %s failed to init\\n\", pe->name1);\n\t\tpe = &pe_null;\n\t\tpe->InitParticles();\n\t}\n\tpe->ClearParticles();\n\tCL_RegisterParticles();\n}\n\ncvar_t r_decal_noperpendicular = CVARD(\"r_decal_noperpendicular\", \"1\", \"When enabled, decals will not be generated on planes at a steep angle from clipped decal orientation.\");\ncvar_t r_rockettrail = CVARFC(\"r_rockettrail\", \"1\", CVAR_SEMICHEAT, R_Rockettrail_Callback);\ncvar_t r_grenadetrail = CVARFC(\"r_grenadetrail\", \"1\", CVAR_SEMICHEAT, R_Grenadetrail_Callback);\n#ifndef PSET_CLASSIC\ncvar_t r_particlesystem\t= CVARFC(\"r_particlesystem\",\t\"script\",\t\t\t\t\t\tCVAR_SEMICHEAT|CVAR_ARCHIVE|CVAR_NOSET, R_ParticleSystem_Callback);\ncvar_t r_particledesc\t= CVARAF(\"r_particledesc\",\t\t\"\",\t\t\t\"r_particlesdesc\",\tCVAR_SEMICHEAT|CVAR_ARCHIVE);\n#else\ncvar_t r_particlesystem = CVARFC(\"r_particlesystem\",\tIFMINIMAL(\"classic\", \"script\"), CVAR_SEMICHEAT|CVAR_ARCHIVE, R_ParticleSystem_Callback);\ncvar_t r_particledesc = CVARAF(\"r_particledesc\",\t\t\"classic\",\t\"r_particlesdesc\", CVAR_SEMICHEAT|CVAR_ARCHIVE);\n#endif\nextern cvar_t r_bouncysparks;\nextern cvar_t r_part_rain;\nextern cvar_t r_bloodstains;\nextern cvar_t gl_part_flame;\ncvar_t r_part_rain_quantity = CVARF(\"r_part_rain_quantity\", \"1\", CVAR_ARCHIVE);\n\ncvar_t r_particle_tracelimit = CVARFD(\"r_particle_tracelimit\", \"0x7fffffff\", CVAR_ARCHIVE, \"Number of traces to allow per frame for particle physics.\");\ncvar_t r_part_sparks = CVAR(\"r_part_sparks\", \"1\");\ncvar_t r_part_sparks_trifan = CVAR(\"r_part_sparks_trifan\", \"1\");\ncvar_t r_part_sparks_textured = CVAR(\"r_part_sparks_textured\", \"1\");\ncvar_t r_part_beams = CVAR(\"r_part_beams\", \"1\");\ncvar_t r_part_contentswitch = CVARFD(\"r_part_contentswitch\", \"1\", CVAR_ARCHIVE, \"Enable particle effects to change based on content (ex. water).\");\ncvar_t r_part_density = CVARF(\"r_part_density\", \"1\", CVAR_ARCHIVE);\ncvar_t r_part_classic_expgrav = CVARFD(\"r_part_classic_expgrav\", \"10\", CVAR_ARCHIVE, \"Scaler for how fast classic explosion particles should accelerate due to gravity. 1 for like vanilla, 10 for like zquake.\");\ncvar_t r_part_classic_opaque = CVARFD(\"r_part_classic_opaque\", \"0\", CVAR_ARCHIVE, \"Disables transparency on classic particles, for the oldskool look.\");\ncvar_t r_part_classic_square = CVARFD(\"r_part_classic_square\", \"0\", CVAR_ARCHIVE, \"Enables square particles, for the oldskool look.\");\n\ncvar_t r_part_maxparticles = CVAR(\"r_part_maxparticles\", \"65536\");\ncvar_t r_part_maxdecals = CVAR(\"r_part_maxdecals\", \"8192\");\n\n\nparticleengine_t *pe;\n\nstatic struct partalias_s\n{\n\tstruct partalias_s *next;\n\tconst char *from;\n\tconst char *to;\n} *partaliaslist;\n\nvoid P_ParticleEffect_f(void);\nstatic void P_ParticleEffectAlias_f(void);\n\nvoid P_InitParticleSystem(void)\n{\n\tchar *particlecvargroupname = \"Particle effects\";\n\n\tCvar_Register(&r_decal_noperpendicular, particlecvargroupname);\t//decals might actually be used for more than just particles, but oh well.\n\n\tCvar_Register(&r_particlesystem, particlecvargroupname);\n\n\t//particles\n\tCvar_Register(&r_particledesc, particlecvargroupname);\n\tCvar_Register(&r_bouncysparks, particlecvargroupname);\n\tCvar_Register(&r_part_rain, particlecvargroupname);\n\n\tCvar_Register(&r_part_rain_quantity, particlecvargroupname);\n\n\tCvar_Register(&r_particle_tracelimit, particlecvargroupname);\n\n\tCvar_Register(&r_part_maxparticles, particlecvargroupname);\n\tCvar_Register(&r_part_maxdecals, particlecvargroupname);\n\n\tCvar_Register(&r_part_sparks, particlecvargroupname);\n\tCvar_Register(&r_part_sparks_trifan, particlecvargroupname);\n\tCvar_Register(&r_part_sparks_textured, particlecvargroupname);\n\tCvar_Register(&r_part_beams, particlecvargroupname);\n\tCvar_Register(&r_part_contentswitch, particlecvargroupname);\n\tCvar_Register(&r_part_density, particlecvargroupname);\n\tCvar_Register(&r_part_classic_expgrav, particlecvargroupname);\n\tCvar_Register(&r_part_classic_opaque, particlecvargroupname);\n\tCvar_Register(&r_part_classic_square, particlecvargroupname);\n\n\tCvar_Register (&gl_part_flame, particlecvargroupname);\n\n\tCvar_Register (&r_rockettrail, particlecvargroupname);\n\tCvar_Register (&r_grenadetrail, particlecvargroupname);\n\n\t//always registered to suck up stray r_part commands even when the scripted system is not active.\n#ifdef PSET_SCRIPT\n\tCmd_AddCommand(\"r_part\", P_ParticleEffect_f);\n#endif\n\tCmd_AddCommand(\"r_partredirect\", P_ParticleEffectAlias_f);\n\n\tR_Clutter_Init();\n}\n\nvoid P_ShutdownParticleSystem(void)\n{\n\tstruct partalias_s *l;\n\n\twhile (partaliaslist)\n\t{\n\t\tl = partaliaslist;\n\t\tpartaliaslist = l->next;\n\t\tZ_Free(l);\n\t}\n}\n\nstatic void P_ParticleEffectAlias_f(void)\n{\n\tstruct partalias_s **link, *l;\n\tchar *from = Cmd_Argv(1);\n\tchar *to = Cmd_Argv(2);\n\n\t//user wants to list all\n\tif (!*from)\n\t{\n\t\tfor (l = partaliaslist; l; l = l->next)\n\t\t{\n\t\t\tCon_Printf(\"%s -> %s\\n\", l->from, l->to);\n\t\t}\n\t\treturn;\n\t}\n\n\t//unlink the current value\n\tfor (link = &partaliaslist; (l=*link); link = &(*link)->next)\n\t{\n\t\tif (!Q_strcasecmp(l->from, from))\n\t\t{\n\t\t\t//they didn't specify a to, so just print out this one effect without removing it.\n\t\t\tif (Cmd_Argc() == 2)\n\t\t\t{\n\t\t\t\tCon_Printf(\"particle %s is currently remapped to %s\\n\", l->from, l->to);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t*link = l->next;\n\t\t\tZ_Free(l);\n\t\t\tbreak;\n\t\t}\n\t}\n\n\t//create a new entry.\n\tif (*to && Q_strcasecmp(from, to))\n\t{\n\t\tl = Z_Malloc(sizeof(*l) + strlen(from) + strlen(to) + 2);\n\t\tl->from = (char*)(l + 1);\n\t\tstrcpy((char*)l->from, from);\n\t\tl->to = l->from + strlen(l->from)+1;\n\t\tstrcpy((char*)l->to, to);\n\t\tl->next = partaliaslist;\n\t\tpartaliaslist = l;\n\t}\n\n\tCL_RegisterParticles();\n}\nint P_FindParticleType(const char *efname)\n{\n\tstruct partalias_s *l;\n\tint recurselimit = 5;\n\tif (!pe)\n\t\treturn P_INVALID;\n\tfor (l = partaliaslist; l; )\n\t{\n\t\tif (!Q_strcasecmp(l->from, efname))\n\t\t{\n\t\t\tefname = l->to;\n\n\t\t\tif (recurselimit --> 0)\n\t\t\t\tl = partaliaslist;\n\t\t\telse\n\t\t\t\treturn P_INVALID;\n\t\t}\n\t\telse\n\t\t\tl = l->next;\n\t}\n\n\treturn pe->FindParticleType(efname);\n}\n\nvoid P_Shutdown(void)\n{\n\tif (pe)\n\t{\n\t\tCL_ClearTEntParticleState();\n\t\tCL_ClearLerpEntsParticleState();\n#ifdef Q2CLIENT\n\t\tCLQ2_ClearParticleState();\n#endif\n\t\tpe->ShutdownParticles();\n\t}\n\tpe = NULL;\n\n\tR_Clutter_Purge();\n}\n\n//traces against renderable entities\n//0 says hit nothing.\n//1 says hit world\n//>1 says hit some entity\nentity_t *TraceLineR (vec3_t start, vec3_t end, vec3_t impact, vec3_t normal, qboolean bsponly)\n{\n\ttrace_t\t\ttrace;\n\tfloat len, bestlen;\n\tint i, j;\n\tvec3_t delta, ts, te;\n\tentity_t *pe;\n\tentity_t *result=NULL;\n//\tvec3_t axis[3];\n\tvec3_t movemins, movemaxs;\n\n\tmemset (&trace, 0, sizeof(trace));\n\n\tVectorSubtract(end, start, delta);\n\tbestlen = Length(delta);\n\n\tVectorCopy (end, impact);\n\n\tfor (i = 0; i < 3; i++)\n\t{\n\t\tif (start[i] > end[i])\n\t\t{\n\t\t\tmovemins[i] = end[i];\n\t\t\tmovemaxs[i] = start[i];\n\t\t}\n\t\telse\n\t\t{\n\t\t\tmovemins[i] = start[i];\n\t\t\tmovemaxs[i] = end[i];\n\t\t}\n\t}\n\n\tfor (i=-1 ; i<cl_numvisedicts ; i++)\n\t{\n\t\tif (i == -1)\n\t\t\tpe = &r_worldentity;\n\t\telse\n\t\t\tpe = &cl_visedicts[i];\n\t\tif (pe->rtype != RT_MODEL || !pe->model || (pe->shaderRGBAf[3] < 1&&(pe->flags&RF_TRANSLUCENT)) || (pe->flags & (RF_ADDITIVE|RF_NODEPTHTEST|RF_TRANSLUCENT|RF_EXTERNALMODEL)))\n\t\t\tcontinue;\n\t\tif (bsponly && pe->model->type != mod_brush)\n\t\t\tcontinue;\n\t\tif (pe->model->funcs.NativeTrace && pe->model->loadstate == MLS_LOADED)\n\t\t{\n\t\t\t//try to trivially reject the mesh.\n\t\t\tfloat ext = 0;\n\t\t\tfloat t;\n\t\t\tfor (j = 0; j < 3; j++)\n\t\t\t{\n\t\t\t\tt = fabs(pe->model->maxs[j]);\n\t\t\t\text = max(ext, t);\n\t\t\t\tt = fabs(pe->model->mins[j]);\n\t\t\t\text = max(ext, t);\n\t\t\t}\n\t\t\tif (\tmovemins[0] > pe->origin[0]+ext\n\t\t\t\t||\tmovemins[1] > pe->origin[1]+ext\n\t\t\t\t||\tmovemins[2] > pe->origin[2]+ext\n\t\t\t\t||\tmovemaxs[0] < pe->origin[0]-ext\n\t\t\t\t||\tmovemaxs[1] < pe->origin[1]-ext\n\t\t\t\t||\tmovemaxs[2] < pe->origin[2]-ext )\n\t\t\t\tcontinue;\n\n\t\t\tVectorSubtract(start, pe->origin, ts);\n\t\t\tVectorSubtract(end, pe->origin, te);\n\t\t\tpe->model->funcs.NativeTrace(pe->model, 0, &pe->framestate, pe->axis, ts, te, vec3_origin, vec3_origin, false, MASK_WORLDSOLID, &trace);\n\t\t\tif (trace.fraction<1)\n\t\t\t{\n\t\t\t\tVectorSubtract(trace.endpos, ts, delta);\n\t\t\t\tlen = DotProduct(delta,delta);\n\t\t\t\tif (len < bestlen)\n\t\t\t\t{\n\t\t\t\t\tbestlen = len;\n\t\t\t\t\tif (normal)\n\t\t\t\t\t\tVectorCopy (trace.plane.normal, normal);\n\t\t\t\t\tVectorAdd (pe->origin, trace.endpos, impact);\n\t\t\t\t}\n\n\t\t\t\tresult = pe;\n\t\t\t}\n\t\t\t/*if (trace.startsolid)\n\t\t\t{\n\t\t\t\tVectorNormalize(delta);\n\t\t\t\tif (normal)\n\t\t\t\t{\n\t\t\t\t\tnormal[0] = -delta[0];\n\t\t\t\t\tnormal[1] = -delta[1];\n\t\t\t\t\tnormal[2] = -delta[2];\n\t\t\t\t}\n\t\t\t\tVectorCopy (end, impact);\n\t\t\t\treturn NULL;\n\t\t\t}*/\n\t\t}\n\t}\n\n\treturn result;\n}\n\n#include \"pr_common.h\"\n//traces against networked entities only.\nfloat CL_TraceLine (vec3_t start, vec3_t end, vec3_t impact, vec3_t normal, int *ent)\n{\n\ttrace_t\t\ttrace;\n#ifdef CSQC_DAT\n\textern world_t csqc_world;\n\tif (csqc_world.progs)\n\t{\n\t\ttrace = World_Move(&csqc_world, start, vec3_origin, vec3_origin, end, MOVE_NOMONSTERS, csqc_world.edicts);\n\t\tVectorCopy(trace.endpos, impact);\n\t\tif (normal)\n\t\t\tVectorCopy(trace.plane.normal, normal);\n\t\tif (ent)\n\t\t{\n\t\t\tif (trace.entnum)\t//ssqc numbers...\n\t\t\t\t*ent = trace.entnum; //aka: trace_networkentity\n\t\t\telse if (trace.ent)\t//csqc numbers are negative...\n\t\t\t\t*ent = -((wedict_t*)trace.ent)->entnum;\n\t\t\t//else makestatic, though those are assumed non-solid so won't be returned anyway.\n\t\t\telse\n\t\t\t\t*ent = 0;\n\t\t}\n\t\treturn trace.fraction;\n\t}\n\telse\n#endif\n\t{\n\t\tfloat bestfrac=1;\n\t\tint i;\n\t\tvec3_t ts, te;\n\t\tphysent_t *pe;\n\t\tmodel_t *mod;\n\t\tint result=0;\n\t\tvec3_t axis[3];\n\n\t\tmemset (&trace, 0, sizeof(trace));\n\n\t\tVectorCopy (end, impact);\n\t\tif (normal)\n\t\t\tVectorClear(normal);\n\n\t\tfor (i=0 ; i < pmove.numphysent ; i++)\n\t\t{\n\t\t\tpe = &pmove.physents[i];\n\t\t\tif (pe->nonsolid)\n\t\t\t\tcontinue;\n\t\t\tmod = pe->model;\n\t\t\tif (mod && mod->loadstate == MLS_LOADED && mod->funcs.NativeTrace)\n\t\t\t{\n\t\t\t\tVectorSubtract(start, pe->origin, ts);\n\t\t\t\tVectorSubtract(end, pe->origin, te);\n\t\t\t\tif (pe->angles[0] || pe->angles[1] || pe->angles[2])\n\t\t\t\t{\n\t\t\t\t\tAngleVectors(pe->angles, axis[0], axis[1], axis[2]);\n\t\t\t\t\tVectorNegate(axis[1], axis[1]);\n\t\t\t\t\tmod->funcs.NativeTrace(mod, 0, PE_FRAMESTATE, axis, ts, te, vec3_origin, vec3_origin, false, MASK_WORLDSOLID, &trace);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tmod->funcs.NativeTrace(mod, 0, PE_FRAMESTATE, NULL, ts, te, vec3_origin, vec3_origin, false, MASK_WORLDSOLID, &trace);\n\t\t\t\tif (trace.fraction<1)\n\t\t\t\t{\n\t\t\t\t\tif (bestfrac > trace.fraction)\n\t\t\t\t\t{\n\t\t\t\t\t\tbestfrac = trace.fraction;\n\t\t\t\t\t\tif (normal)\n\t\t\t\t\t\t\tVectorCopy (trace.plane.normal, normal);\n\t\t\t\t\t\tVectorAdd (pe->origin, trace.endpos, impact);\n\t\t\t\t\t\tresult = pe->info;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (trace.startsolid)\n\t\t\t\t{\n\t\t\t\t\tif (normal)\n\t\t\t\t\t{\n\t\t\t\t\t\tVectorSubtract(start, end, normal);\n\t\t\t\t\t\tVectorNormalize(normal);\n\t\t\t\t\t}\n\t\t\t\t\tVectorCopy (end, impact);\n\n\t\t\t\t\t//hit nothing\n\t\t\t\t\tif (ent)\n\t\t\t\t\t\t*ent = 0;\n\t\t\t\t\treturn 1;\n\t\t\t\t}\n\n\t\t\t}\n\t\t}\n\n\t\tif (ent)\n\t\t\t*ent = result;\n\t\treturn bestfrac;\n\t}\n}\n\n//handy utility...\nvoid P_EmitEffect (vec3_t pos, vec3_t orientation[3], unsigned int modeleflags, int type, trailkey_t *tk)\n{\n\tfloat count;\n\tif (cl.paused)\n\t\treturn;\n\n\tcount = ((host_frametime>0.1)?0.1:host_frametime);\n\tif (orientation)\n\t{\n\t\tif (modeleflags & MDLF_EMITFORWARDS)\n\t\t\tpe->RunParticleEffectState(pos, orientation[0], count, type, tk);\n\t\telse\n\t\t{\n\t\t\tvec3_t down;\n\t\t\tVectorNegate(orientation[2], down);\n\t\t\tpe->RunParticleEffectState(pos, down, count, type, tk);\n\t\t}\n\t}\n\telse\n\t\tpe->RunParticleEffectState(pos, NULL, count, type, tk);\n}\n\n\n\n\n\n\n\n\n\n\n\n// P_SelectableTrail: given default/opposite effects, model pointer, and a user selection cvar\n// changes model to the appropriate trail effect and default trail index\nstatic void P_SelectableTrail(int *trailid, int *trailpalidx, cvar_t *selection, int mdleffect, int mdlcidx, int oppeffect, int oppcidx)\n{\n\tint select = (int)(selection->value);\n\n\tswitch (select)\n\t{\n\tcase 0: // check for string, otherwise no trail\n\t\tif (selection->string[0] == '0')\n\t\t{\n\t\t\t*trailid = P_INVALID;\n\t\t\t*trailpalidx = -1;\n\t\t\tbreak;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tint effect = P_FindParticleType(selection->string);\n\n\t\t\tif (effect >= 0)\n\t\t\t{\n\t\t\t\t*trailid = effect;\n\t\t\t\t*trailpalidx = mdlcidx;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\t// fall through to default (so semicheat will work properly)\n\tcase 1: // default model effect\n\tdefault:\n\t\t*trailid = mdleffect;\n\t\t*trailpalidx = mdlcidx;\n\t\tbreak;\n\tcase 2: // opposite effect\n\t\t*trailid = oppeffect;\n\t\t*trailpalidx= oppcidx;\n\t\tbreak;\n\tcase 3: // alt rocket effect\n\t\t*trailid = P_FindParticleType(\"TR_ALTROCKET\");\n\t\t*trailpalidx = 107;\n\t\tbreak;\n\tcase 4: // gib\n\t\t*trailid = P_FindParticleType(\"TR_BLOOD\");\n\t\t*trailpalidx = 70;\n\t\tbreak;\n\tcase 5: // zombie gib\n\t\t*trailid = P_FindParticleType(\"TR_SLIGHTBLOOD\");\n\t\t*trailpalidx = 70;\n\t\tbreak;\n\tcase 6: // Scrag tracer\n\t\t*trailid = P_FindParticleType(\"TR_WIZSPIKE\");\n\t\t*trailpalidx = 60;\n\t\tbreak;\n\tcase 7: // Knight tracer\n\t\t*trailid = P_FindParticleType(\"TR_KNIGHTSPIKE\");\n\t\t*trailpalidx = 238;\n\t\tbreak;\n\tcase 8: // Vore tracer\n\t\t*trailid = P_FindParticleType(\"TR_VORESPIKE\");\n\t\t*trailpalidx = 154;\n\t\tbreak;\n\tcase 9: // rail trail\n\t\t*trailid = P_FindParticleType(\"TE_RAILTRAIL\");\n\t\t*trailpalidx = 15;\n\t\tbreak;\n\t}\n}\n\n\n\n//figure out which particle trail to use for the given model, filling in its values as required.\nvoid P_DefaultTrail (unsigned int entityeffects, unsigned int modelflags, int *trailid, int *trailpalidx)\n{\n\t// TODO: EF_BRIGHTFIELD should probably be handled in here somewhere\n\t// TODO: make trail default color into RGB values instead of indexes\n\tif (!pe)\n\t\treturn;\n\n\tif (entityeffects & EF_BRIGHTFIELD)\n\t{\n\t\t*trailid = P_FindParticleType(\"EF_BRIGHTFIELD\");\n\t\t*trailpalidx = 70;\n\t}\n\telse if (entityeffects & DPEF_FLAME)\n\t{\n\t\t*trailid = P_FindParticleType(\"EF_FLAME\");\n\t\t*trailpalidx = 70;\n\t}\n\telse if (entityeffects & DPEF_STARDUST)\n\t{\n\t\t*trailid = P_FindParticleType(\"EF_STARDUST\");\n\t\t*trailpalidx = 70;\n\t}\n\telse if (modelflags & MF_ROCKET)\n\t\tP_SelectableTrail(trailid, trailpalidx, &r_rockettrail, P_FindParticleType(\"TR_ROCKET\"), 109, P_FindParticleType(\"TR_GRENADE\"), 6);\n\telse if (modelflags & MF_GRENADE)\n\t\tP_SelectableTrail(trailid, trailpalidx, &r_grenadetrail, P_FindParticleType(\"TR_GRENADE\"), 6, P_FindParticleType(\"TR_ROCKET\"), 109);\n\telse if (modelflags & MF_GIB)\n\t{\n\t\t*trailid = P_FindParticleType(\"TR_BLOOD\");\n\t\t*trailpalidx = 70;\n\t}\n\telse if (modelflags & MF_TRACER)\n\t{\n\t\t*trailid = P_FindParticleType(\"TR_WIZSPIKE\");\n\t\t*trailpalidx = 60;\n\t}\n\telse if (modelflags & MF_ZOMGIB)\n\t{\n\t\t*trailid = P_FindParticleType(\"TR_SLIGHTBLOOD\");\n\t\t*trailpalidx = 70;\n\t}\n\telse if (modelflags & MF_TRACER2)\n\t{\n\t\t*trailid = P_FindParticleType(\"TR_KNIGHTSPIKE\");\n\t\t*trailpalidx = 238;\n\t}\n\telse if (modelflags & MF_TRACER3)\n\t{\n\t\t*trailid = P_FindParticleType(\"TR_VORESPIKE\");\n\t\t*trailpalidx = 154;\n\t}\n#ifdef HEXEN2\n\telse if (modelflags & MFH2_BLOODSHOT)\t//these are the hexen2 ones.\n\t{\n\t\t*trailid = P_FindParticleType(\"tr_bloodshot\");\n\t\t*trailpalidx = 136;\n\t}\n\telse if (modelflags & MFH2_FIREBALL)\n\t{\n\t\t*trailid = P_FindParticleType(\"tr_fireball\");\n\t\t*trailpalidx = 424;\n\t}\n\telse if (modelflags & MFH2_ACIDBALL)\n\t{\n\t\t*trailid = P_FindParticleType(\"tr_acidball\");\n\t\t*trailpalidx = 440;\n\t}\n\telse if (modelflags & MFH2_ICE)\n\t{\n\t\t*trailid = P_FindParticleType(\"tr_ice\");\n\t\t*trailpalidx = 408;\n\t}\n\telse if (modelflags & MFH2_SPIT)\n\t{\n\t\t*trailid = P_FindParticleType(\"tr_spit\");\n\t\t*trailpalidx = 260;\n\t}\n\telse if (modelflags & MFH2_SPELL)\n\t{\n\t\t*trailid = P_FindParticleType(\"tr_spell\");\n\t\t*trailpalidx = 260;\n\t}\n\telse if (modelflags & MFH2_VORP_MISSILE)\n\t{\n\t\t*trailid = P_FindParticleType(\"tr_vorpmissile\");\n\t\t*trailpalidx = 302;\n\t}\n\telse if (modelflags & MFH2_SET_STAFF)\n\t{\n\t\t*trailid = P_FindParticleType(\"tr_setstaff\");\n\t\t*trailpalidx = 424;\n\t}\n\telse if (modelflags & MFH2_MAGICMISSILE)\n\t{\n\t\t*trailid = P_FindParticleType(\"tr_magicmissile\");\n\t\t*trailpalidx = 149;\n\t}\n\telse if (modelflags & MFH2_BONESHARD)\n\t{\n\t\t*trailid = P_FindParticleType(\"tr_boneshard\");\n\t\t*trailpalidx = 384;\n\t}\n\telse if (modelflags & MFH2_SCARAB)\n\t{\n\t\t*trailid = P_FindParticleType(\"tr_scarab\");\n\t\t*trailpalidx = 254;\n\t}\n\telse if (modelflags & MFH2_SPIDERBLOOD)\n\t{\n\t\t//spiders\n\t\t*trailid = P_FindParticleType(\"TR_GREENBLOOD\");\n\t\t*trailpalidx = 70;\t//fixme\n\t}\n#endif\n\telse\n\t{\n\t\t*trailid = P_INVALID;\n\t\t*trailpalidx = -1;\n\t}\n}\n"
  },
  {
    "path": "engine/client/r_partset.c",
    "content": "/*\nWARNING: THIS FILE IS GENERATED BY 'generatebuiltin.c'.\nYOU SHOULD NOT EDIT THIS FILE BY HAND\n*/\n\n#include \"bothdefs.h\"\n#ifndef QUAKETC\n#include \"r_partset.h\"\n\n\nchar *particle_set_spikeset =\n// spikeset, originally by Spike\n// with contributions from TimeServ, purplehaze, Jedilamma\n// and some others I probably forgot to mention\n/////////////////////////////////////////////////\n//rocket trails (derived from purplehaze's, with only minor tweeks)\n\n\"r_part rocketsmoke\\n\"\n\"{\\n\"\n\"texture \\\"particles/fteparticlefont.tga\\\"\\n\"\n\"tcoords 97 97 191 191 256\\n\"\n\"step 8\\n\"\n\"scale 7.5\\n\"\n\"alpha 0.8\\n\"\n\"die 2\\n\"\n\"randomvel 3\\n\"\n\"rgb 10 10 10\\n\"\n\"blend modulate\\n\"\n\"spawnmode spiral\\n\"\n\"scalefactor 1\\n\"\n\"spawnvel 5\\n\"\n\"}\\n\"\n\n\"r_part rockettrail\\n\"\n\"{\\n\"\n\"texture \\\"particles/fteparticlefont.tga\\\"\\n\"\n\"tcoords 97 97 191 191 256\\n\"\n\"step 4\\n\"\n\"scale 30\\n\"\n\"alpha 0.3\\n\"\n\"die 1.4\\n\"\n\"diesubrand 0.7\\n\"\n\"randomvel 1\\n\"\n\"rgb 255 50 10\\n\"\n\"rgbdelta -230 -45 -9\\n\"\n\"gravity -25\\n\"\n\"scalefactor 1\\n\"\n\"assoc rocketsmoke\\n\"\n\"spawnvel 10\\n\"\n\"}\\n\"\n\n\"r_part t_rocket\\n\"\n\"{\\n\"\n\"texture \\\"particles/fteparticlefont.tga\\\"\\n\"\n\"tcoords 97 97 191 191 256\\n\"\n\"step 2\\n\"\n\"scale 10\\n\"\n\"alpha 0.6\\n\"\n\"die 0.25\\n\"\n\"rgb 255 192 128\\n\"\n\"rgbdelta -14 -300 -300\\n\"\n\"blend add\\n\"\n\"assoc rockettrail\\n\"\n\"scalefactor 0.8\\n\"\n\"scaledelta -10\\n\"\n\"}\\n\"\n\n\"r_part rockettail\\n\"\n\"{\\n\"\n\"texture \\\"particles/fteparticlefont.tga\\\"\\n\"\n\"tcoords 97 97 191 191 256\\n\"\n\"step 7\\n\"\n\"scale 10\\n\"\n\"alpha 0.3\\n\"\n\"die 10\\n\"\n\"randomvel 64\\n\"\n\"veladd 512\\n\"\n\"rgb 192 192 192\\n\"\n\"gravity 100\\n\"\n\"cliptype rockettail\\n\"\n\"}\\n\"\n\n\"r_part t_altrocket\\n\"\n\"{\\n\"\n\"texture \\\"particles/fteparticlefont.tga\\\"\\n\"\n\"tcoords 97 97 191 191 256\\n\"\n\"step 4\\n\"\n\"scale 10\\n\"\n\"alpha 0.3\\n\"\n\"die 0.7\\n\"\n\"randomvel 32\\n\"\n\"veladd 32\\n\"\n\"rgb 255 198 128\\n\"\n\"rgbdelta -64 0 0\\n\"\n\"gravity -100\\n\"\n\"blend add\\n\"\n\"assoc rockettail\\n\"\n\"}\\n\"\n\n// te_railtrail, used with Quake 2 railgun and also used with\n// TeamFortress engineer railgun\n\"r_part railtrailinner\\n\"\n\"{\\n\"\n\"step 30\\n\"\n\"scale 5\\n\"\n\"die 1\\n\"\n\"alpha 0.5\\n\"\n\"rgb 255 255 255\\n\"\n\"blend add\\n\"\n\"type beam\\n\"\n\"spawnvel 2 2\\n\"\n\"}\\n\"\n\n\"r_part railtrail240\\n\"\n\"{\\n\"\n\"step 15\\n\"\n\"scale 3\\n\"\n\"die 1\\n\"\n\"alpha 0\\n\"\n\"rgb 32 32 255\\n\"\n\"rampmode delta\\n\"\n\"ramp -255 -255 0 -2.5 0\\n\"\n\"ramp 0 0 0 0.65 0\\n\"\n\"ramp 0 0 0 0.65 0\\n\"\n\"ramp 0 0 0 0.65 0\\n\"\n\"ramp 0 0 -128 0.65 10\\n\"\n\"blend add\\n\"\n\"type beam\\n\"\n\"spawnmode spiral\\n\"\n\"spawnparam1 256\\n\"\n\"spawnparam2 240\\n\"\n\"spawnvel 12\\n\"\n\"assoc railtrailinner\\n\"\n\"}\\n\"\n\n\"r_part railtrail120\\n\"\n\"{\\n\"\n\"step 15\\n\"\n\"scale 3\\n\"\n\"die 1\\n\"\n\"alpha 0\\n\"\n\"rgb 32 32 255\\n\"\n\"rampmode delta\\n\"\n\"ramp -255 -255 0 -2.5 0\\n\"\n\"ramp 0 0 0 0.65 0\\n\"\n\"ramp 0 0 0 0.65 0\\n\"\n\"ramp 0 0 0 0.65 0\\n\"\n\"ramp 0 0 -128 0.65 10\\n\"\n\"blend add\\n\"\n\"type beam\\n\"\n\"spawnmode spiral\\n\"\n\"spawnparam1 256\\n\"\n\"spawnparam2 120\\n\"\n\"spawnvel 12\\n\"\n\"assoc railtrail240\\n\"\n\"}\\n\"\n\n\"r_part te_railtrail\\n\"\n\"{\\n\"\n\"step 15\\n\"\n\"scale 3\\n\"\n\"die 1\\n\"\n\"alpha 0\\n\"\n\"rgb 32 32 255\\n\"\n\"rampmode delta\\n\"\n\"ramp -255 -255 0 -2.5 0\\n\"\n\"ramp 0 0 0 0.65 0\\n\"\n\"ramp 0 0 0 0.65 0\\n\"\n\"ramp 0 0 0 0.65 0\\n\"\n\"ramp 0 0 -128 0.65 10\\n\"\n\"blend add\\n\"\n\"type beam\\n\"\n\"spawnmode spiral\\n\"\n\"spawnparam1 256\\n\"\n\"spawnvel 12\\n\"\n\"assoc railtrail120\\n\"\n\"}\\n\"\n\n\"r_part shortfume\\n\"\n\"{\\n\"\n\"texture \\\"particles/fteparticlefont.tga\\\"\\n\"\n\"tcoords 97 97 191 191 256\\n\"\n\"scale 15\\n\"\n\"scaledelta 20\\n\"\n\"alpha 0.5\\n\"\n\"step 8\\n\"\n\"die 0.3\\n\"\n\"randomvel 12\\n\"\n\"scaledelta 0.81\\n\"\n\"rgb 150 150 150\\n\"\n\"}\\n\"\n\n\"r_part t_grenade\\n\"\n\"{\\n\"\n\"texture \\\"particles/fteparticlefont.tga\\\"\\n\"\n\"tcoords 97 97 191 191 256\\n\"\n\"step 24\\n\"\n\"scale 16\\n\"\n\"scaledelta 4\\n\"\n\"alpha 0.3\\n\"\n\"die 4\\n\"\n\"randomvel 8\\n\"\n\"veladd 15\\n\"\n\"rgb 140 140 140\\n\"\n\"rgbdelta -55 -55 -55\\n\"\n\"gravity -50\\n\"\n\"scalefactor 0.0\\n\"\n\"assoc shortfume\\n\"\n\"}\\n\"\n\n//cool's blood trails (cos they're cooler)\n\"r_part t_gib\\n\"\n\"{\\n\"\n\"texture \\\"particles/fteparticlefont.tga\\\"\\n\"\n\"tcoords 1 1 63 63 256 2 64\\n\"\n\"step 32\\n\"\n\"scale 64\\n\"\n\"alpha 0.6\\n\"\n\"die 1\\n\"\n\"randomvel 64\\n\"\n\"veladd 10\\n\"\n\"rotationspeed 90\\n\"\n\"rotationstart 0 360\\n\"\n\"rgb 128 0 0\\n\"\n\"gravity 200\\n\"\n\"scalefactor 0.8\\n\"\n\"scaledelta -10\\n\"\n\"stains 5\\n\"\n\"}\\n\"\n\n\"r_part t_zomgib\\n\"\n\"{\\n\"\n\"texture \\\"particles/fteparticlefont.tga\\\"\\n\"\n\"tcoords 1 1 63 63 256 2 64\\n\"\n\"step 64\\n\"\n\"scale 64\\n\"\n\"alpha 0.6\\n\"\n\"die 1\\n\"\n\"randomvel 64\\n\"\n\"veladd 10\\n\"\n\"rotationspeed 90\\n\"\n\"rotationstart 0 360\\n\"\n\"rgb 32 0 0\\n\"\n\"gravity 200\\n\"\n\"scalefactor 0.8\\n\"\n\"scaledelta -10\\n\"\n\"stains 5\\n\"\n\"}\\n\"\n\n\"r_part t_tracer\\n\"\n\"{\\n\"\n\"texture \\\"particles/fteparticlefont.tga\\\"\\n\"\n\"tcoords 1 97 95 191 256\\n\"\n\"scale 15\\n\"\n\"step 5\\n\"\n\"alpha 0.6\\n\"\n\"rgb 192 192 48\\n\"\n\"die 1\\n\"\n\"veladd 50\\n\"\n\"randomvel 50\\n\"\n\"friction 4\\n\"\n\"scalefactor 0.825\\n\"\n\"}\\n\"\n\n\"r_part t_tracer2\\n\"\n\"{\\n\"\n\"texture \\\"particles/fteparticlefont.tga\\\"\\n\"\n\"tcoords 1 97 95 191 256\\n\"\n\"scale 15\\n\"\n\"step 5\\n\"\n\"alpha 0.6\\n\"\n\"die 1\\n\"\n\"rgb 192 96 48\\n\"\n\"veladd 50\\n\"\n\"randomvel 50\\n\"\n\"friction 4\\n\"\n\"scalefactor 0.825\\n\"\n\"}\\n\"\n\n\"r_part t_tracer3\\n\"\n\"{\\n\"\n\"texture \\\"particles/fteparticlefont.tga\\\"\\n\"\n\"tcoords 1 97 95 191 256\\n\"\n\"scale 10\\n\"\n\"scaledelta -10\\n\"\n\"step 5\\n\"\n\"alpha 0.9\\n\"\n\"die 0.75\\n\"\n\"rgb 192 96 192\\n\"\n\"veladd 20\\n\"\n\"randomvel 5\\n\"\n\"spawnmode spiral\\n\"\n\"spawnvel 60 0\\n\"\n\"friction 4\\n\"\n\"scalefactor 0.825\\n\"\n\"}\\n\"\n\n//qw blood\n\"r_part te_lightningblood\\n\"\n\"{\\n\"\n\"texture \\\"particles/fteparticlefont.tga\\\"\\n\"\n\"tcoords 193 97 255 159 256\\n\"\n\"count 3\\n\"\n\"scale 20\\n\"\n\"alpha 0.4\\n\"\n\"die 2\\n\"\n\"randomvel 32\\n\"\n\"veladd 32\\n\"\n\"rgb 192 0 0\\n\"\n\"rgbdelta -128 0 0\\n\"\n\"gravity 100\\n\"\n\"friction 1\\n\"\n\"stains 1\\n\"\n\"blend add\\n\"\n\"}\\n\"\n\n//qw blood\n\"r_part te_blood\\n\"\n\"{\\n\"\n\"texture \\\"particles/fteparticlefont.tga\\\"\\n\"\n\"tcoords 193 97 255 159 256\\n\"\n\"count 10\\n\"\n\"scale 10\\n\"\n\"alpha 0.3\\n\"\n\"die 2\\n\"\n\"randomvel 40\\n\"\n\"rgb 220 0 0\\n\"\n\"rgbdelta -100 0 0\\n\"\n\"gravity 200\\n\"\n\"stains 2\\n\"\n\"scalefactor 0.9\\n\"\n\"rotationstart 0 360\\n\"\n\"}\\n\"\n\n//nq blood\n\"r_part pe_73\\n\"\n\"{\\n\"\n\"texture \\\"particles/fteparticlefont.tga\\\"\\n\"\n\"tcoords 193 97 255 159 256\\n\"\n\"count 1\\n\"\n\"scale 20\\n\"\n\"alpha 0.3\\n\"\n\"die 2\\n\"\n\"randomvel 40\\n\"\n\"rgb 220 0 0\\n\"\n\"rgbdelta -100 0 0\\n\"\n\"gravity 200\\n\"\n\"stains 2\\n\"\n\"scalefactor 0.9\\n\"\n\"rotationstart 0 360\\n\"\n\"}\\n\"\n\n/////////////////////////////////////////////////\n//rocket explosions\n\"r_part ember\\n\"\n\"{\\n\"\n\"count 1\\n\"\n\"texture \\\"particles/fteparticlefont.tga\\\"\\n\"\n\"tcoords 97 97 191 191 256\\n\"\n\"rgb 255 128 76\\n\"\n\"alpha 0\\n\"\n\"scale 15\\n\"\n\"scalefactor 1\\n\"\n\"friction 8\\n\"\n\"gravity 50\\n\"\n\"die 1\\n\"\n\"blend add\\n\"\n\"randomvel 5\\n\"\n\"veladd 1\\n\"\n\"rampmode delta\\n\"\n\"ramp 0 0 0 -0.5 0\\n\"\n\"ramp 0 0 0 0.1 0\\n\"\n\"ramp 0 0 0 0.1 0\\n\"\n\"ramp 0 0 0 0.1 0\\n\"\n\"ramp 0 0 0 0.1 0\\n\"\n\"ramp 0 0 0 0.1 0\\n\"\n\"}\\n\"\n\n//the bits that fly off\n\"r_part expgib\\n\"\n\"{\\n\"\n\"cliptype expgib\\n\"\n\"texture \\\"particles/fteparticlefont.tga\\\"\\n\"\n\"tcoords 97 97 191 191 256\\n\"\n\"alpha 0\\n\"\n\"count 16\\n\"\n\"die 1\\n\"\n\"randomvel 128\\n\"\n\"gravity 50\\n\"\n\"friction 2\\n\"\n\"emit ember\\n\"\n\"emitinterval 0.01\\n\"\n\"spawnmode circle\\n\"\n\"}\\n\"\n\n//the heart of the explosion\n\"r_part te_explosion\\n\"\n\"{\\n\"\n\"texture \\\"particles/fteparticlefont.tga\\\"\\n\"\n\"tcoords 97 97 191 191 256\\n\"\n\"count 1\\n\"\n\"scale 200\\n\"\n\"scalefactor 1\\n\"\n\"die 1\\n\"\n\"rgb 255 128 76\\n\"\n\"rgbdelta 0 -32 -32\\n\"\n\"friction 1\\n\"\n\"blend add\\n\"\n\"assoc expgib\\n\"\n\"}\\n\"\n\n\"r_part gunshotsmoke\\n\"\n\"{\\n\"\n\"texture \\\"particles/fteparticlefont.tga\\\"\\n\"\n\"tcoords 1 65 31 95 256 8 32\\n\"\n\"count 3\\n\"\n\"scale 25\\n\"\n\"scalefactor 1\\n\"\n\"die 0.8\\n\"\n\"alpha 0.12\\n\"\n\"rgb 32 32 32\\n\"\n\"blend add\\n\"\n\"spawnmode ball\\n\"\n\"spawnorg 2\\n\"\n\"spawnvel 20\\n\"\n\"veladd -20\\n\"\n\"}\\n\"\n\n\"r_part te_gunshot\\n\"\n\"{\\n\"\n\"type texturedspark\\n\"\n\"texture \\\"particles/fteparticlefont.tga\\\"\\n\"\n\"tcoords 1 65 31 95 256 8 32\\n\"\n\"count 3\\n\"\n\"scale 2\\n\"\n\"scalefactor 1\\n\"\n\"alpha 0.5\\n\"\n\"die 0.8\\n\"\n\"rgb 255 128 0\\n\"\n\"blend add\\n\"\n\"spawnmode ball\\n\"\n\"spawnorg 1\\n\"\n\"spawnvel 100\\n\"\n\"veladd -80\\n\"\n\"friction 0.3\\n\"\n\"gravity 400\\n\"\n\"assoc gunshotsmoke\\n\"\n\"}\\n\"\n\n\"r_part spikecore\\n\"\n\"{\\n\"\n\"texture \\\"particles/fteparticlefont.tga\\\"\\n\"\n\"tcoords 1 97 95 191 256\\n\"\n\"count 1\\n\"\n\"scale 1\\n\"\n\"scalefactor 1\\n\"\n\"scaledelta 190\\n\"\n\"die 0.1\\n\"\n\"alpha 0.6\\n\"\n\"rgb 255 128 0\\n\"\n\"blend add\\n\"\n\"assoc gunshotsmoke\\n\"\n\"}\\n\"\n\n\"r_part te_spike\\n\"\n\"{\\n\"\n\"type sparkfan\\n\"\n\"count 10\\n\"\n\"scale 1\\n\"\n\"scalefactor 1\\n\"\n\"alpha 0.5\\n\"\n\"die 0.2\\n\"\n\"rgb 255 128 0\\n\"\n\"blend add\\n\"\n\"spawnmode ball\\n\"\n\"spawnorg 12\\n\"\n\"spawnvel 300\\n\"\n\"assoc spikecore\\n\"\n\"}\\n\"\n\n\"r_part te_lavasplash\\n\"\n\"{\\n\"\n\"texture \\\"particles/fteparticlefont.tga\\\"\\n\"\n\"tcoords 129 1 191 63 256\\n\"\n\"count 654\\n\"\n\"scale 15\\n\"\n\"alpha 0.7\\n\"\n\"die 4\\n\"\n\"randomvel 64\\n\"\n\"rgb 255 128 128\\n\"\n\"gravity 50\\n\"\n\"blend add\\n\"\n\"spawnorg 192 64\\n\"\n\"up 48\\n\"\n\"}\\n\"\n\n//////////////////////////////////////////////////\n//Teleport splash\n\n//two rings moving upwards, costs less\n\"r_part teleportsplashdown\\n\"\n\"{\\n\"\n\"texture \\\"particles/fteparticlefont.tga\\\"\\n\"\n\"tcoords 193 1 255 63 256\\n\"\n\"count 32\\n\"\n\"scale 32\\n\"\n\"scalefactor 1\\n\"\n\"alpha 0.3\\n\"\n\"die 1\\n\"\n\"veladd -52\\n\"\n\"rgb 255 255 255\\n\"\n\"friction 1\\n\"\n\"spawnorg 32 0\\n\"\n\"spawnmode uniformcircle\\n\"\n\"}\\n\"\n\"r_part te_teleportsplash\\n\"\n\"{\\n\"\n\"texture \\\"particles/fteparticlefont.tga\\\"\\n\"\n\"tcoords 193 1 255 63 256\\n\"\n\"count 32\\n\"\n\"scale 32\\n\"\n\"scalefactor 1\\n\"\n\"alpha 0.3\\n\"\n\"die 1\\n\"\n\"veladd 52\\n\"\n\"rgb 255 255 255\\n\"\n\"friction 1\\n\"\n\"spawnorg 32 0\\n\"\n\"spawnmode uniformcircle\\n\"\n\"assoc teleportsplashdown\\n\"\n\"}\\n\"\n\n//flame effect\n\"r_part cu_flame\\n\"\n\"{\\n\"\n\"texture \\\"particles/fteparticlefont.tga\\\"\\n\"\n\"tcoords 129 1 191 63 256\\n\"\n\"count 1024\\n\"\n\"scale 0.4\\n\"\n\"scalerand 6\\n\"\n\"scalefactor 1\\n\"\n\"alpha 0.4\\n\"\n\"die 0.8\\n\"\n\"randomvel 4 24\\n\"\n\"veladd -24\\n\"\n\"rgb 255 128 76\\n\"\n\"blend add\\n\"\n\"up -8\\n\"\n\"spawnorg 6 0\\n\"\n\"spawnvel -15 0\\n\"\n\"}\\n\"\n\n//flame effect\n\"r_part cu_torch\\n\"\n\"{\\n\"\n\"texture \\\"particles/fteparticlefont.tga\\\"\\n\"\n\"tcoords 129 1 191 63 256\\n\"\n\"count 256\\n\"\n\"scale 3\\n\"\n\"scalefactor 1\\n\"\n\"alpha 0.7\\n\"\n\"die 0.5\\n\"\n\"randomvel 8\\n\"\n\"veladd -32\\n\"\n\"rgb 255 128 76\\n\"\n\"blend add\\n\"\n\"spawnmode circle\\n\"\n\"spawnorg 4 1\\n\"\n\"spawnvel -12 -8\\n\"\n\"}\\n\"\n\n\"r_part explodesprite\\n\"\n\"{\\n\"\n\"texture \\\"particles/fteparticlefont.tga\\\"\\n\"\n\"tcoords 97 97 191 191 256\\n\"\n\"count 180\\n\"\n\"scale 70\\n\"\n\"scaledelta -140\\n\"\n\"scalefactor 1\\n\"\n\"alpha 0.2\\n\"\n\"die 0.5\\n\"\n\"randomvel 23\\n\"\n\"veladd -20\\n\"\n\"rgb 255 128 76\\n\"\n\"blend add\\n\"\n\"spawnorg 4 1\\n\"\n\"spawnvel -8 -2\\n\"\n\"up -8\\n\"\n\"}\\n\"\n\n//you'll probably never see this one\n\"r_part ef_entityparticles\\n\"\n\"{\\n\"\n\"texture \\\"particles/fteparticlefont.tga\\\"\\n\"\n\"tcoords 1 97 95 191 256\\n\"\n\"count 1\\n\"\n\"scale 15\\n\"\n\"alpha 0.2\\n\"\n\"die 0\\n\"\n\"veladd 16\\n\"\n\"rgb 255 128 128\\n\"\n\"blend add\\n\"\n\"}\\n\"\n\n// emp effect, based off of purplehaze's idea\n\"r_part empshocktrail\\n\"\n\"{\\n\"\n\"texture \\\"particles/fteparticlefont.tga\\\"\\n\"\n\"tcoords 193 1 255 63 256\\n\"\n\"step 3.2\\n\"\n\"scale 3\\n\"\n\"alpha 0.7\\n\"\n\"die 0.2\\n\"\n\"rgb 64 0 255\\n\"\n\"blend add\\n\"\n\"scalefactor 1\\n\"\n\"spawnorg 12 0\\n\"\n\"}\\n\"\n\n\"r_part empcore\\n\"\n\"{\\n\"\n\"texture \\\"particles/fteparticlefont.tga\\\"\\n\"\n\"tcoords 193 1 255 63 256\\n\"\n\"count 90\\n\"\n\"scale 55\\n\"\n\"scaledelta -110\\n\"\n\"die 0.55\\n\"\n\"rgb 168 128 255\\n\"\n\"spawnmode circle\\n\"\n\"spawnorg 12\\n\"\n\"spawnvel -192\\n\"\n\"blend add\\n\"\n\"scalefactor 0.8\\n\"\n\"emit empshocktrail\\n\"\n\"emitinterval -1\\n\"\n\"}\\n\"\n\n\n\"r_part empflash\\n\"\n\"{\\n\"\n\"die 0.1\\n\"\n\"texture \\\"particles/fteparticlefont.tga\\\"\\n\"\n\"tcoords 193 1 255 63 256\\n\"\n\"alpha 1\\n\"\n\"count 1\\n\"\n\"scale 400\\n\"\n\"scaledelta -4000\\n\"\n\"alphadelta 0\\n\"\n\"rgb 192 160 255\\n\"\n\"blend add\\n\"\n\"scalefactor 1\\n\"\n\"assoc empcore\\n\"\n\"}\\n\"\n\n\"r_part te_tarexplosion\\n\"\n\"{\\n\"\n\"texture \\\"particles/fteparticlefont.tga\\\"\\n\"\n\"tcoords 1 97 95 191 256\\n\"\n\"count 120\\n\"\n\"scale 35\\n\"\n\"die 0.75\\n\"\n\"alpha 0.4\\n\"\n\"rgb 128 0 255\\n\"\n\"rampmode delta\\n\"\n\"ramp -32 0 0 0\\n\"\n\"ramp -32 0 0 0\\n\"\n\"ramp -32 0 0 2\\n\"\n\"friction -0.9\\n\"\n\"blend add\\n\"\n\"spawnmode uniformcircle\\n\"\n\"spawnorg 24 0\\n\"\n\"spawnvel 280 0\\n\"\n\"scalefactor 1\\n\"\n\"emit empshocktrail\\n\"\n\"emitinterval -1\\n\"\n\"assoc empflash\\n\"\n\"}\\n\"\n\n\"r_part pe_default\\n\"\n\"{\\n\"\n\"texture \\\"particles/fteparticlefont.tga\\\"\\n\"\n\"tcoords 1 97 95 191 256\\n\"\n\"count 1\\n\"\n\"scale 4\\n\"\n\"veladd 15\\n\"\n\"die 0.4\\n\"\n\"alphadelta 0\\n\"\n\"diesubrand 0.4\\n\"\n\"gravity 40\\n\"\n\"spawnorg 8\\n\"\n\"}\\n\"\n\n\"r_part pe_defaulttrail\\n\"\n\"{\\n\"\n\"texture \\\"particles/fteparticlefont.tga\\\"\\n\"\n\"tcoords 1 97 95 191 256\\n\"\n\"step 12\\n\"\n\"die 1\\n\"\n\"scale 10\\n\"\n\"scaledelta -10\\n\"\n\"veladd 15\\n\"\n\"spawnorg 1\\n\"\n\"scalefactor 0.8\\n\"\n\"}\\n\"\n\n\"r_part pe_pointfile\\n\"\n\"{\\n\"\n\"texture \\\"particles/fteparticlefont.tga\\\"\\n\"\n\"tcoords 1 97 95 191 256\\n\"\n\"count 1\\n\"\n\"scale 50\\n\"\n\"die 30\\n\"\n\"alphadelta 0\\n\"\n\"rgb 255 255 0\\n\"\n\"}\\n\"\n\n\"r_effect \\\"progs/s_explod.spr\\\" explodesprite 1\\n\"\n\"r_effect \\\"progs/flame.spr\\\" explodesprite 1\\n\"\n\n\"r_effect \\\"progs/flame2.mdl\\\" cu_flame 1\\n\"\n\"r_effect \\\"progs/flame.mdl\\\" cu_torch\\n\"\n\"r_trail \\\"progs/e_spike1.mdl\\\" te_railtrail\\n\"\n;\n\n\n\n//////////////////////////////////////////////////////\n\n\nchar *particle_set_faithful =\n// faithful, by TimeServ\n\"r_part t_gib\\n\"\n\"{\\n\"\n\"texture \\\"particles/quake\\\"\\n\"\n\"step 3\\n\"\n\"scale 4\\n\"\n\"die 2\\n\"\n\"alphadelta 0\\n\"\n\"randomvel 80\\n\"\n\"veladd 100\\n\"\n\"colorindex 67 4\\n\"\n\"gravity 40\\n\"\n\"spawnorg 3\\n\"\n\"stains 1\\n\"\n\"}\\n\"\n\n\"r_part t_zomgib\\n\"\n\"{\\n\"\n\"texture \\\"particles/quake\\\"\\n\"\n\"step 6\\n\"\n\"scale 4\\n\"\n\"die 2\\n\"\n\"alphadelta 0\\n\"\n\"randomvel 72\\n\"\n\"veladd 100\\n\"\n\"colorindex 67 4\\n\"\n\"gravity 40\\n\"\n\"spawnorg 3\\n\"\n\"stains 1\\n\"\n\"}\\n\"\n\n\"r_part t_tracer3\\n\"\n\"{\\n\"\n\"texture \\\"particles/quake\\\"\\n\"\n\"step 3\\n\"\n\"scale 4\\n\"\n\"die 0.3\\n\"\n\"alphadelta 0\\n\"\n\"colorindex 152 4\\n\"\n\"spawnorg 8\\n\"\n\"}\\n\"\n\n\"r_part t_tracer\\n\"\n\"{\\n\"\n\"texture \\\"particles/quake\\\"\\n\"\n\"step 3\\n\"\n\"scale 4\\n\"\n\"die 0.5\\n\"\n\"alphadelta 0\\n\"\n\"colorindex 52\\n\"\n\"citracer\\n\"\n\"spawnvel 30 0\\n\"\n\"spawnmode tracer\\n\"\n\"}\\n\"\n\n\"r_part t_tracer2\\n\"\n\"{\\n\"\n\"texture \\\"particles/quake\\\"\\n\"\n\"step 3\\n\"\n\"scale 4\\n\"\n\"die 0.5\\n\"\n\"alphadelta 0\\n\"\n\"colorindex 230\\n\"\n\"citracer\\n\"\n\"spawnvel 30 0\\n\"\n\"spawnmode tracer\\n\"\n\"}\\n\"\n\n\"r_part t_rocket\\n\"\n\"{\\n\"\n\"texture \\\"particles/quake\\\"\\n\"\n\"step 3\\n\"\n\"scale 4\\n\"\n\"die 1.2\\n\"\n\"diesubrand 0.6\\n\"\n\"rampmode absolute\\n\"\n\"rampindex 109 1.0\\n\"\n\"rampindex 107 0.833\\n\"\n\"rampindex 6 0.667\\n\"\n\"rampindex 5 0.5\\n\"\n\"rampindex 4 0.333\\n\"\n\"rampindex 3 0.167\\n\"\n\"spawnorg 3\\n\"\n\"gravity -40\\n\"\n\"}\\n\"\n\n\"r_part t_altrocket\\n\"\n\"{\\n\"\n\"texture \\\"particles/quake\\\"\\n\"\n\"step 3\\n\"\n\"scale 4\\n\"\n\"die 1.2\\n\"\n\"diesubrand 0.6\\n\"\n\"rampmode absolute\\n\"\n\"rampindexlist 109 107 6 5 4 3\\n\"\n\"spawnorg 3\\n\"\n\"gravity -40\\n\"\n\"}\\n\"\n\n\"r_part t_grenade\\n\"\n\"{\\n\"\n\"texture \\\"particles/quake\\\"\\n\"\n\"step 3\\n\"\n\"scale 4\\n\"\n\"die 0.8\\n\"\n\"diesubrand 0.6\\n\"\n\"rampmode absolute\\n\"\n\"rampindex 6 0.667\\n\"\n\"rampindex 5 0.5\\n\"\n\"rampindex 4 0.333\\n\"\n\"rampindex 3 0.167\\n\"\n\"spawnorg 3\\n\"\n\"gravity -40\\n\"\n\"}\\n\"\n\n\"r_part pe_size3\\n\"\n\"{\\n\"\n\"texture \\\"particles/quake\\\"\\n\"\n\"count 1\\n\"\n\"scale 4\\n\"\n\"veladd 15\\n\"\n\"die 0.4\\n\"\n\"alphadelta 0\\n\"\n\"diesubrand 0.4\\n\"\n\"gravity 40\\n\"\n\"spawnorg 24\\n\"\n\"}\\n\"\n\n\"r_part pe_size2\\n\"\n\"{\\n\"\n\"texture \\\"particles/quake\\\"\\n\"\n\"count 1\\n\"\n\"scale 4\\n\"\n\"veladd 15\\n\"\n\"die 0.4\\n\"\n\"alphadelta 0\\n\"\n\"diesubrand 0.4\\n\"\n\"gravity 40\\n\"\n\"spawnorg 16\\n\"\n\"}\\n\"\n\n\"r_part pe_default\\n\"\n\"{\\n\"\n\"texture \\\"particles/quake\\\"\\n\"\n\"count 1\\n\"\n\"scale 4\\n\"\n\"veladd 15\\n\"\n\"die 0.4\\n\"\n\"alphadelta 0\\n\"\n\"diesubrand 0.4\\n\"\n\"gravity 40\\n\"\n\"spawnorg 8\\n\"\n\"}\\n\"\n\n\"r_part explode2\\n\"\n\"{\\n\"\n\"texture \\\"particles/quake\\\"\\n\"\n\"count 512\\n\"\n\"scale 4\\n\"\n\"alphadelta 0\\n\"\n\"die 0.5333\\n\"\n\"diesubrand 0.2667\\n\"\n\"rampmode absolute\\n\"\n\"rampindexlist 111 110 109 108 107 106 104 102 \\n\"\n\"randomvel 256\\n\"\n\"gravity 40\\n\"\n\"friction 1\\n\"\n\"spawnorg 16\\n\"\n\"}\\n\"\n\n\"r_part te_explosion\\n\"\n\"{\\n\"\n\"texture \\\"particles/quake\\\"\\n\"\n\"count 512\\n\"\n\"scale 4\\n\"\n\"die 0.8\\n\"\n\"diesubrand 0.4\\n\"\n\"randomvel 256\\n\"\n\"rampmode absolute\\n\"\n\"rampindexlist 111 109 107 105 103 101 99 97 \\n\"\n\"gravity 40\\n\"\n\"friction -4\\n\"\n\"spawnorg 16\\n\"\n\"assoc explode2\\n\"\n\"}\\n\"\n\n\"r_part blobexp2b\\n\"\n\"{\\n\"\n\"texture \\\"particles/quake\\\"\\n\"\n\"count 256\\n\"\n\"scale 4\\n\"\n\"alphadelta 0\\n\"\n\"die 1.4\\n\"\n\"colorindex 150 6\\n\"\n\"gravity 40\\n\"\n\"friction 4 0\\n\"\n\"spawnorg 16\\n\"\n\"randomvel 256\\n\"\n\"}\\n\"\n\"r_part blobexp1b\\n\"\n\"{\\n\"\n\"texture \\\"particles/quake\\\"\\n\"\n\"count 256\\n\"\n\"scale 4\\n\"\n\"alphadelta 0\\n\"\n\"die 1.4\\n\"\n\"colorindex 66 6\\n\"\n\"gravity 40\\n\"\n\"friction -4 0\\n\"\n\"spawnorg 16\\n\"\n\"randomvel 256\\n\"\n\"assoc blobexp2b\\n\"\n\"}\\n\"\n\n\"r_part blobexp2\\n\"\n\"{\\n\"\n\"texture \\\"particles/quake\\\"\\n\"\n\"count 256\\n\"\n\"scale 4\\n\"\n\"alphadelta 0\\n\"\n\"die 1\\n\"\n\"colorindex 150 6\\n\"\n\"gravity 40\\n\"\n\"friction 4 0\\n\"\n\"spawnorg 16\\n\"\n\"randomvel 256\\n\"\n\"assoc blobexp1b\\n\"\n\"}\\n\"\n\"r_part te_tarexplosion\\n\"\n\"{\\n\"\n\"texture \\\"particles/quake\\\"\\n\"\n\"count 256\\n\"\n\"scale 4\\n\"\n\"alphadelta 0\\n\"\n\"die 1\\n\"\n\"colorindex 66 6\\n\"\n\"gravity 40\\n\"\n\"friction -4 0\\n\"\n\"randomvel 256\\n\"\n\"spawnorg 16\\n\"\n\"assoc blobexp2\\n\"\n\"}\\n\"\n\n\"r_part te_teleportsplash\\n\"\n\"{\\n\"\n\"texture \\\"particles/quake\\\"\\n\"\n\"count 896\\n\"\n\"scale 4\\n\"\n\"alphadelta 0\\n\"\n\"die 0.34\\n\"\n\"diesubrand 0.14\\n\"\n\"colorindex 7 8\\n\"\n\"gravity 40\\n\"\n\"up 4\\n\"\n\"spawnmode telebox\\n\"\n\"spawnorg 16 28\\n\"\n\"spawnvel 113\\n\"\n\"}\\n\"\n\n\"r_part te_lavasplash\\n\"\n\"{\\n\"\n\"texture \\\"particles/quake\\\"\\n\"\n\"count 1024\\n\"\n\"scale 4\\n\"\n\"alphadelta 0\\n\"\n\"die 2.62\\n\"\n\"diesubrand 0.62\\n\"\n\"colorindex 224 8\\n\"\n\"gravity 40\\n\"\n\"spawnorg 128 63\\n\"\n\"spawnvel 113\\n\"\n\"spawnmode lavasplash\\n\"\n\"}\\n\"\n\n\"r_part pe_defaulttrail\\n\"\n\"{\\n\"\n\"texture \\\"particles/quake\\\"\\n\"\n\"step 3\\n\"\n\"scale 4\\n\"\n\"die 0.6\\n\"\n\"diesubrand 0.6\\n\"\n\"spawnorg 3\\n\"\n\"gravity -40\\n\"\n\"}\\n\"\n\n\"r_part pe_pointfile\\n\"\n\"{\\n\"\n\"texture \\\"particles/quake\\\"\\n\"\n\"count 1\\n\"\n\"scale 4\\n\"\n\"die 30\\n\"\n\"alphadelta 0\\n\"\n\"rgb 255 255 0\\n\"\n\"}\\n\"\n\n;\n\n\n\n//////////////////////////////////////////////////////\n\n\nchar *particle_set_highfps =\n// highfps, originally submitted by 'ShadowWalker'\n// rehashed by TimeServ\n\"r_part t_gib\\n\"\n\"{\\n\"\n\"texture \\\"particles/bloodtrail\\\"\\n\"\n\"step 12\\n\"\n\"scale 10\\n\"\n\"die 1\\n\"\n\"randomvel 32\\n\"\n\"veladd 32\\n\"\n\"rgb 64 0 0\\n\"\n\"rgbdelta -128 0 0\\n\"\n\"}\\n\"\n\"r_part t_zomgib\\n\"\n\"{\\n\"\n\"texture \\\"particles/bloodtrail\\\"\\n\"\n\"step 16\\n\"\n\"scale 8\\n\"\n\"die 1\\n\"\n\"randomvel 32\\n\"\n\"veladd 32\\n\"\n\"rgb 192 0 0\\n\"\n\"rgbdelta -128 0 0\\n\"\n\"}\\n\"\n\n\"r_part t_tracer\\n\"\n\"{\\n\"\n\"texture \\\"particles/tracer\\\"\\n\"\n\"scale 23\\n\"\n\"step 18\\n\"\n\"rgb 192 192 0\\n\"\n\"die 0.5\\n\"\n\"}\\n\"\n\n\"r_part t_tracer2\\n\"\n\"{\\n\"\n\"texture \\\"particles/tracer\\\"\\n\"\n\"scale 23\\n\"\n\"step 18\\n\"\n\"die 0.5\\n\"\n\"rgb 192 96 0\\n\"\n\"}\\n\"\n\n\"r_part t_tracer3\\n\"\n\"{\\n\"\n\"texture \\\"particles/tracer\\\"\\n\"\n\"scale 23\\n\"\n\"step 18\\n\"\n\"die 0.5\\n\"\n\"rgb 192 0 192\\n\"\n\"}\\n\"\n\n\"r_part te_lightningblood\\n\"\n\"{\\n\"\n\"texture \\\"particles/bloodtrail\\\"\\n\"\n\"count 1\\n\"\n\"scale 10\\n\"\n\"die 0.5\\n\"\n\"randomvel 64\\n\"\n\"veladd 128\\n\"\n\"rgb 192 0 0\\n\"\n\"blend add\\n\"\n\"}\\n\"\n\n\"r_part te_blood\\n\"\n\"{\\n\"\n\"texture \\\"particles/bloodtrail\\\"\\n\"\n\"count 1\\n\"\n\"scale 12\\n\"\n\"die 0.5\\n\"\n\"randomvel 32\\n\"\n\"veladd 64\\n\"\n\"spawnvel 0 10\\n\"\n\"rgb 64 0 0\\n\"\n\"}\\n\"\n\n\"r_part sparks\\n\"\n\"{\\n\"\n\"texture \\\"particles/spark\\\"\\n\"\n\"count 32\\n\"\n\"scale 3\\n\"\n\"alpha 1\\n\"\n\"die 1\\n\"\n\"randomvel 256\\n\"\n\"veladd 128\\n\"\n\"rgb 255 128 0\\n\"\n\"blend add\\n\"\n\"cliptype sparks\\n\"\n\"clipcount 1\\n\"\n\"}\\n\"\n\n\"r_part explosioncore\\n\"\n\"{\\n\"\n\"texture \\\"particles/explosion\\\"\\n\"\n\"count 1\\n\"\n\"scale 200\\n\"\n\"scalefactor 1\\n\"\n\"die 1.2\\n\"\n\"rgb 255 128 76\\n\"\n\"blend add\\n\"\n\"assoc sparks\\n\"\n\"}\\n\"\n\n\"r_part te_explosion\\n\"\n\"{\\n\"\n\"texture \\\"particles/explosion\\\"\\n\"\n\"count 8\\n\"\n\"scale 60\\n\"\n\"alpha 0.5\\n\"\n\"die 1\\n\"\n\"rgb 255 128 76\\n\"\n\"blend add\\n\"\n\"assoc explosioncore\\n\"\n\"spawnmode ball\\n\"\n\"spawnorg 64\\n\"\n\"}\\n\"\n\n\"r_part te_railtrail\\n\"\n\"{\\n\"\n\"step 1000000\\n\"\n\"scale 5\\n\"\n\"die 1.2\\n\"\n\"alpha 0.7\\n\"\n\"rgb 16 16 255\\n\"\n\"blend add\\n\"\n\"type beam\\n\"\n\"averageout\\n\"\n\"}\\n\"\n\n//the blob tempent is used quite a bit with teamfortress emp grenades.\n\"r_part te_tarexplosion\\n\"\n\"{\\n\"\n\"texture \\\"particles/blob\\\"\\n\"\n\"count 64\\n\"\n\"scale 30\\n\"\n\"scalefactor 1\\n\"\n\"die 1\\n\"\n\"randomvel 32\\n\"\n\"veladd 0\\n\"\n\"rgb 255 0 196\\n\"\n\"spawnorg 8 56\\n\"\n\"spawnvel 48 8\\n\"\n\"}\\n\"\n\n\"r_part te_gunshot\\n\"\n\"{\\n\"\n\"texture \\\"particles/spark\\\"\\n\"\n\"count 2\\n\"\n\"scale 3\\n\"\n\"alpha 0.7\\n\"\n\"die 0.5\\n\"\n\"randomvel 64\\n\"\n\"rgb 255 128 0\\n\"\n\"blend add\\n\"\n\"}\\n\"\n\n\"r_part te_lavasplash\\n\"\n\"{\\n\"\n\"texture \\\"particles/lava\\\"\\n\"\n\"count 180\\n\"\n\"scale 60\\n\"\n\"alpha 0.5\\n\"\n\"die 1.6\\n\"\n\"rgb 255 128 128\\n\"\n\"spawnorg 178 64\\n\"\n\"up 56\\n\"\n\"scalefactor 1\\n\"\n\"}\\n\"\n\n\"r_part te_teleportsplash\\n\"\n\"{\\n\"\n\"texture \\\"particles/teleport\\\"\\n\"\n\"count 48\\n\"\n\"scale 30\\n\"\n\"scalefactor 1\\n\"\n\"die 0.5\\n\"\n\"randomvel 32\\n\"\n\"veladd 0\\n\"\n\"rgb 255 255 255\\n\"\n\"spawnorg 4 32\\n\"\n\"spawnvel 25 4\\n\"\n\"}\\n\"\n\n\"r_part t_grenade\\n\"\n\"{\\n\"\n\"texture \\\"particles/smoke\\\"\\n\"\n\"step 20\\n\"\n\"scale 21\\n\"\n\"die 0.5\\n\"\n\"randvel 16\\n\"\n\"rgb 128 128 128\\n\"\n\"}\\n\"\n\n\"r_part t_rocket\\n\"\n\"{\\n\"\n\"texture \\\"particles/rocket\\\"\\n\"\n\"step 15\\n\"\n\"scale 30\\n\"\n\"die 0.2\\n\"\n\"rgb 192 48 0\\n\"\n\"blend add\\n\"\n\"assoc t_grenade\\n\"\n\"}\\n\"\n\n\"r_part t_altrocket\\n\"\n\"{\\n\"\n\"texture \\\"particles/rocket\\\"\\n\"\n\"step 15\\n\"\n\"scale 25\\n\"\n\"randomvel 30\\n\"\n\"veladd 30\\n\"\n\"die 0.5\\n\"\n\"rgb 192 48 0\\n\"\n\"blend add\\n\"\n\"}\\n\"\n\n//you'll probably never see this one\n\"r_part ef_entityparticles\\n\"\n\"{\\n\"\n\"texture \\\"j\\\"\\n\"\n\"count 1\\n\"\n\"scale 10\\n\"\n\"alpha 0.3\\n\"\n\"die 0\\n\"\n\"veladd 16\\n\"\n\"rgb 128 128 0\\n\"\n\"}\\n\"\n\n\"r_part pe_default\\n\"\n\"{\\n\"\n\"texture \\\"particles/quake\\\"\\n\"\n\"count 1\\n\"\n\"scale 4\\n\"\n\"veladd 15\\n\"\n\"die 0.5\\n\"\n\"spawnorg 8\\n\"\n\"}\\n\"\n\n\"r_part pe_defaulttrail\\n\"\n\"{\\n\"\n\"texture \\\"particles/quake\\\"\\n\"\n\"step 15\\n\"\n\"die 0.5\\n\"\n\"scale 8\\n\"\n\"veladd 15\\n\"\n\"spawnorg 1\\n\"\n\"}\\n\"\n\n\"r_part pe_pointfile\\n\"\n\"{\\n\"\n\"texture \\\"particles/quake\\\"\\n\"\n\"count 1\\n\"\n\"scale 50\\n\"\n\"die 30\\n\"\n\"alphadelta 0\\n\"\n\"rgb 255 255 0\\n\"\n\"}\\n\"\n\n;\n\n\n\n//////////////////////////////////////////////////////\n\n\nchar *particle_set_high =\n///////////////////////////////\n//rain\n\"r_part te_rain\\n\"\n\"{\\n\"\n\"texture ball; scalefactor 1; count 1; alpha 0.4; rgb 255 255 255; die 2; veladd 2; scale 2; type texturedspark\\n\"\n\"cliptype rainsplash\\n\"\n\"clipbounce 1\\n\"\n\"clipcount 5\\n\"\n\"}\\n\"\n\n\"r_part rainsplash\\n\"\n\"{\\n\"\n\"randomvel 50 50\\n\"\n\"count 1;\\n\"\n\"texture ball; scalefactor 1; alpha 0.1; rgb 255 255 255; die 0.4; scale 50;\\n\"\n\"stretchfactor 4\\n\"\n\"veladd 50; scale 1; type texturedspark\\n\"\n\"gravity 400\\n\"\n\"}\\n\"\n\n///////////////////////////////\n//rocket trail\n\n// flame trail\n\"r_part tr_rocket\\n\"\n\"{\\n\"\n\"texture \\\"particles/fteparticlefont.tga\\\"\\n\"\n\"tcoords 97 97 191 191 256\\n\"\n\"step 1\\n\"\n\"scale 12\\n\"\n\"alpha 0.4\\n\"\n\"die 0.5\\n\"\n\"rgb 255 127 100\\n\"\n\"rgbdelta -14 -300 -300\\n\"\n\"blend add\\n\"\n\"scalefactor 1\\n\"\n\"scaledelta -15\\n\"\n\"}\\n\"\n\n// smoke puffs\n\"r_part +tr_rocket\\n\"\n\"{\\n\"\n\"texture \\\"particles/fteparticlefont.tga\\\"\\n\"\n\"tcoords 97 97 191 191 256\\n\"\n\"step 5\\n\"\n\"scale 30\\n\"\n\"alpha 0.2\\n\"\n\"die 0.75\\n\"\n//diesubrand 10.25\n\"randomvel 0.2\\n\"\n\"rgb 5 5 5\\n\"\n//rgbdelta -230 -45 -9\n\"gravity -15\\n\"\n\"scalefactor 1\\n\"\n\"scaledelta 20\\n\"\n\"spawnvel 5\\n\"\n\"}\\n\"\n\n\n// burst sparks\n\"r_part +tr_rocket\\n\"\n\"{\\n\"\n\"type texturedspark\\n\"\n\"texture \\\"particles/fteparticlefont.tga\\\"\\n\"\n\"tcoords 1 65 31 95 256 8 32\\n\"\n\"count 1\\n\"\n\"scale 2\\n\"\n\"scalefactor 1\\n\"\n\"scaledelta -15\\n\"\n\"alpha 0.2\\n\"\n\"die 0.25\\n\"\n\"rgb 255 128 0\\n\"\n\"blend add\\n\"\n\"spawnmode ball\\n\"\n\"spawnorg 1\\n\"\n\"spawnvel 50\\n\"\n\"veladd 500\\n\"\n\"friction 0.01\\n\"\n\"gravity 100\\n\"\n\"}\\n\"\n\n///////////////////////////////////////////\n//alternate rocket trail, which is used by a handful of qw players.\n//r_part tr_altrocket\n//{\n//}\n\n\n///////////////////////////////////////////\n//grenade trail\n\n\"r_part tr_grenade\\n\"\n\"{\\n\"\n\"texture \\\"particles/fteparticlefont.tga\\\"\\n\"\n\"tcoords 97 97 191 191 256\\n\"\n\"step 6\\n\"\n\"scale 32\\n\"\n\"scaledelta 12\\n\"\n\"alpha 0.3\\n\"\n\"die 1.25\\n\"\n\"randomvel 2\\n\"\n\"veladd 15\\n\"\n\"rgb 75 75 75\\n\"\n//rgb 255 50 50\n//rgbdelta -255 -75 -75\n\"gravity -25\\n\"\n\"scalefactor 1\\n\"\n\"blend modulate\\n\"\n\"}\\n\"\n\"r_part +tr_grenade\\n\"\n\"{\\n\"\n\"texture \\\"particles/fteparticlefont.tga\\\"\\n\"\n\"tcoords 97 97 191 191 256\\n\"\n\"scale 1\\n\"\n\"scaledelta 0.25\\n\"\n\"alpha 0.2\\n\"\n\"step 4\\n\"\n\"die 0.8\\n\"\n\"randomvel 0\\n\"\n\"rgb 255 150 150\\n\"\n\"rgbdelta 0 -150 -150\\n\"\n\"type beam\\n\"\n\"blend add\\n\"\n\"}\\n\"\n\n//////////////////////////////////\n//shotgun impacts\n\"r_part gunshotsmoke\\n\"\n\"{\\n\"\n\"texture \\\"particles/fteparticlefont.tga\\\"\\n\"\n\"tcoords 1 65 31 95 256 8 32\\n\"\n\"count 3\\n\"\n\"scale 25\\n\"\n\"scalefactor 1\\n\"\n\"die 0.8\\n\"\n\"alpha 0.12\\n\"\n\"rgb 32 32 32\\n\"\n\"blend add\\n\"\n\"spawnmode ball\\n\"\n\"spawnorg 2\\n\"\n\"spawnvel 20\\n\"\n\"veladd -20\\n\"\n\"}\\n\"\n\"r_part te_gunshot\\n\"\n\"{\\n\"\n\"type texturedspark\\n\"\n\"texture \\\"particles/fteparticlefont.tga\\\"\\n\"\n\"tcoords 1 65 31 95 256 8 32\\n\"\n\"count 3\\n\"\n\"scale 2\\n\"\n\"scalefactor 1\\n\"\n\"alpha 0.5\\n\"\n\"die 0.8\\n\"\n\"rgb 255 128 0\\n\"\n\"blend add\\n\"\n\"spawnmode ball\\n\"\n\"spawnorg 1\\n\"\n\"spawnvel 100\\n\"\n\"veladd -80\\n\"\n\"friction 0.3\\n\"\n\"gravity 400\\n\"\n\"assoc gunshotsmoke\\n\"\n\"}\\n\"\n\n//////////////////////////////////\n//nail impacts\n\n\"r_part te_spike\\n\"\n\"{\\n\"\n\"type sparkfan\\n\"\n\"count 10\\n\"\n\"scale 1\\n\"\n\"scalefactor 1\\n\"\n\"alpha 0.5\\n\"\n\"die 0.2\\n\"\n\"rgb 255 128 0\\n\"\n\"blend add\\n\"\n\"spawnmode ball\\n\"\n\"spawnorg 12\\n\"\n\"spawnvel 300\\n\"\n\"}\\n\"\n\"r_part +te_spike\\n\"\n\"{\\n\"\n\"texture \\\"particles/fteparticlefont.tga\\\"\\n\"\n\"tcoords 1 97 95 191 256\\n\"\n\"count 1\\n\"\n\"scale 1\\n\"\n\"scalefactor 1\\n\"\n\"scaledelta 190\\n\"\n\"die 0.1\\n\"\n\"alpha 0.6\\n\"\n\"rgb 255 128 0\\n\"\n\"blend add\\n\"\n\"assoc gunshotsmoke\\n\"\n\"}\\n\"\n\n\"r_part te_superspike\\n\"\n\"{\\n\"\n\"type sparkfan\\n\"\n\"count 20\\n\"\n\"scale 1\\n\"\n\"scalefactor 1\\n\"\n\"alpha 0.5\\n\"\n\"die 0.2\\n\"\n\"rgb 255 128 0\\n\"\n\"blend add\\n\"\n\"spawnmode ball\\n\"\n\"spawnorg 12\\n\"\n\"spawnvel 300\\n\"\n\"}\\n\"\n\"r_part +te_superspike\\n\"\n\"{\\n\"\n\"texture \\\"particles/fteparticlefont.tga\\\"\\n\"\n\"tcoords 1 97 95 191 256\\n\"\n\"count 1\\n\"\n\"scale 1\\n\"\n\"scalefactor 1\\n\"\n\"scaledelta 190\\n\"\n\"die 0.1\\n\"\n\"alpha 0.6\\n\"\n\"rgb 255 128 0\\n\"\n\"blend add\\n\"\n\"assoc gunshotsmoke\\n\"\n\"}\\n\"\n\n//simple slight trail, to show movement more than anything else.\n\"r_part tr_spike\\n\"\n\"{\\n\"\n\"texture \\\"particles/fteparticlefont.tga\\\"\\n\"\n\"step 1.1\\n\"\n\"scale 4\\n\"\n\"die .4\\n\"\n\"rgb 20 20 20\\n\"\n\"alpha 1\\n\"\n\"blend add\\n\"\n\"spawnmode spiral 512\\n\"\n\"spawnvel 10\\n\"\n\"}\\n\"\n\"r_trail \\\"progs/spike.mdl\\\" tr_spike\\n\"\n\"r_trail \\\"progs/s_spike.mdl\\\" tr_spike\\n\"\n\n////////////////////////////////////////////////\n//explosion\n\n//red bit\n\"r_part te_explosion\\n\"\n\"{\\n\"\n\"texture \\\"particles/fteparticlefont.tga\\\"\\n\"\n\"tcoords 97 97 191 191 256\\n\"\n\"count 1\\n\"\n\"scale 500\\n\"\n\"alpha 0.4\\n\"\n\"die 0.2\\n\"\n\"rgb 255 127 100\\n\"\n\"rgbdelta -14 -300 -300\\n\"\n\"blend add\\n\"\n\"scalefactor 1\\n\"\n\"scaledelta -15\\n\"\n\"randomvel 0\\n\"\n//\tlightradius 350\n//\tlightrgb    1.4 1.2 1.05\n//\tlighttime   0.5\n//\tlightradiusfade 350\n//\tlightrgbfade 2 2 2\t\n\"}\\n\"\n//smoke\n\"r_part +te_explosion\\n\"\n\"{\\n\"\n\"texture \\\"particles/fteparticlefont.tga\\\"\\n\"\n\"tcoords 97 97 191 191 256\\n\"\n\"count 7\\n\"\n\"scale 300\\n\"\n\"alpha 0.2\\n\"\n\"die 0.8\\n\"\n//diesubrand 10.25\n\"randomvel 100\\n\"\n\"rgb 5 5 5\\n\"\n//rgbdelta -230 -45 -9\n\"gravity -15\\n\"\n\"scalefactor 1\\n\"\n\"scaledelta 40\\n\"\n\"spawnvel 5\\n\"\n\"}\\n\"\n// burst sparks\n\"r_part +te_explosion\\n\"\n\"{\\n\"\n\"type texturedspark\\n\"\n\"texture \\\"particles/fteparticlefont.tga\\\"\\n\"\n\"tcoords 1 65 31 95 256 8 32\\n\"\n\"count 100\\n\"\n\"scale 5\\n\"\n\"scalefactor 1\\n\"\n\"scaledelta -15\\n\"\n\"alpha 0.2\\n\"\n\"die 0.5\\n\"\n\"rgb 255 128 0\\n\"\n\"blend add\\n\"\n\"spawnmode ball\\n\"\n\"spawnorg 1\\n\"\n\"randomvel 1000\\n\"\n\"friction 0.01\\n\"\n\"gravity 100\\n\"\n\"stretchfactor -80\\n\"\n\"}\\n\"\n\n//hide lights in explosions.\n//r_explosionlight 0\n\n//hide the explosion sprite in qw\n\"cl_expsprite 0\\n\"\n//hide it in nq - WARNING: some mods use this sprite as a flame thrower.\n//r_effect \"progs/s_explod.spr\" hidden 1\n\n\n//////////////////////////////////////////\n//rogue te_explosion2 effect\n//note: if not otherwise defined, te_explosion2_BASE_RAND maps to this, and specifies the palette colours.\n\"r_part te_explosion2\\n\"\n\"{\\n\"\n\"type texturedspark\\n\"\n\"texture \\\"particles/fteparticlefont.tga\\\"\\n\"\n\"tcoords 1 65 31 95 256 8 32\\n\"\n\"count 256\\n\"\n\"scale 5\\n\"\n\"scalefactor 1\\n\"\n\"scaledelta -15\\n\"\n\"alpha 0.2\\n\"\n\"die 0.5\\n\"\n\"blend add\\n\"\n\"spawnmode ball\\n\"\n\"spawnorg 1\\n\"\n\"randomvel 1000\\n\"\n\"friction 0.01\\n\"\n\"gravity 100\\n\"\n\"stretchfactor -80\\n\"\n\"}\\n\"\n//dragon fireball\n//r_part te_explosion2_228_5\n\n//rogue multigrenade sub explosion\n//also triggered from a shielded rogue player touching another player (and doing some damage)\n//also used during the ending.\n//red particles\n//r_part te_explosion2_230_5\n\n//rogue plasma explosion\n//also rogue timemachine explosion\n//white particles splaying outwards\n//r_part te_explosion2_244_3\n\n//////////////////////////////////////////\n//for when a spawn dies.\n//also used by TF for emp explosions.\n\"r_part te_tarexplosion\\n\"\n\"{\\n\"\n\"type texturedspark\\n\"\n\"texture \\\"particles/fteparticlefont.tga\\\"\\n\"\n\"tcoords 1 65 31 95 256 8 32\\n\"\n\"count 128\\n\"\n\"scale 5\\n\"\n\"scalefactor 1\\n\"\n\"scaledelta -15\\n\"\n\"rgb 0 0 17\\n\"\n\"alpha 0.5\\n\"\n\"die 0.5\\n\"\n\"spawnmode ball\\n\"\n\"spawnorg 1\\n\"\n\"randomvel 500\\n\"\n\"friction 0.01\\n\"\n\"gravity 100\\n\"\n\"stretchfactor -80\\n\"\n\"}\\n\"\n\"r_part +te_tarexplosion\\n\"\n\"{\\n\"\n\"type texturedspark\\n\"\n\"texture \\\"particles/fteparticlefont.tga\\\"\\n\"\n\"tcoords 1 65 31 95 256 8 32\\n\"\n\"count 256\\n\"\n\"scale 5\\n\"\n\"scalefactor 1\\n\"\n\"scaledelta -15\\n\"\n\"rgb 83 67 115\\n\"\n\"alpha 0.3\\n\"\n\"die 0.5\\n\"\n\"blend add\\n\"\n\"spawnmode ball\\n\"\n\"spawnorg 1\\n\"\n\"randomvel 500\\n\"\n\"friction 0.01\\n\"\n\"gravity 100\\n\"\n\"stretchfactor -80\\n\"\n\"}\\n\"\n\n//////////////////////////////////////////\n//cthon falling into lava.\n//often also used for TF gas grenades.\n\"r_part te_lavasplash\\n\"\n\"{\\n\"\n\"texture \\\"particles/fteparticlefont.tga\\\"\\n\"\n\"tcoords 129 1 191 63 256\\n\"\n\"count 654\\n\"\n\"scale 15\\n\"\n\"alpha 0.7\\n\"\n\"die 4\\n\"\n\"randomvel 64\\n\"\n\"rgb 255 128 128\\n\"\n\"gravity 50\\n\"\n\"blend add\\n\"\n\"spawnorg 192 64\\n\"\n\"up 48\\n\"\n\"}\\n\"\n\n//////////////////////////////////////////\n//FIXME: what if we don't have glsl support?\n\"r_part te_teleport\\n\"\n\"{\\n\"\n\"scale 250\\n\"\n\"count 1\\n\"\n\"alpha 0.3\\n\"\n\"die 0.5\\n\"\n\"scalefactor 1\\n\"\n\"rotationstart 45\\n\"\n\"rotationspeed 0\\n\"\n\n\"shader\\n\"\n\"{\\n\"\n\"surfaceparm noshadows\\n\"\n\"surfaceparm nodlight\\n\"\n\"glslprogram\\n\"\n\"{\\n\"\n\"!!samps screen=0\\n\"\n\"varying vec2 tcoord;\\n\"\n\"varying vec4 scoord;\\n\"\n\"varying float alph;\\n\"\n\"#ifdef VERTEX_SHADER\\n\"\n\"attribute vec2 v_texcoord;\\n\"\n\"attribute vec4 v_colour;\\n\"\n\n\"void main(void)\\n\"\n\"{\\n\"\n\"scoord = ftetransform();\\n\"\n\"tcoord = (v_texcoord.st - 0.5)*2.0;\\n\"\n\"alph = v_colour.a;\\n\"\n\"gl_Position = ftetransform();\\n\"\n\"}\\n\"\n\"#endif\\n\"\n\"#ifdef FRAGMENT_SHADER\\n\"\n\"void main(void)\\n\"\n\"{\\n\"\n\"vec2 nst;\\n\"\n\"float f;\\n\"\n\"nst = scoord.xy / scoord.w;\\n\"\n\"nst = (1.0 + nst)/2.0;\\n\"\n\"f = 1.0 - length(tcoord);\\n\"\n//\t\t\t\tf = 1.0 - tcoord*tcoord;\n\"if (f < 0.0) discard;\\n\"\n\"f *= alph;\\n\"\n\"gl_FragColor = texture2D(s_screen, nst - tcoord*f);\\n\"\n\"}\\n\"\n\"#endif\\n\"\n\"}\\n\"\n\"{\\n\"\n\"map $currentrender\\n\"\n\"blendfunc blend\\n\"\n\"}\\n\"\n\"}\\n\"\n\"}\\n\"\n\n\n//////////////////////////////////////////\n//hellknight\n\"r_part tr_knightspike\\n\"\n\"{\\n\"\n\"texture \\\"particles/fteparticlefont.tga\\\"\\n\"\n\"tcoords 1 97 95 191 256\\n\"\n\"scale 15\\n\"\n\"step 1\\n\"\n\"alpha 0.6\\n\"\n\"die 0.2\\n\"\n\"rgb 192 96 48\\n\"\n\"veladd 0\\n\"\n\"randomvel 2\\n\"\n\"friction 4\\n\"\n\"scalefactor 0.825\\n\"\n\"blend add\\n\"\n\"spawnmode spiral\\n\"\n\"spawnvel -50\\n\"\n\"lighttime 0\\n\"\n\"lightshadows 0\\n\"\n\"lightradius 150\\n\"\n\"lightrgb    0.75 0.37 0.18\\n\"\n\"}\\n\"\n\n\"r_part te_knightspike\\n\"\n\"{\\n\"\n\"type sparkfan\\n\"\n\"count 200\\n\"\n\"scale 3\\n\"\n\"scalefactor 1\\n\"\n\"alpha 0.5\\n\"\n\"die 0.5\\n\"\n\"rgb 192 96 48\\n\"\n\"blend add\\n\"\n\"spawnmode ball\\n\"\n\"spawnorg 12\\n\"\n\"spawnvel 100\\n\"\n\"stretchfactor 10\\n\"\n\"}\\n\"\n\n/////////////////////////////////////////\n//vore missiles\n\"r_part tr_vorespike\\n\"\n\"{\\n\"\n\"texture \\\"particles/fteparticlefont.tga\\\"\\n\"\n\"tcoords 1 97 95 191 256\\n\"\n\"scale 15\\n\"\n\"step 1\\n\"\n\"alpha 0.6\\n\"\n\"die 0.5\\n\"\n\"rgb 192 96 192\\n\"\n\"veladd 15\\n\"\n\"spawnmode spiral\\n\"\n\"spawnvel 50\\n\"\n\"randomvel 0\\n\"\n\"friction 0\\n\"\n\"scalefactor 1\\n\"\n\"blend add\\n\"\n\"lighttime 0\\n\"\n\"lightshadows 0\\n\"\n\"lightradius 150\\n\"\n\"lightrgb    0.75 0.37 0.75\\n\"\n\"}\\n\"\n//rygel's pack sucks\n\"r_trail \\\"progs/v_spike.mdl\\\" tr_vorespike\\n\"\n\n\n////////////////////\n//enforcer laser effect\n\"r_part tr_enforcerlaser\\n\"\n\"{\\n\"\n\"type texturedspark\\n\"\n\"texture \\\"particles/fteparticlefont.tga\\\"\\n\"\n\"tcoords 1 97 95 191 256\\n\"\n\"scale 15\\n\"\n\"step 4\\n\"\n\"alpha 0.3\\n\"\n\"die 0.5\\n\"\n\"rgb 255 69 0\\n\"\n\"veladd -32\\n\"\n\"spawnmode spiral\\n\"\n\"spawnvel 16\\n\"\n\"randomvel 32\\n\"\n\"friction 0\\n\"\n\"scalefactor 1\\n\"\n\"blend add\\n\"\n\"lighttime 0.2\\n\"\n\"lightshadows 0\\n\"\n\"lightradius 150\\n\"\n\"lightrgb    1 0.27 0\\n\"\n\"lightrgbfade    5 1 0\\n\"\n\"lightcorona 2 0.25\\n\"\n\"}\\n\"\n\"r_trail \\\"progs/laser.mdl\\\" tr_enforcerlaser\\n\"\n\n/////////////////////////////////////////\n//rogue wrath enemy's projectiles\n\"r_part tr_wrathball\\n\"\n\"{\\n\"\n\"type texturedspark\\n\"\n\"texture \\\"particles/fteparticlefont.tga\\\"\\n\"\n\"tcoords 1 97 95 191 256\\n\"\n\"scale 15\\n\"\n\"step 4\\n\"\n\"alpha 0.3\\n\"\n\"die 0.5\\n\"\n\"rgb 255 0 0\\n\"\n\"veladd -32\\n\"\n\"spawnmode spiral\\n\"\n\"spawnvel 16\\n\"\n\"randomvel 32\\n\"\n\"friction 0\\n\"\n\"scalefactor 1\\n\"\n\"blend add\\n\"\n\"lighttime 0.2\\n\"\n\"lightshadows 0\\n\"\n\"lightradius 150\\n\"\n\"lightrgb    1 0.27 0\\n\"\n\"lightrgbfade    5 1 0\\n\"\n\"lightcorona 2 0.5\\n\"\n\"}\\n\"\n\"r_trail \\\"progs/w_ball.mdl\\\" tr_wrathball\\n\"\n\n//wrath death\n//grey particles\n//no difference from the fallback except for the blend mode. this should ensure that we are not quite so invisible.\n\"r_part te_explosion2_0_4\\n\"\n\"{\\n\"\n\"type texturedspark\\n\"\n\"texture \\\"particles/fteparticlefont.tga\\\"\\n\"\n\"tcoords 1 65 31 95 256 8 32\\n\"\n\"count 256\\n\"\n\"scale 5\\n\"\n\"scalefactor 1\\n\"\n\"scaledelta -15\\n\"\n\"alpha 0.2\\n\"\n\"die 0.5\\n\"\n\"spawnmode ball\\n\"\n\"spawnorg 1\\n\"\n\"randomvel 1000\\n\"\n\"friction 0.01\\n\"\n\"gravity 100\\n\"\n\"stretchfactor -80\\n\"\n\"}\\n\"\n\n/////////////////////////////////////////\n//rogue lavaspikes\n\"r_part tr_lavaspike\\n\"\n\"{\\n\"\n\"type spark\\n\"\n\"texture \\\"particles/fteparticlefont.tga\\\"\\n\"\n\"tcoords 1 97 95 191 256\\n\"\n\"scale 15\\n\"\n\"step 4\\n\"\n\"alpha 0.3\\n\"\n\"die 0.5\\n\"\n\"rgb 255 0 0\\n\"\n\"veladd -32\\n\"\n\"spawnmode spiral\\n\"\n\"spawnvel 16\\n\"\n\"randomvel 32\\n\"\n\"friction 0\\n\"\n\"scalefactor 1\\n\"\n\"blend add\\n\"\n\"}\\n\"\n\"r_trail \\\"progs/lspike.mdl\\\" tr_lavaspike\\n\"\n\n/////////////////////////////////////////\n//rogue plasma gun\n\"r_part tr_plasma\\n\"\n\"{\\n\"\n\"type texturedspark\\n\"\n\"texture \\\"particles/fteparticlefont.tga\\\"\\n\"\n\"tcoords 1 97 95 191 256\\n\"\n\"scale 15\\n\"\n\"step 4\\n\"\n\"alpha 0.3\\n\"\n\"die 0.25\\n\"\n\"rgb 128 128 255\\n\"\n\"veladd -32\\n\"\n\"spawnmode spiral\\n\"\n\"spawnvel 16\\n\"\n\"randomvel 32\\n\"\n\"friction 0\\n\"\n\"scalefactor 1\\n\"\n\"blend add\\n\"\n\"lighttime 0.2\\n\"\n\"lightshadows 0\\n\"\n\"lightradius 150\\n\"\n\"lightrgb    1 1 2\\n\"\n\"lightrgbfade    5 1 0.5\\n\"\n\"lightcorona 2 0.5\\n\"\n\"}\\n\"\n\"r_trail \\\"progs/plasma.mdl\\\" tr_plasma\\n\"\n\n\n/////////////////////////////////////////\n//scrag missiles.\n\"r_part tr_wizspike\\n\"\n\"{\\n\"\n\"texture \\\"particles/fteparticlefont.tga\\\"\\n\"\n\"tcoords 1 97 95 191 256\\n\"\n\"scale 15\\n\"\n\"step 4\\n\"\n\"alpha 0.6\\n\"\n\"die 0.2\\n\"\n\"rgb 25 200 25\\n\"\n\"veladd 0\\n\"\n\"randomvel 2\\n\"\n\"friction 4\\n\"\n\"scalefactor 0.825\\n\"\n\"spawnmode spiral\\n\"\n\"spawnvel 25\\n\"\n\"blend add\\n\"\n\"lighttime 2\\n\"\n\"lightradiusfade 75\\n\"\n\"lightshadows 0\\n\"\n\"lightradius 150\\n\"\n\"lightrgb    0.1 0.7 0.1\\n\"\n\"}\\n\"\n\n\"r_part tr_wizspike2\\n\"\n\"{\\n\"\n\"texture \\\"particles/fteparticlefont.tga\\\"\\n\"\n\"tcoords 1 97 95 191 256\\n\"\n\"scale 4\\n\"\n\"step 1\\n\"\n\"alpha 0.6\\n\"\n\"die 0.2\\n\"\n\"rgb 25 200 25\\n\"\n\"veladd 64\\n\"\n\"randomvel 64\\n\"\n\"friction 4\\n\"\n\"scalefactor 0.825\\n\"\n\"spawnmode spiral\\n\"\n\"spawnvel 25\\n\"\n\"blend add\\n\"\n\"}\\n\"\n//scrag impact\n\"r_part te_wizspike\\n\"\n\"{\\n\"\n\"texture \\\"particles/fteparticlefont.tga\\\"\\n\"\n\"tcoords 1 97 95 191 256\\n\"\n\"scale 15\\n\"\n\"alpha 0.6\\n\"\n\"rgb 25 200 25\\n\"\n\"friction 0\\n\"\n\"scalefactor 0.825\\n\"\n\"blend add\\n\"\n\"count 5\\n\"\n\"veladd -256\\n\"\n\"randomvel 256\\n\"\n\"die 1\\n\"\n\"diesubrand 0.5\\n\"\n\"gravity 800\\n\"\n\"emit tr_wizspike2\\n\"\n\"emitinterval -1\\n\"\n\"bounce 1.5\\n\"\n\"}\\n\"\n\n/////////////////////////////////////////\n//shambler stuff\n\"r_part shambercharging\\n\"\n\"{\\n\"\n\"spawnmode ball\\n\"\n\"count 200\\n\"\n\"spawnorg 128\\n\"\n\"spawnvel -256\\n\"\n\"texture \\\"particles/fteparticlefont.tga\\\"\\n\"\n\"tcoords 1 1 63 63 256 2 64\\n\"\n\"scale 4\\n\"\n\"alpha 1\\n\"\n\"die 0.5\\n\"\n\"orgadd -64\\n\"\n\"rotationspeed 90\\n\"\n\"rotationstart 0 360\\n\"\n\"rgb 100 100 250\\n\"\n\"rgbrand 0 0 0\\n\"\n\"gravity 0\\n\"\n\"scalefactor 0.4\\n\"\n\"lighttime 0\\n\"\n\"lightshadows 0\\n\"\n\"lightradius 400\\n\"\n\"lightrgb    2 2 2\\n\"\n\"}\\n\"\n\"r_effect progs/s_light.mdl shambercharging 0\\n\"\n\n/////////////////////////////////////////\n//blood effects\n\"r_part te_blood\\n\"\n\"{\\n\"\n\"texture fte_bloodparticle\\n\"\n\"blend subtract\\n\"\n\"count 1\\n\"\n\"scale 32\\n\"\n\"alpha 0\\n\"\n\"die 1\\n\"\n\"randomvel 64\\n\"\n\"veladd 10\\n\"\n\"rotationspeed 90\\n\"\n\"rotationstart 0 360\\n\"\n\"rgb 32 64 64\\n\"\n\"rgbdelta -32 -64 -64\\n\"\n\"gravity 200\\n\"\n\"scalefactor 0.8\\n\"\n\"}\\n\"\n\n\"r_part high.pe_73\\n\"\n\"{\\n\"\n\"texture fte_bloodparticle\\n\"\n\"blend subtract\\n\"\n\"count 1\\n\"\n\"scale 32\\n\"\n\"alpha 0\\n\"\n\"die 1\\n\"\n\"randomvel 64\\n\"\n\"veladd 10\\n\"\n\"rotationspeed 90\\n\"\n\"rotationstart 0 360\\n\"\n\"rgb 32 64 64\\n\"\n\"rgbdelta -32 -64 -64\\n\"\n\"gravity 200\\n\"\n\"scalefactor 0.8\\n\"\n\"}\\n\"\n\n\"r_part te_lightningblood\\n\"\n\"{\\n\"\n\"texture fte_bloodparticle\\n\"\n\"blend subtract\\n\"\n\"count 1\\n\"\n\"scale 32\\n\"\n\"alpha 0\\n\"\n\"die 1\\n\"\n\"randomvel 32\\n\"\n\"veladd 5\\n\"\n\"rotationspeed 90\\n\"\n\"rotationstart 0 360\\n\"\n\"rgb 64 128 128\\n\"\n\"rgbdelta -64 -128 -128\\n\"\n\"gravity 200\\n\"\n\"scalefactor 0.8\\n\"\n\"}\\n\"\n\"r_part high.pe_225\\n\"\n\"{\\n\"\n\"texture fte_bloodparticle\\n\"\n\"blend subtract\\n\"\n\"count 0.5\\n\"\n\"scale 32\\n\"\n\"alpha 0\\n\"\n\"die 1\\n\"\n\"randomvel 32\\n\"\n\"veladd 5\\n\"\n\"rotationspeed 90\\n\"\n\"rotationstart 0 360\\n\"\n\"rgb 64 128 128\\n\"\n\"rgbdelta -64 -128 -128\\n\"\n\"gravity 200\\n\"\n\"scalefactor 0.8\\n\"\n\"}\\n\"\n\n/////////////////////////////////////////\n//zombie body-part blood trails\n\"r_part tr_slightblood\\n\"\n\"{\\n\"\n\"texture fte_bloodparticle\\n\"\n\"blend subtract\\n\"\n//\ttcoords 1 1 63 63 256 2 64\n\"step 16\\n\"\n\"scale 64\\n\"\n\"alpha 0\\n\"\n\"die 1\\n\"\n\"randomvel 32\\n\"\n\"veladd 10\\n\"\n\"rotationspeed 90\\n\"\n\"rotationstart 0 360\\n\"\n\"rgb 64 128 128 \\n\"\n\"rgbdelta -64 -128 -128\\n\"\n\"gravity 200\\n\"\n\"scalefactor 0.8\\n\"\n\"scaledelta -10\\n\"\n\"stains -0.5\\n\"\n\"}\\n\"\n\n//////////////////////////////////////////\n//regular ol' blood trails\n\"r_part tr_blood\\n\"\n\"{\\n\"\n\"texture fte_bloodparticle\\n\"\n\"blend subtract\\n\"\n\"step 8\\n\"\n\"scale 64\\n\"\n\"alpha 0\\n\"\n\"die 1\\n\"\n\"randomvel 32\\n\"\n\"veladd 10\\n\"\n\"rotationspeed 90\\n\"\n\"rotationstart 0 360\\n\"\n\"rgb 32 128 128 \\n\"\n\"rgbdelta -32 -128 -128\\n\"\n\"gravity 200\\n\"\n\"scalefactor 0.8\\n\"\n\"scaledelta -10\\n\"\n\"stains -0.5\\n\"\n\"}\\n\"\n\n//////////////////////////////////\n//fallbacks\n\n\"r_part pe_default\\n\"\n\"{\\n\"\n\"texture \\\"particles/fteparticlefont.tga\\\"\\n\"\n\"tcoords 1 97 95 191 256\\n\"\n\"count 1\\n\"\n\"scale 4\\n\"\n\"veladd 15\\n\"\n\"die 0.4\\n\"\n\"alphadelta 0\\n\"\n\"diesubrand 0.4\\n\"\n\"gravity 40\\n\"\n\"spawnorg 8\\n\"\n\"}\\n\"\n\"r_part pe_defaulttrail\\n\"\n\"{\\n\"\n\"texture \\\"particles/fteparticlefont.tga\\\"\\n\"\n\"tcoords 1 97 95 191 256\\n\"\n\"scale 15\\n\"\n\"step 1\\n\"\n\"alpha 0.6\\n\"\n\"die 0.2\\n\"\n\"rgb 192 96 48\\n\"\n\"veladd 0\\n\"\n\"randomvel 2\\n\"\n\"friction 4\\n\"\n\"scalefactor 0.825\\n\"\n\"spawnmode spiral\\n\"\n\"spawnvel 25\\n\"\n\"blend add\\n\"\n\"}\\n\"\n\n//////////////////////////////////\n//map debugging\n\"r_part pe_pointfile\\n\"\n\"{\\n\"\n\"texture \\\"particles/fteparticlefont.tga\\\"\\n\"\n\"tcoords 1 97 95 191 256\\n\"\n\"count 1\\n\"\n\"scale 50\\n\"\n\"die 30\\n\"\n\"alphadelta 0\\n\"\n\"rgb 255 255 0\\n\"\n\"}\\n\"\n\n\n\n\n\n\n\n\n\n\n\n;\n\n\n\n//////////////////////////////////////////////////////\n\n\nchar *particle_set_minimal =\n// minimal, by TimeServ\n\"r_part pe_size3\\n\"\n\"{\\n\"\n\"texture \\\"particles/quake\\\"\\n\"\n\"count 1\\n\"\n\"die 1\\n\"\n\"scale 20\\n\"\n\"scaledelta -20\\n\"\n\"veladd 25\\n\"\n\"spawnorg 38\\n\"\n\"spawnvel 38\\n\"\n\"scalefactor 0.8\\n\"\n\"}\\n\"\n\n\"r_part pe_size2\\n\"\n\"{\\n\"\n\"texture \\\"particles/quake\\\"\\n\"\n\"count 1\\n\"\n\"die 1\\n\"\n\"scale 12\\n\"\n\"scaledelta -12\\n\"\n\"veladd 20\\n\"\n\"spawnorg 16\\n\"\n\"spawnvel 16\\n\"\n\"scalefactor 0.8\\n\"\n\"}\\n\"\n\n\"r_part pe_default\\n\"\n\"{\\n\"\n\"texture \\\"particles/quake\\\"\\n\"\n\"count 1\\n\"\n\"die 1\\n\"\n\"scale 10\\n\"\n\"scaledelta -10\\n\"\n\"veladd 15\\n\"\n\"spawnorg 10\\n\"\n\"spawnvel 10\\n\"\n\"scalefactor 0.8\\n\"\n\"}\\n\"\n\n\"r_part pe_defaulttrail\\n\"\n\"{\\n\"\n\"texture \\\"particles/quake\\\"\\n\"\n\"step 10\\n\"\n\"die 1\\n\"\n\"scale 8\\n\"\n\"scaledelta -8\\n\"\n\"veladd 15\\n\"\n\"spawnorg 2\\n\"\n\"spawnvel 2\\n\"\n\"scalefactor 0.8\\n\"\n\"}\\n\"\n;\n\n\n\n//////////////////////////////////////////////////////\n\n\n#ifdef HEXEN2\nchar *particle_set_h2part =\n//hexen2-compatible particle config\n//for the purposes of faithfulness, I'm using uhexen2 (with gl_missile_glows etc set to 0) as a baseline.\n\n\n//the engine uses the h2part namespace for all hexen2 effects, thus ensuring that the builtin config is loaded.\n//specifying this explicitly means that the engine can find these effects properly even if this config is loaded via some name other than h2part.\n//this line doesn't affect weak/strong stuff, so r_particledesc will still override builtin ones.\n\"r_part namespace h2part\\n\"\n\n//transparent sprites look stupid when alpha tested too. really this shouldn't be here, but its needed to override fps_preset stuff...\n\"gl_blendsprites 1\\n\"\n\n//pe4 effect 255 is reused for the generic\n//move the vel to org and ignore the spawn velocity to mimic hexen2's particleexplosion\n//colour gets overriden\n\"r_part pe4_255\\n\"\n\"{\\n\"\n\"texture \\\"particles/fteparticlefont.tga\\\"\\n\"\n\"tcoords 1 1 63 63 256 2 64\\n\"\n\"count 1\\n\"\n\"scale 4\\n\"\n\"alpha 0.6\\n\"\n\"die 0.5\\n\"\n\"randomvel 256\\n\"\n\"veladd 0\\n\"\n\"orgadd 1\\n\"\n\"rotationspeed 360\\n\"\n\"rotationstart 0 360\\n\"\n\"gravity 200\\n\"\n\"scalefactor 0.8\\n\"\n\"}\\n\"\n\n//icemace hitting a monster\n\"r_part pe2_14_145\\n\"\n\"{\\n\"\n\"texture \\\"particles/fteparticlefont.tga\\\"\\n\"\n\"tcoords 1 1 63 63 256 2 64\\n\"\n\"count 20\\n\"\n\"scale 4\\n\"\n\"alpha 1\\n\"\n\"die 1\\n\"\n\"randomvel 256\\n\"\n\"rgb 160 160 240\\n\"\n\"veladd 0\\n\"\n\"orgadd 1\\n\"\n\"rotationspeed 360\\n\"\n\"rotationstart 0 360\\n\"\n\"gravity 200\\n\"\n\"scalefactor 0.8\\n\"\n\"}\\n\"\n\n//praevus flame summoning particles\n\"r_part pe2_7 //_427\\n\"\n\"{\\n\"\n\"texture \\\"particles/fteparticlefont.tga\\\"\\n\"\n\"tcoords 1 1 63 63 256 2 64\\n\"\n\"count 1\\n\"\n\"scale 4\\n\"\n\"alpha 3\\n\"\n\"die 2\\n\"\n\"randomvel 0\\n\"\n\"veladd 1\\n\"\n\"spawnorg 8\\n\"\n\"spawnvel 0\\n\"\n\"rotationspeed 360\\n\"\n\"rotationstart 0 360\\n\"\n\"gravity 0\\n\"\n\"scalefactor 0.8\\n\"\n\"}\\n\"\n\n//grav. identical to slowgrav. used for the necro's boneshard particle puffs\n\"r_part pe4_1\\n\"\n\"{\\n\"\n\"texture \\\"particles/fteparticlefont.tga\\\"\\n\"\n\"tcoords 1 1 63 63 256 2 64\\n\"\n\"count 1\\n\"\n\"scale 6\\n\"\n\"alpha 1\\n\"\n\"die 1\\n\"\n\"randomvel 0\\n\"\n\"veladd 1\\n\"\n\"orgadd 0\\n\"\n\"spawnorg 8\\n\"\n\"rotationspeed 360\\n\"\n\"rotationstart 0 360\\n\"\n\"gravity 200\\n\"\n\"scalefactor 0.3\\n\"\n\"}\\n\"\n//slowgrav, used for the assassin's grenade's trail, stupidly enough\n\"r_part pe4_3\\n\"\n\"{\\n\"\n\"texture \\\"particles/fteparticlefont.tga\\\"\\n\"\n\"tcoords 1 1 63 63 256 2 64\\n\"\n\"count 1\\n\"\n\"scale 4\\n\"\n\"alpha 0.6\\n\"\n\"die 0.5\\n\"\n\"randomvel 0\\n\"\n\"veladd 1\\n\"\n\"orgadd 0\\n\"\n\"spawnorg 8\\n\"\n\"rotationspeed 360\\n\"\n\"rotationstart 0 360\\n\"\n\"gravity 200\\n\"\n\"scalefactor 0.8\\n\"\n\"}\\n\"\n\n//pt_fastgrav. blood splatters (like in the assassin's tomed set staff when the monster is chained up).\n\"r_part pe4_2\\n\"\n\"{\\n\"\n\"texture \\\"particles/fteparticlefont.tga\\\"\\n\"\n\"tcoords 1 1 63 63 256 2 64\\n\"\n\"count 1\\n\"\n\"scale 8\\n\"\n\"alpha 2\\n\"\n\"die 1\\n\"\n\"randomvel 0\\n\"\n\"veladd 2\\n\"\n\"orgadd 0\\n\"\n\"spawnorg 8\\n\"\n\"rotationspeed 360\\n\"\n\"rotationstart 0 360\\n\"\n\"gravity 800\\n\"\n\"scalefactor 0.8\\n\"\n\"}\\n\"\n\n//the 'rocket trail' flag from quake was repurposed in hexen2 for spider gibs\n\"r_part tr_rocket\\n\"\n\"{\\n\"\n\"texture \\\"particles/fteparticlefont.tga\\\"\\n\"\n\"tcoords 1 1 63 63 256 2 64\\n\"\n\"step 2\\n\"\n\"scale 4\\n\"\n\"alpha 0.6\\n\"\n\"die 1\\n\"\n\"randomvel 64\\n\"\n\"veladd 10\\n\"\n\"rotationspeed 90\\n\"\n\"rotationstart 0 360\\n\"\n\"rgb 16 160 16\\n\"\n\"rgbrand 16 64 16\\n\"\n\"gravity 200\\n\"\n\"scalefactor 0.8\\n\"\n\"}\\n\"\n\n//used for the meteor staff trail (projectile and gibs)\n\"r_part tr_grenade\\n\"\n\"{\\n\"\n\"texture \\\"particles/fteparticlefont.tga\\\"\\n\"\n\"tcoords 1 1 63 63 256 2 64\\n\"\n\"step 4\\n\"\n\"scale 4\\n\"\n\"alpha 1\\n\"\n\"die 1\\n\"\n\"randomvel 8\\n\"\n\"veladd 10\\n\"\n\"gravity -40\\n\"\n\"rotationspeed 360\\n\"\n\"rotationstart 0 360\\n\"\n\"rgb 16\\n\"\n\"rgbrand 48\\n\"\n\"rgbrandsync 1\\n\"\n\"scalefactor 0.8\\n\"\n\"}\\n\"\n\n//used on ice chunks (paladin ice wand thing)\n\"r_part tr_ice\\n\"\n\"{\\n\"\n\"texture \\\"particles/fteparticlefont.tga\\\"\\n\"\n\"tcoords 1 1 63 63 256 2 64\\n\"\n\"step 4\\n\"\n\"scale 4\\n\"\n\"alpha 0.6\\n\"\n\"die 1\\n\"\n\"randomvel 64\\n\"\n\"veladd 10\\n\"\n\"rotationspeed 360\\n\"\n\"rotationstart 0 360\\n\"\n\"rgb 160 160 240\\n\"\n\"rgbrand 0 0 0\\n\"\n\"gravity 200\\n\"\n\"scalefactor 0.8\\n\"\n\"}\\n\"\n\n//hexen2 uses the exact same effect for blood and slightblood, just slightblood is half as dense.\n\"r_part tr_slightblood\\n\"\n\"{\\n\"\n\"texture \\\"particles/fteparticlefont.tga\\\"\\n\"\n\"tcoords 1 1 63 63 256 2 64\\n\"\n\"step 6\\n\"\n\"scale 4\\n\"\n\"alpha 0.6\\n\"\n\"die 1\\n\"\n\"randomvel 64\\n\"\n\"veladd 10\\n\"\n\"rotationspeed 360\\n\"\n\"rotationstart 0 360\\n\"\n\"rgb 240 0 0\\n\"\n\"rgbrand 0 0 0\\n\"\n\"gravity 200\\n\"\n\"scalefactor 0.8\\n\"\n\"}\\n\"\n\"r_part tr_blood\\n\"\n\"{\\n\"\n\"texture \\\"particles/fteparticlefont.tga\\\"\\n\"\n\"tcoords 1 1 63 63 256 2 64\\n\"\n\"step 3\\n\"\n\"scale 4\\n\"\n\"alpha 0.6\\n\"\n\"die 1\\n\"\n\"randomvel 64\\n\"\n\"veladd 10\\n\"\n\"rotationspeed 360\\n\"\n\"rotationstart 0 360\\n\"\n\"rgb 240 0 0\\n\"\n\"rgbrand 0 0 0\\n\"\n\"gravity 200\\n\"\n\"scalefactor 0.8\\n\"\n\"}\\n\"\n\n\"r_part tr_bloodshot\\n\"\n\"{\\n\"\n\"texture \\\"particles/fteparticlefont.tga\\\"\\n\"\n\"tcoords 1 1 63 63 256 2 64\\n\"\n\"step 3\\n\"\n\"scale 4\\n\"\n\"alpha 0.6\\n\"\n\"die 1\\n\"\n\"randomvel 64\\n\"\n\"veladd 10\\n\"\n\"rotationspeed 90\\n\"\n\"rotationstart 0 360\\n\"\n\"rgb 200 0 0\\n\"\n\"rgbdelta -180 0 0\\n\"\n\"rgbrand 50 0 0\\n\"\n\"gravity 200\\n\"\n\"scalefactor 0.8\\n\"\n\"}\\n\"\n\n//demoness acid projectile trails\n\"r_part tr_spiderblood\\n\"\n\"{\\n\"\n\"texture \\\"particles/fteparticlefont.tga\\\"\\n\"\n\"tcoords 1 1 63 63 256 2 64\\n\"\n\"step 4\\n\"\n\"scale 4\\n\"\n\"alpha 0.6\\n\"\n\"die 1\\n\"\n\"randomvel 64\\n\"\n\"veladd 10\\n\"\n\"rotationspeed 90\\n\"\n\"rotationstart 0 360\\n\"\n\"rgb 16 160 16\\n\"\n\"rgbrand 0 0 0\\n\"\n\"gravity 200\\n\"\n\"scalefactor 0.4\\n\"\n\"}\\n\"\n\n//demoness acid projectile trails\n\"r_part tr_acidball\\n\"\n\"{\\n\"\n\"texture \\\"particles/fteparticlefont.tga\\\"\\n\"\n\"tcoords 1 1 63 63 256 2 64\\n\"\n\"step 4\\n\"\n\"scale 4\\n\"\n\"alpha 0.6\\n\"\n\"die 1\\n\"\n\"randomvel 64\\n\"\n\"veladd 10\\n\"\n\"rotationspeed 90\\n\"\n\"rotationstart 0 360\\n\"\n\"rgb 16 160 16\\n\"\n\"rgbrand 0 0 0\\n\"\n\"gravity 200\\n\"\n\"scalefactor 0.4\\n\"\n\"lighttime 0\\n\"\n\"lightshadows 1\\n\"\n\"lightradius 100 120\\n\"\n\"lightrgb    0.50 1.00 0.25\\n\"\n\"}\\n\"\n\n//hydra spit. generally blackish\n\"r_part tr_spit \\n\"\n\"{\\n\"\n\"texture \\\"particles/fteparticlefont.tga\\\"\\n\"\n\"tcoords 1 1 63 63 256 2 64\\n\"\n\"step 3\\n\"\n\"scale 4\\n\"\n\"alpha 1\\n\"\n\"die 1\\n\"\n\"randomvel 5\\n\"\n\"veladd 10\\n\"\n\"up 2\\n\"\n\"rotationspeed 90\\n\"\n\"rotationstart 0 360\\n\"\n\"rgb 0 0 0\\n\"\n\"rgbrand 0 0 0\\n\"\n\"gravity 0\\n\"\n\"scalefactor 0.3\\n\"\n\n\"lighttime 0\\n\"\n\"lightshadows 1\\n\"\n\"lightradius 100 120\\n\"\n\"lightrgb    -2.00 -1.00 -0.25\\n\"\n\"}\\n\"\n//famine missiles\n\"r_part tr_spell\\n\"\n\"{\\n\"\n\"texture \\\"particles/fteparticlefont.tga\\\"\\n\"\n\"tcoords 1 1 63 63 256 2 64\\n\"\n\"step 4\\n\"\n\"scale 4\\n\"\n\"alpha 1\\n\"\n\"die 1\\n\"\n\"randomvel 16\\n\"\n\"spawnorg 4\\n\"\n\"spawnvel 2\\n\"\n\"veladd 64\\n\"\n\"rotationspeed 90\\n\"\n\"rotationstart 0 360\\n\"\n\"rgb 200 32 32\\n\"\n\"rgbrand 0 0 0\\n\"\n\"gravity 0\\n\"\n\"scalefactor 0.3\\n\"\n\"}\\n\"\n//tomed barbarian weapon2 trail\n\"r_part tr_vorpmissile\\n\"\n\"{\\n\"\n\"texture \\\"particles/fteparticlefont.tga\\\"\\n\"\n\"tcoords 1 1 63 63 256 2 64\\n\"\n\"step 4\\n\"\n\"scale 4\\n\"\n\"alpha 0.6\\n\"\n\"die 0.5\\n\"\n\"randomvel 4\\n\"\n\"spawnorg 32 4\\n\"\n\"veladd 64\\n\"\n\"rotationspeed 90\\n\"\n\"rotationstart 0 360\\n\"\n\"rgb 128 128 128\\n\"\n\"rgbrand 0 0 0\\n\"\n\"gravity 0\\n\"\n\"scalefactor 0.8\\n\"\n\"}\\n\"\n//this fades out much faster than regular hexen2. also slightly flies forwards with the missile\n\"r_part tr_magicmissile\\n\"\n\"{\\n\"\n\"texture \\\"particles/fteparticlefont.tga\\\"\\n\"\n\"tcoords 1 1 63 63 256 2 64\\n\"\n\"step 4\\n\"\n\"scale 4\\n\"\n\"alpha 1\\n\"\n\"die 0.5\\n\"\n\"randomvel 64\\n\"\n\"veladd -128\\n\"\n\"spawnorg 8\\n\"\n\"rotationspeed 90\\n\"\n\"rotationstart 0 360\\n\"\n\"rgb 100 100 160\\n\"\n\"rgbrand 0 0 0\\n\"\n\"gravity 200\\n\"\n\"scalefactor 0.8\\n\"\n\"}\\n\"\n\"r_part tr_boneshard\\n\"\n\"{\\n\"\n\"texture \\\"particles/fteparticlefont.tga\\\"\\n\"\n\"tcoords 1 1 63 63 256 2 64\\n\"\n\"step 4\\n\"\n\"scale 4\\n\"\n\"alpha 1\\n\"\n\"die 0.5\\n\"\n\"randomvel 64\\n\"\n\"veladd -128\\n\"\n\"spawnorg 8\\n\"\n\"rotationspeed 90\\n\"\n\"rotationstart 0 360\\n\"\n\"rgb 200 180 85\\n\"\n\"rgbrand 0 0 0\\n\"\n\"gravity 200\\n\"\n\"scalefactor 0.8\\n\"\n\"}\\n\"\n\n//imp fireballs\n\"r_part tr_fireball\\n\"\n\"{\\n\"\n\"texture \\\"particles/fteparticlefont.tga\\\"\\n\"\n\"tcoords 97 97 191 191 256\\n\"\n\"step 1\\n\"\n\"scale 12\\n\"\n\"alpha 0.4\\n\"\n\"die 0.5\\n\"\n\"rgb 255 127 100\\n\"\n\"rgbdelta -14 -300 -300\\n\"\n\"blend add\\n\"\n\"scalefactor 1\\n\"\n\"scaledelta -15\\n\"\n\"lighttime 0\\n\"\n\"lightshadows 1\\n\"\n\"lightradius 100 120\\n\"\n\"lightrgb    2.00 1.00 0.25\\n\"\n\"}\\n\"\n\"r_part +tr_fireball\\n\"\n\"{\\n\"\n\"texture \\\"particles/fteparticlefont.tga\\\"\\n\"\n\"tcoords 97 97 191 191 256\\n\"\n\"step 5\\n\"\n\"scale 30\\n\"\n\"alpha 0.2\\n\"\n\"die 0.75\\n\"\n//diesubrand 10.25\n\"randomvel 0.2\\n\"\n\"rgb 5 5 5\\n\"\n//rgbdelta -230 -45 -9\n\"gravity -15\\n\"\n\"scalefactor 1\\n\"\n\"scaledelta 20\\n\"\n\"spawnvel 5\\n\"\n\"}\\n\"\n\n//assassin weapon4\n\"r_part tr_setstaff\\n\"\n\"{\\n\"\n\"texture \\\"particles/fteparticlefont.tga\\\"\\n\"\n\"tcoords 1 1 63 63 256 2 64\\n\"\n\"step 2\\n\"\n\"scale 4\\n\"\n\"alpha 0.6\\n\"\n\"die 1\\n\"\n\"spawnorg 3 5\\n\"\n\"randomvel 3.5\\n\"\n\"veladd 10\\n\"\n\"rotationspeed 90\\n\"\n\"rotationstart 0 360\\n\"\n\"rgb 220 200 100 \\n\"\n\"rgbrand 0 0 0\\n\"\n\"gravity 40\\n\"\n\"scalefactor 0.8\\n\"\n\"}\\n\"\n//assassin weapon4's tomed projectile trail thing. barely visible in hexen2. framerate dependant. nasty. this effect is not faithful.\n\"r_part tr_scarab\\n\"\n\"{\\n\"\n\"texture \\\"particles/fteparticlefont.tga\\\"\\n\"\n\"tcoords 1 1 63 63 256 2 64\\n\"\n\"step 2\\n\"\n\"scale 4\\n\"\n\"alpha 0.3\\n\"\n\"die 0.2\\n\"\n\"spawnorg 1 2\\n\"\n\"randomvel 1\\n\"\n\"veladd 10\\n\"\n\"rotationspeed 90\\n\"\n\"rotationstart 0 360\\n\"\n\"rgb 220 200 100 \\n\"\n\"rgbrand 0 0 0\\n\"\n\"gravity 40\\n\"\n\"scalefactor 0.8\\n\"\n\"}\\n\"\n\n\n//generic rain. rgb comes from the gamecode's palette index. blurgh. real men specify things precisely.\n\"r_part te_rain\\n\"\n\"{\\n\"\n\"texture \\\"particles/fteparticlefont.tga\\\"\\n\"\n\"tcoords 1 1 63 63 256 2 64\\n\"\n\"count 1 2 1\\n\"\n\"scale 5\\n\"\n\"alpha 3\\n\"\n\"die 2\\n\"\n\"spawnorg 64 64\\n\"\n\"spawnvel 1\\n\"\n\"veladd 0.5\\n\"\n\"rotationspeed 90\\n\"\n\"rotationstart 0 30\\n\"\n\"rgb 255 255 255\\n\"\n\"scalefactor 0.8\\n\"\n\"}\\n\"\n\n\n//Hexen2 triggers various client-side sprite/model effects.\n//model term:\n//model MODELNAME framestart frameend framerate alpha traileffect\n//sprites will always use a fixed alpha (frames should shrink in size or whatever).\n//models will fade out gradually, but can be forced to a constant alpha if a negative alpha is used (will be fabsed as needed) if you have a decent animation.\n\n\"r_part ce_white_smoke_05\\n\"\n\"{\\n\"\n\"model models/whtsmk1.spr 0 0 20 0.5\\n\"\n\"veladd 1\\n\"\n\"}\\n\"\n\"r_part ce_white_smoke_10\\n\"\n\"{\\n\"\n\"model models/whtsmk1.spr 0 0 10 0.5\\n\"\n\"veladd 1\\n\"\n\"}\\n\"\n\"r_part ce_white_smoke_15\\n\"\n\"{\\n\"\n\"model models/whtsmk1.spr 0 0 6.666 0.5\\n\"\n\"veladd 1\\n\"\n\"}\\n\"\n\"r_part ce_white_smoke_20\\n\"\n\"{\\n\"\n\"model models/whtsmk1.spr 0 0 5 0.5\\n\"\n\"veladd 1\\n\"\n\"}\\n\"\n\"r_part ce_white_smoke_50\\n\"\n\"{\\n\"\n\"model models/whtsmk1.spr 0 0 2 0.5\\n\"\n\"veladd 1\\n\"\n\"}\\n\"\n\n\"r_part ce_bluespark\\n\"\n\"{\\n\"\n\"model models/bspark.spr 0 0 20 1\\n\"\n\"}\\n\"\n\"r_part ce_yellowspark\\n\"\n\"{\\n\"\n\"model models/spark.spr 0 0 20 1\\n\"\n\"}\\n\"\n\"r_part ce_sm_circle_exp\\n\"\n\"{\\n\"\n\"model models/fcircle.spr 0 0 20 1\\n\"\n\"}\\n\"\n\"r_part ce_bg_circle_exp\\n\"\n\"{\\n\"\n\"model models/xplod29.spr 0 0 20 1\\n\"\n\"}\\n\"\n\"r_part ce_sm_white_flash\\n\"\n\"{\\n\"\n\"model models/sm_white.spr 0 0 20 1\\n\"\n\"}\\n\"\n\"r_part ce_white_flash\\n\"\n\"{\\n\"\n\"model models/gryspt.spr 0 0 20 0.4\\n\"\n\"}\\n\"\n\"r_part ce_yellowred_flash\\n\"\n\"{\\n\"\n\"model models/yr_flsh.spr 0 0 20 0.4\\n\"\n\"}\\n\"\n\"r_part ce_blue_flash\\n\"\n\"{\\n\"\n\"model models/bluflash.spr 0 0 20 0.4\\n\"\n\"}\\n\"\n\"r_part ce_sm_blue_flash\\n\"\n\"{\\n\"\n\"model models/sm_blue.spr 0 0 20 0.4\\n\"\n\"}\\n\"\n\"r_part ce_red_flash\\n\"\n\"{\\n\"\n\"model models/redspt.spr 0 0 20 0.4\\n\"\n\"}\\n\"\n\"r_part ce_sm_explosion\\n\"\n\"{\\n\"\n\"model models/sm_expld.spr 0 0 20 1\\n\"\n\"}\\n\"\n\"r_part ce_lg_explosion\\n\"\n\"{\\n\"\n\"model models/bg_expld.spr 0 0 20 1\\n\"\n\"}\\n\"\n\"r_part ce_floor_explosion\\n\"\n\"{\\n\"\n\"model models/fl_expld.spr 0 0 20 1\\n\"\n\"}\\n\"\n\"r_part ce_rider_death\\n\"\n\"{\\n\"\n\"}\\n\"\n\"r_part ce_blue_explosion\\n\"\n\"{\\n\"\n\"model models/xpspblue.spr 0 0 20 1\\n\"\n\"}\\n\"\n\"r_part ce_green_smoke_05\\n\"\n\"{\\n\"\n\"model models/grnsmk1.spr 0 0 20 0.5\\n\"\n\"veladd 1\\n\"\n\"}\\n\"\n\"r_part ce_green_smoke_10\\n\"\n\"{\\n\"\n\"model models/grnsmk1.spr 0 0 10 0.5\\n\"\n\"veladd 1\\n\"\n\"}\\n\"\n\"r_part ce_green_smoke_15\\n\"\n\"{\\n\"\n\"model models/grnsmk1.spr 0 0 6.666 0.5\\n\"\n\"veladd 1\\n\"\n\"}\\n\"\n\"r_part ce_green_smoke_20\\n\"\n\"{\\n\"\n\"model models/grnsmk1.spr 0 0 5 0.5\\n\"\n\"veladd 1\\n\"\n\"}\\n\"\n//\tce_grey_smoke\n\"r_part ce_grey_smoke_15\\n\"\n\"{\\n\"\n\"model models/grysmk1.spr 0 0 6.666 0.5\\n\"\n\"veladd 1\\n\"\n\"}\\n\"\n\"r_part ce_red_smoke\\n\"\n\"{\\n\"\n\"model models/redsmk1.spr 0 0 6.666 0.5\\n\"\n\"veladd 1\\n\"\n\"}\\n\"\n\"r_part ce_slow_white_smoke\\n\"\n\"{\\n\"\n\"model models/whtsmk1.spr 0 0 20 0.5\\n\"\n\"veladd 1\\n\"\n\"}\\n\"\n\"r_part ce_redspark\\n\"\n\"{\\n\"\n\"model models/rspark.spr 0 0 20 1\\n\"\n\"}\\n\"\n\"r_part ce_greenspark\\n\"\n\"{\\n\"\n\"model models/gspark.spr 0 0 20 1\\n\"\n\"}\\n\"\n\"r_part ce_telesmk1\\n\"\n\"{\\n\"\n\"model models/telesmk1.spr 0 0 15 0.5\\n\"\n\"veladd 1\\n\"\n\"}\\n\"\n\"r_part ce_telesmk2\\n\"\n\"{\\n\"\n\"model models/telesmk2.spr 0 0 15 1\\n\"\n\"veladd 1\\n\"\n\"}\\n\"\n\"r_part ce_icehit\\n\"\n\"{\\n\"\n\"model models/icehit.spr 0 0 20 0.5\\n\"\n\"}\\n\"\n\"r_part ce_medusa_hit\\n\"\n\"{\\n\"\n\"model models/medhit.spr 0 0 20 1\\n\"\n\"}\\n\"\n\"r_part ce_mezzo_reflect\\n\"\n\"{\\n\"\n\"model models/mezzoref.spr 0 0 20 1\\n\"\n\"}\\n\"\n\"r_part ce_floor_explosion2\\n\"\n\"{\\n\"\n\"model models/flrexpl2.spr 0 0 20 1\\n\"\n\"}\\n\"\n\"r_part ce_xbow_explosion\\n\"\n\"{\\n\"\n\"model models/xbowexpl.spr 0 0 20 1\\n\"\n\"}\\n\"\n\"r_part ce_new_explosion\\n\"\n\"{\\n\"\n\"model models/gen_expl.spr 0 0 20 1\\n\"\n\"}\\n\"\n\"r_part ce_magic_missile_explosion\\n\"\n\"{\\n\"\n\"model models/mm_expld.spr 0 0 20 1\\n\"\n\"}\\n\"\n//\tce_ghost\n\"r_part ce_bone_explosion\\n\"\n\"{\\n\"\n\"model models/bonexpld.spr 0 0 20 1\\n\"\n\"}\\n\"\n//famine teleport effect\n\"r_part ce_redcloud\\n\"\n\"{\\n\"\n\"model models/rcloud.spr 0 0 20 1\\n\"\n\"}\\n\"\n\"r_part ce_teleporterpuffs\\n\"\n\"{\\n\"\n//\tmodel models/telesmk2.spr 0 0 20 1\n\"}\\n\"\n//\tce_teleporterbody\n//\tce_boneshard\n//\tce_boneshrapnel\n//this is transparent so it doesn't obscure your view\n\"r_part ce_flamestream\\n\"\n\"{\\n\"\n\"model models/flamestr.spr 0 0 20 0.4\\n\"\n\"veladd 1\\n\"\n\"}\\n\"\n\"r_part ce_gravitywell\\n\"\n\"{\\n\"\n\"spawnmode ball\\n\"\n\"count 100\\n\"\n\"spawnorg 128\\n\"\n\"spawnvel -64\\n\"\n\"texture \\\"particles/fteparticlefont.tga\\\"\\n\"\n\"tcoords 1 1 63 63 256 2 64\\n\"\n\"scale 4\\n\"\n\"alpha 1\\n\"\n\"die 2\\n\"\n\"rotationspeed 90\\n\"\n\"rotationstart 0 360\\n\"\n\"rgb 220 200 100\\n\"\n\"rgbrand 0 0 0\\n\"\n\"gravity 0\\n\"\n\"scalefactor 0.4\\n\"\n\"}\\n\"\n\"r_part ce_bldrn_expl\\n\"\n\"{\\n\"\n\"model models/xplsn_1.spr 0 0 20 1\\n\"\n\"}\\n\"\n//demoness tomed acid trail\n\"r_part ce_acid_muzzfl\\n\"\n\"{\\n\"\n\"model models/muzzle1.spr 0 0 20 0.4\\n\"\n\"veladd 1\\n\"\n\"}\\n\"\n\"r_part ce_acid_hit\\n\"\n\"{\\n\"\n\"model models/axplsn_2.spr 0 0 20 1\\n\"\n\"}\\n\"\n\"r_part ce_firewall_small\\n\"\n\"{\\n\"\n\"model models/firewal1.spr 0 0 20 1\\n\"\n\"}\\n\"\n\"r_part ce_firewall_medium\\n\"\n\"{\\n\"\n\"model models/firewal5.spr 0 0 20 1\\n\"\n\"}\\n\"\n\"r_part ce_firewall_large\\n\"\n\"{\\n\"\n\"model models/firewal4.spr 0 0 20 1\\n\"\n\"}\\n\"\n\"r_part ce_onfire\\n\"\n\"{\\n\"\n\"model models/firewal1.spr 0 0 20 0.4\\n\"\n\"model models/firewal2.spr 0 0 20 0.4\\n\"\n\"model models/firewal3.spr 0 0 20 0.4\\n\"\n\"veladd 1\\n\"\n\"}\\n\"\n\"r_part ce_flamewall\\n\"\n\"{\\n\"\n\"model models/firewal1.spr 0 0 20 1\\n\"\n\"veladd 1\\n\"\n\"}\\n\"\n\"r_part ce_flamewall2\\n\"\n\"{\\n\"\n\"model models/firewal2.spr 0 0 20 0.4\\n\"\n\"veladd 1\\n\"\n\"}\\n\"\n\"r_part ce_lball_expl\\n\"\n\"{\\n\"\n\"model models/Bluexp3.spr 0 0 20 1\\n\"\n\"}\\n\"\n\"r_part ce_acid_splat\\n\"\n\"{\\n\"\n\"model models/axplsn_1.spr 0 0 20 1\\n\"\n\"}\\n\"\n\"r_part ce_acid_expl\\n\"\n\"{\\n\"\n\"model models/axplsn_5.spr 0 0 20 1\\n\"\n\"}\\n\"\n\"r_part ce_fboom\\n\"\n\"{\\n\"\n\"model models/fboom.spr 0 0 20 1\\n\"\n\"}\\n\"\n//\tce_chunk\n\"r_part ce_bomb\\n\"\n\"{\\n\"\n\"model models/pow.spr 0 0 20 1\\n\"\n\"}\\n\"\n\"r_part ce_brn_bounce\\n\"\n\"{\\n\"\n\"model models/spark.spr 0 0 20 1\\n\"\n\"}\\n\"\n\"r_part ce_lshock\\n\"\n\"{\\n\"\n\"model models/vorpshok.mdl 0 0 20 1\\n\"\n\"}\\n\"\n//\tce_flamewall\n//\tce_flamewall2\n\"r_part ce_floor_explosion3\\n\"\n\"{\\n\"\n\"model models/biggy.spr 0 0 20 1\\n\"\n\"}\\n\"\n\n\"r_part ce_boneshard\\n\"\n\"{\\n\"\n\"model models/boneshot.mdl 0 1 1 1\\n\"\n\"rotationspeed 425\\n\"\n\"veladd 777\\n\"\t//something's getting normalised, so this is an aproximation.\n\"}\\n\"\n\"r_part ce_boneshrapnel\\n\"\n\"{\\n\"\n\"model models/boneshrd.mdl 0 1 1 1\\n\"\n\"rotationspeed 425\\n\"\n\"veladd 777\\n\"\n\"}\\n\"\n\n\"r_part ce_chunk_greystone\\n\"\n\"{\\n\"\n\"model models/schunk1.mdl 0 1 0.25 1\\n\"\n\"model models/schunk2.mdl 0 1 0.25 1\\n\"\n\"model models/schunk3.mdl 0 1 0.25 1\\n\"\n\"model models/schunk4.mdl 0 1 0.25 1\\n\"\n\"randomvel 210 70 280\\n\"\n\"spawnorg 0\\n\"\n\"gravity 800\\n\"\n\"rotationspeed 425\\n\"\n\"}\\n\"\n\"r_part ce_chunk_wood\\n\"\n\"{\\n\"\n\"model models/splnter1.mdl 0 1 0.25 1\\n\"\n\"model models/splnter2.mdl 0 1 0.25 1\\n\"\n\"model models/splnter3.mdl 0 1 0.25 1\\n\"\n\"model models/splnter4.mdl 0 1 0.25 1\\n\"\n\"randomvel 210 70 280\\n\"\n\"spawnorg 0\\n\"\n\"gravity 800\\n\"\n\"rotationspeed 425\\n\"\n\"}\\n\"\n\"r_part ce_chunk_metal\\n\"\n\"{\\n\"\n\"model models/metlchk1.mdl 0 1 0.25 1\\n\"\n\"model models/metlchk2.mdl 0 1 0.25 1\\n\"\n\"model models/metlchk3.mdl 0 1 0.25 1\\n\"\n\"model models/metlchk4.mdl 0 1 0.25 1\\n\"\n\"randomvel 210 70 280\\n\"\n\"spawnorg 0\\n\"\n\"gravity 800\\n\"\n\"rotationspeed 425\\n\"\n\"}\\n\"\n\"r_part ce_chunk_flesh\\n\"\n\"{\\n\"\n\"model models/flesh1.mdl 0 1 0.25 1 tr_bloodshot\\n\"\n\"model models/flesh2.mdl 0 1 0.25 1 tr_bloodshot\\n\"\n\"model models/flesh3.mdl 0 1 0.25 1 tr_bloodshot\\n\"\n\"randomvel 210 70 280\\n\"\n\"spawnorg 0\\n\"\n\"gravity 800\\n\"\n\"rotationspeed 425\\n\"\n\"}\\n\"\n//r_part ce_chunk_fire\n//{\n//}\n\"r_part ce_chunk_clay\\n\"\n\"{\\n\"\n\"model models/clshard1.mdl 0 1 0.25 1\\n\"\n\"model models/clshard2.mdl 0 1 0.25 1\\n\"\n\"model models/clshard3.mdl 0 1 0.25 1\\n\"\n\"model models/clshard4.mdl 0 1 0.25 1\\n\"\n\"randomvel 210 70 280\\n\"\n\"spawnorg 0\\n\"\n\"gravity 800\\n\"\n\"rotationspeed 425\\n\"\n\"}\\n\"\n\"r_part ce_chunk_leaves\\n\"\n\"{\\n\"\n\"model models/leafchk1.mdl 0 1 0.25 1\\n\"\n\"model models/leafchk2.mdl 0 1 0.25 1\\n\"\n\"model models/leafchk3.mdl 0 1 0.25 1\\n\"\n\"randomvel 210 70 280\\n\"\n\"spawnorg 0\\n\"\n\"gravity 800\\n\"\n\"rotationspeed 425\\n\"\n\"}\\n\"\n\"r_part ce_chunk_hay\\n\"\n\"{\\n\"\n\"model models/hay1.mdl 0 1 0.25 1\\n\"\n\"model models/hay2.mdl 0 1 0.25 1\\n\"\n\"model models/hay3.mdl 0 1 0.25 1\\n\"\n\"randomvel 210 70 280\\n\"\n\"spawnorg 0\\n\"\n\"gravity 800\\n\"\n\"rotationspeed 425\\n\"\n\"}\\n\"\n\"r_part ce_chunk_brownstone\\n\"\n\"{\\n\"\n\"model models/schunk1.mdl 1 1 0.25 1\\n\"\n\"model models/schunk2.mdl 1 1 0.25 1\\n\"\n\"model models/schunk3.mdl 1 1 0.25 1\\n\"\n\"model models/schunk4.mdl 1 1 0.25 1\\n\"\n\"randomvel 210 70 280\\n\"\n\"spawnorg 0\\n\"\n\"gravity 800\\n\"\n\"rotationspeed 425\\n\"\n\"}\\n\"\n\"r_part ce_chunk_cloth\\n\"\n\"{\\n\"\n\"model models/clthchk1.mdl 0 1 0.25 1\\n\"\n\"model models/clthchk2.mdl 0 1 0.25 1\\n\"\n\"model models/clthchk3.mdl 0 1 0.25 1\\n\"\n\"randomvel 210 70 280\\n\"\n\"spawnorg 0\\n\"\n\"gravity 800\\n\"\n\"rotationspeed 425\\n\"\n\"}\\n\"\n\"r_part ce_chunk_wood_leaf\\n\"\n\"{\\n\"\n\"model models/splnter1.mdl 0 1 0.25 1\\n\"\n\"model models/splnter2.mdl 0 1 0.25 1\\n\"\n\"model models/splnter3.mdl 0 1 0.25 1\\n\"\n\"model models/splnter4.mdl 0 1 0.25 1\\n\"\n\"model models/leafchk1.mdl 0 1 0.25 1\\n\"\n\"model models/leafchk2.mdl 0 1 0.25 1\\n\"\n\"model models/leafchk3.mdl 0 1 0.25 1\\n\"\n\"randomvel 210 70 280\\n\"\n\"spawnorg 0\\n\"\n\"gravity 800\\n\"\n\"rotationspeed 425\\n\"\n\n\"}\\n\"\n\"r_part ce_chunk_wood_metal\\n\"\n\"{\\n\"\n\"model models/splnter1.mdl 0 1 0.25 1\\n\"\n\"model models/splnter2.mdl 0 1 0.25 1\\n\"\n\"model models/splnter3.mdl 0 1 0.25 1\\n\"\n\"model models/splnter4.mdl 0 1 0.25 1\\n\"\n\"model models/metlchk1.mdl 0 1 0.25 1\\n\"\n\"model models/metlchk2.mdl 0 1 0.25 1\\n\"\n\"model models/metlchk3.mdl 0 1 0.25 1\\n\"\n\"model models/metlchk4.mdl 0 1 0.25 1\\n\"\n\"randomvel 210 70 280\\n\"\n\"spawnorg 0\\n\"\n\"gravity 800\\n\"\n\"rotationspeed 425\\n\"\n\"}\\n\"\n\"r_part ce_chunk_wood_stone\\n\"\n\"{\\n\"\n\"model models/splnter1.mdl 0 1 0.25 1\\n\"\n\"model models/splnter2.mdl 0 1 0.25 1\\n\"\n\"model models/splnter3.mdl 0 1 0.25 1\\n\"\n\"model models/splnter4.mdl 0 1 0.25 1\\n\"\n\"model models/schunk1.mdl 0 1 0.25 1\\n\"\n\"model models/schunk2.mdl 0 1 0.25 1\\n\"\n\"model models/schunk3.mdl 0 1 0.25 1\\n\"\n\"model models/schunk4.mdl 0 1 0.25 1\\n\"\n\"randomvel 210 70 280\\n\"\n\"spawnorg 0\\n\"\n\"gravity 800\\n\"\n\"rotationspeed 425\\n\"\n\"}\\n\"\n\"r_part ce_chunk_metal_stone\\n\"\n\"{\\n\"\n\"model models/metlchk1.mdl 0 1 0.25 1\\n\"\n\"model models/metlchk2.mdl 0 1 0.25 1\\n\"\n\"model models/metlchk3.mdl 0 1 0.25 1\\n\"\n\"model models/metlchk4.mdl 0 1 0.25 1\\n\"\n\"model models/schunk1.mdl 0 1 0.25 1\\n\"\n\"model models/schunk2.mdl 0 1 0.25 1\\n\"\n\"model models/schunk3.mdl 0 1 0.25 1\\n\"\n\"model models/schunk4.mdl 0 1 0.25 1\\n\"\n\"randomvel 210 70 280\\n\"\n\"spawnorg 0\\n\"\n\"gravity 800\\n\"\n\"rotationspeed 425\\n\"\n\"}\\n\"\n\"r_part ce_chunk_metal_cloth\\n\"\n\"{\\n\"\n\"model models/metlchk1.mdl 0 1 0.25 1\\n\"\n\"model models/metlchk2.mdl 0 1 0.25 1\\n\"\n\"model models/metlchk3.mdl 0 1 0.25 1\\n\"\n\"model models/metlchk4.mdl 0 1 0.25 1\\n\"\n\"model models/clthchk1.mdl 0 1 0.25 1\\n\"\n\"model models/clthchk2.mdl 0 1 0.25 1\\n\"\n\"model models/clthchk3.mdl 0 1 0.25 1\\n\"\n\"randomvel 210 70 280\\n\"\n\"spawnorg 0\\n\"\n\"gravity 800\\n\"\n\"rotationspeed 425\\n\"\n\"}\\n\"\n\"r_part ce_chunk_webs\\n\"\n\"{\\n\"\n\"model models/shard1.mdl 3 1 0.25 -0.5\\n\"\n\"model models/shard2.mdl 3 1 0.25 -0.5\\n\"\n\"model models/shard3.mdl 3 1 0.25 -0.5\\n\"\n\"model models/shard4.mdl 3 1 0.25 -0.5\\n\"\n\"model models/shard5.mdl 3 1 0.25 -0.5\\n\"\n\"randomvel 210 70 280\\n\"\n\"spawnorg 0\\n\"\n\"gravity 500\\n\"\n\"rotationspeed 425\\n\"\n\"}\\n\"\n\"r_part ce_chunk_glass\\n\"\n\"{\\n\"\n\"model models/shard1.mdl 0 1 0.25 1\\n\"\n\"model models/shard2.mdl 0 1 0.25 1\\n\"\n\"model models/shard3.mdl 0 1 0.25 1\\n\"\n\"model models/shard4.mdl 0 1 0.25 1\\n\"\n\"model models/shard5.mdl 0 1 0.25 1\\n\"\n\"randomvel 210 70 280\\n\"\n\"spawnorg 0\\n\"\n\"gravity 800\\n\"\n\"rotationspeed 425\\n\"\n\"}\\n\"\n\"r_part ce_chunk_ice\\n\"\n\"{\\n\"\n\"model models/shard.mdl 0 1 0.25 -0.4 tr_ice\\n\"\n\"model models/shard.mdl 1 1 0.25 -0.4 tr_ice\\n\"\n\"rotationspeed 30\\n\"\n\"randomvel 210 70 280\\n\"\n\"spawnorg 0\\n\"\n\"gravity 800\\n\"\n\"}\\n\"\n\"r_part ce_chunk_clearglass\\n\"\n\"{\\n\"\n\"model models/shard1.mdl 1 1 0.25 -0.5\\n\"\n\"model models/shard2.mdl 1 1 0.25 -0.5\\n\"\n\"model models/shard3.mdl 1 1 0.25 -0.5\\n\"\n\"model models/shard4.mdl 1 1 0.25 -0.5\\n\"\n\"model models/shard5.mdl 1 1 0.25 -0.5\\n\"\n\"randomvel 210 70 280\\n\"\n\"spawnorg 0\\n\"\n\"gravity 800\\n\"\n\"rotationspeed 425\\n\"\n\"}\\n\"\n\"r_part ce_chunk_redglass\\n\"\n\"{\\n\"\n\"model models/shard1.mdl 2 1 0.25 1\\n\"\n\"model models/shard2.mdl 2 1 0.25 1\\n\"\n\"model models/shard3.mdl 2 1 0.25 1\\n\"\n\"model models/shard4.mdl 2 1 0.25 1\\n\"\n\"model models/shard5.mdl 2 1 0.25 1\\n\"\n\"randomvel 210 70 280\\n\"\n\"spawnorg 0\\n\"\n\"gravity 800\\n\"\n\"rotationspeed 425\\n\"\n\"}\\n\"\n\"r_part ce_chunk_acid\\n\"\n\"{\\n\"\n\"model models/sucwp2p.mdl 0 1 0.25 1 tr_acidball\\n\"\n\"randomvel 210 70 280\\n\"\n\"spawnorg 0\\n\"\n\"gravity 800\\n\"\n\"rotationspeed 425\\n\"\n\"}\\n\"\n\"r_part ce_chunk_meteor\\n\"\n\"{\\n\"\n\"model models/tempmetr.mdl framestart=0 framecount=1 framerate=0.5 alpha=-1 trail=tr_grenade scalemin=0.30 scalemax=.70 fullbright\\n\"\n\"randomvel 360\\n\"\n\"spawnorg 0\\n\"\n\"gravity 800\\n\"\n\"rotationspeed 425\\n\"\n\"}\\n\"\n\"r_part ce_chunk_greenflesh\\n\"\n\"{\\n\"\n\"model models/sflesh1.mdl 0 1 0.25 1 tr_spiderblood\\n\"\n\"model models/sflesh2.mdl 0 1 0.25 1 tr_spiderblood\\n\"\n\"model models/sflesh3.mdl 0 1 0.25 1 tr_spiderblood\\n\"\n\"randomvel 210 70 280\\n\"\n\"spawnorg 0\\n\"\n\"gravity 800\\n\"\n\"rotationspeed 425\\n\"\n\"}\\n\"\n\"r_part ce_chunk_bone\\n\"\n\"{\\n\"\n\"model models/clshard1.mdl 1 1 0.25 1\\n\"\n\"model models/clshard2.mdl 1 1 0.25 1\\n\"\n\"model models/clshard3.mdl 1 1 0.25 1\\n\"\n\"model models/clshard4.mdl 1 1 0.25 1\\n\"\n\"randomvel 210 70 280\\n\"\n\"spawnorg 0\\n\"\n\"gravity 800\\n\"\n\"rotationspeed 425\\n\"\n\"}\\n\"\n\n\"r_part ce_fountain\\n\"\n\"{\\n\"\n\"texture \\\"particles/fteparticlefont.tga\\\"\\n\"\n\"tcoords 1 1 63 63 256 2 64\\n\"\n\"count 1\\n\"\n\"scale 10\\n\"\n\"rotationspeed -64 64\\n\"\n\"scalefactor 1\\n\"\n\"die 1\\n\"\n\"alpha 0.2\\n\"\n\"rgb 128 128 128\\n\"\n\"rgbdelta 0 -32 -32\\n\"\n\"blend add\\n\"\n\"spawnvel 100\\n\"\n\"veladd 1\\n\"\n\"gravity 800\\n\"\n\"}\\n\"\n\n\"r_part ce_snow\\n\"\n\"{\\n\"\n\"texture \\\"particles/fteparticlefont.tga\\\"\\n\"\n\"tcoords 1 1 63 63 256 2 64\\n\"\n\"count 1\\n\"\n\"scale 30\\n\"\n\"scaledelta -10\\n\"\n\"rotationspeed -64 64\\n\"\n\"scalefactor 1\\n\"\n\"die 7\\n\"\n\"alpha 0.2\\n\"\n\"rgb 255 255 255\\n\"\n\"rgbdelta 0 -32 -32\\n\"\n\"friction 0\\n\"\n\"blend add\\n\"\n\"veladd 1\\n\"\n\"gravity 0\\n\"\n\"flurry 32\\n\"\n\"}\\n\"\n\n//eidolon's arena\n\"r_part ce_rain\\n\"\n\"{\\n\"\n\"texture \\\"particles/fteparticlefont.tga\\\"\\n\"\n\"tcoords 1 1 63 63 256 2 64\\n\"\n\"type texturedspark\\n\"\n\"count   1\\n\"\n\"scale 2\\n\"\n\"scalefactor 1\\n\"\n\"die 1\\n\"\n\"alpha 0.2\\n\"\n\"alphadelta 0\\n\"\n\"rgb 255 255 255\\n\"\n\"friction 0\\n\"\n\"blend add\\n\"\n\"veladd 1\\n\"\n\"gravity 0\\n\"\n\"}\\n\"\n\n//this teleport effect is nothing like hexen2's. hopefully it'll be acceptable :s\n//the down ring\n\"r_part ce_teleporterbody\\n\"\n\"{\\n\"\n\"texture \\\"particles/fteparticlefont.tga\\\"\\n\"\n\"tcoords 193 1 255 63 256\\n\"\n\"count 32\\n\"\n\"scale 16\\n\"\n\"scalefactor 1\\n\"\n\"alpha 0.3\\n\"\n\"die 1\\n\"\n\"veladd -52\\n\"\n\"rgb 255 255 255\\n\"\n\"friction 1\\n\"\n\"spawnorg 32 0\\n\"\n\"spawnmode uniformcircle\\n\"\n\"}\\n\"\n//the up ring\n\"r_part +ce_teleporterbody\\n\"\n\"{\\n\"\n\"texture \\\"particles/fteparticlefont.tga\\\"\\n\"\n\"tcoords 193 1 255 63 256\\n\"\n\"count 32\\n\"\n\"scale 16\\n\"\n\"scalefactor 1\\n\"\n\"alpha 0.3\\n\"\n\"die 1\\n\"\n\"veladd 52\\n\"\n\"rgb 255 255 255\\n\"\n\"friction 1\\n\"\n\"spawnorg 32 0\\n\"\n\"spawnmode uniformcircle\\n\"\n\"}\\n\"\n\n\n//h2part.ce_quake was not loaded\n//h2part.ce_ghost was not loaded\n//h2part.ce_teleporterbody_1 was not loaded\n//h2part.ce_grey_smoke_100 was not loaded\n//h2part.ce_chunk_fire was not loaded\n;\n#endif\n\n\n\n//////////////////////////////////////////////////////\n\n\n#ifdef Q2CLIENT\nchar *particle_set_q2part =\n//model \"name\" framestart= frames= framerate= alpha= trail= orient additive transparent fullbright shadow noshadow\n\n\n\"r_part namespace q2part\\n\"\n\n\"r_part pe_default\\n\"\n\"{\\n\"\n\"texture \\\"classicparticle\\\"\\n\"\n\"tcoords 0 0 16 16 32\\n\"\n\"count 1\\n\"\n\"scale 1\\n\"\n\"alpha 1\\n\"\n\"die 0.3 0.8\\n\"\n\"randomvel 20\\n\"\n\"orgadd 0 31\\n\"\n\"spawnorg 4\\n\"\n\"gravity 40\\n\"\n\"scalefactor 0.8\\n\"\n\"}\\n\"\n\n\"r_part te_splashsparks\\n\"\n\"{\\n\"\n\"texture \\\"classicparticle\\\"\\n\"\n\"tcoords 0 0 16 16 32\\n\"\n\"count 1\\n\"\n\"scale 1\\n\"\n\"alpha 1\\n\"\n\"die 0.3 0.8\\n\"\n\"randomvel 20\\n\"\n\"orgadd 0 31\\n\"\n\"spawnorg 4\\n\"\n\"gravity 40\\n\"\n\"scalefactor 0.8\\n\"\n\"colorindex 0xe0\\n\"\n\"}\\n\"\n\"r_part te_splashunknown\\n\"\n\"{\\n\"\n\"assoc te_splashsparks\\n\"\n\"}\\n\"\n\"r_part teq2_sparks\\n\"\n\"{\\n\"\n\"texture \\\"classicparticle\\\"\\n\"\n\"tcoords 0 0 16 16 32\\n\"\n\"count 6\\n\"\n\"scale 1\\n\"\n\"alpha 1\\n\"\n\"die 0.3 0.8\\n\"\n\"randomvel 20\\n\"\n\"orgadd 0 31\\n\"\n\"spawnorg 4\\n\"\n\"gravity 40\\n\"\n\"scalefactor 0.8\\n\"\n\"colorindex 0xe0\\n\"\n\"}\\n\"\n\"r_part te_splashbluewater\\n\"\n\"{\\n\"\n\"texture \\\"classicparticle\\\"\\n\"\n\"tcoords 0 0 16 16 32\\n\"\n\"count 1\\n\"\n\"scale 1\\n\"\n\"alpha 1\\n\"\n\"die 0.3 0.8\\n\"\n\"randomvel 20\\n\"\n\"orgadd 0 31\\n\"\n\"spawnorg 4\\n\"\n\"gravity 40\\n\"\n\"scalefactor 0.8\\n\"\n\"colorindex 0xb0\\n\"\n\"}\\n\"\n\"r_part te_splashbrownwater\\n\"\n\"{\\n\"\n\"texture \\\"classicparticle\\\"\\n\"\n\"tcoords 0 0 16 16 32\\n\"\n\"count 1\\n\"\n\"scale 1\\n\"\n\"alpha 1\\n\"\n\"die 0.3 0.8\\n\"\n\"randomvel 20\\n\"\n\"orgadd 0 31\\n\"\n\"spawnorg 4\\n\"\n\"gravity 40\\n\"\n\"scalefactor 0.8\\n\"\n\"colorindex 0x50\\n\"\n\"}\\n\"\n\"r_part te_splashslime\\n\"\n\"{\\n\"\n\"texture \\\"classicparticle\\\"\\n\"\n\"tcoords 0 0 16 16 32\\n\"\n\"count 1\\n\"\n\"scale 1\\n\"\n\"alpha 1\\n\"\n\"die 0.3 0.8\\n\"\n\"randomvel 20\\n\"\n\"orgadd 0 31\\n\"\n\"spawnorg 4\\n\"\n\"gravity 40\\n\"\n\"scalefactor 0.8\\n\"\n\"colorindex 0xd0\\n\"\n\"}\\n\"\n\"r_part te_splashlava\\n\"\n\"{\\n\"\n\"texture \\\"classicparticle\\\"\\n\"\n\"tcoords 0 0 16 16 32\\n\"\n\"count 1\\n\"\n\"scale 1\\n\"\n\"alpha 1\\n\"\n\"die 0.3 0.8\\n\"\n\"randomvel 20\\n\"\n\"orgadd 0 31\\n\"\n\"spawnorg 4\\n\"\n\"gravity 40\\n\"\n\"scalefactor 0.8\\n\"\n\"colorindex 0xe0\\n\"\n\"}\\n\"\n\"r_part te_splashelect\\n\"\n\"{\\n\"\n\"texture \\\"classicparticle\\\"\\n\"\n\"tcoords 0 0 16 16 32\\n\"\n\"count 20\\n\"\n\"scale 1\\n\"\n\"alpha 1\\n\"\n\"die 0.3 0.5\\n\"\n\"randomvel 20\\n\"\n\"orgadd 0 31\\n\"\n\"spawnorg 4\\n\"\n\"gravity 40\\n\"\n\"scalefactor 0.8\\n\"\n\"colorindex 0xb0\\n\"\n\"}\\n\"\n\"r_part te_splashblood\\n\"\n\"{\\n\"\n\"texture \\\"classicparticle\\\"\\n\"\n\"tcoords 0 0 16 16 32\\n\"\n\"count 1\\n\"\n\"scale 1\\n\"\n\"alpha 1\\n\"\n\"die 0.3 0.8\\n\"\n\"randomvel 20\\n\"\n\"orgadd 0 31\\n\"\n\"spawnorg 4\\n\"\n\"gravity 40\\n\"\n\"scalefactor 0.8\\n\"\n\"colorindex 0xe8\\n\"\n\"}\\n\"\n\n\"r_part teq2_laser_sparks\\n\"\n\"{\\n\"\n\"texture \\\"classicparticle\\\"\\n\"\n\"tcoords 0 0 16 16 32\\n\"\n\"count 1\\n\"\n\"scale 1\\n\"\n\"alpha 1\\n\"\n\"die 0.3 0.8\\n\"\n\"randomvel 20\\n\"\n\"orgadd 0 7\\n\"\n\"spawnorg 4\\n\"\n\"gravity 40\\n\"\n\"scalefactor 0.8\\n\"\n\"}\\n\"\n\"r_part teq2_welding_sparks\\n\"\n\"{ //identical to teq2_laser_sparks, except for the +form that adds in some extra mesh+lighting effect.\\n\"\n\"texture \\\"classicparticle\\\"\\n\"\n\"tcoords 0 0 16 16 32\\n\"\n\"count 1\\n\"\n\"scale 1\\n\"\n\"alpha 1\\n\"\n\"die 0.3 0.8\\n\"\n\"randomvel 20\\n\"\n\"orgadd 0 7\\n\"\n\"spawnorg 4\\n\"\n\"gravity 40\\n\"\n\"scalefactor 0.8\\n\"\n\"}\\n\"\n\"r_part +teq2_welding_sparks\\n\"\n\"{\\n\"\n\"count 0 0 1\\n\"\n\"model \\\"models/objects/flash/tris.md2\\\" framestart=0 frameend=2 framerate=10 alpha=-1 fullbright\\n\"\n\"lightradius 100 175\\n\"\n\"lightradiusfade 400\\n\"\n\"lightrgb 1 1 0.3\\n\"\n\"}\\n\"\n\"r_part teq2_tunnel_sparks\\n\"\n\"{ //this is apparently identical to teq2_laser_sparks... either way the protocol provides a palette colour (particle system provides a customised variation)\\n\"\n\"texture \\\"classicparticle\\\"\\n\"\n\"tcoords 0 0 16 16 32\\n\"\n\"count 1\\n\"\n\"scale 1\\n\"\n\"alpha 1\\n\"\n\"die 0.3 0.8\\n\"\n\"randomvel 20\\n\"\n\"orgadd 0 7\\n\"\n\"spawnorg 4\\n\"\n\"gravity 40\\n\"\n\"scalefactor 0.8\\n\"\n\"}\\n\"\n\"r_part teq2_shield_sparks\\n\"\n\"{\\n\"\n\"texture \\\"classicparticle\\\"\\n\"\n\"tcoords 0 0 16 16 32\\n\"\n\"count 40\\n\"\n\"colorindex 0xb0\\n\"\n\"scale 1\\n\"\n\"alpha 1\\n\"\n\"die 0.3 0.8\\n\"\n\"randomvel 20\\n\"\n\"orgadd 0 31\\n\"\n\"spawnorg 4\\n\"\n\"gravity 40\\n\"\n\"scalefactor 0.8\\n\"\n\"}\\n\"\n\"r_part teq2_screen_sparks\\n\"\n\"{\\n\"\n\"texture \\\"classicparticle\\\"\\n\"\n\"tcoords 0 0 16 16 32\\n\"\n\"count 40\\n\"\n\"colorindex 0xd0\\n\"\n\"scale 1\\n\"\n\"alpha 1\\n\"\n\"die 0.3 0.8\\n\"\n\"randomvel 20\\n\"\n\"orgadd 0 31\\n\"\n\"spawnorg 4\\n\"\n\"gravity 40\\n\"\n\"scalefactor 0.8\\n\"\n\"}\\n\"\n\"r_part teq2_bullet_sparks\\n\"\n\"{\\n\"\n\"texture \\\"classicparticle\\\"\\n\"\n\"tcoords 0 0 16 16 32\\n\"\n\"count 6\\n\"\n\"colorindex 0xe0\\n\"\n\"scale 1\\n\"\n\"alpha 1\\n\"\n\"die 0.3 0.8\\n\"\n\"randomvel 20\\n\"\n\"orgadd 0 31\\n\"\n\"spawnorg 4\\n\"\n\"gravity 40\\n\"\n\"scalefactor 0.8\\n\"\n\"}\\n\"\n\n\"r_part q2_smoke\\n\"\n\"{\\n\"\n\"count 0 0 1\\n\"\n\"model \\\"models/objects/smoke/tris.md2\\\" framestart=0 frameend=4 framerate=10 alpha=1\\n\"\n\"}\\n\"\n\"r_part q2_smokeandflash\\n\"\n\"{\\n\"\n\"count 0 0 1\\n\"\n\"model \\\"models/objects/flash/tris.md2\\\" framestart=0 frameend=2 framerate=10 alpha=-1 fullbright\\n\"\n\"assoc q2_smoke\\n\"\n\"}\\n\"\n\n\"r_part teq2_gunshot /*machinegun*/\\n\"\n\"{\\n\"\n\"texture \\\"classicparticle\\\"\\n\"\n\"tcoords 0 0 16 16 32\\n\"\n\"count 40\\n\"\n\"scale 1\\n\"\n\"alpha 1\\n\"\n\"die 0.3 0.8\\n\"\n\"randomvel 20\\n\"\n\"orgadd 0 31\\n\"\n\"spawnorg 4\\n\"\n\"gravity 40\\n\"\n\"scalefactor 0.8\\n\"\n\"colorindex 0 7\\n\"\n\"/*smoke puff models*/\\n\"\n\"assoc q2_smokeandflash\\n\"\n\"/*low chance of various sounds*/\\n\"\n\"sound world/ric1.wav 1 1 0 0 1\\n\"\n\"sound world/ric2.wav 1 1 0 0 1\\n\"\n\"sound world/ric3.wav 1 1 0 0 1\\n\"\n\"sound \\\"\\\" 1 1 0 0 12\\n\"\n\"}\\n\"\n\n\"r_part teq2_shotgun /*shotgun... duh*/\\n\"\n\"{\\n\"\n\"texture \\\"classicparticle\\\"\\n\"\n\"tcoords 0 0 16 16 32\\n\"\n\"count 20\\n\"\n\"scale 1\\n\"\n\"alpha 1\\n\"\n\"die 0.3 0.8\\n\"\n\"randomvel 20\\n\"\n\"orgadd 0 31\\n\"\n\"spawnorg 4\\n\"\n\"gravity 40\\n\"\n\"scalefactor 0.8\\n\"\n\"colorindex 0 7\\n\"\n\"/*smoke puff models*/\\n\"\n\"assoc q2_smokeandflash\\n\"\n\"}\\n\"\n\n\"r_part teq2_blood\\n\"\n\"{\\n\"\n\"texture \\\"classicparticle\\\"\\n\"\n\"tcoords 0 0 16 16 32\\n\"\n\"count 60\\n\"\n\"scale 1\\n\"\n\"alpha 1\\n\"\n\"die 0.3 0.8\\n\"\n\"randomvel 20\\n\"\n\"orgadd 0 31\\n\"\n\"spawnorg 4\\n\"\n\"gravity 40\\n\"\n\"scalefactor 0.8\\n\"\n\"colorindex 232 7\\n\"\n\"}\\n\"\n\"r_part teq2_moreblood\\n\"\n\"{ //teq2_blood, but with count 250 instead of 60.\\n\"\n\"texture \\\"classicparticle\\\"\\n\"\n\"tcoords 0 0 16 16 32\\n\"\n\"count 250\\n\"\n\"scale 1\\n\"\n\"alpha 1\\n\"\n\"die 0.3 0.8\\n\"\n\"randomvel 20\\n\"\n\"orgadd 0 31\\n\"\n\"spawnorg 4\\n\"\n\"gravity 40\\n\"\n\"scalefactor 0.8\\n\"\n\"colorindex 232 7\\n\"\n\"}\\n\"\n\"r_part teq2_greenblood\\n\"\n\"{\\n\"\n\"texture \\\"classicparticle\\\"\\n\"\n\"tcoords 0 0 16 16 32\\n\"\n\"count 30\\n\"\n\"scale 1\\n\"\n\"alpha 1\\n\"\n\"die 0.3 0.8\\n\"\n\"randomvel 20\\n\"\n\"orgadd 0 31\\n\"\n\"spawnorg 4\\n\"\n\"gravity 40\\n\"\n\"scalefactor 0.8\\n\"\n\"colorindex 0xdf 7\\n\"\n\"}\\n\"\n\n\"r_part teq2_electric_sparks\\n\"\n\"{\\n\"\n\"texture \\\"classicparticle\\\"\\n\"\n\"tcoords 0 0 16 16 32\\n\"\n\"count 40\\n\"\n\"scale 1\\n\"\n\"alpha 1\\n\"\n\"die 0.3 0.8\\n\"\n\"randomvel 20\\n\"\n\"orgadd 0 31\\n\"\n\"spawnorg 4\\n\"\n\"gravity 40\\n\"\n\"scalefactor 0.8\\n\"\n\"colorindex 0x75 7\\n\"\n\"sound \\\"weapons/lashit.wav\\\" 1 1 0 0\\n\"\n\"}\\n\"\n\n\"r_part q2_blasterpuff\\n\"\n\"{\\n\"\n\"count 0 0 1\\n\"\n\"model \\\"models/objects/explode/tris.md2\\\" framestart=0 frameend=4 framerate=10 alpha=1 orient additive fullbright noshadow skin=0\\n\"\n\"}\\n\"\n\"r_part q2_blaster2puff\\n\"\n\"{\\n\"\n\"count 0 0 1\\n\"\n\"model \\\"models/objects/explode/tris.md2\\\" framestart=0 frameend=4 framerate=10 alpha=1 orient additive fullbright noshadow skin=1\\n\"\n\"}\\n\"\n\"r_part q2_flechettepuff\\n\"\n\"{\\n\"\n\"count 0 0 1\\n\"\n\"model \\\"models/objects/explode/tris.md2\\\" framestart=0 frameend=4 framerate=10 alpha=1 orient additive fullbright noshadow skin=2\\n\"\n\"}\\n\"\n\"r_part teq2_blaster\\n\"\n\"{\\n\"\n\"texture \\\"classicparticle\\\"\\n\"\n\"tcoords 0 0 16 16 32\\n\"\n\"count 60\\n\"\n\"scale 1\\n\"\n\"alpha 1\\n\"\n\"die 0.3 0.8\\n\"\n\"randomvel 40\\n\"\n\"orgadd 0 15\\n\"\n\"veladd 30\\n\"\n\"spawnorg 4\\n\"\n\"gravity 40\\n\"\n\"scalefactor 0.8\\n\"\n\"colorindex 0xe0 7\\n\"\n\"assoc q2_blasterpuff /*the model*/\\n\"\n\"lightradius 150\\n\"\n\"lightradiusfade 400\\n\"\n\"lightrgb 1 1 0\\n\"\n\"lightshadows 1\\n\"\n\"sound \\\"weapons/lashit.wav\\\" 1 1 0 0\\n\"\n\"}\\n\"\n\"r_part teq2_blaster2\\n\"\n\"{ //green version.\\n\"\n\"texture \\\"classicparticle\\\"\\n\"\n\"tcoords 0 0 16 16 32\\n\"\n\"count 60\\n\"\n\"scale 1\\n\"\n\"alpha 1\\n\"\n\"die 0.3 0.8\\n\"\n\"randomvel 40\\n\"\n\"orgadd 0 15\\n\"\n\"veladd 30\\n\"\n\"spawnorg 4\\n\"\n\"gravity 40\\n\"\n\"scalefactor 0.8\\n\"\n\"colorindex 0xd0 7\\n\"\n\"assoc q2_blaster2puff /*the model*/\\n\"\n\"lightradius 150\\n\"\n\"lightradiusfade 400\\n\"\n\"lightrgb 0.05 1.0 0.05\\n\"\n\"lightshadows 1\\n\"\n\"sound \\\"weapons/lashit.wav\\\" 1 1 0 0\\n\"\n\"}\\n\"\n\"r_part teq2_flechette\\n\"\n\"{ //grey version.\\n\"\n\"texture \\\"classicparticle\\\"\\n\"\n\"tcoords 0 0 16 16 32\\n\"\n\"count 60\\n\"\n\"scale 1\\n\"\n\"alpha 1\\n\"\n\"die 0.3 0.8\\n\"\n\"randomvel 40\\n\"\n\"orgadd 0 15\\n\"\n\"veladd 30\\n\"\n\"spawnorg 4\\n\"\n\"gravity 40\\n\"\n\"scalefactor 0.8\\n\"\n\"colorindex 0x6f 7\\n\"\n\"assoc q2_flechettepuff /*the model*/\\n\"\n\"lightradius 150\\n\"\n\"lightradiusfade 400\\n\"\n\"lightrgb 0.19 0.41 0.75\\n\"\n\"lightshadows 1\\n\"\n\"sound \\\"weapons/lashit.wav\\\" 1 1 0 0\\n\"\n\"}\\n\"\n\"r_part teq2_bluehyperblaster\\n\"\n\"{ //misnamed - just the regular orangey particles without sound/puff\\n\"\n\"texture \\\"classicparticle\\\"\\n\"\n\"tcoords 0 0 16 16 32\\n\"\n\"count 60\\n\"\n\"scale 1\\n\"\n\"alpha 1\\n\"\n\"die 0.3 0.8\\n\"\n\"randomvel 40\\n\"\n\"orgadd 0 15\\n\"\n\"veladd 30\\n\"\n\"spawnorg 4\\n\"\n\"gravity 40\\n\"\n\"scalefactor 0.8\\n\"\n\"colorindex 0xe0 7\\n\"\n\"}\\n\"\n\"r_part TR_BLASTERTRAIL\\n\"\n\"{\\n\"\n\"texture \\\"classicparticle\\\"\\n\"\n\"tcoords 0 0 16 16 32\\n\"\n\"scale 0.5\\n\"\n\"alpha 1\\n\"\n\"scalefactor 0.8\\n\"\n\"step 5\\n\"\n\"spawnorg 1\\n\"\n\"randomvel 5\\n\"\n\"die 0.3 0.5\\n\"\n\"colorindex 0xe0\\n\"\n\"lightradius 200\\n\"\n\"lightradiusfade 400\\n\"\n\"lightrgb 1.0 1.0 0.0\\n\"\n\"lightshadows 1\\n\"\n\"}\\n\"\n\n//green version\n\"r_part TR_BLASTERTRAIL2\\n\"\n\"{\\n\"\n\"texture \\\"classicparticle\\\"\\n\"\n\"tcoords 0 0 16 16 32\\n\"\n\"scale 0.5\\n\"\n\"alpha 1\\n\"\n\"scalefactor 0.8\\n\"\n\"step 5\\n\"\n\"spawnorg 1\\n\"\n\"randomvel 5\\n\"\n\"die 0.3 0.5\\n\"\n\"colorindex 0xd0\\n\"\n\"lightradius 200\\n\"\n\"lightradiusfade 400\\n\"\n\"lightrgb 0.0 1.0 0.0\\n\"\n\"lightshadows 1\\n\"\n\"}\\n\"\n\n\n\"r_part teq2_bubbletrail\\n\"\n\"{\\n\"\n\"texture \\\"classicparticle\\\"\\n\"\n\"tcoords 0 0 16 16 32\\n\"\n\"scale 0.5\\n\"\n\"alpha 1\\n\"\n\"scalefactor 0.8\\n\"\n\"step 32\\n\"\n\"spawnorg 2\\n\"\n\"spawnvel 5\\n\"\n\"die 1 1.2\\n\"\n\"colorindex 4 7\\n\"\n\"velbias 0 0 6\\n\"\n\"}\\n\"\n\"r_part teq2_bubbletrail2\\n\"\n\"{\\n\"\n\"texture \\\"classicparticle\\\"\\n\"\n\"tcoords 0 0 16 16 32\\n\"\n\"scale 0.5\\n\"\n\"alpha 1\\n\"\n\"scalefactor 0.8\\n\"\n\"step 8\\n\"\n\"spawnorg 2\\n\"\n\"spawnvel 10\\n\"\n\"die 1 1.1\\n\"\n\"colorindex 4 7\\n\"\n\"velbias 0 0 20\\n\"\n\"sound \\\"weapons/lashit.wav\\\" 1 1 0 0\\n\"\n\"}\\n\"\n\n\"r_part teq2_railtrail\\n\"\n\"{\\n\"\n//blue spiral\n\"texture \\\"classicparticle\\\"\\n\"\n\"tcoords 0 0 16 16 32\\n\"\n\"scale 0.5\\n\"\n\"alpha 1\\n\"\n\"scalefactor 0.8\\n\"\n\"step 1\\n\"\n\"spawnmode spiral 64\\n\"\n\"spawnorg 3\\n\"\n\"spawnvel 6\\n\"\n\"die 1 1.2\\n\"\n\"colorindex 116 7\\n\"\n\n\"sound \\\"weapons/railgf1a.wav\\\" 1 1 0 0\\n\"\n\"}\\n\"\n\"r_part +teq2_railtrail\\n\"\n\"{\\n\"\n//grey filler\n\"texture \\\"classicparticle\\\"\\n\"\n\"tcoords 0 0 16 16 32\\n\"\n\"scale 0.5\\n\"\n\"alpha 1\\n\"\n\"scalefactor 0.8\\n\"\n\"step 0.75\\n\"\n\"spawnorg 3\\n\"\n\"spawnvel 3\\n\"\n\"die 0.6 0.8\\n\"\n\"colorindex 0 15\\n\"\n\"}\\n\"\n\n\"r_part teq2_railtrail2\\n\"\n\"{ //This is not implemented in vanilla, so we've no idea what it should really look like.\\n\"\n//we just use the blue spiral with no core.\n//blue spiral\n\"texture \\\"classicparticle\\\"\\n\"\n\"tcoords 0 0 16 16 32\\n\"\n\"scale 0.5\\n\"\n\"alpha 1\\n\"\n\"scalefactor 0.8\\n\"\n\"step 1\\n\"\n\"spawnmode spiral 64\\n\"\n\"spawnorg 3\\n\"\n\"spawnvel 6\\n\"\n\"die 1 1.2\\n\"\n\"colorindex 116 7\\n\"\n\n\"sound \\\"weapons/railgf1a.wav\\\" 1 1 0 0\\n\"\n\"}\\n\"\n\n//regular explosion particles\n\"r_part std_explosion_particles\\n\"\n\"{\\n\"\n\"texture \\\"classicparticle\\\"\\n\"\n\"tcoords 0 0 16 16 32\\n\"\n\"count 256\\n\"\n\"scale 4\\n\"\n\"alpha 0.4\\n\"\n\"die 1 0.625\\n\"\n\"spawnmode ball\\n\"\n\"spawnorg 16\\n\"\n\"spawnvel 192\\n\"\n\"scalefactor 0.8\\n\"\n\"gravity 40\\n\"\n\"colorindex 0xe0 7\\n\"\n\"}\\n\"\n\n\"r_part teq2_explosion1_big\\n\"\n\"{\\n\"\n\"lighttime 0.5\\n\"\n\"lightradius 350\\n\"\n\"lightradiusfade 300\\n\"\n\"lightrgb 1.0 0.5 0.4\\n\"\n\"lightrgbfade 0.36 0.19 0.19\\n\"\n\"sound \\\"weapons/rocklx1a.wav\\\" 1 1 0 0\\n\"\n\"model \\\"models/objects/r_explode2/tris.md2\\\" framestart=0 frames=15 skin=-1 transparent fullbright noshadow\\n\"\n\"model \\\"models/objects/r_explode2/tris.md2\\\" framestart=15 frames=15 skin=-1 transparent fullbright noshadow\\n\"\n\"}\\n\"\n\"r_part teq2_explosion1_np\\n\"\n\"{\\n\"\n\"lighttime 0.5\\n\"\n\"lightradius 350\\n\"\n\"lightradiusfade 300\\n\"\n\"lightrgb 1.0 0.5 0.4\\n\"\n\"lightrgbfade 0.36 0.19 0.19\\n\"\n\"sound \\\"weapons/rocklx1a.wav\\\" 1 1 0 0\\n\"\n\"model \\\"models/objects/r_explode/tris.md2\\\" framestart=0 frames=15 skin=-1 fullbright noshadow\\n\"\n\"model \\\"models/objects/r_explode/tris.md2\\\" framestart=15 frames=150 skin=-1 fullbright noshadow\\n\"\n\"}\\n\"\n\"r_part teq2_explosion1\\n\"\n\"{\\n\"\n\"assoc teq2_rocket_explosion\\n\"\n\"}\\n\"\n\"r_part teq2_rocket_explosion\\n\"\n\"{\\n\"\n\"assoc std_explosion_particles\\n\"\n\"lighttime 0.5\\n\"\n\"lightradius 350\\n\"\n\"lightradiusfade 300\\n\"\n\"lightrgb 1.0 0.5 0.4\\n\"\n\"lightrgbfade 0.36 0.19 0.19\\n\"\n\"sound \\\"weapons/rocklx1a.wav\\\" 1 1 0 0\\n\"\n\"model \\\"models/objects/r_explode/tris.md2\\\" framestart=0 frames=15 skin=-1 fullbright noshadow\\n\"\n\"model \\\"models/objects/r_explode/tris.md2\\\" framestart=15 frames=15 skin=-1 fullbright noshadow\\n\"\n\"}\\n\"\n\"r_part teq2_rocket_explosion_water\\n\"\n\"{\\n\"\n\"assoc std_explosion_particles\\n\"\n\"lighttime 0.5\\n\"\n\"lightradius 350\\n\"\n\"lightradiusfade 300\\n\"\n\"lightrgb 1.0 0.5 0.4\\n\"\n\"lightrgbfade 0.36 0.19 0.19\\n\"\n\"sound \\\"weapons/xpld_wat.wav\\\" 1 1 0 0\\n\"\n\"model \\\"models/objects/r_explode/tris.md2\\\" framestart=0 frames=15 skin=-1 fullbright noshadow\\n\"\n\"model \\\"models/objects/r_explode/tris.md2\\\" framestart=15 frames=15 skin=-1 fullbright noshadow\\n\"\n\"}\\n\"\n\n\"r_part teq2_explosion2\\n\"\n\"{\\n\"\n\"assoc teq2_grenade_explosion\\n\"\n\"}\\n\"\n\"r_part teq2_grenade_explosion\\n\"\n\"{\\n\"\n\"assoc std_explosion_particles\\n\"\n\"lighttime 0.5\\n\"\n\"lightradius 350\\n\"\n\"lightradiusfade 300\\n\"\n\"lightrgb 1.0 0.5 0.4\\n\"\n\"lightrgbfade 0.36 0.19 0.19\\n\"\n\"sound \\\"weapons/grenlx1a.wav\\\" 1 1 0 0\\n\"\n\"model \\\"models/objects/r_explode/tris.md2\\\" framestart=30 frames=19 skin=-1 fullbright noshadow\\n\"\n\"}\\n\"\n\"r_part teq2_grenade_explosion_water\\n\"\n\"{\\n\"\n\"assoc std_explosion_particles\\n\"\n\"lighttime 0.5\\n\"\n\"lightradius 350\\n\"\n\"lightradiusfade 300\\n\"\n\"lightrgb 1.0 0.5 0.4\\n\"\n\"lightrgbfade 0.36 0.19 0.19\\n\"\n\"sound \\\"weapons/xpld_wat.wav\\\" 1 1 0 0\\n\"\n\"model \\\"models/objects/r_explode/tris.md2\\\" framestart=30 frames=19 skin=-1 fullbright noshadow\\n\"\n\"}\\n\"\n\n\"r_part teq2_plain_explosion\\n\"\n\"{ //basically like regular explosions, but with no particle effect.\\n\"\n\"lighttime 0.5\\n\"\n\"lightradius 350\\n\"\n\"lightradiusfade 300\\n\"\n\"lightrgb 1.0 0.5 0.4\\n\"\n\"lightrgbfade 0.36 0.19 0.19\\n\"\n\"sound \\\"weapons/rocklx1a.wav\\\" 1 1 0 0\\n\"\n\"model \\\"models/objects/r_explode/tris.md2\\\" framestart=0 frames=15 skin=-1 fullbright noshadow\\n\"\n\"model \\\"models/objects/r_explode/tris.md2\\\" framestart=15 frames=15 skin=-1 fullbright noshadow\\n\"\n\"}\\n\"\n\"r_part teq2_plasma_explosion\\n\"\n\"{ //not actually any different\\n\"\n\"assoc teq2_explosion1\\n\"\n\"}\\n\"\n\n\"r_part teq2_tracker_explosion\\n\"\n\"{\\n\"\n\"texture \\\"classicparticle\\\"\\n\"\n\"tcoords 0 0 16 16 32\\n\"\n\"count 128\\n\"\n\"scale 1\\n\"\n\"alpha 1\\n\"\n\"die 1.5 2\\n\"\n\"randomvel 128\\n\"\n\"orgadd 0 31\\n\"\n\"spawnorg 16\\n\"\n\"gravity 40\\n\"\n\"scalefactor 0.8\\n\"\n\"colorindex 0 1\\n\"\n\"lighttime 0.1 //this is kinda too short.\\n\"\n\"lightradius 150\\n\"\n\"lightradiusfade 300\\n\"\n\"lightrgb -1.0 -1.0 -1.0\\n\"\n\"sound \\\"weapons/disrupthit.wav\\\" 1 1 0 0\\n\"\n\"}\\n\"\n\"r_part teq2_teleport_effect\\n\"\n\"{\\n\"\n\"texture \\\"classicparticle\\\"\\n\"\n\"tcoords 0 0 16 16 32\\n\"\n\"count 400\\n\"\n\"scale 1\\n\"\n\"alpha 1\\n\"\n\"die 0.5 0.8\\n\"\n\"orgadd 8 -48\\n\"\n\"veladd -100 -0\\n\"\n\"spawnmode circle\\n\"\n\"spawnorg 0 0\\n\"\n\"spawnvel -50 30\\n\"\n\"randomvel -32 -31\\n\"\n\"gravity 0\\n\"\n\"colorindex 15\\n\"\n\"scalefactor 0.8\\n\"\n\"}\\n\"\n\"r_part teq2_dball_goal\\n\"\n\"{  // same as teq2_teleport_effect\\n\"\n\"assoc teq2_teleport_effect\\n\"\n\"}\\n\"\n\"r_part teq2_widowsplash\\n\"\n\"{  // particle ball that slowly expands\\n\"\n\"texture \\\"classicparticle\\\"\\n\"\n\"tcoords 0 0 16 16 32\\n\"\n\"count 400\\n\"\n\"scale 1\\n\"\n\"alpha 1\\n\"\n\"die 3 3\\n\"\n\"veladd 10 10\\n\"\n\"spawnmode circle\\n\"\n\"spawnorg 0 0\\n\"\n\"randomvel -10 10\\n\"\n\"gravity 0\\n\"\n\"colorindex 104 7\\n\"\n\"scalefactor 0.8\\n\"\n\"}\\n\"\n\"r_part teq2_debugtrail\\n\"\n\"{\\n\"\n\"texture \\\"classicparticle\\\"\\n\"\n\"tcoords 0 0 16 16 32\\n\"\n\"scale 1\\n\"\n\"alpha 1\\n\"\n\"step 5\\n\"\n\"die 5 5\\n\"\n\"colorindex 111\\n\"\n\"}\\n\"\n\"r_part teq2_chainfist_smoke\\n\"\n\"{\\n\"\n\"texture \\\"classicparticle\\\"\\n\"\n\"tcoords 0 0 16 16 32\\n\"\n\"count 20\\n\"\n\"scale 1\\n\"\n\"alpha 1\\n\"\n\"die 0.5 0.8\\n\"\n\"spawnorg 2\\n\"\n\"randomvel 6.66 20\\n\"\n\"gravity 40\\n\"\n\"scalefactor 0.8\\n\"\n\"colorindex 0 7\\n\"\n\"}\\n\"\n\n\"r_part teq2_flashlight\\n\"\n\"{ //JUST a light.\\n\"\n\"lightradius 400\\n\"\n\"lighttime 0.15\\n\"\n\"lightrgb 1.0 1.0 1.0\\n\"\n\"lightshadows 1\\n\"\n\"}\\n\"\n\n\"r_part trq2_rocket\\n\"\n\"{\\n\"\n\"texture \\\"particles/quake\\\"\\n\"\n\"step 8\\n\"\n\"scale 4\\n\"\n\"die 1.0 1.2\\n\"\n\"colorindex 0xdc 3\\n\"\n\"spawnorg 1\\n\"\n\"spawnvel 20\\n\"\n\"gravity 40\\n\"\n\"assoc trq2_grenade\\n\"\n\"}\\n\"\n\"r_part trq2_grenade\\n\"\n\"{\\n\"\n\"texture \\\"particles/quake\\\"\\n\"\n\"step 3\\n\"\n\"scale 4\\n\"\n\"die 1.0 1.2\\n\"\n\"colorindex 0x4 7\\n\"\n\"spawnorg 1\\n\"\n\"spawnvel 5\\n\"\n\"gravity -20\\n\"\n\"}\\n\"\n\"r_part trq2_gib\\n\"\n\"{\\n\"\n\"texture \\\"particles/quake\\\"\\n\"\n\"step 3\\n\"\n\"scale 4\\n\"\n\"die 1.0 1.4\\n\"\n\"colorindex 0xe8 7\\n\"\n\"spawnorg 1\\n\"\n\"spawnvel 5\\n\"\n\"gravity -20\\n\"\n\"}\\n\"\n\"r_part trq2_greengib\\n\"\n\"{\\n\"\n\"texture \\\"particles/quake\\\"\\n\"\n\"step 3\\n\"\n\"scale 4\\n\"\n\"die 1.0 1.4\\n\"\n\"colorindex 0xdb 7\\n\"\n\"spawnorg 1\\n\"\n\"spawnvel 5\\n\"\n\"gravity -20\\n\"\n\"}\\n\"\n\n\"r_part TR_PLASMA\\n\"\n\"{\\n\"\n\"assoc TR_BLASTERTRAIL\\n\"\n\"}\\n\"\n\n\"r_part tr_ionripper\\n\"\n\"{\\n\"\n\"texture \\\"classicparticle\\\"\\n\"\n\"tcoords 0 0 16 16 32\\n\"\n\"step 3\\n\"\n\"scale 1\\n\"\n\"alpha 0.5\\n\"\n\"die 0.15 0.25\\n\"\n\"colorindex 0xe4 3\\n\"\n\"spawnmode tracer\\n\"\n\"spawnorg 0\\n\"\n\"spawnvel 10\\n\"\n\n\"lighttime 0\\n\"\n\"lightradius 100\\n\"\n\"lightrgb 1.0 0.5 0.5\\n\"\n\"}\\n\"\n\n\"r_part tr_tracker\\n\"\n\"{ //FIXME: doesn't match vanilla. works well enough though I guess.\\n\"\n\"texture \\\"classicparticle\\\"\\n\"\n\"tcoords 0 0 16 16 32\\n\"\n\"step 3\\n\"\n\"scale 1\\n\"\n\"alpha 1\\n\"\n\"die 0.5\\n\"\n\"spawnmode spiral 8.34\\n\"\n\"spawnorg 1\\n\"\n\"spawnvel 32\\n\"\n\"veladd 32\\n\"\n\"scalefactor 0\\n\"\n\"colorindex 0\\n\"\n\"lighttime 0\\n\"\n\"lightradius 200\\n\"\n\"lightrgb -1.0 -1.0 -1.0\\n\"\n\"}\\n\"\n\n\"r_part tr_tagtrail\\n\"\n\"{\\n\"\n\"texture \\\"classicparticle\\\"\\n\"\n\"tcoords 0 0 16 16 32\\n\"\n\"step 5\\n\"\n\"scale 1\\n\"\n\"alpha 1\\n\"\n\"die 1.0 0.8\\n\"\n\"spawnorg 16\\n\"\n\"spawnvel 5\\n\"\n\"scalefactor 0\\n\"\n\"colorindex 220\\n\"\n\"lighttime 0\\n\"\n\"lightradius 225\\n\"\n\"lightrgb 1.0 1.0 0.0\\n\"\n\"}\\n\"\n\n\"r_part tr_trap\\n\"\n\"{\\n\"\n\"texture \\\"classicparticle\\\"\\n\"\n\"tcoords 0 0 16 16 32\\n\"\n\"count 30\\n\"\n\"scale 1\\n\"\n\"alpha 1\\n\"\n\"die 0.4 0.8\\n\"\n\"randomvel 8\\n\"\n\"veladd 48\\n\"\n\"colorindex 0xe0 7\\n\"\n\"spawnmode telebox 0 4\\n\"\n\"spawnorg 8 16\\n\"\n\"lighttime 0\\n\"\n\"lightradius 100 200\\n\"\n\"lightrgb 1.0 0.8 0.25\\n\"\n\"}\\n\"\n\n\"r_part +tr_trap\\n\"\n\"{\\n\"\n\"texture \\\"classicparticle\\\"\\n\"\n\"tcoords 0 0 16 16 32\\n\"\n\"count 128\\n\"\n\"scale 1\\n\"\n\"alpha 1\\n\"\n\"die 0.4 0.8\\n\"\n\"randomvel 8\\n\"\n\"veladd 20\\n\"\n\"colorindex 58\\n\"\n\"spawnmode telebox 0 4\\n\"\n\"spawnorg 1 48\\n\"\n\"}\\n\"\n\n\"r_part ef_trap\\n\"\n\"{ //FIXME\\n\"\n\"placeholder\\n\"\n\"}\\n\"\n\n//flags do NOT use coronas, because it obscures the holding player's skin colour\n\"r_part tr_flag1\\n\"\n\"{\\n\"\n\"texture \\\"classicparticle\\\"\\n\"\n\"tcoords 0 0 16 16 32\\n\"\n\"step 5\\n\"\n\"scale 1\\n\"\n\"alpha 1\\n\"\n\"die 1.0 0.8\\n\"\n\"spawnorg 16\\n\"\n\"spawnvel 5\\n\"\n\"veladd 32\\n\"\n\"scalefactor 0\\n\"\n\"colorindex 0xf2\\n\"\n\"lighttime 0\\n\"\n\"lightcorona 0.0 0.0\\n\"\n\"lightradius 225\\n\"\n\"lightrgb 1.0 0.25 0.25\\n\"\n\"}\\n\"\n\"r_part tr_flag2\\n\"\n\"{\\n\"\n\"texture \\\"classicparticle\\\"\\n\"\n\"tcoords 0 0 16 16 32\\n\"\n\"step 5\\n\"\n\"scale 1\\n\"\n\"alpha 1\\n\"\n\"die 1.0 0.8\\n\"\n\"spawnorg 16\\n\"\n\"spawnvel 5\\n\"\n\"veladd 32\\n\"\n\"scalefactor 0\\n\"\n\"colorindex 0x73\\n\"\n\"lighttime 0\\n\"\n\"lightcorona 0.0 0.0\\n\"\n\"lightradius 225\\n\"\n\"lightrgb 0.25 0.25 1.0\\n\"\n\"}\\n\"\n\n\"r_part EF_FLIES\\n\"\n\"{\\n\"\n\"texture \\\"classicparticle\\\"\\n\"\n\"tcoords 0 0 16 16 32\\n\"\n\"count 1\\n\"\n\"scale 0.5\\n\"\n\"alpha 1\\n\"\n\"die 0\\n\"\n\"spawnmode syncfield 16 64\\n\"\n\"spawnorg 0\\n\"\n\"scalefactor 0\\n\"\n\"colorindex 0\\n\"\n\"}\\n\"\n\n\"r_part EF_BFGPARTICLES\\n\"\n\"{\\n\"\n\"texture \\\"classicparticle\\\"\\n\"\n\"tcoords 0 0 16 16 32\\n\"\n\"count 0 0 162\\n\"\n\"scale 0.5\\n\"\n\"alpha 1\\n\"\n\"die 0\\n\"\n\"spawnmode syncfield 16 64\\n\"\n\"spawnorg 0\\n\"\n\"scalefactor 0\\n\"\n\"colorindex 0xd0 7\\n\"\n\"}\\n\"\n\n\"r_part ev_item_respawn\\n\"\n\"{\\n\"\n\"sound \\\"items/respawn1.wav\\\" 1 2 0 0 1\\n\"\n\"texture \\\"classicparticle\\\"\\n\"\n\"tcoords 0 0 16 16 32\\n\"\n\"count 64\\n\"\n\"scale 1\\n\"\n\"alpha 1\\n\"\n\"die 1.3 1\\n\"\n\"randomvel 8\\n\"\n\"orgadd 0 31\\n\"\n\"spawnorg 8\\n\"\n\"gravity 8\\n\"\n\"scalefactor 0.8\\n\"\n\"colorindex 0xd4 3\\n\"\n\"}\\n\"\n\"r_part ev_player_teleport\\n\"\n\"{\\n\"\n\"sound \\\"misc/tele1.wav\\\" 1 2 0 0 1\\n\"\n\"texture \\\"classicparticle\\\"\\n\"\n\"tcoords 0 0 16 16 32\\n\"\n\"count 96\\n\"\n\"scale 1\\n\"\n\"alpha 1\\n\"\n\"die 0.3 0.4\\n\"\n\"randomvel 8\\n\"\n\"orgadd 0 31\\n\"\n\"spawnmode telebox 0 4\\n\"\n\"spawnorg 32 48\\n\"\n\"gravity 40\\n\"\n\"scalefactor 0.8\\n\"\n\"colorindex 0x07 7\\n\"\n\"}\\n\"\n\"r_part ev_footstep\\n\"\n\"{\\n\"\n\"sound \\\"player/step1.wav\\\" 1 1 0 0 1\\n\"\n\"sound \\\"player/step2.wav\\\" 1 1 0 0 1\\n\"\n\"sound \\\"player/step3.wav\\\" 1 1 0 0 1\\n\"\n\"sound \\\"player/step4.wav\\\" 1 1 0 0 1\\n\"\n\"}\\n\"\n\"r_part ev_other_footstep\\n\"\n\"{ //q2e - same but with idle attenuation\\n\"\n\"sound \\\"player/step1.wav\\\" 1 2 0 0 1\\n\"\n\"sound \\\"player/step2.wav\\\" 1 2 0 0 1\\n\"\n\"sound \\\"player/step3.wav\\\" 1 2 0 0 1\\n\"\n\"sound \\\"player/step4.wav\\\" 1 2 0 0 1\\n\"\n\"}\\n\"\n\n//central explosion\n\"r_part teq2_bfg_bigexplosion\\n\"\n\"{\\n\"\n\"texture \\\"classicparticle\\\"\\n\"\n\"tcoords 0 0 16 16 32\\n\"\n\"count 256\\n\"\n\"scale 1\\n\"\n\"alpha 1\\n\"\n\"die 0.625 1\\n\"\n\"spawnmode ball\\n\"\n\"spawnorg 16\\n\"\n\"spawnvel 192\\n\"\n\"scalefactor 0.8\\n\"\n\"gravity 40\\n\"\n\"colorindex 0xd0 7\\n\"\n\"}\\n\"\n\n//splashed onto an entity\n\"r_part teq2_bfg_explosion\\n\"\n\"{\\n\"\n\"lighttime 0.5\\n\"\n\"lightradius 350\\n\"\n\"lightradiusfade 300\\n\"\n\"lightrgb 0.0 1.0 0.0\\n\"\n\"lightrgbfade 0.0 0.0 0.0\\n\"\n\"sound \\\"weapons/xpld_wat.wav\\\" 1 1 0 0\\n\"\n\"model \\\"sprites/s_bfg2.sp2\\\" framestart=0 frameend=4 alpha=0.3 transparent fullbright noshadow\\n\"\n\"}\\n\"\n\n\n//31qu cylinder, 8-98 high\n//should look like its sucked up into some thingie above\n\"r_part TEQ2_BOSSTPORT\\n\"\n\"{\\n\"\n\"texture \\\"classicparticle\\\"\\n\"\n\"tcoords 0 0 16 16 32\\n\"\n\"count 800\\n\"\n\"scale 1\\n\"\n\"alpha 1\\n\"\n\"die 0.5 0.8\\n\"\n\"orgadd 8 -98\\n\"\n\"veladd 100 200\\n\"\n\"spawnmode circle\\n\"\n\"spawnorg 48 0\\n\"\n\"spawnvel -50 30\\n\"\n\"randomvel 32 31\\n\"\n\"gravity -800\\n\"\n\"rgbf 1 1 1\\n\"\n\"scalefactor 0.8\\n\"\n\"sound \\\"misc/bigtele.wav\\\" 1 0 0 0 1\\n\"\n\"}\\n\"\n\n\"r_part teq2_heatbeam_sparks\\n\"\n\"{\\n\"\n\"texture \\\"classicparticle\\\"\\n\"\n\"tcoords 0 0 16 16 32\\n\"\n\"count 30\\n\"\n\"scale 1\\n\"\n\"alpha 1\\n\"\n\"die 0.3 0.8\\n\"\n\"veladd 64\\n\"\n\"orgadd 0 16\\n\"\n\"spawnorg 4\\n\"\n\"gravity 40\\n\"\n\"scalefactor 0.8\\n\"\n\"colorindex 9 7\\n\"\n\"}\\n\"\n\"r_part teq2_heatbeam_steam\\n\"\n\"{\\n\"\n\"texture \\\"classicparticle\\\"\\n\"\n\"tcoords 0 0 16 16 32\\n\"\n\"count 30\\n\"\n\"scale 1\\n\"\n\"alpha 1\\n\"\n\"die 0.2 0.4\\n\"\n\"randomvel 40\\n\"\n\"orgadd 0 15\\n\"\n\"veladd 30\\n\"\n\"spawnorg 4\\n\"\n\"gravity 40\\n\"\n\"scalefactor 0.8\\n\"\n\"colorindex 0xe0 7\\n\"\n\"}\\n\"\n\n//r_part teq2_heatbeam_steam\n//{\n//\tcount 20\n//\tcolorindex 0xe0 7\n////\tmagnitude 60\n//\ttexture \"classicparticle\"\n//\ttcoords 0 0 16 16 32\n//\tscale 1\n//\talpha 1\n//\tdie 0.3 0.8\n//\trandomvel 20 magnitude/3\n//\tveladd magnitude\n//\torgadd magnitude/10\n//\tspawnorg 4\n//\tgravity -400\n//\tscalefactor 0.8\n//}\n\n\n//this is apparently just a trail effect (palette index specified by netcode)\n\"r_part teq2_forcewall\\n\"\n\"{\\n\"\n\"texture \\\"classicparticle\\\"\\n\"\n\"tcoords 0 0 16 16 32\\n\"\n\"scale 0.5\\n\"\n\"alpha 1\\n\"\n\"scalefactor 0.8\\n\"\n\"step 5\\n\"\n\"spawnorg 3\\n\"\n\"randomvel 5\\n\"\n\"die 3 3.5\\n\"\n\"}\\n\"\n\n\"r_part teq2ex_bluehyperblaster_puff\\n\"\n\"{\\n\"\n\"count 0 0 1\\n\"\n\"model \\\"models/objects/explode/tris.md2\\\" framestart=0 frameend=4 framerate=10 alpha=1 orient additive fullbright noshadow skin=1\\n\"\n\"}\\n\"\n\n\"r_part teq2ex_bluehyperblaster\\n\"\n\"{\\n\"\n\"texture \\\"classicparticle\\\"\\n\"\n\"tcoords 0 0 16 16 32\\n\"\n\"count 60\\n\"\n\"scale 1\\n\"\n\"alpha 1\\n\"\n\"die 0.3 0.8\\n\"\n\"randomvel 40\\n\"\n\"orgadd 0 15\\n\"\n\"veladd 30\\n\"\n\"spawnorg 4\\n\"\n\"gravity 40\\n\"\n\"scalefactor 0.8\\n\"\n\"colorindex 111 108\\n\"\n\"assoc teq2ex_bluehyperblaster_puff\\n\"\n\"lightradius 150\\n\"\n\"lightradiusfade 400\\n\"\n\"lightrgb 0 0 1\\n\"\n\"lightshadows 1\\n\"\n\"sound \\\"weapons/lashit.wav\\\" 1 1 0 0\\n\"\n\"}\\n\"\n\n\"r_part teq2ex_bfgzap_end\\n\"\n\"{\\n\"\n\"assoc teq2_bfg_explosion\\n\"\n\"}\\n\"\n\n\"r_part teq2ex_bfgzap\\n\"\n\"{ // green bolt-beam with small green explosion sprite at the end\\n\"\n\"texture \\\"particles/fteparticlefont.tga\\\"\\n\"\n\"tcoords 97 97 191 191 256\\n\"\n\"scale 4\\n\"\n\"alpha 1.0\\n\"\n\"step 4\\n\"\n\"randomvel 0\\n\"\n\"type beam\\n\"\n\"die 1\\n\"\n\"colorindex 0xd0\\n\"\n\"}\\n\"\n\n\"r_part teq2ex_berserk_slampuff\\n\"\n\"{\\n\"\n\"count 0 0 1\\n\"\n\"model \\\"models/objects/explode/tris.md2\\\" framestart=0 frameend=4 framerate=10 alpha=1 orient additive fullbright noshadow skin=3\\n\"\n\"}\\n\"\n\"r_part teq2ex_berserk_slam\\n\"\n\"{\\n\"\n\"texture \\\"classicparticle\\\"\\n\"\n\"tcoords 0 0 16 16 32\\n\"\n\"count 256\\n\"\n\"scale 1\\n\"\n\"alpha 1\\n\"\n\"die 0.625 1\\n\"\n\"spawnmode ball\\n\"\n\"spawnorg 16\\n\"\n\"spawnvel 192\\n\"\n\"scalefactor 0.8\\n\"\n\"gravity 40\\n\"\n\"colorindex 111 108\\n\"\n\"assoc teq2ex_berserk_slampuff\\n\"\n\"}\\n\"\n\"r_part teq2ex_grapple_cable_2\\n\"\n\"{\\n\"\n\"texture \\\"particles/fteparticlefont.tga\\\"\\n\"\n\"tcoords 97 97 191 191 256\\n\"\n\"scale 2\\n\"\n\"alpha 1.0\\n\"\n\"step 4\\n\"\n\"randomvel 0\\n\"\n\"type beam\\n\"\n\"die 0.25\\n\"\n\"colorindex 6\\n\"\n\"}\\n\"\n\"r_part teq2ex_power_splash\\n\"\n\"{\\n\"\n\"texture \\\"classicparticle\\\"\\n\"\n\"tcoords 0 0 16 16 32\\n\"\n\"count 256\\n\"\n\"scale 4\\n\"\n\"alpha 0.4\\n\"\n\"die 1 0.625\\n\"\n\"spawnmode ball\\n\"\n\"spawnorg 16\\n\"\n\"spawnvel 192\\n\"\n\"scalefactor 0.8\\n\"\n\"gravity 40\\n\"\n\"colorindex 208 209\\n\"\n\"}\\n\"\n\"r_part teq2ex_lightning_beam\\n\"\n\"{\\n\"\n\"texture \\\"particles/fteparticlefont.tga\\\"\\n\"\n\"tcoords 97 97 191 191 256\\n\"\n\"scale 2\\n\"\n\"alpha 1.0\\n\"\n\"step 4\\n\"\n\"randomvel 0\\n\"\n\"type beam\\n\"\n\"die 0.25\\n\"\n\"colorindex 108\\n\"\n\"}\\n\"\n\"r_part teq2ex_explosion1_nl\\n\"\n\"{ // the _nl stands for 'no light'\\n\"\n\"assoc std_explosion_particles\\n\"\n\"sound \\\"weapons/rocklx1a.wav\\\" 1 1 0 0\\n\"\n\"model \\\"models/objects/r_explode/tris.md2\\\" framestart=0 frames=15 skin=-1 fullbright noshadow\\n\"\n\"}\\n\"\n\"r_part teq2ex_explosion2_nl\\n\"\n\"{ // ditto\\n\"\n\"assoc std_explosion_particles\\n\"\n\"sound \\\"weapons/rocklx1a.wav\\\" 1 1 0 0\\n\"\n\"model \\\"models/objects/r_explode/tris.md2\\\" framestart=15 frames=15 skin=-1 fullbright noshadow\\n\"\n\"}\\n\"\n\"r_part ef_trackershell\\n\"\n\"{ // the black glob model thing\\n\"\n\"model \\\"models/objects/r_explode/tris.md2\\\" framestart=30 frames=19 skin=-1 fullbright noshadow\\n\"\n\"}\\n\"\n\"r_part ev_ladder_step\\n\"\n\"{\\n\"\n\"sound \\\"player/steps/ladder1.wav\\\" 1 1 0 0 1\\n\"\n\"sound \\\"player/steps/ladder2.wav\\\" 1 1 0 0 1\\n\"\n\"sound \\\"player/steps/ladder3.wav\\\" 1 1 0 0 1\\n\"\n\"sound \\\"player/steps/ladder4.wav\\\" 1 1 0 0 1\\n\"\n\"sound \\\"player/steps/ladder5.wav\\\" 1 1 0 0 1\\n\"\n\"}\\n\"\n;\n#endif\n\n\n\n//////////////////////////////////////////////////////\n\n\nchar *particle_set_tsshaft =\n// TE_LIGHTNING2 replacement, (c) 2005 TimeServ\n// If you steal this GPLed code you will be violating several international laws\n// as well as several laws of physics.\n\"r_part tlightningflash\\n\"\n\"{\\n\"\n\"spawntime 0.1\\n\"\n\"spawnchance 0.1\\n\"\n\"die 0.25\\n\"\n\"type beam\\n\"\n\"alpha 1\\n\"\n\"step 80\\n\"\n\"scale 14\\n\"\n\"scaledelta -52\\n\"\n\"rgb 255 255 255\\n\"\n\"spawnmode distball\\n\"\n\"spawnorg 16\\n\"\n\"spawnparam1 0.5\\n\"\n\"averageout\\n\"\n\"nospreadfirst\\n\"\n\"blend add\\n\"\n\"}\\n\"\n\n\"r_part tlightningglow\\n\"\n\"{\\n\"\n\"step 50\\n\"\n\"scale 35\\n\"\n\"scalefactor 1\\n\"\n\"alpha 1\\n\"\n\"die 0\\n\"\n\"rgb 1 1 8\\n\"\n\"blend add\\n\"\n\"assoc tlightningflash\\n\"\n\"}\\n\"\n\n\"r_part tlightningfade\\n\"\n\"{\\n\"\n\"spawntime 0.05\\n\"\n\"die 0.2\\n\"\n\"type beam\\n\"\n\"alpha 2\\n\"\n\"step 96\\n\"\n\"scale 1.5\\n\"\n\"rgb 16 16 64\\n\"\n\"spawnmode distball\\n\"\n\"spawnorg 9\\n\"\n\"spawnparam1 0.9\\n\"\n\"blend add\\n\"\n\"averageout\\n\"\n\"nospreadfirst\\n\"\n\"assoc tlightningglow\\n\"\n\"}\\n\"\n\n\"r_part te_lightning2\\n\"\n\"{\\n\"\n\"die 0\\n\"\n\"type beam\\n\"\n\"alpha 2\\n\"\n\"step 96\\n\"\n\"scale 4\\n\"\n\"rgb 196 196 255\\n\"\n\"spawnmode distball\\n\"\n\"spawnorg 9\\n\"\n\"spawnparam1 0.9\\n\"\n\"blend add\\n\"\n\"averageout\\n\"\n\"nospreadfirst\\n\"\n\"assoc tlightningfade\\n\"\n\"}\\n\"\n\n\"r_part lbolttrail\\n\"\n\"{\\n\"\n\"die 0.5\\n\"\n\"type beam\\n\"\n\"alpha 2\\n\"\n\"step 32\\n\"\n\"scale 1\\n\"\n\"rgb 196 196 255\\n\"\n\"rgbdelta -512 -512 -128\\n\"\n\"spawnmode distball\\n\"\n\"spawnorg 5\\n\"\n\"spawnvel 4\\n\"\n\"spawnparam1 0.5\\n\"\n\"blend add\\n\"\n\"}\\n\"\n\n\"r_part lbolt\\n\"\n\"{\\n\"\n\"die 0.5\\n\"\n\"count 1\\n\"\n\"spawnmode circle\\n\"\n\"spawnvel 2000\\n\"\n\"spawnorg 1\\n\"\n\"emit lbolttrail\\n\"\n\"emitinterval -1\\n\"\n\"}\\n\"\n\n\"r_part lemit\\n\"\n\"{\\n\"\n\"die 0.1\\n\"\n\"count 1\\n\"\n\"spawnchance 1\\n\"\n\"emit lbolt\\n\"\n\"emitinterval 100\\n\"\n\"spawnchance 0.1\\n\"\n\"cliptype lemit\\n\"\n\"clipcount 1\\n\"\n\"clipbounce 0\\n\"\n\"}\\n\"\n\n\"r_part lflash\\n\"\n\"{\\n\"\n\"die 0.1\\n\"\n\"texture \\\"particles/lflash\\\"\\n\"\n\"count 1\\n\"\n\"alpha 1\\n\"\n\"scale 100\\n\"\n\"scalefactor 1\\n\"\n\"scaledelta -500\\n\"\n\"rgb 255 255 255\\n\"\n\"blend add\\n\"\n\"assoc lemit\\n\"\n\"}\\n\"\n\n\"r_part te_lightning2_end\\n\"\n\"{\\n\"\n\"die 0.3\\n\"\n\"alpha 1\\n\"\n\"count 8\\n\"\n\"scale 2\\n\"\n\"rgb 128 128 255\\n\"\n\"rgbrand 63 63 0\\n\"\n\"rgbrandsync 1\\n\"\n\"spawnvel 100\\n\"\n\"spawnorg 5\\n\"\n\"blend add\\n\"\n\"assoc lflash\\n\"\n\"}\\n\"\n;\n#endif\n"
  },
  {
    "path": "engine/client/r_partset.h",
    "content": "/*\nWARNING: THIS FILE IS GENERATED BY 'generatebuiltin.c'.\nYOU SHOULD NOT EDIT THIS FILE BY HAND\n*/\n\n#ifndef QUAKETC\nextern char *particle_set_spikeset;\n#define R_PARTSET_BUILTINS_spikeset {\"spikeset\", &particle_set_spikeset},\nextern char *particle_set_faithful;\n#define R_PARTSET_BUILTINS_faithful {\"faithful\", &particle_set_faithful},\nextern char *particle_set_highfps;\n#define R_PARTSET_BUILTINS_highfps {\"highfps\", &particle_set_highfps},\nextern char *particle_set_high;\n#define R_PARTSET_BUILTINS_high {\"high\", &particle_set_high},\nextern char *particle_set_minimal;\n#define R_PARTSET_BUILTINS_minimal {\"minimal\", &particle_set_minimal},\n#ifdef HEXEN2\nextern char *particle_set_h2part;\n#define R_PARTSET_BUILTINS_h2part {\"h2part\", &particle_set_h2part},\n#else\n#define R_PARTSET_BUILTINS_h2part\n#endif\n#ifdef Q2CLIENT\nextern char *particle_set_q2part;\n#define R_PARTSET_BUILTINS_q2part {\"q2part\", &particle_set_q2part},\n#else\n#define R_PARTSET_BUILTINS_q2part\n#endif\nextern char *particle_set_tsshaft;\n#define R_PARTSET_BUILTINS_tsshaft {\"tsshaft\", &particle_set_tsshaft},\n#define R_PARTSET_BUILTINS R_PARTSET_BUILTINS_spikeset R_PARTSET_BUILTINS_faithful R_PARTSET_BUILTINS_highfps R_PARTSET_BUILTINS_high R_PARTSET_BUILTINS_minimal R_PARTSET_BUILTINS_h2part R_PARTSET_BUILTINS_q2part R_PARTSET_BUILTINS_tsshaft \n#endif\n"
  },
  {
    "path": "engine/client/r_surf.c",
    "content": "/*\nCopyright (C) 1996-1997 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n*/\n// r_surf.c: surface-related refresh code\n\n#include \"quakedef.h\"\n#ifndef SERVERONLY\n#include \"glquake.h\"\n#include \"shader.h\"\n#include \"renderque.h\"\n#include \"com_mesh.h\"\n#include <math.h>\n\n#if (defined(GLQUAKE) || defined(VKQUAKE)) && defined(MULTITHREAD)\n#define THREADEDWORLD\nint webo_blocklightmapupdates;\t//0 no webo, &1=using threadedworld, &2=already uploaded. so update when !=3\n#else\n#define webo_blocklightmapupdates 0\n#endif\n#ifdef BEF_PUSHDEPTH\nqboolean r_pushdepth;\n#endif\nqboolean r_dlightlightmaps; //updated each frame, says whether to do lightmap hack dlights.\n\nextern cvar_t\t\tr_ambient;\n\nmodel_t\t\t\t\t*currentmodel;\n\nstatic size_t\t\tmaxblocksize;\nstatic vec3_t\t\t*blocknormals;\nstatic unsigned\t\t*blocklights;\n\nlightmapinfo_t **lightmap;\nint numlightmaps;\nextern const float rgb9e5tab[32];\n\nextern cvar_t r_stains;\nextern cvar_t r_loadlits;\nextern cvar_t r_stainfadetime;\nextern cvar_t r_stainfadeammount;\nextern cvar_t r_lightmap_nearest;\nextern cvar_t r_lightmap_format;\n\ndouble r_loaderstalltime;\n\nextern int r_dlightframecount;\n\nstatic void Surf_FreeLightmap(lightmapinfo_t *lm);\n\nstatic int lightmap_shift;\nint Surf_LightmapShift (model_t *model)\n{\n\textern cvar_t gl_overbright_all, gl_overbright;\n\n\tif (gl_overbright_all.ival || (model->engineflags & MDLF_NEEDOVERBRIGHT))\n\t\tlightmap_shift = bound(0, gl_overbright.ival, 2);\n\telse\n\t\tlightmap_shift = 0;\n\treturn lightmap_shift;\n}\n\nvoid QDECL Surf_RebuildLightmap_Callback (struct cvar_s *var, char *oldvalue)\n{\n\tMod_RebuildLightmaps();\n}\n\n//radius, x y z, r g b\nvoid Surf_StainSurf (model_t *mod, msurface_t *surf, float *parms)\n{\n\tint\t\t\tsd, td;\n\tfloat\t\tdist, rad, minlight;\n\tfloat change;\n\tvec3_t\t\timpact, local;\n\tint\t\t\ts, t;\n\tint\t\t\ti;\n\tint\t\t\tsmax, tmax;\n\tfloat amm;\n\tint lim;\n\tvec4_t\t*lmvecs;\n\tfloat\t*lmvecscale;\n\tstmap *stainbase;\n\tlightmapinfo_t *lm;\n\n\tlim = 255 - (r_stains.value*255);\n\n#define stain(x)\t\t\t\t\t\t\t\\\n\tchange = stainbase[(s)*3+x] + amm*parms[4+x];\t\\\n\tstainbase[(s)*3+x] = bound(lim, change, 255);\n\n\tif (surf->lightmaptexturenums[0] < 0)\n\t\treturn;\n\tlm = lightmap[surf->lightmaptexturenums[0]];\n\n\tsmax = (surf->extents[0]>>surf->lmshift)+1;\n\ttmax = (surf->extents[1]>>surf->lmshift)+1;\n\tif (mod->facelmvecs)\n\t\tlmvecs = mod->facelmvecs[surf-mod->surfaces].lmvecs, lmvecscale = mod->facelmvecs[surf-mod->surfaces].lmvecscale;\n\telse\n\t\tlmvecs = surf->texinfo->vecs, lmvecscale = surf->texinfo->vecscale;\n\n\tstainbase = lm->stainmaps;\n\tstainbase += (surf->light_t[0] * lm->width + surf->light_s[0]) * 3;\n\n\trad = *parms;\n\tdist = DotProduct ((parms+1), surf->plane->normal) - surf->plane->dist;\n\trad -= fabs(dist);\n\tminlight = 0;\n\tif (rad < minlight)\t//not hit\n\t\treturn;\n\tminlight = rad - minlight;\n\n\tfor (i=0 ; i<3 ; i++)\n\t{\n\t\timpact[i] = (parms+1)[i] - surf->plane->normal[i]*dist;\n\t}\n\n\tlocal[0] = DotProduct (impact, lmvecs[0]) + lmvecs[0][3];\n\tlocal[1] = DotProduct (impact, lmvecs[1]) + lmvecs[1][3];\n\n\tlocal[0] -= surf->texturemins[0];\n\tlocal[1] -= surf->texturemins[1];\n\n\tfor (t = 0 ; t<tmax ; t++)\n\t{\n\t\ttd = (local[1] - (t<<surf->lmshift))*lmvecscale[1];\n\t\tif (td < 0)\n\t\t\ttd = -td;\n\t\tfor (s=0 ; s<smax ; s++)\n\t\t{\n\t\t\tsd = (local[0] - (s<<surf->lmshift))*lmvecscale[0];\n\t\t\tif (sd < 0)\n\t\t\t\tsd = -sd;\n\t\t\tif (sd > td)\n\t\t\t\tdist = sd + (td>>1);\n\t\t\telse\n\t\t\t\tdist = td + (sd>>1);\n\t\t\tif (dist < minlight)\n\t\t\t{\n\t\t\t\tamm = (rad - dist);\n\t\t\t\tstain(0);\n\t\t\t\tstain(1);\n\t\t\t\tstain(2);\n\n\t\t\t\tsurf->stained = true;\n\t\t\t}\n\t\t}\n\t\tstainbase += 3*lm->width;\n\t}\n\n\tif (surf->stained)\n\t\tsurf->cached_dlight=-1;\n}\n\n//combination of R_AddDynamicLights and R_MarkLights\n/*\nstatic void Surf_StainNode (mnode_t *node, float *parms)\n{\n\tmplane_t\t*splitplane;\n\tfloat\t\tdist;\n\tmsurface_t\t*surf;\n\tint\t\t\ti;\n\n\tif (node->contents < 0)\n\t\treturn;\n\n\tsplitplane = node->plane;\n\tdist = DotProduct ((parms+1), splitplane->normal) - splitplane->dist;\n\n\tif (dist > (*parms))\n\t{\n\t\tSurf_StainNode (node->children[0], parms);\n\t\treturn;\n\t}\n\tif (dist < (-*parms))\n\t{\n\t\tSurf_StainNode (node->children[1], parms);\n\t\treturn;\n\t}\n\n// mark the polygons\n\tsurf = cl.worldmodel->surfaces + node->firstsurface;\n\tfor (i=0 ; i<node->numsurfaces ; i++, surf++)\n\t{\n\t\tif (surf->flags&~(SURF_DONTWARP|SURF_PLANEBACK))\n\t\t\tcontinue;\n\t\tSurf_StainSurf(surf, parms);\n\t}\n\n\tSurf_StainNode (node->children[0], parms);\n\tSurf_StainNode (node->children[1], parms);\n}\n*/\n\nvoid Surf_AddStain(vec3_t org, float red, float green, float blue, float radius)\n{\n\tphysent_t *pe;\n\tint i;\n\n\tfloat parms[7];\n\tif (!cl.worldmodel || cl.worldmodel->loadstate != MLS_LOADED || r_stains.value <= 0)\n\t\treturn;\n\tparms[0] = radius;\n\tparms[1] = org[0];\n\tparms[2] = org[1];\n\tparms[3] = org[2];\n\tparms[4] = red;\n\tparms[5] = green;\n\tparms[6] = blue;\n\n\n\tcl.worldmodel->funcs.StainNode(cl.worldmodel, parms);\n\n\t//now stain inline bsp models other than world.\n\n\tfor (i=1 ; i< pmove.numphysent ; i++)\t//0 is world...\n\t{\n\t\tpe = &pmove.physents[i];\n\t\tif (pe->model && pe->model->surfaces == cl.worldmodel->surfaces && pe->model->loadstate == MLS_LOADED)\n\t\t{\n\t\t\tparms[1] = org[0] - pe->origin[0];\n\t\t\tparms[2] = org[1] - pe->origin[1];\n\t\t\tparms[3] = org[2] - pe->origin[2];\n\n\t\t\tif (pe->angles[0] || pe->angles[1] || pe->angles[2])\n\t\t\t{\n\t\t\t\tvec3_t f, r, u, temp;\n\t\t\t\tAngleVectors(pe->angles, f, r, u);\n\t\t\t\tVectorCopy((parms+1), temp);\n\t\t\t\tparms[1] = DotProduct(temp, f);\n\t\t\t\tparms[2] = -DotProduct(temp, r);\n\t\t\t\tparms[3] = DotProduct(temp, u);\n\t\t\t}\n\n\n\t\t\tpe->model->funcs.StainNode(pe->model, parms);\n\t\t}\n\t}\n}\n\nvoid Surf_WipeStains(void)\n{\n\tint i;\n\tfor (i = 0; i < numlightmaps; i++)\n\t{\n\t\tif (!lightmap[i])\n\t\t\tbreak;\n\t\tif (lightmap[i]->stainmaps)\n\t\t\tmemset(lightmap[i]->stainmaps, 255, lightmap[i]->width*lightmap[i]->height*3*sizeof(stmap));\n\t}\n}\n\nvoid Surf_LessenStains(void)\n{\n\tint i;\n\tmsurface_t\t*surf;\n\n\tint\t\t\tsmax, tmax;\n\tint\t\t\ts, t;\n\tstmap *stain;\n\tint stride;\n\tint ammount;\n\tint limit;\n\tlightmapinfo_t *lm;\n\n\tstatic float time;\n\n\tif (!r_stains.value || !r_stainfadeammount.value)\n\t\treturn;\n\n\ttime += host_frametime;\n\tif (time < r_stainfadetime.value)\n\t\treturn;\n\ttime-=r_stainfadetime.value;\n\n\tammount = r_stainfadeammount.value;\n\tlimit = 255 - ammount;\n\n\tsurf = cl.worldmodel->surfaces;\n\tfor (i=0 ; i<cl.worldmodel->numsurfaces ; i++, surf++)\n\t{\n\t\tif (surf->stained)\n\t\t{\n\t\t\tlm = lightmap[surf->lightmaptexturenums[0]];\n\n\t\t\tsurf->cached_dlight=-1;//nice hack here...\n\n\t\t\tsmax = (surf->extents[0]>>surf->lmshift)+1;\n\t\t\ttmax = (surf->extents[1]>>surf->lmshift)+1;\n\n\t\t\tstain = lm->stainmaps;\n\t\t\tstain += (surf->light_t[0] * lm->width + surf->light_s[0]) * 3;\n\n\t\t\tstride = (lm->width-smax)*3;\n\n\t\t\tsurf->stained = false;\n\n\t\t\tsmax*=3;\n\n\t\t\tfor (t = 0 ; t<tmax ; t++, stain+=stride)\n\t\t\t{\n\t\t\t\tfor (s=0 ; s<smax ; s++)\n\t\t\t\t{\n\t\t\t\t\tif (*stain < limit)\t//eventually decay to 255\n\t\t\t\t\t{\n\t\t\t\t\t\t*stain += ammount;\n\t\t\t\t\t\tsurf->stained=true;\n\t\t\t\t\t}\n\t\t\t\t\telse\t//reset to 255\n\t\t\t\t\t\t*stain = 255;\n\n\t\t\t\t\tstain++;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n/*\n===============\nR_AddDynamicLights\n===============\n*/\nstatic void Surf_AddDynamicLights_Lum (msurface_t *surf)\n{\n\tsize_t\t\tlnum;\n\tint\t\t\tsd, td;\n\tfloat\t\tdist, rad, minlight;\n\tvec3_t\t\timpact, local;\n\tint\t\t\ts, t;\n\tint\t\t\ti;\n\tint\t\t\tsmax, tmax;\n\tfloat l;\n\tunsigned\t*bl;\n\tvec4_t\t\t*lmvecs;\n\tfloat\t\t*lmvecscale;\n\n\tsmax = (surf->extents[0]>>surf->lmshift)+1;\n\ttmax = (surf->extents[1]>>surf->lmshift)+1;\n\tif (currentmodel->facelmvecs)\n\t\tlmvecs = currentmodel->facelmvecs[surf-currentmodel->surfaces].lmvecs, lmvecscale = currentmodel->facelmvecs[surf-currentmodel->surfaces].lmvecscale;\n\telse\n\t\tlmvecs = surf->texinfo->vecs, lmvecscale = surf->texinfo->vecscale;\n\tfor (lnum=rtlights_first; lnum<RTL_FIRST; lnum++)\n\t{\n\t\tif ( !(surf->dlightbits & ((dlightbitmask_t)1u<<lnum) ) )\n\t\t\tcontinue;\t\t// not lit by this light\n\n\t\tif (!(cl_dlights[lnum].flags & LFLAG_LIGHTMAP))\n\t\t\tcontinue;\n\n\t\trad = cl_dlights[lnum].radius;\n\t\tdist = DotProduct (cl_dlights[lnum].origin, surf->plane->normal) -\n\t\t\t\tsurf->plane->dist;\n\t\trad -= fabs(dist);\n\t\tminlight = cl_dlights[lnum].minlight;\n\t\tif (rad < minlight)\n\t\t\tcontinue;\n\t\tminlight = rad - minlight;\n\n\t\tfor (i=0 ; i<3 ; i++)\n\t\t{\n\t\t\timpact[i] = cl_dlights[lnum].origin[i] -\n\t\t\t\t\tsurf->plane->normal[i]*dist;\n\t\t}\n\n\t\tlocal[0] = DotProduct (impact, lmvecs[0]) + lmvecs[0][3];\n\t\tlocal[1] = DotProduct (impact, lmvecs[1]) + lmvecs[1][3];\n\n\t\tlocal[0] -= surf->texturemins[0];\n\t\tlocal[1] -= surf->texturemins[1];\n\n\t\tl = 256*(cl_dlights[lnum].color[0]*NTSC_RED + cl_dlights[lnum].color[1]*NTSC_GREEN + cl_dlights[lnum].color[2]*NTSC_BLUE);\n\n\t\tbl = blocklights;\n\t\tfor (t = 0 ; t<tmax ; t++)\n\t\t{\n\t\t\ttd = (local[1] - (t<<surf->lmshift))*lmvecscale[1];\n\t\t\tif (td < 0)\n\t\t\t\ttd = -td;\n\t\t\tfor (s=0 ; s<smax ; s++)\n\t\t\t{\n\t\t\t\tsd = (local[0] - (s<<surf->lmshift))*lmvecscale[0];\n\t\t\t\tif (sd < 0)\n\t\t\t\t\tsd = -sd;\n\t\t\t\tif (sd > td)\n\t\t\t\t\tdist = sd + (td>>1);\n\t\t\t\telse\n\t\t\t\t\tdist = td + (sd>>1);\n\t\t\t\tif (dist < minlight)\n\t\t\t\t\tbl[0] += (rad - dist)*l;\n\t\t\t\tbl++;\n\t\t\t}\n\t\t}\n\t}\n}\n\n/*\nstatic void Surf_AddDynamicLightNorms (msurface_t *surf)\n{\n\tint\t\t\tlnum;\n\tint\t\t\tsd, td;\n\tfloat\t\tdist, rad, minlight;\n\tvec3_t\t\timpact, local;\n\tint\t\t\ts, t;\n\tint\t\t\ti;\n\tint\t\t\tsmax, tmax;\n\tvec4_t\t\t*lmvecs;\n\tfloat\t\t*lmvecscale;\n\tfloat a;\n\n\tsmax = (surf->extents[0]>>4)+1;\n\ttmax = (surf->extents[1]>>4)+1;\n\ttex = surf->texinfo;\n\n\tfor (lnum=rtlights_first; lnum<RTL_FIRST; lnum++)\n\t{\n\t\tif ( !(surf->dlightbits & ((dlightbitmask_t)1u<<lnum) ) )\n\t\t\tcontinue;\t\t// not lit by this light\n\n\t\tif (!(cl_dlights[lnum].flags & LFLAG_ALLOW_LMHACK))\n\t\t\tcontinue;\n\n\t\trad = cl_dlights[lnum].radius;\n\t\tdist = DotProduct (cl_dlights[lnum].origin, surf->plane->normal) -\n\t\t\t\tsurf->plane->dist;\n\t\trad -= fabs(dist);\n\t\tminlight = cl_dlights[lnum].minlight;\n\t\tif (rad < minlight)\n\t\t\tcontinue;\n\t\tminlight = rad - minlight;\n\n\t\tfor (i=0 ; i<3 ; i++)\n\t\t{\n\t\t\timpact[i] = cl_dlights[lnum].origin[i] -\n\t\t\t\t\tsurf->plane->normal[i]*dist;\n\t\t}\n\n\t\tlocal[0] = DotProduct (impact, lmvecs[0]) + lmvecs[0][3];\n\t\tlocal[1] = DotProduct (impact, lmvecs[1]) + lmvecs[1][3];\n\n\t\tlocal[0] -= surf->texturemins[0];\n\t\tlocal[1] -= surf->texturemins[1];\n\n\t\ta = 256*(cl_dlights[lnum].color[0]*NTSC_RED + cl_dlights[lnum].color[1]*NTSC_GREEN + cl_dlights[lnum].color[2]*NTSC_BLUE);\n\n\t\tfor (t = 0 ; t<tmax ; t++)\n\t\t{\n\t\t\ttd = (local[1] - t*surf->lmscale)*lmvecscale[1];\n\t\t\tif (td < 0)\n\t\t\t\ttd = -td;\n\t\t\tfor (s=0 ; s<smax ; s++)\n\t\t\t{\n\t\t\t\tsd = (local[0] - s*surf->lmscale)*lmvecscale[0];\n\t\t\t\tif (sd < 0)\n\t\t\t\t\tsd = -sd;\n\t\t\t\tif (sd > td)\n\t\t\t\t\tdist = sd + (td>>1);\n\t\t\t\telse\n\t\t\t\t\tdist = td + (sd>>1);\n\t\t\t\tif (dist < minlight)\n\t\t\t\t{\n//\t\t\t\t\tblocknormals[t*smax + s][0] -= (rad - dist)*(impact[0]-local[0])/8192.0;\n//\t\t\t\t\tblocknormals[t*smax + s][1] -= (rad - dist)*(impact[1]-local[1])/8192.0;\n\t\t\t\t\tblocknormals[t*smax + s][2] += 0.5*blocknormals[t*smax + s][2]*(rad - dist)/256;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n*/\n\n#ifdef PEXT_LIGHTSTYLECOL\nstatic void Surf_AddDynamicLights_RGB (msurface_t *surf)\n{\n\tint\t\t\tlnum;\n\tfloat\t\tsd, td;\n\tfloat\t\tdist, rad, minlight;\n\tvec3_t\t\timpact;\n\tvec2_t\t\tlocal;\n\tint\t\t\ts, t;\n\tint\t\t\ti;\n\tint\t\t\tsmax, tmax;\n\tvec4_t\t\t*lmvecs;\n\tfloat\t\t*lmvecscale;\n//\tfloat temp;\n\tfloat r, g, b;\n\tunsigned\t*bl;\n\tvec3_t lightofs;\n\n\tsmax = (surf->extents[0]>>surf->lmshift)+1;\n\ttmax = (surf->extents[1]>>surf->lmshift)+1;\n\tif (currentmodel->facelmvecs)\n\t\tlmvecs = currentmodel->facelmvecs[surf-currentmodel->surfaces].lmvecs, lmvecscale = currentmodel->facelmvecs[surf-currentmodel->surfaces].lmvecscale;\n\telse\n\t\tlmvecs = surf->texinfo->vecs, lmvecscale = surf->texinfo->vecscale;\n\n\tfor (lnum=rtlights_first; lnum<RTL_FIRST; lnum++)\n\t{\n\t\tif ( !(surf->dlightbits & ((dlightbitmask_t)1u<<lnum) ) )\n\t\t\tcontinue;\t\t// not lit by this light\n\n\t\trad = cl_dlights[lnum].radius;\n\t\tVectorSubtract(cl_dlights[lnum].origin, currententity->origin, lightofs);\n\t\t//FIXME: transform by currententity->axis\n\t\tdist = DotProduct (lightofs, surf->plane->normal) - surf->plane->dist;\n\t\trad -= fabs(dist);\n\t\tminlight = cl_dlights[lnum].minlight;\n\t\tif (rad < minlight)\n\t\t\tcontinue;\n\t\tminlight = rad - minlight;\n\n\t\tfor (i=0 ; i<3 ; i++)\n\t\t{\n\t\t\timpact[i] = lightofs[i] -\n\t\t\t\t\tsurf->plane->normal[i]*dist;\n\t\t}\n\n\t\tlocal[0] = DotProduct (impact, lmvecs[0]) + lmvecs[0][3];\n\t\tlocal[1] = DotProduct (impact, lmvecs[1]) + lmvecs[1][3];\n\n\t\tlocal[0] -= surf->texturemins[0];\n\t\tlocal[1] -= surf->texturemins[1];\n\n\n\t\tif (r_dynamic.ival == 2)\n\t\t\tr = g = b = 256;\n\t\telse\n\t\t{\n\t\t\tr = cl_dlights[lnum].color[0]*128;\n\t\t\tg = cl_dlights[lnum].color[1]*128;\n\t\t\tb = cl_dlights[lnum].color[2]*128;\n\t\t}\n\n\t\tbl = blocklights;\n\t\tif (r < 0 || g < 0 || b < 0)\n\t\t{\n\t\t\tfor (t = 0 ; t<tmax ; t++)\n\t\t\t{\n\t\t\t\ttd = (local[1] - (t<<surf->lmshift))*lmvecscale[1];\n\t\t\t\tif (td < 0)\n\t\t\t\t\ttd = -td;\n\t\t\t\tfor (s=0 ; s<smax ; s++)\n\t\t\t\t{\n\t\t\t\t\tsd = (local[0] - (s<<surf->lmshift))*lmvecscale[0];\n\t\t\t\t\tif (sd < 0)\n\t\t\t\t\t\tsd = -sd;\n\t\t\t\t\tif (sd > td)\n\t\t\t\t\t\tdist = sd + td*0.5;\n\t\t\t\t\telse\n\t\t\t\t\t\tdist = td + sd*0.5;\n\t\t\t\t\tif (dist < minlight)\n\t\t\t\t\t{\n\t\t\t\t\t\ti = bl[0] + (rad - dist)*r;\n\t\t\t\t\t\tbl[0] = (i<0)?0:i;\n\t\t\t\t\t\ti = bl[1] + (rad - dist)*g;\n\t\t\t\t\t\tbl[1] = (i<0)?0:i;\n\t\t\t\t\t\ti = bl[2] + (rad - dist)*b;\n\t\t\t\t\t\tbl[2] = (i<0)?0:i;\n\t\t\t\t\t}\n\t\t\t\t\tbl += 3;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tfor (t = 0 ; t<tmax ; t++)\n\t\t\t{\n\t\t\t\ttd = (local[1] - (t<<surf->lmshift))*lmvecscale[1];\n\t\t\t\tif (td < 0)\n\t\t\t\t\ttd = -td;\n\t\t\t\tfor (s=0 ; s<smax ; s++)\n\t\t\t\t{\n\t\t\t\t\tsd = (local[0] - (s<<surf->lmshift))*lmvecscale[0];\n\t\t\t\t\tif (sd < 0)\n\t\t\t\t\t\tsd = -sd;\n\t\t\t\t\tif (sd > td)\n\t\t\t\t\t\tdist = sd + td*0.5;\n\t\t\t\t\telse\n\t\t\t\t\t\tdist = td + sd*0.5;\n\t\t\t\t\tif (dist < minlight)\n\t\t\t\t\t{\n\t\t\t\t\t\tbl[0] += (rad - dist)*r;\n\t\t\t\t\t\tbl[1] += (rad - dist)*g;\n\t\t\t\t\t\tbl[2] += (rad - dist)*b;\n\t\t\t\t\t}\n\t\t\t\t\tbl += 3;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n#endif\n\n\n\nstatic void Surf_BuildDeluxMap (model_t *wmodel, msurface_t *surf, qbyte *dest, lightmapinfo_t *lm, vec3_t *blocknormals)\n{\n\tint\t\t\tsmax, tmax;\n\tint\t\t\ti, j, size;\n\tqbyte\t\t*lightmap;\n\tqbyte\t\t*deluxmap;\n\tunsigned\tscale;\n\tint\t\t\tmaps;\n\tfloat intensity;\n\tvec_t\t\t*bnorm;\n\tvec3_t temp;\n\n\tint stride;\n\n\tif (!dest)\n\t\treturn;\n\n\tsmax = (surf->extents[0]>>surf->lmshift)+1;\n\ttmax = (surf->extents[1]>>surf->lmshift)+1;\n\tsize = smax*tmax;\n\tlightmap = surf->samples;\n\n\t// set to full bright if no light data\n\tif (!wmodel->deluxdata)\n\t{\n\t\tfor (i=0 ; i<size ; i++)\n\t\t{\n\t\t\tblocknormals[i][0] = 0.9;//surf->orientation[2][0];\n\t\t\tblocknormals[i][1] = 0.8;//surf->orientation[2][1];\n\t\t\tblocknormals[i][2] = 1;//surf->orientation[2][2];\n\t\t}\n\t\tgoto store;\n\t}\n\n// clear to no light\n\tfor (i=0 ; i<size ; i++)\n\t{\n\t\tblocknormals[i][0] = 0;\n\t\tblocknormals[i][1] = 0;\n\t\tblocknormals[i][2] = 0;\n\t}\n\n// add all the lightmaps\n\tif (lightmap)\n\t{\n\t\tswitch(wmodel->lightmaps.fmt)\n\t\t{\n\t\tcase LM_E5BGR9:\n\t\t\tdeluxmap = ((surf->samples - wmodel->lightdata)/4)*3 + wmodel->deluxdata;\n\t\t\tfor (maps = 0 ; maps < MAXCPULIGHTMAPS && surf->styles[maps] != INVALID_LIGHTSTYLE ; maps++)\n\t\t\t{\n\t\t\t\tscale = d_lightstylevalue[surf->styles[maps]];\n\t\t\t\tfor (i=0 ; i<size ; i++)\n\t\t\t\t{\n\t\t\t\t\tunsigned lm = ((unsigned int*)lightmap)[i];\n\t\t\t\t\tintensity = max3(((lm>>0)&0x1ff),((lm>>9)&0x1ff),((lm>>18)&0x1ff)) * scale * (rgb9e5tab[lm>>27]*(1<<7));\n\t\t\t\t\tblocknormals[i][0] += intensity*(deluxmap[i*3+0]-127);\n\t\t\t\t\tblocknormals[i][1] += intensity*(deluxmap[i*3+1]-127);\n\t\t\t\t\tblocknormals[i][2] += intensity*(deluxmap[i*3+2]-127);\n\t\t\t\t}\n\t\t\t\tlightmap += size*4;\t// skip to next lightmap\n\t\t\t\tdeluxmap += size*3;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase LM_RGB8:\n\t\t\tdeluxmap = surf->samples - wmodel->lightdata + wmodel->deluxdata;\n\t\t\tfor (maps = 0 ; maps < MAXCPULIGHTMAPS && surf->styles[maps] != INVALID_LIGHTSTYLE ; maps++)\n\t\t\t{\n\t\t\t\tscale = d_lightstylevalue[surf->styles[maps]];\n\t\t\t\tfor (i=0 ; i<size ; i++)\n\t\t\t\t{\n\t\t\t\t\tintensity = (lightmap[i*3]+lightmap[i*3+1]+lightmap[i*3+2]) * scale;\n\t\t\t\t\tblocknormals[i][0] += intensity*(deluxmap[i*3+0]-127);\n\t\t\t\t\tblocknormals[i][1] += intensity*(deluxmap[i*3+1]-127);\n\t\t\t\t\tblocknormals[i][2] += intensity*(deluxmap[i*3+2]-127);\n\t\t\t\t}\n\t\t\t\tlightmap += size*3;\t// skip to next lightmap\n\t\t\t\tdeluxmap += size*3;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase LM_L8:\n\t\t\tdeluxmap = (surf->samples - wmodel->lightdata)*3 + wmodel->deluxdata;\n\t\t\tfor (maps = 0 ; maps < MAXCPULIGHTMAPS && surf->styles[maps] != INVALID_LIGHTSTYLE ; maps++)\n\t\t\t{\n\t\t\t\tscale = d_lightstylevalue[surf->styles[maps]];\n\t\t\t\tfor (i=0 ; i<size ; i++)\n\t\t\t\t{\n\t\t\t\t\tintensity = (lightmap[i]) * scale;\n\t\t\t\t\tblocknormals[i][0] += intensity*(deluxmap[i*3+0]-127);\n\t\t\t\t\tblocknormals[i][1] += intensity*(deluxmap[i*3+1]-127);\n\t\t\t\t\tblocknormals[i][2] += intensity*(deluxmap[i*3+2]-127);\n\t\t\t\t}\n\t\t\t\tlightmap += size;\t// skip to next lightmap\n\t\t\t\tdeluxmap += size*3;\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t}\n\nstore:\n\t// add all the dynamic lights\n//\tif (surf->dlightframe == r_dlightframecount)\n//\t\tGLR_AddDynamicLightNorms (surf);\n\n// bound, invert, and shift\n\n\tswitch (lm->fmt)\n\t{\n\tdefault:\n\t\tSys_Error(\"Bad deluxemap format\\n\");\n\t\tbreak;\n\tcase PTI_A2BGR10:\n\t\t{\n\t\t\tunsigned int *destl = (void*)dest, r;\n\n\t\t\tstride = (lm->width-smax);\n\t\t\tbnorm = blocknormals[0];\n\t\t\tfor (i=0 ; i<tmax ; i++, destl += stride)\n\t\t\t{\n\t\t\t\tfor (j=0 ; j<smax ; j++)\n\t\t\t\t{\n\t\t\t\t\ttemp[0] = bnorm[0];\n\t\t\t\t\ttemp[1] = bnorm[1];\n\t\t\t\t\ttemp[2] = bnorm[2];\t//half the effect? so we emulate light's scalecos of 0.5\n\t\t\t\t\tbnorm+=3;\n\t\t\t\t\tVectorNormalize(temp);\n\t\t\t\t\tr  = (unsigned int)((temp[0]+1)/2*1023)<<0;\n\t\t\t\t\tr |= (unsigned int)((temp[1]+1)/2*1023)<<10;\n\t\t\t\t\tr |= (unsigned int)((temp[2]+1)/2*1023)<<20;\n\t\t\t\t\t*destl++ = r;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tbreak;\n\tcase PTI_BGRX8:\n\t\tstride = (lm->width-smax)*4;\n\t\tbnorm = blocknormals[0];\n\t\tfor (i=0 ; i<tmax ; i++, dest += stride)\n\t\t{\n\t\t\tfor (j=0 ; j<smax ; j++)\n\t\t\t{\n\t\t\t\ttemp[0] = bnorm[0];\n\t\t\t\ttemp[1] = bnorm[1];\n\t\t\t\ttemp[2] = bnorm[2];\t//half the effect? so we emulate light's scalecos of 0.5\n\t\t\t\tVectorNormalize(temp);\n\t\t\t\tdest[2] = (temp[0]+1)/2*255;\n\t\t\t\tdest[1] = (temp[1]+1)/2*255;\n\t\t\t\tdest[0] = (temp[2]+1)/2*255;\n\n\t\t\t\tdest += 4;\n\t\t\t\tbnorm+=3;\n\t\t\t}\n\t\t}\n\t\tbreak;\n\tcase PTI_RGBX8:\n\tcase PTI_RGB8:\n\t\tstride = (lm->width-smax)*lm->pixbytes;\n\t\tbnorm = blocknormals[0];\n\t\tfor (i=0 ; i<tmax ; i++, dest += stride)\n\t\t{\n\t\t\tfor (j=0 ; j<smax ; j++)\n\t\t\t{\n\t\t\t\ttemp[0] = bnorm[0];\n\t\t\t\ttemp[1] = bnorm[1];\n\t\t\t\ttemp[2] = bnorm[2];\t//half the effect? so we emulate light's scalecos of 0.5\n\t\t\t\tVectorNormalize(temp);\n\t\t\t\tdest[0] = (temp[0]+1)/2*255;\n\t\t\t\tdest[1] = (temp[1]+1)/2*255;\n\t\t\t\tdest[2] = (temp[2]+1)/2*255;\n\n\t\t\t\tdest += lm->pixbytes;\n\t\t\t\tbnorm+=3;\n\t\t\t}\n\t\t}\n\t\tbreak;\n\t}\n}\n\nstatic unsigned int Surf_PackE5BRG9(int r, int g, int b, int shift)\n{\t//5 bits exponent, 3*9 bits of mantissa. no sign bit.\n\tint e = 0;\n\tfloat m = max(max(r, g), b) / (float)(1u<<shift);\n\tfloat scale;\n\n\tif (m >= 0.5)\n\t{\t//positive exponent\n\t\twhile (m >= (1u<<(e)) && e < 30-15)\t//don't do nans.\n\t\t\te++;\n\t}\n\telse\n\t{\t//negative exponent...\n\t\twhile (m < 1/(1u<<-e) && e > -15)\t//don't do denormals.\n\t\t\te--;\n\t}\n\n\tscale = pow(2, e-9);\n\tscale *= (1u<<shift);\n\n\tr = bound(0, r/scale + 0.5, 0x1ff);\n\tg = bound(0, g/scale + 0.5, 0x1ff);\n\tb = bound(0, b/scale + 0.5, 0x1ff);\n\n\treturn ((e+15)<<27) | (b<<18) | (g<<9) | r;\n}\n\nstatic unsigned short Surf_GenHalf(float val)\n{\t//1-bit sign (ignored here)\n\t//5-bit exponent (biased by 15)\n\t//10-bit mantissa (normalised, so effectively 11 bits when exponent!=0)\n\tunion \n\t{\n\t\tfloat f;\n\t\tunsigned int u;\n\t} u = {val};\n\tint e = 0;\n\tint m;\n\n\te = ((u.u>>23)&0xff) - 127;\n\tif (e < -15)\n\t\treturn 0; //too small exponent, treat it as a 0 denormal\n\tif (e > 15)\n\t\tm = 0; //infinity instead of a nan\n\telse\n\t\tm = (u.u&((1u<<23)-1))>>13;\n\treturn ((e+15)<<10) | m;\n}\nstatic void Surf_PackRGB16F(void *result, int r, int g, int b, int one)\n{\n#if 0\n\t//bulldozer+ or skylake+ supposedly. which means I can't test it, which means I can't enable it.\n\t__v4sf rgba = (__v4sf){r, g, b, one} / (float)one;\n\tunion\n\t{\n\t\t__v8hi v;\n\t\t__v4hi i;\n\t} tmp;\n\t//vcvtps2ph writes either a 64bit mem location, or the lower half of an xmm register. unfortunately ts still an xmm register, and that's 128bit.\n\t//the __vXhi weirdness is because half-floats just don't work anywhere but conversions so __vXhf would cause all sorts of compiler errors, is the theory\n\ttmp.v = __builtin_ia32_vcvtps2ph(rgba, 1);\n\t*(__v4hi*)result = tmp.i;\n#else\n\t((unsigned short*)result)[0] = Surf_GenHalf(r / (float)one);\n\t((unsigned short*)result)[1] = Surf_GenHalf(g / (float)one);\n\t((unsigned short*)result)[2] = Surf_GenHalf(b / (float)one);\n\t((unsigned short*)result)[3] = /*Surf_GenHalf(1.0);*/0x0fu<<10; //a standard ieee float should have all but the lead bit set of its exponent, and its mantissa 0.\n#endif\n}\nstatic void Surf_PackRGBX32F(void *result, int r, int g, int b, int one)\n{\n\t((float*)result)[0] = r/(float)one;\n\t((float*)result)[1] = g/(float)one;\n\t((float*)result)[2] = b/(float)one;\n\t((float*)result)[3] = 1.0;\n}\n\n/*any sane compiler will inline and split this, removing the stainsrc stuff\njust unpacks the internal lightmap block into texture info ready for upload\nmerges stains and oversaturates overbrights.\n*/\nstatic void Surf_StoreLightmap_RGB(qbyte *dest, unsigned int *bl, int smax, int tmax, unsigned int shift, stmap *stainsrc, lightmapinfo_t *lm)\n{\n\tint r, g, b, m;\n\tunsigned int i, j;\n\tint stride;\n\n\tswitch(lm->fmt)\n\t{\n\tdefault:\n\t\tSys_Error(\"Surf_StoreLightmap_RGB: Bad format - %s\\n\", Image_FormatName(lm->fmt));\n\t\tbreak;\n\tcase PTI_A2BGR10:\n\t\tstride = (lm->width-smax)<<2;\n\n\t\tshift -= 2;\n\n\t\tfor (i=0 ; i<tmax ; i++, dest += stride)\n\t\t{\n\t\t\tfor (j=0 ; j<smax ; j++)\n\t\t\t{\n\t\t\t\tr = *bl++ >> shift;\n\t\t\t\tg = *bl++ >> shift;\n\t\t\t\tb = *bl++ >> shift;\n\n\t\t\t\tif (stainsrc)\t// merge in stain\n\t\t\t\t{\n\t\t\t\t\tr = (127+r*(*stainsrc++)) >> 8;\n\t\t\t\t\tg = (127+g*(*stainsrc++)) >> 8;\n\t\t\t\t\tb = (127+b*(*stainsrc++)) >> 8;\n\t\t\t\t}\n\n\t\t\t\t// quake 2 method, scale highest down to\n\t\t\t\t// maintain hue\n\t\t\t\tm = max(max(r, g), b);\n\t\t\t\tif (m > 1023)\n\t\t\t\t{\n\t\t\t\t\tr *= 1023.0/m;\n\t\t\t\t\tg *= 1023.0/m;\n\t\t\t\t\tb *= 1023.0/m;\n\t\t\t\t}\n\n\t\t\t\t*(unsigned int*)dest = (3u<<30) | ((b&0x3ff)<<20) | ((g&0x3ff)<<10) | (r&0x3ff);\n\t\t\t\tdest += 4;\n\t\t\t}\n\t\t\tif (stainsrc)\n\t\t\t\tstainsrc += (lm->width - smax)*3;\n\t\t}\n\t\tbreak;\n\tcase PTI_E5BGR9:\n\t\tstride = (lm->width-smax)<<2;\n\n\t\t//5bit shared exponent, with bias of 15.\n\t\tfor (i=0 ; i<tmax ; i++, dest += stride)\n\t\t{\n\t\t\tfor (j=0 ; j<smax ; j++)\n\t\t\t{\n\t\t\t\tr = *bl++;\n\t\t\t\tg = *bl++;\n\t\t\t\tb = *bl++;\n\n\t\t\t\tif (stainsrc)\t// merge in stain\n\t\t\t\t{\n\t\t\t\t\tr = (127+r*(*stainsrc++)) >> 8;\n\t\t\t\t\tg = (127+g*(*stainsrc++)) >> 8;\n\t\t\t\t\tb = (127+b*(*stainsrc++)) >> 8;\n\t\t\t\t}\n\n\t\t\t\t*(unsigned int*)dest = Surf_PackE5BRG9(r,g,b,shift+8);\n\t\t\t\tdest += 4;\n\t\t\t}\n\t\t\tif (stainsrc)\n\t\t\t\tstainsrc += (lm->width - smax)*3;\n\t\t}\n\t\tbreak;\n\tcase PTI_RGBA16F:\n\t\tstride = (lm->width-smax)<<3;\n\t\tfor (i=0 ; i<tmax ; i++, dest += stride)\n\t\t{\n\t\t\tfor (j=0 ; j<smax ; j++)\n\t\t\t{\n\t\t\t\tr = *bl++;\n\t\t\t\tg = *bl++;\n\t\t\t\tb = *bl++;\n\n\t\t\t\tif (stainsrc)\t// merge in stain\n\t\t\t\t{\n\t\t\t\t\tr = (127+r*(*stainsrc++)) >> 8;\n\t\t\t\t\tg = (127+g*(*stainsrc++)) >> 8;\n\t\t\t\t\tb = (127+b*(*stainsrc++)) >> 8;\n\t\t\t\t}\n\n\t\t\t\tSurf_PackRGB16F(dest, r,g,b,1<<(shift+8));\n\t\t\t\tdest += 8;\n\t\t\t}\n\t\t\tif (stainsrc)\n\t\t\t\tstainsrc += (lm->width - smax)*3;\n\t\t}\n\t\tbreak;\n\tcase PTI_RGBA32F:\n\t\tshift = 1u<<(shift+8);\n\t\tstride = (lm->width-smax)<<4;\n\t\tfor (i=0 ; i<tmax ; i++, dest += stride)\n\t\t{\n\t\t\tfor (j=0 ; j<smax ; j++)\n\t\t\t{\n\t\t\t\tr = *bl++;\n\t\t\t\tg = *bl++;\n\t\t\t\tb = *bl++;\n\n\t\t\t\tif (stainsrc)\t// merge in stain\n\t\t\t\t{\n\t\t\t\t\tr = (127+r*(*stainsrc++)) >> 8;\n\t\t\t\t\tg = (127+g*(*stainsrc++)) >> 8;\n\t\t\t\t\tb = (127+b*(*stainsrc++)) >> 8;\n\t\t\t\t}\n\n\t\t\t\tSurf_PackRGBX32F(dest, r,g,b,shift);\n\t\t\t\tdest += sizeof(float)*4;\n\t\t\t}\n\t\t\tif (stainsrc)\n\t\t\t\tstainsrc += (lm->width - smax)*3;\n\t\t}\n\t\tbreak;\n\tcase PTI_RGBX8:\n\tcase PTI_RGBA8:\n\t\tstride = (lm->width-smax)<<2;\n\n\t\tfor (i=0 ; i<tmax ; i++, dest += stride)\n\t\t{\n\t\t\tfor (j=0 ; j<smax ; j++)\n\t\t\t{\n\t\t\t\tr = *bl++ >> shift;\n\t\t\t\tg = *bl++ >> shift;\n\t\t\t\tb = *bl++ >> shift;\n\n\t\t\t\tif (stainsrc)\t// merge in stain\n\t\t\t\t{\n\t\t\t\t\tr = (127+r*(*stainsrc++)) >> 8;\n\t\t\t\t\tg = (127+g*(*stainsrc++)) >> 8;\n\t\t\t\t\tb = (127+b*(*stainsrc++)) >> 8;\n\t\t\t\t}\n\n\t\t\t\t// quake 2 method, scale highest down to\n\t\t\t\t// maintain hue\n\t\t\t\tm = max(max(r, g), b);\n\t\t\t\tif (m > 255)\n\t\t\t\t{\n\t\t\t\t\tr *= 255.0/m;\n\t\t\t\t\tg *= 255.0/m;\n\t\t\t\t\tb *= 255.0/m;\n\t\t\t\t}\n\n\t\t\t\tdest[0] = r;\n\t\t\t\tdest[1] = g;\n\t\t\t\tdest[2] = b;\n\t\t\t\tdest[3] = 255;\n\n\t\t\t\tdest += 4;\n\t\t\t}\n\t\t\tif (stainsrc)\n\t\t\t\tstainsrc += (lm->width - smax)*3;\n\t\t}\n\t\tbreak;\n\tcase PTI_BGRX8:\n\tcase PTI_BGRA8:\n\t\tstride = (lm->width-smax)<<2;\n\n\t\tfor (i=0 ; i<tmax ; i++, dest += stride)\n\t\t{\n\t\t\tfor (j=0 ; j<smax ; j++)\n\t\t\t{\n\t\t\t\tr = *bl++ >> shift;\n\t\t\t\tg = *bl++ >> shift;\n\t\t\t\tb = *bl++ >> shift;\n\n\t\t\t\tif (stainsrc)\t// merge in stain\n\t\t\t\t{\n\t\t\t\t\tr = (127+r*(*stainsrc++)) >> 8;\n\t\t\t\t\tg = (127+g*(*stainsrc++)) >> 8;\n\t\t\t\t\tb = (127+b*(*stainsrc++)) >> 8;\n\t\t\t\t}\n\n\t\t\t\t// quake 2 method, scale highest down to\n\t\t\t\t// maintain hue\n\t\t\t\tm = max(max(r, g), b);\n\t\t\t\tif (m > 255)\n\t\t\t\t{\n\t\t\t\t\tr *= 255.0/m;\n\t\t\t\t\tg *= 255.0/m;\n\t\t\t\t\tb *= 255.0/m;\n\t\t\t\t}\n\n\t\t\t\tdest[0] = b;\n\t\t\t\tdest[1] = g;\n\t\t\t\tdest[2] = r;\n\t\t\t\tdest[3] = 255;\n\n\t\t\t\tdest += 4;\n\t\t\t}\n\t\t\tif (stainsrc)\n\t\t\t\tstainsrc += (lm->width - smax)*3;\n\t\t}\n\t\tbreak;\n/*\n\tcase PTI_BGRX8:\n\tcase PTI_BGRA8:\n\t\tstride = (lm->width-smax)<<2;\n\n\t\tbl = blocklights;\n\n\t\tfor (i=0 ; i<tmax ; i++, dest += stride)\n\t\t{\n\t\t\tfor (j=0 ; j<smax ; j++)\n\t\t\t{\n\t\t\t\tr = *bl++ >> shift;\n\t\t\t\tg = *bl++ >> shift;\n\t\t\t\tb = *bl++ >> shift;\n\n\t\t\t\tif (stainsrc)\t// merge in stain\n\t\t\t\t{\n\t\t\t\t\tr = (127+r*(*stainsrc++)) >> 8;\n\t\t\t\t\tg = (127+g*(*stainsrc++)) >> 8;\n\t\t\t\t\tb = (127+b*(*stainsrc++)) >> 8;\n\t\t\t\t}\n\n\t\t\t\tif (r > 255)\n\t\t\t\t\tdest[2] = 255;\n\t\t\t\telse if (r < 0)\n\t\t\t\t\tdest[2] = 0;\n\t\t\t\telse\n\t\t\t\t\tdest[2] = r;\n\n\t\t\t\tif (g > 255)\n\t\t\t\t\tdest[1] = 255;\n\t\t\t\telse if (g < 0)\n\t\t\t\t\tdest[1] = 0;\n\t\t\t\telse\n\t\t\t\t\tdest[1] = g;\n\n\t\t\t\tif (b > 255)\n\t\t\t\t\tdest[0] = 255;\n\t\t\t\telse if (b < 0)\n\t\t\t\t\tdest[0] = 0;\n\t\t\t\telse\n\t\t\t\t\tdest[0] = b;\n\n\t\t\t\tdest[3] = 255;\n\t\t\t\tdest += 4;\n\t\t\t}\n\t\t\tif (stainsrc)\n\t\t\t\tstainsrc += (lmwidth - smax)*3;\n\t\t}\n\t\tbreak;\n*/\n\tcase PTI_RGB565:\n\t\tstride = (lm->width-smax)<<1;\n\n\t\tfor (i=0 ; i<tmax ; i++, dest += stride)\n\t\t{\n\t\t\tfor (j=0 ; j<smax ; j++)\n\t\t\t{\n\t\t\t\tr = *bl++ >> shift;\n\t\t\t\tg = *bl++ >> shift;\n\t\t\t\tb = *bl++ >> shift;\n\n\t\t\t\tif (stainsrc)\t// merge in stain\n\t\t\t\t{\n\t\t\t\t\tr = (127+r*(*stainsrc++)) >> 8;\n\t\t\t\t\tg = (127+g*(*stainsrc++)) >> 8;\n\t\t\t\t\tb = (127+b*(*stainsrc++)) >> 8;\n\t\t\t\t}\n\n\t\t\t\t// quake 2 method, scale highest down to\n\t\t\t\t// maintain hue\n\t\t\t\tm = max(max(r, g), b);\n\t\t\t\tif (m > 255)\n\t\t\t\t{\n\t\t\t\t\tr *= 255.0/m;\n\t\t\t\t\tg *= 255.0/m;\n\t\t\t\t\tb *= 255.0/m;\n\t\t\t\t}\n\n\t\t\t\t*(unsigned short*)dest = (b>>3) | ((g>>2)<<5) | ((r>>3)<<11);\n\t\t\t\tdest += 2;\n\t\t\t}\n\t\t\tif (stainsrc)\n\t\t\t\tstainsrc += (lm->width - smax)*3;\n\t\t}\n\t\tbreak;\n\tcase PTI_RGBA4444:\n\t\tstride = (lm->width-smax)<<1;\n\n\t\tfor (i=0 ; i<tmax ; i++, dest += stride)\n\t\t{\n\t\t\tfor (j=0 ; j<smax ; j++)\n\t\t\t{\n\t\t\t\tr = *bl++ >> shift;\n\t\t\t\tg = *bl++ >> shift;\n\t\t\t\tb = *bl++ >> shift;\n\n\t\t\t\tif (stainsrc)\t// merge in stain\n\t\t\t\t{\n\t\t\t\t\tr = (127+r*(*stainsrc++)) >> 8;\n\t\t\t\t\tg = (127+g*(*stainsrc++)) >> 8;\n\t\t\t\t\tb = (127+b*(*stainsrc++)) >> 8;\n\t\t\t\t}\n\n\t\t\t\t// quake 2 method, scale highest down to\n\t\t\t\t// maintain hue\n\t\t\t\tm = max(max(r, g), b);\n\t\t\t\tif (m > 255)\n\t\t\t\t{\n\t\t\t\t\tr *= 255.0/m;\n\t\t\t\t\tg *= 255.0/m;\n\t\t\t\t\tb *= 255.0/m;\n\t\t\t\t}\n\n\t\t\t\t*(unsigned short*)dest = ((r>>4)<<12) | ((g>>4)<<8) | ((b>>4)<<4) | 0x000f;\n\t\t\t\tdest += 2;\n\t\t\t}\n\t\t\tif (stainsrc)\n\t\t\t\tstainsrc += (lm->width - smax)*3;\n\t\t}\n\t\tbreak;\n\tcase PTI_RGBA5551:\n\t\tstride = (lm->width-smax)<<1;\n\n\t\tfor (i=0 ; i<tmax ; i++, dest += stride)\n\t\t{\n\t\t\tfor (j=0 ; j<smax ; j++)\n\t\t\t{\n\t\t\t\tr = *bl++ >> shift;\n\t\t\t\tg = *bl++ >> shift;\n\t\t\t\tb = *bl++ >> shift;\n\n\t\t\t\tif (stainsrc)\t// merge in stain\n\t\t\t\t{\n\t\t\t\t\tr = (127+r*(*stainsrc++)) >> 8;\n\t\t\t\t\tg = (127+g*(*stainsrc++)) >> 8;\n\t\t\t\t\tb = (127+b*(*stainsrc++)) >> 8;\n\t\t\t\t}\n\n\t\t\t\t// quake 2 method, scale highest down to\n\t\t\t\t// maintain hue\n\t\t\t\tm = max(max(r, g), b);\n\t\t\t\tif (m > 255)\n\t\t\t\t{\n\t\t\t\t\tr *= 255.0/m;\n\t\t\t\t\tg *= 255.0/m;\n\t\t\t\t\tb *= 255.0/m;\n\t\t\t\t}\n\n\t\t\t\t*(unsigned short*)dest = ((r>>3)<<11) | ((g>>3)<<6) | ((b>>3)<<1) | 0x0001;\n\t\t\t\tdest += 2;\n\t\t\t}\n\t\t\tif (stainsrc)\n\t\t\t\tstainsrc += (lm->width - smax)*3;\n\t\t}\n\t\tbreak;\n\tcase PTI_ARGB4444:\n\t\tstride = (lm->width-smax)<<1;\n\n\t\tfor (i=0 ; i<tmax ; i++, dest += stride)\n\t\t{\n\t\t\tfor (j=0 ; j<smax ; j++)\n\t\t\t{\n\t\t\t\tr = *bl++ >> shift;\n\t\t\t\tg = *bl++ >> shift;\n\t\t\t\tb = *bl++ >> shift;\n\n\t\t\t\tif (stainsrc)\t// merge in stain\n\t\t\t\t{\n\t\t\t\t\tr = (127+r*(*stainsrc++)) >> 8;\n\t\t\t\t\tg = (127+g*(*stainsrc++)) >> 8;\n\t\t\t\t\tb = (127+b*(*stainsrc++)) >> 8;\n\t\t\t\t}\n\n\t\t\t\t// quake 2 method, scale highest down to\n\t\t\t\t// maintain hue\n\t\t\t\tm = max(max(r, g), b);\n\t\t\t\tif (m > 255)\n\t\t\t\t{\n\t\t\t\t\tr *= 255.0/m;\n\t\t\t\t\tg *= 255.0/m;\n\t\t\t\t\tb *= 255.0/m;\n\t\t\t\t}\n\n\t\t\t\t*(unsigned short*)dest = 0xf000 | ((r>>4)<<8) | ((g>>4)<<4) | ((b>>4)<<0);\n\t\t\t\tdest += 2;\n\t\t\t}\n\t\t\tif (stainsrc)\n\t\t\t\tstainsrc += (lm->width - smax)*3;\n\t\t}\n\t\tbreak;\n\tcase PTI_ARGB1555:\n\t\tstride = (lm->width-smax)<<1;\n\n\t\tfor (i=0 ; i<tmax ; i++, dest += stride)\n\t\t{\n\t\t\tfor (j=0 ; j<smax ; j++)\n\t\t\t{\n\t\t\t\tr = *bl++ >> shift;\n\t\t\t\tg = *bl++ >> shift;\n\t\t\t\tb = *bl++ >> shift;\n\n\t\t\t\tif (stainsrc)\t// merge in stain\n\t\t\t\t{\n\t\t\t\t\tr = (127+r*(*stainsrc++)) >> 8;\n\t\t\t\t\tg = (127+g*(*stainsrc++)) >> 8;\n\t\t\t\t\tb = (127+b*(*stainsrc++)) >> 8;\n\t\t\t\t}\n\n\t\t\t\t// quake 2 method, scale highest down to\n\t\t\t\t// maintain hue\n\t\t\t\tm = max(max(r, g), b);\n\t\t\t\tif (m > 255)\n\t\t\t\t{\n\t\t\t\t\tr *= 255.0/m;\n\t\t\t\t\tg *= 255.0/m;\n\t\t\t\t\tb *= 255.0/m;\n\t\t\t\t}\n\n\t\t\t\t*(unsigned short*)dest = 0x8000 | ((r>>3)<<10) | ((g>>3)<<5) | ((b>>3)<<0);\n\t\t\t\tdest += 2;\n\t\t\t}\n\t\t\tif (stainsrc)\n\t\t\t\tstainsrc += (lm->width - smax)*3;\n\t\t}\n\t\tbreak;\n\tcase PTI_RGB8:\n\t\tstride = lm->width*3 - (smax*3);\n\n\t\tfor (i=0 ; i<tmax ; i++, dest += stride)\n\t\t{\n\t\t\tfor (j=0 ; j<smax ; j++)\n\t\t\t{\n\t\t\t\tr = *bl++ >> shift;\n\t\t\t\tg = *bl++ >> shift;\n\t\t\t\tb = *bl++ >> shift;\n\n\t\t\t\tif (stainsrc)\t// merge in stain\n\t\t\t\t{\n\t\t\t\t\tr = (127+r*(*stainsrc++)) >> 8;\n\t\t\t\t\tg = (127+g*(*stainsrc++)) >> 8;\n\t\t\t\t\tb = (127+b*(*stainsrc++)) >> 8;\n\t\t\t\t}\n\n\t\t\t\t// quake 2 method, scale highest down to\n\t\t\t\t// maintain hue\n\t\t\t\tm = max(max(r, g), b);\n\t\t\t\tif (m > 255)\n\t\t\t\t{\n\t\t\t\t\tr *= 255.0/m;\n\t\t\t\t\tg *= 255.0/m;\n\t\t\t\t\tb *= 255.0/m;\n\t\t\t\t}\n\n\t\t\t\tdest[0] = r;\n\t\t\t\tdest[1] = g;\n\t\t\t\tdest[2] = b;\n\t\t\t\tdest += 3;\n\t\t\t}\n\t\t\tif (stainsrc)\n\t\t\t\tstainsrc += (lm->width - smax)*3;\n\t\t}\n\t\tbreak;\n\t}\n}\nstatic void Surf_StoreLightmap_Lum(qbyte *dest, unsigned int *bl, int smax, int tmax, unsigned int shift, stmap *stainsrc, unsigned int lmwidth)\n{\n\tint t;\n\tunsigned int i, j;\n\n\tfor (i=0 ; i<tmax ; i++, dest += lmwidth)\n\t{\n\t\tfor (j=0 ; j<smax ; j++)\n\t\t{\n\t\t\tt = *bl++;\n\t\t\tt >>= shift;\n\t\t\tif (t > 255)\n\t\t\t\tt = 255;\n\t\t\tdest[j] = t;\n\t\t}\n\t}\n}\n\n/*\n===============\nR_BuildLightMap\n\nCombine and scale multiple lightmaps into the 8.8 format in blocklights\n===============\n*/\nstatic void Surf_BuildLightMap (model_t *model, msurface_t *surf, int map, int shift, int ambient, int *d_lightstylevalue)\n{\n\tint\t\t\tsmax = (surf->extents[0]>>surf->lmshift)+1;\n\tint\t\t\ttmax = (surf->extents[1]>>surf->lmshift)+1;\n\tint\t\t\tt;\n\tint\t\t\ti;\n\tsize_t\t\tsize = (size_t)smax*tmax;\n\tunsigned\tscalergb[3];\n\tunsigned\tscale;\n\tint\t\t\tmaps;\n\tunsigned\t*bl;\n\tglRect_t\t*theRect;\n\tvoid\t\t*dest;\n\tvoid\t\t*deluxedest;\n\tvoid\t\t*stainsrc;\n\tlightmapinfo_t *lm = lightmap[surf->lightmaptexturenums[map]];\n\tqbyte\t\t*src = surf->samples;\n\n\tshift += 7; // increase to base value\n\tsurf->cached_dlight = (surf->dlightframe == r_dlightframecount);\n\n\tif (size > maxblocksize)\n\t{\t//fixme: fill in?\n\t\t//Threading: this should not be a problem, all surfaces should have been built from the main thread at map load so it should have maxed it there.\n\t\tBZ_Free(blocklights);\n\t\tBZ_Free(blocknormals);\n\n\t\tmaxblocksize = size;\n\t\tblocknormals = BZ_Malloc(maxblocksize * sizeof(*blocknormals));\t//already a vector\n\t\tblocklights = BZ_Malloc(maxblocksize * 3*sizeof(*blocklights));\n\t}\n\n\t//make sure we flag the output rect properly.\n\ttheRect = &lm->rectchange;\n\tif (theRect->t > surf->light_t[map])\n\t\ttheRect->t = surf->light_t[map];\n\tif (theRect->l > surf->light_s[map])\n\t\ttheRect->l = surf->light_s[map];\n\tif (theRect->r < surf->light_s[map]+smax)\n\t\ttheRect->r = surf->light_s[map]+smax;\n\tif (theRect->b < surf->light_t[map]+tmax)\n\t\ttheRect->b = surf->light_t[map]+tmax;\n\n\tdest = lm->lightmaps + (surf->light_t[map] * lm->width + surf->light_s[map]) * lm->pixbytes;\n\tif (!r_stains.value || !surf->stained)\n\t\tstainsrc = NULL;\n\telse\n\t\tstainsrc = lm->stainmaps + (surf->light_t[map] * lm->width + surf->light_s[map]) * 3;\n\n\tlm->modified = true;\n\tif (lm->hasdeluxe && model->deluxdata)\n\t{\n\t\tlightmapinfo_t *dlm = lightmap[surf->lightmaptexturenums[map]+1];\n\t\tdlm->modified = true;\n\t\ttheRect = &dlm->rectchange;\n\t\tif (theRect->t > surf->light_t[map])\n\t\t\ttheRect->t = surf->light_t[map];\n\t\tif (theRect->l > surf->light_s[map])\n\t\t\ttheRect->l = surf->light_s[map];\n\t\tif (theRect->r < surf->light_s[map]+smax)\n\t\t\ttheRect->r = surf->light_s[map]+smax;\n\t\tif (theRect->b < surf->light_t[map]+tmax)\n\t\t\ttheRect->b = surf->light_t[map]+tmax;\n\n\t\tdeluxedest = dlm->lightmaps + (surf->light_t[map] * dlm->width + surf->light_s[map]) * dlm->pixbytes;\n\n\t\tSurf_BuildDeluxMap(model, surf, deluxedest, dlm, blocknormals);\n\t}\n\n\tif (lm->fmt != PTI_L8)\n\t{\n\t\t// set to full bright if no light data\n\t\tif (ambient < 0)\n\t\t{\n\t\t\tt = (-1-ambient)*255;\n\t\t\tfor (i=0 ; i<size*3 ; i++)\n\t\t\t\tblocklights[i] = t;\n\t\t\tfor (maps = 0 ; maps < MAXCPULIGHTMAPS ; maps++)\n\t\t\t{\n\t\t\t\tsurf->cached_light[maps] = -1-ambient;\n\t\t\t\tsurf->cached_colour[maps] = 0xff;\n\t\t\t}\n\t\t}\n\t\telse if (r_fullbright.value>0)\t//not qw\n\t\t{\n\t\t\tfor (i=0 ; i<size*3 ; i++)\n\t\t\t\tblocklights[i] = r_fullbright.value*255*256;\n\t\t\tif (!surf->samples)\n\t\t\t{\n\t\t\t\tsurf->cached_light[0] = d_lightstylevalue[0];\n\t\t\t\tsurf->cached_colour[0] = cl_lightstyle[0].colourkey;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tfor (maps = 0 ; maps < MAXCPULIGHTMAPS && surf->styles[maps] != INVALID_LIGHTSTYLE ; maps++)\n\t\t\t\t{\n\t\t\t\t\tsurf->cached_light[maps] = d_lightstylevalue[surf->styles[maps]];\n\t\t\t\t\tsurf->cached_colour[maps] = cl_lightstyle[surf->styles[maps]].colourkey;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse if (!model->lightdata)\n\t\t{\n\t\t\t/*fullbright if map is not lit. but not overbright*/\n\t\t\tfor (i=0 ; i<size*3 ; i++)\n\t\t\t\tblocklights[i] = 128*256;\n\t\t}\n\t\telse if (!surf->samples)\n\t\t{\n\t\t\t/*no samples, but map is otherwise lit = pure black*/\n\t\t\tfor (i=0 ; i<size*3 ; i++)\n\t\t\t\tblocklights[i] = 0;\n\t\t\tsurf->cached_light[0] = d_lightstylevalue[0];\n\t\t\tsurf->cached_colour[0] = cl_lightstyle[0].colourkey;\n\t\t}\n\t\telse\n\t\t{\n// clear to no light\n\t\t\tt = ambient;\n\t\t\tif (t == 0)\n\t\t\t\tmemset(blocklights, 0, size*3*sizeof(*bl));\n\t\t\telse\n\t\t\t{\n\t\t\t\tfor (i=0 ; i<size*3 ; i++)\n\t\t\t\t{\n\t\t\t\t\tblocklights[i] = t;\n\t\t\t\t}\n\t\t\t}\n\n// add all the lightmaps\n\t\t\tif (src)\n\t\t\t{\n\t\t\t\tif (model->lightmaps.prebaked)\n\t\t\t\t\tSys_Error(\"Surf_BuildLightMap: q3bsp\");\n\t\t\t\tswitch(model->lightmaps.fmt)\n\t\t\t\t{\n\t\t\t\tcase LM_E5BGR9:\n\t\t\t\t\tfor (maps = 0 ; maps < MAXCPULIGHTMAPS && surf->styles[maps] != INVALID_LIGHTSTYLE ; maps++)\n\t\t\t\t\t{\n\t\t\t\t\t\tsurf->cached_light[maps] = scale = d_lightstylevalue[surf->styles[maps]];\t// 8.8 fraction\n\t\t\t\t\t\tsurf->cached_colour[maps] = cl_lightstyle[surf->styles[maps]].colourkey;\n\t\t\t\t\t\tif (scale)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tVectorScale(cl_lightstyle[surf->styles[maps]].colours, scale, scalergb);\n\t\t\t\t\t\t\tfor (i=0 ; i<size ; i++)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tunsigned int l = ((unsigned int*)src)[i];\n\t\t\t\t\t\t\t\tfloat e = rgb9e5tab[l>>27]*(1<<7);\n\t\t\t\t\t\t\t\tblocklights[i*3+0] += scalergb[0] * e * ((l>> 0)&0x1ff);\n\t\t\t\t\t\t\t\tblocklights[i*3+1] += scalergb[1] * e * ((l>> 9)&0x1ff);\n\t\t\t\t\t\t\t\tblocklights[i*3+2] += scalergb[2] * e * ((l>>18)&0x1ff);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tsrc += size*4;\t// skip to next lightmap\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase LM_RGB8:\n\t\t\t\t\tfor (maps = 0 ; maps < MAXCPULIGHTMAPS && surf->styles[maps] != INVALID_LIGHTSTYLE ; maps++)\n\t\t\t\t\t{\n\t\t\t\t\t\tsurf->cached_light[maps] = scale = d_lightstylevalue[surf->styles[maps]];\n\t\t\t\t\t\tsurf->cached_colour[maps] = cl_lightstyle[surf->styles[maps]].colourkey;\n\t\t\t\t\t\tif (scale)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tVectorScale(cl_lightstyle[surf->styles[maps]].colours, scale, scalergb);\n\t\t\t\t\t\t\tbl = blocklights;\n\t\t\t\t\t\t\tfor (i=0 ; i<size ; i++)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t*bl++\t\t+=   *src++ * scalergb[0];\n\t\t\t\t\t\t\t\t*bl++\t\t+=   *src++ * scalergb[1];\n\t\t\t\t\t\t\t\t*bl++\t\t+=   *src++ * scalergb[2];\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tsrc += size*3;\t// skip to next lightmap\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase LM_L8:\n\t\t\t\t\tfor (maps = 0 ; maps < MAXCPULIGHTMAPS && surf->styles[maps] != INVALID_LIGHTSTYLE ;\n\t\t\t\t\t\t maps++)\n\t\t\t\t\t{\n\t\t\t\t\t\tsurf->cached_light[maps] = scale = d_lightstylevalue[surf->styles[maps]];\t// 8.8 fraction\n\t\t\t\t\t\tsurf->cached_colour[maps] = cl_lightstyle[surf->styles[maps]].colourkey;\n\t\t\t\t\t\tif (scale)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tVectorScale(cl_lightstyle[surf->styles[maps]].colours, scale, scalergb);\n\t\t\t\t\t\t\tbl = blocklights;\n\t\t\t\t\t\t\tfor (i=0 ; i<size ; i++)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t*bl++\t\t+= *src * scalergb[0];\n\t\t\t\t\t\t\t\t*bl++\t\t+= *src * scalergb[1];\n\t\t\t\t\t\t\t\t*bl++\t\t+= *src * scalergb[2];\n\t\t\t\t\t\t\t\tsrc++;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tsrc += size;\t// skip to next lightmap\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// add all the dynamic lights\n\t\tif (surf->dlightframe == r_dlightframecount)\n\t\t\tSurf_AddDynamicLights_RGB (surf);\n\n\t\tSurf_StoreLightmap_RGB(dest, blocklights, smax, tmax, shift, stainsrc, lm);\n\t}\n\telse\n\t{\n\t\t// set to full bright if no light data\n\t\tif (ambient < 0)\n\t\t{\n\t\t\tt = (-1-ambient)*255;\n\t\t\tfor (i=0 ; i<size ; i++)\n\t\t\t\tblocklights[i] = t;\n\t\t\tfor (maps = 0 ; maps < MAXCPULIGHTMAPS ; maps++)\n\t\t\t{\n\t\t\t\tsurf->cached_light[maps] = -1-ambient;\n\t\t\t\tsurf->cached_colour[maps] = 0xff;\n\t\t\t}\n\t\t}\n\t\telse if (r_fullbright.value > 0)\n\t\t{\t//r_fullbright is meant to be a scaler.\n\t\t\tfor (i=0 ; i<size ; i++)\n\t\t\t\tblocklights[i] = r_fullbright.value*255*256;\n\t\t\tif (!surf->samples)\n\t\t\t{\n\t\t\t\tsurf->cached_light[0] = d_lightstylevalue[0];\n\t\t\t\tsurf->cached_colour[0] = cl_lightstyle[0].colourkey;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tfor (maps = 0 ; maps < MAXCPULIGHTMAPS && surf->styles[maps] != INVALID_LIGHTSTYLE ; maps++)\n\t\t\t\t{\n\t\t\t\t\tsurf->cached_light[maps] = d_lightstylevalue[surf->styles[maps]];\n\t\t\t\t\tsurf->cached_colour[maps] = cl_lightstyle[surf->styles[maps]].colourkey;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse if (!model->lightdata)\n\t\t{\t//no scalers here.\n\t\t\tfor (i=0 ; i<size ; i++)\n\t\t\t\tblocklights[i] = 255*256;\n\t\t\tsurf->cached_light[0] = d_lightstylevalue[0];\n\t\t\tsurf->cached_colour[0] = cl_lightstyle[0].colourkey;\n\t\t}\n\t\t//surfaces with no light data on lit maps are black\n\t\telse if (!surf->samples)\n\t\t{\n\t\t\tfor (i=0 ; i<size*3 ; i++)\n\t\t\t\tblocklights[i] = 0;\n\t\t\tsurf->cached_light[0] = d_lightstylevalue[0];\n\t\t\tsurf->cached_colour[0] = cl_lightstyle[0].colourkey;\n\t\t}\n\t\telse\n\t\t{\n// clear to no light\n\t\t\tfor (i=0 ; i<size ; i++)\n\t\t\t\tblocklights[i] = 0;\n\n// add all the lightmaps\n\t\t\tif (src)\n\t\t\t{\n\t\t\t\tswitch(model->lightmaps.fmt)\n\t\t\t\t{\n\t\t\t\tcase LM_E5BGR9:\n\t\t\t\t\tfor (maps = 0 ; maps < MAXCPULIGHTMAPS && surf->styles[maps] != INVALID_LIGHTSTYLE ; maps++)\n\t\t\t\t\t{\n\t\t\t\t\t\tscale = d_lightstylevalue[surf->styles[maps]];\n\t\t\t\t\t\tsurf->cached_light[maps] = scale;\t// 8.8 fraction\n\t\t\t\t\t\tsurf->cached_colour[maps] = cl_lightstyle[surf->styles[maps]].colourkey;\n\t\t\t\t\t\tfor (i=0 ; i<size ; i++)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tunsigned int lm = ((unsigned int *)src)[i];\n\t\t\t\t\t\t\tblocklights[i] += max3(((lm>>0)&0x1ff),((lm>>9)&0x1ff),((lm>>18)&0x1ff)) * scale * (rgb9e5tab[lm>>27]*(1<<7));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tsrc += size*4;\t// skip to next lightmap\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase LM_RGB8:\n\t\t\t\t\tfor (maps = 0 ; maps < MAXCPULIGHTMAPS && surf->styles[maps] != INVALID_LIGHTSTYLE ; maps++)\n\t\t\t\t\t{\n\t\t\t\t\t\tscale = d_lightstylevalue[surf->styles[maps]];\n\t\t\t\t\t\tsurf->cached_light[maps] = scale;\t// 8.8 fraction\n\t\t\t\t\t\tsurf->cached_colour[maps] = cl_lightstyle[surf->styles[maps]].colourkey;\n\t\t\t\t\t\tfor (i=0 ; i<size ; i++)\n\t\t\t\t\t\t\tblocklights[i] += max3(src[i*3],src[i*3+1],src[i*3+2]) * scale;\n\t\t\t\t\t\tsrc += size*3;\t// skip to next lightmap\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase LM_L8:\n\t\t\t\t\tfor (maps = 0 ; maps < MAXCPULIGHTMAPS && surf->styles[maps] != INVALID_LIGHTSTYLE ; maps++)\n\t\t\t\t\t{\n\t\t\t\t\t\tscale = d_lightstylevalue[surf->styles[maps]];\n\t\t\t\t\t\tsurf->cached_light[maps] = scale;\t// 8.8 fraction\n\t\t\t\t\t\tsurf->cached_colour[maps] = cl_lightstyle[surf->styles[maps]].colourkey;\n\t\t\t\t\t\tfor (i=0 ; i<size ; i++)\n\t\t\t\t\t\t\tblocklights[i] += src[i] * scale;\n\t\t\t\t\t\tsrc += size;\t// skip to next lightmap\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n// add all the dynamic lights\n\t\t\tif (surf->dlightframe == r_dlightframecount)\n\t\t\t\tSurf_AddDynamicLights_Lum (surf);\n\t\t}\n\n\t\tSurf_StoreLightmap_Lum(dest, blocklights, smax, tmax, shift, stainsrc, lm->width);\n\t}\n}\n\n#if defined(THREADEDWORLD) && (defined(Q1BSPS)||defined(Q2BSPS))\nstatic void Surf_BuildLightMap_Worker (model_t *wmodel, msurface_t *surf, int shift, int ambient, int *d_lightstylevalue)\n{\n\tint\t\t\tsmax, tmax;\n\tint\t\t\tt;\n\tint\t\t\ti, j;\n\tsize_t\t\tsize;\n\tlightmapinfo_t *lm = lightmap[surf->lightmaptexturenums[0]];\n\tqbyte\t\t*src;\n\tunsigned\tscalergb[3];\n\tunsigned\tscale;\n\tint\t\t\tmaps;\n\tunsigned\t*bl;\n\tqbyte\t\t*dest;\n\tqbyte\t\t*deluxedest;\n\tstmap\t\t*stainsrc;\n\tglRect_t\t*theRect;\n\n\tstatic size_t maxblocksize;\n\tstatic vec3_t *blocknormals;\n\tstatic unsigned int *blocklights;\n\n\tshift += 7; // increase to base value\n\tsurf->cached_dlight = false;\n\n\tsmax = (surf->extents[0]>>surf->lmshift)+1;\n\ttmax = (surf->extents[1]>>surf->lmshift)+1;\n\tsize = (size_t)smax*tmax;\n\tsrc = surf->samples;\n\n\tif (size > maxblocksize)\n\t{\t//fixme: fill in?\n\t\tmaxblocksize = size;\n\t\tblocknormals = BZ_Realloc(blocknormals, maxblocksize * sizeof(*blocknormals));\t//already a vector\n\t\tblocklights = BZ_Realloc(blocklights, maxblocksize * 3*sizeof(*blocklights));\n\t}\n\n\tdest = lm->lightmaps + (surf->light_t[0] * lm->width + surf->light_s[0]) * lm->pixbytes;\n\tif (!r_stains.value || !surf->stained)\n\t\tstainsrc = NULL;\n\telse\n\t\tstainsrc = lm->stainmaps + (surf->light_t[0] * lm->width + surf->light_s[0]) * 3;\n\n\tif (lm->hasdeluxe)\n\t{\n\t\tlightmapinfo_t *dlm = lightmap[surf->lightmaptexturenums[0]+1];\n\t\tdeluxedest = dlm->lightmaps + (surf->light_t[0] * dlm->width + surf->light_s[0]) * dlm->pixbytes;\n\n\t\tSurf_BuildDeluxMap(wmodel, surf, deluxedest, lm, blocknormals);\n\t}\n\n\tif (lm->fmt != PTI_L8)\n\t{\n\t\t// set to full bright if no light data\n\t\tif (ambient < 0)\n\t\t{\t//abslight for hexen2\n\t\t\tt = (-1-ambient)*255;\n\t\t\tfor (i=0 ; i<size*3 ; i++)\n\t\t\t{\n\t\t\t\tblocklights[i] = t;\n\t\t\t}\n\n\t\t\tfor (maps = 0 ; maps < MAXCPULIGHTMAPS ; maps++)\n\t\t\t{\n\t\t\t\tsurf->cached_light[maps] = -1-ambient;\n\t\t\t\tsurf->cached_colour[maps] = 0xff;\n\t\t\t}\n\t\t}\n\t\telse if (r_fullbright.value>0)\n\t\t{\t//fullbright cheat\n\t\t\tfor (i=0 ; i<size*3 ; i++)\n\t\t\t{\n\t\t\t\tblocklights[i] = r_fullbright.value*255*256;\n\t\t\t}\n\t\t}\n\t\telse if (!wmodel->lightdata)\n\t\t{\t/*fullbright if map is not lit. but not overbright*/\n\t\t\tfor (i=0 ; i<size*3 ; i++)\n\t\t\t{\n\t\t\t\tblocklights[i] = 128*256;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n// clear to no light\n\t\t\tt = ambient;\n\t\t\tif (t == 0)\n\t\t\t\tmemset(blocklights, 0, size*3*sizeof(*bl));\n\t\t\telse\n\t\t\t{\n\t\t\t\tfor (i=0 ; i<size*3 ; i++)\n\t\t\t\t{\n\t\t\t\t\tblocklights[i] = t;\n\t\t\t\t}\n\t\t\t}\n\n// add all the lightmaps\n\t\t\tif (src)\n\t\t\t{\n\t\t\t\tif (wmodel->lightmaps.prebaked)\t//rgb\n\t\t\t\t{\n\t\t\t\t\t/*q3 lightmaps are meant to be pre-built\n\t\t\t\t\tthis code is misguided, and ought never be executed anyway.\n\t\t\t\t\t*/\n\t\t\t\t\tint pixbytes = lm->pixbytes;\n\t\t\t\t\tbl = blocklights;\n\t\t\t\t\tfor (i = 0; i < tmax; i++)\n\t\t\t\t\t{\n\t\t\t\t\t\tfor (j = 0; j < smax; j++)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tbl[0]\t\t= 255*src[(i*pixbytes+j)*3];\n\t\t\t\t\t\t\tbl[1]\t\t= 255*src[(i*pixbytes+j)*3+1];\n\t\t\t\t\t\t\tbl[2]\t\t= 255*src[(i*pixbytes+j)*3+2];\n\t\t\t\t\t\t\tbl+=3;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tSys_Error(\"Surf_BuildLightMap_Worker: q3bsp\");\n\t\t\t\t}\n\t\t\t\telse switch(wmodel->lightmaps.fmt)\n\t\t\t\t{\n\t\t\t\tcase LM_E5BGR9:\n\t\t\t\t\tfor (maps = 0 ; maps < MAXCPULIGHTMAPS && surf->styles[maps] != INVALID_LIGHTSTYLE ; maps++)\n\t\t\t\t\t{\n\t\t\t\t\t\tsurf->cached_light[maps] = scale = d_lightstylevalue[surf->styles[maps]];\t// 8.8 fraction\n\t\t\t\t\t\tsurf->cached_colour[maps] = cl_lightstyle[surf->styles[maps]].colourkey;\n\t\t\t\t\t\tif (scale)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tVectorScale(cl_lightstyle[surf->styles[maps]].colours, scale, scalergb);\n\t\t\t\t\t\t\tfor (i=0 ; i<size ; i++)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tunsigned int l = ((unsigned int*)src)[i];\n\t\t\t\t\t\t\t\tfloat e = rgb9e5tab[l>>27]*(1u<<7);\n\t\t\t\t\t\t\t\tblocklights[i*3+0] += scalergb[0] * e * ((l>> 0)&0x1ff);\n\t\t\t\t\t\t\t\tblocklights[i*3+1] += scalergb[1] * e * ((l>> 9)&0x1ff);\n\t\t\t\t\t\t\t\tblocklights[i*3+2] += scalergb[2] * e * ((l>>18)&0x1ff);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tsrc += size*4;\t// skip to next lightmap\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase LM_RGB8:\n\t\t\t\t\tfor (maps = 0 ; maps < MAXCPULIGHTMAPS && surf->styles[maps] != INVALID_LIGHTSTYLE ; maps++)\n\t\t\t\t\t{\n\t\t\t\t\t\tsurf->cached_light[maps] = scale = d_lightstylevalue[surf->styles[maps]];\n\t\t\t\t\t\tsurf->cached_colour[maps] = cl_lightstyle[surf->styles[maps]].colourkey;\n\t\t\t\t\t\tif (scale)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tVectorScale(cl_lightstyle[surf->styles[maps]].colours, scale, scalergb);\n\t\t\t\t\t\t\tbl = blocklights;\n\t\t\t\t\t\t\tfor (i=0 ; i<size ; i++)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t*bl++\t\t+=   *src++ * scalergb[0];\n\t\t\t\t\t\t\t\t*bl++\t\t+=   *src++ * scalergb[1];\n\t\t\t\t\t\t\t\t*bl++\t\t+=   *src++ * scalergb[2];\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tsrc += size*3;\t// skip to next lightmap\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase LM_L8:\n\t\t\t\t\tfor (maps = 0 ; maps < MAXCPULIGHTMAPS && surf->styles[maps] != INVALID_LIGHTSTYLE ;\n\t\t\t\t\t\t maps++)\n\t\t\t\t\t{\n\t\t\t\t\t\tsurf->cached_light[maps] = scale = d_lightstylevalue[surf->styles[maps]];\t// 8.8 fraction\n\t\t\t\t\t\tsurf->cached_colour[maps] = cl_lightstyle[surf->styles[maps]].colourkey;\n\t\t\t\t\t\tif (scale)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tVectorScale(cl_lightstyle[surf->styles[maps]].colours, scale, scalergb);\n\t\t\t\t\t\t\tbl = blocklights;\n\t\t\t\t\t\t\tfor (i=0 ; i<size ; i++)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t*bl++\t\t+= *src * scalergb[0];\n\t\t\t\t\t\t\t\t*bl++\t\t+= *src * scalergb[1];\n\t\t\t\t\t\t\t\t*bl++\t\t+= *src * scalergb[2];\n\t\t\t\t\t\t\t\tsrc++;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tsrc += size;\t// skip to next lightmap\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (!r_stains.value || !surf->stained)\n\t\t\tstainsrc = NULL;\n\n\t\tSurf_StoreLightmap_RGB(dest, blocklights, smax, tmax, shift, stainsrc, lm);\n\t}\n\telse\n\t{\n\t// set to full bright if no light data\n\t\tif (r_fullbright.ival)\n\t\t{\n\t\t\tfor (i=0 ; i<size ; i++)\n\t\t\t\tblocklights[i] = 255*256;\n\t\t}\n\t\telse if (!wmodel->lightdata)\n\t\t{\n\t\t\tfor (i=0 ; i<size*3 ; i++)\n\t\t\t{\n\t\t\t\tblocklights[i] = 255*256;\n\t\t\t}\n\t\t\tsurf->cached_light[0] = d_lightstylevalue[0];\n\t\t\tsurf->cached_colour[0] = cl_lightstyle[0].colourkey;\n\t\t}\n\t\telse\n\t\t{\n// clear to no light\n\t\t\tfor (i=0 ; i<size ; i++)\n\t\t\t\tblocklights[i] = ambient;\n\n// add all the lightmaps\n\t\t\tif (src)\n\t\t\t{\n\t\t\t\tswitch(wmodel->lightmaps.fmt)\n\t\t\t\t{\n\t\t\t\tcase LM_E5BGR9:\n\t\t\t\t\tfor (maps = 0 ; maps < MAXCPULIGHTMAPS && surf->styles[maps] != INVALID_LIGHTSTYLE ; maps++)\n\t\t\t\t\t{\n\t\t\t\t\t\tscale = d_lightstylevalue[surf->styles[maps]];\n\t\t\t\t\t\tsurf->cached_light[maps] = scale;\t// 8.8 fraction\n\t\t\t\t\t\tsurf->cached_colour[maps] = cl_lightstyle[surf->styles[maps]].colourkey;\n\t\t\t\t\t\tif (scale)\n\t\t\t\t\t\t\tfor (i=0 ; i<size ; i++)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tunsigned int lm = ((unsigned int *)lightmap)[i];\n\t\t\t\t\t\t\t\tblocklights[i] += max3(((lm>>0)&0x1ff),((lm>>9)&0x1ff),((lm>>18)&0x1ff)) * scale * (rgb9e5tab[lm>>27]*(1<<7));\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\tlightmap += size*4;\t// skip to next lightmap\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase LM_RGB8:\n\t\t\t\t\tfor (maps = 0 ; maps < MAXCPULIGHTMAPS && surf->styles[maps] != INVALID_LIGHTSTYLE ; maps++)\n\t\t\t\t\t{\n\t\t\t\t\t\tscale = d_lightstylevalue[surf->styles[maps]];\n\t\t\t\t\t\tsurf->cached_light[maps] = scale;\t// 8.8 fraction\n\t\t\t\t\t\tsurf->cached_colour[maps] = cl_lightstyle[surf->styles[maps]].colourkey;\n\t\t\t\t\t\tif (scale)\n\t\t\t\t\t\t\tfor (i=0 ; i<size ; i++)\n\t\t\t\t\t\t\t\tblocklights[i] += max3(src[i*3],src[i*3+1],src[i*3+2]) * scale;\n\t\t\t\t\t\tsrc += size*3;\t// skip to next lightmap\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase LM_L8:\n\t\t\t\t\tfor (maps = 0 ; maps < MAXCPULIGHTMAPS && surf->styles[maps] != INVALID_LIGHTSTYLE ; maps++)\n\t\t\t\t\t{\n\t\t\t\t\t\tscale = d_lightstylevalue[surf->styles[maps]];\n\t\t\t\t\t\tsurf->cached_light[maps] = scale;\t// 8.8 fraction\n\t\t\t\t\t\tsurf->cached_colour[maps] = cl_lightstyle[surf->styles[maps]].colourkey;\n\t\t\t\t\t\tif (scale)\n\t\t\t\t\t\t\tfor (i=0 ; i<size ; i++)\n\t\t\t\t\t\t\t\tblocklights[i] += src[i] * scale;\n\t\t\t\t\t\tsrc += size;\t// skip to next lightmap\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tSurf_StoreLightmap_Lum(dest, blocklights, smax, tmax, shift, stainsrc, lm->width);\n\t}\n\n\t//make sure we flag the output rect properly.\n\ttheRect = &lm->rectchange;\n\tif (theRect->t > surf->light_t[0])\n\t\ttheRect->t = surf->light_t[0];\n\tif (theRect->l > surf->light_s[0])\n\t\ttheRect->l = surf->light_s[0];\n\tif (theRect->r < surf->light_s[0]+smax)\n\t\ttheRect->r = surf->light_s[0]+smax;\n\tif (theRect->b < surf->light_t[0]+tmax)\n\t\ttheRect->b = surf->light_t[0]+tmax;\n\tlm->modified = true;\n\n\tif (lm->hasdeluxe)\n\t{\n\t\tlightmapinfo_t *dlm = lm+1;\n\t\ttheRect = &dlm->rectchange;\n\t\tif (theRect->t > surf->light_t[0])\n\t\t\ttheRect->t = surf->light_t[0];\n\t\tif (theRect->l > surf->light_s[0])\n\t\t\ttheRect->l = surf->light_s[0];\n\t\tif (theRect->r < surf->light_s[0]+smax)\n\t\t\ttheRect->r = surf->light_s[0]+smax;\n\t\tif (theRect->b < surf->light_t[0]+tmax)\n\t\t\ttheRect->b = surf->light_t[0]+tmax;\n\t\tdlm->modified = true;\n\t}\n}\n#endif\n\n/*\n=============================================================\n\n\tBRUSH MODELS\n\n=============================================================\n*/\n\n/*\n================\nR_RenderDynamicLightmaps\nMultitexture\n================\n*/\nvoid Surf_RenderDynamicLightmaps (msurface_t *fa)\n{\n\tint\t\t\tmaps;\n\n\t//surfaces without lightmaps\n\tif (fa->lightmaptexturenums[0]<0 || !lightmap)\n\t\treturn;\n\n\t// check for lightmap modification\n\tif (!fa->samples)\n\t{\n\t\tif (fa->cached_light[0] != d_lightstylevalue[0]\n\t\t\t|| fa->cached_colour[0] != cl_lightstyle[0].colourkey)\n\t\t\tgoto dynamic;\n\t}\n\telse\n\t{\n\t\tfor (maps = 0 ; maps < MAXCPULIGHTMAPS && fa->styles[maps] != INVALID_LIGHTSTYLE ;\n\t\t\t maps++)\n\t\t\tif (d_lightstylevalue[fa->styles[maps]] != fa->cached_light[maps]\n\t\t\t\t|| cl_lightstyle[fa->styles[maps]].colourkey != fa->cached_colour[maps])\n\t\t\t\tgoto dynamic;\n\t}\n\n\tif (fa->dlightframe == r_dlightframecount\t// dynamic this frame\n\t\t|| fa->cached_dlight)\t\t\t// dynamic previously\n\t{\n\t\tRSpeedLocals();\ndynamic:\n\t\tRSpeedRemark();\n\n#ifdef _DEBUG\n\t\tif ((unsigned)fa->lightmaptexturenums[0] >= numlightmaps)\n\t\t\tSys_Error(\"Invalid lightmap index\\n\");\n#endif\n\n\t\tSurf_BuildLightMap (currentmodel, fa, 0, lightmap_shift, r_ambient.value*255, d_lightstylevalue);\n\n\t\tRSpeedEnd(RSPEED_DYNAMIC);\n\t}\n}\n\n#if defined(THREADEDWORLD) && (defined(Q1BSPS)||defined(Q2BSPS))\nstatic void Surf_RenderDynamicLightmaps_Worker (model_t *wmodel, msurface_t *fa, int *d_lightstylevalue)\n{\n\tint\t\t\tmaps;\n\n\t//surfaces without lightmaps\n\tif (fa->lightmaptexturenums[0]<0 || !lightmap)\n\t\treturn;\n\n\t// check for lightmap modification\n\tif (!fa->samples)\n\t{\n\t\tif (fa->cached_light[0] != d_lightstylevalue[0]\n\t\t\t|| fa->cached_colour[0] != cl_lightstyle[0].colourkey)\n\t\t\tgoto dynamic;\n\t}\n\telse\n\t{\n\t\tfor (maps = 0 ; maps < MAXCPULIGHTMAPS && fa->styles[maps] != INVALID_LIGHTSTYLE ;\n\t\t\t maps++)\n\t\t\tif (d_lightstylevalue[fa->styles[maps]] != fa->cached_light[maps]\n\t\t\t\t|| cl_lightstyle[fa->styles[maps]].colourkey != fa->cached_colour[maps])\n\t\t\t\tgoto dynamic;\n\t}\n\n\treturn;\n\ndynamic:\n\n#ifdef _DEBUG\n\tif ((unsigned)fa->lightmaptexturenums[0] >= numlightmaps)\n\t{\n\t\tstatic float throttle;\n\t\tCon_ThrottlePrintf(&throttle, 0, CON_WARNING\"Invalid lightmap index\\n\");\n\t\treturn;\n\t}\n#endif\n\n\n\tSurf_BuildLightMap_Worker (wmodel, fa, lightmap_shift, r_ambient.value*255, d_lightstylevalue);\n}\n#endif //THREADEDWORLD\n\nvoid Surf_RenderAmbientLightmaps (msurface_t *fa, int ambient)\n{\n\tif (!fa->mesh)\n\t\treturn;\n\n\t//surfaces without lightmaps\n\tif (fa->lightmaptexturenums[0]<0)\n\t\treturn;\n\n\tif (fa->cached_light[0] != ambient || fa->cached_colour[0] != 0xff)\n\t\tgoto dynamic;\n\n\tif (fa->dlightframe == r_dlightframecount\t// dynamic this frame\n\t\t|| fa->cached_dlight)\t\t\t// dynamic previously\n\t{\n\t\tRSpeedLocals();\ndynamic:\n\t\tRSpeedRemark();\n\n\t\tSurf_BuildLightMap (currentmodel, fa, 0, lightmap_shift, -1-ambient, d_lightstylevalue);\n\n\t\tRSpeedEnd(RSPEED_DYNAMIC);\n\t}\n}\n\n/*\n=============================================================\n\n\tWORLD MODEL\n\n=============================================================\n*/\n\n\n\nstatic void Surf_PushChains(batch_t **batches)\n{\n\tbatch_t *batch;\n\tint i;\n\n\tif (r_refdef.recurse == R_MAX_RECURSE)\n\t\tSys_Error(\"Recursed too deep\\n\");\n\n\tif (!r_refdef.recurse)\n\t{\n\t\tfor (i = 0; i < SHADER_SORT_COUNT; i++)\n\t\tfor (batch = batches[i]; batch; batch = batch->next)\n\t\t{\n\t\t\tbatch->firstmesh = 0;\n\t\t}\n\t}\n#if R_MAX_RECURSE > 2\n\telse if (r_refdef.recurse > 1)\n\t{\n\t\tfor (i = 0; i < SHADER_SORT_COUNT; i++)\n\t\tfor (batch = batches[i]; batch; batch = batch->next)\n\t\t{\n\t\t\tbatch->recursefirst[r_refdef.recurse] = batch->firstmesh;\n\t\t\tbatch->firstmesh = batch->meshes;\n\t\t}\n\t}\n#endif\n\telse\n\t{\n\t\tfor (i = 0; i < SHADER_SORT_COUNT; i++)\n\t\tfor (batch = batches[i]; batch; batch = batch->next)\n\t\t{\n\t\t\tbatch->firstmesh = batch->meshes;\n\t\t}\n\t}\n}\nstatic void Surf_PopChains(batch_t **batches)\n{\n\tbatch_t *batch;\n\tint i;\n\n\tif (!r_refdef.recurse)\n\t{\n\t\tfor (i = 0; i < SHADER_SORT_COUNT; i++)\n\t\tfor (batch = batches[i]; batch; batch = batch->next)\n\t\t{\n\t\t\tbatch->meshes = 0;\n\t\t}\n\t}\n#if R_MAX_RECURSE > 2\n\telse if (r_refdef.recurse > 1)\n\t{\n\t\tfor (i = 0; i < SHADER_SORT_COUNT; i++)\n\t\tfor (batch = batches[i]; batch; batch = batch->next)\n\t\t{\n\t\t\tbatch->meshes = batch->firstmesh;\n\t\t\tbatch->firstmesh = batch->recursefirst[r_refdef.recurse];\n\t\t}\n\t}\n#endif\n\telse\n\t{\n\t\tfor (i = 0; i < SHADER_SORT_COUNT; i++)\n\t\tfor (batch = batches[i]; batch; batch = batch->next)\n\t\t{\n\t\t\tbatch->meshes = batch->firstmesh;\n\t\t\tbatch->firstmesh = 0;\n\t\t}\n\t}\n}\n\n//most of this is a direct copy from gl\nvoid Surf_SetupFrame(void)\n{\n\tvec3_t\tpvsorg;\n\tint viewcontents;\n\n\tif (!cl.worldmodel || cl.worldmodel->loadstate!=MLS_LOADED)\n\t\tr_refdef.flags |= RDF_NOWORLDMODEL;\n\n\tR_AnimateLight();\n\n\tif (r_refdef.recurse)\n\t{\n\t\tVectorCopy(r_refdef.pvsorigin, pvsorg);\n\t}\n\telse\n\t{\n\t\tVectorCopy(r_refdef.vieworg, pvsorg);\n\t\tR_UpdateHDR(r_refdef.vieworg);\n\t}\n\n\tr_viewarea = 0;\n\tviewcontents = 0;\n\tif (r_refdef.flags & RDF_NOWORLDMODEL)\n\t{\n\t}\n\telse if (cl.worldmodel && cl.worldmodel->loadstate == MLS_LOADED && cl.worldmodel->funcs.InfoForPoint)\n\t{\n\t\tvec3_t\ttemp;\n\t\tunsigned int cont2;\n\t\tint area2;\n\t\tcl.worldmodel->funcs.InfoForPoint (cl.worldmodel, pvsorg, &r_viewarea, &r_viewcluster, &viewcontents);\n\t\t// check above and below so crossing solid water doesn't draw wrong\n\t\tif (!viewcontents)\n\t\t{\t// look down a bit\n\t\t\tVectorCopy (pvsorg, temp);\n\t\t\ttemp[2] -= 16;\n\t\t\tcl.worldmodel->funcs.InfoForPoint (cl.worldmodel, temp, &area2, &r_viewcluster2, &cont2);\n\t\t\tif (cont2 & FTECONTENTS_SOLID)\n\t\t\t\tr_viewcluster2 = r_viewcluster;\n\t\t}\n\t\telse\n\t\t{\t// look up a bit\n\t\t\tVectorCopy (pvsorg, temp);\n\t\t\ttemp[2] += 16;\n\t\t\tcl.worldmodel->funcs.InfoForPoint (cl.worldmodel, temp, &area2, &r_viewcluster2, &cont2);\n\t\t\tif (cont2 & FTECONTENTS_SOLID)\n\t\t\t\tr_viewcluster2 = r_viewcluster;\n\t\t}\n\t}\n\telse\n\t{\n\t\tr_viewcluster = -1;\n\t\tr_viewcluster2 = -1;\n\t}\n\n#ifdef TERRAIN\n\tif (!(r_refdef.flags & RDF_NOWORLDMODEL) && cl.worldmodel && cl.worldmodel->terrain)\n\t{\n\t\tviewcontents |= Heightmap_PointContents(cl.worldmodel, NULL, pvsorg);\n\t}\n#endif\n\n\t/*pick up any extra water entities*/\n\t{\n\t\tvec3_t t1,t2;\n\t\tVectorCopy(pmove.player_mins, t1);\n\t\tVectorCopy(pmove.player_maxs, t2);\n\t\tVectorClear(pmove.player_maxs);\n\t\tVectorClear(pmove.player_mins);\n\t\tviewcontents |= PM_ExtraBoxContents(pvsorg);\n\t\tVectorCopy(t1, pmove.player_mins);\n\t\tVectorCopy(t2, pmove.player_maxs);\n\t}\n\tif (!r_refdef.recurse)\n\t{\n\t\tr_viewcontents = viewcontents;\n\t\tif (!r_secondaryview)\n\t\t\tV_SetContentsColor (viewcontents);\n\t}\n\n\n\tif (r_refdef.playerview->audio.defaulted)\n\t{\n\t\t//first scene is the 'main' scene and audio defaults to that (unless overridden later in the frame)\n\t\tr_refdef.playerview->audio.defaulted = false;\n\t\tr_refdef.playerview->audio.entnum = r_refdef.playerview->viewentity;\n\t\tVectorCopy(r_refdef.vieworg, r_refdef.playerview->audio.origin);\n\t\tAngleVectors(r_refdef.viewangles, r_refdef.playerview->audio.forward,r_refdef.playerview->audio.right, r_refdef.playerview->audio.up);\n//\t\tI'm fed up of openal users getting audio bugs when underwater.\n//\t\tif (r_viewcontents & FTECONTENTS_FLUID)\n//\t\t\tr_refdef.playerview->audio.reverbtype = 1;\n//\t\telse\n\t\t\tr_refdef.playerview->audio.reverbtype = 0;\n\t\tVectorCopy(r_refdef.playerview->simvel, r_refdef.playerview->audio.velocity);\n\t}\n}\n\n/*\nstatic mesh_t *surfbatchmeshes[256];\nstatic void Surf_BuildBrushBatch(batch_t *batch)\n{\n\tmodel_t *model = batch->ent->model;\n\tunsigned int i;\n\tbatch->mesh = surfbatchmeshes;\n\tbatch->meshes = batch->surf_count;\n\tfor (i = 0; i < batch->surf_count; i++)\n\t{\n\t\tsurfbatchmeshes[i] = model->surfaces[batch->surf_first + i].mesh;\n\t}\n}\n*/\n\nvoid Surf_GenBrushBatches(batch_t **batches, entity_t *ent)\n{\n\tint i;\n\tmsurface_t *s;\n\tbatch_t *ob;\n\tmodel_t *model;\n\tbatch_t *b;\n\tunsigned int bef;\n\n\tmodel = ent->model;\n\n\tif (R_CullEntityBox (ent, model->mins, model->maxs))\n\t\treturn;\n\n#ifdef RTLIGHTS\n\tif (BE_LightCullModel(ent->origin, model))\n\t\treturn;\n#endif\n\n// calculate dynamic lighting for bmodel if it's not an\n// instanced model\n\tif (!model->lightmaps.prebaked && lightmap && !(webo_blocklightmapupdates&1))\n\t{\n\t\tint k;\n\n\t\tcurrententity = ent;\n\t\tcurrentmodel = ent->model;\n\t\tif (model->nummodelsurfaces != 0 && r_dlightlightmaps && model->funcs.MarkLights)\n\t\t{\n\t\t\tfor (k=rtlights_first; k<RTL_FIRST; k++)\n\t\t\t{\n\t\t\t\tif (!cl_dlights[k].radius)\n\t\t\t\t\tcontinue;\n\t\t\t\tif (!(cl_dlights[k].flags & LFLAG_LIGHTMAP))\n\t\t\t\t\tcontinue;\n\t\t\t\tif ((cl_dlights[k].flags & LFLAG_NORMALMODE) && r_shadow_realtime_dlight.ival)\n\t\t\t\t\tcontinue;\n\t\t\t\tif ((cl_dlights[k].flags & LFLAG_REALTIMEMODE) && r_shadow_realtime_world.ival)\n\t\t\t\t\tcontinue;\n\t\t\t\tmodel->funcs.MarkLights (&cl_dlights[k], (dlightbitmask_t)1<<k, model->rootnode);\n\t\t\t}\n\t\t}\n\n\t\tSurf_LightmapShift(model);\n#ifdef HEXEN2\n\t\tif ((ent->drawflags & MLS_MASK) == MLS_ABSLIGHT)\n\t\t{\n\t\t\t//update lightmaps.\n\t\t\tfor (s = model->surfaces+model->firstmodelsurface,i = 0; i < model->nummodelsurfaces; i++, s++)\n\t\t\t\tSurf_RenderAmbientLightmaps (s, ent->abslight);\n\t\t}\n\t\telse if (ent->drawflags & DRF_TRANSLUCENT)\n\t\t{\n\t\t\t//update lightmaps.\n\t\t\tfor (s = model->surfaces+model->firstmodelsurface,i = 0; i < model->nummodelsurfaces; i++, s++)\n\t\t\t\tSurf_RenderAmbientLightmaps (s, 255);\n\t\t}\n\t\telse\n#endif\n\t\t{\n\t\t\t//update lightmaps.\n\t\t\tfor (s = model->surfaces+model->firstmodelsurface,i = 0; i < model->nummodelsurfaces; i++, s++)\n\t\t\t\tSurf_RenderDynamicLightmaps (s);\n\t\t}\n\t\tcurrententity = NULL;\n\t}\n\n#ifdef BEF_PUSHDEPTH\n\tif (r_pushdepth && model->submodelof == r_worldentity.model)\n\t\tbef = BEF_PUSHDEPTH;\n\telse\n\t\tbef = 0;\n#else\n\tbef = 0;\n#endif\n\tif (ent->flags & RF_ADDITIVE)\n\t\tbef |= BEF_FORCEADDITIVE;\n#ifdef HEXEN2\n\telse if ((ent->drawflags & DRF_TRANSLUCENT) && r_wateralpha.value != 1)\n\t{\n\t\tbef |= BEF_FORCETRANSPARENT;\n\t\tent->shaderRGBAf[3] = r_wateralpha.value;\n\t}\n#endif\n\telse if ((ent->flags & RF_TRANSLUCENT) && cls.protocol != CP_QUAKE3)\n\t\tbef |= BEF_FORCETRANSPARENT;\n\tif (ent->flags & RF_NODEPTHTEST)\n\t\tbef |= BEF_FORCENODEPTH;\n\tif (ent->flags & RF_NOSHADOW)\n\t\tbef |= BEF_NOSHADOWS;\n\n\tfor (i = 0; i < SHADER_SORT_COUNT; i++)\n\tfor (ob = model->batches[i]; ob; ob = ob->next)\n\t{\n\t\tb = BE_GetTempBatch();\n\t\tif (!b)\n\t\t\tcontinue;\n\t\t*b = *ob;\n\t\tif (b->vbo && b->maxmeshes)\n\t\t{\n\t\t\tb->user.meshbuf = *b->mesh[0];\n\t\t\tb->user.meshbuf.numindexes = b->mesh[b->maxmeshes-1]->indexes+b->mesh[b->maxmeshes-1]->numindexes-b->mesh[0]->indexes;\n\t\t\tb->user.meshbuf.numvertexes = b->mesh[b->maxmeshes-1]->xyz_array+b->mesh[b->maxmeshes-1]->numvertexes-b->mesh[0]->xyz_array;\n\n\t\t\tb->mesh = &b->user.meshptr;\n\t\t\tb->user.meshptr = &b->user.meshbuf;\n\t\t\tb->meshes = b->maxmeshes = 1;\n\t\t}\n\t\telse\n\t\t{\n//\t\tif (b->texture)\n//\t\t\tb->shader = R_TextureAnimation(ent->framestate.g[FS_REG].frame[0], b->texture)->shader;\n\t\t\tb->meshes = b->maxmeshes;\n\t\t}\n\t\tb->ent = ent;\n\t\tb->flags = bef;\n\n\t\tif (b->buildmeshes)\n\t\t\tb->buildmeshes(b);\n\n\t\tif (!b->shader)\n\t\t\tb->shader = R_TextureAnimation(ent->framestate.g[FS_REG].frame[0], b->texture)->shader;\n\n\t\tif (bef & BEF_FORCEADDITIVE && b->shader->sort==SHADER_SORT_OPAQUE)\n\t\t{\n\t\t\tb->next = batches[SHADER_SORT_ADDITIVE];\n\t\t\tbatches[SHADER_SORT_ADDITIVE] = b;\n\t\t}\n\t\telse if (bef & BEF_FORCETRANSPARENT && b->shader->sort==SHADER_SORT_OPAQUE)\n\t\t{\n\t\t\tb->next = batches[SHADER_SORT_BLEND];\n\t\t\tbatches[SHADER_SORT_BLEND] = b;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tb->next = batches[b->shader->sort];\n\t\t\tbatches[b->shader->sort] = b;\n\t\t}\n\t}\n}\n\n#ifdef THREADEDWORLD\nstruct webostate_s\n{\n\tchar dbgid[12];\n\tstruct webostate_s *next;\n\tint lastvalid;\t//keyed to cls.framecount, for cleaning up.\n\tmodel_t *wmodel;\n\tint framecount;\n\tint cluster[2];\n\tqboolean generating;\n\tpvsbuffer_t pvs;\n\tvboarray_t ebo;\n\tvboarray_t vbo;\n\tvoid *ebomem;\n\tsize_t idxcount;\n\tint numbatches;\n\tqbyte areamask[MAX_Q2MAP_AREAS/8];\n\tint lightstylevalues[MAX_NET_LIGHTSTYLES];\t//when using workers that only reprocessing lighting at 10fps, things get too ugly when things go out of sync\n\n//TODO\tqbyte *bakedsubmodels;\t//flags saying whether each submodel was baked or not. baked submodels need to be untinted uncaled unrotated at origin etc\n\n\tvec3_t lastpos;\t//for better stale ebo selection when we're generating a new position.\n\n\tbatch_t *rbatches[SHADER_SORT_COUNT];\n\n\tstruct wesbatch_s\n\t{\n\t\tqboolean inefficient;\t//this batch's shader needs special care with vertex data too\n\t\tsize_t numidx;\n\t\tsize_t maxidx;\n\t\tsize_t firstidx;\t//offset into the final ebo\n\t\tindex_t *idxbuffer;\n\t\tbatch_t b;\n\t\tmesh_t m;\n\t\tmesh_t *pm;\n\t\tvbo_t vbo;\n\n\t\tsize_t maxverts;\n\t} batches[1];\n};\nstatic struct webostate_s *webostates;\nstatic struct webostate_s *webogenerating;\nstatic int webogeneratingstate;\t//1 if generating, 0 if not, for waiting for sync.\nstatic void R_DestroyWorldEBO(struct webostate_s *es)\n{\n\tint i;\n\tif (!es)\n\t\treturn;\n\n\tfor (i = 0; i < es->numbatches; i++)\n\t{\n\t\tif (es->batches[i].inefficient)\n\t\t{\n\t\t\tBZ_Free(es->batches[i].m.xyz_array);\n\t\t\tBZ_Free(es->batches[i].m.st_array);\n\t\t\tBZ_Free(es->batches[i].m.lmst_array[0]);\n\t\t\tBZ_Free(es->batches[i].m.normals_array);\n\t\t\tBZ_Free(es->batches[i].m.snormals_array);\n\t\t\tBZ_Free(es->batches[i].m.tnormals_array);\n\t\t}\n\t\tBZ_Free(es->batches[i].idxbuffer);\n\t}\n\n#ifdef GLQUAKE\n\tif (qrenderer == QR_OPENGL)\n\t{\n\t\tif (es->ebo.gl.vbo)\n\t\t\tqglDeleteBuffersARB(1, &es->ebo.gl.vbo);\n\t\tif (es->vbo.gl.vbo)\n\t\t\tqglDeleteBuffersARB(1, &es->vbo.gl.vbo);\n\t}\n#endif\n#ifdef VKQUAKE\n\tif (qrenderer == QR_VULKAN)\n\t\tBE_VBO_Destroy(&es->ebo, es->ebomem);\n#endif\n\tBZ_Free(es);\n}\nvoid R_GeneratedWorldEBO(void *ctx, void *data, size_t a_, size_t b_)\n{\n\tdouble starttime = Sys_DoubleTime();\n\tsize_t idxcount, vertcount;\n\tunsigned int i;\n\tmodel_t *mod;\n\tbatch_t *b, *batch;\n\tmesh_t *m;\n\tint sortid;\n\tstruct webostate_s *webostate = ctx;\n\twebostate->next = webostates;\n\twebostates = webostate;\n\twebogenerating = NULL;\n\twebogeneratingstate = 0;\n\twebo_blocklightmapupdates = 1;\n\tmod = webostate->wmodel;\n\n\twebostate->lastvalid = cls.framecount;\n\n\tfor (i = 0, idxcount = 0, vertcount = 0; i < webostate->numbatches; i++)\n\t{\n\t\tidxcount += webostate->batches[i].numidx;\n\t\tvertcount += webostate->batches[i].m.numvertexes;\n\t}\n#ifdef GLQUAKE\n\tif (qrenderer == QR_OPENGL)\n\t{\n\t\tGL_DeselectVAO();\n\n\t\tif (vertcount)\n\t\t{\n\t\t\tsize_t vc;\n\t\t\tvbo_t *vbo;\n\t\t\tsize_t v_coord\t= 0;\n\t\t\tsize_t v_tc\t\t= v_coord\t+ sizeof(vecV_t)*vertcount;\n\t\t\tsize_t v_lmtc\t= v_tc\t\t+ sizeof(vec2_t)*vertcount;\n\t\t\tsize_t v_norm\t= v_lmtc\t+ sizeof(vec2_t)*vertcount;\n\t\t\tsize_t v_snorm\t= v_norm\t+ sizeof(vec3_t)*vertcount;\n\t\t\tsize_t v_tnorm\t= v_snorm\t+ sizeof(vec3_t)*vertcount;\n\t\t\tsize_t v_colour\t= v_tnorm\t+ sizeof(vec3_t)*vertcount;\n\t\t\tsize_t vbosize\t= v_colour\t+ sizeof(vec4_t)*vertcount;\n\n\t\t\tif (!webostate->vbo.gl.vbo)\n\t\t\t\tqglGenBuffersARB(1, &webostate->vbo.gl.vbo);\n\t\t\tGL_SelectVBO(webostate->vbo.gl.vbo);\n\t\t\tqglBufferDataARB(GL_ARRAY_BUFFER_ARB, vbosize, NULL, GL_STATIC_DRAW_ARB);\n\t\t\tfor (i = 0, vertcount = 0; i < webostate->numbatches; i++)\n\t\t\t{\n\t\t\t\tif (webostate->batches[i].inefficient)\n\t\t\t\t{\n\t\t\t\t\tvc = webostate->batches[i].m.numvertexes;\n\n\t\t\t\t\tvbo = &webostate->batches[i].vbo;\n\t\t\t\t\tvbo->coord.gl.vbo\t\t= webostate->vbo.gl.vbo;\tvbo->coord.gl.addr\t\t= (char*)v_coord\t+ sizeof(vecV_t)*vertcount;\n\t\t\t\t\tvbo->texcoord.gl.vbo\t= webostate->vbo.gl.vbo;\tvbo->texcoord.gl.addr\t= (char*)v_tc\t\t+ sizeof(vec2_t)*vertcount;\n\t\t\t\t\tvbo->lmcoord[0].gl.vbo\t= webostate->vbo.gl.vbo;\tvbo->lmcoord[0].gl.addr = (char*)v_lmtc\t\t+ sizeof(vec2_t)*vertcount;\n\t\t\t\t\tvbo->normals.gl.vbo\t\t= webostate->vbo.gl.vbo;\tvbo->normals.gl.addr\t= (char*)v_norm\t\t+ sizeof(vec3_t)*vertcount;\n\t\t\t\t\tvbo->svector.gl.vbo\t\t= webostate->vbo.gl.vbo;\tvbo->svector.gl.addr\t= (char*)v_snorm\t+ sizeof(vec3_t)*vertcount;\n\t\t\t\t\tvbo->tvector.gl.vbo\t\t= webostate->vbo.gl.vbo;\tvbo->tvector.gl.addr\t= (char*)v_tnorm\t+ sizeof(vec3_t)*vertcount;\n\t\t\t\t\tvbo->colours[0].gl.vbo\t= webostate->vbo.gl.vbo;\tvbo->colours[0].gl.addr\t= (char*)v_colour\t+ sizeof(vec4_t)*vertcount;\n\n\t\t\t\t\tqglBufferSubDataARB(GL_ARRAY_BUFFER_ARB,(qintptr_t)vbo->coord.gl.addr,\t\tvc*sizeof(vecV_t), webostate->batches[i].m.xyz_array);\n\t\t\t\t\tqglBufferSubDataARB(GL_ARRAY_BUFFER_ARB,(qintptr_t)vbo->texcoord.gl.addr,\tvc*sizeof(vec2_t), webostate->batches[i].m.st_array);\n\t\t\t\t\tqglBufferSubDataARB(GL_ARRAY_BUFFER_ARB,(qintptr_t)vbo->lmcoord[0].gl.addr,\tvc*sizeof(vec2_t), webostate->batches[i].m.lmst_array[0]);\n\t\t\t\t\tqglBufferSubDataARB(GL_ARRAY_BUFFER_ARB,(qintptr_t)vbo->normals.gl.addr,\tvc*sizeof(vec3_t), webostate->batches[i].m.normals_array);\n\t\t\t\t\tqglBufferSubDataARB(GL_ARRAY_BUFFER_ARB,(qintptr_t)vbo->svector.gl.addr,\tvc*sizeof(vec3_t), webostate->batches[i].m.snormals_array);\n\t\t\t\t\tqglBufferSubDataARB(GL_ARRAY_BUFFER_ARB,(qintptr_t)vbo->tvector.gl.addr,\tvc*sizeof(vec3_t), webostate->batches[i].m.tnormals_array);\n\t\t\t\t\tqglBufferSubDataARB(GL_ARRAY_BUFFER_ARB,(qintptr_t)vbo->colours[0].gl.addr,\tvc*sizeof(vec4_t), webostate->batches[i].m.colors4f_array[0]);\n\t\t\t\t\twebostate->batches[i].m.vbofirstvert = 0;\n\t\t\t\t\tvertcount += vc;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\twebostate->ebo.gl.addr = NULL;\n\t\tif (!webostate->ebo.gl.vbo)\n\t\t\tqglGenBuffersARB(1, &webostate->ebo.gl.vbo);\n\t\tGL_SelectEBO(webostate->ebo.gl.vbo);\n\t\tqglBufferDataARB(GL_ELEMENT_ARRAY_BUFFER_ARB, idxcount*sizeof(index_t), NULL, GL_STATIC_DRAW_ARB);\n\t\tfor (i = 0, idxcount = 0; i < webostate->numbatches; i++)\n\t\t{\n\t\t\tqglBufferSubDataARB(GL_ELEMENT_ARRAY_BUFFER_ARB, idxcount*sizeof(index_t), webostate->batches[i].numidx*sizeof(index_t), webostate->batches[i].idxbuffer);\n//\t\t\tBZ_Free(webostate->batches[i].idxbuffer);\n//\t\t\twebostate->batches[i].idxbuffer = NULL;\n\t\t\twebostate->batches[i].firstidx = idxcount;\n\t\t\tidxcount += webostate->batches[i].numidx;\n\t\t}\n\t}\n#endif\n#ifdef VKQUAKE\n\tif (qrenderer == QR_VULKAN)\n\t{\t//this malloc is stupid.\n\t\t//with vulkan we really should be doing this on the worker instead, at least the staging part.\n\t\tindex_t *indexes = malloc(sizeof(*indexes) * idxcount);\n\t\tBE_VBO_Destroy(&webostate->ebo, webostate->ebomem);\n\t\tmemset(&webostate->ebo, 0, sizeof(webostate->ebo));\n\t\twebostate->ebomem = NULL;\n\t\twebostate->ebo.vk.offs = 0;\n\t\tfor (i = 0, idxcount = 0; i < webostate->numbatches; i++)\n\t\t{\n\t\t\tmemcpy(indexes + idxcount, webostate->batches[i].idxbuffer, webostate->batches[i].numidx*sizeof(index_t));\n//\t\t\tBZ_Free(webostate->batches[i].idxbuffer);\n//\t\t\twebostate->batches[i].idxbuffer = NULL;\n\t\t\twebostate->batches[i].firstidx = idxcount;\n\t\t\tidxcount += webostate->batches[i].numidx;\n\t\t}\n\t\tif (idxcount)\n\t\t\tBE_VBO_Finish(NULL, indexes, sizeof(*indexes) * idxcount, &webostate->ebo, NULL, &webostate->ebomem);\n\t\telse\n\t\t{\n\t\t\tmemset(&webostate->ebo, 0, sizeof(webostate->ebo));\n\t\t\twebostate->ebomem = NULL;\n\t\t}\n\t\tfree(indexes);\n\n\t\tvertcount = 0; //unsupported for now.\n\t}\n#endif\n\n\t//should be doing this on the worker, but whatever\n\tfor (i = 0, sortid = 0; sortid < SHADER_SORT_COUNT; sortid++)\n\t{\n\t\twebostate->rbatches[sortid] = NULL;\n\t\tfor (batch = mod->batches[sortid]; batch != NULL; batch = batch->next, i++)\n\t\t{\n\t\t\tif (!webostate->batches[i].numidx)\n\t\t\t\tcontinue;\n\n\t\t\tif (batch->shader->flags & SHADER_NODRAW)\n\t\t\t\tcontinue;\n\n\t\t\tm = &webostate->batches[i].m;\n\t\t\twebostate->batches[i].pm = m;\n\t\t\tb = &webostate->batches[i].b;\n\t\t\tmemcpy(b, batch, sizeof(*b));\n\n\t\t\tb->mesh = &webostate->batches[i].pm;\n\t\t\tb->meshes = 1;\n\t\t\tb->vbo = &webostate->batches[i].vbo;\n\t\t\tif (webostate->batches[i].inefficient)\n\t\t\t{\t//we had to generate new buffers because there's something evil in the shader..\n\t\t\t\tm->indexes = webostate->batches[i].idxbuffer;\n\t\t\t\tb->vbo->vao = 0;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t*b->vbo = *batch->vbo;\n\t\t\t\tif (b->shader->flags & SHADER_NEEDSARRAYS)\n\t\t\t\t{\t//this ebo cache stuff tracks only indexes, we don't know the actual surfs any more.\n\t\t\t\t\t//if NEEDSARRAYS is flagged then the cpu will need access to the mesh data - which it doesn't have.\n\t\t\t\t\t//while we could figure out this info, there would be a lot of vertexes that are not referenced, which would be horrendously slow.\n\t\t\t\t\tif (b->shader->flags & SHADER_SKY)\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\tb->shader = R_RegisterShader_Vertex(mod, \"unsupported\");\n\t\t\t\t}\n\t\t\t\tm->numvertexes = webostate->batches[i].b.vbo->vertcount;\n\t\t\t}\n\t\t\tb->vbo->indicies = webostate->ebo;\n\t\t\tb->vbo->vao = 0;\n\t\t\tm->numindexes = webostate->batches[i].numidx;\n\t\t\tm->vbofirstelement = webostate->batches[i].firstidx;\n\n\n\t\t\tb->next = webostate->rbatches[sortid];\n\t\t\twebostate->rbatches[sortid] = b;\n\t\t}\n\t}\n\n\tr_loaderstalltime += Sys_DoubleTime() - starttime;\n}\n#ifdef Q1BSPS\nstatic void Surf_SimpleWorld_Q1BSP(struct webostate_s *es, qbyte *pvs)\n{\n\tmleaf_t\t\t*leaf;\n\tmsurface_t\t*surf, **mark, **end;\n\tmesh_t\t\t*mesh;\n\tmodel_t *wmodel = es->wmodel;\n\tint l = wmodel->numclusters;\n\tint fc = es->framecount;\n\tint i;\n\tint s, f, lastface;\n\tstruct wesbatch_s *eb;\n\tfor (leaf = wmodel->leafs+l; l-- > 0; leaf--)\n\t{\n\t\tif ((pvs[l>>3] & (1u<<(l&7))) && leaf->nummarksurfaces)\n\t\t{\n\t\t\tmark = leaf->firstmarksurface;\n\t\t\tend = mark+leaf->nummarksurfaces;\n\t\t\twhile(mark < end)\n\t\t\t{\n\t\t\t\tsurf = *mark++;\n\t\t\t\tif (surf->visframe != fc)\n\t\t\t\t{\n\t\t\t\t\tsurf->visframe = fc;\n\t\t\t\t\tSurf_RenderDynamicLightmaps_Worker (wmodel, surf, es->lightstylevalues);\n\n\t\t\t\t\tmesh = surf->mesh;\n\t\t\t\t\teb = &es->batches[surf->sbatch->user.bmodel.ebobatch];\n\t\t\t\t\tif (eb->maxidx < eb->numidx + mesh->numindexes)\n\t\t\t\t\t{\n\t\t\t\t\t\t//FIXME: pre-allocate\n\t\t\t\t\t\teb->maxidx = eb->numidx + mesh->numindexes + 512;\n\t\t\t\t\t\teb->idxbuffer = BZ_Realloc(eb->idxbuffer, eb->maxidx * sizeof(index_t));\n\t\t\t\t\t}\n\n\t\t\t\t\tif (eb->inefficient)\n\t\t\t\t\t{\t//slow path that needs to create new VBOs on the fly too.\n\t\t\t\t\t\tif (eb->maxverts < eb->m.numvertexes + mesh->numvertexes)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t//FIXME: pre-allocate\n\t\t\t\t\t\t\teb->maxverts = eb->m.numvertexes + mesh->numvertexes + 512;\n\t\t\t\t\t\t\teb->m.xyz_array\t\t\t= BZ_Realloc(eb->m.xyz_array,\t\t\teb->maxverts * sizeof(*eb->m.xyz_array));\n\t\t\t\t\t\t\teb->m.st_array\t\t\t= BZ_Realloc(eb->m.st_array,\t\t\teb->maxverts * sizeof(*eb->m.st_array));\n\t\t\t\t\t\t\teb->m.lmst_array[0]\t\t= BZ_Realloc(eb->m.lmst_array[0],\t\teb->maxverts * sizeof(*eb->m.lmst_array[0]));\n\t\t\t\t\t\t\teb->m.normals_array\t\t= BZ_Realloc(eb->m.normals_array,\t\teb->maxverts * sizeof(*eb->m.normals_array));\n\t\t\t\t\t\t\teb->m.snormals_array\t= BZ_Realloc(eb->m.snormals_array,\t\teb->maxverts * sizeof(*eb->m.snormals_array));\n\t\t\t\t\t\t\teb->m.tnormals_array\t= BZ_Realloc(eb->m.tnormals_array,\t\teb->maxverts * sizeof(*eb->m.tnormals_array));\n\t\t\t\t\t\t\teb->m.colors4f_array[0]\t= BZ_Realloc(eb->m.colors4f_array[0],\teb->maxverts * sizeof(*eb->m.colors4f_array[0]));\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tmemcpy(eb->m.xyz_array+eb->m.numvertexes,\t\tmesh->xyz_array,\t\tsizeof(*eb->m.xyz_array)*mesh->numvertexes);\n\t\t\t\t\t\tmemcpy(eb->m.st_array+eb->m.numvertexes,\t\tmesh->st_array,\t\t\tsizeof(*eb->m.st_array)*mesh->numvertexes);\n\t\t\t\t\t\tmemcpy(eb->m.lmst_array[0]+eb->m.numvertexes,\tmesh->lmst_array[0],\tsizeof(*eb->m.lmst_array[0])*mesh->numvertexes);\n\t\t\t\t\t\tmemcpy(eb->m.normals_array+eb->m.numvertexes,\tmesh->normals_array,\tsizeof(*eb->m.normals_array)*mesh->numvertexes);\n\t\t\t\t\t\tmemcpy(eb->m.snormals_array+eb->m.numvertexes,\tmesh->snormals_array,\tsizeof(*eb->m.snormals_array)*mesh->numvertexes);\n\t\t\t\t\t\tmemcpy(eb->m.tnormals_array+eb->m.numvertexes,\tmesh->tnormals_array,\tsizeof(*eb->m.tnormals_array)*mesh->numvertexes);\n\t\t\t\t\t\tmemcpy(eb->m.colors4f_array[0]+eb->m.numvertexes,mesh->colors4f_array[0],sizeof(*eb->m.colors4f_array[0])*mesh->numvertexes);\n\n\t\t\t\t\t\tfor (i = 0; i < mesh->numindexes; i++)\n\t\t\t\t\t\t\teb->idxbuffer[eb->numidx+i] = mesh->indexes[i] + eb->m.numvertexes;\n\t\t\t\t\t\teb->m.numvertexes+=mesh->numvertexes;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tfor (i = 0; i < mesh->numindexes; i++)\n\t\t\t\t\t\t\teb->idxbuffer[eb->numidx+i] = mesh->indexes[i] + mesh->vbofirstvert;\n\t\t\t\t\t}\n\t\t\t\t\teb->numidx += mesh->numindexes;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tfor (s = 1; s < wmodel->numsubmodels; s++)\n\t{\n//\t\tif (!es->bakedsubmodels[s])\n//\t\t\tcontinue;\t//not baking this one (not currently visible or something)\n\t\t//FIXME: pvscull it here?\n\t\tlastface = wmodel->submodels[s].firstface + wmodel->submodels[s].numfaces;\n\t\tfor (f = wmodel->submodels[s].firstface; f < lastface; f++)\n\t\t{\n\t\t\tsurf = wmodel->surfaces+f;\n\n\t\t\tSurf_RenderDynamicLightmaps_Worker (wmodel, surf, es->lightstylevalues);\n/*\n\t\t\tmesh = surf->mesh;\n\t\t\teb = &es->batches[surf->sbatch->webobatch];\n\t\t\tif (eb->maxidx < eb->numidx + mesh->numindexes)\n\t\t\t{\n\t\t\t\t//FIXME: pre-allocate\n\t\t\t\teb->maxidx = eb->numidx + surf->mesh->numindexes + 512;\n\t\t\t\teb->idxbuffer = BZ_Realloc(eb->idxbuffer, eb->maxidx * sizeof(index_t));\n\t\t\t}\n\t\t\tfor (i = 0; i < mesh->numindexes; i++)\n\t\t\t\teb->idxbuffer[eb->numidx+i] = mesh->indexes[i] + mesh->vbofirstvert;\n\t\t\teb->numidx += mesh->numindexes;*/\n\t\t}\n\t}\n}\n#endif\n#if defined(Q2BSPS) || defined(Q3BSPS)\nstatic void Surf_SimpleWorld_Q3BSP(struct webostate_s *es, qbyte *pvs)\n{\n\tmleaf_t\t\t*leaf;\n\tmsurface_t\t*surf, **mark, **end;\n\tmesh_t\t\t*mesh;\n\tmodel_t *wmodel = es->wmodel;\n\tint l = wmodel->numleafs;\t//is this doing submodels too?\n\tint c;\n\tint fc = es->framecount;\n\tfor (leaf = wmodel->leafs; l --> 0; leaf++)\n\t{\n\t\tc = leaf->cluster;\n\t\tif (c < 0 || !leaf->parent)\n\t\t\tcontinue;\t//o.O\n\t\tif ((pvs[c>>3] & (1u<<(c&7))) && leaf->nummarksurfaces && (((unsigned)leaf->area>=MAX_Q2MAP_AREAS)||es->areamask[leaf->area>>3]&1<<(leaf->area&7)))\n\t\t{\n\t\t\tmark = leaf->firstmarksurface;\n\t\t\tend = mark+leaf->nummarksurfaces;\n\t\t\twhile(mark < end)\n\t\t\t{\n\t\t\t\tsurf = *mark++;\n\t\t\t\tif (surf->visframe != fc)\n\t\t\t\t{\n\t\t\t\t\tint i;\n\t\t\t\t\tstruct wesbatch_s *eb;\n\t\t\t\t\tsurf->visframe = fc;\n\n\t\t\t\t\tmesh = surf->mesh;\n\t\t\t\t\teb = &es->batches[surf->sbatch->user.bmodel.ebobatch];\n\t\t\t\t\tif (eb->maxidx < eb->numidx + mesh->numindexes)\n\t\t\t\t\t{\n\t\t\t\t\t\t//FIXME: pre-allocate\n\t\t\t\t\t\teb->maxidx = eb->numidx + mesh->numindexes + 512;\n\t\t\t\t\t\teb->idxbuffer = BZ_Realloc(eb->idxbuffer, eb->maxidx * sizeof(index_t));\n\t\t\t\t\t}\n\t\t\t\t\tif (eb->inefficient)\n\t\t\t\t\t{\t//slow path that needs to create a single ram-backed mesh\n\n\t\t\t\t\t\t//FIXME: for portal/refract surfaces, track surfaces for refract pvs info\n\t\t\t\t\t\tif (eb->maxverts < eb->m.numvertexes + mesh->numvertexes)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t//FIXME: pre-allocate\n\t\t\t\t\t\t\teb->maxverts = eb->m.numvertexes + mesh->numvertexes + 512;\n\t\t\t\t\t\t\teb->m.xyz_array\t\t= BZ_Realloc(eb->m.xyz_array,\t\teb->maxverts * sizeof(*eb->m.xyz_array));\n\t\t\t\t\t\t\teb->m.st_array\t\t= BZ_Realloc(eb->m.st_array,\t\teb->maxverts * sizeof(*eb->m.st_array));\n\t\t\t\t\t\t\teb->m.lmst_array[0]\t= BZ_Realloc(eb->m.lmst_array[0],\teb->maxverts * sizeof(*eb->m.lmst_array[0]));\n\t\t\t\t\t\t\teb->m.normals_array\t= BZ_Realloc(eb->m.normals_array,\teb->maxverts * sizeof(*eb->m.normals_array));\n\t\t\t\t\t\t\teb->m.snormals_array= BZ_Realloc(eb->m.snormals_array,\teb->maxverts * sizeof(*eb->m.snormals_array));\n\t\t\t\t\t\t\teb->m.tnormals_array= BZ_Realloc(eb->m.tnormals_array,\teb->maxverts * sizeof(*eb->m.tnormals_array));\n\t\t\t\t\t\t\teb->m.colors4f_array[0]= BZ_Realloc(eb->m.colors4f_array[0],eb->maxverts * sizeof(*eb->m.colors4f_array[0]));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tmemcpy(eb->m.numvertexes+eb->m.xyz_array,\t\tmesh->xyz_array,\t\tsizeof(*eb->m.xyz_array)*mesh->numvertexes);\n\t\t\t\t\t\tmemcpy(eb->m.numvertexes+eb->m.st_array,\t\tmesh->st_array,\t\t\tsizeof(*eb->m.st_array)*mesh->numvertexes);\n\t\t\t\t\t\tmemcpy(eb->m.numvertexes+eb->m.lmst_array[0],\tmesh->lmst_array[0],\tsizeof(*eb->m.lmst_array[0])*mesh->numvertexes);\n\t\t\t\t\t\tmemcpy(eb->m.numvertexes+eb->m.normals_array,\tmesh->normals_array,\tsizeof(*eb->m.normals_array)*mesh->numvertexes);\n\t\t\t\t\t\tmemcpy(eb->m.numvertexes+eb->m.snormals_array,\tmesh->snormals_array,\tsizeof(*eb->m.snormals_array)*mesh->numvertexes);\n\t\t\t\t\t\tmemcpy(eb->m.numvertexes+eb->m.tnormals_array,\tmesh->tnormals_array,\tsizeof(*eb->m.tnormals_array)*mesh->numvertexes);\n\t\t\t\t\t\tmemcpy(eb->m.numvertexes+eb->m.colors4f_array[0],mesh->colors4f_array[0],sizeof(*eb->m.colors4f_array[0])*mesh->numvertexes);\n\n\t\t\t\t\t\tfor (i = 0; i < mesh->numindexes; i++)\n\t\t\t\t\t\t\teb->idxbuffer[eb->numidx+i] = mesh->indexes[i] + eb->m.numvertexes;\n\t\t\t\t\t\teb->m.numvertexes+=mesh->numvertexes;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\t//using the general prebaked entire-batch vbos\n\t\t\t\t\t\tfor (i = 0; i < mesh->numindexes; i++)\n\t\t\t\t\t\t\teb->idxbuffer[eb->numidx+i] = mesh->indexes[i] + mesh->vbofirstvert;\n\t\t\t\t\t}\n\t\t\t\t\teb->numidx += mesh->numindexes;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n#endif\nvoid R_GenWorldEBO(void *ctx, void *data, size_t a, size_t b)\n{\n\tint i;\n\tstruct webostate_s *es = ctx;\n\tqbyte *pvs;\n\n\tint sortid;\n\tbatch_t *batch;\n\tqboolean inefficient;\n\n\tif (!es->numbatches)\n\t{\n\t\tes->numbatches = es->wmodel->numbatches;\n\n\t\tfor (i = 0; i < es->numbatches; i++)\n\t\t{\n\t\t\tes->batches[i].firstidx = 0;\n\t\t\tes->batches[i].numidx = 0;\n\t\t\tes->batches[i].maxidx = 0;\n\t\t\tes->batches[i].idxbuffer = NULL;\n\t\t\tes->batches[i].inefficient = false;\n\n\t\t\tes->batches[i].maxverts = 0;\n\t\t\tmemset(&es->batches[i].m, 0, sizeof(es->batches[i].m));\n\t\t\tmemset(&es->batches[i].vbo, 0, sizeof(es->batches[i].vbo));\n\t\t}\n\t}\n\telse\n\t{\n\t\tfor (i = 0; i < es->numbatches; i++)\n\t\t{\n\t\t\tes->batches[i].firstidx = 0;\n\t\t\tes->batches[i].numidx = 0;\n\t\t\tes->batches[i].m.numvertexes = 0;\n\t\t}\n\t}\n\n\t//set to 2 to reveal the inefficient surfaces...\n\tfor (sortid = 0; sortid < SHADER_SORT_COUNT; sortid++)\n\t\tfor (batch = es->wmodel->batches[sortid]; batch != NULL; batch = batch->next)\n\t\t{\n\t\t\tinefficient = false;\n\t\t\tif (r_temporalscenecache.ival < 2)\n\t\t\t{\n#if MAXRLIGHTMAPS > 1\n\t\t\t\tif (batch->lmlightstyle[1] != INVALID_LIGHTSTYLE || batch->vtlightstyle[1] != INVALID_VLIGHTSTYLE)\n\t\t\t\t\tcontinue;\t//not supported here, show fallback shader instead (would work but with screwed lighting, we prefer a better-defined result).\n#endif\n\t\t\t\tif (!batch->shader)\n\t\t\t\t\tinefficient = true;\n\t\t\t\telse if (batch->shader->flags & SHADER_NEEDSARRAYS)\n\t\t\t\t\tinefficient = true;\n\t\t\t}\n\t\t\tif (es->batches[batch->user.bmodel.ebobatch].inefficient != inefficient)\n\t\t\t{\n\t\t\t\tes->batches[batch->user.bmodel.ebobatch].inefficient = inefficient;\n\t\t\t\tif (!inefficient)\n\t\t\t\t{\n\t\t\t\t\tif (es->batches[i].inefficient)\n\t\t\t\t\t{\n\t\t\t\t\t\tBZ_Free(es->batches[i].m.xyz_array);\n\t\t\t\t\t\tBZ_Free(es->batches[i].m.st_array);\n\t\t\t\t\t\tBZ_Free(es->batches[i].m.lmst_array[0]);\n\t\t\t\t\t\tBZ_Free(es->batches[i].m.normals_array);\n\t\t\t\t\t\tBZ_Free(es->batches[i].m.snormals_array);\n\t\t\t\t\t\tBZ_Free(es->batches[i].m.tnormals_array);\n\t\t\t\t\t}\n\t\t\t\t\tBZ_Free(es->batches[i].idxbuffer);\n\n\t\t\t\t\tmemset(&es->batches[i], 0, sizeof(es->batches[i]));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t//maybe we should just use fatpvs instead, and wait for completion when outside?\n\tif (r_novis.ival)\n\t{\n\t\tif (es->pvs.buffersize < es->wmodel->pvsbytes)\n\t\t\tes->pvs.buffer = BZ_Realloc(es->pvs.buffer, es->pvs.buffersize=es->wmodel->pvsbytes);\n\t\tmemset(es->pvs.buffer, 0xff, es->pvs.buffersize);\n\t\tpvs = es->pvs.buffer;\n\t}\n\telse if (es->cluster[1] != -1 && es->cluster[0] != es->cluster[1])\n\t{\t//view is near to a water boundary. this implies the water crosses the near clip plane. we need both leafs.\n\t\tpvs = es->wmodel->funcs.ClusterPVS(es->wmodel, es->cluster[0], &es->pvs, PVM_REPLACE);\n\t\tpvs = es->wmodel->funcs.ClusterPVS(es->wmodel, es->cluster[1], &es->pvs, PVM_MERGE);\n\t}\n\telse\n\t\tpvs = es->wmodel->funcs.ClusterPVS(es->wmodel, es->cluster[0], &es->pvs, PVM_REPLACE);\n\n#if defined(Q2BSPS) || defined(Q3BSPS)\n\tif (es->wmodel->fromgame == fg_quake2 || es->wmodel->fromgame == fg_quake3)\n\t\tSurf_SimpleWorld_Q3BSP(es, pvs);\n\telse\n#endif\n#ifdef Q1BSPS\n\tif (es->wmodel->fromgame == fg_quake || es->wmodel->fromgame == fg_halflife)\n\t\tSurf_SimpleWorld_Q1BSP(es, pvs);\n\telse\n#endif\n\t{\n\t\t//panic\n\t}\n\n\tCOM_AddWork(WG_MAIN, R_GeneratedWorldEBO, es, NULL, 0, 0);\n}\ncvar_t r_temporalscenecache\t\t\t\t\t= CVARAFD (\"r_temporalscenecache\", \"\", \"r_scenecache\", CVAR_ARCHIVE, \"Controls whether to generate+reuse a scene cache over multiple frames. This is generated on a separate thread to avoid any associated costs. This can significantly boost framerates on complex maps, but can also stress the gpu more (performance tradeoff that varies per map). An outdated cache may be used if the cache takes too long to build (eg: lightmap animations), which could cause the odd glitch when moving fast (but retain more consistent framerates - another tradeoff).\\n0: Tranditional quake rendering.\\n1: Generate+Use the scene cache.\");\n#else\ncvar_t r_temporalscenecache\t\t\t\t\t= CVARAFD (\"r_temporalscenecache\", \"\", \"r_scenecache\", CVAR_NOSET, \"Controls whether to generate+reuse a scene cache over multiple frames. This is generated on a separate thread to avoid any associated costs. This can significantly boost framerates on complex maps, but can also stress the gpu more (performance tradeoff that varies per map). An outdated cache may be used if the cache takes too long to build (eg: lightmap animations), which could cause the odd glitch when moving fast (but retain more consistent framerates - another tradeoff).\\n0: Tranditional quake rendering.\\n1: Generate+Use the scene cache.\");\n#endif\n\n/*\n=============\nR_DrawWorld\n=============\n*/\n\nstatic pvsbuffer_t surf_frustumvis[R_MAX_RECURSE];\nvoid Surf_DrawWorld (void)\n{\n\t//surfvis vs entvis - the key difference is that surfvis is surfaces while entvis is volume. though surfvis should be frustum culled also for lighting. entvis doesn't care.\n\tqbyte *surfvis, *entvis;\n\tint areas[2];\n\tRSpeedLocals();\n\n\tif (r_refdef.flags & RDF_NOWORLDMODEL)\n\t{\n\t\tr_refdef.flags |= RDF_NOWORLDMODEL;\n\t\tr_refdef.scenevis = NULL;\n\t\tBE_DrawWorld(NULL);\n\t\treturn;\n\t}\n\tif (!cl.worldmodel || cl.worldmodel->loadstate != MLS_LOADED)\n\t{\n\t\t/*Don't act as a wallhack*/\n\t\treturn;\n\t}\n\n\tif (!r_refdef.areabitsknown && cl.worldmodel->funcs.WriteAreaBits)\n\t{\t//generate the info each frame, as the gamecode didn't tell us what to use.\n\t\tcl.worldmodel->funcs.WriteAreaBits(cl.worldmodel, r_refdef.areabits, sizeof(r_refdef.areabits), r_viewarea, false);\n\t\tr_refdef.areabitsknown = true;\n\t}\n\n\tcurrentmodel = cl.worldmodel;\n\tcurrententity = &r_worldentity;\n\n\tr_dlightlightmaps = !!r_dynamic.ival;\n\n\t{\n#ifdef THREADEDWORLD\n\t\tint sc = r_temporalscenecache.ival;\n#endif\n\t\tRSpeedRemark();\n\n\t\tSurf_LightmapShift(currentmodel);\n\n#ifdef THREADEDWORLD\n\t\tif (!*r_temporalscenecache.string && cl.worldmodel && cl.worldmodel->loadstate == MLS_LOADED && (cl.worldmodel->fromgame == fg_quake || cl.worldmodel->fromgame == fg_halflife))\n\t\t{\t//when empty, pick a suitable default.\n\t\t\t//at what point is it a win? should we consider batch counts? probability of offscreen-only surfaces?\n\t\t\tif (cl.worldmodel->fromgame == fg_quake || cl.worldmodel->fromgame == fg_halflife)\n\t\t\t\tsc = ((r_novis.ival==1)||(cl.worldmodel->numleafs > 6000)) && r_waterstyle.ival<=1 && r_telestyle.ival<=1 && r_slimestyle.ival<=1 && r_lavastyle.ival<=1 && Media_Capturing()<2;\n\t\t}\n\t\tif (sc != r_temporalscenecache.ival)\n\t\t{\n\t\t\tr_temporalscenecache.ival = sc;\n\t\t\tr_temporalscenecache.modified = true;\n\t\t}\n\n\t\tif (r_temporalscenecache.modified || r_dynamic.modified)\n\t\t{\n\t\t\tr_dynamic.modified = false;\n\t\t\tr_temporalscenecache.modified = false;\n#ifdef RTLIGHTS\n//\t\t\tSh_CheckSettings(); //fiddle with r_dynamic vs r_shadow_realtime_dlight.\n#endif\n\t\t\tCOM_WorkerPartialSync(webogenerating, &webogeneratingstate, true);\n\t\t\twhile (webostates)\n\t\t\t{\n\t\t\t\tvoid *webostate = webostates;\n\t\t\t\twebostates = webostates->next;\n\t\t\t\tR_DestroyWorldEBO(webostate);\n\t\t\t}\n\t\t\twebo_blocklightmapupdates = false;\n\t\t}\n\n\t\tif (!r_temporalscenecache.ival)\n\t\t\t;\n\t\telse if (!r_refdef.recurse && currentmodel->type == mod_brush)\n\t\t{\n\t\t\tstruct webostate_s *webostate, *best = NULL, *kill, **link;\n\t\t\tvec_t bestdist = FLT_MAX;\n\t\t\tfor (webostate = webostates; webostate; webostate = webostate->next)\n\t\t\t{\n\t\t\t\tif (webostate->wmodel != currentmodel)\n\t\t\t\t\tcontinue;\n\n//\t\t\t\tkill = webostate->next;\n//\t\t\t\tif (kill && kill->lastvalid < cls.framecount-5)\n//\t\t\t\t{\n//\t\t\t\t\twebostate->next = kill->next;\n//\t\t\t\t\tR_DestroyWorldEBO(kill);\n//\t\t\t\t}\n\n\t\t\t\tif (webostate->cluster[0] == r_viewcluster && webostate->cluster[1] == r_viewcluster2)\n\t\t\t\t{\n\t\t\t\t\tVectorCopy(r_refdef.vieworg, webostate->lastpos);\n\t\t\t\t\tif (!r_refdef.areabitsknown || !memcmp(webostate->areamask, r_refdef.areabits, MAX_MAP_AREA_BYTES))\n\t\t\t\t\t{\n\t\t\t\t\t\tbest = webostate;\n\t\t\t\t\t\tbestdist = 0;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\telse if (bestdist)\n\t\t\t\t\t{\n\t\t\t\t\t\tbest = webostate;\n\t\t\t\t\t\tbestdist = 0;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tvec3_t m;\n\t\t\t\t\tfloat d;\n\t\t\t\t\tVectorSubtract(webostate->lastpos, r_refdef.vieworg, m);\n\t\t\t\t\td = DotProduct(m,m);\n\t\t\t\t\tif (bestdist > d)\n\t\t\t\t\t{\n\t\t\t\t\t\tbestdist = d;\n\t\t\t\t\t\tbest = webostate;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\twebostate = best;\n\n\t\t\tif (qrenderer != QR_OPENGL && qrenderer != QR_VULKAN)\n\t\t\t\t;\n#ifdef Q1BSPS\n\t\t\telse if (currentmodel->fromgame == fg_quake || currentmodel->fromgame == fg_halflife || currentmodel->fromgame == fg_quake3)\n\t\t\t{\n\t\t\t\tif (!webogenerating)\n\t\t\t\t{\n\t\t\t\t\tqboolean gennew = false;\n\t\t\t\t\tif (!webostate)\n\t\t\t\t\t\tgennew = true;\t//generate an initial one, if we can.\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tif (!gennew && !currentmodel->lightmaps.prebaked)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tint i = cl_max_lightstyles;\n\t\t\t\t\t\t\tfor (i = 0; i < cl_max_lightstyles; i++)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tif (webostate->lightstylevalues[i] != d_lightstylevalue[i])\n\t\t\t\t\t\t\t\t{\t//a lightstyle changed. something needs to be rebuilt. FIXME: should probably have a bitmask for whether the lightstyle is relevant...\n\t\t\t\t\t\t\t\t\tgennew = true;\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (!gennew && r_refdef.areabitsknown && memcmp(webostate->areamask, r_refdef.areabits, MAX_MAP_AREA_BYTES))\n\t\t\t\t\t\t\tgennew = true;\n\n\t\t\t\t\t\tif (!gennew && (webostate->cluster[0] != r_viewcluster || webostate->cluster[1] != r_viewcluster2))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (webostate->pvs.buffersize != currentmodel->pvsbytes || r_viewcluster2 < 0)\n\t\t\t\t\t\t\t\tgennew = true;\t//o.O\n\t\t\t\t\t\t\telse if (memcmp(webostate->pvs.buffer, webostate->wmodel->funcs.ClusterPVS(webostate->wmodel, r_viewcluster, NULL, PVM_FAST), currentmodel->pvsbytes))\n\t\t\t\t\t\t\t\tgennew = true;\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t{\t//okay, so the pvs didn't change despite the clusters changing. this happens when using unvised maps or lots of func_detail\n\t\t\t\t\t\t\t\t//just hack the cluster numbers so we don't have to do the memcmp above repeatedly for no reason.\n\t\t\t\t\t\t\t\twebostate->cluster[0] = r_viewcluster;\n\t\t\t\t\t\t\t\twebostate->cluster[1] = r_viewcluster2;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif (gennew)\n\t\t\t\t\t{\n\t\t\t\t\t\tint i;\n\t\t\t\t\t\tstatic int ebogensequence;\n\t\t\t\t\t\tif (!currentmodel->numbatches)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tint sortid;\n\t\t\t\t\t\t\tbatch_t *batch;\n\t\t\t\t\t\t\tcurrentmodel->numbatches = 0;\n\t\t\t\t\t\t\tfor (sortid = 0; sortid < SHADER_SORT_COUNT; sortid++)\n\t\t\t\t\t\t\t\tfor (batch = currentmodel->batches[sortid]; batch != NULL; batch = batch->next)\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tbatch->user.bmodel.ebobatch = currentmodel->numbatches;\n\t\t\t\t\t\t\t\t\tcurrentmodel->numbatches++;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t/*TODO submodels too*/\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\twebogeneratingstate = true;\n\n\t\t\t\t\t\twebogenerating = NULL;\n\t\t\t\t\t\tif (webostate)\n\t\t\t\t\t\t\twebostate->lastvalid = cls.framecount;\n\t\t\t\t\t\tfor (link = &webostates; (kill=*link); )\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (kill->lastvalid < cls.framecount-5 && kill->wmodel == currentmodel && kill != webostate)\n\t\t\t\t\t\t\t{\t//this one looks old... kill it.\n\t\t\t\t\t\t\t\tif (webogenerating)\n\t\t\t\t\t\t\t\t\tR_DestroyWorldEBO(webogenerating);\t//can't use more than one, tidy up stale ones\n\t\t\t\t\t\t\t\twebogenerating = kill;\n\t\t\t\t\t\t\t\t*link = kill->next;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\tlink = &(*link)->next;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (!webogenerating)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\twebogenerating = BZ_Malloc(sizeof(*webogenerating) + sizeof(webogenerating->batches[0]) * (currentmodel->numbatches-1) + currentmodel->pvsbytes);\n\t\t\t\t\t\t\tmemset(&webogenerating->vbo, 0, sizeof(webogenerating->vbo));\n\t\t\t\t\t\t\tmemset(&webogenerating->ebo, 0, sizeof(webogenerating->ebo));\n\t\t\t\t\t\t\twebogenerating->ebomem = NULL;\n\t\t\t\t\t\t\twebogenerating->numbatches = 0;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tVectorCopy(r_refdef.vieworg, webogenerating->lastpos);\n\t\t\t\t\t\twebogenerating->wmodel = currentmodel;\n\t\t\t\t\t\twebogenerating->framecount = --ebogensequence;\n\t\t\t\t\t\twebogenerating->cluster[0] = r_viewcluster;\n\t\t\t\t\t\twebogenerating->cluster[1] = r_viewcluster2;\n\t\t\t\t\t\twebogenerating->pvs.buffer = (qbyte*)(webogenerating+1) + sizeof(webogenerating->batches[0])*(currentmodel->numbatches-1);\n\t\t\t\t\t\twebogenerating->pvs.buffersize = currentmodel->pvsbytes;\n\t\t\t\t\t\tmemcpy(webogenerating->areamask, r_refdef.areabits, MAX_MAP_AREA_BYTES);\n\t\t\t\t\t\tfor (i = 0; i < cl_max_lightstyles; i++)\n\t\t\t\t\t\t\twebogenerating->lightstylevalues[i] = d_lightstylevalue[i];\n\t\t\t\t\t\tQ_strncpyz(webogenerating->dbgid, \"webostate\", sizeof(webogenerating->dbgid));\n\t\t\t\t\t\tCOM_AddWork(WG_LOADER, R_GenWorldEBO, webogenerating, NULL, 0, 0);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n#endif\n\n\t\t\t//if they teleported, don't show something ugly - like obvious wallhacks.\n\t\t\tif (webogenerating && !r_novis.ival && cl.splitclients<=1 && webostate && (webostate->cluster[0] != r_viewcluster || webostate->cluster[1] != r_viewcluster2))\n\t\t\t{\n\t\t\t\tvec3_t m;\n\t\t\t\tfloat d;\n\t\t\t\tVectorSubtract(webostate->lastpos, r_refdef.vieworg, m);\n\t\t\t\td = sqrt(DotProduct(m,m));\n\t\t\t\tif (d > 40 && memcmp(webostate->pvs.buffer, webogenerating->wmodel->funcs.ClusterPVS(webogenerating->wmodel, webogenerating->cluster[0], NULL, PVM_FAST), webostate->pvs.buffersize))\n\t\t\t\t{\n\t\t\t\t\tCon_DLPrintf(2, \"Blocking for scenecache generation (distance = %g)\\n\", d);\n\t\t\t\t\twebostate = webogenerating;\n\t\t\t\t\tCOM_WorkerPartialSync(webogenerating, &webogeneratingstate, true);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (webogenerating && !webostate)\n\t\t\t{\t//block the first time around to avoid possible race conditions.\n\t\t\t\twebostate = webogenerating;\n\t\t\t\tCOM_WorkerPartialSync(webogenerating, &webogeneratingstate, true);\n\t\t\t}\n\n\t\t\tif (webostate)\n\t\t\t{\n\t\t\t\tentvis = surfvis = webostate->pvs.buffer;\n\n\t\t\t\twebostate->lastvalid = cls.framecount;\n\n\t\t\t\tif (webostate->cluster[0] == r_viewcluster && webostate->cluster[1] == r_viewcluster2)\n\t\t\t\t\tVectorCopy(r_refdef.vieworg, webostate->lastpos);\n\n\t\t\t\tr_dlightlightmaps = false;\t//don't waste time on dlighting bmodels.\n\n\t\t\t\tRSpeedEnd(RSPEED_WORLDNODE);\n\n\t\t\t\tareas[0] = 1;\n\t\t\t\tareas[1] = r_viewarea;\n\t\t\t\tCL_LinkStaticEntities(entvis, areas);\n\t\t\t\tTRACE((\"dbg: calling R_DrawParticles\\n\"));\n\t\t\t\tif (!r_refdef.recurse && !(r_refdef.flags & RDF_DISABLEPARTICLES))\n\t\t\t\t\tP_DrawParticles ();\n\n\t\t\t\tTRACE((\"dbg: calling BE_DrawWorld\\n\"));\n\t\t\t\tr_refdef.scenevis = surfvis;\n\t\t\t\tBE_DrawWorld(webostate->rbatches);\n\n\t\t\t\t/*FIXME: move this away*/\n\t\t\t\tif (currentmodel->fromgame == fg_quake || currentmodel->fromgame == fg_halflife)\n\t\t\t\t\tSurf_LessenStains();\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n#endif\n\n#ifdef RTLIGHTS\n\t\tif (r_shadow_realtime_dlight.ival || currentmodel->type != mod_brush || !(currentmodel->fromgame == fg_quake || currentmodel->fromgame == fg_halflife) || !currentmodel->funcs.MarkLights)\n\t\t\tr_dlightlightmaps = false; //don't do double lighting.\n#endif\n\n\t\tSurf_PushChains(currentmodel->batches);\n\n\t\tif (currentmodel->funcs.PrepareFrame)\n\t\t{\n\t\t\tint clusters[2] = {r_viewcluster, r_viewcluster2};\n\t\t\tcurrentmodel->funcs.PrepareFrame(currentmodel, &r_refdef, r_viewarea, clusters, &surf_frustumvis[r_refdef.recurse], &entvis, &surfvis);\n\t\t}\n\t\telse if (currentmodel->type != mod_brush)\n\t\t\tentvis = surfvis = NULL;\n#ifdef MAP_DOOM\n\t\telse if (currentmodel->fromgame == fg_doom)\n\t\t{\n\t\t\tentvis = surfvis = NULL;\n\t\t\tR_DoomWorld();\n\t\t}\n#endif\n\t\telse\n\t\t\tentvis = surfvis = NULL;\n\n\t\tRSpeedEnd(RSPEED_WORLDNODE);\n\n\t\tareas[0] = 1;\n\t\tareas[1] = r_viewarea;\n\t\tr_refdef.sceneareas = areas;\n\t\tif (!(r_refdef.flags & RDF_NOWORLDMODEL))\n\t\t{\n\t\t\tCL_LinkStaticEntities(entvis, r_refdef.sceneareas);\n\t\t\tTRACE((\"dbg: calling R_DrawParticles\\n\"));\n\t\t\tif (!r_refdef.recurse && !(r_refdef.flags & RDF_DISABLEPARTICLES))\n\t\t\t\tP_DrawParticles ();\n\t\t}\n\n\t\tTRACE((\"dbg: calling BE_DrawWorld\\n\"));\n\t\tr_refdef.scenevis = surfvis;\n\t\tBE_DrawWorld(cl.worldmodel->batches);\n\n\t\tSurf_PopChains(cl.worldmodel->batches);\n\n\t\t/*FIXME: move this away*/\n\t\tif (cl.worldmodel->fromgame == fg_quake || cl.worldmodel->fromgame == fg_halflife)\n\t\t\tSurf_LessenStains();\n\n\t\tr_refdef.sceneareas = NULL;\n\t}\n}\n\nunsigned int Surf_CalcMemSize(msurface_t *surf)\n{\n\tif (surf->mesh)\n\t\treturn 0;\n\n\tif (!surf->numedges)\n\t\treturn 0;\n\n\t//figure out how much space this surface needs\n\treturn sizeof(mesh_t) + \n\tsizeof(index_t)*(surf->numedges-2)*3 +\n\t(sizeof(vecV_t)+sizeof(vec2_t)*2+sizeof(vec3_t)*3+sizeof(vec4_t))*surf->numedges;\n}\n\nvoid Surf_DeInit(void)\n{\n\tint i;\n\n#ifdef THREADEDWORLD\n\twebo_blocklightmapupdates = 0;\n\twhile(webogenerating)\n\t\tCOM_WorkerPartialSync(webogenerating, &webogeneratingstate, true);\n\twhile (webostates)\n\t{\n\t\tvoid *webostate = webostates;\n\t\twebostates = webostates->next;\n\t\tR_DestroyWorldEBO(webostate);\n\t}\n#endif\n\n\tfor (i = 0; i < numlightmaps; i++)\n\t{\n\t\tSurf_FreeLightmap(lightmap[i]);\n\t\tlightmap[i] = NULL;\n\t}\n\n\tif (lightmap)\n\t\tBZ_Free(lightmap);\n\n\tfor (i = 0; i < R_MAX_RECURSE; i++)\n\t\tZ_Free(surf_frustumvis[i].buffer);\n\tmemset(surf_frustumvis, 0, sizeof(surf_frustumvis));\n\n\tCL_FreeDlights();\n\n\tlightmap=NULL;\n\tnumlightmaps=0;\n\n\tAlias_Shutdown();\n\tShader_ResetRemaps();\n}\n\nvoid Surf_Clear(model_t *mod)\n{\n\tint i;\n\tvbo_t *vbo;\n//\tif (mod->fromgame == fg_doom3)\n//\t\treturn;/*they're on the hunk*/\n\n#ifdef THREADEDWORLD\n\tstruct webostate_s **link, *t;\n\twhile(webogenerating)\n\t\tCOM_WorkerPartialSync(webogenerating, &webogeneratingstate, true);\n\n\tfor (link = &webostates; (t=*link); )\n\t{\n\t\tif (t->wmodel == mod)\n\t\t{\n\t\t\t*link = t->next;\n\t\t\tR_DestroyWorldEBO(t);\n\t\t}\n\t\telse\n\t\t\tlink = &(*link)->next;\n\t}\n#endif\n\n\twhile(mod->vbos)\n\t{\n\t\tvbo = mod->vbos;\n\t\tmod->vbos = vbo->next;\n\t\tBE_ClearVBO(vbo, false);\n\t}\n\n\tif (!mod->submodelof)\n\t{\n\t\tfor (i = 0; i < mod->numtextures; i++)\n\t\t{\n\t\t\tR_UnloadShader(mod->textures[i]->shader);\n\t\t\tmod->textures[i]->shader = NULL;\n\t\t}\n\t}\n\tmod->numtextures = 0;\n\n\tBZ_Free(mod->shadowbatches);\n\tmod->numshadowbatches = 0;\n\tmod->shadowbatches = NULL;\n#ifdef RTLIGHTS\n\tSh_PurgeShadowMeshes();\n#endif\n\n\tBZ_Free(blocklights);\n\tBZ_Free(blocknormals);\n\tblocklights = NULL;\n\tblocknormals = NULL;\n\tmaxblocksize = 0;\n}\n\nuploadfmt_t Surf_NameToFormat(const char *nam)\n{\n\tstatic uploadfmt_t tab[] = {PTI_L8, PTI_RGB8, PTI_BGRA8, PTI_A2BGR10, PTI_E5BGR9, PTI_RGBA16F, PTI_RGBA32F, PTI_RGB565, PTI_RGBA4444, PTI_RGBA5551};\n\tint idx = atoi(nam)-1;\n\tif (idx>=0 && idx < countof(tab))\n\t\treturn tab[idx];\n\n\tif (!Q_strcasecmp(nam, \"e5bgr9\") || !Q_strcasecmp(nam, \"rgb9e5\"))\n\t\treturn PTI_E5BGR9;\t//prefered hdr format, for some reason.\n\tif (!Q_strcasecmp(nam, \"a2bgr10\") || !Q_strcasecmp(nam, \"rgb10a2\") || !Q_strcasecmp(nam, \"rgb10\"))\n\t\treturn PTI_A2BGR10;\t//prefered ldr format. hurrah for 10 bits.\n\tif (!Q_strcasecmp(nam, \"rgba32f\"))\n\t\treturn PTI_RGBA32F;\t//big bulky hdr format\n\tif (!Q_strcasecmp(nam, \"rgba16f\"))\n\t\treturn PTI_RGBA16F;\t//tolerable hdr format\n//\tif (!Q_strcasecmp(nam, \"rgba8s\"))\n//\t\treturn PTI_RGBA8_SIGNED;\n\tif (!Q_strcasecmp(nam, \"rgb565\") || !Q_strcasecmp(nam, \"rgb5\"))\n\t\treturn PTI_RGB565;\t//boo hiss\n\tif (!Q_strcasecmp(nam, \"rgba4444\") || !Q_strcasecmp(nam, \"rgba4\"))\n\t\treturn PTI_RGBA4444;\t//erk\n\tif (!Q_strcasecmp(nam, \"rgba5551\") || !Q_strcasecmp(nam, \"rgba51\") || !Q_strcasecmp(nam, \"rgb5a1\"))\n\t\treturn PTI_RGBA5551;\n\tif (!Q_strcasecmp(nam, \"argb4444\"))\n\t\treturn PTI_ARGB4444;\n\tif (!Q_strcasecmp(nam, \"argb1555\"))\n\t\treturn PTI_ARGB1555;\n\tif (!Q_strcasecmp(nam, \"rgbx8\") || !Q_strcasecmp(nam, \"bgrx8\") || !Q_strcasecmp(nam, \"rgba8\") || !Q_strcasecmp(nam, \"bgra8\"))\n\t{\t//most common format(s) for lightmaps in various engines...\n\t\tif (sh_config.texfmt[PTI_BGRX8])\n\t\t\treturn PTI_BGRX8;\t//probably fastest\n\t\tif (sh_config.texfmt[PTI_RGBX8])\n\t\t\treturn PTI_RGBX8;\t//no bgr? odd...\n\t\tif (sh_config.texfmt[PTI_BGRA8])\n\t\t\treturn PTI_BGRA8;\t//no padded formats at all? erk!\n\t\treturn PTI_RGBA8;\t//probably the slowest for pc hardware.\n\t}\n\tif (!Q_strcasecmp(nam, \"rgb8\") || !Q_strcasecmp(nam, \"bgr8\"))\n\t\treturn PTI_RGB8;\t//generally not recommended (misaligned so the gpu has to compensate)\n\tif (!Q_strcasecmp(nam, \"l8\"))\n\t\treturn PTI_L8;\n\tif (*nam)\n\t\tCon_Printf(\"Unknown lightmap format: %s\\n\", nam);\n\treturn PTI_INVALID;\n}\n\n//pick fastest mode for lightmap data\nuploadfmt_t Surf_LightmapMode(model_t *model)\n{\n\tuploadfmt_t fmt = Surf_NameToFormat(r_lightmap_format.string);\n\tif (model && model->lightmaps.prebaked && model->lightmaps.fmt!=LM_RGB8)\n\t\tfmt = PTI_INVALID;\t//don't let them force it away if we can't support it. this sucks.\n\tif (!sh_config.texfmt[fmt])\n\t{\n\t\tqboolean hdr = (vid.flags&VID_SRGBAWARE), rgb = false;\n\n\t\tif (fmt != PTI_INVALID)\n\t\t\tCon_Printf(\"lightmap format %s not supported by renderer\\n\", r_lightmap_format.string);\n\n\t\tif (model)\n\t\t{\n\t\t\tswitch (model->lightmaps.fmt)\n\t\t\t{\n\t\t\tcase LM_E5BGR9:\n\t\t\t\thdr = rgb = true;\n\t\t\t\tbreak;\n\t\t\tcase LM_RGB8:\n\t\t\t\trgb = true;\n\t\t\t\tbreak;\n\t\t\tcase LM_L8:\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (model->deluxdata)\n\t\t\t\trgb = true;\n\n\t\t\tif (model->terrain)\t//the terrain code requires rgba8.\n\t\t\t\thdr = false;\n\t\t}\n\n\t\tif (sh_config.texfmt[PTI_E5BGR9] && hdr)\n\t\t\tfmt = PTI_E5BGR9;\n\t\telse if (sh_config.texfmt[PTI_RGBA16F] && hdr)\n\t\t\tfmt = PTI_RGBA16F;\n\t\telse if (sh_config.texfmt[PTI_RGBA32F] && hdr)\n\t\t\tfmt = PTI_RGBA32F;\n\t\telse if (sh_config.texfmt[PTI_A2BGR10] && rgb)\n\t\t\tfmt = PTI_A2BGR10;\n\t\telse if (sh_config.texfmt[PTI_L8] && !rgb && !r_deluxemapping && r_dynamic.ival<=0)\n\t\t\tfmt = PTI_L8;\n\t\telse if (sh_config.texfmt[PTI_BGRX8])\n\t\t\tfmt = PTI_BGRX8;\n\t\telse if (sh_config.texfmt[PTI_RGB8])\n\t\t\tfmt = PTI_RGB8;\n\t\telse\n\t\t\tfmt = PTI_RGBX8;\n\n\t}\n\n\tif (!model->submodelof)\n\t\tCon_DPrintf(\"%s: Using lightmap format %s\\n\", model->name, Image_FormatName(fmt));\n\n\treturn fmt;\n}\n\nstatic void Surf_FreeLightmap(lightmapinfo_t *lm)\n{\n\tif (lm)\n\t{\n#ifdef GLQUAKE\n\t\tif (lm->pbo_handle)\n\t\t{\n\t\t\tqglBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, lm->pbo_handle);\n\t\t\tqglUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB);\n\t\t\tqglBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0);\n\t\t\tqglDeleteBuffersARB(1, &lm->pbo_handle);\n\t\t}\n#endif\n\t\tif (!lm->external)\n\t\t\tImage_DestroyTexture(lm->lightmap_texture);\n\t\tBZ_Free(lm);\n\t}\n}\n\n//needs to be followed by a BE_UploadAllLightmaps at some point\nint Surf_NewLightmaps(int count, int width, int height, uploadfmt_t fmt, qboolean deluxe)\n{\n\tint first = numlightmaps;\n\tint i;\n\n\tunsigned int pixbytes, pixw, pixh, pixd;\n\tunsigned int dpixbytes, dpixw, dpixh, dpixd;\n\tuploadfmt_t dfmt;\n#ifdef THREADEDWORLD\n\textern int webo_blocklightmapupdates;\n\twebo_blocklightmapupdates = 0;\n#endif\n\n\tif (!count)\n\t\treturn -1;\n\n\tif (deluxe && (count & 1))\n\t{\n\t\tdeluxe = false;\n//\t\tcount+=1;\n\t\tCon_Print(\"WARNING: Deluxemapping with odd number of lightmaps\\n\");\n\t}\n\n\tImage_BlockSizeForEncoding(fmt, &pixbytes, &pixw, &pixh, &pixd);\n\tif (pixw != 1 || pixh != 1 || pixd != 1)\n\t\treturn -1;\t//compressed formats are unsupported\n\tdfmt = PTI_A2BGR10;\t//favour this one, because it tends to be slightly faster.\n\tif (!sh_config.texfmt[dfmt])\n\t\tdfmt = PTI_BGRX8;\n\tif (!sh_config.texfmt[dfmt])\n\t\tdfmt = PTI_RGBX8;\n\tif (!sh_config.texfmt[dfmt])\n\t\tdfmt = PTI_RGB8;\n\tImage_BlockSizeForEncoding(dfmt, &dpixbytes, &dpixw, &dpixh, &dpixd);\n\tif (dpixw != 1 || dpixh != 1 || dpixd != 1)\n\t\treturn -1;\t//compressed formats are unsupported\n\n\tSys_LockMutex(com_resourcemutex);\n\n\ti = numlightmaps + count;\n\tlightmap = BZ_Realloc(lightmap, sizeof(*lightmap)*(i));\n\twhile(i --> first)\n\t{\n#ifdef GLQUAKE\n\t\textern cvar_t gl_pbolightmaps;\n\t\t//we might as well use a pbo for our staging memory.\n\t\tif (qrenderer == QR_OPENGL && qglBufferStorage && qglMapBufferRange && gl_pbolightmaps.ival && Sys_IsMainThread())\n\t\t{\t//glBufferStorage and GL_MAP_PERSISTENT_BIT generally means gl4.4+ (we need persistent for scenecache)\n\t\t\t//pbos are 2.1\n\t\t\tif (deluxe && ((i - numlightmaps)&1))\n\t\t\t{\n\t\t\t\tlightmap[i] = Z_Malloc(sizeof(*lightmap[i]));\n\t\t\t\tlightmap[i]->width = width;\n\t\t\t\tlightmap[i]->height = height;\n\t\t\t\tlightmap[i]->lightmaps = NULL;\n\t\t\t\tlightmap[i]->stainmaps = NULL;\n\t\t\t\tlightmap[i]->hasdeluxe = false;\n\t\t\t\tlightmap[i]->pixbytes = dpixbytes;\n\t\t\t\tlightmap[i]->fmt = dfmt;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tlightmap[i] = Z_Malloc(sizeof(*lightmap[i]) + (sizeof(stmap)*3)*width*height);\n\t\t\t\tlightmap[i]->width = width;\n\t\t\t\tlightmap[i]->height = height;\n\t\t\t\tlightmap[i]->lightmaps = NULL;\n\t\t\t\tlightmap[i]->stainmaps = (qbyte*)(lightmap[i]+1);\n\t\t\t\tlightmap[i]->hasdeluxe = deluxe;\n\t\t\t\tlightmap[i]->pixbytes = pixbytes;\n\t\t\t\tlightmap[i]->fmt = fmt;\n\t\t\t}\n\n\t\t\tqglGenBuffersARB(1, &lightmap[i]->pbo_handle);\n\t\t\tqglBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, lightmap[i]->pbo_handle);\n\t\t\t//note: we only write the memory. the pbo would normally be in system memory anyway so there shouldn't be too much cost from coherent mappings.\n\t\t\tqglBufferStorage(GL_PIXEL_UNPACK_BUFFER_ARB, lightmap[i]->pixbytes*width*height, NULL, GL_MAP_WRITE_BIT|GL_MAP_PERSISTENT_BIT|GL_MAP_COHERENT_BIT);\n\t\t\tlightmap[i]->lightmaps = qglMapBufferRange(GL_PIXEL_UNPACK_BUFFER_ARB, 0, lightmap[i]->pixbytes*width*height, GL_MAP_WRITE_BIT|GL_MAP_PERSISTENT_BIT|GL_MAP_COHERENT_BIT);\n\t\t\tqglBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0);\n\t\t}\n\t\telse\n#endif\n\t\t{\n\t\t\tif (deluxe && ((i - numlightmaps)&1))\n\t\t\t{\t//deluxemaps always use a specific format.\n\t\t\t\tlightmap[i] = Z_Malloc(sizeof(*lightmap[i]) + (sizeof(qbyte)*dpixbytes)*width*height);\n\t\t\t\tlightmap[i]->width = width;\n\t\t\t\tlightmap[i]->height = height;\n\t\t\t\tlightmap[i]->lightmaps = (qbyte*)(lightmap[i]+1);\n\t\t\t\tlightmap[i]->stainmaps = NULL;\n\t\t\t\tlightmap[i]->hasdeluxe = false;\n\t\t\t\tlightmap[i]->pixbytes = dpixbytes;\n\t\t\t\tlightmap[i]->fmt = dfmt;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tlightmap[i] = Z_Malloc(sizeof(*lightmap[i]) + (sizeof(qbyte)*pixbytes + sizeof(stmap)*3)*width*height);\n\t\t\t\tlightmap[i]->width = width;\n\t\t\t\tlightmap[i]->height = height;\n\t\t\t\tlightmap[i]->lightmaps = (qbyte*)(lightmap[i]+1);\n\t\t\t\tlightmap[i]->stainmaps = (stmap*)(lightmap[i]->lightmaps+pixbytes*width*height);\n\t\t\t\tlightmap[i]->hasdeluxe = deluxe;\n\t\t\t\tlightmap[i]->pixbytes = pixbytes;\n\t\t\t\tlightmap[i]->fmt = fmt;\n\t\t\t}\n\t\t}\n\n\t\tlightmap[i]->rectchange.l = 0;\n\t\tlightmap[i]->rectchange.t = 0;\n\t\tlightmap[i]->rectchange.b = lightmap[i]->height;\n\t\tlightmap[i]->rectchange.r = lightmap[i]->width;\n\n\n\t\tlightmap[i]->lightmap_texture = r_nulltex;\n\t\tlightmap[i]->modified = true;\n//\t\t\tlightmap[i]->shader = NULL;\n\t\tlightmap[i]->external = false;\n\t\t// reset stainmap since it now starts at 255\n\t\tif (lightmap[i]->stainmaps)\n\t\t\tmemset(lightmap[i]->stainmaps, 255, width*height*3*sizeof(stmap));\n\t}\n\n\tnumlightmaps += count;\n\n\tSys_UnlockMutex(com_resourcemutex);\n\n\treturn first;\n}\nint Surf_NewExternalLightmaps(int count, char *filepattern, qboolean deluxe)\n{\n\tunsigned int nulllight = 0xffffffff;\n\tunsigned int nulldeluxe = 0xffff7f7f;\n\tint first = numlightmaps;\n\tint i;\n\tchar nname[MAX_QPATH];\n\tqboolean odd = (count & 1) && deluxe;\n\n#ifdef THREADEDWORLD\n\textern int webo_blocklightmapupdates;\n\twebo_blocklightmapupdates = 0;\n#endif\n\n\tif (!count)\n\t\treturn -1;\n\n\tif (odd)\n\t\tcount++;\n\n\ti = numlightmaps + count;\n\tlightmap = BZ_Realloc(lightmap, sizeof(*lightmap)*(i));\n\twhile(i > first)\n\t{\n\t\ti--;\n\n\t\tlightmap[i] = Z_Malloc(sizeof(*lightmap[i]));\n\t\tlightmap[i]->width = 0;\n\t\tlightmap[i]->height = 0;\n\t\tlightmap[i]->lightmaps = NULL;\n\t\tlightmap[i]->stainmaps = NULL;\n\n\t\tlightmap[i]->modified = false;\n\t\tlightmap[i]->external = true;\n\t\tlightmap[i]->hasdeluxe = (deluxe && !((i - numlightmaps)&1));\n\n\t\tQ_snprintfz(nname, sizeof(nname), filepattern, i - numlightmaps);\n\n\t\tTEXASSIGN(lightmap[i]->lightmap_texture, R_LoadHiResTexture(nname, NULL, (r_lightmap_nearest.ival?IF_NEAREST:IF_LINEAR)|IF_NOMIPMAP));\n\t\tif (lightmap[i]->lightmap_texture->status == TEX_LOADING)\n\t\t\tCOM_WorkerPartialSync(lightmap[i]->lightmap_texture, &lightmap[i]->lightmap_texture->status, TEX_LOADING);\n\t\tif (lightmap[i]->lightmap_texture->status == TEX_FAILED)\n\t\t{\n\t\t\tif ((i&1) && deluxe)\n\t\t\t\tlightmap[i]->lightmap_texture = R_LoadReplacementTexture(\"*nulldeluxe\", NULL, IF_LOADNOW, &nulldeluxe, 1, 1, TF_RGBX32);\n\t\t\telse\n\t\t\t\tlightmap[i]->lightmap_texture = R_LoadReplacementTexture(\"*nulllight\", NULL, IF_LOADNOW, &nulllight, 1, 1, TF_RGBX32);\n\t\t}\n\t\tlightmap[i]->width = lightmap[i]->lightmap_texture->width;\n\t\tlightmap[i]->height = lightmap[i]->lightmap_texture->height;\n\t\tlightmap[i]->fmt = lightmap[i]->lightmap_texture->format;\n\t}\n\n\tif (odd)\n\t{\n\t\ti = numlightmaps+count-1;\n\t\tif (!TEXVALID(lightmap[i]->lightmap_texture))\n\t\t{\t//FIXME: no deluxemaps after all...\n\t\t\tZ_Free(lightmap[i]);\n\t\t\tlightmap[i] = NULL;\n\t\t\tcount--;\n\t\t}\n\t}\n\n\tnumlightmaps += count;\n\n\treturn first;\n}\n\nvoid Surf_BuildModelLightmaps (model_t *m)\n{\n\tint\t\ti;\n\tint shift;\n\tmsurface_t *surf;\n\tbatch_t *batch;\n\tint sortid;\n\tint newfirst;\n\tuploadfmt_t fmt;\n\n\tif (m->loadstate != MLS_LOADED)\n\t\treturn;\n\n#ifdef TERRAIN\n\t//easiest way to deal with heightmap lightmaps is to just purge the entire thing.\n\tif (m->terrain)\n\t\tTerr_PurgeTerrainModel(m, false, false);\t//FIXME: cop out. middle arg should be 'true'.\n#endif\n\n\tif (m->type != mod_brush)\n\t\treturn;\n\n\tif (!m->lightmaps.count)\n\t\treturn;\n\n\tcurrentmodel = m;\n\tshift = Surf_LightmapShift(currentmodel);\n\n\tfmt = Surf_LightmapMode(m);\n\n#ifdef THREADEDWORLD\n\t//make sure nothing is poking the lightmaps while we're rewriting them\n\twhile(webogenerating)\n\t\tCOM_WorkerPartialSync(webogenerating, &webogeneratingstate, true);\n#endif\n\n\tR_BumpLightstyles(m->lightmaps.maxstyle);\t//should only really happen with lazy loading\n\n\tif (m->submodelof && m->lightmaps.prebaked)\t//FIXME: should be all bsp formats\n\t{\n\t\tif (m->submodelof->loadstate != MLS_LOADED)\n\t\t\treturn;\n\t\tnewfirst = m->submodelof->lightmaps.first;\n\t}\n\telse\n\t{\n\t\tif (!m->lightdata && m->lightmaps.count && m->lightmaps.prebaked)\n\t\t{\n\t\t\tchar pattern[MAX_QPATH];\n\t\t\tCOM_StripAllExtensions(m->name, pattern, sizeof(pattern));\n\t\t\tQ_strncatz(pattern, \"/lm_%04u.tga\", sizeof(pattern));\n\t\t\tnewfirst = Surf_NewExternalLightmaps(m->lightmaps.count, pattern, m->lightmaps.deluxemapping);\n\t\t\tm->lightmaps.count = numlightmaps - newfirst;\n\t\t}\n\t\telse\n\t\t\tnewfirst = Surf_NewLightmaps(m->lightmaps.count, m->lightmaps.width, m->lightmaps.height, fmt, m->lightmaps.deluxemapping);\n\t}\n\n\t//fixup batch lightmaps\n\tfor (sortid = 0; sortid < SHADER_SORT_COUNT; sortid++)\n\tfor (batch = m->batches[sortid]; batch != NULL; batch = batch->next)\n\t{\n\t\tfor (i = 0; i < MAXRLIGHTMAPS; i++)\n\t\t{\n\t\t\tif (batch->lightmap[i] < 0)\n\t\t\t\tcontinue;\n\t\t\tbatch->lightmap[i] = batch->lightmap[i] - m->lightmaps.first + newfirst;\n\t\t}\n\t}\n\n\tif (m->lightmaps.prebaked)\n\t{\n\t\tint j;\n\t\tunsigned char *src, *stop;\n\t\tunsigned char *dst;\n\n\n\t\t//fixup surface lightmaps, and paint\n\t\tfor (i=0; i<m->nummodelsurfaces; i++)\n\t\t{\n\t\t\tsurf = m->surfaces + i + m->firstmodelsurface;\n\t\t\tfor (j = 0; j < MAXRLIGHTMAPS; j++)\n\t\t\t{\n\t\t\t\tif (surf->lightmaptexturenums[j] < m->lightmaps.first)\n\t\t\t\t{\n\t\t\t\t\tsurf->lightmaptexturenums[j] = -1;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (surf->lightmaptexturenums[j] >= m->lightmaps.first+m->lightmaps.count)\n\t\t\t\t{\n\t\t\t\t\tsurf->lightmaptexturenums[j] = -1;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tsurf->lightmaptexturenums[j] = surf->lightmaptexturenums[0] - m->lightmaps.first + newfirst;\n\t\t\t}\n\t\t}\n\n\t\tif (!m->submodelof)\n\t\tfor (i = 0; i < m->lightmaps.count; i++)\n\t\t{\n\t\t\tif (lightmap[newfirst+i]->external || !m->lightdata)\n\t\t\t\tcontinue;\n\n\t\t\tif (lightmap[newfirst+i]->fmt == m->lightmaps.prebaked)\n\t\t\t{\n\t\t\t\tunsigned int bb,bw,bh,bd;\n\t\t\t\tImage_BlockSizeForEncoding(m->lightmaps.prebaked, &bb,&bw,&bh,&bd);\n\n\t\t\t\tdst = lightmap[newfirst+i]->lightmaps;\n\t\t\t\tsrc = m->lightdata + i*m->lightmaps.width*m->lightmaps.height*bb;\n\t\t\t\tstop = m->lightdata + (i+1)*m->lightmaps.width*m->lightmaps.height*bb;\n\t\t\t\tif (stop-m->lightdata > m->lightdatasize)\n\t\t\t\t\tstop = m->lightdata + m->lightdatasize;\n\t\t\t\tmemcpy(dst, src, stop-src);\n\t\t\t}\n\t\t\t//FIXME: replace with Image_ChangeFormat here. but the data may be partial for the last mip.\n\t\t\telse switch(m->lightmaps.fmt)\n\t\t\t{\n\t\t\tcase LM_RGB8:\n\t\t\t\tdst = lightmap[newfirst+i]->lightmaps;\n\t\t\t\tsrc = m->lightdata + i*m->lightmaps.width*m->lightmaps.height*3;\n\t\t\t\tstop = m->lightdata + (i+1)*m->lightmaps.width*m->lightmaps.height*3;\n\t\t\t\tif (stop-m->lightdata > m->lightdatasize)\n\t\t\t\t\tstop = m->lightdata + m->lightdatasize;\n\t\t\t\tswitch(lightmap[newfirst+i]->fmt)\n\t\t\t\t{\n\t\t\t\tdefault:\n\t\t\t\t\tSys_Error(\"Surf_BuildModelLightmaps: Bad format - %s\\n\", Image_FormatName(lightmap[newfirst+i]->fmt));\n\t\t\t\t\tbreak;\n\t\t\t\tcase PTI_A2BGR10:\n\t\t\t\t\tfor (; src < stop; dst += 4, src += 3)\n\t\t\t\t\t\t*(unsigned int*)dst = (0x3u<<30) | (src[2]<<22) | (src[1]<<12) | (src[0]<<2);\n\t\t\t\t\tbreak;\n\t\t\t\tcase PTI_E5BGR9:\n\t\t\t\t\tfor (; src < stop; dst += 4, src += 3)\n\t\t\t\t\t\t*(unsigned int*)dst = Surf_PackE5BRG9(src[0], src[1], src[2], 8);\n\t\t\t\t\tbreak;\n\t\t\t\tcase PTI_BGRA8:\n\t\t\t\tcase PTI_BGRX8:\n\t\t\t\t\tfor (; src < stop; dst += 4, src += 3)\n\t\t\t\t\t{\n\t\t\t\t\t\tdst[0] = src[2];\n\t\t\t\t\t\tdst[1] = src[1];\n\t\t\t\t\t\tdst[2] = src[0];\n\t\t\t\t\t\tdst[3] = 255;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase PTI_RGBA8:\n\t\t\t\tcase PTI_RGBX8:\n\t\t\t\t\tfor (; src < stop; dst += 4, src += 3)\n\t\t\t\t\t{\n\t\t\t\t\t\tdst[0] = src[0];\n\t\t\t\t\t\tdst[1] = src[1];\n\t\t\t\t\t\tdst[2] = src[2];\n\t\t\t\t\t\tdst[3] = 255;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase PTI_BGR8:\n\t\t\t\t\tfor (; src < stop; dst += 3, src += 3)\n\t\t\t\t\t{\n\t\t\t\t\t\tdst[0] = src[2];\n\t\t\t\t\t\tdst[1] = src[1];\n\t\t\t\t\t\tdst[2] = src[0];\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase PTI_RGB8:\n\t\t\t\t\tfor (; src < stop; dst += 3, src += 3)\n\t\t\t\t\t{\n\t\t\t\t\t\tdst[0] = src[0];\n\t\t\t\t\t\tdst[1] = src[1];\n\t\t\t\t\t\tdst[2] = src[2];\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase PTI_RGB565:\n\t\t\t\t\tfor (; src < stop; dst += 2, src += 3)\n\t\t\t\t\t\t*(unsigned short*)dst = ((src[0]>>3)<<11)|((src[1]>>2)<<5)|((src[2]>>3)<<0);\n\t\t\t\t\tbreak;\n\t\t\t\tcase PTI_L8:\n\t\t\t\t\tfor (; src < stop; dst += 1, src += 3)\n\t\t\t\t\t{\n\t\t\t\t\t\tdst[0] = max(max(src[0], src[1]), src[2]);\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase LM_E5BGR9:\n\t\t\t\tdst = lightmap[newfirst+i]->lightmaps;\n\t\t\t\tsrc = m->lightdata + i*m->lightmaps.width*m->lightmaps.height*4;\n\t\t\t\tstop = m->lightdata + (i+1)*m->lightmaps.width*m->lightmaps.height*4;\n\t\t\t\tif (stop-m->lightdata > m->lightdatasize)\n\t\t\t\t\tstop = m->lightdata + m->lightdatasize;\n\n\t\t\t\tif (lightmap[newfirst+i]->fmt == PTI_E5BGR9)\n\t\t\t\t\tmemcpy(dst, src, stop-src);\n\t\t\t\telse\t//this can happen on older gpus...\n\t\t\t\t\tCon_Printf(CON_WARNING\"Unsupported lightmap format. set ^[/r_lightmap_format e5bgr9^]\\n\");\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tCon_Printf(CON_WARNING\"Unsupported input lightmap format\\n\");\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\telse\n\t{\n\t\tint j;\n\n//\t\tif (*m->name == '*')\n//\t\t{\n//\t\t\tif (!cl.worldmodel || cl.worldmodel->loadstate != MLS_LOADED)\n//\t\t\t\treturn;\n//\t\t}\n\t\t//fixup surface lightmaps, and paint\n\t\tfor (i=0; i<m->nummodelsurfaces; i++)\n\t\t{\n\t\t\tsurf = m->surfaces + i + m->firstmodelsurface;\n\t\t\tfor (j = 0; j < MAXRLIGHTMAPS; j++)\n\t\t\t{\n\t\t\t\tif (surf->lightmaptexturenums[j] < m->lightmaps.first)\n\t\t\t\t{\n\t\t\t\t\tsurf->lightmaptexturenums[j] = -1;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (surf->lightmaptexturenums[j] >= m->lightmaps.first+m->lightmaps.count)\n\t\t\t\t{\n\t\t\t\t\tsurf->lightmaptexturenums[j] = -1;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tsurf->lightmaptexturenums[j] = surf->lightmaptexturenums[j] - m->lightmaps.first + newfirst;\n\n\t\t\t\tSurf_BuildLightMap (m, surf, j, shift, r_ambient.value*255, d_lightstylevalue);\n\t\t\t}\n\t\t}\n\t}\n\tm->lightmaps.first = newfirst;\n}\n\nvoid Surf_ClearSceneCache(void)\n{\n#ifdef THREADEDWORLD\n\twhile(webogenerating)\n\t\tCOM_WorkerPartialSync(webogenerating, &webogeneratingstate, true);\n\twhile (webostates)\n\t{\n\t\tvoid *webostate = webostates;\n\t\twebostates = webostates->next;\n\t\tR_DestroyWorldEBO(webostate);\n\t}\n#endif\n}\n\n/*\n==================\nGL_BuildLightmaps\n\nBuilds the lightmap texture\nwith all the surfaces from all brush models\nGroups surfaces into their respective batches (based on the lightmap number).\n==================\n*/\nvoid Surf_BuildLightmaps (void)\n{\n\tunsigned int\t\ti, j;\n\tmodel_t\t*m;\n\n\textern model_t\t*mod_known;\n\textern int\t\tmod_numknown;\n\n\tint maxstyle;\n\n\t//make sure the lightstyle values are correct (and be sure that the sizes cover all models).\n\tfor (i = 0, maxstyle=0; i < mod_numknown; i++)\n\t{\n\t\tm = &mod_known[i];\n\t\tif (m->loadstate == MLS_LOADED)\n\t\t\tif (maxstyle < m->lightmaps.maxstyle)\n\t\t\t\tmaxstyle = m->lightmaps.maxstyle;\n\t}\n\tR_BumpLightstyles(maxstyle);\t//should only really happen with lazy loading\n\tR_AnimateLight();\n\n\twhile(numlightmaps > 0)\n\t{\n\t\tnumlightmaps--;\n\t\tSurf_FreeLightmap(lightmap[numlightmaps]);\n\t\tlightmap[numlightmaps] = NULL;\n\t}\n\n\t//FIXME: unload stuff that's no longer relevant somehow.\n\tfor (i = 0; i < mod_numknown; i++)\n\t{\n\t\tm = &mod_known[i];\n\t\tif (m->loadstate != MLS_LOADED)\n\t\t\tcontinue;\n\t\tSurf_BuildModelLightmaps(m);\n\n\t\tfor (j = 0; j < m->numenvmaps; j++)\n\t\t\tif (m->envmaps[j].image)\n\t\t\t\tm->envmaps[j].image->regsequence = r_regsequence;\n\t}\n\tBE_UploadAllLightmaps();\n}\n\n\n\n/*\n===============\nSurf_NewMap\n===============\n*/\nvoid Surf_NewMap (model_t *worldmodel)\n{\n\tchar namebuf[MAX_QPATH];\n\textern cvar_t host_mapname;\n#ifdef BEF_PUSHDEPTH\n\textern cvar_t r_polygonoffset_submodel_maps;\n\tchar *s;\n#endif\n\tint\t\ti;\n\n\tcl.worldmodel = worldmodel;\n\n\t//evil haxx\n\tr_dynamic.ival = r_dynamic.value;\n\tif (r_dynamic.ival > 0 && (!cl.worldmodel || cl.worldmodel->lightmaps.prebaked)) //quake3 has no lightmaps, disable r_dynamic\n\t\tr_dynamic.ival = 0;\n\n\tmemset (&r_worldentity, 0, sizeof(r_worldentity));\n\tAngleVectors(r_worldentity.angles, r_worldentity.axis[0], r_worldentity.axis[1], r_worldentity.axis[2]);\n\tVectorInverse(r_worldentity.axis[1]);\n\tr_worldentity.model = cl.worldmodel;\n\tVector4Set(r_worldentity.shaderRGBAf, 1, 1, 1, 1);\n\tVectorSet(r_worldentity.light_avg, 1, 1, 1);\n\n\n\tif (cl.worldmodel)\n\t\tCOM_FileBase(cl.worldmodel->name, namebuf, sizeof(namebuf));\n\telse\n\t\t*namebuf = '\\0';\n\tCvar_Set(&host_mapname, namebuf);\n\n\tSurf_DeInit();\n\n\tr_viewcluster = -1;\n\tr_viewcluster2 = -1;\n#ifdef BEF_PUSHDEPTH\n\tr_pushdepth = false;\n\tfor (s = r_polygonoffset_submodel_maps.string; s && *s; )\n\t{\n\t\ts = COM_Parse(s);\n\t\tif (*com_token)\n\t\t\tif (wildcmp(com_token, namebuf))\n\t\t\t{\n\t\t\t\tr_pushdepth = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t}\n#endif\n\n\tTRACE((\"dbg: Surf_NewMap: clear particles\\n\"));\n\tP_ClearParticles ();\n\tCL_RegisterParticles();\n\n\tShader_DoReload();\n\tif (cl.worldmodel)\n\t{\n\t\tif (cl.worldmodel->loadstate == MLS_LOADING)\n\t\t\tCOM_WorkerPartialSync(cl.worldmodel, &cl.worldmodel->loadstate, MLS_LOADING);\n\t\tMod_ParseInfoFromEntityLump(cl.worldmodel);\n\t}\n\tShader_DoReload();\n\n#ifdef THREADEDWORLD\n\tCvar_ForceCallback(&r_temporalscenecache);\n#endif\n\n\tif (!pe)\n\t\tCvar_ForceCallback(&r_particlesystem);\n\tR_Clutter_Purge();\nTRACE((\"dbg: Surf_NewMap: wiping them stains (getting the cloth out)\\n\"));\n\tSurf_WipeStains();\nTRACE((\"dbg: Surf_NewMap: building lightmaps\\n\"));\n\tSurf_BuildLightmaps ();\n\n\nTRACE((\"dbg: Surf_NewMap: ui\\n\"));\n#ifdef VM_UI\n\tif (q3)\n\t\tq3->ui.Reset();\n#endif\nTRACE((\"dbg: Surf_NewMap: tp\\n\"));\n\tTP_NewMap();\n\n\tfor (i = 0; i < cl.num_statics; i++)\n\t{\n\t\tvec3_t mins, maxs;\n\t\t//fixme: no rotation\n\t\tif (!cl_static_entities[i].ent.model && cl_static_entities[i].mdlidx > 0 && cl_static_entities[i].mdlidx < countof(cl.model_precache))\n\t\t\tcl_static_entities[i].ent.model = cl.model_precache[cl_static_entities[i].mdlidx];\n\t\telse if (!cl_static_entities[i].ent.model && cl_static_entities[i].mdlidx < 0 && (-cl_static_entities[i].mdlidx) < countof(cl.model_csqcprecache))\n\t\t\tcl_static_entities[i].ent.model = cl.model_csqcprecache[-cl_static_entities[i].mdlidx];\n\t\tif (cl_static_entities[i].ent.model)\n\t\t{\n\t\t\t//unfortunately, we need to know the actual size so that we can get this right. bum.\n\t\t\tif (cl_static_entities[i].ent.model->loadstate == MLS_NOTLOADED)\n\t\t\t\tMod_LoadModel(cl_static_entities[i].ent.model, MLV_WARNSYNC);\n\t\t\tif (cl_static_entities[i].ent.model->loadstate == MLS_LOADING)\n\t\t\t\tCOM_WorkerPartialSync(cl_static_entities[i].ent.model, &cl_static_entities[i].ent.model->loadstate, MLS_LOADING);\n\t\t\tVectorAdd(cl_static_entities[i].ent.origin, cl_static_entities[i].ent.model->mins, mins);\n\t\t\tVectorAdd(cl_static_entities[i].ent.origin, cl_static_entities[i].ent.model->maxs, maxs);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tVectorCopy(mins, cl_static_entities[i].ent.origin);\n\t\t\tVectorCopy(maxs, cl_static_entities[i].ent.origin);\n\t\t}\n\t\tif (cl.worldmodel && cl.worldmodel->loadstate == MLS_LOADED)\n\t\t\tcl.worldmodel->funcs.FindTouchedLeafs(cl.worldmodel, &cl_static_entities[i].ent.pvscache, mins, maxs);\n\t\tcl_static_entities[i].emit = trailkey_null;\n\t}\n\n\tCL_InitDlights();\n#ifdef RTLIGHTS\n\tSh_PreGenerateLights();\n#endif\n}\n\nvoid Surf_PreNewMap(void)\n{\n\textern cvar_t gl_specular;\n\n\tr_loadbumpmapping = r_deluxemapping || r_glsl_offsetmapping.ival;\n\tr_loadbumpmapping |= gl_specular.value>0;\n#ifdef RTLIGHTS\n\tr_loadbumpmapping |= r_shadow_realtime_world.ival || r_shadow_realtime_dlight.ival;\n#endif\n\tr_viewcluster = -1;\n\tr_viewcluster2 = -1;\n\n\tShader_DoReload();\n}\n\n\n\nstatic float sgn(float a)\n{\n    if (a > 0.0F) return (1.0F);\n    if (a < 0.0F) return (-1.0F);\n    return (0.0F);\n}\nvoid R_ObliqueNearClip(float *viewmat, mplane_t *wplane)\n{\n\tfloat f;\n\tvec4_t q, c;\n\tvec3_t ping, pong;\n\tvec4_t vplane;\n\n\t//convert world plane into view space\n\tMatrix4x4_CM_Transform3x3(viewmat, wplane->normal, vplane);\n\tVectorScale(wplane->normal, wplane->dist, ping);\n\tMatrix4x4_CM_Transform3(viewmat, ping, pong);\n\tvplane[3] = -DotProduct(pong, vplane);\n\n\t// Calculate the clip-space corner point opposite the clipping plane\n\t// as (sgn(clipPlane.x), sgn(clipPlane.y), 1, 1) and\n\t// transform it into camera space by multiplying it\n\t// by the inverse of the projection matrix\n\n\tq[0] = (sgn(vplane[0]) + r_refdef.m_projection_std[8]) / r_refdef.m_projection_std[0];\n\tq[1] = (sgn(vplane[1]) + fabs(r_refdef.m_projection_std[9])) / fabs(r_refdef.m_projection_std[5]);\n\tq[2] = -1.0F;\n\tq[3] = (1.0F + r_refdef.m_projection_std[10]) / r_refdef.m_projection_std[14];\n\n\t// Calculate the scaled plane vector\n\tf = 2.0F / DotProduct4(vplane, q);\n\tVector4Scale(vplane, f, c);\n\n\t// Replace the third row of the projection matrix\n\tr_refdef.m_projection_std[2] = c[0];\n\tr_refdef.m_projection_std[6] = c[1];\n\tr_refdef.m_projection_std[10] = c[2] + 1.0F;\n\tr_refdef.m_projection_std[14] = c[3];\n}\n\n\n#endif\n"
  },
  {
    "path": "engine/client/render.h",
    "content": "/*\nCopyright (C) 1996-1997 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  \n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n*/\n\n// refresh.h -- public interface to refresh functions\n\n// default soldier colors\n#define TOP_DEFAULT\t\t1\n#define BOTTOM_DEFAULT\t6\n\n#define\tTOP_RANGE\t\t(TOP_DEFAULT<<4)\n#define\tBOTTOM_RANGE\t(BOTTOM_DEFAULT<<4)\n\nstruct msurface_s;\nstruct batch_s;\nstruct model_s;\nstruct texnums_s;\nstruct texture_s;\n\nstatic const texid_t r_nulltex = NULL;\n\n//GLES2 requires GL_UNSIGNED_SHORT (gles3 or GL_OES_element_index_uint relax this requirement)\n//geforce4 only does shorts. gffx can do ints, but with a performance hit (like most things on that gpu)\n//ati is generally more capable, but generally also has a smaller market share\n//desktop-gl will generally cope with ints, but expect a performance hit from that with old gpus (so we don't bother)\n//vulkan+dx10 can cope with ints, but might be 24bit\n//either way, all renderers in the same build need to use the same thing.\n#ifndef sizeof_index_t\n\t#ifdef VERTEXINDEXBYTES\t//maybe set in config_*.h\n\t\t#define sizeof_index_t VERTEXINDEXBYTES\n\t#elif (defined(GLQUAKE) && defined(HAVE_LEGACY)) || defined(MINIMAL) || defined(D3D8QUAKE) || defined(D3D9QUAKE) || defined(ANDROID) || defined(FTE_TARGET_WEB)\n\t\t#define sizeof_index_t 2\n\t#endif\n#endif\n#if sizeof_index_t == 2\n\t#define GL_INDEX_TYPE GL_UNSIGNED_SHORT\n\t#define D3DFMT_QINDEX D3DFMT_INDEX16\n\t#define DXGI_FORMAT_INDEX_UINT DXGI_FORMAT_R16_UINT\n\t#define VK_INDEX_TYPE VK_INDEX_TYPE_UINT16\n\ttypedef unsigned short index_t;\n\t#define MAX_INDICIES 0xffffu\n#else\n\t#undef sizeof_index_t\n\t#define sizeof_index_t 4\n\t#define GL_INDEX_TYPE GL_UNSIGNED_INT\n\t#define D3DFMT_QINDEX D3DFMT_INDEX32\n\t#define DXGI_FORMAT_INDEX_UINT DXGI_FORMAT_R32_UINT\n\t#define VK_INDEX_TYPE VK_INDEX_TYPE_UINT32\n\ttypedef unsigned int index_t;\n\t#define MAX_INDICIES 0x00ffffffu\n#endif\n\n//=============================================================================\n\n//the eye doesn't see different colours in the same proportion.\n//must add to slightly less than 1\n#define NTSC_RED 0.299\n#define NTSC_GREEN 0.587\n#define NTSC_BLUE 0.114\n#define NTSC_SUM (NTSC_RED + NTSC_GREEN + NTSC_BLUE)\n\ntypedef enum {\n\tRT_MODEL,\n\tRT_POLY,\n\tRT_SPRITE,\n\tRT_BEAM,\n\tRT_RAIL_CORE,\n\tRT_RAIL_RINGS,\n\tRT_LIGHTNING,\n\tRT_PORTALSURFACE,\t\t// doesn't draw anything, just info for portals\n\t//q3 ones stop here.\n\n\t//fte ones start here\n\tRT_PORTALCAMERA,\t\t// an alternative to RT_PORTALSURFACE.\n\n\tRT_MAX_REF_ENTITY_TYPE\n} refEntityType_t;\n\ntypedef unsigned int skinid_t;\t//skin 0 is 'unused'\n\nstruct dlight_s;\ntypedef struct entity_s\n{\n\t//FIXME: instancing somehow. separate visentity+visinstance. only viable with full glsl though.\n\t//will need to generate a vbo somehow for the instances.\n\n\tint\t\t\t\t\t\tkeynum;\t\t\t// for matching entities in different frames\n\tvec3_t\t\t\t\t\torigin;\n\tvec3_t\t\t\t\t\tangles;\t\t\t// fixme: should be redundant.\n\tvec3_t\t\t\t\t\taxis[3];\n\n\tvec4_t\t\t\t\t\tshaderRGBAf; /*colormod+alpha, available for shaders to mix*/\n\tfloat\t\t\t\t\tshaderTime;  /*timestamp, for syncing shader times to spawns*/\n\tvec3_t\t\t\t\t\tglowmod;     /*meant to be a multiplier for the fullbrights*/\n\n\tint\t\t\t\t\t\tlight_known; /*bsp lighting has been calced*/\n\tvec3_t\t\t\t\t\tlight_avg;   /*midpoint level*/\n\tvec3_t\t\t\t\t\tlight_range; /*avg + this = max, avg - this = min*/\n\tvec3_t\t\t\t\t\tlight_dir;\n\n\tvec3_t\t\t\t\t\toldorigin;\t/*for q2/q3 beams*/\n\n\tstruct model_s\t\t\t*model;\t\t\t// NULL = no model\n\tint\t\t\t\t\t\tskinnum;\t\t// for Alias models\n\tskinid_t\t\t\t\tcustomskin;\t\t// quake3 style skins\n\n\tint\t\t\t\t\t\tplayerindex;\t//for qw skins\n\tint\t\t\t\t\t\ttopcolour;\t\t//colourmapping\n\tint\t\t\t\t\t\tbottomcolour;\t//colourmapping\n#ifdef HEXEN2\n\tint\t\t\t\t\t\th2playerclass;\t//hexen2's quirky colourmapping\n#endif\n\n//\tstruct efrag_s\t\t\t*efrag;\t\t\t// linked list of efrags (FIXME)\n//\tint\t\t\t\t\t\tvisframe;\t\t// last frame this entity was\n\t\t\t\t\t\t\t\t\t\t\t// found in an active leaf\n\t\t\t\t\t\t\t\t\t\t\t// only used for static objects\n\t\t\t\t\t\t\t\t\t\t\t\n//\tint\t\t\t\t\t\tdlightframe;\t// dynamic lighting\n//\tdlightbitmask_t\t\t\tdlightbits;\n\t\n// FIXME: could turn these into a union\n//\tint\t\t\t\t\t\ttrivial_accept;\n//\tstruct mnode_s\t\t\t*topnode;\t\t// for bmodels, first world node\n\t\t\t\t\t\t\t\t\t\t\t//  that splits bmodel, or NULL if\n\t\t\t\t\t\t\t\t\t\t\t//  not split\n\n\tframestate_t\t\t\tframestate;\n\n\tint flags;\n\n\trefEntityType_t rtype;\n\tfloat rotation;\n\n\tstruct shader_s *forcedshader;\n\n\tpvscache_t pvscache; //for culling of csqc ents.\n\n#ifdef PEXT_SCALE\n\tfloat scale;\n#endif\n#ifdef PEXT_FATNESS\n\tfloat fatness;\n#endif\n#ifdef HEXEN2\n\tint drawflags;\n\tint abslight;\n#endif\n} entity_t;\n\n#define MAX_GEOMSETS 32u\n#define Q1UNSPECIFIED 0x00ffffff\t//0xffRRGGBB or 0x0000000V are both valid values. so this is an otherwise-illegal value to say its not been set.\ntypedef struct\n{\n\tint refcount;\n\tchar skinname[MAX_QPATH];\n\tint nummappings;\n\tint maxmappings;\n\tqbyte geomset[MAX_GEOMSETS];\t//allows selecting a single set of geometry from alternatives. this might be a can of worms.\n#ifdef QWSKINS\n\tchar qwskinname[MAX_QPATH];\n\tstruct qwskin_s *qwskin;\n\tunsigned int q1upper;\t//Q1UNSPECIFIED\n\tunsigned int q1lower;\t//Q1UNSPECIFIED\n\tunsigned int h2class;\t//Q1UNSPECIFIED. urgh.\n#endif\n\tstruct\n\t{\n\t\tchar surface[MAX_QPATH];\n\t\tshader_t *shader;\n\t\ttexnums_t texnums;\n\t\tint needsfree;\t//which textures need to be freed.\n\t} mappings[1];\n} skinfile_t;\n\n// plane_t structure\ntypedef struct mplane_s\n{\n\tvec3_t\tnormal;\n\tfloat\tdist;\n\tqbyte\ttype;\t\t\t// for texture axis selection and fast side tests\n\tqbyte\tsignbits;\t\t// signx + signy<<1 + signz<<1\n\tqbyte\tpad[2];\n} mplane_t;\n#define MAXFRUSTUMPLANES 7\t//4 side, 1 near, 1 far (fog), 1 water plane.\n\ntypedef struct\n{\n\t//note: uniforms expect specific padding/ordering. be really careful with reordering this\n\tvec3_t colour;\t\t//w_fog[0].xyz\n\tfloat alpha;\t\t//w_fog[0].w scales clamped fog value\n\tfloat density;\t\t//w_fog[1].x egads, everyone has a different opinion.\n\tfloat depthbias;\t//w_fog[1].y distance until the fog actually starts\n\tfloat glslpad1;\t\t//w_fog[1].z\n\tfloat glslpad2;\t\t//w_fog[1].w\n\n//\tfloat start;\n//\tfloat end;\n//\tfloat height;\n//\tfloat fadedepth;\n\n\tfloat time;\t//timestamp for when its current.\n} fogstate_t;\nvoid CL_BlendFog(fogstate_t *result, fogstate_t *oldf, float time, fogstate_t *newf);\nvoid CL_ResetFog(int fogtype);\n\ntypedef enum {\n\tSTEREO_OFF,\n\tSTEREO_QUAD,\n\tSTEREO_RED_CYAN,\n\tSTEREO_RED_BLUE,\n\tSTEREO_RED_GREEN,\n\tSTEREO_CROSSEYED,\n\n\t//these are internal methods and do not form part of any public API\n\tSTEREO_LEFTONLY,\n\tSTEREO_RIGHTONLY\n} stereomethod_t;\n\ntypedef enum\n{\n\tPROJ_STANDARD\t\t\t= 0,\n\tPROJ_STEREOGRAPHIC\t\t= 1,\n\tPROJ_FISHEYE\t\t\t= 2,\t//standard fisheye\n\tPROJ_PANORAMA\t\t\t= 3,\t//for nice panoramas\n\tPROJ_LAEA\t\t\t\t= 4,\t//lambert azimuthal equal-area \n\tPROJ_EQUIRECTANGULAR\t= 5,\t//projects a sphere into 2d. used by vr screenshots.\n\tPROJ_PANINI       = 6\t\t//like stereographic, but vertical lines stay straight.\n} qprojection_t;\n\ntypedef struct {\n\tchar texname[MAX_QPATH];\n} rtname_t;\n#define R_MAX_RENDERTARGETS 8\n\n#ifndef R_MAX_RECURSE\n#define R_MAX_RECURSE\t6\n#endif\n#define RDFD_FOV 1\ntypedef struct refdef_s\n{\n\tvrect_t\t\tgrect;\t\t\t\t// game rectangle. fullscreen except for csqc/splitscreen/hud.\n\tvrect_t\t\tvrect;\t\t\t\t// subwindow in grect for 3d view. equal to grect if no hud.\n\n\tvec3_t\t\tpvsorigin;\t\t\t/*render the view using this point for pvs (useful for mirror views)*/\n\tvec3_t\t\tvieworg;\t\t\t/*logical view center*/\n\tvec3_t\t\tviewangles;\n\tvec3_t\t\tviewaxis[3];\t\t/*forward, left, up (NOT RIGHT)*/\n\tvec3_t\t\teyeoffset;\t\t\t/*world space, for vr screenies*/\n\tvec2_t\t\tprojectionoffset;\t/*for off-centre rendering*/\n\n\tqboolean\tbase_known;\t\t\t/*otherwise we do some fallback behaviour (ie: viewangles.0y0 and forcing input_angles)*/\n\tvec3_t\t\tbase_angles, base_origin; /*for vr output, overrides per-eye viewangles according to that eye's matrix.*/\n\n\tvec3_t\t\tweaponmatrix[4];\t\t/*forward/left/up/origin*/\n\tvec3_t\t\tweaponmatrix_bob[4];\t/*forward/left/up/origin*/\n\n\tfloat\t\tfov_x, fov_y, afov;\n\tfloat\t\tfovv_x, fovv_y;\t//viewmodel fovs\n\tfloat\t\tmindist, maxdist;\t//maxdist may be 0, for 'infinite', in which case mindist probably isn't valid either.\n\n\tqboolean\tdrawsbar;\n\tqboolean\tdrawcrosshair;\n\tint\t\t\tflags;\t//(Q2)RDF_ flags\n\tint\t\t\tdirty;\n\n\tplayerview_t *playerview;\n//\tint\t\t\tcurrentplayernum;\n\n\tfloat\t\ttime;\n//\tfloat\t\twaterheight;\t//updated by the renderer. stuff sitting at this height generate ripple effects\n\n\tfloat\t\tm_projection_std[16];\t//projection matrix for normal stuff\n\tfloat\t\tm_projection_view[16];\t//projection matrix for the viewmodel. because people are weird.\n\tfloat\t\tm_view[16];\n\tqbyte\t\t*scenevis;\t\t\t/*this is the vis that's currently being draw*/\n\tint\t\t\t*sceneareas;\t\t/*this is the area info for the camera (should normally be count+one area, but could be two areas near an opaque water plane)*/\n\n\tmplane_t\tfrustum[MAXFRUSTUMPLANES];\n\tint\t\t\tfrustum_numworldplanes;\t//all but far, which isn't culled because this wouldn't cover the entire screen.\n\tint\t\t\tfrustum_numplanes;\t//includes far plane (which is reduced with fog).\n\n\tfogstate_t\tglobalfog;\n\tfloat\t\thdr_value;\n\n\tvec3_t\t\tskyroom_pos;\t\t/*the camera position for sky rooms*/\n\tvec4_t\t\tskyroom_spin;\t\t/*the camera spin for sky rooms*/\n\tqboolean\tskyroom_enabled;\t/*whether a skyroom position is defined*/\n\tint\t\t\tfirstvisedict;\t\t/*so we can skip visedicts in skies*/\n\n\tpxrect_t\tpxrect;\t\t\t\t/*vrect, but in pixels rather than virtual coords*/\n\tqboolean\texternalview;\t\t/*draw external models and not viewmodels*/\n\tint\t\t\trecurse;\t\t\t/*in a mirror/portal/half way through drawing something else*/\n\tqboolean\tforcevis;\t\t\t/*if true, vis comes from the forcedvis field instead of recalculated*/\n\tunsigned int\tflipcull;\t\t/*reflected/flipped view, requires inverted culling (should be set to SHADER_CULL_FLIPPED or 0 - its implemented as a xor)*/\n\tunsigned int\tcolourmask;\t\t/*shaderbits mask. anything not here will be forced to 0. this is for red/green type stereo*/\n\tqboolean\tuseperspective;\t\t/*not orthographic*/\n\n\tstereomethod_t stereomethod;\n\trtname_t\trt_destcolour[R_MAX_RENDERTARGETS];\t/*used for 2d. written by 3d*/\n\trtname_t\trt_sourcecolour;\t/*read by 2d. not used for 3d. */\n\trtname_t\trt_depth;\t\t\t/*read by 2d. used by 3d (renderbuffer used if not set)*/\n\trtname_t\trt_ripplemap;\t\t/*read by 2d. used by 3d (internal ripplemap buffer used if not set)*/\n\trtname_t\tnearenvmap;\t\t\t/*provides a fallback endmap cubemap to render with*/\n\n\tqbyte\t\t*forcedvis;\t\t\t/*set if forcevis is set*/\n\tqboolean\tareabitsknown;\n\tqbyte\t\tareabits[MAX_MAP_AREA_BYTES];\n\n\tvec4_t\t\tuserdata[16];\t\t/*for custom glsl*/\n\n\tqboolean\twarndraw;\t\t\t/*buggy gamecode likes drawing outside of te drawing logic*/\n\n\tqboolean\tfixedview;\t\t\t/*if true, use a fixed camera setup for out-of-body screenshots (usually to build cubemaps)*/\n\tvec3_t\t\tfixedvieworg;\n\tvec3_t\t\tfixedviewangles;\n} refdef_t;\n\nextern\trefdef_t\tr_refdef;\nextern vec3_t\tr_origin, vpn, vright, vup;\n\nextern\tstruct texture_s\t*r_notexture_mip;\n\nextern\tentity_t\tr_worldentity;\n\nvoid BE_GenModelBatches(struct batch_s **batches, const struct dlight_s *dl, unsigned int bemode, const qbyte *worldpvs, const int *worldareas);\t//if dl, filters based upon the dlight.\n\n//gl_alias.c\nvoid R_GAliasFlushSkinCache(qboolean final);\nvoid R_GAlias_DrawBatch(struct batch_s *batch);\nvoid R_GAlias_GenerateBatches(entity_t *e, struct batch_s **batches);\nvoid R_LightArraysByte_BGR(const entity_t *entity, vecV_t *coords, byte_vec4_t *colours, int vertcount, vec3_t *normals, qboolean colormod);\nvoid R_LightArrays(const entity_t *entity, vecV_t *coords, avec4_t *colours, int vertcount, vec3_t *normals, float scale, qboolean colormod);\n\nqboolean R_DrawSkyChain (struct batch_s *batch); /*called from the backend, and calls back into it*/\nvoid R_InitSky (shader_t *shader, const char *skyname, uploadfmt_t fmt, qbyte *src, unsigned int width, unsigned int height);\t/*generate q1 sky texnums*/\n\nvoid R_Clutter_Emit(struct batch_s **batches);\nvoid R_Clutter_Purge(void);\n\n//r_surf.c\nvoid Surf_NewMap (struct model_s *worldmodel);\nvoid Surf_PreNewMap(void);\nvoid Surf_SetupFrame(void);\t//determine pvs+viewcontents\nvoid Surf_DrawWorld(void);\nvoid Surf_GenBrushBatches(struct batch_s **batches, entity_t *ent);\nvoid Surf_StainSurf(struct model_s *mod, struct msurface_s *surf, float *parms);\nvoid Surf_AddStain(vec3_t org, float red, float green, float blue, float radius);\nvoid Surf_LessenStains(void);\nvoid Surf_WipeStains(void);\nvoid Surf_DeInit(void);\nvoid Surf_Clear(struct model_s *mod);\nvoid Surf_BuildLightmaps(void);\t\t\t\t//enables Surf_BuildModelLightmaps, calls it for each bsp.\nvoid Surf_ClearSceneCache(void);\t\t\t//stops Surf_BuildModelLightmaps from working.\nvoid Surf_BuildModelLightmaps (struct model_s *m);\t//rebuild lightmaps for a single bsp. beware of submodels.\nvoid Surf_RenderDynamicLightmaps (struct msurface_s *fa);\nvoid Surf_RenderAmbientLightmaps (struct msurface_s *fa, int ambient);\nint Surf_LightmapShift (struct model_s *model);\n#define LMBLOCK_SIZE_MAX 2048\t//single axis\ntypedef struct glRect_s {\n\tunsigned short l,t,r,b;\n} glRect_t;\ntypedef unsigned char stmap;\nstruct mesh_s;\ntypedef struct {\n\ttexid_t lightmap_texture;\n\tqboolean\tmodified;\t//data was changed. consult rectchange to see the bounds.\n\tqboolean\texternal;\t//the data was loaded from a file (q3bsp feature where we shouldn't be blending lightmaps at all)\n\tqboolean\thasdeluxe;\t//says that the next lightmap index contains deluxemap info\n\tuploadfmt_t\tfmt;\t\t//texture format that we're using\n\tqbyte\t\tpixbytes;\t//yes, this means no compressed formats.\n\tint\t\t\twidth;\n\tint\t\t\theight;\n\tglRect_t\trectchange;\n\tqbyte\t\t*lightmaps;\t//[pixbytes*LMBLOCK_WIDTH*LMBLOCK_HEIGHT];\n\tstmap\t\t*stainmaps;\t//[3*LMBLOCK_WIDTH*LMBLOCK_HEIGHT];\t//rgb no a. added to lightmap for added (hopefully) speed.\n#ifdef GLQUAKE\n\tint\t\t\tpbo_handle;\t//when set, lightmaps is a persistently mapped write-only pbo for us to scribble data into, ready to be copied to the actual texture without waiting for glTexSubImage to complete.\n#endif\n} lightmapinfo_t;\nextern lightmapinfo_t **lightmap;\nextern int numlightmaps;\n\nvoid QDECL Surf_RebuildLightmap_Callback (struct cvar_s *var, char *oldvalue);\n\n\nvoid R_Sky_Register(void);\nvoid R_SkyShutdown(void);\nvoid R_SetSky(const char *skyname);\ntexid_t R_GetDefaultEnvmap(void);\n\n#if defined(GLQUAKE)\nvoid GLR_Init (void);\nvoid GLR_InitTextures (void);\nvoid GLR_InitEfrags (void);\nvoid GLR_RenderView (void);\t\t// must set r_refdef first\n\t\t\t\t\t\t\t\t// called whenever r_refdef or vid change\nvoid GLR_DrawPortal(struct batch_s *batch, struct batch_s **blist, struct batch_s *depthmasklist[2], int portaltype);\n\nvoid GLR_PushDlights (void);\nvoid GLR_DrawWaterSurfaces (void);\n\nvoid GLVID_DeInit (void);\nvoid GLR_DeInit (void);\nvoid GLSCR_DeInit (void);\nvoid GLVID_Console_Resize(void);\n#endif\nint R_LightPoint (vec3_t p);\nvoid R_RenderDlights (void);\n\ntypedef struct\n{\n\tint allocated[LMBLOCK_SIZE_MAX];\n\tint firstlm;\n\tint lmnum;\n\tunsigned int width;\n\tunsigned int height;\n\tqboolean deluxe;\n} lmalloc_t;\nvoid Mod_LightmapAllocInit(lmalloc_t *lmallocator, qboolean hasdeluxe, unsigned int width, unsigned int height, int firstlm);\t//firstlm is for debugging stray lightmap indexes\n//void Mod_LightmapAllocDone(lmalloc_t *lmallocator, model_t *mod);\nvoid Mod_LightmapAllocBlock(lmalloc_t *lmallocator, int w, int h, unsigned short *x, unsigned short *y, int *tnum);\n\nenum imageflags\n{\n\t/*warning: many of these flags only apply the first time it is requested*/\n\tIF_CLAMP\t\t\t= 1<<0,\t\t//disable texture coord wrapping.\n\tIF_NOMIPMAP\t\t\t= 1<<1,\t\t//disable mipmaps.\n\tIF_NEAREST\t\t\t= 1<<2,\t\t//force nearest\n\tIF_LINEAR\t\t\t= 1<<3,\t\t//force linear\n\tIF_UIPIC\t\t\t= 1<<4,\t\t//subject to texturemode2d\n\t//IF_DEPTHCMD\t\t= 1<<5,\t\t//Reserved for d3d11\n\tIF_SRGB\t\t\t\t= 1<<6,\t\t//texture data is srgb (read-as-linear)\n\t/*WARNING: If the above are changed, be sure to change shader pass flags*/\n\n\tIF_NOPICMIP\t\t\t= 1<<7,\n\tIF_NOALPHA\t\t\t= 1<<8,\t\t/*hint rather than requirement*/\n\tIF_NOGAMMA\t\t\t= 1<<9,\t\t/*do not apply texture-based gamma*/\n\tIF_TEXTYPEMASK\t\t\t= (1<<10) | (1<<11) | (1<<12), /*0=2d, 1=3d, 2=cubeface, 3=2d array texture*/\n#define IF_TEXTYPESHIFT\t\t10\n#define IF_TEXTYPE_2D (PTI_2D<<IF_TEXTYPESHIFT)\n#define IF_TEXTYPE_3D (PTI_3D<<IF_TEXTYPESHIFT)\n#define IF_TEXTYPE_CUBE (PTI_CUBE<<IF_TEXTYPESHIFT)\n#define IF_TEXTYPE_2D_ARRAY (PTI_2D_ARRAY<<IF_TEXTYPESHIFT)\n#define IF_TEXTYPE_CUBE_ARRAY (PTI_CUBE_ARRAY<<IF_TEXTYPESHIFT)\n#define IF_TEXTYPE_ANY (PTI_ANY<<IF_TEXTYPESHIFT)\n\tIF_MIPCAP\t\t\t= 1<<13,\t//allow the use of d_mipcap\n\tIF_PREMULTIPLYALPHA\t= 1<<14,\t//rgb *= alpha\n\n\tIF_UNUSED15\t\t\t= 1<<15,\t//\n\tIF_UNUSED16\t\t\t= 1<<16,\t//\n\tIF_INEXACT\t\t\t= 1<<17,\t//subdir info isn't to be used for matching\n\n\tIF_WORLDTEX\t\t\t= 1<<18,\t//gl_picmip_world\n\tIF_SPRITETEX\t\t= 1<<19,\t//gl_picmip_sprites\n\tIF_NOSRGB\t\t\t= 1<<20,\t//ignore srgb when loading. this is guarenteed to be linear, for normalmaps etc.\n\n\tIF_PALETTIZE\t\t= 1<<21,\t//convert+load it as an RTI_P8 texture for the current palette/colourmap\n\tIF_NOPURGE\t\t\t= 1<<22,\t//texture is not flushed when no more shaders refer to it (for C code that holds a permanant reference to it - still purged on vid_reloads though)\n\tIF_HIGHPRIORITY\t\t= 1<<23,\t//pushed to start of worker queue instead of end...\n\tIF_LOWPRIORITY\t\t= 1<<24,\t//\n\tIF_LOADNOW\t\t\t= 1<<25,\t/*hit the disk now, and delay the gl load until its actually needed. this is used only so that the width+height are known in advance. valid on worker threads.*/\n\tIF_NOPCX\t\t\t= 1<<26,\t/*block pcx format. meaning qw skins can use team colours and cropping*/\n\tIF_TRYBUMP\t\t\t= 1<<27,\t/*attempt to load _bump if the specified _norm texture wasn't found*/\n\tIF_RENDERTARGET\t\t= 1<<28,\t/*never loaded from disk, loading can't fail*/\n\tIF_EXACTEXTENSION\t= 1<<29,\t/*don't mangle extensions, use what is specified and ONLY that*/\n\tIF_NOREPLACE\t\t= 1<<30,\t/*don't load a replacement, for some reason*/\n\tIF_NOWORKER\t\t\t= 1u<<31\t/*don't pass the work to a loader thread. this gives fully synchronous loading. only valid from the main thread.*/\n};\n\n#define R_LoadTexture8(id,w,h,d,f,t)\t\tImage_GetTexture(id, NULL, f, d, NULL, w, h, t?TF_TRANS8:TF_SOLID8)\n#define R_LoadTexture32(id,w,h,d,f)\t\t\tImage_GetTexture(id, NULL, f, d, NULL, w, h, TF_RGBA32)\n#define R_LoadTextureFB(id,w,h,d,f)\t\t\tImage_GetTexture(id, NULL, f, d, NULL, w, h, TF_TRANS8_FULLBRIGHT)\n#define R_LoadTexture(id,w,h,fmt,d,fl)\t\tImage_GetTexture(id, NULL, fl, d, NULL, w, h, fmt)\n\nimage_t *Image_TextureIsValid(qintptr_t address);\nimage_t *Image_FindTexture\t(const char *identifier, const char *subpath, unsigned int flags);\nimage_t *Image_CreateTexture(const char *identifier, const char *subpath, unsigned int flags);\nimage_t *QDECL Image_GetTexture\t(const char *identifier, const char *subpath, unsigned int flags, void *fallbackdata, void *fallbackpalette, int fallbackwidth, int fallbackheight, uploadfmt_t fallbackfmt);\nqboolean Image_UnloadTexture(image_t *tex);\t//true if it did something.\nvoid Image_DestroyTexture\t(image_t *tex);\nqboolean Image_LoadTextureFromMemory(texid_t tex, int flags, const char *iname, const char *fname, qbyte *filedata, int filesize);\t//intended really for worker threads, but should be fine from the main thread too\nqboolean Image_LocateHighResTexture(image_t *tex, flocation_t *bestloc, char *bestname, size_t bestnamesize, unsigned int *bestflags);\nvoid Image_Upload\t\t\t(texid_t tex, uploadfmt_t fmt, void *data, void *palette, int width, int height, int depth, unsigned int flags);\nvoid Image_Purge(void);\t//purge any textures which are not needed any more (releases memory, but doesn't give null pointers).\nvoid Image_Init(void);\nvoid Image_Shutdown(void);\nvoid Image_PrintInputFormatVersions(void); //for version info\nqboolean Image_WriteKTXFile(const char *filename, enum fs_relative fsroot, struct pendingtextureinfo *mips);\nqboolean Image_WriteDDSFile(const char *filename, enum fs_relative fsroot, struct pendingtextureinfo *mips);\nvoid Image_BlockSizeForEncoding(uploadfmt_t encoding, unsigned int *blockbytes, unsigned int *blockwidth, unsigned int *blockheight, unsigned int *blockdepth);\nconst char *Image_FormatName(uploadfmt_t encoding);\nqboolean Image_FormatHasAlpha(uploadfmt_t encoding);\nimage_t *Image_LoadTexture\t(const char *identifier, int width, int height, uploadfmt_t fmt, void *data, unsigned int flags);\nstruct pendingtextureinfo *Image_LoadMipsFromMemory(int flags, const char *iname, const char *fname, qbyte *filedata, int filesize);\nvoid Image_ChangeFormat(struct pendingtextureinfo *mips, qboolean *allowedformats, uploadfmt_t origfmt, const char *imagename);\nvoid Image_Premultiply(struct pendingtextureinfo *mips);\nvoid *Image_FlipImage(const void *inbuffer, void *outbuffer, int *inoutwidth, int *inoutheight, int pixelbytes, qboolean flipx, qboolean flipy, qboolean flipd);\n\ntypedef struct\n{\n\tconst char *loadername;\n\tsize_t pendingtextureinfosize;\n\tqboolean canloadcubemaps;\n\tstruct pendingtextureinfo *(*ReadImageFile)(unsigned int imgflags, const char *fname, qbyte *filedata, size_t filesize);\n#define plugimageloaderfuncs_name \"ImageLoader\"\n} plugimageloaderfuncs_t;\nqboolean Image_RegisterLoader(void *module, plugimageloaderfuncs_t *loader);\n\n#ifdef D3D8QUAKE\nvoid\t\tD3D8_Set2D (void);\nvoid\t\tD3D8_UpdateFiltering\t(image_t *imagelist, int filtermip[3], int filterpic[3], int mipcap[2], float anis);\nqboolean\tD3D8_LoadTextureMips\t(texid_t tex, const struct pendingtextureinfo *mips);\nvoid\t\tD3D8_DestroyTexture\t\t(texid_t tex);\n#endif\n#ifdef D3D9QUAKE\nvoid\t\tD3D9_Set2D (void);\nvoid\t\tD3D9_UpdateFiltering\t(image_t *imagelist, int filtermip[3], int filterpic[3], int mipcap[2], float lodbias, float anis);\nqboolean\tD3D9_LoadTextureMips\t(texid_t tex, const struct pendingtextureinfo *mips);\nvoid\t\tD3D9_DestroyTexture\t\t(texid_t tex);\n#endif\n#ifdef D3D11QUAKE\nvoid\t\tD3D11_UpdateFiltering\t(image_t *imagelist, int filtermip[3], int filterpic[3], int mipcap[2], float lodbias, float anis);\nqboolean\tD3D11_LoadTextureMips\t(texid_t tex, const struct pendingtextureinfo *mips);\nvoid\t\tD3D11_DestroyTexture\t(texid_t tex);\n#endif\n\n//extern int image_width, image_height;\ntexid_t R_LoadReplacementTexture(const char *name, const char *subpath, unsigned int flags, void *lowres, int lowreswidth, int lowresheight, uploadfmt_t fmt);\ntexid_tf R_LoadHiResTexture(const char *name, const char *subpath, unsigned int flags);\ntexid_tf R_LoadBumpmapTexture(const char *name, const char *subpath);\nvoid R_LoadNumberedLightTexture(struct dlight_s *dl, int cubetexnum);\n\nextern\ttexid_t\tparticletexture;\nextern\ttexid_t particlecqtexture;\nextern\ttexid_t explosiontexture;\nextern\ttexid_t balltexture;\nextern\ttexid_t beamtexture;\nextern\ttexid_t ptritexture;\n\nskinid_t Mod_RegisterSkinFile(const char *skinname);\nskinid_t Mod_ReadSkinFile(const char *skinname, const char *skintext);\nvoid Mod_WipeSkin(skinid_t id, qboolean force);\nskinfile_t *Mod_LookupSkin(skinid_t id);\n\nvoid\tMod_Init (qboolean initial);\nvoid Mod_Shutdown (qboolean final);\nint Mod_TagNumForName(struct model_s *model, const char *name, int firsttag);\nint Mod_SkinNumForName(struct model_s *model, int surfaceidx, const char *name);\nint Mod_FrameNumForName(struct model_s *model, int surfaceidx, const char *name);\nint Mod_FrameNumForAction(struct model_s *model, int surfaceidx, int actionid);\nfloat Mod_GetFrameDuration(struct model_s *model, int surfaceidx, int frameno);\n\nvoid Mod_ResortShaders(void);\nvoid\tMod_ClearAll (void);\nstruct model_s *Mod_FindName (const char *name);\nvoid\t*Mod_Extradata (struct model_s *mod);\t// handles caching\nvoid\tMod_TouchModel (const char *name);\nvoid Mod_RebuildLightmaps (void);\n\nvoid Mod_NowLoadExternal(struct model_s *loadmodel);\nvoid GLR_LoadSkys (void);\nvoid R_BloomRegister(void);\n\nint Mod_RegisterModelFormatText(void *module, const char *formatname, char *magictext, qboolean (QDECL *load) (struct model_s *mod, void *buffer, size_t fsize));\nint Mod_RegisterModelFormatMagic(void *module, const char *formatname, qbyte *magic, size_t magicsize, qboolean (QDECL *load) (struct model_s *mod, void *buffer, size_t fsize));\nvoid Mod_UnRegisterModelFormat(void *module, int idx);\nvoid Mod_UnRegisterAllModelFormats(void *module);\nvoid Mod_ModelLoaded(void *ctx, void *data, size_t a, size_t b);\nvoid Mod_SubmodelLoaded(struct model_s *mod, int state);\n\n#ifdef RUNTIMELIGHTING\nstruct relight_ctx_s;\nstruct llightinfo_s;\nvoid LightPlane (struct relight_ctx_s *ctx, struct llightinfo_s *threadctx, lightstyleindex_t surf_styles[MAXCPULIGHTMAPS], unsigned int *surf_expsamples, qbyte *surf_rgbsamples, qbyte *surf_deluxesamples, vec4_t surf_plane, vec4_t surf_texplanes[2], vec2_t exactmins, vec2_t exactmaxs, int texmins[2], int texsize[2], float lmscale);\t//special version that doesn't know what a face is or anything.\nstruct relight_ctx_s *LightStartup(struct relight_ctx_s *ctx, struct model_s *model, qboolean shadows, qboolean skiplit);\nvoid LightReloadEntities(struct relight_ctx_s *ctx, const char *entstring, qboolean ignorestyles);\nvoid LightShutdown(struct relight_ctx_s *ctx, struct model_s *model);\nextern const size_t lightthreadctxsize;\n\nqboolean RelightSetup (struct model_s *model, size_t lightsamples, qboolean generatelit);\nvoid RelightThink (void);\nconst char *RelightGetProgress(float *progress);\t//reports filename and progress\nvoid RelightTerminate(struct model_s *mod);\t//NULL acts as a wildcard\n#endif\n\nstruct builddata_s\n{\n\tvoid (*buildfunc)(struct model_s *mod, struct msurface_s *surf, struct builddata_s *bd);\n\tqboolean paintlightmaps;\n\tvoid *facedata;\n};\nvoid Mod_Batches_Build(struct model_s *mod, struct builddata_s *bd);\nshader_t *Mod_RegisterBasicShader(struct model_s *mod, const char *texname, unsigned int usageflags, const char *shadertext, uploadfmt_t pixelfmt, unsigned int width, unsigned int height, void *pixeldata, void *palettedata);\n\nextern struct model_s\t\t*currentmodel;\n\nvoid Media_CaptureDemoEnd(void);\nvoid Media_RecordFrame (void);\nqboolean Media_PausedDemo (qboolean fortiming);\nint Media_Capturing (void);\ndouble Media_TweekCaptureFrameTime(double oldtime, double time);\nvoid Media_WriteCurrentTrack(sizebuf_t *buf);\nvoid Media_VideoRestarting(void);\nvoid Media_VideoRestarted(void);\n\nvoid MYgluPerspective(double fovx, double fovy, double zNear, double zFar);\n\nvoid\tR_PushDlights\t\t\t\t(void);\nvoid R_SetFrustum (float projmat[16], float viewmat[16]);\nvoid R_SetRenderer(rendererinfo_t *ri);\nqboolean R_RegisterRenderer(void *module, rendererinfo_t *ri);\nstruct plugvrfuncs_s;\nqboolean R_RegisterVRDriver(void *module, struct plugvrfuncs_s *vrfuncs);\nqboolean R_UnRegisterModule(void *module);\nvoid R_AnimateLight (void);\nvoid R_UpdateHDR(vec3_t org);\nvoid R_UpdateLightStyle(unsigned int style, const char *stylestring, float r, float g, float b);\nvoid R_BumpLightstyles(unsigned int maxstyle);\t//bumps the cl_max_lightstyles array size, if needed.\nqboolean R_CalcModelLighting(entity_t *e, struct model_s *clmodel);\nstruct texture_s *R_TextureAnimation (int frame, struct texture_s *base);\t//mostly deprecated, only lingers for rtlights so world only.\nstruct texture_s *R_TextureAnimation_Q2 (struct texture_s *base);\t//mostly deprecated, only lingers for rtlights so world only.\nvoid RQ_Init(void);\nvoid RQ_Shutdown(void);\n\nqboolean WritePCXfile (const char *filename, enum fs_relative fsroot, qbyte *data, int width, int height, int rowbytes, qbyte *palette, qboolean upload); //data is 8bit.\nqbyte *ReadPCXFile(qbyte *buf, int length, int *width, int *height);\nvoid *ReadTargaFile(qbyte *buf, int length, int *width, int *height, uploadfmt_t *format, qboolean greyonly, uploadfmt_t forceformat);\nqbyte *ReadPNGFile(const char *fname, qbyte *buf, int length, int *width, int *height, uploadfmt_t *format, qboolean force_rgb32);\nqbyte *ReadPCXPalette(qbyte *buf, int len, qbyte *out);\n#ifdef IMAGEFMT_PVR\nqbyte *ReadPVRFile(qbyte *buf, int len, int *width, int *height, uploadfmt_t *format, qboolean force_rgba8);\n#endif\n\nqbyte *ReadRawImageFile(qbyte *buf, int len, int *width, int *height, uploadfmt_t *format, qboolean force_rgba8, const char *fname);\nvoid *Image_ResampleTexture (uploadfmt_t format, const void *in, int inwidth, int inheight, void *out,  int outwidth, int outheight);\nvoid Image_ReadExternalAlpha(qbyte *rgbadata, size_t imgwidth, size_t imgheight, const char *fname, uploadfmt_t *format);\nvoid BoostGamma(qbyte *rgba, int width, int height, uploadfmt_t fmt);\nvoid SaturateR8G8B8(qbyte *data, int size, float sat);\nvoid AddOcranaLEDsIndexed (qbyte *image, int h, int w);\n\nvoid Renderer_Init(void);\nvoid Renderer_Start(void);\nqboolean Renderer_Started(void);\nvoid R_ShutdownRenderer(qboolean videotoo);\nvoid R_RestartRenderer_f (void);//this goes here so we can save some stack when first initing the sw renderer.\n\n//used to live in glquake.h\nqbyte GetPaletteIndexRange(int first, int stop, int red, int green, int blue);\nqbyte GetPaletteIndex(int red, int green, int blue);\nextern\tcvar_t\tr_norefresh;\nextern\tcvar_t\tr_drawentities;\nextern\tcvar_t\tr_drawworld;\nextern\tcvar_t\tr_drawviewmodel;\nextern\tcvar_t\tr_drawviewmodelinvis;\nextern\tcvar_t\tr_speeds;\nextern\tcvar_t\tr_waterwarp;\nextern\tcvar_t\tr_fullbright;\nextern\tcvar_t\tr_lightmap;\nextern\tcvar_t\tr_glsl_offsetmapping;\nextern\tcvar_t\tr_skyfog;\t//additional fog alpha on sky\nextern\tcvar_t\tr_shadow_playershadows;\nextern\tcvar_t\tr_shadow_realtime_dlight, r_shadow_realtime_dlight_shadows;\nextern\tcvar_t\tr_shadow_realtime_dlight_ambient;\nextern\tcvar_t\tr_shadow_realtime_dlight_diffuse;\nextern\tcvar_t\tr_shadow_realtime_dlight_specular;\nextern\tcvar_t\tr_shadow_realtime_world, r_shadow_realtime_world_shadows, r_shadow_realtime_world_lightmaps, r_shadow_realtime_world_importlightentitiesfrommap;\nextern\tfloat r_shadow_realtime_world_lightmaps_force;\nextern\tcvar_t\tr_shadow_raytrace;\nextern\tcvar_t\tr_shadow_shadowmapping;\nextern\tcvar_t\tr_halfrate;\nextern\tcvar_t\tr_mirroralpha;\nextern\tcvar_t\tr_wateralpha;\nextern\tcvar_t\tr_lavaalpha;\nextern\tcvar_t\tr_slimealpha;\nextern\tcvar_t\tr_telealpha;\nextern\tcvar_t\tr_waterstyle;\nextern\tcvar_t\tr_lavastyle;\nextern\tcvar_t\tr_slimestyle;\nextern\tcvar_t\tr_telestyle;\nextern\tcvar_t\tr_dynamic;\nextern qboolean r_dlightlightmaps;\nextern\tcvar_t\tr_temporalscenecache;\nextern\tcvar_t\tr_novis;\nextern\tcvar_t\tr_netgraph;\nextern\tcvar_t\tr_deluxemapping_cvar;\nextern\tqboolean r_deluxemapping;\n#ifdef RTLIGHTS\nextern\tqboolean r_fakeshadows; //enables the use of ortho model-only shadows\n#endif\nextern\tfloat\tr_blobshadows;\nextern\tcvar_t r_softwarebanding_cvar;\nextern\tqboolean r_softwarebanding;\nextern\tcvar_t r_lightprepass_cvar;\nextern\tint r_lightprepass;\t//0=off,1=16bit,2=32bit\n\nextern cvar_t\tr_xflip;\n\nextern cvar_t gl_mindist, gl_maxdist;\nextern\tcvar_t\tr_clear;\nextern\tcvar_t\tr_clearcolour;\nextern\tcvar_t\tgl_poly;\nextern\tcvar_t\tgl_affinemodels;\nextern\tcvar_t r_renderscale;\nextern\tcvar_t\tgl_nohwblend;\nextern\tcvar_t\tr_coronas, r_coronas_intensity, r_coronas_occlusion, r_coronas_mindist, r_coronas_fadedist, r_flashblend, r_flashblendscale;\nextern\tcvar_t\tr_lightstylesmooth;\nextern\tcvar_t\tr_lightstylesmooth_limit;\nextern\tcvar_t\tr_lightstylespeed;\nextern\tcvar_t\tr_lightstylescale;\nextern\tcvar_t\tr_lightmap_scale;\n#ifdef QWSKINS\nextern\tcvar_t\tgl_nocolors;\n#endif\nextern\tcvar_t\tgl_load24bit;\nextern\tcvar_t\tgl_finish;\n\nextern\tcvar_t\tgl_max_size;\nextern\tcvar_t\tgl_playermip;\n\nextern  cvar_t\tr_lightmap_saturation;\n\n#ifdef FTEPLUGIN\t//evil hack... boo hiss.\nextern cvar_t *cvar_r_meshpitch;\nextern cvar_t *cvar_r_meshroll;\n#define r_meshpitch (*cvar_r_meshpitch)\n#define r_meshroll (*cvar_r_meshroll)\n#else\nextern cvar_t r_meshpitch;\nextern cvar_t r_meshroll;\t//gah!\n#endif\nextern cvar_t vid_hardwaregamma;\n\nenum {\n\tRSPEED_TOTALREFRESH,\n\tRSPEED_CSQCPHYSICS,\n\tRSPEED_CSQCREDRAW,\n\tRSPEED_LINKENTITIES,\n\tRSPEED_WORLDNODE,\n\tRSPEED_DYNAMIC,\n\tRSPEED_OPAQUE,\n\tRSPEED_RTLIGHTS,\n\tRSPEED_TRANSPARENTS,\n\tRSPEED_PROTOCOL,\n\tRSPEED_PARTICLES,\n\tRSPEED_PARTICLESDRAW,\n\tRSPEED_PALETTEFLASHES,\n\tRSPEED_2D,\n\tRSPEED_SERVER,\n\tRSPEED_AUDIO,\n\tRSPEED_SETUP,\n\tRSPEED_SUBMIT,\n\tRSPEED_PRESENT,\n\tRSPEED_ACQUIRE,\n\n\tRSPEED_MAX\n};\nextern int rspeeds[RSPEED_MAX];\n\nenum {\n\tRQUANT_MSECS,\t//old r_speeds\n\tRQUANT_PRIMITIVEINDICIES,\n\tRQUANT_DRAWS,\n\tRQUANT_ENTBATCHES,\n\tRQUANT_WORLDBATCHES,\n\tRQUANT_2DBATCHES,\n\n\tRQUANT_SHADOWINDICIES,\n\tRQUANT_SHADOWEDGES,\n\tRQUANT_SHADOWSIDES,\n\tRQUANT_LITFACES,\n\n\tRQUANT_RTLIGHT_DRAWN,\n\tRQUANT_RTLIGHT_CULL_FRUSTUM,\n\tRQUANT_RTLIGHT_CULL_PVS,\n\tRQUANT_RTLIGHT_CULL_SCISSOR,\n\n\tRQUANT_MAX\n};\nextern int rquant[RQUANT_MAX];\n\n#define RQuantAdd(type,quant) rquant[type] += quant\n\n#if 0//defined(NDEBUG) || !defined(_WIN32)\n#define RSpeedLocals()\n#define RSpeedMark()\n#define RSpeedRemark()\n#define RSpeedEnd(spt)\n#else\n#define RSpeedLocals() double rsp\n#define RSpeedMark() double rsp = (r_speeds.ival>1)?Sys_DoubleTime()*1000000:0\n#define RSpeedRemark() rsp = (r_speeds.ival>1)?Sys_DoubleTime()*1000000:0\n\n#if defined(_WIN32) && defined(GLQUAKE)\nextern void (_stdcall *qglFinish) (void);\n#define RSpeedEnd(spt) do {if(r_speeds.ival > 1){if(r_speeds.ival > 2 && qglFinish)qglFinish(); rspeeds[spt] += (double)(Sys_DoubleTime()*1000000) - rsp;}}while (0)\n#else\n#define RSpeedEnd(spt) rspeeds[spt] += (r_speeds.ival>1)?Sys_DoubleTime()*1000000 - rsp:0\n#endif\n#endif\n"
  },
  {
    "path": "engine/client/renderer.c",
    "content": "#include \"quakedef.h\"\n#include \"winquake.h\"\n#include \"pr_common.h\"\n#include \"gl_draw.h\"\n#include \"shader.h\"\n#include \"glquake.h\"\n#include \"vr.h\"\n#include <string.h>\n\n#ifdef __GLIBC__\n#include <malloc.h>\t//for malloc_trim\n#endif\n\n\n#define DEFAULT_WIDTH 640\n#define DEFAULT_HEIGHT 480\n#define DEFAULT_BPP 32\n\nrefdef_t\tr_refdef;\nvec3_t\t\tr_origin, vpn, vright, vup;\nentity_t\tr_worldentity;\nentity_t\t*currententity;\t//nnggh\nint\t\t\tr_framecount;\nqboolean\tr_forceheadless;\nstruct texture_s\t*r_notexture_mip;\n\nint\tr_blockvidrestart;\t//'block' is a bit of a misnomer. 0=filesystem, configs, cinematics, video are all okay as they are. 1=starting up, waiting for filesystem, will restart after. 2=configs execed, but still need cinematics. 3=video will be restarted without any other init needed\nint r_regsequence;\n\nint rspeeds[RSPEED_MAX];\nint rquant[RQUANT_MAX];\n\nstatic void R_RegisterBuiltinRenderers(void);\nvoid R_InitParticleTexture (void);\nvoid R_RestartRenderer (rendererstate_t *newr);\nstatic void R_UpdateRendererOpts(void);\n\nqboolean vid_isfullscreen;\n\n#define VIDCOMMANDGROUP \"Video config\"\n#define GRAPHICALNICETIES \"Graphical Nicaties\"\t//or eyecandy, which ever you prefer.\n#define SCREENOPTIONS\t\"Screen Options\"\n\n#define GLRENDEREROPTIONS\t\"GL Renderer Options\" //fixme: often used for generic cvars that apply to more than just gl...\n#define D3DRENDEREROPTIONS\t\"D3D Renderer Options\"\n\nunsigned int\td_8to24rgbtable[256];\nunsigned int\td_8to24srgbtable[256];\nunsigned int\td_8to24bgrtable[256];\nunsigned int\td_quaketo24srgbtable[256];\n\nextern int gl_anisotropy_factor;\n\n// callbacks used for cvars\nvoid QDECL SCR_Viewsize_Callback (struct cvar_s *var, char *oldvalue);\nvoid QDECL SCR_Fov_Callback (struct cvar_s *var, char *oldvalue);\nvoid QDECL Image_TextureMode_Callback (struct cvar_s *var, char *oldvalue);\nstatic void QDECL R_Lightmap_Format_Changed(struct cvar_s *var, char *oldvalue)\n{\n\tif (qrenderer)\n\t\tSurf_BuildLightmaps();\n}\nstatic void QDECL R_HDR_FramebufferFormat_Changed(struct cvar_s *var, char *oldvalue)\n{\n\tint i;\n\tchar *e;\n\tfor (i = 0; i < PTI_MAX; i++)\n\t{\n\t\tif (!Q_strcasecmp(var->string, Image_FormatName(i)))\n\t\t{\n\t\t\tvar->ival = -i;\n\t\t\treturn;\n\t\t}\n\t}\n\tvar->ival = strtol(var->string, &e, 0);\n\tif (*e && e == var->string)\n\t\tCon_Printf(\"%s set to unknown image format\\n\", var->name);\n\tif (var->ival < 0)\n\t\tvar->ival = 0;\n}\nstatic void QDECL R_ClearColour_Changed(struct cvar_s *var, char *oldvalue)\n{\t//just exists to force ival=0 when string==\"R G B\", so we don't have to do it every frame.\n\t//don't bother baking the palette. that isn't quite robust when vid_reloading etc.\n\tchar *e;\n\tstrtol(var->string, &e, 0);\n\tif (*e)\n\t\tvar->ival = 0;\t//junk at the end means its an RGB value instead of a simple palette index.\n}\n\n#ifdef FTE_TARGET_WEB\t//webgl sucks too much to get a stable framerate without vsync.\ncvar_t vid_vsync\t\t\t\t\t\t\t= CVARAF  (\"vid_vsync\", \"1\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t   \"vid_wait\", CVAR_ARCHIVE);\n#else\ncvar_t vid_vsync\t\t\t\t\t\t\t= CVARAF  (\"vid_vsync\", \"0\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t   \"vid_wait\", CVAR_ARCHIVE);\n#endif\n\ncvar_t in_windowed_mouse\t\t\t\t\t\t= CVARF (\"in_windowed_mouse\",\"1\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t CVAR_ARCHIVE);\t//renamed this, because of freecs users complaining that it doesn't work. I don't personally see why you'd want it set to 0, but that's winquake's default so boo hiss to that.\n\ncvar_t con_ocranaleds\t\t\t\t\t\t= CVAR  (\"con_ocranaleds\", \"2\");\n\ncvar_t cl_cursor\t\t\t\t\t\t\t= CVAR  (\"cl_cursor\", \"\");\ncvar_t cl_cursorscale\t\t\t\t\t\t= CVAR  (\"cl_cursor_scale\", \"1.0\");\ncvar_t cl_cursorbiasx\t\t\t\t\t\t= CVAR  (\"cl_cursor_bias_x\", \"0.0\");\ncvar_t cl_cursorbiasy\t\t\t\t\t\t= CVAR  (\"cl_cursor_bias_y\", \"0.0\");\n\n#ifdef QWSKINS\ncvar_t gl_nocolors\t\t\t\t\t\t\t= CVARFD  (\"gl_nocolors\", \"0\", CVAR_ARCHIVE, \"Ignores player colours and skins, reducing texture memory usage at the cost of not knowing whether you're killing your team mates.\");\n#endif\ncvar_t gl_part_flame\t\t\t\t\t\t= CVARFD  (\"gl_part_flame\", \"1\", CVAR_ARCHIVE, \"Enable particle emitting from models. Mainly used for torch and flame effects.\");\n\n//opengl library, blank means try default.\nstatic cvar_t gl_driver\t\t\t\t\t\t= CVARFD (\"gl_driver\", \"\", CVAR_ARCHIVE | CVAR_VIDEOLATCH, \"Specifies the graphics driver name to load. This is typically a filename. Blank for default.\");\ncvar_t vid_devicename\t\t\t\t\t\t= CVARFD (\"vid_devicename\", \"\", CVAR_ARCHIVE | CVAR_VIDEOLATCH, \"Specifies which video device to try to use. If blank or invalid then one will be guessed.\");\ncvar_t gl_shadeq1_name\t\t\t\t\t\t= CVARD  (\"gl_shadeq1_name\", \"*\", \"Rename all surfaces from quake1 bsps using this pattern for the purposes of shader names.\");\nextern cvar_t r_vertexlight;\nextern cvar_t r_forceprogramify;\nextern cvar_t r_glsl_precache;\nextern cvar_t r_halfrate;\nextern cvar_t dpcompat_nopremulpics;\n#ifdef PSKMODELS\ncvar_t dpcompat_psa_ungroup\t\t\t\t\t= CVAR  (\"dpcompat_psa_ungroup\", \"0\");\n#endif\n\n#ifdef HAVE_LEGACY\ncvar_t r_ignoreentpvs\t\t\t\t\t\t= CVARD (\"r_ignoreentpvs\", \"1\", \"Disables pvs culling of entities that have been submitted to the renderer.\");\n#else\ncvar_t r_ignoreentpvs\t\t\t\t\t\t= CVARD (\"r_ignoreentpvs\", \"0\", \"Disables pvs culling of entities that have been submitted to the renderer.\");\n#endif\n\ncvar_t mod_md3flags\t\t\t\t\t\t\t= CVARD  (\"mod_md3flags\", \"1\", \"The flags field of md3s was never officially defined. If this is set to 1, the flags will be treated identically to mdl files. Otherwise they will be ignored. Naturally, this is required to provide rotating pickups in quake.\");\n\ncvar_t r_ambient\t\t\t\t\t\t\t= CVARF (\"r_ambient\", \"0\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tCVAR_CHEAT);\ncvar_t r_bloodstains\t\t\t\t\t\t= CVARF  (\"r_bloodstains\", \"1\", CVAR_ARCHIVE);\ncvar_t r_bouncysparks\t\t\t\t\t\t= CVARFD (\"r_bouncysparks\", \"1\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tCVAR_ARCHIVE,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\"Enables particle interaction with world surfaces, allowing for bouncy particles, stains, and decals.\");\ncvar_t r_drawentities\t\t\t\t\t\t= CVARFD  (\"r_drawentities\", \"1\", CVAR_CHEAT, \"Controls whether to draw entities or not.\\n0: Draw no entities.\\n1: Draw everything as normal.\\n2: Draw everything but bmodels.\\n3: Draw bmodels only.\");\ncvar_t r_max_gpu_bones\t\t\t\t\t\t= CVARD  (\"r_max_gpu_bones\", \"\", \"Specifies the maximum number of bones that can be handled on the GPU. If empty, will guess.\");\ncvar_t r_drawflat\t\t\t\t\t\t\t= CVARAF (\"r_drawflat\", \"0\", \"gl_textureless\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tCVAR_ARCHIVE | CVAR_SEMICHEAT | CVAR_RENDERERCALLBACK | CVAR_SHADERSYSTEM);\ncvar_t r_lightmap\t\t\t\t\t\t\t= CVARF (\"r_lightmap\", \"0\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tCVAR_ARCHIVE | CVAR_SEMICHEAT | CVAR_RENDERERCALLBACK | CVAR_SHADERSYSTEM);\ncvar_t r_wireframe\t\t\t\t\t\t\t= CVARAFD (\"r_showtris\", \"0\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\"r_wireframe\", CVAR_CHEAT, \"Developer feature where everything is drawn with wireframe over the top. Only active where cheats are permitted.\");\ncvar_t r_outline\t\t\t\t\t\t\t= CVARD (\"gl_outline\", \"0\", \"Draw some stylised outlines.\");\ncvar_t r_outline_width\t\t\t\t\t\t= CVARD (\"gl_outline_width\", \"2\", \"The width of those outlines.\");\ncvar_t r_wireframe_smooth\t\t\t\t\t= CVAR (\"r_wireframe_smooth\", \"0\");\ncvar_t r_refract_fbo\t\t\t\t\t\t= CVARD (\"r_refract_fbo\", \"1\", \"Use an fbo for refraction. If 0, just renders as a portal and uses a copy of the current framebuffer.\");\ncvar_t r_refractreflect_scale\t\t\t\t= CVARD (\"r_refractreflect_scale\", \"0.5\", \"Use a different scale for refraction and reflection texturemaps. Because $reasons.\");\ncvar_t r_drawviewmodel\t\t\t\t\t\t= CVARF  (\"r_drawviewmodel\", \"1\", CVAR_ARCHIVE);\ncvar_t r_drawviewmodelinvis\t\t\t\t\t= CVAR  (\"r_drawviewmodelinvis\", \"0\");\ncvar_t r_dynamic\t\t\t\t\t\t\t= CVARFD (\"r_dynamic\", IFMINIMAL(\"0\",\"1\"),\n\t\t\t\t\t\t\t\t\t\t\t\t\t  CVAR_ARCHIVE, \"0: no standard dlights at all.\\n1: coloured dlights will be used, they may show through walls. These are not realtime things.\\n2: The dlights will be forced to monochrome (this does not affect coronas/flashblends/rtlights attached to the same light).\");\nextern cvar_t r_temporalscenecache;\ncvar_t r_fastturb\t\t\t\t\t\t\t= CVARF (\"r_fastturb\", \"0\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tCVAR_SHADERSYSTEM);\ncvar_t r_fb_bmodels\t\t\t\t\t\t\t= CVARAFD(\"r_fb_bmodels\", \"1\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\"gl_fb_bmodels\", CVAR_SEMICHEAT|CVAR_RENDERERLATCH, \"Enables loading lumas on the map, as well as any external bsp models.\");\ncvar_t r_fb_models\t\t\t\t\t\t\t= CVARAFD  (\"r_fb_models\", \"1\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\"gl_fb_models\", CVAR_SEMICHEAT, \"Enables the use of lumas on models. Note that if ruleset_allow_fbmodels is enabled, then all models are unconditionally fullbright in deathmatch, because cheaters would set up their models like that anyway, hurrah for beating them at their own game. QuakeWorld players suck.\");\ncvar_t gl_overbright_models\t\t\t\t\t= CVARFD(\"gl_overbright_models\", \"0\", CVAR_SEMICHEAT|CVAR_ARCHIVE, \"Doubles the brightness of models, to match QuakeSpasm's misfeature of the same name.\");\n//cvar_t r_skin_overlays\t\t\t\t\t\t= CVARF  (\"r_skin_overlays\", \"1\",\n//\t\t\t\t\t\t\t\t\t\t\t\t\tCVAR_SEMICHEAT|CVAR_RENDERERLATCH);\ncvar_t r_globalskin_first\t\t\t\t\t= CVARFD  (\"r_globalskin_first\", \"100\", CVAR_RENDERERLATCH, \"Specifies the first .skin value that is a global skin. Entities within this range will use the shader/image called 'gfx/skinSKIN.lmp' instead of their regular skin. See also: r_globalskin_count.\");\ncvar_t r_globalskin_count\t\t\t\t\t= CVARFD  (\"r_globalskin_count\", \"10\", CVAR_RENDERERLATCH, \"Specifies how many globalskins there are.\");\ncvar_t r_coronas\t\t\t\t\t\t\t= CVARFD (\"r_coronas\", \"0\",\tCVAR_ARCHIVE, \"Draw coronas on realtime lights. Overrides glquake-esque flashblends.\");\ncvar_t r_coronas_intensity\t\t\t\t\t= CVARFD (\"r_coronas_intensity\", \"1\",\tCVAR_ARCHIVE, \"Alternative intensity multiplier for coronas.\");\ncvar_t r_coronas_occlusion\t\t\t\t\t= CVARFD (\"r_coronas_occlusion\", \"\", CVAR_ARCHIVE, \"Specifies that coronas should be occluded more carefully.\\n0: No occlusion, at all.\\n1: BSP occlusion only (simple tracelines).\\n2: non-bsp occlusion also (complex tracelines).\\n3: Depthbuffer reads (forces synchronisation).\\n4: occlusion queries.\");\ncvar_t r_coronas_mindist\t\t\t\t\t= CVARFD (\"r_coronas_mindist\", \"128\", CVAR_ARCHIVE, \"Coronas closer than this will be invisible, preventing near clip plane issues.\");\ncvar_t r_coronas_fadedist\t\t\t\t\t= CVARFD (\"r_coronas_fadedist\", \"256\", CVAR_ARCHIVE, \"Coronas will fade out over this distance.\");\n\ncvar_t r_flashblend\t\t\t\t\t\t\t= CVARF (\"gl_flashblend\", \"0\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tCVAR_ARCHIVE);\ncvar_t r_flashblendscale\t\t\t\t\t= CVARF (\"gl_flashblendscale\", \"0.35\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tCVAR_ARCHIVE);\ncvar_t r_floorcolour\t\t\t\t\t\t= CVARAF (\"r_floorcolour\", \"64 64 128\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\"r_floorcolor\", CVAR_RENDERERCALLBACK|CVAR_SHADERSYSTEM);\n//cvar_t r_floortexture\t\t\t\t\t\t= SCVARF (\"r_floortexture\", \"\",\n//\t\t\t\t\t\t\t\t\t\t\t\tCVAR_RENDERERCALLBACK|CVAR_SHADERSYSTEM);\ncvar_t r_fullbright\t\t\t\t\t\t\t= CVARFD (\"r_fullbright\", \"0\",\n\t\t\t\t\t\t\t\t\t\t\t\tCVAR_CHEAT|CVAR_SHADERSYSTEM, \"Ignore world lightmaps, drawing *everything* fully lit.\");\ncvar_t r_fullbrightSkins\t\t\t\t\t= CVARFD\t(\"r_fullbrightSkins\", \"0.8\", /*don't default to 1, as it looks a little ugly (too bright), but don't default to 0 either because then you're handicapped in the dark*/\n\t\t\t\t\t\t\t\t\t\t\t\tCVAR_SEMICHEAT|CVAR_SHADERSYSTEM, \"Force the use of fullbright skins on other players. No more hiding in the dark.\");\ncvar_t r_lightmap_saturation\t\t\t\t= CVAR\t(\"r_lightmap_saturation\", \"1\");\ncvar_t r_lightstylesmooth\t\t\t\t\t= CVARF\t(\"r_lightstylesmooth\", \"0\", CVAR_ARCHIVE);\ncvar_t r_lightstylesmooth_limit\t\t\t\t= CVAR\t(\"r_lightstylesmooth_limit\", \"2\");\ncvar_t r_lightstylespeed\t\t\t\t\t= CVAR\t(\"r_lightstylespeed\", \"10\");\ncvar_t r_lightstylescale\t\t\t\t\t= CVAR\t(\"r_lightstylescale\", \"1\");\ncvar_t r_lightmap_scale\t\t\t\t\t\t= CVARFD (\"r_shadow_realtime_nonworld_lightmaps\", \"1\", 0, \"Scaler for lightmaps used when not using realtime world lighting. Probably broken.\");\ncvar_t r_hdr_framebuffer\t\t\t\t\t= CVARFCD(\"r_hdr_framebuffer\", \"0\", CVAR_ARCHIVE, R_HDR_FramebufferFormat_Changed, \"If enabled, the map will be rendered into a high-precision image framebuffer. This avoids issues with shaders that contribute more than 1 in any single pass (like overbrights). Can also be set to the name of an image format, to force rendering to that format first - interesting formats are L8, RGB565, B10G11R11F, and others.\");\ncvar_t r_hdr_irisadaptation\t\t\t\t\t= CVARF\t(\"r_hdr_irisadaptation\", \"0\", CVAR_ARCHIVE);\ncvar_t r_hdr_irisadaptation_multiplier\t\t= CVAR\t(\"r_hdr_irisadaptation_multiplier\", \"2\");\ncvar_t r_hdr_irisadaptation_minvalue\t\t= CVAR\t(\"r_hdr_irisadaptation_minvalue\", \"0.5\");\ncvar_t r_hdr_irisadaptation_maxvalue\t\t= CVAR\t(\"r_hdr_irisadaptation_maxvalue\", \"4\");\ncvar_t r_hdr_irisadaptation_fade_down\t\t= CVAR\t(\"r_hdr_irisadaptation_fade_down\", \"0.5\");\ncvar_t r_hdr_irisadaptation_fade_up\t\t\t= CVAR\t(\"r_hdr_irisadaptation_fade_up\", \"0.1\");\n#ifdef RUNTIMELIGHTING\ncvar_t r_loadlits\t\t\t\t\t\t\t= CVARFD(\"r_loadlit\", \"1\", CVAR_ARCHIVE, \"Whether to load lit files.\\n0: Do not load external rgb lightmap data.\\n1: Load but don't generate.\\n2: Generate ldr lighting (if none found).\\n3: Generate hdr lighting (if none found).\\nNote that regeneration of lightmap data may be unreliable if the map was made for more advanced lighting tools.\\nDeluxemap information will also be generated, as appropriate.\");\n#else\ncvar_t r_loadlits\t\t\t\t\t\t\t= CVARFD(\"r_loadlit\", \"1\", CVAR_ARCHIVE, \"Whether to load lit files.\");\n#endif\ncvar_t r_menutint\t\t\t\t\t\t\t= CVARF\t(\"r_menutint\", \"0.68 0.4 0.13\",\n\t\t\t\t\t\t\t\t\t\t\t\tCVAR_RENDERERCALLBACK);\ncvar_t r_netgraph\t\t\t\t\t\t\t= CVARD\t(\"r_netgraph\", \"0\", \"Displays a graph of packet latency. A value of 2 will give additional info about what sort of data is being received from the server.\");\nextern cvar_t r_lerpmuzzlehack;\nextern cvar_t mod_h2holey_bugged, mod_halftexel, mod_nomipmap;\ncvar_t r_nolerp\t\t\t\t\t\t\t\t= CVARF\t(\"r_nolerp\", \"0\", CVAR_ARCHIVE);\ncvar_t r_noframegrouplerp\t\t\t\t\t= CVARF\t(\"r_noframegrouplerp\", \"0\", CVAR_ARCHIVE);\ncvar_t r_nolightdir\t\t\t\t\t\t\t= CVARF\t(\"r_nolightdir\", \"0\", CVAR_ARCHIVE);\ncvar_t r_novis\t\t\t\t\t\t\t\t= CVARF\t(\"r_novis\", \"0\", CVAR_ARCHIVE);\ncvar_t r_part_rain\t\t\t\t\t\t\t= CVARFD (\"r_part_rain\", \"0\",\n\t\t\t\t\t\t\t\t\t\t\t\tCVAR_ARCHIVE,\n\t\t\t\t\t\t\t\t\t\t\t\t\"Enable particle effects to emit off of surfaces. Mainly used for weather or lava/slime effects.\");\ncvar_t r_softwarebanding_cvar\t\t\t\t= CVARFD (\"r_softwarebanding\", \"0\", CVAR_SHADERSYSTEM|CVAR_RENDERERLATCH|CVAR_ARCHIVE, \"Utilise the Quake colormap in order to emulate 8bit software rendering. This results in banding as well as other artifacts that some believe adds character. Also forces nearest sampling on affected surfaces (palette indicies do not interpolate well).\");\nqboolean r_softwarebanding;\ncvar_t r_speeds\t\t\t\t\t\t\t\t= CVAR (\"r_speeds\", \"0\");\ncvar_t r_stainfadeammount\t\t\t\t\t= CVAR  (\"r_stainfadeammount\", \"1\");\ncvar_t r_stainfadetime\t\t\t\t\t\t= CVAR  (\"r_stainfadetime\", \"1\");\ncvar_t r_stains\t\t\t\t\t\t\t\t= CVARFC(\"r_stains\", IFMINIMAL(\"0\",\"0\"),\n\t\t\t\t\t\t\t\t\t\t\t\tCVAR_ARCHIVE,\n\t\t\t\t\t\t\t\t\t\t\t\tCvar_Limiter_ZeroToOne_Callback);\ncvar_t r_renderscale\t\t\t\t\t\t= CVARFD(\"r_renderscale\", \"1\", CVAR_ARCHIVE, \"Provides a way to enable subsampling or super-sampling\");\ncvar_t r_fxaa\t\t\t\t\t\t\t\t= CVARFD(\"r_fxaa\", \"0\", CVAR_ARCHIVE, \"Runs a post-procesing pass to strip the jaggies.\");\ncvar_t r_graphics\t\t\t\t\t\t\t= CVARFD(\"r_graphics\", \"1\", CVAR_ARCHIVE, \"Turning this off will result in ascii-style rendering.\");\ncvar_t r_postprocshader\t\t\t\t\t\t= CVARD(\"r_postprocshader\", \"\", \"Specifies a custom shader to use as a post-processing shader\");\ncvar_t r_wallcolour\t\t\t\t\t\t\t= CVARAF (\"r_wallcolour\", \"128 128 128\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t  \"r_wallcolor\", CVAR_RENDERERCALLBACK|CVAR_SHADERSYSTEM);//FIXME: broken\n//cvar_t r_walltexture\t\t\t\t\t\t= CVARF (\"r_walltexture\", \"\",\n//\t\t\t\t\t\t\t\t\t\t\t\tCVAR_RENDERERCALLBACK|CVAR_SHADERSYSTEM);\t//FIXME: broken\ncvar_t r_wateralpha\t\t\t\t\t\t\t= CVARF  (\"r_wateralpha\", \"1\",\n\t\t\t\t\t\t\t\t\t\t\t\tCVAR_ARCHIVE | CVAR_SHADERSYSTEM);\ncvar_t r_lavaalpha\t\t\t\t\t\t\t= CVARF  (\"r_lavaalpha\", \"\",\n\t\t\t\t\t\t\t\t\t\t\t\tCVAR_ARCHIVE | CVAR_SHADERSYSTEM);\ncvar_t r_slimealpha\t\t\t\t\t\t\t= CVARF  (\"r_slimealpha\", \"\",\n\t\t\t\t\t\t\t\t\t\t\t\tCVAR_ARCHIVE | CVAR_SHADERSYSTEM);\ncvar_t r_telealpha\t\t\t\t\t\t\t= CVARF  (\"r_telealpha\", \"\",\n\t\t\t\t\t\t\t\t\t\t\t\tCVAR_ARCHIVE | CVAR_SHADERSYSTEM);\ncvar_t r_waterwarp\t\t\t\t\t\t\t= CVARFD (\"r_waterwarp\", \"1\",\n\t\t\t\t\t\t\t\t\t\t\t\tCVAR_ARCHIVE, \"Enables fullscreen warp, preferably via glsl. -1 specifies to force the fov warp fallback instead which can give a smidge more performance.\");\n\ncvar_t r_replacemodels\t\t\t\t\t\t= CVARFD (\"r_replacemodels\", IFMINIMAL(\"\",\"md3 md2 md5mesh\"),\n\t\t\t\t\t\t\t\t\t\t\t\tCVAR_ARCHIVE, \"A list of filename extensions to attempt to use instead of mdl.\");\n\ncvar_t r_lightmap_nearest\t\t\t\t\t= CVARFD (\"gl_lightmap_nearest\", \"0\", CVAR_ARCHIVE, \"Use nearest sampling for lightmaps. This will give a more blocky look. Meaningless when gl_lightmap_average is enabled.\");\ncvar_t r_lightmap_average\t\t\t\t\t= CVARFD (\"gl_lightmap_average\", \"0\", CVAR_ARCHIVE, \"Determine lightmap values based upon the center of the polygon. This will give a more buggy look, quite probably.\");\ncvar_t r_lightmap_format\t\t\t\t\t= CVARFCD (\"r_lightmap_format\", \"\", CVAR_ARCHIVE, R_Lightmap_Format_Changed, \"Overrides the default texture format used for lightmaps. rgb9e5 is a good choice for HDR.\");\n\n//otherwise it would defeat the point.\ncvar_t scr_allowsnap\t\t\t\t\t\t= CVARFD (\"scr_allowsnap\", \"0\",\n\t\t\t\t\t\t\t\t\t\t\t\tCVAR_NOTFROMSERVER, \"Whether to allow server-requested screenshot requests to check for gpu driver hacks.\");\ncvar_t scr_centersbar\t\t\t\t\t\t= CVAR  (\"scr_centersbar\", \"2\");\ncvar_t scr_centertime\t\t\t\t\t\t= CVARD  (\"scr_centertime\", \"2\", \"Centerprint messages will be displayed for this long before disappearing.\");\ncvar_t scr_logcenterprint\t\t\t\t\t= CVARD  (\"con_logcenterprint\", \"1\", \"Specifies whether to print centerprints on the console.\\n0: never\\n1: single-player or coop only.\\n2: always.\");\ncvar_t scr_conalpha\t\t\t\t\t\t\t= CVARCD (\"scr_conalpha\", \"0.7\",\n\t\t\t\t\t\t\t\t\t\t\t\tCvar_Limiter_ZeroToOne_Callback, \"How opaque the console should be when there's still stuff going on behind.\");\ncvar_t scr_consize\t\t\t\t\t\t\t= CVARD  (\"scr_consize\", \"0.5\", \"Proportion of the screen to be covered by the console when its focused. Valid range is 0ish to 1.\");\ncvar_t scr_conspeed\t\t\t\t\t\t\t= CVARD  (\"scr_conspeed\", \"2000\", \"How fast the console careers onto the screen.\");\ncvar_t scr_fov_mode\t\t\t\t\t\t\t= CVARFD  (\"scr_fov_mode\", \"4\", CVAR_ARCHIVE, \"Controls what the fov cvar actually controls:\\n0: largest axis (ultra-wide monitors means less height will be visible).\\n1: smallest axis (ultra-wide monitors will distort at the edges).\\n2: horizontal axis.\\n3: vertical axis.\\n4: 4:3 horizontal axis, padded for wider resolutions (for a more classic fov)\");\ncvar_t scr_fov\t\t\t\t\t\t\t\t= CVARFCD(\"fov\", \"90\", CVAR_ARCHIVE, SCR_Fov_Callback,\n\t\t\t\t\t\t\t\t\t\t\t\t\"field of vision, 1-170 degrees, standard fov is 90, nquake defaults to 108.\");\ncvar_t scr_fov_viewmodel\t\t\t\t\t= CVARFD(\"r_viewmodel_fov\", \"\", CVAR_ARCHIVE,\n\t\t\t\t\t\t\t\t\t\t\t\t\"field of vision, 1-170 degrees, standard fov is 90, nquake defaults to 108.\");\ncvar_t scr_printspeed\t\t\t\t\t\t= CVARD  (\"scr_printspeed\", \"16\", \"How rapidly each character is displayed during outro messages.\");\ncvar_t scr_showpause\t\t\t\t\t\t= CVAR  (\"showpause\", \"1\");\ncvar_t scr_showturtle\t\t\t\t\t\t= CVARD  (\"showturtle\", \"0\", \"Enables a low-framerate indicator.\");\ncvar_t scr_turtlefps\t\t\t\t\t\t= CVAR  (\"scr_turtlefps\", \"10\");\ncvar_t scr_sshot_compression\t\t\t\t= CVARD  (\"scr_sshot_compression\", \"75\", \"Requsted compression ratio as a percentage. For jpeg this is the quantisation quality and has a direct impact on image quality vs size. For png this ranges between 0 for best and 100 for worst with final image quality being unchanged because png is loseless.\");\ncvar_t scr_sshot_type\t\t\t\t\t\t= CVARD  (\"scr_sshot_type\", \"png\", \"This specifies the default extension(and thus file format) for screenshots.\\nKnown extensions are: png, jpg/jpeg, bmp, pcx, tga, ktx, dds.\");\ncvar_t scr_sshot_prefix\t\t\t\t\t\t= CVARF  (\"scr_sshot_prefix\", \"screenshots/fte-\", CVAR_NOTFROMSERVER);\ncvar_t scr_viewsize\t\t\t\t\t\t\t= CVARFC(\"viewsize\", \"100\", CVAR_ARCHIVE, SCR_Viewsize_Callback);\n\n#ifdef ANDROID\ncvar_t vid_conautoscale\t\t\t\t\t\t= CVARAF (\"vid_conautoscale\", \"2\",\n\t\t\t\t\t\t\t\t\t\t\t\t\"scr_conscale\"/*qs*/ /*\"vid_conscale\"ez*/, CVAR_ARCHIVE | CVAR_RENDERERCALLBACK);\n#else\ncvar_t vid_conautoscale\t\t\t\t\t\t= CVARAFD (\"vid_conautoscale\", \"0\",\n\t\t\t\t\t\t\t\t\t\t\t\t\"scr_conscale\"/*qs*/ /*\"vid_conscale\"ez*/, CVAR_ARCHIVE | CVAR_RENDERERCALLBACK, \"Changes the 2d scale, including hud, console, and fonts. To specify an explicit font size, divide the desired 'point' size by 8 to get the scale. High values will be clamped to maintain at least a 320*200 virtual size.\");\n#endif\ncvar_t vid_baseheight\t\t\t\t\t\t= CVARD (\"vid_baseheight\", \"\", \"Specifies a mod's target height and used only when the 2d scale is not otherwise forced. Unlike vid_conheight the size is not fixed and will be padded to avoid inconsistent filtering.\");\ncvar_t vid_minsize\t\t\t\t\t\t\t= CVARFD (\"vid_minsize\", \"320 200\",\n\t\t\t\t\t\t\t\t\t\t\t\t0, \"Specifies a mod's minimum virtual size (to be set inside the mod's default.cfg file). The virtual size will always be at least this big. Windows may not be resized below this value either (which may reduce video mode options), but might require a vid_restart to take effect when switching between mods.\");\ncvar_t vid_conheight\t\t\t\t\t\t= CVARFD (\"vid_conheight\", \"0\",\n\t\t\t\t\t\t\t\t\t\t\t\tCVAR_ARCHIVE, \"Specifies the virtual height of the screen. If 0, will be inferred by vid_conwidth or other settings, according to aspect etc.\");\ncvar_t vid_conwidth\t\t\t\t\t\t\t= CVARFD (\"vid_conwidth\", \"0\",\n\t\t\t\t\t\t\t\t\t\t\t\tCVAR_ARCHIVE | CVAR_RENDERERCALLBACK, \"Specifies the virtual width of the screen. Should generally be left as 0 to allow the correct aspect to be used despite video mode changes.\");\n//see R_RestartRenderer_f for the effective default 'if (newr.renderer == -1)'.\ncvar_t vid_renderer\t\t\t\t\t\t\t= CVARFD (\"vid_renderer\", \"\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t CVAR_ARCHIVE | CVAR_VIDEOLATCH, \"Specifies which backend is used. Values that might work are: sv (dedicated server), headless (null renderer), vk (vulkan), gl (opengl), egl (opengl es), d3d9 (direct3d 9), d3d11 (direct3d 11, with default hardware rendering), d3d11 warp (direct3d 11, with software rendering).\");\ncvar_t vid_renderer_opts\t\t\t\t\t= CVARFD (\"_vid_renderer_opts\", NULL, CVAR_NOSET|CVAR_NOSAVE, \"The possible video renderer apis, in \\\"value\\\" \\\"description\\\" pairs, for gamecode to read.\");\n\ncvar_t vid_bpp\t\t\t\t\t\t\t\t= CVARFD (\"vid_bpp\", \"0\",\n\t\t\t\t\t\t\t\t\t\t\t\tCVAR_ARCHIVE | CVAR_VIDEOLATCH, \"The number of colour bits to request from the renedering context\");\ncvar_t vid_depthbits\t\t\t\t\t\t= CVARFD (\"vid_depthbits\", \"0\",\n\t\t\t\t\t\t\t\t\t\t\t\tCVAR_ARCHIVE | CVAR_VIDEOLATCH, \"The number of depth bits to request from the renedering context. Try 24.\");\ncvar_t vid_desktopsettings\t\t\t\t\t= CVARFD (\"vid_desktopsettings\", \"0\",\n\t\t\t\t\t\t\t\t\t\t\t\tCVAR_ARCHIVE | CVAR_VIDEOLATCH, \"Ignore the values of vid_width and vid_height, and just use the same settings that are used for the desktop.\");\n#ifdef FTE_TARGET_WEB\ncvar_t vid_fullscreen\t\t\t\t\t\t= CVARF (\"vid_fullscreen\", \"0\",\tCVAR_ARCHIVE|CVAR_VIDEOLATCH);\n#else\ncvar_t vid_fullscreen\t\t\t\t\t\t= CVARFD (\"vid_fullscreen\", \"2\",\tCVAR_ARCHIVE|CVAR_VIDEOLATCH, \"Specifies whether the game should be fullscreen or not (requires vid_restart).\\n0: Run in a resizable window, which can be manually maximized (with borders).\\n1: Traditional fullscreen-exclusive video mode with mode switching and everything.\\n2: Simply maximize the window and hide any borders without interfering with any other parts of the system.\");\n#endif\ncvar_t vid_height\t\t\t\t\t\t\t= CVARFD (\"vid_height\", \"0\",\n\t\t\t\t\t\t\t\t\t\t\t\tCVAR_ARCHIVE | CVAR_VIDEOLATCH, \"The screen height to attempt to use, in physical pixels. 0 means use desktop resolution.\");\ncvar_t vid_multisample\t\t\t\t\t\t= CVARAFD (\"vid_multisample\", \"0\", \"vid_samples\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tCVAR_ARCHIVE, \"The number of samples to use for Multisample AntiAliasing (aka: msaa). A value of 1 explicitly disables it.\");\ncvar_t vid_refreshrate\t\t\t\t\t\t= CVARF (\"vid_displayfrequency\", \"0\",\n\t\t\t\t\t\t\t\t\t\t\t\tCVAR_ARCHIVE | CVAR_VIDEOLATCH);\ncvar_t vid_srgb\t\t\t\t\t\t\t\t= CVARFD (\"vid_srgb\", \"0\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t  CVAR_ARCHIVE, \"-1: Only the framebuffer should use sRGB colourspace, textures and colours will be assumed to be linear. This has the effect of just brightening the screen according to a gamma ramp of about 2.2 (or .45 in quake's backwards gamma terms).\\n0: Off. Colour blending will be wrong, you're likely to see obvious banding.\\n1: Use sRGB extensions/support to ensure that the lighting aproximately matches real-world lighting (required for PBR).\\n2: Attempt to use a linear floating-point framebuffer, which should enable monitor support for HDR.\\nNote that driver behaviour varies by a disturbing amount, and much of the documentation conflicts with itself (the term 'linear' is awkward when the eye's perception of linear is non-linear).\");\ncvar_t vid_wndalpha\t\t\t\t\t\t\t= CVARD (\"vid_wndalpha\", \"1\", \"When running windowed, specifies the window's transparency level.\");\n#if defined(_WIN32) && defined(MULTITHREAD)\ncvar_t vid_winthread\t\t\t\t\t\t= CVARFD (\"vid_winthread\", \"\", CVAR_ARCHIVE|CVAR_VIDEOLATCH, \"When enabled, window messages will be handled by a separate thread. This allows the game to continue rendering when Microsoft Windows blocks while resizing, etc.\");\n#endif\n//more readable defaults to match conwidth/conheight.\ncvar_t vid_width\t\t\t\t\t\t\t= CVARFD (\"vid_width\", \"0\",\n\t\t\t\t\t\t\t\t\t\t\t\tCVAR_ARCHIVE | CVAR_VIDEOLATCH, \"The screen width to attempt to use, in physical pixels. 0 means use desktop resolution.\");\ncvar_t vid_dpi_x\t\t\t\t\t\t\t= CVARFD (\"vid_dpi_x\", \"0\", CVAR_NOSET, \"For mods that need to determine the physical screen size (like with touchscreens). 0 means unknown\");\ncvar_t vid_dpi_y\t\t\t\t\t\t\t= CVARFD (\"vid_dpi_y\", \"0\", CVAR_NOSET, \"For mods that need to determine the physical screen size (like with touchscreens). 0 means unknown\");\n\ncvar_t\tr_stereo_separation\t\t\t\t\t= CVARD(\"r_stereo_separation\", \"4\", \"How far apart your eyes are, in quake units. A non-zero value will enable stereoscoping rendering. You might need some of them retro 3d glasses. Hardware support is recommended, see r_stereo_context.\");\ncvar_t\tr_stereo_convergence\t\t\t\t= CVARD(\"r_stereo_convergence\", \"0\", \"Nudges the angle of each eye inwards when using stereoscopic rendering.\");\ncvar_t\tr_stereo_method\t\t\t\t\t\t= CVARFD(\"r_stereo_method\", \"0\", CVAR_ARCHIVE, \"Value 0 = Off.\\nValue 1 = Attempt hardware acceleration. Requires vid_restart.\\nValue 2 = red/cyan.\\nValue 3 = red/blue.\\nValue 4=red/green.\\nValue 5=eye strain.\");\n\ncvar_t\tr_xflip = CVAR(\"leftisright\", \"0\");\n\nextern cvar_t r_dodgytgafiles;\nextern cvar_t r_dodgypcxfiles;\nextern cvar_t r_keepimages;\nextern cvar_t r_ignoremapprefixes;\nextern cvar_t r_dodgymiptex;\nextern char *r_defaultimageextensions;\nextern cvar_t r_imageextensions;\nextern cvar_t r_image_downloadsizelimit;\nextern cvar_t r_drawentities;\nextern cvar_t r_drawviewmodel;\nextern cvar_t r_drawworld;\nextern cvar_t r_fullbright;\ncvar_t\tr_mirroralpha = CVARFD(\"r_mirroralpha\",\"1\", CVAR_CHEAT|CVAR_SHADERSYSTEM|CVAR_RENDERERLATCH, \"Specifies how the default shader is generated for the 'window02_1' texture. Values less than 1 will turn it into a mirror.\");\nextern cvar_t r_netgraph;\ncvar_t\tr_norefresh = CVAR(\"r_norefresh\",\"0\");\nextern cvar_t r_novis;\nextern cvar_t r_speeds;\nextern cvar_t r_waterwarp;\n\n#ifdef BEF_PUSHDEPTH\ncvar_t\tr_polygonoffset_submodel_factor = CVARD(\"r_polygonoffset_submodel_factor\", \"0\", \"z-fighting avoidance. Pushes submodel depth values slightly towards the camera depending on the slope of the surface.\");\ncvar_t\tr_polygonoffset_submodel_offset = CVARD(\"r_polygonoffset_submodel_offset\", \"0\", \"z-fighting avoidance. Pushes submodel depth values slightly towards the camera by a consistent distance.\");\ncvar_t\tr_polygonoffset_submodel_maps = CVARD(\"r_polygonoffset_submodel_map\", \"e?m? r?m? hip?m?\", \"List of maps on which z-fighting reduction should be used. wildcards accepted.\");\n#endif\ncvar_t\tr_polygonoffset_shadowmap_offset = CVAR(\"r_polygonoffset_shadowmap_factor\", \"0.05\");\ncvar_t\tr_polygonoffset_shadowmap_factor = CVAR(\"r_polygonoffset_shadowmap_offset\", \"0\");\n\ncvar_t\tr_polygonoffset_stencil_factor = CVAR(\"r_polygonoffset_stencil_factor\", \"0.01\");\ncvar_t\tr_polygonoffset_stencil_offset = CVAR(\"r_polygonoffset_stencil_offset\", \"1\");\n\nrendererstate_t currentrendererstate;\n\n#if defined(GLQUAKE)\ncvar_t\tgl_workaround_ati_shadersource\t\t= CVARD\t (\"gl_workaround_ati_shadersource\", \"1\", \"Work around ATI driver bugs in the glShaderSource function. Can safely be enabled with other drivers too.\");\ncvar_t\tvid_gl_context_version\t\t\t\t= CVARD  (\"vid_gl_context_version\", \"\", \"Specifies the version of OpenGL to try to create.\");\ncvar_t\tvid_gl_context_forwardcompatible\t= CVARD  (\"vid_gl_context_forwardcompatible\", \"0\", \"Requests an opengl context with no depricated features enabled.\");\ncvar_t\tvid_gl_context_compatibility\t\t= CVARD  (\"vid_gl_context_compatibility\", \"1\", \"Requests an OpenGL context with fixed-function backwards compat.\");\ncvar_t\tvid_gl_context_debug\t\t\t\t= CVARD  (\"vid_gl_context_debug\", \"0\", \"Requests a debug opengl context. This provides better error oreporting.\");\t//for my ati drivers, debug 1 only works if version >= 3\ncvar_t\tvid_gl_context_es\t\t\t\t\t= CVARD  (\"vid_gl_context_es\", \"0\", \"Requests an OpenGLES context. Be sure to set vid_gl_context_version to 2 or so.\"); //requires version set correctly, no debug, no compat\ncvar_t\tvid_gl_context_robustness\t\t\t= CVARD\t(\"vid_gl_context_robustness\", \"1\", \"Attempt to enforce extra buffer protection in the gl driver, but can be slower with pre-gl3 hardware.\");\ncvar_t\tvid_gl_context_selfreset\t\t\t= CVARD\t(\"vid_gl_context_selfreset\", \"1\", \"Upon hardware failure, have the engine create a new context instead of depending on the drivers to restore everything. This can help to avoid graphics drivers randomly killing your game, and can help reduce memory requirements.\");\ncvar_t\tvid_gl_context_noerror\t\t\t\t= CVARD\t(\"vid_gl_context_noerror\", \"\", \"Disables OpenGL's error checks for a small performance speedup. May cause segfaults if stuff wasn't properly implemented/tested.\");\n\ncvar_t\tgl_immutable_textures\t\t\t\t= CVARFD (\"gl_immutable_textures\", \"1\", CVAR_VIDEOLATCH, \"Controls whether to use immutable GPU memory allocations for OpenGL textures. This potentially means less work for the drivers and thus higher framerates.\");\ncvar_t\tgl_immutable_buffers\t\t\t\t= CVARFD (\"gl_immutable_buffers\", \"1\", CVAR_VIDEOLATCH, \"Controls whether to use immutable GPU memory allocations for static OpenGL vertex buffers. This potentially means less work for the drivers and thus higher framerates.\");\ncvar_t\tgl_pbolightmaps\t\t\t\t\t\t= CVARFD (\"gl_pbolightmaps\", \"1\", CVAR_RENDERERLATCH, \"Controls whether to use PBOs for streaming lightmap updates. This prevents CPU stalls while the driver reads out the lightmap data (lightmap updates are still not free though).\");\n#endif\n\n#if 1\ncvar_t r_tessellation\t\t\t\t\t\t= CVARAFD  (\"r_tessellation\", \"0\", \"gl_ati_truform\", CVAR_SHADERSYSTEM, \"Enables+controls the use of blinn tessellation on the fallback shader for meshes, equivelent to a shader with 'program defaultskin#TESS'. This will look stupid unless the meshes were actually designed for it and have suitable vertex normals.\");\ncvar_t gl_ati_truform_type\t\t\t\t\t= CVAR  (\"gl_ati_truform_type\", \"1\");\ncvar_t r_tessellation_level\t\t\t\t\t= CVAR  (\"r_tessellation_level\", \"5\");\ncvar_t gl_blend2d\t\t\t\t\t\t\t= CVAR  (\"gl_blend2d\", \"1\");\ncvar_t gl_blendsprites\t\t\t\t\t\t= CVARFD  (\"gl_blendsprites\", \"0\", CVAR_SHADERSYSTEM, \"Specifies how sprites are blended.\\n0: Alpha tested.\\n1: Premultiplied blend.\\n2: Additive blend.\");\ncvar_t r_deluxemapping_cvar\t\t\t\t\t= CVARAFD (\"r_deluxemapping\", \"1\", \"r_glsl_deluxemapping\",\n\t\t\t\t\t\t\t\t\t\t\t\tCVAR_ARCHIVE|CVAR_RENDERERLATCH, \"Enables bumpmapping based upon precomputed light directions.\\n0=off\\n1=use if available\\n2=auto-generate (if possible)\");\ncvar_t mod_loadsurfenvmaps\t\t\t\t\t= CVARD (\"r_loadsurfenvmaps\", \"1\", \"Load local reflection environment-maps, where available. These are normally defined via env_cubemap entities dotted around the place.\");\nqboolean r_deluxemapping;\ncvar_t r_shaderblobs\t\t\t\t\t\t= CVARD (\"r_shaderblobs\", \"0\", \"If enabled, can massively accelerate vid restarts / loading (especially with the d3d renderer). Can cause issues when upgrading engine versions, so this is disabled by default.\");\ncvar_t gl_compress\t\t\t\t\t\t\t= CVARAFD (\"gl_compress\", \"0\", \"r_ext_compressed_textures\"/*q3*/, CVAR_ARCHIVE, \"Enable automatic texture compression even for textures which are not pre-compressed.\");\ncvar_t gl_conback\t\t\t\t\t\t\t= CVARFCD (\"gl_conback\", \"\",\n\t\t\t\t\t\t\t\t\t\t\t\tCVAR_RENDERERCALLBACK, R2D_Conback_Callback, \"Specifies which conback shader/image to use. The Quake fallback is gfx/conback.lmp\");\n//cvar_t gl_detail\t\t\t\t\t\t\t= CVARF (\"gl_detail\", \"0\",\n//\t\t\t\t\t\t\t\t\t\t\t\tCVAR_ARCHIVE);\n//cvar_t gl_detailscale\t\t\t\t\t\t= CVAR  (\"gl_detailscale\", \"5\");\ncvar_t gl_font\t\t\t\t\t\t\t\t= CVARFD (\"gl_font\", \"\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t  CVAR_RENDERERCALLBACK|CVAR_ARCHIVE, \"Specifies the font file to use. a value such as FONT:ALTFONT specifies an alternative font to be used when ^^a is used.\\n\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t  \"When using TTF fonts, you will likely need to scale text to at least 150% - vid_conautoscale 1.5 will do this.\\n\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t  \"TTF fonts may be loaded from your windows directory. \\'gl_font cour?col=1,1,1:couri?col=0,1,0\\' loads eg: c:\\\\windows\\\\fonts\\\\cour.ttf, and uses the italic version of courier for alternative text, with specific colour tints.\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t  );\ncvar_t con_textfont\t\t\t\t\t\t\t= CVARAFD (\"con_textfont\", \"\", \"gl_consolefont\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t  CVAR_RENDERERCALLBACK|CVAR_ARCHIVE, \"Specifies the font file to use. a value such as FONT:ALTFONT specifies an alternative font to be used when ^^a is used.\\n\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t  \"When using TTF fonts, you will likely need to scale text to at least 150% - vid_conautoscale 1.5 will do this.\\n\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t  \"TTF fonts may be loaded from your windows directory. \\'gl_font cour?col=1,1,1:couri?col=0,1,0\\' loads eg: c:\\\\windows\\\\fonts\\\\cour.ttf, and uses the italic version of courier for alternative text, with specific colour tints.\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t  );\ncvar_t gl_lateswap\t\t\t\t\t\t\t= CVAR  (\"gl_lateswap\", \"0\");\ncvar_t gl_lerpimages\t\t\t\t\t\t= CVARFD  (\"gl_lerpimages\", \"1\", CVAR_ARCHIVE, \"Enables smoother resampling for images which are not power-of-two, when the drivers do not support non-power-of-two textures.\");\n//cvar_t gl_lightmapmode\t\t\t\t\t\t= SCVARF(\"gl_lightmapmode\", \"\",\n//\t\t\t\t\t\t\t\t\t\t\t\tCVAR_ARCHIVE);\ncvar_t gl_load24bit\t\t\t\t\t\t\t= CVARF (\"gl_load24bit\", \"1\",\n\t\t\t\t\t\t\t\t\t\t\t\tCVAR_ARCHIVE|CVAR_RENDERERLATCH);\n\ncvar_t\tr_clear\t\t\t\t\t\t\t\t= CVARAF(\"r_clear\",\"0\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t \"gl_clear\", 0);\ncvar_t\tr_clearcolour\t\t\t\t\t\t= CVARAFC(\"r_clearcolour\", \"0.12 0.12 0.12\", \"r_clearcolor\"/*american spelling*/, 0, R_ClearColour_Changed);\ncvar_t gl_max_size\t\t\t\t\t\t\t= CVARFD  (\"gl_max_size\", \"8192\", CVAR_RENDERERLATCH, \"Specifies the maximum texture size that the engine may use. Textures larger than this will be downsized. Clamped by the value the driver supports.\");\ncvar_t gl_menutint_shader\t\t\t\t\t= CVARD  (\"gl_menutint_shader\", \"1\", \"Controls the use of GLSL to desaturate the background when drawing the menu, like quake's dos software renderer used to do before the ugly dithering of winquake.\");\n\n//by setting to 64 or something, you can use this as a wallhack\ncvar_t gl_mindist\t\t\t\t\t\t\t= CVARAD (\"gl_mindist\", \"1\", \"r_nearclip\",\n\t\t\t\t\t\t\t\t\t\t\t\t\"Distance to the near clip plane. Smaller values may damage depth precision, high values can potentialy be used to see through walls...\");\n\ncvar_t gl_motionblur\t\t\t\t\t\t= CVARF (\"gl_motionblur\", \"0\",\n\t\t\t\t\t\t\t\t\t\t\t\tCVAR_ARCHIVE);\ncvar_t gl_motionblurscale\t\t\t\t\t= CVAR  (\"gl_motionblurscale\", \"1\");\ncvar_t gl_overbright\t\t\t\t\t\t= CVARFC (\"gl_overbright\", \"1\",\n\t\t\t\t\t\t\t\t\t\t\t\tCVAR_ARCHIVE,\n\t\t\t\t\t\t\t\t\t\t\t\tSurf_RebuildLightmap_Callback);\ncvar_t gl_overbright_all\t\t\t\t\t= CVARF (\"gl_overbright_all\", \"0\",\n\t\t\t\t\t\t\t\t\t\t\t\tCVAR_ARCHIVE);\ncvar_t gl_picmip\t\t\t\t\t\t\t= CVARFD  (\"gl_picmip\", \"0\", CVAR_ARCHIVE, \"Reduce world/model texture sizes by some exponential factor.\");\ncvar_t gl_picmip2d\t\t\t\t\t\t\t= CVARFD  (\"gl_picmip2d\", \"0\", CVAR_ARCHIVE, \"Reduce hud/menu texture sizes by some exponential factor.\");\ncvar_t gl_picmip_world\t\t\t\t\t\t= CVARFD  (\"gl_picmip_world\", \"0\", CVAR_ARCHIVE, \"Effectively added to gl_picmip for the purposes of world textures.\");\ncvar_t gl_picmip_sprites\t\t\t\t\t= CVARFD  (\"gl_picmip_sprites\", \"0\", CVAR_ARCHIVE, \"Effectively added to gl_picmip for the purposes of sprite textures.\");\ncvar_t gl_picmip_other\t\t\t\t\t\t= CVARFD  (\"gl_picmip_other\", \"0\", CVAR_ARCHIVE, \"Effectively added to gl_picmip for the purposes of model textures.\");\ncvar_t gl_nohwblend\t\t\t\t\t\t\t= CVARD  (\"gl_nohwblend\",\"1\", \"If 1, don't use hardware gamma ramps for transient effects that change each frame (does not affect long-term effects like holding quad or underwater tints).\");\n//cvar_t gl_schematics\t\t\t\t\t\t= CVARD  (\"gl_schematics\", \"0\", \"Gimmick rendering mode that draws the length of various world edges.\");\ncvar_t gl_smoothcrosshair\t\t\t\t\t= CVAR  (\"gl_smoothcrosshair\", \"1\");\ncvar_t\tgl_maxdist\t\t\t\t\t\t\t= CVARAD\t(\"gl_maxdist\", \"0\", \"gl_farclip\", \"The distance of the far clip plane. If set to 0, some fancy maths will be used to place it at an infinite distance.\");\n\n#ifdef SPECULAR\ncvar_t gl_specular\t\t\t\t\t\t\t= CVARFD  (\"gl_specular\", \"0.3\", CVAR_ARCHIVE|CVAR_SHADERSYSTEM, \"Multiplier for specular effects.\");\ncvar_t gl_specular_power\t\t\t\t\t= CVARF  (\"gl_specular_power\", \"32\", CVAR_ARCHIVE|CVAR_SHADERSYSTEM);\ncvar_t gl_specular_fallback\t\t\t\t\t= CVARF  (\"gl_specular_fallback\", \"0.05\", CVAR_ARCHIVE|CVAR_RENDERERLATCH);\ncvar_t gl_specular_fallbackexp\t\t\t\t= CVARF  (\"gl_specular_fallbackexp\", \"1\", CVAR_ARCHIVE|CVAR_RENDERERLATCH);\n#endif\n\n// The callbacks are not in D3D yet (also ugly way of seperating this)\ncvar_t gl_texture_anisotropic_filtering\t\t= CVARAFCD(\"gl_texture_anisotropy\", \"4\",\n\t\t\t\t\t\t\t\t\t\t\t\t\"gl_texture_anisotropic_filtering\"/*old*/, CVAR_ARCHIVE | CVAR_RENDERERCALLBACK,\n\t\t\t\t\t\t\t\t\t\t\t\tImage_TextureMode_Callback, \"Allows for higher quality textures on surfaces that slope away from the camera (like the floor). Set to 16 or something. Only supported with trilinear filtering.\");\ncvar_t gl_texturemode\t\t\t\t\t\t= CVARAFCD(\"gl_texturemode\", \"GL_LINEAR_MIPMAP_LINEAR\", \"r_texturemode\"/*q3*/,\n\t\t\t\t\t\t\t\t\t\t\t\tCVAR_ARCHIVE | CVAR_RENDERERCALLBACK | CVAR_SAVE, Image_TextureMode_Callback,\n\t\t\t\t\t\t\t\t\t\t\t\t\"Specifies how world/model textures appear. Typically 3 letters eg \"S_COLOR_GREEN\"nll\"S_COLOR_WHITE\" or \"S_COLOR_GREEN\"lll\"S_COLOR_WHITE\".\\nFirst letter can be l(inear) or n(earest) and says how to upscale low-res textures (n for the classic look - often favoured for embedded textures, l for blurry - best for high-res textures).\\nThe middle letter can be set to '.' to disable mipmaps, or n for ugly banding with distance, or l for smooth mipmap transitions.\\nThe third letter says what to do when the texture is too high resolution, and should generally be set to 'l' to reduce sparkles including when aiming for the classic lego look.\");\ncvar_t gl_texture_lodbias\t\t\t\t\t= CVARAFCD(\"d_lodbias\", \"0\", \"gl_texture_lodbias\",\n\t\t\t\t\t\t\t\t\t\t\t\tCVAR_ARCHIVE | CVAR_RENDERERCALLBACK,\n\t\t\t\t\t\t\t\t\t\t\t\tImage_TextureMode_Callback, \"Biases choice of mipmap levels. Positive values will give more blury textures, while negative values will give crisper images (but will also give some mid-surface aliasing artifacts).\");\ncvar_t gl_mipcap\t\t\t\t\t\t\t= CVARAFCD(\"d_mipcap\", \"0 1000\", \"gl_miptexLevel\",\n\t\t\t\t\t\t\t\t\t\t\t\tCVAR_ARCHIVE | CVAR_RENDERERCALLBACK,\n\t\t\t\t\t\t\t\t\t\t\t\tImage_TextureMode_Callback, \"Specifies the range of mipmap levels to use.\");\ncvar_t gl_texturemode2d\t\t\t\t\t\t= CVARFCD(\"gl_texturemode2d\", \"GL_LINEAR\",\n\t\t\t\t\t\t\t\t\t\t\t\tCVAR_ARCHIVE | CVAR_RENDERERCALLBACK, Image_TextureMode_Callback,\n\t\t\t\t\t\t\t\t\t\t\t\t\"Specifies how 2d images are sampled. format is a 3-tupple \");\ncvar_t r_font_linear\t\t\t\t\t\t= CVARF(\"r_font_linear\", \"1\", CVAR_ARCHIVE);\ncvar_t r_font_postprocess_outline\t\t\t= CVARFD(\"r_font_postprocess_outline\", \"0\", 0, \"Controls the number of pixels of dark borders to use around fonts.\");\ncvar_t r_font_postprocess_mono\t\t\t\t= CVARFD(\"r_font_postprocess_mono\", \"0\", 0, \"Disables anti-aliasing on fonts.\");\n\n#if defined(HAVE_LEGACY) && defined(AVAIL_FREETYPE)\ncvar_t dpcompat_smallerfonts\t\t\t\t= CVARFD(\"dpcompat_smallerfonts\", \"0\", 0, \"Mimics DP's behaviour of using a smaller font size than was actually requested.\");\n#endif\n\ncvar_t vid_triplebuffer\t\t\t\t\t\t= CVARAFD (\"vid_triplebuffer\", \"1\", \"gl_triplebuffer\", CVAR_ARCHIVE, \"Specifies whether the hardware is forcing tripplebuffering on us, this is the number of extra page swaps required before old data has been completely overwritten.\");\n\ncvar_t r_portalrecursion\t\t\t\t\t= CVARD  (\"r_portalrecursion\", \"1\", \"The number of portals the camera is allowed to recurse through.\");\ncvar_t r_portaldrawplanes\t\t\t\t\t= CVARD  (\"r_portaldrawplanes\", \"0\", \"Draw front and back planes in portals. Debug feature.\");\ncvar_t r_portalonly\t\t\t\t\t\t\t= CVARD  (\"r_portalonly\", \"0\", \"Don't draw things which are not portals. Debug feature.\");\ncvar_t r_noaliasshadows\t\t\t\t\t\t= CVARF (\"r_noaliasshadows\", \"0\", CVAR_ARCHIVE);\ncvar_t r_lodscale\t\t\t\t\t\t\t= CVARFD (\"r_lodscale\", \"5\", CVAR_ARCHIVE, \"Scales the level-of-detail reduction on models (for those that have lod).\");\ncvar_t r_lodbias\t\t\t\t\t\t\t= CVARFD (\"r_lodbias\", \"0\", CVAR_ARCHIVE, \"Biases the level-of-detail on models (for those that have lod).\");\ncvar_t r_shadows\t\t\t\t\t\t\t= CVARFD (\"r_shadows\", \"0\", CVAR_ARCHIVE, \"Draw basic blob shadows underneath entities without using realtime lighting.\");\ncvar_t r_showbboxes\t\t\t\t\t\t\t= CVARFD(\"r_showbboxes\", \"0\", CVAR_CHEAT, \"Debugging. Shows bounding boxes. 1=ssqc, 2=csqc. Red=solid, Green=stepping/toss/bounce, Blue=onground.\");\ncvar_t r_showfields\t\t\t\t\t\t\t= CVARD(\"r_showfields\", \"0\", \"Debugging. Shows entity fields boxes (entity closest to crosshair). 1=ssqc, 2=csqc, 3=snapshots.\");\ncvar_t r_showshaders\t\t\t\t\t\t= CVARD(\"r_showshaders\", \"0\", \"Debugging. Shows the name of the (worldmodel) shader being pointed to.\");\ncvar_t r_lightprepass_cvar\t\t\t\t\t= CVARFD(\"r_lightprepass\", \"0\", CVAR_ARCHIVE, \"Experimental. Attempt to use a different lighting mechanism (aka: deferred lighting). Requires vid_reload to take effect.\");\nint r_lightprepass;\n\ncvar_t r_shadow_bumpscale_basetexture\t\t= CVARD  (\"r_shadow_bumpscale_basetexture\", \"0\", \"bumpyness scaler for generation of fallback normalmap textures from models\");\ncvar_t r_shadow_bumpscale_bumpmap\t\t\t= CVARD  (\"r_shadow_bumpscale_bumpmap\", \"4\", \"bumpyness scaler for _bump textures\");\n\ncvar_t r_shadow_heightscale_basetexture\t\t= CVARD  (\"r_shadow_heightscale_basetexture\", \"0\", \"scaler for generation of height maps from legacy paletted content.\");\ncvar_t r_shadow_heightscale_bumpmap\t\t\t= CVARD  (\"r_shadow_heightscale_bumpmap\", \"1\", \"height scaler for 8bit _bump textures\");\n\ncvar_t r_glsl_pbr\t\t\t\t\t\t\t= CVARFD  (\"r_glsl_pbr\", \"0\", CVAR_ARCHIVE|CVAR_SHADERSYSTEM, \"Force PBR shading.\");\ncvar_t r_glsl_offsetmapping\t\t\t\t\t= CVARFD  (\"r_glsl_offsetmapping\", \"0\", CVAR_ARCHIVE|CVAR_SHADERSYSTEM, \"Enables the use of paralax mapping, adding fake depth to textures.\");\ncvar_t r_glsl_offsetmapping_scale\t\t\t= CVAR  (\"r_glsl_offsetmapping_scale\", \"0.04\");\ncvar_t r_glsl_offsetmapping_reliefmapping\t= CVARFD(\"r_glsl_offsetmapping_reliefmapping\", \"0\", CVAR_ARCHIVE|CVAR_SHADERSYSTEM, \"Changes the paralax sampling mode to be a bit nicer, but noticably more expensive at high resolutions. r_glsl_offsetmapping must be set.\");\ncvar_t r_glsl_turbscale_reflect\t\t\t\t= CVARFD  (\"r_glsl_turbscale_reflect\", \"1\", CVAR_ARCHIVE, \"Controls the strength of the water reflection ripples (used by the altwater glsl code).\");\ncvar_t r_glsl_turbscale_refract\t\t\t\t= CVARFD  (\"r_glsl_turbscale_refract\", \"1\", CVAR_ARCHIVE, \"Controls the strength of the underwater ripples (used by the altwater glsl code).\");\ncvar_t r_glsl_emissive\t\t\t\t\t\t= CVARFD  (\"r_glsl_emissive\", \"1\", CVAR_SHADERSYSTEM, \"When set, specifies that the _luma or _glow textures are emissive... When 0 they are taken as a mask for the proportion of the lightmap that will apply (for q2e compat, has issues with overbrights).\");\n\ncvar_t r_fastturbcolour\t\t\t\t\t\t= CVARFD (\"r_fastturbcolour\", \"0.1 0.2 0.3\", CVAR_ARCHIVE, \"The colour to use for water surfaces draw with r_waterstyle 0.\");\ncvar_t r_waterstyle\t\t\t\t\t\t\t= CVARFD (\"r_waterstyle\", \"1\", CVAR_ARCHIVE|CVAR_SHADERSYSTEM, \"Changes how water, and teleporters are drawn. Possible values are:\\n0: fastturb-style block colour.\\n1: regular q1-style water.\\n2: refraction(ripply and transparent)\\n3: refraction with reflection at an angle\\n4: ripplemapped without reflections (requires particle effects)\\n5: ripples+reflections\");\ncvar_t r_slimestyle\t\t\t\t\t\t\t= CVARFD (\"r_slimestyle\", \"\", CVAR_ARCHIVE|CVAR_SHADERSYSTEM, \"See r_waterstyle, but affects only slime. If empty, defers to r_waterstyle.\");\ncvar_t r_lavastyle\t\t\t\t\t\t\t= CVARFD (\"r_lavastyle\", \"1\", CVAR_ARCHIVE|CVAR_SHADERSYSTEM, \"See r_waterstyle, but affects only lava. If empty, defers to r_waterstyle.\");\ncvar_t r_telestyle\t\t\t\t\t\t\t= CVARFD (\"r_telestyle\", \"1\", CVAR_ARCHIVE|CVAR_SHADERSYSTEM, \"See r_waterstyle, but affects only teleporters. If empty, defers to r_waterstyle.\");\n\ncvar_t r_vertexdlights\t\t\t\t\t\t= CVARD\t(\"r_vertexdlights\", \"0\", \"Determine model lighting with respect to nearby dlights. Poor-man's rtlights.\");\n\ncvar_t vid_preservegamma\t\t\t\t\t= CVARD (\"vid_preservegamma\", \"0\", \"Restore initial hardware gamma ramps when quitting.\");\ncvar_t vid_hardwaregamma\t\t\t\t\t= CVARFD (\"vid_hardwaregamma\", \"1\",\n\t\t\t\t\t\t\t\t\t\t\t\tCVAR_ARCHIVE | CVAR_RENDERERLATCH, \"Use hardware gamma ramps. 0=ugly texture-based gamma, 1=glsl(windowed) or hardware(fullscreen), 2=always glsl, 3=always hardware gamma (disabled if hardware doesn't support), 4=scene-only gamma.\");\ncvar_t vid_desktopgamma\t\t\t\t\t\t= CVARFD (\"vid_desktopgamma\", \"0\",\n\t\t\t\t\t\t\t\t\t\t\t\tCVAR_ARCHIVE | CVAR_RENDERERLATCH, \"Apply gamma ramps upon the desktop rather than the window.\");\n\ncvar_t r_fog_cullentities\t\t\t\t\t= CVARD (\"r_fog_cullentities\", \"1\", \"0: Never cull entities by fog...\\n1: Automatically cull entities according to fog.\\n2: Force fog culling regardless \");\ncvar_t r_fog_linear\t\t\t\t\t\t\t= CVARD (\"r_fog_linear\", \"0\", \"0: Use Exp/Exp2 fog. 1: Use linear fog.\");\ncvar_t r_fog_exp2\t\t\t\t\t\t\t= CVARD (\"r_fog_exp2\", \"1\", \"Expresses how fog fades with distance. 0 (matching DarkPlaces's default) is typically more realistic, while 1 (matching FitzQuake and others) is more common.\");\ncvar_t r_fog_permutation\t\t\t\t\t= CVARFD (\"r_fog_permutation\", \"1\", CVAR_SHADERSYSTEM, \"Renders fog using a material permutation. 0 plays nicer with q3 shaders, but 1 is otherwise a better choice.\");\n\nextern cvar_t gl_dither;\ncvar_t\tgl_screenangle = CVAR(\"gl_screenangle\", \"0\");\n#endif\n\n#ifdef D3D9QUAKE\ncvar_t d3d9_hlsl\t\t\t\t\t\t\t= CVAR(\"d3d_hlsl\", \"1\");\n#endif\n\n#if defined(D3DQUAKE)\nvoid GLD3DRenderer_Init(void)\n{\n#ifdef D3D9QUAKE\n\tCvar_Register (&d3d9_hlsl, D3DRENDEREROPTIONS);\n#endif\n}\n#endif\n\n#if defined(GLQUAKE)\nextern cvar_t gl_blacklist_texture_compression;\nextern cvar_t gl_blacklist_generatemipmap;\nvoid GLRenderer_Init(void)\n{\n\t//gl-specific video vars\n\tCvar_Register (&gl_workaround_ati_shadersource, GLRENDEREROPTIONS);\n\tCvar_Register (&vid_gl_context_version, GLRENDEREROPTIONS);\n\tCvar_Register (&vid_gl_context_debug, GLRENDEREROPTIONS);\n\tCvar_Register (&vid_gl_context_forwardcompatible, GLRENDEREROPTIONS);\n\tCvar_Register (&vid_gl_context_compatibility, GLRENDEREROPTIONS);\n\tCvar_Register (&vid_gl_context_es, GLRENDEREROPTIONS);\n\tCvar_Register (&vid_gl_context_robustness, GLRENDEREROPTIONS);\n\tCvar_Register (&vid_gl_context_selfreset, GLRENDEREROPTIONS);\n\tCvar_Register (&vid_gl_context_noerror, GLRENDEREROPTIONS);\n\n\tCvar_Register (&gl_immutable_textures, GLRENDEREROPTIONS);\n\tCvar_Register (&gl_immutable_buffers, GLRENDEREROPTIONS);\n\tCvar_Register (&gl_pbolightmaps, GLRENDEREROPTIONS);\n//renderer\n\n\tCvar_Register (&gl_affinemodels, GLRENDEREROPTIONS);\n\tCvar_Register (&gl_nohwblend, GLRENDEREROPTIONS);\n#ifdef QWSKINS\n\tCvar_Register (&gl_nocolors, GLRENDEREROPTIONS);\n#endif\n\tCvar_Register (&gl_finish, GLRENDEREROPTIONS);\n\tCvar_Register (&gl_lateswap, GLRENDEREROPTIONS);\n\tCvar_Register (&gl_lerpimages, GLRENDEREROPTIONS);\n\n#ifdef PSKMODELS\n\tCvar_Register (&dpcompat_psa_ungroup, GLRENDEREROPTIONS);\n#endif\n#ifdef MD1MODELS\n\tCvar_Register (&mod_h2holey_bugged, GLRENDEREROPTIONS);\n\tCvar_Register (&mod_halftexel, GLRENDEREROPTIONS);\n\tCvar_Register (&mod_nomipmap, GLRENDEREROPTIONS);\n#endif\n\tCvar_Register (&r_lerpmuzzlehack, GLRENDEREROPTIONS);\n\tCvar_Register (&r_noframegrouplerp, GLRENDEREROPTIONS);\n\tCvar_Register (&r_portalrecursion, GLRENDEREROPTIONS);\n\tCvar_Register (&r_portaldrawplanes, GLRENDEREROPTIONS);\n\tCvar_Register (&r_portalonly, GLRENDEREROPTIONS);\n\tCvar_Register (&r_noaliasshadows, GLRENDEREROPTIONS);\n\n\tCvar_Register (&r_lodscale, GRAPHICALNICETIES);\n\tCvar_Register (&r_lodbias, GRAPHICALNICETIES);\n\n\tCvar_Register (&gl_motionblur, GLRENDEREROPTIONS);\n\tCvar_Register (&gl_motionblurscale, GLRENDEREROPTIONS);\n\n\tCvar_Register (&gl_smoothcrosshair, GRAPHICALNICETIES);\n\n//\tCvar_Register (&gl_lightmapmode, GLRENDEREROPTIONS);\n\n\tCvar_Register (&gl_picmip, GLRENDEREROPTIONS);\n\tCvar_Register (&gl_picmip2d, GLRENDEREROPTIONS);\n\tCvar_Register (&gl_picmip_world, GLRENDEREROPTIONS);\n\tCvar_Register (&gl_picmip_sprites, GLRENDEREROPTIONS);\n\tCvar_Register (&gl_picmip_other, GLRENDEREROPTIONS);\n\n\tCvar_Register (&r_shaderblobs, GLRENDEREROPTIONS);\n\tCvar_Register (&gl_compress, GLRENDEREROPTIONS);\n\tCvar_Register (&gl_blacklist_texture_compression, \"gl blacklists\");\n\tCvar_Register (&gl_blacklist_generatemipmap,\t\t\"gl blacklists\");\n//\tCvar_Register (&gl_detail, GRAPHICALNICETIES);\n//\tCvar_Register (&gl_detailscale, GRAPHICALNICETIES);\n\tCvar_Register (&gl_overbright, GRAPHICALNICETIES);\n\tCvar_Register (&gl_overbright_all, GRAPHICALNICETIES);\n\tCvar_Register (&gl_dither, GRAPHICALNICETIES);\n\tCvar_Register (&r_fog_cullentities, GRAPHICALNICETIES);\n\tCvar_Register (&r_fog_linear, GLRENDEREROPTIONS);\n\tCvar_Register (&r_fog_exp2, GLRENDEREROPTIONS);\n\tCvar_Register (&r_fog_permutation, GLRENDEREROPTIONS);\n\n\tCvar_Register (&r_tessellation, GRAPHICALNICETIES);\n\tCvar_Register (&gl_ati_truform_type, GRAPHICALNICETIES);\n\tCvar_Register (&r_tessellation_level, GRAPHICALNICETIES);\n\n\tCvar_Register (&gl_screenangle, GLRENDEREROPTIONS);\n\n\tCvar_Register (&r_wallcolour, GLRENDEREROPTIONS);\n\tCvar_Register (&r_floorcolour, GLRENDEREROPTIONS);\n//\tCvar_Register (&r_walltexture, GLRENDEREROPTIONS);\n//\tCvar_Register (&r_floortexture, GLRENDEREROPTIONS);\n\n\tCvar_Register (&r_vertexdlights, GLRENDEREROPTIONS);\n\n//\tCvar_Register (&gl_schematics, GLRENDEREROPTIONS);\n\n\tCvar_Register (&r_vertexlight, GLRENDEREROPTIONS);\n\n\tCvar_Register (&gl_blend2d, GLRENDEREROPTIONS);\n\n\tCvar_Register (&gl_menutint_shader, GLRENDEREROPTIONS);\n\n\tCvar_Register (&r_lightmap_nearest, GLRENDEREROPTIONS);\n\tCvar_Register (&r_lightmap_average, GLRENDEREROPTIONS);\n\tCvar_Register (&mod_loadsurfenvmaps, GLRENDEREROPTIONS);\n}\n#endif\n\nvoid\tR_InitTextures (void)\n{\n\tint\t\tx,y, m;\n\tqbyte\t*dest;\n\tstatic FTE_ALIGN(4) char r_notexture_mip_mem[(sizeof(texture_t) + 16*16)];\n\n// create a simple checkerboard texture for the default\n\tr_notexture_mip = (texture_t*)r_notexture_mip_mem;\n\n\tr_notexture_mip->vwidth = r_notexture_mip->vheight = 16;\n\tr_notexture_mip->srcwidth = r_notexture_mip->srcheight = 16;\n\tr_notexture_mip->srcfmt = TF_SOLID8;\n\n\tfor (m=0 ; m<1 ; m++)\n\t{\n\t\tdest = (qbyte *)(r_notexture_mip+1);\n\t\tfor (y=0 ; y< (16>>m) ; y++)\n\t\t\tfor (x=0 ; x< (16>>m) ; x++)\n\t\t\t{\n\t\t\t\tif (  (y< (8>>m) ) ^ (x< (8>>m) ) )\n\t\t\t\t\t*dest++ = 0;\n\t\t\t\telse\n\t\t\t\t\t*dest++ = 0xff;\n\t\t\t}\n\t}\n}\n\nstatic int QDECL ShowFileList (const char *name, qofs_t flags, time_t mtime, void *parm, searchpathfuncs_t *spath)\n{\n\t//ignore non-diffuse texture filenames, because they're annoying as heck.\n\tif (!strstr(name, \"_pants.\") && !strstr(name, \"_shirt.\") && !strstr(name, \"_upper.\") && !strstr(name, \"_lower.\") && !strstr(name, \"_bump.\") && !strstr(name, \"_norm.\") && !strstr(name, \"_gloss.\") && !strstr(name, \"_luma.\"))\n\t{\n\t\tCon_Printf(\"%s\\n\", name);\n\t}\n\treturn true;\n}\nvoid R_ListConfigs_f(void)\n{\n\tCOM_EnumerateFiles(\"*.cfg\", ShowFileList, NULL);\n\tCOM_EnumerateFiles(\"configs/*.cfg\", ShowFileList, NULL);\n}\nvoid R_ListFonts_f(void)\n{\n\tCOM_EnumerateFiles(\"charsets/*.*\", ShowFileList, NULL);\n\tCOM_EnumerateFiles(\"textures/charsets/*.*\", ShowFileList, NULL);\n}\nvoid R_ListSkins_f(void)\n{\n\tCOM_EnumerateFiles(\"skins/*.*\", ShowFileList, NULL);\n}\n\n\nvoid R_SetRenderer_f (void);\nvoid R_ReloadRenderer_f (void);\n\n#ifdef _DEBUG\nstatic void R_ShowBatches_f(void)\n{\n\tsh_config.showbatches = true;\n}\n#endif\n\nvoid R_ToggleFullscreen_f(void)\n{\n\tdouble time;\n\trendererstate_t newr;\n\n\tif (currentrendererstate.renderer == NULL)\n\t{\n\t\tCon_Printf(\"vid_toggle: no renderer currently set\\n\");\n\t\treturn;\n\t}\n\t//vid toggle makes no sense with these two...\n\tif (currentrendererstate.renderer->rtype == QR_HEADLESS || currentrendererstate.renderer->rtype == QR_NONE)\n\t\treturn;\n\n\tCvar_ApplyLatches(CVAR_VIDEOLATCH|CVAR_RENDERERLATCH, false);\n\n\tnewr = currentrendererstate;\n\tif (newr.fullscreen)\n\t\tnewr.fullscreen = 0;\t//if we're currently any sort of fullscreen then go windowed\n\telse if (vid_fullscreen.ival)\n\t\tnewr.fullscreen = vid_fullscreen.ival;\t//if we're normally meant to be fullscreen, use that\n\telse\n\t\tnewr.fullscreen = 2;\t//otherwise use native resolution\n\tif (newr.fullscreen)\n\t{\n\t\tint dbpp, dheight, dwidth, drate;\n\t\tif (!Sys_GetDesktopParameters(&dwidth, &dheight, &dbpp, &drate))\n\t\t{\n\t\t\tdwidth = DEFAULT_WIDTH;\n\t\t\tdheight = DEFAULT_HEIGHT;\n\t\t\tdbpp = DEFAULT_BPP;\n\t\t\tdrate = 0;\n\t\t}\n\n\t\tif (newr.fullscreen == 1 && vid_width.ival>0)\n\t\t\tnewr.width = vid_width.ival;\n\t\telse\n\t\t\tnewr.width = dwidth;\n\t\tif (newr.fullscreen == 1 && vid_height.ival>0)\n\t\t\tnewr.height = vid_height.ival;\n\t\telse\n\t\t\tnewr.height = dheight;\n\t}\n\telse\n\t{\n\t\tnewr.width = DEFAULT_WIDTH;\n\t\tnewr.height = DEFAULT_HEIGHT;\n\t}\n\n\ttime = Sys_DoubleTime();\n\tR_RestartRenderer(&newr);\n\tCon_DPrintf(\"main thread video restart took %f secs\\n\", Sys_DoubleTime() - time);\n//\tCOM_WorkerFullSync();\n//\tCon_Printf(\"full video restart took %f secs\\n\", Sys_DoubleTime() - time);\n}\n\nvoid Renderer_Init(void)\n{\n\tcurrentrendererstate.renderer = NULL;\n\tqrenderer = QR_NONE;\n\n\tr_blockvidrestart = true;\n\tCmd_AddCommand(\"setrenderer\", R_SetRenderer_f);\n\tCmd_AddCommand(\"vid_restart\", R_RestartRenderer_f);\n\tCmd_AddCommand(\"vid_reload\", R_ReloadRenderer_f);\n\tCmd_AddCommand(\"vid_toggle\", R_ToggleFullscreen_f);\n\n#ifdef RTLIGHTS\n\tR_EditLights_RegisterCommands();\n#endif\n\n\tCmd_AddCommand(\"r_dumpshaders\", Shader_WriteOutGenerics_f);\n\tCmd_AddCommand(\"r_remapshader\", Shader_RemapShader_f);\n\tCmd_AddCommand(\"r_showshader\", Shader_ShowShader_f);\n\tCmd_AddCommandD(\"r_shaderlist\", Shader_ShaderList_f, \"Prints out a list of the currently-loaded shaders.\");\n\n#ifdef _DEBUG\n\tCmd_AddCommand(\"r_showbatches\", R_ShowBatches_f);\n#endif\n\n#ifdef SWQUAKE\n\t{\n\textern cvar_t sw_interlace;\n\textern cvar_t sw_vthread;\n\textern cvar_t sw_fthreads;\n\tCvar_Register(&sw_interlace, \"Software Rendering Options\");\n\tCvar_Register(&sw_vthread, \"Software Rendering Options\");\n\tCvar_Register(&sw_fthreads, \"Software Rendering Options\");\n\t}\n#endif\n\n\tCvar_Register (&gl_conback, GRAPHICALNICETIES);\n\n\tCvar_Register (&r_novis, GLRENDEREROPTIONS);\n\n\t//but register ALL vid_ commands.\n\tCvar_Register (&gl_driver, VIDCOMMANDGROUP);\n\tCvar_Register (&vid_devicename, VIDCOMMANDGROUP);\n\tCvar_Register (&vid_vsync, VIDCOMMANDGROUP);\n\tCvar_Register (&vid_wndalpha, VIDCOMMANDGROUP);\n#if defined(_WIN32) && defined(MULTITHREAD)\n\tCvar_Register (&vid_winthread, VIDCOMMANDGROUP);\n#endif\n\tCvar_Register (&in_windowed_mouse, VIDCOMMANDGROUP);\n\tCvar_Register (&vid_renderer, VIDCOMMANDGROUP);\n\n\tR_UpdateRendererOpts();\n\n\tCvar_Register (&vid_renderer_opts, VIDCOMMANDGROUP);\n\n\tCvar_Register (&vid_fullscreen, VIDCOMMANDGROUP);\n\tCvar_Register (&vid_bpp, VIDCOMMANDGROUP);\n\tCvar_Register (&vid_depthbits, VIDCOMMANDGROUP);\n\n\tCvar_Register (&vid_baseheight, VIDCOMMANDGROUP);\n\tCvar_Register (&vid_conwidth, VIDCOMMANDGROUP);\n\tCvar_Register (&vid_conheight, VIDCOMMANDGROUP);\n\tCvar_Register (&vid_conautoscale, VIDCOMMANDGROUP);\n\tCvar_Register (&vid_minsize, VIDCOMMANDGROUP);\n\n\tCvar_Register (&vid_triplebuffer, VIDCOMMANDGROUP);\n\tCvar_Register (&vid_width, VIDCOMMANDGROUP);\n\tCvar_Register (&vid_height, VIDCOMMANDGROUP);\n\tCvar_Register (&vid_refreshrate, VIDCOMMANDGROUP);\n\tCvar_Register (&vid_multisample, GLRENDEREROPTIONS);\n\tCvar_Register (&vid_srgb, GLRENDEREROPTIONS);\n\tCvar_Register (&vid_dpi_x, GLRENDEREROPTIONS);\n\tCvar_Register (&vid_dpi_y, GLRENDEREROPTIONS);\n\n\tCvar_Register (&vid_desktopsettings, VIDCOMMANDGROUP);\n\tCvar_Register (&vid_preservegamma, GLRENDEREROPTIONS);\n\tCvar_Register (&vid_hardwaregamma, GLRENDEREROPTIONS);\n\tCvar_Register (&vid_desktopgamma, GLRENDEREROPTIONS);\n\n\n\tCvar_Register (&r_norefresh, GLRENDEREROPTIONS);\n\tCvar_Register (&r_mirroralpha, GLRENDEREROPTIONS);\n\tCvar_Register (&r_softwarebanding_cvar, GRAPHICALNICETIES);\n\n\tCvar_Register(&r_keepimages, GRAPHICALNICETIES);\n\tCvar_Register(&r_ignoremapprefixes, GRAPHICALNICETIES);\n\tCvar_ForceCallback(&r_keepimages);\n\tCvar_ForceCallback(&r_ignoremapprefixes);\n#ifdef IMAGEFMT_TGA\n\tCvar_Register(&r_dodgytgafiles, \"Hacky bug workarounds\");\n#endif\n#ifdef IMAGEFMT_PCX\n\tCvar_Register(&r_dodgypcxfiles, \"Hacky bug workarounds\");\n#endif\n\tCvar_Register(&r_dodgymiptex, \"Hacky bug workarounds\");\n\tr_imageextensions.enginevalue = r_defaultimageextensions;\n\tCvar_Register(&r_imageextensions, GRAPHICALNICETIES);\n\tr_imageextensions.callback(&r_imageextensions, NULL);\n\tCvar_Register(&r_image_downloadsizelimit, GRAPHICALNICETIES);\n\tCvar_Register(&r_loadlits, GRAPHICALNICETIES);\n\tCvar_Register(&r_lightstylesmooth, GRAPHICALNICETIES);\n\tCvar_Register(&r_lightstylesmooth_limit, GRAPHICALNICETIES);\n\tCvar_Register(&r_lightstylespeed, GRAPHICALNICETIES);\n\tCvar_Register(&r_lightstylescale, GRAPHICALNICETIES);\n\tCvar_Register(&r_lightmap_scale, GRAPHICALNICETIES);\n\tCvar_Register(&r_lightmap_format, GRAPHICALNICETIES);\n\n\tCvar_Register(&r_hdr_framebuffer, GRAPHICALNICETIES);\n\tCvar_Register(&r_hdr_irisadaptation, GRAPHICALNICETIES);\n\tCvar_Register(&r_hdr_irisadaptation_multiplier, GRAPHICALNICETIES);\n\tCvar_Register(&r_hdr_irisadaptation_minvalue, GRAPHICALNICETIES);\n\tCvar_Register(&r_hdr_irisadaptation_maxvalue, GRAPHICALNICETIES);\n\tCvar_Register(&r_hdr_irisadaptation_fade_down, GRAPHICALNICETIES);\n\tCvar_Register(&r_hdr_irisadaptation_fade_up, GRAPHICALNICETIES);\n\n\tCvar_Register(&r_stains, GRAPHICALNICETIES);\n\tCvar_Register(&r_stainfadetime, GRAPHICALNICETIES);\n\tCvar_Register(&r_stainfadeammount, GRAPHICALNICETIES);\n\tCvar_Register(&r_lightprepass_cvar, GLRENDEREROPTIONS);\n\tCvar_Register (&r_coronas, GRAPHICALNICETIES);\n\tCvar_Register (&r_coronas_intensity, GRAPHICALNICETIES);\n\tCvar_Register (&r_coronas_occlusion, GRAPHICALNICETIES);\n\tCvar_Register (&r_coronas_mindist, GRAPHICALNICETIES);\n\tCvar_Register (&r_coronas_fadedist, GRAPHICALNICETIES);\n\tCvar_Register (&r_flashblend, GRAPHICALNICETIES);\n\tCvar_Register (&r_flashblendscale, GRAPHICALNICETIES);\n\tCvar_Register (&r_glsl_pbr, GRAPHICALNICETIES);\n\tCvar_Register (&gl_specular, GRAPHICALNICETIES);\n\tCvar_Register (&gl_specular_power, GRAPHICALNICETIES);\n\tCvar_Register (&gl_specular_fallback, GRAPHICALNICETIES);\n\tCvar_Register (&gl_specular_fallbackexp, GRAPHICALNICETIES);\n\n\tSh_RegisterCvars();\n\n\tCvar_Register (&r_fastturbcolour, GRAPHICALNICETIES);\n\tCvar_Register (&r_waterstyle, GRAPHICALNICETIES);\n\tCvar_Register (&r_lavastyle, GRAPHICALNICETIES);\n\tCvar_Register (&r_slimestyle, GRAPHICALNICETIES);\n\tCvar_Register (&r_telestyle, GRAPHICALNICETIES);\n\tCvar_Register (&r_wireframe, GRAPHICALNICETIES);\n\tCvar_Register (&r_wireframe_smooth, GRAPHICALNICETIES);\n\tCvar_Register (&r_outline, GRAPHICALNICETIES);\n\tCvar_Register (&r_outline_width, GRAPHICALNICETIES);\n\tCvar_Register (&r_refract_fbo, GRAPHICALNICETIES);\n\tCvar_Register (&r_refractreflect_scale, GRAPHICALNICETIES);\n\tCvar_Register (&r_postprocshader, GRAPHICALNICETIES);\n\tCvar_Register (&r_fxaa, GRAPHICALNICETIES);\n\tCvar_Register (&r_graphics, GRAPHICALNICETIES);\n\tCvar_Register (&r_renderscale, GRAPHICALNICETIES);\n\tCvar_Register (&r_stereo_separation, GRAPHICALNICETIES);\n\tCvar_Register (&r_stereo_convergence, GRAPHICALNICETIES);\n\tCvar_Register (&r_stereo_method, GRAPHICALNICETIES);\n\n\tCvar_Register (&r_shadow_bumpscale_basetexture, GRAPHICALNICETIES);\n\tCvar_Register (&r_shadow_bumpscale_bumpmap, GRAPHICALNICETIES);\n\tCvar_Register (&r_shadow_heightscale_basetexture, GRAPHICALNICETIES);\n\tCvar_Register (&r_shadow_heightscale_bumpmap, GRAPHICALNICETIES);\n\n\tCvar_Register (&r_glsl_offsetmapping, GRAPHICALNICETIES);\n\tCvar_Register (&r_glsl_offsetmapping_scale, GRAPHICALNICETIES);\n\tCvar_Register (&r_glsl_offsetmapping_reliefmapping, GRAPHICALNICETIES);\n\tCvar_Register (&r_glsl_turbscale_reflect, GRAPHICALNICETIES);\n\tCvar_Register (&r_glsl_turbscale_refract, GRAPHICALNICETIES);\n\tCvar_Register (&r_glsl_emissive, GRAPHICALNICETIES);\n\n\tCvar_Register(&scr_viewsize, SCREENOPTIONS);\n\tCvar_Register(&scr_fov, SCREENOPTIONS);\n\tCvar_Register(&scr_fov_viewmodel, SCREENOPTIONS);\n\tCvar_Register(&scr_fov_mode, SCREENOPTIONS);\n\n\tCvar_Register (&scr_sshot_type, SCREENOPTIONS);\n\tCvar_Register (&scr_sshot_compression, SCREENOPTIONS);\n\tCvar_Register (&scr_sshot_prefix, SCREENOPTIONS);\n\n\tCvar_Register(&cl_cursor,\tSCREENOPTIONS);\n\tCvar_Register(&cl_cursorscale,\tSCREENOPTIONS);\n\tCvar_Register(&cl_cursorbiasx,\tSCREENOPTIONS);\n\tCvar_Register(&cl_cursorbiasy,\tSCREENOPTIONS);\n\n\n//screen\n\tCvar_Register (&con_textfont, GRAPHICALNICETIES);\n\tCvar_Register (&gl_font, GRAPHICALNICETIES);\n\tCvar_Register (&scr_conspeed, SCREENOPTIONS);\n\tCvar_Register (&scr_conalpha, SCREENOPTIONS);\n\tCvar_Register (&scr_showturtle, SCREENOPTIONS);\n\tCvar_Register (&scr_turtlefps, SCREENOPTIONS);\n\tCvar_Register (&scr_showpause, SCREENOPTIONS);\n\tCvar_Register (&scr_centertime, SCREENOPTIONS);\n\tCvar_Register (&scr_logcenterprint, SCREENOPTIONS);\n\tCvar_Register (&scr_printspeed, SCREENOPTIONS);\n\tCvar_Register (&scr_allowsnap, SCREENOPTIONS);\n\tCvar_Register (&scr_consize, SCREENOPTIONS);\n\tCvar_Register (&scr_centersbar, SCREENOPTIONS);\n\tCvar_Register (&r_xflip, GLRENDEREROPTIONS);\n\n\tCvar_Register(&r_bloodstains, GRAPHICALNICETIES);\n\n\tCvar_Register(&r_fullbrightSkins, GRAPHICALNICETIES);\n\n\tCvar_Register (&mod_md3flags, GRAPHICALNICETIES);\n\n\n//renderer\n\tCvar_Register (&r_ignoreentpvs, \"Hacky bug workarounds\");\n\tCvar_Register (&r_fullbright, SCREENOPTIONS);\n\tCvar_Register (&r_drawentities, GRAPHICALNICETIES);\n\tCvar_Register (&r_drawviewmodel, GRAPHICALNICETIES);\n\tCvar_Register (&r_drawviewmodelinvis, GRAPHICALNICETIES);\n\tCvar_Register (&r_waterwarp, GRAPHICALNICETIES);\n\tCvar_Register (&r_speeds, SCREENOPTIONS);\n\tCvar_Register (&r_netgraph, SCREENOPTIONS);\n\n\tCvar_Register (&r_temporalscenecache, GRAPHICALNICETIES);\n\tCvar_Register (&r_dynamic, GRAPHICALNICETIES);\n\tCvar_Register (&r_lightmap_saturation, GRAPHICALNICETIES);\n\n\tCvar_Register (&r_nolerp, GRAPHICALNICETIES);\n\tCvar_Register (&r_nolightdir, GRAPHICALNICETIES);\n\n\tCvar_Register (&r_fastturb, GRAPHICALNICETIES);\n\tCvar_Register (&r_wateralpha, GRAPHICALNICETIES);\n\tCvar_Register (&r_lavaalpha, GRAPHICALNICETIES);\n\tCvar_Register (&r_slimealpha, GRAPHICALNICETIES);\n\tCvar_Register (&r_telealpha, GRAPHICALNICETIES);\n\tCvar_Register (&gl_shadeq1_name, GLRENDEREROPTIONS);\n\n\tCvar_Register (&gl_mindist, GLRENDEREROPTIONS);\n\tCvar_Register (&gl_load24bit, GRAPHICALNICETIES);\n\tCvar_Register (&gl_blendsprites, GLRENDEREROPTIONS);\n\n\tCvar_Register (&r_clear, GLRENDEREROPTIONS);\n\tCvar_Register (&r_clearcolour, GLRENDEREROPTIONS);\n\tCvar_Register (&gl_max_size, GLRENDEREROPTIONS);\n\tCvar_Register (&gl_maxdist, GLRENDEREROPTIONS);\n\tCvar_Register (&gl_texturemode, GLRENDEREROPTIONS);\n\tCvar_Register (&gl_texturemode2d, GLRENDEREROPTIONS);\n\tCvar_Register (&r_font_linear, GLRENDEREROPTIONS);\n\tCvar_Register (&r_font_postprocess_outline, GLRENDEREROPTIONS);\n\tCvar_Register (&r_font_postprocess_mono, GLRENDEREROPTIONS);\n#if defined(HAVE_LEGACY) && defined(AVAIL_FREETYPE)\n\tCvar_Register (&dpcompat_smallerfonts, GLRENDEREROPTIONS);\n#endif\n\tCvar_Register (&gl_mipcap, GLRENDEREROPTIONS);\n\tCvar_Register (&gl_texture_lodbias, GLRENDEREROPTIONS);\n\tCvar_Register (&gl_texture_anisotropic_filtering, GLRENDEREROPTIONS);\n\tCvar_Register (&r_deluxemapping_cvar, GRAPHICALNICETIES);\n\tCvar_Register (&r_max_gpu_bones, GRAPHICALNICETIES);\n\tCvar_Register (&r_drawflat, GRAPHICALNICETIES);\n\tCvar_Register (&r_lightmap, GRAPHICALNICETIES);\n\tCvar_Register (&r_menutint, GRAPHICALNICETIES);\n\n\tCvar_Register (&r_fb_bmodels, GRAPHICALNICETIES);\n\tCvar_Register (&r_fb_models, GRAPHICALNICETIES);\n\tCvar_Register (&gl_overbright_models, GRAPHICALNICETIES);\n//\tCvar_Register (&r_fullbrights, GRAPHICALNICETIES);\t//dpcompat: 1 if r_fb_bmodels&&r_fb_models\n//\tCvar_Register (&r_skin_overlays, GRAPHICALNICETIES);\n\tCvar_Register (&r_globalskin_first, GRAPHICALNICETIES);\n\tCvar_Register (&r_globalskin_count, GRAPHICALNICETIES);\n\tCvar_Register (&r_shadows, GRAPHICALNICETIES);\n\n\tCvar_Register (&r_replacemodels, GRAPHICALNICETIES);\n\n\tCvar_Register (&r_showbboxes, GLRENDEREROPTIONS);\n\tCvar_Register (&r_showfields, GLRENDEREROPTIONS);\n\tCvar_Register (&r_showshaders, GLRENDEREROPTIONS);\n#ifdef BEF_PUSHDEPTH\n\tCvar_Register (&r_polygonoffset_submodel_factor, GLRENDEREROPTIONS);\n\tCvar_Register (&r_polygonoffset_submodel_offset, GLRENDEREROPTIONS);\n\tCvar_Register (&r_polygonoffset_submodel_maps, GLRENDEREROPTIONS);\n#endif\n\tCvar_Register (&r_polygonoffset_shadowmap_factor, GLRENDEREROPTIONS);\n\tCvar_Register (&r_polygonoffset_shadowmap_offset, GLRENDEREROPTIONS);\n\tCvar_Register (&r_polygonoffset_stencil_factor, GLRENDEREROPTIONS);\n\tCvar_Register (&r_polygonoffset_stencil_offset, GLRENDEREROPTIONS);\n\n\tCvar_Register (&r_forceprogramify, GLRENDEREROPTIONS);\n\tCvar_Register (&r_glsl_precache, GLRENDEREROPTIONS);\n\tCvar_Register (&r_halfrate, GRAPHICALNICETIES);\n#ifdef HAVE_LEGACY\n\tCvar_Register (&dpcompat_nopremulpics, GLRENDEREROPTIONS);\n#endif\n#ifdef VKQUAKE\n\tVK_RegisterVulkanCvars();\n#endif\n\n// misc\n\tCvar_Register(&con_ocranaleds, \"Console controls\");\n\n\tCmd_AddCommandD (\"listfonts\", R_ListFonts_f, \"Displays a list of every installed font.\");\n\tCmd_AddCommandD (\"listskins\", R_ListSkins_f, \"Displays a list of every installed or downloaded QuakeWorld player skin.\");\n\tCmd_AddCommandD (\"listconfigs\", R_ListConfigs_f, \"Displays a list of every installed config file.\");\n\n\tR_Sky_Register();\n\n#if defined(D3DQUAKE)\n\tGLD3DRenderer_Init();\n#endif\n#if defined(GLQUAKE)\n\tGLRenderer_Init();\n#endif\n\n#if defined(GLQUAKE) || defined(VKQUAKE)\n\tR_BloomRegister();\n#endif\n\n\tP_InitParticleSystem();\n\tR_InitTextures();\n\n\n\tR_RegisterBuiltinRenderers();\n}\n\nqboolean Renderer_Started(void)\n{\n\treturn !r_blockvidrestart && !!currentrendererstate.renderer;\n}\n\nvoid Renderer_Start(void)\n{\n\tr_blockvidrestart = false;\n\tCvar_ApplyLatches(CVAR_VIDEOLATCH|CVAR_RENDERERLATCH, false);\n\n\t//renderer = none && currentrendererstate.bpp == -1 means we've never applied any mode at all\n\t//if we currently have none, we do actually need to apply it still\n\tif (qrenderer == QR_NONE && *vid_renderer.string)\n\t{\n\t\tCmd_ExecuteString(\"vid_restart\\n\", RESTRICT_LOCAL);\n\t}\n\tif (!currentrendererstate.renderer)\n\t{\t//we still failed. Try again, but use the default renderer.\n\t\tCvar_Set(&vid_renderer, \"\");\n\t\tCmd_ExecuteString(\"vid_restart\\n\", RESTRICT_LOCAL);\n\t}\n\tif (!currentrendererstate.renderer)\n\t\tSys_Error(\"No renderer was set!\\n\");\n\n\tif (qrenderer == QR_NONE)\n\t\tCon_Printf(\"Use the setrenderer command to use a gui\\n\");\n}\n\n\nvoid\t(*Draw_Init)\t\t\t\t(void);\nvoid\t(*Draw_Shutdown)\t\t\t(void);\n\nvoid\t(*R_Init)\t\t\t\t\t(void);\nvoid\t(*R_DeInit)\t\t\t\t\t(void);\nvoid\t(*R_RenderView)\t\t\t\t(void);\t\t// must set r_refdef first\n\nqboolean (*VID_Init)\t\t\t\t(rendererstate_t *info, unsigned char *palette);\nvoid\t (*VID_DeInit)\t\t\t\t(void);\nchar\t*(*VID_GetRGBInfo)\t\t\t(int *stride, int *truevidwidth, int *truevidheight, enum uploadfmt *fmt);\nvoid\t(*VID_SetWindowCaption)\t\t(const char *msg);\n\nqboolean (*SCR_UpdateScreen)\t\t\t(void);\n\nr_qrenderer_t qrenderer;\nchar *q_renderername = \"Non-Selected renderer\";\n\nstatic struct\n{\n\tvoid *module;\n\trendererinfo_t *ri;\n} rendererinfo[16];\n\nqboolean R_RegisterRenderer(void *module, rendererinfo_t *ri)\n{\n\tsize_t i;\n\tfor (i = 0; i < countof(rendererinfo); i++)\n\t{\t//already registered\n\t\tif (rendererinfo[i].ri == ri)\n\t\t\treturn true;\n\t}\n\tfor (i = 0; i < countof(rendererinfo); i++)\n\t{\t//register it in the first empty slot\n\t\tif (!rendererinfo[i].ri)\n\t\t{\n\t\t\trendererinfo[i].module = module;\n\t\t\trendererinfo[i].ri = ri;\n\t\t\tR_UpdateRendererOpts();\n\t\t\treturn true;\n\t\t}\n\t}\n\tSys_Printf(\"unable to register renderer %s\\n\", ri->description);\n\treturn false;\n}\n\nstatic plugvrfuncs_t *vrfuncs;\nstatic void *vrmodule;\nqboolean R_RegisterVRDriver(void *module, plugvrfuncs_t *vr)\n{\n\tif (!vr)\n\t{\n\t\tif (vrmodule == module)\n\t\t{\n\t\t\tvrfuncs = NULL;\n\t\t\tvrmodule = NULL;\n\t\t}\n\t\treturn false;\n\t}\n\n\tif (vrfuncs && vrfuncs!=vr)\n\t{\n\t\tCon_Printf(\"unable to register renderer %s (%s already registered)\\n\", vr->description, vrfuncs->description);\n\t\treturn false;\n\t}\n\tvrfuncs = vr;\n\tvrmodule = module;\n\treturn true;\n}\n\n\n\nstatic rendererinfo_t dedicatedrendererinfo = {\n\t//ALL builds need a 'none' renderer, as 0.\n\t\"No renderer\",\n\t{\n\t\t\"none\",\n\t\t\"dedicated\",\n\t\t\"terminal\",\n\t\t\"sv\"\n\t},\n\tQR_NONE,\n\n\tNULL,\t//Draw_Init;\n\tNULL,\t//Draw_Shutdown;\n\n\tNULL,\t//IMG_UpdateFiltering\n\tNULL,\t//IMG_LoadTextureMips\n\tNULL,\t//IMG_DestroyTexture\n\n\tNULL,\t//R_Init;\n\tNULL,\t//R_DeInit;\n\tNULL,\t//R_RenderView;\n\n\tNULL, //VID_Init,\n\tNULL, //VID_DeInit,\n\tNULL, //VID_SwapBuffers\n\tNULL, //VID_ApplyGammaRamps,\n\tNULL,\n\tNULL,\n\tNULL,\n\tNULL,\t//set caption\n\tNULL, //VID_GetRGBInfo,\n\n\tNULL,\t//SCR_UpdateScreen;\n\n\t/*backend*/\n\tNULL,\n\tNULL,\n\tNULL,\n\tNULL,\n\tNULL,\n\tNULL,\n\tNULL,\n\tNULL,\n\tNULL,\n\tNULL,\n\tNULL,\n\tNULL,\n\tNULL,\n\tNULL,\n\tNULL,\n\tNULL,\n\tNULL,\n\tNULL,\n\n\tNULL,\n\n\t\"\"\n};\nstatic void R_RegisterBuiltinRenderers(void)\n{\n\t#ifdef GLQUAKE\n\t{\n\t\textern rendererinfo_t openglrendererinfo;\n\t\t#ifdef FTE_RPI\n\t\t{\n\t\t\textern rendererinfo_t rpirendererinfo;\n\t\t\tR_RegisterRenderer(NULL, &rpirendererinfo);\n\t\t}\n\t\t#endif\n\t\tR_RegisterRenderer(NULL, &openglrendererinfo);\n\t\t#ifdef USE_EGL\n\t\t{\n\t\t\textern rendererinfo_t eglrendererinfo;\n\t\t\tR_RegisterRenderer(NULL, &eglrendererinfo);\n\t\t}\n\t\t#endif\n\t}\n\t#endif\n\n\n\t#ifdef D3D9QUAKE\n\t{\n\t\textern rendererinfo_t d3d9rendererinfo;\n\t\tR_RegisterRenderer(NULL, &d3d9rendererinfo);\n\t}\n\t#endif\n\t#ifdef VKQUAKE\n\t{\n\t\textern rendererinfo_t vkrendererinfo;\n\t\tR_RegisterRenderer(NULL, &vkrendererinfo);\n\t\t#if defined(_WIN32) && defined(GLQUAKE) && !defined(FTE_SDL)\n\t\t{\n\t\t\textern rendererinfo_t nvvkrendererinfo;\n\t\t\tR_RegisterRenderer(NULL, &nvvkrendererinfo);\n\t\t}\n\t\t#endif\n\t}\n\t#endif\n\t#ifdef D3D11QUAKE\n\t{\n\t\textern rendererinfo_t d3d11rendererinfo;\n\t\tR_RegisterRenderer(NULL, &d3d11rendererinfo);\n\t}\n\t#endif\n\t#ifdef SWQUAKE\n\t{\n\t\textern rendererinfo_t swrendererinfo;\n\t\tR_RegisterRenderer(NULL, &swrendererinfo);\n\t}\n\t#endif\n\t#ifdef D3D8QUAKE\n\t{\n\t\textern rendererinfo_t d3d8rendererinfo;\n\t\tR_RegisterRenderer(NULL, &d3d8rendererinfo);\n\t}\n\t#endif\n\t#ifdef WAYLANDQUAKE\n\t\t#ifdef GLQUAKE\n\t\t{\n\t\t\textern rendererinfo_t rendererinfo_wayland_gl;\n\t\t\tR_RegisterRenderer(NULL, &rendererinfo_wayland_gl);\n\t\t}\n\t\t#endif\n\t\t#ifdef VKQUAKE\n\t\t{\n\t\t\textern rendererinfo_t rendererinfo_wayland_vk;\n\t\t\tR_RegisterRenderer(NULL, &rendererinfo_wayland_vk);\n\t\t}\n\t\t#endif\n\t#endif\n\t#if defined(GLQUAKE) && defined(USE_FBDEV)\n\t{\n\t\textern rendererinfo_t fbdevrendererinfo;\n\t\tR_RegisterRenderer(NULL, &fbdevrendererinfo);\t//direct stuff that doesn't interact well with the system should always be low priority\n\t}\n\t#endif\n\t\tR_RegisterRenderer(NULL, &dedicatedrendererinfo);\n\t#ifdef HEADLESSQUAKE\n\t{\n\t\textern rendererinfo_t headlessrenderer;\n\t\tR_RegisterRenderer(NULL, &headlessrenderer);\n\t\t#ifdef VKQUAKE\n\t\t\t//R_RegisterRenderer(NULL, &headlessvkrendererinfo);\n\t\t#endif\n\t}\n\t#endif\n\t#if defined(GLQUAKE) && defined(USE_EGL)\n\t{\n\t\textern rendererinfo_t rendererinfo_headless_egl;\n\t\tR_RegisterRenderer(NULL, &rendererinfo_headless_egl);\n\t}\n\t#endif\n}\n\n\nvoid R_SetRenderer(rendererinfo_t *ri)\n{\n\tcurrentrendererstate.renderer = ri;\n\tif (!ri)\n\t\tri = &dedicatedrendererinfo;\n\n\tqrenderer = ri->rtype;\n\tq_renderername = ri->name[0];\n\n\tDraw_Init\t\t\t\t= ri->Draw_Init;\n\tDraw_Shutdown\t\t\t= ri->Draw_Shutdown;\n\n\tR_Init\t\t\t\t\t= ri->R_Init;\n\tR_DeInit\t\t\t\t= ri->R_DeInit;\n\tR_RenderView\t\t\t= ri->R_RenderView;\n\n\tVID_Init\t\t\t\t= ri->VID_Init;\n\tVID_DeInit\t\t\t\t= ri->VID_DeInit;\n\tVID_GetRGBInfo\t\t\t= ri->VID_GetRGBInfo;\n\tVID_SetWindowCaption\t= ri->VID_SetWindowCaption;\n\n\tSCR_UpdateScreen\t\t= ri->SCR_UpdateScreen;\n}\n\nqbyte default_quakepal[768] =\n{\n0,0,0,15,15,15,31,31,31,47,47,47,63,63,63,75,75,75,91,91,91,107,107,107,123,123,123,139,139,139,155,155,155,171,171,171,187,187,187,203,203,203,219,219,219,235,235,235,15,11,7,23,15,11,31,23,11,39,27,15,47,35,19,55,43,23,63,47,23,75,55,27,83,59,27,91,67,31,99,75,31,107,83,31,115,87,31,123,95,35,131,103,35,143,111,35,11,11,15,19,19,27,27,27,39,39,39,51,47,47,63,55,55,75,63,63,87,71,71,103,79,79,115,91,91,127,99,99,\n139,107,107,151,115,115,163,123,123,175,131,131,187,139,139,203,0,0,0,7,7,0,11,11,0,19,19,0,27,27,0,35,35,0,43,43,7,47,47,7,55,55,7,63,63,7,71,71,7,75,75,11,83,83,11,91,91,11,99,99,11,107,107,15,7,0,0,15,0,0,23,0,0,31,0,0,39,0,0,47,0,0,55,0,0,63,0,0,71,0,0,79,0,0,87,0,0,95,0,0,103,0,0,111,0,0,119,0,0,127,0,0,19,19,0,27,27,0,35,35,0,47,43,0,55,47,0,67,\n55,0,75,59,7,87,67,7,95,71,7,107,75,11,119,83,15,131,87,19,139,91,19,151,95,27,163,99,31,175,103,35,35,19,7,47,23,11,59,31,15,75,35,19,87,43,23,99,47,31,115,55,35,127,59,43,143,67,51,159,79,51,175,99,47,191,119,47,207,143,43,223,171,39,239,203,31,255,243,27,11,7,0,27,19,0,43,35,15,55,43,19,71,51,27,83,55,35,99,63,43,111,71,51,127,83,63,139,95,71,155,107,83,167,123,95,183,135,107,195,147,123,211,163,139,227,179,151,\n171,139,163,159,127,151,147,115,135,139,103,123,127,91,111,119,83,99,107,75,87,95,63,75,87,55,67,75,47,55,67,39,47,55,31,35,43,23,27,35,19,19,23,11,11,15,7,7,187,115,159,175,107,143,163,95,131,151,87,119,139,79,107,127,75,95,115,67,83,107,59,75,95,51,63,83,43,55,71,35,43,59,31,35,47,23,27,35,19,19,23,11,11,15,7,7,219,195,187,203,179,167,191,163,155,175,151,139,163,135,123,151,123,111,135,111,95,123,99,83,107,87,71,95,75,59,83,63,\n51,67,51,39,55,43,31,39,31,23,27,19,15,15,11,7,111,131,123,103,123,111,95,115,103,87,107,95,79,99,87,71,91,79,63,83,71,55,75,63,47,67,55,43,59,47,35,51,39,31,43,31,23,35,23,15,27,19,11,19,11,7,11,7,255,243,27,239,223,23,219,203,19,203,183,15,187,167,15,171,151,11,155,131,7,139,115,7,123,99,7,107,83,0,91,71,0,75,55,0,59,43,0,43,31,0,27,15,0,11,7,0,0,0,255,11,11,239,19,19,223,27,27,207,35,35,191,43,\n43,175,47,47,159,47,47,143,47,47,127,47,47,111,47,47,95,43,43,79,35,35,63,27,27,47,19,19,31,11,11,15,43,0,0,59,0,0,75,7,0,95,7,0,111,15,0,127,23,7,147,31,7,163,39,11,183,51,15,195,75,27,207,99,43,219,127,59,227,151,79,231,171,95,239,191,119,247,211,139,167,123,59,183,155,55,199,195,55,231,227,87,127,191,255,171,231,255,215,255,255,103,0,0,139,0,0,179,0,0,215,0,0,255,0,0,255,243,147,255,247,199,255,255,255,159,91,83\n};\n\nqboolean R_ApplyRenderer_Load (rendererstate_t *newr);\nvoid D3DSucks(void)\n{\n\tSCR_DeInit();\n\n\tif (!R_ApplyRenderer_Load(NULL))//&currentrendererstate))\n\t\tSys_Error(\"Failed to reload content after mode switch\\n\");\n}\n\nvoid R_ShutdownRenderer(qboolean devicetoo)\n{\n#ifdef MENU_NATIVECODE\n\tif (mn_entry)\n\t\tmn_entry->Shutdown(MI_RENDERER);\n#endif\n\n\t//make sure the worker isn't still loading stuff\n\tCOM_WorkerFullSync();\n\n\tCL_AllowIndependantSendCmd(false);\t//FIXME: figure out exactly which parts are going to affect the model loading.\n\n#ifdef QWSKINS\n\tSkin_FlushAll();\n#endif\n\n\tP_Shutdown();\n\tMod_Shutdown(false);\n\n\tIN_Shutdown();\n\n\tMedia_VideoRestarting();\n\n\t//these functions need to be able to cope with vid_reload, so don't clear them.\n\t//they also need to be able to cope with being re-execed in the case of failed startup.\n\tif (R_DeInit)\n\t{\n\t\tTRACE((\"dbg: R_ApplyRenderer: R_DeInit\\n\"));\n\t\tR_DeInit();\n\t}\n\n\tif (Draw_Shutdown)\n\t\tDraw_Shutdown();\n\n\tTRACE((\"dbg: R_ApplyRenderer: SCR_DeInit\\n\"));\n\tSCR_DeInit();\n\n\tif (VID_DeInit && devicetoo)\n\t{\n\t\tif (vid.vr)\n\t\t\tvid.vr->Shutdown();\n\t\tvid.vr = NULL;\n\n\t\tTRACE((\"dbg: R_ApplyRenderer: VID_DeInit\\n\"));\n\t\tVID_DeInit();\n\t}\n\n\tCOM_FlushTempoaryPacks();\n\n\tW_Shutdown();\n\tif (h2playertranslations)\n\t\tBZ_Free(h2playertranslations);\n\th2playertranslations = NULL;\n\tif (host_basepal)\n\t\tBZ_Free(host_basepal);\n\thost_basepal = NULL;\n\tSurf_ClearSceneCache();\n\n\tRQ_Shutdown();\n\n\tif (devicetoo)\n\t\tS_Shutdown(false);\n\telse\n\t\tS_StopAllSounds (true);\n}\n\nvoid R_GenPaletteLookup(void)\n{\n\textern qbyte default_quakepal[];\n\tint r,g,b,i;\n\tunsigned char *pal = host_basepal;\n\tfor (i=0 ; i<256 ; i++)\n\t{\n\t\tr = pal[0];\n\t\tg = pal[1];\n\t\tb = pal[2];\n\t\tpal += 3;\n\n\t\td_8to24rgbtable[i] = (255<<24) + (r<<0) + (g<<8) + (b<<16);\n\t\td_8to24srgbtable[i] = (255<<24) + (SRGBb(r)<<0) + (SRGBb(g)<<8) + (SRGBb(b)<<16);\n\t\td_8to24bgrtable[i] = (255<<24) + (b<<0) + (g<<8) + (r<<16);\n\t}\n\td_8to24rgbtable[255] &= 0xffffff;\t// 255 is transparent\n\td_8to24srgbtable[255] &= 0xffffff;\t// 255 is transparent\n\td_8to24bgrtable[255] &= 0xffffff;\t// 255 is transparent\n\n\tfor (i=0 ; i<256 ; i++)\n\t\td_quaketo24srgbtable[i] = (255<<24) | (SRGBb(default_quakepal[(i)*3+0])<<0) | (SRGBb(default_quakepal[(i)*3+1])<<8) | (SRGBb(default_quakepal[(i)*3+2])<<16);\n}\nqboolean R_ApplyRenderer (rendererstate_t *newr)\n{\n\tdouble time;\n\tif (newr->bpp == -1)\n\t\treturn false;\n\tif (!newr->renderer)\n\t\treturn false;\n\n\tCOM_WorkerFullSync();\n\n\ttime = Sys_DoubleTime();\n\n#ifndef NOBUILTINMENUS\n//\tM_RemoveAllMenus(true);\n#endif\n\tMedia_CaptureDemoEnd();\n\tR_ShutdownRenderer(true);\n\tCon_DPrintf(\"video shutdown took %f seconds\\n\", Sys_DoubleTime() - time);\n\n#ifdef __GLIBC__\n\tmalloc_trim(0);\n#endif\n\n\tif (qrenderer == QR_NONE)\n\t{\n\t\tif (newr->renderer->rtype == qrenderer && currentrendererstate.renderer)\n\t\t{\n\t\t\tR_SetRenderer(newr->renderer);\n\t\t\treturn true;\t//no point\n\t\t}\n\n\t\tSys_CloseTerminal ();\n\t}\n\n\ttime = Sys_DoubleTime();\n\tR_SetRenderer(newr->renderer);\n\tCon_DPrintf(\"video startup took %f seconds\\n\", Sys_DoubleTime() - time);\n\n\treturn R_ApplyRenderer_Load(newr);\n}\nqboolean R_ApplyRenderer_Load (rendererstate_t *newr)\n{\n\tint i, j;\n\tdouble start = Sys_DoubleTime();\n\n\tCOM_WorkerFullSync();\n\n\tCache_Flush();\n\tCOM_FlushFSCache(false, true);\t//make sure the fs cache is built if needed. there's lots of loading here.\n\n\tTRACE((\"dbg: R_ApplyRenderer: old renderer closed\\n\"));\n\n\tpmove.numphysent = 0;\n\tpmove.physents[0].model = NULL;\n\n\tvid.dpi_x = 96;\n\tvid.dpi_y = 96;\n\n\tCvar_ApplyLatches(CVAR_RENDEREROVERRIDE, true);\n\n#ifndef CLIENTONLY\n\tsv.world.lastcheckpvs = NULL;\n#endif\n\n\tif (qrenderer != QR_NONE)\t//graphics stuff only when not dedicated\n\t{\n\t\tsize_t sz;\n\t\tqbyte *data;\n#ifndef CLIENTONLY\n\t\tisDedicated = false;\n#endif\n\t\tif (newr)\n\t\t\tif (!r_forceheadless || newr->renderer->rtype != QR_HEADLESS)\n\t\t\t{\n\t\t\t\tif (newr->fullscreen == 2)\n\t\t\t\t\tCon_TPrintf(\"Setting fullscreen windowed %s%s\\n\", newr->srgb?\"SRGB \":\"\", newr->renderer->description);\n\t\t\t\telse if (newr->fullscreen)\n\t\t\t\t{\n\t\t\t\t\tif (newr->rate)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (newr->width || newr->height)\n\t\t\t\t\t\t\tCon_TPrintf(\"Setting mode %i*%i %ibpp %ihz %s%s\\n\", newr->width, newr->height, newr->bpp, newr->rate, newr->srgb?\"SRGB \":\"\", newr->renderer->description);\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tCon_TPrintf(\"Setting mode auto %ibpp %ihz %s%s\\n\", newr->bpp, newr->rate, newr->srgb?\"SRGB \":\"\", newr->renderer->description);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tif (newr->width || newr->height)\n\t\t\t\t\t\t\tCon_TPrintf(\"Setting mode %i*%i %ibpp %s%s\\n\", newr->width, newr->height, newr->bpp, newr->srgb?\"SRGB \":\"\", newr->renderer->description);\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tCon_TPrintf(\"Setting mode auto %ibpp %s%s\\n\", newr->bpp, newr->srgb?\"SRGB \":\"\", newr->renderer->description);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tCon_TPrintf(\"Setting windowed mode %i*%i %s%s\\n\", newr->width, newr->height, newr->srgb?\"SRGB \":\"\", newr->renderer->description);\n\t\t\t}\n\n\t\tvid.fullbright=0;\n\n\t\tif (host_basepal)\n\t\t\tBZ_Free(host_basepal);\n\t\thost_basepal = (qbyte *)FS_LoadMallocFile (\"gfx/palette.lmp\", &sz);\n\t\tvid.fullbright = host_basepal?32:0;\t//q1-like mods are assumed to have 32 fullbright pixels, even if the colormap is missing.\n\t\tif (!host_basepal)\n\t\t{\n\t\t\thost_basepal = (qbyte *)FS_LoadMallocFile (\"wad/playpal\", &sz);\n\t\t\tif (host_basepal && sz > 768)\n\t\t\t\tsz = 768;\n\t\t}\n\t\tif (!host_basepal || sz != 768)\n\t\t{\n#if defined(Q2CLIENT) && defined(IMAGEFMT_PCX)\n\t\t\tqbyte *pcx = COM_LoadTempFile(\"pics/colormap.pcx\", 0, &sz);\n#endif\n\t\t\tif (host_basepal)\n\t\t\t\tZ_Free(host_basepal);\n\t\t\thost_basepal = BZ_Malloc(768);\n#if defined(Q2CLIENT) && defined(IMAGEFMT_PCX)\n\t\t\tif (pcx && ReadPCXPalette(pcx, sz, host_basepal))\n\t\t\t\tgoto q2colormap;\t//skip the colormap.lmp file as we already read it\n\t\t\telse\n#endif\n\t\t\t{\n\t\t\t\tmemcpy(host_basepal, default_quakepal, 768);\n\t\t\t}\n\t\t}\n\t\tif (!Ruleset_FileLoaded(\"gfx/palette.lmp\", host_basepal, 768))\n\t\t\tmemcpy(host_basepal, default_quakepal, 768);\n\n\t\t{\n\t\t\tsize_t csize;\n\t\t\tqbyte *colormap = (qbyte *)FS_LoadMallocFile (\"gfx/colormap.lmp\", &csize);\n\n\t\t\tif (colormap && csize == VID_GRADES*256+1 && Ruleset_FileLoaded(\"gfx/colormap.lmp\", colormap, csize))\n\t\t\t{\n\t\t\t\tj = VID_GRADES-1;\n\t\t\t\tdata = colormap + j*256;\n\t\t\t\tvid.fullbright = 0;\n\t\t\t\tfor (i = 255; i >= 0; i--)\n\t\t\t\t{\n\t\t\t\t\tif (colormap[i] == data[i])\n\t\t\t\t\t\tvid.fullbright++;\n\t\t\t\t\telse\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tBZ_Free(colormap);\n\t\t}\n\n#ifdef HEXEN2\n\t\tif (h2playertranslations)\n\t\t\tBZ_Free(h2playertranslations);\n\t\th2playertranslations = FS_LoadMallocFile (\"gfx/player.lmp\", NULL);\n#endif\n\n\t\tif (vid.fullbright < 2)\n\t\t\tvid.fullbright = 0;\t//transparent colour doesn't count.\n\n#if defined(Q2CLIENT) && defined(IMAGEFMT_PCX)\nq2colormap:\n#endif\n\nTRACE((\"dbg: R_ApplyRenderer: Palette loaded\\n\"));\n\n\t\tif (newr)\n\t\t{\n\t\t\tvid.flags = 0;\n\t\t\tvid.gammarampsize = 256;\t//make a guess.\n\t\t\tif (!VID_Init(newr, host_basepal))\n\t\t\t{\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\nTRACE((\"dbg: R_ApplyRenderer: vid applied\\n\"));\n\n\t\t//update palettes now that we know whether srgb is to be used etc\n\t\tR_GenPaletteLookup();\n\n\t\tr_softwarebanding = false;\n\t\tr_deluxemapping = false;\n\t\tr_lightprepass = false;\n\n\t\tW_LoadWadFile(\"gfx.wad\");\nTRACE((\"dbg: R_ApplyRenderer: wad loaded\\n\"));\n\t\tImage_Init();\n\t\tDraw_Init();\nTRACE((\"dbg: R_ApplyRenderer: draw inited\\n\"));\n#ifdef MENU_NATIVECODE\n\t\tif (mn_entry)\n\t\t\tmn_entry->Init(MI_RENDERER, vid.width, vid.height, vid.rotpixelwidth, vid.rotpixelheight);\n#endif\n\t\tR_Init();\n\t\tRQ_Init();\n\t\tR_InitParticleTexture ();\nTRACE((\"dbg: R_ApplyRenderer: renderer inited\\n\"));\n\t\tSCR_Init();\nTRACE((\"dbg: R_ApplyRenderer: screen inited\\n\"));\n\t\tSbar_Flush();\n\n\t\tIN_ReInit();\n\t\tMedia_VideoRestarted();\n\n\t\tCvar_ForceCallback(&v_gamma);\n\t}\n\telse\n\t{\n#ifdef CLIENTONLY\n\t\tSys_Error(\"Tried setting dedicated mode\\n\");\n\t\t//we could support this, but there's no real reason to actually do so.\n\n\t\t//fixme: despite the checks in the setrenderer command, we can still get here via a config using vid_renderer.\n#else\nTRACE((\"dbg: R_ApplyRenderer: isDedicated = true\\n\"));\n\t\tisDedicated = true;\n\t\tif (cls.state)\n\t\t{\n\t\t\tint os = sv.state;\n\t\t\tsv.state = ss_dead;\t//prevents server from being killed off too.\n\t\t\tCL_Disconnect(\"Graphics rendering disabled\");\n\t\t\tsv.state = os;\n\t\t}\n\t\tif (!Sys_InitTerminal())\n\t\t\treturn false;\n\t\tCon_PrintToSys();\n#endif\n\t}\nTRACE((\"dbg: R_ApplyRenderer: initing mods\\n\"));\n\tMod_Init(false);\n\n//\thost_hunklevel = Hunk_LowMark();\n\n\tCvar_ForceSetValue(&vid_dpi_x, vid.dpi_x);\n\tCvar_ForceSetValue(&vid_dpi_y, vid.dpi_y);\n#if 0//def HAVE_LEGACY\n\t{\t//if dp's vid_pixelheight cvar exists then lets force it to what we know.\n\t\t//that said, some dp mods just end up trying to use a fov of 0('auto') when this is 0, which fixes all our fov woes, so don't break that if its explictly 0 (a bit of a hack, but quite handy).\n\t\t//setting this properly seems to fuck over xonotic.\n\t\tcvar_t *pixelheight = Cvar_FindVar(\"vid_pixelheight\");\n\t\tif (pixelheight && (pixelheight->value || !*pixelheight->string))\n\t\t{\n\t\t\tif (vid.dpi_x && vid.dpi_y)\n\t\t\t{\n\t\t\t\tfloat ipd_x = 1/vid.dpi_x;\n\t\t\t\tfloat ipd_y = 1/vid.dpi_y;\n\t\t\t\tCvar_ForceSetValue(pixelheight, ipd_y/ipd_x);\n\t\t\t}\n\t\t\telse\n\t\t\t\tCvar_ForceSetValue(pixelheight, 1);\n\t\t}\n\t}\n#endif\n\n\tTRACE((\"dbg: R_ApplyRenderer: R_PreNewMap (how handy)\\n\"));\n\tSurf_PreNewMap();\n\n#ifndef CLIENTONLY\n\tif (sv.world.worldmodel)\n\t{\nTRACE((\"dbg: R_ApplyRenderer: reloading server map\\n\"));\n\t\tsv.world.worldmodel = Mod_ForName (sv.modelname, MLV_WARNSYNC);\nTRACE((\"dbg: R_ApplyRenderer: loaded\\n\"));\n\t\tif (sv.world.worldmodel->loadstate == MLS_LOADING)\n\t\t\tCOM_WorkerPartialSync(sv.world.worldmodel, &sv.world.worldmodel->loadstate, MLS_LOADING);\nTRACE((\"dbg: R_ApplyRenderer: doing that funky phs thang\\n\"));\n\t\tSV_CalcPHS ();\n\nTRACE((\"dbg: R_ApplyRenderer: clearing world\\n\"));\n\n\t\tif (sv.world.worldmodel->loadstate != MLS_LOADED)\n\t\t\tSV_UnspawnServer();\n\t\telse if (svs.gametype == GT_PROGS || svs.gametype == GT_Q1QVM)\n\t\t{\n\t\t\tfor (i = 0; i < MAX_PRECACHE_MODELS; i++)\n\t\t\t{\n\t\t\t\tif (sv.strings.model_precache[i] && *sv.strings.model_precache[i] && (!strcmp(sv.strings.model_precache[i] + strlen(sv.strings.model_precache[i]) - 4, \".bsp\") || i-1 < sv.world.worldmodel->numsubmodels))\n\t\t\t\t\tsv.models[i] = Mod_FindName(Mod_FixName(sv.strings.model_precache[i], sv.strings.model_precache[1]));\n\t\t\t\telse\n\t\t\t\t\tsv.models[i] = NULL;\n\t\t\t}\n\n\t\t\tWorld_ClearWorld (&sv.world, true);\n//\t\t\tent = sv.world.edicts;\n//\t\t\tent->v->model = PR_NewString(svprogfuncs, sv.worldmodel->name);\t//FIXME: is this a problem for normal ents?\n\t\t}\n#ifdef Q2SERVER\n\t\telse if (svs.gametype == GT_QUAKE2)\n\t\t{\n\t\t\tq2edict_t *q2ent;\n\t\t\tfor (i = 0; i < Q2MAX_MODELS; i++)\n\t\t\t{\n\t\t\t\tif (sv.strings.configstring[Q2CS_MODELS+i] && *sv.strings.configstring[Q2CS_MODELS+i] && (!strcmp(sv.strings.configstring[Q2CS_MODELS+i] + strlen(sv.strings.configstring[Q2CS_MODELS+i]) - 4, \".bsp\") || i-1 < sv.world.worldmodel->numsubmodels))\n\t\t\t\t\tsv.models[i] = Mod_FindName(Mod_FixName(sv.strings.configstring[Q2CS_MODELS+i], sv.modelname));\n\t\t\t\telse\n\t\t\t\t\tsv.models[i] = NULL;\n\t\t\t}\n\t\t\tfor (; i < MAX_PRECACHE_MODELS; i++)\n\t\t\t{\n\t\t\t\tif (sv.strings.q2_extramodels[i] && *sv.strings.q2_extramodels[i] && (!strcmp(sv.strings.q2_extramodels[i] + strlen(sv.strings.q2_extramodels[i]) - 4, \".bsp\") || i-1 < sv.world.worldmodel->numsubmodels))\n\t\t\t\t\tsv.models[i] = Mod_FindName(Mod_FixName(sv.strings.q2_extramodels[i], sv.modelname));\n\t\t\t\telse\n\t\t\t\t\tsv.models[i] = NULL;\n\t\t\t}\n\n\t\t\tWorld_ClearWorld (&sv.world, false);\n\t\t\tq2ent = ge->edicts;\n\t\t\tfor (i=0 ; i<ge->num_edicts ; i++, q2ent = (q2edict_t *)((char *)q2ent + ge->edict_size))\n\t\t\t{\n\t\t\t\tif (!q2ent)\n\t\t\t\t\tcontinue;\n\t\t\t\tif (!q2ent->inuse)\n\t\t\t\t\tcontinue;\n\n\t\t\t\tif (q2ent->area.prev)\n\t\t\t\t{\n\t\t\t\t\tq2ent->area.prev = q2ent->area.next = NULL;\n\t\t\t\t\tWorldQ2_LinkEdict (&sv.world, q2ent);\t// relink ents so touch functions continue to work.\n\t\t\t\t}\n\t\t\t}\n\t\t}\n#endif\n#ifdef Q3SERVER\n\t\telse if (svs.gametype == GT_QUAKE3)\n\t\t{\n\t\t\tmemset(&sv.models, 0, sizeof(sv.models));\n\t\t\tsv.models[1] = Mod_FindName(sv.modelname);\n\t\t\t//traditionally a q3 server can just keep hold of its world cmodel and nothing is harmed.\n\t\t\t//this means we just need to reload the worldmodel and all is fine...\n\t\t\t//there are some edge cases however, like lingering pointers refering to entities.\n\t\t}\n#endif\n\t\telse\n\t\t\tSV_UnspawnServer();\n\t}\n#endif\n#ifdef PLUGINS\n\tPlug_ResChanged(true);\n#endif\n\tCvar_ForceCallback(&r_particlesystem);\n#ifdef MENU_NATIVECODE\n\tif (mn_entry)\n\t\tmn_entry->Init(MI_RENDERER, vid.width, vid.height, vid.rotpixelwidth, vid.rotpixelheight);\n#endif\n\n\tCL_InitDlights();\n\nTRACE((\"dbg: R_ApplyRenderer: starting on client state\\n\"));\n\n\tif (newr)\n\t\tmemcpy(&currentrendererstate, newr, sizeof(currentrendererstate));\n\n\tTRACE((\"dbg: R_ApplyRenderer: S_Restart_f\\n\"));\n\tif (!isDedicated && newr)\n\t\tS_DoRestart(true);\n\n#ifdef VM_UI\n\tif (q3)\n\t\tq3->ui.Reset();\n#endif\n\n#ifdef Q3SERVER\n\tif (svs.gametype == GT_QUAKE3)\n\t{\n\t\tcl.worldmodel = NULL;\n\t\tif (q3)\n\t\t\tq3->cg.VideoRestarted();\n\t}\n\telse\n#endif\n\tif (cl.worldmodel)\n\t{\n\t\tint wmidx = 0;\n\t\tmodel_t *oldwm = cl.worldmodel;\n\t\tcl.worldmodel = NULL;\n\t\tCL_ClearEntityLists();\t//shouldn't really be needed, but we're paranoid\n\n\t\t//FIXME: this code should not be here. call CL_LoadModels instead? that does csqc loading etc though. :s\nTRACE((\"dbg: R_ApplyRenderer: reloading ALL models\\n\"));\n\t\tfor (i=1 ; i<MAX_PRECACHE_MODELS ; i++)\n\t\t{\n\t\t\tif (!cl.model_name[i])\n\t\t\t\tbreak;\n\n\t\t\tTRACE((\"dbg: R_ApplyRenderer: reloading model %s\\n\", cl.model_name[i]));\n\n\t\t\tif (oldwm == cl.model_precache[i])\n\t\t\t\twmidx = i;\n\n#ifdef Q2CLIENT\t//skip vweps\n\t\t\tif (cls.protocol == CP_QUAKE2 && *cl.model_name[i] == '#')\n\t\t\t\tcl.model_precache[i] = NULL;\n\t\t\telse\n#endif\n\t\t\t\tif (i == 1)\n\t\t\t\t\tcl.model_precache[i] = Mod_ForName (cl.model_name[i], MLV_SILENT);\n\t\t\t\telse\n\t\t\t\t\tcl.model_precache[i] = Mod_FindName (Mod_FixName(cl.model_name[i], cl.model_name[1]));\n\t\t}\n\n#ifdef HAVE_LEGACY\n\t\tfor (i=0; i < MAX_VWEP_MODELS; i++)\n\t\t{\n\t\t\tif (cl.model_name_vwep[i])\n\t\t\t\tcl.model_precache_vwep[i] = Mod_ForName (cl.model_name_vwep[i], MLV_SILENT);\n\t\t\telse\n\t\t\t\tcl.model_precache_vwep[i] = NULL;\n\t\t}\n#endif\n\n#ifdef CSQC_DAT\n\t\tfor (i=1 ; i<MAX_CSMODELS ; i++)\n\t\t{\n\t\t\tif (!cl.model_csqcname[i][0])\n\t\t\t\tbreak;\n\n\t\t\tif (oldwm == cl.model_csqcprecache[i])\n\t\t\t\twmidx = -i;\n\n\t\t\tcl.model_csqcprecache[i] = NULL;\n\t\t\tTRACE((\"dbg: R_ApplyRenderer: reloading csqc model %s\\n\", cl.model_csqcname[i]));\n\t\t\tcl.model_csqcprecache[i] = Mod_ForName (Mod_FixName(cl.model_csqcname[i], cl.model_name[1]), MLV_SILENT);\n\t\t}\n\n\t\tif (wmidx < 0)\n\t\t\tcl.worldmodel = cl.model_csqcprecache[-wmidx];\n\t\telse\n#endif\n\t\t\tcl.worldmodel = cl.model_precache[wmidx];\n\n\t\tif (cl.worldmodel && cl.worldmodel->loadstate == MLS_LOADING)\n\t\t\tCOM_WorkerPartialSync(cl.worldmodel, &cl.worldmodel->loadstate, MLS_LOADING);\n\nTRACE((\"dbg: R_ApplyRenderer: done the models\\n\"));\n\t\tif (!cl.worldmodel || cl.worldmodel->loadstate != MLS_LOADED)\n\t\t{\n//\t\t\tCon_Printf (\"\\nThe required model file '%s' could not be found.\\n\\n\", cl.model_name[i]);\n//\t\t\tCon_Printf (\"You may need to download or purchase a client pack in order to play on this server.\\n\\n\");\n\n\t\t\tCL_Disconnect (\"Worldmodel missing after video reload\");\n\n\t\t\tif (newr)\n\t\t\t\tmemcpy(&currentrendererstate, newr, sizeof(currentrendererstate));\n\t\t\treturn true;\n\t\t}\n\nTRACE((\"dbg: R_ApplyRenderer: checking any wad textures\\n\"));\n\t\tMod_NowLoadExternal(cl.worldmodel);\n\n\t\tfor (i = 0; i < cl.num_statics; i++)\t//make the static entities reappear.\n\t\t\tcl_static_entities[i].ent.model = NULL;\n\nTRACE((\"dbg: R_ApplyRenderer: Surf_NewMap\\n\"));\n\t\tSurf_NewMap(cl.worldmodel);\nTRACE((\"dbg: R_ApplyRenderer: efrags\\n\"));\n\n//\t\tSkin_FlushAll();\n\t\tSkin_FlushPlayers();\n\n\t}\n\n#ifdef SKELETALOBJECTS\n\tskel_reload();\n#endif\n#ifdef CSQC_DAT\n\tShader_DoReload();\n\tCSQC_RendererRestarted(false);\n#endif\n#ifdef MENU_DAT\n\tMP_RendererRestarted();\n#endif\n\n\tif (newr && qrenderer != QR_NONE)\n\t{\n\t\tif (!r_forceheadless || newr->renderer->rtype != QR_HEADLESS)\n\t\t\tCon_TPrintf(\"%s renderer initialized\\n\", newr->renderer->description);\n\t}\n\n\tTRACE((\"dbg: R_ApplyRenderer: done\\n\"));\n\n\n\tCon_DPrintf(\"video restart took %f seconds\\n\", Sys_DoubleTime() - start);\n\treturn true;\n}\n\nvoid R_ReloadRenderer_f (void)\n{\n#if !defined(CLIENTONLY)\n\tvoid *portalblob = NULL;\n\tsize_t portalsize = 0;\n#endif\n\n\tfloat time = Sys_DoubleTime();\n\tif (qrenderer == QR_NONE || qrenderer == QR_HEADLESS)\n\t\treturn;\t//don't bother reloading the renderer if its not actually rendering anything anyway.\n\n#if !defined(CLIENTONLY)\n\tif (sv.state == ss_active && sv.world.worldmodel && sv.world.worldmodel->loadstate == MLS_LOADED && sv.world.worldmodel->funcs.SaveAreaPortalBlob)\n\t{\n\t\tvoid *t;\n\t\tportalsize = sv.world.worldmodel->funcs.SaveAreaPortalBlob(sv.world.worldmodel, &t);\n\t\tif (portalsize && (portalblob = BZ_Malloc(portalsize)))\n\t\t\tmemcpy(portalblob, t, portalsize);\n\t}\n#endif\n\n\tCvar_ApplyLatches(CVAR_VIDEOLATCH|CVAR_RENDERERLATCH, false);\n\tR_ShutdownRenderer(false);\n\tCon_DPrintf(\"teardown = %f\\n\", Sys_DoubleTime() - time);\n\t//reloads textures without destroying video context.\n\tR_ApplyRenderer_Load(NULL);\n\tCvar_ApplyCallbacks(CVAR_RENDERERCALLBACK);\n\n#if !defined(CLIENTONLY)\n\tif (portalblob)\n\t{\n\t\tif (sv.world.worldmodel && sv.world.worldmodel->loadstate == MLS_LOADED && sv.world.worldmodel->funcs.LoadAreaPortalBlob)\n\t\t\tsv.world.worldmodel->funcs.LoadAreaPortalBlob(sv.world.worldmodel, portalblob, portalsize);\n\t\tBZ_Free(portalblob);\n\t}\n#endif\n\tCon_DPrintf(\"vid_reload time: %f\\n\", Sys_DoubleTime() - time);\n}\n\nstatic int R_PriorityForRenderer(rendererinfo_t *r)\n{\n\tif (r && r->name[0])\n\t{\n\t\tif (r->VID_GetPriority)\n\t\t\treturn r->VID_GetPriority();\n\t\telse if (r->rtype == QR_HEADLESS)\n\t\t\treturn -1;\t//headless renderers are a really poor choice, and will make the user think it buggy.\n\t\telse if (r->rtype == QR_NONE)\n\t\t\treturn 0;\t//dedicated servers are possible, but we really don't want to use them unless we have no other choice.\n\t\telse\n\t\t\treturn 1;\t//assume 1 for most renderers.\n\t}\n\treturn -2;\t//invalid renderer\n}\n\n//use Cvar_ApplyLatches(CVAR_RENDERERLATCH) beforehand.\nqboolean R_BuildRenderstate(rendererstate_t *newr, char *rendererstring)\n{\n\tint i, j;\n\n\tmemset(newr, 0, sizeof(*newr));\n\n\tnewr->width = vid_width.value;\n\tnewr->height = vid_height.value;\n\n\tnewr->triplebuffer = vid_triplebuffer.value;\n\tnewr->multisample = vid_multisample.value;\n\tnewr->bpp = vid_bpp.value;\n\tnewr->depthbits = vid_depthbits.value;\n\tnewr->fullscreen = vid_fullscreen.value;\n\tnewr->rate = vid_refreshrate.value;\n\tnewr->stereo = (r_stereo_method.ival == 1);\n\tnewr->srgb = vid_srgb.ival;\n\tnewr->vr = vrfuncs;\n\n#if defined(_WIN32) && !defined(FTE_SDL)\n\tif (newr->bpp && newr->bpp < 24)\n\t{\n\t\textern int qwinvermaj;\n\t\textern int qwinvermin;\n\t\tif ((qwinvermaj == 6 && qwinvermin >= 2) || qwinvermaj>6)\n\t\t{\n\t\t\t/*\n\t\t\tNote  Apps that you design to target Windows 8 and later can no longer query or set display modes that are less than 32 bits per pixel (bpp);\n\t\t\t\tthese operations will fail. These apps have a compatibility manifest that targets Windows 8.\n\t\t\tWindows 8 still supports 8-bit and 16-bit color modes for desktop apps that were built without a Windows 8 manifest;\n\t\t\t\tWindows 8 emulates these modes but still runs in 32-bit color mode.\n\t\t\t*/\n\t\t\tCon_Printf(\"Starting with windows 8, windows no longer supports 16-bit video modes\\n\");\n\t\t\tnewr->bpp = 24;\t//we don't count alpha as part of colour depth. windows does, so this'll end up as 32bit regardless.\n\t\t}\n\t}\n#endif\n\n\tif (com_installer)\n\t{\n\t\tnewr->fullscreen = false;\n\t\tnewr->width = 640;\n\t\tnewr->height = 480;\n\t}\n\n\tif (!*vid_vsync.string || vid_vsync.value < 0)\n\t\tnewr->wait = -1;\n\telse\n\t\tnewr->wait = vid_vsync.value;\n\n\tnewr->renderer = NULL;\n\n\trendererstring = COM_Parse(rendererstring);\n\tif (r_forceheadless)\n\t{\t//special hack so that android doesn't weird out when not focused.\n\t\tfor (i = 0; i < countof(rendererinfo); i++)\n\t\t{\n\t\t\tif (rendererinfo[i].ri && rendererinfo[i].ri->name[0] && rendererinfo[i].ri->rtype == QR_HEADLESS)\n\t\t\t{\n\t\t\t\tnewr->renderer = rendererinfo[i].ri;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\telse if (!*com_token)\n\t{\n\t\tint bestpri = -2;\n\t\tint pri;\n\t\tnewr->renderer = NULL;\n\t\t//I'd like to just qsort the renderers, but that isn't stable and might reorder gl+d3d etc.\n\t\tfor (i = 0; i < countof(rendererinfo); i++)\n\t\t{\n\t\t\tpri = R_PriorityForRenderer(rendererinfo[i].ri);\n\t\t\tif (pri > bestpri)\n\t\t\t{\n\t\t\t\tbestpri = pri;\n\t\t\t\tnewr->renderer = rendererinfo[i].ri;\n\t\t\t}\n\t\t}\n\t}\n\telse if (!strcmp(com_token, \"random\"))\n\t{\n\t\tint count;\n\t\tfor (i = 0, count = 0; i < countof(rendererinfo); i++)\n\t\t{\n\t\t\tif (!rendererinfo[i].ri || !rendererinfo[i].ri->description)\n\t\t\t\tcontinue;\t//not valid in this build. :(\n\t\t\tif (rendererinfo[i].ri->rtype == QR_NONE\t\t||\t//dedicated servers are not useful\n\t\t\t\trendererinfo[i].ri->rtype == QR_HEADLESS\t||\t//headless appears buggy\n\t\t\t\trendererinfo[i].ri->rtype == QR_SOFTWARE\t)\t//software is just TOO buggy/limited for us to care.\n\t\t\t\tcontinue;\n\t\t\tcount++;\n\t\t}\n\t\tcount = rand()%count;\n\t\tfor (i = 0; i < countof(rendererinfo); i++)\n\t\t{\n\t\t\tif (!rendererinfo[i].ri || !rendererinfo[i].ri->description)\n\t\t\t\tcontinue;\t//not valid in this build. :(\n\t\t\tif (rendererinfo[i].ri->rtype == QR_NONE\t\t||\n\t\t\t\trendererinfo[i].ri->rtype == QR_HEADLESS\t||\n\t\t\t\trendererinfo[i].ri->rtype == QR_SOFTWARE\t)\n\t\t\t\tcontinue;\n\t\t\tif (!count--)\n\t\t\t{\n\t\t\t\tnewr->renderer = rendererinfo[i].ri;\n\t\t\t\tCon_Printf(\"randomly selected renderer: %s\\n\", rendererinfo[i].ri->description);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\telse\n\t{\n\t\tint bestpri = -2, pri;\n\t\tfor (i = 0; i < countof(rendererinfo); i++)\n\t\t{\n\t\t\tif (!rendererinfo[i].ri || !rendererinfo[i].ri->description)\n\t\t\t\tcontinue;\t//not valid in this build. :(\n\t\t\tfor (j = 4-1; j >= 0; j--)\n\t\t\t{\n\t\t\t\tif (!rendererinfo[i].ri->name[j])\n\t\t\t\t\tcontinue;\n\t\t\t\tif (!stricmp(rendererinfo[i].ri->name[j], com_token))\n\t\t\t\t{\n\t\t\t\t\tpri = R_PriorityForRenderer(rendererinfo[i].ri);\n\n\t\t\t\t\tif (pri > bestpri)\n\t\t\t\t\t{\n\t\t\t\t\t\tbestpri = pri;\n\t\t\t\t\t\tnewr->renderer = rendererinfo[i].ri;\n\t\t\t\t\t}\n\t\t\t\t\tbreak; //try the next renderer now.\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\trendererstring = COM_Parse(rendererstring);\n\tif (*com_token)\n\t\tQ_strncpyz(newr->subrenderer, com_token, sizeof(newr->subrenderer));\n\telse if (newr->renderer && newr->renderer->rtype == QR_OPENGL)\n\t{\n\t\tQ_strncpyz(newr->subrenderer, gl_driver.string, sizeof(newr->subrenderer));\n\t\tif (strchr(newr->subrenderer, '/') || strchr(newr->subrenderer, '\\\\'))\n\t\t\t*newr->subrenderer = 0;\t//don't allow this to contain paths. that would be too exploitable - this often takes the form of dll/so names.\n\t}\n\t\n\tQ_strncpyz(newr->devicename, vid_devicename.string, sizeof(newr->devicename));\n\n\t// use desktop settings if set to 0 and not dedicated\n\tif (newr->renderer && newr->renderer->rtype != QR_NONE)\n\t{\n\t\textern int isPlugin;\n\n\t\tif (vid_desktopsettings.value)\n\t\t{\n\t\t\tnewr->width = 0;\n\t\t\tnewr->height = 0;\n\t\t\tnewr->bpp = 0;\n\t\t\tnewr->rate = 0;\n\t\t}\n\n\t\tif (newr->width <= 0 || newr->height <= 0 || newr->bpp <= 0)\n\t\t{\n\t\t\tint dbpp, dheight, dwidth, drate;\n\t\t\t\n\t\t\tif (!newr->fullscreen || isPlugin || !Sys_GetDesktopParameters(&dwidth, &dheight, &dbpp, &drate))\n\t\t\t{\n\t\t\t\tdwidth = DEFAULT_WIDTH;\n\t\t\t\tdheight = DEFAULT_HEIGHT;\n\t\t\t\tdbpp = DEFAULT_BPP;\n\t\t\t\tdrate = 0;\n\t\t\t}\n\n\t\t\tif (newr->width <= 0)\n\t\t\t\tnewr->width = dwidth;\n\t\t\tif (newr->height <= 0)\n\t\t\t\tnewr->height = dheight;\n\t\t\tif (newr->bpp <= 0)\n\t\t\t\tnewr->bpp = dbpp;\n\t\t}\n\t}\n\n#ifdef CLIENTONLY\n\tif (newr->renderer && newr->renderer->rtype == QR_NONE)\n\t{\n\t\tCon_Printf(\"Client-only builds cannot use dedicated modes.\\n\");\n\t\treturn false;\n\t}\n#endif\n\n\treturn newr->renderer != NULL;\n}\n\nstruct sortedrenderers_s\n{\n\tint index;\t//original index, to try to retain stable sort orders.\n\tint pri;\n\trendererinfo_t *r;\n};\nstatic int QDECL R_SortRenderers(const void *av, const void *bv)\n{\n\tconst struct sortedrenderers_s *a = av;\n\tconst struct sortedrenderers_s *b = bv;\n\tif (a->pri == b->pri)\n\t\treturn (a->index > b->index)?1:-1;\n\treturn (a->pri < b->pri)?1:-1;\n}\n\nvoid R_RestartRenderer (rendererstate_t *newr)\n{\n#if !defined(CLIENTONLY)\n\tvoid *portalblob = NULL;\n\tsize_t portalsize = 0;\n#endif\n\trendererstate_t oldr;\n\tif (r_blockvidrestart)\n\t{\n\t\tif (r_blockvidrestart != 2)\n\t\t\tCon_TPrintf(\"Unable to restart renderer at this time\\n\");\n\t\treturn;\n\t}\n\n#ifdef HAVE_SERVER\n\tif (sv.state == ss_active && sv.world.worldmodel && sv.world.worldmodel->loadstate == MLS_LOADED && sv.world.worldmodel->funcs.SaveAreaPortalBlob)\n\t{\n\t\tvoid *t;\n\t\tportalsize = sv.world.worldmodel->funcs.SaveAreaPortalBlob(sv.world.worldmodel, &t);\n\t\tif (portalsize && (portalblob = BZ_Malloc(portalsize)))\n\t\t\tmemcpy(portalblob, t, portalsize);\n\t}\n#endif\n\n\tTRACE((\"dbg: R_RestartRenderer_f renderer %p\\n\", newr->renderer));\n\n\tmemcpy(&oldr, &currentrendererstate, sizeof(rendererstate_t));\n\tif (!R_ApplyRenderer(newr))\n\t{\n\t\tTRACE((\"dbg: R_RestartRenderer_f failed\\n\"));\n\t\tif (R_ApplyRenderer(&oldr))\n\t\t{\n\t\t\tTRACE((\"dbg: R_RestartRenderer_f old restored\\n\"));\n\t\t\tCon_Printf(CON_ERROR \"Video mode switch failed. Old mode restored.\\n\");\t//go back to the old mode, the new one failed.\n\t\t}\n\t\telse\n\t\t{\n\t\t\tint i;\n\t\t\tqboolean failed = true;\n\t\t\trendererinfo_t *skip = newr->renderer;\n\t\t\tstruct sortedrenderers_s sorted[countof(rendererinfo)];\n\n\t\t\tif (failed && newr->vr)\n\t\t\t{\n\t\t\t\tCon_Printf(CON_NOTICE \"Trying without vr\\n\");\n\t\t\t\tnewr->vr = NULL;\n\t\t\t\tfailed = !R_ApplyRenderer(newr);\n\t\t\t}\n\t\t\tif (failed && newr->fullscreen == 1)\n\t\t\t{\n\t\t\t\tCon_Printf(CON_NOTICE \"Trying fullscreen windowed\"CON_DEFAULT\"\\n\");\n\t\t\t\tnewr->fullscreen = 2;\n\t\t\t\tfailed = !R_ApplyRenderer(newr);\n\t\t\t}\n\t\t\tif (failed && newr->rate != 0)\n\t\t\t{\n\t\t\t\tCon_Printf(CON_NOTICE \"Trying default refresh rate\"CON_DEFAULT\"\\n\");\n\t\t\t\tnewr->rate = 0;\n\t\t\t\tfailed = !R_ApplyRenderer(newr);\n\t\t\t}\n\t\t\tif (failed && newr->width != DEFAULT_WIDTH && newr->height != DEFAULT_HEIGHT)\n\t\t\t{\n\t\t\t\tCon_Printf(CON_NOTICE \"Trying %i*%i\"CON_DEFAULT\"\\n\", DEFAULT_WIDTH, DEFAULT_HEIGHT);\n\t\t\t\tif (newr->fullscreen == 2)\n\t\t\t\t\tnewr->fullscreen = 1;\n\t\t\t\tnewr->width = DEFAULT_WIDTH;\n\t\t\t\tnewr->height = DEFAULT_HEIGHT;\n\t\t\t\tfailed = !R_ApplyRenderer(newr);\n\t\t\t}\n\t\t\tif (failed && newr->fullscreen)\n\t\t\t{\n\t\t\t\tCon_Printf(CON_NOTICE \"Trying windowed\"CON_DEFAULT\"\\n\");\n\t\t\t\tnewr->fullscreen = 0;\n\t\t\t\tfailed = !R_ApplyRenderer(newr);\n\t\t\t}\n\n\t\t\tfor (i = 0; i < countof(sorted); i++)\n\t\t\t{\n\t\t\t\tsorted[i].index = i;\n\t\t\t\tsorted[i].r = rendererinfo[i].ri;\n\t\t\t\tsorted[i].pri = R_PriorityForRenderer(sorted[i].r);\n\t\t\t}\n\t\t\tqsort(sorted, countof(sorted), sizeof(sorted[0]), R_SortRenderers);\n\t\t\tfor (i = 0; failed && i < countof(sorted); i++)\n\t\t\t{\n\t\t\t\tnewr->renderer = sorted[i].r;\n\t\t\t\tif (newr->renderer && newr->renderer != skip && newr->renderer->rtype != QR_HEADLESS)\n\t\t\t\t{\n\t\t\t\t\tCon_Printf(CON_NOTICE \"Trying %s\"CON_DEFAULT\"\\n\", newr->renderer->description);\n\t\t\t\t\tfailed = !R_ApplyRenderer(newr);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t//if we ended up resorting to our last choice (dedicated) then print some informative message about it\n\t\t\t//fixme: on unixy systems, we should make sure we're actually printing to something (ie: that we're not running via some x11 shortcut with our stdout redirected to /dev/nul\n\t\t\tif (!failed && (!newr->renderer || newr->renderer->rtype == QR_NONE))\n\t\t\t{\n\t\t\t\tCon_Printf(CON_ERROR \"Video mode switch failed. Console forced.\\n\\nPlease change the following vars to something useable, and then use the setrenderer command.\\n\");\n\t\t\t\tCon_Printf(\"%s: %s\\n\", vid_width.name, vid_width.string);\n\t\t\t\tCon_Printf(\"%s: %s\\n\", vid_height.name, vid_height.string);\n\t\t\t\tCon_Printf(\"%s: %s\\n\", vid_bpp.name, vid_bpp.string);\n\t\t\t\tCon_Printf(\"%s: %s\\n\", vid_depthbits.name, vid_depthbits.string);\n\t\t\t\tCon_Printf(\"%s: %s\\n\", vid_refreshrate.name, vid_refreshrate.string);\n\t\t\t\tCon_Printf(\"%s: %s\\n\", vid_renderer.name, vid_renderer.string);\n\t\t\t\tCon_Printf(\"%s: %s\\n\", gl_driver.name, gl_driver.string);\n\t\t\t}\n\n\t\t\tif (failed)\n\t\t\t\tSys_Error(\"Unable to initialise any video mode\\n\");\n\t\t}\n\t}\n\n#ifdef HAVE_SERVER\n\tif (portalblob)\n\t{\n\t\tif (sv.world.worldmodel && sv.world.worldmodel->loadstate == MLS_LOADED && sv.world.worldmodel->funcs.LoadAreaPortalBlob)\n\t\t\tsv.world.worldmodel->funcs.LoadAreaPortalBlob(sv.world.worldmodel, portalblob, portalsize);\n\t\tBZ_Free(portalblob);\n\t}\n#endif\n\n\tCvar_ApplyCallbacks(CVAR_RENDERERCALLBACK);\n\tSCR_EndLoadingPlaque();\n\n\tTRACE((\"dbg: R_RestartRenderer_f success\\n\"));\n//\tM_Reinit();\n}\n\nvoid R_RestartRenderer_f (void)\n{\n\tdouble time;\n\trendererstate_t newr;\n\n\tif (r_blockvidrestart)\n\t{\n\t\tif (r_blockvidrestart!=2)\n\t\t\tCon_TPrintf(\"Ignoring vid_restart from config\\n\");\n\t\treturn;\n\t}\n\n\tCvar_ApplyLatches(CVAR_VIDEOLATCH|CVAR_RENDERERLATCH, false);\n\tif (!R_BuildRenderstate(&newr, vid_renderer.string))\n\t{\n\t\tCon_Printf(\"vid_renderer \\\"%s\\\" unsupported. Using default.\\n\", vid_renderer.string);\n\n\t\t//gotta do this after main hunk is saved off.\n\t\tCmd_ExecuteString(\"setrenderer \\\"\\\"\\n\", RESTRICT_LOCAL);\n\t\treturn;\n\t}\n\n\ttime = Sys_DoubleTime();\n\tR_RestartRenderer(&newr);\n\tCon_DPrintf(\"main thread video restart took %f secs\\n\", Sys_DoubleTime() - time);\n//\tCOM_WorkerFullSync();\n//\tCon_Printf(\"full video restart took %f secs\\n\", Sys_DoubleTime() - time);\n}\n\nstatic void R_EnumeratedRenderer(void *ctx, const char *devname, const char *outputname, const char *desc)\n{\n\trendererinfo_t *r = ctx;\n\tchar quoteddesc[1024];\n\n\tqboolean iscurrent = (currentrendererstate.renderer == r && (!*devname || !strcmp(devname, currentrendererstate.subrenderer)));\n\n\tconst char *dev;\n\tif (*outputname)\n\t\tdev = va(\"%s %s %s\", r->name[0], devname, outputname);\n\telse if (*devname)\n\t\tdev = va(\"%s %s\", r->name[0], devname);\n\telse\n\t\tdev = r->name[0];\n\tdev = COM_QuotedString(dev, quoteddesc,sizeof(quoteddesc), false);\n\n\tif (*outputname)\n\t\tCon_Printf(\"^[%s (%s, %s)\\\\type\\\\/setrenderer %s^]^7: %s%s\\n\",\n\t\t\tr->name[0], devname, outputname,\t//link text\n\t\t\tdev,\t//link itself.\n\t\t\tdesc, iscurrent?\" ^2(current)\":\"\");\n\telse if (*devname)\n\t\tCon_Printf(\"^[%s (%s)\\\\type\\\\/setrenderer %s^]^7: %s%s\\n\",\n\t\t\tr->name[0], devname,\t//link text\n\t\t\tdev,\t//link itself.\n\t\t\tdesc, iscurrent?\" ^2(current)\":\"\");\n\telse\n\t\tCon_Printf(\"^[%s\\\\type\\\\/setrenderer %s^]^7: %s%s\\n\", r->name[0], dev, r->description, iscurrent?\" ^2(current)\":\"\");\n}\n\nvoid R_SetRenderer_f (void)\n{\n\tint i;\n\tchar *param = Cmd_Argv(1);\n\trendererstate_t newr;\n\n\tif (Cmd_Argc() == 1 || !stricmp(param, \"help\"))\n\t{\n\t\tstruct sortedrenderers_s sorted[countof(rendererinfo)];\n\t\tfor (i = 0; i < countof(sorted); i++)\n\t\t{\n\t\t\tsorted[i].index = i;\n\t\t\tsorted[i].r = rendererinfo[i].ri;\n\t\t\tsorted[i].pri = R_PriorityForRenderer(sorted[i].r);\n\t\t}\n\t\tqsort(sorted, countof(sorted), sizeof(sorted[0]), R_SortRenderers);\n\n\t\tCon_Printf (\"\\nValid setrenderer parameters are:\\n\");\n\t\tfor (i = 0; i < countof(rendererinfo); i++)\n\t\t{\n\t\t\trendererinfo_t *r = sorted[i].r;\n\t\t\tif (r && r->description)\n\t\t\t{\n\t\t\t\tif (!r->VID_EnumerateDevices || !r->VID_EnumerateDevices(r, R_EnumeratedRenderer))\n\t\t\t\t\tR_EnumeratedRenderer(r, \"\", \"\", r->description);\n\t\t\t}\n\t\t}\n\t\treturn;\n\t}\n\n\tCvar_ApplyLatches(CVAR_VIDEOLATCH|CVAR_RENDERERLATCH, false);\n\tif (!R_BuildRenderstate(&newr, param))\n\t{\n\t\tCon_Printf(\"setrenderer: parameter not supported (%s)\\n\", param);\n\t\treturn;\n\t}\n\telse\n\t{\n\t\tif (Cmd_Argc() == 3)\n\t\t\tCvar_Set(&vid_bpp, Cmd_Argv(2));\n\t}\n\n\tif (newr.renderer->rtype != QR_HEADLESS && !strstr(param, \"headless\"))\t//don't save headless in the vid_renderer cvar via the setrenderer command. 'setrenderer headless;vid_restart' can then do what is most sane.\n\t\tCvar_ForceSet(&vid_renderer, param);\n\n\tif (!r_blockvidrestart)\n\t\tR_RestartRenderer(&newr);\n}\n\nstruct videnumctx_s\n{\n\tchar *v;\n\tchar *apiname;\n};\nstatic void R_DeviceEnumerated(void *context, const char *devicename, const char *outputname, const char *description)\n{\n\tstruct videnumctx_s *ctx = context;\n\tchar quotedname[1024];\n\tchar quoteddesc[1024];\n\tchar *name = va(\"%s %s %s\", ctx->apiname,\n\t\t\tCOM_QuotedString(devicename, quotedname,sizeof(quotedname), false),\n\t\t\tCOM_QuotedString(outputname, quoteddesc,sizeof(quoteddesc), false));\n\tZ_StrCat(&ctx->v, va(\"%s %s \", COM_QuotedString(name, quotedname, sizeof(quotedname), false), COM_QuotedString(description, quoteddesc, sizeof(quoteddesc), false)));\n}\n\nstatic qboolean rendereroptsupdated;\nstatic void R_UpdateRendererOptsNow(int iarg, void *data)\n{\n\tstruct videnumctx_s e = {NULL};\n\tsize_t i;\n\tstruct sortedrenderers_s sorted[countof(rendererinfo)];\n\tqboolean safe = COM_CheckParm(\"-noenumerate\") || COM_CheckParm(\"-safe\");\n\tif (!rendereroptsupdated)\n\t\treturn;\t//multiple got queued...\n\trendereroptsupdated = false;\n\n\tfor (i = 0; i < countof(sorted); i++)\n\t{\n\t\tsorted[i].index = i;\n\t\tsorted[i].r = rendererinfo[i].ri;\n\t\tsorted[i].pri = R_PriorityForRenderer(sorted[i].r);\n\t}\n\tqsort(sorted, countof(sorted), sizeof(sorted[0]), R_SortRenderers);\n\n\n\te.v = NULL;\n\tfor (i = 0; i < countof(rendererinfo); i++)\n\t{\n\t\trendererinfo_t *r = sorted[i].r;\n\t\tif (r && r->description)\n\t\t{\n\t\t\tif (r->rtype == QR_HEADLESS || r->rtype == QR_NONE)\n\t\t\t\tcontinue;\t//skip these, they're kinda dangerous.\n\t\t\te.apiname = r->name[0];\n\t\t\tif (safe || !r->VID_EnumerateDevices || !r->VID_EnumerateDevices(&e, R_DeviceEnumerated))\n\t\t\t\tR_DeviceEnumerated(&e, r->name[0], \"\", r->description);\n\t\t}\n\t}\n\n\tCvar_SetEngineDefault(&vid_renderer_opts, e.v?e.v:\"\");\n\tCvar_ForceSet(&vid_renderer_opts, e.v?e.v:\"\");\n\tZ_Free(e.v);\n}\nstatic void R_UpdateRendererOpts(void)\n{\t//use a timer+flag, so we don't reenumerate everything any time any device gets registered.\n\trendereroptsupdated\t= true;\n\tCmd_AddTimer(0, R_UpdateRendererOptsNow, 0, NULL, 0);\n}\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n/*\n================\nR_GetSpriteFrame\n================\n*/\nmspriteframe_t *R_GetSpriteFrame (entity_t *currententity)\n{\n\tmsprite_t\t\t*psprite;\n\tmspritegroup_t\t*pspritegroup;\n\tmspriteframe_t\t*pspriteframe;\n\tint\t\t\t\ti, numframes, frame;\n\tfloat\t\t\t*pintervals, fullinterval, targettime, time;\n\n\tpsprite = currententity->model->meshinfo;\n\tframe = currententity->framestate.g[FS_REG].frame[0];\n\n\tif ((frame >= psprite->numframes) || (frame < 0))\n\t{\n\t\tCon_DPrintf (\"R_DrawSprite: no such frame %d (%s)\\n\", frame, currententity->model->name);\n\t\tframe = 0;\n\t}\n\n\tif (psprite->frames[frame].type == SPR_SINGLE)\n\t{\n\t\tpspriteframe = psprite->frames[frame].frameptr;\n\t}\n\telse if (psprite->frames[frame].type == SPR_ANGLED)\n\t{\n\t\tfloat f = DotProduct(vpn,currententity->axis[0]);\n\t\tfloat r = DotProduct(vright,currententity->axis[0]);\n\t\tfloat ang;\n\t\tpspritegroup = (mspritegroup_t *)psprite->frames[frame].frameptr;\n\t\tang = (atan2(r, f)+M_PI)/(2*M_PI);\t//to give 0 - 1 range\n\t\ti = (ang*pspritegroup->numframes) + 0.5;\n\n//\t\tpspriteframe = pspritegroup->frames[(int)((r_refdef.viewangles[1]-currententity->angles[1])/360*pspritegroup->numframes + 0.5-4)%pspritegroup->numframes];\n\t\t//int dir = (int)((r_refdef.viewangles[1]-currententity->angles[1])/360*8 + 8 + 0.5-4)&7;\n\t\tpspriteframe = pspritegroup->frames[i%pspritegroup->numframes];\n\t}\n\telse\n\t{\n\t\tpspritegroup = (mspritegroup_t *)psprite->frames[frame].frameptr;\n\t\tpintervals = pspritegroup->intervals;\n\t\tnumframes = pspritegroup->numframes;\n\t\tfullinterval = pintervals[numframes-1];\n\n\t\ttime = currententity->framestate.g[FS_REG].frametime[0];\n\n\t// when loading in Mod_LoadSpriteGroup, we guaranteed all interval values\n\t// are positive, so we don't have to worry about division by 0\n\t\ttargettime = time - ((int)(time / fullinterval)) * fullinterval;\n\n\t\tfor (i=0 ; i<(numframes-1) ; i++)\n\t\t{\n\t\t\tif (pintervals[i] > targettime)\n\t\t\t\tbreak;\n\t\t}\n\n\t\tpspriteframe = pspritegroup->frames[i];\n\t}\n\n\treturn pspriteframe;\n}\n\n\n/*\n===============\nR_TextureAnimation\n\nReturns the proper texture for a given time and base texture\n===============\n*/\ntexture_t *R_TextureAnimation (int frame, texture_t *base)\n{\n\tunsigned int\trelative;\n\tint\t\t\t\tcount;\n\n\tif (frame)\n\t{\n\t\tif (base->alternate_anims)\n\t\t\tbase = base->alternate_anims;\n\t}\n\n\tif (!base->anim_total)\n\t\treturn base;\n\n\trelative = (unsigned int)(cl.time*10) % base->anim_total;\n\n\tcount = 0;\n\twhile (base->anim_min > relative || base->anim_max <= relative)\n\t{\n\t\tbase = base->anim_next;\n\t\tif (!base)\n\t\t\tSys_Error (\"R_TextureAnimation: broken cycle\");\n\t\tif (++count > 100)\n\t\t\tSys_Error (\"R_TextureAnimation: infinite cycle\");\n\t}\n\n\treturn base;\n}\n\ntexture_t *R_TextureAnimation_Q2 (texture_t *base)\n{\n\tint\t\treletive;\n\tint\t\tframe;\n\t\n\tif (!base->anim_total)\n\t\treturn base;\n\n\t//this is only ever used on world. everything other than rtlights have proper batches.\n\tframe = cl.time*2;\t//q2 is lame\n\n\treletive = frame % base->anim_total;\n\n\twhile (reletive --> 0)\n\t{\n\t\tbase = base->anim_next;\n\t\tif (!base)\n\t\t\tSys_Error (\"R_TextureAnimation: broken cycle\");\n\t}\n\n\treturn base;\n}\n\n\n\n\nunsigned int\tr_viewcontents;\n//mleaf_t\t\t*r_viewleaf, *r_oldviewleaf;\n//mleaf_t\t\t*r_viewleaf2, *r_oldviewleaf2;\nint r_viewarea;\nint\t\tr_viewcluster, r_viewcluster2;\n\n\n/*\n=================\nR_CullBox\n\nReturns true if the box is completely outside the frustom\n=================\n*/\nqboolean R_CullBox (vec3_t mins, vec3_t maxs)\n{\n\t//this isn't very precise.\n\t//checking each plane individually can be problematic\n\t//if you have a large object behind the view, it can cross multiple planes, and be infront of each one at some point, yet should still be outside the view.\n\t//this is quite noticable with terrain where the potential height of a section is essentually infinite.\n\t//note that this is not a concern for spheres, just boxes.\n\tint\t\ti;\n\n\tfor (i = 0; i < r_refdef.frustum_numplanes; i++)\n\t\tif (BOX_ON_PLANE_SIDE (mins, maxs, &r_refdef.frustum[i]) == 2)\n\t\t\treturn true;\n\treturn false;\n}\n\nqboolean R_CullSphere (vec3_t org, float radius)\n{\n\t//four frustrum planes all point inwards in an expanding 'cone'.\n\tint\t\ti;\n\tfloat d;\n\n\tfor (i = 0; i < r_refdef.frustum_numplanes; i++)\n\t{\n\t\td = DotProduct(r_refdef.frustum[i].normal, org)-r_refdef.frustum[i].dist;\n\t\tif (d <= -radius)\n\t\t\treturn true;\n\t}\n\treturn false;\n}\n\nqboolean R_CullEntityBox(entity_t *e, vec3_t modmins, vec3_t modmaxs)\n{\n\tint i;\n\tvec3_t wmin, wmax;\n\n#if 1\n\tfloat mrad = 0, v;\n\n\tif (e->axis[0][0]==1 && e->axis[0][1]==0 && e->axis[0][2]==0 &&\n\t\te->axis[1][0]==0 && e->axis[1][1]==1 && e->axis[1][2]==0 &&\n\t\te->axis[2][0]==0 && e->axis[2][1]==0 && e->axis[2][2]==1)\n\t{\n\t\tfor (i = 0; i < 3; i++)\n\t\t{\n\t\t\twmin[i] = e->origin[i]+modmins[i]*e->scale;\n\t\t\twmax[i] = e->origin[i]+modmaxs[i]*e->scale;\n\t\t}\n\t}\n\telse\n\t{\n\t\tfor (i = 0; i < 3; i++)\n\t\t{\n\t\t\tv = fabs(modmins[i]);\n\t\t\tif (mrad < v)\n\t\t\t\tmrad = v;\n\t\t\tv = fabs(modmaxs[i]);\n\t\t\tif (mrad < v)\n\t\t\t\tmrad = v;\n\t\t}\n\t\tmrad *= e->scale;\n\t\tfor (i = 0; i < 3; i++)\n\t\t{\n\t\t\twmin[i] = e->origin[i]-mrad;\n\t\t\twmax[i] = e->origin[i]+mrad;\n\t\t}\n\t}\n#else\n\tfloat fmin, fmax;\n\n\t//convert the model's bbox to the expanded maximum size of the entity, as drawn with this model.\n\t//The result is an axial box, which we pass to R_CullBox\n\n\tfor (i = 0; i < 3; i++)\n\t{\n\t\tfmin = DotProduct(modmins, e->axis[i]);\n\t\tfmax = DotProduct(modmaxs, e->axis[i]);\n\n\t\tif (fmin > -16)\n\t\t\tfmin = -16;\n\t\tif (fmax < 16)\n\t\t\tfmax = 16;\n\n\t\tif (fmin < fmax)\n\t\t{\n\t\t\twmin[i] = e->origin[i]+fmin;\n\t\t\twmax[i] = e->origin[i]+fmax;\n\t\t}\n\t\telse\n\t\t{       //box went inside out\n\t\t\twmin[i] = e->origin[i]+fmax;\n\t\t\twmax[i] = e->origin[i]+fmin;\n\t\t}\n\t}\n#endif\n\n\treturn R_CullBox(wmin, wmax);\n}\n\n\n\n\nint SignbitsForPlane (mplane_t *out)\n{\n\tint\tbits, j;\n\n\t// for fast box on planeside test\n\n\tbits = 0;\n\tfor (j=0 ; j<3 ; j++)\n\t{\n\t\tif (out->normal[j] < 0)\n\t\t\tbits |= 1<<j;\n\t}\n\treturn bits;\n}\nvoid R_SetFrustum (float projmat[16], float viewmat[16])\n{\n\tfloat scale;\n\tint i;\n\tfloat mvp[16];\n\tmplane_t *p;\n\n\tif (r_novis.ival & 4)\n\t\treturn;\n\n\tMatrix4_Multiply(projmat, viewmat, mvp);\n\n\tr_refdef.frustum_numplanes = 0;\n\n\tfor (i = 0; i < 4; i++)\n\t{\n\t\t//each of the four side planes\n\t\tp = &r_refdef.frustum[r_refdef.frustum_numplanes++];\n\t\tif (i & 1)\n\t\t{\n\t\t\tp->normal[0] = mvp[3] + mvp[0+i/2];\n\t\t\tp->normal[1] = mvp[7] + mvp[4+i/2];\n\t\t\tp->normal[2] = mvp[11] + mvp[8+i/2];\n\t\t\tp->dist\t\t = mvp[15] + mvp[12+i/2];\n\t\t}\n\t\telse\n\t\t{\n\t\t\tp->normal[0] = mvp[3] - mvp[0+i/2];\n\t\t\tp->normal[1] = mvp[7] - mvp[4+i/2];\n\t\t\tp->normal[2] = mvp[11] - mvp[8+i/2];\n\t\t\tp->dist\t\t = mvp[15] - mvp[12+i/2];\n\t\t}\n\n\t\tscale = 1/sqrt(DotProduct(p->normal, p->normal));\n\t\tp->normal[0] *= scale;\n\t\tp->normal[1] *= scale;\n\t\tp->normal[2] *= scale;\n\t\tp->dist\t\t *= -scale;\n\n\t\tp->type\t\t = PLANE_ANYZ;\n\t\tp->signbits\t = SignbitsForPlane (p);\n\t}\n\n\t//the near clip plane.\n\tp = &r_refdef.frustum[r_refdef.frustum_numplanes++];\n\n\tp->normal[0] = mvp[3] - mvp[2];\n\tp->normal[1] = mvp[7] - mvp[6];\n\tp->normal[2] = mvp[11] - mvp[10];\n\tp->dist      = mvp[15] - mvp[14];\n\n\tscale = 1/sqrt(DotProduct(p->normal, p->normal));\n\tp->normal[0] *= scale;\n\tp->normal[1] *= scale;\n\tp->normal[2] *= scale;\n\tp->dist *= -scale;\n\n\tp->type      = PLANE_ANYZ;\n\tp->signbits  = SignbitsForPlane (p);\n\n\tr_refdef.frustum_numworldplanes = r_refdef.frustum_numplanes;\n\n\t//do far plane\n\t//fog will logically not actually reach 0, though precision issues will force it. we cut off at an exponant of -500\n\tif (r_refdef.globalfog.density && r_refdef.globalfog.alpha>=1 && (r_fog_cullentities.ival==2||(r_fog_cullentities.ival&&r_skyfog.value>=1)) && !r_refdef.globalfog.depthbias)\n\t{\n\t\tfloat culldist;\n\t\tfloat fog;\n\t\textern cvar_t r_fog_exp2;\n\n\t\t/*Documentation: the GLSL/GL will do this maths:\n\t\tfloat dist = 1024;\n\t\tif (r_fog_exp2.ival)\n\t\t\tfog = pow(2, -r_refdef.globalfog.density * r_refdef.globalfog.density * dist * dist * 1.442695);\n\t\telse\n\t\t\tfog = pow(2, -r_refdef.globalfog.density * dist * 1.442695);\n\t\t*/\n\n\t\t//the fog factor cut-off where its pointless to allow it to get closer to 0 (0 is technically infinite)\n\t\tfog = 2/255.0f;\n\n\t\t//figure out the eyespace distance required to reach that fog value\n\t\tculldist = log(fog);\n\t\tif (r_fog_exp2.ival)\n\t\t\tculldist = sqrt(culldist / (-r_refdef.globalfog.density * r_refdef.globalfog.density));\n\t\telse\n\t\t\tculldist = culldist / (-r_refdef.globalfog.density);\n\t\t//anything drawn beyond this point is fully obscured by fog\n\n\t\tp = &r_refdef.frustum[r_refdef.frustum_numplanes++];\n\t\tp->normal[0] = mvp[3] - mvp[2];\n\t\tp->normal[1] = mvp[7] - mvp[6];\n\t\tp->normal[2] = mvp[11] - mvp[10];\n\t\tp->dist      = mvp[15] - mvp[14];\n\n\t\tscale = 1/sqrt(DotProduct(p->normal, p->normal));\n\t\tp->normal[0] *= -scale;\n\t\tp->normal[1] *= -scale;\n\t\tp->normal[2] *= -scale;\n//\t\tp->dist *= scale;\n\t\tp->dist\t= DotProduct(r_refdef.vieworg, p->normal)-culldist;\n\n\t\tp->type      = PLANE_ANYZ;\n\t\tp->signbits  = SignbitsForPlane (p);\n\t}\n}\n\n\n\n\n#include \"glquake.h\"\n\n//we could go for nice smooth round particles... but then we would loose a little bit of the chaotic nature of the particles.\nstatic qbyte\tdottexture[8][8] =\n{\n\t{0,0,0,0,0,0,0,0},\n\t{0,0,0,1,1,0,0,0},\n\t{0,0,1,1,1,1,0,0},\n\t{0,1,1,1,1,1,1,0},\n\t{0,1,1,1,1,1,1,0},\n\t{0,0,1,1,1,1,0,0},\n\t{0,0,0,1,1,0,0,0},\n\t{0,0,0,0,0,0,0,0},\n};\nstatic qbyte\texptexture[16][16] =\n{\n\t{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},\n\t{0,0,0,0,1,0,0,0,1,0,0,1,0,0,0,0},\n\t{0,0,0,1,1,1,1,1,3,1,1,2,1,0,0,0},\n\t{0,0,0,1,1,1,1,4,4,4,5,4,2,1,1,0},\n\t{0,0,1,1,6,5,5,8,6,8,3,6,3,2,1,0},\n\t{0,0,1,5,6,7,5,6,8,8,8,3,3,1,0,0},\n\t{0,0,0,1,6,8,9,9,9,9,4,6,3,1,0,0},\n\t{0,0,2,1,7,7,9,9,9,9,5,3,1,0,0,0},\n\t{0,0,2,4,6,8,9,9,9,9,8,6,1,0,0,0},\n\t{0,0,2,2,3,5,6,8,9,8,8,4,4,1,0,0},\n\t{0,0,1,2,4,1,8,7,8,8,6,5,4,1,0,0},\n\t{0,1,1,1,7,8,1,6,7,5,4,7,1,0,0,0},\n\t{0,1,2,1,1,5,1,3,4,3,1,1,0,0,0,0},\n\t{0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0},\n\t{0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0},\n\t{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},\n};\n\ntexid_t\t\t\tparticletexture;\t// little dot for particles\ntexid_t\t\t\tparticlecqtexture;\t// little dot for particles\ntexid_t\t\t\texplosiontexture;\ntexid_t\t\t\tballtexture;\ntexid_t\t\t\tbeamtexture;\ntexid_t\t\t\tptritexture;\nvoid R_InitParticleTexture (void)\n{\n#define PARTICLETEXTURESIZE 64\n\tint\t\tx,y;\n\tfloat dx, dy, d;\n\tqbyte\tdata[PARTICLETEXTURESIZE*PARTICLETEXTURESIZE][4];\n\n\t//\n\t// particle texture\n\t//\n\tfor (x=0 ; x<8 ; x++)\n\t{\n\t\tfor (y=0 ; y<8 ; y++)\n\t\t{\n\t\t\tdata[y*8+x][0] = 255;\n\t\t\tdata[y*8+x][1] = 255;\n\t\t\tdata[y*8+x][2] = 255;\n\t\t\tdata[y*8+x][3] = dottexture[x][y]*255;\n\t\t}\n\t}\n\n\tTEXASSIGN(particletexture, R_LoadTexture32(\"dotparticle\", 8, 8, data, IF_NOMIPMAP|IF_NOPICMIP|IF_CLAMP|IF_NOPURGE|IF_INEXACT));\n\n\n\t//\n\t// particle triangle texture\n\t//\n\n\t// clear to transparent white\n\tfor (x = 0; x < 32 * 32; x++)\n\t{\n\t\t\tdata[x][0] = 255;\n\t\t\tdata[x][1] = 255;\n\t\t\tdata[x][2] = 255;\n\t\t\tdata[x][3] = 0;\n\t}\n\t//draw a circle in the top left.\n\tfor (x=0 ; x<16 ; x++)\n\t{\n\t\tfor (y=0 ; y<16 ; y++)\n\t\t{\n\t\t\tif ((x - 7.5) * (x - 7.5) + (y - 7.5) * (y - 7.5) <= 8 * 8)\n\t\t\t\tdata[y*32+x][3] = 255;\n\t\t}\n\t}\n\tparticlecqtexture = Image_GetTexture(\"classicparticle\", \"particles\", IF_NOMIPMAP|IF_NOPICMIP|IF_CLAMP|IF_NOPURGE|IF_INEXACT, data, NULL, 32, 32, TF_RGBA32);\n\n\t//draw a square in the top left. still a triangle.\n\tfor (x=0 ; x<16 ; x++)\n\t{\n\t\tfor (y=0 ; y<16 ; y++)\n\t\t{\n\t\t\tdata[y*32+x][3] = 255;\n\t\t}\n\t}\n\tImage_GetTexture(\"classicparticle_square\", \"particles\", IF_NOMIPMAP|IF_NOPICMIP|IF_CLAMP|IF_NOPURGE|IF_INEXACT, data, NULL, 32, 32, TF_RGBA32);\n\n\n\tfor (x=0 ; x<16 ; x++)\n\t{\n\t\tfor (y=0 ; y<16 ; y++)\n\t\t{\n\t\t\tdata[y*16+x][0] = 255;\n\t\t\tdata[y*16+x][1] = 255;\n\t\t\tdata[y*16+x][2] = 255;\n\t\t\tdata[y*16+x][3] = exptexture[x][y]*255/9.0;\n\t\t}\n\t}\n\texplosiontexture = Image_GetTexture(\"fte_fuzzyparticle\", \"particles\", IF_NOMIPMAP|IF_NOPICMIP|IF_NOPURGE|IF_INEXACT, data, NULL, 16, 16, TF_RGBA32);\n\n\tfor (x=0 ; x<16 ; x++)\n\t{\n\t\tfor (y=0 ; y<16 ; y++)\n\t\t{\n\t\t\tdata[y*16+x][0] = exptexture[x][y]*255/9.0;\n\t\t\tdata[y*16+x][1] = exptexture[x][y]*255/9.0;\n\t\t\tdata[y*16+x][2] = exptexture[x][y]*255/9.0;\n\t\t\tdata[y*16+x][3] = exptexture[x][y]*255/9.0;\n\t\t}\n\t}\n\tImage_GetTexture(\"fte_bloodparticle\", \"particles\", IF_NOMIPMAP|IF_NOPICMIP|IF_NOPURGE|IF_INEXACT, data, NULL, 16, 16, TF_RGBA32);\n\n\tfor (x=0 ; x<16 ; x++)\n\t{\n\t\tfor (y=0 ; y<16 ; y++)\n\t\t{\n\t\t\tdata[y*16+x][0] = min(255, exptexture[x][y]*255/9.0);\n\t\t\tdata[y*16+x][1] = min(255, exptexture[x][y]*255/5.0);\n\t\t\tdata[y*16+x][2] = min(255, exptexture[x][y]*255/5.0);\n\t\t\tdata[y*16+x][3] = 255;\n\t\t}\n\t}\n\tImage_GetTexture(\"fte_blooddecal\", \"particles\", IF_NOMIPMAP|IF_NOPICMIP|IF_NOPURGE|IF_INEXACT, data, NULL, 16, 16, TF_RGBA32);\n\n\tmemset(data, 255, sizeof(data));\n\tfor (y = 0;y < PARTICLETEXTURESIZE;y++)\n\t{\n\t\tdy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);\n\t\tfor (x = 0;x < PARTICLETEXTURESIZE;x++)\n\t\t{\n\t\t\tdx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);\n\t\t\td = 256 * (1 - (dx*dx+dy*dy));\n\t\t\td = bound(0, d, 255);\n\t\t\tdata[y*PARTICLETEXTURESIZE+x][3] = (qbyte) d;\n\t\t}\n\t}\n\tballtexture = R_LoadTexture32(\"balltexture\", PARTICLETEXTURESIZE, PARTICLETEXTURESIZE, data, IF_NOMIPMAP|IF_NOPICMIP|IF_NOPURGE|IF_INEXACT);\n\n\tmemset(data, 255, sizeof(data));\n\tfor (y = 0;y < PARTICLETEXTURESIZE;y++)\n\t{\n\t\tdy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);\n\t\td = 256 * (1 - (dy*dy));\n\t\td = bound(0, d, 255);\n\t\tfor (x = 0;x < PARTICLETEXTURESIZE;x++)\n\t\t{\n\t\t\tdata[y*PARTICLETEXTURESIZE+x][3] = (qbyte) d;\n\t\t}\n\t}\n\tbeamtexture = R_LoadTexture32(\"beamparticle\", PARTICLETEXTURESIZE, PARTICLETEXTURESIZE, data, IF_NOMIPMAP|IF_NOPICMIP|IF_NOPURGE|IF_INEXACT);\n\n\tfor (y = 0;y < PARTICLETEXTURESIZE;y++)\n\t{\n\t\tdy = y / (PARTICLETEXTURESIZE*0.5f-1);\n\t\td = 256 * (1 - (dy*dy));\n\t\td = bound(0, d, 255);\n\t\tfor (x = 0;x < PARTICLETEXTURESIZE;x++)\n\t\t{\n\t\t\tdx = x / (PARTICLETEXTURESIZE*0.5f-1);\n\t\t\td = 256 * (1 - (dx+dy));\n\t\t\td = bound(0, d, 255);\n\t\t\tdata[y*PARTICLETEXTURESIZE+x][0] = (qbyte) d;\n\t\t\tdata[y*PARTICLETEXTURESIZE+x][1] = (qbyte) d;\n\t\t\tdata[y*PARTICLETEXTURESIZE+x][2] = (qbyte) d;\n\t\t\tdata[y*PARTICLETEXTURESIZE+x][3] = (qbyte) d/2;\n\t\t}\n\t}\n\tptritexture = R_LoadTexture32(\"ptritexture\", PARTICLETEXTURESIZE, PARTICLETEXTURESIZE, data, IF_NOMIPMAP|IF_NOPICMIP|IF_NOPURGE|IF_INEXACT);\n}\n\n"
  },
  {
    "path": "engine/client/renderque.c",
    "content": "//this is to render transparent things in a distance oriented order\n\n#include \"quakedef.h\"\n#include \"renderque.h\"\n\n#define NUMGRADUATIONS 0x400\nstatic renderque_t *freerque;\nstatic renderque_t *activerque;\nstatic renderque_t *initialque;\n\nstatic renderque_t *distlastarque[NUMGRADUATIONS];\nstatic renderque_t *distrque[NUMGRADUATIONS];\n\nint rqmaxgrad, rqmingrad;\n\nint rquesize = 0x2000;\nfloat rquepivot;\n\nvoid RQ_BeginFrame(void)\n{\n\trquepivot = DotProduct(r_refdef.vieworg, vpn);\n}\n\nvoid RQ_AddDistReorder(void (*render) (int count, void **objects, void *objtype), void *object, void *objtype, float *pos)\n{\n\tint dist;\n\trenderque_t *rq;\n\tif (!freerque)\n\t{\n\t\trender(1, &object, objtype);\n\t\treturn;\n\t}\n\n\tdist = DotProduct(pos, vpn)-rquepivot;\n\n\tif (dist > rqmaxgrad)\n\t{\n\t\tif (dist >= NUMGRADUATIONS)\n\t\t\tdist = NUMGRADUATIONS-1;\n\t\trqmaxgrad = dist;\n\t}\n\tif (dist < rqmingrad)\n\t{\n\t\tif (dist < 0)\t//hmm... value wrapped? shouldn't happen\n\t\t\treturn;\n//\t\t\tdist = 0;\n\t\trqmingrad = dist;\n\t}\n\n\trq = freerque;\n\tfreerque = freerque->next;\n\trq->next = NULL;\n\tif (distlastarque[dist])\n\t\tdistlastarque[dist]->next = rq;\n\tdistlastarque[dist] = rq;\n\n\trq->render = render;\n\trq->data1 = object;\n\trq->data2 = objtype;\n\n\tif (!distrque[dist])\n\t\tdistrque[dist] = rq;\n}\n/*\nvoid RQ_RenderDistAndClear(void)\n{\n\tint i;\n\trenderque_t *rq;\n\tfor (i = rqmaxgrad; i>=rqmingrad; i--)\n//\tfor (i = rqmingrad; i<=rqmaxgrad; i++)\n\t{\n\t\tfor (rq = distrque[i]; rq; rq=rq->next)\t\n\t\t{\n\t\t\trq->render(1, &rq->data1, rq->data2);\n\t\t}\n\t\tif (distlastarque[i])\n\t\t{\n\t\t\tdistlastarque[i]->next = freerque;\n\t\t\tfreerque = distrque[i];\n\t\t\tdistrque[i] = NULL;\n\t\t\tdistlastarque[i] = NULL;\n\t\t}\n\t}\n\trqmaxgrad=0;\n\trqmingrad = NUMGRADUATIONS-1;\n}\n*/\nvoid RQ_RenderBatchClear(void)\n{\n#define SLOTS 512\n\tvoid *slot[SLOTS];\n\tvoid *typeptr = NULL;\n\tint maxslot = SLOTS;\n\tvoid (*lr) (int count, void **objects, void *objtype) = NULL;\n\tint i;\n\trenderque_t *rq;\n\n\tfor (i = rqmaxgrad; i>=rqmingrad; i--)\n//\tfor (i = rqmingrad; i<=rqmaxgrad; i++)\n\t{\n\t\tfor (rq = distrque[i]; rq; rq=rq->next)\t\n\t\t{\n\t\t\tif (!maxslot || rq->render != lr || typeptr != rq->data2)\n\t\t\t{\n\t\t\t\tif (maxslot != SLOTS)\n\t\t\t\t\tlr(SLOTS - maxslot, &slot[maxslot], typeptr);\n\t\t\t\tmaxslot = SLOTS;\n\t\t\t}\n\t\t\t\n\t\t\tslot[--maxslot] = rq->data1;\n\t\t\ttypeptr = rq->data2;\n\t\t\tlr  = rq->render;\n\t\t}\n\t\tif (distlastarque[i])\n\t\t{\n\t\t\tdistlastarque[i]->next = freerque;\n\t\t\tfreerque = distrque[i];\n\t\t\tdistrque[i] = NULL;\n\t\t\tdistlastarque[i] = NULL;\n\t\t}\n\t}\n\tif (maxslot != SLOTS)\n\t\tlr(SLOTS - maxslot, &slot[maxslot], typeptr);\n\trqmaxgrad=0;\n\trqmingrad = NUMGRADUATIONS-1;\n}\n\n//render without clearing\nvoid RQ_RenderBatch(void)\n{\n#define SLOTS 512\n\tvoid *slot[SLOTS];\n\tvoid *typeptr = NULL;\n\tint maxslot = SLOTS;\n\tvoid (*lr) (int count, void **objects, void *objtype) = NULL;\n\tint i;\n\trenderque_t *rq;\n\n\tfor (i = rqmaxgrad; i>=rqmingrad; i--)\n//\tfor (i = rqmingrad; i<=rqmaxgrad; i++)\n\t{\n\t\tfor (rq = distrque[i]; rq; rq=rq->next)\t\n\t\t{\n\t\t\tif (!maxslot || rq->render != lr || typeptr != rq->data2)\n\t\t\t{\n\t\t\t\tif (maxslot != SLOTS)\n\t\t\t\t\tlr(SLOTS - maxslot, &slot[maxslot], typeptr);\n\t\t\t\tmaxslot = SLOTS;\n\t\t\t}\n\t\t\t\n\t\t\tslot[--maxslot] = rq->data1;\n\t\t\ttypeptr = rq->data2;\n\t\t\tlr  = rq->render;\n\t\t}\n\t}\n\tif (maxslot != SLOTS)\n\t\tlr(SLOTS - maxslot, &slot[maxslot], typeptr);\n}\n\n\nvoid RQ_Shutdown(void)\n{\n\tZ_Free(initialque);\n\tinitialque = NULL;\n\tfreerque = NULL;\n}\n\nvoid RQ_Init(void)\n{\n\tint\t\ti;\n\n\tif (initialque)\n\t\treturn;\n\n\tinitialque = (renderque_t *) Z_Malloc (rquesize * sizeof(renderque_t));\n\n\n\tfreerque = &initialque[0];\n\tactiverque = NULL;\n\n\tfor (i=0 ;i<rquesize-1 ; i++)\n\t\tinitialque[i].next = &initialque[i+1];\n\tinitialque[rquesize-1].next = NULL;\n}\n"
  },
  {
    "path": "engine/client/renderque.h",
    "content": "#ifndef RENDERQUE_H\n#define RENDERQUE_H\n\nvoid RQ_BeginFrame(void);\nvoid RQ_AddDistReorder(void (*render) (int count, void **objects, void *objtype), void *object, void *objtype, float *pos);\n\nvoid RQ_RenderBatchClear(void);\nvoid RQ_RenderBatch(void);\n\ntypedef struct renderque_s\n{\n\tstruct renderque_s *next;\n\tvoid (*render) (int count, void **objects, void *objtype);\n\tvoid *data1;\n\tvoid *data2;\n} renderque_t;\n\n#endif\n"
  },
  {
    "path": "engine/client/resource.h",
    "content": "//{{NO_DEPENDENCIES}}\n// Microsoft Developer Studio generated include file.\n// Used by winquake.rc\n//\n#define IDC_STATIC    -1\n#define IDI_ICON1                       1\n#define IDI_ICON2                       2\n#define IDI_ICON3                       3\n\n// Next default values for new objects\n// \n#ifdef APSTUDIO_INVOKED\n#ifndef APSTUDIO_READONLY_SYMBOLS\n#define _APS_NEXT_RESOURCE_VALUE        123\n#define _APS_NEXT_COMMAND_VALUE         40001\n#define _APS_NEXT_CONTROL_VALUE         1005\n#define _APS_NEXT_SYMED_VALUE           101\n#endif\n#endif\n"
  },
  {
    "path": "engine/client/roq.h",
    "content": "#ifndef _av_roq_h\n#define _av_roq_h\n\n#define RoQ_INFO\t\t\t\t\t0x1001\n#define RoQ_QUAD_CODEBOOK\t\t0x1002\n#define RoQ_QUAD_VQ\t\t\t\t0x1011\n#define RoQ_SOUND_MONO\t\t\t0x1020\n#define RoQ_SOUND_STEREO\t\t0x1021\n\n#define RoQ_ID_MOT\t\t0x00\n#define RoQ_ID_FCC\t\t0x01\n#define RoQ_ID_SLD\t\t0x02\n#define RoQ_ID_CCC\t\t0x03\n\ntypedef struct {\n\tunsigned char y0, y1, y2, y3, u, v;\n} roq_cell;\n\ntypedef struct {\n\tchar p[16];\n} roq_cell_rgba;\n\ntypedef struct {\n\tint idx[4];\n} roq_qcell;\n\ntypedef struct roq_info_s {\n\tvfsfile_t *fp;\n\tqofs_t maxpos;\t//addition for pack files. all seeks add this, all tells subtract this.\n\tint buf_size;\n\tunsigned char *buf;\n\troq_cell cells[256];\n\troq_cell_rgba cells_rgba[256];\n\troq_qcell qcells[256];\n\tshort snd_sqr_arr[256];\n\tqofs_t roq_start, aud_pos, vid_pos;\n\tlong *frame_offset;\n\tunsigned long num_frames, num_audio_bytes;\n\tint width, height, frame_num, audio_channels;\n\tbyte_vec4_t *rgba[2];\n\tlong stream_length;\n\tint audio_buf_size, audio_size;\n\tunsigned char *audio;\n} roq_info;\n\n/* -------------------------------------------------------------------------- */\n//void roq_init(void);\n//void roq_cleanup(void);\nroq_info *roq_open(char *fname);\nvoid roq_rewind(roq_info *ri);\nvoid roq_close(roq_info *ri);\nint roq_read_frame(roq_info *ri);\nint roq_read_audio(roq_info *ri);\n\n#endif\n\n"
  },
  {
    "path": "engine/client/roq_read.c",
    "content": "/* ------------------------------------------------------------------------\n * Id Software's RoQ video file format decoder\n *\n * Dr. Tim Ferguson, 2001.\n * For more details on the algorithm:\n *         http://www.csse.monash.edu.au/~timf/videocodec.html\n *\n * This is a simple decoder for the Id Software RoQ video format.  In\n * this format, audio samples are DPCM coded and the video frames are\n * coded using motion blocks and vector quantisation.\n *\n * Note: All information on the RoQ file format has been obtained through\n *   pure reverse engineering.  This was achieved by giving known input\n *   audio and video frames to the roq.exe encoder and analysing the\n *   resulting output text and RoQ file.  No decompiling of the Quake III\n *   Arena game was required.\n *\n * You may freely use this source code.  I only ask that you reference its\n * source in your projects documentation:\n *       Tim Ferguson: http://www.csse.monash.edu.au/~timf/\n * ------------------------------------------------------------------------ */\n\n#include \"quakedef.h\"\n\n#if defined(HAVE_MEDIA_DECODER) && defined(Q3CLIENT)\n\n\nstatic int VFS_GETC(vfsfile_t *fp)\n{\n\tunsigned char c;\n\tVFS_READ(fp, &c, 1);\n\treturn c;\n}\n\n//#include <stdio.h>\n//#include <stdlib.h>\n//#include <string.h>\n#include \"roq.h\"\n\n//#define DBUG\t1\n\n#define FAST\n\n/* -------------------------------------------------------------------------- */\nstatic unsigned int get_word(vfsfile_t *fp)\n{\nunsigned int ret;\n\n\tret  = ((VFS_GETC(fp)) & 0xff);\n\tret |= ((VFS_GETC(fp)) & 0xff) << 8;\n\treturn(ret);\n}\n\n\n/* -------------------------------------------------------------------------- */\nstatic unsigned long get_long(vfsfile_t *fp)\n{\nunsigned long ret;\n\n\tret  = ((VFS_GETC(fp)) & 0xff);\n\tret |= ((VFS_GETC(fp)) & 0xff) << 8;\n\tret |= ((VFS_GETC(fp)) & 0xff) << 16;\n\tret |= ((VFS_GETC(fp)) & 0xff) << 24;\n\treturn(ret);\n}\n\n\n/* -------------------------------------------------------------------------- */\nstatic int roq_parse_file(vfsfile_t *fp, roq_info *ri)\n{\nunsigned int head1, head3, chunk_id;//, chunk_arg;\nlong head2, chunk_size;\nqofs_t fpos;\n#ifndef FAST\nint max_frame;\n#endif\n\n#ifndef FAST\n\tri->num_audio_bytes = ri->num_frames = max_frame = 0;\n\tri->audio_channels = 0;\n\tri->frame_offset = NULL;\n#endif\n\tri->buf_size = 0;\n\thead1 = get_word(fp);\n\thead2 = get_long(fp);\n\thead3 = get_word(fp);\n\tif(head1 != 0x1084 && head2 != 0xffffffff && head3 != 0x1e)\n\t{\n\t\treturn 1;\n\t}\n\n\tri->roq_start = fpos = VFS_TELL(fp);\n\twhile(fpos+8 <= ri->maxpos)\n\t{\n#if DBUG > 20\n\t\tCon_Printf(\"---------------------------------------------------------------------------\\n\");\n#endif\n\t\tVFS_SEEK(fp, fpos);\n\n\t\tchunk_id = get_word(fp);\n\t\tchunk_size = get_long(fp);\n\t\t/*chunk_arg =*/ get_word(fp);\n\t\tfpos += 8 + chunk_size;\n\t\tif (chunk_size == 0xffffffff || fpos > ri->maxpos)\t//FIXME: THIS SHOULD NOT HAPPEN\n\t\t\tbreak;\n\t\tif (chunk_size > ri->buf_size)\n\t\t\tri->buf_size = chunk_size;\n#if DBUG > 20\n\t\tCon_Printf(\"%03d  0x%06lx: chunk: 0x%02x size: %ld  cells: 2x2=%d,4x4=%d\\n\", i,\n\t\t\tfpos, chunk_id, chunk_size, v1>>8,v1&0xff);\n#endif\n\n\t\tif(chunk_id == RoQ_INFO)\t\t/* video info */\n\t\t{\n\t\t\tri->width = get_word(fp);\n\t\t\tri->height = get_word(fp);\n\t\t\tget_word(fp);\n\t\t\tget_word(fp);\n#ifdef FAST\n\t\t\treturn 0;\t//we have all the data we need now. We always find a sound chunk first, or none at all.\n#endif\n\t\t}\n#ifndef FAST\n\t\telse if(chunk_id == RoQ_QUAD_VQ)\n\t\t{\n\t\t\tri->num_frames++;\n\t\t\tif(ri->num_frames > max_frame)\n\t\t\t{\n\t\t\t\tmax_frame += 5000;\n\t\t\t\t\tif((ri->frame_offset = BZ_Realloc(ri->frame_offset, sizeof(long) * max_frame)) == NULL)\n\t\t\t\t\t\treturn 1;\n\t\t\t\t}\n\t\t\t\tri->frame_offset[ri->num_frames] = fpos;\n\t\t\t}\n\t\t}\n#endif\n\t\telse if(chunk_id == RoQ_SOUND_MONO || chunk_id == RoQ_SOUND_STEREO)\n\t\t{\n\t\t\tif(chunk_id == RoQ_SOUND_MONO)\n\t\t\t\tri->audio_channels = 1;\n\t\t\telse\n\t\t\t\tri->audio_channels = 2;\n#ifndef FAST\n\t\t\tri->num_audio_bytes += chunk_size;\n#endif\n\t\t}\n\t}\n\n\treturn 0;\n}\n\n/* -------------------------------------------------------------------------- */\nstatic void apply_vector_2x2(roq_info *ri, int x, int y, roq_cell_rgba *cell)\n{\n\t// place 2x2 vector codeword in framebuffer\n\n\tint idxa = (y * ri->width) + x;\n\tint idxb = 0;\n\n\tint *ptra = (int*) ri->rgba[0][idxa];\n\tint *ptrb = (int*) &cell->p[idxb];\n\n\tptra[0] = ptrb[0];\n\tptra[1] = ptrb[1];\n\tptra += ri->width;\n\tptra[0] = ptrb[2];\n\tptra[1] = ptrb[3];\n}\n\n/* -------------------------------------------------------------------------- */\nstatic void apply_vector_4x4(roq_info *ri, int x, int y, roq_cell_rgba *cell)\n{\n\t// upsample 2x2 vector codeword to 4x4 and place in framebuffer\n\n\tint idxa = (y * ri->width) + x;\n\tint idxb = 0;\n\n\tint *ptra = (int*) ri->rgba[0][idxa];\n\tint *ptrb = (int*) &cell->p[idxb];\n\n\tint i;\n\tfor(i = 0; i < 4; i++) {\n\t\tptra[0] = ptrb[0];\n\t\tptra[1] = ptrb[0];\n\t\tptra[2] = ptrb[1];\n\t\tptra[3] = ptrb[1];\n\n\t\tptra += ri->width;\n\t\tif(i & 0x1) { // increase src pointer only every second dest line\n\t\t\tptrb += 2;\n\t\t}\n\t}\n}\n\n\n/* -------------------------------------------------------------------------- */\nstatic void apply_motion_4x4(roq_info *ri, int x, int y, unsigned char mv, char mean_x, char mean_y)\n{\n\tint mx = x + 8 - (mv >> 4) - mean_x;\n\tint my = y + 8 - (mv & 0xf) - mean_y;\n\t\n\tint idxa = (y * ri->width) + x;\n\tint idxb = (my * ri->width) + mx;\n\n\tint *ptra = (int*) ri->rgba[0][idxa];\n\tint *ptrb = (int*) ri->rgba[1][idxb];\n\t\n\tint i;\n\tfor(i = 0; i < 4; i++) {\n\t\tptra[0] = ptrb[0];\n\t\tptra[1] = ptrb[1];\n\t\tptra[2] = ptrb[2];\n\t\tptra[3] = ptrb[3];\n\n\t\tptra += ri->width;\n\t\tptrb += ri->width;\n\t}\n}\n\n\n/* -------------------------------------------------------------------------- */\nstatic void apply_motion_8x8(roq_info *ri, int x, int y, unsigned char mv, char mean_x, char mean_y)\n{\n\tint mx = x + 8 - (mv >> 4) - mean_x;\n\tint my = y + 8 - (mv & 0xf) - mean_y;\n\n\tint idxa = (y * ri->width) + x;\n\tint idxb = (my * ri->width) + mx;\n\t\n\tint *ptra = (int*) ri->rgba[0][idxa];\n\tint *ptrb = (int*) ri->rgba[1][idxb];\n\n\tint i;\n\tfor(i = 0; i < 8; i++) {\n\t\tptra[0] = ptrb[0];\n\t\tptra[1] = ptrb[1];\n\t\tptra[2] = ptrb[2];\n\t\tptra[3] = ptrb[3];\n\t\tptra[4] = ptrb[4];\n\t\tptra[5] = ptrb[5];\n\t\tptra[6] = ptrb[6];\n\t\tptra[7] = ptrb[7];\n\n\t\tptra += ri->width;\n\t\tptrb += ri->width;\n\t}\n}\n\n\n/* -------------------------------------------------------------------------- */\nroq_info *roq_open(char *fname)\n{\nvfsfile_t *fp;\nroq_info *ri;\nint i;\n\n\tif((fp = FS_OpenVFS(fname, \"rb\", FS_GAME)) == NULL)\n\t{\n\t\tif((fp = FS_OpenVFS(va(\"video/%s\", fname), \"rb\", FS_GAME)) == NULL)\t//for q3 compat\n\t\t\treturn NULL;\n\t}\n\n\tif((ri = BZF_Malloc(sizeof(roq_info))) == NULL)\n\t{\n\t\tCon_Printf(\"Error allocating memory.\\n\");\n\t\treturn NULL;\n\t}\n\n\tmemset(ri, 0, sizeof(roq_info));\n\n\tri->maxpos = VFS_TELL(fp)+VFS_GETLEN(fp);//no adds/subracts for fileoffset here\n\n\tri->fp = fp;\n\tif(roq_parse_file(fp, ri))\n\t\treturn NULL;\n#ifndef FAST\n\tri->stream_length = (ri->num_frames * 1000)/30;\n#endif\n\tfor(i = 0; i < 128; i++)\n\t{\n\t\tri->snd_sqr_arr[i] = i * i;\n\t\tri->snd_sqr_arr[i + 128] = -(i * i);\n\t}\n\n\tfor(i = 0; i < 2; i++)\n\t{\n\t\tif((ri->rgba[i] = BZF_Malloc(ri->width * ri->height * sizeof(byte_vec4_t))) == NULL)\n\t\t{\n\t\t\tCon_Printf(\"Memory allocation error.\\n\");\n\t\t\treturn NULL;\n\t\t}\n\t}\n\n\tri->buf_size *= 2;\n\tif((ri->buf = BZF_Malloc(ri->buf_size)) == NULL)\n\t{\n\t\tCon_Printf(\"Memory allocation error.\\n\");\n\t\treturn NULL;\n\t}\n\tri->audio_buf_size = 0;\n\tri->audio = NULL;\n\n\tri->frame_num = 0;\n\tri->aud_pos = ri->vid_pos = ri->roq_start;\n\n\treturn ri;\n}\n\n//reset enough that we'll start decoding from the start next time we try to read a frame.\nvoid roq_rewind(roq_info *ri)\n{\n\tri->frame_num = 0;\n\tri->aud_pos = ri->vid_pos = ri->roq_start;\n}\n\n/* -------------------------------------------------------------------------- */\nvoid roq_close(roq_info *ri)\n{\nint i;\n\n\tif(ri == NULL)\n\t\treturn;\n\tVFS_CLOSE(ri->fp);\n\tfor(i = 0; i < 2; i++)\n\t{\n\t\tif(ri->rgba[i] != NULL)\n\t\t\tBZ_Free(ri->rgba[i]);\n\t}\n\tif(ri->buf != NULL)\n\t\tBZ_Free(ri->buf);\n\tif (ri->audio)\n\t\tBZ_Free(ri->audio);\n\tBZ_Free(ri);\n}\n\n/* -------------------------------------------------------------------------- */\n#define LIMIT(x) ((((x) > 0xffffff) ? 0xff0000 : (((x) <= 0xffff) ? 0 : (x) & 0xff0000)) >> 16)\nvoid roq_cells_to_rgba(roq_info *ri)\n{\n\tunsigned char *pptr;\n\tint i, r, g, b, y, u, v, t;\n\tfor(i = 0; i < 256; i++) {\n\t\tpptr = ri->cells_rgba[i].p;\n\n\t\tu = ri->cells[i].u - 128;\n\t\tv = ri->cells[i].v - 128;\n\n\t\tr = 91881 * v;\n\t\tg = -22554 * u + -46802 * v;\n\t\tb = 116130 * u;\n\n\t\t// first pixel\n\t\ty = (ri->cells[i].y0) << 16;\n\t\tt = r + y;\n\t\tpptr[0] = LIMIT(t); // R\n\t\tt = g + y;\n\t\tpptr[1] = LIMIT(t); // G\n\t\tt = b + y;\n\t\tpptr[2] = LIMIT(t); // B\n\t\tpptr[3] = 255;      // A\n\n\t\t// second pixel\n\t\ty = (ri->cells[i].y1) << 16;\n\t\tt = r + y;\n\t\tpptr[4] = LIMIT(t); // R\n\t\tt = g + y;\n\t\tpptr[5] = LIMIT(t); // G\n\t\tt = b + y;\n\t\tpptr[6] = LIMIT(t); // B\n\t\tpptr[7] = 255;      // A\n\n\t\t// third pixel\n\t\ty = (ri->cells[i].y2) << 16;\n\t\tt = r + y;\n\t\tpptr[8] = LIMIT(t); // R\n\t\tt = g + y;\n\t\tpptr[9] = LIMIT(t); // G\n\t\tt = b + y;\n\t\tpptr[10] = LIMIT(t); // B\n\t\tpptr[11] = 255;      // A\n\n\t\t// fourth pixel\n\t\ty = (ri->cells[i].y3) << 16;\n\t\tt = r + y;\n\t\tpptr[12] = LIMIT(t); // R\n\t\tt = g + y;\n\t\tpptr[13] = LIMIT(t); // G\n\t\tt = b + y;\n\t\tpptr[14] = LIMIT(t); // B\n\t\tpptr[15] = 255;      // A\n\t}\n}\n\n\n/* -------------------------------------------------------------------------- */\nint roq_read_frame(roq_info *ri)\n{\nvfsfile_t *fp = ri->fp;\nunsigned int chunk_id = 0, chunk_arg = 0;\nunsigned long chunk_size = 0;\nint i, j, k, nv1, nv2, vqflg = 0, vqflg_pos = -1, vqid, bpos, xpos, ypos, xp, yp, x, y;\nunsigned char *buf;\nint frame_stats[2][4] = {{0},{0}};\nroq_qcell *qcell;\n\nqofs_t fpos = ri->vid_pos;\n\n\tVFS_SEEK(fp, fpos);\n\twhile(fpos+8 < ri->maxpos)\n\t{\n\t\tchunk_id = get_word(fp);\n\t\tchunk_size = get_long(fp);\n\t\tchunk_arg = get_word(fp);\n \t\tfpos += 8+chunk_size;\n\t\tif (chunk_size == 0xffffffff || fpos > ri->maxpos)\n\t\t\treturn -1;\n\t\tif (chunk_id == RoQ_QUAD_VQ)\n\t\t\tbreak;\n\t\tif(chunk_id == RoQ_QUAD_CODEBOOK)\n\t\t{\n\t\t\tif((nv1 = chunk_arg >> 8) == 0)\n\t\t\t\tnv1 = 256;\n\t\t\tif((nv2 = chunk_arg & 0xff) == 0 && nv1 * 6 < chunk_size)\n\t\t\t\tnv2 = 256;\n\t\t\tVFS_READ(fp, ri->cells, nv1 * sizeof(roq_cell));\n\t\t\troq_cells_to_rgba(ri);\n\t\t\tfor(i = 0; i < nv2; i++)\n\t\t\t\tfor(j = 0; j < 4; j++) ri->qcells[i].idx[j] = VFS_GETC(fp);\n\t\t}\n\t\telse\n\t\t\tVFS_SEEK(fp, fpos);\n\t}\n\n\tif(chunk_id != RoQ_QUAD_VQ)\n\t{\n\t\tri->vid_pos = fpos;\n\t\treturn 0;\n\t}\n\n\tri->frame_num++;\n\tif(ri->buf_size < chunk_size)\n\t{\n\t\tri->buf_size *= 2;\n\t\tif (ri->buf_size < chunk_size)\t//double wasn't enough\n\t\t\tri->buf_size = chunk_size;\n\t\tBZ_Free(ri->buf);\n\t\tif((ri->buf = BZ_Malloc(ri->buf_size)) == NULL)\n\t\t{\n\t\t\tCon_Printf(\"Memory allocation error.\\n\");\n\t\t\treturn -1;\n\t\t}\n\t}\n\tVFS_READ(fp, ri->buf, chunk_size);\n\tbuf = ri->buf;\n\n\tbpos = xpos = ypos = 0;\n\twhile(bpos < chunk_size)\n\t{\n\t\tfor(yp = ypos; yp < ypos + 16; yp += 8)\n\t\t\tfor(xp = xpos; xp < xpos + 16; xp += 8)\n\t\t\t{\n\t\t\t\tif(vqflg_pos < 0)\n\t\t\t\t{\n\t\t\t\t\tvqflg = buf[bpos++]; vqflg |= (buf[bpos++] << 8);\n\t\t\t\t\tvqflg_pos = 7;\n\t\t\t\t}\n\t\t\t\tvqid = (vqflg >> (vqflg_pos * 2)) & 0x3;\n\t\t\t\tframe_stats[0][vqid]++;\n\t\t\t\tvqflg_pos--;\n\n\t\t\t\tswitch(vqid)\n\t\t\t\t{\n\t\t\t\t\tcase RoQ_ID_MOT: break;\n\t\t\t\t\tcase RoQ_ID_FCC:\n\t\t\t\t\t\tapply_motion_8x8(ri, xp, yp, buf[bpos], (char)(chunk_arg >> 8), (char)(chunk_arg & 0xff));\n\t\t\t\t\t\tbpos++;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase RoQ_ID_SLD:\n\t\t\t\t\t\tqcell = ri->qcells + buf[bpos++];\n\t\t\t\t\t\tapply_vector_4x4(ri, xp, yp, ri->cells_rgba + qcell->idx[0]);\n\t\t\t\t\t\tapply_vector_4x4(ri, xp+4, yp, ri->cells_rgba + qcell->idx[1]);\n\t\t\t\t\t\tapply_vector_4x4(ri, xp, yp+4, ri->cells_rgba + qcell->idx[2]);\n\t\t\t\t\t\tapply_vector_4x4(ri, xp+4, yp+4, ri->cells_rgba + qcell->idx[3]);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase RoQ_ID_CCC:\n\t\t\t\t\t\tfor(k = 0; k < 4; k++)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tx = xp; y = yp;\n\t\t\t\t\t\t\tif(k & 0x01) x += 4;\n\t\t\t\t\t\t\tif(k & 0x02) y += 4;\n\n\t\t\t\t\t\t\tif(vqflg_pos < 0)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tvqflg = buf[bpos++]; vqflg |= (buf[bpos++] << 8);\n\t\t\t\t\t\t\t\tvqflg_pos = 7;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tvqid = (vqflg >> (vqflg_pos * 2)) & 0x3;\n\t\t\t\t\t\t\tframe_stats[1][vqid]++;\n\t\t\t\t\t\t\tvqflg_pos--;\n\t\t\t\t\t\t\tswitch(vqid)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tcase RoQ_ID_MOT: break;\n\t\t\t\t\t\t\t\tcase RoQ_ID_FCC:\n\t\t\t\t\t\t\t\t\tapply_motion_4x4(ri, x, y, buf[bpos], (char)(chunk_arg >> 8), (char)(chunk_arg & 0xff));\n\t\t\t\t\t\t\t\t\tbpos++;\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\tcase RoQ_ID_SLD:\n\t\t\t\t\t\t\t\t\tqcell = ri->qcells + buf[bpos++];\n\t\t\t\t\t\t\t\t\tapply_vector_2x2(ri, x, y, ri->cells_rgba + qcell->idx[0]);\n\t\t\t\t\t\t\t\t\tapply_vector_2x2(ri, x+2, y, ri->cells_rgba + qcell->idx[1]);\n\t\t\t\t\t\t\t\t\tapply_vector_2x2(ri, x, y+2, ri->cells_rgba + qcell->idx[2]);\n\t\t\t\t\t\t\t\t\tapply_vector_2x2(ri, x+2, y+2, ri->cells_rgba + qcell->idx[3]);\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\tcase RoQ_ID_CCC:\n\t\t\t\t\t\t\t\t\tapply_vector_2x2(ri, x, y, ri->cells_rgba + buf[bpos]);\n\t\t\t\t\t\t\t\t\tapply_vector_2x2(ri, x+2, y, ri->cells_rgba + buf[bpos+1]);\n\t\t\t\t\t\t\t\t\tapply_vector_2x2(ri, x, y+2, ri->cells_rgba + buf[bpos+2]);\n\t\t\t\t\t\t\t\t\tapply_vector_2x2(ri, x+2, y+2, ri->cells_rgba + buf[bpos+3]);\n\t\t\t\t\t\t\t\t\tbpos += 4;\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tCon_Printf(\"Unknown vq code: %d\\n\", vqid);\n\t\t\t\t}\n\t\t\t}\n\n\t\txpos += 16;\n\t\tif(xpos >= ri->width)\n\t\t{\n\t\t\txpos -= ri->width;\n\t\t\typos += 16;\n\t\t}\n\t\tif(ypos >= ri->height) break;\n\t}\n\n#if 0\n\tframe_stats[0][3] = 0;\n\tCon_Printf(\"<%d  0x%04x -> %d,%d>\\n\", ri->frame_num, chunk_arg, (char)(chunk_arg >> 8), (char)(chunk_arg & 0xff));\n\tCon_Printf(\"for 08x08 CCC = %d, FCC = %d, MOT = %d, SLD = %d, PAT = 0\\n\", frame_stats[0][3], frame_stats[0][1], frame_stats[0][0], frame_stats[0][2]);\n\tCon_Printf(\"for 04x04 CCC = %d, FCC = %d, MOT = %d, SLD = %d, PAT = 0\\n\", frame_stats[1][3], frame_stats[1][1], frame_stats[1][0], frame_stats[1][2]);\n#endif\n\n\tri->vid_pos = fpos;\n\n\tif(ri->frame_num == 1)\n\t{\n\t\tmemcpy(ri->rgba[1], ri->rgba[0], ri->width * ri->height * sizeof(byte_vec4_t));\n\t}\n\telse\n\t{\n\t\tbyte_vec4_t *tp = ri->rgba[0];\n\t\tri->rgba[0] = ri->rgba[1];\n\t\tri->rgba[1] = tp;\n\t}\n\n\treturn 1;\n}\n\n\n/* -------------------------------------------------------------------------- */\nint roq_read_audio(roq_info *ri)\n{\nvfsfile_t *fp = ri->fp;\nunsigned int chunk_id = 0, chunk_arg = 0;\nunsigned long chunk_size = 0;\nint i, snd_left, snd_right;\n\nlong fpos;\n\n\tfpos = ri->aud_pos;\n\n\tri->audio_size = 0;\n\n\tfor(;;)\n\t{\n\t\tVFS_SEEK(fp, fpos);\n\t\tif(fpos >= ri->maxpos)\n\t\t\treturn -1;\n\t\tchunk_id = get_word(fp);\n\t\tchunk_size = get_long(fp);\n\t\tchunk_arg = get_word(fp);\n\t\tfpos += 8+chunk_size;\n\t\tif (chunk_size == 0xffffffff || fpos > ri->maxpos)\n\t\t\treturn -1;\n\t\tif (chunk_id == RoQ_SOUND_MONO || chunk_id == RoQ_SOUND_STEREO)\n\t\t\tbreak;\n\t}\n\n\tif(ri->audio_buf_size < chunk_size*2)\n\t{\n\t\tif(ri->audio != NULL) BZ_Free(ri->audio);\n\t\tri->audio=NULL;\n\t\tri->audio_buf_size = chunk_size * 3;\n\t\tif (ri->audio_buf_size <= 0)\n\t\t\treturn -1;\n\t\tif((ri->audio = BZ_Malloc(ri->audio_buf_size)) == NULL) return -1;\n\t}\n\tif (ri->audio_buf_size < 0)\n\t\treturn -1;\n\n\tif(chunk_id == RoQ_SOUND_MONO)\n\t{\n\t\tri->audio_size = chunk_size;\n\t\tsnd_left = chunk_arg;\n\t\tfor(i = 0; i < chunk_size; i++)\n\t\t{\n\t\t\tsnd_left += (int)ri->snd_sqr_arr[(unsigned)VFS_GETC(fp)];\n\t\t\t*(short *)&ri->audio[i * 2] = snd_left;\n\t\t}\n\t\tri->aud_pos = fpos;\n\t\treturn chunk_size;\n\t}\n\n\tif(chunk_id == RoQ_SOUND_STEREO)\n\t{\n\t\tri->audio_size = chunk_size;\n\t\tsnd_left = (chunk_arg & 0xFF00);\n\t\tsnd_right = (chunk_arg & 0xFF) << 8;\n\t\tfor(i = 0; i < chunk_size; i += 2)\n\t\t{\n\t\t\tsnd_left += (int)ri->snd_sqr_arr[(unsigned)VFS_GETC(fp)];\n\t\t\tsnd_right += (int)ri->snd_sqr_arr[(unsigned)VFS_GETC(fp)];\n\t\t\t*(short *)&ri->audio[i * 2] = snd_left & 0xffff;\n\t\t\t*(short *)&ri->audio[i * 2 + 2] = snd_right & 0xffff;\n\t\t}\n\t\tri->aud_pos = fpos;\n\t\treturn chunk_size;\n\t}\n\n\tri->aud_pos = fpos;\n\treturn 0;\n}\n\n#endif\n"
  },
  {
    "path": "engine/client/sbar.c",
    "content": "/*\nCopyright (C) 1996-1997 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n*/\n// sbar.c -- status bar code\n\n#include \"quakedef.h\"\n#include \"shader.h\"\n\n#define CON_ALTMASK (CON_2NDCHARSETTEXT|CON_WHITEMASK)\n\n#ifdef QUAKEHUD\n\nextern cvar_t *hud_tracking_show;\nextern cvar_t *hud_miniscores_show;\n\nstatic cvar_t scr_scoreboard_drawtitle = CVARD(\"scr_scoreboard_drawtitle\", \"1\", \"Wastes screen space when looking at the scoreboard.\");\nstatic cvar_t scr_scoreboard_forcecolors = CVARD(\"scr_scoreboard_forcecolors\", \"0\", \"Makes the scoreboard colours obey enemycolor/teamcolor rules.\");\t//damn americans\nstatic cvar_t scr_scoreboard_newstyle = CVARD(\"scr_scoreboard_newstyle\", \"1\", \"Display team colours and stuff in a style popularised by Electro. Looks more modern, but might not quite fit classic huds.\");\t// New scoreboard style ported from Electro, by Molgrum\nstatic cvar_t scr_scoreboard_showfrags = CVARD(\"scr_scoreboard_showfrags\", \"0\", \"Display kills+deaths+teamkills, as determined by fragfile.dat-based conprint parsing. These may be inaccurate if you join mid-game.\");\n#ifdef QUAKESTATS\nstatic cvar_t scr_scoreboard_showlocation = CVARD(\"scr_scoreboard_showlocation\", \"1\", \"Display player location names when playing mvd/qtv streams, if available.\");\nstatic cvar_t scr_scoreboard_showhealth = CVARD(\"scr_scoreboard_showhealth\", \"3\", \"Display health information when playing mvd/qtv streams.\\n0: off\\n1: on\\n2: show armour too. 3: combined health ('+' says more armour than health allows).\");\nstatic cvar_t scr_scoreboard_showweapon = CVARD(\"scr_scoreboard_showweapon\", \"1\", \"Display weapon information when playing mvd/qtv streams.\");\n#endif\nstatic cvar_t scr_scoreboard_showflags = CVARD(\"scr_scoreboard_showflags\", \"2\", \"Display flag caps+touches on the scoreboard, where our fragfile.dat supports them.\\n0: off\\n1: on\\n2: on only if someone appears to have interacted with a flag.\");\nstatic cvar_t scr_scoreboard_fillalpha = CVARD(\"scr_scoreboard_fillalpha\", \"0.7\", \"Transparency amount for newstyle scoreboard.\");\nstatic cvar_t scr_scoreboard_backgroundalpha = CVARD(\"scr_scoreboard_backgroundalpha\", \"0.5\", \"Further multiplier for the background alphas.\");\nstatic cvar_t scr_scoreboard_teamscores = CVARD(\"scr_scoreboard_teamscores\", \"1\", \"Makes +showscores act as +showteamscores. Because reasons.\");\nstatic cvar_t scr_scoreboard_teamsort = CVARD(\"scr_scoreboard_teamsort\", \"0\", \"On the scoreboard, sort players by their team BEFORE their personal score.\");\nstatic cvar_t scr_scoreboard_titleseperator = CVAR(\"scr_scoreboard_titleseperator\", \"1\");\nstatic cvar_t scr_scoreboard_showruleset = CVAR(\"scr_scoreboard_showruleset\", \"1\");\nstatic cvar_t scr_scoreboard_afk = CVARD(\"scr_scoreboard_afk\", \"1\", \"Show 'afk' in the packetloss column when they're afk.\");\nstatic cvar_t scr_scoreboard_ping_status = CVARD(\"scr_scoreboard_ping_status\", \"25 50 100 150\", \"Threshholds required to switch ping display from green to white, yellow, megenta and red.\");\nstatic cvar_t sbar_teamstatus = CVARD(\"sbar_teamstatus\", \"1\", \"Display the last team say from each of your team members just above the sbar area.\");\n\nstatic cvar_t cl_sbaralpha = CVARAFD(\"cl_sbaralpha\", \"0.75\", \"scr_sbaralpha\", CVAR_ARCHIVE, \"Specifies the transparency of the status bar. Only Takes effect when cl_sbar is set to 2.\");\t//with premultiplied alpha, this needs to affect the RGB values too.\n\n//===========================================\n//rogue changed and added defines\n#define RIT_SHELLS              (1u<<7)\n#define RIT_NAILS               (1u<<8)\n#define RIT_ROCKETS             (1u<<9)\n#define RIT_CELLS               (1u<<10)\n#define RIT_AXE                 (1u<<11)\n#define RIT_LAVA_NAILGUN        (1u<<12)\n#define RIT_LAVA_SUPER_NAILGUN  (1u<<13)\n#define RIT_MULTI_GRENADE       (1u<<14)\n#define RIT_MULTI_ROCKET        (1u<<15)\n#define RIT_PLASMA_GUN          (1u<<16)\n#define RIT_ARMOR1              (1u<<23)\n#define RIT_ARMOR2              (1u<<24)\n#define RIT_ARMOR3              (1u<<25)\n#define RIT_LAVA_NAILS          (1u<<26)\n#define RIT_PLASMA_AMMO         (1u<<27)\n#define RIT_MULTI_ROCKETS       (1u<<28)\n#define RIT_SHIELD              (1u<<29)\n#define RIT_ANTIGRAV            (1u<<30)\n#define RIT_SUPERHEALTH         (1u<<31)\n\n//===========================================\n//hipnotic added defines\n\n#define HIT_PROXIMITY_GUN_BIT 16\n#define HIT_MJOLNIR_BIT       7\n#define HIT_LASER_CANNON_BIT  23\n#define HIT_PROXIMITY_GUN   (1<<HIT_PROXIMITY_GUN_BIT)\n#define HIT_MJOLNIR         (1<<HIT_MJOLNIR_BIT)\n#define HIT_LASER_CANNON    (1<<HIT_LASER_CANNON_BIT)\n#define HIT_WETSUIT         (1<<(23+2))\n#define HIT_EMPATHY_SHIELDS (1<<(23+3))\n\n\n\n\n\nint\t\t\tsb_updates;\t\t// if >= vid.numpages, no update needed\n\nqboolean\tsbar_parsingteamstatuses;\t//so we don't eat it if its not displayed\n\n#define STAT_MINUS\t\t10\t// num frame for '-' stats digit\nstatic apic_t\t\t*sb_nums[2][11];\nstatic apic_t\t\t*sb_colon, *sb_slash;\nstatic apic_t\t\t*sb_ibar;\nstatic apic_t\t\t*sb_sbar;\nstatic apic_t\t\t*sb_scorebar;\n\n       apic_t\t\t*sb_weapons[7][8];\t// 0 is active, 1 is owned, 2-5 are flashes\nstatic apic_t\t\t*sb_ammo[4];\nstatic apic_t\t\t*sb_sigil[4];\nstatic apic_t\t\t*sb_armor[3];\nstatic apic_t\t\t*sb_items[32];\n\nstatic apic_t\t*sb_faces[7][2];\t\t// 0 is gibbed, 1 is dead, 2-6 are alive\n\t\t\t\t\t\t\t// 0 is static, 1 is temporary animation\nstatic apic_t\t*sb_face_invis;\nstatic apic_t\t*sb_face_quad;\nstatic apic_t\t*sb_face_invuln;\nstatic apic_t\t*sb_face_invis_invuln;\n\n//rogue pictures.\nstatic qboolean\tsbar_rogue;\nstatic apic_t      *rsb_invbar[2];\nstatic apic_t      *rsb_weapons[5];\nstatic apic_t      *rsb_items[2];\nstatic apic_t      *rsb_ammo[3];\nstatic apic_t      *rsb_teambord;\n//all must be found for any to be used.\n\n//hipnotic pictures and stuff\nstatic qboolean\tsbar_hipnotic;\nstatic apic_t      *hsb_weapons[7][5];   // 0 is active, 1 is owned, 2-5 are flashes\nstatic int         hipweapons[4] = {HIT_LASER_CANNON_BIT,HIT_MJOLNIR_BIT,4,HIT_PROXIMITY_GUN_BIT};\nstatic apic_t      *hsb_items[2];\n//end hipnotic\n\nstatic qboolean\tsbarfailed;\n#ifdef HEXEN2\nqboolean\tsbar_hexen2;\nstatic const char *puzzlenames;\n#endif\n#ifdef NQPROT\nstatic void Sbar_CTFScores_f(void);\n#endif\n\nvrect_t\t\tsbar_rect;\t//screen area that the sbar must fit.\nfloat\t\tsbar_rect_left;\n\nint\t\t\tsb_lines;\t\t\t// scan lines to draw\n\nvoid Sbar_DeathmatchOverlay (playerview_t *pv, int start);\nvoid Sbar_TeamOverlay (playerview_t *pv);\nstatic void Sbar_MiniDeathmatchOverlay (playerview_t *pv);\nvoid Sbar_ChatModeOverlay(playerview_t *pv);\n\nstatic int Sbar_PlayerNum(playerview_t *pv)\n{\n\tint num;\n\tnum = pv->spectator?Cam_TrackNum(pv):-1;\n\tif (num < 0)\n\t\tnum = pv->playernum;\n\treturn num;\n}\n\nstatic int Sbar_TopColour(player_info_t *p)\n{\n\tif (cl.teamfortress)\n\t{\n\t\tif (!Q_strcasecmp(p->team, \"red\"))\n\t\t\treturn 4;\n\t\tif (!Q_strcasecmp(p->team, \"blue\"))\n\t\t\treturn 13;\n\t}\n\tif (scr_scoreboard_forcecolors.ival)\n\t\treturn p->ttopcolor;\n\telse\n\t\treturn p->rtopcolor;\n}\n\nstatic int Sbar_BottomColour(player_info_t *p)\n{\n\tif (cl.teamfortress)\n\t{\n\t\tif (!Q_strcasecmp(p->team, \"red\"))\n\t\t\treturn 4;\n\t\tif (!Q_strcasecmp(p->team, \"blue\"))\n\t\t\treturn 13;\n\t}\n\tif (scr_scoreboard_forcecolors.ival)\n\t\treturn p->tbottomcolor;\n\telse\n\t\treturn p->rbottomcolor;\n}\n\n#endif\n//Draws a pre-marked-up string with no width limit. doesn't support new lines\nvoid Draw_ExpandedString(struct font_s *font, float x, float y, conchar_t *str)\n{\n\tint px, py;\n\tunsigned int codeflags, codepoint;\n\tFont_BeginString(font, x, y, &px, &py);\n\twhile(*str)\n\t{\n\t\tstr = Font_Decode(str, &codeflags, &codepoint);\n\t\tpx = Font_DrawChar(px, py, codeflags, codepoint);\n\t}\n\tFont_EndString(font);\n}\n\n//Draws a marked-up string using the regular char set with no width limit. doesn't support new lines\nvoid Draw_FunString(float x, float y, const void *str)\n{\n\tconchar_t buffer[2048];\n\tCOM_ParseFunString(CON_WHITEMASK, str, buffer, sizeof(buffer), false);\n\n\tDraw_ExpandedString(font_default, x, y, buffer);\n}\nvoid Draw_FunStringU8(unsigned int flags, float x, float y, const void *str)\n{\n\tconchar_t buffer[2048];\n\tCOM_ParseFunString(flags, str, buffer, sizeof(buffer), PFS_FORCEUTF8);\n\n\tDraw_ExpandedString(font_default, x, y, buffer);\n}\n//Draws a marked up string using the alt char set (legacy mode would be |128)\nvoid Draw_AltFunString(float x, float y, const void *str)\n{\n\tconchar_t buffer[2048];\n\tCOM_ParseFunString(CON_ALTMASK, str, buffer, sizeof(buffer), false);\n\n\tDraw_ExpandedString(font_default, x, y, buffer);\n}\n\n//Draws a marked up string no wider than $width virtual pixels.\nvoid Draw_FunStringWidthFont(struct font_s *font, float x, float y, const void *str, int width, int rightalign, qboolean highlight)\n{\n\tconchar_t buffer[2048];\n\tconchar_t *w;\n\tint px, py;\n\tint fw = 0;\n\tunsigned int codeflags, codepoint;\n\n\t//be generous and round up, to avoid too many issues with truncations\n\twidth = ceil((width*(float)vid.rotpixelwidth)/vid.width);\n\n\tif (highlight&4)\n\t\tcodeflags = COLOR_GREY<<CON_FGSHIFT;\n\telse\n\t\tcodeflags = (highlight&1)?CON_ALTMASK:CON_WHITEMASK;\n\tif (highlight&2)\n\t\tcodeflags |= CON_BLINKTEXT;\n\tCOM_ParseFunString(codeflags, str, buffer, sizeof(buffer), false);\n\n\tFont_BeginString(font, x, y, &px, &py);\n\tif (rightalign)\n\t{\n\t\tfor (w = buffer; *w; )\n\t\t{\n\t\t\tw = Font_Decode(w, &codeflags, &codepoint);\n\t\t\tfw += Font_CharWidth(codeflags, codepoint);\n\t\t}\n\t\tif (rightalign == 2)\n\t\t{\n\t\t\tif (fw < width)\n\t\t\t{\n\t\t\t\tpx += (width-fw)/2;\n\t\t\t\twidth = fw;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tpx += width;\n\t\t\tif (fw > width)\n\t\t\t\tfw = width;\n\t\t\tpx -= fw;\n\t\t}\n\t}\n\n\tfor (w = buffer; *w; )\n\t{\n\t\tw = Font_Decode(w, &codeflags, &codepoint);\n\n\t\twidth -= Font_CharWidth(codeflags, codepoint);\n\t\tif (width < 0)\n\t\t\treturn;\n\t\tpx = Font_DrawChar(px, py, codeflags, codepoint);\n\t}\n\tFont_EndString(font);\n}\n#ifdef QUAKEHUD\n\n\n\n#ifdef Q2CLIENT\nstatic void DrawHUDString (char *string, float x, float y, int centerwidth, qboolean alt)\n{\n\tvec2_t fontscale = {8,8};\n\tR_DrawTextField(x, y, centerwidth, 1024, string, alt?CON_ALTMASK:CON_WHITEMASK, CPRINT_TALIGN, font_default, fontscale);\n}\n#define STAT_MINUS\t\t10\t// num frame for '-' stats digit\nstatic char\t\t*q2sb_nums[2][11] =\n{\n\t{\"num_0\", \"num_1\", \"num_2\", \"num_3\", \"num_4\", \"num_5\",\n\t\"num_6\", \"num_7\", \"num_8\", \"num_9\", \"num_minus\"},\n\t{\"anum_0\", \"anum_1\", \"anum_2\", \"anum_3\", \"anum_4\", \"anum_5\",\n\t\"anum_6\", \"anum_7\", \"anum_8\", \"anum_9\", \"anum_minus\"}\n};\n\nstatic mpic_t *Sbar_Q2CachePic(char *name)\n{\n\tmpic_t *pic = R2D_SafeCachePic(strncmp(name, \"../\", 3)?va(\"pics/%s.pcx\", name):va(\"%s.pcx\", name+3));\n#if defined(IMAGEFMT_PCX)\n\tif (pic->width == 0 && pic->height == 0)\n\t{\n\t\tint xmin,ymin,swidth,sheight;\n\t\tsize_t length;\n\t\tpcx_t *pcx = (pcx_t*)COM_LoadTempFile(strncmp(name, \"../\", 3)?va(\"pics/%s.pcx\", name):va(\"%s.pcx\", name+3), 0, &length);\n\t\tif (pcx && length >= sizeof(*pcx))\n\t\t{\n\t\t\txmin = LittleShort(pcx->xmin);\n\t\t\tymin = LittleShort(pcx->ymin);\n\t\t\tswidth = LittleShort(pcx->xmax)-xmin+1;\n\t\t\tsheight = LittleShort(pcx->ymax)-ymin+1;\n\n\t\t\tif (pcx->manufacturer == 0x0a\n\t\t\t\t&& pcx->version == 5\n\t\t\t\t&& pcx->encoding == 1\n\t\t\t\t&& pcx->bits_per_pixel == 8\n\t\t\t\t&& swidth <= 1024\n\t\t\t\t&& sheight <= 1024)\n\t\t\t{\n\t\t\t\tpic->width = swidth;\n\t\t\t\tpic->height = sheight;\n\t\t\t}\n\t\t}\n\t}\n#endif\n\treturn pic;\n}\n\n#define\tICON_WIDTH\t24\n#define\tICON_HEIGHT\t24\n#define\tQCHAR_WIDTH\t16\n#define\tICON_SPACE\t8\nstatic void SCR_DrawField (float x, float y, int color, float width, int value)\n{\n\tchar\tnum[16], *ptr;\n\tint\t\tl;\n\tint\t\tframe;\n\tmpic_t *p;\n\tint pw,ph;\n\n\tif (width < 1)\n\t\treturn;\n\n\t// draw number string\n\tif (width > 5)\n\t\twidth = 5;\n\n\tsnprintf (num, sizeof(num), \"%i\", value);\n\tl = strlen(num);\n\tif (l > width)\n\t\tl = width;\n\tx += 2 + QCHAR_WIDTH*(width - l);\n\n\tptr = num;\n\twhile (*ptr && l)\n\t{\n\t\tif (*ptr == '-')\n\t\t\tframe = STAT_MINUS;\n\t\telse\n\t\t\tframe = *ptr -'0';\n\n\t\tp = Sbar_Q2CachePic(q2sb_nums[color][frame]);\n\t\tif (p && R_GetShaderSizes(p, &pw, &ph, false)>0)\n\t\t\tR2D_ScalePic (x,y,pw, ph, p);\n\t\tx += QCHAR_WIDTH;\n\t\tptr++;\n\t\tl--;\n\t}\n}\n\nchar *Get_Q2ConfigString(int i)\n{\n\tif (i >= Q2CS_IMAGES && i < Q2CS_IMAGES\t+ Q2MAX_IMAGES)\n\t\treturn cl.image_name[i-Q2CS_IMAGES]?cl.image_name[i-Q2CS_IMAGES]:\"\";\n\tif (i >= Q2CS_ITEMS && i < Q2CS_ITEMS + Q2MAX_ITEMS)\n\t\treturn cl.item_name[i-Q2CS_ITEMS]?cl.item_name[i-Q2CS_ITEMS]:\"\";\n\tif (i == Q2CS_STATUSBAR)\n\t\treturn cl.q2statusbar;\n\tif (i == Q2CS_NAME)\n\t\treturn cl.levelname;\n\n\tif (i >= Q2CS_MODELS && i < Q2CS_MODELS\t+ Q2MAX_MODELS)\n\t\treturn cl.model_name [i-Q2CS_MODELS];\n\tif (i >= Q2CS_SOUNDS && i < Q2CS_SOUNDS\t+ Q2MAX_SOUNDS)\n\t\treturn cl.sound_name [i-Q2CS_SOUNDS];\n\tif (i == Q2CS_AIRACCEL)\n\t\treturn cl.q2airaccel;\n\tif (i >= Q2CS_PLAYERSKINS && i < Q2CS_GENERAL+Q2MAX_GENERAL)\n\t\treturn cl.configstring_general[i-Q2CS_PLAYERSKINS]?cl.configstring_general[i-Q2CS_PLAYERSKINS]:\"\";\n//#define\tQ2CS_LIGHTS\t\t\t\t(Q2CS_IMAGES\t+Q2MAX_IMAGES)\n//#define\tQ2CS_ITEMS\t\t\t\t(Q2CS_LIGHTS\t+Q2MAX_LIGHTSTYLES)\n//#define\tQ2CS_PLAYERSKINS\t\t(Q2CS_ITEMS\t\t+Q2MAX_ITEMS)\n//#define Q2CS_GENERAL\t\t\t(Q2CS_PLAYERSKINS\t+Q2MAX_CLIENTS)\n\treturn \"\";\n}\nvoid Sbar_ExecuteLayoutString (char *s, int seat)\n{\n\tint\t\tx, y;\n\tint\t\tvalue;\n\tint\t\twidth;\n\tint\t\tindex;\n\tint pw, ph;\n//\tq2clientinfo_t\t*ci;\n\tmpic_t *p;\n\tq2player_state_t *ps = &cl.q2frame.seat[seat].playerstate;\n\n\tif (cls.state != ca_active)\n\t\treturn;\n\n\tif (!s[0])\n\t\treturn;\n\n\tx = sbar_rect.x;\n\ty = sbar_rect.y;\n\twidth = 3;\n\n\twhile (s)\n\t{\n\t\ts = COM_Parse (s);\n\t\tif (!strcmp(com_token, \"xl\"))\n\t\t{\t//relative to left\n\t\t\ts = COM_Parse (s);\n\t\t\tx = sbar_rect.x + atoi(com_token);\n\t\t\tcontinue;\n\t\t}\n\t\tif (!strcmp(com_token, \"xr\"))\n\t\t{\t//relative to right\n\t\t\ts = COM_Parse (s);\n\t\t\tx = sbar_rect.x + sbar_rect.width + atoi(com_token);\n\t\t\tcontinue;\n\t\t}\n\t\tif (!strcmp(com_token, \"xv\"))\n\t\t{\t//relative to central 640*480 box.\n\t\t\ts = COM_Parse (s);\n\t\t\tx = sbar_rect.x + (sbar_rect.width-320)/2 + atoi(com_token);\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (!strcmp(com_token, \"yt\"))\n\t\t{\t//relative to top\n\t\t\ts = COM_Parse (s);\n\t\t\ty = sbar_rect.y + atoi(com_token);\n\t\t\tcontinue;\n\t\t}\n\t\tif (!strcmp(com_token, \"yb\"))\n\t\t{\t//relative to bottom\n\t\t\ts = COM_Parse (s);\n\t\t\ty = sbar_rect.y + sbar_rect.height + atoi(com_token);\n\t\t\tcontinue;\n\t\t}\n\t\tif (!strcmp(com_token, \"yv\"))\n\t\t{\t//relative to central 640*480 box.\n\t\t\ts = COM_Parse (s);\n\t\t\ty = sbar_rect.y + (sbar_rect.height-240)/2 + atoi(com_token);\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (!strcmp(com_token, \"pic\"))\n\t\t{\t// draw a pic from a stat number\n\t\t\ts = COM_Parse (s);\n\t\t\tindex = atoi(com_token);\n\t\t\tif (index < 0 || index >= countof(ps->stats))\n\t\t\t\tHost_EndGame (\"Bad stat index\");\n\t\t\tvalue = ps->stats[index];\n\t\t\tif (value >= Q2MAX_IMAGES || value < 0)\n\t\t\t\tHost_EndGame (\"Pic >= Q2MAX_IMAGES\");\n\t\t\tif (*Get_Q2ConfigString(Q2CS_IMAGES+value))\n\t\t\t{\n//\t\t\t\tSCR_AddDirtyPoint (x, y);\n//\t\t\t\tSCR_AddDirtyPoint (x+23, y+23);\n\t\t\t\tp = Sbar_Q2CachePic(Get_Q2ConfigString(Q2CS_IMAGES+value));\n\t\t\t\tif (p && R_GetShaderSizes(p, &pw, &ph, false)>0)\n\t\t\t\t\tR2D_ScalePic (x, y, pw, ph, p);\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (!strcmp(com_token, \"client\"))\n\t\t{\t// draw a deathmatch client block\n\t\t\tint\t\tscore, ping, time;\n\n\t\t\ts = COM_Parse (s);\n\t\t\tx = sbar_rect.x + sbar_rect.width/2 - 160 + atoi(com_token);\n\t\t\ts = COM_Parse (s);\n\t\t\ty = sbar_rect.y + sbar_rect.height/2 - 120 + atoi(com_token);\n//\t\t\tSCR_AddDirtyPoint (x, y);\n//\t\t\tSCR_AddDirtyPoint (x+159, y+31);\n\n\t\t\ts = COM_Parse (s);\n\t\t\tvalue = atoi(com_token);\n\t\t\tif (value >= MAX_CLIENTS || value < 0)\n\t\t\t\tHost_EndGame (\"client >= MAX_CLIENTS\");\n\n\t\t\ts = COM_Parse (s);\n\t\t\tscore = atoi(com_token);\n\n\t\t\ts = COM_Parse (s);\n\t\t\tping = atoi(com_token);\n\n\t\t\ts = COM_Parse (s);\n\t\t\ttime = atoi(com_token);\n\n\t\t\tDraw_AltFunString (x+32, y, cl.players[value].name);\n\t\t\tDraw_FunString (x+32, y+8,  \"Score: \");\n\t\t\tDraw_AltFunString (x+32+7*8, y+8,  va(\"%i\", score));\n\t\t\tDraw_FunString (x+32, y+16, va(\"Ping:  %i\", ping));\n\t\t\tDraw_FunString (x+32, y+24, va(\"Time:  %i\", time));\n\n\t\t\tp = R2D_SafeCachePic(va(\"players/%s_i.pcx\", InfoBuf_ValueForKey(&cl.players[value].userinfo, \"skin\")));\n\t\t\tif (!p || !R_GetShaderSizes(p, NULL, NULL, false))\t//display a default if the icon couldn't be found.\n\t\t\t\tp = R2D_SafeCachePic(\"players/male/grunt_i.pcx\");\n\t\t\tR2D_ScalePic (x, y, 32, 32, p);\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (!strcmp(com_token, \"ctf\"))\n\t\t{\t// draw a ctf client block\n\t\t\tint\t\tscore, ping;\n\t\t\tchar\tblock[80];\n\n\t\t\ts = COM_Parse (s);\n\t\t\tx = sbar_rect.x + sbar_rect.width/2 - 160 + atoi(com_token);\n\t\t\ts = COM_Parse (s);\n\t\t\ty = sbar_rect.y + sbar_rect.height/2 - 120 + atoi(com_token);\n//\t\t\tSCR_AddDirtyPoint (x, y);\n//\t\t\tSCR_AddDirtyPoint (x+159, y+31);\n\n\t\t\ts = COM_Parse (s);\n\t\t\tvalue = atoi(com_token);\n\t\t\tif (value >= MAX_CLIENTS || value < 0)\n\t\t\t\tHost_EndGame (\"client >= MAX_CLIENTS\");\n\n\t\t\ts = COM_Parse (s);\n\t\t\tscore = atoi(com_token);\n\n\t\t\ts = COM_Parse (s);\n\t\t\tping = atoi(com_token);\n\t\t\tif (ping > 999)\n\t\t\t\tping = 999;\n\n\t\t\tsprintf(block, \"%3d %3d %-12.12s\", score, ping, cl.players[value].name);\n\n//\t\t\tif (value == cl.playernum)\n//\t\t\t\tDraw_Alt_String (x, y, block);\n//\t\t\telse\n\t\t\t\tDraw_FunString (x, y, block);\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (!strcmp(com_token, \"picn\"))\n\t\t{\t// draw a pic from a name\n\t\t\ts = COM_Parse (s);\n//\t\t\tSCR_AddDirtyPoint (x, y);\n//\t\t\tSCR_AddDirtyPoint (x+23, y+23);\n\t\t\tp = Sbar_Q2CachePic(com_token);\n\t\t\tif (p && R_GetShaderSizes(p, &pw, &ph, false)>0)\n\t\t\t\tR2D_ScalePic (x, y, pw, ph, p);\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (!strcmp(com_token, \"num\"))\n\t\t{\t// draw a number\n\t\t\ts = COM_Parse (s);\n\t\t\twidth = atoi(com_token);\n\t\t\ts = COM_Parse (s);\n\t\t\tindex = atoi(com_token);\n\t\t\tif (index < 0 || index >= countof(ps->stats))\n\t\t\t\tvalue = 0;\n\t\t\telse\n\t\t\t\tvalue = ps->stats[index];\n\t\t\tSCR_DrawField (x, y, 0, width, value);\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (!strcmp(com_token, \"hnum\"))\n\t\t{\t// health number\n\t\t\tint\t\tcolor;\n\n\t\t\twidth = 3;\n\t\t\tvalue = ps->stats[Q2STAT_HEALTH];\n\t\t\tif (value > 25)\n\t\t\t\tcolor = 0;\t// green\n\t\t\telse if (value > 0)\n\t\t\t\tcolor = (cl.q2frame.serverframe>>2) & 1;\t\t// flash\n\t\t\telse\n\t\t\t\tcolor = 1;\n\n\t\t\tif (ps->stats[Q2STAT_FLASHES] & 1)\n\t\t\t{\n\t\t\t\tp = Sbar_Q2CachePic(\"field_3\");\n\t\t\t\tif (p && R_GetShaderSizes(p, &pw, &ph, false)>0)\n\t\t\t\t\tR2D_ScalePic (x, y, pw, ph, p);\n\t\t\t}\n\n\t\t\tSCR_DrawField (x, y, color, width, value);\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (!strcmp(com_token, \"anum\"))\n\t\t{\t// ammo number\n\t\t\tint\t\tcolor;\n\n\t\t\twidth = 3;\n\t\t\tvalue = ps->stats[Q2STAT_AMMO];\n\t\t\tif (value > 5)\n\t\t\t\tcolor = 0;\t// green\n\t\t\telse if (value >= 0)\n\t\t\t\tcolor = (cl.q2frame.serverframe>>2) & 1;\t\t// flash\n\t\t\telse\n\t\t\t\tcontinue;\t// negative number = don't show\n\n\t\t\tif (ps->stats[Q2STAT_FLASHES] & 4)\n\t\t\t{\n\t\t\t\tp = Sbar_Q2CachePic(\"field_3\");\n\t\t\t\tif (p && R_GetShaderSizes(p, &pw, &ph, false)>0)\n\t\t\t\t\tR2D_ScalePic (x, y, pw, ph, p);\n\t\t\t}\n\n\t\t\tSCR_DrawField (x, y, color, width, value);\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (!strcmp(com_token, \"rnum\"))\n\t\t{\t// armor number\n\t\t\tint\t\tcolor;\n\n\t\t\twidth = 3;\n\t\t\tvalue = ps->stats[Q2STAT_ARMOR];\n\t\t\tif (value < 1)\n\t\t\t\tcontinue;\n\n\t\t\tcolor = 0;\t// green\n\n\t\t\tif (ps->stats[Q2STAT_FLASHES] & 2)\n\t\t\t\tR2D_ScalePic (x, y, 64, 64, R2D_SafeCachePic(\"field_3\"));\n\n\t\t\tSCR_DrawField (x, y, color, width, value);\n\t\t\tcontinue;\n\t\t}\n\n\n\t\tif (!strcmp(com_token, \"stat_string\"))\n\t\t{\n\t\t\ts = COM_Parse (s);\n\t\t\tindex = atoi(com_token);\n\t\t\tif (index < 0 || index >= Q2MAX_CONFIGSTRINGS)\n\t\t\t\tHost_EndGame (\"Bad stat_string index\");\n\t\t\tindex = ps->stats[index];\n\t\t\tif (index < 0 || index >= Q2MAX_CONFIGSTRINGS)\n\t\t\t\tHost_EndGame (\"Bad stat_string index\");\n\t\t\tDraw_FunString (x, y, Get_Q2ConfigString(index));\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (!strcmp(com_token, \"cstring\"))\n\t\t{\n\t\t\ts = COM_Parse (s);\n\t\t\tDrawHUDString (com_token, x, y, 320, false);\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (!strcmp(com_token, \"string\"))\n\t\t{\n\t\t\ts = COM_Parse (s);\n\t\t\tDraw_FunString (x, y, com_token);\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (!strcmp(com_token, \"cstring2\"))\n\t\t{\n\t\t\ts = COM_Parse (s);\n\t\t\tDrawHUDString (com_token, x, y, 320, true);\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (!strcmp(com_token, \"string2\"))\n\t\t{\n\t\t\ts = COM_Parse (s);\n\t\t\tDraw_AltFunString (x, y, com_token);\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (!strcmp(com_token, \"if\"))\n\t\t{\t// draw a number\n\t\t\ts = COM_Parse (s);\n\t\t\tindex = atoi(com_token);\n\t\t\tif (index < 0 || index >= countof(ps->stats))\n\t\t\t\tvalue = 0;\n\t\t\telse\n\t\t\t\tvalue = ps->stats[index];\n\t\t\tif (!value)\n\t\t\t{\t// skip to endif\n\t\t\t\twhile (s && strcmp(com_token, \"endif\") )\n\t\t\t\t{\n\t\t\t\t\ts = COM_Parse (s);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tcontinue;\n\t\t}\n\t\tif (!strcmp(com_token, \"endif\"))\n\t\t{\t//conditional was taken (so its endif wasn't ignored)\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (*com_token)\n\t\t{\n\t\t\tstatic float throttle;\n\t\t\tCon_ThrottlePrintf(&throttle, 2, \"Unknown layout command \\\"%s\\\"\\n\", com_token);\n\t\t}\n\t}\n}\n\nstatic void Sbar_Q2DrawInventory(int seat)\n{\n\tint keys[1], keymods[1];\n\tchar cmd[1024];\n\tconst char *boundkey;\n\tq2player_state_t *ps = &cl.q2frame.seat[seat].playerstate;\n\tunsigned int validlist[Q2MAX_ITEMS], rows, i, item, selected = ps->stats[Q2STAT_SELECTED_ITEM];\n\tint first;\n\tunsigned int maxrows = ((240-24*2-8*2)/8);\n\t//draw background\n\tfloat x = sbar_rect.x + (sbar_rect.width - 256)/2;\n\tfloat y = sbar_rect.y + (sbar_rect.height - 240)/2;\n\tint deflang = TL_FindLanguage(\"\");\t//ffs... read: english\n\tif (y < sbar_rect.y)\n\t\ty = sbar_rect.y;\t//try to fix small-res 3-way splitscreen slightly\n\tR2D_ScalePic(x, y, 256, 240, Sbar_Q2CachePic(\"inventory\"));\n\t//move into the frame\n\tx += 24;\n\ty += 24;\n\n\t//figure out which items we have\n\tfor (i = 0, rows = 0, first = -1; i < Q2MAX_ITEMS; i++)\n\t{\n\t\tif (!cl.inventory[seat][i])\n\t\t\tcontinue;\n\t\tif (i <= selected)\n\t\t\tfirst = rows;\n\t\tvalidlist[rows++] = i;\n\t}\n\tfirst -= maxrows/2;\n\tfirst = min(first, (signed)(rows-maxrows));\n\tfirst = max(0, first);\n\trows = min(rows, first+maxrows);\n\n\t//match q2, because why not.\n\tDraw_FunString(x, y, \"hotkey ### item\");y+=8;\n\tDraw_FunString(x, y, \"------ --- ----\");y+=8;\n\tfor (i = first; i < rows; i++)\n\t{\n\t\titem = validlist[i];\n\n\t\tQ_snprintfz(cmd, sizeof(cmd), \"use %s\", TL_Translate(deflang, Get_Q2ConfigString(Q2CS_ITEMS+item)));\n\t\tif (!M_FindKeysForCommand(0, 0, cmd, keys, keymods, countof(keys)))\n\t\t\tboundkey = \"\";\t//we don't actually know which ones can be selected at all.\n\t\telse\n\t\t\tboundkey = Key_KeynumToString(keys[0], keymods[0]);\n\n\t\tQ_snprintfz(cmd, sizeof(cmd), \"%6s %3i %s\", boundkey, cl.inventory[seat][item], TL_Translate(com_language, Get_Q2ConfigString(Q2CS_ITEMS+item)));\n\t\tDraw_FunStringWidth(x, y, cmd, 256-24*2+8, false, item != selected);\ty+=8;\n\t}\n}\n#endif\n\n/*\n===============\nSbar_ShowTeamScores\n\nTab key down\n===============\n*/\nvoid Sbar_ShowTeamScores (void)\n{\n\tint seat = CL_TargettedSplit(false);\n\tif (cl.playerview[seat].sb_showteamscores)\n\t\treturn;\n\n#ifdef CSQC_DAT\n\tif (CSQC_ConsoleCommand(seat, Cmd_Argv(0)))\n\t\t;//return;\n#endif\n\n\tcl.playerview[seat].sb_showteamscores = true;\n\tsb_updates = 0;\n}\n\n/*\n===============\nSbar_DontShowTeamScores\n\nTab key up\n===============\n*/\nvoid Sbar_DontShowTeamScores (void)\n{\n\tint seat = CL_TargettedSplit(false);\n\tcl.playerview[seat].sb_showteamscores = false;\n\tsb_updates = 0;\n\n#ifdef CSQC_DAT\n\tif (CSQC_ConsoleCommand(seat, Cmd_Argv(0)))\n\t\treturn;\n#endif\n}\n\n/*\n===============\nSbar_ShowScores\n\nTab key down\n===============\n*/\nvoid Sbar_ShowScores (void)\n{\n\tint seat = CL_TargettedSplit(false);\n\tif (scr_scoreboard_teamscores.ival)\n\t{\n\t\tSbar_ShowTeamScores();\n\t\treturn;\n\t}\n\n\tif (cl.playerview[seat].sb_showscores)\n\t\treturn;\n\n#ifdef CSQC_DAT\n\tif (CSQC_ConsoleCommand(seat, Cmd_Argv(0)))\n\t\t;//return;\n#endif\n\n\tcl.playerview[seat].sb_showscores = true;\n\tsb_updates = 0;\n}\n\n#ifdef HEXEN2\nstatic void Sbar_Hexen2InvLeft_f(void)\n{\n\tint seat = CL_TargettedSplit(false);\n#ifdef CSQC_DAT\n\tif (CSQC_ConsoleCommand(seat, Cmd_Argv(0)))\n\t\treturn;\n#endif\n\tif (cls.protocol == CP_QUAKE2)\n\t{\n\t\tCL_SendSeatClientCommand(true, seat, \"invprev\");\n\t}\n\telse\n\t{\n\t\tint tries = 15;\n\t\tplayerview_t *pv = &cl.playerview[seat];\n\t\tpv->sb_hexen2_item_time = realtime;\n\t\tS_LocalSound(\"misc/invmove.wav\");\n\t\twhile (tries-- > 0)\n\t\t{\n\t\t\tpv->sb_hexen2_cur_item--;\n\t\t\tif (pv->sb_hexen2_cur_item < 0)\n\t\t\t\tpv->sb_hexen2_cur_item = 14;\n\n\t\t\tif (pv->stats[STAT_H2_CNT_FIRST+pv->sb_hexen2_cur_item] > 0)\n\t\t\t\tbreak;\n\t\t}\n\t}\n}\nstatic void Sbar_Hexen2InvRight_f(void)\n{\n\tint seat = CL_TargettedSplit(false);\n#ifdef CSQC_DAT\n\tif (CSQC_ConsoleCommand(seat, Cmd_Argv(0)))\n\t\treturn;\n#endif\n\tif (cls.protocol == CP_QUAKE2)\n\t{\n\t\tCL_SendSeatClientCommand(true, seat, \"invnext\");\n\t}\n\telse\n\t{\n\t\tint tries = 15;\n\t\tplayerview_t *pv = &cl.playerview[seat];\n\t\tpv->sb_hexen2_item_time = realtime;\n\t\tS_LocalSound(\"misc/invmove.wav\");\n\t\twhile (tries-- > 0)\n\t\t{\n\t\t\tpv->sb_hexen2_cur_item++;\n\t\t\tif (pv->sb_hexen2_cur_item > 14)\n\t\t\t\tpv->sb_hexen2_cur_item = 0;\n\n\t\t\tif (pv->stats[STAT_H2_CNT_FIRST+pv->sb_hexen2_cur_item] > 0)\n\t\t\t\tbreak;\n\t\t}\n\t}\n}\nstatic void Sbar_Hexen2InvUse_f(void)\n{\n\tint seat = CL_TargettedSplit(false);\n#ifdef CSQC_DAT\n\tif (CSQC_ConsoleCommand(seat, Cmd_Argv(0)))\n\t\treturn;\n#endif\n\n\tif (cls.protocol == CP_QUAKE2)\n\t{\n\t\tCL_SendSeatClientCommand(true, seat, \"invuse\");\n\t}\n\telse\n\t{\n\t\tplayerview_t *pv = &cl.playerview[seat];\n\t\tS_LocalSound(\"misc/invuse.wav\");\n\t\tCmd_ExecuteString(va(\"impulse %d\\n\", 100+pv->sb_hexen2_cur_item), Cmd_ExecLevel);\n\t}\n}\nstatic void Sbar_Hexen2ShowInfo_f(void)\n{\n\tint seat = CL_TargettedSplit(false);\n\tplayerview_t *pv = &cl.playerview[seat];\n#ifdef CSQC_DAT\n\tif (CSQC_ConsoleCommand(seat, Cmd_Argv(0)))\n\t\treturn;\n#endif\n\tpv->sb_hexen2_extra_info = true;\n}\nstatic void Sbar_Hexen2DontShowInfo_f(void)\n{\n\tint seat = CL_TargettedSplit(false);\n\tplayerview_t *pv = &cl.playerview[seat];\n#ifdef CSQC_DAT\n\tif (CSQC_ConsoleCommand(seat, Cmd_Argv(0)))\n\t\treturn;\n#endif\n\tpv->sb_hexen2_extra_info = false;\n}\nstatic void Sbar_Hexen2PInfoPlaque_f(void)\n{\n\tint seat = CL_TargettedSplit(false);\n\tplayerview_t *pv = &cl.playerview[seat];\n#ifdef CSQC_DAT\n\tif (CSQC_ConsoleCommand(seat, Cmd_Argv(0)))\n\t\treturn;\n#endif\n\tpv->sb_hexen2_infoplaque = true;\n}\nstatic void Sbar_Hexen2MInfoPlaque_f(void)\n{\n\tint seat = CL_TargettedSplit(false);\n\tplayerview_t *pv = &cl.playerview[seat];\n#ifdef CSQC_DAT\n\tif (CSQC_ConsoleCommand(seat, Cmd_Argv(0)))\n\t\treturn;\n#endif\n\tpv->sb_hexen2_infoplaque = false;\n}\n#endif\n\n/*\n===============\nSbar_DontShowScores\n\nTab key up\n===============\n*/\nvoid Sbar_DontShowScores (void)\n{\n\tint seat = CL_TargettedSplit(false);\n\tif (scr_scoreboard_teamscores.ival)\n\t{\n\t\tSbar_DontShowTeamScores();\n\t\treturn;\n\t}\n\n\tcl.playerview[seat].sb_showscores = false;\n\tsb_updates = 0;\n\n#ifdef CSQC_DAT\n\tif (CSQC_ConsoleCommand(seat, Cmd_Argv(0)))\n\t\treturn;\n#endif\n}\n\n/*\n===============\nSbar_Changed\n===============\n*/\nvoid Sbar_Changed (void)\n{\n\tsb_updates = 0;\t// update next frame\n}\n\n/*\n===============\nSbar_Init\n===============\n*/\n\nstatic qboolean sbar_loaded;\n\nstatic apic_t *Sbar_PicFromWad(char *name)\n{\n\tapic_t *ret;\n\tchar savedname[MAX_QPATH];\n\tQ_strncpyz(savedname, name, sizeof(savedname));\n\tret = R2D_LoadAtlasedPic(savedname);\n\n\tif (ret)\n\t\treturn ret;\n\n\treturn NULL;\n}\nvoid Sbar_Flush (void)\n{\n\tsbar_loaded = false;\n\tmemset(sb_weapons, 0, sizeof(sb_weapons));\n}\nvoid Sbar_Start (void)\t//if one of these fails, skip the entire status bar.\n{\n\tint\t\ti;\n\tsize_t\tlumpsize;\n\tqbyte\tlumptype;\n\tif (sbar_loaded)\n\t\treturn;\n\n\tmemset(sb_weapons, 0, sizeof(sb_weapons));\n\n\tsbar_loaded = true;\n\n\tCOM_FlushFSCache(false, true);\t//make sure the fs cache is built if needed. there's lots of loading here.\n\n\tif (!wad_base)\t//the wad isn't loaded. This is an indication that it doesn't exist.\n\t{\n\t\tsbarfailed = true;\n\t\treturn;\n\t}\n\n\tsbarfailed = false;\n\n\tfor (i=0 ; i<10 ; i++)\n\t{\n\t\tsb_nums[0][i] = Sbar_PicFromWad (va(\"num_%i\",i));\n\t\tsb_nums[1][i] = Sbar_PicFromWad (va(\"anum_%i\",i));\n\t}\n\n#ifdef HEXEN2\n\tsbar_hexen2 = false;\n\tif (W_GetLumpName(\"tinyfont\", &lumpsize, &lumptype))\n\t\tsbar_hexen2 = true;\n//\tif (sb_nums[0][0] && sb_nums[0][0]->width < 13)\n//\t\tsbar_hexen2 = true;\n#endif\n\n\tsb_nums[0][10] = Sbar_PicFromWad (\"num_minus\");\n\tsb_nums[1][10] = Sbar_PicFromWad (\"anum_minus\");\n\n\tsb_colon = Sbar_PicFromWad (\"num_colon\");\n\tsb_slash = Sbar_PicFromWad (\"num_slash\");\n\n\tsb_weapons[0][0] = Sbar_PicFromWad (\"inv_shotgun\");\n\tsb_weapons[0][1] = Sbar_PicFromWad (\"inv_sshotgun\");\n\tsb_weapons[0][2] = Sbar_PicFromWad (\"inv_nailgun\");\n\tsb_weapons[0][3] = Sbar_PicFromWad (\"inv_snailgun\");\n\tsb_weapons[0][4] = Sbar_PicFromWad (\"inv_rlaunch\");\n\tsb_weapons[0][5] = Sbar_PicFromWad (\"inv_srlaunch\");\n\tsb_weapons[0][6] = Sbar_PicFromWad (\"inv_lightng\");\n\n\tsb_weapons[1][0] = Sbar_PicFromWad (\"inv2_shotgun\");\n\tsb_weapons[1][1] = Sbar_PicFromWad (\"inv2_sshotgun\");\n\tsb_weapons[1][2] = Sbar_PicFromWad (\"inv2_nailgun\");\n\tsb_weapons[1][3] = Sbar_PicFromWad (\"inv2_snailgun\");\n\tsb_weapons[1][4] = Sbar_PicFromWad (\"inv2_rlaunch\");\n\tsb_weapons[1][5] = Sbar_PicFromWad (\"inv2_srlaunch\");\n\tsb_weapons[1][6] = Sbar_PicFromWad (\"inv2_lightng\");\n\n\tfor (i=0 ; i<5 ; i++)\n\t{\n\t\tsb_weapons[2+i][0] = Sbar_PicFromWad (va(\"inva%i_shotgun\",i+1));\n\t\tsb_weapons[2+i][1] = Sbar_PicFromWad (va(\"inva%i_sshotgun\",i+1));\n\t\tsb_weapons[2+i][2] = Sbar_PicFromWad (va(\"inva%i_nailgun\",i+1));\n\t\tsb_weapons[2+i][3] = Sbar_PicFromWad (va(\"inva%i_snailgun\",i+1));\n\t\tsb_weapons[2+i][4] = Sbar_PicFromWad (va(\"inva%i_rlaunch\",i+1));\n\t\tsb_weapons[2+i][5] = Sbar_PicFromWad (va(\"inva%i_srlaunch\",i+1));\n\t\tsb_weapons[2+i][6] = Sbar_PicFromWad (va(\"inva%i_lightng\",i+1));\n\t}\n\n\tsb_ammo[0] = Sbar_PicFromWad (\"sb_shells\");\n\tsb_ammo[1] = Sbar_PicFromWad (\"sb_nails\");\n\tsb_ammo[2] = Sbar_PicFromWad (\"sb_rocket\");\n\tsb_ammo[3] = Sbar_PicFromWad (\"sb_cells\");\n\n\tsb_armor[0] = Sbar_PicFromWad (\"sb_armor1\");\n\tsb_armor[1] = Sbar_PicFromWad (\"sb_armor2\");\n\tsb_armor[2] = Sbar_PicFromWad (\"sb_armor3\");\n\n\tsb_items[0] = Sbar_PicFromWad (\"sb_key1\");\n\tsb_items[1] = Sbar_PicFromWad (\"sb_key2\");\n\tsb_items[2] = Sbar_PicFromWad (\"sb_invis\");\n\tsb_items[3] = Sbar_PicFromWad (\"sb_invuln\");\n\tsb_items[4] = Sbar_PicFromWad (\"sb_suit\");\n\tsb_items[5] = Sbar_PicFromWad (\"sb_quad\");\n\n\tsb_sigil[0] = Sbar_PicFromWad (\"sb_sigil1\");\n\tsb_sigil[1] = Sbar_PicFromWad (\"sb_sigil2\");\n\tsb_sigil[2] = Sbar_PicFromWad (\"sb_sigil3\");\n\tsb_sigil[3] = Sbar_PicFromWad (\"sb_sigil4\");\n\n\tsb_faces[4][0] = Sbar_PicFromWad (\"face1\");\n\tsb_faces[4][1] = Sbar_PicFromWad (\"face_p1\");\n\tsb_faces[3][0] = Sbar_PicFromWad (\"face2\");\n\tsb_faces[3][1] = Sbar_PicFromWad (\"face_p2\");\n\tsb_faces[2][0] = Sbar_PicFromWad (\"face3\");\n\tsb_faces[2][1] = Sbar_PicFromWad (\"face_p3\");\n\tsb_faces[1][0] = Sbar_PicFromWad (\"face4\");\n\tsb_faces[1][1] = Sbar_PicFromWad (\"face_p4\");\n\tsb_faces[0][0] = Sbar_PicFromWad (\"face5\");\n\tsb_faces[0][1] = Sbar_PicFromWad (\"face_p5\");\n\n\tsb_face_invis = Sbar_PicFromWad (\"face_invis\");\n\tsb_face_invuln = Sbar_PicFromWad (\"face_invul2\");\n\tsb_face_invis_invuln = Sbar_PicFromWad (\"face_inv2\");\n\tsb_face_quad = Sbar_PicFromWad (\"face_quad\");\n\n\tsb_ibar = Sbar_PicFromWad (\"ibar\");\n\tsb_sbar = Sbar_PicFromWad (\"sbar\");\n\tsb_scorebar = Sbar_PicFromWad (\"scorebar\");\n\n\t//try to detect rogue wads, and thus the stats we will be getting from the server.\n\tsbar_rogue = COM_CheckParm(\"-rogue\") || !!W_GetLumpName(\"r_lava\", &lumpsize, &lumptype);\n\tif (sbar_rogue)\n\t{\n\t\trsb_invbar[0] = Sbar_PicFromWad (\"r_invbar1\");\n\t\trsb_invbar[1] = Sbar_PicFromWad (\"r_invbar2\");\n\n\t\trsb_weapons[0] = Sbar_PicFromWad (\"r_lava\");\n\t\trsb_weapons[1] = Sbar_PicFromWad (\"r_superlava\");\n\t\trsb_weapons[2] = Sbar_PicFromWad (\"r_gren\");\n\t\trsb_weapons[3] = Sbar_PicFromWad (\"r_multirock\");\n\t\trsb_weapons[4] = Sbar_PicFromWad (\"r_plasma\");\n\n\t\trsb_items[0] = Sbar_PicFromWad (\"r_shield1\");\n\t\trsb_items[1] = Sbar_PicFromWad (\"r_agrav1\");\n\n\t\trsb_teambord = Sbar_PicFromWad (\"r_teambord\");\n\n\t\trsb_ammo[0] = Sbar_PicFromWad (\"r_ammolava\");\n\t\trsb_ammo[1] = Sbar_PicFromWad (\"r_ammomulti\");\n\t\trsb_ammo[2] = Sbar_PicFromWad (\"r_ammoplasma\");\n\t}\n\n\tsbar_hipnotic = COM_CheckParm(\"-hipnotic\") || !!W_GetLumpName(\"inv_mjolnir\", &lumpsize, &lumptype);\n\tif (sbar_hipnotic)\n\t{\n\t\thsb_weapons[0][0] = Sbar_PicFromWad (\"inv_laser\");\n\t\thsb_weapons[0][1] = Sbar_PicFromWad (\"inv_mjolnir\");\n\t\thsb_weapons[0][2] = Sbar_PicFromWad (\"inv_gren_prox\");\n\t\thsb_weapons[0][3] = Sbar_PicFromWad (\"inv_prox_gren\");\n\t\thsb_weapons[0][4] = Sbar_PicFromWad (\"inv_prox\");\n\t\thsb_weapons[1][0] = Sbar_PicFromWad (\"inv2_laser\");\n\t\thsb_weapons[1][1] = Sbar_PicFromWad (\"inv2_mjolnir\");\n\t\thsb_weapons[1][2] = Sbar_PicFromWad (\"inv2_gren_prox\");\n\t\thsb_weapons[1][3] = Sbar_PicFromWad (\"inv2_prox_gren\");\n\t\thsb_weapons[1][4] = Sbar_PicFromWad (\"inv2_prox\");\n\t\tfor (i=0 ; i<5 ; i++)\n\t\t{\n\t\t\thsb_weapons[2+i][0] = Sbar_PicFromWad (va(\"inva%i_laser\",i+1));\n\t\t\thsb_weapons[2+i][1] = Sbar_PicFromWad (va(\"inva%i_mjolnir\",i+1));\n\t\t\thsb_weapons[2+i][2] = Sbar_PicFromWad (va(\"inva%i_gren_prox\",i+1));\n\t\t\thsb_weapons[2+i][3] = Sbar_PicFromWad (va(\"inva%i_prox_gren\",i+1));\n\t\t\thsb_weapons[2+i][4] = Sbar_PicFromWad (va(\"inva%i_prox\",i+1));\n\t\t}\n\t\thsb_items[0] = Sbar_PicFromWad (\"sb_wsuit\");\n\t\thsb_items[1] = Sbar_PicFromWad (\"sb_eshld\");\n\t}\n}\n\nvoid Sbar_Init (void)\n{\n\tCvar_Register(&scr_scoreboard_drawtitle, \"Scoreboard settings\");\n\tCvar_Register(&scr_scoreboard_forcecolors, \"Scoreboard settings\");\n\tCvar_Register(&scr_scoreboard_newstyle, \"Scoreboard settings\");\n\tCvar_Register(&scr_scoreboard_showfrags, \"Scoreboard settings\");\n#ifdef QUAKESTATS\n\tCvar_Register(&scr_scoreboard_showlocation, \"Scoreboard settings\");\n\tCvar_Register(&scr_scoreboard_showhealth, \"Scoreboard settings\");\n\tCvar_Register(&scr_scoreboard_showweapon, \"Scoreboard settings\");\n#endif\n\tCvar_Register(&scr_scoreboard_showflags, \"Scoreboard settings\");\n\tCvar_Register(&scr_scoreboard_showruleset, \"Scoreboard settings\");\n\tCvar_Register(&scr_scoreboard_afk, \"Scoreboard settings\");\n\tCvar_Register(&scr_scoreboard_ping_status, \"Scoreboard settings\");\n\tCvar_Register(&scr_scoreboard_fillalpha, \"Scoreboard settings\");\n\tCvar_Register(&scr_scoreboard_backgroundalpha, \"Scoreboard settings\");\n\tCvar_Register(&scr_scoreboard_teamscores, \"Scoreboard settings\");\n\tCvar_Register(&scr_scoreboard_teamsort, \"Scoreboard settings\");\n\tCvar_Register(&scr_scoreboard_titleseperator, \"Scoreboard settings\");\n\n\tCvar_Register(&sbar_teamstatus, \"Status bar settings\");\n\tCvar_Register(&cl_sbaralpha, \"Status bar settings\");\n\n\tCmd_AddCommand (\"+showscores\", Sbar_ShowScores);\n\tCmd_AddCommand (\"-showscores\", Sbar_DontShowScores);\n\n\tCmd_AddCommand (\"+showteamscores\", Sbar_ShowTeamScores);\n\tCmd_AddCommand (\"-showteamscores\", Sbar_DontShowTeamScores);\n\n#ifdef NQPROT\n\tCmd_AddCommand (\"ctfscores\", Sbar_CTFScores_f);\t//server->client score updates.\n#endif\n\n#ifdef HEXEN2\n\t//stuff to get hexen2 working out-of-the-box\n\tCmd_AddCommand (\"invleft\", Sbar_Hexen2InvLeft_f);\n\tCmd_AddCommand (\"invright\", Sbar_Hexen2InvRight_f);\n\tCmd_AddCommand (\"invprev\", Sbar_Hexen2InvLeft_f);\n\tCmd_AddCommand (\"invnext\", Sbar_Hexen2InvRight_f);\n\tCmd_AddCommand (\"invuse\", Sbar_Hexen2InvUse_f);\n\tCmd_AddCommandD (\"+showinfo\", Sbar_Hexen2ShowInfo_f, \"Hexen2 Compat\");\n\tCmd_AddCommandD (\"-showinfo\", Sbar_Hexen2DontShowInfo_f, \"Hexen2 Compat\");\n\tCmd_AddCommandD (\"+infoplaque\", Sbar_Hexen2PInfoPlaque_f, \"Hexen2 Compat\");\n\tCmd_AddCommandD (\"-infoplaque\", Sbar_Hexen2MInfoPlaque_f, \"Hexen2 Compat\");\n\tCmd_AddCommandD (\"+showdm\", Sbar_ShowScores, \"Hexen2 Compat\");\n\tCmd_AddCommandD (\"-showdm\", Sbar_DontShowScores, \"Hexen2 Compat\");\n\tCbuf_AddText(\"alias +crouch \\\"impulse 22\\\"\\n\", RESTRICT_LOCAL);\n\tCbuf_AddText(\"alias -crouch \\\"impulse 22\\\"\\n\", RESTRICT_LOCAL);\n#endif\n}\n\n\n//=============================================================================\n\n// drawing routines are reletive to the status bar location\n/*\n=============\nSbar_DrawPic\n=============\n*/\nstatic void Sbar_DrawPic (float x, float y, float w, float h, apic_t *pic)\n{\n\tR2D_ImageAtlas(sbar_rect.x + x /* + ((sbar_rect.width - 320)>>1) */, sbar_rect.y + y + (sbar_rect.height-SBAR_HEIGHT), w, h, 0, 0, 1, 1, pic);\n}\nstatic void Sbar_DrawMPic (float x, float y, float w, float h, mpic_t *pic)\n{\n\tR2D_ScalePic(sbar_rect.x + x /* + ((sbar_rect.width - 320)>>1) */, sbar_rect.y + y + (sbar_rect.height-SBAR_HEIGHT), w, h, pic);\n}\n\n/*\n=============\nSbar_DrawSubPic\n=============\nJACK: Draws a portion of the picture in the status bar.\n*/\n\nstatic void Sbar_DrawSubPic(float x, float y, float width, float height, apic_t *pic, int srcx, int srcy, int srcwidth, int srcheight)\n{\n\tfloat newsl, newtl, newsh, newth;\n\n\tnewsl = (srcx)/(float)srcwidth;\n\tnewsh = newsl + (width)/(float)srcwidth;\n\n\tnewtl = (srcy)/(float)srcheight;\n\tnewth = newtl + (height)/(float)srcheight;\n\n\tR2D_ImageAtlas (sbar_rect.x + x, sbar_rect.y + y+(sbar_rect.height-SBAR_HEIGHT), width, height, newsl, newtl, newsh, newth, pic);\n}\n\n/*\n================\nSbar_DrawCharacter\n\nDraws one solid graphics character\n================\n*/\nvoid Sbar_DrawCharacter (float x, float y, int num)\n{\n\tint px, py;\n\tFont_BeginString(font_default, sbar_rect.x + x + 4, sbar_rect.y + y + sbar_rect.height-SBAR_HEIGHT, &px, &py);\n\tFont_DrawChar(px, py, CON_WHITEMASK, num | 0xe000);\n\tFont_EndString(font_default);\n}\n\n/*\n================\nSbar_DrawString\n================\n*/\nvoid Sbar_DrawString (float x, float y, char *str)\n{\n\tDraw_FunString (sbar_rect.x + x /*+ ((sbar_rect.width - 320)>>1) */, sbar_rect.y + y+ sbar_rect.height-SBAR_HEIGHT, str);\n}\n\nvoid Sbar_DrawExpandedString (float x, float y, conchar_t *str)\n{\n\tDraw_ExpandedString (font_default, sbar_rect.x + x /*+ ((sbar_rect.width - 320)>>1) */, sbar_rect.y + y+ sbar_rect.height-SBAR_HEIGHT, str);\n}\n\nvoid Draw_TinyString (float x, float y, const qbyte *str)\n{\n\tfloat xstart;\n\tint px, py;\n\tunsigned int codepoint;\n\tint error;\n\n\tif (!font_tiny)\n\t{\n\t\tfont_tiny = Font_LoadFont(\"gfx/tinyfont\", 8, 1, 0, 0);\n\t\tif (!font_tiny)\n\t\t\treturn;\n\t}\n\n\tFont_BeginString(font_tiny, x, y, &px, &py);\n\txstart = px;\n\n\twhile (*str)\n\t{\n\t\tcodepoint = unicode_decode(&error, str, (char const**)&str, true);\n\n\t\tif (codepoint == '\\n')\n\t\t{\n\t\t\tpx = xstart;\n\t\t\tpy += Font_CharHeight();\n\t\t\tstr++;\n\t\t\tcontinue;\n\t\t}\n\t\tpx = Font_DrawChar(px, py, CON_WHITEMASK, codepoint);\n\t}\n\tFont_EndString(font_tiny);\n}\nvoid Sbar_DrawTinyString (float x, float y, char *str)\n{\n\tDraw_TinyString (sbar_rect.x + x /*+ ((sbar_rect.width - 320)>>1) */, sbar_rect.y + y+ sbar_rect.height-SBAR_HEIGHT, str);\n}\nvoid Sbar_DrawTinyStringf (float x, float y, char *fmt, ...)\n{\n\tva_list\t\targptr;\n\tchar\t\tstring[256];\n\n\tva_start (argptr, fmt);\n\tvsnprintf (string, sizeof(string)-1, fmt, argptr);\n\tva_end (argptr);\n\t\n\tDraw_TinyString (sbar_rect.x + x /*+ ((sbar_rect.width - 320)>>1) */, sbar_rect.y + y+ sbar_rect.height-SBAR_HEIGHT, string);\n}\n\n\nvoid Sbar_FillPC (float x, float y, float w, float h, unsigned int pcolour)\n{\n\tif (pcolour >= 16)\n\t{\n\t\tR2D_ImageColours (SRGBA(((pcolour&0xff0000)>>16)/255.0f, ((pcolour&0xff00)>>8)/255.0f, (pcolour&0xff)/255.0f, 1.0));\n\t\tR2D_FillBlock (x, y, w, h);\n\t}\n\telse\n\t{\n\t\tR2D_ImagePaletteColour(Sbar_ColorForMap(pcolour), 1.0);\n\t\tR2D_FillBlock (x, y, w, h);\n\t}\n}\nstatic void Sbar_FillPCDark (float x, float y, float w, float h, unsigned int pcolour, float alpha)\n{\n\tif (pcolour >= 16)\n\t{\n\t\tR2D_ImageColours (SRGBA(((pcolour&0xff0000)>>16)/1024.0f, ((pcolour&0xff00)>>8)/1024.0f, (pcolour&0xff)/1024.0f, alpha));\n\t\tR2D_FillBlock (x, y, w, h);\n\t}\n\telse\n\t{\n\t\tR2D_ImagePaletteColour(Sbar_ColorForMap(pcolour)-1, alpha);\n\t\tR2D_FillBlock (x, y, w, h);\n\t}\n}\n\n\n/*\n=============\nSbar_itoa\n=============\n*/\nint Sbar_itoa (int num, char *buf)\n{\n\tchar\t*str;\n\tint\t\tpow10;\n\tint\t\tdig;\n\n\tstr = buf;\n\n\tif (num < 0)\n\t{\n\t\t*str++ = '-';\n\t\tnum = -num;\n\t}\n\n\tfor (pow10 = 10 ; num >= pow10 && pow10>=10; pow10 *= 10)\n\t;\n\n\tif (pow10 > 0)\n\tdo\n\t{\n\t\tpow10 /= 10;\n\t\tdig = num/pow10;\n\t\t*str++ = '0'+dig;\n\t\tnum -= dig*pow10;\n\t} while (pow10 != 1);\n\n\t*str = 0;\n\n\treturn str-buf;\n}\n\n\n/*\n=============\nSbar_DrawNum\n=============\n*/\nvoid Sbar_DrawNum (float x, float y, int num, int digits, int color)\n{\n\tchar\t\t\tstr[16];\n\tchar\t\t\t*ptr;\n\tint\t\t\t\tl, frame;\n#undef small\n\tint small=false;\n\n\tif (digits < 0)\n\t{\n\t\tsmall = true;\n\t\tdigits*=-1;\n\t}\n\n\tl = Sbar_itoa (num, str);\n\tptr = str;\n\tif (l > digits)\n\t\tptr += (l-digits);\n\n\tif (small)\n\t{\n\t\tif (l < digits)\n\t\t\tx += (digits-l)*8;\n\t\twhile (*ptr)\n\t\t{\n\t\t\tSbar_DrawCharacter(x, y, *ptr+18 - '0');\n\t\t\tptr++;\n\t\t\tx+=8;\n\t\t}\n\t\treturn;\n\t}\n\tif (l < digits)\n\t\tx += (digits-l)*24;\n\n\twhile (*ptr)\n\t{\n\t\tif (*ptr == '-')\n\t\t\tframe = STAT_MINUS;\n\t\telse\n\t\t\tframe = *ptr -'0';\n\n\t\tSbar_DrawPic (x, y, 24, 24, sb_nums[color][frame]);\n\t\tx += 24;\n\t\tptr++;\n\t}\n}\n\n#ifdef HEXEN2\nstatic void Sbar_Hexen2DrawNum (float x, float y, int num, int digits)\n{\n\tchar\t\t\tstr[12];\n\tchar\t\t\t*ptr;\n\tint\t\t\t\tl, frame;\n\n\tl = Sbar_itoa (num, str);\n\tptr = str;\n\tif (l > digits)\n\t\tptr += (l-digits);\n\n\t//hexen2 hud has it centered\n\tif (l < digits)\n\t\tx += ((digits-l)*13)/2;\n\n\twhile (*ptr)\n\t{\n\t\tif (*ptr == '-')\n\t\t\tframe = STAT_MINUS;\n\t\telse\n\t\t\tframe = *ptr -'0';\n\n\t\tSbar_DrawPic (x, y, 12, 16, sb_nums[0][frame]);\n\t\tx += 13;\n\t\tptr++;\n\t}\n}\n#endif\n\n#ifdef NQPROT\n//this stuff was added to the rerelease's ctf mod.\nint cl_ctfredscore;\nint cl_ctfbluescore;\nint cl_ctfflags;\nstatic void Sbar_CTFScores_f(void)\n{\t//issued via stuffcmds.\n\tcl_ctfredscore = atoi(Cmd_Argv(1));\n\tcl_ctfbluescore = atoi(Cmd_Argv(2));\n\tcl_ctfflags = atoi(Cmd_Argv(3)) | 0x100;\t//base|carried|dropped | base|carried|dropped\n}\nstatic void Sbar_DrawCTFScores(playerview_t *pv)\n{\n\tif (cl_ctfflags)\n\t{\n\t\tint i, x, y, sy;\n\t\tstatic struct\n\t\t{\n\t\t\tint colour;\n\t\t\tint *score;\n\t\t\tconst char *base, *held, *dropped;\t//at base, held, dropped.\n\t\t\tint shift;\n\t\t} team[] =\n\t\t{\n\t\t\t{4, &cl_ctfredscore, \"gfx/redf1.lmp\", \"gfx/redf2.lmp\", \"gfx/redf3.lmp\", 0},\n\t\t\t{13, &cl_ctfbluescore, \"gfx/bluef1.lmp\", \"gfx/bluef2.lmp\", \"gfx/bluef3.lmp\", 3},\n\t\t};\n\n\t\tx = sbar_rect.x + sbar_rect.width - (48+2);\n\t\tsy = sbar_rect.y + sbar_rect.height-sb_lines;\n\n\t\tif (!cl_sbar.value && sb_lines > 24 && scr_viewsize.value>=100 && !cl_hudswap.value)\n\t\t\tx -= 42;\t//QW hud nudges it in by 24 to not cover ammo.\n\n\t\tfor (i = 0, y=sy-17; i < countof(team); i++, y -= 18)\n\t\t{\n\t\t\tif (cl.players[pv->playernum].rbottomcolor == team[i].colour)\n\t\t\t\tSbar_FillPC (x-1, y-1, 48+2, 16+2, 12);\n\t\t\tSbar_FillPC (x+16, y, 32, 16, team[i].colour);\n\t\t}\n\t\tR2D_ImageColours(1.0, 1.0, 1.0, 1.0);\n\n\t\tfor (i = 0, y=sy-17; i < countof(team); i++, y -= 18)\n\t\t{\n\t\t\tint fl = (cl_ctfflags>>team[i].shift)&7;\n\t\t\tmpic_t *pic = NULL;\n\t\t\tif (fl == 1)\n\t\t\t\tpic = R2D_SafeCachePic(team[i].base);\n\t\t\telse if (fl == 2)\n\t\t\t\tpic = R2D_SafeCachePic(team[i].held);\n\t\t\telse if (fl == 4)\n\t\t\t\tpic = R2D_SafeCachePic(team[i].dropped);\n\t\t\tif (pic)\n\t\t\t\tR2D_ScalePic(x, y, 16, 16, pic);\n\t\t}\n\t\tfor (i = 0, y=sy-17; i < countof(team); i++, y -= 18)\n\t\t\tDraw_FunStringWidth (x+16, y+((16-8)/2), va(\"%i\", *team[i].score), 32, 2, false);\n\t}\n}\n#endif\n\n//=============================================================================\n\ntypedef struct {\n\tchar team[16+1];\n\tint frags;\n\tint players;\n\tint plow, phigh, ptotal;\n\tint topcolour, bottomcolour;\n\tqboolean ownteam;\n} team_t;\nstatic int\t\tplayerteam[MAX_CLIENTS];\nstatic team_t teams[MAX_CLIENTS];\nstatic int teamsort[MAX_CLIENTS];\nstatic int scoreboardteams;\nstatic qboolean consistentteams;\t//can hide the 'team' displays when colours are enough.\n\nstatic struct\n{\n\tunsigned char upper;\n\tshort frags;\n} nqteam[14];\nvoid Sbar_PQ_Team_New(unsigned int lower, unsigned int upper)\n{\n\tif (lower >= 14)\n\t\treturn;\n\tnqteam[lower].upper = upper;\n}\nvoid Sbar_PQ_Team_Frags(unsigned int lower, int frags)\n{\n\tif (lower >= 14)\n\t\treturn;\n\tnqteam[lower].frags = frags;\n}\nvoid Sbar_PQ_Team_Reset(void)\n{\n\tmemset(nqteam, 0, sizeof(nqteam));\n}\n\n\n#endif\n\nunsigned int\tSbar_ColorForMap (unsigned int m)\n{\n\tif (m >= 16)\n\t\treturn m;\n\n\tm = (m > 13) ? 13 : m;\n\n\tm *= 16;\n\treturn m < 128 ? m + 8 : m + 8;\n}\n\nint\t\tscoreboardlines;\nint\t\tfragsort[MAX_CLIENTS];\n/*\n===============\nSbar_SortFrags\n===============\n*/\nvoid Sbar_SortFrags (qboolean includespec, qboolean doteamsort)\n{\n\tint\t\ti, j, k;\n\n\tif (!cl.teamplay)\n\t\tdoteamsort = false;\n\n// sort by frags\n\tscoreboardlines = 0;\n\tfor (i=0 ; i<MAX_CLIENTS ; i++)\n\t{\n\t\tif (cl.players[i].name[0] &&\n\t\t\t(!cl.players[i].spectator || includespec))\n\t\t{\n\t\t\tfragsort[scoreboardlines] = i;\n\t\t\tscoreboardlines++;\n\t\t}\n\t}\n\n\tfor (i=0 ; i<scoreboardlines ; i++)\n\t\tfor (j = i + 1; j < scoreboardlines; j++)\n\t\t{\n\t\t\tint w1, w2;\n#ifdef QUAKEHUD\n\t\t\tint t1 = playerteam[fragsort[i]];\n\t\t\tint t2 = playerteam[fragsort[j]];\n\n\t\t\t//teams are already sorted by frags\n\t\t\tw1 = t1<0?-999:-teamsort[t1];\n\t\t\tw2 = t2<0?-999:-teamsort[t2];\n\t\t\t//okay, they're on the same team then? go ahead and sort by personal frags\n\t\t\tif (!doteamsort || w1 == w2)\n#endif\n\t\t\t{\n\t\t\t\tw1 = cl.players[fragsort[i]].spectator==1?-999:cl.players[fragsort[i]].frags;\n\t\t\t\tw2 = cl.players[fragsort[j]].spectator==1?-999:cl.players[fragsort[j]].frags;\n\t\t\t}\n\t\t\tif (w1 < w2)\n\t\t\t{\n\t\t\t\tk = fragsort[i];\n\t\t\t\tfragsort[i] = fragsort[j];\n\t\t\t\tfragsort[j] = k;\n\t\t\t}\n\t\t}\n}\n\n#ifdef QUAKEHUD\n\nvoid Sbar_SortTeams (playerview_t *pv)\n{\n\tint\t\t\t\ti, j, k;\n\tplayer_info_t\t*s;\n\tchar t[16+1];\n\tint ownnum;\n\tunsigned int seen;\n\tchar *end;\n\n// request new ping times every two second\n\tscoreboardteams = 0;\n\tconsistentteams = false;\n\n\tif (!cl.teamplay)\n\t\treturn;\n\n\tmemset(teams, 0, sizeof(teams));\n// sort the teams\n\tfor (i = 0; i < cl.allocated_client_slots; i++)\n\t\tteams[i].plow = 999;\n\n\townnum = Sbar_PlayerNum(pv);\n\n\tfor (i = 0; i < cl.allocated_client_slots; i++)\n\t{\n\t\tplayerteam[i] = -1;\n\t\ts = &cl.players[i];\n\t\tif (!s->name[0] || s->spectator)\n\t\t\tcontinue;\n\n\t\t// find his team in the list\n\t\tQ_strncpyz(t, s->team, sizeof(t));\n\t\tif (!t[0])\n\t\t\tcontinue; // not on team\n\t\tif (cls.protocol == CP_NETQUAKE)\n\t\t{\n\t\t\tk = Sbar_BottomColour(s);\n\t\t\tif (!k)\t//team 0 = spectator\n\t\t\t\tcontinue;\n\t\t\tfor (j = 0; j < scoreboardteams; j++)\n\t\t\t\tif (teams[j].bottomcolour == k)\n\t\t\t\t{\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tfor (j = 0; j < scoreboardteams; j++)\n\t\t\t\tif (!strcmp(teams[j].team, t))\n\t\t\t\t{\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t}\n\n\t\t/*if (cl.teamfortress)\n\t\t{\n\t\t\tteams[j].topcolour = teams[j].bottomcolour = TF_TeamToColour(t);\n\t\t}\n\t\telse*/ if (j == scoreboardteams || i == ownnum)\n\t\t{\n\t\t\tteams[j].topcolour = Sbar_TopColour(s);\n\t\t\tteams[j].bottomcolour = Sbar_BottomColour(s);\n\t\t}\n\n\t\tif (j == scoreboardteams)\n\t\t{ // create a team for this player\n\t\t\tscoreboardteams++;\n\t\t\tstrcpy(teams[j].team, t);\n\t\t}\n\n\t\tplayerteam[i] = j;\n\t\tteams[j].frags += s->frags;\n\t\tteams[j].players++;\n\n\t\tif (teams[j].plow > s->ping)\n\t\t\tteams[j].plow = s->ping;\n\t\tif (teams[j].phigh < s->ping)\n\t\t\tteams[j].phigh = s->ping;\n\t\tteams[j].ptotal += s->ping;\n\t}\n\n\t// sort\n\tfor (i = 0; i < scoreboardteams; i++)\n\t\tteamsort[i] = i;\n\n\t// good 'ol bubble sort\n\tfor (i = 0; i < scoreboardteams - 1; i++)\n\t\tfor (j = i + 1; j < scoreboardteams; j++)\n\t\t\tif (teams[teamsort[i]].frags < teams[teamsort[j]].frags)\n\t\t\t{\n\t\t\t\tk = teamsort[i];\n\t\t\t\tteamsort[i] = teamsort[j];\n\t\t\t\tteamsort[j] = k;\n\t\t\t}\n\n\tseen = 0;\n\tfor (i = 0; i < scoreboardteams; i++)\n\t{\n\t\t//make sure the colour is one of quake's valid non-fullbright colour ranges.\n\t\tif (teams[i].bottomcolour < 0 || teams[i].bottomcolour > 13)\n\t\t\treturn;\n\n\t\t//don't allow multiple teams with the same colour but different names.\n\t\tif (seen & (1<<teams[i].bottomcolour))\n\t\t\treturn;\n\t\tseen |= (1<<teams[i].bottomcolour);\n\n\t\tif (*teams[i].team == 't')\n\t\t{\t//fte servers use t%i for nq team names\n\t\t\tif (teams[i].bottomcolour != strtoul(teams[i].team+1, &end, 10) || *end)\n\t\t\t\treturn;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (teams[i].bottomcolour != strtoul(teams[i].team, &end, 10) || *end)\n\t\t\t{\n\t\t\t\tif (teams[i].bottomcolour == 4 && !strcasecmp(teams[i].team, \"red\"))\n\t\t\t\t\t;\n\t\t\t\telse if (teams[i].bottomcolour == 13 && !strcasecmp(teams[i].team, \"blue\"))\n\t\t\t\t\t;\n\t\t\t\telse\n\t\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n\n\t//if we got this far, we had no dupe colours and every name checked out.\n\tconsistentteams = true;\n}\n\n/*\n===============\nSbar_SoloScoreboard\n===============\n*/\nvoid Sbar_SoloScoreboard (void)\n{\n\tint l;\n\tfloat time;\n\tchar\tstr[80];\n\tint\t\tminutes, seconds, tens, units;\n\n\tSbar_DrawPic (0, 0, 320, 24, sb_scorebar);\n\n\t// time\n\ttime = cl.servertime;\n\tminutes = time / 60;\n\tseconds = time - 60*minutes;\n\ttens = seconds / 10;\n\tunits = seconds - 10*tens;\n\tsprintf (str,\"Time :%3i:%i%i\", minutes, tens, units);\n\tSbar_DrawString (184, 4, str);\n\n\t// draw level name\n\tl = strlen (cl.levelname);\n\tSbar_DrawString (232 - l*4, 12, cl.levelname);\n}\n\nvoid Sbar_CoopScoreboard (void)\n{\n\tfloat time;\n\tchar\tstr[80];\n\tint\t\tminutes, seconds, tens, units;\n\tint\t\tl;\n\tint pnum = 0;\t//doesn't matter, should all be the same\n\n\tsprintf (str,\"Monsters:%3i /%3i\", cl.playerview[pnum].stats[STAT_MONSTERS], cl.playerview[pnum].stats[STAT_TOTALMONSTERS]);\n\tSbar_DrawString (8, 4, str);\n\n\tsprintf (str,\"Secrets :%3i /%3i\", cl.playerview[pnum].stats[STAT_SECRETS], cl.playerview[pnum].stats[STAT_TOTALSECRETS]);\n\tSbar_DrawString (8, 12, str);\n\n// time\n\ttime = cl.servertime;\n\tminutes = time / 60;\n\tseconds = time - 60*minutes;\n\ttens = seconds / 10;\n\tunits = seconds - 10*tens;\n\tsprintf (str,\"Time :%3i:%i%i\", minutes, tens, units);\n\tSbar_DrawString (184, 4, str);\n\n// draw level name\n\tl = strlen (cl.levelname);\n\tSbar_DrawString (232 - l*4, 12, cl.levelname);\n}\n\n//=============================================================================\n\n/*\n===============\nSbar_DrawInventory\n===============\n*/\nvoid Sbar_DrawInventory (playerview_t *pv)\n{\n\tint\t\ti;\n\tchar\t  num[6];\n\tconchar_t numc[6];\n\tfloat\ttime;\n\tint\t\tflashon;\n\tqboolean\theadsup;\n\tqboolean\thudswap;\n\tfloat\twleft, wtop;\n\tapic_t *ibar;\n\n\theadsup = !(cl_sbar.value || (scr_viewsize.value<100));\n\thudswap = cl_hudswap.value; // Get that nasty float out :)\n\n\t//coord for the left of the weapons, with hud\n\twleft = hudswap?sbar_rect_left:(sbar_rect.width-24);\n\twtop = -180;//68-(7-0)*16;\n\tif (sbar_hipnotic)\n\t\twtop -= 16*2;\n\n\tif (sbar_rogue)\n\t{\n\t\tif ( pv->stats[STAT_ACTIVEWEAPON] >= RIT_LAVA_NAILGUN )\n\t\t\tibar = rsb_invbar[0];\n\t\telse\n\t\t\tibar = rsb_invbar[1];\n\t}\n\telse\n\t\tibar = sb_ibar;\n\n\tif (!headsup)\n\t{\n\t\tif (cl_sbar.ival != 1 && scr_viewsize.value >= 100)\n\t\t\tR2D_ImageColours (cl_sbaralpha.value, cl_sbaralpha.value, cl_sbaralpha.value, cl_sbaralpha.value);\n\t\tSbar_DrawPic (0, -24, 320, 24, ibar);\n\t\tR2D_ImageColours (1, 1, 1, 1);\n\t}\n\n// weapons\n\tfor (i=0 ; i<7 ; i++)\n\t{\n\t\tif (pv->stats[STAT_ITEMS] & (IT_SHOTGUN<<i) )\n\t\t{\n\t\t\ttime = pv->item_gettime[i];\n\t\t\tflashon = (int)((cl.time - time)*10);\n\t\t\tif (flashon < 0)\n\t\t\t\tflashon = 0;\n\t\t\tif (flashon >= 10)\n\t\t\t{\n\t\t\t\tif ( pv->stats[STAT_ACTIVEWEAPON] == (IT_SHOTGUN<<i)  )\n\t\t\t\t\tflashon = 1;\n\t\t\t\telse\n\t\t\t\t\tflashon = 0;\n\t\t\t}\n\t\t\telse\n\t\t\t\tflashon = (flashon%5) + 2;\n\n\t\t\tif (headsup)\n\t\t\t{\n\t\t\t\tif (i || sbar_rect.height>200)\n\t\t\t\t\tSbar_DrawSubPic (wleft,wtop+i*16, 24,16, sb_weapons[flashon][i],0,0,(i==6)?(sbar_hipnotic?32:48):24, 16);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tSbar_DrawPic (i*24, -16, (i==6)?(sbar_hipnotic?32:48):24, 16, sb_weapons[flashon][i]);\n\t\t\t}\n\n\t\t\tif (flashon > 1)\n\t\t\t\tsb_updates = 0;\t\t// force update to remove flash\n\t\t}\n\t}\n\n\tif (sbar_hipnotic)\n\t{\n\t\tint grenadeflashing=0;\n\t\tfor (i=0 ; i<4 ; i++)\n\t\t{\n\t\t\tif (pv->stats[STAT_ITEMS] & (1<<hipweapons[i]))\n\t\t\t{\n\t\t\t\ttime = pv->item_gettime[hipweapons[i]];\n\t\t\t\tflashon = (int)((cl.time - time)*10);\n\t\t\t\tif (flashon >= 10)\n\t\t\t\t{\n\t\t\t\t\tif (pv->stats[STAT_ACTIVEWEAPON] == (1<<hipweapons[i]))\n\t\t\t\t\t\tflashon = 1;\n\t\t\t\t\telse\n\t\t\t\t\t\tflashon = 0;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tflashon = (flashon%5) + 2;\n\n\t\t\t\t// check grenade launcher\n\t\t\t\tif (i==2)\n\t\t\t\t{\n\t\t\t\t\tif (pv->stats[STAT_ITEMS] & HIT_PROXIMITY_GUN)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (flashon)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tgrenadeflashing = 1;\n\t\t\t\t\t\t\tSbar_DrawPic (headsup?wleft:96, headsup?wtop+4*16:-16, 24, 16, hsb_weapons[flashon][2]);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse if (i==3)\n\t\t\t\t{\n\t\t\t\t\tif (pv->stats[STAT_ITEMS] & (IT_SHOTGUN<<4))\n\t\t\t\t\t{\n\t\t\t\t\t\tif (flashon && !grenadeflashing)\n\t\t\t\t\t\t\tSbar_DrawPic (headsup?wleft:96, headsup?wtop+4*16:-16, 24, 16, hsb_weapons[flashon][3]);\n\t\t\t\t\t\telse if (!grenadeflashing)\n\t\t\t\t\t\t\tSbar_DrawPic (headsup?wleft:96, headsup?wtop+4*16:-16, 24, 16, hsb_weapons[0][3]);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\tSbar_DrawPic (headsup?wleft:96, headsup?wtop+4*16:-16, 24, 16, hsb_weapons[flashon][4]);\n\t\t\t\t}\n//\t\t\t\telse if (i == 1)\n//\t\t\t\t\tSbar_DrawPic (176 + (i*24), -16, 24, 16, hsb_weapons[flashon][i]);\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tif (headsup)\n\t\t\t\t\t\tSbar_DrawPic (headsup?wleft:(176 + (i*24)), headsup?wtop+(i+7)*16:-16, 24, 16, hsb_weapons[flashon][i]);\n\t\t\t\t\telse\n\t\t\t\t\t\tSbar_DrawPic (headsup?wleft:(176 + (i*24)), headsup?wtop+(i+7)*16:-16, 24, 16, hsb_weapons[flashon][i]);\n\t\t\t\t}\n\t\t\t\tif (flashon > 1)\n\t\t\t\t\tsb_updates = 0;      // force update to remove flash\n\t\t\t}\n\t\t}\n\t}\n\n\tif (sbar_rogue)\n\t{\n    // check for powered up weapon.\n\t\tif ( pv->stats[STAT_ACTIVEWEAPON] >= RIT_LAVA_NAILGUN )\n\t\t{\n\t\t\tfor (i=0;i<5;i++)\n\t\t\t{\n\t\t\t\tif (pv->stats[STAT_ACTIVEWEAPON] == (RIT_LAVA_NAILGUN << i))\n\t\t\t\t{\n\t\t\t\t\tif (headsup)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (sbar_rect.height>200)\n\t\t\t\t\t\t\tSbar_DrawSubPic ((hudswap) ? 0 : (sbar_rect.width-24),-68-(5-i)*16, 24, 16, rsb_weapons[i],0,0,((i==4)?48:24),16);\n\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\tSbar_DrawPic ((i+2)*24, -16, (i==4)?48:24, 16, rsb_weapons[i]);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tflashon = 0;\n// items\n\tfor (i=(sbar_hipnotic?2:0) ; i<6 ; i++)\n\t{\n\t\tif (pv->stats[STAT_ITEMS] & (1<<(17+i)))\n\t\t{\n\t\t\ttime = pv->item_gettime[17+i];\n\t\t\tif (time &&\ttime > cl.time - 2 && flashon )\n\t\t\t{\t// flash frame\n\t\t\t\tsb_updates = 0;\n\t\t\t}\n\t\t\telse\n\t\t\t\tSbar_DrawPic (192 + i*16, -16, 16, 16, sb_items[i]);\n\t\t\tif (time &&\ttime > cl.time - 2)\n\t\t\t\tsb_updates = 0;\n\t\t}\n\t}\n\n\tif (sbar_hipnotic)\n\t{\n\t\tfor (i=0 ; i<2 ; i++)\n\t\t{\n\t\t\tif (pv->stats[STAT_ITEMS] & (1<<(24+i)))\n\t\t\t{\n\t\t\t\ttime = pv->item_gettime[24+i];\n\t\t\t\tif (time && time > cl.time - 2 && flashon )\t\t// flash frame\n\t\t\t\t\tsb_updates = 0;\n\t\t\t\telse\n\t\t\t\t\tSbar_DrawPic (288 + i*16, -16, 16, 16, hsb_items[i]);\n\t\t\t\tif (time && time > cl.time - 2)\n\t\t\t\t\tsb_updates = 0;\n\t\t\t}\n\t\t}\n\t}\n\n\tif (sbar_rogue)\n\t{\n\t// new rogue items\n\t\tfor (i=0 ; i<2 ; i++)\n\t\t{\n\t\t\tif (pv->stats[STAT_ITEMS] & (1<<(29+i)))\n\t\t\t{\n\t\t\t\ttime = pv->item_gettime[29+i];\n\t\t\t\tif (time &&\ttime > cl.time - 2 && flashon )\t// flash frame\n\t\t\t\t\tsb_updates = 0;\n\t\t\t\telse\n\t\t\t\t\tSbar_DrawPic (288 + i*16, -16, 16, 16, rsb_items[i]);\n\t\t\t\tif (time &&\ttime > cl.time - 2)\n\t\t\t\t\tsb_updates = 0;\n\t\t\t}\n\t\t}\n\t}\n\telse\n\t{\n\t// sigils\n\t\tfor (i=0 ; i<4 ; i++)\n\t\t{\n\t\t\tif (pv->stats[STAT_ITEMS] & (1<<(28+i)))\n\t\t\t{\n\t\t\t\ttime = pv->item_gettime[28+i];\n\t\t\t\tif (time &&\ttime > cl.time - 2 && flashon )\n\t\t\t\t{\t// flash frame\n\t\t\t\t\tsb_updates = 0;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tSbar_DrawPic (320-32 + i*8, -16, 8, 16, sb_sigil[i]);\n\t\t\t\tif (time &&\ttime > cl.time - 2)\n\t\t\t\t\tsb_updates = 0;\n\t\t\t}\n\t\t}\n\t}\n\n\t// ammo counts\n\tif (headsup)\n\t{\n\t\tfor (i=0 ; i<4 ; i++)\n\t\t\tSbar_DrawSubPic((hudswap) ? sbar_rect_left : (sbar_rect.width-42), -24 - (4-i)*11, 42, 11, ibar, 3+(i*48), 0, 320, 24);\n\t}\n\tfor (i=0 ; i<4 ; i++)\n\t{\n\t\tsnprintf (num, sizeof(num), \"%4i\", pv->stats[STAT_SHELLS+i] );\n\t\tnumc[0] = CON_WHITEMASK|0xe000|((num[0]!=' ')?(num[0] + 18-'0'):' ');\n\t\tnumc[1] = CON_WHITEMASK|0xe000|((num[1]!=' ')?(num[1] + 18-'0'):' ');\n\t\tnumc[2] = CON_WHITEMASK|0xe000|((num[2]!=' ')?(num[2] + 18-'0'):' ');\n\t\tnumc[3] = CON_WHITEMASK|0xe000|((num[3]!=' ')?(num[3] + 18-'0'):' ');\n\t\tnumc[4] = 0;\n\t\tif (headsup)\n\t\t{\n\t\t\tSbar_DrawExpandedString(((hudswap) ? sbar_rect_left+3 : (sbar_rect.width-39)) - 4, -24 - (4-i)*11, numc);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tSbar_DrawExpandedString((6*i+1)*8 - 2 - 4, -24, numc);\n\t\t}\n\t}\n}\n\nstatic qboolean PointInBox(float px, float py, float x, float y, float w, float h)\n{\n\tif (px >= x && px < x+w)\n\t\tif (py >= y && py < y+h)\n\t\t\treturn true;\n\treturn false;\n}\nint Sbar_TranslateHudClick(void)\n{\n\tint i;\n\tfloat vx = mousecursor_x - sbar_rect.x;\n\tfloat vy = mousecursor_y - (sbar_rect.y + (sbar_rect.height-SBAR_HEIGHT));\n\n\tqboolean headsup = !(cl_sbar.value || (scr_viewsize.value<100));\n\tqboolean hudswap = cl_hudswap.value; // Get that nasty float out :)\n\n\t//inventory. clicks do specific-weapon impulses.\n\tif (sb_lines > 24)\n\t{\n\t\tfor (i=0 ; i<7 ; i++)\n\t\t{\n\t\t\tif (headsup)\n\t\t\t{\n\t\t\t\tif (i || sbar_rect.height>200)\n\t\t\t\t\tif (PointInBox (vx, vy, (hudswap) ? 0 : (sbar_rect.width-24),-68-(7-i)*16, 24,16))\n\t\t\t\t\t\treturn '2' + i;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (PointInBox (vx, vy, i*24, -16, (i==6)?48:24, 16))\n\t\t\t\t\treturn '2' + i;\n\t\t\t}\n\t\t}\n\t}\n\n\t//armour. trigger backtick, to toggle the console (which enables the on-screen keyboard on android).\n\tif (PointInBox (vx, vy, 0, 0, 96, 24))\n\t\treturn '`';\n\t//face. do showscores.\n\tif (PointInBox (vx, vy, 112, 0, 96, 24))\n\t\treturn K_TAB;\n\t//currentammo+icon. trigger '/' binding, which defaults to weapon-switch (impulse 10)\n\tif (PointInBox (vx, vy, 224, 0, 96, 24))\n\t\treturn '/';\n\n\treturn 0;\n}\n\n//=============================================================================\n\n/*\n===============\nSbar_DrawFrags\n===============\n*/\nvoid Sbar_DrawFrags (playerview_t *pv)\n{\n\tint\t\t\t\ti, k, l;\n\tint\t\t\t\ttop, bottom;\n\tfloat\t\t\tx, y;\n\tint\t\t\t\tf;\n\tint\t\t\t\townnum;\n\tchar\t\t\tnum[12];\n\tplayer_info_t\t*s;\n\n\tSbar_SortFrags (false, false);\n\n\townnum = Sbar_PlayerNum(pv);\n\n// draw the text\n\tl = scoreboardlines <= 4 ? scoreboardlines : 4;\n\n\tx = 23;\n//\txofs = (sbar_rect.width - 320)>>1;\n\ty = sbar_rect.height - SBAR_HEIGHT - 23;\n\n\tfor (i=0 ; i<l ; i++)\n\t{\n\t\tk = fragsort[i];\n\t\ts = &cl.players[k];\n\t\tif (!s->name[0])\n\t\t\tcontinue;\n\t\tif (s->spectator)\n\t\t\tcontinue;\n\n\t// draw background\n\t\ttop = Sbar_TopColour(s);\n\t\tbottom = Sbar_BottomColour(s);\n\n\n//\t\tDraw_Fill (xofs + x*8 + 10, y, 28, 4, top);\n//\t\tDraw_Fill (xofs + x*8 + 10, y+4, 28, 3, bottom);\n\t\tSbar_FillPC (sbar_rect.x+x*8 + 10, sbar_rect.y+y, 28, 4, top);\n\t\tSbar_FillPC (sbar_rect.x+x*8 + 10, sbar_rect.y+y+4, 28, 3, bottom);\n\n\t\tR2D_ImageColours(1, 1, 1, 1);\n\n\t// draw number\n\t\tf = s->frags;\n\t\tsprintf (num, \"%3i\",f);\n\n\t\tSbar_DrawCharacter ( (x+1)*8 , -24, num[0]);\n\t\tSbar_DrawCharacter ( (x+2)*8 , -24, num[1]);\n\t\tSbar_DrawCharacter ( (x+3)*8 , -24, num[2]);\n\n\t\tif (k == ownnum)\n\t\t{\n\t\t\tSbar_DrawCharacter (x*8+2, -24, 16);\n\t\t\tSbar_DrawCharacter ( (x+4)*8-4, -24, 17);\n\t\t}\n\t\tx+=4;\n\t}\n\tR2D_ImageColours(1.0, 1.0, 1.0, 1.0);\n}\n\n//=============================================================================\n\n\n/*\n===============\nSbar_DrawFace\n===============\n*/\nvoid Sbar_DrawFace (playerview_t *pv)\n{\n\tint\t\tf, anim;\n\n\tif ( (pv->stats[STAT_ITEMS] & (IT_INVISIBILITY | IT_INVULNERABILITY) )\n\t== (IT_INVISIBILITY | IT_INVULNERABILITY) )\n\t{\n\t\tSbar_DrawPic (112, 0, 24, 24, sb_face_invis_invuln);\n\t\treturn;\n\t}\n\tif (pv->stats[STAT_ITEMS] & IT_QUAD)\n\t{\n\t\tSbar_DrawPic (112, 0, 24, 24, sb_face_quad );\n\t\treturn;\n\t}\n\tif (pv->stats[STAT_ITEMS] & IT_INVISIBILITY)\n\t{\n\t\tSbar_DrawPic (112, 0, 24, 24, sb_face_invis );\n\t\treturn;\n\t}\n\tif (pv->stats[STAT_ITEMS] & IT_INVULNERABILITY)\n\t{\n\t\tSbar_DrawPic (112, 0, 24, 24, sb_face_invuln);\n\t\treturn;\n\t}\n\n\tif (pv->stats[STAT_HEALTH] >= 100)\n\t\tf = 4;\n\telse\n\t\tf = pv->stats[STAT_HEALTH] / 20;\n\n\tif (f < 0)\n\t\tf=0;\n\n\tif (cl.time <= pv->faceanimtime)\n\t{\n\t\tanim = 1;\n\t\tsb_updates = 0;\t\t// make sure the anim gets drawn over\n\t}\n\telse\n\t\tanim = 0;\n\tSbar_DrawPic (112, 0, 24, 24, sb_faces[f][anim]);\n}\n\n/*\n=============\nSbar_DrawNormal\n=============\n*/\nvoid Sbar_DrawNormal (playerview_t *pv)\n{\n\tif (cl_sbar.value || (scr_viewsize.value<100))\n\t{\n\t\tif (cl_sbar.ival != 1 && scr_viewsize.value >= 100)\n\t\t\tR2D_ImageColours (cl_sbaralpha.value, cl_sbaralpha.value, cl_sbaralpha.value, cl_sbaralpha.value);\n\t\tSbar_DrawPic (0, 0, 320, 24, sb_sbar);\n\t\tR2D_ImageColours (1, 1, 1, 1);\n\t}\n\n\t//hipnotic's keys appear to the right of health.\n\tif (sbar_hipnotic)\n\t{\n\t\tif (pv->stats[STAT_ITEMS] & IT_KEY1)\n\t\t\tSbar_DrawPic (209, 3, 16, 9, sb_items[0]);\n\t\tif (pv->stats[STAT_ITEMS] & IT_KEY2)\n\t\t\tSbar_DrawPic (209, 12, 16, 9, sb_items[1]);\n\t}\n\n// armor\n\tif (pv->stats[STAT_ITEMS] & IT_INVULNERABILITY)\n\t{\n\t\tSbar_DrawNum (24, 0, 666, 3, 1);\n\t\tSbar_DrawMPic (0, 0, 24, 24, draw_disc);\n\t}\n\telse\n\t{\n\t\tif (sbar_rogue)\n\t\t{\n\t\t\tSbar_DrawNum (24, 0, pv->stats[STAT_ARMOR], 3,\n\t\t\t\tpv->stats[STAT_ARMOR] <= 25);\n\t\t\tif (pv->stats[STAT_ITEMS] & RIT_ARMOR3)\n\t\t\t\tSbar_DrawPic (0, 0, 24, 24, sb_armor[2]);\n\t\t\telse if (pv->stats[STAT_ITEMS] & RIT_ARMOR2)\n\t\t\t\tSbar_DrawPic (0, 0, 24, 24, sb_armor[1]);\n\t\t\telse if (pv->stats[STAT_ITEMS] & RIT_ARMOR1)\n\t\t\t\tSbar_DrawPic (0, 0, 24, 24, sb_armor[0]);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tSbar_DrawNum (24, 0, pv->stats[STAT_ARMOR], 3,\n\t\t\t\tpv->stats[STAT_ARMOR] <= 25);\n\t\t\tif (pv->stats[STAT_ITEMS] & IT_ARMOR3)\n\t\t\t\tSbar_DrawPic (0, 0, 24, 24, sb_armor[2]);\n\t\t\telse if (pv->stats[STAT_ITEMS] & IT_ARMOR2)\n\t\t\t\tSbar_DrawPic (0, 0, 24, 24, sb_armor[1]);\n\t\t\telse if (pv->stats[STAT_ITEMS] & IT_ARMOR1)\n\t\t\t\tSbar_DrawPic (0, 0, 24, 24, sb_armor[0]);\n\t\t}\n\t}\n\n// face\n\tSbar_DrawFace (pv);\n\n// health\n\tSbar_DrawNum (136, 0, pv->stats[STAT_HEALTH], 3\n\t, pv->stats[STAT_HEALTH] <= 25);\n\n// ammo icon\n\tif (sbar_rogue)\n\t{\n\t\tif (pv->stats[STAT_ITEMS] & RIT_SHELLS)\n\t\t\tSbar_DrawPic (224, 0, 24, 24, sb_ammo[0]);\n\t\telse if (pv->stats[STAT_ITEMS] & RIT_NAILS)\n\t\t\tSbar_DrawPic (224, 0, 24, 24, sb_ammo[1]);\n\t\telse if (pv->stats[STAT_ITEMS] & RIT_ROCKETS)\n\t\t\tSbar_DrawPic (224, 0, 24, 24, sb_ammo[2]);\n\t\telse if (pv->stats[STAT_ITEMS] & RIT_CELLS)\n\t\t\tSbar_DrawPic (224, 0, 24, 24, sb_ammo[3]);\n\t\telse if (pv->stats[STAT_ITEMS] & RIT_LAVA_NAILS)\n\t\t\tSbar_DrawPic (224, 0, 24, 24, rsb_ammo[0]);\n\t\telse if (pv->stats[STAT_ITEMS] & RIT_PLASMA_AMMO)\n\t\t\tSbar_DrawPic (224, 0, 24, 24, rsb_ammo[1]);\n\t\telse if (pv->stats[STAT_ITEMS] & RIT_MULTI_ROCKETS)\n\t\t\tSbar_DrawPic (224, 0, 24, 24, rsb_ammo[2]);\n\t}\n\telse\n\t{\n\t\tif (pv->stats[STAT_ITEMS] & IT_SHELLS)\n\t\t\tSbar_DrawPic (224, 0, 24, 24, sb_ammo[0]);\n\t\telse if (pv->stats[STAT_ITEMS] & IT_NAILS)\n\t\t\tSbar_DrawPic (224, 0, 24, 24, sb_ammo[1]);\n\t\telse if (pv->stats[STAT_ITEMS] & IT_ROCKETS)\n\t\t\tSbar_DrawPic (224, 0, 24, 24, sb_ammo[2]);\n\t\telse if (pv->stats[STAT_ITEMS] & IT_CELLS)\n\t\t\tSbar_DrawPic (224, 0, 24, 24, sb_ammo[3]);\n\t}\n\n\tSbar_DrawNum (248, 0, pv->stats[STAT_AMMO], 3\n\t, pv->stats[STAT_AMMO] <= 10);\n}\n\nqboolean Sbar_ShouldDraw (playerview_t *pv)\n{\n#ifdef TEXTEDITOR\n\textern qboolean editoractive;\n#endif\n\tqboolean headsup;\n\n\tif (scr_con_current == vid.height)\n\t\treturn false;\t\t// console is full screen\n\n#ifdef TEXTEDITOR\n\tif (editoractive)\n\t\treturn false;\n#endif\n\n\theadsup = !(cl_sbar.value || (scr_viewsize.value<100));\n\tif ((sb_updates >= vid.numpages) && !headsup)\n\t\treturn false;\n\n\treturn true;\n}\n\nvoid Sbar_DrawScoreboard (playerview_t *pv)\n{\n\tqboolean isdead;\n\n\tif (cls.protocol == CP_QUAKE2)\n\t\treturn;\n\n\tif (Key_Dest_Has(~kdm_game))\n\t\treturn;\n\n#ifdef CSQC_DAT\n\tif (CSQC_DrawScores(pv))\n\t\treturn;\n#endif\n\n#ifndef CLIENTONLY\n\t/*no scoreboard in single player (if you want bots, set deathmatch)*/\n\tif (sv.state && sv.allocated_client_slots == 1)\n\t{\n\t\treturn;\n\t}\n#endif\n\n\tisdead = false;\n\tif (pv->spectator && cls.demoplayback == DPB_MVD)\n\t{\n\t\tint t = pv->cam_spec_track;\n\t\tif (t >= 0 && CAM_ISLOCKED(pv) && cl.players[t].statsf[STAT_HEALTH] <= 0)\n\t\t\tisdead = true;\n\t}\n\telse if (!pv->spectator && pv->statsf[STAT_HEALTH] <= 0)\n\t\tisdead = true;\n\n\tif (isdead)// && !cl.spectator)\n\t{\n\t\tif (cl.teamplay > 0 && !pv->sb_showscores)\n\t\t\tSbar_TeamOverlay(pv);\n\t\telse\n\t\t\tSbar_DeathmatchOverlay (pv, 0);\n\t}\n\telse if (pv->sb_showscores)\n\t\tSbar_DeathmatchOverlay (pv, 0);\n\telse if (pv->sb_showteamscores)\n\t\tSbar_TeamOverlay(pv);\n\telse\n\t\treturn;\n\n\tsb_updates = 0;\n}\n\n\n#ifdef HEXEN2\nstatic void Sbar_Hexen2DrawActiveStuff(playerview_t *pv)\n{\n\tint x = r_refdef.grect.x + r_refdef.grect.width;\n\tint y = 0;\n\n\t//rings are vertical...\n\tif (pv->stats[STAT_H2_RINGS_ACTIVE] & 8)\n\t{\t//turning...\n\t\tR2D_ScalePic(x-32, r_refdef.grect.y+y, 32, 32, R2D_SafeCachePic(va(\"gfx/rngtrn%d.lmp\", ((int)(cl.time*16)%15)+1)));\n\t\ty += 32;\n\t}\n\tif (pv->stats[STAT_H2_RINGS_ACTIVE] & 2)\n\t{\t//water breathing\n\t\tR2D_ScalePic(x-32, r_refdef.grect.y+y, 32, 32, R2D_SafeCachePic(va(\"gfx/rngwtr%d.lmp\", ((int)(cl.time*16)%15)+1)));\n\t\ty += 32;\n\t}\n\tif (pv->stats[STAT_H2_RINGS_ACTIVE] & 1)\n\t{\t//flight\n\t\tR2D_ScalePic(x-32, r_refdef.grect.y+y, 32, 32, R2D_SafeCachePic(va(\"gfx/rngfly%d.lmp\", ((int)(cl.time*16)%15)+1)));\n\t\ty += 32;\n\t}\n\n\tif (y)\t//if we drew the rings column, move artifacts over so they don't fight\n\t\tx -= 50;\n\n\t//artifacts are horizontal (without stomping on rings)...\n\tif (pv->stats[STAT_H2_ARTIFACT_ACTIVE] & 4)\n\t{\t//tome of power\n\t\tx -= 32;\n\t\tR2D_ScalePic(x, r_refdef.grect.y, 32, 32, R2D_SafeCachePic(va(\"gfx/pwrbook%d.lmp\", ((int)(cl.time*16)%15)+1)));\n\t\tx -= 18;\n\t}\n\tif (pv->stats[STAT_H2_ARTIFACT_ACTIVE] & 1)\n\t{\t//boots\n\t\tx -= 32;\n\t\tR2D_ScalePic(x, r_refdef.grect.y, 32, 32, R2D_SafeCachePic(va(\"gfx/durhst%d.lmp\", ((int)(cl.time*16)%15)+1)));\n\t\tx -= 18;\n\t}\n\tif (pv->stats[STAT_H2_ARTIFACT_ACTIVE] & 2)\n\t{\t//invincibility\n\t\tx -= 32;\n\t\tR2D_ScalePic(x, r_refdef.grect.y, 32, 32, R2D_SafeCachePic(va(\"gfx/durshd%d.lmp\", ((int)(cl.time*16)%15)+1)));\n\t\tx -= 18;\n\t}\n}\nstatic void Sbar_Hexen2DrawItem(playerview_t *pv, float x, float y, int itemnum)\n{\n\tint num;\n\tSbar_DrawMPic(x, y, 29, 28, R2D_SafeCachePic(va(\"gfx/arti%02d.lmp\", itemnum)));\n\n\tnum = pv->stats[STAT_H2_CNT_FIRST+itemnum];\n\tif(num > 0)\n\t{\n\t\tif (num > 99)\n\t\t\tnum = 99;\n\t\tif (num >= 10)\n\t\t\tSbar_DrawMPic(x+20, y+21, 4, 6, R2D_SafeCachePic(va(\"gfx/artinum%d.lmp\", num/10)));\n\t\tSbar_DrawMPic(x+20+4, y+21, 4, 6, R2D_SafeCachePic(va(\"gfx/artinum%d.lmp\", num%10)));\n\t}\n}\n\nstatic void Sbar_Hexen2DrawInventory(playerview_t *pv)\n{\n\tint i;\n\tint x, y=-37;\n\tint activeleft = 0;\n\tint activeright = 0;\n\n\t/*always select an artifact that we actually have whether we are drawing the full bar or not.*/\n\t/*NOTE: Hexen2 reorders them in collection order.*/\n\tfor (i = 0; i < STAT_H2_CNT_COUNT; i++)\n\t{\n\t\tif (pv->stats[STAT_H2_CNT_FIRST+(i+pv->sb_hexen2_cur_item)%STAT_H2_CNT_COUNT])\n\t\t{\n\t\t\tpv->sb_hexen2_cur_item = (pv->sb_hexen2_cur_item + i)%STAT_H2_CNT_COUNT;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tif (pv->sb_hexen2_item_time+3 < realtime)\n\t\treturn;\n\tif (!pv->stats[STAT_H2_CNT_FIRST+pv->sb_hexen2_cur_item])\n\t\treturn; //no items... don't confuse the user.\n\n\tfor (i = pv->sb_hexen2_cur_item; i < STAT_H2_CNT_COUNT; i++)\n\t\tif (pv->sb_hexen2_cur_item == i || pv->stats[STAT_H2_CNT_FIRST+i] > 0)\n\t\t\tactiveright++;\n\tfor (i = pv->sb_hexen2_cur_item-1; i >= 0; i--)\n\t\tif (pv->sb_hexen2_cur_item == i || pv->stats[STAT_H2_CNT_FIRST+i] > 0)\n\t\t\tactiveleft++;\n\n\tif (activeleft > 3 + (activeright<=3?(4-activeright):0))\n\t\tactiveleft = 3 + (activeright<=3?(4-activeright):0);\n\tx=320/2-114 + (activeleft-1)*33;\n\tfor (i = pv->sb_hexen2_cur_item-1; x>=320/2-114; i--)\n\t{\n\t\tif (!pv->stats[STAT_H2_CNT_FIRST+i])\n\t\t\tcontinue;\n\n\t\tif (i == pv->sb_hexen2_cur_item)\n\t\t\tSbar_DrawMPic(x+9, y-12, 11, 11, R2D_SafeCachePic(\"gfx/artisel.lmp\"));\n\t\tSbar_Hexen2DrawItem(pv, x, y, i);\n\t\tx -= 33;\n\t}\n\n\tx=320/2-114 + activeleft*33;\n\tfor (i = pv->sb_hexen2_cur_item; i < STAT_H2_CNT_COUNT && x < 320/2-114+7*33; i++)\n\t{\n\t\tif (i != pv->sb_hexen2_cur_item && !pv->stats[STAT_H2_CNT_FIRST+i])\n\t\t\tcontinue;\n\t\tif (i == pv->sb_hexen2_cur_item)\n\t\t\tSbar_DrawMPic(x+9, y-12, 11, 11, R2D_SafeCachePic(\"gfx/artisel.lmp\"));\n\t\tSbar_Hexen2DrawItem(pv, x, y, i);\n\t\tx+=33;\n\t}\n}\n\nstatic void Sbar_Hexen2DrawPuzzles (playerview_t *pv)\n{\n\tunsigned int i, place=0, x, y, mid, j;\n\tchar name[64];\n\tconst char *line;\n\tmpic_t *pic;\n\n\tpuzzlenames = FS_LoadMallocFile(\"puzzles.txt\", NULL);\n\tif (!puzzlenames)\n\t\tpuzzlenames = Z_StrDup(\"\");\n\n\tfor (i = 0; i < 8; i++)\n\t{\n\t\tif (pv->statsstr[STAT_H2_PUZZLE1+i] && *pv->statsstr[STAT_H2_PUZZLE1+i])\n\t\t{\n\t\t\tpic = R2D_SafeCachePic(va(\"gfx/puzzle/%s.lmp\", pv->statsstr[STAT_H2_PUZZLE1+i]));\n\n\t\t\tstrcpy(name, \"Unknown\");\n\t\t\tfor (line = puzzlenames; (line = COM_Parse(line)); )\n\t\t\t{\n\t\t\t\tif (!Q_strcasecmp(com_token, pv->statsstr[STAT_H2_PUZZLE1+i]))\n\t\t\t\t{\n\t\t\t\t\twhile (*line == ' ' || *line == '\\t')\n\t\t\t\t\t\tline++;\n\t\t\t\t\tfor (j = 0; j < countof(name)-1; j++)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (*line == '\\r' || *line == '\\n' || !*line)\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tname[j] = *line++;\n\t\t\t\t\t}\n\t\t\t\t\tname[j] = 0;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tline = strchr(line, '\\n');\n\t\t\t\tif (!line)\n\t\t\t\t\tbreak;\n\t\t\t\tline++;\n\t\t\t}\n\n\t\t\tif (r_refdef.grect.width < 320)\n\t\t\t{\t//screen too narrow for side by side. depend on height.\n\t\t\t\tx = r_refdef.grect.x + 10;\n\t\t\t\ty = 50 + place*32;\n\t\t\t\tif (y+26 > r_refdef.grect.height)\n\t\t\t\t\tcontinue;\n\t\t\t\ty += sbar_rect.y;\n\t\t\t\tmid = r_refdef.grect.x + r_refdef.grect.width;\n\t\t\t\tR2D_ScalePic(x, y, 26, 26, pic);\n\t\t\t\tDraw_FunStringWidth(x+35, y+(26-8)/2, name, mid-(x+35), false, false);\n\t\t\t}\n\t\t\telse if (place < 4)\n\t\t\t{\t//first four are on the left.\n\t\t\t\tx = r_refdef.grect.x + 10;\n\t\t\t\ty = 50 + place*32;\n\t\t\t\tif (y+26 > r_refdef.grect.height)\n\t\t\t\t\tcontinue;\n\t\t\t\ty += sbar_rect.y;\n\t\t\t\tmid = r_refdef.grect.x + r_refdef.grect.width/2;\n\t\t\t\tR2D_ScalePic(x, y, 26, 26, pic);\n\t\t\t\tR_DrawTextField(x+35, y, mid-(x+35), 26, name, CON_WHITEMASK, CPRINT_LALIGN, font_default, NULL);\n\t\t\t}\n\t\t\telse\n\t\t\t{\t//last four are on the right\n\t\t\t\tx = r_refdef.grect.x + r_refdef.grect.width - 10 - 26;\n\t\t\t\ty = 50 + (place-4)*32;\n\t\t\t\tif (y+26 > r_refdef.grect.height)\n\t\t\t\t\tcontinue;\n\t\t\t\ty += sbar_rect.y;\n\t\t\t\tmid = r_refdef.grect.x + r_refdef.grect.width/2;\n\t\t\t\tR2D_ScalePic(x, y, 26, 26, pic);\n\t\t\t\tR_DrawTextField(mid, y, x-(35-26)-mid, 26, name, CON_WHITEMASK, CPRINT_RALIGN, font_default, NULL);\n\t\t\t}\n\t\t\tplace++;\n\t\t}\n\t}\n}\n\nstatic void Sbar_Hexen2DrawExtra (playerview_t *pv)\n{\n\tunsigned int i, slot;\n\tunsigned int pclass;\n\tint ringpos[] = {6, 44, 81, 119};\n\t//char *ringimages[] = {\"gfx/ring_f.lmp\", \"gfx/ring_w.lmp\", \"gfx/ring_t.lmp\", \"gfx/ring_r.lmp\"}; //unused variable\n\tfloat val;\n\tchar *pclassname[] = {\n\t\t\"Unknown\",\n\t\t\"Paladin\",\n\t\t\"Crusader\",\n\t\t\"Necromancer\",\n\t\t\"Assasin\",\n\t\t\"Demoness\"\n\t};\n\n//\tif (!pv->sb_hexen2_extra_info)\n//\t\treturn;\n\n\tpclass = cl.players[pv->playernum].h2playerclass;\n\tif (pclass >= sizeof(pclassname)/sizeof(pclassname[0]))\n\t\tpclass = 0;\n\n\tSbar_DrawMPic(0, 46, 160, 98, R2D_SafeCachePic(\"gfx/btmbar1.lmp\"));\n\tSbar_DrawMPic(160, 46, 160, 98, R2D_SafeCachePic(\"gfx/btmbar2.lmp\"));\n\n\tSbar_DrawTinyString (11, 48, pclassname[pclass]);\n\n\tSbar_DrawTinyString (11, 58, \"int\");\n\tSbar_DrawTinyStringf (33, 58, \"%02d\", pv->stats[STAT_H2_INTELLIGENCE]);\n\n\tSbar_DrawTinyString (11, 64, \"wis\");\n\tSbar_DrawTinyStringf (33, 64, \"%02d\", pv->stats[STAT_H2_WISDOM]);\n\n\tSbar_DrawTinyString (11, 70, \"dex\");\n\tSbar_DrawTinyStringf (33, 70, \"%02d\", pv->stats[STAT_H2_DEXTERITY]);\n\n\n\tSbar_DrawTinyString (58, 58, \"str\");\n\tSbar_DrawTinyStringf (80, 58, \"%02d\", pv->stats[STAT_H2_STRENGTH]);\n\n\tSbar_DrawTinyString (58, 64, \"lvl\");\n\tSbar_DrawTinyStringf (80, 64, \"%02d\", pv->stats[STAT_H2_LEVEL]);\n\n\tSbar_DrawTinyString (58, 70, \"exp\");\n\tSbar_DrawTinyStringf (80, 70, \"%06d\", pv->stats[STAT_H2_EXPERIENCE]);\n\n\tSbar_DrawTinyString (11, 79, \"abilities\");\n\tif (pv->stats[STAT_H2_FLAGS] & (1<<22))\n\t\tSbar_DrawTinyString (8, 89, T_GetString(400 + 2*(pclass-1) + 0));\n\tif (pv->stats[STAT_H2_FLAGS] & (1<<23))\n\t\tSbar_DrawTinyString (8, 96, T_GetString(400 + 2*(pclass-1) + 1));\n\n\tfor (i = 0; i < 4; i++)\n\t{\n\t\tif (pv->stats[STAT_H2_ARMOUR1+i] > 0)\n\t\t{\n\t\t\tSbar_DrawMPic (164+i*40, 115, 28, 19, R2D_SafeCachePic(va(\"gfx/armor%d.lmp\", i+1)));\n\t\t\tSbar_DrawTinyStringf (168+i*40, 136, \"+%d\", pv->stats[STAT_H2_ARMOUR1+i]);\n\t\t}\n\t}\n\tfor (i = 0; i < 4; i++)\n\t{\n\t\tif (pv->stats[STAT_H2_FLIGHT_T+i] > 0)\n\t\t{\n\t\t\tSbar_DrawMPic (ringpos[i], 119, 32, 22, R2D_SafeCachePic(va(\"gfx/ring_f.lmp\")));\n\t\t\tval = pv->stats[STAT_H2_FLIGHT_T+i];\n\t\t\tif (val > 100)\n\t\t\t\tval = 100;\n\t\t\tif (val < 0)\n\t\t\t\tval = 0;\n\t\t\tSbar_DrawMPic(ringpos[i]+29 - (int)(26 * (val/(float)100)),142, 26, 1, R2D_SafeCachePic(\"gfx/ringhlth.lmp\"));\n\t\t\tSbar_DrawMPic(ringpos[i]+29, 142, 26, 1, R2D_SafeCachePic(\"gfx/rhlthcvr.lmp\"));\n\t\t}\n\t}\n\n\tslot = 0;\n\tfor (i = 0; i < 8; i++)\n\t{\n\t\tif (pv->statsstr[STAT_H2_PUZZLE1+i] && *pv->statsstr[STAT_H2_PUZZLE1+i])\n\t\t{\n\t\t\tSbar_DrawMPic (194+(slot%4)*31, slot<4?51:82, 26, 26, R2D_SafeCachePic(va(\"gfx/puzzle/%s.lmp\", pv->statsstr[STAT_H2_PUZZLE1+i])));\n\t\t\tslot++;\n\t\t}\n\t}\n\n\tSbar_DrawMPic(134, 50, 49, 56, R2D_SafeCachePic(va(\"gfx/cport%d.lmp\", pclass)));\n}\n\nstatic int Sbar_Hexen2ArmourValue(playerview_t *pv)\n{\n\tint i;\n\tfloat ac = 0;\n\t/*\n\tWARNING: these values match the engine - NOT the gamecode!\n\tEven the gamecode's values are misleading due to an indexing bug.\n\t*/\n\tstatic int acv[5][4] =\n\t{\n\t\t{8, 6, 2, 4},\n\t\t{4, 8, 6, 2},\n\t\t{2, 4, 8, 6},\n\t\t{6, 2, 4, 8},\n\t\t{6, 2, 4, 8}\n\t};\n\n\tint classno;\n\tclassno = cl.players[pv->playernum].h2playerclass;\n\tif (classno >= 1 && classno <= 5)\n\t{\n\t\tclassno--;\n\t\tfor (i = 0; i < 4; i++)\n\t\t{\n\t\t\tif (pv->stats[STAT_H2_ARMOUR1+i])\n\t\t\t{\n\t\t\t\tac += acv[classno][i];\n\t\t\t\tac += pv->stats[STAT_H2_ARMOUR1+i]/5.0;\n\t\t\t}\n\t\t}\n\t}\n\treturn ac;\n}\n\nstatic void Sbar_Hexen2DrawBasic(playerview_t *pv)\n{\n\tint chainpos;\n\tint val, maxval;\n\tSbar_DrawMPic(0, 0, 160, 46, R2D_SafeCachePic(\"gfx/topbar1.lmp\"));\n\tSbar_DrawMPic(160, 0, 160, 46, R2D_SafeCachePic(\"gfx/topbar2.lmp\"));\n\tSbar_DrawMPic(0, -23, 51, 23, R2D_SafeCachePic(\"gfx/topbumpl.lmp\"));\n\tSbar_DrawMPic(138, -8, 39, 8, R2D_SafeCachePic(\"gfx/topbumpm.lmp\"));\n\tSbar_DrawMPic(269, -23, 51, 23, R2D_SafeCachePic(\"gfx/topbumpr.lmp\"));\n\n\t//mana1\n\tmaxval = pv->stats[STAT_H2_MAXMANA];\n\tval = pv->stats[STAT_H2_BLUEMANA];\n\tval = bound(0, val, maxval);\n\tSbar_DrawTinyStringf(201, 22, \"%03d\", val);\n\tif(val)\n\t{\n\t\tSbar_DrawMPic(190, 26-(int)((val*18.0)/(float)maxval+0.5), 3, 19, R2D_SafeCachePic(\"gfx/bmana.lmp\"));\n\t\tSbar_DrawMPic(190, 27, 3, 19, R2D_SafeCachePic(\"gfx/bmanacov.lmp\"));\n\t}\n\n\t//mana2\n\tmaxval = pv->stats[STAT_H2_MAXMANA];\n\tval = pv->stats[STAT_H2_GREENMANA];\n\tval = bound(0, val, maxval);\n\tSbar_DrawTinyStringf(243, 22, \"%03d\", val);\n\tif(val)\n\t{\n\t\tSbar_DrawMPic(232, 26-(int)((val*18.0)/(float)maxval+0.5), 3, 19, R2D_SafeCachePic(\"gfx/gmana.lmp\"));\n\t\tSbar_DrawMPic(232, 27, 3, 19, R2D_SafeCachePic(\"gfx/gmanacov.lmp\"));\n\t}\n\n\n\t//health\n\tval = pv->stats[STAT_HEALTH];\n\tif (val < -99)\n\t\tval = -99;\n\tSbar_Hexen2DrawNum(58, 14, val, 3);\n\n\t//armour\n\tval = Sbar_Hexen2ArmourValue(pv);\n\tSbar_Hexen2DrawNum(105, 14, val, 2);\n\n//\tSetChainPosition(cl.v.health, cl.v.max_health);\n\tchainpos = (195.0f*pv->stats[STAT_HEALTH]) / pv->stats[STAT_H2_MAXHEALTH];\n\tif (chainpos < 0)\n\t\tchainpos = 0;\n\tSbar_DrawMPic(45+((int)chainpos&7), 38, 222, 5, R2D_SafeCachePic(\"gfx/hpchain.lmp\"));\n\tSbar_DrawMPic(45+(int)chainpos, 36,\t35, 9, R2D_SafeCachePic(\"gfx/hpgem.lmp\"));\n\tSbar_DrawMPic(43, 36, 10, 10, R2D_SafeCachePic(\"gfx/chnlcov.lmp\"));\n\tSbar_DrawMPic(267, 36, 10, 10, R2D_SafeCachePic(\"gfx/chnrcov.lmp\"));\n\n\tif (pv->stats[STAT_H2_CNT_FIRST+pv->sb_hexen2_cur_item])\n\t\tSbar_Hexen2DrawItem(pv, 144, 3, pv->sb_hexen2_cur_item);\n}\n\nstatic void Sbar_Hexen2DrawMinimal(playerview_t *pv)\n{\n\tint y;\n\ty = -16;\n\tSbar_DrawMPic(3, y, 31, 17, R2D_SafeCachePic(\"gfx/bmmana.lmp\"));\n\tSbar_DrawMPic(3, y+18, 31, 17, R2D_SafeCachePic(\"gfx/gmmana.lmp\"));\n\n\tSbar_DrawTinyStringf(10, y+6, \"%03d\", pv->stats[STAT_H2_BLUEMANA]);\n\tSbar_DrawTinyStringf(10, y+18+6, \"%03d\", pv->stats[STAT_H2_GREENMANA]);\n\n\tSbar_Hexen2DrawNum(38, y+18, pv->stats[STAT_HEALTH], 3);\n\n\tif (pv->stats[STAT_H2_CNT_FIRST+pv->sb_hexen2_cur_item])\n\t\tSbar_Hexen2DrawItem(pv, 320-32, y+10, pv->sb_hexen2_cur_item);\n}\n#endif\n\nstatic void Sbar_DrawTeamStatus(playerview_t *pv)\n{\n\tint p;\n\tint y;\n\tint track;\n\n#ifdef NQPROT\n\tSbar_DrawCTFScores(pv);\n#endif\n\n\tif (!sbar_teamstatus.ival)\n\t\treturn;\n\ty = -32;\n\n\ttrack = Cam_TrackNum(pv);\n\tif (track == -1 || !pv->spectator)\n\t\ttrack = pv->playernum;\n\n\tfor (p = 0; p < cl.allocated_client_slots; p++)\n\t{\n\t\tif (pv->playernum == p)\t//self is not shown\n\t\t\tcontinue;\n\t\tif (track == p)\t//nor is the person you are tracking\n\t\t\tcontinue;\n\n\t\tif (cl.players[p].teamstatustime < realtime)\n\t\t\tcontinue;\n\n\t\tif (!*cl.players[p].teamstatus)\t//only show them if they have something. no blank lines thanks\n\t\t\tcontinue;\n\t\tif (strcmp(cl.players[p].team, cl.players[track].team))\n\t\t\tcontinue;\n\n\t\tif (*cl.players[p].name)\n\t\t{\n\t\t\tSbar_DrawString (0, y, cl.players[p].teamstatus);\n\t\t\ty-=8;\n\t\t}\n\t}\n\tsbar_parsingteamstatuses = true;\n}\n\nqboolean Sbar_UpdateTeamStatus(player_info_t *player, char *status)\n{\n\tqboolean aswhite = false;\n\tchar *outb;\n\tint outlen;\n\tchar *msgstart;\n\tchar *ledstatus;\n\n\tif (*status != '\\r')// && !(strchr(status, 0x86) || strchr(status, 0x87) || strchr(status, 0x88) || strchr(status, 0x89)))\n\t{\n\t\tif (*status != 'x' || status[1] != '\\r')\n\t\t\treturn false;\n\t\tstatus++;\n\t}\n\n\tif (*status == '\\r')\n\t{\n\t\twhile (*status == ' ' || *status == '\\r')\n\t\t\tstatus++;\n\t\tledstatus = status;\n\t\tif (*(unsigned char*)ledstatus >= 0x86 && *(unsigned char*)ledstatus <= 0x89)\n\t\t{\n\t\t\tmsgstart = strchr(status, ':');\n\t\t\tif (!status)\n\t\t\t\treturn false;\n\t\t\tif (msgstart)\n\t\t\t\tstatus = msgstart+1;\n\t\t\telse\n\t\t\t\tledstatus = NULL;\n\t\t}\n\t\telse\n\t\t\t\tledstatus = NULL;\n\t}\n\telse\n\t\tledstatus = NULL;\n\n\twhile (*status == ' ' || *status == '\\r')\n\t\tstatus++;\n\n\t//fixme: handle { and } stuff (assume red?)\n\toutb = player->teamstatus;\n\toutlen = sizeof(player->teamstatus)-1;\n\tif (ledstatus)\n\t{\n\t\t*outb++ = *ledstatus;\n\t\toutlen--;\n\t}\n\n\twhile(outlen>0 && *status)\n\t{\n\t\tif (*status == '{')\n\t\t{\n\t\t\taswhite=true;\n\t\t\tstatus++;\n\t\t\tcontinue;\n\t\t}\n\t\tif (aswhite)\n\t\t{\n\t\t\tif (*status == '}')\n\t\t\t{\n\t\t\t\taswhite = false;\n\t\t\t\tstatus++;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t*outb++ = *status++;\n\t\t}\n\t\telse\n\t\t\t*outb++ = *status++|128;\n\t\toutlen--;\n\t}\n\n\tplayer->teamstatustime = realtime + 10;\n\n\t*outb = '\\0';\n\n\tif (sbar_teamstatus.value == 2)\n\t\treturn sbar_parsingteamstatuses;\n\treturn false;\n}\n\n\nstatic void Sbar_Voice(int y)\n{\n#ifdef VOICECHAT\n\tint loudness;\n\tif (!snd_voip_showmeter.ival)\n\t\treturn;\n\tloudness = S_Voip_Loudness(snd_voip_showmeter.ival==2);\n\tif (loudness >= 0)\n\t{\n\t\tint w;\n\t\tint x=0;\n\t\tint s, i;\n\t\tfloat range = loudness/100.0f;\n\t\tw = 0;\n\t\tFont_BeginString(font_default, sbar_rect.x + min(320,sbar_rect.width)/2, sbar_rect.y + y + sbar_rect.height-SBAR_HEIGHT, &x, &y);\n\t\tw += Font_CharWidth(CON_WHITEMASK, 0xe080);\n\t\tw += Font_CharWidth(CON_WHITEMASK, 0xe081)*16;\n\t\tw += Font_CharWidth(CON_WHITEMASK, 0xe082);\n\t\tw += Font_CharWidth(CON_WHITEMASK, 'M');\n\t\tw += Font_CharWidth(CON_WHITEMASK, 'i');\n\t\tw += Font_CharWidth(CON_WHITEMASK, 'c');\n\t\tw += Font_CharWidth(CON_WHITEMASK, ' ');\n\t\tx -= w/2;\n\t\tx = Font_DrawChar(x, y, CON_WHITEMASK, 'M');\n\t\tx = Font_DrawChar(x, y, CON_WHITEMASK, 'i');\n\t\tx = Font_DrawChar(x, y, CON_WHITEMASK, 'c');\n\t\tx = Font_DrawChar(x, y, CON_WHITEMASK, ' ');\n\t\tx = Font_DrawChar(x, y, CON_WHITEMASK, 0xe080);\n\t\ts = x;\n\t\tfor (i=0 ; i<16 ; i++)\n\t\t\tx = Font_DrawChar(x, y, CON_WHITEMASK, 0xe081);\n\t\tFont_DrawChar(x, y, CON_WHITEMASK, 0xe082);\n\t\tFont_DrawChar(s + (x-s) * range - Font_CharWidth(CON_WHITEMASK, 0xe083)/2, y, CON_WHITEMASK, 0xe083);\n\t\tFont_EndString(font_default);\n\t}\n#endif\n}\n\nvoid SCR_StringXY(const char *str, float x, float y);\nvoid SCR_DrawClock(void);\nvoid SCR_DrawGameClock(void);\nstatic void Sbar_DrawUPS(playerview_t *pv)\n{\n\textern cvar_t show_speed;\n\tstatic double lastupstime;\n\tdouble t;\n\tstatic float lastups;\n\tchar str[80];\n\tfloat *vel;\n\tint track;\nextern cvar_t\tshow_speed_x;\nextern cvar_t\tshow_speed_y;\n\n\tif (!show_speed.ival)\n\t\treturn;\n\n\tt = Sys_DoubleTime();\n\tif ((t - lastupstime) >= 1.0/20)\n\t{\n\t\tif (pv->spectator)\n\t\t\ttrack = Cam_TrackNum(pv);\n\t\telse\n\t\t\ttrack = -1;\n\t\tif (track != -1)\n\t\t\tvel = cl.inframes[cl.validsequence&UPDATE_MASK].playerstate[track].velocity;\n\t\telse\n\t\t\tvel = pv->simvel;\n\t\tlastups = sqrt((vel[0]*vel[0]) + (vel[1]*vel[1]));\n\t\tlastupstime = t;\n\t}\n\n\tsprintf(str, \"%3.1f UPS\", lastups);\n\tSCR_StringXY(str, show_speed_x.value, show_speed_y.value);\n}\n\n/*\n===============\nSbar_Draw\n===============\n*/\nvoid Sbar_Draw (playerview_t *pv)\n{\n\tqboolean headsup;\n\tchar st[512];\n\tint sbarwidth;\n\tqboolean minidmoverlay;\n\textern cvar_t scr_centersbar;\n\n#ifdef CSQC_DAT\n\tif (CSQC_DrawHud(pv))\n\t\treturn;\n#endif\n\n\theadsup = !(cl_sbar.value || (scr_viewsize.value<100));\n\tif ((sb_updates >= vid.numpages) && !headsup)\n\t\treturn;\n\n\tsbar_parsingteamstatuses = false;\n\n#ifdef HLCLIENT\n\tif (CLHL_DrawHud())\n\t\treturn;\n#endif\n\n#ifdef Q2CLIENT\n\tif (cls.protocol == CP_QUAKE2)\n\t{\n\t\tint seat = pv - cl.playerview;\n\t\tif (seat >= cl.splitclients)\n\t\t\tseat = cl.splitclients-1;\n\t\tif (seat < 0)\n\t\t\tseat = 0;\n\t\tsbar_rect = r_refdef.grect;\n\t\tR2D_ImageColours(1, 1, 1, 1);\n\t\tif (*cl.q2statusbar)\n\t\t\tSbar_ExecuteLayoutString(cl.q2statusbar, seat);\n\t\tif (*cl.q2layout && (cl.q2frame.seat[seat].playerstate.stats[Q2STAT_LAYOUTS] & 1))\n\t\t\tSbar_ExecuteLayoutString(cl.q2layout[seat], seat);\n\t\tif (cl.q2frame.seat[seat].playerstate.stats[Q2STAT_LAYOUTS] & 2)\n\t\t\tSbar_Q2DrawInventory(seat);\n\t\treturn;\n\t}\n#endif\n\n\tSbar_Start();\n\n\tR2D_ImageColours(1, 1, 1, 1);\n\n\tminidmoverlay = cl.deathmatch && hud_miniscores_show->ival;\n\tsbar_rect = r_refdef.grect;\n\n\tsbarwidth = 320;\n\tif (minidmoverlay && sbar_rect.width >= 640 && cl.teamplay)\n\t\tsbarwidth += 320;\n\telse if (minidmoverlay && sbar_rect.width >= 512)\n\t\tsbarwidth += 192;\n\telse\n\t\tminidmoverlay = 0;\n\n\tif (scr_centersbar.ival)\n\t{\n\t\tfloat ofs = (sbar_rect.width - 320)/2;\n\t\tif (ofs > sbar_rect.width-sbarwidth)\n\t\t\tofs = sbar_rect.width-sbarwidth;\n\t\tsbar_rect.x += ofs;\n\t\tsbar_rect.width -= ofs;\n\t\tsbar_rect_left = -ofs;\n\t}\n\n\tsb_updates++;\n\n#ifdef HEXEN2\n\tif (sbar_hexen2)\n\t{\n\t\textern cvar_t scr_conspeed;\n\t\tfloat targlines;\n\t\t//hexen2 hud\n\n\t\tif (cl_sbar.value == 1 || scr_viewsize.value<100)\n\t\t\tR2D_TileClear (r_refdef.grect.x, r_refdef.grect.y+sbar_rect.height - sb_lines, r_refdef.grect.width, sb_lines);\n\n\t\tif (pv->sb_hexen2_infoplaque)\n\t\t{\n\t\t\tqboolean foundone = false;\n\t\t\tint i;\n\t\t\tchar *text = Z_StrDup(\"Objectives:\\n\");\n\t\t\tfor (i = 0; i < 64; i++)\n\t\t\t{\n\t\t\t\tif (pv->stats[STAT_H2_OBJECTIVE1 + i/32] & (1<<(i&31)))\n\t\t\t\t{\n\t\t\t\t\tZ_StrCat(&text, va(\"%s\\n\", T_GetInfoString(i)));\n\t\t\t\t\tfoundone = true;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!foundone)\n\t\t\t\tZ_StrCat(&text, va(\"<No Current Objectives>\\n\"));\n\n\t\t\tR_DrawTextField(r_refdef.grect.x, r_refdef.grect.y, r_refdef.grect.width, r_refdef.grect.height, text, CON_WHITEMASK, CPRINT_BACKGROUND|CPRINT_LALIGN, font_default, NULL);\n\t\t\tZ_Free(text);\n\t\t}\n\n\t\tif (pv->sb_hexen2_extra_info)\n\t\t\ttarglines = 46+98;\t//extra stuff shown when hitting tab\n\t\telse if (sb_lines > SBAR_HEIGHT)\n\t\t\ttarglines = sb_lines;\t\t//viewsize 100 stuff...\n\t\telse\n\t\t\ttarglines = -23;\t//viewsize 110/120 transparent overlay. negative covers the extra translucent details above.\n\t\tif (targlines > pv->sb_hexen2_extra_info_lines)\n\t\t{\t//expand\n\t\t\tpv->sb_hexen2_extra_info_lines += scr_conspeed.value*host_frametime;\n\t\t\tif (pv->sb_hexen2_extra_info_lines > targlines)\n\t\t\t\tpv->sb_hexen2_extra_info_lines = targlines;\n\t\t}\n\t\telse\n\t\t{\t//shrink\n\t\t\tpv->sb_hexen2_extra_info_lines -= scr_conspeed.value*host_frametime;\n\t\t\tif (pv->sb_hexen2_extra_info_lines < targlines)\n\t\t\t\tpv->sb_hexen2_extra_info_lines = targlines;\n\t\t}\n\n\t\tif (sb_lines > 0 && pv->sb_hexen2_extra_info_lines < 46)\n\t\t\tSbar_Hexen2DrawMinimal(pv);\n\t\tif (pv->sb_hexen2_extra_info_lines > -23)\n\t\t{\n\t\t\tsbar_rect.y -= pv->sb_hexen2_extra_info_lines - SBAR_HEIGHT;\t//Sbar_DrawMPic... eww.\n\t\t\tif (pv->sb_hexen2_extra_info_lines > 46)\n\t\t\t\tSbar_Hexen2DrawExtra(pv);\n\t\t\tSbar_Hexen2DrawBasic(pv);\n\t\t}\n\t\tSbar_Hexen2DrawInventory(pv);\n\n\t\tif (minidmoverlay)\n\t\t\tSbar_MiniDeathmatchOverlay (pv);\n\n\t\tSbar_Hexen2DrawActiveStuff(pv);\n\n\t\tif (!cls.deathmatch)\n\t\tif (pv->sb_showscores || pv->sb_showteamscores || pv->stats[STAT_HEALTH] <= 0)\n\t\t\tSbar_Hexen2DrawPuzzles(pv);\n\t}\n\telse\n#endif\n\t\tif (sbarfailed)\t//files failed to load.\n\t{\n\t\t//fallback hud\n\t\tif (pv->stats[STAT_HEALTH] > 0)\t//when dead, show nothing\n\t\t{\n//\t\t\tif (scr_viewsize.value != 120)\n//\t\t\t\tCvar_Set(&scr_viewsize, \"120\");\n\n\t\t\tSbar_DrawString (0, -8, va(\"Health: %i\", pv->stats[STAT_HEALTH]));\n\t\t\tSbar_DrawString (0, -16, va(\" Armor: %i\", pv->stats[STAT_ARMOR]));\n\n\t\t\tSbar_Voice(-24);\n\t\t}\n\t}\n\telse\n\t{\n\t\tif (cl_sbar.value == 1 || scr_viewsize.value<100)\n\t\t{\n\t\t\tif (sbar_rect.x>r_refdef.grect.x)\n\t\t\t{\t// left\n\t\t\t\tR2D_TileClear (r_refdef.grect.x, r_refdef.grect.y+sbar_rect.height - sb_lines, sbar_rect.x - r_refdef.grect.x, sb_lines);\n\t\t\t}\n\t\t\tif (sbar_rect.x + 320 <= r_refdef.grect.x + sbar_rect.width && !headsup)\n\t\t\t\tR2D_TileClear (sbar_rect.x + 320, r_refdef.grect.y+sbar_rect.height - sb_lines, sbar_rect.width - (320), sb_lines);\n\t\t}\n\n\t//standard quake(world) hud.\n\t// main area\n\t\tif (sb_lines > 0)\n\t\t{\n\t\t\tif (pv->spectator)\n\t\t\t{\n\t\t\t\tif (pv->cam_state == CAM_FREECAM || pv->cam_state == CAM_PENDING)\n\t\t\t\t{\n\t\t\t\t\tif (hud_tracking_show->ival || cl_sbar.ival)\n\t\t\t\t\t{\t//this is annoying.\n\t\t\t\t\t\tSbar_DrawPic (0, 0, 320, 24, sb_scorebar);\n\t\t\t\t\t\tSbar_DrawString (160-7*8,4, \"SPECTATOR MODE\");\n\t\t\t\t\t\tif (pv->cam_state == CAM_FREECAM)\n\t\t\t\t\t\t\tSbar_DrawString(160-14*8+4, 12, \"Press [ATTACK] for AutoCamera\");\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tif (pv->sb_showscores || pv->sb_showteamscores || pv->stats[STAT_HEALTH] <= 0)\n\t\t\t\t\t\tSbar_SoloScoreboard ();\n//\t\t\t\t\telse if (cls.gamemode != GAME_DEATHMATCH)\n//\t\t\t\t\t\tSbar_CoopScoreboard ();\n\t\t\t\t\telse\n\t\t\t\t\t\tSbar_DrawNormal (pv);\n\n\t\t\t\t\tif (hud_tracking_show->ival)\n\t\t\t\t\t{\n\t\t\t\t\t\tQ_snprintfz(st, sizeof(st), \"Tracking %-.64s\",\n\t\t\t\t\t\t\tcl.players[pv->cam_spec_track].name);\n\t\t\t\t\t\tSbar_DrawString(0, -8, st);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (pv->sb_showscores || pv->sb_showteamscores || (pv->stats[STAT_HEALTH] <= 0 && cl.splitclients == 1))\n\t\t\t{\n\t\t\t\tif (pv == cl.playerview)\n\t\t\t\t{\n\t\t\t\t\tif (!cls.deathmatch)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (cl_sbar.value || (scr_viewsize.value<100))\n\t\t\t\t\t\t\tSbar_DrawPic (0, 0, 320, 24, sb_scorebar);\n\t\t\t\t\t\tSbar_CoopScoreboard ();\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\tSbar_SoloScoreboard ();\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t\tSbar_DrawNormal (pv);\n\t\t}\n\n\t// top line\n\t\tif (sb_lines > 24)\n\t\t{\n\t\t\tif (!pv->spectator || pv->cam_state == CAM_WALLCAM || pv->cam_state == CAM_EYECAM)\n\t\t\t\tSbar_DrawInventory (pv);\n\t\t\telse if (cl_sbar.ival)\n\t\t\t\tSbar_DrawPic (0, -24, 320, 24, sb_scorebar);\t//make sure we don't get HoM\n\t\t\tif (!headsup && minidmoverlay)\n\t\t\t\tSbar_DrawFrags (pv);\n\t\t}\n\n\t\tif (minidmoverlay)\n\t\t\tSbar_MiniDeathmatchOverlay (pv);\n\n\t\tif (sb_lines > 0)\n\t\t\tSbar_DrawTeamStatus(pv);\n\t\tR2D_ImageColours (1, 1, 1, 1);\n\t}\n\n\tif (sb_lines > 24)\n\t\tSbar_Voice(-32);\n\telse if (sb_lines > 0)\n\t\tSbar_Voice(-8);\n\telse\n\t\tSbar_Voice(16);\n\n\tSbar_DrawUPS (pv);\n\tSCR_DrawClock();\n\tSCR_DrawGameClock();\n}\n\n//=============================================================================\n\n/*\n==================\nSbar_IntermissionNumber\n\n==================\n*/\nvoid Sbar_IntermissionNumber (float x, float y, int num, int digits, int color, qboolean left)\n{\n\tchar\t\t\tstr[12];\n\tchar\t\t\t*ptr;\n\tint\t\t\t\tl, frame;\n\n\tl = Sbar_itoa (num, str);\n\tptr = str;\n\tif (l > digits)\n\t\tptr += (l-digits);\n\tif (!left)\n\t\tif (l < digits)\n\t\t\tx += (digits-l)*24;\n\n\twhile (*ptr)\n\t{\n\t\tif (*ptr == '-')\n\t\t\tframe = STAT_MINUS;\n\t\telse\n\t\t\tframe = *ptr -'0';\n\n\t\tR2D_ScalePicAtlas (x,y, 24, 24, sb_nums[color][frame]);\n\t\tx += 24;\n\t\tptr++;\n\t}\n}\n\n#define COL_TEAM_LOWAVGHIGH\tCOLUMN(\"low/avg/high\", 12*8, {sprintf (num, \"%3i/%3i/%3i\", plow, pavg, phigh); Draw_FunString ( x, y, num); })\n#define COL_TEAM_TEAM\t\tif (!consistentteams){COLUMN(\"team\", 4*8, \t\t{Draw_FunStringWidth ( x, y, tm->team, 4*8, false, false); })}\n#define COL_TEAM_TOTAL\t\tCOLUMN(\"total\", 5*8, \t\t{Draw_FunString ( x, y, va(\"%5i\", tm->frags)); \\\n\t\tif (ourteam)\\\n\t\t{\\\n\t\t\tDraw_FunString ( x - 1*8, y, \"^Ue010\");\\\n\t\t\tDraw_FunString ( x + 5*8, y, \"^Ue011\");\\\n\t\t}\\\n\t})\n#define COL_TEAM_PLAYERS\tCOLUMN(\"players\", 7*8,\t\t{Draw_FunString ( x, y, va(\"%5i\", tm->players)); })\n#define ALL_TEAM_COLUMNS\tCOL_TEAM_LOWAVGHIGH COL_TEAM_TEAM COL_TEAM_TOTAL COL_TEAM_PLAYERS\n\n\n/*\n==================\nSbar_TeamOverlay\n\nteam frags\nadded by Zoid\n==================\n*/\nvoid Sbar_TeamOverlay (playerview_t *pv)\n{\n\tmpic_t\t\t\t*pic;\n\tint\t\t\t\ti, k;\n\tint\t\t\t\tx, y, l;\n\tchar\t\t\tnum[12];\n\tteam_t *tm;\n\tint plow, phigh, pavg;\n\tint pw,ph;\n\n\tvrect_t\t\tgr = r_refdef.grect;\n\tint rank_width = 320-32*2;\n\tint startx;\n\tint trackplayer;\n\n\tqboolean ourteam;\n\n\tif (!pv)\n\t\tpv = &cl.playerview[0];\n\n// request new ping times every two second\n\tif (!cl.teamplay)\n\t{\n\t\tSbar_DeathmatchOverlay(pv, 0);\n\t\treturn;\n\t}\n\n\t// sort the teams\n\tSbar_SortTeams(pv);\n\n\ty = gr.y;\n\n\tif (scr_scoreboard_drawtitle.ival)\n\t{\n\t\tpic = R2D_SafeCachePic (\"gfx/ranking.lmp\");\n\t\tif (pic && R_GetShaderSizes(pic, &pw, &ph, false)>0)\n\t\t{\n\t\t\tk = (pw * 24) / ph;\n\t\t\tR2D_ScalePic (gr.x+(gr.width-k)/2, y, k, 24, pic);\n\t\t}\n\t\ty += 24;\n\t}\n\n\tx = l = gr.x + (gr.width - 320)/2 + 36;\n\n\tstartx = x;\n\n\tif (scr_scoreboard_newstyle.ival)\n\t{\n\t\ty += 8;\n\t\t// Electro's scoreboard eyecandy: Draw top border\n\t\tR2D_ImagePaletteColour (0, scr_scoreboard_fillalpha.value);\n\t\tR2D_FillBlock(startx - 3, y - 1, rank_width - 1, 1);\n\n\t\t// Electro's scoreboard eyecandy: Draw the title row background\n\t\tR2D_ImagePaletteColour (1, scr_scoreboard_fillalpha.value);\n\t\tR2D_FillBlock(startx - 2, y, rank_width - 3, 9);\n\t\tR2D_ImageColours (1,1,1,1);\n\t}\n\n#define COLUMN(title, cwidth, code) Draw_FunString(x, y, title), x+=cwidth + 8;\n\tALL_TEAM_COLUMNS\n//\tif (rank_width+(cwidth)+8 <= vid.width) {showcolumns |= (1<<COLUMN##title); rank_width += cwidth+8;}\n\n//\tDraw_FunString(x, y, \"low/avg/high\");\n//\tDraw_FunString(x+13*8, y, \"team\");\n//\tDraw_FunString(x+18*8, y, \"total\");\n//\tDraw_FunString(x+24*8, y, \"players\");\n\ty += 8;\n//\tDraw_String(x, y, \"------------ ---- ----- -------\");\n\tx = l;\n#undef COLUMN\n\n\tif (scr_scoreboard_newstyle.ival)\n\t{\n\t\t// Electro's scoreboard eyecandy: Draw top border (under header)\n\t\tR2D_ImagePaletteColour (0, scr_scoreboard_fillalpha.value);\n\t\tR2D_FillBlock (startx - 3, y + 1, rank_width - 1, 1);\n\t\t// Electro's scoreboard eyecandy: Don't go over the black border, move the rest down\n\t\ty += 2;\n\t\t// Electro's scoreboard eyecandy: Draw left border\n\t\tR2D_FillBlock (startx - 3, y - 10, 1, 9);\n\t\t// Electro's scoreboard eyecandy: Draw right border\n\t\tR2D_FillBlock (startx - 3 + rank_width - 2, y - 10, 1, 9);\n\t}\n\telse if (scr_scoreboard_titleseperator.ival)\n\t{\n#define COLUMN(title, cwidth, code) {char buf[64*6]; int t = (cwidth)/8; int c=0; while (t-->0) {buf[c++] = '^'; buf[c++] = 'U'; buf[c++] = 'e'; buf[c++] = '0'; buf[c++] = '1'; buf[c] = (c==5?'d':(!t?'f':'e')); c++;} buf[c] = 0; Draw_FunString(x, y, buf); x += cwidth + 8;}\n\t\tALL_TEAM_COLUMNS\n//\t\tDraw_FunString(x, y, \"^Ue01d^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01f ^Ue01d^Ue01e^Ue01e^Ue01f ^Ue01d^Ue01e^Ue01e^Ue01e^Ue01f ^Ue01d^Ue01e^Ue01e^Ue01e^Ue01e^Ue01e^Ue01f\");\n\t\ty += 8;\n\n#undef COLUMN\n\t}\n\n\tif (pv->spectator)\n\t\ttrackplayer = Cam_TrackNum(pv);\n\telse\n\t\ttrackplayer = pv->playernum;\n\n// draw the text\n\tfor (i=0 ; i < scoreboardteams && y <= vid.height-10 ; i++)\n\t{\n\t\tk = teamsort[i];\n\t\ttm = teams + k;\n\n\t\tourteam = !strncmp(cl.players[trackplayer].team, tm->team, 16);\n\n\t\tif (scr_scoreboard_newstyle.ival)\n\t\t{\n\t\t\t// Electro's scoreboard eyecandy: Render the main background transparencies behind players row\n\t\t\t// TODO: Alpha values on the background\n\t\t\tint background_color;\n\t\t\tfloat backalpha;\n\n\t\t\tif (!(strcmp(\"red\", tm->team)))\n\t\t\t\tbackground_color = 4; // forced red\n\t\t\telse if (!(strcmp(\"blue\", tm->team)))\n\t\t\t\tbackground_color = 13; // forced blue\n\t\t\telse\n\t\t\t\tbackground_color = tm->bottomcolour;\n\n\t\t\tbackalpha = scr_scoreboard_backgroundalpha.value*scr_scoreboard_fillalpha.value;\n\t\t\tif (ourteam)\n\t\t\t\tbackalpha *= 1.7;\n\n\t\t\tSbar_FillPCDark (startx - 2, y, rank_width - 3, 8, background_color, backalpha);\n\n\t\t\tR2D_ImagePaletteColour (0, scr_scoreboard_fillalpha.value);\n\t\t\tR2D_FillBlock (startx - 3, y, 1, 8); // Electro - Border - Left\n\t\t\tR2D_FillBlock (startx - 3 + rank_width - 2, y, 1, 8); // Electro - Border - Right\n\n\t\t\tR2D_ImageColours(1, 1, 1, 1);\n\t\t}\n\n\t// draw pings\n\t\tplow = tm->plow;\n\t\tif (plow < 0 || plow > 999)\n\t\t\tplow = 999;\n\t\tphigh = tm->phigh;\n\t\tif (phigh < 0 || phigh > 999)\n\t\t\tphigh = 999;\n\t\tif (!tm->players)\n\t\t\tpavg = 999;\n\t\telse\n\t\t\tpavg = tm->ptotal / tm->players;\n\t\tif (pavg < 0 || pavg > 999)\n\t\t\tpavg = 999;\n\n\t\tx = l;\n\n#if 1\n#define COLUMN(title, cwidth, code) code; x+=cwidth + 8;\n\t\tALL_TEAM_COLUMNS\n#undef COLUMN\n#else\n\t\tsprintf (num, \"%3i/%3i/%3i\", plow, pavg, phigh);\n\t\tDraw_FunString ( x, y, num);\n\n\t// draw team\n\t\tQ_strncpyz (team, tm->team, sizeof(team));\n\t\tDraw_FunString (x + 104, y, team);\n\n\t// draw total\n\t\tsprintf (num, \"%5i\", tm->frags);\n\t\tDraw_FunString (x + 104 + 40, y, num);\n\n\t// draw players\n\t\tsprintf (num, \"%5i\", tm->players);\n\t\tDraw_FunString (x + 104 + 88, y, num);\n\n\t\tif (!strncmp(cl.players[trackplayer].team, tm->team, 16))\n\t\t{\n\t\t\tDraw_FunString ( x + 104 - 8, y, \"^Ue010\");\n\t\t\tDraw_FunString ( x + 104 + 32, y, \"^Ue011\");\n\t\t}\n#endif\n\t\ty += 8;\n\t}\n\n\tif (scr_scoreboard_newstyle.ival)\n\t{\n\t\tR2D_ImagePaletteColour (0, scr_scoreboard_fillalpha.value);\n\t\tR2D_FillBlock (startx - 3, y, rank_width - 1, 1); // Electro - Border - Bottom\n\t}\n\telse\n\t\ty += 8;\n\tSbar_DeathmatchOverlay(pv, y-gr.y);\n}\n\n/*\n==================\nSbar_DeathmatchOverlay\n\nping time frags name\n==================\n*/\n\n#define NOFILL\n//for reference:\n//define COLUMN(title, width, code)\n\n#define COLUMN_PING COLUMN(ping, 4*8,\t\t\t\t\t\\\n{\t\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n\tint p = s->ping;\t\t\t\t\t\t\t\t\t\\\n\tif (p < 0 || p > 999) p = 999;\t\t\t\t\t\t\\\n\tif (p >= scr_scoreboard_ping_status.vec4[3] && scr_scoreboard_ping_status.vec4[3] && *scr_scoreboard_ping_status.string)\t\\\n\t\tsprintf(num, S_COLOR_RED\"%4i\", p);\t\t\t\\\n\telse if (p >= scr_scoreboard_ping_status.vec4[2] && scr_scoreboard_ping_status.vec4[2])\t\\\n\t\tsprintf(num, S_COLOR_MAGENTA\"%4i\", p);\t\t\t\\\n\telse if (p >= scr_scoreboard_ping_status.vec4[1] && scr_scoreboard_ping_status.vec4[1])\t\\\n\t\tsprintf(num, S_COLOR_YELLOW\"%4i\", p);\t\t\t\\\n\telse if (p >= scr_scoreboard_ping_status.vec4[0] || !scr_scoreboard_ping_status.vec4[0])\t\\\n\t\tsprintf(num, S_COLOR_WHITE\"%4i\", p);\t\t\t\\\n\telse\t\t\t\t\t\t\t\t\t\t\t\t\\\n\t\tsprintf(num, S_COLOR_GREEN\"%4i\", p);\t\t\t\t\t\t\t\\\n\tDraw_FunStringWidth(x, y, num, 4*8+4, false, highlight);\t\\\n},NOFILL)\n\n#define COLUMN_PL COLUMN(pl, 2*8,\t\t\t\t\t\t\\\n{\t\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n\tint p = s->pl;\t\t\t\t\t\t\t\t\t\t\\\n\tsprintf(num, \"%2i\", p);\t\t\t\t\t\t\t\t\\\n\tDraw_FunStringWidth(x, y, num, 2*8+4, false, highlight);\t\\\n},NOFILL)\n#define COLUMN_TIME COLUMN(time, 4*8,\t\t\t\t\t\\\n{\t\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n\tif (scr_scoreboard_afk.ival && s->chatstate&2)\t\t\\\n\t\tsprintf (num, S_COLOR_RED\"afk\");\t\t\t\t\\\n\telse\t\t\t\t\t\t\t\t\t\t\t\t\\\n\t{\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n\t\ttotal = realtime - s->realentertime;\t\t\t\\\n\t\tminutes = (int)total/60;\t\t\t\t\t\t\\\n\t\tsprintf (num, \"%4i\", minutes);\t\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n\tDraw_FunStringWidth(x, y, num, 4*8+4, false, highlight);\t\\\n},NOFILL)\n#define COLUMN_FRAGS COLUMN(frags, 5*8,\t\t\t\t\t\\\n{\t\\\n\tint cx; int cy;\t\t\t\t\t\t\t\t\t\t\\\n\tif (s->spectator && s->spectator != 2)\t\t\t\t\\\n\t{\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n\t\tDraw_FunStringWidth(x, y, \"spectator\", 5*8+4, false, false);\t\\\n\t}\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n\telse\t\t\t\t\t\t\t\t\t\t\t\t\\\n\t{\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n\t\tf = s->frags;\t\t\t\t\t\t\t\t\t\\\n\t\tsprintf(num, \"%3i\",f);\t\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n\t\tFont_BeginString(font_default, x+8, y, &cx, &cy);\t\t\t\t\\\n\t\tFont_DrawChar(cx, cy, CON_WHITEMASK, num[0] | 0xe000);\t\t\t\\\n\t\tFont_BeginString(font_default, x+16, y, &cx, &cy);\t\t\t\t\\\n\t\tFont_DrawChar(cx, cy, CON_WHITEMASK, num[1] | 0xe000);\t\t\t\\\n\t\tFont_BeginString(font_default, x+24, y, &cx, &cy);\t\t\t\t\\\n\t\tFont_DrawChar(cx, cy, CON_WHITEMASK, num[2] | 0xe000);\t\t\t\\\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n\t\tif (isme)\t\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n\t\t{\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n\t\t\tFont_BeginString(font_default, x, y, &cx, &cy);\t\t\t\t\\\n\t\t\tFont_DrawChar(cx, cy, CON_WHITEMASK, 16 | 0xe000);\t\t\t\\\n\t\t\tFont_BeginString(font_default, x+32, y, &cx, &cy);\t\t\t\\\n\t\t\tFont_DrawChar(cx, cy, CON_WHITEMASK, 17 | 0xe000);\t\t\t\\\n\t\t}\t\t\t\t\t\t\t\t\t\t\t\t\\\n\t\tFont_EndString(font_default);\t\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n},{\t\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n\tif (!s->spectator)\t\t\t\t\t\t\t\t\t\\\n\t{\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n\t\tif (skip==8)\t\t\t\t\t\t\t\t\t\\\n\t\t\tSbar_FillPC(x, y+1, 40, 3, top);\t\t\t\\\n\t\telse\t\t\t\t\t\t\t\t\t\t\t\\\n\t\t\tSbar_FillPC(x, y, 40, 4, top);\t\t\t\t\\\n\t\tSbar_FillPC(x, y+4, 40, 4, bottom);\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n})\n#define COLUMN_TEAMNAME COLUMN(team, (consistentteams?0:(4*8)),\t\t\t\t\\\n{\t\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n\tif (!s->spectator)\t\t\t\t\t\t\t\t\t\\\n\t{\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n\t\tDraw_FunStringWidth(x, y, s->team, 4*8+4, false, highlight);\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n},NOFILL)\n#define COLUMN_STAT(title, width, code, fill) COLUMN(title, width, {\t\\\n\tif (!(s->spectator && s->spectator != 2))\t\t\t\t\t\t\t\\\n\t{\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n\t\tcode\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n}, fill)\n#define COLUMN_STAT2(title, width, code, fill) COLUMN(title, width, {\t\\\n\tif (!s->spectator)\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n\t{\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n\t\tcode\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n}, fill)\n#define COLUMN_RULESET COLUMN(ruleset, 8*8,\t{Draw_FunStringWidth(x, y, s->ruleset, 8*8+4, false, false);},NOFILL)\n#define COLUMN_NAME COLUMN(name, namesize,\t{Draw_FunStringWidth(x, y, s->name, namesize, false, highlight);},NOFILL)\n#define COLUMN_KILLS COLUMN_STAT(kils, 4*8, {Draw_FunStringWidth(x, y, va(\"%4i\", Stats_GetKills(k)), 4*8+4, false, false);},NOFILL)\n#define COLUMN_TKILLS COLUMN_STAT(tkil, 4*8, {Draw_FunStringWidth(x, y, va(\"%4i\", Stats_GetTKills(k)), 4*8+4, false, false);},NOFILL)\n#define COLUMN_DEATHS COLUMN_STAT(dths, 4*8, {Draw_FunStringWidth(x, y, va(\"%4i\", Stats_GetDeaths(k)), 4*8+4, false, false);},NOFILL)\n#define COLUMN_TOUCHES COLUMN_STAT(tchs, 4*8, {Draw_FunStringWidth(x, y, va(\"%4i\", Stats_GetTouches(k)), 4*8+4, false, false);},NOFILL)\n#define COLUMN_CAPS COLUMN_STAT(caps, 4*8, {Draw_FunStringWidth(x, y, va(\"%4i\", Stats_GetCaptures(k)), 4*8+4, false, false);},NOFILL)\n#ifdef QUAKESTATS\n\t#define COLUMN_HEALTH COLUMN_STAT2(hlth, (scr_scoreboard_showhealth.ival==2)?7*8:4*8, {\t\\\n\t\tfloat t;int c;\t\\\n\t\tint a = cl.players[k].stats[STAT_ARMOR];\t\t\\\n\t\tint h = cl.players[k].stats[STAT_HEALTH];\t\\\n\t\tif\t\t(cl.players[k].stats[STAT_ITEMS] & IT_ARMOR3)\t{c='1';t = 0.8f;}\\\n\t\telse if (cl.players[k].stats[STAT_ITEMS] & IT_ARMOR2)\t{c='3';t = 0.6f;}\\\n\t\telse if (cl.players[k].stats[STAT_ITEMS] & IT_ARMOR1)\t{c='2';t = 0.3f;}\\\n\t\telse\t\t\t\t\t\t\t\t\t\t\t\t\t{c='7';t = 0.f ;}\\\n\t\tif (h <= 0)  /*draw nothing*/  ;\t\\\n\t\telse if (scr_scoreboard_showhealth.ival==3&&a>0)\t{\tint m = h/(1-t);\tDraw_FunStringWidth(x, y, va(a>m?\"^%c%+4i\":\"^%c%4i\", c,h + bound(0, m, a)), 4*8+4, false, false); }\t\\\n\t\telse if (scr_scoreboard_showhealth.ival==2)\t\t\t\t\t\t\t\t\tDraw_FunStringWidth(x, y, (a>0)?va(\"^%c%3i^7/%-3i\", c,a,h):va(\"---/%-3i\", h), 7*8+4, false, false);\t\\\n\t\telse\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tDraw_FunStringWidth(x, y, va(\"%4i\", h), 4*8+4, false, false);\t\\\n\t},NOFILL)\n\t#define COLUMN_BESTWEAPON COLUMN_STAT2(wep, 4*8, {\t\\\n\t\tDraw_FunStringWidth(x, y, va(\"%s%s%s\",\t\t\\\n\t\t\t(cl.players[k].stats[STAT_ITEMS] & IT_LIGHTNING)?\"^5L\":\" \",\t\\\n\t\t\t(cl.players[k].stats[STAT_ITEMS] & IT_ROCKET_LAUNCHER)?\"^1R\":\" \",\t\\\n\t\t\t(cl.players[k].stats[STAT_ITEMS] & IT_GRENADE_LAUNCHER)?\"^2G\":\" \"\t\\\n\t\t), 4*8+4, false, false);\t\t\\\n\t},NOFILL)\n\t#define COLUMN_LOCATION COLUMN_STAT2(loc, 8*8, {\t\\\n\t\tlerpents_t *le;\t\\\n\t\tconst char *loc;\t\\\n\t\tif (k+1 < cl.maxlerpents && cl.lerpentssequence && cl.lerpents[k+1].sequence == cl.lerpentssequence)\tle = &cl.lerpents[k+1];\t\\\n\t\telse if (cl.lerpentssequence && cl.lerpplayers[k].sequence == cl.lerpentssequence)\t\t\t\t\t\tle = &cl.lerpplayers[k];\\\n\t\telse\tle = NULL;\\\n\t\tloc = le?TP_LocationName(le->origin):\"\";\t\\\n\t\tDraw_FunStringWidth(x, y, loc, 8*8+4, false, false);\t\\\n\t},NOFILL)\n#else\n\t#define COLUMN_HEALTH\n\t#define COLUMN_BESTWEAPON\n\t#define COLUMN_LOCATION\n#endif\n#define COLUMN_AFK COLUMN(afk, 0, {int cs = atoi(InfoBuf_ValueForKey(&s->userinfo, \"chat\")); if (cs)Draw_FunStringWidth(x+4, y, (cs&2)?\"afk\":\"msg\", 4*8, false, false);},NOFILL)\n\n\n//columns are listed here in display order\n#define ALLCOLUMNS COLUMN_PING COLUMN_PL COLUMN_TIME COLUMN_RULESET COLUMN_FRAGS COLUMN_TEAMNAME COLUMN_NAME COLUMN_HEALTH COLUMN_BESTWEAPON COLUMN_LOCATION COLUMN_KILLS COLUMN_TKILLS COLUMN_DEATHS COLUMN_TOUCHES COLUMN_CAPS COLUMN_AFK\n\nenum\n{\n#define COLUMN(title, width, code, fill) COLUMN##title,\n\tALLCOLUMNS\n#undef COLUMN\n\tCOLUMN_MAX\n};\n\n#define ADDCOLUMN(id) showcolumns |= (1<<id)\nvoid Sbar_DeathmatchOverlay (playerview_t *pv, int start)\n{\n\tmpic_t\t\t\t*pic;\n\tint\t\t\t\ti, k;\n\tint\t\t\t\tx, y, f;\n\tchar\t\t\tnum[12];\n\tplayer_info_t\t*s;\n\tint\t\t\t\ttotal;\n\tint\t\t\t\tminutes;\n\tint\t\t\t\tskip = 10;\n\tint showcolumns;\n\tint startx, rank_width;\n\tqboolean\t\tisme;\n\n\tvrect_t\t\tgr = r_refdef.grect;\n\tint namesize = (cl.teamplay ? 12*8 : 16*8);\n\tfloat backalpha;\n\n\tint pages;\n\tint linesperpage, firstline, lastline;\n\tint highlight;\n\n\tif (!pv)\n\t\treturn;\n\n// request new ping times every two second\n\tif (realtime - cl.last_ping_request > 2\t&& !cls.demoplayback)\n\t{\n\t\tif (cls.protocol == CP_QUAKEWORLD)\n\t\t{\n\t\t\tcl.last_ping_request = realtime;\n\t\t\tCL_SendClientCommand(true, \"pings\");\n\t\t}\n#ifdef NQPROT\n\t\telse if (cls.protocol == CP_NETQUAKE && !cls.qex)\n\t\t{\n\t\t\tcl.last_ping_request = realtime;\n\t\t\tCL_SendClientCommand(true, \"ping\");\n\t\t}\n#endif\n\t}\n#ifdef NQPROT\n\tif (cls.protocol == CP_NETQUAKE && !cls.qex)\n\t{\n\t\tif (cl.nqplayernamechanged && cl.nqplayernamechanged < realtime)\n\t\t{\n\t\t\tcl.nqplayernamechanged = 0;\n\t\t\tcls.nqexpectingstatusresponse = true;\n\t\t\tCL_SendClientCommand(true, \"status\");\n\t\t}\n\t}\n#endif\n\n\tif (start)\n\t\ty = start;\n\telse\n\t{\n\t\ty = 0;\n\t\tif (scr_scoreboard_drawtitle.ival)\n\t\t{\n\t\t\tpic = R2D_SafeCachePic (\"gfx/ranking.lmp\");\n\t\t\tif (pic)\n\t\t\t{\n\t\t\t\tint w, h;\n\t\t\t\tif (R_GetShaderSizes(pic, &w, &h, false)>0)\n\t\t\t\t{\n\t\t\t\t\tk = (w * 24) / h;\n\t\t\t\t\tR2D_ScalePic (gr.x + (gr.width-k)/2, gr.y, k, 24, pic);\n\t\t\t\t}\n\t\t\t}\n\t\t\ty += 24;\n\t\t}\n\t}\n\n// scores\n\tSbar_SortFrags(true, scr_scoreboard_teamsort.ival);\n\n// draw the text\n\tif (start)\n\t\ty = start;\n\telse\n\t\ty = 24;\n\n\tif (scr_scoreboard_newstyle.ival)\n\t{\n\t\t// Electro's scoreboard eyecandy: Increase to fit the new scoreboard\n\t\ty += 8;\n\t}\n\n\tstart = y;\n\ty+=8;\n\tlinesperpage = floor(((gr.height-10.)-y)/skip);\n\twhile (scoreboardlines > linesperpage && skip > 8)\n\t{\t//won't fit, shrink the gaps.\n\t\tskip--;\n\t\tlinesperpage = floor(((vid.height-10.)-y)/skip);\n\t}\n\tlinesperpage = max(linesperpage,1);\n\tpages = max(1, (scoreboardlines+linesperpage-1)/linesperpage);\n\n\tshowcolumns = 0;\n\n\trank_width = 0;\n\n#define COLUMN(title, cwidth, code, fill) if (rank_width+(cwidth)+8 <= gr.width && cwidth) {showcolumns |= (1<<COLUMN##title); rank_width += cwidth+8;}\n//columns are listed here in priority order (if the screen is too narrow, later ones will be hidden)\n\tCOLUMN_NAME\n\tCOLUMN_PING\n\tif (cls.protocol == CP_QUAKEWORLD)\n\t{\n\t\tCOLUMN_PL\n\t\tCOLUMN_TIME\n\t}\n\tCOLUMN_FRAGS\n\tif (cl.teamplay)\n\t{\n\t\tCOLUMN_TEAMNAME\n\t}\n\tif (scr_scoreboard_showflags.ival && cl.teamplay && Stats_HaveFlags(scr_scoreboard_showflags.ival&1))\n\t{\n\t\tCOLUMN_CAPS\n\t}\n\tif (scr_scoreboard_showfrags.ival && Stats_HaveKills())\n\t{\n\t\tCOLUMN_KILLS\n\t\tCOLUMN_DEATHS\n\t\tif (cl.teamplay)\n\t\t{\n\t\t\tCOLUMN_TKILLS\n\t\t}\n\t}\n#ifdef QUAKESTATS\n\tif (scr_scoreboard_showhealth.ival && cls.demoplayback==DPB_MVD)\n\t{\n\t\tCOLUMN_HEALTH\n\t}\n\tif (scr_scoreboard_showweapon.ival && cls.demoplayback==DPB_MVD)\n\t{\n\t\tCOLUMN_BESTWEAPON\n\t}\n\tif (scr_scoreboard_showlocation.ival && cls.demoplayback==DPB_MVD && TP_HaveLocations())\n\t{\n\t\tCOLUMN_LOCATION\n\t}\n#endif\n\tif (scr_scoreboard_showflags.ival && cl.teamplay && Stats_HaveFlags(scr_scoreboard_showflags.ival&1))\n\t{\n\t\tCOLUMN_TOUCHES\n\t}\n\tif (scr_scoreboard_showruleset.ival && (scr_scoreboard_showruleset.ival==2||Ruleset_GetRulesetName()))\n\t{\n\t\tCOLUMN_RULESET\n\t}\n\tCOLUMN_AFK\n#undef COLUMN\n\n\trank_width -= namesize;\n\tif (rank_width < 320 && pages <= 1)\n\t{\n\t\tnamesize += 320-rank_width;\n\t\tif (namesize > 32*8)\n\t\t\tnamesize = 32*8;\n\t}\n\trank_width += namesize;\n\n\tstartx = (gr.width-rank_width*pages)/2;\n\tif (startx < 0)\n\t{\n\t\tstartx = fmod(realtime*128, rank_width*pages) - (gr.width/2);\n\t\tstartx = -bound(0, startx, (rank_width*pages-gr.width));\n\t}\n\tstartx += gr.x;\n\tfor (firstline = 0; firstline < scoreboardlines; firstline += linesperpage, startx += rank_width)\n\t{\n\t\tif (startx+rank_width < gr.x || startx > gr.x+gr.width)\n\t\t\tcontinue;\n\t\tlastline = min(firstline + linesperpage, scoreboardlines);\n\t\ty = start + gr.y;\n\n\t\tif (scr_scoreboard_newstyle.ival)\n\t\t{\n\t\t\t// Electro's scoreboard eyecandy: Draw top border\n\t\t\tR2D_ImagePaletteColour (0, scr_scoreboard_fillalpha.value);\n\t\t\tR2D_FillBlock(startx - 3, y - 1, rank_width - 1, 1);\n\n\t\t\t// Electro's scoreboard eyecandy: Draw the title row background\n\t\t\tR2D_ImagePaletteColour (1, scr_scoreboard_fillalpha.value);\n\t\t\tR2D_FillBlock(startx - 2, y, rank_width - 3, 9);\n\n\t\t\tR2D_ImageColours(1, 1, 1, 1);\n\t\t}\n\n\t\tx = startx;\n#define COLUMN(title, width, code, fill) if (showcolumns & (1<<COLUMN##title)) {Draw_FunString(x, y, #title); x += width+8;}\n\tALLCOLUMNS\n#undef COLUMN\n\n\n\t\ty += 8;\n\n\t\tif (scr_scoreboard_titleseperator.ival && !scr_scoreboard_newstyle.ival)\n\t\t{\n\t\t\tx = startx;\n#define COLUMN(title, width, code, fill) \\\nif (showcolumns & (1<<COLUMN##title)) \\\n{ \\\n\tDraw_FunString(x, y, \"^Ue01d\"); \\\n\tfor (i = 8; i < width-8; i+= 8) \\\n\t\tDraw_FunString(x+i, y, \"^Ue01e\"); \\\n\tDraw_FunString(x+i, y, \"^Ue01f\"); \\\n\tx += width+8; \\\n}\n\t\t\tALLCOLUMNS\n#undef COLUMN\n\t\t\ty += 8;\n\t\t}\n\n\t\tif (scr_scoreboard_newstyle.ival)\n\t\t{\n\t\t\t// Electro's scoreboard eyecandy: Draw top border (under header)\n\t\t\tR2D_ImagePaletteColour (0, scr_scoreboard_fillalpha.value);\n\t\t\tR2D_FillBlock (startx - 3, y + 1, rank_width - 1, 1);\n\t\t\t// Electro's scoreboard eyecandy: Don't go over the black border, move the rest down\n\t\t\ty += 2;\n\t\t\t// Electro's scoreboard eyecandy: Draw left border\n\t\t\tR2D_FillBlock (startx - 3, y - 10, 1, 9);\n\t\t\t// Electro's scoreboard eyecandy: Draw right border\n\t\t\tR2D_FillBlock (startx - 3 + rank_width - 2, y - 10, 1, 9);\n\t\t}\n\n\t\ty -= skip;\n\n\t\t//drawfills (these are split out to aid batching)\n\t\tfor (i = firstline; i < lastline; i++)\n\t\t{\n\t\t\tchar\tteam[5];\n\t\t\tunsigned int\t\ttop, bottom;\n\n\t\t\t// TODO: Sort players so that the leading teams are drawn first\n\t\t\tk = fragsort[i];\n\t\t\ts = &cl.players[k];\n\t\t\tif (!s->name[0])\n\t\t\t\tcontinue;\n\n\t\t\ty += skip;\n\t\t\tif (y > vid.height-10)\n\t\t\t\tbreak;\n\t\t\tisme =\t(pv->cam_state == CAM_FREECAM && k == pv->playernum) ||\n\t\t\t\t\t(pv->cam_state != CAM_FREECAM && k == pv->cam_spec_track);\n\n\t\t\t// Electro's scoreboard eyecandy: Moved this up here for usage with the row background color\n\t\t\ttop = Sbar_TopColour(s);\n\t\t\tbottom = Sbar_BottomColour(s);\n\n\t\t\tif (scr_scoreboard_newstyle.ival)\n\t\t\t{\n\t\t\t\tbackalpha = scr_scoreboard_backgroundalpha.value*scr_scoreboard_fillalpha.value;\n\t\t\t\tif (isme)\n\t\t\t\t\tbackalpha *= 1.7;\n\n\t\t\t\t// Electro's scoreboard eyecandy: Render the main background transparencies behind players row\n\t\t\t\t// TODO: Alpha values on the background\n\t\t\t\tif ((cl.teamplay) && (!s->spectator))\n\t\t\t\t{\n\t\t\t\t\tint background_color;\n\t\t\t\t\t// Electro's scoreboard eyecandy: red vs blue are common teams, force the colours\n\t\t\t\t\tQ_strncpyz (team, InfoBuf_ValueForKey(&s->userinfo, \"team\"), sizeof(team));\n\n\t\t\t\t\tif (S_Voip_Speaking(k))\n\t\t\t\t\t\tbackground_color = 0x00ff00;\n\t\t\t\t\telse if (!(strcmp(\"red\", team)))\n\t\t\t\t\t\tbackground_color = 4; // forced red\n\t\t\t\t\telse if (!(strcmp(\"blue\", team)))\n\t\t\t\t\t\tbackground_color = 13; // forced blue\n\t\t\t\t\telse\n\t\t\t\t\t\tbackground_color = bottom;\n\n\t\t\t\t\tSbar_FillPCDark (startx - 2, y, rank_width - 3, skip, background_color, backalpha);\n\t\t\t\t}\n\t\t\t\telse if (S_Voip_Speaking(k))\n\t\t\t\t\tSbar_FillPCDark (startx - 2, y, rank_width - 3, skip, 0x00ff00, backalpha);\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tR2D_ImagePaletteColour (2, backalpha);\n\t\t\t\t\tR2D_FillBlock (startx - 2, y, rank_width - 3, skip);\n\t\t\t\t}\n\n\t\t\t\tR2D_ImagePaletteColour (0, scr_scoreboard_fillalpha.value);\n\t\t\t\tR2D_FillBlock (startx - 3, y, 1, skip); // Electro - Border - Left\n\t\t\t\tR2D_FillBlock (startx - 3 + rank_width - 2, y, 1, skip); // Electro - Border - Right\n\t\t\t}\n\n\t\t\tx = startx;\n#define COLUMN(title, width, code, fills) \\\nif (showcolumns & (1<<COLUMN##title)) \\\n{ \\\n\tfills \\\n\tx += width+8; \\\n}\n\t\t\tALLCOLUMNS\n#undef COLUMN\n\t\t}\n\t\tif (scr_scoreboard_newstyle.ival)\n\t\t{\n\t\t\tR2D_ImagePaletteColour (0, scr_scoreboard_fillalpha.value);\n\t\t\tR2D_FillBlock (startx - 3, y + skip, rank_width - 1, 1); // Electro - Border - Bottom\n\t\t}\n\t\tR2D_ImageColours(1.0, 1.0, 1.0, 1.0);\n\t\ty -= (i-firstline) * skip;\n\n\t\t//text parts\n\t\tfor (i = firstline; i < lastline; i++)\n\t\t{\n\t\t\t// TODO: Sort players so that the leading teams are drawn first\n\t\t\tk = fragsort[i];\n\t\t\ts = &cl.players[k];\n\t\t\tif (!s->name[0])\n\t\t\t\tcontinue;\n\n\t\t\ty += skip;\n\t\t\tif (y > vid.height-10)\n\t\t\t\tbreak;\n\t\t\tisme =\t(pv->cam_state == CAM_FREECAM && k == pv->playernum) ||\n\t\t\t\t\t(pv->cam_state != CAM_FREECAM && k == pv->cam_spec_track);\n\n\t\t\tif ((key_dest_absolutemouse & key_dest_mask & ~kdm_game) &&\n\t\t\t\t!Key_Dest_Has(~kdm_game) &&\n\t\t\t\tmousecursor_x >= startx && mousecursor_x < startx+rank_width &&\n\t\t\t\tmousecursor_y >= y && mousecursor_y < y+skip)\n\t\t\t{\n\t\t\t\thighlight = 2;\n\t\t\t\tcl.mouseplayerview = pv;\n\t\t\t\tcl.mousenewtrackplayer = k;\n\t\t\t}\n\t\t\telse\n\t\t\t\thighlight = 0;\n\n\t\t\tx = startx;\n#define COLUMN(title, width, code, fills) \\\nif (showcolumns & (1<<COLUMN##title)) \\\n{ \\\n\tcode \\\n\tx += width+8; \\\n}\n\t\t\tALLCOLUMNS\n#undef COLUMN\n\t\t}\n\t}\n}\n\n/*\n==================\nSbar_MiniDeathmatchOverlay\n\nfrags name\nfrags team name\ndisplayed to right of status bar if there's room\n==================\n*/\nstatic void Sbar_MiniDeathmatchOverlay (playerview_t *pv)\n{\n\tint\t\t\t\ti, k;\n\tint\t\t\t\ttop, bottom;\n\tint\t\t\t\tx, y, f, px, py;\n\tchar\t\t\tnum[12];\n\tplayer_info_t\t*s;\n\tint\t\t\t\tnumlines;\n\tchar\t\t\tname[64+1];\n\tteam_t\t\t\t*tm;\n\n// scores\n\tSbar_SortFrags (false, false);\n//\tif (sbar_rect.width >= 640)\n\tSbar_SortTeams(pv);\n\n\tif (!scoreboardlines)\n\t\treturn; // no one there?\n\n// draw the text\n\ty = sbar_rect.y + sbar_rect.height - sb_lines - 1;\n\tnumlines = sb_lines/8;\n\tif (numlines < 3)\n\t\treturn; // not enough room\n\n\t// find us\n\tfor (i=0 ; i < scoreboardlines; i++)\n\t\tif (fragsort[i] == pv->playernum)\n\t\t\tbreak;\n\n\tif (i == scoreboardlines) // we're not there, we are probably a spectator, just display top\n\t\ti = 0;\n\telse // figure out start\n\t\ti = i - numlines/2;\n\n\tif (i > scoreboardlines - numlines)\n\t\ti = scoreboardlines - numlines;\n\tif (i < 0)\n\t\ti = 0;\n\n\tx = sbar_rect.x + 320 + 4;\n\n\tfor (f = i, py = y; f < scoreboardlines && py < sbar_rect.y + sbar_rect.height - 8 + 1; f++)\n\t{\n\t\tk = fragsort[f];\n\t\ts = &cl.players[k];\n\t\tif (!s->name[0])\n\t\t\tcontinue;\n\t// draw ping\n\t\ttop = Sbar_TopColour(s);\n\t\tbottom = Sbar_BottomColour(s);\n\n\t\tif (S_Voip_Speaking(k))\n\t\t\tSbar_FillPCDark (x, py, ((cl.teamplay && !consistentteams)?40:0)+48+MAX_DISPLAYEDNAME*8, 8, 0x00ff00, scr_scoreboard_backgroundalpha.value*scr_scoreboard_fillalpha.value);\n\n\t\tSbar_FillPC ( x, py+1, 40, 3, top);\n\t\tSbar_FillPC ( x, py+4, 40, 4, bottom);\n\t\tpy += 8;\n\t}\n\tR2D_ImageColours(1, 1, 1, 1);\n\tfor (/* */ ; i < scoreboardlines && y < sbar_rect.y + sbar_rect.height - 8 + 1; i++)\n\t{\n\t\tk = fragsort[i];\n\t\ts = &cl.players[k];\n\t\tif (!s->name[0])\n\t\t\tcontinue;\n\n\t// draw number\n\t\tf = s->frags;\n\t\tsprintf (num, \"%3i\",f);\n\n\t\tFont_BeginString(font_default, x+8, y, &px, &py);\n\t\tFont_DrawChar ( px, py, CON_WHITEMASK, num[0] | 0xe000);\n\t\tFont_BeginString(font_default, x+16, y, &px, &py);\n\t\tFont_DrawChar ( px, py, CON_WHITEMASK, num[1] | 0xe000);\n\t\tFont_BeginString(font_default, x+24, y, &px, &py);\n\t\tFont_DrawChar ( px, py, CON_WHITEMASK, num[2] | 0xe000);\n\n\t\tif ((pv->spectator && k == pv->cam_spec_track && pv->cam_state != CAM_FREECAM) ||\n\t\t\t(!pv->spectator && k == pv->playernum))\n\t\t{\n\t\t\tFont_BeginString(font_default, x, y, &px, &py);\n\t\t\tFont_DrawChar ( px, py, CON_WHITEMASK, 16 | 0xe000);\n\t\t\tFont_BeginString(font_default, x+32, y, &px, &py);\n\t\t\tFont_DrawChar ( px, py, CON_WHITEMASK, 17 | 0xe000);\n\t\t}\n\n\t\tQ_strncpyz(name, s->name, sizeof(name));\n\t// team and name\n\t\tif (cl.teamplay && !consistentteams)\n\t\t{\n\t\t\tDraw_FunStringWidth (x+48, y, s->team, 32, false, false);\n\t\t\tDraw_FunStringWidth (x+48+40, y, name, MAX_DISPLAYEDNAME*8, false, false);\n\t\t}\n\t\telse\n\t\t\tDraw_FunStringWidth (x+48, y, name, MAX_DISPLAYEDNAME*8, false, false);\n\t\ty += 8;\n\t}\n\tx += 48;\n\tif (cl.teamplay && !consistentteams)\n\t\tx += 40;\n\tx += MAX_DISPLAYEDNAME*8;\n\tx += 8;\n\n\t// draw teams if room\n\tif (x + (consistentteams?40:80) > sbar_rect.x+sbar_rect.width || !cl.teamplay)\n\t\treturn;\n\n\ty = sbar_rect.y + sbar_rect.height - sb_lines;\n\n\t// draw seperator\n\tR2D_ImageColours(0.3, 0.3, 0.3, 1);\n\tR2D_FillBlock(x, y, 2, sb_lines);\n\tR2D_ImageColours(1,1,1,1);\n\tx += 2;\n//\tfor (y = sbar_rect.height - sb_lines; y < sbar_rect.height - 6; y += 2)\n//\t\tDraw_ColouredCharacter(x, y, CON_WHITEMASK|14);\n\n\tx += 8;\n\n\tfor (i=0 ; i < scoreboardteams && y <= sbar_rect.y+sbar_rect.height; i++)\n\t{\n\t\tk = teamsort[i];\n\t\ttm = teams + k;\n\n\t\tif (consistentteams)\n\t\t{\n\t\t// draw total\n\t\t\tSbar_FillPC ( x, y+1, 48, 3, tm->topcolour);\n\t\t\tSbar_FillPC ( x, y+4, 48, 4, tm->bottomcolour);\n\t\t\tR2D_ImageColours(1,1,1,1);\n\t\t\tsprintf (num, \"%i\", tm->frags);\n\t\t\tDraw_FunStringWidth(x,y,num, 40, true, false);\n\t\t\tif (!strncmp(cl.players[pv->playernum].team, tm->team, 16))\n\t\t\t{\n\t\t\t\tFont_BeginString(font_default, x, y, &px, &py);\n\t\t\t\tFont_DrawChar(px, py, CON_WHITEMASK, 16|0xe000);\n\t\t\t\tFont_BeginString(font_default, x+40, y, &px, &py);\n\t\t\t\tFont_DrawChar(px, py, CON_WHITEMASK, 17|0xe000);\n\t\t\t\tFont_EndString(font_default);\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t// draw name\n\t\t\tDraw_FunStringWidth (x, y, tm->team, 32, false, false);\n\n\t\t// draw total\n\t\t\tsprintf (num, \"%5i\", tm->frags);\n\t\t\tDraw_FunString(x + 40, y, num);\n\n\t\t\tif (!strncmp(cl.players[pv->playernum].team, tm->team, 16))\n\t\t\t{\n\t\t\t\tFont_BeginString(font_default, x-8, y, &px, &py);\n\t\t\t\tFont_DrawChar(px, py, CON_WHITEMASK, 16|0xe000);\n\t\t\t\tFont_BeginString(font_default, x+32, y, &px, &py);\n\t\t\t\tFont_DrawChar(px, py, CON_WHITEMASK, 17|0xe000);\n\t\t\t\tFont_EndString(font_default);\n\t\t\t}\n\t\t}\n\n\t\ty += 8;\n\t}\n\n}\n\nvoid Sbar_CoopIntermission (playerview_t *pv)\n{\n\tmpic_t\t*pic;\n\tint\t\tdig;\n\tint\t\tnum;\n\n\tsbar_rect.width = vid.width;\n\tsbar_rect.height = vid.height;\n\tsbar_rect.x = 0;\n\tsbar_rect.y = 0;\n\n\tpic = R2D_SafeCachePic (\"gfx/complete.lmp\");\n\tif (!pic)\n\t\treturn;\n\tR2D_ScalePic ((sbar_rect.width - 320)/2 + 64, (sbar_rect.height - 200)/2 + 24, 192, 24, pic);\n\n\tpic = R2D_SafeCachePic (\"gfx/inter.lmp\");\n\tif (pic)\n\t\tR2D_ScalePic ((sbar_rect.width - 320)/2 + 0, (sbar_rect.height - 200)/2 + 56, 160, 144, pic);\n\n// time\n\tdig = cl.completed_time/60;\n\tSbar_IntermissionNumber ((sbar_rect.width - 320)/2 + 230 - 24*4, (sbar_rect.height - 200)/2 + 64, dig, 4, 0, false);\n\tnum = cl.completed_time - dig*60;\n\tR2D_ScalePicAtlas ((sbar_rect.width - 320)/2 + 230,(sbar_rect.height - 200)/2 + 64, 24, 24, sb_colon);\n\tR2D_ScalePicAtlas ((sbar_rect.width - 320)/2 + 254,(sbar_rect.height - 200)/2 + 64, 24, 24, sb_nums[0][num/10]);\n\tR2D_ScalePicAtlas ((sbar_rect.width - 320)/2 + 278,(sbar_rect.height - 200)/2 + 64, 24, 24, sb_nums[0][num%10]);\n\n//it is assumed that secrits/monsters are going to be constant for any player...\n\tSbar_IntermissionNumber ((sbar_rect.width - 320)/2 + 230 - 24*4, (sbar_rect.height - 200)/2 + 104, pv->stats[STAT_SECRETS], 4, 0, false);\n\tR2D_ScalePicAtlas ((sbar_rect.width - 320)/2 + 230, (sbar_rect.height - 200)/2 + 104, 24, 24, sb_slash);\n\tSbar_IntermissionNumber ((sbar_rect.width - 320)/2 + 254, (sbar_rect.height - 200)/2 + 104, pv->stats[STAT_TOTALSECRETS], 4, 0, true);\n\n\tSbar_IntermissionNumber ((sbar_rect.width - 320)/2 + 230 - 24*4, (sbar_rect.height - 200)/2 + 144, pv->stats[STAT_MONSTERS], 4, 0, false);\n\tR2D_ScalePicAtlas ((sbar_rect.width - 320)/2 + 230,(sbar_rect.height - 200)/2 + 144, 24, 24, sb_slash);\n\tSbar_IntermissionNumber ((sbar_rect.width - 320)/2 + 254, (sbar_rect.height - 200)/2 + 144, pv->stats[STAT_TOTALMONSTERS], 4, 0, true);\n}\n/*\n==================\nSbar_IntermissionOverlay\n\n==================\n*/\nvoid Sbar_IntermissionOverlay (playerview_t *pv)\n{\n\tSbar_Start();\n\n\tif (!cls.deathmatch)\n\t\tSbar_CoopIntermission(pv);\n\telse if (cl.teamplay > 0 && !pv->sb_showscores)\n\t\tSbar_TeamOverlay (pv);\n\telse\n\t\tSbar_DeathmatchOverlay (pv, 0);\n}\n#endif\n"
  },
  {
    "path": "engine/client/sbar.h",
    "content": "/*\nCopyright (C) 1996-1997 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  \n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n*/\n\n// the status bar is only redrawn if something has changed, but if anything\n// does, the entire thing will be redrawn for the next vid.numpages frames.\n\n#ifdef QUAKEHUD\n#define\tSBAR_HEIGHT\t\t24\n\nextern\tint\t\t\tsb_lines;\t\t\t// scan lines to draw\n\nvoid Sbar_Init (void);\nstruct player_info_s;\nqboolean Sbar_UpdateTeamStatus(struct player_info_s *player, char *status);\n\nvoid Sbar_Changed (void);\n// call whenever any of the client stats represented on the sbar changes\n\nqboolean Sbar_ShouldDraw(playerview_t *pv);\nvoid Sbar_Draw (playerview_t *pv);\t//uses the current r_refdef.grect\nvoid Sbar_DrawScoreboard (playerview_t *pv);\n// called every frame by screen\n\nvoid Sbar_IntermissionOverlay (playerview_t *pv);\n// called each frame after the level has been completed\n\nvoid Sbar_FinaleOverlay (void);\n\nvoid Sbar_PQ_Team_New(unsigned int team, unsigned int shirt);\nvoid Sbar_PQ_Team_Frags(unsigned int team, int frags);\nvoid Sbar_PQ_Team_Reset(void);\n\nvoid Sbar_Start (void);\nvoid Sbar_Flush (void);\nint Sbar_TranslateHudClick(void);\n\n#else\n#define sb_lines 0\n#define Sbar_Init()\n#define Sbar_Start()\n#define Sbar_Changed()\n#define Sbar_Draw(pv)\n#define Sbar_Flush()\n#define Sbar_ShouldDraw(pv) false\n#define Sbar_DrawScoreboard(pv)\n#define Sbar_FinaleOverlay()\n#define Sbar_IntermissionOverlay(pv)\n#define Sbar_TranslateHudClick() 0\n#endif\n\nunsigned int\tSbar_ColorForMap (unsigned int m);\n\nvoid Sbar_SortFrags (qboolean includespec, qboolean teamsort);\nextern int scoreboardlines;\nextern int fragsort[];\n"
  },
  {
    "path": "engine/client/screen.h",
    "content": "/*\nCopyright (C) 1996-1997 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  \n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n*/\n// screen.h\n\ntypedef struct playerview_s playerview_t;\n\nextern\tfloat\t\tscr_con_current;\nextern\tfloat\t\tscr_con_target;\t\t// lines of console to display\n\nextern\tint\t\t\tsb_lines;\n\nextern\tint\t\t\tclearnotify;\t// set to 0 whenever notify text is drawn\nextern\tqboolean\tscr_disabled_for_loading;\n\nextern\tcvar_t\t\tscr_fov;\nextern\tcvar_t\t\tscr_fov_viewmodel;\nextern\tcvar_t\t\tscr_viewsize;\n\nqboolean SCR_RSShot (void);\n\ntypedef struct\n{\n\tqboolean enabled;\n\tvec3_t angles;\n} vrui_t;\nextern vrui_t vrui;\nvoid VRUI_SnapAngle(void);\n\n//void SCR_DrawConsole (qboolean noback);\n//void SCR_SetUpToDrawConsole (void);\n\n//void SCR_BeginLoadingPlaque (void);\n//void SCR_EndLoadingPlaque (void);\n\n\n//void SCR_Init (void);\n\n//void SCR_UpdateScreen (void);\n\n#if defined(GLQUAKE)\nqboolean GLSCR_UpdateScreen (void);\n#endif\n\nvoid SCR_ImageName (const char *mapname);\n\n//this stuff is internal to the screen systems.\nvoid RSpeedShow(void);\n\nvoid SCR_CrosshairPosition(playerview_t *pview, float *x, float *y);\nvoid SCR_DrawLoading (qboolean opaque);\nvoid SCR_TileClear (int skipbottom);\nvoid SCR_DrawNotifyString (void);\nvoid SCR_CheckDrawCenterString (void);\nvoid SCR_DrawNet (void);\nvoid SCR_DrawTurtle (void);\nvoid SCR_DrawPause (void);\nqboolean SCR_HardwareCursorIsActive(void);\n\nvoid CLSCR_Init(void);\t//basically so I can register a few friendly cvars.\n\n//TEI_SHOWLMP2 stuff\nvoid SCR_ShowPics_Draw(void);\nvoid SCR_ShowPic_Create(void);\nvoid SCR_ShowPic_Hide(void);\nvoid SCR_ShowPic_Move(void);\nvoid SCR_ShowPic_Update(void);\nvoid SCR_ShowPic_ClearAll(qboolean persistflag);\nconst char *SCR_ShowPics_ClickCommand(float cx, float cy, qboolean loadtouch);\nvoid SCR_ShowPic_Script_f(void);\nvoid SCR_ShowPic_Remove_f(void);\n\n//a header is better than none...\nvoid Draw_TextBox (int x, int y, int width, int lines);\nvoid Draw_ApproxTextBox (float x, float y, float width, float height);\nenum fs_relative;\n\n\ntypedef enum uploadfmt\n{\n//NOTE: these values are exposed to native plugins but not QC.\n\tPTI_INVALID,\n\n\t//these formats are specified as direct byte access (listed in byte order, aka big-endian 0xrrggbbaa order)\n\tPTI_RGBA8,\t//rgba byte ordering\n\tPTI_RGBX8,\t//rgb pad byte ordering\n\tPTI_BGRA8,\t//alpha channel\n\tPTI_BGRX8,\t//no alpha channel\n\tPTI_RGBA8_SRGB,\t//rgba byte ordering\n\tPTI_RGBX8_SRGB,\t//rgb pad byte ordering\n\tPTI_BGRA8_SRGB,\t//alpha channel\n\tPTI_BGRX8_SRGB,\t//no alpha channel\n\tPTI_RGB8,\t\t//24bit packed format. generally not supported\n\tPTI_BGR8,\t\t//24bit packed format. generally not supported\n\tPTI_RGB8_SRGB,\t//24bit packed format. generally not supported\n\tPTI_BGR8_SRGB,\t//24bit packed format. generally not supported\n\tPTI_L8,\t\t\t//8bit format. luminance gets flooded to all RGB channels. might be supported using swizzles.\n\tPTI_L8A8,\t\t//16bit format. L=luminance. might be supported using swizzles.\n\tPTI_L8_SRGB,\t//8bit format. luminance gets flooded to all RGB channels. might be supported using swizzles.\n\tPTI_L8A8_SRGB,\t//16bit format. L=luminance. note: this cannot be implemented as a swizzle as there's no way to get srgb on red without it on green.\n\t//small formats.\n\tPTI_P8,\t\t\t//used for paletted data. Loaded as R8, but separate purely due to mipmap generation. should probably make a mipgen enum.\n\tPTI_R8,\t\t\t//used for greyscale data (that doesn't need to get expanded to rgb).\n\tPTI_RG8,\t\t//might be useful for normalmaps\n\tPTI_R8_SNORM,\n\tPTI_RG8_SNORM,\t//might be useful for normalmaps\n\t//big formats\n\tPTI_R16,\t\t//useful for heightmaps\n\tPTI_RGBA16,\t\t//if people use 16bit pngs, people get 16 bits per channel textures. muppets.\n\t//floating point formats\n\tPTI_R16F,\n\tPTI_R32F,\n\tPTI_RGB32F,\t\t//so qc can just use vectors for rgb. not really recommended.\n\tPTI_RGBA16F,\t//consider using e5bgr9 or bc6/astc\n\tPTI_RGBA32F,\t//usually overkill\n\t//packed/misaligned formats: these are specified in native endian order (high bits listed first because that's how things are represented in hex), so may need byte swapping...\n\tPTI_A2BGR10,\t//mostly for rendertargets, might also be useful for overbight lightmaps.\n\tPTI_B10G11R11F,\t//unshared exponents\n\tPTI_RGB565,\t\t//16bit alphaless format.\n\tPTI_RGBA4444,\t//16bit format (gl)\n\tPTI_ARGB4444,\t//16bit format (d3d)\n\tPTI_RGBA5551,\t//16bit alpha format (gl).\n\tPTI_ARGB1555,\t//16bit alpha format (d3d).\n#define PTI_FIRSTCOMPRESSED PTI_E5BGR9\n\tPTI_E5BGR9,\t\t//mostly for fancy lightmaps (technically compressed, with a block size of 1...)\n\t//(desktop/tegra) compressed formats\n\tPTI_BC1_RGB,\t\t/*4bpp*/\n\tPTI_BC1_RGB_SRGB,\t/*4bpp*/\n\tPTI_BC1_RGBA,\t\t/*4bpp*/\n\tPTI_BC1_RGBA_SRGB,\t/*4bpp*/\n\tPTI_BC2_RGBA,\t\t/*8bpp*/\n\tPTI_BC2_RGBA_SRGB,\t/*8bpp*/\n\tPTI_BC3_RGBA,\t\t/*8bpp*/ //maybe add a bc3 normalmapswizzle type for d3d9?\n\tPTI_BC3_RGBA_SRGB,\t/*8bpp*/\n\tPTI_BC4_R,\t\t\t/*4bpp*/ //greyscale, kinda\n\tPTI_BC4_R_SNORM,\t/*4bpp*/\n\tPTI_BC5_RG,\t\t\t/*8bpp*/ //useful for normalmaps\n\tPTI_BC5_RG_SNORM,\t/*8bpp*/ //useful for normalmaps\n\tPTI_BC6_RGB_UFLOAT,\t/*8bpp*/ //unsigned (half) floats!\n\tPTI_BC6_RGB_SFLOAT,\t/*8bpp*/ //signed (half) floats!\n\tPTI_BC7_RGBA,\t\t/*8bpp*/ //multimode compression, using as many bits as bc2/bc3\n\tPTI_BC7_RGBA_SRGB,\t/*8bpp*/\n\t//(mobile/intel) compressed formats\n\tPTI_ETC1_RGB8,\t\t/*4bpp*/ //limited form\n\tPTI_ETC2_RGB8,\t\t/*4bpp*/ //extended form\n\tPTI_ETC2_RGB8A1,\t/*4bpp*/\n\tPTI_ETC2_RGB8A8,\t/*8bpp*/\n\tPTI_ETC2_RGB8_SRGB,\t/*4bpp*/\n\tPTI_ETC2_RGB8A1_SRGB,/*4bpp*/\n\tPTI_ETC2_RGB8A8_SRGB,/*8bpp*/\n\tPTI_EAC_R11,\t\t/*4bpp*/ //might be useful for overlays, with swizzles.\n\tPTI_EAC_R11_SNORM,\t/*4bpp*/ //no idea what this might be used for, whatever\n\tPTI_EAC_RG11,\t\t/*8bpp*/ //useful for normalmaps (calculate blue)\n\tPTI_EAC_RG11_SNORM,\t/*8bpp*/ //useful for normalmaps (calculate blue)\n\t//astc... zomg.\n#define PTI_ASTC_FIRST\tPTI_ASTC_4X4_LDR\n\tPTI_ASTC_4X4_LDR,\t/*8bpp*/ //ldr/srgb/hdr formats are technically all the same.\n\tPTI_ASTC_5X4_LDR,\t/*6.40*/ //srgb formats are different because of an extra srgb lookup step\n\tPTI_ASTC_5X5_LDR,\t/*5.12*/ //ldr formats are identical to hdr except for the extended colour modes disabled.\n\tPTI_ASTC_6X5_LDR,\t/*4.17*/\n\tPTI_ASTC_6X6_LDR,\t/*3.56*/\n\tPTI_ASTC_8X5_LDR,\t/*3.20*/\n\tPTI_ASTC_8X6_LDR,\t/*2.67*/\n\tPTI_ASTC_10X5_LDR,\t/*2.56*/\n\tPTI_ASTC_10X6_LDR,\t/*2.13*/\n\tPTI_ASTC_8X8_LDR,\t/*2bpp*/\n\tPTI_ASTC_10X8_LDR,\t/*1.60*/\n\tPTI_ASTC_10X10_LDR,\t/*1.28*/\n\tPTI_ASTC_12X10_LDR,\t/*1.07*/\n\tPTI_ASTC_12X12_LDR,\t/*0.89*/\n//\t#define ASTC3D\n#ifdef ASTC3D\n\tPTI_ASTC_3X3X3_LDR,\t/*4.74*/\t//astc volume ldr textures are worth tracking only to provide hints to cache them as 8bit instead of 16bit (reducing gpu cache needed).\n\tPTI_ASTC_4X3X3_LDR,\t/*3.56*/\n\tPTI_ASTC_4X4X3_LDR,\t/*2.67*/\n\tPTI_ASTC_4X4X4_LDR,\t/*2.00*/\n\tPTI_ASTC_5X4X4_LDR,\t/*1.60*/\n\tPTI_ASTC_5X5X4_LDR,\t/*1.28*/\n\tPTI_ASTC_5X5X5_LDR,\t/*1.02*/\n\tPTI_ASTC_6X5X5_LDR,\t/*0.85*/\n\tPTI_ASTC_6X6X5_LDR,\t/*0.71*/\n\tPTI_ASTC_6X6X6_LDR,\t/*0.59*/\n#endif\n\tPTI_ASTC_4X4_SRGB,\n\tPTI_ASTC_5X4_SRGB,\n\tPTI_ASTC_5X5_SRGB,\n\tPTI_ASTC_6X5_SRGB,\n\tPTI_ASTC_6X6_SRGB,\n\tPTI_ASTC_8X5_SRGB,\n\tPTI_ASTC_8X6_SRGB,\n\tPTI_ASTC_10X5_SRGB,\n\tPTI_ASTC_10X6_SRGB,\n\tPTI_ASTC_8X8_SRGB,\n\tPTI_ASTC_10X8_SRGB,\n\tPTI_ASTC_10X10_SRGB,\n\tPTI_ASTC_12X10_SRGB,\n\tPTI_ASTC_12X12_SRGB,\n#ifdef ASTC3D\n\tPTI_ASTC_3X3X3_SRGB,\n\tPTI_ASTC_4X3X3_SRGB,\n\tPTI_ASTC_4X4X3_SRGB,\n\tPTI_ASTC_4X4X4_SRGB,\n\tPTI_ASTC_5X4X4_SRGB,\n\tPTI_ASTC_5X5X4_SRGB,\n\tPTI_ASTC_5X5X5_SRGB,\n\tPTI_ASTC_6X5X5_SRGB,\n\tPTI_ASTC_6X6X5_SRGB,\n\tPTI_ASTC_6X6X6_SRGB,\n#endif\n\tPTI_ASTC_4X4_HDR,\t//these are not strictly necessary, and are likely to be treated identically to the ldr versions, but they may use extra features that the hardware does not support\n\tPTI_ASTC_5X4_HDR,\n\tPTI_ASTC_5X5_HDR,\n\tPTI_ASTC_6X5_HDR,\n\tPTI_ASTC_6X6_HDR,\n\tPTI_ASTC_8X5_HDR,\n\tPTI_ASTC_8X6_HDR,\n\tPTI_ASTC_10X5_HDR,\n\tPTI_ASTC_10X6_HDR,\n\tPTI_ASTC_8X8_HDR,\n\tPTI_ASTC_10X8_HDR,\n\tPTI_ASTC_10X10_HDR,\n\tPTI_ASTC_12X10_HDR,\n\tPTI_ASTC_12X12_HDR,\n#ifdef ASTC3D\n\tPTI_ASTC_3X3X3_HDR,\n\tPTI_ASTC_4X3X3_HDR,\n\tPTI_ASTC_4X4X3_HDR,\n\tPTI_ASTC_4X4X4_HDR,\n\tPTI_ASTC_5X4X4_HDR,\n\tPTI_ASTC_5X5X4_HDR,\n\tPTI_ASTC_5X5X5_HDR,\n\tPTI_ASTC_6X5X5_HDR,\n\tPTI_ASTC_6X6X5_HDR,\n\tPTI_ASTC_6X6X6_HDR,\n#define PTI_ASTC_LAST\tPTI_ASTC_6X6X6_HDR\n#else\n#define PTI_ASTC_LAST\tPTI_ASTC_12X12_HDR\n#endif\n\n\t//depth formats\n\tPTI_DEPTH16,\n\tPTI_DEPTH24,\n\tPTI_DEPTH32,\n\tPTI_DEPTH24_8,\n\n\t//non-native formats (generally requiring weird palettes that are not supported by hardware)\n\tTF_BGR24_FLIP,\t\t\t/*bgr byte order, no alpha channel nor pad, and bottom up*/\n\tTF_MIP4_P8,\t\t/*8bit 4-mip image in default palette, that will be loaded as an R8 texture.*/\n\tTF_MIP4_SOLID8,\t/*8bit 4-mip image in default palette, that will be expanded to an RGB texture.*/\n\tTF_MIP4_8PAL24,\t/*8bit 4-mip image with included palette*/\n\tTF_MIP4_8PAL24_T255,/*8bit 4-mip image with included palette where index 255 is alpha 0*/\n\tTF_SOLID8,      /*8bit quake-palette image*/\n\tTF_TRANS8,      /*8bit quake-palette image, index 255=transparent*/\n\tTF_TRANS8_FULLBRIGHT,   /*fullbright 8 - fullbright texels have alpha 255, everything else 0*/\n\tTF_HEIGHT8,     /*image data is greyscale, convert to a normalmap and load that, uploaded alpha contains the original heights*/\n\tTF_HEIGHT8PAL, /*source data is palette values rather than actual heights, generate a fallback heightmap. actual palette is ignored...*/\n\tTF_H2_T7G1, /*8bit data, odd indexes give greyscale transparence*/\n\tTF_H2_TRANS8_0, /*8bit data, 0 is transparent, not 255*/\n\tTF_H2_T4A4,     /*8bit data, weird packing*/\n\n\tPTI_LLLX8,\t\t/*RGBX data where the RGB values were all the same. we can convert to L8 to use less memory (common with shirt/pants/reflection)*/\n\tPTI_LLLA8,\t\t/*RGBA data where the RGB values were all the same. we can convert to LA8 to use less memory (common with gloss)*/\n\n\t/*this block requires an explicit (separate) palette*/\n\tTF_8PAL24,\n\tTF_8PAL32,\n\n#ifdef FTE_TARGET_WEB\n\t//weird specialcase mess to take advantage of webgl so we don't need redundant bloat where we're already strugging with potential heap limits\n\tPTI_WHOLEFILE,\n#endif\n\n\tPTI_MAX,\n\n\tTF_INVALID = PTI_INVALID,\n\tTF_RGBA32 = PTI_RGBA8,              /*rgba byte order*/\n\tTF_BGRA32 = PTI_BGRA8,              /*bgra byte order*/\n\tTF_RGBX32 = PTI_RGBX8,              /*rgb byte order, with extra wasted byte after blue*/\n\tTF_BGRX32 = PTI_BGRX8,              /*rgb byte order, with extra wasted byte after blue*/\n\tTF_RGB24 = PTI_RGB8,\t\t\t\t/*rgb byte order, no alpha channel nor pad, and regular top down*/\n\tTF_BGR24 = PTI_BGR8,               /*bgr byte order, no alpha channel nor pad, and regular top down*/\n\n\t//these are emulated formats. this 'case' value allows drivers to easily ignore them\n#define PTI_EMULATED \tTF_INVALID:case TF_BGR24_FLIP:case TF_MIP4_P8:case TF_MIP4_SOLID8:case TF_MIP4_8PAL24:case TF_MIP4_8PAL24_T255:case TF_SOLID8:case TF_TRANS8:case TF_TRANS8_FULLBRIGHT:case TF_HEIGHT8:case TF_HEIGHT8PAL:case TF_H2_T7G1:case TF_H2_TRANS8_0:case TF_H2_T4A4:case TF_8PAL24:case TF_8PAL32:case PTI_LLLX8:case PTI_LLLA8\n} uploadfmt_t;\n#define PTI_FULLMIPCHAIN 0x80000000\t//valid for Image_GetTexture (and thus GenMip0) to signify that there's a full round-down mipchain there, not a single one (or 4)\n\nqboolean SCR_ScreenShot (char *filename, enum fs_relative fsroot, void **buffer, int numbuffers, qintptr_t bytestride, int width, int height, enum uploadfmt fmt, qboolean writemeta);\n\nvoid SCR_DrawTwoDimensional(qboolean nohud);\n\nenum\n{\n\tLS_NONE,\n\tLS_CONNECTION,\n\tLS_SERVER,\n\tLS_CLIENT,\n};\nint SCR_GetLoadingStage(void);\nvoid SCR_SetLoadingStage(int stage);\nvoid SCR_SetLoadingFile(char *str);\n\n/*fonts*/\nvoid Font_Init(void);\nvoid Font_Shutdown(void);\nint Font_RegisterTrackerImage(const char *image);\t//returns a unicode char value that can be used to embed the char within a line of text.\nqboolean Font_TrackerValid(unsigned int imid);\nstruct font_s *Font_LoadFont(const char *fontfilename, float height, float scale, int outline, unsigned int flags);\n#define FONT_MONO 1\nvoid Font_Free(struct font_s *f);\nvoid Font_BeginString(struct font_s *font, float vx, float vy, int *px, int *py);\nvoid Font_BeginScaledString(struct font_s *font, float vx, float vy, float szx, float szy, float *px, float *py); /*avoid using*/\nvoid Font_Transform(float vx, float vy, int *px, int *py);\nint Font_CharHeight(void);\nfloat Font_CharVHeight(struct font_s *font);\nint Font_CharPHeight(struct font_s *font);\nint Font_GetTrueHeight(struct font_s *font);\nfloat Font_CharScaleHeight(void);\nint Font_CharWidth(unsigned int charflags, unsigned int codepoint);\nfloat Font_CharScaleWidth(unsigned int charflags, unsigned int codepoint);\nint Font_CharEndCoord(struct font_s *font, int x, unsigned int charflags, unsigned int codepoint);\nint Font_DrawChar(int px, int py, unsigned int charflags, unsigned int codepoint);\nfloat Font_DrawScaleChar(float px, float py, unsigned int charflags, unsigned int codepoint); /*avoid using*/\nvoid Font_EndString(struct font_s *font);\nvoid Font_InvalidateColour(vec4_t newcolour);\n/*these three functions deal with formatted blocks of text (including tabs and new lines)*/\nfte_inline conchar_t *Font_Decode(conchar_t *start, unsigned int *codeflags, unsigned int *codepoint)\n{\n\tif (*start & CON_LONGCHAR)\n\t\tif (!(*start & CON_RICHFORECOLOUR))\n\t\t{\n\t\t\t*codeflags = start[1] & CON_FLAGSMASK;\n\t\t\t*codepoint = ((start[0] & CON_CHARMASK)<<16) | (start[1] & CON_CHARMASK);\n\t\t\treturn start+2;\n\t\t}\n\n\t*codeflags = start[0] & CON_FLAGSMASK;\n\t*codepoint = start[0] & CON_CHARMASK;\n\treturn start+1;\n}\nconchar_t *Font_DecodeReverse(conchar_t *start, conchar_t *stop, unsigned int *codeflags, unsigned int *codepoint);\nint Font_LineBreaks(conchar_t *start, conchar_t *end, int maxpixelwidth, int maxlines, conchar_t **starts, conchar_t **ends);\nint Font_LineWidth(conchar_t *start, conchar_t *end);\nfloat Font_LineScaleWidth(conchar_t *start, conchar_t *end);\nvoid Font_LineDraw(int x, int y, conchar_t *start, conchar_t *end);\nconchar_t *Font_CharAt(int x, conchar_t *start, conchar_t *end);\nextern struct font_s *font_menu;\nextern struct font_s *font_default;\nextern struct font_s *font_console;\nextern struct font_s *font_tiny;\nvoid PR_ReleaseFonts(unsigned int purgeowner);\t//for menu/csqc\nvoid PR_ReloadFonts(qboolean reload);\n/*end fonts*/\n\n//normally we're not srgb aware, which means that while the intensity may APPEAR linear, it actually isn't.\nfte_inline float M_SRGBToLinear(float x, float mag)\n{\n\tx /= mag;\n\tif (x <= 0.04045f)\n\t\tx = x * (1.0f / 12.92f);\n\telse\n\t\tx = pow(( x + 0.055f) * (1.0f / 1.055f), 2.4f);\n\tx *= mag;\n\treturn x;\n}\nfte_inline float M_LinearToSRGB(float x, float mag)\n{\n\tx /= mag;\n\tif (x <= 0.00031308)\n\t\tx = 12.92 * x;\n\telse\n\t\tx = 1.055*pow(x,(float)(1.0 / 2.4) ) - 0.055;\n\tx *= mag;\n\treturn x;\n}\n//macros that are used to explicitly state that a value is srgb, and convert to linear as needed.\n#define SRGBf(x) ((vid.flags&VID_SRGBAWARE)?M_SRGBToLinear(x,1):x)\n#define SRGBb(x) ((vid.flags&VID_SRGBAWARE)?(unsigned char)M_SRGBToLinear(x,255):x)\n#define SRGB3(x,y,z) SRGBf(x),SRGBf(y),SRGBf(z)\n#define SRGBA(x,y,z,w) SRGBf(x),SRGBf(y),SRGBf(z),w\n\nvoid R_NetgraphInit(void);\nvoid R_NetGraph (void);\nvoid R_FrameTimeGraph (float frametime, float scale);\n"
  },
  {
    "path": "engine/client/skin.c",
    "content": "/*\nCopyright (C) 1996-1997 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  \n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n*/\n\n#include \"quakedef.h\"\n\n#ifdef QWSKINS\ncvar_t\t\tbaseskin = CVARD(\"baseskin\", \"\", \"The name of the player skin to use as a fallback.\");\ncvar_t\t\tnoskins = CVAR(\"noskins\", \"0\");\n\nextern cvar_t\tcl_teamskin;\nextern cvar_t\tcl_enemyskin;\n\nextern cvar_t\tr_fb_models;\n\nchar\t\tallskins[128];\n#define\tMAX_CACHED_SKINS\t\t256\t//max_clients is 255. hopefully this will not be reached, but hey.\nqwskin_t\t\tskins[MAX_CACHED_SKINS];\nint\t\t\tnumskins;\n\n//returns the name\nchar *Skin_FindName (player_info_t *sc)\n{\n\tint tracknum;\n\tchar *s;\n\tstatic char name[MAX_OSPATH];\n\n\tchar *skinforcing_team;\n\n\tif (allskins[0])\n\t{\n\t\tQ_strncpyz(name, allskins, sizeof(name));\n\t}\n\telse\n\t{\n\t\ts = InfoBuf_ValueForKey(&sc->userinfo, \"skin\");\n\t\tif (s && s[0])\n\t\t\tQ_strncpyz(name, s, sizeof(name));\n\t\telse\n\t\t\tQ_strncpyz(name, baseskin.string, sizeof(name));\n\t}\n\n\tif (cl.playerview[0].cam_state == CAM_FREECAM)\n\t\ttracknum = cl.playerview[0].playernum;\n\telse\n\t\ttracknum = cl.playerview[0].cam_spec_track;\n\n\tif (cl.players[tracknum].spectator)\n\t\tskinforcing_team = \"spec\";\n\telse\n\t\tskinforcing_team = cl.players[tracknum].team;\n\n\t//Don't force skins in splitscreen (it's probable that the new skin would be wrong).\n\t//Don't force skins in TF (where skins are already forced on a class basis by the mod).\n\t//Don't force skins on servers that have it disabled.\n\t//Don't force the local player's skin\n\tif (cl.splitclients<2 && !cl.teamfortress && !(cl.fpd & FPD_NO_FORCE_SKIN))\n\tif (&cl.players[tracknum] != sc)\n\t{\n\t\tchar *skinname = NULL;\n\t\tqboolean teammate;\n\n\t\tteammate = (cl.teamplay && !strcmp(sc->team, skinforcing_team)) ? true : false;\n/*\n\t\tif (cl.validsequence)\n\t\t{\n\t\t\tplayer_state_t *state = cl.frames[cl.parsecount & UPDATE_MASK].playerstate + (sc - cl.players);\n\t\t\tif (state->messagenum == cl.parsecount)\n\t\t\t{\n\t\t\t\tif ((state->effects & (EF_BLUE | EF_RED)) == (EF_BLUE | EF_RED))\n\t\t\t\t\tskinname = teammate ? cl_teambothskin.string : cl_enemybothskin.string;\n\t\t\t\telse if (state->effects & EF_BLUE)\n\t\t\t\t\tskinname = teammate ? cl_teamquadskin.string : cl_enemyquadskin.string;\n\t\t\t\telse if (state->effects & EF_RED)\n\t\t\t\t\tskinname = teammate ? cl_teampentskin.string : cl_enemypentskin.string;\n\t\t\t}\n\t\t}\n*/\n\t\tif (!skinname || !skinname[0])\n\t\t\tskinname = teammate ? cl_teamskin.string : cl_enemyskin.string;\n\n\t\t//per-player skin forcing\n\t\tif (teammate && sc->colourised && *sc->colourised->skin)\n\t\t\tskinname = sc->colourised->skin;\n\n\t\tif (skinname[0] && !strchr(skinname, '/'))\t// a '/' in a skin name is deemed as a model name, so we ignore it.\n\t\t\tQ_strncpyz(name, skinname, sizeof(name));\n\t}\n\n\tif (strstr(name, \"..\") || *name == '.')\n\t\tQ_strncpyz(name, baseskin.string, sizeof(name));\n\n\treturn name;\n}\n\nqwskin_t *Skin_Lookup (char *fullname)\n{\n\tint i;\n\tqwskin_t *skin;\n\tchar cleanname[sizeof(skin->name)];\n\tCOM_StripExtension (fullname, cleanname, sizeof(cleanname));\n\tfor (i=0 ; i<numskins ; i++)\n\t{\n\t\tif (!strcmp (cleanname, skins[i].name))\n\t\t{\n\t\t\tskin = &skins[i];\n\t\t\tSkin_TryCache8 (skin);\n\t\t\treturn skin;\n\t\t}\n\t}\n\n\t//FIXME: this is stupid.\n\tif (numskins == MAX_CACHED_SKINS)\n\t{\t// ran out of spots, so flush everything\n\t\tSkin_Skins_f ();\n\t}\n\n\tskin = &skins[numskins];\n\tnumskins++;\n\n\tmemset (skin, 0, sizeof(*skin));\n\tQ_strncpyz(skin->name, cleanname, sizeof(skin->name));\n\tSkin_TryCache8 (skin);\n\treturn skin;\n}\n/*\n================\nSkin_Find\n\n  Determines the best skin for the given scoreboard\n  slot, and sets scoreboard->skin\n\n================\n*/\nvoid Skin_Find (player_info_t *sc)\n{\n\tqwskin_t\t\t*skin;\n\tint\t\t\ti;\n\tchar\t\tname[128], *s;\n\n\n\tsc->model = NULL;\n\tsc->skinid = 0;\n\tsc->qwskin = NULL;\n\n\ts = Skin_FindName(sc);\n\tif (!*s)\n\t\treturn;\n\tCOM_StripExtension (s, name, sizeof(name));\n\n\tfor (i=0 ; i<numskins ; i++)\n\t{\n\t\tif (!strcmp (name, skins[i].name))\n\t\t{\n\t\t\tsc->qwskin = &skins[i];\n\t\t\treturn;\n\t\t}\n\t}\n\n\tif (numskins == MAX_CACHED_SKINS)\n\t{\t// ran out of spots, so flush everything\n\t\tSkin_Skins_f ();\n\t\treturn;\n\t}\n\n\tskin = &skins[numskins];\n\tsc->qwskin = skin;\n\tnumskins++;\n\n\tmemset (skin, 0, sizeof(*skin));\n\tQ_strncpyz(skin->name, name, sizeof(skin->name));\n}\n\n\nqbyte *Skin_ParsePCX(const char *name, const pcx_t *pcx, size_t pcxsize, int *pcxheight, int *pcxwidth)\n{\n\tconst qbyte\t*raw;\n\tqbyte\t*out, *pix;\n\tint\t\tx, y, srcw, srch;\n\tint\t\tdataByte;\n\tint\t\trunLength;\n\tint fbremap[256];\n\n\tunsigned short xmax, ymax, xmin, ymin;\n\n\t*pcxheight = *pcxwidth = 0;\n\n//\n// parse the PCX file\n//\n\traw = (const qbyte *)(pcx+1);\n\n\t//check format (sizes are checked later)\n\tif (pcx->manufacturer != 0x0a\n\t\t|| pcx->version != 5\n\t\t|| pcx->encoding != 1\n\t\t|| pcx->bits_per_pixel != 8)\n\t{\n\t\tCon_Printf (\"Bad skin %s (unsupported format)\\n\", name);\n\t\treturn NULL;\n\t}\n\n\txmax = (unsigned short)LittleShort(pcx->xmax);\n\tymax = (unsigned short)LittleShort(pcx->ymax);\n\txmin = (unsigned short)LittleShort(pcx->xmin);\n\tymin = (unsigned short)LittleShort(pcx->ymin);\n\n\tsrcw = xmax-xmin+1;\n\tsrch = ymax-ymin+1;\n\n\tif (srcw < 1 || srch < 1 || srcw > 320 || srch > 200)\n\t{\n\t\tCon_Printf (\"Bad skin %s (unsupported size)\\n\", name);\n\t\treturn NULL;\n\t}\n\n\t*pcxheight = srcw;\n\t*pcxwidth = srch;\n\n\tout = BZ_Malloc(srcw*srch);\n\tif (!out)\n\t\tSys_Error (\"Skin_Cache: couldn't allocate\");\n\n\t// TODO: we build a fullbright remap.. can we get rid of this?\n\tfor (x = 0; x < vid.fullbright; x++)\n\t\tfbremap[x] = x + (256-vid.fullbright);\t//fullbrights don't exist, so don't loose palette info.\n\n\n\tpix = out;\n//\tmemset (out, 0, skin->width*skin->height);\n\n\tdataByte = 0;\t//typically black (this is in case a 0*0 file is loaded... which won't happen anyway)\n\tfor (y=0 ; y < srch ; y++, pix += srcw)\n\t{\n\t\tfor (x=0 ; x < srcw ; )\n\t\t{\n\t\t\tif (raw - (const qbyte*)pcx > pcxsize)\n\t\t\t{\n\t\t\t\tBZ_Free(out);\n\t\t\t\tCon_Printf (\"Skin %s was malformed.  You should delete it.\\n\", name);\n\t\t\t\treturn NULL;\n\t\t\t}\n\t\t\tdataByte = *raw++;\n\n\t\t\tif((dataByte & 0xC0) == 0xC0)\n\t\t\t{\n\t\t\t\trunLength = dataByte & 0x3F;\n\t\t\t\tif (raw - (const qbyte*)pcx > pcxsize)\n\t\t\t\t{\n\t\t\t\t\tBZ_Free(out);\n\t\t\t\t\tCon_Printf (\"Skin %s was malformed.  You should delete it.\\n\", name);\n\t\t\t\t\treturn NULL;\n\t\t\t\t}\n\t\t\t\tdataByte = *raw++;\n\t\t\t}\n\t\t\telse\n\t\t\t\trunLength = 1;\n\n\t\t\t// skin sanity check\n\t\t\tif (runLength + x > xmax + 2)\n\t\t\t{\n\t\t\t\tBZ_Free(out);\n\t\t\t\tCon_Printf (\"Skin %s was malformed.  You should delete it.\\n\", name);\n\t\t\t\treturn NULL;\n\t\t\t}\n\n\t\t\tif (dataByte >= 256-vid.fullbright)\t//kill the fb componant\n\t\t\t\tif (!r_fb_models.ival)\n\t\t\t\t\tdataByte = fbremap[dataByte + vid.fullbright-256];\n\n\t\t\twhile(runLength-- > 0)\n\t\t\t\tpix[x++] = dataByte;\n\t\t}\n\n\t\t//pad the end of the scan line with the trailing pixel\n\t\tfor ( ; x < srcw ; )\n\t\t\tpix[x++] = dataByte;\n\t}\n\t//pad the bottom of the skin with that final pixel\n\tfor ( ; y < srch; y++, pix += srcw)\n\t\tfor (x = 0; x < srcw; )\n\t\t\tpix[x++] = dataByte;\n\n\tif ( raw - (const qbyte *)pcx > pcxsize)\n\t{\n\t\tBZ_Free(out);\n\t\tCon_Printf (\"Skin %s was malformed.  You should delete it.\\n\", name);\n\t\treturn NULL;\n\t}\n\n\treturn out;\n}\n\nvoid Skin_WorkerDone(void *skinptr, void *skindata, size_t width, size_t height)\n{\n\tqwskin_t *skin = skinptr;\n\tskin->width = width;\n\tskin->height = height;\n\tskin->skindata = skindata;\n\tif (skindata || TEXLOADED(skin->textures.base))\n\t\tskin->loadstate = SKIN_LOADED;\n\telse\n\t\tskin->loadstate = SKIN_FAILED;\n}\nvoid Skin_WorkerLoad(void *skinptr, void *data, size_t a, size_t b)\n{\n\tqwskin_t *skin = skinptr;\n\tchar\tname[MAX_QPATH];\n\tqbyte\t*out = NULL;\n\tint\t\tsrcw = 0, srch = 0;\n\n\tsize_t\tpcxsize = 0;\n\tvoid *pcxfiledata = NULL;\n\n\tconst char *skinpath = \"skins\";\n\n\tskin->textures.base = r_nulltex;\n\tif (gl_load24bit.ival)\n\t\tskin->textures.base = R_LoadHiResTexture(skin->name, skinpath, IF_LOADNOW|IF_NOALPHA|IF_NOPCX);\n\t\n\tif (skin->textures.base && skin->textures.base->width)\t//result was already posted and will be handled before Skin_WorkerDone.\n\t{\n\t\tif (!skin->textures.upperoverlay)\n\t\t{\n\t\t\tQ_snprintfz (name, sizeof(name), \"%s_shirt\", skin->name);\n\t\t\tTEXASSIGN(skin->textures.upperoverlay, R_LoadHiResTexture(name, skinpath, IF_LOADNOW));\n\t\t}\n\t\tif (!skin->textures.loweroverlay)\n\t\t{\n\t\t\tQ_snprintfz (name, sizeof(name), \"%s_pants\", skin->name);\n\t\t\tTEXASSIGN(skin->textures.loweroverlay, R_LoadHiResTexture(name, skinpath, IF_LOADNOW));\n\t\t}\n\t\tif (!skin->textures.fullbright)\n\t\t{\n\t\t\tQ_snprintfz (name, sizeof(name), \"%s_luma\", skin->name);\n\t\t\tTEXASSIGN(skin->textures.fullbright, R_LoadHiResTexture(skin->name, skinpath, IF_LOADNOW));\n\t\t}\n\t\tif (!skin->textures.specular)\n\t\t{\n\t\t\tQ_snprintfz (name, sizeof(name), \"%s_gloss\", skin->name);\n\t\t\tTEXASSIGN(skin->textures.specular, R_LoadHiResTexture(skin->name, skinpath, IF_LOADNOW));\n\t\t}\n\t}\n\telse\n\t{\n\t\tQ_snprintfz (name, sizeof(name), \"skins/%s.pcx\", skin->name);\n\t\tpcxfiledata = FS_LoadMallocFileFlags (name, FSLF_IGNOREPURE, &pcxsize);\n\t\tif (!pcxfiledata)\n\t\t{\t//FIXME: use 24bit skins even if gl_load24bit is failed\n\t\t\tif (!strcmp(skin->name, \"solid\") || !strcmp(skin->name, \"block\"))\n\t\t\t{\t//allow block colour, even if the file isn't found.\n\t\t\t\tsrcw = srch = 1;\n\t\t\t\tout = BZ_Malloc(srcw*srch);\n\t\t\t\tmemset(out, BOTTOM_DEFAULT | 15, srcw*srch);\n\t\t\t}\n\t\t\telse if (strcmp(skin->name, baseskin.string))\n\t\t\t{\n\t\t\t\t//if its not already the base skin, try the base (and warn if anything not base couldn't load).\n\t\t\t\tCon_Printf (\"Couldn't load skin %s\\n\", name);\n\t\t\t\tif (*baseskin.string)\n\t\t\t\t{\n\t\t\t\t\tQ_snprintfz (name, sizeof(name), \"skins/%s.pcx\", baseskin.string);\n\t\t\t\t\tpcxfiledata = FS_LoadMallocFileFlags (name, FSLF_IGNOREPURE, &pcxsize);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tif (out)\n\t\t;\n\telse if (pcxfiledata)\n\t{\n\t\tout = Skin_ParsePCX(name, pcxfiledata, pcxsize, &srcw, &srch);\n\t\tFS_FreeFile(pcxfiledata);\n\t}\n\telse\n\t{\n\t\tout = NULL;\n\t\tsrcw = srch = 0;\n\t}\n\tif (!out)\n\t\tCOM_AddWork(WG_MAIN, Skin_WorkerDone, skin, NULL, 0, 0);\n\telse\n\t\tCOM_AddWork(WG_MAIN, Skin_WorkerDone, skin, out, srcw, srch);\n}\n\n/*\n==========\nSkin_Cache\n\nReturns a pointer to the skin bitmap, or NULL to use the default\n==========\n*/\nqbyte\t*Skin_TryCache8 (qwskin_t *skin)\n{\n\tif (noskins.value==1) // JACK: So NOSKINS > 1 will show skins, but\n\t\treturn NULL;\t  // not download new ones.\n\n\tif (skin->loadstate==SKIN_LOADING)\n\t\treturn NULL;\n\tif (skin->loadstate==SKIN_LOADED)\n\t\treturn skin->skindata;\n\n//\n// load the pic from disk\n//\n\tif (strchr(skin->name, ' ')) //see if it's actually three colours\n\t{\n\t\tqbyte bv;\n\t\tint col[3];\n\t\tchar *s;\n\t\tqbyte\t*out;\n\n\t\ts = COM_Parse(skin->name);\n\t\tcol[0] = atof(com_token);\n\t\ts = COM_Parse(s);\n\t\tcol[1] = atof(com_token);\n\t\ts = COM_Parse(s);\n\t\tcol[2] = atof(com_token);\n\n\t\tbv = GetPaletteIndex(col[0], col[1], col[2]);\n\n\t\tskin->width = 320;\n\t\tskin->height = 200;\n\n\t\tskin->skindata = out = BZ_Malloc(skin->width*skin->height);\n\n\t\tmemset (out, bv, skin->width*skin->height);\n\n\t\tskin->loadstate = SKIN_LOADED;\n\n\t\treturn out;\n\t}\n\n\tif (skin->loadstate == SKIN_FAILED)\n\t\treturn NULL;\n\tskin->loadstate = SKIN_LOADING;\n\tCOM_AddWork(WG_LOADER, Skin_WorkerLoad, skin, NULL,0,0);\n\treturn NULL;\n}\n\n/*\n=================\nSkin_NextDownload\n=================\n*/\nvoid Skin_NextDownload (void)\n{\n\tplayer_info_t\t*sc;\n\tint\t\t\ti;\n\n\t//Con_Printf (\"Checking skins...\\n\");\n\tif (cls.protocol == CP_QUAKE2)\n\t{\n\t\tint j;\n\t\tchar *slash;\n\t\tchar *skinname;\n\t\tchar *dogtag;\n\t\tfor (i = 0; i != MAX_CLIENTS; i++)\n\t\t{\n\t\t\tsc = &cl.players[i];\n\t\t\tif (!sc->name[0])\n\t\t\t\tcontinue;\n\t\t\tskinname = InfoBuf_ValueForKey(&sc->userinfo, \"skin\");\n\t\t\tslash = strchr(skinname, '/');\n\t\t\tif (slash)\n\t\t\t{\n\t\t\t\t*slash = 0;\n\n\t\t\t\tdogtag = strchr(slash+1, '\\\\');\t//q2e\n\t\t\t\tif (dogtag)\n\t\t\t\t\t*dogtag++ = 0;\n\n\t\t\t\tCL_CheckOrEnqueDownloadFile(va(\"players/%s/tris.md2\", skinname), NULL, 0);\n\t\t\t\tfor (j = 1; j < MAX_PRECACHE_MODELS; j++)\n\t\t\t\t{\n\t\t\t\t\tif (!cl.model_name[j])\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tif (cl.model_name[j][0] == '#')\n\t\t\t\t\t\tCL_CheckOrEnqueDownloadFile(va(\"players/%s/%s\", skinname, cl.model_name[j]+1), NULL, 0);\n\t\t\t\t}\n\t\t\t\tfor (j = 1; j < MAX_PRECACHE_SOUNDS; j++)\n\t\t\t\t{\n\t\t\t\t\tif (!cl.sound_name[j])\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tif (cl.sound_name[j][0] == '*')\n\t\t\t\t\t\tCL_CheckOrEnqueDownloadFile(va(\"players/%s/%s\", skinname, cl.sound_name[j]+1), NULL, 0);\n\t\t\t\t}\n\t\t\t\t*slash = '/';\n\t\t\t\tCL_CheckOrEnqueDownloadFile(va(\"players/%s.pcx\", skinname), NULL, 0);\n\t\t\t}\n\t\t}\n\t\treturn;\n\t}\n\tfor (i = 0; i < MAX_CLIENTS; i++)\n\t{\n\t\tsc = &cl.players[i];\n\t\tsc->lastskin = NULL;\t//invalidate any 'safe' skins\n\t\tif (!sc->name[0])\n\t\t\tcontinue;\n\t\tSkin_Find (sc);\n\t\tif (noskins.ival || !sc->qwskin)\n\t\t\tcontinue;\n\t\tif (strchr(sc->qwskin->name, ' '))\t//skip over skins using a space\n\t\t\tcontinue;\n\t\tif (!*sc->qwskin->name)\n\t\t\tcontinue;\n\n\t\tCL_CheckOrEnqueDownloadFile(va(\"skins/%s.pcx\", sc->qwskin->name), NULL, 0);\n\t}\n\n\t// now load them in for real\n\tfor (i=0 ; i<MAX_CLIENTS ; i++)\n\t{\n\t\tsc = &cl.players[i];\n\t\tif (!sc->name[0] || !sc->qwskin)\n\t\t\tcontinue;\n\t\tSkin_TryCache8 (sc->qwskin);\n\t\t//sc->qwskin = NULL;\n\t}\n}\n\n//called from a few places when some skin cheat is applied.\n//flushes all player skins.\nvoid Skin_FlushPlayers(void)\n{\t//wipe the skin info\n\tint i;\n\tfor (i = 0; i < MAX_CLIENTS; i++)\n\t\tcl.players[i].qwskin = NULL;\n\n\tfor (i = 0; i < cl.allocated_client_slots; i++)\n\t\tCL_NewTranslation(i);\n}\n\n//call on shutdown. does not refresh any skins at all.\nvoid Skin_FlushAll(void)\n{\t//wipe the skin info\n\tint i;\n\tfor (i=0 ; i<numskins ; i++)\n\t{\n\t\tif (skins[i].loadstate==SKIN_LOADING)\n\t\t\tCOM_WorkerPartialSync(&skins[i], &skins[i].loadstate, SKIN_LOADING);\n\t\tif (skins[i].skindata)\n\t\t\tBZ_Free(skins[i].skindata);\n\t}\n\tnumskins = 0;\n\tfor (i = 0; i < MAX_CLIENTS; i++)\n\t{\n\t\tcl.players[i].qwskin = NULL;\n\t\tcl.players[i].lastskin = NULL;\n\t}\n}\n\n/*\n==========\nSkin_Skins_f\n\nRefind all skins, downloading if needed.\n==========\n*/\nvoid\tSkin_Skins_f (void)\n{\n\tint\t\ti;\n\n\tif (cls.state == ca_disconnected)\n\t{\n\t\tCon_TPrintf (\"Can't \\\"%s\\\", not connected\\n\", Cmd_Argv(0));\n\t\treturn;\n\t}\n\n\tR_GAliasFlushSkinCache(false);\n\tfor (i=0 ; i<numskins ; i++)\n\t{\n\t\tif (skins[i].loadstate==SKIN_LOADING)\n\t\t\tCOM_WorkerPartialSync(&skins[i], &skins[i].loadstate, SKIN_LOADING);\n\t\tif (skins[i].skindata)\n\t\t\tBZ_Free(skins[i].skindata);\n\t}\n\tnumskins = 0;\n\tfor (i = 0; i < MAX_CLIENTS; i++)\n\t\tcl.players[i].lastskin = NULL;\n\n\tSkin_NextDownload ();\n\n\n//\tif (Cmd_FromServer())\n\t{\n\t\tSCR_SetLoadingStage(LS_NONE);\n\n\t\tCL_SendClientCommand(true, \"begin %i\", cl.servercount);\n\t}\n}\n\n\n/*\n==========\nSkin_AllSkins_f\n\nSets all skins to one specific one\n==========\n*/\nvoid\tSkin_AllSkins_f (void)\n{\n\tstrcpy (allskins, Cmd_Argv(1));\n\tSkin_Skins_f ();\n}\n\nvoid Skin_FlushSkin(char *name)\n{\n\tint i;\n\tchar sname[16]=\"\";\n\tif (strncmp(name, \"skins/\", 6))\n\t\treturn;\n\tQ_strncpyz(sname, (name + 6), strlen(name+6)-3);\n\tfor (i=0 ; i<numskins ; i++)\n\t{\n\t\tif (!strcmp(skins[i].name, sname))\n\t\t{\n\t\t\tskins[i].loadstate = SKIN_NOTLOADED;\n\t\t\tmemset(&skins[i].textures, 0, sizeof(skins[i].textures));\n\t\t}\n\t}\n}\n#else\nvoid Skin_FlushPlayers(void)\n{\n}\n//required for the qw protocol (server stuffcmds 'skins' to get the client to send 'begin'. *sigh*\nvoid\tSkin_Skins_f (void)\n{\n\tif (cls.state == ca_disconnected)\n\t{\n\t\tCon_TPrintf (\"Can't \\\"%s\\\", not connected\\n\", Cmd_Argv(0));\n\t\treturn;\n\t}\n\n\tR_GAliasFlushSkinCache(false);\n\n//\tif (Cmd_FromServer())\n\t{\n\t\tSCR_SetLoadingStage(LS_NONE);\n\n\t\tCL_SendClientCommand(true, \"begin %i\", cl.servercount);\n\t}\n}\n#endif\n\n"
  },
  {
    "path": "engine/client/snd_al.c",
    "content": "#include \"quakedef.h\"\r\n\r\n/*\r\nThis is based on Jogi's OpenAL support.\r\nMuch of it is stripped, to try and get it clean/compliant.\r\n\r\nEmscripten/WebAudio is buggy or limited.\r\nThis means we force distance models and use hacks to avoid bugs in browsers.\r\nWe also have no doppler with WebAudio.\r\n*/\r\n\r\n/*Bug list:\r\n\r\n\tunderwater cacaphoy\r\n\t\topenal bug with reverb. either disable reverb or disable openal.\r\n\r\n\t\"build/openal-soft-1.19.1/Alc/filters/filter.c:25: BiquadFilter_setParams: Assertion `gain > 0.00001f' failed.\" + SIGABRT\r\n\t\tbug started with 1.19.1. Not fte's bug. either disable reverb or disable openal.\r\n\t\t(happens when reverb properties are changed too fast)\r\n\r\n\tAL_OUT_OF_MEMORY\r\n\t\tshitty openal implementation with too-low limits on number of sources.\r\n\r\n\tAL_INVALID_VALUE\r\n\t\tshitty (apple) openal implementation with too-low limits on number of sources.\r\n\r\n*/\r\n\r\n\r\n#ifdef AVAIL_OPENAL\r\n\r\n#ifdef FTE_TARGET_WEB\r\n#include <emscripten/emscripten.h>\r\n\t//emscripten provides an openal -> webaudio wrapper. its not the best, but does get the job done.\r\n\t#define OPENAL_STATIC\t\t//our javascript port doesn't support dynamic linking  (bss+data segments get too messy).\r\n\t#define SDRVNAME \"WebAudio\"\t//IE doesn't support webaudio, resulting in noticable error messages about no openal, which is technically incorrect. So lets be clear about this.\r\n\t#define SDRVNAMEDESC \"WebAudio:\"\r\n\r\n\t#define QMIX_SDRVNAME \"qmix\"\t//IE doesn't support webaudio, resulting in noticable error messages about no openal, which is technically incorrect. So lets be clear about this.\r\n\t#define QMIX_SDRVNAMEDESC \"QMIX:\"\r\n#else\r\n\t#define SDRVNAME \"OpenAL\"\r\n\t#define SDRVNAMEDESC \"OAL:\"\r\n\t#define QMIX_SDRVNAME \"qmix\"\r\n\t#define QMIX_SDRVNAMEDESC \"OALQ:\"\r\n\t#define USEEFX\r\n#endif\r\n#ifndef HAVE_MIXER\r\n\t#undef SDRVNAMEDESC\r\n\t#define SDRVNAMEDESC \"\"\t//remove the prefixes in user-visible desciptions when there's (probably) no other devices anyway\r\n#endif\r\n\r\n#ifdef OPENAL_STATIC\r\n#include <AL/al.h>\t//output\r\n#include <AL/alc.h>\t//context+input\r\n#ifdef USEEFX\r\n#include <AL/efx.h>\r\n#endif\r\n\r\n#ifndef AL_API\r\n#define AL_API\r\n#endif\r\n#ifndef AL_APIENTRY\r\n#define AL_APIENTRY\r\n#endif\r\n\r\n#define palGetError\t\t\t\talGetError\r\n#define palSourcef\t\t\t\talSourcef\r\n#define palSourcei\t\t\t\talSourcei\r\n#define palSourcePlayv\t\t\talSourcePlayv\r\n#define palSourceStopv\t\t\talSourceStopv\r\n#define palSourcePlay\t\t\talSourcePlay\r\n#define palSourceStop\t\t\talSourceStop\r\n#define palDopplerFactor\t\talDopplerFactor\r\n#define palGenBuffers\t\t\talGenBuffers\r\n#define palIsBuffer\t\t\t\talIsBuffer\r\n#define palBufferData\t\t\talBufferData\r\n#define palBufferiv\t\t\t\talBufferiv\r\n#define palDeleteBuffers\t\talDeleteBuffers\r\n#define palListenerfv\t\t\talListenerfv\r\n#define palSourcefv\t\t\t\talSourcefv\r\n#define palGetString\t\t\talGetString\r\n#define palGenSources\t\t\talGenSources\r\n#define palIsSource\t\t\t\talIsSource\r\n#define palListenerf\t\t\talListenerf\r\n#define palDeleteBuffers\t\talDeleteBuffers\r\n#define palDeleteSources\t\talDeleteSources\r\n#define palSpeedOfSound\t\t\talSpeedOfSound\r\n#define palDistanceModel\t\talDistanceModel\r\n#define palGetSourcei\t\t\talGetSourcei\r\n#define palSourceQueueBuffers\talSourceQueueBuffers\r\n#define palSourceUnqueueBuffers\talSourceUnqueueBuffers\r\n#define\tpalIsExtensionPresent\talIsExtensionPresent\r\n\r\n\r\n#define palcOpenDevice\t\t\talcOpenDevice\r\n#define palcCloseDevice\t\t\talcCloseDevice\r\n#define palcCreateContext\t\talcCreateContext\r\n#define palcDestroyContext\t\talcDestroyContext\r\n#define palcMakeContextCurrent\talcMakeContextCurrent\r\n#define palcProcessContext\t\talcProcessContext\r\n#define palcGetString\t\t\talcGetString\r\n#define palcGetIntegerv\t\t\talcGetIntegerv\r\n#define palcIsExtensionPresent\talcIsExtensionPresent\r\n#define palcGetProcAddress\t\talcGetProcAddress\r\n\r\n#define palGetProcAddress alGetProcAddress\r\n\r\n//voip stuff\r\n#define palcCaptureOpenDevice\talcCaptureOpenDevice\r\n#define palcCaptureCloseDevice\talcCaptureCloseDevice\r\n#define palcCaptureStart\t\talcCaptureStart\r\n#define palcCaptureStop\t\t\talcCaptureStop\r\n#define palcCaptureSamples\t\talcCaptureSamples\r\n\r\n#ifdef FTE_TARGET_WEB\t//emscripten sucks.\r\nAL_API void (AL_APIENTRY alSpeedOfSound)( ALfloat value ) {}\r\n#endif\r\n#else\r\n\r\n#if defined(_WIN32)\r\n #define AL_APIENTRY __cdecl\r\n#else\r\n #define AL_APIENTRY\r\n#endif\r\n#define AL_API\r\n\r\n#undef AL_ALEXT_PROTOTYPES\r\n\r\n\r\ntypedef int ALint;\r\ntypedef unsigned int ALuint;\r\ntypedef float ALfloat;\r\ntypedef int ALenum;\r\ntypedef char ALchar;\r\ntypedef char ALboolean;\r\ntypedef int ALsizei;\r\ntypedef void ALvoid;\r\ntypedef unsigned char ALubyte;\r\n\r\nstatic dllhandle_t *openallib;\r\nstatic qboolean openallib_tried;\r\nstatic AL_API ALenum (AL_APIENTRY *palGetError)( void );\r\nstatic AL_API void (AL_APIENTRY *palSourcef)( ALuint sid, ALenum param, ALfloat value ); \r\nstatic AL_API void (AL_APIENTRY *palSourcei)( ALuint sid, ALenum param, ALint value ); \r\nstatic AL_API void (AL_APIENTRY *palSource3i)(ALuint source, ALenum param, ALint value1, ALint value2, ALint value3);\r\n\r\nstatic AL_API void (AL_APIENTRY *palSourcePlayv)( ALsizei ns, const ALuint *sids );\r\nstatic AL_API void (AL_APIENTRY *palSourceStopv)( ALsizei ns, const ALuint *sids );\r\nstatic AL_API void (AL_APIENTRY *palSourcePlay)( ALuint sid );\r\nstatic AL_API void (AL_APIENTRY *palSourceStop)( ALuint sid );\r\n\r\nstatic AL_API void (AL_APIENTRY *palDopplerFactor)( ALfloat value );\r\n\r\nstatic AL_API void (AL_APIENTRY *palGenBuffers)( ALsizei n, ALuint* buffers );\r\nstatic AL_API ALboolean (AL_APIENTRY *palIsBuffer)( ALuint bid );\r\nstatic AL_API void (AL_APIENTRY *palBufferData)( ALuint bid, ALenum format, const ALvoid* data, ALsizei size, ALsizei freq );\r\nstatic AL_API void (AL_APIENTRY *palBufferiv)(ALuint buffer, ALenum param, const ALint *values);\r\nstatic AL_API void (AL_APIENTRY *palDeleteBuffers)( ALsizei n, const ALuint* buffers );\r\n\r\nstatic AL_API void (AL_APIENTRY *palListenerfv)( ALenum param, const ALfloat* values ); \r\nstatic AL_API void (AL_APIENTRY *palSourcefv)( ALuint sid, ALenum param, const ALfloat* values ); \r\nstatic AL_API const ALchar* (AL_APIENTRY *palGetString)( ALenum param );\r\nstatic AL_API void (AL_APIENTRY *palGenSources)( ALsizei n, ALuint* sources ); \r\nstatic AL_API ALboolean (AL_APIENTRY *palIsSource)( ALuint sourceName );\r\nstatic AL_API void (AL_APIENTRY *palListenerf)( ALenum param, ALfloat value );\r\nstatic AL_API void (AL_APIENTRY *palDeleteBuffers)( ALsizei n, const ALuint* buffers );\r\nstatic AL_API void (AL_APIENTRY *palDeleteSources)( ALsizei n, const ALuint* sources );\r\nstatic AL_API void (AL_APIENTRY *palSpeedOfSound)( ALfloat value );\r\nstatic AL_API void (AL_APIENTRY *palDistanceModel)( ALenum distanceModel );\r\n\r\n//needed for streaming\r\nstatic AL_API void (AL_APIENTRY *palGetSourcei)(ALuint source, ALenum pname, ALint *value);\r\nstatic AL_API void (AL_APIENTRY *palSourceQueueBuffers)(ALuint source, ALsizei n, ALuint* buffers);\r\nstatic AL_API void (AL_APIENTRY *palSourceUnqueueBuffers)(ALuint source, ALsizei n, ALuint* buffers);\r\n\r\n//for extensions like efx\r\nstatic AL_API ALboolean (AL_APIENTRY *palIsExtensionPresent)(const ALchar *fextame);\r\nstatic AL_API void*(AL_APIENTRY *palGetProcAddress)(const ALchar *fname);\r\n\r\n#define AL_NONE                                   0\r\n#define AL_FALSE                                  0\r\n#define AL_TRUE                                   1\r\n#define AL_SOURCE_RELATIVE                        0x202\r\n#define AL_PITCH                                  0x1003\r\n#define AL_POSITION                               0x1004\r\n#define AL_VELOCITY                               0x1006\r\n#define AL_LOOPING                                0x1007\r\n#define AL_BUFFER                                 0x1009\r\n#define AL_GAIN                                   0x100A\r\n#define AL_ORIENTATION                            0x100F\r\n#define\tAL_SOURCE_STATE                           0x1010\r\n#define\tAL_PLAYING                                0x1012\t\r\n#define\tAL_BUFFERS_QUEUED                         0x1015\r\n#define\tAL_BUFFERS_PROCESSED                      0x1016\r\n#define AL_REFERENCE_DISTANCE                     0x1020\r\n#define AL_ROLLOFF_FACTOR                         0x1021\r\n#define AL_MAX_DISTANCE                           0x1023\r\n#define AL_SAMPLE_OFFSET                          0x1025\r\n#define\tAL_SOURCE_TYPE                            0x1027\r\n#define\tAL_STREAMING                              0x1029\r\n#define AL_FORMAT_MONO8                           0x1100\r\n#define AL_FORMAT_MONO16                          0x1101\r\n#define AL_FORMAT_STEREO8                         0x1102\r\n#define AL_FORMAT_STEREO16                        0x1103\r\n#define AL_INVALID_NAME                           0xA001\r\n#define AL_INVALID_ENUM                           0xA002\r\n#define AL_INVALID_VALUE                          0xA003\r\n#define AL_INVALID_OPERATION                      0xA004\r\n#define AL_OUT_OF_MEMORY                          0xA005\r\n#define AL_VENDOR                                 0xB001\r\n#define AL_VERSION                                0xB002\r\n#define AL_RENDERER                               0xB003\r\n#define AL_EXTENSIONS                             0xB004\r\n#define AL_DISTANCE_MODEL                         0xD000\r\n#define AL_INVERSE_DISTANCE                       0xD001\r\n#define AL_INVERSE_DISTANCE_CLAMPED               0xD002\r\n#define AL_LINEAR_DISTANCE                        0xD003\r\n#define AL_LINEAR_DISTANCE_CLAMPED                0xD004\r\n#define AL_EXPONENT_DISTANCE                      0xD005\r\n#define AL_EXPONENT_DISTANCE_CLAMPED              0xD006\r\n\r\n\r\n\r\n\r\n#if defined(_WIN32)\r\n #define ALC_APIENTRY __cdecl\r\n#else\r\n #define ALC_APIENTRY\r\n#endif\r\n#define ALC_API\r\n\r\ntypedef char ALCboolean;\r\ntypedef char ALCchar;\r\ntypedef int ALCint;\r\ntypedef unsigned int ALCuint;\r\ntypedef int ALCenum;\r\ntypedef size_t ALCsizei;\r\ntypedef struct ALCdevice_struct ALCdevice;\r\ntypedef struct ALCcontext_struct ALCcontext;\r\ntypedef void ALCvoid;\r\n\r\nstatic ALC_API ALCdevice *     (ALC_APIENTRY *palcOpenDevice)( const ALCchar *devicename );\r\nstatic ALC_API ALCboolean      (ALC_APIENTRY *palcCloseDevice)( ALCdevice *device );\r\n\r\nstatic ALC_API ALCcontext *    (ALC_APIENTRY *palcCreateContext)( ALCdevice *device, const ALCint* attrlist );\r\nstatic ALC_API void            (ALC_APIENTRY *palcDestroyContext)( ALCcontext *context );\r\nstatic ALC_API ALCboolean      (ALC_APIENTRY *palcMakeContextCurrent)( ALCcontext *context );\r\nstatic ALC_API void            (ALC_APIENTRY *palcProcessContext)( ALCcontext *context );\r\n\r\nstatic ALC_API const ALCchar * (ALC_APIENTRY *palcGetString)( ALCdevice *device, ALCenum param );\r\nstatic ALC_API ALCboolean      (ALC_APIENTRY *palcIsExtensionPresent)( ALCdevice *device, const ALCchar *extname );\r\nstatic ALC_API void*           (ALC_APIENTRY *palcGetProcAddress)(ALCdevice *device, const ALCchar *funcname);\r\n\r\n\r\n#define ALC_DEFAULT_DEVICE_SPECIFIER             0x1004\r\n#define ALC_DEVICE_SPECIFIER                     0x1005\r\n#define ALC_EXTENSIONS                           0x1006\r\n#define ALC_ALL_DEVICES_SPECIFIER\t\t\t\t 0x1013\t//ALC_ENUMERATE_ALL_EXT\r\n\r\n//#include \"AL/alut.h\"\r\n//#include \"AL/al.h\"\r\n//#include \"AL/alext.h\"\r\n\r\n#if defined(VOICECHAT)\r\n//capture-specific stuff\r\nstatic void           (ALC_APIENTRY *palcGetIntegerv)( ALCdevice *device, ALCenum param, ALCsizei size, ALCint *data );\r\nstatic ALCdevice *    (ALC_APIENTRY *palcCaptureOpenDevice)( const ALCchar *devicename, ALCuint frequency, ALCenum format, ALCsizei buffersize );\r\nstatic ALCboolean     (ALC_APIENTRY *palcCaptureCloseDevice)( ALCdevice *device );\r\nstatic void           (ALC_APIENTRY *palcCaptureStart)( ALCdevice *device );\r\nstatic void           (ALC_APIENTRY *palcCaptureStop)( ALCdevice *device );\r\nstatic void           (ALC_APIENTRY *palcCaptureSamples)( ALCdevice *device, ALCvoid *buffer, ALCsizei samples );\r\n#define ALC_CAPTURE_DEVICE_SPECIFIER             0x310\r\n#define ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER     0x311\r\n#define ALC_CAPTURE_SAMPLES                      0x312\r\n#endif\r\n\r\n\r\n//efx\r\n#ifdef USEEFX\r\n#define AL_AUXILIARY_SEND_FILTER                 0x20006\r\n#define AL_FILTER_NULL                           0x0000\r\n#define AL_EFFECTSLOT_EFFECT                     0x0001\r\n#define AL_EFFECT_TYPE                           0x8001\r\n#define AL_EFFECT_REVERB                         0x0001\r\n#define AL_EFFECT_EAXREVERB                      0x8000\r\n\r\n#define AL_REVERB_DENSITY                        0x0001\r\n#define AL_REVERB_DIFFUSION                      0x0002\r\n#define AL_REVERB_GAIN                           0x0003\r\n#define AL_REVERB_GAINHF                         0x0004\r\n#define AL_REVERB_DECAY_TIME                     0x0005\r\n#define AL_REVERB_DECAY_HFRATIO                  0x0006\r\n#define AL_REVERB_REFLECTIONS_GAIN               0x0007\r\n#define AL_REVERB_REFLECTIONS_DELAY              0x0008\r\n#define AL_REVERB_LATE_REVERB_GAIN               0x0009\r\n#define AL_REVERB_LATE_REVERB_DELAY              0x000A\r\n#define AL_REVERB_AIR_ABSORPTION_GAINHF          0x000B\r\n#define AL_REVERB_ROOM_ROLLOFF_FACTOR            0x000C\r\n#define AL_REVERB_DECAY_HFLIMIT                  0x000D\r\n\r\n/* EAX Reverb effect parameters */\r\n#define AL_EAXREVERB_DENSITY                     0x0001\r\n#define AL_EAXREVERB_DIFFUSION                   0x0002\r\n#define AL_EAXREVERB_GAIN                        0x0003\r\n#define AL_EAXREVERB_GAINHF                      0x0004\r\n#define AL_EAXREVERB_GAINLF                      0x0005\r\n#define AL_EAXREVERB_DECAY_TIME                  0x0006\r\n#define AL_EAXREVERB_DECAY_HFRATIO               0x0007\r\n#define AL_EAXREVERB_DECAY_LFRATIO               0x0008\r\n#define AL_EAXREVERB_REFLECTIONS_GAIN            0x0009\r\n#define AL_EAXREVERB_REFLECTIONS_DELAY           0x000A\r\n#define AL_EAXREVERB_REFLECTIONS_PAN             0x000B\r\n#define AL_EAXREVERB_LATE_REVERB_GAIN            0x000C\r\n#define AL_EAXREVERB_LATE_REVERB_DELAY           0x000D\r\n#define AL_EAXREVERB_LATE_REVERB_PAN             0x000E\r\n#define AL_EAXREVERB_ECHO_TIME                   0x000F\r\n#define AL_EAXREVERB_ECHO_DEPTH                  0x0010\r\n#define AL_EAXREVERB_MODULATION_TIME             0x0011\r\n#define AL_EAXREVERB_MODULATION_DEPTH            0x0012\r\n#define AL_EAXREVERB_AIR_ABSORPTION_GAINHF       0x0013\r\n#define AL_EAXREVERB_HFREFERENCE                 0x0014\r\n#define AL_EAXREVERB_LFREFERENCE                 0x0015\r\n#define AL_EAXREVERB_ROOM_ROLLOFF_FACTOR         0x0016\r\n#define AL_EAXREVERB_DECAY_HFLIMIT               0x0017\r\n#endif\r\n#endif\r\n\r\n#ifdef USEEFX\r\n\t#if defined(AL_ALEXT_PROTOTYPES) && defined(OPENAL_STATIC)\r\n\t\t#define\tpalAuxiliaryEffectSloti\t\t\talAuxiliaryEffectSloti\r\n\t\t#define\tpalGenAuxiliaryEffectSlots\t\talGenAuxiliaryEffectSlots\r\n\t\t#define\tpalDeleteAuxiliaryEffectSlots\talDeleteAuxiliaryEffectSlots\r\n\t\t#define\tpalDeleteEffects\t\t\t\talDeleteEffects\r\n\t\t#define\tpalGenEffects\t\t\t\t\talGenEffects\r\n\t\t#define\tpalEffecti\t\t\t\t\t\talEffecti\r\n//\t\t#define\tpalEffectiv\t\t\t\t\t\talEffectiv\r\n\t\t#define\tpalEffectf\t\t\t\t\t\talEffectf\r\n\t\t#define\tpalEffectfv\t\t\t\t\t\talEffectfv\r\n\t#else\r\n\t\tstatic void (AL_APIENTRY *palAuxiliaryEffectSloti)(ALuint effectslot, ALenum param, ALint iValue);\r\n\t\tstatic ALvoid (AL_APIENTRY *palGenAuxiliaryEffectSlots)(ALsizei n, ALuint *effectslots);\r\n\t\tstatic ALvoid (AL_APIENTRY *palDeleteAuxiliaryEffectSlots)(ALsizei n, const ALuint *effectslots);\r\n\t\tstatic ALvoid (AL_APIENTRY *palDeleteEffects)(ALsizei n, const ALuint *effects);\r\n\r\n\t\tstatic ALvoid (AL_APIENTRY *palGenEffects)(ALsizei n, ALuint *effects);\r\n\t\tstatic ALvoid (AL_APIENTRY *palEffecti)(ALuint effect, ALenum param, ALint iValue);\r\n//\t\tstatic ALvoid (AL_APIENTRY *palEffectiv)(ALuint effect, ALenum param, const ALint *piValues);\r\n\t\tstatic ALvoid (AL_APIENTRY *palEffectf)(ALuint effect, ALenum param, ALfloat flValue);\r\n\t\tstatic ALvoid (AL_APIENTRY *palEffectfv)(ALuint effect, ALenum param, const ALfloat *pflValues);\r\n\t#endif\r\n#endif\r\n\r\n//AL_EXT_float32\r\n#define AL_FORMAT_MONO_FLOAT32                      0x10010\r\n#define AL_FORMAT_STEREO_FLOAT32                    0x10011\r\n\r\n//AL_SOFT_source_spatialize\r\n#define AL_SOURCE_SPATIALIZE_SOFT\t\t\t\t\t0x1214\r\n\r\n//AL_SOFT_loop_points\r\n#define AL_LOOP_POINTS_SOFT\t\t\t\t\t\t\t0x2015\r\n\r\n//ALC_SOFT_HRTF\r\n#define\tALC_HRTF_SOFT\t\t\t\t\t\t\t\t0x1992\r\n#define\t\tALC_DONT_CARE_SOFT\t\t\t\t\t\t0x0002\r\n#define ALC_HRTF_STATUS_SOFT\t\t\t\t\t\t0x1993\r\n#define\t\tALC_HRTF_DISABLED_SOFT\t\t\t\t\t0x0000\r\n#define\t\tALC_HRTF_ENABLED_SOFT\t\t\t\t\t0x0001\r\n#define\t\tALC_HRTF_DENIED_SOFT\t\t\t\t\t0x0002\r\n#define\t\tALC_HRTF_REQUIRED_SOFT\t\t\t\t\t0x0003\r\n#define\t\tALC_HRTF_HEADPHONES_DETECTED_SOFT\t\t0x0004\r\n#define\t\tALC_HRTF_UNSUPPORTED_FORMAT_SOFT\t\t0x0005\r\n#define\tALC_NUM_HRTF_SPECIFIERS_SOFT\t\t\t\t0x1994\r\n#define\tALC_HRTF_SPECIFIER_SOFT\t\t\t\t\t\t0x1995\r\n#define\tALC_HRTF_ID_SOFT\t\t\t\t\t\t\t0x1996\r\nstatic const ALCchar *(*palcGetStringiSOFT)(ALCdevice *device, ALCenum paramName, ALCsizei index);\r\n\r\n#define SOUNDVARS SDRVNAME\" variables\"\r\n\r\n\r\nextern sfx_t *known_sfx; //sfxindex = (sfx-known_sfx);\r\n\r\n#ifdef USEEFX\r\nstatic ALuint OpenAL_LoadEffect(const struct reverbproperties_s *reverb);\r\n#endif\r\nstatic void OpenAL_Shutdown (soundcardinfo_t *sc);\r\nstatic void QDECL OnChangeALSettings (cvar_t *var, char *value);\r\n/*\r\nstatic void S_Init_f(void);\r\nstatic void S_Info(void);\r\n\r\nstatic void S_Shutdown_f(void);\r\n*/\r\n#ifdef FTE_TARGET_WEB\r\nstatic cvar_t s_al_disable = CVARD(\"s_al_disable\", \"1\", \"0: OpenAL works (generally as the highest priority).\\n1: OpenAL will be used only when a specific device is selected.\\n2: Don't allow ANY use of OpenAl.\\nWith OpenAL disabled, audio ouput will fall back to platform-specific output, avoiding miscilaneous third-party openal limitation bugs.\");\r\n#else\r\nstatic cvar_t s_al_disable = CVARD(\"s_al_disable\", \"0\", \"0: OpenAL works (generally as the highest priority).\\n1: OpenAL will be used only when a specific device is selected.\\n2: Don't allow ANY use of OpenAl.\\nWith OpenAL disabled, audio ouput will fall back to platform-specific output, avoiding miscilaneous third-party openal limitation bugs.\");\r\n#endif\r\nstatic cvar_t s_al_debug = CVARD(\"s_al_debug\", \"0\", \"Enables periodic checks for OpenAL errors.\");\r\nstatic cvar_t s_al_hrtf = CVARD(\"s_al_hrtf\", \"\", \"Enables use of HRTF, and which HRTF table to use.\\nempty: auto, depending on openal config to enable it.\\n\\0: force off.\\n1: Use the default HRTF.\");\r\nstatic cvar_t s_al_use_reverb = CVARD(\"s_al_use_reverb\", \"1\", \"Controls whether reverb effects will be used. Set to 0 to block them. Reverb requires gamecode to configure the reverb properties, other than underwater.\");\r\n//static cvar_t s_al_max_distance = CVARFC(\"s_al_max_distance\", \"1000\",0,OnChangeALSettings);\r\nstatic cvar_t s_al_speedofsound = CVARFCD(\"s_al_speedofsound\", \"343.3\",0,OnChangeALSettings, \"Configures the speed of sound, in game units per second. This affects doppler.\");\r\nstatic cvar_t s_al_dopplerfactor = CVARFCD(\"s_al_dopplerfactor\", \"1.0\",0,OnChangeALSettings, \"Multiplies the strength of doppler effects.\");\r\nstatic cvar_t s_al_distancemodel = CVARFCD(\"s_al_distancemodel\", legacyval(\"2\",\"0\"),0,OnChangeALSettings, \"Controls how sounds fade with distance.\\n0: Inverse (most realistic)\\n1: Inverse Clamped\\n2: Linear (Quake-like)\\n3: Linear Clamped\\n4: Exponential\\n5: Exponential Clamped\\n6: None\");\r\n//static cvar_t s_al_rolloff_factor = CVAR(\"s_al_rolloff_factor\", \"1\");\r\nstatic cvar_t s_al_reference_distance = CVARD(\"s_al_reference_distance\", \"120\", \"This is the distance at which the sound is audiable with standard volume in the inverse distance models. Nearer sounds will be louder than the original sample.\");\r\nstatic cvar_t s_al_velocityscale = CVARD(\"s_al_velocityscale\", \"1\", \"Rescales velocity values, before doppler can be calculated.\");\r\nstatic cvar_t s_al_static_listener = CVAR(\"s_al_static_listener\", \"0\");\t//cheat\r\nextern cvar_t snd_doppler;\r\n\r\nenum distancemodel_e\r\n{\r\n\tDM_INVERSE\t\t\t= 0,\r\n\tDM_INVERSE_CLAMPED\t= 1,\r\n\tDM_LINEAR\t\t\t= 2,\r\n\tDM_LINEAR_CLAMPED\t= 3,\r\n\tDM_EXPONENT\t\t\t= 4,\r\n\tDM_EXPONENT_CLAMPED\t= 5,\r\n\tDM_NONE\t\t\t\t= 6\r\n};\r\n\r\ntypedef struct\r\n{\r\n\tstruct\r\n\t{\r\n\t\tALuint handle;\r\n\t\tunsigned int queuesize;\r\n\t\tALuint queue[64];\r\n\t} qmix;\r\n\tstruct\r\n\t{\r\n\t\tALuint handle;\r\n\t\tqbyte allocated;\t//there is no guarenteed-unused handle (and I don't want to have to keep spamming alIsSource).\r\n\t\tqbyte queuesize;\r\n\t\tALuint\t\t queue_b[3];\r\n\t\tusamplepos_t queue_f[3];\r\n\t} *source;\r\n\tsize_t max_sources;\r\n\r\n\tstruct\r\n\t{\r\n\t\tALuint buffer;\r\n\t\tqbyte allocated;\t//again no guarentee.\r\n\t} *sounds;\r\n\tsize_t max_sounds;\r\n\r\n\tALCdevice *OpenAL_Device;\r\n\tALCcontext *OpenAL_Context;\r\n\tqboolean can_source_spatialise;\r\n\tqboolean can_looppoints;\r\n\r\n\tint ListenEnt;\t\t\t//listener's entity number, so we don't get weird sound displacements\r\n\tALfloat ListenPos[3];\t//their origin.\r\n\tALfloat ListenVel[3];\t// Velocity of the listener.\r\n\tALfloat ListenOri[6];\t// Orientation of the listener. (first 3 elements are \"at\", second 3 are \"up\")\r\n\tunsigned int listenerdirty;\r\n\r\n#ifdef MIXER_F32\r\n\tqboolean canfloataudio;\r\n#endif\r\n\r\n\tint cureffect;\r\n\tALuint effectslot;\t\t\t//the global reverb slot\r\n\tsize_t numeffecttypes;\r\n\tstruct\r\n\t{\r\n\t\tALuint effect;\r\n\t\tunsigned int modificationcount;\t//so we know if reverb state needs to get rebuilt\r\n\t} *effecttype;\r\n} oalinfo_t;\r\nstatic void PrintALError(char *string)\r\n{\r\n\tALenum err;\r\n\tchar *text = NULL;\r\n\tif (!s_al_debug.value)\r\n\t\treturn;\r\n\terr = palGetError();\r\n\tswitch(err)\r\n\t{\r\n\tcase 0:\r\n\t\treturn;\r\n\tcase AL_INVALID_NAME:\r\n\t\ttext = \"invalid name\";\r\n\t\tbreak;\r\n\tcase AL_INVALID_ENUM:\r\n\t\ttext = \"invalid enum\";\r\n\t\tbreak;\r\n\tcase AL_INVALID_VALUE:\r\n\t\ttext = \"invalid value\";\r\n\t\tbreak;\r\n\tcase AL_INVALID_OPERATION:\r\n\t\ttext = \"invalid operation\";\r\n\t\tbreak;\r\n\tcase AL_OUT_OF_MEMORY:\r\n\t\ttext = \"out of memory\";\r\n\t\tbreak;\r\n\tdefault:\r\n\t\ttext = \"unknown\";\r\n\t\tbreak;\r\n\t}\r\n\tCon_Printf(\"OpenAL - %s: %x: %s\\n\",string,err,text);\r\n}\r\n\r\nstatic qboolean OpenAL_LoadCache(oalinfo_t *oali, unsigned int *bufptr, sfxcache_t *sc, float volume, int loopstart)\r\n{\r\n\tunsigned int fmt;\r\n\tunsigned int size;\r\n\tswitch(sc->format)\r\n\t{\r\n#ifdef FTE_TARGET_WEB\r\n\tcase QAF_BLOB:\r\n\t\tpalGenBuffers(1, bufptr);\r\n\t\temscriptenfte_al_loadaudiofile(*bufptr, sc->data, sc->length);\r\n\t\t//alIsBuffer will report false until success or failure...\r\n\t\treturn true;\t//but we do have a 'proper' reference to the buffer.\r\n#endif\r\n\tcase QAF_S8:\r\n\t\tif (sc->numchannels == 2)\r\n\t\t{\r\n\t\t\tfmt = AL_FORMAT_STEREO8;\r\n\t\t\tsize = sc->length*2;\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tfmt = AL_FORMAT_MONO8;\r\n\t\t\tsize = sc->length*1;\r\n\t\t}\r\n\t\tbreak;\r\n\tcase QAF_S16:\r\n\t\tif (sc->numchannels == 2)\r\n\t\t{\r\n\t\t\tfmt = AL_FORMAT_STEREO16;\r\n\t\t\tsize = sc->length*4;\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tfmt = AL_FORMAT_MONO16;\r\n\t\t\tsize = sc->length*2;\r\n\t\t}\r\n\t\tbreak;\r\n#ifdef MIXER_F32\r\n\tcase QAF_F32:\r\n\t\tif (!oali->canfloataudio)\r\n\t\t\treturn false;\r\n\t\tif (sc->numchannels == 2)\r\n\t\t{\r\n\t\t\tfmt = AL_FORMAT_STEREO_FLOAT32;\r\n\t\t\tsize = sc->length*8;\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tfmt = AL_FORMAT_MONO_FLOAT32;\r\n\t\t\tsize = sc->length*4;\r\n\t\t}\r\n\t\tbreak;\r\n#endif\r\n\tdefault:\r\n\t\treturn false;\r\n\t}\r\n\tPrintALError(\"pre Buffer Data\");\r\n\tpalGenBuffers(1, bufptr);\r\n\t/*openal is inconsistant and supports only 8bit unsigned or 16bit signed*/\r\n\tif (!sc->data)\r\n\t{\r\n\t\t//buffer some silence.\r\n\t\tshort *tmp = malloc(size);\r\n\t\tmemset(tmp, 0, size);\r\n\t\tpalBufferData(*bufptr, fmt, tmp, size, sc->speed);\r\n\t\tfree(tmp);\r\n\t}\r\n\telse if (volume != 1)\r\n\t{\r\n\t\tswitch(sc->format)\r\n\t\t{\r\n#ifdef FTE_TARGET_WEB\r\n\t\tcase QAF_BLOB:\r\n\t\t\tbreak;\t//unreachable\r\n#endif\r\n\t\tcase QAF_S8:\r\n\t\t\t{\r\n\t\t\t\tunsigned char *tmp = malloc(size);\r\n\t\t\t\tchar *src = sc->data;\r\n\t\t\t\tint i;\r\n\t\t\t\tfor (i = 0; i < size; i++)\r\n\t\t\t\t\ttmp[i] = src[i]*volume+128;\t//signed->unsigned\r\n\t\t\t\tpalBufferData(*bufptr, fmt, tmp, size, sc->speed);\r\n\t\t\t\tfree(tmp);\r\n\t\t\t}\r\n\t\t\tbreak;\r\n\t\tcase QAF_S16:\r\n\t\t\t{\r\n\t\t\t\tshort *tmp = malloc(size);\r\n\t\t\t\tshort *src = (short*)sc->data;\r\n\t\t\t\tint i;\r\n\t\t\t\tfor (i = 0; i < (size>>1); i++)\r\n\t\t\t\t\ttmp[i] = bound(-32767, src[i]*volume, 32767);\t//signed.\r\n\t\t\t\tpalBufferData(*bufptr, fmt, tmp, size, sc->speed);\r\n\t\t\t\tfree(tmp);\r\n\t\t\t}\r\n\t\t\tbreak;\r\n#ifdef MIXER_F32\r\n\t\tcase QAF_F32:\r\n\t\t\t{\r\n\t\t\t\tfloat *tmp = malloc(size);\r\n\t\t\t\tfloat *src = (float*)sc->data;\r\n\t\t\t\tint i;\r\n\t\t\t\tfor (i = 0; i < (size>>2); i++)\r\n\t\t\t\t\ttmp[i] = src[i]*volume;\t//signed. oversaturation isn't my problem\r\n\t\t\t\tpalBufferData(*bufptr, fmt, tmp, size, sc->speed);\r\n\t\t\t\tfree(tmp);\r\n\t\t\t}\r\n\t\t\tbreak;\r\n#endif\r\n\t\t}\r\n\t}\r\n\telse\r\n\t{\r\n\t\tswitch(sc->format)\r\n\t\t{\r\n#ifdef FTE_TARGET_WEB\r\n\t\tcase QAF_BLOB:\r\n\t\t\tbreak;\t//unreachable\r\n#endif\r\n\t\tcase QAF_S8:\r\n\t\t\t{\r\n\t\t\t\tunsigned char *tmp = malloc(size);\r\n\t\t\t\tchar *src = sc->data;\r\n\t\t\t\tint i;\r\n\t\t\t\tfor (i = 0; i < size; i++)\r\n\t\t\t\t{\r\n\t\t\t\t\ttmp[i] = src[i]+128;\r\n\t\t\t\t}\r\n\t\t\t\tpalBufferData(*bufptr, fmt, tmp, size, sc->speed);\r\n\t\t\t\tfree(tmp);\r\n\t\t\t}\r\n\t\t\tbreak;\r\n\t\t//case QAF_U8:\r\n\t\tcase QAF_S16:\r\n\t\t//case QAF_S32:\r\n#ifdef MIXER_F32\r\n\t\tcase QAF_F32:\r\n#endif\r\n\t\t\tpalBufferData(*bufptr, fmt, sc->data, size, sc->speed);\r\n\t\t\tbreak;\r\n\t\t}\r\n\t}\r\n\r\n\tif (oali->can_looppoints && loopstart>0)\r\n\t{\r\n\t\tALint points[2] = {loopstart, sc->length};\r\n\t\tpalBufferiv(*bufptr, AL_LOOP_POINTS_SOFT, points);\r\n\t}\r\n\r\n\t//FIXME: we need to handle oal-oom error codes\r\n\r\n\tPrintALError(\"Buffer Data\");\r\n\treturn true;\r\n}\r\n\r\nstatic void QDECL OpenAL_CvarInit(void)\r\n{\r\n\tCvar_Register(&s_al_disable, SOUNDVARS);\r\n\tCvar_Register(&s_al_debug, SOUNDVARS);\r\n\tCvar_Register(&s_al_hrtf, SOUNDVARS);\r\n\tCvar_Register(&s_al_use_reverb, SOUNDVARS);\r\n//\tCvar_Register(&s_al_max_distance, SOUNDVARS);\r\n\tCvar_Register(&s_al_dopplerfactor, SOUNDVARS);\r\n\tCvar_Register(&s_al_distancemodel, SOUNDVARS);\r\n\tCvar_Register(&s_al_reference_distance, SOUNDVARS);\r\n//\tCvar_Register(&s_al_rolloff_factor, SOUNDVARS);\r\n\tCvar_Register(&s_al_velocityscale, SOUNDVARS);\r\n\tCvar_Register(&s_al_static_listener, SOUNDVARS);\r\n\tCvar_Register(&s_al_speedofsound, SOUNDVARS);\r\n}\r\n\r\nstatic void OpenAL_ListenerUpdate(soundcardinfo_t *sc, int entnum, vec3_t origin, vec3_t forward, vec3_t right, vec3_t up, vec3_t velocity)\r\n{\r\n\toalinfo_t *oali = sc->handle;\r\n\tvec3_t vel;\r\n\r\n\tif (snd_doppler.modified)\r\n\t{\r\n\t\tsnd_doppler.modified = false;\r\n\t\tOnChangeALSettings(NULL,NULL);\r\n\t}\r\n\r\n\tif (!VectorCompare(origin, oali->ListenPos))\r\n\t{\r\n\t\tVectorCopy(origin, oali->ListenPos);\r\n\t\toali->listenerdirty |= 1;\r\n\t}\r\n\r\n\tVectorScale(velocity, s_al_velocityscale.value/35.0, vel);\r\n\tif (!VectorCompare(vel, oali->ListenVel))\r\n\t{\r\n\t\tVectorCopy(vel, oali->ListenVel);\r\n\t\toali->listenerdirty |= 2;\r\n\t}\r\n\r\n\toali->ListenEnt = entnum;\r\n\tif (!VectorCompare(forward, oali->ListenOri) || !VectorCompare(up, oali->ListenOri+3))\r\n\t{\r\n\t\toali->ListenOri[0] = forward[0];\r\n\t\toali->ListenOri[1] = forward[1];\r\n\t\toali->ListenOri[2] = forward[2];\r\n\t\toali->ListenOri[3] = up[0];\r\n\t\toali->ListenOri[4] = up[1];\r\n\t\toali->ListenOri[5] = up[2];\r\n\t\toali->listenerdirty |= 4;\r\n\t}\r\n\r\n\r\n\tif (!s_al_static_listener.value)\r\n\t{\r\n\t\t//I'm using listenerdirty flags because emscripten's openal stuff seems to be wasting massive amounts of time on these.\r\n//\t\tpalListenerf(AL_GAIN, 1);\r\n\t\tif (oali->listenerdirty & 1)\r\n\t\t\tpalListenerfv(AL_POSITION, oali->ListenPos);\r\n#ifndef FTE_TARGET_WEB\t//webaudio sucks.\r\n\t\tif (oali->listenerdirty & 2)\r\n\t\t\tpalListenerfv(AL_VELOCITY, oali->ListenVel);\r\n#endif\r\n\t\tif (oali->listenerdirty & 4)\r\n\t\t\tpalListenerfv(AL_ORIENTATION, oali->ListenOri);\r\n\r\n\t\toali->listenerdirty = 0;\r\n\t}\r\n}\r\n\r\nstatic qboolean OpenAL_ReclaimASource(soundcardinfo_t *sc)\r\n{\r\n\toalinfo_t *oali = sc->handle;\r\n\tALuint src;\r\n\tALuint buf;\r\n\tint i;\r\n\tint success = 0;\r\n\tfor (i = 0; i < sc->total_chans; i++)\r\n\t{\r\n//\t\tchannel_t *chan = &sc->channel[i];\r\n\t\tsrc = oali->source[i].handle;\r\n\t\tif (oali->source[i].allocated)\r\n\t\t{\r\n\t\t\tpalGetSourcei(src, AL_SOURCE_STATE, &buf);\r\n\t\t\tif (buf != AL_PLAYING)\r\n\t\t\t{\r\n\t\t\t\tpalDeleteSources(1, &src);\r\n\t\t\t\tif (oali->source[i].queuesize)\r\n\t\t\t\t\tpalDeleteBuffers(oali->source[i].queuesize, oali->source[i].queue_b);\r\n\t\t\t\toali->source[i].queuesize = 0;\r\n\t\t\t\toali->source[i].handle = 0;\r\n\t\t\t\toali->source[i].allocated = false;\r\n\t\t\t\tsuccess++;\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\tif (!success)\r\n\t{\r\n\t\tint furthest=-1;\r\n\t\tfloat dist, bdist=-1;\r\n\t\tvec3_t d;\r\n\t\tfor (i = DYNAMIC_FIRST; i < sc->total_chans; i++)\r\n\t\t{\r\n\t\t\tif (oali->source[i].allocated)\r\n\t\t\t{\r\n\t\t\t\tVectorSubtract(sc->channel[i].origin, oali->ListenPos, d);\r\n\t\t\t\tdist = DotProduct(d,d);\r\n\t\t\t\tif (dist > bdist)\r\n\t\t\t\t{\r\n\t\t\t\t\tbdist = dist;\r\n\t\t\t\t\tfurthest = i;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t\tif (furthest >= 0)\r\n\t\t{\r\n\t\t\ti = furthest;\r\n\t\t\tpalDeleteSources(1, &oali->source[i].handle);\r\n\t\t\tif (oali->source[i].queuesize)\r\n\t\t\t\tpalDeleteBuffers(oali->source[i].queuesize, oali->source[i].queue_b);\r\n\t\t\toali->source[i].queuesize = 0;\r\n\t\t\toali->source[i].handle = 0;\r\n\t\t\toali->source[i].allocated = false;\r\n\t\t\tsuccess++;\r\n\t\t}\r\n\t}\r\n\r\n\treturn success;\r\n}\r\n\r\n//for querying sound offsets (for various hacks).\r\nstatic ssamplepos_t OpenAL_GetChannelPos(soundcardinfo_t *sc, channel_t *chan)\r\n{\r\n\tALint spos = 0;\r\n\toalinfo_t *oali = sc->handle;\r\n\tint chnum = chan - sc->channel;\r\n\tALuint src;\r\n\tsrc = oali->source[chnum].handle;\r\n\tif (!oali->source[chnum].allocated)\r\n\t\treturn (ssamplepos_t)(~(usamplepos_t)0)>>1;\t//not actually playing...\r\n\r\n\t//alcMakeContextCurrent\r\n\r\n\tif (oali->source[chnum].queuesize)\r\n\t{\t//we're streaming, for whatever reason.\r\n\t\tssamplepos_t pos;\r\n\t\tALuint processed;\r\n\t\tint i;\r\n\t\t//reclaim any queued buffers\r\n\t\tpalGetSourcei(src, AL_BUFFERS_PROCESSED, &processed);\t//get number of buffers\r\n\t\tpalGetSourcei(src, AL_SAMPLE_OFFSET, &spos);\t//get our position within the current one.\r\n\t\tif (processed)\r\n\t\t{\r\n\t\t\tpalSourceUnqueueBuffers(src, processed, oali->source[chnum].queue_b);\r\n\t\t\tpalDeleteBuffers(processed, oali->source[chnum].queue_b);\r\n\t\t\toali->source[chnum].queuesize -= processed;\r\n\t\t\tmemmove(oali->source[chnum].queue_b, oali->source[chnum].queue_b+processed, oali->source[chnum].queuesize*sizeof(*oali->source[chnum].queue_b));\r\n\t\t\tmemmove(oali->source[chnum].queue_f, oali->source[chnum].queue_f+processed, oali->source[chnum].queuesize*sizeof(*oali->source[chnum].queue_f));\r\n\t\t}\r\n\r\n\t\tpos = chan->pos>>PITCHSHIFT; //this is the point of thedata that was already submitted to openal.\r\n\t\tfor (i = 0; i < oali->source[chnum].queuesize; i++)\r\n\t\t\tpos -= oali->source[chnum].queue_f[i];\r\n\t\t//pos is now 'chan->pos at start of current buffer'\r\n\t\tpos += spos; //current playback position (should always be smaller than chan->pos originally was...)\r\n\t\treturn pos;\r\n\t}\r\n\telse\r\n\t\tpalGetSourcei(src, AL_SAMPLE_OFFSET, &spos);\r\n\treturn spos;\t//FIXME: result is probably going to be wrong when streaming\r\n}\r\n\r\n//schanged says the sample has changed, otherwise its merely moved around a little, maybe changed in volume, but nothing that will restart it.\r\nstatic void OpenAL_ChannelUpdate(soundcardinfo_t *sc, channel_t *chan, chanupdatereason_t schanged)\r\n{\r\n\toalinfo_t *oali = sc->handle;\r\n\tALuint src;\r\n\tsfx_t *sfx = chan->sfx;\r\n\tfloat pitch, cvolume;\r\n\tint chnum = chan - sc->channel;\r\n\tALuint buf;\r\n\tqboolean stream;\r\n\tqboolean srcrel;\r\n\tALuint processed;\r\n\r\n\tif (chnum >= oali->max_sources)\r\n\t\tZ_ReallocElements((void**)&oali->source, &oali->max_sources, chnum+1+64, sizeof(*oali->source));\r\n\r\n\t//alcMakeContextCurrent\r\n\r\n\tif (!oali->source[chnum].allocated)\r\n\t{\r\n\t\t//not currently playing. be prepared to create one\r\n\t\tif (!sfx || chan->master_vol == 0)\r\n\t\t\treturn;\r\n\t\tpalGetError(); //gah this is so shite\r\n\t\tpalGenSources(1, &src);\r\n\t\tif (palGetError() || !palIsSource(src))\r\n\t\t{\t//can't just test for invalid, and failure leaving src unchanged could refer to a different sound.\r\n\t\t\t//try to kill some pther sound\r\n\t\t\tif (OpenAL_ReclaimASource(sc))\r\n\t\t\t{\t//okay, we killed one. hopefully we can start a new one now.\r\n\t\t\t\tpalGenSources(1, &src);\r\n\t\t\t\tif (palGetError() || !palIsSource(src))\r\n\t\t\t\t{\r\n\t\t\t\t\tPrintALError(\"alGenSources\");\r\n\t\t\t\t\treturn;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\telse return;\r\n\t\t}\r\n\t\toali->source[chnum].handle = src;\r\n\t\toali->source[chnum].allocated = true;\r\n\t\toali->source[chnum].queuesize = 0;\r\n\t\tschanged |= CUR_EVERYTHING;\t//should normally be true anyway, but hey\r\n\t}\r\n\telse\r\n\t\tsrc = oali->source[chnum].handle;\r\n\r\n\tPrintALError(\"pre start sound\");\r\n\r\n\tif (schanged&CUR_SOUNDCHANGE)\r\n\t{\r\n\t\tpalSourceStop(src);\r\n\t\tpalSourcei(src, AL_BUFFER, 0);\r\n\t\tif (oali->source[chnum].queuesize)\r\n\t\t\tpalDeleteBuffers(oali->source[chnum].queuesize, oali->source[chnum].queue_b);\r\n\t\toali->source[chnum].queuesize = 0;\r\n\r\n\t}\r\n\telse if (oali->source[chnum].queuesize)\r\n\t{\r\n\t\t//reclaim any queued buffers\r\n\t\tpalGetSourcei(src, AL_BUFFERS_PROCESSED, &processed);\r\n\t\tif (processed)\r\n\t\t{\r\n\t\t\tpalSourceUnqueueBuffers(src, processed, oali->source[chnum].queue_b);\r\n\t\t\tpalDeleteBuffers(processed, oali->source[chnum].queue_b);\r\n\t\t\toali->source[chnum].queuesize -= processed;\r\n\t\t\tmemmove(oali->source[chnum].queue_b, oali->source[chnum].queue_b+processed, oali->source[chnum].queuesize*sizeof(*oali->source[chnum].queue_b));\r\n\t\t\tmemmove(oali->source[chnum].queue_f, oali->source[chnum].queue_f+processed, oali->source[chnum].queuesize*sizeof(*oali->source[chnum].queue_f));\r\n\t\t}\r\n\t}\r\n\r\n\tif (!schanged && sfx)\t//if we don't figure out when they've finished, they'll not get replaced properly.\r\n\t{\r\n\t\tpalGetSourcei(src, AL_SOURCE_STATE, &buf);\r\n\t\tif (buf != AL_PLAYING)\r\n\t\t{\r\n\t\t\tschanged |= CUR_EVERYTHING;\r\n\t\t\tif(sfx->loopstart != -1)\r\n\t\t\t\tchan->pos = sfx->loopstart<<PITCHSHIFT;\r\n\t\t\telse if (chan->flags & CF_FORCELOOP)\r\n\t\t\t\tchan->pos = 0;\r\n\t\t\telse\r\n\t\t\t\tsfx = chan->sfx = NULL;\r\n\t\t}\r\n\t}\r\n\r\n\t/*just wanted to stop it?*/\r\n\tif (!sfx)\r\n\t{\r\n#ifdef FTE_TARGET_WEB\r\n\t\t//emscripten's webaudio wrapper spams error messages after alDeleteSources has been called, if the context isn't also killed.\r\n\t\tif (!schanged)\r\n\t\t\tpalSourceStop(src);\r\n#else\r\n\t\tpalDeleteSources(1, &src);\r\n\t\tif (oali->source[chnum].queuesize)\r\n\t\t\tpalDeleteBuffers(oali->source[chnum].queuesize, oali->source[chnum].queue_b);\r\n\t\toali->source[chnum].queuesize = 0;\r\n\t\toali->source[chnum].handle = 0;\r\n\t\toali->source[chnum].allocated = false;\r\n#endif\r\n\t\treturn;\r\n\t}\r\n\r\n\tcvolume = chan->master_vol/255.0f;\r\n\tif (!(chan->flags & CF_CL_ABSVOLUME))\r\n\t\tcvolume *= volume.value*voicevolumemod;\r\n\telse\r\n\t\tcvolume *= mastervolume.value;\r\n\r\n\t//openal doesn't support loopstart (entire sample loops or not at all), so if we're meant to skip the first half then we need to stream it.\r\n\t//FIXME: AL_SOFT_loop_points\r\n\tstream = sfx->decoder.decodedata || (sfx->loopstart > 0 && !oali->can_looppoints);\r\n\tsrcrel = (chan->flags & CF_NOSPACIALISE) || (chan->entnum && chan->entnum == oali->ListenEnt) || !chan->dist_mult;\r\n\r\n\tif ((schanged&CUR_SOUNDCHANGE) || stream)\r\n\t{\r\n\t\tint sndnum = sfx-known_sfx;\r\n\t\tint buf;\r\n\t\tif (sndnum >= oali->max_sounds)\r\n\t\t\tZ_ReallocElements((void**)&oali->sounds, &oali->max_sounds, sndnum+1+64, sizeof(*oali->sounds));\r\n\t\tbuf = oali->sounds[sndnum].buffer;\r\n\t\tif (!oali->sounds[sndnum].allocated || stream)\r\n\t\t{\r\n\t\t\tif (!S_LoadSound(sfx, false))\r\n\t\t\t\treturn;\t//can't load it\r\n\t\t\tif (sfx->loadstate != SLS_LOADED)\r\n\t\t\t{\r\n\t\t\t\tif (sfx->loadstate == SLS_LOADING)\r\n\t\t\t\t{\t//kill the source so that it gets regenerated again soonish\r\n\t\t\t\t\tpalDeleteSources(1, &src);\r\n\t\t\t\t\toali->source[chnum].handle = 0;\r\n\t\t\t\t\toali->source[chnum].allocated = false;\r\n\t\t\t\t}\r\n\t\t\t\treturn;\t//not available yet\r\n\t\t\t}\r\n\r\n\t\t\tif (stream)\r\n\t\t\t{\r\n\t\t\t\tint offset;\r\n\t\t\t\tsfxcache_t sbuf, *sc;\r\n\t\t\t\twhile (oali->source[chnum].queuesize < countof(oali->source[chnum].queue_b))\r\n\t\t\t\t{\t//decode periodically instead of all at the start.\r\n\t\t\t\t\tint tryduration = snd_speed*0.5;\r\n\t\t\t\t\tssamplepos_t pos = chan->pos>>PITCHSHIFT;\r\n\r\n\t\t\t\t\tif (sfx->decoder.decodedata)\r\n\t\t\t\t\t\tsc = sfx->decoder.decodedata(sfx, &sbuf, pos, tryduration);\r\n\t\t\t\t\telse\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tsc = sfx->decoder.buf;\r\n\t\t\t\t\t\tif (pos >= sc->length)\r\n\t\t\t\t\t\t\tsc = NULL;\r\n\t\t\t\t\t}\r\n\t\t\t\t\tif (sc)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tmemcpy(&sbuf, sc, sizeof(sbuf));\r\n\r\n\t\t\t\t\t\t//hack up the sound to offset it correctly\r\n\t\t\t\t\t\tif (pos < sbuf.soundoffset || pos > sbuf.soundoffset+sbuf.length)\r\n\t\t\t\t\t\t\tsbuf.length = 0;\t//didn't contain the requested samples... the decoder is struggling.\r\n\t\t\t\t\t\telse\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\toffset = pos - sbuf.soundoffset;\r\n\t\t\t\t\t\t\tsbuf.data += offset * QAF_BYTES(sc->format)*sc->numchannels;\r\n\t\t\t\t\t\t\tsbuf.length -= offset;\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\tsbuf.soundoffset = 0;\r\n\r\n\t\t\t\t\t\tif (sbuf.length > tryduration)\r\n\t\t\t\t\t\t\tsbuf.length = tryduration;\t//don't bother queuing more than 3*0.5 secs\r\n\r\n\t\t\t\t\t\tif (sbuf.length)\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t//build a buffer with it and queue it up.\r\n\t\t\t\t\t\t\t//buffer will be purged later on when its unqueued\r\n\t\t\t\t\t\t\tif (OpenAL_LoadCache(oali, &buf, &sbuf, max(1,cvolume), 0))\r\n\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\tpalSourceQueueBuffers(src, 1, &buf);\r\n\t\t\t\t\t\t\t\toali->source[chnum].queue_b[oali->source[chnum].queuesize] = buf;\r\n\t\t\t\t\t\t\t\toali->source[chnum].queue_f[oali->source[chnum].queuesize] = sbuf.length;\r\n\t\t\t\t\t\t\t\toali->source[chnum].queuesize++;\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\telse\r\n\t\t\t\t\t\t{\t//decoder isn't ready yet, but didn't signal an error/eof. queue a little silence, because that's better than constant micro stutters\r\n\t\t\t\t\t\t\tsfxcache_t silence;\r\n\t\t\t\t\t\t\tsilence.speed = snd_speed;\r\n\t\t\t\t\t\t\tsilence.format = QAF_S16;\r\n\t\t\t\t\t\t\tsilence.numchannels = 1;\r\n\t\t\t\t\t\t\tsilence.data = NULL;\r\n\t\t\t\t\t\t\tsilence.length = 0.1 * silence.speed;\r\n\t\t\t\t\t\t\tsilence.soundoffset = 0;\r\n\t\t\t\t\t\t\tif (OpenAL_LoadCache(oali, &buf, &silence, 1, 0))\r\n\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\tpalSourceQueueBuffers(src, 1, &buf);\r\n\t\t\t\t\t\t\t\toali->source[chnum].queue_b[oali->source[chnum].queuesize] = buf;\r\n\t\t\t\t\t\t\t\toali->source[chnum].queue_f[oali->source[chnum].queuesize] = 0;\t//don't count silence.\r\n\t\t\t\t\t\t\t\toali->source[chnum].queuesize++;\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t}\r\n\r\n\t\t\t\t\t\t//yay\r\n\t\t\t\t\t\tchan->pos += sbuf.length<<PITCHSHIFT;\r\n\r\n\t\t\t\t\t\tpalGetSourcei(src, AL_SOURCE_STATE, &buf);\r\n\t\t\t\t\t\tif (buf != AL_PLAYING)\r\n\t\t\t\t\t\t\tschanged = CUR_EVERYTHING;\r\n\t\t\t\t\t}\r\n\t\t\t\t\telse\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tif(sfx->loopstart != -1)\r\n\t\t\t\t\t\t\tchan->pos = sfx->loopstart<<PITCHSHIFT;\r\n\t\t\t\t\t\telse if (chan->flags & CF_FORCELOOP)\r\n\t\t\t\t\t\t\tchan->pos = 0;\r\n\t\t\t\t\t\telse //we don't want to play anything more.\r\n\t\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t\tif (!oali->source[chnum].queuesize)\r\n\t\t\t\t\t\t{\t//queue 0.1 secs if we're starting/resetting a new stream this is to try to cover up discontinuities caused by packetloss or whatever\r\n\t\t\t\t\t\t\tsfxcache_t silence;\r\n\t\t\t\t\t\t\tsilence.speed = snd_speed;\r\n\t\t\t\t\t\t\tsilence.format = QAF_S16;\r\n\t\t\t\t\t\t\tsilence.numchannels = 1;\r\n\t\t\t\t\t\t\tsilence.data = NULL;\r\n\t\t\t\t\t\t\tsilence.length = 0.1 * silence.speed;\r\n\t\t\t\t\t\t\tsilence.soundoffset = 0;\r\n\t\t\t\t\t\t\tif (OpenAL_LoadCache(oali, &buf, &silence, 1, 0))\r\n\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\tpalSourceQueueBuffers(src, 1, &buf);\r\n\t\t\t\t\t\t\t\toali->source[chnum].queue_b[oali->source[chnum].queuesize] = buf;\r\n\t\t\t\t\t\t\t\toali->source[chnum].queue_f[oali->source[chnum].queuesize] = 0;\t//don't count silence.\r\n\t\t\t\t\t\t\t\toali->source[chnum].queuesize++;\r\n\t\t\t\t\t\t\t\tif (oali->can_source_spatialise)\t//force spacialisation as desired, if supported (this solves browsers forcing stereo on mono files which should mean static audio is full volume...)\r\n\t\t\t\t\t\t\t\t\tpalSourcei(src, AL_SOURCE_SPATIALIZE_SOFT, !srcrel);\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t\tif (!oali->source[chnum].queuesize)\r\n\t\t\t\t{\r\n\t\t\t\t\tpalGetSourcei(src, AL_SOURCE_STATE, &buf);\r\n\t\t\t\t\tif (buf != AL_PLAYING)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tchan->sfx = NULL;\r\n\t\t\t\t\t\tif (sfx->decoder.ended)\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tif (!S_IsPlayingSomewhere(sfx))\r\n\t\t\t\t\t\t\t\tsfx->decoder.ended(sfx);\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\treturn;\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\t//unstreamed\r\n\t\t\t\tif (!sfx->decoder.buf)\r\n\t\t\t\t\treturn;\r\n\t\t\t\toali->sounds[sndnum].allocated = OpenAL_LoadCache(oali, &buf, sfx->decoder.buf, 1, sfx->loopstart);\r\n\t\t\t\tif (!oali->sounds[sndnum].allocated)\r\n\t\t\t\t\treturn;\r\n\t\t\t\toali->sounds[sndnum].buffer = buf;\r\n\t\t\t}\r\n\t\t}\r\n\t\tif (!stream)\r\n\t\t{\r\n#ifdef FTE_TARGET_WEB\r\n\t\t\t//loading an ogg is async, so we must wait until its valid.\r\n\t\t\t//our javascript will hack the buffer so that its not valid until the browser has decoded it for us.\r\n\t\t\tif (!palIsBuffer(buf))\r\n\t\t\t{\t//same as the SLS_LOADING case above\r\n\t\t\t\tpalDeleteSources(1, &src);\r\n\t\t\t\toali->source[chnum].handle = 0;\r\n\t\t\t\toali->source[chnum].allocated = false;\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n#endif\r\n\t\t\tpalSourcei(src, AL_BUFFER, buf);\r\n\t\t\tif (oali->can_source_spatialise)\t//force spacialisation as desired, if supported (this solves browsers forcing stereo on mono files which should mean static audio is full volume...)\r\n\t\t\t\tpalSourcei(src, AL_SOURCE_SPATIALIZE_SOFT, !srcrel);\r\n\t\t}\r\n\t}\r\n\tpalSourcef(src, AL_GAIN, min(cvolume, 1));\t//openal only supports a max volume of 1. anything above is an error and will be clamped.\r\n\tif (srcrel)\r\n\t{\r\n\t\tpalSourcefv(src, AL_POSITION, vec3_origin);\r\n#ifndef FTE_TARGET_WEB\t//webaudio sucks.\r\n\t\tpalSourcefv(src, AL_VELOCITY, vec3_origin);\r\n#endif\r\n\t}\r\n\telse\r\n\t{\r\n\t\tpalSourcefv(src, AL_POSITION, chan->origin);\r\n#ifndef FTE_TARGET_WEB\t//webaudio sucks.\r\n\t\tpalSourcefv(src, AL_VELOCITY, chan->velocity);\r\n#endif\r\n\t}\r\n\r\n\tif (schanged)\r\n\t{\r\n\t\tif (schanged & CUR_OFFSET && chan->pos)\r\n\t\t{\t//complex update, but not restart. pos contains an offset, rather than an absolute time.\r\n\t\t\tpalSourcei(src, AL_SAMPLE_OFFSET, (chan->pos>>PITCHSHIFT));\r\n\t\t}\r\n\r\n\t\tpitch = (float)chan->rate/(1<<PITCHSHIFT);\r\n\t\tpitch = max(0.01, pitch); // OpenAL will clamp inside the implementation if need be, only min is important\r\n\t\tpalSourcef(src, AL_PITCH, pitch);\r\n\r\n#ifdef USEEFX\r\n\t\tif (chan->flags & CF_NOREVERB)\t//don't do the underwater thing on static sounds. it sounds like arse with all those sources.\r\n\t\t\tpalSource3i(src, AL_AUXILIARY_SEND_FILTER, 0, 0, AL_FILTER_NULL);\r\n\t\telse\r\n\t\t\tpalSource3i(src, AL_AUXILIARY_SEND_FILTER, oali->effectslot, 0, AL_FILTER_NULL);\r\n#endif\r\n\r\n\t\tpalSourcei(src, AL_LOOPING, (!stream && ((chan->flags & CF_FORCELOOP)||(sfx->loopstart>=0&&!stream)))?AL_TRUE:AL_FALSE);\r\n\t\tif (srcrel)\r\n\t\t{\r\n\t\t\tpalSourcei(src, AL_SOURCE_RELATIVE, AL_TRUE);\r\n//\t\t\tpalSourcef(src, AL_ROLLOFF_FACTOR, 0.0f);\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tpalSourcei(src, AL_SOURCE_RELATIVE, AL_FALSE);\r\n//\t\t\tpalSourcef(src, AL_ROLLOFF_FACTOR, s_al_rolloff_factor.value*chan->dist_mult);\r\n\t\t}\r\n\r\n\t\t//this is disgustingly shit.\r\n\t\t//logically we want to set the distance divisor to 1 and the rolloff factor to dist_mult.\r\n\t\t//but openal clamps in an annoying order (probably to keep things signed in hardware) and webaudio refuses infinity, so we need to special case no attenuation to get around the issue\r\n\t\tif (srcrel)\r\n\t\t{\r\n#if 0//def FTE_TARGET_WEB\r\n\t\t\tswitch(DM_INVERSE)\t//emscripten omits it, and this is webaudio's default too.\r\n#else\r\n\t\t\tswitch((enum distancemodel_e)s_al_distancemodel.ival)\r\n#endif\r\n\t\t\t{\r\n\t\t\tdefault:\r\n\t\t\tcase DM_INVERSE:\r\n\t\t\tcase DM_INVERSE_CLAMPED:\r\n\t\t\t\tpalSourcef(src, AL_ROLLOFF_FACTOR, 0);\r\n\t\t\t\tpalSourcef(src, AL_REFERENCE_DISTANCE, 1);\t//0 would be silent, or a division by 0\r\n\t\t\t\tpalSourcef(src, AL_MAX_DISTANCE, 1);\t//only used for clamped mode\r\n\t\t\t\tbreak;\r\n\t\t\tcase DM_LINEAR:\r\n\t\t\tcase DM_LINEAR_CLAMPED:\r\n\t\t\t\tpalSourcef(src, AL_ROLLOFF_FACTOR, 0);\r\n\t\t\t\tpalSourcef(src, AL_REFERENCE_DISTANCE, 0);\t//doesn't matter when rolloff is 0\r\n\t\t\t\tpalSourcef(src, AL_MAX_DISTANCE, 1);\t//doesn't matter, so long as its not a nan\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n#if 0//def FTE_TARGET_WEB\r\n\t\t\tswitch(DM_LINEAR)\t//emscripten hardcodes it in a buggy kind of way.\r\n#else\r\n\t\t\tswitch((enum distancemodel_e)s_al_distancemodel.ival)\r\n#endif\r\n\t\t\t{\r\n\t\t\tdefault:\r\n\t\t\tcase DM_INVERSE:\r\n\t\t\tcase DM_INVERSE_CLAMPED:\r\n\t\t\t\tpalSourcef(src, AL_ROLLOFF_FACTOR, s_al_reference_distance.value);\r\n\t\t\t\tpalSourcef(src, AL_REFERENCE_DISTANCE, 1);\r\n\t\t\t\tpalSourcef(src, AL_MAX_DISTANCE, 1/chan->dist_mult);\t//clamp to the maximum distance you'd normally be allowed to hear... this is probably going to be annoying.\r\n\t\t\t\tbreak;\r\n\t\t\tcase DM_LINEAR:\t//linear, mimic quake.\r\n\t\t\tcase DM_LINEAR_CLAMPED: //linear clamped to further than ref distance\r\n\t\t\t\tpalSourcef(src, AL_ROLLOFF_FACTOR, 1);\r\n#if 0//def FTE_TARGET_WEB\r\n\t\t\t\t//chrome complains about 0.\r\n\t\t\t\t//with the expontential model, 0 results in division by zero, but we're not using that model and the maths for the linear model is fine with it.\r\n\t\t\t\t//the web audio spec says 'The default value is 1. A RangeError exception must be thrown if this is set to a non-negative value.'\r\n\t\t\t\t//which of course means that the PannerNode's constructor must throw an exception, which kinda prevents you ever creating one.\r\n\t\t\t\t//it also says elsewhere 'If dref = 0, the value of the [exponential|inverse] model is taken to be 0, ...'\r\n\t\t\t\t//which shows that the spec should read 'negative values' for rangeerrors (rather than non-positive). so chrome is being shit.\r\n\t\t\t\t//unfortutely due to the nature of javascript and exceptions, this is fucking everything else up. thanks chrome!\r\n\t\t\t\tpalSourcef(src, AL_REFERENCE_DISTANCE, 0.01);\r\n#else\r\n\t\t\t\tpalSourcef(src, AL_REFERENCE_DISTANCE, 0);\r\n#endif\r\n\t\t\t\tpalSourcef(src, AL_MAX_DISTANCE, 1/chan->dist_mult);\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t/*and start it up again*/\r\n\t\tif (schanged != CUR_UPDATE)\r\n\t\t\tpalSourcePlay(src);\r\n\t}\r\n\r\n\tPrintALError(sfx?sfx->name:\"post start sound\");\r\n}\r\n\r\n/*\r\nstatic void S_Info (void)\r\n{\r\n\tif (OpenAL_Device == NULL)\r\n\t\treturn;\r\n\r\n\tCon_Printf(\"OpenAL Version        : %s\\n\",palGetString(AL_VERSION));\r\n\tCon_Printf(\"OpenAL Vendor         : %s\\n\",palGetString(AL_VENDOR));\r\n\tCon_Printf(\"OpenAL Renderer       : %s\\n\",palGetString(AL_RENDERER));\r\n\tif(palcIsExtensionPresent(NULL, (const ALCchar*)\"ALC_ENUMERATION_EXT\")==AL_TRUE)\r\n\t{\r\n\t\tCon_Printf(\"OpenAL Device         : %s\\n\",palcGetString(OpenAL_Device,ALC_DEVICE_SPECIFIER));\r\n\t}\r\n\tCon_Printf(\"OpenAL Default Device : %s\\n\",palcGetString(OpenAL_Device,ALC_DEFAULT_DEVICE_SPECIFIER));\r\n\tCon_Printf(\"OpenAL AL Extension   : %s\\n\",palGetString(AL_EXTENSIONS));\r\n\tCon_Printf(\"OpenAL ALC Extension  : %s\\n\",palcGetString(NULL,ALC_EXTENSIONS));\r\n}\r\n*/\r\n\r\nstatic qboolean OpenAL_InitLibrary(void)\r\n{\r\n#ifdef OPENAL_STATIC\r\n\tif (s_al_disable.ival > 1)\r\n\t\treturn false;\r\n\treturn true;\r\n#else\r\n\tstatic dllfunction_t openalfuncs[] =\r\n\t{\r\n\t\t{(void*)&palGetError, \"alGetError\"},\r\n\t\t{(void*)&palSourcef, \"alSourcef\"},\r\n\t\t{(void*)&palSourcei, \"alSourcei\"},\r\n\t\t{(void*)&palSource3i, \"alSource3i\"},\r\n\t\t{(void*)&palSourcePlayv, \"alSourcePlayv\"},\r\n\t\t{(void*)&palSourceStopv, \"alSourceStopv\"},\r\n\t\t{(void*)&palSourcePlay, \"alSourcePlay\"},\r\n\t\t{(void*)&palSourceStop, \"alSourceStop\"},\r\n\t\t{(void*)&palDopplerFactor, \"alDopplerFactor\"},\r\n\t\t{(void*)&palGenBuffers, \"alGenBuffers\"},\r\n\t\t{(void*)&palIsBuffer, \"alIsBuffer\"},\r\n\t\t{(void*)&palBufferData, \"alBufferData\"},\r\n\t\t{(void*)&palBufferiv, \"alBufferiv\"},\r\n\t\t{(void*)&palDeleteBuffers, \"alDeleteBuffers\"},\r\n\t\t{(void*)&palListenerfv, \"alListenerfv\"},\r\n\t\t{(void*)&palSourcefv, \"alSourcefv\"},\r\n\t\t{(void*)&palGetString, \"alGetString\"},\r\n\t\t{(void*)&palGenSources, \"alGenSources\"},\r\n\t\t{(void*)&palIsSource, \"alIsSource\"},\r\n\t\t{(void*)&palListenerf, \"alListenerf\"},\r\n\t\t{(void*)&palDeleteSources, \"alDeleteSources\"},\r\n\t\t{(void*)&palSpeedOfSound, \"alSpeedOfSound\"},\r\n\t\t{(void*)&palDistanceModel, \"alDistanceModel\"},\r\n\r\n\t\t{(void*)&palIsExtensionPresent, \"alIsExtensionPresent\"},\r\n\t\t{(void*)&palGetProcAddress, \"alGetProcAddress\"},\r\n\t\t{(void*)&palGetSourcei, \"alGetSourcei\"},\r\n\t\t{(void*)&palSourceQueueBuffers, \"alSourceQueueBuffers\"},\r\n\t\t{(void*)&palSourceUnqueueBuffers, \"alSourceUnqueueBuffers\"},\r\n\r\n\t\t{(void*)&palcOpenDevice, \"alcOpenDevice\"},\r\n\t\t{(void*)&palcCloseDevice, \"alcCloseDevice\"},\r\n\t\t{(void*)&palcCreateContext, \"alcCreateContext\"},\r\n\t\t{(void*)&palcDestroyContext, \"alcDestroyContext\"},\r\n\t\t{(void*)&palcMakeContextCurrent, \"alcMakeContextCurrent\"},\r\n\t\t{(void*)&palcProcessContext, \"alcProcessContext\"},\r\n\t\t{(void*)&palcGetString, \"alcGetString\"},\r\n\t\t{(void*)&palcGetIntegerv, \"alcGetIntegerv\"},\r\n\t\t{(void*)&palcIsExtensionPresent, \"alcIsExtensionPresent\"},\r\n\t\t{(void*)&palcGetProcAddress, \"alcGetProcAddress\"},\r\n\t\t{NULL}\r\n\t};\r\n\r\n\tif (s_al_disable.ival > 1)\r\n\t\treturn false;\r\n\tif (COM_CheckParm(\"-noopenal\"))\r\n\t\treturn false;\r\n\r\n\tif (!openallib_tried)\r\n\t{\r\n\t\topenallib_tried = true;\r\n#ifdef _WIN32\r\n\t\topenallib = Sys_LoadLibrary(\"OpenAL32\", openalfuncs);\r\n\t\tif (!openallib)\r\n\t\t\topenallib = Sys_LoadLibrary(\"soft_oal\", openalfuncs);\r\n#else\r\n\t\topenallib = Sys_LoadLibrary(\"libopenal.so.1\", openalfuncs);\r\n\t\tif (!openallib)\r\n\t\t\topenallib = Sys_LoadLibrary(\"libopenal\", openalfuncs);\r\n#endif\r\n\t}\r\n\treturn !!openallib;\r\n#endif\r\n}\r\n\r\nstatic qboolean OpenAL_Init(soundcardinfo_t *sc, const char *devname, qboolean qmix)\r\n{\r\n\toalinfo_t *oali;\r\n\r\n\tif (!OpenAL_InitLibrary())\r\n\t{\r\n\t\tif (!s_al_disable.ival)\r\n\t\t{\r\n\t\t\tif (devname)\r\n\t\t\t\tCon_Printf(SDRVNAME\" library is not installed\\n\");\r\n\t\t\telse\r\n\t\t\t\tCon_DPrintf(SDRVNAME\" library is not installed\\n\");\r\n\t\t}\r\n\t\treturn false;\r\n\t}\r\n\r\n\tif (!devname || !*devname)\r\n\t{\r\n\t\tif (s_al_disable.ival && !qmix)\r\n\t\t\treturn false;\t//no default device\r\n\t\tdevname = palcGetString(NULL, ALC_DEFAULT_DEVICE_SPECIFIER);\r\n\t}\r\n\tQ_snprintfz(sc->name, sizeof(sc->name), \"%s\", devname);\r\n\tif (qmix)\r\n\t\tCon_TPrintf(\"Initiating \"QMIX_SDRVNAME\": %s.\\n\", devname);\r\n\telse\r\n\t\tCon_TPrintf(\"Initiating \"SDRVNAME\": %s.\\n\", devname);\r\n\r\n\toali = Z_Malloc(sizeof(oalinfo_t));\r\n\tsc->handle = oali;\r\n\r\n\toali->OpenAL_Device = palcOpenDevice(devname);\r\n\tif (oali->OpenAL_Device == NULL)\r\n\t\tPrintALError(\"Could not init a sound device\\n\");\r\n\telse\r\n\t{\r\n\t\tsize_t i = 0;\r\n\t\tALCint attrs[5];\r\n\r\n\t\tpalcGetStringiSOFT = (!qmix&&palcIsExtensionPresent(oali->OpenAL_Device, \"ALC_SOFT_HRTF\"))?palcGetProcAddress(oali->OpenAL_Device, \"alcGetStringiSOFT\"):NULL;\r\n\t\tif (palcGetStringiSOFT)\r\n\t\t{\r\n\t\t\tif (!*s_al_hrtf.string)\r\n\t\t\t{\r\n\t\t\t\tattrs[i++] = ALC_HRTF_SOFT;\r\n\t\t\t\tattrs[i++] = ALC_DONT_CARE_SOFT;\r\n\t\t\t}\r\n\t\t\telse if (!strcmp(s_al_hrtf.string, \"0\") || !strcmp(s_al_hrtf.string, \"1\"))\r\n\t\t\t{\t//explicitly switch it off or on(default)\r\n\t\t\t\tattrs[i++] = ALC_HRTF_SOFT;\r\n\t\t\t\tattrs[i++] = !strcmp(s_al_hrtf.string, \"1\");\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\t//we want an explicit hrtf\r\n\t\t\t\tALCint hrtf_count = 0;\r\n\t\t\t\tALCint idx;\r\n\t\t\t\tconst ALCchar *hrtfname;\r\n\t\t\t\tattrs[i++] = ALC_HRTF_SOFT;\r\n\t\t\t\tattrs[i++] = true;\r\n\r\n\t\t\t\tpalcGetIntegerv(oali->OpenAL_Device, ALC_NUM_HRTF_SPECIFIERS_SOFT, 1, &hrtf_count);\r\n\t\t\t\tfor (idx = 0; idx < hrtf_count; idx++)\r\n\t\t\t\t{\r\n\t\t\t\t\thrtfname = palcGetStringiSOFT(oali->OpenAL_Device, ALC_HRTF_SPECIFIER_SOFT, idx);\r\n\t\t\t\t\tif (hrtfname && !strcmp(hrtfname, s_al_hrtf.string))\r\n\t\t\t\t\t\tbreak;\r\n\t\t\t\t}\r\n\r\n\t\t\t\tif (idx < hrtf_count)\r\n\t\t\t\t{\r\n\t\t\t\t\tattrs[i++] = ALC_HRTF_ID_SOFT;\r\n\t\t\t\t\tattrs[i++] = idx;\r\n\t\t\t\t}\r\n\t\t\t\telse if (hrtf_count)\r\n\t\t\t\t{\r\n\t\t\t\t\tCon_Printf(\"HRTF \\\"%s\\\" not known, available options are:\\n\", s_al_hrtf.string);\r\n\t\t\t\t\tfor (idx = 0; idx < hrtf_count; idx++)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\thrtfname = palcGetStringiSOFT(oali->OpenAL_Device, ALC_HRTF_SPECIFIER_SOFT, idx);\r\n\t\t\t\t\t\tif (hrtfname)\r\n\t\t\t\t\t\t\tCon_Printf(\"\\t\\\"%s\\\"\\n\", hrtfname);\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t\tattrs[i] = 0;\t//EOL\r\n\r\n\t\toali->OpenAL_Context = palcCreateContext(oali->OpenAL_Device, attrs);\r\n\t\tif (!oali->OpenAL_Context)\r\n\t\t\tPrintALError(\"Could not init a sound context\\n\");\r\n\t\telse\r\n\t\t{\r\n\t\t\tpalcMakeContextCurrent(oali->OpenAL_Context);\r\n\t\t//\tpalcProcessContext(oali->OpenAL_Context);\r\n\r\n\t\t\t//S_Info();\r\n\r\n\t\t\t//fixme...\r\n\t\t\tmemset(oali->source, 0, sizeof(*oali->source)*oali->max_sources);\r\n\t\t\tPrintALError(\"alGensources for normal sources\");\r\n\r\n\t\t\tpalListenerf(AL_GAIN, 1);\r\n\t\t\tpalListenerfv(AL_POSITION, oali->ListenPos);\r\n#ifndef FTE_TARGET_WEB\t//webaudio sucks.\r\n\t\t\tpalListenerfv(AL_VELOCITY, oali->ListenVel);\r\n#endif\r\n\t\t\tpalListenerfv(AL_ORIENTATION, oali->ListenOri);\r\n\r\n\t\t\toali->can_source_spatialise = palIsExtensionPresent(\"AL_SOFT_source_spatialize\");\r\n\t\t\toali->can_looppoints = palIsExtensionPresent(\"AL_SOFT_loop_points\");\r\n\r\n\t\t\tif (palcGetStringiSOFT)\r\n\t\t\t{\r\n\t\t\t\tALCint stat;\r\n\t\t\t\tpalcGetIntegerv(oali->OpenAL_Device, ALC_HRTF_STATUS_SOFT, 1, &stat);\r\n\t\t\t\tsafeswitch(stat)\r\n\t\t\t\t{\r\n\t\t\t\tcase ALC_HRTF_DISABLED_SOFT:\t\t\tCon_Printf(\"AL_HRTF_STATUS: DISABLED.\\n\"); break;\r\n\t\t\t\tcase ALC_HRTF_ENABLED_SOFT:\t\t\t\tCon_Printf(\"AL_HRTF_STATUS: ENABLED.\\n\"); break;\r\n\t\t\t\tcase ALC_HRTF_DENIED_SOFT:\t\t\t\tCon_Printf(\"AL_HRTF_STATUS: DENIED.\\n\"); break;\r\n\t\t\t\tcase ALC_HRTF_REQUIRED_SOFT:\t\t\tCon_Printf(\"AL_HRTF_STATUS: REQUIRED.\\n\"); break;\r\n\t\t\t\tcase ALC_HRTF_HEADPHONES_DETECTED_SOFT:\tCon_Printf(\"AL_HRTF_STATUS: HEADPHONES_DETECTED.\\n\"); break;\r\n\t\t\t\tcase ALC_HRTF_UNSUPPORTED_FORMAT_SOFT:\tCon_Printf(\"AL_HRTF_STATUS: UNSUPPORTED_FORMAT.\\n\"); break;\r\n\t\t\t\tsafedefault:\t\t\t\t\t\t\tCon_Printf(\"AL_HRTF_STATUS: %#x.\\n\", stat); break;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\treturn true;\r\n\t\t}\r\n\t\tpalcCloseDevice(oali->OpenAL_Device);\r\n\t}\r\n\tZ_Free(oali);\r\n\treturn false;\r\n}\r\n\r\n//called when some al-specific cvar has changed that is linked to openal state.\r\nstatic void QDECL OnChangeALSettings (cvar_t *var, char *value)\r\n{\r\n\tsoundcardinfo_t *sc;\r\n\tfor (sc = sndcardinfo; sc; sc = sc->next)\r\n\t{\r\n\t\t//we only want openal devices.\r\n\t\tif (sc->Shutdown != OpenAL_Shutdown)\r\n\t\t\tcontinue;\r\n\t\t//alcMakeContextCurrent\r\n\r\n\t\tif (palSpeedOfSound)\r\n\t\t\tpalSpeedOfSound(s_al_speedofsound.value);\r\n\r\n\t\tif (palDopplerFactor)\r\n\t\t\tpalDopplerFactor(s_al_dopplerfactor.value * snd_doppler.value);\r\n\r\n\t\tif (palDistanceModel)\r\n\t\t{\r\n\t\t\tswitch ((enum distancemodel_e)s_al_distancemodel.ival)\r\n\t\t\t{\r\n\t\t\t\tcase DM_INVERSE:\r\n\t\t\t\t\t//gain = AL_REFERENCE_DISTANCE / (AL_REFERENCE_DISTANCE +  AL_ROLLOFF_FACTOR * (distance - AL_REFERENCE_DISTANCE) )\r\n\t\t\t\t\tpalDistanceModel(AL_INVERSE_DISTANCE);\r\n\t\t\t\t\tbreak;\r\n\t\t\t\tcase DM_INVERSE_CLAMPED:\t//openal's default mode\r\n\t\t\t\t\t//istance = max(distance,AL_REFERENCE_DISTANCE); \r\n\t\t\t\t\t//distance = min(distance,AL_MAX_DISTANCE); \r\n\t\t\t\t\t//gain = AL_REFERENCE_DISTANCE / (AL_REFERENCE_DISTANCE +  AL_ROLLOFF_FACTOR * (distance - AL_REFERENCE_DISTANCE) )\r\n\t\t\t\t\tpalDistanceModel(AL_INVERSE_DISTANCE_CLAMPED);\r\n\t\t\t\t\tbreak;\r\n\t\t\t\tcase DM_LINEAR:\t//most quake-like. linear\r\n\t\t\t\t\t//distance = min(distance, AL_MAX_DISTANCE) // avoid negative gain \r\n\t\t\t\t\t//gain = ( 1 - AL_ROLLOFF_FACTOR * (distance - AL_REFERENCE_DISTANCE) / (AL_MAX_DISTANCE - AL_REFERENCE_DISTANCE) )\r\n\t\t\t\t\tpalDistanceModel(AL_LINEAR_DISTANCE);\r\n\t\t\t\t\tbreak;\r\n\t\t\t\tcase DM_LINEAR_CLAMPED: //linear, with near stuff clamped to further away\r\n\t\t\t\t\t//distance = max(distance, AL_REFERENCE_DISTANCE) \r\n\t\t\t\t\t//distance = min(distance, AL_MAX_DISTANCE) \r\n\t\t\t\t\t//gain = ( 1 - AL_ROLLOFF_FACTOR * (distance - AL_REFERENCE_DISTANCE) / (AL_MAX_DISTANCE - AL_REFERENCE_DISTANCE) )\r\n\t\t\t\t\tpalDistanceModel(AL_LINEAR_DISTANCE_CLAMPED);\r\n\t\t\t\t\tbreak;\r\n\t\t\t\tcase DM_EXPONENT:\r\n\t\t\t\t\t//gain = (distance / AL_REFERENCE_DISTANCE) ^ (- AL_ROLLOFF_FACTOR)\r\n\t\t\t\t\tpalDistanceModel(AL_EXPONENT_DISTANCE);\r\n\t\t\t\t\tbreak;\r\n\t\t\t\tcase DM_EXPONENT_CLAMPED:\r\n\t\t\t\t\t//distance = max(distance, AL_REFERENCE_DISTANCE) \r\n\t\t\t\t\t//distance = min(distance, AL_MAX_DISTANCE) \r\n\t\t\t\t\t//gain = (distance / AL_REFERENCE_DISTANCE) ^ (- AL_ROLLOFF_FACTOR)\r\n\t\t\t\t\tpalDistanceModel(AL_EXPONENT_DISTANCE_CLAMPED);\r\n\t\t\t\t\tbreak;\r\n\t\t\t\tcase DM_NONE:\r\n\t\t\t\t\t//gain = 1\r\n\t\t\t\t\tpalDistanceModel(AL_NONE);\r\n\t\t\t\t\tbreak;\r\n\t\t\t\tdefault:\r\n\t\t\t\t\tCvar_ForceSet(&s_al_distancemodel, \"2\");\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n}\r\n\r\n/*stub should not be called*/\r\nstatic void *OpenAL_LockBuffer (soundcardinfo_t *sc, unsigned int *sampidx)\r\n{\r\n\t//Con_Printf(\"OpenAL: LockBuffer\\n\");\r\n\treturn NULL;\r\n}\r\n\r\n/*stub should not be called*/\r\nstatic void OpenAL_UnlockBuffer (soundcardinfo_t *sc, void *buffer)\r\n{\r\n\t//Con_Printf(\"OpenAL: UnlockBuffer\\n\");\r\n}\r\n\r\n/*stub should not be called*/\r\nstatic void OpenAL_Submit (soundcardinfo_t *sc, int start, int end)\r\n{\r\n\t//Con_Printf(\"OpenAL: Submit\\n\");\r\n}\r\n\r\n/*stub should not be called*/\r\nstatic unsigned int OpenAL_GetDMAPos (soundcardinfo_t *sc)\r\n{\r\n\t//Con_Printf(\"OpenAL: GetDMAPos\\n\");\r\n\treturn 0;\r\n}\r\n\r\n#ifdef USEEFX\r\nstatic void OpenAL_SetReverb (soundcardinfo_t *sc, size_t reverbeffect)\r\n{\r\n#ifdef USEEFX\r\n\toalinfo_t *oali = sc->handle;\r\n\r\n\tif (!oali->effectslot)\r\n\t\treturn;\r\n\r\n\tif (reverbeffect >= numreverbproperties)\r\n\t\treturn;\t//err... you're doing it wrong.\r\n\r\n\t//alcMakeContextCurrent\r\n\r\n\tif (reverbeffect >= oali->numeffecttypes)\r\n\t{\r\n\t\tvoid *n = BZ_Realloc(oali->effecttype, sizeof(*oali->effecttype)*numreverbproperties);\r\n\t\tif (!n)\r\n\t\t\treturn;\t//erk?\r\n\t\toali->effecttype = n;\r\n\t\tmemset(oali->effecttype + oali->numeffecttypes, 0, sizeof(*oali->effecttype)*(numreverbproperties-oali->numeffecttypes));\r\n\t\toali->numeffecttypes = numreverbproperties;\r\n\t}\r\n\tif (oali->effecttype[reverbeffect].modificationcount != reverbproperties[reverbeffect].modificationcount)\r\n\t{\t//the desired effect was modified\r\n\t\toali->cureffect = ~0;\r\n\t\toali->effecttype[reverbeffect].modificationcount = reverbproperties[reverbeffect].modificationcount;\r\n\r\n\t\tpalDeleteEffects(1, &oali->effecttype[reverbeffect].effect);\r\n\t\toali->effecttype[reverbeffect].effect = OpenAL_LoadEffect(&reverbproperties[reverbeffect].props);\r\n\t}\r\n\telse\r\n\t{\r\n\t\t//don't spam it\r\n\t\tif (oali->cureffect == reverbeffect)\r\n\t\t\treturn;\r\n\t}\r\n\toali->cureffect = reverbeffect;\r\n\tPrintALError(\"preunderwater\");\r\n\tpalAuxiliaryEffectSloti(oali->effectslot, AL_EFFECTSLOT_EFFECT, oali->effecttype[oali->cureffect].effect);\r\n\tPrintALError(\"postunderwater\");\r\n\t//Con_Printf(\"OpenAL: SetUnderWater %i\\n\", underwater);\r\n#endif\r\n}\r\n#endif\r\n\r\nstatic void OpenAL_Shutdown (soundcardinfo_t *sc)\r\n{\r\n\toalinfo_t *oali = sc->handle;\r\n\tint i;\r\n\r\n\t//alcMakeContextCurrent\r\n\r\n\tfor (i=0;i<oali->max_sources;i++)\r\n\t{\r\n\t\tif (oali->source[i].allocated)\r\n\t\t{\r\n\t\t\tpalDeleteSources(1, &oali->source[i].handle);\r\n\t\t\toali->source[i].handle = 0;\r\n\t\t\toali->source[i].allocated = false;\r\n\t\t}\r\n\t}\r\n\r\n\t/*make sure the buffers are cleared from the sound effects*/\r\n\tfor (i=0;i<oali->max_sounds;i++)\r\n\t{\r\n\t\tif (oali->sounds[i].allocated)\r\n\t\t{\r\n\t\t\tpalDeleteBuffers(1,&oali->sounds[i].buffer);\r\n\t\t\toali->sounds[i].allocated = false;\r\n\t\t}\r\n\t}\r\n\r\n#ifdef USEEFX\r\n\tif (palDeleteAuxiliaryEffectSlots)\r\n\t{\r\n\t\tpalDeleteAuxiliaryEffectSlots(1, &oali->effectslot);\r\n\t\tfor (i = 0; i < oali->numeffecttypes; i++)\r\n\t\t{\r\n\t\t\tif (oali->effecttype[i].effect)\r\n\t\t\t\tpalDeleteEffects(1, &oali->effecttype[i].effect);\r\n\t\t}\r\n\t}\r\n\tZ_Free(oali->effecttype);\r\n#endif\r\n\r\n\tpalcMakeContextCurrent(NULL);\r\n\tpalcDestroyContext(oali->OpenAL_Context);\r\n\tpalcCloseDevice(oali->OpenAL_Device);\r\n\tZ_Free(oali->sounds);\r\n\tZ_Free(oali->source);\r\n\tZ_Free(oali);\r\n}\r\n\r\n#ifdef USEEFX\r\nstatic ALuint OpenAL_LoadEffect(const struct reverbproperties_s *reverb)\r\n{\r\n\tALuint effect = 0;\r\n#ifdef AL_EFFECT_EAXREVERB\r\n\tpalGetError();\r\n\tpalGenEffects(1, &effect);\r\n\r\n\t//try eax reverb for more settings\r\n\tpalEffecti(effect, AL_EFFECT_TYPE, AL_EFFECT_EAXREVERB);\r\n\tif (!palGetError())\r\n\t{\r\n\t\t/* EAX Reverb is available. Set the EAX effect type then load the\r\n\t\t * reverb properties. */\r\n\t\tpalEffectf(effect, AL_EAXREVERB_DENSITY, reverb->flDensity);\r\n\t\tpalEffectf(effect, AL_EAXREVERB_DIFFUSION, reverb->flDiffusion);\r\n\t\tpalEffectf(effect, AL_EAXREVERB_GAIN, reverb->flGain);\r\n\t\tpalEffectf(effect, AL_EAXREVERB_GAINHF, reverb->flGainHF);\r\n\t\tpalEffectf(effect, AL_EAXREVERB_GAINLF, reverb->flGainLF);\r\n\t\tpalEffectf(effect, AL_EAXREVERB_DECAY_TIME, reverb->flDecayTime);\r\n\t\tpalEffectf(effect, AL_EAXREVERB_DECAY_HFRATIO, reverb->flDecayHFRatio);\r\n\t\tpalEffectf(effect, AL_EAXREVERB_DECAY_LFRATIO, reverb->flDecayLFRatio);\r\n\t\tpalEffectf(effect, AL_EAXREVERB_REFLECTIONS_GAIN, reverb->flReflectionsGain);\r\n\t\tpalEffectf(effect, AL_EAXREVERB_REFLECTIONS_DELAY, reverb->flReflectionsDelay);\r\n\t\tpalEffectfv(effect, AL_EAXREVERB_REFLECTIONS_PAN, reverb->flReflectionsPan);\r\n\t\tpalEffectf(effect, AL_EAXREVERB_LATE_REVERB_GAIN, reverb->flLateReverbGain);\r\n\t\tpalEffectf(effect, AL_EAXREVERB_LATE_REVERB_DELAY, reverb->flLateReverbDelay);\r\n\t\tpalEffectfv(effect, AL_EAXREVERB_LATE_REVERB_PAN, reverb->flLateReverbPan);\r\n\t\tpalEffectf(effect, AL_EAXREVERB_ECHO_TIME, reverb->flEchoTime);\r\n\t\tpalEffectf(effect, AL_EAXREVERB_ECHO_DEPTH, reverb->flEchoDepth);\r\n\t\tpalEffectf(effect, AL_EAXREVERB_MODULATION_TIME, reverb->flModulationTime);\r\n\t\tpalEffectf(effect, AL_EAXREVERB_MODULATION_DEPTH, reverb->flModulationDepth);\r\n\t\tpalEffectf(effect, AL_EAXREVERB_AIR_ABSORPTION_GAINHF, reverb->flAirAbsorptionGainHF);\r\n\t\tpalEffectf(effect, AL_EAXREVERB_HFREFERENCE, reverb->flHFReference);\r\n\t\tpalEffectf(effect, AL_EAXREVERB_LFREFERENCE, reverb->flLFReference);\r\n\t\tpalEffectf(effect, AL_EAXREVERB_ROOM_ROLLOFF_FACTOR, reverb->flRoomRolloffFactor);\r\n\t\tpalEffecti(effect, AL_EAXREVERB_DECAY_HFLIMIT, reverb->iDecayHFLimit);\r\n\t}\r\n\telse\r\n#endif\r\n\t{\r\n#ifdef AL_EFFECT_REVERB\r\n\t\t/* No EAX Reverb. Set the standard reverb effect type then load the\r\n\t\t * available reverb properties. */\r\n\t\tpalEffecti(effect, AL_EFFECT_TYPE, AL_EFFECT_REVERB);\r\n\r\n\t\tpalEffectf(effect, AL_REVERB_DENSITY, reverb->flDensity);\r\n\t\tpalEffectf(effect, AL_REVERB_DIFFUSION, reverb->flDiffusion);\r\n\t\tpalEffectf(effect, AL_REVERB_GAIN, reverb->flGain);\r\n\t\tpalEffectf(effect, AL_REVERB_GAINHF, reverb->flGainHF);\r\n\t\tpalEffectf(effect, AL_REVERB_DECAY_TIME, reverb->flDecayTime);\r\n\t\tpalEffectf(effect, AL_REVERB_DECAY_HFRATIO, reverb->flDecayHFRatio);\r\n\t\tpalEffectf(effect, AL_REVERB_REFLECTIONS_GAIN, reverb->flReflectionsGain);\r\n\t\tpalEffectf(effect, AL_REVERB_REFLECTIONS_DELAY, reverb->flReflectionsDelay);\r\n\t\tpalEffectf(effect, AL_REVERB_LATE_REVERB_GAIN, reverb->flLateReverbGain);\r\n\t\tpalEffectf(effect, AL_REVERB_LATE_REVERB_DELAY, reverb->flLateReverbDelay);\r\n\t\tpalEffectf(effect, AL_REVERB_AIR_ABSORPTION_GAINHF, reverb->flAirAbsorptionGainHF);\r\n\t\tpalEffectf(effect, AL_REVERB_ROOM_ROLLOFF_FACTOR, reverb->flRoomRolloffFactor);\r\n\t\tpalEffecti(effect, AL_REVERB_DECAY_HFLIMIT, reverb->iDecayHFLimit);\r\n#endif\r\n\t}\r\n\treturn effect;\r\n}\r\n#endif\r\n\r\n#ifdef HAVE_MIXER\r\n#define CHUNKSAMPLES 1024\r\nstatic void *OAQM_LockBuffer (soundcardinfo_t *sc, unsigned int *sampidx)\r\n{\r\n\toalinfo_t *oali = sc->handle;\r\n\tif (oali->qmix.queuesize==countof(oali->qmix.queue))\r\n\t\treturn NULL;\t//not available yet.\r\n\treturn sc->sn.buffer;\r\n}\r\nstatic void OAQM_UnlockBuffer (soundcardinfo_t *sc, void *buffer)\r\n{\r\n}\r\nstatic void OAQM_Submit (soundcardinfo_t *sc, int start, int end)\r\n{\r\n\toalinfo_t *oali = sc->handle;\r\n\tALint buf;\r\n\tint framesize = sc->sn.samplebytes*sc->sn.numchannels;\r\n\tif (end==start)\r\n\t\treturn;\r\n\tif (oali->qmix.queuesize == countof(oali->qmix.queue))\r\n\t\treturn;\r\n\tpalGenBuffers(1, &buf);\r\n\tswitch(sc->sn.sampleformat)\r\n\t{\r\n\tcase QSF_F32:\r\n\t\tpalBufferData(buf, (sc->sn.numchannels>1)?AL_FORMAT_STEREO_FLOAT32:AL_FORMAT_MONO_FLOAT32, sc->sn.buffer, (end-start)*framesize, sc->sn.speed);\r\n\t\tbreak;\r\n\tcase QSF_S16:\r\n\t\tpalBufferData(buf, (sc->sn.numchannels>1)?AL_FORMAT_STEREO16:AL_FORMAT_MONO16, sc->sn.buffer, (end-start)*framesize, sc->sn.speed);\r\n\t\tbreak;\r\n\tcase QSF_U8:\r\n\t\tpalBufferData(buf, (sc->sn.numchannels>1)?AL_FORMAT_STEREO8:AL_FORMAT_MONO8, sc->sn.buffer, (end-start)*framesize, sc->sn.speed);\r\n\t\tbreak;\r\n\tdefault:\r\n\t\tbreak;\r\n\t}\r\n\tpalSourceQueueBuffers(oali->qmix.handle, 1, &buf);\r\n\toali->qmix.queue[oali->qmix.queuesize++] = buf;\r\n\tsc->snd_completed += (end-start);\r\n\r\n\tpalGetSourcei(oali->qmix.handle, AL_SOURCE_STATE, &buf);\r\n\tif (buf != AL_PLAYING)\r\n\t\tpalSourcePlay(oali->qmix.handle);\r\n}\r\n\r\n/*stub should not be called*/\r\nstatic unsigned int OAQM_GetDMAPos (soundcardinfo_t *sc)\r\n{\r\n\textern cvar_t _snd_mixahead;\r\n\toalinfo_t *oali = sc->handle;\r\n\tALint src = oali->qmix.handle;\r\n\tALint processed = 0;\r\n\tunsigned int avail;\r\n\tpalGetSourcei(src, AL_BUFFERS_PROCESSED, &processed);\r\n\tif (processed)\r\n\t{\r\n\t\tpalSourceUnqueueBuffers(src, processed, oali->qmix.queue);\r\n\t\tpalDeleteBuffers(processed, oali->qmix.queue);\r\n\t\toali->qmix.queuesize -= processed;\r\n\t\tmemmove(oali->qmix.queue, oali->qmix.queue+processed, oali->qmix.queuesize*sizeof(*oali->qmix.queue));\r\n\t}\r\n\r\n\tavail = ((_snd_mixahead.value*sc->sn.speed)+CHUNKSAMPLES/2)/CHUNKSAMPLES;\t//how many buffers we want to try using.\r\n\tavail = bound(2, avail, countof(oali->qmix.queue));\r\n\tif (oali->qmix.queuesize > avail)\r\n\t\tavail = 0;\r\n\telse\r\n\t\tavail = avail-oali->qmix.queuesize;\r\n\tavail *= CHUNKSAMPLES;\r\n\r\n\tsc->sn.samplepos = (sc->snd_completed+avail);\r\n\tsc->sn.samplepos *= sc->sn.numchannels;\r\n\treturn sc->sn.samplepos;\r\n}\r\n\r\nstatic qboolean QDECL OpenAL_Enumerate_QMix(void (QDECL *callback)(const char *driver, const char *devicecode, const char *readabledevice))\r\n{\r\n\tconst char *devnames;\r\n\tif (!OpenAL_InitLibrary())\r\n\t\treturn true; //enumerate nothing if al is disabled\r\n\r\n\tdevnames = palcGetString(NULL, ALC_ALL_DEVICES_SPECIFIER);\r\n\tif (!devnames)\r\n\t\tdevnames = palcGetString(NULL, ALC_DEVICE_SPECIFIER);\r\n\twhile(*devnames)\r\n\t{\r\n\t\tcallback(QMIX_SDRVNAME, devnames, va(QMIX_SDRVNAMEDESC\"%s\", devnames));\r\n\t\tdevnames += strlen(devnames)+1;\r\n\t}\r\n\treturn true;\r\n}\r\n#endif\r\n\r\nstatic qboolean QDECL OpenAL_Enumerate(void (QDECL *callback)(const char *driver, const char *devicecode, const char *readabledevice))\r\n{\r\n\tconst char *devnames;\r\n\tif (!OpenAL_InitLibrary())\r\n\t\treturn true; //enumerate nothing if al is disabled\r\n\r\n\tdevnames = palcGetString(NULL, ALC_ALL_DEVICES_SPECIFIER);\r\n\tif (!devnames)\r\n\t\tdevnames = palcGetString(NULL, ALC_DEVICE_SPECIFIER);\r\n\twhile(*devnames)\r\n\t{\r\n\t\tcallback(SDRVNAME, devnames, va(SDRVNAMEDESC\"%s\", devnames));\r\n\t\tdevnames += strlen(devnames)+1;\r\n\t}\r\n\treturn true;\r\n}\r\n\r\n\r\nstatic qboolean QDECL OpenAL_InitCard2(soundcardinfo_t *sc, const char *devname, qboolean qmix)\r\n{\r\n\toalinfo_t *oali;\r\n\r\n\tsoundcardinfo_t *old;\r\n//\textern soundcardinfo_t *sndcardinfo;\r\n\r\n\t//\r\n\tfor (old = sndcardinfo; old; old = old->next)\r\n\t{\r\n\t\tif (old->Shutdown == OpenAL_Shutdown)\r\n\t\t{\r\n\t\t\t//in theory, we could relax this by using alcMakeContextCurrent lots, but we'd also need to do something about the per-sound audio buffer handle hack\r\n\t\t\tCon_Printf(CON_ERROR SDRVNAME \": only a single device may be active at once\\n\");\r\n\t\t\treturn false;\r\n\t\t}\r\n\t}\r\n\r\n\r\n\tif (OpenAL_Init(sc, devname, qmix) == false)\r\n\t\treturn false;\r\n\toali = sc->handle;\r\n\r\n\tCon_DPrintf( \"AL_VERSION: %s\\n\",palGetString(AL_VERSION));\r\n\tCon_DPrintf( \"AL_RENDERER: %s\\n\",palGetString(AL_RENDERER));\r\n\tCon_DPrintf( \"AL_VENDOR: %s\\n\",palGetString(AL_VENDOR));\r\n\tCon_DPrintf(\"AL_EXTENSIONS: %s\\n\",palGetString(AL_EXTENSIONS));\r\n\tCon_DPrintf(\"ALC_EXTENSIONS: %s\\n\",palcGetString(oali->OpenAL_Device,ALC_EXTENSIONS));\r\n\r\n#ifdef MIXER_F32\r\n\toali->canfloataudio = palIsExtensionPresent(\"AL_EXT_float32\");\r\n#endif\r\n\r\n\tsc->inactive_sound = true;\r\n\tsc->Shutdown = OpenAL_Shutdown;\r\n#ifdef HAVE_MIXER\r\n\tif (qmix)\r\n\t{\r\n\t\tsc->Lock\t\t= OAQM_LockBuffer;\r\n\t\tsc->Unlock\t\t= OAQM_UnlockBuffer;\r\n\t\tsc->GetDMAPos\t= OAQM_GetDMAPos;\r\n\t\tsc->Submit\t\t= OAQM_Submit;\r\n\r\n\t\tsc->sn.numchannels = bound(1, sc->sn.numchannels, 2);\r\n\t\tsc->sn.samples = CHUNKSAMPLES*sc->sn.numchannels;\r\n#ifdef MIXER_F32\r\n\t\tif (sc->sn.samplebytes == 4 && oali->canfloataudio)\r\n\t\t{\r\n\t\t\tsc->sn.sampleformat = QSF_F32;\r\n\t\t\tsc->sn.samplebytes = 4;\r\n\t\t}\r\n\t\telse\r\n#endif\r\n\t\tif (sc->sn.samplebytes > 1)\r\n\t\t{\r\n\t\t\tsc->sn.sampleformat = QSF_S16;\r\n\t\t\tsc->sn.samplebytes = 2;\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tsc->sn.sampleformat = QSF_U8;\r\n\t\t\tsc->sn.samplebytes = 1;\r\n\t\t}\r\n//\t\tsc->sn.speed = 11025;\r\n\t\tsc->sn.buffer = malloc(sc->sn.samples * sc->sn.samplebytes);\r\n\t\tsc->samplequeue = -1;\r\n\r\n\t\toali->qmix.handle = 0;\r\n\t\toali->qmix.queuesize = 0;\r\n\t\tpalGenSources(1, &oali->qmix.handle);\r\n\t\tpalSourcef(oali->qmix.handle, AL_GAIN, 1);\r\n\t\tpalSourcei(oali->qmix.handle, AL_SOURCE_RELATIVE, AL_TRUE);\r\n\t\t//palSourcePlay(oali->qmix.handle);\r\n\t}\r\n\telse\r\n#endif\r\n\t{\r\n#ifdef USEEFX\r\n\t\tsc->SetEnvironmentReverb = OpenAL_SetReverb;\r\n#endif\r\n\t\tsc->ChannelUpdate = OpenAL_ChannelUpdate;\r\n\t\tsc->ListenerUpdate = OpenAL_ListenerUpdate;\r\n\t\tsc->GetChannelPos = OpenAL_GetChannelPos;\r\n\t\t//these are stubs for our software mixer, and are not used with hardware mixing.\r\n\t\tsc->Lock = OpenAL_LockBuffer;\r\n\t\tsc->Unlock = OpenAL_UnlockBuffer;\r\n\t\tsc->Submit = OpenAL_Submit;\r\n\t\tsc->GetDMAPos = OpenAL_GetDMAPos;\r\n\r\n\t\tsc->selfpainting = true;\r\n\t\tsc->sn.sampleformat = QSF_EXTERNALMIXER;\r\n\r\n\t\tOnChangeALSettings(NULL, NULL);\r\n\r\n#ifdef USEEFX\r\n\t\tPrintALError(\"preeffects\");\r\n\t#ifndef AL_ALEXT_PROTOTYPES\r\n\t\tpalAuxiliaryEffectSloti = palGetProcAddress(\"alAuxiliaryEffectSloti\");\r\n\t\tpalGenAuxiliaryEffectSlots = palGetProcAddress(\"alGenAuxiliaryEffectSlots\");\r\n\t\tpalDeleteAuxiliaryEffectSlots = palGetProcAddress(\"alDeleteAuxiliaryEffectSlots\");\r\n\t\tpalDeleteEffects = palGetProcAddress(\"alDeleteEffects\");\r\n\t\tpalGenEffects = palGetProcAddress(\"alGenEffects\");\r\n\t\tpalEffecti = palGetProcAddress(\"alEffecti\");\r\n//\t\tpalEffectiv = palGetProcAddress(\"alEffectiv\");\r\n\t\tpalEffectf = palGetProcAddress(\"alEffectf\");\r\n\t\tpalEffectfv = palGetProcAddress(\"alEffectfv\");\r\n\t#endif\r\n\r\n\t\tif (palGenAuxiliaryEffectSlots && s_al_use_reverb.ival)\r\n\t\t\tpalGenAuxiliaryEffectSlots(1, &oali->effectslot);\r\n\r\n\t\toali->cureffect = ~0;\r\n\t\tPrintALError(\"posteffects\");\r\n#endif\r\n\t}\r\n\treturn true;\r\n}\r\n\r\nstatic qboolean QDECL OpenAL_InitCard(soundcardinfo_t *sc, const char *devname)\r\n{\r\n\treturn OpenAL_InitCard2(sc, devname, false);\r\n}\r\n\r\nsounddriver_t OPENAL_Output =\r\n{\r\n\tSDRVNAME,\r\n\tOpenAL_InitCard,\r\n\tOpenAL_Enumerate,\r\n\tOpenAL_CvarInit\r\n};\r\n#ifdef HAVE_MIXER\r\nstatic qboolean QDECL OpenAL_InitCard_QMix(soundcardinfo_t *sc, const char *devname)\r\n{\r\n\treturn OpenAL_InitCard2(sc, devname, true);\r\n}\r\nsounddriver_t OPENAL_Output_Lame =\r\n{\r\n\tQMIX_SDRVNAME,\r\n\tOpenAL_InitCard_QMix,\r\n\tOpenAL_Enumerate_QMix,\r\n\tNULL\r\n};\r\n#endif\r\n\r\n\r\n#if defined(VOICECHAT)\r\n\r\nstatic qboolean OpenAL_InitCapture(void)\r\n{\r\n\tif (!OpenAL_InitLibrary())\r\n\t\treturn false;\r\n\r\n\t//is there really much point checking for the name when the functions should exist or not regardless?\r\n\t//if its not really supported, I would trust the open+enumerate operations to reliably fail. the functions are exported as actual symbols after all, not some hidden driver feature.\r\n\t//it doesn't really matter if the default driver supports it, so long as one does, I guess.\r\n\t//if (!palcIsExtensionPresent(NULL, \"ALC_EXT_capture\"))\r\n\t//\treturn false;\r\n\r\n#ifdef OPENAL_STATIC\r\n\treturn true;\r\n#else\r\n\tif(!palcCaptureOpenDevice)\r\n\t{\r\n\t\tpalcCaptureOpenDevice = Sys_GetAddressForName(openallib, \"alcCaptureOpenDevice\");\r\n\t\tpalcCaptureStart = Sys_GetAddressForName(openallib, \"alcCaptureStart\");\r\n\t\tpalcCaptureSamples = Sys_GetAddressForName(openallib, \"alcCaptureSamples\");\r\n\t\tpalcCaptureStop = Sys_GetAddressForName(openallib, \"alcCaptureStop\");\r\n\t\tpalcCaptureCloseDevice = Sys_GetAddressForName(openallib, \"alcCaptureCloseDevice\");\r\n\t}\r\n\r\n\treturn palcGetIntegerv&&palcCaptureOpenDevice&&palcCaptureStart&&palcCaptureSamples&&palcCaptureStop&&palcCaptureCloseDevice;\r\n#endif\r\n}\r\nstatic qboolean QDECL OPENAL_Capture_Enumerate (void (QDECL *callback) (const char *drivername, const char *devicecode, const char *readablename))\r\n{\r\n\tconst char *devnames;\r\n\tif (!OpenAL_InitCapture())\r\n\t\treturn true; //enumerate nothing if al is disabled\r\n\r\n\tdevnames = palcGetString(NULL, ALC_CAPTURE_DEVICE_SPECIFIER);\r\n\twhile(*devnames)\r\n\t{\r\n\t\tcallback(SDRVNAME, devnames, va(SDRVNAMEDESC\"%s\", devnames));\r\n\t\tdevnames += strlen(devnames)+1;\r\n\t}\r\n\treturn true;\r\n}\r\n//fte's capture api specifies mono 16.\r\nstatic void *QDECL OPENAL_Capture_Init (int samplerate, const char *device)\r\n{\r\n#ifndef OPENAL_STATIC\r\n\tif (!device)\t//no default devices please, too buggy for that.\r\n\t\treturn NULL;\r\n#endif\r\n\r\n\tif (!OpenAL_InitCapture())\r\n\t\treturn NULL; //enumerate nothing if al is disabled\r\n\r\n\tif (!device || !*device)\r\n\t{\r\n#if defined(FTE_TARGET_WEB) && (__EMSCRIPTEN_major__>2 || (__EMSCRIPTEN_major__==2&&__EMSCRIPTEN_tiny__>=14))\r\n\t\t//emscripten, and recent enough to actually work. don't check s_al_disable here as we don't have dsound/alsa fallbacks and we do want to actually use it.\r\n\t\t//older versions of emscripten are too buggy to use.\r\n#else\r\n\t\tif (s_al_disable.ival)\r\n\t\t\treturn NULL;\t//no default device\r\n#endif\r\n\t\tdevice = palcGetString(NULL, ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER);\r\n\t}\r\n\r\n\treturn palcCaptureOpenDevice(device, samplerate, AL_FORMAT_MONO16, 0.5*samplerate);\r\n}\r\nstatic void QDECL OPENAL_Capture_Start (void *ctx)\r\n{\r\n\tALCdevice *device = ctx;\r\n\tpalcCaptureStart(device);\r\n}\r\nstatic unsigned int QDECL OPENAL_Capture_Update (void *ctx, unsigned char *buffer, unsigned int minbytes, unsigned int maxbytes)\r\n{\r\n#define samplesize sizeof(short)\r\n\tALCdevice *device = ctx;\r\n\tint avail = 0;\r\n\tpalcGetIntegerv(device, ALC_CAPTURE_SAMPLES, sizeof(ALint), &avail);\r\n\tif (avail*samplesize < minbytes)\r\n\t\treturn 0;\t//don't bother grabbing it if its below the threshold.\r\n\tpalcCaptureSamples(device, (ALCvoid *)buffer, avail);\r\n\treturn avail * samplesize;\r\n}\r\nstatic void QDECL OPENAL_Capture_Stop (void *ctx)\r\n{\r\n\tALCdevice *device = ctx;\r\n\tpalcCaptureStop(device);\r\n}\r\nstatic void QDECL OPENAL_Capture_Shutdown (void *ctx)\r\n{\r\n\tALCdevice *device = ctx;\r\n\tpalcCaptureCloseDevice(device);\r\n}\r\n\r\nsnd_capture_driver_t OPENAL_Capture =\r\n{\r\n\t1,\r\n\tSDRVNAME,\r\n\tOPENAL_Capture_Enumerate,\r\n\tOPENAL_Capture_Init,\r\n\tOPENAL_Capture_Start,\r\n\tOPENAL_Capture_Update,\r\n\tOPENAL_Capture_Stop,\r\n\tOPENAL_Capture_Shutdown\r\n};\r\n#endif\r\n\r\n#endif\r\n"
  },
  {
    "path": "engine/client/snd_alsa.c",
    "content": "/*\n\tsnd_alsa.c\n\n\tSupport for the ALSA 1.0.1 sound driver\n\n\tCopyright (C) 1999,2000  contributors of the QuakeForge project\n\tPlease see the file \"AUTHORS\" for a list of contributors\n\n\tThis program is free software; you can redistribute it and/or\n\tmodify it under the terms of the GNU General Public License\n\tas published by the Free Software Foundation; either version 2\n\tof the License, or (at your option) any later version.\n\n\tThis program is distributed in the hope that it will be useful,\n\tbut WITHOUT ANY WARRANTY; without even the implied warranty of\n\tMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\n\tSee the GNU General Public License for more details.\n\n\tYou should have received a copy of the GNU General Public License\n\talong with this program; if not, write to:\n\n\t\tFree Software Foundation, Inc.\n\t\t59 Temple Place - Suite 330\n\t\tBoston, MA  02111-1307, USA\n\n*/\n//actually stolen from darkplaces.\n//I guess noone can be arsed to write it themselves. :/\n//\n//This file is otherwise known as 'will the linux jokers please stop fucking over the open sound system please'\n\n\n#include \"quakedef.h\"\n#ifdef AUDIO_ALSA\n#include <alsa/asoundlib.h>\n#include <dlfcn.h>\n\nstatic void *alsasharedobject;\n\nstatic int (*psnd_pcm_open)\t\t\t\t(snd_pcm_t **pcm, const char *name, snd_pcm_stream_t stream, int mode);\nstatic int (*psnd_pcm_close)\t\t\t\t(snd_pcm_t *pcm);\nstatic int (*psnd_config_update_free_global)(void);\nstatic const char *(*psnd_strerror)\t\t\t(int errnum);\nstatic int (*psnd_pcm_hw_params_any)\t\t\t(snd_pcm_t *pcm, snd_pcm_hw_params_t *params);\nstatic int (*psnd_pcm_hw_params_set_access)\t\t(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_access_t _access);\nstatic int (*psnd_pcm_hw_params_set_format)\t\t(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_format_t val);\nstatic int (*psnd_pcm_hw_params_set_channels)\t\t(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int val);\nstatic int (*psnd_pcm_hw_params_set_rate_near)\t\t(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val, int *dir);\nstatic int (*psnd_pcm_hw_params_set_period_size_near)\t(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_uframes_t *val, int *dir);\nstatic int (*psnd_pcm_hw_params)\t\t\t(snd_pcm_t *pcm, snd_pcm_hw_params_t *params);\nstatic int (*psnd_pcm_sw_params_current)\t\t(snd_pcm_t *pcm, snd_pcm_sw_params_t *params);\nstatic int (*psnd_pcm_sw_params_set_start_threshold)\t(snd_pcm_t *pcm, snd_pcm_sw_params_t *params, snd_pcm_uframes_t val);\nstatic int (*psnd_pcm_sw_params_set_stop_threshold)\t(snd_pcm_t *pcm, snd_pcm_sw_params_t *params, snd_pcm_uframes_t val);\nstatic int (*psnd_pcm_sw_params)\t\t\t(snd_pcm_t *pcm, snd_pcm_sw_params_t *params);\nstatic int (*psnd_pcm_hw_params_get_buffer_size)\t(const snd_pcm_hw_params_t *params, snd_pcm_uframes_t *val);\nstatic int (*psnd_pcm_hw_params_set_buffer_size_near)\t(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_uframes_t *val);\nstatic int (*psnd_pcm_set_params)\t\t\t(snd_pcm_t *pcm, snd_pcm_format_t format, snd_pcm_access_t access, unsigned int channels, unsigned int rate, int soft_resample, unsigned int latency);\nstatic snd_pcm_sframes_t (*psnd_pcm_avail_update)\t(snd_pcm_t *pcm);\nstatic snd_pcm_state_t (*psnd_pcm_state)\t\t(snd_pcm_t *pcm);\nstatic int (*psnd_pcm_start)\t\t\t\t(snd_pcm_t *pcm);\nstatic int (*psnd_pcm_recover)\t\t\t\t(snd_pcm_t *pcm, int err, int silent);\n\nstatic size_t (*psnd_pcm_hw_params_sizeof)\t\t(void);\nstatic size_t (*psnd_pcm_sw_params_sizeof)\t\t(void);\n\nstatic int (*psnd_pcm_mmap_begin)\t\t\t(snd_pcm_t *pcm, const snd_pcm_channel_area_t **areas, snd_pcm_uframes_t *offset, snd_pcm_uframes_t *frames);\nstatic snd_pcm_sframes_t (*psnd_pcm_mmap_commit)\t(snd_pcm_t *pcm, snd_pcm_uframes_t offset, snd_pcm_uframes_t frames);\n\nstatic snd_pcm_sframes_t (*psnd_pcm_writei)\t\t(snd_pcm_t *pcm, const void *buffer, snd_pcm_uframes_t size);\nstatic int (*psnd_pcm_prepare)\t\t\t\t(snd_pcm_t *pcm);\n\nstatic int \t(*psnd_device_name_hint)\t\t(int card, const char *iface, void ***hints);\nstatic char * \t(*psnd_device_name_get_hint)\t(const void *hint, const char *id);\nstatic int\t\t(*psnd_device_name_free_hint)\t(void **hints);\n\n\nstatic unsigned int ALSA_MMap_GetDMAPos (soundcardinfo_t *sc)\n{\n\tconst snd_pcm_channel_area_t *areas;\n\tsnd_pcm_uframes_t offset;\n\tsnd_pcm_uframes_t nframes = sc->sn.samples / sc->sn.numchannels;\n\n\tpsnd_pcm_avail_update (sc->handle);\n\tpsnd_pcm_mmap_begin (sc->handle, &areas, &offset, &nframes);\n\toffset *= sc->sn.numchannels;\n\tnframes *= sc->sn.numchannels;\n\tsc->sn.samplepos = offset;\n\tsc->sn.buffer = areas->addr;\n\treturn sc->sn.samplepos;\n}\n\nstatic void ALSA_MMap_Submit (soundcardinfo_t *sc, int start, int end)\n{\n\tint\t\t\tstate;\n\tint\t\t\tcount = end - start;\n\tconst snd_pcm_channel_area_t *areas;\n\tsnd_pcm_uframes_t nframes;\n\tsnd_pcm_uframes_t offset;\n\n\tnframes = count / sc->sn.numchannels;\n\n\tpsnd_pcm_avail_update (sc->handle);\n\tpsnd_pcm_mmap_begin (sc->handle, &areas, &offset, &nframes);\n\n\tstate = psnd_pcm_state (sc->handle);\n\n\tswitch (state) {\n\t\tcase SND_PCM_STATE_PREPARED:\n\t\t\tpsnd_pcm_mmap_commit (sc->handle, offset, nframes);\n\t\t\tpsnd_pcm_start (sc->handle);\n\t\t\tbreak;\n\t\tcase SND_PCM_STATE_RUNNING:\n\t\t\tpsnd_pcm_mmap_commit (sc->handle, offset, nframes);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tbreak;\n\t}\n}\nstatic unsigned int ALSA_RW_GetDMAPos (soundcardinfo_t *sc)\n{\n\tint frames;\n\tframes = psnd_pcm_avail_update(sc->handle);\n\tif (frames < 0)\n\t{\n\t\tpsnd_pcm_start (sc->handle);\n\t\tpsnd_pcm_recover(sc->handle, frames, true);\n\t\tframes = psnd_pcm_avail_update(sc->handle);\n\t}\n\tif (frames >= 0)\n\t{\n\t\tsc->sn.samplepos = (sc->snd_sent + frames) * sc->sn.numchannels;\n\t}\n\treturn sc->sn.samplepos;\n}\nstatic void ALSA_RW_Submit (soundcardinfo_t *sc, int start, int end)\n{\n//\tint\t\t\tstate;\n\tunsigned int frames, offset, ringsize;\n\tunsigned chunk;\n\tint result;\n\tint stride = sc->sn.numchannels * sc->sn.samplebytes;\n\n\twhile(1)\n\t{\n\t\t/*we can't change the data that was already written*/\n\t\tframes = end - sc->snd_sent;\n\t\tif (frames <= 0)\n\t\t\treturn;\n\n//\t\tstate = psnd_pcm_state (sc->handle);\n\n\t\tringsize = sc->sn.samples / sc->sn.numchannels;\n\n\t\tchunk = frames;\n\t\toffset = sc->snd_sent % ringsize;\n\n\t\tif (offset + chunk >= ringsize)\n\t\t\tchunk = ringsize - offset;\n\t\tresult = psnd_pcm_writei(sc->handle, sc->sn.buffer + offset*stride, chunk);\n\t\tif (result < chunk)\n\t\t{\n\t\t\tif (result < 0)\n\t\t\t\treturn;\n\t\t}\n\t\tsc->snd_sent += chunk;\n\n\t\tchunk = frames - chunk;\n\t\tif (chunk)\n\t\t{\n\t\t\tresult = psnd_pcm_writei(sc->handle, sc->sn.buffer, chunk);\n\t\t\tif (result > 0)\n\t\t\t\tsc->snd_sent += result;\n\t\t}\n\n//\t\tif (state == SND_PCM_STATE_PREPARED)\n//\t\t\tpsnd_pcm_start (sc->handle);\n\t};\n}\n\nstatic void ALSA_Shutdown (soundcardinfo_t *sc)\n{\n\tpsnd_pcm_close (sc->handle);\n\tpsnd_config_update_free_global();\t//and try to reduce leaks\n\n\tif (sc->Submit == ALSA_RW_Submit)\n\t\tfree(sc->sn.buffer);\n\n\tCon_DPrintf(\"Alsa closed\\n\");\n}\n\nstatic void *ALSA_LockBuffer(soundcardinfo_t *sc, unsigned int *sampidx)\n{\n\treturn sc->sn.buffer;\n}\n\nstatic void ALSA_UnlockBuffer(soundcardinfo_t *sc, void *buffer)\n{\n}\n\nstatic qboolean Alsa_InitAlsa(void)\n{\n\tstatic qboolean tried;\n\tstatic qboolean alsaworks;\n\n\tstatic dllfunction_t funcs[] =\n\t{\n\t\t{(void**)&psnd_pcm_open,\t\t\t\t\t\t\t\"snd_pcm_open\"},\n\t\t{(void**)&psnd_pcm_close,\t\t\t\t\t\t\t\"snd_pcm_close\"},\n\t\t{(void**)&psnd_config_update_free_global,\t\t\t\"snd_config_update_free_global\"},\n\t\t{(void**)&psnd_strerror,\t\t\t\t\t\t\t\"snd_strerror\"},\n\t\t{(void**)&psnd_pcm_hw_params_any,\t\t\t\t\t\"snd_pcm_hw_params_any\"},\n\t\t{(void**)&psnd_pcm_hw_params_set_access,\t\t\t\"snd_pcm_hw_params_set_access\"},\n\t\t{(void**)&psnd_pcm_hw_params_set_format,\t\t\t\"snd_pcm_hw_params_set_format\"},\n\t\t{(void**)&psnd_pcm_hw_params_set_channels,\t\t\t\"snd_pcm_hw_params_set_channels\"},\n\t\t{(void**)&psnd_pcm_hw_params_set_rate_near,\t\t\t\"snd_pcm_hw_params_set_rate_near\"},\n\t\t{(void**)&psnd_pcm_hw_params_set_period_size_near,\t\"snd_pcm_hw_params_set_period_size_near\"},\n\t\t{(void**)&psnd_pcm_hw_params,\t\t\t\t\t\t\"snd_pcm_hw_params\"},\n\t\t{(void**)&psnd_pcm_sw_params_current,\t\t\t\t\"snd_pcm_sw_params_current\"},\n\t\t{(void**)&psnd_pcm_sw_params_set_start_threshold,\t\"snd_pcm_sw_params_set_start_threshold\"},\n\t\t{(void**)&psnd_pcm_sw_params_set_stop_threshold,\t\"snd_pcm_sw_params_set_stop_threshold\"},\n\t\t{(void**)&psnd_pcm_sw_params,\t\t\t\t\t\t\"snd_pcm_sw_params\"},\n\t\t{(void**)&psnd_pcm_hw_params_get_buffer_size,\t\t\"snd_pcm_hw_params_get_buffer_size\"},\n\t\t{(void**)&psnd_pcm_avail_update,\t\t\t\t\t\"snd_pcm_avail_update\"},\n\t\t{(void**)&psnd_pcm_state,\t\t\t\t\t\t\t\"snd_pcm_state\"},\n\t\t{(void**)&psnd_pcm_start,\t\t\t\t\t\t\t\"snd_pcm_start\"},\n\t\t{(void**)&psnd_pcm_recover,\t\t\t\t\t\t\t\"snd_pcm_recover\"},\n\t\t{(void**)&psnd_pcm_set_params,\t\t\t\t\t\t\"snd_pcm_set_params\"},\n\t\t{(void**)&psnd_pcm_hw_params_sizeof,\t\t\t\t\"snd_pcm_hw_params_sizeof\"},\n\t\t{(void**)&psnd_pcm_sw_params_sizeof,\t\t\t\t\"snd_pcm_sw_params_sizeof\"},\n\t\t{(void**)&psnd_pcm_hw_params_set_buffer_size_near,\t\"snd_pcm_hw_params_set_buffer_size_near\"},\n\n\t\t{(void**)&psnd_pcm_mmap_begin,\t\t\t\t\t\t\"snd_pcm_mmap_begin\"},\n\t\t{(void**)&psnd_pcm_mmap_commit,\t\t\t\t\t\t\"snd_pcm_mmap_commit\"},\n\n\t\t{(void**)&psnd_pcm_writei,\t\t\t\t\t\t\t\"snd_pcm_writei\"},\n\t\t{(void**)&psnd_pcm_prepare,\t\t\t\t\t\t\t\"snd_pcm_prepare\"},\n\n\t\t{(void**)&psnd_device_name_hint,\t\t\t\t\t\"snd_device_name_hint\"},\n\t\t{(void**)&psnd_device_name_get_hint,\t\t\t\t\"snd_device_name_get_hint\"},\n\t\t{(void**)&psnd_device_name_free_hint,\t\t\t\t\"snd_device_name_free_hint\"},\n\t\t{NULL,NULL}\n\t};\n\n\tif (tried)\n\t\treturn alsaworks;\n\ttried = true;\n\n\t//pulseaudio's wrapper library fucks with alsa in bad ways, making it unusable on some systems.\n\tif (COM_CheckParm(\"-noalsa\"))\n\t\treturn false;\n\n\t// Try alternative names of libasound, sometimes it is not linked correctly.\n\talsasharedobject = Sys_LoadLibrary(\"libasound.so.2\", funcs);\n\tif (!alsasharedobject)\n\t\talsasharedobject = Sys_LoadLibrary(\"libasound.so\", funcs);\n\tif (!alsasharedobject)\n\t\treturn false;\n\n\talsaworks = true;\n\treturn alsaworks;\n}\n\nstatic qboolean QDECL ALSA_InitCard (soundcardinfo_t *sc, const char *pcmname)\n{\n\tsnd_pcm_t   *pcm;\n\tsnd_pcm_uframes_t buffer_size;\n\n\tint\t\t\t\t\t err;\n\tsnd_pcm_hw_params_t\t*hw;\n\tsnd_pcm_sw_params_t\t*sw;\n#if 0\n\tint\t\t\t\t\t bps, stereo;\n\tunsigned int\t\t rate;\n\tsnd_pcm_uframes_t\t frag_size;\n#endif\n\tqboolean mmap = false;\n\n\tif (!Alsa_InitAlsa())\n\t{\n\t\tCon_Printf(CON_ERROR \"Alsa does not appear to be installed or compatible\\n\");\n\t\treturn false;\n\t}\n\n\thw = alloca(psnd_pcm_hw_params_sizeof());\n\tsw = alloca(psnd_pcm_sw_params_sizeof());\n\tmemset(sw, 0, psnd_pcm_sw_params_sizeof());\n\tmemset(hw, 0, psnd_pcm_hw_params_sizeof());\n\n//WARNING: 'default' as the default sucks arse. it adds about a second's worth of lag.\n\tif (!pcmname)\n\t\tpcmname = \"default\";\n\n\tsc->inactive_sound = true;\t//linux sound devices always play sound, even when we're not the active app...\n\n\tCon_Printf(\"Initing ALSA sound device \\\"%s\\\"\\n\", pcmname);\n\n\terr = psnd_pcm_open (&pcm, pcmname, SND_PCM_STREAM_PLAYBACK,\n\t\t\t\t\t\t  SND_PCM_NONBLOCK);\n\tif (0 > err)\n\t{\n\t\tCon_Printf (CON_ERROR \"ALSA Error: open error (%s): %s\\n\", pcmname, psnd_strerror (err));\n\t\treturn 0;\n\t}\n\tCon_Printf (\"ALSA: Using PCM %s.\\n\", pcmname);\n\n#if 1\n\tif (!sc->sn.sampleformat)\n\t{\n\t\tif (sc->sn.samplebytes >= 4)\n\t\t\tsc->sn.sampleformat = QSF_F32;\n\t\telse if (sc->sn.samplebytes != 1)\n\t\t\tsc->sn.sampleformat = QSF_S16;\n\t\telse\n\t\t\tsc->sn.sampleformat = QSF_U8;\n\t}\n\tswitch(sc->sn.sampleformat)\n\t{\n\tcase QSF_U8:\terr = SND_PCM_FORMAT_U8;\tsc->sn.samplebytes=1; break;\n\tcase QSF_S8:\terr = SND_PCM_FORMAT_S8;\tsc->sn.samplebytes=1; break;\n\tcase QSF_S16:\terr = SND_PCM_FORMAT_S16;\tsc->sn.samplebytes=2; break;\n\tcase QSF_F32:\terr = SND_PCM_FORMAT_FLOAT;\tsc->sn.samplebytes=4; break;\n\tdefault:\n\t\tCon_Printf (CON_ERROR \"ALSA: unsupported sample format %i\\n\", sc->sn.sampleformat);\n\t\tgoto error;\n\t}\n\n\terr = psnd_pcm_set_params(pcm, err, (mmap?SND_PCM_ACCESS_MMAP_INTERLEAVED:SND_PCM_ACCESS_RW_INTERLEAVED), sc->sn.numchannels, sc->sn.speed, true, 0.04*1000000);\n\tif (0 > err)\n\t{\n\t\tCon_Printf (CON_ERROR \"ALSA: error setting params. %s\\n\", psnd_strerror (err));\n\t\tgoto error;\n\t}\n\n//\tsc->sn.numchannels = stereo;\n//\tsc->sn.samplepos = 0;\n//\tsc->sn.samplebytes = bps/8;\n\n\tsc->samplequeue = buffer_size = 2048;\n#else\t\n\terr = psnd_pcm_hw_params_any (pcm, hw);\n\tif (0 > err)\n\t{\n\t\tCon_Printf (CON_ERROR \"ALSA: error setting hw_params_any. %s\\n\", psnd_strerror (err));\n\t\tgoto error;\n\t}\n\n\terr = psnd_pcm_hw_params_set_access (pcm, hw,  mmap?SND_PCM_ACCESS_MMAP_INTERLEAVED:SND_PCM_ACCESS_RW_INTERLEAVED);\n\tif (0 > err)\n\t{\n\t\tCon_Printf (CON_ERROR \"ALSA: Failure to set interleaved PCM access. %s\\n\", psnd_strerror (err));\n\t\tgoto error;\n\t}\n\n\t// get sample bit size\n\tbps = sc->sn.samplebytes*8;\n\t{\n\t\tsnd_pcm_format_t spft;\n\t\tif (bps == 16)\n\t\t\tspft = SND_PCM_FORMAT_S16;\n\t\telse\n\t\t\tspft = SND_PCM_FORMAT_U8;\n\n\t\terr = psnd_pcm_hw_params_set_format (pcm, hw, spft);\n\t\twhile (err < 0)\n\t\t{\n\t\t\tif (spft == SND_PCM_FORMAT_S16)\n\t\t\t{\n\t\t\t\tbps = 8;\n\t\t\t\tspft = SND_PCM_FORMAT_U8;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tCon_Printf (CON_ERROR \"ALSA: no usable formats. %s\\n\", psnd_strerror (err));\n\t\t\t\tgoto error;\n\t\t\t}\n\t\t\terr = psnd_pcm_hw_params_set_format (pcm, hw, spft);\n\t\t}\n\t}\n\n\t// get speaker channels\n\tstereo = sc->sn.numchannels;\n\terr = psnd_pcm_hw_params_set_channels (pcm, hw, stereo);\n\twhile (err < 0)\n\t{\n\t\tif (stereo > 2)\n\t\t\tstereo = 2;\n\t\telse if (stereo > 1)\n\t\t\tstereo = 1;\n\t\telse\n\t\t{\n\t\t\tCon_Printf (CON_ERROR \"ALSA: no usable number of channels. %s\\n\", psnd_strerror (err));\n\t\t\tgoto error;\n\t\t}\n\t\terr = psnd_pcm_hw_params_set_channels (pcm, hw, stereo);\n\t}\n\n\t// get rate\n\trate = sc->sn.speed;\n\terr = psnd_pcm_hw_params_set_rate_near (pcm, hw, &rate, 0);\n\twhile (err < 0)\n\t{\n\t\tif (rate > 48000)\n\t\t\trate = 48000;\n\t\telse if (rate > 44100)\n\t\t\trate = 44100;\n\t\telse if (rate > 22150)\n\t\t\trate = 22150;\n\t\telse if (rate > 11025)\n\t\t\trate = 11025;\n\t\telse if (rate > 800)\n\t\t\trate = 800;\n\t\telse\n\t\t{\n\t\t\tCon_Printf (CON_ERROR \"ALSA: no usable rates. %s\\n\", psnd_strerror (err));\n\t\t\tgoto error;\n\t\t}\n\t\terr = psnd_pcm_hw_params_set_rate_near (pcm, hw, &rate, 0);\n\t}\n\n\tif (rate > 11025)\n\t\tfrag_size = 8 * bps * rate / 11025;\n\telse\n\t\tfrag_size = 8 * bps;\n\n\terr = psnd_pcm_hw_params_set_period_size_near (pcm, hw, &frag_size, 0);\n\tif (0 > err)\n\t{\n\t\tCon_Printf (CON_ERROR \"ALSA: unable to set period size near %i. %s\\n\", (int) frag_size, psnd_strerror (err));\n\t\tgoto error;\n\t}\n\terr = psnd_pcm_hw_params (pcm, hw);\n\tif (0 > err) {\n\t\tCon_Printf (CON_ERROR \"ALSA: unable to install hw params: %s\\n\",\n\t\t\t\t\tpsnd_strerror (err));\n\t\tgoto error;\n\t}\n\terr = psnd_pcm_sw_params_current (pcm, sw);\n\tif (0 > err) {\n\t\tCon_Printf (CON_ERROR \"ALSA: unable to determine current sw params. %s\\n\", psnd_strerror (err));\n\t\tgoto error;\n\t}\n\terr = psnd_pcm_sw_params_set_start_threshold (pcm, sw, ~0U);\n\tif (0 > err) {\n\t\tCon_Printf (CON_ERROR \"ALSA: unable to set playback threshold. %s\\n\", psnd_strerror (err));\n\t\tgoto error;\n\t}\n\terr = psnd_pcm_sw_params_set_stop_threshold (pcm, sw, ~0U);\n\tif (0 > err) {\n\t\tCon_Printf (CON_ERROR \"ALSA: unable to set playback stop threshold. %s\\n\", psnd_strerror (err));\n\t\tgoto error;\n\t}\n\terr = psnd_pcm_sw_params (pcm, sw);\n\tif (0 > err) {\n\t\tCon_Printf (CON_ERROR \"ALSA: unable to install sw params. %s\\n\", psnd_strerror (err));\n\t\tgoto error;\n\t}\n\n\tsc->sn.numchannels = stereo;\n\tsc->sn.samplepos = 0;\n\tsc->sn.samplebytes = bps/8;\n\n\tbuffer_size = sc->sn.samples / stereo;\n\tif (buffer_size)\n\t{\n\t\terr = psnd_pcm_hw_params_set_buffer_size_near(pcm, hw, &buffer_size);\n\t\tif (err < 0)\n\t\t{\n\t\t\tCon_Printf (CON_ERROR \"ALSA: unable to set buffer size. %s\\n\", psnd_strerror (err));\n\t\t\tgoto error;\n\t\t}\n\t}\n\n\terr = psnd_pcm_hw_params_get_buffer_size (hw, &buffer_size);\n\tif (0 > err) {\n\t\tCon_Printf (CON_ERROR \"ALSA: unable to get buffer size. %s\\n\", psnd_strerror (err));\n\t\tgoto error;\n\t}\n\tsc->sn.speed = rate;\n#endif\n\n\tsc->sn.samples = buffer_size * sc->sn.numchannels;\t\t// mono samples in buffer\n\tsc->handle = pcm;\n\n\tsc->Lock\t\t= ALSA_LockBuffer;\n\tsc->Unlock\t\t= ALSA_UnlockBuffer;\n\tsc->Shutdown\t\t= ALSA_Shutdown;\n\tif (mmap)\n\t{\n\t\tsc->GetDMAPos\t= ALSA_MMap_GetDMAPos;\n\t\tsc->Submit\t= ALSA_MMap_Submit;\n\t\tsc->GetDMAPos(sc);\t\t// sets shm->buffer\n\n\t\t//alsa doesn't seem to like high mixahead values\n\t\t//(maybe it tells us above somehow...)\n\t\t//so force it lower\n\t\t//quake's default of 0.2 was for 10fps rendering anyway\n\t\t//so force it down to 0.1 which is the default for halflife at least, and should give better latency\n\t\t{\n\t\t\textern cvar_t _snd_mixahead;\n\t\t\tif (_snd_mixahead.value >= 0.2)\n\t\t\t{\n\t\t\t\tCon_Printf(\"Alsa Hack: _snd_mixahead forced lower\\n\");\n\t\t\t\t_snd_mixahead.value = 0.1;\n\t\t\t}\n\t\t}\n\t}\n\telse\n\t{\n\t\tsc->GetDMAPos\t= ALSA_RW_GetDMAPos;\n\t\tsc->Submit\t= ALSA_RW_Submit;\n\n\t\tsc->samplequeue = sc->sn.samples;\n\t\tsc->sn.buffer = malloc(sc->sn.samples * sc->sn.samplebytes);\n\n\t\terr = psnd_pcm_prepare(pcm);\n\t\tif (0 > err)\n\t\t{\n\t\t\tCon_Printf (CON_ERROR \"ALSA: unable to prepare for use. %s\\n\", psnd_strerror (err));\n\t\t\tgoto error;\n\t\t}\n\t}\n\n\treturn true;\n\nerror:\n\tpsnd_pcm_close (pcm);\n\treturn false;\n}\n#define SDRVNAME \"ALSA\"\nstatic qboolean QDECL ALSA_Enumerate(void (QDECL *cb) (const char *drivername, const char *devicecode, const char *readablename))\n{\n\tsize_t i;\n\tvoid **hints;\n\n\tif (Alsa_InitAlsa())\n\t{\n\t\tif (!psnd_device_name_hint(-1, \"pcm\", &hints))\n\t\t{\n\t\t\tfor (i = 0; hints[i]; i++)\n\t\t\t{\n\t\t\t\tchar *n = psnd_device_name_get_hint(hints[i], \"NAME\");\n\t\t\t\tif (n)\n\t\t\t\t{\n\t\t\t\t\tchar *t = psnd_device_name_get_hint(hints[i], \"IOID\");\n\t\t\t\t\tif (!t || strcasecmp(t, \"Input\"))\n\t\t\t\t\t{\n\t\t\t\t\t\tchar *d = psnd_device_name_get_hint(hints[i], \"DESC\");\n\t\t\t\t\t\tif (d)\n\t\t\t\t\t\t\tcb(SDRVNAME, n, va(\"ALSA:%s\", d));\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tcb(SDRVNAME, n, n);\n\t\t\t\t\t\tfree(d);\n\t\t\t\t\t}\n\t\t\t\t\tfree(t);\n\t\t\t\t\tfree(n);\t//dangerous to free things across boundaries.\n\t\t\t\t}\n\t\t\t}\n\t\t\tpsnd_device_name_free_hint(hints);\n\t\t}\n\t\telse\n\t\t\treturn false;\n\t}\n\treturn true;\n}\nsounddriver_t ALSA_Output =\n{\n\tSDRVNAME,\n\tALSA_InitCard,\n\tALSA_Enumerate\n};\n#endif\n"
  },
  {
    "path": "engine/client/snd_directx.c",
    "content": "/*\nCopyright (C) 1996-1997 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n*/\n#include \"quakedef.h\"\n#include \"winquake.h\"\n\n#ifdef AVAIL_DSOUND\n\n//#define DIRECTSOUND_VERSION 0x800 //either < 0x800 (eax-only) or 0x800 (microsoft's fx stuff).\n#include <dsound.h>\n\n#if !defined(IDirectSoundFXI3DL2Reverb_SetAllParameters) && DIRECTSOUND_VERSION>=0x800\n\t//mingw defines version as 0x900, but doesn't provide all the extra interfaces (only the core stuff).\n\t//which makes it kinda pointless, so lets provide the crap that its missing.\n\ttypedef struct {\n\t\tLONG lRoom;\n\t\tLONG lRoomHF;\n\t\tFLOAT flRoomRolloffFactor;\n\t\tFLOAT flDecayTime;\n\t\tFLOAT flDecayHFRatio;\n\t\tLONG lReflections;\n\t\tFLOAT flReflectionsDelay;\n\t\tLONG lReverb;\n\t\tFLOAT flReverbDelay;\n\t\tFLOAT flDiffusion;\n\t\tFLOAT flDensity;\n\t\tFLOAT flHFReference;\n\t} DSFXI3DL2Reverb;\n\n\ttypedef struct IDirectSoundFXI3DL2Reverb\n\t{\n\t\tstruct\n\t\t{\n\t\t\tSTDMETHOD(QueryInterface)(struct IDirectSoundFXI3DL2Reverb *this_, REFIID riid, LPVOID * ppvObj);\n\t\t\tSTDMETHOD_(ULONG,AddRef)(struct IDirectSoundFXI3DL2Reverb *this_);\n\t\t\tSTDMETHOD_(ULONG,Release)(struct IDirectSoundFXI3DL2Reverb *this_);\n\t\t\tSTDMETHOD(SetAllParameters)(struct IDirectSoundFXI3DL2Reverb *this_, const DSFXI3DL2Reverb *pcDsFxI3DL2Reverb);\n\t\t\t//INCOMPLETE\n\t\t} *lpVtbl;\n\t} IDirectSoundFXI3DL2Reverb;\n\t#define IDirectSoundFXI3DL2Reverb8 IDirectSoundFXI3DL2Reverb\n\t#define IID_IDirectSoundFXI3DL2Reverb8 IID_IDirectSoundFXI3DL2Reverb\n\n\t#define IDirectSoundFXI3DL2Reverb_Release(a) (a)->lpVtbl->Release(a)\n\t#define IDirectSoundFXI3DL2Reverb_SetAllParameters(a,b) (a)->lpVtbl->SetAllParameters(a,b)\n#endif\n\n#if _MSC_VER <= 1200\n\t#define FORCE_DEFINE_GUID DEFINE_GUID\n#else\n\t#ifndef DECLSPEC_SELECTANY\n\t\t#define DECLSPEC_SELECTANY\n\t#endif\n\t#define FORCE_DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \\\n\t\t\tEXTERN_C const GUID DECLSPEC_SELECTANY name \\\n\t\t\t\t\t= { l, w1, w2, { b1, b2,  b3,  b4,  b5,  b6,  b7,  b8 } }\n#endif\n\n#if DIRECTSOUND_VERSION >= 0x0800\nFORCE_DEFINE_GUID(IID_IDirectSoundBuffer8, 0x6825a449, 0x7524, 0x4d82, 0x92, 0x0f, 0x50, 0xe3, 0x6a, 0xb3, 0xab, 0x1e);\nFORCE_DEFINE_GUID(IID_IDirectSound8, 0xC50A7E93, 0xF395, 0x4834, 0x9E, 0xF6, 0x7F, 0xA9, 0x9D, 0xE5, 0x09, 0x66);\nFORCE_DEFINE_GUID(IID_IDirectSoundFXI3DL2Reverb, 0x4b166a6a, 0x0d66, 0x43f3, 0x80, 0xe3, 0xee, 0x62, 0x80, 0xde, 0xe1, 0xa4);\nFORCE_DEFINE_GUID(GUID_DSFX_STANDARD_I3DL2REVERB, 0xef985e71, 0xd5c7, 0x42d4, 0xba, 0x4d, 0x2d, 0x07, 0x3e, 0x2e, 0x96, 0xf4);\nHRESULT (WINAPI *pDirectSoundCreate8)(GUID FAR *lpGUID, LPDIRECTSOUND8 FAR *lplpDS, IUnknown FAR *pUnkOuter);\n#endif\n\nFORCE_DEFINE_GUID(IID_IDirectSound, 0x279AFA83, 0x4981, 0x11CE, 0xA5, 0x21, 0x00, 0x20, 0xAF, 0x0B, 0xE5, 0x60);\nFORCE_DEFINE_GUID(IID_IKsPropertySet, 0x31efac30, 0x515c, 0x11d0, 0xa9, 0xaa, 0x00, 0xaa, 0x00, 0x61, 0xbe, 0x93);\nHRESULT (WINAPI *pDirectSoundCreate)(GUID FAR *lpGUID, LPDIRECTSOUND FAR *lplpDS, IUnknown FAR *pUnkOuter);\nHRESULT (WINAPI *pDirectSoundEnumerate)(LPDSENUMCALLBACKA lpCallback, LPVOID lpContext);\n#if defined(VOICECHAT)\nHRESULT (WINAPI *pDirectSoundCaptureCreate)(GUID FAR *lpGUID, LPDIRECTSOUNDCAPTURE FAR *lplpDS, IUnknown FAR *pUnkOuter);\nHRESULT (WINAPI *pDirectSoundCaptureEnumerate)(LPDSENUMCALLBACK lpDSEnumCallback, LPVOID lpContext);\n#endif\n\n// 64K is > 1 second at 16-bit, 22050 Hz\n#define\tWAV_BUFFERS\t\t\t\t64\n#define\tWAV_MASK\t\t\t\t0x3F\n#define\tWAV_BUFFER_SIZE\t\t\t0x0400\n#define SECONDARY_BUFFER_SIZE\t0x10000\n\ntypedef struct {\n\tLPDIRECTSOUND pDS;\n\tLPDIRECTSOUNDBUFFER pDSBuf;\n\tLPDIRECTSOUNDBUFFER pDSPBuf;\n\n#if DIRECTSOUND_VERSION >= 0x0800\n\t//dsound8 interfaces, for reverb effects\n\tLPDIRECTSOUND8 pDS8;\n\tLPDIRECTSOUNDBUFFER8 pDSBuf8;\n\tIDirectSoundFXI3DL2Reverb8 *pReverb;\n#endif\n\n\tDWORD gSndBufSize;\n\tDWORD\t\tmmstarttime;\n\n\tint curreverb;\n\tint curreverbmodcount;\t//so it updates if the effect itself is updated\n\n#ifdef _IKsPropertySet_\n\tLPKSPROPERTYSET\tEaxKsPropertiesSet;\n#endif\n} dshandle_t;\n\nHINSTANCE hInstDS;\n\nstatic void DSOUND_Restore(soundcardinfo_t *sc)\n{\n\tDWORD\tdwStatus;\n\tdshandle_t *dh = sc->handle;\n\tif (IDirectSoundBuffer_GetStatus (dh->pDSBuf, &dwStatus) != ERROR_SUCCESS)\n\t\tCon_Printf (\"Couldn't get sound buffer status\\n\");\n\n\tif (dwStatus & DSBSTATUS_BUFFERLOST)\n\t\tIDirectSoundBuffer_Restore (dh->pDSBuf);\n\n\tif (!(dwStatus & DSBSTATUS_PLAYING))\n\t\tIDirectSoundBuffer_Play(dh->pDSBuf, 0, 0, DSBPLAY_LOOPING);\n}\n\nstatic DWORD\tdsound_locksize;\nstatic void *DSOUND_Lock(soundcardinfo_t *sc, unsigned int *sampidx)\n{\n\tvoid *ret;\n\tint reps;\n\tDWORD\tdwSize2=0;\n\tDWORD\t*pbuf2;\n\tHRESULT\thresult;\n\n\tdshandle_t *dh = sc->handle;\n\tdsound_locksize=0;\n\n\treps = 0;\n\n\twhile ((hresult = IDirectSoundBuffer_Lock(dh->pDSBuf, 0, dh->gSndBufSize, (void**)&ret, &dsound_locksize,\n\t\t\t\t\t\t\t\t   (void**)&pbuf2, &dwSize2, 0)) != DS_OK)\n\t{\n\t\tif (hresult != DSERR_BUFFERLOST)\n\t\t{\n\t\t\tCon_Printf (\"S_TransferStereo16: DS::Lock Sound Buffer Failed\\n\");\n\t\t\treturn NULL;\n\t\t}\n\n\t\tif (++reps > 10000)\n\t\t{\n\t\t\tCon_Printf (\"S_TransferStereo16: DS: couldn't restore buffer\\n\");\n\t\t\treturn NULL;\n\t\t}\n\n\t\tDSOUND_Restore(sc);\n\t}\n\n\treturn ret;\n}\n\n//called when the mixer is done with it.\nstatic void DSOUND_Unlock(soundcardinfo_t *sc, void *buffer)\n{\n\tdshandle_t *dh = sc->handle;\n\tIDirectSoundBuffer_Unlock(dh->pDSBuf, buffer, dsound_locksize, NULL, 0);\n}\n\n/*\n==================\nFreeSound\n==================\n*/\n//per device\nstatic void DSOUND_Shutdown_Internal (soundcardinfo_t *sc)\n{\n\tdshandle_t *dh = sc->handle;\n\tif (!dh)\n\t\treturn;\n\n\tsc->handle = NULL;\n#ifdef _IKsPropertySet_\n\tif (dh->EaxKsPropertiesSet)\n\t\tIKsPropertySet_Release(dh->EaxKsPropertiesSet);\n\tdh->EaxKsPropertiesSet = NULL;\n#endif\n\n#if DIRECTSOUND_VERSION >= 0x0800\n\tif (dh->pReverb)\n\t\tIDirectSoundFXI3DL2Reverb_Release(dh->pReverb);\n\tdh->pReverb = NULL;\n\tif (dh->pDSBuf8)\n\t\tIDirectSoundBuffer8_Release(dh->pDSBuf8);\n\tdh->pDSBuf8 = NULL;\n#endif\n\n\tif (dh->pDSBuf)\n\t{\n\t\tIDirectSoundBuffer_Stop(dh->pDSBuf);\n\t\tIDirectSoundBuffer_Release(dh->pDSBuf);\n\t\tif (dh->pDSBuf == dh->pDSPBuf)\n\t\t\tdh->pDSPBuf = NULL;\n\t\tdh->pDSBuf = NULL;\n\t}\n\n\tif (dh->pDSPBuf)\n\t\tIDirectSoundBuffer_Release(dh->pDSPBuf);\n\tdh->pDSPBuf = NULL;\n\n\tif (dh->pDS)\n\t{\n\t\tIDirectSound_SetCooperativeLevel (dh->pDS, mainwindow, DSSCL_NORMAL);\n\t\tIDirectSound_Release(dh->pDS);\n\t}\n\n\tdh->pDS = NULL;\n\n\tZ_Free(dh);\n}\n\nstatic void DSOUND_Shutdown (soundcardinfo_t *sc)\n{\n#ifdef MULTITHREAD\n\tif  (sc->thread)\n\t{\n\t\t//thread does the actual closing.\n\t\tsc->selfpainting = false;\n\t\tSys_WaitOnThread(sc->thread);\n\t\tsc->thread = NULL;\n\t}\n\telse\n#endif\n\t\tDSOUND_Shutdown_Internal(sc);\n}\n\n/*\n\tDirect Sound.\n\tThese following defs should be moved to winquake.h somewhere.\n\n\tWe tell DS to use a different wave format. We do this to gain extra channels. >2\n\tWe still use the old stuff too, when we can for compatability.\n\n\tEAX 2 is also supported.\n\tThis is a global state. Once applied, it's applied for other programs too.\n\tWe have to do a few special things to try to ensure support in all it's different versions.\n*/\n\n/* new formatTag:*/\n#ifndef WAVE_FORMAT_EXTENSIBLE\n# define WAVE_FORMAT_EXTENSIBLE (0xfffe)\n#endif\n\n/* Speaker Positions:*/\n# define SPEAKER_FRONT_LEFT              0x1\n# define SPEAKER_FRONT_RIGHT             0x2\n# define SPEAKER_FRONT_CENTER            0x4\n# define SPEAKER_LOW_FREQUENCY           0x8\n# define SPEAKER_BACK_LEFT               0x10\n# define SPEAKER_BACK_RIGHT              0x20\n# define SPEAKER_FRONT_LEFT_OF_CENTER    0x40\n# define SPEAKER_FRONT_RIGHT_OF_CENTER   0x80\n# define SPEAKER_BACK_CENTER             0x100\n# define SPEAKER_SIDE_LEFT               0x200\n# define SPEAKER_SIDE_RIGHT              0x400\n# define SPEAKER_TOP_CENTER              0x800\n# define SPEAKER_TOP_FRONT_LEFT          0x1000\n# define SPEAKER_TOP_FRONT_CENTER        0x2000\n# define SPEAKER_TOP_FRONT_RIGHT         0x4000\n# define SPEAKER_TOP_BACK_LEFT           0x8000\n# define SPEAKER_TOP_BACK_CENTER         0x10000\n# define SPEAKER_TOP_BACK_RIGHT          0x20000\n\n/* Bit mask locations reserved for future use*/\n#ifndef SPEAKER_RESERVED\n# define SPEAKER_RESERVED                0x7FFC0000\n#endif\n\n/* Used to specify that any possible permutation of speaker configurations*/\n# define SPEAKER_ALL                     0x80000000\n\n/* DirectSound Speaker Config*/\n# define KSAUDIO_SPEAKER_MONO            (SPEAKER_FRONT_CENTER)\n# define KSAUDIO_SPEAKER_STEREO          (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT)\n# define KSAUDIO_SPEAKER_QUAD            (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | \\\n                                         SPEAKER_BACK_LEFT  | SPEAKER_BACK_RIGHT)\n# define KSAUDIO_SPEAKER_SURROUND        (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | \\\n                                         SPEAKER_FRONT_CENTER | SPEAKER_BACK_CENTER)\n# define KSAUDIO_SPEAKER_5POINT1         (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | \\\n                                         SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | \\\n                                         SPEAKER_BACK_LEFT  | SPEAKER_BACK_RIGHT)\n# define KSAUDIO_SPEAKER_7POINT1         (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | \\\n                                         SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | \\\n                                         SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | \\\n                                         SPEAKER_FRONT_LEFT_OF_CENTER | SPEAKER_FRONT_RIGHT_OF_CENTER)\n\ntypedef struct {\n\tWAVEFORMATEX    Format;\n\tunion {\n\t\tWORD wValidBitsPerSample;       /* bits of precision  */\n\t\tWORD wSamplesPerBlock;          /* valid if wBitsPerSample==0 */\n\t\tWORD wReserved;                 /* If neither applies, set to */\n\t\t\t\t\t\t\t\t\t\t/* zero. */\n\t} Samples;\n\tDWORD           dwChannelMask;      /* which channels are */\n\t\t\t\t\t\t\t\t\t\t/* present in stream  */\n\tGUID            SubFormat;\n} QWAVEFORMATEX;\n\nstatic const GUID  QKSDATAFORMAT_SUBTYPE_PCM\t\t= {0x00000001,0x0000,0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};\nstatic const GUID QKSDATAFORMAT_SUBTYPE_IEEE_FLOAT\t= {0x00000003,0x0000,0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};\n\n#ifdef _IKsPropertySet_\n//const static GUID  CLSID_EAXDIRECTSOUND = {0x4ff53b81, 0x1ce0, 0x11d3,\n//{0xaa, 0xb8, 0x0, 0xa0, 0xc9, 0x59, 0x49, 0xd5}};\nstatic const GUID  DSPROPSETID_EAX20_LISTENERPROPERTIES = {0x306a6a8, 0xb224, 0x11d2,\n{0x99, 0xe5, 0x0, 0x0, 0xe8, 0xd8, 0xc7, 0x22}};\n\ntypedef struct _EAXLISTENERPROPERTIES\n{\n    long lRoom;                    // room effect level at low frequencies\n    long lRoomHF;                  // room effect high-frequency level re. low frequency level\n    float flRoomRolloffFactor;     // like DS3D flRolloffFactor but for room effect\n    float flDecayTime;             // reverberation decay time at low frequencies\n    float flDecayHFRatio;          // high-frequency to low-frequency decay time ratio\n    long lReflections;             // early reflections level relative to room effect\n    float flReflectionsDelay;      // initial reflection delay time\n    long lReverb;                  // late reverberation level relative to room effect\n    float flReverbDelay;           // late reverberation delay time relative to initial reflection\n    unsigned long dwEnvironment;   // sets all listener properties\n    float flEnvironmentSize;       // environment size in meters\n    float flEnvironmentDiffusion;  // environment diffusion\n    float flAirAbsorptionHF;       // change in level per meter at 5 kHz\n    unsigned long dwFlags;         // modifies the behavior of properties\n} EAXLISTENERPROPERTIES, *LPEAXLISTENERPROPERTIES;\nenum\n{\n    EAX_ENVIRONMENT_GENERIC,\n    EAX_ENVIRONMENT_PADDEDCELL,\n    EAX_ENVIRONMENT_ROOM,\n    EAX_ENVIRONMENT_BATHROOM,\n    EAX_ENVIRONMENT_LIVINGROOM,\n    EAX_ENVIRONMENT_STONEROOM,\n    EAX_ENVIRONMENT_AUDITORIUM,\n    EAX_ENVIRONMENT_CONCERTHALL,\n    EAX_ENVIRONMENT_CAVE,\n    EAX_ENVIRONMENT_ARENA,\n    EAX_ENVIRONMENT_HANGAR,\n    EAX_ENVIRONMENT_CARPETEDHALLWAY,\n    EAX_ENVIRONMENT_HALLWAY,\n    EAX_ENVIRONMENT_STONECORRIDOR,\n    EAX_ENVIRONMENT_ALLEY,\n    EAX_ENVIRONMENT_FOREST,\n    EAX_ENVIRONMENT_CITY,\n    EAX_ENVIRONMENT_MOUNTAINS,\n    EAX_ENVIRONMENT_QUARRY,\n    EAX_ENVIRONMENT_PLAIN,\n    EAX_ENVIRONMENT_PARKINGLOT,\n    EAX_ENVIRONMENT_SEWERPIPE,\n    EAX_ENVIRONMENT_UNDERWATER,\n    EAX_ENVIRONMENT_DRUGGED,\n    EAX_ENVIRONMENT_DIZZY,\n    EAX_ENVIRONMENT_PSYCHOTIC,\n\n    EAX_ENVIRONMENT_COUNT\n};\ntypedef enum\n{\n    DSPROPERTY_EAXLISTENER_NONE,\n    DSPROPERTY_EAXLISTENER_ALLPARAMETERS,\n    DSPROPERTY_EAXLISTENER_ROOM,\n    DSPROPERTY_EAXLISTENER_ROOMHF,\n    DSPROPERTY_EAXLISTENER_ROOMROLLOFFFACTOR,\n    DSPROPERTY_EAXLISTENER_DECAYTIME,\n    DSPROPERTY_EAXLISTENER_DECAYHFRATIO,\n    DSPROPERTY_EAXLISTENER_REFLECTIONS,\n    DSPROPERTY_EAXLISTENER_REFLECTIONSDELAY,\n    DSPROPERTY_EAXLISTENER_REVERB,\n    DSPROPERTY_EAXLISTENER_REVERBDELAY,\n    DSPROPERTY_EAXLISTENER_ENVIRONMENT,\n    DSPROPERTY_EAXLISTENER_ENVIRONMENTSIZE,\n    DSPROPERTY_EAXLISTENER_ENVIRONMENTDIFFUSION,\n    DSPROPERTY_EAXLISTENER_AIRABSORPTIONHF,\n    DSPROPERTY_EAXLISTENER_FLAGS\n} DSPROPERTY_EAX_LISTENERPROPERTY;\n\n/*const static GUID DSPROPSETID_EAX20_BUFFERPROPERTIES ={\n    0x306a6a7,\n    0xb224,\n    0x11d2,\n    {0x99, 0xe5, 0x0, 0x0, 0xe8, 0xd8, 0xc7, 0x22}};*/\n\nstatic const GUID CLSID_EAXDirectSound ={\n\t\t0x4ff53b81,\n\t\t0x1ce0,\n\t\t0x11d3,\n\t\t{0xaa, 0xb8, 0x0, 0xa0, 0xc9, 0x59, 0x49, 0xd5}};\n\ntypedef struct _EAXBUFFERPROPERTIES\n{\n    long lDirect;                // direct path level\n    long lDirectHF;              // direct path level at high frequencies\n    long lRoom;                  // room effect level\n    long lRoomHF;                // room effect level at high frequencies\n    float flRoomRolloffFactor;   // like DS3D flRolloffFactor but for room effect\n    long lObstruction;           // main obstruction control (attenuation at high frequencies)\n    float flObstructionLFRatio;  // obstruction low-frequency level re. main control\n    long lOcclusion;             // main occlusion control (attenuation at high frequencies)\n    float flOcclusionLFRatio;    // occlusion low-frequency level re. main control\n    float flOcclusionRoomRatio;  // occlusion room effect level re. main control\n    long lOutsideVolumeHF;       // outside sound cone level at high frequencies\n    float flAirAbsorptionFactor; // multiplies DSPROPERTY_EAXLISTENER_AIRABSORPTIONHF\n    unsigned long dwFlags;       // modifies the behavior of properties\n} EAXBUFFERPROPERTIES, *LPEAXBUFFERPROPERTIES;\n\ntypedef enum\n{\n    DSPROPERTY_EAXBUFFER_NONE,\n    DSPROPERTY_EAXBUFFER_ALLPARAMETERS,\n    DSPROPERTY_EAXBUFFER_DIRECT,\n    DSPROPERTY_EAXBUFFER_DIRECTHF,\n    DSPROPERTY_EAXBUFFER_ROOM,\n    DSPROPERTY_EAXBUFFER_ROOMHF,\n    DSPROPERTY_EAXBUFFER_ROOMROLLOFFFACTOR,\n    DSPROPERTY_EAXBUFFER_OBSTRUCTION,\n    DSPROPERTY_EAXBUFFER_OBSTRUCTIONLFRATIO,\n    DSPROPERTY_EAXBUFFER_OCCLUSION,\n    DSPROPERTY_EAXBUFFER_OCCLUSIONLFRATIO,\n    DSPROPERTY_EAXBUFFER_OCCLUSIONROOMRATIO,\n    DSPROPERTY_EAXBUFFER_OUTSIDEVOLUMEHF,\n    DSPROPERTY_EAXBUFFER_AIRABSORPTIONFACTOR,\n    DSPROPERTY_EAXBUFFER_FLAGS\n} DSPROPERTY_EAX_BUFFERPROPERTY;\n#endif\n\nstatic long GainToMillibels(float gain)\n{\n\treturn 100*20*(0.43429*log(gain));\n}\nstatic void DSOUND_SetReverb(soundcardinfo_t *sc, size_t reverbidx)\n{\n\tdshandle_t *dh = sc->handle;\n\tstruct reverbproperties_s *prop;\n\tif (reverbidx >= numreverbproperties)\n\t\treturn;\t//invalid\n\tif (dh->curreverb == reverbidx && dh->curreverbmodcount == reverbproperties[reverbidx].modificationcount)\n\t\treturn;\t//nothing changed.\n\tdh->curreverb = reverbidx;\n\tdh->curreverbmodcount = reverbproperties[reverbidx].modificationcount;\n\n\tprop = &reverbproperties[dh->curreverb].props;\n\n#ifdef _IKsPropertySet_\n\t//attempt at eax support.\n\t//EAX is a global thing. Get it going in a game and your media player will be doing it too.\n\n\tif (dh->EaxKsPropertiesSet)\t//only on ds cards.\n\t{\n\t\tEAXLISTENERPROPERTIES ListenerProperties =  {0};\n\n\t\tListenerProperties.flEnvironmentSize = prop->flEchoTime;\n\t\tListenerProperties.flEnvironmentDiffusion = prop->flDiffusion;\n\t\tListenerProperties.lRoom = GainToMillibels(prop->flGain);\n\t\tListenerProperties.lRoomHF = GainToMillibels(prop->flGainHF);\n\t\tListenerProperties.flRoomRolloffFactor = prop->flRoomRolloffFactor;\n\t\tListenerProperties.flAirAbsorptionHF = prop->flAirAbsorptionGainHF;\n\t\tListenerProperties.lReflections = GainToMillibels(prop->flReflectionsGain);\n\t\tListenerProperties.flReflectionsDelay  = prop->flReflectionsDelay;\n\t\tListenerProperties.lReverb = GainToMillibels(prop->flLateReverbGain);\n\t\tListenerProperties.flReverbDelay = prop->flLateReverbDelay;\n\t\tListenerProperties.flDecayTime = prop->flDecayTime;\n\t\tListenerProperties.flDecayHFRatio = prop->flDecayHFRatio;\n\t\tListenerProperties.dwFlags = 0x3f;\n\t\tListenerProperties.dwEnvironment = reverbidx?EAX_ENVIRONMENT_UNDERWATER:0;\n\n\t\tif (FAILED(IKsPropertySet_Set(dh->EaxKsPropertiesSet, &DSPROPSETID_EAX20_LISTENERPROPERTIES,\n\t\t\t\t\tDSPROPERTY_EAXLISTENER_ALLPARAMETERS, 0, 0, &ListenerProperties,\n\t\t\t\t\tsizeof(ListenerProperties))))\n\t\t\tCon_SafePrintf (\"EAX set failed\\n\");\n\t}\n#endif\n\n#if DIRECTSOUND_VERSION >= 0x0800\n\tif (dh->pReverb)\n\t{\n\t\tDSFXI3DL2Reverb reverb;\n\t\treverb.lRoom\t\t\t\t= bound(-10000, GainToMillibels(prop->flGain), 0);\t\t\t// [-10000, 0]      default: -1000 mB\n\t\treverb.lRoomHF\t\t\t\t= bound(-10000, GainToMillibels(prop->flGainHF), 0);\t\t\t// [-10000, 0]      default: 0 mB\n\t\treverb.flRoomRolloffFactor\t= bound(0.0, prop->flRoomRolloffFactor, 10.0);\t\t\t\t// [0.0, 10.0]      default: 0.0\n\t\treverb.flDecayTime\t\t\t= bound(0.1, prop->flDecayTime, 20.0);\t\t\t\t\t\t// [0.1, 20.0]      default: 1.49s\n\t\treverb.flDecayHFRatio\t\t= bound(0.1, prop->flDecayHFRatio, 2.0);\t\t\t\t\t\t// [0.1, 2.0]       default: 0.83\n\t\treverb.lReflections\t\t\t= bound(-10000, GainToMillibels(prop->flReflectionsGain), 1000);\t// [-10000, 1000]   default: -2602 mB\n\t\treverb.flReflectionsDelay\t= bound(0.0, prop->flReflectionsDelay, 0.3);\t\t\t\t\t// [0.0, 0.3]       default: 0.007 s\n\t\treverb.lReverb\t\t\t\t= bound(-10000, GainToMillibels(prop->flLateReverbGain), 2000);\t// [-10000, 2000]   default: 200 mB\n\t\treverb.flReverbDelay\t\t= bound(0.0, prop->flLateReverbDelay, 0.1);\t\t\t\t\t// [0.0, 0.1]       default: 0.011 s\n\t\treverb.flDiffusion\t\t\t= bound(0.0, prop->flDiffusion*100, 100.0);\t\t\t\t\t// [0.0, 100.0]     default: 100.0 %\n\t\treverb.flDensity\t\t\t= bound(0.0, prop->flDensity*100, 100.0);\t\t\t\t\t\t// [0.0, 100.0]     default: 100.0 %\n\t\treverb.flHFReference\t\t= bound(20.0, prop->flHFReference, 20000.0);\t\t\t\t\t\t// [20.0, 20000.0]  default: 5000.0 Hz\n\n\t\tIDirectSoundFXI3DL2Reverb_SetAllParameters(dh->pReverb, &reverb);\n\t}\n#endif\n}\n\n/*\n==============\nSNDDMA_GetDMAPos\n\nreturn the current sample position (in mono samples read)\ninside the recirculating dma buffer, so the mixing code will know\nhow many sample are required to fill it up.\n===============\n*/\nstatic unsigned int DSOUND_GetDMAPos(soundcardinfo_t *sc)\n{\n\tDWORD\tmmtime;\n\tint\t\ts;\n\tDWORD\tdwWrite;\n\n\tdshandle_t *dh = sc->handle;\n\n\tIDirectSoundBuffer_GetCurrentPosition(dh->pDSBuf, &mmtime, &dwWrite);\n\ts = mmtime - dh->mmstarttime;\n\n\ts /= sc->sn.samplebytes;\n\ts %= (sc->sn.samples);\n\treturn s;\n}\n\n/*\n==============\nSNDDMA_Submit\n\nSend sound to device if buffer isn't really the dma buffer\n===============\n*/\nstatic void DSOUND_Submit(soundcardinfo_t *sc, int start, int end)\n{\n}\n\nstatic qboolean DSOUND_InitOutputLibrary(void)\n{\n\tif (!hInstDS)\n\t\thInstDS = LoadLibrary(\"dsound.dll\");\n\tif (!hInstDS)\n\t{\n\t\tCon_SafePrintf (\"Couldn't load dsound.dll\\n\");\n\t\treturn false;\n\t}\n\tif (!pDirectSoundCreate)\n\t\tpDirectSoundCreate = (void *)GetProcAddress(hInstDS,\"DirectSoundCreate\");\n#if DIRECTSOUND_VERSION >= 0x0800\n\tif (!pDirectSoundCreate8)\n\t\tpDirectSoundCreate8 = (void *)GetProcAddress(hInstDS,\"DirectSoundCreate8\");\t//xp+\n\tif (!pDirectSoundCreate8)\n#endif\n\t{\n\t\tif (!pDirectSoundCreate)\n\t\t{\n\t\t\tCon_SafePrintf (\"Couldn't get DS proc addr\\n\");\n\t\t\treturn false;\n\t\t}\n\t}\n\tif (!pDirectSoundEnumerate)\n\t\tpDirectSoundEnumerate = (void *)GetProcAddress(hInstDS,\"DirectSoundEnumerateA\");\n\treturn true;\n}\n/*\n==================\nSNDDMA_InitDirect\n\nDirect-Sound support\n==================\n*/\nstatic int DSOUND_InitCard_Internal (soundcardinfo_t *sc, char *cardname)\n{\n\textern cvar_t snd_inactive;\n\textern cvar_t snd_eax;\n\tint usereverb;\t//2=eax, 1=ds8\n\tDSBUFFERDESC\tdsbuf;\n\tDSBCAPS\t\t\tdsbcaps;\n\tDWORD\t\t\tdwSize, dwWrite;\n\tDSCAPS\t\t\tdscaps;\n\tQWAVEFORMATEX\tformat, pformat;\n\tHRESULT\t\t\thresult;\n\tint\t\t\t\treps;\n\tqboolean\t\tprimary_format_set;\n\tdshandle_t *dh;\n\tchar *buffer;\n\tGUID guid, *dsguid;\n\n\n\tmemset (&format, 0, sizeof(format));\n\n\tif (*sc->name)\n\t{\n\t\twchar_t mssuck[128];\n\t\tmbstowcs(mssuck, sc->name, sizeof(mssuck)/sizeof(mssuck[0])-1);\n\t\tCLSIDFromString(mssuck, &guid);\n\t\tdsguid = &guid;\n\t}\n\telse\n\t{\n\t\tmemset(&guid, 0, sizeof(GUID));\n\t\tdsguid = NULL;\n\t}\n\n\tif (sc->sn.numchannels >= 8) // 7.1 surround\n\t{\n\t\tformat.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;\n\t\tformat.Format.cbSize = 22;\n\t\tmemcpy(&format.SubFormat, &QKSDATAFORMAT_SUBTYPE_PCM, sizeof(GUID));\n\n\t\tformat.dwChannelMask = KSAUDIO_SPEAKER_7POINT1;\n\t\tsc->sn.numchannels = 8;\n\t}\n\telse if (sc->sn.numchannels >= 6)\t//5.1 surround\n\t{\n\t\tformat.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;\n\t\tformat.Format.cbSize = 22;\n\t\tmemcpy(&format.SubFormat, &QKSDATAFORMAT_SUBTYPE_PCM, sizeof(GUID));\n\n\t\tformat.dwChannelMask = KSAUDIO_SPEAKER_5POINT1;\n\t\tsc->sn.numchannels = 6;\n\t}\n\telse if (sc->sn.numchannels >= 4)\t//4 speaker quad\n\t{\n\t\tformat.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;\n\t\tformat.Format.cbSize = 22;\n\t\tmemcpy(&format.SubFormat, &QKSDATAFORMAT_SUBTYPE_PCM, sizeof(GUID));\n\n\t\tformat.dwChannelMask = KSAUDIO_SPEAKER_QUAD;\n\t\tsc->sn.numchannels = 4;\n\t}\n\telse if (sc->sn.numchannels >= 2)\t//stereo\n\t{\n\t\tformat.Format.wFormatTag = WAVE_FORMAT_PCM;\n\t\tformat.Format.cbSize = 0;\n\t\tsc->sn.numchannels = 2;\n\t}\n\telse //mono time\n\t{\n\t\tformat.Format.wFormatTag = WAVE_FORMAT_PCM;\n\t\tformat.Format.cbSize = 0;\n\t\tsc->sn.numchannels = 1;\n\t}\n\n\tswitch(sc->sn.samplebytes)\n\t{\n\tcase 4:\n\t\t//FTE does not support 32bit int audio, rather we interpret samplebits 32 as floats.\n\t\tformat.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;\n\t\tformat.Format.cbSize = 22;\n\t\tmemcpy(&format.SubFormat, &QKSDATAFORMAT_SUBTYPE_IEEE_FLOAT, sizeof(GUID));\n\t\tsc->sn.sampleformat = QSF_F32;\n\t\tbreak;\n\tcase 2:\n\t\tsc->sn.sampleformat = QSF_S16;\n\t\tbreak;\n\tcase 1:\n\t\tsc->sn.sampleformat = QSF_U8;\n\t\tbreak;\n\t}\n\n\tformat.Format.nChannels = sc->sn.numchannels;\n\tformat.Format.wBitsPerSample = sc->sn.samplebytes*8;\n\tformat.Format.nSamplesPerSec = sc->sn.speed;\n\tformat.Format.nBlockAlign = format.Format.nChannels * format.Format.wBitsPerSample / 8;\n\tformat.Format.nAvgBytesPerSec = format.Format.nSamplesPerSec * format.Format.nBlockAlign;\n\n\tif (!DSOUND_InitOutputLibrary())\n\t\treturn false;\n\n\tsc->handle = Z_Malloc(sizeof(dshandle_t));\n\tdh = sc->handle;\n\n\tusereverb = !!snd_eax.ival;\n //EAX attempt\n#if 1//_MSC_VER > 1200\n#ifdef _IKsPropertySet_\n\tdh->pDS = NULL;\n\tif (usereverb)\n\t{\n\t\tCoInitialize(NULL);\n\t\tif (FAILED(CoCreateInstance( &CLSID_EAXDirectSound, NULL, CLSCTX_INPROC_SERVER, &IID_IDirectSound, (void **)&dh->pDS )))\n\t\t\tdh->pDS=NULL;\n\t\telse\n\t\t{\n\t\t\tIDirectSound_Initialize(dh->pDS, dsguid);\n\t\t\tusereverb = 2;\n\t\t}\n\t}\n\n\tif (!dh->pDS)\n#endif\n#endif\n\t{\n\t\tfor(;;)\n\t\t{\n\t\t\tdh->pDS = NULL;\n#if DIRECTSOUND_VERSION >= 0x0800\n\t\t\tdh->pDS8 = NULL;\n\t\t\tif (pDirectSoundCreate8)\n\t\t\t{\n\t\t\t\thresult = pDirectSoundCreate8(dsguid, &dh->pDS8, NULL);\n\t\t\t\tdh->pDS = (void*)dh->pDS8;\t//evil cast\n\t\t\t}\n\t\t\telse\n#endif\n\t\t\t\thresult = pDirectSoundCreate(dsguid, &dh->pDS, NULL);\n\t\t\tif (hresult == DS_OK)\n\t\t\t\tbreak;\n\n\t\t\tif (hresult != DSERR_ALLOCATED)\n\t\t\t{\n\t\t\t\tCon_SafePrintf (\": create failed\\n\");\n\t\t\t\treturn false;\n\t\t\t}\n\n//\t\t\tif (MessageBox (NULL,\n//\t\t\t\t\t\t\t\"The sound hardware is in use by another app.\\n\\n\"\n//\t\t\t\t\t\t\t\"Select Retry to try to start sound again or Cancel to run Quake with no sound.\",\n//\t\t\t\t\t\t\t\"Sound not available\",\n//\t\t\t\t\t\t\tMB_RETRYCANCEL | MB_SETFOREGROUND | MB_ICONEXCLAMATION) != IDRETRY)\n//\t\t\t{\n\t\t\t\tCon_SafePrintf (\": failure\\n\"\n\t\t\t\t\t\t\t\t\"  hardware already in use\\n\"\n\t\t\t\t\t\t\t\t\"  Close the other app then use snd_restart\\n\");\n\t\t\t\treturn false;\n//\t\t\t}\n\t\t}\n\t}\n\n#ifdef FTE_SDL\n#define mainwindow GetDesktopWindow()\n#endif\n\tif (DS_OK != IDirectSound_SetCooperativeLevel (dh->pDS, mainwindow, DSSCL_EXCLUSIVE))\n\t{\n\t\tCon_SafePrintf (\"Set coop level failed\\n\");\n\t\tDSOUND_Shutdown_Internal (sc);\n\t\treturn false;\n\t}\n\n\tdscaps.dwSize = sizeof(dscaps);\n\n\tif (DS_OK != IDirectSound_GetCaps (dh->pDS, &dscaps))\n\t{\n\t\tCon_SafePrintf (\"Couldn't get DS caps\\n\");\n\t}\n\n\tif (dscaps.dwFlags & DSCAPS_EMULDRIVER)\n\t{\n\t\tCon_SafePrintf (\"No DirectSound driver installed\\n\");\n\t\tDSOUND_Shutdown_Internal (sc);\n\t\treturn false;\n\t}\n\n\n// get access to the primary buffer, if possible, so we can set the\n// sound hardware format\n\tmemset (&dsbuf, 0, sizeof(dsbuf));\n\tdsbuf.dwSize = sizeof(DSBUFFERDESC);\n\tdsbuf.dwFlags = DSBCAPS_PRIMARYBUFFER|DSBCAPS_CTRLVOLUME;\n\tdsbuf.dwBufferBytes = 0;\n\tdsbuf.lpwfxFormat = NULL;\n\n#ifdef DSBCAPS_GLOBALFOCUS\n#ifndef FTE_SDL\n\tif (snd_inactive.ival || sys_parentwindow\n\t\t) /*always inactive if we have a parent window, because we can't tell properly otherwise*/\n#endif\n\t{\n\t\tdsbuf.dwFlags |= DSBCAPS_GLOBALFOCUS;\n\t\tsc->inactive_sound = true;\n\t}\n#endif\n\n\tmemset(&dsbcaps, 0, sizeof(dsbcaps));\n\tdsbcaps.dwSize = sizeof(dsbcaps);\n\tprimary_format_set = false;\n\n\tif (!COM_CheckParm (\"-snoforceformat\"))\n\t{\n\t\tif (DS_OK == IDirectSound_CreateSoundBuffer(dh->pDS, &dsbuf, &dh->pDSPBuf, NULL))\n\t\t{\n\t\t\tpformat = format;\n\n\t\t\tif (DS_OK != IDirectSoundBuffer_SetFormat (dh->pDSPBuf, (WAVEFORMATEX *)&pformat))\n\t\t\t{\n//\t\t\t\tif (snd_firsttime)\n//\t\t\t\t\tCon_SafePrintf (\"Set primary sound buffer format: no\\n\");\n\t\t\t}\n\t\t\telse\n//\t\t\t{\n//\t\t\t\tif (snd_firsttime)\n//\t\t\t\t\tCon_SafePrintf (\"Set primary sound buffer format: yes\\n\");\n\n\t\t\t\tprimary_format_set = true;\n//\t\t\t}\n\t\t}\n\t}\n\n\tif (!primary_format_set || !COM_CheckParm (\"-primarysound\"))\n\t{\n\t// create the secondary buffer we'll actually work with\n\t\tmemset (&dsbuf, 0, sizeof(dsbuf));\n\t\tdsbuf.dwSize = sizeof(DSBUFFERDESC);\n\t\tdsbuf.dwFlags = DSBCAPS_CTRLFREQUENCY|DSBCAPS_LOCSOFTWARE;\t//dmw 29 may, 2003 removed locsoftware\n\n#if DIRECTSOUND_VERSION >= 0x0800\n\t\tif (usereverb == 1)\n\t\t\tdsbuf.dwFlags |= DSBCAPS_CTRLFX;\n#endif\n\n#ifdef DSBCAPS_GLOBALFOCUS\n\t\tif (snd_inactive.ival)\n\t\t{\n\t\t\tdsbuf.dwFlags |= DSBCAPS_GLOBALFOCUS;\n\t\t\tsc->inactive_sound = true;\n\t\t}\n#endif\n\t\tdsbuf.dwBufferBytes = sc->sn.samples / format.Format.nChannels;\n\t\tif (!dsbuf.dwBufferBytes)\n\t\t{\n\t\t\tdsbuf.dwBufferBytes = SECONDARY_BUFFER_SIZE;\n\t\t\t// the fast rates will need a much bigger buffer\n\t\t\tif (format.Format.nSamplesPerSec > 48000)\n\t\t\t\tdsbuf.dwBufferBytes *= 4;\n\t\t}\n\t\tdsbuf.lpwfxFormat = (WAVEFORMATEX *)&format;\n\n\t\tmemset(&dsbcaps, 0, sizeof(dsbcaps));\n\t\tdsbcaps.dwSize = sizeof(dsbcaps);\n\n\t\tif (DS_OK != IDirectSound_CreateSoundBuffer(dh->pDS, &dsbuf, &dh->pDSBuf, NULL))\n\t\t{\n\t\t\tCon_SafePrintf (\"DS:CreateSoundBuffer Failed\");\n\t\t\tDSOUND_Shutdown_Internal (sc);\n\t\t\treturn false;\n\t\t}\n\n\t\tsc->sn.numchannels = format.Format.nChannels;\n\t\tsc->sn.samplebytes = format.Format.wBitsPerSample/8;\n\t\tsc->sn.speed = format.Format.nSamplesPerSec;\n\n\t\tif (DS_OK != IDirectSoundBuffer_GetCaps (dh->pDSBuf, &dsbcaps))\n\t\t{\n\t\t\tCon_SafePrintf (\"DS:GetCaps failed\\n\");\n\t\t\tDSOUND_Shutdown_Internal (sc);\n\t\t\treturn false;\n\t\t}\n\n//\t\tif (snd_firsttime)\n//\t\t\tCon_SafePrintf (\"Using secondary sound buffer\\n\");\n\t}\n\telse\n\t{\n\t\tif (DS_OK != IDirectSound_SetCooperativeLevel (dh->pDS, mainwindow, DSSCL_WRITEPRIMARY))\n\t\t{\n\t\t\tCon_SafePrintf (\"Set coop level failed\\n\");\n\t\t\tDSOUND_Shutdown_Internal (sc);\n\t\t\treturn false;\n\t\t}\n\n\t\tif (DS_OK != IDirectSoundBuffer_GetCaps (dh->pDSPBuf, &dsbcaps))\n\t\t{\n\t\t\tCon_Printf (\"DS:GetCaps failed\\n\");\n\t\t\tDSOUND_Shutdown_Internal (sc);\n\t\t\treturn false;\n\t\t}\n\n\t\tdh->pDSBuf = dh->pDSPBuf;\n//\t\tCon_SafePrintf (\"Using primary sound buffer\\n\");\n\t}\n\n\tdh->gSndBufSize = dsbcaps.dwBufferBytes;\n\n#if DIRECTSOUND_VERSION >= 0x0800\n\tif (usereverb == 1)\n\t{\n\t\tif (SUCCEEDED(IDirectSoundBuffer_QueryInterface(dh->pDSBuf, &IID_IDirectSoundBuffer8, (void*)&dh->pDSBuf8)))\n\t\t{\n\t\t\tDSEFFECTDESC effects[1];\n\t\t\tDWORD results[1];\n\t\t\tmemset(effects, 0, sizeof(effects));\n\t\t\teffects[0].dwSize = sizeof(effects[0]);\n\t\t\teffects[0].dwFlags = 0;\n\t\t\teffects[0].guidDSFXClass = GUID_DSFX_STANDARD_I3DL2REVERB;\n\t\t\tif (SUCCEEDED(IDirectSoundBuffer8_SetFX(dh->pDSBuf8, 1, effects, results)))\n\t\t\t\tif (SUCCEEDED(IDirectSoundBuffer8_GetObjectInPath(dh->pDSBuf8, &GUID_DSFX_STANDARD_I3DL2REVERB, 0, &IID_IDirectSoundFXI3DL2Reverb8, (void*)&dh->pReverb)))\n\t\t\t\t\tusereverb = 0;\n\t\t}\n\t}\n#endif\n\n#if 1\n\t// Make sure mixer is active\n\tIDirectSoundBuffer_Play(dh->pDSBuf, 0, 0, DSBPLAY_LOOPING);\n\n\tCon_DPrintf(\"   %d channel(s)\\n\"\n\t\t\t\t\"   %d bits/sample\\n\"\n\t\t\t\t\"   %d bytes/sec\\n\",\n\t\t\t\tsc->sn.numchannels, sc->sn.samplebytes*8, sc->sn.speed);\n\n\n// initialize the buffer\n\treps = 0;\n\n\twhile ((hresult = IDirectSoundBuffer_Lock(dh->pDSBuf, 0, dh->gSndBufSize, (void**)&buffer, &dwSize, NULL, NULL, 0)) != DS_OK)\n\t{\n\t\tif (hresult != DSERR_BUFFERLOST)\n\t\t{\n\t\t\tCon_SafePrintf (\"SNDDMA_InitDirect: DS::Lock Sound Buffer Failed\\n\");\n\t\t\tDSOUND_Shutdown_Internal (sc);\n\t\t\treturn false;\n\t\t}\n\n\t\tif (++reps > 10000)\n\t\t{\n\t\t\tCon_SafePrintf (\"SNDDMA_InitDirect: DS: couldn't restore buffer\\n\");\n\t\t\tDSOUND_Shutdown_Internal (sc);\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tmemset(buffer, 0, dwSize);\n//\t\tlpData[4] = lpData[5] = 0x7f;\t// force a pop for debugging\n\n//\tSleep(500);\n\n\tIDirectSoundBuffer_Unlock(dh->pDSBuf, buffer, dwSize, NULL, 0);\n\n\n\tIDirectSoundBuffer_Stop(dh->pDSBuf);\n#endif\n\tIDirectSoundBuffer_GetCurrentPosition(dh->pDSBuf, &dh->mmstarttime, &dwWrite);\n\tIDirectSoundBuffer_Play(dh->pDSBuf, 0, 0, DSBPLAY_LOOPING);\n\n\tsc->sn.samples = dh->gSndBufSize/sc->sn.samplebytes;\n\tsc->sn.samplepos = 0;\n\tsc->sn.buffer = NULL;\n\n\tdh->curreverb = ~0;\n\tdh->curreverbmodcount = ~0;\n\n\n\tsc->Lock\t\t= DSOUND_Lock;\n\tsc->Unlock\t\t= DSOUND_Unlock;\n\tsc->SetEnvironmentReverb\t= DSOUND_SetReverb;\n\tsc->Submit\t\t= DSOUND_Submit;\n\tsc->Shutdown\t= DSOUND_Shutdown;\n\tsc->GetDMAPos\t= DSOUND_GetDMAPos;\n\tsc->Restore\t\t= DSOUND_Restore;\n\n#if 1//_MSC_VER > 1200\n#ifdef _IKsPropertySet_\n\t//attempt at eax support\n\tif (usereverb == 2)\n\t{\n\t\tint r;\n\t\tDWORD support;\n\n\t\tif (SUCCEEDED(IDirectSoundBuffer_QueryInterface(dh->pDSBuf, &IID_IKsPropertySet, (void*)&dh->EaxKsPropertiesSet)))\n\t\t{\n\t\t\tr = IKsPropertySet_QuerySupport(dh->EaxKsPropertiesSet, &DSPROPSETID_EAX20_LISTENERPROPERTIES, DSPROPERTY_EAXLISTENER_ALLPARAMETERS, &support);\n\t\t\tif(!SUCCEEDED(r) || (support&(KSPROPERTY_SUPPORT_GET|KSPROPERTY_SUPPORT_SET))\n\t\t\t\t\t!= (KSPROPERTY_SUPPORT_GET|KSPROPERTY_SUPPORT_SET))\n\t\t\t{\n\t\t\t\tIKsPropertySet_Release(dh->EaxKsPropertiesSet);\n\t\t\t\tdh->EaxKsPropertiesSet = NULL;\n\t\t\t\tCon_SafePrintf (\"EAX 2 not supported\\n\");\n\t\t\t\t//otherwise successful. It can be used for normal sound anyway.\n\t\t\t}\n\t\t\telse\n\t\t\t\tusereverb = 0; //worked. EAX is fully inited.\n\t\t}\n\t\telse\n\t\t{\n\t\t\tCon_DPrintf (\"Couldn't get extended properties\\n\");\n\t\t\tdh->EaxKsPropertiesSet = NULL;\n\t\t}\n\t}\n#endif\n#endif\n\n\tif (usereverb)\n\t\tCon_SafePrintf (\"Couldn't enable environmental reverb effects\\n\");\n\n\treturn true;\n}\n\n\n\n\n#ifdef MULTITHREAD\nstatic int DSOUND_Thread(void *arg)\n{\n\tsoundcardinfo_t *sc = arg;\n\tvoid *cond = sc->handle;\n\tsc->handle = NULL;\n\n\t//once creating the thread, the main thread will wait for us to signal that we have inited the dsound device.\n\tif (!DSOUND_InitCard_Internal(sc, sc->name))\n\t\tsc->selfpainting = false;\n\n\t//wake up the main thread.\n\tSys_ConditionSignal(cond);\n\n\twhile(sc->selfpainting)\n\t{\n\t\tS_MixerThread(sc);\n\t\t/* Quote:\n\t\tOn NT (Win2K and XP) the cursors in SW buffers (and HW buffers on some devices) move in 10ms increments, so calling GetCurrentPosition() every 10ms is ideal.\n\t\tCalling it more often than every 5ms will cause some perf degradation.\n\t\t*/\n\t\tSleep(9);\n\t}\n\n\t//we created the device, we need to kill it.\n\tDSOUND_Shutdown_Internal(sc);\n\treturn 0;\n}\n#endif\n\nstatic qboolean QDECL DSOUND_InitCard (soundcardinfo_t *sc, const char *device)\n{\n\tif (COM_CheckParm(\"-wavonly\"))\n\t\treturn false;\n\n\tQ_strncpyz(sc->name, device?device:\"\", sizeof(sc->name));\n\n#ifdef MULTITHREAD\n\tif (snd_mixerthread.ival)\n\t{\n\t\tvoid *cond;\n\t\tsc->selfpainting = true;\n\t\tsc->handle = cond = Sys_CreateConditional();\n\t\tSys_LockConditional(cond);\n\t\tsc->thread = Sys_CreateThread(\"dsoundmixer\", DSOUND_Thread, sc, THREADP_HIGHEST, 0);\n\t\tif (!sc->thread)\n\t\t{\n\t\t\tCon_SafePrintf (\"Unable to create sound mixing thread\\n\");\n\t\t\treturn false;\n\t\t}\n\n\t\t//wait for the thread to finish (along with all its error con printfs etc\n\t\tif (!Sys_ConditionWait(cond))\n\t\t\tCon_SafePrintf (\"Looks like the sound thread isn't starting up\\n\");\n\t\tSys_UnlockConditional(cond);\n\t\tSys_DestroyConditional(cond);\n\n\t\tif (!sc->selfpainting)\n\t\t{\n\t\t\tSys_WaitOnThread(sc->thread);\n\t\t\tsc->thread = NULL;\n\t\t\treturn false;\n\t\t}\n\t\treturn true;\n\t}\n\telse\n#endif\n\t\treturn DSOUND_InitCard_Internal(sc, sc->name);\n}\n\n#define SDRVNAME \"DSound\"\nstatic BOOL (CALLBACK  DSound_EnumCallback)(GUID FAR *guid, LPCSTR str1, LPCSTR str2, LPVOID parm)\n{\n\tchar guidbuf[128];\n\twchar_t mssuck[128];\n\tvoid (QDECL *callback) (const char *drivername, const char *devicecode, const char *readablename) = parm;\n\tif (guid == NULL)\t//we don't care about the (dupe) default device\n\t\treturn TRUE;\n\n\tStringFromGUID2(guid, mssuck, sizeof(mssuck)/sizeof(mssuck[0]));\n\twcstombs(guidbuf, mssuck, sizeof(guidbuf));\n\tcallback(SDRVNAME, guidbuf, va(\"DS: %s\", str1));\n\treturn TRUE;\n}\nstatic qboolean QDECL DSOUND_Enumerate(void (QDECL *cb) (const char *drivername, const char *devicecode, const char *readablename))\n{\n\tif (!DSOUND_InitOutputLibrary())\n\t\treturn false;\n\tif (pDirectSoundEnumerate)\n\t{\n\t\tpDirectSoundEnumerate(&DSound_EnumCallback, cb);\n\t\treturn true;\n\t}\n\treturn false;\n}\n\nsounddriver_t DSOUND_Output =\n{\n\tSDRVNAME,\n\tDSOUND_InitCard,\n\tDSOUND_Enumerate\n};\n\n#endif\n\n\n\n\n\n\n\n\n\n\n\n\n#if defined(VOICECHAT) && defined(AVAIL_DSOUND)\n\n\ntypedef struct\n{\n\tLPDIRECTSOUNDCAPTURE DSCapture;\n\tLPDIRECTSOUNDCAPTUREBUFFER DSCaptureBuffer;\n\tlong lastreadpos;\n} dsndcapture_t;\nstatic const long bufferbytes = 1024*1024;\n\nstatic const long inputwidth = 2;\n\nstatic BOOL CALLBACK dsound_capture_enumerate_ds(LPGUID lpGuid, LPCSTR lpcstrDescription, LPCSTR lpcstrModule, LPVOID lpContext)\n{\n\tchar guidbuf[128];\n\twchar_t mssuck[128];\n\tvoid (QDECL *callback) (const char *drivername, const char *devicecode, const char *readablename) = lpContext;\n\n\tif (lpGuid == NULL)\t//we don't care about the (dupe) default device\n\t\treturn TRUE;\n\n\tStringFromGUID2(lpGuid, mssuck, sizeof(mssuck)/sizeof(mssuck[0]));\n\twcstombs(guidbuf, mssuck, sizeof(guidbuf));\n\tcallback(SDRVNAME, guidbuf, va(\"DS: %s\", lpcstrDescription));\n\treturn TRUE;\n}\n\nstatic qboolean QDECL DSOUND_Capture_Enumerate (void (QDECL *callback) (const char *drivername, const char *devicecode, const char *readablename))\n{\n\tif (!pDirectSoundCaptureEnumerate)\n\t{\n\t\t/*make sure its loaded*/\n\t\tif (!hInstDS)\n\t\t\thInstDS = LoadLibrary(\"dsound.dll\");\n\t\tif (hInstDS)\n\t\t\tpDirectSoundCaptureEnumerate = (void *)GetProcAddress(hInstDS,\"DirectSoundCaptureEnumerateA\");\n\n\t\tif (!pDirectSoundCaptureEnumerate)\n\t\t\treturn false;\n\t}\n\n\tif (!FAILED(pDirectSoundCaptureEnumerate(dsound_capture_enumerate_ds, callback)))\n\t\treturn true;\n\treturn false;\n}\n\nstatic void *QDECL DSOUND_Capture_Init (int rate, const char *device)\n{\n\tdsndcapture_t *result;\n\tDSCBUFFERDESC bufdesc;\n\n\tWAVEFORMATEX  wfxFormat;\n\tGUID *dsguid, guid;\n\n\twfxFormat.wFormatTag = WAVE_FORMAT_PCM;\n\twfxFormat.nChannels = 1;\n\twfxFormat.nSamplesPerSec = rate;\n\twfxFormat.wBitsPerSample = 8*inputwidth;\n\twfxFormat.nBlockAlign = wfxFormat.nChannels * (wfxFormat.wBitsPerSample / 8);\n\twfxFormat.nAvgBytesPerSec = wfxFormat.nSamplesPerSec * wfxFormat.nBlockAlign;\n\twfxFormat.cbSize = 0;\n\n\tbufdesc.dwSize = sizeof(bufdesc);\n\tbufdesc.dwBufferBytes = bufferbytes;\n\tbufdesc.dwFlags = 0;\n\tbufdesc.dwReserved = 0;\n\tbufdesc.lpwfxFormat = &wfxFormat;\n\n\tif (device && *device)\n\t{\n\t\twchar_t mssuck[128];\n\t\tmbstowcs(mssuck, device, sizeof(mssuck)/sizeof(mssuck[0])-1);\n\t\tCLSIDFromString(mssuck, &guid);\n\t\tdsguid = &guid;\n\t}\n\telse\n\t{\n\t\tmemset(&guid, 0, sizeof(GUID));\n\t\tdsguid = NULL;\n\t}\n\n\t/*probably already inited*/\n\tif (!hInstDS)\n\t{\n\t\thInstDS = LoadLibrary(\"dsound.dll\");\n\n\t\tif (hInstDS == NULL)\n\t\t{\n\t\t\tCon_SafePrintf (\"Couldn't load dsound.dll\\n\");\n\t\t\treturn NULL;\n\t\t}\n\t}\n\t/*global pointer, used only in this function*/\n\tif (!pDirectSoundCaptureCreate)\n\t{\n\t\tpDirectSoundCaptureCreate = (void *)GetProcAddress(hInstDS,\"DirectSoundCaptureCreate\");\n\n\t\tif (!pDirectSoundCaptureCreate)\n\t\t{\n\t\t\tCon_SafePrintf (\"Couldn't get DS proc addr\\n\");\n\t\t\treturn NULL;\n\t\t}\n\t}\n\n\tresult = Z_Malloc(sizeof(*result));\n\tif (!FAILED(pDirectSoundCaptureCreate(dsguid, &result->DSCapture, NULL)))\n\t{\n\t\tif (!FAILED(IDirectSoundCapture_CreateCaptureBuffer(result->DSCapture, &bufdesc, &result->DSCaptureBuffer, NULL)))\n\t\t{\n\t\t\treturn result;\n\t\t}\n\t\tIDirectSoundCapture_Release(result->DSCapture);\n\t\tCon_SafePrintf (\"Couldn't create a capture buffer\\n\");\n\t}\n\tZ_Free(result);\n\treturn NULL;\n}\n\nstatic void QDECL DSOUND_Capture_Start(void *ctx)\n{\n\tDWORD capturePos;\n\tdsndcapture_t *c = ctx;\n\tIDirectSoundCaptureBuffer_Start(c->DSCaptureBuffer, DSBPLAY_LOOPING);\n\n\tc->lastreadpos = 0;\n\tIDirectSoundCaptureBuffer_GetCurrentPosition(c->DSCaptureBuffer, &capturePos, &c->lastreadpos);\n}\n\nstatic void QDECL DSOUND_Capture_Stop(void *ctx)\n{\n\tdsndcapture_t *c = ctx;\n\tIDirectSoundCaptureBuffer_Stop(c->DSCaptureBuffer);\n}\n\nstatic void QDECL DSOUND_Capture_Shutdown(void *ctx)\n{\n\tdsndcapture_t *c = ctx;\n\tif (c->DSCaptureBuffer)\n\t{\n\t\tIDirectSoundCaptureBuffer_Stop(c->DSCaptureBuffer);\n\t\tIDirectSoundCaptureBuffer_Release(c->DSCaptureBuffer);\n\t}\n\tif (c->DSCapture)\n\t{\n\t\tIDirectSoundCapture_Release(c->DSCapture);\n\t}\n\tZ_Free(ctx);\n}\n\n/*minsamples is a hint*/\nstatic unsigned int QDECL DSOUND_Capture_Update(void *ctx, unsigned char *buffer, unsigned int minbytes, unsigned int maxbytes)\n{\n\tdsndcapture_t *c = ctx;\n\tHRESULT hr;\n\tLPBYTE lpbuf1 = NULL;\n\tLPBYTE lpbuf2 = NULL;\n\tDWORD dwsize1 = 0;\n\tDWORD dwsize2 = 0;\n\n\tDWORD capturePos;\n\tDWORD readPos;\n\tlong  filled;\n\n// Query to see how much data is in buffer.\n\thr = IDirectSoundCaptureBuffer_GetCurrentPosition(c->DSCaptureBuffer, &capturePos, &readPos);\n\tif (hr != DS_OK)\n\t{\n\t\treturn 0;\n\t}\n\tfilled = readPos - c->lastreadpos;\n\tif (filled < 0)\n\t\tfilled += bufferbytes; // unwrap offset\n\n\tif (filled > maxbytes)\t//figure out how much we need to empty it by, and if that's enough to be worthwhile.\n\t\tfilled = maxbytes;\n\telse if (filled < minbytes)\n\t\treturn 0;\n\n//\tfilled /= inputwidth;\n//\tfilled *= inputwidth;\n\n\t// Lock free space in the DS\n\thr = IDirectSoundCaptureBuffer_Lock(c->DSCaptureBuffer, c->lastreadpos, filled, (void **) &lpbuf1, &dwsize1, (void **) &lpbuf2, &dwsize2, 0);\n\tif (hr == DS_OK)\n\t{\n\t\t// Copy from DS to the buffer\n\t\tmemcpy(buffer, lpbuf1, dwsize1);\n\t\tif(lpbuf2 != NULL)\n\t\t{\n\t\t\tmemcpy(buffer+dwsize1, lpbuf2, dwsize2);\n\t\t}\n\t\t// Update our buffer offset and unlock sound buffer\n \t\tc->lastreadpos = (c->lastreadpos + dwsize1 + dwsize2) % bufferbytes;\n\t\tIDirectSoundCaptureBuffer_Unlock(c->DSCaptureBuffer, lpbuf1, dwsize1, lpbuf2, dwsize2);\n\t}\n\telse\n\t{\n\t\treturn 0;\n\t}\n\treturn filled;\n}\nsnd_capture_driver_t DSOUND_Capture =\n{\n\t1,\n\tSDRVNAME,\n\tDSOUND_Capture_Enumerate,\n\tDSOUND_Capture_Init,\n\tDSOUND_Capture_Start,\n\tDSOUND_Capture_Update,\n\tDSOUND_Capture_Stop,\n\tDSOUND_Capture_Shutdown\n};\n#endif\n"
  },
  {
    "path": "engine/client/snd_dma.c",
    "content": "/*\nCopyright (C) 1996-1997 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n*/\n// snd_dma.c -- main control for any streaming sound output devices\n\n#include \"quakedef.h\"\n\n#ifdef __GNUC__\n\t#define fte_weakstruct __attribute__((weak))\n#else\n\t//msvc's uninitialised symbols are always weak, so this is fine.\n\t#define fte_weakstruct\n#endif\n\n#ifdef CSQC_DAT\n//for sounds following csqc ents\n\t#include \"pr_common.h\"\n\textern world_t csqc_world;\n#endif\n\nstatic void S_Play_f(void);\nstatic void S_SoundList_f(void);\n#ifdef HAVE_MIXER\nstatic void S_Update_(soundcardinfo_t *sc);\n#endif\nvoid S_StopAllSounds(qboolean clear);\nstatic void S_StopAllSounds_f (void);\n\nstatic void S_UpdateCard(soundcardinfo_t *sc);\nstatic void S_ClearBuffer (soundcardinfo_t *sc);\n\n// =======================================================================\n// Internal sound data & structures\n// =======================================================================\n\nsoundcardinfo_t *sndcardinfo;\t//the master card.\n\nint\t\t\t\tsnd_blocked = 0;\nstatic qboolean\tsnd_ambient = 1;\nqboolean\t\tsnd_initialized = false;\nint\t\t\t\tsnd_speed;\nfloat\t\t\tvoicevolumemod = 1;\n\nstatic struct listener_s\n{\n\tint\t\tentnum;\n\tvec3_t\torigin;\n\tvec3_t\tvelocity;\n\tvec3_t\tforward;\n\tvec3_t\tright;\n\tvec3_t\tup;\n} listener[MAX_SPLITS];\ncvar_t snd_nominaldistance\t\t= CVARAFD(\"s_nominaldistance\", \"1000\", \"snd_soundradius\", CVAR_CHEAT, \"This cvar defines how far an attenuation=1 sound can be heard.\");\n\n#define\tMAX_SFX\t\t8192\nsfx_t\t\t*known_sfx;\t\t// hunk allocated [MAX_SFX]\nint\t\t\tnum_sfx;\n\nsfx_t\t\t*ambient_sfx[NUM_AMBIENTS];\n\n//int \t\tdesired_speed = 44100;\nint \t\tdesired_bits = 16;\n\nint sound_started=0;\n\ncvar_t mastervolume\t\t\t\t= CVARFD(\t\"mastervolume\", \"1\", CVAR_ARCHIVE, \"Additional multiplier for all other sounds.\");\ncvar_t bgmvolume\t\t\t\t= CVARAFD(\t\"musicvolume\", \"0.3\", \"bgmvolume\", CVAR_ARCHIVE,\n\t\t\t\t\t\t\t\t\t\t\t\"Volume level for background music.\");\ncvar_t volume\t\t\t\t\t= CVARAFD(\t\"volume\", \"0.7\", /*q3*/\"s_volume\",CVAR_ARCHIVE,\n\t\t\t\t\t\t\t\t\t\t\t\"Volume level for game sounds (does not affect music, voice, or cinematics).\");\n\ncvar_t nosound\t\t\t\t\t= CVARFD(\t\"nosound\", \"0\", CVAR_ARCHIVE,\n\t\t\t\t\t\t\t\t\t\t\t\"Disable all sound from the engine. Cannot be overriden by configs or anything if set via the -nosound commandline argument.\");\ncvar_t snd_precache\t\t\t\t= CVARAF(\t\"s_precache\", \"1\",\n\t\t\t\t\t\t\t\t\t\t\t\"precache\", 0);\ncvar_t snd_loadas8bit\t\t\t= CVARAFD(\t\"s_loadas8bit\", \"0\",\n\t\t\t\t\t\t\t\t\t\t\t\"loadas8bit\", CVAR_ARCHIVE,\n\t\t\t\t\t\t\t\t\t\t\t\"Downsample sounds on load as lower quality 8-bit sound, to save memory.\");\ncvar_t snd_loadasstereo\t\t\t= CVARD(\t\"snd_loadasstereo\", \"0\",\n\t\t\t\t\t\t\t\t\t\t\t\"Force mono sounds to load as if stereo ones, to waste memory. Not normally useful.\");\ncvar_t ambient_level\t\t\t= CVARAFD(\t\"s_ambientlevel\", \"0.3\",\n\t\t\t\t\t\t\t\t\t\t\t\"ambient_level\", CVAR_ARCHIVE,\n\t\t\t\t\t\t\t\t\t\t\t\"This controls the volume levels of automatic area-based sounds (like water or sky), and is quite annoying. If you're playing deathmatch you'll definitely want this OFF.\");\ncvar_t ambient_fade\t\t\t\t= CVARAF(\t\"s_ambientfade\", \"100\",\n\t\t\t\t\t\t\t\t\t\t\t\"ambient_fade\", CVAR_ARCHIVE);\ncvar_t snd_noextraupdate\t\t= CVARAF(\t\"s_noextraupdate\", \"0\",\n\t\t\t\t\t\t\t\t\t\t\t\"snd_noextraupdate\", 0);\ncvar_t snd_show\t\t\t\t\t= CVARAF(\t\"s_show\", \"0\",\n\t\t\t\t\t\t\t\t\t\t\t\"snd_show\", 0);\n#ifdef __DJGPP__\n#define DEFAULT_SND_KHZ \"11\"\n#else\n//fixme: are android devices more likely to use 44.1khz?\n#define DEFAULT_SND_KHZ \"48\"\t//most modern systems should go with 48khz audio (dvd quality). various hardware codecs support nothing else.\n#endif\ncvar_t snd_khz\t\t\t\t\t= CVARAFD(\t\"s_khz\", DEFAULT_SND_KHZ,\n\t\t\t\t\t\t\t\t\t\t\t\"snd_khz\", CVAR_ARCHIVE, \"Sound speed, in kilohertz. Common values are 11, 22, 44, 48. Values above 1000 are explicitly in hertz.\");\ncvar_t\tsnd_inactive\t\t\t= CVARAFD(\t\"s_inactive\", \"1\",\n\t\t\t\t\t\t\t\t\t\t\t\"snd_inactive\", CVAR_ARCHIVE,\n\t\t\t\t\t\t\t\t\t\t\t\"Play sound while application is inactive (ie: tabbed out). Needs a snd_restart if changed.\"\n\t\t\t\t\t\t\t\t\t\t\t);\t//set if you want sound even when tabbed out.\ncvar_t _snd_mixahead\t\t\t= CVARAFD(\t\"s_mixahead\", \"0.1\",\n\t\t\t\t\t\t\t\t\t\t\t\"_snd_mixahead\", CVAR_ARCHIVE, \"Specifies how many seconds to prebuffer audio. Lower values give less latency, but might result in crackling. Different hardware/drivers have different tolerances, and this setting may be ignored completely where drivers are expected to provide their own tolerances.\");\ncvar_t snd_leftisright\t\t\t= CVARAF(\t\"s_swapstereo\", \"0\",\n\t\t\t\t\t\t\t\t\t\t\t\"snd_leftisright\", CVAR_ARCHIVE);\ncvar_t snd_eax\t\t\t\t\t= CVARAF(\t\"s_eax\", \"0\",\n\t\t\t\t\t\t\t\t\t\t\t\"snd_eax\", 0);\ncvar_t snd_speakers\t\t\t\t= CVARAFD(\t\"s_numspeakers\", \"2\",\n\t\t\t\t\t\t\t\t\t\t\t\"snd_numspeakers\", CVAR_ARCHIVE, \"Number of hardware audio channels to use. \"FULLENGINENAME\" supports up to 6.\");\ncvar_t snd_buffersize\t\t\t= CVARAF(\t\"s_buffersize\", \"0\",\n\t\t\t\t\t\t\t\t\t\t\t\"snd_buffersize\", 0);\n#if defined(MIXER_F32) && defined(FTE_TARGET_WEB)\t//let emscripten have float audio\ncvar_t snd_samplebits\t\t\t= CVARAF(\t\"s_bits\", \"32\",\n\t\t\t\t\t\t\t\t\t\t\t\"snd_samplebits\", CVAR_ARCHIVE);\n#else\ncvar_t snd_samplebits\t\t\t= CVARAF(\t\"s_bits\", \"16\",\n\t\t\t\t\t\t\t\t\t\t\t\"snd_samplebits\", CVAR_ARCHIVE);\n#endif\ncvar_t snd_playersoundvolume\t= CVARAFD(\t\"s_localvolume\", \"1\",\n\t\t\t\t\t\t\t\t\t\t\t\"snd_localvolume\", CVAR_ARCHIVE,\n\t\t\t\t\t\t\t\t\t\t\t\"Sound level for sounds local or originating from the player such as firing and pain sounds.\");\t//sugested by crunch\ncvar_t snd_doppler\t\t\t\t= CVARAFD(\t\"s_doppler\", \"0\",\n\t\t\t\t\t\t\t\t\t\t\t\"snd_doppler\", CVAR_ARCHIVE,\n\t\t\t\t\t\t\t\t\t\t\t\"Enables doppler, with a multiplier for the scale.\");\ncvar_t snd_doppler_min\t\t\t= CVARAFD(\t\"s_doppler_min\", \"0.5\",\n\t\t\t\t\t\t\t\t\t\t\t\"snd_doppler_min\", CVAR_ARCHIVE,\n\t\t\t\t\t\t\t\t\t\t\t\"Slowest allowed doppler scale.\");\ncvar_t snd_doppler_max\t\t\t= CVARAFD(\t\"s_doppler_max\", \"2\",\n\t\t\t\t\t\t\t\t\t\t\t\"snd_doppler_max\", CVAR_ARCHIVE,\n\t\t\t\t\t\t\t\t\t\t\t\"Highest allowed doppler scale, to avoid things getting too weird.\");\ncvar_t snd_playbackrate\t\t\t= CVARFD(\t\"snd_playbackrate\", \"1\", CVAR_CHEAT, \"Debugging cvar that changes the playback rate of all new sounds.\");\ncvar_t snd_ignoregamespeed\t\t= CVARFD(\t\"snd_ignoregamespeed\", \"0\", 0, \"When set, allows sounds to desynchronise with game time or demo speeds.\");\n\ncvar_t snd_ignorecueloops\t\t= CVARD(\t\"snd_ignorecueloops\", \"0\", \"Ignores cue commands in wav files, for q3 compat.\");\ncvar_t snd_linearresample\t\t= CVARAF(\t\"s_linearresample\", \"1\",\n\t\t\t\t\t\t\t\t\t\t\t\"snd_linearresample\", 0);\ncvar_t snd_linearresample_stream = CVARAF(\t\"s_linearresample_stream\", \"0\",\n\t\t\t\t\t\t\t\t\t\t\t\"snd_linearresample_stream\", 0);\n\ncvar_t snd_mixerthread\t\t\t= CVARAD(\t\"s_mixerthread\", \"1\",\n\t\t\t\t\t\t\t\t\t\t\t\"snd_mixerthread\", \"When enabled sound mixing will be run on a separate thread. Currently supported only by directsound. Other drivers may unconditionally thread audio. Set to 0 only if you have issues.\");\ncvar_t snd_device\t\t\t\t= CVARAFD(\t\"s_device\", \"\",\n\t\t\t\t\t\t\t\t\t\t  \"snd_device\", CVAR_ARCHIVE, \"This is the sound device(s) to use, in the form of driver:device.\\nIf desired, multiple devices can be listed in space-seperated (quoted) tokens. _s_device_opts contains any enumerated options.\\nIn all seriousness, use the menu to set this if you wish to keep your hair.\");\ncvar_t snd_device_opts\t\t\t= CVARFD(\t\"_s_device_opts\", \"\", CVAR_NOSET|CVAR_NOSAVE, \"The possible audio output devices, in \\\"value\\\" \\\"description\\\" pairs, for gamecode to read.\");\n\n#ifdef VOICECHAT\nstatic void QDECL S_Voip_Play_Callback(cvar_t *var, char *oldval);\ncvar_t snd_voip_capturedevice\t= CVARF(\"cl_voip_capturedevice\", \"\", CVAR_ARCHIVE);\ncvar_t snd_voip_capturedevice_opts\t= CVARFD(\"_cl_voip_capturedevice_opts\", \"\", CVAR_NOSET|CVAR_NOSAVE, \"The possible audio capture devices, in \\\"value\\\" \\\"description\\\" pairs, for gamecode to read.\");\nint voipbutton;\t//+voip, no longer part of cl_voip_send to avoid it getting saved\ncvar_t snd_voip_send\t\t\t= CVARFD(\"cl_voip_send\", \"0\", CVAR_ARCHIVE|CVAR_NOTFROMSERVER, \"Sends voice-over-ip data to the server whenever it is set.\\n0: only send voice if +voip is pressed.\\n1: voice activation.\\n2: constantly send.\\n+4: Do not send to game, only to rtp sessions.\");\ncvar_t snd_voip_test\t\t\t= CVARD(\"cl_voip_test\", \"0\", \"If 1, enables you to hear your own voice directly, bypassing the server and thus without networking latency, but is fine for checking audio levels. Note that sv_voip_echo can be set if you want to include latency and packetloss considerations, but setting that cvar requires server admin access and is thus much harder to use.\");\ncvar_t snd_voip_vad_threshhold\t= CVARFD(\"cl_voip_vad_threshhold\", \"15\", CVAR_ARCHIVE, \"This is the threshhold for voice-activation-detection when sending voip data\");\ncvar_t snd_voip_vad_delay\t\t= CVARD(\"cl_voip_vad_delay\", \"0.3\", \"Keeps sending voice data for this many seconds after voice activation would normally stop\");\ncvar_t snd_voip_capturingvol\t= CVARAFD(\"cl_voip_capturingvol\", \"0.5\", NULL, CVAR_ARCHIVE, \"Volume multiplier applied while capturing, to avoid your audio from being heard by others. Does not affect game volume when others speak (minimum of cl_voip_capturingvol and cl_voip_ducking is used).\");\ncvar_t snd_voip_showmeter\t\t= CVARAFD(\"cl_voip_showmeter\", \"1\", NULL, CVAR_ARCHIVE, \"Shows your speech volume above the standard hud. 0=hide, 1=show when transmitting, 2=ignore voice-activation disable\");\n\ncvar_t snd_voip_play\t\t\t= CVARAFCD(\"cl_voip_play\", \"1\", NULL, CVAR_ARCHIVE, S_Voip_Play_Callback, \"Enables voip playback. Value is a volume scaler.\");\ncvar_t snd_voip_ducking\t\t\t= CVARAFD(\"cl_voip_ducking\", \"0.5\", NULL, CVAR_ARCHIVE, \"Scales game audio by this much when someone is talking to you. Does not affect your speaker volume when you speak (minimum of cl_voip_capturingvol and cl_voip_ducking is used).\");\ncvar_t snd_voip_micamp\t\t\t= CVARAFD(\"cl_voip_micamp\", \"2\", NULL, CVAR_ARCHIVE, \"Amplifies your microphone when using voip.\");\ncvar_t snd_voip_codec\t\t\t= CVARAFD(\"cl_voip_codec\", \"\", NULL, CVAR_ARCHIVE, \"0: speex(@11khz). 1: raw. 2: opus. 3: speex(@8khz). 4: speex(@16). 5:speex(@32). 6: pcma. 7: pcmu.\");\n#ifdef HAVE_SPEEX\ncvar_t snd_voip_noisefilter\t\t= CVARAFD(\"cl_voip_noisefilter\", \"1\", NULL, CVAR_ARCHIVE, \"Enable the use of the noise cancelation filter.\");\ncvar_t snd_voip_autogain\t\t= CVARAFD(\"cl_voip_autogain\", \"0\", NULL, CVAR_ARCHIVE, \"Attempts to normalize your voice levels to a standard level. Useful for lazy people, but interferes with voice activation levels.\");\n#endif\ncvar_t snd_voip_bitrate\t\t\t= CVARAFD(\"cl_voip_bitrate\", \"3000\", NULL, CVAR_ARCHIVE, \"For codecs with non-specific bitrates, this specifies the target bitrate to use.\");\n#endif\n\nextern vfsfile_t *rawwritefile;\n#ifdef MULTITHREAD\nvoid *mixermutex;\nvoid S_LockMixer(void)\n{\n\tSys_LockMutex(mixermutex);\n}\nvoid S_UnlockMixer(void)\n{\n\tSys_UnlockMutex(mixermutex);\n}\n#else\nvoid S_LockMixer(void)\n{\n}\nvoid S_UnlockMixer(void)\n{\n}\n#endif\n\nvoid S_AmbientOff (void)\n{\n\tsnd_ambient = false;\n}\n\n\nvoid S_AmbientOn (void)\n{\n\tsnd_ambient = true;\n}\n\nqboolean S_HaveOutput(void)\n{\n\treturn sound_started && sndcardinfo;\n}\n\n\nvoid S_SoundInfo_f(void)\n{\n\tint i, j;\n\tint active, known;\n\tsoundcardinfo_t *sc;\n\tif (!sound_started)\n\t{\n\t\tCon_Printf (\"sound system not started\\n\");\n\t\treturn;\n\t}\n\n\tif (!sndcardinfo)\n\t{\n\t\tCon_Printf (\"No sound cards\\n\");\n\t\treturn;\n\t}\n\tfor (sc = sndcardinfo; sc; sc = sc->next)\n\t{\n\t\tCon_Printf(\"Audio Device: %s\\n\", sc->name);\n\t\tCon_Printf(\" %d channels, %gkhz, %d bit audio%s\\n\", sc->sn.numchannels, sc->sn.speed/1000.0, sc->sn.samplebytes*8, sc->selfpainting?\", threaded\":\"\");\n\t\tCon_Printf(\" %d samples in buffer\\n\", sc->sn.samples);\n\t\tfor (i = 0, active = 0, known = 0; i < sc->total_chans; i++)\n\t\t{\n\t\t\tif (sc->channel[i].sfx)\n\t\t\t{\n\t\t\t\tknown++;\n\t\t\t\tfor (j = 0; j < MAXSOUNDCHANNELS; j++)\n\t\t\t\t{\n\t\t\t\t\tif (sc->channel[i].vol[j])\n\t\t\t\t\t{\n\t\t\t\t\t\tactive++;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (j<MAXSOUNDCHANNELS)\n\t\t\t\t\tCon_Printf(\" %s (%i %i, %g %g %g, active)\\n\", sc->channel[i].sfx->name, sc->channel[i].entnum, sc->channel[i].entchannel, sc->channel[i].origin[0], sc->channel[i].origin[1], sc->channel[i].origin[2]);\n\t\t\t\telse\n\t\t\t\t\tCon_DPrintf(\" %s (%i %i, %g %g %g, inactive)\\n\", sc->channel[i].sfx->name, sc->channel[i].entnum, sc->channel[i].entchannel, sc->channel[i].origin[0], sc->channel[i].origin[1], sc->channel[i].origin[2]);\n\t\t\t}\n\t\t}\n\t\tCon_Printf(\" %d/%d/%\"PRIiSIZE\"/%\"PRIiSIZE\" active/known/highest/max\\n\", active, known, sc->total_chans, sc->max_chans);\n\t\tfor (i = 0; i < sc->sn.numchannels; i++)\n\t\t{\n\t\t\tCon_Printf(\" chan %i: fwd:%-4g rt:%-4g up:%-4g dist:%-4g\\n\", i, sc->speakerdir[i][0], sc->speakerdir[i][1], sc->speakerdir[i][2], sc->dist[i]);\n\t\t}\n\t}\n}\n\n#ifdef VOICECHAT\n#ifdef SPEEX_STATIC\n#include <speex/speex.h>\n#include <speex/speex_preprocess.h>\n#else\ntypedef struct {int stuff[15];} SpeexBits;\ntypedef struct SpeexMode SpeexMode;\ntypedef struct SpeexPreprocessState SpeexPreprocessState;\ntypedef qint16_t spx_int16_t;\n\n#define SPEEX_MODEID_NB 0\n#define SPEEX_MODEID_WB 1\n#define SPEEX_MODEID_UWB 2\n#define SPEEX_GET_FRAME_SIZE 3\n\n#define SPEEX_SET_SAMPLING_RATE 24\n#define SPEEX_GET_SAMPLING_RATE 25\n\n\n#define SPEEX_PREPROCESS_SET_DENOISE 0\n#define SPEEX_PREPROCESS_SET_AGC 2\n#define SPEEX_PREPROCESS_SET_AGC_MAX_GAIN 30\n#endif\n\nenum\n{\n\tVOIP_SPEEX_OLD\t= 0,\t//original supported codec (with needless padding and at the wrong rate to keep quake implementations easy)\n\tVOIP_RAW16\t\t= 1,\t//support is not recommended.\n\tVOIP_OPUS\t\t= 2,\t//supposed to be better than speex.\n\tVOIP_SPEEX_NARROW = 3,\t//narrowband speex. packed data.\n\tVOIP_SPEEX_WIDE = 4,\t//wideband speex. packed data.\n\tVOIP_SPEEX_ULTRAWIDE = 5,//wideband speex. packed data.\n\tVOIP_PCMA\t\t= 6,\t//G711 is kinda shit, encoding audio at 8khz with funny truncation for 13bit to 8bit\n\tVOIP_PCMU\t\t= 7,\t//ulaw version of g711 (instead of alaw)\n\n\tVOIP_INVALID = 16\t//not currently generating audio.\n};\n#if defined(HAVE_LEGACY) && defined(HAVE_OPUS) && defined(HAVE_SPEEX)\n\t#define VOIP_DEFAULT_CODEC ((cls.protocol==CP_QUAKEWORLD && !(cls.fteprotocolextensions2&PEXT2_REPLACEMENTDELTAS))?VOIP_SPEEX_OLD:VOIP_OPUS)\t//opus is preferred, but ezquake is still common and only supports my first attempt at voice compression so favour that for mvdsv servers.\n#elif defined(HAVE_OPUS)\n\t#define VOIP_DEFAULT_CODEC VOIP_OPUS\n#elif defined(HAVE_SPEEX)\n\t#define VOIP_DEFAULT_CODEC VOIP_SPEEX_OLD\n#else\n\t#define VOIP_DEFAULT_CODEC VOIP_PCMA\n#endif\n\nstatic struct\n{\n#ifdef HAVE_SPEEX\n\tstruct\n\t{\n\t\tqboolean inited;\n\t\tqboolean loaded;\n\t\tdllhandle_t *speexlib;\n\n\t\tSpeexBits encbits;\n\t\tSpeexBits decbits[MAX_CLIENTS];\n\n\t\tconst SpeexMode *modenb;\n\t\tconst SpeexMode *modewb;\n\t\tconst SpeexMode *modeuwb;\n\t} speex;\n\n\tstruct\n\t{\n\t\tqboolean inited;\n\t\tqboolean loaded;\n\t\tdllhandle_t *speexdsplib;\n\n\t\tSpeexPreprocessState *preproc;\t//filter out noise\n\t\tint curframesize;\n\t\tint cursamplerate;\n\t} speexdsp;\n#endif\n\n#ifdef HAVE_OPUS\n\tstruct\n\t{\n\t\tqboolean inited;\n\t\tqboolean loaded;\n\t\tdllhandle_t *opuslib;\n\t} opus;\n#endif\n\n\tunsigned char enccodec;\n\tvoid *encoder;\n\tunsigned int encframesize;\n\tunsigned int encsamplerate;\n\n\tvoid *decoder[MAX_CLIENTS];\n\tfloat declevel[MAX_CLIENTS];\n\tunsigned char deccodec[MAX_CLIENTS];\n\tunsigned char decseq[MAX_CLIENTS];\t/*sender's sequence, to detect+cover minor packetloss*/\n\tunsigned char decgen[MAX_CLIENTS];\t/*last generation. if it changes, we flush speex to reset packet loss*/\n\tunsigned int decsamplerate[MAX_CLIENTS];\n\tunsigned int decframesize[MAX_CLIENTS];\n\tfloat lastspoke[MAX_CLIENTS];\t/*time when they're no longer considered talking. if future, they're talking (timeout avoids flickering, and harder to troll with fake-tourettes when noone is looking)*/\n\tfloat lastspoke_any;\n\n\tunsigned char capturebuf[32768]; /*pending data*/\n\tunsigned int capturepos;/*amount of pending data*/\n\tunsigned int encsequence;/*the outgoing sequence count*/\n\tunsigned int enctimestamp;/*for rtp streaming*/\n\tunsigned int generation;/*incremented whenever capture is restarted*/\n\tqboolean wantsend;\t/*set if we're capturing data to send*/\n\tfloat voiplevel;\t/*your own voice level*/\n\tunsigned int dumps;\t/*trigger a new generation thing after a bit*/\n\tunsigned int keeps;\t/*for vad_delay*/\n\tint curbitrate;\n\n\tsnd_capture_driver_t *cdriver;/*capture driver's functions*/\n\tvoid *cdriverctx;\t/*capture driver context*/\n} s_voip;\n\n#ifdef HAVE_OPUS\n#define OPUS_APPLICATION_VOIP\t\t\t\t2048\n#define OPUS_SET_BITRATE_REQUEST\t\t\t4002\n#define OPUS_RESET_STATE\t\t\t\t\t4028\n#ifdef OPUS_STATIC\n#include \"opus.h\"\n#define qopus_encoder_create\topus_encoder_create\n#define qopus_encoder_destroy\topus_encoder_destroy\n#define qopus_encoder_ctl\t\topus_encoder_ctl\n#define qopus_encode\t\t\topus_encode\n#define qopus_decoder_create\topus_decoder_create\n#define qopus_decoder_destroy\topus_decoder_destroy\n#define qopus_decoder_ctl\t\topus_decoder_ctl\n#define qopus_decode\t\t\topus_decode\n#else\n#define opus_int32 int\n#define opus_int16 short\n#define OpusEncoder void\n#define OpusDecoder void\nstatic OpusEncoder *(VARGS *qopus_encoder_create)(opus_int32 Fs, int channels, int application, int *error);\nstatic void (VARGS *qopus_encoder_destroy)(OpusEncoder *st);\nstatic int (VARGS *qopus_encoder_ctl)(OpusEncoder *st, int request, ...);\nstatic opus_int32 (VARGS *qopus_encode)(OpusEncoder *st, const opus_int16 *pcm, int frame_size, unsigned char *data, opus_int32 max_data_bytes);\nstatic OpusDecoder *(VARGS *qopus_decoder_create)(opus_int32 Fs, int channels, int *error);\nstatic void (VARGS *qopus_decoder_destroy)(OpusDecoder *st);\nstatic int (VARGS *qopus_decoder_ctl)(OpusDecoder *st, int request, ...);\nstatic int (VARGS *qopus_decode)(OpusDecoder *st, const unsigned char *data, opus_int32 len, opus_int16 *pcm, int frame_size, int decode_fec);\nstatic dllfunction_t qopusfuncs[] =\n{\n\t{(void*)&qopus_encoder_create,\t\"opus_encoder_create\"},\n\t{(void*)&qopus_encoder_destroy,\t\"opus_encoder_destroy\"},\n\t{(void*)&qopus_encoder_ctl,\t\t\"opus_encoder_ctl\"},\n\t{(void*)&qopus_encode,\t\t\t\"opus_encode\"},\n\n\t{(void*)&qopus_decoder_create,\t\"opus_decoder_create\"},\n\t{(void*)&qopus_decoder_destroy,\t\"opus_decoder_destroy\"},\n\t{(void*)&qopus_decoder_ctl,\t\t\"opus_decoder_ctl\"},\n\t{(void*)&qopus_decode,\t\t\t\"opus_decode\"},\n\n\t{NULL}\n};\n#endif\n\nstatic qboolean S_Opus_Init(void)\n{\n#ifndef OPUS_STATIC\n#ifdef _WIN32\n\tchar *modulename = \"libopus-0\" ARCH_DL_POSTFIX;\n#else\n\tchar *modulename = \"libopus\"ARCH_DL_POSTFIX\".0\";\n#endif\n\n\tif (s_voip.opus.inited)\n\t\treturn s_voip.opus.loaded;\n\ts_voip.opus.inited = true;\n\n\ts_voip.opus.opuslib = Sys_LoadLibrary(modulename, qopusfuncs);\n\tif (!s_voip.opus.opuslib)\n\t{\n\t\tCon_Printf(\"%s not found. Voice chat is not available.\\n\", modulename);\n\t\treturn false;\n\t}\n#endif\n\n\ts_voip.opus.loaded = true;\n\treturn s_voip.opus.loaded;\n}\n#endif\n\n#ifdef HAVE_SPEEX\n#ifdef SPEEX_STATIC\n#define qspeex_lib_get_mode speex_lib_get_mode\n#define qspeex_bits_init speex_bits_init\n#define qspeex_bits_reset speex_bits_reset\n#define qspeex_bits_write speex_bits_write\n\n#define qspeex_preprocess_state_init speex_preprocess_state_init\n#define qspeex_preprocess_state_destroy speex_preprocess_state_destroy\n#define qspeex_preprocess_ctl speex_preprocess_ctl\n#define qspeex_preprocess_run speex_preprocess_run\n\n#define qspeex_encoder_init speex_encoder_init\n#define qspeex_encoder_destroy speex_encoder_destroy\n#define qspeex_encoder_ctl speex_encoder_ctl\n#define qspeex_encode_int speex_encode_int\n\n#define qspeex_decoder_init speex_decoder_init\n#define qspeex_decoder_destroy speex_decoder_destroy\n#define qspeex_decode_int speex_decode_int\n#define qspeex_bits_read_from speex_bits_read_from\n#else\nstatic const SpeexMode *(VARGS *qspeex_lib_get_mode)(int mode);\nstatic void (VARGS *qspeex_bits_init)(SpeexBits *bits);\nstatic void (VARGS *qspeex_bits_reset)(SpeexBits *bits);\nstatic int (VARGS *qspeex_bits_write)(SpeexBits *bits, char *bytes, int max_len);\n\nstatic SpeexPreprocessState *(VARGS *qspeex_preprocess_state_init)(int frame_size, int sampling_rate);\nstatic void (VARGS *qspeex_preprocess_state_destroy)(SpeexPreprocessState *st);\nstatic int (VARGS *qspeex_preprocess_ctl)(SpeexPreprocessState *st, int request, void *ptr);\nstatic int (VARGS *qspeex_preprocess_run)(SpeexPreprocessState *st, spx_int16_t *x);\n\nstatic void * (VARGS *qspeex_encoder_init)(const SpeexMode *mode);\nstatic int (VARGS *qspeex_encoder_ctl)(void *state, int request, void *ptr);\nstatic int (VARGS *qspeex_encode_int)(void *state, spx_int16_t *in, SpeexBits *bits);\n\nstatic void *(VARGS *qspeex_decoder_init)(const SpeexMode *mode);\nstatic void (VARGS *qspeex_decoder_destroy)(void *state);\nstatic int (VARGS *qspeex_decode_int)(void *state, SpeexBits *bits, spx_int16_t *out);\nstatic void (VARGS *qspeex_bits_read_from)(SpeexBits *bits, char *bytes, int len);\n\nstatic dllfunction_t qspeexfuncs[] =\n{\n\t{(void*)&qspeex_lib_get_mode, \"speex_lib_get_mode\"},\n\t{(void*)&qspeex_bits_init, \"speex_bits_init\"},\n\t{(void*)&qspeex_bits_reset, \"speex_bits_reset\"},\n\t{(void*)&qspeex_bits_write, \"speex_bits_write\"},\n\n\t{(void*)&qspeex_encoder_init, \"speex_encoder_init\"},\n\t{(void*)&qspeex_encoder_ctl, \"speex_encoder_ctl\"},\n\t{(void*)&qspeex_encode_int, \"speex_encode_int\"},\n\n\t{(void*)&qspeex_decoder_init, \"speex_decoder_init\"},\n\t{(void*)&qspeex_decoder_destroy, \"speex_decoder_destroy\"},\n\t{(void*)&qspeex_decode_int, \"speex_decode_int\"},\n\t{(void*)&qspeex_bits_read_from, \"speex_bits_read_from\"},\n\n\t{NULL}\n};\nstatic dllfunction_t qspeexdspfuncs[] =\n{\n\t{(void*)&qspeex_preprocess_state_init, \"speex_preprocess_state_init\"},\n\t{(void*)&qspeex_preprocess_state_destroy, \"speex_preprocess_state_destroy\"},\n\t{(void*)&qspeex_preprocess_ctl, \"speex_preprocess_ctl\"},\n\t{(void*)&qspeex_preprocess_run, \"speex_preprocess_run\"},\n\n\t{NULL}\n};\n#endif\n\nstatic qboolean S_SpeexDSP_Init(void)\n{\n#ifndef SPEEX_STATIC\n\tif (s_voip.speexdsp.inited)\n\t\treturn s_voip.speexdsp.loaded;\n\ts_voip.speexdsp.inited = true;\n\n\n\ts_voip.speexdsp.speexdsplib = Sys_LoadLibrary(\"libspeexdsp\", qspeexdspfuncs);\n\tif (!s_voip.speexdsp.speexdsplib)\n\t{\n\t\tCon_Printf(\"libspeexdsp not found. Your mic may be noisy.\\n\");\n\t\treturn false;\n\t}\n#endif\n\n\ts_voip.speexdsp.loaded = true;\n\treturn s_voip.speexdsp.loaded;\n}\n\nstatic qboolean S_Speex_Init(void)\n{\n#ifndef SPEEX_STATIC\n\tif (s_voip.speex.inited)\n\t\treturn s_voip.speex.loaded;\n\ts_voip.speex.inited = true;\n\n\ts_voip.speex.speexlib = Sys_LoadLibrary(\"libspeex\", qspeexfuncs);\n\tif (!s_voip.speex.speexlib)\n\t{\n\t\tCon_Printf(\"libspeex not found. Voice chat is not available.\\n\");\n\t\treturn false;\n\t}\n#endif\n\n\ts_voip.speex.modenb = qspeex_lib_get_mode(SPEEX_MODEID_NB);\n\ts_voip.speex.modewb = qspeex_lib_get_mode(SPEEX_MODEID_WB);\n\ts_voip.speex.modeuwb = qspeex_lib_get_mode(SPEEX_MODEID_UWB);\n\n\ts_voip.speex.loaded = true;\n\treturn s_voip.speex.loaded;\n}\n#endif\n\n#ifdef AVAIL_OPENAL\nextern snd_capture_driver_t OPENAL_Capture;\n#endif\n#ifdef AVAIL_DSOUND\nsnd_capture_driver_t fte_weakstruct DSOUND_Capture;\n#endif\nsnd_capture_driver_t fte_weakstruct OSS_Capture;\nsnd_capture_driver_t fte_weakstruct SDL_Capture;\n\nsnd_capture_driver_t *capturedrivers[] =\n{\n#ifdef AVAIL_DSOUND\n\t&DSOUND_Capture,\n#endif\n\t&SDL_Capture,\n\t&OSS_Capture,\n#ifdef AVAIL_OPENAL\n\t&OPENAL_Capture,\n#endif\n\tNULL\n};\n\nsize_t PCMA_Decode(short *out, unsigned char *in, size_t samples)\n{\n\tsize_t i = 0;\n\tfor (i = 0; i < samples; i++)\n\t{\n\t\tunsigned char inv = in[i]^0x55;\t//g711 alaw inverts every other bit\n\t\tint m = inv&0xf;\n\t\tint e = (inv&0x70)>>4;\n\t\tif (e)\n\t\t\tm = (((m)<<1)|0x21) << (e-1);\n\t\telse\n\t\t\tm = (((m)<<1)|1);\n\t\tif (inv & 0x80)\n\t\t\tout[i] = -m;\n\t\telse\n\t\t\tout[i] = m;\n\t}\n\treturn i;\n}\nsize_t PCMA_Encode(unsigned char *out, size_t outsize, short *in, size_t samples)\n{\n\tsize_t i = 0;\n\tfor (i = 0; i < samples; i++)\n\t{\n\t\tint o = in[i];\n\t\tunsigned char b;\n\t\tif (o < 0)\n\t\t{\n\t\t\to = -o;\n\t\t\tb = 0x80;\n\t\t}\n\t\telse\n\t\t\tb = 0;\n\n\t\tif (o >= 0x0800)\n\t\t\tb |= ((o>>7)&0xf) | 0x70;\n\t\telse if (o >= 0x0400)\n\t\t\tb |= ((o>>6)&0xf) | 0x60;\n\t\telse if (o >= 0x0200)\n\t\t\tb |= ((o>>5)&0xf) | 0x50;\n\t\telse if (o >= 0x0100)\n\t\t\tb |= ((o>>4)&0xf) | 0x40;\n\t\telse if (o >= 0x0080)\n\t\t\tb |= ((o>>3)&0xf) | 0x30;\n\t\telse if (o >= 0x0040)\n\t\t\tb |= ((o>>2)&0xf) | 0x20;\n\t\telse if (o >= 0x0020)\n\t\t\tb |= ((o>>1)&0xf) | 0x10;\n\t\telse\n\t\t\tb |= ((o>>1)&0xf) | 0x00;\n\t\tout[i] = b^0x55;\t//invert every-other bit.\n\t}\n\n\treturn samples;\n}\nsize_t PCMU_Decode(short *out, unsigned char *in, size_t samples)\n{\n\tsize_t i = 0;\n\tfor (i = 0; i < samples; i++)\n\t{\n\t\tunsigned char inv = in[i]^0xff;\n\t\tint m = (((inv&0xf)<<1)|0x21) << ((inv&0x70)>>4);\n\t\tm -= 33;\n\t\tif (inv & 0x80)\n\t\t\tout[i] = -m;\n\t\telse\n\t\t\tout[i] = m;\n\t}\n\treturn i;\n}\nsize_t PCMU_Encode(unsigned char *out, size_t outsize, short *in, size_t samples)\n{\n\tsize_t i = 0;\n\tfor (i = 0; i < samples; i++)\n\t{\n\t\tint o = in[i];\n\t\tunsigned char b;\n\t\tif (o < 0)\n\t\t{\n\t\t\to = ~o;\n\t\t\tb = 0x80;\n\t\t}\n\t\telse\n\t\t\tb = 0;\n\t\to+=33;\n\n\t\tif (o >= 0x1000)\n\t\t\tb |= ((o>>8)&0xf) | 0x70;\n\t\telse if (o >= 0x0800)\n\t\t\tb |= ((o>>7)&0xf) | 0x60;\n\t\telse if (o >= 0x0400)\n\t\t\tb |= ((o>>6)&0xf) | 0x50;\n\t\telse if (o >= 0x0200)\n\t\t\tb |= ((o>>5)&0xf) | 0x40;\n\t\telse if (o >= 0x0100)\n\t\t\tb |= ((o>>4)&0xf) | 0x30;\n\t\telse if (o >= 0x0080)\n\t\t\tb |= ((o>>3)&0xf) | 0x20;\n\t\telse if (o >= 0x0040)\n\t\t\tb |= ((o>>2)&0xf) | 0x10;\n\t\telse\n\t\t\tb |= ((o>>1)&0xf) | 0x00;\n\t\tout[i] = b^0xff;\n\t}\n\n\treturn samples;\n}\n\nvoid S_Voip_Decode(unsigned int sender, unsigned int codec, unsigned int gen, unsigned char seq, unsigned int bytes, unsigned char *data)\n{\n\tunsigned char *start;\n\tshort decodebuf[8192];\n\tunsigned int decodesamps, len, drops;\n\tint r;\n\n\tif (sender >= MAX_CLIENTS)\n\t\treturn;\n\n\tdecodesamps = 0;\n\tdrops = 0;\n\tstart = data;\n\n\ts_voip.lastspoke[sender] = realtime + 0.5;\n\tif (s_voip.lastspoke[sender] > s_voip.lastspoke_any)\n\t\ts_voip.lastspoke_any = s_voip.lastspoke[sender];\n\n\t//if they re-started speaking, flush any old state to avoid things getting weirdly delayed and reset the codec properly.\n\tif (s_voip.decgen[sender] != gen || s_voip.deccodec[sender] != codec)\n\t{\n\t\tS_RawAudio(sender, NULL, s_voip.decsamplerate[sender], 0, 1, 2, 0);\n\n\t\tif (s_voip.deccodec[sender] != codec)\n\t\t{\n\t\t\t//make sure old state is closed properly.\n\t\t\tswitch(s_voip.deccodec[sender])\n\t\t\t{\n#ifdef HAVE_SPEEX\n\t\t\tcase VOIP_SPEEX_OLD:\n\t\t\tcase VOIP_SPEEX_NARROW:\n\t\t\tcase VOIP_SPEEX_WIDE:\n\t\t\tcase VOIP_SPEEX_ULTRAWIDE:\n\t\t\t\tqspeex_decoder_destroy(s_voip.decoder[sender]);\n\t\t\t\tbreak;\n#endif\n\t\t\tcase VOIP_RAW16:\n\t\t\t\tbreak;\n#ifdef HAVE_OPUS\n\t\t\tcase VOIP_OPUS:\n\t\t\t\tqopus_decoder_destroy(s_voip.decoder[sender]);\n\t\t\t\tbreak;\n#endif\n\t\t\t}\n\t\t\ts_voip.decoder[sender] = NULL;\n\t\t\ts_voip.deccodec[sender] = VOIP_INVALID;\n\t\t}\n\n\t\tswitch(codec)\n\t\t{\n\t\tdefault:\t//codec not supported.\n\t\t\treturn;\n\t\tcase VOIP_RAW16:\n\t\t\ts_voip.decsamplerate[sender] = 11025;\n\t\t\tbreak;\n\t\tcase VOIP_PCMA:\n\t\tcase VOIP_PCMU:\n\t\t\ts_voip.decsamplerate[sender] = 8000;\n\t\t\ts_voip.decframesize[sender] = 8000/20;\n\t\t\tbreak;\n#ifdef HAVE_SPEEX\n\t\tcase VOIP_SPEEX_OLD:\n\t\tcase VOIP_SPEEX_NARROW:\n\t\tcase VOIP_SPEEX_WIDE:\n\t\tcase VOIP_SPEEX_ULTRAWIDE:\n\t\t\t{\n\t\t\t\tconst SpeexMode *smode;\n\t\t\t\tif (!S_Speex_Init())\n\t\t\t\t\treturn;\t//speex not usable.\n\t\t\t\tif (codec == VOIP_SPEEX_NARROW)\n\t\t\t\t{\n\t\t\t\t\ts_voip.decsamplerate[sender] = 8000;\n\t\t\t\t\ts_voip.decframesize[sender] = 160;\n\t\t\t\t\tsmode = s_voip.speex.modenb;\n\t\t\t\t}\n\t\t\t\telse if (codec == VOIP_SPEEX_WIDE)\n\t\t\t\t{\n\t\t\t\t\ts_voip.decsamplerate[sender] = 16000;\n\t\t\t\t\ts_voip.decframesize[sender] = 320;\n\t\t\t\t\tsmode = s_voip.speex.modewb;\n\t\t\t\t}\n\t\t\t\telse if (codec == VOIP_SPEEX_ULTRAWIDE)\n\t\t\t\t{\n\t\t\t\t\ts_voip.decsamplerate[sender] = 32000;\n\t\t\t\t\ts_voip.decframesize[sender] = 640;\n\t\t\t\t\tsmode = s_voip.speex.modeuwb;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\ts_voip.decsamplerate[sender] = 11025;\n\t\t\t\t\ts_voip.decframesize[sender] = 160;\n\t\t\t\t\tsmode = s_voip.speex.modenb;\n\t\t\t\t}\n\t\t\t\tif (!s_voip.decoder[sender])\n\t\t\t\t{\n\t\t\t\t\tqspeex_bits_init(&s_voip.speex.decbits[sender]);\n\t\t\t\t\tqspeex_bits_reset(&s_voip.speex.decbits[sender]);\n\t\t\t\t\ts_voip.decoder[sender] = qspeex_decoder_init(smode);\n\t\t\t\t\tif (!s_voip.decoder[sender])\n\t\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tqspeex_bits_reset(&s_voip.speex.decbits[sender]);\n\t\t\t}\n\t\t\tbreak;\n#endif\n#ifdef HAVE_OPUS\n\t\tcase VOIP_OPUS:\n\t\t\tif (!S_Opus_Init())\n\t\t\t\treturn;\n\n\t\t\t//the lazy way to reset the codec!\n\t\t\tif (!s_voip.decoder[sender])\n\t\t\t{\n\t\t\t\t//opus outputs to 8, 12, 16, 24, or 48khz. pick whichever has least excess samples and resample to fit it.\n\t\t\t\tif (snd_speed <= 8000)\n\t\t\t\t\ts_voip.decsamplerate[sender] = 8000;\n\t\t\t\telse if (snd_speed <= 12000)\n\t\t\t\t\ts_voip.decsamplerate[sender] = 12000;\n\t\t\t\telse if (snd_speed <= 16000)\n\t\t\t\t\ts_voip.decsamplerate[sender] = 16000;\n\t\t\t\telse if (snd_speed <= 24000)\n\t\t\t\t\ts_voip.decsamplerate[sender] = 24000;\n\t\t\t\telse\n\t\t\t\t\ts_voip.decsamplerate[sender] = 48000;\n\t\t\t\ts_voip.decoder[sender] = qopus_decoder_create(s_voip.decsamplerate[sender], 1/*FIXME: support stereo where possible*/, NULL);\n\t\t\t\tif (!s_voip.decoder[sender])\n\t\t\t\t\treturn;\n\n\t\t\t\ts_voip.decframesize[sender] = s_voip.decsamplerate[sender]/400;\t//this is the maximum size in a single frame.\n\t\t\t}\n\t\t\telse\n\t\t\t\tqopus_decoder_ctl(s_voip.decoder[sender], OPUS_RESET_STATE);\n\t\t\tbreak;\n#endif\n\t\t}\n\t\ts_voip.deccodec[sender] = codec;\n\t\ts_voip.decgen[sender] = gen;\n\t\ts_voip.decseq[sender] = seq;\n\t\ts_voip.declevel[sender] = 0;\n\t}\n\n\n\t//if there's packetloss, tell the decoder about the missing parts.\n\t//no infinite loops please.\n\tif ((unsigned)(seq - s_voip.decseq[sender]) > 10)\n\t\ts_voip.decseq[sender] = seq - 10;\n\twhile(s_voip.decseq[sender] != seq)\n\t{\n\t\tif (decodesamps + s_voip.decframesize[sender] > sizeof(decodebuf)/sizeof(decodebuf[0]))\n\t\t{\n\t\t\tS_RawAudio(sender, (qbyte*)decodebuf, s_voip.decsamplerate[sender], decodesamps, 1, 2, snd_voip_play.value);\n\t\t\tdecodesamps = 0;\n\t\t}\n\t\tswitch(codec)\n\t\t{\n\t\tcase VOIP_RAW16:\n\t\tcase VOIP_PCMA:\n\t\tcase VOIP_PCMU:\n\t\t\tbreak;\n#ifdef HAVE_SPEEX\n\t\tcase VOIP_SPEEX_OLD:\n\t\tcase VOIP_SPEEX_NARROW:\n\t\tcase VOIP_SPEEX_WIDE:\n\t\tcase VOIP_SPEEX_ULTRAWIDE:\n\t\t\tqspeex_decode_int(s_voip.decoder[sender], NULL, decodebuf + decodesamps);\n\t\t\tdecodesamps += s_voip.decframesize[sender];\n\t\t\tbreak;\n#endif\n#ifdef HAVE_OPUS\n\t\tcase VOIP_OPUS:\n\t\t\tr = qopus_decode(s_voip.decoder[sender], NULL, 0, decodebuf + decodesamps, min(s_voip.decframesize[sender], sizeof(decodebuf)/sizeof(decodebuf[0]) - decodesamps), false);\n\t\t\tif (r > 0)\n\t\t\t\tdecodesamps += r;\n\t\t\tbreak;\n#endif\n\t\t}\n\t\ts_voip.decseq[sender]++;\n\t}\n\n\twhile (bytes > 0)\n\t{\n\t\tif (decodesamps + s_voip.decframesize[sender] >= sizeof(decodebuf)/sizeof(decodebuf[0]))\n\t\t{\n\t\t\tS_RawAudio(sender, (qbyte*)decodebuf, s_voip.decsamplerate[sender], decodesamps, 1, 2, snd_voip_play.value);\n\t\t\tdecodesamps = 0;\n\t\t}\n\t\tswitch(codec)\n\t\t{\n\t\tdefault:\n\t\t\tbytes = 0;\n\t\t\tbreak;\n#ifdef HAVE_SPEEX\n\t\tcase VOIP_SPEEX_OLD:\n\t\tcase VOIP_SPEEX_NARROW:\n\t\tcase VOIP_SPEEX_WIDE:\n\t\tcase VOIP_SPEEX_ULTRAWIDE:\n\t\t\tif (codec == VOIP_SPEEX_OLD)\n\t\t\t{\t//older versions support only this, and require this extra bit.\n\t\t\t\tbytes--;\n\t\t\t\tlen = *start++;\n\t\t\t\tif (bytes < len)\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\telse\n\t\t\t\tlen = bytes;\n\t\t\tqspeex_bits_read_from(&s_voip.speex.decbits[sender], start, len);\n\t\t\tbytes -= len;\n\t\t\tstart += len;\n\t\t\twhile (qspeex_decode_int(s_voip.decoder[sender], &s_voip.speex.decbits[sender], decodebuf + decodesamps) == 0)\n\t\t\t{\n\t\t\t\tdecodesamps += s_voip.decframesize[sender];\n\t\t\t\ts_voip.decseq[sender]++;\n\t\t\t\tseq++;\n\t\t\t\tif (decodesamps + s_voip.decframesize[sender] >= sizeof(decodebuf)/sizeof(decodebuf[0]))\n\t\t\t\t{\n\t\t\t\t\tS_RawAudio(sender, (qbyte*)decodebuf, s_voip.decsamplerate[sender], decodesamps, 1, 2, snd_voip_play.value);\n\t\t\t\t\tdecodesamps = 0;\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n#endif\n\t\tcase VOIP_RAW16:\n\t\t\tlen = min(bytes, sizeof(decodebuf)-(sizeof(decodebuf[0])*decodesamps));\n\t\t\tmemcpy(decodebuf+decodesamps, start, len);\n\t\t\tdecodesamps += len / sizeof(decodebuf[0]);\n\t\t\ts_voip.decseq[sender]++;\n\t\t\tbytes -= len;\n\t\t\tstart += len;\n\t\t\tbreak;\n\t\tcase VOIP_PCMA:\n\t\tcase VOIP_PCMU:\n\t\t\tlen = min(bytes, sizeof(decodebuf)-(sizeof(decodebuf[0])*decodesamps));\n\t\t\tif (len > s_voip.decframesize[sender]*2)\n\t\t\t\tlen = s_voip.decframesize[sender]*2;\n\t\t\tif (codec == VOIP_PCMA)\n\t\t\t\tdecodesamps += PCMA_Decode(decodebuf+decodesamps, start, len);\n\t\t\telse\n\t\t\t\tdecodesamps += PCMU_Decode(decodebuf+decodesamps, start, len);\n\t\t\ts_voip.decseq[sender]++;\n\t\t\tbytes -= len;\n\t\t\tstart += len;\n\t\t\tbreak;\n#ifdef HAVE_OPUS\n\t\tcase VOIP_OPUS:\n\t\t\tlen = bytes;\n\t\t\tif (decodesamps > 0)\n\t\t\t{\n\t\t\t\tS_RawAudio(sender, (qbyte*)decodebuf, s_voip.decsamplerate[sender], decodesamps, 1, 2, snd_voip_play.value);\n\t\t\t\tdecodesamps = 0;\n\t\t\t}\n\t\t\tr = qopus_decode(s_voip.decoder[sender], start, len, decodebuf + decodesamps, sizeof(decodebuf)/sizeof(decodebuf[0]) - decodesamps, false);\n//\t\t\tCon_Printf(\"Decoded %i frames from %i bytes\\n\", r, len);\n\t\t\tif (r > 0)\n\t\t\t{\n\t\t\t\tint frames = r / s_voip.decframesize[sender];\n\t\t\t\tdecodesamps += r;\n\t\t\t\ts_voip.decseq[sender] = (s_voip.decseq[sender] + frames) & 0xff;\n\t\t\t\tseq = (seq+frames)&0xff;\n\t\t\t}\n\t\t\telse if (r < 0)\n\t\t\t\tCon_Printf(\"Opus decoding error %i\\n\", r);\n\n\t\t\tbytes -= len;\n\t\t\tstart += len;\n\t\t\tbreak;\n#endif\n\t\t}\n\t}\n\n\tif (drops)\n\t\tCon_DPrintf(\"%i dropped audio frames\\n\", drops);\n\n\tif (decodesamps > 0)\n\t{\t//calculate levels of other people. eukara demanded this.\n\t\tfloat level = 0;\n\t\tfloat f;\n\t\tfor (len = 0; len < decodesamps; len++)\n\t\t{\n\t\t\tf = decodebuf[len];\n\t\t\tlevel += f*f;\n\t\t}\n\t\tlevel = (3000*level) / (32767.0f*32767*decodesamps);\n\t\ts_voip.declevel[sender] = (s_voip.declevel[sender]*7 + level)/8;\n\n\t\tS_RawAudio(sender, (qbyte*)decodebuf, s_voip.decsamplerate[sender], decodesamps, 1, 2, snd_voip_play.value);\n\t}\n}\n\n#ifdef SUPPORT_ICE\nstatic int S_Voip_NameToId(const char *codec)\n{\n\tif (!Q_strcasecmp(codec, \"speex@8000\"))\n\t\treturn VOIP_SPEEX_NARROW;\n\telse if (!Q_strcasecmp(codec, \"speex@11025\"))\n\t\treturn VOIP_SPEEX_OLD;\n\telse if (!Q_strcasecmp(codec, \"speex@16000\"))\n\t\treturn VOIP_SPEEX_WIDE;\n\telse if (!Q_strcasecmp(codec, \"speex@32000\"))\n\t\treturn VOIP_SPEEX_ULTRAWIDE;\n\telse if (!Q_strcasecmp(codec, \"opus\") || !strcmp(codec, \"opus@48000\"))\n\t\treturn VOIP_OPUS;\n\telse if (!Q_strcasecmp(codec, \"pcma@8000\"))\n\t\treturn VOIP_PCMA;\n\telse if (!Q_strcasecmp(codec, \"pcmu@8000\"))\n\t\treturn VOIP_PCMU;\n\telse\n\t\treturn VOIP_INVALID;\n}\nqboolean S_Voip_RTP_CodecOkay(const char *codec)\n{\n\tswitch(S_Voip_NameToId(codec))\n\t{\n#ifdef HAVE_SPEEX\n\tcase VOIP_SPEEX_NARROW:\n\tcase VOIP_SPEEX_OLD:\n\tcase VOIP_SPEEX_WIDE:\n\tcase VOIP_SPEEX_ULTRAWIDE:\n\t\treturn S_Speex_Init();\n#endif\n\tcase VOIP_PCMA:\n\tcase VOIP_PCMU:\n\t\treturn true;\n#ifdef HAVE_OPUS\n\tcase VOIP_OPUS:\n\t\treturn S_Opus_Init();\n#endif\n\tdefault:\n\t\treturn false;\n\t}\n}\nvoid S_Voip_RTP_Parse(unsigned short sequence, char *codec, unsigned char *data, unsigned int datalen)\n{\n\tS_Voip_Decode(MAX_CLIENTS-1, S_Voip_NameToId(codec), 0, sequence&0xff, datalen, data);\n}\nqboolean NET_RTP_Transmit(unsigned int sequence, unsigned int timestamp, const char *codec, char *cdata, int clength);\nqboolean NET_RTP_Active(void);\n#else\n#define NET_RTP_Active() false\n#endif\n\nvoid S_Voip_Parse(void)\n{\n\tunsigned int sender;\n\tunsigned int bytes;\n\tunsigned char data[1024];\n\tunsigned char seq, gen;\n\tunsigned char codec;\n\n\tsender = MSG_ReadByte();\n\tgen = MSG_ReadByte();\n\tcodec = gen>>4;\n\tgen &= 0x0f;\n\tseq = MSG_ReadByte();\n\tbytes = MSG_ReadShort();\n\tif (bytes > sizeof(data) || snd_voip_play.value <= 0)\n\t{\n\t\tMSG_ReadSkip(bytes);\n\t\treturn;\n\t}\n\tMSG_ReadData(data, bytes);\n\n\tsender %= MAX_CLIENTS;\n\n\t//if testing, don't get confused if the server is echoing voice too!\n\tif (snd_voip_test.ival)\n\t\tif (sender == cl.playerview[0].playernum)\n\t\t\treturn;\n\n\tS_Voip_Decode(sender, codec, gen, seq, bytes, data);\n}\nstatic float S_Voip_Preprocess(short *start, unsigned int samples, float micamp)\n{\n\tint i;\n\tfloat level = 0, f;\n\tint framesize = s_voip.encframesize;\n\twhile(samples >= framesize)\n\t{\n#ifdef HAVE_SPEEX\n\t\tif (s_voip.speexdsp.preproc)\n\t\t\tqspeex_preprocess_run(s_voip.speexdsp.preproc, start);\n#endif\n\t\tfor (i = 0; i < framesize; i++)\n\t\t{\n\t\t\tf = start[i] * micamp;\n\t\t\tstart[i] = bound(-32768, f, 32767);\t//clamp it carefully, so it doesn't go to crap when given far too high a mic amp\n\t\t\tlevel += f*f;\n\t\t}\n\n\t\tstart += framesize;\n\t\tsamples -= framesize;\n\t}\n\treturn level;\n}\nstatic void S_Voip_TryInitCaptureContext(char *driver, char *device, int rate)\n{\n\tstatic float throttle;\n\tint i;\n\n\ts_voip.cdriver = NULL;\n\n\t/*Add new drivers in order of priority*/\n\tfor (i = 0; capturedrivers[i]; i++)\n\t{\n\t\tif (capturedrivers[i]->Init && (!driver || !strcmp(capturedrivers[i]->drivername, driver)))\n\t\t{\n\t\t\ts_voip.cdriver = capturedrivers[i];\n\n\t\t\ts_voip.cdriverctx = s_voip.cdriver->Init(s_voip.encsamplerate, device);\n\t\t\tif (s_voip.cdriverctx)\n\t\t\t{\n\t\t\t\t//success!\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n\n\tif (!s_voip.cdriver)\n\t{\n\t\tif (!driver)\n\t\t\tCon_ThrottlePrintf(&throttle, 0, CON_ERROR\"No microphone drivers supported\\n\");\n\t\telse\n\t\t\tCon_ThrottlePrintf(&throttle, 0, CON_ERROR\"Microphone driver \\\"%s\\\" is not valid\\n\", driver);\n\t}\n\telse\n\t\tCon_ThrottlePrintf(&throttle, 0, CON_ERROR\"No microphone detected\\n\");\n\ts_voip.cdriver = NULL;\n}\n\nstatic void S_Voip_InitCaptureContext(int rate)\n{\n\tchar *s;\n\n\ts_voip.cdriver = NULL;\n\ts_voip.cdriverctx = NULL;\n\n\tfor (s = snd_voip_capturedevice.string; ; )\n\t{\n\t\tchar *sep;\n\t\ts = COM_Parse(s);\n\t\tif (!*com_token)\n\t\t\tbreak;\n\n\t\tsep = strchr(com_token, ':');\n\t\tif (sep)\n\t\t\t*sep++ = 0;\n\t\tS_Voip_TryInitCaptureContext(com_token, sep, rate);\n\t}\n\tif (!s_voip.cdriver)\n\t\tS_Voip_TryInitCaptureContext(NULL, NULL, rate);\n}\n\nvoid S_Voip_Transmit(unsigned char clc, sizebuf_t *buf)\n{\n\tunsigned char outbuf[8192];\n\tunsigned int outpos;//in bytes\n\tunsigned int encpos;//in bytes\n\tshort *start;\n\tunsigned int initseq;//in frames\n#ifdef SUPPORT_ICE\n\tunsigned int inittimestamp;//in samples\n#endif\n\tunsigned int samps;\n\tfloat level;\n\tint len;\n\tfloat micamp = snd_voip_micamp.value;\n\tqboolean voipsendenable = true;\n\tint voipcodec = *snd_voip_codec.string?snd_voip_codec.ival:VOIP_DEFAULT_CODEC;\n\tqboolean rtpstream = NET_RTP_Active();\n\n\tif (buf)\n\t{\n\t\t/*if you're sending sound, you should be prepared to accept others yelling at you to shut up*/\n\t\tif (snd_voip_play.value <= 0)\n\t\t\tvoipsendenable = false;\n\t\t/*don't send sound if its not supported. that'll break stuff*/\n\t\tif (!(cls.fteprotocolextensions2 & PEXT2_VOICECHAT))\n\t\t\tvoipsendenable = false;\n\t}\n\telse\n\t{\n\t\t/*we're not sending it to a server. the above considerations don't matter*/\n\t\tvoipsendenable = snd_voip_test.ival;\n\t}\n\t/*don't send sound if mic volume won't send anything anyway*/\n\tif (micamp <= 0)\n\t\tvoipsendenable = false;\n\n\tif (rtpstream)\n\t{\n\t\tvoipsendenable = true;\n\t\t//if rtp streaming is enabled, hack the codec to something better supported\n#ifdef HAVE_SPEEX\n\t\tif (voipcodec == VOIP_SPEEX_OLD)\n\t\t\tvoipcodec = VOIP_SPEEX_WIDE;\n#endif\n\t}\n\n\n\tvoicevolumemod = s_voip.lastspoke_any > realtime?snd_voip_ducking.value:1;\n\tvoicevolumemod *= mastervolume.value;\n\n\tif (!voipsendenable || (voipcodec != s_voip.enccodec && s_voip.cdriver))\n\t{\n\t\tif (s_voip.cdriver)\n\t\t{\n\t\t\tif (s_voip.cdriverctx)\n\t\t\t{\n\t\t\t\tif (s_voip.wantsend)\n\t\t\t\t{\n\t\t\t\t\ts_voip.cdriver->Stop(s_voip.cdriverctx);\n\t\t\t\t\ts_voip.wantsend = false;\n\t\t\t\t}\n\t\t\t\ts_voip.cdriver->Shutdown(s_voip.cdriverctx);\n\t\t\t\ts_voip.cdriverctx = NULL;\n\t\t\t}\n\t\t\ts_voip.cdriver = NULL;\n\t\t}\n\t\tswitch(s_voip.enccodec)\n\t\t{\n#ifdef HAVE_SPEEX\n\t\tcase VOIP_SPEEX_OLD:\n\t\tcase VOIP_SPEEX_NARROW:\n\t\tcase VOIP_SPEEX_WIDE:\n\t\tcase VOIP_SPEEX_ULTRAWIDE:\n\t\t\tbreak;\n#endif\n#ifdef HAVE_OPUS\n\t\tcase VOIP_OPUS:\n\t\t\tqopus_encoder_destroy(s_voip.encoder);\n\t\t\tbreak;\n#endif\n\t\t}\n\t\ts_voip.encoder = NULL;\n\t\ts_voip.enccodec = VOIP_INVALID;\n\n\t\tif (!voipsendenable)\n\t\t\treturn;\n\t}\n\n\tvoipsendenable = voipbutton || (snd_voip_send.ival>0);\n\n\tif (!s_voip.cdriver)\n\t{\n\t\ts_voip.voiplevel = -1;\n\t\t/*only init the first time capturing is requested*/\n\t\tif (!voipsendenable)\n\t\t\treturn;\n\n\t\t/*see if we can init our encoding codec...*/\n\t\tswitch(voipcodec)\n\t\t{\n#ifdef HAVE_SPEEX\n\t\tcase VOIP_SPEEX_OLD:\n\t\tcase VOIP_SPEEX_NARROW:\n\t\tcase VOIP_SPEEX_WIDE:\n\t\tcase VOIP_SPEEX_ULTRAWIDE:\n\t\t\t{\n\t\t\t\tconst SpeexMode *smode;\n\t\t\t\tif (!S_Speex_Init())\n\t\t\t\t{\n\t\t\t\t\tCon_Printf(\"Unable to use speex codec - not installed\\n\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tif (voipcodec == VOIP_SPEEX_ULTRAWIDE)\n\t\t\t\t\tsmode = s_voip.speex.modeuwb;\n\t\t\t\telse if (voipcodec == VOIP_SPEEX_WIDE)\n\t\t\t\t\tsmode = s_voip.speex.modewb;\n\t\t\t\telse\n\t\t\t\t\tsmode = s_voip.speex.modenb;\n\t\t\t\tqspeex_bits_init(&s_voip.speex.encbits);\n\t\t\t\tqspeex_bits_reset(&s_voip.speex.encbits);\n\t\t\t\ts_voip.encoder = qspeex_encoder_init(smode);\n\t\t\t\tif (!s_voip.encoder)\n\t\t\t\t\treturn;\n\t\t\t\tqspeex_encoder_ctl(s_voip.encoder, SPEEX_GET_FRAME_SIZE, &s_voip.encframesize);\n\t\t\t\tqspeex_encoder_ctl(s_voip.encoder, SPEEX_GET_SAMPLING_RATE, &s_voip.encsamplerate);\n\t\t\t\tif (voipcodec == VOIP_SPEEX_NARROW)\n\t\t\t\t\ts_voip.encsamplerate = 8000;\n\t\t\t\telse if (voipcodec == VOIP_SPEEX_WIDE)\n\t\t\t\t\ts_voip.encsamplerate = 16000;\n\t\t\t\telse if (voipcodec == VOIP_SPEEX_ULTRAWIDE)\n\t\t\t\t\ts_voip.encsamplerate = 32000;\n\t\t\t\telse\n\t\t\t\t\ts_voip.encsamplerate = 11025;\n\t\t\t\tqspeex_encoder_ctl(s_voip.encoder, SPEEX_SET_SAMPLING_RATE, &s_voip.encsamplerate);\n\t\t\t}\n\t\t\tbreak;\n#endif\n\t\tcase VOIP_PCMA:\n\t\tcase VOIP_PCMU:\n\t\t\ts_voip.encsamplerate = 8000;\n\t\t\ts_voip.encframesize = 8000/20;\n\t\t\tbreak;\n\t\tcase VOIP_RAW16:\n\t\t\ts_voip.encsamplerate = 11025;\n\t\t\ts_voip.encframesize = 256;\n\t\t\tbreak;\n#ifdef HAVE_OPUS\n\t\tcase VOIP_OPUS:\n\t\t\tif (!S_Opus_Init())\n\t\t\t{\n\t\t\t\tCon_Printf(\"Unable to use opus codec - not installed\\n\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t//use whatever is convienient.\n\t\t\ts_voip.encsamplerate = 48000;\n\t\t\ts_voip.encframesize = s_voip.encsamplerate / 400;\t//2.5ms frames, at a minimum.\n\t\t\ts_voip.encoder = qopus_encoder_create(s_voip.encsamplerate, 1, OPUS_APPLICATION_VOIP, NULL);\n\t\t\tif (!s_voip.encoder)\n\t\t\t\treturn;\n\n\t\t\ts_voip.curbitrate = 0;\n\n//\t\t\topus_encoder_ctl(s_voip.encoder, OPUS_SET_BITRATE(bitrate_bps));\n//\t\t\topus_encoder_ctl(s_voip.encoder, OPUS_SET_BANDWIDTH(OPUS_BANDWIDTH_NARROWBAND));\n//\t\t\topus_encoder_ctl(s_voip.encoder, OPUS_SET_VBR(use_vbr));\n//\t\t\topus_encoder_ctl(s_voip.encoder, OPUS_SET_VBR_CONSTRAINT(cvbr));\n//\t\t\topus_encoder_ctl(s_voip.encoder, OPUS_SET_COMPLEXITY(complexity));\n//\t\t\topus_encoder_ctl(s_voip.encoder, OPUS_SET_INBAND_FEC(use_inbandfec));\n//\t\t\topus_encoder_ctl(s_voip.encoder, OPUS_SET_FORCE_CHANNELS(forcechannels));\n//\t\t\topus_encoder_ctl(s_voip.encoder, OPUS_SET_DTX(use_dtx));\n//\t\t\topus_encoder_ctl(s_voip.encoder, OPUS_SET_PACKET_LOSS_PERC(packet_loss_perc));\n\n//\t\t\topus_encoder_ctl(s_voip.encoder, OPUS_GET_LOOKAHEAD(&skip));\n//\t\t\topus_encoder_ctl(s_voip.encoder, OPUS_SET_LSB_DEPTH(16));\n\t\t\tbreak;\n#endif\n\t\tdefault:\n\t\t\tCon_Printf(\"Unable to use that codec - not implemented\\n\");\n\t\t\t//can't start up other coedcs, cos we're too lame.\n\t\t\treturn;\n\t\t}\n\t\ts_voip.enccodec = voipcodec;\n\n\t\tS_Voip_InitCaptureContext(s_voip.encsamplerate);\t//sets cdriver+cdriverctx\n\t}\n\n\t/*couldn't init a driver?*/\n\tif (!s_voip.cdriverctx || !s_voip.cdriver)\n\t{\n\t\treturn;\n\t}\n\n\tif (!voipsendenable && s_voip.wantsend)\n\t{\n\t\ts_voip.wantsend = false;\n\t\ts_voip.capturepos += s_voip.cdriver->Update(s_voip.cdriverctx, (unsigned char*)s_voip.capturebuf + s_voip.capturepos, 1, sizeof(s_voip.capturebuf) - s_voip.capturepos);\n\t\ts_voip.cdriver->Stop(s_voip.cdriverctx);\n\t\t/*note: we still grab audio to flush everything that was captured while it was active*/\n\t}\n\telse if (voipsendenable && !s_voip.wantsend)\n\t{\n\t\ts_voip.wantsend = true;\n\t\tif (!s_voip.capturepos)\n\t\t{\t/*if we were actually still sending, it was probably only off for a single frame, in which case don't reset it*/\n\t\t\ts_voip.dumps = 0;\n\t\t\ts_voip.generation++;\n\t\t\ts_voip.encsequence = 0;\n\n\t\t\t//reset codecs so they start with a clean slate when new audio blocks are generated.\n\t\t\tswitch(s_voip.enccodec)\n\t\t\t{\n#ifdef HAVE_SPEEX\n\t\t\tcase VOIP_SPEEX_OLD:\n\t\t\tcase VOIP_SPEEX_NARROW:\n\t\t\tcase VOIP_SPEEX_WIDE:\n\t\t\tcase VOIP_SPEEX_ULTRAWIDE:\n\t\t\t\tqspeex_bits_reset(&s_voip.speex.encbits);\n\t\t\t\tbreak;\n#endif\n\t\t\tcase VOIP_RAW16:\n\t\t\t\tbreak;\n#ifdef HAVE_OPUS\n\t\t\tcase VOIP_OPUS:\n\t\t\t\tqopus_encoder_ctl(s_voip.encoder, OPUS_RESET_STATE);\n\t\t\t\tbreak;\n#endif\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\ts_voip.capturepos += s_voip.cdriver->Update(s_voip.cdriverctx, (unsigned char*)s_voip.capturebuf + s_voip.capturepos, 1, sizeof(s_voip.capturebuf) - s_voip.capturepos);\n\t\t}\n\t\ts_voip.cdriver->Start(s_voip.cdriverctx);\n\t}\n\n\tif (s_voip.wantsend)\n\t\tvoicevolumemod = min(voicevolumemod, snd_voip_capturingvol.value);\n\n\ts_voip.capturepos += s_voip.cdriver->Update(s_voip.cdriverctx, (unsigned char*)s_voip.capturebuf + s_voip.capturepos, s_voip.encframesize*2, sizeof(s_voip.capturebuf) - s_voip.capturepos);\n\n\tif (!voipsendenable || (!s_voip.wantsend && s_voip.capturepos < s_voip.encframesize*2))\n\t{\n\t\ts_voip.voiplevel = -1;\n\t\ts_voip.capturepos = 0;\n\t\treturn;\n\t}\n\n\tinitseq = s_voip.encsequence;\n#ifdef SUPPORT_ICE\n\tinittimestamp = s_voip.enctimestamp;\n#endif\n\tlevel = 0;\n\tsamps=0;\n\t//*2 for 16bit audio input.\n\tfor (encpos = 0, outpos = 0; encpos+s_voip.encframesize*2 <= s_voip.capturepos && outpos+256 < sizeof(outbuf); )\n\t{\n\t\tstart = (short*)(s_voip.capturebuf + encpos);\n\n#ifdef HAVE_SPEEX\n\t\tif (snd_voip_noisefilter.ival || snd_voip_autogain.ival)\n\t\t{\n\t\t\tif (!s_voip.speexdsp.preproc || snd_voip_noisefilter.modified || snd_voip_noisefilter.modified || s_voip.speexdsp.curframesize != s_voip.encframesize || s_voip.speexdsp.cursamplerate != s_voip.encsamplerate)\n\t\t\t{\n\t\t\t\tif (s_voip.speexdsp.preproc)\n\t\t\t\t\tqspeex_preprocess_state_destroy(s_voip.speexdsp.preproc);\n\t\t\t\ts_voip.speexdsp.preproc = NULL;\n\t\t\t\tif (S_SpeexDSP_Init())\n\t\t\t\t{\n\t\t\t\t\tint i;\n\t\t\t\t\ts_voip.speexdsp.preproc = qspeex_preprocess_state_init(s_voip.encframesize, s_voip.encsamplerate);\n\t\t\t\t\ti = snd_voip_noisefilter.ival;\n\t\t\t\t\tqspeex_preprocess_ctl(s_voip.speexdsp.preproc, SPEEX_PREPROCESS_SET_DENOISE, &i);\n\t\t\t\t\ti = snd_voip_autogain.ival;\n\t\t\t\t\tqspeex_preprocess_ctl(s_voip.speexdsp.preproc, SPEEX_PREPROCESS_SET_AGC, &i);\n\n\t\t\t\t\ts_voip.speexdsp.curframesize = s_voip.encframesize;\n\t\t\t\t\ts_voip.speexdsp.cursamplerate = s_voip.encsamplerate;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse if (s_voip.speexdsp.preproc)\n\t\t{\n\t\t\tqspeex_preprocess_state_destroy(s_voip.speexdsp.preproc);\n\t\t\ts_voip.speexdsp.preproc = NULL;\n\t\t}\n#endif\n\n\t\tswitch(s_voip.enccodec)\n\t\t{\n#ifdef HAVE_SPEEX\n\t\tcase VOIP_SPEEX_OLD:\n\t\t\t//this is from before I understood speex properly.\n\t\t\tlevel += S_Voip_Preprocess(start, s_voip.encframesize, micamp);\n\t\t\tqspeex_bits_reset(&s_voip.speex.encbits);\n\t\t\tqspeex_encode_int(s_voip.encoder, start, &s_voip.speex.encbits);\n\t\t\tlen = qspeex_bits_write(&s_voip.speex.encbits, outbuf+(outpos+1), sizeof(outbuf) - (outpos+1));\n\t\t\tif (len < 0 || len > 255)\n\t\t\t\tlen = 0;\n\t\t\toutbuf[outpos] = len;\n\t\t\toutpos += 1+len;\n\t\t\ts_voip.encsequence++;\n\t\t\tsamps+=s_voip.encframesize;\n\t\t\tencpos += s_voip.encframesize*2;\n\t\t\tbreak;\n\t\tcase VOIP_SPEEX_NARROW:\n\t\tcase VOIP_SPEEX_WIDE:\n\t\tcase VOIP_SPEEX_ULTRAWIDE:\n\t\t\t//write multiple speex frames into a single merged frame\n\t\t\tqspeex_bits_reset(&s_voip.speex.encbits);\n\t\t\tfor (; encpos+s_voip.encframesize*2 <= s_voip.capturepos; )\n\t\t\t{\n\t\t\t\tstart = (short*)(s_voip.capturebuf + encpos);\n\t\t\t\tlevel += S_Voip_Preprocess(start, s_voip.encframesize, micamp);\n\t\t\t\tqspeex_encode_int(s_voip.encoder, start, &s_voip.speex.encbits);\n\t\t\t\ts_voip.encsequence++;\n\t\t\t\tsamps+=s_voip.encframesize;\n\t\t\t\tencpos += s_voip.encframesize*2;\n\n\t\t\t\tif (rtpstream)\t//FIXME: why?\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tlen = qspeex_bits_write(&s_voip.speex.encbits, outbuf+outpos, sizeof(outbuf) - outpos);\n\t\t\toutpos += len;\n\t\t\tbreak;\n#endif\n\t\tcase VOIP_RAW16:\n\t\t\tlen = s_voip.capturepos-encpos;\t//amount of data to be eaten in this frame\n\t\t\tlen = min(len, sizeof(outbuf)-outpos);\n\t\t\tlen &= ~((s_voip.encframesize*2)-1);\n\t\t\tlevel += S_Voip_Preprocess(start, len/2, micamp);\n\t\t\tmemcpy(outbuf+outpos, start, len);\t//'encode'\n\t\t\toutpos += len;\t\t\t//bytes written to output\n\t\t\tencpos += len;\t\t\t//number of bytes consumed\n\n\t\t\ts_voip.encsequence++;\t//increment number of packets, for packetloss detection.\n\t\t\tsamps+=len / 2;\t//number of samplepairs eaten in this packet. for stats.\n\t\t\tbreak;\n\t\tcase VOIP_PCMA:\n\t\tcase VOIP_PCMU:\n\t\t\t//FIXME: what's with this /2? these are just 8-bit mono (logarithmic) pcm...\n\t\t\tlen = s_voip.capturepos-encpos;\t//amount of data to be eaten in this frame\n\t\t\tlen = min(len, sizeof(outbuf)-outpos);\n\t\t\tlen = min(len, s_voip.encframesize*2);\n\t\t\tlevel += S_Voip_Preprocess(start, len/2, micamp);\n\t\t\tif (s_voip.enccodec == VOIP_PCMA)\n\t\t\t\toutpos += PCMA_Encode(outbuf+outpos, sizeof(outbuf)-outpos, start, len/2);\n\t\t\telse\n\t\t\t\toutpos += PCMU_Encode(outbuf+outpos, sizeof(outbuf)-outpos, start, len/2);\n\t\t\tencpos += len;\t\t\t//number of bytes consumed\n\t\t\ts_voip.encsequence++;\t//increment number of packets, for packetloss detection.\n\t\t\tsamps+=len / 2;\t//number of samplepairs eaten in this packet. for stats.\n\t\t\tbreak;\n#ifdef HAVE_OPUS\n\t\tcase VOIP_OPUS:\n\t\t\t{\n\t\t\t\t//opus rtp only supports/allows a single chunk in each packet.\n\t\t\t\tint frames;\n\t\t\t\tint nrate;\n\t\t\t\t//densely pack the frames.\n\t\t\t\tstart = (short*)(s_voip.capturebuf + encpos);\n\t\t\t\tframes = (s_voip.capturepos-encpos)/2;\n\n\t\t\t\tnrate = snd_voip_bitrate.value;\n\t\t\t\tif (nrate != s_voip.curbitrate)\n\t\t\t\t{\n\t\t\t\t\ts_voip.curbitrate = nrate;\n\t\t\t\t\tif (nrate == 0)\n\t\t\t\t\t\tnrate = -1000;\n\t\t\t\t\tqopus_encoder_ctl(s_voip.encoder, OPUS_SET_BITRATE_REQUEST, (int)nrate);\n\t\t\t\t\tnrate = 10000;\n\t\t\t\t}\n\n\t\t\t\tif (frames >= 2880)\n\t\t\t\t\tframes = 2880;\n\t\t\t\telse if (frames >= 1920 && nrate > 100)\n\t\t\t\t\tframes = 1920;\n\t\t\t\telse if (frames >= 960 && nrate > 500)\n\t\t\t\t\tframes = 960;\n\t\t\t\telse if (frames >= 480 && nrate > 1000)\n\t\t\t\t\tframes = 480;\n\t\t\t\telse if (snd_voip_send.ival & 4)\n\t\t\t\t\tbreak;\t//don't send small rtp packets, its abusive.\n\t\t\t\telse if (frames >= 240 && nrate > 2000)\n\t\t\t\t\tframes = 240;\n\t\t\t\telse if (frames >= 120 && nrate > 4000)\n\t\t\t\t\tframes = 120;\n\t\t\t\telse\n\t\t\t\t\tbreak;\t//invalid size, wait for more.\n\n\t\t\t\tlevel += S_Voip_Preprocess(start, frames, micamp);\n\t\t\t\tlen = qopus_encode(s_voip.encoder, start, frames, outbuf+outpos, sizeof(outbuf) - outpos);\n\t\t\t\tif (len >= 0)\n\t\t\t\t{\n\t\t\t\t\ts_voip.encsequence += frames / s_voip.encframesize;\n\t\t\t\t\toutpos += len;\n\t\t\t\t\tsamps+=frames;\n\t\t\t\t\tencpos += frames*2;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tCon_Printf(\"Opus encoding error: %i\\n\", len);\n\t\t\t\t\tencpos = s_voip.capturepos;\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n#endif\n\t\tdefault:\n\t\t\toutbuf[outpos] = 0;\n\t\t\tbreak;\n\t\t}\n\n\t\t//opus has no way to detect the end properly.\n\t\t//standard rtp favours many small packets.\n\t\tif (rtpstream || s_voip.enccodec == VOIP_OPUS)\n\t\t\tbreak;\n\t}\n\tif (samps)\n\t{\n\t\tfloat nl;\n\t\ts_voip.enctimestamp += samps;\n\t\tnl = (3000*level) / (32767.0f*32767*samps);\n\t\ts_voip.voiplevel = (s_voip.voiplevel*7 + nl)/8;\n\t\tif (s_voip.voiplevel < snd_voip_vad_threshhold.ival && !voipbutton && !(snd_voip_send.ival & 6))\n\t\t{\n\t\t\t/*try and dump it, it was too quiet, and they're not pressing +voip*/\n\t\t\tif (s_voip.keeps > samps)\n\t\t\t{\n\t\t\t\t/*but not instantly*/\n\t\t\t\ts_voip.keeps -= samps;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\toutpos = 0;\n\t\t\t\ts_voip.dumps += samps;\n\t\t\t\ts_voip.keeps = 0;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t\ts_voip.keeps = s_voip.encsamplerate * snd_voip_vad_delay.value;\n\t\tif (outpos)\n\t\t{\n\t\t\tif (s_voip.dumps > s_voip.encsamplerate/4)\n\t\t\t\ts_voip.generation++;\n\t\t\ts_voip.dumps = 0;\n\t\t}\n\t}\n\n\tif (outpos)\n\t{\n\t\tif (buf && !(snd_voip_send.ival & 4))\n\t\t{\n\t\t\tif (buf->maxsize - buf->cursize >= 5+outpos)\n\t\t\t{\n\t\t\t\tqbyte cgen = ((s_voip.enccodec&0x7)<<4) | (s_voip.generation & 0x0f);\n\t\t\t\tif (s_voip.enccodec >= 8 || 0)\n\t\t\t\t\tcgen |= 0x80;\n\n\t\t\t\tMSG_WriteByte(buf, clc);\n\t\t\t\tMSG_WriteByte(buf, cgen);\n\t\t\t\tMSG_WriteByte(buf, initseq&0xff);\n\t\t\t\t/*if (cgen & 0x80)\n\t\t\t\t{\n\t\t\t\t\tMSG_WriteShort(buf, 1+outpos);\n\t\t\t\t\tMSG_WriteByte(buf, s_voip.enccodec>>3);\n\t\t\t\t}\n\t\t\t\telse*/\n\t\t\t\t\tMSG_WriteShort(buf, outpos);\t//even with codecs where the size is easy to determine, this is still useful for servers (which are unaware of the actual codec)\n\t\t\t\tSZ_Write(buf, outbuf, outpos);\n\t\t\t}\n\t\t\telse\n\t\t\t\tCon_Printf(\"Audio frame too small %i vs %i\\n\", outpos+4, buf->maxsize - buf->cursize);\n\t\t}\n\n#ifdef SUPPORT_ICE\n\t\tif (rtpstream)\n\t\t{\n\t\t\tswitch(s_voip.enccodec)\n\t\t\t{\n#ifdef HAVE_SPEEX\n\t\t\tcase VOIP_SPEEX_NARROW:\n\t\t\tcase VOIP_SPEEX_WIDE:\n\t\t\tcase VOIP_SPEEX_ULTRAWIDE:\n\t\t\tcase VOIP_SPEEX_OLD:\n\t\t\t\tNET_RTP_Transmit(initseq, inittimestamp, va(\"speex@%i\", s_voip.encsamplerate), outbuf, outpos);\n\t\t\t\tbreak;\n#endif\n\t\t\tcase VOIP_PCMA:\n\t\t\t\tNET_RTP_Transmit(initseq, inittimestamp, \"pcma@8000\", outbuf, outpos);\n\t\t\t\tbreak;\n\t\t\tcase VOIP_PCMU:\n\t\t\t\tNET_RTP_Transmit(initseq, inittimestamp, \"pcmu@8000\", outbuf, outpos);\n\t\t\t\tbreak;\n#ifdef HAVE_OPUS\n\t\t\tcase VOIP_OPUS:\n\t\t\t\tNET_RTP_Transmit(initseq, inittimestamp, \"opus@48000\", outbuf, outpos);\n\t\t\t\tbreak;\n#endif\n\t\t\t}\n\t\t}\n#endif\n\n\t\tif (snd_voip_test.ival)\n\t\t\tS_Voip_Decode(cl.playerview[0].playernum, s_voip.enccodec, s_voip.generation & 0x0f, initseq&0xff, outpos, outbuf);\n\n\t\t//update our own lastspoke, so queries shows that we're speaking when we're speaking in a generic way, even if we can't hear ourselves.\n\t\t//but don't update general lastspoke, so ducking applies only when others speak. use capturingvol for yourself. they're more explicit that way.\n\t\ts_voip.lastspoke[cl.playerview[0].playernum] = realtime + 0.5;\n\t}\n\n\t/*remove sent data*/\n\tif (encpos)\n\t{\n\t\tmemmove(s_voip.capturebuf, s_voip.capturebuf + encpos, s_voip.capturepos-encpos);\n\t\ts_voip.capturepos -= encpos;\n\t}\n}\nvoid S_Voip_Ignore(unsigned int slot, qboolean ignore)\n{\n\tCL_SendClientCommand(true, \"vignore %i %i\", slot, ignore);\n}\nstatic void S_Voip_Enable_f(void)\n{\n\tif (Cmd_IsInsecure())\n\t\treturn;\n\tvoipbutton = true;\n}\nstatic void S_Voip_Disable_f(void)\n{\n\tvoipbutton = false;\n}\nstatic void S_Voip_f(void)\n{\n#ifdef HAVE_SPEEX\n\tif (!strcmp(Cmd_Argv(1), \"maxgain\"))\n\t{\n\t\tint i = atoi(Cmd_Argv(2));\n\t\tif (s_voip.speexdsp.preproc)\n\t\t\tqspeex_preprocess_ctl(s_voip.speexdsp.preproc, SPEEX_PREPROCESS_SET_AGC_MAX_GAIN, &i);\n\t}\n\telse\n#endif\n\t{\n\t\tCon_Printf(\"unrecognised parameter \\\"%s\\\"\\n\", Cmd_Argv(1));\n\t}\n}\nstatic void QDECL S_Voip_Play_Callback(cvar_t *var, char *oldval)\n{\n\tif (cls.fteprotocolextensions2 & PEXT2_VOICECHAT)\n\t{\n\t\tif (var->value > 0)\n\t\t\tCL_SendClientCommand(true, \"unmuteall\");\n\t\telse\n\t\t\tCL_SendClientCommand(true, \"muteall\");\n\t}\n}\nvoid S_Voip_MapChange(void)\n{\n\tvoipbutton = false;\n\tCvar_ForceCallback(&snd_voip_play);\n}\nint S_Voip_Loudness(qboolean ignorevad)\n{\n\tif (!s_voip.cdriverctx || (!ignorevad && s_voip.dumps))\n\t\treturn -1;\n\tif (s_voip.voiplevel > 100)\n\t\treturn 100;\n\treturn s_voip.voiplevel;\n}\nint S_Voip_ClientLoudness(unsigned int plno)\n{\n\tif (plno >= MAX_CLIENTS)\n\t\treturn 0;\n\tif (s_voip.lastspoke[plno] > realtime)\n\t\treturn s_voip.declevel[plno];\n\treturn -1;\n}\nqboolean S_Voip_Speaking(unsigned int plno)\n{\n\tif (plno >= MAX_CLIENTS)\n\t\treturn false;\n\treturn s_voip.lastspoke[plno] > realtime;\n}\n\nvoid S_Voip_Init(void)\n{\n\tint i;\n\tfor (i = 0; i < MAX_CLIENTS; i++)\n\t\ts_voip.deccodec[i] = VOIP_INVALID;\n\ts_voip.enccodec = VOIP_INVALID;\n\n\tCvar_Register(&snd_voip_capturedevice,\t\t\"Voice Chat\");\n\tCvar_Register(&snd_voip_capturedevice_opts,\t\t\"Voice Chat\");\n\tCvar_Register(&snd_voip_send,\t\t\"Voice Chat\");\n\tCvar_Register(&snd_voip_vad_threshhold,\t\"Voice Chat\");\n\tCvar_Register(&snd_voip_vad_delay,\t\"Voice Chat\");\n\tCvar_Register(&snd_voip_capturingvol,\t\"Voice Chat\");\n\tCvar_Register(&snd_voip_showmeter,\t\"Voice Chat\");\n\tCvar_Register(&snd_voip_play,\t\t\"Voice Chat\");\n\tCvar_Register(&snd_voip_test,\t\t\"Voice Chat\");\n\tCvar_Register(&snd_voip_ducking,\t\t\"Voice Chat\");\n\tCvar_Register(&snd_voip_micamp,\t\t\"Voice Chat\");\n\tCvar_Register(&snd_voip_codec,\t\t\"Voice Chat\");\n#ifdef HAVE_SPEEX\n\tCvar_Register(&snd_voip_noisefilter,\t\t\"Voice Chat\");\n\tCvar_Register(&snd_voip_autogain,\t\t\"Voice Chat\");\n#endif\n\tCvar_Register(&snd_voip_bitrate,\t\t\"Voice Chat\");\n\tCmd_AddCommand(\"+voip\", S_Voip_Enable_f);\n\tCmd_AddCommand(\"-voip\", S_Voip_Disable_f);\n\tCmd_AddCommand(\"voip\", S_Voip_f);\n}\n#else\nvoid S_Voip_Parse(void)\n{\n\tunsigned int bytes;\n\n\tMSG_ReadByte();\n\tMSG_ReadByte();\n\tMSG_ReadByte();\n\tbytes = MSG_ReadShort();\n\tMSG_ReadSkip(bytes);\n}\nint S_Voip_ClientLoudness(unsigned int plno)\n{\n\treturn -1;\n}\n#endif\n\n\n\nvoid S_DefaultSpeakerConfiguration(soundcardinfo_t *sc)\n{\n\tsc->dist[0] = 1;\n\tsc->dist[1] = 1;\n\tsc->dist[2] = 1;\n\tsc->dist[3] = 1;\n\tsc->dist[4] = 1;\n\tsc->dist[5] = 1;\n\n\tswitch (sc->sn.numchannels)\n\t{\n\tcase 1:\n\t\tVectorSet(sc->speakerdir[0], 0, 0, 0);\n\t\tbreak;\n\tcase 2:\n\tcase 3:\n\t\tVectorSet(sc->speakerdir[0], 0, -1, 0);\n\t\tVectorSet(sc->speakerdir[1], 0, 1, 0);\n\t\tVectorSet(sc->speakerdir[2], 0, 0, 0);\n\t\tbreak;\n\tcase 4: // quad\n\tcase 5:\n\t\tVectorSet(sc->speakerdir[0], 0.7, -0.7, 0);\n\t\tVectorSet(sc->speakerdir[1], 0.7, 0.7, 0);\n\t\tVectorSet(sc->speakerdir[2], -0.7, -0.7, 0);\n\t\tVectorSet(sc->speakerdir[3], -0.7, 0.7, 0);\n\t\tVectorSet(sc->speakerdir[4], 0, 0, 0);\n\t\tbreak;\n\tcase 6: // 5.1\n\tcase 7:\n\t\tVectorSet(sc->speakerdir[0], 0.7, -0.7, 0);\t//front-left\n\t\tVectorSet(sc->speakerdir[1], 0.7, 0.7, 0);\t//front-right\n\t\tVectorSet(sc->speakerdir[2], 1, 0, 0);\t\t//center\n\t\tVectorSet(sc->speakerdir[3], 0, 0, 0);\t\t//bass\n\t\tVectorSet(sc->speakerdir[4], -0.7, -0.7, 0);//back-left\n\t\tVectorSet(sc->speakerdir[5], -0.7, 0.7, 0);\t//back-right\n\t\tVectorSet(sc->speakerdir[6], 0, 0, 0);\n\t\tbreak;\n\tcase 8: // 7.1\n\tdefault:\n\t\tVectorSet(sc->speakerdir[0], 0.7, -0.7, 0);\n\t\tVectorSet(sc->speakerdir[1], 0.7, 0.7, 0);\n\t\tVectorSet(sc->speakerdir[2], 1, 0, 0);\n\t\tVectorSet(sc->speakerdir[3], 0, 0, 0);\n\t\tVectorSet(sc->speakerdir[4], -0.7, -0.7, 0);\n\t\tVectorSet(sc->speakerdir[5], -0.7, 0.7, 0);\n\t\tVectorSet(sc->speakerdir[6], 0, -1, 0);\n\t\tVectorSet(sc->speakerdir[7], 0, 1, 0);\n\t\tbreak;\n\t}\n}\n\n#ifdef AVAIL_WASAPI\nextern sounddriver_t WASAPI_Output;\n#endif\n#ifdef AVAIL_XAUDIO2\nextern sounddriver_t XAUDIO2_Output;\n#endif\n#ifdef AVAIL_DSOUND\nextern sounddriver_t DSOUND_Output;\n#endif\nsounddriver_t fte_weakstruct SDL_Output;\n#if defined(__unix__) && !defined(__APPLE__) // Alsa, OSS and PulseAudio can all be installed on most Unixes these days; They can fall back to OSS_Output if needed - Brad\nextern sounddriver_t ALSA_Output;\nextern sounddriver_t Pulse_Output;\n#endif\nsounddriver_t fte_weakstruct OSS_Output;\n#ifdef AVAIL_OPENAL\nextern sounddriver_t OPENAL_Output;\nextern sounddriver_t OPENAL_Output_Lame;\n#endif\n#ifdef __DJGPP__\nextern sounddriver_t SBLASTER_Output;\n#endif\n#if defined(_WIN32) && !defined(WINRT) && !defined(FTE_SDL)\nextern sounddriver_t WaveOut_Output;\n#endif\n\n#ifdef MACOSX\nsounddriver_t fte_weakstruct MacOS_AudioOutput;\t//prefered on mac\n#endif\n#ifdef ANDROID\nsounddriver_t fte_weakstruct OSL_Output;\t\t\t//general audio library, but android has all kinds of quirks.\nsounddriver_t fte_weakstruct Droid_AudioOutput;\n#endif\n#if defined(__MORPHOS__)\nsounddriver_t fte_weakstruct AHI_AudioOutput;\t\t//prefered on morphos\n#endif\nsounddriver_t fte_weakstruct SNDIO_AudioOutput;\t//bsd\n\n//in order of preference\nstatic sounddriver_t *outputdrivers[] =\n{\n#ifdef AVAIL_OPENAL\n\t&OPENAL_Output,\t//refuses to run as the default device, at least until its perfected.\n#endif\n\n#ifdef HAVE_MIXER\n#ifdef AVAIL_DSOUND\n\t&DSOUND_Output,\n#endif\n#ifdef AVAIL_XAUDIO2\n\t&XAUDIO2_Output,\n#endif\n#ifdef AVAIL_WASAPI\n\t&WASAPI_Output,\t//this is last, so that we can default to exclusive. woot.\n#endif\n\n\t&SDL_Output,\t\t//prefered on linux. distros can ensure that its configured correctly.\n#ifdef AUDIO_PULSE\n\t&Pulse_Output,\t\t//wasteful, and availability generally means Alsa is broken/defective.\n#endif\n#ifdef AUDIO_ALSA\n\t&ALSA_Output,\t\t//pure shite, and availability generally means OSS is broken/defective.\n#endif\n#ifdef AUDIO_OSS\n\t&OSS_Output,\t\t//good for low latency audio, but not likely to work any more on linux (unlike every other unix system with a decent opengl driver)\n#endif\n#ifdef __DJGPP__\n\t&SBLASTER_Output,\t//zomgwtfdos?\n#endif\n#if defined(_WIN32) && !defined(WINRT) && !defined(FTE_SDL)\n\t&WaveOut_Output,\t//doesn't work properly in vista, etc.\n#endif\n\n#ifdef MACOSX\n\t&MacOS_AudioOutput,\t//prefered on mac\n#endif\n#ifdef ANDROID\n\t&OSL_Output,\t\t//opensl(es)\n#endif\n#if defined(__MORPHOS__)\n\t&AHI_AudioOutput,\t//prefered on morphos\n#endif\n\t&SNDIO_AudioOutput,\t//prefered on OpenBSD\n\n#ifdef AVAIL_OPENAL\n\t&OPENAL_Output_Lame,//streaming quake's audio via openal instead of using openal properly. used in our browser port to work around issues with webaudio (at least in chromium).\n#endif\n#endif\n\tNULL\n};\n\nstatic soundcardinfo_t *SNDDMA_Init(char *driver, char *device, int seat)\n{\n\tsoundcardinfo_t *sc = Z_Malloc(sizeof(soundcardinfo_t));\n\tsounddriver_t *sd;\n\tint i;\n\tint st;\n\n\tmemset(sc, 0, sizeof(*sc));\n\tsc->seat = seat;\n\n\tsc->next = sndcardinfo;\n\tsndcardinfo = sc;\n\n\t// set requested rate\n\tif (snd_khz.ival >= 1000)\n\t\tsc->sn.speed = snd_khz.ival;\n\telse if (snd_khz.ival <= 0)\n\t\tsc->sn.speed = 22050;\n/*\telse if (snd_khz.ival >= 195)\n\t\tsc->sn.speed = 200000;\n\telse if (snd_khz.ival >= 180)\n\t\tsc->sn.speed = 192000;\n\telse if (snd_khz.ival >= 90)\n\t\tsc->sn.speed = 96000; */\n\telse if (snd_khz.ival >= 45)\n\t\tsc->sn.speed = 48000;\n\telse if (snd_khz.ival >= 30)\n\t\tsc->sn.speed = 44100;\n\telse if (snd_khz.ival >= 20)\n\t\tsc->sn.speed = 22050;\n\telse if (snd_khz.ival >= 10)\n\t\tsc->sn.speed = 11025;\n\telse\n\t\tsc->sn.speed = 8000;\n\n\t// set requested speaker count\n\tif (snd_speakers.ival > MAXSOUNDCHANNELS)\n\t\tsc->sn.numchannels = MAXSOUNDCHANNELS;\n\telse if (snd_speakers.ival > 1)\n\t\tsc->sn.numchannels = (int)snd_speakers.ival;\n\telse\n\t\tsc->sn.numchannels = 1;\n\n\t// set requested sample bits\n\tif (snd_samplebits.ival >= 32)\n\t\tsc->sn.samplebytes = 4;\n\telse if (snd_samplebits.ival >= 16)\n\t\tsc->sn.samplebytes = 2;\n\telse\n\t\tsc->sn.samplebytes = 1;\n\n\t// set requested buffer size\n\tif (snd_buffersize.ival > 0)\n\t\tsc->sn.samples = snd_buffersize.ival * sc->sn.numchannels;\n\telse\n\t\tsc->sn.samples = 0;\n\n\tfor (i = 0; outputdrivers[i]; i++)\n\t{\n\t\tsd = outputdrivers[i];\n\t\tif (sd && sd->name && (!driver || !Q_strcasecmp(sd->name, driver)))\n\t\t{\n\t\t\t//skip drivers which are not present.\n\t\t\tif (!sd->InitCard)\n\t\t\t\tcontinue;\n\n\t\t\tst = (**sd->InitCard)(sc, device);\n\t\t\tif (st)\n\t\t\t{\n\t\t\t\tif (!sc->sn.sampleformat)\n\t\t\t\t{\n\t\t\t\t\tCon_TPrintf(\"S_Startup: Ignoring soundcard %s due to unspecified sample format.\\n\", sc->name);\n\t\t\t\t\tS_ShutdownCard(sc);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tS_DefaultSpeakerConfiguration(sc);\n\t\t\t\tif (snd_speed)\n\t\t\t\t{\t//if the sample speeds of multiple soundcards do not match, it'll fail.\n\t\t\t\t\tif (snd_speed != sc->sn.speed)\n\t\t\t\t\t{\n\t\t\t\t\t\tCon_TPrintf(\"S_Startup: Ignoring soundcard %s due to mismatched sample speeds.\\n\", sc->name);\n\t\t\t\t\t\tS_ShutdownCard(sc);\n\t\t\t\t\t\treturn NULL;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tsnd_speed = sc->sn.speed;\n\n\t\t\t\tif (sc->seat == -1 && sc->ListenerUpdate)\n\t\t\t\t\tsc->seat = 0;\t//hardware rendering won't cope with seat=-1\n\n\t\t\t\tZ_ReallocElements((void**)&sc->channel, &sc->max_chans, NUM_AMBIENTS+NUM_MUSICS, sizeof(*sc->channel));\n\t\t\t\treturn sc;\n\t\t\t}\n\t\t}\n\t}\n\n\tS_ShutdownCard(sc);\n\n\tif (!driver)\n\t\tCon_TPrintf(\"Could not start audio device \\\"%s\\\"\\n\", device?device:\"default\");\n\telse\n\t\tCon_TPrintf(\"Could not start \\\"%s\\\" device \\\"%s\\\"\\n\", driver, device?device:\"default\");\n\treturn NULL;\n}\n\nsoundcardinfo_t *S_SetupDeviceSeat(char *driver, char *device, int seat)\n{\n\treturn SNDDMA_Init(driver, device, seat);\n\t/*\n\tsoundcardinfo_t *sc;\n\tfor (sc = sndcardinfo; sc; sc = sc->next)\n\t{\n\t\tsc->seat = seat;\n\t}*/\n}\n\nstatic void QDECL S_EnumeratedOutDevice(const char *driver, const char *devicecode, const char *readabledevice)\n{\n\tconst char *fullintname;\n\tchar opts[8192];\n\tchar nbuf[1024];\n\tchar dbuf[1024];\n\t\n\tif (devicecode)\n\t\tfullintname = va(\"%s:%s\", driver, devicecode);\n\telse\n\t\tfullintname = driver;\n\n\tQ_snprintfz(opts, sizeof(opts), \"%s%s%s %s\", snd_device_opts.string, *snd_device_opts.string?\" \":\"\", COM_QuotedString(fullintname, nbuf, sizeof(nbuf), false), COM_QuotedString(readabledevice, dbuf, sizeof(dbuf), false));\n\tCvar_ForceSet(&snd_device_opts, opts);\n}\n#ifdef VOICECHAT\nstatic void QDECL S_Voip_EnumeratedCaptureDevice(const char *driver, const char *devicecode, const char *readabledevice)\n{\n\tconst char *fullintname;\n\tchar opts[8192];\n\tchar nbuf[1024];\n\tchar dbuf[1024];\n\n\tif (devicecode)\n\t\tfullintname = va(\"%s:%s\", driver, devicecode);\n\telse\n\t\tfullintname = driver;\n\t\n\tQ_snprintfz(opts, sizeof(opts), \"%s%s%s %s\", snd_voip_capturedevice_opts.string, *snd_voip_capturedevice_opts.string?\" \":\"\", COM_QuotedString(fullintname, nbuf, sizeof(nbuf), false), COM_QuotedString(readabledevice, dbuf, sizeof(dbuf), false));\n\tCvar_ForceSet(&snd_voip_capturedevice_opts, opts);\n}\n#endif\nvoid S_EnumerateDevices(void)\n{\n\tint i;\n\tsounddriver_t *sd;\n\tqboolean safe = COM_CheckParm(\"-noenumerate\") || COM_CheckParm(\"-safe\");\n\n\tCvar_ForceSet(&snd_device_opts, \"\");\n\tS_EnumeratedOutDevice(\"\", NULL, \"Default\");\n\tS_EnumeratedOutDevice(\"none\", NULL, \"None\");\n\n\tfor (i = 0; outputdrivers[i]; i++)\n\t{\n\t\tsd = outputdrivers[i];\n\t\tif (sd && sd->name)\n\t\t{\n\t\t\tif (safe || !sd->Enumerate || !sd->Enumerate(S_EnumeratedOutDevice))\n\t\t\t\tS_EnumeratedOutDevice(sd->name, \"\", va(\"Default %s\", sd->name));\n\t\t}\n\t}\n\n#ifdef VOICECHAT\n\tCvar_ForceSet(&snd_voip_capturedevice_opts, \"\");\n\tS_Voip_EnumeratedCaptureDevice(\"\", NULL, \"Default\");\n\tfor (i = 0; capturedrivers[i]; i++)\n\t{\n\t\tif (!capturedrivers[i]->Init)\n\t\t\tcontinue;\n\t\tif (safe || !capturedrivers[i]->Enumerate || !capturedrivers[i]->Enumerate(S_Voip_EnumeratedCaptureDevice))\n\t\t\tS_Voip_EnumeratedCaptureDevice(capturedrivers[i]->drivername, NULL, va(\"Default %s\", capturedrivers[i]->drivername));\n\t}\n#endif\n}\n\n/*\n================\nS_Startup\n================\n*/\n\nvoid S_ClearRaw(void);\nvoid S_Startup (void)\n{\n\tqboolean nodefault = false;\n\tchar *s;\n\n\tif (!snd_initialized)\n\t\treturn;\n\n\tif (sound_started)\n\t\tS_Shutdown(false);\n\n\tsnd_blocked = 0;\n\tsnd_speed = 0;\n\n\tS_UpdateReverb(0, NULL, 0);\n\t{\t//we can actually use underwater hints automatically easily enough. q3 also does this.\n\t\t//its other things that are more awkward.\n\t\tstruct reverbproperties_s underwater = REVERB_PRESET_UNDERWATER;\n\t\tS_UpdateReverb(1, &underwater, sizeof(underwater));\n\t}\n\n\tfor (s = snd_device.string; ; )\n\t{\n\t\tchar *sep;\n\t\ts = COM_Parse(s);\n\t\tif (!*com_token)\n\t\t\tbreak;\n\n\t\tif (!Q_strcasecmp(com_token, \"none\"))\n\t\t\tnodefault = true;\n\t\telse\n\t\t{\n\t\t\tsep = strchr(com_token, ':');\n\t\t\tif (sep)\n\t\t\t\t*sep++ = 0;\n\t\t\tSNDDMA_Init(com_token, sep, -1);\n\t\t}\n\t}\n\tif (!sndcardinfo && !nodefault)\n\t{\n#if defined(_WIN32) && !defined(FTE_SDL)\n\t\tINS_SetupControllerAudioDevices(true);\n#endif\n\t\tif (!sndcardinfo)\n\t\t\tSNDDMA_Init(NULL, NULL, -1);\n\t}\n\n\tsound_started = true;\n\n\tS_ClearRaw();\n\n\tif (!known_sfx)\n\t\tknown_sfx = Z_Malloc(MAX_SFX*sizeof(sfx_t));\n\tnum_sfx = 0;\n\n\tCL_InitTEntSounds();\n\n\tambient_sfx[AMBIENT_WATER] = S_PrecacheSound (\"ambience/water1.wav\");\n\tambient_sfx[AMBIENT_SKY] = S_PrecacheSound (\"ambience/wind2.wav\");\n}\n\n//why isn't this part of S_Restart_f anymore?\n//so that the video code can call it directly without flushing the models it's just loaded.\nvoid S_DoRestart (qboolean onlyifneeded)\n{\n\tint i;\n\tif (onlyifneeded && sound_started)\n\t\treturn;\t//don't need to if its already running.\n\n\tS_StopAllSounds (true);\n\tS_Shutdown(false);\n\n\tif (nosound.ival)\n\t\treturn;\n\n\tS_Startup();\n\n\tS_StopAllSounds (true);\n\n\n\tfor (i=1 ; i<MAX_PRECACHE_SOUNDS ; i++)\n\t{\n\t\tif (!cl.sound_name[i])\n\t\t\tbreak;\n\t\tcl.sound_precache[i] = S_FindName (cl.sound_name[i], true, false);\n\t}\n}\n\nvoid S_Restart_f (void)\n{\n\tS_EnumerateDevices();\n\n\tS_DoRestart(false);\n}\n\nvoid S_Control_f (void)\n{\n\tint i;\n\tchar *command;\n\n\tcommand = Cmd_Argv (1);\n\n\tif (!Q_strcasecmp(command, \"off\"))\n\t{\n\t\tCache_Flush();//forget the old sounds.\n\n\t\tS_StopAllSounds (true);\n\n\t\tS_Shutdown(false);\n\t\tsound_started = 0;\n\t}\n\n\tif (!Q_strcasecmp(command, \"rate\") || !Q_strcasecmp(command, \"speed\"))\n\t{\n\t\tCvar_SetValue(&snd_khz, atof(Cmd_Argv (2))/1000);\n\t\tS_Restart_f();\n\t\treturn;\n\t}\n\n\t//individual device control\n\tif (!Q_strncasecmp(command, \"card\", 4))\n\t{\n\t\tint card;\n\t\tsoundcardinfo_t *sc;\n\t\tcard = atoi(command+4);\n\n\t\tfor (i = 0, sc = sndcardinfo; i < card && sc; i++,sc=sc->next)\n\t\t\t;\n\n\t\tif (!sc)\n\t\t{\n\t\t\tCon_Printf(\"Sound card %i is invalid (try resetting first)\\n\", card);\n\t\t\treturn;\n\t\t}\n\t\tif (Cmd_Argc() < 3)\n\t\t{\n\t\t\tCon_Printf(\"Scard %i is %s\\n\", card, sc->name);\n\t\t\treturn;\n\t\t}\n\t\tcommand = Cmd_Argv (2);\n\t\tif (!Q_strcasecmp(command, \"mono\"))\n\t\t{\n\t\t\tfor (i = 0; i < MAXSOUNDCHANNELS; i++)\n\t\t\t{\n\t\t\t\tVectorSet(sc->speakerdir[i], 0, 0, 0);\n\t\t\t\tsc->dist[i] = 1;\n\t\t\t}\n\t\t}\n\t\telse if (!Q_strcasecmp(command, \"standard\") || !Q_strcasecmp(command, \"stereo\"))\n\t\t{\n\t\t\tfor (i = 0; i < MAXSOUNDCHANNELS; i++)\n\t\t\t{\n\t\t\t\tVectorSet(sc->speakerdir[i], 0, (i&1)?1:-1, 0);\n\t\t\t\tsc->dist[i] = 1;\n\t\t\t}\n\t\t}\n\t\telse if (!Q_strcasecmp(command, \"swap\"))\n\t\t{\n\t\t\tfor (i = 0; i < MAXSOUNDCHANNELS; i++)\n\t\t\t{\n\t\t\t\tsc->speakerdir[i][1] *= -1;\n\t\t\t}\n\t\t}\n\t\telse if (!Q_strcasecmp(command, \"front\"))\n\t\t{\n\t\t\tfor (i = 0; i < MAXSOUNDCHANNELS; i++)\n\t\t\t{\n\t\t\t\tVectorSet(sc->speakerdir[i], 0.7, (i&1)?-0.7:0.7, 0);\n\t\t\t\tsc->dist[i] = 1;\n\t\t\t}\n\t\t}\n\t\telse if (!Q_strcasecmp(command, \"back\"))\n\t\t{\n\t\t\tfor (i = 0; i < MAXSOUNDCHANNELS; i++)\n\t\t\t{\n\t\t\t\tVectorSet(sc->speakerdir[i], -0.7, (i&1)?-0.7:0.7, 0);\n\t\t\t\tsc->dist[i] = 1;\n\t\t\t}\n\t\t}\n\t\treturn;\n\t}\n\telse\n\t\tCon_Printf(\"valid commands are: off, single, multi, cardX mono, cardX stereo, cardX front, cardX back\\n\");\n}\n\n/*\n================\nS_Init\n================\n*/\nvoid S_Init (void)\n{\n\tint p, i;\n\n\tCon_DPrintf(\"\\nSound Initialization\\n\");\n\n\tCmd_AddCommand(\"play\", S_Play_f);\t//sound that doesn't follow the player\n\tCmd_AddCommand(\"play2\", S_Play_f);\t//sound that DOES follow the player\n\tCmd_AddCommand(\"playvol\", S_Play_f);\n\tCmd_AddCommand(\"stopsound\", S_StopAllSounds_f);\n\tCmd_AddCommand(\"soundlist\", S_SoundList_f);\n\tCmd_AddCommand(\"soundinfo\", S_SoundInfo_f);\n\n\tCmd_AddCommand(\"snd_restart\", S_Restart_f);\n\n\tCmd_AddCommand(\"soundcontrol\", S_Control_f);\n\n\tCvar_Register(&nosound,\t\t\t\t\"Sound controls\");\n\tCvar_Register(&mastervolume,\t\t\"Sound controls\");\n\tCvar_Register(&volume,\t\t\t\t\"Sound controls\");\n\tCvar_Register(&snd_precache,\t\t\"Sound controls\");\n\tCvar_Register(&snd_loadas8bit,\t\t\"Sound controls\");\n\tCvar_Register(&snd_loadasstereo,\t\"Sound controls\");\n\tCvar_Register(&bgmvolume,\t\t\t\"Sound controls\");\n\tCvar_Register(&snd_nominaldistance,\t\"Sound controls\");\n\tCvar_Register(&ambient_level,\t\t\"Sound controls\");\n\tCvar_Register(&ambient_fade,\t\t\"Sound controls\");\n\tCvar_Register(&snd_noextraupdate,\t\"Sound controls\");\n\tCvar_Register(&snd_show,\t\t\t\"Sound controls\");\n\tCvar_Register(&_snd_mixahead,\t\t\"Sound controls\");\n\tCvar_Register(&snd_khz,\t\t\t\t\"Sound controls\");\n\tCvar_Register(&snd_leftisright,\t\t\"Sound controls\");\n\tCvar_Register(&snd_eax,\t\t\t\t\"Sound controls\");\n\tCvar_Register(&snd_speakers,\t\t\"Sound controls\");\n\tCvar_Register(&snd_buffersize,\t\t\"Sound controls\");\n\tCvar_Register(&snd_samplebits,\t\t\"Sound controls\");\n\tCvar_Register(&snd_playbackrate,\t\"Sound controls\");\n\tCvar_Register(&snd_ignoregamespeed,\t\"Sound controls\");\n\tCvar_Register(&snd_doppler,\t\t\t\"Sound controls\");\n\tCvar_Register(&snd_doppler_min,\t\t\"Sound controls\");\n\tCvar_Register(&snd_doppler_max,\t\t\"Sound controls\");\n\n\tCvar_Register(&snd_inactive,\t\t\"Sound controls\");\n\n#ifdef MULTITHREAD\n\tCvar_Register(&snd_mixerthread,\t\t\t\t\"Sound controls\");\n#endif\n\tCvar_Register(&snd_playersoundvolume,\t\t\"Sound controls\");\n\tCvar_Register(&snd_device,\t\t\"Sound controls\");\n\tCvar_Register(&snd_device_opts,\t\t\"Sound controls\");\n\n\tCvar_Register(&snd_ignorecueloops, \"Sound controls\");\n\tCvar_Register(&snd_linearresample, \"Sound controls\");\n\tCvar_Register(&snd_linearresample_stream, \"Sound controls\");\n\n#ifdef VOICECHAT\n\tS_Voip_Init();\n#endif\n\n#ifdef MULTITHREAD\n\tmixermutex = Sys_CreateMutex();\n#endif\n\n\tfor (i = 0; outputdrivers[i]; i++)\n\t{\n\t\tsounddriver_t *sd = outputdrivers[i];\n\t\tif (sd && sd->name && sd->RegisterCvars)\n\t\t\tsd->RegisterCvars();\n\t}\n\n\tif (COM_CheckParm(\"-nosound\"))\n\t{\n\t\tCvar_ForceSet(&nosound, \"1\");\n\t\tnosound.flags |= CVAR_NOSET;\n\t\treturn;\n\t}\n\n\tS_EnumerateDevices();\n\n\tp = COM_CheckParm (\"-soundspeed\");\n\tif (!p)\n\t\tp = COM_CheckParm (\"-sspeed\");\n\tif (!p)\n\t\tp = COM_CheckParm (\"-sndspeed\");\n\tif (p)\n\t{\n\t\tif (p < com_argc-1)\n\t\t\tCvar_SetValue(&snd_khz, atof(com_argv[p+1]));\n\t\telse\n\t\t\tSys_Error (\"S_Init: you must specify a speed in KB after -soundspeed\");\n\t}\n\n\tsnd_initialized = true;\n\n\tknown_sfx = Z_Malloc(MAX_SFX*sizeof(sfx_t));\n\tnum_sfx = 0;\n}\n\n\n// =======================================================================\n// Shutdown sound engine\n// =======================================================================\n\nvoid S_ShutdownCard(soundcardinfo_t *sc)\n{\n\tsoundcardinfo_t **link;\n\n\tfor (link = &sndcardinfo; *link; link = &(*link)->next)\n\t{\n\t\tif (*link == sc)\n\t\t{\n\t\t\t*link = sc->next;\n\t\t\tif (sc->Shutdown)\n\t\t\t\tsc->Shutdown(sc);\n\t\t\tZ_Free(sc->channel);\n\t\t\tZ_Free(sc);\n\t\t\tbreak;\n\t\t}\n\t}\n}\nvoid S_Shutdown(qboolean final)\n{\n\tsoundcardinfo_t *sc, *next;\n\n#if defined(_WIN32) && !defined(FTE_SDL)\n\tINS_SetupControllerAudioDevices(false);\n#endif\n\n\tfor (sc = sndcardinfo; sc; sc=next)\n\t{\n\t\tnext = sc->next;\n\t\tsc->Shutdown(sc);\n\t\tZ_Free(sc->channel);\n\t\tZ_Free(sc);\n\t\tsndcardinfo = next;\n\t}\n\n\tsound_started = 0;\n\tS_Purge(false);\n\n\tZ_Free(known_sfx);\n\tknown_sfx = NULL;\n\tnum_sfx = 0;\n\n\tif (final)\n\t{\n\t\tZ_Free(reverbproperties);\n\t\treverbproperties = NULL;\n\t\tnumreverbproperties = 0;\n\t}\n\n#ifdef MULTITHREAD\n\tif (final && mixermutex)\n\t{\n\t\tSys_DestroyMutex(mixermutex);\n\t\tmixermutex = NULL;\n\t}\n#endif\n}\n\n\n// =======================================================================\n// Load a sound\n// =======================================================================\n\n/*\n==================\nS_FindName\n\nalso touches it\n==================\n*/\nsfx_t *S_FindName (const char *name, qboolean create, qboolean syspath)\n{\n\tint\t\ti;\n\tsfx_t\t*sfx;\n\n\tif (!name)\n\t\tSys_Error (\"S_FindName: NULL\\n\");\n\n\tif (Q_strlen(name) >= MAX_OSPATH)\n\t\tSys_Error (\"Sound name too long: %s\", name);\n\n// see if already loaded\n\tfor (i=0 ; i < num_sfx ; i++)\n\t\tif (!Q_strcmp(known_sfx[i].name, name) && known_sfx[i].syspath == syspath)\n\t\t{\n\t\t\tknown_sfx[i].touched = true;\n\t\t\treturn &known_sfx[i];\n\t\t}\n\n\tif (num_sfx == MAX_SFX)\n\t\tSys_Error (\"S_FindName: out of sfx_t\");\n\n\tif (create && known_sfx)\n\t{\n\t\tsfx = &known_sfx[i];\n\t\tstrcpy (sfx->name, name);\n\t\tsfx->syspath = syspath;\n\t\tsfx->touched = true;\n\n\t\tnum_sfx++;\n\t}\n\telse\n\t\tsfx = NULL;\n\n\treturn sfx;\n}\n\nvoid S_Purge(qboolean retaintouched)\n{\n\tsfx_t\t*sfx;\n\tint i;\n\n\t//make sure ambients are kept. silly ambients.\n\tif (retaintouched)\n\t{\n\t\tambient_sfx[AMBIENT_WATER] = S_PrecacheSound (\"ambience/water1.wav\");\n\t\tambient_sfx[AMBIENT_SKY] = S_PrecacheSound (\"ambience/wind2.wav\");\n\t}\n\n\tif (!num_sfx)\n\t\treturn;\n\n\tS_LockMixer();\n\tfor (i=0 ; i < num_sfx ; i++)\n\t{\n\t\tsfx = &known_sfx[i];\n\t\t/*don't hurt sounds if they're being processed by a worker thread*/\n\t\tif (sfx->loadstate == SLS_LOADING)\n\t\t{\n\t\t\tif (retaintouched)\n\t\t\t\tcontinue;\t//don't bother waiting\n\n\t\t\t//trying to shut down or something.\n\t\t\t//make sure there's no worker about to write to sfx after the memory is freed\n\t\t\tCOM_WorkerPartialSync(sfx, &sfx->loadstate, SLS_LOADING);\n\t\t}\n\n\t\t/*don't purge the file if its still relevent*/\n\t\tif (retaintouched && sfx->touched)\n\t\t\tcontinue;\n\n\t\tif (S_IsPlayingSomewhere(sfx))\n\t\t\tcontinue;\t//eep?!?\n\n\t\tsfx->loadstate = SLS_NOTLOADED;\n\t\t/*nothing to do if there's no data within*/\n\t\tif (!sfx->decoder.buf)\n\t\t\tcontinue;\n\t\t/*stop the decoder first*/\n\t\tif (sfx->decoder.purge)\n\t\t\tsfx->decoder.purge(sfx);\n\t\telse if (sfx->decoder.ended)\n\t\t\tsfx->decoder.ended(sfx);\n\n\t\t/*if there's any data associated still, kill it. if present, it should be a single sfxcache_t (with data in same alloc)*/\n\t\tif (sfx->decoder.buf)\n\t\t\tBZ_Free(sfx->decoder.buf);\n\t\tmemset(&sfx->decoder, 0, sizeof(sfx->decoder));\n\t}\n\tS_UnlockMixer();\n}\n\nvoid S_ResetFailedLoad(void)\n{\n\tint i;\n\tfor (i=0 ; i < num_sfx ; i++)\n\t{\n\t\tif (known_sfx[i].loadstate == SLS_FAILED)\n\t\t\tknown_sfx[i].loadstate = SLS_NOTLOADED;\n\t}\n}\n\nvoid S_UntouchAll(void)\n{\n\tint i;\n\tfor (i=0 ; i < num_sfx ; i++)\n\t\tknown_sfx[i].touched = false;\n}\n\n/*\n==================\nS_PrecacheSound\n\n==================\n*/\nsfx_t *S_PrecacheSound2 (const char *name, qboolean syspath)\n{\n\tsfx_t\t*sfx;\n\n\tif (nosound.ival || !known_sfx || !*name)\n\t\treturn NULL;\n\n\tsfx = S_FindName (name, true, syspath);\n\n// cache it in\n\tif (snd_precache.ival && snd_precache.ival != 2 && sndcardinfo)\n\t\tS_LoadSound (sfx, true);\n\n\treturn sfx;\n}\n\n\n//=============================================================================\n\n/*\n=================\nSND_PickChannel\n=================\n*/\nchannel_t *SND_PickChannel(soundcardinfo_t *sc, int entnum, int entchannel)\n{\n\tint ch_idx;\n\tint oldest;\n\n// Check for replacement sound, or an idle channel\n\toldest = -1;\n\tfor (ch_idx=DYNAMIC_FIRST; ch_idx < sc->max_chans ; ch_idx++)\n\t{\n\t\tif (entchannel != 0\t\t// channel 0 never overrides\n\t\t&& sc->channel[ch_idx].entnum == entnum\n\t\t&& sc->channel[ch_idx].entchannel == entchannel)\n\t\t{\t// always override sound from same entity\n\t\t\toldest = ch_idx;\n\t\t\tbreak;\n\t\t}\n\n\t\tif (!sc->channel[ch_idx].sfx)\n\t\t\toldest = ch_idx;\n\t}\n\n\tif (oldest == -1)\n\t{\n\t\toldest = sc->max_chans;\n\t\tZ_ReallocElements((void**)&sc->channel, &sc->max_chans, oldest+1, sizeof(*sc->channel));\n\t}\n\n\tsc->channel[oldest].sfx = NULL;\n\n\tif (sc->total_chans <= oldest)\n\t\tsc->total_chans = oldest+1;\n#ifdef Q3CLIENT\t//presumably we should be using this instead of pos for oldest, but blurgh.\n\tsc->channel[oldest].starttime = Sys_Milliseconds();\n#endif\n\treturn &sc->channel[oldest];\n}\n\nstatic void SND_AccumulateSpacialization(soundcardinfo_t *sc, channel_t *ch, vec3_t origin)\n{\n\tvec3_t listener_vec;\n\tvec_t dist;\n\tvec_t scale;\n\tvec3_t world_vec;\n\tint i, v;\n\tfloat volscale;\n\tint seat;\n\n\tif (ch->flags & CF_CL_ABSVOLUME)\n\t\tvolscale = mastervolume.value;\n\telse\n\t\tvolscale = volume.value * voicevolumemod;\n\n\tif (sc->seat == -1)\n\t{\n\t\tseat = 0;\n\t\tVectorSubtract(origin, listener[seat].origin, world_vec);\n\t\tdist = DotProduct(world_vec,world_vec);\n\t\tfor (i = 1; i < cl.splitclients; i++)\n\t\t{\n\t\t\tVectorSubtract(origin, listener[i].origin, world_vec);\n\t\t\tscale = DotProduct(world_vec,world_vec);\n\t\t\tif (scale < dist)\n\t\t\t{\n\t\t\t\tdist = scale;\n\t\t\t\tseat = i;\n\t\t\t}\n\t\t}\n\t}\n\telse\n\t{\n\t\tseat = sc->seat;\n\t}\n\n\t// anything coming from the view entity will always be full volume\n\tif (ch->entnum == listener[seat].entnum)\n\t{\n\t\tv = ch->master_vol * (ruleset_allow_localvolume.value ? snd_playersoundvolume.value : 1) * volscale;\n\t\tv = bound(0, v, 255);\n\t\tfor (i = 0; i < sc->sn.numchannels; i++)\n\t\t\tch->vol[i] = v;\n\t\treturn;\n\t}\n\n// calculate stereo seperation and distance attenuation\n\tVectorSubtract(origin, listener[seat].origin, world_vec);\n\n\tdist = VectorNormalize(world_vec) * ch->dist_mult;\n\n\tif ((ch->flags & CF_NOSPACIALISE) || !ch->dist_mult)\n\t{\n\t\tscale = 1;\n\t\tscale = (1.0 - dist) * scale;\n\t\tv = ch->master_vol * scale * volscale;\n\t\tfor (i = 0; i < sc->sn.numchannels; i++)\n\t\t\tch->vol[i] += bound(0, v, 255);\n\t\treturn;\n\t}\n\n\t//rotate the world_vec into listener space, so that the audio direction stored in the speakerdir array can be used directly.\n\tlistener_vec[0] = DotProduct(listener[seat].forward, world_vec);\n\tlistener_vec[1] = DotProduct(listener[seat].right, world_vec);\n\tlistener_vec[2] = DotProduct(listener[seat].up, world_vec);\n\n\tif (snd_leftisright.ival^r_xflip.ival)\n\t\tlistener_vec[1] = -listener_vec[1];\n\n\tfor (i = 0; i < sc->sn.numchannels; i++)\n\t{\n\t\tscale = 1 + DotProduct(listener_vec, sc->speakerdir[i]);\n\t\tscale = (1.0 - dist) * scale * sc->dist[i];\n\t\tv = ch->master_vol * scale * volscale;\n\t\tch->vol[i] += bound(0, v, 255);\n\t}\n}\n/*\n=================\nSND_Spatialize\n=================\n*/\nstatic void SND_Spatialize(soundcardinfo_t *sc, channel_t *ch)\n{\n\tvec3_t listener_vec, sound_vel;\n\tvec_t dist;\n\tvec_t scale;\n\tvec3_t world_vec;\n\tint i, v;\n\tfloat volscale;\n\tint seat;\n\n\tif (ch->flags & CF_FOLLOW)\n\t{\n\t\t//sounds following ents should update their position to match that ent's position.\n\t\t//its important that they do not snap back to where they were if the entity vanishes, so we just overwrite the channel origin for that. its simpler.\n#ifdef CSQC_DAT\n\t\tif (ch->entnum < 0 && -ch->entnum < csqc_world.num_edicts)\n\t\t{\n\t\t\twedict_t *ed = WEDICT_NUM_PB(csqc_world.progs, -ch->entnum);\n\t\t\tif (ed->ereftype == ER_ENTITY)\n\t\t\t{\n\t\t\t\tVectorCopy(ed->v->origin, ch->origin);\n\t\t\t\tVectorCopy(ed->v->velocity, ch->velocity);\n\n\t\t\t\tif (ed->v->solid == SOLID_BSP)\n\t\t\t\t{\n\t\t\t\t\tVectorMA(ch->origin, 0.5, ed->v->absmin, ch->origin);\n\t\t\t\t\tVectorMA(ch->origin, 0.5, ed->v->absmax, ch->origin);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse\n#endif\n\t\tif (ch->entnum > 0 && ch->entnum < cl.maxlerpents && cl.lerpents[ch->entnum].sequence == cl.lerpentssequence)\n\t\t{\n\t\t\tlerpents_t *le = cl.lerpents+ch->entnum;\n\t\t\tint midx = le->entstate->modelindex;\n\t\t\t\n\t\t\tVectorCopy(le->origin, ch->origin);\n\t\t\t//VectorCopy(le->velocity, ch->velocity);\t//fixme: bmodels should use their center rather than their origin. check le->state->solid?\n\n\t\t\t//bmodels should report the center of the entity rather than the origin (which is frequently at 0 0 0 or merely used as a pivot)\n\t\t\tif (le->entstate->solidsize == ES_SOLID_BSP && midx > 0 && midx < countof(cl.model_precache))\n\t\t\t{\n\t\t\t\tif (cl.model_precache[midx] && cl.model_precache[midx]->loadstate == MLS_LOADED && cl.model_precache[midx]->type == mod_brush)\n\t\t\t\t{\n\t\t\t\t\t//fixme: should probably deal with rotations.\n\t\t\t\t\tVectorMA(ch->origin, 0.5, cl.model_precache[midx]->mins, ch->origin);\n\t\t\t\t\tVectorMA(ch->origin, 0.5, cl.model_precache[midx]->maxs, ch->origin);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t//FIXME: update rate to provide doppler\n\t}\n\n\t//sounds with absvolume ignore all volume etc cvars+settings\n\tif (ch->flags & CF_CL_ABSVOLUME)\n\t\tvolscale = mastervolume.value;\n\telse\n\t\tvolscale = volume.value * voicevolumemod;\n\n\tif (!vid.activeapp && !snd_inactive.ival && !(ch->flags & CF_CLI_INACTIVE))\n\t\tvolscale = 0;\n\n\tif (sc->seat == -1)\n\t{\n\t\tseat = 0;\n\t\tVectorSubtract(ch->origin, listener[seat].origin, world_vec);\n\t\tdist = DotProduct(world_vec,world_vec);\n#if MAX_SPLITS > 1\n\t\tfor (i = 1; i < cl.splitclients; i++)\n\t\t{\n\t\t\tVectorSubtract(ch->origin, listener[i].origin, world_vec);\n\t\t\tscale = DotProduct(world_vec,world_vec);\n\t\t\tif (scale < dist)\n\t\t\t{\n\t\t\t\tdist = scale;\n\t\t\t\tseat = i;\n\t\t\t}\n\t\t}\n#endif\n\t}\n\telse\n\t{\n\t\tseat = sc->seat;\n\t}\n\n\t// anything coming from the view entity will always be full volume\n\t// (no, I don't like this hack)\n\tif (ch->entnum == listener[seat].entnum && ch->entnum)\n\t{\n\t\tv = ch->master_vol * (ruleset_allow_localvolume.value ? snd_playersoundvolume.value : 1) * volscale;\n\t\tv = bound(0, v, 255);\n\t\tfor (i = 0; i < sc->sn.numchannels; i++)\n\t\t\tch->vol[i] = v;\n\t\treturn;\n\t}\n\n// calculate stereo seperation and distance attenuation\n\tVectorSubtract(ch->origin, listener[seat].origin, world_vec);\n\n\tdist = VectorNormalize(world_vec) * ch->dist_mult;\n\n\tif ((ch->flags & CF_NOSPACIALISE) || !ch->dist_mult)\n\t{\n\t\tscale = 1;\n\t\tscale = (1.0 - dist) * scale;\n\t\tv = ch->master_vol * scale * volscale;\n\t\tv = bound(0, v, 255);\n\t\tfor (i = 0; i < sc->sn.numchannels; i++)\n\t\t\tch->vol[i] = v;\n\t\treturn;\n\t}\n\n\t//an attempt at doppler.\n\tif (snd_doppler.value)\n\t{\n\t\t//according to feh, the speed of sound is about 9000 qu/s.\n\t\tVectorAdd(listener[seat].velocity, ch->velocity, sound_vel);\n\t\tscale = 1 + snd_doppler.value * DotProduct(world_vec, sound_vel) / (9000.0);\n\t\tif (scale > snd_doppler_max.value)\n\t\t\tscale = snd_doppler_max.value;\n\t\tif (scale < snd_doppler_min.value)\n\t\t\tscale = snd_doppler_min.value;\n\t\tch->rate = (1<<PITCHSHIFT) * scale + 0.5;\n\t\tif (ch->rate < 1)\t//too small values result in crashes.\n\t\t\tch->rate = 1;\n\t}\n\n\t//rotate the world_vec into listener space, so that the audio direction stored in the speakerdir array can be used directly.\n\tlistener_vec[0] = DotProduct(listener[seat].forward, world_vec);\n\tlistener_vec[1] = DotProduct(listener[seat].right, world_vec);\n\tlistener_vec[2] = DotProduct(listener[seat].up, world_vec);\n\n\tif (snd_leftisright.ival^r_xflip.ival)\n\t\tlistener_vec[1] = -listener_vec[1];\n\n\tfor (i = 0; i < sc->sn.numchannels; i++)\n\t{\n\t\tscale = 1 + DotProduct(listener_vec, sc->speakerdir[i]);\n\t\tscale = (1.0 - dist) * scale * sc->dist[i];\n\t\tv = ch->master_vol * scale * volscale;\n\t\tv = bound(0, v, 255);\n\t\tch->vol[i] = v;\n\t}\n}\n\n// =======================================================================\n// Start a sound effect\n// =======================================================================\nstatic void S_UpdateSoundCard(soundcardinfo_t *sc, qboolean updateonly, channel_t *target_chan, int entnum, int entchannel, sfx_t *sfx, vec3_t origin, vec3_t velocity, float fvol, float attenuation, float timeoffset, float ratemul, unsigned int flags)\n{\n\tchannel_t *check;\n\tint\t\tvol;\n\tint\t\tch_idx;\n\tint\t\tskip;\n\tint\t\tabsstartpos = updateonly?sc->GetChannelPos?sc->GetChannelPos(sc, target_chan)<<PITCHSHIFT:target_chan->pos:0;\n\textern cvar_t cl_demospeed;\n\tchanupdatereason_t chanupdatetype = updateonly?CUR_UPDATE:CUR_EVERYTHING;\n\n\tif (!sfx)\n\t\tsfx = target_chan->sfx;\n\n\tif (fvol < 0 || !sfx)\n\t{\t//stopsound, apparently.\n\t\ttarget_chan->sfx = NULL;\n\t\treturn;\n\t}\n\n\tif (timeoffset != 0.0)\n\t\tchanupdatetype |= CUR_OFFSET;\n\n\tif (!ratemul)\t//rate of 0\n\t\tratemul = 1;\n\tratemul *= snd_playbackrate.value;\n\tif (!snd_ignoregamespeed.ival)\n\t\tratemul *= (cls.state?cl.gamespeed:1) * (cls.demoplayback?cl_demospeed.value:1);\n\tif (ratemul <= 0)\t//in case the user set the cvars weirdly\n\t\tratemul = 1;\n\n\tvol = fvol*255;\n\n// spatialize\n\tif (target_chan->sfx != sfx)\n\t\tchanupdatetype |= CUR_SOUNDCHANGE;\n\n\tmemset (target_chan, 0, sizeof(*target_chan));\n\tif (!origin)\n\t{\n\t\tif (sc->seat == -1)\n\t\t{\n\t\t\tVectorClear(target_chan->origin);\n\t\t\tattenuation = 0;\n\t\t\tflags |= CF_NOSPACIALISE;\n\t\t}\n\t\telse\n\t\t\tVectorCopy(listener[sc->seat].origin, target_chan->origin);\n\t}\n\telse\n\t{\n\t\tVectorCopy(origin, target_chan->origin);\n\t}\n\tif (velocity)\n\t\tVectorCopy(velocity, target_chan->velocity);\n\telse\n\t\tVectorClear(target_chan->velocity);\n\ttarget_chan->flags = flags;\n\ttarget_chan->dist_mult = attenuation / snd_nominaldistance.value;\n\ttarget_chan->master_vol = vol;\n\ttarget_chan->entnum = entnum;\n\ttarget_chan->entchannel = entchannel;\n\tSND_Spatialize(sc, target_chan);\n\n\tif (!S_LoadSound (sfx, false))\n\t{\n\t\ttarget_chan->sfx = NULL;\n\t\treturn;\t\t// couldn't load the sound's data\n\t}\n\n\t//FIXME: why does this only filter for openal devices? its weird.\n\tif (!updateonly && !target_chan->vol[0] && !target_chan->vol[1] && !target_chan->vol[2] && !target_chan->vol[3] && !target_chan->vol[4] && !target_chan->vol[5] && sc->ChannelUpdate)\n\tif (sfx->loopstart == -1 && !(flags&CF_FORCELOOP))\t//only skip if its not looping.\n\t{\n\t\ttarget_chan->sfx = NULL;\n\t\tgoto updatechannel;\n\t}\n\n\ttarget_chan->sfx = sfx;\n\n\ttarget_chan->rate = ((1<<PITCHSHIFT) * ratemul); //*sfx->rate/sc->sn.speed;\n\tif (target_chan->rate < 1)\t/*make sure the rate won't crash us*/\n\t\ttarget_chan->rate = 1;\n\ttarget_chan->pos = absstartpos + (int)(timeoffset*sc->sn.speed*target_chan->rate);\n\n\tif (!updateonly)\n\t{\n// if an identical sound has also been started this frame, offset the pos\n// a bit to keep it from just making the first one louder\n\t\tcheck = &sc->channel[DYNAMIC_FIRST];\n\t\tfor (ch_idx=DYNAMIC_FIRST; ch_idx < sc->total_chans; ch_idx++, check++)\n\t\t{\n\t\t\tif (check == target_chan)\n\t\t\t\tcontinue;\n\t\t\tif (check->sfx == sfx && !check->pos)\n\t\t\t{\n\t\t\t\tskip = rand () % (int)(0.1*sc->sn.speed);\n\t\t\t\ttarget_chan->pos -= skip*target_chan->rate;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\nupdatechannel:\n\n\tif (sc->ChannelUpdate)\n\t\tsc->ChannelUpdate(sc, target_chan, chanupdatetype);\n}\n\nfloat S_UpdateSound(int entnum, int entchannel, sfx_t *sfx, vec3_t origin, vec3_t velocity, float fvol, float attenuation, float timeofs, float pitchadj, unsigned int flags)\n{\n\tint i;\n\tint result = 0;\n\tint cards = 0;\n\tsoundcardinfo_t *sc;\n\tchannel_t *chan;\n\n\tif (cls.demoseeking)\n\t\treturn result;\n\tS_LockMixer();\n\tfor (sc = sndcardinfo; sc; sc = sc->next)\n\t{\n\t\tcards++;\n\t\tfor (i = 0; i < sc->total_chans; i++)\n\t\t{\n\t\t\tif (sc->channel[i].entnum == entnum && sc->channel[i].entchannel == entchannel && sc->channel[i].sfx)\n\t\t\t{\n\t\t\t\tS_UpdateSoundCard(sc, true, &sc->channel[i], entnum, entchannel, sfx, origin, velocity, fvol, attenuation, timeofs, pitchadj, flags);\n\t\t\t\tresult++;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\t//start it if we couldn't find it.\n\t\tif (i == sc->total_chans && sfx)\n\t\t{\n\t\t\tchan = SND_PickChannel(sc, entnum, entchannel);\n\t\t\tif (chan)\n\t\t\t\tS_UpdateSoundCard(sc, false, chan, entnum, entchannel, sfx, origin, velocity, fvol, attenuation, timeofs, pitchadj, flags);\n\t\t}\n\t}\n\tS_UnlockMixer();\n\tif (!cards)\n\t\tcards=1;\n\treturn result / (float)cards;\n}\n\nvoid S_StartSound(int entnum, int entchannel, sfx_t *sfx, vec3_t origin, vec3_t velocity, float fvol, float attenuation, float timeofs, float pitchadj, unsigned int flags)\n{\n\tsoundcardinfo_t *sc;\n\tchannel_t *target_chan;\n\n\tif (!sfx || !*sfx->name)\t//no named sounds would need specific starting.\n\t\treturn;\n\n\tif (cls.demoseeking)\n\t\treturn;\n\n\tif (!sound_started)\n\t\treturn;\n\n\tif (nosound.ival)\n\t\treturn;\n\n\tS_LockMixer();\n\tfor (sc = sndcardinfo; sc; sc = sc->next)\n\t{\n\t\tif (flags & CF_NOREPLACE)\n\t\t{\n\t\t\tint i;\n\t\t\tfor (i = 0; i < sc->total_chans; i++)\n\t\t\t\tif (sc->channel[i].entnum == entnum && sc->channel[i].entchannel == entchannel)\n\t\t\t\t\tbreak;\n\t\t\tif (i < sc->total_chans)\n\t\t\t\tcontinue;\n\t\t}\n#ifdef Q3CLIENT\n\t\tif (flags & CF_CLI_NODUPES)\n\t\t{\t//don't start too many simultaneous sounds. q3 sucks or something.\n\t\t\tint active = 0, i;\n\t\t\tunsigned int time = Sys_Milliseconds();\n\t\t\tfor (i = 0; i < sc->total_chans; i++)\n\t\t\t{\t//as per q3, channel isn't important.\n\t\t\t\tif (sc->channel[i].entnum == entnum && sc->channel[i].sfx == sfx)\n\t\t\t\t{\n\t\t\t\t\t//never allow a new sound within 50ms of the previous one\n\t\t\t\t\tif (time - sc->channel[i].starttime < 50)\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tactive++;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (active >= 4 || i < sc->total_chans)\n\t\t\t{\n\t\t\t\tCon_DPrintf(\"CF_CLI_NODUPES strikes again!\\n\");\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n#endif\n\t\t// pick a channel to play on\n\t\ttarget_chan = SND_PickChannel(sc, entnum, entchannel);\n\t\tif (!target_chan)\n\t\t\tbreak;\n\n\t\tS_UpdateSoundCard(sc, false, target_chan, entnum, entchannel, sfx, origin, velocity, fvol, attenuation, timeofs, pitchadj, flags);\n\t}\n\tS_UnlockMixer();\n}\n\nqboolean S_GetMusicInfo(int musicchannel, float *time, float *duration, char *title, size_t titlesize)\n{\n\tqboolean result = false;\n\tsoundcardinfo_t *sc;\n\tsfx_t *sfx;\n\t*time = 0;\n\t*duration = 0;\n\n\tif (titlesize)\n\t\t*title = 0;\n\n\tmusicchannel += MUSIC_FIRST;\n\n\tS_LockMixer();\n\tfor (sc = sndcardinfo; sc; sc = sc->next)\n\t{\n\t\tsfx = sc->channel[musicchannel].sfx;\n\t\tif (sfx)\n\t\t{\n\t\t\tQ_strncpyz(title, COM_SkipPath(sfx->name), titlesize);\n\t\t\tif (sfx->loadstate == SLS_LOADED)\n\t\t\t{\n\t\t\t\tif (sfx->decoder.querydata)\n\t\t\t\t\t*duration = sfx->decoder.querydata(sfx, NULL, title, titlesize);\n\t\t\t\telse if (sfx->decoder.buf)\n\t\t\t\t{\n\t\t\t\t\tsfxcache_t *c = sfx->decoder.buf;\n\t\t\t\t\t*duration = (float)c->length / c->speed;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\t*duration = 0;\n\n\t\t\t\t//FIXME: openal doesn't report the actual time.\n\t\t\t\t*time = (sc->channel[musicchannel].pos>>PITCHSHIFT) / (float)snd_speed;\t//the time into the sound, ignoring play rate.\n\t\t\t\tresult = true;\n\t\t\t}\n\t\t}\n\t}\n\tS_UnlockMixer();\n\n\treturn result;\n}\n\nfloat S_GetSoundTime(int entnum, int entchannel)\n{\n\tint i;\n\tfloat result = -1;\t//if we didn't find one\n\tsoundcardinfo_t *sc;\n\tS_LockMixer();\n\tfor (sc = sndcardinfo; sc && result == -1; sc = sc->next)\n\t{\n\t\tfor (i = 0; i < sc->total_chans; i++)\n\t\t{\n\t\t\tif (sc->channel[i].entnum == entnum && sc->channel[i].entchannel == entchannel && sc->channel[i].sfx)\n\t\t\t{\n\t\t\t\tssamplepos_t spos = sc->GetChannelPos?sc->GetChannelPos(sc, &sc->channel[i]):(sc->channel[i].pos>>PITCHSHIFT);\n\t\t\t\tresult = spos / (float)snd_speed;\t//the time into the sound, ignoring play rate.\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\t//we found one on this sound device card, ignore others.\n\t\tif (result != -1)\n\t\t\tbreak;\n\t}\n\tS_UnlockMixer();\n\treturn result;\n}\nfloat S_GetChannelLevel(int entnum, int entchannel)\n{\n\tint i, j;\n\tfloat result = -1;\t//if we didn't find one\n\tsoundcardinfo_t *sc;\n\tsfxcache_t scachebuf, *scache;\n\tS_LockMixer();\n\tfor (sc = sndcardinfo; sc && result == -1; sc = sc->next)\n\t{\n\t\tfor (i = 0; i < sc->total_chans; i++)\n\t\t{\n\t\t\tif (sc->channel[i].entnum == entnum && sc->channel[i].entchannel == entchannel && sc->channel[i].sfx)\n\t\t\t{\n\t\t\t\tssamplepos_t spos = sc->GetChannelPos?sc->GetChannelPos(sc, &sc->channel[i]):(sc->channel[i].pos>>PITCHSHIFT);\n\t\t\t\tif (sc->channel[i].sfx->decoder.decodedata)\n\t\t\t\t\tscache = sc->channel[i].sfx->decoder.decodedata(sc->channel[i].sfx, &scachebuf, spos, 1);\n\t\t\t\telse \n\t\t\t\t\tscache = NULL;\n\t\t\t\tif (!scache)\n\t\t\t\t\tscache = sc->channel[i].sfx->decoder.buf;\n\n\t\t\t\tif (scache && spos >= scache->soundoffset && spos < scache->soundoffset+scache->length)\n\t\t\t\t{\n\t\t\t\t\tspos -= scache->soundoffset;\n\t\t\t\t\tspos *= scache->numchannels;\n\t\t\t\t\tswitch(scache->format)\n\t\t\t\t\t{\n#ifdef FTE_TARGET_WEB\n\t\t\t\t\tcase QAF_BLOB:\n\t\t\t\t\t\tresult = 0;\t//sorry. you're going to have to use .wav :(\n\t\t\t\t\t\tbreak;\n#endif\n\t\t\t\t\tcase QAF_S8:\n\t\t\t\t\t\tfor (j = 0; j < scache->numchannels; j++)\t//average the channels\n\t\t\t\t\t\t\tresult += abs(((signed char*)scache->data)[spos+j]);\n\t\t\t\t\t\tresult /= scache->numchannels*127.0;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase QAF_S16:\n\t\t\t\t\t\tfor (j = 0; j < scache->numchannels; j++)\t//average the channels\n\t\t\t\t\t\t\tresult += abs(((signed short*)scache->data)[spos+j]);\n\t\t\t\t\t\tresult /= scache->numchannels*32767.0;\n\t\t\t\t\t\tbreak;\n#ifdef MIXER_F32\n\t\t\t\t\tcase QAF_F32:\n\t\t\t\t\t\tfor (j = 0; j < scache->numchannels; j++)\t//average the channels\n\t\t\t\t\t\t\tresult += fabs(((float*)scache->data)[spos+j]);\n\t\t\t\t\t\tresult /= scache->numchannels;\n\t\t\t\t\t\tbreak;\n#endif\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tresult = 0;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\t//we found one on this sound device card, ignore others.\n\t\tif (result != -1)\n\t\t\tbreak;\n\t}\n\tS_UnlockMixer();\n\treturn result;\n}\n\nqboolean S_IsPlayingSomewhere(sfx_t *s)\n{\n\tsoundcardinfo_t *si;\n\tint i;\n\tfor (si = sndcardinfo; si; si=si->next)\n\t{\n\t\tfor (i = 0; i < si->total_chans; i++)\n\t\tif (si->channel[i].sfx == s)\n\t\t\treturn true;\n\t}\n\treturn false;\n}\n\nstatic void S_StopSoundCard(soundcardinfo_t *sc, int entnum, int entchannel)\n{\n\tint i;\n\n\tfor (i=0 ; i<sc->total_chans ; i++)\n\t{\n\t\tif (sc->channel[i].entnum == entnum\n\t\t\t&& (!entchannel || sc->channel[i].entchannel == entchannel))\n\t\t{\n\t\t\tsc->channel[i].sfx = NULL;\n\t\t\tif (sc->ChannelUpdate)\n\t\t\t\tsc->ChannelUpdate(sc, &sc->channel[i], CUR_EVERYTHING);\n\t\t\tif (entchannel)\n\t\t\t\tbreak;\n\t\t}\n\t}\n}\n\nvoid S_StopSound(int entnum, int entchannel)\n{\n\tsoundcardinfo_t *sc;\n\tS_LockMixer();\n\tfor (sc = sndcardinfo; sc; sc = sc->next)\n\t\tS_StopSoundCard(sc, entnum, entchannel);\n\tS_UnlockMixer();\n}\n\nvoid S_StopAllSounds(qboolean clear)\n{\n\tint\t\ti;\n\tsfx_t *s;\n\tchannel_t musics[NUM_MUSICS];\n\n\tsoundcardinfo_t *sc;\n\n\tif (!sound_started)\n\t\treturn;\n\tS_LockMixer();\n\n\tfor (sc = sndcardinfo; sc; sc = sc->next)\n\t{\n\t\tfor (i=sc->total_chans ; i --> 0 ; )\n\t\t{\n\t\t\tif (i >= MUSIC_FIRST && i < MUSIC_FIRST+NUM_MUSICS && sc->selfpainting)\n\t\t\t\tcontinue;\t//don't reset music if is safe to continue playing it without stuttering\n\t\t\ts = sc->channel[i].sfx;\n\t\t\tif (s)\n\t\t\t{\n\t\t\t\tsc->channel[i].sfx = NULL;\n\t\t\t\tif (s->loadstate == SLS_LOADED && s->decoder.ended)\n\t\t\t\tif (!S_IsPlayingSomewhere(s))\t//if we aint playing it elsewhere, free it compleatly.\n\t\t\t\t{\n\t\t\t\t\tif (s->decoder.ended)\n\t\t\t\t\t\ts->decoder.ended(s);\n\t\t\t\t}\n\n\t\t\t\tif (sc->ChannelUpdate)\n\t\t\t\t\tsc->ChannelUpdate(sc, &sc->channel[i], CUR_EVERYTHING);\n\t\t\t}\n\t\t}\n\n\t\tsc->total_chans = NUM_AMBIENTS + NUM_MUSICS;\t// no statics\n\t\tZ_ReallocElements((void**)&sc->channel, &sc->max_chans, sc->total_chans, sizeof(*sc->channel));\n\n\t\tmemcpy(musics, &sc->channel[MUSIC_FIRST], sizeof(musics));\n\t\tQ_memset(sc->channel, 0, sc->max_chans * sizeof(channel_t));\n\t\tmemcpy(&sc->channel[MUSIC_FIRST], musics, sizeof(musics));\n\n\t\tif (clear && !sc->selfpainting)\t//if its self-painting, then the mixer will continue painting anyway (which is important if its still painting music, but otherwise don't stutter at all when loading)\n\t\t\tS_ClearBuffer (sc);\n\t}\n\n\tS_UnlockMixer();\n}\n\nstatic void S_StopAllSounds_f (void)\n{\n\tS_StopAllSounds (true);\n}\n\nstatic void S_ClearBuffer (soundcardinfo_t *sc)\n{\n\tvoid *buffer;\n\tunsigned int dummy;\n\n\tint\t\tclear;\n\n\tif (!sound_started || !sc->sn.buffer)\n\t\treturn;\n\n\tif (sc->sn.sampleformat == QSF_U8)\n\t\tclear = 0x80;\n\telse\n\t\tclear = 0;\n\n\tdummy = 0;\n\tbuffer = sc->Lock(sc, &dummy);\n\tif (buffer)\n\t{\n\t\tQ_memset(buffer, clear, sc->sn.samples * sc->sn.samplebytes);\n\t\tsc->Unlock(sc, buffer);\n\t}\n}\n\n/*\n=================\nS_StaticSound\n=================\n*/\nvoid S_StaticSound (sfx_t *sfx, vec3_t origin, float vol, float attenuation)\n{\n\tchannel_t\t*ss;\n\tsoundcardinfo_t *scard;\n\n\tif (!sfx)\n\t\treturn;\n\n\tS_LockMixer();\n\n\tfor (scard = sndcardinfo; scard; scard = scard->next)\n\t{\n\t\tif (scard->total_chans == scard->max_chans)\n\t\t{\n\t\t\tif (!ZF_ReallocElements((void**)&scard->channel, &scard->max_chans, scard->max_chans+64, sizeof(*scard->channel)))\n\t\t\t{\n\t\t\t\tCon_Printf (\"total_channels == MAX_CHANNELS\\n\");\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\n\t\tif (!S_LoadSound (sfx, true))\n\t\t\tbreak;\n\n\t\tss = &scard->channel[scard->total_chans];\n\t\tscard->total_chans++;\n\n\t\tss->entnum = 0;\n\t\tss->sfx = sfx;\n\t\tss->rate = 1<<PITCHSHIFT;\n\t\tVectorCopy (origin, ss->origin);\n\t\tss->master_vol = vol*255;\n\t\tss->dist_mult = attenuation / snd_nominaldistance.value;\n\t\tss->pos = 0;\n\t\tss->flags = CF_FORCELOOP|CF_CLI_STATIC;\n\n\t\tSND_Spatialize (scard, ss);\n\n\t\tif (scard->ChannelUpdate)\n\t\t\tscard->ChannelUpdate(scard, ss, CUR_EVERYTHING);\n\t}\n\n\tS_UnlockMixer();\n}\n\n\n//=============================================================================\n\nvoid S_Music_Clear(sfx_t *onlyifsample)\n{\n\t//stops the current BGM music\n\t//calling this will trigger Media_NextTrack later\n\tsfx_t *s;\n\tsoundcardinfo_t *sc;\n\tint i;\n\tfor (i = MUSIC_FIRST; i < MUSIC_STOP; i++)\n\t{\n\t\tfor (sc = sndcardinfo; sc; sc=sc->next)\n\t\t{\n\t\t\ts = sc->channel[i].sfx;\n\t\t\tif (!s)\n\t\t\t\tcontinue;\n\t\t\tif (onlyifsample && s != onlyifsample)\n\t\t\t\tcontinue;\n\n\t\t\tsc->channel[i].pos = 0;\n\t\t\tsc->channel[i].sfx = NULL;\n\n\t\t\tif (sc->ChannelUpdate)\n\t\t\t\tsc->ChannelUpdate(sc, &sc->channel[i], CUR_EVERYTHING);\n\n\t\t\tif (s && s->decoder.ended && !S_IsPlayingSomewhere(s))\t//if we aint playing it elsewhere, free it compleatly.\n\t\t\t\ts->decoder.ended(s);\n\t\t}\n\t}\n}\nvoid S_Music_Seek(float time)\n{\n\tsoundcardinfo_t *sc;\n\tint i;\n\tfor (i = MUSIC_FIRST; i < MUSIC_STOP; i++)\n\t{\n\t\tfor (sc = sndcardinfo; sc; sc=sc->next)\n\t\t{\n\t\t\tsc->channel[i].pos += sc->sn.speed*time * sc->channel[i].rate;\n\n\t\t\tif (sc->channel[i].pos < 0)\n\t\t\t{\t//clamp to the start of the track\n\t\t\t\tsc->channel[i].pos=0;\n\t\t\t}\n\t\t\t//if we seek over the end, ignore it. The sound playing code will spot that.\n\t\t}\n\t}\n}\n\n//mixer must be locked\nqboolean S_Music_Playing(int musicchannel)\n{\n\tsoundcardinfo_t *sc;\n\tmusicchannel += MUSIC_FIRST;\n\tfor (sc = sndcardinfo; sc; sc=sc->next)\n\t{\n\t\tif (sc->channel[musicchannel].sfx)\n\t\t\treturn true;\n\t}\n\treturn false;\n}\n\n/*\n===================\nS_UpdateAmbientSounds\n===================\n*/\nmleaf_t *Q1BSP_LeafForPoint (model_t *model, vec3_t p);\nvoid S_UpdateAmbientSounds (soundcardinfo_t *sc)\n{\n\tfloat\t\tvol;\n\tchannel_t\t*chan;\n\tint i;\n#ifdef Q1BSPS\n\tmleaf_t\t\t*l;\n\tfloat oldvol;\n\tint ambientlevel[NUM_AMBIENTS];\n#endif\n\n\tif (!snd_ambient)\n\t\treturn;\n\n\tfor (i = MUSIC_FIRST; i < MUSIC_STOP; i++)\n\t{\n\t\tchanupdatereason_t changed = CUR_SPACIALISEONLY;\n\t\tchan = &sc->channel[i];\n\t\tif (!chan->sfx)\n\t\t{\n\t\t\tfloat time = 0;\n\t\t\tsfx_t *newmusic;\n\t\t\tif (!S_Music_Playing(i-MUSIC_FIRST))\n\t\t\t{\n\t\t\t\tnewmusic = Media_NextTrack(i-MUSIC_FIRST, &time);\n\t\t\t\tif (newmusic && newmusic->loadstate != SLS_FAILED)\n\t\t\t\t{\t//okay, now we know which track we're meant to be playing, all devices can play it at once.\n\t\t\t\t\tsoundcardinfo_t *sc2;\n\t\t\t\t\tfor (sc2 = sndcardinfo; sc2; sc2=sc2->next)\n\t\t\t\t\t{\n\t\t\t\t\t\tchannel_t\t*chan = &sc2->channel[i];\n\t\t\t\t\t\tchan->sfx = newmusic;\n\t\t\t\t\t\tchan->rate = 1<<PITCHSHIFT;\n\t\t\t\t\t\tchan->pos = (int)(time * sc->sn.speed) * chan->rate;\n\t\t\t\t\t\tchanged = CUR_EVERYTHING;\n\n\t\t\t\t\t\tchan->master_vol = bound(0, 1, 255);\n\t\t\t\t\t\tchan->vol[0] = chan->vol[1] = chan->vol[2] = chan->vol[3] = chan->vol[4] = chan->vol[5] = chan->master_vol;\n\t\t\t\t\t\tif (sc->ChannelUpdate)\n\t\t\t\t\t\t\tsc->ChannelUpdate(sc, chan, changed);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (chan->sfx)\n\t\t{\n\t\t\tchan->flags = /*CF_CL_INACTIVE|*/CF_CL_ABSVOLUME|CF_NOSPACIALISE|CF_NOREVERB;\t//bypasses volume cvar completely.\n\t\t\tvol = 255*bgmvolume.value*voicevolumemod;\n\t\t\tif (!vid.activeapp && !snd_inactive.ival && !(chan->flags & CF_CLI_INACTIVE))\n\t\t\t\tvol = 0;\n\t\t\tvol = bound(0, vol, 255);\n\t\t\tvol = Media_CrossFade(i-MUSIC_FIRST, vol, (chan->pos>>PITCHSHIFT) / (float)snd_speed);\n\t\t\tif (vol < 0)\n\t\t\t{\t//cross fading wants to KILL this track now, apparently.\n\t\t\t\tsfx_t *s = chan->sfx;\n\n\t\t\t\tif (s->loadstate != SLS_LOADING)\n\t\t\t\t{\n\t\t\t\t\tchan->pos = 0;\n\t\t\t\t\tchan->sfx = NULL;\n\n\t\t\t\t\tif (sc->ChannelUpdate)\n\t\t\t\t\t\tsc->ChannelUpdate(sc, chan, CUR_EVERYTHING);\n\n\t\t\t\t\tif (s && s->decoder.ended && !S_IsPlayingSomewhere(s))\t//if we aint playing it elsewhere, free it compleatly.\n\t\t\t\t\t\ts->decoder.ended(s);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tchan->master_vol = bound(0, vol, 255);\n\t\t\t\tchan->vol[0] = chan->vol[1] = chan->vol[2] = chan->vol[3] = chan->vol[4] = chan->vol[5] = chan->master_vol;\n\t\t\t\tif (sc->ChannelUpdate)\n\t\t\t\t\tsc->ChannelUpdate(sc, chan, changed);\n\t\t\t}\n\t\t}\n\t}\n\n#ifdef Q1BSPS\n// calc ambient sound levels\n\tfor (i = 0; i < NUM_AMBIENTS; i++)\n\t\tambientlevel[i] = 0;\n\tif (cl.worldmodel && cl.worldmodel->type == mod_brush && cl.worldmodel->fromgame == fg_quake && cl.worldmodel->loadstate == MLS_LOADED)\n\t{\n\t\tif (ambient_level.value)\n\t\t{\n\t\t\tif (sc->seat < 0)\n\t\t\t{\n\t\t\t\tint seat = max(1,cl.splitclients);\n\t\t\t\twhile(seat --> 0)\n\t\t\t\t{\n\t\t\t\t\tl = Q1BSP_LeafForPoint(cl.worldmodel, listener[seat].origin);\n\t\t\t\t\tif (!l)\n\t\t\t\t\t\tcontinue;\n\n\t\t\t\t\tfor (i = 0; i < NUM_AMBIENTS; i++)\n\t\t\t\t\t\tambientlevel[i] = max(ambientlevel[i], l->ambient_sound_level[i]);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tl = Q1BSP_LeafForPoint(cl.worldmodel, listener[sc->seat].origin);\n\t\t\t\tif (l)\n\t\t\t\t\tfor (i = 0; i < NUM_AMBIENTS; i++)\n\t\t\t\t\t\tambientlevel[i] = l->ambient_sound_level[i];\n\t\t\t}\n\t\t}\n\t}\n\n\tfor (i = 0 ; i< NUM_AMBIENTS ; i++)\n\t{\n\t\tchan = &sc->channel[AMBIENT_FIRST+i];\n\t\tchan->sfx = ambient_sfx[AMBIENT_FIRST+i];\n\t\tchan->entnum = 0;\n\t\tchan->flags = CF_FORCELOOP | CF_NOSPACIALISE;\n\t\tchan->rate = 1<<PITCHSHIFT;\n\n\t\tVectorClear(chan->origin);\n\n\t\tvol = ambient_level.value * ambientlevel[i];\n\t\tif (vol < 8)\n\t\t\tvol = 0;\n\n\t\toldvol = sc->ambientlevels[i];\n\n\t// don't adjust volume too fast\n\t\tif (sc->ambientlevels[i] < vol)\n\t\t{\n\t\t\tsc->ambientlevels[i] += host_frametime * ambient_fade.value;\n\t\t\tif (sc->ambientlevels[i] > vol)\n\t\t\t\tsc->ambientlevels[i] = vol;\n\t\t}\n\t\telse if (chan->master_vol > vol)\n\t\t{\n\t\t\tsc->ambientlevels[i] -= host_frametime * ambient_fade.value;\n\t\t\tif (sc->ambientlevels[i] < vol)\n\t\t\t\tsc->ambientlevels[i] = vol;\n\t\t}\n\n\t\tchan->master_vol = sc->ambientlevels[i];\n\t\tchan->vol[0] = chan->vol[1] = chan->vol[2] = chan->vol[3] = chan->vol[4] = chan->vol[5] = bound(0, chan->master_vol * (volume.value*voicevolumemod), 255);\n\n\t\tif (sc->ChannelUpdate)\n\t\t\tsc->ChannelUpdate(sc, chan, ((oldvol == 0) ^ (sc->ambientlevels[i] == 0))?CUR_EVERYTHING:CUR_SPACIALISEONLY);\n\t}\n#endif\n}\n\nstruct sndreverbproperties_s *reverbproperties;\nsize_t numreverbproperties;\nqboolean S_UpdateReverb(size_t slot, void *reverb, size_t reverbsize)\n{\n\tstruct reverbproperties_s newprops;\n\tif (slot >= 1024)\n\t\treturn false;\n\n\tif (slot >= numreverbproperties)\n\t{\n\t\tint slots = slot+1;\n\t\tvoid *n = BZ_Realloc(reverbproperties, sizeof(*reverbproperties)*slots);\n\t\tif (!n)\n\t\t\treturn false;\n\t\treverbproperties = n;\n\t\tmemset(reverbproperties+numreverbproperties, 0, sizeof(*reverbproperties) * (slots-numreverbproperties));\n\t\tnumreverbproperties = slots;\n\t}\n\n\tmemset(&newprops, 0, sizeof(newprops));\n\tif (reverb)\n\t{\n\t\t//clamp the size for possible future extensibility\n\t\tif (reverbsize > sizeof(newprops))\n\t\t\treverbsize = sizeof(newprops);\n\t\tmemcpy(&newprops, reverb, reverbsize);\n\t}\n\n\tif (memcmp(&newprops, &reverbproperties[slot].props, sizeof(newprops)))\n\t{\n\t\treverbproperties[slot].props = newprops;\n\t\treverbproperties[slot].modificationcount++;\n\t}\n\treturn true;\n}\n\n/*\n============\nS_Update\n\nCalled once each time through the main loop\n============\n*/\nvoid S_UpdateListener(int seat, int entnum, vec3_t origin, vec3_t forward, vec3_t right, vec3_t up, size_t reverbtype, vec3_t velocity)\n{\n\tsoundcardinfo_t *sc;\n\tlistener[seat].entnum = entnum;\n\tVectorCopy(origin, listener[seat].origin);\n\tVectorCopy(forward, listener[seat].forward);\n\tVectorCopy(right, listener[seat].right);\n\tVectorCopy(up, listener[seat].up);\n\tVectorCopy(velocity, listener[seat].velocity);\n\n\tfor (sc = sndcardinfo; sc; sc=sc->next)\n\t\tif (sc->SetEnvironmentReverb && (sc->seat == seat || (sc->seat == -1 && seat == 0)))\n\t\t\tsc->SetEnvironmentReverb(sc, reverbtype);\n}\n\nvoid S_GetListenerInfo(int seat, float *origin, float *forward, float *right, float *up)\n{\n\tVectorCopy(listener[seat].origin, origin);\n\tVectorCopy(listener[seat].forward, forward);\n\tVectorCopy(listener[seat].right, right);\n\tVectorCopy(listener[seat].up, up);\n}\n\nstatic void S_Q2_AddEntitySounds(soundcardinfo_t *sc)\n{\n\tvec3_t positions[2048];\n\tint entnums[countof(positions)];\n\tsfx_t *sounds[countof(positions)];\n\tunsigned int count;\n\tunsigned int j;\n\tchannel_t *c;\n\n#ifdef Q2CLIENT\n\tif (cls.protocol == CP_QUAKE2)\n\t\tcount = CLQ2_GatherSounds(positions, entnums, sounds, countof(sounds));\n\telse\n#endif\n#ifdef VM_CG\n\tif (cls.protocol == CP_QUAKE3 && q3)\n\t\tcount = q3->cg.GatherLoopingSounds(positions, entnums, sounds, countof(sounds));\n\telse\n#endif\n\t\treturn;\n\t\n\twhile(count --> 0)\n\t{\n\t\tsfx_t *sfx = sounds[count];\n\t\tif (!sfx)\n\t\t\tcontinue;\n\t\tif (sfx->loadstate == SLS_NOTLOADED)\n\t\t\tS_LoadSound(sfx, true);\n\t\tif (sfx->loadstate != SLS_LOADED)\n\t\t\tcontinue;\t//not ready yet\n\n\t\tif (sc->ChannelUpdate)\n\t\t{\n\t\t\tfor (c = NULL, j=DYNAMIC_FIRST; j < sc->total_chans ; j++)\n\t\t\t{\n\t\t\t\tif (sc->channel[j].entnum == entnums[count] && !sc->channel[j].entchannel && (sc->channel[j].flags & CF_CLI_AUTOSOUND))\n\t\t\t\t{\n\t\t\t\t\tc = &sc->channel[j];\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tfor (c = NULL, j=DYNAMIC_FIRST; j < sc->total_chans ; j++)\n\t\t\t{\n\t\t\t\tif (sc->channel[j].sfx == sfx && (sc->channel[j].flags & CF_CLI_AUTOSOUND))\n\t\t\t\t{\n\t\t\t\t\tc = &sc->channel[j];\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (!c)\n\t\t{\n\t\t\tc = SND_PickChannel(sc, 0, 0);\n\t\t\tif (!c)\n\t\t\t\tcontinue;\n\t\t\tc->flags = CF_CLI_AUTOSOUND|CF_FORCELOOP;\n\t\t\tc->entnum = sc->ChannelUpdate?entnums[count]:0;\n\t\t\tc->entchannel = 0;\n\t\t\tc->dist_mult = 3 / snd_nominaldistance.value;\n\t\t\tc->master_vol = 255 * 1;\n\t\t\tc->pos = 0<<PITCHSHIFT;\t//q2 does weird stuff with the pos. we just forceloop and detect when it became irrelevant. this is required for stream decoding or openal\n\t\t\tc->rate = 1<<PITCHSHIFT;\n\t\t\tfor (j = 0; j < countof(c->vol); j++)\n\t\t\t\tc->vol[j] = 0;\n\t\t\tc->sfx = NULL;\n\t\t}\n\t\tif (sc->ChannelUpdate)\n\t\t{\t//hardware mixing doesn't support merging\n\t\t\tVectorCopy(positions[count], c->origin);\n\t\t\tSND_Spatialize(sc, c);\n\n\t\t\tif (c->sfx)\n\t\t\t\tsc->ChannelUpdate(sc, c, CUR_SPACIALISEONLY);\n\t\t}\n\t\telse\n\t\t{\t//merge with any other ents, if we can\n\t\t\tfor (j = 0; j <= count; j++)\n\t\t\t{\n\t\t\t\tif (sounds[j] == sfx)\n\t\t\t\t{\n\t\t\t\t\tsounds[j] = NULL;\n\t\t\t\t\tSND_AccumulateSpacialization(sc, c, positions[j]);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (!c->sfx)\n\t\t{\n\t\t\tfor (j = 0; j < countof(c->vol); j++)\n\t\t\t\tif (c->vol[j])\n\t\t\t\t\tbreak;\n\t\t\tif (j == countof(c->vol))\n\t\t\t\tc->sfx = NULL;\t//err, never mind\n\t\t\telse\n\t\t\t{\n\t\t\t\tc->sfx = sfx;\n\t\t\t\tif (sc->ChannelUpdate)\n\t\t\t\t\tsc->ChannelUpdate(sc, c, CUR_EVERYTHING);\n\t\t\t}\n\t\t}\n\t}\n}\n\nstatic void S_UpdateCard(soundcardinfo_t *sc)\n{\n\tint\t\t\ti, j;\n\tchannel_t\t*ch;\n\tchannel_t\t*combine;\n\n\tif (!sound_started)\n\t\treturn;\n\tif ((snd_blocked > 0))\n\t{\n\t\tif (!sc->inactive_sound)\n\t\t\treturn;\n\t}\n\n#ifdef AVAIL_OPENAL\n\tif (sc->ListenerUpdate)\n\t{\n\t\tsc->ListenerUpdate(sc, listener[sc->seat].entnum, listener[sc->seat].origin, listener[sc->seat].forward, listener[sc->seat].right, listener[sc->seat].up, listener[sc->seat].velocity);\n\t}\n#endif\n\n// update general area ambient sound sources\n\tS_UpdateAmbientSounds (sc);\n\n\tcombine = NULL;\n\n// update spatialization for static and dynamic sounds\n\tch = sc->channel+DYNAMIC_FIRST;\n\tfor (i=DYNAMIC_FIRST ; i<sc->total_chans; i++, ch++)\n\t{\n\t\tif (!ch->sfx)\n\t\t\tcontinue;\n\t\tif (ch->flags & CF_CLI_AUTOSOUND)\n\t\t{\n\t\t\tif (!ch->vol[0] && !ch->vol[1] && !ch->vol[2] && !ch->vol[3] && !ch->vol[4] && !ch->vol[5])\n\t\t\t{\n\t\t\t\tch->sfx = NULL;\n\t\t\t\tif (sc->ChannelUpdate)\n\t\t\t\t\tsc->ChannelUpdate(sc, ch, CUR_EVERYTHING);\n\t\t\t}\n\t\t\tch->vol[0] = ch->vol[1] = ch->vol[2] = ch->vol[3] = ch->vol[4] = ch->vol[5] = 0;\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (sc->ChannelUpdate)\n\t\t{\n\t\t\tif (ch->flags & CF_FOLLOW)\n\t\t\t\tSND_Spatialize(sc, ch);\t//update it a little\n\t\t\tsc->ChannelUpdate(sc, ch, CUR_SPACIALISEONLY);\n\t\t\tcontinue;\n\t\t}\n\n\t\tSND_Spatialize(sc, ch);         // respatialize channel\n\t\tif (!ch->vol[0] && !ch->vol[1] && !ch->vol[2] && !ch->vol[3] && !ch->vol[4] && !ch->vol[5])\n\t\t\tcontinue;\n\n\t// try to combine static sounds with a previous channel of the same\n\t// sound effect so we don't mix five torches every frame\n\n\t\tif (ch->flags & CF_CLI_STATIC)\n\t\t{\n\t\t// see if it can just use the last one\n\t\t\tif (combine && combine->sfx == ch->sfx)\n\t\t\t{\n\t\t\t\tcombine->vol[0] += ch->vol[0];\n\t\t\t\tcombine->vol[1] += ch->vol[1];\n\t\t\t\tcombine->vol[2] += ch->vol[2];\n\t\t\t\tcombine->vol[3] += ch->vol[3];\n\t\t\t\tcombine->vol[4] += ch->vol[4];\n\t\t\t\tcombine->vol[5] += ch->vol[5];\n\t\t\t\tch->vol[0] = ch->vol[1] = ch->vol[2] = ch->vol[3] = ch->vol[4] = ch->vol[5] = 0;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t// search for one\n\t\t\tcombine = sc->channel+DYNAMIC_FIRST;\n\t\t\tfor (j=DYNAMIC_FIRST ; j<i; j++, combine++)\n\t\t\t\tif (combine->sfx == ch->sfx)\n\t\t\t\t\tbreak;\n\n\t\t\tif (j == sc->total_chans)\n\t\t\t{\n\t\t\t\tcombine = NULL;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (combine != ch)\n\t\t\t\t{\n\t\t\t\t\tcombine->vol[0] += ch->vol[0];\n\t\t\t\t\tcombine->vol[1] += ch->vol[1];\n\t\t\t\t\tcombine->vol[2] += ch->vol[2];\n\t\t\t\t\tcombine->vol[3] += ch->vol[3];\n\t\t\t\t\tcombine->vol[4] += ch->vol[4];\n\t\t\t\t\tcombine->vol[5] += ch->vol[5];\n\t\t\t\t\tch->vol[0] = ch->vol[1] = ch->vol[2] = ch->vol[3] = ch->vol[4] = ch->vol[5] = 0;\n\t\t\t\t}\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\t}\n\n\tS_Q2_AddEntitySounds(sc);\n\n//\n// debugging output\n//\n\tif (snd_show.ival)\n\t{\n\t\tstruct listener_s *l;\n\t\tint\t\t\tactive, mute;\n\t\tactive = 0;\n\t\tmute = 0;\n\t\tch = sc->channel;\n\t\tfor (i=0 ; i<sc->total_chans; i++, ch++)\n\t\t{\n\t\t\tif (ch->sfx && (ch->vol[0] || ch->vol[1]) )\n\t\t\t{\n\t\t\t\tif (snd_show.ival > 1)\n\t\t\t\t\tCon_Printf (\"%i, %i/%i/%i/%i/%i/%i %s\\n\", i, ch->vol[0], ch->vol[1], ch->vol[2], ch->vol[3], ch->vol[4], ch->vol[5], ch->sfx->name);\n\t\t\t\tactive++;\n\t\t\t}\n\t\t\telse if (ch->sfx)\n\t\t\t\tmute++;\n\t\t}\n\n\t\tif (sc->seat < 0)\n\t\t\tl = &listener[0];\n\t\telse\n\t\t\tl = &listener[sc->seat];\n\t\tCon_Printf (\"----(%i+%i %s %i %.1f %.1f %.1f)----\\n\", active, mute, sc->name, l->entnum, l->origin[0], l->origin[1], l->origin[2]);\n\t}\n\n#ifdef HAVE_MIXER\n// mix some sound\n\n\tif (sc->selfpainting)\n\t\treturn;\n\n\tif (snd_blocked > 0)\n\t{\n\t\tif (!sc->inactive_sound)\n\t\t\treturn;\n\t}\n\n\tS_Update_(sc);\n#endif\n}\n\n#ifdef HAVE_MIXER\nint S_GetMixerTime(soundcardinfo_t *sc)\n{\n\tint\t\tsamplepos;\n\tint\t\tfullsamples;\n\n\tfullsamples = sc->sn.samples / sc->sn.numchannels;\n\n// it is possible to miscount buffers if it has wrapped twice between\n// calls to S_Update.  Oh well.\n\tsamplepos = sc->GetDMAPos(sc);\n\n\tif (sc->samplequeue > 0)\n\t\tsamplepos -= sc->samplequeue;\n\n\tif (samplepos < 0)\n\t{\n\t\tsamplepos = 0;\n\t}\n\tif (samplepos < sc->oldsamplepos)\n\t{\n\t\tint bias;\n\t\tsc->buffers++;\t\t\t\t\t// buffer wrapped\n\n\t\tif (sc->paintedtime > 0x40000000)\n\t\t{\n\t\t\t//when things get too large, we push everything back to prevent overflows\n\t\t\tbias = sc->paintedtime;\n\t\t\tbias -= bias % fullsamples;\n\t\t\tsc->paintedtime -= bias;\n\t\t\tsc->buffers -= bias / fullsamples;\n\t\t}\n\t}\n\tsc->oldsamplepos = samplepos;\n\n\treturn sc->buffers*fullsamples + samplepos/sc->sn.numchannels;\n}\n#endif\n\nvoid S_Update (void)\n{\n\tsoundcardinfo_t *sc;\n\tRSpeedMark();\n\tS_LockMixer();\n\tfor (sc = sndcardinfo; sc; sc = sc->next)\n\t\tS_UpdateCard(sc);\n\tS_UnlockMixer();\n\tRSpeedEnd(RSPEED_AUDIO);\n}\n\nvoid S_ExtraUpdate (void)\n{\n#ifdef HAVE_MIXER\n\tsoundcardinfo_t *sc;\n#endif\n\n\tif (!sound_started)\n\t\treturn;\n\n#if defined(_WIN32) && !defined(WINRT)\n\tINS_Accumulate ();\n#endif\n#ifdef HAVE_MIXER\n\tif (snd_noextraupdate.ival)\n\t\treturn;\t\t// don't pollute timings\n\n\tfor (sc = sndcardinfo; sc; sc = sc->next)\n\t{\n\t\tif (sc->selfpainting)\n\t\t\tcontinue;\n\n\t\tif (snd_blocked > 0)\n\t\t{\n\t\t\tif (!sc->inactive_sound)\n\t\t\t\tcontinue;\n\t\t}\n\n\t\tS_LockMixer();\n\t\tS_Update_(sc);\n\t\tS_UnlockMixer();\n\t}\n#endif\n}\n\n\n#ifdef HAVE_MIXER\nstatic void S_Update_(soundcardinfo_t *sc)\n{\n\tint soundtime; /*in pairs*/\n\tunsigned        endtime;\n\tint\t\t\t\tsamps;\n\n// Updates DMA time\n\tsoundtime = S_GetMixerTime(sc);\n\n\tif (sc->samplequeue > 0)\n\t{\n\t\t/*device uses a write-once queue*/\n\t\tendtime = soundtime + sc->samplequeue/sc->sn.numchannels;\n\t\tsoundtime = sc->paintedtime;\n\t\tsamps = sc->samplequeue / sc->sn.numchannels;\n\t}\n\telse if (sc->samplequeue < 0)\n\t{\t/*device is telling us the exact point that we should be mixing to*/\n\t\tendtime = soundtime;\n\t\tsoundtime = sc->paintedtime;\n\t\tsamps = sc->sn.samples / sc->sn.numchannels;\n\t}\n\telse\n\t{\n\t\t/*device uses memory-mapped output*/\n\t\t// check to make sure that we haven't overshot\n\t\tif (sc->paintedtime < soundtime)\n\t\t{\n\t\t\t//Con_Printf (\"S_Update_ : overflow\\n\");\n\t\t\tsc->paintedtime = soundtime;\n\t\t}\n\n\t\t// mix ahead of current position\n\t\tendtime = soundtime + (int)(_snd_mixahead.value * sc->sn.speed);\t\n\t\tsamps = sc->sn.samples / sc->sn.numchannels;\n\t}\n\tif (endtime - soundtime > samps)\n\t{\n\t\tendtime = soundtime + samps;\n\t}\n\n\t/*DirectSound may have killed us to give priority to another app, ask to restore it*/\n\tif (sc->Restore)\n\t\tsc->Restore(sc);\n\n\tS_PaintChannels (sc, endtime);\n\n\tsc->Submit(sc, soundtime, endtime);\n}\n\n/*\ncalled periodically by dedicated mixer threads.\ndo any blocking calls AFTER this returns. note that this means you can't use the Submit/unlock method to submit blocking audio.\n*/\nvoid S_MixerThread(soundcardinfo_t *sc)\n{\n\tS_LockMixer();\n\tS_Update_(sc);\n\tS_UnlockMixer();\n}\n#endif\n\n/*\n===============================================================================\n\nconsole functions\n\n===============================================================================\n*/\n\nvoid S_Play_f(void)\n{\t//plays a sound located around the player\n\tint \ti;\n\tchar name[256];\n\tsfx_t\t*sfx;\n\tconst char *cmdname = Cmd_Argv(0);\n\tfloat vol, attenuation = 0;\n\tunsigned int flags = CF_NOSPACIALISE;\n\tint entnum = 0;\n\tfloat *origin = NULL;\n\n\n/*\t//Vanilla compat (breaks modern QW mods):\n   \tif (!strcmp(cmdname, \"play\"))\n\t{\n\t\tflags = 0;\n\t\tattenuation = 1;\n\t\torigin = listener[0].origin;\n\t\tentnum = listener[0].entnum;\n\t}\n*/\n\n\ti = 1;\n\twhile (i<Cmd_Argc())\n\t{\n\t\tif (!Q_strrchr(Cmd_Argv(i), '.'))\n\t\t{\n\t\t\tQ_strncpyz(name, Cmd_Argv(i), sizeof(name)-4);\n\t\t\tQ_strcat(name, \".wav\");\n\t\t}\n\t\telse\n\t\t\tQ_strncpyz(name, Cmd_Argv(i), sizeof(name));\n\t\ti++;\n\t\tsfx = S_PrecacheSound(name);\n\n\t\tif (!strcmp(cmdname, \"playvol\"))\n\t\t\tvol = Q_atof(Cmd_Argv(i++));\n\t\telse\n\t\t\tvol = 1.0;\n\t\tS_StartSound(entnum, 0, sfx, origin, NULL, vol, attenuation, 0, 0, flags);\n\t}\n}\n\nvoid S_SoundList_f(void)\n{\n\tint\t\ti;\n\tsfx_t\t*sfx;\n\tsfxcache_t\t*sc;\n\tsfxcache_t\tscachebuf;\n\tint\t\tsize, total;\n\tint duration;\n\n\tS_LockMixer();\n\n\n\ttotal = 0;\n\tfor (sfx=known_sfx, i=0 ; i<num_sfx ; i++, sfx++)\n\t{\n\t\tif (sfx->loadstate != SLS_LOADED)\n\t\t\tsc = NULL;\n\t\telse if (sfx->decoder.decodedata)\n\t\t{\n\t\t\tif (sfx->decoder.querydata)\n\t\t\t\tsc = (sfx->decoder.querydata(sfx, &scachebuf, NULL, 0) < 0)?NULL:&scachebuf;\n\t\t\telse\n\t\t\t\tsc = NULL;\t//don't bother trying to actually decode anything here.\n\t\t\tif (!sc)\n\t\t\t{\n\t\t\t\tCon_Printf(\"S(      )            : %s\\n\", sfx->name);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t\tsc = sfx->decoder.buf;\n\t\tif (!sc)\n\t\t{\n\t\t\tCon_Printf(\"?(      )            : %s\\n\", sfx->name);\n\t\t\tcontinue;\n\t\t}\n\t\tsize = (sc->soundoffset+sc->length)*QAF_BYTES(sc->format)*(sc->numchannels);\n\t\tduration = (sc->soundoffset+sc->length) / sc->speed;\n\t\ttotal += size;\n\t\tif (sfx->loopstart >= 0)\n\t\t\tCon_Printf (\"L\");\n\t\telse\n\t\t\tCon_Printf (\" \");\n\t\tCon_Printf(\"(%2db%2ic) %6i %2is : %s\\n\",QAF_BYTES(sc->format)*8, sc->numchannels, size, duration, sfx->name);\n\t}\n\tCon_Printf (\"Total resident: %i\\n\", total);\n\n\tS_UnlockMixer();\n}\n\n\nvoid S_LocalSound2 (const char *sound, int channel, float volume)\n{\n\tsfx_t\t*sfx;\n\n\tif (nosound.ival)\n\t\treturn;\n\tif (!sound_started)\n\t\treturn;\n\n\tsfx = S_PrecacheSound (sound);\n\tif (!sfx)\n\t{\n\t\tCon_Printf (\"S_LocalSound: can't cache %s\\n\", sound);\n\t\treturn;\n\t}\n\tS_StartSound (0, channel, sfx, NULL, NULL, volume, 0, 0, 0, CF_CLI_INACTIVE|CF_NOSPACIALISE|CF_NOREVERB);\n}\nvoid S_LocalSound (const char *sound)\n{\n\tS_LocalSound2(sound, 256, 1);\n}\n\n\n\n\n\n\n\n\n\n\n\ntypedef struct {\n\tsfxdecode_t decoder;\n\n\tqboolean inuse;\n\tint id;\n\tsfx_t *sfx;\n\n\tint numchannels;\n\tqaudiofmt_t format;\n\tint length;\n\tvoid *data;\n} streaming_t;\n#define MAX_RAW_SOURCES (MAX_CLIENTS+3)\nstreaming_t s_streamers[MAX_RAW_SOURCES];\n\nvoid S_ClearRaw(void)\n{\n\tmemset(s_streamers, 0, sizeof(s_streamers));\n}\n\n//returns an sfxcache_t stating where the data is\nsfxcache_t *QDECL S_Raw_Locate(sfx_t *sfx, sfxcache_t *buf, ssamplepos_t start, int length)\n{\n\tstreaming_t *s = sfx->decoder.buf;\n\tif (buf)\n\t{\n\t\tbuf->data = s->data;\n\t\tbuf->length = s->length;\n\t\tbuf->numchannels = s->numchannels;\n\t\tbuf->soundoffset = 0;\n\t\tbuf->speed = snd_speed;\n\t\tbuf->format = s->format;\n\t}\n\tif (start >= s->length)\n\t\treturn NULL;\t//eof...\n\treturn buf;\n}\nvoid QDECL S_Raw_Ended(sfx_t *sfx)\n{\t//no longer playing anywhere...\n\tstreaming_t *s = sfx->decoder.buf;\n\ts->inuse = false;\t//let it get reused now.\n}\nvoid QDECL S_Raw_Purge(sfx_t *sfx)\n{\t//flush all caches, will be re-read from disk (or not, because this is streamed)\n\tstreaming_t *s = sfx->decoder.buf;\n\ts->length = 0;\n\ts->numchannels = 0;\n\tBZ_Free(s->data);\n\ts->data = NULL;\n\ts->inuse = false;\n\n\tmemset(&sfx->decoder, 0, sizeof(sfx->decoder));\n}\n\nfloat S_RawAudioQueued(int sourceid)\t//returns in seconds. we don't know what the original sample count was.\n{\n\tsoundcardinfo_t *si;\n\tstreaming_t *s;\n\tint i;\n\tfloat r;\n\tssamplepos_t highest, pos;\n\tfor (s = s_streamers, i = 0; i < MAX_RAW_SOURCES; i++, s++)\n\t{\n\t\tif (s->inuse && s->id == sourceid)\n\t\t{\n\t\t\tS_LockMixer();\n\n\t\t\thighest = ((~(usamplepos_t)0)>>1);\n\t\t\tfor (si = sndcardinfo; si; si=si->next)\t//make sure all cards are playing, and that we still get a prepad if just one is.\n\t\t\t{\n\t\t\t\tfor (i = 0; i < si->total_chans; i++)\n\t\t\t\t\tif (si->channel[i].sfx == s->sfx)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (si->GetChannelPos)\n\t\t\t\t\t\t\tpos = si->GetChannelPos(si, &si->channel[i]);\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tpos = si->channel[i].pos>>PITCHSHIFT;\n\t\t\t\t\t\tif (highest > pos)\n\t\t\t\t\t\t\thighest = pos;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t}\n\t\t\tif (highest == ((~(usamplepos_t)0)>>1))\n\t\t\t\tr = 0;\t//nothing playing it... needs to be woken up. pretend nothing is there so it gets poked a bit.\n\t\t\telse\n\t\t\t\tr = (s->length - highest) / (float)snd_speed;\n\t\t\tS_UnlockMixer();\n\t\t\treturn r;\n\t\t}\n\t}\n\treturn 0;\t//not found\n}\n\n//streaming audio.\t//this is useful when there is one source, and the sound is to be played with no attenuation\nvoid S_RawAudio(int sourceid, const qbyte *data, int speed, int samples, int channels, qaudiofmt_t format, float volume)\n{\n\tsoundcardinfo_t *si;\n\tint i;\n\tint prepadl;\t//this is the amount of data that was previously available, and will be removed from the buffer.\n\tint spare;\t\t//the amount of existing data that is still left to be played\n\tint outsamples;\t//the amount of data we're going to add (at the output rate)\n\tdouble speedfactor;\n\tqbyte *newcache;\n\tstreaming_t *s, *free=NULL;\n\n\tif (!sound_started)\n\t\treturn;\n\n\tfor (s = s_streamers, i = 0; i < MAX_RAW_SOURCES; i++, s++)\n\t{\n\t\tif (!s->inuse)\n\t\t{\n\t\t\tif (!free)\n\t\t\t\tfree = s;\n\t\t\tcontinue;\n\t\t}\n\t\tif (s->id == sourceid)\n\t\t\tbreak;\n\t}\n\tif (!data)\n\t{\n\t\tif (i == MAX_RAW_SOURCES)\n\t\t\treturn;\t//wierd, it wasn't even playing.\n\t\ts->inuse = false;\n\n\t\tS_LockMixer();\n\t\tfor (si = sndcardinfo; si; si=si->next)\n\t\tfor (i = 0; i < si->total_chans; i++)\n\t\t\tif (si->channel[i].sfx == s->sfx)\n\t\t\t{\n\t\t\t\tsi->channel[i].sfx = NULL;\n\t\t\t\tbreak;\n\t\t\t}\n\t\tBZ_Free(s->data);\n\t\ts->data = NULL;\n\t\tS_UnlockMixer();\n\t\treturn;\n\t}\n\tif (i == MAX_RAW_SOURCES || !s->inuse)\t//whoops.\n\t{\n\t\tif (i == MAX_RAW_SOURCES)\n\t\t{\n\t\t\tif (!free)\n\t\t\t{\n\t\t\t\tCon_Printf(\"No free audio streams\\n\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\ts = free;\n\t\t}\n\n\t\tif (!s->sfx)\n\t\t\ts->sfx = S_FindName(va(\"***stream_%i***\", i), true, false);\n\t\ts->sfx->decoder.buf = s;\n\t\ts->sfx->decoder.decodedata = S_Raw_Locate;\n\t\ts->sfx->decoder.ended = S_Raw_Ended;\n\t\ts->sfx->decoder.purge = S_Raw_Purge;\n\t\ts->sfx->loopstart = -1; //non-looping...\n\t\ts->sfx->loadstate = SLS_LOADED;\n\n\t\ts->numchannels = channels;\n\t\ts->format = format;\n\t\ts->data = NULL;\n\t\ts->length = 0;\n\n\t\ts->id = sourceid;\n\t\ts->inuse = true;\n//\t\tCon_Printf(\"Added new raw stream\\n\");\n\t}\n\tS_LockMixer();\n\n\tif (s->format != format || s->numchannels != channels)\n\t{\n\t\ts->format = format;\n\t\ts->numchannels = channels;\n\t\ts->length = 0;\n\t\tCon_Printf(\"Restarting raw stream\\n\");\n\t}\n\n\tspeedfactor\t= (double)speed/snd_speed;\n\toutsamples = samples/speedfactor;\n\n\tprepadl = 0x7fffffff;\n\tfor (si = sndcardinfo; si; si=si->next)\t//make sure all cards are playing, and that we still get a prepad if just one is.\n\t{\n\t\tfor (i = 0; i < si->total_chans; i++)\n\t\t\tif (si->channel[i].sfx == s->sfx)\n\t\t\t{\n\t\t\t\tif (prepadl > (si->channel[i].pos>>PITCHSHIFT))\n\t\t\t\t\tprepadl = (si->channel[i].pos>>PITCHSHIFT);\n\t\t\t\tbreak;\n\t\t\t}\n\t}\n\n\tif (prepadl == 0x7fffffff)\n\t{\n\t\tif (snd_show.ival)\n\t\t\tCon_Printf(\"Wasn't playing\\n\");\n\t\tprepadl = 0;\n\t\tspare = 0;\n\t\tif (spare > snd_speed)\n\t\t{\n\t\t\tCon_DPrintf(\"Sacrificed raw sound stream\\n\");\n\t\t\tspare = 0;\t//too far out. sacrifice it all\n\t\t}\n\t}\n\telse\n\t{\n\t\tif (prepadl < 0)\n\t\t\tprepadl = 0;\n\t\tspare = s->length - prepadl;\n\t\tif (spare < 0)\t//remaining samples since last time\n\t\t\tspare = 0;\n\n\t\tif (spare > snd_speed*2) // more than 2 seconds of sound. don't buffer more than 2 seconds. 1: its probably buggy if we need to. 2: takes too much memory, and we use malloc+copies.\n\t\t{\n\t\t\tCon_DPrintf(\"Sacrificed raw sound stream\\n\");\n\t\t\tspare = 0;\t//too far out. sacrifice it all\n\t\t}\n\t}\n\n\tnewcache = BZ_Malloc((spare+outsamples) * (s->numchannels) * QAF_BYTES(s->format));\n\tmemcpy(newcache, (qbyte*)s->data + prepadl * (s->numchannels) * QAF_BYTES(s->format), spare * (s->numchannels) * QAF_BYTES(s->format));\n\n\tBZ_Free(s->data);\n\ts->data = newcache;\n\n\ts->length = spare + outsamples;\n\n\t{\n\t\textern cvar_t snd_linearresample_stream;\n\t\tshort *outpos = (short *)((char*)s->data + spare * (s->numchannels) * QAF_BYTES(s->format));\n\t\tSND_ResampleStream(data,\n\t\t\tspeed,\n\t\t\tformat,\n\t\t\tchannels,\n\t\t\tsamples,\n\t\t\toutpos,\n\t\t\tsnd_speed,\n\t\t\ts->format,\n\t\t\ts->numchannels,\n\t\t\tsnd_linearresample_stream.ival);\n\t}\n\n\tfor (si = sndcardinfo; si; si=si->next)\n\t{\n\t\tfor (i = 0; i < si->total_chans; i++)\n\t\t\tif (si->channel[i].sfx == s->sfx)\n\t\t\t{\n\t\t\t\tsi->channel[i].pos -= prepadl*si->channel[i].rate;\n\n\t\t\t\tif (si->channel[i].pos < 0)\n\t\t\t\t\tsi->channel[i].pos = 0;\n\t\t\t\tsi->channel[i].master_vol = 255 * volume;\n\t\t\t\tif (si->ChannelUpdate)\n\t\t\t\t\tsi->ChannelUpdate(si, &si->channel[i], CUR_SPACIALISEONLY);\n\t\t\t\tbreak;\n\t\t\t}\n\t\tif (i == si->total_chans)\t//this one wasn't playing.\n\t\t{\n\t\t\tchannel_t *c = SND_PickChannel(si, -1, 0);\n\t\t\tif (c)\n\t\t\t{\n\t\t\t\tc->flags = (sourceid>=0?CF_CLI_INACTIVE:0)|CF_CL_ABSVOLUME|CF_NOSPACIALISE;\n\t\t\t\tc->entnum = 0;\n\t\t\t\tc->entchannel = 0;\n\t\t\t\tc->dist_mult = 0;\n\t\t\t\tc->master_vol = 255 * volume;\n\t\t\t\tc->pos = 0;\n\t\t\t\tc->rate = 1<<PITCHSHIFT;\n\t\t\t\tc->sfx = s->sfx;\n\t\t\t\tSND_Spatialize(si, c);\n\n\t\t\t\tif (si->ChannelUpdate)\n\t\t\t\t\tsi->ChannelUpdate(si, c, CUR_EVERYTHING);\n\t\t\t}\n\t\t}\n\t}\n\tS_UnlockMixer();\n}\n"
  },
  {
    "path": "engine/client/snd_droid.c",
    "content": "/*\r\nthis file is basically a copy of the SDL one\r\njava code has a function or two which just periodically calls us to ask us to dump out audio for it\r\n*/\r\n#include \"quakedef.h\"\r\n#include <jni.h>\r\n#include <pthread.h>\r\n\r\nstatic soundcardinfo_t *sys_sc = NULL;\r\nextern int sys_soundflags;\r\n\r\n//called by the java code when it wants to know what sort of AudioTrack format to use.\r\nJNIEXPORT jint JNICALL Java_com_fteqw_FTEDroidEngine_audioinfo(JNIEnv *env, jclass this, jint arg)\r\n{\r\n\tsoundcardinfo_t *sc = sys_sc;\r\n\tif (!sc)\r\n\t\treturn 0;\r\n\r\n\tswitch(arg)\r\n\t{\r\n\tcase 1:\r\n\t\treturn sc->sn.numchannels;\r\n\tcase 2:\r\n\t\treturn sc->sn.samplebytes*8;\r\n\tdefault:\r\n\t\treturn sc->sn.speed;\r\n\t}\r\n}\r\n\r\nextern int S_GetMixerTime(soundcardinfo_t *sc);\r\n//transfer the 'dma' buffer into the buffer it requests, called from a dedicated sound thread created by the java code.\r\nJNIEXPORT jint JNICALL Java_com_fteqw_FTEDroidEngine_paintaudio(JNIEnv *env, jclass this, jbyteArray stream, jint len)\r\n{\r\n\tint offset = 0;\r\n\tsoundcardinfo_t *sc = sys_sc;\r\n\tint framesz;\r\n\r\n\tif (sc)\r\n\t{\r\n\t\tint buffersize = sc->sn.samples*sc->sn.samplebytes;\r\n\r\n\t\tint curtime = S_GetMixerTime(sc);\r\n\t\tframesz = sc->sn.numchannels * sc->sn.samplebytes;\r\n\r\n\t\tS_PaintChannels (sc, curtime + (len / framesz));\r\n\r\n\t\tif (len > buffersize)\r\n\t\t{\r\n\t\t\tlen = buffersize;       //whoa nellie!\r\n\t\t}\r\n\r\n\t\tif (len + sc->snd_sent%buffersize > buffersize)\r\n\t\t{\t//buffer will wrap, fill in the rest\r\n\t\t\t(*env)->SetByteArrayRegion(env, stream, offset, buffersize - (sc->snd_sent%buffersize), (char*)sc->sn.buffer + (sc->snd_sent%buffersize));\r\n\t\t\toffset += buffersize - (sc->snd_sent%buffersize);\r\n\t\t\tsc->snd_sent += buffersize - (sc->snd_sent%buffersize);\r\n\t\t\tlen -= buffersize - (sc->snd_sent%buffersize);\r\n\t\t\tif (len < 0) /*this must be impossible, surely?*/\r\n\t\t\t\tlen = 0;\r\n\t\t}\r\n\t\t//and finish from the start\r\n\t\t(*env)->SetByteArrayRegion(env, stream, offset, len, (char*)sc->sn.buffer + (sc->snd_sent%buffersize));\r\n\t\toffset += len;\r\n\t\tsc->snd_sent += len;\r\n\t}\r\n\telse\r\n\t\toffset = len;\t/*so the playback thread ends up blocked properly*/\r\n\treturn offset;\r\n}\r\n\r\n\r\nstatic void Droid_Shutdown(soundcardinfo_t *sc)\r\n{\r\n\t//fixme: what if we're currently inside Java_com_fteqw_FTEDroidEngine_paintaudio?\r\n\tsys_sc = NULL;\r\n\tfree(sc->sn.buffer);\r\n\tsys_soundflags = 0;\r\n}\r\n\r\n//return the number of samples that have already been submitted to the device.\r\nstatic unsigned int Droid_GetDMAPos(soundcardinfo_t *sc)\r\n{\r\n\tsc->sn.samplepos = sc->snd_sent / sc->sn.samplebytes;\r\n\treturn sc->sn.samplepos;\r\n}\r\n\r\nstatic void Droid_UnlockBuffer(soundcardinfo_t *sc, void *buffer)\r\n{\r\n}\r\n\r\nstatic void *Droid_LockBuffer(soundcardinfo_t *sc, unsigned int *sampidx)\r\n{\r\n\treturn sc->sn.buffer;\r\n}\r\n\r\n/*\r\nstatic void Droid_SetEnvironmentReverb(soundcardinfo_t *sc, qboolean uw)\r\n{\r\n}\r\n*/\r\n\r\nstatic void Droid_Submit(soundcardinfo_t *sc, int start, int end)\r\n{\r\n}\r\n\r\n//on android, 16bit audio is 'guarenteed'.\r\n//8bit is not guarenteed.\r\n//there's no reference to sample rates.\tI assume 44.1khz will always work, though we want to avoid that cpu+mem load if we can\r\n//nor any guarentee about channels supported. I assume mono will always work.\r\nstatic qboolean Droid_InitCard (soundcardinfo_t *sc, const char *cardname)\r\n{\r\n\tif (sys_sc)\r\n\t\treturn false;\t//can only cope with one device.\r\n\tif (cardname && *cardname)\r\n\t\treturn false;\t//only the default device\r\n\r\n\tsc->selfpainting = true;\r\n//\tsc->sn.speed = 11025;\r\n//\tsc->sn.samplebytes = 2;\r\n//\tsc->sn.numchannels = 1;\r\n\r\n\tif (sc->sn.samplebytes == 1)\r\n\t\tsc->sn.sampleformat = QSF_U8;\r\n\telse /*if (sc->sn.samplebytes == 2)*/\r\n\t{\r\n\t\tsc->sn.samplebytes = 2;\r\n\t\tsc->sn.sampleformat = QSF_S16;\r\n\t}\r\n\r\n\t/*internal buffer should have 1 sec audio*/\r\n\tsc->sn.samples = sc->sn.speed*sc->sn.numchannels;\r\n\r\n\tsc->Lock = Droid_LockBuffer;\r\n\tsc->Unlock = Droid_UnlockBuffer;\r\n//\tsc->SetEnvironmentReverb = Droid_SetEnvironmentReverb;\r\n\tsc->Submit = Droid_Submit;\r\n\tsc->Shutdown = Droid_Shutdown;\r\n\tsc->GetDMAPos = Droid_GetDMAPos;\r\n\r\n\tsc->sn.buffer = malloc(sc->sn.samples*sc->sn.samplebytes);\r\n\t\r\n\tsys_sc = sc;\r\n\r\n\tsys_soundflags = 3;\r\n\r\n\treturn 1;\r\n}\r\n\r\nsounddriver_t Droid_AudioOutput =\r\n{\r\n\t\"Android\",\r\n\tDroid_InitCard,\r\n\tNULL\r\n};\r\n"
  },
  {
    "path": "engine/client/snd_linux.c",
    "content": "#include <unistd.h>\n#include <fcntl.h>\n#include <stdlib.h>\n#include <sys/types.h>\n#include <sys/ioctl.h>\n#include <sys/mman.h>\n#include <sys/shm.h>\n#include <sys/wait.h>\n#if defined(__OpenBSD__)\n\t#include <soundcard.h>\t//OpenBSD emulates this, so its no longer sys/.\n#else\n\t#include <sys/soundcard.h>\n#endif\n#include <stdio.h>\n#include \"quakedef.h\"\n\n#ifdef HAVE_MIXER\n\n#ifdef __linux__\n#include <sys/stat.h>\n#endif\n\n#ifndef AFMT_FLOAT\n\t#define AFMT_FLOAT 0x00004000\t//OSS4 supports it, but linux is too shit to define it.\n#endif\n\n\nstatic int tryrates[] = { 11025, 22051, 44100, 8000, 48000 };\n\nstatic unsigned int OSS_MMap_GetDMAPos(soundcardinfo_t *sc)\n{\n\tstruct count_info count;\n\n\tif (sc->audio_fd != -1)\n\t{\n\t\tif (ioctl(sc->audio_fd, SNDCTL_DSP_GETOPTR, &count)==-1)\n\t\t{\n\t\t\tperror(\"/dev/dsp\");\n\t\t\tCon_Printf(\"Uh, sound dead.\\n\");\n\t\t\tclose(sc->audio_fd);\n\t\t\tsc->audio_fd = -1;\n\t\t\treturn 0;\n\t\t}\n//\t\tshm->samplepos = (count.bytes / shm->samplebytes) & (shm->samples-1);\n//\t\tfprintf(stderr, \"%d    \\r\", count.ptr);\n\t\tsc->sn.samplepos = count.ptr / sc->sn.samplebytes;\n\t}\n\treturn sc->sn.samplepos;\n\n}\nstatic void OSS_MMap_Submit(soundcardinfo_t *sc, int start, int end)\n{\n}\n\nstatic unsigned int OSS_Alsa_GetDMAPos(soundcardinfo_t *sc)\n{\n\tstruct audio_buf_info info;\n\tunsigned int bytes;\n\tif (ioctl (sc->audio_fd, SNDCTL_DSP_GETOSPACE, &info) != -1)\n\t{\n\t\tbytes = sc->snd_sent + info.bytes;\n\t\tsc->sn.samplepos = bytes / sc->sn.samplebytes;\n\t}\n\treturn sc->sn.samplepos;\n}\n\n\nstatic void OSS_Alsa_Submit(soundcardinfo_t *sc, int start, int end)\n{\n\tunsigned int bytes, offset, ringsize;\n\tunsigned chunk;\n\tint result;\n\n\t/*we can't change the data that was already written*/\n\tbytes = end * sc->sn.numchannels * sc->sn.samplebytes;\n\tbytes -= sc->snd_sent;\n\tif (!bytes)\n\t\treturn;\n\n\tringsize = sc->sn.samples * sc->sn.samplebytes;\n\n\tchunk = bytes;\n\toffset = sc->snd_sent % ringsize;\n\n\tif (offset + chunk >= ringsize)\n\t\tchunk = ringsize - offset;\n\tresult = write(sc->audio_fd, sc->sn.buffer + offset, chunk);\n\tif (result < chunk)\n\t{\n\t\tif (result >= 0)\n\t\t\tsc->snd_sent += result;\n//\t\tprintf(\"full?\\n\");\n\t\treturn;\n\t}\n\tsc->snd_sent += chunk;\n\n\tchunk = bytes - chunk;\n\tif (chunk)\n\t{\n\t\tresult = write(sc->audio_fd, sc->sn.buffer, chunk);\n\t\tif (result > 0)\n\t\t\tsc->snd_sent += result;\n\t}\n}\n\nstatic void OSS_Shutdown(soundcardinfo_t *sc)\n{\n\tif (sc->sn.buffer)\t//close it properly, so we can go and restart it later.\n\t{\n\t\tif (sc->Submit == OSS_Alsa_Submit)\n\t\t\tfree(sc->sn.buffer); /*if using alsa-compat, just free the buffer*/\n\t\telse\n\t\t\tmunmap(sc->sn.buffer, sc->sn.samples * sc->sn.samplebytes);\n\t}\n\tif (sc->audio_fd != -1)\n\t\tclose(sc->audio_fd);\n\t*sc->name = '\\0';\n}\n\nstatic void *OSS_Lock(soundcardinfo_t *sc, unsigned int *sampidx)\n{\n\treturn sc->sn.buffer;\n}\n\nstatic void OSS_Unlock(soundcardinfo_t *sc, void *buffer)\n{\n}\n\nstatic qboolean OSS_InitCard(soundcardinfo_t *sc, const char *snddev)\n{\t//FIXME: implement snd_multipledevices somehow.\n\tint rc;\n\tint fmt;\n\tint tmp;\n\tint i;\n\tstruct audio_buf_info info;\n\tint caps;\n\tqboolean alsadetected = false;\n\n#ifdef __linux__\n\tstruct stat sb;\n\tif (stat(\"/proc/asound\", &sb) != -1)\n\t\talsadetected = true;\n#endif\n\n\tif (COM_CheckParm(\"-nooss\"))\n\t\treturn false;\n\n\tif (!snddev || !*snddev)\n\t\tsnddev = \"/dev/dsp\";\n\telse if (strncmp(snddev, \"/dev/dsp\", 8))\n\t{\n\t\tCon_Printf(\"Refusing to use non-dsp device\\n\");\n\t\treturn false;\n\t}\n\n\tsc->inactive_sound = true;\t//linux sound devices always play sound, even when we're not the active app...\n\n// open the sound device, confirm capability to mmap, and get size of dma buffer\n\n\tCon_Printf(\"Initing OSS sound device %s\\n\", snddev);\n\n#ifdef __linux__\n\t//linux is a pile of shit.\n\t//nonblock is needed to get around issues with the old/buggy linux oss3 clone implementation, as well as because this code is too lame to thread audio.\n\tsc->audio_fd = open(snddev, O_RDWR | O_NONBLOCK);\t//try the primary device\n\t//fixme: the following is desired once threading is supported.\n\t//int flags = fcntl(fd, F_GETFL, 0);\n\t//fcntl(fd, F_SETFL, flags & ~O_NONBLOCK);\n#else\n\t//FIXME: remove non-block if we're using threads.\n\tsc->audio_fd = open(snddev, O_WRONLY | O_NONBLOCK);\t//try the primary device\n#endif\n\tif (sc->audio_fd < 0)\n\t{\n\t\tperror(snddev);\n\t\tCon_Printf(CON_ERROR \"OSS: Could not open %s\\n\", snddev);\n\t\tOSS_Shutdown(sc);\n\t\treturn false;\n\t}\n\tQ_strncpyz(sc->name, snddev, sizeof(sc->name));\n\n//reset it\n\trc = ioctl(sc->audio_fd, SNDCTL_DSP_RESET, 0);\n\tif (rc < 0)\n\t{\n\t\tperror(snddev);\n\t\tCon_Printf(CON_ERROR \"OSS: Could not reset %s\\n\", snddev);\n\t\tOSS_Shutdown(sc);\n\t\treturn false;\n\t}\n\n//check its general capabilities, we need trigger+mmap\n\tif (ioctl(sc->audio_fd, SNDCTL_DSP_GETCAPS, &caps)==-1)\n\t{\n\t\tperror(snddev);\n\t\tCon_Printf(CON_ERROR \"OSS: Sound driver too old\\n\");\n\t\tOSS_Shutdown(sc);\n\t\treturn false;\n\t}\n\n//choose channels\n#ifdef SNDCTL_DSP_CHANNELS /*I'm paranoid, okay?*/\n\ttmp = sc->sn.numchannels;\n\trc = ioctl(sc->audio_fd, SNDCTL_DSP_CHANNELS, &tmp);\n\tif (rc < 0)\n\t{\n\t\tperror(snddev);\n\t\tCon_Printf(CON_ERROR \"OSS: Could not set %s to channels=%d\\n\", snddev, sc->sn.numchannels);\n\t\tOSS_Shutdown(sc);\n\t\treturn false;\n\t}\n\tsc->sn.numchannels = tmp;\n#else\n\ttmp = 0;\n\tif (sc->sn.numchannels == 2)\n\t\ttmp = 1;\n\trc = ioctl(sc->audio_fd, SNDCTL_DSP_STEREO, &tmp);\n\tif (rc < 0)\n\t{\n\t\tperror(snddev);\n\t\tCon_Printf(CON_ERROR \"OSS: Could not set %s to stereo=%d\\n\", snddev, sc->sn.numchannels);\n\t\tOSS_Shutdown(sc);\n\t\treturn false;\n\t}\n\tif (tmp)\n\t\tsc->sn.numchannels = 2;\n\telse\n\t\tsc->sn.numchannels = 1;\n#endif\n\n\t// ask the device what it supports\n\tioctl(sc->audio_fd, SNDCTL_DSP_GETFMTS, &fmt);\n\n\t//choose a format\n\tif (sc->sn.samplebytes >= 4 && (fmt & AFMT_FLOAT))\n\t{\n\t\tsc->sn.samplebytes = 4;\n\t\tsc->sn.sampleformat = QSF_F32;\n\t\trc = AFMT_FLOAT;\n\t\trc = ioctl(sc->audio_fd, SNDCTL_DSP_SETFMT, &rc);\n\t\tif (rc < 0)\n\t\t{\n\t\t\tperror(snddev);\n\t\t\tCon_Printf(CON_ERROR \"OSS: Could not support 16-bit data.  Try 8-bit.\\n\");\n\t\t\tOSS_Shutdown(sc);\n\t\t\treturn false;\n\t\t}\n\t}\n\telse if (sc->sn.samplebytes >= 2 && (fmt & AFMT_S16_NE))\n\t{\n\t\tsc->sn.samplebytes = 2;\n\t\tsc->sn.sampleformat = QSF_S16;\n\t\trc = AFMT_S16_NE;\n\t\trc = ioctl(sc->audio_fd, SNDCTL_DSP_SETFMT, &rc);\n\t\tif (rc < 0)\n\t\t{\n\t\t\tperror(snddev);\n\t\t\tCon_Printf(CON_ERROR \"OSS: Could not support 16-bit data.  Try 8-bit.\\n\");\n\t\t\tOSS_Shutdown(sc);\n\t\t\treturn false;\n\t\t}\n\t}\n\telse if (/*sc->sn.samplebytes == 1 && */(fmt & AFMT_U8))\n\t{\n\t\tsc->sn.samplebytes = 1;\n\t\tsc->sn.sampleformat = QSF_U8;\n\t\trc = AFMT_U8;\n\t\trc = ioctl(sc->audio_fd, SNDCTL_DSP_SETFMT, &rc);\n\t\tif (rc < 0)\n\t\t{\n\t\t\tperror(snddev);\n\t\t\tCon_Printf(CON_ERROR \"OSS: Could not support 8-bit data.\\n\");\n\t\t\tOSS_Shutdown(sc);\n\t\t\treturn false;\n\t\t}\n\t}\n\telse if (/*sc->sn.samplebytes == 1 && */(fmt & AFMT_S8))\n\t{\n\t\tsc->sn.samplebytes = 1;\n\t\tsc->sn.sampleformat = QSF_S8;\n\t\trc = AFMT_S8;\n\t\trc = ioctl(sc->audio_fd, SNDCTL_DSP_SETFMT, &rc);\n\t\tif (rc < 0)\n\t\t{\n\t\t\tperror(snddev);\n\t\t\tCon_Printf(CON_ERROR \"OSS: Could not support 8-bit data.\\n\");\n\t\t\tOSS_Shutdown(sc);\n\t\t\treturn false;\n\t\t}\n\t}\n\telse\n\t{\n\t\tperror(snddev);\n\t\tCon_Printf(CON_ERROR \"OSS: %d-bit sound not supported.\\n\", sc->sn.samplebytes*8);\n\t\tOSS_Shutdown(sc);\n\t\treturn false;\n\t}\n\n//choose speed\n\t//use the default - menu set value.\n\ttmp = sc->sn.speed;\n\tif (ioctl(sc->audio_fd, SNDCTL_DSP_SPEED, &tmp) != 0)\n\t{\t//humph, default didn't work. Go for random preset ones that should work.\n\t\tfor (i=0 ; i<sizeof(tryrates)/4 ; i++)\n\t\t{\n\t\t\ttmp = tryrates[i];\n\t\t\tif (!ioctl(sc->audio_fd, SNDCTL_DSP_SPEED, &tmp)) break;\n\t\t}\n\t\tif (i == (sizeof(tryrates)/4))\n\t\t{\n\t\t\tperror(snddev);\n\t\t\tCon_Printf(CON_ERROR \"OSS: Failed to obtain a suitable rate\\n\");\n\t\t\tOSS_Shutdown(sc);\n\t\t\treturn false;\n\t\t}\n\t}\n\tsc->sn.speed = tmp;\n\n//figure out buffer size\n\tif (ioctl(sc->audio_fd, SNDCTL_DSP_GETOSPACE, &info)==-1)\n\t{\n\t\tperror(\"GETOSPACE\");\n\t\tCon_Printf(CON_ERROR \"OSS: Um, can't do GETOSPACE?\\n\");\n\t\tOSS_Shutdown(sc);\n\t\treturn false;\n\t}\n\tsc->sn.samples = info.fragstotal * info.fragsize;\n\tsc->sn.samples /= sc->sn.samplebytes;\n\t/*samples is the number of samples*channels */\n\n// memory map the dma buffer\n\tsc->sn.buffer = MAP_FAILED;\n\tif (alsadetected)\n\t{\n\t\tCon_Printf(\"Refusing to mmap oss device in case alsa's oss emulation crashes.\\n\");\n\t}\n\telse if ((caps & DSP_CAP_TRIGGER) && (caps & DSP_CAP_MMAP))\n\t{\n\t\tsc->sn.buffer = (unsigned char *) mmap(NULL, sc->sn.samples*sc->sn.samplebytes, PROT_WRITE, MAP_FILE|MAP_SHARED, sc->audio_fd, 0);\n\t\tif (sc->sn.buffer == MAP_FAILED)\n\t\t{\n\t\t\tCon_Printf(\"%s: device reported mmap capability, but mmap failed.\\n\", snddev);\n\t\t\tif (alsadetected)\n\t\t\t{\n\t\t\t\tchar *f, *n;\n\t\t\t\tf = (char *)com_argv[0];\n\t\t\t\twhile((n = strchr(f, '/')))\n\t\t\t\t\tf = n + 1;\n\t\t\t\tCon_Printf(\"Your system is running alsa.\\nTry: sudo echo \\\"%s 0 0 direct\\\" > /proc/asound/card0/pcm0p/oss\\n\", f);\n\t\t\t}\n\t\t}\n\t}\n\tif (sc->sn.buffer == MAP_FAILED)\n\t{\n\t\tsc->sn.buffer = NULL;\n\n\t\tsc->samplequeue = info.bytes / sc->sn.samplebytes;\n\t\tsc->sn.samples*=2;\n\t\tsc->sn.buffer = malloc(sc->sn.samples*sc->sn.samplebytes);\n\t\tsc->Submit\t\t= OSS_Alsa_Submit;\n\t\tsc->GetDMAPos\t= OSS_Alsa_GetDMAPos;\n\t}\n\telse\n\t{\n\t\t// toggle the trigger & start her up\n\t\ttmp = 0;\n\t\trc  = ioctl(sc->audio_fd, SNDCTL_DSP_SETTRIGGER, &tmp);\n\t\tif (rc < 0)\n\t\t{\n\t\t\tperror(snddev);\n\t\t\tCon_Printf(CON_ERROR \"OSS: Could not toggle.\\n\");\n\t\t\tOSS_Shutdown(sc);\n\t\t\treturn false;\n\t\t}\n\t\ttmp = PCM_ENABLE_OUTPUT;\n\t\trc = ioctl(sc->audio_fd, SNDCTL_DSP_SETTRIGGER, &tmp);\n\t\tif (rc < 0)\n\t\t{\n\t\t\tperror(snddev);\n\t\t\tCon_Printf(CON_ERROR \"OSS: Could not toggle.\\n\");\n\t\t\tOSS_Shutdown(sc);\n\t\t\treturn false;\n\t\t}\n\t\tsc->Submit\t\t= OSS_MMap_Submit;\n\t\tsc->GetDMAPos\t= OSS_MMap_GetDMAPos;\n\t}\n\n\tsc->sn.samplepos = 0;\n\n\tsc->Lock\t\t= OSS_Lock;\n\tsc->Unlock\t\t= OSS_Unlock;\n\tsc->Shutdown\t= OSS_Shutdown;\n\n\treturn true;\n}\n\n#define SDRVNAME \"OSS\"\n#if defined(__linux__) && !defined(SNDCTL_SYSINFO)\ntypedef struct oss_sysinfo {\n\tchar product[32];   /* E.g. SunOS Audio */\n\tchar version[32];   /* E.g. 4.0a */\n\tint versionnum;     /* See OSS_GETVERSION */\n\tchar options[128];  /* NOT SUPPORTED */\n\tint numaudios;      /* # of audio/dsp devices */\n\tint openedaudio[8]; /* Reserved, always 0 */\n\tint numsynths;        /* NOT SUPPORTED, always 0 */\n\tint nummidis;         /* NOT SUPPORTED, always 0 */\n\tint numtimers;        /* NOT SUPPORTED, always 0 */\n\tint nummixers;        /* # of mixer devices */\n\tint openedmidi[8];    /* Mask of midi devices are busy */\n\tint numcards;         /* Number of sound cards in the system */\n\tint numaudioengines;  /* Number of audio engines in the system */\n\tchar license[16];     /* E.g. \"GPL\" or \"CDDL\" */\n\tchar revision_info[256];  /* Reserved */\n\tint filler[172];          /* Reserved */\n} oss_sysinfo;\n#define SNDCTL_SYSINFO          _IOR ('X', 1, oss_sysinfo)\n#endif\n\n#if defined(__linux__) && !defined(SNDCTL_AUDIOINFO)\ntypedef struct oss_audioinfo {\n\tint dev;  /* Device to query */\n\tchar name[64];  /* Human readable name */\n\tint busy;  /* reserved */\n\tint pid;  /* reserved */\n\tint caps;  /* PCM_CAP_INPUT, PCM_CAP_OUTPUT */\n\tint iformats;  /* Supported input formats */\n\tint oformats;  /* Supported output formats */\n\tint magic;  /* reserved */\n\tchar cmd[64];  /* reserved */\n\tint card_number;\n\tint port_number;  /* reserved */\n\tint mixer_dev;\n\tint legacy_device; /* Obsolete field. Replaced by devnode */\n\tint enabled;  /* reserved */\n\tint flags;  /* reserved */\n\tint min_rate;  /* Minimum sample rate */\n\tint max_rate;  /* Maximum sample rate */\n\tint min_channels;  /* Minimum number of channels */\n\tint max_channels;  /* Maximum number of channels */\n\tint binding;  /* reserved */\n\tint rate_source;  /* reserved */\n\tchar handle[32];  /* reserved */\n\tunsigned int nrates;  /* reserved */\n\tunsigned int rates[20];  /* reserved */\n\tchar song_name[64];  /* reserved */\n\tchar label[16];  /* reserved */\n\tint latency;  /* reserved */\n\tchar devnode[32];  /* Device special file name (absolute path) */\n\tint next_play_engine;  /* reserved */\n\tint next_rec_engine;  /* reserved */\n\tint filler[184];  /* reserved */\n} oss_audioinfo;\n#define SNDCTL_AUDIOINFO      _IOWR('X', 7, oss_audioinfo)\n#endif\n\nstatic qboolean QDECL OSS_Enumerate(void (QDECL *cb) (const char *drivername, const char *devicecode, const char *readablename))\n{\n#if defined(SNDCTL_SYSINFO) && defined(SNDCTL_AUDIOINFO)\n\tint i;\n\tint fd;\n\toss_sysinfo si;\n\tconst char *devmixer;\n\n\tif (COM_CheckParm(\"-nooss\"))\n\t\treturn true;\n\n\tdevmixer = getenv(\"OSS_MIXERDEV\");\n\tif (!devmixer)\n\t\tdevmixer = \"/dev/mixer\";\n\tfd = open(devmixer, O_RDWR|O_NONBLOCK, 0);\n\n\tif (fd == -1)\n\t\treturn true;\t//oss not supported. don't list any devices.\n\n\tmemset(&si, 0, sizeof(si));\t//just in case the driver is really dodgy...\n\tif (ioctl(fd, SNDCTL_SYSINFO, &si) >= 0)\n\t{\n\t\tif ((si.versionnum>>16) >= 4 || si.numaudios > 128)\n\t\t{\t//only trust all the fields if its recent enough and doesn't look dodgy.\n\t\t\tfor(i = 0; i < si.numaudios; i++)\n\t\t\t{\n\t\t\t\toss_audioinfo ai;\n\t\t\t\tmemset(&ai, 0, sizeof(ai));\t//just in case the driver is really dodgy...\n\t\t\t\tai.dev = i;\n\t\t\t\tif (ioctl(fd, SNDCTL_AUDIOINFO, &ai) >= 0)\n\t\t\t\t\tcb(SDRVNAME, ai.devnode, ai.name);\n\t\t\t}\n\t\t\tclose(fd);\n\t\t\treturn true;\n\t\t}\n\t\telse\n\t\t\tprintf(\"Not enumerating OSS %u.%u.%u devices.\\n\", (si.versionnum>>16)&0xffff, (si.versionnum>>8)&0xff, si.versionnum&0xff);\n\t}\n\telse\n\t\tprintf(\"OSS driver is too old to support device enumeration.\\n\");\n\tclose(fd);\n#endif\n\treturn false;\t//enumeration failed, will show only a default device.\n}\n\nsounddriver_t OSS_Output =\n{\n\tSDRVNAME,\n\tOSS_InitCard,\n\tOSS_Enumerate\n};\n\n#endif\n#ifdef VOICECHAT\t//this does apparently work after all.\n#include <stdint.h>\n\nstatic qboolean QDECL OSS_Capture_Enumerate (void (QDECL *callback) (const char *drivername, const char *devicecode, const char *readablename))\n{\n\tif (COM_CheckParm(\"-nooss\"))\n\t\treturn true;\t//no default devices or anything\n\n\t//open /dev/dsp or /dev/mixer or env(\"OSS_MIXERDEV\") or something\n\t//SNDCTL_SYSINFO to get sysinfo.numcards\n\t//for i=0; i<sysinfo.numcards\n\t//SNDCTL_CARDINFO\n\treturn false;\n}\nstatic void *OSS_Capture_Init(int rate, const char *snddev)\n{\n\tint tmp;\n\tintptr_t fd;\n\tif (!snddev || !*snddev)\n\t\tsnddev = \"/dev/dsp\";\n\tif (COM_CheckParm(\"-nooss\"))\n\t\treturn NULL;\n\tfd = open(snddev, O_RDONLY | O_NONBLOCK);       //try the primary device\n\tif (fd == -1)\n\t\treturn NULL;\n\n#ifdef SNDCTL_DSP_CHANNELS\n\ttmp = 1;\n\tif (ioctl(fd, SNDCTL_DSP_CHANNELS, &tmp) != 0)\n#else\n\ttmp = 0;\n\tif (ioctl(fd, SNDCTL_DSP_STEREO, &tmp) != 0)\n#endif\n\t{\n\t\tCon_Printf(\"Couldn't set mono\\n\");\n\t\tperror(snddev);\n\t}\n\n\ttmp = AFMT_S16_LE;\n\tif (ioctl(fd, SNDCTL_DSP_SETFMT, &tmp) != 0)\n\t{\n\t\tCon_Printf(\"Couldn't set sample bits\\n\");\n\t\tperror(snddev);\n\t}\n\n\ttmp = rate;\n\tif (ioctl(fd, SNDCTL_DSP_SPEED, &tmp) != 0)\n\t{\n\t\tCon_Printf(\"Couldn't set capture rate\\n\");\n\t\tperror(snddev);\n\t}\n\n\tfd++;\n\treturn (void*)fd;\n}\nstatic void OSS_Capture_Start(void *ctx)\n{\n\t/*oss will automagically restart it when we next read*/\n}\n\nstatic void OSS_Capture_Stop(void *ctx)\n{\n\tintptr_t fd = ((intptr_t)ctx)-1;\n\n\tioctl(fd, SNDCTL_DSP_RESET, NULL);\n}\n\nstatic void OSS_Capture_Shutdown(void *ctx)\n{\n\tintptr_t fd = ((intptr_t)ctx)-1;\n\n\tclose(fd);\n}\n\nstatic unsigned int OSS_Capture_Update(void *ctx, unsigned char *buffer, unsigned int minbytes, unsigned int maxbytes)\n{\n\tintptr_t fd = ((intptr_t)ctx)-1;\n\tssize_t res;\n\n\tres = read(fd, buffer, maxbytes);\n\tif (res < 0)\n\t\treturn 0;\n\treturn res;\n}\n\nsnd_capture_driver_t OSS_Capture =\n{\n\t1,\n\t\"OSS\",\n\tOSS_Capture_Enumerate,\n\tOSS_Capture_Init,\n\tOSS_Capture_Start,\n\tOSS_Capture_Update,\n\tOSS_Capture_Stop,\n\tOSS_Capture_Shutdown\n};\n#endif\n"
  },
  {
    "path": "engine/client/snd_macos.c",
    "content": "/*\r\n \r\n Copyright (C) 2001-2002       A Nourai\r\n Copyright (C) 2006            Jacek Piszczek (Mac OSX port)\r\n \r\n This program is free software; you can redistribute it and/or\r\n modify it under the terms of the GNU General Public License\r\n as published by the Free Software Foundation; either version 2\r\n of the License, or (at your option) any later version.\r\n \r\n This program is distributed in the hope that it will be useful,\r\n but WITHOUT ANY WARRANTY; without even the implied warranty of\r\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  \r\n \r\n See the included (GNU.txt) GNU General Public License for more details.\r\n \r\n You should have received a copy of the GNU General Public License\r\n along with this program; if not, write to the Free Software\r\n Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\r\n*/\r\n\r\n#include \"quakedef.h\"\r\n#include \"sound.h\"\r\n#include <CoreServices/CoreServices.h>\r\n#include <AudioUnit/AudioUnit.h>\r\n\r\n// Jacek:\r\n// coreaudio is poorly documented so I'm not 100% sure the code below\r\n// is correct :(\r\n\r\nstruct MacOSSound_Private\r\n{\r\n\tAudioUnit\tgOutputUnit;\r\n\tunsigned int readpos;\r\n};\r\n\r\nstatic OSStatus AudioRender(void\t*inRefCon, \r\n\tAudioUnitRenderActionFlags\t*ioActionFlags, \r\n\tconst AudioTimeStamp\t\t*inTimeStamp, \r\n\tUInt32\t\t\t\tinBusNumber, \r\n\tUInt32\t\t\t\tinNumberFrames, \r\n\tAudioBufferList\t\t\t*ioData)\r\n{\r\n\tsoundcardinfo_t *sc = inRefCon;\r\n\tstruct MacOSSound_Private *pdata = sc->handle;\r\n\r\n\tint start = pdata->readpos;\r\n\tint buffersize = sc->sn.samples * sc->sn.samplebytes;\r\n\tint bytes = ioData->mBuffers[0].mDataByteSize;\r\n\tint remaining;\r\n\r\n\tstart %= buffersize;\r\n\tif (start + bytes > buffersize)\r\n\t{\r\n\t\tremaining = bytes;\r\n\t\tbytes = buffersize - start;\r\n\t\tremaining -= bytes;\r\n\t}\r\n\telse\r\n\t{\r\n\t\tremaining = 0;\r\n\t}\r\n\r\n\tmemcpy(ioData->mBuffers[0].mData, sc->sn.buffer + start, bytes);\r\n\tmemcpy((char*)ioData->mBuffers[0].mData+bytes, sc->sn.buffer, remaining);\r\n\t\r\n\tpdata->readpos += inNumberFrames*sc->sn.numchannels * sc->sn.samplebytes;\r\n\r\n\treturn noErr;\r\n}\r\n\r\nstatic void MacOS_Shutdown(soundcardinfo_t *sc)\r\n{\r\n\tstruct MacOSSound_Private *pdata = sc->handle;\r\n\tsc->handle = NULL;\r\n\tif (!pdata)\r\n\t\treturn;\r\n\r\n\t// stop playback\r\n\tAudioOutputUnitStop (pdata->gOutputUnit);\r\n\r\n\t// release the unit\r\n\tAudioUnitUninitialize (pdata->gOutputUnit);\r\n\r\n\t// free the unit\r\n\tCloseComponent (pdata->gOutputUnit);\r\n\r\n\t// free the buffer memory\r\n\tZ_Free(sc->sn.buffer);\r\n\tZ_Free(pdata);\r\n}\r\n\r\nstatic unsigned int MacOS_GetDMAPos(soundcardinfo_t *sc)\r\n{\r\n\tstruct MacOSSound_Private *pdata = sc->handle;\r\n\tsc->sn.samplepos = pdata->readpos/sc->sn.samplebytes;\r\n\treturn sc->sn.samplepos;\r\n}\r\n\r\nstatic void MacOS_Submit(soundcardinfo_t *sc)\r\n{\r\n}\r\n\r\nstatic void *MacOS_Lock(soundcardinfo_t *sc, unsigned int *sampidx)\r\n{\r\n\treturn sc->sn.buffer;\r\n}\r\n\r\nstatic void MacOS_Unlock(soundcardinfo_t *sc, void *buffer)\r\n{\r\n}\r\n\r\nstatic qboolean MacOS_InitCard(soundcardinfo_t *sc, const char *cardname)\r\n{\r\n\tComponentResult err = noErr;\r\n\r\n\tif (cardname && *cardname)\r\n\t\treturn false;\t//only the default device will be used for now.\r\n\r\n\tstruct MacOSSound_Private *pdata = Z_Malloc(sizeof(*pdata));\r\n\tif (!pdata)\r\n\t\treturn FALSE;\r\n\r\n\t// Open the default output unit\r\n\tComponentDescription desc;\r\n\tdesc.componentType = kAudioUnitType_Output;\r\n\tdesc.componentSubType = kAudioUnitSubType_DefaultOutput;\r\n\tdesc.componentManufacturer = kAudioUnitManufacturer_Apple;\r\n\tdesc.componentFlags = 0;\r\n\tdesc.componentFlagsMask = 0;\r\n\t\r\n\tComponent comp = FindNextComponent(NULL, &desc);\r\n\tif (comp == NULL)\r\n\t{\r\n\t\tCon_Printf(\"FindNextComponent failed\\n\");\r\n\t\tZ_Free(pdata);\r\n\t\treturn FALSE;\r\n\t}\r\n\r\n\terr = OpenAComponent(comp, &pdata->gOutputUnit);\r\n\tif (comp == NULL)\r\n\t{\r\n\t\tCon_Printf(\"OpenAComponent failed\\n\");\r\n\t\tZ_Free(pdata);\r\n\t\treturn FALSE;\r\n\t}\r\n\r\n\t// Set up a callback function to generate output to the output unit\r\n\tAURenderCallbackStruct input;\r\n\tinput.inputProc = AudioRender;\r\n\tinput.inputProcRefCon = sc;\r\n\r\n\terr = AudioUnitSetProperty (\tpdata->gOutputUnit, \r\n\t\t\t\t\tkAudioUnitProperty_SetRenderCallback, \r\n\t\t\t\t\tkAudioUnitScope_Input,\r\n\t\t\t\t\t0, \r\n\t\t\t\t\t&input, \r\n\t\t\t\t\tsizeof(input));\r\n\tif (err) \r\n\t{\r\n\t\tCon_Printf(\"AudioUnitSetProperty failed\\n\");\r\n\t\tCloseComponent(pdata->gOutputUnit);\r\n\t\tZ_Free(pdata);\r\n\t\treturn FALSE;\r\n\t}\r\n\r\n\t// describe our audio data\r\n\tAudioStreamBasicDescription streamFormat;\r\n\tstreamFormat.mSampleRate = sc->sn.speed;\r\n\tstreamFormat.mFormatID = kAudioFormatLinearPCM;\r\n\tstreamFormat.mFormatFlags = kAudioFormatFlagsNativeEndian\r\n\t\t\t\t\t| kLinearPCMFormatFlagIsPacked;\r\n\t\t\t\t\t//| kAudioFormatFlagIsNonInterleaved;\r\n\tstreamFormat.mFramesPerPacket = 1;\r\n\tstreamFormat.mChannelsPerFrame = 2;\r\n\tstreamFormat.mBitsPerChannel = 16;\r\n\tif (streamFormat.mBitsPerChannel >= 16)\r\n\t\tstreamFormat.mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger;\r\n\telse\r\n\t\tstreamFormat.mFormatFlags |= 0;\r\n\t\t\r\n\tstreamFormat.mBytesPerFrame = streamFormat.mChannelsPerFrame * (streamFormat.mBitsPerChannel/8);\r\n\tstreamFormat.mBytesPerPacket = streamFormat.mBytesPerFrame * streamFormat.mFramesPerPacket;\r\n\r\n\terr = AudioUnitSetProperty (pdata->gOutputUnit,\r\n\t\t\t\tkAudioUnitProperty_StreamFormat,\r\n\t\t\t\tkAudioUnitScope_Input,\r\n\t\t\t\t0,\r\n\t\t\t\t&streamFormat,\r\n\t\t\t\tsizeof(AudioStreamBasicDescription));\r\n\tif (err) \r\n\t{\r\n\t\tCon_Printf(\"AudioUnitSetProperty failed\\n\");\r\n\t\tCloseComponent(pdata->gOutputUnit);\r\n\t\tZ_Free(pdata);\r\n\t\treturn FALSE;\r\n\t}\r\n\r\n\t// set the shm structure\r\n\tsc->sn.speed = streamFormat.mSampleRate;\r\n\tsc->sn.samplebytes = streamFormat.mBitsPerChannel/8;\r\n\tsc->sn.sampleformat = QCF_S16;\r\n\tsc->sn.numchannels = streamFormat.mChannelsPerFrame;\r\n\tsc->sn.samples = 256 * 1024;\r\n\tsc->sn.buffer = Z_Malloc(sc->sn.samples*sc->sn.samplebytes);\r\n\r\n\tint i;\r\n\tfor (i = 0; i < sc->sn.samples*sc->sn.samplebytes; i++)\r\n\t\tsc->sn.buffer[i] = rand();\r\n\r\n\tif (sc->sn.buffer == 0)\r\n\t{\r\n\t\tCon_Printf(\"Malloc failed - cannot allocate sound buffer\\n\");\r\n\t\tCloseComponent(pdata->gOutputUnit);\r\n\t\tZ_Free(pdata);\r\n\t\treturn FALSE;\r\n\t}\r\n\r\n\t// Initialize unit\r\n\terr = AudioUnitInitialize(pdata->gOutputUnit);\r\n\tif (err) \r\n\t{\r\n\t\tCon_Printf(\"AudioOutputInitialize failed\\n\");\r\n\t\tCloseComponent(pdata->gOutputUnit);\r\n\t\tZ_Free(sc->sn.buffer);\r\n\t\tZ_Free(pdata);\r\n\t\treturn FALSE;\r\n\t}\r\n\r\n\t// start playing :)\r\n\terr = AudioOutputUnitStart (pdata->gOutputUnit);\r\n\tif (err) \r\n\t{\r\n\t\tCon_Printf(\"AudioOutputUnitStart failed\\n\");\r\n\t\tAudioUnitUninitialize (pdata->gOutputUnit);\r\n\t\tCloseComponent(pdata->gOutputUnit);\r\n\t\tZ_Free(sc->sn.buffer);\r\n\t\tZ_Free(pdata);\r\n\t\treturn FALSE;\r\n\t}\r\n\r\n\tsc->handle = pdata;\r\n\tsc->Lock = MacOS_Lock;\r\n\tsc->Unlock = MacOS_Unlock;\r\n\tsc->Submit = MacOS_Submit;\r\n\tsc->GetDMAPos = MacOS_GetDMAPos;\r\n\tsc->Shutdown = MacOS_Shutdown;\r\n\r\n\tCon_Printf(\"Sound initialised\\n\");\r\n\treturn TRUE;\r\n}\r\n\r\nsounddriver_t MacOS_AudioOutput =\r\n{\r\n\t\"CoreAudio\",\r\n\tMacOS_InitCard,\r\n\tNULL\r\n};\r\n"
  },
  {
    "path": "engine/client/snd_mem.c",
    "content": "/*\nCopyright (C) 1996-1997 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n*/\n// snd_mem.c: sound caching\n\n#include \"quakedef.h\"\n\n#include \"winquake.h\"\n#include \"fs.h\"\n\ntypedef struct\n{\n\tint\t\tformat;\n\tint\t\trate;\n\tint\t\tbitwidth;\n\tint\t\tnumchannels;\n\tint\t\tloopstart;\n\tint\t\tsamples;\n\tint\t\tdataofs;\t\t// chunk starts this many bytes from file start\n} wavinfo_t;\n\nstatic wavinfo_t GetWavinfo (char *name, qbyte *wav, int wavlength);\n\n#define LINEARUPSCALE(in, inrate, insamps, out, outrate, outlshift, outrshift) \\\n\t{ \\\n\t\tscale = inrate / (double)outrate; \\\n\t\tinfrac = floor(scale * 65536); \\\n\t\toutsamps = insamps / scale; \\\n\t\tinaccum = 0; \\\n\t\toutnlsamps = floor(1.0 / scale); \\\n\t\toutsamps -= outnlsamps; \\\n\t\\\n\t\twhile (outsamps) \\\n\t\t{ \\\n\t\t\t*out = ((0xFFFF - inaccum)*in[0] + inaccum*in[1]) >> (16 - outlshift + outrshift); \\\n\t\t\tinaccum += infrac; \\\n\t\t\tin += (inaccum >> 16); \\\n\t\t\tinaccum &= 0xFFFF; \\\n\t\t\tout++; \\\n\t\t\toutsamps--; \\\n\t\t} \\\n\t\twhile (outnlsamps) \\\n\t\t{ \\\n\t\t\t*out = (*in >> outrshift) << outlshift; \\\n\t\t\tout++; \\\n\t\t\toutnlsamps--; \\\n\t\t} \\\n\t}\n\n#define LINEARUPSCALESTEREO(in, inrate, insamps, out, outrate, outlshift, outrshift) \\\n\t{ \\\n\t\tscale = inrate / (double)outrate; \\\n\t\tinfrac = floor(scale * 65536); \\\n\t\toutsamps = insamps / scale; \\\n\t\tinaccum = 0; \\\n\t\toutnlsamps = floor(1.0 / scale); \\\n\t\toutsamps -= outnlsamps; \\\n\t\\\n\t\twhile (outsamps) \\\n\t\t{ \\\n\t\t\tout[0] = ((0xFFFF - inaccum)*in[0] + inaccum*in[2]) >> (16 - outlshift + outrshift); \\\n\t\t\tout[1] = ((0xFFFF - inaccum)*in[1] + inaccum*in[3]) >> (16 - outlshift + outrshift); \\\n\t\t\tinaccum += infrac; \\\n\t\t\tin += (inaccum >> 16) * 2; \\\n\t\t\tinaccum &= 0xFFFF; \\\n\t\t\tout += 2; \\\n\t\t\toutsamps--; \\\n\t\t} \\\n\t\twhile (outnlsamps) \\\n\t\t{ \\\n\t\t\tout[0] = (in[0] >> outrshift) << outlshift; \\\n\t\t\tout[1] = (in[1] >> outrshift) << outlshift; \\\n\t\t\tout += 2; \\\n\t\t\toutnlsamps--; \\\n\t\t} \\\n\t}\n\n#define LINEARUPSCALESTEREOTOMONO(in, inrate, insamps, out, outrate, outlshift, outrshift) \\\n\t{ \\\n\t\tscale = inrate / (double)outrate; \\\n\t\tinfrac = floor(scale * 65536); \\\n\t\toutsamps = insamps / scale; \\\n\t\tinaccum = 0; \\\n\t\toutnlsamps = floor(1.0 / scale); \\\n\t\toutsamps -= outnlsamps; \\\n\t\\\n\t\twhile (outsamps) \\\n\t\t{ \\\n\t\t\t*out = ((((0xFFFF - inaccum)*in[0] + inaccum*in[2]) >> (16 - outlshift + outrshift)) + \\\n\t\t\t\t(((0xFFFF - inaccum)*in[1] + inaccum*in[3]) >> (16 - outlshift + outrshift))) >> 1; \\\n\t\t\tinaccum += infrac; \\\n\t\t\tin += (inaccum >> 16) * 2; \\\n\t\t\tinaccum &= 0xFFFF; \\\n\t\t\tout++; \\\n\t\t\toutsamps--; \\\n\t\t} \\\n\t\twhile (outnlsamps) \\\n\t\t{ \\\n\t\t\tout[0] = (((in[0] >> outrshift) << outlshift) + ((in[1] >> outrshift) << outlshift)) >> 1; \\\n\t\t\tout++; \\\n\t\t\toutnlsamps--; \\\n\t\t} \\\n\t}\n\n#define LINEARDOWNSCALE(in, inrate, insamps, out, outrate, outlshift, outrshift) \\\n\t{ \\\n\t\tscale = outrate / (double)inrate; \\\n\t\tinfrac = floor(scale * 65536); \\\n\t\tinaccum = 0; \\\n\t\tinsamps--; \\\n\t\toutsampleft = 0; \\\n\t\\\n\t\twhile (insamps) \\\n\t\t{ \\\n\t\t\tinaccum += infrac; \\\n\t\t\tif (inaccum >> 16) \\\n\t\t\t{ \\\n\t\t\t\tinaccum &= 0xFFFF; \\\n\t\t\t\toutsampleft += (infrac - inaccum) * (*in); \\\n\t\t\t\t*out = outsampleft >> (16 - outlshift + outrshift); \\\n\t\t\t\tout++; \\\n\t\t\t\toutsampleft = inaccum * (*in); \\\n\t\t\t} \\\n\t\t\telse \\\n\t\t\t\toutsampleft += infrac * (*in); \\\n\t\t\tin++; \\\n\t\t\tinsamps--; \\\n\t\t} \\\n\t\toutsampleft += (0xFFFF - inaccum) * (*in);\\\n\t\t*out = outsampleft >> (16 - outlshift + outrshift); \\\n\t}\n\n#define LINEARDOWNSCALESTEREO(in, inrate, insamps, out, outrate, outlshift, outrshift) \\\n\t{ \\\n\t\tscale = outrate / (double)inrate; \\\n\t\tinfrac = floor(scale * 65536); \\\n\t\tinaccum = 0; \\\n\t\tinsamps--; \\\n\t\toutsampleft = 0; \\\n\t\toutsampright = 0; \\\n\t\\\n\t\twhile (insamps) \\\n\t\t{ \\\n\t\t\tinaccum += infrac; \\\n\t\t\tif (inaccum >> 16) \\\n\t\t\t{ \\\n\t\t\t\tinaccum &= 0xFFFF; \\\n\t\t\t\toutsampleft += (infrac - inaccum) * in[0]; \\\n\t\t\t\toutsampright += (infrac - inaccum) * in[1]; \\\n\t\t\t\tout[0] = outsampleft >> (16 - outlshift + outrshift); \\\n\t\t\t\tout[1] = outsampright >> (16 - outlshift + outrshift); \\\n\t\t\t\tout += 2; \\\n\t\t\t\toutsampleft = inaccum * in[0]; \\\n\t\t\t\toutsampright = inaccum * in[1]; \\\n\t\t\t} \\\n\t\t\telse \\\n\t\t\t{ \\\n\t\t\t\toutsampleft += infrac * in[0]; \\\n\t\t\t\toutsampright += infrac * in[1]; \\\n\t\t\t} \\\n\t\t\tin += 2; \\\n\t\t\tinsamps--; \\\n\t\t} \\\n\t\toutsampleft += (0xFFFF - inaccum) * in[0];\\\n\t\toutsampright += (0xFFFF - inaccum) * in[1];\\\n\t\tout[0] = outsampleft >> (16 - outlshift + outrshift); \\\n\t\tout[1] = outsampright >> (16 - outlshift + outrshift); \\\n\t}\n\n#define LINEARDOWNSCALESTEREOTOMONO(in, inrate, insamps, out, outrate, outlshift, outrshift) \\\n\t{ \\\n\t\tscale = outrate / (double)inrate; \\\n\t\tinfrac = floor(scale * 65536); \\\n\t\tinaccum = 0; \\\n\t\tinsamps--; \\\n\t\toutsampleft = 0; \\\n\t\\\n\t\twhile (insamps) \\\n\t\t{ \\\n\t\t\tinaccum += infrac; \\\n\t\t\tif (inaccum >> 16) \\\n\t\t\t{ \\\n\t\t\t\tinaccum &= 0xFFFF; \\\n\t\t\t\toutsampleft += (infrac - inaccum) * ((in[0] + in[1]) >> 1); \\\n\t\t\t\t*out = outsampleft >> (16 - outlshift + outrshift); \\\n\t\t\t\tout++; \\\n\t\t\t\toutsampleft = inaccum * ((in[0] + in[1]) >> 1); \\\n\t\t\t} \\\n\t\t\telse \\\n\t\t\t\toutsampleft += infrac * ((in[0] + in[1]) >> 1); \\\n\t\t\tin += 2; \\\n\t\t\tinsamps--; \\\n\t\t} \\\n\t\toutsampleft += (0xFFFF - inaccum) * ((in[0] + in[1]) >> 1);\\\n\t\t*out = outsampleft >> (16 - outlshift + outrshift); \\\n\t}\n\n#define STANDARDRESCALE(in, inrate, insamps, out, outrate, outlshift, outrshift) \\\n\t{ \\\n\t\tscale = inrate / (double)outrate; \\\n\t\tinfrac = floor(scale * 65536); \\\n\t\toutsamps = insamps / scale; \\\n\t\tinaccum = 0; \\\n\t\\\n\t\twhile (outsamps) \\\n\t\t{ \\\n\t\t\t*out = (*in >> outrshift) << outlshift; \\\n\t\t\tinaccum += infrac; \\\n\t\t\tin += (inaccum >> 16); \\\n\t\t\tinaccum &= 0xFFFF; \\\n\t\t\tout++; \\\n\t\t\toutsamps--; \\\n\t\t} \\\n\t}\n\n#define STANDARDRESCALESTEREO(in, inrate, insamps, out, outrate, outlshift, outrshift) \\\n\t{ \\\n\t\tscale = inrate / (double)outrate; \\\n\t\tinfrac = floor(scale * 65536); \\\n\t\toutsamps = insamps / scale; \\\n\t\tinaccum = 0; \\\n\t\\\n\t\twhile (outsamps) \\\n\t\t{ \\\n\t\t\tout[0] = (in[0] >> outrshift) << outlshift; \\\n\t\t\tout[1] = (in[1] >> outrshift) << outlshift; \\\n\t\t\tinaccum += infrac; \\\n\t\t\tin += (inaccum >> 16) * 2; \\\n\t\t\tinaccum &= 0xFFFF; \\\n\t\t\tout += 2; \\\n\t\t\toutsamps--; \\\n\t\t} \\\n\t}\n\n#define STANDARDRESCALESTEREOTOMONO(in, inrate, insamps, out, outrate, outlshift, outrshift) \\\n\t{ \\\n\t\tscale = inrate / (double)outrate; \\\n\t\tinfrac = floor(scale * 65536); \\\n\t\toutsamps = insamps / scale; \\\n\t\tinaccum = 0; \\\n\t\\\n\t\twhile (outsamps) \\\n\t\t{ \\\n\t\t\tout[0] = (((in[0] >> outrshift) << outlshift) + ((in[1] >> outrshift) << outlshift)) >> 1; \\\n\t\t\tinaccum += infrac; \\\n\t\t\tin += (inaccum >> 16) * 2; \\\n\t\t\tinaccum &= 0xFFFF; \\\n\t\t\tout++; \\\n\t\t\toutsamps--; \\\n\t\t} \\\n\t}\n\n#define QUICKCONVERT(in, insamps, out, outlshift, outrshift) \\\n\t{ \\\n\t\twhile (insamps) \\\n\t\t{ \\\n\t\t\t*out = (*in >> outrshift) << outlshift; \\\n\t\t\tout++; \\\n\t\t\tin++; \\\n\t\t\tinsamps--; \\\n\t\t} \\\n\t}\n\n#define QUICKCONVERTSTEREOTOMONO(in, insamps, out, outlshift, outrshift) \\\n\t{ \\\n\t\twhile (insamps) \\\n\t\t{ \\\n\t\t\t*out = (((in[0] >> outrshift) << outlshift) + ((in[1] >> outrshift) << outlshift)) >> 1; \\\n\t\t\tout++; \\\n\t\t\tin += 2; \\\n\t\t\tinsamps--; \\\n\t\t} \\\n\t}\n\n// SND_ResampleStream: takes a sound stream and converts with given parameters. Limited to\n// 8-16-bit signed conversions and mono-to-mono/stereo-to-stereo conversions.\n// Not an in-place algorithm.\nvoid SND_ResampleStream (const void *in, int inrate, qaudiofmt_t informat, int inchannels, int insamps, void *out, int outrate, qaudiofmt_t outformat, int outchannels, int resampstyle)\n{\n\tdouble scale;\n\tconst signed char *in8 = (const signed char *)in;\n\tconst short *in16 = (const short *)in;\n\tsigned char *out8 = (signed char *)out;\n\tshort *out16 = (short *)out;\n\tint outsamps, outnlsamps, outsampleft, outsampright;\n\tint infrac, inaccum;\n\n\tif (insamps <= 0)\n\t\treturn;\n\n\tif (inchannels == outchannels && informat == outformat && inrate == outrate)\n\t{\n\t\tmemcpy(out, in, informat*insamps*inchannels);\n\t\treturn;\n\t}\n\n\tif (inchannels == 1 && outchannels == 1)\n\t{\n\t\tif (informat == QAF_S8)\n\t\t{\n\t\t\tif (outformat == QAF_S8)\n\t\t\t{\n\t\t\t\tif (inrate < outrate) // upsample\n\t\t\t\t{\n\t\t\t\t\tif (resampstyle)\n\t\t\t\t\t\tLINEARUPSCALE(in8, inrate, insamps, out8, outrate, 0, 0)\n\t\t\t\t\telse\n\t\t\t\t\t\tSTANDARDRESCALE(in8, inrate, insamps, out8, outrate, 0, 0)\n\t\t\t\t}\n\t\t\t\telse // downsample\n\t\t\t\t{\n\t\t\t\t\tif (resampstyle > 1)\n\t\t\t\t\t\tLINEARDOWNSCALE(in8, inrate, insamps, out8, outrate, 0, 0)\n\t\t\t\t\telse\n\t\t\t\t\t\tSTANDARDRESCALE(in8, inrate, insamps, out8, outrate, 0, 0)\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\t\t\telse if (outformat == QAF_S16)\n\t\t\t{\n\t\t\t\tif (inrate == outrate) // quick convert\n\t\t\t\t\tQUICKCONVERT(in8, insamps, out16, 8, 0)\n\t\t\t\telse if (inrate < outrate) // upsample\n\t\t\t\t{\n\t\t\t\t\tif (resampstyle)\n\t\t\t\t\t\tLINEARUPSCALE(in8, inrate, insamps, out16, outrate, 8, 0)\n\t\t\t\t\telse\n\t\t\t\t\t\tSTANDARDRESCALE(in8, inrate, insamps, out16, outrate, 8, 0)\n\t\t\t\t}\n\t\t\t\telse // downsample\n\t\t\t\t{\n\t\t\t\t\tif (resampstyle > 1)\n\t\t\t\t\t\tLINEARDOWNSCALE(in8, inrate, insamps, out16, outrate, 8, 0)\n\t\t\t\t\telse\n\t\t\t\t\t\tSTANDARDRESCALE(in8, inrate, insamps, out16, outrate, 8, 0)\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\telse if (informat == QAF_S16) // 16-bit\n\t\t{\n\t\t\tif (outformat == QAF_S16)\n\t\t\t{\n\t\t\t\tif (inrate < outrate) // upsample\n\t\t\t\t{\n\t\t\t\t\tif (resampstyle)\n\t\t\t\t\t\tLINEARUPSCALE(in16, inrate, insamps, out16, outrate, 0, 0)\n\t\t\t\t\telse\n\t\t\t\t\t\tSTANDARDRESCALE(in16, inrate, insamps, out16, outrate, 0, 0)\n\t\t\t\t}\n\t\t\t\telse // downsample\n\t\t\t\t{\n\t\t\t\t\tif (resampstyle > 1)\n\t\t\t\t\t\tLINEARDOWNSCALE(in16, inrate, insamps, out16, outrate, 0, 0)\n\t\t\t\t\telse\n\t\t\t\t\t\tSTANDARDRESCALE(in16, inrate, insamps, out16, outrate, 0, 0)\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\t\t\telse if (outformat == QAF_S8)\n\t\t\t{\n\t\t\t\tif (inrate == outrate) // quick convert\n\t\t\t\t\tQUICKCONVERT(in16, insamps, out8, 0, 8)\n\t\t\t\telse if (inrate < outrate) // upsample\n\t\t\t\t{\n\t\t\t\t\tif (resampstyle)\n\t\t\t\t\t\tLINEARUPSCALE(in16, inrate, insamps, out8, outrate, 0, 8)\n\t\t\t\t\telse\n\t\t\t\t\t\tSTANDARDRESCALE(in16, inrate, insamps, out8, outrate, 0, 8)\n\t\t\t\t}\n\t\t\t\telse // downsample\n\t\t\t\t{\n\t\t\t\t\tif (resampstyle > 1)\n\t\t\t\t\t\tLINEARDOWNSCALE(in16, inrate, insamps, out8, outrate, 0, 8)\n\t\t\t\t\telse\n\t\t\t\t\t\tSTANDARDRESCALE(in16, inrate, insamps, out8, outrate, 0, 8)\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n\telse if (outchannels == 2 && inchannels == 2)\n\t{\n\t\tif (informat == QAF_S8)\n\t\t{\n\t\t\tif (outformat == QAF_S8)\n\t\t\t{\n\t\t\t\tif (inrate < outrate) // upsample\n\t\t\t\t{\n\t\t\t\t\tif (resampstyle)\n\t\t\t\t\t\tLINEARUPSCALESTEREO(in8, inrate, insamps, out8, outrate, 0, 0)\n\t\t\t\t\telse\n\t\t\t\t\t\tSTANDARDRESCALESTEREO(in8, inrate, insamps, out8, outrate, 0, 0)\n\t\t\t\t}\n\t\t\t\telse // downsample\n\t\t\t\t{\n\t\t\t\t\tif (resampstyle > 1)\n\t\t\t\t\t\tLINEARDOWNSCALESTEREO(in8, inrate, insamps, out8, outrate, 0, 0)\n\t\t\t\t\telse\n\t\t\t\t\t\tSTANDARDRESCALESTEREO(in8, inrate, insamps, out8, outrate, 0, 0)\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (inrate == outrate) // quick convert\n\t\t\t\t{\n\t\t\t\t\tinsamps *= 2;\n\t\t\t\t\tQUICKCONVERT(in8, insamps, out16, 8, 0)\n\t\t\t\t}\n\t\t\t\telse if (inrate < outrate) // upsample\n\t\t\t\t{\n\t\t\t\t\tif (resampstyle)\n\t\t\t\t\t\tLINEARUPSCALESTEREO(in8, inrate, insamps, out16, outrate, 8, 0)\n\t\t\t\t\telse\n\t\t\t\t\t\tSTANDARDRESCALESTEREO(in8, inrate, insamps, out16, outrate, 8, 0)\n\t\t\t\t}\n\t\t\t\telse // downsample\n\t\t\t\t{\n\t\t\t\t\tif (resampstyle > 1)\n\t\t\t\t\t\tLINEARDOWNSCALESTEREO(in8, inrate, insamps, out16, outrate, 8, 0)\n\t\t\t\t\telse\n\t\t\t\t\t\tSTANDARDRESCALESTEREO(in8, inrate, insamps, out16, outrate, 8, 0)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse if (informat == QAF_S16) // 16-bit\n\t\t{\n\t\t\tif (outformat == QAF_S16)\n\t\t\t{\n\t\t\t\tif (inrate < outrate) // upsample\n\t\t\t\t{\n\t\t\t\t\tif (resampstyle)\n\t\t\t\t\t\tLINEARUPSCALESTEREO(in16, inrate, insamps, out16, outrate, 0, 0)\n\t\t\t\t\telse\n\t\t\t\t\t\tSTANDARDRESCALESTEREO(in16, inrate, insamps, out16, outrate, 0, 0)\n\t\t\t\t}\n\t\t\t\telse // downsample\n\t\t\t\t{\n\t\t\t\t\tif (resampstyle > 1)\n\t\t\t\t\t\tLINEARDOWNSCALESTEREO(in16, inrate, insamps, out16, outrate, 0, 0)\n\t\t\t\t\telse\n\t\t\t\t\t\tSTANDARDRESCALESTEREO(in16, inrate, insamps, out16, outrate, 0, 0)\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (outformat == QAF_S8)\n\t\t\t{\n\t\t\t\tif (inrate == outrate) // quick convert\n\t\t\t\t{\n\t\t\t\t\tinsamps *= 2;\n\t\t\t\t\tQUICKCONVERT(in16, insamps, out8, 0, 8)\n\t\t\t\t}\n\t\t\t\telse if (inrate < outrate) // upsample\n\t\t\t\t{\n\t\t\t\t\tif (resampstyle)\n\t\t\t\t\t\tLINEARUPSCALESTEREO(in16, inrate, insamps, out8, outrate, 0, 8)\n\t\t\t\t\telse\n\t\t\t\t\t\tSTANDARDRESCALESTEREO(in16, inrate, insamps, out8, outrate, 0, 8)\n\t\t\t\t}\n\t\t\t\telse // downsample\n\t\t\t\t{\n\t\t\t\t\tif (resampstyle > 1)\n\t\t\t\t\t\tLINEARDOWNSCALESTEREO(in16, inrate, insamps, out8, outrate, 0, 8)\n\t\t\t\t\telse\n\t\t\t\t\t\tSTANDARDRESCALESTEREO(in16, inrate, insamps, out8, outrate, 0, 8)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n#if 0\n\telse if (outchannels == 1 && inchannels == 2)\n\t{\n\t\tif (informat == QAF_S8)\n\t\t{\n\t\t\tif (outformat == QAF_S8)\n\t\t\t{\n\t\t\t\tif (inrate < outrate) // upsample\n\t\t\t\t{\n\t\t\t\t\tif (resampstyle)\n\t\t\t\t\t\tLINEARUPSCALESTEREOTOMONO(in8, inrate, insamps, out8, outrate, 0, 0)\n\t\t\t\t\telse\n\t\t\t\t\t\tSTANDARDRESCALESTEREOTOMONO(in8, inrate, insamps, out8, outrate, 0, 0)\n\t\t\t\t}\n\t\t\t\telse // downsample\n\t\t\t\t\tSTANDARDRESCALESTEREOTOMONO(in8, inrate, insamps, out8, outrate, 0, 0)\n\t\t\t}\n\t\t\telse if (outformat == QAF_S16)\n\t\t\t{\n\t\t\t\tif (inrate == outrate) // quick convert\n\t\t\t\t\tQUICKCONVERTSTEREOTOMONO(in8, insamps, out16, 8, 0)\n\t\t\t\telse if (inrate < outrate) // upsample\n\t\t\t\t{\n\t\t\t\t\tif (resampstyle)\n\t\t\t\t\t\tLINEARUPSCALESTEREOTOMONO(in8, inrate, insamps, out16, outrate, 8, 0)\n\t\t\t\t\telse\n\t\t\t\t\t\tSTANDARDRESCALESTEREOTOMONO(in8, inrate, insamps, out16, outrate, 8, 0)\n\t\t\t\t}\n\t\t\t\telse // downsample\n\t\t\t\t\tSTANDARDRESCALESTEREOTOMONO(in8, inrate, insamps, out16, outrate, 8, 0)\n\t\t\t}\n\t\t}\n\t\telse if (informat == QAF_S16) // 16-bit\n\t\t{\n\t\t\tif (outformat == QAF_S16)\n\t\t\t{\n\t\t\t\tif (inrate < outrate) // upsample\n\t\t\t\t{\n\t\t\t\t\tif (resampstyle)\n\t\t\t\t\t\tLINEARUPSCALESTEREOTOMONO(in16, inrate, insamps, out16, outrate, 0, 0)\n\t\t\t\t\telse\n\t\t\t\t\t\tSTANDARDRESCALESTEREOTOMONO(in16, inrate, insamps, out16, outrate, 0, 0)\n\t\t\t\t}\n\t\t\t\telse // downsample\n\t\t\t\t\tSTANDARDRESCALESTEREOTOMONO(in16, inrate, insamps, out16, outrate, 0, 0)\n\t\t\t}\n\t\t\telse if (outformat == QAF_S8)\n\t\t\t{\n\t\t\t\tif (inrate == outrate) // quick convert\n\t\t\t\t\tQUICKCONVERTSTEREOTOMONO(in16, insamps, out8, 0, 8)\n\t\t\t\telse if (inrate < outrate) // upsample\n\t\t\t\t{\n\t\t\t\t\tif (resampstyle)\n\t\t\t\t\t\tLINEARUPSCALESTEREOTOMONO(in16, inrate, insamps, out8, outrate, 0, 8)\n\t\t\t\t\telse\n\t\t\t\t\t\tSTANDARDRESCALESTEREOTOMONO(in16, inrate, insamps, out8, outrate, 0, 8)\n\t\t\t\t}\n\t\t\t\telse // downsample\n\t\t\t\t\tSTANDARDRESCALESTEREOTOMONO(in16, inrate, insamps, out8, outrate, 0, 8)\n\t\t\t}\n\t\t}\n\t}\n#endif\n}\n\n/*\n================\nResampleSfx\n================\n*/\nstatic qboolean ResampleSfx (sfx_t *sfx, int inrate, int inchannels, qaudiofmt_t informat, int insamps, int inloopstart, qbyte *data)\n{\n\textern cvar_t snd_linearresample;\n\textern cvar_t snd_loadasstereo;\n\tdouble scale;\n\tsfxcache_t\t*sc;\n\tint outsamps;\n\tint len;\n\tqaudiofmt_t outformat;\n\n\tscale = snd_speed / (double)inrate;\n\toutsamps = insamps * scale;\n\tif (snd_loadas8bit.ival < 0)\n\t\toutformat = QAF_S16;\n\telse if (snd_loadas8bit.ival)\n\t\toutformat = QAF_S8;\n\telse\n\t\toutformat = informat;\n\tlen = outsamps * QAF_BYTES(outformat) * inchannels;\n\n\tsfx->decoder.buf = sc = BZ_Malloc(sizeof(sfxcache_t) + len);\n\tif (!sc)\n\t{\n\t\treturn false;\n\t}\n\n\tsc->numchannels = inchannels;\n\tsc->format = outformat;\n\tsc->speed = snd_speed;\n\tsc->length = outsamps;\n\tsc->soundoffset = 0;\n\tsc->data = (qbyte*)(sc+1);\n\tif (inloopstart == -1)\n\t\tsfx->loopstart = inloopstart;\n\telse\n\t\tsfx->loopstart = inloopstart * scale;\n\n\tSND_ResampleStream (data,\n\t\tinrate,\n\t\tinformat,\n\t\tinchannels,\n\t\tinsamps,\n\t\tsc->data,\n\t\tsc->speed,\n\t\tsc->format,\n\t\tsc->numchannels,\n\t\tsnd_linearresample.ival);\n\n\tif (inchannels == 1 && snd_loadasstereo.ival)\n\t{\t//I'm implementing this to work around what looks like a firefox bug, where mono buffers don't get played (but stereo works just fine despite all the spacialisation issues associated with that).\n\t\tsfxcache_t *nc = sfx->decoder.buf = BZ_Malloc(sizeof(sfxcache_t) + len*2);\n\t\t*nc = *sc;\n\t\tnc->data = (qbyte*)(nc+1);\n\t\tSND_ResampleStream (sc->data,\n\t\t\tsc->speed,\n\t\t\tsc->format,\n\t\t\tsc->numchannels,\n\t\t\toutsamps,\n\t\t\tnc->data,\n\t\t\tnc->speed*2,\n\t\t\tnc->format,\n\t\t\tnc->numchannels,\n\t\t\tfalse);\n\t\tnc->numchannels *= 2;\n\t\tBZ_Free(sc);\n\t}\n\n\treturn true;\n}\n\n//=============================================================================\n#ifdef PACKAGE_DOOMWAD\n#define DSPK_RATE 140\n#define DSPK_BASE 170.0\n#define DSPK_EXP 0.0433\n\n/*\nqboolean QDECL S_LoadDoomSpeakerSound (sfx_t *s, qbyte *data, size_t datalen, int sndspeed, qboolean forcedecode)\n{\n\tsfxcache_t\t*sc;\n\n\t// format data from Unofficial Doom Specs v1.6\n\tunsigned short *dataus;\n\tint samples, len, inrate, inaccum;\n\tqbyte *outdata;\n\tqbyte towrite;\n\tdouble timeraccum, timerfreq;\n\n\tif (datalen < 4)\n\t\treturn NULL;\n\n\tdataus = (unsigned short*)data;\n\n\tif (LittleShort(dataus[0]) != 0)\n\t\treturn NULL;\n\n\tsamples = LittleShort(dataus[1]);\n\n\tdata += 4;\n\tdatalen -= 4;\n\n\tif (datalen != samples)\n\t\treturn NULL;\n\n\tlen = (int)((double)samples * (double)snd_speed / DSPK_RATE);\n\n\tsc = Cache_Alloc (&s->cache, len + sizeof(sfxcache_t), s->name);\n\tif (!sc)\n\t{\n\t\treturn NULL;\n\t}\n\n\tsc->length = len;\n\ts->loopstart = -1;\n\tsc->numchannels = 1;\n\tsc->width = 1;\n\tsc->speed = snd_speed;\n\n\ttimeraccum = 0;\n\toutdata = sc->data;\n\ttowrite = 0x40;\n\tinrate = (int)((double)snd_speed / DSPK_RATE);\n\tinaccum = inrate;\n\tif (*data)\n\t\ttimerfreq = DSPK_BASE * pow((double)2.0, DSPK_EXP * (*data));\n\telse\n\t\ttimerfreq = 0;\n\n\twhile (len > 0)\n\t{\n\t\ttimeraccum += timerfreq;\n\t\tif (timeraccum > (float)snd_speed)\n\t\t{\n\t\t\ttowrite ^= 0xFF; // swap speaker component\n\t\t\ttimeraccum -= (float)snd_speed;\n\t\t}\n\n\t\tinaccum--;\n\t\tif (!inaccum)\n\t\t{\n\t\t\tdata++;\n\t\t\tif (*data)\n\t\t\t\ttimerfreq = DSPK_BASE * pow((double)2.0, DSPK_EXP * (*data));\n\t\t\tinaccum = inrate;\n\t\t}\n\t\t*outdata = towrite;\n\t\toutdata++;\n\t\tlen--;\n\t}\n\n\treturn sc;\n}\n*/\nstatic qboolean QDECL S_LoadDoomSound (sfx_t *s, qbyte *data, size_t datalen, int sndspeed, qboolean forcedecode)\n{\n\t// format data from Unofficial Doom Specs v1.6\n\tunsigned short *dataus;\n\tint samples, rate;\n\n\tif (datalen < 8)\n\t\treturn false;\n\n\tdataus = (unsigned short*)data;\n\n\tif (LittleShort(dataus[0]) != 3)\n\t\treturn false;\n\n\trate = LittleShort(dataus[1]);\n\tsamples = LittleShort(dataus[2]);\n\n\tdata += 8;\n\tdatalen -= 8;\n\n\tif (datalen != samples)\n\t\treturn false;\n\n\tCOM_CharBias(data, datalen);\n\n\treturn ResampleSfx (s, rate, 1, 1, samples, -1, data);\n}\n#endif\n\nstatic qboolean QDECL S_LoadWavSound (sfx_t *s, qbyte *data, size_t datalen, int sndspeed, qboolean forcedecode)\n{\n\twavinfo_t\tinfo;\n\tqaudiofmt_t\tformat;\n\n\tif (datalen < 4 || strncmp(data, \"RIFF\", 4))\n\t\treturn false;\n\n\tinfo = GetWavinfo (s->name, data, datalen);\n\tif (info.numchannels < 1 || info.numchannels > 2)\n\t{\n\t\ts->loadstate = SLS_FAILED;\n\t\tCon_Printf (\"%s has an unsupported quantity of channels.\\n\",s->name);\n\t\treturn false;\n\t}\n\n\tif (info.format == 1 && info.bitwidth == 8)\t//unsigned bytes\n\t{\n\t\tCOM_CharBias(data + info.dataofs, info.samples*info.numchannels);\n\t\tformat = QAF_S8;\n\t}\n\telse if (info.format == 1 && (info.bitwidth > 8 && info.bitwidth <= 16))\t//signed shorts\n\t{\n\t\tCOM_SwapLittleShortBlock((short *)(data + info.dataofs), info.samples*info.numchannels);\n\t\tformat = QAF_S16;\n\t}\n\telse if (info.format == 1 && (info.bitwidth > 16 && info.bitwidth <= 24))\n\t{\t//packed\n\t\tshort *out = (short *)(data + info.dataofs);\n\t\tqbyte *in = (qbyte *)(data + info.dataofs);\n\t\tint s;\n\t\tsize_t samples = info.samples*info.numchannels;\n\t\twhile(samples --> 0)\n\t\t{\n\t\t\ts  = *in++<<0;\n\t\t\ts |= *in++<<8;\n\t\t\ts |= *in++<<16;\n\t\t\ts |= 0    <<24;\n\t\t\t*out++ = s>>8;\t//just drop the least significant bits.\n\t\t}\n\t\tformat = QAF_S16;\n\t}\n\telse if (info.format == 1 && (info.bitwidth > 24 && info.bitwidth <= 32))\t//24(padded) or 32bit int audio\n\t{\n\t\tshort *out = (short *)(data + info.dataofs);\n\t\tint *in = (int *)(data + info.dataofs);\n\t\tsize_t samples = info.samples*info.numchannels;\n\t\twhile(samples --> 0)\n\t\t{\t//in place size conversion, so we need to do it forwards.\n\t\t\t*out++ = LittleLong(*in++)>>16;\t//just drop the least significant bits.\n\t\t}\n\t\tformat = QAF_S16;\n\t}\n#ifdef MIXER_F32\n\telse if (info.format == 3 && info.bitwidth == 32)\t//signed floats\n\t{\n\t\tif (bigendian)\n\t\t{\n\t\t\tsize_t i = info.samples*info.numchannels;\n\t\t\tfloat *ptr = (float*)(data + info.dataofs);\n\t\t\twhile(i --> 0)\n\t\t\t\tptr[i] = LittleFloat(ptr[i]);\n\t\t}\n\t\tformat = QAF_F32;\n\t}\n\telse if (info.format == 3 && info.bitwidth == 64)\t//signed doubles, converted to floats cos doubles is just silly.\n\t{\n\t\tif (bigendian)\n\t\t{\n\t\t\tsize_t i = info.samples*info.numchannels;\n\t\t\tqint64_t *in = (qint64_t*)(data + info.dataofs);\n\t\t\tfloat *out = (short *)(data + info.dataofs);\n\t\t\tunion {\n\t\t\t\tqint64_t i;\n\t\t\t\tdouble d;\n\t\t\t} s;\n\t\t\twhile(i --> 0)\n\t\t\t{\n\t\t\t\ts.i = LittleI64(in[i]);\n\t\t\t\tout[i] = s.d;\n\t\t\t}\n\t\t}\n\t\tformat = QAF_F32;\n\t}\n#else\n\telse if (info.format == 3 && info.bitwidth == 32)\t//signed floats\n\t{\n\t\tshort *out = (short *)(data + info.dataofs);\n\t\tfloat *in = (float *)(data + info.dataofs);\n\t\tsize_t samples = info.samples*info.numchannels;\n\t\tint t;\n\t\twhile(samples --> 0)\n\t\t{\t//in place size conversion, so we need to do it forwards.\n\t\t\tt = LittleFloat(*in++) * 32767;\n\t\t\tt = bound(-32768, t, 32767);\n\t\t\t*out++ = t;\n\t\t}\n\t\tformat = QAF_S16;\n\t}\n\telse if (info.format == 3 && info.bitwidth == 64)\t//signed doubles\n\t{\n\t\tshort *out = (short *)(data + info.dataofs);\n\t\tqint64_t *in = (qint64_t *)(data + info.dataofs);\n\t\tunion {\n\t\t\tqint64_t i;\n\t\t\tdouble d;\n\t\t} s;\n\t\tsize_t samples = info.samples*info.numchannels;\n\t\tint t;\n\t\twhile(samples --> 0)\n\t\t{\t//in place size conversion, so we need to do it forwards.\n\t\t\ts.i = LittleI64(*in++);\n\t\t\tt = s.d * 32767;\n\t\t\tt = bound(-32768, t, 32767);\n\t\t\t*out++ = t;\n\t\t}\n\t\tformat = QAF_S16;\n\t}\n#endif\n\telse\n\t{\n\t\ts->loadstate = SLS_FAILED;\n\t\tswitch(info.format)\n\t\t{\n\t\tcase 1/*WAVE_FORMAT_PCM*/:\n\t\tcase 3/*WAVE_FORMAT_IEEE_FLOAT*/:\t\tCon_Printf (\"%s has an unsupported width (%i bits).\\n\", s->name, info.bitwidth); break;\n\t\tcase 6/*WAVE_FORMAT_ALAW*/:\t\t\t\tCon_Printf (\"%s uses unsupported a-law format.\\n\", s->name); break;\n\t\tcase 7/*WAVE_FORMAT_MULAW*/:\t\t\tCon_Printf (\"%s uses unsupported mu-law format.\\n\", s->name); break;\n\t\tcase 0xfffe/*WAVE_FORMAT_EXTENSIBLE*/:\n\t\tdefault:\t\t\t\t\t\t\t\tCon_Printf (\"%s has an unsupported format (%#\"PRIX16\").\\n\", s->name, info.format); break;\n\t\t}\n\t\treturn false;\n\t}\n\n\treturn ResampleSfx (s, info.rate, info.numchannels, format, info.samples, info.loopstart, data + info.dataofs);\n}\n\n#ifdef FTE_TARGET_WEB\n#if 1\nvoid S_BrowserDecoded (void *ctx, void *dataptr, int frames, int channels, float rate)\n{\n\tsfx_t *sfx = ctx;\n\n\t//make sure we were not restarting at the time... FIXME: make stricter?\n\textern sfx_t\t\t*known_sfx;\n\textern int\t\t\tnum_sfx;\n\tint id = sfx-known_sfx;\n\tif (id < 0 || id >= num_sfx || sfx != &known_sfx[id])\n\t\treturn; //err... don't crash out!\n\n\tsfx->loopstart = -1;\n\tif (dataptr)\n\t{\t//okay, something loaded. woo.\n\t\tZ_Free(sfx->decoder.buf);\n\t\tsfx->decoder.buf = NULL;\n\t\tsfx->decoder.decodedata = NULL;\n\t\tResampleSfx (sfx, rate, channels, QAF_S16, frames, -1, dataptr);\n\t}\n\telse\n\t{\n\t\tCon_Printf(CON_WARNING\"Failed to decode %s\\n\", sfx->name);\n\t\tsfx->loadstate = SLS_FAILED;\n\t}\n}\nstatic qboolean QDECL S_LoadBrowserFile (sfx_t *s, qbyte *data, size_t datalen, int sndspeed, qboolean forcedecode)\n{\n\tstruct sfxcache_s *buf;\n\n\tif (datalen > 4 && !strncmp(data, \"RIFF\", 4))\n\t\treturn false;\t//do NOT use this code for wav files. we have no way to read the looping flags which would break things in certain situations. we MUST fall back on our normal loader.\n\n\ts->decoder.buf = buf = Z_Malloc(sizeof(*buf)+128);\n\t//fill with a placeholder\n\tbuf->length = 128;\n\tbuf->speed = snd_speed;\n\tbuf->format = QAF_S8; //something basic\n\tbuf->numchannels=1;\n\tbuf->soundoffset = 0;\n\tbuf->data = (qbyte*)(buf+1);\n\n\ts->loopstart = 0; //keep looping silence until it actually loads something.\n\n\treturn emscriptenfte_pcm_loadaudiofile(s, S_BrowserDecoded, data, datalen, sndspeed);\n}\n#else\n//web browsers contain their own decoding libraries that our openal stuff can use.\nstatic qboolean QDECL S_LoadBrowserFile (sfx_t *s, qbyte *data, size_t datalen, int sndspeed, qboolean forcedecode)\n{\n\tsfxcache_t *sc;\n\ts->decoder.buf = sc = BZ_Malloc(sizeof(sfxcache_t) + datalen);\n\ts->loopstart = -1;\n\tsc->data = (qbyte*)(sc+1);\n\tsc->length = datalen;\n\tsc->format = QAF_BLOB;\t//ie: not pcm\n\tsc->speed = sndspeed;\n\tsc->numchannels = 2;\n\tsc->soundoffset = 0;\n\tmemcpy(sc->data, data, datalen);\n\n\treturn true;\n}\n#endif\n#endif\n\nqboolean QDECL S_LoadOVSound (sfx_t *s, qbyte *data, size_t datalen, int sndspeed, qboolean forcedecode);\n\n//highest priority is last.\nstatic struct\n{\n\tS_LoadSound_t loadfunc;\n\tvoid *module;\n} AudioInputPlugins[10] =\n{\n#ifdef FTE_TARGET_WEB\n\t{S_LoadBrowserFile},\n#endif\n#ifdef AVAIL_OGGVORBIS\n\t{S_LoadOVSound},\n#endif\n\t{S_LoadWavSound},\n#ifdef PACKAGE_DOOMWAD\n\t{S_LoadDoomSound},\n//\t{S_LoadDoomSpeakerSound},\n#endif\n};\n\nqboolean S_RegisterSoundInputPlugin(void *module, S_LoadSound_t loadfnc)\n{\n\tint i;\n\tfor (i = 0; i < sizeof(AudioInputPlugins)/sizeof(AudioInputPlugins[0]); i++)\n\t{\n\t\tif (!AudioInputPlugins[i].loadfunc)\n\t\t{\n\t\t\tAudioInputPlugins[i].module = module;\n\t\t\tAudioInputPlugins[i].loadfunc = loadfnc;\n\t\t\treturn true;\n\t\t}\n\t}\n\treturn false;\n}\nvoid S_UnregisterSoundInputModule(void *module)\n{\t//unregister all sound handlers for the given module.\n\tint i;\n\tfor (i = 0; i < sizeof(AudioInputPlugins)/sizeof(AudioInputPlugins[0]); i++)\n\t{\n\t\tif (AudioInputPlugins[i].module == module)\n\t\t{\n\t\t\tAudioInputPlugins[i].module = NULL;\n\t\t\tAudioInputPlugins[i].loadfunc = NULL;\n\t\t}\n\t}\n}\n\nstatic void S_LoadedOrFailed (void *ctx, void *ctxdata, size_t a, size_t b)\n{\n\tsfx_t *s = ctx;\n\ts->loadstate = a;\n}\n/*\n==============\nS_LoadSound\n==============\n*/\n\nstatic void S_LoadSoundWorker (void *ctx, void *ctxdata, size_t forcedecode, size_t b)\n{\n\tsfx_t *s = ctx;\n\tchar\tnamebuffer[256];\n\tqbyte\t*data;\n\tint i;\n\tsize_t result;\n\tchar *name = s->name;\n\tsize_t filesize;\n\n\ts->loopstart = -1;\n\n\tif (s->syspath)\n\t{\n\t\tvfsfile_t *f;\n\n\t\tif ((f = VFSOS_Open(name, \"rb\")))\n\t\t{\n\t\t\tfilesize = VFS_GETLEN(f);\n\t\t\tdata = BZ_Malloc (filesize);\n\t\t\tresult = VFS_READ(f, data, filesize);\n\n\t\t\tif (result != filesize)\n\t\t\t\tCon_SafePrintf(\"S_LoadSound() fread: Filename: %s, expected %\"PRIuSIZE\", result was %\"PRIuSIZE\"\\n\", name, filesize, result);\n\n\t\t\tVFS_CLOSE(f);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tCon_SafePrintf (\"Couldn't load %s\\n\", namebuffer);\n\t\t\tCOM_AddWork(WG_MAIN, S_LoadedOrFailed, s, NULL, SLS_FAILED, 0);\n\t\t\treturn;\n\t\t}\n\t}\n\telse\n\t{\n\t//Con_Printf (\"S_LoadSound: %x\\n\", (int)stackbuf);\n\t// load it in\n\t\tconst char *prefixes[] = {\"sound/\", \"\"};\n\t\tconst char *extensions[] = {\n\t\t\t\".wav\", \n#ifdef AVAIL_OGGOPUS\n\t\t\t\".opus\",\n#endif\n#ifdef AVAIL_OGGVORBIS\n\t\t\t\".ogg\",\n#endif\n\t\t};\n\t\tchar altname[sizeof(namebuffer)];\n\t\tchar orig[16];\n\t\tsize_t pre, ex;\n\n\t\tdata = NULL;\n\t\tfilesize = 0;\n\t\tif (*name == '*')\t//q2 sexed sounds\n\t\t{\n\t\t\t//clq2_parsestartsound detects this also, and should not try playing these sounds.\n\t\t\ts->loadstate = SLS_FAILED;\n\t\t\treturn;\n\t\t}\n\n\t\tfor (pre = 0; !data && pre < countof(prefixes); pre++)\n\t\t{\n\t\t\tif (name[0] == '.' && name[1] == '.' && name[2] == '/')\n\t\t\t{\t//someone's being specific. disable prefixes entirely.\n\t\t\t\tif (pre)\n\t\t\t\t\tbreak;\n\t\t\t\t//not relative to sound/\n\t\t\t\tQ_snprintfz(namebuffer, sizeof(namebuffer), \"%s\", name+3);\n\t\t\t}\n\t\t\telse\n\t\t\t\tQ_snprintfz(namebuffer, sizeof(namebuffer), \"%s%s\", prefixes[pre], name);\n\n\t\t\tdata = FS_LoadMallocFile(namebuffer, &filesize);\n\t\t\tif (data)\n\t\t\t\tbreak;\n\t\t\tCOM_FileExtension(namebuffer, orig, sizeof(orig));\n\t\t\tCOM_StripExtension(namebuffer, altname, sizeof(altname));\n\t\t\tfor (ex = 0; ex < countof(extensions); ex++)\n\t\t\t{\n\t\t\t\tif (!strcmp(orig, extensions[ex]+1))\n\t\t\t\t\tcontinue;\n\t\t\t\tQ_snprintfz(namebuffer, sizeof(namebuffer), \"%s%s\", altname, extensions[ex]);\n\t\t\t\tdata = FS_LoadMallocFile(namebuffer, &filesize);\n\t\t\t\tif (data)\n\t\t\t\t{\n\t\t\t\t\tstatic float throttletimer;\n\t\t\t\t\tCon_ThrottlePrintf(&throttletimer, 1, \"S_LoadSound: %s%s requested, but could only find %s\\n\", prefixes[pre], name, namebuffer);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (data && !Ruleset_FileLoaded(name, data, filesize))\n\t\t{\n\t\t\tBZ_Free(data);\n\t\t\tdata = NULL;\n\t\t\tfilesize = 0;\n\t\t}\n\t}\n\n\tif (!data)\n\t{\n\t\t//FIXME: check to see if queued for download.\n\t\tif (name[0] == '.' && name[1] == '.' && name[2] == '/')\n\t\t\tCon_DPrintf (\"Couldn't load %s\\n\", name+3);\n\t\telse\n\t\t\tCon_DPrintf (\"Couldn't load sound/%s\\n\", name);\n\t\tCOM_AddWork(WG_MAIN, S_LoadedOrFailed, s, NULL, SLS_FAILED, 0);\n\t\treturn;\n\t}\n\n\tfor (i = sizeof(AudioInputPlugins)/sizeof(AudioInputPlugins[0])-1; i >= 0; i--)\n\t{\n\t\tif (AudioInputPlugins[i].loadfunc)\n\t\t{\n\t\t\tif (AudioInputPlugins[i].loadfunc(s, data, filesize, snd_speed, forcedecode))\n\t\t\t{\n\t\t\t\t//wake up the main thread in case it decided to wait for us.\n\t\t\t\tCOM_AddWork(WG_MAIN, S_LoadedOrFailed, s, NULL, SLS_LOADED, 0);\n\t\t\t\tBZ_Free(data);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n\n\tif (s->loadstate != SLS_FAILED)\n\t\tCon_Printf (\"Format not recognised: %s\\n\", namebuffer);\n\n\tCOM_AddWork(WG_MAIN, S_LoadedOrFailed, s, NULL, SLS_FAILED, 0);\n\tBZ_Free(data);\n\treturn;\n}\n\nqboolean S_LoadSound (sfx_t *s, qboolean force)\n{\n\tif (s->loadstate == SLS_NOTLOADED && sndcardinfo)\n\t{\n\t\ts->loadstate = SLS_LOADING;\n\t\tCOM_AddWork(WG_LOADER, S_LoadSoundWorker, s, NULL, force, 0);\n\t}\n\tif (s->loadstate == SLS_FAILED)\n\t\treturn false;\t//it failed to load once before, don't bother trying again.\n\n\treturn true;\t//loaded okay, or still loading\n}\n\n/*\n===============================================================================\n\nWAV loading\n\n===============================================================================\n*/\n\ntypedef struct\n{\n\tchar\t*wavname;\n\tqbyte\t*data_p;\n\tqbyte \t*iff_end;\n\tqbyte \t*last_chunk;\n\tqbyte \t*iff_data;\n\tint \tiff_chunk_len;\n} wavctx_t;\n\nstatic short GetLittleShort(wavctx_t *ctx)\n{\n\tshort val = 0;\n\tval = *ctx->data_p;\n\tval = val + (*(ctx->data_p+1)<<8);\n\tctx->data_p += 2;\n\treturn val;\n}\n\nstatic int GetLittleLong(wavctx_t *ctx)\n{\n\tint val = 0;\n\tval = *ctx->data_p;\n\tval = val + (*(ctx->data_p+1)<<8);\n\tval = val + (*(ctx->data_p+2)<<16);\n\tval = val + (*(ctx->data_p+3)<<24);\n\tctx->data_p += 4;\n\treturn val;\n}\n\nstatic unsigned int FindNextChunk(wavctx_t *ctx, char *name)\n{\n\tunsigned int dataleft;\n\n\twhile (1)\n\t{\n\t\tdataleft = ctx->iff_end - ctx->last_chunk;\n\t\tif (dataleft < 8)\n\t\t{\t// didn't find the chunk\n\t\t\tctx->data_p = NULL;\n\t\t\treturn 0;\n\t\t}\n\n\t\tctx->data_p=ctx->last_chunk;\n\t\tctx->data_p += 4;\n\t\tdataleft-= 8;\n\t\tctx->iff_chunk_len = GetLittleLong(ctx);\n\t\tif (ctx->iff_chunk_len < 0)\n\t\t{\n\t\t\tctx->data_p = NULL;\n\t\t\treturn 0;\n\t\t}\n\t\tif (ctx->iff_chunk_len > dataleft)\n\t\t{\n\t\t\tCon_DPrintf (\"\\\"%s\\\" seems truncated by %i bytes\\n\", ctx->wavname, ctx->iff_chunk_len-dataleft);\n#if 1\n\t\t\tctx->iff_chunk_len = dataleft;\n#else\n\t\t\tctx->data_p = NULL;\n\t\t\treturn 0;\n#endif\n\t\t}\n\n\t\tdataleft-= ctx->iff_chunk_len;\n//\t\tif (iff_chunk_len > 1024*1024)\n//\t\t\tSys_Error (\"FindNextChunk: %i length is past the 1 meg sanity limit\", iff_chunk_len);\n\t\tctx->data_p -= 8;\n\t\tctx->last_chunk = ctx->data_p + 8 + ctx->iff_chunk_len;\n\t\tif ((ctx->iff_chunk_len&1) && dataleft)\n\t\t\tctx->last_chunk++;\n\t\tif (!Q_strncmp(ctx->data_p, name, 4))\n\t\t\treturn ctx->iff_chunk_len;\n\t}\n}\n\nstatic unsigned int FindChunk(wavctx_t *ctx, char *name)\n{\n\tctx->last_chunk = ctx->iff_data;\n\treturn FindNextChunk (ctx, name);\n}\n\n\n#if 0\nstatic void DumpChunks(void)\n{\n\tchar\tstr[5];\n\n\tstr[4] = 0;\n\tdata_p=iff_data;\n\tdo\n\t{\n\t\tmemcpy (str, data_p, 4);\n\t\tdata_p += 4;\n\t\tiff_chunk_len = GetLittleLong();\n\t\tCon_Printf (\"0x%x : %s (%d)\\n\", (int)(data_p - 4), str, iff_chunk_len);\n\t\tdata_p += (iff_chunk_len + 1) & ~1;\n\t} while (data_p < iff_end);\n}\n#endif\n\n/*\n============\nGetWavinfo\n============\n*/\nstatic wavinfo_t GetWavinfo (char *name, qbyte *wav, int wavlength)\n{\n\textern cvar_t snd_ignorecueloops;\n\twavinfo_t\tinfo;\n\tint\t\ti;\n\tint\t\tsamples;\n\tint\t\tchunklen;\n\twavctx_t ctx;\n\n\tmemset (&info, 0, sizeof(info));\n\n\tif (!wav)\n\t\treturn info;\n\n\tctx.data_p = NULL;\n\tctx.last_chunk = NULL;\n\tctx.iff_chunk_len = 0;\n\n\tctx.iff_data = wav;\n\tctx.iff_end = wav + wavlength;\n\tctx.wavname = name;\n\n// find \"RIFF\" chunk\n\tchunklen = FindChunk(&ctx, \"RIFF\");\n\tif (chunklen < 4 ||  Q_strncmp(ctx.data_p+8, \"WAVE\", 4))\n\t{\n\t\tCon_Printf(\"Missing RIFF/WAVE chunks in %s\\n\", name);\n\t\treturn info;\n\t}\n\n// get \"fmt \" chunk\n\tctx.iff_data = ctx.data_p + 12;\n// DumpChunks ();\n\n\tchunklen = FindChunk(&ctx, \"fmt \");\n\tif (chunklen < 24-8)\n\t{\n\t\tCon_Printf(\"Missing/truncated fmt chunk\\n\");\n\t\treturn info;\n\t}\n\tctx.data_p += 8;\n\tinfo.format = (unsigned short)GetLittleShort(&ctx);\n\n\tinfo.numchannels = (unsigned short)GetLittleShort(&ctx);\n\tinfo.rate = GetLittleLong(&ctx);\n\tctx.data_p += 4;\t//nAvgBytesPerSec\n\tctx.data_p += 2;\t//nBlockAlign\n\tinfo.bitwidth = (unsigned short)GetLittleShort(&ctx);\t//meant to be a multiple of 8, but when its not we will treat it as 'nValidBits' and assume the lower bits are padded to bytes.\n\n\tif (info.format == 0xfffe)\n\t{\n\t\tif (GetLittleShort(&ctx) >= 22) //cbSize\n\t\t{\n\t\t\tctx.data_p += 2;\t//wValidBitsPerSample. don't really care\n\t\t\tctx.data_p += 4;\t//dwChannelMask. don't really care.\n\t\t\tif      (!memcmp(ctx.data_p, \"\\x01\\x00\\x00\\x00\\x00\\x00\\x10\\x00\\x80\\x00\\x00\\xaa\\x00\\x38\\x9b\\x71\", 16))\n\t\t\t\tinfo.format = 1;\t//pcm(regular ints)\n\t\t\telse if (!memcmp(ctx.data_p, \"\\x03\\x00\\x00\\x00\\x00\\x00\\x10\\x00\\x80\\x00\\x00\\xaa\\x00\\x38\\x9b\\x71\", 16))\n\t\t\t\tinfo.format = 3;\t//float\n\t\t\t//else leave it unusable.\n\t\t\tctx.data_p += 16;\t//SubFormat. convert to the real format\n\t\t}\n\t}\n\n// get cue chunk\n\tchunklen = FindChunk(&ctx, \"cue \");\n\tif (chunklen >= 36-8 && !snd_ignorecueloops.ival)\n\t{\n\t\tctx.data_p += 32;\n\t\tinfo.loopstart = GetLittleLong(&ctx);\n//\t\tCon_Printf(\"loopstart=%d\\n\", sfx->loopstart);\n\n\t// if the next chunk is a LIST chunk, look for a cue length marker\n\t\tchunklen = FindNextChunk (&ctx, \"LIST\");\n\t\tif (chunklen >= 32-8)\n\t\t{\n\t\t\tif (!strncmp (ctx.data_p + 28, \"mark\", 4))\n\t\t\t{\t// this is not a proper parse, but it works with cooledit...\n\t\t\t\tctx.data_p += 24;\n\t\t\t\ti = GetLittleLong (&ctx);\t// samples in loop\n\t\t\t\tinfo.samples = info.loopstart + i;\n//\t\t\t\tCon_Printf(\"looped length: %i\\n\", i);\n\t\t\t}\n\t\t}\n\t}\n\telse\n\t\tinfo.loopstart = -1;\n\n// find data chunk\n\tchunklen = FindChunk(&ctx, \"data\");\n\tif (!ctx.data_p)\n\t{\n\t\tCon_Printf(\"Missing data chunk in %s\\n\", name);\n\t\treturn info;\n\t}\n\n\tctx.data_p += 8;\n\tsamples = (chunklen<<3) / ((info.bitwidth+7)&~7) / info.numchannels;\n\n\tif (info.samples)\n\t{\n\t\tif (samples < info.samples)\n\t\t{\n\t\t\tinfo.samples = samples;\n\t\t\tCon_Printf (\"Sound %s has a bad loop length\\n\", name);\n\t\t}\n\t}\n\telse\n\t\tinfo.samples = samples;\n\n\tif (info.loopstart > info.samples)\n\t{\n\t\tCon_Printf (\"Sound %s has a bad loop start\\n\", name);\n\t\tinfo.loopstart = info.samples;\n\t}\n\n\tinfo.dataofs = ctx.data_p - wav;\n\n\treturn info;\n}\n"
  },
  {
    "path": "engine/client/snd_mix.c",
    "content": "/*\nCopyright (C) 1996-1997 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n*/\n// snd_mix.c -- portable code to mix sounds for snd_dma.c\n\n#include \"quakedef.h\"\n\n#ifdef HAVE_MIXER\n\n\n//#define MIXER_PAINT_F32\n#ifdef MIXER_PAINT_F32\n\t#define MIX_16_8(val) ((val)/(float)(1<<(15+8)))\n\t#define MIX_8_8(val) ((val)/(float)(1<<(7+8)))\n\ttypedef struct {\n\t\tfloat s[MAXSOUNDCHANNELS];\n\t} portable_samplegroup_t;\n#else\n\t#define MIX_16_8(val) ((val)>>8)\t//value is a 16bit*8bit value (audio*vol) value. discard the lower 8 bits to treat the volume as a fraction\n\t#define MIX_8_8(val) (val)\t//value is a 8bit*8bit value (audio*vol) value. result is 16bit.\n\n\ttypedef struct {\n\t\tint s[MAXSOUNDCHANNELS];\t//signed, 1=0x7fff ish. will be clamped to allow oversaturation\n\t} portable_samplegroup_t;\n#endif\n\n#define\tPAINTBUFFER_SIZE\t2048\n\nstatic portable_samplegroup_t paintbuffer[PAINTBUFFER_SIZE];\t//FIXME: we really ought to be using SSE and floats or something.\n\nvoid S_TransferPaintBuffer(soundcardinfo_t *sc, int endtime)\n{\n\tunsigned int \tout_idx;\n\tunsigned int \tcount;\n\tunsigned int \toutlimit;\n#ifdef MIXER_PAINT_F32\n\tfloat \t\t\t*p = (float *fte_restrict)paintbuffer;\n#else\n\tint \t\t\t*p = (int *fte_restrict) paintbuffer;\n#endif\n\tint\t\t\t\tval;\n//\tint\t\t\t\tsnd_vol;\n\tvoid\t\t\t*pbuf;\n\tint\t\t\t\ti, numc;\n\n\tcount = (endtime - sc->paintedtime) * sc->sn.numchannels;\n\toutlimit = sc->sn.samples;\n\tout_idx = (sc->paintedtime * sc->sn.numchannels) % outlimit;\n//\tsnd_vol = (volume.value*voicevolumemod)*256;\n\tnumc = sc->sn.numchannels;\n\n\tpbuf = sc->Lock(sc, &out_idx);\n\tif (!pbuf)\n\t\treturn;\n\n\tswitch(sc->sn.sampleformat)\n\t{\n\tcase QSF_INVALID:\t//erk...\n\tcase QSF_EXTERNALMIXER:\t//shouldn't reach this.\n\t\tbreak;\n\tcase QSF_U8:\n\t\t{\n\t\t\tunsigned char *out = (unsigned char *) pbuf;\n\t\t\twhile (count)\n\t\t\t{\n\t\t\t\tfor (i = 0; i < numc; i++)\n\t\t\t\t{\n#ifdef MIXER_PAINT_F32\n\t\t\t\t\tval = (*p++ + 1)*128;\n\t\t\t\t\tif (val > 255)\n\t\t\t\t\t\tval = 255;\n\t\t\t\t\telse if (val < 0)\n\t\t\t\t\t\tval = 0;\n\t\t\t\t\tout[out_idx] = val;\n#else\n\t\t\t\t\tval = *p++;\n\t\t\t\t\tif (val > 0x7fff)\n\t\t\t\t\t\tval = 0x7fff;\n\t\t\t\t\telse if (val < (short)0x8000)\n\t\t\t\t\t\tval = (short)0x8000;\n\t\t\t\t\tout[out_idx] = (val>>8) + 128;\n#endif\n\t\t\t\t\tout_idx = (out_idx + 1) % outlimit;\n\t\t\t\t}\n\t\t\t\tp += MAXSOUNDCHANNELS - numc;\n\t\t\t\tcount -= numc;\n\t\t\t}\n\t\t}\n\t\tbreak;\n\tcase QSF_S8:\n\t\t{\n\t\t\tchar *out = (char *) pbuf;\n\t\t\twhile (count)\n\t\t\t{\n\t\t\t\tfor (i = 0; i < numc; i++)\n\t\t\t\t{\n#ifdef MIXER_PAINT_F32\n\t\t\t\t\tval = *p++*128;\n\t\t\t\t\tif (val > 127)\n\t\t\t\t\t\tval = 127;\n\t\t\t\t\telse if (val < -128)\n\t\t\t\t\t\tval = -128;\n\t\t\t\t\tout[out_idx] = val;\n#else\n\t\t\t\t\tval = *p++;\n\t\t\t\t\tif (val > 0x7fff)\n\t\t\t\t\t\tval = 0x7fff;\n\t\t\t\t\telse if (val < (short)0x8000)\n\t\t\t\t\t\tval = (short)0x8000;\n\t\t\t\t\tout[out_idx] = (val>>8);\n#endif\n\t\t\t\t\tout_idx = (out_idx + 1) % outlimit;\n\t\t\t\t}\n\t\t\t\tp += MAXSOUNDCHANNELS - numc;\n\t\t\t\tcount -= numc;\n\t\t\t}\n\t\t}\n\t\tbreak;\n\tcase QSF_S16:\n\t\t{\n\t\t\tshort *out = (short *) pbuf;\n\t\t\twhile (count)\n\t\t\t{\n\t\t\t\tfor (i = 0; i < numc; i++)\n\t\t\t\t{\n#ifdef MIXER_PAINT_F32\n\t\t\t\t\tval = *p++*0x7fff;\n#else\n\t\t\t\t\tval = *p++;\n#endif\n\t\t\t\t\tif (val > 0x7fff)\n\t\t\t\t\t\tval = 0x7fff;\n\t\t\t\t\telse if (val < (short)0x8000)\n\t\t\t\t\t\tval = (short)0x8000;\n\t\t\t\t\tout[out_idx] = val;\n\t\t\t\t\tout_idx = (out_idx + 1) % outlimit;\n\t\t\t\t}\n\t\t\t\tp += MAXSOUNDCHANNELS - numc;\n\t\t\t\tcount -= numc;\n\t\t\t}\n\t\t}\n\t\tbreak;\n\tcase QSF_F32:\n\t\t{\n\t\t\tfloat *out = (float *) pbuf;\n\t\t\twhile (count)\n\t\t\t{\n\t\t\t\tfor (i = 0; i < numc; i++)\n\t\t\t\t{\n#ifdef MIXER_PAINT_F32\t//FIXME: replace with a memcpy.\n\t\t\t\t\tout[out_idx] = *p++;\n#else\n\t\t\t\t\tout[out_idx] = *p++ * (1.0 / 32768);\n#endif\n\t\t\t\t\tout_idx = (out_idx + 1) % outlimit;\n\t\t\t\t}\n\t\t\t\tp += MAXSOUNDCHANNELS - numc;\n\t\t\t\tcount -= numc;\n\t\t\t}\n\t\t}\n\t\tbreak;\n\t}\n\n\tsc->Unlock(sc, pbuf);\n}\n\n\n/*\n===============================================================================\n\nCHANNEL MIXING\n\n===============================================================================\n*/\n\nstatic void SND_PaintChannel8_O2I1\t(channel_t *ch, sfxcache_t *sc, int starttime, int count, int rate);\nstatic void SND_PaintChannel16_O2I1\t(channel_t *ch, sfxcache_t *sc, int starttime, int count, int rate);\nstatic void SND_PaintChannel8_O4I1\t(channel_t *ch, sfxcache_t *sc, int count, int rate);\nstatic void SND_PaintChannel16_O4I1\t(channel_t *ch, sfxcache_t *sc, int count, int rate);\nstatic void SND_PaintChannel8_O6I1\t(channel_t *ch, sfxcache_t *sc, int count, int rate);\nstatic void SND_PaintChannel16_O6I1\t(channel_t *ch, sfxcache_t *sc, int count, int rate);\nstatic void SND_PaintChannel8_O8I1\t(channel_t *ch, sfxcache_t *sc, int count, int rate);\nstatic void SND_PaintChannel16_O8I1\t(channel_t *ch, sfxcache_t *sc, int count, int rate);\nstatic void SND_PaintChannel8_O2I2\t(channel_t *ch, sfxcache_t *sc, int starttime, int count, int rate);\nstatic void SND_PaintChannel16_O2I2\t(channel_t *ch, sfxcache_t *sc, int starttime, int count, int rate);\n\n#ifdef MIXER_F32\nstatic void SND_PaintChannel32F_O2I1(channel_t *ch, sfxcache_t *sc, int starttime, int count, int rate);\nstatic void SND_PaintChannel32F_O4I1(channel_t *ch, sfxcache_t *sc, int count, int rate);\nstatic void SND_PaintChannel32F_O6I1(channel_t *ch, sfxcache_t *sc, int count, int rate);\nstatic void SND_PaintChannel32F_O8I1(channel_t *ch, sfxcache_t *sc, int count, int rate);\nstatic void SND_PaintChannel32F_O2I2(channel_t *ch, sfxcache_t *sc, int starttime, int count, int rate);\n#endif\n\n//NOTE: MAY NOT CALL SYS_ERROR\nvoid S_PaintChannels(soundcardinfo_t *sc, int endtime)\n{\n\tint \ti;\n\tint \tend;\n\tchannel_t *ch;\n\tsfxcache_t\tscachebuf;\n\tsfxcache_t\t*scache;\n\tsfx_t *s;\n\tint\t\tltime, count;\n\tint avail;\n\tunsigned int maxlen = ruleset_allow_overlongsounds.ival?0xffffffffu>>PITCHSHIFT:snd_speed*20;\n\tint rate;\n\n\twhile (sc->paintedtime < endtime)\n\t{\n\t// if paintbuffer is smaller than DMA buffer\n\t\tend = endtime;\n\t\tif (endtime - sc->paintedtime > PAINTBUFFER_SIZE)\n\t\t\tend = sc->paintedtime + PAINTBUFFER_SIZE;\n\n\t// clear the paint buffer\n\t\tQ_memset(paintbuffer, 0, (end - sc->paintedtime) * sizeof(portable_samplegroup_t));\n\n\t// paint in the channels.\n\t\tch = sc->channel;\n\t\tfor (i=0; i<sc->total_chans ; i++, ch++)\n\t\t{\n\t\t\ts = ch->sfx;\n\t\t\tif (!s || s->loadstate == SLS_LOADING)\n\t\t\t\tcontinue;\n\t\t\tif (!ch->vol[0] && !ch->vol[1] && !ch->vol[2] && !ch->vol[3] && !ch->vol[4] && !ch->vol[5])\n\t\t\t{\n\t\t\t\t//does it still make a sound if it cannot be heard?...\n\t\t\t\t//technically no...\n\t\t\t\t//this code is hacky.\n\t\t\t\tif (s->decoder.querydata)\n\t\t\t\t\ts->decoder.querydata(s, scache=&scachebuf, NULL, 0);\n\t\t\t\telse if (s->decoder.decodedata)\n\t\t\t\t\tscache = s->decoder.decodedata(s, &scachebuf, ch->pos>>PITCHSHIFT, 0);\t/*1 for luck - balances audio termination below*/\n\t\t\t\telse\n\t\t\t\t\tscache = s->decoder.buf;\n\t\t\t\tch->pos += (end-sc->paintedtime)*ch->rate;\n\t\t\t\tif (!scache || (ch->pos>>PITCHSHIFT) > scache->soundoffset+scache->length)\n\t\t\t\t{\n\t\t\t\t\tch->pos = 0;\n\t\t\t\t\tif (s->loopstart != -1)\n\t\t\t\t\t\tch->pos = s->loopstart<<PITCHSHIFT;\n\t\t\t\t\telse if (!(ch->flags & CF_FORCELOOP))\n\t\t\t\t\t{\n\t\t\t\t\t\tch->sfx = NULL;\n\t\t\t\t\t\tif (s->decoder.ended)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (!S_IsPlayingSomewhere(s))\n\t\t\t\t\t\t\t\ts->decoder.ended(s);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tltime = sc->paintedtime;\n\t\t\twhile (ltime < end)\n\t\t\t{\n\t\t\t\tssamplepos_t spos = ch->pos>>PITCHSHIFT;\n\t\t\t\tif (s->decoder.decodedata)\n\t\t\t\t\tscache = s->decoder.decodedata(s, &scachebuf, spos, 1 + (((end - ltime) * ch->rate)>>PITCHSHIFT));\t/*1 for luck - balances audio termination below*/\n\t\t\t\telse if (s->decoder.buf)\n\t\t\t\t{\n\t\t\t\t\tscache = s->decoder.buf;\n\t\t\t\t\tif (spos >= scache->length)\n\t\t\t\t\t\tscache = NULL;\t//EOF\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tscache = NULL;\n\n\t\t\t\tif (!scache)\n\t\t\t\t{\t//hit eof, loop it or stop it\n\t\t\t\t\tif (s->loopstart != -1)\t/*some wavs contain a loop offset directly in the sound file, such samples loop even if a non-looping builtin was used*/\n\t\t\t\t\t{\n\t\t\t\t\t\tch->pos &= ~((~0u)<<PITCHSHIFT);\t/*clear out all but the subsample offset*/\n\t\t\t\t\t\tch->pos += s->loopstart<<PITCHSHIFT;\t//ignore the offset if its off the end of the file\n\t\t\t\t\t}\n\t\t\t\t\telse if (ch->flags & CF_FORCELOOP)\t/*(static)channels which are explicitly looping always loop from the start*/\n\t\t\t\t\t{\n\t\t\t\t\t\t/*restart it*/\n\t\t\t\t\t\tch->pos = 0;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\t// channel just stopped\n\t\t\t\t\t\tch->sfx = NULL;\n\t\t\t\t\t\tif (s->decoder.ended)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (!S_IsPlayingSomewhere(s))\n\t\t\t\t\t\t\t\ts->decoder.ended(s);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\t//retry at that new offset (continue here could give an infinite loop)\n\t\t\t\t\tspos = ch->pos>>PITCHSHIFT;\n\t\t\t\t\tif (s->decoder.decodedata)\n\t\t\t\t\t\tscache = s->decoder.decodedata(s, &scachebuf, spos, 1 + (((end - ltime) * ch->rate)>>PITCHSHIFT));\t/*1 for luck - balances audio termination below*/\n\t\t\t\t\telse if (s->decoder.buf)\n\t\t\t\t\t{\n\t\t\t\t\t\tscache = s->decoder.buf;\n\t\t\t\t\t\tif (spos >= scache->length)\n\t\t\t\t\t\t\tscache = NULL;\t//EOF\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\tscache = NULL;\n\n\t\t\t\t\tif (!scache)\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tif (sc->sn.speed != scache->speed)\n\t\t\t\t\trate = (ch->rate * scache->speed) / sc->sn.speed;\t//sound was loaded at the wrong speed. just play it back at a different speed and hope that all is well. this is nearest sampling, so expect it to be a little poo.\n\t\t\t\telse\n\t\t\t\t\trate = ch->rate;\n\n\t\t\t\tif (spos < scache->soundoffset || spos > scache->soundoffset+scache->length)\n\t\t\t\t\tavail = 0;\t//urm, we would be trying to read outside of the buffer. let mixing slip when there's no data available yet.\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t// find how many samples till the sample ends (clamp max length)\n\t\t\t\t\tavail = scache->length;\n\t\t\t\t\tif (avail > maxlen)\n\t\t\t\t\t\tavail = snd_speed*10;\n\t\t\t\t\tavail = (((int)(scache->soundoffset + avail)<<PITCHSHIFT) - ch->pos + (rate-1)) / rate;\n\t\t\t\t}\n\t\t\t\t// mix the smaller of how much is available or the time left\n\t\t\t\tcount = min(avail, end - ltime);\n\n\t\t\t\tif (count > 0)\n\t\t\t\t{\n\t\t\t\t\tif (ch->pos < 0)\t//sounds with a pos of 0 are delay-start sounds\n\t\t\t\t\t{\n\t\t\t\t\t\t//don't progress past 0, so it actually starts properly at the right time with no clicks or anything\n\t\t\t\t\t\tif (count > (-ch->pos+255)>>PITCHSHIFT)\n\t\t\t\t\t\t\tcount = ((-ch->pos+255)>>PITCHSHIFT);\n\t\t\t\t\t\tltime += count;\n\t\t\t\t\t\tch->pos += count*rate;\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\tswitch(scache->format)\n\t\t\t\t\t{\n\t\t\t\t\tcase QAF_S8:\n\t\t\t\t\t\tif (scache->numchannels==2)\n\t\t\t\t\t\t\tSND_PaintChannel8_O2I2(ch, scache, ltime-sc->paintedtime, count, rate);\n\t\t\t\t\t\telse if (sc->sn.numchannels <= 2)\n\t\t\t\t\t\t\tSND_PaintChannel8_O2I1(ch, scache, ltime-sc->paintedtime, count, rate);\n\t\t\t\t\t\telse if (sc->sn.numchannels <= 4)\n\t\t\t\t\t\t\tSND_PaintChannel8_O4I1(ch, scache, count, rate);\n\t\t\t\t\t\telse if (sc->sn.numchannels <= 6)\n\t\t\t\t\t\t\tSND_PaintChannel8_O6I1(ch, scache, count, rate);\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tSND_PaintChannel8_O8I1(ch, scache, count, rate);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase QAF_S16:\n\t\t\t\t\t\tif (scache->numchannels==2)\n\t\t\t\t\t\t\tSND_PaintChannel16_O2I2(ch, scache, ltime-sc->paintedtime, count, rate);\n\t\t\t\t\t\telse if (sc->sn.numchannels <= 2)\n\t\t\t\t\t\t\tSND_PaintChannel16_O2I1(ch, scache, ltime-sc->paintedtime, count, rate);\n\t\t\t\t\t\telse if (sc->sn.numchannels <= 4)\n\t\t\t\t\t\t\tSND_PaintChannel16_O4I1(ch, scache, count, rate);\n\t\t\t\t\t\telse if (sc->sn.numchannels <= 6)\n\t\t\t\t\t\t\tSND_PaintChannel16_O6I1(ch, scache, count, rate);\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tSND_PaintChannel16_O8I1(ch, scache, count, rate);\n\t\t\t\t\t\tbreak;\n#ifdef MIXER_F32\n\t\t\t\t\tcase QAF_F32:\n\t\t\t\t\t\tif (scache->numchannels==2)\n\t\t\t\t\t\t\tSND_PaintChannel32F_O2I2(ch, scache, ltime-sc->paintedtime, count, rate);\n\t\t\t\t\t\telse if (sc->sn.numchannels <= 2)\n\t\t\t\t\t\t\tSND_PaintChannel32F_O2I1(ch, scache, ltime-sc->paintedtime, count, rate);\n\t\t\t\t\t\telse if (sc->sn.numchannels <= 4)\n\t\t\t\t\t\t\tSND_PaintChannel32F_O4I1(ch, scache, count, rate);\n\t\t\t\t\t\telse if (sc->sn.numchannels <= 6)\n\t\t\t\t\t\t\tSND_PaintChannel32F_O6I1(ch, scache, count, rate);\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tSND_PaintChannel32F_O8I1(ch, scache, count, rate);\n\t\t\t\t\t\tbreak;\n#endif\n#ifdef FTE_TARGET_WEB\n\t\t\t\t\tcase QAF_BLOB:\n\t\t\t\t\t\tbreak;\n#endif\n\t\t\t\t\t}\n\t\t\t\t\tltime += count;\n\t\t\t\t\tch->pos += rate * count;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t// transfer out according to DMA format\n\t\tS_TransferPaintBuffer(sc, end);\n\t\tsc->paintedtime = end;\n\t}\n}\n\nstatic void SND_PaintChannel8_O2I1 (channel_t *ch, sfxcache_t *sc, int starttime, int count, int rate)\n{\n\tint \tdata;\n\tsigned char *sfx;\n\tint\t\ti;\n\tunsigned int pos = ch->pos-(sc->soundoffset<<PITCHSHIFT);\n\n\tif (rate != (1<<PITCHSHIFT))\n\t{\n\t\tsfx = (signed char *fte_restrict)sc->data;\n\t\tfor (i=0 ; i<count ; i++)\n\t\t{\n\t\t\tdata = sfx[pos>>PITCHSHIFT];\n\t\t\tpos += rate;\n\t\t\tpaintbuffer[starttime+i].s[0] += MIX_8_8(ch->vol[0] * data);\n\t\t\tpaintbuffer[starttime+i].s[1] += MIX_8_8(ch->vol[1] * data);\n\t\t}\n\t}\n\telse\n\t{\n\t\tsfx = (signed char *fte_restrict)sc->data + (pos>>PITCHSHIFT);\n\t\tfor (i=0 ; i<count ; i++)\n\t\t{\n\t\t\tdata = sfx[i];\n\t\t\tpaintbuffer[starttime+i].s[0] += MIX_8_8(ch->vol[0] * data);\n\t\t\tpaintbuffer[starttime+i].s[1] += MIX_8_8(ch->vol[1] * data);\n\t\t}\n\t}\n}\n\nstatic void SND_PaintChannel8_O2I2 (channel_t *ch, sfxcache_t *sc, int starttime, int count, int rate)\n{\n//\tint \tdata;\n\tsigned char *sfx;\n\tint\t\ti;\n\tunsigned int pos = ch->pos-(sc->soundoffset<<PITCHSHIFT);\n\n\tif (rate != (1<<PITCHSHIFT))\n\t{\n\t\tsfx = (signed char *fte_restrict)sc->data;\n\t\tfor (i=0 ; i<count ; i++)\n\t\t{\n\t\t\tpaintbuffer[starttime+i].s[0] += MIX_8_8(ch->vol[0] * sfx[(pos>>(PITCHSHIFT-1))&~1]);\n\t\t\tpaintbuffer[starttime+i].s[1] += MIX_8_8(ch->vol[1] * sfx[(pos>>(PITCHSHIFT-1))|1]);\n\t\t\tpos += rate;\n\t\t}\n\t}\n\telse\n\t{\n\t\tsfx = (signed char *fte_restrict)sc->data + (pos>>PITCHSHIFT)*2;\n\t\tfor (i=0 ; i<count ; i++)\n\t\t{\n\t\t\tpaintbuffer[starttime+i].s[0] += MIX_8_8(ch->vol[0] * sfx[(i<<1)]);\n\t\t\tpaintbuffer[starttime+i].s[1] += MIX_8_8(ch->vol[1] * sfx[(i<<1)+1]);\n\t\t}\n\t}\n}\n\nstatic void SND_PaintChannel8_O4I1 (channel_t *ch, sfxcache_t *sc, int count, int rate)\n{\n\tsigned char *sfx;\n\tint\t\ti;\n\tunsigned int pos = ch->pos-(sc->soundoffset<<PITCHSHIFT);\n\n\tif (rate != (1<<PITCHSHIFT))\n\t{\n\t\tsigned char data;\n\t\tsfx = (signed char *fte_restrict)sc->data;\n\t\tfor (i=0 ; i<count ; i++)\n\t\t{\n\t\t\tdata = sfx[pos>>PITCHSHIFT];\n\t\t\tpos += rate;\n\t\t\tpaintbuffer[i].s[0] += MIX_8_8(ch->vol[0] * data);\n\t\t\tpaintbuffer[i].s[1] += MIX_8_8(ch->vol[1] * data);\n\t\t\tpaintbuffer[i].s[2] += MIX_8_8(ch->vol[2] * data);\n\t\t\tpaintbuffer[i].s[3] += MIX_8_8(ch->vol[3] * data);\n\t\t}\n\t}\n\telse\n\t{\n\t\tsfx = (signed char *fte_restrict)sc->data + (pos>>PITCHSHIFT);\n\t\tfor (i=0 ; i<count ; i++)\n\t\t{\n\t\t\tpaintbuffer[i].s[0] += MIX_8_8(ch->vol[0] * sfx[i]);\n\t\t\tpaintbuffer[i].s[1] += MIX_8_8(ch->vol[1] * sfx[i]);\n\t\t\tpaintbuffer[i].s[2] += MIX_8_8(ch->vol[2] * sfx[i]);\n\t\t\tpaintbuffer[i].s[3] += MIX_8_8(ch->vol[3] * sfx[i]);\n\t\t}\n\t}\n}\n\nstatic void SND_PaintChannel8_O6I1 (channel_t *ch, sfxcache_t *sc, int count, int rate)\n{\n\tsigned char *sfx;\n\tint\t\ti;\n\tunsigned int pos = ch->pos-(sc->soundoffset<<PITCHSHIFT);\n\n\tif (rate != (1<<PITCHSHIFT))\n\t{\n\t\tsigned char data;\n\t\tsfx = (signed char *fte_restrict)sc->data;\n\t\tfor (i=0 ; i<count ; i++)\n\t\t{\n\t\t\tdata = sfx[pos>>PITCHSHIFT];\n\t\t\tpos += rate;\n\t\t\tpaintbuffer[i].s[0] += MIX_8_8(ch->vol[0] * data);\n\t\t\tpaintbuffer[i].s[1] += MIX_8_8(ch->vol[1] * data);\n\t\t\tpaintbuffer[i].s[2] += MIX_8_8(ch->vol[2] * data);\n\t\t\tpaintbuffer[i].s[3] += MIX_8_8(ch->vol[3] * data);\n\t\t\tpaintbuffer[i].s[4] += MIX_8_8(ch->vol[4] * data);\n\t\t\tpaintbuffer[i].s[5] += MIX_8_8(ch->vol[5] * data);\n\t\t}\n\t}\n\telse\n\t{\n\t\tsfx = (signed char *fte_restrict)sc->data + (pos>>PITCHSHIFT);\n\t\tfor (i=0 ; i<count ; i++)\n\t\t{\n\t\t\tpaintbuffer[i].s[0] += MIX_8_8(ch->vol[0] * sfx[i]);\n\t\t\tpaintbuffer[i].s[1] += MIX_8_8(ch->vol[1] * sfx[i]);\n\t\t\tpaintbuffer[i].s[2] += MIX_8_8(ch->vol[2] * sfx[i]);\n\t\t\tpaintbuffer[i].s[3] += MIX_8_8(ch->vol[3] * sfx[i]);\n\t\t\tpaintbuffer[i].s[4] += MIX_8_8(ch->vol[4] * sfx[i]);\n\t\t\tpaintbuffer[i].s[5] += MIX_8_8(ch->vol[5] * sfx[i]);\n\t\t}\n\t}\n}\n\nstatic void SND_PaintChannel8_O8I1 (channel_t *ch, sfxcache_t *sc, int count, int rate)\n{\n\tsigned char *sfx;\n\tint\t\ti;\n\tunsigned int pos = ch->pos-(sc->soundoffset<<PITCHSHIFT);\n\n\tif (rate != (1<<PITCHSHIFT))\n\t{\n\t\tsigned char data;\n\t\tsfx = (signed char *fte_restrict)sc->data;\n\t\tfor (i=0 ; i<count ; i++)\n\t\t{\n\t\t\tdata = sfx[pos>>PITCHSHIFT];\n\t\t\tpos += rate;\n\t\t\tpaintbuffer[i].s[0] += MIX_8_8(ch->vol[0] * data);\n\t\t\tpaintbuffer[i].s[1] += MIX_8_8(ch->vol[1] * data);\n\t\t\tpaintbuffer[i].s[2] += MIX_8_8(ch->vol[2] * data);\n\t\t\tpaintbuffer[i].s[3] += MIX_8_8(ch->vol[3] * data);\n\t\t\tpaintbuffer[i].s[4] += MIX_8_8(ch->vol[4] * data);\n\t\t\tpaintbuffer[i].s[5] += MIX_8_8(ch->vol[5] * data);\n\t\t\tpaintbuffer[i].s[6] += MIX_8_8(ch->vol[6] * data);\n\t\t\tpaintbuffer[i].s[7] += MIX_8_8(ch->vol[7] * data);\n\t\t}\n\t}\n\telse\n\t{\n\t\tsfx = (signed char *fte_restrict)sc->data + (pos>>PITCHSHIFT);\n\t\tfor (i=0 ; i<count ; i++)\n\t\t{\n\t\t\tpaintbuffer[i].s[0] += MIX_8_8(ch->vol[0] * sfx[i]);\n\t\t\tpaintbuffer[i].s[1] += MIX_8_8(ch->vol[1] * sfx[i]);\n\t\t\tpaintbuffer[i].s[2] += MIX_8_8(ch->vol[2] * sfx[i]);\n\t\t\tpaintbuffer[i].s[3] += MIX_8_8(ch->vol[3] * sfx[i]);\n\t\t\tpaintbuffer[i].s[4] += MIX_8_8(ch->vol[4] * sfx[i]);\n\t\t\tpaintbuffer[i].s[5] += MIX_8_8(ch->vol[5] * sfx[i]);\n\t\t\tpaintbuffer[i].s[6] += MIX_8_8(ch->vol[6] * sfx[i]);\n\t\t\tpaintbuffer[i].s[7] += MIX_8_8(ch->vol[7] * sfx[i]);\n\t\t}\n\t}\n}\n\n\nstatic void SND_PaintChannel16_O2I1 (channel_t *ch, sfxcache_t *sc, int starttime, int count, int rate)\n{\n\tint data;\n\tint leftvol, rightvol;\n\tsigned short *sfx;\n\tint\ti;\n\tunsigned int pos = ch->pos-(sc->soundoffset<<PITCHSHIFT);\n\n\tleftvol = ch->vol[0];\n\trightvol = ch->vol[1];\n\n\tif (rate != (1<<PITCHSHIFT))\n\t{\n\t\tsigned int data;\n\t\tsfx = (signed short *fte_restrict)sc->data;\n\t\tfor (i=0 ; i<count ; i++)\n\t\t{\n\t\t\tint frac = pos&((1<<PITCHSHIFT)-1);\n\t\t\tdata = sfx[pos>>PITCHSHIFT] * ((1<<PITCHSHIFT)-frac) + sfx[(pos>>PITCHSHIFT)+1] * frac;\n\t\t\tpos += rate;\n\t\t\tpaintbuffer[starttime+i].s[0] += MIX_16_8((leftvol * data)>>PITCHSHIFT);\n\t\t\tpaintbuffer[starttime+i].s[1] += MIX_16_8((rightvol * data)>>PITCHSHIFT);\n\t\t}\n\t}\n\telse\n\t{\n\t\tsfx = (signed short *fte_restrict)sc->data + (pos>>PITCHSHIFT);\n\t\tfor (i=0 ; i<count ; i++)\n\t\t{\n\t\t\tdata = sfx[i];\n\t\t\tpaintbuffer[starttime+i].s[0] += MIX_16_8(data * leftvol);\n\t\t\tpaintbuffer[starttime+i].s[1] += MIX_16_8(data * rightvol);\n\t\t}\n\t}\n}\n\nstatic void SND_PaintChannel16_O2I2 (channel_t *ch, sfxcache_t *sc, int starttime, int count, int rate)\n{\n\tint leftvol, rightvol;\n\tsigned short *sfx;\n\tint\ti;\n\tunsigned int pos = ch->pos-(sc->soundoffset<<PITCHSHIFT);\n\n\tleftvol = ch->vol[0];\n\trightvol = ch->vol[1];\n\n\tif (rate != (1<<PITCHSHIFT))\n\t{\n\t\tsigned short l, r;\n\t\tsfx = (signed short *fte_restrict)sc->data;\n\t\tfor (i=0 ; i<count ; i++)\n\t\t{\n\t\t\tl = sfx[(pos>>(PITCHSHIFT-1))&~1];\n\t\t\tr = sfx[(pos>>(PITCHSHIFT-1))|1];\n\t\t\tpos += rate;\n\t\t\tpaintbuffer[starttime+i].s[0] += MIX_16_8(ch->vol[0] * l);\n\t\t\tpaintbuffer[starttime+i].s[1] += MIX_16_8(ch->vol[1] * r);\n\t\t}\n\t}\n\telse\n\t{\n\t\tsfx = (signed short *fte_restrict)sc->data + (pos>>PITCHSHIFT)*2;\n\t\tfor (i=0 ; i<count ; i++)\n\t\t{\n\t\t\tpaintbuffer[starttime+i].s[0] += MIX_16_8(*sfx++ * leftvol);\n\t\t\tpaintbuffer[starttime+i].s[1] += MIX_16_8(*sfx++ * rightvol);\n\t\t}\n\t}\n}\n\nstatic void SND_PaintChannel16_O4I1 (channel_t *ch, sfxcache_t *sc, int count, int rate)\n{\n\tint vol[4];\n\tsigned short *sfx;\n\tint\ti;\n\tunsigned int pos = ch->pos-(sc->soundoffset<<PITCHSHIFT);\n\n\tvol[0] = ch->vol[0];\n\tvol[1] = ch->vol[1];\n\tvol[2] = ch->vol[2];\n\tvol[3] = ch->vol[3];\n\n\tif (rate != (1<<PITCHSHIFT))\n\t{\n\t\tsigned short data;\n\t\tsfx = (signed short *fte_restrict)sc->data;\n\t\tfor (i=0 ; i<count ; i++)\n\t\t{\n\t\t\tdata = sfx[pos>>PITCHSHIFT];\n\t\t\tpos += rate;\n\t\t\tpaintbuffer[i].s[0] += MIX_16_8(vol[0] * data);\n\t\t\tpaintbuffer[i].s[1] += MIX_16_8(vol[1] * data);\n\t\t\tpaintbuffer[i].s[2] += MIX_16_8(vol[2] * data);\n\t\t\tpaintbuffer[i].s[3] += MIX_16_8(vol[3] * data);\n\t\t}\n\t}\n\telse\n\t{\n\t\tsfx = (signed short *fte_restrict)sc->data + (pos>>PITCHSHIFT);\n\t\tfor (i=0 ; i<count ; i++)\n\t\t{\n\t\t\tpaintbuffer[i].s[0] += MIX_16_8(sfx[i] * vol[0]);\n\t\t\tpaintbuffer[i].s[1] += MIX_16_8(sfx[i] * vol[1]);\n\t\t\tpaintbuffer[i].s[2] += MIX_16_8(sfx[i] * vol[2]);\n\t\t\tpaintbuffer[i].s[3] += MIX_16_8(sfx[i] * vol[3]);\n\t\t}\n\t}\n}\n\nstatic void SND_PaintChannel16_O6I1 (channel_t *ch, sfxcache_t *sc, int count, int rate)\n{\n\tint vol[6];\n\tsigned short *sfx;\n\tint\ti;\n\tunsigned int pos = ch->pos-(sc->soundoffset<<PITCHSHIFT);\n\n\tvol[0] = ch->vol[0];\n\tvol[1] = ch->vol[1];\n\tvol[2] = ch->vol[2];\n\tvol[3] = ch->vol[3];\n\tvol[4] = ch->vol[4];\n\tvol[5] = ch->vol[5];\n\n\tif (rate != (1<<PITCHSHIFT))\n\t{\n\t\tsigned short data;\n\t\tsfx = (signed short *fte_restrict)sc->data;\n\t\tfor (i=0 ; i<count ; i++)\n\t\t{\n\t\t\tdata = sfx[pos>>PITCHSHIFT];\n\t\t\tpos += rate;\n\t\t\tpaintbuffer[i].s[0] += MIX_16_8(vol[0] * data);\n\t\t\tpaintbuffer[i].s[1] += MIX_16_8(vol[1] * data);\n\t\t\tpaintbuffer[i].s[2] += MIX_16_8(vol[2] * data);\n\t\t\tpaintbuffer[i].s[3] += MIX_16_8(vol[3] * data);\n\t\t\tpaintbuffer[i].s[4] += MIX_16_8(vol[4] * data);\n\t\t\tpaintbuffer[i].s[5] += MIX_16_8(vol[5] * data);\n\t\t}\n\t}\n\telse\n\t{\n\t\tsfx = (signed short *fte_restrict)sc->data + (pos>>PITCHSHIFT);\n\t\tfor (i=0 ; i<count ; i++)\n\t\t{\n\t\t\tpaintbuffer[i].s[0] += MIX_16_8(sfx[i] * vol[0]);\n\t\t\tpaintbuffer[i].s[1] += MIX_16_8(sfx[i] * vol[1]);\n\t\t\tpaintbuffer[i].s[2] += MIX_16_8(sfx[i] * vol[2]);\n\t\t\tpaintbuffer[i].s[3] += MIX_16_8(sfx[i] * vol[3]);\n\t\t\tpaintbuffer[i].s[4] += MIX_16_8(sfx[i] * vol[4]);\n\t\t\tpaintbuffer[i].s[5] += MIX_16_8(sfx[i] * vol[5]);\n\t\t}\n\t}\n}\n\nstatic void SND_PaintChannel16_O8I1 (channel_t *ch, sfxcache_t *sc, int count, int rate)\n{\n\tint vol[8];\n\tsigned short *sfx;\n\tint\ti;\n\tunsigned int pos = ch->pos-(sc->soundoffset<<PITCHSHIFT);\n\n\tvol[0] = ch->vol[0];\n\tvol[1] = ch->vol[1];\n\tvol[2] = ch->vol[2];\n\tvol[3] = ch->vol[3];\n\tvol[4] = ch->vol[4];\n\tvol[5] = ch->vol[5];\n\tvol[6] = ch->vol[6];\n\tvol[7] = ch->vol[7];\n\n\tif (rate != (1<<PITCHSHIFT))\n\t{\n\t\tsigned short data;\n\t\tsfx = (signed short *fte_restrict)sc->data;\n\t\tfor (i=0 ; i<count ; i++)\n\t\t{\n\t\t\tdata = sfx[pos>>PITCHSHIFT];\n\t\t\tpos += rate;\n\t\t\tpaintbuffer[i].s[0] += MIX_16_8(vol[0] * data);\n\t\t\tpaintbuffer[i].s[1] += MIX_16_8(vol[1] * data);\n\t\t\tpaintbuffer[i].s[2] += MIX_16_8(vol[2] * data);\n\t\t\tpaintbuffer[i].s[3] += MIX_16_8(vol[3] * data);\n\t\t\tpaintbuffer[i].s[4] += MIX_16_8(vol[4] * data);\n\t\t\tpaintbuffer[i].s[5] += MIX_16_8(vol[5] * data);\n\t\t\tpaintbuffer[i].s[6] += MIX_16_8(vol[6] * data);\n\t\t\tpaintbuffer[i].s[7] += MIX_16_8(vol[7] * data);\n\t\t}\n\t}\n\telse\n\t{\n\t\tsfx = (signed short *fte_restrict)sc->data + (pos>>PITCHSHIFT);\n\t\tfor (i=0 ; i<count ; i++)\n\t\t{\n\t\t\tpaintbuffer[i].s[0] += MIX_16_8(sfx[i] * vol[0]);\n\t\t\tpaintbuffer[i].s[1] += MIX_16_8(sfx[i] * vol[1]);\n\t\t\tpaintbuffer[i].s[2] += MIX_16_8(sfx[i] * vol[2]);\n\t\t\tpaintbuffer[i].s[3] += MIX_16_8(sfx[i] * vol[3]);\n\t\t\tpaintbuffer[i].s[4] += MIX_16_8(sfx[i] * vol[4]);\n\t\t\tpaintbuffer[i].s[5] += MIX_16_8(sfx[i] * vol[5]);\n\t\t\tpaintbuffer[i].s[6] += MIX_16_8(sfx[i] * vol[6]);\n\t\t\tpaintbuffer[i].s[7] += MIX_16_8(sfx[i] * vol[7]);\n\t\t}\n\t}\n}\n\n#ifdef MIXER_F32\nstatic void SND_PaintChannel32F_O2I1 (channel_t *ch, sfxcache_t *sc, int starttime, int count, int rate)\n{\n\tfloat data;\n\tint left, right;\n\tint leftvol, rightvol;\n\tfloat *sfx;\n\tint\ti;\n\tunsigned int pos = ch->pos-(sc->soundoffset<<PITCHSHIFT);\n\n\tleftvol = ch->vol[0]*128;\n\trightvol = ch->vol[1]*128;\n\n\tif (rate != (1<<PITCHSHIFT))\n\t{\n\t\tsfx = (float *fte_restrict)sc->data;\n\t\tfor (i=0 ; i<count ; i++)\n\t\t{\n\t\t\tfloat frac = pos&((1<<PITCHSHIFT)-1);\n\t\t\tdata = sfx[pos>>PITCHSHIFT] * (1-frac) + sfx[(pos>>PITCHSHIFT)+1] * frac;\n\t\t\tpos += rate;\n\t\t\tpaintbuffer[starttime+i].s[0] += (leftvol * data);\n\t\t\tpaintbuffer[starttime+i].s[1] += (rightvol * data);\n\t\t}\n\t}\n\telse\n\t{\n\t\tsfx = (float *fte_restrict)sc->data + (pos>>PITCHSHIFT);\n\t\tfor (i=0 ; i<count ; i++)\n\t\t{\n\t\t\tdata = sfx[i];\n\t\t\tleft = (data * leftvol);\n\t\t\tright = (data * rightvol);\n\t\t\tpaintbuffer[starttime+i].s[0] += left;\n\t\t\tpaintbuffer[starttime+i].s[1] += right;\n\t\t}\n\t}\n}\n\nstatic void SND_PaintChannel32F_O2I2 (channel_t *ch, sfxcache_t *sc, int starttime, int count, int rate)\n{\n\tfloat leftvol, rightvol;\n\tfloat *sfx;\n\tint\ti;\n\tunsigned int pos = ch->pos-(sc->soundoffset<<PITCHSHIFT);\n\n\tleftvol = ch->vol[0]*128;\n\trightvol = ch->vol[1]*128;\n\n\tif (rate != (1<<PITCHSHIFT))\n\t{\n\t\tfloat l, r;\n\t\tsfx = (float *fte_restrict)sc->data;\n\t\tfor (i=0 ; i<count ; i++)\n\t\t{\n\t\t\tl = sfx[(pos>>(PITCHSHIFT-1))&~1];\n\t\t\tr = sfx[(pos>>(PITCHSHIFT-1))|1];\n\t\t\tpos += rate;\n\t\t\tpaintbuffer[starttime+i].s[0] += (l * leftvol);\n\t\t\tpaintbuffer[starttime+i].s[1] += (r * rightvol);\n\t\t}\n\t}\n\telse\n\t{\n\t\tsfx = (float *fte_restrict)sc->data + (pos>>PITCHSHIFT)*2;\n\t\tfor (i=0 ; i<count ; i++)\n\t\t{\n\t\t\tpaintbuffer[starttime+i].s[0] += (*sfx++ * leftvol);\n\t\t\tpaintbuffer[starttime+i].s[1] += (*sfx++ * rightvol);\n\t\t}\n\t}\n}\n\nstatic void SND_PaintChannel32F_O4I1 (channel_t *ch, sfxcache_t *sc, int count, int rate)\n{\n\tint vol[4];\n\tfloat *sfx;\n\tint\ti;\n\tunsigned int pos = ch->pos-(sc->soundoffset<<PITCHSHIFT);\n\n\tvol[0] = ch->vol[0]*128;\n\tvol[1] = ch->vol[1]*128;\n\tvol[2] = ch->vol[2]*128;\n\tvol[3] = ch->vol[3]*128;\n\n\tif (rate != (1<<PITCHSHIFT))\n\t{\n\t\tfloat data;\n\t\tsfx = (float *fte_restrict)sc->data;\n\t\tfor (i=0 ; i<count ; i++)\n\t\t{\n\t\t\tdata = sfx[pos>>PITCHSHIFT];\n\t\t\tpos += rate;\n\t\t\tpaintbuffer[i].s[0] += (vol[0] * data);\n\t\t\tpaintbuffer[i].s[1] += (vol[1] * data);\n\t\t\tpaintbuffer[i].s[2] += (vol[2] * data);\n\t\t\tpaintbuffer[i].s[3] += (vol[3] * data);\n\t\t}\n\t}\n\telse\n\t{\n\t\tsfx = (float *fte_restrict)sc->data + (pos>>PITCHSHIFT);\n\t\tfor (i=0 ; i<count ; i++)\n\t\t{\n\t\t\tpaintbuffer[i].s[0] += (sfx[i] * vol[0]);\n\t\t\tpaintbuffer[i].s[1] += (sfx[i] * vol[1]);\n\t\t\tpaintbuffer[i].s[2] += (sfx[i] * vol[2]);\n\t\t\tpaintbuffer[i].s[3] += (sfx[i] * vol[3]);\n\t\t}\n\t}\n}\n\nstatic void SND_PaintChannel32F_O6I1 (channel_t *ch, sfxcache_t *sc, int count, int rate)\n{\n\tint vol[6];\n\tfloat *sfx;\n\tint\ti;\n\tunsigned int pos = ch->pos-(sc->soundoffset<<PITCHSHIFT);\n\n\tvol[0] = ch->vol[0]*128;\n\tvol[1] = ch->vol[1]*128;\n\tvol[2] = ch->vol[2]*128;\n\tvol[3] = ch->vol[3]*128;\n\tvol[4] = ch->vol[4]*128;\n\tvol[5] = ch->vol[5]*128;\n\n\tif (rate != (1<<PITCHSHIFT))\n\t{\n\t\tfloat data;\n\t\tsfx = (float *fte_restrict)sc->data;\n\t\tfor (i=0 ; i<count ; i++)\n\t\t{\n\t\t\tdata = sfx[pos>>PITCHSHIFT];\n\t\t\tpos += rate;\n\t\t\tpaintbuffer[i].s[0] += (vol[0] * data);\n\t\t\tpaintbuffer[i].s[1] += (vol[1] * data);\n\t\t\tpaintbuffer[i].s[2] += (vol[2] * data);\n\t\t\tpaintbuffer[i].s[3] += (vol[3] * data);\n\t\t\tpaintbuffer[i].s[4] += (vol[4] * data);\n\t\t\tpaintbuffer[i].s[5] += (vol[5] * data);\n\t\t}\n\t}\n\telse\n\t{\n\t\tsfx = (float *fte_restrict)sc->data + (pos>>PITCHSHIFT);\n\t\tfor (i=0 ; i<count ; i++)\n\t\t{\n\t\t\tpaintbuffer[i].s[0] += (sfx[i] * vol[0]);\n\t\t\tpaintbuffer[i].s[1] += (sfx[i] * vol[1]);\n\t\t\tpaintbuffer[i].s[2] += (sfx[i] * vol[2]);\n\t\t\tpaintbuffer[i].s[3] += (sfx[i] * vol[3]);\n\t\t\tpaintbuffer[i].s[4] += (sfx[i] * vol[4]);\n\t\t\tpaintbuffer[i].s[5] += (sfx[i] * vol[5]);\n\t\t}\n\t}\n}\n\nstatic void SND_PaintChannel32F_O8I1 (channel_t *ch, sfxcache_t *sc, int count, int rate)\n{\n\tint vol[8];\n\tfloat *sfx;\n\tint\ti;\n\tunsigned int pos = ch->pos-(sc->soundoffset<<PITCHSHIFT);\n\n\t//input is +/- 1, output is +/- 32767. ch->vol is 0-255, so we just need to scale up by an extra 128\n\tvol[0] = ch->vol[0]*128;\n\tvol[1] = ch->vol[1]*128;\n\tvol[2] = ch->vol[2]*128;\n\tvol[3] = ch->vol[3]*128;\n\tvol[4] = ch->vol[4]*128;\n\tvol[5] = ch->vol[5]*128;\n\tvol[6] = ch->vol[6]*128;\n\tvol[7] = ch->vol[7]*128;\n\n\tif (rate != (1<<PITCHSHIFT))\n\t{\n\t\tfloat data;\n\t\tsfx = (float *fte_restrict)sc->data;\n\t\tfor (i=0 ; i<count ; i++)\n\t\t{\n\t\t\tdata = sfx[pos>>PITCHSHIFT];\n\t\t\tpos += rate;\n\t\t\tpaintbuffer[i].s[0] += (vol[0] * data);\n\t\t\tpaintbuffer[i].s[1] += (vol[1] * data);\n\t\t\tpaintbuffer[i].s[2] += (vol[2] * data);\n\t\t\tpaintbuffer[i].s[3] += (vol[3] * data);\n\t\t\tpaintbuffer[i].s[4] += (vol[4] * data);\n\t\t\tpaintbuffer[i].s[5] += (vol[5] * data);\n\t\t\tpaintbuffer[i].s[6] += (vol[6] * data);\n\t\t\tpaintbuffer[i].s[7] += (vol[7] * data);\n\t\t}\n\t}\n\telse\n\t{\n\t\tsfx = (float *fte_restrict)sc->data + (pos>>PITCHSHIFT);\n\t\tfor (i=0 ; i<count ; i++)\n\t\t{\n\t\t\tpaintbuffer[i].s[0] += (sfx[i] * vol[0]);\n\t\t\tpaintbuffer[i].s[1] += (sfx[i] * vol[1]);\n\t\t\tpaintbuffer[i].s[2] += (sfx[i] * vol[2]);\n\t\t\tpaintbuffer[i].s[3] += (sfx[i] * vol[3]);\n\t\t\tpaintbuffer[i].s[4] += (sfx[i] * vol[4]);\n\t\t\tpaintbuffer[i].s[5] += (sfx[i] * vol[5]);\n\t\t\tpaintbuffer[i].s[6] += (sfx[i] * vol[6]);\n\t\t\tpaintbuffer[i].s[7] += (sfx[i] * vol[7]);\n\t\t}\n\t}\n}\n#endif\n#endif\n"
  },
  {
    "path": "engine/client/snd_morphos.c",
    "content": "/*\nCopyright (C) 2006-2007 Mark Olsen\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  \n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n*/\n\n#include <exec/exec.h>\n#include <devices/ahi.h>\n#include <proto/exec.h>\n#define USE_INLINE_STDARG\n#include <proto/ahi.h>\n\n#include \"quakedef.h\"\n\n#warning Remove this once Spike fixes the sound input code.\nvoid S_UpdateCapture(void)\n{\n}\n\nstruct AHIChannelInfo\n{\n\tstruct AHIEffChannelInfo aeci;\n\tULONG offset;\n};\n\nstruct AHIdata\n{\n\tstruct MsgPort *msgport;\n\tstruct AHIRequest *ahireq;\n\tint ahiopen;\n\tstruct AHIAudioCtrl *audioctrl;\n\tvoid *samplebuffer;\n\tstruct Hook EffectHook;\n\tstruct AHIChannelInfo aci;\n\tunsigned int readpos;\n};\n\nULONG EffectFunc()\n{\n\tstruct Hook *hook = (struct Hook *)REG_A0;\n\tstruct AHIEffChannelInfo *aeci = (struct AHIEffChannelInfo *)REG_A1;\n\n\tstruct AHIdata *ad;\n\n\tad = hook->h_Data;\n\n\tad->readpos = aeci->ahieci_Offset[0];\n\n\treturn 0;\n}\n\nstatic struct EmulLibEntry EffectFunc_Gate =\n{\n\tTRAP_LIB, 0, (void (*)(void))EffectFunc\n};\n\nstatic void AHI_Shutdown(soundcardinfo_t *sc)\n{\n\tstruct AHIdata *ad;\n\n\tstruct Library *AHIBase;\n\n\tad = sc->handle;\n\n\tAHIBase = (struct Library *)ad->ahireq->ahir_Std.io_Device;\n\n\tad->aci.aeci.ahie_Effect = AHIET_CHANNELINFO|AHIET_CANCEL;\n\tAHI_SetEffect(&ad->aci.aeci, ad->audioctrl);\n\tAHI_ControlAudio(ad->audioctrl,\n\t                 AHIC_Play, FALSE,\n\t                 TAG_END);\n\n\tAHI_FreeAudio(ad->audioctrl);\n\tFreeVec(ad->samplebuffer);\n\tCloseDevice((struct IORequest *)ad->ahireq);\n\tDeleteIORequest((struct IORequest *)ad->ahireq);\n\tDeleteMsgPort(ad->msgport);\n\tFreeVec(ad);\n}\n\nstatic unsigned int AHI_GetDMAPos(soundcardinfo_t *sc)\n{\n\tstruct AHIdata *ad;\n\n\tad = sc->handle;\n\n\tsc->sn.samplepos = ad->readpos*sc->sn.numchannels;\n\n\treturn sc->sn.samplepos;\n}\n\nstatic void AHI_UnlockBuffer(soundcardinfo_t *sc, void *buffer)\n{\n}\n\nstatic void *AHI_LockBuffer(soundcardinfo_t *sc, unsigned int *sampidx)\n{\n\treturn sc->sn.buffer;\n}\n\nstatic void AHI_Submit(soundcardinfo_t *sc)\n{\n}\n\nstatic qboolean AHI_InitCard(soundcardinfo_t *sc, const char *cardname)\n{\n\tstruct AHIdata *ad;\n\n\tULONG channels;\n\tULONG speed;\n\tULONG bits;\n\n\tULONG r;\n\n\tstruct Library *AHIBase;\n\n\tstruct AHISampleInfo sample;\n\n\tif (cardname && *cardname)\n\t\treturn false; /* only allow the default audio device */\n\n\tad = AllocVec(sizeof(*ad), MEMF_ANY);\n\tif (ad)\n\t{\n\t\tad->msgport = CreateMsgPort();\n\t\tif (ad->msgport)\n\t\t{\n\t\t\tad->ahireq = (struct AHIRequest *)CreateIORequest(ad->msgport, sizeof(struct AHIRequest));\n\t\t\tif (ad->ahireq)\n\t\t\t{\n\t\t\t\tad->ahiopen = !OpenDevice(\"ahi.device\", AHI_NO_UNIT, (struct IORequest *)ad->ahireq, 0);\n\t\t\t\tif (ad->ahiopen)\n\t\t\t\t{\n\t\t\t\t\tAHIBase = (struct Library *)ad->ahireq->ahir_Std.io_Device;\n\n\t\t\t\t\tad->audioctrl = AHI_AllocAudio(AHIA_AudioID, AHI_DEFAULT_ID,\n\t\t\t\t\t                               AHIA_MixFreq, sc->sn.speed,\n\t\t\t\t\t                               AHIA_Channels, 1,\n\t\t\t\t\t                               AHIA_Sounds, 1,\n\t\t\t\t\t                               TAG_END);\n\n\t\t\t\t\tif (ad->audioctrl)\n\t\t\t\t\t{\n\t\t\t\t\t\tAHI_GetAudioAttrs(AHI_INVALID_ID, ad->audioctrl,\n\t\t\t\t\t\t                  AHIDB_BufferLen, sizeof(sc->name),\n\t\t\t\t\t\t                  AHIDB_Name, (ULONG)sc->name,\n\t\t\t\t\t\t                  AHIDB_MaxChannels, (ULONG)&channels,\n\t\t\t\t\t\t                  AHIDB_Bits, (ULONG)&bits,\n\t\t\t\t\t\t                  TAG_END);\n\n\t\t\t\t\t\tAHI_ControlAudio(ad->audioctrl,\n\t\t\t\t\t\t                 AHIC_MixFreq_Query, (ULONG)&speed,\n\t\t\t\t\t\t                 TAG_END);\n\n\t\t\t\t\t\tif (bits == 8 || bits == 16)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (channels > 2)\n\t\t\t\t\t\t\t\tchannels = 2;\n\n\t\t\t\t\t\t\tsc->sn.speed = speed;\n\t\t\t\t\t\t\tsc->sn.samplebytes = bits/8;\n\t\t\t\t\t\t\tsc->sn.numchannels = channels;\n\t\t\t\t\t\t\tsc->sn.samples = speed*channels;\n\n\t\t\t\t\t\t\tad->samplebuffer = AllocVec(speed*(bits/8)*channels, MEMF_ANY);\n\t\t\t\t\t\t\tif (ad->samplebuffer)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tsc->sn.buffer = ad->samplebuffer;\n\n\t\t\t\t\t\t\t\tif (channels == 1)\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tif (bits == 8)\n\t\t\t\t\t\t\t\t\t\tsample.ahisi_Type = AHIST_M8S;\n\t\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t\t\tsample.ahisi_Type = AHIST_M16S;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tif (bits == 8)\n\t\t\t\t\t\t\t\t\t\tsample.ahisi_Type = AHIST_S8S;\n\t\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t\t\tsample.ahisi_Type = AHIST_S16S;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tsc->sn.sampleformat = (bits==8)?QSF_S8:QSF_S16;\n\n\t\t\t\t\t\t\t\tsample.ahisi_Address = ad->samplebuffer;\n\t\t\t\t\t\t\t\tsample.ahisi_Length = (speed*(bits/8)*channels)/AHI_SampleFrameSize(sample.ahisi_Type);\n\n\t\t\t\t\t\t\t\tr = AHI_LoadSound(0, AHIST_DYNAMICSAMPLE, &sample, ad->audioctrl);\n\t\t\t\t\t\t\t\tif (r == 0)\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tr = AHI_ControlAudio(ad->audioctrl,\n\t\t\t\t\t\t\t\t\t                     AHIC_Play, TRUE,\n\t\t\t\t\t\t\t\t\t                     TAG_END);\n\n\t\t\t\t\t\t\t\t\tif (r == 0)\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tAHI_Play(ad->audioctrl,\n\t\t\t\t\t\t\t\t\t\t         AHIP_BeginChannel, 0,\n\t\t\t\t\t\t\t\t\t\t         AHIP_Freq, speed,\n\t\t\t\t\t\t\t\t\t\t         AHIP_Vol, 0x10000,\n\t\t\t\t\t\t\t\t\t\t         AHIP_Pan, 0x8000,\n\t\t\t\t\t\t\t\t\t\t         AHIP_Sound, 0,\n\t\t\t\t\t\t\t\t\t\t         AHIP_EndChannel, NULL,\n\t\t\t\t\t\t\t\t\t\t         TAG_END);\n\n\t\t\t\t\t\t\t\t\t\tad->aci.aeci.ahie_Effect = AHIET_CHANNELINFO;\n\t\t\t\t\t\t\t\t\t\tad->aci.aeci.ahieci_Func = &ad->EffectHook;\n\t\t\t\t\t\t\t\t\t\tad->aci.aeci.ahieci_Channels = 1;\n\n\t\t\t\t\t\t\t\t\t\tad->EffectHook.h_Entry = (void *)&EffectFunc_Gate;\n\t\t\t\t\t\t\t\t\t\tad->EffectHook.h_Data = ad;\n\n\t\t\t\t\t\t\t\t\t\tAHI_SetEffect(&ad->aci, ad->audioctrl);\n\n\t\t\t\t\t\t\t\t\t\tsc->handle = ad;\n\n\t\t\t\t\t\t\t\t\t\tsc->Lock = AHI_LockBuffer;\n\t\t\t\t\t\t\t\t\t\tsc->Unlock = AHI_UnlockBuffer;\n\t\t\t\t\t\t\t\t\t\tsc->Submit = AHI_Submit;\n\t\t\t\t\t\t\t\t\t\tsc->Shutdown = AHI_Shutdown;\n\t\t\t\t\t\t\t\t\t\tsc->GetDMAPos = AHI_GetDMAPos;\n\n\t\t\t\t\t\t\t\t\t\tCon_Printf(\"Using AHI mode \\\"%s\\\" for audio output\\n\", sc->name);\n\t\t\t\t\t\t\t\t\t\tCon_Printf(\"Channels: %d bits: %d frequency: %d\\n\", channels, bits, speed);\n\n\t\t\t\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tFreeVec(ad->samplebuffer);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tAHI_FreeAudio(ad->audioctrl);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\tCon_Printf(\"Failed to allocate AHI audio\\n\");\n\n\t\t\t\t\tCloseDevice((struct IORequest *)ad->ahireq);\n\t\t\t\t}\n\t\t\t\tDeleteIORequest((struct IORequest *)ad->ahireq);\n\t\t\t}\n\t\t\tDeleteMsgPort(ad->msgport);\n\t\t}\n\t\tFreeVec(ad);\n\t}\n\n\treturn false;\n}\n\nsounddriver_t AHI_AudioOutput =\n{\n\t\"AHI\",\n\tAHI_InitCard,\n\tNULL\n};\n"
  },
  {
    "path": "engine/client/snd_mp3.c",
    "content": "//source file by david walton, derived from minimad.c\n//modified slightly to use new library functions instead for single threaded asyncronous mp3 sound.\n//in Quake!\n\n//Be aware that MP3s are patented and thus not GPLable. This file is an interface to madlib which contains the decoder.\n//This file is excluded from the main source tree because of this, and madlib is not distributed - in binary or source form.\n//The GPL disallows distribution of patented software. You may reactive, but do not distribute binaries.\n\n//Really madlib should come with the exception of the mp3 patent.\n\n#include \"quakedef.h\"\n\n#ifdef AVAIL_MP3\n\n#include \"winquake.h\"\n#undef channels\n\n\n#ifdef _WIN32\n#define inline _inline\n#endif\n\n//the problem with this is that we store the entire decoded file in memory.\n//this takes a lot of mem.\n\n//uses madlib\n\n\n\n/*\n * mad - MPEG audio decoder\n * Copyright (C) 2000-2001 Robert Leslie\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\n *\n */\n\n# include \"libmad/mad.h\"\n#define USE_MADLIB\n#include \"mymad.c\"\n\n/*\n * This is perhaps the simplest example use of the MAD high-level API.\n * Standard input is mapped into memory via mmap(), then the high-level API\n * is invoked with three callbacks: input, output, and error. The output\n * callback converts MAD's high-resolution PCM samples to 16 bits, then\n * writes them to standard output in little-endian, stereo-interleaved\n * format.\n */\n\n\n\n/*\n * This is a private message structure. A generic pointer to this structure\n * is passed to each of the callback functions. Put here any data you need\n * to access from within the callbacks.\n */\n\ntypedef struct {\n\tunsigned char *start;\n\tunsigned long length;\n\tqboolean failed;\n\n\tsfxcache_t\tmp3sc;\n\tchar *mp3aswavdata;\t\n\tint mp3aswavpos;\n\tint mp3aswavbuflen;\n\n\tstruct mad_decoder decoder;\n\n\tsfx_t *s;\n} decoderbuffer_t;\n\nint mymad_run(struct mad_decoder *decoder);\nint mymad_reset(struct mad_decoder *decoder);\nvoid mymad_finish(struct mad_decoder *decoder);\nstatic int startdecode(unsigned char *start, unsigned long length, decoderbuffer_t *buffer);\n\n#define BUFFERSIZEINC (2*1024*1024)\n\nint DecodeSomeMP3(sfx_t *s, int minlength);\nqbyte *COM_LoadFile (char *path, int usehunk);\nint MP3_decode(unsigned char *start, unsigned long length, decoderbuffer_t *buffer);\nvoid CancelDecoder(sfx_t *s);\nsfxcache_t *S_LoadMP3Sound (sfx_t *s)\n{\n\tchar\tnamebuffer[MAX_OSPATH];\n\tchar\t*name;\n\tdecoderbuffer_t *buffer;\n\n\tchar *data;\n\tqboolean telluser;\n\tint len;\n\n\tname = s->name;\n\n\tif (name[0] == '#')\n\t\tstrcpy(namebuffer, &name[1]);\n\telse\n\t\tQ_snprintfz (namebuffer, sizeof(namebuffer), \"sound/%s\", name);\n\n\tlen = strlen(namebuffer);\n\ttelluser = strcmp(namebuffer+len-4, \".wav\");\n\tif (!telluser)\n\t\tstrcpy(namebuffer+len-4, \".mp3\");\t\n\n\t//try opening from a quake path\n\tdata = COM_LoadFile(namebuffer, 5);\n\tif (!data)\n\t{//if that didn't work, try opening direct from exe - this is media after all.\n\t\tFILE *f;\n\n\t\tif (!telluser)\n\t\t\treturn NULL;\t//never go out of the quake path for a wav replacement.\n#ifndef _WIN32\n\t\tchar unixname[128];\n\t\tif (name[1] == ':' && name[2] == '\\\\')\t//convert from windows to a suitable alternative.\n\t\t{\t\t\t\n\t\t\tsnprintf(unixname, sizeof(unixname), \"/mnt/%c/%s\", name[0]-'A'+'a', name+3);\n\t\t\tname = unixname;\n\t\t\twhile (*name)\n\t\t\t{\n\t\t\t\tif (*name == '\\\\')\n\t\t\t\t\t*name = '/';\n\t\t\t\tname++;\n\t\t\t}\t\t\t\n\t\t\tname = unixname;\t\t\t\n\t\t}\n#endif\n\t\tif ((f = fopen(name, \"rb\")))\n\t\t{\n\t\t\tcom_filesize = COM_filelength(f);\n\t\t\tdata = BZ_Malloc(com_filesize);\n\t\t\tfread(data, 1, com_filesize, f);\n\t\t\tfclose(f);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tCon_SafePrintf (\"Couldn't load %s\\n\", namebuffer);\n\t\t\treturn NULL;\n\t\t}\n\t}\n\n\tif (s->decoder)\n\t\tSys_Error(\"Decoding already decoding file\\n\");\n\ts->decoder = Z_Malloc(sizeof(decoderbuffer_t) + sizeof(sfxdecode_t));\n\tbuffer = (decoderbuffer_t*)(s->decoder+1);\n\n\tbuffer->mp3aswavpos=0;\n\tbuffer->mp3sc.length=0;\n\tbuffer->s = s;\n\ts->decoder->buf = buffer;\n\ts->decoder->decodemore = DecodeSomeMP3;\n\ts->decoder->abort = CancelDecoder;\n\n\tif (!startdecode(data, com_filesize, buffer))\n\t{\n\t\tif (buffer->mp3aswavdata)\n\t\t{\n\t\t\tBZ_Free(buffer->mp3aswavdata);\n\n\t\t\tbuffer->mp3aswavdata=NULL;\n\t\t}\n\t\tCon_SafePrintf (\"Couldn't load %s - corrupt.\\n\", namebuffer);\n\t\treturn NULL;\n\t}\n\n\ts->decoder->decodemore(s, 100);\n\tif (!s->decoder)\t//wow, short file. :/\n\t\treturn s->cache.data;\n\n\ts->cache.fake=true;\n\treturn buffer->s->cache.data;\n}\n\n\n/*\n * This is the input callback. The purpose of this callback is to (re)fill\n * the stream buffer which is to be decoded. In this example, an entire file\n * has been mapped into memory, so we just call mad_stream_buffer() with the\n * address and length of the mapping. When this callback is called a second\n * time, we are finished decoding.\n */\n\nstatic\nenum mad_flow input(void *data,\n\t\t    struct mad_stream *stream)\n{\n  decoderbuffer_t *buffer = data;\n\n  if (!buffer->length)\n    return MAD_FLOW_STOP;\n\n  mad_stream_buffer(stream, buffer->start, buffer->length);\n\n  buffer->length = 0;\n\n  return MAD_FLOW_CONTINUE;\n}\n\n/*\n * The following utility routine performs simple rounding, clipping, and\n * scaling of MAD's high-resolution samples down to 16 bits. It does not\n * perform any dithering or noise shaping, which would be recommended to\n * obtain any exceptional audio quality. It is therefore not recommended to\n * use this routine if high-quality output is desired.\n */\n\nstatic inline\nsigned int scale(mad_fixed_t sample)\n{\n  /* round */\n  sample += (1L << (MAD_F_FRACBITS - 16));\n\n  /* clip */\n  if (sample >= MAD_F_ONE)\n    sample = MAD_F_ONE - 1;\n  else if (sample < -MAD_F_ONE)\n    sample = -MAD_F_ONE;\n\n  /* quantize */\n  return sample >> (MAD_F_FRACBITS + 1 - 16);\n}\n\n/*\n * This is the output callback function. It is called after each frame of\n * MPEG audio data has been completely decoded. The purpose of this callback\n * is to output (or play) the decoded PCM audio.\n */\n\nstatic\nenum mad_flow output(void *data,\n\t\t\tstruct mad_header const *header,\n\t\t\tstruct mad_pcm *pcm)\n{\n\tdecoderbuffer_t *buffer = data;\n\tsfxcache_t *cache;\n\n\tunsigned int nchannels, nsamples;\n\tmad_fixed_t const *left_ch, *right_ch;\n\n\tchar *outpos;\n\tsigned int sample;\n\tfloat speedfactor;\n\n\tspeedfactor\t= (float)snd_speed/buffer->mp3sc.speed;\n\n  /* pcm->samplerate contains the sampling frequency */\n\n\tnchannels = pcm->channels;\n\tnsamples  = pcm->length*speedfactor;\n\tleft_ch   = pcm->samples[0];\n\tright_ch  = pcm->samples[1];\n\n\tif (buffer->mp3aswavpos + nsamples*nchannels*sizeof(short) > buffer->mp3aswavbuflen || !buffer->mp3aswavdata)\n\t{\n\t\tint\t  newsize = buffer->mp3aswavpos + nsamples*nchannels*sizeof(short) + BUFFERSIZEINC+sizeof(sfxcache_t);\n\t\tchar *newbuf = BZ_Malloc(newsize);\n\t\tif (!newbuf)\n\t\t\treturn MAD_FLOW_STOP;\t//damn and blast!\n\n\t\tif (buffer->mp3aswavdata)\t//if we had an old buffer, fill the new one and free the old\n\t\t{\n//\t\t\tmemset(newbuf, 0, newsize);\n\t\t\tmemcpy(newbuf, buffer->mp3aswavdata, buffer->mp3aswavpos+sizeof(sfxcache_t));\n//\t\t\tnewbuf[newsize] = '\\0';\n\t\t\tBZ_Free(buffer->mp3aswavdata);\n\t\t}\n\t\tbuffer->mp3aswavdata = newbuf;\n\t\tbuffer->mp3aswavbuflen = newsize;\n\n\t\tbuffer->s->cache.data = buffer->mp3aswavdata;\n\t}\n\n\tcache = (sfxcache_t *)buffer->mp3aswavdata;\n\toutpos = buffer->mp3aswavdata+sizeof(sfxcache_t) + buffer->mp3aswavpos;\n\n\tif (nchannels == 1)\n\t{\n\t\tif (speedfactor==1)\t//fast\n\t\t{\n\t\t\twhile (nsamples--)\n\t\t\t{\n\t\t\t\tsample = scale(*left_ch++);\n\t\t\t\t*outpos++ = ((sample >> 0) & 0xff);\n\t\t\t\t*outpos++ = ((sample >> 8) & 0xff);\t\t\t\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tint src=0;\n\t\t\tint pos=0;\t\t\t\n\n\t\t\twhile (pos < nsamples)\n\t\t\t{\n\t\t\t\tsrc = pos/speedfactor;\n\t\t\t\tsample = scale(left_ch[src]);\n\t\t\t\t*outpos++ = (((sample >> 0) & 0xff));\n\t\t\t\t*outpos++ = (((sample >> 8) & 0xff));\n\n\t\t\t\tpos++;\n\t\t\t}\n\t\t}\n\t\tcache->stereo = 0;\n\t}\n\telse\n\t{\n\t\tif (speedfactor==1)\t//fast\n\t\t{\n\t\t\twhile (nsamples--)\n\t\t\t{\t//merge channels?\n\t\t\t\tsample = scale(*left_ch++);\n\t\t\t\t*outpos++ = (((sample >> 0) & 0xff));\n\t\t\t\t*outpos++ = (((sample >> 8) & 0xff));\n\n\t\t\t\tsample = scale(*right_ch++)/2;\n\t\t\t\t*outpos++ = (((sample >> 0) & 0xff));\n\t\t\t\t*outpos++ = (((sample >> 8) & 0xff));\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tint src=0;\n\t\t\tint pos=0;\t\t\t\n\n\t\t\twhile (pos < nsamples)\n\t\t\t{\n\t\t\t\tsrc = pos/speedfactor;\n\t\t\t\tsample = scale(left_ch[src]);\n\t\t\t\t*outpos++ = (((sample >> 0) & 0xff));\n\t\t\t\t*outpos++ = (((sample >> 8) & 0xff));\n\n\t\t\t\tsample = scale(right_ch[src]);\n\t\t\t\t*outpos++ = (((sample >> 0) & 0xff));\n\t\t\t\t*outpos++ = (((sample >> 8) & 0xff));\n\n\t\t\t\tpos++;\n\t\t\t}\n\t\t}\n\t\tcache->stereo = 1;\n\t}\n\n\tbuffer->mp3aswavpos += pcm->length*speedfactor*sizeof(short)*nchannels;\n\tbuffer->mp3sc.length+= pcm->length*speedfactor;\n\n\tcache->speed = snd_speed;\n\tcache->length = buffer->s->decoder->decodedlen = buffer->mp3sc.length;\n\tcache->loopstart = -1;//buffer->mp3sc.loopstart;\n\tcache->width = buffer->mp3sc.width;\t\n\n\treturn MAD_FLOW_CONTINUE;\n}\n\n/*\n * This is the error callback function. It is called whenever a decoding\n * error occurs. The error is indicated by stream->error; the list of\n * possible MAD_ERROR_* errors can be found in the mad.h (or\n * libmad/stream.h) header file.\n */\n\nstatic\nenum mad_flow error(void *data,\n\t\t    struct mad_stream *stream,\n\t\t    struct mad_frame *frame)\n{\n  decoderbuffer_t *buffer = data;\n\n  if (stream->error == 257)\n\t  return MAD_FLOW_IGNORE;\n\n  if (developer.value)\n\tCon_SafePrintf(\"MP3Error: decoding error 0x%04x (%s) at byte offset %u\\n\",\n\t  stream->error, mad_stream_errorstr(stream),\n\t  stream->this_frame - buffer->start);\n\n  buffer->failed = true;\n\n  return MAD_FLOW_IGNORE;\n}\n\nstatic enum mad_flow header(void *data,\n\t\t\t\t\t\t   struct mad_header const *header)\n{\n\tdecoderbuffer_t *buffer = data;\n\n\tbuffer->mp3sc.speed = header->samplerate;\n\tbuffer->mp3sc.width = 2;\n\treturn MAD_FLOW_CONTINUE;\n}\n\nvoid CancelDecoder(sfx_t *s)\n{\n\tdecoderbuffer_t *dec = s->decoder->buf;\n\n\tmymad_finish(&dec->decoder);\n\tmad_decoder_finish(&dec->decoder);\n\n\ts->cache.fake = false;\t//give it a true cache now, and hope that we don't need to free it while it's still playing.\n\ts->cache.data=NULL;\n\n\tBZ_Free(dec->mp3aswavdata);\n\tif (dec->start)\n\t\tBZ_Free(dec->start);\n\n\tZ_Free(s->decoder);\n\ts->decoder = NULL;\t\n}\n\n/*\n * This is the function called by main() above to perform all the\n * decoding. It instantiates a decoder object and configures it with the\n * input, output, and error callback functions above. A single call to\n * mad_decoder_run() continues until a callback function returns\n * MAD_FLOW_STOP (to stop decoding) or MAD_FLOW_BREAK (to stop decoding and\n * signal an error).\n */\n\nint DecodeSomeMP3(sfx_t *s, int minlength)\n{\t\n\tdecoderbuffer_t *dec = s->decoder->buf;\n\n//\tint oldlen;\n\n\tif (!dec->start)\n\t\treturn 1;\n\n\twhile(dec->mp3sc.length <= minlength)\n\t{\n\t\tif (!mymad_run(&dec->decoder) || dec->failed)\n\t\t{\n\t\t\tif (dec->mp3aswavbuflen>441000)//10 secs at highest quality\n\t\t\t{\t//big fat heffers of mp3s are kept like this and are flushed when they finish playing fully.\n\t\t\t\tBZ_Free(dec->start);\n\t\t\t\tdec->start = NULL;\n\t\t\t}\n\t\t\telse\n\t\t\t{\t//small mp3 files are now treated like wavs\n\t\t\t\tvoid *newmem;\n\t\t\t\tmymad_finish(&dec->decoder);\n\t\t\t\tmad_decoder_finish(&dec->decoder);\n\n\t\t\t\ts->cache.fake = false;\t//give it a true cache now, and hope that we don't need to free it while it's still playing.\n\t\t\t\ts->cache.data=NULL;\n\t\t\t\tif (dec->mp3aswavdata)\n\t\t\t\t{\n\t\t\t\t\tnewmem = Cache_Alloc(&s->cache, dec->mp3aswavbuflen+sizeof(sfxcache_t), s->name);\n\t\t\t\t\tmemcpy(newmem, dec->mp3aswavdata, dec->mp3aswavbuflen+sizeof(sfxcache_t));\n\t\t\t\t\tBZ_Free(dec->mp3aswavdata);\n\t\t\t\t}\n\t\t\t\tBZ_Free(dec->start);\n\n\t\t\t\tZ_Free(s->decoder);\n\t\t\t\ts->decoder = NULL;\n\t\t\t}\n\t\t\treturn 1;\n\t\t}\n\t}\n\treturn 0;\n}\n\nint mymad_decoder_run(struct mad_decoder *decoder, enum mad_decoder_mode mode);\nstatic\nint startdecode(unsigned char *start, unsigned long length, decoderbuffer_t *buffer)\n{\n  /* initialize our private message structure */\n\n  buffer->start  = start;\n  buffer->length = length;\n  buffer->failed = false;\n\n  /* configure input, output, and error functions */\n\n  mad_decoder_init(&buffer->decoder, buffer,\n\t\t   input, header, 0 /* filter */, output,\n\t\t   error, 0 /* message */);\n\n  /* start decoding */\n\n  mymad_reset(&buffer->decoder);\n\n  if (buffer->failed)\n\t  return false;\n\n  return true;\n}\n#endif\n\n\n\n"
  },
  {
    "path": "engine/client/snd_opensl.c",
    "content": "#include \"quakedef.h\"\n\n#include <SLES/OpenSLES.h>\n\n#define AUDIODRIVERNAME \"OpenSLES\"\n\nstatic void QDECL OSL_RegisterCvars(void)\n{\n}\n\nstatic void *OSL_Lock(soundcardinfo_t *sc, unsigned int *startoffset)\n{\n\treturn sc->sn.buffer;\n}\nstatic void OSL_Unlock(soundcardinfo_t *sc, void *buffer)\n{\n\t//no need to do anything\n}\nstatic void OSL_Submit(soundcardinfo_t *sc, int start, int end)\n{\n\t//submit happens outside the mixer\n}\nstatic unsigned int OSL_GetDMAPos(soundcardinfo_t *sc)\n{\n\tsc->sn.samplepos = sc->snd_sent;\n\treturn sc->sn.samplepos;\n}\n\ntypedef struct \n{\n\tSLObjectItf sl;\n\tSLEngineItf engine;\n\tSLPlayItf play;\n\tSLObjectItf player;\n\tSLObjectItf output;\n\tSLBufferQueueItf bufferqueue;\n\n\n\tunsigned char *buffer;\n\tsize_t buffersegmentsize;\n\tsize_t buffersegments;\n\tsize_t buffersegment;\n} osl_data_t;\n\n//assumption: that each buffer is released in sequence.\nstatic void buffercallback(SLBufferQueueItf queue, void *ctx)\n{\n\tsoundcardinfo_t *sc = ctx;\n\tosl_data_t *p = sc->handle;\n\t//we got the buffer back.\n\tif (sc->Shutdown)\n\t{\t//we're not shutting down yet, so paint more stuff into the buffer and throw it back.\n\t\tsc->sn.buffer = p->buffer + (p->buffersegment%p->buffersegments)*p->buffersegmentsize;\n\t\tsc->sn.samples = p->buffersegmentsize/sc->sn.samplebytes;//numFramesAvailable * sc->sn.numchannels;\n\t\tsc->samplequeue = sc->sn.samples;\n\t\tS_MixerThread(sc);\n\t\tsc->snd_sent = p->buffersegment++*p->buffersegmentsize;\n\n\t\t(*queue)->Enqueue(queue, sc->sn.buffer, p->buffersegmentsize);\n\t}\n}\n\nstatic void OSL_Shutdown(soundcardinfo_t *sc)\n{\n\tosl_data_t *p = sc->handle;\n\tsc->Shutdown = NULL;\t//stop posting new buffers\n\tif (p)\n\t{\n\t\tif (p->play)\n\t\t\t(*p->play)->SetPlayState(p->play, SL_PLAYSTATE_STOPPED);\n\t\tif (p->player)\n\t\t\t(*p->player)->Destroy(p->player);\n\t\tif (p->output)\n\t\t\t(*p->output)->Destroy(p->output);\n\t\tif (p->sl)\n\t\t\t(*p->sl)->Destroy(p->sl);\n\t\tsc->handle = NULL;\n\t\tZ_Free(p);\n\t}\n}\n\nstatic qboolean QDECL OSL_InitCard (soundcardinfo_t *sc, const char *cardname)\n{\n\tosl_data_t *p;\n\tsize_t segments = 4;\t\t//lets cycle through 4 segments in our single logical buffer\n\tsize_t segmentframes = 256;\t//with X frames per segment\n\t\t\t\t\t//FIXME: mixahead\n\n\tsc->sn.numchannels = 2;\n\tsc->sn.sampleformat = QSF_S16;\n\tswitch(sc->sn.sampleformat)\n\t{\n\tcase QSF_U8:\n\t//case QSF_S8;\n\t\tsc->sn.samplebytes = 1;\n\t\tbreak;\n\tcase QSF_S16:\n\t\tsc->sn.samplebytes = 2;\n\t\tbreak;\n\t//case QSF_F32:\n\t\t//sc->sn.samplebytes = 4;\n\t\t//break;\n\tdefault:\n\t\treturn false;\n//\t\tsc->sn.sampleformat = QSF_INVALID;\n//\t\tsc->sn.samplebytes = 0;\n//\t\tbreak;\n\t}\n\n\tCon_DPrintf(\"Opening OpenSLES, %.1fkhz\\n\", sc->sn.speed/1000.0);\n\n\tsc->Shutdown\t= OSL_Shutdown;\n\tsc->Lock\t\t= OSL_Lock;\n\tsc->Unlock\t\t= OSL_Unlock;\n\tsc->Submit\t\t= OSL_Submit;\n\tsc->GetDMAPos\t= OSL_GetDMAPos;\n\tsc->handle = p = Z_Malloc(sizeof(*p) + segmentframes*segments*sc->sn.samplebytes*sc->sn.numchannels);\n\tif (p)\n\t{\n\t\tp->buffer = (unsigned char*)(p+1);\n\t\tp->buffersegments = segments;\n\t\tp->buffersegmentsize = segmentframes*sc->sn.samplebytes*sc->sn.numchannels;\n\t\tsc->selfpainting = true;\n\t\tQ_strncpyz(sc->name, cardname?cardname:\"\", sizeof(sc->name));\n\n\t\tSLEngineOption options[] = \n\t\t{\n\t\t\t{(SLuint32) SL_ENGINEOPTION_THREADSAFE, (SLuint32)SL_BOOLEAN_TRUE},\n\t//\t\t{(SLuint32) SL_ENGINEOPTION_MAJORVERSION, (SLuint32)1},\n\t//\t\t{(SLuint32) SL_ENGINEOPTION_MINORVERSION, (SLuint32)1},\n\t\t};\n\t\tslCreateEngine(&p->sl, countof(options), options, 0, NULL, NULL);\n\t\tif (p->sl)\n\t\t{\n\t\t\t(*p->sl)->Realize(p->sl, SL_BOOLEAN_FALSE);\n\t\t\t(*p->sl)->GetInterface(p->sl, SL_IID_ENGINE, &p->engine);\n\t\t\tif (p->engine)\n\t\t\t{\n\t\t\t\t(*p->engine)->CreateOutputMix(p->engine, &p->output, 0, NULL, NULL);\n\t\t\t\tif (p->output)\n\t\t\t\t{\n\t\t\t\t\tSLboolean TRUE_ = true;\n\t\t\t\t\tSLDataLocator_BufferQueue loc_bufferqueue = {SL_DATALOCATOR_BUFFERQUEUE, segments};\n\t\t\t\t\tSLDataFormat_PCM pcmformat = {SL_DATAFORMAT_PCM, sc->sn.numchannels, sc->sn.speed*1000, sc->sn.samplebytes*8, sc->sn.samplebytes*8/*+pad*/, SL_SPEAKER_FRONT_LEFT|SL_SPEAKER_FRONT_RIGHT, SL_BYTEORDER_LITTLEENDIAN};\n\t\t\t\t\tSLDataSource audiosource = {&loc_bufferqueue, &pcmformat};\n\t\t\t\t\tSLDataLocator_OutputMix loc_outputmix = {SL_DATALOCATOR_OUTPUTMIX, p->output};\n\t\t\t\t\tSLDataSink audiosink = {&loc_outputmix, NULL};\n\t\t\t\t\t(*p->output)->Realize(p->output, false);\n\t\t\t\t\t(*p->engine)->CreateAudioPlayer(p->engine, &p->player, &audiosource, &audiosink, 1, &SL_IID_BUFFERQUEUE, &TRUE_);\n\t\t\t\t\tif (p->player)\n\t\t\t\t\t{\n\t\t\t\t\t\t(*p->player)->Realize(p->player, false);\n\t\t\t\t\t\t(*p->player)->GetInterface(p->player, SL_IID_PLAY, &p->play);\n\t\t\t\t\t\t(*p->player)->GetInterface(p->player, SL_IID_BUFFERQUEUE, &p->bufferqueue);\n\t\t\t\t\t\tif (p->bufferqueue)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t(*p->bufferqueue)->RegisterCallback(p->bufferqueue, buffercallback, sc);\n\t\t\t\t\t\t\tbuffercallback(p->bufferqueue, sc);\n\t\t\t\t\t\t\tbuffercallback(p->bufferqueue, sc);\n\t\t\t\t\t\t\tbuffercallback(p->bufferqueue, sc);\n\t\t\t\t\t\t\tif (SL_RESULT_SUCCESS == (*p->play)->SetPlayState(p->play, SL_PLAYSTATE_PLAYING))\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t//should now be playing our buffers, and recycling them when one terminates.i\n\t\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tOSL_Shutdown(sc);\n\treturn false;\n}\n\nstatic qboolean QDECL OSL_Enumerate (void (QDECL *callback) (const char *drivername, const char *devicecode, const char *readablename))\n{\n\t//callback(AUDIODRIVERNAME, internalname, nicename);\n\treturn false;\n}\n\nsounddriver_t OSL_Output =\n{\n\tAUDIODRIVERNAME,\n\tOSL_InitCard,\n\tOSL_Enumerate,\n\tOSL_RegisterCvars\n};\n\n"
  },
  {
    "path": "engine/client/snd_ov.c",
    "content": "#include \"quakedef.h\"\n\n#ifdef AVAIL_OGGVORBIS\n#define OV_EXCLUDE_STATIC_CALLBACKS\n\n#ifdef __MORPHOS__\n\t#include <exec/exec.h>\n\t#include <libraries/vorbisfile.h>\n\n\t#include <proto/exec.h>\n\t#include <proto/vorbisfile.h>\n#else\n\t#include <vorbis/vorbisfile.h>\n#endif\n\n\n#ifdef LIBVORBISFILE_STATIC\n\t#define p_ov_open_callbacks ov_open_callbacks\n\t#define p_ov_clear ov_clear\n\t#define p_ov_info ov_info\n\t#define p_ov_comment ov_comment\n\t#define p_ov_pcm_total ov_pcm_total\n\t#define p_ov_time_total ov_time_total\n\t#define p_ov_read ov_read\n\t#define p_ov_pcm_seek ov_pcm_seek\n#else\n\t#if defined(__MORPHOS__)\n\n\t\t#define oggvorbislibrary VorbisFileBase\n\t\tstruct Library *VorbisFileBase;\n\n\t#else\n\t\tdllhandle_t *oggvorbislibrary;\n\t#endif\n\n\t#ifdef __MORPHOS__\n\t\t#define p_ov_open_callbacks(a, b, c, d, e) ov_open_callbacks(a, b, c, d, &e)\n\t\t#define p_ov_clear ov_clear\n\t\t#define p_ov_info ov_info\n\t\t#define p_ov_comment ov_comment\n\t\t#define p_ov_pcm_total ov_pcm_total\n\t\t#define p_ov_time_total ov_time_total\n\t\t#define p_ov_read ov_read\n\t\t#define p_ov_pcm_seek ov_pcm_seek\n\t#else\n\t\tint (VARGS *p_ov_open_callbacks) (void *datasource, OggVorbis_File *vf, char *initial, long ibytes, ov_callbacks callbacks);\n\t\tint (VARGS *p_ov_clear)(OggVorbis_File *vf);\n\t\tvorbis_info *(VARGS *p_ov_info)(OggVorbis_File *vf,int link);\n\t\tvorbis_comment *(VARGS *p_ov_comment) (OggVorbis_File *vf,int link);\n\t\togg_int64_t (VARGS *p_ov_pcm_total)(OggVorbis_File *vf,int i);\n\t\tdouble (VARGS *p_ov_time_total)(OggVorbis_File *vf,int i);\n\t\tlong (VARGS *p_ov_read)(OggVorbis_File *vf,char *buffer,int length,int bigendianp,int word,int sgned,int *bitstream);\n\t\tint (VARGS *p_ov_pcm_seek)(OggVorbis_File *vf,ogg_int64_t pos);\n\t#endif\n#endif\n\n\ntypedef struct {\n\tunsigned char *start;\t//file positions\n\tunsigned long length;\n\tunsigned long pos;\n\tint srcspeed;\n\tint srcchannels;\n\n\tqboolean nopurge;\n\tqboolean failed;\n\n\tchar *tempbuffer;\n\tint tempbufferbytes;\n\n\tchar *decodedbuffer;\n\tint decodedbufferbytes;\n\tint decodedbytestart;\n\tint decodedbytecount;\n\n\tquintptr_t pcmtotal;\n\tfloat timetotal;\n\tOggVorbis_File vf;\n\n\tsfx_t *s;\n} ovdecoderbuffer_t;\n\nstatic float QDECL OV_Query(struct sfx_s *sfx, struct sfxcache_s *buf, char *name, size_t namesize)\n{\n\tovdecoderbuffer_t *dec = sfx->decoder.buf;\n\tif (!dec)\n\t\treturn -1;\n\n//\tif (dec->pcmtotal == 0)\n//\t\tdec->pcmtotal = p_ov_pcm_total(&dec->vf, -1);\n//\tif (dec->timetotal < 0)\n//\t\tdec->timetotal = p_ov_time_total(&dec->vf, -1);\n\n\tif (buf)\n\t{\n\t\tbuf->data = NULL;\t//you're not meant to actually be using the data here\n\t\tbuf->soundoffset = 0;\n\t\tbuf->length = dec->pcmtotal;\n\t\tbuf->numchannels = dec->srcchannels;\n\t\tbuf->speed = dec->srcspeed;\n\t\tbuf->format = QAF_S16;\n\t}\n\tif (name)\n\t{\n\t\tvorbis_comment *c = p_ov_comment(&dec->vf, -1);\n\t\tint i;\n\t\tconst char *artist = NULL;\n\t\tconst char *title = NULL;\n\t\tfor (i = 0; i < c->comments; i++)\n\t\t{\n\t\t\tif (!strncmp(c->user_comments[i], \"ARTIST=\", 7))\n\t\t\t\tartist = c->user_comments[i]+7;\n\t\t\telse if (!strncmp(c->user_comments[i], \"TITLE=\", 6))\n\t\t\t\ttitle = c->user_comments[i]+6;\n\t\t}\n\n\t\tif (artist && title)\n\t\t\tQ_snprintfz(name, namesize, \"%s - %s\", artist, title);\n\t\telse if (title)\n\t\t\tQ_snprintfz(name, namesize, \"%s\", title);\n\t}\n\treturn dec->timetotal;\n}\n\nstatic sfxcache_t *QDECL OV_DecodeSome(struct sfx_s *sfx, struct sfxcache_s *buf, ssamplepos_t start, int length)\n{\n\textern int snd_speed;\n\textern cvar_t snd_linearresample_stream;\n\tint bigendianp = bigendian;\n\tint current_section = 0;\n\n\tovdecoderbuffer_t *dec = sfx->decoder.buf;\n\tint bytesread;\n\n\tint outspeed = snd_speed;\n\n//\tCon_Printf(\"Minlength = %03i   \", minlength);\n\n\tstart *= 2*dec->srcchannels;\n\tlength *= 2*dec->srcchannels;\n\n\tif (length)\n\t{\n\t\tif (start < dec->decodedbytestart)\n\t\t{\n\t//\t\tCon_Printf(\"Rewound to %i\\n\", start);\n\t\t\tdec->failed = false;\n\n\t\t\t//check pos\n\t\t\tif (p_ov_pcm_seek(&dec->vf, start * (dec->srcspeed/(2.0*dec->srcchannels*outspeed))) == 0)\n\t\t\t{\n\t\t\t\t/*something rewound, purge clear the buffer*/\n\t\t\t\tdec->decodedbytecount = 0;\n\t\t\t\tdec->decodedbytestart = start;\n\t\t\t}\n\t\t}\n\n\t/*\tif (start > dec->decodedbytestart + dec->decodedbytecount)\n\t\t{\n\t\t\tdec->decodedbytestart = start;\n\t\t\tp_ov_pcm_seek(&dec->vf, (dec->decodedbytestart * dec->srcspeed) / outspeed);\n\t\t}\n\t*/\n\t\tif (dec->decodedbytecount > outspeed*8 && !dec->nopurge)\n\t\t{\n\t\t\t/*everything is okay, but our buffer is getting needlessly large.\n\t\t\tkeep anything after the 'new' position, but discard all before that\n\t\t\ttrim shouldn't be able to go negative\n\t\t\t*/\n\t\t\tint trim = start - dec->decodedbytestart;\n\t\t\tif (trim < 0)\n\t\t\t{\n\t\t\t\tdec->decodedbytecount = 0;\n\t\t\t\tdec->decodedbytestart = start;\n\t//\t\t\tCon_Printf(\"trim < 0\\n\");\n\t\t\t}\n\t\t\telse if (trim > dec->decodedbytecount)\n\t\t\t{\n\t\t\t\tif (0==p_ov_pcm_seek(&dec->vf, start * (dec->srcspeed/(2.0*dec->srcchannels*outspeed))))\n\t\t\t\t{\n\t\t\t\t\tdec->decodedbytecount = 0;\n\t\t\t\t\tdec->decodedbytestart = start;\n\t\t\t\t}\n\t//\t\t\tCon_Printf(\"trim > count\\n\");\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t//\t\t\tCon_Printf(\"trim retain\\n\");\n\t\t\t\t//FIXME: retain an extra half-second for dual+ sound devices running slightly out of sync\n\t\t\t\tmemmove(dec->decodedbuffer, dec->decodedbuffer + trim, dec->decodedbytecount - trim);\n\t\t\t\tdec->decodedbytecount -= trim;\n\t\t\t\tdec->decodedbytestart += trim;\n\t\t\t}\n\t\t}\n\n\t\tfor (;;)\n\t\t{\n\t\t\tif (dec->failed || start+length <= dec->decodedbytestart + dec->decodedbytecount)\n\t\t\t\tbreak;\n\n\t\t\tif (dec->decodedbufferbytes < start+length - dec->decodedbytestart + 4096 && !dec->nopurge)\t//expand if needed. 4096 seems to be the recommended size.\n\t\t\t{\n//\t\t\t\tCon_Printf(\"Expand buffer for %s\\n\", sfx->name);\n\t\t\t\tdec->decodedbufferbytes = (start+length - dec->decodedbytestart) + max(outspeed, 4096); //over allocate, because we can.\n\t\t\t\tdec->decodedbuffer = BZ_Realloc(dec->decodedbuffer, dec->decodedbufferbytes);\n\t\t\t}\n\n\t\t\tif (outspeed == dec->srcspeed)\n\t\t\t{\n\t\t\t\tbytesread = p_ov_read(&dec->vf, dec->decodedbuffer+dec->decodedbytecount, (start+length) - (dec->decodedbytestart+dec->decodedbytecount), bigendianp, 2, 1, &current_section);\n\t\t\t\tif (bytesread <= 0)\n\t\t\t\t{\n\t\t\t\t\tif (bytesread != 0)\t//0==eof\n\t\t\t\t\t{\n\t\t\t\t\t\tdec->failed = true;\n\t\t\t\t\t\tCon_Printf(\"ogg decoding failed %i\\n\", bytesread);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tif (start >= dec->decodedbytestart+dec->decodedbytecount)\n\t\t\t\t\t\treturn NULL;\t//let the mixer know that we hit the end\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tdouble scale = dec->srcspeed / (double)outspeed;\n\t\t\t\tint decodesize = dec->decodedbufferbytes-dec->decodedbytecount; //bytes available\n\t\t\t\tdecodesize /= 2*dec->srcchannels; //convert bytes to frames\n\t\t\t\tdecodesize = floor(decodesize * scale); //round down, so that the SND_ResampleStream won't overflow the target buffer.\n\t\t\t\tdecodesize *= 2*dec->srcchannels; //convert from frames back to bytes\n\t\t\t\tif (decodesize > dec->tempbufferbytes)\n\t\t\t\t{\n\t\t\t\t\tdec->tempbuffer = BZ_Realloc(dec->tempbuffer, decodesize);\n\t\t\t\t\tdec->tempbufferbytes = decodesize;\n\t\t\t\t}\n\n\t\t\t\tbytesread = p_ov_read(&dec->vf, dec->tempbuffer, decodesize, bigendianp, 2, 1, &current_section);\n\n\t\t\t\tif (bytesread <= 0)\n\t\t\t\t{\n\t\t\t\t\tif (bytesread != 0)\t//0==eof\n\t\t\t\t\t{\n\t\t\t\t\t\tdec->failed = true;\n\t\t\t\t\t\tCon_Printf(\"ogg decoding failed %i\\n\", bytesread);\n\t\t\t\t\t\treturn NULL;\n\t\t\t\t\t}\n\t\t\t\t\tif (start >= dec->decodedbytestart+dec->decodedbytecount)\n\t\t\t\t\t\treturn NULL;\t//let the mixer know that we hit the end\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tSND_ResampleStream(dec->tempbuffer,\n\t\t\t\t\tdec->srcspeed,\n\t\t\t\t\t2,\n\t\t\t\t\tdec->srcchannels,\n\t\t\t\t\tbytesread / (2 * dec->srcchannels),\n\t\t\t\t\tdec->decodedbuffer+dec->decodedbytecount,\n\t\t\t\t\toutspeed,\n\t\t\t\t\t2,\n\t\t\t\t\tdec->srcchannels,\n\t\t\t\t\tsnd_linearresample_stream.ival);\n\t\t\t\tbytesread /= 2*dec->srcchannels;\t//convert bytes to frames\n\t\t\t\tbytesread = bytesread / scale;\t\t//calculate the same ammount that SND_ResampleStream will have splurged (we should probably make the output count explicit).\n\t\t\t\tbytesread *= 2*dec->srcchannels;\t//convert frames to bytes\n\t\t\t}\n\n\t\t\tdec->decodedbytecount += bytesread;\n\t\t}\n\t}\n\n\tif (buf)\n\t{\n\t\tbuf->data = dec->decodedbuffer;\n\t\tbuf->soundoffset = dec->decodedbytestart / (2 * dec->srcchannels);\n\t\tbuf->length = dec->decodedbytecount / (2 * dec->srcchannels);\n\t\tbuf->numchannels = dec->srcchannels;\n\t\tbuf->speed = snd_speed;\n\t\tbuf->format = QAF_S16;\n\t}\n\treturn buf;\n}\n/*static void OV_CanceledDecoder(void *ctx, void *data, size_t a, size_t b)\n{\n\tsfx_t *s = ctx;\n\tif (s->loadstate != SLS_LOADING)\n\t\ts->loadstate = SLS_NOTLOADED;\n}*/\nstatic void QDECL OV_CancelDecoder(sfx_t *s)\n{\t//called when the sound is unloaded. the entire thing is going away.\n\tovdecoderbuffer_t *dec;\n\ts->loadstate = SLS_FAILED;\n\n\tdec = s->decoder.buf;\n\ts->decoder.buf = NULL;\n\ts->decoder.purge = NULL;\n\ts->decoder.ended = NULL;\n\ts->decoder.querydata = NULL;\n\ts->decoder.decodedata = NULL;\n\tp_ov_clear (&dec->vf);\t//close the decoder\n\n\tif (dec->tempbuffer)\n\t{\n\t\tBZ_Free(dec->tempbuffer);\n\t\tdec->tempbufferbytes = 0;\n\t}\n\n\tBZ_Free(dec->decodedbuffer);\n\tdec->decodedbuffer = NULL;\n\n\tBZ_Free(dec);\n\n\t//due to the nature of message passing, we can get into a state where the main thread is going to flag us as loaded when we have already failed.\n\t//that is bad.\n\t//so post a message to the main thread to override it, just in case.\n//\tCOM_AddWork(WG_MAIN, OV_CanceledDecoder, s, NULL, SLS_NOTLOADED, 0);\n\ts->loadstate = SLS_NOTLOADED;\n}\nstatic void QDECL OV_ClearDecoder(sfx_t *s)\n{\t//called when the sound is no longer playing.\n\tovdecoderbuffer_t *dec;\n\tdec = s->decoder.buf;\n\tif (dec->nopurge)\n\t{\n/*\t\tBZ_Free(dec->tempbuffer);\n\t\tdec->tempbuffer = NULL;\n\t\tdec->tempbufferbytes = 0;\n\n\t\tBZ_Free(dec->decodedbuffer);\n\t\tdec->decodedbuffer = NULL;\n\t\tdec->decodedbufferbytes = 0;\n\t\tdec->decodedbytestart = 0;\n\t\tdec->decodedbytecount = 0;\n*/\n\t\treturn;\n\t}\n\tOV_CancelDecoder(s);\n}\n\nstatic size_t VARGS read_func (void *ptr, size_t size, size_t nmemb, void *datasource)\n{\n\tovdecoderbuffer_t *buffer = datasource;\n\tint spare = buffer->length - buffer->pos;\n\n\tif (size*nmemb > spare)\n\t\tnmemb = spare / size;\n\tmemcpy(ptr, &buffer->start[buffer->pos], size*nmemb);\n\tbuffer->pos += size*nmemb;\n\treturn nmemb;\n}\n\nstatic int VARGS seek_func (void *datasource, ogg_int64_t offset, int whence)\n{\n\tovdecoderbuffer_t *buffer = datasource;\n\tswitch(whence)\n\t{\n\tcase SEEK_SET:\n\t\tbuffer->pos = offset;\n\t\tbreak;\n\tcase SEEK_END:\n\t\tbuffer->pos = buffer->length+offset;\n\t\tbreak;\n\tcase SEEK_CUR:\n\t\tbuffer->pos+=offset;\n\t\tbreak;\n\t}\n\treturn 0;\n}\n\nstatic int VARGS close_func (void *datasource)\n{\n\tovdecoderbuffer_t *buffer = datasource;\n\tBZ_Free(buffer->start);\n\tbuffer->start=0;\n\treturn 0;\n}\n\nstatic long VARGS tell_func (void *datasource)\n{\n\tovdecoderbuffer_t *buffer = datasource;\n\treturn buffer->pos;\n}\nstatic ov_callbacks callbacks = {\n\tread_func,\n\tseek_func,\n\tclose_func,\n\ttell_func,\n};\nstatic qboolean OV_StartDecode(unsigned char *start, unsigned long length, ovdecoderbuffer_t *buffer)\n{\n#ifndef LIBVORBISFILE_STATIC\n\tstatic qboolean tried;\n#ifndef __MORPHOS__\n\tstatic dllfunction_t funcs[] =\n\t{\n\t\t{(void*)&p_ov_open_callbacks, \"ov_open_callbacks\"},\n\t\t{(void*)&p_ov_comment, \"ov_comment\"},\n\t\t{(void*)&p_ov_pcm_total, \"ov_pcm_total\"},\n\t\t{(void*)&p_ov_time_total, \"ov_time_total\"},\n\t\t{(void*)&p_ov_clear, \"ov_clear\"},\n\t\t{(void*)&p_ov_info, \"ov_info\"},\n\t\t{(void*)&p_ov_read, \"ov_read\"},\n\t\t{(void*)&p_ov_pcm_seek, \"ov_pcm_seek\"},\n\t\t{NULL}\n\t};\n#endif\n\n\tif (!oggvorbislibrary && !tried)\n#if defined(__MORPHOS__)\n\t{\n\t\tVorbisFileBase = OpenLibrary(\"vorbisfile.library\", 2);\n\t\tif (!VorbisFileBase)\n\t\t{\n\t\t\tCon_Printf(\"Unable to open vorbisfile.library version 2\\n\");\n\t\t}\n\t}\n#elif defined(_WIN32)\n\t{\n\t\toggvorbislibrary = Sys_LoadLibrary(\"vorbisfile\", funcs);\n\t\tif (!oggvorbislibrary)\n\t\t\toggvorbislibrary = Sys_LoadLibrary(\"libvorbisfile\", funcs);\n\n\t\tif (!oggvorbislibrary)\n\t\t{\n\t\t\toggvorbislibrary = Sys_LoadLibrary(\"libvorbisfile-3\", funcs);\n\t\t\tif (!oggvorbislibrary)\n\t\t\t\toggvorbislibrary = Sys_LoadLibrary(\"libvorbisfile\", funcs);\n\t\t}\n\n\t\tif (!oggvorbislibrary)\n\t\t\tCon_Printf(\"Couldn't load DLL: \\\"vorbisfile.dll\\\" or \\\"libvorbisfile-3\\\".\\n\");\n\t}\n#else\n\t{\n\t\toggvorbislibrary = Sys_LoadLibrary(\"libvorbisfile.so.3\", funcs);\n\t\tif (!oggvorbislibrary)\n\t\t\toggvorbislibrary = Sys_LoadLibrary(\"libvorbisfile\", funcs);\n\t\tif (!oggvorbislibrary)\n\t\t\tCon_Printf(\"Couldn't load library: \\\"libvorbisfile\\\".\\n\");\n\t}\n#endif\n\n\ttried = true;\n\n\tif (!oggvorbislibrary)\n\t{\n\t\tCon_Printf(\"ogg vorbis library is not loaded.\\n\");\n\t\treturn false;\n\t}\n#endif\n\n\tbuffer->start = start;\n\tbuffer->length = length;\n\tbuffer->pos = 0;\n\tif (p_ov_open_callbacks(buffer, &buffer->vf, NULL, 0, callbacks))\n\t{\n\t\tCon_Printf(\"Input %s does not appear to be an Ogg Vorbis bitstream.\\n\", buffer->s->name);\n\t\treturn false;\n\t}\n\n  /* Print the comments plus a few lines about the bitstream we're\n     decoding */\n  {\n//    char **ptr=p_ov_comment(&buffer->vf,-1)->user_comments;\n    vorbis_info *vi=p_ov_info(&buffer->vf,-1);\n\n\tif (vi->channels < 1 || vi->channels > 2)\n\t{\n\t\tp_ov_clear (&buffer->vf);\n\t\tCon_Printf(\"Input %s has %i channels.\\n\", buffer->s->name, vi->channels);\n\t\treturn false;\n\t}\n\n\tbuffer->srcchannels = vi->channels;\n\tbuffer->srcspeed = vi->rate;\n/*\n    while(*ptr){\n      Con_Printf(\"%s\\n\",*ptr);\n      ptr++;\n    }\n    Con_Printf(\"\\nBitstream is %d channel, %ldHz\\n\",vi->channels,vi->rate);\n    Con_Printf(\"\\nDecoded length: %ld samples\\n\",\n\t    (long)p_ov_pcm_total(&buffer->vf,-1));\n    Con_Printf(\"Encoded by: %s\\n\\n\",p_ov_comment(&buffer->vf,-1)->vendor);\n*/  }\n\tbuffer->tempbuffer = NULL;\n\tbuffer->tempbufferbytes = 0;\n\n\tbuffer->start = BZ_Malloc(length);\n\tmemcpy(buffer->start, start, length);\n\n\tbuffer->pcmtotal = p_ov_pcm_total(&buffer->vf, -1);\n\tbuffer->timetotal = buffer->pcmtotal / (float)buffer->srcspeed;\n\n\tif (buffer->timetotal < 5)\t//short sounds might as well remain cached.\n\t\tbuffer->nopurge = true;\n\n\tif (buffer->nopurge)\n\t{\t//waste more memory upfront to avoid reallocs later.\n\t\tbuffer->decodedbufferbytes = buffer->pcmtotal;\n\t\tbuffer->decodedbufferbytes += 4096;\t //ogg vorbis apparently lies/fails sometimes.\n\t\tbuffer->decodedbufferbytes *= (double)snd_speed / buffer->srcspeed;\t//from src rate to dst rate\n\t\tbuffer->decodedbufferbytes *= 2*buffer->srcchannels; //convert from frames to bytes.\n\t\tbuffer->decodedbufferbytes += 4096;\t //just general paranoia.\n\t\tbuffer->decodedbuffer = BZ_Realloc(buffer->decodedbuffer, buffer->decodedbufferbytes);\n\t}\n\treturn true;\n}\n\n\nqboolean QDECL S_LoadOVSound (sfx_t *s, qbyte *data, size_t datalen, int sndspeed, qboolean forcedecode)\n{\n\tovdecoderbuffer_t *buffer;\n\n\tif (datalen < 4 || strncmp(data, \"OggS\", 4))\n\t\treturn false;\n\n\tbuffer = Z_Malloc(sizeof(ovdecoderbuffer_t));\n\n\tbuffer->decodedbytestart = 0;\n\tbuffer->decodedbytecount = 0;\n\tbuffer->nopurge = forcedecode;\n\tbuffer->s = s;\n\ts->decoder.buf = buffer;\n\ts->loopstart = -1;\n\n\tif (!OV_StartDecode(data, datalen, buffer))\n\t{\n\t\tif (buffer->decodedbuffer)\n\t\t{\n\t\t\tBZ_Free(buffer->decodedbuffer);\n\t\t\tbuffer->decodedbuffer = NULL;\n\t\t}\n\t\tbuffer->decodedbufferbytes = 0;\n\t\tbuffer->decodedbytestart = 0;\n\t\tbuffer->decodedbytecount = 0;\n\t\tZ_Free(s->decoder.buf);\n\t\ts->decoder.buf = NULL;\n\t\ts->loadstate = SLS_FAILED;\t//failed!\n\t\treturn false;\n\t}\n\ts->decoder.decodedata = OV_DecodeSome;\n\ts->decoder.querydata = OV_Query;\n\ts->decoder.purge = OV_CancelDecoder;\n\ts->decoder.ended = OV_ClearDecoder;\n\n\ts->decoder.decodedata(s, NULL, 0, 100);\n\n\treturn true;\n}\n\n#endif\n\n"
  },
  {
    "path": "engine/client/snd_pulse.c",
    "content": "#include \"quakedef.h\"\n#ifdef AUDIO_PULSE\n\n#if 0\n#include <pulse/simple.h>\n#else\ntypedef struct pa_simple pa_simple;\ntypedef enum pa_stream_direction {PA_STREAM_PLAYBACK=1} pa_stream_direction_t;\ntypedef enum pa_sample_format {\n    PA_SAMPLE_U8,\n    PA_SAMPLE_ALAW,\n    PA_SAMPLE_ULAW,\n    PA_SAMPLE_S16LE,\n    PA_SAMPLE_S16BE,\n    PA_SAMPLE_FLOAT32LE,\n    PA_SAMPLE_FLOAT32BE,\n    PA_SAMPLE_S32LE,\n    PA_SAMPLE_S32BE,\n    PA_SAMPLE_S24LE,\n    PA_SAMPLE_S24BE,\n    PA_SAMPLE_S24_32LE,\n    PA_SAMPLE_S24_32BE,\n    PA_SAMPLE_MAX,\n    PA_SAMPLE_INVALID = -1\n} pa_sample_format_t;\ntypedef struct pa_sample_spec {\n    pa_sample_format_t format;\n    uint32_t rate;\n    uint8_t channels;\n} pa_sample_spec;\ntypedef struct pa_channel_map pa_channel_map;\ntypedef struct pa_buffer_attr pa_buffer_attr;\ntypedef uint64_t pa_usec_t;\n\n#if __BYTE_ORDER == __BIG_ENDIAN\n#define PA_SAMPLE_FLOAT32 PA_SAMPLE_FLOAT32BE\n#define PA_SAMPLE_S16NE PA_SAMPLE_S16BE\n#else\n#define PA_SAMPLE_FLOAT32 PA_SAMPLE_FLOAT32LE\n#define PA_SAMPLE_S16NE PA_SAMPLE_S16LE\n#endif\n#endif\n\n\nstatic pa_simple *(*qpa_simple_new)(const char *server,const char *name,pa_stream_direction_t dir, const char *dev, const char *stream_name, const pa_sample_spec *ss, const pa_channel_map *map, const pa_buffer_attr *attr, int *error);\nstatic pa_usec_t (*qpa_simple_get_latency)(pa_simple *s, int *error);\nstatic int (*qpa_simple_write)(pa_simple *s, const void *data, size_t bytes, int *error);\nstatic void (*qpa_simple_free)(pa_simple *s);\n\nstatic qboolean Pulse_Init(void)\n{\n\tstatic qboolean tried;\n\tstatic void *pulsemodule;\n\tstatic dllfunction_t funcs[] =\n\t{\n\t\t{(void**)&qpa_simple_new, \"pa_simple_new\"},\n\t\t{(void**)&qpa_simple_get_latency, \"pa_simple_get_latency\"},\n\t\t{(void**)&qpa_simple_write, \"pa_simple_write\"},\n\t\t{(void**)&qpa_simple_free, \"pa_simple_free\"},\n\t\t{NULL, NULL}\n\t};\n\tif (COM_CheckParm(\"-nopulse\"))\n\t\treturn false;\n\n\tif (!tried)\n\t{\n\t\ttried = true;\n\t\tpulsemodule = Sys_LoadLibrary(\"libpulse-simple.so.0\", funcs);\n\t}\n\n\treturn pulsemodule!=NULL;\n}\n\nstatic unsigned int Pulse_GetDMAPos(soundcardinfo_t *sc)\n{\n\tsc->sn.samplepos = sc->snd_sent / sc->sn.samplebytes;\n\treturn sc->sn.samplepos;\n}\nstatic void Pulse_Submit(soundcardinfo_t *sc, int start, int end)\n{\n}\n\nstatic void Pulse_Shutdown(soundcardinfo_t *sc)\n{\n\tsc->selfpainting = false;\n\tif (sc->thread)\n\t\tSys_WaitOnThread(sc->thread);\n\tsc->thread = NULL;\n\t*sc->name = '\\0';\n}\n\nstatic void *Pulse_Lock(soundcardinfo_t *sc, unsigned int *sampidx)\n{\n\treturn sc->sn.buffer;\n}\n\nstatic void Pulse_Unlock(soundcardinfo_t *sc, void *buffer)\n{\n}\n\nstatic int Pulse_Thread(void *arg)\n{\n\tchar buffer[256];\n\tsoundcardinfo_t *sc = arg;\n\tvoid *cond = sc->handle;\n\tint err = 0;\n\tint showlatency = 64;\n\n\tpa_simple *pulse;\n\tpa_sample_spec ss;\n\tss.rate = sc->sn.speed;\n\tswitch(sc->sn.sampleformat)\n\t{\n\tcase QSF_INVALID:\n\tcase QSF_EXTERNALMIXER:\n\tcase QSF_S8:\t//no signed 8bit formats here\n\t\tss.format = PA_SAMPLE_INVALID;\n\t\tbreak;\n\tcase QSF_U8:\n\t\tss.format = PA_SAMPLE_U8;\n\t\tbreak;\n\tcase QSF_S16:\n\t\tss.format = PA_SAMPLE_S16NE;\n\t\tbreak;\n\tcase QSF_F32:\n\t\tss.format = PA_SAMPLE_FLOAT32;\n\t\tbreak;\n\t}\n\tss.channels = sc->sn.numchannels;\n\n\tpulse = qpa_simple_new(\tNULL,               // Use the default server.\n\t\t\t\t\t\t\tFULLENGINENAME,     // Our application's name.\n\t\t\t\t\t\t\tPA_STREAM_PLAYBACK,\n\t\t\t\t\t\t\tNULL,               // Use the default device.\n\t\t\t\t\t\t\t\"Game Audio\",       // Description of our stream.\n\t\t\t\t\t\t\t&ss,                // Our sample format.\n\t\t\t\t\t\t\tNULL,               // Use default channel map\n\t\t\t\t\t\t\tNULL,               // Use default buffering attributes.\n\t\t\t\t\t\t\tNULL               // Ignore error code.\n\t\t\t\t\t\t\t);\n\tif (pulse)\n\t\tsc->selfpainting = true;\t//its going!\n\n\tSys_LockConditional(cond);\n\tSys_ConditionSignal(cond);\n\tSys_UnlockConditional(cond);\n\n\twhile(sc->selfpainting)\n\t{\n\t\tsc->sn.buffer = buffer;\n\t\tsc->sn.samples = sizeof(buffer)/sc->sn.samplebytes;\n\t\tsc->samplequeue = sc->sn.samples;\n\t\tS_MixerThread(sc);\n\t\tsc->snd_sent += sc->sn.samplebytes*sc->samplequeue;\n\n\t\tif (qpa_simple_write(pulse, buffer, sc->sn.samplebytes*sc->samplequeue, &err) < 0)\n\t\t{\n\t\t\tCon_Printf(\"pa_simple_write failed\\n\");\n\t\t\tsc->selfpainting = false;\t//some sort of error\n\t\t}\n\n\t\tif (showlatency > 0)\n\t\tif (--showlatency == 0)\n\t\t{\t//we delay this print so that we have a chance of finding out the real value\n\t\t\tpa_usec_t latency = qpa_simple_get_latency(pulse, &err);\n\t\t\tCon_Printf(\"PulseAudio latency is about %.3f seconds\\n\", latency/1000000.0);\n\t\t}\n\t}\n\n\tif (pulse)\n\t\tqpa_simple_free(pulse);\n\treturn 0;\n}\n\nstatic qboolean Pulse_InitCard(soundcardinfo_t *sc, const char *snddev)\n{\t//FIXME: implement snd_multipledevices somehow.\n\n\tif (!Pulse_Init())\n\t\treturn false;\n\n\tsc->inactive_sound = true;\t//linux sound devices always play sound, even when we're not the active app...\n\tsc->sn.samplebytes = 4;\n\tsc->sn.sampleformat = QSF_F32;\n\tsc->sn.buffer = NULL;\n\tsc->sn.samplepos = 0;\n\tsc->Submit\t\t= Pulse_Submit;\n\tsc->GetDMAPos\t= Pulse_GetDMAPos;\n\tsc->Lock\t\t= Pulse_Lock;\n\tsc->Unlock\t\t= Pulse_Unlock;\n\tsc->Shutdown\t= Pulse_Shutdown;\n\n\tsc->handle = Sys_CreateConditional();\n\tSys_LockConditional(sc->handle);\n\tsc->thread = Sys_CreateThread(\"pulse\", Pulse_Thread, sc, THREADP_HIGHEST, 0);\n\tif (sc->thread)\n\t{\n\t\tif (!Sys_ConditionWait(sc->handle))\n\t\t\tsc->selfpainting = false;\n\t\t//thread is up and running now.\n\t}\n\tSys_UnlockConditional(sc->handle);\n\tSys_DestroyConditional(sc->handle);\n\n\tif (!sc->selfpainting)\n\t{\t//err, thread signalled itself to die?\n\t\tPulse_Shutdown(sc);\n\t\treturn false;\n\t}\n\treturn true;\n}\n\n#define SDRVNAME \"Pulse\"\n\nstatic qboolean QDECL Pulse_Enumerate(void (QDECL *cb) (const char *drivername, const char *devicecode, const char *readablename))\n{\n\tif (!Pulse_Init())\n\t\treturn true;\t//sucessfully enumerated no devices\n\treturn false;\t\t//not implemented (we'll get a default device only)\n}\n\nsounddriver_t Pulse_Output =\n{\n\tSDRVNAME,\n\tPulse_InitCard,\n\tPulse_Enumerate\n};\n\n#endif\n"
  },
  {
    "path": "engine/client/snd_sblaster.c",
    "content": "/*\r\nCopyright (C) 1996-1997 Id Software, Inc.\r\n\r\nThis program is free software; you can redistribute it and/or\r\nmodify it under the terms of the GNU General Public License\r\nas published by the Free Software Foundation; either version 2\r\nof the License, or (at your option) any later version.\r\n\r\nThis program is distributed in the hope that it will be useful,\r\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\r\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  \r\n\r\nSee the GNU General Public License for more details.\r\n\r\nYou should have received a copy of the GNU General Public License\r\nalong with this program; if not, write to the Free Software\r\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\r\n\r\n*/\r\n\r\n\r\n//I had one at least, back in the day.\r\n//should be fine for dosbox, if nothing else.\r\n\r\n//warning: this sound code doesn't seem to cope well with low framerates. the dma buffer is too small.\r\n//4096 bytes 16bit stereo means 1024 samples. so less than 10 fps and the mixer will miss buffer wraps.\r\n\r\n\r\n#include <quakedef.h>\r\n\r\n#include <dos.h>\r\n#include <dpmi.h>\r\n#include <go32.h>\r\n#include <sys/nearptr.h>\r\n\r\n#define SDRVNAME \"SoundBlaster\"\r\n\r\n/*\r\n===============================================================================\r\n\r\nBLASTER SUPPORT\r\n\r\n===============================================================================\r\n*/\r\n\r\n_go32_dpmi_seginfo dma_buffer_memory;\r\nstatic short *dma_buffer=0;\t//realigned pointer\r\nquintptr_t dma_buffer_phys;\t//realigned physical address - must be within the first 16mb\r\nstatic int dma_size;\r\nstatic\tint dma;\r\n\r\nstatic\tint dsp_port;\r\nstatic\tint irq;\r\nstatic\tint low_dma;\r\nstatic\tint high_dma;\r\nstatic\tint mixer_port;\r\nstatic\tint mpu401_port;\r\n\r\nstatic int dsp_version;\r\nstatic int dsp_minor_version;\r\n\r\nstatic int timeconstant=-1;\r\nstatic int\t\toldmixervalue;\r\n\r\nstatic int mode_reg;\r\nstatic int flipflop_reg;\r\nstatic int disable_reg;\r\nstatic int clear_reg;\r\n\r\nstatic soundcardinfo_t *becauseglobalssuck;\t//just protects against multiple devices being spawned at once.\r\n\r\n\r\nstatic void PrintBits (qbyte b)\r\n{\r\n\tint\ti;\r\n\tchar\tstr[9];\r\n\t\r\n\tfor (i=0 ; i<8 ; i++)\r\n\t\tstr[i] = '0' + ((b & (1<<(7-i))) > 0);\r\n\t\t\r\n\tstr[8] = 0;\r\n\tCon_Printf (\"%s (%i)\", str, b);\r\n}\r\n\r\n// =======================================================================\r\n// Interprets BLASTER variable\r\n// =======================================================================\r\n\r\nstatic int GetBLASTER(void)\r\n{\r\n\tchar *BLASTER;\r\n\tchar *param;\r\n\r\n\tBLASTER = getenv(\"BLASTER\");\r\n\tif (!BLASTER)\r\n\t\treturn 0;\r\n\r\n\tparam = strchr(BLASTER, 'A');\r\n\tif (!param)\r\n\t\tparam = strchr(BLASTER, 'a');\r\n\tif (!param)\r\n\t\treturn 0;\r\n\tsscanf(param+1, \"%x\", &dsp_port);\r\n\r\n\tparam = strchr(BLASTER, 'I');\r\n\tif (!param)\r\n\t\tparam = strchr(BLASTER, 'i');\r\n\tif (!param)\r\n\t\treturn 0;\r\n\tsscanf(param+1, \"%d\", &irq);\r\n\r\n\tparam = strchr(BLASTER, 'D');\r\n\tif (!param)\r\n\t\tparam = strchr(BLASTER, 'd');\r\n\tif (!param)\r\n\t\treturn 0;\r\n\tsscanf(param+1, \"%d\", &low_dma);\r\n\r\n\tparam = strchr(BLASTER, 'H');\r\n\tif (!param)\r\n\t\tparam = strchr(BLASTER, 'h');\r\n\tif (param)\r\n\t\tsscanf(param+1, \"%d\", &high_dma);\r\n\r\n\tparam = strchr(BLASTER, 'M');\r\n\tif (!param)\r\n\t\tparam = strchr(BLASTER, 'm');\r\n\tif (param)\r\n\t\tsscanf(param+1, \"%x\", &mixer_port);\r\n\telse\r\n\t\tmixer_port = dsp_port;\r\n\r\n\tparam = strchr(BLASTER, 'P');\r\n\tif (!param)\r\n\t\tparam = strchr(BLASTER, 'p');\r\n\tif (param)\r\n\t\tsscanf(param+1, \"%x\", &mpu401_port);\r\n\r\n\treturn 1;\r\n\r\n}\r\n\r\n// ==================================================================\r\n// Resets DSP.  Returns 0 on success.\r\n// ==================================================================\r\n\r\nstatic int ResetDSP(void)\r\n{\r\n\tvolatile int i;\r\n\r\n\toutportb(dsp_port + 6, 1);\r\n\tfor (i=65536 ; i ; i--) ;\r\n\toutportb(dsp_port + 6, 0);\r\n\tfor (i=65536 ; i ; i--)\r\n\t{\r\n\t\tif (!(inportb(dsp_port + 0xe) & 0x80)) continue;\r\n\t\tif (inportb(dsp_port + 0xa) == 0xaa) break;\r\n\t}\r\n\tif (i) return 0;\r\n\telse return 1;\r\n\r\n}\r\n\r\nstatic int ReadDSP(void)\r\n{\r\n\twhile (!(inportb(dsp_port+0xe)&0x80)) ;\r\n\treturn inportb(dsp_port+0xa);\r\n}\r\n\r\nstatic void WriteDSP(int val)\r\n{\r\n\twhile ((inportb(dsp_port+0xc)&0x80)) ;\r\n\toutportb(dsp_port+0xc, val);\r\n}\r\n\r\nstatic int ReadMixer(int addr)\r\n{\r\n\toutportb(mixer_port+4, addr);\r\n\treturn inportb(mixer_port+5);\r\n}\r\n\r\nstatic void WriteMixer(int addr, int val)\r\n{\r\n\toutportb(mixer_port+4, addr);\r\n\toutportb(mixer_port+5, val);\r\n}\r\n\r\n/*\r\n================\r\nStartSB\r\n\r\n================\r\n*/\r\nstatic void StartSB(soundcardinfo_t *sc)\r\n{\r\n\tint\t\ti;\r\n\r\n// version 4.xx startup code\r\n\tif (dsp_version >= 4)\r\n\t{\r\n\t\tCon_Printf(\"Version 4 SB startup\\n\");\r\n\t\tWriteDSP(0xd1); // turn on speaker\r\n\r\n\t\tWriteDSP(0x41);\r\n\r\n\t\tWriteDSP(sc->sn.speed>>8);\r\n\t\tWriteDSP(sc->sn.speed&0xff);\r\n\r\n\t\tWriteDSP(0xb6);\t// 16-bit output\r\n\t\tWriteDSP(0x30);\t// stereo\r\n\t\tWriteDSP((sc->sn.samples-1) & 0xff);\t// # of samples - 1\r\n\t\tWriteDSP((sc->sn.samples-1) >> 8);\r\n\t}\r\n// version 3.xx startup code\r\n\telse if (dsp_version == 3)\r\n\t{\r\n\t\tCon_Printf(\"Version 3 SB startup\\n\");\r\n\t\tWriteDSP(0xd1); // turn on speaker\r\n\r\n\t\toldmixervalue = ReadMixer (0xe);\r\n\t\tWriteMixer (0xe, oldmixervalue | 0x2);// turn on stereo\r\n\r\n\t\tWriteDSP(0x14);\t\t\t// send one byte\r\n\t\tWriteDSP(0x0);\r\n\t\tWriteDSP(0x0);\r\n\r\n\t\tfor (i=0 ; i<0x10000 ; i++)\r\n\t\t\tinportb(dsp_port+0xe);\t\t// ack the dsp\r\n\t\t\r\n\t\ttimeconstant = 65536-(256000000/(sc->sn.numchannels*sc->sn.speed));\r\n\t\tWriteDSP(0x40);\r\n\t\tWriteDSP(timeconstant>>8);\r\n\r\n\t\tWriteMixer (0xe, ReadMixer(0xe) | 0x20);// turn off filter\r\n\r\n\t\tWriteDSP(0x48);\r\n\t\tWriteDSP((sc->sn.samples-1) & 0xff);\t// # of samples - 1\r\n\t\tWriteDSP((sc->sn.samples-1) >> 8);\r\n\r\n\t\tWriteDSP(0x90); // high speed 8 bit stereo\r\n\t}\r\n// normal speed mono\r\n\telse\r\n\t{\r\n\t\tCon_Printf(\"Version 2 SB startup\\n\");\r\n\t\tWriteDSP(0xd1); // turn on speaker\r\n\r\n\t\ttimeconstant = 65536-(256000000/(sc->sn.numchannels*sc->sn.speed));\r\n\t\tWriteDSP(0x40);\r\n\t\tWriteDSP(timeconstant>>8);\r\n\r\n\t\tWriteDSP(0x48);\r\n\t\tWriteDSP((sc->sn.samples-1) & 0xff);\t// # of samples - 1\r\n\t\tWriteDSP((sc->sn.samples-1) >> 8);\r\n\r\n\t\tWriteDSP(0x1c); // normal speed 8 bit mono\r\n\t}\r\n}\r\n\r\nstatic const int page_reg[] = { 0x87, 0x83, 0x81, 0x82, 0x8f, 0x8b, 0x89, 0x8a };\r\nstatic const int addr_reg[] = { 0, 2, 4, 6, 0xc0, 0xc4, 0xc8, 0xcc };\r\nstatic const int count_reg[] = { 1, 3, 5, 7, 0xc2, 0xc6, 0xca, 0xce };\r\n\r\n/*\r\n================\r\nStartDMA\r\n\r\n================\r\n*/\r\nstatic void StartDMA(void)\r\n{\r\n\tint mode;\r\n\r\n// use a high dma channel if specified\r\n\tif (high_dma && dsp_version >= 4)\t// 8 bit snd can never use 16 bit dma\r\n\t\tdma = high_dma;\r\n\telse\r\n\t\tdma = low_dma;\r\n\r\n\tCon_Printf (\"Using DMA channel %i\\n\", dma);\r\n\r\n\tif (dma > 3)\r\n\t{\r\n\t\tmode_reg = 0xd6;\r\n\t\tflipflop_reg = 0xd8;\r\n\t\tdisable_reg = 0xd4;\r\n\t\tclear_reg = 0xdc;\r\n\t}\r\n\telse\r\n\t{\r\n\t\tmode_reg = 0xb;\r\n\t\tflipflop_reg = 0xc;\r\n\t\tdisable_reg = 0xa;\r\n\t\tclear_reg = 0xe;\r\n\t}\r\n\r\n\toutportb(disable_reg, dma|4);\t// disable channel\r\n\t// set mode- see \"undocumented pc\", p.876\r\n\tmode =\t(1<<6)\t// single-cycle\r\n\t\t+(0<<5)\t\t// address increment\r\n\t\t+(1<<4)\t\t// auto-init dma\r\n\t\t+(2<<2)\t\t// read\r\n\t\t+(dma&3);\t// channel #\r\n\toutportb(mode_reg, mode);\r\n\t\r\n// set address\r\n\t// set page\r\n\toutportb(page_reg[dma], dma_buffer_phys >> 16);\r\n\r\n\tif (dma > 3)\r\n\t{\t// address is in words\r\n\t\toutportb(flipflop_reg, 0);\t\t// prepare to send 16-bit value\r\n\t\toutportb(addr_reg[dma], (dma_buffer_phys>>1) & 0xff);\r\n\t\toutportb(addr_reg[dma], (dma_buffer_phys>>9) & 0xff);\r\n\r\n\t\toutportb(flipflop_reg, 0);\t\t// prepare to send 16-bit value\r\n\t\toutportb(count_reg[dma], ((dma_size>>1)-1) & 0xff);\r\n\t\toutportb(count_reg[dma], ((dma_size>>1)-1) >> 8);\r\n\t}\r\n\telse\r\n\t{\t// address is in bytes\r\n\t\toutportb(flipflop_reg, 0);\t\t// prepare to send 16-bit value\r\n\t\toutportb(addr_reg[dma], dma_buffer_phys & 0xff);\r\n\t\toutportb(addr_reg[dma], (dma_buffer_phys>>8) & 0xff);\r\n\r\n\t\toutportb(flipflop_reg, 0);\t\t// prepare to send 16-bit value\r\n\t\toutportb(count_reg[dma], (dma_size-1) & 0xff);\r\n\t\toutportb(count_reg[dma], (dma_size-1) >> 8);\r\n\t}\r\n\r\n\toutportb(clear_reg, 0);\t\t// clear write mask\r\n\toutportb(disable_reg, dma&~4);\r\n}\r\n\r\n/*\r\n==============\r\nBLASTER_GetDMAPos\r\n\r\nreturn the current sample position (in mono samples read)\r\ninside the recirculating dma buffer, so the mixing code will know\r\nhow many sample are required to fill it up.\r\n===============\r\n*/\r\nstatic unsigned int SBLASTER_GetDMAPos(soundcardinfo_t *sc)\r\n{\r\n\tint count;\r\n\r\n// this function is called often.  acknowledge the transfer completions\r\n// all the time so that it loops\r\n\tif (dsp_version >= 4)\r\n\t\tinportb(dsp_port+0xf);\t// 16 bit audio\r\n\telse\r\n\t\tinportb(dsp_port+0xe);\t// 8 bit audio\r\n\r\n// clear 16-bit reg flip-flop\r\n// load the current dma count register\r\n\tif (dma < 4)\r\n\t{\r\n\t\toutportb(0xc, 0);\r\n\t\tcount = inportb(dma*2+1);\r\n\t\tcount += inportb(dma*2+1) << 8;\r\n\t\tif (sc->sn.samplebytes == 2)\r\n\t\t\tcount /= 2;\r\n\t\tcount = sc->sn.samples - (count+1);\r\n\t}\r\n\telse\r\n\t{\r\n\t\toutportb(0xd8, 0);\r\n\t\tcount = inportb(0xc0+(dma-4)*4+2);\r\n\t\tcount += inportb(0xc0+(dma-4)*4+2) << 8;\r\n\t\tif (sc->sn.samplebytes == 1)\r\n\t\t\tcount *= 2;\r\n\t\tcount = sc->sn.samples - (count+1);\r\n\t}\r\n\r\n//\tCon_Printf(\"DMA pos = 0x%x\\n\", count);\r\n\r\n//\tsc->sn.samplepos = count & (sc->sn.samples-1);\r\n\treturn count;\r\n\r\n}\r\n\r\n/*\r\n==============\r\nBLASTER_Shutdown\r\n\r\nReset the sound device for exiting\r\n===============\r\n*/\r\nstatic void SBLASTER_Shutdown(soundcardinfo_t *sc)\r\n{\r\n\tif (becauseglobalssuck == sc)\r\n\t\tbecauseglobalssuck = NULL;\r\n\r\n\tif (dsp_version >= 4)\r\n\t{\r\n\t}\r\n\telse if (dsp_version == 3)\r\n\t{\r\n\t\tResetDSP ();\t\t\t// stop high speed mode\r\n\t\tWriteMixer (0xe, oldmixervalue); // turn stereo off and filter on\r\n\t}\r\n\telse\r\n\t{\r\n\t\r\n\t}\r\n\t\r\n\tWriteDSP(0xd3); // turn off speaker\r\n\tResetDSP ();\r\n\r\n\toutportb(disable_reg, dma|4);\t// disable dma channel\r\n\r\n\t_go32_dpmi_free_dos_memory(&dma_buffer_memory);\r\n}\r\n\r\n//simple ring buffer\r\nstatic void *SBLASTER_LockBuffer(soundcardinfo_t *sc, unsigned int *sampidx)\r\n{\r\n\treturn sc->sn.buffer;\r\n}\r\n\r\n//that's permanently locked\r\nstatic void SBLASTER_UnlockBuffer(soundcardinfo_t *sc, void *buffer)\r\n{\r\n}\r\n\r\n//that the hardware has direct access to.\r\nstatic void SBLASTER_Submit (soundcardinfo_t *sc, int start, int end)\r\n{\r\n}\r\n\r\n//returns the address of some memory.\r\n//ctx is required to free the memory afterwards\r\nstatic qboolean dosmem_alloc(_go32_dpmi_seginfo *ctx, size_t size)\r\n{\r\n\tctx->size = (size+15)>>4;\r\n\tif (_go32_dpmi_allocate_dos_memory(ctx))\r\n\t\treturn false;\t//failed\r\n\treturn true;\r\n}\r\n\r\nstatic quintptr_t dosmem_phys(_go32_dpmi_seginfo *ctx)\r\n{\r\n\treturn ctx->rm_segment<<4;\r\n}\r\nstatic void *dosmem_ptr(_go32_dpmi_seginfo *ctx)\r\n{\r\n\t__djgpp_nearptr_enable();\r\n\treturn (void*)(__djgpp_conventional_base+dosmem_phys(ctx));\r\n}\r\n\r\n\r\n/*\r\n==================\r\nBLASTER_Init\r\n\r\nReturns false if nothing is found.\r\n==================\r\n*/\r\nstatic qboolean SBLASTER_InitCard(soundcardinfo_t *sc, const char *pcmname)\r\n{\r\n\tint \tsize;\r\n\tint\t\tp;\r\n\r\n\tif (becauseglobalssuck)\r\n\t\treturn 0;\r\n\r\n//\r\n// must have a blaster variable set\r\n//\r\n\tif (!GetBLASTER())\r\n\t{\r\n\t\tCon_NotifyBox (\r\n\t\t\"The BLASTER environment variable\\n\"\r\n\t\t\"is not set, sound effects are\\n\"\r\n\t\t\"disabled.  See README.TXT for help.\\n\"\r\n\t\t);\t\t\t\r\n\t\treturn 0;\r\n\t}\r\n\r\n\tif (ResetDSP())\r\n\t{\r\n\t\tCon_Printf(\"Could not reset SB\");\r\n\t\treturn 0;\r\n\t}\r\n\r\n//\r\n// get dsp version\r\n//\r\n\tWriteDSP(0xe1);\r\n\tdsp_version = ReadDSP();\r\n\tdsp_minor_version = ReadDSP();\r\n\r\n// we need at least v2 for auto-init dma\r\n\tif (dsp_version < 2)\r\n\t{\r\n\t\tCon_Printf (\"Sound blaster must be at least v2.0\\n\");\r\n\t\treturn 0;\r\n\t}\r\n\r\n// allow command line parm to set quality down\r\n\tp = COM_CheckParm (\"-dsp\");\r\n\tif (p && p < com_argc - 1)\r\n\t{\r\n\t\tp = Q_atoi (com_argv[p+1]);\r\n\t\tif (p < 2 || p > 4)\r\n\t\t\tCon_Printf (\"-dsp parameter can only be 2, 3, or 4\\n\");\r\n\t\telse if (p > dsp_version)\r\n\t\t\tCon_Printf (\"Can't -dsp %i on v%i hardware\\n\", p, dsp_version);\r\n\t\telse\r\n\t\t\tdsp_version = p;\r\n\t}\t\r\n\r\n\r\n// everyone does 11khz sampling rate unless told otherwise\r\n//\tsc->sn.speed = 11025;\r\n//\trc = COM_CheckParm(\"-sspeed\");\r\n//\tif (rc)\r\n//\t\tsc->sn.speed = Q_atoi(com_argv[rc+1]);\r\n\r\n// version 4 cards (sb 16) do 16 bit stereo\r\n\tif (dsp_version >= 4)\r\n\t{\r\n\t\tif (sc->sn.numchannels != 1)\r\n\t\t\tsc->sn.numchannels = 2;\r\n\t\tif (sc->sn.samplebytes != 1)\r\n\t\t\tsc->sn.samplebytes = 2;\r\n\t}\r\n// version 3 cards (sb pro) do 8 bit stereo\r\n\telse if (dsp_version == 3)\r\n\t{\r\n\t\tif (sc->sn.numchannels != 1)\r\n\t\t\tsc->sn.numchannels = 2;\r\n\t\tsc->sn.samplebytes = 1;\t\r\n\t}\r\n// v2 cards do 8 bit mono\r\n\telse\r\n\t{\r\n\t\tsc->sn.numchannels = 1;\r\n\t\tsc->sn.samplebytes = 1;\r\n\t}\r\n\r\n\tif (sc->sn.samplebytes == 2)\r\n\t\tsc->sn.sampleformat = QSF_S16;\r\n\telse\r\n\t\tsc->sn.sampleformat = QSF_U8;\r\n\r\n\tsc->Lock\t\t= SBLASTER_LockBuffer;\r\n\tsc->Unlock\t\t= SBLASTER_UnlockBuffer;\r\n\tsc->Shutdown\t= SBLASTER_Shutdown;\r\n\tsc->GetDMAPos\t= SBLASTER_GetDMAPos;\r\n\tsc->Submit\t\t= SBLASTER_Submit;\t\r\n\r\n\tsize = 4096;\r\n\r\n// allocate 8k and get a 4k-aligned buffer from it\r\n\tif (!dosmem_alloc(&dma_buffer_memory, size*2))\r\n\t{\r\n\t\tCon_Printf(\"Couldn't allocate sound dma buffer\");\r\n\t\treturn false;\r\n\t}\r\n\tdma_buffer_phys = ((dosmem_phys(&dma_buffer_memory) + size) & ~(size-1));\r\n\tdma_buffer = (short *)((qbyte*)dosmem_ptr(&dma_buffer_memory) + dma_buffer_phys-dosmem_phys(&dma_buffer_memory));\r\n\r\n\tdma_size = size;\r\n\tmemset(dma_buffer, 0, dma_size);\r\n\r\n\tsc->sn.samples = size/sc->sn.samplebytes;\r\n\tsc->sn.samplepos = 0;\r\n\tsc->sn.buffer = (unsigned char *) dma_buffer;\r\n\tsc->sn.samples = size/sc->sn.samplebytes;\r\n\r\n\tStartDMA();\r\n\tStartSB(sc);\r\n\r\n\tbecauseglobalssuck = sc;\r\n\r\n\treturn true;\r\n}\r\n\r\n\r\n\r\n\r\nstatic qboolean QDECL SBLASTER_Enumerate(void (QDECL *cb) (const char *drivername, const char *devicecode, const char *readablename))\r\n{\r\n\treturn false;\r\n}\r\n\r\nsounddriver_t SBLASTER_Output =\r\n{\r\n\tSDRVNAME,\r\n\tSBLASTER_InitCard,\r\n\tSBLASTER_Enumerate\r\n};\r\n\r\n"
  },
  {
    "path": "engine/client/snd_sdl.c",
    "content": "#include \"quakedef.h\"\n\n#if defined(HAVE_MIXER) || defined(VOICECHAT)\n#include \"winquake.h\"\n\n#ifdef DYNAMIC_SDL\n#define SDL_MAJOR_VERSION 2\n#define SDL_MINOR_VERSION 0\n#define SDL_PATCHLEVEL 5\n#define SDL_VERSIONNUM(X, Y, Z)\t\t\t((X)*1000 + (Y)*100 + (Z))\n#define SDL_COMPILEDVERSION\t\t\t\tSDL_VERSIONNUM(SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_PATCHLEVEL)\n#define SDL_VERSION_ATLEAST(X, Y, Z)\t(SDL_COMPILEDVERSION >= SDL_VERSIONNUM(X, Y, Z))\n\n\n//if we're not an sdl build, we probably want to link to sdl libraries dynamically or something.\n#include <stdint.h>\n#define SDL_AudioDeviceID uint32_t\n#define SDL_INIT_AUDIO          0x00000010\n#define SDL_INIT_NOPARACHUTE    0x00100000\n#define SDL_AUDIO_ALLOW_FREQUENCY_CHANGE    0x00000001\n#define SDL_AUDIO_ALLOW_FORMAT_CHANGE       0x00000002\n#define SDL_AUDIO_ALLOW_CHANNELS_CHANGE     0x00000004\n#define AUDIO_U8    0x0008\n#define AUDIO_S8    0x8008\n#define AUDIO_S16LSB    0x8010\n#define AUDIO_S16MSB    0x9010\n#define AUDIO_F32LSB    0x8120\n#define AUDIO_F32MSB    0x9120\n#if __BYTE_ORDER == __BIG_ENDIAN\n#define AUDIO_S16SYS    AUDIO_S16MSB\n#define AUDIO_F32SYS    AUDIO_F32MSB\n#else\n#define AUDIO_S16SYS    AUDIO_S16LSB\n#define AUDIO_F32SYS    AUDIO_F32LSB\n#endif\n#define SDLCALL QDECL\n\ntypedef uint16_t SDL_AudioFormat;\ntypedef void (SDLCALL *SDL_AudioCallback)(void *userdata, uint8_t *stream, int len);\n\ntypedef struct SDL_AudioSpec\n{\n\tint freq;\n\tSDL_AudioFormat format;\n\tuint8_t channels;\n\tuint8_t silence;\n\tuint16_t samples;\n\tuint16_t padding;\n\tuint32_t size;\n\tSDL_AudioCallback callback;\n\tvoid *userdata;\n} SDL_AudioSpec;\n\nstatic int (SDLCALL *SDL_Init)\t\t\t\t\t\t\t\t(uint32_t flags);\nstatic int (SDLCALL *SDL_InitSubSystem)\t\t\t\t\t\t(uint32_t flags);\nstatic SDL_AudioDeviceID (SDLCALL *SDL_OpenAudioDevice)\t\t(const char *dev, int iscapture, const SDL_AudioSpec *desired, SDL_AudioSpec *obtained, int allowed_changes);\nstatic void (SDLCALL *SDL_PauseAudioDevice)\t\t\t\t\t(SDL_AudioDeviceID fd, int pausestate);\nstatic void (SDLCALL *SDL_LockAudioDevice)\t\t\t\t\t(SDL_AudioDeviceID fd);\nstatic void (SDLCALL *SDL_UnlockAudioDevice)\t\t\t\t(SDL_AudioDeviceID fd);\nstatic int (SDLCALL *SDL_CloseAudioDevice)\t\t\t\t\t(SDL_AudioDeviceID fd);\nstatic int (SDLCALL *SDL_GetNumAudioDevices)\t\t\t\t(int iscapture);\nstatic const char *(SDLCALL *SDL_GetAudioDeviceName)\t\t(int index, int iscapture);\nstatic const char *(SDLCALL *SDL_GetError)\t\t\t\t\t(void);\n#if SDL_VERSION_ATLEAST(2,0,4)\nstatic int (SDLCALL *SDL_QueueAudio)\t\t\t\t\t\t(SDL_AudioDeviceID dev, const void *data, uint32_t len);\nstatic uint32_t (SDLCALL *SDL_GetQueuedAudioSize)\t\t\t(SDL_AudioDeviceID dev);\n#endif\n#if SDL_VERSION_ATLEAST(2,0,5)\nstatic uint32_t (SDLCALL *SDL_DequeueAudio)\t\t\t\t\t(SDL_AudioDeviceID dev, void *data, uint32_t len);\n#endif\nstatic dllfunction_t sdl_funcs[] =\n{\n\t{(void*)&SDL_Init, \"SDL_Init\"},\n\t{(void*)&SDL_InitSubSystem, \"SDL_InitSubSystem\"},\n\t{(void*)&SDL_OpenAudioDevice, \"SDL_OpenAudioDevice\"},\n\t{(void*)&SDL_PauseAudioDevice, \"SDL_PauseAudioDevice\"},\n\t{(void*)&SDL_LockAudioDevice, \"SDL_LockAudioDevice\"},\n\t{(void*)&SDL_UnlockAudioDevice, \"SDL_UnlockAudioDevice\"},\n\t{(void*)&SDL_CloseAudioDevice, \"SDL_CloseAudioDevice\"},\n\t{(void*)&SDL_GetNumAudioDevices, \"SDL_GetNumAudioDevices\"},\n\t{(void*)&SDL_GetAudioDeviceName, \"SDL_GetAudioDeviceName\"},\n\t{(void*)&SDL_GetError, \"SDL_GetError\"},\n#if SDL_VERSION_ATLEAST(2,0,4)\n\t{(void*)&SDL_QueueAudio, \"SDL_QueueAudio\"},\n\t{(void*)&SDL_GetQueuedAudioSize, \"SDL_GetQueuedAudioSize\"},\n#endif\n#if SDL_VERSION_ATLEAST(2,0,5)\n\t{(void*)&SDL_DequeueAudio, \"SDL_DequeueAudio\"},\n#endif\n\t{NULL, NULL}\n};\nstatic dllhandle_t *libsdl;\n#else\n\t#ifdef FTE_SDL3\n\t\t#include <SDL3/SDL.h>\n\t#else\n\t\t#include <SDL.h>\n\t#endif\n#endif\n#define SDRVNAME \"SDL\"\n\n//SDL calls a callback each time it needs to repaint the 'hardware' buffers\n//This results in extra latency due it needing to buffer that much data.\n//So we tell it a fairly pathetically sized buffer and try and get it to copy often\n//hopefully this lowers sound latency, and has no suddenly starting sounds and stuff.\n//It still has greater latency than direct access, of course.\n//On the plus side, SDL calls the callback from another thread. this means we can set up some tiny buffer size, and if we're mixing inside the callback then you can actually get lower latency than waiting for an entire frame (yay rtlights)\n\nstatic qboolean SSDL_InitAudio(void)\n{\n\tstatic qboolean inited = false;\n\tif (COM_CheckParm(\"-nosdlsnd\") || COM_CheckParm(\"-nosdl\"))\n\t\treturn false;\n#ifdef DYNAMIC_SDL\n\tif (!libsdl)\n\t{\n\t\tlibsdl = Sys_LoadLibrary(\"libSDL2-2.0.so.0\", sdl_funcs);\n\t\tif (!libsdl)\n\t\t\tlibsdl = Sys_LoadLibrary(\"libSDL2.so\", sdl_funcs);\t//maybe they have a dev package installed that fixes this mess.\n#ifdef _WIN32\n\t\tif (!libsdl)\n\t\t\tlibsdl = Sys_LoadLibrary(\"SDL2\", sdl_funcs);\n#endif\n\t\tif (libsdl)\n\t\t\tSDL_Init(SDL_INIT_NOPARACHUTE);\n\t\telse\n\t\t{\n\t\t\tCon_Printf(\"Unable to load libSDL2 library\\n\");\n\t\t\treturn false;\n\t\t}\n\t}\n#endif\n\n\tif (!inited)\n\t{\n#if SDL_VERSION_ATLEAST(3,0,0)\n\t\tif(SDL_InitSubSystem(SDL_INIT_AUDIO))\n#else\n\t\tif(0==SDL_InitSubSystem(SDL_INIT_AUDIO | SDL_INIT_NOPARACHUTE))\n#endif\n\t\t\tinited = true;\n\t\telse\n\t\t\tCon_Printf(\"Couldn't initialize SDL audio subsystem (%s)\\n\", SDL_GetError());\n\t}\n\treturn inited;\n}\n#else\n#define SDL_VERSION_ATLEAST(x,y,z) 0\n#endif\n\n#ifdef HAVE_MIXER\n#define SELFPAINT\nstatic void SSDL_Shutdown(soundcardinfo_t *sc)\n{\n\tCon_DPrintf(\"Shutdown SDL sound\\n\");\n\n#if SDL_VERSION_ATLEAST(3,0,0)\n\tif (sc->handle)\n\t{\n\t\tSDL_PauseAudioStreamDevice(sc->handle);\n\t\tSDL_DestroyAudioStream(sc->handle);\n\t}\n#elif SDL_VERSION_ATLEAST(2,0,0)\n\tif (sc->audio_fd)\n\t{\n\t\tSDL_PauseAudioDevice(sc->audio_fd, 1);\n\t\tSDL_CloseAudioDevice(sc->audio_fd);\n\t}\n#else\n\tSDL_CloseAudio();\n#endif\n#ifndef SELFPAINT\n\tif (sc->sn.buffer)\n\t\tfree(sc->sn.buffer);\n#endif\n\tsc->sn.buffer = NULL;\n}\nstatic unsigned int SSDL_Callback_GetDMAPos(soundcardinfo_t *sc)\n{\n\tsc->sn.samplepos = sc->snd_sent / sc->sn.samplebytes;\n\treturn sc->sn.samplepos;\n}\n\n//this function is called from inside SDL.\n//transfer the 'dma' buffer into the buffer it requests.\n#if SDL_VERSION_ATLEAST(3,0,0)\nstatic void SDLCALL SSDL3_Callback_Paint(void *userdata, SDL_AudioStream *stream, int additional, int total)\n{\n\tsoundcardinfo_t *sc = userdata;\n\tsc->sn.buffer = SDL_stack_alloc(Uint8, additional);\n\tif (sc->sn.buffer)\n\t{\n\t\tsc->sn.samples = additional / sc->sn.samplebytes;\n\t\tsc->samplequeue = sc->sn.samples;\n\t\tS_MixerThread(sc);\n\t\tsc->snd_sent += additional;\n\t\tSDL_PutAudioStreamData(sc->handle, sc->sn.buffer, additional);\n\t\tSDL_stack_free(sc->sn.buffer);\n\t\tsc->sn.buffer = NULL;\n\t}\n}\n#else\nstatic void VARGS SSDL_Callback_Paint(void *userdata, qbyte *stream, int len)\n{\n\tsoundcardinfo_t *sc = userdata;\n\n#ifdef SELFPAINT\n\tsc->sn.buffer = stream;\n\tsc->sn.samples = len / sc->sn.samplebytes;\n\tsc->samplequeue = sc->sn.samples;\n\tS_MixerThread(sc);\n\tsc->snd_sent += len;\n#else\n\tint buffersize = sc->sn.samples*sc->sn.samplebytes;\n\n\tif (len > buffersize)\n\t{\n\t\tlen = buffersize;\t//whoa nellie!\n\t}\n\n\tif (len + sc->snd_sent%buffersize > buffersize)\n\t{\t//buffer will wrap, fill in the rest\n\t\tmemcpy(stream, (char*)sc->sn.buffer + (sc->snd_sent%buffersize), buffersize - (sc->snd_sent%buffersize));\n\t\tstream += buffersize - sc->snd_sent%buffersize;\n\t\tsc->snd_sent += buffersize - sc->snd_sent%buffersize;\n\t\tlen -= buffersize - (sc->snd_sent%buffersize);\n\t\tif (len < 0)\n\t\t\treturn;\n\t}\t//and finish from the start\n\tmemcpy(stream, (char*)sc->sn.buffer + (sc->snd_sent%buffersize), len);\n\tsc->snd_sent += len;\n#endif\n}\n#endif\n\nstatic void *SSDL_Callback_LockBuffer(soundcardinfo_t *sc, unsigned int *sampidx)\n{\n#if SDL_VERSION_ATLEAST(3,0,0)\n\tSDL_LockAudioStream(sc->handle);\n#elif SDL_VERSION_ATLEAST(2,0,0)\n\tSDL_LockAudioDevice(sc->audio_fd);\n#else\n\tSDL_LockAudio();\n#endif\n\n\treturn sc->sn.buffer;\n}\n\nstatic void SSDL_Callback_UnlockBuffer(soundcardinfo_t *sc, void *buffer)\n{\n#if SDL_VERSION_ATLEAST(3,0,0)\n\tSDL_UnlockAudioStream(sc->handle);\n#elif SDL_VERSION_ATLEAST(2,0,0)\n\tSDL_UnlockAudioDevice(sc->audio_fd);\n#else\n\tSDL_UnlockAudio();\n#endif\n}\n\nstatic void SSDL_Callback_Submit(soundcardinfo_t *sc, int start, int end)\n{\n\t//SDL will call SSDL_Paint to paint when it's time, and the sound buffer is always there...\n}\n\n#if SDL_VERSION_ATLEAST(2,0,4) && !SDL_VERSION_ATLEAST(3,0,0)\nstatic unsigned int SSDL_Queue_GetDMAPos(soundcardinfo_t *sc)\n{\t//keep proper track of how much data has actually been sent to the audio device.\n\t//note that SDL may have already submitted more than this to the physical device.\n\t//note: if we don't mix enough data then sdl will mix 0s for us.\n\tuint32_t queued = SDL_GetQueuedAudioSize(sc->audio_fd);\n\textern cvar_t _snd_mixahead;\n\tint ahead = (_snd_mixahead.value*sc->sn.speed) - (queued / (sc->sn.samplebytes*sc->sn.numchannels));\n\tif (ahead < 0)\n\t\tahead = 0;\t//never behind\n\tsc->samplequeue = -1;\t//return value is a desired timestamp\n\treturn sc->sn.samplepos + ahead*sc->sn.numchannels;\n}\nstatic void *SSDL_Queue_LockBuffer(soundcardinfo_t *sc, unsigned int *sampidx)\n{\t//queuing uses private memory, so no need to lock\n\t*sampidx = 0;\t//don't bother ringing it.\n\treturn sc->sn.buffer;\n}\nstatic void SSDL_Queue_UnlockBuffer(soundcardinfo_t *sc, void *buffer)\n{\t//nor a need to unlock\n}\n\nstatic void SSDL_Queue_Submit(soundcardinfo_t *sc, int start, int end)\n{\n\tint bytecount = (end-start)*sc->sn.samplebytes*sc->sn.numchannels;\n\tSDL_QueueAudio(sc->audio_fd, sc->sn.buffer, bytecount);\n\n\tsc->sn.samplepos += bytecount/sc->sn.samplebytes;\n}\n#endif\n\nstatic qboolean QDECL SDL_InitCard(soundcardinfo_t *sc, const char *devicename)\n{\n\tSDL_AudioSpec desired, obtained;\n\n\tif(!SSDL_InitAudio())\n\t{\n\t\tCon_Printf(\"Couldn't initialize SDL audio subsystem\\n\");\n\t\treturn false;\n\t}\n\n#if SDL_VERSION_ATLEAST(3,0,0)\n\t{\n\t\tSDL_AudioDeviceID devid = SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK;\n\t\tSDL_AudioDeviceID *dev;\n\t\tint count, i;\n\n\t\t//figure out which device to use.\n\t\tif (!devicename || !*devicename)\n\t\t\tdev = NULL, count = 0;\t//not specified, just go with the default.\n\t\telse\n\t\t\tdev = SDL_GetAudioPlaybackDevices(&count);\n\t\tfor (i = 0; i < count; i++)\n\t\t{\n\t\t\tconst char *devname = SDL_GetAudioDeviceName(dev[i]);\n\t\t\tif (devname && !strcmp(devname, devicename))\n\t\t\t{\t//this one matched\n\t\t\t\tdevid = dev[i];\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\t//set up what we desire...\n\t\tsc->sn.samplebytes = 2;\n\t\tsc->sn.sampleformat = QSF_S16;\n\t\tdesired.channels = sc->sn.numchannels;\n\t\tdesired.freq = sc->sn.speed;\n\t\tswitch(sc->sn.sampleformat)\n\t\t{\n\t\tcase QSF_U8:\tdesired.format = SDL_AUDIO_U8; break;\n\t\tcase QSF_S8:\tdesired.format = SDL_AUDIO_S8; break;\n\t\tdefault:\n\t\tcase QSF_S16:\tdesired.format = SDL_AUDIO_S16; break;\n\t\tcase QSF_F32:\tdesired.format = SDL_AUDIO_F32; break;\n\t\t}\n\n\t\t//and then tailor that to what we're likely to actually get.\n\t\tif (SDL_GetAudioDeviceFormat(devid, &obtained, NULL))\n\t\t{\n\t\t\t//clamp the channels.\n\t\t\tif (desired.channels > obtained.channels)\n\t\t\t\tdesired.channels = obtained.channels;\n\t\t\t//if its a format that we support, just give it what it prefers.\n\t\t\tswitch(obtained.format)\n\t\t\t{\n\t\t\tcase SDL_AUDIO_U8:\t\tdesired.format = obtained.format; sc->sn.samplebytes = 1;\tsc->sn.sampleformat = QSF_U8;\tbreak;\n\t\t\tcase SDL_AUDIO_S8:\t\tdesired.format = obtained.format; sc->sn.samplebytes = 1;\tsc->sn.sampleformat = QSF_S8;\tbreak;\n\t\t\tcase SDL_AUDIO_S16LE:\n\t\t\tcase SDL_AUDIO_S16BE:\tdesired.format = obtained.format; sc->sn.samplebytes = 2;\tsc->sn.sampleformat = QSF_S16;\tbreak;\n\t\t\t/*case SDL_AUDIO_S32LE:\n\t\t\tcase SDL_AUDIO_S32BE:\tdesired.format = obtained.format; sc->sn.samplebytes = 4;\tsc->sn.sampleformat = QSF_S32;\tbreak;*/\n\t\t\tcase SDL_AUDIO_F32LE:\n\t\t\tcase SDL_AUDIO_F32BE:\tdesired.format = obtained.format; sc->sn.samplebytes = 4;\tsc->sn.sampleformat = QSF_F32;\tbreak;\n\t\t\tdefault:\t//just use what we originally asked for. let sdl figure out the conversions.\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\n\t\tsc->handle = SDL_OpenAudioDeviceStream(devid, &desired, SSDL3_Callback_Paint, sc);\n\t\tSDL_free(dev);\n\t}\n\tif (!sc->handle)\n\t\treturn false;\n\tif (SDL_GetAudioStreamFormat(sc->handle, &desired, &obtained))\n\t{\t//try to match the hardware format.\n\t\tif (desired.format != obtained.format || desired.freq != obtained.freq || desired.channels != obtained.channels)\n\t\t\tCon_Printf(\"SDL2: %ibit%s %ihz audio input\\n\", SDL_AUDIO_BITSIZE(desired.format), SDL_AUDIO_ISFLOAT(desired.format)?\" float\":\"\", desired.freq);\n\t\tCon_Printf(\"SDL2: %ibit%s %ihz audio output\\n\", SDL_AUDIO_BITSIZE(obtained.format), SDL_AUDIO_ISFLOAT(obtained.format)?\" float\":\"\", obtained.freq);\n\t\tswitch(desired.format)\n\t\t{\n\t\tcase SDL_AUDIO_U8:\t\tsc->sn.samplebytes = 1;\tsc->sn.sampleformat = QSF_U8;\tbreak;\n\t\tcase SDL_AUDIO_S8:\t\tsc->sn.samplebytes = 1;\tsc->sn.sampleformat = QSF_S8;\tbreak;\n\t\tcase SDL_AUDIO_S16LE:\n\t\tcase SDL_AUDIO_S16BE:\tsc->sn.samplebytes = 2;\tsc->sn.sampleformat = QSF_S16;\tbreak;\n\t\t/*case SDL_AUDIO_S32LE:\n\t\tcase SDL_AUDIO_S32BE:\tsc->sn.samplebytes = 4;\tsc->sn.sampleformat = QSF_S32;\tbreak;*/\n\t\tcase SDL_AUDIO_F32LE:\n\t\tcase SDL_AUDIO_F32BE:\tsc->sn.samplebytes = 4;\tsc->sn.sampleformat = QSF_F32;\tbreak;\n\t\tdefault:\t//just use what we originally asked for. let sdl figure out the conversions.\n\t\t\tbreak;\n\t\t}\n\t}\n\tsc->Lock\t\t= SSDL_Callback_LockBuffer;\n\tsc->Unlock\t\t= SSDL_Callback_UnlockBuffer;\n\tsc->Submit\t\t= SSDL_Callback_Submit;\n\tsc->Shutdown\t= SSDL_Shutdown;\n\tsc->GetDMAPos\t= SSDL_Callback_GetDMAPos;\n\tsc->selfpainting = true;\n\n\tSDL_ResumeAudioStreamDevice(sc->handle);\n#else\n\n\tmemset(&desired, 0, sizeof(desired));\n\n\tdesired.freq = sc->sn.speed;\n\tdesired.channels = sc->sn.numchannels;\t//fixme!\n\tdesired.samples = 0x0200;\t//'Good values seem to range between 512 and 8192 inclusive, depending on the application and CPU speed.'\n#if SDL_VERSION_ATLEAST(2,0,4)\n\tif (!snd_mixerthread.ival)\n\t\tdesired.callback = NULL;\n\telse\n#endif\n\t\tdesired.callback = (void*)SSDL_Callback_Paint;\n\tdesired.userdata = sc;\n\tmemcpy(&obtained, &desired, sizeof(obtained));\n\n#if SDL_VERSION_ATLEAST(2,0,0)\n\tdesired.format = AUDIO_F32SYS;\t//most modern audio APIs favour float audio nowadays.\n\tsc->audio_fd = SDL_OpenAudioDevice(devicename, false, &desired, &obtained, (sndcardinfo?0:SDL_AUDIO_ALLOW_FREQUENCY_CHANGE) | SDL_AUDIO_ALLOW_CHANNELS_CHANGE | SDL_AUDIO_ALLOW_FORMAT_CHANGE);\n\tif (!sc->audio_fd)\n\t{\n\t\tCon_Printf(\"SDL_OpenAudioDevice(%s) failed: couldn't open sound device (%s).\\n\", devicename?devicename:\"default\", SDL_GetError());\n\t\treturn false;\n\t}\n\tif (obtained.format != AUDIO_U8 && obtained.format != AUDIO_S8 && obtained.format != AUDIO_S16SYS && obtained.format != AUDIO_F32SYS)\n\t{\t//can't cope with that... try again but force the format (so SDL converts)\n\t\tSDL_CloseAudioDevice(sc->audio_fd);\n\t\tsc->audio_fd = SDL_OpenAudioDevice(devicename, false, &desired, &obtained, (sndcardinfo?0:SDL_AUDIO_ALLOW_FREQUENCY_CHANGE) | SDL_AUDIO_ALLOW_CHANNELS_CHANGE);\n\t}\n\tif (devicename)\n\t\tCon_Printf(\"Initing SDL audio device '%s'.\\n\", devicename);\n\telse\n\t\tCon_Printf(\"Initing default SDL audio device.\\n\");\n#else\n\tdesired.format = AUDIO_S16SYS;\n\tif (sndcardinfo)\n\t\treturn false;\t//SDL1 only supports opening one audio device at a time. the existing one might not be sdl, but I don't care.\n\tif (SDL_OpenAudio(&desired, &obtained) < 0)\n\t{\n\t\tCon_Printf(\"SDL_OpenAudio failed: couldn't open sound device (%s).\\n\", SDL_GetError());\n\t\treturn false;\n\t}\n\tCon_Printf(\"Initing default SDL audio device.\\n\");\n#endif\n\tsc->sn.numchannels = obtained.channels;\n\tsc->sn.speed = obtained.freq;\n\tsc->sn.samples = 32768;//*sc->sn.numchannels;\t//doesn't really matter, so long as it's higher than obtained.samples\n\n\tswitch(obtained.format)\n\t{\n\tcase AUDIO_U8:\n\t\tsc->sn.samplebytes = 1;\n\t\tsc->sn.sampleformat = QSF_U8;\n\t\tbreak;\n\tcase AUDIO_S8:\n\t\tsc->sn.samplebytes = 1;\n\t\tsc->sn.sampleformat = QSF_S8;\n\t\tbreak;\n\tcase AUDIO_S16SYS:\n\t\tsc->sn.samplebytes = 2;\n\t\tsc->sn.sampleformat = QSF_S16;\n\t\tbreak;\n#if SDL_MAJOR_VERSION >= 2\n\tcase AUDIO_F32SYS:\n\t\tsc->sn.samplebytes = 4;\n\t\tsc->sn.sampleformat = QSF_F32;\n\t\tbreak;\n#endif\n\tdefault:\n\t\t//unsupported. shouldn't have obtained that.\n#if SDL_VERSION_ATLEAST(2,0,0)\n\t\tSDL_CloseAudioDevice(sc->audio_fd);\n\t\tsc->audio_fd = 0;\n#else\n\t\tSDL_CloseAudio();\n#endif\n\t\tbreak;\n\t}\n\n\tCon_DPrintf(\"channels: %i\\n\", sc->sn.numchannels);\n\tCon_DPrintf(\"Speed: %i\\n\", sc->sn.speed);\n\tCon_DPrintf(\"Samplebits: %i\\n\", sc->sn.samplebytes*8);\n\tCon_DPrintf(\"SDLSamples: %i (low for latency)\\n\", obtained.samples);\n\tCon_DPrintf(\"FakeSamples: %i\\n\", sc->sn.samples);\n\n\tCon_DPrintf(\"Got sound %i-%i\\n\", obtained.freq, obtained.format);\n\n#if SDL_VERSION_ATLEAST(2,0,4)\n\tif (!obtained.callback)\n\t{\n\t\tsc->Lock\t\t= SSDL_Queue_LockBuffer;\n\t\tsc->Unlock\t\t= SSDL_Queue_UnlockBuffer;\n\t\tsc->Submit\t\t= SSDL_Queue_Submit;\n\t\tsc->Shutdown\t= SSDL_Shutdown;\n\t\tsc->GetDMAPos\t= SSDL_Queue_GetDMAPos;\n\n\t\tsc->sn.buffer = malloc(sc->sn.samples*sc->sn.samplebytes);\n\n\t\tCon_DPrintf(\"Using SDL audio queues\\n\");\n\t}\n\telse\n#endif\n\t{\n\t\tsc->Lock\t\t= SSDL_Callback_LockBuffer;\n\t\tsc->Unlock\t\t= SSDL_Callback_UnlockBuffer;\n\t\tsc->Submit\t\t= SSDL_Callback_Submit;\n\t\tsc->Shutdown\t= SSDL_Shutdown;\n\t\tsc->GetDMAPos\t= SSDL_Callback_GetDMAPos;\n\n#ifdef SELFPAINT\n\t\tsc->selfpainting = true;\n\t\tCon_DPrintf(\"Using SDL audio threading\\n\");\n#else\n\t\tsc->sn.buffer = malloc(sc->sn.samples*sc->sn.samplebytes);\n\t\tCon_DPrintf(\"Using SDL audio callbacks\\n\");\n#endif\n\t}\n\n#if SDL_VERSION_ATLEAST(2,0,0)\n\tSDL_PauseAudioDevice(sc->audio_fd, 0);\n#else\n\tSDL_PauseAudio(0);\n#endif\n#endif\n\n\treturn true;\n}\n\nstatic qboolean QDECL SDL_Enumerate(void (QDECL *cb) (const char *drivername, const char *devicecode, const char *readablename))\n{\n#if SDL_VERSION_ATLEAST(3,0,0)\n\tSDL_AudioDeviceID *dev;\n\tint count, i;\n\tif(SSDL_InitAudio())\n\t{\n\t\tdev = SDL_GetAudioPlaybackDevices(&count);\n\t\tfor (i = 0; i < count; i++)\n\t\t{\n\t\t\tconst char *devname = SDL_GetAudioDeviceName(dev[i]);\n\t\t\tif (devname)\n\t\t\t\tcb(SDRVNAME, devname, va(\"SDL:%s\", devname));\n\t\t}\n\t\tSDL_free(dev);\n\t}\n\treturn true;\n#elif SDL_VERSION_ATLEAST(2,0,0)\n\tint max, i;\n\tif(SSDL_InitAudio())\n\t{\n\t\tmax = SDL_GetNumAudioDevices(false);\n\t\tfor (i = 0; i < max; i++)\n\t\t{\n\t\t\tconst char *devname = SDL_GetAudioDeviceName(i, false);\n\t\t\tif (devname)\n\t\t\t\tcb(SDRVNAME, devname, va(\"SDL:%s\", devname));\n\t\t}\n\t}\n\treturn true;\n#else\n\treturn false;\n#endif\n}\n\nsounddriver_t SDL_Output =\n{\n\tSDRVNAME,\n\tSDL_InitCard,\n\tSDL_Enumerate\n};\n#endif\n\n#if SDL_VERSION_ATLEAST(2,0,5) && defined(VOICECHAT)\n//Requires SDL 2.0.5+ supposedly.\n//Bugging out for me on windows, with really low audio levels. looks like there's been some float->int conversion without a multiplier. asking for float audio gives stupidly low values too.\ntypedef struct\n{\n#if SDL_VERSION_ATLEAST(3,0,0)\n\tSDL_AudioStream *dev;\n#else\n\tSDL_AudioDeviceID dev;\n#endif\n} sdlcapture_t;\n\nstatic void QDECL SDL_Capture_Start(void *ctx)\n{\n\tsdlcapture_t *d = ctx;\n#if SDL_VERSION_ATLEAST(3,0,0)\n\tSDL_ResumeAudioStreamDevice(d->dev);\n#else\n\tSDL_PauseAudioDevice(d->dev, false);\n#endif\n}\n\nstatic void QDECL SDL_Capture_Stop(void *ctx)\n{\n\tsdlcapture_t *d = ctx;\n#if SDL_VERSION_ATLEAST(3,0,0)\n\tSDL_PauseAudioStreamDevice(d->dev);\n#else\n\tSDL_PauseAudioDevice(d->dev, true);\n#endif\n}\n\nstatic void QDECL SDL_Capture_Shutdown(void *ctx)\n{\n\tsdlcapture_t *d = ctx;\n#if SDL_VERSION_ATLEAST(3,0,0)\n\tSDL_DestroyAudioStream(d->dev);\n#else\n\tSDL_CloseAudioDevice(d->dev);\n#endif\n\tZ_Free(d);\n}\n\nstatic qboolean QDECL SDL_Capture_Enumerate(void (QDECL *callback) (const char *drivername, const char *devicecode, const char *readablename))\n{\n\tint i, count;\n\tif (SSDL_InitAudio())\n\t{\n#if SDL_VERSION_ATLEAST(3,0,0)\n\t\tSDL_AudioDeviceID *dev = SDL_GetAudioRecordingDevices(&count);\n\t\tfor (i = 0; i < count; i++)\n\t\t{\n\t\t\tconst char *name = SDL_GetAudioDeviceName(dev[i]);\n\t\t\tif (name)\n\t\t\t\tcallback(SDRVNAME, name, va(\"SDL:%s\", name));\n\t\t}\n\t\tSDL_free(dev);\n#else\n\t\tcount = SDL_GetNumAudioDevices(true);\n\t\tfor (i = 0; i < count; i++)\n\t\t{\n\t\t\tconst char *name = SDL_GetAudioDeviceName(i, true);\n\t\t\tif (name)\n\t\t\t\tcallback(SDRVNAME, name, va(\"SDL:%s\", name));\n\t\t}\n#endif\n\t}\n\treturn true;\n}\nstatic void *QDECL SDL_Capture_Init (int rate, const char *devname)\n{\n\tsdlcapture_t c, *r;\n\n\tif (SSDL_InitAudio())\n\t{\n#if SDL_VERSION_ATLEAST(3,0,0)\n\t\tint i, count;\n\t\tSDL_AudioSpec have;\n\t\tSDL_AudioDeviceID dev = SDL_AUDIO_DEVICE_DEFAULT_RECORDING;\t//assume the default device.\n\t\tSDL_AudioDeviceID *devs = SDL_GetAudioRecordingDevices(&count);\n\t\tif (devname && *devname)\n\t\tfor (i = 0; i < count; i++)\n\t\t{\n\t\t\tconst char *name = SDL_GetAudioDeviceName(devs[i]);\n\t\t\tif (name && !strcmp(name, devname))\n\t\t\t{\t//found it. use it instead of the default.\n\t\t\t\tdev = devs[i];\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tSDL_free(devs);\n\t\thave.format = SDL_AUDIO_S16;\n\t\thave.channels = 1;\n\t\thave.freq = rate;\n\t\tc.dev = SDL_OpenAudioDeviceStream(dev, &have, NULL, NULL);\n#else\n\t\tSDL_AudioSpec want, have;\n\t\tmemset(&want, 0, sizeof(want));\n\n\t\twant.freq = rate;\n\t\twant.format = AUDIO_S16SYS;\n\t\twant.channels = 1;\n\t\twant.samples = 256;\t//this seems to be chunk sizes rather than total buffer size, so lets keep it reasonably small for lower latencies\n\t\twant.callback = NULL;\n\n\t\tc.dev = SDL_OpenAudioDevice(devname, true, &want, &have, 0);\n#endif\n\t\tif (c.dev)\n\t\t{\n\t\t\tr = Z_Malloc(sizeof(*r));\n\t\t\t*r = c;\n\t\t\treturn r;\n\t\t}\n\t}\n\treturn NULL;\n}\n\n/*minbytes is a hint to not bother wasting time*/\nstatic unsigned int QDECL SDL_Capture_Update(void *ctx, unsigned char *buffer, unsigned int minbytes, unsigned int maxbytes)\n{\n\tsdlcapture_t *c = ctx;\n#if SDL_VERSION_ATLEAST(3,0,0)\n\tunsigned int queuedsize = SDL_GetAudioStreamAvailable(c->dev);\n\tif (queuedsize < minbytes)\n\t\treturn 0;\n\tif (queuedsize > maxbytes)\n\t\tqueuedsize = maxbytes;\n\n\tqueuedsize = SDL_GetAudioStreamData(c->dev, buffer, queuedsize);\n#else\n\tunsigned int queuedsize = SDL_GetQueuedAudioSize(c->dev);\n\tif (queuedsize < minbytes)\n\t\treturn 0;\n\tif (queuedsize > maxbytes)\n\t\tqueuedsize = maxbytes;\n\n\tqueuedsize = SDL_DequeueAudio(c->dev, buffer, queuedsize);\n#endif\n\treturn queuedsize;\n}\nsnd_capture_driver_t SDL_Capture =\n{\n\t1,\n\tSDRVNAME,\n\tSDL_Capture_Enumerate,\n\tSDL_Capture_Init,\n\tSDL_Capture_Start,\n\tSDL_Capture_Update,\n\tSDL_Capture_Stop,\n\tSDL_Capture_Shutdown\n};\n#endif\n\n"
  },
  {
    "path": "engine/client/snd_sndio.c",
    "content": "/*\r\n* Copyright (c) 2010 Jacob Meuser <jakemsr@sdf.lonestar.org>\r\n*\r\n* Permission to use, copy, modify, and distribute this software for any\r\n* purpose with or without fee is hereby granted, provided that the above\r\n* copyright notice and this permission notice appear in all copies.\r\n*\r\n* THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\r\n* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\r\n* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR\r\n* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\r\n* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN\r\n* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF\r\n* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\r\n*/\r\n/* Modifified for FTEQW by Alf Schlichting, a.schlichting@lemarit.com */\r\n/* note: this is for OpenBSD */\r\n\r\n#include \"quakedef.h\"\r\n#include \"sound.h\"\r\n\r\n#include <sys/types.h>\r\n#include <stdio.h>\r\n#include <stdlib.h>\r\n#include <unistd.h>\r\n#include <fcntl.h>\r\n#include <poll.h>\r\n#include <errno.h>\r\n\r\n#include <sndio.h>\r\n\r\nstruct sndio_private\r\n{\r\n\tstruct sio_hdl *hdl;\r\n\r\n\tunsigned char *dma_buffer;\r\n\tsize_t dma_buffer_size, dma_ptr;\r\n};\r\n\r\nstatic qboolean sndio_init(soundcardinfo_t *, const char *);\r\nstatic void *sndio_lock(soundcardinfo_t *);\r\nstatic void sndio_unlock(soundcardinfo_t *, void *);\r\nstatic void sndio_shutdown(soundcardinfo_t *);\r\nstatic unsigned int sndio_getdmapos(soundcardinfo_t *);\r\nstatic void sndio_submit(soundcardinfo_t *, int, int);\r\nstatic void sndio_setunderwater(soundcardinfo_t *sc, qboolean underwater);      //simply a stub. Any ideas how to actually implement this properly?\r\n\r\nstatic void sndio_setunderwater(soundcardinfo_t *sc, qboolean underwater)       //simply a stub. Any ideas how to actually implement this properly?\r\n{\r\n}\r\n\r\nstatic qboolean sndio_init(soundcardinfo_t *sc, const char *cardname)\r\n{\r\n\tstruct sndio_private *sp;\r\n\tstruct sio_par par;\r\n\tunsigned samp_per_buf;\r\n\tchar *s;\r\n\tint i;\r\n\r\n\tCon_DPrintf(\"sndio_init called\\n\");\r\n\tif (cardname && *cardname)\r\n\t\treturn false;\t//only support the default device for now.\r\n\r\n\tsp = calloc(sizeof(struct sndio_private), 1);\r\n\tif (sp == NULL)\r\n\t{\r\n\t\tCon_Printf(\"Could not get mem\");\r\n\t\treturn false;\r\n\t}\r\n\r\n\tCon_DPrintf(\"trying to open sp->hdl\\n\");\r\n\tsp->hdl = sio_open(SIO_DEVANY, SIO_PLAY, 1);\r\n\tif (sp->hdl == NULL)\r\n\t{\r\n\t\tCon_Printf(\"Could not open sndio device\\n\");\r\n\t\treturn false;\r\n\t}\r\n\tCon_DPrintf(\"Opened sndio\\n\");\r\n\tsc->GetDMAPos = sndio_getdmapos;\r\n\tsc->Submit = sndio_submit;\r\n\tsc->Shutdown = sndio_shutdown;\r\n\tsc->Lock = sndio_lock;\r\n\tsc->Unlock = sndio_unlock;\r\n\tsc->SetWaterDistortion = sndio_setunderwater;\r\n\tsc->handle = sp;\r\n\r\n\tsio_initpar(&par);\r\n\tpar.rate = sc->sn.speed;\r\n\tpar.bits = (sc->sn.samplebytes==1)?8:16;\r\n\tpar.sig = 1;\r\n\tpar.le = SIO_LE_NATIVE;\r\n\tpar.pchan = sc->sn.numchannels;\r\n\tpar.appbufsz = par.rate / 20;   /* 1/20 second latency */\r\n\r\n\tif (!sio_setpar(sp->hdl, &par) || !sio_getpar(sp->hdl, &par))\r\n\t{\r\n\t\tCon_Printf(\"Error setting audio parameters\\n\");\r\n\t\tsio_close(sp->hdl);\r\n\t\treturn false;\r\n\t}\r\n\tif ((par.pchan != 1 && par.pchan != 2) ||\r\n\t\t(par.bits != 16 || par.sig != 1))\r\n\t{\r\n\t\tCon_Printf(\"Could not set appropriate audio parameters\\n\");\r\n\t\tsio_close(sp->hdl);\r\n\t\treturn false;\r\n\t}\r\n\r\n\tif (par.bits == 16)\r\n\t{\r\n\t\tsc->sn.sampleformat = QSF_S16;\r\n\t\tsc->sn.samplebytes = 2;\r\n\t}\r\n\telse if (par.bits == 8)\r\n\t{\r\n\t\tsc->sn.sampleformat = QSF_U8;\r\n\t\tsc->sn.samplebytes = 1;\r\n\t}\r\n/*\tsc->sn.speed = par.rate;\r\n\tsc->sn.numchannels = par.pchan;\r\n\tsc->sn.samplebits = par.bits;\r\n*/\r\n\r\n\t/*\r\n\t* find the smallest power of two larger than the buffer size\r\n\t* and use it as the internal buffer's size\r\n\t*/\r\n\tfor (i = 1; i < par.appbufsz; i <<= 1)\r\n\t\t; /* nothing */\r\n\tsc->sn.samples = i * par.pchan;\r\n\r\n\tsp->dma_buffer_size = sc->sn.samples * sc->sn.samplebytes;\r\n\tsc->sn.buffer = calloc(1, sp->dma_buffer_size);\r\n\tif (sc->sn.buffer == NULL)\r\n\t{\r\n\t\tCon_Printf(\"Could not allocate audio ring buffer\\n\");\r\n\t\treturn false;\r\n\t}\r\n\tdma_ptr = 0;\r\n\tif (!sio_start(sp->hdl))\r\n\t{\r\n\t\tCon_Printf(\"Could not start audio\\n\");\r\n\t\tsio_close(sp->hdl);\r\n\t\treturn false;\r\n\t}\r\n\tsc->sn.samplepos = 0;\r\n\r\n\tCon_DPrintf(\"sc->sn.speed = %d, par.rate = %d\\n\", sc->sn.speed, par.rate);\r\n\tCon_DPrintf(\"sc->sn.samplebits = %d, par.bits = %d\\n\", sc->sn.samplebytes*8, par.bits);\r\n\tCon_DPrintf(\"sc->sn.numchannels = %d, par.pchan = %d\\n\", sc->sn.numchannels, par.pchan);\r\n\tCon_DPrintf(\"sc->sn.samples = %d, par.pchan = %d\\n\", sc->sn.samples, par.pchan);\r\n\tCon_DPrintf(\"dma_buffer_size = %d\\n\", sp->dma_buffer_size);\r\n\treturn true;\r\n}\r\n\r\n\r\nstatic void *\r\nsndio_lock(soundcardinfo_t *sc, unsigned int *sampidx)\r\n{\r\n\treturn sc->sn.buffer;\r\n}\r\n\r\nstatic void\r\nsndio_unlock(soundcardinfo_t *sci, void *p)\r\n{\r\n}\r\n\r\nstatic void\r\nsndio_shutdown(soundcardinfo_t *sc)\r\n{\r\n\tstruct sndio_private *sp = sc->handle;\r\n\r\n\tsio_close(sp->hdl);\r\n\tfree(sc->sn.buffer);\r\n\tsc->sn.buffer = NULL;\r\n\t*sc->name = '\\0';\r\n}\r\n\r\nstatic unsigned int\r\nsndio_getdmapos(soundcardinfo_t *sc)\r\n{\r\n\tstruct sndio_private *sp = sc->handle;\r\n\tsc->sn.samplepos = dma_ptr / sc->sn.samplebytes;\r\n\treturn sc->sn.samplepos;\r\n}\r\n\r\nstatic void\r\nsndio_submit(soundcardinfo_t *sc, int startcount, int endcount)\r\n{\r\n\tstruct pollfd pfd;\r\n\tstruct sndio_private *sp = sc->handle;\r\n\tsize_t count, todo, avail;\r\n\tint n;\r\n\r\n\tn = sio_pollfd(sp->hdl, &pfd, POLLOUT);\r\n\twhile (poll(&pfd, n, 0) < 0 && errno == EINTR)\r\n\t\t;\r\n\tif (!(sio_revents(sp->hdl, &pfd) & POLLOUT))\r\n\t\treturn;\r\n\tavail = sp->dma_buffer_size;\r\n\twhile (avail > 0)\r\n\t{\r\n\t\ttodo = sp->dma_buffer_size - dma_ptr;\r\n\t\tif (todo > avail)\r\n\t\t\ttodo = avail;\r\n\t\tcount = sio_write(sp->hdl, sc->sn.buffer + dma_ptr, todo);\r\n\t\tif (count == 0)\r\n\t\t\tbreak;\r\n\t\tdma_ptr += count;\r\n\t\tif (dma_ptr >= sp->dma_buffer_size)\r\n\t\t\tdma_ptr -= sp->dma_buffer_size;\r\n\t\tavail -= count;\r\n\t}\r\n}\r\n\r\nsounddriver_t SNDIO_AudioOutput =\r\n{\r\n\t\"sndio\",\r\n\tsndio_init,\r\n\tNULL\r\n};\r\n\r\n"
  },
  {
    "path": "engine/client/snd_wasapi.c",
    "content": "#include \"quakedef.h\"\n\n#if defined(AVAIL_WASAPI) && !defined(SERVERONLY)\n//wasapi is nice in that you can use it to bypass the windows audio mixer. hurrah for exclusive audio.\n//this should give slightly lower latency audio.\n//its otherwise not that interesting.\n\n//side note: wasapi does provide proper notifications for when a sound device is enabled/disabled, which is useful even if you're using directsound instead.\n//this means that we can finally restart the audio if someone plugs in a headset.\n#include \"winquake.h\"\n\n#include <audioclient.h>\n#include <mmdeviceapi.h>\n#include <audiopolicy.h>\n\n#define AUDIODRIVERNAME \"WASAPI\"\n\n\n#define REFTIMES_PER_SEC  10000000\n\n#define FORCE_DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \\\n        const GUID DECLSPEC_SELECTANY name \\\n                = { l, w1, w2, { b1, b2,  b3,  b4,  b5,  b6,  b7,  b8 } }\n#define FORCE_DEFINE_PROPERTYKEY(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8, pid) const PROPERTYKEY name = { { l, w1, w2, { b1, b2,  b3,  b4,  b5,  b6,  b7,  b8 } }, pid }\n\nFORCE_DEFINE_GUID(CLSID_MMDeviceEnumerator,\t\t\t0xBCDE0395, 0xE52F, 0x467C, 0x8E, 0x3D, 0xC4, 0x57, 0x92, 0x91, 0x69, 0x2E);\nFORCE_DEFINE_GUID(IID_IMMDeviceEnumerator,\t\t\t0xA95664D2, 0x9614, 0x4F35, 0xA7, 0x46, 0xDE, 0x8D, 0xB6, 0x36, 0x17, 0xE6);\nFORCE_DEFINE_GUID(IID_IAudioClient,\t\t\t\t\t0x1CB9AD4C, 0xDBFA, 0x4c32, 0xB1, 0x78, 0xC2, 0xF5, 0x68, 0xA7, 0x03, 0xB2);\nFORCE_DEFINE_GUID(IID_IAudioRenderClient,\t\t\t0xF294ACFC, 0x3146, 0x4483, 0xA7, 0xBF, 0xAD, 0xDC, 0xA7, 0xC2, 0x60, 0xE2);\n\nFORCE_DEFINE_GUID(KSDATAFORMAT_SUBTYPE_PCM,\t\t\t0x00000001, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);\nFORCE_DEFINE_GUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT,\t0x00000003, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);\n\nstatic cvar_t wasapi_forcerate\t\t= CVARD(\"wasapi_forcerate\", \"0\", \"Attempts to force snd_khz instead of using the system's default channel count.\\nFor this to work, you will need to set wasapi_exclusive 1\");\nstatic cvar_t wasapi_forcechannels\t= CVARD(\"wasapi_forcechannels\", \"0\", \"Attempts to force snd_numchannels instead of using the system's default channel count.\\nFor this to work, you will need to set wasapi_exclusive 1\");\nstatic cvar_t wasapi_exclusive\t\t= CVARD(\"wasapi_exclusive\", \"0\", \"When set, attempts to take exclusive control of the output device, to the detriment of other programs (causing errors or even crashes in them).\\nExclusive mode leaves the game free to change the hardware's playback details, instead of being required to use a single system-wide 'mixer' rate.\\nIt should also reduce latency a little.\");\nstatic cvar_t wasapi_buffersize\t\t= CVAR(\"wasapi_buffersize\", \"0.01\");\nstatic void QDECL WASAPI_RegisterCvars(void)\n{\n\tCvar_Register(&wasapi_forcerate, \"WASAPI audio output\");\n\tCvar_Register(&wasapi_forcechannels, \"WASAPI audio output\");\n\tCvar_Register(&wasapi_exclusive, \"WASAPI audio output\");\n\tCvar_Register(&wasapi_buffersize, \"WASAPI audio output\");\n}\n\nstatic void *WASAPI_Lock(soundcardinfo_t *sc, unsigned int *startoffset)\n{\n\treturn sc->sn.buffer;\n}\nstatic void WASAPI_Unlock(soundcardinfo_t *sc, void *buffer)\n{\n\t//no need to do anything\n}\nstatic void WASAPI_Submit(soundcardinfo_t *sc, int start, int end)\n{\n\t//submit happens outside the mixer\n}\nstatic unsigned int WASAPI_GetDMAPos(soundcardinfo_t *sc)\n{\n\tsc->sn.samplepos = sc->snd_sent;\n\treturn sc->sn.samplepos;\n}\nstatic void WASAPI_Shutdown(soundcardinfo_t *sc)\n{\n\tsc->Shutdown = NULL;\n\tSys_WaitOnThread(sc->thread);\n\tsc->thread = NULL;\n}\n\nstatic qboolean WASAPI_AcceptableFormat(soundcardinfo_t *sc, IAudioClient *dev, WAVEFORMATEX *pwfx, qboolean isexclusive)\n{\n\tif (pwfx->wFormatTag == WAVE_FORMAT_EXTENSIBLE && !memcmp(&((WAVEFORMATEXTENSIBLE*)pwfx)->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, sizeof(GUID)) && pwfx->wBitsPerSample == 32)\n\t{\t//oo, floating point audio. I guess this means we can have fun with clamping, right?\n\t\tsc->sn.samplebytes = 4;\n\t\tsc->sn.sampleformat = QSF_F32;\n\t}\n\telse if (pwfx->wFormatTag == WAVE_FORMAT_EXTENSIBLE && memcmp(&((WAVEFORMATEXTENSIBLE*)pwfx)->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM, sizeof(GUID))) \n\t{\n\t\tCon_Printf(\"WASAPI: unsupported sample type\\n\");\n\t\treturn false;\t//we only support pcm / floats\n\t}\n\telse if (pwfx->wBitsPerSample == 8)\n\t{\n\t\tsc->sn.samplebytes = 1;\n\t\tsc->sn.sampleformat = QSF_U8;\n\t}\n\telse if (pwfx->wBitsPerSample == 16)\n\t{\n\t\tsc->sn.samplebytes = 2;\n\t\tsc->sn.sampleformat = QSF_S16;\n\t}\n\telse\n\t{\n\t\tCon_Printf(\"WASAPI: unsupported sample size\\n\");\n\t\treturn false;\t//unsupported bit formats\n\t}\n\n\tif (pwfx->nChannels > MAXSOUNDCHANNELS)\n\t{\n\t\tCon_Printf(\"WASAPI: too many channels\\n\");\n\t\treturn false;\n\t}\n\n\tsc->sn.numchannels = pwfx->nChannels;\n\tsc->sn.speed = pwfx->nSamplesPerSec;\n\n\tCon_Printf(\"WASAPI: %i channel %ibit %ukhz%s\\n\", sc->sn.numchannels, pwfx->wBitsPerSample, (unsigned int)pwfx->nSamplesPerSec, isexclusive?\" exclusive\":\" non-exclusive\");\n\treturn true;\n}\nstatic qboolean WASAPI_DetermineFormat(soundcardinfo_t *sc, IAudioClient *dev, qboolean exclusive, WAVEFORMATEX **ret)\n{\n\tWAVEFORMATEX *pwfx;\n\n\tif (!SUCCEEDED(dev->lpVtbl->GetMixFormat(dev, &pwfx)))\n\t\treturn false;\n\n\tif (snd_speed || wasapi_forcerate.ival)\n\t{\t//if some other driver has already committed us to a set rate, we need to drive wasapi at that rate too.\n\t\t//this may cause failures later in this function.\n\t\tCon_Printf(\"WASAPI: overriding sampler rate\\n\");\n\t\tpwfx->nSamplesPerSec = snd_speed;\n\t\tpwfx->nAvgBytesPerSec = pwfx->nSamplesPerSec * pwfx->nBlockAlign;\n\t}\n\n\tif (wasapi_forcechannels.ival)\n\t{\n\t\tCon_Printf(\"WASAPI: overriding channels\\n\");\n\n\t\tif (sc->sn.numchannels >= 8)\n\t\t{\n\t\t\tpwfx->nChannels = 8;\n\t\t\tif (pwfx->wFormatTag == WAVE_FORMAT_EXTENSIBLE)\n\t\t\t\t((WAVEFORMATEXTENSIBLE*)pwfx)->dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | SPEAKER_FRONT_LEFT_OF_CENTER | SPEAKER_FRONT_RIGHT_OF_CENTER;\n\t\t}\n\t\tif (sc->sn.numchannels >= 6)\n\t\t{\n\t\t\tpwfx->nChannels = 6;\n\t\t\tif (pwfx->wFormatTag == WAVE_FORMAT_EXTENSIBLE)\n\t\t\t\t((WAVEFORMATEXTENSIBLE*)pwfx)->dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT  | SPEAKER_BACK_RIGHT;\n\t\t}\n\t\telse if (sc->sn.numchannels >= 4)\n\t\t{\n\t\t\tpwfx->nChannels = 4;\n\t\t\tif (pwfx->wFormatTag == WAVE_FORMAT_EXTENSIBLE)\n\t\t\t\t((WAVEFORMATEXTENSIBLE*)pwfx)->dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT  | SPEAKER_BACK_RIGHT;\n\t\t}\n\t\telse if (sc->sn.numchannels >= 2)\n\t\t{\n\t\t\tpwfx->nChannels = 2;\n\t\t\tif (pwfx->wFormatTag == WAVE_FORMAT_EXTENSIBLE)\n\t\t\t\t((WAVEFORMATEXTENSIBLE*)pwfx)->dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tpwfx->nChannels = 1;\n\t\t\tif (pwfx->wFormatTag == WAVE_FORMAT_EXTENSIBLE)\n\t\t\t\t((WAVEFORMATEXTENSIBLE*)pwfx)->dwChannelMask = SPEAKER_FRONT_CENTER;\n\t\t}\n\t\tpwfx->nBlockAlign = pwfx->wBitsPerSample/8 * pwfx->nChannels;\n\t\tpwfx->nAvgBytesPerSec = pwfx->nSamplesPerSec * pwfx->nBlockAlign;\n\t}\n\n\tif (!exclusive)\n\t{\n\t\tWAVEFORMATEX *pwfx2;\n\t\tif (SUCCEEDED(dev->lpVtbl->IsFormatSupported(dev, exclusive?AUDCLNT_SHAREMODE_EXCLUSIVE:AUDCLNT_SHAREMODE_SHARED, pwfx, &pwfx2)))\n\t\t{\n\t\t\tif (pwfx2)\n\t\t\t{\n\t\t\t\tCoTaskMemFree(pwfx);\n\t\t\t\tpwfx = pwfx2;\n\t\t\t}\n\t\t}\n\t\tif (!WASAPI_AcceptableFormat(sc, dev, pwfx, false))\n\t\t\treturn false;\n\t}\n\telse\n\t{\n\t\tif (FAILED(dev->lpVtbl->IsFormatSupported(dev, exclusive?AUDCLNT_SHAREMODE_EXCLUSIVE:AUDCLNT_SHAREMODE_SHARED, pwfx, NULL)))\n\t\t{\n\t\t\t//try to switch over to 16bit pcm\n\t\t\tpwfx->wBitsPerSample = 16;\n\t\t\tpwfx->nBlockAlign = pwfx->wBitsPerSample/8 * pwfx->nChannels;\n\t\t\tpwfx->nAvgBytesPerSec = pwfx->nSamplesPerSec * pwfx->nBlockAlign;\n\t\t\tif (pwfx->wFormatTag == WAVE_FORMAT_EXTENSIBLE)\n\t\t\t{\n\t\t\t\t((WAVEFORMATEXTENSIBLE*)pwfx)->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;\n\t\t\t\t((WAVEFORMATEXTENSIBLE*)pwfx)->Samples.wValidBitsPerSample = pwfx->wBitsPerSample;\n\t\t\t}\n\n\t\t\tif (FAILED(dev->lpVtbl->IsFormatSupported(dev, exclusive?AUDCLNT_SHAREMODE_EXCLUSIVE:AUDCLNT_SHAREMODE_SHARED, pwfx, NULL)))\n\t\t\t{\n\t\t\t\t//fixme: try float audio...\n\n\t\t\t\t//try to switch over to 24bit pcm (although with no more 16bit audio)\n/*\t\t\t\tpwfx->wBitsPerSample = 24;\n\t\t\t\tpwfx->nBlockAlign = pwfx->wBitsPerSample/8 * pwfx->nChannels;\n\t\t\t\tpwfx->nAvgBytesPerSec = pwfx->nSamplesPerSec * pwfx->nBlockAlign;\n\t\t\t\tif (pwfx->wFormatTag == WAVE_FORMAT_EXTENSIBLE)\n\t\t\t\t\t((WAVEFORMATEXTENSIBLE*)pwfx)->Samples.wValidBitsPerSample = 16;\n\t\t\t\tif (FAILED(dev->lpVtbl->IsFormatSupported(dev, exclusive?AUDCLNT_SHAREMODE_EXCLUSIVE:AUDCLNT_SHAREMODE_SHARED, pwfx, NULL)))\n*/\n\t\t\t\t{\n\t\t\t\t\tCon_Printf(\"WASAPI: IsFormatSupported failed\\n\");\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (!WASAPI_AcceptableFormat(sc, dev, pwfx, true))\n\t\t\treturn false;\n\t}\n\n\t*ret = pwfx;\n\treturn true;\n}\n\nstatic IMMDevice *WASAPI_GetDevice(soundcardinfo_t *sc)\n{\n\tIMMDeviceEnumerator *pEnumerator = NULL;\n\tIMMDevice *pDevice = NULL;\n\tCoInitialize(NULL);\t//sigh.\n\tif (SUCCEEDED(CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, &IID_IMMDeviceEnumerator, (void**)&pEnumerator)))\n\t{\n\t\tif (*sc->name)\n\t\t{\n\t\t\tWCHAR wname[256];\n\t\t\tpEnumerator->lpVtbl->GetDevice(pEnumerator, widen(wname, sizeof(wname), sc->name), &pDevice);\n\t\t}\n\t\telse\n\t\t\tpEnumerator->lpVtbl->GetDefaultAudioEndpoint(pEnumerator, eRender, eConsole, &pDevice);\n\t\tpEnumerator->lpVtbl->Release(pEnumerator);\n\t}\n\treturn pDevice;\n}\nstatic int WASAPI_Thread(void *arg)\n{\n\tsoundcardinfo_t *sc = arg;\n\tqboolean inited = false;\n\n//\tREFERENCE_TIME hnsRequestedDuration = REFTIMES_PER_SEC;\n\tIAudioClient *pAudioClient = NULL;\n\tIAudioRenderClient *pRenderClient = NULL;\n\tUINT32 bufferFrameCount = 0;\n\tHANDLE hEvent = NULL;\n\tWAVEFORMATEX *pwfx;\n\n\tqboolean exclusive = wasapi_exclusive.ival;\n\n\tvoid *cond = sc->handle;\n\n\t//main thread will wait for us to finish initing, so lets do that...\n\tIMMDevice *pDevice = WASAPI_GetDevice(sc);\n\tif (pDevice)\n\t{\n\t\tif (SUCCEEDED(pDevice->lpVtbl->Activate(pDevice, &IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&pAudioClient)))\n\t\t{\n\t\t\tif (!WASAPI_DetermineFormat(sc, pAudioClient, exclusive, &pwfx))\n\t\t\t{\n\t\t\t\tCon_Printf(\"WASAPI: unable to determine mutually supported audio format\\n\");\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (sc->sn.samplebytes && (!snd_speed || sc->sn.speed == snd_speed))\n\t\t\t\t{\n\t\t\t\t\tHRESULT hr;\n\t\t\t\t\tREFERENCE_TIME buffersize = REFTIMES_PER_SEC * wasapi_buffersize.value;\n\t\t\t\t\tif (exclusive)\n\t\t\t\t\t\tpAudioClient->lpVtbl->GetDevicePeriod(pAudioClient, NULL, &buffersize);\n\n\t\t\t\t\thr = pAudioClient->lpVtbl->Initialize(pAudioClient, exclusive?AUDCLNT_SHAREMODE_EXCLUSIVE:AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_EVENTCALLBACK, buffersize, (exclusive?buffersize:0), pwfx, NULL);\n\n\t\t\t\t\tif (hr == AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED)\n\t\t\t\t\t{\t//this is stupid, but does what the documentation says should be done.\n\t\t\t\t\t\tif (SUCCEEDED(pAudioClient->lpVtbl->GetBufferSize(pAudioClient, &bufferFrameCount)))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (pAudioClient)\n\t\t\t\t\t\t\t\tpAudioClient->lpVtbl->Release(pAudioClient);\n\t\t\t\t\t\t\tpAudioClient = NULL;\n\t\t\t\t\t\t\tif (SUCCEEDED(pDevice->lpVtbl->Activate(pDevice, &IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&pAudioClient)))\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tbuffersize = (REFERENCE_TIME)((10000.0 * 1000 / pwfx->nSamplesPerSec * bufferFrameCount) + 0.5);\n\t\t\t\t\t\t\t\thr = pAudioClient->lpVtbl->Initialize(pAudioClient, exclusive?AUDCLNT_SHAREMODE_EXCLUSIVE:AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_EVENTCALLBACK, buffersize, (exclusive?buffersize:0), pwfx, NULL);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif (SUCCEEDED(hr))\n\t\t\t\t\t{\n\t\t\t\t\t\thEvent = CreateEvent(NULL, FALSE, FALSE, NULL);\n\t\t\t\t\t\tif (hEvent)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tpAudioClient->lpVtbl->SetEventHandle(pAudioClient, hEvent);\n\t\t\t\t\t\t\tif (SUCCEEDED(pAudioClient->lpVtbl->GetBufferSize(pAudioClient, &bufferFrameCount)))\n\t\t\t\t\t\t\tif (SUCCEEDED(pAudioClient->lpVtbl->GetService(pAudioClient, &IID_IAudioRenderClient, (void**)&pRenderClient)))\n\t\t\t\t\t\t\t\tinited = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tswitch(hr)\n\t\t\t\t\t\t{\n\t\t\t\t\t\tcase AUDCLNT_E_UNSUPPORTED_FORMAT:\n\t\t\t\t\t\t\tCon_Printf(\"WASAPI Initialize: AUDCLNT_E_UNSUPPORTED_FORMAT\\n\");\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED:\n\t\t\t\t\t\t\tCon_Printf(\"WASAPI Initialize: AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED\\n\");\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase AUDCLNT_E_EXCLUSIVE_MODE_ONLY:\n\t\t\t\t\t\t\tCon_Printf(\"WASAPI Initialize: AUDCLNT_E_EXCLUSIVE_MODE_ONLY\\n\");\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase AUDCLNT_E_DEVICE_IN_USE:\n\t\t\t\t\t\t\tCon_Printf(\"WASAPI Initialize: AUDCLNT_E_DEVICE_IN_USE\\n\");\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED:\n\t\t\t\t\t\t\tCon_Printf(\"WASAPI Initialize: AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED\\n\");\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase E_INVALIDARG:\n\t\t\t\t\t\t\tCon_Printf(\"WASAPI Initialize: E_INVALIDARG\\n\");\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\tCon_Printf(\"pAudioClient->lpVtbl->Initialize failed (%x)\\n\", (unsigned int)hr);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tCoTaskMemFree(pwfx);\n\t\t\t}\n\t\t}\n\t\tpDevice->lpVtbl->Release(pDevice);\n\t\tpDevice = NULL;\n\t}\n\n\tif (inited)\n\t\tsc->Shutdown\t= WASAPI_Shutdown;\n\telse\n\t\tCon_Printf(\"Unable to initialise WASAPI\\n\");\n\tsc->Lock\t\t= WASAPI_Lock;\n\tsc->Unlock\t\t= WASAPI_Unlock;\n\tsc->Submit\t\t= WASAPI_Submit;\n\tsc->GetDMAPos\t= WASAPI_GetDMAPos;\n\t\n\t//wake up the main thread now that we know if it worked.\n\tSys_ConditionSignal(cond);\n\n\t//extra crap to get the OS to favour waking us up on demand.\n\t{\n\t\tHANDLE (WINAPI *pAvSetMmThreadCharacteristics)(LPCTSTR TaskName, LPDWORD TaskIndex);\n\t\tdllfunction_t funcs[] = {{(void*)&pAvSetMmThreadCharacteristics, \"AvSetMmThreadCharacteristics\"}, {NULL}};\n\t\tDWORD taskIndex = 0;\n\n\t\tif (Sys_LoadLibrary(\"avrt.dll\", funcs))\n\t\t\tpAvSetMmThreadCharacteristics(TEXT(\"Pro Audio\"), &taskIndex);\n\t}\n\n\twhile(sc->Shutdown != NULL)\n\t{\n\t\tUINT32 numFramesPadding = 0;\n\t\tif (exclusive || SUCCEEDED(pAudioClient->lpVtbl->GetCurrentPadding(pAudioClient, &numFramesPadding)))\n\t\t{\n\t\t\tUINT32 numFramesAvailable = bufferFrameCount - numFramesPadding;\n\t\t\tBYTE *pData;\n\t\t\tif (SUCCEEDED(pRenderClient->lpVtbl->GetBuffer(pRenderClient, numFramesAvailable, &pData)))\n\t\t\t{\n\t\t\t\tsc->sn.buffer = pData;\n\t\t\t\tsc->sn.samples = numFramesAvailable * sc->sn.numchannels;\n\t\t\t\tsc->samplequeue = sc->sn.samples;\n\t\t\t\tS_MixerThread(sc);\n\t\t\t\tsc->snd_sent += numFramesAvailable * sc->sn.numchannels;\n\n\t\t\t\tpRenderClient->lpVtbl->ReleaseBuffer(pRenderClient, numFramesAvailable, 0);\n\t\t\t}\n\t\t}\n\n\t\tif (inited)\n\t\t{\n\t\t\tpAudioClient->lpVtbl->Start(pAudioClient);\n\t\t\tinited = false;\n\t\t}\n\n\t\tif (hEvent && WaitForSingleObject(hEvent, 2000) != WAIT_OBJECT_0)\n\t\t{\n\t\t\tCon_Printf(\"WASAPI timeout\\n\");\n\t\t\tbreak;\n\t\t}\n\n\t\t/* Quote:\n\t\tOn NT (Win2K and XP) the cursors in SW buffers (and HW buffers on some devices) move in 10ms increments, so calling GetCurrentPosition() every 10ms is ideal.\n\t\tCalling it more often than every 5ms will cause some perf degradation.\n\t\t*/\n//\t\tSleep(10);\n\t}\n\n\tif (inited)\n\t\tpAudioClient->lpVtbl->Stop(pAudioClient);\n\n\tif (pRenderClient)\n\t\tpRenderClient->lpVtbl->Release(pRenderClient);\n\tif (pAudioClient)\n\t\tpAudioClient->lpVtbl->Release(pAudioClient);\n\treturn 0;\n}\n\nstatic qboolean QDECL WASAPI_InitCard (soundcardinfo_t *sc, const char *cardname)\n{\n\tvoid *cond;\n\tQ_strncpyz(sc->name, cardname?cardname:\"\", sizeof(sc->name));\n\n\tsc->selfpainting = true;\n\tsc->handle = cond = Sys_CreateConditional();\n\tSys_LockConditional(cond);\n\tsc->thread = Sys_CreateThread(\"wasapimixer\", WASAPI_Thread, sc, THREADP_NORMAL, 0);\n\tif (!sc->thread)\n\t{\n\t\tCon_Printf (\"Unable to create sound mixing thread\\n\");\n\t\treturn false;\n\t}\n//\tMessageBox(0,\"main thread waiting\", \"...\", 0);\n\n\t//wait for the thread to finish (along with all its error con printfs etc\n\tif (!Sys_ConditionWait(cond))\n\t\tCon_Printf (\"Looks like the sound thread isn't starting up\\n\");\n\tSys_UnlockConditional(cond);\n\tSys_DestroyConditional(cond);\n\tCOM_MainThreadWork();\t//flush any prints from the worker thread, so that things make sense\n\n\tif (sc->Shutdown == NULL)\n\t{\n\t\tSys_WaitOnThread(sc->thread);\n\t\tsc->thread = NULL;\n\t\treturn false;\n\t}\n\treturn true;\n}\n\n\n/*I HATE C++ APIS! THEY'RE ANNOYING AS HELL*/\nstatic void WASAPI_DeviceChanged(void *ctx, void *data, size_t a, size_t b)\n{\n\tS_EnumerateDevices();\n\tif (data)\n\t{\n\t\tchar *msg = data;\n\t\tCon_Printf(\"%s\", msg);\n\t\tCbuf_AddText(\"\\nsnd_restart\\n\", RESTRICT_LOCAL);\n\t}\n}\nstatic HRESULT\tSTDMETHODCALLTYPE WASAPI_Notifications_QueryInterface(IMMNotificationClient * This,REFIID riid,void **ppvObject)\t\t\t\t\t\t\t\t\t{*ppvObject = NULL;return E_NOINTERFACE;}\nstatic ULONG\tSTDMETHODCALLTYPE WASAPI_Notifications_AddRef(IMMNotificationClient * This)\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{return 1;}\nstatic ULONG\tSTDMETHODCALLTYPE WASAPI_Notifications_Release(IMMNotificationClient * This)\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{return 1;}\nstatic HRESULT\tSTDMETHODCALLTYPE WASAPI_Notifications_OnDeviceStateChanged(IMMNotificationClient * This,LPCWSTR pwstrDeviceId,DWORD dwNewState)\t\t\t\t\t{COM_AddWork(WG_MAIN, WASAPI_DeviceChanged, NULL, NULL, 0, 0); return S_OK;}\nstatic HRESULT\tSTDMETHODCALLTYPE WASAPI_Notifications_OnDeviceAdded(IMMNotificationClient * This,LPCWSTR pwstrDeviceId)\t\t\t\t\t\t\t\t\t\t\t{COM_AddWork(WG_MAIN, WASAPI_DeviceChanged, NULL, NULL, 0, 0); return S_OK;}\nstatic HRESULT\tSTDMETHODCALLTYPE WASAPI_Notifications_OnDeviceRemoved(IMMNotificationClient * This,LPCWSTR pwstrDeviceId)\t\t\t\t\t\t\t\t\t\t\t{COM_AddWork(WG_MAIN, WASAPI_DeviceChanged, NULL, NULL, 0, 0); return S_OK;}\nstatic HRESULT\tSTDMETHODCALLTYPE WASAPI_Notifications_OnDefaultDeviceChanged(IMMNotificationClient * This,EDataFlow flow,ERole role,LPCWSTR pwstrDefaultDeviceId)\t{COM_AddWork(WG_MAIN, WASAPI_DeviceChanged, NULL, \"Default audio device changed. Restarting audio.\\n\", 0, 0); return S_OK;}\nstatic HRESULT\tSTDMETHODCALLTYPE WASAPI_Notifications_OnPropertyValueChanged(IMMNotificationClient * This,LPCWSTR pwstrDeviceId,const PROPERTYKEY key)\t\t\t\t{COM_AddWork(WG_MAIN, WASAPI_DeviceChanged, NULL, NULL, 0, 0); return S_OK;}\nstatic CONST_VTBL IMMNotificationClientVtbl WASAPI_NotificationsVtbl =\n{\n\tWASAPI_Notifications_QueryInterface,\n\tWASAPI_Notifications_AddRef,\n\tWASAPI_Notifications_Release,\n\tWASAPI_Notifications_OnDeviceStateChanged,\n\tWASAPI_Notifications_OnDeviceAdded,\n\tWASAPI_Notifications_OnDeviceRemoved,\n\tWASAPI_Notifications_OnDefaultDeviceChanged,\n\tWASAPI_Notifications_OnPropertyValueChanged\n};\nstatic IMMNotificationClient WASAPI_Notifications =\n{\n\t&WASAPI_NotificationsVtbl\n};\n\nstatic qboolean QDECL WASAPI_Enumerate (void (QDECL *callback) (const char *drivername, const char *devicecode, const char *readablename))\n{\n\tFORCE_DEFINE_PROPERTYKEY(PKEY_Device_FriendlyName,           0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 14);    // DEVPROP_TYPE_STRING\n\n\tstatic IMMDeviceEnumerator *pEnumerator = NULL;\n\tIMMDeviceCollection *pCollection = NULL;\n\tCoInitialize(NULL);\n\tif (!pEnumerator)\n\t{\n\t\tif (SUCCEEDED(CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, &IID_IMMDeviceEnumerator, (void**)&pEnumerator)))\n\t\t{\n\t\t\tpEnumerator->lpVtbl->RegisterEndpointNotificationCallback(pEnumerator, &WASAPI_Notifications);\n\t\t}\n\t}\n\n\tif (pEnumerator)\n\t{\n\t\tif (SUCCEEDED(pEnumerator->lpVtbl->EnumAudioEndpoints(pEnumerator, eRender, DEVICE_STATE_ACTIVE, &pCollection)))\n\t\t{\n\t\t\tIMMDevice *pEndpoint;\n\t\t\tIPropertyStore *pProps;\n\t\t\tLPWSTR pwszID;\n\t\t\tUINT count, i;\n\t\t\tif (FAILED(pCollection->lpVtbl->GetCount(pCollection, &count)))\n\t\t\t\tcount = 0;\n\t\t\tfor (i = 0; i < count; i++)\n\t\t\t{\n\t\t\t\tif (SUCCEEDED(pCollection->lpVtbl->Item(pCollection, i, &pEndpoint)))\n\t\t\t\t{\n\t\t\t\t\tif (SUCCEEDED(pEndpoint->lpVtbl->GetId(pEndpoint, &pwszID)))\n\t\t\t\t\t{\n\t\t\t\t\t\tif (SUCCEEDED(pEndpoint->lpVtbl->OpenPropertyStore(pEndpoint, STGM_READ, &pProps)))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tPROPVARIANT varName;\n\t\t\t\t\t\t\tPropVariantInit(&varName);\n\t\t\t\t\t\t\tif (SUCCEEDED(pProps->lpVtbl->GetValue(pProps, &PKEY_Device_FriendlyName, &varName)))\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tchar nicename[256];\n\t\t\t\t\t\t\t\tchar internalname[256];\n\t\t\t\t\t\t\t\tstrcpy(nicename, AUDIODRIVERNAME \": \");\n\t\t\t\t\t\t\t\tnarrowen(nicename+strlen(AUDIODRIVERNAME)+2, sizeof(nicename)-(strlen(AUDIODRIVERNAME)+2), varName.pwszVal);\n\t\t\t\t\t\t\t\tnarrowen(internalname, sizeof(internalname), pwszID);\n\t\t\t\t\t\t\t\tcallback(AUDIODRIVERNAME, internalname, nicename);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tPropVariantClear(&varName);\n\t\t\t\t\t\t\tpProps->lpVtbl->Release(pProps);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tCoTaskMemFree(pwszID);\n\t\t\t\t\t}\n\t\t\t\t\tpEndpoint->lpVtbl->Release(pEndpoint);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tpCollection->lpVtbl->Release(pCollection);\n\t\t}\n\n//\t\tpEnumerator->lpVtbl->Release(pEnumerator);\n//\t\tpEnumerator = NULL;\n\t\treturn true;\n\t}\n\treturn true;\t//if we couldn't enumerate stuff, we won't be able to initialise anything anyway, so there's no point in doing any default device crap\n}\n\nsounddriver_t WASAPI_Output =\n{\n\tAUDIODRIVERNAME,\n\tWASAPI_InitCard,\n\tWASAPI_Enumerate,\n\tWASAPI_RegisterCvars\n};\n\n#endif"
  },
  {
    "path": "engine/client/snd_win.c",
    "content": "/*\nCopyright (C) 1996-1997 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n*/\n#include \"quakedef.h\"\n#include \"winquake.h\"\n#ifndef WINRT\n\n// 64K is > 1 second at 16-bit, 22050 Hz\n#define\tWAV_BUFFERS\t\t\t\t64\n#define\tWAV_MASK\t\t\t\t0x3F\n#define\tWAV_BUFFER_SIZE\t\t\t0x0400\n#define SECONDARY_BUFFER_SIZE\t0x10000\n\nstatic void WAV_Submit(soundcardinfo_t *sc, int start, int end);\n\ntypedef struct {\n\tHWAVEOUT hWaveOut;\n\tHANDLE hData;\n\tHGLOBAL hWaveHdr;\n\tHPSTR lpData;\n\tLPWAVEHDR lpWaveHdr;\n//\tDWORD\t\tmmstarttime;\n\tDWORD gSndBufSize;\n} wavhandle_t;\n\n/*\n==================\nS_BlockSound\n==================\n*/\n//all devices\nvoid S_BlockSound (void)\n{\n\tsoundcardinfo_t *sc;\n\twavhandle_t *wh;\n\n\tsnd_blocked++;\n\n\tfor (sc = sndcardinfo; sc; sc=sc->next)\n\t{\n\t\tif (sc->Submit == WAV_Submit && !sc->inactive_sound)\n\t\t{\n\t\t\twh = sc->handle;\n\t\t\tif (snd_blocked == 1)\n\t\t\t\twaveOutReset (wh->hWaveOut);\n\t\t}\n\t}\n}\n\n\n/*\n==================\nS_UnblockSound\n==================\n*/\n//all devices\nvoid S_UnblockSound (void)\n{\n\tsnd_blocked--;\n}\n\n\nstatic void *WAV_Lock (soundcardinfo_t *sc, unsigned int *sampidx)\n{\n\treturn sc->sn.buffer;\n}\nstatic void WAV_Unlock (soundcardinfo_t *sc, void *buffer)\n{\n}\n\n/*\n==================\nFreeSound\n==================\n*/\n//per device\nstatic void WAV_Shutdown (soundcardinfo_t *sc)\n{\n\tint\t\ti;\n\twavhandle_t *wh = sc->handle;\n\n\tif (!wh)\n\t\treturn;\n\tsc->handle = NULL;\n\n\twaveOutReset (wh->hWaveOut);\n\n\tif (wh->lpWaveHdr)\n\t{\n\t\tfor (i=0 ; i< WAV_BUFFERS ; i++)\n\t\t\twaveOutUnprepareHeader (wh->hWaveOut, wh->lpWaveHdr+i, sizeof(WAVEHDR));\n\t}\n\n\twaveOutClose (wh->hWaveOut);\n\n\tif (wh->hWaveHdr)\n\t{\n\t\tGlobalUnlock(wh->hWaveHdr);\n\t\tGlobalFree(wh->hWaveHdr);\n\t}\n\n\tif (wh->hData)\n\t{\n\t\tGlobalUnlock(wh->hData);\n\t\tGlobalFree(wh->hData);\n\t}\n\n\twh->hWaveOut = 0;\n\twh->hData = 0;\n\twh->hWaveHdr = 0;\n\twh->lpData = NULL;\n\twh->lpWaveHdr = NULL;\n\n\tZ_Free(wh);\n}\n\n/*\n==============\nSNDDMA_GetDMAPos\n\nreturn the current sample position (in mono samples read)\ninside the recirculating dma buffer, so the mixing code will know\nhow many sample are required to fill it up.\n===============\n*/\nstatic unsigned int WAV_GetDMAPos(soundcardinfo_t *sc)\n{\n\tint\t\ts;\n\ts = sc->snd_sent * WAV_BUFFER_SIZE;\n\ts >>= sc->sn.samplebytes - 1;\n\treturn s;\n}\n\n/*\n==============\nWAV_Submit\n\nSend sound to device if buffer isn't really the dma buffer\n===============\n*/\nstatic void WAV_Submit(soundcardinfo_t *sc, int start, int end)\n{\n\tLPWAVEHDR\th;\n\tint\t\t\twResult;\n\twavhandle_t *wh = sc->handle;\n\tint chunkstosubmit;\n\n\t//\n\t// find which sound blocks have completed\n\t//\n\twhile (1)\n\t{\n\t\tif ( sc->snd_completed == sc->snd_sent )\n\t\t{\n\t\t\tCon_DPrintf (\"Sound overrun\\n\");\n\t\t\tbreak;\n\t\t}\n\n\t\tif ( ! (wh->lpWaveHdr[ sc->snd_completed & WAV_MASK].dwFlags & WHDR_DONE) )\n\t\t{\n\t\t\tbreak;\n\t\t}\n\n\t\tsc->snd_completed++;\t// this buffer has been played\n\t}\n\n\t//\n\t// submit two new sound blocks\n\t//\n\tif (sc->sn.speed <= 22050)\n\t\tchunkstosubmit = 4;\n\telse\n\t\tchunkstosubmit = 4 + (sc->sn.speed/6000);\n\n\twhile (((sc->snd_sent - sc->snd_completed) >> (sc->sn.samplebytes - 1)) < chunkstosubmit)\n\t{\n\t\th = wh->lpWaveHdr + ( sc->snd_sent&WAV_MASK );\n\n\t\tsc->snd_sent++;\n\t\t/*\n\t\t * Now the data block can be sent to the output device. The\n\t\t * waveOutWrite function returns immediately and waveform\n\t\t * data is sent to the output device in the background.\n\t\t */\n\t\twResult = waveOutWrite(wh->hWaveOut, h, sizeof(WAVEHDR));\n\n\t\tif (wResult != MMSYSERR_NOERROR)\n\t\t{\n\t\t\tCon_SafePrintf (\"Failed to write block to device\\n\");\n\t\t\tWAV_Shutdown (sc);\n\t\t\treturn;\n\t\t}\n\t}\n}\n\n\n\n/*\n==================\nSNDDM_InitWav\n\nCrappy windows multimedia base\n==================\n*/\nqboolean QDECL WAV_InitCard (soundcardinfo_t *sc, const char *cardname)\n{\n\tWAVEFORMATEX  format;\n\tint\t\t\t\ti;\n\tHRESULT\t\t\thr;\n\twavhandle_t *wh;\n\n\tif (cardname && *cardname)\n\t\treturn false;\t//we don't support explicit devices, so only accept default devices.\n\n\twh = sc->handle = Z_Malloc(sizeof(wavhandle_t));\n\n\tsc->snd_sent = 0;\n\tsc->snd_completed = 0;\n\n\tif (sc->sn.speed > 48000) // limit waveout to 48000 until that buffer issue gets solved\n\t\tsc->sn.speed = 48000;\n\n\tif (sc->sn.samplebytes > 2)\n\t{\n\t\tsc->sn.samplebytes = 2;\n\t\tsc->sn.sampleformat = QSF_S16;\n\t}\n\telse\n\t{\n\t\tsc->sn.samplebytes = 1;\n\t\tsc->sn.sampleformat = QSF_U8;\n\t}\n\n\tmemset (&format, 0, sizeof(format));\n\tformat.wFormatTag = WAVE_FORMAT_PCM;\n\tformat.nChannels = sc->sn.numchannels;\n\tformat.wBitsPerSample = sc->sn.samplebytes*8;\n\tformat.nSamplesPerSec = sc->sn.speed;\n\tformat.nBlockAlign = format.nChannels\n\t\t*format.wBitsPerSample / 8;\n\tformat.cbSize = 0;\n\tformat.nAvgBytesPerSec = format.nSamplesPerSec\n\t\t*format.nBlockAlign;\n\n\t/* Open a waveform device for output using window callback. */\n\twhile ((hr = waveOutOpen((LPHWAVEOUT)&wh->hWaveOut, WAVE_MAPPER,\n\t\t\t\t\t&format,\n\t\t\t\t\t0, 0L, CALLBACK_NULL)) != MMSYSERR_NOERROR)\n\t{\n\t\tif (hr != MMSYSERR_ALLOCATED)\n\t\t{\n\t\t\tif (hr == WAVERR_BADFORMAT)\n\t\t\t\tCon_SafePrintf (CON_ERROR \"waveOutOpen failed, format not supported\\n\");\n\t\t\telse\n\t\t\t\tCon_SafePrintf (CON_ERROR \"waveOutOpen failed, return code %i\\n\", (int)hr);\n\t\t\tWAV_Shutdown (sc);\n\t\t\treturn false;\n\t\t}\n\n//\t\tif (MessageBox (NULL,\n//\t\t\t\t\t\t\"The sound hardware is in use by another app.\\n\\n\"\n//\t\t\t\t\t    \"Select Retry to try to start sound again or Cancel to run Quake with no sound.\",\n//\t\t\t\t\t\t\"Sound not available\",\n//\t\t\t\t\t\tMB_RETRYCANCEL | MB_SETFOREGROUND | MB_ICONEXCLAMATION) != IDRETRY)\n//\t\t{\n\t\t\tCon_SafePrintf (CON_ERROR \"waveOutOpen failure;\\n\"\n\t\t\t\t\t\t\t\"  hardware already in use\\nclose the app, then try using snd_restart\\n\");\n\t\t\tWAV_Shutdown (sc);\n\t\t\treturn false;\n//\t\t}\n\t}\n\n\t/*\n\t * Allocate and lock memory for the waveform data. The memory\n\t * for waveform data must be globally allocated with\n\t * GMEM_MOVEABLE and GMEM_SHARE flags.\n\n\t*/\n\twh->gSndBufSize = WAV_BUFFERS*WAV_BUFFER_SIZE;\n\twh->hData = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, wh->gSndBufSize);\n\tif (!wh->hData)\n\t{\n\t\tCon_SafePrintf (CON_ERROR \"Sound: Out of memory.\\n\");\n\t\tWAV_Shutdown (sc);\n\t\treturn false;\n\t}\n\twh->lpData = GlobalLock(wh->hData);\n\tif (!wh->lpData)\n\t{\n\t\tCon_SafePrintf (CON_ERROR \"Sound: Failed to lock.\\n\");\n\t\tWAV_Shutdown (sc);\n\t\treturn false;\n\t}\n\tmemset (wh->lpData, 0, wh->gSndBufSize);\n\n\t/*\n\t * Allocate and lock memory for the header. This memory must\n\t * also be globally allocated with GMEM_MOVEABLE and\n\t * GMEM_SHARE flags.\n\t */\n\twh->hWaveHdr = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE,\n\t\t(DWORD) sizeof(WAVEHDR) * WAV_BUFFERS);\n\n\tif (wh->hWaveHdr == NULL)\n\t{\n\t\tCon_SafePrintf (CON_ERROR \"Sound: Failed to Alloc header.\\n\");\n\t\tWAV_Shutdown (sc);\n\t\treturn false;\n\t}\n\n\twh->lpWaveHdr = (LPWAVEHDR) GlobalLock(wh->hWaveHdr);\n\n\tif (wh->lpWaveHdr == NULL)\n\t{\n\t\tCon_SafePrintf (CON_ERROR \"Sound: Failed to lock header.\\n\");\n\t\tWAV_Shutdown (sc);\n\t\treturn false;\n\t}\n\n\tmemset (wh->lpWaveHdr, 0, sizeof(WAVEHDR) * WAV_BUFFERS);\n\n\t/* After allocation, set up and prepare headers. */\n\tfor (i=0 ; i<WAV_BUFFERS ; i++)\n\t{\n\t\twh->lpWaveHdr[i].dwBufferLength = WAV_BUFFER_SIZE;\n\t\twh->lpWaveHdr[i].lpData = wh->lpData + i*WAV_BUFFER_SIZE;\n\n\t\tif (waveOutPrepareHeader(wh->hWaveOut, wh->lpWaveHdr+i, sizeof(WAVEHDR)) !=\n\t\t\t\tMMSYSERR_NOERROR)\n\t\t{\n\t\t\tCon_SafePrintf (CON_ERROR \"Sound: failed to prepare wave headers\\n\");\n\t\t\tWAV_Shutdown (sc);\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tsc->sn.samples = wh->gSndBufSize/sc->sn.samplebytes;\n\tsc->sn.samplepos = 0;\n\tsc->sn.buffer = (unsigned char *) wh->lpData;\n\tQ_strncpyz(sc->name, \"wav out\", sizeof(sc->name));\n\n\n\tsc->Lock\t\t= WAV_Lock;\n\tsc->Unlock\t\t= WAV_Unlock;\n\tsc->Submit\t\t= WAV_Submit;\n\tsc->Shutdown\t= WAV_Shutdown;\n\tsc->GetDMAPos\t= WAV_GetDMAPos;\n\n\treturn true;\n}\n//int (*pWAV_InitCard) (soundcardinfo_t *sc, int cardnum) = &WAV_InitCard;\nsounddriver_t WaveOut_Output =\n{\n\t\"WaveOut\",\n\tWAV_InitCard,\n\tNULL\n};\n#endif\n"
  },
  {
    "path": "engine/client/snd_xaudio.c",
    "content": "#include \"quakedef.h\"\r\n\r\n//frankly, xaudio2 gives nothing over directsound, unless we're getting it to do all the mixing instead. which gets really messy and far too involved.\r\n//I suppose it has a use with WINRT... although that doesn't apply to any actual users.\r\n//(on xp, its actually implemented as a wrapper over directsound, so why even bother. on vista+ its implemented as a wrapper over wasapi)\r\n\r\n//we're lazy and don't do any special threading, this makes it inferior to the directsound implementation - potentially, the callback feature could allow for slightly lower latencies.\r\n//also no reverb (fixme: XAUDIO2FX_REVERB_PARAMETERS).\r\n\r\n//dxsdk  = 2.7 = win7+ (redistributable)\r\n//w8sdk  = 2.8 = win8+ (system component, not available on vista/win7)\r\n//w10sdk = 2.9 = win10+ (system component, not available on vista/win7/win8/win8.1)\r\n\r\n#if defined(AVAIL_XAUDIO2) && !defined(SERVERONLY)\r\n#include \"winquake.h\"\r\n#include <xaudio2.h>\r\n\r\n#define SDRVNAME \"XAudio2\"\r\n\r\ntypedef struct\r\n{\r\n\tIXAudio2VoiceCallback cb;\t//must be first. yay for fake single inheritance.\r\n\r\n\tIXAudio2 *ixa;\r\n\tIXAudio2MasteringVoice *master;\r\n\tIXAudio2SourceVoice *source;\r\n\r\n\t//contiguous block of memory, because its easier.\r\n\tqbyte *bufferstart;\r\n\tunsigned int subbuffersize;\t//in samplepairs\r\n\tunsigned int buffercount;\r\n\tunsigned int bufferidx;\r\n\tunsigned int bufferavail;\r\n} xaud_t;\r\n\r\nstatic void *XAUDIO_Lock(soundcardinfo_t *sc, unsigned int *startoffset)\r\n{\r\n\tqbyte *ret;\r\n\txaud_t *xa = sc->handle;\r\n\tret = xa->bufferstart;\r\n\r\n//\t*startoffset = 0;\r\n//\tret += xa->subbuffersize * xa->bufferidx;\r\n\r\n\treturn ret;\r\n}\r\nstatic void XAUDIO_Unlock(soundcardinfo_t *sc, void *buffer)\r\n{\r\n}\r\nstatic unsigned int XAUDIO_GetDMAPos(soundcardinfo_t *sc)\r\n{\r\n\txaud_t *xa = sc->handle;\r\n\tunsigned int s = (xa->bufferidx+xa->bufferavail) * xa->subbuffersize * sc->sn.numchannels;\r\n\treturn s;\r\n}\r\nstatic void XAUDIO_Submit(soundcardinfo_t *sc, int start, int end)\r\n{\r\n\txaud_t *xa = sc->handle;\r\n\tXAUDIO2_BUFFER buf;\r\n\r\n\t//determine total buffer size\r\n\tint buffersize = sc->sn.samples*sc->sn.samplebytes;\r\n\r\n\t//determine time offsets in bytes\r\n\tstart *= sc->sn.numchannels*sc->sn.samplebytes;\r\n\tend *= sc->sn.numchannels*sc->sn.samplebytes;\r\n\r\n\twhile (start < end)\r\n\t{\r\n\t\tif (!xa->bufferavail)\r\n\t\t\tbreak;\t//o.O that's not meant to happen\r\n\t\tmemset(&buf, 0, sizeof(buf));\r\n\t\tbuf.AudioBytes = end - start;\r\n\t\tif (buf.AudioBytes > xa->subbuffersize * sc->sn.numchannels*sc->sn.samplebytes)\r\n\t\t{\r\n\t\t\tif (buf.AudioBytes < xa->subbuffersize * sc->sn.numchannels*sc->sn.samplebytes)\r\n\t\t\t{\t//dma code should ensure that only multiples of 'samplequeue' are processed.\r\n\t\t\t\tCon_Printf(\"XAudio2 underrun\\n\");\r\n\t\t\t\tbreak;\t\r\n\t\t\t}\r\n\t\t\tbuf.AudioBytes = xa->subbuffersize * sc->sn.numchannels*sc->sn.samplebytes;\r\n\t\t}\r\n\t\tbuf.pAudioData = xa->bufferstart + (start%buffersize);\r\n\t\tif ((qbyte*)buf.pAudioData + buf.AudioBytes > xa->bufferstart + buffersize)\r\n\t\t{\t//this shouldn't ever happen either\r\n\t\t\tCon_Printf(\"XAudio2 overrun\\n\");\r\n\t\t\tbreak;\t\r\n\t\t}\r\n\t\tIXAudio2SourceVoice_SubmitSourceBuffer(xa->source, &buf, NULL);\r\n\t\txa->bufferidx += 1;\r\n\t\txa->bufferavail -= 1;\r\n\t\tstart += buf.AudioBytes;\r\n\t}\r\n}\r\n\r\nstatic void XAUDIO_Shutdown(soundcardinfo_t *sc)\r\n{\r\n\txaud_t *xa = sc->handle;\r\n\t//releases are allowed to block, supposedly.\r\n\tIXAudio2SourceVoice_DestroyVoice(xa->source);\r\n\tIXAudio2MasteringVoice_DestroyVoice(xa->master);\r\n\tIXAudio2_Release(xa->ixa);\r\n\tBZ_Free(xa->bufferstart);\r\n\tZ_Free(xa);\r\n\tsc->handle = NULL;\r\n}\r\n\r\nvoid WINAPI XAUDIO_CB_OnVoiceProcessingPassStart (IXAudio2VoiceCallback *ths, UINT32 BytesRequired) {}\r\nvoid WINAPI XAUDIO_CB_OnVoiceProcessingPassEnd (IXAudio2VoiceCallback *ths) {}\r\nvoid WINAPI XAUDIO_CB_OnStreamEnd (IXAudio2VoiceCallback *ths) {}\r\nvoid WINAPI XAUDIO_CB_OnBufferStart (IXAudio2VoiceCallback *ths, void* pBufferContext) {}\r\nvoid WINAPI XAUDIO_CB_OnBufferEnd (IXAudio2VoiceCallback *ths, void* pBufferContext) {xaud_t *xa = (xaud_t*)ths; S_LockMixer(); xa->bufferavail+=1; S_UnlockMixer();}\r\nvoid WINAPI XAUDIO_CB_OnLoopEnd (IXAudio2VoiceCallback *ths, void* pBufferContext) {}\r\nvoid WINAPI XAUDIO_CB_OnVoiceError (IXAudio2VoiceCallback *ths, void* pBufferContext, HRESULT Error) {}\r\nstatic IXAudio2VoiceCallbackVtbl cbvtbl =\r\n{\r\n\tXAUDIO_CB_OnVoiceProcessingPassStart,\r\n\tXAUDIO_CB_OnVoiceProcessingPassEnd,\r\n\tXAUDIO_CB_OnStreamEnd,\r\n\tXAUDIO_CB_OnBufferStart,\r\n\tXAUDIO_CB_OnBufferEnd,\r\n\tXAUDIO_CB_OnLoopEnd,\r\n\tXAUDIO_CB_OnVoiceError\r\n};\r\n\r\nstatic qboolean QDECL XAUDIO_InitCard(soundcardinfo_t *sc, const char *cardname)\r\n{\r\n#ifdef WINRT\r\n\tchar *dev = NULL;\r\n#else\r\n\tint dev = 0;\r\n#endif\r\n\txaud_t *xa = Z_Malloc(sizeof(*xa));\r\n\tWAVEFORMATEXTENSIBLE wfmt;\r\n\tconst static GUID QKSDATAFORMAT_SUBTYPE_IEEE_FLOAT\t= {0x00000003,0x0000,0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};\r\n\r\n\txa->cb.lpVtbl = &cbvtbl;\r\n\r\n\tsc->sn.numchannels = 2;\r\n\r\n\tmemset(&wfmt, 0, sizeof(wfmt));\r\n\twfmt.Format.wFormatTag = WAVE_FORMAT_PCM;\r\n\twfmt.Format.nChannels = sc->sn.numchannels;\r\n\twfmt.Format.nSamplesPerSec = sc->sn.speed;\r\n\twfmt.Format.wBitsPerSample = sc->sn.samplebytes*8;\r\n\twfmt.Format.nBlockAlign = wfmt.Format.nChannels * (wfmt.Format.wBitsPerSample / 8);\r\n\twfmt.Format.nAvgBytesPerSec = wfmt.Format.nSamplesPerSec * wfmt.Format.nBlockAlign;\r\n\twfmt.Format.cbSize = 0;\r\n\tif (wfmt.Format.wBitsPerSample == 32)\r\n\t{\r\n\t\twfmt.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;\r\n\t\twfmt.Format.cbSize = 22;\r\n\t\tmemcpy(&wfmt.SubFormat, &QKSDATAFORMAT_SUBTYPE_IEEE_FLOAT, sizeof(wfmt.SubFormat));\r\n\t}\r\n\r\n\tsc->inactive_sound = true;\r\n\txa->buffercount = xa->bufferavail = 3;\t//submit this many straight up\r\n\txa->subbuffersize = 256;\t//number of sampleblocks per submission\r\n\tsc->samplequeue = -1;\t//-1 means we're streaming, XAUDIO_GetDMAPos returns exactly as much as we want to paint to.\r\n\r\n\tsc->sn.samples = xa->buffercount * xa->subbuffersize * sc->sn.numchannels;\r\n\r\n\txa->bufferstart = BZ_Malloc(sc->sn.samples * sc->sn.samplebytes);\r\n\r\n\tif (xa->bufferstart)\r\n\t{\r\n\t\tif (SUCCEEDED(XAudio2Create(&xa->ixa, 0, XAUDIO2_DEFAULT_PROCESSOR)))\r\n\t\t{\r\n#ifdef WINRT\r\n\t\t\tif (SUCCEEDED(IXAudio2_CreateMasteringVoice(xa->ixa, &xa->master, XAUDIO2_DEFAULT_CHANNELS, XAUDIO2_DEFAULT_SAMPLERATE, 0, dev, NULL, AudioCategory_GameEffects)))\r\n#else\r\n\t\t\tif (cardname && *cardname)\r\n\t\t\t{\r\n\t\t\t\tUINT32 devs = 0;\r\n\t\t\t\tXAUDIO2_DEVICE_DETAILS details;\r\n\t\t\t\tchar id[MAX_QPATH];\r\n\t\t\t\tif (FAILED(IXAudio2_GetDeviceCount(xa->ixa, &devs)))\r\n\t\t\t\t\tdevs = 0;\r\n\r\n\t\t\t\tfor (dev = 0; dev < devs; dev++)\r\n\t\t\t\t{\r\n\t\t\t\t\tif (SUCCEEDED(IXAudio2_GetDeviceDetails(xa->ixa, dev, &details)))\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tnarrowen(id, sizeof(id), details.DeviceID);\r\n\r\n\t\t\t\t\t\tif (!strcmp(id, cardname))\r\n\t\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t\tif (dev == devs)\r\n\t\t\t\t\tdev = ~0;\t//something invalid.\r\n\t\t\t}\r\n\r\n\t\t\t/*\r\n\t\t\t//FIXME: correct the details to match the hardware\r\n\t\t\t*/\r\n\r\n\r\n\t\t\tif (dev != ~0 && SUCCEEDED(IXAudio2_CreateMasteringVoice(xa->ixa, &xa->master, XAUDIO2_DEFAULT_CHANNELS, XAUDIO2_DEFAULT_SAMPLERATE, 0, dev, NULL)))\r\n#endif\r\n\t\t\t{\r\n\t\t\t\t//egads\r\n\t\t\t\tXAUDIO2_VOICE_SENDS vs;\r\n\t\t\t\tXAUDIO2_SEND_DESCRIPTOR sd[1];\r\n\t\t\t\tvs.SendCount = 1;\r\n\t\t\t\tvs.pSends = sd;\r\n\t\t\t\tsd[0].Flags = 0;\r\n\t\t\t\tsd[0].pOutputVoice = (IXAudio2Voice*)xa->master;\r\n\r\n\t\t\t\tif (SUCCEEDED(IXAudio2_CreateSourceVoice(xa->ixa, &xa->source, &wfmt.Format, 0, XAUDIO2_DEFAULT_FREQ_RATIO, &xa->cb, &vs, NULL)))\r\n\t\t\t\t{\r\n\t\t\t\t\tsc->handle = xa;\r\n\t\t\t\t\tsc->GetDMAPos = XAUDIO_GetDMAPos;\r\n\t\t\t\t\tsc->Lock = XAUDIO_Lock;\r\n\t\t\t\t\tsc->Unlock = XAUDIO_Unlock;\r\n\t\t\t\t\tsc->Submit = XAUDIO_Submit;\r\n\t\t\t\t\tsc->Shutdown = XAUDIO_Shutdown;\r\n\r\n\t\t\t\t\tIXAudio2SourceVoice_Start(xa->source, 0, XAUDIO2_COMMIT_NOW);\r\n\t\t\t\t\treturn true;\r\n\t\t\t\t}\r\n\t\t\t\tIXAudio2MasteringVoice_DestroyVoice(xa->master);\r\n\t\t\t}\r\n\t\t\tIXAudio2_Release(xa->ixa);\r\n\t\t}\r\n\t\tBZ_Free(xa->bufferstart);\r\n\t}\r\n\tZ_Free(xa);\r\n\treturn false;\r\n}\r\n\r\nqboolean QDECL XAUDIO_Enumerate(void (QDECL *callback) (const char *drivername, const char *devicecode, const char *readablename))\r\n{\r\n\tIXAudio2 *ixa;\r\n#ifndef WINRT\r\n\tCoInitializeEx(NULL, COINIT_MULTITHREADED);\r\n\t//winrt provides no enumeration mechanism.\r\n\tif (SUCCEEDED(XAudio2Create(&ixa, 0, XAUDIO2_DEFAULT_PROCESSOR)))\r\n\t{\r\n\t\tUINT32 devs = 0, i;\r\n\t\tXAUDIO2_DEVICE_DETAILS details;\r\n\t\tchar id[MAX_QPATH], name[MAX_QPATH];\r\n\t\tif (FAILED(IXAudio2_GetDeviceCount(ixa, &devs)))\r\n\t\t\tdevs = 0;\r\n\r\n\t\tstrcpy(name, \"XA2:\");\r\n\r\n\t\tfor (i = 0; i < devs; i++)\r\n\t\t{\r\n\t\t\tif (SUCCEEDED(IXAudio2_GetDeviceDetails(ixa, i, &details)))\r\n\t\t\t{\r\n\t\t\t\tnarrowen(id, sizeof(id), details.DeviceID);\r\n\t\t\t\tnarrowen(name+4, sizeof(name)-4, details.DisplayName);\r\n\r\n\t\t\t\tcallback(SDRVNAME, id, name);\r\n\t\t\t}\r\n\t\t}\r\n\t\tIXAudio2_Release(ixa);\r\n\t\treturn true;\r\n\t}\r\n#endif\r\n\treturn false;\r\n}\r\n\r\nsounddriver_t XAUDIO2_Output =\r\n{\r\n\tSDRVNAME,\r\n\tXAUDIO_InitCard,\r\n\tXAUDIO_Enumerate\r\n};\r\n#endif\r\n"
  },
  {
    "path": "engine/client/sound.h",
    "content": "/*\nCopyright (C) 1996-1997 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n*/\n// sound.h -- client sound i/o functions\n\n#ifndef __SOUND__\n#define __SOUND__\n\n//#define MIXER_F32\n#define MAXSOUNDCHANNELS 8\t//on a per device basis\n\n//pitch/rate changes require that we track stuff with subsample precision.\n//this can result in some awkward overflows.\n#define ssamplepos_t qintptr_t\n#define usamplepos_t quintptr_t\n#define PITCHSHIFT 6\t/*max audio file length = ((1<<32)>>PITCHSHIFT)/KHZ*/\n\nstruct sfx_s;\n\ntypedef struct {\n\tstruct sfxcache_s *(QDECL *decodedata) (struct sfx_s *sfx, struct sfxcache_s *buf, ssamplepos_t start, int length);\t//return true when done.\n\tfloat (QDECL *querydata) (struct sfx_s *sfx, struct sfxcache_s *buf, char *title, size_t titlesize);\t//reports length + original format info without actually decoding anything.\n\tvoid (QDECL *ended) (struct sfx_s *sfx);\t//sound stopped playing and is now silent (allow rewinding or something).\n\tvoid (QDECL *purge) (struct sfx_s *sfx);\t//sound is being purged from memory. destroy everything.\n\tvoid *buf;\n} sfxdecode_t;\n\nenum\n{\n\tSLS_NOTLOADED,\t//not tried to load it\n\tSLS_LOADING,\t//loading it on a worker thread.\n\tSLS_LOADED,\t\t//currently in memory and usable.\n\tSLS_FAILED\t\t//already tried to load it. it won't work. not found, invalid format, etc\n};\ntypedef struct sfx_s\n{\n\tchar \tname[MAX_OSPATH];\n\tsfxdecode_t\t\tdecoder;\n\n\tint loadstate; //no more super-spammy\n\tqboolean touched:1; //if the sound is still relevent\n\tqboolean syspath:1; //if the sound is still relevent\n\n\tint loopstart;\t//-1 or sample index to begin looping at once the sample ends\n} sfx_t;\n\ntypedef enum\n{\n#ifdef FTE_TARGET_WEB\n\t\tQAF_BLOB=0,\n#endif\n\t\tQAF_S8=1,\n\t\t//QAF_U8=0x80|1,\n\t\tQAF_S16=2,\n\t\t//QAF_S32=4,\n#ifdef MIXER_F32\n\t\tQAF_F32=0x80|4,\n#endif\n#define QAF_BYTES(v) (v&0x7f)\t//to make memory allocation easier.\n} qaudiofmt_t;\n\n// !!! if this is changed, it much be changed in asm_i386.h too !!!\ntypedef struct sfxcache_s\n{\n\tusamplepos_t length;\t//sample count\n\tunsigned int speed;\n\tqaudiofmt_t format;\n\tunsigned int numchannels;\n\tusamplepos_t soundoffset;\t//byte index into the sound\n\tqbyte\t*data;\t\t// variable sized\n} sfxcache_t;\n\ntypedef struct\n{\n\tint\t\t\t\tnumchannels;\t\t\t// this many samples per frame\n\tint\t\t\t\tsamples;\t\t\t\t// mono samples in buffer (individual, non grouped)\n\tint\t\t\t\tsamplepos;\t\t\t\t// in mono samples\n\tint\t\t\t\tsamplebytes;\t\t\t// per channel (NOT per frame)\n\tenum\n\t{\n\t\tQSF_INVALID,\t//not selected yet...\n\t\tQSF_EXTERNALMIXER,\t//this sample format is totally irrelevant as this device uses some sort of external mixer.\n\t\tQSF_U8,\t\t//FIXME: more unsigned formats need changes to S_ClearBuffer\n\t\tQSF_S8,\t\t//signed 8bit format is actually quite rare.\n\t\tQSF_S16,\t//normal format\n//\t\tQSF_X8_S24,\t//upper 8 bits unused. hopefully we don't need any packed thing\n//\t\tQSF_S32,\t//lower 8 bits probably unused. this makes overflow detection messy.\n\t\tQSF_F32,\t//modern mixers can use SSE/SIMD stuff, and we can skip clamping so this can be quite nippy.\n\t} sampleformat;\n\tint\t\t\t\tspeed;\t\t\t\t\t// this many frames per second\n\tunsigned char\t*buffer;\t\t\t\t// pointer to mixed pcm buffer (not directly used by mixer)\n} dma_t;\n\n//client and server\n#define CF_SV_RELIABLE\t\t1\t// send reliably\n#define CF_NET_SENTVELOCITY\tCF_SV_RELIABLE\n#define CF_FORCELOOP\t\t2\t// forces looping. set on static sounds.\n#define CF_NOSPACIALISE\t\t4\t// these sounds are played at a fixed volume in both speakers, but still gets quieter with distance.\n//#define CF_PAUSED\t\t\t8\t// rate = 0. or something.\n#define CF_CL_ABSVOLUME\t\t16\t// ignores volume cvar (but not mastervolume). this is ignored if received from the server because there's no practical way for the server to respect the client's preferences.\n//#define CF_SV_RESERVED\tCF_CL_ABSVOLUME\n#define CF_NOREVERB\t\t\t32\t// disables reverb on this channel, if possible.\n#define CF_FOLLOW\t\t\t64\t// follows the owning entity (stops moving if we lose track)\n#define CF_NOREPLACE\t\t128\t// start sound event is ignored if there's already a sound playing on that entchannel (probably paired with CF_FORCELOOP).\n\n#define CF_SV_UNICAST\t\t256 // serverside only. the sound is sent to msg_entity only.\n#define CF_SV_SENDVELOCITY\t512\t// serverside hint that velocity is important\n#define CF_CLI_AUTOSOUND\t1024\t// generated from q2 entities, which avoids breaking regular sounds, using it outside the sound system will probably break things.\n#define CF_CLI_INACTIVE\t\t2048\t// try to play even when inactive\n#ifdef Q3CLIENT\n#define CF_CLI_NODUPES\t\t4096\t// block multiple identical sounds being started on the same entity within rapid succession (regardless of channel). required by quake3.\n#endif\n#define CF_CLI_STATIC\t\t8192\t//started via ambientsound/svc_spawnstaticsound\n#define CF_NETWORKED (CF_NOSPACIALISE|CF_NOREVERB|CF_FORCELOOP|CF_FOLLOW|CF_NOREPLACE)\n\ntypedef struct\n{\n\tsfx_t\t*sfx;\t\t\t// sfx number\n\tint\t\tvol[MAXSOUNDCHANNELS];\t\t// volume, 0.8 fixed point.\n\tssamplepos_t pos;\t\t// sample position in sfx, <0 means delay sound start (shifted up by PITCHSHIFT)\n\tint\t\trate;\t\t\t// fixed point rate scaling\n\tint\t\tflags;\t\t\t// cf_ flags\n\tint\t\tentnum;\t\t\t// to allow overriding a specific sound\n\tint\t\tentchannel;\t\t// to avoid overriding a specific sound too easily\n\tvec3_t\torigin;\t\t\t// origin of sound effect\n\tvec3_t\tvelocity;\t\t// velocity of sound effect\n\tvec_t\tdist_mult;\t\t// distance multiplier (attenuation/clipK)\n\tint\t\tmaster_vol;\t\t// 0-255 master volume\n#ifdef Q3CLIENT\n\tunsigned int starttime;\t// start time, to replicate q3's 50ms embargo on duped sounds.\n#endif\n} channel_t;\n\nstruct soundcardinfo_s;\ntypedef struct soundcardinfo_s soundcardinfo_t;\n\nextern struct sndreverbproperties_s\n{\n\tint modificationcount;\n\tstruct reverbproperties_s \n\t{\t//note: this struct originally comes from openal's eaxreverb\n\t\t//it is shared with gamecode\n\t\tfloat flDensity;\n\t\tfloat flDiffusion;\n\t\tfloat flGain;\n\t\tfloat flGainHF;\n\t\tfloat flGainLF;\n\t\tfloat flDecayTime;\n\t\tfloat flDecayHFRatio;\n\t\tfloat flDecayLFRatio;\n\t\tfloat flReflectionsGain;\n\t\tfloat flReflectionsDelay;\n\t\tfloat flReflectionsPan[3];\n\t\tfloat flLateReverbGain;\n\t\tfloat flLateReverbDelay;\n\t\tfloat flLateReverbPan[3];\n\t\tfloat flEchoTime;\t\n\t\tfloat flEchoDepth;\n\t\tfloat flModulationTime;\n\t\tfloat flModulationDepth;\n\t\tfloat flAirAbsorptionGainHF;\n\t\tfloat flHFReference;\n\t\tfloat flLFReference;\n\t\tfloat flRoomRolloffFactor;\n\t\tint   iDecayHFLimit;\n\t} props;\n} *reverbproperties;\nextern size_t numreverbproperties;\n\n//reverbproperties_s presets, from efx-presets.h\n//mostly for testing\n#define REVERB_PRESET_PSYCHOTIC \\\n    { 0.0625f, 0.5000f, 0.3162f, 0.8404f, 1.0000f, 7.5600f, 0.9100f, 1.0000f, 0.4864f, 0.0200f, { 0.0000f, 0.0000f, 0.0000f }, 2.4378f, 0.0300f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 4.0000f, 1.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x0 }\n//default reverb 1\n#define REVERB_PRESET_UNDERWATER \\\n { 0.3645f, 1.0000f, 0.3162f, 0.0100f, 1.0000f, 1.4900f, 0.1000f, 1.0000f, 0.5963f, 0.0070f, { 0.0000f, 0.0000f, 0.0000f }, 7.0795f, 0.0110f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 1.1800f, 0.3480f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 }\n\nvoid S_Init (void);\nvoid S_Startup (void);\nvoid S_EnumerateDevices(void);\nvoid S_Shutdown (qboolean final);\nfloat S_GetSoundTime(int entnum, int entchannel);\nfloat S_GetChannelLevel(int entnum, int entchannel);\nvoid S_StartSound (int entnum, int entchannel, sfx_t *sfx, vec3_t origin, vec3_t velocity, float fvol, float attenuation, float timeofs, float pitchadj, unsigned int flags);\nfloat S_UpdateSound(int entnum, int entchannel, sfx_t *sfx, vec3_t origin, vec3_t velocity, float fvol, float attenuation, float timeofs, float pitchadj, unsigned int flags);\nvoid S_StaticSound (sfx_t *sfx, vec3_t origin, float vol, float attenuation);\nvoid S_StopSound (int entnum, int entchannel);\nvoid S_StopAllSounds(qboolean clear);\nvoid S_UpdateListener(int seat, int entnum, vec3_t origin, vec3_t forward, vec3_t right, vec3_t up, size_t reverbtype, vec3_t velocity);\nqboolean S_UpdateReverb(size_t reverbtype, void *reverb, size_t reverbsize);\nvoid S_GetListenerInfo(int seat, float *origin, float *forward, float *right, float *up);\nvoid S_Update (void);\nvoid S_ExtraUpdate (void);\nvoid S_MixerThread(soundcardinfo_t *sc);\nvoid S_Purge(qboolean retaintouched);\n\nvoid S_LockMixer(void);\nvoid S_UnlockMixer(void);\n\nqboolean S_HaveOutput(void);\n\nvoid S_Music_Clear(sfx_t *onlyifsample);\nvoid S_Music_Seek(float time);\nqboolean S_GetMusicInfo(int musicchannel, float *time, float *duration, char *title, size_t titlesize);\nqboolean S_Music_Playing(int musicchannel);\nfloat Media_CrossFade(int musicchanel, float vol, float time);\t//queries the volume we're meant to be playing (checks for fade out). -1 for no more, otherwise returns vol.\nsfx_t *Media_NextTrack(int musicchanel, float *time);\t//queries the track we're meant to be playing now.\n\nsfx_t *S_FindName (const char *name, qboolean create, qboolean syspath);\nsfx_t *S_PrecacheSound2 (const char *sample, qboolean syspath);\n#define S_PrecacheSound(s) S_PrecacheSound2(s,false)\nvoid S_UntouchAll(void);\nvoid S_ClearPrecache (void);\nvoid S_BeginPrecaching (void);\nvoid S_EndPrecaching (void);\n\nvoid S_PaintChannels(soundcardinfo_t *sc, int endtime);\nvoid S_InitPaintChannels (soundcardinfo_t *sc);\n\nsoundcardinfo_t *S_SetupDeviceSeat(char *driver, char *device, int seat);\nvoid S_ShutdownCard (soundcardinfo_t *sc);\n\nvoid S_DefaultSpeakerConfiguration(soundcardinfo_t *sc);\nvoid S_ResetFailedLoad(void);\n\n#ifdef PEXT2_VOICECHAT\nvoid S_Voip_Parse(void);\n#endif\nint S_Voip_ClientLoudness(unsigned int plno);\n#ifdef VOICECHAT\nextern cvar_t snd_voip_showmeter;\nvoid S_Voip_Transmit(unsigned char clc, sizebuf_t *buf);\nvoid S_Voip_MapChange(void);\nint S_Voip_Loudness(qboolean ignorevad);\t//-1 for not capturing, otherwise between 0 and 100\nqboolean S_Voip_Speaking(unsigned int plno);\nvoid S_Voip_Ignore(unsigned int plno, qboolean ignore);\n#else\n#define S_Voip_Loudness() -1\n#define S_Voip_Speaking(p) false\n#define S_Voip_Ignore(p,s)\n#endif\n\nqboolean S_IsPlayingSomewhere(sfx_t *s);\n//qboolean ResampleSfx (sfx_t *sfx, int inrate, int inchannels, int inwidth, int insamps, int inloopstart, qbyte *data);\n\n// picks a channel based on priorities, empty slots, number of channels\nchannel_t *SND_PickChannel(soundcardinfo_t *sc, int entnum, int entchannel);\n\nvoid SND_ResampleStream (const void *in, int inrate, qaudiofmt_t inwidth, int inchannels, int insamps, void *out, int outrate, qaudiofmt_t outwidth, int outchannels, int resampstyle);\n\n// restart entire sound subsystem (doesn't flush old sounds, so make sure that happens)\nvoid S_DoRestart (qboolean onlyifneeded);\n\nvoid S_Restart_f (void);\n\n//plays streaming audio\n#define SOURCEID_MENUQC -3\n#define SOURCEID_CSQC -2\n#define SOURCEID_CINEMATIC -1\n#define SOURCEID_VOIP_FIRST 0\n#define SOURCEID_VOIP_MAX MAX_CLIENTS-1\nvoid S_RawAudio(int sourceid, const qbyte *data, int speed, int samples, int channels, qaudiofmt_t width, float volume);\nfloat S_RawAudioQueued(int sourceid);\n\nvoid CLVC_Poll (void);\n\nvoid SNDVC_MicInput(qbyte *buffer, int samples, int freq, int width);\n\n\n\n// ====================================================================\n// User-setable variables\n// ====================================================================\n\n#define NUM_MUSICS\t\t\t\t1\n\n#define AMBIENT_FIRST 0\n#define AMBIENT_STOP NUM_AMBIENTS\n#define MUSIC_FIRST AMBIENT_STOP\n#define MUSIC_STOP (MUSIC_FIRST + NUM_MUSICS)\n#define DYNAMIC_FIRST MUSIC_STOP\n\n//\n// Fake dma is a synchronous faking of the DMA progress used for\n// isolating performance in the renderer.  The fakedma_updates is\n// number of times S_Update() is called per second.\n//\n\nextern int\t\t\t\tsnd_speed;\n\nextern cvar_t snd_nominaldistance;\n\nextern\tcvar_t snd_loadas8bit;\nextern\tcvar_t bgmvolume;\nextern\tcvar_t volume, mastervolume;\nextern\tcvar_t snd_capture;\nextern\tcvar_t nosound;\n\nextern float voicevolumemod;\n\nextern qboolean\tsnd_initialized;\nextern cvar_t snd_mixerthread;\n\nextern int\t\tsnd_blocked;\n\nvoid S_LocalSound (const char *s);\nvoid S_LocalSound2 (const char *sound, int channel, float volume);\nqboolean S_LoadSound (sfx_t *s, qboolean forcedecode);\n\ntypedef qboolean (QDECL *S_LoadSound_t) (sfx_t *s, qbyte *data, size_t datalen, int sndspeed, qboolean forcedecode);\nqboolean S_RegisterSoundInputPlugin(void *module, S_LoadSound_t loadfnc); //called to register additional sound input plugins\nvoid S_UnregisterSoundInputModule(void *module);\n\nvoid S_AmbientOff (void);\nvoid S_AmbientOn (void);\n\n\n//inititalisation functions.\ntypedef struct\n{\n\tconst char *name;\t//must be a single token, with no :\n\tqboolean (QDECL *InitCard) (soundcardinfo_t *sc, const char *cardname);\t//NULL for default device.\n\tqboolean (QDECL *Enumerate) (void (QDECL *callback) (const char *drivername, const char *devicecode, const char *readablename));\n\tvoid (QDECL *RegisterCvars) (void);\n} sounddriver_t;\n/*typedef int (*sounddriver) (soundcardinfo_t *sc, int cardnum);\nextern sounddriver pOPENAL_InitCard;\nextern sounddriver pDSOUND_InitCard;\nextern sounddriver pALSA_InitCard;\nextern sounddriver pSNDIO_InitCard;\nextern sounddriver pOSS_InitCard;\nextern sounddriver pSDL_InitCard;\nextern sounddriver pWAV_InitCard;\nextern sounddriver pAHI_InitCard;\n*/\n\ntypedef enum\n{\n\tCUR_SPACIALISEONLY\t= 0,\t\t\t//for ticking over, respacialising, etc\n\tCUR_UPDATE\t\t\t= (1u<<1),\t\t//flags/rate changed without changing the sound itself\n\tCUR_SOUNDCHANGE\t\t= (1u<<2),\t\t//the audio file changed too. reset everything.\n\tCUR_OFFSET\t\t\t= (1u<<3),\n\tCUR_EVERYTHING\t\t= CUR_UPDATE|CUR_SOUNDCHANGE|CUR_OFFSET\n} chanupdatereason_t;\n\nstruct soundcardinfo_s { //windows has one defined AFTER directsound\n\tchar name[256];\t//a description of the card.\n\tchar guid[256];\t//device name as detected (so input code can create sound devices without bugging out too much)\n\tstruct soundcardinfo_s *next;\n\tint seat;\n\n//speaker orientations for spacialisation.\n\tfloat dist[MAXSOUNDCHANNELS];\n\n\tvec3_t speakerdir[MAXSOUNDCHANNELS];\n\n//info on which sound effects are playing\n\t//FIXME: use a linked list\n\tchannel_t\t*channel;\n\tsize_t\t\ttotal_chans;\n\tsize_t\t\tmax_chans;\n\n\tfloat\tambientlevels[NUM_AMBIENTS];\t//we use a float instead of the channel's int volume value to avoid framerate dependancies with slow transitions.\n\n//mixer\n\tvolatile dma_t sn;\t//why is this volatile?\n\tqboolean inactive_sound;\t//continue mixing for this card even when the window isn't active.\n\tqboolean selfpainting;\t//allow the sound code to call the right functions when it feels the need (not properly supported).\n\n\tint\tpaintedtime;\t//used in the mixer as last-written pos (in frames)\n\tint\toldsamplepos;\t//this is used to track buffer wraps\n\tint\tbuffers;\t//used to keep track of how many buffer wraps for consistant sound\n\tint\tsamplequeue;\t//this is the number of samples the device can enqueue. if set, DMAPos returns the write point (rather than hardware read point) (in samplepairs).\n\n//callbacks\n\tvoid *(*Lock) (soundcardinfo_t *sc, unsigned int *startoffset);\t//grab a pointer to the hardware ringbuffer or whatever. startoffset is the starting offset. you can set it to 0 and bump the start offset if you need.\n\tvoid (*Unlock) (soundcardinfo_t *sc, void *buffer);\t\t\t\t//release the hardware ringbuffer memory\n\tvoid (*Submit) (soundcardinfo_t *sc, int start, int end);\t\t//if the ringbuffer is emulated, this is where you should push it to the device.\n\tvoid (*Shutdown) (soundcardinfo_t *sc);\t\t\t\t\t\t\t//kill the device\n\tunsigned int (*GetDMAPos) (soundcardinfo_t *sc);\t\t\t\t//get the current point that the hardware is reading from (the return value should not wrap, at least not very often)\n\tvoid (*SetEnvironmentReverb) (soundcardinfo_t *sc, size_t reverb);\t//if you have eax enabled, change the environment. generally this is a stub. optional.\n\tvoid (*Restore) (soundcardinfo_t *sc);\t\t\t\t\t\t\t//called before lock/unlock/lock/unlock/submit. optional\n\tvoid (*ChannelUpdate) (soundcardinfo_t *sc, channel_t *channel, chanupdatereason_t schanged);\t//properties of a sound effect changed. this is to notify hardware mixers. optional.\n\tvoid (*ListenerUpdate) (soundcardinfo_t *sc, int entnum, vec3_t origin, vec3_t forward, vec3_t right, vec3_t up, vec3_t velocity);\t//player moved or something. this is to notify hardware mixers. optional.\n\tssamplepos_t (*GetChannelPos) (soundcardinfo_t *sc, channel_t *channel);\t//queries a hardware mixer's channel position (essentially returns channel->pos, except more up to date)\n\n//driver-specific - if you need more stuff, you should just shove it in the handle pointer\n\tvoid *thread;\n\tvoid *handle;\n\tint snd_sent;\n\tint snd_completed;\n\tint audio_fd;\n};\n\nextern soundcardinfo_t *sndcardinfo;\n\ntypedef struct\n{\n\tint apiver;\n\tchar *drivername;\n\tqboolean (QDECL *Enumerate) (void (QDECL *callback) (const char *drivername, const char *devicecode, const char *readablename));\n\tvoid *(QDECL *Init) (int samplerate, const char *device);\t\t\t/*create a new context*/\n\tvoid (QDECL *Start) (void *ctx);\t\t/*begin grabbing new data, old data is potentially flushed*/\n\tunsigned int (QDECL *Update) (void *ctx, unsigned char *buffer, unsigned int minbytes, unsigned int maxbytes);\t/*grab the data into a different buffer*/\n\tvoid (QDECL *Stop) (void *ctx);\t\t/*stop grabbing new data, old data may remain*/\n\tvoid (QDECL *Shutdown) (void *ctx);\t/*destroy everything*/\n} snd_capture_driver_t;\n\n#endif\n"
  },
  {
    "path": "engine/client/spritegn.h",
    "content": "/*\nCopyright (C) 1996-1997 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  \n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n*/\n//\n// spritegn.h: header file for sprite generation program\n//\n\n// **********************************************************\n// * This file must be identical in the spritegen directory *\n// * and in the Quake directory, because it's used to       *\n// * pass data from one to the other via .spr files.        *\n// **********************************************************\n\n//-------------------------------------------------------\n// This program generates .spr sprite package files.\n// The format of the files is as follows:\n//\n// dsprite_t file header structure\n// <repeat dsprite_t.numframes times>\n//   <if spritegroup, repeat dspritegroup_t.numframes times>\n//     dspriteframe_t frame header structure\n//     sprite bitmap\n//   <else (single sprite frame)>\n//     dspriteframe_t frame header structure\n//     sprite bitmap\n// <endrepeat>\n//-------------------------------------------------------\n\n#ifdef INCLUDELIBS\n\n#include <stdlib.h>\n#include <stdio.h>\n#include <math.h>\n#include <string.h>\n\n#include \"cmdlib.h\"\n#include \"scriplib.h\"\n#include \"dictlib.h\"\n#include \"trilib.h\"\n#include \"lbmlib.h\"\n#include \"mathlib.h\"\n\n#endif\n\n#define SPRITE_VERSION\t1\n#define SPRITEHL_VERSION\t2\n#define SPRITE32_VERSION\t32\n\n// must match definition in modelgen.h\n#ifndef SYNCTYPE_T\n#define SYNCTYPE_T\ntypedef enum {ST_SYNC=0, ST_RAND } synctype_t;\n#endif\n\n// TODO: shorten these?\ntypedef struct {\n\tint\t\t\tident;\n\tint\t\t\tversion;\n\tint\t\t\ttype;\n\t//int\t\trendermode;\t//present only in halflife sprites.\n\tfloat\t\tboundingradius;\n\tint\t\t\twidth;\n\tint\t\t\theight;\n\tint\t\t\tnumframes;\n\tfloat\t\tbeamlength;\n\tsynctype_t\tsynctype;\n} dsprite_t;\n\n#define SPR_VP_PARALLEL_UPRIGHT\t\t0\t//xy faces view. z is always up.\n#define SPR_FACING_UPRIGHT\t\t\t1\t//xy faces view. z is always up.\n#define SPR_VP_PARALLEL\t\t\t\t2\t//faces camera (traditional sprite)\n#define SPR_ORIENTED\t\t\t\t3\t//uses axis\n#define SPR_VP_PARALLEL_ORIENTED\t4\t//faces camera *then* rotated\n#define SPRDP_LABEL\t\t\t\t\t5\n#define SPRDP_LABEL_SCALE\t\t\t6\n#define SPRDP_OVERHEAD\t\t\t\t7\n\n#define SPR_ORIENTED_BACKFACE\t\t8\t//SPR_ORIENTED except facing along its normal...\n\n#define SPRHL_OPAQUE\t0\n#define SPRHL_ADDITIVE\t1\n#define SPRHL_INDEXALPHA\t2\n#define SPRHL_ALPHATEST\t3\n\ntypedef struct {\n\tint\t\t\torigin[2];\n\tint\t\t\twidth;\n\tint\t\t\theight;\n} dspriteframe_t;\n\ntypedef struct {\n\tint\t\t\tnumframes;\n} dspritegroup_t;\n\ntypedef struct {\n\tfloat\tinterval;\n} dspriteinterval_t;\n\ntypedef enum { SPR_SINGLE=0, SPR_GROUP, SPR_ANGLED } spriteframetype_t;\n\ntypedef struct {\n\tspriteframetype_t\ttype;\n} dspriteframetype_t;\n\n#define IDSPRITEHEADER\t\"IDSP\",4\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t// little-endian \"IDSP\"\n\n\n\n\n\n\n\n\n#define MAX_SKINNAME 64\n#define IDSPRITE2HEADER\t\"IDS2\",4\n\t\t// little-endian \"IDS2\"\n#define SPRITE2_VERSION\t2\n\ntypedef struct\n{\n\tint\t\twidth, height;\n\tint\t\torigin_x, origin_y;\t\t// raster coordinates inside pic\n\tchar\tname[MAX_SKINNAME];\t\t// name of pcx file\n} dmd2sprframe_t;\n\ntypedef struct {\n\tint\t\t\tident;\n\tint\t\t\tversion;\n\tint\t\t\tnumframes;\n\tdmd2sprframe_t\tframes[1];\t\t\t// variable sized\n} dmd2sprite_t;\n\n\n"
  },
  {
    "path": "engine/client/sys_axfte.cpp",
    "content": "#include \"quakedef.h\"\r\n\r\n#ifdef _WIN32\r\n#include \"sys_plugfte.h\"\r\n\r\n#include <windows.h>\r\n#include <objsafe.h>\t/*IObjectSafety*/\r\n#include <mshtmdid.h>\t/*DISPID_SECURITYCTX*/\r\n\r\n#include <olectl.h> /*common dispid values*/\r\n\r\n#ifndef DISPID_READYSTATE\r\n/*my oldctl.h is too old*/\r\n#define DISPID_READYSTATE -525\r\n#endif\r\n#ifndef __IOleInPlaceObjectWindowless_INTERFACE_DEFINED__\r\n/*mshtmdid.h didn't declare this, so fall back*/\r\n#define IID_IOleInPlaceObjectWindowless IID_IOleInPlaceObject\r\n#define IOleInPlaceObjectWindowless IOleInPlaceObject\r\n#endif\r\n\r\n#ifndef __IOleInPlaceSiteWindowless_INTERFACE_DEFINED__\r\n#define IOleInPlaceSiteWindowless IOleInPlaceSite\r\n#define IID_IOleInPlaceSiteWindowless IID_IOleInPlaceSite\r\n#endif\r\n\r\nconst GUID axfte_iid = {0x7d676c9f, 0xfb84, 0x40b6, {0xb3, 0xff, 0xe1, 0x08, 0x31, 0x55, 0x7e, 0xeb}};\r\n#define axfte_iid_str \"7d676c9f-fb84-40b6-b3ff-e10831557eeb\"\r\nextern \"C\"\r\n{\r\n\textern HINSTANCE\tglobal_hInstance;\r\n}\r\n\r\n#ifdef _MSC_VER\r\n#pragma warning(disable:4584) /*shush now*/\r\n#endif\r\n\r\nclass axfte : public IDispatch, public IClassFactory, public IObjectSafety, \r\n\tpublic IOleObject, public IOleInPlaceObjectWindowless, public IViewObject, public IPersistPropertyBag2\r\n{\r\nprivate:\r\n\tunsigned int ref;\r\n\tIUnknown *site;\r\n\tstruct context *plug;\r\n\tconst struct plugfuncs *funcs;\r\n\tHWND phwnd;\r\n\tstatic const struct browserfuncs axbrowserfuncs;\r\n\r\npublic:\r\n\taxfte()\r\n\t{\r\n\t\tref = 0;\r\n\t\tsite = NULL;\r\n\t\tphwnd = NULL;\r\n\t\tfuncs = Plug_GetFuncs(PLUG_APIVER);\r\n\t\tplug = funcs->CreateContext(this, &axbrowserfuncs);\r\n\t}\r\n\t~axfte()\r\n\t{\r\n\t\tfuncs->DestroyContext(plug);\r\n\t\tif (site)\r\n\t\t\tsite->Release();\r\n\t\tsite = NULL;\r\n\t}\r\n\tstatic void statuschanged(void *arg)\r\n\t{\r\n\t\t//potentially comes from another thread\r\n\t\t//axfte *fte = (axfte*)arg;\r\n\t\tInvalidateRect(NULL, NULL, FALSE);\r\n\t}\r\n\r\n\t/*IUnknown*/\r\n\tvirtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void __RPC_FAR *__RPC_FAR *ppvObject)\r\n\t{\r\n\t\t*ppvObject = NULL;\r\n\t\tif (riid == IID_IUnknown)\r\n\t\t{\r\n\t\t\t*ppvObject = (IUnknown*)(IDispatch*)this;\r\n\t\t\t((LPUNKNOWN)*ppvObject)->AddRef();\r\n\t\t\treturn S_OK;\r\n\t\t}\r\n\t\telse if (riid == IID_IDispatch)\r\n\t\t{\r\n\t\t\t*ppvObject = (IDispatch*)this;\r\n\t\t\t((LPUNKNOWN)*ppvObject)->AddRef();\r\n\t\t\treturn S_OK;\r\n\t\t}\r\n\t\telse if (riid == IID_IClassFactory)\r\n\t\t{\r\n\t\t\t*ppvObject = (IClassFactory*)this;\r\n\t\t\t((LPUNKNOWN)*ppvObject)->AddRef();\r\n\t\t\treturn S_OK;\r\n\t\t}\r\n\t\telse if (riid == IID_IObjectSafety)\r\n\t\t{\r\n\t\t\t*ppvObject = (IObjectSafety*)this;\r\n\t\t\t((LPUNKNOWN)*ppvObject)->AddRef();\r\n\t\t\treturn S_OK;\r\n\t\t}\r\n/*\t\telse if (riid == IID_IPersistPropertyBag2)\r\n\t\t{\r\n\t\t\t*ppvObject = (IPersistPropertyBag2*)this;\r\n\t\t\t((LPUNKNOWN)*ppvObject)->AddRef();\r\n\t\t\treturn S_OK;\r\n\t\t}*/\r\n\t\telse if (riid == IID_IOleObject)\r\n\t\t{\r\n\t\t\t*ppvObject = (IOleObject*)this;\r\n\t\t\t((LPUNKNOWN)*ppvObject)->AddRef();\r\n\t\t\treturn S_OK;\r\n\t\t}\r\n\t\telse if (riid == IID_IOleInPlaceObject)\r\n\t\t{\r\n\t\t\t*ppvObject = (IOleInPlaceObject*)this;\r\n\t\t\t((LPUNKNOWN)*ppvObject)->AddRef();\r\n\t\t\treturn S_OK;\r\n\t\t}\r\n\t\telse if (riid == IID_IOleInPlaceObjectWindowless)\r\n\t\t{\r\n\t\t\t*ppvObject = (IOleInPlaceObjectWindowless*)this;\r\n\t\t\t((LPUNKNOWN)*ppvObject)->AddRef();\r\n\t\t\treturn S_OK;\r\n\t\t}\r\n\r\n\t\telse if (riid == IID_IOleWindow)\r\n\t\t{\r\n\t\t\t*ppvObject = (IOleWindow*)(IOleInPlaceObject*)this;\r\n\t\t\t((LPUNKNOWN)*ppvObject)->AddRef();\r\n\t\t\treturn S_OK;\r\n\t\t}\r\n\t\telse if (riid == IID_IOleInPlaceObject)\r\n\t\t{\r\n\t\t\t*ppvObject = (IOleInPlaceObject*)this;\r\n\t\t\t((LPUNKNOWN)*ppvObject)->AddRef();\r\n\t\t\treturn S_OK;\r\n\t\t}\r\n\t\telse if (riid == IID_IViewObject)\r\n\t\t{\r\n\t\t\t*ppvObject = (IViewObject*)this;\r\n\t\t\t((LPUNKNOWN)*ppvObject)->AddRef();\r\n\t\t\treturn S_OK;\r\n\t\t}\r\n\r\n\t\treturn E_NOINTERFACE;\r\n\t}\r\n\r\n\tvirtual ULONG STDMETHODCALLTYPE AddRef( void)\r\n\t{\r\n\t\treturn ++ref;\r\n\t}\r\n\r\n\tvirtual ULONG STDMETHODCALLTYPE Release( void)\r\n\t{\r\n\t\tif (ref == 1)\r\n\t\t{\r\n\t\t\tdelete this;\r\n\t\t\treturn 0;\r\n\t\t}\r\n\t\treturn --ref;\r\n\t}\r\n\r\n\r\n\r\n\r\n\r\n\r\n\t/*IDispatch*/\r\n\tvirtual HRESULT STDMETHODCALLTYPE GetTypeInfoCount( \r\n\t\t/* [out] */ UINT *pctinfo)\r\n\t{\r\n\t\treturn E_NOTIMPL;\r\n\t}\r\n\r\n\tvirtual HRESULT STDMETHODCALLTYPE GetTypeInfo( \r\n\t\t/* [in] */ UINT iTInfo,\r\n\t\t/* [in] */ LCID lcid,\r\n\t\t/* [out] */ ITypeInfo **ppTInfo)\r\n\t{\r\n\t\treturn E_NOTIMPL;\r\n\t}\r\n\r\n\tvirtual HRESULT STDMETHODCALLTYPE GetIDsOfNames( \r\n\t\t/* [in] */ REFIID riid,\r\n\t\t/* [size_is][in] */ LPOLESTR *rgszNames,\r\n\t\t/* [in] */ UINT cNames,\r\n\t\t/* [in] */ LCID lcid,\r\n\t\t/* [size_is][out] */ DISPID *rgDispId)\r\n\t{\r\n\t\tchar tmp[1024];\r\n\t\tHRESULT ret = S_OK;\r\n\t\tUINT i;\r\n\t\tint prop;\r\n\t\tfor (i = 0; i < cNames; i++)\r\n\t\t{\r\n\t\t\twcstombs(tmp, rgszNames[i], sizeof(tmp));\r\n\t\t\tprop = funcs->FindProp(plug, tmp);\r\n\t\t\tif (prop >= 0)\r\n\t\t\t{\r\n\t\t\t\trgDispId[i] = prop;\r\n\t\t\t}\r\n\t\t\telse if (!stricmp(tmp, \"unselectable\"))\r\n\t\t\t\trgDispId[i] = 5001;\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\trgDispId[i] = DISPID_UNKNOWN;\r\n\t\t\t\tret = DISP_E_UNKNOWNNAME;\r\n\t\t\t}\r\n\t\t}\r\n\t\treturn ret;\r\n\t}\r\n\r\n\tvirtual /* [local] */ HRESULT STDMETHODCALLTYPE Invoke( \r\n\t\t/* [in] */ DISPID dispIdMember,\r\n\t\t/* [in] */ REFIID riid,\r\n\t\t/* [in] */ LCID lcid,\r\n\t\t/* [in] */ WORD wFlags,\r\n\t\t/* [out][in] */ DISPPARAMS *pDispParams,\r\n\t\t/* [out] */ VARIANT *pVarResult,\r\n\t\t/* [out] */ EXCEPINFO *pExcepInfo,\r\n\t\t/* [out] */ UINT *puArgErr)\r\n\t{\r\n\t\tif(wFlags & DISPATCH_METHOD)\r\n\t\t{\r\n\t\t\tMessageBox(NULL, \"\", \"invoke method!\", 0);\r\n\t\t\treturn DISP_E_MEMBERNOTFOUND;\r\n\t\t}\r\n\t\telse if (wFlags & DISPATCH_PROPERTYGET)\r\n\t\t{\r\n\t\t\tVariantClear(pVarResult);\r\n\t\t\tswitch(dispIdMember)\r\n\t\t\t{\r\n\t\t\tcase DISPID_READYSTATE:\r\n\t\t\t\tpVarResult->vt = VT_INT;\r\n\t\t\t\tpVarResult->intVal = READYSTATE_COMPLETE;\r\n\t\t\t\tbreak;\r\n\t\t\tcase DISPID_ENABLED:\r\n\t\t\t\treturn DISP_E_MEMBERNOTFOUND;\r\n\t\t\tcase DISPID_SECURITYCTX:\r\n\t\t\t\treturn DISP_E_MEMBERNOTFOUND;\r\n\t\t\tdefault:\r\n\t\t\t\tif (dispIdMember >= 0 && dispIdMember < 1000)\r\n\t\t\t\t{\r\n\t\t\t\t\tconst char *tmpa;\r\n\t\t\t\t\twchar_t tmpw[1024];\r\n\t\t\t\t\tif (funcs->GetFloat(plug, dispIdMember, &pVarResult->fltVal))\r\n\t\t\t\t\t\tpVarResult->vt = VT_R4;\r\n\t\t\t\t\telse if (funcs->GetInteger(plug, dispIdMember, &pVarResult->intVal))\r\n\t\t\t\t\t\tpVarResult->vt = VT_I4;\r\n\t\t\t\t\telse if (funcs->GetString(plug, dispIdMember, &tmpa))\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tmbstowcs(tmpw, tmpa, sizeof(tmpw)/sizeof(tmpw[0]));\r\n\t\t\t\t\t\tfuncs->GotString(tmpa);\r\n\t\t\t\t\t\tpVarResult->vt = VT_BSTR;\r\n\t\t\t\t\t\tpVarResult->bstrVal = SysAllocString(tmpw);\r\n\t\t\t\t\t}\r\n\t\t\t\t\telse\r\n\t\t\t\t\t\treturn DISP_E_MEMBERNOTFOUND;\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\tchar tmp[1024];\r\n\t\t\t\t\tsprintf(tmp, \"DISPATCH_PROPERTYGET dispIdMember=%i\", (unsigned int)dispIdMember);\r\n\t\t\t\t\tOutputDebugStringA(tmp);\r\n\t\t\t\t\treturn DISP_E_MEMBERNOTFOUND;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t\telse if (wFlags & DISPATCH_PROPERTYPUT)\r\n\t\t{\r\n\t\t\tif (dispIdMember >= 0 && dispIdMember < 1000)\r\n\t\t\t{\r\n\t\t\t\tVARIANT *v = &pDispParams->rgvarg[0];\r\n\t\t\t\tswitch(v->vt)\r\n\t\t\t\t{\r\n\t\t\t\tcase VT_R4:\r\n\t\t\t\t\tfuncs->SetFloat(plug, dispIdMember, v->fltVal);\r\n\t\t\t\t\tbreak;\r\n\t\t\t\tcase VT_R8:\r\n\t\t\t\t\tfuncs->SetFloat(plug, dispIdMember, v->dblVal);\r\n\t\t\t\t\tbreak;\r\n\t\t\t\tcase VT_INT:\r\n\t\t\t\tcase VT_I4:\r\n\t\t\t\t\tfuncs->SetInteger(plug, dispIdMember, v->intVal);\r\n\t\t\t\t\tbreak;\r\n\t\t\t\tcase VT_BSTR:\r\n\t\t\t\t\tfuncs->SetWString(plug, dispIdMember, v->bstrVal);\r\n\t\t\t\t\tbreak;\r\n\t\t\t\tdefault:\r\n\t\t\t\t\treturn DISP_E_TYPEMISMATCH;\r\n\t\t\t\t}\r\n\t\t\t\treturn S_OK;\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tchar tmp[1024];\r\n\t\t\t\tsprintf(tmp, \"DISPATCH_PROPERTYPUT dispIdMember=%i\", (unsigned int)dispIdMember);\r\n\t\t\t\tOutputDebugStringA(tmp);\r\n\t\t\t\treturn DISP_E_MEMBERNOTFOUND;\r\n\t\t\t}\r\n\t\t}\r\n\t\telse if (wFlags & DISPATCH_PROPERTYPUTREF)\r\n\t\t{\r\n\t\t\tchar tmp[1024];\r\n\t\t\tsprintf(tmp, \"DISPATCH_PROPERTYPUTREF dispIdMember=%i\", (unsigned int)dispIdMember);\r\n\t\t\tOutputDebugStringA(tmp);\r\n\t\t\treturn DISP_E_MEMBERNOTFOUND;\r\n\t\t}\r\n\t\telse\r\n\t\t\treturn DISP_E_MEMBERNOTFOUND;\r\n\r\n\t\treturn S_OK;\r\n\t}\r\n\r\n\r\n\t/*IClassFactory*/\r\n\tvirtual /* [local] */ HRESULT STDMETHODCALLTYPE CreateInstance( \r\n\t\t/* [unique][in] */ IUnknown *pUnkOuter,\r\n\t\t/* [in] */ REFIID riid,\r\n\t\t/* [iid_is][out] */ void **ppvObject)\r\n\t{\r\n\t\tHRESULT res;\r\n\r\n\t\tif (pUnkOuter)\r\n\t\t\treturn CLASS_E_NOAGGREGATION;\r\n\r\n\t\taxfte *newaxfte = new axfte();\r\n\t\tres = newaxfte->QueryInterface(riid, ppvObject);\r\n\t\tif (!*ppvObject)\r\n\t\t\tdelete newaxfte;\r\n\t\treturn res;\r\n\t}\r\n\r\n\tvirtual /* [local] */ HRESULT STDMETHODCALLTYPE LockServer( \r\n\t\t/* [in] */ BOOL fLock)\r\n\t{\r\n\t\treturn S_OK;\r\n\t}\r\n\r\n\t/*IObjectSafety*/\r\n\tvirtual HRESULT STDMETHODCALLTYPE GetInterfaceSafetyOptions( \r\n\t\t/* [in] */ REFIID riid,\r\n\t\t/* [out] */ DWORD *pdwSupportedOptions,\r\n\t\t/* [out] */ DWORD *pdwEnabledOptions)\r\n\t{\r\n\t\t*pdwSupportedOptions = *pdwEnabledOptions = INTERFACESAFE_FOR_UNTRUSTED_CALLER | INTERFACESAFE_FOR_UNTRUSTED_DATA;\r\n\t\treturn S_OK;\r\n\t}\r\n\tvirtual HRESULT STDMETHODCALLTYPE SetInterfaceSafetyOptions( \r\n\t\t/* [in] */ REFIID riid,\r\n\t\t/* [in] */ DWORD dwOptionSetMask,\r\n\t\t/* [in] */ DWORD dwEnabledOptions)\r\n\t{\r\n\t\treturn S_OK;\r\n\t}\r\n\r\n\t/*IOleWindow*/\r\n\tvirtual /* [input_sync] */ HRESULT STDMETHODCALLTYPE GetWindow( \r\n\t\t/* [out] */ HWND *phwnd) \r\n\t{\r\n\t\t*phwnd = NULL;\r\n\t\treturn E_NOTIMPL;\r\n\t}\r\n\r\n\tvirtual HRESULT STDMETHODCALLTYPE ContextSensitiveHelp( \r\n\t\t/* [in] */ BOOL fEnterMode)\r\n\t{\r\n\t\treturn E_NOTIMPL;\r\n\t}\r\n\r\n\t/*IOleInPlaceObject*/\r\n\tvirtual HRESULT STDMETHODCALLTYPE InPlaceDeactivate( void)\r\n\t{\r\n\t\treturn E_NOTIMPL;\r\n\t}\r\n\r\n\tvirtual HRESULT STDMETHODCALLTYPE UIDeactivate( void)\r\n\t{\r\n\t\treturn E_NOTIMPL;\r\n\t}\r\n\r\n\tvirtual /* [input_sync] */ HRESULT STDMETHODCALLTYPE SetObjectRects( \r\n\t\t/* [in] */ LPCRECT lprcPosRect,\r\n\t\t/* [in] */ LPCRECT lprcClipRect)\r\n\t{\r\n\t\tif (phwnd)\r\n\t\t\tfuncs->ChangeWindow(plug, phwnd, lprcPosRect->left, lprcPosRect->top, lprcPosRect->right - lprcPosRect->left, lprcPosRect->bottom - lprcPosRect->top);\r\n\t\treturn S_OK;\r\n\t}\r\n\r\n\tvirtual HRESULT STDMETHODCALLTYPE ReactivateAndUndo( void)\r\n\t{\r\n\t\treturn E_NOTIMPL;\r\n\t}\r\n\r\n\t/*IOleObject*/\r\n\tvirtual HRESULT STDMETHODCALLTYPE SetClientSite( \r\n\t\t/* [unique][in] */ IOleClientSite *pClientSite)\r\n\t{\r\n\t\tIUnknown *osite = site;\r\n\t\tsite = pClientSite;\r\n\t\tif (site)\r\n\t\t\tsite->AddRef();\r\n\r\n\t\tIOleInPlaceSiteWindowless *oipc;\r\n\t\tif (site)\r\n\t\tif (!FAILED(site->QueryInterface(IID_IOleInPlaceSiteWindowless, (void**)&oipc)))\r\n\t\t{\r\n\t\t\tIOleInPlaceFrame *pframe;\r\n\t\t\tIOleInPlaceUIWindow *pdoc;\r\n\t\t\tRECT posrect;\r\n\t\t\tRECT cliprect;\r\n\t\t\tOLEINPLACEFRAMEINFO frameinfo;\r\n\t\t\tmemset(&frameinfo, 0, sizeof(frameinfo));\r\n\t\t\tframeinfo.cb = sizeof(frameinfo);\r\n\t\t\toipc->GetWindowContext(&pframe, &pdoc, &posrect, &cliprect, &frameinfo);\r\n\t\t\tif (pframe) pframe->Release();\r\n\t\t\tif (pdoc) pdoc->Release();\r\n\t\t\tphwnd = frameinfo.hwndFrame;\r\n\t\t\toipc->Release();\r\n\t\t}\r\n\r\n\t\tif (osite)\r\n\t\t\tosite->Release();\r\n\t\treturn S_OK;\r\n\t}\r\n\r\n\tvirtual HRESULT STDMETHODCALLTYPE GetClientSite( \r\n\t\t/* [out] */ IOleClientSite **ppClientSite)\r\n\t{\r\n\t\treturn E_NOTIMPL;\r\n\t}\r\n\r\n\tvirtual HRESULT STDMETHODCALLTYPE SetHostNames( \r\n\t\t/* [in] */ LPCOLESTR szContainerApp,\r\n\t\t/* [unique][in] */ LPCOLESTR szContainerObj)\r\n\t{\r\n\t\treturn E_NOTIMPL;\r\n\t}\r\n\r\n\tvirtual HRESULT STDMETHODCALLTYPE Close( \r\n\t\t/* [in] */ DWORD dwSaveOption)\r\n\t{\r\n\t\tfuncs->SetInteger(plug, funcs->FindProp(plug, \"running\"), 0);\r\n\t\treturn E_NOTIMPL;\r\n\t}\r\n\r\n\tvirtual HRESULT STDMETHODCALLTYPE SetMoniker( \r\n\t\t/* [in] */ DWORD dwWhichMoniker,\r\n\t\t/* [unique][in] */ IMoniker *pmk)\r\n\t{\r\n\t\treturn E_NOTIMPL;\r\n\t}\r\n\r\n\tvirtual HRESULT STDMETHODCALLTYPE GetMoniker( \r\n\t\t/* [in] */ DWORD dwAssign,\r\n\t\t/* [in] */ DWORD dwWhichMoniker,\r\n\t\t/* [out] */ IMoniker **ppmk)\r\n\t{\r\n\t\treturn E_NOTIMPL;\r\n\t}\r\n\r\n\tvirtual HRESULT STDMETHODCALLTYPE InitFromData( \r\n\t\t/* [unique][in] */ IDataObject *pDataObject,\r\n\t\t/* [in] */ BOOL fCreation,\r\n\t\t/* [in] */ DWORD dwReserved)\r\n\t{\r\n\t\treturn E_NOTIMPL;\r\n\t}\r\n\r\n\tvirtual HRESULT STDMETHODCALLTYPE GetClipboardData( \r\n\t\t/* [in] */ DWORD dwReserved,\r\n\t\t/* [out] */ IDataObject **ppDataObject)\r\n\t{\r\n\t\treturn E_NOTIMPL;\r\n\t}\r\n\r\n\tvirtual HRESULT STDMETHODCALLTYPE DoVerb( \r\n\t\t/* [in] */ LONG iVerb,\r\n\t\t/* [unique][in] */ LPMSG lpmsg,\r\n\t\t/* [unique][in] */ IOleClientSite *pActiveSite,\r\n\t\t/* [in] */ LONG lindex,\r\n\t\t/* [in] */ HWND hwndParent,\r\n\t\t/* [unique][in] */ LPCRECT lprcPosRect)\r\n\t{\r\n\t\tswitch(iVerb)\r\n\t\t{\r\n\t\tcase OLEIVERB_INPLACEACTIVATE:\r\n\t\t\tIOleInPlaceSiteWindowless *oipc;\r\n\t\t\tif (!FAILED(pActiveSite->QueryInterface(IID_IOleInPlaceSiteWindowless, (void**)&oipc)))\r\n\t\t\t{\r\n\t\t\t\tIOleInPlaceFrame *pframe;\r\n\t\t\t\tIOleInPlaceUIWindow *pdoc;\r\n\t\t\t\tRECT posrect;\r\n\t\t\t\tRECT cliprect;\r\n\t\t\t\tOLEINPLACEFRAMEINFO frameinfo;\r\n\t\t\t\tmemset(&frameinfo, 0, sizeof(frameinfo));\r\n\t\t\t\tframeinfo.cb = sizeof(frameinfo);\r\n\t\t\t\toipc->GetWindowContext(&pframe, &pdoc, &posrect, &cliprect, &frameinfo);\r\n\t\t\t\tif (pframe) pframe->Release();\r\n\t\t\t\tif (pdoc) pdoc->Release();\r\n\r\n\t\t\t\tphwnd = frameinfo.hwndFrame;\r\n\t\t\t\tfuncs->ChangeWindow(plug, frameinfo.hwndFrame, lprcPosRect->left, lprcPosRect->top, lprcPosRect->right - lprcPosRect->left, lprcPosRect->bottom - lprcPosRect->top);\r\n\t\t\t\t#ifndef __IOleInPlaceSiteWindowless_INTERFACE_DEFINED__\r\n\t\t\t\toipc->OnInPlaceActivate();\r\n\t\t\t\t#else\r\n\t\t\t\toipc->OnInPlaceActivateEx(NULL, 1);\r\n\t\t\t\t#endif\r\n\t\t\t\toipc->Release();\r\n\t\t\t}\r\n\t\t\tbreak;\r\n\t\t}\r\n\t\treturn S_OK;\r\n\t}\r\n\r\n\tvirtual HRESULT STDMETHODCALLTYPE EnumVerbs( \r\n\t\t/* [out] */ IEnumOLEVERB **ppEnumOleVerb)\r\n\t{\r\n\t\treturn E_NOTIMPL;\r\n\t}\r\n\r\n\tvirtual HRESULT STDMETHODCALLTYPE Update( void)\r\n\t{\r\n\t\treturn E_NOTIMPL;\r\n\t}\r\n\r\n\tvirtual HRESULT STDMETHODCALLTYPE IsUpToDate( void)\r\n\t{\r\n\t\treturn E_NOTIMPL;\r\n\t}\r\n\r\n\tvirtual HRESULT STDMETHODCALLTYPE GetUserClassID( \r\n\t\t/* [out] */ CLSID *pClsid)\r\n\t{\r\n\t\treturn E_NOTIMPL;\r\n\t}\r\n\r\n\tvirtual HRESULT STDMETHODCALLTYPE GetUserType( \r\n\t\t/* [in] */ DWORD dwFormOfType,\r\n\t\t/* [out] */ LPOLESTR *pszUserType)\r\n\t{\r\n\t\treturn E_NOTIMPL;\r\n\t}\r\n\r\n\tvirtual HRESULT STDMETHODCALLTYPE SetExtent( \r\n\t\t/* [in] */ DWORD dwDrawAspect,\r\n\t\t/* [in] */ SIZEL *psizel)\r\n\t{\r\n\t\treturn E_NOTIMPL;\r\n\t}\r\n\r\n\tvirtual HRESULT STDMETHODCALLTYPE GetExtent( \r\n\t\t/* [in] */ DWORD dwDrawAspect,\r\n\t\t/* [out] */ SIZEL *psizel)\r\n\t{\r\n\t\treturn E_NOTIMPL;\r\n\t}\r\n\r\n\tvirtual HRESULT STDMETHODCALLTYPE Advise( \r\n\t\t/* [unique][in] */ IAdviseSink *pAdvSink,\r\n\t\t/* [out] */ DWORD *pdwConnection)\r\n\t{\r\n\t\treturn E_NOTIMPL;\r\n\t}\r\n\r\n\tvirtual HRESULT STDMETHODCALLTYPE Unadvise( \r\n\t\t/* [in] */ DWORD dwConnection)\r\n\t{\r\n\t\treturn E_NOTIMPL;\r\n\t}\r\n\r\n\tvirtual HRESULT STDMETHODCALLTYPE EnumAdvise( \r\n\t\t/* [out] */ IEnumSTATDATA **ppenumAdvise)\r\n\t{\r\n\t\treturn E_NOTIMPL;\r\n\t}\r\n\r\n\tvirtual HRESULT STDMETHODCALLTYPE GetMiscStatus( \r\n\t\t/* [in] */ DWORD dwAspect,\r\n\t\t/* [out] */ DWORD *pdwStatus)\r\n\t{\r\n\t\t*pdwStatus = OLEMISC_RECOMPOSEONRESIZE;\r\n\t\treturn S_OK;\r\n\t}\r\n\r\n\tvirtual HRESULT STDMETHODCALLTYPE SetColorScheme( \r\n\t\t/* [in] */ LOGPALETTE *pLogpal)\r\n\t{\r\n\t\treturn E_NOTIMPL;\r\n\t}\r\n\r\n\t/*IViewObject*/\r\n\tvirtual /* [local] */ HRESULT STDMETHODCALLTYPE Draw( \r\n\t\t/* [in] */ DWORD dwDrawAspect,\r\n\t\t/* [in] */ LONG lindex,\r\n\t\t/* [unique][in] */ void *pvAspect,\r\n\t\t/* [unique][in] */ DVTARGETDEVICE *ptd,\r\n\t\t/* [in] */ HDC hdcTargetDev,\r\n\t\t/* [in] */ HDC hdcDraw,\r\n\t\t/* [in] */ LPCRECTL lprcBounds,\r\n\t\t/* [unique][in] */ LPCRECTL lprcWBounds,\r\n\t\t/* [in] */ BOOL ( STDMETHODCALLTYPE *pfnContinue )( \r\n\t\t\t\t\t\t  ULONG_PTR dwContinue),\r\n\t\t/* [in] */ ULONG_PTR dwContinue)\r\n\t{\r\n\t\tstruct contextpublic *pub = (struct contextpublic*)plug;\r\n\t\tint width, height;\r\n\t\tHBITMAP bmp = (HBITMAP)funcs->GetSplashBack(plug, hdcDraw, &width, &height);\r\n\t\tif (bmp)\r\n\t\t{\r\n\t\t\tHDC memDC;\r\n\t\t\tRECT irect;\r\n\t\t\tirect.left = lprcBounds->left;\r\n\t\t\tirect.right = lprcBounds->right;\r\n\t\t\tirect.top = lprcBounds->top;\r\n\t\t\tirect.bottom = lprcBounds->bottom;\r\n\r\n\t\t\tmemDC = CreateCompatibleDC(hdcDraw);\r\n\t\t\tSelectObject(memDC, bmp);\r\n\t\t\tStretchBlt(hdcDraw, irect.left, irect.top, irect.right-irect.left,irect.bottom-irect.top, memDC, 0, 0, width, height, SRCCOPY);\r\n\t\t\tSelectObject(memDC, NULL);\r\n\t\t\tDeleteDC(memDC);\r\n\t\t\tfuncs->ReleaseSplashBack(plug, bmp);\r\n\t\t}\r\n\t\tif (*pub->statusmessage)\r\n\t\t{\r\n\t\t\tSetBkMode(hdcDraw, TRANSPARENT);\r\n\t\t\tTextOutA(hdcDraw, 0, 0, pub->statusmessage, strlen(pub->statusmessage));\r\n\t\t}\r\n\r\n\t\treturn S_OK;\r\n\t}\r\n    \r\n\tvirtual /* [local] */ HRESULT STDMETHODCALLTYPE GetColorSet( \r\n\t\t/* [in] */ DWORD dwDrawAspect,\r\n\t\t/* [in] */ LONG lindex,\r\n\t\t/* [unique][in] */ void *pvAspect,\r\n\t\t/* [unique][in] */ DVTARGETDEVICE *ptd,\r\n\t\t/* [in] */ HDC hicTargetDev,\r\n\t\t/* [out] */ LOGPALETTE **ppColorSet)\r\n\t{\r\n\t\treturn E_NOTIMPL;\r\n\t}\r\n    \r\n\tvirtual /* [local] */ HRESULT STDMETHODCALLTYPE Freeze( \r\n\t\t/* [in] */ DWORD dwDrawAspect,\r\n\t\t/* [in] */ LONG lindex,\r\n\t\t/* [unique][in] */ void *pvAspect,\r\n\t\t/* [out] */ DWORD *pdwFreeze)\r\n\t{\r\n\t\treturn E_NOTIMPL;\r\n\t}\r\n    \r\n\tvirtual HRESULT STDMETHODCALLTYPE Unfreeze( \r\n\t\t/* [in] */ DWORD dwFreeze)\r\n\t{\r\n\t\treturn E_NOTIMPL;\r\n\t}\r\n    \r\n\tvirtual HRESULT STDMETHODCALLTYPE SetAdvise( \r\n\t\t/* [in] */ DWORD aspects,\r\n\t\t/* [in] */ DWORD advf,\r\n\t\t/* [unique][in] */ IAdviseSink *pAdvSink)\r\n\t{\r\n\t\treturn E_NOTIMPL;\r\n\t}\r\n    \r\n\tvirtual /* [local] */ HRESULT STDMETHODCALLTYPE GetAdvise( \r\n\t\t/* [unique][out] */ DWORD *pAspects,\r\n\t\t/* [unique][out] */ DWORD *pAdvf,\r\n\t\t/* [out] */ IAdviseSink **ppAdvSink)\r\n\t{\r\n\t\treturn E_NOTIMPL;\r\n\t}\r\n\r\n\t/*IOleInPlaceObjectWindowless*/\r\n\tvirtual HRESULT STDMETHODCALLTYPE OnWindowMessage( \r\n\t\t/* [in] */ UINT msg,\r\n\t\t/* [in] */ WPARAM wParam,\r\n\t\t/* [in] */ LPARAM lParam,\r\n\t\t/* [out] */ LRESULT *plResult)\r\n\t{\r\n\t\tswitch(msg)\r\n\t\t{\r\n\t\tcase WM_LBUTTONDOWN:\r\n\t\t\tfuncs->SetInteger(plug, funcs->FindProp(plug, \"running\"), 1);\r\n\t\t\treturn S_OK;\r\n\t\tdefault:\r\n\t\t\treturn E_NOTIMPL;\r\n\t\t}\r\n\t}\r\n\r\n\tvirtual HRESULT STDMETHODCALLTYPE GetDropTarget( \r\n\t\t/* [out] */ IDropTarget **ppDropTarget)\r\n\t{\r\n\t\treturn E_NOTIMPL;\r\n\t}\r\n\r\n\t/*IPersist*/\r\n\tvirtual HRESULT STDMETHODCALLTYPE GetClassID( \r\n\t\t/* [out] */ CLSID *pClassID)\r\n\t{\r\n\t\treturn E_NOTIMPL;\r\n\t}\r\n\r\n\t/*IPersistPropertyBag2*/\r\n\tvirtual HRESULT STDMETHODCALLTYPE InitNew( void)\r\n\t{\r\n\t\treturn S_OK;\r\n\t}\r\n\r\n\tvirtual HRESULT STDMETHODCALLTYPE Load( \r\n\t\t/* [in] */ IPropertyBag2 *pPropBag,\r\n\t\t/* [in] */ IErrorLog *pErrLog)\r\n\t{\r\n\t\tPROPBAG2 prop[] =\r\n\t\t{\r\n\t\t\t{PROPBAG2_TYPE_DATA, VT_BSTR, 0, 0, (WCHAR *)L\"splash\", {0}},\r\n\t\t\t{PROPBAG2_TYPE_DATA, VT_BSTR, 0, 0, (WCHAR *)L\"game\", {0}},\r\n\t\t\t{PROPBAG2_TYPE_DATA, VT_BSTR, 0, 0, (WCHAR *)L\"dataDownload\", {0}}\r\n\t\t};\r\n\t\tVARIANT val[sizeof(prop)/sizeof(prop[0])];\r\n\t\tHRESULT res[sizeof(prop)/sizeof(prop[0])];\r\n\t\tmemset(val, 0, sizeof(val));\r\n\t\tpPropBag->Read(sizeof(prop)/sizeof(prop[0]), prop, NULL, val, res);\r\n\r\n\t\tfuncs->SetWString(plug, funcs->FindProp(plug, \"splash\"),\t\tval[0].bstrVal);\r\n\t\tfuncs->SetWString(plug, funcs->FindProp(plug, \"game\"),\t\t\tval[1].bstrVal);\r\n\t\tfuncs->SetWString(plug, funcs->FindProp(plug, \"dataDownload\"),\tval[2].bstrVal);\r\n\t\treturn S_OK;\r\n\t}\r\n\r\n\tvirtual HRESULT STDMETHODCALLTYPE Save( \r\n\t\t/* [in] */ IPropertyBag2 *pPropBag,\r\n\t\t/* [in] */ BOOL fClearDirty,\r\n\t\t/* [in] */ BOOL fSaveAllProperties)\r\n\t{\r\n\t\t/*we don't actually save anything*/\r\n\t\treturn E_NOTIMPL;\r\n\t}\r\n\r\n\tvirtual HRESULT STDMETHODCALLTYPE IsDirty( void)\r\n\t{\r\n\t\treturn E_NOTIMPL;\r\n\t}\r\n};\r\n\r\nconst struct browserfuncs axfte::axbrowserfuncs = {NULL, axfte::statuschanged};\r\n\r\n\r\nextern \"C\"\r\n{\r\n\r\nHRESULT WINAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppv)\r\n{\r\n\t*ppv = NULL;\r\n\r\n\tif (rclsid == axfte_iid)\r\n\t{\r\n\t\tHRESULT res;\r\n\t\taxfte *newaxfte = new axfte();\r\n\t\tres = newaxfte->QueryInterface(riid, ppv);\r\n\t\tif (!*ppv)\r\n\t\t\tdelete newaxfte;\r\n\t\treturn res;\r\n\t}\r\n\r\n\treturn CLASS_E_CLASSNOTAVAILABLE;\r\n}\r\n\r\nHRESULT WINAPI DllCanUnloadNow(void)\r\n{\r\n\treturn S_OK;\r\n}\r\n\r\nstruct\r\n{\r\n\tconst char *key;\r\n\tconst char *value;\r\n} regkeys[] = \r\n{\r\n\t{\"Software\\\\Classes\\\\FTE.FTEPlug\\\\\",\t\t\t\t\t\t\t\t\t\t\t\"FTEPlug Class\"},\r\n\t{\"Software\\\\Classes\\\\FTE.FTEPlug\\\\CurVer\\\\\",\t\t\t\t\t\t\t\t\t\"FTE.FTEPlug.1\"},\r\n\t{\"Software\\\\Classes\\\\FTE.FTEPlug.1\\\\\",\t\t\t\t\t\t\t\t\t\t\t\"FTEPlug Class\"},\r\n\t{\"Software\\\\Classes\\\\FTE.FTEPlug.1\\\\CLSID\\\\\",\t\t\t\t\t\t\t\t\t\"{\"axfte_iid_str\"}\"},\r\n\r\n\t{\"Software\\\\Classes\\\\CLSID\\\\{\"axfte_iid_str\"}\\\\\",\t\t\t\t\t\t\t\t\"\"},\r\n\t{\"Software\\\\Classes\\\\CLSID\\\\{\"axfte_iid_str\"}\\\\InprocHandler32\\\\\",\t\t\t\t\"ole32.dll\"},\r\n\t{\"Software\\\\Classes\\\\CLSID\\\\{\"axfte_iid_str\"}\\\\InprocServer32\\\\\",\t\t\t\t\"***DLLNAME***\"},\r\n\t{\"Software\\\\Classes\\\\CLSID\\\\{\"axfte_iid_str\"}\\\\InprocServer32\\\\ThreadingModel\",\t\"Apartment\"},\r\n\t{\"Software\\\\Classes\\\\CLSID\\\\{\"axfte_iid_str\"}\\\\Programmable\\\\\",\t\t\t\t\t\"\"},\r\n\t{\"Software\\\\Classes\\\\CLSID\\\\{\"axfte_iid_str\"}\\\\VersionIndependentProgID\\\\\",\t\t\"FTE.FTEPlug\"},\r\n\t{\"Software\\\\Classes\\\\CLSID\\\\{\"axfte_iid_str\"}\\\\ProgID\\\\\",\t\t\t\t\t\t\"FTE.FTEPlug.1.0\"},\r\n\r\n\t{\"Software\\\\MozillaPlugins\\\\@fteqw.com/FTE\\\\Description\",\t\t\t\t\t\t\t\t\t\tENGINEWEBSITE},\r\n\t{\"Software\\\\MozillaPlugins\\\\@fteqw.com/FTE\\\\GeckoVersion\",\t\t\t\t\t\t\t\t\t\t\"1.00\"},\r\n\t{\"Software\\\\MozillaPlugins\\\\@fteqw.com/FTE\\\\Path\",\t\t\t\t\t\t\t\t\t\t\t\t\"***DLLNAME***\"},\r\n\t{\"Software\\\\MozillaPlugins\\\\@fteqw.com/FTE\\\\ProductName\",\t\t\t\t\t\t\t\t\t\tFULLENGINENAME},\r\n\t{\"Software\\\\MozillaPlugins\\\\@fteqw.com/FTE\\\\Vendor\",\t\t\t\t\t\t\t\t\t\t\tDISTRIBUTIONLONG},\r\n\t{\"Software\\\\MozillaPlugins\\\\@fteqw.com/FTE\\\\Version\",\t\t\t\t\t\t\t\t\t\t\t\"***VERSION***\"},\r\n\t{\"Software\\\\MozillaPlugins\\\\@fteqw.com/FTE\\\\MimeTypes\\\\application/x-fteplugin\\\\Description\",\t\"FTE Game Engine Plugin\"},\r\n\t{\"Software\\\\MozillaPlugins\\\\@fteqw.com/FTE\\\\MimeTypes\\\\application/x-ftemanifest\\\\Description\",\t\"FTE Game File Manifest Listing\"},\r\n\t{\"Software\\\\MozillaPlugins\\\\@fteqw.com/FTE\\\\MimeTypes\\\\application/x-qtv\\\\Description\",\t\t\t\"QuakeTV Stream Information File\"},\r\n\t{\"Software\\\\MozillaPlugins\\\\@fteqw.com/FTE\\\\MimeTypes\\\\application/x-qtv\\\\Suffixes\",\t\t\t\"qtv\"},\r\n\t{\"Software\\\\MozillaPlugins\\\\@fteqw.com/FTE\\\\Suffixes\\\\qtv\",\t\t\t\t\t\t\t\t\t\t\"\"},\r\n\t{\"Software\\\\MozillaPlugins\\\\@fteqw.com/FTE\\\\Suffixes\\\\mvd\",\t\t\t\t\t\t\t\t\t\t\"\"},\r\n\t{NULL}\r\n};\r\nHRESULT WINAPI DllRegisterServer(void)\r\n{\r\n\tchar binaryname[1024];\r\n\tchar tmp[1024];\r\n\tGetModuleFileName(global_hInstance, binaryname, sizeof(binaryname));\r\n\r\n\tHKEY h;\r\n\tbool allusers = false;\r\n\tint i;\r\n\tconst char *ls;\r\n\r\n\tfor (i = 0; regkeys[i].key; i++)\r\n\t{\r\n\t\tls = strrchr(regkeys[i].key, '\\\\') + 1;\r\n\t\tmemcpy(tmp, regkeys[i].key, ls - regkeys[i].key);\r\n\t\ttmp[ls - regkeys[i].key] = 0;\r\n\r\n\t\tif (RegCreateKeyExA(allusers?HKEY_LOCAL_MACHINE:HKEY_CURRENT_USER, tmp, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &h, NULL))\r\n\t\t\tcontinue;\r\n\t\tif (!strcmp(regkeys[i].value, \"***DLLNAME***\"))\r\n\t\t\tRegSetValueExA(h, ls, 0, REG_SZ, (BYTE*)binaryname, strlen(binaryname));\r\n\t\telse if (!strcmp(regkeys[i].value, \"***VERSION***\"))\r\n\t\t{\r\n\t\t\tchar s[128];\r\n#ifdef OFFICIAL_RELEASE\r\n\t\t\tQ_snprintfz(s, sizeof(s), \"%s v%i.%02i\", DISTRIBUTION, FTE_VER_MAJOR, FTE_VER_MINOR);\r\n#elif defined(SVNREVISION)\r\n\t\t\tQ_snprintfz(s, sizeof(s), \"%s SVN %s\", DISTRIBUTION, STRINGIFY(SVNREVISION));\r\n#else\r\n\t\t\tQ_snprintfz(s, sizeof(s), \"%s build %s\", DISTRIBUTION, __DATE__);\r\n#endif\r\n\t\t\tRegSetValueExA(h, ls, 0, REG_SZ, (BYTE*)s, strlen(s));\r\n\t\t}\r\n\t\telse\r\n\t\t\tRegSetValueExA(h, ls, 0, REG_SZ, (BYTE*)regkeys[i].value, strlen(regkeys[i].value));\r\n\t\tRegCloseKey(h);\r\n\t}\r\n\r\n\treturn S_OK;\r\n}\r\n\r\nHRESULT WINAPI DllUnregisterServer(void)\r\n{\r\n\tint i;\r\n\tbool allusers = false;\r\n\tconst char *ls;\r\n\tchar tmp[1024];\r\n\tHKEY h;\r\n\r\n\tfor (i = 0; regkeys[i].key; i++)\r\n\t{\r\n\t}\r\n\r\n\t/*go backwards*/\r\n\tfor (i--; i>=0; i--)\r\n\t{\r\n\t\tls = strrchr(regkeys[i].key, '\\\\') + 1;\r\n\t\tmemcpy(tmp, regkeys[i].key, ls - regkeys[i].key);\r\n\t\ttmp[ls - regkeys[i].key] = 0;\r\n\r\n\t\tif (*ls)\r\n\t\t{\r\n\t\t\th = NULL;\r\n\t\t\tif (!RegOpenKeyEx(allusers?HKEY_LOCAL_MACHINE:HKEY_CURRENT_USER, tmp, 0, KEY_SET_VALUE, &h))\r\n\t\t\t{\r\n\t\t\t\tRegDeleteValue(h, ls);\r\n\t\t\t\tRegCloseKey(h);\r\n\t\t\t}\r\n\t\t}\r\n\t\telse\r\n\t\t\tRegDeleteKey(allusers?HKEY_LOCAL_MACHINE:HKEY_CURRENT_USER, tmp);\r\n\t}\r\n\r\n\treturn S_OK;\r\n}\r\n\r\n}//externC\r\n#endif\r\n"
  },
  {
    "path": "engine/client/sys_dos.c",
    "content": "#include <quakedef.h>\r\n\r\n//because cake.\r\n#include \"sys_linux.c\"\r\n\r\n#include <dos.h>\r\n#include <dpmi.h>\r\n_go32_dpmi_registers regs;\r\nint dos_int86(int vec)\r\n{\r\n\tint rc;\r\n\tregs.x.ss = regs.x.sp = 0;\r\n\trc = _go32_dpmi_simulate_int(vec, &regs);\r\n\treturn rc || (regs.x.flags & 1);\r\n}\r\n\r\n\r\nstatic int mouse_buttons;\r\nstatic int mouse_numbuttons;\r\nstatic unsigned int dosmousedeviceid;\r\n\r\nstatic unsigned int doskeyboarddeviceid;\r\n\r\n#define KBRINGSIZE 256\r\nstatic struct\r\n{\r\n\tunsigned char buf[KBRINGSIZE];\r\n\tint write;\r\n\tint read;\r\n} kbring;\r\n\r\nvoid TheKBHandler(void)\r\n{\t//this needs to be kept simple and small.\r\n\t//we write to a really simple ringbuffer to avoid needing to lock various code/data pages.\r\n\tkbring.buf[kbring.write++&(KBRINGSIZE-1)] = inportb(0x60);\r\n\toutportb(0x20, 0x20);\r\n}\r\n\r\nunsigned char keymap[256] =\r\n{\r\n\t//this is a copy of the US keymap from vanilla quake.\r\n\t//its so very tempting to switch it to a UK keymap...\r\n\r\n//\t0\t\t\t1\t\t2\t\t3\t\t\t4\t\t5\t\t\t6\t\t\t7 \r\n//\t8\t\t\t9\t\tA\t\tB\t\t\tC\t\tD\t\t\tE\t\t\tF \r\n\t0  ,\t\t27,\t\t'1',\t'2',\t\t'3',\t'4',\t\t'5',\t\t'6',\r\n\t'7',\t\t'8',\t'9',\t'0',\t\t'-',\t'=',\t\tK_BACKSPACE,K_TAB,\t\t// 0\r\n\t'q',\t\t'w',\t'e',\t'r',\t\t't',\t'y',\t\t'u',\t\t'i',\r\n\t'o',\t\t'p',\t'[',\t']',\t\t13 ,\tK_CTRL,\t\t'a',\t\t's',\t// 1\r\n\t'd',\t\t'f',\t'g',\t'h',\t\t'j',\t'k',\t\t'l',\t\t';',\r\n\t'\\'' ,\t\t'`',\tK_LSHIFT,'\\\\',\t\t'z',\t'x',\t\t'c',\t\t'v',\t// 2\r\n\t'b',\t\t'n',\t'm',\t',',\t\t'.',\t'/',\t\tK_RSHIFT,\t'*',\r\n\tK_ALT,\t\t' ',\t0  ,\tK_F1,\t\tK_F2,\tK_F3,\t\tK_F4,\t\tK_F5,\t// 3\r\n\tK_F6,\t\tK_F7,\tK_F8,\tK_F9,\t\tK_F10,\t0  ,\t\t0  ,\t\tK_HOME,\r\n\tK_UPARROW,\tK_PGUP,\t'-',\tK_LEFTARROW,'5',\tK_RIGHTARROW,'+',\t\tK_END,\t// 4\r\n\tK_DOWNARROW,K_PGDN,\tK_INS,\tK_DEL,\t\t0,\t\t0  ,\t\t0,\t\t\tK_F11,\r\n\tK_F12,\t\t0  ,\t0  ,\t0  ,\t\t0  ,\t0  ,\t\t0  ,\t\t0,\t\t// 5\r\n\t0  ,\t\t0  ,\t0  ,\t0  ,\t\t0  ,\t0  ,\t\t0  ,\t\t0,\r\n\t0  ,\t\t0  ,\t0  ,\t0  ,\t\t0  ,\t0  ,\t\t0  ,\t\t0,\t\t// 6\r\n\t0  ,\t\t0  ,\t0  ,\t0  ,\t\t0  ,\t0  ,\t\t0  ,\t\t0,\r\n\t0  ,\t\t0  ,\t0  ,\t0  ,\t\t0  ,\t0  ,\t\t0  ,\t\t0,\t\t// 7\r\n\r\n//\t0\t\t\t1\t\t2\t\t3\t\t\t4\t\t5\t\t\t6\t\t\t7\r\n//\t8\t\t\t9\t\tA\t\tB\t\t\tC\t\tD\t\t\tE\t\t\tF\r\n\t0  ,\t\t27,\t\t'!',\t'@',\t\t'#',\t'$',\t\t'%',\t\t'^',\r\n\t'&',\t\t'*',\t'(',\t')',\t\t'_',\t'+',\t\tK_BACKSPACE,K_TAB,\t\t// 0\r\n\t'Q',\t\t'W',\t'E',\t'R',\t\t'T',\t'Y',\t\t'U',\t\t'I',\r\n\t'O',\t\t'P',\t'{',\t'}',\t\t13 ,\tK_CTRL,\t\t'A',\t\t'S',\t// 1\r\n\t'D',\t\t'F',\t'G',\t'H',\t\t'J',\t'K',\t\t'L',\t\t':',\r\n\t'\\\"',\t\t'~',\tK_LSHIFT,'|',\t\t'Z',\t'X',\t\t'C',\t\t'V',\t// 2\r\n\t'B',\t\t'N',\t'M',\t'<',\t\t'>',\t'?',\t\tK_RSHIFT,\t'*',\r\n\tK_ALT,\t\t' ',\t0  ,\tK_F1,\t\tK_F2,\tK_F3,\t\tK_F4,\t\tK_F5,\t// 3\r\n\tK_F6,\t\tK_F7,\tK_F8,\tK_F9,\t\tK_F10,\t0  ,\t\t0  ,\t\tK_HOME,\r\n\tK_UPARROW,\tK_PGUP,\t'_',\tK_LEFTARROW,'%',\tK_RIGHTARROW,'+',\t\tK_END,\t// 4\r\n\tK_DOWNARROW,K_PGDN,\tK_INS,\tK_DEL,\t\t0,\t\t0,\t\t\t0,\t\t\tK_F11,\r\n\tK_F12,\t\t0  ,\t0  ,\t0  ,\t\t0  ,\t0  ,\t\t0  ,\t\t0,\t\t// 5\r\n\t0  ,\t\t0  ,\t0  ,\t0  ,\t\t0  ,\t0  ,\t\t0  ,\t\t0,\r\n\t0  ,\t\t0  ,\t0  ,\t0  ,\t\t0  ,\t0  ,\t\t0  ,\t\t0,\t\t// 6\r\n\t0  ,\t\t0  ,\t0  ,\t0  ,\t\t0  ,\t0  ,\t\t0  ,\t\t0,\r\n\t0  ,\t\t0  ,\t0  ,\t0  ,\t\t0  ,\t0  ,\t\t0  ,\t\t0\t\t// 7\r\n};\r\n\r\nvoid INS_Init(void)\r\n{\r\n\t//make sure the kb handler and its data won't get paged out.\r\n\t_go32_dpmi_lock_code((void *) TheKBHandler, 512);\r\n\t_go32_dpmi_lock_data((void *) &kbring, sizeof(kbring));\r\n\r\n\t//set up our interrupt handler.\r\n\t_go32_dpmi_seginfo info;\r\n\tinfo.pm_offset = (int) TheKBHandler;\r\n\t_go32_dpmi_allocate_iret_wrapper(&info);\r\n\t_go32_dpmi_set_protected_mode_interrupt_vector(9, &info);\r\n}\r\n\r\nvoid Sys_SendKeyEvents(void)\r\n{\t//this is kinda silly, but the handler can't do it if we want to be correct with respect to virtual memory.\r\n\tstatic int shift_down;\r\n\twhile (kbring.read != kbring.write)\r\n\t{\t//keyboard maps are complicated, annoyingly so. left/right/extended.... and don't get me started on sysreq. some keys don't even have release events!\r\n\t\tunsigned char\tc = kbring.buf[kbring.read++&(KBRINGSIZE-1)];\r\n\t\tunsigned int\tqkey, ukey;\r\n\r\n\t\tqkey = keymap[c&0x7f];\r\n\t\tukey = (qkey >= 32 && qkey < 127)?keymap[(c&0x7f)|(shift_down?128:0)]:0;\r\n\t\tif (c == 0xe0)\r\n\t\t{\t//extended keys...\r\n\t\t\tif (kbring.buf[kbring.read&(KBRINGSIZE-1)] == 0x1d)\r\n\t\t\t\tqkey = K_RSHIFT;\r\n\t\t\telse\r\n\t\t\t\tcontinue;\t//annoying extended keys.\r\n\t\t\tukey = 0;\r\n\t\t\tkbring.read++;\r\n\t\t}\r\n\t\tif (qkey == K_LSHIFT)\r\n\t\t\tshift_down = (shift_down&~1) | ((c&0x80)?0:1);\r\n\t\tif (qkey == K_RSHIFT)\r\n\t\t\tshift_down = (shift_down&~2) | ((c&0x80)?0:2);\r\n//\t\tCon_Printf(\"Keyboard: %x\\n\", c);\r\n\t\tIN_KeyEvent(doskeyboarddeviceid, !(c&0x80), qkey, ukey);\r\n\t}\r\n}\r\n\r\nvoid INS_Move(float *movements, int pnum)\r\n{\r\n}\r\nvoid INS_Commands(void)\r\n{\r\n\tif (!mouse_numbuttons)\r\n\t\treturn;\r\n\r\n\tregs.x.ax = 11;\t\t// read move\r\n\tdos_int86(0x33);\r\n\tif (regs.x.cx || regs.x.dx)\r\n\t{\r\n\t\tIN_MouseMove(dosmousedeviceid, false, (short)regs.x.cx, (short)regs.x.dx, 0, 0);\r\n\r\n\t\tCon_Printf(\"Mouse Move: %i %i\\n\", (short)regs.x.cx, (short)regs.x.dx);\r\n\t}\r\n\r\n\tregs.x.ax = 3;\t\t// read buttons\r\n\tdos_int86(0x33);\r\n\tint b = mouse_buttons ^ regs.x.bx;\r\n\tmouse_buttons = regs.x.bx;\r\n\tfor (int i = 0; i < mouse_numbuttons; i++)\r\n\t{\r\n\t\tif (b&(1u<<i))\r\n\t\t\tIN_KeyEvent(dosmousedeviceid, !!(mouse_buttons&(1u<<i)), K_MOUSE1+i, 0);\r\n\t}\r\n\r\n\tSys_SendKeyEvents();\r\n}\r\nvoid INS_ReInit(void)\r\n{\r\n\tdosmousedeviceid = 0;\r\n\tmouse_buttons = 0;\r\n\tmouse_numbuttons = 0;\r\n\r\n\tregs.x.ax = 0;\r\n\tdos_int86(0x33);\r\n\tif (!regs.x.ax)\r\n\t{\r\n\t\tCon_Printf (\"No mouse found\\n\");\r\n\t\treturn;\r\n\t}\r\n\tmouse_numbuttons = regs.x.bx;\r\n\tif (mouse_numbuttons >= 255)\r\n\t\tmouse_numbuttons = 2;\r\n\tif (mouse_numbuttons > 10)\r\n\t\tmouse_numbuttons = 10;\r\n\tCon_Printf(\"%d-button mouse available\\n\", mouse_numbuttons);\r\n}\r\nvoid INS_Shutdown(void)\r\n{\r\n}\r\nvoid INS_EnumerateDevices(void *ctx, void(*callback)(void *ctx, const char *type, const char *devicename, unsigned int *qdevid))\r\n{\r\n\tcallback(ctx, \"mouse\", \t\t\"dosmouse\", \t&dosmousedeviceid);\r\n\tcallback(ctx, \"keyboard\", \t\"doskeyboard\", \t&doskeyboarddeviceid);\r\n}\r\n\r\nvoid Sys_Sleep (double seconds)\r\n{\r\n\tusleep(seconds * 1000000);\r\n}\r\n"
  },
  {
    "path": "engine/client/sys_droid.c",
    "content": "#include <jni.h>\r\n#include <errno.h>\r\n\r\n#include <android/log.h>\r\n\r\n#include \"quakedef.h\"\r\n#include <unistd.h>\r\n#include <fcntl.h>\r\n#include <dlfcn.h>\r\n#include <sys/stat.h>\r\n#include <dirent.h>\r\n#include <pthread.h>\r\n#include \"glquake.h\"\r\n\r\n#ifndef ANDROID\r\n#error ANDROID wasnt defined\r\n#endif\r\n\r\n#include <android/keycodes.h>\r\n#include <android/native_window_jni.h>\r\n\r\n#ifndef isDedicated\r\n#ifdef SERVERONLY\r\nqboolean isDedicated = true;\r\n#else\r\nqboolean isDedicated = false;\r\n#endif\r\n#endif\r\nextern int r_blockvidrestart;\r\nfloat sys_dpi_x, sys_dpi_y;\r\nstatic void *sys_memheap;\r\n//static unsigned int vibrateduration;\r\nstatic char sys_binarydir[MAX_OSPATH];\r\nstatic char sys_basedir[MAX_OSPATH];\r\nstatic char sys_basepak[MAX_OSPATH];\r\nextern  jmp_buf \thost_abort;\r\nextern qboolean r_forceheadless;\r\nstatic qboolean r_forcevidrestart;\r\nANativeWindow *sys_nativewindow;\r\n\r\n//cvar_t sys_vibrate = CVARFD(\"sys_vibrate\", \"1\", CVAR_ARCHIVE, \"Enables the system vibrator for damage events and such things. The value provided is a duration scaler.\");\r\nstatic cvar_t sys_osk = CVAR(\"sys_osk\", \"0\");\t//to be toggled\r\nstatic cvar_t sys_keepscreenon = CVARFD(\"sys_keepscreenon\", \"1\", CVAR_ARCHIVE, \"If set, the screen will never darken. This might cost some extra battery power, but then so will running a 3d engine.\");\t//to be toggled\r\ncvar_t sys_orientation = CVARFD(\"sys_orientation\", \"landscape\", CVAR_ARCHIVE, \"Specifies what angle to render quake at.\\nValid values are: sensor (autodetect), landscape, portrait, reverselandscape, reverseportrait\");\r\nextern cvar_t vid_conautoscale;\r\nvoid VID_Register(void);\r\n\r\nstatic qboolean sys_wantshutdown;\r\nstatic JavaVM* sys_javavm;\r\nstatic jobject *sys_activity;\r\nstatic jobject *sys_cursurface;\t//surface we're currently trying to draw to\r\nstatic jobject *sys_cursholder; //silly android junk\r\nstatic jobject *sys_newsurface;\t//surface we're meant to be switching our gl context to\r\nstatic jobject *sys_newsholder; //silly android junk\r\nstatic void *sys_mainthread;\r\nstatic void *sys_mainconditional;\r\n\r\n#undef LOGI\r\n#undef LOGW\r\n#undef LOGE\r\n#ifndef LOGI\r\n#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, DISTRIBUTION\"Droid\", __VA_ARGS__))\r\n#endif\r\n#ifndef LOGW\r\n#define LOGW(...) ((void)__android_log_print(ANDROID_LOG_WARN, DISTRIBUTION\"Droid\", __VA_ARGS__))\r\n#endif\r\n#ifndef LOGE\r\n#define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, DISTRIBUTION\"Droid\", __VA_ARGS__))\r\n#endif\r\n\r\nvoid INS_Move(void)\r\n{\r\n}\r\nvoid INS_Commands(void)\r\n{\r\n}\r\nvoid INS_EnumerateDevices(void *ctx, void(*callback)(void *ctx, const char *type, const char *devicename, unsigned int *qdevid))\r\n{\r\n}\r\nvoid INS_Init(void)\r\n{\r\n}\r\nvoid INS_ReInit(void)\r\n{\r\n}\r\nvoid INS_Shutdown(void)\r\n{\r\n}\r\nenum controllertype_e INS_GetControllerType(int id)\r\n{\r\n\treturn CONTROLLER_NONE;\r\n}\r\nvoid INS_Rumble(int joy, uint16_t amp_low, uint16_t amp_high, uint32_t duration)\r\n{\r\n}\r\nvoid INS_RumbleTriggers(int joy, uint16_t left, uint16_t right, uint32_t duration)\r\n{\r\n}\r\nvoid INS_SetLEDColor(int id, vec3_t color)\r\n{\r\n}\r\nvoid INS_SetTriggerFX(int id, const void *data, size_t size)\r\n{\r\n}\r\nvoid Sys_Vibrate(float count)\r\n{\r\n//\tif (count < 0)\r\n//\t\tcount = 0;\r\n//\tvibrateduration += count*10*sys_vibrate.value;\r\n}\r\n\r\nqboolean INS_KeyToLocalName(int qkey, char *buf, size_t bufsize)\r\n{\t//onscreen keyboard? erk.\r\n\treturn false;\r\n}\r\nstatic int mapkey(int androidkey)\r\n{\r\n\tswitch(androidkey)\r\n\t{\r\n\tcase AKEYCODE_SOFT_LEFT:\treturn K_LEFTARROW;\r\n\tcase AKEYCODE_SOFT_RIGHT:\treturn K_RIGHTARROW;\r\n\tcase AKEYCODE_HOME:\t\t\treturn K_HOME;\t//not quite right, but w/e\r\n\tcase AKEYCODE_BACK:\t\t\treturn K_ESCAPE;\r\n//\tcase AKEYCODE_CALL:\t\t\treturn K_;\r\n//\tcase AKEYCODE_ENDCALL:\t\treturn K_;\r\n\tcase AKEYCODE_0:\t\t\treturn '0';\r\n\tcase AKEYCODE_1:\t\t\treturn '1';\r\n\tcase AKEYCODE_2:\t\t\treturn '2';\r\n\tcase AKEYCODE_3:\t\t\treturn '3';\r\n\tcase AKEYCODE_4:\t\t\treturn '4';\r\n\tcase AKEYCODE_5:\t\t\treturn '5';\r\n\tcase AKEYCODE_6:\t\t\treturn '6';\r\n\tcase AKEYCODE_7:\t\t\treturn '7';\r\n\tcase AKEYCODE_8:\t\t\treturn '8';\r\n\tcase AKEYCODE_9:\t\t\treturn '9';\r\n\tcase AKEYCODE_STAR:\t\t\treturn '*';\r\n\tcase AKEYCODE_POUND:\t\treturn '#';\t//americans don't know what a pound symbol looks like.\r\n\tcase AKEYCODE_DPAD_UP:\t\treturn K_GP_DPAD_UP;\r\n\tcase AKEYCODE_DPAD_DOWN:\treturn K_GP_DPAD_DOWN;\r\n\tcase AKEYCODE_DPAD_LEFT:\treturn K_GP_DPAD_LEFT;\r\n\tcase AKEYCODE_DPAD_RIGHT:\treturn K_GP_DPAD_RIGHT;\r\n\tcase AKEYCODE_DPAD_CENTER:\treturn K_ENTER;\r\n\tcase AKEYCODE_VOLUME_UP:\treturn K_VOLUP;\r\n\tcase AKEYCODE_VOLUME_DOWN:\treturn K_VOLDOWN;\r\n\tcase AKEYCODE_POWER:\t\treturn K_POWER;\r\n//\tcase AKEYCODE_CAMERA:\t\treturn K_CAMERA;\r\n//\tcase AKEYCODE_CLEAR:\t\treturn K_;\r\n\tcase AKEYCODE_A:\t\t\treturn 'a';\r\n\tcase AKEYCODE_B:\t\t\treturn 'b';\r\n\tcase AKEYCODE_C:\t\t\treturn 'c';\r\n\tcase AKEYCODE_D:\t\t\treturn 'd';\r\n\tcase AKEYCODE_E:\t\t\treturn 'e';\r\n\tcase AKEYCODE_F:\t\t\treturn 'f';\r\n\tcase AKEYCODE_G:\t\t\treturn 'g';\r\n\tcase AKEYCODE_H:\t\t\treturn 'h';\r\n\tcase AKEYCODE_I:\t\t\treturn 'i';\r\n\tcase AKEYCODE_J:\t\t\treturn 'j';\r\n\tcase AKEYCODE_K:\t\t\treturn 'k';\r\n\tcase AKEYCODE_L:\t\t\treturn 'l';\r\n\tcase AKEYCODE_M:\t\t\treturn 'm';\r\n\tcase AKEYCODE_N:\t\t\treturn 'n';\r\n\tcase AKEYCODE_O:\t\t\treturn 'o';\r\n\tcase AKEYCODE_P:\t\t\treturn 'p';\r\n\tcase AKEYCODE_Q:\t\t\treturn 'q';\r\n\tcase AKEYCODE_R:\t\t\treturn 'r';\r\n\tcase AKEYCODE_S:\t\t\treturn 's';\r\n\tcase AKEYCODE_T:\t\t\treturn 't';\r\n\tcase AKEYCODE_U:\t\t\treturn 'u';\r\n\tcase AKEYCODE_V:\t\t\treturn 'v';\r\n\tcase AKEYCODE_W:\t\t\treturn 'w';\r\n\tcase AKEYCODE_X:\t\t\treturn 'x';\r\n\tcase AKEYCODE_Y:\t\t\treturn 'y';\r\n\tcase AKEYCODE_Z:\t\t\treturn 'z';\r\n\tcase AKEYCODE_COMMA:\t\treturn ',';\r\n\tcase AKEYCODE_PERIOD:\t\treturn '.';\r\n\tcase AKEYCODE_ALT_LEFT:\t\treturn K_LALT;\r\n\tcase AKEYCODE_ALT_RIGHT:\treturn K_RALT;\r\n\tcase AKEYCODE_SHIFT_LEFT:\treturn K_LSHIFT;\r\n\tcase AKEYCODE_SHIFT_RIGHT:\treturn K_RSHIFT;\r\n\tcase AKEYCODE_TAB:\t\t\treturn K_TAB;\r\n\tcase AKEYCODE_SPACE:\t\treturn K_SPACE;\r\n//\tcase AKEYCODE_SYM:\t\t\treturn K_IMEMODE_SYMBOL;\r\n//\tcase AKEYCODE_EXPLORER:\t\treturn K_MM_APP_FILES;\r\n//\tcase AKEYCODE_ENVELOPE:\t\treturn K_MM_APP_EMAIL;\r\n\tcase AKEYCODE_ENTER:\t\treturn K_ENTER;\r\n\tcase AKEYCODE_DEL:\t\t\treturn K_BACKSPACE;\r\n\tcase AKEYCODE_GRAVE:\t\treturn '`';\r\n\tcase AKEYCODE_MINUS:\t\treturn '-';\r\n\tcase AKEYCODE_EQUALS:\t\treturn '=';\r\n\tcase AKEYCODE_LEFT_BRACKET:\treturn '[';\r\n\tcase AKEYCODE_RIGHT_BRACKET:return ']';\r\n\tcase AKEYCODE_BACKSLASH:\treturn '#';\t//this kinda sums up keymaps like this.\r\n\tcase AKEYCODE_SEMICOLON:\treturn ';';\r\n\tcase AKEYCODE_APOSTROPHE:\treturn '\\'';\r\n\tcase AKEYCODE_SLASH:\t\treturn '/';\r\n\tcase AKEYCODE_AT:\t\t\treturn '@';\r\n//\tcase AKEYCODE_NUM:\t\t\treturn K_;\r\n//\tcase AKEYCODE_HEADSETHOOK:\treturn K_;\r\n//\tcase AKEYCODE_FOCUS:\t\treturn K_CAMERAFOCUS;\r\n\tcase AKEYCODE_PLUS:\t\t\treturn '+';\r\n\tcase AKEYCODE_MENU:\t\t\treturn K_APP;\r\n//\tcase AKEYCODE_NOTIFICATION:\treturn K_;\r\n\tcase AKEYCODE_SEARCH:\t\treturn K_SEARCH;\r\n\tcase AKEYCODE_MEDIA_PLAY_PAUSE:\t\treturn K_MM_TRACK_PLAYPAUSE;\r\n\tcase AKEYCODE_MEDIA_STOP:\t\t\treturn K_MM_TRACK_STOP;\r\n\tcase AKEYCODE_MEDIA_NEXT:\t\t\treturn K_MM_TRACK_NEXT;\r\n\tcase AKEYCODE_MEDIA_PREVIOUS:\t\treturn K_MM_TRACK_PREV;\r\n//\tcase AKEYCODE_MEDIA_REWIND:\t\t\treturn K_MM_TRACK_REWIND;\r\n//\tcase AKEYCODE_MEDIA_FAST_FORWARD:\treturn K_MM_TRACK_FASTFWD;\r\n\tcase AKEYCODE_MUTE:\t\t\t\t\treturn K_MM_VOLUME_MUTE;\r\n\tcase AKEYCODE_PAGE_UP:\t\t\t\treturn K_PGUP;\r\n\tcase AKEYCODE_PAGE_DOWN:\t\t\treturn K_PGDN;\r\n//\tcase AKEYCODE_PICTSYMBOLS:\t\t\treturn K_IMEMODE_EMOJI;\r\n//\tcase AKEYCODE_SWITCH_CHARSET:\t\treturn K_IMEMODE_CHARSET;\r\n\tcase AKEYCODE_BUTTON_A:\t\t\t\treturn K_GP_A;\r\n\tcase AKEYCODE_BUTTON_B:\t\t\t\treturn K_GP_B;\r\n//\tcase AKEYCODE_BUTTON_C:\t\t\t\treturn K_GP_C;\r\n\tcase AKEYCODE_BUTTON_X:\t\t\t\treturn K_GP_X;\r\n\tcase AKEYCODE_BUTTON_Y:\t\t\t\treturn K_GP_Y;\r\n//\tcase AKEYCODE_BUTTON_Z:\t\t\t\treturn K_GP_Z;\r\n\tcase AKEYCODE_BUTTON_L1:\t\t\treturn K_GP_LEFT_SHOULDER;\r\n\tcase AKEYCODE_BUTTON_R1:\t\t\treturn K_GP_RIGHT_SHOULDER;\r\n\tcase AKEYCODE_BUTTON_L2:\t\t\treturn K_GP_LEFT_TRIGGER;\r\n\tcase AKEYCODE_BUTTON_R2:\t\t\treturn K_GP_RIGHT_TRIGGER;\r\n\tcase AKEYCODE_BUTTON_THUMBL:\t\treturn K_GP_LEFT_STICK;\r\n\tcase AKEYCODE_BUTTON_THUMBR:\t\treturn K_GP_RIGHT_STICK;\r\n\tcase AKEYCODE_BUTTON_START:\t\t\treturn K_GP_START;\r\n\tcase AKEYCODE_BUTTON_SELECT:\t\treturn K_GP_BACK;\r\n\tcase AKEYCODE_BUTTON_MODE:\t\t\treturn K_GP_GUIDE;\r\n\r\n//And this is the part where you start to see quite why I hate android so much\r\n\tcase 111/*AKEYCODE_ESCAPE*/:\t\treturn K_ESCAPE;\r\n\tcase 112/*AKEYCODE_FORWARD_DEL*/:\treturn K_DEL;\r\n\tcase 113/*AKEYCODE_CTRL_LEFT*/:\t\treturn K_LCTRL;\r\n\tcase 114/*AKEYCODE_CTRL_RIGHT*/:\treturn K_RCTRL;\r\n\tcase 115/*AKEYCODE_CAPS_LOCK*/:\t\treturn K_CAPSLOCK;\r\n\tcase 116/*AKEYCODE_SCROLL_LOCK*/:\treturn K_SCRLCK;\r\n\tcase 117/*AKEYCODE_META_LEFT*/:\t\treturn K_LWIN;\r\n\tcase 118/*AKEYCODE_META_RIGHT*/:\treturn K_RWIN;\r\n//\tcase 119/*AKEYCODE_FUNCTION*/:\t\treturn K_FUNCTION;\r\n//\tcase 120/*AKEYCODE_SYSRQ*/:\t\t\treturn K_SYSRQ;\r\n\tcase 121/*AKEYCODE_BREAK*/:\t\t\treturn K_PAUSE;\r\n\tcase 122/*AKEYCODE_MOVE_HOME*/:\t\treturn K_HOME;\r\n\tcase 123/*AKEYCODE_MOVE_END*/:\t\treturn K_END;\r\n\tcase 124/*AKEYCODE_INSERT*/:\t\treturn K_INS;\r\n//\tcase 125/*AKEYCODE_FORWARD*/:\t\treturn K_FORWARD;\r\n//\tcase 126/*AKEYCODE_MEDIA_PLAY*/:\treturn K_MEDIA_PLAY;\r\n//\tcase 127/*AKEYCODE_MEDIA_PAUSE*/:\treturn K_MEDIA_PAUSE;\r\n//\tcase 128/*AKEYCODE_MEDIA_CLOSE*/:\treturn K_MEDIA_CLOSE;\r\n//\tcase 129/*AKEYCODE_MEDIA_EJECT*/:\treturn K_MEDIA_EJECT;\r\n//\tcase 130/*AKEYCODE_MEDIA_RECORD*/:\treturn K_MEDIA_RECORD;\r\n\tcase 131/*AKEYCODE_F1*/:\t\t\treturn K_F1;\r\n\tcase 132/*AKEYCODE_F2*/:\t\t\treturn K_F2;\r\n\tcase 133/*AKEYCODE_F3*/:\t\t\treturn K_F3;\r\n\tcase 134/*AKEYCODE_F4*/:\t\t\treturn K_F4;\r\n\tcase 135/*AKEYCODE_F5*/:\t\t\treturn K_F5;\r\n\tcase 136/*AKEYCODE_F6*/:\t\t\treturn K_F6;\r\n\tcase 137/*AKEYCODE_F7*/:\t\t\treturn K_F7;\r\n\tcase 138/*AKEYCODE_F8*/:\t\t\treturn K_F8;\r\n\tcase 139/*AKEYCODE_F9*/:\t\t\treturn K_F9;\r\n\tcase 140/*AKEYCODE_F10*/:\t\t\treturn K_F10;\r\n\tcase 141/*AKEYCODE_F11*/:\t\t\treturn K_F11;\r\n\tcase 142/*AKEYCODE_F12*/:\t\t\treturn K_F12;\r\n\tcase 143/*AKEYCODE_NUM_LOCK*/:\t\t\treturn K_KP_NUMLOCK;\r\n\tcase 144/*AKEYCODE_NUMPAD_0*/:\t\t\treturn K_KP_INS;\r\n\tcase 145/*AKEYCODE_NUMPAD_1*/:\t\t\treturn K_KP_END;\r\n\tcase 146/*AKEYCODE_NUMPAD_2*/:\t\t\treturn K_KP_DOWNARROW;\r\n\tcase 147/*AKEYCODE_NUMPAD_3*/:\t\t\treturn K_KP_PGDN;\r\n\tcase 148/*AKEYCODE_NUMPAD_4*/:\t\t\treturn K_KP_LEFTARROW;\r\n\tcase 149/*AKEYCODE_NUMPAD_5*/:\t\t\treturn K_KP_5;\r\n\tcase 150/*AKEYCODE_NUMPAD_6*/:\t\t\treturn K_KP_RIGHTARROW;\r\n\tcase 151/*AKEYCODE_NUMPAD_7*/:\t\t\treturn K_KP_HOME;\r\n\tcase 152/*AKEYCODE_NUMPAD_8*/:\t\t\treturn K_KP_UPARROW;\r\n\tcase 153/*AKEYCODE_NUMPAD_9*/:\t\t\treturn K_KP_PGUP;\r\n\tcase 154/*AKEYCODE_NUMPAD_DIVIDE*/:\t\treturn K_KP_SLASH;\r\n\tcase 155/*AKEYCODE_NUMPAD_MULTIPLY*/:\treturn K_KP_STAR;\r\n\tcase 156/*AKEYCODE_NUMPAD_SUBTRACT*/:\treturn K_KP_MINUS;\r\n\tcase 157/*AKEYCODE_NUMPAD_ADD*/:\t\treturn K_KP_PLUS;\r\n\tcase 158/*AKEYCODE_NUMPAD_DOT*/:\t\treturn K_KP_DEL;\r\n//\tcase 159/*AKEYCODE_NUMPAD_COMMA*/:\t\treturn K_KP_COMMA;\r\n\tcase 160/*AKEYCODE_NUMPAD_ENTER*/:\t\treturn K_KP_ENTER;\r\n\tcase 161/*AKEYCODE_NUMPAD_EQUALS*/:\t\treturn K_KP_EQUALS;\r\n//\tcase 162/*AKEYCODE_NUMPAD_LEFT_PAREN*/:\treturn K_KP_;\r\n//\tcase 163/*AKEYCODE_NUMPAD_RIGHT_PAREN*/:return K_KP_;\r\n\r\n//\tcase 164/*AKEYCODE_VOLUME_MUTE*/:\t\treturn K_;\r\n//\tcase 165/*AKEYCODE_INFO*/:\t\t\t\treturn K_;\r\n//\tcase 166/*AKEYCODE_CHANNEL_UP*/:\t\treturn K_;\r\n//\tcase 167/*AKEYCODE_CHANNEL_DOWN*/:\t\treturn K_;\r\n//\tcase 168/*AKEYCODE_ZOOM_IN*/:\t\t\treturn K_;\r\n//\tcase 169/*AKEYCODE_ZOOM_OUT*/:\t\t\treturn K_;\r\n//\tcase 170/*AKEYCODE_TV*/:\t\t\t\treturn K_;\r\n//\tcase 171/*AKEYCODE_WINDOW*/:\t\t\treturn K_;\r\n//\tcase 172/*AKEYCODE_GUIDE*/:\t\t\t\treturn K_;\r\n//\tcase 173/*AKEYCODE_DVR*/:\t\t\t\treturn K_;\r\n//\tcase 174/*AKEYCODE_BOOKMARK*/:\t\t\treturn K_;\r\n//\tcase 175/*AKEYCODE_CAPTIONS*/:\t\t\treturn K_;\r\n//\tcase 176/*AKEYCODE_SETTINGS*/:\t\t\treturn K_;\r\n//\tcase 177/*AKEYCODE_TV_POWER*/:\t\t\treturn K_;\r\n//\tcase 178/*AKEYCODE_TV_INPUT*/:\t\t\treturn K_;\r\n//\tcase 179/*AKEYCODE_STB_POWER*/:\t\t\treturn K_;\r\n//\tcase 180/*AKEYCODE_STB_INPUT*/:\t\t\treturn K_;\r\n//\tcase 181/*AKEYCODE_AVR_POWER*/:\t\t\treturn K_;\r\n//\tcase 182/*AKEYCODE_AVR_INPUT*/:\t\t\treturn K_;\r\n//\tcase 183/*AKEYCODE_PROG_RED*/:\t\t\treturn K_;\r\n//\tcase 184/*AKEYCODE_PROG_GREEN*/:\t\treturn K_;\r\n//\tcase 185/*AKEYCODE_PROG_YELLOW*/:\t\treturn K_;\r\n//\tcase 186/*AKEYCODE_PROG_BLUE*/:\t\t\treturn K_;\r\n//\tcase 187/*AKEYCODE_APP_SWITCH*/:\t\treturn K_;\r\n\tcase 188/*AKEYCODE_BUTTON_1*/:\t\t\treturn K_AUX1;\r\n\tcase 189/*AKEYCODE_BUTTON_2*/:\t\t\treturn K_AUX2;\r\n\tcase 190/*AKEYCODE_BUTTON_3*/:\t\t\treturn K_AUX3;\r\n\tcase 191/*AKEYCODE_BUTTON_4*/:\t\t\treturn K_AUX4;\r\n\tcase 192/*AKEYCODE_BUTTON_5*/:\t\t\treturn K_AUX5;\r\n\tcase 193/*AKEYCODE_BUTTON_6*/:\t\t\treturn K_AUX6;\r\n\tcase 194/*AKEYCODE_BUTTON_7*/:\t\t\treturn K_AUX7;\r\n\tcase 195/*AKEYCODE_BUTTON_8*/:\t\t\treturn K_AUX8;\r\n\tcase 196/*AKEYCODE_BUTTON_9*/:\t\t\treturn K_AUX9;\r\n\tcase 197/*AKEYCODE_BUTTON_10*/:\t\t\treturn K_AUX10;\r\n\tcase 198/*AKEYCODE_BUTTON_11*/:\t\t\treturn K_AUX11;\r\n\tcase 199/*AKEYCODE_BUTTON_12*/:\t\t\treturn K_AUX12;\r\n\tcase 200/*AKEYCODE_BUTTON_13*/:\t\t\treturn K_AUX13;\r\n\tcase 201/*AKEYCODE_BUTTON_14*/:\t\t\treturn K_AUX14;\r\n\tcase 202/*AKEYCODE_BUTTON_15*/:\t\t\treturn K_AUX15;\r\n\tcase 203/*AKEYCODE_BUTTON_16*/:\t\t\treturn K_AUX16;\r\n//\tcase 204/*AKEYCODE_LANGUAGE_SWITCH*/:\t\t\treturn K_;\t//like shift+space\r\n//\tcase 205/*AKEYCODE_MANNER_MODE*/:\t\t\t\treturn K_;\t//toggles silent-mode\r\n//\tcase 206/*AKEYCODE_3D_MODE*/:\t\t\t\t\treturn K_;\r\n//\tcase 207/*AKEYCODE_CONTACTS*/:\t\t\t\t\treturn K_MM_APP_CONTACTS;\r\n//\tcase 208/*AKEYCODE_CALENDAR*/:\t\t\t\t\treturn K_MM_APP_CALENDAR;\r\n//\tcase 209/*AKEYCODE_MUSIC*/:\t\t\t\t\t\treturn K_MM_APP_MUSIC;\r\n//\tcase 210/*AKEYCODE_CALCULATOR*/:\t\t\t\treturn K_MM_APP_CALCULATOR;\r\n//\tcase 211/*AKEYCODE_ZENKAKU_HANKAKU*/:\t\t\treturn K_IME_;\r\n//\tcase 212/*AKEYCODE_EISU*/:\t\t\t\t\t\treturn K_IME_;\r\n//\tcase 213/*AKEYCODE_MUHENKAN*/:\t\t\t\t\treturn K_IME_;\r\n//\tcase 214/*AKEYCODE_HENKAN*/:\t\t\t\t\treturn K_IME_;\r\n//\tcase 215/*AKEYCODE_KATAKANA_HIRAGANA*/:\t\t\treturn K_IME_;\r\n//\tcase 216/*AKEYCODE_YEN*/:\t\t\t\t\t\treturn K_;\r\n//\tcase 217/*AKEYCODE_RO*/:\t\t\t\t\t\treturn K_;\r\n//\tcase 218/*AKEYCODE_KANA*/:\t\t\t\t\t\treturn K_;\r\n//\tcase 219/*AKEYCODE_ASSIST*/:\t\t\t\t\treturn K_MM_APP_ASSIST;\r\n//\tcase 220/*AKEYCODE_BRIGHTNESS_DOWN*/:\t\t\treturn K_;\r\n//\tcase 221/*AKEYCODE_BRIGHTNESS_UP*/:\t\t\t\treturn K_;\r\n//\tcase 222/*AKEYCODE_MEDIA_AUDIO_TRACK*/:\t\t\treturn K_;\r\n//\tcase 223/*AKEYCODE_SLEEP*/:\t\t\t\t\t\treturn K_;\r\n//\tcase 224/*AKEYCODE_WAKEUP*/:\t\t\t\t\treturn K_;\r\n//\tcase 225/*AKEYCODE_PAIRING*/:\t\t\t\t\treturn K_;\r\n//\tcase 226/*AKEYCODE_MEDIA_TOP_MENU*/:\t\t\treturn K_;\r\n//\tcase 227/*AKEYCODE_11*/:\t\t\t\t\t\treturn K_;\r\n//\tcase 228/*AKEYCODE_12*/:\t\t\t\t\t\treturn K_;\r\n//\tcase 229/*AKEYCODE_LAST_CHANNEL*/:\t\t\t\treturn K_;\r\n//\tcase 230/*AKEYCODE_TV_DATA_SERVICE*/:\t\t\treturn K_;\r\n//\tcase 231/*AKEYCODE_VOICE_ASSIST*/:\t\t\t\treturn K_MM_APP_VOICE;\r\n//\tcase 232/*AKEYCODE_TV_RADIO_SERVICE*/:\t\t\treturn K_;\r\n//\tcase 233/*AKEYCODE_TV_TELETEXT*/:\t\t\t\treturn K_;\r\n//\tcase 234/*AKEYCODE_TV_NUMBER_ENTRY*/:\t\t\treturn K_;\r\n//\tcase 235/*AKEYCODE_TV_TERRESTRIAL_ANALOG*/:\t\treturn K_;\r\n//\tcase 236/*AKEYCODE_TV_TERRESTRIAL_DIGITAL*/:\treturn K_;\r\n//\tcase 237/*AKEYCODE_TV_SATELLITE*/:\t\t\t\treturn K_;\r\n//\tcase 238/*AKEYCODE_TV_SATELLITE_BS*/:\t\t\treturn K_;\r\n//\tcase 239/*AKEYCODE_TV_SATELLITE_CS*/:\t\t\treturn K_;\r\n//\tcase 240/*AKEYCODE_TV_SATELLITE_SERVICE*/:\t\treturn K_;\r\n//\tcase 241/*AKEYCODE_TV_NETWORK*/:\t\t\t\treturn K_;\r\n//\tcase 242/*AKEYCODE_TV_ANTENNA_CABLE*/:\t\t\treturn K_;\r\n//\tcase 243/*AKEYCODE_TV_INPUT_HDMI_1*/:\t\t\treturn K_;\r\n//\tcase 244/*AKEYCODE_TV_INPUT_HDMI_2*/:\t\t\treturn K_;\r\n//\tcase 245/*AKEYCODE_TV_INPUT_HDMI_3*/:\t\t\treturn K_;\r\n//\tcase 246/*AKEYCODE_TV_INPUT_HDMI_4*/:\t\t\treturn K_;\r\n//\tcase 247/*AKEYCODE_TV_INPUT_COMPOSITE_1*/:\t\treturn K_;\r\n//\tcase 248/*AKEYCODE_TV_INPUT_COMPOSITE_2*/:\t\treturn K_;\r\n//\tcase 249/*AKEYCODE_TV_INPUT_COMPONENT_1*/:\t\treturn K_;\r\n//\tcase 250/*AKEYCODE_TV_INPUT_COMPONENT_2*/:\t\treturn K_;\r\n//\tcase 251/*AKEYCODE_TV_INPUT_VGA_1*/:\t\t\treturn K_;\r\n//\tcase 252/*AKEYCODE_TV_AUDIO_DESCRIPTION*/:\t\treturn K_;\r\n//\tcase 253/*AKEYCODE_TV_AUDIO_DESCRIPTION_MIX_UP*/:\treturn K_;\r\n//\tcase 254/*AKEYCODE_TV_AUDIO_DESCRIPTION_MIX_DOWN*/:\treturn K_;\r\n//\tcase 255/*AKEYCODE_TV_ZOOM_MODE*/:\t\t\t\treturn K_;\r\n//\tcase 256/*AKEYCODE_TV_CONTENTS_MENU*/:\t\t\treturn K_;\r\n//\tcase 257/*AKEYCODE_TV_MEDIA_CONTEXT_MENU*/:\t\treturn K_;\r\n//\tcase 258/*AKEYCODE_TV_TIMER_PROGRAMMING*/:\t\treturn K_;\r\n//\tcase 259/*AKEYCODE_HELP*/:\t\t\t\t\t\treturn K_;\r\n//\tcase 260/*AKEYCODE_NAVIGATE_PREVIOUS*/:\t\t\treturn K_;\r\n//\tcase 261/*AKEYCODE_NAVIGATE_NEXT*/:\t\t\t\treturn K_;\r\n//\tcase 262/*AKEYCODE_NAVIGATE_IN*/:\t\t\t\treturn K_;\r\n//\tcase 263/*AKEYCODE_NAVIGATE_OUT*/:\t\t\t\treturn K_;\r\n//\tcase 264/*AKEYCODE_STEM_PRIMARY*/:\t\t\t\treturn K_;\r\n//\tcase 265/*AKEYCODE_STEM_1*/:\t\t\t\t\treturn K_;\r\n//\tcase 266/*AKEYCODE_STEM_2*/:\t\t\t\t\treturn K_;\r\n//\tcase 267/*AKEYCODE_STEM_3*/:\t\t\t\t\treturn K_;\r\n//\tcase 268/*AKEYCODE_DPAD_UP_LEFT*/:\t\t\t\treturn K_UPLEFTARROW;\r\n//\tcase 269/*AKEYCODE_DPAD_DOWN_LEFT*/:\t\t\treturn K_DOWNLEFTARROW;\r\n//\tcase 270/*AKEYCODE_DPAD_UP_RIGHT*/:\t\t\t\treturn K_UPRIGHTARROW;\r\n//\tcase 271/*AKEYCODE_DPAD_DOWN_RIGHT*/:\t\t\treturn K_DOWNRIGHTARROW;\r\n//\tcase 272/*AKEYCODE_MEDIA_SKIP_FORWARD*/:\t\treturn K_;\r\n//\tcase 273/*AKEYCODE_MEDIA_SKIP_BACKWARD*/:\t\treturn K_;\r\n//\tcase 274/*AKEYCODE_MEDIA_STEP_FORWARD*/:\t\treturn K_;\r\n//\tcase 275/*AKEYCODE_MEDIA_STEP_BACKWARD*/:\t\treturn K_;\r\n//\tcase 276/*AKEYCODE_SOFT_SLEEP*/:\t\t\t\treturn K_;\r\n//\tcase 277/*AKEYCODE_CUT*/:\t\t\t\t\t\treturn K_;\r\n//\tcase 278/*AKEYCODE_COPY*/:\t\t\t\t\t\treturn K_;\r\n//\tcase 279/*AKEYCODE_PASTE*/:\t\t\t\t\t\treturn K_;\r\n\tcase 280/*KEYCODE_SYSTEM_NAVIGATION_UP*/:\t\treturn K_UPARROW;\r\n\tcase 281/*KEYCODE_SYSTEM_NAVIGATION_DOWN*/:\t\treturn K_DOWNARROW;\r\n\tcase 282/*KEYCODE_SYSTEM_NAVIGATION_LEFT*/:\t\treturn K_LEFTARROW;\r\n\tcase 283/*KEYCODE_SYSTEM_NAVIGATION_RIGHT*/:\treturn K_RIGHTARROW;\r\n//\tcase 284/*AKEYCODE_ALL_APPS */:\t\t\t\t\treturn K_;\r\n//\tcase 285/*AKEYCODE_REFRESH */:\t\t\t\t\treturn K_;\r\n\tdefault:\r\n\t\tCon_DPrintf(\"Android keycode %i is not supported\\n\", androidkey);\r\n\t}\r\n\treturn 0;\r\n}\r\n\r\n#if 0\r\nstatic void run_intent_url(void)\r\n{\r\n\tjobject act = sys_activity;\r\n\tJNIEnv *jni;\r\n\tif (JNI_OK == (*sys_javavm)->AttachCurrentThread(sys_javavm, &jni, NULL))\r\n\t{\r\n\t\tjobject intent = (*jni)->CallObjectMethod(jni, act, (*jni)->GetMethodID(jni, (*jni)->GetObjectClass(jni, act), \"getIntent\", \"()Landroid/content/Intent;\"));\r\n\t\tif (intent)\r\n\t\t{\r\n\t\t\tjstring data = (*jni)->CallObjectMethod(jni, intent, (*jni)->GetMethodID(jni, (*jni)->GetObjectClass(jni, intent), \"getDataString\", \"()Ljava/lang/String;\"));\r\n\t\t\tif (data)\r\n\t\t\t{\r\n\t\t\t\tconst char *url = (*jni)->GetStringUTFChars(jni, data, NULL);\r\n\t\t\t\tif (url)\r\n\t\t\t\t{\r\n\t\t\t\t\tif (!strncmp(url, \"content:\", 8))\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tCon_Printf(CON_ERROR\"Content uris are not supported\\n\");\r\n\t\t\t\t\t\t/*Java:\r\n\t\t\t\t\t\tCursor cursor = this.getContentResolver().query(data, null, null, null, null);\r\n\t\t\t\t\t\tcursor.moveToFirst();   \r\n\t\t\t\t\t\tString myloc = cursor.getString(0);\r\n\t\t\t\t\t\tcursor.close();\r\n\t\t\t\t\t\t*/\r\n\t\t\t\t\t}\r\n\t\t\t\t\telse\r\n\t\t\t\t\t\tHost_RunFile(url, strlen(url), NULL);\r\n\t\t\t\t\t(*jni)->ReleaseStringUTFChars(jni, data, url);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t\t//FIXME: do we need to release methodids/objects?\r\n\t\t(*sys_javavm)->DetachCurrentThread(sys_javavm);\r\n\t}\r\n}\r\n#endif\r\n\r\nstatic qboolean read_apk_path(char *out, size_t outsize)\r\n{\r\n\tqboolean res = false;\r\n\tjobject act = sys_activity;\r\n\tJNIEnv *jni;\r\n\tif (JNI_OK == (*sys_javavm)->AttachCurrentThread(sys_javavm, &jni, NULL))\r\n\t{\r\n\t\tjstring result = (*jni)->CallObjectMethod(jni, act, (*jni)->GetMethodID(jni, (*jni)->GetObjectClass(jni, act), \"getPackageCodePath\", \"()Ljava/lang/String;\"));\r\n\t\tconst char *tmp = (*jni)->GetStringUTFChars(jni, result, NULL);\r\n\t\tif (tmp)\r\n\t\t{\r\n\t\t\tres = true;\r\n\t\t\tQ_strncpyz(out, tmp, outsize);\r\n\t\t\t(*jni)->ReleaseStringUTFChars(jni, result, tmp);\r\n\t\t}\r\n\r\n\t\t//FIXME: do we need to release methodids/objects?\r\n\t\t(*sys_javavm)->DetachCurrentThread(sys_javavm);\r\n\t}\r\n\r\n\treturn res;\r\n}\r\n\r\nstatic void setsoftkeyboard(int flags)\r\n{\t//the NDK is unusably buggy when it comes to keyboards, so call into java.\r\n\tjobject act = sys_activity;\r\n\tJNIEnv *jni;\r\n\tif (JNI_OK == (*sys_javavm)->AttachCurrentThread(sys_javavm, &jni, NULL))\r\n\t{\r\n\t\tjmethodID func = (*jni)->GetMethodID(jni, (*jni)->GetObjectClass(jni, act), \"showKeyboard\", \"(I)V\" );\r\n\t\tif (func)\r\n\t\t\t(*jni)->CallVoidMethod(jni, act, func, flags);\r\n\r\n\t\t(*sys_javavm)->DetachCurrentThread(sys_javavm);\r\n\t}\r\n}\r\nstatic void showMessageAndQuit(const char *errormsg)\r\n{\t//no nice way to do this from native.\r\n\tjobject act = sys_activity;\r\n\tJNIEnv *jni;\r\n\tif (JNI_OK == (*sys_javavm)->AttachCurrentThread(sys_javavm, &jni, NULL))\r\n\t{\r\n\t\tjmethodID func = (*jni)->GetMethodID(jni, (*jni)->GetObjectClass(jni, act), \"showMessageAndQuit\", \"(Ljava/lang/String;)V\" );\r\n\t\tif (func)\r\n\t\t\t(*jni)->CallVoidMethod(jni, act, func, (*jni)->NewStringUTF(jni, errormsg));\r\n\t\t(*sys_javavm)->DetachCurrentThread(sys_javavm);\r\n\t}\r\n}\r\nstatic void updateOrientation(const char *neworientation)\r\n{\t//no nice way to do this from native.\r\n\tjobject act = sys_activity;\r\n\tJNIEnv *jni;\r\n\tif (JNI_OK == (*sys_javavm)->AttachCurrentThread(sys_javavm, &jni, NULL))\r\n\t{\r\n\t\tjmethodID func = (*jni)->GetMethodID(jni, (*jni)->GetObjectClass(jni, act), \"updateOrientation\", \"(Ljava/lang/String;)V\" );\r\n\t\tif (func)\r\n\t\t\t(*jni)->CallVoidMethod(jni, act, func, (*jni)->NewStringUTF(jni, neworientation));\r\n\t\t(*sys_javavm)->DetachCurrentThread(sys_javavm);\r\n\t}\r\n}\r\nstatic void updateScreenKeepOn(jboolean keepon)\r\n{\t//the NDK is unusably buggy when it comes to keyboards, so call into java.\r\n\tjobject act = sys_activity;\r\n\tJNIEnv *jni;\r\n\tif (JNI_OK == (*sys_javavm)->AttachCurrentThread(sys_javavm, &jni, NULL))\r\n\t{\r\n\t\tjmethodID func = (*jni)->GetMethodID(jni, (*jni)->GetObjectClass(jni, act), \"updateScreenKeepOn\", \"(Z)V\" );\r\n\t\tif (func)\r\n\t\t\t(*jni)->CallVoidMethod(jni, act, func, keepon);\r\n\r\n\t\t(*sys_javavm)->DetachCurrentThread(sys_javavm);\r\n\t}\r\n}\r\n\r\nstatic void setCursorVisibility(jboolean visible)\r\n{\t//this is meant to use the nvidia-added setCursorVisibility function\r\n\t//but its fatal if it doesn't exist, and it doesn't seem to exist.\r\n#if 0\r\n\tjobject act = sys_activity;\r\n\tJNIEnv *jni;\r\n\tif (JNI_OK == (*sys_javavm)->AttachCurrentThread(sys_javavm, &jni, NULL))\r\n\t{\r\n\t\tjobject inputManager = NULL;\r\n\t\tjmethodID setvis = NULL;\r\n\t\tjmethodID func = (*jni)->GetMethodID(jni, (*jni)->GetObjectClass(jni, act), \"getSystemService\", \"(Ljava/lang/String;)Ljava/lang/Object;\" );\r\n\t\tif (func)\r\n\t\t\tinputManager = (*jni)->CallObjectMethod(jni, act, func, (*jni)->NewStringUTF(jni, \"input\"));\r\n\t\tif (inputManager)\r\n\t\t\tsetvis = (*jni)->GetMethodID(jni, (*jni)->GetObjectClass(jni, inputManager), \"setCursorVisibility\", \"(Z)V\" );\r\n\t\tif (setvis)\r\n\t\t\t(*jni)->CallVoidMethod(jni, inputManager, setvis, visible);\r\n\r\n\t\t(*sys_javavm)->DetachCurrentThread(sys_javavm);\r\n\t}\r\n#endif\r\n}\r\n\r\nstatic void FTENativeActivity_keypress(JNIEnv *env, jobject this, jint devid, jboolean down, jint keycode, jint unicode)\r\n{\r\n\tint qkeycode = mapkey(keycode);\r\n//\tSys_Printf(\"FTENativeActivity_keypress: d=%i s=%i a=%i,q=%i u=%i\\n\", devid, down, keycode, qkeycode, unicode);\r\n\tif (devid < 0)\r\n\t\tdevid = 0;\r\n\tIN_KeyEvent(devid, down, qkeycode, unicode);\r\n}\r\nstatic jboolean FTENativeActivity_wantrelative(JNIEnv *env, jobject this)\r\n{\r\n\tif (!in_windowed_mouse.ival)\t//emulators etc have no grabs so we're effectively always windowed in such situations.\r\n\t\treturn false;\r\n\treturn !Key_MouseShouldBeFree();\r\n}\r\nstatic void FTENativeActivity_mousepress(JNIEnv *env, jobject this, jint devid, jint buttonbits)\r\n{\r\n\tstatic int heldbuttons;\r\n\tjint changed = buttonbits^heldbuttons;\r\n//\tSys_Printf(\"FTENativeActivity_mousepress: d=%i bits=%x (changed=%x)\\n\", devid, buttonbits, changed);\r\n\tstatic int qbutton[] = {\r\n\t\tK_MOUSE1,\t//primary\r\n\t\tK_MOUSE2,\t//secondary\r\n\t\tK_MOUSE3,\t//tertiary\r\n\t\tK_MOUSE4,\t//back\r\n\t\tK_MOUSE5,\t//forward\r\n\t\tK_MOUSE1,\t//stylus_primary\r\n\t\tK_MOUSE2,\t//stylus_secondary\r\n\t};\r\n\tsize_t i;\r\n\tif (devid < 0)\r\n\t\tdevid = 0;\r\n\theldbuttons = buttonbits;\r\n\tif (changed)\r\n\tfor (i = 0; i < countof(qbutton); i++)\r\n\t{\r\n\t\tif (changed&(1<<i))\r\n\t\t\tIN_KeyEvent(devid, buttonbits&(1<<i), qbutton[i], 0);\r\n\t}\r\n}\r\nstatic void FTENativeActivity_motion(JNIEnv *env, jobject this, jint ptrid, jint act, jfloat x, jfloat y, jfloat z, jfloat size)\r\n{\r\n\tif (ptrid < 0)\r\n\t\tptrid = 0;\r\n//\tSys_Printf(\"FTENativeActivity_motion: d=%i a=%i x=%f y=%f z=%f s=%f\\n\", ptrid, act, x, y, z, size);\r\n\tswitch(act)\r\n\t{\r\n\tcase 2:\t//mouse down\r\n\tcase 3:\t//mouse up\r\n\t\tIN_KeyEvent(ptrid, act==2, K_TOUCH, 0);\r\n\t\tbreak;\r\n\tcase 1:\t//relative motion\r\n\tcase 0: //absolute motion (android sucks)\r\n\t\tIN_MouseMove(ptrid, act==0, x, y, z, size);\r\n\t\tbreak;\r\n\t};\r\n}\r\nstatic void FTENativeActivity_axis(JNIEnv *env, jobject this, jint devid, jint axis, jfloat value)\r\n{\r\n\tif (devid < 0)\r\n\t\tdevid = 0;\r\n\tIN_JoystickAxisEvent(devid, axis, value);\r\n}\r\n//static void FTENativeActivity_accelerometer(JNIEnv *env, jobject obj, jint devid, jfloat x, jfloat y, jfloat z)\r\n//{\r\n//\tIN_Accelerometer(devid, x, y, z);\r\n//}\r\n//static void FTENativeActivity_gryoscope(JNIEnv *env, jobject obj, jint devid, jfloat pitch, jfloat yaw, jfloat roll)\r\n//{\r\n//\tIN_Gyroscope(devid, pitch, yaw, roll);\r\n//}\r\n\r\nstatic int FTEDroid_MainThread(void *arg)\r\n{\r\n\tint osk = 0, wantgrabs = 0, t;\r\n\tdouble newtime,oldtime=Sys_DoubleTime(), time, sleeptime;\r\n\tr_forceheadless = true;\r\n\tsys_nativewindow = NULL;\r\n\tvid.activeapp = true;\r\n\r\n\tif (!host_initialized)\r\n\t{\r\n\t\tstatic const char *args [] =\r\n\t\t{\r\n\t\t\t\"ftedroid\",\t/*binary name, not really meaningful*/\r\n\t\t\t\"-basepack\",\r\n\t\t\tsys_basepak,\t/*filled in later*/\r\n\t\t\t\"\",\r\n\t\t\t\"\"\r\n\t\t};\r\n\t\tstatic quakeparms_t parms;\r\n\t\tif (sys_memheap)\r\n\t\t\tfree(sys_memheap);\r\n\t\tmemset(&parms, 0, sizeof(parms));\r\n\t\tparms.binarydir = sys_binarydir;\r\n\t\tparms.basedir = sys_basedir;\t/*filled in later*/\r\n\t\tparms.argc = read_apk_path(sys_basepak, sizeof(sys_basepak))?3:1;\r\n\t\tparms.argv = args;\r\n#ifdef CONFIG_MANIFEST_TEXT\r\n\t\tparms.manifest = CONFIG_MANIFEST_TEXT;\r\n#endif\r\n\t\tsys_dpi_x = 72;\t//no idea\r\n\t\tsys_dpi_y = 72;\t//no idea\r\n\r\n\t\tSys_Printf(\"Starting up (apk=%s, usr=%s)\\n\", sys_basepak, parms.basedir);\r\n\r\n\t\tVID_Register();\r\n\t\tCOM_InitArgv(parms.argc, parms.argv);\r\n\t\tTL_InitLanguages(sys_basedir);\r\n\r\n\t\tHost_Init(&parms);\r\n\t\tSys_Printf(\"Host Inited\\n\");\r\n\t}\r\n\telse\r\n\t\tSys_Printf(\"Restarting up!\\n\");\r\n\r\n\tsys_orientation.modified = false;\r\n\tupdateOrientation(sys_orientation.string);\r\n\tsys_keepscreenon.modified = false;\r\n\tupdateScreenKeepOn(sys_keepscreenon.ival);\r\n\r\n//\trun_intent_url();\r\n\t/*if (state->savedState != NULL)\r\n\t{\t//oh look, we're pretending to already be running...\r\n\t\t//oh.\r\n\t}*/\r\n\r\n\r\n\t//we're sufficiently done loading. let the ui thread resume.\r\n\tSys_LockConditional(sys_mainconditional);\r\n\tSys_ConditionSignal(sys_mainconditional);\r\n\tSys_UnlockConditional(sys_mainconditional);\r\n\r\n\twhile (!sys_wantshutdown)\r\n\t{\r\n\t\t//handle things if the UI thread is blocking for us (video restarts)\r\n\t\tSys_LockConditional(sys_mainconditional);\r\n\t\tif (r_forcevidrestart)\r\n\t\t{\r\n\t\t\tANativeWindow *oldwnd = NULL;\r\n\t\t\tjobject oldholder = NULL;\r\n\t\t\tjobject oldsurf = NULL;\r\n\t\t\tJNIEnv *env = NULL;\r\n\t\t\tif (JNI_OK == (*sys_javavm)->AttachCurrentThread(sys_javavm, &env, NULL))\r\n\t\t\t{\r\n\t\t\t\toldholder = sys_cursholder;\r\n\t\t\t\tsys_cursholder = (*env)->NewGlobalRef(env, sys_newsholder);\r\n\t\t\t\toldsurf = sys_cursurface;\r\n\t\t\t\tsys_cursurface = (*env)->NewGlobalRef(env, sys_newsurface);\r\n\r\n\t\t\t\toldwnd = sys_nativewindow;\r\n\t\t\t\tif (sys_cursurface)\r\n\t\t\t\t\tsys_nativewindow = ANativeWindow_fromSurface(env, sys_cursurface);\r\n\t\t\t\telse\r\n\t\t\t\t\tsys_nativewindow = NULL;\r\n\t\t\t\tANativeWindow_acquire(sys_nativewindow);\r\n\t\t\t}\r\n\r\n\t\t\tr_forceheadless = r_forcevidrestart&1;\r\n\t\t\tr_forcevidrestart = 0;\r\n\t\t\tSys_ConditionSignal(sys_mainconditional);\t//let the java ui thread thread wake up now that we've got a handle to the new+old surfaces\r\n\t\t\tSys_UnlockConditional(sys_mainconditional);\r\n\t\t\tLOGI(\"Video Restart...\\n\");\r\n\t\t\tR_RestartRenderer_f();\r\n\t\t\tLOGI(\"Video Restarted...\\n\");\r\n\t\t\t//main thread can wake up now.\r\n\r\n\t\t\tif (oldwnd)\r\n\t\t\t\tANativeWindow_release(oldwnd);\r\n\t\t\tif (oldsurf)\r\n\t\t\t\t(*env)->DeleteGlobalRef(env, oldsurf);\r\n\t\t\tif (oldholder)\r\n\t\t\t\t(*env)->DeleteGlobalRef(env, oldholder);\r\n\t\t\tif (env)\r\n\t\t\t\t(*sys_javavm)->DetachCurrentThread(sys_javavm);\r\n\r\n\t\t\tcontinue;\r\n\t\t}\r\n\r\n\t\tif (sys_nativewindow && vid.activeapp && !r_forcevidrestart)\r\n\t\t{\r\n\t\t\t// find time spent rendering last frame\r\n\t\t\tnewtime = Sys_DoubleTime ();\r\n\t\t\ttime = newtime - oldtime;\r\n\r\n\t\t\tsleeptime = Host_Frame(time);\r\n\t\t\toldtime = newtime;\r\n\r\n\t\t\tif (sleeptime)\r\n\t\t\t\tSys_Sleep(sleeptime);\r\n\t\t}\r\n\t\telse\r\n\t\t\tsleeptime = 0.25;\r\n\t\tSys_UnlockConditional(sys_mainconditional);\r\n\r\n\t\tt = 0;\r\n\t\tif (sys_osk.ival >= 0)\r\n\t\t{\r\n\t\t\tif (Key_Dest_Has(kdm_console|kdm_message))\r\n\t\t\t\tt |= 1;\r\n\t\t\tif (!Key_Dest_Has(~kdm_game) && cls.state == ca_disconnected)\r\n\t\t\t\tt |= 1;\r\n\t\t\tif (sys_osk.ival)\r\n\t\t\t\tt |= 2;\r\n\t\t}\r\n\t\tif (osk != t)\r\n\t\t{\r\n\t\t\tsetsoftkeyboard(t);\r\n\t\t\tosk = t;\r\n\t\t}\r\n\t\r\n\t\tif (sys_orientation.modified)\r\n\t\t{\r\n\t\t\tsys_orientation.modified = false;\r\n\t\t\tupdateOrientation(sys_orientation.string);\r\n\t\t}\r\n\t\r\n\t\tif (sys_keepscreenon.modified)\r\n\t\t{\r\n\t\t\tsys_keepscreenon.modified = false;\r\n\t\t\tupdateScreenKeepOn(sys_keepscreenon.ival);\r\n\t\t}\r\n\r\n\t\tt = FTENativeActivity_wantrelative(NULL,NULL);\r\n\t\tif (wantgrabs != t)\r\n\t\t{\r\n\t\t\twantgrabs = t;\r\n\t\t\tsetCursorVisibility(wantgrabs);\r\n\t\t}\r\n\r\n\r\n\t\tif (sleeptime)\r\n\t\t\tSys_Sleep(sleeptime);\r\n\t}\r\n\r\n\t//don't permanently hold these when there's no active activity.\r\n\t//(hopefully there's no gl context active right now...)\r\n\tJNIEnv *env = NULL;\r\n\tif (JNI_OK == (*sys_javavm)->AttachCurrentThread(sys_javavm, &env, NULL))\r\n\t{\r\n\t\tif (sys_nativewindow)\r\n\t\t\tANativeWindow_release(sys_nativewindow);\r\n\t\tsys_nativewindow = NULL;\r\n\t\tif (sys_cursurface)\r\n\t\t\t(*env)->DeleteGlobalRef(env, sys_cursurface);\r\n\t\tsys_cursurface = NULL;\r\n\t\tif (sys_cursholder)\r\n\t\t\t(*env)->DeleteGlobalRef(env, sys_cursholder);\r\n\t\tsys_cursholder = NULL;\r\n\t\tif (env)\r\n\t\t\t(*sys_javavm)->DetachCurrentThread(sys_javavm);\r\n\t}\r\n\r\n\treturn 0;\r\n}\r\n\r\nstatic int secbase;\r\n\r\n#ifdef _POSIX_TIMERS\r\ndouble Sys_DoubleTime(void)\r\n{\r\n\tstruct timespec ts;\r\n\tclock_gettime(CLOCK_MONOTONIC, &ts);\r\n\r\n\tif (!secbase)\r\n\t{\r\n\t\tsecbase = ts.tv_sec;\r\n\t\treturn ts.tv_nsec/1000000000.0;\r\n\t}\r\n\treturn (ts.tv_sec - secbase) + ts.tv_nsec/1000000000.0;\r\n}\r\nunsigned int Sys_Milliseconds(void)\r\n{\r\n\tstruct timespec ts;\r\n\tclock_gettime(CLOCK_MONOTONIC, &ts);\r\n\r\n\tif (!secbase)\r\n\t{\r\n\t\tsecbase = ts.tv_sec;\r\n\t\treturn ts.tv_nsec/1000000;\r\n\t}\r\n\treturn (ts.tv_sec - secbase)*1000 + ts.tv_nsec/1000000;\r\n}\r\n#else\r\ndouble Sys_DoubleTime(void)\r\n{\r\n\tstruct timeval tp;\r\n\tstruct timezone tzp;\r\n\r\n\tgettimeofday(&tp, &tzp);\r\n\r\n\tif (!secbase)\r\n\t{\r\n\t\t\tsecbase = tp.tv_sec;\r\n\t\t\treturn tp.tv_usec/1000000.0;\r\n\t}\r\n\r\n\treturn (tp.tv_sec - secbase) + tp.tv_usec/1000000.0;\r\n}\r\nunsigned int Sys_Milliseconds(void)\r\n{\r\n\tstruct timeval tp;\r\n\tstruct timezone tzp;\r\n\r\n\tgettimeofday(&tp, &tzp);\r\n\r\n\tif (!secbase)\r\n\t{\r\n\t\tsecbase = tp.tv_sec;\r\n\t\treturn tp.tv_usec/1000;\r\n\t}\r\n\r\n\treturn (tp.tv_sec - secbase)*1000 + tp.tv_usec/1000;\r\n}\r\n#endif\r\n\r\nvoid Sys_Shutdown(void)\r\n{\r\n\tfree(sys_memheap);\r\n}\r\nvoid Sys_Quit(void)\r\n{\r\n#ifndef SERVERONLY\r\n\tHost_Shutdown ();\r\n#else\r\n\tSV_Shutdown();\r\n#endif\r\n\r\n\tLOGI(\"%s\", \"quitting\");\r\n\tshowMessageAndQuit(\"\");\r\n\r\n\tlongjmp(host_abort, 1);\r\n\texit(0);\r\n}\r\nvoid Sys_Error (const char *error, ...)\r\n{\r\n\tva_list         argptr;\r\n\tchar             string[1024];\r\n\r\n\tva_start (argptr, error);\r\n\tvsnprintf (string,sizeof(string)-1, error,argptr);\r\n\tva_end (argptr);\r\n\tCOM_WorkerAbort(string);\r\n\tif (!*string)\r\n\t\tstrcpy(string, \"no error\");\r\n\r\n\tLOGE(\"e: %s\", string);\r\n\tshowMessageAndQuit(string);\r\n\r\n\thost_initialized = false;\t//don't keep calling Host_Frame, because it'll screw stuff up more. Can't trust Host_Shutdown either. :(\r\n\tvid.activeapp = false;\t\t//make sure we don't busyloop.\r\n\tlongjmp(host_abort, 1);\r\n\texit(1);\r\n}\r\nvoid Sys_Printf (char *fmt, ...)\r\n{\r\n\tva_list         argptr;\r\n\tchar *e;\r\n\r\n\t//android doesn't do \\ns properly *sigh*\r\n\t//this means we have to buffer+split it ourselves.\r\n\t//and because of lots of threads, we have to mutex it too.\r\n\tstatic char linebuf[2048];\r\n\tstatic char *endbuf = linebuf;\r\n\tstatic pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;\r\n\tpthread_mutex_lock(&lock);\r\n\r\n\t//append the new data\r\n\tva_start (argptr, fmt);\r\n\tvsnprintf (endbuf,sizeof(linebuf)-(endbuf-linebuf)-1, fmt,argptr);\r\n\tva_end (argptr);\r\n\tendbuf += strlen(endbuf);\r\n\r\n\t//split it on linebreaks\r\n\twhile ((e = strchr(linebuf, '\\n')))\r\n\t{\r\n\t\t*e = 0;\r\n\t\tLOGI(\"%s\", linebuf);\r\n\t\tmemmove(linebuf, e+1, endbuf-(e+1));\r\n\t\tlinebuf[endbuf-(e+1)] = 0;\r\n\t\tendbuf -= (e+1)-linebuf;\r\n\t}\r\n\r\n\tpthread_mutex_unlock(&lock);\r\n}\r\nvoid Sys_Warn (char *fmt, ...)\r\n{\r\n\tva_list         argptr;\r\n\tchar             string[1024];\r\n\r\n\tva_start (argptr, fmt);\r\n\tvsnprintf (string,sizeof(string)-1, fmt,argptr);\r\n\tva_end (argptr);\r\n\r\n\tLOGW(\"w: %s\", string);\r\n}\r\n\r\nvoid Sys_CloseLibrary(dllhandle_t *lib)\r\n{\r\n\tif (lib)\r\n\t\tdlclose(lib);\r\n}\r\nvoid *Sys_GetAddressForName(dllhandle_t *module, const char *exportname)\r\n{\r\n\treturn dlsym(module, exportname);\r\n}\r\ndllhandle_t *Sys_LoadLibrary(const char *name, dllfunction_t *funcs)\r\n{\r\n\tsize_t i;\r\n\tdllhandle_t *h;\r\n\th = dlopen(va(\"%s.so\", name), RTLD_LAZY|RTLD_LOCAL);\r\n\tif (!h)\r\n\t\th = dlopen(name, RTLD_LAZY|RTLD_LOCAL);\r\n\tif (!h)\r\n\t{\r\n\t\tCon_DLPrintf(2,\"%s\\n\", dlerror());\r\n\t\treturn NULL;\r\n\t}\r\n\r\n\tif (h && funcs)\r\n\t{\r\n\t\tfor (i = 0; funcs[i].name; i++)\r\n\t\t{\r\n\t\t\t*funcs[i].funcptr = dlsym(h, funcs[i].name);\r\n\t\t\tif (!*funcs[i].funcptr)\r\n\t\t\t\tbreak;\r\n\t\t}\r\n\t\tif (funcs[i].name)\r\n\t\t{\r\n\t\t\tCon_DPrintf(\"Unable to find symbol \\\"%s\\\" in \\\"%s\\\"\\n\", funcs[i].name, name);\r\n\t\t\tSys_CloseLibrary(h);\r\n\t\t\th = NULL;\r\n\t\t}\r\n\t}\r\n\treturn h;\r\n}\r\nchar *Sys_ConsoleInput (void)\r\n{\r\n\treturn NULL;\r\n}\r\nvoid Sys_mkdir (const char *path)    //not all pre-unix systems have directories (including dos 1)\r\n{\r\n\tmkdir(path, 0755);\r\n}\r\nqboolean Sys_rmdir (const char *path)\r\n{\r\n\tif (rmdir (path) == 0)\r\n\t\treturn true;\r\n\tif (errno == ENOENT)\r\n\t\treturn true;\r\n\treturn false;\r\n}\r\nqboolean Sys_remove (const char *path)\r\n{\r\n\treturn !unlink(path);\r\n}\r\nqboolean Sys_Rename (const char *oldfname, const char *newfname)\r\n{\r\n\treturn !rename(oldfname, newfname);\r\n}\r\n\r\n#if _POSIX_C_SOURCE >= 200112L\r\n\t#include <sys/statvfs.h>\r\n#endif\r\nqboolean Sys_GetFreeDiskSpace(const char *path, quint64_t *freespace)\r\n{\r\n#if _POSIX_C_SOURCE >= 200112L\r\n\t//posix 2001\r\n\tstruct statvfs inf;\r\n\tif(0==statvfs(path, &inf))\r\n\t{\r\n\t\t*freespace = inf.f_bsize*(quint64_t)inf.f_bavail;\r\n\t\treturn true;\r\n\t}\r\n#endif\r\n\treturn false;\r\n}\r\n\r\nvoid Sys_SendKeyEvents(void)\r\n{\r\n}\r\nchar *Sys_URIScheme_NeedsRegistering(void)\r\n{\t//android does its mime/etc registrations via android xml junk. dynamically registering stuff isn't supported, so pretend that its already registered to avoid annoying prompts.\r\n\treturn NULL;\r\n}\r\nvoid Sys_Init(void)\r\n{\r\n\tCvar_Register(&sys_keepscreenon, \"android stuff\");\r\n\tCvar_Register(&sys_orientation, \"android stuff\");\r\n\tCvar_Register(&sys_osk, \"android stuff\");\r\n}\r\n\r\nqboolean Sys_GetDesktopParameters(int *width, int *height, int *bpp, int *refreshrate)\r\n{\r\n\t*width = 320;\r\n\t*height = 240;\r\n\t*bpp = 16;\r\n\t*refreshrate = 60;\r\n\treturn false;\r\n}\r\nqboolean Sys_RandomBytes(qbyte *string, int len)\r\n{\r\n\tqboolean res = false;\r\n\tint fd = open(\"/dev/urandom\", 0);\r\n\tif (fd >= 0)\r\n\t{\r\n\t\tres = (read(fd, string, len) == len);\r\n\t\tclose(fd);\r\n\t}\r\n\r\n\treturn res;\r\n}\r\n\r\nvoid Sys_ServerActivity(void)\r\n{\r\n\t/*FIXME: flash window*/\r\n}\r\n\r\n#ifdef WEBCLIENT\r\nqboolean Sys_RunInstaller(void)\r\n{       //not implemented\r\n\treturn false;\r\n}\r\n#endif\r\n\r\n#ifndef MULTITHREAD\r\nvoid Sys_Sleep (double seconds)\r\n{\r\n\tstruct timespec ts;\r\n\r\n\tts.tv_sec = (time_t)seconds;\r\n\tseconds -= ts.tv_sec;\r\n\tts.tv_nsec = seconds * 1000000000.0;\r\n\r\n\tnanosleep(&ts, NULL);\r\n}\r\n#endif\r\nqboolean Sys_InitTerminal(void)\r\n{\r\n\t/*switching to dedicated mode, show text window*/\r\n\treturn false;\r\n}\r\nvoid Sys_CloseTerminal(void)\r\n{\r\n}\r\n\r\n#define SYS_CLIPBOARD_SIZE  256\r\nstatic char clipboard_buffer[SYS_CLIPBOARD_SIZE] = {0};\r\nvoid Sys_Clipboard_PasteText(clipboardtype_t cbt, void (*callback)(void *cb, const char *utf8), void *ctx)\r\n{\r\n\tcallback(ctx, clipboard_buffer);\r\n}\r\nvoid Sys_SaveClipboard(clipboardtype_t cbt, const char *text)\r\n{\r\n \tQ_strncpyz(clipboard_buffer, text, SYS_CLIPBOARD_SIZE);\r\n}\r\n\r\nint Sys_EnumerateFiles (const char *gpath, const char *match, int (*func)(const char *, qofs_t, time_t mtime, void *, searchpathfuncs_t *), void *parm, searchpathfuncs_t *spath)\r\n{\r\n\tDIR *dir;\r\n\tchar apath[MAX_OSPATH];\r\n\tchar file[MAX_OSPATH];\r\n\tchar truepath[MAX_OSPATH];\r\n\tchar *s;\r\n\tstruct dirent *ent;\r\n\tstruct stat st;\r\n\r\n\t//printf(\"path = %s\\n\", gpath);\r\n\t//printf(\"match = %s\\n\", match);\r\n\r\n\tif (!gpath)\r\n\t\tgpath = \"\";\r\n\t*apath = '\\0';\r\n\r\n\tQ_strncpyz(apath, match, sizeof(apath));\r\n\tfor (s = apath+strlen(apath)-1; s >= apath; s--)\r\n\t{\r\n\t\tif (*s == '/')\r\n\t\t{\r\n\t\t\ts[1] = '\\0';\r\n\t\t\tmatch += s - apath+1;\r\n\t\t\tbreak;\r\n\t\t}\r\n\t}\r\n\tif (s < apath)  //didn't find a '/' \r\n\t\t*apath = '\\0'; \r\n\r\n\tQ_snprintfz(truepath, sizeof(truepath), \"%s/%s\", gpath, apath); \r\n\r\n\r\n\t//printf(\"truepath = %s\\n\", truepath); \r\n\t//printf(\"gamepath = %s\\n\", gpath); \r\n\t//printf(\"apppath = %s\\n\", apath); \r\n\t//printf(\"match = %s\\n\", match); \r\n\tdir = opendir(truepath); \r\n\tif (!dir) \r\n\t{ \r\n\t\tCon_DPrintf(\"Failed to open dir %s\\n\", truepath); \r\n\t\treturn true; \r\n\t} \r\n\tdo \r\n\t{ \r\n\t\tent = readdir(dir); \r\n\t\tif (!ent) \r\n\t\t\tbreak; \r\n\t\tif (*ent->d_name != '.') \r\n\t\t{ \r\n\t\t\tif (wildcmp(match, ent->d_name)) \r\n\t\t\t{ \r\n\t\t\t\tQ_snprintfz(file, sizeof(file), \"%s/%s\", truepath, ent->d_name); \r\n\r\n\t\t\t\tif (stat(file, &st) == 0) \r\n\t\t\t\t{ \r\n\t\t\t\t\tQ_snprintfz(file, sizeof(file), \"%s%s%s\", apath, ent->d_name, S_ISDIR(st.st_mode)?\"/\":\"\"); \r\n\r\n\t\t\t\t\tif (!func(file, st.st_size, st.st_mtime, parm, spath)) \r\n\t\t\t\t\t{ \r\n\t\t\t\t\t\tclosedir(dir); \r\n\t\t\t\t\t\treturn false; \r\n\t\t\t\t\t} \r\n\t\t\t\t} \r\n\t\t\t\telse \r\n\t\t\t\t\tprintf(\"Stat failed for \\\"%s\\\"\\n\", file); \r\n\t\t\t} \r\n\t\t} \r\n\t} while(1); \r\n\tclosedir(dir); \r\n\r\n\treturn true; \r\n}\r\n\r\nstatic jboolean FTENativeActivity_startup(JNIEnv *jni, jobject this, jstring externalDataPath, jstring libraryPath)\r\n{\r\n\tconst char *tmp;\r\n\tif (sys_mainthread)\r\n\t\treturn false;\r\n\tif (!sys_activity)\r\n\t{\r\n\t\tsys_activity = (*jni)->NewGlobalRef(jni, this);\r\n\r\n\t\ttmp = (*jni)->GetStringUTFChars(jni, externalDataPath, NULL);\r\n\t\tif (tmp)\r\n\t\t{\r\n\t\t\tQ_strncpyz(sys_basedir, tmp, sizeof(sys_basedir));\r\n\t\t\tif (*sys_basedir && sys_basedir[strlen(sys_basedir)-1] != '/')\r\n\t\t\t\tQ_strncatz(sys_basedir, \"/\", sizeof(sys_basedir));\r\n\t\t\t(*jni)->ReleaseStringUTFChars(jni, externalDataPath, tmp);\r\n\t\t}\r\n\t\telse\r\n\t\t\t*sys_basedir = 0;\r\n\t\ttmp = (*jni)->GetStringUTFChars(jni, libraryPath, NULL);\r\n\t\tif (tmp)\r\n\t\t{\r\n\t\t\tQ_strncpyz(sys_binarydir, tmp, sizeof(sys_binarydir));\r\n\t\t\tif (*sys_binarydir && sys_binarydir[strlen(sys_binarydir)-1] != '/')\r\n\t\t\t\tQ_strncatz(sys_binarydir, \"/\", sizeof(sys_binarydir));\r\n\t\t\t(*jni)->ReleaseStringUTFChars(jni, libraryPath, tmp);\r\n\t\t}\r\n\t\telse\r\n\t\t\t*sys_binarydir = 0;\r\n\r\n\t\tLOGI(\"FTENativeActivity_startup: basedir=%s binarydir=%s\\n\", sys_basedir, sys_binarydir);\r\n\r\n\t\tsys_wantshutdown = false;\r\n\t\tsys_mainconditional = Sys_CreateConditional();\r\n\t\tSys_LockConditional(sys_mainconditional);\r\n\t\tsys_mainthread = Sys_CreateThread(\"ftedroid\", FTEDroid_MainThread, NULL, THREADP_NORMAL, -1);\r\n\t\tif (sys_mainthread)\r\n\t\t\tSys_ConditionWait(sys_mainconditional);\r\n\t\tSys_UnlockConditional(sys_mainconditional);\r\n\t\treturn !!sys_mainthread;\r\n\t}\r\n\tLOGI(\"conflicting FTENativeActivity_startup. ignoring.\\n\");\r\n\treturn false;\r\n}\r\n\r\nstatic void FTENativeActivity_surfacechange(JNIEnv *env, jobject this, jboolean teardown, jboolean recreate, jobject holder, jobject surface)\r\n{\r\n\tif (!(*env)->IsSameObject(env, this, sys_activity))\r\n\t{\r\n\t\tLOGI(\"FTENativeActivity_surfacechange: inactive %p, active %p\\n\", this, sys_activity);\r\n\t\treturn;\t//wasn't me...\r\n\t}\r\n\tLOGI(\"FTENativeActivity_surfacechange: %i %i %p\\n\", teardown, recreate, surface);\r\n\r\n//FIXME: if teardown&&recreate then this is a window RESIZE.\r\n//there shouldn't be a need to destroy the entire context but anbox crashes when simply moving the window if we early out here.\r\n//\tif (teardown && recreate && (*env)->IsSameObject(env, surface, sys_newsurface))\r\n//\t\treturn;\r\n//\tLOGI(\"FTENativeActivity_surfacechange: %i %i (%p==%p)==%i\\n\", teardown, recreate, surface, sys_newsurface, (*env)->IsSameObject(env, surface, sys_newsurface));\r\n\r\n\tSys_LockConditional(sys_mainconditional);\r\n\t//get the main thread to let us know when its done...\r\n\tif (qrenderer || (r_forceheadless && recreate))\r\n\t\tr_forcevidrestart = recreate?2:1;\r\n\tif (sys_newsurface)\r\n\t\t(*env)->DeleteGlobalRef(env, sys_newsurface);\r\n\tsys_newsurface = surface?(*env)->NewGlobalRef(env, surface):NULL;\r\n\tif (sys_newsholder)\r\n\t\t(*env)->DeleteGlobalRef(env, sys_newsholder);\r\n\tsys_newsholder = holder?(*env)->NewGlobalRef(env, holder):NULL;\r\n\t//and wake up then\t\r\n\tSys_ConditionWait(sys_mainconditional);\r\n\t//and we're done...\r\n\tSys_UnlockConditional(sys_mainconditional);\r\n}\r\nstatic void FTENativeActivity_shutdown(JNIEnv *env, jobject this)\r\n{\r\n\tif (!(*env)->IsSameObject(env, this, sys_activity))\r\n\t{\r\n\t\tLOGI(\"FTENativeActivity_shutdown: inactive %p, active %p\\n\", this, sys_activity);\r\n\t\treturn;\t//wasn't me...\r\n\t}\r\n\tLOGI(\"FTENativeActivity_shutdown\\n\");\r\n\r\n\tsys_wantshutdown = true;\r\n\tif (sys_mainthread)\r\n\t\tSys_WaitOnThread(sys_mainthread);\r\n\tsys_mainthread = NULL;\r\n\tif (sys_mainconditional)\r\n\t\tSys_DestroyConditional(sys_mainconditional);\r\n\tsys_mainconditional = NULL;\r\n\r\n\t(*env)->DeleteGlobalRef(env, sys_newsurface);\r\n\tsys_newsurface = NULL;\r\n\t\r\n\t(*env)->DeleteGlobalRef(env, sys_newsholder);\r\n\tsys_newsholder = NULL;\r\n\r\n\t(*env)->DeleteGlobalRef(env, sys_activity);\r\n\tsys_activity = NULL;\r\n}\r\n\r\n//FIXME: we need a version of this that takes a byte array instead of a filename, for android's content gibberish.\r\nstatic void FTENativeActivity_openfile(JNIEnv *env, jobject this, jstring filename)\r\n{\r\n\tconst char *tmp = (*env)->GetStringUTFChars(env, filename, NULL);\r\n\tif (tmp)\r\n\t{\r\n\t\tSys_Printf(\"FTENativeActivity_openfile: %s\\n\", tmp);\r\n\t\tHost_RunFile(tmp, strlen(tmp), NULL);\r\n\t\t(*env)->ReleaseStringUTFChars(env, filename, tmp);\r\n\t}\r\n}\r\n\r\nstatic JNINativeMethod methods[] = {\r\n\t//\r\n\t{\"startup\",\t\t\t\"(Ljava/lang/String;Ljava/lang/String;)Z\",\tFTENativeActivity_startup},\t\t\t//creates our 'main' thread too\r\n\t{\"surfacechange\",\t\"(ZZLandroid/view/SurfaceHolder;Landroid/view/Surface;)V\",\t\t\t\tFTENativeActivity_surfacechange},\t//syncs\r\n\t{\"shutdown\",\t\t\"()V\",\t\t\t\t\t\t\t\t\t\tFTENativeActivity_shutdown},\t\t//joins 'main' thread.\r\n\t{\"openfile\",\t\t\"(Ljava/lang/String;)V\",\t\t\t\t\tFTENativeActivity_openfile},\r\n\r\n\t//inputs. these use our in_generic.c ringbuffer so don't need to sync at all\r\n\t{\"keypress\",\t\t\"(IZII)V\", \t\tFTENativeActivity_keypress},\r\n\t{\"mousepress\",\t\t\"(II)V\", \t\tFTENativeActivity_mousepress},\r\n\t{\"motion\",\t\t\t\"(IIFFFF)V\", \tFTENativeActivity_motion},\r\n\t{\"wantrelative\",\t\"()Z\", \t\t\tFTENativeActivity_wantrelative},\t//so the java code knows if it should use (often buggy) relative mouse movement or (limited) abs cursor coords.\r\n\t{\"axis\",\t\t\t\"(IIF)V\", \t\tFTENativeActivity_axis},\r\n//\t{\"accelerometer\",\t\"(IFFF)V\",\t\tFTENativeActivity_accelerometer},\r\n//\t{\"gyroscope\",\t\t\"(IFFF)V\",\t\tFTENativeActivity_gyroscope},\r\n};\r\nJNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved)\r\n{\r\n\tJNIEnv *jni;\r\n\tsys_javavm = vm;\r\n\tif (JNI_OK == (*vm)->GetEnv(vm, (void**)&jni, JNI_VERSION_1_2))\r\n\t{\r\n\t\tjclass naclass = (*jni)->FindClass(jni, \"com/fteqw/FTENativeActivity\");\r\n\t\tif (naclass)\r\n\t\t{\r\n\t\t\t(*jni)->RegisterNatives(jni, naclass, methods, countof(methods));\r\n\t\t\treturn JNI_VERSION_1_2;\r\n\t\t}\r\n\t}\r\n\treturn -1;\r\n}\r\n"
  },
  {
    "path": "engine/client/sys_linux.c",
    "content": "/*\nCopyright (C) 1996-1997 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n*/\n\n//well, linux or cygwin (windows with posix emulation layer), anyway...\n\n#ifndef _GNU_SOURCE\n# define _GNU_SOURCE\n#endif\n#include <unistd.h>\n#include <signal.h>\n#include <stdlib.h>\n#include <limits.h>\n#include <time.h>\n#include <sys/time.h>\n#include <sys/types.h>\n#include <fcntl.h>\n#include <stdarg.h>\n#include <stdio.h>\n#include <dlfcn.h>\n#include <dirent.h>\n#include <sys/stat.h>\n#include <string.h>\n#include <ctype.h>\n#include <sys/wait.h>\n#include <sys/mman.h>\n#include <errno.h>\n#ifndef NO_X11\n# if !defined(__MACOSX__) && !defined(__DJGPP__) && !defined(NO_X11)\n#  include <X11/Xlib.h>\n# else\n#  define NO_X11\n# endif\n#endif\n\n#ifdef __CYGWIN__\n#define USE_LIBTOOL\n#endif\n#ifdef USE_LIBTOOL\n#include <ltdl.h>\n#endif\n\n#include \"quakedef.h\"\n#include \"netinc.h\"\n#ifdef PLUGINS\n#define FTEENGINE\n#include \"../plugins/plugin.h\"\n#endif\n#include \"fs.h\"\n\n#undef malloc\n\nstatic int noconinput = 0;\nstatic int nostdout = 0;\n\nstatic FILE *dedcon; //replaces our stdin/out when set.\nstatic int dedconproc = -1;\n\nextern int isPlugin;\nint sys_parentleft;\nint sys_parenttop;\nint sys_parentwidth;\nint sys_parentheight;\nlong\tsys_parentwindow;\nqboolean sys_gracefulexit;\n\nqboolean X11_GetDesktopParameters(int *width, int *height, int *bpp, int *refreshrate);\nstatic void Sys_InitClock(void);\n\nqboolean Sys_InitTerminal (void)\t//we either have one or we don't.\n{\n\tif (isatty(STDIN_FILENO))\n\t\treturn true;\t//already attached to a terminal in some form. don't need another.\n\n\tif (dedcon)\t//already got one open... shouldn't really happen. future paranoia.\n\t\treturn true;\n\telse\n\t{\n\t\tint pty = posix_openpt(O_RDWR|O_NOCTTY);\n\t\tif (pty >= 0)\n\t\t{\n\t\t\tint fd;\n\t\t\tchar *slavename, window[64], buf[64];\n\t\t\tgrantpt(pty);\n\t\t\tunlockpt(pty);\n\t\t\tslavename = ptsname(pty);\n\t\t\tdedcon = fopen(slavename, \"r+e\");\n\t\t\tif (dedcon)\n\t\t\t{\n\t\t\t\tsnprintf(buf, sizeof buf, \"-S%s/%d\", strrchr(slavename,'/')+1, pty);\n\t\t\t\tdedconproc = fork();\n\t\t\t\tif(!dedconproc)\n\t\t\t\t{\t//oh hey, we're the child.\n\t\t\t\t\texeclp(\"xterm\", \"xterm\", buf, (char *)0);\n\t\t\t\t\t_exit(1);\n\t\t\t\t}\n\t\t\t\tclose(pty);\t//can close it now, we'll keep track of it with our slave fd\n\t\t\t\tif (dedconproc >= 0)\n\t\t\t\t{\t//if the xterm fails, does this EPIPE properly?\n\t\t\t\t\tfgets(window, sizeof window, dedcon);\n\t\t\t\t\t//printf(\"window: %s\\n\", window);\n\n\t\t\t\t\t//switch input to non-blocking, so we can actually still do stuff...\n\t\t\t\t\tfd = fileno(dedcon);\n\t\t\t\t\tfcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);\n\n\t\t\t\t\t#ifdef HAVE_EPOLL\n\t\t\t\t\t{\n\t\t\t\t\t\textern int epoll_fd;\n\t\t\t\t\t\tif (epoll_fd >= 0)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tstruct epoll_event event = {EPOLLIN, {NULL}};\n\t\t\t\t\t\t\tepoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &event);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t#else\n\t\t\t\t\t\t//FIXME: NET_Sleep needs to wake up on dedcon input\n\t\t\t\t\t#endif\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t\t//else fork failed.\n\t\t\t\tfclose(dedcon);\n\t\t\t\tdedcon = NULL;\n\t\t\t}\n\t\t\tclose(pty);\n\t\t}\n\t}\n\treturn false;\t//nope, soz\n}\nvoid Sys_CloseTerminal (void)\n{\n\tif (dedcon)\n\t{\n#ifdef HAVE_EPOLL\n\t\textern int epoll_fd;\n\t\tif (epoll_fd >= 0)\n\t\t\tepoll_ctl(epoll_fd, EPOLL_CTL_DEL, fileno(dedcon), NULL);\t//don't get confused if the FD# is later reopened as something else...\n#endif\n\t\tfclose(dedcon);\n\t\tdedcon = NULL;\n\t}\n\tdedconproc = -1;\n}\n\nvoid Sys_RecentServer(char *command, char *target, char *title, char *desc)\n{\n}\n\n#ifndef CLIENTONLY\nqboolean isDedicated;\n#endif\n// =======================================================================\n// General routines\n// =======================================================================\n\n#if 1\nstatic int ansiremap[8] = {0, 4, 2, 6, 1, 5, 3, 7};\nstatic void ApplyColour(FILE *out, unsigned int chrflags)\n{\n\tstatic int oldflags = CON_WHITEMASK;\n\tint bg, fg;\n\n\tif (oldflags == chrflags)\n\t\treturn;\n\toldflags = chrflags;\n\n\tfprintf(out, \"\\e[0;\"); // reset\n\n\tif (chrflags & CON_BLINKTEXT)\n\t\tfprintf(out, \"5;\"); // set blink\n\n\tbg = (chrflags & CON_BGMASK) >> CON_BGSHIFT;\n\tfg = (chrflags & CON_FGMASK) >> CON_FGSHIFT;\n\n\t// don't handle intensive bit for background\n\t// as terminals differ too much in displaying \\e[1;7;3?m\n\tbg &= 0x7;\n\n\tif (chrflags & CON_NONCLEARBG)\n\t{\n\t\tif (fg & 0x8) // intensive bit set for foreground\n\t\t{\n\t\t\tfprintf(out, \"1;\"); // set bold/intensity ansi flag\n\t\t\tfg &= 0x7; // strip intensive bit\n\t\t}\n\n\t\t// set foreground and background colors\n\t\tfprintf(out, \"3%i;4%im\", ansiremap[fg], ansiremap[bg]);\n\t}\n\telse\n\t{\n\t\tswitch(fg)\n\t\t{\n\t\t//to get around wierd defaults (like a white background) we have these special hacks for colours 0 and 7\n\t\tcase COLOR_BLACK:\n\t\t\tfprintf(out, \"7m\"); // set inverse\n\t\t\tbreak;\n\t\tcase COLOR_GREY:\n\t\t\tfprintf(out, \"1;30m\"); // treat as dark grey\n\t\t\tbreak;\n\t\tcase COLOR_WHITE:\n\t\t\tfprintf(out, \"m\"); // set nothing else\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tif (fg & 0x8) // intensive bit set for foreground\n\t\t\t{\n\t\t\t\tfprintf(out, \"1;\"); // set bold/intensity ansi flag\n\t\t\t\tfg &= 0x7; // strip intensive bit\n\t\t\t}\n\n\t\t\tfprintf(out, \"3%im\", ansiremap[fg]); // set foreground\n\t\t\tbreak;\n\t\t}\n\t}\n}\n\n#include <wchar.h>\nvoid Sys_Printf (char *fmt, ...)\n{\n\tva_list\t\targptr;\n\tchar\t\ttext[2048];\n\tconchar_t\tctext[2048];\n\tconchar_t       *c, *e;\n\twchar_t\t\tw;\n\tunsigned int codeflags, codepoint;\n\tFILE *out = stdout;\n\n#ifdef SUBSERVERS\n\tif (SSV_IsSubServer())\n\t{\n\t\tva_start (argptr,fmt);\n\t\tvsnprintf (text,sizeof(text)-1, fmt,argptr);\n\t\tva_end (argptr);\n\t\tif (SSV_PrintToMaster(text))\n\t\t\treturn;\n\t}\n#endif\n\n\tif (dedcon)\n\t\tout = dedcon;\n\telse if (nostdout)\n\t{\n#ifdef _DEBUG\n\t\tout = stderr;\n#else\n\t\treturn;\n#endif\n\t}\n\n\tva_start (argptr,fmt);\n\tvsnprintf (text,sizeof(text)-1, fmt,argptr);\n\tva_end (argptr);\n\n\tif (strlen(text) > sizeof(text))\n\t\tSys_Error(\"memory overwrite in Sys_Printf\");\n\n\te = COM_ParseFunString(CON_WHITEMASK, text, ctext, sizeof(ctext), false);\n\n\tfor (c = ctext; c < e; )\n\t{\n\t\tc = Font_Decode(c, &codeflags, &codepoint);\n\t\tif (codeflags & CON_HIDDEN)\n\t\t\tcontinue;\n\n\t\tif ((codeflags&CON_RICHFORECOLOUR) || (codepoint == '\\n' && (codeflags&CON_NONCLEARBG)))\n\t\t\tcodeflags = CON_WHITEMASK;\t//make sure we don't get annoying backgrounds on other lines.\n\t\tApplyColour(out, codeflags);\n\t\tw = codepoint;\n\t\tif (w >= 0xe000 && w < 0xe100)\n\t\t{\n\t\t\t/*not all quake chars are ascii compatible, so map those control chars to safe ones so we don't mess up anyone's xterm*/\n\t\t\tif ((w & 0x7f) >= 0x20)\n\t\t\t\tputc(w&0x7f, out);\n\t\t\telse if (w & 0x80)\n\t\t\t{\n\t\t\t\tstatic char tab[32] = \"---#@.@@@@ # >..\" \"[]0123456789.---\";\n\t\t\t\tputc(tab[w&31], out);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tstatic char tab[32] = \".####.#### # >..\" \"[]0123456789.---\";\n\t\t\t\tputc(tab[w&31], out);\n\t\t\t}\n\t\t}\n\t\telse if (w < ' ' && w != '\\t' && w != '\\r' && w != '\\n')\n\t\t\tputc('?', out);\t//don't let anyone print escape codes or other things that could crash an xterm.\n\t\telse\n\t\t{\n\t\t\t/*putwc doesn't like me. force it in utf8*/\n\t\t\tif (w >= 0x80)\n\t\t\t{\n\t\t\t\tif (w > 0x800)\n\t\t\t\t{\n\t\t\t\t\tputc(0xe0 | ((w>>12)&0x0f), out);\n\t\t\t\t\tputc(0x80 | ((w>>6)&0x3f), out);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tputc(0xc0 | ((w>>6)&0x1f), out);\n\t\t\t\tputc(0x80 | (w&0x3f), out);\n\t\t\t}\n\t\t\telse\n\t\t\t\tputc(w, out);\n\t\t}\n\t}\n\n\tApplyColour(out, CON_WHITEMASK);\n\tfflush(out);\n}\n#else\nvoid Sys_Printf (char *fmt, ...)\n{\n\tva_list\t\targptr;\n\tchar\t\ttext[2048];\n\tunsigned char\t\t*p;\n\n\tva_start (argptr,fmt);\n\tvsnprintf (text,sizeof(text)-1, fmt,argptr);\n\tva_end (argptr);\n\n\tif (strlen(text) > sizeof(text))\n\t\tSys_Error(\"memory overwrite in Sys_Printf\");\n\n\tif (nostdout)\n\t\treturn;\n\n\tfor (p = (unsigned char *)text; *p; p++)\n\t\tif ((*p > 128 || *p < 32) && *p != 10 && *p != 13 && *p != 9)\n\t\t\tprintf(\"[%02x]\", *p);\n\t\telse\n\t\t\tputc(*p, stdout);\n}\n#endif\n\nvoid Sys_Quit (void)\n{\n\tHost_Shutdown();\n#ifndef __DJGPP__\n\tif (!noconinput)\n\t\tfcntl (0, F_SETFL, fcntl (0, F_GETFL, 0) & ~FNDELAY);\n#endif\n\n#ifdef USE_LIBTOOL\n\tlt_dlexit();\n#endif\n\texit(0);\n}\n\nchar *Sys_URIScheme_NeedsRegistering(void)\n{\n\tqboolean found;\n\tqofs_t insize;\n\tchar *in;\n\tchar confbase[MAX_OSPATH];\n\n\tchar scheme[64];\n\tconst char *schemes = fs_manifest->schemes;\n\n\tschemes=COM_ParseOut(schemes, scheme, sizeof(scheme));\n\tif (!schemes)\n\t\treturn NULL;\n\n\t{\t//user\n\t\tconst char *config = getenv(\"XDG_CONFIG_HOME\");\n\t\tconst char *home = getenv(\"HOME\");\n\t\tif (config && *config)\n\t\t\tQ_strncpyz(confbase, config, sizeof(confbase));\n\t\telse\n\t\t{\n\t\t\tif (home && *home)\n\t\t\t\tQ_snprintfz(confbase, sizeof(confbase), \"%s/.config\", home);\n\t\t\telse\n\t\t\t\treturn NULL;\t//can't register anyway, just pretend its registered.\n\t\t}\n\t}\n\n\t//check if the scheme is registered or not.\n\tin = FS_MallocFile(va(\"%s/mimeapps.list\", confbase), FS_SYSTEM, &insize);\n\tif (in)\n\t{\n\t\tdo\n\t\t{\n\t\t\tqboolean inadded = false;\n\t\t\tchar *l = in;\n\t\t\tconst char *schemeline = va(\"x-scheme-handler/%s=fte-%s.desktop;\", scheme, fs_manifest->installation);\n\t\t\tsize_t schemelinelen = strlen(schemeline);\n\t\t\tfound = false;\n\t\t\twhile(*l)\n\t\t\t{\n\t\t\t\tchar *le;\n\t\t\t\twhile(*l == ' ' || *l == '\\n')\n\t\t\t\t\tl++;\n\t\t\t\tle = strchr(l, '\\n');\n\t\t\t\tif (le)\n\t\t\t\t\tle = le+1;\n\t\t\t\telse\n\t\t\t\t\tle = l + strlen(l);\n\t\t\t\tif (!strncmp(l, \"[Added Associations]\", 20))\n\t\t\t\t\tinadded = true;\n\t\t\t\telse if (!strncmp(l, \"[\", 1))\n\t\t\t\t\tinadded = false;\n\t\t\t\telse if (inadded && !strncmp(l, schemeline, schemelinelen))\n\t\t\t\t{\n\t\t\t\t\tfound = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tl = le;\n\t\t\t}\n\t\t} while(found && (schemes=COM_ParseOut(schemes, scheme, sizeof(scheme))));\n\t\tZ_Free(in);\n\t}\n\telse\n\t\tfound = false;\n\n\tif (found)\n\t\treturn NULL;\n\treturn Z_StrDup(scheme);\n}\nstatic void Sys_Register_File_Associations_f(void)\n{\n\tconst char *s;\n\tchar xdgbase[MAX_OSPATH];\n\tchar confbase[MAX_OSPATH];\n\n\tchar scheme[MAX_OSPATH];\n\tconst char *schemes = fs_manifest->schemes;\n\n\tif (1)\n\t{\t//user\n\t\tconst char *data = getenv(\"XDG_DATA_HOME\");\n\t\tconst char *config = getenv(\"XDG_CONFIG_HOME\");\n\t\tconst char *home = getenv(\"HOME\");\n\t\tif (data && *data)\n\t\t\tQ_strncpyz(xdgbase, data, sizeof(xdgbase));\n\t\telse\n\t\t{\n\t\t\tif (home && *home)\n\t\t\t\tQ_snprintfz(xdgbase, sizeof(xdgbase), \"%s/.local/share\", home);\n\t\t\telse\n\t\t\t{\n\t\t\t\tCon_Printf(\"homedir not known\\n\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\tif (config && *config)\n\t\t\tQ_strncpyz(confbase, config, sizeof(confbase));\n\t\telse\n\t\t{\n\t\t\tif (home && *home)\n\t\t\t\tQ_snprintfz(confbase, sizeof(confbase), \"%s/.config\", home);\n\t\t\telse\n\t\t\t{\n\t\t\t\tCon_Printf(\"homedir not known\\n\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n\telse\n\t{\t//system... gotta be root...\n\t\tconst char *data = getenv(\"XDG_DATA_DIRS\");\n\t\tconst char *config = getenv(\"XDG_CONFIG_DIRS\");\n\t\twhile (data && *data == ':')\n\t\t\tdata++;\n\t\tif (data && *data)\n\t\t{\n\t\t\tchar *c;\n\t\t\tQ_strncpyz(xdgbase, data, sizeof(xdgbase));\n\t\t\tc = strchr(xdgbase, ':');\n\t\t\tif (*c)\n\t\t\t\t*c = 0;\n\t\t}\n\t\telse\n\t\t\tQ_strncpyz(xdgbase, \"/usr/local/share/\", sizeof(xdgbase));\n\t\twhile (config && *config == ':')\n\t\t\tconfig++;\n\t\tif (config && *config)\n\t\t{\n\t\t\tchar *c;\n\t\t\tQ_strncpyz(confbase, config, sizeof(confbase));\n\t\t\tc = strchr(confbase, ':');\n\t\t\tif (*c)\n\t\t\t\t*c = 0;\n\t\t}\n\t\telse\n\t\t\tQ_strncpyz(confbase, \"/etc/xdg/\", sizeof(confbase));\n\t}\n\n\t//we need to create some .desktop file first, so stuff knows how to start us up.\n\t{\n\t\tvfsfile_t *f;\n\t\tchar iconsyspath[MAX_OSPATH];\n\t\tchar *exe = realpath(host_parms.argv[0], NULL);\n\t\tchar *basedir = realpath(com_gamepath, NULL);\n\t\tconst char *iconname = fs_manifest->installation;\n\n\t\tif (!exe)\n\t\t{\n\t\t\tint i;\n\t\t\tif (strchr(host_parms.argv[0], '/') && (i=readlink(\"/proc/self/exe\", iconsyspath, sizeof(iconsyspath)-1))>0)\n\t\t\t{\t//if they used a relative path to invoke the binary, replace it with an absolute one.\n\t\t\t\ticonsyspath[i] = 0;\n\t\t\t\texe = strdup(iconsyspath);\n\t\t\t}\n\t\t\telse\t//no absolute path. assume it was loaded from the (default) path.\n\t\t\t\texe = strdup(host_parms.argv[0]);\n\t\t}\n\n\t\tif (!strcmp(iconname, \"afterquake\") || !strcmp(iconname, \"nq\"))\t//hacks so that we don't need to create icons.\n\t\t\ticonname = \"quake\";\n\n\t\tif (FS_SystemPath(\"icon.png\", FS_PUBBASEGAMEONLY, iconsyspath, sizeof(iconsyspath)))\n\t\t\ticonname = iconsyspath;\n\n\t\ts = va(\"%s/applications/fte-%s.desktop\", xdgbase, fs_manifest->installation);\n\t\tFS_CreatePath(s, FS_SYSTEM);\n\t\tf = FS_OpenVFS(s, \"wb\", FS_SYSTEM);\n\t\tif (f)\n\t\t{\n\t\t\tVFS_PRINTF(f,\n\t\t\t\t\"[Desktop Entry]\\n\"\n\t\t\t\t\"Type=Application\\n\"\n\t\t\t\t\"Encoding=UTF-8\\n\"\n\t\t\t\t\"Name=%s\\n\"\n\t\t\t\t\"Comment=Awesome First Person Shooter\\n\"\t//again should be a manicfest item\n\t\t\t\t\"Exec=\\\"%s\\\" %%u %s\\n\"\n\t\t\t\t\"Path=%s\\n\"\n\t\t\t\t\"Icon=%s\\n\"\n\t\t\t\t\"Terminal=false\\n\"\n\t\t\t\t\"Categories=Game;\\n\"\n\t\t\t\t\"MimeType=\" \"application/x-quakeworlddemo;\",\n\t\t\t\t\tfs_manifest->formalname?fs_manifest->formalname:fs_manifest->installation,\n\t\t\t\t\texe, FS_GetManifestArgs(), basedir, iconname);\n\n\t\t\tfor (s = schemes; (s=COM_ParseOut(s,scheme,sizeof(scheme)));)\n\t\t\t\tVFS_PRINTF(f, \"x-scheme-handler/%s;\", scheme);\n\t\t\tVFS_PRINTF(f, \"\\n\");\n\n\t\t\tVFS_CLOSE(f);\n\t\t}\n\n\t\tfree(exe);\n\t\tfree(basedir);\n\n\t\t//FIXME: read icon.png and write it to ~/.local/share/icons/hicolor/WxH/apps/foo.png\n\t}\n\n\t//we need to set some default applications.\n\t//write out a new file and rename the new over the top of the old\n\tfor (s = schemes; (s=COM_ParseOut(s,scheme,sizeof(scheme)));)\n\t{\n\t\tchar *foundassoc = NULL;\n\t\tvfsfile_t *out = FS_OpenVFS(va(\"%s/.mimeapps.list.new\", confbase), \"wb\", FS_SYSTEM);\n\t\tif (out)\n\t\t{\n\t\t\tqofs_t insize;\n\t\t\tchar *in = FS_MallocFile(va(\"%s/mimeapps.list\", confbase), FS_SYSTEM, &insize);\n\t\t\tif (in)\n\t\t\t{\n\t\t\t\tqboolean inadded = false;\n\t\t\t\tchar *l = in;\n\t\t\t\tconst char *schemeline = va(\"x-scheme-handler/%s=\", scheme);\n\t\t\t\tsize_t schemelinelen = strlen(schemeline);\n\t\t\t\twhile(*l)\n\t\t\t\t{\n\t\t\t\t\tchar *le;\n\t\t\t\t\twhile(*l == ' ' || *l == '\\n')\n\t\t\t\t\t\tl++;\n\t\t\t\t\tle = strchr(l, '\\n');\n\t\t\t\t\tif (le)\n\t\t\t\t\t\tle = le+1;\n\t\t\t\t\telse\n\t\t\t\t\t\tle = l + strlen(l);\n\t\t\t\t\tif (!strncmp(l, \"[Added Associations]\", 20))\n\t\t\t\t\t{\n\t\t\t\t\t\tinadded = true;\n\t\t\t\t\t\tif (!foundassoc)\n\t\t\t\t\t\t\tfoundassoc = le;\n\t\t\t\t\t}\n\t\t\t\t\telse if (!strncmp(l, \"[\", 1))\n\t\t\t\t\t\tinadded = false;\n\t\t\t\t\telse if (inadded && !strncmp(l, schemeline, schemelinelen))\n\t\t\t\t\t{\n\t\t\t\t\t\tfoundassoc = l;\n\t\t\t\t\t\tinsize -= le-l;\n\t\t\t\t\t\tmemmove(l, le, strlen(le));\t//remove the line\n\t\t\t\t\t}\n\t\t\t\t\tl = le;\n\t\t\t\t}\n\t\t\t\tif (foundassoc)\n\t\t\t\t{\t//if we found it, or somewhere to insert it, then insert it.\n\t\t\t\t\tVFS_WRITE(out, in, foundassoc-in);\n\t\t\t\t\tVFS_PRINTF(out, \"x-scheme-handler/%s=fte-%s.desktop;\\n\", scheme, fs_manifest->installation);\n\t\t\t\t\tVFS_WRITE(out, foundassoc, insize - (foundassoc-in));\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tVFS_WRITE(out, in, insize);\t//not found, just write everything as-is\n\t\t\t\tZ_Free(in);\n\t\t\t}\n\t\t\tif (!foundassoc)\n\t\t\t{\t//if file not found, or no appropriate section, just concat it on the end.\n\t\t\t\tVFS_PRINTF(out, \"[Added Associations]\\n\");\n\t\t\t\tVFS_PRINTF(out, \"x-scheme-handler/%s=fte-%s.desktop;\\n\", scheme, fs_manifest->installation);\n\t\t\t}\n\t\t\tVFS_FLUSH(out);\n\t\t\tVFS_CLOSE(out);\n\t\t\tFS_Rename2(va(\"%s/.mimeapps.list.new\", confbase), va(\"%s/mimeapps.list\", confbase), FS_SYSTEM, FS_SYSTEM);\n\t\t}\n\t}\n}\n\nvoid Sys_Init(void)\n{\n\tSys_InitClock();\n\tCmd_AddCommandD(\"sys_register_file_associations\", Sys_Register_File_Associations_f, \"Register FTE as the default handler for various file+protocol types, using FreeDesktop standards.\\n\");\n}\nvoid Sys_Shutdown(void)\n{\n\tZ_Free((char*)(host_parms.binarydir));\n\thost_parms.binarydir = NULL;\n}\n\nvoid Sys_Error (const char *error, ...)\n{\n\tva_list argptr;\n\tchar string[1024];\n\n#ifndef __DJGPP__\n// change stdin back to blocking, so the shell doesn't bug out.\n\tif (!noconinput)\n\t\tfcntl (0, F_SETFL, fcntl (0, F_GETFL, 0) & ~FNDELAY);\n#endif\n\n\tva_start (argptr,error);\n\tvsnprintf (string,sizeof(string)-1, error,argptr);\n\tva_end (argptr);\n\n\tCOM_WorkerAbort(string);\n\n\tfprintf(stderr, \"Error: %s\\n\", string);\n\n\tHost_Shutdown ();\n\n#ifdef USE_LIBTOOL\n\tlt_dlexit();\n#endif\n\n\tfflush(stdout);\n\tfflush(stderr);\n\n\tif (!isatty(STDERR_FILENO))\n\t{\t//if we're a graphical x11 program then its quite likely that we have no stdout with the user never knowing why the game just disappeared\n\t\t//the crash could have come from malloc failures, this means we can't really depend upon xlib\n\t\t//but we can start some other program to display the message.\n\t\texecl(\"/usr/bin/xmessage\", \"xmessage\", string, NULL);\n\t}\n\texit (1);\n}\n\nvoid Sys_Warn (char *warning, ...)\n{\n\tva_list argptr;\n\tchar string[1024];\n\n\tva_start (argptr,warning);\n\tvsnprintf (string,sizeof(string)-1, warning,argptr);\n\tva_end (argptr);\n\n\tfprintf(stderr, \"Warning: %s\", string);\n}\n\nvoid Sys_mkdir (const char *path)\n{\n\tmkdir (path, 0760);\n}\nqboolean Sys_rmdir (const char *path)\n{\n\tif (rmdir (path) == 0)\n\t\treturn true;\n\tif (errno == ENOENT)\n\t\treturn true;\n\treturn false;\n}\nqboolean Sys_remove (const char *path)\n{\n\t//remove is part of c89.\n\tif (remove(path) == -1)\n\t\treturn false;\n\treturn true;\n}\nqboolean Sys_Rename (const char *oldfname, const char *newfname)\n{\n\treturn !rename(oldfname, newfname);\n}\n#if _POSIX_C_SOURCE >= 200112L\n\t#include <sys/statvfs.h>\n#endif\nqboolean Sys_GetFreeDiskSpace(const char *path, quint64_t *freespace)\n{\n#if _POSIX_C_SOURCE >= 200112L\n\t//posix 2001\n\tstruct statvfs inf;\n\tif(0==statvfs(path, &inf))\n\t{\n\t\t*freespace = inf.f_bsize*(quint64_t)inf.f_bavail;\t//grab the quota-free value rather than the actual free space\n\t\treturn true;\n\t}\n#endif\n\treturn false;\n}\n\nint Sys_DebugLog(char *file, char *fmt, ...)\n{\n\tva_list argptr;\n\tstatic char data[1024];\n\tint fd;\n\tsize_t result;\n\n\tva_start(argptr, fmt);\n\tvsnprintf (data,sizeof(data)-1, fmt, argptr);\n\tva_end(argptr);\n\n\tif (strlen(data) > sizeof(data))\n\t\tSys_Error(\"Sys_DebugLog's buffer was stomped\\n\");\n\n//\tfd = open(file, O_WRONLY | O_BINARY | O_CREAT | O_APPEND, 0666);\n\tfd = open(file, O_WRONLY | O_CREAT | O_APPEND, 0666);\n\tif (fd)\n\t{\n\t\tresult = write(fd, data, strlen(data)); // do something with result\n\n\t\tif (result != strlen(data))\n\t\t\tCon_SafePrintf(\"Sys_DebugLog() write: Filename: %s, expected %lu, result was %lu (%s)\\n\",file,(unsigned long)strlen(data),(unsigned long)result,strerror(errno));\n\n\t\tclose(fd);\n\t\treturn 0;\n\t}\n\treturn 1;\n}\n\nstatic int Sys_EnumerateFiles2 (const char *truepath, int apathofs, const char *match, int (*func)(const char *, qofs_t, time_t modtime, void *, searchpathfuncs_t *), void *parm, searchpathfuncs_t *spath)\n{\n\tDIR *dir;\n\tchar file[MAX_OSPATH];\n\tconst char *s;\n\tstruct dirent *ent;\n\tstruct stat st;\n\tconst char *wild;\n\tconst char *apath = truepath+apathofs;\n\n\t//if there's a * in a system path, then we need to scan its parent directory to figure out what the * expands to.\n\t//we can just recurse quicklyish to try to handle it.\n\twild = strchr(apath, '*');\n\tif (!wild)\n\t\twild = strchr(apath, '?');\n\tif (wild)\n\t{\n\t\tchar subdir[MAX_OSPATH];\n\t\tfor (s = wild+1; *s && *s != '/'; s++)\n\t\t\t;\n\t\twhile (wild > truepath)\n\t\t{\n\t\t\tif (*(wild-1) == '/')\n\t\t\t\tbreak;\n\t\t\twild--;\n\t\t}\n\t\tmemcpy(file, truepath, wild-truepath);\n\t\tfile[wild-truepath] = 0;\n\n\t\tdir = opendir(file);\n\t\tmemcpy(subdir, wild, s-wild);\n\t\tsubdir[s-wild] = 0;\n\t\tif (dir)\n\t\t{\n\t\t\tdo\n\t\t\t{\n\t\t\t\tent = readdir(dir);\n\t\t\t\tif (!ent)\n\t\t\t\t\tbreak;\n\t\t\t\tif (*ent->d_name != '.')\n\t\t\t\t{\n#ifdef _DIRENT_HAVE_D_TYPE\n\t\t\t\t\tif (ent->d_type != DT_DIR && ent->d_type != DT_UNKNOWN)\n\t\t\t\t\t\tcontinue;\n#endif\n\t\t\t\t\tif (wildcmp(subdir, ent->d_name))\n\t\t\t\t\t{\n\t\t\t\t\t\tmemcpy(file, truepath, wild-truepath);\n\t\t\t\t\t\tQ_snprintfz(file+(wild-truepath), sizeof(file)-(wild-truepath), \"%s%s\", ent->d_name, s);\n\t\t\t\t\t\tif (!Sys_EnumerateFiles2(file, apathofs, match, func, parm, spath))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tclosedir(dir);\n\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} while(1);\n\t\t\tclosedir(dir);\n\t\t}\n\t\treturn true;\n\t}\n\n\n\tdir = opendir(truepath);\n\tif (!dir)\n\t{\n\t\tCon_DLPrintf((errno==ENOENT)?2:1, \"Failed to open dir %s\\n\", truepath);\n\t\treturn true;\n\t}\n\tdo\n\t{\n\t\tent = readdir(dir);\n\t\tif (!ent)\n\t\t\tbreak;\n\t\tif (*ent->d_name != '.')\n\t\t{\n\t\t\tif (wildcmp(match, ent->d_name))\n\t\t\t{\n\t\t\t\tQ_snprintfz(file, sizeof(file), \"%s%s\", truepath, ent->d_name);\n\n\t\t\t\tif (stat(file, &st) == 0)\n\t\t\t\t{\n\t\t\t\t\tQ_snprintfz(file, sizeof(file), \"%s%s%s\", apath, ent->d_name, S_ISDIR(st.st_mode)?\"/\":\"\");\n\n\t\t\t\t\tif (!func(file, st.st_size, st.st_mtime, parm, spath))\n\t\t\t\t\t{\n//\t\t\t\t\t\tCon_DPrintf(\"giving up on search after finding %s\\n\", file);\n\t\t\t\t\t\tclosedir(dir);\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t}\n//\t\t\t\telse\n//\t\t\t\t\tCon_DPrintf(\"Stat failed for \\\"%s\\\"\\n\", file);\t//can happen with dead symlinks\n\t\t\t}\n\t\t}\n\t} while(1);\n\tclosedir(dir);\n\treturn true;\n}\nint Sys_EnumerateFiles (const char *gpath, const char *match, int (*func)(const char *, qofs_t, time_t modtime, void *, searchpathfuncs_t *), void *parm, searchpathfuncs_t *spath)\n{\n\tchar apath[MAX_OSPATH];\n\tchar truepath[MAX_OSPATH];\n\tchar *s;\n\tint  suboffset;\n\n\tif (!gpath)\n\t\tgpath = \"\";\n\t*apath = '\\0';\n\n\tQ_strncpyz(apath, match, sizeof(apath));\n\tfor (s = apath+strlen(apath)-1; s >= apath; s--)\n\t{\n\t\tif (*s == '/')\n\t\t{\n\t\t\ts[1] = '\\0';\n\t\t\tmatch += s - apath+1;\n\t\t\tbreak;\n\t\t}\n\t}\n\tif (s < apath)\t//didn't find a '/'\n\t\t*apath = '\\0';\n\n\tsuboffset = strlen(gpath);\n\tif (suboffset + 1 + strlen(apath) >= sizeof(truepath))\n\t\treturn false;\t//overflow...\n\tmemcpy(truepath, gpath, suboffset);\n\tif (suboffset && truepath[suboffset-1] != '/')\n\t\ttruepath[suboffset++] = '/';\n\tQ_strncpyz(truepath+suboffset, apath, sizeof(truepath)-suboffset);\n\n\treturn Sys_EnumerateFiles2(truepath, suboffset, match, func, parm, spath);\n}\n\nstatic quint64_t timer_basetime;\t//used by all clocks to bias them to starting at 0\nstatic void Sys_ClockType_Changed(cvar_t *var, char *oldval);\nstatic cvar_t sys_clocktype = CVARFCD(\"sys_clocktype\", \"\", CVAR_NOTFROMSERVER, Sys_ClockType_Changed, \"Controls which system clock to base timings from.\\n0: auto\\n\"\n\t\"1: gettimeofday (may be discontinuous).\\n\"\n\t\"2: monotonic.\");\nstatic enum\n{\n\tQCLOCK_AUTO = 0,\n\n\tQCLOCK_GTOD,\n\tQCLOCK_MONOTONIC,\n\tQCLOCK_REALTIME,\n\n\tQCLOCK_INVALID\n} timer_clocktype;\nstatic quint64_t Sys_GetClock(quint64_t *freq)\n{\n\tquint64_t t;\n\tswitch(timer_clocktype)\n\t{\n#ifdef CLOCK_MONOTONIC\n\tcase QCLOCK_MONOTONIC:\n\t\t{\n\t\t\tstruct timespec ts;\n\t\t\tclock_gettime(CLOCK_MONOTONIC, &ts);\n\t\t\t*freq = 1000000000;\n\t\t\tt = (ts.tv_sec*(quint64_t)1000000000) + ts.tv_nsec;\n\t\t}\n\t\tbreak;\n#endif\n#ifdef CLOCK_REALTIME\n\tcase QCLOCK_REALTIME:\n\t\t{\n\t\t\tstruct timespec ts;\n\t\t\tclock_gettime(CLOCK_REALTIME, &ts);\n\t\t\t*freq = 1000000000;\n\t\t\tt = (ts.tv_sec*(quint64_t)1000000000) + ts.tv_nsec;\n\n\t\t\t//WARNING t can go backwards\n\t\t}\n\t\tbreak;\n#endif\n\tdefault:\t//QCLOCK_GTOD\n\t\t{\n\t\t\tstruct timeval tp;\n\t\t\tgettimeofday(&tp, NULL);\n\t\t\t*freq = 1000000;\n\t\t\tt = tp.tv_sec*(quint64_t)1000000 + tp.tv_usec;\n\n\t\t\t//WARNING t can go backwards\n\t\t}\n\t\tbreak;\n\t}\n\treturn t - timer_basetime;\n}\nstatic void Sys_ClockType_Changed(cvar_t *var, char *oldval)\n{\n\tint newtype = var?var->ival:0;\n\tif (newtype >= QCLOCK_INVALID)\n\t\tnewtype = QCLOCK_AUTO;\n\tif (newtype <= QCLOCK_AUTO)\n\t\tnewtype = QCLOCK_MONOTONIC;\n\n\tif (newtype != timer_clocktype)\n\t{\n\t\tquint64_t oldtime, oldfreq;\n\t\tquint64_t newtime, newfreq;\n\n\t\toldtime = Sys_GetClock(&oldfreq);\n\t\ttimer_clocktype = newtype;\n\t\ttimer_basetime = 0;\n\t\tnewtime = Sys_GetClock(&newfreq);\n\n\t\ttimer_basetime = newtime - (newfreq * (oldtime) / oldfreq);\n\n\t\t/*if (host_initialized)\n\t\t{\n\t\t\tconst char *clockname = \"unknown\";\n\t\t\tswitch(timer_clocktype)\n\t\t\t{\n\t\t\tcase QCLOCK_GTOD:\t\tclockname = \"gettimeofday\";\tbreak;\n#ifdef CLOCK_MONOTONIC\n\t\t\tcase QCLOCK_MONOTONIC:\tclockname = \"monotonic\";\tbreak;\n#endif\n#ifdef CLOCK_REALTIME\n\t\t\tcase QCLOCK_REALTIME:\tclockname = \"realtime\";\tbreak;\n#endif\n\t\t\tcase QCLOCK_AUTO:\n\t\t\tcase QCLOCK_INVALID:\tbreak;\n\t\t\t}\n\t\t\tCon_Printf(\"Clock %s, wraps after %\"PRIu64\" days, %\"PRIu64\" years\\n\", clockname, (((quint64_t)-1)/newfreq)/(24*60*60), (((quint64_t)-1)/newfreq)/(24*60*60*365));\n\t\t}*/\n\t}\n}\nstatic void Sys_InitClock(void)\n{\n\tquint64_t freq;\n\n\tCvar_Register(&sys_clocktype, \"System vars\");\n\n\t//calibrate it, and apply.\n\tSys_ClockType_Changed(NULL, NULL);\n\ttimer_basetime = 0;\n\ttimer_basetime = Sys_GetClock(&freq);\n}\ndouble Sys_DoubleTime (void)\n{\n\tquint64_t denum, num = Sys_GetClock(&denum);\n\treturn num / (long double)denum;\n}\nunsigned int Sys_Milliseconds (void)\n{\n\tquint64_t denum, num = Sys_GetClock(&denum);\n\tnum *= 1000;\n\treturn num / denum;\n}\n\n\n\n#ifdef USE_LIBTOOL\nvoid Sys_CloseLibrary(dllhandle_t *lib)\n{\n\tlt_dlclose((void*)lib);\n}\n\ndllhandle_t *Sys_LoadLibrary(const char *name, dllfunction_t *funcs)\n{\n\tint i;\n\tdllhandle_t *lib;\n\n\tlib = NULL;\n\tif (!lib)\n\t\tlib = lt_dlopenext (name);\n\tif (!lib)\n\t{\n\t\tCon_DLPrintf(2, \"%s: %s\\n\", name, lt_dlerror());\n\t\treturn NULL;\n\t}\n\n\tif (funcs)\n\t{\n\t\tfor (i = 0; funcs[i].name; i++)\n\t\t{\n\t\t\t*funcs[i].funcptr = lt_dlsym(lib, funcs[i].name);\n\t\t\tif (!*funcs[i].funcptr)\n\t\t\t\tbreak;\n\t\t}\n\t\tif (funcs[i].name)\n\t\t{\n\t\t\tCon_DPrintf(\"Unable to find symbol \\\"%s\\\" in \\\"%s\\\"\\n\", funcs[i].name, name);\n\t\t\tSys_CloseLibrary((dllhandle_t*)lib);\n\t\t\tlib = NULL;\n\t\t}\n\t}\n\n\treturn (dllhandle_t*)lib;\n}\n\nvoid *Sys_GetAddressForName(dllhandle_t *module, const char *exportname)\n{\n\tif (!module)\n\t\treturn NULL;\n\treturn lt_dlsym((void*)module, exportname);\n}\n#else\nvoid Sys_CloseLibrary(dllhandle_t *lib)\n{\n\tdlclose((void*)lib);\n}\n\ndllhandle_t *Sys_LoadLibrary(const char *name, dllfunction_t *funcs)\n{\n\tint i;\n\tdllhandle_t *lib;\n\n\tlib = NULL;\n\tif (!lib)\n\t\tlib = dlopen (name, RTLD_LOCAL|RTLD_LAZY);\n\tif (!lib && !strstr(name, ARCH_DL_POSTFIX))\n\t\tlib = dlopen (va(\"%s\"ARCH_DL_POSTFIX, name), RTLD_LOCAL|RTLD_LAZY);\n\tif (!lib && !strstr(name, ARCH_DL_POSTFIX) && !strncmp(name, \"./\", 2) && host_parms.binarydir)\n\t\tlib = dlopen (va(\"%s%s\"ARCH_DL_POSTFIX, host_parms.binarydir, name+2), RTLD_LOCAL|RTLD_LAZY);\n\tif (!lib)\n\t{\n\t\tCon_DLPrintf(2,\"%s\\n\", dlerror());\n\t\treturn NULL;\n\t}\n\n\tif (funcs)\n\t{\n\t\tfor (i = 0; funcs[i].name; i++)\n\t\t{\n\t\t\t*funcs[i].funcptr = dlsym(lib, funcs[i].name);\n\t\t\tif (!*funcs[i].funcptr)\n\t\t\t\tbreak;\n\t\t}\n\t\tif (funcs[i].name)\n\t\t{\n\t\t\tCon_DPrintf(\"Unable to find symbol \\\"%s\\\" in \\\"%s\\\"\\n\", funcs[i].name, name);\n\t\t\tSys_CloseLibrary((dllhandle_t*)lib);\n\t\t\tlib = NULL;\n\t\t}\n\t}\n\n\treturn (dllhandle_t*)lib;\n}\n\nvoid *Sys_GetAddressForName(dllhandle_t *module, const char *exportname)\n{\n\tif (!module)\n\t\treturn NULL;\n\treturn dlsym(module, exportname);\n}\n#endif\n\n// =======================================================================\n//friendly way to crash, including stack traces. should help reduce gdb use.\n#if defined(__linux__) && defined(__GLIBC__) /*should probably be GNUC but whatever*/\n#include <execinfo.h>\n#ifdef __i386__\n#include <ucontext.h>\n#endif\n#ifdef DEBUG\nvoid DumpGLState(void);\n#endif\nstatic void Friendly_Crash_Handler(int sig, siginfo_t *info, void *vcontext)\n{\n\tint fd;\n\tvoid *array[64];\n\tsize_t size;\n\tint firstframe = 0;\n\tchar signame[32];\n\n\tswitch(sig)\n\t{\n\tcase SIGINT:    strcpy(signame, \"SIGINT\");  break;\n\tcase SIGILL:    strcpy(signame, \"SIGILL\");  break;\n\tcase SIGFPE:    strcpy(signame, \"SIGFPE\");  break;\n\tcase SIGBUS:    strcpy(signame, \"SIGBUS\");  break;\n\tcase SIGSEGV:   Q_snprintfz(signame, sizeof(signame), \"SIGSEGV (%p)\", info->si_addr);   break;\n\tdefault:    Q_snprintfz(signame, sizeof(signame), \"%i\", sig);   break;\n\t}\n\n\t// get void*'s for all entries on the stack\n\tsize = backtrace(array, sizeof(array)/sizeof(array[0]));\n\n#if defined(__i386__)\n\t//x86 signals don't leave the stack in a clean state, so replace the signal handler with the real crash address, and hide this function\n\tarray[1] = (void*)((ucontext_t*)vcontext)->uc_mcontext.gregs[REG_EIP];\n\tfirstframe = 1;\n#elif defined(__amd64__)\n\t//amd64 is sane enough, but this function and the libc signal handler are on the stack, and should be ignored.\n\tfirstframe = 2;\n#endif\n\n\t// print out all the frames to stderr\n#ifdef SVNREVISION\n\tfprintf(stderr, \"Error: signal %s (revision \"STRINGIFY(SVNREVISION)\")\\n\", signame);\n#else\n\tfprintf(stderr, \"Error: signal %s:\\n\", signame);\n#endif\n\tbacktrace_symbols_fd(array+firstframe, size-firstframe, 2);\n\n\tif (sig == SIGINT || fs_readonly)\n\t\tfd = -1;\t//don't write out crash logs on ctrl+c\n\telse\n\t\tfd = open(\"crash.log\", O_WRONLY|O_CREAT|O_APPEND, S_IRUSR | S_IWUSR | S_IRGRP);\n\tif (fd != -1)\n\t{\n\t\ttime_t rawtime;\n\t\tstruct tm * timeinfo;\n\t\tchar buffer [80];\n\n\t\ttime (&rawtime);\n\t\ttimeinfo = localtime (&rawtime);\n\t\tstrftime (buffer, sizeof(buffer), \"Time: %Y-%m-%d %H:%M:%S\\n\",timeinfo);\n\t\twrite(fd, buffer, strlen(buffer));\n\n\t\tQ_snprintfz(buffer, sizeof(buffer), \"Ver: %i.%02i%s\\n\", FTE_VER_MAJOR, FTE_VER_MINOR,\n#ifdef OFFICIAL_RELEASE\n\t\t\t\" (official)\");\n#else\n\t\t\t\"\");\n#endif\n\t\twrite(fd, buffer, strlen(buffer));\n\n#if defined(SVNREVISION) && defined(SVNDATE)\n\t\tQ_snprintfz(buffer, sizeof(buffer), \"Revision: %s, %s\\n\", STRINGIFY(SVNREVISION), STRINGIFY(SVNDATE));\n#else\n\t\tQ_snprintfz(buffer, sizeof(buffer),\n\t\t#ifdef SVNREVISION\n\t\t\t\"Revision: \"STRINGIFY(SVNREVISION)\"\\n\"\n\t\t#endif\n\t\t\"Binary: \"__DATE__\" \"__TIME__\"\\n\");\n#endif\n\t\twrite(fd, buffer, strlen(buffer));\n\n\t\tbacktrace_symbols_fd(array + firstframe, size - firstframe, fd);\n\t\twrite(fd, \"\\n\", 1);\n\t\tclose(fd);\n\t}\n#if defined(DEBUG) && defined(GLQUAKE)\n\tif (qrenderer == QR_OPENGL)\n\t\tDumpGLState();\n#endif\n\texit(1);\n}\n#endif\n// =======================================================================\n// Sleeps for microseconds\n// =======================================================================\n\nchar *Sys_ConsoleInput(void)\n{\n#if 1\n\tstatic char text[256];\n\tchar *nl;\n\n\tif (dedcon)\n\t{\n\t\tint e;\n\t\tif (fgets(text, sizeof(text), dedcon))\n\t\t\treturn text;\n\t\te = errno;\n\t\tswitch(e)\n\t\t{\n\t\tcase EAGAIN:\n\t\tcase EINTR:\t//not meant to be blocking, but can still be interrupted.\n\t\t\tbreak;\t//cos we made it non-blocking.\n\t\tcase EPIPE:\t//not seen, but possible I guess.\n\t\tcase EIO:\t//can happen if the other end dies.\n\t\t\tSys_CloseTerminal();\n\t\t\treturn \"quit\";\t//kill the server if we were actually using the terminal... or at least try not to run silently.\n\t\tdefault:\n\t\t\tSys_Printf(CON_WARNING \"fgets errno %i\\n\", e);\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tif (noconinput)\n\t\treturn NULL;\n\n#if defined(__linux__) && defined(_DEBUG)\n\t{\n\t\tint fl = fcntl (STDIN_FILENO, F_GETFL, 0);\n\t\tif (!(fl & FNDELAY))\n\t\t{\n\t\t\tfcntl(STDIN_FILENO, F_SETFL, fl | FNDELAY);\n//\t\t\tSys_Printf(CON_WARNING \"stdin flags became blocking - gdb bug?\\n\");\n\t\t}\n\t}\n#endif\n\n//\tif (!qrenderer)\n\t{\n\t\tif (!fgets(text, sizeof(text), stdin))\n\t\t{\n\t\t\tif (errno == EIO)\n\t\t\t{\n\t\t\t\tSys_Printf(CON_WARNING \"Backgrounded, ignoring stdin\\n\");\n\t\t\t\tnoconinput |= 2;\n\t\t\t}\n\t\t\treturn NULL;\n\t\t}\n\t\tnl = strchr(text, '\\n');\n\t\tif (!nl)\t//err? wut?\n\t\t\treturn NULL;\n\t\t*nl = 0;\n\n//Con_Printf(\"console input: %s\\n\", text);\n\n\t\tif (!strncmp(text, \"vid_recenter \", 13))\n\t\t{\n\t\t\tCmd_TokenizeString(text, false, false);\n\t\t\tsys_parentleft = strtoul(Cmd_Argv(1), NULL, 0);\n\t\t\tsys_parenttop = strtoul(Cmd_Argv(2), NULL, 0);\n\t\t\tsys_parentwidth = strtoul(Cmd_Argv(3), NULL, 0);\n\t\t\tsys_parentheight = strtoul(Cmd_Argv(4), NULL, 0);\n\t\t\tsys_parentwindow = strtoul(Cmd_Argv(5), NULL, 16);\n\t\t}\n\n\t\treturn text;\n\t}\n#endif\n\treturn NULL;\n}\n\n//begin meta generation helper\nstatic int Crypto_GenerateSignature(qbyte *hashdata, size_t hashsize, qbyte *signdata, size_t signsizemax)\n{\n\tint i;\n\tint sigsize = -1;\n\tfor (i = 0; sigsize==-1 && i<cryptolib_count; i++)\n\t{\n\t\tif (cryptolib[i] && cryptolib[i]->GenerateSignature)\n\t\t\tsigsize = cryptolib[i]->GenerateSignature(hashdata, hashsize, signdata, signsizemax);\n\t}\n\treturn sigsize;\n}\nstatic void DoSign(const char *fname, int signtype)\n{\n\tqbyte digest[1024], digest2[1024];\n\tqbyte signature[2048];\n\tqbyte base64[2048*4];\n\tint sigsize;\n\tvfsfile_t *f;\n\tconst char *auth = \"Unknown\";\n\tconst char *prefix = \"\";\n\tint i = COM_CheckParm(\"-certhost\");\n\tif (i)\n\t\tauth = com_argv[i+1];\n\ti = COM_CheckParm(\"-prefix\");\n\tif (i)\n\t\tprefix = com_argv[i+1];\n\n\tf = FS_OpenVFS(fname, \"rb\", FS_SYSTEM);\n\tif (f && signtype == -1)\n\t{\t//just report the qhash\n\t\tsearchpathfuncs_t *search = FS_OpenPackByExtension(f, NULL, fname, fname, prefix);\n\t\tif (search)\n\t\t{\n\t\t\tprintf(\"%#08x\", search->GeneratePureCRC(search, NULL));\n\t\t\tsearch->ClosePath(search);\n\t\t}\n\t\telse\n\t\t\tprintf(\"-\");\n\t}\n\telse if (f)\n\t{\n\t\thashfunc_t *h = (signtype==1)?&hash_sha1:\n\t\t\t\t\t\t(signtype==256)?&hash_sha2_256:\n\t\t\t\t\t\t&hash_sha2_512;\n\t\tsize_t l, ts = 0;\n\t\tvoid *ctx = alloca(h->contextsize);\n\t\tqbyte data[65536*16];\n\t\th->init(ctx);\n\t\twhile ((l=VFS_READ(f, data, sizeof(data)))>0)\n\t\t{\n\t\t\th->process(ctx, data, l);\n\t\t\tts += l;\n\t\t}\n\t\th->terminate(digest, ctx);\n\t\tVFS_CLOSE(f);\n\n\t\t//prefix it by the prefix\n\t\tif (*prefix)\n\t\t{\n\t\t\th->init(ctx);\n\t\t\th->process(ctx, prefix, strlen(prefix));\n\t\t\th->process(ctx, \"\\0\", 1);\n\t\t\th->process(ctx, digest, h->digestsize);\n\t\t\th->terminate(digest2, ctx);\n\t\t}\n\t\telse\n\t\t\tmemcpy(digest2, digest, h->digestsize);\n\n\t\tif (signtype == 3)\n\t\t{\t//the stupid package crap in fmf files which is different just to be an absolute pain\n\t\t\tprintf(\" prefix \\\"%s\\\"\", prefix);\n\t\t\tprintf(\" filesize %zu\", ts);\n\n\t\t\tbase64[Base16_EncodeBlock(digest, h->digestsize, base64+2048, sizeof(base64)-2048-1)] = 0;\n\t\t\tprintf(\" sha512 \\\"%s\\\"\", base64+2048);\n\n\t\t\tsigsize = Crypto_GenerateSignature(digest2, h->digestsize, signature, sizeof(signature));\n\t\t\tBase64_EncodeBlock(signature, sigsize, base64, sizeof(base64));\n\t\t\tprintf(\" signature \\\"%s:%s\\\"\\n\", auth, base64);\n\t\t}\n\t\telse if (signtype == 0)\n\t\t{\t//old junk\n\t\t\tif (*prefix)\n\t\t\t\tprintf(\" \\\\\\\"prefix=%s\\\\\\\"\", prefix);\n\t\t\tprintf(\" \\\\\\\"dlsize=%zu\\\\\\\"\", ts);\n\n\t\t\tbase64[Base16_EncodeBlock(digest, h->digestsize, base64, sizeof(base64)-1)] = 0;\n\t\t\tprintf(\" \\\\\\\"sha512=%s\\\\\\\"\", base64);\n\n\t\t\tsigsize = Crypto_GenerateSignature(digest2, h->digestsize, signature, sizeof(signature));\n\t\t\tBase64_EncodeBlock(signature, sigsize, base64, sizeof(base64));\n\t\t\tprintf(\" \\\\\\\"sign=%s:%s\\\\\\\"\\n\", auth, base64);\n\t\t}\n\t\telse if (signtype == 2)\n\t\t{\t//spits out the raw signature.\n\t\t\tsigsize = Crypto_GenerateSignature(digest2, h->digestsize, signature, sizeof(signature));\n\t\t\tBase64_EncodeBlock(signature, sigsize, base64, sizeof(base64));\n\n\t\t\tprintf(\"%s\", base64);\n\t\t}\n\t\telse\n\t\t{\t//just spits out the hash\n\t\t\tbase64[Base16_EncodeBlock(digest, h->digestsize, base64, sizeof(base64))] = 0;\n\t\t\tprintf(\"%s\", base64);\n\t\t}\n\t}\n}\n//end meta helpers\n\nstatic char *Sys_GetBinary(void)\n{\n#ifdef __linux__\n\t#define SYMLINK_TO_BINARY \"/proc/self/exe\"\n#elif defined(__bsd__)\n\t#define SYMLINK_TO_BINARY \"/proc/curproc/file\"\n#endif\n\n#ifdef KERN_PROC_PATHNAME\n\t//freebsd favours this over /proc, for good reason.\n\tint id[] =\n\t{\n\t\tCTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1\n\t};\n\tsize_t sz=0;\n\tif (sysctl(id, countof(id), NULL, &sz, NULL, 0) && sz > 0)\n\t{\n\t\tchar *bindir = Z_Malloc(sz+1);\n\t\tif (sysctl(id, countof(id), bindir, &sz, NULL, 0) && sz > 0)\n\t\t{\n\t\t\tbindir[sz] = 0;\n\t\t\treturn bindir;\n\t\t}\n\t\tZ_Free(bindir);\n\t}\n#endif\n#ifdef SYMLINK_TO_BINARY\n\t{\n\t\t//attempt to figure out where the exe is located\n\t\tsize_t sz = 64;\n\t\tfor(sz = 64; ; sz += 64)\n\t\t{\n\t\t\tchar *bindir = Z_Malloc(sz+1);\n\t\t\tssize_t i = readlink(SYMLINK_TO_BINARY, bindir, sz);\n\t\t\tif (i > 0 && i < sz)\n\t\t\t{\n\t\t\t\tbindir[i] = 0;\n\t\t\t\treturn bindir;\n\t\t\t}\n\t\t\tZ_Free(bindir);\n\t\t\tif (i == sz)\n\t\t\t\tcontinue;\t//continue\n\t\t\tbreak;\t//some other kind of screwup\n\t\t}\n\t}\n#endif\n\treturn NULL;\n}\n\n#ifdef _POSIX_C_SOURCE\nstatic void SigCont(int code)\n{\n\tint fl = fcntl (STDIN_FILENO, F_GETFL, 0);\n\tif (!(fl & FNDELAY))\n\t\tfcntl(STDIN_FILENO, F_SETFL, fl | FNDELAY);\n\tnoconinput &= ~2;\n}\nstatic void SigChldTerminalDied(int code, void *data)\n{\t//our terminal dieded? restart again (this shouldn't loop infinitely...)\n\tSys_CloseTerminal();\n\tCbuf_AddText (\"vid_renderer \\\"\\\"; vid_restart\\n\", RESTRICT_LOCAL);\n}\nstatic void SigChld(int code)\n{\n\tint wstat;\n\tpid_t\tpid;\n\n\tfor(;;)\n\t{\n\t\tpid = wait3 (&wstat, WNOHANG, (struct rusage *)NULL);\n\t\tif (pid == -1)\n\t\t\treturn;\t//error\n\t\telse if (pid == 0)\n\t\t\treturn;\t//nothing left to report (linux seems to like errors instead)\n\t\telse if (pid == dedconproc)\n\t\t\tCmd_AddTimer(0, SigChldTerminalDied, 0, NULL, 0);\n//\t\telse\t//forked subserver? we use pipes to track when it dies.\n//\t\t\tprintf (\"Return code: %d\\n\", wstat);\n\t}\n}\n#endif\nint main (int c, const char **v)\n{\n\tdouble time, oldtime, newtime;\n\tquakeparms_t parms;\n\tint i;\n\n\tsignal(SIGFPE, SIG_IGN);\n\tsignal(SIGPIPE, SIG_IGN);\n#ifdef _POSIX_C_SOURCE\n\tsignal(SIGTTIN, SIG_IGN);\t//have to ignore this if we want to not lock up when running backgrounded.\n\tsignal(SIGCONT, SigCont);\n\tsignal(SIGCHLD, SigChld);\t//mapcluster stuff might leak zombie processes if we don't do this.\n#endif\n\n\n\tmemset(&parms, 0, sizeof(parms));\n\n\tparms.argc = c;\n\tparms.argv = v;\n#ifdef CONFIG_MANIFEST_TEXT\n\tparms.manifest = CONFIG_MANIFEST_TEXT;\n#endif\n\tparms.binarydir = Sys_GetBinary();\n\tif (parms.binarydir)\n\t\t*COM_SkipPath(parms.binarydir) = 0;\n\tCOM_InitArgv(parms.argc, parms.argv);\n\n#ifdef USE_LIBTOOL\n\tlt_dlinit();\n#endif\n\n#ifdef __linux__\n\t{\n\t\tuid_t ruid, euid, suid;\n\t\tgetresuid(&ruid, &euid, &suid);\n\t\tif (!ruid || !euid || !suid)\n\t\t\tfprintf(stderr, \"WARNING: you should NOT be running this as root!\\n\");\n\t}\n#endif\n\n#ifdef PLUGINS\n\tc = COM_CheckParm(\"--plugwrapper\");\n\tif (c)\n\t{\n\t\tint (QDECL *thefunc) (plugcorefuncs_t *corefuncs);\n\t\tdllhandle_t *lib;\n\t\thost_parms = parms;//not really initialising, but the filesystem needs it\n\t\tlib = Sys_LoadLibrary(com_argv[c+1], NULL);\n\t\tif (lib)\n\t\t{\n\t\t\tthefunc = Sys_GetAddressForName(lib, com_argv[c+2]);\n\n\t\t\tif (thefunc)\n\t\t\t\treturn thefunc(&plugcorefuncs);\n\t\t}\n\t\treturn 0;\n\t}\n#endif\n\n#if defined(__linux__) && defined(__GLIBC__)\n\tif (!COM_CheckParm(\"-nodumpstack\"))\n\t{\n\t\tstruct sigaction act;\n\t\tmemset(&act, 0, sizeof(act));\n\t\tact.sa_sigaction = Friendly_Crash_Handler;\n\t\tact.sa_flags = SA_SIGINFO | SA_RESTART;\n\t\tsigaction(SIGILL, &act, NULL);\n\t\tsigaction(SIGSEGV, &act, NULL);\n\t\tsigaction(SIGBUS, &act, NULL);\n\t\tsigaction(SIGINT, &act, NULL);\n\t}\n#endif\n\n\tif (COM_CheckParm(\"-qcdebug\"))\n\t{\n\t\tisPlugin = 3;\n\t\tnostdout = true;\t//only spew debugging messages.\n\t}\n\telse\n\t{\n\t\tisPlugin = !!COM_CheckParm(\"-plugin\");\n\t\tif (isPlugin)\n\t\t{\n\t\t\tprintf(\"status Starting up!\\n\");\n\t\t\tfflush(stdout);\n\t\t\tnostdout = true;\n\t\t}\n\t}\n\n#if _POSIX_C_SOURCE >= 200809L\n\t{\n\t\tchar *path = realpath(\".\", NULL);\n\t\tif (path)\n\t\t{\n\t\t\tsize_t l = strlen(path)+2;\n\t\t\tchar *npath = malloc(strlen(path)+2);\n\t\t\tQ_snprintfz(npath, l, \"%s/\", path);\n\t\t\tparms.basedir = npath;\n\t\t\tfree(path);\n\t\t}\n\t}\n#elif _POSIX_C_SOURCE >= 200112L && defined(PATH_MAX)\n\t{\n\t\tchar path[PATH_MAX];\n\t\tif (realpath(\".\", path))\n\t\t{\n\t\t\tsize_t l = strlen(path)+2;\n\t\t\tchar *npath = malloc(strlen(path)+2);\n\t\t\tQ_snprintfz(npath, l, \"%s/\", path);\n\t\t\tparms.basedir = npath;\n\t\t}\n\t}\n#else\n\tparms.basedir = \"./\";\n#endif\n\n\tTL_InitLanguages(parms.binarydir);\n\n\tif (!isatty(STDIN_FILENO))\n\t\tnoconinput = !isPlugin;\t//don't read the stdin if its probably screwed (running in qtcreator seems to pipe stdout to stdin in an attempt to screw everything up).\n\telse\n\t\tnoconinput = COM_CheckParm(\"-noconinput\") || COM_CheckParm(\"-nostdin\");\n#ifndef __DJGPP__\n\tif (!noconinput)\n\t\tfcntl(STDIN_FILENO, F_SETFL, fcntl (0, F_GETFL, 0) | FNDELAY);\n#endif\n\n#ifdef HAVE_SERVER\n#ifdef SUBSERVERS\n\tif (COM_CheckParm(\"-clusterslave\"))\n\t{\n\t\tisDedicated = true;\n\t\tnostdout = noconinput = true;\n\t\tSSV_SetupControlPipe(Sys_GetStdInOutStream(), false);\n\t}\n#endif\n\tif (COM_CheckParm(\"-dedicated\"))\n\t\tisDedicated = true;\n#endif\n\n\tif (COM_CheckParm(\"-nostdout\"))\n\t\tnostdout = 1;\n\n//begin meta generation helpers\n\t//fteqw -privkey privcert.key -pubkey pubcert.key -certhost Spike -prefix foo -sign binaryfile.pk3\n\t{\n\t\tstatic struct\n\t\t{\n\t\t\tconst char *arg;\n\t\t\tint type;\n\t\t} signarg[] =\n\t\t{\n\t\t\t{\"-sign\", 0},\n\t\t\t{\"-sign2\", 2},\n\t\t\t{\"-signraw\", 2},\n\t\t\t{\"-signfmfpkg\", 3},\n\t\t\t{\"-qhash\", -1},\n\t\t\t{\"-sha1\", 1},\n\t\t\t{\"-sha256\", 256},\n\t\t\t{\"-sha512\", 512},\n\t\t};\n\t\tint j;\n\t\tfor (j = 0; j < countof(signarg); j++)\n\t\t{\n\t\t\ti = COM_CheckParm(signarg[j].arg);\n\t\t\tif (i)\n\t\t\t{\n\t\t\t\t//init some useless crap\n\t\t\t\thost_parms = parms;\n\t\t\t\tCvar_Init();\n\t\t\t\tMemory_Init ();\n\t\t\t\tCOM_Init ();\n\n\t\t\t\tDoSign(com_argv[i+1], signarg[j].type);\n\t\t\t\treturn EXIT_SUCCESS;\n\t\t\t}\n\t\t}\n\t}\n//end\n\n//\tif (parms.binarydir)\n//\t\tSys_Printf(\"Binary is located at \\\"%s\\\"\\n\", parms.binarydir);\n\n#ifndef CLIENTONLY\n\tif (isDedicated)    //compleate denial to switch to anything else - many of the client structures are not initialized.\n\t{\n\t\tfloat delay;\n\n\t\tSV_Init (&parms);\n\n\t\tdelay = SV_Frame();\n\n\t\twhile (1)\n\t\t{\n\t\t\tif (!isDedicated)\n\t\t\t\tSys_Error(\"Dedicated was cleared\");\n\t\t\tNET_Sleep(delay, false);\n\t\t\tdelay = SV_Frame();\n\t\t}\n\t}\n#endif\n\n\tHost_Init(&parms);\n\n\toldtime = Sys_DoubleTime ();\n\twhile (1)\n\t{\n\t\tdouble sleeptime;\n\n#ifdef __MACOSX__\n\t\t//wow, not even windows was this absurd.\n#ifdef GLQUAKE\n\t\tif (glcocoaRunLoop())\n\t\t{\n\t\t\toldtime = Sys_DoubleTime ();\n\t\t\tcontinue;\n\t\t}\n#endif\n#endif\n\n// find time spent rendering last frame\n\t\tnewtime = Sys_DoubleTime ();\n\t\ttime = newtime - oldtime;\n\n#ifdef HAVE_SERVER\n\t\tif (isDedicated)\n\t\t{\n\t\t\tsleeptime = SV_Frame();\n\t\t\toldtime = newtime;\n\t\t\tNET_Sleep(sleeptime, noconinput?false:true);\n\t\t}\n\t\telse\n#endif\n\t\t{\n\t\t\tsleeptime = Host_Frame(time);\n\t\t\toldtime = newtime;\n\n\t\t\tif (sleeptime)\n\t\t\t\tSys_Sleep(sleeptime);\n\t\t}\n\t}\n}\n\n\n/*\n================\nSys_MakeCodeWriteable\n================\n*/\n#if 0\nvoid Sys_MakeCodeWriteable (void *startptr, unsigned long length)\n{\n\n\tint r;\n\tuintptr_t addr;\n\tint psize = getpagesize();\n\n\taddr = ((uintptr_t)startptr & ~(psize-1)) - psize;\n\n//\tfprintf(stderr, \"writable code %lx(%lx)-%lx, length=%lx\\n\", startaddr,\n//\t\t\taddr, startaddr+length, length);\n\n\tr = mprotect((char*)addr, length + startaddr - addr + psize, 7);\n\n\tif (r < 0)\n    \t\tSys_Error(\"Protection change failed\\n\");\n}\n#endif\n\n//fixme: some sort of taskbar/gnome panel flashing.\nvoid Sys_ServerActivity(void)\n{\n}\n\n//FIXME: this is hacky. Unlike other OSes where the GUI is part of the OS, X is seperate\n//from the OS. This will cause problems with framebuffer-only setups.\nqboolean Sys_GetDesktopParameters(int *width, int *height, int *bpp, int *refreshrate)\n{\n#if defined(NO_X11)\n//this about sums up the problem with this function\n\treturn false;\n#else\n\treturn X11_GetDesktopParameters(width, height, bpp, refreshrate);\n/*\n\tDisplay *xtemp;\n\tint scr;\n\n\txtemp = XOpenDisplay(NULL);\n\n\tif (!xtemp)\n\t\treturn false;\n\n\tscr = DefaultScreen(xtemp);\n\n\t*width = DisplayWidth(xtemp, scr);\n\t*height = DisplayHeight(xtemp, scr);\n\t*bpp = DefaultDepth(xtemp, scr);\n\t*refreshrate = 0;\n\n\tXCloseDisplay(xtemp);\n\n\treturn true;\n*/\n#endif\n}\n\n#if defined(NO_X11)\n#define SYS_CLIPBOARD_SIZE\t\t256\nstatic char clipboard_buffer[SYS_CLIPBOARD_SIZE] = {0};\n\nvoid Sys_Clipboard_PasteText(clipboardtype_t cbt, void (*callback)(void *cb, const char *utf8), void *ctx)\n{\n\tcallback(ctx, clipboard_buffer);\n}\n\nvoid Sys_SaveClipboard(clipboardtype_t cbt, const char *text) {\n\tQ_strncpyz(clipboard_buffer, text, SYS_CLIPBOARD_SIZE);\n}\n#endif\n\nqboolean Sys_RandomBytes(qbyte *string, int len)\n{\n\tqboolean res;\n\tint fd = open(\"/dev/urandom\", 0);\n\tres = (read(fd, string, len) == len);\n\tclose(fd);\n\n\treturn res;\n}\n\n#ifdef MANIFESTDOWNLOADS\nqboolean Sys_RunInstaller(void)\n{\n\treturn false;\n}\n#endif\n"
  },
  {
    "path": "engine/client/sys_morphos.c",
    "content": "/*\nCopyright (C) 2006-2007 Mark Olsen\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  \n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n*/\n\n#include <stdlib.h>\n#include <stdio.h>\n#include <stdarg.h>\n#include <sys/time.h>\n\n#include <proto/exec.h>\n#include <proto/dos.h>\n#include <proto/random.h>\n\n#include <dlfcn.h>\n\n#include \"quakedef.h\"\n\n#warning Find a better stack size\n\nint __stack = 4*1024*1024;\n\nstruct Library *DynLoadBase;\n\nextern struct Library *VorbisFileBase;\n\n#ifndef CLIENTONLY\nqboolean isDedicated;\n#endif\n\nvoid Sys_RecentServer(char *command, char *target, char *title, char *desc)\n{\n}\n\nvoid Sys_Shutdown()\n{\n\tif(DynLoadBase)\n\t{\n\t\tCloseLibrary(DynLoadBase);\n\t\tDynLoadBase = 0;\n\t}\n\n\tif (VorbisFileBase)\n\t{\n\t\tCloseLibrary(VorbisFileBase);\n\t\tVorbisFileBase = 0;\n\t}\n}\n\nvoid Sys_Quit (void)\n{\n\tHost_Shutdown();\n\n\tSys_Shutdown();\n\n\texit(0);\n}\n\nstatic void ftevprintf(const char *fmt, va_list arg)\n{\n\tchar buf[4096];\n\tunsigned char *p;\n\n\tvsnprintf(buf, sizeof(buf), fmt, arg);\n\n\tfor (p = (unsigned char *)buf; *p; p++)\n\t\tif ((*p > 128 || *p < 32) && *p != 10 && *p != 13 && *p != 9)\n\t\t\tprintf(\"[%02x]\", *p);\n\t\telse\n\t\t\tputc(*p, stdout);\n}\n\nvoid Sys_Printf(char *fmt, ...)\n{\n\tva_list arg;\n\n\tva_start(arg, fmt);\n\tftevprintf(fmt, arg);\n\tva_end(arg);\n}\n\nvoid Sys_Error(const char *error, ...)\n{\n\tva_list arg;\n\n\tprintf(\"Error: \");\n\tva_start(arg, error);\n\tftevprintf(error, arg);\n\tva_end(arg);\n\n\tHost_Shutdown ();\n\texit (1);\n}\n\nvoid Sys_Warn(char *warning, ...)\n{\n\tva_list arg;\n\n\tprintf(\"Warning: \");\n\tva_start(arg, warning);\n\tftevprintf(warning, arg);\n\tva_end(arg);\n}\n\nint Sys_DebugLog(char *file, char *fmt, ...)\n{\n\tva_list arg;\n\tchar buf[4096];\n\tBPTR fh;\n\n\tfh = Open(file, MODE_READWRITE);\n\tif (fh)\n\t{\n\t\tSeek(fh, OFFSET_END, 0);\n\n\t\tva_start(arg, fmt);\n\t\tvsnprintf(buf, sizeof(buf), fmt, arg);\n\t\tva_end(arg);\n\n\t\tWrite(fh, buf, strlen(buf));\n\n\t\tClose(fh);\n\n\t\treturn 0;\n\t}\n\n\treturn 1;\n}\n\nint secbase;\nunsigned int Sys_Milliseconds(void)\n{\n\tstruct timeval tp;\n\tstruct timezone tzp;\n\n\tgettimeofday(&tp, &tzp);\n\n\tif (!secbase)\n\t{\n\t\tsecbase = tp.tv_sec;\n\t\treturn tp.tv_usec/1000;\n\t}\n\n\treturn (tp.tv_sec - secbase)*1000 + tp.tv_usec/1000;\n}\n\ndouble Sys_DoubleTime(void)\n{\n\tstruct timeval tp;\n\tstruct timezone tzp;\n\n\tgettimeofday(&tp, &tzp);\n\n\tif (!secbase)\n\t{\n\t\tsecbase = tp.tv_sec;\n\t\treturn tp.tv_usec/1000000.0;\n\t}\n\n\treturn (tp.tv_sec - secbase) + tp.tv_usec/1000000.0;\n}\n\n/* FS stuff */\n\nint Sys_FileTime(char *path)\n{\n\tBPTR lock;\n\tstruct FileInfoBlock fib;\n\tint ret = -1;\n\n\tif (path[0] == '.' && path[1] == '/')\n\t\tpath+= 2;\n\n\tlock = Lock(path, ACCESS_READ);\n\tif (lock)\n\t{\n\t\tif (Examine(lock, &fib))\n\t\t{\n\t\t\tret = ((fib.fib_Date.ds_Days+2922)*1440+fib.fib_Date.ds_Minute)*60+fib.fib_Date.ds_Tick/TICKS_PER_SECOND;\n\t\t}\n\n\t\tUnLock(lock);\n\t}\n\n\treturn ret;\n}\n\nint Sys_EnumerateFiles(const char *gpath, const char *match, int (*func)(const char *, qofs_t, void *), void *parm)\n{\n\tchar *pattern;\n\tchar pattrans[256];\n\tchar finddir[256];\n\tchar findpattern[514];\n\tchar filename[256];\n\tint i, j;\n\tBPTR lock;\n\tstruct FileInfoBlock fib;\n\tint ret = false;\n\n\tsnprintf(finddir, sizeof(finddir), \"%s/%s\", gpath, match);\n\n\tpattern = strrchr(finddir, '/');\n\tif (pattern)\n\t{\n\t\tfinddir[((unsigned int)(pattern-finddir))] = 0;\n\t\tpattern++;\n\t}\n\n\tfor(i=0,j=0;i<sizeof(pattrans)-1 && *pattern;i++,j++)\n\t{\n\t\tif (pattern[j] == '*')\n\t\t{\n\t\t\tif (i < sizeof(pattrans)-2)\n\t\t\t{\n\t\t\t\tpattrans[i] = '#';\n\t\t\t\tpattrans[i+1] = '?';\n\t\t\t\ti++;\n\t\t\t}\n\t\t\telse\n\t\t\t\tpattrans[i] = 0;\n\t\t}\n\t\telse\n\t\t\tpattrans[i] = pattern[j];\n\t}\n\n\tpattrans[i] = 0;\n\n\tlock = Lock(finddir, ACCESS_READ);\n\tif (lock)\n\t{\n\t\tif (Examine(lock, &fib))\n\t\t{\n\t\t\tif (ParsePatternNoCase(pattrans, findpattern, sizeof(findpattern)) >= 0)\n\t\t\t{\n\t\t\t\tret = true;\n\n\t\t\t\twhile(ExNext(lock, &fib))\n\t\t\t\t{\n\t\t\t\t\tif (MatchPatternNoCase(findpattern, fib.fib_FileName))\n\t\t\t\t\t{\n\t\t\t\t\t\tsnprintf(filename, sizeof(filename), \"%s%s\", fib.fib_FileName, fib.fib_DirEntryType>=0?\"/\":\"\");\n\t\t\t\t\t\tif (func(filename, fib.fib_Size, parm) == 0)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tret = false;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tUnLock(lock);\n\t}\n\n\treturn ret;\n}\n\nvoid Sys_mkdir(const char *path)\n{\n\tBPTR lock;\n\n\tif (path[0] == '.' && path[1] == '/')\n\t\tpath+= 2;\n\n\tlock = CreateDir(path);\n\tif (lock)\n\t{\n\t\tUnLock(lock);\n\t}\n}\n\nqboolean Sys_rmdir (const char *path)\n{\n\treturn false;\n}\n\nqboolean Sys_remove(const char *path)\n{\n\tif (path[0] == '.' && path[1] == '/')\n\t\tpath+= 2;\n\n\treturn DeleteFile(path);\n}\nqboolean Sys_Rename (const char *oldfname, const char *newfname)\n{\n\treturn !rename(oldfname, newfname);\n}\n\nvoid Sys_CloseLibrary(dllhandle_t *lib)\n{\n\tdlclose((void*)lib);\n}\n\ndllhandle_t *Sys_LoadLibrary(const char *name, dllfunction_t *funcs)\n{\n\tint i;\n\tdllhandle_t lib;\n\n\tlib = dlopen (name, RTLD_LAZY);\n\tif (!lib)\n\t\treturn NULL;\n\n\tif (funcs)\n\t{\n\t\tfor (i = 0; funcs[i].name; i++)\n\t\t{\n\t\t\t*funcs[i].funcptr = dlsym(lib, funcs[i].name);\n\t\t\tif (!*funcs[i].funcptr)\n\t\t\t\tbreak;\n\t\t}\n\t\tif (funcs[i].name)\n\t\t{\n\t\t\tSys_CloseLibrary((dllhandle_t*)lib);\n\t\t\tlib = NULL;\n\t\t}\n\t}\n\n\treturn (dllhandle_t*)lib;\n}\n\nvoid *Sys_GetAddressForName(dllhandle_t *module, const char *exportname)\n{\n\tif (!module)\n\t\treturn NULL;\n\treturn dlsym(module, exportname);\n}\n\nint main(int argc, char **argv)\n{\n\tdouble oldtime, newtime;\n\tquakeparms_t parms;\n\tint i;\n\n\tmemset(&parms, 0, sizeof(parms));\n\n\tCOM_InitArgv(argc, argv);\n\tTL_InitLanguages(\"\");\n\n\tparms.basedir = \"\";\n\tparms.argc = argc;\n\tparms.argv = argv;\n\n\tDynLoadBase = OpenLibrary(\"dynload.library\", 0);\n\n\tHost_Init(&parms);\n\n\toldtime = Sys_DoubleTime ();\n\twhile(!(SetSignal(0, 0)&SIGBREAKF_CTRL_C))\n\t{\n\t\tdouble sleeptime;\n\n\t\tnewtime = Sys_DoubleTime ();\n\t\tsleeptime = Host_Frame(newtime - oldtime);\n\t\toldtime = newtime;\n\n\t\tSys_Sleep(sleeptime);\n\t}\n}\n\nchar *Sys_URIScheme_NeedsRegistering(void)\n{\t//no support, report something that'll disable annoying prompts.\n    return NULL;\n}\nvoid Sys_Init()\n{\n}\n\n#define SYS_CLIPBOARD_SIZE  256\nstatic char clipboard_buffer[SYS_CLIPBOARD_SIZE] = {0};\nvoid Sys_Clipboard_PasteText(clipboardtype_t cbt, void (*callback)(void *cb, char *utf8), void *ctx)\n{\n\tcallback(ctx, clipboard_buffer);\n}\nvoid Sys_SaveClipboard(clipboardtype_t cbt, char *text)\n{\n \tQ_strncpyz(clipboard_buffer, text, SYS_CLIPBOARD_SIZE);\n}\n\nqboolean Sys_InitTerminal()\n{\n\treturn false;\n}\nvoid Sys_CloseTerminal()\n{\n}\n\nchar *Sys_ConsoleInput()\n{\n\treturn 0;\n}\n\nvoid Sys_ServerActivity(void)\n{\n}\n\nqboolean Sys_GetDesktopParameters(int *width, int *height, int *bpp, int *refreshrate)\n{\n\treturn false;\n}\n\nqboolean Sys_RandomBytes(qbyte *string, int len)\n{\n\twhile(len--)\n\t\t*string++ = RandomByte();\n\n\treturn true;\n}\n\n#ifdef MULTITHREAD\n/* Everything here is stubbed because I don't know MorphOS */\n/* Thread creation calls */\nvoid *Sys_CreateThread(char *name, int (*func)(void *), void *args, int priority, int stacksize) { return NULL; }\nvoid Sys_WaitOnThread(void *thread) {}\n/* Mutex calls */\nvoid *Sys_CreateMutex(void) { return NULL; }\nqboolean Sys_TryLockMutex(void *mutex) { return false; }\nqboolean Sys_LockMutex(void *mutex) { return false; }\nqboolean Sys_UnlockMutex(void *mutex) { return false; }\nvoid Sys_DestroyMutex(void *mutex) {}\n/* Conditional wait calls */\nvoid *Sys_CreateConditional(void) { return NULL; }\nqboolean Sys_LockConditional(void *condv) { return false; }\nqboolean Sys_UnlockConditional(void *condv) { return false; }\nqboolean Sys_ConditionWait(void *condv) { return false; }\nqboolean Sys_ConditionSignal(void *condv) { return false; }\nqboolean Sys_ConditionBroadcast(void *condv) { return false; }\nvoid Sys_DestroyConditional(void *condv) {}\n#endif\n\nvoid Sys_Sleep(double seconds) {}\n"
  },
  {
    "path": "engine/client/sys_plugfte.c",
    "content": "#include \"quakedef.h\"\r\n#include \"winquake.h\"\r\n#include \"sys_plugfte.h\"\r\n#include \"../http/iweb.h\"\r\n#ifndef _WIN32\r\n#include <sys/stat.h>\r\n#endif\r\n\r\nstatic void UnpackAndExtractPakFiles_Complete(struct dl_download *dl);\r\nstatic void pscript_property_splash_sets(struct context *ctx, const char *val);\r\nvoid *globalmutex;\r\n\r\n#ifdef _MSC_VER\r\n# ifdef _WIN64\r\n# pragma comment(lib, MSVCLIBSPATH \"zlib64.lib\")\r\n# else\r\n# pragma comment(lib, MSVCLIBSPATH \"zlib.lib\")\r\n# endif\r\n#endif\r\n\r\n#ifndef INVALID_FILE_ATTRIBUTES\r\n#define INVALID_FILE_ATTRIBUTES ~0\r\n#endif\r\n\r\n#ifdef _WIN32\r\ndllhandle_t *Sys_LoadLibrary(const char *name, dllfunction_t *funcs)\r\n{\r\n\treturn NULL;\r\n}\r\n#else\r\n#include <dlfcn.h>\r\nvoid Sys_CloseLibrary(dllhandle_t *lib)\r\n{\r\n\tif (lib)\r\n\t\tdlclose(lib);\r\n}\r\ndllhandle_t *Sys_LoadLibrary(const char *name, dllfunction_t *funcs)\r\n{\r\n\tchar soname[MAX_OSPATH];\r\n\tint i;\r\n\tdllhandle_t *lib;\r\n\r\n\tlib = NULL;\r\n\tif (!lib)\r\n\t{\r\n\t\tQ_snprintfz(soname, sizeof(soname), \"%s.so\", name);\r\n\t\tlib = (dllhandle_t*)dlopen (soname, RTLD_LAZY);\r\n\t}\r\n\tif (!lib)\r\n\t\tlib = (dllhandle_t*)dlopen (name, RTLD_LAZY);\r\n\tif (!lib)\r\n\t{\r\n\t\tCon_Printf(\"%s\\n\", dlerror());\r\n\t\treturn NULL;\r\n\t}\r\n\r\n\tif (funcs)\r\n\t{\r\n\t\tfor (i = 0; funcs[i].name; i++)\r\n\t\t{\r\n\t\t\t*funcs[i].funcptr = dlsym(lib, funcs[i].name);\r\n\t\t\tif (!*funcs[i].funcptr)\r\n\t\t\t\tbreak;\r\n\t\t}\r\n\t\tif (funcs[i].name)\r\n\t\t{\r\n\t\t\tCon_Printf(\"Unable to find symbol \\\"%s\\\" in \\\"%s\\\"\\n\", funcs[i].name, name);\r\n\t\t\tSys_CloseLibrary((dllhandle_t*)lib);\r\n\t\t\tlib = NULL;\r\n\t\t}\r\n\t}\r\n\r\n\treturn (dllhandle_t*)lib;\r\n}\r\n#endif\r\n\r\nvoid BZ_Free(void *ptr)\r\n{\r\n\tfree(ptr);\r\n}\r\nvoid *BZF_Malloc(int size)\r\n{\r\n\treturn malloc(size);\r\n}\r\n//FIXME: we can't use this.\r\nvoid *BZ_Malloc(int size)\r\n{\r\n\treturn BZF_Malloc(size);\r\n}\r\n\r\nvoid QDECL Q_strncpyz(char *d, const char *s, int n)\r\n{\r\n\tint i;\r\n\tn--;\r\n\tif (n < 0)\r\n\t\treturn;\t//this could be an error\r\n\r\n\tfor (i=0; *s; i++)\r\n\t{\r\n\t\tif (i == n)\r\n\t\t\tbreak;\r\n\t\t*d++ = *s++;\r\n\t}\r\n\t*d='\\0';\r\n}\r\nchar *COM_SkipPath (const char *pathname)\r\n{\r\n\tconst char\t*last;\r\n\r\n\tlast = pathname;\r\n\twhile (*pathname)\r\n\t{\r\n\t\tif (*pathname=='/' || *pathname == '\\\\')\r\n\t\t\tlast = pathname+1;\r\n\t\tpathname++;\r\n\t}\r\n\treturn (char *)last;\r\n}\r\nvoid VARGS Q_vsnprintfz (char *dest, size_t size, const char *fmt, va_list argptr)\r\n{\r\n\tvsnprintf (dest, size, fmt, argptr);\r\n\tdest[size-1] = 0;\r\n}\r\nvoid VARGS Q_snprintfz (char *dest, size_t size, const char *fmt, ...)\r\n{\r\n\tva_list\t\targptr;\r\n\r\n\tva_start (argptr, fmt);\r\n\tQ_vsnprintfz(dest, size, fmt, argptr);\r\n\tva_end (argptr);\r\n}\r\n\r\nchar *COM_TrimString(char *str, char *buffer, int buffersize)\r\n{\r\n\tint i;\r\n\twhile (*str <= ' ' && *str>'\\0')\r\n\t\tstr++;\r\n\r\n\tfor (i = 0; i < buffersize-1; i++)\r\n\t{\r\n\t\tif (*str <= ' ')\r\n\t\t\tbreak;\r\n\t\tbuffer[i] = *str++;\r\n\t}\r\n\tbuffer[i] = '\\0';\r\n\treturn buffer;\r\n}\r\nvoid VARGS Con_Printf (const char *fmt, ...)\r\n{\r\n\tva_list\t\targptr;\r\n\tchar dest[256];\r\n\r\n\tva_start (argptr, fmt);\r\n\tQ_vsnprintfz(dest, sizeof(dest), fmt, argptr);\r\n\tva_end (argptr);\r\n\r\n#ifdef _WIN32\r\n\tOutputDebugString(dest);\r\n#else\r\n\tFILE *f = fopen(\"/tmp/ftelog\", \"a\");\r\n\tif (f)\r\n\t{\r\n\t\tfputs(dest, f);\r\n\t\tfclose(f);\r\n\t}\r\n\twrite(2, dest, strlen(dest));\r\n#endif\r\n}\r\nvoid VARGS Con_DPrintf (const char *fmt, ...)\r\n{\r\n\tva_list\t\targptr;\r\n\tchar dest[256];\r\n\r\n\tva_start (argptr, fmt);\r\n\tQ_vsnprintfz(dest, sizeof(dest), fmt, argptr);\r\n\tva_end (argptr);\r\n\r\n#ifdef _WIN32\r\n\tOutputDebugString(dest);\r\n#else\r\n\tFILE *f = fopen(\"/tmp/ftelog\", \"a\");\r\n\tif (f)\r\n\t{\r\n\t\tfputs(dest, f);\r\n\t\tfclose(f);\r\n\t}\r\n\twrite(2, dest, strlen(dest));\r\n#endif\r\n}\r\n#ifdef _WIN32\r\n// don't use these functions in MSVC8\r\n#if (_MSC_VER < 1400)\r\nint VARGS linuxlike_snprintf(char *buffer, int size, const char *format, ...)\r\n{\r\n#undef _vsnprintf\r\n\tint ret;\r\n\tva_list\t\targptr;\r\n\r\n\tif (size <= 0)\r\n\t\treturn 0;\r\n\tsize--;\r\n\r\n\tva_start (argptr, format);\r\n\tret = _vsnprintf (buffer,size, format,argptr);\r\n\tva_end (argptr);\r\n\r\n\tbuffer[size] = '\\0';\r\n\r\n\treturn ret;\r\n}\r\n\r\nint VARGS linuxlike_vsnprintf(char *buffer, int size, const char *format, va_list argptr)\r\n{\r\n#undef _vsnprintf\r\n\tint ret;\r\n\r\n\tif (size <= 0)\r\n\t\treturn 0;\r\n\tsize--;\r\n\r\n\tret = _vsnprintf (buffer,size, format,argptr);\r\n\r\n\tbuffer[size] = '\\0';\r\n\r\n\treturn ret;\r\n}\r\n#else\r\nint VARGS linuxlike_snprintf_vc8(char *buffer, int size, const char *format, ...)\r\n{\r\n\tint ret;\r\n\tva_list\t\targptr;\r\n\r\n\tva_start (argptr, format);\r\n\tret = vsnprintf_s (buffer,size, _TRUNCATE, format,argptr);\r\n\tva_end (argptr);\r\n\r\n\treturn ret;\r\n}\r\n#endif\r\n\r\n#endif\r\n\r\n#include \"netinc.h\"\r\n#ifndef _WIN32\r\n#define pgetaddrinfo getaddrinfo\r\n#define pfreeaddrinfo freeaddrinfo\r\n#else\r\n#if 0//def _MSC_VER\r\n#include <wspiapi.h>\r\n#define pgetaddrinfo getaddrinfo\r\n#define pfreeaddrinfo freeaddrinfo\r\n#else\r\n#include <ws2tcpip.h>\r\n#endif\r\n#endif\r\nsize_t\tNET_StringToSockaddr2 (const char *s, int defaultport, struct sockaddr_qstorage *sadr, int *addrfamily, int *addrsize, size_t addrcount)\r\n{\r\n\tstruct addrinfo *addrinfo = NULL;\r\n\tstruct addrinfo *pos;\r\n\tstruct addrinfo udp6hint;\r\n\tint error;\r\n\tchar *port;\r\n\tchar dupbase[256];\r\n\tint len;\r\n#ifndef pgetaddrinfo\r\n\tint (WINAPI *pgetaddrinfo)(const char *nodename, const char *servname, const struct addrinfo *hints, struct addrinfo **res);\r\n\tvoid (WINAPI *pfreeaddrinfo)(struct addrinfo *ai);\r\n\tvoid *lib = LoadLibrary(\"ws2_32.dll\");\r\n\tif (!lib)\r\n\t\treturn false;\r\n\tif (addrcount != 1)\r\n\t\treturn false;\r\n\tpgetaddrinfo = (void*)GetProcAddress(lib, \"getaddrinfo\");\r\n\tpfreeaddrinfo = (void*)GetProcAddress(lib, \"freeaddrinfo\");\r\n\tif (!pgetaddrinfo || !pfreeaddrinfo)\r\n\t\treturn 0;\r\n#endif\r\n\r\n\tif (!(*s))\r\n\t\treturn 0;\r\n\r\n\tmemset (sadr, 0, sizeof(*sadr));\r\n\r\n\r\n\tmemset(&udp6hint, 0, sizeof(udp6hint));\r\n\tudp6hint.ai_family = 0;//Any... we check for AF_INET6 or 4\r\n\tudp6hint.ai_socktype = SOCK_DGRAM;\r\n\tudp6hint.ai_protocol = IPPROTO_UDP;\r\n\r\n\t//handle parsing of ipv6 literal addresses\r\n\tif (*s == '[')\r\n\t{\r\n\t\tport = strstr(s, \"]\");\r\n\t\tif (!port)\r\n\t\t\terror = EAI_NONAME;\r\n\t\telse\r\n\t\t{\r\n\t\t\tlen = port - (s+1);\r\n\t\t\tif (len >= sizeof(dupbase))\r\n\t\t\t\tlen = sizeof(dupbase)-1;\r\n\t\t\tstrncpy(dupbase, s+1, len);\r\n\t\t\tdupbase[len] = '\\0';\r\n\t\t\terror = pgetaddrinfo(dupbase, (port[1] == ':')?port+2:NULL, &udp6hint, &addrinfo);\r\n\t\t}\r\n\t}\r\n\telse\r\n\t{\r\n\t\tport = strrchr(s, ':');\r\n\r\n\t\tif (port)\r\n\t\t{\r\n\t\t\tlen = port - s;\r\n\t\t\tif (len >= sizeof(dupbase))\r\n\t\t\t\tlen = sizeof(dupbase)-1;\r\n\t\t\tstrncpy(dupbase, s, len);\r\n\t\t\tdupbase[len] = '\\0';\r\n\t\t\terror = pgetaddrinfo(dupbase, port+1, &udp6hint, &addrinfo);\r\n\t\t}\r\n\t\telse\r\n\t\t\terror = EAI_NONAME;\r\n\t\tif (error)\t//failed, try string with no port.\r\n\t\t\terror = pgetaddrinfo(s, NULL, &udp6hint, &addrinfo);\t//remember, this func will return any address family that could be using the udp protocol... (ip4 or ip6)\r\n\t}\r\n\tif (error)\r\n\t{\r\n\t\treturn 0;\r\n\t}\r\n\t((struct sockaddr*)sadr)->sa_family = 0;\r\n\tfor (pos = addrinfo; pos; pos = pos->ai_next)\r\n\t{\r\n\t\tswitch(pos->ai_family)\r\n\t\t{\r\n\t\tdefault:\r\n\t\t\t//unrecognised address families are ignored.\r\n\t\t\tbreak;\r\n\t\tcase AF_INET6:\r\n\t\t\tif (((struct sockaddr_in *)sadr)->sin_family == AF_INET6)\r\n\t\t\t\tbreak;\t//first one should be best...\r\n\t\t\t//fallthrough\r\n\t\tcase AF_INET:\r\n\t\t\tmemcpy(sadr, pos->ai_addr, pos->ai_addrlen);\r\n\t\t\tif (pos->ai_family == AF_INET)\r\n\t\t\t\tgoto dblbreak;\t//don't try finding any more, this is quake, they probably prefer ip4...\r\n\t\t\tbreak;\r\n\t\t}\r\n\t}\r\ndblbreak:\r\n\tpfreeaddrinfo (addrinfo);\r\n\tif (!((struct sockaddr*)sadr)->sa_family)\t//none suitablefound\r\n\t\treturn 0;\r\n\r\n\tif (addrfamily)\r\n\t\t*addrfamily = ((struct sockaddr*)sadr)->sa_family;\r\n\r\n\tif (((struct sockaddr*)sadr)->sa_family == AF_INET)\r\n\t{\r\n\t\tif (!((struct sockaddr_in *)sadr)->sin_port)\r\n\t\t\t((struct sockaddr_in *)sadr)->sin_port = htons(defaultport);\r\n\t\tif (addrsize)\r\n\t\t\t*addrsize = sizeof(struct sockaddr_in);\r\n\t}\r\n\telse\r\n\t{\r\n\t\tif (!((struct sockaddr_in6 *)sadr)->sin6_port)\r\n\t\t\t((struct sockaddr_in6 *)sadr)->sin6_port = htons(defaultport);\r\n\t\tif (addrsize)\r\n\t\t\t*addrsize = sizeof(struct sockaddr_in6);\r\n\t}\r\n\r\n\treturn 1;\r\n}\r\n\r\nchar *COM_ParseType (const char *data, char *out, int outlen, com_tokentype_t *toktype)\r\n{\r\n\tint\t\tc;\r\n\tint\t\tlen;\r\n\r\n//\tif (out == com_token)\r\n//\t\tCOM_AssertMainThread(\"COM_ParseOut: com_token\");\r\n\r\n\tlen = 0;\r\n\tout[0] = 0;\r\n\tif (toktype)\r\n\t\t*toktype = TTP_EOF;\r\n\r\n\tif (!data)\r\n\t\treturn NULL;\r\n\r\n// skip whitespace\r\nskipwhite:\r\n\twhile ( (c = *data) <= ' ')\r\n\t{\r\n\t\tif (c == 0)\r\n\t\t\treturn NULL;\t\t\t// end of file;\r\n\t\tdata++;\r\n\t}\r\n\r\n// skip // comments\r\n\tif (c=='/')\r\n\t{\r\n\t\tif (data[1] == '/')\r\n\t\t{\r\n\t\t\twhile (*data && *data != '\\n')\r\n\t\t\t\tdata++;\r\n\t\t\tgoto skipwhite;\r\n\t\t}\r\n\t}\r\n\r\n//skip / * comments\r\n\tif (c == '/' && data[1] == '*')\r\n\t{\r\n\t\tdata+=2;\r\n\t\twhile(*data)\r\n\t\t{\r\n\t\t\tif (*data == '*' && data[1] == '/')\r\n\t\t\t{\r\n\t\t\t\tdata+=2;\r\n\t\t\t\tgoto skipwhite;\r\n\t\t\t}\r\n\t\t\tdata++;\r\n\t\t}\r\n\t\tgoto skipwhite;\r\n\t}\r\n\r\n// handle quoted strings specially\r\n\tif (c == '\\\"')\r\n\t{\r\n\t\tif (toktype)\r\n\t\t\t*toktype = TTP_STRING;\r\n\r\n\t\tdata++;\r\n\t\twhile (1)\r\n\t\t{\r\n\t\t\tif (len >= outlen-1)\r\n\t\t\t{\r\n\t\t\t\tout[len] = 0;\r\n\t\t\t\treturn (char*)data;\r\n\t\t\t}\r\n\r\n\t\t\tc = *data++;\r\n\t\t\tif (c=='\\\"' || !c)\r\n\t\t\t{\r\n\t\t\t\tout[len] = 0;\r\n\t\t\t\treturn (char*)data;\r\n\t\t\t}\r\n\t\t\tout[len] = c;\r\n\t\t\tlen++;\r\n\t\t}\r\n\t}\r\n\r\n// parse a regular word\r\n\tif (toktype)\r\n\t\t*toktype = TTP_RAWTOKEN;\r\n\tdo\r\n\t{\r\n\t\tif (len >= outlen-1)\r\n\t\t{\r\n\t\t\tout[len] = 0;\r\n\t\t\treturn (char*)data;\r\n\t\t}\r\n\r\n\t\tout[len] = c;\r\n\t\tdata++;\r\n\t\tlen++;\r\n\t\tc = *data;\r\n\t} while (c>32);\r\n\r\n\tout[len] = 0;\r\n\treturn (char*)data;\r\n}\r\n\r\n#if 0\r\ntypedef struct \r\n{\r\n\tvfsfile_t funcs;\r\n\r\n\tchar *data;\r\n\tint maxlen;\r\n\tint writepos;\r\n\tint readpos;\r\n} vfspipe_t;\r\n\r\nvoid VFSPIPE_Close(vfsfile_t *f)\r\n{\r\n\tvfspipe_t *p = (vfspipe_t*)f;\r\n\tfree(p->data);\r\n\tfree(p);\r\n}\r\nunsigned long VFSPIPE_GetLen(vfsfile_t *f)\r\n{\r\n\tvfspipe_t *p = (vfspipe_t*)f;\r\n\treturn p->writepos - p->readpos;\r\n}\r\nunsigned long VFSPIPE_Tell(vfsfile_t *f)\r\n{\r\n\treturn 0;\r\n}\r\nqboolean VFSPIPE_Seek(vfsfile_t *f, unsigned long offset)\r\n{\r\n\tCon_Printf(\"Seeking is a bad plan, mmkay?\\n\");\r\n\treturn false;\r\n}\r\nint VFSPIPE_ReadBytes(vfsfile_t *f, void *buffer, int len)\r\n{\r\n\tvfspipe_t *p = (vfspipe_t*)f;\r\n\tif (len > p->writepos - p->readpos)\r\n\t\tlen = p->writepos - p->readpos;\r\n\tmemcpy(buffer, p->data+p->readpos, len);\r\n\tp->readpos += len;\r\n\r\n\tif (p->readpos > 8192)\r\n\t{\r\n\t\t//shift the memory down periodically\r\n\t\t//fixme: use cyclic buffer? max size, etc?\r\n\t\tmemmove(p->data, p->data+p->readpos, p->writepos-p->readpos);\r\n\r\n\t\tp->writepos -= p->readpos;\r\n\t\tp->readpos = 0;\r\n\t}\r\n\treturn len;\r\n}\r\nint VFSPIPE_WriteBytes(vfsfile_t *f, const void *buffer, int len)\r\n{\r\n\tvfspipe_t *p = (vfspipe_t*)f;\r\n\tif (p->writepos + len > p->maxlen)\r\n\t{\r\n\t\tp->maxlen = p->writepos + len;\r\n\t\tp->data = realloc(p->data, p->maxlen);\r\n\t}\r\n\tmemcpy(p->data+p->writepos, buffer, len);\r\n\tp->writepos += len;\r\n\treturn len;\r\n}\r\n\r\nvfsfile_t *VFSPIPE_Open(void)\r\n{\r\n\tvfspipe_t *newf;\r\n\tnewf = malloc(sizeof(*newf));\r\n\tnewf->data = NULL;\r\n\tnewf->maxlen = 0;\r\n\tnewf->readpos = 0;\r\n\tnewf->writepos = 0;\r\n\tnewf->funcs.Close = VFSPIPE_Close;\r\n\tnewf->funcs.Flush = NULL;\r\n\tnewf->funcs.GetLen = VFSPIPE_GetLen;\r\n\tnewf->funcs.ReadBytes = VFSPIPE_ReadBytes;\r\n\tnewf->funcs.Seek = VFSPIPE_Seek;\r\n\tnewf->funcs.Tell = VFSPIPE_Tell;\r\n\tnewf->funcs.WriteBytes = VFSPIPE_WriteBytes;\r\n\tnewf->funcs.seekingisabadplan = true;\r\n\r\n\treturn &newf->funcs;\r\n}\r\n#endif\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n#if defined(OFFICIAL_RELEASE)\r\n\t#define BUILDTYPE \"rel\"\r\n#else\r\n\t#define BUILDTYPE \"test\"\r\n\t#define UPDATE_URL \"http://triptohell.info/moodles/\"\r\n\t#define UPDATE_URL_VERSION UPDATE_URL \"version.txt\"\r\n\r\n\t//we should perhaps use the uname stuff instead.\r\n\t#ifdef _WIN32\r\n\t\t#ifdef _WIN64\r\n\t\t\t#define UPDATE_URL_BUILD UPDATE_URL \"win64/fte\" EXETYPE \".exe\"\r\n\t\t#else\r\n\t\t\t#define UPDATE_URL_BUILD UPDATE_URL \"win32/fte\" EXETYPE \".exe\"\r\n\t\t#endif\r\n\t#else\r\n\t\t#if defined(__amd64__)\r\n\t\t\t#define UPDATE_URL_BUILD UPDATE_URL \"linux_amd64/fteqw.\" EXETYPE \"64\"\r\n\t\t#else\r\n\t\t\t#define UPDATE_URL_BUILD UPDATE_URL \"linux_x86/fteqw.\" EXETYPE \"32\"\r\n\t\t#endif\r\n\t#endif\r\n#endif\r\n\r\n#ifdef _WIN32\r\n//#if defined(GLQUAKE) && defined(D3DQUAKE)\r\n//\t#define EXETYPE \"qw\"\r\n//#elif defined(GLQUAKE)\r\n//\t#ifdef MINIMAL\r\n//\t\t#define EXETYPE \"minglqw\"\r\n//\t#else\r\n\t\t#define EXETYPE \"glqw\"\r\n//\t#endif\r\n//#elif defined(D3DQUAKE)\r\n//\t#define EXETYPE \"d3dqw\"\r\n//#elif defined(SWQUAKE)\r\n//\t#define EXETYPE \"swqw\"\r\n//#else \t//erm...\r\n//\t#define EXETYPE \"qw\"\r\n//#endif\r\n#else\r\n\t#define EXETYPE \"gl\"\r\n#endif\r\n\r\n#ifdef _WIN32\r\nqboolean MyRegGetStringValue(void *base, const char *keyname, const char *valuename, void *data, size_t datalen)\r\n{\r\n\tqboolean result = false;\r\n\tHKEY subkey;\r\n\tDWORD type = REG_NONE;\r\n\tif (RegOpenKeyEx(base, keyname, 0, KEY_READ, &subkey) == ERROR_SUCCESS)\r\n\t{\r\n\t\tDWORD dl = datalen;\r\n\t\tresult = ERROR_SUCCESS == RegQueryValueEx(subkey, valuename, NULL, &type, data, &dl);\r\n\t\tdatalen = dl;\r\n\t\tRegCloseKey (subkey);\r\n\t}\r\n\r\n\tif (type == REG_SZ || type == REG_EXPAND_SZ)\r\n\t\t((char*)data)[datalen] = 0;\r\n\telse\r\n\t\t((char*)data)[0] = 0;\r\n\treturn result;\r\n}\r\n\r\nqboolean MyRegSetValue(void *base, const char *keyname, const char *valuename, int type, const void *data, int datalen)\r\n{\r\n\tHKEY subkey;\r\n\tif (RegCreateKeyEx(base, keyname, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &subkey, NULL) == ERROR_SUCCESS)\r\n\t{\r\n\t\tRegSetValueEx(subkey, valuename, 0, type, data, datalen);\r\n\t\tRegCloseKey (subkey);\r\n\t}\r\n\treturn true;\r\n}\r\nvoid MyRegDeleteKeyValue(void *base, const char *keyname, const char *valuename)\r\n{\r\n\tHKEY subkey;\r\n\tif (RegOpenKeyEx(base, keyname, 0, KEY_WRITE, &subkey) == ERROR_SUCCESS)\r\n\t{\r\n\t\tRegDeleteValue(subkey, valuename);\r\n\t\tRegCloseKey (subkey);\r\n\t}\r\n}\r\n\r\nqboolean Update_GetHomeDirectory(char *homedir, int homedirsize)\r\n{\r\n\tHMODULE shfolder = LoadLibrary(\"shfolder.dll\");\r\n\r\n\tif (shfolder)\r\n\t{\r\n\t\tHRESULT (WINAPI *dSHGetFolderPath) (HWND hwndOwner, int nFolder, HANDLE hToken, DWORD dwFlags, LPTSTR pszPath);\r\n\t\tdSHGetFolderPath = (void *)GetProcAddress(shfolder, \"SHGetFolderPathA\");\r\n\t\tif (dSHGetFolderPath)\r\n\t\t{\r\n\t\t\tchar folder[MAX_PATH];\r\n\t\t\t// 0x5 == CSIDL_PERSONAL\r\n\t\t\tif (dSHGetFolderPath(NULL, 0x5, NULL, 0, folder) == S_OK)\r\n\t\t\t{\r\n\t\t\t\tQ_snprintfz(homedir, homedirsize, \"%s/My Games/%s/\", folder, FULLENGINENAME);\r\n\t\t\t\treturn true;\r\n\t\t\t}\r\n\t\t}\r\n//\t\tFreeLibrary(shfolder);\r\n\t}\r\n\treturn false;\r\n}\r\n\r\nvoid Sys_mkdir (const char *path)\r\n{\r\n\tCreateDirectory (path, NULL);\r\n}\r\n#else\r\nvoid Sys_mkdir (const char *path)\r\n{\r\n\tmkdir (path, 0777);\r\n}\r\nqboolean Update_GetHomeDirectory(char *homedir, int homedirsize)\r\n{\r\n\tqboolean result = false;\r\n\tchar *val = getenv(\"HOME\");\r\n\tif (!val) val = \".\";\r\n\telse\tresult = true;\r\n\r\n\tQ_strncpyz(homedir, val, homedirsize);\r\n\tQ_strncatz(homedir, \"/.fte/\", homedirsize);\r\n\r\n\treturn result;\r\n}\r\n#endif\r\nstatic void\tUpdate_CreatePath (char *path)\r\n{\r\n\tchar\t*ofs;\r\n\r\n\tfor (ofs = path+1 ; *ofs ; ofs++)\r\n\t{\r\n\t\tif (*ofs == '/' || *ofs == '\\\\')\r\n\t\t{\t// create the directory\r\n\t\t\t*ofs = 0;\r\n\t\t\tSys_mkdir (path);\r\n\t\t\t*ofs = '/';\r\n\t\t}\r\n\t}\r\n}\r\n\r\n#include \"fs.h\"\r\nstruct dl_download *enginedownloadactive;\r\nvoid Update_Version_Updated(struct dl_download *dl)\r\n{\r\n\t//happens in a thread, avoid va\r\n\tif (dl->file)\r\n\t{\r\n\t\tif (dl->status == DL_FINISHED)\r\n\t\t{\r\n\t\t\tchar buf[8192];\r\n\t\t\tunsigned int size = 0, chunk, fullsize;\r\n\t\t\tchar pendingname[MAX_OSPATH];\r\n\t\t\tFILE *pending;\r\n\t\t\tUpdate_GetHomeDirectory(pendingname, sizeof(pendingname));\r\n\t\t\tQ_strncatz(pendingname, \"pending\" BUILDTYPE EXETYPE \".tmp\", sizeof(pendingname));\r\n\t\t\tfullsize = VFS_GETLEN(dl->file);\r\n\t\t\tUpdate_CreatePath(pendingname);\r\n#ifndef _WIN32\r\n\t\t\tunlink(pendingname);\r\n#endif\r\n\t\t\tpending = fopen(pendingname, \"wb\");\r\n\t\t\tif (pending)\r\n\t\t\t{\r\n\t\t\t\twhile(1)\r\n\t\t\t\t{\r\n\t\t\t\t\tchunk = VFS_READ(dl->file, buf, sizeof(buf));\r\n\t\t\t\t\tif (!chunk)\r\n\t\t\t\t\t\tbreak;\r\n\t\t\t\t\tsize += fwrite(buf, 1, chunk, pending);\r\n\t\t\t\t}\r\n\t\t\t\tfclose(pending);\r\n\t\t\t\tif (fullsize == size)\r\n\t\t\t\t{\r\n#ifdef _WIN32\r\n\t\t\t\t\tMyRegSetValue(HKEY_CURRENT_USER, \"Software\\\\\"FULLENGINENAME, \"pending\" BUILDTYPE EXETYPE, REG_SZ, pendingname, strlen(pendingname)+1);\r\n#else\r\n\t\t\t\t\tchmod(pendingname, S_IRUSR|S_IXUSR);\r\n#endif\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\tdl->status = DL_FAILED;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t\tdl->status = DL_FAILED;\r\n\t\t}\r\n\t\tVFS_CLOSE(dl->file);\r\n\t\tdl->file = NULL;\r\n\t}\r\n}\r\nqboolean Plug_GetDownloadedName(char *updatedpath, int updatedpathlen)\r\n{\r\n#ifdef _WIN32\r\n\tchar pendingpath[MAX_OSPATH];\r\n\tchar temppath[MAX_OSPATH];\r\n\r\n\tSys_LockMutex(globalmutex);\r\n\r\n\tif (!enginedownloadactive || (enginedownloadactive->status == DL_FINISHED || enginedownloadactive->status == DL_FAILED))\r\n\t{\r\n\t\tif (enginedownloadactive)\r\n\t\t\tDL_Close(enginedownloadactive);\r\n\t\tenginedownloadactive = NULL;\r\n\r\n\t\t/*if there's some downloaded binary which isn't running yet, kill the old active one and update it*/\r\n\t\tMyRegGetStringValue(HKEY_CURRENT_USER, \"Software\\\\\"FULLENGINENAME, \"pending\" BUILDTYPE EXETYPE, pendingpath, sizeof(pendingpath));\r\n\t\tif (*pendingpath)\r\n\t\t{\r\n\t\t\tMyRegDeleteKeyValue(HKEY_CURRENT_USER, \"Software\\\\\"FULLENGINENAME, \"pending\" BUILDTYPE EXETYPE);\r\n\t\t\tUpdate_GetHomeDirectory(temppath, sizeof(temppath));\r\n\t\t\tUpdate_CreatePath(temppath);\r\n\t\t\tQ_strncatz(temppath, \"cur\" BUILDTYPE EXETYPE\".exe\", sizeof(temppath));\r\n\t\t\tDeleteFile(temppath);\r\n\t\t\tif (MoveFile(pendingpath, temppath))\r\n\t\t\t\tMyRegSetValue(HKEY_CURRENT_USER, \"Software\\\\\"FULLENGINENAME, BUILDTYPE EXETYPE, REG_SZ, temppath, strlen(temppath)+1);\r\n\t\t}\r\n\r\n\t\t/*grab the binary to run from the registry*/\r\n\t\tMyRegGetStringValue(HKEY_CURRENT_USER, \"Software\\\\\"FULLENGINENAME, BUILDTYPE EXETYPE, updatedpath, updatedpathlen);\r\n\t\tif (*updatedpath && INVALID_FILE_ATTRIBUTES==GetFileAttributes(updatedpath))\t//make sure its actually still there.\r\n\t\t\t*updatedpath = 0;\r\n\t\tif (!*updatedpath)\r\n\t\t{\r\n\t\t\t/*ooer, its not set, try and download one. updates are handled by the client itself.*/\r\n\t\t\tenginedownloadactive = DL_Create(UPDATE_URL_BUILD);\r\n\t\t\tif (enginedownloadactive)\r\n\t\t\t{\r\n\t\t\t\tenginedownloadactive->user_ctx = NULL;\r\n\t\t\t\tDL_CreateThread(enginedownloadactive, VFSPIPE_Open(1, false), Update_Version_Updated);\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\tSys_UnlockMutex(globalmutex);\r\n#else\r\n\r\n\tchar pendingpath[MAX_OSPATH];\r\n\tchar currentpath[MAX_OSPATH];\r\n\tstruct stat st;\r\n\r\n\tUpdate_GetHomeDirectory(currentpath, sizeof(currentpath));\r\n\tQ_strncatz(currentpath, \"cur\" BUILDTYPE EXETYPE, sizeof(currentpath));\r\n\tUpdate_GetHomeDirectory(pendingpath, sizeof(pendingpath));\r\n\tQ_strncatz(pendingpath, \"pending\" BUILDTYPE EXETYPE \".tmp\", sizeof(pendingpath));\r\n\r\n\t//truncated to be the same string? :s\r\n\tif (!strcmp(currentpath, pendingpath))\r\n\t\treturn false;\r\n\r\n\tSys_LockMutex(globalmutex);\r\n\r\n\tif (!enginedownloadactive || (enginedownloadactive->status == DL_FINISHED || enginedownloadactive->status == DL_FAILED))\r\n\t{\r\n\t\t//make sure the download is cleaned up properly.\r\n\t\tif (enginedownloadactive)\r\n\t\t\tDL_Close(enginedownloadactive);\r\n\t\tenginedownloadactive = NULL;\r\n\r\n\t\t//if the engine wrote a new version to use, copy it over to the proper path now (two paths means we're less likely to be trying to write a file that is already open).\r\n\t\tif (!stat(pendingpath, &st))\r\n\t\t{\r\n\t\t\t//rename supposedly overwrites automagically. I'm paranoid.\r\n\t\t\tunlink(currentpath);\r\n\t\t\trename(pendingpath, currentpath);\r\n\t\t}\r\n\r\n\t\tif (!stat(currentpath, &st))\r\n\t\t{\r\n\t\t\t//there's a current file, yay.\r\n\t\t\tQ_strncpyz(updatedpath, currentpath, updatedpathlen);\r\n\t\t}\r\n\t\telse\r\n\t\t{\t//no engine available to run. we'll have to download one (we only need to do it in this case because the engine itself autoupdates,\r\n\t\t\t//and we can't because we don't actually know what version we have, only the binary that's running knows its version.\r\n\t\t\t//that's the theory anyway.\r\n\t\t\tCon_Printf(\"downloading %s\\n\", UPDATE_URL_BUILD);\r\n\t\t\tenginedownloadactive = DL_Create(UPDATE_URL_BUILD);\r\n                        if (enginedownloadactive)\r\n                        {\r\n                                enginedownloadactive->user_ctx = NULL;\r\n                                DL_CreateThread(enginedownloadactive, VFSPIPE_Open(), Update_Version_Updated);\r\n                        }\r\n\r\n\t\t}\r\n\t}\r\n\tSys_UnlockMutex(globalmutex);\r\n\t\r\n#endif\r\n\treturn !!*updatedpath;\r\n}\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\nstruct context\r\n{\r\n\tstruct contextpublic pub;\r\n\r\n\tvoid *windowhnd;\r\n\tint windowleft;\r\n\tint windowtop;\r\n\tint windowwidth;\r\n\tint windowheight;\r\n\r\n\tint waitingfordatafiles;\r\n\r\n\tchar *datadownload;\r\n\tchar *gamename;\r\n\tchar *password;\r\n\tchar *onstart;\r\n\tchar *onend;\r\n\tchar *ondemoend;\r\n\tchar *curserver;\t//updated by engine\r\n\r\n\tvoid *hostinstance;\r\n\r\n\tint read;\r\n\tint written;\r\n\r\n\tqtvfile_t qtvf;\r\n\r\n\tunsigned char *splashdata;\r\n\tint splashwidth;\r\n\tint splashheight;\r\n\tstruct dl_download *splashdownload;\r\n\tstruct dl_download *packagelist;\r\n\r\n\tvoid *mutex;\r\n\tvoid *thread;\r\n\tchar resetvideo;\r\n\tqboolean shutdown;\r\n\tqboolean multiplecontexts;\r\n\r\n\tstruct context *next;\r\n\r\n\tstruct browserfuncs bfuncs;\r\n\r\n#ifdef _WIN32\r\n\tHANDLE pipetoengine;\r\n\tHANDLE pipefromengine;\r\n\tHANDLE engineprocess;\r\n#else\r\n\tint pipetoengine;\r\n\tint pipefromengine;\r\n\tpid_t engineprocess;\r\n#endif\r\n};\r\n\r\n#ifdef _WIN32\r\n\r\nextern HWND sys_parentwindow;\r\nextern unsigned int sys_parentwidth;\r\nextern unsigned int sys_parentheight;\r\nHINSTANCE\tglobal_hInstance;\r\nstatic char binarypath[MAX_PATH];\r\n\r\nBOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)\r\n{\r\n\tchar *bp;\r\n\tswitch (fdwReason)\r\n\t{\r\n\tcase DLL_PROCESS_ATTACH:\r\n\t\tglobal_hInstance = hinstDLL;\r\n\t\tGetModuleFileName(global_hInstance, binarypath, sizeof(binarypath));\r\n\t\tbp = COM_SkipPath(binarypath);\r\n\t\tif (bp)\r\n\t\t\t*bp = 0;\r\n\t\tbreak;\r\n\tdefault:\r\n\t\tbreak;\r\n\t}\r\n\treturn TRUE;\r\n}\r\n#endif\r\n\r\nstruct context *activecontext;\r\nstruct context *contextlist;\r\n\r\n#define ADDRARG(x) do {if (argc < maxargs) argv[argc++] = strdup(x);} while(0)\t\t\t\t//for strings that are safe\r\n#define ADDCARG(x) do {if (argc < maxargs) argv[argc++] = cleanarg(x);} while(0)\t//for arguments that we don't trust.\r\nchar *cleanarg(char *arg)\r\n{\r\n\tunsigned char *c;\r\n\t//skip over any leading spaces.\r\n\twhile (*arg && *arg <= ' ')\r\n\t\targ++;\r\n\r\n\t//reject anything with a leading + or -\r\n\tif (*arg == '-' || *arg == '+')\r\n\t\treturn strdup(\"badarg\");\r\n\r\n\t//clean up the argument\r\n\tfor (c = (unsigned char *)arg; *c; c++)\r\n\t{\r\n\t\t//remove special chars... we automagically add quotes so any that exist are someone trying to get past us.\r\n\t\tif (*c == ';' || *c == '\\n' || *c == '\\\"')\r\n\t\t\t*c = '?';\r\n\t\t//remove other control chars.\r\n\t\t//we allow spaces\r\n\t\tif (*c < ' ')\r\n\t\t\t*c = '?';\r\n\t}\r\n\r\n\treturn strdup(arg);\r\n}\r\n\r\nqboolean Plug_GetBinaryName(char *exe, int exelen, char *basedir, int basedirlen)\r\n{\r\n//\tchar buffer[1024];\r\n//\tchar cmd[64];\r\n//\tchar value[1024];\r\n//\tFILE *f;\r\n\r\n\t*exe = 0;\r\n\t*basedir = 0;\r\n\r\n#ifdef _DEBUG\r\n#ifdef _WIN32\r\n\tQ_strncpyz(exe, \"C:/Games/Quake/fte_trunk/fteglqw_dbg.exe\", exelen);\r\n#else\r\n\tQ_strncpyz(exe, \"/home/david/quake/fte_trunk/engine/debug/fteqw.gl\", exelen);\r\n#endif\r\n#endif\r\n/*\r\n\tQ_snprintfz(buffer, sizeof(buffer), \"%s%s\", binarypath, \"npfte.txt\");\r\n\r\n\tf = fopen(buffer, \"rt\");\r\n\tif (f)\r\n\t{\r\n\t\twhile(fgets(buffer, sizeof(buffer), f))\r\n\t\t{\r\n\t\t\t*cmd = 0;\r\n\t\t\t*value = 0;\r\n\t\t\tCOM_ParseOut(COM_ParseOut(buffer, cmd, sizeof(cmd)), value, sizeof(value));\r\n\t\t\tif (!strcmp(cmd, \"relexe\"))\r\n\t\t\t\tQ_snprintfz(exe, exelen, \"%s%s\", binarypath, value);\r\n\t\t\telse if (!strcmp(cmd, \"absexe\"))\r\n\t\t\t\tQ_strncpyz(exe, value, exelen);\r\n\t\t\telse if (!strcmp(cmd, \"basedir\"))\r\n\t\t\t\tQ_strncpyz(basedir, value, basedirlen);\r\n\t\t}\r\n\t\tfclose(f);\r\n\t}\r\n*/\r\n\tif (!*exe)\r\n\t\treturn Plug_GetDownloadedName(exe, exelen);\r\n\treturn false;\r\n}\r\n\r\nint Plug_GenCommandline(struct context *ctx, char **argv, int maxargs)\r\n{\r\n\tchar *s;\r\n\tint argc;\r\n\tchar tok[256];\r\n\tchar exe[1024];\r\n\tchar basedir[1024];\r\n\tqboolean autoupdate;\r\n\r\n\t*exe = 0;\r\n\t*basedir = 0;\r\n\r\n\tautoupdate = Plug_GetBinaryName(exe, sizeof(exe), basedir, sizeof(basedir));\r\n\r\n\tif (!*exe)\r\n\t{\r\n\t\tCon_Printf(\"Unable to determine what to run\\n\");\r\n\t\treturn 0;\t//error\r\n\t}\r\n\r\n\targc = 0;\r\n\r\n\tADDRARG(exe);\r\n\r\n\tif (autoupdate)\r\n\t{\r\n\t\tADDRARG(\"-autoupdate\");\r\n\t}\r\n\r\n\tADDRARG(\"-plugin\");\r\n\r\n\tif (*basedir)\r\n\t{\r\n\t\tADDRARG(\"-basedir\");\r\n\t\tADDCARG(basedir);\r\n\t}\r\n\r\n\tswitch(ctx->qtvf.connectiontype)\r\n\t{\r\n\tdefault:\r\n\t\tbreak;\r\n\tcase QTVCT_STREAM:\r\n\t\tADDRARG(\"+qtvplay\");\r\n\t\tADDCARG(ctx->qtvf.server);\r\n\t\tbreak;\r\n\tcase QTVCT_CONNECT:\r\n\t\tADDRARG(\"+connect\");\r\n\t\tADDCARG(ctx->qtvf.server);\r\n\t\tbreak;\r\n\tcase QTVCT_JOIN:\r\n\t\tADDRARG(\"+join\");\r\n\t\tADDCARG(ctx->qtvf.server);\r\n\t\tbreak;\r\n\tcase QTVCT_OBSERVE:\r\n\t\tADDRARG(\"+observe\");\r\n\t\tADDCARG(ctx->qtvf.server);\r\n\t\tbreak;\r\n\tcase QTVCT_MAP:\r\n\t\tADDRARG(\"+map\");\r\n\t\tADDCARG(ctx->qtvf.server);\r\n\t\tbreak;\r\n\t}\r\n\r\n\tif (ctx->password)\r\n\t{\r\n\t\tADDRARG(\"+password\");\r\n\t\tADDCARG(ctx->password);\r\n\t}\r\n\r\n\t//figure out the game dirs (first token is the base game)\r\n\ts = ctx->gamename;\r\n\ts = COM_ParseOut(s, tok, sizeof(tok));\r\n\tif (!*tok || !strcmp(tok, \"q1\") || !strcmp(tok, \"qw\") || !strcmp(tok, \"quake\"))\r\n\t\tADDRARG(\"-quake\");\r\n\telse if (!strcmp(tok, \"q2\") || !strcmp(tok, \"quake2\"))\r\n\t\tADDRARG(\"-q2\");\r\n\telse if (!strcmp(tok, \"q3\") || !strcmp(tok, \"quake3\"))\r\n\t\tADDRARG(\"-q3\");\r\n\telse if (!strcmp(tok, \"hl\") || !strcmp(tok, \"halflife\"))\r\n\t\tADDRARG(\"-halflife\");\r\n\telse if (!strcmp(tok, \"h2\") || !strcmp(tok, \"hexen2\"))\r\n\t\tADDRARG(\"-hexen2\");\r\n\telse if (!strcmp(tok, \"nex\") || !strcmp(tok, \"nexuiz\"))\r\n\t\tADDRARG(\"-nexuiz\");\r\n\telse\r\n\t{\r\n\t\tADDRARG(\"-basegame\");\r\n\t\tADDCARG(tok);\r\n\t}\r\n\t//later options are additions to that\r\n\twhile ((s = COM_ParseOut(s, tok, sizeof(tok))))\r\n\t{\r\n\t\tif (argc == sizeof(argv)/sizeof(argv[0]))\r\n\t\t\tbreak;\r\n\t\tADDRARG(\"-addbasegame\");\r\n\t\tADDCARG(tok);\r\n\t}\r\n\t//make sure we always use the home directory. because its better than using some random browser-defined working directory.\r\n\tADDRARG(\"-usehome\");\r\n\r\n\tif (ctx->datadownload)\r\n\t{\r\n\t\tADDRARG(\"-manifest\");\r\n\t\tADDCARG(ctx->datadownload);\r\n\t}\r\n\r\n\treturn argc;\r\n}\r\nqboolean Plug_GenCommandlineString(struct context *ctx, char *cmdline, int cmdlinelen)\r\n{\r\n\tchar *argv[64];\r\n\tint argc, i;\r\n\targc = Plug_GenCommandline(ctx, argv, 64);\r\n\tif (!argc)\r\n\t\treturn false;\r\n\tfor (i = 0; i < argc; i++)\r\n\t{\r\n\t\t//add quotes for any arguments with spaces\r\n\t\tif (!*argv[i] || strchr(argv[i], ' ') || strchr(argv[i], '\\t'))\r\n\t\t{\r\n\t\t\tQ_strncatz(cmdline, \"\\\"\", cmdlinelen);\r\n\t\t\tQ_strncatz(cmdline, argv[i], cmdlinelen);\r\n\t\t\tQ_strncatz(cmdline, \"\\\"\", cmdlinelen);\r\n\t\t}\r\n\t\telse\r\n\t\t\tQ_strncatz(cmdline, argv[i], cmdlinelen);\r\n\t\tQ_strncatz(cmdline, \" \", cmdlinelen);\r\n\t}\r\n\treturn true;\r\n}\r\n\r\nvoid Plug_ExecuteCommand(struct context *ctx, char *message, ...)\r\n{\r\n\tva_list\t\tva;\r\n\r\n\tchar\t\tfinalmessage[1024];\r\n\r\n\tva_start (va, message);\r\n\tvsnprintf (finalmessage, sizeof(finalmessage)-1, message, va);\r\n\tva_end (va);\r\n\r\n\tif (ctx->pipetoengine)\r\n\t{\r\n#ifdef _WIN32\r\n\t\tDWORD written = 0;\r\n\t\tWriteFile(ctx->pipetoengine, finalmessage, strlen(finalmessage), &written, NULL);\r\n#else\r\n\t\twrite(ctx->pipetoengine, finalmessage, strlen(finalmessage));\r\n#endif\r\n\t}\r\n}\r\n\r\nqboolean Plug_CreatePluginProcess(struct context *ctx)\r\n{\r\n#ifdef _WIN32\r\n\tchar cmdline[8192];\r\n\tPROCESS_INFORMATION childinfo;\r\n\tSTARTUPINFO startinfo;\r\n\tSECURITY_ATTRIBUTES pipesec = {sizeof(pipesec), NULL, TRUE};\r\n\tif (!Plug_GenCommandlineString(ctx, cmdline, sizeof(cmdline)))\r\n\t\treturn false;\r\n\r\n\tmemset(&startinfo, 0, sizeof(startinfo));\r\n\tstartinfo.cb = sizeof(startinfo);\r\n\tstartinfo.hStdInput = NULL;\r\n\tstartinfo.hStdError = NULL;\r\n\tstartinfo.hStdOutput = NULL;\r\n\tstartinfo.dwFlags |= STARTF_USESTDHANDLES;\r\n\r\n\t//create pipes for the stdin/stdout.\r\n\tCreatePipe(&ctx->pipefromengine, &startinfo.hStdOutput, &pipesec, 0);\r\n\tCreatePipe(&startinfo.hStdInput, &ctx->pipetoengine, &pipesec, 0);\r\n\r\n\tSetHandleInformation(ctx->pipefromengine, HANDLE_FLAG_INHERIT, 0);\r\n\tSetHandleInformation(ctx->pipetoengine, HANDLE_FLAG_INHERIT, 0);\r\n\r\n\tPlug_ExecuteCommand(ctx, \"vid_recenter %i %i %i %i %#p\\n\", ctx->windowleft, ctx->windowtop, ctx->windowwidth, ctx->windowheight, (void*)ctx->windowhnd);\r\n\r\n\tCreateProcess(NULL, cmdline, NULL, NULL, TRUE, 0, NULL, binarypath, &startinfo, &childinfo);\r\n\r\n\t//these ends of the pipes were inherited by now, so we can discard them in the caller.\r\n\tCloseHandle(startinfo.hStdOutput);\r\n\tCloseHandle(startinfo.hStdInput);\r\n\r\n\treturn true;\r\n#else\r\n\tint in[2];\r\n\tint out[2];\r\n        char *argv[64] = {NULL};\r\n\tint i;\r\n\r\n\ti = Plug_GenCommandline(ctx, argv, sizeof(argv) / sizeof(argv[0]));\r\n\tif (!i)\r\n\t\treturn false;\r\n\r\n\targv[i] = NULL;\r\n\r\n/*\tchar buf[512];\r\n\tsnprintf(buf, sizeof(buf), \"%i args\\n\", i);\r\n\twrite(2, buf, strlen(buf));\r\n\tfor (i = 0; argv[i]; i++)\r\n\t{\r\n\t\tsnprintf(buf, sizeof(buf), \"argv[%i] = %s\\n\", i, argv[i]);\r\n\t\twrite(2, buf, strlen(buf));\r\n\t}\r\n*/\r\n\tpipe(in);\r\n\tpipe(out);\r\n\r\n\tctx->pipefromengine = in[0];\r\n\tctx->pipetoengine = out[1];\r\n\r\n\tPlug_ExecuteCommand(ctx, \"vid_recenter %i %i %i %i %#p\\n\", ctx->windowleft, ctx->windowtop, ctx->windowwidth, ctx->windowheight, (void*)ctx->windowhnd);\r\n\r\n\tctx->engineprocess = fork();\r\n\tif (ctx->engineprocess == 0)\r\n\t{\r\n\t\t//close the ends we don't care about\r\n\t\tclose(in[0]);\r\n\t\tclose(out[1]);\r\n\r\n\t\t//dupe them to the stdin/stdout, for ease of use, and clean up the extra handles\r\n\t\tdup2(out[0], 0);\r\n\t\tdup2(in[1], 1);\r\n\t\t//dup2(in[1], 2);\r\n\t\tclose(in[1]);\r\n\t\tclose(out[0]);\r\n\r\n\t\t//invoke the child and kill ourselves if that failed.\r\n\t\twrite(2, \"invoking engine\\n\", 16);\r\n\t\texecv(argv[0], argv);\r\n\t\tfprintf(stderr, \"execv failed: %s\\n\", argv[0]);\r\n\t\texit(1);\r\n\t}\r\n\tclose(in[1]);\r\n\tclose(out[0]);\r\n\r\n\tif (ctx->engineprocess == -1)\r\n\t\treturn false;\r\n\r\n\treturn true;\r\n#endif\r\n}\r\n\r\nvoid Plug_LockPlugin(struct context *ctx, qboolean lockstate)\r\n{\r\n\tif (!ctx || !ctx->mutex)\r\n\t\treturn;\r\n\r\n\tif (lockstate)\r\n\t\tSys_LockMutex(ctx->mutex);\r\n\telse\r\n\t\tSys_UnlockMutex(ctx->mutex);\r\n}\r\n//#define Plug_LockPlugin(c,s) do{Plug_LockPlugin(c,s);VS_DebugLocation(__FILE__, __LINE__, s?\"Lock\":\"Unlock\"); }while(0)\r\n\r\nint Plug_PluginThread(void *ctxptr)\r\n{\r\n\tchar buffer[1024];\r\n\tchar *nl;\r\n\tint bufoffs = 0;\r\n\tstruct context *ctx = ctxptr;\r\n\r\n#if 0\r\n\t//I really don't know what to do about multiple active clients downloading at once.\r\n\t//maybe just depricate this feature. android ports have the same issue with missing files.\r\n\t//we should probably just have the engine take charge of the downloads.\r\n\tif (ctx->datadownload)\r\n\t{\r\n\t\tchar token[1024];\r\n\t\tstruct dl_download *dl;\r\n\t\tchar *s = ctx->datadownload;\r\n\t\tchar *c;\r\n\t\tvfsfile_t *f;\r\n\t\twhile ((s = COM_ParseOut(s, token, sizeof(token))))\r\n\t\t{\r\n\t\t\t//FIXME: do we want to add some sort of file size indicator?\r\n\t\t\tc = strchr(token, ':');\r\n\t\t\tif (!c)\r\n\t\t\t\tcontinue;\r\n\t\t\t*c++ = 0;\r\n\t\t\tf = VFSSTDIO_Open(va(\"%s/%s\", basedir, token), \"rb\", NULL);\r\n\t\t\tif (f)\r\n\t\t\t{\r\n\t\t\t\tPlug_ExecuteCommand(ctx, \"echo \" \"Already have %s\\n\", token);\r\n\t\t\t\tVFS_CLOSE(f);\r\n\t\t\t\tcontinue;\r\n\t\t\t}\r\n\r\n\t\t\tPlug_ExecuteCommand(ctx, \"echo \" \"Attempting to download %s\\n\", c);\r\n\r\n\t\t\tdl = DL_Create(c);\r\n\t\t\tdl->user_ctx = ctx;\r\n\t\t\tdl->next = ctx->packagelist;\r\n\t\t\tif (DL_CreateThread(dl, FS_OpenTemp(), UnpackAndExtractPakFiles_Complete))\r\n\t\t\t\tctx->packagelist = dl;\r\n\t\t}\r\n\r\n\t\tctx->pub.downloading = true;\r\n\t\twhile(!ctx->shutdown && ctx->packagelist)\r\n\t\t{\r\n\t\t\tint total=0, done=0;\r\n\t\t\tctx->resetvideo = false;\r\n\t\t\tSys_LockMutex(ctx->mutex);\r\n\t\t\tfor (dl = ctx->packagelist; dl; dl = dl->next)\r\n\t\t\t{\r\n\t\t\t\ttotal += dl->totalsize;\r\n\t\t\t\tdone += dl->completed;\r\n\t\t\t}\r\n\t\t\tdl = ctx->packagelist;\r\n\t\t\tif (total != ctx->pub.dlsize || done != ctx->pub.dldone)\r\n\t\t\t{\r\n\t\t\t\tctx->pub.dlsize = total;\r\n\t\t\t\tctx->pub.dldone = done;\r\n\t\t\t\tif (ctx->bfuncs.StatusChanged)\r\n\t\t\t\t\tctx->bfuncs.StatusChanged(ctx->hostinstance);\r\n\t\t\t}\r\n\t\t\tif (!dl->file)\r\n\t\t\t\tctx->packagelist = dl->next;\r\n\t\t\telse\r\n\t\t\t\tdl = NULL;\r\n\t\t\tSys_UnlockMutex(ctx->mutex);\r\n\r\n\t\t\t/*file downloads are not canceled while the plugin is locked, to avoid a race condition*/\r\n\t\t\tif (dl)\r\n\t\t\t\tDL_Close(dl);\r\n\t\t\tSleep(10);\r\n\t\t}\r\n\t\tctx->pub.downloading = false;\r\n\t}\r\n#endif\r\n\r\n\tif (!Plug_CreatePluginProcess(ctx))\r\n\t{\r\n\t\tif (!enginedownloadactive)\r\n\t\t{\r\n\t\t\tstrcpy(ctx->pub.statusmessage, \"Unable to determine engine to run. Plugin is not correctly installed.\");\r\n\t\t\tif (ctx->bfuncs.StatusChanged)\r\n\t\t\t\tctx->bfuncs.StatusChanged(ctx->hostinstance);\r\n\t\t\tctx->pub.running = false;\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tint os = -1;\r\n\t\t\twhile(1)\r\n\t\t\t{\r\n\t\t\t\tSys_LockMutex(globalmutex);\r\n\t\t\t\tif (!enginedownloadactive)\r\n\t\t\t\t{\r\n\t\t\t\t\tSys_UnlockMutex(globalmutex);\r\n\t\t\t\t\tif (!Plug_CreatePluginProcess(ctx))\r\n\t\t\t\t\t\tctx->pub.running = false;\r\n\t\t\t\t\tbreak;\r\n\t\t\t\t}\r\n\r\n\t\t\t\tif (os != enginedownloadactive->status)\r\n\t\t\t\t{\r\n\t\t\t\t\tos = enginedownloadactive->status;\r\n\t\t\t\t\tswitch(os)\r\n\t\t\t\t\t{\r\n\t\t\t\t\tcase DL_PENDING:\r\n\t\t\t\t\t\tstrcpy(ctx->pub.statusmessage, \"Download pending.\");\r\n\t\t\t\t\t\tbreak;\r\n\r\n\t\t\t\t\tcase DL_FAILED:\r\n\t\t\t\t\t\tstrcpy(ctx->pub.statusmessage, \"Engine download failed. Try later, or fix internet connection.\");\r\n\t\t\t\t\t\tos = -2;\t//stop trying.\r\n\t\t\t\t\t\tctx->pub.running = false;\r\n\t\t\t\t\t\tbreak;\r\n\r\n\t\t\t\t\tcase DL_RESOLVING:\r\n\t\t\t\t\t\tstrcpy(ctx->pub.statusmessage, \"Download pending. Resolving hostname.\");\r\n\t\t\t\t\t\tbreak;\r\n\r\n\t\t\t\t\tcase DL_QUERY:\r\n\t\t\t\t\t\tstrcpy(ctx->pub.statusmessage, \"Download pending. Reqeusting file.\");\r\n\t\t\t\t\t\tbreak;\r\n\r\n\t\t\t\t\tcase DL_ACTIVE:\r\n\t\t\t\t\t\tQ_snprintfz(ctx->pub.statusmessage, sizeof(ctx->pub.statusmessage), \"Download pending. At %u of %u.\", enginedownloadactive->completed, enginedownloadactive->totalsize);\r\n\t\t\t\t\t\tos = -1;\t//keep refreshing\r\n\t\t\t\t\t\tbreak;\r\n\r\n\t\t\t\t\tcase DL_FINISHED:\r\n\t\t\t\t\t\tstrcpy(ctx->pub.statusmessage, \"Download completed!\");\r\n\t\t\t\t\t\tDL_Close(enginedownloadactive);\r\n\t\t\t\t\t\tenginedownloadactive = NULL;\r\n\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t}\r\n\t\t\t\t\tSys_UnlockMutex(globalmutex);\r\n\t\t\t\t\tif (ctx->bfuncs.StatusChanged)\r\n\t\t\t\t\t\tctx->bfuncs.StatusChanged(ctx->hostinstance);\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t\tSys_UnlockMutex(globalmutex);\r\n\t\t\t\tif (os == -2)\r\n\t\t\t\t\tbreak;\r\n#ifdef _WIN32\r\n\t\t\t\tSleep(1000);\r\n#else\r\n\t\t\t\tsleep(1);\r\n#endif\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\tif (ctx->bfuncs.StatusChanged)\r\n\t\tctx->bfuncs.StatusChanged(ctx->hostinstance);\r\n\r\n\tif (ctx->pub.running)\r\n\twhile(1)\r\n\t{\r\n#ifdef _WIN32\r\n\t\tDWORD avail;\r\n\t\t//use Peek so we can read exactly how much there is without blocking, so we don't have to read byte-by-byte.\r\n\t\tPeekNamedPipe(ctx->pipefromengine, NULL, 0, NULL, &avail, NULL);\r\n\t\tif (!avail)\r\n\t\t\tavail = 1;\t//so we do actually sleep.\r\n\t\tif (avail > sizeof(buffer)-1 - bufoffs)\r\n\t\t\tavail = sizeof(buffer)-1 - bufoffs;\r\n\t\tif (!ReadFile(ctx->pipefromengine, buffer + bufoffs, avail, &avail, NULL) || !avail)\r\n\t\t{\r\n\t\t\t//broken pipe, client died.\r\n\t\t\t*ctx->pub.statusmessage = 0;\r\n\t\t\tbreak;\r\n\t\t}\r\n#else\r\n\t\tint avail;\r\n//\t\tCon_Printf(\"Attempting to read from pipe...\\n\");\r\n\t\tif (bufoffs == sizeof(buffer))\r\n\t\t{\r\n\t\t\tCon_Printf(\"ERROR: Pipe full!\\n\");\r\n\t\t\tbufoffs = 0;\t//not much we can really do\r\n\t\t}\r\n\t\tavail = read(ctx->pipefromengine, buffer + bufoffs, 1);\r\n\t\tif (!avail)\r\n\t\t{\r\n\t\t\tCon_Printf(\"eof on pipe\\n\");\r\n\t\t\t//broken pipe, client died?\r\n\t\t\t*ctx->pub.statusmessage = 0;\r\n\t\t\tbreak;\r\n\t\t}\r\n\t\tif (avail < 0)\r\n\t\t{\r\n\t\t\tint err = errno;\r\n\t\t\tif (err == EAGAIN)\r\n\t\t\t\tcontinue;\r\n\t\t\tperror(\"hello moo\\n\");\r\n\t\t\t//broken pipe, client died?\r\n\t\t\tQ_strncpyz(ctx->pub.statusmessage, \"pipe error\", sizeof(ctx->pub.statusmessage));\r\n\t\t\tbreak;\r\n\t\t}\r\n#endif\r\n\t\tbufoffs += avail;\r\n\t\twhile(1)\r\n\t\t{\r\n\t\t\tbuffer[bufoffs] = 0;\r\n\t\t\tnl = strchr(buffer, '\\n');\r\n\t\t\tif (nl)\r\n\t\t\t{\r\n\t\t\t\t*nl = 0;\r\n\t\t\t\tif (!strncmp(buffer, \"status \", 7))\r\n\t\t\t\t{\r\n\t\t\t\t\t//don't just strcpy it, copy by byte, saves locking.\r\n\t\t\t\t\tint i = strlen(buffer+7)+1;\r\n\t\t\t\t\tif (i > sizeof(ctx->pub.statusmessage))\r\n\t\t\t\t\t\ti = sizeof(ctx->pub.statusmessage);\r\n\t\t\t\t\tctx->pub.statusmessage[i] = 0;\r\n\t\t\t\t\twhile (i-->0)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tctx->pub.statusmessage[i] = buffer[7+i];\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\tif (ctx->bfuncs.StatusChanged)\r\n\t\t\t\t\t\tctx->bfuncs.StatusChanged(ctx->hostinstance);\r\n\t\t\t\t}\r\n\t\t\t\telse if (!strcmp(buffer, \"status\"))\r\n\t\t\t\t{\r\n\t\t\t\t\t*ctx->pub.statusmessage = 0;\r\n\t\t\t\t\tif (ctx->bfuncs.StatusChanged)\r\n\t\t\t\t\t\tctx->bfuncs.StatusChanged(ctx->hostinstance);\r\n\t\t\t\t\tCon_Printf(\"Status changed to \\\"%s\\\"\\n\", buffer+6);\r\n\t\t\t\t}\r\n\t\t\t\telse if (!strcmp(buffer, \"curserver\"))\r\n\t\t\t\t{\r\n\t\t\t\t\tPlug_LockPlugin(ctx, true);\r\n\t\t\t\t\tfree(ctx->curserver);\r\n\t\t\t\t\tctx->curserver = strdup(buffer + 10);\r\n\t\t\t\t\tPlug_LockPlugin(ctx, false);\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\t//handle anything else we need to handle here\r\n\t\t\t\t\tCon_Printf(\"Unknown command from engine \\\"%s\\\"\\n\", buffer);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t\tbreak;\r\n\t\t}\r\n\t}\r\n\tif (ctx->bfuncs.StatusChanged)\r\n\t\tctx->bfuncs.StatusChanged(ctx->hostinstance);\r\n\r\n\tif (ctx == activecontext)\r\n\t\tactivecontext = NULL;\r\n\tctx->pub.running = false;\r\n\treturn 0;\r\n}\r\n\r\n//begins the context, fails if one is already active\r\nqboolean Plug_StartContext(struct context *ctx)\r\n{\r\n\tif (activecontext && !ctx->multiplecontexts)\r\n\t\treturn false;\r\n\r\n\tSys_LockMutex(globalmutex);\r\n\tif (!ctx->pub.running)\r\n\t{\r\n\t\tif (ctx->thread)\r\n\t\t{\r\n\t\t\t//make sure the thread is killed properly, so we don't get two threads trying to drive the same context\r\n\t\t\tSys_UnlockMutex(globalmutex);\r\n\t\t\tPlug_StopContext(ctx, true);\r\n\t\t\tSys_LockMutex(globalmutex);\r\n\t\t}\r\n\r\n\t}\r\n\tif (!ctx->pub.running && !ctx->thread)\r\n\t{\r\n\t\tctx->pub.running = true;\r\n\t\tif (!ctx->multiplecontexts)\r\n\t\t\tactivecontext = ctx;\r\n\t\tif (!ctx->mutex)\r\n\t\t\tctx->mutex = Sys_CreateMutex();\r\n\t\tctx->thread = Sys_CreateThread(\"pluginctx\", Plug_PluginThread, ctx, THREADP_NORMAL, 0);\r\n\t}\r\n\tSys_UnlockMutex(globalmutex);\r\n\r\n\treturn true;\r\n}\r\n\r\n//asks a context to stop, is not instant.\r\nvoid Plug_StopContext(struct context *ctx, qboolean wait)\r\n{\r\n\tvoid *thread;\r\n\tif (ctx == NULL)\r\n\t\tctx = activecontext;\r\n\tif (!ctx)\r\n\t\treturn;\r\n\r\n\tPlug_ExecuteCommand(ctx, \"quit force\\n\");\r\n\r\n\tthread = ctx->thread;\r\n\tif (thread)\r\n\t{\r\n\t\tif (wait)\r\n\t\t{\r\n#ifdef _WIN32\r\n\t\t\twhile (ctx->pub.running && ctx->windowhnd)\r\n\t\t\t{\r\n\t\t\t\tMSG msg;\r\n\t\t\t\twhile (PeekMessage(&msg, ctx->windowhnd,  0, 0, PM_REMOVE))\r\n\t\t\t\t{\r\n\t\t\t\t\tTranslateMessage(&msg);\r\n\t\t\t\t\tDispatchMessage(&msg);\r\n\t\t\t\t}\r\n\t\t\t\tSleep(10);\r\n\t\t\t}\r\n#endif\r\n\t\t\tctx->thread = NULL;\r\n\t\t\tSys_WaitOnThread(thread);\r\n\t\t}\r\n\t}\r\n}\r\n\r\n//creates a plugin context\r\nstruct context *Plug_CreateContext(void *sysctx, const struct browserfuncs *funcs)\r\n{\r\n\tstruct context *ctx;\r\n\r\n\tif (!sysctx || !funcs)\r\n\t\treturn NULL;\r\n\r\n\tctx = malloc(sizeof(struct context));\r\n\tif (!ctx)\r\n\t\treturn NULL;\r\n\tmemset(ctx, 0, sizeof(struct context));\r\n\tmemcpy(&ctx->bfuncs, funcs, sizeof(ctx->bfuncs));\r\n\r\n\t//link the instance to the context and the context to the instance\r\n\tctx->hostinstance = sysctx;\r\n\r\n\tctx->gamename = strdup(\"q1\");\r\n\r\n\t//add it to the linked list\r\n\tctx->next = contextlist;\r\n\tcontextlist = ctx;\r\n\r\n\tctx->qtvf.connectiontype = QTVCT_NONE;\r\n\r\n\treturn ctx;\r\n}\r\n\r\n//change the plugin's parent window, width, and height, returns true if the window handle actually changed, false otherwise\r\nqboolean Plug_ChangeWindow(struct context *ctx, void *whnd, int left, int top, int width, int height)\r\n{\r\n\tqboolean result = false;\r\n\r\n\tPlug_LockPlugin(ctx, true);\r\n\r\n\t//if the window changed\r\n\tif (ctx->windowhnd != whnd)\r\n\t{\r\n\t\tresult = true;\r\n\t\tctx->windowhnd = whnd;\r\n\t\tctx->resetvideo = 2;\r\n\t}\r\n\r\n\tctx->windowleft = left;\r\n\tctx->windowtop = top;\r\n\tctx->windowwidth = width;\r\n\tctx->windowheight = height;\r\n\r\n\tif (ctx->pub.running && !ctx->resetvideo)\r\n\t\tctx->resetvideo = true;\r\n\r\n\tif (ctx->pub.running)\r\n\t\tPlug_ExecuteCommand(ctx, \"vid_recenter %i %i %i %i %#p\\n\", ctx->windowleft, ctx->windowtop, ctx->windowwidth, ctx->windowheight, (void*)ctx->windowhnd);\r\n\r\n\tPlug_LockPlugin(ctx, false);\r\n\r\n\treturn result;\r\n}\r\n\r\nvoid Plug_DestroyContext(struct context *ctx)\r\n{\r\n\tstruct context *prev;\r\n\tif (ctx == contextlist)\r\n\t\tcontextlist = ctx->next;\r\n\telse\r\n\t{\r\n\t\tfor (prev = contextlist; prev->next; prev = prev->next)\r\n\t\t{\r\n\t\t\tif (prev->next == ctx)\r\n\t\t\t{\r\n\t\t\t\tprev->next = ctx->next;\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\tif (ctx->splashdownload)\r\n\t{\r\n\t\tDL_Close(ctx->splashdownload);\r\n\t\tctx->splashdownload = NULL;\r\n\t}\r\n\r\n\tPlug_StopContext(ctx, true);\r\n\r\n\tif (ctx->mutex)\r\n\t\tSys_DestroyMutex(ctx->mutex);\r\n\r\n\t//actually these ifs are not required, just the frees\r\n\tif (ctx->gamename)\r\n\t\tfree(ctx->gamename);\r\n\tif (ctx->password)\r\n\t\tfree(ctx->password);\r\n\tif (ctx->datadownload)\r\n\t\tfree(ctx->datadownload);\r\n\tif (ctx->splashdata)\r\n\t\tfree(ctx->splashdata);\r\n\r\n\tfree(ctx);\r\n}\r\n\r\n\r\n////////////////////////////////////////\r\n\r\n#if 0\r\n#include \"fs.h\"\r\nextern searchpathfuncs_t zipfilefuncs;\r\n\r\nstatic int ExtractDataFile(const char *fname, int fsize, void *ptr)\r\n{\r\n\tchar buffer[8192];\r\n\tint read;\r\n\tvoid *zip = ptr;\r\n\tflocation_t loc;\r\n\tint slashes;\r\n\tconst char *s;\r\n\tvfsfile_t *compressedpak;\r\n\tvfsfile_t *decompressedpak;\r\n\r\n\tif (zipfilefuncs.FindFile(zip, &loc, fname, NULL))\r\n\t{\r\n\t\tcompressedpak = zipfilefuncs.OpenVFS(zip, &loc, \"rb\");\r\n\t\tif (compressedpak)\r\n\t\t{\r\n\t\t\t//this extra logic is so we can handle things like nexuiz/data/blah.pk3\r\n\t\t\t//as well as just data/blah.pk3\r\n\t\t\tslashes = 0;\r\n\t\t\tfor (s = strchr(fname, '/'); s; s = strchr(s+1, '/'))\r\n\t\t\t\tslashes++;\r\n\t\t\tfor (; slashes > 1; slashes--)\r\n\t\t\t\tfname = strchr(fname, '/')+1;\r\n\r\n\t\t\tif (!slashes)\r\n\t\t\t{\r\n\t\t\t\tFS_CreatePath(fname, FS_GAMEONLY);\r\n\t\t\t\tdecompressedpak = FS_OpenVFS(fname, \"wb\", FS_GAMEONLY);\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tFS_CreatePath(fname, FS_ROOT);\r\n\t\t\t\tdecompressedpak = FS_OpenVFS(fname, \"wb\", FS_ROOT);\r\n\t\t\t}\r\n\t\t\tif (decompressedpak)\r\n\t\t\t{\r\n\t\t\t\tfor(;;)\r\n\t\t\t\t{\r\n\t\t\t\t\tread = VFS_READ(compressedpak, buffer, sizeof(buffer));\r\n\t\t\t\t\tif (read <= 0)\r\n\t\t\t\t\t\tbreak;\r\n\t\t\t\t\tVFS_WRITE(decompressedpak, buffer, read);\r\n\t\t\t\t}\r\n\t\t\t\tVFS_CLOSE(decompressedpak);\r\n\t\t\t}\r\n\t\t\tVFS_CLOSE(compressedpak);\r\n\t\t}\r\n\t}\r\n\treturn true;\r\n}\r\n\r\nstatic void UnpackAndExtractPakFiles_Complete(struct dl_download *dl)\r\n{\r\n\textern searchpathfuncs_t zipfilefuncs;\r\n\tvoid *zip;\r\n\r\n\tPlug_LockPlugin(dl->user_ctx, true);\r\n\r\n\tif (dl->status == DL_FINISHED)\r\n\t\tzip = zipfilefuncs.OpenNew(dl->file, dl->url);\r\n\telse\r\n\t\tzip = NULL;\r\n\t/*the zip code will have eaten the file handle*/\r\n\tdl->file = NULL;\r\n\tif (zip)\r\n\t{\r\n\t\t/*scan it to extract its contents*/\r\n\t\tzipfilefuncs.EnumerateFiles(zip, \"*.pk3\", ExtractDataFile, zip);\r\n\t\tzipfilefuncs.EnumerateFiles(zip, \"*.pak\", ExtractDataFile, zip);\r\n\r\n\t\t/*close it, delete the temp file from disk, etc*/\r\n\t\tzipfilefuncs.ClosePath(zip);\r\n\r\n\t\t/*restart the filesystem so those new files can be found*/\r\n\t\tPlug_ExecuteCommand(dl->user_ctx, \"fs_restart\\n\");\r\n\t}\r\n\r\n\tPlug_LockPlugin(dl->user_ctx, false);\r\n}\r\n#endif\r\n\r\nvoid LoadSplashImage(struct dl_download *dl)\r\n{\r\n\tstruct context *ctx = dl->user_ctx;\r\n\tvfsfile_t *f = dl->file;\r\n\tint x, y;\r\n\tint width = 0;\r\n\tint height = 0;\r\n\tint len;\r\n\tchar *buffer;\r\n\tunsigned char *image;\r\n\r\n\tPlug_LockPlugin(ctx, true);\r\n\tctx->splashwidth = 0;\r\n\tctx->splashheight = 0;\r\n\timage = ctx->splashdata;\r\n\tctx->splashdata = NULL;\r\n\tfree(image);\r\n\tPlug_LockPlugin(ctx, false);\r\n\r\n\tif (!f)\r\n\t{\r\n\t\tif (ctx->bfuncs.StatusChanged)\r\n\t\t\tctx->bfuncs.StatusChanged(ctx->hostinstance);\r\n\t\treturn;\r\n\t}\r\n\r\n\tlen = VFS_GETLEN(f);\r\n\tbuffer = malloc(len);\r\n\tVFS_READ(f, buffer, len);\r\n\tVFS_CLOSE(f);\r\n\tdl->file = NULL;\r\n\r\n\timage = NULL;\r\n\tif (!image)\r\n\t\timage = ReadJPEGFile(buffer, len, &width, &height);\r\n\tif (!image)\r\n\t\timage = ReadPNGFile(buffer, len, &width, &height, dl->url);\r\n\r\n\tfree(buffer);\r\n\tif (image)\r\n\t{\r\n\t\tPlug_LockPlugin(ctx, true);\r\n\t\tif (ctx->splashdata)\r\n\t\t\tfree(ctx->splashdata);\r\n\t\tctx->splashdata = malloc(width*height*4);\r\n\t\tfor (y = 0; y < height; y++)\r\n\t\t{\r\n\t\t\tfor (x = 0; x < width; x++)\r\n\t\t\t{\r\n\t\t\t\tctx->splashdata[(y*width + x)*4+0] = image[((height-y-1)*width + x)*4+2];\r\n\t\t\t\tctx->splashdata[(y*width + x)*4+1] = image[((height-y-1)*width + x)*4+1];\r\n\t\t\t\tctx->splashdata[(y*width + x)*4+2] = image[((height-y-1)*width + x)*4+0];\r\n\t\t\t}\r\n\t\t}\r\n\t\tctx->splashwidth = width;\r\n\t\tctx->splashheight = height;\r\n\t\tBZ_Free(image);\r\n\t\tPlug_LockPlugin(ctx, false);\r\n\t\tif (ctx->bfuncs.StatusChanged)\r\n\t\t\tctx->bfuncs.StatusChanged(ctx->hostinstance);\r\n\t}\r\n}\r\n\r\n#if 0\r\nstatic void ReadQTVFileDescriptor(struct context *ctx, vfsfile_t *f, const char *name)\r\n{\r\n\tCL_ParseQTVFile(f, name, &ctx->qtvf);\r\n\r\n\tpscript_property_splash_sets(ctx, ctx->qtvf.splashscreen);\r\n}\r\n\r\nvoid CL_QTVPlay (vfsfile_t *newf, qboolean iseztv);\r\nstatic void BeginDemo(struct context *ctx, vfsfile_t *f, const char *name)\r\n{\r\n\tif (!activecontext)\r\n\t\tactivecontext = ctx;\r\n\r\n\tCL_QTVPlay(f, false);\r\n}\r\nstatic void EndDemo(struct context *ctx, vfsfile_t *f, const char *name)\r\n{\r\n\tCmd_ExecuteString(\"disconnect\", RESTRICT_LOCAL);\r\n}\r\n#endif\r\n/////////////////////////////////////\r\n\r\n\r\n\r\n\r\n\r\n\r\nstruct pscript_property\r\n{\r\n\tchar *name;\r\n\r\n\tchar *cvarname;\r\n\r\n\tchar *(*getstring)(struct context *ctx);\r\n\tvoid (*setstring)(struct context *ctx, const char *val);\r\n\r\n\tint (*getint)(struct context *ctx);\r\n\tvoid (*setint)(struct context *ctx, int val);\r\n\r\n\tfloat (*getfloat)(struct context *ctx);\r\n\tvoid (*setfloat)(struct context *ctx, float val);\r\n};\r\n\r\nint pscript_property_running_getb(struct context *ctx)\r\n{\r\n\tif (ctx->pub.running)\r\n\t\treturn true;\r\n\telse\r\n\t\treturn false;\r\n}\r\n\r\nvoid pscript_property_running_setb(struct context *ctx, int i)\r\n{\r\n\ti = !!i;\r\n\tif (ctx->pub.running == i)\r\n\t\treturn;\r\n\tif (i)\r\n\t\tPlug_StartContext(ctx);\r\n\telse\r\n\t\tPlug_StopContext(ctx, false);\r\n}\r\n\r\nchar *pscript_property_startserver_gets(struct context *ctx)\r\n{\r\n\treturn strdup(ctx->qtvf.server);\r\n}\r\nvoid pscript_property_startserver_sets(struct context *ctx, const char *val)\r\n{\r\n\tif (strchr(val, '$') || strchr(val, ';') || strchr(val, '\\n'))\r\n\t\treturn;\r\n\r\n\tctx->qtvf.connectiontype = QTVCT_JOIN;\r\n\tQ_strncpyz(ctx->qtvf.server, val, sizeof(ctx->qtvf.server));\r\n}\r\nchar *pscript_property_curserver_gets(struct context *ctx)\r\n{\r\n\tif (!pscript_property_running_getb(ctx))\r\n\t\treturn pscript_property_startserver_gets(ctx);\r\n\r\n\tif (ctx->curserver)\r\n\t\treturn strdup(ctx->curserver);\r\n\telse\r\n\t\treturn strdup(\"\");\r\n}\r\nvoid pscript_property_curserver_sets(struct context *ctx, const char *val)\r\n{\r\n\tif (strchr(val, '$') || strchr(val, ';') || strchr(val, '\\n'))\r\n\t\treturn;\r\n\r\n\tif (!pscript_property_running_getb(ctx))\r\n\t{\r\n\t\tpscript_property_startserver_sets(ctx, val);\r\n\t\treturn;\r\n\t}\r\n\r\n\tPlug_ExecuteCommand(ctx, \"connect \\\"%s\\\"\\n\", val);\r\n}\r\n\r\nvoid pscript_property_stream_sets(struct context *ctx, const char *val)\r\n{\r\n\tif (strchr(val, '$') || strchr(val, ';') || strchr(val, '\\n'))\r\n\t\treturn;\r\n\r\n\tctx->qtvf.connectiontype = QTVCT_STREAM;\r\n\tQ_strncpyz(ctx->qtvf.server, val, sizeof(ctx->qtvf.server));\r\n\r\n\tPlug_ExecuteCommand(ctx, \"qtvplay \\\"%s\\\"\\n\", val);\r\n}\r\nvoid pscript_property_map_sets(struct context *ctx, const char *val)\r\n{\r\n\tif (strchr(val, '$') || strchr(val, ';') || strchr(val, '\\n'))\r\n\t\treturn;\r\n\tctx->qtvf.connectiontype = QTVCT_MAP;\r\n\tQ_strncpyz(ctx->qtvf.server, val, sizeof(ctx->qtvf.server));\r\n\r\n\tPlug_ExecuteCommand(ctx, \"map \\\"%s\\\"\\n\", val);\r\n}\r\n\r\nfloat pscript_property_curver_getf(struct context *ctx)\r\n{\r\n\tint base = FTE_VER_MAJOR * 10000 + FTE_VER_MINOR * 100;\r\n\treturn base;\r\n//\treturn version_number();\r\n}\r\n\r\nvoid pscript_property_availver_setf(struct context *ctx, float val)\r\n{\r\n\tctx->pub.availver = val;\r\n\tif (ctx->pub.availver <= pscript_property_curver_getf(ctx))\r\n\t\tctx->pub.availver = 0;\r\n}\r\n\r\nvoid pscript_property_datadownload_sets(struct context *ctx, const char *val)\r\n{\r\n\tfree(ctx->datadownload);\r\n\tctx->datadownload = strdup(val);\r\n}\r\n\r\nvoid pscript_property_game_sets(struct context *ctx, const char *val)\r\n{\r\n\tif (strchr(val, '$') || strchr(val, ';') || strchr(val, '\\n'))\r\n\t\treturn;\r\n\r\n\tif (!strstr(val, \".\"))\r\n\t\tif (!strstr(val, \"/\"))\r\n\t\t\tif (!strstr(val, \"\\\\\"))\r\n\t\t\t\tif (!strstr(val, \":\"))\r\n\t\t\t\t{\r\n\t\t\t\t\tfree(ctx->gamename);\r\n\t\t\t\t\tctx->gamename = strdup(val);\r\n\t\t\t\t}\r\n}\r\n\r\nvoid pscript_property_splash_sets(struct context *ctx, const char *val)\r\n{\r\n\tif (ctx->splashdownload)\r\n\t\tDL_Close(ctx->splashdownload);\r\n\tctx->splashdownload = NULL;\r\n\r\n\tif (val != ctx->qtvf.splashscreen)\r\n\t\tQ_strncpyz(ctx->qtvf.splashscreen, val, sizeof(ctx->qtvf.splashscreen));\r\n\r\n/*\r\n\tctx->splashdownload = DL_Create(ctx->qtvf.splashscreen);\r\n\tctx->splashdownload->user_ctx = ctx;\r\n\tif (!DL_CreateThread(ctx->splashdownload, VFSPIPE_Open(), LoadSplashImage))\r\n\t{\r\n\t\tDL_Close(ctx->splashdownload);\r\n\t\tctx->splashdownload = NULL;\r\n\t}\r\n*/\r\n}\r\n\r\nchar *pscript_property_build_gets(struct context *ctx)\r\n{\r\n\treturn strdup(DISTRIBUTION \" \" __DATE__ \" \" __TIME__\r\n#if defined(DEBUG) || defined(_DEBUG)\r\n\t\t\" (debug)\"\r\n#endif\r\n\t\t);\r\n}\r\n\r\nfloat pscript_property_multi_getf(struct context *ctx)\r\n{\r\n\treturn ctx->multiplecontexts;\r\n}\r\nvoid pscript_property_multi_setf(struct context *ctx, float f)\r\n{\r\n\tctx->multiplecontexts = !!f;\r\n}\r\n\r\nstatic struct pscript_property pscript_properties[] =\r\n{\r\n\t{\"\",\t\t\tNULL,\tpscript_property_curserver_gets, pscript_property_curserver_sets},\r\n\t{\"server\",\t\tNULL,\tpscript_property_curserver_gets, pscript_property_curserver_sets},\r\n\t{\"running\",\t\tNULL,\tNULL, NULL, pscript_property_running_getb, pscript_property_running_setb},\r\n\t{\"startserver\",\tNULL,\tpscript_property_startserver_gets, pscript_property_startserver_sets},\r\n\t{\"join\",\t\tNULL,\tNULL, pscript_property_curserver_sets},\r\n\t{\"playername\",\t\"name\"},\r\n\t{NULL,\t\t\t\"skin\"},\r\n\t{NULL,\t\t\t\"team\"},\r\n\t{NULL,\t\t\t\"topcolor\"},\r\n\t{NULL,\t\t\t\"bottomcolor\"},\r\n\t{NULL,\t\t\t\"password\"},\t//cvars are write only, just so you know.\r\n//\t{NULL,\t\t\t\"spectator\"},\r\n\t{\"mapsrc\",\t\t\"cl_download_mapsrc\"},\r\n\t{\"fullscreen\",\t\"vid_fullscreen\"},\r\n\r\n\t{\"datadownload\",NULL,\tNULL, pscript_property_datadownload_sets},\r\n\t\r\n\t{\"game\",\t\tNULL,\tNULL, pscript_property_game_sets},\r\n\t{\"availver\",\tNULL,\tNULL, NULL,\tNULL, NULL,\tNULL, pscript_property_availver_setf},\r\n\t{\"plugver\",\t\tNULL,\tNULL, NULL,\tNULL, NULL,\tpscript_property_curver_getf},\r\n\t{\"multiple\",\tNULL,\tNULL, NULL,\tNULL, NULL,\tpscript_property_multi_getf, pscript_property_multi_setf},\r\n\t\r\n\t{\"splash\",\t\tNULL,\tNULL, pscript_property_splash_sets},\r\n\r\n\t{\"stream\",\t\tNULL,\tNULL, pscript_property_stream_sets},\r\n\t{\"map\",\t\t\tNULL,\tNULL, pscript_property_map_sets},\r\n\r\n\t{\"build\",\t\tNULL,\tpscript_property_build_gets},\r\n\r\n\t{NULL}\r\n};\r\n\r\nint Plug_FindProp(struct context *ctx, const char *field)\r\n{\r\n\tstruct pscript_property *prop;\r\n\tfor (prop = pscript_properties; prop->name||prop->cvarname; prop++)\r\n\t{\r\n\t\tif (!stricmp(prop->name?prop->name:prop->cvarname, field))\r\n\t\t{\r\n//\t\t\tif (prop->onlyifactive)\r\n//\t\t\t{\r\n//\t\t\t\tif (!ctx->pub.running)\r\n//\t\t\t\t\treturn -1;\r\n//\t\t\t}\r\n\t\t\treturn prop - pscript_properties;\r\n\t\t}\r\n\t}\r\n\treturn -1;\r\n}\r\n\r\nqboolean Plug_SetString(struct context *ctx, int fieldidx, const char *value)\r\n{\r\n\tstruct pscript_property *field = pscript_properties + fieldidx;\r\n\tif (!ctx || fieldidx < 0 || fieldidx >= sizeof(pscript_properties)/sizeof(pscript_properties[0]) || !value)\r\n\t\treturn false;\r\n\tif (field->setstring)\r\n\t{\r\n\t\tPlug_LockPlugin(ctx, true);\r\n\t\tfield->setstring(ctx, value);\r\n\t\tPlug_LockPlugin(ctx, false);\r\n\t}\r\n\telse if (field->setint)\r\n\t{\r\n\t\tPlug_LockPlugin(ctx, true);\r\n\t\tfield->setint(ctx, atoi(value));\r\n\t\tPlug_LockPlugin(ctx, false);\r\n\t}\r\n\telse if (field->setfloat)\r\n\t{\r\n\t\tPlug_LockPlugin(ctx, true);\r\n\t\tfield->setfloat(ctx, atof(value));\r\n\t\tPlug_LockPlugin(ctx, false);\r\n\t}\r\n\telse if (field->cvarname && ctx->pub.running)\r\n\t{\r\n\t\tPlug_LockPlugin(ctx, true);\r\n\t\tPlug_ExecuteCommand(ctx, \"%s \\\"%s\\\"\\n\", field->cvarname, value);\r\n\t\tPlug_LockPlugin(ctx, false);\r\n\t}\r\n\telse\r\n\t\treturn false;\r\n\treturn true;\r\n}\r\nqboolean Plug_SetWString(struct context *ctx, int fieldidx, const wchar_t *value)\r\n{\r\n\tchar tmp[1024];\r\n\twcstombs(tmp, value, sizeof(tmp));\r\n\treturn Plug_SetString(ctx, fieldidx, tmp);\r\n}\r\nqboolean Plug_SetInteger(struct context *ctx, int fieldidx, int value)\r\n{\r\n\tstruct pscript_property *field = pscript_properties + fieldidx;\r\n\tif (!ctx || fieldidx < 0 || fieldidx >= sizeof(pscript_properties)/sizeof(pscript_properties[0]))\r\n\t\treturn false;\r\n\tif (field->setint)\r\n\t{\r\n\t\tPlug_LockPlugin(ctx, true);\r\n\t\tfield->setint(ctx, value);\r\n\t\tPlug_LockPlugin(ctx, false);\r\n\t}\r\n\telse if (field->setfloat)\r\n\t{\r\n\t\tPlug_LockPlugin(ctx, true);\r\n\t\tfield->setfloat(ctx, value);\r\n\t\tPlug_LockPlugin(ctx, false);\r\n\t}\r\n\telse if (field->cvarname && ctx->pub.running)\r\n\t{\r\n\t\tPlug_LockPlugin(ctx, true);\r\n\t\tPlug_ExecuteCommand(ctx, \"%s \\\"%i\\\"\\n\", field->cvarname, value);\r\n\t\tPlug_LockPlugin(ctx, false);\r\n\t}\r\n\telse\r\n\t\treturn false;\r\n\treturn true;\r\n}\r\nqboolean Plug_SetFloat(struct context *ctx, int fieldidx, float value)\r\n{\r\n\tstruct pscript_property *field = pscript_properties + fieldidx;\r\n\tif (!ctx || fieldidx < 0 || fieldidx >= sizeof(pscript_properties)/sizeof(pscript_properties[0]))\r\n\t\treturn false;\r\n\tif (field->setfloat)\r\n\t{\r\n\t\tPlug_LockPlugin(ctx, true);\r\n\t\tfield->setfloat(ctx, value);\r\n\t\tPlug_LockPlugin(ctx, false);\r\n\t}\r\n\telse if (field->setint)\r\n\t{\r\n\t\tPlug_LockPlugin(ctx, true);\r\n\t\tfield->setint(ctx, value);\r\n\t\tPlug_LockPlugin(ctx, false);\r\n\t}\r\n\telse if (field->cvarname && ctx->pub.running)\r\n\t{\r\n\t\tPlug_LockPlugin(ctx, true);\r\n\t\tPlug_ExecuteCommand(ctx, \"%s \\\"%f\\\"\\n\", field->cvarname, value);\r\n\t\tPlug_LockPlugin(ctx, false);\r\n\t}\r\n\telse\r\n\t\treturn false;\r\n\treturn true;\r\n}\r\n\r\nqboolean Plug_GetString(struct context *ctx, int fieldidx, const char **value)\r\n{\r\n\tstruct pscript_property *field = pscript_properties + fieldidx;\r\n\tif (!ctx || fieldidx < 0 || fieldidx >= sizeof(pscript_properties)/sizeof(pscript_properties[0]))\r\n\t\treturn false;\r\n\r\n\tif (field->getstring)\r\n\t{\r\n\t\t*value = field->getstring(ctx);\r\n\t\treturn true;\r\n\t}\r\n\treturn false;\r\n}\r\nvoid Plug_GotString(const char *value)\r\n{\r\n\tfree((char*)value);\r\n}\r\nqboolean Plug_GetInteger(struct context *ctx, int fieldidx, int *value)\r\n{\r\n\tstruct pscript_property *field = pscript_properties + fieldidx;\r\n\tif (!ctx || fieldidx < 0 || fieldidx >= sizeof(pscript_properties)/sizeof(pscript_properties[0]))\r\n\t\treturn false;\r\n\r\n\tif (field->getint)\r\n\t{\r\n\t\t*value = field->getint(ctx);\r\n\t\treturn true;\r\n\t}\r\n\treturn false;\r\n}\r\nqboolean Plug_GetFloat(struct context *ctx, int fieldidx, float *value)\r\n{\r\n\tstruct pscript_property *field = pscript_properties + fieldidx;\r\n\tif (!ctx || fieldidx < 0 || fieldidx >= sizeof(pscript_properties)/sizeof(pscript_properties[0]))\r\n\t\treturn false;\r\n\r\n\tif (field->getfloat)\r\n\t{\r\n\t\t*value = field->getfloat(ctx);\r\n\t\treturn true;\r\n\t}\r\n\treturn false;\r\n}\r\n\r\n#ifdef _WIN32\r\nvoid *Plug_GetSplashBack(struct context *ctx, void *hdc, int *width, int *height)\r\n{\r\n\tBITMAPINFOHEADER bmh;\r\n\r\n\tif (!ctx->splashdata)\r\n\t\treturn NULL;\r\n\r\n\tbmh.biSize = sizeof(bmh);\r\n    bmh.biWidth = *width = ctx->splashwidth;\r\n    bmh.biHeight = *height = ctx->splashheight;\r\n    bmh.biPlanes = 1;\r\n    bmh.biBitCount = 32;\r\n    bmh.biCompression = BI_RGB;\r\n    bmh.biSizeImage = 0;\r\n    bmh.biXPelsPerMeter = 0;\r\n    bmh.biYPelsPerMeter = 0;\r\n    bmh.biClrUsed = 0;\r\n    bmh.biClrImportant = 0;\r\n\r\n\treturn CreateDIBitmap(hdc, \r\n            &bmh, \r\n            CBM_INIT, \r\n            (LPSTR)ctx->splashdata, \r\n            (LPBITMAPINFO)&bmh, \r\n            DIB_RGB_COLORS ); \r\n}\r\nvoid Plug_ReleaseSplashBack(struct context *ctx, void *bmp)\r\n{\r\n\tDeleteObject(bmp);\r\n}\r\n#endif\r\n\r\nstatic const struct plugfuncs exportedplugfuncs_1 =\r\n{\r\n\tPlug_CreateContext,\r\n\tPlug_DestroyContext,\r\n\tPlug_LockPlugin,\r\n\tPlug_StartContext,\r\n\tPlug_StopContext,\r\n\tPlug_ChangeWindow,\r\n\r\n\tPlug_FindProp,\r\n\tPlug_SetString,\r\n\tPlug_GetString,\r\n\tPlug_GotString,\r\n\tPlug_SetInteger,\r\n\tPlug_GetInteger,\r\n\tPlug_SetFloat,\r\n\tPlug_GetFloat,\r\n\r\n#ifdef _WIN32\r\n\tPlug_GetSplashBack,\r\n\tPlug_ReleaseSplashBack,\r\n#endif\r\n\r\n\tPlug_SetWString\r\n};\r\n\r\nconst struct plugfuncs *Plug_GetFuncs(int ver)\r\n{\r\n\tif (!globalmutex)\r\n\t\tglobalmutex = Sys_CreateMutex();\r\n\tif (ver == 1)\r\n\t\treturn &exportedplugfuncs_1;\r\n\telse\r\n\t\treturn NULL;\r\n}\r\n"
  },
  {
    "path": "engine/client/sys_plugfte.h",
    "content": "#ifndef __QUAKEDEF_H__\r\ntypedef enum qboolean;\r\ntypedef void *vfsfile_t;\r\n#endif\r\n\r\nstruct pipetype;\r\nstruct browserfuncs\r\n{\r\n\tqboolean (*RequestDownload)(void *ctx, struct pipetype *ftype, char *url);\r\n\tvoid (*StatusChanged)(void *ctx);\t/*tells it that it needs to redraw the pre-active image*/\r\n};\r\n\r\n/*the conext structure contains this at the start (you can safely cast context->contextpublic)*/\r\nstruct contextpublic\r\n{\r\n\tqboolean running;\t/*set if the plugin context is actually active*/\r\n\tqboolean downloading;\r\n\tunsigned int dlsize;\r\n\tunsigned int dldone;\r\n\tfloat availver;\t/*this is the version of the plugin that is available, if current is better, use 0*/\r\n\tchar statusmessage[256];\r\n\r\n#if defined(_WIN32) && defined(__QUAKEDEF_H__)\r\n\t/*the npapi stuff is lazy and uses this*/\r\n\tvoid *oldwnd;\t/*not used in the plugin itself*/\r\n\tvoid *oldproc;\t/*not used in the plugin itself*/\r\n#endif\r\n\tvoid *user;\r\n};\r\n\r\n/*\r\n#include <windows.h>\r\nstatic void VS_DebugLocation(char *fname, int line, char *fmt, ...)\r\n{\r\n\tchar buffer[640];\r\n\tva_list va;\r\n\r\n\tsprintf(buffer, \"%s(%i) : \", fname, line);\r\n\tOutputDebugStringA(buffer);\r\n\r\n\tva_start(va, fmt);\r\n\tvsprintf(buffer, fmt, va);\r\n\tva_end(va);\r\n\tOutputDebugStringA(buffer);\r\n\tOutputDebugStringA(\"\\n\");\r\n}\r\n*/\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\nstruct context;\r\nstruct pscript_property;\r\n\r\nstruct context *Plug_CreateContext(void *sysctx, const struct browserfuncs *funcs);\r\nvoid Plug_DestroyContext(struct context *ctx);\r\nvoid Plug_LockPlugin(struct context *ctx, qboolean lockstate);\r\nqboolean Plug_StartContext(struct context *ctx);\r\nvoid Plug_StopContext(struct context *ctx, qboolean wait);\r\nqboolean Plug_ChangeWindow(struct context *ctx, void *whnd, int left, int top, int width, int height);\r\n\r\nint Plug_FindProp(struct context *ctx, const char *field);\r\nqboolean Plug_SetString(struct context *ctx, int field, const char *value);\r\nqboolean Plug_GetString(struct context *ctx, int field, const char **value);\r\nvoid Plug_GotString(const char *value);\r\nqboolean Plug_SetInteger(struct context *ctx, int field, int value);\r\nqboolean Plug_GetInteger(struct context *ctx, int field, int *value);\r\nqboolean Plug_SetFloat(struct context *ctx, int field, float value);\r\nqboolean Plug_GetFloat(struct context *ctx, int field, float *value);\r\n\r\n#ifdef _WIN32\r\nvoid *Plug_GetSplashBack(struct context *ctx, void *hdc, int *width, int *height);/*returns an HBITMAP*/\r\nvoid Plug_ReleaseSplashBack(struct context *ctx, void *bmp);\r\n#endif\r\n\r\nstruct plugfuncs\r\n{\r\n\tstruct context *(*CreateContext)(void *sysctx, const struct browserfuncs *funcs);\r\n\tvoid (*DestroyContext)(struct context *ctx);\r\n\tvoid (*LockPlugin)(struct context *ctx, qboolean lockstate);\r\n\tqboolean (*StartContext)(struct context *ctx);\r\n\tvoid (*StopContext)(struct context *ctx, qboolean wait);\r\n\tqboolean (*ChangeWindow)(struct context *ctx, void *whnd, int left, int top, int width, int height);\r\n\r\n\tint (*FindProp)(struct context *ctx, const char *field);\r\n\tqboolean (*SetString)(struct context *ctx, int  field, const char *value);\r\n\tqboolean (*GetString)(struct context *ctx, int  field, const char **value);\r\n\tvoid (*GotString)(const char *value);\r\n\tqboolean (*SetInteger)(struct context *ctx, int  field, int value);\r\n\tqboolean (*GetInteger)(struct context *ctx, int  field, int *value);\r\n\tqboolean (*SetFloat)(struct context *ctx, int  field, float value);\r\n\tqboolean (*GetFloat)(struct context *ctx, int  field, float *value);\r\n\r\n#ifdef _WIN32\r\n\tvoid *(*GetSplashBack)(struct context *ctx, void *hdc, int *width, int *height);/*returns an HBITMAP*/\r\n\tvoid (*ReleaseSplashBack)(struct context *ctx, void *bmp);\r\n#endif\r\n\r\n\tqboolean (*SetWString)(struct context *ctx, int  field, const wchar_t *value);\r\n};\r\n\r\n#define PLUG_APIVER 1\r\n#ifdef __cplusplus\r\nextern \"C\"\r\n#endif\r\nconst struct plugfuncs *Plug_GetFuncs(int ver);\r\n"
  },
  {
    "path": "engine/client/sys_sdl.c",
    "content": "#include \"quakedef.h\"\n\n#ifdef FTE_SDL3\n\t#include <SDL3/SDL.h>\n#else\n\t#include <SDL.h>\n\t#ifdef MULTITHREAD\n\t\t#include <SDL_thread.h>\n\t#endif\n\n\t#include <SDL_loadso.h>\n#endif\n\n#ifdef _WIN32\n\t#include <direct.h>\n#else\n\t#include <fcntl.h>\n\t#include <sys/stat.h>\n\t#if defined(__unix__) || defined(__unix) ||defined(__HAIKU__) || (defined(__APPLE__) && defined(__MACH__))\t//apple make everything painful.\n\t\t#include <unistd.h>\n\t#endif\n#endif\n\n#if SDL_VERSION_ATLEAST(2,0,0)\nextern SDL_Window *sdlwindow;\n#endif\n\n#ifndef isDedicated\nqboolean isDedicated;\n#endif\n\nvoid Sys_Error (const char *error, ...)\n{\n\tva_list argptr;\n\tchar string[1024];\n\n\tva_start (argptr,error);\n\tvsnprintf (string, sizeof (string), error, argptr);\n\tva_end (argptr);\n\tCOM_WorkerAbort(string);\n\tfprintf(stderr, \"Error: %s\\n\", string);\n\n\tSys_Printf (\"Quake Error: %s\\n\", string);\n\n#if SDL_VERSION_ATLEAST(2,0,0)\n\tSDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, \"Sys_Error\", string, sdlwindow);\n#endif\n\n\tif (COM_CheckParm(\"-crashonerror\"))\n\t\t*(int*)-3 = 0;\n\n\tHost_Shutdown ();\n\texit (1);\n}\n\nvoid Sys_RecentServer(char *command, char *target, char *title, char *desc)\n{\n}\n\n\n#if defined(_WIN32)\n#include <windows.h>\n#include <wincrypt.h>\nqboolean Sys_RandomBytes(qbyte *string, int len)\n{\n    HCRYPTPROV  prov;\n\n    if(!CryptAcquireContext( &prov, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT))\n    {\n        return false;\n    }\n\n    if(!CryptGenRandom(prov, len, (BYTE *)string))\n    {\n        CryptReleaseContext( prov, 0);\n        return false;\n    }\n    CryptReleaseContext(prov, 0);\n    return true;\n}\n#elif defined(__linux__) || defined(BSD)\nqboolean Sys_RandomBytes(qbyte *string, int len)\n{\n\tqboolean res = false;\n\tint fd = open(\"/dev/urandom\", 0);\n\tif (fd != -1)\n\t{\n\t\tres = (read(fd, string, len) == len);\n\t\tclose(fd);\n\t}\n\treturn res;\n}\n#else\nqboolean Sys_RandomBytes(qbyte *string, int len)\n{\n\treturn false;\n}\n#endif\n\nstatic void ApplyColour(unsigned int chrflags)\n{\n//on win32, SDL usually redirected stdout to a file (as it won't get printed anyway.\n//win32 doesn't do ascii escapes, and text editors like to show the gibberish too, so just don't bother emitting any.\n#ifndef _WIN32\n\tstatic const int ansiremap[8] = {0, 4, 2, 6, 1, 5, 3, 7};\n\tstatic int oldflags = CON_WHITEMASK;\n\tint bg, fg;\n\n\tif (oldflags == chrflags)\n\t\treturn;\n\toldflags = chrflags;\n\n\tprintf(\"\\e[0;\"); // reset\n\n\tif (chrflags & CON_BLINKTEXT)\n\t\tprintf(\"5;\"); // set blink\n\n\tbg = (chrflags & CON_BGMASK) >> CON_BGSHIFT;\n\tfg = (chrflags & CON_FGMASK) >> CON_FGSHIFT;\n\n\t// don't handle intensive bit for background\n\t// as terminals differ too much in displaying \\e[1;7;3?m\n\tbg &= 0x7;\n\n\tif (chrflags & CON_NONCLEARBG)\n\t{\n\t\tif (fg & 0x8) // intensive bit set for foreground\n\t\t{\n\t\t\tprintf(\"1;\"); // set bold/intensity ansi flag\n\t\t\tfg &= 0x7; // strip intensive bit\n\t\t}\n\n\t\t// set foreground and background colors\n\t\tprintf(\"3%i;4%im\", ansiremap[fg], ansiremap[bg]);\n\t}\n\telse\n\t{\n\t\tswitch(fg)\n\t\t{\n\t\t//to get around wierd defaults (like a white background) we have these special hacks for colours 0 and 7\n\t\tcase COLOR_BLACK:\n\t\t\tprintf(\"7m\"); // set inverse\n\t\t\tbreak;\n\t\tcase COLOR_GREY:\n\t\t\tprintf(\"1;30m\"); // treat as dark grey\n\t\t\tbreak;\n\t\tcase COLOR_WHITE:\n\t\t\tprintf(\"m\"); // set nothing else\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tif (fg & 0x8) // intensive bit set for foreground\n\t\t\t{\n\t\t\t\tprintf(\"1;\"); // set bold/intensity ansi flag\n\t\t\t\tfg &= 0x7; // strip intensive bit\n\t\t\t}\n\n\t\t\tprintf(\"3%im\", ansiremap[fg]); // set foreground\n\t\t\tbreak;\n\t\t}\n\t}\n#endif\n}\n//#include <wchar.h>\nvoid Sys_Printf (char *fmt, ...)\n{\n\tva_list\t\targptr;\n\tchar\t\ttext[2048];\n\tconchar_t\tctext[2048];\n\tconchar_t       *c, *e;\n\twchar_t\t\tw;\n\tunsigned int codeflags, codepoint;\n\n//\tif (nostdout)\n//\t\treturn;\n\n\tva_start (argptr,fmt);\n\tvsnprintf (text,sizeof(text)-1, fmt,argptr);\n\tva_end (argptr);\n\n#ifdef SUBSERVERS\n\tif (SSV_IsSubServer())\n\t{\n\t\tif (SSV_PrintToMaster(text))\n\t\t\treturn;\n\t}\n#endif\n\n\tif (strlen(text) > sizeof(text))\n\t\tSys_Error(\"memory overwrite in Sys_Printf\");\n\n\te = COM_ParseFunString(CON_WHITEMASK, text, ctext, sizeof(ctext), false);\n\n\tfor (c = ctext; c < e; )\n\t{\n\t\tc = Font_Decode(c, &codeflags, &codepoint);\n\t\tif (codeflags & CON_HIDDEN)\n\t\t\tcontinue;\n\n\t\tif (codepoint == '\\n' && (codeflags&CON_NONCLEARBG))\n\t\t\tcodeflags &= CON_WHITEMASK;\n\t\tApplyColour(codeflags);\n\t\tw = codepoint;\n\t\tif (w >= 0xe000 && w < 0xe100)\n\t\t{\n\t\t\t/*not all quake chars are ascii compatible, so map those control chars to safe ones so we don't mess up anyone's xterm*/\n\t\t\tif ((w & 0x7f) > 0x20)\n\t\t\t\tputc(w&0x7f, stdout);\n\t\t\telse if (w & 0x80)\n\t\t\t{\n\t\t\t\tstatic char tab[32] = \"---#@.@@@@ # >..\" \"[]0123456789.---\";\n\t\t\t\tputc(tab[w&31], stdout);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tstatic char tab[32] = \".####.#### # >..\" \"[]0123456789.---\";\n\t\t\t\tputc(tab[w&31], stdout);\n\t\t\t}\n\t\t}\n\t\telse if (w < ' ' && w != '\\t' && w != '\\r' && w != '\\n')\n\t\t\tputc('?', stdout);\t//don't let anyone print escape codes or other things that could crash an xterm.\n\t\telse\n\t\t{\n\t\t\t/*putwc doesn't like me. force it in utf8*/\n\t\t\tif (w >= 0x80)\n\t\t\t{\n\t\t\t\tif (w > 0x800)\n\t\t\t\t{\n\t\t\t\t\tputc(0xe0 | ((w>>12)&0x0f), stdout);\n\t\t\t\t\tputc(0x80 | ((w>>6)&0x3f), stdout);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tputc(0xc0 | ((w>>6)&0x1f), stdout);\n\t\t\t\tputc(0x80 | (w&0x3f), stdout);\n\t\t\t}\n\t\t\telse\n\t\t\t\tputc(w, stdout);\n\t\t}\n\t}\n\n\tApplyColour(CON_WHITEMASK);\n\tfflush(stdout);\n}\n\n//#define QCLOCK(enumname,readablename,query,frequency,initcode) //query must have t=\n\n#if SDL_VERSION_ATLEAST(3,0,0)\n\t#define CLOCKDEF_SDL_TICKSNS QCLOCK(TICKSNS, \"ticksns\", t=SDL_GetTicksNS(), SDL_NS_PER_SECOND,;)\n#else\n\t#define CLOCKDEF_SDL_TICKSNS\n#endif\n#if SDL_VERSION_ATLEAST(2,0,18) && !SDL_VERSION_ATLEAST(3,0,0)\t//less wrappy... still terrible precision.\n\t#define CLOCKDEF_SDL_TICKS QCLOCK(TICKS, \"ticks\", t=SDL_GetTicks64(), 1000,;)\n#else\n\t#define CLOCKDEF_SDL_TICKS QCLOCK(TICKS, \"ticks\", t=SDL_GetTicks(), 1000,;)\n#endif\n\nstatic quint64_t sdlperf_freq;\n#define CLOCKDEF_SDL_PERF  QCLOCK(PERF,  \"perf\", t=SDL_GetPerformanceCounter(), sdlperf_freq,sdlperf_freq=SDL_GetPerformanceFrequency())\n\n#if defined(CLOCK_MONOTONIC) && (_POSIX_C_SOURCE >= 200112L)\n\t#define CLOCKDEF_LINUX_MONOTONIC QCLOCK(MONOTONIC, \"monotonic\", {\t\\\n\t\t\t\tstruct timespec ts;\t\\\n\t\t\t\tclock_gettime(CLOCK_MONOTONIC, &ts);\t\\\n\t\t\t\tt = (ts.tv_sec*(quint64_t)1000000000) + ts.tv_nsec;\t\\\n\t\t\t}, 1000000000,;)\n#else\n\t#define CLOCKDEF_LINUX_MONOTONIC\n#endif\n\n#if defined(CLOCK_REALTIME) && (_POSIX_C_SOURCE >= 200112L)\n\t#define CLOCKDEF_LINUX_REALTIME QCLOCK(REALTIME, \"realtime\", {\t\\\n\t\t\t\tstruct timespec ts;\t\\\n\t\t\t\tclock_gettime(CLOCK_REALTIME, &ts);\t\\\n\t\t\t\tt = (ts.tv_sec*(quint64_t)1000000000) + ts.tv_nsec;\t\\\n\t\t\t}, 1000000000,;)\n#else\n\t#define CLOCKDEF_LINUX_REALTIME\n#endif\n\n#if _POSIX_C_SOURCE >= 200112L\n\t#include <sys/time.h>\n\t#define CLOCKDEF_POSIX_GTOD QCLOCK(GTOD, \"gettimeofday\", {\t\\\n\t\t\tstruct timeval tp;\t\\\n\t\t\tgettimeofday(&tp, NULL);\t\\\n\t\t\tt = tp.tv_sec*(quint64_t)1000000 + tp.tv_usec;\t\\\n\t\t}, 1000000,;)\n#else\n\t#define CLOCKDEF_POSIX_GTOD\n#endif\n\n#define CLOCKDEF_ALL\t\\\n\t\t\t\t\t CLOCKDEF_SDL_TICKSNS /*sdl*/\\\n\t\t\t\t\t CLOCKDEF_LINUX_MONOTONIC CLOCKDEF_LINUX_REALTIME /*linux-specific clocks*/\\\n\t\t\t\t\t CLOCKDEF_SDL_PERF CLOCKDEF_SDL_TICKS /*sdl clocks*/\\\n\t\t\t\t\t CLOCKDEF_POSIX_GTOD /*posix clocks*/\n\nstatic quint64_t timer_basetime;    //used by all clocks to bias them to starting at 0\nstatic quint64_t timer_nobacksies;  //used by all clocks to bias them to starting at 0\nstatic void Sys_ClockType_Changed(cvar_t *var, char *oldval);\n#define QCLOCK(e,n,q,f,i) n\"\\n\"\nstatic cvar_t sys_clocktype = CVARFCD(\"sys_clocktype\", \"\", CVAR_NOTFROMSERVER, Sys_ClockType_Changed, \"Controls which system clock to base timings from.\\nAvailable Clocks:\\n\"\n\tCLOCKDEF_ALL);\n#undef QCLOCK\n\nstatic enum\n{\n#define QCLOCK(e,n,q,f,i) CLOCKID_##e,\n\tCLOCKDEF_ALL\n#undef QCLOCK\n\tCLOCKID_INVALID\n} timer_clocktype=CLOCKID_INVALID;\nstatic quint64_t Sys_GetClock(quint64_t *freq)\n{\n\tquint64_t t;\n\tswitch(timer_clocktype)\n\t{\n\tdefault:\n#define QCLOCK(e,n,q,f,i) case CLOCKID_##e: *freq = f; q; break;\n\tCLOCKDEF_ALL\n#undef QCLOCK\n\t}\n\tt -= timer_basetime;\n\tif (t < timer_nobacksies && t-*freq>timer_nobacksies)\n\t{\n\t\tquint64_t back = timer_nobacksies-t;\n\t\tt = timer_nobacksies;\n\t\tif (back > 0)//*freq/1000)\t//warn if the clock went backwards by more than 1ms.\n\t\t\tCon_Printf(\"Warning: clock went backwards by %g secs.\\n\", back/(double)*freq);\n\t\tif (back > 0x8000000 && *freq==1000)\n\t\t{\t//32bit value wrapped?\n\t\t\ttimer_basetime -= 0x8000000;\n\t\t\tt += 0x8000000;\n\t\t}\n\t}\n\telse\n\t\ttimer_nobacksies = t;\n\treturn t;\n}\nstatic void Sys_ClockType_Changed(cvar_t *var, char *oldval)\n{\n\tint newtype;\n\n#define QCLOCK(e,n,q,f,i) if (!strcasecmp(var->string, n)) newtype = CLOCKID_##e; else\n\tCLOCKDEF_ALL\n#undef QCLOCK\n\t{\n\t\tquint64_t freq;\n\t\tif (!*var->string || !strcasecmp(var->string, \"auto\"))\n\t\t\t;\n\t\telse\n\t\t{\n\t\t\tCon_Printf(\"%s: Unknown clock name %s available clocks\", var->name, var->string);\n#define QCLOCK(e,n,q,f,i) Con_Printf(\", %s\", n);\n\tCLOCKDEF_ALL\n#undef QCLOCK\n\t\t\tCon_Printf(\"\\n\");\n\t\t}\n\n\t\t//look for any that work.\n\t\tfor(newtype = 0; ; newtype++)\n\t\t{\n\t\t\tswitch(newtype)\n\t\t\t{\n\t\t\tdefault:\n\t\t\t\tfreq = 0;\n\t\t\t\tSys_Error(\"No usable clocks\");\n#define QCLOCK(e,n,q,f,i) case CLOCKID_##e: freq = f; break;\n\tCLOCKDEF_ALL\n#undef QCLOCK\n\t\t\t}\n\t\t\tif (freq)\n\t\t\t\tbreak;\t//this clock seems usable.\n\t\t}\n\t}\n\n\tif (newtype != timer_clocktype)\n\t{\n\t\tquint64_t oldtime, oldfreq;\n\t\tquint64_t newtime, newfreq;\n\n\t\toldtime = Sys_GetClock(&oldfreq);\n\t\ttimer_clocktype = newtype;\n\t\ttimer_nobacksies = timer_basetime = 0;\t//make sure we get the raw clock.\n\t\tnewtime = Sys_GetClock(&newfreq);\n\n\t\ttimer_basetime = newtime - (newfreq * ((long double)oldtime) / oldfreq);\n\t\ttimer_nobacksies = newtime - timer_basetime;\n\n\t\tSys_GetClock(&newfreq);\n\t}\n}\nstatic void Sys_InitClock(void)\n{\n\tquint64_t freq;\n\n\tCvar_Register(&sys_clocktype, \"System vars\");\n\n#define QCLOCK(e,n,q,f,i) i;\n\tCLOCKDEF_ALL\n#undef QCLOCK\n\n\t//calibrate it, and apply.\n\ttimer_clocktype = CLOCKID_INVALID;\n\tSys_ClockType_Changed(&sys_clocktype, NULL);\n\ttimer_basetime = timer_nobacksies = 0;\n\ttimer_basetime = Sys_GetClock(&freq);\n\ttimer_nobacksies = 0;\n}\ndouble Sys_DoubleTime (void)\n{\n\tquint64_t denum, num = Sys_GetClock(&denum);\n\treturn num / (long double)denum;\n}\nunsigned int Sys_Milliseconds (void)\n{\n\tquint64_t denum, num = Sys_GetClock(&denum);\n\tnum *= 1000;\n\treturn num / denum;\n}\n\n//create a directory\nvoid Sys_mkdir (const char *path)\n{\n#if SDL_VERSION_ATLEAST(3,0,0)\n\tSDL_CreateDirectory(path);\n#elif WIN32\n\t_mkdir (path);\n#else\n\t//user, group, others\n\tmkdir (path, 0755);\t//WARNING: DO NOT RUN AS ROOT!\n#endif\n}\n\nqboolean Sys_rmdir (const char *path)\n{\n#if SDL_VERSION_ATLEAST(3,0,0)\n\treturn SDL_RemovePath(path);\t//dir or file\n#else\n\tint ret;\n#if WIN32\n\tret = _rmdir (path);\n#else\n\tret = rmdir (path);\n#endif\n\tif (ret == 0)\n\t\treturn true;\n//\tif (errno == ENOENT)\n//\t\treturn true;\n\treturn false;\n#endif\n}\n\n//unlink a file\nqboolean Sys_remove (const char *path)\n{\n#if SDL_VERSION_ATLEAST(3,0,0)\n\treturn SDL_RemovePath(path);\t//file or dir\n#else\n\tremove(path);\n\treturn true;\n#endif\n}\n\nqboolean Sys_Rename (const char *oldfname, const char *newfname)\n{\n#if SDL_VERSION_ATLEAST(3,0,0)\n\treturn SDL_RenamePath(oldfname, newfname);\n#else\n\treturn !rename(oldfname, newfname);\n#endif\n}\n\n#ifndef _WIN32\n#if _POSIX_C_SOURCE >= 200112L\n\t#include <sys/statvfs.h>\n#endif\nqboolean Sys_GetFreeDiskSpace(const char *path, quint64_t *freespace)\n{\n#if _POSIX_C_SOURCE >= 200112L\n\t//posix 2001\n\tstruct statvfs inf;\n\tif(0==statvfs(path, &inf))\n\t{\n\t\t*freespace = inf.f_bsize*(quint64_t)inf.f_bavail;\n\t\treturn true;\n\t}\n#endif\n\treturn false;\n}\n#endif\n\n//someone used the 'quit' command\nvoid Sys_Quit (void)\n{\n\tHost_Shutdown();\n\n#if !SDL_VERSION_ATLEAST(3, 0, 0)\n\tSDL_free((char*)host_parms.binarydir);\n#endif\n\thost_parms.binarydir = NULL;\n\n\texit (0);\n}\n\n#if SDL_VERSION_ATLEAST(3,0,0)&&!defined(_WIN32)\nint Sys_EnumerateFiles (const char *gpath, const char *match, int (*func)(const char *, qofs_t, time_t modtime, void *, searchpathfuncs_t *), void *parm, searchpathfuncs_t *spath)\n{\t//SDL_GlobDirectory does seem to do wildcards in parent directories properly, at least on loonix.\n\t//on wine it seems to do dumb slow shit ('*' crossing the '\\\\' directory seperator and enumerating the entire FS tree).\n\tchar file[MAX_OSPATH];\n\tint i, count;\n\tSDL_PathInfo st;\n\tchar **list = SDL_GlobDirectory(gpath, match, SDL_GLOB_CASEINSENSITIVE, &count);\n\tfor(i = 0; i < count; i++)\n\t{\n\t\tif (list[i][0] != '.' && !strstr(list[i], \"/.\"))\t//unfortunately seems to include hidden files (we don't like creating hidden files by mistake, so avoid warnings later by refusing to find them here).\n\t\t{\n\t\t\tQ_snprintfz(file, sizeof(file), \"%s/%s\", gpath, list[i]);\n\n\t\t\tif (SDL_GetPathInfo(file, &st))\n\t\t\t{\n\t\t\t\tQ_snprintfz(file, sizeof(file), \"%s%s\", list[i], (st.type==SDL_PATHTYPE_DIRECTORY)?\"/\":\"\");\n\n\t\t\t\tif (!func(file, st.size, st.modify_time, parm, spath))\n\t\t\t\t{\n\t\t\t\t\tSDL_free(list);\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tSDL_free(list);\n\treturn true;\n}\n\n//enumerate the files in a directory (of both gpath and match - match may not contain ..)\n//calls the callback for each one until the callback returns 0\n//SDL2 provides no file enumeration facilities.\n#elif defined(_WIN32)\nstatic int Sys_EnumerateFiles2 (const char *match, int matchstart, int neststart, int (QDECL *func)(const char *fname, qofs_t fsize, time_t mtime, void *parm, searchpathfuncs_t *spath), void *parm, searchpathfuncs_t *spath)\n{\n\tqboolean go;\n\n\tHANDLE r;\n\tWIN32_FIND_DATAW fd;\n\tint nest = neststart;\t//neststart refers to just after a /\n\tqboolean wild = false;\n\n\twhile(match[nest] && match[nest] != '/')\n\t{\n\t\tif (match[nest] == '?' || match[nest] == '*')\n\t\t\twild = true;\n\t\tnest++;\n\t}\n\tif (match[nest] == '/')\n\t{\n\t\tchar submatch[MAX_OSPATH];\n\t\tchar tmproot[MAX_OSPATH];\n\n\t\tif (!wild)\n\t\t\treturn Sys_EnumerateFiles2(match, matchstart, nest+1, func, parm, spath);\n\n\t\tif (nest-neststart+1> MAX_OSPATH)\n\t\t\treturn 1;\n\t\tmemcpy(submatch, match+neststart, nest - neststart);\n\t\tsubmatch[nest - neststart] = 0;\n\t\tnest++;\n\n\t\tif (neststart+4 > MAX_OSPATH)\n\t\t\treturn 1;\n\t\tmemcpy(tmproot, match, neststart);\n\t\tstrcpy(tmproot+neststart, \"*.*\");\n\n\t\t{\n\t\t\twchar_t wroot[MAX_OSPATH];\n\t\t\tr = FindFirstFileExW(widen(wroot, sizeof(wroot), tmproot), FindExInfoStandard, &fd, FindExSearchNameMatch, NULL, 0);\n\t\t}\n\t\tstrcpy(tmproot+neststart, \"\");\n\t\tif (r==(HANDLE)-1)\n\t\t\treturn 1;\n\t\tgo = true;\n\t\tdo\n\t\t{\n\t\t\tchar utf8[MAX_OSPATH];\n\t\t\tchar file[MAX_OSPATH];\n\t\t\tnarrowen(utf8, sizeof(utf8), fd.cFileName);\n\t\t\tif (*utf8 == '.');\t//don't ever find files with a name starting with '.'\n\t\t\telse if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)\t//is a directory\n\t\t\t{\n\t\t\t\tif (wildcmp(submatch, utf8))\n\t\t\t\t{\n\t\t\t\t\tint newnest;\n\t\t\t\t\tif (strlen(tmproot) + strlen(utf8) + strlen(match+nest) + 2 < MAX_OSPATH)\n\t\t\t\t\t{\n\t\t\t\t\t\tQ_snprintfz(file, sizeof(file), \"%s%s/\", tmproot, utf8);\n\t\t\t\t\t\tnewnest = strlen(file);\n\t\t\t\t\t\tstrcpy(file+newnest, match+nest);\n\t\t\t\t\t\tgo = Sys_EnumerateFiles2(file, matchstart, newnest, func, parm, spath);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} while(FindNextFileW(r, &fd) && go);\n\t\tFindClose(r);\n\t}\n\telse\n\t{\n\t\tconst char *submatch = match + neststart;\n\t\tchar tmproot[MAX_OSPATH];\n\n\t\tif (neststart+4 > MAX_OSPATH)\n\t\t\treturn 1;\n\t\tmemcpy(tmproot, match, neststart);\n\t\tstrcpy(tmproot+neststart, \"*.*\");\n\n\t\t{\n\t\t\twchar_t wroot[MAX_OSPATH];\n\t\t\tr = FindFirstFileExW(widen(wroot, sizeof(wroot), tmproot), FindExInfoStandard, &fd, FindExSearchNameMatch, NULL, 0);\n\t\t}\n\t\tstrcpy(tmproot+neststart, \"\");\n\t\tif (r==(HANDLE)-1)\n\t\t\treturn 1;\n\t\tgo = true;\n\t\tdo\n\t\t{\n\t\t\tchar utf8[MAX_OSPATH];\n\t\t\tchar file[MAX_OSPATH];\n\n\t\t\tnarrowen(utf8, sizeof(utf8), fd.cFileName);\n\t\t\tif (*utf8 == '.')\n\t\t\t\t;\t//don't ever find files with a name starting with '.' (includes .. and . directories, and unix hidden files)\n\t\t\telse if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)\t//is a directory\n\t\t\t{\n\t\t\t\tif (wildcmp(submatch, utf8))\n\t\t\t\t{\n\t\t\t\t\tif (strlen(tmproot+matchstart) + strlen(utf8) + 2 < MAX_OSPATH)\n\t\t\t\t\t{\n\t\t\t\t\t\tQ_snprintfz(file, sizeof(file), \"%s%s/\", tmproot+matchstart, utf8);\n\t\t\t\t\t\tgo = func(file, qofs_Make(fd.nFileSizeLow, fd.nFileSizeHigh), 0, parm, spath);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (wildcmp(submatch, utf8))\n\t\t\t\t{\n\t\t\t\t\tif (strlen(tmproot+matchstart) + strlen(utf8) + 1 < MAX_OSPATH)\n\t\t\t\t\t{\n\t\t\t\t\t\tQ_snprintfz(file, sizeof(file), \"%s%s\", tmproot+matchstart, utf8);\n\t\t\t\t\t\tgo = func(file, qofs_Make(fd.nFileSizeLow, fd.nFileSizeHigh), 0, parm, spath);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} while(FindNextFileW(r, &fd) && go);\n\t\tFindClose(r);\n\t}\n\treturn go;\n}\nint Sys_EnumerateFiles (const char *gpath, const char *match, int (QDECL *func)(const char *fname, qofs_t fsize, time_t modtime, void *parm, searchpathfuncs_t *spath), void *parm, searchpathfuncs_t *spath)\n{\n\tchar fullmatch[MAX_OSPATH];\n\tint start;\n\tif (strlen(gpath) + strlen(match) + 2 > MAX_OSPATH)\n\t\treturn 1;\n\n\tstrcpy(fullmatch, gpath);\n\tstart = strlen(fullmatch);\n\tif (start && fullmatch[start-1] != '/')\n\t\tfullmatch[start++] = '/';\n\tfullmatch[start] = 0;\n\tstrcat(fullmatch, match);\n\treturn Sys_EnumerateFiles2(fullmatch, start, start, func, parm, spath);\n}\n#elif defined(linux) || defined(__unix__) || defined(__MACH__) || defined(__HAIKU__)\n#include <dirent.h>\n#include <errno.h>\nstatic int Sys_EnumerateFiles2 (const char *truepath, int apathofs, const char *match, int (*func)(const char *, qofs_t, time_t modtime, void *, searchpathfuncs_t *), void *parm, searchpathfuncs_t *spath)\n{\n\tDIR *dir;\n\tchar file[MAX_OSPATH];\n\tconst char *s;\n\tstruct dirent *ent;\n\tstruct stat st;\n\tconst char *wild;\n\tconst char *apath = truepath+apathofs;\n\n\t//if there's a * in a system path, then we need to scan its parent directory to figure out what the * expands to.\n\t//we can just recurse quicklyish to try to handle it.\n\twild = strchr(apath, '*');\n\tif (!wild)\n\t\twild = strchr(apath, '?');\n\tif (wild)\n\t{\n\t\tchar subdir[MAX_OSPATH];\n\t\tfor (s = wild+1; *s && *s != '/'; s++)\n\t\t\t;\n\t\twhile (wild > truepath)\n\t\t{\n\t\t\tif (*(wild-1) == '/')\n\t\t\t\tbreak;\n\t\t\twild--;\n\t\t}\n\t\tmemcpy(file, truepath, wild-truepath);\n\t\tfile[wild-truepath] = 0;\n\n\t\tdir = opendir(file);\n\t\tmemcpy(subdir, wild, s-wild);\n\t\tsubdir[s-wild] = 0;\n\t\tif (dir)\n\t\t{\n\t\t\tdo\n\t\t\t{\n\t\t\t\tent = readdir(dir);\n\t\t\t\tif (!ent)\n\t\t\t\t\tbreak;\n\t\t\t\tif (*ent->d_name != '.')\n\t\t\t\t{\n#ifdef _DIRENT_HAVE_D_TYPE\n\t\t\t\t\tif (ent->d_type != DT_DIR && ent->d_type != DT_UNKNOWN)\n\t\t\t\t\t\tcontinue;\n#endif\n\t\t\t\t\tif (wildcmp(subdir, ent->d_name))\n\t\t\t\t\t{\n\t\t\t\t\t\tmemcpy(file, truepath, wild-truepath);\n\t\t\t\t\t\tQ_snprintfz(file+(wild-truepath), sizeof(file)-(wild-truepath), \"%s%s\", ent->d_name, s);\n\t\t\t\t\t\tif (!Sys_EnumerateFiles2(file, apathofs, match, func, parm, spath))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tclosedir(dir);\n\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} while(1);\n\t\t\tclosedir(dir);\n\t\t}\n\t\treturn true;\n\t}\n\n\n\tdir = opendir(truepath);\n\tif (!dir)\n\t{\n\t\tCon_DLPrintf((errno==ENOENT)?2:1, \"Failed to open dir %s\\n\", truepath);\n\t\treturn true;\n\t}\n\tdo\n\t{\n\t\tent = readdir(dir);\n\t\tif (!ent)\n\t\t\tbreak;\n\t\tif (*ent->d_name != '.')\n\t\t{\n\t\t\tif (wildcmp(match, ent->d_name))\n\t\t\t{\n\t\t\t\tQ_snprintfz(file, sizeof(file), \"%s/%s\", truepath, ent->d_name);\n\n\t\t\t\tif (stat(file, &st) == 0 || lstat(file, &st) == 0)\n\t\t\t\t{\n\t\t\t\t\tQ_snprintfz(file, sizeof(file), \"%s%s%s\", apath, ent->d_name, S_ISDIR(st.st_mode)?\"/\":\"\");\n\n\t\t\t\t\tif (!func(file, st.st_size, st.st_mtime, parm, spath))\n\t\t\t\t\t{\n//\t\t\t\t\t\tCon_DPrintf(\"giving up on search after finding %s\\n\", file);\n\t\t\t\t\t\tclosedir(dir);\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t}\n//\t\t\t\telse\n//\t\t\t\t\tprintf(\"Stat failed for \\\"%s\\\"\\n\", file);\n\t\t\t}\n\t\t}\n\t} while(1);\n\tclosedir(dir);\n\treturn true;\n}\nint Sys_EnumerateFiles (const char *gpath, const char *match, int (*func)(const char *, qofs_t, time_t modtime, void *, searchpathfuncs_t *), void *parm, searchpathfuncs_t *spath)\n{\n\tchar apath[MAX_OSPATH];\n\tchar truepath[MAX_OSPATH];\n\tchar *s;\n\n\tif (!gpath)\n\t\tgpath = \"\";\n\t*apath = '\\0';\n\n\tQ_strncpyz(apath, match, sizeof(apath));\n\tfor (s = apath+strlen(apath)-1; s >= apath; s--)\n\t{\n\t\tif (*s == '/')\n\t\t{\n\t\t\ts[1] = '\\0';\n\t\t\tmatch += s - apath+1;\n\t\t\tbreak;\n\t\t}\n\t}\n\tif (s < apath)\t//didn't find a '/'\n\t\t*apath = '\\0';\n\n\tQ_snprintfz(truepath, sizeof(truepath), \"%s/%s\", gpath, apath);\n\treturn Sys_EnumerateFiles2(truepath, strlen(gpath)+1, match, func, parm, spath);\n}\n\n#else\nint Sys_EnumerateFiles (const char *gpath, const char *match, int (*func)(const char *, qofs_t, time_t mtime, void *, void *), void *parm, void *spath)\n{\n\tCon_Printf(\"Warning: Sys_EnumerateFiles not implemented\\n\");\n\treturn false;\n}\n#endif\n\n#if SDL_VERSION_ATLEAST(3,0,0)\n#include \"fs.h\"\n\ntypedef struct\n{\n\tvfsfile_t f;\n\tSDL_IOStream *s;\n} sdlfile_t;\nstatic int IOF_ReadBytes(struct vfsfile_s *file, void *buffer, int bytestoread)\n{\n\tsdlfile_t *f = (void*)file;\n\tint r = SDL_ReadIO(f->s, buffer, bytestoread);\n\tif (!r && bytestoread)\n\t{\n\t\tSDL_IOStatus s = SDL_GetIOStatus(f->s);\n\t\tif (s == SDL_IO_STATUS_NOT_READY)\n\t\t\treturn 0;\n\t\telse if (s == SDL_IO_STATUS_EOF)\n\t\t\treturn VFS_ERROR_EOF;\n\t\telse\n\t\t\treturn VFS_ERROR_UNSPECIFIED;\n\t}\n\treturn r;\n}\nstatic int IOF_WriteBytes(struct vfsfile_s *file,const  void *buffer, int bytestowrite)\n{\n\tsdlfile_t *f = (void*)file;\n\tint r = SDL_WriteIO(f->s, buffer, bytestowrite);\n\tif (!r && bytestowrite)\n\t{\n\t\tSDL_IOStatus s = SDL_GetIOStatus(f->s);\n\t\tif (s == SDL_IO_STATUS_NOT_READY)\n\t\t\treturn 0;\n\t\telse if (s == SDL_IO_STATUS_EOF)\n\t\t\treturn VFS_ERROR_EOF;\n\t\telse\n\t\t\treturn VFS_ERROR_UNSPECIFIED;\n\t}\n\treturn r;\n}\nstatic qboolean IOF_Seek(struct vfsfile_s *file, qofs_t pos)\n{\n\tsdlfile_t *f = (void*)file;\n\tSint64 r = SDL_SeekIO(f->s, pos, SDL_IO_SEEK_SET);\n\treturn r == pos;\n}\nstatic qofs_t IOF_Tell(struct vfsfile_s *file)\n{\n\tsdlfile_t *f = (void*)file;\n\treturn SDL_TellIO(f->s);\n}\nstatic qofs_t IOF_GetLen(struct vfsfile_s *file)\n{\n\tsdlfile_t *f = (void*)file;\n\treturn SDL_GetIOSize(f->s);\n}\nstatic qboolean IOF_Close(struct vfsfile_s *file)\n{\n\tsdlfile_t *f = (void*)file;\n\tqboolean ret = SDL_CloseIO(f->s);\n\tZ_Free(f);\n\treturn ret;\n}\nstatic void IOF_Flush(struct vfsfile_s *file)\n{\n\tsdlfile_t *f = (void*)file;\n\tSDL_FlushIO(f->s);\n}\nstatic vfsfile_t *IOF_Setup(sdlfile_t *r, SDL_IOStream *f)\n{\n\tif (f)\n\t{\n\t\tr->s = f;\n\t\tr->f.ReadBytes = IOF_ReadBytes;\n\t\tr->f.WriteBytes = IOF_WriteBytes;\n\t\tr->f.Seek = IOF_Seek;\n\t\tr->f.Tell = IOF_Tell;\n\t\tr->f.GetLen = IOF_GetLen;\n\t\tr->f.Close = IOF_Close;\n\t\tr->f.Flush = IOF_Flush;\n\t\tr->f.seekstyle = SS_SEEKABLE;\n\t\treturn &r->f;\n\t}\n\telse\n\t\tZ_Free(r);\t//oops.\n\treturn NULL;\n}\nstatic vfsfile_t *IOF_AsVFS(SDL_IOStream *f)\n{\n\treturn IOF_Setup(Z_Malloc(sizeof(sdlfile_t)), f);\n}\nstatic vfsfile_t *VFSSDL_Open(const char *fname, char *mode)\n{\n\treturn IOF_AsVFS(SDL_IOFromFile(fname, mode));\n}\n\nstatic void SDLCALL FS_OpenFilePicked (void *userdata, const char * const *filelist, int filter)\n{\n\tint i;\n\tfor(i = 0; filelist && filelist[i]; i++)\n\t{\n\t\tvfsfile_t *f = VFSSDL_Open(filelist[i], \"rb\");\t//using this instead of VFSOS_Open, to handle platform weirdness here.\n\t\tif (f)\n\t\t\tHost_RunFile(filelist[i],strlen(filelist[i]), f);\n\t}\n}\nstatic void FS_OpenFilePicker_f(void)\n{\n\tSDL_DialogFileFilter exts[] = {\n\t\t{\"Packages\", \"pk3;pk4;pak\"},\n\t\t{\"Demos\", \"mvd.gz;mvd;qwd.gz;qwd;dem.gz;dem\"},\n\t\t{\"Maps\", \"bsp.gz;bsp;map\"},\n\t\t{\"Mods\", \"fmf\"},\n\t\t{\"All Files\", \"*\"},\n\t};\n\tSDL_ShowOpenFileDialog(FS_OpenFilePicked, NULL, sdlwindow, exts, countof(exts), NULL, true);\n}\n\n#if 0\ntypedef struct\n{\n\tstruct searchpathfuncs_s pub;\n\tSDL_Storage *s;\n\tqatomic32_t refs;\n} sdlsstore_t;\nstatic void Sys_Store_Close(searchpathfuncs_t *handle)\n{\n\tsdlsstore_t *s = (sdlsstore_t*)handle;\n\tif (FTE_Atomic32_Dec(&s->refs) > 0)\n\t\treturn;\t//still open somewhere else.\n\tSDL_CloseStorage(s->s);\n\tZ_Free(s);\n}\nstatic void Sys_Store_AddReference(searchpathfuncs_t *handle)\n{\n\tsdlsstore_t *s = (sdlsstore_t*)handle;\n\tFTE_Atomic32_Inc(&s->refs);\n}\nstatic void Sys_Store_GetPathDetails(searchpathfuncs_t *handle, char *outdetails, size_t sizeofdetails)\n{\n\tQ_snprintfz(outdetails, sizeofdetails, \"b0rked\");\n}\nstruct buildhashctx_s\n{\n\tsdlsstore_t *searchpath;\n\tint depth;\n\tvoid (QDECL *cb)(int depth, const char *fname, fsbucket_t *filehandle, void *pathhandle);\n};\nstatic SDL_EnumerationResult Sys_Store_BuildHash_cb(void *userdata, const char *dirname, const char *fname)\n{\n\tstruct buildhashctx_s *ctx = userdata;\n\tSDL_PathInfo info;\n\tchar fullname[MAX_OSPATH];\n\tif (Q_snprintfz(fullname, sizeof(fullname), \"%s%s\", dirname, fname))\n\t\treturn SDL_ENUM_CONTINUE;\n\tif (SDL_GetStoragePathInfo(ctx->searchpath->s, fullname, &info))\n\t{\n\t\tif (info.type == SDL_PATHTYPE_DIRECTORY)\n\t\t\tSDL_EnumerateStorageDirectory(ctx->searchpath->s, fullname, Sys_Store_BuildHash_cb, &ctx);\n\t\telse if (info.type == SDL_PATHTYPE_FILE)\n\t\t\tctx->cb(ctx->depth, fullname, NULL, ctx->searchpath);\n\t}\n\treturn SDL_ENUM_CONTINUE;\n}\nstatic void Sys_Store_BuildHash(searchpathfuncs_t *handle, int depth, void (QDECL *FS_AddFileHash)(int depth, const char *fname, fsbucket_t *filehandle, void *pathhandle))\n{\n\tsdlsstore_t *s = (sdlsstore_t*)handle;\n\tstruct buildhashctx_s ctx = {s, depth, FS_AddFileHash};\n\tSDL_EnumerateStorageDirectory(s->s, NULL, Sys_Store_BuildHash_cb, &ctx);\n}\nstatic unsigned int Sys_Store_FindFile(searchpathfuncs_t *handle, flocation_t *loc, const char *name, void *hashedresult)\n{\t//create a loc that can be OpenVFSed with a \"r\"/\"r+\"/etc mode.\n\tsdlsstore_t *s = (sdlsstore_t*)handle;\n\tif (SDL_GetStorageFileSize(s->s, name, &loc->len))\n\t{\n\t\tloc->offset = 0;\n\t\tloc->fhandle = NULL;\t//not really useful to us, there's no freeing etc.\n\t\t*loc->rawname = 0;\t//must have a leading null, because we don't know its actual path and can't pass it to system apis to bypass the quake filesystem.\n\t\tQ_strncpyz(loc->rawname+1, name, sizeof(loc->rawname)-1); //but we do need to keep track of the name for when it is actually opened.\n\t\treturn FF_FOUND;\n\t}\n\treturn FF_NOTFOUND;\n}\nstatic qboolean Sys_Store_CreateFile(searchpathfuncs_t *handle, flocation_t *loc, const char *name)\n{\t//create a loc that can be OpenVFSed with a \"w\" mode, even if it didn't exist before.\n\tsdlsstore_t *s = (sdlsstore_t*)handle;\n\tSDL_PathInfo info;\n\tif (SDL_GetStoragePathInfo(s->s, name, &info) && (info.type == SDL_PATHTYPE_NONE || info.type == SDL_PATHTYPE_FILE))\t//create or overwrite is fine.\n\t{\n\t\tloc->offset = 0;\n\t\tloc->fhandle = NULL;\t//not really useful to us, there's no freeing etc.\n\t\t*loc->rawname = 0;\t//must have a leading null, because we don't know its actual path and can't pass it to system apis to bypass the quake filesystem.\n\t\tQ_strncpyz(loc->rawname+1, name, sizeof(loc->rawname)-1); //but we do need to keep track of the name for when it is actually opened.\n\t\treturn true;\n\t}\n\treturn false;\n}\nstatic void Sys_Store_ReadFile(searchpathfuncs_t *handle, flocation_t *loc, char *buffer)\n{\n\tvfsfile_t *f;\n\tf = handle->OpenVFS(handle, loc, \"rb\");\n\tif (!f)\t//err...\n\t\treturn;\n\tVFS_READ(f, buffer, loc->len);\n\tVFS_CLOSE(f);\n}\nstatic int Sys_Store_EnumerateFiles(searchpathfuncs_t *handle, const char *match, int (QDECL *func)(const char *fname, qofs_t fsize, time_t mtime, void *parm, searchpathfuncs_t *spath), void *parm)\n{\n\t//seems to accept `*/*/*` just fine.\n\tsdlsstore_t *s = (sdlsstore_t*)handle;\n\tint i, count;\n\tchar **list = SDL_GlobStorageDirectory(s->s, NULL, match, SDL_GLOB_CASEINSENSITIVE, &count);\n\tSDL_PathInfo info;\n\tint r = 1;\n\tfor(i = 0; i < count; i++)\n\t{\n\t\tif (*list[i] == '.')\n\t\t\tcontinue; //no unix hidden files...\n\t\tif (SDL_GetStoragePathInfo(s->s, list[i], &info))\n\t\t{\n\t\t\tif (!func(list[i], info.size, info.modify_time, parm, handle))\n\t\t\t{\n\t\t\t\tr = 0;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\tSDL_free(list);\n\treturn r;\n}\nstatic qboolean Sys_Store_FileStat(searchpathfuncs_t *handle, flocation_t *loc, time_t *mtime)\n{\n\tsdlsstore_t *s = (sdlsstore_t*)handle;\n\tSDL_PathInfo info;\n\tif (SDL_GetStoragePathInfo(s->s, loc->rawname+1, &info))\n\t{\n\t\t*mtime = info.modify_time;\n\t\treturn true;\n\t}\n\treturn false;\n}\nstatic qboolean Sys_Store_RenameFile(searchpathfuncs_t *handle, const char *oldname, const char *newname)\t//returns true on success, false if source doesn't exist, or if dest does (cached locs may refer to either new or old name).\n{\n\tsdlsstore_t *s = (sdlsstore_t*)handle;\n\treturn SDL_RenameStoragePath(s->s, oldname, newname);\n}\nstatic qboolean Sys_Store_RemoveFile(searchpathfuncs_t *handle, const char *filename)\t//returns true on success, false if it wasn't found or is readonly.\n{\n\tsdlsstore_t *s = (sdlsstore_t*)handle;\n\treturn SDL_RemoveStoragePath(s->s, filename);\n}\nstatic vfsfile_t *\tSys_Store_OpenVFS(searchpathfuncs_t *handle, flocation_t *loc, const char *mode)\n{\n\tsdlsstore_t *s = (sdlsstore_t*)handle;\n\tvfsfile_t *f = NULL;\n\tvoid *tmp;\n\n\twhile(*mode)\n\t{\n\t\tswitch(*mode++)\n\t\t{\n\t\tcase 'r':\t//fine\n\t\tcase 'b':\t//always binary\n\t\tcase 't':\t//irrelevant\n\t\tcase 'p':\t//persistent hint, safe to ignore (should really use the user store)\n\t\t\tbreak;\n\t\tcase 'w':\t//not writable\n\t\tcase '+':\t//not updatable\n\t\tcase 'a':\t//not appendable (easier though)\n\t\tdefault:\t//unknown.\n\t\t\treturn NULL;\n\t\t}\n\t}\n\n\ttmp = BZ_Malloc(sizeof(*f)+loc->len);\n\tif (SDL_ReadStorageFile(s->s, loc->rawname+1, tmp+1, loc->len))\n\t\tf = IOF_Setup(tmp, SDL_IOFromConstMem(tmp+1, loc->len));\t//allocates it as a single block.\n\telse\n\t\tBZ_Free(tmp);\n\n\treturn f;\n}\nstatic struct searchpathfuncs_s *Sys_OpenStore(SDL_Storage *s)\n{\n\tsdlsstore_t *n;\n\tif (!s)\t//oops?\n\t\treturn NULL;\n\n\t//this is bad.\n\twhile (!SDL_StorageReady(s))\n\t\tSys_Sleep(0.001);\n\n\tn = Z_Malloc(sizeof(*n));\n\tif (!n)\n\t{\n\t\tSDL_CloseStorage(s);\n\t\treturn NULL;\n\t}\n\telse\n\t{\n\t\tn->refs\t= 1;\n\t\tn->s\t= s;\n\t\tn->pub.fsver\t\t\t= FSVER;\n\t\tn->pub.ClosePath\t\t= Sys_Store_Close;\n\t\tn->pub.AddReference\t\t= Sys_Store_AddReference; //needs an extra close call...\n\t\tn->pub.GetPathDetails\t= Sys_Store_GetPathDetails;\n\t\tn->pub.BuildHash\t\t= Sys_Store_BuildHash;\n\t\tn->pub.FindFile\t\t\t= Sys_Store_FindFile;\n\t\tn->pub.ReadFile\t\t\t= Sys_Store_ReadFile;\n\t\tn->pub.EnumerateFiles\t= Sys_Store_EnumerateFiles;\n//\t\tn->pub.GeneratePureCRC\t= Sys_Store_GeneratePureCRC;\n\t\tn->pub.OpenVFS\t\t\t= Sys_Store_OpenVFS;\n//\t\tn->pub.PollChanges\t\t= Sys_Store_PollChanges;\n\t\tn->pub.FileStat\t\t\t= Sys_Store_FileStat;\n\t\tn->pub.CreateFile\t\t= Sys_Store_CreateFile;\n\t\tn->pub.RenameFile\t\t= Sys_Store_RenameFile;\n\t\tn->pub.RemoveFile\t\t= Sys_Store_RemoveFile;\n\n\t\treturn &n->pub;\n\t}\n}\nstruct searchpathfuncs_s *Sys_OpenTitleStore(void)\n{\n\treturn Sys_OpenStore(SDL_OpenTitleStorage(NULL, 0));\n}\n/*struct searchpathfuncs_s *Sys_OpenUserStore(void)\n{\n\treturn Sys_OpenStore(SDL_OpenUserStorage(org, app, 0));\n}*/\n#endif\n#endif\n\n//blink window if possible (it's not)\nvoid Sys_ServerActivity(void)\n{\n#if SDL_VERSION_ATLEAST(2,0,16)\n\tSDL_FlashWindow(sdlwindow, SDL_FLASH_BRIEFLY);\n#endif\n}\n\nvoid Sys_CloseLibrary(dllhandle_t *lib)\n{\n\tSDL_UnloadObject((void*)lib);\n}\ndllhandle_t *Sys_LoadLibrary(const char *name, dllfunction_t *funcs)\n{\n\tint i;\n\tvoid *lib;\n\n\tlib = SDL_LoadObject(name);\n\tif (!lib)\n\t{\n\t\tchar libpath[MAX_OSPATH];\n\t\tQ_snprintfz(libpath, sizeof(libpath), \"%s\"ARCH_DL_POSTFIX, name);\n\t\tlib = SDL_LoadObject(libpath);\n\t}\n\tif (!lib)\n\t\treturn NULL;\n\n\tif (funcs)\n\t{\n\t\tfor (i = 0; funcs[i].name; i++)\n\t\t{\n\t\t\t*funcs[i].funcptr = SDL_LoadFunction(lib, funcs[i].name);\n\t\t\tif (!*funcs[i].funcptr)\n\t\t\t\tbreak;\n\t\t}\n\t\tif (funcs[i].name)\n\t\t{\n\t\t\tSys_CloseLibrary((dllhandle_t*)lib);\n\t\t\tlib = NULL;\n\t\t}\n\t}\n\n\treturn (dllhandle_t*)lib;\n}\nvoid *Sys_GetAddressForName(dllhandle_t *module, const char *exportname)\n{\n\tif (!module)\n\t\treturn NULL;\n\treturn SDL_LoadFunction((void *)module, exportname);\n}\n\n\n\n\n\n//used to see if a file exists or not.\nint\tSys_FileTime (char *path)\n{\n\tFILE\t*f;\n\t\n\tf = fopen(path, \"rb\");\n\tif (f)\n\t{\n\t\tfclose(f);\n\t\treturn 1;\n\t}\n\t\n\treturn -1;\n}\n\nchar *Sys_URIScheme_NeedsRegistering(void)\n{   //no support.\n    return NULL;\n}\n\nvoid Sys_Init(void)\n{\n//TODO:\tSDL_SetHint(SDL_HINT_IME_IMPLEMENTED_UI, \"composition,candidates\");\n\n#if SDL_VERSION_ATLEAST(3,0,0)\n\tCmd_AddCommandD(\"sys_openfile\", FS_OpenFilePicker_f,\t\"Select a file to open/install/etc.\");\n\n\tSDL_Init(SDL_INIT_EVENTS);\n#else\n\tSDL_Init(SDL_INIT_TIMER | SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE);\n#endif\n\tSys_InitClock();\n}\nvoid Sys_Shutdown(void)\n{\n\tSDL_Quit();\n}\n\n\n\nint VARGS Sys_DebugLog(char *file, char *fmt, ...)\n{\n\tFILE *fd;\n\tva_list argptr; \n\tstatic char data[1024];\n    \n\tva_start(argptr, fmt);\n\tvsnprintf(data, sizeof(data)-1, fmt, argptr);\n\tva_end(argptr);\n\n#if defined(CRAZYDEBUGGING) && CRAZYDEBUGGING > 1\n\t{\n\t\tstatic int sock;\n\t\tif (!sock)\n\t\t{\n\t\t\tstruct sockaddr_in sa;\n\t\t\tnetadr_t na;\n\t\t\tint _true = true;\n\t\t\tint listip;\n\t\t\tlistip = COM_CheckParm(\"-debugip\");\n\t\t\tNET_StringToAdr(listip?com_argv[listip+1]:\"127.0.0.1\", &na);\n\t\t\tNetadrToSockadr(&na, (struct sockaddr_qstorage*)&sa);\n\t\t\tsa.sin_port = htons(10000);\n\t\t\tsock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);\n\t\t\tif (-1==connect(sock, (struct sockaddr*)&sa, sizeof(sa)))\n\t\t\t\tSys_Error(\"Couldn't send debug log lines\\n\");\n\t\t\tsetsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char*)&_true, sizeof(_true));\n\t\t}\n\t\tsend(sock, data, strlen(data), 0);\n\t}\n#endif\n\tfd = fopen(file, \"wt\");\n\tif (fd)\n\t{\n\t\tfwrite(data, 1, strlen(data), fd);\n\t\tfclose(fd);\n\t\treturn 0;\n\t}\n\treturn 1;\n};\n\n\nstatic qbyte nostdin;\n#ifdef _WIN32\n#ifndef WIN32_LEAN_AND_MEAN\n#define WIN32_LEAN_AND_MEAN\n#endif\n\n#include <windows.h>\n#endif\n\n#ifdef _WIN32\nstatic qboolean gotconsole;\nstatic HANDLE con_stdin;\nqboolean Sys_InitTerminal(void)\n{\n\tgotconsole = AllocConsole();\t//failure is okay if we already had one.\n\tcon_stdin = CreateFile(\"CONIN$\", GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);\n\tfreopen(\"CON\", \"w\", stdout);\t//unfuck the stdout too.\n    return true;\n}\nchar *Sys_ConsoleInput(void)\n{\t//NET_Sleep won't sleep on this handle, so expect it to be sluggish.\n\tDWORD numevents, i;\n\twhile (GetNumberOfConsoleInputEvents(con_stdin, &numevents) && numevents>0)\n\t{\n\t\tstatic char text[256];\n\t\tstatic int textlen;\n\t\tINPUT_RECORD event[1];\t//longer might miss presses. especially with delays.\n\t\tif (numevents > countof(event))\n\t\t\tnumevents = countof(event);\n\t\tif (ReadConsoleInputW(con_stdin, event, numevents, &numevents))\n\t\t{\n\t\t\tfor(i = 0; i < numevents; i++)\n\t\t\t{\n\t\t\t\tif (event[i].EventType == KEY_EVENT && event[i].Event.KeyEvent.bKeyDown && event[i].Event.KeyEvent.uChar.UnicodeChar)\n\t\t\t\t{\n\t\t\t\t\tif (textlen >= countof(text))\n\t\t\t\t\t\ttextlen = countof(text)-1;\t//don't overflow.\n\t\t\t\t\ttext[textlen] = event[i].Event.KeyEvent.uChar.UnicodeChar;\n\t\t\t\t\tif (text[textlen] == '\\r')\n\t\t\t\t\t{\n\t\t\t\t\t\ttext[textlen] = 0;\t//caller will add its own \\n\n\t\t\t\t\t\tprintf(\"\\r]%s\\n\", text);\n\t\t\t\t\t\ttextlen = 0; //start from the start\n\t\t\t\t\t\tfflush(stdout);\n\t\t\t\t\t\treturn text;\n\t\t\t\t\t}\n\t\t\t\t\ttextlen++;\n\t\t\t\t\ttext[textlen] = 0;\t//caller will add its own \\n\n\t\t\t\t\tprintf(\"\\r]%s\", text);\n\t\t\t\t\tfflush(stdout);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn NULL;\n}\nvoid Sys_CloseTerminal (void)\n{\n\tif (gotconsole)\n\t{\t//don't close our initial one. don't detach that way.\n\t\tFreeConsole();\n\t\tgotconsole = false;\n\t}\n\tif (con_stdin)\n\t{\n\t\tCloseHandle(con_stdin);\n\t\tcon_stdin = NULL;\n\t}\n}\n#elif defined(__unix__) && !defined(__ANDROID__)\n#include <errno.h>\nqboolean Sys_InitTerminal(void)\n{\n\tif (COM_CheckParm(\"-nostdin\"))\n\t\tnostdin = true;\n\tif (nostdin)\n\t\treturn true;\t//they okayed it, let it start regardless.\n\tif (isatty(STDIN_FILENO))\n\t\treturn true;\n\tCon_Printf(CON_WARNING\"Sys_InitTerminal: not started from a tty\\n\"); //no easy way to kill it otherwise.\n\treturn false;\n}\nchar *Sys_ConsoleInput(void)\n{\n\tstatic char text[256];\n\tchar *nl;\n\n\tif (nostdin)\n\t\treturn NULL;\n\n#if defined(__linux__) && defined(_DEBUG)\n\t{\n\t\tint fl = fcntl (STDIN_FILENO, F_GETFL, 0);\n\t\tif (!(fl & FNDELAY))\n\t\t{\n\t\t\tfcntl(STDIN_FILENO, F_SETFL, fl | FNDELAY);\n//          Sys_Printf(CON_WARNING \"stdin flags became blocking - gdb bug?\\n\");\n\t\t}\n\t}\n#endif\n\n\tif (!fgets(text, sizeof(text), stdin))\n\t{\n\t\tif (errno == EIO)\n\t\t{\n\t\t\tSys_Printf(CON_WARNING \"Backgrounded, ignoring stdin\\n\");\n\t\t\tnostdin |= 2;\n\t\t}\n\t\treturn NULL;\n\t}\n\tnl = strchr(text, '\\n');\n\tif (!nl)    //err? wut?\n\t\treturn NULL;\n\t*nl = 0;\n\n\treturn text;\n}\nvoid Sys_CloseTerminal (void)\n{\n}\n#else\nqboolean Sys_InitTerminal(void)\n{\n\tCon_Printf(CON_WARNING\"Sys_InitTerminal: not implemented in this build.\\n\");\n    return false;\t//Sys_ConsoleInput cannot work, so return false here.\n}\nchar *Sys_ConsoleInput(void)\n{\n\treturn NULL;\n}\nvoid Sys_CloseTerminal (void)\n{\n}\n#endif\n\n#if SDL_VERSION_ATLEAST(3,0,0)\n#define SDL_MAIN_USE_CALLBACKS\n#include <SDL3/SDL_main.h>\n\nvoid SDL_AppQuit(void *appstate, SDL_AppResult result)\n{\n\tHost_Shutdown();\n}\n\nstatic double oldtime;\nSDL_AppResult SDL_AppInit(void **appstate, int argc, char **argv)\n{\n#else\nint QDECL main(int argc, char **argv)\n{\n\tdouble time, newtime, oldtime, sleeptime;\n#endif\n\tquakeparms_t\tparms;\n\n\tmemset(&parms, 0, sizeof(parms));\n\n#if SDL_VERSION_ATLEAST(3, 0, 0)\n\tparms.basedir = SDL_GetCurrentDirectory();\n#else\n\tparms.basedir = \"./\";\n#endif\n\tparms.binarydir = SDL_GetBasePath();\n\n\tparms.argc = argc;\n\tparms.argv = (const char**)argv;\n#ifdef CONFIG_MANIFEST_TEXT\n\tparms.manifest = CONFIG_MANIFEST_TEXT;\n#endif\n\n#if !defined(WIN32)\n\tfcntl(0, F_SETFL, fcntl (0, F_GETFL, 0) | O_NDELAY);\n#endif\n\n\tCOM_InitArgv (parms.argc, parms.argv);\n\n#ifdef SUBSERVERS\n\tif (COM_CheckParm(\"-clusterslave\"))\n\t{\n\t\tisDedicated = isClusterSlave = true;\n\t\tSSV_SetupControlPipe(Sys_GetStdInOutStream(), false);\n\t}\n#endif\n\n\tTL_InitLanguages(parms.basedir);\n\n\tif (parms.binarydir)\n\t\tSys_Printf(\"Binary is located at \\\"%s\\\"\\n\", parms.binarydir);\n\n#ifdef HAVE_CLIENT\n\tif (COM_CheckParm (\"-dedicated\"))\n\t\tisDedicated = true;\n\tif (isDedicated)    //compleate denial to switch to anything else - many of the client structures are not initialized.\n\t{\n\t\tfloat delay;\n\n\t\tSV_Init (&parms);\n\n\t\tif (!Sys_InitTerminal())\n\t\t\tCon_Printf(CON_WARNING\"Stdin unavailable\\n\");\n\n\t\tdelay = SV_Frame();\n\t\twhile (1)\n\t\t{\n\t\t\tif (!isDedicated)\n\t\t\t\tSys_Error(\"Dedicated was cleared\");\n\t\t\tNET_Sleep(delay, false);\n\t\t\tdelay = SV_Frame();\n\t\t}\n\t\treturn EXIT_FAILURE;\n\t}\n#endif\n\n\n\tHost_Init (&parms);\n\n\toldtime = Sys_DoubleTime ();\n#if SDL_VERSION_ATLEAST(3,0,0)\n\treturn SDL_APP_CONTINUE;\n}\n\nSDL_AppResult SDL_AppIterate(void *appstate)\n{\n\tdouble time, newtime, sleeptime;\n#else\n\n//client console should now be initialized.\n\n    /* main window message loop */\n\twhile (1)\n\t{\n#endif\n\n#ifndef CLIENTONLY\n\t\tif (isDedicated)\n\t\t{\n\t\t// find time passed since last cycle\n\t\t\tnewtime = Sys_DoubleTime ();\n\t\t\ttime = newtime - oldtime;\n\t\t\toldtime = newtime;\n\t\t\t\n\t\t\tsleeptime = SV_Frame();\n\t\t\tNET_Sleep(sleeptime, false);\n\t\t}\n\t\telse\n#endif\n\t\t{\n\n\t// yield the CPU for a little while when paused, minimized, or not the focus\n#if SDL_VERSION_ATLEAST(3,0,0)\n\t\t\t//using the callbacks. let sdl deal with it. also sleeping sucks on windows anyway.\n#elif SDL_VERSION_ATLEAST(2,0,0)\n\t\t\tif (!vid.activeapp)\n\t\t\t\tSDL_Delay(1);\n#else\n\t\t\tif (!(SDL_GetAppState() & SDL_APPINPUTFOCUS))\n\t\t\t\tSDL_Delay(1);\n#endif\n\n\t\t\tnewtime = Sys_DoubleTime ();\n\t\t\ttime = newtime - oldtime;\n\t\t\tsleeptime = Host_Frame (time);\n\t\t\toldtime = newtime;\n\n\t\t\tif (sleeptime)\n\t\t\t\tSys_Sleep(sleeptime);\n\t\t}\n#if SDL_VERSION_ATLEAST(3,0,0)\n\treturn SDL_APP_CONTINUE;\n}\n#else\n\t}\n\n\treturn 0;\n}\n\n#ifdef _MSC_VER\n//our version of sdl_main.lib, which doesn't fight c runtimes.\nint WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)\n{\n\tint argc;\n\tint i, l, c;\n\tLPWSTR *argvw;\n\tchar **argv;\n\tchar utf8arg[1024];\n\targvw = CommandLineToArgvW(GetCommandLineW(), &argc);\n\targv = malloc(argc * sizeof(char*));\n\tfor (i = 0; i < argc; i++)\n\t{\n\t\tfor(l = 0, c = 0; argvw[i][l]; l++)\n\t\t\tc += utf8_encode(utf8arg+c, argvw[i][l], sizeof(utf8arg) - c-1);\n\t\tutf8arg[c] = 0;\n\t\targv[i] = strdup(utf8arg);\n\t}\n\treturn main(argc, argv);\n}\n#endif\n#endif\n\nqboolean Sys_GetDesktopParameters(int *width, int *height, int *bpp, int *refreshrate)\n{\n#if SDL_VERSION_ATLEAST(3,0,0)\n\tconst SDL_DisplayMode *mode;\n\tCOM_AssertMainThread(\"SDL_GetDesktopDisplayMode\");\n\tmode = SDL_GetDesktopDisplayMode(SDL_GetPrimaryDisplay());\n\tif (mode)\n\t{\n\t\t*width = mode->w;\n\t\t*height = mode->h;\n\t\t*bpp = (SDL_PIXELTYPE(mode->format) == SDL_PIXELTYPE_PACKED32)?32:16;\n\t\t*refreshrate = mode->refresh_rate;\n\t\treturn true;\n\t}\n#elif SDL_VERSION_ATLEAST(2,0,0)\n\tSDL_DisplayMode mode;\n\tif (!SDL_GetDesktopDisplayMode(0, &mode))\n\t{\n\t\t*width = mode.w;\n\t\t*height = mode.h;\n\t\t*bpp = (SDL_PIXELTYPE(mode.format) == SDL_PIXELTYPE_PACKED32)?32:16;\n\t\t*refreshrate = mode.refresh_rate;\n\t\treturn true;\n\t}\n#endif\n\treturn false;\n}\n\n\n\n#if SDL_VERSION_ATLEAST(2,0,0)\t//probably could include 1.3\n#if !SDL_VERSION_ATLEAST(3,0,0)\t//probably could include 1.3\n#include <SDL_clipboard.h>\n#endif\nvoid Sys_Clipboard_PasteText(clipboardtype_t cbt, void (*callback)(void *cb, const char *utf8), void *ctx)\n{\n\tchar *txt;\n#if SDL_VERSION_ATLEAST(2,26,0)\n\tif (cbt == CBT_SELECTION)\n\t\ttxt = SDL_GetPrimarySelectionText();\n\telse\n#endif\n\t\ttxt = SDL_GetClipboardText();\n\tcallback(ctx, txt);\n\tSDL_free(txt);\n}\nvoid Sys_SaveClipboard(clipboardtype_t cbt, const char *text)\n{\n#if SDL_VERSION_ATLEAST(2,26,0)\n\tif (cbt == CBT_SELECTION)\n\t\tSDL_SetPrimarySelectionText(text);\n\telse\n#endif\n\t\tSDL_SetClipboardText(text);\n}\n#else\nstatic char *clipboard_buffer;\nvoid Sys_Clipboard_PasteText(clipboardtype_t cbt, void (*callback)(void *cb, const char *utf8), void *ctx)\n{\n\tcallback(ctx, clipboard_buffer);\n}\nvoid Sys_SaveClipboard(clipboardtype_t cbt, const char *text)\n{\n\tfree(clipboard_buffer);\n\tclipboard_buffer = strdup(text);\n}\n#endif\n\n#ifdef MULTITHREAD\n\n/*Thread management stuff*/\n#if SDL_VERSION_ATLEAST(3,0,0)\nstatic SDL_ThreadID mainthread;\n#else\n#define SDL_GetCurrentThreadID SDL_ThreadID\nstatic SDL_threadID mainthread;\n#endif\nstatic SDL_TLSID tls_threadinfo;\nstruct threadinfo_s\n{\n\tjmp_buf jmpbuf;\t//so we can actually abort our threads...\n\tint (*threadfunc)(void *args);\n\tvoid *args;\n};\n\nvoid Sys_ThreadsInit(void)\n{\n\tmainthread = SDL_GetCurrentThreadID();\n}\nqboolean Sys_IsThread(void *thread)\n{\n\treturn SDL_GetThreadID(thread) == SDL_GetCurrentThreadID();\n}\nqboolean Sys_IsMainThread(void)\n{\n\treturn mainthread == SDL_GetCurrentThreadID();\n}\nvoid Sys_ThreadAbort(void)\n{\n\t//SDL_KillThread(NULL) got removed... so we have to do things the shitty way.\n#if SDL_VERSION_ATLEAST(3,0,0)\n\tstruct threadinfo_s *tinfo = SDL_GetTLS(&tls_threadinfo);\n#else\n\tstruct threadinfo_s *tinfo = SDL_TLSGet(tls_threadinfo);\n#endif\n\tif (!tinfo)\n\t{\t//erk... not created via Sys_CreateThread?!?\n\t\tSDL_Delay(10*1000);\n\t\texit(0);\n\t}\n\tlongjmp(tinfo->jmpbuf, 1);\n}\nstatic int FTESDLThread(void *args)\n{\t//all for Sys_ThreadAbort\n\tstruct threadinfo_s *tinfo = args;\n\tint r;\n#if SDL_VERSION_ATLEAST(3,0,0)\n\tSDL_SetTLS(&tls_threadinfo, tinfo, NULL);\n#else\n\tSDL_TLSSet(tls_threadinfo, tinfo, NULL);\n#endif\n\tif (setjmp(tinfo->jmpbuf))\n\t\tr = 0;\t//aborted...\n\telse\n\t\tr = tinfo->threadfunc(tinfo->args);\n#if SDL_VERSION_ATLEAST(3,0,0)\n\tSDL_SetTLS(&tls_threadinfo, NULL, NULL);\n#else\n\tSDL_TLSSet(tls_threadinfo, NULL, NULL);\n#endif\n\tZ_Free(tinfo);\n\treturn r;\n}\nvoid *Sys_CreateThread(char *name, int (*func)(void *), void *args, int priority, int stacksize)\n{\n\t// SDL threads do not support setting thread stack size\n\tstruct threadinfo_s *tinfo = Z_Malloc(sizeof(*tinfo));\n\ttinfo->threadfunc = func;\n\ttinfo->args = args;\n#if SDL_MAJOR_VERSION >= 2\n\treturn (void *)SDL_CreateThread(FTESDLThread, name, tinfo);\n#else\n\treturn (void *)SDL_CreateThread(FTESDLThread, tinfo);\n#endif\n}\n\nvoid Sys_WaitOnThread(void *thread)\n{\n\tSDL_WaitThread((SDL_Thread *)thread, NULL);\n}\n\n\n/* Mutex calls */\n#if SDL_VERSION_ATLEAST(2,0,0)\nvoid *Sys_CreateMutex(void)\n{\n\treturn (void *)SDL_CreateMutex();\n}\n\nqboolean Sys_TryLockMutex(void *mutex)\n{\n\treturn !SDL_TryLockMutex(mutex);\n}\n\nqboolean Sys_LockMutex(void *mutex)\n{\n#if SDL_VERSION_ATLEAST(3,0,0)\n\tSDL_LockMutex(mutex);\n\treturn true;\n#else\n\treturn !SDL_LockMutex(mutex);\n#endif\n}\n\nqboolean Sys_UnlockMutex(void *mutex)\n{\n#if SDL_VERSION_ATLEAST(3,0,0)\n\tSDL_UnlockMutex(mutex);\n\treturn true;\n#else\n\treturn !SDL_UnlockMutex(mutex);\n#endif\n}\n\nvoid Sys_DestroyMutex(void *mutex)\n{\n\tSDL_DestroyMutex(mutex);\n}\n#else\n// SDL mutexes don't have try-locks for mutexes in the spec so we stick with 1-value semaphores\nvoid *Sys_CreateMutex(void)\n{\n\treturn (void *)SDL_CreateSemaphore(1);\n}\n\nqboolean Sys_TryLockMutex(void *mutex)\n{\n\treturn !SDL_SemTryWait(mutex);\n}\n\nqboolean Sys_LockMutex(void *mutex)\n{\n\treturn !SDL_SemWait(mutex);\n}\n\nqboolean Sys_UnlockMutex(void *mutex)\n{\n\treturn !SDL_SemPost(mutex);\n}\n\nvoid Sys_DestroyMutex(void *mutex)\n{\n\tSDL_DestroySemaphore(mutex);\n}\n#endif\n\n#if SDL_VERSION_ATLEAST(3,0,0)\n/* Conditional wait calls */\ntypedef struct condvar_s\n{\n\tSDL_Mutex *mutex;\n\tSDL_Condition *cond;\n} condvar_t;\n\nvoid *Sys_CreateConditional(void)\n{\n\tcondvar_t *condv;\n\tSDL_Mutex *mutex;\n\tSDL_Condition *cond;\n\n\tcondv = (condvar_t *)malloc(sizeof(condvar_t));\n\tif (!condv)\n\t\treturn NULL;\n\n\tmutex = SDL_CreateMutex();\n\tcond = SDL_CreateCondition();\n\n\tif (mutex)\n\t{\n\t\tif (cond)\n\t\t{\n\t\t\tcondv->cond = cond;\n\t\t\tcondv->mutex = mutex;\n\n\t\t\treturn (void *)condv;\n\t\t}\n\t\telse\n\t\t\tSDL_DestroyMutex(mutex);\n\t}\n\n\tfree(condv);\n\treturn NULL;\n}\n\nqboolean Sys_LockConditional(void *condv)\n{\n\tSDL_LockMutex(((condvar_t *)condv)->mutex);\n\treturn true;\n}\n\nqboolean Sys_UnlockConditional(void *condv)\n{\n\tSDL_UnlockMutex(((condvar_t *)condv)->mutex);\n\treturn true;\n}\n\nqboolean Sys_ConditionWait(void *condv)\n{\n\tSDL_WaitCondition(((condvar_t *)condv)->cond, ((condvar_t *)condv)->mutex);\n\treturn true;\n}\n\nqboolean Sys_ConditionSignal(void *condv)\n{\n\tSDL_SignalCondition(((condvar_t *)condv)->cond);\n\treturn true;\n}\n\nqboolean Sys_ConditionBroadcast(void *condv)\n{\n\tSDL_BroadcastCondition(((condvar_t *)condv)->cond);\n\treturn true;\n}\n\nvoid Sys_DestroyConditional(void *condv)\n{\n\tcondvar_t *cv = (condvar_t *)condv;\n\n\tSDL_DestroyCondition(cv->cond);\n\tSDL_DestroyMutex(cv->mutex);\n\tfree(cv);\n}\n#else\n/* Conditional wait calls */\ntypedef struct condvar_s\n{\n\tSDL_mutex *mutex;\n\tSDL_cond *cond;\n} condvar_t;\n\nvoid *Sys_CreateConditional(void)\n{\n\tcondvar_t *condv;\n\tSDL_mutex *mutex;\n\tSDL_cond *cond;\n\t\n\tcondv = (condvar_t *)malloc(sizeof(condvar_t));\n\tif (!condv)\n\t\treturn NULL;\n\t\t\n\tmutex = SDL_CreateMutex();\n\tcond = SDL_CreateCond();\n\t\n\tif (mutex)\n\t{\n\t\tif (cond)\n\t\t{\n\t\t\tcondv->cond = cond;\n\t\t\tcondv->mutex = mutex;\n\t\t\n\t\t\treturn (void *)condv;\n\t\t}\n\t\telse\n\t\t\tSDL_DestroyMutex(mutex);\n\t}\n\t\n\tfree(condv);\n\treturn NULL;\t\n}\n\nqboolean Sys_LockConditional(void *condv)\n{\n\treturn !SDL_mutexP(((condvar_t *)condv)->mutex);\n}\n\nqboolean Sys_UnlockConditional(void *condv)\n{\n\treturn !SDL_mutexV(((condvar_t *)condv)->mutex);\n}\n\nqboolean Sys_ConditionWait(void *condv)\n{\n\treturn !SDL_CondWait(((condvar_t *)condv)->cond, ((condvar_t *)condv)->mutex);\n}\n\nqboolean Sys_ConditionSignal(void *condv)\n{\n\treturn !SDL_CondSignal(((condvar_t *)condv)->cond);\n}\n\nqboolean Sys_ConditionBroadcast(void *condv)\n{\n\treturn !SDL_CondBroadcast(((condvar_t *)condv)->cond);\n}\n\nvoid Sys_DestroyConditional(void *condv)\n{\n\tcondvar_t *cv = (condvar_t *)condv;\n\t\n\tSDL_DestroyCond(cv->cond);\n\tSDL_DestroyMutex(cv->mutex);\n\tfree(cv);\n}\n#endif\n#endif\n\nvoid Sys_Sleep (double seconds)\n{\n\tSDL_Delay(seconds * 1000);\n}\n\n#ifdef WEBCLIENT\nqboolean Sys_RunInstaller(void)\n{       //not implemented\n    return false;\n}\n#endif\n\n#ifdef HAVEAUTOUPDATE\n//returns true if we could sucessfull overwrite the engine binary.\nqboolean Sys_SetUpdatedBinary(const char *fname)\n{\n\treturn false;\n}\n//says whether the system code is able/allowed to overwrite itself.\n//(ie: return false if we don't know the binary name or if its write-protected etc)\nqboolean Sys_EngineMayUpdate(void)\n{\n\treturn false;\t//sorry\n}\n#endif\n\n#ifdef SUBSERVERS\n#include <errno.h>\nstatic int QDECL Sys_StdoutWrite (struct vfsfile_s *file, const void *buffer, int bytestowrite)\n{\n\tssize_t r = write(STDOUT_FILENO, buffer, bytestowrite);\n\tif (r == 0 && bytestowrite)\n\t\treturn -1;\t//eof\n\tif (r < 0)\n\t{\n\t\tint e = errno;\n\t\tif (e == EINTR || e == EAGAIN || e == EWOULDBLOCK)\n\t\t\treturn 0;\n\t}\n\treturn r;\n}\nstatic int QDECL Sys_StdinRead (struct vfsfile_s *file, void *buffer, int bytestoread)\n{\n#ifdef _WIN32\n\tDWORD avail;\n\tHANDLE input = GetStdHandle(STD_INPUT_HANDLE);\n\tif (!PeekNamedPipe(input, NULL, 0, NULL, &avail, NULL))\n\t\treturn -1;\t//some kind of error? EOF? Hangup? just report it as an error.\n\tif (avail)\n\t{\n\t\tif (avail > bytestoread)\n\t\t\tavail = bytestoread;\n\t\tif (!ReadFile(input, buffer, avail, &avail, NULL))\n\t\t\treturn -1;\n\t}\n\treturn avail;\n#else\n\t//standard posix\n\tssize_t r;\n#if defined(__linux__) && defined(_DEBUG)\t//this tends to g\n\tint fl = fcntl (STDIN_FILENO, F_GETFL, 0);\n\tif (!(fl & O_NONBLOCK))\n\t{\n\t\tfcntl(STDIN_FILENO, F_SETFL, fl | O_NONBLOCK);\n\t\tSys_Printf(CON_WARNING \"stdin flags became blocking - gdb bug?\\n\");\n\t}\n#endif\n\tr = read(STDIN_FILENO, buffer, bytestoread);\n\tif (r == 0 && bytestoread)\n\t\treturn VFS_ERROR_EOF;\t//eof\n\tif (r < 0)\n\t{\n\t\tint e = errno;\n\t\tif (e == EINTR || e == EAGAIN || e == EWOULDBLOCK)\n\t\t\treturn 0;\n\t}\n\treturn r;\n#endif\n}\nstatic qboolean QDECL Sys_StdinOutClose(vfsfile_t *fs)\n{\n\tCbuf_AddText(\"\\nquit\\n\", RESTRICT_LOCAL);\n\tZ_Free(fs);\n\treturn true;\n}\nvfsfile_t *Sys_GetStdInOutStream(void)\n{\n\tvfsfile_t *stream = Z_Malloc(sizeof(*stream));\t//not using extra state, so no need to subclass.\n\n\tnostdin = true;\t//detatch from stdin, read no more commands.\n\n#ifdef _WIN32\n#else\n\t//make sure nothing bad is going to happen.\n\tfcntl(STDIN_FILENO, F_SETFL, fcntl(STDIN_FILENO, F_GETFL, 0)|O_NONBLOCK);\n\tfcntl(STDOUT_FILENO, F_SETFL, fcntl(STDOUT_FILENO, F_GETFL, 0)|O_NONBLOCK);\n#endif\n\n\tstream->WriteBytes = Sys_StdoutWrite;\n\tstream->ReadBytes = Sys_StdinRead;\n\tstream->Close = Sys_StdinOutClose;\n\tstream->seekstyle = SS_UNSEEKABLE;\n\treturn stream;\n}\n\ntypedef struct\n{\n\tvfsfile_t pub;\n\tSDL_IOStream *in;\t//attached to the stdin of the process\t(writable)\n\tSDL_IOStream *out;\t//attached to the stdout of the process (readable)\n\tSDL_Process *proc;\n} sdlsubserver_t;\nstatic int Sys_MSV_ReadBytes(struct vfsfile_s *file, void *buffer, int bytestoread)\n{\n\tsdlsubserver_t *f = (void*)file;\n\tint r = SDL_ReadIO(f->out, buffer, bytestoread);\n\tif (!r && bytestoread)\n\t{\n\t\tSDL_IOStatus s = SDL_GetIOStatus(f->out);\n\t\tif (s == SDL_IO_STATUS_NOT_READY)\n\t\t\treturn 0;\n\t\telse if (s == SDL_IO_STATUS_EOF)\n\t\t\treturn VFS_ERROR_EOF;\n\t\telse\n\t\t\treturn VFS_ERROR_UNSPECIFIED;\n\t}\n\treturn r;\n}\nstatic int Sys_MSV_WriteBytes(struct vfsfile_s *file,const  void *buffer, int bytestowrite)\n{\n\tsdlsubserver_t *f = (void*)file;\n\tint r = SDL_WriteIO(f->in, buffer, bytestowrite);\n\tif (!r && bytestowrite)\n\t{\n\t\tSDL_IOStatus s = SDL_GetIOStatus(f->in);\n\t\tif (s == SDL_IO_STATUS_NOT_READY)\n\t\t\treturn 0;\n\t\telse if (s == SDL_IO_STATUS_EOF)\n\t\t\treturn VFS_ERROR_EOF;\n\t\telse\n\t\t\treturn VFS_ERROR_UNSPECIFIED;\n\t}\n\treturn r;\n}\nstatic qboolean Sys_MSV_Close(struct vfsfile_s *file)\n{\n\tsdlsubserver_t *f = (void*)file;\n\tSDL_KillProcess(f->proc, false);\t//let it die on its own...\n\tSDL_DestroyProcess(f->proc);\t//clear up any state (does not force-terminate though!).\n\tZ_Free(f);\n\treturn true;\n}\nvfsfile_t *Sys_ForkServer(void)\n{\n\tsdlsubserver_t *ctx;\n\tconst char *argv[64];\n\tint argc = 0;\n\n\targv[argc++] = com_argv[0];\t//best guess. not exactly reliable.\n\targv[argc++] = \"-clusterslave\";\n\targc += FS_GetManifestArgv(argv+argc, countof(argv)-argc-1);\n\targv[argc++] = NULL;\n\n\tCon_DPrintf(\"Execing %s\\n\", argv[0]);\n\n\tctx = Z_Malloc(sizeof(*ctx));\n\n\tctx->proc = SDL_CreateProcess(argv, true);\n\tctx->in = SDL_GetProcessInput(ctx->proc);\n\tctx->out = SDL_GetProcessOutput(ctx->proc);\n\n\tctx->pub.ReadBytes = Sys_MSV_ReadBytes;\n\tctx->pub.WriteBytes = Sys_MSV_WriteBytes;\n\tctx->pub.Close = Sys_MSV_Close;\n\tctx->pub.seekstyle = SS_UNSEEKABLE;\n\treturn &ctx->pub;\n}\n#endif\n"
  },
  {
    "path": "engine/client/sys_win.c",
    "content": "/*\nCopyright (C) 1996-1997 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n*/\n// sys_win.h\n\n#include \"quakedef.h\"\n\n#include \"winquake.h\"\n#include \"resource.h\"\n#include \"errno.h\"\n#include \"fcntl.h\"\n#include <limits.h>\n#include <conio.h>\n#include <io.h>\n#include <direct.h>\n#include \"pr_common.h\"\n#include \"fs.h\"\n\n#ifdef _MSC_VER\n#define MSVC_SEH\n#endif\n\n//#define RESTARTTEST\n\n#ifdef MULTITHREAD\n#include <process.h>\n#endif\n\n//exports that 3rd-party drivers can see in order to use descrete graphics cards over integrated ones, FOR MORE POWAH!\n__declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001;\n__declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;\t//13.35+\n\nstatic void Sys_InitClock(void);\nstatic void Sys_ClockType_Changed(cvar_t *var, char *oldval);\nstatic void Sys_ClockPrecision_Changed(cvar_t *var, char *oldval);\n\n#ifdef WINRT\t//you're going to need a different sys_ port.\nqboolean isDedicated = false;\nvoid VARGS Sys_Error (const char *error, ...){}\t//eep\nvoid VARGS Sys_Printf (char *fmt, ...){}\t\t//safe, but not ideal (esp for debugging)\nvoid Sys_SendKeyEvents (void){}\t\t\t\t\t//safe, but not ideal\nvoid Sys_ServerActivity(void){}\t\t\t\t\t//empty is safe\nvoid Sys_RecentServer(char *command, char *target, char *title, char *desc){}\t//empty is safe\nqboolean Sys_InitTerminal(void){return false;}\t//failure will break 'setrenderer sv'\nchar *Sys_ConsoleInput (void){return NULL;}\t\t//safe to stub\nvoid Sys_CloseTerminal (void){}\t\t\t\t\t//called when switching from dedicated->non-dedicated\ndllhandle_t *Sys_LoadLibrary(const char *name, dllfunction_t *funcs){return NULL;}\t//can just about get away with it\nvoid *Sys_GetAddressForName(dllhandle_t *module, const char *exportname){return NULL;}\nvoid Sys_CloseLibrary(dllhandle_t *lib){}\t\t//safe, ish\nvoid Sys_Init (void){}\t\t\t\t\t\t\t//safe, stub is fine. used to register system-specific cvars/commands.\nvoid Sys_Shutdown(void){}\t\t\t\t\t\t//safe\nqboolean Sys_RandomBytes(qbyte *string, int len){return false;}\nqboolean Sys_GetDesktopParameters(int *width, int *height, int *bpp, int *refreshrate){return false;}\nvoid INS_Move(void){}\t\t\t\t\t\t\t//safe\nvoid INS_Commands(void){}\t\t\t\t\t\t//safe\nvoid INS_Init(void){}\t\t\t\t\t\t\t//safe. should be xinput2 I guess. nothing else is actually supported. touchscreens don't really count.\nvoid INS_ReInit(void){}\t\t\t\t\t\t\t//safe\nvoid INS_Shutdown(void){}\t\t\t\t\t\t//safe\nvoid INS_UpdateGrabs(int fullscreen, int activeapp){}\t//safe\nvoid *RT_GetCoreWindow(int *width, int *height){return NULL;}\t//I already wrote the d3d11 code, but it needs a window to attach to. you can override width+height by writing to them\nvoid D3D11_DoResize(int newwidth, int newheight);\t//already written, call if resized since getcorewindow\n\nstatic char *clippy;\nvoid Sys_Clipboard_PasteText(clipboardtype_t cbt, void (*callback)(void *cb, char *utf8), void *ctx)\n{\n\tcallback(ctx, clippy);\n}\nvoid Sys_SaveClipboard(clipboardtype_t cbt, char *text)\n{\n\tif (cbt != CBT_CLIPBOARD)\n\t\treturn;\t//don't copy on mere selection. windows users won't expect it.\n\tZ_Free(clippy);\n\tclippy = Z_StrDup(text);\n}\n\nstatic LARGE_INTEGER timestart, timefreq;\nstatic qboolean timeinited = false;\nunsigned int Sys_Milliseconds(void)\n{\n\tLARGE_INTEGER cur, diff;\n\tif (!timeinited)\n\t{\n\t\ttimeinited = true;\n\t\tQueryPerformanceFrequency(&timefreq); \n\t\tQueryPerformanceCounter(&timestart);\n\t}\n\tQueryPerformanceCounter(&cur);\n\tdiff.QuadPart = cur.QuadPart - timestart.QuadPart;\n\tdiff.QuadPart *= 1000;\n\tdiff.QuadPart /= timefreq.QuadPart;\n\treturn diff.QuadPart;\n}\ndouble Sys_DoubleTime (void)\n{\n\tLARGE_INTEGER cur, diff;\n\tif (!timeinited)\n\t{\n\t\ttimeinited = true;\n\t\tQueryPerformanceFrequency(&timefreq); \n\t\tQueryPerformanceCounter(&timestart);\n\t}\n\tQueryPerformanceCounter(&cur);\n\tdiff.QuadPart = cur.QuadPart - timestart.QuadPart;\n\tdiff.QuadPart *= 1000;\n\treturn (double)diff.QuadPart / (double)timefreq.QuadPart;\t//I hope the timefreq doesn't get rounded and cause milliseconds and doubletime to start to drift apart.\n}\n\nvoid Sys_Quit (void)\n{\n\tHost_Shutdown ();\n\texit(EXIT_SUCCESS);\n}\n\nvoid Sys_mkdir (const char *path)\n{\n\twchar_t wide[MAX_OSPATH];\n\twiden(wide, sizeof(wide), path);\n\tCreateDirectoryW(wide, NULL);\n}\nqboolean Sys_rmdir (const char *path)\n{\n\tRemoveDirectoryW(wide)\n\n\tif (rmdir (path) == 0)\n\t\treturn true;\n\tif (errno == ENOENT)\n\t\treturn true;\n\treturn false;\n}\n\nqboolean Sys_remove (const char *path)\n{\n\twchar_t wide[MAX_OSPATH];\n\twiden(wide, sizeof(wide), path);\n\tif (DeleteFileW(wide))\n\t\treturn true;\t//success\n\tif (GetLastError() == ERROR_FILE_NOT_FOUND)\n\t\treturn true;\t//succeed when the file already didn't exist\n\treturn false;\t\t//other errors? panic\n}\n\nqboolean Sys_Rename (const char *oldfname, const char *newfname)\n{\n\twchar_t oldwide[MAX_OSPATH];\n\twchar_t newwide[MAX_OSPATH];\n\twiden(oldwide, sizeof(oldwide), oldfname);\n\twiden(newwide, sizeof(newwide), newfname);\n\treturn MoveFileExW(oldwide, newwide, MOVEFILE_REPLACE_EXISTING|MOVEFILE_COPY_ALLOWED);\n}\n\n//enumeratefiles is recursive for */* to work\nstatic int Sys_EnumerateFiles2 (const char *match, int matchstart, int neststart, int (QDECL *func)(const char *fname, qofs_t fsize, time_t mtime, void *parm, searchpathfuncs_t *spath), void *parm, searchpathfuncs_t *spath)\n{\n\tqboolean go;\n\n\tHANDLE r;\n\tWIN32_FIND_DATAW fd;\n\tint nest = neststart;\t//neststart refers to just after a /\n\tqboolean wild = false;\n\n\twhile(match[nest] && match[nest] != '/')\n\t{\n\t\tif (match[nest] == '?' || match[nest] == '*')\n\t\t\twild = true;\n\t\tnest++;\n\t}\n\tif (match[nest] == '/')\n\t{\n\t\tchar submatch[MAX_OSPATH];\n\t\tchar tmproot[MAX_OSPATH];\n\n\t\tif (!wild)\n\t\t\treturn Sys_EnumerateFiles2(match, matchstart, nest+1, func, parm, spath);\n\n\t\tif (nest-neststart+1> MAX_OSPATH)\n\t\t\treturn 1;\n\t\tmemcpy(submatch, match+neststart, nest - neststart);\n\t\tsubmatch[nest - neststart] = 0;\n\t\tnest++;\n\n\t\tif (neststart+4 > MAX_OSPATH)\n\t\t\treturn 1;\n\t\tmemcpy(tmproot, match, neststart);\n\t\tstrcpy(tmproot+neststart, \"*.*\");\n\n\t\t{\n\t\t\twchar_t wroot[MAX_OSPATH];\n\t\t\tr = FindFirstFileExW(widen(wroot, sizeof(wroot), tmproot), FindExInfoStandard, &fd, FindExSearchNameMatch, NULL, 0);\n\t\t}\n\t\tstrcpy(tmproot+neststart, \"\");\n\t\tif (r==(HANDLE)-1)\n\t\t\treturn 1;\n\t\tgo = true;\n\t\tdo\n\t\t{\n\t\t\tchar utf8[MAX_OSPATH];\n\t\t\tchar file[MAX_OSPATH];\n\t\t\tnarrowen(utf8, sizeof(utf8), fd.cFileName);\n\t\t\tif (*utf8 == '.');\t//don't ever find files with a name starting with '.'\n\t\t\telse if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)\t//is a directory\n\t\t\t{\n\t\t\t\tif (wildcmp(submatch, utf8))\n\t\t\t\t{\n\t\t\t\t\tint newnest;\n\t\t\t\t\tif (strlen(tmproot) + strlen(utf8) + strlen(match+nest) + 2 < MAX_OSPATH)\n\t\t\t\t\t{\n\t\t\t\t\t\tQ_snprintfz(file, sizeof(file), \"%s%s/\", tmproot, utf8);\n\t\t\t\t\t\tnewnest = strlen(file);\n\t\t\t\t\t\tstrcpy(file+newnest, match+nest);\n\t\t\t\t\t\tgo = Sys_EnumerateFiles2(file, matchstart, newnest, func, parm, spath);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} while(FindNextFileW(r, &fd) && go);\n\t\tFindClose(r);\n\t}\n\telse\n\t{\n\t\tconst char *submatch = match + neststart;\n\t\tchar tmproot[MAX_OSPATH];\n\n\t\tif (neststart+4 > MAX_OSPATH)\n\t\t\treturn 1;\n\t\tmemcpy(tmproot, match, neststart);\n\t\tstrcpy(tmproot+neststart, \"*.*\");\n\n\t\t{\n\t\t\twchar_t wroot[MAX_OSPATH];\n\t\t\tr = FindFirstFileExW(widen(wroot, sizeof(wroot), tmproot), FindExInfoStandard, &fd, FindExSearchNameMatch, NULL, 0);\n\t\t}\n\t\tstrcpy(tmproot+neststart, \"\");\n\t\tif (r==(HANDLE)-1)\n\t\t\treturn 1;\n\t\tgo = true;\n\t\tdo\n\t\t{\n\t\t\tchar utf8[MAX_OSPATH];\n\t\t\tchar file[MAX_OSPATH];\n\n\t\t\tnarrowen(utf8, sizeof(utf8), fd.cFileName);\n\t\t\tif (*utf8 == '.')\n\t\t\t\t;\t//don't ever find files with a name starting with '.' (includes .. and . directories, and unix hidden files)\n\t\t\telse if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)\t//is a directory\n\t\t\t{\n\t\t\t\tif (wildcmp(submatch, utf8))\n\t\t\t\t{\n\t\t\t\t\tif (strlen(tmproot+matchstart) + strlen(utf8) + 2 < MAX_OSPATH)\n\t\t\t\t\t{\n\t\t\t\t\t\tQ_snprintfz(file, sizeof(file), \"%s%s/\", tmproot+matchstart, utf8);\n\t\t\t\t\t\tgo = func(file, qofs_Make(fd.nFileSizeLow, fd.nFileSizeHigh), 0, parm, spath);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (wildcmp(submatch, utf8))\n\t\t\t\t{\n\t\t\t\t\tif (strlen(tmproot+matchstart) + strlen(utf8) + 1 < MAX_OSPATH)\n\t\t\t\t\t{\n\t\t\t\t\t\tQ_snprintfz(file, sizeof(file), \"%s%s\", tmproot+matchstart, utf8);\n\t\t\t\t\t\tgo = func(file, qofs_Make(fd.nFileSizeLow, fd.nFileSizeHigh), 0, parm, spath);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} while(FindNextFileW(r, &fd) && go);\n\t\tFindClose(r);\n\t}\n\treturn go;\n}\nint Sys_EnumerateFiles (const char *gpath, const char *match, int (QDECL *func)(const char *fname, qofs_t fsize, void *parm, searchpathfuncs_t *spath), void *parm, searchpathfuncs_t *spath)\n{\n\tchar fullmatch[MAX_OSPATH];\n\tint start;\n\tif (strlen(gpath) + strlen(match) + 2 > MAX_OSPATH)\n\t\treturn 1;\n\n\tstrcpy(fullmatch, gpath);\n\tstart = strlen(fullmatch);\n\tif (start && fullmatch[start-1] != '/')\n\t\tfullmatch[start++] = '/';\n\tfullmatch[start] = 0;\n\tstrcat(fullmatch, match);\n\treturn Sys_EnumerateFiles2(fullmatch, start, start, func, parm, spath);\n}\n\n#else\n\n#if defined(GLQUAKE)\n#define PRINTGLARRAYS\n#endif\n\n#if defined(_DEBUG) || defined(DEBUG)\n#if !defined(_MSC_VER) || _MSC_VER > 1200\n#define CATCHCRASH\n#endif\n#endif\n\n#if !defined(CLIENTONLY) && !defined(SERVERONLY)\nqboolean isDedicated = false;\n#endif\nextern int isPlugin;\nqboolean debugout;\nfloat gammapending;\t//to cope with ATI. When it times out, v_gamma is reforced in order to correct/update gamma now the drivers think that they have won.\n\nHWND sys_parentwindow;\nunsigned int sys_parentleft;\t//valid if sys_parentwindow is set\nunsigned int sys_parenttop;\nunsigned int sys_parentwidth;\t//valid if sys_parentwindow is set\nunsigned int sys_parentheight;\n\nstatic struct\n{\n\tint width;\n\tint height;\n\tint rate;\n\tint bpp;\n} desktopsettings;\nstatic void Sys_QueryDesktopParameters(void);\n\n//used to do special things with awkward windows versions.\nint qwinvermaj;\nint qwinvermin;\n\nchar\t\t*sys_argv[MAX_NUM_ARGVS];\n\n\n#ifdef RESTARTTEST\njmp_buf restart_jmpbuf;\n#endif\n/*\n================\nSys_RandomBytes\n================\n*/\n#include <wincrypt.h>\nqboolean Sys_RandomBytes(qbyte *string, int len)\n{\n\tHCRYPTPROV  prov;\n\n\tif(!CryptAcquireContext( &prov, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT))\n\t{\n\t\treturn false;\n\t}\n\n\tif(!CryptGenRandom(prov, len, (BYTE *)string))\n\t{\n\t\tCryptReleaseContext( prov, 0);\n\t\treturn false;\n\t}\n\tCryptReleaseContext(prov, 0);\n\treturn true;\n}\n#ifndef CALG_SHA_512\n#define ALG_SID_SHA_256                 12\n#define ALG_SID_SHA_384                 13\n#define ALG_SID_SHA_512                 14\n#define CALG_SHA_256            (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_SHA_256)\n#define CALG_SHA_384            (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_SHA_384)\n#define CALG_SHA_512            (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_SHA_512)\n#endif\n\n//returns 0 on failure, otherwise returns the actual digest size and the digest (overallocate if lazy)\nsize_t HashCalculate(const char *hashtype, const void *data, size_t data_size, void *digest_out, size_t digest_size)\n{\n\tHCRYPTPROV\tprov;\n\tHCRYPTHASH\thash;\n\tALG_ID\t\talg;\n\n\tif (!Q_strcasecmp(hashtype, \"MD4\"))\n\t\talg = CALG_MD4;\n\telse if (!Q_strcasecmp(hashtype, \"MD5\"))\n\t\talg = CALG_MD5;\n\telse if (!Q_strcasecmp(hashtype, \"SHA1\"))\n\t\talg = CALG_SHA1;\n#ifdef CALG_SHA_256\n\telse if (!Q_strcasecmp(hashtype, \"SHA256\"))\n\t\talg = CALG_SHA_256;\t//only on xp sp3+\n#endif\n#ifdef CALG_SHA_384\n\telse if (!Q_strcasecmp(hashtype, \"SHA384\"))\n\t\talg = CALG_SHA_384;\t//only on xp sp3+\n#endif\n#ifdef CALG_SHA_512\n\telse if (!Q_strcasecmp(hashtype, \"SHA512\"))\n\t\talg = CALG_SHA_512;\t//only on xp sp3+\n#endif\n\telse\n\t\treturn 0;\n\n\tmemset(digest_out, 0, digest_size);\n\n\tif(CryptAcquireContext(&prov, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT))\n\t{\n\t\tif (CryptCreateHash(prov, alg, 0, 0, &hash))\n\t\t{\n\t\t\tif (CryptHashData(hash, data, (DWORD)data_size, 0))\n\t\t\t{\n\t\t\t\tDWORD grr = digest_size;\n\t\t\t\tif (CryptGetHashParam(hash, HP_HASHVAL, digest_out, &grr, 0))\n\t\t\t\t{\n\t\t\t\t\tCryptDestroyHash(hash);\n\t\t\t\t\tCryptReleaseContext(prov, 0);\n\t\t\t\t\treturn grr;\n\t\t\t\t}\n\t\t\t}\n\t\t\tCryptDestroyHash(hash);\n\t\t}\n\t\tCryptReleaseContext(prov, 0);\n\t}\n\treturn 0;\n}\n\n/*\n=================\nLibrary loading\n=================\n*/\nvoid Sys_CloseLibrary(dllhandle_t *lib)\n{\n\tFreeLibrary((HMODULE)lib);\n}\nHMODULE LoadLibraryU(const char *name)\n{\n\tHMODULE ret;\n\tif (WinNT)\n\t{\n\t\twchar_t wide[MAX_OSPATH];\n\t\twiden(wide, sizeof(wide), name);\n\n\t\tret = LoadLibraryW(wide);\n\t}\n\telse\n\t{\n\t\twchar_t wide[MAX_OSPATH];\n\t\tchar ansi[MAX_OSPATH];\n\t\twiden(wide, sizeof(wide), name);\n\t\tansi[WideCharToMultiByte(CP_ACP, 0, wide, wcslen(wide), ansi, sizeof(ansi)-1, NULL, NULL)] = 0;\n\t\tret = LoadLibraryA(ansi);\n\t}\n\treturn ret;\n}\ndllhandle_t *Sys_LoadLibrary(const char *name, dllfunction_t *funcs)\n{\n\tint i;\n\tHMODULE lib;\n\tDWORD err;\n\n\tlib = LoadLibraryU(name);\n\tif (!lib)\n\t{\n\t\terr = GetLastError();\n\t\tswitch(err)\n\t\t{\n\t\tcase ERROR_MOD_NOT_FOUND:\n\t\t\tbreak;\n\t\tcase ERROR_BAD_EXE_FORMAT:\n\t\t\tCon_Printf(\"Error ERROR_BAD_EXE_FORMAT loading %s\\n\", name);\n\t\t\tbreak;\n\t\tcase ERROR_PROC_NOT_FOUND:\n\t\t\tCon_Printf(\"Error ERROR_PROC_NOT_FOUND loading %s\\n\", name);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tCon_Printf(\"Error %u loading %s\\n\", (unsigned)err, name);\n\t\t\tbreak;\n\t\t}\n\n\t\tif (!strstr(COM_SkipPath(name), \".dll\"))\n\t\t{\t//.dll implies that it is a system dll, or something that is otherwise windows-specific already.\n\t\t\tchar libname[MAX_OSPATH];\n#ifdef _WIN64\n\t\t\tQ_snprintfz(libname, sizeof(libname), \"%s_64\", name);\n#elif defined(_WIN32)\n\t\t\tQ_snprintfz(libname, sizeof(libname), \"%s_32\", name);\n#else\n#error wut? not win32?\n#endif\n\t\t\tlib = LoadLibraryU(libname);\n\t\t}\n\t\tif (!lib)\n\t\t\treturn NULL;\n\t}\n\n\tif (funcs)\n\t{\n\t\tfor (i = 0; funcs[i].name; i++)\n\t\t{\n\t\t\t*funcs[i].funcptr = GetProcAddress(lib, funcs[i].name);\n\t\t\tif (!*funcs[i].funcptr)\n\t\t\t\tbreak;\n\t\t}\n\t\tif (funcs[i].name)\n\t\t{\n\t\t\tCon_DPrintf(\"Missing export \\\"%s\\\" in \\\"%s\\\"\\n\", funcs[i].name, name);\n\t\t\tSys_CloseLibrary((dllhandle_t*)lib);\n\t\t\tlib = NULL;\n\t\t}\n\t}\n\n\treturn (dllhandle_t*)lib;\n}\n\nvoid *Sys_GetAddressForName(dllhandle_t *module, const char *exportname)\n{\n\tif (!module)\n\t\treturn NULL;\n\treturn GetProcAddress((HINSTANCE)module, exportname);\n}\n#ifdef HLSERVER\nchar *Sys_GetNameForAddress(dllhandle_t *module, void *address)\n{\n\t//windows doesn't provide a function to do this, so we have to do it ourselves.\n\t//this isn't the fastest way...\n\t//halflife needs this function.\n\tchar *base = (char *)module;\n\n\tIMAGE_DATA_DIRECTORY *datadir;\n\tIMAGE_EXPORT_DIRECTORY *block;\n\tIMAGE_NT_HEADERS *ntheader;\n\tIMAGE_DOS_HEADER *dosheader = (void*)base;\n\n\tint i, j;\n\tDWORD *funclist;\n\tDWORD *namelist;\n\tSHORT *ordilist;\n\n\tif (!dosheader || dosheader->e_magic != IMAGE_DOS_SIGNATURE)\n\t\treturn NULL; //yeah, that wasn't an exe\n\n\tntheader = (void*)(base + dosheader->e_lfanew);\n\tif (!dosheader->e_lfanew || ntheader->Signature != IMAGE_NT_SIGNATURE)\n\t\treturn NULL;\t//urm, wait, a 16bit dos exe?\n\n\n\tdatadir = &ntheader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];\n\n\tblock = (IMAGE_EXPORT_DIRECTORY *)(base + datadir->VirtualAddress);\n\tfunclist = (DWORD*)(base+block->AddressOfFunctions);\n\tnamelist = (DWORD*)(base+block->AddressOfNames);\n\tordilist = (SHORT*)(base+block->AddressOfNameOrdinals);\n\tfor (i = 0; i < block->NumberOfFunctions; i++)\n\t{\n\t\tif (base+funclist[i] == address)\n\t\t{\n\t\t\tfor (j = 0; j < block->NumberOfNames; j++)\n\t\t\t{\n\t\t\t\tif (ordilist[j] == i)\n\t\t\t\t{\n\t\t\t\t\treturn base+namelist[i];\n\t\t\t\t}\n\t\t\t}\n\t\t\t//it has no name. huh?\n\t\t\treturn NULL;\n\t\t}\n\t}\n\treturn NULL;\n}\n#endif\n\nint\t\tstarttime;\nqboolean Minimized;\nqboolean\tWinNT;\t//NT has a) proper unicode support that does not unconditionally result in errors. b) a few different registry paths.\n\nstatic HANDLE\t\thinput, houtput;\n\nHANDLE\t\tqwclsemaphore;\n\nstatic HANDLE\ttevent;\n\nint VARGS Sys_DebugLog(char *file, char *fmt, ...)\n{\n\tFILE *fd;\n\tva_list argptr;\n\tstatic char data[1024];\n\n\tva_start(argptr, fmt);\n\tvsnprintf(data, sizeof(data)-1, fmt, argptr);\n\tva_end(argptr);\n\n#if defined(CRAZYDEBUGGING) && CRAZYDEBUGGING > 1\n\t{\n\t\tstatic int sock;\n\t\tif (!sock)\n\t\t{\n\t\t\tstruct sockaddr_in sa;\n\t\t\tnetadr_t na;\n\t\t\tint _true = true;\n\t\t\tint listip;\n\t\t\tlistip = COM_CheckParm(\"-debugip\");\n\t\t\tNET_StringToAdr(listip?com_argv[listip+1]:\"127.0.0.1\", &na);\n\t\t\tNetadrToSockadr(&na, (struct sockaddr_qstorage*)&sa);\n\t\t\tsa.sin_port = htons(10000);\n\t\t\tsock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);\n\t\t\tif (-1==connect(sock, (struct sockaddr*)&sa, sizeof(sa)))\n\t\t\t\tSys_Error(\"Couldn't send debug log lines\\n\");\n\t\t\tsetsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char*)&_true, sizeof(_true));\n\t\t}\n\t\tsend(sock, data, strlen(data), 0);\n\t}\n#endif\n\tfd = fopen(file, \"ab\");\n\tif (fd)\n\t{\n\t\tfprintf(fd, \"%s\", data);\n\t\tfclose(fd);\n\t\treturn 0;\n\t}\n\n\treturn 1;\n};\n\n#ifdef CATCHCRASH\n#include \"dbghelp.h\"\ntypedef BOOL (WINAPI *MINIDUMPWRITEDUMP) (\n\tHANDLE hProcess,\n\tDWORD ProcessId,\n\tHANDLE hFile,\n\tMINIDUMP_TYPE DumpType,\n\tPMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,\n\tPMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,\n\tPMINIDUMP_CALLBACK_INFORMATION CallbackParam\n\t);\nvoid DumpGLState(void);\nvoid *watchdogthread;\nDWORD CrashExceptionHandler (qboolean iswatchdog, DWORD exceptionCode, LPEXCEPTION_POINTERS exceptionInfo)\n{\n\tHANDLE hProc = GetCurrentProcess();\n\tHMODULE hKernel;\n\tBOOL (WINAPI *pIsDebuggerPresent)(void);\n\tDWORD (WINAPI *pSymSetOptions)(DWORD SymOptions);\n\tBOOL (WINAPI *pSymInitialize)(HANDLE hProcess, PSTR UserSearchPath, BOOL fInvadeProcess);\n\tBOOL (WINAPI *pSymFromAddr)(HANDLE hProcess, DWORD64 Address, PDWORD64 Displacement, PSYMBOL_INFO Symbol);\n\n#ifdef _WIN64\n#define DBGHELP_POSTFIX \"64\"\n\tBOOL (WINAPI *pStackWalkX)(DWORD MachineType, HANDLE hProcess, HANDLE hThread, LPSTACKFRAME64 StackFrame, PVOID ContextRecord, PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemoryRoutine, PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccessRoutine, PGET_MODULE_BASE_ROUTINE64 GetModuleBaseRoutine, PTRANSLATE_ADDRESS_ROUTINE64 TranslateAddress);\n\tPVOID (WINAPI *pSymFunctionTableAccessX)(HANDLE hProcess, DWORD64 AddrBase);\n\tDWORD64 (WINAPI *pSymGetModuleBaseX)(HANDLE hProcess, DWORD64 qwAddr);\n\tBOOL (WINAPI *pSymGetLineFromAddrX)(HANDLE hProcess, DWORD64 qwAddr, PDWORD pdwDisplacement, PIMAGEHLP_LINE64 Line64);\n\tBOOL (WINAPI *pSymGetModuleInfoX)(HANDLE hProcess, DWORD64 qwAddr, PIMAGEHLP_MODULE64 ModuleInfo);\n\t#define STACKFRAMEX STACKFRAME64\n\t#define IMAGEHLP_LINEX IMAGEHLP_LINE64\n\t#define IMAGEHLP_MODULEX IMAGEHLP_MODULE64\n#else\n#define DBGHELP_POSTFIX \"\"\n\tBOOL (WINAPI *pStackWalkX)(DWORD MachineType, HANDLE hProcess, HANDLE hThread, LPSTACKFRAME StackFrame, PVOID ContextRecord, PREAD_PROCESS_MEMORY_ROUTINE ReadMemoryRoutine, PFUNCTION_TABLE_ACCESS_ROUTINE FunctionTableAccessRoutine, PGET_MODULE_BASE_ROUTINE GetModuleBaseRoutine, PTRANSLATE_ADDRESS_ROUTINE TranslateAddress);\n\tPVOID (WINAPI *pSymFunctionTableAccessX)(HANDLE hProcess, DWORD AddrBase);\n\tDWORD (WINAPI *pSymGetModuleBaseX)(HANDLE hProcess, DWORD dwAddr);\n\tBOOL (WINAPI *pSymGetLineFromAddrX)(HANDLE hProcess, DWORD dwAddr, PDWORD pdwDisplacement, PIMAGEHLP_LINE Line);\n\tBOOL (WINAPI *pSymGetModuleInfoX)(HANDLE hProcess, DWORD dwAddr, PIMAGEHLP_MODULE ModuleInfo);\n\t#define STACKFRAMEX STACKFRAME\n\t#define IMAGEHLP_LINEX IMAGEHLP_LINE\n\t#define IMAGEHLP_MODULEX IMAGEHLP_MODULE\n#endif\n\tdllfunction_t debughelpfuncs[] =\n\t{\n\t\t{(void*)&pSymFromAddr,\t\t\t\t\"SymFromAddr\"},\n\t\t{(void*)&pSymSetOptions,\t\t\t\"SymSetOptions\"},\n\t\t{(void*)&pSymInitialize,\t\t\t\"SymInitialize\"},\n\t\t{(void*)&pStackWalkX,\t\t\t\t\"StackWalk\"DBGHELP_POSTFIX},\n\t\t{(void*)&pSymFunctionTableAccessX,\t\"SymFunctionTableAccess\"DBGHELP_POSTFIX},\n\t\t{(void*)&pSymGetModuleBaseX,\t\t\"SymGetModuleBase\"DBGHELP_POSTFIX},\n\t\t{(void*)&pSymGetLineFromAddrX,\t\t\"SymGetLineFromAddr\"DBGHELP_POSTFIX},\n\t\t{(void*)&pSymGetModuleInfoX,\t\t\"SymGetModuleInfo\"DBGHELP_POSTFIX},\n\t\t{NULL, NULL}\n\t};\n\n\tswitch(exceptionCode)\n\t{\n\tcase EXCEPTION_ACCESS_VIOLATION:\n\tcase EXCEPTION_DATATYPE_MISALIGNMENT:\n\tcase EXCEPTION_SINGLE_STEP:\n\tcase EXCEPTION_ARRAY_BOUNDS_EXCEEDED:\n\tcase EXCEPTION_FLT_DENORMAL_OPERAND:\n\tcase EXCEPTION_FLT_DIVIDE_BY_ZERO:\n\tcase EXCEPTION_FLT_INEXACT_RESULT:\n\tcase EXCEPTION_FLT_INVALID_OPERATION:\n\tcase EXCEPTION_FLT_OVERFLOW:\n\tcase EXCEPTION_FLT_STACK_CHECK:\n\tcase EXCEPTION_FLT_UNDERFLOW:\n\tcase EXCEPTION_INT_DIVIDE_BY_ZERO:\n\tcase EXCEPTION_INT_OVERFLOW:\n\tcase EXCEPTION_PRIV_INSTRUCTION:\n\tcase EXCEPTION_IN_PAGE_ERROR:\n\tcase EXCEPTION_ILLEGAL_INSTRUCTION:\n\tcase EXCEPTION_NONCONTINUABLE_EXCEPTION:\n\tcase EXCEPTION_STACK_OVERFLOW:\n\tcase EXCEPTION_INVALID_DISPOSITION:\n\tcase EXCEPTION_GUARD_PAGE:\n\tcase EXCEPTION_INVALID_HANDLE:\n//\tcase EXCEPTION_POSSIBLE_DEADLOCK:\n\t\tbreak;\n\tcase EXCEPTION_BREAKPOINT:\n\t\thKernel = LoadLibrary (\"kernel32\");\n\t\tpIsDebuggerPresent = (void*)GetProcAddress(hKernel, \"IsDebuggerPresent\");\n\t\tif (pIsDebuggerPresent && pIsDebuggerPresent())\n\t\t\treturn EXCEPTION_CONTINUE_SEARCH;\n\t\tbreak;\n\t\treturn EXCEPTION_CONTINUE_EXECUTION;\n\tdefault:\n\t\t//because windows is a steaming pile of shite, we have to ignore any software-generated exceptions, because most of them are not in fact fatal, *EVEN IF THEY CLAIM TO BE NON-CONTINUABLE*\n\t\treturn EXCEPTION_CONTINUE_SEARCH;\n\t}\n\n#ifdef PRINTGLARRAYS\n\tif (!iswatchdog && qrenderer == QR_OPENGL && Sys_IsMainThread())\n\t\tDumpGLState();\n#endif\n\n\thKernel = LoadLibrary (\"kernel32\");\n\tpIsDebuggerPresent = (void*)GetProcAddress(hKernel, \"IsDebuggerPresent\");\n\n#ifdef GLQUAKE\n\t//restores gamma\n\tGLVID_Crashed();\n#endif\n\n\tif (!iswatchdog && pIsDebuggerPresent && pIsDebuggerPresent ())\n\t{\n\t\t/*if we have a current window, minimize it to bring us out of fullscreen*/\n/*\t\textern qboolean vid_initializing;\n\t\tqboolean oldval = vid_initializing;\n\t\tvid_initializing = true;\n\t\tShowWindow(mainwindow, SW_MINIMIZE);\n\t\tvid_initializing = oldval;\n*/\n\t\treturn EXCEPTION_CONTINUE_SEARCH;\n\t}\n\n\t/*if we have a current window, kill it, so it can't steal input of handle window messages or anything risky like that*/\n\tif (iswatchdog)\n\t{\n\t}\n\telse\n\t{\n\t\tDestroyWindow(mainwindow);\n\n\t\tif (Sys_LoadLibrary(\"DBGHELP\", debughelpfuncs))\n\t\t{\n\t\t\tSTACKFRAMEX stack;\n\t\t\tCONTEXT *pcontext = exceptionInfo->ContextRecord;\n\t\t\tIMAGEHLP_LINEX line;\n\t\t\tIMAGEHLP_MODULEX module;\n\t\t\tstruct\n\t\t\t{\n\t\t\t\tSYMBOL_INFO sym;\n\t\t\t\tchar name[1024];\n\t\t\t} sym;\n\t\t\tint frameno;\n\t\t\tchar stacklog[8192];\n\t\t\tint logpos, logstart;\n\t\t\tchar *logline;\n\n\t\t\tstacklog[logpos=0] = 0;\n\n\t\t\tpSymInitialize(hProc, NULL, TRUE);\n\t\t\tpSymSetOptions(SYMOPT_LOAD_LINES);\n\n\t\t\tmemset(&stack, 0, sizeof(stack));\n#ifdef _WIN64\n\t\t\t#define IMAGE_FILE_MACHINE_THIS IMAGE_FILE_MACHINE_AMD64\n\t\t\tstack.AddrPC.Mode = AddrModeFlat;\n\t\t\tstack.AddrPC.Offset = pcontext->Rip;\n\t\t\tstack.AddrFrame.Mode = AddrModeFlat;\n\t\t\tstack.AddrFrame.Offset = pcontext->Rbp;\n\t\t\tstack.AddrStack.Mode = AddrModeFlat;\n\t\t\tstack.AddrStack.Offset = pcontext->Rsp;\n#else\n\t\t\t#define IMAGE_FILE_MACHINE_THIS IMAGE_FILE_MACHINE_I386\n\t\t\tstack.AddrPC.Mode = AddrModeFlat;\n\t\t\tstack.AddrPC.Offset = pcontext->Eip;\n\t\t\tstack.AddrFrame.Mode = AddrModeFlat;\n\t\t\tstack.AddrFrame.Offset = pcontext->Ebp;\n\t\t\tstack.AddrStack.Mode = AddrModeFlat;\n\t\t\tstack.AddrStack.Offset = pcontext->Esp;\n#endif\n\n\t\t\tQ_strncpyz(stacklog+logpos, FULLENGINENAME \" or dependancy has crashed. The following stack dump been copied to your windows clipboard.\\n\"\n#ifdef _MSC_VER\n\t\t\t\t\"Would you like to generate a core dump too?\\n\"\n#endif\n\t\t\t\t\"\\n\", sizeof(stacklog)-logpos);\n\t\t\tlogstart = logpos += strlen(stacklog+logpos);\n\n\t\t\t//so I know which one it is\n#if defined(DEBUG) || defined(_DEBUG)\n\t#define BUILDDEBUGREL \"Debug\"\n#else\n\t#define BUILDDEBUGREL \"Optimised\"\n#endif\n#ifdef MINIMAL\n\t#define BUILDMINIMAL \"Min\"\n#else\n\t#define BUILDMINIMAL \"\"\n#endif\n#if defined(GLQUAKE) && !defined(D3DQUAKE) && !defined(VKQUAKE)\n\t#define BUILDTYPE \"GL\"\n#elif !defined(GLQUAKE) && defined(D3DQUAKE) && !defined(VKQUAKE)\n\t#define BUILDTYPE \"D3D\"\n#elif !defined(GLQUAKE) && !defined(D3DQUAKE) && defined(VKQUAKE)\n\t#define BUILDTYPE \"VK\"\n#else\n\t#define BUILDTYPE \"Merged\"\n#endif\n\n\t\t\tQ_snprintfz(stacklog+logpos, sizeof(stacklog)-logpos, \"Build: %s %s %s: %s\\r\\n\", BUILDDEBUGREL, PLATFORM, BUILDMINIMAL BUILDTYPE, version_string());\n\t\t\tlogpos += strlen(stacklog+logpos);\n\n\t\t\tfor(frameno = 0; ; frameno++)\n\t\t\t{\n\t\t\t\tDWORD64 symdisp;\n\t\t\t\tDWORD linedisp;\n\t\t\t\tDWORD_PTR symaddr;\n\t\t\t\tif (!pStackWalkX(IMAGE_FILE_MACHINE_THIS, hProc, GetCurrentThread(), &stack, pcontext, NULL, pSymFunctionTableAccessX, pSymGetModuleBaseX, NULL))\n\t\t\t\t\tbreak;\n\t\t\t\tmemset(&module, 0, sizeof(module));\n\t\t\t\tmodule.SizeOfStruct = sizeof(module);\n\t\t\t\tpSymGetModuleInfoX(hProc, stack.AddrPC.Offset, &module);\n\t\t\t\tmemset(&line, 0, sizeof(line));\n\t\t\t\tline.SizeOfStruct = sizeof(line);\n\t\t\t\tsymdisp = 0;\n\t\t\t\tmemset(&sym, 0, sizeof(sym));\n\t\t\t\tsym.sym.MaxNameLen = sizeof(sym.name);\n\t\t\t\tsymaddr = stack.AddrPC.Offset;\n\t\t\t\tsym.sym.SizeOfStruct = sizeof(sym.sym);\n\t\t\t\tif (pSymFromAddr(hProc, symaddr, &symdisp, &sym.sym))\n\t\t\t\t{\n\t\t\t\t\tif (pSymGetLineFromAddrX(hProc, stack.AddrPC.Offset, &linedisp, &line))\n\t\t\t\t\t\tlogline = va(\"%-20s - %s:%i (%s)\\r\\n\", sym.sym.Name, line.FileName, (int)line.LineNumber, module.LoadedImageName);\n\t\t\t\t\telse\n\t\t\t\t\t\tlogline = va(\"%-20s+%#x (%s)\\r\\n\", sym.sym.Name, (unsigned int)symdisp, module.LoadedImageName);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tlogline = va(\"0x%p (%s)\\r\\n\", (void*)(DWORD_PTR)stack.AddrPC.Offset, module.LoadedImageName);\n\t\t\t\tQ_strncpyz(stacklog+logpos, logline, sizeof(stacklog)-logpos);\n\t\t\t\tlogpos += strlen(stacklog+logpos);\n\t\t\t\tif (logpos+1 >= sizeof(stacklog))\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tSys_SaveClipboard(CBT_CLIPBOARD, stacklog+logstart);\n#ifdef _MSC_VER\n\t\t\tif (MessageBoxA(0, stacklog, \"KABOOM!\", MB_ICONSTOP|MB_YESNO) != IDYES)\n\t\t\t{\n\t\t\t\tif (pIsDebuggerPresent && pIsDebuggerPresent ())\n\t\t\t\t{\n\t\t\t\t\t//its possible someone attached a debugger while we were showing that message\n\t\t\t\t\treturn EXCEPTION_CONTINUE_SEARCH;\n\t\t\t\t}\n\t\t\t\treturn EXCEPTION_EXECUTE_HANDLER;\n\t\t\t}\n#else\n\t\t\tMessageBox(0, stacklog, \"KABOOM!\", MB_ICONSTOP);\n\t\t\treturn EXCEPTION_EXECUTE_HANDLER;\n#endif\n\t\t}\n\t\telse\n\t\t{\n\t\t\tMessageBoxA(NULL, \"We crashed.\\nUnable to load dbghelp library. Stack info is not available\", DISTRIBUTION \" Sucks\", MB_ICONSTOP);\n\t\t\treturn EXCEPTION_EXECUTE_HANDLER;\n\t\t}\n\t}\n\n\t//generate a minidump, but only if we were compiled by something that used usable debugging info. its a bit pointless otherwise.\n#ifdef _MSC_VER\n\t{\n\t\tchar dumpPath[1024];\n\t\tchar msg[1024];\n\t\tDWORD procid = GetCurrentProcessId();\n\t\tHANDLE dumpfile;\n\t\tHMODULE hDbgHelp;\n\t\tMINIDUMPWRITEDUMP fnMiniDumpWriteDump;\n\n\t\thDbgHelp = LoadLibraryA (\"DBGHELP\");\n\t\tif (hDbgHelp)\n\t\t\tfnMiniDumpWriteDump = (MINIDUMPWRITEDUMP)GetProcAddress (hDbgHelp, \"MiniDumpWriteDump\");\n\t\telse\n\t\t\tfnMiniDumpWriteDump = NULL;\n\n\t\tif (fnMiniDumpWriteDump)\n\t\t{\n\t\t\tif (iswatchdog)\n\t\t\t{\n\t\t\t\tswitch (MessageBoxA(NULL, \"Fizzle... We hit an infinite loop! Or something is just really slow.\\nBlame the monkey in the corner.\\nI hope you saved your work.\\nWould you like to take a dump now?\\n(click cancel to wait a bit longer)\", DISTRIBUTION \" Sucks\", MB_ICONSTOP|MB_YESNOCANCEL|MB_DEFBUTTON3))\n\t\t\t\t{\n\t\t\t\tcase IDYES:\n\t\t\t\t\tbreak;\t//take a dump.\n\t\t\t\tcase IDNO:\n\t\t\t\t\texit(EXIT_FAILURE);\n\t\t\t\tdefault:\t//cancel = run the exception handler, which means we reset the watchdog.\n\t\t\t\t\treturn EXCEPTION_EXECUTE_HANDLER;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t/*take a dump*/\n\t\t\tif (*com_homepath)\n\t\t\t\tQ_strncpyz(dumpPath, com_homepath, sizeof(dumpPath));\n\t\t\telse if (*com_gamepath)\n\t\t\t\tQ_strncpyz(dumpPath, com_gamepath, sizeof(dumpPath));\n\t\t\telse\n\t\t\t\tGetTempPathA (sizeof(dumpPath)-16, dumpPath);\n\t\t\tQ_strncatz(dumpPath, DISTRIBUTION\"CrashDump.dmp\", sizeof(dumpPath));\n\t\t\tdumpfile = CreateFileA (dumpPath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);\n\t\t\tif (dumpfile)\n\t\t\t{\n\t\t\t\tMINIDUMP_EXCEPTION_INFORMATION crashinfo;\n\t\t\t\tcrashinfo.ClientPointers = TRUE;\n\t\t\t\tcrashinfo.ExceptionPointers = exceptionInfo;\n\t\t\t\tcrashinfo.ThreadId = GetCurrentThreadId ();\n\t\t\t\tif (fnMiniDumpWriteDump(hProc, procid, dumpfile, MiniDumpWithIndirectlyReferencedMemory|MiniDumpWithDataSegs, &crashinfo, NULL, NULL))\n\t\t\t\t{\n\t\t\t\t\tCloseHandle(dumpfile);\n\t\t\t\t\tQ_snprintfz(msg, sizeof(msg), \"You can find the crashdump at:\\n%s\\nPlease send this file to someone.\\n\\nWarning: sensitive information (like your current user name) might be present in the dump.\\nYou will probably want to compress it.\", dumpPath);\n\t\t\t\t\tMessageBoxA(NULL, msg, DISTRIBUTION \" Sucks\", 0);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tMessageBoxA(NULL, \"MiniDumpWriteDump failed\", \"oh noes\", 0);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tQ_snprintfz(msg, sizeof(msg), \"unable to open %s\\nno dump created.\", dumpPath);\n\t\t\t\tMessageBoxA(NULL, msg, \"oh noes\", 0);\n\t\t\t}\n\t\t}\n\t\telse\n\t\t\tMessageBoxA(NULL, \"Kaboom! Sorry. No MiniDumpWriteDump function.\", DISTRIBUTION \" Sucks\", 0);\n\t}\n#endif\n\treturn EXCEPTION_EXECUTE_HANDLER;\n}\n\n//most compilers do not support __try. perhaps we should avoid its use entirely?\nLONG CALLBACK nonmsvc_CrashExceptionHandler(PEXCEPTION_POINTERS ExceptionInfo)\n{\n\tDWORD foo = EXCEPTION_CONTINUE_SEARCH;\n\tfoo = CrashExceptionHandler(false, ExceptionInfo->ExceptionRecord->ExceptionCode, ExceptionInfo);\n\t//we have no handler. thus we handle it by exiting.\n\tif (foo == EXCEPTION_EXECUTE_HANDLER)\n\t\texit(EXIT_FAILURE);\n\treturn foo;\n}\n\nvolatile int watchdogframe;\t//incremented each frame.\nvoid *watchdogthread;\nint watchdogthreadfunction(void *arg)\n{\n#ifdef _MSC_VER\n\tint oldframe = watchdogframe;\n\tint newframe;\n\tint secs = 0;\n\twhile(1)\n\t{ \n\t\tnewframe = watchdogframe;\n\t\tif (oldframe != newframe)\n\t\t{\n\t\t\toldframe = newframe;\n\t\t\tsecs = 0;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tsecs++;\n\t\t\tif (secs > 10)\n\t\t\t{\n\t\t\t\tsecs = 0;\n\t\t\t\t__try\n\t\t\t\t{\n\t\t\t\t\t*(int*)arg = -3;\n\t\t\t\t}\n\t\t\t\t__except (CrashExceptionHandler(true, GetExceptionCode(), GetExceptionInformation()))\n\t\t\t\t{\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tSleep(1000);\n\t}\n#endif\n\treturn 0;\n}\n#endif\n\n#ifndef SERVERONLY\n\n#if (_WIN32_WINNT < 0x0400)\n\t#define LLKHF_ALTDOWN        0x00000020\n\t#define LLKHF_UP             0x00000080\n\t#define WH_KEYBOARD_LL     13\n\ttypedef struct {\n\t\tDWORD vkCode;\n\t\tDWORD scanCode;\n\t\tDWORD flags;\n\t\tDWORD time;\n\t\tDWORD dwExtraInfo;\n\t} KBDLLHOOKSTRUCT;\n#elif defined(MINGW)\n\t#ifndef LLKHF_UP\n\t\t#define LLKHF_UP             0x00000080\n\t#endif\n#endif\n\nstatic struct\n{\n\tHHOOK llkeyboardhook;\n\n\t//windows hooks can be used for code injection etc.\n\t//hide these symbols from shitty exports scanners so we don't look like the keylogger that we aren't. Note the 'vid.activeapp' requirement below - we are not a keylogger, we only see a limited set of keys and only when we already have focus.\n\tHHOOK (WINAPI *pSetWindowsHookEx) (int idHook, HOOKPROC lpfn, HINSTANCE hmod, DWORD dwThreadId);\t//W and A versions have the same signature.\n\tLRESULT (WINAPI *pCallNextHookEx) (HHOOK hhk, int nCode, WPARAM wParam, LPARAM lParam);\n\tBOOL (WINAPI *pUnhookWindowsHookEx) (HHOOK hhk);\n} winkeys;\n\ncvar_t\tsys_disableWinKeys = CVAR(\"sys_disableWinKeys\", \"0\");\ncvar_t\tsys_disableTaskSwitch = CVARF(\"sys_disableTaskSwitch\", \"0\", CVAR_NOTFROMSERVER);\t// please don't encourage people to use this...\n\nLRESULT CALLBACK LowLevelKeyboardProc (INT nCode, WPARAM wParam, LPARAM lParam)\n{\n\tKBDLLHOOKSTRUCT *pkbhs = (KBDLLHOOKSTRUCT *) lParam;\n\tif (vid.activeapp)\n\tswitch (nCode)\n\t{\n\tcase HC_ACTION:\n\t\t{\n\t\t//Trap the Left Windowskey\n\t\t\tif (pkbhs->vkCode == VK_SNAPSHOT)\n\t\t\t{\n\t\t\t\tIN_KeyEvent (0, !(pkbhs->flags & LLKHF_UP), K_PRINTSCREEN, 0);\n\t\t\t\treturn 1;\n\t\t\t}\n\t\t\tif (sys_disableWinKeys.ival)\n\t\t\t{\n\t\t\t\tif (pkbhs->vkCode == VK_LWIN)\n\t\t\t\t{\n\t\t\t\t\tIN_KeyEvent (0, !(pkbhs->flags & LLKHF_UP), K_LWIN, 0);\n\t\t\t\t\treturn 1;\n\t\t\t\t}\n\t\t\t//Trap the Right Windowskey\n\t\t\t\tif (pkbhs->vkCode == VK_RWIN)\n\t\t\t\t{\n\t\t\t\t\tIN_KeyEvent(0, !(pkbhs->flags & LLKHF_UP), K_RWIN, 0);\n\t\t\t\t\treturn 1;\n\t\t\t\t}\n\t\t\t//Trap the Application Key (what a pointless key)\n\t\t\t\tif (pkbhs->vkCode == VK_APPS)\n\t\t\t\t{\n\t\t\t\t\tIN_KeyEvent (0, !(pkbhs->flags & LLKHF_UP), K_APP, 0);\n\t\t\t\t\treturn 1;\n\t\t\t\t}\n\t\t\t}\n\n\t\t// Disable CTRL+ESC\n\t\t\t//this works, but we've got to give some way to tab out...\n\t\t\tif (sys_disableTaskSwitch.ival)\n\t\t\t{\n\t\t\t\tif (pkbhs->vkCode == VK_ESCAPE && GetAsyncKeyState (VK_CONTROL) >> ((sizeof(SHORT) * 8) - 1))\n\t\t\t\t\treturn 1;\n\t\t// Disable ATL+TAB\n\t\t\t\tif (pkbhs->vkCode == VK_TAB && pkbhs->flags & LLKHF_ALTDOWN)\n\t\t\t\t\treturn 1;\n\t\t// Disable ALT+ESC\n\t\t\t\tif (pkbhs->vkCode == VK_ESCAPE && pkbhs->flags & LLKHF_ALTDOWN)\n\t\t\t\t\treturn 1;\n\t\t\t}\n\n\t\t\tbreak;\n\t\t}\n\tdefault:\n\t\tbreak;\n\t}\n\treturn winkeys.pCallNextHookEx (winkeys.llkeyboardhook, nCode, wParam, lParam);\n}\n\nvoid SetHookState(qboolean state)\n{\n\tif (!sys_disableTaskSwitch.ival && !sys_disableWinKeys.ival)\n\t\tstate = false;\n\n\tif (!state == !winkeys.llkeyboardhook)\t//not so types are comparable\n\t\treturn;\n\tif (!winkeys.pSetWindowsHookEx)\n\t{\n\t\tHMODULE dll = LoadLibraryA(\"user32.dll\");\n\t\tif (!dll)\n\t\t\treturn;\n\t\twinkeys.pSetWindowsHookEx\t\t= (void*)GetProcAddress(dll, WinNT?\"SetWindowsHookExW\":\"SetWindowsHookExA\");\n\t\twinkeys.pCallNextHookEx\t\t\t= (void*)GetProcAddress(dll, \"CallNextHookEx\");\n\t\twinkeys.pUnhookWindowsHookEx\t= (void*)GetProcAddress(dll, \"UnhookWindowsHookEx\");\n\t\tif (!winkeys.pSetWindowsHookEx)\n\t\t\treturn;\n\t}\n\n\tif (winkeys.llkeyboardhook)\n\t{\n\t\twinkeys.pUnhookWindowsHookEx(winkeys.llkeyboardhook);\n\t\twinkeys.llkeyboardhook = NULL;\n\t}\n\tif (state)\n\t\twinkeys.llkeyboardhook = winkeys.pSetWindowsHookEx(WH_KEYBOARD_LL, LowLevelKeyboardProc, GetModuleHandle(NULL), 0);\n}\n\n#endif\n\n/*\n===============================================================================\n\nFILE IO\n\n===============================================================================\n*/\n\nvoid Sys_mkdir (const char *path)\n{\n\tif (WinNT)\n\t{\n\t\twchar_t wide[MAX_OSPATH];\n\t\twiden(wide, sizeof(wide), path);\n\t\tCreateDirectoryW(wide, NULL);\n\t}\n\telse\n\t\t_mkdir (path);\n}\n\nqboolean Sys_rmdir (const char *path)\n{\n\tif (WinNT)\n\t{\n\t\twchar_t wide[MAX_OSPATH];\n\t\twiden(wide, sizeof(wide), path);\n\t\treturn RemoveDirectoryW(wide);\n\t}\n\telse\n\t\treturn 0==_mkdir (path);\n}\n\n\nqboolean Sys_remove (const char *path)\n{\n\tif (WinNT)\n\t{\n\t\twchar_t wide[MAX_OSPATH];\n\t\tDWORD err;\n\t\twiden(wide, sizeof(wide), path);\n\t\tif (DeleteFileW(wide))\n\t\t\treturn true;\t//success\n\t\terr = GetLastError();\n\t\tif (err == ERROR_FILE_NOT_FOUND || err == ERROR_PATH_NOT_FOUND)\n\t\t\treturn true;\t//succeed when the file already didn't exist\n\t\tif (err == ERROR_ACCESS_DENIED)\n\t\t\treturn false;\t//windows is shite. this may simply include that its open in another process that didn't include the SHARE_DELETE permission.\n\t\treturn false;\t\t//other errors? panic\n\t}\n\telse\n\t{\n\t\tif (remove (path) != 0)\n\t\t{\n\t\t\tint e = errno;\n\t\t\tif (e == ENOENT)\n\t\t\t\treturn true;\t//return success if it doesn't already exist.\n\t\t\treturn false;\n\t\t}\n\t\treturn true;\n\t}\n}\n\nqboolean Sys_Rename (const char *oldfname, const char *newfname)\n{\n\tif (WinNT)\n\t{\n\t\twchar_t oldwide[MAX_OSPATH];\n\t\twchar_t newwide[MAX_OSPATH];\n\t\twiden(oldwide, sizeof(oldwide), oldfname);\n\t\twiden(newwide, sizeof(newwide), newfname);\n\t\treturn MoveFileW(oldwide, newwide);\n\t}\n\telse\n\t\treturn !rename(oldfname, newfname);\n}\n\n#ifdef _MSC_VER\n#define ULL(x) x##ui64\n#else\n#define ULL(x) x##ull\n#endif\n\nstatic time_t Sys_FileTimeToTime(FILETIME ft)\n{\n\tULARGE_INTEGER ull;\n\tull.LowPart = ft.dwLowDateTime;\n\tull.HighPart = ft.dwHighDateTime;\n\treturn ull.QuadPart / ULL(10000000) - ULL(11644473600);\n}\n\nstatic int Sys_EnumerateFiles_9x (const char *match, int matchstart, int neststart, int (QDECL *func)(const char *fname, qofs_t fsize, time_t mtime, void *parm, searchpathfuncs_t *spath), void *parm, searchpathfuncs_t *spath)\n{\n\tqboolean go;\n\tHANDLE r;\n\tWIN32_FIND_DATAA fd;\n\tint nest = neststart;\t//neststart refers to just after a /\n\tqboolean wild = false;\n\n\twhile(match[nest] && match[nest] != '/')\n\t{\n\t\tif (match[nest] == '?' || match[nest] == '*')\n\t\t\twild = true;\n\t\tnest++;\n\t}\n\tif (match[nest] == '/')\n\t{\n\t\tchar submatch[MAX_OSPATH];\n\t\tchar tmproot[MAX_OSPATH];\n\t\tchar file[MAX_OSPATH];\n\n\t\tif (!wild)\n\t\t\treturn Sys_EnumerateFiles_9x(match, matchstart, nest+1, func, parm, spath);\n\n\t\tif (nest-neststart+1> MAX_OSPATH)\n\t\t\treturn 1;\n\t\tmemcpy(submatch, match+neststart, nest - neststart);\n\t\tsubmatch[nest - neststart] = 0;\n\t\tnest++;\n\n\t\tif (neststart+4 > MAX_OSPATH)\n\t\t\treturn 1;\n\t\tmemcpy(tmproot, match, neststart);\n\t\tstrcpy(tmproot+neststart, \"*.*\");\n\n\t\tr = FindFirstFileA(tmproot, &fd);\n\t\tstrcpy(tmproot+neststart, \"\");\n\t\tif (r==(HANDLE)-1)\n\t\t\treturn 1;\n\t\tgo = true;\n\t\tdo\n\t\t{\n\t\t\tif (*fd.cFileName == '.');\t//don't ever find files with a name starting with '.'\n\t\t\telse if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)\t//is a directory\n\t\t\t{\n\t\t\t\tif (wildcmp(submatch, fd.cFileName))\n\t\t\t\t{\n\t\t\t\t\tint newnest;\n\t\t\t\t\tif (strlen(tmproot) + strlen(fd.cFileName) + strlen(match+nest) + 2 < MAX_OSPATH)\n\t\t\t\t\t{\n\t\t\t\t\t\tQ_snprintfz(file, sizeof(file), \"%s%s/\", tmproot, fd.cFileName);\n\t\t\t\t\t\tnewnest = strlen(file);\n\t\t\t\t\t\tstrcpy(file+newnest, match+nest);\n\t\t\t\t\t\tgo = Sys_EnumerateFiles_9x(file, matchstart, newnest, func, parm, spath);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} while(FindNextFileA(r, &fd) && go);\n\t\tFindClose(r);\n\t}\n\telse\n\t{\n\t\tconst char *submatch = match + neststart;\n\t\tchar tmproot[MAX_OSPATH];\n\t\tchar file[MAX_OSPATH];\n\n\t\tif (neststart+4 > MAX_OSPATH)\n\t\t\treturn 1;\n\t\tmemcpy(tmproot, match, neststart);\n\t\tstrcpy(tmproot+neststart, \"*.*\");\n\n\t\tr = FindFirstFileA(tmproot, &fd);\n\t\tstrcpy(tmproot+neststart, \"\");\n\t\tif (r==(HANDLE)-1)\n\t\t\treturn 1;\n\t\tgo = true;\n\t\tdo\n\t\t{\n\t\t\tif (*fd.cFileName == '.')\n\t\t\t\t;\t//don't ever find files with a name starting with '.' (includes .. and . directories, and unix hidden files)\n\t\t\telse if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)\t//is a directory\n\t\t\t{\n\t\t\t\tif (wildcmp(submatch, fd.cFileName))\n\t\t\t\t{\n\t\t\t\t\tif (strlen(tmproot+matchstart) + strlen(fd.cFileName) + 2 < MAX_OSPATH)\n\t\t\t\t\t{\n\t\t\t\t\t\tQ_snprintfz(file, sizeof(file), \"%s%s/\", tmproot+matchstart, fd.cFileName);\n\t\t\t\t\t\tgo = func(file, qofs_Make(fd.nFileSizeLow, fd.nFileSizeHigh), Sys_FileTimeToTime(fd.ftLastWriteTime), parm, spath);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (wildcmp(submatch, fd.cFileName))\n\t\t\t\t{\n\t\t\t\t\tif (strlen(tmproot+matchstart) + strlen(fd.cFileName) + 1 < MAX_OSPATH)\n\t\t\t\t\t{\n\t\t\t\t\t\tQ_snprintfz(file, sizeof(file), \"%s%s\", tmproot+matchstart, fd.cFileName);\n\t\t\t\t\t\tgo = func(file, qofs_Make(fd.nFileSizeLow, fd.nFileSizeHigh), Sys_FileTimeToTime(fd.ftLastWriteTime), parm, spath);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} while(FindNextFileA(r, &fd) && go);\n\t\tFindClose(r);\n\t}\n\treturn go;\n}\nstatic int Sys_EnumerateFiles_NT (const char *match, int matchstart, int neststart, int (QDECL *func)(const char *fname, qofs_t fsize, time_t mtime, void *parm, searchpathfuncs_t *spath), void *parm, searchpathfuncs_t *spath)\n{\n\tqboolean go;\n\tHANDLE r;\n\tWIN32_FIND_DATAW fd;\n\tint nest = neststart;\t//neststart refers to just after a /\n\tqboolean wild = false;\n\tchar tmproot[MAX_OSPATH];\n\tchar utf8[MAX_OSPATH];\n\tchar file[MAX_OSPATH];\n\n\tfor(;;)\n\t{\n\t\twhile(match[nest] && match[nest] != '/')\n\t\t{\n\t\t\tif (match[nest] == '?' || match[nest] == '*')\n\t\t\t\twild = true;\n\t\t\tnest++;\n\t\t}\n\n\t\tif (match[nest] == '/' && !wild)\n\t\t{\n\t\t\tnest = neststart = nest+1;\n\t\t\twild = false;\n\t\t}\n\t\telse\n\t\t\tbreak;\n\t}\n\tif (match[nest] == '/')\n\t{\n\t\tchar submatch[MAX_OSPATH];\n\n\t\tif (nest-neststart+1> MAX_OSPATH)\n\t\t\treturn 1;\n\t\tmemcpy(submatch, match+neststart, nest - neststart);\n\t\tsubmatch[nest - neststart] = 0;\n\t\tnest++;\n\n\t\tif (neststart+4 > MAX_OSPATH)\n\t\t\treturn 1;\n\t\tmemcpy(tmproot, match, neststart);\n\t\tstrcpy(tmproot+neststart, \"*.*\");\n\n\t\t{\n\t\t\twchar_t wroot[MAX_OSPATH];\n\t\t\tr = FindFirstFileW(widen(wroot, sizeof(wroot), tmproot), &fd);\n\t\t}\n\t\tstrcpy(tmproot+neststart, \"\");\n\t\tif (r==(HANDLE)-1)\n\t\t\treturn 1;\n\t\tgo = true;\n\t\tdo\n\t\t{\n\t\t\tnarrowen(utf8, sizeof(utf8), fd.cFileName);\n\t\t\tif (*utf8 == '.');\t//don't ever find files with a name starting with '.'\n\t\t\telse if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)\t//is a directory\n\t\t\t{\n\t\t\t\tif (wildcmp(submatch, utf8))\n\t\t\t\t{\n\t\t\t\t\tint newnest;\n\t\t\t\t\tif (strlen(tmproot) + strlen(utf8) + strlen(match+nest) + 2 < MAX_OSPATH)\n\t\t\t\t\t{\n\t\t\t\t\t\tQ_snprintfz(file, sizeof(file), \"%s%s/\", tmproot, utf8);\n\t\t\t\t\t\tnewnest = strlen(file);\n\t\t\t\t\t\tstrcpy(file+newnest, match+nest);\n\t\t\t\t\t\tgo = Sys_EnumerateFiles_NT(file, matchstart, newnest, func, parm, spath);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} while(FindNextFileW(r, &fd) && go);\n\t\tFindClose(r);\n\t}\n\telse\n\t{\n\t\tconst char *submatch = match + neststart;\n\t\tchar tmproot[MAX_OSPATH];\n\n\t\tif (neststart+4 > MAX_OSPATH)\n\t\t\treturn 1;\n\t\tmemcpy(tmproot, match, neststart);\n\t\tstrcpy(tmproot+neststart, \"*.*\");\n\n\t\t{\n\t\t\twchar_t wroot[MAX_OSPATH];\n\t\t\tr = FindFirstFileW(widen(wroot, sizeof(wroot), tmproot), &fd);\n\t\t}\n\t\tstrcpy(tmproot+neststart, \"\");\n\t\tif (r==(HANDLE)-1)\n\t\t\treturn 1;\n\t\tgo = true;\n\t\tdo\n\t\t{\n\t\t\tnarrowen(utf8, sizeof(utf8), fd.cFileName);\n\t\t\tif (*utf8 == '.')\n\t\t\t\t;\t//don't ever find files with a name starting with '.' (includes .. and . directories, and unix hidden files)\n\t\t\telse if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)\t//is a directory\n\t\t\t{\n\t\t\t\tif (wildcmp(submatch, utf8))\n\t\t\t\t{\n\t\t\t\t\tif (strlen(tmproot+matchstart) + strlen(utf8) + 2 < MAX_OSPATH)\n\t\t\t\t\t{\n\t\t\t\t\t\tQ_snprintfz(file, sizeof(file), \"%s%s/\", tmproot+matchstart, utf8);\n\t\t\t\t\t\tgo = func(file, qofs_Make(fd.nFileSizeLow, fd.nFileSizeHigh), Sys_FileTimeToTime(fd.ftLastWriteTime), parm, spath);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (wildcmp(submatch, utf8))\n\t\t\t\t{\n\t\t\t\t\tif (strlen(tmproot+matchstart) + strlen(utf8) + 1 < MAX_OSPATH)\n\t\t\t\t\t{\n\t\t\t\t\t\tQ_snprintfz(file, sizeof(file), \"%s%s\", tmproot+matchstart, utf8);\n\t\t\t\t\t\tgo = func(file, qofs_Make(fd.nFileSizeLow, fd.nFileSizeHigh), Sys_FileTimeToTime(fd.ftLastWriteTime), parm, spath);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} while(FindNextFileW(r, &fd) && go);\n\t\tFindClose(r);\n\t}\n\treturn go;\n}\nint Sys_EnumerateFiles (const char *gpath, const char *match, int (QDECL *func)(const char *fname, qofs_t fsize, time_t mtime, void *parm, searchpathfuncs_t *spath), void *parm, searchpathfuncs_t *spath)\n{\n\tchar fullmatch[MAX_OSPATH];\n\tint start;\n\tif (!gpath)\n\t\tgpath = \"\";\n\tif (strlen(gpath) + strlen(match) + 2 > MAX_OSPATH)\n\t\treturn 1;\n\n\tstrcpy(fullmatch, gpath);\n\tstart = strlen(fullmatch);\n\tif (start && fullmatch[start-1] != '/')\n\t\tfullmatch[start++] = '/';\n\tfullmatch[start] = 0;\n\tstrcat(fullmatch, match);\n\tif (WinNT)\n\t\treturn Sys_EnumerateFiles_NT(fullmatch, start, start, func, parm, spath);\n\telse\n\t\treturn Sys_EnumerateFiles_9x(fullmatch, start, start, func, parm, spath);\n}\n\n//wide only. we let the windows api sort out the mess of file urls. system-wide consistancy.\nqboolean Sys_ResolveFileURL(const char *inurl, int inlen, char *out, int outlen)\n{\n\tchar *cp;\n\twchar_t wurl[MAX_PATH];\n\twchar_t local[MAX_PATH];\n\tDWORD grr;\n\tstatic HRESULT (WINAPI *pPathCreateFromUrlW)(PCWSTR pszUrl, PWSTR pszPath, DWORD *pcchPath, DWORD dwFlags);\n\tif (!pPathCreateFromUrlW)\n\t\tpPathCreateFromUrlW = Sys_GetAddressForName(Sys_LoadLibrary(\"Shlwapi.dll\", NULL), \"PathCreateFromUrlW\");\n\tif (!pPathCreateFromUrlW)\n\t\treturn false;\n\n\t//need to make a copy, because we can't terminate the inurl easily.\n\tcp = malloc(inlen+1);\n\tmemcpy(cp, inurl, inlen);\n\tcp[inlen] = 0;\n\twiden(wurl, sizeof(wurl), cp);\n\tfree(cp);\n\tgrr = sizeof(local)/sizeof(wchar_t);\n\tif (FAILED(pPathCreateFromUrlW(wurl, local, &grr, 0)))\n\t\treturn false;\n\tnarrowen(out, outlen, local);\n\twhile(*out)\n\t{\n\t\tif (*out == '\\\\')\n\t\t\t*out = '/';\n\t\tout++;\n\t}\n\treturn true;\n}\n/*\n===============================================================================\n\nSYSTEM IO\n\n===============================================================================\n*/\n\n/*\n================\nSys_MakeCodeWriteable\n================\n*/\n#if 0\nvoid Sys_MakeCodeWriteable (void *startaddr, unsigned long length)\n{\n\tDWORD  flOldProtect;\n\n//@@@ copy on write or just read-write?\n\tif (!VirtualProtect(startaddr, length, PAGE_EXECUTE_READWRITE, &flOldProtect))\n\t{\n\t\tchar str[1024];\n\n\t\tFormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\n\t\t\t\t\t\tNULL,\n\t\t\t\t\t\tGetLastError(),\n\t\t\t\t\t\tMAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),\n\t\t\t\t\t\tstr,\n\t\t\t\t\t\tsizeof(str),\n\t\t\t\t\t\tNULL);\n\t\tSys_Error(\"Protection change failed!\\nError %d: %s\\n\", (int)GetLastError(), str);\n\t}\n}\n#endif\n\nvoid Sys_DoFileAssociations(int elevated, const char *scheme);\nvoid Sys_Register_File_Associations_f(void)\n{\n\tif (!Q_strcasecmp(Cmd_Argv(1), \"quiet\"))\n\t\tSys_DoFileAssociations(2, fs_manifest->schemes);\t//current user only.\n\telse\n\t\tSys_DoFileAssociations(0, fs_manifest->schemes);\t//user+machine(with elevation on failure)\n}\n\nstatic void QDECL Sys_Priority_Changed(cvar_t *var, char *oldval)\n{\n\tHANDLE h = GetCurrentProcess();\n\tDWORD pc;\n\n\tif (var->ival >= 3)\n\t\tpc = REALTIME_PRIORITY_CLASS;\n\telse if (var->ival >= 2)\n\t\tpc = HIGH_PRIORITY_CLASS;\n\telse if (var->ival >= 1)\n\t\tpc = ABOVE_NORMAL_PRIORITY_CLASS;\n\telse if (var->ival >= 0)\n\t\tpc = NORMAL_PRIORITY_CLASS;\n\telse if (var->ival >= -1)\n\t\tpc = BELOW_NORMAL_PRIORITY_CLASS;\n\telse\n\t\tpc = IDLE_PRIORITY_CLASS;\n\n\tSetPriorityClass(h, pc);\n}\nstatic cvar_t sys_priority = CVARFCD(\"sys_highpriority\", \"0\", CVAR_NOTFROMSERVER, Sys_Priority_Changed, \"Controls the process priority\");\nstatic cvar_t sys_clocktype = CVARFCD(\"sys_clocktype\", \"\", CVAR_NOTFROMSERVER, Sys_ClockType_Changed, \"Controls which system clock to base timings from.\\n0: auto\\n1: timeGetTime (low precision).\\n2: QueryPerformanceCounter (may drift, desync between cpu cores, or run fast with longer uptimes depending on cpu(s) and windows version).\\n3: QueryPerformanceCounter-with-force-affinity (shouldn't drift, but may result in less cpu time available).\");\nstatic cvar_t sys_clockprecision = CVARFCD(\"sys_clockprecision\", \"1\", CVAR_NOTFROMSERVER, Sys_ClockPrecision_Changed, \"Attempts to control windows' interrupt interval, in milliseconds. This can cause windows to give better clock precision and shorter waits, but also more overhead from process rescheduling.\");\n/*\n================\nSys_Init\n================\n*/\nvoid Sys_Init (void)\n{\n//\tLARGE_INTEGER\tPerformanceFreq;\n//\tunsigned int\tlowpart, highpart;\n\n\tSys_QueryDesktopParameters();\n\n\tCvar_Register(&sys_priority, \"System vars\");\n\n\tCvar_Register(&sys_clocktype, \"System vars\");\n\tCvar_Register(&sys_clockprecision, \"System vars\");\n#ifndef SERVERONLY\n\tCvar_Register(&sys_disableWinKeys, \"System vars\");\n\tCvar_Register(&sys_disableTaskSwitch, \"System vars\");\n\tCmd_AddCommandD(\"sys_register_file_associations\", Sys_Register_File_Associations_f, \"Register FTE as the system handler for .bsp .mvd .qwd .dem files. Also register the URL protocol. This command will probably trigger a UAC prompt in Windows Vista and up. Deny it for current-user-only asociations (will also prevent listing in windows' 'default programs' ui due to microsoft bugs/limitations).\");\n\n#ifdef QUAKESPYAPI\n#ifndef CLIENTONLY\n\tif (!isDedicated && !COM_CheckParm(\"-nomutex\"))\n#else\n\tif (!COM_CheckParm(\"-nomutex\"))\t//we need to create a mutex to allow gamespy to realise that we're running, but it might not be desired as it prevents other clients from running too.\n#endif\n\t{\n\t\t// allocate a named semaphore on the client so the\n\t\t// front end can tell if it is alive\n\n\t\t// mutex will fail if semephore already exists\n\t\tqwclsemaphore = CreateMutex(\n\t\t\tNULL,         // Security attributes\n\t\t\t0,            // owner\n\t\t\t\"qwcl\"); // Semaphore name\n\t//\tif (!qwclsemaphore)\n\t//\t\tSys_Error (\"QWCL is already running on this system\");\n\t\tCloseHandle (qwclsemaphore);\n\n\t\tqwclsemaphore = CreateSemaphore(\n\t\t\tNULL,         // Security attributes\n\t\t\t0,            // Initial count\n\t\t\t1,            // Maximum count\n\t\t\t\"qwcl\"); // Semaphore name\n\t}\n#endif\n#endif\n\n\tSys_InitClock();\n\n#if _MSC_VER >= 1600 //msvc2010 runtime does not work on 9x any more. get rid of the deprecation warnings in later versions.\n\tWinNT = true;\n\n\tqwinvermaj = 6;\t//Hack: assume 6.2 (aka win8). this will block 16bit colour depths.\n\tqwinvermin = 2;\n#else\n\t{\n\t\tOSVERSIONINFO\tvinfo;\n\t\tvinfo.dwOSVersionInfoSize = sizeof(vinfo);\n\t\tif (!GetVersionEx (&vinfo))\n\t\t\tSys_Error (\"Couldn't get OS info\");\n\n\t\tif ((vinfo.dwMajorVersion < 4) ||\n\t\t\t(vinfo.dwPlatformId == VER_PLATFORM_WIN32s))\n\t\t{\n\t\t\tSys_Error (FULLENGINENAME \" requires at least Win95 or NT 4.0\");\n\t\t}\n\n\t\tif (vinfo.dwPlatformId == VER_PLATFORM_WIN32_NT)\n\t\t\tWinNT = true;\n\t\telse\n\t\t\tWinNT = false;\n\n\t\tqwinvermaj = vinfo.dwMajorVersion;\n\t\tqwinvermin = vinfo.dwMinorVersion;\n\t}\n#endif\n}\n\n\nvoid Sys_Shutdown(void)\n{\n\tint i;\n\tif (tevent)\n\t\tCloseHandle (tevent);\n\ttevent = NULL;\n\n\tif (qwclsemaphore)\n\t\tCloseHandle (qwclsemaphore);\n\tqwclsemaphore = NULL;\n\n\tfor (i = 0; i < MAX_NUM_ARGVS; i++)\n\t{\n\t\tif (!sys_argv[i])\n\t\t\tbreak;\n\t\tfree(sys_argv[i]);\n\t\tsys_argv[i] = NULL;\n\t}\n}\n\nvoid VARGS Sys_Error (const char *error, ...)\n{\n\tva_list\t\targptr;\n\tchar\t\ttext[1024];\n//\tDWORD\t\tdummy;\n\n \tva_start (argptr, error);\n\tvsnprintf (text, sizeof(text), error, argptr);\n\tva_end (argptr);\n\n\tCOM_WorkerAbort(text);\n\n#ifndef SERVERONLY\n\tSetHookState(false);\n\tHost_Shutdown ();\n#else\n\tSV_Shutdown();\n#endif\n\n\t{\n\t\twchar_t\t\twtext[1024];\n\t\twiden(wtext, sizeof(wtext), text);\n\t\tMessageBoxW(NULL, wtext, L\"Error\", 0);\n\t}\n\n#ifndef SERVERONLY\n\tCloseHandle (qwclsemaphore);\n\tSetHookState(false);\n#endif\n\n\tTL_Shutdown();\n\n#ifdef USE_MSVCRT_DEBUG\n\tif (_CrtDumpMemoryLeaks())\n\t\tOutputDebugStringA(\"Leaks detected\\n\");\n#endif\n\n\texit (EXIT_FAILURE);\n}\n\nvoid VARGS Sys_Printf (char *fmt, ...)\n{\n\tva_list\t\targptr;\n\tchar\t\ttext[4096];\n\tDWORD\t\tdummy;\n\n\tconchar_t msg[4096], *end, *in;\n\twchar_t wide[4096], *out;\n\tint wlen;\n\n\tif (!houtput && !debugout && !SSV_IsSubServer())\n\t\treturn;\n\n\tva_start (argptr,fmt);\n\tvsnprintf (text, sizeof(text), fmt, argptr);\n\tva_end (argptr);\n\n#ifdef SUBSERVERS\n\tif (SSV_IsSubServer())\n\t{\n\t\tSSV_PrintToMaster(text);\n\t\treturn;\n\t}\n#endif\n\n\tend = COM_ParseFunString(CON_WHITEMASK, text, msg, sizeof(msg), false);\n\tout = wide;\n\tin = msg;\n\twlen = 0;\n\tfor (in = msg; in < end && wlen+3 < countof(wide); )\n\t{\n\t\tunsigned int flags, cp;\n\t\tin = Font_Decode(in, &flags, &cp);\n\t\tif (!(flags & CON_HIDDEN))\n\t\t{\n\t\t\tcp = COM_DeQuake(cp);\n\t\t\tif (cp > 0xffff)\n\t\t\t{\n\t\t\t\tcp -= 0x10000;\n\t\t\t\t*out++ = 0xD800 | (cp>>10);\n\t\t\t\t*out++ = 0xDC00 | (cp&0x3ff);\n\t\t\t\twlen += 2;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t*out++ = cp;\n\t\t\t\twlen++;\n\t\t\t}\n\t\t}\n\t}\n\t*out = 0;\n\n\tif (debugout)\n\t\tOutputDebugStringW(wide);\n\tif (houtput)\n\t{\n\t\tif (WinNT)\n\t\t\tWriteConsoleW(houtput, wide, wlen, &dummy, NULL);\n\t\telse\n\t\t{\n\t\t\t//win95 doesn't support wide chars *sigh*. blank consoles suck. this conversion might loose stuff if the multibytes are too long.\n\t\t\tWriteConsole(houtput, text, WideCharToMultiByte(CP_ACP, 0, wide, wlen, text, sizeof(text), NULL, NULL), &dummy, NULL);\n\t\t}\n\t}\n}\n\nstatic unsigned int sys_interrupt_freq;\nstatic void Sys_ClockPrecision_Changed(cvar_t *var, char *oldval)\n{\n\tif (sys_interrupt_freq)\n\t\ttimeEndPeriod(sys_interrupt_freq);\n\tsys_interrupt_freq = 0;\n\n\tif (var && var->ival > 0)\n\t{\n\t\tsys_interrupt_freq = var->ival;\n\t\tif (TIMERR_NOERROR != timeBeginPeriod(sys_interrupt_freq) && oldval)\n\t\t\tCon_Printf(CON_ERROR\"%s: timeBeginPeriod(%u) failed.\\n\", var->name, sys_interrupt_freq);\n\t}\n}\n\nstatic quint64_t timer_qpc_frequency;\nstatic unsigned int timer_tgt_period;\nstatic quint64_t timer_basetime;\t//used by all clocks to bias them to starting at 0\nstatic DWORD_PTR timer_threadaffinity;\nstatic enum\n{\n\tCLOCK_TGT = 1,\n\tCLOCK_QPC = 2,\n\tCLOCK_QPC_SINGLE = 3,\n} timer_clocktype;\nstatic quint64_t Sys_GetClock(quint64_t *freq)\n{\n\tif (timer_clocktype == CLOCK_QPC || timer_clocktype == CLOCK_QPC_SINGLE)\n\t{\n\t\tstatic LARGE_INTEGER last;\n\t\tLARGE_INTEGER pc;\n\t\tQueryPerformanceCounter(&pc);\n\t\t*freq = timer_qpc_frequency;\n\t\tif (last.QuadPart <= pc.QuadPart)\t//never let it go backwards. multiple cpus are bad. ignore it till it catches up.\n\t\t\tlast.QuadPart = pc.QuadPart;\n\t\treturn last.QuadPart - timer_basetime;\n\t}\n\telse //if (timer_clocktype == CLOCK_TGT)\n\t{\n\t\tstatic DWORD last;\n\t\tDWORD cur = timeGetTime();\n\t\tif (last > cur)\n\t\t{\n\t\t\ttimer_basetime -= (quint64_t)1 << 32;\t//if it wrapped then try to compensate with the 64bit var that we do use so that Sys_DoubleTime won't suddenly go backwards\n\t\t\tif (host_initialized)\n\t\t\t\tCon_Printf(\"Clock wrapped\\n\");\n\t\t}\n\t\tlast = cur;\n\t\t*freq = 1000;\n\t\treturn last - timer_basetime;\n\t}\n}\nstatic void Sys_ClockType_Changed(cvar_t *var, char *oldval)\n{\n\tint newtype = var?var->ival:0;\n\tif (newtype <= 0)\n\t\tnewtype = CLOCK_QPC;\n\tif ((newtype == CLOCK_QPC || newtype == CLOCK_QPC_SINGLE) && !timer_qpc_frequency)\n\t\tnewtype = CLOCK_TGT;\t//QueryPerformanceCounter can fail on older versions of windows.\n\n\tif (newtype != timer_clocktype)\n\t{\n\t\tquint64_t oldtime, oldfreq;\n\t\tquint64_t newtime, newfreq;\n\n\n\t\toldtime = Sys_GetClock(&oldfreq);\n\t\t//kill old clock's evil global settings\n\t\tif (timer_clocktype == CLOCK_TGT)\n\t\t\ttimeEndPeriod(timer_tgt_period);\n\t\tif (timer_clocktype == CLOCK_QPC_SINGLE)\t//restore it.\n\t\t\tSetThreadAffinityMask(GetCurrentThread(), timer_threadaffinity);\n\t\t\n\t\t//override necessary global state for new clock mode\n\t\tif (newtype == CLOCK_QPC_SINGLE)\t\t\t//lock it down to a single core\n\t\t{\n\t\t\tDWORD_PTR old = timer_threadaffinity, sys, m;\n\t\t\tif (!old)\n\t\t\t{\t//try and get the default/existing value....\n\t\t\t\tGetProcessAffinityMask(GetCurrentProcess(), &old, &sys);\t//wtf? there is no thread call!\n\t\t\t\told &= sys;\n\t\t\t}\n\t\t\tfor (m = 1; old > m; m<<=1)\n\t\t\t\t;\t//scan to find the LAST cpu. First cpu often has overheads like interrupt handling, second is weird and unreliable (and often hyperthreads with the first).\n\t\t\told = SetThreadAffinityMask(GetCurrentThread(), old);\n\t\t\tif (!timer_threadaffinity)\n\t\t\t\ttimer_threadaffinity = old;\n\t\t}\n\t\tif (newtype == CLOCK_TGT)\n\t\t{\n\t\t\ttimeBeginPeriod(timer_tgt_period);\n\t\t\tif (host_initialized && timer_tgt_period > 1)\n\t\t\t\tCon_Printf(CON_WARNING\"System timer is limited to only %ums precision\\n\", timer_tgt_period);\n\t\t}\n\n\t\t//switch over internal state\n\t\ttimer_clocktype = newtype;\n\t\ttimer_basetime = 0;\n\t\tnewtime = Sys_GetClock(&newfreq);\n\n\t\t//and fix the bias to avoid crazy stalls due to offsets/frequencies changing.\n\t\ttimer_basetime = newtime - (newfreq * (oldtime) / oldfreq);\n\t}\n}\nstatic void Sys_InitClock(void)\n{\n\tquint64_t freq;\n\tTIMECAPS tc;\n\tLARGE_INTEGER t;\n\n\t//QPC timer\n\tif (QueryPerformanceFrequency(&t))\n\t\ttimer_qpc_frequency = t.QuadPart;\n\telse\n\t\ttimer_qpc_frequency = 0;\n\n\t//TGT timer\n\ttimeGetDevCaps(&tc, sizeof(tc));\n\ttimer_tgt_period = max(1,tc.wPeriodMin);\t//make sure its at least 1, because 0 is probably a bug...\n\n\t//calibrate it, and apply.\n\ttimer_basetime = Sys_GetClock(&freq);\n\tSys_ClockType_Changed(&sys_clocktype, NULL);\n\n\tSys_ClockPrecision_Changed(&sys_clocktype, NULL);\n}\ndouble Sys_DoubleTime (void)\n{\n\tquint64_t denum, num = Sys_GetClock(&denum);\n\treturn num / (long double)denum;\n}\nunsigned int Sys_Milliseconds (void)\n{\n\tquint64_t denum, num = Sys_GetClock(&denum);\n\tnum *= 1000;\n\treturn num / denum;\n}\n\nvoid Sys_Quit (void)\n{\n\tSys_ClockType_Changed(NULL, NULL);\n\tSys_ClockPrecision_Changed(NULL, NULL);\n\n#ifndef SERVERONLY\n\tSetHookState(false);\n\n\tHost_Shutdown ();\n\n\tSetHookState(false);\n#else\n\tSV_Shutdown();\n#endif\n\n\tTL_Shutdown();\n\n#ifdef RESTARTTEST\n\tlongjmp(restart_jmpbuf, 1);\n#endif\n\n#ifdef USE_MSVCRT_DEBUG\n\tif (_CrtDumpMemoryLeaks())\n\t\tOutputDebugStringA(\"Leaks detected\\n\");\n#endif\n\n\texit(EXIT_SUCCESS);\n}\n\n\n#if 0\n/*\n================\nSys_DoubleTime\n================\n*/\ndouble Sys_DoubleTime (void)\n{\n\tstatic int\t\t\tfirst = 1;\n\tstatic LARGE_INTEGER\t\tqpcfreq;\n\tLARGE_INTEGER\t\tPerformanceCount;\n\tstatic LONGLONG\t\t\toldcall;\n\tstatic LONGLONG\t\t\tfirsttime;\n\tLONGLONG\t\t\tdiff;\n\n\tQueryPerformanceCounter (&PerformanceCount);\n\tif (first)\n\t{\n\t\tfirst = 0;\n\t\tQueryPerformanceFrequency(&qpcfreq);\n\t\tfirsttime = PerformanceCount.QuadPart;\n\t\tdiff = 0;\n\t}\n\telse\n\t\tdiff = PerformanceCount.QuadPart - oldcall;\n\tif (diff >= 0)\n\t\toldcall = PerformanceCount.QuadPart;\n\treturn (oldcall - firsttime) / (double)qpcfreq.QuadPart;\n}\nunsigned int Sys_Milliseconds (void)\n{\n\treturn Sys_DoubleTime()*1000;\n}\n#elif 0\nunsigned int Sys_Milliseconds (void)\n{\n\tstatic DWORD starttime;\n\tstatic qboolean first = true;\n\tDWORD now;\n//\tdouble t;\n\n\tnow = timeGetTime();\n\n\tif (first) {\n\t\tfirst = false;\n\t\tstarttime = now;\n\t\treturn 0.0;\n\t}\n\t/*\n\tif (now < starttime) // wrapped?\n\t{\n\t\tdouble r;\n\t\tr = (now) + (LONG_MAX - starttime);\n\t\tstarttime = now;\n\t\treturn r;\n\t}\n\n\tif (now - starttime == 0)\n\t\treturn 0.0;\n*/\n\treturn (now - starttime);\n}\n\ndouble Sys_DoubleTime (void)\n{\n\treturn Sys_Milliseconds()/1000.f;\n}\n#endif\n\n\n\n/////////////////////////////////////////////////////////////\n//clipboard\nvoid Sys_Clipboard_PasteText(clipboardtype_t cbt, void (*callback)(void *cb, const char *utf8), void *ctx)\n{\n\tHANDLE\tclipboardhandle;\n\tchar *cliputf8;\n\tif (OpenClipboard(NULL))\n\t{\n\t\t//windows programs interpret CF_TEXT as ansi (aka: gibberish)\n\t\t//so grab utf-16 text and convert it to utf-8 if our console parsing is set to accept that.\n\t\tclipboardhandle = GetClipboardData(CF_UNICODETEXT);\n\t\tif (clipboardhandle)\n\t\t{\n\t\t\tunsigned short *clipWText = GlobalLock(clipboardhandle);\n\t\t\tif (clipWText)\n\t\t\t{\n\t\t\t\tunsigned int l, c;\n\t\t\t\tchar *utf8;\n\t\t\t\tfor (l = 0; clipWText[l]; l++)\n\t\t\t\t\t;\n\t\t\t\tl = l*4 + 1;\n\t\t\t\tutf8 = cliputf8 = malloc(l);\n\t\t\t\twhile(*clipWText)\n\t\t\t\t{\n\t\t\t\t\tunsigned int cp = *clipWText++;\n\t\t\t\t\tif (cp == '\\r' && *clipWText == '\\n')\t//bloomin microsoft.\n\t\t\t\t\t\tcp = *clipWText++;\n\t\t\t\t\tif (cp >= 0xD800u && cp <= 0xDBFFu)\n\t\t\t\t\t{\t//handle utf-16 surrogates\n\t\t\t\t\t\tif (*clipWText >= 0xDC00u && *clipWText <= 0xDFFFu)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcp = (cp&0x3ff)<<10;\n\t\t\t\t\t\t\tcp |= *clipWText++ & 0x3ff;\n\t\t\t\t\t\t\tcp += 0x10000;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tcp = 0xFFFDu;\n\t\t\t\t\t}\n\n\t\t\t\t\tc = utf8_encode(utf8, cp, l);\n\t\t\t\t\tif (!c)\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tl -= c;\n\t\t\t\t\tutf8 += c;\n\t\t\t\t}\n\t\t\t\t*utf8 = 0;\n\t\t\t\tcallback(ctx, cliputf8);\n\t\t\t\tfree(cliputf8);\n\t\t\t\tGlobalUnlock(clipboardhandle);\n\t\t\t\tCloseClipboard();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t//failed at the last hurdle\n\n\t\t\tGlobalUnlock(clipboardhandle);\n\t\t}\n\n\t\tclipboardhandle = GetClipboardData(CF_TEXT);\n\t\tif (clipboardhandle)\n\t\t{\n\t\t\tchar *clipText = GlobalLock(clipboardhandle);\n\t\t\tif (clipText)\n\t\t\t{\n\t\t\t\tunsigned int l, c;\n\t\t\t\tchar *utf8;\n\t\t\t\tfor (l = 0; clipText[l]; l++)\n\t\t\t\t\t;\n\t\t\t\tl = l*4 + 1;\n\t\t\t\tutf8 = cliputf8 = malloc(l);\n\t\t\t\twhile(*clipText)\n\t\t\t\t{\n\t\t\t\t\tif (clipText[0] == '\\r' && clipText[1] == '\\n')\t//bloomin microsoft.\n\t\t\t\t\t\tclipText++;\n\t\t\t\t\tc = utf8_encode(utf8, *clipText++, l);\n\t\t\t\t\tif (!c)\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tl -= c;\n\t\t\t\t\tutf8 += c;\n\t\t\t\t}\n\t\t\t\t*utf8 = 0;\n\t\t\t\tcallback(ctx, cliputf8);\n\t\t\t\tfree(cliputf8);\n\t\t\t}\n\n\t\t\t//failed at the last hurdle\n\n\t\t\tGlobalUnlock(clipboardhandle);\n\t\t}\n\t\tCloseClipboard();\n\t}\n}\nvoid Sys_SaveClipboard(clipboardtype_t cbt, const char *text)\n{\n\tHANDLE glob;\n\tchar *temp;\n\tunsigned short *tempw;\n\tunsigned int codepoint;\n\tif (!OpenClipboard(NULL))\n\t\treturn;\n\tEmptyClipboard();\n\n\tif (WinNT)\n\t{\n\t\tglob = GlobalAlloc(GMEM_MOVEABLE, (strlen(text) + 1)*4);\n\t\tif (glob)\n\t\t{\n\t\t\ttempw = GlobalLock(glob);\n\t\t\tif (tempw != NULL)\n\t\t\t{\n\t\t\t\tint error;\n\t\t\t\twhile(*text)\n\t\t\t\t{\n\t\t\t\t\tcodepoint = unicode_decode(&error, text, &text, false);\n\t\t\t\t\tif (codepoint == '\\n')\n\t\t\t\t\t{\t//windows is stupid and annoying.\n\t\t\t\t\t\t*tempw++ = '\\r';\n\t\t\t\t\t\t*tempw++ = '\\n';\n\t\t\t\t\t}\n\t\t\t\t\telse if (codepoint > 0xffff)\n\t\t\t\t\t{\t//and badly designed, too.\n\t\t\t\t\t\tcodepoint -= 0x10000;\n\t\t\t\t\t\t*tempw++ = 0xD800 | ((codepoint>>10)&0x3ff);\n\t\t\t\t\t\t*tempw++ = 0xDC00 | (codepoint&0x3ff);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\t*tempw++ = codepoint;\n\t\t\t\t}\n\t\t\t\t*tempw = 0;\n\t\t\t\tGlobalUnlock(glob);\n\t\t\t\tSetClipboardData(CF_UNICODETEXT, glob);\n\t\t\t}\n\t\t\telse\n\t\t\t\tGlobalFree(glob);\n\t\t}\n\t}\n\telse\n\t{\n\t\tglob = GlobalAlloc(GMEM_MOVEABLE, strlen(text)*2 + 1);\n\t\tif (glob)\n\t\t{\n\t\t\t//yes, quake chars will get mangled horribly.\n\t\t\ttemp = GlobalLock(glob);\n\t\t\tif (temp != NULL)\n\t\t\t{\n\t\t\t\tint error;\n\t\t\t\twhile (*text)\n\t\t\t\t{\n\t\t\t\t\tcodepoint = unicode_decode(&error, text, &text, false);\n\t\t\t\t\tif (codepoint == '\\n')\n\t\t\t\t\t\t*temp++ = '\\r';\n\t\t\t\t\t*temp++ = codepoint;\n\t\t\t\t}\n\t\t\t\t*temp = 0;\n\t\t\t\tstrcpy(temp, text);\n\t\t\t\tGlobalUnlock(glob);\n\t\t\t\tSetClipboardData(CF_TEXT, glob);\n\t\t\t}\n\t\t\telse\n\t\t\t\tGlobalFree(glob);\n\t\t}\n\t}\n\n\tCloseClipboard();\n}\n\n\n//end of clipboard\n/////////////////////////////////////////////////////////////\n\n\n\n\n/////////////////////////////////////////\n//the system console stuff\n\nchar *Sys_ConsoleInput (void)\n{\n\tstatic char\ttext[256];\n\tstatic int\t\tlen;\n\tINPUT_RECORD\trecs[1024];\n//\tint\t\tcount;\n\tint\t\tch;\n\tDWORD numevents, numread, dummy=0;\n\tHANDLE\tth;\n\tchar\t*clipText, *textCopied;\n\n#ifdef SUBSERVERS\n\tif (SSV_IsSubServer())\n\t\treturn NULL;\n#endif\n\n\n\tif (!hinput)\n\t\treturn NULL;\n\n\tfor ( ;; )\n\t{\n\t\tif (!GetNumberOfConsoleInputEvents (hinput, &numevents))\n\t\t\tSys_Error (\"Error getting # of console events\");\n\n\t\tif (numevents <= 0)\n\t\t\tbreak;\n\n\t\tif (WinNT)\n\t\t{\n\t\t\tif (!ReadConsoleInputW(hinput, recs, 1, &numread))\n\t\t\t\tSys_Error (\"Error reading console input\");\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (!ReadConsoleInputA(hinput, recs, 1, &numread))\n\t\t\t\tSys_Error (\"Error reading console input\");\n\t\t}\n\n\t\tif (numread != 1)\n\t\t\tSys_Error (\"Couldn't read console input\");\n\n\t\tif (recs[0].EventType == KEY_EVENT)\n\t\t{\n\t\t\tif (recs[0].Event.KeyEvent.bKeyDown)\n\t\t\t{\n\t\t\t\tch = recs[0].Event.KeyEvent.uChar.UnicodeChar;\n\n\t\t\t\tswitch (ch)\n\t\t\t\t{\n\t\t\t\t\tcase '\\r':\n\t\t\t\t\t\tWriteFile(houtput, \"\\r\\n\", 2, &dummy, NULL);\n\n\t\t\t\t\t\tif (len)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttext[len] = 0;\n\t\t\t\t\t\t\tlen = 0;\n\t\t\t\t\t\t\treturn text;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase '\\b':\n\t\t\t\t\t\tif (len)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tlen--;\n\t\t\t\t\t\t\tWriteFile(houtput, \"\\b \\b\", 3, &dummy, NULL);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tif (((ch=='V' || ch=='v') && (recs[0].Event.KeyEvent.dwControlKeyState &\n\t\t\t\t\t\t\t(LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED))) || ((recs[0].Event.KeyEvent.dwControlKeyState\n\t\t\t\t\t\t\t& SHIFT_PRESSED) && (recs[0].Event.KeyEvent.wVirtualKeyCode\n\t\t\t\t\t\t\t==VK_INSERT))) {\n\t\t\t\t\t\t\tif (OpenClipboard(NULL)) {\n\t\t\t\t\t\t\t\tth = GetClipboardData(CF_TEXT);\n\t\t\t\t\t\t\t\tif (th) {\n\t\t\t\t\t\t\t\t\tclipText = GlobalLock(th);\n\t\t\t\t\t\t\t\t\tif (clipText) {\n\t\t\t\t\t\t\t\t\t\tint i;\n\t\t\t\t\t\t\t\t\t\ttextCopied = BZ_Malloc(GlobalSize(th)+1);\n\t\t\t\t\t\t\t\t\t\tstrcpy(textCopied, clipText);\n/* Substitutes a NULL for every token */strtok(textCopied, \"\\n\\r\\b\");\n\t\t\t\t\t\t\t\t\t\ti = strlen(textCopied);\n\t\t\t\t\t\t\t\t\t\tif (i+len>=256)\n\t\t\t\t\t\t\t\t\t\t\ti=256-len;\n\t\t\t\t\t\t\t\t\t\tif (i>0) {\n\t\t\t\t\t\t\t\t\t\t\ttextCopied[i]=0;\n\t\t\t\t\t\t\t\t\t\t\ttext[len]=0;\n\t\t\t\t\t\t\t\t\t\t\tstrcat(text, textCopied);\n\t\t\t\t\t\t\t\t\t\t\tlen+=dummy;\n\t\t\t\t\t\t\t\t\t\t\tWriteFile(houtput, textCopied, i, &dummy, NULL);\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\tBZ_Free(textCopied);\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tGlobalUnlock(th);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tCloseClipboard();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else if (ch >= ' ')\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\twchar_t wch = ch;\n\t\t\t\t\t\t\tif (WinNT)\n\t\t\t\t\t\t\t\tWriteConsoleW(houtput, &wch, 1, &dummy, NULL);\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tchar mb[8];\t//hopefully ucs-2-only will be sufficient...\n\t\t\t\t\t\t\t\tWriteConsoleA(houtput, mb, WideCharToMultiByte(CP_ACP, 0, &wch, 1, mb, sizeof(mb), NULL, NULL), &dummy, NULL);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tlen += utf8_encode(text+len, ch, sizeof(text)-1-len);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn NULL;\n}\n\nBOOL WINAPI HandlerRoutine (DWORD dwCtrlType)\n{\n\tswitch (dwCtrlType)\n\t{\n\t\tcase CTRL_C_EVENT:\n\t\tcase CTRL_BREAK_EVENT:\n\t\tcase CTRL_CLOSE_EVENT:\n\t\tcase CTRL_LOGOFF_EVENT:\n\t\tcase CTRL_SHUTDOWN_EVENT:\n\t\t\tCbuf_AddText (\"quit\\n\", RESTRICT_LOCAL);\n\t\t\treturn true;\n\t}\n\n\treturn false;\n}\n\n#ifndef CP_UTF8\n#define CP_UTF8                   65001\n#endif\nqboolean Sys_InitTerminal (void)\n{\n\tDWORD m;\n\n\tif (SSV_IsSubServer())\n\t\treturn true;\t//just pretend we did\n\n\tif (!AllocConsole())\n\t\treturn false;\n\n#ifndef SERVERONLY\n\tif (qwclsemaphore)\n\t{\n\t\tCloseHandle(qwclsemaphore);\n\t\tqwclsemaphore = NULL;\n\t}\n#endif\n\n\tSetConsoleCtrlHandler (HandlerRoutine, TRUE);\n\tSetConsoleCP(CP_UTF8);\n\tSetConsoleOutputCP(CP_UTF8);\n\tSetConsoleTitleW (_L(FULLENGINENAME) L\" dedicated server\");\n\tif (isPlugin)\n\t{\n\t\thinput = CreateFileA(\"CONIN$\",GENERIC_READ|GENERIC_WRITE,FILE_SHARE_READ,0,OPEN_EXISTING,0,0);\n\t\thoutput = CreateFileA(\"CONOUT$\",GENERIC_READ|GENERIC_WRITE,FILE_SHARE_READ,0,OPEN_EXISTING,0,0);\n\t}\n\telse\n\t{\n\t\thinput = GetStdHandle (STD_INPUT_HANDLE);\n\t\thoutput = GetStdHandle (STD_OUTPUT_HANDLE);\n\t}\n\n\tif (hinput)\n\t{\n\t\tGetConsoleMode(hinput, &m);\n\t\tSetConsoleMode(hinput, m | 0x40 | 0x80);\n\t}\n\n\treturn true;\n}\nvoid Sys_CloseTerminal (void)\n{\n\tFreeConsole();\n\n\thinput = NULL;\n\thoutput = NULL;\n}\n\n\n//\n////////////////////////////\n\nqboolean QCExternalDebuggerCommand(char *text);\nvoid Sys_SendKeyEvents (void)\n{\n    MSG        msg;\n\n\tif (isPlugin)\n\t{\n\t\tDWORD avail;\n\t\tstatic char\ttext[256], *nl;\n\t\tstatic int textpos = 0;\n\n\t\tHANDLE input = GetStdHandle(STD_INPUT_HANDLE);\n\t\tif (!PeekNamedPipe(input, NULL, 0, NULL, &avail, NULL))\n\t\t{\n\t\t\twantquit = true;\n\t\t\tCmd_ExecuteString(\"quit force\", RESTRICT_LOCAL);\n\t\t}\n\t\telse if (avail)\n\t\t{\n\t\t\tif (avail > sizeof(text)-1-textpos)\n\t\t\t\tavail = sizeof(text)-1-textpos;\n\t\t\tif (ReadFile(input, text+textpos, avail, &avail, NULL))\n\t\t\t{\n\t\t\t\ttextpos += avail;\n\t\t\t\tif (textpos > sizeof(text)-1)\n\t\t\t\t\tSys_Error(\"No.\");\n\t\t\t}\n\t\t}\n\t\twhile (textpos)\n\t\t{\n\t\t\ttext[textpos] = 0;\n\t\t\tnl = strchr(text, '\\n');\n\t\t\tif (nl)\n\t\t\t{\n\t\t\t\t*nl++ = 0;\n\t\t\t\tif (qrenderer <= QR_NONE && !strncmp(text, \"vid_recenter \", 13))\n\t\t\t\t{\n\t\t\t\t\tCmd_TokenizeString(text, false, false);\n\t\t\t\t\tsys_parentleft = strtoul(Cmd_Argv(1), NULL, 0);\n\t\t\t\t\tsys_parenttop = strtoul(Cmd_Argv(2), NULL, 0);\n\t\t\t\t\tsys_parentwidth = strtoul(Cmd_Argv(3), NULL, 0);\n\t\t\t\t\tsys_parentheight = strtoul(Cmd_Argv(4), NULL, 0); \n\t\t\t\t\tsys_parentwindow = (HWND)(qintptr_t)strtoull(Cmd_Argv(5), NULL, 16);\n\t\t\t\t}\n#if !defined(CLIENTONLY) || defined(CSQC_DAT) || defined(MENU_DAT)\n\t\t\t\telse if (QCExternalDebuggerCommand(text))\n\t\t\t\t\t/*handled elsewhere*/;\n#endif\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tCbuf_AddText(text, RESTRICT_LOCAL);\n\t\t\t\t\tCbuf_AddText(\"\\n\", RESTRICT_LOCAL);\n\t\t\t\t}\n\t\t\t\tmemmove(text, nl, textpos - (nl - text));\n\t\t\t\ttextpos -= (nl - text);\n\t\t\t}\n\t\t\telse\n\t\t\t\tbreak;\n\t\t}\n\t}\n\tif (isDedicated)\n\t{\n#ifndef CLIENTONLY\n\t\tSV_GetConsoleCommands ();\n#endif\n\t\treturn;\n\t}\n\n\tif (gammapending)\n\t{\n\t\tgammapending -= host_frametime;\n\t\tif (gammapending < host_frametime)\n\t\t{\n\t\t\tgammapending = 0;\n\t\t\tCvar_ForceCallback(&v_gamma);\n\t\t}\n\t}\n\n\twhile (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE))\n\t{\n\t// we always update if there are any event, even if we're paused\n\t\t//if (!GetMessage (&msg, NULL, 0, 0))\n\t\t//\tbreak;\n//\t\t\tSys_Quit ();\n//\t\tif (TranslateMessage (&msg))\n//\t\t\tcontinue;\n      \tDispatchMessage (&msg);\n\t}\n}\n\n\nvoid Sys_ServerActivity(void)\n{\n#ifndef SERVERONLY\n\tif (GetActiveWindow() != mainwindow)\n\t\tFlashWindow(mainwindow, true);\n#endif\n}\n\n/*\n==============================================================================\n\n WINDOWS CRAP\n\n==============================================================================\n*/\n\n/*\n==================\nWinMain\n==================\n*/\nvoid SleepUntilInput (int time)\n{\n\n\tMsgWaitForMultipleObjects(1, &tevent, FALSE, time, QS_ALLINPUT);\n}\n\n\n\n\n\n\n\n\nqboolean Sys_Startup_CheckMem(quakeparms_t *parms)\n{\n\treturn true;\n}\n\n/*\n==================\nWinMain\n==================\n*/\nHINSTANCE\tglobal_hInstance;\nint\t\t\tglobal_nCmdShow;\nHWND\t\thwnd_dialog;\n\nstatic const IID qIID_IShellLinkW\t= {0x000214F9L, 0, 0, {0xc0,0,0,0,0,0,0,0x46}};\n\n#include <shlobj.h>\n#if defined(_MSC_VER) && _MSC_VER <= 1200\n#define pSHBrowseForFolderW SHBrowseForFolderW\n#define pSHGetPathFromIDListW SHGetPathFromIDListW\n#define pSHGetSpecialFolderPathW SHGetSpecialFolderPathW\n#define pShell_NotifyIconW Shell_NotifyIconW\nvoid Win7_Init(void)\n{\n}\n#else\n\ntypedef struct qSHARDAPPIDINFOLINK {\n  IShellLinkW *psl;\n  PCWSTR     pszAppID;\n} qSHARDAPPIDINFOLINK;\n\n#define qSHARD_APPIDINFOLINK 0x00000007\n\ntypedef struct {\n  GUID  fmtid;\n  DWORD pid;\n} qPROPERTYKEY;\n\ntypedef struct qIPropertyStore qIPropertyStore;\nstruct qIPropertyStore\n{\n    CONST_VTBL struct\n\t{\n\t\t/*IUnknown*/\n\t\tHRESULT ( STDMETHODCALLTYPE *QueryInterface )(\n\t\t\t\tqIPropertyStore * This,\n\t\t\t\tREFIID riid,\n\t\t\t\tvoid **ppvObject);\n\t\tULONG ( STDMETHODCALLTYPE *AddRef )(\n\t\t\t\tqIPropertyStore * This);\n\t\tULONG ( STDMETHODCALLTYPE *Release )(\n\t\t\t\tqIPropertyStore * This);\n\n\t\t/*property store stuff*/\n\t\tHRESULT ( STDMETHODCALLTYPE *GetCount)(\n\t\t\t\tqIPropertyStore * This,\n\t\t\t\tULONG *count);\n\n\t\tHRESULT  ( STDMETHODCALLTYPE *GetAt)(\n\t\t\t\tqIPropertyStore * This,\n\t\t\t\tDWORD prop,\n\t\t\t\tqPROPERTYKEY * key);\n\n\t\tHRESULT  ( STDMETHODCALLTYPE *GetValue)(\n\t\t\t\tqIPropertyStore * This,\n\t\t\t\tqPROPERTYKEY * key,\n\t\t\t\tPROPVARIANT * val);\n\n\t\tHRESULT  ( STDMETHODCALLTYPE *SetValue)(\n\t\t\t\tqIPropertyStore * This,\n\t\t\t\tqPROPERTYKEY * key,\n\t\t\t\tPROPVARIANT * val);\n\n\t\tHRESULT  ( STDMETHODCALLTYPE *Commit)(\n\t\t\t\tqIPropertyStore * This);\n\t} *lpVtbl;\n};\n\nstatic const IID qIID_IPropertyStore = {0x886d8eeb, 0x8cf2, 0x4446, {0x8d, 0x02, 0xcd, 0xba, 0x1d, 0xbd, 0xcf, 0x99}};\n\n#define qIObjectArray IUnknown \nstatic const IID qIID_IObjectArray = {0x92ca9dcd, 0x5622, 0x4bba, {0xa8,0x05,0x5e,0x9f,0x54,0x1b,0xd8,0xc9}};\n\ntypedef struct qIObjectCollection\n{\n    struct qIObjectCollectionVtbl\n\t{\n\t\tHRESULT ( __stdcall *QueryInterface )(\n\t\t\t/* [in] IShellLink*/ void *This,\n\t\t\t/* [in] */ const GUID * const riid,\n\t\t\t/* [out] */ void **ppvObject);\n\n\t\tULONG ( __stdcall *AddRef )(\n\t\t\t/* [in] IShellLink*/ void *This);\n\n\t\tULONG ( __stdcall *Release )(\n\t\t\t/* [in] IShellLink*/ void *This);\n\n\t\tHRESULT ( __stdcall *GetCount )(\n\t\t\t/* [in] IShellLink*/ void *This,\n\t\t\t/* [out] */ UINT *pcObjects);\n\n\t\tHRESULT ( __stdcall *GetAt )(\n\t\t\t/* [in] IShellLink*/ void *This,\n\t\t\t/* [in] */ UINT uiIndex,\n\t\t\t/* [in] */ const GUID * const riid,\n\t\t\t/* [iid_is][out] */ void **ppv);\n\n\t\tHRESULT ( __stdcall *AddObject )(\n\t\t\t/* [in] IShellLink*/ void *This,\n\t\t\t/* [in] */ void *punk);\n\n\t\tHRESULT ( __stdcall *AddFromArray )(\n\t\t\t/* [in] IShellLink*/ void *This,\n\t\t\t/* [in] */ qIObjectArray *poaSource);\n\n\t\tHRESULT ( __stdcall *RemoveObjectAt )(\n\t\t\t/* [in] IShellLink*/ void *This,\n\t\t\t/* [in] */ UINT uiIndex);\n\n\t\tHRESULT ( __stdcall *Clear )(\n\t\t\t/* [in] IShellLink*/ void *This);\n\t} *lpVtbl;\n} qIObjectCollection;\nstatic const IID qIID_IObjectCollection = {0x5632b1a4, 0xe38a, 0x400a, {0x92,0x8a,0xd4,0xcd,0x63,0x23,0x02,0x95}};\nstatic const CLSID qCLSID_EnumerableObjectCollection = {0x2d3468c1, 0x36a7, 0x43b6, {0xac,0x24,0xd3,0xf0,0x2f,0xd9,0x60,0x7a}};\n\ntypedef struct qICustomDestinationList\n{\n\tstruct qICustomDestinationListVtbl\n\t{\n\t\tHRESULT ( __stdcall *QueryInterface ) (\n\t\t\t/* [in] ICustomDestinationList*/ void *This,\n\t\t\t/* [in] */  const GUID * const riid,\n\t\t\t/* [out] */ void **ppvObject);\n\n\t\tULONG ( __stdcall *AddRef )(\n\t\t\t/* [in] ICustomDestinationList*/ void *This);\n\n\t\tULONG ( __stdcall *Release )(\n\t\t\t/* [in] ICustomDestinationList*/ void *This);\n\n\t\tHRESULT ( __stdcall *SetAppID )(\n\t\t\t/* [in] ICustomDestinationList*/ void *This,\n\t\t\t/* [string][in] */ LPCWSTR pszAppID);\n\n\t\tHRESULT ( __stdcall *BeginList )(\n\t\t\t/* [in] ICustomDestinationList*/ void *This,\n\t\t\t/* [out] */ UINT *pcMinSlots,\n\t\t\t/* [in] */  const GUID * const riid,\n\t\t\t/* [out] */ void **ppv);\n\n\t\tHRESULT ( __stdcall *AppendCategory )(\n\t\t\t/* [in] ICustomDestinationList*/ void *This,\n\t\t\t/* [string][in] */ LPCWSTR pszCategory,\n\t\t\t/* [in] IObjectArray*/ void *poa);\n\n\t\tHRESULT ( __stdcall *AppendKnownCategory )(\n\t\t\t/* [in] ICustomDestinationList*/ void *This,\n\t\t\t/* [in] KNOWNDESTCATEGORY*/ int category);\n\n\t\tHRESULT ( __stdcall *AddUserTasks )(\n\t\t\t/* [in] ICustomDestinationList*/ void *This,\n\t\t\t/* [in] IObjectArray*/ void *poa);\n\n\t\tHRESULT ( __stdcall *CommitList )(\n\t\t\t/* [in] ICustomDestinationList*/ void *This);\n\n\t\tHRESULT ( __stdcall *GetRemovedDestinations )(\n\t\t\t/* [in] ICustomDestinationList*/ void *This,\n\t\t\t/* [in] */ const IID * const riid,\n\t\t\t/* [out] */ void **ppv);\n\n\t\tHRESULT ( __stdcall *DeleteList )(\n\t\t\t/* [in] ICustomDestinationList*/ void *This,\n\t\t\t/* [string][unique][in] */ LPCWSTR pszAppID);\n\n\t\tHRESULT ( __stdcall *AbortList )(\n\t\t\t/* [in] ICustomDestinationList*/ void *This);\n\n\t} *lpVtbl;\n} qICustomDestinationList;\n\nstatic const IID qIID_ICustomDestinationList = {0x6332debf, 0x87b5, 0x4670, {0x90,0xc0,0x5e,0x57,0xb4,0x08,0xa4,0x9e}};\nstatic const CLSID qCLSID_DestinationList = {0x77f10cf0, 0x3db5, 0x4966, {0xb5,0x20,0xb7,0xc5,0x4f,0xd3,0x5e,0xd6}};\n\n#define WIN7_APPNAME L\"FTEQuake\"\n\nstatic IShellLinkW *CreateShellLink(char *command, char *target, char *title, char *desc)\n{\n\tHRESULT hr;\n\tIShellLinkW *link;\n\tqIPropertyStore *prop_store;\n\n\tWCHAR buf[1024];\n\tchar tmp[1024], *s;\n\n\t// Get a pointer to the IShellLink interface.\n\thr = CoCreateInstance(&CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, &qIID_IShellLinkW, (void**)&link);\n\tif (FAILED(hr))\n\t\treturn NULL;\n\n\tGetModuleFileNameW(NULL, buf, countof(buf)-1);\n\tIShellLinkW_SetIconLocation(link, buf, 0);  /*grab the first icon from our exe*/\n\tIShellLinkW_SetPath(link, buf); /*program to run*/\n\n\tQ_strncpyz(tmp, com_gamepath, sizeof(tmp));\n\t/*normalize the gamedir, so we don't end up with the same thing multiple times*/\n\tfor(s = tmp; *s; s++)\n\t{\n\t\tif (*s == '\\\\')\n\t\t\t*s = '/';\n\t\telse\n\t\t\t*s = tolower(*s);\n\t}\n\tIShellLinkW_SetArguments(link, widen(buf, sizeof(buf), va(\"%s \\\"%s\\\" -basedir \\\"%s\\\"\", command, target, tmp))); /*args*/\n\tIShellLinkW_SetDescription(link, widen(buf, sizeof(buf), desc));  /*tooltip*/\n\n\n\thr = IShellLinkW_QueryInterface(link, &qIID_IPropertyStore, (void**)&prop_store);\n\n\tif(SUCCEEDED(hr))\n\t{\n\t\tPROPVARIANT pv;\n\t\tqPROPERTYKEY PKEY_Title;\n\t\tpv.vt=VT_LPSTR;\n\t\tpv.pszVal=title; /*item text*/\n\t\tCLSIDFromString(L\"{F29F85E0-4FF9-1068-AB91-08002B27B3D9}\", &(PKEY_Title.fmtid));\n\t\tPKEY_Title.pid=2;\n\t\thr = prop_store->lpVtbl->SetValue(prop_store, &PKEY_Title, &pv);\n\t\thr = prop_store->lpVtbl->Commit(prop_store);\n\t\tprop_store->lpVtbl->Release(prop_store);\n\t}\n\n\treturn link;\n}\n\nvoid Sys_RecentServer(char *command, char *target, char *title, char *desc)\n{\n\tqSHARDAPPIDINFOLINK appinfo;\n\tIShellLinkW *link;\n\n\tlink = CreateShellLink(command, target, title, desc);\n\tif (!link)\n\t\treturn;\n\n\tappinfo.pszAppID=WIN7_APPNAME;\n\tappinfo.psl=link;\n\tSHAddToRecentDocs(qSHARD_APPIDINFOLINK, &appinfo);\n\tIShellLinkW_Release(link);\n}\n\n\ntypedef struct {\n  LPCWSTR            pcszFile;\n  LPCWSTR            pcszClass;\n  int oaifInFlags;\n} qOPENASINFO;\nHRESULT (WINAPI *pSHOpenWithDialog)(HWND hwndParent, const qOPENASINFO *poainfo);\n\n\nLPITEMIDLIST (STDAPICALLTYPE *pSHBrowseForFolderW)(LPBROWSEINFOW lpbi);\nBOOL (STDAPICALLTYPE *pSHGetPathFromIDListW)(LPCITEMIDLIST pidl, LPWSTR pszPath);\nBOOL (STDAPICALLTYPE *pSHGetSpecialFolderPathW)(HWND hwnd, LPWSTR pszPath, int csidl, BOOL fCreate);\nBOOL (STDAPICALLTYPE *pShell_NotifyIconW)(DWORD dwMessage, PNOTIFYICONDATAW lpData);\nvoid Win7_Init(void)\n{\n\tHANDLE h;\n\tHRESULT (WINAPI *pSetCurrentProcessExplicitAppUserModelID)(PCWSTR AppID);\n\n\th = LoadLibraryU(\"shell32.dll\");\n\tif (h)\n\t{\n\t\tpSHBrowseForFolderW\t\t\t= (void*)GetProcAddress(h, \"SHBrowseForFolderW\");\n\t\tpSHGetPathFromIDListW\t\t= (void*)GetProcAddress(h, \"SHGetPathFromIDListW\");\n\t\tpSHGetSpecialFolderPathW\t= (void*)GetProcAddress(h, \"SHGetSpecialFolderPathW\");\n\t\tpShell_NotifyIconW\t\t\t= (void*)GetProcAddress(h, \"Shell_NotifyIconW\");\n\n\t\tpSHOpenWithDialog = (void*)GetProcAddress(h, \"SHOpenWithDialog\");\n\n\t\tpSetCurrentProcessExplicitAppUserModelID = (void*)GetProcAddress(h, \"SetCurrentProcessExplicitAppUserModelID\");\n\t\tif (pSetCurrentProcessExplicitAppUserModelID)\n\t\t\tpSetCurrentProcessExplicitAppUserModelID(WIN7_APPNAME);\n\t}\n}\n\nvoid Win7_TaskListInit(void)\n{\n\tqICustomDestinationList *cdl;\n\tqIObjectCollection *col;\n\tqIObjectArray *arr;\n\tIShellLinkW *link;\n\tCoInitialize(NULL);\n\tif (SUCCEEDED(CoCreateInstance(&qCLSID_DestinationList, NULL, CLSCTX_INPROC_SERVER, &qIID_ICustomDestinationList, (void**)&cdl)))\n\t{\n\t\tUINT minslots;\n\t\tIUnknown *removed;\n\t\tcdl->lpVtbl->BeginList(cdl, &minslots, &qIID_IObjectArray, (void**)&removed);\n\n\t\tif (SUCCEEDED(CoCreateInstance(&qCLSID_EnumerableObjectCollection, NULL, CLSCTX_INPROC_SERVER, &qIID_IObjectCollection, (void**)&col)))\n\t\t{\n\n\t\t\tswitch(M_GameType())\n\t\t\t{\n\t\t\tcase MGT_QUAKE1:\n\t\t\t\tlink = CreateShellLink(\"+menu_servers\", \"\", \"Server List\", \"Pick a multiplayer server to join\");\n\t\t\t\tif (link)\n\t\t\t\t{\n\t\t\t\t\tcol->lpVtbl->AddObject(col, (IUnknown*)link);\n\t\t\t\t\tlink->lpVtbl->Release(link);\n\t\t\t\t}\n\t\t\t\tlink = CreateShellLink(\"+map start\", \"\", \"Start New Game (Quake)\", \"Begin a new single-player game\");\n\t\t\t\tif (link)\n\t\t\t\t{\n\t\t\t\t\tcol->lpVtbl->AddObject(col, (IUnknown*)link);\n\t\t\t\t\tlink->lpVtbl->Release(link);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase MGT_QUAKE2:\n\t\t\t\tlink = CreateShellLink(\"+menu_servers\", \"\", \"Quake2 Server List\", \"Pick a multiplayer server to join\");\n\t\t\t\tif (link)\n\t\t\t\t{\n\t\t\t\t\tcol->lpVtbl->AddObject(col, (IUnknown*)link);\n\t\t\t\t\tlink->lpVtbl->Release(link);\n\t\t\t\t}\n\t\t\t\tlink = CreateShellLink(\"+map unit1\", \"\", \"Start New Game (Quake2)\", \"Begin a new game\");\n\t\t\t\tif (link)\n\t\t\t\t{\n\t\t\t\t\tcol->lpVtbl->AddObject(col, (IUnknown*)link);\n\t\t\t\t\tlink->lpVtbl->Release(link);\n\t\t\t\t}\n\t\t\t\tbreak;\n#ifdef HEXEN2\n\t\t\tcase MGT_HEXEN2:\n\t\t\t\tlink = CreateShellLink(\"+menu_servers\", \"\", \"Hexen2 Server List\", \"Pick a multiplayer server to join\");\n\t\t\t\tif (link)\n\t\t\t\t{\n\t\t\t\t\tcol->lpVtbl->AddObject(col, (IUnknown*)link);\n\t\t\t\t\tlink->lpVtbl->Release(link);\n\t\t\t\t}\n\t\t\t\tlink = CreateShellLink(\"+map demo1\", \"\", \"Start New Game (Hexen2)\", \"Begin a new game\");\n\t\t\t\tif (link)\n\t\t\t\t{\n\t\t\t\t\tcol->lpVtbl->AddObject(col, (IUnknown*)link);\n\t\t\t\t\tlink->lpVtbl->Release(link);\n\t\t\t\t}\n\t\t\t\tbreak;\n#endif\n\t\t\t}\n\n\t\t\tif (SUCCEEDED(col->lpVtbl->QueryInterface(col, &qIID_IObjectArray, (void**)&arr)))\n\t\t\t{\n\t\t\t\tcdl->lpVtbl->AddUserTasks(cdl, arr);\n\t\t\t\tarr->lpVtbl->Release(arr);\n\t\t\t}\n\t\t\tcol->lpVtbl->Release(col);\n\t\t}\n\t\tcdl->lpVtbl->AppendKnownCategory(cdl, 1);\n\t\tcdl->lpVtbl->CommitList(cdl);\n\t\tcdl->lpVtbl->Release(cdl);\n\t}\n}\n#endif\n\n//using this like posix' access function, but with much more code, microsoftisms, and no errno codes/info\n//no, I don't really have a clue why it needs to be so long.\n//#include <svrapi.h>\n#ifndef ACCESS_READ\t\n#define         ACCESS_READ     0x1\n#define         ACCESS_WRITE    0x2\n#endif\nstatic BOOL microsoft_accessW(LPWSTR pszFolder, DWORD dwAccessDesired)\n{\n\tHANDLE\t\t\thToken;\n\tPRIVILEGE_SET\tPrivilegeSet;\n\tDWORD\t\t\tdwPrivSetSize;\n\tDWORD\t\t\tdwAccessGranted;\n\tBOOL\t\t\tfAccessGranted = FALSE;\n\tGENERIC_MAPPING\tGenericMapping;\n\tSECURITY_INFORMATION si = (SECURITY_INFORMATION)( OWNER_SECURITY_INFORMATION|GROUP_SECURITY_INFORMATION|DACL_SECURITY_INFORMATION);\n\tPSECURITY_DESCRIPTOR psdSD = NULL;\n\tDWORD\t\t\tdwNeeded;\n\tGetFileSecurityW(pszFolder,si,NULL,0,&dwNeeded);\n\tpsdSD = malloc(dwNeeded);\n\tGetFileSecurityW(pszFolder,si,psdSD,dwNeeded,&dwNeeded);\n\tImpersonateSelf(SecurityImpersonation);\n\tOpenThreadToken(GetCurrentThread(), TOKEN_ALL_ACCESS, TRUE, &hToken);\n\tmemset(&GenericMapping, 0xff, sizeof(GENERIC_MAPPING));\n\tGenericMapping.GenericRead = ACCESS_READ;\n\tGenericMapping.GenericWrite = ACCESS_WRITE;\n\tGenericMapping.GenericExecute = 0;\n\tGenericMapping.GenericAll = ACCESS_READ | ACCESS_WRITE;\n\tMapGenericMask(&dwAccessDesired, &GenericMapping);\n\tdwPrivSetSize = sizeof(PRIVILEGE_SET);\n\tAccessCheck(psdSD, hToken, dwAccessDesired, &GenericMapping, &PrivilegeSet, &dwPrivSetSize, &dwAccessGranted, &fAccessGranted);\n\tfree(psdSD);\n\treturn fAccessGranted;\n}\nstatic BOOL microsoft_accessU(LPCSTR pszFolder, DWORD dwAccessDesired)\n{\n\twchar_t\t\t\twpath[MAX_OSPATH];\n\treturn microsoft_accessW(widen(wpath, sizeof(wpath), pszFolder), dwAccessDesired);\n}\n\n\n#ifndef SVNREVISION\n\t#if 0\t//1 to debug engine update in msvc.\n\t\t#define SVNREVISION 1\n\t#else\n\t\t#define SVNREVISION -\n\t#endif\n#endif\n#define SVNREVISIONSTR STRINGIFY(SVNREVISION)\n\n#if 0//ndef NOLEGACY\n\t#if defined(SVNREVISION) && !defined(MINIMAL)\n\t\t#if defined(OFFICIAL_RELEASE)\n\t\t\t#define UPD_BUILDTYPE \"rel\"\n\t\t#else\n\t\t\t#define UPD_BUILDTYPE \"test\"\n\t\t\t//WARNING: Security comes from the fact that the triptohell.info certificate is hardcoded in the tls code.\n\t\t\t//this will correctly detect insecure tls proxies also.\n//\t\t\t#define UPDATE_URL_ROOT\t\t\"https://triptohell.info/moodles/\"\n//\t\t\t#define UPDATE_URL_TESTED\tUPDATE_URL_ROOT \"autoup/\"\n//\t\t\t#define UPDATE_URL_NIGHTLY\tUPDATE_URL_ROOT\n//\t\t\t#define UPDATE_URL_VERSION\t\"%sversion.txt\"\n/*\t\t\t#ifdef NOLEGACY\n\t\t\t\t#ifdef _WIN64\n\t\t\t\t\t#define UPDATE_URL_BUILD \"%snocompat64/fte\" EXETYPE \"64.exe\"\n\t\t\t\t#else\n\t\t\t\t\t#define UPDATE_URL_BUILD \"%snocompat/fte\" EXETYPE \".exe\"\n\t\t\t\t#endif\n\t\t\t#else\n\t\t\t\t#ifdef _WIN64\n\t\t\t\t\t#define UPDATE_URL_BUILD \"%swin64/fte\" EXETYPE \"64.exe\"\n\t\t\t\t#else\n\t\t\t\t\t#define UPDATE_URL_BUILD \"%swin32/fte\" EXETYPE \".exe\"\n\t\t\t\t#endif\n\t\t\t#endif*/\n\t\t#endif\n\n\t\t#if defined(SERVERONLY)\n\t\t\t#define EXETYPE \"qwsv\"\t//not gonna happen, but whatever.\n\t\t#elif defined(GLQUAKE) && defined(D3DQUAKE)\n\t\t\t#define EXETYPE \"qw\"\n\t\t#elif defined(GLQUAKE)\n\t\t\t#ifdef MINIMAL\n\t\t\t\t#define EXETYPE \"minglqw\"\n\t\t\t#else\n\t\t\t\t#define EXETYPE \"glqw\"\n\t\t\t#endif\n\t\t#elif defined(D3DQUAKE)\n\t\t\t#define EXETYPE \"d3dqw\"\n\t\t#elif defined(SWQUAKE)\n\t\t\t#define EXETYPE \"swqw\"\n\t\t#else\n\t\t\t//erm...\n\t\t\t#define EXETYPE \"qw\"\n\t\t#endif\n\t#endif\n#endif\n\n#ifdef HAVEAUTOUPDATE\n//this is for legacy reasons. old builds stored a 'pending' name in the registry for the 'frontend' to rename+use\nqboolean Sys_SetUpdatedBinary(const char *newbinary)\n{\n/* //legacy crap, completely redundant when we're replacing the original binary\n#ifdef UPD_BUILDTYPE\n\t//downloads menu has provided a new binary to use\n\tMyRegSetValue(HKEY_CURRENT_USER, \"Software\\\\\"FULLENGINENAME, \"pending\" UPD_BUILDTYPE EXETYPE, REG_SZ, newbinary, strlen(newbinary)+1);\n#endif\n*/\n\twchar_t newbinaryw[MAX_OSPATH];\n\twchar_t enginebinary[MAX_OSPATH];\n\twchar_t enginebinarybackup[MAX_OSPATH+4];\n\tsize_t len;\n\tstatic qboolean alreadymoved = false;\n\t\n\t//update blocked via commandline. to be doubly sure its checked.\n\tif (COM_CheckParm(\"-noupdate\") || COM_CheckParm(\"--noupdate\") || COM_CheckParm(\"-noautoupdate\") || COM_CheckParm(\"--noautoupdate\"))\n\t\treturn false;\n\n\t//windows is annoying. we can't delete a file that's in use (no orphaning)\n\t//we can normally rename it to something else before writing a new file with the original name.\n\t//then delete the old file later (sadly only on reboot)\n\n\t//because microsoft suck.\n\twiden(newbinaryw, sizeof(newbinaryw), newbinary);\n\t//get the binary name\n\tGetModuleFileNameW(NULL, enginebinary, countof(enginebinary)-1);\n#ifdef HAVE_LEGACY\n\t//--fromfrontend was removed for r5596.\n\t//it had two args - launcher version and launcher filename, so that if the user manually updated then it'd use the more recent build.\n\t//replace the original launcher instead.\n\t{\n\t\tint arg = COM_CheckParm(\"--fromfrontend\");\n\t\tif (arg)\n\t\t\twiden(enginebinary, sizeof(enginebinary), com_argv[arg+2]);\n\t}\n#endif\n\t//generate the temp name\n\tmemcpy(enginebinarybackup, enginebinary, sizeof(enginebinary));\n\tlen = wcslen(enginebinarybackup);\n\tif (len > 4 && !_wcsicmp(enginebinarybackup+len-4, L\".exe\"))\n\t\tlen -= 4;\t//swap its extension over, if we can.\n\twcscpy(enginebinarybackup+len, L\".bak\");\n\n\t//can fail if we don't have write access. can fail for a few other reasons.\n\tif (alreadymoved || MoveFileExW(enginebinary, enginebinarybackup, MOVEFILE_REPLACE_EXISTING))\n\t{\n\t\t//can fail if windows is bugging out again\n\t\tif (CopyFileW(newbinaryw, enginebinary, FALSE))\n\t\t{\t//delete the old one eventually, when we can.\n\t\t\tif (!DeleteFileW(enginebinarybackup))\t//if we can directly delete it... sadly this will normally fail but lets try it anyway. maybe it'll get opened with SHARE_DELETE some time!\n\t\t\t\tMoveFileExW(enginebinarybackup, NULL, MOVEFILE_DELAY_UNTIL_REBOOT);\t//schedule it to delete if we can't do it from the start.\n\t\t\talreadymoved = true; //if we already moved it, don't try to delete-by-overwrite the current binary, just delete it...\n\t\t\treturn true;\t//yay! it worked!\n\t\t}\n\t\tif (!alreadymoved)\n\t\t\tMoveFileExW(enginebinarybackup, enginebinary, 0);\n\t}\n\talreadymoved = false;\n\treturn false;\n}\nqboolean Sys_EngineMayUpdate(void)\n{\n\twchar_t enginebinaryw[MAX_OSPATH];\n\tif (!COM_CheckParm(\"-allowupdate\"))\n\t{\n\t\tchar enginebinary[MAX_OSPATH*4];\n\t\tchar *s;\n\t\tif (revision_number(true) <= 0)\n\t\t\treturn false;\n\n\t\tGetModuleFileNameW(NULL, enginebinaryw, countof(enginebinaryw)-1);\n\t\tnarrowen(enginebinary, sizeof(enginebinary), enginebinaryw);\n\t\t//if there's 3 consecutive digits or digit.digit then assume the user is doing their own versioning, and disallow engine updates (unless they use the -allowupdate arg).\n\t\t//(that or they ran one of our older builds directly)\n        for (s=COM_SkipPath(enginebinary); *s; s++)\n\t\t{\n\t\t\tif ( s[0] >= '0' && s[0] <= '9')\n\t\t\tif ((s[1] >= '0' && s[1] <= '9') || s[1] == '.' || s[1] == '_' || s[1] == '-')\n\t\t\tif ( s[2] >= '0' && s[2] <= '9')\n\t\t\t\treturn false;\n\t\t}\n\t}\n\n\t//update blocked via commandline\n\tif (COM_CheckParm(\"-noupdate\") || COM_CheckParm(\"--noupdate\") || COM_CheckParm(\"-noautoupdate\") || COM_CheckParm(\"--noautoupdate\"))\n\t\treturn false;\n\n\treturn true;\n}\n/*\nqboolean Sys_EngineWasUpdated(char *newbinary)\n{\n\twchar_t launchbinary[2048];\n\twchar_t launcherbinary[MAX_OSPATH];\n\twchar_t wargs[8192];\n\twchar_t rev[2048];\n\tPROCESS_INFORMATION childinfo;\n\tSTARTUPINFOW startinfo = {sizeof(startinfo)};\n\n\t//if we were called from a frontend, then don't chain to another, because that would be recursive, and that would be bad.\n\tif (COM_CheckParm(\"--fromfrontend\"))\n\t\treturn false;\n\t//if we're not allowed for some other reason\n\tif (!Sys_EngineCanUpdate())\n\t\treturn false;\n\n\tmemset(&childinfo, 0, sizeof(childinfo));\n\tstartinfo.dwFlags = STARTF_USESTDHANDLES;\n\tstartinfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE);\n\tstartinfo.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);\n\tstartinfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);\n\n\tGetModuleFileNameW(NULL, launcherbinary, countof(launcherbinary)-1);\n\twiden(launchbinary, sizeof(launchbinary), newbinary);\n\t_snwprintf(wargs, countof(wargs), L\"%s --fromfrontend \\\"%s\\\" \\\"%s\\\"\", GetCommandLineW(), widen(rev, sizeof(rev), SVNREVISIONSTR), launcherbinary);\n\tif (CreateProcessW(launchbinary, wargs, NULL, NULL, TRUE, 0, NULL, NULL, &startinfo, &childinfo))\n\t{\n\t\tCloseHandle(childinfo.hProcess);\n\t\tCloseHandle(childinfo.hThread);\n\t\treturn true;\t//it started up, we need to die now.\n\t}\n\n\treturn false;\t//failure!\n}\n*/\n#endif\n\n#include \"shellapi.h\"\nconst GUID qIID_IApplicationAssociationRegistrationUI = {0x1f76a169,0xf994,0x40ac, {0x8f,0xc8,0x09,0x59,0xe8,0x87,0x47,0x10}};\nconst GUID qCLSID_ApplicationAssociationRegistrationUI = {0x1968106d,0xf3b5,0x44cf,{0x89,0x0e,0x11,0x6f,0xcb,0x9e,0xce,0xf1}};\nstruct qIApplicationAssociationRegistrationUI;\ntypedef struct qIApplicationAssociationRegistrationUI\n{\n\tstruct qIApplicationAssociationRegistrationUI_vtab\n\t{\n\t\tHRESULT  (WINAPI *QueryInterface)\t\t\t\t(struct qIApplicationAssociationRegistrationUI *, const GUID *riid, void **ppvObject);\n\t\tHRESULT  (WINAPI *AddRef)\t\t\t\t\t\t(struct qIApplicationAssociationRegistrationUI *);\n\t\tHRESULT  (WINAPI *Release)\t\t\t\t\t\t(struct qIApplicationAssociationRegistrationUI *);\n\t\tHRESULT  (WINAPI *LaunchAdvancedAssociationUI)\t(struct qIApplicationAssociationRegistrationUI *, LPCWSTR app);\n\t} *lpVtbl;\n} qIApplicationAssociationRegistrationUI;\n\nchar *Sys_URIScheme_NeedsRegistering(void)\n{   //just disables the prompts.\n\tHKEY root;\n\tchar buffer[2048];\n\tchar scheme[64];\n\tconst char *s, *schemes = fs_manifest->schemes;\n\tchar *exec, *me;\n\tsize_t i;\n\twchar_t enginebinaryw[MAX_OSPATH];\n\tchar enginebinary[MAX_OSPATH*4];\n\n\tfor (s = schemes; (s=COM_ParseOut(s, scheme, sizeof(scheme))); )\n\t{\n\t\troot = HKEY_CURRENT_USER;\n\t\tif (!MyRegGetStringValue(root, va(\"Software\\\\Classes\\\\%s\", scheme), \"\", buffer, sizeof(buffer)))\n\t\t{\n\t\t\troot = HKEY_LOCAL_MACHINE;\n\t\t\tif (!MyRegGetStringValue(root, va(\"Software\\\\Classes\\\\%s\", scheme), \"\", buffer, sizeof(buffer)))\n\t\t\t\tbreak;\n\t\t}\n\n\t\t//the scheme exists at least...\n\t\tif (!MyRegGetStringValue(root, va(\"Software\\\\Classes\\\\%s\\\\shell\\\\open\\\\command\", scheme), \"\", buffer, sizeof(buffer)))\n\t\t\tbreak; //erk, missing.\n\t\tCOM_Parse(buffer);\n\t\tif (!microsoft_accessU(com_token, ACCESS_READ))\n\t\t\tbreak;\t//can't read it? doesn't exist?\n\t\texec = COM_SkipPath(com_token);\n\t\tfor (i = 0; exec[i]; i++)\n\t\t\tif (exec[i] == '_' || exec[i] == '.' || (exec[i] >= '0' && exec[i] <= '9'))\n\t\t\t{\t//anything that looks like a revision number\n\t\t\t\texec[i] = 0;\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\tGetModuleFileNameW(NULL, enginebinaryw, countof(enginebinaryw)-1);\n\t\tnarrowen(enginebinary, sizeof(enginebinary), enginebinaryw);\n\t\tme = COM_SkipPath(enginebinary);\n\t\tfor (i = 0; me[i]; i++)\n\t\t\tif (me[i] == '_' || me[i] == '.' || (me[i] >= '0' && me[i] <= '9'))\n\t\t\t{\t//anything that looks like a revision number\n\t\t\t\tme[i] = 0;\n\t\t\t\tbreak;\n\t\t\t}\n\t\tif (Q_strcasecmp(exec, me))\n\t\t\tbreak;\t//looks like its set to something else.\n\t}\n\n\tif (s)\n\t\treturn Z_StrDup(scheme);\n    return NULL;\n}\nvoid Sys_DoFileAssociations(int elevated, const char *schemes)\n{\n\t//elevated:\n\t//\t0: console command\n\t//\t1: running as an elevated/admin process\n\t//\t2: register as current user only (do not show associations prompt).\n\tchar command[1024];\n\tchar scheme[64];\n\tconst char *s;\n\tqboolean ok = true;\n\tHKEY root;\n\n\t//I'd do everything in current_user if I could, but windows sucks too much for that.\n\t//'registered applications' simply does not work in hkcu, we MUST use hklm for that.\n\t//if there's a registered application and we are not, we are unable to grab that association, ever.\n\t//thus we HAVE to do things to the local machine or we might as well not bother doing anything.\n\t//still, with a manifest not giving false success, if the user clicks 'no' to the UAC prompt, we'll write everything to the current user anyway, so if microsoft do ever fix things, then yay.\n\t//also, I hate the idea of creating a 'registered application' in globally without the file types it uses being local.\n\n\t//on xp, we use ONLY current user. no 'registered applications' means no 'registered applications bug', which means no need to use hklm at all.\n\t//in vista/7, we have to create stuff in local_machine. in which case we might as well put ALL associations in there. the ui stuff will allow user-specific settings, so this is not an issue other than the fact that it triggers uac.\n\t//in 8, we cannot programatically force ownership of our associations, so we might as well just use the ui method even for vista+7 instead of the ruder version.\n\t//in win10, the 'ui' stuff is just a quick popup to tell the user to configure defaults themselves. hopefully we can fall back on the regular associations for when the user didn't override anyting.\n\tif (qwinvermaj < 6)\n\t\televated = 2;\n\n\troot = (elevated>=2)?HKEY_CURRENT_USER:HKEY_LOCAL_MACHINE;\n\n//\t#define ASSOC_VERSION 2\n#define ASSOCV \"1\"\n\n\t//register the basic demo class\n\tQ_snprintfz(command, sizeof(command), \"Quake or QuakeWorld Demo\");\n\tok = ok & MyRegSetValue(root, \"Software\\\\Classes\\\\\"DISTRIBUTION\"_DemoFile.\"ASSOCV, \"\", REG_SZ, command, strlen(command));\n\tQ_snprintfz(command, sizeof(command), \"\\\"%s\\\",0\", com_argv[0]);\n\tok = ok & MyRegSetValue(root, \"Software\\\\Classes\\\\\"DISTRIBUTION\"_DemoFile.\"ASSOCV\"\\\\DefaultIcon\", \"\", REG_SZ, command, strlen(command));\n\tQ_snprintfz(command, sizeof(command), \"\\\"%s\\\" \\\"%%1\\\"\", com_argv[0]);\n\tok = ok & MyRegSetValue(root, \"Software\\\\Classes\\\\\"DISTRIBUTION\"_DemoFile.\"ASSOCV\"\\\\shell\\\\open\\\\command\", \"\", REG_SZ, command, strlen(command));\n\tif (ok)\n\t{\t//and now the extensions themselves...\n\t\tMyRegSetValue(root, \"Software\\\\Classes\\\\.qtv\", \"\", REG_SZ, DISTRIBUTION\"_DemoFile.\"ASSOCV, strlen(DISTRIBUTION\"_DemoFile.\"ASSOCV));\n\t\tMyRegSetValue(root, \"Software\\\\Classes\\\\.mvd\", \"\", REG_SZ, DISTRIBUTION\"_DemoFile.\"ASSOCV, strlen(DISTRIBUTION\"_DemoFile.\"ASSOCV));\n\t\tMyRegSetValue(root, \"Software\\\\Classes\\\\.qwd\", \"\", REG_SZ, DISTRIBUTION\"_DemoFile.\"ASSOCV, strlen(DISTRIBUTION\"_DemoFile.\"ASSOCV));\n\t\tMyRegSetValue(root, \"Software\\\\Classes\\\\.dem\", \"\", REG_SZ, DISTRIBUTION\"_DemoFile.\"ASSOCV, strlen(DISTRIBUTION\"_DemoFile.\"ASSOCV));\n\t}\n\n\t//register the basic map class. yeah, the command is the same as for demos. but the description is different!\n\tQ_snprintfz(command, sizeof(command), \"Quake Map\");\n\tok = ok & MyRegSetValue(root, \"Software\\\\Classes\\\\\"DISTRIBUTION\"_BSPFile.\"ASSOCV, \"\", REG_SZ, command, strlen(command));\n\tQ_snprintfz(command, sizeof(command), \"\\\"%s\\\",0\", com_argv[0]);\n\tok = ok & MyRegSetValue(root, \"Software\\\\Classes\\\\\"DISTRIBUTION\"_BSPFile.\"ASSOCV\"\\\\DefaultIcon\", \"\", REG_SZ, command, strlen(command));\n\tQ_snprintfz(command, sizeof(command), \"\\\"%s\\\" \\\"%%1\\\"\", com_argv[0]);\n\tok = ok & MyRegSetValue(root, \"Software\\\\Classes\\\\\"DISTRIBUTION\"_BSPFile.\"ASSOCV\"\\\\shell\\\\open\\\\command\", \"\", REG_SZ, command, strlen(command));\n\tif (ok)\n\t{\t//and now the extensions themselves...\n\t\tMyRegSetValue(root, \"Software\\\\Classes\\\\.bsp\", \"\", REG_SZ, DISTRIBUTION\"_BSPFile.\"ASSOCV, strlen(DISTRIBUTION\"_BSPFile.\"ASSOCV));\n\t\tMyRegSetValue(root, \"Software\\\\Classes\\\\.map\", \"\", REG_SZ, DISTRIBUTION\"_BSPFile.\"ASSOCV, strlen(DISTRIBUTION\"_BSPFile.\"ASSOCV));\n\t}\n\n\t//register the basic uri scheme class\n\tQ_snprintfz(command, sizeof(command), \"QuakeWorld Server\");\n\tok = ok & MyRegSetValue(root, \"Software\\\\Classes\\\\\"DISTRIBUTION\"_Server.\"ASSOCV\"\", \"\", REG_SZ, command, strlen(command));\n\tok = ok & MyRegSetValue(root, \"Software\\\\Classes\\\\\"DISTRIBUTION\"_Server.\"ASSOCV\"\", \"URL Protocol\", REG_SZ, \"\", strlen(\"\"));\n\tQ_snprintfz(command, sizeof(command), \"\\\"%s\\\",0\", com_argv[0]);\n\tok = ok & MyRegSetValue(root, \"Software\\\\Classes\\\\\"DISTRIBUTION\"_Server.\"ASSOCV\"\\\\DefaultIcon\", \"\", REG_SZ, command, strlen(command));\n\tQ_snprintfz(command, sizeof(command), \"\\\"%s\\\" \\\"%%1\\\"\", com_argv[0]);\n\tok = ok & MyRegSetValue(root, \"Software\\\\Classes\\\\\"DISTRIBUTION\"_Server.\"ASSOCV\"\\\\shell\\\\open\\\\command\", \"\", REG_SZ, command, strlen(command));\n\tif (ok)\n\t{\t//and now the schemes themselves... (doesn't really use the same scheme stuff)\n\t\tfor (s = schemes; (s=COM_ParseOut(s, scheme, sizeof(scheme))); )\n\t\t{\n\t\t\tQ_snprintfz(command, sizeof(command), \"QuakeWorld Server\");\n\t\t\tMyRegSetValue(root, va(\"Software\\\\Classes\\\\%s\", scheme), \"\", REG_SZ, command, strlen(command));\n\t\t\tMyRegSetValue(root, va(\"Software\\\\Classes\\\\%s\", scheme), \"URL Protocol\", REG_SZ, \"\", strlen(\"\"));\n\t\t\tQ_snprintfz(command, sizeof(command), \"\\\"%s\\\",0\", com_argv[0]);\n\t\t\tMyRegSetValue(root, va(\"Software\\\\Classes\\\\%s\\\\DefaultIcon\", scheme), \"\", REG_SZ, command, strlen(command));\n\t\t\tQ_snprintfz(command, sizeof(command), \"\\\"%s\\\" \\\"%%1\\\"\", com_argv[0]);\n\t\t\tMyRegSetValue(root, va(\"Software\\\\Classes\\\\%s\\\\shell\\\\open\\\\command\", scheme), \"\", REG_SZ, command, strlen(command));\n\t\t}\n\t}\n\n\n\t//now try to get ourselves listed in windows' 'default programs' ui.\n\tQ_snprintfz(command, sizeof(command), \"%s\", FULLENGINENAME);\n\tok = ok & MyRegSetValue(root, \"Software\\\\\"FULLENGINENAME\"\\\\Capabilities\", \"ApplicationName\", REG_SZ, command, strlen(command));\n\tQ_snprintfz(command, sizeof(command), \"%s\", FULLENGINENAME\" is an awesome hybrid game engine able to run multiple Quake-compatible/derived games.\");\n\tok = ok & MyRegSetValue(root, \"Software\\\\\"FULLENGINENAME\"\\\\Capabilities\", \"ApplicationDescription\", REG_SZ, command, strlen(command));\n\n\tQ_snprintfz(command, sizeof(command), DISTRIBUTION\"_DemoFile.1\");\n\tok = ok & MyRegSetValue(root, \"Software\\\\\"FULLENGINENAME\"\\\\Capabilities\\\\FileAssociations\", \".qtv\", REG_SZ, command, strlen(command));\n\tok = ok & MyRegSetValue(root, \"Software\\\\\"FULLENGINENAME\"\\\\Capabilities\\\\FileAssociations\", \".mvd\", REG_SZ, command, strlen(command));\n\tok = ok & MyRegSetValue(root, \"Software\\\\\"FULLENGINENAME\"\\\\Capabilities\\\\FileAssociations\", \".qwd\", REG_SZ, command, strlen(command));\n\tok = ok & MyRegSetValue(root, \"Software\\\\\"FULLENGINENAME\"\\\\Capabilities\\\\FileAssociations\", \".dem\", REG_SZ, command, strlen(command));\n//\tok = ok & MyRegSetValue(root, \"Software\\\\\"FULLENGINENAME\"\\\\Capabilities\\\\FileAssociations\", \".dm2\", REG_SZ, command, strlen(command));\n\n\tQ_snprintfz(command, sizeof(command), DISTRIBUTION\"_BSPFile.1\");\n\tok = ok & MyRegSetValue(root, \"Software\\\\\"FULLENGINENAME\"\\\\Capabilities\\\\FileAssociations\", \".bsp\", REG_SZ, command, strlen(command));\n\tok = ok & MyRegSetValue(root, \"Software\\\\\"FULLENGINENAME\"\\\\Capabilities\\\\FileAssociations\", \".map\", REG_SZ, command, strlen(command));\n\n//\tok = ok & MyRegSetValue(root, \"Software\\\\\"FULLENGINENAME\"\\\\Capabilities\\\\FileAssociations\", \".fmf\", REG_SZ, DISTRIBUTION\"_ManifestFile\", strlen(DISTRIBUTION\"_ManifestFile\"));\n//\tok = ok & MyRegSetValue(root, \"Software\\\\\"FULLENGINENAME\"\\\\Capabilities\\\\MIMEAssociations\", \"application/x-ftemanifest\", REG_SZ, DISTRIBUTION\"_ManifestFile\", strlen(DISTRIBUTION\"_ManifestFile\"));\n\n\tQ_snprintfz(command, sizeof(command), DISTRIBUTION\"_Server.\"ASSOCV);\n\tfor (s = schemes; (s=COM_ParseOut(s, scheme, sizeof(scheme))); )\n\t{\n\t\tok = ok & MyRegSetValue(root, \"Software\\\\\"FULLENGINENAME \"\\\\Capabilities\\\\UrlAssociations\", scheme, REG_SZ, command, strlen(command));\n\t}\n\t\n\tQ_snprintfz(command, sizeof(command), \"Software\\\\\"FULLENGINENAME\"\\\\Capabilities\");\n\tok = ok & MyRegSetValue(root, \"Software\\\\RegisteredApplications\", FULLENGINENAME, REG_SZ, command, strlen(command));\n\n\t//also try to add it to current user.\n\tif (root==HKEY_LOCAL_MACHINE)\n\t\tSys_DoFileAssociations(2, schemes);\n\n\t//let the shell know that file associations changed (otherwise we might have to wait for a reboot)\n\tSHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, NULL, NULL);\n\n\tif (!ok && root==HKEY_LOCAL_MACHINE)\n\t{\n\t\tShellExecute(mainwindow, \"runas\", com_argv[0], va(\"-register_types \\\"%s\\\"\", schemes), NULL, SW_SHOWNORMAL);\n\t\treturn;\n\t}\n\n\tif (ok && root==HKEY_LOCAL_MACHINE)\n\t{\n\t\t//attempt to display the vista+ prompt (only way possible in win8, apparently)\n\t\t//note that in win10 this will supposedly just show a notification popup with the user required to configure it manually via control panel.\n\t\tqIApplicationAssociationRegistrationUI *aarui = NULL;\n\n\t\tCoInitialize(NULL);\n\t\tif (FAILED(CoCreateInstance(&qCLSID_ApplicationAssociationRegistrationUI, 0, CLSCTX_INPROC_SERVER, &qIID_IApplicationAssociationRegistrationUI, (LPVOID*)&aarui)))\n\t\t\taarui = NULL;\n\n\t\tif (aarui)\n\t\t{\n#define wideify2(a) L##a\n#define wideify(a) wideify2(a)\n\t\t\taarui->lpVtbl->LaunchAdvancedAssociationUI(aarui, wideify(FULLENGINENAME));\n\t\t\taarui->lpVtbl->Release(aarui);\n\t\t}\n\t\telse\n\t\t{\n\n/*\n#define wideify2(a) L##a\n#define wideify(a) wideify2(a)\n\t\t\tqOPENASINFO open_as_info = {0};\n\t\t\topen_as_info.pcszFile = L\".mvd\";\n\t\t\topen_as_info.pcszClass = wideify(DISTRIBUTION)L\"_DemoFile.1\";\n\t\t\topen_as_info.oaifInFlags = 8 | 2;//OAIF_FORCE_REGISTRATION | OAIF_REGISTER_EXT;\n\t\t\tif (pSHOpenWithDialog)\n\t\t\t\tpSHOpenWithDialog(NULL, &open_as_info);\n\t\t\tSHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, NULL, NULL);\n*/\n\t\t}\n\t}\n}\n\n/*\n#ifdef _MSC_VER\n#include <signal.h>\nvoid VARGS Signal_Error_Handler(int i)\n{\n\tint *basepointer;\n\t__asm {mov basepointer,ebp};\n\tSys_Error(\"Received signal, offset was 0x%8x\", basepointer[73]);\n}\n#endif\n*/\n\nextern char sys_language[64];\n\nstatic int Sys_ProcessCommandline(char **argv, int maxargc, char *argv0)\n{\n\tint argc = 0, i;\n\twchar_t *wc = GetCommandLineW();\n\tunsigned char utf8cmdline[4096], *cl = utf8cmdline;\n\tnarrowen(utf8cmdline, sizeof(utf8cmdline), wc);\n\n//\targv[argc] = argv0;\n//\targc++;\n\n\twhile (*cl && (argc < maxargc))\n\t{\n\t\twhile (*cl && *cl <= 32)\n\t\t\tcl++;\n\n\t\tif (*cl)\n\t\t{\n\t\t\tif (*cl == '\\\"')\n\t\t\t{\n\t\t\t\tcl++;\n\n\t\t\t\targv[argc] = cl;\n\t\t\t\targc++;\n\n\t\t\t\twhile (*cl && *cl != '\\\"')\n\t\t\t\t\tcl++;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\targv[argc] = cl;\n\t\t\t\targc++;\n\n\n\t\t\t\twhile (*cl && *cl > 32)\n\t\t\t\t\tcl++;\n\t\t\t}\n\n\t\t\tif (*cl)\n\t\t\t{\n\t\t\t\t*cl = 0;\n\t\t\t\tcl++;\n\t\t\t}\n\t\t}\n\t}\n\tif (argc < 1)\n\t{\n\t\targv[0] = argv0;\n\t\targc = 1;\n\t}\n\tfor (i = 0; i < argc; i++)\n\t\targv[i] = strdup(argv[i]);\n\treturn i;\n}\n\nint MessageBoxU(HWND hWnd, char *lpText, char *lpCaption, UINT uType)\n{\n\twchar_t widecaption[256];\n\twchar_t widetext[2048];\n\twiden(widetext, sizeof(widetext), lpText);\n\twiden(widecaption, sizeof(widecaption), lpCaption);\n\treturn MessageBoxW(hWnd, widetext, widecaption, uType);\n}\n\n#ifdef WEBCLIENT\n#ifndef GWLP_WNDPROC\n#define GWLP_WNDPROC GWL_WNDPROC\n#define SetWindowLongPtr SetWindowLong\n#define LONG_PTR LONG\n#endif\n#ifndef BIF_NEWDIALOGSTYLE\n#define BIF_NEWDIALOGSTYLE\t0x00000040\t\t//v5\n#define BFFM_SETOKTEXT\t\t(WM_USER + 105)\t//v6\n#define BFFM_SETEXPANDED\t(WM_USER + 106)\t//v6\n#endif\nstatic const IID qIID_IPersistFile\t= {0x0000010BL, 0, 0, {0xc0,0,0,0,0,0,0,0x46}};\n\nstatic WNDPROC omgwtfwhyohwhy;\nstatic LRESULT CALLBACK stoopidstoopidstoopid(HWND w, UINT m, WPARAM wp, LPARAM lp)\n{\n\tswitch (m)\n\t{\n\tcase WM_NOTIFY:\n\t\tswitch (((LPNMHDR)lp)->code)\n\t\t{\n\t\tcase TVN_ENDLABELEDITW:\n\t\t\t{\n\t\t\t\tLRESULT r;\n\t\t\t\tNMTVDISPINFOW *fu = (NMTVDISPINFOW*)lp;\n\t\t\t\tNMTREEVIEWW gah;\n\t\t\t\tgah.action = TVC_UNKNOWN;\n\t\t\t\tgah.itemOld = fu->item;\n\t\t\t\tgah.itemNew = fu->item;\n\t\t\t\tgah.ptDrag.x = gah.ptDrag.y = 0;\n\t\t\t\tgah.hdr = fu->hdr;\n\t\t\t\tgah.hdr.code = TVN_SELCHANGEDW;\n\t\t\t\tr = CallWindowProcW(omgwtfwhyohwhy,w,m,wp,lp);\n\t\t\t\tCallWindowProcW(omgwtfwhyohwhy,w,WM_NOTIFY,wp,(LPARAM)&gah);\n\t\t\t\treturn r;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase TVN_ENDLABELEDITA:\n\t\t\t{\n\t\t\t\tLRESULT r;\n\t\t\t\tNMTVDISPINFOA *fu = (NMTVDISPINFOA*)lp;\n\t\t\t\tNMTREEVIEWA gah;\n\t\t\t\tgah.action = TVC_UNKNOWN;\n\t\t\t\tgah.itemOld = fu->item;\n\t\t\t\tgah.itemNew = fu->item;\n\t\t\t\tgah.ptDrag.x = gah.ptDrag.y = 0;\n\t\t\t\tgah.hdr = fu->hdr;\n\t\t\t\tgah.hdr.code = TVN_SELCHANGEDA;\n\t\t\t\tr = CallWindowProcW(omgwtfwhyohwhy,w,m,wp,lp);\n\t\t\t\tCallWindowProcW(omgwtfwhyohwhy,w,WM_NOTIFY,wp,(LPARAM)&gah);\n\t\t\t\treturn r;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase TVN_SELCHANGEDA:\n\t\tcase TVN_SELCHANGEDW:\n\t\t\tbreak;\n\t\t}\n\t\tbreak;\n\t}\n\treturn CallWindowProcW(omgwtfwhyohwhy,w,m,wp,lp);\n}\n\nstruct egadsthisisretarded\n{\n\twchar_t title[MAX_OSPATH];\n\tchar subdir[MAX_OSPATH];\n\tchar parentdir[MAX_OSPATH];\n\tchar statustext[MAX_OSPATH];\n};\n\nvoid FS_Directorize(char *fname, size_t fnamesize)\n{\n\tsize_t l = strlen(fname);\n\tif (!l)\t//technically already a directory\n\t\treturn;\n\tif (fname[l-1] == '\\\\' || fname[l-1] == '/')\n\t\treturn;\t//already a directory\n\tQ_strncatz(fname, \"/\", fnamesize);\n}\n\nstatic INT CALLBACK StupidBrowseCallbackProc(HWND hwnd, UINT uMsg, LPARAM lp, LPARAM pDatafoo) \n{\t//'stolen' from microsoft's knowledge base.\n\t//required to work around microsoft being annoying.\n\tstruct egadsthisisretarded *pData = (struct egadsthisisretarded*)pDatafoo;\n//\tchar *foo;\n\tHWND edit = FindWindowEx(hwnd, NULL, \"EDIT\", NULL);\n\tHWND list;\n//\tOutputDebugString(va(\"got %u (%u)\\n\", uMsg, lp));\n\tswitch(uMsg)\n\t{\n\tcase BFFM_INITIALIZED:\n\t\t//combat windows putting new windows behind everything else if it takes a while for UAC prompts to go away\n\t\tSetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\n\n\t\t//combat windows bug where renaming something doesn't update the dialog's path\n\t\tlist = FindWindowExW(hwnd, NULL, L\"SHBROWSEFORFOLDER SHELLNAMESPACE CONTROL\", NULL);\n\t\tif (list)\n\t\t\tomgwtfwhyohwhy = (WNDPROC)SetWindowLongPtr(list, GWLP_WNDPROC, (LONG_PTR)stoopidstoopidstoopid);\n\n\t\t{\n\t\t\twchar_t szDir[MAX_PATH];\n#ifndef _DEBUG\n\t\t\textern qboolean\tcom_homepathenabled;\n\t\t\t//the standard location iiuc\n\t\t\tif (com_homepathenabled && pSHGetSpecialFolderPathW(NULL, szDir, CSIDL_PROGRAM_FILES, TRUE) && microsoft_accessW(szDir, ACCESS_READ | ACCESS_WRITE))\n\t\t\t\t;\n\t\t\telse if (microsoft_accessU(\"C:\\\\Games\\\\\", ACCESS_READ | ACCESS_WRITE))\n\t\t\t\twiden(szDir, sizeof(szDir), \"C:\\\\Games\\\\\");\n\t\t\telse if (microsoft_accessU(\"C:\\\\\", ACCESS_READ | ACCESS_WRITE))\n\t\t\t\twiden(szDir, sizeof(szDir), \"C:\\\\\");\n\t\t\t//if we're not an admin, install it somewhere else.\n\t\t\telse if (pSHGetSpecialFolderPathW(NULL, szDir, CSIDL_LOCAL_APPDATA, TRUE) && microsoft_accessW(szDir, ACCESS_READ | ACCESS_WRITE))\n\t\t\t\t;\n\t\t\telse\n#endif\n\t\t\t\tif (GetCurrentDirectoryW(countof(szDir), szDir))// && microsoft_access(szDir, ACCESS_READ | ACCESS_WRITE))\n\t\t\t\t\t;\n\t\t\tSendMessageW(hwnd, BFFM_SETSELECTIONW, TRUE, (LPARAM)szDir);\n\t\t\tSendMessageW(hwnd, BFFM_SETEXPANDED, TRUE, (LPARAM)szDir);\n\t\t\tSendMessageW(hwnd, BFFM_SETOKTEXT, TRUE, (LPARAM)widen(szDir, sizeof(szDir), \"Install\"));\n\t\t}\n\t\tbreak;\n\tcase BFFM_VALIDATEFAILEDA:\n\tcase BFFM_VALIDATEFAILEDW:\n\t\tif (!microsoft_accessU(pData->parentdir, ACCESS_READ | ACCESS_WRITE))\n\t\t{\n\t\t\tMessageBoxU(hwnd, va(\"%s is not writable.\", pData->parentdir), fs_gamename.string, 0);\n\t\t\treturn 1;\n\t\t}\n\t\tif (edit)\n\t\t{\n\t\t\twchar_t wide[256];\n\t\t\tGetWindowTextW(edit, wide, countof(wide));\n\t\t\tnarrowen(pData->subdir, sizeof(pData->subdir), wide);\n\t\t}\n\n\t\tif (microsoft_accessU(va(\"%s%s\", pData->parentdir, pData->subdir), ACCESS_READ))\n\t\t\treturn MessageBoxU(hwnd, va(\"%s%s already exists!\\nThis installer will (generally) not overwrite existing data files.\\nIf you want to re-install, you must manually uninstall it first.\\n\\nContinue?\", pData->parentdir, pData->subdir), fs_gamename.string, MB_ICONWARNING|MB_OKCANCEL|MB_TOPMOST) == IDCANCEL;\n\t\telse\n\t\t\treturn MessageBoxU(hwnd, va(\"Install to %s%s ?\", pData->parentdir, pData->subdir), fs_gamename.string, MB_OKCANCEL) == IDCANCEL;\n\tcase BFFM_SELCHANGED: \n\t\t{\n\t\t\twchar_t wide[MAX_PATH*2+2];\n\t\t\tchar *foo;\n\t\t\tif (pSHGetPathFromIDListW((LPITEMIDLIST)lp, wide))\n\t\t\t{\n\t\t\t\tnarrowen(pData->parentdir, sizeof(pData->parentdir), wide);\n\t\t\t\tFS_Directorize(pData->parentdir, sizeof(pData->parentdir));\n\n\t\t\t\t//this'll make microsoft happy.\n\t\t\t\twhile((foo = strchr(pData->parentdir, '/')))\n\t\t\t\t\t*foo = '\\\\';\n\n\t\t\t\tif (edit)\n\t\t\t\t{\n\t\t\t\t\twchar_t wide[128];\n\t\t\t\t\tSetWindowTextW(edit, widen(wide, sizeof(wide), fs_gamename.string));\n\t\t\t\t\tSendMessageW(hwnd, BFFM_SETSTATUSTEXT, 0, (LPARAM)widen(wide, sizeof(wide), va(\"%s\", pData->parentdir)));\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tSendMessageW(hwnd, BFFM_SETSTATUSTEXT, 0, (LPARAM)widen(wide, sizeof(wide), va(\"%s%s\", pData->parentdir, fs_gamename.string)));\n\t\t\t}\n\t\t}\n\t\tbreak;\n//\tcase BFFM_IUNKNOWN:\n//\t\tbreak;\n\t}\n\treturn 0;\n}\n\nLRESULT CALLBACK NoCloseWindowProc(HWND w, UINT m, WPARAM wp, LPARAM lp)\n{\n\tif (m == WM_CLOSE)\n\t\treturn 0;\n\treturn DefWindowProc(w, m, wp, lp);\n}\n\nBOOL CopyFileU(const char *src, const char *dst, BOOL bFailIfExists)\n{\n\twchar_t wide1[2048];\n\twchar_t wide2[2048];\n\treturn CopyFileW(widen(wide1, sizeof(wide1), src), widen(wide2, sizeof(wide2), dst), bFailIfExists);\n}\n\n#ifdef WEBCLIENT\nstatic qboolean Sys_DoInstall(void)\n{\n\textern ftemanifest_t *fs_manifest;\n\tchar exepath[MAX_OSPATH];\n\tchar newexepath[MAX_OSPATH];\n\twchar_t wide[MAX_PATH];\n\twchar_t wide2[MAX_PATH];\n\tchar resultpath[MAX_OSPATH];\n\tBROWSEINFOW bi;\n\tLPITEMIDLIST il;\n\tstruct egadsthisisretarded diediedie;\n\n\tif (!pSHGetSpecialFolderPathW)\n\t{\n\t\tMessageBoxU(NULL, \"SHGetSpecialFolderPathW is not supported\\n\", fs_gamename.string, 0);\n\t\treturn TRUE;\n\t}\n\n\tif (fs_manifest && fs_manifest->eula)\n\t{\n\t\tif (MessageBoxU(NULL, fs_manifest->eula, fs_gamename.string, MB_OKCANCEL|MB_TOPMOST|MB_DEFBUTTON2) != IDOK)\n\t\t\treturn TRUE;\n\t}\n\n\tQ_strncpyz(diediedie.subdir, fs_gamename.string, sizeof(diediedie.subdir));\n\t_snwprintf(diediedie.title, countof(diediedie.title), L\"Where would you like to install %s to?\", widen(wide, sizeof(wide), fs_gamename.string));\n\tGetCurrentDirectoryW(countof(wide)-1, wide);\n\n\tmemset(&bi, 0, sizeof(bi));\n\tbi.hwndOwner = mainwindow; //note that this is usually still null\n\tbi.pidlRoot = NULL;\n\tbi.pszDisplayName = wide;\n\tbi.lpszTitle = diediedie.title;\n\tbi.ulFlags = BIF_RETURNONLYFSDIRS|BIF_STATUSTEXT | BIF_EDITBOX|BIF_NEWDIALOGSTYLE|BIF_VALIDATE;\n\tbi.lpfn = StupidBrowseCallbackProc;\n\tbi.lParam = (LPARAM)&diediedie;\n\tbi.iImage = 0;\n\n\til = pSHBrowseForFolderW?pSHBrowseForFolderW(&bi):NULL;\n\tif (il)\n\t{\n\t\tpSHGetPathFromIDListW(il, wide);\n\t\tCoTaskMemFree(il);\n\t}\n\telse\n\t\treturn true;\n\tnarrowen(resultpath, sizeof(resultpath), wide);\n\n\tFS_Directorize(resultpath, sizeof(resultpath));\n\tif (*diediedie.subdir)\n\t{\n\t\tQ_strncatz(resultpath, diediedie.subdir, sizeof(resultpath));\n\t\tFS_Directorize(resultpath, sizeof(resultpath));\n\t}\n\n\tFS_CreateBasedir(resultpath);\n\n\tGetModuleFileNameW(NULL, wide, countof(wide));\n\tnarrowen(exepath, sizeof(exepath), wide);\n\tFS_SystemPath(va(\"%s.exe\", fs_gamename.string), FS_ROOT, newexepath, sizeof(newexepath));\n\tCopyFileU(exepath, newexepath, FALSE);\n\n\t/*the game can now be run (using regular autoupdate stuff), but most installers are expected to install the data instead of just more downloaders, so lets do that with a 'nice' progress box*/\n\t{\n\t\tHINSTANCE hInstance = NULL;\n\t\tHWND progress, label, wnd;\n\t\tWNDCLASS wc;\n\t\tRECT ca;\n\t\tint sh;\n\t\tint pct = -100;\n\t\tchar fname[MAX_OSPATH];\n\t\tmemset(&wc, 0, sizeof(wc));\n\t\twc.style = 0;\n\t\twc.lpfnWndProc\t\t= NoCloseWindowProc;//Progress_Wnd;\n\t\twc.hInstance\t\t= hInstance;\n\t\twc.hCursor\t\t\t= LoadCursor (NULL,IDC_ARROW);\n\t\twc.hbrBackground\t= (void *)COLOR_WINDOW;\n\t\twc.lpszClassName\t= \"FTEPROG\";\n\t\tRegisterClass(&wc);\n\n\t\tca.right = GetSystemMetrics(SM_CXSCREEN);\n\t\tca.bottom = GetSystemMetrics(SM_CYSCREEN);\n\n\t\tmainwindow = wnd = CreateWindowEx(0, wc.lpszClassName, va(\"%s Installer\",  fs_gamename.string), 0, (ca.right-320)/2, (ca.bottom-100)/2, 320, 100, NULL, NULL, hInstance, NULL);\n\n\t\tGetClientRect(wnd, &ca); \n\t\tsh = GetSystemMetrics(SM_CYVSCROLL);\n\n\t\tSys_LoadLibrary(\"comctl32.dll\", NULL);\n//\t\tInitCommonControls();\n\n\t\tlabel = CreateWindow(\"STATIC\",\"\", WS_CHILD | WS_VISIBLE | SS_PATHELLIPSIS, sh, ((ca.bottom-ca.top-sh)/3), ca.right-ca.left-2*sh, sh, wnd, NULL, hInstance, NULL);\n\t\tprogress = CreateWindowEx(0, PROGRESS_CLASS, NULL, WS_CHILD | WS_VISIBLE | PBS_SMOOTH, sh, ((ca.bottom-ca.top-sh)/3)*2, ca.right-ca.left-2*sh, sh, wnd, NULL, hInstance, NULL);\n\n\t\tShowWindow(wnd, SW_NORMAL);\n\t\tSetWindowPos(wnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\n\n\t\tSendMessage(progress, PBM_SETRANGE32, 0, 10000);\n\t\t*fname = 0;\n\t\tHTTP_CL_Think(NULL, NULL);\n\t\twhile(FS_DownloadingPackage())\n\t\t{\n\t\t\tMSG msg;\n\t\t\tchar *cur = cls.download?COM_SkipPath(cls.download->localname):\"Please Wait\";\n\t\t\tint newpct = cls.download?cls.download->percent*100:0;\n\n\t\t\tif (cls.download && cls.download->sizeunknown)\n\t\t\t{\n\t\t\t\t//marquee needs manifest bollocks in order to work. so lets just not bother.\n\t\t\t\tfloat time = Sys_DoubleTime();\n\t\t\t\tnewpct = 10000 * (time - (int)time);\n\t\t\t\tif ((int)time & 1)\n\t\t\t\t\tnewpct = 10000 - newpct;\n\t\t\t}\n\n\t\t\tif (Q_strcmp(fname, cur))\n\t\t\t{\n\t\t\t\tQ_strncpyz(fname, cur, sizeof(fname));\n\t\t\t\tSetWindowText(label, fname);\n\t\t\t}\n\t\t\tif (pct != newpct)\n\t\t\t{\n\t\t\t\tSendMessage(progress, PBM_SETPOS, pct, 0);\n\t\t\t\tpct = newpct;\n\t\t\t}\n\n\t\t\twhile (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE))\n\t\t\t\tDispatchMessage (&msg);\n\n\t\t\tSleep(10);\n\t\t\tHTTP_CL_Think(NULL, NULL);\n\t\t}\n\t\tDestroyWindow(progress);\n\t\tDestroyWindow(wnd);\n\t\tUnregisterClass(\"FTEPROG\", hInstance);\n\t\tmainwindow = NULL;\n\t}\n\n\t/*create startmenu icon*/\n\tif (MessageBoxU(NULL, va(\"Create start-menu icon for %s?\", fs_gamename.string), fs_gamename.string, MB_YESNO|MB_ICONQUESTION|MB_TOPMOST) == IDYES)\n\t{\n\t\tHRESULT hres;\n\t\tIShellLinkW *psl;\n\t\thres = CoCreateInstance(&CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, &qIID_IShellLinkW, (LPVOID*)&psl);\n\t\tif (SUCCEEDED(hres))\n\t\t{\n\t\t\tchar startmenu[MAX_OSPATH];\n\t\t\tWCHAR wsz[MAX_PATH];\n\t\t\tIPersistFile *ppf;\n\t\t\twiden(wsz, sizeof(wsz), newexepath);\n\t\t\tpsl->lpVtbl->SetPath(psl, wsz);\n\t\t\twiden(wsz, sizeof(wsz), resultpath);\n\t\t\tpsl->lpVtbl->SetWorkingDirectory(psl, wsz);\n\t\t\thres = psl->lpVtbl->QueryInterface(psl, &qIID_IPersistFile, (LPVOID*)&ppf);\n\t\t\tif (SUCCEEDED(hres) && pSHGetSpecialFolderPathW(NULL, wsz, CSIDL_COMMON_PROGRAMS, TRUE))\n\t\t\t{\n\t\t\t\tWCHAR wsz[MAX_PATH];\n\t\t\t\tnarrowen(startmenu, sizeof(startmenu), wsz);\n\t\t\t\twiden(wsz, sizeof(wsz), va(\"%s/%s.lnk\", startmenu, fs_gamename.string));\n\t\t\t\thres = ppf->lpVtbl->Save(ppf, wsz, TRUE);\n\t\t\t\tif (hres == E_ACCESSDENIED && pSHGetSpecialFolderPathW(NULL, wsz, CSIDL_PROGRAMS, TRUE))\n\t\t\t\t{\n\t\t\t\t\tnarrowen(startmenu, sizeof(startmenu), wsz);\n\t\t\t\t\twiden(wsz, sizeof(wsz), va(\"%s/%s.lnk\", startmenu, fs_gamename.string));\n\t\t\t\t\thres = ppf->lpVtbl->Save(ppf, wsz, TRUE);\n\t\t\t\t}\n\t\t\t\tppf->lpVtbl->Release(ppf);\n\t\t\t}\n\t\t\tpsl->lpVtbl->Release(psl);\n\t\t}\n\t}\n\n\t//now start it up properly.\n\tShellExecuteW(mainwindow, L\"open\", widen(wide, sizeof(wide), newexepath), Q_strcasecmp(fs_manifest->installation, \"quake\")?L\"\":L\"+sys_register_file_associations\", widen(wide2, sizeof(wide2), resultpath), SW_SHOWNORMAL);\n\treturn true;\n}\nqboolean Sys_RunInstaller(void)\n{\n\tHINSTANCE ch;\n\tchar exepath[MAX_OSPATH];\n\tif (COM_CheckParm(\"-doinstall\"))\n\t\treturn Sys_DoInstall();\n\tif (!com_installer)\n\t\treturn false;\n\tif (MessageBoxU(NULL, va(\"%s is not installed. Install now?\", fs_gamename.string), fs_gamename.string, MB_OKCANCEL|MB_ICONQUESTION|MB_TOPMOST) == IDOK)\n\t{\n\t\tGetModuleFileName(NULL, exepath, sizeof(exepath));\n\t\tch = ShellExecute(mainwindow, \"runas\", com_argv[0], va(\"%s -doinstall\", COM_Parse(GetCommandLine())), NULL, SW_SHOWNORMAL);\n\t\tif ((qintptr_t)ch > 32)\n\t\t\treturn true;\t//succeeded. should quit out.\n\t\treturn Sys_DoInstall();\t//if it failed, try doing it with the current privileges\n\t}\n\treturn true;\n}\n#endif\n\n#define RESLANG MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_UK)\nstatic const char *Sys_FindManifest(void)\n{\n\tconst char *fmf;\n\tHRSRC hdl = FindResource(NULL, MAKEINTRESOURCE(1), RT_RCDATA);\n\tHGLOBAL hgl = LoadResource(NULL, hdl);\n\tfmf = LockResource(hgl);\n//\tMessageBox(NULL, fmf, \"Embedded manifest\", 0);\n\treturn fmf;\n}\n\n//size info that microsoft recommends\nstatic const struct\n{\n\tint width;\n\tint height;\n\tint bpp;\n} icosizes[] = {\n//\t{96, 96, 32},\n//\t{48, 48, 32},\n//\t{32, 32, 32},\n//\t{16, 16, 32},\n//\t{16, 16, 4},\n//\t{48, 48, 4},\n//\t{32, 32, 4},\n//\t{16, 16, 1},\n//\t{48, 48, 1},\n//\t{32, 32, 1},\n\t{256, 256, 32}\t//vista!\n};\n//dates back to 16bit windows. bah.\n#pragma pack(push)\n#pragma pack(2)\ntypedef struct\n{\n\tWORD idReserved;\n\tWORD idType;\n\tWORD idCount;\n\tstruct\n\t{\n\t\tBYTE  bWidth;\n\t\tBYTE  bHeight;\n\t\tBYTE  bColorCount;\n\t\tBYTE  bReserved;\n\t\tWORD  wPlanes;\n\t\tWORD  wBitCount;\n\t\tDWORD dwBytesInRes;\n\t\tWORD  nId;\n\t} idEntries[sizeof(icosizes)/sizeof(icosizes[0])];\n} icon_group_t;\n#pragma pack(pop)\n\nstatic void Sys_MakeInstaller(const char *name)\n{\n\tvfsfile_t *filehandle;\n\tqbyte *filedata;\n\tunsigned int filelen;\n\tchar *error = NULL;\n\tchar *warn = NULL;\n\tHANDLE bin;\n\tchar ourname[MAX_OSPATH];\n\tchar newname[MAX_OSPATH];\n\tchar tmpname[MAX_OSPATH];\n\n\tQ_snprintfz(newname, sizeof(newname), \"%s.exe\", name);\n\tQ_snprintfz(tmpname, sizeof(tmpname), \"tmp.exe\");\n\n\tGetModuleFileName(NULL, ourname, sizeof(ourname));\n\n\tif (!CopyFile(ourname, tmpname, FALSE))\n\t\terror = va(\"\\\"%s\\\" already exists or cannot be written\", tmpname);\n\n\tif (!(bin = BeginUpdateResource(tmpname, FALSE)))\n\t\terror = \"BeginUpdateResource failed\";\n\telse\n\t{\n\t\tfilehandle = VFSOS_Open(va(\"%s.png\", name), \"rb\");\n\t\tif (filehandle)\n\t\t{\n\t\t\ticon_group_t icondata;\n\t\t\tqbyte *rgbadata;\n\t\t\tint imgwidth, imgheight;\n\t\t\tint iconid = 1;\n\t\t\tuploadfmt_t format;\n\t\t\tmemset(&icondata, 0, sizeof(icondata));\n\t\t\ticondata.idType = 1;\n\t\t\tfilelen = VFS_GETLEN(filehandle);\n\t\t\tfiledata = BZ_Malloc(filelen);\n\t\t\tVFS_READ(filehandle, filedata, filelen);\n\t\t\tVFS_CLOSE(filehandle);\n\n\t\t\t//nuke existing icons.\n\t\t\tUpdateResource(bin, RT_GROUP_ICON, MAKEINTRESOURCE(1), RESLANG, NULL, 0);\n\t\t\tUpdateResource(bin, RT_GROUP_ICON, MAKEINTRESOURCE(2), RESLANG, NULL, 0);\n//\t\t\tUpdateResource(bin, RT_GROUP_ICON, MAKEINTRESOURCE(3), RESLANG, NULL, 0);\n\n\t\t\trgbadata = ReadRawImageFile(filedata, filelen, &imgwidth, &imgheight, &format, true, va(\"%s.png\", name));\n\t\t\tif (!rgbadata)\n\t\t\t\terror = \"unable to read icon image\";\n\t\t\telse\n\t\t\t{\n\t\t\t\tvoid *data = NULL;\n\t\t\t\tunsigned int datalen = 0;\n\t\t\t\tunsigned int i;\n\t\t\t\textern cvar_t gl_lerpimages;\n\t\t\t\tgl_lerpimages.ival = 1;\n\t\t\t\tfor (i = 0; i < sizeof(icosizes)/sizeof(icosizes[0]); i++)\n\t\t\t\t{\n\t\t\t\t\tunsigned int x,y;\n\t\t\t\t\tif (icosizes[i].width > imgwidth || icosizes[i].height > imgheight)\n\t\t\t\t\t\tcontinue;\t//ignore icons if they're bigger than the original icon.\n\n\t\t\t\t\tif (icosizes[i].bpp == 32 && icosizes[i].width >= 128 && icosizes[i].height >= 128 && icosizes[i].width == imgwidth && icosizes[i].height == imgheight)\n\t\t\t\t\t{\t//png compression. oh look. we originally loaded a png!\n\t\t\t\t\t\tdata = filedata;\n\t\t\t\t\t\tdatalen = filelen;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\t//generate the bitmap info\n\t\t\t\t\t\tBITMAPV4HEADER *bi;\n\t\t\t\t\t\tqbyte *out, *outmask;\n\t\t\t\t\t\tqbyte *in, *inrow;\n\t\t\t\t\t\tunsigned int outidx;\n\n\t\t\t\t\t\tbi = data = Z_Malloc(sizeof(*bi) + icosizes[i].width * icosizes[i].height * 5 + icosizes[i].height*4);\n\t\t\t\t\t\tmemset(bi,0, sizeof(BITMAPINFOHEADER));\n\t\t\t\t\t\tbi->bV4Size\t\t\t\t= sizeof(BITMAPINFOHEADER);\n\t\t\t\t\t\tbi->bV4Width\t\t\t= icosizes[i].width;\n\t\t\t\t\t\tbi->bV4Height\t\t\t= icosizes[i].height * 2;\n\t\t\t\t\t\tbi->bV4Planes\t\t\t= 1;\n\t\t\t\t\t\tbi->bV4BitCount\t\t\t= icosizes[i].bpp;\n\t\t\t\t\t\tbi->bV4V4Compression\t= BI_RGB;\n\t\t\t\t\t\tbi->bV4ClrUsed\t\t\t= (icosizes[i].bpp>=32?0:(1u<<icosizes[i].bpp));\n\n\t\t\t\t\t\tdatalen = bi->bV4Size;\n\t\t\t\t\t\tout = (qbyte*)data + datalen;\n\t\t\t\t\t\tdatalen += ((icosizes[i].width*icosizes[i].bpp/8+3)&~3) * icosizes[i].height;\n\t\t\t\t\t\toutmask = (qbyte*)data + datalen;\n\t\t\t\t\t\tdatalen += ((icosizes[i].width+31)&~31)/8 * icosizes[i].height;\n\n\t\t\t\t\t\tin = Image_ResampleTexture(format, rgbadata, imgwidth, imgheight, NULL, icosizes[i].width, icosizes[i].height);\n\n\t\t\t\t\t\tinrow = in;\n\t\t\t\t\t\toutidx = 0;\n\t\t\t\t\t\tif (icosizes[i].bpp == 32)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tfor (y = 0; y < icosizes[i].height; y++)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tinrow = in + 4*icosizes[i].width*(icosizes[i].height-1-y);\n\t\t\t\t\t\t\t\tfor (x = 0; x < icosizes[i].width; x++)\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tif (inrow[3] == 0)\t//transparent\n\t\t\t\t\t\t\t\t\t\toutmask[outidx>>3] |= 1u<<(outidx&7);\n\t\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tout[0] = inrow[2];\n\t\t\t\t\t\t\t\t\t\tout[1] = inrow[1];\n\t\t\t\t\t\t\t\t\t\tout[2] = inrow[0];\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tout += 4;\n\t\t\t\t\t\t\t\t\toutidx++;\n\t\t\t\t\t\t\t\t\tinrow += 4;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tif (x & 3)\n\t\t\t\t\t\t\t\t\tout += 4 - (x&3);\n\t\t\t\t\t\t\t\toutidx = (outidx + 31)&~31;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif (!error && !UpdateResource(bin, RT_ICON, MAKEINTRESOURCE(iconid), 0, data, datalen))\n\t\t\t\t\t\terror = \"UpdateResource failed (icon data)\";\n\n\t\t\t\t\t//and make a copy of it in the icon list\n\t\t\t\t\ticondata.idEntries[icondata.idCount].bWidth = (icosizes[i].width<256)?icosizes[i].width:0;\n\t\t\t\t\ticondata.idEntries[icondata.idCount].bHeight = (icosizes[i].height<256)?icosizes[i].height:0;\n\t\t\t\t\ticondata.idEntries[icondata.idCount].wBitCount = icosizes[i].bpp;\n\t\t\t\t\ticondata.idEntries[icondata.idCount].wPlanes = 1;\n\t\t\t\t\ticondata.idEntries[icondata.idCount].bColorCount = (icosizes[i].bpp>=8)?0:(1u<<icosizes[i].bpp);\n\t\t\t\t\ticondata.idEntries[icondata.idCount].dwBytesInRes = datalen;\n\t\t\t\t\ticondata.idEntries[icondata.idCount].nId = iconid++;\n\t\t\t\t\ticondata.idCount++;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (!error && !UpdateResource(bin, RT_GROUP_ICON, MAKEINTRESOURCE(IDI_ICON1), RESLANG, &icondata, (qbyte*)&icondata.idEntries[icondata.idCount] - (qbyte*)&icondata))\n\t\t\t\terror = \"UpdateResource failed (icon group)\";\n\t\t\tBZ_Free(filedata);\n\t\t}\n\t\telse\n\t\t\twarn = va(\"%s.png not found\", name);\n\n\t\tfilehandle = VFSOS_Open(va(\"%s.fmf\", name), \"rb\");\n\t\tif (filehandle)\n\t\t{\n\t\t\tfilelen = VFS_GETLEN(filehandle);\n\t\t\tfiledata = BZ_Malloc(filelen+1);\n\t\t\tfiledata[filelen] = 0;\n\t\t\tVFS_READ(filehandle, filedata, filelen);\n\t\t\tVFS_CLOSE(filehandle);\n\t\t\tif (!error && !UpdateResource(bin, RT_RCDATA, MAKEINTRESOURCE(1), 0, filedata, filelen+1))\n\t\t\t\terror = \"UpdateResource failed (manicfest)\";\n\t\t\tBZ_Free(filedata);\n\t\t}\n\t\telse\n\t\t\terror = va(\"%s.fmf not found in working directory\", name);\n\n\t\tif (!EndUpdateResource(bin, !!error) && !error)\n\t\t\terror = \"EndUpdateResource failed. Check access permissions.\";\n\n\t\tDeleteFile(newname);\n\t\tMoveFile(tmpname, newname);\n\t}\n\n\tif (!error)\n\t\terror = warn;\n\n\tif (error)\n\t{\n\t\tSys_Printf(\"%s\", error);\n\t\tSys_Error(\"%s\", error);\n\t}\n}\n#endif\n\n#if defined(_MSC_VER) && defined(_AMD64_)\n#pragma optimize( \"\", off)\t//64bit msvc sucks and falls over when trying to inline Host_Frame\n#endif\n#ifdef __GNUC__\n__attribute__((visibility(\"default\")))\n#endif\nint WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)\n{\n//    MSG\t\t\t\tmsg;\n\tquakeparms_t\tparms;\n\tdouble\t\t\ttime, oldtime, newtime;\n\tchar\tcwd[1024], bindir[1024];\n\tconst char *qtvfile = NULL;\n\tchar lang[32];\n\tchar ctry[32];\n\tint c;\n\n\t/* previous instances do not exist in Win32 */\n\tif (hPrevInstance)\n\t\treturn EXIT_FAILURE;\n\n\t/* determine if we're on nt early, so we don't do the wrong thing when checking commandlines */\n\t{\n\t\tOSVERSIONINFOA vinfo;\n\t\tvinfo.dwOSVersionInfoSize = sizeof(vinfo);\n\t\tif (GetVersionExA (&vinfo))\n\t\t\tWinNT = vinfo.dwPlatformId == VER_PLATFORM_WIN32_NT;\n\t}\n\n#if defined(_DEBUG) && defined(_MSC_VER) && defined(MULTITHREAD)\n\tSys_SetThreadName(-1, \"main thread\");\n#endif\n\n\tmemset(&parms, 0, sizeof(parms));\n\n\t/*work around potentially serious windows flaw loading dlls from the current directory, by loading a dll...*/\n\t{\n\t\tBOOL (WINAPI *pSetDllDirectoryW)(LPCWSTR lpPathName);\n\t\tdllfunction_t ffsfuncs[] = {{(void*)&pSetDllDirectoryW, \"SetDllDirectoryW\"}, {NULL,NULL}};\n\t\tif (Sys_LoadLibrary(\"kernel32.dll\", ffsfuncs))\n\t\t\tpSetDllDirectoryW(L\"\");\t//disables it (null for 'use working directory')\n\t}\n\n\tWin7_Init();\n\n#ifdef _MSC_VER\n#if _M_IX86_FP >= 1\n\t{\n\t\tint idedx;\n\t\tchar cpuname[13];\n\t\t/*I'm not going to check to make sure cpuid works.*/\n\t\t__asm\n\t\t{\n\t\t\txor eax, eax\n\t\t\tcpuid\n\t\t\tmov dword ptr [cpuname+0],ebx\n\t\t\tmov dword ptr [cpuname+4],edx\n\t\t\tmov dword ptr [cpuname+8],ecx\n\t\t}\n\t\tcpuname[12] = 0;\n\t\t__asm\n\t\t{\n\t\t\tmov eax, 0x1\n\t\t\tcpuid\n\t\t\tmov idedx, edx\n\t\t}\n#if _M_IX86_FP >= 2\n\t\tif (!(idedx&(1<<26)))\n\t\t\tMessageBox(NULL, \"This is an SSE2 optimised build, and your cpu doesn't seem to support it\", DISTRIBUTION, 0);\n\t\telse\n#endif\n\t\t\t  if (!(idedx&(1<<25)))\n\t\t\t\tMessageBox(NULL, \"This is an SSE optimised build, and your cpu doesn't seem to support it\", DISTRIBUTION, 0);\n\t}\n#endif\n#endif\n\n#ifdef CATCHCRASH\n\tLoadLibraryU (\"DBGHELP\");\t//heap corruption can prevent loadlibrary from working properly, so do this in advance.\n#ifdef MSVC_SEH\n\t__try\n#else\n\t{\n\t\tPVOID (WINAPI *pAddVectoredExceptionHandler)(ULONG\tFirstHandler,\tPVECTORED_EXCEPTION_HANDLER VectoredHandler);\n\t\tdllfunction_t dbgfuncs[] = {{(void*)&pAddVectoredExceptionHandler, \"AddVectoredExceptionHandler\"}, {NULL,NULL}};\n\t\tif (Sys_LoadLibrary(\"kernel32.dll\", dbgfuncs) && pAddVectoredExceptionHandler)\n\t\t\tpAddVectoredExceptionHandler(0, nonmsvc_CrashExceptionHandler);\n\t}\n#endif\n#endif\n\t{\n/*\n#ifndef _DEBUG\n#ifdef _MSC_VER\n\t\tsignal (SIGFPE,\tSignal_Error_Handler);\n\t\tsignal (SIGILL,\tSignal_Error_Handler);\n\t\tsignal (SIGSEGV,\tSignal_Error_Handler);\n#endif\n#endif\n*/\n\t\tglobal_hInstance = hInstance;\n\t\tglobal_nCmdShow = nCmdShow;\n\n#ifdef RESTARTTEST\n\t\tsetjmp (restart_jmpbuf);\n#endif\n\n\t\tif (WinNT)\n\t\t{\n\t\t\twchar_t widebindir[1024];\n\t\t\tGetModuleFileNameW(NULL, widebindir, sizeof(widebindir)/sizeof(widebindir[0])-1);\n\t\t\tnarrowen(bindir, sizeof(bindir)-1, widebindir);\n\t\t}\n\t\telse\n\t\t\tGetModuleFileNameA(NULL, bindir, sizeof(bindir)-1);\n\t\tparms.argc = Sys_ProcessCommandline(sys_argv, MAX_NUM_ARGVS, bindir);\n\t\t*COM_SkipPath(bindir) = 0;\n\t\tparms.argv = (const char **)sys_argv;\n\n\t\tparms.binarydir = bindir;\n\t\tCOM_InitArgv (parms.argc, parms.argv);\n\n#ifdef PLUGINS\n\t\tc = COM_CheckParm(\"--plugwrapper\");\n\t\tif (c)\n\t\t{\n\t\t\tint (QDECL *thefunc) (void);\n\t\t\tdllhandle_t *lib;\n\t\t\thost_parms = parms;//not really initialising, but the filesystem needs it\n\t\t\tlib = Sys_LoadLibrary(com_argv[c+1], NULL);\n\t\t\tif (lib)\n\t\t\t{\n\t\t\t\tthefunc = Sys_GetAddressForName(lib, com_argv[c+2]);\n\t\t\t\tif (thefunc)\n\t\t\t\t\treturn thefunc();\n\t\t\t}\n\t\t\tMessageBox(NULL, \"Unable to start up plugin wrapper\", FULLENGINENAME, 0);\n\t\t\treturn EXIT_FAILURE;\n\t\t}\n#endif\n\n\t\tc = COM_CheckParm(\"-qcdebug\");\n\t\tif (c)\n\t\t\tisPlugin = 3;\n\t\telse\n\t\t{\n\t\t\tc = COM_CheckParm(\"-plugin\");\n\t\t\tif (c)\n\t\t\t{\n\t\t\t\tif (c < com_argc && !strcmp(com_argv[c+1], \"qcdebug\"))\n\t\t\t\t\tisPlugin = 2;\n\t\t\t\telse\n\t\t\t\t\tisPlugin = 1;\n\t\t\t}\n\t\t\telse\n\t\t\t\tisPlugin = 0;\n\t\t}\n\n\t\tc = COM_CheckParm(\"-register_types\");\n\t\tif (c)\n\t\t{\n\t\t\tSys_DoFileAssociations(1, (c+1 < com_argc)?com_argv[c+1]:NULL);\n\t\t\treturn EXIT_SUCCESS;\n\t\t}\n\t\t/*\n\t\telse if (!isPlugin)\n\t\t{\n\t\t\tif (MyRegGetIntValue(HKEY_CURRENT_USER, \"Software\\\\\"FULLENGINENAME, \"filetypes\", -1) != ASSOC_VERSION)\n\t\t\t{\n\t\t\t\tDWORD dw = ASSOC_VERSION;\n\t\t\t\tif (IDYES == MessageBox(NULL, \"Register file associations?\", \"FTE First Start\", MB_YESNO))\n\t\t\t\t\tSys_DoFileAssociations(0);\n\t\t\t\tMyRegSetValue(HKEY_CURRENT_USER, \"Software\\\\\"FULLENGINENAME, \"filetypes\", REG_DWORD, &dw, sizeof(dw)); \n\t\t\t}\n\t\t}\n\t\t*/\n\n#if defined(CATCHCRASH) && defined(MULTITHREAD)\n#ifdef _MSC_VER\n\t\tif (COM_CheckParm(\"-watchdog\"))\n\t\t\twatchdogthread = Sys_CreateThread(\"watchdog\", watchdogthreadfunction, NULL, 0, 0); \n#endif\n#endif\n\n\t\tif (isPlugin==1)\n\t\t{\n\t\t\tprintf(\"status Starting up!\\n\");\n\t\t\tfflush(stdout);\n\t\t}\n\n\t\tif (COM_CheckParm(\"--version\") || COM_CheckParm(\"-v\"))\n\t\t{\n\t\t\tprintf(\"version: %s\\n\", version_string());\n\t\t\treturn EXIT_SUCCESS;\n\t\t}\n\t\tif (COM_CheckParm(\"-outputdebugstring\"))\n\t\t\tdebugout = true;\n\n\t\tif (WinNT)\n\t\t{\n\t\t\twchar_t wcwd[MAX_OSPATH];\n\t\t\tif (!GetCurrentDirectoryW (sizeof(wcwd)/sizeof(wchar_t), wcwd))\n\t\t\t\tSys_Error (\"Couldn't determine current directory\");\n\t\t\tnarrowen(cwd, sizeof(cwd), wcwd);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (!GetCurrentDirectoryA (sizeof(cwd), cwd))\n\t\t\t\tSys_Error (\"Couldn't determine current directory\");\n\t\t}\n\n#ifdef WEBCLIENT\n\t\tc = COM_CheckParm(\"-makeinstaller\");\n\t\tif (c)\n\t\t{\n\t\t\tSys_MakeInstaller(parms.argv[c+1]);\n\t\t\treturn EXIT_SUCCESS;\n\t\t}\n\t\tparms.manifest = Sys_FindManifest();\n#endif\n\n\t\tif (parms.argc >= 2)\n\t\t{\n\t\t\tif (*parms.argv[1] != '-' && *parms.argv[1] != '+')\n\t\t\t{\n\t\t\t\tchar *e;\n\n\t\t\t\tif (parms.argc == 2 && !strchr(parms.argv[1], '\\\"') && !strchr(parms.argv[1], ';') && !strchr(parms.argv[1], '\\n') && !strchr(parms.argv[1], '\\r'))\n\t\t\t\t{\n\t\t\t\t\tHWND old;\n\t\t\t\t\tqtvfile = parms.argv[1];\n\n\t\t\t\t\told = FindWindowW(L\"FTEGLQuake\", NULL);\n\t\t\t\t\tif (!old)\n\t\t\t\t\t\told = FindWindowW(L\"FTED3D11QUAKE\", NULL);\n\t\t\t\t\tif (!old)\n\t\t\t\t\t\told = FindWindowW(L\"FTED3D9QUAKE\", NULL);\n\t\t\t\t\tif (!old)\n\t\t\t\t\t\told = FindWindowW(L\"FTED3D8QUAKE\", NULL);\n\t\t\t\t\tif (old)\n\t\t\t\t\t{\n\t\t\t\t\t\tCOPYDATASTRUCT cds;\n\t\t\t\t\t\tcds.dwData = 0xdeadf11eu;\n\t\t\t\t\t\tcds.cbData = strlen(qtvfile);\n\t\t\t\t\t\tcds.lpData = (void*)qtvfile;\n\t\t\t\t\t\tif (SendMessage(old, WM_COPYDATA, (WPARAM)GetDesktopWindow(), (LPARAM)&cds))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tSleep(10*1000);\t//sleep for 10 secs so the real engine has a chance to open it, if the program that gave it is watching to see if we quit.\n\t\t\t\t\t\t\treturn EXIT_SUCCESS;\t//message sent.\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tMessageBox(NULL, va(\"Invalid commandline:\\n%s\", lpCmdLine), FULLENGINENAME, 0);\n\t\t\t\t\treturn EXIT_FAILURE;\n\t\t\t\t}\n\n\t\t\t\tGetModuleFileName(NULL, cwd, sizeof(cwd)-1);\n\t\t\t\tfor (e = cwd+strlen(cwd)-1; e >= cwd; e--)\n\t\t\t\t{\n\t\t\t\t\tif (*e == '/' || *e == '\\\\')\n\t\t\t\t\t{\n\t\t\t\t\t\t*e = 0;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t//98+/nt4+\n\t\tif (GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SISO639LANGNAME, lang, sizeof(lang)) > 0)\n\t\t{\n\t\t\tif (GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_SISO3166CTRYNAME, ctry, sizeof(ctry)) > 0)\n\t\t\t\tQ_snprintfz(sys_language, sizeof(sys_language), \"%s_%s\", lang, ctry); \n\t\t\telse\n\t\t\t\tQ_snprintfz(sys_language, sizeof(sys_language), \"%s\", lang); \n\t\t}\n\n\t\tTL_InitLanguages(parms.binarydir);\n\t\t//tprints are now allowed\n\n\t\tif (*cwd && cwd[strlen(cwd)-1] != '\\\\' && cwd[strlen(cwd)-1] != '/')\n\t\t\tQ_strncatz(cwd, \"/\", sizeof(cwd));\n\n\t\tparms.basedir = cwd;\n\n\t\tparms.argc = com_argc;\n\t\tparms.argv = com_argv;\n\n#if !defined(CLIENTONLY) && !defined(SERVERONLY)\n\t\tif (COM_CheckParm (\"-dedicated\"))\n\t\t\tisDedicated = true;\n\t#ifdef SUBSERVERS\n\t\tif (COM_CheckParm(\"-clusterslave\"))\n\t\t{\n\t\t\tisDedicated = isClusterSlave = true;\n#ifdef _DEBUG\n\t\t\tMessageBox(0, \"New cluster slave starting\\nAttach to process now, if desired.\", \"FTEQW Debug Build\", 0);\n#endif\n\t\t\tSSV_SetupControlPipe(Sys_GetStdInOutStream(), false);\n\t\t}\n\t#endif\n#endif\n\n\t\tif (isDedicated)\n\t\t{\n#if !defined(CLIENTONLY)\n\t\t\tif (!Sys_InitTerminal())\n\t\t\t\tSys_Error (\"Couldn't allocate dedicated server console\");\n#endif\n\t\t}\n\n\t\tif (!Sys_Startup_CheckMem(&parms))\n\t\t\tSys_Error (\"Not enough memory free; check disk space\\n\");\n\n//\t\tFS_ChangeGame(NULL, true, true);\n//\t\tif (Sys_CheckUpdated(bindir, sizeof(bindir)))\n//\t\t\treturn true;\n\n\n#ifndef CLIENTONLY\n\t\tif (isDedicated)\t//compleate denial to switch to anything else - many of the client structures are not initialized.\n\t\t{\n\t\t\tfloat delay;\n\n\t\t\tSV_Init (&parms);\n\n\t\t\tdelay = SV_Frame();\n\n\t\t\twhile (1)\n\t\t\t{\n\t\t\t\tif (!isDedicated)\n\t\t\t\t\tSys_Error(\"Dedicated was cleared\");\n\t\t\t\tNET_Sleep(delay, false);\n\t\t\t\tdelay = SV_Frame();\n\t\t\t}\n\t\t\treturn EXIT_FAILURE;\n\t\t}\n#endif\n\n\t\ttevent = CreateEvent(NULL, FALSE, FALSE, NULL);\n\t\tif (!tevent)\n\t\t\tSys_Error (\"Couldn't create event\");\n\n#ifdef SERVERONLY\n\t\tSys_Printf (\"SV_Init\\n\");\n\t\tSV_Init(&parms);\n#else\n\t\tSys_Printf (\"Host_Init\\n\");\n\t\tHost_Init (&parms);\n#endif\n\n\t\toldtime = Sys_DoubleTime ();\n\n\t//client console should now be initialized.\n\n\t\t#ifndef MINGW\n\t\t#if _MSC_VER > 1200\n\t\tWin7_TaskListInit();\n\t\t#endif\n\t\t#endif\n\n\t\tif (isPlugin==1)\n\t\t{\n\t\t\tprintf(\"status Running!\\n\");\n\t\t\tfflush(stdout);\n\t\t}\n\n\t\t/* main window message loop */\n\t\twhile (1)\n\t\t{\n#ifdef CATCHCRASH\n\t\t\twatchdogframe++;\n#endif\n\t\t\tif (isDedicated)\n\t\t\t{\n\t#ifndef CLIENTONLY\n\t\t\t\tfloat delay;\n\n\t\t\t// find time passed since last cycle\n\t\t\t\tnewtime = Sys_DoubleTime ();\n\t\t\t\ttime = newtime - oldtime;\n\t\t\t\toldtime = newtime;\n\n\t\t\t\tdelay = SV_Frame ();\n\n\t\t\t\tNET_Sleep(delay, false);\n\t#else\n\t\t\t\tSys_Error(\"wut?\");\n\t#endif\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t#ifndef SERVERONLY\n\t\t\t\tdouble sleeptime;\n\t\t\t\tnewtime = Sys_DoubleTime ();\n\t\t\t\ttime = newtime - oldtime;\n\t\t\t\tsleeptime = Host_Frame (time);\n\t\t\t\toldtime = newtime;\n\n\t\t\t\tSetHookState(vid.activeapp);\n\n\t\t\t\t/*sleep if its not yet time for a frame*/\n\t\t\t\tif (sleeptime)\n\t\t\t\t\tSys_Sleep(sleeptime);\n\t#else\n\t\t\t\tSys_Error(\"wut?\");\n\t#endif\n\t\t\t}\n\t\t}\n\t}\n#ifdef CATCHCRASH\n#ifdef MSVC_SEH\n\t__except (CrashExceptionHandler(false, GetExceptionCode(), GetExceptionInformation()))\n\t{\n\t\treturn EXIT_FAILURE;\n\t}\n#endif\n#endif\n\n\t/* return success of application */\n\treturn EXIT_FAILURE;\n}\n#ifdef _MSC_VER\n#pragma optimize( \"\", on)\t//revert back to default optimisations again.\n#endif\n\n#if 0\t//define this if you're somehow getting windows' consle subsystem instead of the proper windows one\nint __cdecl main(void)\n{\n\tchar *cmdline;\n\tFreeConsole();\n\tcmdline = GetCommandLine();\n\twhile (*cmdline && *cmdline == ' ')\n\t\tcmdline++;\n\tif (*cmdline == '\\\"')\n\t{\n\t\tcmdline++;\n\t\twhile (*cmdline && *cmdline != '\\\"')\n\t\t\tcmdline++;\n\t\tif (*cmdline == '\\\"')\n\t\t\tcmdline++;\n\t}\n\telse\n\t{\n\t\twhile (*cmdline && *cmdline != ' ')\n\t\t\tcmdline++;\n\t}\n\treturn WinMain(GetModuleHandle(NULL), NULL, cmdline, SW_NORMAL);\n}\n#endif \n\n//now queries at startup and then caches, to avoid mode changes from giving weird results.\nqboolean Sys_GetDesktopParameters(int *width, int *height, int *bpp, int *refreshrate)\n{\n\t*width = desktopsettings.width;\n\t*height = desktopsettings.height;\n\t*bpp = desktopsettings.bpp;\n\t*refreshrate = desktopsettings.rate;\n\treturn true;\n}\n\nstatic void Sys_QueryDesktopParameters(void)\n{\n\tHDC hdc;\n\n\thdc = GetDC(NULL);\n\n\tdesktopsettings.width = GetDeviceCaps(hdc, HORZRES);\n\tdesktopsettings.height = GetDeviceCaps(hdc, VERTRES);\n\tdesktopsettings.bpp = GetDeviceCaps(hdc, BITSPIXEL);\n\tdesktopsettings.rate = GetDeviceCaps(hdc, VREFRESH);\n\n\tif (desktopsettings.rate == 1)\n\t\tdesktopsettings.rate = 0;\n\n\tReleaseDC(NULL, hdc);\n}\n\nvoid Sys_Sleep (double seconds)\n{\n\tSleep(seconds * 1000);\n}\n\n\n\n\nHCURSOR\thArrowCursor, hCustomCursor;\nvoid *WIN_CreateCursor(const qbyte *imagedata, int width, int height, uploadfmt_t format, float hotx, float hoty, float scale)\n{\n\tBITMAPV4HEADER bi;\n\tDWORD x,y;\n\tHCURSOR hAlphaCursor = NULL;\n\tICONINFO ii;\n\tHDC maindc;\n\n\tconst qbyte *rgbadata;\n\tqbyte *bgradata, *bgradata_start;\n\tvoid *scaled = NULL;\n\tif (!imagedata)\n\t\treturn NULL;\n\n\t// FIXME: CreateIconIndirect does NOT understand DPI scaling, and will show a tiny cursor in such cases.\n\t// we should rescale scale by vid_conautoscale etc.\n\tif (scale != 1)\n\t{\n\t\tint nw,nh;\n\t\tqbyte *nd;\n\t\tnw = width * scale;\n\t\tnh = height * scale;\n\t\tif (nw <= 0 || nh <= 0 || nw > 128 || nh > 128)\t//don't go crazy.\n\t\t\treturn NULL;\n\t\tnd = Image_ResampleTexture(format, imagedata, width, height, NULL, nw, nh);\n\t\twidth = nw;\n\t\theight = nh;\n\t\timagedata = scaled = nd;\n\t}\n\n\tmemset(&bi,0, sizeof(bi));\n\tbi.bV4Size\t\t\t= sizeof(bi);\n\tbi.bV4Width\t\t\t= width;\n\tbi.bV4Height\t\t= height;\n\tbi.bV4Planes\t\t= 1;\n\tbi.bV4BitCount\t\t= 32;\n\tbi.bV4V4Compression\t= BI_BITFIELDS;\n\t// The following mask specification specifies a supported 32 BPP\n\t// alpha format for Windows XP.\n\t//FIXME: can we not just specify it as RGBA? meh.\n\tbi.bV4RedMask\t\t= 0x00FF0000;\n\tbi.bV4GreenMask\t\t= 0x0000FF00;\n\tbi.bV4BlueMask\t\t= 0x000000FF;\n\tbi.bV4AlphaMask\t\t= 0xFF000000; \n\n\t// Create the DIB section with an alpha channel.\n\tmaindc = GetDC(mainwindow);\n\tii.hbmColor = CreateDIBSection(maindc, (BITMAPINFO *)&bi, DIB_RGB_COLORS, (void **)&bgradata_start, NULL, 0);\n\tReleaseDC(mainwindow, maindc);\n\n\tif (!ii.hbmColor)\n\t{\n\t\tBZ_Free(scaled);\n\t\treturn NULL;\n\t}\n\n\tfor (rgbadata=imagedata,y=0;y<height;y++)\n\t{\n\t\tbgradata = bgradata_start + (height-1-y)*width*4;\n\t\tfor (x=0;x<width;x++)\n\t\t{\n\t\t\tbgradata[0] = rgbadata[2];\n\t\t\tbgradata[1] = rgbadata[1];\n\t\t\tbgradata[2] = rgbadata[0];\n\t\t\tbgradata[3] = rgbadata[3];\n\t\t\tbgradata+=4;\n\t\t\trgbadata+=4;\n\t\t}\n\t}\n\n\tBZ_Free(scaled);\n\n\tii.fIcon = FALSE;  // Change fIcon to TRUE to create an alpha icon\n\tii.xHotspot = hotx;\n\tii.yHotspot = hoty;\n\tii.hbmMask = CreateBitmap(width,height,1,1,NULL);\n\n\t// Create the alpha cursor with the alpha DIB section.\n\thAlphaCursor = CreateIconIndirect(&ii);\n\n\tDeleteObject(ii.hbmColor);\n\tDeleteObject(ii.hbmMask);\n\n\treturn hAlphaCursor;\n}\n\nqboolean WIN_SetCursor(void *cursor)\n{\n\tstatic POINT\t\tcurrent_pos;\t//static to avoid bugs in vista(32) with largeaddressaware (this is fixed in win7). fixed exe base address prevents this from going above 2gb.\n\n\thCustomCursor = cursor;\n\n\t//move the cursor to ensure the WM_SETCURSOR thing is invoked properly.\n\t//this ensures all the nastyness of random programs randomly setting the current global cursor is handled by microsoft's code instead of mine.\n\t//if you're using rawinput there'll be no lost inpuit problems, yay...\n\tGetCursorPos(&current_pos);\n\tSetCursorPos(current_pos.x, current_pos.y);\n\treturn true;\n}\nvoid WIN_DestroyCursor(void *cursor)\n{\n\tDestroyIcon(cursor);\n}\n\n\n\n/*\nstatic HRESULT STDMETHODCALLTYPE DD_QueryInterface(IDropTarget *This, REFIID riid, void **ppvObject) {return E_NOINTERFACE;}\nstatic ULONG STDMETHODCALLTYPE DD_AddRef(IDropTarget *This) {return 1;}\nstatic ULONG STDMETHODCALLTYPE DD_Release(IDropTarget *This) {return 1;}\nstatic HRESULT STDMETHODCALLTYPE DD_DragEnter(IDropTarget *This, IDataObject *pDataObj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)\n{\n\t*pdwEffect &= DROPEFFECT_COPY;\n\treturn S_OK;\n}\nstatic HRESULT STDMETHODCALLTYPE DD_DragOver(IDropTarget *This, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)\n{\n\t*pdwEffect &= DROPEFFECT_COPY;\n\treturn S_OK;\n}\nstatic HRESULT STDMETHODCALLTYPE DD_DragLeave(IDropTarget *This)\n{\n\treturn S_OK;\n}\nstatic HRESULT STDMETHODCALLTYPE DD_Drop(IDropTarget *This, IDataObject *pDataObj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)\n{\n\t*pdwEffect &= DROPEFFECT_COPY;\n\treturn S_OK;\n}\nstatic struct IDropTargetVtbl MyDropTargetVtbl =\n{\n\tDD_QueryInterface,\n\tDD_AddRef,\n\tDD_Release,\n\tDD_DragEnter,\n\tDD_DragOver,\n\tDD_DragLeave,\n\tDD_Drop\n};\nstatic IDropTarget MyDropTarget = {&MyDropTargetVtbl};*/\nvoid WIN_WindowCreated(HWND window)\n{\n//\tOleInitialize(NULL);\n//\tif (FAILED(RegisterDragDrop(window, &MyDropTarget)))\n//\t\tCon_Printf(\"RegisterDragDrop failed\\n\");\n\tDragAcceptFiles(window, TRUE);\n}\n#endif\n"
  },
  {
    "path": "engine/client/sys_xdk.c",
    "content": "#include \"quakedef.h\"\r\n#include <xtl.h>\r\n\r\n#define MAXPRINTMSG 1024 // Yes\r\nqboolean isDedicated = false;\r\n\r\n/*Timers, supposedly xbox supports QueryPerformanceCounter stuff*/\r\ndouble Sys_DoubleTime (void)\r\n{\r\n\tstatic int\t\t\tfirst = 1;\r\n\tstatic LARGE_INTEGER\t\tqpcfreq;\r\n\tLARGE_INTEGER\t\tPerformanceCount;\r\n\tstatic LONGLONG\t\t\toldcall;\r\n\tstatic LONGLONG\t\t\tfirsttime;\r\n\tLONGLONG\t\t\tdiff;\r\n\r\n\tQueryPerformanceCounter (&PerformanceCount);\r\n\tif (first)\r\n\t{\r\n\t\tfirst = 0;\r\n\t\tQueryPerformanceFrequency(&qpcfreq);\r\n\t\tfirsttime = PerformanceCount.QuadPart;\r\n\t\tdiff = 0;\r\n\t}\r\n\telse\r\n\t\tdiff = PerformanceCount.QuadPart - oldcall;\r\n\tif (diff >= 0)\r\n\t\toldcall = PerformanceCount.QuadPart;\r\n\treturn (oldcall - firsttime) / (double)qpcfreq.QuadPart;\r\n}\r\nunsigned int Sys_Milliseconds (void)\r\n{\r\n\treturn Sys_DoubleTime()*1000;\r\n}\r\n\r\nNORETURN void VARGS Sys_Error (const char *error, ...)\r\n{\r\n\tCOM_WorkerAbort(error);\r\n\r\n\t//FIXME: panic! everyone panic!\r\n\t//you might want to figure out some way to display the message...\r\n\tfor(;;)\r\n\t\t;\r\n}\r\n\r\nvoid Sys_Sleep(double seconds)\r\n{\t//yields to other processes/threads for a bit.\r\n}\r\n\r\nvoid Sys_Quit (void)\r\n{\r\n#if 0\r\n\tHost_Shutdown ();\r\n\t//successful execution.\r\n\t//should go back to the system menu or something, or possibly longjmp out of the main loop.\r\n\tfor (;;)\r\n\t\t;\r\n\texit(1);\r\n#endif\r\n}\r\n\r\nvoid Sys_Shutdown(void)\r\n{\r\n}\r\nvoid Sys_Init(void)\r\n{\t//register system-specific cvars here\r\n}\r\n\r\n/*prints to dedicated server console or debug output*/\r\nvoid VARGS Sys_Printf (char *fmt, ...)\r\n{\r\n\tva_list\t\targptr;\r\n\tchar\t\tmsg[MAXPRINTMSG];\r\n\r\n\tva_start (argptr,fmt);\r\n\tvsnprintf(msg,sizeof(msg)-1, fmt,argptr);\r\n\tmsg[sizeof(msg)-1] = 0;\t//_vsnprintf sucks.\r\n\tva_end (argptr);\r\n\r\n\t//no idea what the stdout is hooked up to, lets try it anyway.\r\n\tprintf(\"%s\", msg);\r\n}\r\n\r\n/*returns the system video mode settings, ish*/\r\nqboolean Sys_GetDesktopParameters(int *width, int *height, int *bpp, int *refreshrate)\r\n{\r\n\t//FIXME: use XGetVideoStandard or XGetVideoFlags or something\r\n\t*width = 640;\r\n\t*height = 480;\r\n\t*bpp = 32;\r\n\t*refreshrate = 60;\r\n\treturn true;\r\n}\r\n\r\n/*dedicated server type stuff*/\r\nqboolean Sys_InitTerminal(void)\r\n{\r\n\treturn false;\t//failure\r\n}\r\nvoid Sys_CloseTerminal (void)\r\n{\r\n}\r\nchar *Sys_ConsoleInput (void)\r\n{\t//returns any text typed on the stdin, when acting as a dedicated server.\r\n\t//this includes debugger commands if we're integrated with fteqccgui\r\n\treturn NULL;\r\n}\r\n\r\n/*various system notifications*/\r\nvoid Sys_ServerActivity(void)\r\n{\t//player joined the server or said something. would normally flash the app in the system tray.\r\n}\r\n\r\nvoid Sys_RecentServer(char *command, char *target, char *title, char *desc) {\r\n\r\n}\r\n\r\n/*check any system message queues here*/\r\nvoid Sys_SendKeyEvents(void)\r\n{\r\n}\r\n\r\nqboolean Sys_RandomBytes(qbyte *string, int len)\r\n{\r\n\t//FIXME: should return some cryptographically strong random number data from some proper crypto library. C's rand function isn't really strong enough.\r\n\treturn false;\r\n}\r\n\r\n/*filesystem stuff*/\r\nvoid Sys_mkdir (const char *path)\r\n{\r\n}\r\nvoid Sys_rmdir (const char *path)\r\n{\r\n}\r\nqboolean Sys_remove (const char *path)\r\n{\r\n\treturn false;\t//false for failure\r\n}\r\nqboolean Sys_Rename (const char *oldfname, const char *newfname)\r\n{\r\n\treturn false;\t//false for failure\r\n}\r\nint Sys_EnumerateFiles (const char *gpath, const char *match, int (QDECL *func)(const char *fname, qofs_t fsize, void *parm, searchpathfuncs_t *spath), void *parm, searchpathfuncs_t *spath)\r\n{\r\n\t//if func returns false, abort with false return value, otherwise return true.\r\n\t//use wildcmd to compare two filenames\r\n\t//note that match may contain wildcards and multiple directories with multiple wildcards all over.\r\n\treturn true;\r\n}\r\n\r\n/*consoles don't tend to need system clipboards, so this is fully internal to our engine*/\r\n#define SYS_CLIPBOARD_SIZE  256\r\nstatic char clipboard_buffer[SYS_CLIPBOARD_SIZE] = {0};\r\nvoid Sys_Clipboard_PasteText(clipboardtype_t cbt, void (*callback)(void *cb, char *utf8), void *ctx)\r\n{\r\n\tcallback(ctx, clipboard_buffer);\r\n}\r\nvoid Sys_SaveClipboard(clipboardtype_t cbt, char *text)\r\n{\r\n \tQ_strncpyz(clipboard_buffer, text, SYS_CLIPBOARD_SIZE);\r\n}\r\n\r\n/*dynamic library stubs*/\r\ndllhandle_t *Sys_LoadLibrary(const char *name, dllfunction_t *funcs)\r\n{\r\n\tCon_Printf(\"Sys_LoadLibrary: %s\\n\", name);\r\n\treturn NULL;\r\n}\r\nvoid Sys_CloseLibrary(dllhandle_t *lib)\r\n{\r\n}\r\nvoid *Sys_GetAddressForName(dllhandle_t *module, const char *exportname)\r\n{\r\n\treturn NULL;\t//unsupported\r\n}\r\nchar *Sys_GetNameForAddress(dllhandle_t *module, void *address)\r\n{\r\n\treturn NULL;\t//unsupported (on most platforms, actually)\r\n}\r\n\r\nvoid main( int argc, char **argv)\r\n{\r\n\tfloat time, lasttime;\r\n\tquakeparms_t parms;\r\n\r\n\tmemset(&parms, 0, sizeof(parms));\r\n\r\n\tparms.argc = argc;\r\n\tparms.argv = argv;\r\n#ifdef CONFIG_MANIFEST_TEXT\r\n\tparms.manifest = CONFIG_MANIFEST_TEXT;\r\n#endif\r\n\r\n\tCOM_InitArgv(parms.argc, parms.argv);\r\n\tTL_InitLanguages(parms.basedir);\r\n\tHost_Init(&parms);\r\n\r\n\t//main loop\r\n\tlasttime = Sys_DoubleTime();\r\n\r\n\twhile (1)\r\n\t{\r\n\t\ttime = Sys_DoubleTime();\r\n\t\tHost_Frame(time - lasttime);\r\n\t\tlasttime = time;\r\n\t}\r\n\r\n}\r\n\r\n/*input stubs\r\nin_generic.c should make these kinda useless, but sometimes you need more than the following three functions:\r\nvoid IN_JoystickAxisEvent(unsigned int devid, int axis, float value);\r\nvoid IN_KeyEvent(unsigned int devid, int down, int keycode, int unicode);\r\nvoid IN_MouseMove(unsigned int devid, int abs, float x, float y, float z, float size);\r\nthat said, if they're enough, then just call those in Sys_SendKeyEvents. You can also call them from other threads just fine.\r\ntheir devid values should be assigned only when a button is first pressed, or something, so controllers get assigned to seats in the order that they're pressed. so player 1 always hits a button first to ensure that they are player 1.\r\nor just hardcode those based upon usbport numbers, but that's unreliable with usb hubs.\r\n*/\r\n\r\nvoid INS_Shutdown (void)\r\n{\r\n}\r\nvoid INS_ReInit (void)\r\n{\r\n}\r\nvoid INS_Move(void)\r\n{\r\n\t//accululates system-specific inputs on a per-seat basis.\r\n}\r\nvoid INS_Init (void)\r\n{\r\n}\r\nvoid INS_Accumulate(void)\t//input polling\r\n{\r\n}\r\nvoid INS_Commands (void)\t//used to Cbuf_AddText joystick button events in windows.\r\n{\r\n}\r\nvoid INS_EnumerateDevices(void *ctx, void(*callback)(void *ctx, const char *type, const char *devicename, unsigned int *qdevid))\r\n{\r\n#if 0\r\n\tunsigned int i;\r\n\tfor (i = 0; i < MAX_JOYSTICKS; i++)\r\n\t\tif (sdljoy[i].controller)\r\n\t\t\tcallback(ctx, \"joy\", sdljoy[i].devname, &sdljoy[i].qdevid);\r\n#endif\r\n}\r\n\r\nvoid INS_SetupControllerAudioDevices (void) // Not used\r\n{\r\n}\r\n\r\nvoid INS_UpdateGrabs (void) // Not used\r\n{\r\n}\r\n\r\n"
  },
  {
    "path": "engine/client/teamplay.c",
    "content": "/*\n\treplaced by zqtp.c (read: teamplay.c ported from zquake instead, with later fuhquake+ezquake additions, the ravages of time can be merciless)\n*/\n\n"
  },
  {
    "path": "engine/client/textedit.c",
    "content": "//DMW\n\n/*\nF1 will return to progs.src\nF2 will try to open a file with the name of which is on that line. (excluding comments/tabs). Needs conditions.\nF3 will give a prompt for typing in a value name to see the value.\nF4 will save\nF5 will run (unbreak).\nF6 will list the stack.\nF7 will compile.\nF8 will move execution\nF9 will set a break point.\nF10 will step over.\nF11 will step into.\n*/\n\n#include \"quakedef.h\"\n#ifdef TEXTEDITOR\n#include \"pr_common.h\"\n\n#include \"shader.h\"\n\n//#if defined(ANDROID) || defined(SERVERONLY)\n#define debugger_default \"0\"\n//#else\n//#define debugger_default \"1\"\n//#endif\n\nstatic cvar_t editstripcr = CVARD(\"edit_stripcr\", \"1\", \"remove \\\\r from eols (on load)\");\nstatic cvar_t editaddcr = CVARD(\"edit_addcr\", \"\", \"make sure that each line ends with a \\\\r (on save). Empty will be assumed to be 1 on windows and 0 otherwise.\");\n//static cvar_t edittabspacing = CVARD(\"edit_tabsize\", \"4\", \"How wide tab alignment is\");\ncvar_t pr_debugger = CVARAFD(\"pr_debugger\", debugger_default, \"debugger\", CVAR_SAVE, \"When enabled, QC errors and debug events will enable step-by-step tracing.\");\nextern cvar_t pr_sourcedir;\n\nstatic pubprogfuncs_t *editprogfuncs;\n\nqboolean editoractive;\t\t\t//(export)\nconsole_t *editormodal;\t\t\t//doesn't return. (export)\nint editorstep;\t\t\t\t\t//execution resumption type\nstatic qboolean stepasm;\t\t//debugging with (generated) asm.\nstatic int executionlinenum;\t//debugging execution line.\n\n#if defined(CSQC_DAT) && !defined(SERVERONLY)\nextern world_t csqc_world;\n#endif\n#if defined(MENU_DAT) && !defined(SERVERONLY)\nextern world_t menu_world;\n#endif\n\n\n\nvoid Editor_Draw(void)\n{\n\tR2D_EditorBackground();\n\tKey_Dest_Add(kdm_cwindows);\n}\nqboolean Editor_Key(int key, int unicode)\n{\n\tif (editormodal)\n\t{\n\t\tif (key >= K_F1 && key <= K_F12)\n\t\t\treturn editormodal->redirect(editormodal, unicode, key);\n\t}\n\treturn false;\n}\n\nvoid Editor_Demodalize(void)\n{\n\tif (editormodal && editormodal->highlightline)\n\t{\n\t\teditormodal->highlightline->flags &= ~CONL_EXECUTION;\n\t\teditormodal->highlightline = NULL;\n\t}\n\teditormodal = NULL;\n}\n\n\nint Con_Editor_GetLine(console_t *con, conline_t *line)\n{\n\tint linenum = 1;\n\tconline_t *l;\n\tfor (l = con->oldest; l; l = l->newer, linenum++)\n\t{\n\t\tif (l == line)\n\t\t\treturn linenum;\n\t}\n\treturn 0;\n}\nconline_t *Con_Editor_FindLine(console_t *con, int line)\n{\n\tconline_t *l;\n\tfor (l = con->oldest; l; l = l->newer)\n\t{\n\t\tif (--line == 0)\n\t\t\treturn l;\n\t}\n\treturn NULL;\n}\n\nint Con_Editor_Evaluate(console_t *con, const char *evalstring)\n{\n\tchar *eq, *term;\n\n\teq = strchr(evalstring, '=');\n\tif (eq)\n\t{\n\t\tterm = strchr(eq, ';');\n\t\tif (!term)\n\t\t\tterm = strchr(eq, '\\n');\n\t\tif (!term)\n\t\t\tterm = strchr(eq, '\\r');\n\t\tif (term)\n\t\t{\n\t\t\t*term = '\\0';\n\t\t\teq = NULL;\n\t\t}\n\t\telse\n\t\t\t*eq = '\\0';\n\t}\n\tCon_Footerf(con, false, \"%s\", evalstring);\n\tif (eq)\n\t{\n\t\t*eq = '=';\n\t\tCon_Footerf(con, true, \" = %s\", editprogfuncs->EvaluateDebugString(editprogfuncs, evalstring));\n\t}\n\telse\n\t\tCon_Footerf(con, true, \" == %s\", editprogfuncs->EvaluateDebugString(editprogfuncs, evalstring));\n\tcon->linebuffered = NULL;\n\treturn true;\n}\n\n//creates a new line following an existing line by splitting the previous\nconline_t *Con_EditorSplit(console_t *con, conline_t *orig, int offset)\n{\n\tconline_t *l;\n\tl = BZ_Malloc(sizeof(*l)+(orig->length-offset)*sizeof(conchar_t));\n\t*l = *orig;\n\tl->length = l->maxlength = orig->length-con->useroffset;\n\tmemcpy(l+1, (conchar_t*)(orig+1)+offset, l->length*sizeof(conchar_t));\n\torig->length = offset;\t//truncate the old line\n\tl->older = orig;\n\tl->flags &= ~(CONL_EXECUTION|CONL_BREAKPOINT);\n\torig->newer = l;\n\tif (con->current == orig)\n\t\tcon->current = l;\n\telse\n\t\tl->newer->older = l;\n\tif (con->display == orig)\n\t\tcon->display = l;\n\tcon->linecount++;\n\n\tcon->selendline = con->selstartline = NULL;\n\treturn l;\n}\nconline_t *Con_EditorMerge(console_t *con, conline_t *first, conline_t *second)\n{\n\tconline_t *l;\n\tl = Con_ResizeLineBuffer(con, first, first->length+second->length);\n\n\t//unlink the second line\n\tl->newer = second->newer;\n\tif (l->newer)\n\t\tl->newer->older = l;\n\n\t//heal references to the second to point to the first\n\tif (con->selstartline == second)\n\t{\n\t\tcon->selstartline = l;\n\t\tcon->selstartoffset += l->length;\n\t}\n\tif (con->selendline == second)\n\t{\n\t\tcon->selendline = l;\n\t\tcon->selendoffset += l->length;\n\t}\n\tif (con->display == second)\n\t\tcon->display = l;\n\tif (con->oldest == second)\n\t\tcon->oldest = l;\n\tif (con->current == second)\n\t\tcon->current = l;\n\tif (con->userline == second)\n\t{\n\t\tcon->userline = l;\n\t\tcon->useroffset += l->length;\n\t}\n\tif (con->highlightline == second)\n\t{\n\t\tcon->highlightline = l;\n\t\tcon->highlightline->flags |= CONL_EXECUTION;\n\t}\n\t\n\t//copy over the chars\n\tmemcpy((conchar_t*)(l+1)+l->length, (conchar_t*)(second+1), second->length*sizeof(conchar_t));\n\tl->length += second->length;\n\n\t//and that line is now dead.\n\tcon->linecount--;\n\tBZ_Free(second);\n\n\treturn l;\n}\nstatic void Con_Editor_DeleteSelection(console_t *con)\n{\n\tconline_t *n;\n\tcon->flags &= ~CONF_KEEPSELECTION;\n\tif (con->selstartline)\n\t{\n\t\tif (con->selstartline == con->selendline)\n\t\t{\n\t\t\tmemmove((conchar_t*)(con->selstartline+1)+con->selstartoffset, (conchar_t*)(con->selendline+1)+con->selendoffset, sizeof(conchar_t)*(con->selendline->length - con->selendoffset));\n\t\t\tcon->selendline->length = con->selstartoffset + (con->selendline->length - con->selendoffset);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tcon->selstartline->length = con->selstartoffset;\n\t\t\tfor(n = con->selstartline;;)\n\t\t\t{\n\t\t\t\tn = n->newer;\n\t\t\t\tif (!n)\n\t\t\t\t\tbreak;\t//shouldn't happen\n\t\t\t\tif (n == con->selendline)\n\t\t\t\t{\n\t\t\t\t\t//this is the last line, we need to keep the end of the string but not the start.\n\t\t\t\t\tmemmove(n+1, (conchar_t*)(n+1)+con->selendoffset, sizeof(conchar_t)*(n->length - con->selendoffset));\n\t\t\t\t\tn->length = n->length - con->selendoffset;\n\t\t\t\t\tn = Con_EditorMerge(con, con->selstartline, n);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\t//truncate and merge\n\t\t\t\tn->length = 0;\n\t\t\t\tn = Con_EditorMerge(con, con->selstartline, n);\n\t\t\t}\n\t\t}\n\t\tcon->userline = con->selstartline;\n\t\tcon->useroffset = con->selstartoffset;\n\t}\n}\nstatic void Con_Editor_DoPaste(void *ctx, const char *utf8)\n{\n\tconsole_t *con = ctx;\n\tif (utf8)\n\t{\n\t\tconchar_t buffer[8192], *end;\n\t\tconst char *s;\n\t\tchar *nl;\n\t\tif (*utf8 && (con->flags & CONF_KEEPSELECTION))\n\t\t\tCon_Editor_DeleteSelection(con);\n\t\tfor(s = utf8; ; )\n\t\t{\n\t\t\tnl = strchr(s, '\\n');\n\t\t\tif (nl)\n\t\t\t\t*nl = 0;\n\t\t\tend = COM_ParseFunString(CON_WHITEMASK, s, buffer, sizeof(buffer), PFS_FORCEUTF8);\n\t\t\tif (Con_InsertConChars(con, con->userline, con->useroffset, buffer, end-buffer))\n\t\t\t\tcon->useroffset += end-buffer;\n\n\t\t\tif (nl)\n\t\t\t{\n\t\t\t\tcon->userline = Con_EditorSplit(con, con->userline, con->useroffset);\n\t\t\t\tcon->useroffset = 0;\n\t\t\t\ts = nl+1;\n\t\t\t}\n\t\t\telse\n\t\t\t\tbreak;\n\t\t}\n\t}\n}\nstatic void Con_Editor_Paste(console_t *con)\n{\n\tSys_Clipboard_PasteText(CBT_CLIPBOARD, Con_Editor_DoPaste, con);\n}\nstatic void Con_Editor_Save(console_t *con)\n{\n\tvfsfile_t *file;\n\tconline_t *line;\n\n\tFS_CreatePath(con->name, FS_GAMEONLY);\n\tfile = FS_OpenVFS(con->name, \"wb\", FS_GAMEONLY);\n\tif (file)\n\t{\n\t\tfor (line = con->oldest; line; line = line->newer)\n\t\t{\n\t\t\tconchar_t *cl = (conchar_t*)(line+1);\n\t\t\tconchar_t *el = cl + line->length;\n\t\t\tchar buffer[65536];\n\t\t\tchar *bend = COM_DeFunString(cl, el, buffer, sizeof(buffer)-2, true, !!(con->parseflags & PFS_FORCEUTF8));\n\t\t\tif (editaddcr.ival \n#ifdef _WIN32\n\t\t\t\t|| !*editaddcr.string\t//windows editors have a history of bugging out so make sure we default to whatever b0rkedness they mandate.\n#endif\n\t\t\t\t)\n\t\t\t\t*bend++ = '\\r';\n\t\t\t*bend++ = '\\n';\n\t\t\tVFS_WRITE(file, buffer, bend-buffer);\n\t\t}\n\t\tVFS_CLOSE(file);\n\t\n\n\t\tQ_snprintfz(con->title, sizeof(con->title), \"SAVED: %s\", con->name);\n\n\t\tif (!Q_strncasecmp(con->name, \"scripts/\", 8))\n\t\t\tShader_NeedReload(true);\n\t\telse if (!Q_strncasecmp(con->name, \"particles/\", 10))\n\t\t{\n\t\t\textern cvar_t r_particledesc;\n\t\t\tCvar_ForceCallback(&r_particledesc);\n\t\t}\n\t}\n}\nqboolean\tCon_Editor_MouseOver(struct console_s *con, char **out_tiptext, shader_t **out_shader)\n{\n\tchar *mouseover = Con_CopyConsole(con, true, false, false);\n\n\tif (mouseover)\n\t{\n\t\tconst char *ext = COM_GetFileExtension(con->name, NULL);\n\t\tif (!Q_strcasecmp(ext, \".cfg\"))\n\t\t{\n\t\t\t*out_tiptext = NULL;\n\t\t\t//look for a command...\n\t\t\tif (*out_tiptext == NULL && Cmd_Exists(mouseover))\n\t\t\t{\n\t\t\t\tconst char *desc = Cmd_Describe(mouseover);\n\t\t\t\t*out_tiptext = va(\"%s\\n\\n%s\", mouseover, desc?desc:\"<NO DESCRIPTION>\");\n\t\t\t}\n\t\t\tif (!*out_tiptext)\t//check if we can get the body of an alias...\n\t\t\t{\n\t\t\t\tconst char *desc = Cmd_AliasExist(mouseover, RESTRICT_LOCAL);\n\t\t\t\tif (desc)\n\t\t\t\t\t*out_tiptext = va(\"alias \\\"%s\\\"\\n\\n%s\", mouseover, *desc?desc:\"<NO TEXT>\");\n\t\t\t}\n\t\t\tif (!*out_tiptext)\t//now look to see if its a cvar...\n\t\t\t{\n\t\t\t\tcvar_t *cv = Cvar_FindVar(mouseover);\n\t\t\t\tif (cv)\n\t\t\t\t{\n\t\t\t\t\tconst char *cmd = \"\";\n\t\t\t\t\tchar fl[4];\n\t\t\t\t\tint i = 0;\n\t\t\t\t\tif ((cv->flags & CVAR_USERINFO) || (cv->flags & CVAR_SERVERINFO))\n\t\t\t\t\t{\n\t\t\t\t\t\tcmd = \"setfl \";\n\t\t\t\t\t\tfl[i++] = ' ';\n\t\t\t\t\t\tif (cv->flags & CVAR_SERVERINFO)\n\t\t\t\t\t\t\tfl[i++] = 's';\n\t\t\t\t\t\tif (cv->flags & CVAR_USERINFO)\n\t\t\t\t\t\t\tfl[i++] = 'u';\n\t\t\t\t\t\tif (cv->flags & CVAR_ARCHIVE)\n\t\t\t\t\t\t\tfl[i++] = 'a';\n\t\t\t\t\t}\n\t\t\t\t\telse if (cv->flags & CVAR_ARCHIVE)\n\t\t\t\t\t\tcmd = \"seta \";\n\t\t\t\t\telse// if (cv->flags & CVAR_POINTER)\n\t\t\t\t\t\tcmd = \"set \";\n\t\t\t\t\tfl[i] = 0;\n\t\t\t\t\t*out_tiptext = va(\"%s%s \\\"%s\\\"%s\\nDefault: \\\"%s\\\"\\n\\n%s\", cmd, cv->name, fl, cv->string, cv->defaultstr, cv->description?cv->description:\"<NO DESCRIPTION>\");\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse if (!Q_strcasecmp(ext, \".qc\") || !Q_strcasecmp(ext, \".hc\")/*hexenc...*/ || !Q_strcasecmp(ext, \".src\") || !Q_strcasecmp(ext, \".c\")/*weird people. you know who you are...*/)\n\t\t{\t//qc files\n\t\t\tif (editprogfuncs && editprogfuncs->EvaluateDebugString)\n\t\t\t\t*out_tiptext = editprogfuncs->EvaluateDebugString(editprogfuncs, mouseover);\n\t\t\telse\n\t\t\t{\n#ifndef SERVERONLY\n#ifdef CSQC_DAT\n\t\t\t\tif (csqc_world.progs && csqc_world.progs->EvaluateDebugString && !*out_tiptext)\n\t\t\t\t\t*out_tiptext = csqc_world.progs->EvaluateDebugString(csqc_world.progs, mouseover);\n#endif\n#ifdef MENU_DAT\n\t\t\t\tif (menu_world.progs && menu_world.progs->EvaluateDebugString && !*out_tiptext)\n\t\t\t\t\t*out_tiptext = menu_world.progs->EvaluateDebugString(menu_world.progs, mouseover);\n#endif\n#endif\n#ifndef CLIENTONLY\n\t\t\t\tif (sv.world.progs && sv.world.progs->EvaluateDebugString && !*out_tiptext)\n\t\t\t\t\t*out_tiptext = sv.world.progs->EvaluateDebugString(sv.world.progs, mouseover);\n#endif\n\t\t\t}\n\t\t}\n\t\tZ_Free(mouseover);\n\t}\n\n\treturn true;\n}\nvoid Con_EditorMoveCursor(console_t *con, conline_t *newline, int newoffset, qboolean shiftheld, qboolean moveprior)\n{\n\tif (!shiftheld)\n\t\tcon->flags &= ~CONF_KEEPSELECTION;\n\telse\n\t{\n\t\tif (!(con->flags & CONF_KEEPSELECTION) || (con->selendline == con->selstartline && con->selendoffset == con->selstartoffset))\n\t\t{\n\t\t\tcon->flags |= CONF_KEEPSELECTION;\n\t\t\tif (moveprior)\n\t\t\t{\n\t\t\t\tcon->selstartline = newline;\n\t\t\t\tcon->selstartoffset = newoffset;\n\t\t\t\tcon->selendline = con->userline;\n\t\t\t\tcon->selendoffset = con->useroffset;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tcon->selstartline = con->userline;\n\t\t\t\tcon->selstartoffset = con->useroffset;\n\t\t\t\tcon->selendline = newline;\n\t\t\t\tcon->selendoffset = newoffset;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (con->selendline == con->userline && con->selendoffset == con->useroffset)\n\t\t\t{\n\t\t\t\tif (con->selstartline != con->selendline && con->selstartline == newline && moveprior)\n\t\t\t\t{\t//inverted\n\t\t\t\t\tcon->selendline = con->selstartline;\n\t\t\t\t\tcon->selendoffset = con->selstartoffset;\n\n\t\t\t\t\tcon->selstartline = newline;\n\t\t\t\t\tcon->selstartoffset = newoffset;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tcon->selendline = newline;\n\t\t\t\t\tcon->selendoffset = newoffset;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (con->selstartline == con->userline && con->selstartoffset == con->useroffset)\n\t\t\t{\n\t\t\t\tif (con->selstartline == con->selendline && con->selstartline != newline && !moveprior)\n\t\t\t\t{\t//inverted\n\t\t\t\t\tcon->selstartline = con->selendline;\n\t\t\t\t\tcon->selstartoffset = con->selendoffset;\n\n\t\t\t\t\tcon->selendline = newline;\n\t\t\t\t\tcon->selendoffset = newoffset;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tcon->selstartline = newline;\n\t\t\t\t\tcon->selstartoffset = newoffset;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tif (con->userline == con->display && !moveprior)\n\t\tcon->display = newline;\n\n\tcon->userline = newline;\n\tcon->useroffset = newoffset;\n}\nstatic conchar_t *Con_Editor_Equals(conchar_t *start, conchar_t *end, const char *match)\n{\n\tconchar_t *n;\n\tunsigned int ccode, flags;\n\n\tfor (; start < end; start = n)\n\t{\n\t\tn = Font_Decode(start, &flags, &ccode);\n\t\tif (*match)\n\t\t{\n\t\t\tif (ccode != *(unsigned char*)match++)\n\t\t\t\treturn NULL;\n\t\t}\n\t\telse if (ccode == ' ' || ccode == '\\t')\n\t\t\tbreak;\t//found whitespace after the token, its complete.\n\t\telse\n\t\t\treturn NULL;\n\t}\n\tif (*match)\n\t\treturn NULL;\t//truncated\n\n\t//and skip any trailing whitespace, because we can.\n\tfor (; start < end; start = n)\n\t{\n\t\tn = Font_Decode(start, &flags, &ccode);\n\t\tif (ccode == ' ' || ccode == '\\t')\n\t\t\tcontinue;\n\t\telse\n\t\t\tbreak;\n\t}\n\treturn start;\n}\nstatic conchar_t *Con_Editor_SkipWhite(conchar_t *start, conchar_t *end)\n{\n\tconchar_t *n;\n\tunsigned int ccode, flags;\n\tfor (; start < end; start = n)\n\t{\n\t\tn = Font_Decode(start, &flags, &ccode);\n\t\tif (ccode == ' ' || ccode == '\\t')\n\t\t\tcontinue;\n\t\telse\n\t\t\tbreak;\n\t}\n\treturn start;\n}\nstatic void Con_Editor_LineChanged_Shader(conline_t *line)\n{\n\tstatic const char *maplines[] = {\"map\", \"clampmap\"};\n\tsize_t i;\n\tconchar_t *start = (conchar_t*)(line+1), *end = start + line->length, *n;\n\n\tstart = Con_Editor_SkipWhite(start, end);\n\n\tline->flags &= ~(CONL_BREAKPOINT|CONL_EXECUTION);\n\n\tfor (i = 0; i < countof(maplines); i++)\n\t{\n\t\tn = Con_Editor_Equals(start, end, maplines[i]);\n\t\tif (n)\n\t\t{\n\t\t\tchar mapname[8192];\n\t\t\tchar fname[MAX_QPATH];\n\t\t\tflocation_t loc;\n\t\t\tunsigned int flags;\n\t\t\timage_t img;\n\n\t\t\tmemset(&img, 0, sizeof(img));\n\t\t\timg.ident = mapname;\n\t\t\tCOM_DeFunString(n, end, mapname, sizeof(mapname), true, true);\n\t\t\twhile(*img.ident == '$')\n\t\t\t{\n\t\t\t\tif (!Q_strncasecmp(img.ident, \"$lightmap\", 9))\n\t\t\t\t\treturn;\t//lightmaps don't need to load from disk\n\t\t\t\tif (!Q_strncasecmp(img.ident, \"$rt:\", 4))\n\t\t\t\t\treturn;\t//render targets never come from disk\n\t\t\t\tif (!Q_strncasecmp(img.ident, \"$clamp:\", 7) || !Q_strncasecmp(img.ident, \"$3d:\", 4) || !Q_strncasecmp(img.ident, \"$cube:\", 6) || !Q_strncasecmp(img.ident, \"$nearest:\", 9) || !Q_strncasecmp(img.ident, \"$linear:\", 8))\n\t\t\t\t\timg.ident = strchr(img.ident, ':')+1;\n\t\t\t\telse\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (!Image_LocateHighResTexture(&img, &loc, fname, sizeof(fname), &flags))\n\t\t\t\tline->flags |= CONL_BREAKPOINT;\n\t\t\treturn;\n\t\t}\n\t}\n}\nstatic void Con_Editor_LineChanged(console_t *con, conline_t *line)\n{\n\tif (!Q_strncasecmp(con->name, \"scripts/\", 8))\n\t\tCon_Editor_LineChanged_Shader(line);\n}\nqboolean Con_Editor_Key(console_t *con, unsigned int unicode, int key)\n{\n\tqboolean altdown = keydown[K_LALT] || keydown[K_RALT];\n\tqboolean ctrldown = keydown[K_LCTRL] || keydown[K_RCTRL];\n\tqboolean shiftdown = keydown[K_LSHIFT] || keydown[K_RSHIFT];\n\tif (key == K_MOUSE1)\n\t{\n\t\tcon->flags &= ~CONF_KEEPSELECTION;\n\t\tcon->buttonsdown = CB_SELECT;\n\t\treturn true;\n\t}\n\tif (key == K_MOUSE2)\n\t{\n\t\tcon->flags &= ~CONF_KEEPSELECTION;\n\t\tcon->buttonsdown = CB_SCROLL;\n\t\treturn true;\n\t}\n\tif (!con->userline)\n\t\treturn false;\n\tif (con->linebuffered)\n\t\treturn false;\n\tswitch(key)\n\t{\n\tcase K_BACKSPACE:\n\t\tif (con->flags & CONF_KEEPSELECTION)\n\t\t\tCon_Editor_DeleteSelection(con);\n\t\telse if (con->useroffset == 0)\n\t\t{\n\t\t\tif (con->userline->older)\n\t\t\t\tCon_EditorMerge(con, con->userline->older, con->userline);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tcon->useroffset--;\n\t\t\tmemmove((conchar_t*)(con->userline+1)+con->useroffset, (conchar_t*)(con->userline+1)+con->useroffset+1, (con->userline->length - con->useroffset)*sizeof(conchar_t));\n\t\t\tcon->userline->length -= 1;\n\t\t\tCon_Editor_LineChanged(con, con->userline);\n\t\t}\n\t\treturn true;\n\tcase K_DEL:\n\t\tif (con->flags & CONF_KEEPSELECTION)\n\t\t\tCon_Editor_DeleteSelection(con);\n\t\telse if (con->useroffset == con->userline->length)\n\t\t{\n\t\t\tif (con->userline->newer)\n\t\t\t\tCon_EditorMerge(con, con->userline, con->userline->newer);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tmemmove((conchar_t*)(con->userline+1)+con->useroffset, (conchar_t*)(con->userline+1)+con->useroffset+1, (con->userline->length - con->useroffset)*sizeof(conchar_t));\n\t\t\tcon->userline->length -= 1;\n\t\t\tCon_Editor_LineChanged(con, con->userline);\n\t\t}\n\t\tbreak;\n\tcase K_ENTER:\t/*split the line into two, selecting the new line*/\n\t\tif (con->flags & CONF_KEEPSELECTION)\n\t\t\tCon_Editor_DeleteSelection(con);\n\t\tcon->userline = Con_EditorSplit(con, con->userline, con->useroffset);\n\t\tcon->useroffset = 0;\n\t\tbreak;\n\tcase K_HOME:\n\t\tif (ctrldown)\n\t\t\tcon->display = con->oldest;\n\t\telse\n\t\t\tCon_EditorMoveCursor(con, con->userline, 0, shiftdown, true);\n\t\treturn true;\n\tcase K_END:\n\t\tif (ctrldown)\n\t\t\tcon->display = con->current;\n\t\telse\n\t\t\tCon_EditorMoveCursor(con, con->userline, con->userline->length, shiftdown, false);\n\t\treturn true;\n\tcase K_UPARROW:\n\tcase K_KP_UPARROW:\n\tcase K_GP_DPAD_UP:\n\t\tif (con->userline->older)\n\t\t{\n\t\t\tif (con->useroffset > con->userline->older->length)\n\t\t\t\tCon_EditorMoveCursor(con, con->userline->older, con->userline->older->length, shiftdown, true);\n\t\t\telse\n\t\t\t\tCon_EditorMoveCursor(con, con->userline->older, con->useroffset, shiftdown, true);\n\t\t}\n\t\treturn true;\n\tcase K_DOWNARROW:\n\tcase K_KP_DOWNARROW:\n\tcase K_GP_DPAD_DOWN:\n\t\tif (con->userline->newer)\n\t\t{\n\t\t\tif (con->useroffset > con->userline->newer->length)\n\t\t\t\tCon_EditorMoveCursor(con, con->userline->newer, con->userline->newer->length, shiftdown, false);\n\t\t\telse\n\t\t\t\tCon_EditorMoveCursor(con, con->userline->newer, con->useroffset, shiftdown, false);\n\t\t}\n\t\treturn true;\n\tcase K_LEFTARROW:\n\tcase K_KP_LEFTARROW:\n\tcase K_GP_DPAD_LEFT:\n\t\tif (con->useroffset == 0)\n\t\t{\n\t\t\tif (con->userline->older)\n\t\t\t\tCon_EditorMoveCursor(con, con->userline->older, con->userline->older->length, shiftdown, true);\n\t\t}\n\t\telse\n\t\t\tCon_EditorMoveCursor(con, con->userline, con->useroffset-1, shiftdown, true);\n\t\treturn true;\n\tcase K_RIGHTARROW:\n\tcase K_KP_RIGHTARROW:\n\tcase K_GP_DPAD_RIGHT:\n\t\tif (con->useroffset == con->userline->length)\n\t\t{\n\t\t\tif (con->userline->newer)\n\t\t\t\tCon_EditorMoveCursor(con, con->userline->newer, 0, shiftdown, false);\n\t\t}\n\t\telse\n\t\t\tCon_EditorMoveCursor(con, con->userline, con->useroffset+1, shiftdown, false);\n\t\treturn true;\n\tcase K_INS:\n\t\tif (shiftdown)\n\t\t{\n\t\t\tif (con->flags & CONF_KEEPSELECTION)\n\t\t\t\tCon_Editor_DeleteSelection(con);\n\t\t\tCon_Editor_Paste(con);\n\t\t\tbreak;\n\t\t}\n\t\tif (ctrldown && (con->flags & CONF_KEEPSELECTION))\n\t\t{\n\t\t\tchar *buffer = Con_CopyConsole(con, true, false, true);\t//don't keep markup if we're copying to the clipboard\n\t\t\tif (buffer)\n\t\t\t{\n\t\t\t\tSys_SaveClipboard(CBT_CLIPBOARD, buffer);\n\t\t\t\tZ_Free(buffer);\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t\treturn false;\n\tcase K_LSHIFT:\n\tcase K_RSHIFT:\n\tcase K_LCTRL:\n\tcase K_RCTRL:\n\tcase K_LALT:\n\tcase K_RALT:\n\t\treturn true;\t//these non-printable chars generally should not be allowed to trigger bindings.\n\n\tcase K_F1:\n\t\tCon_Printf(\n\t\t\t\"Editor help:\\n\"\n\t\t\t\"F1: Show help\\n\"\n\t\t\t\"F2: Open file named on cursor line\\n\"\n\t\t\t\"F3: Toggle expression evaluator\\n\"\n\t\t\t\"CTRL+S: Save file\\n\"\n\t\t\t\"F5: Stop tracing (continue running)\\n\"\n\t\t\t\"F6: Print stack trace\\n\"\n\t\t\t\"F8: Change current point of execution\\n\"\n\t\t\t\"F9: Set breakpoint\\n\"\n\t\t\t\"ALT+F10: save+recompile\\n\"\n\t\t\t\"F10: Step Over (skipping children)\\n\"\n\t\t\t\"SHIFT+F11: Step Out\\n\"\n\t\t\t\"F11: Step Into\\n\"\n//\t\t\t\"F12: Go to definition\\n\"\n\t\t\t);\n\t\tCbuf_AddText(\"toggleconsole\\n\", RESTRICT_LOCAL);\n\t\treturn true;\n\tcase K_F2:\n\t\t/*{\n\t\t\tchar file[1024];\n\t\t\tchar *s;\n\t\t\tQ_strncpyz(file, cursorblock->data, sizeof(file));\n\t\t\ts = file;\n\t\t\twhile (*s)\n\t\t\t{\n\t\t\t\tif ((*s == '/' && s[1] == '/') || (*s == '\\t'))\n\t\t\t\t{\n\t\t\t\t\t*s = '\\0';\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\ts++;\n\t\t\t}\n\t\t\tif (*file)\n\t\t\t\tEditorOpenFile(file, false);\n\t\t}*/\n\t\treturn true;\n\tcase K_F3:\n\t\tif (editprogfuncs)\n\t\t{\n\t\t\tcon->linebuffered = Con_Editor_Evaluate;\n\t\t}\n\t\treturn true;\n\tcase K_F5:\t//stop debugging\n\t\tif (editormodal)\n\t\t{\n\t\t\tEditor_Demodalize();\n\t\t\teditorstep = DEBUG_TRACE_OFF;\n\t\t}\n\t\treturn true;\n\tcase K_F6:\n\t\tif (editprogfuncs)\n\t\t{\n\t\t\tPR_StackTrace(editprogfuncs, 2);\n\t\t\tKey_Dest_Add(kdm_console);\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\tcase K_F8:\t//move execution point to here - I hope you move to the same function!\n\t\tif (editprogfuncs && con->userline)\n\t\t{\n\t\t\tint l = Con_Editor_GetLine(con, con->userline);\n\t\t\tif (l)\n\t\t\t{\n\t\t\t\tconline_t *n = Con_Editor_FindLine(con, l);\n\t\t\t\tif (n)\n\t\t\t\t{\n\t\t\t\t\tif (con->highlightline)\n\t\t\t\t\t{\n\t\t\t\t\t\tcon->highlightline->flags &= ~CONL_EXECUTION;\n\t\t\t\t\t\tcon->highlightline = NULL;\n\t\t\t\t\t}\n\n\t\t\t\t\texecutionlinenum = l;\n\t\t\t\t\tcon->highlightline = n;\n\t\t\t\t\tn->flags |= CONL_EXECUTION;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn true;\n\tcase K_F9: /*set breakpoint*/\n\t\t{\n\t\t\tconline_t *cl;\n\t\t\tchar *fname = con->name;\n\t\t\tint mode;\n\t\t\tint line;\n\t\t\tif (!strncmp(fname, pr_sourcedir.string, strlen(pr_sourcedir.string)) && fname[strlen(pr_sourcedir.string)] == '/')\n\t\t\t\tfname += strlen(pr_sourcedir.string)+1;\n\t\t\telse if (!strncmp(fname, \"src/\", 4))\n\t\t\t\tfname += 4;\n\t\t\telse if (!strncmp(fname, \"source/\", 7))\n\t\t\t\tfname += 7;\n\t\t\telse if (!strncmp(fname, \"qcsrc/\", 6))\n\t\t\t\tfname += 6;\n\t\t\telse if (!strncmp(fname, \"progs/\", 6))\n\t\t\t\tfname += 6;\n\n\n\t\t\tcl = con->userline;\n\t\t\tline = Con_Editor_GetLine(con, cl);\n\n\t\t\tif (cl->flags & CONL_BREAKPOINT)\n\t\t\t\tmode = 0;\n\t\t\telse\n\t\t\t\tmode = 1;\n\n#ifndef SERVERONLY\n#ifdef CSQC_DAT\n\t\t\tif (csqc_world.progs && csqc_world.progs->ToggleBreak)\n\t\t\t\tcsqc_world.progs->ToggleBreak(csqc_world.progs, fname, line, mode);\n#endif\n#ifdef MENU_DAT\n\t\t\tif (menu_world.progs && menu_world.progs->ToggleBreak)\n\t\t\t\tmenu_world.progs->ToggleBreak(menu_world.progs, fname, line, mode);\n#endif\n#endif\n#ifndef CLIENTONLY\n\t\t\tif (sv.world.progs && sv.world.progs->ToggleBreak)\n\t\t\t\tsv.world.progs->ToggleBreak(sv.world.progs, fname, line, mode);\n#endif\n\n\t\t\tif (mode)\n\t\t\t\tcl->flags |= CONL_BREAKPOINT;\n\t\t\telse\n\t\t\t\tcl->flags &= ~CONL_BREAKPOINT;\n\t\t}\n\t\treturn true;\n\tcase K_F10:\n\t\tif (altdown)\n\t\t{\n\t\t\tCon_Editor_Save(con);\n\t\t\tif (!editprogfuncs)\n\t\t\t\tCbuf_AddText(\"compile; toggleconsole\\n\", RESTRICT_LOCAL);\n\t\t\treturn true;\n\t\t}\n\t\t//if (editormodal)\t//careful of autorepeat\n\t\t{\n\t\t\tEditor_Demodalize();\n\t\t\teditorstep = DEBUG_TRACE_OVER;\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\tcase K_F11: //single step\n\t\t//if (editormodal)\t//careful of auto-repeat\n\t\t{\n\t\t\tEditor_Demodalize();\n\t\t\teditorstep = shiftdown?DEBUG_TRACE_OUT:DEBUG_TRACE_INTO;\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\n\n\tdefault:\n\t\tif (ctrldown && key =='s')\n\t\t{\n\t\t\tCon_Editor_Save(con);\n\t\t\treturn true;\n\t\t}\n\t\tif (ctrldown && key =='v')\n\t\t{\n\t\t\tif (con->flags & CONF_KEEPSELECTION)\n\t\t\t\tCon_Editor_DeleteSelection(con);\n\t\t\tCon_Editor_Paste(con);\n\t\t\tbreak;\n\t\t}\n\t\tif (ctrldown && key =='c' && (con->flags & CONF_KEEPSELECTION))\n\t\t{\n\t\t\tchar *buffer = Con_CopyConsole(con, true, false, true);\t//don't keep markup if we're copying to the clipboard\n\t\t\tif (buffer)\n\t\t\t{\n\t\t\t\tSys_SaveClipboard(CBT_CLIPBOARD, buffer);\n\t\t\t\tZ_Free(buffer);\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t\tif (unicode)\n\t\t{\n\t\t\tconchar_t c[2];\n\t\t\tint l = 0;\n\t\t\tif (unicode > 0xffff)\n\t\t\t\tc[l++] = CON_LONGCHAR | (unicode>>16);\n\t\t\tc[l++] = CON_WHITEMASK | (unicode&0xffff);\n\t\t\tif (con->flags & CONF_KEEPSELECTION)\n\t\t\t\tCon_Editor_DeleteSelection(con);\n\t\t\tif (con->userline && Con_InsertConChars(con, con->userline, con->useroffset, c, l))\n\t\t\t{\n\t\t\t\tcon->useroffset += l;\n\t\t\t\tCon_Editor_LineChanged(con, con->userline);\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t\treturn false;\n\t}\n\tQ_snprintfz(con->title, sizeof(con->title), \"MODIFIED: %s\", con->name);\n\treturn true;\n}\nvoid Con_Editor_CloseCallback(void *ctx, promptbutton_t op)\n{\n\tconsole_t *con = ctx;\n\n\tif (con != con_curwindow)\t//ensure that it still exists (lame only-active-window check)\n\t\treturn;\n\t\n\tif (op == PROMPT_YES)\n\t\tCon_Editor_Save(con);\n\tif (op != PROMPT_CANCEL)\n\t\tCon_Destroy(con);\n}\nqboolean Con_Editor_Close(console_t *con, qboolean force)\n{\n\tif (!force)\n\t{\n\t\tif (!strncmp(con->title, \"MODIFIED: \", 10))\n\t\t{\n\t\t\tMenu_Prompt(Con_Editor_CloseCallback, con, va(localtext(\"Save changes?\\n%s\\n\"), con->name), \"Yes\", \"No\", \"Cancel\", true);\n\t\t\treturn false;\n\t\t}\n\t}\n\tif (con == editormodal)\n\t{\n\t\tEditor_Demodalize();\n\t\teditorstep = DEBUG_TRACE_OFF;\n\t}\n\treturn true;\n}\nvoid Con_Editor_GoToLine(console_t *con, int line)\n{\n\tcon->userline = con->oldest;\n\twhile (line --> 1)\n\t{\n\t\tif (!con->userline->newer)\n\t\t\tbreak;\n\t\tcon->userline = con->userline->newer;\n\t}\n\tcon->useroffset = 0;\n\tcon->display = con->userline;\n\n\t//FIXME: we REALLY need to support top-down style consoles.\n\tline = con->wnd_h / 8;\n\tline /= 2;\n\twhile (con->display->newer && line --> 0)\n\t{\n\t\tcon->display = con->display->newer;\n\t}\n}\nconsole_t *Con_TextEditor(const char *fname, const char *line, pubprogfuncs_t *disasmfuncs)\n{\n\tstatic int editorcascade;\n\tconsole_t *con;\n\tconline_t *l;\n\tcon = Con_FindConsole(fname);\n\tif (con)\n\t{\n\t\tCon_SetActive(con);\n\t\tif (line && con->redirect == Con_Editor_Key)\n\t\t\tCon_Editor_GoToLine(con, atoi(line));\n\n\t\tif (con->close != Con_Editor_Close)\n\t\t\tcon = NULL;\n\t}\n\telse\n\t{\n\t\tcon = Con_Create(fname, 0);\n\t\tif (con)\n\t\t{\n\t\t\tvfsfile_t *file;\n\t\t\tQ_snprintfz(con->title, sizeof(con->title), \"EDIT: %s\", con->name);\n\n\t\t\t/*make it a console window thing*/\n\t\t\tcon->flags |= CONF_ISWINDOW;\n\t\t\tcon->wnd_x = (editorcascade & 1)?vid.width/2:0;\n#ifdef ANDROID\n\t\t\tcon->wnd_y = 0;\n#else\n\t\t\tcon->wnd_y = (editorcascade & 2)?vid.height/2:0;\n#endif\n\t\t\tcon->wnd_w = vid.width/2;\n\t\t\tcon->wnd_h = vid.height/2;\n\t\t\teditorcascade++;\n\n\t\t\tcon->flags |= CONF_NOWRAP;\t//disable line wrapping. yay editors.\n\t\t\tcon->flags |= CONF_KEEPSELECTION;\t//only change the selection if we ask for it.\n\t\t\tcon->parseflags = PFS_FORCEUTF8;\n\t\t\tcon->userdata = NULL;\n\t\t\tcon->linebuffered = NULL;\n\t\t\tcon->redirect = Con_Editor_Key;\n\t\t\tcon->mouseover = Con_Editor_MouseOver;\n\t\t\tcon->close = Con_Editor_Close;\n\t\t\tcon->maxlines = 0x7fffffff;\t//line limit is effectively unbounded, for a 31-bit process.\n\t\t\t\n\t\t\tif (disasmfuncs)\n\t\t\t{\n\t\t\t\tint i;\n\t\t\t\tchar buffer[65536];\n\t\t\t\tint start = 1;\n\t\t\t\tint end = 0x7fffffff;\n\t\t\t\tchar *colon = strchr(con->name, ':');\n\t\t\t\tif (colon && *colon==':')\n\t\t\t\t\tstart = strtol(colon+1, &colon, 0);\n\t\t\t\tif (colon && *colon==':')\n\t\t\t\t\tend = strtol(colon+1, &colon, 0);\n\t\t\t\tfor (i = start; !end || i < end; i++)\n\t\t\t\t{\n\t\t\t\t\tdisasmfuncs->GenerateStatementString(disasmfuncs, i, buffer, sizeof(buffer));\n\t\t\t\t\tif (!*buffer)\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tCon_PrintCon(con, buffer, PFS_FORCEUTF8|PFS_KEEPMARKUP|PFS_NONOTIFY);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tfile = FS_OpenVFS(fname, \"rb\", FS_GAME);\n\t\t\t\tif (file)\n\t\t\t\t{\n\t\t\t\t\tunsigned char buffer[65536];\n#ifdef HAVE_LEGACY\n\t\t\t\t\tif (!strcmp(\".bin\", COM_GetFileExtension(fname, NULL)) && VFS_GETLEN(file) == 80*25*2)\n\t\t\t\t\t{\n\t\t\t\t\t\tstatic unsigned short ibmtounicode[256] =\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t0x0000,0x263A,0x263B,0x2665,0x2666,0x2663,0x2660,0x2022,0x25D8,0x25CB,0x25D9,0x2642,0x2640,0x266A,0x266B,0x263C,\t//0x00(non-ascii, display-only)\n\t\t\t\t\t\t\t0x25BA,0x25C4,0x2195,0x203C,0x00B6,0x00A7,0x25AC,0x21A8,0x2191,0x2193,0x2192,0x2190,0x221F,0x2194,0x25B2,0x25BC,\t//0x10(non-ascii, display-only)\n\t\t\t\t\t\t\t0x0020,0x0021,0x0022,0x0023,0x0024,0x0025,0x0026,0x0027,0x0028,0x0029,0x002A,0x002B,0x002C,0x002D,0x002E,0x002F,\t//0x20(ascii)\n\t\t\t\t\t\t\t0x0030,0x0031,0x0032,0x0033,0x0034,0x0035,0x0036,0x0037,0x0038,0x0039,0x003A,0x003B,0x003C,0x003D,0x003E,0x003F,\t//0x30(ascii)\n\t\t\t\t\t\t\t0x0040,0x0041,0x0042,0x0043,0x0044,0x0045,0x0046,0x0047,0x0048,0x0049,0x004A,0x004B,0x004C,0x004D,0x004E,0x004F,\t//0x40(ascii)\n\t\t\t\t\t\t\t0x0050,0x0051,0x0052,0x0053,0x0054,0x0055,0x0056,0x0057,0x0058,0x0059,0x005A,0x005B,0x005C,0x005D,0x005E,0x005F,\t//0x50(ascii)\n\t\t\t\t\t\t\t0x0060,0x0061,0x0062,0x0063,0x0064,0x0065,0x0066,0x0067,0x0068,0x0069,0x006A,0x006B,0x006C,0x006D,0x006E,0x006F,\t//0x60(ascii)\n\t\t\t\t\t\t\t0x0070,0x0071,0x0072,0x0073,0x0074,0x0075,0x0076,0x0077,0x0078,0x0079,0x007A,0x007B,0x007C,0x007D,0x007E,0x2302,\t//0x70(mostly ascii, one display-only)\n\t\t\t\t\t\t\t0x00C7,0x00FC,0x00E9,0x00E2,0x00E4,0x00E0,0x00E5,0x00E7,0x00EA,0x00EB,0x00E8,0x00EF,0x00EE,0x00EC,0x00C4,0x00C5,\t//0x80(non-ascii, printable)\n\t\t\t\t\t\t\t0x00C9,0x00E6,0x00C6,0x00F4,0x00F6,0x00F2,0x00FB,0x00F9,0x00FF,0x00D6,0x00DC,0x00A2,0x00A3,0x00A5,0x20A7,0x0192,\t//0x90(non-ascii, printable)\n\t\t\t\t\t\t\t0x00E1,0x00ED,0x00F3,0x00FA,0x00F1,0x00D1,0x00AA,0x00BA,0x00BF,0x2310,0x00AC,0x00BD,0x00BC,0x00A1,0x00AB,0x00BB,\t//0xa0(non-ascii, printable)\n\t\t\t\t\t\t\t0x2591,0x2592,0x2593,0x2502,0x2524,0x2561,0x2562,0x2556,0x2555,0x2563,0x2551,0x2557,0x255D,0x255C,0x255B,0x2510,\t//0xb0(box-drawing, printable)\n\t\t\t\t\t\t\t0x2514,0x2534,0x252C,0x251C,0x2500,0x253C,0x255E,0x255F,0x255A,0x2554,0x2569,0x2566,0x2560,0x2550,0x256C,0x2567,\t//0xc0(box-drawing, printable)\n\t\t\t\t\t\t\t0x2568,0x2564,0x2565,0x2559,0x2558,0x2552,0x2553,0x256B,0x256A,0x2518,0x250C,0x2588,0x2584,0x258C,0x2590,0x2580,\t//0xd0(box-drawing, printable)\n\t\t\t\t\t\t\t0x03B1,0x00DF,0x0393,0x03C0,0x03A3,0x03C3,0x00B5,0x03C4,0x03A6,0x0398,0x03A9,0x03B4,0x221E,0x03C6,0x03B5,0x2229,\t//0xe0(maths(greek), printable)\n\t\t\t\t\t\t\t0x2261,0x00B1,0x2265,0x2264,0x2320,0x2321,0x00F7,0x2248,0x00B0,0x2219,0x00B7,0x221A,0x207F,0x00B2,0x25A0,0x00A0,\t//0xf0(maths, printable)\n\t\t\t\t\t\t};\n\t\t\t\t\t\tint c, l, len;\n\t\t\t\t\t\tunsigned int u, code, oldcode;\n\t\t\t\t\t\tchar line[7*80+2];\n\t\t\t\t\t\tchar tohex[] = \"0123456789ABCDEF\";\n\t\t\t\t\t\tVFS_READ(file, buffer, 80*25*2);\n\t\t\t\t\t\tVFS_CLOSE(file);\n\t\t\t\t\t\tfor (l = 0; l < 25*(80*2); l+=80*2)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tfor(c = 0, len = 0, oldcode = ~0; c < 80; c++)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tu = buffer[l+c*2];\n\t\t\t\t\t\t\t\tcode = buffer[l+c*2+1];\n\t\t\t\t\t\t\t\tif (oldcode != code)\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\toldcode = code;\n\t\t\t\t\t\t\t\t\tline[len++] = '^';\n\t\t\t\t\t\t\t\t\tline[len++] = '&';\n\t\t\t\t\t\t\t\t\tline[len++] = tohex[(code&0xf)>>0];\n\t\t\t\t\t\t\t\t\tline[len++] = tohex[(code&0x70)>>4];\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tu = ibmtounicode[u];\t//convert from dos's IBM437 codepage to unicode.\n\t\t\t\t\t\t\t\tif (u == 0 || u == '\\t' || u == '\\n' || u == '\\r')\n\t\t\t\t\t\t\t\t\tline[len++] = ' ';\n\t\t\t\t\t\t\t\telse if (u == '^')\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tline[len++] = '^';\n\t\t\t\t\t\t\t\t\tline[len++] = '^';\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t\tlen += utf8_encode(&line[len], u, 3);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tline[len++] = '\\n';\n\t\t\t\t\t\t\tline[len] = 0;\n\t\t\t\t\t\t\tCon_PrintCon(con, line, PFS_FORCEUTF8|PFS_NONOTIFY);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\telse\n#endif\n\t\t\t\t\t{\n\t\t\t\t\t\twhile (VFS_GETS(file, buffer, sizeof(buffer)))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tCon_PrintCon(con, buffer, PFS_FORCEUTF8|PFS_KEEPMARKUP|PFS_NONOTIFY);\n\t\t\t\t\t\t\tCon_PrintCon(con, \"\\n\", PFS_FORCEUTF8|PFS_KEEPMARKUP|PFS_NONOTIFY);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tVFS_CLOSE(file);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tfor (l = con->oldest; l; l = l->newer)\n\t\t\t\tCon_Editor_LineChanged(con, l);\n\n\t\t\tcon->display = con->oldest;\n\t\t\tcon->selstartline = con->selendline = con->oldest;\t//put the cursor at the start of the file\n\t\t\tcon->selstartoffset = con->selendoffset = 0;\n\n\t\t\tif (line)\n\t\t\t\tCon_Editor_GoToLine(con, atoi(line));\n\t\t\telse\n\t\t\t\tCon_Editor_GoToLine(con, 1);\n\n\t\t\tCon_Footerf(con, false, \"    ^2%i lines\", con->linecount);\n\n\t\t\tCon_SetActive(con);\n\t\t}\n\t}\n\treturn con;\n}\n\nvoid Con_TextEditor_f(void)\n{\n\tchar *fname = Cmd_Argv(1);\n\tchar *line = strrchr(fname, ':');\n\tchar *lineend = NULL;\n\tif (line && strtol(line+1, &lineend, 0) && !*lineend)\n\t\t*line++ = 0;\n\tif (!*fname)\n\t{\n\t\tCon_Printf(\"%s [filename[:line]]: edit a file\\n\", Cmd_Argv(0));\n\t\treturn;\n\t}\n\tCon_TextEditor(fname, line, NULL);\n}\n\nint QCLibEditor(pubprogfuncs_t *prfncs, const char *filename, int *line, int *statement, int firststatement, char *reason, pbool fatal)\n{\n\tchar newname[MAX_QPATH];\n\tconsole_t *edit;\n\n\tif (!strncmp(filename, \"./\", 2))\n\t\tfilename+=2;\n\n\tstepasm = !line || *line < 0 || pr_debugger.ival==2;\n\n\t//we can cope with no line info by displaying asm\n\tif (editormodal || (stepasm && !statement))\n\t{\n\t\tif (fatal)\n\t\t\treturn DEBUG_TRACE_ABORTERROR;\n\t\treturn DEBUG_TRACE_OFF;\t//whoops\n\t}\n\n\tif (reason)\n\t\tCon_Printf(\"QC Exception: %s\\n\", reason);\n\n\tif (!pr_debugger.ival)\n\t{\n\t\tif (!stepasm && *filename)\n\t\t\tCon_Printf(\"Set %s to trace\\n\", pr_debugger.name);\n\t\tif (fatal)\n\t\t\treturn DEBUG_TRACE_ABORTERROR;\n\t\treturn DEBUG_TRACE_OFF;\t//get lost\n\t}\n\n\tif (qrenderer == QR_NONE)// || stepasm)\n\t{\t//just dump the line of code that's being execed onto the console.\n\t\tint i;\n\t\tchar buffer[8192];\n\t\tchar *r;\n\t\tvfsfile_t *f;\n\n\t\tif (stepasm)\n\t\t{\n\t\t\tprfncs->GenerateStatementString(prfncs, *statement, buffer, sizeof(buffer));\n\t\t\tCon_Printf(\"%s\", buffer);\n\t\t\treturn DEBUG_TRACE_INTO;\n\t\t}\n\n\t\tif (!line)\n\t\t{\t//please don't crash\n\t\t\tif (fatal)\n\t\t\t\treturn DEBUG_TRACE_ABORTERROR;\n\t\t\treturn DEBUG_TRACE_OFF;\t//whoops\n\t\t}\n\n\t\tf = FS_OpenVFS(filename, \"rb\", FS_GAME);\n\t\tif (!f)\n\t\t\tCon_Printf(\"%s - %i\\n\", filename, *line);\n\t\telse\n\t\t{\n\t\t\tfor (i = 0; i < *line; i++)\n\t\t\t{\n\t\t\t\tVFS_GETS(f, buffer, sizeof(buffer));\n\t\t\t}\n\t\t\tif ((r = strchr(buffer, '\\r')))\n\t\t\t{ r[0] = '\\n';r[1]='\\0';}\n\t\t\tCon_Printf(\"%s\", buffer);\n\t\t\tVFS_CLOSE(f);\n\t\t}\n\t\treturn DEBUG_TRACE_OUT;\t//only display the line itself.\n\t}\n\n\teditprogfuncs = prfncs;\n\n\tif (!stepasm && *filename && !COM_FCheckExists(filename))\n\t{\n\t\t//people generally run their qcc from $mod/src/ or so, so paths are usually relative to that instead of the mod directory.\n\t\t//this means we now need to try and guess what the user used.\n\t\tif (filename != newname && *pr_sourcedir.string)\n\t\t{\n\t\t\tQ_snprintfz(newname, sizeof(newname), \"%s/%s\", pr_sourcedir.string, filename);\n\t\t\tif (COM_FCheckExists(newname))\n\t\t\t\tfilename = newname;\n\t\t}\n\t\tif (filename != newname)\n\t\t{\n\t\t\tQ_snprintfz(newname, sizeof(newname), \"src/%s\", filename);\n\t\t\tif (COM_FCheckExists(newname))\n\t\t\t\tfilename = newname;\n\t\t}\n\t\tif (filename != newname)\n\t\t{\n\t\t\tQ_snprintfz(newname, sizeof(newname), \"source/%s\", filename);\n\t\t\tif (COM_FCheckExists(newname))\n\t\t\t\tfilename = newname;\n\t\t}\n\t\tif (filename != newname)\n\t\t{\n\t\t\tQ_snprintfz(newname, sizeof(newname), \"qcsrc/%s\", filename);\n\t\t\tif (COM_FCheckExists(newname))\n\t\t\t\tfilename = newname;\n\t\t}\n\t\tif (filename != newname)\n\t\t{\t//some people are fucked in the head\n\t\t\tQ_snprintfz(newname, sizeof(newname), \"progs/%s\", filename);\n\t\t\tif (COM_FCheckExists(newname))\n\t\t\t\tfilename = newname;\n\t\t}\n\t\tif (filename != newname)\n\t\t{\n\t\t\tstepasm = true;\n\t\t\tif (fatal)\n\t\t\t\tCon_Printf(CON_ERROR \"Unable to find file \\\"%s\\\"\\n\", filename);\n\t\t\telse\n\t\t\t\tCon_Printf(CON_WARNING \"Unable to find file \\\"%s\\\"\\n\", filename);\n\t\t}\n\t}\n\n\tif (stepasm)\n\t{\n\t\tif (*statement)\n\t\t{\n\t\t\tchar *fname = va(\":%#x:%#x\", firststatement, firststatement+300);\n\t\t\tedit = Con_TextEditor(fname, NULL, prfncs);\n\t\t\tif (!edit)\n\t\t\t\treturn DEBUG_TRACE_OFF;\n\t\t\tfirststatement--;\t//displayed statements are +1\n\t\t\texecutionlinenum = *statement - firststatement;\n\t\t\tCon_Editor_GoToLine(edit, executionlinenum);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (fatal)\n\t\t\t\treturn DEBUG_TRACE_ABORTERROR;\n\t\t\treturn DEBUG_TRACE_OFF;\t//whoops\n\t\t}\n\t}\n\telse\n\t{\n\t\tedit = Con_TextEditor(filename, NULL, NULL);\n\t\tif (!edit)\n\t\t\treturn DEBUG_TRACE_OFF;\n\t\tCon_Editor_GoToLine(edit, *line);\n\t\texecutionlinenum = *line;\n\t}\n\n\t{\n\t\tdouble oldrealtime = realtime;\n\n\t\tEditor_Demodalize();\n\t\teditormodal = edit;\n\t\teditorstep = DEBUG_TRACE_OFF;\n\n\t\tif (edit->userline)\n\t\t{\n\t\t\tedit->highlightline = edit->userline;\n\t\t\tedit->highlightline->flags |= CONL_EXECUTION;\n\t\t}\n\n\t\twhile(editormodal && editprogfuncs)\n\t\t{\n\t\t\trealtime = Sys_DoubleTime();\n\t\t\tscr_disabled_for_loading=false;\n\t\t\tSCR_UpdateScreen();\n\t\t\tSys_SendKeyEvents();\n\t\t\tIN_Commands ();\n\t\t\tS_ExtraUpdate();\n\n#ifdef CLIENTONLY\n\t\t\tSys_Sleep(20/1000.0);\n#else\n\t\t\tNET_Sleep(20/1000.0, false);\t//any os.\n#endif\n\t\t}\n\t\trealtime = oldrealtime;\n\n\t\tEditor_Demodalize();\n\t}\n\n\tif (stepasm)\n\t{\n\t\tif (line)\n\t\t\t*line = 0;\n\t\t*statement = executionlinenum+firststatement;\n\t}\n\telse if (line)\n\t\t*line = executionlinenum;\n\treturn editorstep;\n}\nvoid Editor_ProgsKilled(pubprogfuncs_t *dead)\n{\n\tif (editprogfuncs == dead)\n\t{\n\t\teditprogfuncs = NULL;\n\t\tEditor_Demodalize();\n\t\teditorstep = DEBUG_TRACE_OFF;\n\t}\n}\n\n\n\n\nvoid Editor_Init(void)\n{\n\tCmd_AddCommand(\"edit\", Con_TextEditor_f);\n\n\tCvar_Register(&editstripcr, \"Text editor\");\n\tCvar_Register(&editaddcr, \"Text editor\");\n//\tCvar_Register(&edittabspacing, \"Text editor\");\n\tCvar_Register(&pr_debugger, \"Text editor\");\n}\n\n#endif\n"
  },
  {
    "path": "engine/client/valid.c",
    "content": "#include \"quakedef.h\"\n#include <ctype.h>\n\n//ruleset validation:\n//internal rulesets are taken at their word.\n//\twe cannot verify the client build itself.\n//\tits too easy to spoof replies from the client (eg by using a proxy) so there's not much point trying to sign things by hashing the engine binary etc\n//\tand private keys in gpl software is not feasable.\n//custom/external rulesets may get out of sync with the engine. or the file hand-edited, or poorly updated etc.\n//\twe DO hash the rules and report them with the hash. this stops trivial text-editing cheaters.\n//\twe sort the rules etc so comments or reordering does not affect anything, allowing for some maintainence of the files without functional changes.\n//\tthis ensures newcomers to a tournament do not have to worry about inadvertantly using a cheat, they just need to update their ruleset files (presumably alongside the rest of whatever mod(s) they're playing with).\n\n#ifdef _WIN32\n#include \"winquake.h\"\n#endif\n\nqboolean care_f_modified;\nqboolean f_modified_particles;\n\n\ntypedef struct {\n\tchar *rulecond;\n\tchar *rulename;\n\tchar *rulevalue;\n} rulesetrule_t;\ntypedef struct {\n\thashfunc_t *hashfunc;\n\tchar *filename;\n\tchar *hash;\n\tsize_t numhashes;\t//we may allow more than one hash.\n} rulesetfilehashes_t;\ntypedef struct ruleset_s {\n\tstruct ruleset_s *next;\n\tchar *rulesetname;\n\n\tsize_t rules;\n\trulesetrule_t *rule;\n\n\tsize_t filehashes;\n\trulesetfilehashes_t *filehash;\n\n\tqboolean flagged;\n\n\tqbyte hash[20];\t//sha1\n\tqboolean external;\n} ruleset_t;\n\nstatic ruleset_t *ruleset_list;\nstatic ruleset_t *ruleset_current;\n\n\n\n\nstatic void RulesetLatch(cvar_t *cvar);\nstatic void QDECL rulesetcallback(cvar_t *var, char *oldval)\n{\n\tValidation_Apply_Ruleset();\n}\n\ncvar_t allow_f_version\t\t= CVAR(\"allow_f_version\", \"1\");\ncvar_t allow_f_server\t\t= CVAR(\"allow_f_server\", \"1\");\ncvar_t allow_f_skins\t\t= CVAR(\"allow_f_skins\", \"1\");\ncvar_t allow_f_ruleset\t\t= CVAR(\"allow_f_ruleset\", \"1\");\ncvar_t allow_f_scripts\t\t= CVAR(\"allow_f_scripts\", \"1\");\n#ifdef HAVE_LEGACY\ncvar_t allow_f_modified\t\t= CVAR(\"allow_f_modified\", \"1\");\ncvar_t allow_f_fakeshaft\t= CVAR(\"allow_f_fakeshaft\", \"1\");\ncvar_t auth_validateclients\t= CVAR(\"auth_validateclients\", \"1\");\n#endif\ncvar_t allow_f_system\t\t= CVAR(\"allow_f_system\", \"0\");\ncvar_t allow_f_cmdline\t\t= CVAR(\"allow_f_cmdline\", \"0\");\ncvar_t ruleset\t\t\t= CVARCD(\"ruleset\", \"none\", rulesetcallback, \"Known rulesets are:\\nnone: no explicit rules, all 'minor cheats' are allowed.\\nstrict: equivelent to the smackdown ruleset. Note that this will block certain graphical enhancements too.\");\n\n#ifdef HAVE_LEGACY\n#define SECURITY_INIT_BAD_CHECKSUM\t1\n#define SECURITY_INIT_BAD_VERSION\t2\n#define SECURITY_INIT_ERROR\t\t\t3\n#define SECURITY_INIT_NOPROC\t\t4\n\ntypedef struct signed_buffer_s {\n\tqbyte *buf;\n\tunsigned long size;\n} signed_buffer_t;\n\ntypedef signed_buffer_t *(*Security_Verify_Response_t) (int playernum, unsigned char *, char *userinfo, char *serverinfo);\ntypedef int (*Security_Init_t) (char *);\ntypedef signed_buffer_t *(*Security_Generate_Crc_t) (int playernum, char *userinfo, char *serverinfo);\ntypedef signed_buffer_t *(*Security_IsModelModified_t) (char *, int, qbyte *, int);\ntypedef void (*Security_Supported_Binaries_t) (void *);\ntypedef void (*Security_Shutdown_t) (void);\n\n\nstatic Security_Verify_Response_t Security_Verify_Response;\nstatic Security_Init_t Security_Init;\nstatic Security_Generate_Crc_t Security_Generate_Crc;\nstatic Security_IsModelModified_t Security_IsModelModified;\nstatic Security_Supported_Binaries_t Security_Supported_Binaries;\nstatic Security_Shutdown_t Security_Shutdown;\n\n\n#if 0//def _WIN32\nstatic void *secmodule;\n#endif\n#endif\n\nstatic void Validation_Version(void)\n{\n\tchar sr[256];\n\tchar *s = sr;\n\tchar authbuf[256];\n\tchar *auth = authbuf;\n\n\textern cvar_t r_drawflat;\n\n\t//print certain allowed 'cheat' options.\n\t//realtime lighting (shadows can show around corners)\n\t//drawflat is just lame\n\t//24bits can be considered eeeevil, by some.\n#ifdef RTLIGHTS\n\tif (r_shadow_realtime_world.ival)\n\t\t*s++ = 'W';\n\telse if (r_shadow_realtime_dlight.ival)\n\t\t*s++ = 'S';\n#endif\n\tif (r_drawflat.ival || r_lightmap.ival)\n\t\t*s++ = 'F';\n\tif (gl_load24bit.ival)\n\t\t*s++ = 'H';\n\n\t*s = '\\0';\n\n\tif (!allow_f_version.ival)\n\t\treturn;\t//suppress it\n\n#ifdef HAVE_LEGACY\n\tif (Security_Generate_Crc)\n\t{\n\t\tsigned_buffer_t *resp;\n\n\t\tresp = NULL;//Security_Generate_Crc(cl.playerview[0].playernum, cl.players[cl.playerview[0].playernum].userinfo, cl.serverinfo);\n\t\tif (!resp || !resp->buf)\n\t\t\tauth = \"\";\n\t\telse\n\t\t\tQ_snprintfz(auth, sizeof(authbuf), \" crc: %s\", resp->buf);\n\t}\n\telse\n#endif\n\t\tauth = \"\";\n\n\tif (*sr)\n\t\tCbuf_AddText (va(\"say %s \"PLATFORM\"/%s/%s%s\\n\", version_string(), q_renderername, sr, auth), RESTRICT_RCON);\n\telse\n\t\tCbuf_AddText (va(\"say %s \"PLATFORM\"/%s%s\\n\", version_string(), q_renderername, auth), RESTRICT_RCON);\n}\nvoid Validation_CheckIfResponse(char *text)\n{\n#ifdef HAVE_LEGACY\n\t//client name, version type(os-renderer where it matters, os/renderer where renderer doesn't), 12 char hex crc\n\tint f_query_client;\n\tint i;\n\tchar *crc;\n\tchar *versionstring;\n\n\tif (!Security_Verify_Response)\n\t\treturn;\t//valid or not, we can't check it.\n\n\tif (!auth_validateclients.ival)\n\t\treturn;\n\n\t//do the parsing.\n\t{\n\t\tchar *comp;\n\t\tint namelen;\n\n\t\tfor (crc = text + strlen(text) - 1; crc > text; crc--)\n\t\t\tif ((unsigned)*crc > ' ')\n\t\t\t\tbreak;\n\n\t\t//find the crc.\n\t\tfor (i = 0; i < 29; i++)\n\t\t{\n\t\t\tif (crc <= text)\n\t\t\t\treturn;\t//not enough chars.\n\t\t\tif ((unsigned)crc[-1] <= ' ')\n\t\t\t\tbreak;\n\t\t\tcrc--;\n\t\t}\n\n\t\t//we now want 3 string seperated tokens, so the first starts at the fourth found ' ' + 1\n\t\ti = 7;\n\t\tfor (comp = crc-1; ; comp--)\n\t\t{\n\t\t\tif (comp < text)\n\t\t\t\treturn;\n\t\t\tif (*comp == ' ')\n\t\t\t{\n\t\t\t\ti--;\n\t\t\t\tif (!i)\n\t\t\t\t\tbreak;\n\t\t\t}\n\n\t\t}\n\n\t\tversionstring = comp+1;\n\t\tif (comp <= text)\n\t\t\treturn;\t//not enough space for the 'name:'\n\t\tif (*(comp-1) != ':')\n\t\t\treturn;\t//whoops. not a say.\n\n\t\tnamelen = comp - text-1;\n\n\t\tfor (f_query_client = 0; f_query_client < cl.allocated_client_slots; f_query_client++)\n\t\t{\n\t\t\tif (strlen(cl.players[f_query_client].name) == namelen)\n\t\t\t\tif (!strncmp(cl.players[f_query_client].name, text, namelen))\n\t\t\t\t\tbreak;\n\t\t}\n\t\tif (f_query_client == cl.allocated_client_slots)\n\t\t\treturn; //looks like a validation, but it's not from a known client.\n\t}\n\n\t{\n\t\tchar *match = DISTRIBUTION\" v\";\n\t\tif (strncmp(versionstring, match, strlen(match)))\n\t\t\treturn;\t//this is not us\n\t}\n\n\t//now do the validation\n\t{\n\t\tsigned_buffer_t *resp;\n\n\t\tresp = NULL;//Security_Verify_Response(f_query_client, crc, cl.players[f_query_client].userinfo, cl.serverinfo);\n\n\t\tif (resp && resp->size && *resp->buf)\n\t\t\tCon_Printf(CON_NOTICE \"Authentication Successful.\\n\");\n\t\telse// if (!resp)\n\t\t\tCon_Printf(CON_ERROR \"AUTHENTICATION FAILED.\\n\");\n\t}\n#endif\n}\n\nvoid InitValidation(void)\n{\n\tCvar_Register(&allow_f_version,\t\"Authentication\");\n\tCvar_Register(&allow_f_server,\t\"Authentication\");\n#ifdef HAVE_LEGACY\n\tCvar_Register(&allow_f_modified,\t\"Authentication\");\n\tCvar_Register(&allow_f_fakeshaft,\t\"Authentication\");\n#endif\n\tCvar_Register(&allow_f_skins,\t\"Authentication\");\n\tCvar_Register(&allow_f_ruleset,\t\"Authentication\");\n\tCvar_Register(&allow_f_scripts,\t\"Authentication\");\n\tCvar_Register(&allow_f_system,\t\"Authentication\");\n\tCvar_Register(&allow_f_cmdline,\t\"Authentication\");\n\tCvar_Register(&ruleset,\t\t\"Authentication\");\n\n#ifdef HAVE_LEGACY\n#if 0//def _WIN32\n\tsecmodule = LoadLibrary(\"fteqw-security.dll\");\n\tif (secmodule)\n\t{\n\t\tSecurity_Verify_Response\t= (void*)GetProcAddress(secmodule, \"Security_Verify_Response\");\n\t\tSecurity_Init\t\t\t\t= (void*)GetProcAddress(secmodule, \"Security_Init\");\n\t\tSecurity_Generate_Crc\t\t= (void*)GetProcAddress(secmodule, \"Security_Generate_Crc\");\n\t\tSecurity_IsModelModified\t= (void*)GetProcAddress(secmodule, \"Security_IsModelModified\");\n\t\tSecurity_Supported_Binaries\t= (void*)GetProcAddress(secmodule, \"Security_Supported_Binaries\");\n\t\tSecurity_Shutdown\t\t\t= (void*)GetProcAddress(secmodule, \"Security_Shutdown\");\n\t}\n#endif\n\n\tif (Security_Init)\n\t{\n\t\tswitch(Security_Init(va(\"%s %.2f %i\", DISTRIBUTION, 2.57, version_number())))\n\t\t{\n\t\tcase SECURITY_INIT_BAD_CHECKSUM:\n\t\t\tCon_Printf(\"Checksum failed. Security module does not support this build. Go upgrade it.\\n\");\n\t\t\tbreak;\n\t\tcase SECURITY_INIT_BAD_VERSION:\n\t\t\tCon_Printf(\"Version failed. Security module does not support this version. Go upgrade.\\n\");\n\t\t\tbreak;\n\t\tcase SECURITY_INIT_ERROR:\n\t\t\tCon_Printf(\"'Generic' security error. Stop hacking.\\n\");\n\t\t\tbreak;\n\t\tcase SECURITY_INIT_NOPROC:\n\t\t\tCon_Printf(\"/proc/* does not exist. You will need to upgrade/reconfigure your kernel.\\n\");\n\t\t\tbreak;\n\t\tcase 0:\n\t\t\tCvar_Register(&auth_validateclients,\t\"Authentication\");\n\t\t\treturn;\n\t\t}\n#if 0//def _WIN32\n\t\tFreeLibrary(secmodule);\n#endif\n\t}\n\tSecurity_Verify_Response\t= NULL;\n\tSecurity_Init\t\t\t\t= NULL;\n\tSecurity_Generate_Crc\t\t= NULL;\n\tSecurity_IsModelModified\t= NULL;\n\tSecurity_Supported_Binaries\t= NULL;\n\tSecurity_Shutdown\t\t\t= NULL;\n#endif\n}\n\n//////////////////////\n//f_modified\n\n#ifdef HAVE_LEGACY\n#define FMOD_DM 1\n#define FMOD_TF 2\nstatic const struct {\n\tconst char *name;\n\tunsigned int flags;\n\tunsigned int hashes;\n\tconst char *hash;\n} modifiles[] =\n/*Note: I don't know what that 'debug' package refers to, these are just the hashes from ezquake.*/\n{\n\t{\"progs/armor.mdl\", FMOD_DM | FMOD_TF, 4,\t\t\t\"\\xef\\xb8\\xd1\\x18\\x73\\xd9\\x43\\xfe\\x13\\x43\\x10\\xbb\\x90\\x90\\x6a\\xef\\x96\\x86\\x04\\x2b\"\t//quake\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\\xfc\\x4f\\x26\\x8d\\x7c\\x1e\\xbb\\xfa\\xbc\\x28\\x11\\xc8\\x32\\x3d\\x63\\x21\\x4f\\x59\\x0b\\xfa\"\t//debug\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\\x2a\\xc5\\x72\\x77\\x13\\x7c\\xe3\\xde\\x23\\x85\\xbc\\xbb\\xba\\x25\\x85\\x34\\x49\\xd6\\x1b\\x6e\"\t//ruohis\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\\xb2\\xe7\\x47\\x56\\x46\\x57\\x46\\x52\\xfa\\x20\\x03\\xc1\\x0c\\xc9\\x92\\x1e\\x72\\x7c\\x57\\x27\"},//plaguespack\n\t{\"progs/backpack.mdl\", FMOD_DM | FMOD_TF, 2,\t\t\"\\xeb\\xda\\xce\\x80\\x82\\xd2\\xf7\\x18\\xc2\\xe7\\x11\\xa9\\xaa\\x09\\xe6\\xff\\x05\\x60\\x0a\\x05\"\t//quake\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\\x82\\xf3\\xc1\\xe7\\x2e\\xc2\\x3d\\x0c\\xc0\\x04\\x1a\\xd3\\x52\\xed\\x51\\x72\\x23\\xf2\\xba\\x45\"},//debug\n\t{\"progs/bolt2.mdl\", FMOD_DM | FMOD_TF, 1,\t\t\t\"\\xde\\xa8\\xfb\\x14\\xe4\\x7a\\x23\\x99\\x43\\x60\\x80\\xc8\\x54\\xa4\\xee\\xeb\\xa9\\x99\\x4a\\x02\"},//quake\n\t{\"progs/end1.mdl\", FMOD_DM | FMOD_TF, 2,\t\t\t\"\\x9f\\xd1\\xfe\\xd1\\x32\\xc6\\x67\\x5d\\xe6\\xa0\\x72\\x1d\\xd7\\x39\\xab\\x14\\xc4\\x35\\xf4\\x4b\"\t//quake\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\\x22\\x93\\xff\\x51\\x3d\\xb8\\x31\\x8b\\xbf\\xbe\\x88\\x89\\xe7\\xdc\\x17\\x09\\x04\\x8e\\x57\\xd9\"},//ruohis\n\t{\"progs/end2.mdl\", FMOD_DM | FMOD_TF, 3,\t\t\t\"\\x20\\xe6\\xee\\x98\\x79\\xcd\\x7a\\x10\\x0d\\x62\\x31\\x83\\x48\\x18\\x9a\\x2a\\x1a\\x9e\\xd2\\x64\"\t//quake\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\\x10\\xb9\\xa1\\xe3\\x6a\\xe3\\xc4\\x28\\x21\\x93\\xf6\\x1a\\x99\\xdf\\x19\\x9d\\xcb\\xbf\\xce\\x5b\"\t//unknown\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\\x4b\\x1e\\xa7\\x2e\\x22\\xcf\\xf6\\x0e\\xa5\\x3b\\xd5\\x86\\x3d\\x43\\x45\\x11\\x54\\x20\\x71\\xe6\"},//ruohis\n\t{\"progs/end3.mdl\", FMOD_DM | FMOD_TF, 3,\t\t\t\"\\x6d\\x33\\x1e\\x7f\\xb0\\x4b\\x4f\\xe0\\x1a\\x5a\\xc2\\x4a\\xe7\\xdd\\xae\\x2c\\x19\\x33\\x97\\x09\"\t//quake\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\\x10\\xb9\\xa1\\xe3\\x6a\\xe3\\xc4\\x28\\x21\\x93\\xf6\\x1a\\x99\\xdf\\x19\\x9d\\xcb\\xbf\\xce\\x5b\"\t//unknown\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\\xa0\\xed\\xc3\\x04\\x34\\x25\\x4f\\x2b\\xca\\xbb\\x7a\\x12\\xcf\\x97\\x5c\\x75\\x97\\x65\\x5b\\x13\"},//ruohis\n\t{\"progs/end4.mdl\", FMOD_DM | FMOD_TF, 3,\t\t\t\"\\xaa\\x72\\xb5\\xa4\\x8d\\xc3\\x00\\xdd\\xa2\\x8d\\x0f\\x13\\x55\\x0e\\x7f\\x79\\x76\\x2b\\xa5\\xcc\"\t//quake\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\\x10\\xb9\\xa1\\xe3\\x6a\\xe3\\xc4\\x28\\x21\\x93\\xf6\\x1a\\x99\\xdf\\x19\\x9d\\xcb\\xbf\\xce\\x5b\"\t//unkown\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\\xc1\\x54\\x5a\\x4d\\xba\\xaa\\xa8\\x1f\\x2b\\x5b\\x0a\\x60\\x3f\\xff\\x08\\x75\\xde\\x03\\xaf\\xc7\"},//ruohis\n\t{\"progs/eyes.mdl\", FMOD_DM | FMOD_TF, 1,\t\t\t\"\\xa8\\x25\\x6f\\x27\\x82\\xf7\\xb8\\xc6\\x52\\x5d\\xf7\\x3e\\x3e\\x16\\x1d\\x91\\x57\\xe5\\x79\\x5d\"},//quake\n\t{\"progs/g_light.mdl\", FMOD_DM | FMOD_TF, 4,\t\t\t\"\\x66\\x93\\x9b\\xcb\\x91\\x4d\\xd7\\x1f\\xf6\\x82\\x6a\\x9a\\x3e\\x2e\\x6b\\x5d\\xac\\xf5\\x58\\xc4\"\t//quake\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\\x78\\xb4\\x78\\x9d\\x49\\xc8\\x44\\xfb\\x9d\\x6d\\x6b\\xc5\\x39\\x78\\x73\\xab\\x48\\xd8\\xcf\\x1f\"\t//debug\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\\x92\\xb8\\x31\\x62\\x16\\xd6\\x39\\x1f\\x5d\\x71\\x42\\x14\\x26\\x28\\xfc\\xfb\\xd6\\xd8\\x66\\xc8\"\t//plague\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\\xb8\\x50\\x2d\\xfe\\xb4\\x6e\\xd5\\x6a\\xeb\\xfc\\x79\\xe5\\x6d\\x6f\\xe4\\x43\\xda\\x16\\xf6\\x82\"},//ruohis\n\t{\"progs/g_nail.mdl\", FMOD_DM | FMOD_TF, 5,\t\t\t\"\\x71\\x9f\\x20\\x7b\\x0f\\xd0\\x7c\\xa7\\x53\\x49\\xf2\\x91\\x4b\\x26\\x2f\\x93\\x40\\x74\\x0e\\x35\"\t//quake\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\\x5d\\x9f\\x61\\xc6\\x85\\x1a\\x51\\x55\\x63\\xcc\\xe0\\x6d\\x1a\\x17\\x61\\xef\\x73\\xb1\\xb1\\x36\"\t//debug\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\\xc6\\x31\\x2e\\xcb\\xab\\x64\\x10\\x1f\\x81\\xe6\\x0b\\x5e\\x42\\x86\\x65\\x5e\\xf4\\x1e\\x41\\xd9\"\t//plague\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\\x7e\\xa3\\x7d\\xbe\\x2b\\x27\\x31\\xb5\\x9a\\xe8\\x7d\\x0c\\xbd\\x87\\xf3\\x26\\xee\\x31\\xdc\\x20\"\t//ruohis\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\\x64\\x19\\xdd\\x86\\x85\\x6f\\xe6\\xeb\\x4c\\x5b\\x7e\\xf2\\xda\\xae\\x30\\x32\\x60\\x38\\xc1\\x10\"},//pdp\n\t{\"progs/g_nail2.mdl\", FMOD_DM | FMOD_TF, 4,\t\t\t\"\\x99\\x44\\xcb\\x97\\x1a\\xe1\\xe3\\x88\\xca\\x6e\\xec\\xec\\x8f\\x6c\\x3f\\x88\\x0c\\xcf\\x7e\\xac\"\t//quake\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\\x69\\x83\\xd5\\x9c\\x27\\x85\\x4c\\xe5\\xb4\\x34\\xa2\\xce\\x3e\\xd9\\x2b\\x47\\x55\\x86\\xfa\\x7d\"\t//debug\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\\x01\\xe4\\x3f\\x7d\\x44\\x43\\x8a\\x73\\x39\\x24\\xb6\\x4a\\xeb\\x13\\x73\\xba\\x36\\x85\\x6f\\x6a\"\t//plague\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\\xca\\x15\\xdf\\x38\\x50\\x43\\xdb\\xe2\\x41\\x58\\x06\\x5c\\x8c\\x54\\xec\\x67\\xb2\\xf0\\xb3\\x3d\"},//ruohis\n\t{\"progs/g_rock.mdl\", FMOD_DM | FMOD_TF, 5,\t\t\t\"\\x83\\xdd\\x17\\x90\\x4c\\x95\\x0d\\x15\\x44\\x45\\x5f\\x9b\\x72\\x3b\\x84\\x10\\x8e\\x06\\x97\\x46\"\t//quake\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\\x53\\x92\\x4e\\x33\\x52\\xa2\\xa5\\xa5\\x56\\xa8\\xb9\\x68\\x47\\x66\\x22\\x5e\\xc7\\xee\\xba\\xe4\"\t//debug\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\\xb0\\xc7\\x1f\\xe3\\x18\\x06\\x20\\x35\\x3c\\x97\\xa6\\x8b\\x55\\xc5\\x96\\x12\\xde\\x1b\\x54\\xb2\"\t//plague\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\\x9a\\xe2\\xe3\\xd2\\xcf\\x2a\\x3a\\x1e\\x53\\x1d\\xf2\\xa2\\xdd\\x2a\\x45\\x60\\xfa\\x2a\\x73\\xd8\"\t//ruohis\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\\xfe\\x90\\xe9\\x10\\xd2\\x1e\\x40\\x3b\\xad\\x71\\x9d\\x9a\\x59\\xf0\\x85\\x90\\x8f\\x08\\x10\\x77\"},//pdp\n\t{\"progs/g_rock2.mdl\", FMOD_DM | FMOD_TF, 5,\t\t\t\"\\x20\\xec\\x47\\x5a\\xdc\\x1c\\x21\\xd0\\x60\\xaf\\xb8\\xd6\\xab\\x3e\\x81\\xaf\\x5b\\x0b\\x33\\xba\"\t//quake\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\\xf9\\x10\\x4e\\xe2\\x41\\xbc\\x53\\x0f\\x2b\\xee\\x43\\x60\\xec\\x7e\\x57\\x3d\\x4c\\x12\\x75\\xbc\"\t//debug\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\\xe9\\xb3\\x4e\\x67\\x27\\x59\\x32\\xde\\x37\\x43\\xbd\\xda\\x5c\\x75\\x7a\\xc9\\xf9\\xf1\\xf4\\x97\"\t//plagur\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\\x58\\x0c\\x35\\x54\\x88\\xfd\\x09\\x6a\\x80\\x4b\\x21\\xae\\xde\\x71\\x1d\\xc7\\xe6\\x0f\\x9d\\x10\"\t//ruohis\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\\xd9\\x80\\x42\\x3c\\x56\\x3a\\xa4\\xd8\\xeb\\x31\\xf9\\xef\\xaf\\x10\\x63\\xb7\\xad\\x39\\x8c\\xb2\"},//pdp\n\t{\"progs/g_shot.mdl\", FMOD_DM | FMOD_TF, 5,\t\t\t\"\\xe1\\x35\\xa7\\x35\\x90\\x60\\xee\\xc1\\xb5\\x40\\x89\\x9f\\x1c\\xfd\\xde\\x6c\\x67\\x1d\\xec\\x7e\"\t//quake\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\\x28\\xa8\\xbb\\x7b\\x98\\x8f\\x43\\x99\\x47\\x37\\x5e\\x97\\x2b\\x8a\\xbc\\x6c\\xb7\\x4d\\xa6\\xd3\"\t//debug\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\\x58\\x63\\x48\\xd3\\x37\\x3d\\x3a\\x4a\\xe4\\x43\\xfc\\x0e\\x89\\x2f\\xa4\\x55\\x19\\x85\\x07\\xf8\"\t//plague\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\\xd8\\xaf\\x4c\\xc7\\x02\\xf9\\x3a\\xbc\\x88\\xb5\\x52\\xbb\\x30\\xca\\x6f\\x6f\\x54\\xb5\\x2a\\x5b\"\t//ruohis\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\\x5f\\x01\\x9f\\x3a\\x9e\\xa2\\x17\\xd8\\xfa\\x87\\x7a\\xdc\\x28\\x16\\xe3\\xc6\\x19\\x11\\x99\\xaa\"},//pdp\n\t{\"progs/gib1.mdl\", FMOD_DM | FMOD_TF, 3,\t\t\t\"\\xa4\\x9e\\xdc\\x99\\x4a\\xf7\\x9b\\x6e\\x1e\\x0a\\x71\\x25\\x7b\\xc7\\x1f\\x70\\x92\\x70\\x77\\x09\"\t//quake\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\\xfd\\x06\\xee\\x69\\x83\\x84\\xac\\x8b\\x3e\\xa4\\xc5\\xf9\\x22\\x37\\x51\\xd7\\xff\\xa4\\xd5\\x55\"\t//debug\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\\xf8\\x57\\xff\\x05\\x96\\xa0\\x73\\x10\\x55\\xd0\\xe5\\xc7\\x0b\\x04\\x6c\\x9c\\x8a\\xeb\\xd2\\x96\"},//ruohis\n\t{\"progs/gib2.mdl\", FMOD_DM | FMOD_TF, 3,\t\t\t\"\\x9b\\xe2\\xc3\\x5a\\xd9\\x58\\x63\\xd6\\x7a\\xd2\\x44\\x10\\xad\\x48\\xda\\xb3\\xbb\\x9f\\x1e\\x5f\"\t//quake\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\\xdf\\xa1\\x51\\x32\\x08\\x82\\xe6\\x50\\x97\\xf7\\xf0\\xef\\x71\\x4e\\x89\\x89\\xe3\\x5b\\x50\\x65\"\t//debug\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\\x87\\xd6\\x23\\xdf\\x47\\x90\\xea\\x51\\x97\\x34\\xdb\\xbf\\xdb\\x63\\x7b\\xed\\xfb\\x1b\\x1a\\x13\"},//ruohis\n\t{\"progs/gib3.mdl\", FMOD_DM | FMOD_TF, 3,\t\t\t\"\\x61\\x8e\\x55\\xc6\\x63\\x4f\\xea\\x13\\x45\\xda\\xc9\\x20\\x2e\\x21\\x40\\x06\\x50\\xf3\\x98\\x7b\"\t//quake\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\\x46\\x0b\\x8f\\x79\\x50\\x72\\x5c\\xe5\\xf5\\xf3\\x2f\\x88\\x80\\x5a\\x49\\x75\\x99\\xed\\xa3\\x19\"\t//debug\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\\x1e\\xf0\\x5d\\xff\\x2f\\x95\\x06\\x76\\x84\\xc7\\x36\\xdd\\x33\\x2d\\xd7\\xa0\\x33\\xfa\\x6a\\x06\"},//ruohis\n\t{\"progs/grenade.mdl\", FMOD_DM | FMOD_TF, 4,\t\t\t\"\\xb8\\xff\\xdf\\x60\\x0c\\x1f\\x87\\xfc\\x25\\xc3\\xf3\\xd9\\xaf\\xdc\\xaa\\x61\\xbf\\x7c\\xc3\\x0e\"\t//quake\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\\x12\\xdb\\xf5\\xda\\x02\\xfc\\xd4\\x41\\x5a\\xd3\\x4d\\x76\\x88\\x08\\x49\\xa4\\xea\\x6c\\xd2\\xd5\"\t//debug\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\\x10\\xb9\\xa1\\xe3\\x6a\\xe3\\xc4\\x28\\x21\\x93\\xf6\\x1a\\x99\\xdf\\x19\\x9d\\xcb\\xbf\\xce\\x5b\"\t//plague\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\\x1e\\x57\\xe5\\xec\\xe8\\x61\\x5d\\xa3\\x49\\xdf\\xb6\\xe2\\xd9\\x72\\x53\\xf3\\x10\\x89\\xc7\\x7a\"},//ruohis\n\t{\"progs/invisibl.mdl\", FMOD_DM | FMOD_TF, 3,\t\t\"\\x89\\xf3\\xe2\\x23\\x7f\\x65\\x79\\x84\\x25\\x0d\\x7e\\x43\\xae\\x0b\\x10\\xee\\x75\\xa7\\xd6\\xba\"\t//quake\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\\x60\\xae\\xac\\xe5\\xfd\\xe8\\x2f\\x8b\\x78\\x8e\\xef\\xb8\\xe4\\x6a\\x23\\x8d\\xe3\\x0b\\xdb\\xc3\"\t//debug\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\\x7e\\x7e\\x50\\xb7\\x19\\x70\\xdc\\x84\\x39\\x1c\\x5f\\x8f\\x79\\x29\\xdf\\xc0\\xdd\\x93\\x6b\\xe8\"},//ruohis\n\t{\"progs/invulner.mdl\", FMOD_DM | FMOD_TF, 3,\t\t\"\\x75\\xe1\\x7e\\x98\\x35\\x4f\\x0d\\xfd\\x1c\\x64\\xd2\\x06\\xc8\\x0d\\x5c\\x72\\x7a\\x53\\x1f\\x87\"\t//quake\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\\x3e\\x0a\\xb0\\x57\\x6d\\xfa\\x9a\\x00\\xcb\\xc8\\xc2\\xa4\\xcc\\xec\\xb0\\xa7\\x49\\x70\\xe5\\xa9\"\t//debug\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\\x18\\xa1\\xcc\\xdd\\x41\\x3d\\x14\\x9e\\x57\\xdc\\xa8\\xa2\\xb7\\x4e\\x74\\x82\\x1b\\x30\\xaf\\x09\"},//ruohis\n\t{\"progs/missile.mdl\", FMOD_DM | FMOD_TF, 4,\t\t\t\"\\xe8\\xee\\xdf\\x9a\\xc1\\x72\\x58\\x18\\xf8\\x36\\xbb\\xb3\\xab\\x29\\x6e\\x99\\xa9\\xb2\\x6a\\xd4\"\t//quake\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\\x78\\xa0\\xe7\\x2a\\xd4\\x93\\x93\\xc3\\x88\\x67\\x57\\x73\\xd2\\x99\\x26\\x24\\xfd\\x0b\\x19\\x8f\"\t//debug\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\\xec\\xb3\\x47\\xe0\\xe2\\xd2\\x03\\xad\\x07\\x62\\x14\\x2a\\xdf\\xf2\\xe1\\x99\\x42\\x9f\\x22\\xfb\"\t//plague\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\\xca\\x4a\\x84\\x7e\\xf9\\x7e\\xb0\\xb1\\xd8\\x94\\x89\\x3d\\x4e\\xd1\\xb4\\xe6\\x58\\x98\\xc4\\x56\"},//ruohis\n\t{\"progs/quaddama.mdl\", FMOD_DM | FMOD_TF, 3,\t\t\"\\x63\\xf6\\x60\\x27\\x05\\x84\\xdc\\x32\\xdf\\x63\\x75\\x05\\xa7\\xc3\\x14\\x96\\x9b\\x94\\x25\\x01\"\t//quake\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\\x56\\xa1\\x10\\x90\\xdb\\xad\\x63\\x1b\\xe3\\xd9\\x9b\\xbc\\x4e\\x6e\\x8d\\xff\\x60\\x12\\xcd\\xce\"\t//debug\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\\x6c\\xcf\\x93\\xdb\\xb8\\xa0\\x06\\x70\\x56\\x8c\\xb2\\x90\\xa7\\xfb\\x7a\\xf9\\xcb\\x99\\x36\\x42\"},//ruohis\n\t{\"progs/s_spike.mdl\", FMOD_DM | FMOD_TF, 4,\t\t\t\"\\xe5\\xf8\\x08\\xf3\\xe2\\x42\\xc2\\xcd\\x1f\\xb0\\x71\\x4f\\x0a\\x88\\xb9\\xaf\\x9f\\x8e\\x19\\x52\"\t//quake\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\\xcc\\xe4\\x59\\xb1\\xf0\\xcc\\x5d\\xbc\\xab\\x93\\x6e\\x65\\x24\\xdd\\x72\\x3e\\xc6\\x6f\\x44\\x10\"\t//debug\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\\x11\\x1b\\xc6\\xe7\\x30\\x7f\\x3a\\x70\\xda\\xa5\\x51\\x00\\xd1\\x5b\\x4a\\xb8\\xac\\x45\\x36\\xe2\"\t//plague\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\\xc5\\x84\\xbf\\x40\\x9c\\x5a\\xb8\\xca\\x24\\xaa\\x8b\\x49\\xc6\\xd9\\x07\\xbe\\x56\\x67\\x66\\x6b\"},//ruohis\n\t{\"progs/spike.mdl\", FMOD_DM | FMOD_TF, 4,\t\t\t\"\\xaf\\xad\\xd9\\xeb\\x28\\x2f\\x3b\\xfb\\x34\\x2c\\xcc\\x67\\x1a\\xc2\\x6e\\x92\\x33\\xa2\\xe1\\x09\"\t//quake\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\\x44\\xb9\\x8b\\xe7\\xe4\\x53\\xa7\\x92\\x6b\\x22\\x5c\\x43\\x5e\\xa6\\x21\\x40\\x6b\\x8c\\x38\\xef\"\t//debug\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\\x95\\xcb\\xf1\\x28\\x91\\xed\\xb8\\xaf\\xff\\x00\\x83\\x6a\\x3f\\xc0\\x29\\xeb\\xcb\\xbb\\xa2\\x28\"\t//plague\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\\x80\\x59\\x22\\xd8\\xf7\\xe9\\x99\\x02\\x66\\xfd\\x32\\x67\\x64\\x52\\x55\\x54\\x03\\xa4\\xbd\\x67\"},//ruohis\n\t{\"progs/suit.mdl\", FMOD_DM | FMOD_TF, 2,\t\t\t\"\\xdd\\xb9\\xdc\\xb7\\x3b\\xa0\\x8d\\xed\\x5e\\xfc\\x6e\\x41\\x5a\\x8d\\xe3\\x8e\\x25\\xbf\\x63\\x40\"\t//quake\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\\xf3\\xe4\\x4d\\xbf\\xc1\\x27\\x09\\xe6\\x00\\x4f\\x82\\xf4\\x56\\xef\\xfe\\x21\\x97\\xa8\\x00\\xac\"},//ruohis\n\t{\"progs/player.mdl\", FMOD_DM, 2,\t\t\t\t\t\"\\xb4\\x0a\\xca\\x95\\x2e\\xe6\\x1b\\x02\\xa5\\xe9\\x55\\x66\\x1c\\xef\\xa7\\xd4\\x2f\\x58\\x84\\xb4\"\t//quake\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\\x59\\x34\\x22\\xc0\\x8d\\x7d\\xb9\\x42\\x72\\xf4\\x2f\\xc5\\x10\\x07\\xee\\xf3\\x32\\x11\\xe2\\x41\"},//capnbubs\n\t{\"progs/player.mdl\", FMOD_TF, 1,\t\t\t\t\t\"\\x58\\xb6\\xca\\x8f\\xef\\x97\\x7a\\x02\\x3d\\xee\\x6e\\xa9\\x46\\x4f\\xe4\\xc1\\xc9\\x33\\xa5\\x18\"},//tf\n\t{\"progs/tf_flag.mdl\", FMOD_TF, 1,\t\t\t\t\t\"\\xf0\\x9d\\x96\\x6e\\xef\\x9e\\x1b\\xc9\\x40\\x1a\\xcb\\x84\\x2e\\x12\\xee\\xca\\x28\\x3d\\x1d\\x1b\"},//tf\n\t{\"progs/turrbase.mdl\", FMOD_TF, 1,\t\t\t\t\t\"\\xd5\\x73\\xda\\x0c\\xcb\\x03\\x27\\x82\\x6b\\xbe\\x6c\\x9e\\xf7\\x95\\xfb\\x94\\xed\\x4b\\xc0\\xc7\"},//tf\n\t{\"progs/turrgun.mdl\", FMOD_TF, 1,\t\t\t\t\t\"\\x6f\\x12\\x4b\\x31\\xb5\\x7c\\x8d\\x7d\\xc8\\x85\\xf1\\x8d\\xf7\\x62\\x36\\x1f\\x7b\\x95\\x72\\xa2\"},//tf\n\t{\"progs/disp.mdl\", FMOD_TF, 1,\t\t\t\t\t\t\"\\x4a\\xfa\\x96\\x55\\x57\\x24\\x4e\\xd9\\xdf\\x84\\x0f\\x14\\xa8\\xdd\\x59\\x58\\xfc\\x52\\x67\\xe9\"},//tf\n\t{\"progs/tf_stan.mdl\", FMOD_TF, 1,\t\t\t\t\t\"\\x1b\\x2a\\x73\\x1b\\xc2\\x69\\x30\\x76\\x70\\xe2\\x79\\xe7\\xaa\\x9d\\x2b\\x25\\x2a\\xdb\\xf7\\xdd\"},//tf\n\t{\"progs/hgren2.mdl\", FMOD_TF, 1,\t\t\t\t\t\"\\x5f\\x71\\xc3\\x1d\\x0f\\x3c\\xc5\\x58\\xfc\\x04\\x15\\xdb\\xdf\\x4b\\x35\\xab\\x97\\xee\\xac\\x6a\"},//tf\n\t{\"progs/grenade2.mdl\", FMOD_TF, 1,\t\t\t\t\t\"\\xef\\x6c\\x11\\x4b\\x74\\xf0\\xc6\\xeb\\x73\\xea\\xcd\\x2a\\x16\\x87\\x6a\\x87\\x9d\\x40\\xe7\\xe5\"},//tf\n\t{\"progs/detpack.mdl\", FMOD_TF, 1,\t\t\t\t\t\"\\xe1\\xd4\\x73\\xaf\\x38\\xdc\\x98\\x0c\\xc0\\x83\\x39\\x6d\\x33\\x00\\xa7\\xd7\\x5e\\x67\\x6a\\x61\"},//tf\n\t{\"progs/s_bubble.spr\", FMOD_DM | FMOD_TF, 1,\t\t\"\\xf2\\xfa\\xbe\\x3d\\x54\\x86\\xb3\\x73\\x74\\x07\\x91\\x70\\xf3\\x85\\xe2\\xdf\\x0e\\xd9\\xb5\\x4d\"},//quake\n\t{\"progs/s_explod.spr\", FMOD_DM | FMOD_TF, 1,\t\t\"\\x06\\x7b\\x5a\\x29\\x88\\x1f\\xdf\\xac\\x94\\x08\\xa2\\x50\\x5f\\x90\\x75\\x5c\\x0b\\x5d\\xd9\\xb9\"},//quake\n\t{\"maps/b_bh100.bsp\", FMOD_DM | FMOD_TF, 4,\t\t\t\"\\x02\\xe3\\xe7\\x65\\x4d\\x5b\\xa8\\x94\\x74\\xe6\\x92\\x80\\xf0\\xe5\\x00\\xf7\\xcc\\x7f\\x66\\xde\"\t//quake\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\\xb6\\xd5\\x7f\\x52\\x7f\\x70\\x90\\xd4\\x22\\x96\\xd6\\xab\\x69\\x8a\\xd1\\xf3\\xb9\\x0e\\xbc\\xe9\"\t//ruohis1\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\\xc1\\x35\\x21\\xb4\\x9c\\x42\\x16\\x68\\x3e\\xce\\xe3\\x21\\xfc\\xf8\\xb9\\xfa\\xe1\\x7a\\x38\\x09\"\t//ruohis2\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\\xff\\x55\\xb4\\xc6\\x45\\xb8\\x7c\\xad\\x29\\x67\\x35\\xa7\\xcf\\x5f\\x37\\x35\\x79\\xb4\\xad\\x10\"},//generations\n\t{\"sound/buttons/airbut1.wav\", FMOD_DM | FMOD_TF, 2,\t\"\\x1e\\x14\\xd5\\x47\\xeb\\xe8\\x25\\xc9\\x3c\\x58\\xe5\\x26\\x21\\xd0\\xdf\\xc8\\xef\\x92\\x67\\x22\"\t//quake\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\\xe0\\xbc\\xfb\\xd5\\x31\\xe4\\x83\\x20\\x4d\\xc5\\x11\\xaa\\x53\\xe0\\x9c\\x08\\x11\\xce\\x03\\xdf\"},//mindgrind\n\t{\"sound/items/armor1.wav\", FMOD_DM | FMOD_TF, 2,\t\"\\xb0\\x8d\\x48\\x44\\x1d\\x0a\\x0b\\xef\\xb4\\xa8\\xcd\\x3a\\x67\\xb4\\x87\\x3d\\xcc\\x4f\\xdd\\xe4\"\t//quake\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\\xed\\xda\\x47\\x1b\\x6a\\xcd\\x60\\x5a\\x99\\x04\\x94\\x83\\x1b\\x65\\xeb\\x65\\xcf\\xf6\\x41\\x44\"},//mindgrind\n\t{\"sound/items/damage.wav\", FMOD_DM | FMOD_TF, 2,\t\"\\xac\\x78\\x77\\x19\\xc5\\x52\\xa2\\x92\\x56\\x02\\xc3\\x90\\x64\\xf2\\xa6\\x7b\\x4f\\x65\\xab\\x56\"\t//quake\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\\xa3\\x87\\xa0\\x3d\\xc7\\xd4\\x99\\x17\\x31\\xbf\\xda\\x8d\\x6f\\xde\\xe2\\x42\\xec\\xab\\xfd\\x5b\"},//mindgrind\n\t{\"sound/items/damage2.wav\", FMOD_DM | FMOD_TF, 2,\t\"\\x4d\\x9b\\x9a\\x71\\x18\\xb2\\x76\\x1c\\x9f\\x97\\xbf\\xfc\\xfa\\xe6\\xa5\\x5e\\x0e\\xda\\xaf\\x68\"\t//quake\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\\x9c\\x77\\xeb\\xd0\\x3c\\xd8\\xf1\\x0b\\x7e\\x1c\\x0e\\x12\\x9b\\x44\\xfa\\x50\\x5b\\x65\\xd4\\x2c\"},//mindgrind\n\t{\"sound/items/damage3.wav\", FMOD_DM | FMOD_TF, 2,\t\"\\x6c\\xde\\x07\\xfa\\x39\\x6d\\x30\\x6e\\xed\\xf3\\x18\\x7d\\x58\\x25\\x27\\x90\\x7a\\x1e\\xd0\\x63\"\t//quake\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\\x71\\x3b\\x0c\\x2e\\xc9\\xcf\\x52\\xdd\\xe5\\xe1\\x5f\\x8e\\xd9\\x8f\\xc4\\x0a\\x70\\xb9\\xd8\\xa2\"},//mindgrind\n\t{\"sound/items/health1.wav\", FMOD_DM | FMOD_TF, 1,\t\"\\xb2\\x17\\xdd\\xeb\\x80\\x9b\\x28\\xa1\\xf2\\xe3\\x10\\x78\\xbf\\x01\\x8d\\xdb\\x96\\x5a\\x49\\x0b\"},//quake\n\t{\"sound/items/inv1.wav\", FMOD_DM | FMOD_TF, 2,\t\t\"\\xb8\\x40\\x12\\xa0\\x30\\xd4\\x88\\xb5\\xe0\\x06\\x24\\xc6\\xfd\\x9d\\xe5\\x39\\x98\\x4b\\x5a\\xad\"\t//quake\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\\xba\\x08\\x77\\x78\\x51\\x2c\\xf5\\x63\\x32\\x59\\x83\\x55\\x86\\x6d\\xb4\\x7d\\xcc\\x30\\xc8\\xcc\"},//mindgrind\n\t{\"sound/items/inv2.wav\", FMOD_DM | FMOD_TF, 2,\t\t\"\\x5d\\x02\\xb6\\x9a\\xa2\\x24\\x9d\\x2d\\x7c\\xb1\\x27\\xc1\\x8a\\x90\\x9e\\x01\\xd3\\xf7\\x21\\xa5\"\t//quake\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\\x8c\\xbc\\xa3\\xb2\\x5f\\x6b\\xb5\\x7e\\x67\\x4d\\x5e\\x67\\x40\\x40\\x05\\x24\\x0b\\xcd\\x2e\\x3b\"},//mindgrind\n\t{\"sound/items/inv3.wav\", FMOD_DM | FMOD_TF, 2,\t\t\"\\x77\\x57\\x78\\xfb\\x26\\x28\\x62\\x9c\\x5c\\xd0\\x21\\x61\\x61\\x56\\x2d\\xf4\\x29\\x54\\x4a\\xa3\"\t//quake\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\\x33\\x54\\x94\\xa1\\x52\\x2c\\x3e\\x7f\\x8a\\x52\\x44\\x91\\xf0\\x1c\\x89\\x02\\x55\\xb2\\x5e\\xd9\"},//mindgrind\n\t{\"sound/items/itembk2.wav\", FMOD_DM | FMOD_TF, 2,\t\"\\x9b\\x51\\x8c\\x17\\x27\\x05\\x03\\x3b\\xd2\\xae\\x9a\\x75\\xd7\\xa7\\xdc\\xf7\\x36\\x1e\\x1a\\xf0\"\t//quake\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\\xad\\x6d\\xdd\\x41\\x4b\\xc2\\xa8\\x45\\x5e\\xb7\\x56\\xef\\x5a\\x91\\xb3\\x5a\\x0f\\xa7\\x01\\x29\"},//mindgrind\n\t{\"sound/player/land.wav\", FMOD_DM | FMOD_TF, 1,\t\t\"\\x41\\x5e\\xbb\\xb8\\x8e\\xad\\x87\\xf0\\xd5\\x3c\\x32\\x13\\x52\\x20\\x2d\\x2e\\x38\\x9e\\x8e\\x33\"},//quake\n\t{\"sound/player/land2.wav\", FMOD_DM | FMOD_TF, 1,\t\"\\x5c\\xb5\\x13\\x6c\\x89\\x15\\x6c\\xc4\\x42\\xac\\xab\\xee\\x4e\\xd2\\x7c\\x08\\x86\\x23\\x55\\xd9\"},//quake\n\t{\"sound/misc/outwater.wav\", FMOD_DM | FMOD_TF, 2,\t\"\\x36\\xfc\\xb6\\x9c\\xba\\xe9\\x20\\x9c\\x18\\x84\\x5f\\x59\\x9f\\x6d\\xe7\\x50\\xfd\\x3d\\x50\\xa7\"\t//quake\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\\x15\\x85\\xe9\\x6b\\x26\\x01\\xab\\xfe\\x11\\xc8\\xed\\x80\\x12\\x70\\xcf\\xe7\\x80\\x49\\xef\\x1d\"},//mindgrind\n\t{\"sound/weapons/pkup.wav\", FMOD_DM | FMOD_TF, 2,\t\"\\x23\\x35\\xa4\\x05\\x60\\xab\\xbb\\x09\\xa0\\x67\\xce\\x77\\x3d\\xe2\\x2f\\xb5\\x01\\x57\\x71\\xf2\"\t//quake\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\\xbc\\x1f\\x26\\xda\\x68\\x7c\\xf4\\x12\\x94\\xe7\\x91\\x9e\\x4d\\x43\\x0a\\xce\\x77\\x5e\\xe3\\xc4\"},//mindgrind\n\t{\"sound/player/plyrjmp8.wav\", FMOD_DM | FMOD_TF, 2,\t\"\\x46\\xe9\\xe2\\x75\\xf2\\x54\\xad\\xc9\\x03\\x7b\\xd4\\x6c\\xd4\\xc9\\xf0\\xee\\xda\\x86\\x33\\x5d\"\t//quake\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\\x63\\x82\\x9e\\x5a\\x6e\\xf0\\xb3\\xb9\\xe1\\x7d\\x04\\x8a\\xec\\x8b\\xc1\\x72\\x77\\xf9\\x7d\\x54\"},//mindgrind\n\t{\"sound/items/protect.wav\", FMOD_DM | FMOD_TF, 2,\t\"\\x51\\x13\\xff\\x38\\xc7\\x99\\x67\\x7d\\x6d\\x25\\x77\\x75\\x92\\x31\\x23\\xbf\\xaa\\x3b\\x22\\xb6\"\t//quake\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\\x3f\\x33\\x48\\x59\\x2a\\x88\\x8e\\xa2\\x64\\x29\\xb8\\xb4\\xc5\\x79\\x06\\xe1\\xb1\\x8c\\x1f\\x19\"},//mindgrind\n\t{\"sound/items/protect2.wav\", FMOD_DM | FMOD_TF, 2,\t\"\\x59\\xd1\\x8d\\x58\\x2c\\x9f\\x48\\xae\\x8e\\x11\\xf7\\x22\\x4c\\xeb\\x7e\\x48\\x97\\xc9\\x54\\x3c\"\t//quake\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\\xb0\\x77\\xe9\\x29\\xfe\\x78\\x93\\x0d\\x37\\x90\\x48\\xb4\\x62\\x3d\\xd8\\x02\\x8d\\x71\\x0b\\xce\"},//mindgrind\n\t{\"sound/items/protect3.wav\", FMOD_DM | FMOD_TF, 2,\t\"\\x43\\x11\\x9a\\x86\\x04\\xa4\\xe6\\xc9\\x0e\\xd9\\xd5\\xcb\\x4c\\x7e\\xb5\\xa1\\x20\\xdd\\x77\\xae\"\t//quake\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\\x4c\\x31\\x19\\x76\\x1c\\x90\\xb6\\x07\\xe2\\x01\\x06\\x00\\x7e\\x36\\x58\\xc3\\xb1\\x90\\x9f\\x99\"},//mindgrind\n\t{\"sound/items/r_item1.wav\", FMOD_DM | FMOD_TF, 2,\t\"\\x41\\x29\\xe1\\xdd\\x04\\xb8\\xb3\\xfb\\xfa\\xf5\\x6c\\x77\\xea\\xf8\\x1d\\xe8\\x63\\x9f\\x42\\xa1\"\t//quake\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\\xd4\\x54\\x57\\x0f\\x5f\\x4c\\xb8\\xa8\\x3a\\x90\\x01\\x00\\x9c\\x28\\xab\\xd3\\x48\\xc8\\x3b\\xa9\"},//mindgrind\n\t{\"sound/items/r_item2.wav\", FMOD_DM | FMOD_TF, 4,\t\"\\x47\\x40\\xda\\xd9\\x1a\\x19\\x7b\\x5a\\x0d\\x86\\x2d\\xc0\\xde\\x79\\xf6\\x18\\x3a\\xd9\\x7b\\xc4\"\t//quake\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\\xd2\\x00\\x2a\\xf6\\xca\\xaa\\xce\\x5f\\x92\\x16\\xf9\\x25\\xb7\\x2c\\x60\\xf7\\x25\\xa5\\x0d\\x23\"\t//us\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\\x46\\xe6\\xa8\\x83\\x13\\xc3\\x4c\\xbc\\x6e\\xa9\\x7a\\xa0\\xda\\xea\\x81\\x9d\\x10\\xbd\\x19\\xf6\"\t//ru\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\\x39\\xde\\xd0\\xd2\\x33\\x8d\\xb7\\x2b\\x81\\x30\\xef\\x09\\xcf\\xba\\x90\\xbe\\xce\\xec\\x71\\xbf\"},//mindgrind\n\t{\"sound/misc/water1.wav\", FMOD_DM | FMOD_TF, 2,\t\t\"\\xc7\\xdb\\x25\\x86\\xe6\\x0a\\xf3\\xba\\x65\\x39\\xb5\\xfd\\xd6\\xb9\\x7e\\x2d\\x04\\x93\\xfd\\xf9\"\t//quake\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\\x18\\x03\\x8a\\x83\\x3e\\xe5\\x05\\x08\\x65\\x40\\xbf\\x6e\\xa0\\xb9\\x5f\\xaa\\x07\\xd8\\xca\\xe1\"},//mindgrind\n\t{\"sound/misc/water2.wav\", FMOD_DM | FMOD_TF, 2,\t\t\"\\xec\\x75\\xda\\xcc\\x80\\xd7\\x5c\\xfb\\x5b\\x3b\\xd7\\xe2\\xad\\x60\\x35\\x9f\\x85\\x6b\\x11\\x4e\"\t//quake\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\\x17\\x6b\\x0a\\xfb\\x33\\xbc\\x5f\\x06\\xc5\\x7c\\xab\\x5f\\xaa\\x01\\x0c\\x11\\x2e\\x61\\x8b\\x27\"},//mindgrind\n\t{\"sound/misc/menu1.wav\", FMOD_DM | FMOD_TF, 2,\t\t\"\\x17\\xb8\\x19\\x2e\\x5f\\x1c\\x0e\\x0c\\xf8\\xec\\xa0\\xd7\\x7e\\xc2\\x78\\xb2\\x3c\\x92\\xe1\\xb0\"\t//quake\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\\x73\\x63\\x3f\\xf5\\x3c\\x61\\x35\\xd6\\xd4\\x41\\x67\\x9f\\x05\\xc2\\x5c\\x9d\\x25\\xc4\\x15\\x96\"},//sean\n\t{\"sound/misc/menu2.wav\", FMOD_DM | FMOD_TF, 2,\t\t\"\\xb1\\x9a\\x71\\xd6\\x2b\\x7e\\x40\\x0a\\x3b\\x05\\x3a\\xb0\\xcb\\xc2\\x4b\\x2a\\xf3\\x7f\\xcd\\x61\"\t//quake\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\\xeb\\x31\\x56\\xca\\x89\\x71\\x57\\x7b\\x4e\\xb1\\xfd\\x58\\x90\\x51\\x56\\xc4\\xc4\\x36\\xc9\\x6f\"},//sean\n\t{\"sound/misc/menu3.wav\", FMOD_DM | FMOD_TF, 2,\t\t\"\\x9a\\x0b\\x12\\xae\\x2e\\x7d\\x21\\x3a\\x90\\x09\\x4f\\xc3\\xed\\x32\\x43\\x2d\\x76\\x8f\\xc1\\x97\"\t//quake\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\\xfe\\xa5\\x0c\\xe6\\x49\\xf9\\x0c\\x30\\x09\\x5c\\x2f\\xb7\\x70\\x1c\\x6d\\x2b\\x1f\\xff\\x2f\\xd9\"},//sean\n\t{\"sound/misc/talk.wav\", FMOD_DM | FMOD_TF, 2,\t\t\"\\x1c\\x32\\x15\\x5b\\x26\\xd8\\xf2\\x1a\\x9f\\x8e\\x22\\xb0\\x56\\x6f\\xc0\\x49\\x8b\\x5e\\x35\\xa8\"\t//quake\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\\x66\\x60\\x59\\xab\\x9d\\xd4\\xe1\\xc7\\xab\\x9a\\xdf\\x00\\x07\\xfc\\x14\\xbd\\xee\\xae\\xb2\\x4a\"},//sean\n\t{\"sound/misc/basekey.wav\", FMOD_DM | FMOD_TF, 2,\t\"\\xce\\x34\\x61\\x92\\xd3\\x6d\\x80\\x22\\x4a\\x62\\x52\\x19\\xe9\\xf7\\x43\\x8f\\x64\\xfe\\xfd\\xa6\"\t//quake\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\\xd4\\x71\\x98\\xc9\\xef\\x0f\\x0e\\xc6\\xf9\\x4b\\xe6\\x14\\x34\\x39\\xf4\\xba\\x18\\xdf\\x76\\x53\"},//gpl\n\t{\"sound/doors/runeuse.wav\", FMOD_DM | FMOD_TF, 2,\t\"\\x59\\x6d\\x1d\\xf4\\xe8\\x93\\x9b\\xe9\\x25\\xfd\\xed\\x15\\x9c\\x49\\x8a\\x66\\x72\\x25\\xc6\\x1a\"\t//quake\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\\x00\\xb9\\x76\\xc5\\xb3\\x88\\x89\\x8f\\x72\\x0d\\xa1\\x85\\xec\\x42\\x31\\x4f\\xba\\xe3\\x91\\xac\"},//sean\n\t{\"gfx/colormap.lmp\", FMOD_DM | FMOD_TF, 1,\t\t\t\"\\x95\\x37\\x3b\\xa9\\x97\\x63\\x01\\x17\\x38\\x1d\\x76\\x0a\\x7d\\xe6\\x66\\x48\\x75\\x2a\\x2a\\x3c\"},//quake\n\t{\"gfx/palette.lmp\", FMOD_DM | FMOD_TF, 1,\t\t\t\"\\x42\\xe2\\xa2\\xa6\\xda\\xf7\\xd0\\xba\\x1f\\x35\\x63\\xe2\\xad\\xf8\\x51\\x6d\\x4a\\x5d\\xa4\\x49\"},//quake\n};\nstatic enum {\n\tFMOD_UNCHECKED,\n\tFMOD_MODIFIED,\n\tFMOD_UNMODIFIED,\n} modifiles_status[countof(modifiles)];\nstatic qboolean modified_neednotify;\nstatic double modified_timestamp; //so we don't spam reanouncements\nstatic void Validation_FilesModified (void)\n{\n\tsize_t i;\n\tunsigned int flagmatch = cl.teamfortress?FMOD_TF:FMOD_DM;\n\tchar buf[512];\n\tqboolean evilhaxxor = false;\n\n\tmodified_timestamp = 0;\n\tmodified_neednotify = true;\n\tQ_strncpyz(buf, \"modified:\", sizeof(buf));\n\tfor (i = 0; i < countof(modifiles); i++)\n\t{\n\t\tif ((modifiles[i].flags & flagmatch) && modifiles_status[i] == FMOD_MODIFIED)\n\t\t{\n\t\t\tevilhaxxor = true;\n\t\t\tif (strlen(buf)>240)\n\t\t\t{\n\t\t\t\tmodified_neednotify = false;//just spam at this point\n\t\t\t\tQ_strncatz(buf, \" & more...\", sizeof(buf));\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tQ_strncatz(buf, \" \", sizeof(buf));\n\t\t\t\tQ_strncatz(buf, COM_SkipPath(modifiles[i].name), sizeof(buf));\n\t\t\t}\n\t\t}\n\t}\n\tif (!evilhaxxor)\n\t\tQ_strncpyz(buf, \"all models okay\", sizeof(buf));\n\n\tCL_SendClientCommand(true, \"say %s\", buf);\n}\nstatic qboolean Validation_IsModified(void)\n{\n\tunsigned int flagmatch = cl.teamfortress?FMOD_TF:FMOD_DM;\n\tsize_t i;\n\tfor (i = 0; i < countof(modifiles); i++)\n\t{\n\t\tif ((modifiles[i].flags & flagmatch) && modifiles_status[i] == FMOD_MODIFIED)\n\t\t\treturn true;\n\t}\n\tmodified_timestamp = 0;\n\tmodified_neednotify = true;\n\treturn false;\n}\nstatic void Validation_WarnModified(void *ctx, void *data, size_t a, size_t b)\n{\n\tif (modified_neednotify)\n\t{\n\t\tif (modified_timestamp && modified_timestamp > realtime)\n\t\t\treturn;\n\t\tCL_SendClientCommand(true, \"say warning: stuff changed! Previous f_modified response is no longer valid.\");\n\t\tmodified_timestamp = realtime+3.0;\t//mute further anouncements for a while.\n\t}\n}\n#endif\nqboolean Ruleset_FileLoaded(const char *filename, const qbyte *filedata, size_t filesize)\n{\t//usually called on worker threads\n\tqbyte digest[20];\n\tsize_t i, j;\n\n\tif (ruleset_current && ruleset_current->filehashes)\n\t{\n\t\tsize_t filehashes = ruleset_current->filehashes;\n\t\trulesetfilehashes_t *filehash = ruleset_current->filehash;\n\t\tfor (i = 0; i < filehashes; i++, filehash++)\n\t\t{\n\t\t\tif (!strcmp(filename, filehash->filename))\n\t\t\t{\n\t\t\t\tCalcHash(filehash->hashfunc, digest, sizeof(digest), filedata, filesize);\n\n\t\t\t\tfor (j = 0; j < filehash->numhashes; j++)\n\t\t\t\t{\n\t\t\t\t\tif (!memcmp(digest, filehash->hash+j*filehash->hashfunc->digestsize, filehash->hashfunc->digestsize))\n\t\t\t\t\t\treturn true;\t//it matched one of the allowed hashes...\n\t\t\t\t}\n\t\t\t\t{\n\t\t\t\t\tchar base16[512+1];\n\t\t\t\t\t//none of the hashes matched. bail. refuse usage of the file.\n\t\t\t\t\tbase16[Base16_EncodeBlock(digest, filehash->hashfunc->digestsize, base16, sizeof(base16)-1)] = 0;\n\t\t\t\t\tCon_Printf(CON_ERROR\"ERROR: File version \\\"%s\\\" \\\"%s\\\" is not permitted by ruleset %s\\n\", filename, base16, ruleset_current->rulesetname);\n\t\t\t\t}\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t}\n#ifdef HAVE_LEGACY\n\telse for (i = 0; i < countof(modifiles); i++)\n\t{\n\t\tif (!strcmp(filename, modifiles[i].name) && (modifiles[i].flags & (cl.teamfortress?FMOD_TF:FMOD_DM)))\n\t\t{\n\t\t\tunsigned int status;\n\t\t\tCalcHash(&hash_sha1, digest, sizeof(digest), filedata, filesize);\n\n\t\t\tfor (j = 0; j < modifiles[i].hashes; j++)\n\t\t\t{\n\t\t\t\tif (!memcmp(digest, modifiles[i].hash+j*20, 20))\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tstatus = (j==modifiles[i].hashes)?FMOD_MODIFIED:FMOD_UNMODIFIED;\n\t\t\tif (status == FMOD_MODIFIED && modifiles_status[i] == FMOD_UNMODIFIED)\n\t\t\t\tCOM_AddWork(WG_MAIN, Validation_WarnModified, NULL, NULL, 0, 0); //make sure its the main thread that does the anouncing.\n\t\t\tmodifiles_status[i] = status;\n\t\t\treturn true;\n\t\t}\n\t}\n#endif\n\treturn true;\t//nothing restricting it.\n}\nvoid Validation_FlushFileList(void)\n{\n#ifdef HAVE_LEGACY\n\t//just wipe the lot, resetting it back to 'unchecked'\n\tmemset(modifiles_status, 0, sizeof(modifiles_status));\n\tmodified_neednotify = false;\n\tmodified_timestamp = 0;\n#endif\n}\n\n/////////////////////////\n//minor (codewise) responses\n\nstatic void Validation_Server(void)\n{\n\tchar adr[MAX_ADR_SIZE];\n\n#ifdef warningmsg\n#pragma warningmsg(\"is allowing the user to turn this off practical?..\")\n#endif\n\tif (!allow_f_server.ival)\n\t\treturn;\n\tCbuf_AddText(va(\"say server is %s\\n\", NET_AdrToString(adr, sizeof(adr), &cls.netchan.remote_address)), RESTRICT_LOCAL);\n}\n\nstatic void Validation_Skins(void)\n{\n\textern cvar_t r_fullbrightSkins, r_fb_models, ruleset_allow_fbmodels;\n\tint percent = r_fullbrightSkins.value*100;\n\n\tif (!allow_f_skins.ival)\n\t\treturn;\n\n\tRulesetLatch(&r_fb_models);\n\tRulesetLatch(&r_fullbrightSkins);\n\n\tif (percent < 0)\n\t\tpercent = 0;\n\tif (percent > cls.allow_fbskins*100)\n\t\tpercent = cls.allow_fbskins*100;\n\tif (percent)\n\t\tCbuf_AddText(va(\"say all player skins %i%% fullbright%s\\n\", percent, (r_fb_models.ival == 1 && ruleset_allow_fbmodels.ival)?\" (non-player 100%%)\":(r_fb_models.value?\" (plus luma)\":\"\")), RESTRICT_LOCAL);\n\telse if (r_fb_models.ival == 1 && ruleset_allow_fbmodels.ival)\n\t\tCbuf_AddText(\"say non-player entities glow in the dark like a bright big cheat\\n\", RESTRICT_LOCAL);\n\telse if (r_fb_models.ival)\n\t\tCbuf_AddText(\"say luma textures only\\n\", RESTRICT_LOCAL);\n\telse\n\t\tCbuf_AddText(\"say Only cheaters use full bright skins\\n\", RESTRICT_LOCAL);\n}\n\nstatic void Validation_Scripts(void)\n{\t//subset of ruleset\n\tif (!allow_f_scripts.ival)\n\t\treturn;\n\tif (ruleset_allow_frj.ival)\n\t\tCbuf_AddText(\"say scripts are allowed\\n\", RESTRICT_LOCAL);\n\telse\n\t\tCbuf_AddText(\"say scripts are capped\\n\", RESTRICT_LOCAL);\n}\n\n#ifdef HAVE_LEGACY\nstatic void Validation_FakeShaft(void)\n{\n\textern cvar_t cl_truelightning;\n\tif (!allow_f_fakeshaft.ival)\n\t\treturn;\n\tif (cl_truelightning.value > 0.999)\n\t\tCbuf_AddText(\"say fakeshaft on\\n\", RESTRICT_LOCAL);\n\telse if (cl_truelightning.value > 0)\n\t\tCbuf_AddText(va(\"say fakeshaft %.1f%%\\n\", cl_truelightning.value), RESTRICT_LOCAL);\n\telse\n\t\tCbuf_AddText(\"say fakeshaft off\\n\", RESTRICT_LOCAL);\n}\n#endif\n\nstatic void Validation_System(void)\n{\t//subset of ruleset\n\tif (!allow_f_system.ival)\n\t\treturn;\n\tCbuf_AddText(\"say f_system not supported\\n\", RESTRICT_LOCAL);\n}\n\nstatic void Validation_CmdLine(void)\n{\n\tif (!allow_f_cmdline.ival)\n\t\treturn;\n\tCbuf_AddText(\"say f_cmdline not supported\\n\", RESTRICT_LOCAL);\n}\n\n//////////////////////\n//rulesets\n\n/*when a ruleset is activated, we flag its various cvars are applicable to rulesets, and notify the servers whenever they're changed after having been queried.\nchanging a cvar to something and then back MUST still warn, in case its a cvar that locks down a command before being switched back.\n\nwe used to latch cvars so they couldn't be changed after querying, but this results in potential race conditions with other reasons to latch cvars. changing a ruleset cvar is now equivelent to just changing ruleset midgame.\n*/\n\nstatic ruleset_t *Ruleset_Find(const char *name)\t//finds a ruleset by name\n{\n\truleset_t *rs;\n\tfor (rs = ruleset_list; rs; rs = rs->next)\n\t{\n\t\tif (!Q_strcasecmp(name, rs->rulesetname))\n\t\t\tbreak;\n\t}\n\treturn rs;\n}\n\nstatic int QDECL Ruleset_SortRuleCB (const void *v1, const void *v2)\n{\n\tconst rulesetrule_t *r1 = v1;\n\tconst rulesetrule_t *r2 = v2;\n\tint r = Q_strcmp(r1->rulename, r2->rulename);\n\tif (!r)\n\t{\n\t\tr = Q_strcmp(r1->rulecond, r2->rulecond);\n\t\tif (!r)\n\t\t\tr = Q_strcmp(r1->rulevalue, r2->rulevalue);\n\t}\n\treturn r;\n}\nstatic int QDECL Ruleset_SortFileCB (const void *v1, const void *v2)\n{\n\tconst rulesetfilehashes_t *r1 = v1;\n\tconst rulesetfilehashes_t *r2 = v2;\n\treturn Q_strcmp(r1->filename, r2->filename);\n}\n\nstatic ruleset_t *Ruleset_Parse(const char *name, char *file)\n{\n\truleset_t *rs = Z_Malloc(sizeof(*rs) + strlen(name)+1);\n\tchar *line = file, *linestart, *eol;\t//evil non-const...\n\tunsigned r, hashidx=~0, h;\n\thashfunc_t *hashfunc = &hash_sha1;\n\tvoid *hctx;\n\tchar fname[MAX_QPATH];\n\n\t*fname = 0;\n\trs->rulesetname = (char*)(rs+1);\n\tstrcpy(rs->rulesetname, name);\n\n\tfor (line = file; line; )\n\t{\n\t\teol = strchr(line, '\\n');\n\t\tif (eol)\n\t\t\t*eol = 0;\n\n\t\tif (*line)\n\t\t{\n\t\t\tlinestart = line;\n\t\t\tline = COM_Parse(line);\n\t\t\tif (!strcmp(com_token, \"set\"))\n\t\t\t{\n\t\t\t\tchar *var, *val;\n\t\t\t\tline = COM_Parse(line);\n\t\t\t\tvar = Z_StrDup(com_token);\n\t\t\t\tline = COM_Parse(line);\n\t\t\t\tval = Z_StrDup(com_token);\n\n\t\t\t\tr = rs->rules;\n\t\t\t\tZ_ReallocElements((void**)&rs->rule, &rs->rules, r+1, sizeof(*rs->rule));\n\t\t\t\trs->rule[r].rulecond = NULL;\n\t\t\t\trs->rule[r].rulename = var;\n\t\t\t\trs->rule[r].rulevalue = val;\n\n\t\t\t\thashidx = ~0;\n\t\t\t}\n\t\t\telse if (!strcmp(com_token, \"if\"))\n\t\t\t{\n\t\t\t\tchar *var, *val, *cond = NULL;\n\t\t\t\tline = COM_Parse(line);\n\t\t\t\tif (*com_token)\n\t\t\t\t\tZ_StrCat(&cond, com_token);\n\t\t\t\tline = COM_Parse(line);\n\t\t\t\tif (*com_token == '>' || *com_token == '<' || *com_token == '=' || *com_token == '!')\n\t\t\t\t{\n\t\t\t\t\tif (*com_token)\n\t\t\t\t\t\tZ_StrCat(&cond, com_token);\n\t\t\t\t\tline = COM_Parse(line);\n\t\t\t\t\tif (*com_token)\n\t\t\t\t\t\tZ_StrCat(&cond, com_token);\n\t\t\t\t\tline = COM_Parse(line);\n\t\t\t\t}\n\t\t\t\tvar = Z_StrDup(com_token);\n\t\t\t\tline = COM_Parse(line);\n\t\t\t\tval = Z_StrDup(com_token);\n\n\t\t\t\tr = rs->rules;\n\t\t\t\tZ_ReallocElements((void**)&rs->rule, &rs->rules, r+1, sizeof(*rs->rule));\n\t\t\t\trs->rule[r].rulecond = cond;\n\t\t\t\trs->rule[r].rulename = var;\n\t\t\t\trs->rule[r].rulevalue = val;\n\n\t\t\t\thashidx = ~0;\n\t\t\t}\n\t\t\t/*else if (!strcmp(com_token, \"alias\"))\n\t\t\t{\n\t\t\t\tline = COM_Parse(line);\n\t\t\t\t//alias = Z_StrDup(com_token);\n\t\t\t}*/\n\t\t\telse if (!strcmp(com_token, \"sha1file\"))\n\t\t\t{\n\t\t\t\thashfunc = &hash_sha1;\n\t\t\t\tline = COM_ParseOut(line, fname, sizeof(fname));\n\t\t\t\tline = COM_Parse(line);\n\n\t\t\t\tfor (hashidx = 0; hashidx < rs->filehashes; hashidx++)\n\t\t\t\t{\n\t\t\t\t\tif (!strcmp(rs->filehash[hashidx].filename, fname) && hashfunc == rs->filehash[hashidx].hashfunc)\n\t\t\t\t\t\tgoto extrafilehash;\n\t\t\t\t}\n\n\t\t\t\tZ_ReallocElements((void**)&rs->filehash, &rs->filehashes, hashidx+1, sizeof(*rs->filehash));\n\t\t\t\trs->filehash[hashidx].filename = Z_StrDup(fname);\n\t\t\t\trs->filehash[hashidx].numhashes = 0;\n\t\t\t\trs->filehash[hashidx].hash = NULL;\n\t\t\t\trs->filehash[hashidx].hashfunc = hashfunc;\nextrafilehash:\n\t\t\t\tif (!*com_token)\n\t\t\t\t\t;\n\t\t\t\telse if (strlen(com_token) != hashfunc->digestsize*2)\n\t\t\t\t\tCon_Printf(\"Ruleset %s file %s is of incorrect length.\\n\", name, fname);\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\th = rs->filehash[hashidx].numhashes++;\n\t\t\t\t\trs->filehash[hashidx].hash = BZ_Realloc(rs->filehash[hashidx].hash, rs->filehash[hashidx].numhashes*hashfunc->digestsize);\n\t\t\t\t\tif (hashfunc->digestsize != Base16_DecodeBlock(com_token, rs->filehash[hashidx].hash+h*hashfunc->digestsize, hashfunc->digestsize))\n\t\t\t\t\t\tCon_Printf(\"Ruleset %s file %s is not base16.\\n\", name, fname);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (!strcmp(com_token, \"+\"))\n\t\t\t{\n\t\t\t\tline = COM_Parse(line);\n\t\t\t\tif (hashidx != ~0 && hashfunc)\n\t\t\t\t\tgoto extrafilehash;\n\t\t\t}\n\t\t\telse if (*com_token)\n\t\t\t{\n\t\t\t\tCon_Printf(\"%s.rules: Unknown directive \\\"%s\\\".\\n\", name, com_token);\n\t\t\t\thashidx = ~0;\n\t\t\t\tline += strlen(line);\n\t\t\t}\t//else blank line\n\n\t\t\tline = COM_Parse(line);\n\t\t\tif (*com_token)\n\t\t\t\tCon_Printf(\"%s.rules: Trailing junk at end of line \\\"%s\\\".\\n\", name, linestart);\n\t\t}\n\n\t\tif (eol)\n\t\t\t*eol++ = '\\n';\n\t\tline = eol;\n\t}\n\n\t//sort it for consistency\n\tqsort(rs->rule, rs->rules, sizeof(*rs->rule), Ruleset_SortRuleCB);\n\tqsort(rs->filehash, rs->filehashes, sizeof(*rs->filehash), Ruleset_SortFileCB);\n\n\thashfunc = &hash_sha1;\n\thctx = alloca(hashfunc->contextsize);\n\thashfunc->init(hctx);\n\tfor (r = 0; r < rs->rules; r++)\n\t{\t//be sure to hash the nulls too. This prevents cheating lamas from using weird cvar names to fake results.\n\t\tchar *rc = rs->rule[r].rulecond;\n\t\tif (!rc) rc = \"\";\n\t\thashfunc->process(hctx, rc, strlen(rc)+1);\n\t\thashfunc->process(hctx, rs->rule[r].rulename, strlen(rs->rule[r].rulename)+1);\n\t\thashfunc->process(hctx, rs->rule[r].rulevalue, strlen(rs->rule[r].rulevalue)+1);\n\t}\n\tfor (r = 0; r < rs->filehashes; r++)\n\t{\t//be sure to hash the nulls too. This prevents cheating lamas from using weird file names to fake results.\n\t\thash_sha1.process(hctx, rs->filehash[r].filename, strlen(rs->filehash[r].filename)+1);\n\t\thash_sha1.process(hctx, rs->filehash[r].hash, rs->filehash[r].numhashes*rs->filehash[r].hashfunc->digestsize);\t//should this be base16ed first?...\n\t}\n\thashfunc->terminate(rs->hash, hctx);\n\n\trs->next = ruleset_list;\n\truleset_list = rs;\n\n\treturn rs;\n}\n#ifdef HAVE_LEGACY\nstatic ruleset_t *Ruleset_ParseInternal(const char *name, const char *file)\n{\n\truleset_t *rs;\n\tchar *lazy;\n\tlazy = Z_StrDup(file);\n\trs = Ruleset_Parse(name, lazy);\n\tZ_Free(lazy);\n\treturn rs;\n}\n#endif\n\nstatic int QDECL Ruleset_Read(const char *fname, qofs_t size, time_t mtime, void *parm, searchpathfuncs_t *spath)\n{\n\truleset_t *rs;\n\tsize_t fsize;\n\tchar name[128];\n\tchar *file = FS_LoadMallocFile(fname, &fsize);\n\tif (file && fsize == size)\t//o.O\n\t{\n\t\tCOM_StripExtension(fname, name, sizeof(name));\n\t\trs = Ruleset_Parse(name, file);\n\t\tif (rs)\n\t\t\trs->external = true;\n\t}\n\tZ_Free(file);\n\treturn true;\n}\nvoid Ruleset_Shutdown(void)\n{\n\truleset_current = NULL;\n\twhile (ruleset_list)\n\t{\n\t\truleset_t *rs = ruleset_list;\n\t\truleset_list = rs->next;\n\n\t\twhile (rs->rules)\n\t\t{\n\t\t\trs->rules--;\n\t\t\tZ_Free(rs->rule[rs->rules].rulename);\n\t\t\tZ_Free(rs->rule[rs->rules].rulevalue);\n\t\t\tZ_Free(rs->rule[rs->rules].rulecond);\n\t\t}\n\t\tZ_Free(rs->rule);\n\n\t\twhile (rs->filehashes)\n\t\t{\n\t\t\trs->filehashes--;\n\t\t\tZ_Free(rs->filehash[rs->filehashes].filename);\n\t\t\tZ_Free(rs->filehash[rs->filehashes].hash);\n\t\t}\n\t\tZ_Free(rs->filehash);\n\n\t\tZ_Free(rs);\n\t}\n}\nvoid Ruleset_Scan(void)\n{\n\tCOM_EnumerateFiles(\"*.rules\", Ruleset_Read, NULL);\n\n#ifdef HAVE_LEGACY\t//TCs should probably include their own.\n\tRuleset_ParseInternal(\"strict\",\n//\t\t\t\"alias smackdown\\n\"\n//\t\t\t\"alias qcon\\n\"\n\t\t\t\"set ruleset_allow_shaders 0\\n\"\t/*users can potentially create all sorts of wallhacks or spiked models with this*/\n\t\t\t\"set ruleset_allow_watervis 0\\n\" /*oh noes! users might be able to see underwater if they're already in said water. oh wait. what? why do we care, dude*/\n\t\t\t\"set r_vertexlight 0\\n\"\n\t\t\t\"set ruleset_allow_playercount 0\\n\"\n\t\t\t\"set ruleset_allow_frj 0\\n\"\n\t\t\t\"set ruleset_allow_packet 0\\n\"\n\t\t\t\"set ruleset_allow_particle_lightning 0\\n\"\n\t\t\t\"set ruleset_allow_overlong_sounds 0\\n\"\n\t\t\t\"set ruleset_allow_larger_models 0\\n\"\n\t\t\t\"set ruleset_allow_modified_eyes 0\\n\"\n\t\t\t\"set ruleset_allow_sensitive_texture_replacements 0\\n\"\n\t\t\t\"set ruleset_allow_localvolume 0\\n\"\n\t\t\t\"set ruleset_allow_fbmodels 0\\n\"\n\t\t\t\"set ruleset_allow_triggers 0\\n\"\n\t\t\t\"set r_particlesystem classic\\n\"\t/*block custom particles*/\n\t\t\t\"set r_part_density 1\\n\"\t/*don't let people thin them out*/\n\t\t\t\"set scr_autoid_team 0\\n\"\t/*sort of a wallhack*/\n\t\t\t\"set tp_disputablemacros 0\\n\"\n\t\t\t\"set cl_instantrotate 0\\n\"\n\t\t\t\"set v_projectionmode 0\\n\"\t/*no extended fovs*/\n\t\t\t\"set r_shadow_realtime_world 0\\n\" /*static lighting can be used to cast shadows around corners*/\n\t\t\t\"set ruleset_allow_in 0\\n\"\n\t\t\t\"set r_projection 0\\n\"\n\t\t\t\"set gl_shadeq1_name *\\n\"\n\t\t\t\"set cl_rollalpha 20\\n\"\n\t\t\t\"set cl_iDrive 0\\n\"\n\t\t\t);\n\n\tRuleset_ParseInternal(\"thunderdome\",\n\t\t\t\"set ruleset_allow_shaders 0\\n\"\t/*users can potentially create all sorts of wallhacks or spiked models with this*/\n\t\t\t\"set ruleset_allow_watervis 0\\n\" /*oh noes! users might be able to see underwater if they're already in said water. oh wait. what? why do we care, dude*/\n\t\t\t\"set r_vertexlight 0\\n\"\n\t\t\t\"set ruleset_allow_playercount 0\\n\"\n\t\t\t\"set ruleset_allow_frj 0\\n\"\n\t\t\t\"set ruleset_allow_packet 0\\n\"\n\t\t\t\"set ruleset_allow_particle_lightning 0\\n\"\n\t\t\t\"set ruleset_allow_overlong_sounds 0\\n\"\n\t\t\t\"set ruleset_allow_larger_models 0\\n\"\n\t\t\t\"set ruleset_allow_modified_eyes 0\\n\"\n\t\t\t\"set ruleset_allow_sensitive_texture_replacements 0\\n\"\n\t\t\t\"set ruleset_allow_localvolume 0\\n\"\n\t\t\t\"set ruleset_allow_fbmodels 0\\n\"\n\t\t\t\"set ruleset_allow_triggers 0\\n\"\n\t\t\t\"set scr_autoid_team 0\\n\"\n\t\t\t\"set tp_disputablemacros 0\\n\"\n\t\t\t\"set cl_instantrotate 0\\n\"\n\t\t\t\"set v_projectionmode 0\\n\"\t/*no extended fovs*/\n\t\t\t\"set r_shadow_realtime_world 0\\n\" /*static lighting can be used to cast shadows around corners*/\n\t\t\t\"set ruleset_allow_in 0\\n\"\n\t\t\t\"set r_projection 0\\n\"\n\t\t\t\"set gl_shadeq1_name *\\n\"\n\t\t//\t\"set cl_rollalpha 20\\n\"\n\t\t\t\"set cl_iDrive 0\\n\"\n\t\t\t);\n\n\tRuleset_ParseInternal(\"nqr\",\n//\t\t\t\"alias eql\\n\"\n\t\t\t\"set ruleset_allow_larger_models 0\\n\"\n\t\t\t\"set ruleset_allow_watervis 0\\n\" /*block seeing through turbs, as well as all our cool graphics stuff. apparently we're not allowed.*/\n\t\t\t\"set ruleset_allow_overlong_sounds 0\\n\"\n\t\t\t\"set ruleset_allow_particle_lightning 0\\n\"\n\t\t\t\"set ruleset_allow_packet 0\\n\"\n\t\t\t\"set ruleset_allow_frj 0\\n\"\n\t\t\t\"set ruleset_allow_modified_eyes 0\\n\"\n\t\t\t\"set ruleset_allow_sensitive_texture_replacements 0\\n\"\n\t\t\t\"set ruleset_allow_localvolume 0\\n\"\n\t\t\t\"set ruleset_allow_shaders 0\\n\"\n\t\t\t\"set ruleset_allow_fbmodels 0\\n\"\n\t\t\t\"set r_vertexlight 0\\n\"\n\t\t\t\"set v_projectionmode 0\\n\"\n\t\t\t\"set sbar_teamstatus 0\\n\"\n\t\t\t\"set ruleset_allow_in 0\\n\"\n\t\t\t\"set r_projection 0\\n\"\n\t\t\t\"set gl_shadeq1_name *\\n\"\n\t\t\t\"set cl_iDrive 0\\n\"\n\t\t\t);\n#endif\n}\n\nstatic void RulesetLatch(cvar_t *cvar)\n{\n\tcvar->flags |= CVAR_RULESETLATCH;\n}\n\nvoid Validation_DelatchRulesets(void)\n{\t//game has come to an end, allow the ruleset to be changed\n\tif (Cvar_ApplyLatches(CVAR_RULESETLATCH, true))\n\t\tCon_DPrintf(\"Ruleset deactivated\\n\");\n}\n\nconst char *Ruleset_GetRulesetName(void)\n{\n\tif (ruleset_current)\n\t\treturn ruleset_current->rulesetname;\n\telse\n\t\treturn NULL;\n}\n\nstatic void Validation_OldRuleset(void)\n{\n\tconst char *rsname = Ruleset_GetRulesetName();\n\n\tif (rsname)\n\t\tCbuf_AddText(va(\"say Ruleset: %s\\n\", rsname), RESTRICT_LOCAL);\n\telse\n\t\tCbuf_AddText(\"say No specific ruleset\\n\", RESTRICT_LOCAL);\n}\n\nstatic void Validation_AllChecks(void)\n{\n\textern cvar_t cl_iDrive;\n\tchar servername[22];\n\tchar playername[16];\n\tchar *rawenginebuild = version_string();\n\tchar enginebuild[64];\n\tchar localpnamelen = strlen(cl.players[cl.playerview[0].playernum].name);\n\tconst char *ruleset;\n\tchar extras[2][8];\n\tsize_t i, j;\n\tqboolean hadspace;\n\n\t//figure out the padding for the player's name.\n\tif (localpnamelen >= 15)\n\t\tplayername[0] = 0;\n\telse\n\t{\n\t\t//pad the left side to compensate for the player name prefix the server will add in the final svc_print\n\t\tmemset(playername, ' ', 15-localpnamelen);\n\t\tplayername[15-localpnamelen] = 0;\n\t}\n\n\tfor (i = 0, j = 0, hadspace=false; rawenginebuild[i]&& j+1<countof(enginebuild); i++)\n\t{\n\t\tif (rawenginebuild[i] == ' ')\n\t\t{\n\t\t\tif (!strncmp(rawenginebuild+i, \" SVN \", 5))\n\t\t\t{\n\t\t\t\ti+=5-1;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (hadspace && !(isdigit(rawenginebuild[i-1])&&isdigit(rawenginebuild[i+1])))\n\t\t\t\tcontinue;\n\t\t\thadspace=true;\n\t\t\tenginebuild[j++] = '_';\n\t\t}\n\t\telse\n\t\t\tenginebuild[j++] = rawenginebuild[i];\n\t}\n\tenginebuild[j++] = 0;\n\n\t//get the current server address\n\tNET_AdrToString(servername, sizeof(servername), &cls.netchan.remote_address);\n\n\t//get the ruleset names\n\truleset = Ruleset_GetRulesetName();\n\tif (!ruleset)\n\t\truleset = \"default\";\n\n\textras[0][0] = '-';\t//extra restrictions.\n\textras[1][0] = '+';\t//extra cheats.\n\textras[0][1] = extras[1][1] = 0;\n#ifdef HAVE_LEGACY\n\tQ_strncatz(extras[Validation_IsModified()], \"m\", sizeof(extras[0]));\n#endif\n\tQ_strncatz(extras[1/*!!allow_scripts.ival*/], \"s\", sizeof(extras[0]));\t\t//we can't track whether a +foo was from a bind or an alias invoked from a bind, so we have to list it under the +, for now.\n\tQ_strncatz(extras[0/*!!enemyforceskins.ival*/], \"f\", sizeof(extras[0]));\t//we don't support per-player skin forcing, so this should always be under the -.\n\tQ_strncatz(extras[!!cl_iDrive.ival], \"i\", sizeof(extras[0]));\n\tif (!extras[0][1])\n\t\t*extras[0] = 0;\n\tif (!extras[1][1])\n\t\t*extras[1] = 0;\n\n\t//now send it\n\tCL_SendClientCommand(true, \"say \\\"%s%21s \" \"%16s %s\" /*FIXME*/\"_dbg\" \"%s%s\\\"\", playername, servername, enginebuild, ruleset, extras[1], extras[0]);\n}\n\nvoid Ruleset_Check(char *keyval, char *out, size_t outsize)\n{\n\tsize_t l;\n\tchar *status;\n\tchar *b64;\n\truleset_t *rs;\n\n\tif (strchr(keyval, '^'))\t//don't let people corrupt scoreboards...\n\t\tkeyval = \"?\";\n\n\tb64 = strchr(keyval, ':');\n\tif (!b64 || b64==keyval)\n\t{\t//can't validate it. don't bother showing it.\n\t\trs = Ruleset_Find(keyval);\n\t\tif (!rs || rs->external)\n\t\t{\n\t\t\tstatus = S_COLOR_RED;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t//valid internal name... no hash so we can't validate it, but the only way someone can generate this is if they mod the engine, so its as valid as its going to get, so consider it okay.\n\t\t\tstatus = S_COLOR_GREEN;\n\t\t\tkeyval = rs->rulesetname;\n\t\t}\n\t}\n\telse\n\t{\n\t\tchar digest[20];\n\t\t*b64++ = 0;\n\t\tBase64_DecodeBlock(b64, NULL, digest, sizeof(digest));\n\n\t\tfor (rs = ruleset_list; rs; rs = rs->next)\n\t\t\tif (rs->external && !memcmp(rs->hash, digest, sizeof(digest)))\n\t\t\t\tbreak;\n\n\t\tif (rs)\n\t\t{\n\t\t\tstatus = S_COLOR_GREEN;\n\t\t\tkeyval = rs->rulesetname;\t//use our local name for it\n\t\t}\n\t\telse status = S_COLOR_RED;\t//they changed their ruleset file, or they're trying to spoof an internal name...\n\t}\n\n\tif (!*keyval)\n\t\tkeyval = \"default\";\n\n\tif (*keyval)\n\t{\n\t\tl = strlen(status);\n\t\tif (outsize > l)\n\t\t{\n\t\t\tmemcpy(out, status, l);\n\t\t\tout += l;\n\t\t\toutsize -= l;\n\t\t}\n\t\tl = strlen(keyval);\n\t\tif (outsize > l)\n\t\t{\n\t\t\tmemcpy(out, keyval, l);\n\t\t\tout += l;\n\t\t\toutsize -= l;\n\t\t}\n\t}\n\t*out = 0;\n}\n\nstatic int Ruleset_CheckRuleConditionIsOkay(char *cond)\t//-1 on error\n{\n\tif (cond)\n\t{\n\t\tenum ct_e {CT_EQUAL, CT_UNEQUAL, CT_GEQUAL, CT_LESS, CT_LEQUAL, CT_GREATER, CT_INVALID1, CT_INVALID2} checktype;\n\t\tchar *key, *value, c;\n\t\tqboolean not = (*cond=='!');\n\t\tint truth = 1;\n\t\tif (not)\n\t\t\tcond++;\n\n\t\tkey = cond;\n\t\twhile (*cond=='$'||isalnum(*cond))\n\t\t\tcond++;\n\t\tc = *cond;\n\t\tif (c == 0)\n\t\t{\n\t\t\tchecktype = CT_UNEQUAL;\n\t\t\tvalue = \"\";\n\t\t}\n\t\telse\n\t\t{\n\t\t\t*cond = 0;\n\t\t\tvalue = cond+1;\n\t\t\tif (c == '>')\n\t\t\t{\n\t\t\t\tif (cond[1] == '=')\n\t\t\t\t\tvalue++, checktype = CT_GEQUAL;\n\t\t\t\telse\n\t\t\t\t\tchecktype = CT_GREATER;\n\t\t\t}\n\t\t\telse if (c == '<')\n\t\t\t{\n\t\t\t\tif (cond[1] == '=')\n\t\t\t\t\tvalue++, checktype = CT_LEQUAL;\n\t\t\t\telse\n\t\t\t\t\tchecktype = CT_LESS;\n\t\t\t}\n\t\t\telse if (c == '=')\n\t\t\t{\n\t\t\t\tif (cond[1] == '=')\n\t\t\t\t\tvalue++;\t//allow = or ==\n\t\t\t\tchecktype = CT_EQUAL;\n\t\t\t}\n\t\t\telse if (c == '!')\n\t\t\t{\n\t\t\t\tif (cond[1] == '=')\n\t\t\t\t\tvalue++, checktype = CT_EQUAL;\n\t\t\t\telse\n\t\t\t\t\tkey=\"\", value=\"\", checktype = CT_INVALID1;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tCon_Printf (\"Unknown comparison type\\n\");\n\t\t\t\tkey=\"\", value=\"\", checktype = CT_INVALID1;\n\t\t\t}\n\t\t}\n\t\tif (not)\n\t\t\tchecktype ^= 1;\n\n#ifndef SVNREVISION\n#define SVNREVISION -\n#endif\n#define SVNREVISIONSTR STRINGIFY(SVNREVISION)\n//\t\tif (!strcmp(key, \"$GAME\"))\t//FIXME: need to reload rulesets on gamedir changes for that, which we can't reliably do.\n//\t\t\tkey = FS_GetGamedir(true);\n\t\tif (!strcmp(key, \"$FTEQW\"))\n\t\t\tkey = SVNREVISIONSTR;\t//\"-\" is smaller than 0, but not empty. yay for private builds.\n\t\tif (!strcmp(key, \"$QW\"))\n\t\t\tkey = \"2.40\";\t//we're originally derived from the 2.40 source release.\n\t\tif (*key == '$')\n\t\t\tkey = \"\";\t//don't know what this is. some engine-specific key for another engine? treat it as false.\n\t\tswitch(checktype)\n\t\t{\n\t\tcase CT_EQUAL:\ttruth =  ! strcmp(key, value);\tbreak;\n\t\tcase CT_UNEQUAL:truth = !! strcmp(key, value);\tbreak;\n\t\tcase CT_GEQUAL:\ttruth = 0<=strcmp(key, value);\tbreak;\n\t\tcase CT_GREATER:truth = 0< strcmp(key, value);\tbreak;\n\t\tcase CT_LEQUAL:\ttruth = 0>=strcmp(key, value);\tbreak;\n\t\tcase CT_LESS:\ttruth = 0> strcmp(key, value);\tbreak;\n\t\tcase CT_INVALID1:\n\t\tcase CT_INVALID2:\ttruth = -1;\n\t\t}\n\t\t*cond = c;\n\t\treturn truth;\n\t}\n\treturn true;\n}\n\nvoid Validation_Apply_Ruleset(void)\n{\t//rulesets are applied when the client first gets a connection to the server\n\tchar b64[64];\n\truleset_t *rs = NULL;\n\tint i;\n\tchar *rulesetname = ruleset.string;\n\tcvar_t **vars, *var;\n\tunsigned int latches = 0;\n\tint okay;\n\n\tif (!*rulesetname)\n\t\trulesetname = \"default\";\n\trs = Ruleset_Find(rulesetname);\n\n\tif (ruleset_current == rs)\n\t\treturn;\t//ruleset is already applied. no work needed (don't disconnect!).\n\n\t//the worker can poke the current ruleset for file hash checks. make sure none are active.\n\tCOM_WorkerFullSync();\n\n\tValidation_DelatchRulesets();\n\truleset_current = NULL;\n\tInfoBuf_SetStarKey(&cls.userinfo[0], RULESET_USERINFO, \"\");\n\n\tif (!rs)\n\t{\n\t\tif (strcmp(rulesetname, \"default\") && strcmp(rulesetname, \"none\"))\n\t\t{\n\t\t\tCon_Printf(\"Cannot apply ruleset \\\"%s\\\" - not recognised\\n\", rulesetname);\n\t\t\tif (ruleset_list)\n\t\t\t{\n\t\t\t\tCon_Printf(\"Known rulesets:\\n\");\n\t\t\t\tfor (rs = ruleset_list; rs; rs = rs->next)\n\t\t\t\t\tCon_Printf(\"\\t%s\\n\", rs->rulesetname);\n\t\t\t}\n\t\t}\n\n\t\tCon_DPrintf(\"Ruleset set to %s\\n\", \"default\");\n\t\tif (cls.state && !cls.demoplayback)\n\t\t{\t//changing a ruleset while on-server MUST disconnect(+reconnect) you, to make it obvious you just tried to cheat (wiping any scores).\n\t\t\t//note that this can often happen on initial connection (gamedir changes execing configs from different gamedirs).\n\t\t\tCon_Printf(\"Reconnecting to enforce change to ruleset \\\"%s\\\"\\n\", rulesetname);\n\n#ifdef HAVE_SERVER\n\t\t\tif (sv.state)\n\t\t\t\tCbuf_AddText(\"disconnect;map_restart\\n\", RESTRICT_LOCAL);\n\t\t\telse\n#endif\n\t\t\t\tCbuf_AddText(\"disconnect;reconnect\\n\", RESTRICT_LOCAL);\n\t\t}\n\t\treturn;\n\t}\n\n\truleset_current = NULL;\n\tvars = Z_Malloc(sizeof(*vars)*rs->rules);\n\tfor (i = 0; i < rs->rules; i++)\n\t{\t//make sure we're actually allowed to make these changes.\n\t\tvars[i] = NULL;\n\n\t\tokay = Ruleset_CheckRuleConditionIsOkay(rs->rule[i].rulecond);\n\t\tif (okay < 0)\n\t\t\tbreak;\t//error!\n\t\tif (!okay)\n\t\t\tcontinue;\t//condition is false, this rule does not apply to us.\n\n\t\tif (!strcmp(rs->rule[i].rulename, \"ruleset_unsupported\"))\n\t\t{\t//special pseudo-setting to cause the ruleset to fail entirely, with a reason for failure (eg 'Engine version too old').\n\t\t\tif (*rs->rule[i].rulevalue)\n\t\t\t\tCon_Printf(\"Ruleset %s is unsupported: %s\\n\", rs->rulesetname, rs->rule[i].rulevalue);\n\t\t\telse\n\t\t\t\tCon_Printf(\"Ruleset %s is unsupported\\n\", rs->rulesetname);\n\t\t\tbreak;\n\t\t}\n\n\t\tvar = Cvar_FindVar(rs->rule[i].rulename);\n\t\tif (!var)\n\t\t{\n\t\t\tif (!rs->rule[i].rulecond)\n\t\t\t\tcontinue;\t//doesn't exist... assume it was for some other engine\n\t\t\t//they gave a condition. it doesn't exist... wtf.\n\t\t\tCon_Printf(\"Ruleset %s requires cvar %s\\n\", rs->rulesetname, rs->rule[i].rulename);\n\t\t\tbreak;\n\t\t}\n\t\t//FIXME: should cvars need to be engine-defined? it would break mods...\n\t\t//       oh well, default.cfg should have set/seta commands for any mod cvars to make sure they're set in advance.\n\t\tif (var->flags & CVAR_NOSET)\n\t\t{\n\t\t\tCon_Printf(\"Ruleset %s requires change to read-only cvar %s\\n\", rs->rulesetname, var->name);\n\t\t\tbreak;\n\t\t}\n\n\t\tif ((var->flags & CVAR_NOTFROMSERVER) && Cmd_IsInsecure())\n\t\t{\n\t\t\tCon_Printf (\"Server tried setting %s cvar\\n\", var->name);\n\t\t\tbreak;\n\t\t}\n\n\t\tvars[i] = var;\n\t\tif (strcmp(var->string, rs->rule[i].rulevalue))\n\t\t\tlatches |= var->flags;\n\t}\n\n\tif (i == rs->rules)\n\t{\n\t\t//lock down the cvars.\n\t\tfor (i = 0; i < rs->rules; i++)\n\t\t{\n\t\t\tcvar_t *var = vars[i];\n\t\t\tif (!var)\t//for some other engine\n\t\t\t\tcontinue;\n\n\t\t\tif (!Cvar_ApplyLatchFlag(var, rs->rule[i].rulevalue, CVAR_RULESETLATCH,\n\t\t\t\t\tCVAR_VIDEOLATCH|CVAR_RENDERERLATCH|\t//ignore these, we'll vid_restart as required anyway.\n\t\t\t\t\tCVAR_SERVEROVERRIDE|CVAR_MAPLATCH|\t//we're going to reconnect/restart anyway.\n\t\t\t\t\tCVAR_CHEAT|CVAR_SEMICHEAT))\t\t\t//ignore these too,\n\t\t\t{\n\t\t\t\tCon_Printf(\"Failed to apply ruleset %s due to cvar %s\\n\", rs->rulesetname, var->name);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\truleset_current = rs;\n\t\tBase64_EncodeBlock(rs->hash, sizeof(rs->hash), b64, sizeof(b64));\n\t\tif (rs->external)\t//include a hash, so it can be validated\n\t\t\tInfoBuf_SetStarKey(&cls.userinfo[0], RULESET_USERINFO, va(\"%s:%s\", rulesetname, b64));\n\t\telse\t//internals don't bother including a hash, to reduce issues with old servers/clients...\n\t\t\tInfoBuf_SetStarKey(&cls.userinfo[0], RULESET_USERINFO, rulesetname);\n\n\t\tCon_DPrintf(\"Ruleset set to %s\\n\", rs->rulesetname);\n\n\t\t//force video restart, if required.\n\t\tif (latches & CVAR_VIDEOLATCH)\n\t\t\tCbuf_AddText(\"vid_restart\\n\", RESTRICT_LOCAL);\n\t\telse if (latches & CVAR_RENDERERLATCH)\n\t\t\tCbuf_AddText(\"vid_reload\\n\", RESTRICT_LOCAL);\n\t\telse\n\t\t\tCbuf_AddText(\"flush\\n\", RESTRICT_LOCAL);\t//make sure file hashes take effect.\n\n\t\tif ((cls.state && !cls.demoplayback) || (latches&CVAR_MAPLATCH))\n\t\t{\t//changing a ruleset while on-server MUST disconnect(+reconnect) you, to make it obvious you just tried to cheat (wiping any scores).\n\t\t\t//note that this can often happen on initial connection (gamedir changes execing configs from different gamedirs).\n\t\t\tCon_Printf(\"Reconnecting to enforce change to ruleset \\\"%s\\\"\\n\", rulesetname);\n\n#ifdef HAVE_SERVER\n\t\t\tif (sv.state)\n\t\t\t\tCbuf_AddText(\"disconnect;map_restart\\n\", RESTRICT_LOCAL);\n\t\t\telse\n#endif\n\t\t\t\tCbuf_AddText(\"disconnect;reconnect\\n\", RESTRICT_LOCAL);\n\t\t}\n\t}\n\tZ_Free(vars);\n}\n\n//////////////////////\n\nvoid Validation_Auto_Response(int playernum, char *s)\n{\n\tstatic float versionresponsetime;\n#ifdef HAVE_LEGACY\n\tstatic float modifiedresponsetime;\n\tstatic float fakeshaftresponsetime;\n#endif\n\tstatic float skinsresponsetime;\n\tstatic float serverresponsetime;\n\tstatic float rulesetresponsetime;\n\tstatic float systemresponsetime;\n\tstatic float cmdlineresponsetime;\n\tstatic float scriptsresponsetime;\n\n\tif (cls.demoplayback)\n\t\treturn;\t//noone gives a shit about qtv spectator versions that can't even play without reconnecting.\n\n\t//quakeworld tends to use f_*\n\t//netquake uses the slightly more guessable q_* form\n\tif (!strncmp(s, \"f_\", 2))\n\t\ts+=2;\n\telse if (!strncmp(s, \"q_\", 2))\n\t\ts+=2;\n\telse\n\t\treturn;\n\n\tif (!strncmp(s, \"version\", 7) && versionresponsetime < Sys_DoubleTime())\t//respond to it.\n\t{\n\t\tValidation_Version();\n\t\tversionresponsetime = Sys_DoubleTime() + 5;\n\t}\n\telse if (cl.playerview[0].spectator)\n\t\treturn;\n\telse if (!strncmp(s, \"server\", 6) && serverresponsetime < Sys_DoubleTime())\t//respond to it.\n\t{\n\t\tValidation_Server();\n\t\tserverresponsetime = Sys_DoubleTime() + 5;\n\t}\n\telse if (!strncmp(s, \"system\", 6) && systemresponsetime < Sys_DoubleTime())\n\t{\n\t\tValidation_System();\n\t\tsystemresponsetime = Sys_DoubleTime() + 5;\n\t}\n\telse if (!strncmp(s, \"cmdline\", 7) && cmdlineresponsetime < Sys_DoubleTime())\n\t{\n\t\tValidation_CmdLine();\n\t\tcmdlineresponsetime = Sys_DoubleTime() + 5;\n\t}\n#ifdef HAVE_LEGACY\n\telse if (!strncmp(s, \"fakeshaft\", 9) && fakeshaftresponsetime < Sys_DoubleTime())\n\t{\n\t\tValidation_FakeShaft();\n\t\tfakeshaftresponsetime = Sys_DoubleTime() + 5;\n\t}\n\telse if (!strncmp(s, \"modified\", 8) && modifiedresponsetime < Sys_DoubleTime())\t//respond to it.\n\t{\n\t\tValidation_FilesModified();\n\t\tmodifiedresponsetime = Sys_DoubleTime() + 5;\n\t}\n#endif\n\telse if (!strncmp(s, \"scripts\", 7) && scriptsresponsetime < Sys_DoubleTime())\n\t{\n\t\tValidation_Scripts();\n\t\tscriptsresponsetime = Sys_DoubleTime() + 5;\n\t}\n\telse if (!strncmp(s, \"skins\", 5) && skinsresponsetime < Sys_DoubleTime())\t//respond to it.\n\t{\n\t\tValidation_Skins();\n\t\tskinsresponsetime = Sys_DoubleTime() + 5;\n\t}\n\telse if (!strncmp(s, \"ruleset\", 7) && rulesetresponsetime < Sys_DoubleTime())\n\t{\n\t\tif (1)\n\t\t\tValidation_AllChecks();\n\t\telse\n\t\t\tValidation_OldRuleset();\n\t\trulesetresponsetime = Sys_DoubleTime() + 5;\n\t}\n}\n\n\n"
  },
  {
    "path": "engine/client/vid.h",
    "content": "/*\nCopyright (C) 1996-1997 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  \n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n*/\n// vid.h -- video driver defs\n\n#define VID_CBITS\t6\n#define VID_GRADES\t(1 << VID_CBITS)\n\n// a pixel can be one, two, or four bytes\ntypedef qbyte pixel_t;\n\ntypedef enum\n{\n\tQR_NONE,\t\t//server-style operation (no rendering).\n\tQR_HEADLESS,\t//no window/rendering at all (system tray only)\n\tQR_OPENGL,\t\t//gl+gles+etc.\n\tQR_DIRECT3D9,\t//\n\tQR_DIRECT3D11,\t//\n\tQR_SOFTWARE,\t//not worth using\n\tQR_VULKAN,\t\t//\n\tQR_DIRECT3D12,\t//no implementation\n\tQR_METAL,\t\t//no implementation\n\tQR_DIRECT3D8\t//liimted. available for win95 where d3d8.1+ does not. also only renderer supported on the original xbox, if that's ever relevant.\n} r_qrenderer_t;\n\ntypedef struct {\n\t//you are not allowed to make anything not work if it's not based on these vars...\n\tint width;\n\tint height;\n\tint fullscreen;\t//0 = windowed. 1 = fullscreen (mode changes). 2 = borderless+maximized\n\tqboolean stereo;\n\tint srgb;\t//<0 = gamma-only. 0 = no srgb at all, >0 full srgb, including textures and stuff\n\tint bpp;\t//16, 24(aka 32), 30, and 48 are meaningful\n\tint depthbits;\n\tint rate;\n\tint wait;\t//-1 = default, 0 = off, 1 = on, 2 = every other\n\tint multisample;\t//for opengl antialiasing (which requires context stuff)\n\tint triplebuffer;\n\tchar subrenderer[MAX_QPATH];\t//external driver\n\tchar devicename[MAX_QPATH];\t\t//device name (usually monitor)\n\tstruct rendererinfo_s *renderer;\n\tstruct plugvrfuncs_s *vr;\t\t//the vr driver we're trying to use.\n} rendererstate_t;\n#ifndef SERVERONLY\nextern rendererstate_t currentrendererstate;\n#endif\n\ntypedef struct vrect_s\n{\n\tfloat\t\t\t\tx,y,width,height;\n} vrect_t;\ntypedef struct\n{\n\tint x;\n\tint y;\n\tint width;\n\tint height;\n\tint maxheight;\t//vid.pixelheight or so\n} pxrect_t;\n\n//srgb colourspace displays smoother visual gradients, but its more of an illusion than anything else.\n//we can be in a few different modes:\n//linear - rendering is done using some float framebuffer or so. lighting is more correct. textures are converted from srgb colourspace when reading.\n//non-linear - the lame option where lighting is wrong, but used anyway for compat. final colours will be whatever the screen uses (probably srgb, so artwork looks fine)\n//srgb - like linear, except our framebuffer does extra transforms to hide that its storage is actually non-linear.\n#define VID_SRGBAWARE\t\t\t(1u<<0)\t//we need to convert input srgb values to actual linear values (requires vid_reload to change...)\n#define VID_SRGB_FB_LINEAR\t\t(1u<<1) //framebuffer is linear (either the presentation engine is linear, or the blend unit is faking it)\n#define VID_SRGB_FB_FAKED\t\t(1u<<2) //renderer is faking it with a linear texture\n#define VID_SRGB_CAPABLE\t\t(1u<<3)\t//we can toggle VID_SRGB_FB_LINEARISED on or off.\n#define VID_FP16\t\t\t\t(1u<<4)\t//use 16bit currentrender etc to avoid banding\n#define VID_SRGB_FB (VID_SRGB_FB_LINEAR|VID_SRGB_FB_FAKED)\n\ntypedef struct\n{\n\tqboolean\t\tactiveapp;\n\tqboolean\t\tisminimized;\t//can omit rendering as it won't be seen anyway.\n\tint\t\t\t\tfullbright;\t\t// index of first fullbright color\n\tint\t\t\t\tgammarampsize;\t\t//typically 256. but can be up to 1024 (yay 10-bit hardware that's crippled to only actually use 8)\n\n\tunsigned\t\tfbvwidth; /*virtual 2d width of the current framebuffer image*/\n\tunsigned\t\tfbvheight; /*virtual 2d height*/\n\tunsigned\t\tfbpwidth; /*physical 2d width of the current framebuffer image*/\n\tunsigned\t\tfbpheight; /*physical 2d height*/\n\tstruct image_s\t*framebuffer; /*the framebuffer fbo (set by democapture)*/\n\n\tunsigned\t\twidth; /*virtual 2d screen width*/\n\tunsigned\t\theight; /*virtual 2d screen height*/\n\n\tint\t\t\t\tnumpages;\n\tunsigned int\tflags;\t//VID_* flags\n\n\tunsigned\t\trotpixelwidth; /*width after rotation in pixels*/\n\tunsigned\t\trotpixelheight; /*pixel after rotation in pixels*/\n\tunsigned\t\tpixelwidth; /*true height in pixels*/\n\tunsigned\t\tpixelheight; /*true width in pixels*/\n\n\tfloat\t\t\tdpi_x;\n\tfloat\t\t\tdpi_y;\n\n\tconchar_t\t\t*ime_preview;\t\t//pending IME preview text that has been entered but isn't committed yet. set by video code.\n\tsize_t\t\t\time_previewlen;\n\tsize_t\t\t\time_caret;\n\tfloat\t\t\time_position[2];\t//where to display any ime popups (virtual coords)\n\tqboolean\t\time_allow;\t\t\t//enable the ime (ie: at the console or messagemode)\n\n\tqboolean\t\tforcecursor;\n\tfloat\t\t\tforcecursorpos[2];\t//in physical pixels\n\n\tstruct plugvrfuncs_s *vr;\t//how do deal with VR contexts.\n} viddef_t;\n\nextern\tviddef_t\tvid;\t\t\t\t// global video state\n\nextern unsigned int\td_8to24rgbtable[256];\nextern unsigned int\td_8to24srgbtable[256];\nextern unsigned int\td_8to24bgrtable[256];\nextern unsigned int\td_quaketo24srgbtable[256];\n"
  },
  {
    "path": "engine/client/vid_headless.c",
    "content": "#include \"quakedef.h\"\n#ifdef HEADLESSQUAKE\n#ifndef SERVERONLY\n#include \"gl_draw.h\"\n#include \"shader.h\"\n\n#ifdef _WIN32\n#include \"winquake.h\"\n#include \"resource.h\"\n#else\n#include <unistd.h>\n#endif\n#if defined(FTE_SDL3)\n\t#include <SDL3/SDL.h>\n#elif defined(FTE_SDL)\n\t#include <SDL.h>\n#endif\n\nstatic void Headless_Draw_Init(void)\n{\n\tR2D_Init();\n}\nstatic void Headless_Draw_Shutdown(void)\n{\n\tShader_Shutdown();\n}\nstatic void\t\tHeadless_IMG_UpdateFiltering\t(image_t *imagelist, int filtermip[3], int filterpic[3], int mipcap[2], float lodbias, float anis)\n{\n}\nstatic qboolean\tHeadless_IMG_LoadTextureMips\t(texid_t tex, const struct pendingtextureinfo *mips)\n{\n\treturn true;\n}\nstatic void\t\tHeadless_IMG_DestroyTexture\t\t(texid_t tex)\n{\n}\nstatic void\tHeadless_R_Init\t\t\t\t\t(void)\n{\n}\nstatic void\tHeadless_R_DeInit\t\t\t\t\t(void)\n{\n}\nstatic void\tHeadless_R_RenderView\t\t\t\t(void)\n{\n}\n#ifdef FTE_SDL\n\t#if SDL_VERSION_ATLEAST(3,0,0)\nstatic SDL_Tray *thetray;\nstatic void SDLCALL Headless_Restore(void *userdata, SDL_TrayEntry *entry)\n{\n\textern cvar_t vid_renderer;\n\tif (!Q_strcasecmp(vid_renderer.string, \"headless\"))\n\t\tCbuf_AddText(\"vid_renderer \\\"\\\";vid_restart\\n\", RESTRICT_LOCAL);\n\telse\n\t\tCbuf_AddText(\"vid_restart\\n\", RESTRICT_LOCAL);\n}\n\t#endif\n#elif defined(_WIN32)\n//tray icon crap, so the user can still restore the game.\nLRESULT CALLBACK HeadlessWndProc(HWND wnd, UINT msg, WPARAM wparam, LPARAM lparam)\n{\n\textern cvar_t vid_renderer;\n\tswitch(msg)\n\t{\n\tcase WM_USER:\n\t\tswitch(LOWORD(lparam))\n\t\t{\n\t\tcase WM_CONTEXTMENU:\n\t\tcase WM_USER+0:\n\t\tcase WM_RBUTTONUP:\n\t\t\tif (!Q_strcasecmp(vid_renderer.string, \"headless\"))\n\t\t\t\tCbuf_AddText(\"vid_renderer \\\"\\\";vid_restart\\n\", RESTRICT_LOCAL);\n\t\t\telse\n\t\t\t\tCbuf_AddText(\"vid_restart\\n\", RESTRICT_LOCAL);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tbreak;\n\t\t}\n\t\treturn 0;\n\tdefault:\n\t\tif (WinNT)\n\t\t\treturn DefWindowProcW(wnd, msg, wparam, lparam);\n\t\telse\n\t\t\treturn DefWindowProcA(wnd, msg, wparam, lparam);\n\t}\n}\n#endif\n\nstatic qboolean Headless_VID_Init\t\t\t\t(rendererstate_t *info, unsigned char *palette)\n{\n\tmemset(&sh_config, 0, sizeof(sh_config));\n\n#ifdef FTE_SDL\n\t#if SDL_VERSION_ATLEAST(3,0,0)\n\t\tthetray = SDL_CreateTray(NULL, fs_manifest->formalname);\n\t\tif (thetray)\n\t\t{\t//don't know how to respond to direct clicks, so create a menu.\n\t\t\tSDL_TrayMenu *menu = SDL_CreateTrayMenu(thetray);\n\t\t\tSDL_TrayEntry *entry = SDL_InsertTrayEntryAt(menu, -1, \"Restore\", SDL_TRAYENTRY_BUTTON);\n\t\t\tSDL_SetTrayEntryCallback(entry, Headless_Restore, NULL);\n\t\t\treturn true;\n\t\t}\n\t\tCon_Printf(CON_ERROR\"Headless: Unable to create system tray icon for restoring.\\n\");\n\t\treturn false;\n\t#endif\n#elif defined(_WIN32)\n\t//tray icon crap, so the user can still restore the game.\n\textern HWND\tmainwindow;\n\textern HINSTANCE\tglobal_hInstance;\n\tif (WinNT)\n\t{\n\t\tWNDCLASSW wc;\n\t\tNOTIFYICONDATAW data;\n\n\t\t//Shell_NotifyIcon requires a window to provide events etc.\n\t\twc.style         = 0;\n\t\twc.lpfnWndProc   = (WNDPROC)HeadlessWndProc;\n\t\twc.cbClsExtra    = 0;\n\t\twc.cbWndExtra    = 0;\n\t\twc.hInstance     = global_hInstance;\n\t\twc.hIcon         = LoadIcon (global_hInstance, MAKEINTRESOURCE (IDI_ICON1));\n\t\twc.hCursor       = LoadCursor (NULL,IDC_ARROW);\n\t\twc.hbrBackground = NULL;\n\t\twc.lpszMenuName  = 0;\n\t\twc.lpszClassName = L\"FTEHeadlessClass\";\n\t\tRegisterClassW(&wc);\n\n\t\tmainwindow = CreateWindowExW(0L, wc.lpszClassName, L\"FTE QuakeWorld\", 0, 0, 0, 0, 0, NULL, NULL, global_hInstance, NULL);\n\t\tdata.cbSize = sizeof(data);\n\t\tdata.hWnd = mainwindow;\n\t\tdata.uID = 0;\n\t\tdata.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP;\n\t\tdata.uCallbackMessage = WM_USER;\n\t\tdata.hIcon = wc.hIcon;\n\t\twcscpy(data.szTip, L\"Right-click to restore\");\n\t\tif (pShell_NotifyIconW)\n\t\t\tpShell_NotifyIconW(NIM_ADD, &data);\n\t}\n\telse\n\t{\n\t\tWNDCLASSA wc;\n\t\tNOTIFYICONDATAA data;\n\n\t\t//Shell_NotifyIcon requires a window to provide events etc.\n\t\twc.style         = 0;\n\t\twc.lpfnWndProc   = (WNDPROC)HeadlessWndProc;\n\t\twc.cbClsExtra    = 0;\n\t\twc.cbWndExtra    = 0;\n\t\twc.hInstance     = global_hInstance;\n\t\twc.hIcon         = LoadIcon (global_hInstance, MAKEINTRESOURCE (IDI_ICON1));\n\t\twc.hCursor       = LoadCursor (NULL,IDC_ARROW);\n\t\twc.hbrBackground = NULL;\n\t\twc.lpszMenuName  = 0;\n\t\twc.lpszClassName = \"FTEHeadlessClass\";\n\t\tRegisterClassA(&wc);\n\n\t\tmainwindow = CreateWindowExA(0L, wc.lpszClassName, \"FTE QuakeWorld\", 0, 0, 0, 0, 0, NULL, NULL, global_hInstance, NULL);\n\t\tdata.cbSize = sizeof(data);\n\t\tdata.hWnd = mainwindow;\n\t\tdata.uID = 0;\n\t\tdata.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP;\n\t\tdata.uCallbackMessage = WM_USER;\n\t\tdata.hIcon = wc.hIcon;\n\t\t//fixme: proper multibyte\n\t\tQ_strncpyz(data.szTip, \"Right-click to restore\", sizeof(data.szTip));\n\t\tShell_NotifyIconA(NIM_ADD, &data);\n\t}\n#endif\n\treturn true;\n}\nstatic void\t Headless_VID_DeInit\t\t\t\t(void)\n{\n#ifdef FTE_SDL\n\t#if SDL_VERSION_ATLEAST(3,0,0)\n\t\tSDL_DestroyTray(thetray);\n\t\tthetray = NULL;\n\t#endif\n#elif defined(_WIN32)\n\t//tray icon crap, so the user can still restore the game.\n\t//FIXME: remove tray icon. win95 won't do this automagically.\n\textern HWND\tmainwindow;\n\tDestroyWindow(mainwindow);\n\tmainwindow = NULL;\n#endif\n}\nstatic void\tHeadless_VID_SwapBuffers\t\t\t(void)\n{\n}\nstatic qboolean Headless_VID_ApplyGammaRamps\t\t(unsigned int gammarampsize, unsigned short *ramps)\n{\n\treturn false;\n}\nstatic void\tHeadless_VID_SetWindowCaption\t\t(const char *msg)\n{\n}\nstatic int Headless_VID_GetPrioriy\t\t\t\t(void)\n{\t//headless renderers are the lowest priority possible, due to how broken they'd be perceived to be.\n\treturn -1;\n}\nstatic char\t*Headless_VID_GetRGBInfo\t\t\t(int *bytestride, int *truevidwidth, int *truevidheight, enum uploadfmt *fmt)\n{\n\t*fmt = TF_INVALID;\n\t*bytestride = *truevidwidth = *truevidheight = 0;\n\treturn NULL;\n}\nstatic qboolean\tHeadless_SCR_UpdateScreen\t\t\t(void)\n{\n\tif (!cls.timedemo)\n\t{\n#ifdef FTE_SDL\n\t\tSDL_Delay(100);\t\t\n#elif defined(_WIN32)\n\t\tSleep(100);\n#else\n\t\tusleep(100*1000);\n#endif\n\t}\n\treturn true;\n}\nstatic void\tHeadless_BE_SelectMode\t(backendmode_t mode)\n{\n}\nstatic void\tHeadless_BE_DrawMesh_List\t(shader_t *shader, int nummeshes, struct mesh_s **mesh, struct vbo_s *vbo, struct texnums_s *texnums, unsigned int be_flags)\n{\n}\nstatic void\tHeadless_BE_DrawMesh_Single\t(shader_t *shader, struct mesh_s *meshchain, struct vbo_s *vbo, unsigned int be_flags)\n{\n}\nstatic void\tHeadless_BE_SubmitBatch\t(struct batch_s *batch)\n{\n}\nstatic struct batch_s *Headless_BE_GetTempBatch\t(void)\n{\n\treturn NULL;\n}\nstatic void\tHeadless_BE_DrawWorld\t(struct batch_s **worldbatches)\n{\n}\nstatic void\tHeadless_BE_Init\t(void)\n{\n}\nstatic void Headless_BE_GenBrushModelVBO\t(struct model_s *mod)\n{\n}\nstatic void Headless_BE_ClearVBO\t(struct vbo_s *vbo, qboolean dataonly)\n{\n}\nstatic void Headless_BE_UploadAllLightmaps\t(void)\n{\n}\nstatic void Headless_BE_SelectEntity\t(struct entity_s *ent)\n{\n}\nstatic qboolean Headless_BE_SelectDLight\t(struct dlight_s *dl, vec3_t colour, vec3_t axis[3], unsigned int lmode)\n{\n\treturn false;\n}\nstatic void Headless_BE_Scissor\t(srect_t *rect)\n{\n}\nstatic qboolean Headless_BE_LightCullModel\t(vec3_t org, struct model_s *model)\n{\n\treturn false;\n}\nstatic void Headless_BE_VBO_Begin\t(vbobctx_t *ctx, size_t maxsize)\n{\n}\nstatic void Headless_BE_VBO_Data\t(vbobctx_t *ctx, void *data, size_t size, vboarray_t *varray)\n{\n}\nstatic void Headless_BE_VBO_Finish\t(vbobctx_t *ctx, void *edata, size_t esize, vboarray_t *earray, void **vbomem, void **ebomem)\n{\n}\nstatic void Headless_BE_VBO_Destroy\t(vboarray_t *vearray, void *mem)\n{\n}\nstatic void Headless_BE_RenderToTextureUpdate2d\t(qboolean destchanged)\n{\n}\n\nrendererinfo_t headlessrenderer =\n{\n\t\"Headless (Null)\",\n\t{\"headless\"},\n\tQR_HEADLESS,\n\n\tHeadless_Draw_Init,\n\tHeadless_Draw_Shutdown,\n\tHeadless_IMG_UpdateFiltering,\n\tHeadless_IMG_LoadTextureMips,\n\tHeadless_IMG_DestroyTexture,\n\tHeadless_R_Init,\n\tHeadless_R_DeInit,\n\tHeadless_R_RenderView,\n\tHeadless_VID_Init,\n\tHeadless_VID_DeInit,\n\tHeadless_VID_SwapBuffers,\n\tHeadless_VID_ApplyGammaRamps,\n\tNULL,\n\tNULL,\n\tNULL,\n\tHeadless_VID_SetWindowCaption,\n\tHeadless_VID_GetRGBInfo,\n\tHeadless_SCR_UpdateScreen,\n\tHeadless_BE_SelectMode,\n\tHeadless_BE_DrawMesh_List,\n\tHeadless_BE_DrawMesh_Single,\n\tHeadless_BE_SubmitBatch,\n\tHeadless_BE_GetTempBatch,\n\tHeadless_BE_DrawWorld,\n\tHeadless_BE_Init,\n\tHeadless_BE_GenBrushModelVBO,\n\tHeadless_BE_ClearVBO,\n\tHeadless_BE_UploadAllLightmaps,\n\tHeadless_BE_SelectEntity,\n\tHeadless_BE_SelectDLight,\n\tHeadless_BE_Scissor,\n\tHeadless_BE_LightCullModel,\n\tHeadless_BE_VBO_Begin,\n\tHeadless_BE_VBO_Data,\n\tHeadless_BE_VBO_Finish,\n\tHeadless_BE_VBO_Destroy,\n\tHeadless_BE_RenderToTextureUpdate2d,\n\t\"\",\n\tHeadless_VID_GetPrioriy\n};\n\n\n\n\n#if 0//def VKQUAKE\n#include \"../vk/vkrenderer.h\"\nstatic qboolean HeadlessVK_CreateSurface(void)\n{\n\tvk.surface = VK_NULL_HANDLE;\t//nothing to create, we're using offscreen rendering.\n\tvk.allowsubmissionthread = false;\t//waste of threading\n\treturn true;\n}\nstatic void HeadlessVK_Present(struct vkframe *theframe)\n{\n\t//VK_DoPresent(theframe);\n\n\tif (!theframe)\n\t\treturn;\n\tvk\n\n\tqglWaitVkSemaphoreNV(theframe->backbuf->presentsemaphore);\n\t//tell the gl driver to copy it over now\n\tqglDrawVkImageNV(theframe->backbuf->colour.image, theframe->backbuf->colour.sampler, \n\t\t0, 0, vid.pixelwidth, vid.pixelheight,\t//xywh (window coords)\n\t\t0,\t//z\n\t\t0, 1, 1, 0);\t//stst (remember that gl textures are meant to be upside down)\n\n\t//at this point the gl driver can signal the fence so our vk code can wake up and start drawing the next frame (if the gpu is slow)\n\tqglSignalVkFenceNV(vk.acquirefences[vk.aquirelast%ACQUIRELIMIT]);\n\t//and tell our code to expect it.\n\tvk.acquirebufferidx[vk.aquirelast%ACQUIRELIMIT] = vk.aquirelast%vk.backbuf_count;\n\tbarrier\n\tvk.aquirelast++;\n}\nstatic qboolean HeadlessVK_Init (rendererstate_t *info, unsigned char *palette)\n{\n\textern cvar_t vid_conautoscale;\n#ifdef VK_NO_PROTOTYPES\n\tstatic dllhandle_t *hInstVulkan = NULL;\n\tdllfunction_t vkfuncs[] =\n\t{\n\t\t{(void**)&vkGetInstanceProcAddr, \"vkGetInstanceProcAddr\"},\n\t\t{NULL}\n\t};\n\tif (!hInstVulkan)\n\t\thInstVulkan = *info->subrenderer?Sys_LoadLibrary(info->subrenderer, vkfuncs):NULL;\n#ifdef _WIN32\n\tif (!hInstVulkan)\n\t\thInstVulkan = Sys_LoadLibrary(\"vulkan-1.dll\", vkfuncs);\n#else\n\tif (!hInstVulkan)\n\t\thInstVulkan = Sys_LoadLibrary(\"libvulkan.so.1\", vkfuncs);\n\tif (!hInstVulkan)\n\t\thInstVulkan = Sys_LoadLibrary(\"libvulkan.so\", vkfuncs);\n#endif\n\tif (!hInstVulkan)\n\t{\n\t\tCon_Printf(\"Unable to load vulkan library\\nNo Vulkan drivers are installed\\n\");\n\t\treturn false;\n\t}\n#endif\n\n\tvid.pixelwidth = 1920;\n\tvid.pixelheight = 1080;\n\tif (!VK_Init(info, NULL,0, HeadlessVK_CreateSurface, NULL))\n\t\treturn false;\n\tCvar_ForceCallback(&vid_conautoscale);\n\treturn true;\n}\n\nrendererinfo_t headlessvkrendererinfo =\n{\n\t\"Headless Vulkan\",\n\t{\n\t\t\"vkheadless\"\n\t},\n\tQR_VULKAN,\n\n\tVK_Draw_Init,\n\tVK_Draw_Shutdown,\n\n\tVK_UpdateFiltering,\n\tVK_LoadTextureMips,\n\tVK_DestroyTexture,\n\n\tVK_R_Init,\n\tVK_R_DeInit,\n\tVK_R_RenderView,\n\n\tHeadlessVK_Init,\n\tGLVID_DeInit,\n\tGLVID_SwapBuffers,\n\tGLVID_ApplyGammaRamps,\n\tNULL,\n\tNULL,\n\tNULL,\n\tGLVID_SetCaption,\n\tVKVID_GetRGBInfo,\n\n\tVK_SCR_UpdateScreen,\n\n\tVKBE_SelectMode,\n\tVKBE_DrawMesh_List,\n\tVKBE_DrawMesh_Single,\n\tVKBE_SubmitBatch,\n\tVKBE_GetTempBatch,\n\tVKBE_DrawWorld,\n\tVKBE_Init,\n\tVKBE_GenBrushModelVBO,\n\tVKBE_ClearVBO,\n\tVKBE_UploadAllLightmaps,\n\tVKBE_SelectEntity,\n\tVKBE_SelectDLight,\n\tVKBE_Scissor,\n\tVKBE_LightCullModel,\n\n\tVKBE_VBO_Begin,\n\tVKBE_VBO_Data,\n\tVKBE_VBO_Finish,\n\tVKBE_VBO_Destroy,\n\n\tVKBE_RenderToTextureUpdate2d,\n\n\t\"no more\",\n\n\tHeadless_VID_GetPrioriy\n};\n#endif\n#endif\n#endif\n"
  },
  {
    "path": "engine/client/view.c",
    "content": "/*\nCopyright (C) 1996-1997 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n*/\n// view.c -- player eye positioning\n\n#include \"quakedef.h\"\n\n#include \"winquake.h\"\n#include \"glquake.h\"\n#include \"shader.h\"\n\n#include <ctype.h> // for isdigit();\n\ncvar_t r_projection = CVARD(\"r_projection\", \"0\", \"0: regular perspective.\\n1: stereographic.\\n2: fisheye.\\n3: panoramic.\\n4: lambert azimuthal equal-area.\\n5: equirectangular.\\n6: panini.\");\ncvar_t ffov = CVARFD(\"ffov\", \"\", 0, \"Allows you to set a specific field of view for when a custom projection is specified. If empty, will use regular fov cvar, which might get messy.\");\n#if defined(_WIN32) && !defined(MINIMAL)\n//amusing gimmick / easteregg.\n#include \"winquake.h\"\nstatic cvar_t itburnsitburnsmakeitstop = CVARFD(\"itburnsitburnsmakeitstop\", \"0\", CVAR_NOTFROMSERVER, \"Ouch\");\n#endif\n\n/*\n\nThe view is allowed to move slightly from it's true position for bobbing,\nbut if it exceeds 8 pixels linear distance (spherical, not box), the list of\nentities sent from the server may not include everything in the pvs, especially\nwhen crossing a water boudnary.\n\n*/\n\n#ifdef SIDEVIEWS\nstatic struct\n{\n\tcvar_t enabled;\n\tcvar_t x;\n\tcvar_t y;\n\tcvar_t scalex;\n\tcvar_t scaley;\n\tcvar_t yaw;\n} sideview[SIDEVIEWS] = {\n\t{CVAR(\"v2_enabled\", \"2\"),\tCVAR(\"v2_x\", \"0\"),\t\tCVAR(\"v2_y\", \"0\"),\tCVAR(\"v2_scalex\", \"0.25\"),\tCVAR(\"v2_scaley\", \"0.25\"), CVAR(\"v2_yaw\", \"180\")},\n#if SIDEVIEWS > 1\n\t{CVAR(\"v3_enabled\", \"2\"),\tCVAR(\"v3_x\", \"0.25\"),\tCVAR(\"v3_y\", \"0\"),\tCVAR(\"v3_scalex\", \"0.25\"),\tCVAR(\"v3_scaley\", \"0.25\"), CVAR(\"v3_yaw\", \"90\")},\n#endif\n#if SIDEVIEWS > 2\n\t{CVAR(\"v4_enabled\", \"2\"),\tCVAR(\"v4_x\", \"0.5\"),\tCVAR(\"v4_y\", \"0\"),\tCVAR(\"v4_scalex\", \"0.25\"),\tCVAR(\"v4_scaley\", \"0.25\"), CVAR(\"v4_yaw\", \"270\")},\n#endif\n#if SIDEVIEWS > 3\n\t{CVAR(\"v5_enabled\", \"2\"),\tCVAR(\"v5_x\", \"0.75\"),\tCVAR(\"v5_y\", \"0\"),\tCVAR(\"v5_scalex\", \"0.25\"),\tCVAR(\"v5_scaley\", \"0.25\"), CVAR(\"v5_yaw\", \"0\")},\n#endif\n};\n#endif\n\ncvar_t\tcl_rollspeed\t\t\t= CVARD(\"cl_rollspeed\", \"200\", \"Controls the speed required to reach cl_rollangle's tilt.\");\nstatic cvar_t\tcl_rollangle\t\t\t= CVARD(\"cl_rollangle\", \"2.0\", \"Controls the maximum view should tilt while strafing.\");\nstatic cvar_t\tcl_rollalpha\t\t\t= CVARD(\"cl_rollalpha\", \"20\", \"Controls the speed at which the view rolls according to sideways movement.\");\nstatic cvar_t\tv_deathtilt\t\t\t\t= CVARD(\"v_deathtilt\", \"1\", \"Specifies whether to tilt the view when dead.\");\n\nstatic cvar_t\tcl_bob\t\t\t\t\t= CVARD(\"cl_bob\",\"0.02\", \"Controls how much the camera position should bob up down as the player runs around.\");\nstatic cvar_t\tcl_bobcycle\t\t\t\t= CVAR(\"cl_bobcycle\",\"0.6\");\nstatic cvar_t\tcl_bobup\t\t\t\t= CVAR(\"cl_bobup\",\"0.5\");\n\nstatic cvar_t\tcl_bobmodel\t\t\t\t= CVARD(\"cl_bobmodel\", \"0\", \"Controls whether the viewmodel should bob up and down as the player runs around.\");\nstatic cvar_t\tcl_bobmodel_side\t\t= CVAR(\"cl_bobmodel_side\", \"0.15\");\nstatic cvar_t\tcl_bobmodel_up\t\t\t= CVAR(\"cl_bobmodel_up\", \"0.06\");\nstatic cvar_t\tcl_bobmodel_speed\t\t= CVAR(\"cl_bobmodel_speed\", \"7\");\n\nstatic cvar_t\tv_kicktime\t\t\t\t= CVARD(\"v_kicktime\", \"0.5\", \"This controls how long viewangle changes from taking damage will last.\");\nstatic cvar_t\tv_kickroll\t\t\t\t= CVARD(\"v_kickroll\", \"0.6\", \"This controls part of the strength of viewangle changes from taking damage.\");\nstatic cvar_t\tv_kickpitch\t\t\t\t= CVARD(\"v_kickpitch\", \"0.6\", \"This controls part of the strength of viewangle changes from taking damage.\");\n\nstatic cvar_t\tv_iyaw_cycle\t\t\t= CVAR(\"v_iyaw_cycle\", \"2\");\nstatic cvar_t\tv_iroll_cycle\t\t\t= CVAR(\"v_iroll_cycle\", \"0.5\");\nstatic cvar_t\tv_ipitch_cycle\t\t\t= CVAR(\"v_ipitch_cycle\", \"1\");\nstatic cvar_t\tv_iyaw_level\t\t\t= CVAR(\"v_iyaw_level\", \"0.3\");\nstatic cvar_t\tv_iroll_level\t\t\t= CVAR(\"v_iroll_level\", \"0.1\");\nstatic cvar_t\tv_ipitch_level\t\t\t= CVAR(\"v_ipitch_level\", \"0.3\");\nstatic cvar_t\tv_idlescale\t\t\t\t= CVARD(\"v_idlescale\", \"0\", \"Enable swishing of the view (whether idle or otherwise). Often used for concussion effects.\");\n\ncvar_t\tcrosshair\t\t\t\t= CVARF(\"crosshair\", \"1\", CVAR_ARCHIVE);\ncvar_t\tcrosshaircolor\t\t\t= CVARF(\"crosshaircolor\", \"255 255 255\", CVAR_ARCHIVE); //QSSM misnamed it...\ncvar_t\tcrosshairsize\t\t\t= CVARF(\"crosshairsize\", \"8\", CVAR_ARCHIVE);\t//QS has scr_crosshairscale, but its a multiplier rather than (vritual) size.\n\ncvar_t\tcl_crossx\t\t\t\t= CVARF(\"cl_crossx\", \"0\", CVAR_ARCHIVE);\ncvar_t\tcl_crossy\t\t\t\t= CVARF(\"cl_crossy\", \"0\", CVAR_ARCHIVE);\ncvar_t\tcrosshaircorrect\t\t= CVARFD(\"crosshaircorrect\", \"0\", CVAR_SEMICHEAT, \"Moves the crosshair around to represent the impact point of a weapon positioned below the actual view position.\");\ncvar_t\tcrosshairimage\t\t\t= CVARD(\"crosshairimage\", \"\", \"Enables the use of an external/custom crosshair image\");\ncvar_t\tcrosshairalpha\t\t\t= CVAR(\"crosshairalpha\", \"1\");\n\nstatic qboolean v_skyroom_set;\nstatic vec4_t v_skyroom_origin;\nstatic vec4_t v_skyroom_orientation;\nstatic void QDECL V_Skyroom_Changed(struct cvar_s *var, char *oldvalue)\n{\n\tchar tok[64];\n\tchar *line = var->string;\n\tif (*line)\n\t\tv_skyroom_set = true;\n\telse\n\t\tv_skyroom_set = false;\n\tline = COM_ParseTokenOut(line, NULL, tok, sizeof(tok), NULL);\n\tv_skyroom_origin[0] = atof(tok);\n\tline = COM_ParseTokenOut(line, NULL, tok, sizeof(tok), NULL);\n\tv_skyroom_origin[1] = atof(tok);\n\tline = COM_ParseTokenOut(line, NULL, tok, sizeof(tok), NULL);\n\tv_skyroom_origin[2] = atof(tok);\n\tline = COM_ParseTokenOut(line, NULL, tok, sizeof(tok), NULL);\n\tv_skyroom_origin[3] = atof(tok);\n\n\tline = COM_ParseTokenOut(line, NULL, tok, sizeof(tok), NULL);\n\tv_skyroom_orientation[3] = atof(tok);\n\tline = COM_ParseTokenOut(line, NULL, tok, sizeof(tok), NULL);\n\tv_skyroom_orientation[0] = atof(tok);\n\tline = COM_ParseTokenOut(line, NULL, tok, sizeof(tok), NULL);\n\tv_skyroom_orientation[1] = atof(tok);\n\tline = COM_ParseTokenOut(line, NULL, tok, sizeof(tok), NULL);\n\tv_skyroom_orientation[2] = atof(tok);\n}\ncvar_t\tv_skyroom\t\t= CVARFCD(\"skyroom\", \"\", CVAR_SEMICHEAT, V_Skyroom_Changed, \"Specifies the center position of the skyroom's view. Skyrooms are drawn instead of skyboxes (typically with their own skybox around them). Entities in skyrooms will be drawn only when r_ignoreentpvs is 0. Can also be set with the _skyroom worldspawn key. This is overriden by csqc's VF_SKYROOM_CAMERA.\");\n\nstatic cvar_t\tgl_cshiftpercent\t\t= CVAR(\"gl_cshiftpercent\", \"100\");\ncvar_t\tgl_cshiftenabled\t\t= CVARFD(\"gl_polyblend\", \"1\", CVAR_ARCHIVE, \"Controls whether temporary whole-screen colour changes should be honoured or not. Change gl_cshiftpercent if you want to adjust the intensity.\\nThis does not affect v_cshift commands sent from the server.\");\ncvar_t\tgl_cshiftborder\t\t\t= CVARD(\"gl_polyblend_edgesize\", \"128\", \"This constrains colour shifts to the edge of the screen, with the value specifying the size of those borders.\");\n\nstatic cvar_t\tv_bonusflash\t\t\t= CVARD(\"v_bonusflash\", \"1\", \"Controls the strength of temporary screen flashes when picking up items (gl_polyblend must be enabled).\");\n\ncvar_t  v_contentblend\t\t\t= CVARFD(\"v_contentblend\", \"1\", CVAR_ARCHIVE, \"Controls the strength of underwater colour tints (gl_polyblend must be enabled).\");\nstatic cvar_t\tv_damagecshift\t\t\t= CVARD(\"v_damagecshift\", \"1\", \"Controls the strength of damage-taken colour tints (gl_polyblend must be enabled).\");\nstatic cvar_t\tv_quadcshift\t\t\t= CVARD(\"v_quadcshift\", \"1\", \"Controls the strength of quad-damage colour tints (gl_polyblend must be enabled).\");\nstatic cvar_t\tv_suitcshift\t\t\t= CVARD(\"v_suitcshift\", \"1\", \"Controls the strength of envirosuit colour tints (gl_polyblend must be enabled).\");\nstatic cvar_t\tv_ringcshift\t\t\t= CVARD(\"v_ringcshift\", \"1\", \"Controls the strength of ring-of-invisibility colour tints (gl_polyblend must be enabled).\");\nstatic cvar_t\tv_pentcshift\t\t\t= CVARD(\"v_pentcshift\", \"1\", \"Controls the strength of pentagram-of-protection colour tints (gl_polyblend must be enabled).\");\nstatic cvar_t\tv_gunkick\t\t\t\t= CVARD(\"v_gunkick\", \"0\", \"Controls the strength of view angle changes when firing weapons.\");\ncvar_t\tv_gunkick_q2\t\t\t= CVARD(\"v_gunkick_q2\", \"1\", \"Controls the strength of view angle changes when firing weapons (in Quake2).\");\nstatic cvar_t\tv_viewmodel_quake\t\t= CVARD(\"r_viewmodel_quake\", \"0\", \"Controls whether to use weird viewmodel movements from vanilla quake.\");\t//name comes from MarkV.\n\ncvar_t\tv_viewheight\t\t\t= CVARF(\"v_viewheight\", \"0\", CVAR_ARCHIVE);\n//static cvar_t\tv_projectionmode\t\t= CVARF(\"v_projectionmode\", \"0\", CVAR_ARCHIVE);\n\nstatic cvar_t\tv_depthsortentities\t\t= CVARAD(\"v_depthsortentities\", \"0\", \"v_reorderentitiesrandomly\", \"Reorder entities for transparency such that the furthest entities are drawn first, allowing nearer transparent entities to draw over the top of them.\");\n\n#ifdef QUAKESTATS\nstatic cvar_t\tscr_autoid\t\t\t\t= CVARD(\"scr_autoid\", \"1\", \"Display nametags above all players while spectating.\");\nstatic cvar_t\tscr_autoid_team\t\t\t= CVARD(\"scr_autoid_team\", \"0\", \"Display nametags above team members. 0: off. 1: display with half-alpha if occluded. 2: hide when occluded.\");\nstatic cvar_t\tscr_autoid_health\t\t= CVARD(\"scr_autoid_health\", \"1\", \"Display health as part of nametags (when known).\");\nstatic cvar_t\tscr_autoid_armour\t\t= CVARD(\"scr_autoid_armor\", \"1\", \"Display armour as part of nametags (when known).\");\nstatic cvar_t\tscr_autoid_weapon\t\t= CVARD(\"scr_autoid_weapon\", \"1\", \"Display the player's best weapon as part of their nametag (when known).\");\nstatic cvar_t\tscr_autoid_weapon_mask\t= CVARD(\"scr_autoid_weapon_mask\", \"126\", \"Mask of bits for weapon icons to actually show.\\n+1: Shotgun.\\n+2: Super Shotgun.\\n+4: Nail Gun.\\n+8: Super Nail Gun.\\n+16: Grenade Launcher.\\n+32: Rocket Launcher.\\n+64: Lightning\\nShowing only RL and GL is 96\\n\");\nstatic cvar_t\tscr_autoid_teamcolour\t= CVARD(\"scr_autoid_teamcolour\", STRINGIFY(COLOR_BLUE), \"The colour for the text on the nametags of team members.\");\nstatic cvar_t\tscr_autoid_enemycolour\t= CVARD(\"scr_autoid_enemycolour\", STRINGIFY(COLOR_WHITE), \"The colour for the text on the nametags of non-team members.\");\n#endif\n\ncvar_t\tchase_active\t\t\t= CVAR(\"chase_active\", \"0\");\ncvar_t\tchase_back\t\t\t\t= CVAR(\"chase_back\", \"48\");\ncvar_t\tchase_up\t\t\t\t= CVAR(\"chase_up\", \"24\");\ncvar_t\tchase_right\t\t\t\t= CVAR(\"chase_right\", \"0\");\n\n\nextern cvar_t cl_chasecam;\n\nstatic player_state_t\t\t*view_message;\n\n/*\n===============\nV_CalcRoll\n\n===============\n*/\nfloat V_CalcRoll (vec3_t angles, vec3_t velocity)\n{\n\tvec3_t\tforward, right, up;\n\tfloat\tsign;\n\tfloat\tside;\n\tfloat\tvalue;\n\n\tAngleVectors (angles, forward, right, up);\n\tside = DotProduct (velocity, right);\n\tsign = side < 0 ? -1 : 1;\n\tside = fabs(side);\n\n\tvalue = cl_rollangle.value;\n\n\tif (side < cl_rollspeed.value)\n\t\tside = side * value / cl_rollspeed.value;\n\telse\n\t\tside = value;\n\n\treturn side*sign;\n\n}\n\n\n/*\n===============\nV_CalcBob\n\n===============\n*/\nfloat V_CalcBob (playerview_t *pv, qboolean queryold)\n{\n\tfloat\tcycle;\n\tfloat\thspeed, bob;\n\tvec3_t\thvel;\n\n\tif (pv->spectator)\n\t\treturn 0;\n\n\tif (cl_bobcycle.value <= 0 || cl.intermissionmode != IM_NONE)\n\t\treturn 0;\n\n\tif (!pv->onground || cl.paused)\n\t{\n\t\tpv->bobcltime = cl.time;\n\t\treturn pv->bob;\t\t// just use old value. FIXME: diminish over time.\n\t}\n\n\tpv->bobtime += cl.time - pv->bobcltime;\n\tpv->bobcltime = cl.time;\n\tcycle = pv->bobtime - (int)(pv->bobtime/cl_bobcycle.value)*cl_bobcycle.value;\n\tcycle /= cl_bobcycle.value;\n\tif (cycle < cl_bobup.value)\n\t\tcycle = M_PI * cycle / cl_bobup.value;\n\telse\n\t\tcycle = M_PI + M_PI*(cycle-cl_bobup.value)/(1.0 - cl_bobup.value);\n\n// bob is proportional to simulated velocity in the xy plane\n// (don't count Z, or jumping messes it up)\n\thspeed = DotProduct(pv->simvel, pv->gravitydir);\n\tVectorMA(pv->simvel, hspeed, pv->gravitydir, hvel);\n\thspeed = VectorLength(hvel);\n\thspeed = bound(0, hspeed, 400);\n\tbob = hspeed * bound(0, cl_bob.value, 0.05);\n\tpv->bob = bob*0.3 + bob*0.7*sin(cycle);\n\treturn pv->bob;\n\n}\n\n\n//=============================================================================\n\n\nstatic cvar_t\tv_centermove = CVAR(\"v_centermove\", \"0.15\");\nstatic cvar_t\tv_centerspeed = CVAR(\"v_centerspeed\",\"500\");\n\n\nvoid V_StartPitchDrift (playerview_t *pv)\n{\n#if 1\n\tif (pv->laststop == cl.time)\n\t{\n\t\treturn;\t\t// something else is keeping it from drifting\n\t}\n#endif\n\tif (pv->nodrift || !pv->pitchvel)\n\t{\n\t\tpv->pitchvel = v_centerspeed.value;\n\t\tpv->nodrift = false;\n\t\tpv->driftmove = 0;\n\t}\n}\n\nvoid V_StopPitchDrift (playerview_t *pv)\n{\n\tpv->laststop = cl.time;\n\tpv->nodrift = true;\n\tpv->pitchvel = 0;\n}\n\nvoid V_CenterView_f(void)\n{\n\tint pnum = CL_TargettedSplit(false);\n\tV_StartPitchDrift(&cl.playerview[pnum]);\n}\n\n/*\n===============\nV_DriftPitch\n\nMoves the client pitch angle towards cl.idealpitch sent by the server.\n\nIf the user is adjusting pitch manually, either with lookup/lookdown,\nmlook and mouse, or klook and keyboard, pitch drifting is constantly stopped.\n\nDrifting is enabled when the center view key is hit, mlook is released and\nlookspring is non 0, or when\n===============\n*/\nvoid V_DriftPitch (playerview_t *pv)\n{\n\tfloat\t\tdelta, move;\n\n\tif (!pv->onground || cls.demoplayback )\n\t{\n\t\tpv->driftmove = 0;\n\t\tpv->pitchvel = 0;\n\t\treturn;\n\t}\n\n// don't count small mouse motion\n\tif (pv->nodrift)\n\t{\n\t\tif (Length(pv->simvel) < 200)\n\t\t\tpv->driftmove = 0;\n\t\telse\n\t\t\tpv->driftmove += host_frametime;\n\n\t\tif ( pv->driftmove > v_centermove.value)\n\t\t{\n\t\t\tV_StartPitchDrift (pv);\n\t\t}\n\t\treturn;\n\t}\n\n#ifdef QUAKESTATS\n\tdelta = pv->statsf[STAT_IDEALPITCH] - pv->viewangles[PITCH];\n#else\n\tdelta = 0 - pv->viewangles[PITCH];\n#endif\n\n\tif (!delta)\n\t{\n\t\tpv->pitchvel = 0;\n\t\treturn;\n\t}\n\n\tmove = host_frametime * pv->pitchvel;\n\tpv->pitchvel += host_frametime * v_centerspeed.value;\n\n//Con_Printf (\"move: %f (%f)\\n\", move, host_frametime);\n\n\tif (delta > 0)\n\t{\n\t\tif (move > delta)\n\t\t{\n\t\t\tpv->pitchvel = 0;\n\t\t\tmove = delta;\n\t\t}\n\t\tpv->viewangles[PITCH] += move;\n\t}\n\telse if (delta < 0)\n\t{\n\t\tif (move > -delta)\n\t\t{\n\t\t\tpv->pitchvel = 0;\n\t\t\tmove = -delta;\n\t\t}\n\t\tpv->viewangles[PITCH] -= move;\n\t}\n}\n\n\n\n\n\n/*\n==============================================================================\n\n\t\t\t\t\t\tPALETTE FLASHES\n\n==============================================================================\n*/\n\nstatic void QDECL V_Gamma_Callback(struct cvar_s *var, char *oldvalue);\n\nstatic cvar_t\t\tv_cshift_empty = CVARFD(\"v_cshift_empty\", \"130 80 50 0\",\tCVAR_ARCHIVE, \"The colour tint to use when in the open air (additionally scaled by v_contentblend.\");\nstatic cvar_t\t\tv_cshift_water = CVARFD(\"v_cshift_water\", \"130 80 50 128\",\tCVAR_ARCHIVE, \"The colour tint to use when underwater (additionally scaled by v_contentblend.\");\nstatic cvar_t\t\tv_cshift_slime = CVARFD(\"v_cshift_slime\",   \"0 25 5 150\",\tCVAR_ARCHIVE, \"The colour tint to use when submerged in slime (additionally scaled by v_contentblend.\");\nstatic cvar_t\t\tv_cshift_lava  = CVARFD(\"v_cshift_lava\",  \"255 80 0 150\",\tCVAR_ARCHIVE, \"The colour tint to use when burried in lava (ouchie!) (additionally scaled by v_contentblend.\");\n\ncvar_t\t\tv_gamma = CVARAFCD(\"gamma\", \"1.0\", \"v_gamma\", CVAR_ARCHIVE|CVAR_RENDERERCALLBACK, V_Gamma_Callback, \"Controls how bright the screen is. Setting this to anything but 1 without hardware gamma requires glsl support and can noticably harm your framerate.\");\ncvar_t\t\tv_gammainverted = CVARFCD(\"v_gammainverted\", \"0\", CVAR_ARCHIVE, V_Gamma_Callback, \"Boolean that controls whether the gamma should be inverted (like quake) or not.\");\ncvar_t\t\tv_contrast = CVARAFCD(\"contrast\", \"1.0\", \"v_contrast\", CVAR_ARCHIVE, V_Gamma_Callback, \"Scales colour values linearly to make your screen easier to see. Setting this to anything but 1 without hardware gamma will reduce your framerates a little.\");\ncvar_t\t\tv_contrastboost = CVARFCD(\"v_contrastboost\", \"1.0\", CVAR_ARCHIVE, V_Gamma_Callback, \"Amplifies contrast in dark areas\");\ncvar_t\t\tv_brightness = CVARAFCD(\"brightness\", \"0.0\", \"v_brightness\", CVAR_ARCHIVE, V_Gamma_Callback, \"Brightness is how much 'white' to add to each and every pixel on the screen.\");\n\nqbyte\t\tgammatable[256];\t// palette is sent through this\n\n\nqboolean\t\tgammaworks;\nfloat\t\thw_blend[4];\t\t// rgba 0.0 - 1.0\n/*\nvoid BuildGammaTable (float g)\n{\n\tint\t\ti, inf;\n\n\tif (g == 1.0)\n\t{\n\t\tfor (i=0 ; i<256 ; i++)\n\t\t\tgammatable[i] = i;\n\t\treturn;\n\t}\n\n\tfor (i=0 ; i<256 ; i++)\n\t{\n\t\tinf = 255 * pow ( (i+0.5)/255.5 , g ) + 0.5;\n\t\tif (inf < 0)\n\t\t\tinf = 0;\n\t\tif (inf > 255)\n\t\t\tinf = 255;\n\t\tgammatable[i] = inf;\n\t}\n}*/\nvoid BuildGammaTable (float gamma, float cscale, float cboost, float brightness)\n{\n\tint i, inf;\n\tfloat t;\n\n//\tgamma = bound (0.1, gamma, 3);\n//\tcscale = bound (1, cscale, 3);\n\n\tif (gamma == 1 && cscale == 1 && cboost == 1 && brightness == 0)\n\t{\n\t\tfor (i = 0; i < 256; i++)\n\t\t\tgammatable[i] = i;\n\t\treturn;\n\t}\n\n\tfor (i = 0; i < 256; i++)\n\t{\n\t\t//the 0.5s are for rounding.\n\t\tt = (i + 0.5) / 255.5; //scale the lighting\n\t\tt = cboost * t/((cboost-1)*t + 1);\n\t\tinf = 255 * (pow (t, gamma)*cscale + brightness) + 0.5;\n\t\tgammatable[i] = bound(0, inf, 255);\n\t}\t\n}\n\n/*\n=================\nV_CheckGamma\n=================\n*/\nstatic void QDECL V_Gamma_Callback(struct cvar_s *var, char *oldvalue)\n{\n\tBuildGammaTable (v_gammainverted.ival?v_gamma.value:(1/v_gamma.value), v_contrast.value, v_contrastboost.value, v_brightness.value);\n\tV_UpdatePalette (true);\n}\n\n#if defined(_WIN32) && !defined(_XBOX)\nvoid W32_BlowChunk(vec3_t pos, float radius)\n{\n\tvec3_t center;\n\tif (Matrix4x4_CM_Project(pos, center, r_refdef.viewangles, r_refdef.vieworg, r_refdef.fov_x, r_refdef.fov_y))\n\t{\n\t\tint mid_x = center[0]*r_refdef.vrect.width+r_refdef.vrect.x;\n\t\tint mid_y = (1-center[1])*r_refdef.vrect.height+r_refdef.vrect.y;\n\n\t\tHRGN tmp = CreateRectRgn(0,0,0,0);\n\t\tHRGN newrgn = CreateRectRgn(0,0,0,0);\n\t\tHRGN oldrgn = CreateRectRgn(0,0,0,0);\n\t\tHRGN hole = CreateEllipticRgn(mid_x-radius, mid_y-radius, mid_x+radius, mid_y+radius);\n\t\tif (GetWindowRgn(mainwindow, oldrgn) <= NULLREGION)\n\t\t{\n\t\t\tRECT rect;\n\t\t\tDeleteObject(oldrgn);\n\t\t\tGetWindowRect(mainwindow, &rect);\n\t\t\toldrgn = CreateRectRgn(0,0,rect.right-rect.left,rect.bottom-rect.top);\n\t\t}\n\t\tCombineRgn(tmp, oldrgn, hole, RGN_XOR);\n\t\tCombineRgn(newrgn, oldrgn, tmp, RGN_AND);\n\t\tDeleteObject(oldrgn);\n\t\tDeleteObject(hole);\n\t\tDeleteObject(tmp);\n\t\tSetWindowRgn(mainwindow, newrgn, TRUE);\n\t}\n}\n#endif\n\n\n/*\n===============\nV_ParseDamage\n===============\n*/\nvoid V_ParseDamage (playerview_t *pv)\n{\n\tint\t\tarmor, blood;\n\tvec3_t\tfrom;\n\tint\t\ti;\n\tvec3_t\tforward, right, up;\n\tfloat\tside;\n\tfloat\tcount;\n\n\tarmor = MSG_ReadByte ();\n\tblood = MSG_ReadByte ();\n\tfor (i=0 ; i<3 ; i++)\n\t\tfrom[i] = MSG_ReadCoord ();\n\n\tpv->faceanimtime = cl.time + 0.2;\t\t// but sbar face into pain frame\n\n#if defined(_WIN32) && !defined(MINIMAL) && !defined(_XBOX)\n\tif (itburnsitburnsmakeitstop.value > 0)\n\t\tW32_BlowChunk(from, (armor+blood) * itburnsitburnsmakeitstop.value);\n#endif\n\n#ifdef CSQC_DAT\n\tif (CSQC_Parse_Damage(pv-cl.playerview, armor, blood, from))\n\t\treturn;\n#endif\n\n\tcount = blood*0.5 + armor*0.5;\n\tif (count < 10)\n\t\tcount = 10;\n\n#if defined(ANDROID) && !defined(FTE_SDL)\n\t//later versions of android might support strength values, but the simple standard interface is duration only.\n\tSys_Vibrate(count);\n#endif\n\n\tif (v_damagecshift.value >= 0)\n\t\tpv->cshifts[CSHIFT_DAMAGE].percent += 3*count*v_damagecshift.value;\n\telse\n\t\tpv->cshifts[CSHIFT_DAMAGE].percent += 3*count;\n\tif (pv->cshifts[CSHIFT_DAMAGE].percent < 0)\n\t\tpv->cshifts[CSHIFT_DAMAGE].percent = 0;\n\tif (pv->cshifts[CSHIFT_DAMAGE].percent > 150)\n\t\tpv->cshifts[CSHIFT_DAMAGE].percent = 150;\n\n\tif (armor > blood)\n\t{\n\t\tpv->cshifts[CSHIFT_DAMAGE].destcolor[0] = 200;\n\t\tpv->cshifts[CSHIFT_DAMAGE].destcolor[1] = 100;\n\t\tpv->cshifts[CSHIFT_DAMAGE].destcolor[2] = 100;\n\t}\n\telse if (armor)\n\t{\n\t\tpv->cshifts[CSHIFT_DAMAGE].destcolor[0] = 220;\n\t\tpv->cshifts[CSHIFT_DAMAGE].destcolor[1] = 50;\n\t\tpv->cshifts[CSHIFT_DAMAGE].destcolor[2] = 50;\n\t}\n\telse\n\t{\n\t\tpv->cshifts[CSHIFT_DAMAGE].destcolor[0] = 255;\n\t\tpv->cshifts[CSHIFT_DAMAGE].destcolor[1] = 0;\n\t\tpv->cshifts[CSHIFT_DAMAGE].destcolor[2] = 0;\n\t}\n\n//\n// calculate view angle kicks\n//\n\tVectorSubtract (from, pv->simorg, from);\n\tVectorNormalize (from);\n\n\tAngleVectors (pv->simangles, forward, right, up);\n\n\tside = DotProduct (from, right);\n\tpv->v_dmg_roll = count*side*v_kickroll.value;\n\n\tside = DotProduct (from, forward);\n\tpv->v_dmg_pitch = count*side*v_kickpitch.value;\n\n\tpv->v_dmg_time = v_kicktime.value;\n}\n\n\n/*\n==================\nV_cshift_f\n==================\n*/\nvoid V_cshift_f (void)\n{\n\tint r, g, b, p;\n\tplayerview_t *pv = &cl.playerview[CL_TargettedSplit(true)];\n\n\tr = g = b = p = 0;\n\n\tif (Cmd_Argc() >= 5)\n\t{\n\t\tr = atoi(Cmd_Argv(1));\n\t\tg = atoi(Cmd_Argv(2));\n\t\tb = atoi(Cmd_Argv(3));\n\t\tp = atoi(Cmd_Argv(4));\n\t}\n\n\tif (Cmd_FromGamecode())\n\t{\n\t\tif (Cmd_Argc() >= 5)\n\t\t{\n\t\t\tqboolean term = false;\n\t\t\tint i;\n\t\t\tchar *c = Cmd_Argv(4);\n\n\t\t\t// malice jumbles commands into a v_cshift so this attempts to fix\n\t\t\twhile (isdigit(*c) || *c == '.')\n\t\t\t\tc++;\n\n\t\t\tif (*c)\n\t\t\t{\n\t\t\t\tCbuf_AddText(c, RESTRICT_SERVER);\n\t\t\t\tterm = true;\n\t\t\t}\n\t\t\tfor (i = 5; i < Cmd_Argc(); i++)\n\t\t\t{\n\t\t\t\tCbuf_AddText(\" \", RESTRICT_SERVER);\n\t\t\t\tCbuf_AddText(Cmd_Argv(i), RESTRICT_SERVER);\n\t\t\t\tterm = true;\n\t\t\t}\n\t\t\tif (term)\n\t\t\t\tCbuf_AddText(\"\\n\", RESTRICT_SERVER);\n\t\t}\n\t\telse if (Cmd_Argc() > 1)\n\t\t\tCon_DPrintf(\"broken v_cshift from gamecode\\n\");\n\n\t\t// ensure we always clear out or set for nehahra\n\t\tpv->cshifts[CSHIFT_SERVER].destcolor[0] = r;\n\t\tpv->cshifts[CSHIFT_SERVER].destcolor[1] = g;\n\t\tpv->cshifts[CSHIFT_SERVER].destcolor[2] = b;\n\t\tpv->cshifts[CSHIFT_SERVER].percent = p;\n\t\treturn;\n\t}\n\n\tif (Cmd_Argc() != 5 && Cmd_Argc() != 1)\n\t{\n\t\tCon_Printf(\"v_cshift: v_cshift <r> <g> <b> <alpha>\\n\");\n\t\treturn;\n\t}\n\n\t//always empty, to match vanilla.\n\tCvar_Set(&v_cshift_empty, va(\"%d %d %d %d\", r, g, b, p));\n}\n\n\n/*\n==================\nV_BonusFlash_f\n\nWhen you run over an item, the server sends this command\n==================\n*/\nvoid V_BonusFlash_f (void)\n{\n\tplayerview_t *pv = &cl.playerview[CL_TargettedSplit(true)];\n\tfloat frac;\n\tif (!gl_cshiftenabled.ival)\n\t\tfrac = 0;\n\telse if (Cmd_FromGamecode())\n\t\tfrac = v_bonusflash.value;\n\telse\n\t\tfrac = 1;\n\n\t{\n\t\t//still adheres to gl_cshiftpercent even when forced.\n\t\tfloat minfrac = atof(Cmd_Argv(5));\n\t\tif (frac < minfrac)\n\t\t\tfrac = minfrac;\n\t}\n\n\tfrac *= gl_cshiftpercent.value / 100.0;\n\n\tif (frac)\n\t{\n\t\tif (Cmd_Argc() > 1)\n\t\t{\t//this is how I understand DP expects them.\n\t\t\tpv->cshifts[CSHIFT_BONUS].destcolor[0] = atof(Cmd_Argv(1))*255;\n\t\t\tpv->cshifts[CSHIFT_BONUS].destcolor[1] = atof(Cmd_Argv(2))*255;\n\t\t\tpv->cshifts[CSHIFT_BONUS].destcolor[2] = atof(Cmd_Argv(3))*255;\n\t\t\tpv->cshifts[CSHIFT_BONUS].percent = atof(Cmd_Argv(4))*255*frac;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tpv->cshifts[CSHIFT_BONUS].destcolor[0] = 215;\n\t\t\tpv->cshifts[CSHIFT_BONUS].destcolor[1] = 186;\n\t\t\tpv->cshifts[CSHIFT_BONUS].destcolor[2] = 69;\n\t\t\tpv->cshifts[CSHIFT_BONUS].percent = 50*frac;\n\t\t}\n\t}\n}\nvoid V_DarkFlash_f (void)\n{\n\tplayerview_t *pv = &cl.playerview[CL_TargettedSplit(true)];\n\tfloat frac;\n\tif (!gl_cshiftenabled.ival)\n\t\tfrac = 0;\n\telse\n\t\tfrac = 1;\n\tfrac *= gl_cshiftpercent.value / 100.0;\n\n\tpv->cshifts[CSHIFT_BONUS].destcolor[0] = 0;\n\tpv->cshifts[CSHIFT_BONUS].destcolor[1] = 0;\n\tpv->cshifts[CSHIFT_BONUS].destcolor[2] = 0;\n\tpv->cshifts[CSHIFT_BONUS].percent = 255*frac;\n}\nvoid V_WhiteFlash_f (void)\n{\n\tplayerview_t *pv = &cl.playerview[CL_TargettedSplit(true)];\n\tfloat frac;\n\tif (!gl_cshiftenabled.ival)\n\t\tfrac = 0;\n\telse\n\t\tfrac = 1;\n\tfrac *= gl_cshiftpercent.value / 100.0;\n\n\tpv->cshifts[CSHIFT_BONUS].destcolor[0] = 255;\n\tpv->cshifts[CSHIFT_BONUS].destcolor[1] = 255;\n\tpv->cshifts[CSHIFT_BONUS].destcolor[2] = 255;\n\tpv->cshifts[CSHIFT_BONUS].percent = 255*frac;\n}\n\n/*\n=============\nV_SetContentsColor\n\nUnderwater, lava, etc each has a color shift\n\nFIXME: Uses Q1 contents\n=============\n*/\nvoid V_SetContentsColor (int contents)\n{\n\tint i;\n\tplayerview_t *pv = r_refdef.playerview;\n\tcvar_t *v;\n\n\tif (contents & FTECONTENTS_LAVA)\n\t\tv = &v_cshift_lava;\n\telse if (contents & (FTECONTENTS_SLIME | FTECONTENTS_SOLID))\n\t\tv = &v_cshift_slime;\n\telse if (contents & FTECONTENTS_WATER)\n\t\tv = &v_cshift_water;\n\telse\n\t\tv = &v_cshift_empty;\n\n\tpv->cshifts[CSHIFT_CONTENTS].destcolor[0]\t= v->vec4[0];\n\tpv->cshifts[CSHIFT_CONTENTS].destcolor[1]\t= v->vec4[1];\n\tpv->cshifts[CSHIFT_CONTENTS].destcolor[2]\t= v->vec4[2];\n\tpv->cshifts[CSHIFT_CONTENTS].percent\t\t= v->vec4[3] * v_contentblend.value;\n\n\tif (pv->cshifts[CSHIFT_CONTENTS].percent)\n\t{\t//bound contents so it can't go negative\n\t\tif (pv->cshifts[CSHIFT_CONTENTS].percent < 0)\n\t\t\tpv->cshifts[CSHIFT_CONTENTS].percent = 0;\n\n\t\tfor (i = 0; i < 3; i++)\n\t\t\tif (pv->cshifts[CSHIFT_CONTENTS].destcolor[0] < 0)\n\t\t\t\tpv->cshifts[CSHIFT_CONTENTS].destcolor[0] = 0;\n\t}\n}\n\n/*\n=============\nV_CalcPowerupCshift\n=============\n*/\nvoid V_CalcPowerupCshift (playerview_t *pv)\n{\n#ifdef QUAKESTATS\n\tint im = pv->stats[STAT_ITEMS];\n\n\tif (im & IT_QUAD)\n\t{\n\t\tpv->cshifts[CSHIFT_POWERUP].destcolor[0] = 0;\n\t\tpv->cshifts[CSHIFT_POWERUP].destcolor[1] = 0;\n\t\tpv->cshifts[CSHIFT_POWERUP].destcolor[2] = 255;\n\t\tpv->cshifts[CSHIFT_POWERUP].percent = 30*v_quadcshift.value;\n\t}\n\telse if (im & IT_SUIT)\n\t{\n\t\tpv->cshifts[CSHIFT_POWERUP].destcolor[0] = 0;\n\t\tpv->cshifts[CSHIFT_POWERUP].destcolor[1] = 255;\n\t\tpv->cshifts[CSHIFT_POWERUP].destcolor[2] = 0;\n\t\tpv->cshifts[CSHIFT_POWERUP].percent = 20*v_suitcshift.value;\n\t}\n\telse if (im & IT_INVISIBILITY)\n\t{\n\t\tpv->cshifts[CSHIFT_POWERUP].destcolor[0] = 100;\n\t\tpv->cshifts[CSHIFT_POWERUP].destcolor[1] = 100;\n\t\tpv->cshifts[CSHIFT_POWERUP].destcolor[2] = 100;\n\t\tpv->cshifts[CSHIFT_POWERUP].percent = 100*v_ringcshift.value;\n\t}\n\telse if (im & IT_INVULNERABILITY)\n\t{\n\t\tpv->cshifts[CSHIFT_POWERUP].destcolor[0] = 255;\n\t\tpv->cshifts[CSHIFT_POWERUP].destcolor[1] = 255;\n\t\tpv->cshifts[CSHIFT_POWERUP].destcolor[2] = 0;\n\t\tpv->cshifts[CSHIFT_POWERUP].percent = 30*v_pentcshift.value;\n\t}\n\telse\n\t\tpv->cshifts[CSHIFT_POWERUP].percent = 0;\n\n\tif (pv->cshifts[CSHIFT_POWERUP].percent<0)\n\t\tpv->cshifts[CSHIFT_POWERUP].percent=0;\n#endif\n}\n\n\n/*\n=============\nV_CalcBlend\n=============\n*/\nvoid V_CalcBlend (float *hw_blend)\n{\n\textern qboolean r2d_canhwgamma;\n\tfloat\ta2;\n\tint\t\tj, seat;\n\tfloat *blend;\n\tplayerview_t *pv;\n\n\tmemset(hw_blend, 0, sizeof(float)*4);\n\tfor (seat = 0; seat < cl.splitclients; seat++)\n\t{\n\t\tpv = &cl.playerview[seat];\n\t\tmemset(pv->screentint, 0, sizeof(pv->screentint));\n\t\tmemset(pv->bordertint, 0, sizeof(pv->bordertint));\n\n\t\t//don't apply it to the server, we'll blend the two later if the user has no hardware gamma (if they do have it, we use just the server specified value) This way we avoid winnt users having a cheat with flashbangs and stuff.\n\t\tfor (j=0 ; j<NUM_CSHIFTS ; j++)\n\t\t{\n\t\t\tif ((j == CSHIFT_SERVER&&!cls.demoplayback&&!pv->spectator) || j == CSHIFT_BONUS)\n\t\t\t{\n\t\t\t\ta2 = pv->cshifts[j].percent / 255.0;\t//don't allow modification of this one.\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (!gl_cshiftpercent.value || !gl_cshiftenabled.ival)\n\t\t\t\t\tcontinue;\n\n\t\t\t\ta2 = ((pv->cshifts[j].percent * gl_cshiftpercent.value) / 100.0) / 255.0;\n\t\t\t}\n\n\t\t\tif (a2 <= 0)\n\t\t\t\tcontinue;\n\n\t\t\tif (j == CSHIFT_SERVER)\n\t\t\t{\n\t\t\t\t/*server blend always goes into sw, ALWAYS. hardware is too unreliable.*/\n\t\t\t\tblend = pv->screentint;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t/*flashing things should not change hardware gamma ramps - windows is too slow*/\n\t\t\t\t/*splitscreen should only use hw gamma ramps if they're all equal, and they're probably not*/\n\t\t\t\t/*hw blends may also not be supported or may be disabled*/\n\t\t\t\tif (gl_cshiftenabled.ival == 2)\n\t\t\t\t\tblend = pv->bordertint;\t//show the colours only on the borders so we don't blind ourselves\n\t\t\t\telse if (j == CSHIFT_BONUS || j == CSHIFT_DAMAGE || gl_nohwblend.ival || !r2d_canhwgamma || cl.splitclients > 1)\n\t\t\t\t\tblend = pv->screentint;\n\t\t\t\telse\t//powerup or contents?\n\t\t\t\t\tblend = hw_blend;\n\t\t\t}\n\n\t\t\tblend[3] = blend[3] + a2*(1-blend[3]);\n\t\t\ta2 = a2/blend[3];\n\t\t\tblend[0] = blend[0]*(1-a2) + pv->cshifts[j].destcolor[0]*a2/255.0;\n\t\t\tblend[1] = blend[1]*(1-a2) + pv->cshifts[j].destcolor[1]*a2/255.0;\n\t\t\tblend[2] = blend[2]*(1-a2) + pv->cshifts[j].destcolor[2]*a2/255.0;\n\t\t}\n\n\t\tif (pv->screentint[3] > 1)\n\t\t\tpv->screentint[3] = 1;\n\t\tif (pv->screentint[3] < 0)\n\t\t\tpv->screentint[3] = 0;\n\t}\n\tfor (; seat < MAX_SPLITS; seat++)\n\t{\n\t\tpv = &cl.playerview[seat];\n\t\tmemset(pv->screentint, 0, sizeof(pv->screentint));\n\t\tmemset(pv->bordertint, 0, sizeof(pv->bordertint));\n\t}\n\tif (hw_blend[3] > 1)\n\t\thw_blend[3] = 1;\n\tif (hw_blend[3] < 0)\n\t\thw_blend[3] = 0;\n}\n\n/*\n=============\nV_UpdatePalette\n=============\n*/\nvoid V_UpdatePalette (qboolean force)\n{\n\textern\tqboolean r2d_canhwgamma;\n\tint\t\ti;\n\tfloat\tnewhw_blend[4];\n\tint\t\tir, ig, ib;\n\tfloat\tftime;\n\tqboolean applied;\n\tstatic double oldtime;\n\tRSpeedMark();\n\n\tftime = cl.time - oldtime;\n\toldtime = cl.time;\n\tif (ftime < 0)\n\t\tftime = 0;\n\n\tfor (i = 0; i < MAX_SPLITS; i++)\n\t{\n\t\tplayerview_t *pv = &cl.playerview[i];\n\t\tV_CalcPowerupCshift(pv);\n\t// drop the damage value\n\t\tpv->cshifts[CSHIFT_DAMAGE].percent -= ftime*150;\n\t\tif (pv->cshifts[CSHIFT_DAMAGE].percent <= 0)\n\t\t\tpv->cshifts[CSHIFT_DAMAGE].percent = 0;\n\n\t// drop the bonus value\n\t\tpv->cshifts[CSHIFT_BONUS].percent -= ftime*100;\n\t\tif (pv->cshifts[CSHIFT_BONUS].percent <= 0)\n\t\t\tpv->cshifts[CSHIFT_BONUS].percent = 0;\n\t}\n\n\tV_CalcBlend(newhw_blend);\n\n\tif (hw_blend[0] != newhw_blend[0] || hw_blend[1] != newhw_blend[1] || hw_blend[2] != newhw_blend[2] || hw_blend[3] != newhw_blend[3] || force)\n\t{\n\t\tfloat r,g,b,a;\n\t\tstatic unsigned short\t\tallramps[3*2048];\n\t\tunsigned int rampsize = min(vid.gammarampsize, countof(allramps)/3);\n\t\tunsigned short *ramps[3] = {&allramps[0],&allramps[rampsize],&allramps[rampsize*2]};\n\t\tVector4Copy(newhw_blend, hw_blend);\n\n\t\ta = hw_blend[3];\n\t\tr = 255*hw_blend[0]*a;\n\t\tg = 255*hw_blend[1]*a;\n\t\tb = 255*hw_blend[2]*a;\n\n\t\ta = 1-a;\n\t\ta *= 256.0/rampsize;\n\t\tfor (i=0 ; i < rampsize; i++)\n\t\t{\n\t\t\tir = i*a + r;\n\t\t\tig = i*a + g;\n\t\t\tib = i*a + b;\n\t\t\tif (ir > 255)\n\t\t\t\tir = 255;\n\t\t\tif (ig > 255)\n\t\t\t\tig = 255;\n\t\t\tif (ib > 255)\n\t\t\t\tib = 255;\n\n\t\t\t//FIXME: shit precision\n\t\t\tramps[0][i] = gammatable[ir]<<8;\n\t\t\tramps[1][i] = gammatable[ig]<<8;\n\t\t\tramps[2][i] = gammatable[ib]<<8;\n\n#ifdef _WIN32\n\t\t\t//'In fact, any entry in the ramp must be within 32768 of the identity value'\n\t\t\t//so lets try to bound it so that it doesn't randomly fail.\n\t\t\tir = (i<<8)|i;\n\t\t\tramps[0][i] = bound(ir-32767, ramps[0][i], ir+32767);\n\t\t\tramps[1][i] = bound(ir-32767, ramps[1][i], ir+32767);\n\t\t\tramps[2][i] = bound(ir-32767, ramps[2][i], ir+32767);\n#endif\n\t\t}\n\n\t\tif (qrenderer)\n\t\t{\n\t\t\tapplied = rf->VID_ApplyGammaRamps (rampsize, allramps);\n\t\t\tif (!applied && r2d_canhwgamma)\n\t\t\t\trf->VID_ApplyGammaRamps (0, NULL);\n\t\t\tr2d_canhwgamma = applied;\n\t\t}\n\t}\n\n\tRSpeedEnd(RSPEED_PALETTEFLASHES);\n}\n\n/*\n=============\nV_UpdatePalette\n=============\n*/\n\nvoid V_ClearCShifts (void)\n{\n\tint i, seat;\n\n\tfor (seat = 0; seat < MAX_SPLITS; seat++)\n\t\tfor (i = 0; i < NUM_CSHIFTS; i++)\n\t\t\tcl.playerview[seat].cshifts[i].percent = 0;\n}\n\n/*\n==============================================================================\n\n\t\t\t\t\t\tVIEW RENDERING\n\n==============================================================================\n*/\n\nfloat angledelta (float a)\n{\n\ta = anglemod(a);\n\tif (a > 180)\n\t\ta -= 360;\n\treturn a;\n}\n\n/*\n==================\nCalcGunAngle\n==================\n*/\nvoid V_CalcGunPositionAngle (playerview_t *pv, float bob)\n{\n\tfloat\tyaw, pitch, move;\n\tstatic float oldyaw = 0;\n\tstatic float oldpitch = 0;\n\tvec3_t vw_angles;\n\tint i;\n\tfloat xyspeed;\n\tvec3_t xyvel;\n\n\tyaw = r_refdef.viewangles[YAW];\n\tpitch = -r_refdef.viewangles[PITCH];\n\n\tyaw = angledelta(yaw - r_refdef.viewangles[YAW]) * 0.4;\n\tif (yaw > 10)\n\t\tyaw = 10;\n\tif (yaw < -10)\n\t\tyaw = -10;\n\tpitch = angledelta(-pitch - r_refdef.viewangles[PITCH]) * 0.4;\n\tif (pitch > 10)\n\t\tpitch = 10;\n\tif (pitch < -10)\n\t\tpitch = -10;\n\tmove = host_frametime*20;\n\tif (yaw > oldyaw)\n\t{\n\t\tif (oldyaw + move < yaw)\n\t\t\tyaw = oldyaw + move;\n\t}\n\telse\n\t{\n\t\tif (oldyaw - move > yaw)\n\t\t\tyaw = oldyaw - move;\n\t}\n\n\tif (pitch > oldpitch)\n\t{\n\t\tif (oldpitch + move < pitch)\n\t\t\tpitch = oldpitch + move;\n\t}\n\telse\n\t{\n\t\tif (oldpitch - move > pitch)\n\t\t\tpitch = oldpitch - move;\n\t}\n\n\toldyaw = yaw;\n\toldpitch = pitch;\n\n\tvw_angles[YAW] = r_refdef.viewangles[YAW] + yaw;\n\tvw_angles[PITCH] = r_refdef.viewangles[PITCH] + pitch;\n\n\tvw_angles[YAW] = r_refdef.viewangles[YAW];\n\tvw_angles[PITCH] = r_refdef.viewangles[PITCH];\n\tvw_angles[ROLL] = r_refdef.viewangles[ROLL];\n\n\n\tAngleVectors(vw_angles, r_refdef.weaponmatrix[0], r_refdef.weaponmatrix[1], r_refdef.weaponmatrix[2]);\n\tVectorInverse(r_refdef.weaponmatrix[1]);\n\n\n\tVectorCopy (r_refdef.vieworg, r_refdef.weaponmatrix[3]);\n\tfor (i=0 ; i<3 ; i++)\n\t{\n\t\tr_refdef.weaponmatrix[3][i] += r_refdef.weaponmatrix[0][i]*bob*0.4;\n//\t\tr_refdef.weaponmatrix[3][i] += r_refdef.weaponmatrix[1][i]*sin(cl.time*5.5342452354235)*0.1;\n//\t\tr_refdef.weaponmatrix[3][i] += r_refdef.weaponmatrix[2][i]*bob*0.8;\n\t}\n\n\tVectorMA(pv->simvel, -DotProduct(pv->simvel, pv->gravitydir), pv->gravitydir, xyvel);\n\txyspeed = VectorLength(xyvel);\n\t//FIXME: clamp\n\n\t//FIXME: cl_followmodel should lag the viewmodel's position relative to the view\n\t//FIXME: cl_leanmodel should lag the viewmodel's angles relative to the view\n\n// fudge position around to keep amount of weapon visible\n// roughly equal with different FOV\n//FIXME: should use y fov, not viewsize.\n\tif (r_refdef.drawsbar && v_viewmodel_quake.ival)\t//no sbar = no viewsize cvar.\n\t{\n\t\tif (scr_viewsize.value == 110)\n\t\t\tr_refdef.weaponmatrix[3][2] += 1;\n\t\telse if (scr_viewsize.value == 100)\n\t\t\tr_refdef.weaponmatrix[3][2] += 2;\n\t\telse if (scr_viewsize.value == 90)\n\t\t\tr_refdef.weaponmatrix[3][2] += 1;\n\t\telse if (scr_viewsize.value == 80)\n\t\t\tr_refdef.weaponmatrix[3][2] += 0.5;\n\t}\n\n\n\n\tmemcpy(r_refdef.weaponmatrix_bob, r_refdef.weaponmatrix, sizeof(r_refdef.weaponmatrix_bob));\n\tif (cl_bobmodel.value)\n\t{\t//bobmodel causes the viewmodel to bob relative to the view.\n\t\tfloat s = pv->bobtime * cl_bobmodel_speed.value, v;\n\t\tv = xyspeed * 0.01 * cl_bobmodel_side.value * /*cl_viewmodel_scale.value * */sin(s);\n\t\tVectorMA(r_refdef.weaponmatrix_bob[3], v, r_refdef.weaponmatrix[1], r_refdef.weaponmatrix_bob[3]);\n\t\tv = xyspeed * 0.01 * cl_bobmodel_up.value * /*cl_viewmodel_scale.value * */cos(2*s);\n\t\tVectorMA(r_refdef.weaponmatrix_bob[3], v, r_refdef.weaponmatrix[2], r_refdef.weaponmatrix_bob[3]);\n\t}\n}\n\n/*\n==============\nV_BoundOffsets\n==============\n*/\nvoid V_BoundOffsets (int pnum)\n{\n// absolutely bound refresh reletive to entity clipping hull\n// so the view can never be inside a solid wall\n\n\tif (r_refdef.vieworg[0] < cl.playerview[pnum].simorg[0] - 14)\n\t\tr_refdef.vieworg[0] = cl.playerview[pnum].simorg[0] - 14;\n\telse if (r_refdef.vieworg[0] > cl.playerview[pnum].simorg[0] + 14)\n\t\tr_refdef.vieworg[0] = cl.playerview[pnum].simorg[0] + 14;\n\tif (r_refdef.vieworg[1] < cl.playerview[pnum].simorg[1] - 14)\n\t\tr_refdef.vieworg[1] = cl.playerview[pnum].simorg[1] - 14;\n\telse if (r_refdef.vieworg[1] > cl.playerview[pnum].simorg[1] + 14)\n\t\tr_refdef.vieworg[1] = cl.playerview[pnum].simorg[1] + 14;\n\tif (r_refdef.vieworg[2] < cl.playerview[pnum].simorg[2] - 22)\n\t\tr_refdef.vieworg[2] = cl.playerview[pnum].simorg[2] - 22;\n\telse if (r_refdef.vieworg[2] > cl.playerview[pnum].simorg[2] + 30)\n\t\tr_refdef.vieworg[2] = cl.playerview[pnum].simorg[2] + 30;\n}\n\n/*\n==============\nV_AddIdle\n\nIdle swaying\n==============\n*/\nvoid V_AddIdle (playerview_t *pv)\n{\n\t//defaults: for use if idlescale is locked and the var isn't.\n\tfloat yaw_cycle\t\t= 2;\n\tfloat roll_cycle\t= 0.5;\n\tfloat pitch_cycle\t= 1;\n\tfloat yaw_level\t\t= 0.3;\n\tfloat roll_level\t= 0.1;\n\tfloat pitch_level\t= 0.3;\n\n\tif (v_iyaw_cycle.flags & CVAR_SERVEROVERRIDE || !(v_idlescale.flags & CVAR_SERVEROVERRIDE))\n\t\tyaw_cycle = v_iyaw_cycle.value;\n\tif (v_iroll_cycle.flags & CVAR_SERVEROVERRIDE || !(v_idlescale.flags & CVAR_SERVEROVERRIDE))\n\t\troll_cycle = v_iroll_cycle.value;\n\tif (v_ipitch_cycle.flags & CVAR_SERVEROVERRIDE || !(v_idlescale.flags & CVAR_SERVEROVERRIDE))\n\t\tpitch_cycle = v_ipitch_cycle.value;\n\n\tif (v_iyaw_level.flags & CVAR_SERVEROVERRIDE || !(v_idlescale.flags & CVAR_SERVEROVERRIDE))\n\t\tyaw_level = v_iyaw_level.value;\n\tif (v_iroll_level.flags & CVAR_SERVEROVERRIDE || !(v_idlescale.flags & CVAR_SERVEROVERRIDE))\n\t\troll_level = v_iroll_level.value;\n\tif (v_ipitch_level.flags & CVAR_SERVEROVERRIDE || !(v_idlescale.flags & CVAR_SERVEROVERRIDE))\n\t\tpitch_level = v_ipitch_level.value;\n\n\tr_refdef.viewangles[ROLL] += v_idlescale.value * sin(cl.time*roll_cycle) * roll_level;\n\tr_refdef.viewangles[PITCH] += v_idlescale.value * sin(cl.time*pitch_cycle) * pitch_level;\n\tr_refdef.viewangles[YAW] += v_idlescale.value * sin(cl.time*yaw_cycle) * yaw_level;\n\n//\tpv->viewent.angles[ROLL] -= v_idlescale.value * sin(cl.time*roll_cycle) * roll_level;\n//\tpv->viewent.angles[PITCH] -= v_idlescale.value * sin(cl.time*pitch_cycle) * pitch_level;\n//\tpv->viewent.angles[YAW] -= v_idlescale.value * sin(cl.time*yaw_cycle) * yaw_level;\n}\n\n\n/*\n==============\nV_CalcViewRoll\n\nRoll is induced by movement and damage\n==============\n*/\nvoid V_CalcViewRoll (playerview_t *pv)\n{\n\tfloat\t\tside;\n\tfloat\tadjspeed;\n\n\tside = V_CalcRoll (pv->simangles, pv->simvel);\n\n\tadjspeed = cl_rollalpha.value * bound(1, fabs(cl_rollangle.value), 45);\n\tif (side > pv->rollangle)\n\t{\n\t\tpv->rollangle += host_frametime * adjspeed;\n\t\tif (pv->rollangle > side)\n\t\t\tpv->rollangle = side;\n\t}\n\telse if (side < pv->rollangle)\n\t{\n\t\tpv->rollangle -= host_frametime * adjspeed;\n\t\tif (pv->rollangle < side)\n\t\t\tpv->rollangle = side;\n\t}\n\tr_refdef.viewangles[ROLL] += pv->rollangle;\n\n\tif (pv->v_dmg_time > 0)\n\t{\n\t\tr_refdef.viewangles[ROLL] += pv->v_dmg_time/v_kicktime.value*pv->v_dmg_roll;\n\t\tr_refdef.viewangles[PITCH] += pv->v_dmg_time/v_kicktime.value*pv->v_dmg_pitch;\n\t\tpv->v_dmg_time -= host_frametime;\n\t}\n\n}\n\n\n/*\n==================\nV_CalcIntermissionRefdef\n\n==================\n*/\nvoid V_CalcIntermissionRefdef (playerview_t *pv)\n{\n\tfloat\t\told;\n\n\tVectorCopy (pv->simorg, r_refdef.vieworg);\n\tVectorCopy (pv->simangles, r_refdef.viewangles);\n\n\tif (cl.intermissionmode == IM_H2FINALE)\n\t\tVectorMA(r_refdef.vieworg, -pv->viewheight, pv->gravitydir, r_refdef.vieworg);\n\n// always idle in intermission\n\told = v_idlescale.value;\n\tv_idlescale.value = 1;\n\tV_AddIdle (pv);\n\tv_idlescale.value = old;\n}\n\nfloat CalcFov (float fov_x, float width, float height)\n{\n\tfloat\ta;\n\tfloat\tx;\n\n\tif (fov_x <= 0 || fov_x > 179)\n\t\tSys_Error (\"Bad fov: %f\", fov_x);\n\n\tx = fov_x/360*M_PI;\n\tx = tan(x);\n\tx = width/x;\n\n\ta = atan (height/x);\n\n\ta = a*360/M_PI;\n\n\treturn a;\n}\nstatic void V_CalcAFov(float afov, float *x, float *y, float w, float h)\n{\n\textern cvar_t scr_fov_mode;\n\textern cvar_t r_stereo_separation;\n\tint mode = scr_fov_mode.ival;\n\tif (r_refdef.stereomethod == STEREO_CROSSEYED && r_stereo_separation.value)\n\t\tw *= 0.5;\n\n\tafov = bound(0.001, afov, 170);\n\nrestart:\n\tswitch(mode)\n\t{\n\tdefault:\n\tcase 0:\t//maj-\n\t\tif (h > w)\n\t\t{\t//fov specified is interpreted as the horizontal fov at quake's original res: 640*432 (ie: with the sbar removed)...\n\t\t//\tafov = CalcFov(afov, 640, 432);\n\t\t\tmode = 3;\t//vertical \n\t\t}\n\t\telse\n\t\t\tmode = 2;\t//horizontal\n\t\tgoto restart;\n\tcase 1:\t//min+\n\t\tif (h < w)\n\t\t\tmode = 3;\t//vertical \n\t\telse\n\t\t\tmode = 2;\t//horizontal\n\t\tgoto restart;\n\tcase 2:\t//horizontal\n\t\t*x = afov;\n\t\t*y = CalcFov(afov, w, h);\n\n\t\tif (*y > 170)\n\t\t{\n\t\t\t*y = 170;\n\t\t\t*x = CalcFov(170, h, w);\n\t\t}\n\t\tbreak;\n\tcase 3:\t//vertical\n\t\t*y = afov;\n\t\t*x = CalcFov(afov, h, w);\n\n\t\tif (*x > 170)\n\t\t{\t//don't allow screwed fovs.\n\t\t\t*x = 170;\n\t\t\t*y = CalcFov(afov, w, h);\n\t\t}\n\t\tbreak;\n\n\tcase 4:\t//wide 4:3, to match vanilla more closely.\n\t\tif (w/4 < h/3)\n\t\t{\t//don't bug out if they're running on a tall screen\n\t\t\tmode = 3;\n\t\t\tgoto restart;\n\t\t}\n\t\t*y = tan(afov * M_PI / 360.0) * (3.0 / 4.0);\n\t\t*x = *y * w / h;\n\t\t*x = atan(*x) * (360.0 / M_PI);\n\t\t*y = atan(*y) * (360.0 / M_PI);\n\t\tbreak;\n\t}\n}\nvoid V_ApplyAFov(playerview_t *pv)\n{\n\t//explicit fov overrides aproximate fov.\n\t//aproximate fov is our regular fov value. explicit is settable by gamecode for weird aspect ratios\n\tif (!r_refdef.fov_x || !r_refdef.fov_y)\n\t{\n\t\tfloat afov = r_refdef.afov;\n#ifdef Q2CLIENT\n\t\tif (pv->forcefov>0)\n\t\t\tafov = pv->forcefov;\n#endif\n\t\tif (!afov)\t//make sure its sensible.\n\t\t{\n\t\t\tafov = scr_fov.value;\n#ifdef QUAKESTATS\n\t\t\tif (pv && pv->statsf[STAT_VIEWZOOM])\t//match dp - viewzoom only happens when defaulted.\n\t\t\t\tafov *= pv->statsf[STAT_VIEWZOOM]/STAT_VIEWZOOM_SCALE;\n#endif\n\t\t}\n\n\t\tV_CalcAFov(afov, &r_refdef.fov_x, &r_refdef.fov_y, (r_refdef.vrect.width*r_refdef.pxrect.width)/vid.fbvwidth, (r_refdef.vrect.height*r_refdef.pxrect.height)/vid.fbvheight);\n\t}\n\n\tif (!r_refdef.fovv_x || !r_refdef.fovv_y)\n\t{\n\t\tfloat afov = scr_fov_viewmodel.value;\n\t\tif (afov)\n\t\t\tV_CalcAFov(afov, &r_refdef.fovv_x, &r_refdef.fovv_y, (r_refdef.vrect.width*r_refdef.pxrect.width)/vid.fbvwidth, (r_refdef.vrect.height*r_refdef.pxrect.height)/vid.fbvheight);\n\t\telse\n\t\t{\n\t\t\tr_refdef.fovv_x = r_refdef.fov_x;\n\t\t\tr_refdef.fovv_y = r_refdef.fov_y;\n\t\t}\n\t}\n\n\tif (r_refdef.useperspective)\n\t{\n\t\tif (r_refdef.mindist < 1)\n\t\t\tr_refdef.mindist = 1;\n\t\tif (r_refdef.maxdist && r_refdef.maxdist < 100)\n\t\t\tr_refdef.maxdist = 0;\t//small values should just use infinite.\n\t}\n}\n/*\n=================\nv_ApplyRefdef\n\ncalled to apply any dirty refdef bits and recalculates pending data.\n=================\n*/\nvoid V_ApplyRefdef (void)\n{\n#ifdef QUAKEHUD\n\tfloat           size;\n\tint             h;\n\tqboolean\t\tfull = false;\n#endif\n#ifdef HEXEN2\n\textern qboolean sbar_hexen2;\n#endif\n\n// force the status bar to redraw\n\tSbar_Changed ();\n\n//========================================\n\n\tif (r_refdef.playerview->gamerectknown != cls.framecount)\n\t{\n\t\tr_refdef.playerview->gamerectknown = cls.framecount;\n\t\tr_refdef.playerview->gamerect = r_refdef.grect;\n\t}\n\n#ifndef QUAKEHUD\n\tr_refdef.vrect = r_refdef.grect;\n#else\n\n\n// intermission is always full screen\n\tif (cl.intermissionmode != IM_NONE || !r_refdef.drawsbar)\n\t\tsize = 120;\n\telse\n\t\tsize = scr_viewsize.value;\n\n#ifdef Q2CLIENT\n\tif (cls.protocol == CP_QUAKE2)\t//q2 never has a hud.\n\t\tsb_lines = 0;\n\telse\n#endif\n\t     if (size >= 120)\n\t\tsb_lines = 0;           // no status bar at all\n\telse if (size >= 110)\n\t\tsb_lines = 24;          // no inventory\n\telse\n#ifdef HEXEN2\n\t\tif (sbar_hexen2)\n\t\t\tsb_lines = 46;\t//hexen2's sbar is a smidge more crampt.\n\t\telse\n#endif\n\t\tsb_lines = 24+16+8;\n\n\tif (scr_viewsize.value >= 100.0)\n\t{\n\t\tfull = true;\n\t\tsize = 100.0;\n\t}\n\telse\n\t\tsize = scr_viewsize.value;\n\n\tif (cl.intermissionmode != IM_NONE || !r_refdef.drawsbar)\n\t{\n\t\tfull = true;\n\t\tsize = 100.0;\n\t\tsb_lines = 0;\n\t}\n\tsize /= 100.0;\n\n\tif (cl_sbar.value!=1 && full)\n\t\th = r_refdef.grect.height;\n\telse\n\t\th = r_refdef.grect.height - sb_lines;\n\tif (h < 0)\n\t\th = 0;\n\n\tr_refdef.vrect.width = r_refdef.grect.width * size;\n\tif (r_refdef.vrect.width < 96)\n\t\tr_refdef.vrect.width = 96;      // min for icons\n\n\tr_refdef.vrect.height = r_refdef.grect.height * size;\n\tif (cl_sbar.value==1 || !full)\n\t{\n  \t\tif (r_refdef.vrect.height > r_refdef.grect.height - sb_lines)\n  \t\t\tr_refdef.vrect.height = r_refdef.grect.height - sb_lines;\n\t}\n\telse if (r_refdef.vrect.height > r_refdef.grect.height)\n\t\tr_refdef.vrect.height = r_refdef.grect.height;\n\tif (r_refdef.vrect.height < 0)\n\t\tr_refdef.vrect.height = 0;\n\n\tr_refdef.vrect.x = (r_refdef.grect.width - r_refdef.vrect.width)/2;\n\tif (full)\n\t\tr_refdef.vrect.y = 0;\n\telse\n\t\tr_refdef.vrect.y = (h - r_refdef.vrect.height)/2;\n\n\tr_refdef.vrect.x += r_refdef.grect.x;\n\tr_refdef.vrect.y += r_refdef.grect.y;\n#endif\n\n\tif (r_refdef.dirty & RDFD_FOV)\n\t\tV_ApplyAFov(r_refdef.playerview);\n\n\tr_refdef.dirty = 0;\n\n\tif (chase_active.ival && cls.allow_cheats)\n\t\tV_EditExternalModels(0, NULL, 0);\n\tif (v_depthsortentities.ival)\n\t\tV_DepthSortEntities(r_refdef.vieworg);\n}\n\n//if the view entities differ, removes all externalmodel flags except for adding it to the new entity, and removes weaponmodels.\n//returns the number of view entities that were stripped out\nint V_EditExternalModels(int newviewentity, entity_t *viewentities, int maxviewenties)\n{\n\tint i;\n\tint viewents = 0;\n\tfor (i = 0; i < cl_numvisedicts; )\n\t{\n\t\tif (cl_visedicts[i].keynum == newviewentity && newviewentity)\n\t\t\tcl_visedicts[i].flags |= RF_EXTERNALMODEL;\n\t\telse\n\t\t\tcl_visedicts[i].flags &= ~RF_EXTERNALMODEL;\n\n\t\tif (cl_visedicts[i].flags & RF_WEAPONMODEL)\n\t\t{\n\t\t\tif (viewents < maxviewenties)\n\t\t\t\tviewentities[viewents++] = cl_visedicts[i];\n\t\t\tmemmove(&cl_visedicts[i], &cl_visedicts[i+1], sizeof(*cl_visedicts) * (cl_numvisedicts-(i+1)));\n\t\t\tcl_numvisedicts--;\n\t\t}\n\t\telse\n\t\t\ti++;\n\t}\n\treturn viewents;\n}\n\n//this is for transparency effects, so we actually want the furthest first\n//this is contrary for overdraw performance, but transparency doesn't work like that\n//we use angles[0] to hold distance. the renderer shouldn't be using it anyway.\nstatic int QDECL V_DepthSortTwoEntities(const void *p1,const void *p2)\n{\n\tconst entity_t *a = p1;\n\tconst entity_t *b = p2;\n\n\tif (a->angles[0] < b->angles[0])\n\t\treturn 1;\n\tif (a->angles[0] > b->angles[0])\n\t\treturn -1;\n\treturn 0;\n}\nvoid V_DepthSortEntities(float *vieworg)\n{\n\tint i, j;\n\tvec3_t disp;\n\tfor (i = 0; i < cl_numvisedicts; i++)\n\t{\n\t\tif (cl_visedicts[i].flags & RF_WEAPONMODEL)\n\t\t{\t//weapon models have their own extra matrix thing going on. don't mess up because of it.\n\t\t\t//however, qsort is not stable so hide ordering in here so they still come out with the same ordering, at least with respect to each other.\n\t\t\tcl_visedicts[i].angles[0] = -1-i;\n\t\t\tcontinue;\n\t\t}\n\t\tif (cl_visedicts[i].rtype == RT_MODEL && cl_visedicts[i].model && cl_visedicts[i].model->type == mod_brush)\n\t\t{\n\t\t\tif (1)\n\t\t\t{\t//by nearest point.\n\t\t\t\tfor (j=0 ; j<3 ; j++)\n\t\t\t\t{\n\t\t\t\t\tdisp[j] = vieworg[j] - cl_visedicts[i].origin[j];\n\t\t\t\t\tdisp[j] -= bound(cl_visedicts[i].model->mins[j], disp[j], cl_visedicts[i].model->maxs[j]);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\t//by midpoint...\n\t\t\t\tVectorAdd(cl_visedicts[i].model->maxs, cl_visedicts[i].model->mins, disp);\n\t\t\t\tVectorMA(cl_visedicts[i].origin, 0.5, disp, disp);\n\t\t\t\tVectorSubtract(disp, vieworg, disp);\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tVectorSubtract(cl_visedicts[i].origin, vieworg, disp);\n\t\t}\n\t\tcl_visedicts[i].angles[0] = DotProduct(disp,disp);\t//don't bother with sqrts\n\t}\n\tqsort(cl_visedicts, cl_numvisedicts, sizeof(cl_visedicts[0]), V_DepthSortTwoEntities);\n}\n\n/*\nclears the refdef to defaults.\n*/\nvoid V_ClearRefdef(playerview_t *pv)\n{\n\tr_refdef.playerview = pv;\n\tr_refdef.dirty = ~0;\n\n\tr_refdef.grect.x = 0;\n\tr_refdef.grect.y = 0;\n\tr_refdef.grect.width = vid.fbvwidth;//vid.width;\n\tr_refdef.grect.height = vid.fbvheight;//vid.height;\n\n\tr_refdef.afov = scr_fov.value;\t//will have a better value applied if fov is bad. this allows setting.\n\tr_refdef.fov_x = 0;\n\tr_refdef.fov_y = 0;\n\tr_refdef.fovv_x = 0;\n\tr_refdef.fovv_y = 0;\n\tr_refdef.projectionoffset[0] = r_refdef.projectionoffset[1] = 0;\n\n\tr_refdef.drawsbar = (cl.intermissionmode == IM_NONE);\n\tr_refdef.flags = 0;\n\n\tr_refdef.skyroom_enabled = false;\n\tr_refdef.firstvisedict = 0;\t//just in case.\n\n\tr_refdef.areabitsknown = false;\n\n//\tmemset(r_refdef.postprocshader, 0, sizeof(r_refdef.postprocshader));\n//\tmemset(r_refdef.postprocsize, 0, sizeof(r_refdef.postprocsize));\n//\tr_refdef.postproccube = 0;\n}\n\n/*\n==================\nV_CalcFixedRefDef\n\n==================\n*/\nstatic void V_CalcFixedRefDef (playerview_t *pv)\n{\n\tr_refdef.playerview = pv;\n\n\tmemset(&r_refdef.globalfog, 0, sizeof(r_refdef.globalfog));\n\n\tr_refdef.time = cl.servertime;\n\n// refresh position from fixed origin\n\tVectorCopy(r_refdef.fixedvieworg, r_refdef.vieworg);\n\n\tr_refdef.useperspective = true;\n\tr_refdef.mindist = bound(0.1, gl_mindist.value, 4);\n\tr_refdef.maxdist = gl_maxdist.value;\n\n\tVectorCopy(r_refdef.fixedviewangles, r_refdef.viewangles);\n\n\tif (v_skyroom_set)\n\t{\n\t\tr_refdef.skyroom_enabled = true;\n\t\tVectorMA(v_skyroom_origin, v_skyroom_origin[3], r_refdef.vieworg, r_refdef.skyroom_pos);\n\t\tVector4Copy(v_skyroom_orientation, r_refdef.skyroom_spin);\n\t\tr_refdef.skyroom_spin[3] *= cl.time;\n\t}\n}\n\n/*\n==================\nV_CalcRefdef\n\n==================\n*/\nvoid V_CalcRefdef (playerview_t *pv)\n{\n\tfloat\t\tbob;\n\tfloat\t\tviewheight;\n\n\tif (r_refdef.fixedview)\n\t{\n\t\tV_CalcFixedRefDef (pv);\n\t\treturn;\n\t}\n\n\tr_refdef.playerview = pv;\n\n\tmemset(&r_refdef.globalfog, 0, sizeof(r_refdef.globalfog));\n\n\tr_refdef.time = cl.servertime;\n#ifdef Q2CLIENT\n\tif (cls.protocol == CP_QUAKE2)\n\t{\n\t\tVectorCopy (pv->simorg, r_refdef.vieworg);\n\t\tVectorCopy (pv->simangles, r_refdef.viewangles);\n\t\treturn;\n\t}\n#endif\n\n\tif (v_viewheight.value < -7)\n\t\tbob=-7;\n\telse if (v_viewheight.value > 4)\n\t\tbob=4;\n\telse if (v_viewheight.value)\n\t\tbob=v_viewheight.value;\n\telse\n\t\tbob = V_CalcBob (pv, false);\n\n// refresh position from simulated origin\n\tVectorCopy (pv->simorg, r_refdef.vieworg);\n\n\tr_refdef.useperspective = true;\n\tr_refdef.mindist = bound(0.1, gl_mindist.value, 4);\n\tr_refdef.maxdist = gl_maxdist.value;\n\n// never let it sit exactly on a node line, because a water plane can\n// dissapear when viewed with the eye exactly on it.\n// the server protocol only specifies to 1/8 pixel, so add 1/16 in each axis\n\tr_refdef.vieworg[0] += 1.0/16;\n\tr_refdef.vieworg[1] += 1.0/16;\n\tr_refdef.vieworg[2] += 1.0/16;\n\n\tVectorCopy (pv->simangles, r_refdef.viewangles);\n\n\tV_CalcViewRoll (pv);\n\tV_AddIdle (pv);\n\n\tviewheight = pv->viewheight;\n\tif (viewheight == DEFAULT_VIEWHEIGHT && cls.protocol == CP_QUAKEWORLD && !(cls.z_ext & Z_EXT_VIEWHEIGHT))\n\t{\n\t\tif (view_message && view_message->flags & PF_GIB)\n\t\t\tviewheight = 8;\t// gib view height\n\t\telse if (view_message && view_message->flags & PF_DEAD)\n\t\t\tviewheight = 16;\t// corpse view height\n\t}\n\n#ifdef QUAKESTATS\n\tif (pv->stats[STAT_HEALTH] < 0 && (!pv->spectator || pv->cam_state == CAM_EYECAM) && v_deathtilt.value)\t\t// PF_GIB will also set PF_DEAD\n\t{\n\t\tif (!pv->spectator || cl_chasecam.ival)\n\t\t\tr_refdef.viewangles[ROLL] = 80*v_deathtilt.value;\t// dead view angle\n\t}\n\telse\n#endif\n\t{\n\t\t// v_viewheight only affects the view if the player is alive\n\t\tviewheight += bob;\n\t}\n\tviewheight += pv->crouch;\n\n\tVectorMA(r_refdef.vieworg, -viewheight, pv->gravitydir, r_refdef.vieworg);\n\n// set up gun position\n\tV_CalcGunPositionAngle (pv, bob);\n\n// set up the refresh position\n\tif (v_gunkick.value)\n\t{\n\t\tr_refdef.viewangles[PITCH] += pv->punchangle_cl*v_gunkick.value;\n\t\tVectorMA(r_refdef.viewangles, v_gunkick.value, pv->punchangle_sv, r_refdef.viewangles);\n\t\tVectorMA(r_refdef.vieworg, v_gunkick.value, pv->punchorigin, r_refdef.vieworg);\n\t}\n\n\tif (v_skyroom_set)\n\t{\n\t\tr_refdef.skyroom_enabled = true;\n\t\tVectorMA(v_skyroom_origin, v_skyroom_origin[3], r_refdef.vieworg, r_refdef.skyroom_pos);\n\t\tVector4Copy(v_skyroom_orientation, r_refdef.skyroom_spin);\n\t\tr_refdef.skyroom_spin[3] *= cl.time;\n\t}\n\n\tif (chase_active.ival && cls.allow_cheats)\t//cheat restriction might be lifted some time when any wallhacks are solved.\n\t{\n\t\tvec3_t axis[3];\n\t\tvec3_t camorg, camdir;\n\t\ttrace_t tr;\n\t\tfloat len;\n\t\t//r_refdef.viewangles[0] += chase_pitch.value;\n\t\t//r_refdef.viewangles[1] += chase_yaw.value;\n\t\t//r_refdef.viewangles[2] += chase_roll.value;\n\t\tif (chase_active.ival >= 2)\n\t\t\tr_refdef.viewangles[0] = 90;\n\t\tif (chase_active.ival >= 3)\n\t\t\tr_refdef.viewangles[1] = 0;\n\t\tAngleVectors(r_refdef.viewangles, axis[0], axis[1], axis[2]);\n\t\tVectorScale(axis[0], -chase_back.value, camdir);\n\t\tVectorMA(camdir, chase_right.value, axis[1], camdir);\n\t\tif (pv->pmovetype == PM_6DOF)\n\t\t\tVectorMA(camdir, chase_up.value, axis[2], camdir);\n\t\telse\n\t\t\tVectorMA(camdir, -chase_up.value, pv->gravitydir, camdir);\n\t\tlen = VectorLength(camdir);\n\t\tVectorMA(r_refdef.vieworg, (len+128)/len, camdir, camorg);\t//push it 128qu further\n\t\tif (cl.worldmodel && cl.worldmodel->funcs.NativeTrace)\n\t\t{\n\t\t\tcl.worldmodel->funcs.NativeTrace(cl.worldmodel, 0, 0, NULL, r_refdef.vieworg, camorg, vec3_origin, vec3_origin, true, MASK_WORLDSOLID, &tr);\n\t\t\tif (!tr.startsolid)\n\t\t\t{\n\t\t\t\tif (tr.fraction < 1)\n\t\t\t\t{\n\t\t\t\t\t//we found a plane, bisect it weirdly to push 4qu infront\n\t\t\t\t\tfloat d1,d2, frac;\n\t\t\t\t\tVectorMA(r_refdef.vieworg, 1, camdir, camorg);\n\t\t\t\t\td1 = DotProduct(r_refdef.vieworg, tr.plane.normal) - (tr.plane.dist+4);\n\t\t\t\t\td2 = DotProduct(camorg, tr.plane.normal) - (tr.plane.dist+4);\n\t\t\t\t\tfrac = d1 / (d1-d2);\n\t\t\t\t\tfrac = bound(0, frac, 1);\n\t\t\t\t\tVectorMA(r_refdef.vieworg, frac, camdir, r_refdef.vieworg);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tVectorMA(r_refdef.vieworg, 1, camdir, r_refdef.vieworg);\n\t\t\t}\n\t\t}\n\t}\n}\n\n/*\n=============\nDropPunchAngle\n=============\n*/\nvoid DropPunchAngle (playerview_t *pv)\n{\n\tif (pv->punchangle_cl < 0)\n\t{\n\t\tpv->punchangle_cl += 10*host_frametime;\n\t\tif (pv->punchangle_cl > 0)\n\t\t\tpv->punchangle_cl = 0;\n\t}\n\telse\n\t{\n\t\tpv->punchangle_cl -= 10*host_frametime;\n\t\tif (pv->punchangle_cl < 0)\n\t\t\tpv->punchangle_cl = 0;\n\t}\n}\n\n/*\n==================\nV_RenderView\n\nThe player's clipping box goes from (-16 -16 -24) to (16 16 32) from\nthe entity origin, so any view position inside that will be valid\n==================\n*/\nqboolean r_secondaryview;\n#ifdef SIDEVIEWS\n\n#ifdef PEXT_VIEW2\nentity_t *CL_EntityNum(int num)\n{\n\tint i;\n\tfor (i=0 ; i<cl_numvisedicts ; i++)\n\t{\n\t\tif (cl_visedicts[i].keynum == num)\n\t\t\treturn &cl_visedicts[i];\n\t}\n\treturn NULL;\n}\n#endif\n#endif\n\nstatic qboolean SCR_VRectForPlayer(vrect_t *vrect, int pnum, unsigned maxseats)\n{\n\tint w = 1, h = 1;\n\twhile (w*h < maxseats)\n\t{\n\t\t//spread them out so they're all fair.\n\t\t//panorama always stacks vertically, using the full width, because we can.\n\t\tif (vid.fbvwidth/(w*16.0) > vid.fbvheight/(h*10.0) && r_projection.ival != PROJ_PANORAMA)\n\t\t\tw++;\n\t\telse\n\t\t\th++;\n\t}\n\tvrect->width = vid.fbvwidth/(float)w;\n\tvrect->height = vid.fbvheight/(float)h;\n\tvrect->x = (pnum%w) * vrect->width;\n\tvrect->y = (pnum/w) * vrect->height;\n\n\treturn pnum < w*h;\n}\n\nvoid Draw_ExpandedString(struct font_s *font, float x, float y, conchar_t *str);\n\n#ifdef QUAKESTATS\nstatic void SCR_DrawAutoID(vec3_t org, player_info_t *pl, qboolean isteam)\n{\n\tconchar_t buffer[256];\n\tint len;\n\tvec3_t center;\n\tvec3_t tagcenter;\n\tfloat alpha;\n\tqboolean obscured;\n\tint x, y;\n\tint r;\n\tfloat barwidth;\n\tqboolean haveinfo;\n\tunsigned int textflags;\n\tint h;\n\tchar *pname;\n\n\tint health;\n\tint armour;\n\tunsigned int items;\n\tstatic vec4_t healthcolours[] =\n\t{\n\t\t{0.7, 0.45, 0.45, 1},\n\t\t{0.3, 0, 0, 1},\n\t\t{1, 0, 0, 1},\n\t\t{1, 0.4, 0, 1},\n\t\t{1, 1, 1, 1}\n\t};\n\tstatic vec4_t armourcolours[] =\n\t{\n\t\t{25, 170, 0, 0.2},\n\t\t{25, 170, 0, 1},\n\t\t{225, 220, 0, 0.2},\n\t\t{225, 220, 0, 1},\n\t\t{255, 0, 0, 0.2},\n\t\t{255, 0, 0, 1}\n\t};\n\n\textern cvar_t tp_name_sg,tp_name_ssg,tp_name_ng,tp_name_sng,tp_name_gl,tp_name_rl,tp_name_lg;\n\tstatic cvar_t *wbitnames[] =\n\t{\n\t\t&tp_name_sg,\n\t\t&tp_name_ssg,\n\t\t&tp_name_ng,\n\t\t&tp_name_sng,\n\t\t&tp_name_gl,\n\t\t&tp_name_rl,\n\t\t&tp_name_lg\n\t};\n\tstruct font_s *font = font_default;\n\n\tVectorCopy(org, tagcenter);\n\ttagcenter[2] += 32;\n\tif (!Matrix4x4_CM_Project(tagcenter, center, r_refdef.viewangles, r_refdef.vieworg, r_refdef.fov_x, r_refdef.fov_y))\n\t\treturn;\t//behind the camera\n\tif (center[0] < 0 || center[0] > 1 || center[1] < 0 || center[1] > 1)\n\t\treturn;\t//off the side of the screen\n\n\tobscured = !TP_IsPlayerVisible(org);\n\t\n\tif (obscured && !isteam)\n\t\treturn;\t//only teammembers are drawn when obscured\n\tif (isteam)\n\t\ttextflags = scr_autoid_teamcolour.ival << CON_FGSHIFT;\n\telse\n\t\ttextflags = scr_autoid_enemycolour.ival << CON_FGSHIFT;\n\tif (obscured)\n\t{\n\t\tif (scr_autoid_team.ival == 2)\n\t\t\treturn;\n\t\ttextflags |= CON_HALFALPHA;\n\t\talpha = 0.25;\n\t}\n\telse\n\t\talpha = 1;\n\n\tx = center[0]*r_refdef.vrect.width+r_refdef.vrect.x;\n\ty = (1-center[1])*r_refdef.vrect.height+r_refdef.vrect.y;\n\n\tif (cls.demoplayback == DPB_MVD)\n\t{\n\t\thealth = pl->statsf[STAT_HEALTH];\n\t\tarmour = pl->statsf[STAT_ARMOR];\n\t\titems = pl->stats[STAT_ITEMS];\n\t\tpname = pl->name;\n\t\thaveinfo = true;\n\t}\n\telse\n\t{\n\t\thealth = pl->tinfo.health;\n\t\tarmour = pl->tinfo.armour;\n\t\titems = pl->tinfo.items;\n\t\tpname = ((*pl->tinfo.nick)?pl->tinfo.nick:(cl.teamfortress?\"-\":pl->name));\n\t\thaveinfo = pl->tinfo.time > cl.time;\n\t}\n\n\tif (strcmp(pname, \"-\"))\t//a tinfo nick of - hides names. this can be used for TF to hide names for spies.\n\t{\n\t\ty -= 8;\n\t\tlen = COM_ParseFunString(textflags, pname, buffer, sizeof(buffer), false) - buffer;\n\t\tDraw_ExpandedString(font, x - len*4, y, buffer);\n\t}\n\n\tif (!haveinfo)\n\t\treturn;\t//we don't trust the info that we have, so no ids.\n\n\th = 0;\n\n\tbarwidth = 32;\n\n\t//display health bar\n\tif (scr_autoid_health.ival)\n\t{\n\t\tfloat frac = health / 100.0;\n\t\tif (frac < 0)\n\t\t\tfrac = 0;\n\t\tr = frac;\n\t\tfrac -= r;\n\t\tif (r > countof(healthcolours)-2)\n\t\t{\n\t\t\tr = countof(healthcolours)-2;\n\t\t\tfrac = 1;\n\t\t}\n\t\th += 8;\n\t\ty -= 8;\n\t\tR2D_ImageColours(healthcolours[r][0], healthcolours[r][1], healthcolours[r][2], healthcolours[r][3]*alpha);\n\t\tR2D_FillBlock(x - barwidth*0.5 + barwidth * frac, y, barwidth * (1-frac), 4);\n\t\tr++;\n\t\tR2D_ImageColours(healthcolours[r][0], healthcolours[r][1], healthcolours[r][2], healthcolours[r][3]*alpha);\n\t\tR2D_FillBlock(x - barwidth*0.5, y, barwidth * frac, 4);\n\t}\n\n\tif (health <= 0)\t//armour+weapons are not relevant when dead\n\t{\n\t\tR2D_ImageColours(1, 1, 1, 1);\n\t\treturn;\n\t}\n\n\tif (scr_autoid_armour.ival)\n\t{\n\t\t//display armour bar above that\n\t\tif (items & IT_ARMOR3)\n\t\t\tr = 4, health = 200;\n\t\telse if (items & IT_ARMOR2)\n\t\t\tr = 2, health = 150;\n\t\telse if (items & IT_ARMOR1)\n\t\t\tr = 0, health = 100;\n\t\telse r = -1;\n\t\tif (r >= 0)\n\t\t{\n\t\t\th += 5;\n\t\t\ty -= 5;\n\t\t\tarmour = bound(0, armour, health);\n\t\t\tbarwidth = 32;\n\t\t\tR2D_ImageColours(armourcolours[r][0], armourcolours[r][1], armourcolours[r][2], armourcolours[r][3]*alpha);\n\t\t\tR2D_FillBlock(x - barwidth*0.5 + barwidth * armour/(float)health, y, barwidth * (health-armour)/(float)health, 4);\n\t\t\tr++;\n\t\t\tR2D_ImageColours(armourcolours[r][0], armourcolours[r][1], armourcolours[r][2], armourcolours[r][3]*alpha);\n\t\t\tR2D_FillBlock(x - barwidth*0.5, y, barwidth * armour/(float)health, 4);\n\t\t}\n\t}\n\n\tR2D_ImageColours(1, 1, 1, 1);\n\tif (scr_autoid_weapon.ival)\n\t{\n\t\tfor (r = countof(wbitnames)-1; r>=0; r--)\n\t\t\tif (items & (1<<r))\n\t\t\t\tbreak;\n\t\tif (r >= 0 && (scr_autoid_weapon_mask.ival&(1<<1)))\n\t\t{\n#ifdef QUAKEHUD\n\t\t\tif (scr_autoid_weapon.ival==1)\n\t\t\t{\n\t\t\t\textern apic_t *sb_weapons[7][8];\n\t\t\t\tfloat w = 0;\n\t\t\t\tif (r < 8 && sb_weapons[0][r])\n\t\t\t\t{\n\n\t\t\t\t\tif (h < 16)\n\t\t\t\t\t{\n\t\t\t\t\t\ty-= 16-h;\n\t\t\t\t\t\th = 16;\n\t\t\t\t\t}\n\t\t\t\t\ty += (h-16)/2;\n\n\t\t\t\t\tif (sb_weapons[0][r]->width)\n\t\t\t\t\t\tw = sb_weapons[0][r]->width;\n\t\t\t\t\telse\n\t\t\t\t\t\tw = sb_weapons[0][r]->atlas->width;\n\t\t\t\t\tR2D_ImageAtlas(x-barwidth*.5-24-4, y, 24, 16, 0, 0, 24/w, 1, sb_weapons[0][r]);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n#endif\n\n\t\t\tif (h < 8)\n\t\t\t{\n\t\t\t\ty-= 8-h;\n\t\t\t\th = 8;\n\t\t\t}\n\t\t\ty += (h-8)/2;\n\n\t\t\tlen = COM_ParseFunString(textflags, wbitnames[r]->string, buffer, sizeof(buffer), false) - buffer;\n\t\t\tif (textflags & CON_HALFALPHA)\n\t\t\t{\n\t\t\t\tfor (r = 0; r < len; r++)\n\t\t\t\t\tif (!(buffer[r] & CON_RICHFORECOLOUR))\n\t\t\t\t\t\tbuffer[r] |= CON_HALFALPHA;\n\t\t\t}\n\t\t\tif (len && (buffer[0] & CON_CHARMASK) == '{' && (buffer[len-1] & CON_CHARMASK) == '}')\n\t\t\t{\t//these are often surrounded by {} to make them white in chat messages, and recoloured.\n\t\t\t\tbuffer[len-1] = 0;\n\t\t\t\tlen = 1;\n\t\t\t}\n\t\t\telse\n\t\t\t\tlen = 0;\n\t\t\tDraw_ExpandedString(font, x + barwidth*0.5 + 4, y, buffer+len);\n\t\t}\n\t}\n}\n#endif\n\n#include \"pr_common.h\"\nmsurface_t *Mod_GetSurfaceNearPoint(model_t *model, vec3_t point);\nchar *Shader_GetShaderBody(shader_t *s, char *fname, size_t fnamesize);\nextern vec3_t nametagorg[MAX_CLIENTS];\nextern qboolean nametagseen[MAX_CLIENTS];\nextern cvar_t r_showshaders, r_showfields, r_projection;\nvoid R_DrawNameTags(void)\n{\n\tint i;\n#ifdef QUAKESTATS\n\tlerpents_t *le;\n\tqboolean isteam;\n\tchar *ourteam;\n\tint ourcolour;\n#endif\n\n\tif (r_projection.ival)\t//we don't actually know how to transform the points unless the projection is coded in advance. and it isn't.\n\t\treturn;\n\n#if defined(CSQC_DAT) || !defined(CLIENTONLY)\n\tif (r_showshaders.ival && cl.worldmodel && cl.worldmodel->loadstate == MLS_LOADED)\n\t{\n#ifdef CSQC_DAT\n\t\textern world_t csqc_world;\n#endif\n\t\ttrace_t trace;\n\t\tchar *str;\n\t\tvec3_t targ;\n\t\tvec2_t scale = {12,12};\n\t\tmsurface_t *surf;\n\t\tshader_t *shader;\n\t\tconst char *shadername;\n\t\tchar *body;\n\t\tchar fname[MAX_QPATH];\n\t\tVectorMA(r_refdef.vieworg, 8192, vpn, targ);\n#ifdef CSQC_DAT\n\t\tif (csqc_world.progs)\n\t\t{\n\t\t\tint oldhit = csqc_world.edicts->xv->hitcontentsmaski;\n\t\t\tcsqc_world.edicts->xv->hitcontentsmaski = ~0;\n\t\t\ttrace = World_Move(&csqc_world, r_refdef.vieworg, vec3_origin, vec3_origin, targ, MOVE_EVERYTHING, csqc_world.edicts);\n\t\t\tcsqc_world.edicts->xv->hitcontentsmaski = oldhit;\n\t\t}\n\t\telse\n#endif\n\t\t\tcl.worldmodel->funcs.NativeTrace(cl.worldmodel, 0, PE_FRAMESTATE, NULL, r_refdef.vieworg, targ, vec3_origin, vec3_origin, false, ~0, &trace);\n\n\t\tif (trace.fraction >= 1)\n\t\t\tstr = \"hit nothing\";\n\t\telse\n\t\t{\n\t\t\tshader = NULL;\n#ifdef TERRAIN\n\t\t\tif (cl.worldmodel->terrain && trace.brush_id && (shader = Terr_GetShader(cl.worldmodel, &trace)))\n\t\t\t\tshadername = shader->name;\n\t\t\telse\n#endif\n\t\t\t\t if ((surf = (trace.fraction == 1)?NULL:Mod_GetSurfaceNearPoint(cl.worldmodel, trace.endpos)))\n\t\t\t{\n\t\t\t\tshadername = surf->texinfo->texture->name;\n\t\t\t\tshader = surf->texinfo->texture->shader;\n\t\t\t}\n\t\t\telse if (trace.surface && *trace.surface->name)\n\t\t\t{\n\t\t\t\tshadername = trace.surface->name;\n\t\t\t\tshader = NULL;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tshadername = \"the unknown\";\n\t\t\t\tshader = NULL;\n\t\t\t}\n\n\t\t\tbody = shader?Shader_GetShaderBody(shader, fname, countof(fname)):NULL;\n\t\t\tif (body)\n\t\t\t{\n\t\t\t\tint width, height;\n\t\t\t\tchar *cr;\n\t\t\t\tconst char *fl = \"\";\n\t\t\t\tif (shader->usageflags & SUF_LIGHTMAP)\n\t\t\t\t\tfl = S_COLOR_GRAY\" (lightmapped)\"S_COLOR_WHITE;\n\t\t\t\telse if (shader->usageflags & SUF_2D)\n\t\t\t\t\tfl = S_COLOR_GRAY\" (2d)\"S_COLOR_WHITE;\n\t\t\t\telse //if (shader->usageflags & SUF_2D)\n\t\t\t\t\tfl = S_COLOR_GRAY\" (auto)\"S_COLOR_WHITE;\n\t\t\t\tif (R_GetShaderSizes(shader, &width, &height, false)>0)\n\t\t\t\t\tfl = va(\"%s (%ix%i)\", fl, width, height);\n\n\t\t\t\twhile((cr = strchr(body, '\\r')))\n\t\t\t\t\t*cr = ' ';\n\n\t\t\t\tstr = va(S_COLOR_GREEN\"%s\"S_COLOR_WHITE\"\\n%s%s%s\\n{%s\\n\", fname, ruleset_allow_shaders.ival?\"\":CON_ERROR\"WARNING: ruleset_allow_shaders disables external shaders\"CON_DEFAULT\"\\n\", shadername, fl, body);\n\t\t\t\tZ_Free(body);\n\t\t\t}\n\t\t\telse\n\t\t\t\tstr = va(\"hit '%s'\", shadername);\n\t\t}\n\t\tR_DrawTextField(r_refdef.vrect.x + r_refdef.vrect.width/4, r_refdef.vrect.y, r_refdef.vrect.width/2, r_refdef.vrect.height, str, CON_WHITEMASK, CPRINT_LALIGN, font_console, scale);\n\t}\n\telse\n#endif\n\n\tif (r_showfields.ival)\n\t{\n\t\tworld_t *w = NULL;\n\t\twedict_t *e;\n\t\tvec3_t org;\n\t\tvec3_t screenspace;\n\t\tvec3_t diff;\n\t\tint scale=1;\n\t\tif (!cls.allow_cheats)\n\t\t{\n\t\t\tvec2_t scale = {8,8};\n\t\t\tfloat x = 0.5*r_refdef.vrect.width+r_refdef.vrect.x;\n\t\t\tfloat y = (1-0.5)*r_refdef.vrect.height+r_refdef.vrect.y;\n\t\t\tR_DrawTextField(x, y, vid.width - x, vid.height - y, \"r_showfields requires sv_cheats 1\", CON_WHITEMASK, CPRINT_TALIGN|CPRINT_LALIGN, font_console, scale);\n\t\t\tw = NULL;\n\t\t}\n\t\telse if ((r_showfields.ival & 3) == 1)\n\t\t{\n#ifndef CLIENTONLY\n\t\t\tw = &sv.world;\n#endif\n\t\t}\n\t\telse if ((r_showfields.ival & 3) == 2)\n\t\t{\n#ifdef CSQC_DAT\n\t\t\textern world_t csqc_world;\n\t\t\tw = &csqc_world;\n\t\t\tscale = -1;\n#endif\n\t\t}\n\t\telse if ((r_showfields.ival & 3) == 3)\n\t\t{\n\t\t\tinframe_t *frame;\n\t\t\tpacket_entities_t *pak;\n\t\t\tentity_state_t *state;\n\t\t\tmodel_t *mod;\n\n\t\t\tframe = &cl.inframes[cl.parsecount & UPDATE_MASK];\n\t\t\tpak = &frame->packet_entities;\n\n\t\t\tfor (i=0 ; i<pak->num_entities ; i++)\n\t\t\t{\n\t\t\t\tstate = &pak->entities[i];\n\n\t\t\t\tmod = cl.model_precache[state->modelindex];\n\t\t\t\tif (mod && mod->loadstate == MLS_LOADED)\n\t\t\t\t\tVectorInterpolate(mod->mins, 0.5, mod->maxs, org);\n\t\t\t\telse\n\t\t\t\t\tVectorClear(org);\n\t\t\t\tVectorAdd(org, state->origin, org);\n\t\t\t\tif (Matrix4x4_CM_Project(org, screenspace, r_refdef.viewangles, r_refdef.vieworg, r_refdef.fov_x, r_refdef.fov_y))\n\t\t\t\t{\n\t\t\t\t\tchar *entstr;\n\t\t\t\t\tint x, y;\n\n\t\t\t\t\tentstr = va(\"%i\", state->number);\n\t\t\t\t\tif (entstr)\n\t\t\t\t\t{\n\t\t\t\t\t\tvec2_t scale = {8,8};\n\t\t\t\t\t\tx = screenspace[0]*r_refdef.vrect.width+r_refdef.vrect.x;\n\t\t\t\t\t\ty = (1-screenspace[1])*r_refdef.vrect.height+r_refdef.vrect.y;\n\t\t\t\t\t\tR_DrawTextField(x, y, vid.width - x, vid.height - y, entstr, CON_WHITEMASK, CPRINT_TALIGN|CPRINT_LALIGN, font_default, scale);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n#ifdef Q2SERVER //not enough fields for it to really be worth it.\n\t\tif (w == &sv.world && svs.gametype == GT_QUAKE2 && ge)\n\t\t{\n\t\t\tstruct q2edict_s\t*e;\n\n\t\t\tint best = 0;\n\t\t\tfloat bestscore = 0, score = 0;\n\t\t\tfor (i = 1; i < ge->num_edicts; i++)\n\t\t\t{\n\t\t\t\te = &ge->edicts[i];\n\t\t\t\tif (!e->inuse)\n\t\t\t\t\tcontinue;\n\t\t\t\tVectorInterpolate(e->mins, 0.5, e->maxs, org);\n\t\t\t\tVectorAdd(org, e->s.origin, org);\n\t\t\t\tVectorSubtract(org, r_refdef.vieworg, diff);\n\t\t\t\tif (DotProduct(diff, diff) < 16*16)\n\t\t\t\t\tcontinue;\t//ignore stuff too close(like the player themselves)\n\t\t\t\tVectorNormalize(diff);\n\t\t\t\tscore = DotProduct(diff, vpn);// r_refdef.viewaxis[0]);\n\t\t\t\tif (score > bestscore)\n\t\t\t\t{\n\t\t\t\t\tint hitent;\n\t\t\t\t\tvec3_t imp;\n\t\t\t\t\tif (CL_TraceLine(r_refdef.vieworg, org, imp, NULL, &hitent)>=1 || hitent == i)\n\t\t\t\t\t{\n\t\t\t\t\t\tbest = i;\n\t\t\t\t\t\tbestscore = score;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (best)\n\t\t\t{\n\t\t\t\te = &ge->edicts[best];\n\t\t\t\tVectorInterpolate(e->mins, 0.5, e->maxs, org);\n\t\t\t\tVectorAdd(org, e->s.origin, org);\n\t\t\t\tif (Matrix4x4_CM_Project(org, screenspace, r_refdef.viewangles, r_refdef.vieworg, r_refdef.fov_x, r_refdef.fov_y))\n\t\t\t\t{\n\t\t\t\t\tchar *entstr = va(\"entity %i {\\n\\tmodelindex %i\\n\\torigin \\\"%f %f %f\\\"\\n}\\n\", e->s.number, e->s.modelindex, e->s.origin[0], e->s.origin[1], e->s.origin[2]);\n\t\t\t\t\tif (entstr)\n\t\t\t\t\t{\n\t\t\t\t\t\tvec2_t scale = {8,8};\n\t\t\t\t\t\tint x = screenspace[0]*r_refdef.vrect.width+r_refdef.vrect.x;\n\t\t\t\t\t\tint y = (1-screenspace[1])*r_refdef.vrect.height+r_refdef.vrect.y;\n\t\t\t\t\t\tR_DrawTextField(x, y, vid.width - x, vid.height - y, entstr, CON_WHITEMASK, CPRINT_TALIGN|CPRINT_LALIGN, font_console, scale);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse\n#endif\n\t\tif (w && w->progs && w->progs->saveent)\n\t\t{\n\t\t\tint best = 0;\n\t\t\tfloat bestscore = 0, score = 0;\n\t\t\tfor (i = 1; i < w->num_edicts; i++)\n\t\t\t{\n\t\t\t\te = WEDICT_NUM_PB(w->progs, i);\n\t\t\t\tif (ED_ISFREE(e))\n\t\t\t\t\tcontinue;\n\t\t\t\tVectorInterpolate(e->v->mins, 0.5, e->v->maxs, org);\n\t\t\t\tVectorAdd(org, e->v->origin, org);\n\t\t\t\tVectorSubtract(org, r_refdef.vieworg, diff);\n\t\t\t\tif (DotProduct(diff, diff) < 16*16)\n\t\t\t\t\tcontinue;\t//ignore stuff too close(like the player themselves)\n\t\t\t\tVectorNormalize(diff);\n\t\t\t\tscore = DotProduct(diff, vpn);// r_refdef.viewaxis[0]);\n\t\t\t\tif (score > bestscore)\n\t\t\t\t{\n\t\t\t\t\tint hitent;\n\t\t\t\t\tvec3_t imp;\n\t\t\t\t\tif (CL_TraceLine(r_refdef.vieworg, org, imp, NULL, &hitent)>=1 || hitent*scale == i)\n\t\t\t\t\t{\n\t\t\t\t\t\tbest = i;\n\t\t\t\t\t\tbestscore = score;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (best)\n\t\t\t{\n\t\t\t\te = WEDICT_NUM_PB(w->progs, best);\n\t\t\t\tVectorInterpolate(e->v->mins, 0.5, e->v->maxs, org);\n\t\t\t\tVectorAdd(org, e->v->origin, org);\n\t\t\t\tif (Matrix4x4_CM_Project(org, screenspace, r_refdef.viewangles, r_refdef.vieworg, r_refdef.fov_x, r_refdef.fov_y))\n\t\t\t\t{\n\t\t\t\t\tchar asciibuffer[8192];\n\t\t\t\t\tconst char *entstr;\n\t\t\t\t\tsize_t buflen;\n\t\t\t\t\tint x, y;\n\n\t\t\t\t\tsprintf(asciibuffer, \"entity %i \", e->entnum);\n\t\t\t\t\tbuflen = strlen(asciibuffer);\n\t\t\t\t\tentstr = w->progs->saveent(w->progs, asciibuffer, &buflen, sizeof(asciibuffer), (edict_t*)e);\t//will save just one entities vars\n\t\t\t\t\tif (entstr)\n\t\t\t\t\t{\n\t\t\t\t\t\tvec2_t scale = {8,8};\n\t\t\t\t\t\tx = screenspace[0]*r_refdef.vrect.width+r_refdef.vrect.x;\n\t\t\t\t\t\ty = (1-screenspace[1])*r_refdef.vrect.height+r_refdef.vrect.y;\n\t\t\t\t\t\ty += scale[1]*R_DrawTextField(x, y, vid.width - x, vid.height - y, entstr, CON_WHITEMASK, CPRINT_TALIGN|CPRINT_LALIGN, font_console, scale);\n\n#ifdef CSQC_DAT\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\textern world_t csqc_world;\n\t\t\t\t\t\t\tif (w == &csqc_world)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tentstr = CSQC_GetExtraFieldInfo(e, asciibuffer, sizeof(asciibuffer));\n\t\t\t\t\t\t\t\tif (entstr)\n\t\t\t\t\t\t\t\t\ty += scale[1]*R_DrawTextField(x, y, vid.width - x, vid.height - y, entstr, CON_WHITEMASK, CPRINT_TALIGN|CPRINT_LALIGN, font_console, scale);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n#ifdef QUAKESTATS\n\tif (cls.protocol == CP_QUAKE2)\n\t\treturn;\t//FIXME: q2 has its own ent logic, which messes stuff up here.\n\n\tif (((!r_refdef.playerview->spectator && !cls.demoplayback) || !scr_autoid.ival) && (!cl.teamplay || !scr_autoid_team.ival))\n\t\treturn;\n\tif (cls.state != ca_active || !cl.validsequence || cl.intermissionmode != IM_NONE)\n\t\treturn;\n\n\tif (r_refdef.playerview->cam_state != CAM_FREECAM && r_refdef.playerview->cam_spec_track >= 0)\n\t{\n\t\tourteam = cl.players[r_refdef.playerview->cam_spec_track].team;\n\t\tourcolour = cl.players[r_refdef.playerview->cam_spec_track].rbottomcolor;\n\t}\n\telse\n\t{\n\t\tourteam = cl.players[r_refdef.playerview->playernum].team;\n\t\tourcolour = cl.players[r_refdef.playerview->playernum].rbottomcolor;\n\t}\n\n\tpmove.skipent = r_refdef.playerview->playernum+1;\n\tfor (i = 0; i < cl.allocated_client_slots; i++)\n\t{\n\t\tif (!*cl.players[i].name)\n\t\t\tcontinue;\t//slot is empty.\n\n\t\tif (!nametagseen[i])\n\t\t{\n\t\t\tif (i+1 < cl.maxlerpents && cl.lerpentssequence && cl.lerpents[i+1].sequence == cl.lerpentssequence)\n\t\t\t{\n\t\t\t\tle = &cl.lerpents[i+1];\n\t\t\t\tVectorCopy(le->origin, nametagorg[i]);\n\t\t\t}\n\t\t\telse if (cl.lerpplayers[i].sequence == cl.lerpentssequence)\n\t\t\t{\n\t\t\t\tle = &cl.lerpplayers[i];\n\t\t\t\tVectorCopy(le->origin, nametagorg[i]);\n\t\t\t}\n\t\t\telse if (cl.players[i].tinfo.time > cl.time)\n\t\t\t\tVectorCopy(cl.players[i].tinfo.org, nametagorg[i]);\n\t\t\telse\n\t\t\t\tcontinue;\n\t\t}\n\n\t\t//while cl.lerpplayers exists, it tends to not be configured properly.\n\t\tif (i == r_refdef.playerview->playernum)\n\t\t\tcontinue;\t// Don't draw tag for the local player\n\t\tif (cl.players[i].spectator)\n\t\t\tcontinue;\n\t\tif (i == Cam_TrackNum(r_refdef.playerview))\t//no tag for the player that you're tracking, either.\n\t\t\tcontinue;\n\n\t\tif (!cl.teamplay || !scr_autoid_team.ival)\n\t\t\tisteam = false;\n\t\telse if ((cl.teamfortress && !r_refdef.playerview->spectator) || cls.protocol == CP_NETQUAKE)\t//teamfortress should go by their colours instead, because spies. primarily this is to allow enemy spies to appear through walls as well as your own team (note that the qc will also need tinfo stuff for tf, to avoid issues with just checking player names).\n\t\t\tisteam = cl.players[i].rbottomcolor == ourcolour;\n\t\telse\n\t\t\tisteam = !strcmp(cl.players[i].team, ourteam);\n\n\t\tif (!isteam)\n\t\t\tif ((!r_refdef.playerview->spectator && !cls.demoplayback) || !scr_autoid.ival)\n\t\t\t\tcontinue;\t//only show our team when playing, too cheaty otherwise.\n\n\t\tSCR_DrawAutoID(nametagorg[i], &cl.players[i], isteam);\n\t}\n#endif\n}\n\nvoid R2D_PolyBlend (void);\nvoid V_RenderPlayerViews(playerview_t *pv)\n{\n\tint oldnuments;\n\tint oldstris;\n#ifdef SIDEVIEWS\n\tint viewnum;\n#endif\n//\tcl.simangles[plnum][ROLL] = 0;\t// FIXME @@@\n\n\tDropPunchAngle (pv);\n\n\tCam_SelfTrack(pv);\n\n\toldnuments = cl_numvisedicts;\n\toldstris = cl_numstris;\n\tCL_LinkViewModel ();\n\n\tif (cl.intermissionmode != IM_NONE)\n\t{\t// intermission / finale rendering\n\t\tV_CalcIntermissionRefdef (pv);\n\t}\n\telse\n\t{\n\t\tV_DriftPitch (pv);\n\t\tV_CalcRefdef (pv);\n\t}\n\tV_ApplyRefdef();\n\n\tR_RenderView ();\n\tR2D_PolyBlend ();\n\tR_DrawNameTags();\n#ifdef RTLIGHTS\n\tR_EditLights_DrawInfo();\n#endif\n\n\tif(cl.intermissionmode == IM_NONE)\n\t\tR2D_DrawCrosshair();\n\n\tcl_numvisedicts = oldnuments;\n\tcl_numstris = oldstris;\n\n\tr_secondaryview = true;\n\n#ifdef SIDEVIEWS\n/*\t//adjust main view height to strip off the rearviews at the top\n\tif (vsecwidth >= 1)\n\t{\n\t\tr_refdef.vrect.y -= vsecheight;\n\t\tr_refdef.vrect.height += vsecheight;\n\t}\n*/\n\tfor (viewnum = 0; viewnum < SIDEVIEWS; viewnum++)\n\tif (sideview[viewnum].scalex.value>0&&sideview[viewnum].scaley.value>0\n\t\t&& ((sideview[viewnum].enabled.value && sideview[viewnum].enabled.value != 2) \t//rearview if v2_enabled = 1 and not 2\n\t\t|| (sideview[viewnum].enabled.value && pv->stats[STAT_VIEW2]&&viewnum==0)))\t\t\t//v2 enabled if v2_enabled is non-zero\n\t{\n\t\tvrect_t oldrect;\n\t\tvec3_t oldangles;\n\t\tvec3_t oldposition;\n//\t\tint oldviewent;\n\t\tstruct entity_s *e;\n\t\tfloat ofx;\n\t\tfloat ofy;\n\n\t\tif (sideview[viewnum].x.value < 0)\n\t\t\tsideview[viewnum].x.value = 0;\n\t\tif (sideview[viewnum].y.value < 0)\n\t\t\tsideview[viewnum].y.value = 0;\n\n\t\tif (sideview[viewnum].scalex.value+sideview[viewnum].x.value > 1)\n\t\t\tcontinue;\n\t\tif (sideview[viewnum].scaley.value+sideview[viewnum].y.value > 1)\n\t\t\tcontinue;\n\n\t\toldrect = r_refdef.vrect;\n\t\tmemcpy(oldangles, r_refdef.viewangles, sizeof(vec3_t));\n\t\tmemcpy(oldposition, r_refdef.vieworg, sizeof(vec3_t));\n\t\tofx = r_refdef.fov_x;\n\t\tofy = r_refdef.fov_y;\n\n\t\tr_refdef.vrect.x += r_refdef.vrect.width*sideview[viewnum].x.value;\n\t\tr_refdef.vrect.y += r_refdef.vrect.height*sideview[viewnum].y.value;\n\t\tr_refdef.vrect.width *= sideview[viewnum].scalex.value;\n\t\tr_refdef.vrect.height *= sideview[viewnum].scaley.value;\n\n\t\tr_refdef.fov_x = 0;\n\t\tr_refdef.fov_y = 0;\n\t\tV_ApplyAFov(NULL);\n#ifdef PEXT_VIEW2\n\t\t\t//secondary view entity.\n\t\te=NULL;\n\t\tif (viewnum==0&&pv->stats[STAT_VIEW2])\n\t\t{\n\t\t\te = CL_EntityNum (pv->stats[STAT_VIEW2]);\n\t\t}\n\t\tif (e)\n\t\t{\n\t\t\tmemcpy(r_refdef.viewangles, e->angles, sizeof(vec3_t));\n\t\t\tmemcpy(r_refdef.vieworg, e->origin, sizeof(vec3_t));\n//\t\t\tcl.viewentity = cl.viewentity2;\n\n\t\t\tr_refdef.vieworg[0]=r_refdef.vieworg[0];//*s+(1-s)*e->lerporigin[0];\n\t\t\tr_refdef.vieworg[1]=r_refdef.vieworg[1];//*s+(1-s)*e->lerporigin[1];\n\t\t\tr_refdef.vieworg[2]=r_refdef.vieworg[2];//*s+(1-s)*e->lerporigin[2];\n\n\t\t\tr_refdef.viewangles[0]=e->angles[0];//*s+(1-s)*e->msg_angles[1][0];\n\t\t\tr_refdef.viewangles[1]=e->angles[1];//*s+(1-s)*e->msg_angles[1][1];\n\t\t\tr_refdef.viewangles[2]=e->angles[2];//*s+(1-s)*e->msg_angles[1][2];\n\n\t\t\tif (e->keynum >= 1 && e->keynum <= cl.allocated_client_slots)\n\t\t\t{\n\t\t\t\tr_refdef.viewangles[PITCH] *= 3;\n\t\t\t\tr_refdef.vieworg[2] += pv->statsf[STAT_VIEWHEIGHT];\n\t\t\t}\n\n\n\t\t\tV_EditExternalModels(e->keynum, NULL, 0);\n\n\t\t\tR_RenderView ();\n//\t\t\t\tr_framecount = old_framecount;\n\t\t}\n\t\telse\n#endif\n\t\t{\n\t\t\t//rotate the view, keeping pitch and roll.\n\t\t\tr_refdef.viewangles[YAW] += sideview[viewnum].yaw.value;\n\t\t\tr_refdef.viewangles[ROLL] += sin(sideview[viewnum].yaw.value / 180 * 3.14) * r_refdef.viewangles[PITCH];\n\t\t\tr_refdef.viewangles[PITCH] *= -cos((sideview[viewnum].yaw.value / 180 * 3.14)+3.14);\n\t\t\tif (sideview[viewnum].enabled.value!=2)\n\t\t\t{\n\t\t\t\tV_EditExternalModels(pv->viewentity, NULL, 0);\n\t\t\t\tR_RenderView ();\n\t\t\t}\n\t\t}\n\n\t\tr_refdef.vrect = oldrect;\n\t\tmemcpy(r_refdef.viewangles, oldangles, sizeof(vec3_t));\n\t\tmemcpy(r_refdef.vieworg, oldposition, sizeof(vec3_t));\n\t\tr_refdef.fov_x = ofx;\n\t\tr_refdef.fov_y = ofy;\n\t}\n#endif\n\tr_refdef.externalview = false;\n}\n\nvoid V_RenderView (qboolean no2d)\n{\n\tint seatnum;\n\tint maxseats = cl.splitclients;\n\tint pl;\n\n\tSurf_LessenStains();\n\n//\tif (cls.state != ca_active)\n//\t\treturn;\n\n\tif (cl.intermissionmode != IM_NONE)\n\t\tmaxseats = 1;\n\telse if (cl_forceseat.ival && cl_splitscreen.ival >= 4)\n\t\tmaxseats = 1;\n\telse\n\t\tmaxseats = max(1, cl.splitclients);\n\n\tR_PushDlights ();\n\n\tr_secondaryview = 0;\n\tfor (seatnum = 0; seatnum < maxseats; seatnum++)\n\t{\n\t\tpl = (maxseats==1&&cl_forceseat.ival>=1)?(cl_forceseat.ival-1)%cl.splitclients:seatnum;\n\t\tV_ClearRefdef(&cl.playerview[pl]);\n\t\tif (no2d)\n\t\t\tr_refdef.drawcrosshair = r_refdef.drawsbar = 0;\n\t\tif (seatnum)\n\t\t{\n\t\t\t//should be enough to just hack a few things.\n\t\t\tV_EditExternalModels(r_refdef.playerview->viewentity, NULL, 0);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (r_worldentity.model && r_worldentity.model->loadstate == MLS_LOADED)\n\t\t\t{\n\t\t\t\tRSpeedMark();\n\n\t\t\t\tCL_AllowIndependantSendCmd(false);\n\n\t\t\t\tCL_SetSolidEntities ();\n\t\t\t\tCL_TransitionEntities();\n\t\t\t\tCL_PredictMove ();\n\n\t\t\t\t// build a refresh entity list\n\t\t\t\tCL_EmitEntities ();\n\n\t\t\t\tCL_AllowIndependantSendCmd(true);\n\n\t\t\t\tRSpeedEnd(RSPEED_LINKENTITIES);\n\t\t\t}\n\t\t}\n\t\tif (R2D_Flush)\n\t\t\tR2D_Flush();\n\n\t\tSCR_VRectForPlayer(&r_refdef.grect, seatnum, maxseats);\n\t\tV_RenderPlayerViews(r_refdef.playerview);\n\n\t\tif (!vrui.enabled)\n\t\t{\n#ifdef PLUGINS\n\t\t\tPlug_SBar (r_refdef.playerview);\n#else\n\t\t\tif (Sbar_ShouldDraw(r_refdef.playerview))\n\t\t\t{\n\t\t\t\tSCR_TileClear (sb_lines);\n\t\t\t\tSbar_Draw (r_refdef.playerview);\n\t\t\t\tSbar_DrawScoreboard (r_refdef.playerview);\n\t\t\t}\n\t\t\telse\n\t\t\t\tSCR_TileClear (0);\n#endif\n\t\t}\n\t}\n\tif (seatnum > 1)\n\t{\n\t\tint extra = 0;\n\t\tfor (; ; seatnum++, extra++)\n\t\t{\n\t\t\tif (!SCR_VRectForPlayer(&r_refdef.grect, seatnum, maxseats))\n\t\t\t\tbreak;\n\n\t\t\tswitch(extra)\n\t\t\t{\n#if 0//def QUAKEHUD\n\t\t\tcase 0:\t//show a mini-console.\n\t\t\t\t{\n\t\t\t\t\tconsole_t *con = Con_GetMain();\n\t\t\t\t\textern cvar_t gl_conback;\n\t\t\t\t\tshader_t *conback;\n\t\t\t\t\tif (*gl_conback.string && (conback = R_RegisterPic(gl_conback.string, NULL)) && R_GetShaderSizes(conback, NULL, NULL, true) > 0)\n\t\t\t\t\t\tR2D_Image(r_refdef.grect.x, r_refdef.grect.y, r_refdef.grect.width, r_refdef.grect.height, 0, 0, 1, 1, conback);\n\t\t\t\t\telse if ((conback = R_RegisterPic(\"gfx/conback.lmp\", NULL)) && R_GetShaderSizes(conback, NULL, NULL, true) > 0)\n\t\t\t\t\t\tR2D_Image(r_refdef.grect.x, r_refdef.grect.y, r_refdef.grect.width, r_refdef.grect.height, 0, 0, 1, 1, conback);\n\t\t\t\t\telse\n\t\t\t\t\t\tR2D_TileClear (r_refdef.grect.x, r_refdef.grect.y, r_refdef.grect.width, r_refdef.grect.height);\n\t\t\t\t\tif (!scr_con_target && con)\n\t\t\t\t\t{\n\t\t\t\t\t\tint gah;\n\t\t\t\t\t\tFont_BeginString(font_console, 0, 0, &gah, &gah);\n\t\t\t\t\t\tCon_DrawOneConsole(con, con->flags & CONF_KEYFOCUSED, font_console, r_refdef.grect.x+8, r_refdef.grect.y, r_refdef.grect.width-16, r_refdef.grect.height-Font_CharHeight(), 0);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase 1:\t//show some scores, because we can.\n\t\t\t\t{\t//FIXME: show ALL tracked players.\n\t\t\t\t\tint tmp;\n\t\t\t\t\tR2D_TileClear (r_refdef.grect.x, r_refdef.grect.y, r_refdef.grect.width, r_refdef.grect.height);\n\t\t\t\t\tr_refdef.playerview = &cl.playerview[0];\n\t\t\t\t\ttmp = r_refdef.playerview->sb_showscores;\n\t\t\t\t\tr_refdef.playerview->sb_showscores = true;\n\t\t\t\t\tSbar_DrawScoreboard (r_refdef.playerview);\n\t\t\t\t\tr_refdef.playerview->sb_showscores = tmp;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase 2:\n\t\t\t\t{\n\t\t\t\t\tstatic playerview_t cam_view;\n\t\t\t\t\tvec3_t dir;\n\t\t\t\t\tint track = cl.playerview[0].cam_spec_track;\n\t\t\t\t\tif (track < 0)\n\t\t\t\t\t\ttrack = cl.playerview[0].playernum;\n\n\t\t\t\t\tif (cam_view.cam_state == CAM_FREECAM || cam_view.cam_spec_track != track)\n\t\t\t\t\t\tcam_view.cam_state = CAM_PENDING;\n\t\t\t\t\tcam_view.playernum = -1;//cl.playerview[0].playernum;\n\t\t\t\t\tcam_view.cam_spec_track = track;\n\t\t\t\t\tcam_view.viewentity = 0;\n\t\t\t\t\tcam_view.nolocalplayer = true;\n\t\t\t\t\tcam_view.spectator = true;\n\n\t\t\t\t\tCam_Track(&cam_view, NULL);\n\n\t\t\t\t\tVectorSubtract(cl.playerview[0].simorg, cam_view.cam_desired_position, dir);\n\t\t\t\t\tVectorAngles(dir, NULL, cam_view.simangles, false);\n\n\t\t\t\t\tVectorCopy(cam_view.cam_desired_position, cam_view.simorg);\n\n\t\t\t\t\tV_ClearRefdef(&cam_view);\n\t\t\t\t\tSCR_VRectForPlayer(&r_refdef.grect, seatnum, maxseats);\n\t\t\t\t\tV_EditExternalModels(0, NULL, 0);\n\t\t\t\t\tV_RenderPlayerViews(r_refdef.playerview);\n\t\t\t\t}\n\t\t\t\tbreak;\n#endif\n\t\t\tdefault:\t//wow, loads. nothing to show though.\n\t\t\t\tR2D_TileClear (r_refdef.grect.x, r_refdef.grect.y, r_refdef.grect.width, r_refdef.grect.height);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\tr_refdef.playerview = NULL;\n}\n\n//============================================================================\n\n/*\n=============\nV_Init\n=============\n*/\nvoid V_Init (void)\n{\n#define VIEWVARS \"View variables\"\n#ifdef SIDEVIEWS\n\tint i;\n#endif\n\tCmd_AddCommand (\"v_cshift\", V_cshift_f);\n\tCmd_AddCommand (\"bf\", V_BonusFlash_f);\n\tCmd_AddCommand (\"df\", V_DarkFlash_f);\n\tCmd_AddCommand (\"wf\", V_WhiteFlash_f);\n\tCmd_AddCommand (\"centerview\", V_CenterView_f);\n\n\tCvar_Register (&v_centermove, VIEWVARS);\n\tCvar_Register (&v_centerspeed, VIEWVARS);\n\n\tCvar_Register (&v_idlescale, VIEWVARS);\n\tCvar_Register (&v_iyaw_cycle, VIEWVARS);\n\tCvar_Register (&v_iroll_cycle, VIEWVARS);\n\tCvar_Register (&v_ipitch_cycle, VIEWVARS);\n\tCvar_Register (&v_iyaw_level, VIEWVARS);\n\tCvar_Register (&v_iroll_level, VIEWVARS);\n\tCvar_Register (&v_ipitch_level, VIEWVARS);\n\n\tCvar_Register (&v_cshift_empty, VIEWVARS);\n\tCvar_Register (&v_cshift_water, VIEWVARS);\n\tCvar_Register (&v_cshift_slime, VIEWVARS);\n\tCvar_Register (&v_cshift_lava, VIEWVARS);\n\n\tCvar_Register (&v_contentblend, VIEWVARS);\n\tCvar_Register (&v_damagecshift, VIEWVARS);\n\tCvar_Register (&v_quadcshift, VIEWVARS);\n\tCvar_Register (&v_suitcshift, VIEWVARS);\n\tCvar_Register (&v_ringcshift, VIEWVARS);\n\tCvar_Register (&v_pentcshift, VIEWVARS);\n\tCvar_Register (&v_gunkick, VIEWVARS);\n\tCvar_Register (&v_gunkick_q2, VIEWVARS);\n\n\tCvar_Register (&v_bonusflash, VIEWVARS);\n\n\tCvar_Register (&v_viewheight, VIEWVARS);\n\tCvar_Register (&v_depthsortentities, VIEWVARS);\n\n\tCvar_Register (&crosshaircolor, VIEWVARS);\n\tCvar_Register (&crosshair, VIEWVARS);\n\tCvar_Register (&crosshairsize, VIEWVARS);\n\tCvar_Register (&crosshaircorrect, VIEWVARS);\n\tCvar_Register (&crosshairimage, VIEWVARS);\n\tCvar_Register (&crosshairalpha, VIEWVARS);\n\tCvar_Register (&cl_crossx, VIEWVARS);\n\tCvar_Register (&cl_crossy, VIEWVARS);\n\tCvar_Register (&gl_cshiftpercent, VIEWVARS);\n\tCvar_Register (&gl_cshiftenabled, VIEWVARS);\n\tCvar_Register (&gl_cshiftborder, VIEWVARS);\n\n\tCvar_Register (&cl_rollspeed, VIEWVARS);\n\tCvar_Register (&cl_rollangle, VIEWVARS);\n\tCvar_Register (&cl_rollalpha, VIEWVARS);\n\tCvar_Register (&cl_bob, VIEWVARS);\n\tCvar_Register (&cl_bobcycle, VIEWVARS);\n\tCvar_Register (&cl_bobup, VIEWVARS);\n\n\tCvar_Register (&cl_bobmodel, VIEWVARS);\n\tCvar_Register (&cl_bobmodel_side, VIEWVARS);\n\tCvar_Register (&cl_bobmodel_up, VIEWVARS);\n\tCvar_Register (&cl_bobmodel_speed, VIEWVARS);\n\tCvar_Register (&v_viewmodel_quake, VIEWVARS);\n\n\tCvar_Register (&v_kicktime, VIEWVARS);\n\tCvar_Register (&v_kickroll, VIEWVARS);\n\tCvar_Register (&v_kickpitch, VIEWVARS);\n\n\tCvar_Register (&v_deathtilt, VIEWVARS);\n\tCvar_Register (&v_skyroom, VIEWVARS);\n\n#ifdef QUAKESTATS\n\tCvar_Register (&scr_autoid, VIEWVARS);\n\tCvar_Register (&scr_autoid_team, VIEWVARS);\n\tCvar_Register (&scr_autoid_health, VIEWVARS);\n\tCvar_Register (&scr_autoid_armour, VIEWVARS);\n\tCvar_Register (&scr_autoid_weapon, VIEWVARS);\n\tCvar_Register (&scr_autoid_weapon_mask, VIEWVARS);\n\tCvar_Register (&scr_autoid_teamcolour, VIEWVARS);\n\tCvar_Register (&scr_autoid_enemycolour, VIEWVARS);\n#endif\n\n#ifdef SIDEVIEWS\n#define SECONDARYVIEWVARS \"Secondary view vars\"\n\tfor (i = 0; i < SIDEVIEWS; i++)\n\t{\n\t\tCvar_Register (&sideview[i].enabled, SECONDARYVIEWVARS);\n\t\tCvar_Register (&sideview[i].x, SECONDARYVIEWVARS);\n\t\tCvar_Register (&sideview[i].y, SECONDARYVIEWVARS);\n\t\tCvar_Register (&sideview[i].scalex, SECONDARYVIEWVARS);\n\t\tCvar_Register (&sideview[i].scaley, SECONDARYVIEWVARS);\n\t\tCvar_Register (&sideview[i].yaw, SECONDARYVIEWVARS);\n\t}\n#endif\n\n\tCvar_Register (&ffov, VIEWVARS);\n\tCvar_Register (&r_projection, VIEWVARS);\n\n\tBuildGammaTable (1.0, 1.0, 1.0, 0.0);\t// no gamma yet\n\tCvar_Register (&v_gammainverted, VIEWVARS);\n\tCvar_Register (&v_gamma, VIEWVARS);\n\tCvar_Register (&v_contrast, VIEWVARS);\n\tCvar_Register (&v_contrastboost, VIEWVARS);\n\tCvar_Register (&v_brightness, VIEWVARS);\n\n\tCvar_Register (&chase_active, VIEWVARS);\n\tCvar_Register (&chase_back, VIEWVARS);\n\tCvar_Register (&chase_up, VIEWVARS);\n\tCvar_Register (&chase_right, VIEWVARS);\n\n#if defined(_WIN32) && !defined(MINIMAL)\n\tCvar_Register (&itburnsitburnsmakeitstop, VIEWVARS);\n#endif\n}\n"
  },
  {
    "path": "engine/client/view.h",
    "content": "/*\nCopyright (C) 1996-1997 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  \n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n*/\n// view.h\n\nextern\tcvar_t\t\tv_gamma;\nextern\tcvar_t\t\tv_gammainverted;\nextern\tcvar_t\t\tv_contrast;\nextern\tcvar_t\t\tv_contrastboost;\nextern\tcvar_t\t\tv_brightness;\nextern float sw_blend[4];\nextern float hw_blend[4];\n\nextern qboolean r_secondaryview;\n\nvoid V_Init (void);\nvoid V_RenderView (qboolean no2d);\nfloat V_CalcRoll (vec3_t angles, vec3_t velocity);\nvoid V_UpdatePalette (qboolean force);\nvoid V_ClearCShifts (void);\nvoid V_ClearEntity(entity_t *e);\nentity_t *V_AddEntity(entity_t *in);\nentity_t *V_AddNewEntity(void);\nvoid VQ2_AddLerpEntity(entity_t *in);\nvoid V_AddAxisEntity(entity_t *in);\nvoid CLQ1_AddShadow(entity_t *ent);\nint V_AddLight (int entsource, vec3_t org, float quant, float r, float g, float b);\n\nextern qbyte gammatable[256];\t//for texture gamma.\nextern qboolean gammaworks;\n\nextern cvar_t r_projection, ffov;\nextern cvar_t crosshair, crosshairalpha, crosshairsize, crosshaircolor, crosshairimage, cl_crossx, cl_crossy, crosshaircorrect;\nextern cvar_t v_viewheight;\n\nextern cvar_t v_gunkick_q2, gl_cshiftenabled, gl_cshiftborder;\t//q2 logic needs some of these cvars.\n\nextern cvar_t v_contentblend; //for menus\nextern cvar_t chase_active, chase_back, chase_up, chase_right;\t//I fucking hate this cvar. die die die.\n"
  },
  {
    "path": "engine/client/vr.h",
    "content": "#include \"merged.h\"\ntypedef struct vrsetup_s\n{\n\t//engine-set\n\tsize_t structsize;\n\tenum\n\t{\n\t\tVR_HEADLESS,\t//not to be confused with decapitation\n\t\tVR_EGL,\n\t\tVR_X11_GLX,\n//\t\tVR_ANDROID_EGL,\n\t\tVR_WIN_WGL,\n\t\tVR_VULKAN,\t\t//vulkan has no platform variation\n\t\tVR_D3D11,\t\t//d3d11 only works on windows, so no platform variation\n\t} vrplatform;\t\t//the type of renderer/args getting inited. abort if unknown.\n\tvoid *userctx;\t\t//for use in callbacks.\n\tqboolean (*createinstance)(struct vrsetup_s *, char *instanceextensions, void *result);\t\t\t\t//used by vulkan, can be null for other renderers\n\n\t//vr-set (by preinit)\n\tstruct\n\t{\n\t\tint major, minor;\n\t} minver, maxver;\n\tunsigned int deviceid[2];\n\tchar *deviceextensions;\n\n\n\t//engine-set (for full init)\n\t//this stuff is intentionally at the end\n\tunion {\n\t\tstruct\n\t\t{\n\t\t\tvoid *display;\n\t\t\tint visualid;\n\t\t\tvoid *glxfbconfig;\n\t\t\tunsigned long drawable;\t//really int32\n\t\t\tvoid *glxcontext;\n\t\t} x11_glx;\n\n\t\tstruct\n\t\t{\n\t\t\tvoid *(*getprocaddr)(const char *name);\n\t\t\tvoid *egldisplay;\n\t\t\tvoid *eglconfig;\n\t\t\tvoid *eglcontext;\n\t\t} egl;\n\n\t\tstruct\n\t\t{\n\t\t\tvoid *hdc;\n\t\t\tvoid *hglrc;\n\t\t} wgl;\n\n\t\tstruct\n\t\t{\n\t\t\tvoid *device;\n\t\t} d3d;\n\n\t\tstruct\n\t\t{\t//these are ALWAYS pointers in vulkan (annoyingly unlike many of its typedefs).\n\t\t\tvoid *instance;\n\t\t\tvoid *physicaldevice;\n\t\t\tvoid *device;\n\t\t\tunsigned int queuefamily;\n\t\t\tunsigned int queueindex;\n\t\t} vk;\n\t};\n} vrsetup_t;\n\n#define VRF_OVERRIDEFRAMETIME\t1\t//the vr interface is responsible for determining frame intervals instead of using regular clocks (so they can fiddle with prediction etc)\n#define VRF_UIACTIVE\t\t\t2\t//we're actually rendering through a headset. use 3d rendering exclusively, with VR UI and stuff.\n\n//interface registered by plugins for VR stuff.\ntypedef struct plugvrfuncs_s\n{\n\tconst char\t*description;\n\tqboolean\t(*Prepare)\t(vrsetup_t *setupinfo);\t//called before graphics context init\n\tqboolean\t(*Init)\t\t(vrsetup_t *setupinfo, rendererstate_t *info);\t//called after graphics context init\n\tunsigned int\t(*SyncFrame)(double *frametime);\t//called in the client's main loop, to block/tweak frame times. True means the game should render as fast as possible.\n\tqboolean\t(*Render)\t(void(*rendereye)(texid_t tex, const pxrect_t *viewport, const vec4_t fovoverride, const float projmatrix[16], const float eyematrix[12]));\n\tvoid\t\t(*Shutdown)\t(void);\n#define plugvrfuncs_name \"VR\"\n} plugvrfuncs_t;\n"
  },
  {
    "path": "engine/client/wad.c",
    "content": "/*\nCopyright (C) 1996-1997 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  \n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n*/\n// wad.c\n\n#include \"quakedef.h\"\n#include \"shader.h\"\n\nvoid *wadmutex;\n\n#ifndef PACKAGE_TEXWAD\nvoid Wads_Flush (void){}\nqboolean Wad_NextDownload (void){return true;}\nvoid *W_GetLumpName (const char *name, size_t *size, qbyte *type) {return NULL;}\nqbyte *W_GetTexture(const char *name, int *width, int *height, uploadfmt_t *format){return NULL;}\nvoid W_LoadWadFile (char *filename){}\nvoid W_Shutdown (void){}\nvoid CL_Skygroup_f(void){}\n#else\n\nint\t\t\twad_numlumps;\nlumpinfo_t\t*wad_lumps;\nqbyte\t\t*wad_base;\n\nstatic char wads[4096];\n\nvoid SwapPic (qpic_t *pic);\n\n/*\n==================\nW_CleanupName\n\nLowercases name and pads with spaces and a terminating 0 to the length of\nlumpinfo_t->name.\nUsed so lumpname lookups can proceed rapidly by comparing 4 chars at a time\nSpace padding is so names can be printed nicely in tables.\nCan safely be performed in place.\n==================\n*/\nvoid W_CleanupName (const char *in, char *out)\n{\n\tint\t\ti;\n\tint\t\tc;\n\n\tif (!strncmp(in, \"textures/\", 9))\n\t\tin += 9;\n\t\n\tfor (i=0 ; i<16 ; i++ )\n\t{\n\t\tc = in[i];\n\t\tif (!c)\n\t\t\tbreak;\n\t\t\t\n\t\tif (c >= 'A' && c <= 'Z')\n\t\t\tc += ('a' - 'A');\n\t\tif (c == '*')\t//not a valid filesystem char\n\t\t\tc = '#';\n\t\tout[i] = c;\n\t}\n\t\n\tfor ( ; i< 16 ; i++ )\n\t\tout[i] = 0;\n}\n\n\nvoid W_Shutdown (void)\n{\n\tif (wad_base)\n\t\tZ_Free(wad_base);\n\twad_base = NULL;\n}\n\n/*\n====================\nW_LoadWadFile\n====================\n*/\nvoid W_LoadWadFile (char *filename)\n{\n\tlumpinfo_t\t\t*lump_p;\n\twadinfo_t\t\t*header;\n\tunsigned\t\ti;\n\tint\t\t\t\tinfotableofs;\n\n\tif (wad_base)\n\t\tZ_Free(wad_base);\n\t\n\twad_base = COM_LoadFile (filename, 0, 0, NULL);\n\tif (!wad_base)\n\t{\n\t\twad_numlumps = 0;\n\t\tCon_DPrintf (\"W_LoadWadFile: couldn't load %s\\n\", filename);\n\t\treturn;\n\t}\n\n\theader = (wadinfo_t *)wad_base;\n\t\n\tif (header->identification[0] != 'W'\n\t|| header->identification[1] != 'A'\n\t|| header->identification[2] != 'D'\n\t|| (header->identification[3] != '2' && header->identification[3] != '3'))\n\t{\n\t\tCon_Printf (\"W_LoadWadFile: Wad file %s doesn't have WAD2 id\\n\",filename);\n\t\twad_numlumps = 0;\n\t\tZ_Free(wad_base);\n\t\twad_base = NULL;\n\t\treturn;\n\t}\n\t\t\n\twad_numlumps = LittleLong(header->numlumps);\n\tinfotableofs = LittleLong(header->infotableofs);\n\twad_lumps = (lumpinfo_t *)(wad_base + infotableofs);\n\t\n\tfor (i=0, lump_p = wad_lumps ; i<wad_numlumps ; i++,lump_p++)\n\t{\n\t\tlump_p->filepos = LittleLong(lump_p->filepos);\n\t\tlump_p->size = LittleLong(lump_p->size);\n\t\tW_CleanupName (lump_p->name, lump_p->name);\n\t\tif (lump_p->type == TYP_QPIC)\n\t\t\tSwapPic ( (qpic_t *)(wad_base + lump_p->filepos));\n\t}\n}\n\n/*\n=============\nW_GetLumpName\n=============\n*/\nvoid *W_GetLumpName (const char *name, size_t *size, qbyte *type)\n{\n\tint\t\ti;\n\tlumpinfo_t\t*lump_p;\n\tchar\tclean[16];\n\n\t*type = 0;\n\t*size = 0;\n\n\tW_CleanupName (name, clean);\n\n\tfor (lump_p=wad_lumps, i=0 ; i<wad_numlumps ; i++,lump_p++)\n\t{\n\t\tif (!strcmp(clean, lump_p->name))\n\t\t{\n\t\t\t*type = lump_p->type;\n\t\t\t*size = lump_p->disksize;\n\t\t\treturn (void *)(wad_base+lump_p->filepos);\n\t\t}\n\t}\n\treturn NULL;\n}\n\n/*\n=============================================================================\n\nautomatic qbyte swapping\n\n=============================================================================\n*/\n\nvoid SwapPic (qpic_t *pic)\n{\n\tpic->width = LittleLong(pic->width);\n\tpic->height = LittleLong(pic->height);\t\n}\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n// based on original code by LordHavoc\n\n//FIXME: convert to linked list. is hunk possible?\n//hash tables?\n#define TEXWAD_MAXIMAGES 16384\n\ntypedef struct wadfile_s\n{\n\tvfsfile_t *file;\n\tstruct wadfile_s *next;\n\tchar name[1];\n} wadfile_t;\n\ntypedef struct\n{\n\tchar name[16];\n\tvfsfile_t *file;\n\tint position;\n\tint size;\n} texwadlump_t;\nstatic int numwadtextures;\nstatic texwadlump_t texwadlump[TEXWAD_MAXIMAGES];\n\nstatic wadfile_t *openwadfiles;\n\nvoid Wads_Flush (void)\n{\n\twadfile_t *wf;\n\tif (wadmutex)\n\t\tSys_LockMutex(wadmutex);\n\twhile(openwadfiles)\n\t{\n\t\tVFS_CLOSE(openwadfiles->file);\n\n\t\twf = openwadfiles->next;\n\t\tZ_Free(openwadfiles);\n\t\topenwadfiles = wf;\n\t}\n\n\tnumwadtextures=0;\n\tif (wadmutex)\n\t\tSys_UnlockMutex(wadmutex);\n}\n/*\n====================\nW_LoadTextureWadFile\n====================\n*/\nvoid W_LoadTextureWadFile (char *filename, int complain)\n{\n\tlumpinfo_t\t\t*lumps, *lump_p;\n\twadinfo_t\t\theader;\n\tint\t\t\t\ti, j;\n\tint\t\t\t\tinfotableofs;\n\tvfsfile_t\t\t*file;\n\tint\t\t\t\tnumlumps;\n\n\twadfile_t *wf = openwadfiles;\n\twhile(wf)\n\t{\n\t\tif (!strcmp(wf->name, filename))\t//already loaded\n\t\t\treturn;\n\n\t\twf = wf->next;\n\t}\n\n\tfile = FS_OpenVFS(filename, \"rb\", FS_GAME);\n\tif (!file)\n\t\tfile = FS_OpenVFS(va(\"textures/halflife/%s\", filename), \"rb\", FS_GAME);\n\tif (!file)\n\t{\n\t\tif (complain)\n\t\t\tCon_Printf (\"W_LoadTextureWadFile: couldn't find %s\", filename);\n\t\treturn;\n\t}\n\n\tif (VFS_READ(file, &header, sizeof(wadinfo_t)) != sizeof(wadinfo_t))\n\t{Con_Printf (\"W_LoadTextureWadFile: unable to read wad header\");return;}\n\n\tif (memcmp(header.identification, \"WAD3\", 4) && memcmp(header.identification, \"WAD2\", 4))\n\t{Con_Printf (\"W_LoadTextureWadFile: Wad file %s doesn't have WAD3 id\\n\",filename);return;}\n\n\tnumlumps = LittleLong(header.numlumps);\n\tif (numlumps < 1 || numlumps > TEXWAD_MAXIMAGES)\n\t{Con_Printf (\"W_LoadTextureWadFile: invalid number of lumps (%i)\\n\", numlumps);return;}\n\tinfotableofs = LittleLong(header.infotableofs);\n\tif (!VFS_SEEK(file, infotableofs))\n\t{Con_Printf (\"W_LoadTextureWadFile: unable to seek to lump table\");return;}\n\tif (!((lumps = Hunk_TempAlloc(sizeof(lumpinfo_t)*numlumps))))\n\t{Con_Printf (\"W_LoadTextureWadFile: unable to allocate temporary memory for lump table\");return;}\n\n\tif (VFS_READ(file, lumps, sizeof(lumpinfo_t)*numlumps) != (int)sizeof(lumpinfo_t) * numlumps)\n\t{Con_Printf (\"W_LoadTextureWadFile: unable to read lump table\");return;}\n\n\tfor (i=0, lump_p = lumps ; i<numlumps ; i++,lump_p++)\n\t{\n\t\tW_CleanupName (lump_p->name, lump_p->name);\n\t\tfor (j = 0;j < numwadtextures;j++)\n\t\t{\n\t\t\tif (!strcmp(lump_p->name, texwadlump[j].name)) // name match, replace old one\n\t\t\t\tbreak;\n\t\t}\n\t\tif (j >= TEXWAD_MAXIMAGES)\n\t\t\tbreak; // abort loading\n\t\tif (j == numwadtextures)\n\t\t{\n\t\t\tW_CleanupName (lump_p->name, texwadlump[j].name);\n\t\t\ttexwadlump[j].file = file;\n\t\t\ttexwadlump[j].position = LittleLong(lump_p->filepos);\n\t\t\ttexwadlump[j].size = LittleLong(lump_p->disksize);\n\t\t\tnumwadtextures++;\n\t\t}\n\t}\t\n\t// leaves the file open\n\n\twf = BZ_Malloc(sizeof(*wf) + strlen(filename));\n\tstrcpy(wf->name, filename);\n\twf->file = file;\n\twf->next = openwadfiles;\n\topenwadfiles = wf;\n}\n\n/*\nvoid W_ApplyGamma (qbyte *data, int len, int skipalpha)\n{\n\tint\t\ti, inf;\n\tqbyte gammatable[256];\n\t\n\tif (v_gamma.value == 1.0)\n\t{\n\t\tfor (i=0 ; i<256 ; i++)\n\t\t\tgammatable[i] = i;\t\t\n\t}\n\telse\n\t{\n\t\tfor (i=0 ; i<256 ; i++)\n\t\t{\n\t\t\tinf = 255 * pow ( (i+0.5)/255.5 , v_gamma.value ) + 0.5;\n\t\t\tif (inf < 0)\n\t\t\t\tinf = 0;\n\t\t\tif (inf > 255)\n\t\t\t\tinf = 255;\n\t\t\tgammatable[i] = inf;\n\t\t}\n\t}\n}\n*/\nqbyte *W_ConvertWAD3Texture(miptex_t *tex, size_t lumpsize, int *width, int *height, uploadfmt_t *format)\t//returns rgba\n{\t\n\tqbyte *in, *data, *out, *pal;\n\tint d, p;\n\n\tint alpha = 0;\n\n\tif (tex->name[0] == '{')\n\t\talpha = 1;\n\n\tif (tex->width > 0x10000 || tex->height > 0x10000)\n\t\treturn NULL;\n\n\t//use malloc here if you want, but you'll have to free it again... NUR!\n\tdata = out = BZ_Malloc(tex->width * tex->height * 4);\n\n\tif (!data)\n\t\treturn NULL;\n\n\tin = (qbyte *)tex + tex->offsets[0];\n\n\t*width = tex->width;\n\t*height = tex->height;\n\n\t//halflife wads have palettes embedded in them. but make sure everything else is packed because some quake wads are weird.\n\tif (tex->offsets[0] == sizeof(*tex) &&\n\t\ttex->offsets[1] == tex->offsets[0] + (tex->width)*(tex->height) &&\n\t\ttex->offsets[2] == tex->offsets[1] + (tex->width>>1)*(tex->height>>1) && \n\t\ttex->offsets[3] == tex->offsets[2] + (tex->width>>2)*(tex->height>>2) && \n\t\t((lumpsize+3)&~3) >= ((tex->offsets[3] + (tex->width>>3)*(tex->height>>3) + 2 + 768+3)&~3))\n\t{\n\t\tshort fl = *(short*)((qbyte *)tex + tex->offsets[3] + (tex->width>>3)*(tex->height>>3));\n\t\tif (fl == 256)\n\t\t\tpal = (qbyte *)tex + tex->offsets[3] + (tex->width>>3)*(tex->height>>3) + 2;\n\t\telse\n\t\t\tpal = host_basepal;\n\t}\n\telse\n\t\tpal = host_basepal;\n\n\t/* handle decals type textures -eukara */\n\tif (alpha == 1 && (pal[765] == 255 && pal[766] == 255 && pal[767] == 255))\n\t\talpha = 3;\n\tif (alpha == 1 && !(pal[765] == 0 && pal[766] == 0 && pal[767] == 255))\n\t\talpha = 2;\n\n\tif (tex->offsets[0] + tex->width * tex->height > lumpsize)\n\t{\t//fucked texture.\n\t\tfor (d = 0;d < tex->width * tex->height;d++)\t\n\t\t{\n\t\t\tout[0] = 0;\n\t\t\tout[1] = 255;\n\t\t\tout[2] = 0;\n\t\t\tout[3] = 255;\n\t\t\tout += 4;\n\t\t}\n\t}\n\telse for (d = 0;d < tex->width * tex->height;d++)\n\t{\n\t\tp = *in++;\n\t\tif (alpha == 1 && p == 255) {\n\t\t\tout[0] = out[1] = out[2] = out[3] = 0;\n\t\t} else if (alpha == 2) {\n\t\t\tp *= 3;\n\t\t\t/* this will be a blended decal -eukara */\n\t\t\tout[0] = pal[765];\n\t\t\tout[1] = pal[766];\n\t\t\tout[2] = pal[767];\n\t\t\tout[3] = 255 - pal[p];\n\t\t} else if (alpha == 3) {\n\t\t\tp *= 3;\n\t\t\t/* this is used for glass and so on -eukara */\n\t\t\tout[0] = 255;\n\t\t\tout[1] = 255;\n\t\t\tout[2] = 255;\n\t\t\tout[3] = pal[p];\n\t\t} else {\n\t\t\tp *= 3;\n\t\t\t/* non transparent -eukara */\n\t\t\tout[0] = pal[p];\n\t\t\tout[1] = pal[p+1];\n\t\t\tout[2] = pal[p+2];\n\t\t\tout[3] = 255;\n\t\t}\n\t\tout += 4;\n\t}\n\t*format = alpha?PTI_RGBA8:PTI_RGBX8;\n\tif (!vid_hardwaregamma.value)\n\t\tBoostGamma(data, tex->width, tex->height, *format);\n\treturn data;\n}\n\nqbyte *W_GetTexture(const char *name, int *width, int *height, uploadfmt_t *format)//returns rgba\n{\n\tchar texname[17];\n\tint i, j;\n\tvfsfile_t *file;\n\tmiptex_t *tex;\n\tqbyte *data;\n\n\tif (!strncmp(name, \"gfx/\", 4) || !strncmp(name, \"wad/\", 4))\n\t{\n\t\tqpic_t *p;\n\t\tsize_t lumpsize;\n\t\tqbyte lumptype;\n\t\tp = W_GetLumpName(name+4, &lumpsize, &lumptype);\n\t\tif (p)\n\t\t{\n\t\t\tif (/*lumptype == TYP_MIPTEX && */!strcmp(name+4, \"conchars\") && (lumpsize==128*128\n#ifdef HAVE_LEGACY\n\t\t\t\t\t\t\t|| (lumptype == TYP_QPIC&&lumpsize==8+128*128)\n#endif\n\t\t\t\t\t\t\t))\n\t\t\t{\t//conchars has no header.\n\t\t\t\tqbyte *lump = (qbyte*)p;\n\t\t\t\textern cvar_t con_ocranaleds;\n\n\t\t\t\tif (lumpsize==8+128*128)\n\t\t\t\t\tCon_Printf(CON_WARNING\"WARNING: gfx.wad conchars lump has incorrect lump size.\\n\");\n\n\t\t\t\tif (con_ocranaleds.ival)\n\t\t\t\t{\n\t\t\t\t\tif (con_ocranaleds.ival != 2 || CalcHashInt(&hash_crc16, lump, 128*128) == 798)\n\t\t\t\t\t\tAddOcranaLEDsIndexed (lump, 128, 128);\n\t\t\t\t}\n\n\t\t\t\t*width = 128;\n\t\t\t\t*height = 128;\n\t\t\t\t*format = PTI_RGBA8;\n\n\t\t\t\tdata = BZ_Malloc(128 * 128 * 4);\n\t\t\t\tfor (i = 0; i < 128 * 128; i++)\n\t\t\t\t{\n\t\t\t\t\tqbyte b = lump[i];\n\t\t\t\t\tif (b == 0)\n\t\t\t\t\t\t((unsigned int*)data)[i] = 0;\n\t\t\t\t\telse\n\t\t\t\t\t\t((unsigned int*)data)[i] = d_8to24rgbtable[lump[i]] | 0xff000000;\n\t\t\t\t}\n\n\t\t\t\treturn data;\n\t\t\t}\n\t\t\telse if (lumptype == TYP_QPIC && lumpsize == 8+p->width*p->height)\n\t\t\t{\n\t\t\t\tqboolean alpha = false;\n\t\t\t\tqbyte pal;\n\t\t\t\t*width = p->width;\n\t\t\t\t*height = p->height;\n\n\t\t\t\tdata = BZ_Malloc(p->width * p->height * 4);\n\t\t\t\tfor (i = 0; i < p->width * p->height; i++)\n\t\t\t\t{\n\t\t\t\t\tpal = p->data[i];\n\t\t\t\t\t((unsigned int*)data)[i] = d_8to24rgbtable[pal];\n\t\t\t\t\tif (pal == 0xff)\n\t\t\t\t\t\talpha = true;\n\t\t\t\t}\n\t\t\t\t*format = alpha?PTI_RGBA8:PTI_RGBX8;\n\t\t\t\treturn data;\n\t\t\t}\n\t\t\telse if (lumptype == TYP_QPIC && lumpsize == 8+p->width*p->height+4+768)\n\t\t\t{\t//halflife, 24bit palette at end\n\t\t\t\tqbyte *pal = p->data+p->width*p->height+4;\n\t\t\t\t*width = p->width;\n\t\t\t\t*height = p->height;\n\t\t\t\t*format = PTI_RGBA8;\n\n\t\t\t\tdata = BZ_Malloc(p->width * p->height * 4);\n\t\t\t\tfor (i = 0; i < p->width * p->height; i++)\n\t\t\t\t{\n\t\t\t\t\tqbyte *rgb = pal + p->data[i]*3;\n\t\t\t\t\tdata[i*4+0] = rgb[0];\n\t\t\t\t\tdata[i*4+1] = rgb[1];\n\t\t\t\t\tdata[i*4+2] = rgb[2];\n\t\t\t\t\tdata[i*4+3] = ((p->data[i]==255)?0:255);\n\t\t\t\t}\n\t\t\t\treturn data;\n\t\t\t}\n\t\t\telse if (lumptype == TYP_HLFONT)\n\t\t\t\t; //FIXME... gah\n#ifdef IMAGEFMT_PVR\n\t\t\telse if (lumptype == TYP_PVRTEX)\n\t\t\t{\t//hldc, pvr texture\n\t\t\t\treturn ReadPVRFile((qbyte*)p, lumpsize, width, height, format, true);\n\t\t\t}\n#endif\n\t\t\telse\n\t\t\t\tCon_Printf(\"W_GetTexture: unknown lump type\\n\");\n\t\t}\n\t}\n\n\ttexname[16] = 0;\n\tW_CleanupName (name, texname);\n\tSys_LockMutex(wadmutex);\n\tfor (i = 0;i < numwadtextures;i++)\n\t{\n\t\tif (!strcmp(texname, texwadlump[i].name)) // found it\n\t\t{\n\t\t\tfile = texwadlump[i].file;\n\n\t\t\tif (VFS_SEEK(file, texwadlump[i].position))\n\t\t\t{\n\t\t\t\ttex = BZ_Malloc(texwadlump[i].size);\t//temp buffer for disk info (was hunk_tempalloc, but that wiped loading maps and the like\n\t\t\t\tif (tex && VFS_READ(file, tex, texwadlump[i].size) == texwadlump[i].size)\n\t\t\t\t{\n\t\t\t\t\tSys_UnlockMutex(wadmutex);\n\n#ifdef IMAGEFMT_PVR\n\t\t\t\t\t//hldc, pvr texture\n\t\t\t\t\tif (!memcmp((qbyte*)tex, \"GBIX\", 4) || !memcmp((qbyte*)tex, \"PVRT\", 4))\n\t\t\t\t\t{\n\t\t\t\t\t\tdata = ReadPVRFile((qbyte*)tex, texwadlump[i].size, width, height, format, true);\n\t\t\t\t\t\tBZ_Free(tex);\n\t\t\t\t\t\treturn data;\n\t\t\t\t\t}\n#endif\n\n\t\t\t\t\ttex->width = LittleLong(tex->width);\n\t\t\t\t\ttex->height = LittleLong(tex->height);\n\t\t\t\t\tfor (j = 0;j < MIPLEVELS;j++)\n\t\t\t\t\t\ttex->offsets[j] = LittleLong(tex->offsets[j]);\n\n\t\t\t\t\tdata = W_ConvertWAD3Texture(tex, texwadlump[i].size, width, height, format);\n\t\t\t\t\tBZ_Free(tex);\n\t\t\t\t\treturn data;\n\t\t\t\t}\n\t\t\t}\n\t\t\tCon_Printf(\"W_GetTexture: corrupt WAD3 file\\n\");\n\t\t\tbreak;\n\t\t}\n\t}\t\n\tSys_UnlockMutex(wadmutex);\n\treturn NULL;\n}\n\nmiptex_t *W_GetMipTex(const char *name)\n{\n\tchar texname[17];\n\tint i, j;\n\tvfsfile_t *file;\n\tmiptex_t *tex;\n\n\ttexname[16] = 0;\n\tW_CleanupName (name, texname);\n\tSys_LockMutex(wadmutex);\n\tfor (i = 0;i < numwadtextures;i++)\n\t{\n\t\tif (!strcmp(texname, texwadlump[i].name)) // found it\n\t\t{\n\t\t\tfile = texwadlump[i].file;\n\t\t\tif (VFS_SEEK(file, texwadlump[i].position))\n\t\t\t{\n\t\t\t\ttex = BZ_Malloc(texwadlump[i].size);\t//temp buffer for disk info (was hunk_tempalloc, but that wiped loading maps and the like\n\t\t\t\tif (tex && VFS_READ(file, tex, texwadlump[i].size) == texwadlump[i].size)\n\t\t\t\t{\n\t\t\t\t\tSys_UnlockMutex(wadmutex);\n\t\t\t\t\ttex->width = LittleLong(tex->width);\n\t\t\t\t\ttex->height = LittleLong(tex->height);\n\t\t\t\t\tfor (j = 0;j < MIPLEVELS;j++)\n\t\t\t\t\t\ttex->offsets[j] = LittleLong(tex->offsets[j]);\n\t\t\t\t\treturn tex;\n\t\t\t\t}\n\t\t\t}\n\t\t\tCon_Printf(\"W_GetTexture: corrupt WAD3 file\\n\");\n\t\t\tbreak;\n\t\t}\n\t}\t\n\tSys_UnlockMutex(wadmutex);\n\treturn NULL;\n}\n\nvoid WAD_ImageList_f(void)\n{\n\twadfile_t *wad;\n\tint i;\n\tchar *match = Cmd_Argv(1);\n\tSys_LockMutex(wadmutex);\n\tfor (i = 0;i < numwadtextures;i++)\n\t{\n\t\tif (*match && !wildcmp(match, texwadlump[i].name))\n\t\t\tcontinue;\n\t\tfor (wad = openwadfiles; wad; wad = wad->next)\n\t\t\tif (wad->file == texwadlump[i].file)\n\t\t\t\tbreak;\n\t\tCon_Printf(\"^[\\\\img\\\\%s\\\\s\\\\%i\\\\tip\\\\From inside %s^] %s\\n\", texwadlump[i].name, 64, wad?wad->name:\"<unknown>\", texwadlump[i].name);\n\t}\n\tSys_UnlockMutex(wadmutex);\n}\n\ntypedef struct mapgroup_s {\n\tchar *mapname;\n\tchar *skyname;\n\tstruct mapgroup_s *next;\n} mapskys_t;\nstatic mapskys_t *mapskies;\nvoid CL_Skygroup_f(void)\n{\n\tmapskys_t **link;\n\tmapskys_t *ms;\n\tchar *skyname;\n\tchar *mapname;\n\tint i;\n\tint remove;\n\n\tskyname = Cmd_Argv(1);\n\n\tif (!*skyname)\n\t{\n\t\tskyname = NULL;\n\t\tfor (ms = mapskies; ms; ms = ms->next)\n\t\t{\n\t\t\tif (!skyname || strcmp(skyname, ms->skyname))\n\t\t\t{\n\t\t\t\tCon_Printf(\"%s%s:\", skyname?\"\\n\":\"\", ms->skyname);\n\t\t\t\tskyname=ms->skyname;\n\t\t\t}\n\t\t\tCon_Printf(\" %s\", ms->mapname);\n\t\t}\n\t\tif (skyname)\n\t\t\tCon_Printf(\"\\n\");\n\t\telse\n\t\t\tCon_Printf(\"No skygroups defined\\n\");\n\t\treturn;\n\t}\n\n\tif (!strcmp(skyname, \"clear\") && Cmd_Argc() == 2)\n\t{\n\t\twhile (mapskies)\n\t\t{\n\t\t\tms = mapskies->next;\n\t\t\tZ_Free(mapskies);\n\t\t\tmapskies = ms;\n\t\t}\n\t\treturn;\n\t}\n\n\tif (*skyname == '-')\n\t{\n\t\tskyname++;\n\t\tfor (link = &mapskies; *link; )\n\t\t{\n\t\t\tif (!strcmp((*link)->mapname, skyname) || !strcmp((*link)->skyname, skyname))\n\t\t\t{\n\t\t\t\tms = *link;\n\t\t\t\t*link = ms->next;\n\t\t\t\tZ_Free(ms);\n\t\t\t}\n\t\t\telse\n\t\t\t\tlink = &(*link)->next;\n\t\t}\n\t\treturn;\n\t}\n\n\tfor (i = 2; i < Cmd_Argc(); i++)\n\t{\n\t\tmapname = Cmd_Argv(i);\n\n\t\tremove = *mapname == '-';\n\t\tmapname += remove;\n\n\t\tfor (link = &mapskies; *link; link = &(*link)->next)\n\t\t{\n\t\t\tif (!strcmp((*link)->mapname, mapname))\n\t\t\t{\n\t\t\t\tms = *link;\n\t\t\t\t*link = ms->next;\n\t\t\t\tZ_Free(ms);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (remove)\n\t\t\tcontinue;\n\n\t\tms = Z_Malloc(sizeof(*ms) + strlen(mapname) + strlen(skyname) + 2);\n\n\t\tms->mapname = (char*)(ms+1);\n\t\tms->skyname = ms->mapname + strlen(mapname)+1;\n\t\tms->next = mapskies;\n\n\t\tstrcpy(ms->mapname, mapname);\n\t\tstrcpy(ms->skyname, skyname);\n\n\t\tmapskies = ms;\n\t}\n}\n\n//textures/fred.wad is the DP standard - I wanna go for that one.\n//textures/halfline/fred.wad is what fuhquake can use (yuck). \n//fred.wad is what half-life supports.\n\n//we only try one download, for textures/fred.wad\n//but we will load wads from the other two paths if we have them locally.\nqboolean Wad_NextDownload (void)\n{\n\tchar wadname[4096+9]=\"textures/\";\n\tint i, j, k;\n\tmodel_t *wmodel = cl.worldmodel;\n\tif (wmodel && (wmodel->fromgame == fg_halflife || wmodel->type == mod_heightmap) && *wads)\t//now go about checking the wads\n\t{\n\t\tj = 0;\n\t\twads[4095] = '\\0';\n\t\tfor (i = 0;i < 4095;i++)\n\t\t\tif (wads[i] != ';' && wads[i] != '\\\\' && wads[i] != '/' && wads[i] != ':')\n\t\t\t\tbreak;\n\t\tif (wads[i])\n\t\t{\n\t\t\tj=i;\n\t\t\tfor (;i < 4095;i++)\n\t\t\t{\n\t\t\t\t// ignore path...\n\t\t\t\tif (wads[i] == '\\\\' || wads[i] == '/' || wads[i] == ':')\n\t\t\t\t\tj = i+1;\n\t\t\t\telse if (wads[i] == ';' || wads[i] == 0)\n\t\t\t\t{\n\t\t\t\t\tk = wads[i];\n\t\t\t\t\twads[i] = 0;\n\t\t\t\t\tstrcpy(wadname+9, &wads[j]);\n\t\t\t\t\tif (wadname[9])\n\t\t\t\t\t{\n\t\t\t\t\t\tif (!COM_FCheckExists(wadname+9))\t//wad is in root dir, so we don't need to try textures.\n\t\t\t\t\t\t\tCL_CheckOrEnqueDownloadFile(wadname, wadname, DLLF_REQUIRED);\t//don't skip this one, or the world is white.\n\t\t\t\t\t}\n\t\t\t\t\twads[i] = k;\n\t\t\t\t\t\n\t\t\t\t\tj = i+1;\n\t\t\t\t\tif (!k)\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tWads_Flush();\n\tif (*wads)\t//now go about loading the wads, we are now safe from tempallocs\n\t{\n\t\tj = 0;\n\t\twads[4095] = '\\0';\n\t\tfor (i = 0;i < 4095;i++)\n\t\t\tif (wads[i] != ';' && wads[i] != '\\\\' && wads[i] != '/' && wads[i] != ':')\n\t\t\t\tbreak;\n\t\tif (wads[i])\n\t\t{\n\t\t\tj=i;\n\t\t\tfor (;i < 4095;i++)\n\t\t\t{\n\t\t\t\t// ignore path...\n\t\t\t\tif (wads[i] == '\\\\' || wads[i] == '/' || wads[i] == ':')\n\t\t\t\t\tj = i+1;\n\t\t\t\telse if (wads[i] == ';' || wads[i] == 0)\n\t\t\t\t{\n\t\t\t\t\tk = wads[i];\n\t\t\t\t\twads[i] = 0;\n\t\t\t\t\tQ_strncpyz(wadname+9, &wads[j], sizeof(wadname)-9);\n\t\t\t\t\tif (wadname[9])\n\t\t\t\t\t{\n\t\t\t\t\t\tif (COM_FCheckExists(wadname+9))\n\t\t\t\t\t\t\tW_LoadTextureWadFile (wadname+9, false);\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tW_LoadTextureWadFile (wadname, false);\n\t\t\t\t\t}\n\t\t\t\t\tj = i+1;\n\t\t\t\t\tif (!k)\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn true;\n}\n#endif\n\nvoid Mod_ParseInfoFromEntityLump(model_t *wmodel)\t//actually, this should be in the model code.\n{\n\tchar token[4096];\n\tchar key[128];\n\tchar skyname[128];\n\tconst char *data = Mod_GetEntitiesString(wmodel);\n#ifdef PACKAGE_TEXWAD\n\tmapskys_t *msky;\n\n\tif (wmodel && wmodel->fromgame == fg_halflife)\n\t\tstrcpy(wads, \"decals.wad\");\n\telse\n\t\twads[0] = '\\0';\n#endif\n\n\tcl.skyrotate = 0;\n\tVectorClear(cl.skyaxis);\n\n#ifndef CLIENTONLY\n\tif (isDedicated)\t//don't bother\n\t\treturn;\n#endif\n\n\t// this hack is necessary to ensure Quake 2 maps get their default skybox, without breaking q1 etc\n\tif (wmodel && wmodel->fromgame == fg_quake2)\n\t\tstrcpy(skyname, \"unit1_\");\n\telse\n\t\tskyname[0] = '\\0';\n\n\tif (data)\n\tif ((data=COM_ParseOut(data, token, sizeof(token))))\t//read the map info.\n\tif (token[0] == '{')\n\twhile (1)\n\t{\n\t\tif (!(data=COM_ParseOut(data, token, sizeof(token))))\n\t\t\tbreak; // error\n\t\tif (token[0] == '}')\n\t\t\tbreak; // end of worldspawn\n\t\tif (token[0] == '_')\n\t\t\tQ_strncpyz(key, token + 1, sizeof(key));\t//_ vars are for comments/utility stuff that arn't visible to progs. Ignore them.\n\t\telse\n\t\t\tQ_strncpyz(key, token, sizeof(key));\n\t\tif (!((data=COM_ParseOut(data, token, sizeof(token)))))\n\t\t\tbreak; // error\n\t\tif (!strcmp(\"wad\", key)) // for HalfLife maps\n\t\t{\n#ifdef PACKAGE_TEXWAD\n\t\t\tQ_strncatz(wads, \";\", sizeof(wads));\t//cache it for later (so that we don't play with any temp memory yet)\n\t\t\tQ_strncatz(wads, token, sizeof(wads));\t//cache it for later (so that we don't play with any temp memory yet)\n#endif\n\t\t}\n\t\telse if (!strcmp(\"skyfog\", key)) //override cvars so mappers don't end up hacking cvars and fucking over configs (at least in other engines).\n\t\t\tCvar_LockFromServer(&r_skyfog, token);\n\t\telse if (!strcmp(\"fog\", key) || !strcmp(\"airfog\", key))\t//q1 extension. FIXME: should be made temporary.\n\t\t{\n\t\t\tkey[0] = 'f';\n\t\t\tkey[1] = 'o';\n\t\t\tkey[2] = 'g';\n\t\t\tkey[3] = ' ';\n\t\t\tQ_strncpyz(key+4, token, sizeof(key)-4);\n\t\t\tCbuf_AddText(key, RESTRICT_INSECURE);\n\t\t\tCbuf_AddText(\"\\n\", RESTRICT_INSECURE);\n\t\t}\n\t\telse if (!strcmp(\"waterfog\", key))\t//q1 extension. FIXME: should be made temporary.\n\t\t{\n\t\t\tmemcpy(key, \"waterfog \", 9);\n\t\t\tQ_strncpyz(key+9, token, sizeof(key)-9);\n\t\t\tCbuf_AddText(key, RESTRICT_INSECURE);\n\t\t\tCbuf_AddText(\"\\n\", RESTRICT_INSECURE);\n\t\t}\n\t\telse if (!strcmp(\"skyroomfog\", key))\t//q1 extension. FIXME: should be made temporary.\n\t\t{\n\t\t\tmemcpy(key, \"skyroomfog \", 11);\n\t\t\tQ_strncpyz(key+11, token, sizeof(key)-11);\n\t\t\tCbuf_AddText(key, RESTRICT_INSECURE);\n\t\t\tCbuf_AddText(\"\\n\", RESTRICT_INSECURE);\n\t\t}\n\t\telse if (!strncmp(\"cvar_\", key, 5)) //override cvars so mappers don't end up hacking cvars and fucking over configs (at least in other engines).\n\t\t{\n\t\t\tcvar_t *var = Cvar_FindVar(key+5);\n\t\t\tif (var && !(var->flags & CVAR_NOTFROMSERVER))\n\t\t\t\tCvar_LockFromServer(var, token);\n\t\t}\n\t\telse if (!strcmp(\"wateralpha\", key)) //override cvars so mappers don't end up hacking cvars and fucking over configs (at least in other engines).\n\t\t{\n\t\t\tCvar_LockFromServer(&r_wateralpha, token);\n\t\t\tCvar_LockFromServer(&r_waterstyle, \"1\");\t//force vanilla-style water too.\n\t\t}\n\t\telse if (!strcmp(\"slimealpha\", key))\n\t\t{\n\t\t\tCvar_LockFromServer(&r_slimealpha, token);\n\t\t\tCvar_LockFromServer(&r_slimestyle, \"1\");\n\t\t}\n\t\telse if (!strcmp(\"lavaalpha\", key))\n\t\t{\n\t\t\tCvar_LockFromServer(&r_lavaalpha, token);\n\t\t\tCvar_LockFromServer(&r_lavastyle, \"1\");\n\t\t}\n\t\telse if (!strcmp(\"telealpha\", key))\n\t\t{\n\t\t\tCvar_LockFromServer(&r_telealpha, token);\n\t\t\tCvar_LockFromServer(&r_telestyle, \"1\");\n\t\t}\n\t\telse if (!strcmp(\"skyroom\", key)) // for Quake mappers that lack the proper tools\n\t\t{\n\t\t\textern cvar_t v_skyroom;\n\t\t\tCvar_LockFromServer(&v_skyroom, token);\n\t\t}\n\t\telse if (!strcmp(\"skyname\", key)) // for HalfLife maps\n\t\t{\n\t\t\tQ_strncpyz(skyname, token, sizeof(skyname));\n\t\t}\n\t\telse if (!strcmp(\"sky\", key)) // for Quake2 maps\n\t\t{\n\t\t\tQ_strncpyz(skyname, token, sizeof(skyname));\n\t\t}\n\t\telse if (!strcmp(\"skyrotate\", key))\t//q2 feature\n\t\t{\n\t\t\tcl.skyrotate = atof(token);\n\t\t}\n\t\telse if (!strcmp(\"skyautorotate\", key))\t//q2ex feature\n\t\t{\n\t\t\tcl.skyautorotate = atof(token);\n\t\t}\n\t\telse if (!strcmp(\"skyaxis\", key))\t//q2 feature\n\t\t{\n\t\t\tchar *s;\n\t\t\ts = COM_ParseOut(token, key, sizeof(key));\n\t\t\tif (s)\n\t\t\t{\n\t\t\t\tcl.skyaxis[0] = atof(key);\n\t\t\t\ts = COM_ParseOut(s, key, sizeof(key));\n\t\t\t\tif (s)\n\t\t\t\t{\n\t\t\t\t\tcl.skyaxis[1] = atof(key);\n\t\t\t\t\tCOM_ParseOut(s, key, sizeof(key));\n\t\t\t\t\tif (s)\n\t\t\t\t\t\tcl.skyaxis[2] = atof(key);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tif (cl.skyrotate)\n\t{\n\t\tif (cl.skyaxis[0]||cl.skyaxis[1]||cl.skyaxis[2])\n\t\t\tCvar_Set(&r_skybox_orientation, va(\"%g %g %g %g\", cl.skyaxis[0], cl.skyaxis[1], cl.skyaxis[2], cl.skyrotate));\n\t\telse\n\t\t\tCvar_Set(&r_skybox_orientation, va(\"0 0 1 %g\", cl.skyrotate));\n\t}\n\telse\n\t\tCvar_Set(&r_skybox_orientation, \"\");\n\tCvar_SetValue(&r_skybox_autorotate, cl.skyautorotate);\n\n\tif (wmodel)\n\t{\n\t\tCOM_FileBase (wmodel->name, token, sizeof(token));\n\n#ifdef PACKAGE_TEXWAD\n\t\t//map-specific sky override feature\n\t\tfor (msky = mapskies; msky; msky = msky->next)\n\t\t{\n\t\t\tif (!strcmp(msky->mapname, token))\n\t\t\t{\n\t\t\t\tQ_strncpyz(skyname, msky->skyname, sizeof(skyname));\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n#endif\n\t}\n\n\tR_SetSky(skyname);\n}\n"
  },
  {
    "path": "engine/client/wad.h",
    "content": "/*\nCopyright (C) 1996-1997 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  \n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n*/\n// wad.h\n\n//===============\n//   TYPES\n//===============\n\n#define\tCMP_NONE\t\t0\n#define\tCMP_LZSS\t\t1\n\n#define\tTYP_NONE\t\t0\n#define\tTYP_LABEL\t\t1\n\n#define\tTYP_LUMPY\t\t64\t\t\t\t// 64 + grab command number\n#define\tTYP_PALETTE\t\t64\n#define\tTYP_QTEX\t\t65\n#define\tTYP_QPIC\t\t66\n#define\tTYP_SOUND\t\t67\n#define\tTYP_MIPTEX\t\t68\n#define\tTYP_HLFONT\t\t70\n#define\tTYP_PVRTEX\t\t71\n\n//on disk representation of most q1 images.\ntypedef struct\n{\n\tint\t\t\twidth, height;\n\tqbyte\t\tdata[4];\t\t\t// variably sized\n} qpic_t;\n\ntypedef struct shader_s shader_t;\n#define material_t shader_t\t//the material\n#define rshader_t shader_t\t//the shader the material will draw with\n#define mpic_t shader_t\n\n//atlased images within some larger atlas\n//must not be tiled etc\ntypedef struct apic_s\n{\n\tmpic_t\t*atlas;\n\tfloat\tsl, tl, sh, th;\n\tunsigned short x;\n\tunsigned short y;\n\tunsigned short width;\n\tunsigned short height;\n\n\tstruct apic_s *next;\n} apic_t;\n\nextern\tmpic_t\t\t*draw_disc;\t// also used on sbar\n\n\ntypedef struct\n{\n\tchar\t\tidentification[4];\t\t// should be WAD2 or 2DAW\n\tint\t\t\tnumlumps;\n\tint\t\t\tinfotableofs;\n} wadinfo_t;\n\ntypedef struct\n{\n\tint\t\t\tfilepos;\n\tint\t\t\tdisksize;\n\tint\t\t\tsize;\t\t\t\t\t// uncompressed\n\tchar\t\ttype;\n\tchar\t\tcompression;\n\tchar\t\tpad1, pad2;\n\tchar\t\tname[16];\t\t\t\t// must be null terminated\n} lumpinfo_t;\n\nextern\tint\t\t\twad_numlumps;\nextern\tlumpinfo_t\t*wad_lumps;\nextern\tqbyte\t\t*wad_base;\n\nvoid W_Shutdown (void);\nvoid\tW_LoadWadFile (char *filename);\nvoid\tW_CleanupName (const char *in, char *out);\n//lumpinfo_t\t*W_GetLumpinfo (char *name);\nvoid\t*W_GetLumpName (const char *name, size_t *size, qbyte *lumptype);\n//void\t*W_GetLumpNum (int num);\nvoid Wads_Flush (void);\nextern void *wadmutex;\n\nvoid SwapPic (qpic_t *pic);\n\nstruct model_s;\n\nvoid Mod_ParseWadsFromEntityLump(char *data);\nqbyte *W_ConvertWAD3Texture(miptex_t *tex, size_t lumpsize, int *width, int *height, uploadfmt_t *format);\nvoid Mod_ParseInfoFromEntityLump(struct model_s *wmodel);\nqboolean Wad_NextDownload (void);\nqbyte *W_GetTexture(const char *name, int *width, int *height, uploadfmt_t *format);\nmiptex_t *W_GetMipTex(const char *name);\n"
  },
  {
    "path": "engine/client/winamp.h",
    "content": "/*\n\n** Winamp frontend/plug-in control API documentation v1.1.\n\n** By Justin Frankel. Updates by Christophe Thibault.\n\n** Copyright (C) 1997-2000, Nullsoft Inc.\n\n** Last updated: JUL.12.2000.\n\n** \n\n** Introduction\n\n** -----------------------\n\n** This file describes a means to easily communicate to Winamp\n\n** via the classic Win32 Message API. \n\n**\n\n** These definitions/code assume C/C++. Porting to VB/Delphi shouldn't\n\n** be too hard.\n\n** \n\n** First, you find the HWND of the Winamp main window. From a plug-in\n\n** you can easily extract this from the plug-in structure (hMainWindow,\n\n** hwndParent, whatever). For external apps, use:\n\n**\n\n** HWND hwnd_winamp = FindWindow(\"Winamp v1.x\",NULL);\n\n**\n\n** (note: I know, we're in Winamp 2.x, but it's 1.x for compatibility)\n\n**\n\n** Once you have the hwnd_winamp, it's a good idea to check the version \n\n** number. To do this, you send a WM_WA_IPC message to hwnd_winamp.\n\n** Note that WM_WA_IPC is defined as Win32's WM_USER.\n\n**\n\n** Note that sometimes you might want to use PostMessage instead of\n\n** SendMessage.\n\n*/\n\n\n\n#ifndef _WAFE_H_\n\n#define _WAFE_H_\n\n\n\n#define WM_WA_IPC WM_USER\n\n\n\n/**************************************************************************/\n\n\n\n#define IPC_GETVERSION 0\n\n\n\n/*\n\n** int version = SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_GETVERSION);\n\n**\n\n** Version will be 0x20yx for winamp 2.yx. versions previous to Winamp 2.0\n\n** typically (but not always) use 0x1zyx for 1.zx versions. Weird, I know.\n\n**\n\n** The basic format for sending messages to Winamp is:\n\n** int result=SendMessage(hwnd_winamp,WM_WA_IPC,command_data,command);\n\n** (for the version check, command_data is 0).\n\n*/\n\n\n\n\n\n#define IPC_DELETE 101\n\n\n\n/*\n\n** SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_DELETE);\n\n**\n\n** You can use IPC_DELETE to clear Winamp's internal playlist.\n\n*/\n\n\n\n\n\n#define IPC_STARTPLAY 102\n\n\n\n/*\n\n** SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_STARTPLAY);\n\n**\n\n** Using IPC_STARTPLAY is like hitting 'Play' in Winamp, mostly.\n\n*/\n\n\n\n\n\n#define IPC_ISPLAYING 104\n\n\n\n/*\n\n** int res = SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_ISPLAYING);\n\n**\n\n** IPC_ISPLAYING returns the status of playback.\n\n** If it returns 1, it is playing. if it returns 3, it is paused, \n\n** if it returns 0, it is not playing.\n\n*/\n\n\n\n\n\n#define IPC_GETOUTPUTTIME 105\n\n\n\n/*\n\n** int res = SendMessage(hwnd_winamp,WM_WA_IPC,mode,IPC_GETOUTPUTTIME);\n\n**\n\n** IPC_GETOUTPUTTIME returns the position in milliseconds of the \n\n** current song (mode = 0), or the song length, in seconds (mode = 1).\n\n** Returns -1 if not playing or error.\n\n*/\n\n\n\n\n\n#define IPC_JUMPTOTIME 106\n\n\n\n/* (requires Winamp 1.60+)\n\n** SendMessage(hwnd_winamp,WM_WA_IPC,ms,IPC_JUMPTOTIME);\n\n** IPC_JUMPTOTIME sets the position in milliseconds of the \n\n** current song (approximately).\n\n** Returns -1 if not playing, 1 on eof, or 0 if successful\n\n*/\n\n\n\n\n\n#define IPC_WRITEPLAYLIST 120\n\n\n\n/* (requires Winamp 1.666+)\n\n** SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_WRITEPLAYLIST);\n\n**\n\n** IPC_WRITEPLAYLIST writes the current playlist to <winampdir>\\\\Winamp.m3u,\n\n** and returns the current playlist position.\n\n** Kinda obsoleted by some of the 2.x new stuff, but still good for when\n\n** using a front-end (instead of a plug-in)\n\n*/\n\n\n\n\n\n#define IPC_SETPLAYLISTPOS 121\n\n\n\n/* (requires Winamp 2.0+)\n\n** SendMessage(hwnd_winamp,WM_WA_IPC,position,IPC_SETPLAYLISTPOS)\n\n**\n\n** IPC_SETPLAYLISTPOS sets the playlsit position to 'position'.\n\n*/\n\n\n\n\n\n#define IPC_SETVOLUME 122\n\n\n\n/* (requires Winamp 2.0+)\n\n** SendMessage(hwnd_winamp,WM_WA_IPC,volume,IPC_SETVOLUME);\n\n**\n\n** IPC_SETVOLUME sets the volume of Winamp (from 0-255).\n\n*/\n\n\n\n\n\n#define IPC_SETPANNING 123\n\n\n\n/* (requires Winamp 2.0+)\n\n** SendMessage(hwnd_winamp,WM_WA_IPC,panning,IPC_SETPANNING);\n\n**\n\n** IPC_SETPANNING sets the panning of Winamp (from 0 (left) to 255 (right)).\n\n*/\n\n\n\n\n\n#define IPC_GETLISTLENGTH 124\n\n\n\n/* (requires Winamp 2.0+)\n\n** int length = SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_GETLISTLENGTH);\n\n**\n\n** IPC_GETLISTLENGTH returns the length of the current playlist, in\n\n** tracks.\n\n*/\n\n\n\n\n\n#define IPC_SETSKIN 200\n\n\n\n/* (requires Winamp 2.04+, only usable from plug-ins (not external apps))\n\n** SendMessage(hwnd_winamp,WM_WA_IPC,(WPARAM)\"skinname\",IPC_SETSKIN);\n\n**\n\n** IPC_SETSKIN sets the current skin to \"skinname\". Note that skinname \n\n** can be the name of a skin, a skin .zip file, with or without path. \n\n** If path isn't specified, the default search path is the winamp skins \n\n** directory.\n\n*/\n\n\n\n\n\n#define IPC_GETSKIN 201\n\n\n\n/* (requires Winamp 2.04+, only usable from plug-ins (not external apps))\n\n** SendMessage(hwnd_winamp,WM_WA_IPC,(WPARAM)skinname_buffer,IPC_GETSKIN);\n\n**\n\n** IPC_GETSKIN puts the directory where skin bitmaps can be found \n\n** into  skinname_buffer.\n\n** skinname_buffer must be MAX_PATH characters in length.\n\n** When using a .zip'd skin file, it'll return a temporary directory\n\n** where the ZIP was decompressed.\n\n*/\n\n\n\n\n\n#define IPC_EXECPLUG 202\n\n\n\n/* (requires Winamp 2.04+, only usable from plug-ins (not external apps))\n\n** SendMessage(hwnd_winamp,WM_WA_IPC,(WPARAM)\"vis_file.dll\",IPC_EXECPLUG);\n\n**\n\n** IPC_EXECPLUG executes a visualization plug-in pointed to by WPARAM.\n\n** the format of this string can be:\n\n** \"vis_whatever.dll\"\n\n** \"vis_whatever.dll,0\" // (first mod, file in winamp plug-in dir)\n\n** \"C:\\\\dir\\\\vis_whatever.dll,1\" \n\n*/\n\n\n\n\n\n#define IPC_GETPLAYLISTFILE 211\n\n\n\n/* (requires Winamp 2.04+, only usable from plug-ins (not external apps))\n\n** char *name=SendMessage(hwnd_winamp,WM_WA_IPC,index,IPC_GETPLAYLISTFILE);\n\n**\n\n** IPC_GETPLAYLISTFILE gets the filename of the playlist entry [index].\n\n** returns a pointer to it. returns NULL on error.\n\n*/\n\n\n\n\n\n#define IPC_GETPLAYLISTTITLE 212\n\n\n\n/* (requires Winamp 2.04+, only usable from plug-ins (not external apps))\n\n** char *name=SendMessage(hwnd_winamp,WM_WA_IPC,index,IPC_GETPLAYLISTTITLE);\n\n**\n\n** IPC_GETPLAYLISTTITLE gets the title of the playlist entry [index].\n\n** returns a pointer to it. returns NULL on error.\n\n*/\n\n\n\n\n\n#define IPC_GETLISTPOS 125\n\n\n\n/* (requires Winamp 2.05+)\n\n** int pos=SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_GETLISTPOS);\n\n**\n\n** IPC_GETLISTPOS returns the playlist position. A lot like IPC_WRITEPLAYLIST\n\n** only faster since it doesn't have to write out the list. Heh, silly me.\n\n*/\n\n\n\n\n\n#define IPC_GETINFO 126 \n\n\n\n/* (requires Winamp 2.05+)\n\n** int inf=SendMessage(hwnd_winamp,WM_WA_IPC,mode,IPC_GETINFO);\n\n**\n\n** IPC_GETINFO returns info about the current playing song. The value\n\n** it returns depends on the value of 'mode'.\n\n** Mode      Meaning\n\n** ------------------\n\n** 0         Samplerate (i.e. 44100)\n\n** 1         Bitrate  (i.e. 128)\n\n** 2         Channels (i.e. 2)\n\n*/\n\n\n\n\n\n#define IPC_GETEQDATA 127 \n\n\n\n/* (requires Winamp 2.05+)\n\n** int data=SendMessage(hwnd_winamp,WM_WA_IPC,pos,IPC_GETEQDATA);\n\n**\n\n** IPC_GETEQDATA queries the status of the EQ. \n\n** The value returned depends on what 'pos' is set to:\n\n** Value      Meaning\n\n** ------------------\n\n** 0-9        The 10 bands of EQ data. 0-63 (+20db - -20db)\n\n** 10         The preamp value. 0-63 (+20db - -20db)\n\n** 11         Enabled. zero if disabled, nonzero if enabled.\n\n** 12         Autoload. zero if disabled, nonzero if enabled.\n\n*/\n\n\n\n\n\n#define IPC_SETEQDATA 128\n\n/* (requires Winamp 2.05+)\n\n** SendMessage(hwnd_winamp,WM_WA_IPC,pos,IPC_GETEQDATA);\n\n** SendMessage(hwnd_winamp,WM_WA_IPC,value,IPC_SETEQDATA);\n\n**\n\n** IPC_SETEQDATA sets the value of the last position retrieved\n\n** by IPC_GETEQDATA.\n\n*/\n\n\n\n#define IPC_ADDBOOKMARK 129\n\n/* (requires Winamp 2.4+)\n\n** SendMessage(hwnd_winamp,WM_WA_IPC,(WPARAM)file,IPC_ADDBOOKMARK);\n\n**\n\n** IPC_ADDBOOKMARK will add the specified file to the Winamp bookmark list.\n\n*/\n\n\n\n#define IPC_RESTARTWINAMP 135\n\n/* (requires Winamp 2.2+)\n\n** SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_RESTARTWINAMP);\n\n**\n\n** IPC_RESTARTWINAMP will restart Winamp (isn't that obvious ? :)\n\n*/\n\n\n\n#define IPC_MBOPEN 241\n\n/* (requires Winamp 2.05+)\n\n** SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_MBOPEN);\n\n** SendMessage(hwnd_winamp,WM_WA_IPC,(WPARAM)url,IPC_MBOPEN);\n\n**\n\n** IPC_MBOPEN will open a new URL in the minibrowser. if url is NULL, it will open the Minibrowser window.\n\n*/\n\n\n\n#define IPC_INETAVAILABLE 242\n\n/* (requires Winamp 2.05+)\n\n** val=SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_INETAVAILABLE);\n\n**\n\n** IPC_INETAVAILABLE will return 1 if the Internet connection is available for Winamp.\n\n*/\n\n\n\n#define IPC_UPDTITLE 243\n\n/* (requires Winamp 2.2+)\n\n** SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_UPDTITLE);\n\n**\n\n** IPC_UPDTITLE will ask Winamp to update the informations about the current title.\n\n*/\n\n\n\n#define IPC_CHANGECURRENTFILE 245\n\n/* (requires Winamp 2.05+)\n\n** SendMessage(hwnd_winamp,WM_WA_IPC,(WPARAM)file,IPC_CHANGECURRENTFILE);\n\n**\n\n** IPC_CHANGECURRENTFILE will set the current playlist item.\n\n*/\n\n\n\n#define IPC_GETMBURL 246\n\n/* (requires Winamp 2.2+)\n\n** char buffer[4096]; // Urls can be VERY long\n\n** SendMessage(hwnd_winamp,WM_WA_IPC,(WPARAM)buffer,IPC_GETMBURL);\n\n**\n\n** IPC_GETMBURL will retrieve the current Minibrowser URL into buffer.\n\n*/\n\n\n\n#define IPC_REFRESHPLCACHE 247\n\n/* (requires Winamp 2.2+)\n\n** SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_REFRESHPLCACHE);\n\n**\n\n** IPC_REFRESHPLCACHE will flush the playlist cache buffer.\n\n*/\n\n\n\n#define IPC_MBBLOCK 248\n\n/* (requires Winamp 2.4+)\n\n** SendMessage(hwnd_winamp,WM_WA_IPC,value,IPC_MBBLOCK);\n\n**\n\n** IPC_MBBLOCK will block the Minibrowser from updates if value is set to 1\n\n*/\n\n\n\n#define IPC_MBOPENREAL 249\n\n/* (requires Winamp 2.4+)\n\n** SendMessage(hwnd_winamp,WM_WA_IPC,(WPARAM)url,IPC_MBOPENREAL);\n\n**\n\n** IPC_MBOPENREAL works the same as IPC_MBOPEN except that it will works even if \n\n** IPC_MBBLOCK has been set to 1\n\n*/\n\n\n\n#define IPC_GET_SHUFFLE 250\n\n/* (requires Winamp 2.4+)\n\n** val=SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_GET_SHUFFLE);\n\n**\n\n** IPC_GET_SHUFFLE returns the status of the Shuffle option (1 if set)\n\n*/\n\n\n\n#define IPC_GET_REPEAT 251\n\n/* (requires Winamp 2.4+)\n\n** val=SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_GET_REPEAT);\n\n**\n\n** IPC_GET_REPEAT returns the status of the Repeat option (1 if set)\n\n*/\n\n\n\n#define IPC_SET_SHUFFLE 252\n\n/* (requires Winamp 2.4+)\n\n** SendMessage(hwnd_winamp,WM_WA_IPC,value,IPC_SET_SHUFFLE);\n\n**\n\n** IPC_SET_SHUFFLE sets the status of the Shuffle option (1 to turn it on)\n\n*/\n\n\n\n#define IPC_SET_REPEAT 253\n\n/* (requires Winamp 2.4+)\n\n** SendMessage(hwnd_winamp,WM_WA_IPC,value,IPC_SET_REPEAT);\n\n**\n\n** IPC_SET_REPEAT sets the status of the Repeat option (1 to turn it on)\n\n*/\n\n\n\n/**************************************************************************/\n\n\n\n/*\n\n** Some API calls tend to require that you send data via WM_COPYDATA\n\n** instead of WM_USER. Such as IPC_PLAYFILE:\n\n*/\n\n\n\n#define IPC_PLAYFILE 100\n\n\n\n/*\n\n** COPYDATASTRUCT cds;\n\n** cds.dwData = IPC_PLAYFILE;\n\n** cds.lpData = (void *) \"file.mp3\";\n\n** cds.cbData = strlen((char *) cds.lpData)+1; // include space for null char\n\n** SendMessage(hwnd_winamp,WM_COPYDATA,(WPARAM)NULL,(LPARAM)&cds);\n\n**\n\n** This will play the file \"file.mp3\".\n\n**\n\n*/\n\n\n\n\n\n#define IPC_CHDIR 103\n\n\n\n/*\n\n** COPYDATASTRUCT cds;\n\n** cds.dwData = IPC_CHDIR;\n\n** cds.lpData = (void *) \"c:\\\\download\";\n\n** cds.cbData = strlen((char *) cds.lpData)+1; // include space for null char\n\n** SendMessage(hwnd_winamp,WM_COPYDATA,(WPARAM)NULL,(LPARAM)&cds);\n\n**\n\n** This will make Winamp change to the directory C:\\\\download\n\n**\n\n*/\n\n\n\n\n\n/**************************************************************************/\n\n\n\n/*\n\n** Finally there are some WM_COMMAND messages that you can use to send \n\n** Winamp misc commands.\n\n** \n\n** To send these, use:\n\n**\n\n** SendMessage(hwnd_winamp, WM_COMMAND,command_name,0);\n\n*/\n\n\n\n#define WINAMP_OPTIONS_EQ               40036 // toggles the EQ window\n\n#define WINAMP_OPTIONS_PLEDIT           40040 // toggles the playlist window\n\n#define WINAMP_VOLUMEUP                 40058 // turns the volume up a little\n\n#define WINAMP_VOLUMEDOWN               40059 // turns the volume down a little\n\n#define WINAMP_FFWD5S                   40060 // fast forwards 5 seconds\n\n#define WINAMP_REW5S                    40061 // rewinds 5 seconds\n\n\n\n// the following are the five main control buttons, with optionally shift \n\n// or control pressed\n\n// (for the exact functions of each, just try it out)\n\n#define WINAMP_BUTTON1                  40044\n\n#define WINAMP_BUTTON2                  40045\n\n#define WINAMP_BUTTON3                  40046\n\n#define WINAMP_BUTTON4                  40047\n\n#define WINAMP_BUTTON5                  40048\n\n#define WINAMP_BUTTON1_SHIFT            40144\n\n#define WINAMP_BUTTON2_SHIFT            40145\n\n#define WINAMP_BUTTON3_SHIFT            40146\n\n#define WINAMP_BUTTON4_SHIFT            40147\n\n#define WINAMP_BUTTON5_SHIFT            40148\n\n#define WINAMP_BUTTON1_CTRL             40154\n\n#define WINAMP_BUTTON2_CTRL             40155\n\n#define WINAMP_BUTTON3_CTRL             40156\n\n#define WINAMP_BUTTON4_CTRL             40157\n\n#define WINAMP_BUTTON5_CTRL             40158\n\n\n\n#define WINAMP_FILE_PLAY                40029 // pops up the load file(s) box\n\n#define WINAMP_OPTIONS_PREFS            40012 // pops up the preferences\n\n#define WINAMP_OPTIONS_AOT              40019 // toggles always on top\n\n#define WINAMP_HELP_ABOUT               40041 // pops up the about box :)\n\n\n\n\n\n/*\n\n** EOF.. Enjoy.\n\n*/\n\n\n\n#endif\t//_WAFE_H_\n\n"
  },
  {
    "path": "engine/client/winquake.h",
    "content": "/*\nCopyright (C) 1996-1997 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  \n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n*/\n// winquake.h: Win32-specific Quake header file\n\n#ifndef WINQUAKE_H\n#define WINQUAKE_H\n\n#ifdef _WIN32\nvoid GLVID_Crashed(void);\n\n#if defined(_WIN32) && !defined(WIN32)\n#define WIN32 _WIN32\n#endif\n\n#ifdef MSVCDISABLEWARNINGS \n#pragma warning( disable : 4229 )  // mgraph gets this\n#endif\n\n#ifndef _WIN32_WINNT\n#define _WIN32_WINNT 0x0400\n#endif\n#define WIN32_LEAN_AND_MEAN\n#define byte winbyte\n#ifdef _XBOX\n\t#include <xtl.h>\n\t#include <WinSockX.h>\n#else\n\t#include <windows.h>\n\t#include <mmsystem.h>\n\t#include <mmreg.h>\n#endif\n\n#define _LPCWAVEFORMATEX_DEFINED\n\n\n#if defined(WINAPI_FAMILY) && !defined(WINRT)\n\t#if WINAPI_FAMILY != WINAPI_FAMILY_DESKTOP_APP\n\t\t//don't just define it. things that don't #include winquake.h / glquake.h need it too.\n\t\t#error \"WINRT needs to be defined for non-desktop\"\n\t#endif\n#endif\n\n\n#ifndef WM_MOUSEWHEEL\n#define WM_MOUSEWHEEL\t\t\t\t\t0x020A\n#endif\n#ifndef WM_APPCOMMAND\n#define WM_APPCOMMAND\t\t\t\t\t0x0319\n#endif\n\n#define WM_USER_SPEECHTOTEXT\t\t\t(WM_USER+0)\t//used by stt\n#define WM_USER_VIDSHUTDOWN\t\t\t\t(WM_USER+4)\t//used by multithreading\n#define WM_USER_VKPRESENT\t\t\t\t(WM_USER+7)\t//used by vulkan\n#define WM_USER_NVVKPRESENT\t\t\t\t(WM_USER+8)\t//used by vulkan-over-opengl\n\n#undef byte\n\nextern\tHINSTANCE\tglobal_hInstance;\nextern\tint\t\t\tglobal_nCmdShow;\n\nextern HWND sys_parentwindow;\nextern unsigned int sys_parentleft;\nextern unsigned int sys_parenttop;\nextern unsigned int sys_parentwidth;\nextern unsigned int sys_parentheight;\n\n#ifndef SERVERONLY\n\n#ifdef HAVE_CDPLAYER\n#ifdef _WIN32\nLONG CDAudio_MessageHandler(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);\n#endif\n#endif\n\n\n//shell32 stuff that doesn't exist in win95\n#define COBJMACROS\n\n#ifndef _XBOX\n#include <shlobj.h>\n#include <shellapi.h>\nextern LPITEMIDLIST (STDAPICALLTYPE *pSHBrowseForFolderW)(LPBROWSEINFOW lpbi);\nextern BOOL (STDAPICALLTYPE *pSHGetPathFromIDListW)(LPCITEMIDLIST pidl, LPWSTR pszPath);\nextern BOOL (STDAPICALLTYPE *pSHGetSpecialFolderPathW)(HWND hwnd, LPWSTR pszPath, int csidl, BOOL fCreate);\nextern BOOL (STDAPICALLTYPE *pShell_NotifyIconW)(DWORD dwMessage, PNOTIFYICONDATAW lpData);\n#endif\n\n//void\tVID_LockBuffer (void);\n//void\tVID_UnlockBuffer (void);\n\n#endif\n\nextern HWND\t\t\tmainwindow;\nextern qboolean\t\tMinimized;\n\nextern qboolean\tWinNT;\n\nvoid INS_UpdateGrabs(int fullscreen, int activeapp);\nvoid INS_RestoreOriginalMouseState (void);\nvoid INS_SetQuakeMouseState (void);\nvoid INS_MouseEvent (int mstate);\nvoid INS_RawInput_Read(HANDLE in_device_handle);\n\nextern qboolean\twinsock_lib_initialized;\n\nextern int\t\twindow_center_x, window_center_y;\nextern RECT\t\twindow_rect;\n\nextern qboolean\tmouseinitialized;\n\n//extern HANDLE\thinput, houtput;\n\nextern HCURSOR\thArrowCursor, hCustomCursor;\nenum uploadfmt;\nvoid *WIN_CreateCursor(const qbyte *imagedata, int width, int height, enum uploadfmt format, float hotx, float hoty, float scale);\nqboolean WIN_SetCursor(void *cursor);\nvoid WIN_DestroyCursor(void *cursor);\nvoid WIN_WindowCreated(HWND window);\n\nvoid INS_UpdateClipCursor (void);\nvoid CenterWindow(HWND hWndCenter, int width, int height, BOOL lefttopjustify);\nvoid INS_TranslateKeyEvent(WPARAM wParam, LPARAM lParam, qboolean down, int pnum, qboolean genkeystate);\nint INS_AppCommand(LPARAM lParam);\nvoid INS_DeviceChanged(void *ctx, void *data, size_t a ,size_t b);\n\nvoid S_BlockSound (void);\nvoid S_UnblockSound (void);\n\nvoid VID_SetDefaultMode (void);\n/*\nint (PASCAL FAR *pWSAStartup)(WORD wVersionRequired, LPWSADATA lpWSAData);\nint (PASCAL FAR *pWSACleanup)(void);\nint (PASCAL FAR *pWSAGetLastError)(void);\nSOCKET (PASCAL FAR *psocket)(int af, int type, int protocol);\nint (PASCAL FAR *pioctlsocket)(SOCKET s, long cmd, u_long FAR *argp);\nint (PASCAL FAR *psetsockopt)(SOCKET s, int level, int optname,\n\t\t\t\t\t\t\t  const char FAR * optval, int optlen);\nint (PASCAL FAR *precvfrom)(SOCKET s, char FAR * buf, int len, int flags,\n\t\t\t\t\t\t\tstruct sockaddr FAR *from, int FAR * fromlen);\nint (PASCAL FAR *psendto)(SOCKET s, const char FAR * buf, int len, int flags,\n\t\t\t\t\t\t  const struct sockaddr FAR *to, int tolen);\nint (PASCAL FAR *pclosesocket)(SOCKET s);\nint (PASCAL FAR *pgethostname)(char FAR * name, int namelen);\nstruct hostent FAR * (PASCAL FAR *pgethostbyname)(const char FAR * name);\nstruct hostent FAR * (PASCAL FAR *pgethostbyaddr)(const char FAR * addr,\n\t\t\t\t\t\t\t\t\t\t\t\t  int len, int type);\nint (PASCAL FAR *pgetsockname)(SOCKET s, struct sockaddr FAR *name,\n\t\t\t\t\t\t\t   int FAR * namelen);\n*/\n#endif\n\n#endif\n\n"
  },
  {
    "path": "engine/client/winquake.rc",
    "content": "// Microsoft Visual C++ generated resource script.\n//\n#include \"resource.h\"\n\n#define APSTUDIO_READONLY_SYMBOLS\n/////////////////////////////////////////////////////////////////////////////\n//\n// Generated from the TEXTINCLUDE 2 resource.\n//\n#include <windows.h>\n\n#include \"../common/bothdefs.h\"\n\n/////////////////////////////////////////////////////////////////////////////\n#undef APSTUDIO_READONLY_SYMBOLS\n\n/////////////////////////////////////////////////////////////////////////////\n// English (U.S.) resources\n\n#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)\n#ifdef _WIN32\nLANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US\n#pragma code_page(1252)\n#endif //_WIN32\n\n#ifdef APSTUDIO_INVOKED\n/////////////////////////////////////////////////////////////////////////////\n//\n// TEXTINCLUDE\n//\n\n1 TEXTINCLUDE \nBEGIN\n    \"resource.h\\0\"\nEND\n\n2 TEXTINCLUDE \nBEGIN\n    \"#include \"\"windows.h\"\"\\r\\n\"\n    \"\\0\"\nEND\n\n3 TEXTINCLUDE \nBEGIN\n    \"\\r\\n\"\n    \"\\0\"\nEND\n\n#endif    // APSTUDIO_INVOKED\n\n\n/////////////////////////////////////////////////////////////////////////////\n//\n// Dialog\n//\n#if 0\nIDD_DIALOG1 DIALOGEX 0, 0, 67, 40\nSTYLE DS_SETFONT | DS_MODALFRAME | DS_SETFOREGROUND | DS_3DLOOK | DS_CENTER | WS_POPUP | WS_VISIBLE\nEXSTYLE WS_EX_TOOLWINDOW | WS_EX_CLIENTEDGE\nFONT 16, \"Times New Roman\", 0, 0, 0x1\nBEGIN\n    CTEXT           FULLENGINENAME,IDC_STATIC,0,0,67,21,SS_CENTERIMAGE\n    CTEXT           ENGINEWEBSITE,IDC_STATIC,0,23,66,17,SS_CENTERIMAGE\nEND\n#endif\n\n#endif    // English (U.S.) resources\n/////////////////////////////////////////////////////////////////////////////\n\n\n/////////////////////////////////////////////////////////////////////////////\n// English (U.K.) resources\n\n#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENG)\n#ifdef _WIN32\nLANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_UK\n#pragma code_page(1252)\n#endif //_WIN32\n\n/////////////////////////////////////////////////////////////////////////////\n//\n// Icon\n//\n\n// Icon with lowest ID value placed first to ensure application icon\n// remains consistent on all systems.\n#if defined(BRANDING_ICON) && defined(QUAKETC)\n\tIDI_ICON1               ICON                    BRANDING_ICON\n#elif defined(BRANDING_ICON)\n\tIDI_ICON1               ICON                    BRANDING_ICON\n\t#if defined( _MSC_VER ) && ( _MSC_VER >= 1500 )\n\t\tIDI_ICON2\t\t\t\tICON\t\t\t\t\t\"fte_eukara.ico\"\n\t#else\n\t\tIDI_ICON2\t\t\t\tICON\t\t\t\t\t\"fte_eukaranopng.ico\"\n\t#endif\n\tIDI_ICON3               ICON                    \"bymorphed.ico\"\n\tIDI_ICON4               ICON                    \"q2.ico\"\n#else\n\t#if defined( _MSC_VER ) && ( _MSC_VER >= 1500 )\n\t\tIDI_ICON1\t\t\t\tICON\t\t\t\t\t\"fte_eukara.ico\"\n\t#else\n\t\tIDI_ICON1\t\t\t\tICON\t\t\t\t\t\"fte_eukaranopng.ico\"\n\t#endif\n\tIDI_ICON2               ICON                    \"bymorphed.ico\"\n\tIDI_ICON3               ICON                    \"q2.ico\"\n#endif\n\n#ifdef CONFIG_MANIFEST_TEXT\n1\tRCDATA\t{CONFIG_MANIFEST_TEXT}\n#endif\n\n/////////////////////////////////////////////////////////////////////////////\n//\n// Version\n//\n\n// defines needed to transform MAJOR/MINOR defines into an x.xx string define...\n#define STR2(x) #x\n#define STR(x) STR2(x)\n#define V_DOT \".\"\n#define V_MAJ STR(FTE_VER_MAJOR)\n#if FTE_VER_MINOR < 10\n#define V_MIN STR(0) STR(FTE_VER_MINOR)\n#else\n#define V_MIN STR(FTE_VER_MINOR)\n#endif\n#define V_STR V_MAJ V_DOT V_MIN\n\nVS_VERSION_INFO VERSIONINFO\n FILEVERSION FTE_VER_MAJOR,FTE_VER_MINOR,0,0\n PRODUCTVERSION FTE_VER_MAJOR,FTE_VER_MINOR,0,0\n FILEFLAGSMASK 0x17L\n#ifdef OFFICIAL_RELEASE\n FILEFLAGS 0x0L\n#define V_SUFFIX \"\"\n#elif _DEBUG\n FILEFLAGS VS_FF_PRERELEASE|VS_FF_DEBUG\n#define V_SUFFIX \" DEBUG\"\n#else\n FILEFLAGS VS_FF_PRERELEASE\n#define V_SUFFIX \" BETA\"\n#endif\n FILEOS 0x4L\n FILETYPE 0x0L\n FILESUBTYPE 0x0L\nBEGIN\n    BLOCK \"StringFileInfo\"\n    BEGIN\n        BLOCK \"080904b0\"\n        BEGIN\n            VALUE \"CompanyName\", DISTRIBUTIONLONG\n            VALUE \"FileDescription\", FULLENGINENAME\n\t\t\tVALUE \"FileVersion\", V_STR V_SUFFIX\n            VALUE \"InternalName\", \"ftequake\"\n            VALUE \"LegalCopyright\", \"Copyright (C) 2011\"\n            VALUE \"ProductName\", FULLENGINENAME\n\t\t\tVALUE \"ProductVersion\", V_STR V_SUFFIX\n        END\n    END\n    BLOCK \"VarFileInfo\"\n    BEGIN\n        VALUE \"Translation\", 0x809, 1200\n    END\nEND\n\n#endif    // English (U.K.) resources\n/////////////////////////////////////////////////////////////////////////////\n\n\n//we need to use a manifest to avoid issues with UAC and hidden/buggy writes. There is no sane/other way to turn off virtualisation.\n//oh god, this is going to fuck over xp, isn't it.\n1 24 \"quake.manifest\"\n\n\n#ifndef APSTUDIO_INVOKED\n/////////////////////////////////////////////////////////////////////////////\n//\n// Generated from the TEXTINCLUDE 3 resource.\n//\n\n\n/////////////////////////////////////////////////////////////////////////////\n#endif    // not APSTUDIO_INVOKED\n\n"
  },
  {
    "path": "engine/client/zqtp.c",
    "content": "/*\n\tteamplay.c\n\n\tTeamplay enhancements\n\n\tCopyright (C) 2000-2001       Anton Gavrilov\n\n\tThis program is free software; you can redistribute it and/or\n\tmodify it under the terms of the GNU General Public License\n\tas published by the Free Software Foundation; either version 2\n\tof the License, or (at your option) any later version.\n\n\tThis program is distributed in the hope that it will be useful,\n\tbut WITHOUT ANY WARRANTY; without even the implied warranty of\n\tMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\n\tSee the GNU General Public License for more details.\n\n\tYou should have received a copy of the GNU General Public License\n\talong with this program; if not, write to:\n\n\t\tFree Software Foundation, Inc.\n\t\t59 Temple Place - Suite 330\n\t\tBoston, MA  02111-1307, USA\n*/\n\n//Hacked by spike.\n//things to fix:\n//TP_SearchForMsgTriggers: should only allow safe commands. work out what the meaning of safe commands is.\n\n\n#include \"quakedef.h\"\n\n//#include \"version.h\"\n#include \"sound.h\"\n//#include \"pmove.h\"\n#include <time.h>\n#include <ctype.h>\n\ntypedef qboolean qbool;\n\n#define SP 0\n\n#define Com_Printf Con_Printf\n\n#define strlcpy Q_strncpyz\n#define strlcat Q_strncatz\n#define Q_stricmp strcasecmp\n#define Q_strnicmp strncasecmp\n\n\nextern int\t\tcl_spikeindex, cl_playerindex, cl_h_playerindex, cl_flagindex, cl_rocketindex, cl_grenadeindex, cl_gib1index, cl_gib2index, cl_gib3index;\nextern cvar_t\tv_viewheight, dpcompat_console;\ntrace_t PM_TraceLine (vec3_t start, vec3_t end);\n#define ISDEAD(i) ( (i) >= 41 && (i) <= 102 )\n\nqboolean suppress;\n\n//note: csqc obsoletes and even breaks much of this stuff.\n//cl.simorg is no longer valid, cl.viewangles isn't terribly valid either.\n\n/*#define isalpha(x) (((x) >= 'a' && (x) <= 'z') || ((x) >= 'A' && (x) <= 'Z'))\n#define isdigit(x) ((x) >= '0' && (x) <= '9')\n#define isxdigit(x) (isdigit(x) || ((x) >= 'a' && (x) <= 'f'))\n*/\n#define Q_rint(f) ((int)((f)+0.5))\n\n// callbacks used for TP cvars\n\n#ifdef QWSKINS\nstatic void QDECL TP_SkinCvar_Callback(struct cvar_s *var, char *oldvalue);\nstatic void QDECL TP_TeamColor_CB (struct cvar_s *var, char *oldvalue);\nstatic void QDECL TP_EnemyColor_CB (struct cvar_s *var, char *oldvalue);\n//cvar_t enemyforceskins = CVARD(\"enemyforceskins\", \"0\", \"0: Read the skin as-is.\\n1: Use the player's name instead of their skin setting.\\n2: Use their userid (probably too unreliable).3: Use a per-team player index.\\n\");\n//cvar_t teamforceskins = CVARD(\"enemyforceskins\", \"0\", \"0: Read the skin as-is.\\n1: Use the player's name instead of their skin setting.\\n2: Use their userid (probably too unreliable).3: Use a per-team player index.\\n\");\n#define TP_SKIN_CVARS\t\\\n\tTP_CVARAC(cl_teamskin,\t\t\"\", teamskin, TP_SkinCvar_Callback);\t\\\n\tTP_CVARAC(cl_enemyskin,\t\t\"\", enemyskin, TP_SkinCvar_Callback);\t\\\n\tstatic TP_CVARC(enemycolor,\t\t\"off\", TP_EnemyColor_CB);\t\\\n\tstatic TP_CVARC(teamcolor,\t\t\t\"off\", TP_TeamColor_CB);\n#else\n#define TP_SKIN_CVARS\n#endif\n\n#ifdef QUAKESTATS\n#define TP_NAME_CVARS\t\\\n\tstatic TP_CVAR(tp_name_none,\t\t\t\"\");\t\\\n\tstatic TP_CVAR(tp_name_axe,\t\t\t\t\"axe\");\t\\\n\tTP_CVAR(tp_name_sg,\t\t\t\t\t\t\"sg\");\t\\\n\tTP_CVAR(tp_name_ssg,\t\t\t\t\t\"ssg\");\t\\\n\tTP_CVAR(tp_name_ng,\t\t\t\t\t\t\"ng\");\t\\\n\tTP_CVAR(tp_name_sng,\t\t\t\t\t\"sng\");\t\\\n\tTP_CVAR(tp_name_gl,\t\t\t\t\t\t\"gl\");\t\\\n\tTP_CVAR(tp_name_rl,\t\t\t\t\t\t\"rl\");\t\\\n\tTP_CVAR(tp_name_lg,\t\t\t\t\t\t\"lg\");\t\\\n\tstatic TP_CVAR(tp_name_ra,\t\t\t\t\"ra\");\t\\\n\tstatic TP_CVAR(tp_name_ya,\t\t\t\t\"ya\");\t\\\n\tstatic TP_CVAR(tp_name_ga,\t\t\t\t\"ga\");\t\\\n\tstatic TP_CVAR(tp_name_quad,\t\t\t\"quad\");\t\\\n\tstatic TP_CVAR(tp_name_pent,\t\t\t\"pent\");\t\\\n\tstatic TP_CVAR(tp_name_ring,\t\t\t\"ring\");\t\\\n\tstatic TP_CVAR(tp_name_suit,\t\t\t\"suit\");\t\\\n\tstatic TP_CVAR(tp_name_shells,\t\t\t\"shells\");\t\\\n\tstatic TP_CVAR(tp_name_nails,\t\t\t\"nails\");\t\\\n\tstatic TP_CVAR(tp_name_rockets,\t\t\t\"rockets\");\t\\\n\tstatic TP_CVAR(tp_name_cells,\t\t\t\"cells\");\t\\\n\tstatic TP_CVAR(tp_name_mh,\t\t\t\t\"mega\");\t\\\n\tstatic TP_CVAR(tp_name_health,\t\t\t\"health\");\t\\\n\tstatic TP_CVAR(tp_name_backpack,\t\t\"pack\");\t\\\n\tstatic TP_CVAR(tp_name_flag,\t\t\t\"flag\");\t\\\n\tstatic TP_CVAR(tp_name_nothing,\t\t\t\"nothing\");\t\\\n\tstatic TP_CVAR(tp_name_at,\t\t\t\t\"at\");\t\\\n\tstatic TP_CVAR(tp_need_ra,\t\t\t\t\"50\");\t\\\n\tstatic TP_CVAR(tp_need_ya,\t\t\t\t\"50\");\t\\\n\tstatic TP_CVAR(tp_need_ga,\t\t\t\t\"50\");\t\\\n\tstatic TP_CVAR(tp_need_health,\t\t\t\"50\");\t\\\n\tstatic TP_CVAR(tp_need_weapon,\t\t\t\"35687\");\t\\\n\tstatic TP_CVAR(tp_need_rl,\t\t\t\t\"1\");\t\\\n\tstatic TP_CVAR(tp_need_rockets,\t\t\t\"5\");\t\\\n\tstatic TP_CVAR(tp_need_cells,\t\t\t\"20\");\t\\\n\tstatic TP_CVAR(tp_need_nails,\t\t\t\"40\");\t\\\n\tstatic TP_CVAR(tp_need_shells,\t\t\t\"10\");\t\\\n\tstatic TP_CVAR(tp_name_disp,\t\t\t\"dispenser\");\t\\\n\tstatic TP_CVAR(tp_name_sentry,\t\t\t\"sentry gun\");\t\\\n\tstatic TP_CVAR(tp_name_rune_1,\t\t\t\"resistance rune\");\t\\\n\tstatic TP_CVAR(tp_name_rune_2,\t\t\t\"strength rune\");\t\\\n\tstatic TP_CVAR(tp_name_rune_3,\t\t\t\"haste rune\");\t\\\n\tstatic TP_CVAR(tp_name_rune_4,\t\t\t\"regeneration rune\");\t\\\n\t\\\n\tstatic TP_CVAR(tp_name_status_red,\t\t\"$R\");\t\\\n\tstatic TP_CVAR(tp_name_status_green,\t\"$G\");\t\\\n\tstatic TP_CVAR(tp_name_status_yellow,\t\"$Y\");\t\\\n\tstatic TP_CVAR(tp_name_status_blue,\t\t\"$B\");\t\\\n\t\\\n\tstatic TP_CVAR(tp_name_armortype_ga,\t\"g\");\t\\\n\tstatic TP_CVAR(tp_name_armortype_ya,\t\"y\");\t\\\n\tstatic TP_CVAR(tp_name_armortype_ra,\t\"r\");\t\\\n\tstatic TP_CVAR(tp_name_armor,\t\t\t\"armor\");\t\\\n\tstatic TP_CVAR(tp_name_weapon,\t\t\t\"weapon\");\t\\\n\tstatic TP_CVAR(tp_weapon_order,\t\t\t\"78654321\");\t\\\n\t\\\n  \tstatic TP_CVAR(tp_name_quaded,\t\t\t\"quaded\");\t\\\n\tstatic TP_CVAR(tp_name_pented,\t\t\t\"pented\");\t\\\n\tstatic TP_CVAR(tp_name_separator,\t\t\"/\");\t\\\n\t\\\n\tstatic TP_CVAR(tp_name_enemy,\t\t\t\"enemy\");\t\\\n\tstatic TP_CVAR(tp_name_teammate,\t\t\"\");\t\\\n\tstatic TP_CVAR(tp_name_eyes,\t\t\t\"eyes\");\t\\\n\t\\\n\tstatic TP_CVAR(loc_name_separator,\t\t\"-\");\t\\\n\tstatic TP_CVAR(loc_name_ssg,\t\t\t\"ssg\");\t\\\n\tstatic TP_CVAR(loc_name_ng,\t\t\t\t\"ng\");\t\\\n\tstatic TP_CVAR(loc_name_sng,\t\t\t\"sng\");\t\\\n\tstatic TP_CVAR(loc_name_gl,\t\t\t\t\"gl\");\t\\\n\tstatic TP_CVAR(loc_name_rl,\t\t\t\t\"rl\");\t\\\n\tstatic TP_CVAR(loc_name_lg,\t\t\t\t\"lg\");\t\\\n\tstatic TP_CVAR(loc_name_ga,\t\t\t\t\"ga\");\t\\\n\tstatic TP_CVAR(loc_name_ya,\t\t\t\t\"ya\");\t\\\n\tstatic TP_CVAR(loc_name_ra,\t\t\t\t\"ra\");\t\\\n\tstatic TP_CVAR(loc_name_mh,\t\t\t\t\"mh\");\t\\\n\tstatic TP_CVAR(loc_name_quad,\t\t\t\"quad\");\t\\\n\tstatic TP_CVAR(loc_name_pent,\t\t\t\"pent\");\t\\\n\tstatic TP_CVAR(loc_name_ring,\t\t\t\"ring\");\t\\\n\tstatic TP_CVAR(loc_name_suit,\t\t\t\"suit\");\n#else\n\t#define TP_NAME_CVARS\n#endif\n\n//a list of all the cvars\n//this is down to the fact that I keep defining them but forgetting to register. :/\n#define TP_CVARS \\\n\tTP_SKIN_CVARS \\\n\tTP_NAME_CVARS \\\n\tstatic TP_CVAR(cl_fakename,\t\t\"\");\t\\\n\tTP_CVAR(cl_parseSay,\t\t\"1\");\t\\\n\tTP_CVAR(cl_parseFunChars,\t\t\"1\");\t\\\n\tTP_CVAR(cl_triggers,\t\t\"1\");\t\\\n\tstatic TP_CVAR(tp_autostatus,\t\t\"\");\t/* things which will not always change, but are useful */ \\\n\tTP_CVAR(tp_forceTriggers,\t\t\"0\");\t\\\n\tTP_CVAR(tp_loadlocs,\t\t\"1\");\t\\\n\tstatic TP_CVAR(tp_soundtrigger,\t\t\"~\");\t\\\n\t\\\n\tstatic TP_CVAR(tp_name_someplace,\t\"someplace\")\n\n//create the globals for all the TP cvars.\n#define TP_CVAR(name,def) cvar_t\tname = CVAR(#name, def)\n#define TP_CVARC(name,def,call) cvar_t name = CVARC(#name, def, call)\n#define TP_CVARAC(name,def,name2,call) cvar_t name = CVARAFC(#name, def, #name2, 0, call)\nTP_CVARS;\n#undef TP_CVAR\n#undef TP_CVARC\n#undef TP_CVARAC\n\nextern cvar_t\thost_mapname;\n\n#define MAX_LOC_NAME 48\n\n#ifdef QUAKESTATS\nstatic void TP_FindModelNumbers (void);\nstatic void TP_FindPoint (void);\n\n// this structure is cleared after entering a new map\ntypedef struct tvars_s {\n\tchar\tautoteamstatus[256];\n\tfloat\tautoteamstatus_time;\n\n\tint\t\thealth;\n\tint\t\titems;\n\tint\t\tolditems;\n\tint\t\tstat_framecounts[MAX_CL_STATS];\n\tint\t\tactiveweapon;\n\tfloat\trespawntrigger_time;\n\tfloat\tdeathtrigger_time;\n\tfloat\tf_version_reply_time;\n\tchar\tlastdeathloc[MAX_LOC_NAME];\n\tchar\ttookname[32];\n\tchar\ttookloc[MAX_LOC_NAME];\n\tfloat\ttooktime;\n\n\tint\t\tpointframe;\t\t// cls.framecount for which point* vars are valid\n\tchar\tpointname[32];\n\tvec3_t\tpointorg;\n\tchar\tpointloc[MAX_LOC_NAME];\n\tint\t\tdroppedweapon;\n\tchar\tlastdroploc[MAX_LOC_NAME];\n\n\tint last_numenemies;\n\tint numenemies;\n\n\tint last_numfriendlies;\n\tint numfriendlies;\n\n\tint enemy_powerups;\n\tfloat enemy_powerups_time;\n\n\tfloat lastdrop_time;\n\n\tchar lasttrigger_match[256];\n\n\tenum {\n\t\tPOINT_TYPE_ENEMY,\n\t\tPOINT_TYPE_TEAMMATE,\n\t\tPOINT_TYPE_POWERUP,\n\t\tPOINT_TYPE_ITEM\n\t} pointtype;\n\tfloat\tpointtime;\n} tvars_t;\n\nstatic tvars_t vars;\n#endif\n\n\ntypedef struct item_vis_s {\n\tvec3_t\tvieworg;\n\tvec3_t\tforward;\n\tvec3_t\tright;\n\tvec3_t\tup;\n\tvec3_t\tentorg;\n\tfloat\tradius;\n\tvec3_t\tdir;\n\tfloat\tdist;\n} item_vis_t;\n\n\n#define\tTP_TOOK_EXPIRE_TIME\t\t15\n#define\tTP_POINT_EXPIRE_TIME\tTP_TOOK_EXPIRE_TIME\n\n\n\n\n\n//===========================================================================\n//\t\t\t\t\t\t\t\tTRIGGERS\n//===========================================================================\n\nvoid TP_ExecTrigger (char *s, qboolean indemos)\n{\n\tchar *astr;\n\n\tif (!cl_triggers.value)\n\t\treturn;\n\n\tif (!indemos && cls.demoplayback)\n\t\treturn;\n\n\tastr = Cmd_AliasExist(s, RESTRICT_LOCAL);\n\tif (astr)\n\t{\n\t\tchar *p;\n\t\tqbool quote = false;\n\n\t\tfor (p=astr ; *p ; p++)\n\t\t{\n\t\t\tif (*p == '\"')\n\t\t\t\tquote = !quote;\n\t\t\tif (!quote && *p == ';')\n\t\t\t{\n\t\t\t\t// more than one command, add it to the command buffer\n\t\t\t\tCbuf_AddText (astr, RESTRICT_LOCAL);\n\t\t\t\tCbuf_AddText (\"\\n\", RESTRICT_LOCAL);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\t// a single line, so execute it right away\n\t\tCmd_ExecuteString (astr, RESTRICT_LOCAL);\n\t\treturn;\n\t}\n}\n\n/*\n==========================================================================\n\t\t\t\t\t\t        HELPER FUNCTIONS\n==========================================================================\n*/\nstatic int\tTP_CountPlayers (void)\n{\n\tint\ti, count;\n\n\tcount = 0;\n\tfor (i = 0; i < cl.allocated_client_slots ; i++) {\n\t\tif (cl.players[i].name[0] && !cl.players[i].spectator)\n\t\t\tcount++;\n\t}\n\n\treturn count;\n}\n\nstatic char *TP_PlayerTeam (void)\n{\n\treturn cl.players[cl.playerview[SP].playernum].team;\n}\n\nstatic char *TP_EnemyTeam (void)\n{\n\tint\t\t\ti;\n\tstatic char\tenemyteam[MAX_INFO_KEY];\n\tchar *myteam = TP_PlayerTeam();\n\n\tfor (i = 0; i < cl.allocated_client_slots ; i++) {\n\t\tif (cl.players[i].name[0] && !cl.players[i].spectator)\n\t\t{\n\t\t\tstrcpy (enemyteam, cl.players[i].team);\n\t\t\tif (strcmp(myteam, cl.players[i].team) != 0)\n\t\t\t\treturn enemyteam;\n\t\t}\n\t}\n\treturn \"\";\n}\n\nstatic char *TP_PlayerName (void)\n{\n\treturn cl.players[cl.playerview[SP].playernum].name;\n}\n\n\nstatic char *TP_EnemyName (void)\n{\n\tint\t\t\ti;\n\tchar\t\t*myname;\n\tstatic char\tenemyname[MAX_SCOREBOARDNAME];\n\n\tmyname = TP_PlayerName ();\n\n\tfor (i = 0; i < cl.allocated_client_slots ; i++) {\n\t\tif (cl.players[i].name[0] && !cl.players[i].spectator)\n\t\t{\n\t\t\tstrcpy (enemyname, cl.players[i].name);\n\t\t\tif (!strcmp(enemyname, myname))\n\t\t\t\treturn enemyname;\n\t\t}\n\t}\n\treturn \"\";\n}\n\nstatic char *TP_MapName (void)\n{\n\treturn host_mapname.string;\n}\n\nchar *TP_GenerateDemoName(void)\n{\n\tif (cl.playerview[SP].spectator)\n\t{\t// FIXME: if tracking a player, use his name\n\t\treturn va (\"spec_%s_%s\",\n\t\t\t\t\t\tTP_PlayerName(),\n\t\t\t\t\t\tTP_MapName());\n\t}\n\telse\n\t{\t// guess game type and write demo name\n\t\tint i = TP_CountPlayers();\n\t\tif (cl.teamplay && i >= 3)\n\t\t{\t// Teamplay\n\t\t\treturn va (\"%s_%s_vs_%s_%s\",\n\t\t\t\t\t\t\tTP_PlayerName(),\n\t\t\t\t\t\t\tTP_PlayerTeam(),\n\t\t\t\t\t\t\tTP_EnemyTeam(),\n\t\t\t\t\t\t\tTP_MapName());\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (i == 2)\n\t\t\t{\t// Duel\n\t\t\t\treturn va (\"%s_vs_%s_%s\",\n\t\t\t\t\t\t\t\tTP_PlayerName(),\n\t\t\t\t\t\t\t\tTP_EnemyName(),\n\t\t\t\t\t\t\t\tTP_MapName());\n\t\t\t}\n\t\t\telse if (i > 2)\n\t\t\t{\t// FFA\n\t\t\t\treturn va (\"%s_ffa_%s\",\n\t\t\t\t\tTP_PlayerName(),\n\t\t\t\t\tTP_MapName());\n\t\t\t}\n\t\t\telse\n\t\t\t{\t// one player\n\t\t\t\treturn va (\"%s_%s\",\n\t\t\t\t\tTP_PlayerName(),\n\t\t\t\t\tTP_MapName());\n\t\t\t}\n\t\t}\n\t}\n}\n\n\n/*\n==========================================================================\n\t\t\t\t\t\t        MACRO FUNCTIONS\n==========================================================================\n*/\n\n#define MAX_MACRO_VALUE\t256\nstatic char\tmacro_buf[MAX_MACRO_VALUE] = \"\";\n\n#ifdef QUAKESTATS\n// buffer-size-safe helper functions\n//static void MacroBuf_strcat (char *str) {\n//\tstrlcat (macro_buf, str, sizeof(macro_buf));\n//}\nstatic void MacroBuf_strcat_with_separator (char *str) {\n\tif (macro_buf[0])\n\t\tstrlcat (macro_buf, tp_name_separator.string, sizeof(macro_buf));\n\tstrlcat (macro_buf, str, sizeof(macro_buf));\n}\n#endif\n\n\nstatic char *Macro_Latency (void)\n{\n\tsprintf(macro_buf, \"%i\", Q_rint(cls.latency*1000));\n\treturn macro_buf;\n}\n\nstatic char *Macro_Gamedir (void)\n{\n\textern char gamedirfile[];\n\tQ_snprintfz(macro_buf, sizeof(macro_buf), \"%s\", gamedirfile);\n\treturn macro_buf;\n}\n\n#ifdef QUAKESTATS\nstatic char *Macro_Health (void)\n{\n\tsprintf(macro_buf, \"%i\", cl.playerview[SP].stats[STAT_HEALTH]);\n\treturn macro_buf;\n}\n\nstatic char *Macro_Armor (void)\n{\n\tsprintf(macro_buf, \"%i\", cl.playerview[SP].stats[STAT_ARMOR]);\n\treturn macro_buf;\n}\n\nstatic char *Macro_Shells (void)\n{\n\tsprintf(macro_buf, \"%i\", cl.playerview[SP].stats[STAT_SHELLS]);\n\treturn macro_buf;\n}\n\nstatic char *Macro_Nails (void)\n{\n\tsprintf(macro_buf, \"%i\", cl.playerview[SP].stats[STAT_NAILS]);\n\treturn macro_buf;\n}\n\nstatic char *Macro_Rockets (void)\n{\n\tsprintf(macro_buf, \"%i\", cl.playerview[SP].stats[STAT_ROCKETS]);\n\treturn macro_buf;\n}\n\nstatic char *Macro_Cells (void)\n{\n\tsprintf(macro_buf, \"%i\", cl.playerview[SP].stats[STAT_CELLS]);\n\treturn macro_buf;\n}\n\nstatic char *Macro_Ammo (void)\n{\n\tsprintf(macro_buf, \"%i\", cl.playerview[SP].stats[STAT_AMMO]);\n\treturn macro_buf;\n}\n\nstatic char *Weapon_NumToString (int wnum)\n{\n\tswitch (wnum)\n\t{\n\tcase IT_AXE:\t\t\t\treturn tp_name_axe.string;\n\tcase IT_SHOTGUN:\t\t\treturn tp_name_sg.string;\n\tcase IT_SUPER_SHOTGUN:\t\treturn tp_name_ssg.string;\n\tcase IT_NAILGUN:\t\t\treturn tp_name_ng.string;\n\tcase IT_SUPER_NAILGUN:\t\treturn tp_name_sng.string;\n\tcase IT_GRENADE_LAUNCHER:\treturn tp_name_gl.string;\n\tcase IT_ROCKET_LAUNCHER:\treturn tp_name_rl.string;\n\tcase IT_LIGHTNING:\t\t\treturn tp_name_lg.string;\n\tdefault:\t\t\t\t\treturn tp_name_none.string;\n\t}\n}\n\nstatic char *Macro_Weapon (void)\n{\n\treturn Weapon_NumToString(cl.playerview[SP].stats[STAT_ACTIVEWEAPON]);\n}\n\nstatic char *Macro_DroppedWeapon (void)\n{\n\treturn Weapon_NumToString(vars.droppedweapon);\n}\n\nstatic char *Macro_Weapons (void) {\n\tmacro_buf[0] = 0;\n\n\tif (cl.playerview[SP].stats[STAT_ITEMS] & IT_LIGHTNING)\n\t\tstrcpy(macro_buf, tp_name_lg.string);\n\tif (cl.playerview[SP].stats[STAT_ITEMS] & IT_ROCKET_LAUNCHER)\n\t\tMacroBuf_strcat_with_separator (tp_name_rl.string);\n\tif (cl.playerview[SP].stats[STAT_ITEMS] & IT_GRENADE_LAUNCHER)\n\t\tMacroBuf_strcat_with_separator (tp_name_gl.string);\n\tif (cl.playerview[SP].stats[STAT_ITEMS] & IT_SUPER_NAILGUN)\n\t\tMacroBuf_strcat_with_separator (tp_name_sng.string);\n\tif (cl.playerview[SP].stats[STAT_ITEMS] & IT_NAILGUN)\n\t\tMacroBuf_strcat_with_separator (tp_name_ng.string);\n\tif (cl.playerview[SP].stats[STAT_ITEMS] & IT_SUPER_SHOTGUN)\n\t\tMacroBuf_strcat_with_separator (tp_name_ssg.string);\n\tif (cl.playerview[SP].stats[STAT_ITEMS] & IT_SHOTGUN)\n\t\tMacroBuf_strcat_with_separator (tp_name_sg.string);\n\tif (cl.playerview[SP].stats[STAT_ITEMS] & IT_AXE)\n\t\tMacroBuf_strcat_with_separator (tp_name_axe.string);\n//\tif (!macro_buf[0])\n//\t\tstrlcpy(macro_buf, tp_name_none.string, sizeof(macro_buf));\n\n\treturn macro_buf;\n}\n\nstatic char *Macro_WeaponAndAmmo (void)\n{\n\tchar buf[sizeof(macro_buf)];\n\tQ_snprintfz (buf, sizeof(buf), \"%s:%s\", Macro_Weapon(), Macro_Ammo());\n\tstrcpy (macro_buf, buf);\n\treturn macro_buf;\n}\n\nstatic char *Macro_WeaponNum (void)\n{\n\tswitch (cl.playerview[SP].stats[STAT_ACTIVEWEAPON])\n\t{\n\tcase IT_AXE: return \"1\";\n\tcase IT_SHOTGUN: return \"2\";\n\tcase IT_SUPER_SHOTGUN: return \"3\";\n\tcase IT_NAILGUN: return \"4\";\n\tcase IT_SUPER_NAILGUN: return \"5\";\n\tcase IT_GRENADE_LAUNCHER: return \"6\";\n\tcase IT_ROCKET_LAUNCHER: return \"7\";\n\tcase IT_LIGHTNING: return \"8\";\n\tdefault:\n\t\treturn \"0\";\n\t}\n}\n\nstatic int\t_Macro_BestWeapon (void)\n{\n\tint i;\n\tchar *t[] = {tp_weapon_order.string, \"78654321\", NULL}, **s;\n\n\tfor (s = t; *s; s++)\n\t{\n\t\tfor (i = 0 ; i < strlen(*s) ; i++)\n\t\t{\n\t\t\tswitch ((*s)[i]) {\n\t\t\t\tcase '1': if (cl.playerview[SP].stats[STAT_ITEMS] & IT_AXE) return IT_AXE; break;\n\t\t\t\tcase '2': if (cl.playerview[SP].stats[STAT_ITEMS] & IT_SHOTGUN) return IT_SHOTGUN; break;\n\t\t\t\tcase '3': if (cl.playerview[SP].stats[STAT_ITEMS] & IT_SUPER_SHOTGUN) return IT_SUPER_SHOTGUN; break;\n\t\t\t\tcase '4': if (cl.playerview[SP].stats[STAT_ITEMS] & IT_NAILGUN) return IT_NAILGUN; break;\n\t\t\t\tcase '5': if (cl.playerview[SP].stats[STAT_ITEMS] & IT_SUPER_NAILGUN) return IT_SUPER_NAILGUN; break;\n\t\t\t\tcase '6': if (cl.playerview[SP].stats[STAT_ITEMS] & IT_GRENADE_LAUNCHER) return IT_GRENADE_LAUNCHER; break;\n\t\t\t\tcase '7': if (cl.playerview[SP].stats[STAT_ITEMS] & IT_ROCKET_LAUNCHER) return IT_ROCKET_LAUNCHER; break;\n\t\t\t\tcase '8': if (cl.playerview[SP].stats[STAT_ITEMS] & IT_LIGHTNING) return IT_LIGHTNING; break;\n\t\t\t}\n\t\t}\n\t}\n\treturn 0;\n}\n\nstatic char *Macro_BestWeapon (void)\n{\n\treturn Weapon_NumToString(_Macro_BestWeapon());\n}\n\nstatic char *Macro_BestAmmo (void)\n{\n\tswitch (_Macro_BestWeapon())\n\t{\n\tcase IT_SHOTGUN: case IT_SUPER_SHOTGUN:\n\t\tsprintf(macro_buf, \"%i\", cl.playerview[SP].stats[STAT_SHELLS]);\n\t\treturn macro_buf;\n\n\tcase IT_NAILGUN: case IT_SUPER_NAILGUN:\n\t\tsprintf(macro_buf, \"%i\", cl.playerview[SP].stats[STAT_NAILS]);\n\t\treturn macro_buf;\n\n\tcase IT_GRENADE_LAUNCHER: case IT_ROCKET_LAUNCHER:\n\t\tsprintf(macro_buf, \"%i\", cl.playerview[SP].stats[STAT_ROCKETS]);\n\t\treturn macro_buf;\n\n\tcase IT_LIGHTNING:\n\t\tsprintf(macro_buf, \"%i\", cl.playerview[SP].stats[STAT_CELLS]);\n\t\treturn macro_buf;\n\n\tdefault:\n\t\treturn \"0\";\n\t}\n}\n\n// needed for %b parsing\nstatic char *Macro_BestWeaponAndAmmo (void)\n{\n\tchar buf[MAX_MACRO_VALUE];\n\tQ_snprintfz (buf, sizeof(buf), \"%s:%s\", Macro_BestWeapon(), Macro_BestAmmo());\n\tstrcpy (macro_buf, buf);\n\treturn macro_buf;\n}\n\nstatic char *Macro_ArmorType (void)\n{\n\tif (cl.playerview[SP].stats[STAT_ITEMS] & IT_ARMOR1)\n\t\treturn tp_name_armortype_ga.string;\n\telse if (cl.playerview[SP].stats[STAT_ITEMS] & IT_ARMOR2)\n\t\treturn tp_name_armortype_ya.string;\n\telse if (cl.playerview[SP].stats[STAT_ITEMS] & IT_ARMOR3)\n\t\treturn tp_name_armortype_ra.string;\n\telse\n\t\treturn tp_name_none.string;\t// no armor at all\n}\n\nstatic char *Macro_Powerups (void)\n{\n\tint effects;\n\n\tmacro_buf[0] = 0;\n\n\tif (cl.playerview[SP].stats[STAT_ITEMS] & IT_QUAD)\n\t\tMacroBuf_strcat_with_separator (tp_name_quad.string);\n\n\tif (cl.playerview[SP].stats[STAT_ITEMS] & IT_INVULNERABILITY)\n\t\tMacroBuf_strcat_with_separator (tp_name_pent.string);\n\n\tif (cl.playerview[SP].stats[STAT_ITEMS] & IT_INVISIBILITY)\n\t\tMacroBuf_strcat_with_separator (tp_name_ring.string);\n\n\teffects = cl.inframes[cl.parsecount&UPDATE_MASK].playerstate[cl.playerview[SP].playernum].effects;\n\tif ( (effects & (QWEF_FLAG1|QWEF_FLAG2)) ||\t\t// CTF\n\t\t(cl.teamfortress && cl.playerview[SP].stats[STAT_ITEMS] & (IT_KEY1|IT_KEY2)) ) // TF\n\t\tMacroBuf_strcat_with_separator (tp_name_flag.string);\n\n\treturn macro_buf;\n}\n#endif\n\nstatic char *Macro_Location (void)\n{\n\treturn TP_LocationName (cl.playerview[SP].simorg);\n}\n\n#ifdef QUAKESTATS\nstatic char *Macro_LastDeath (void)\n{\n\tif (vars.deathtrigger_time)\n\t\treturn vars.lastdeathloc;\n\telse\n\t\treturn tp_name_someplace.string;\n}\n\nstatic char *Macro_Last_Location (void)\n{\n\tif (vars.deathtrigger_time && realtime - vars.deathtrigger_time <= 5)\n\t\treturn vars.lastdeathloc;\n\treturn Macro_Location();\n}\n\n// returns the last item picked up\nstatic char *Macro_Took (void)\n{\n\tif (!vars.tooktime || realtime > vars.tooktime + 20)\n\t\tstrlcpy (macro_buf, tp_name_nothing.string, sizeof(macro_buf));\n\telse\n\t\tstrcpy (macro_buf, vars.tookname);\n\treturn macro_buf;\n}\n\n// returns location of the last item picked up\nstatic char *Macro_TookLoc (void)\n{\n\tif (!vars.tooktime || realtime > vars.tooktime + 20)\n\t\tstrlcpy (macro_buf, tp_name_someplace.string, sizeof(macro_buf));\n\telse\n\t\tstrcpy (macro_buf, vars.tookloc);\n\treturn macro_buf;\n}\n\n\n// %i macro - last item picked up in \"name at location\" style\nstatic char *Macro_TookAtLoc (void)\n{\n\tif (!vars.tooktime || realtime > vars.tooktime + 20)\n\t\tstrncpy (macro_buf, tp_name_nothing.string, sizeof(macro_buf)-1);\n\telse\n\t{\n\t\tstrlcpy (macro_buf, va(\"%s %s %s\", vars.tookname,\n\t\t\ttp_name_at.string, vars.tookloc), sizeof(macro_buf));\n\t}\n\treturn macro_buf;\n}\n\n// pointing calculations are CPU expensive, so the results are cached\n// in vars.pointname & vars.pointloc\nstatic char *Macro_PointName (void)\n{\n\tif (cls.framecount != vars.pointframe)\n\t\tTP_FindPoint ();\n\treturn vars.pointname;\n}\n\nstatic char *Macro_PointLocation (void)\n{\n\tif (cls.framecount != vars.pointframe)\n\t\tTP_FindPoint ();\n\tif (vars.pointloc[0])\n\t\treturn vars.pointloc;\n\telse {\n\t\tstrlcpy (macro_buf, tp_name_someplace.string, sizeof(macro_buf));\n\t\treturn macro_buf;\n\t}\n}\n\nstatic char *Macro_PointNameAtLocation (void)\n{\n\tif (cls.framecount != vars.pointframe)\n\t\tTP_FindPoint ();\n\tif (vars.pointloc[0])\n\t\treturn va (\"%s %s %s\", vars.pointname, tp_name_at.string, vars.pointloc);\n\telse\n\t\treturn vars.pointname;\n}\n\n\nstatic char *Macro_Need (void)\n{\n\tint i, weapon;\n\tchar\t*needammo = NULL;\n\n\tmacro_buf[0] = 0;\n\n\t// check armor\n\tif (   ((cl.playerview[SP].stats[STAT_ITEMS] & IT_ARMOR1) && cl.playerview[SP].stats[STAT_ARMOR] < tp_need_ga.value)\n\t\t|| ((cl.playerview[SP].stats[STAT_ITEMS] & IT_ARMOR2) && cl.playerview[SP].stats[STAT_ARMOR] < tp_need_ya.value)\n\t\t|| ((cl.playerview[SP].stats[STAT_ITEMS] & IT_ARMOR3) && cl.playerview[SP].stats[STAT_ARMOR] < tp_need_ra.value)\n\t\t|| (!(cl.playerview[SP].stats[STAT_ITEMS] & (IT_ARMOR1|IT_ARMOR2|IT_ARMOR3))\n\t\t\t&& (tp_need_ga.value || tp_need_ya.value || tp_need_ra.value)))\n\t\tstrcpy (macro_buf, tp_name_armor.string);\n\n\t// check health\n\tif (tp_need_health.value && cl.playerview[SP].stats[STAT_HEALTH] < tp_need_health.value) {\n\t\tMacroBuf_strcat_with_separator (tp_name_health.string);\n\t}\n\n\tif (cl.teamfortress)\n\t{\n\t\t// in TF, we have all weapons from the start,\n\t\t// and ammo is checked differently\n\t\tif (cl.playerview[SP].stats[STAT_ROCKETS] < tp_need_rockets.value)\n\t\t\tMacroBuf_strcat_with_separator (tp_name_rockets.string);\n\t\tif (cl.playerview[SP].stats[STAT_SHELLS] < tp_need_shells.value)\n\t\t\tMacroBuf_strcat_with_separator (tp_name_shells.string);\n\t\tif (cl.playerview[SP].stats[STAT_NAILS] < tp_need_nails.value)\n\t\t\tMacroBuf_strcat_with_separator (tp_name_nails.string);\n\t\tif (cl.playerview[SP].stats[STAT_CELLS] < tp_need_cells.value)\n\t\t\tMacroBuf_strcat_with_separator (tp_name_cells.string);\n\t\tgoto done;\n\t}\n\n\t// check weapon\n\tweapon = 0;\n\tfor (i=strlen(tp_need_weapon.string)-1 ; i>=0 ; i--) {\n\t\tswitch (tp_need_weapon.string[i]) {\n\t\t\tcase '2': if (cl.playerview[SP].stats[STAT_ITEMS] & IT_SHOTGUN) weapon = 2; break;\n\t\t\tcase '3': if (cl.playerview[SP].stats[STAT_ITEMS] & IT_SUPER_SHOTGUN) weapon = 3; break;\n\t\t\tcase '4': if (cl.playerview[SP].stats[STAT_ITEMS] & IT_NAILGUN) weapon = 4; break;\n\t\t\tcase '5': if (cl.playerview[SP].stats[STAT_ITEMS] & IT_SUPER_NAILGUN) weapon = 5; break;\n\t\t\tcase '6': if (cl.playerview[SP].stats[STAT_ITEMS] & IT_GRENADE_LAUNCHER) weapon = 6; break;\n\t\t\tcase '7': if (cl.playerview[SP].stats[STAT_ITEMS] & IT_ROCKET_LAUNCHER) weapon = 7; break;\n\t\t\tcase '8': if (cl.playerview[SP].stats[STAT_ITEMS] & IT_LIGHTNING) weapon = 8; break;\n\t\t}\n\t\tif (weapon)\n\t\t\tbreak;\n\t}\n\n\tif (!weapon) {\n\t\tMacroBuf_strcat_with_separator (tp_name_weapon.string);\n\t} else {\n\t\tif (tp_need_rl.value && !(cl.playerview[SP].stats[STAT_ITEMS] & IT_ROCKET_LAUNCHER)) {\n\t\t\tMacroBuf_strcat_with_separator (tp_name_rl.string);\n\t\t}\n\n\t\tswitch (weapon) {\n\t\t\tcase 2: case 3: if (cl.playerview[SP].stats[STAT_SHELLS] < tp_need_shells.value) needammo = tp_name_shells.string; break;\n\t\t\tcase 4: case 5: if (cl.playerview[SP].stats[STAT_NAILS] < tp_need_nails.value) needammo = tp_name_nails.string; break;\n\t\t\tcase 6: case 7: if (cl.playerview[SP].stats[STAT_ROCKETS] < tp_need_rockets.value) needammo = tp_name_rockets.string; break;\n\t\t\tcase 8: if (cl.playerview[SP].stats[STAT_CELLS] < tp_need_cells.value) needammo = tp_name_cells.string; break;\n\t\t}\n\t\tif (needammo) {\n\t\t\tMacroBuf_strcat_with_separator (needammo);\n\t\t}\n\t}\n\ndone:\n\tif (!macro_buf[0])\n\t\tstrcpy (macro_buf, \"nothing\");\n\n\treturn macro_buf;\n}\n\nstatic char *Skin_To_TFSkin (char *myskin)\n{\n\tplayerview_t *pv = &cl.playerview[SP];\n\tif (!cl.teamfortress || pv->spectator || Q_strncasecmp(myskin, \"tf_\", 3))\n\t{\n\t\tQ_strncpyz(macro_buf, myskin, sizeof(macro_buf));\n\t}\n\telse\n\t{\n\t\tif (!Q_strcasecmp(myskin, \"tf_demo\"))\n\t\t\tQ_strncpyz(macro_buf, \"demoman\", sizeof(macro_buf));\n\t\telse if (!Q_strcasecmp(myskin, \"tf_eng\"))\n\t\t\tQ_strncpyz(macro_buf, \"engineer\", sizeof(macro_buf));\n\t\telse if (!Q_strcasecmp(myskin, \"tf_snipe\"))\n\t\t\tQ_strncpyz(macro_buf, \"sniper\", sizeof(macro_buf));\n\t\telse if (!Q_strcasecmp(myskin, \"tf_sold\"))\n\t\t\tQ_strncpyz(macro_buf, \"soldier\", sizeof(macro_buf));\n\t\telse\n\t\t\tQ_strncpyz(macro_buf, myskin + 3, sizeof(macro_buf));\n\t}\n\treturn macro_buf;\n}\n\nstatic char *Macro_TF_Skin (void)\n{\n\treturn Skin_To_TFSkin(InfoBuf_ValueForKey(&cl.players[cl.playerview[SP].playernum].userinfo, \"skin\"));\n}\n\n//Spike: added these:\nstatic char *Macro_Team (void)\n{\n\tinfobuf_t *info;\n\tint seat = SP;\n\t//read the userinfo's team from the server instead of our local/private cvar/userinfo, if we can.\n\tif (cl.players[cl.playerview[seat].playernum].userinfovalid)\n\t\tinfo = &cl.players[cl.playerview[seat].playernum].userinfo;\n\telse //just use the userinfo (which should mirror the cvar - fixme: splitscreen...)\n\t\tinfo = &cls.userinfo[seat];\n\treturn InfoBuf_ValueForKey(info, \"team\");\n}\nstatic char *Macro_ConnectionType (void)\n{\n\tplayerview_t *pv = &cl.playerview[SP];\n\tif (!cls.state)\n\t\treturn \"disconnected\";\n\tif (pv->spectator)\n\t\treturn \"spectator\";\n\treturn \"connected\";\n}\n\nstatic char *Macro_demoplayback (void)\n{\n\tswitch (cls.demoplayback)\n\t{\n\tcase DPB_NONE:\n\t\treturn \"0\";\n\tcase DPB_QUAKEWORLD:\n\t\treturn \"qwdplayback\";\n\tcase DPB_MVD:\n\t\treturn \"mvdplayback\";\n#ifdef NQPROT\n\tcase DPB_NETQUAKE:\n\t\treturn \"demplayback\";\n#endif\n#ifdef Q2CLIENT\n\tcase DPB_QUAKE2:\n\t\treturn \"dm2playback\";\n#endif\n\t}\n\treturn \"1\";\t//unknown.\n}\n\nstatic char *Macro_Match_Name (void)\n{\n\tint i;\n\ti = TP_CountPlayers();\n\tif (cl.teamplay && i >= 3)\n\t{\t// Teamplay\n\t\treturn va (\"%s %s vs %s - %s\",\n\t\t\tTP_PlayerName(),\n\t\t\tTP_PlayerTeam(),\n\t\t\tTP_EnemyTeam(),\n\t\t\tTP_MapName());\n\t}\n\telse\n\t{\n\t\tif (i == 2)\n\t\t{\t// Duel\n\t\t\treturn va (\"%s vs %s - %s\",\n\t\t\t\tTP_PlayerName(),\n\t\t\t\tTP_EnemyName(),\n\t\t\t\tTP_MapName());\n\t\t}\n\t\telse if (i > 2)\n\t\t{\t// FFA\n\t\t\treturn va (\"%s ffa - %s\",\n\t\t\t\tTP_PlayerName(),\n\t\t\t\tTP_MapName());\n\t\t}\n\t\telse\n\t\t{\t// one player\n\t\t\treturn va (\"%s - %s\",\n\t\t\t\tTP_PlayerName(),\n\t\t\t\tTP_MapName());\n\t\t}\n\t}\n}\n\n//$matchtype\n//duel,2on2,4on4,ffa,etc...\nstatic char *Macro_Match_Type (void)\n{\n\tint i;\n\ti = TP_CountPlayers();\n\tif (cl.teamplay && i >= 3)\n\t{\n\t\tif (i >= 6)\n\t\t\treturn \"4on4\";\n\t\treturn \"2on2\";\n\t}\n\tif (i == 2)\n\t\treturn \"duel\";\n\tif (i == 1)\n\t\treturn \"single\";\n\tif (i == 0)\n\t\treturn \"empty\";\n\treturn \"ffa\";\n}\n\nstatic char *Macro_Point_LED(void)\n{\n\tTP_FindPoint();\n\n\tif (vars.pointtype == POINT_TYPE_ENEMY)\n\t\treturn tp_name_status_red.string;\n\telse if (vars.pointtype == POINT_TYPE_TEAMMATE)\n\t\treturn tp_name_status_green.string;\n\telse if (vars.pointtype == POINT_TYPE_POWERUP)\n\t\treturn tp_name_status_yellow.string;\n\telse\t// POINT_TYPE_ITEM\n\t\treturn tp_name_status_blue.string;\n\n\treturn macro_buf;\n}\n\nstatic char *Macro_MyStatus_LED(void)\n{\n\tint count;\n\tfloat save_need_rl;\n\tchar *s, *save_separator;\n\tstatic char separator[] = {'/', '\\0'};\n\n\tsave_need_rl = tp_need_rl.value;\n\tsave_separator = tp_name_separator.string;\n\ttp_need_rl.value = 0;\n\ttp_name_separator.string = separator;\n\ts = Macro_Need();\n\ttp_need_rl.value = save_need_rl;\n\ttp_name_separator.string = save_separator;\n\n\tif (!strcmp(s, tp_name_nothing.string)) {\n\t\tcount = 0;\n\t} else  {\n\t\tfor (count = 1; *s; s++)\n\t\t\tif (*s == separator[0])\n\t\t\t\tcount++;\n\t}\n\n\tif (count == 0)\n\t\tQ_snprintfz(macro_buf, sizeof(macro_buf), \"%s\", tp_name_status_green.string);\n\telse if (count <= 1)\n\t\tQ_snprintfz(macro_buf, sizeof(macro_buf), \"%s\", tp_name_status_yellow.string);\n\telse\n\t\tQ_snprintfz(macro_buf, sizeof(macro_buf), \"%s\", tp_name_status_red.string);\n\n\treturn macro_buf;\n}\n\nstatic void CountNearbyPlayers(qboolean dead)\n{\n\tint i;\n\tplayer_state_t *state;\n\tplayer_info_t *info;\n\tstatic int lastframecount = -1;\n\tplayerview_t *pv = &cl.playerview[SP];\n\n\tif (cls.framecount == lastframecount)\n\t\treturn;\n\tlastframecount = cls.framecount;\n\n\tvars.numenemies = vars.numfriendlies = 0;\n\n\tif (!pv->spectator && !dead)\n\t\tvars.numfriendlies++;\n\n\tif (!cl.oldparsecount || !cl.parsecount || cls.state < ca_active)\n\t\treturn;\n\n\tstate = cl.inframes[cl.oldparsecount & UPDATE_MASK].playerstate;\n\tinfo = cl.players;\n\tfor (i = 0; i < cl.allocated_client_slots; i++, info++, state++) {\n\t\tif (i != pv->playernum && state->messagenum == cl.oldparsecount && !info->spectator && !ISDEAD(state->frame)) {\n\t\t\tif (cl.teamplay && !strcmp(info->team, TP_PlayerTeam()))\n\t\t\t\tvars.numfriendlies++;\n\t\t\telse\n\t\t\t\tvars.numenemies++;\n\t\t}\n\t}\n}\n\nstatic char *Macro_CountNearbyEnemyPlayers (void)\n{\n\tif (!ruleset_allow_playercount.ival)\n\t\treturn \" \";\n\tCountNearbyPlayers(false);\n\tsprintf(macro_buf, \"\\xffz%d\\xff\", vars.numenemies);\n\tsuppress = true;\n\treturn macro_buf;\n}\n\n\nstatic char *Macro_Count_Last_NearbyEnemyPlayers (void)\n{\n\tif (!ruleset_allow_playercount.ival)\n\t\treturn \" \";\n\tif (vars.deathtrigger_time && realtime - vars.deathtrigger_time <= 5)\n\t{\n\t\tsprintf(macro_buf, \"\\xffz%d\\xff\", vars.last_numenemies);\n\t}\n\telse\n\t{\n\t\tCountNearbyPlayers(false);\n\t\tsprintf(macro_buf, \"\\xffz%d\\xff\", vars.numenemies);\n\t}\n\tsuppress = true;\n\treturn macro_buf;\n}\n\n\nstatic char *Macro_CountNearbyFriendlyPlayers (void)\n{\n\tif (!ruleset_allow_playercount.ival)\n\t\treturn \" \";\n\tCountNearbyPlayers(false);\n\tsprintf(macro_buf, \"\\xffz%d\\xff\", vars.numfriendlies);\n\tsuppress = true;\n\treturn macro_buf;\n}\n\n\nstatic char *Macro_Count_Last_NearbyFriendlyPlayers (void)\n{\n\tif (!ruleset_allow_playercount.ival)\n\t\treturn \" \";\n\tif (vars.deathtrigger_time && realtime - vars.deathtrigger_time <= 5)\n\t{\n\t\tsprintf(macro_buf, \"\\xffz%d\\xff\", vars.last_numfriendlies);\n\t}\n\telse\n\t{\n\t\tCountNearbyPlayers(false);\n\t\tsprintf(macro_buf, \"\\xffz%d\\xff\", vars.numfriendlies);\n\t}\n\tsuppress = true;\n\treturn macro_buf;\n}\n\nstatic char *Macro_EnemyStatus_LED(void)\n{\n\tCountNearbyPlayers(false);\n\tif (vars.numenemies == 0)\n\t\tQ_snprintfz(macro_buf, sizeof(macro_buf), \"\\xffl%s\\xff\", tp_name_status_green.string);\n\telse if (vars.numenemies <= vars.numfriendlies)\n\t\tQ_snprintfz(macro_buf, sizeof(macro_buf), \"\\xffl%s\\xff\", tp_name_status_yellow.string);\n\telse\n\t\tQ_snprintfz(macro_buf, sizeof(macro_buf), \"\\xffl%s\\xff\", tp_name_status_red.string);\n\n\tsuppress = true;\n\treturn macro_buf;\n}\n\nstatic char *Macro_LastPointAtLoc (void)\n{\n\tif (!vars.pointtime || realtime - vars.pointtime > TP_POINT_EXPIRE_TIME)\n\t\tQ_strncpyz (macro_buf, tp_name_nothing.string, sizeof(macro_buf));\n\telse\n\t\tQ_snprintfz (macro_buf, sizeof(macro_buf), \"%s %s %s\", vars.pointname, tp_name_at.string, vars.pointloc[0] ? vars.pointloc : Macro_Location());\n\treturn macro_buf;\n}\n\nstatic char *Macro_LastTookOrPointed (void)\n{\n\tif (vars.tooktime && vars.tooktime > vars.pointtime && realtime - vars.tooktime < 5)\n\t\treturn Macro_TookAtLoc();\n\telse if (vars.pointtime && vars.tooktime <= vars.pointtime && realtime - vars.pointtime < 5)\n\t\treturn Macro_LastPointAtLoc();\n\n\tQ_snprintfz(macro_buf, sizeof(macro_buf), \"%s %s %s\", tp_name_nothing.string, tp_name_at.string, tp_name_someplace.string);\n    return macro_buf;\n}\n\n#define TP_PENT 1\n#define TP_QUAD 2\n#define TP_RING 4\n\nstatic char *Macro_LastSeenPowerup(void)\n{\n/*\tif (!vars.enemy_powerups_time || realtime - vars.enemy_powerups_time > 5)\n\t{\n\t\tQ_strncpyz(macro_buf, tp_name_quad.string, sizeof(macro_buf));\n\t}\n\telse*/\n\t{\n\t\tmacro_buf[0] = 0;\n\t\tif (vars.enemy_powerups & TP_QUAD)\n\t\t\tQ_strncatz2(macro_buf, tp_name_quad.string);\n\t\tif (vars.enemy_powerups & TP_PENT)\n\t\t{\n\t\t\tif (macro_buf[0])\n\t\t\t\tQ_strncatz2(macro_buf, tp_name_separator.string);\n\t\t\tQ_strncatz2(macro_buf, tp_name_pent.string);\n\t\t}\n\t\tif (vars.enemy_powerups & TP_RING)\n\t\t{\n\t\t\tif (macro_buf[0])\n\t\t\t\tQ_strncatz2(macro_buf, tp_name_separator.string);\n\t\t\tQ_strncatz2(macro_buf, tp_name_ring.string);\n\t\t}\n\t}\n\treturn macro_buf;\n}\n\nstatic char *Macro_LastDrop (void)\n{\n\tif (vars.lastdrop_time)\n\t\treturn vars.lastdroploc;\n\telse\n\t\treturn tp_name_someplace.string;\n}\n\nstatic char *Macro_LastDropTime (void)\n{\n\tif (vars.lastdrop_time)\n\t\tQ_snprintfz (macro_buf, 32, \"%d\", (int) (realtime - vars.lastdrop_time));\n\telse\n\t\tQ_snprintfz (macro_buf, 32, \"%d\", -1);\n\treturn macro_buf;\n}\n\nstatic char *Macro_CombinedHealth(void)\n{\n\tfloat h;\n\tfloat t, a, m;\n\t//total health = health+armour*armourfrac\n\t//however,you're dead if health drops below 0 rather than the entire equation.\n\n\tif (cl.playerview[SP].stats[STAT_ITEMS] & IT_ARMOR1)\n\t\tt = 0.3;\n\telse if (cl.playerview[SP].stats[STAT_ITEMS] & IT_ARMOR2)\n\t\tt = 0.6;\n\telse if (cl.playerview[SP].stats[STAT_ITEMS] & IT_ARMOR3)\n\t\tt = 0.8;\n\telse\n\t\tt = 0;\n\ta = cl.playerview[SP].stats[STAT_ARMOR];\n\th = cl.playerview[SP].stats[STAT_HEALTH];\n\n\t//work out the max useful armour\n\t//this will under-exagurate, due to usage of ceil based on damage\n\tm = h/(1-t);\n\tif (m < 0)\n\t\ta = 0;\n\telse if (m < a)\n\t\ta = m;\n\n\th = h + a;\n\tQ_snprintfz (macro_buf, 32, \"%d\", (int)h);\n\treturn macro_buf;\n}\n\nstatic char *Macro_Coloured_Armour(void)\n{\n\tif (cl.playerview[SP].stats[STAT_ITEMS] & IT_ARMOR3)\n\t\treturn \"{^s^xe00%%a^r}\";\n\telse if (cl.playerview[SP].stats[STAT_ITEMS] & IT_ARMOR2)\n\t\treturn \"{^s^xff0%%a^r}\";\n\telse if (cl.playerview[SP].stats[STAT_ITEMS] & IT_ARMOR1)\n\t\treturn \"{^s^x0b0%%a^r}\";\n\telse\n\t\treturn \"{0}\";\n}\n\nstatic char *Macro_Coloured_Powerups(void)\n{\n\tchar *quad, *pent, *ring;\n\tquad = (cl.playerview[SP].stats[STAT_ITEMS] & IT_QUAD)\t\t\t\t?va(\"^x03f%s\", tp_name_quad.string):\"\";\n\tpent = (cl.playerview[SP].stats[STAT_ITEMS] & IT_INVULNERABILITY)\t?va(\"^xe00%s\", tp_name_pent.string):\"\";\n\tring = (cl.playerview[SP].stats[STAT_ITEMS] & IT_INVISIBILITY)\t\t?va(\"^xff0%s\", tp_name_ring.string):\"\";\n\n\tif (*quad || *pent || *ring)\n\t{\n\t\tQ_snprintfz (macro_buf, 32, \"{^s%s%s%s^r}\", quad, pent, ring);\n\t\treturn macro_buf;\n\t}\n\telse\n\t\treturn \"\";\n}\nstatic char *Macro_Coloured_Short_Powerups(void)\n{\n\tchar *quad, *pent, *ring;\n\tquad = (cl.playerview[SP].stats[STAT_ITEMS] & IT_QUAD)\t\t\t\t?\"^x03fq\":\"\";\n\tpent = (cl.playerview[SP].stats[STAT_ITEMS] & IT_INVULNERABILITY)\t?\"^xe00p\":\"\";\n\tring = (cl.playerview[SP].stats[STAT_ITEMS] & IT_INVISIBILITY)\t\t?\"^xff0r\":\"\";\n\n\tif (*quad || *pent || *ring)\n\t{\n\t\tQ_snprintfz (macro_buf, 32, \"{^s%s%s%s^r}\", quad, pent, ring);\n\t\treturn macro_buf;\n\t}\n\telse\n\t\treturn \"\";\n}\nstatic char *Macro_Match_Status(void)\n{\n\tif (cls.state == ca_disconnected)\n\t\treturn \"disconnected\";\n\tif (cls.state < ca_active)\n\t\treturn \"connecting\";\n\tswitch(cl.matchstate)\n\t{\n\tcase MATCH_DONTKNOW:\n\tcase MATCH_INPROGRESS:\n\tdefault:\n\t\treturn \"normal\";\n\tcase MATCH_COUNTDOWN:\n\t\treturn \"countdown\";\n\tcase MATCH_STANDBY:\n\t\treturn \"standby\";\n\t}\n}\n/*static char *Macro_LastIP(void)\n{\t//report the last ip that someone said in chat. requires making guesses about what's an ip or not. can't handle hostnames properly\n\treturn \"---\";\n}\nstatic char *Macro_MP3Info(void)\n{\t//for people trying to be cool but really just annoying everyone\n\treturn \"---\";\n}\n*/\nstatic char *Macro_LastTrigger_Match(void)\n{\t//returns the last line that triggered a msg_trigger\n\treturn vars.lasttrigger_match;\n}\n#endif\n\n/*\n$matchname\nyou can use to get the name of the match\nmanually (echo $matchname).\nExample: a matchname might be\n\"[clan]quaker - [4on4_myclan_vs_someclan] - [dm3]\" or whatever.\n\n$matchstatus\n(\"disconnected\", \"standby\" or \"normal\"). This can be\nused for detecting prewar/prematch on ktpro/oztf servers.\n\n$mp3info\nEvaluates to \"author - title\".\nExamples:\nif you bind space \"say listening to $mp3info\"\nthen hitting space will say something like\n\"listening to disturbed - rise\".\nbind x \"if disturbed isin $mp3info then say dde music is cool\"\n\n$triggermatch\n$triggermatch is the last chat message that exec'd a msg_trigger.\n\n*/\n//Spike: added end.\n\nstatic void TP_InitMacros(void)\n{\n\tCmd_AddMacro(\"latency\", Macro_Latency, false);\n\tCmd_AddMacro(\"location\", Macro_Location, false);\n#ifdef QUAKESTATS\n\tCmd_AddMacro(\"health\", Macro_Health, true);\n\tCmd_AddMacro(\"armortype\", Macro_ArmorType, true);\n\tCmd_AddMacro(\"armor\", Macro_Armor, true);\n\tCmd_AddMacro(\"shells\", Macro_Shells, true);\n\tCmd_AddMacro(\"nails\", Macro_Nails, true);\n\tCmd_AddMacro(\"rockets\", Macro_Rockets, true);\n\tCmd_AddMacro(\"cells\", Macro_Cells, true);\n\tCmd_AddMacro(\"weaponnum\", Macro_WeaponNum, true);\n\tCmd_AddMacro(\"weapons\", Macro_Weapons, true);\n\tCmd_AddMacro(\"weapon\", Macro_Weapon, true);\n\tCmd_AddMacro(\"ammo\", Macro_Ammo, true);\n\tCmd_AddMacro(\"bestweapon\", Macro_BestWeapon, true);\n\tCmd_AddMacro(\"bestammo\", Macro_BestAmmo, true);\n\tCmd_AddMacro(\"powerups\", Macro_Powerups, true);\n\tCmd_AddMacro(\"droppedweapon\", Macro_DroppedWeapon, true);\n\tCmd_AddMacro(\"tf_skin\", Macro_TF_Skin, true);\n\tCmd_AddMacro(\"team\", Macro_Team, true);\t//confusing\n\n\tCmd_AddMacro(\"deathloc\", Macro_LastDeath, true);\n\tCmd_AddMacro(\"tookatloc\", Macro_TookAtLoc, true);\n\tCmd_AddMacro(\"tookloc\", Macro_TookLoc, true);\n\tCmd_AddMacro(\"took\", Macro_Took, true);\n\n\t//ones added by Spike, for fuhquake compatability\n\tCmd_AddMacro(\"connectiontype\", Macro_ConnectionType, false);\n\tCmd_AddMacro(\"demoplayback\", Macro_demoplayback, false);\n\tCmd_AddMacro(\"point\", Macro_PointName, true);\n\tCmd_AddMacro(\"pointatloc\", Macro_PointNameAtLocation, true);\n\tCmd_AddMacro(\"pointloc\", Macro_PointLocation, true);\n\tCmd_AddMacro(\"matchname\", Macro_Match_Name, false);\n\tCmd_AddMacro(\"matchtype\", Macro_Match_Type, false);\n\tCmd_AddMacro(\"need\", Macro_Need, true);\n\tCmd_AddMacro(\"ledstatus\", Macro_MyStatus_LED, true);\n\tCmd_AddMacro(\"ledpoint\", Macro_Point_LED, true);\n\tCmd_AddMacro(\"droploc\", Macro_LastDrop, true);\n\tCmd_AddMacro(\"droptime\", Macro_LastDropTime, true);\n\n\tCmd_AddMacro(\"matchstatus\", Macro_Match_Status, false);\n\tCmd_AddMacro(\"triggermatch\", Macro_LastTrigger_Match, false);\n#endif\n//\tCmd_AddMacro(\"mp3info\", Macro_MP3Info, false);\n\n\t//new, fte only (at least when first implemented)\n#ifdef QUAKESTATS\n\tCmd_AddMacro(\"chealth\", Macro_CombinedHealth, true);\n#endif\n\n\t//added for ezquake compatability\n//\tCmd_AddMacro(\"lastip\", Macro_LastIP, false);\n\tCmd_AddMacro(\"ping\", Macro_Latency, false);\n#ifdef QUAKESTATS\n\tCmd_AddMacro(\"colored_armor\", Macro_Coloured_Armour, true);\t//*shudder*\n\tCmd_AddMacro(\"colored_powerups\", Macro_Coloured_Powerups, true);\n\tCmd_AddMacro(\"colored_short_powerups\", Macro_Coloured_Short_Powerups, true);\n\tCmd_AddMacro(\"lastloc\", Macro_Last_Location, true);\n\tCmd_AddMacro(\"lastpowerup\", Macro_LastSeenPowerup, true);\n#endif\n\tCmd_AddMacro(\"gamedir\", Macro_Gamedir, false);\n}\n\n#define MAX_MACRO_STRING 1024\n\n/*\n=============\nTP_ParseMacroString\n\nParses %a-like expressions\n=============\n*/\nstatic char *TP_ParseMacroString (char *s)\n{\n\tstatic char\tbuf[MAX_MACRO_STRING];\n\tint\t\ti = 0;\n\tchar\t*macro_string;\n\n\tif (!cl_parseSay.ival)\n\t\treturn s;\n\n\twhile (*s && i < MAX_MACRO_STRING-1)\n\t{\n\t\t// check %[P], etc\n\t\tif (*s == '%' && s[1]=='[' && s[2] && s[3]==']')\n\t\t{\n#ifdef QUAKESTATS\n\t\t\tstatic char mbuf[MAX_MACRO_VALUE];\n#endif\n\t\t\tswitch (s[2]) {\n#ifdef QUAKESTATS\n\t\t\tcase 'a':\n\t\t\t\tmacro_string = Macro_ArmorType();\n\t\t\t\tif (!macro_string[0])\n\t\t\t\t\tmacro_string = \"a\";\n\t\t\t\tif (cl.playerview[SP].stats[STAT_ARMOR] < 30)\n\t\t\t\t\tQ_snprintfz (mbuf, sizeof(mbuf), \"\\x10%s:%i\\x11\", macro_string, cl.playerview[SP].stats[STAT_ARMOR]);\n\t\t\t\telse\n\t\t\t\t\tQ_snprintfz (mbuf, sizeof(mbuf), \"%s:%i\", macro_string, cl.playerview[SP].stats[STAT_ARMOR]);\n\t\t\t\tmacro_string = mbuf;\n\t\t\t\tbreak;\n\n\t\t\tcase 'h':\n\t\t\t\tif (cl.playerview[SP].stats[STAT_HEALTH] >= 50)\n\t\t\t\t\tQ_snprintfz (macro_buf, sizeof(macro_buf), \"%i\", cl.playerview[SP].stats[STAT_HEALTH]);\n\t\t\t\telse\n\t\t\t\t\tQ_snprintfz (macro_buf, sizeof(macro_buf), \"\\x10%i\\x11\", cl.playerview[SP].stats[STAT_HEALTH]);\n\t\t\t\tmacro_string = macro_buf;\n\t\t\t\tbreak;\n\n\t\t\tcase 'p':\n\t\t\tcase 'P':\n\t\t\t\tmacro_string = Macro_Powerups();\n\t\t\t\tif (macro_string[0])\n\t\t\t\t\tQ_snprintfz (mbuf, sizeof(mbuf), \"\\x10%s\\x11\", macro_string);\n\t\t\t\telse\n\t\t\t\t\tmbuf[0] = 0;\n\t\t\t\tmacro_string = mbuf;\n\t\t\t\tbreak;\n#endif\n\t\t\t\t// todo: %[w], %[b]\n\n\t\t\tdefault:\n\t\t\t\tbuf[i++] = *s++;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (i + strlen(macro_string) >= MAX_MACRO_STRING-1)\n\t\t\t\tSys_Error(\"TP_ParseMacroString: macro string length > MAX_MACRO_STRING)\");\n\t\t\tstrcpy (&buf[i], macro_string);\n\t\t\ti += strlen(macro_string);\n\t\t\ts += 4;\t// skip %[<char>]\n\t\t\tcontinue;\n\t\t}\n\n\t\t// check %a, etc\n\t\tif (*s == '%')\n\t\t{\n\t\t\tswitch (s[1])\n\t\t\t{\n#ifdef QUAKESTATS\n\t\t\t\tcase 'a':\tmacro_string = Macro_Armor(); break;\n\t\t\t\tcase 'A':\tmacro_string = Macro_ArmorType(); break;\n\t\t\t\tcase 'b':\tmacro_string = Macro_BestWeaponAndAmmo(); break;\n\t\t\t\tcase 'c':\tmacro_string = Macro_Cells(); break;\n\t\t\t\tcase 'd':\tmacro_string = Macro_LastDeath(); break;\n//\t\t\t\tcase 'D':\n\t\t\t\tcase 'h':\tmacro_string = Macro_Health(); break;\n\t\t\t\tcase 'i':\tmacro_string = Macro_TookAtLoc(); break;\n\t\t\t\tcase 'j':\tmacro_string = Macro_LastPointAtLoc(); break;\n\t\t\t\tcase 'k':\tmacro_string = Macro_LastTookOrPointed(); break;\n\t\t\t\tcase 'L':\tmacro_string = Macro_Last_Location(); break;\n#endif\n\t\t\t\tcase 'l':\tmacro_string = Macro_Location(); break;\n#ifdef QUAKESTATS\n\t\t\t\tcase 'm':\tmacro_string = Macro_LastTookOrPointed(); break;\n\n\t\t\t\tcase 'o':\tmacro_string = Macro_CountNearbyFriendlyPlayers(); break;\n\t\t\t\tcase 'e':\tmacro_string = Macro_CountNearbyEnemyPlayers(); break;\n\t\t\t\tcase 'O':\tmacro_string = Macro_Count_Last_NearbyFriendlyPlayers(); break;\n\t\t\t\tcase 'E':\tmacro_string = Macro_Count_Last_NearbyEnemyPlayers(); break;\n\n\t\t\t\tcase 'P':\n\t\t\t\tcase 'p':\tmacro_string = Macro_Powerups(); break;\n\t\t\t\tcase 'q':\tmacro_string = Macro_LastSeenPowerup(); break;\n//\t\t\t\tcase 'r':\tmacro_string = Macro_LastReportedLoc(); break;\n\t\t\t\tcase 's':\tmacro_string = Macro_EnemyStatus_LED(); break;\n\t\t\t\tcase 'S':\tmacro_string = Macro_TF_Skin(); break;\n\t\t\t\tcase 't':\tmacro_string = Macro_PointNameAtLocation(); break;\n\t\t\t\tcase 'u':\tmacro_string = Macro_Need(); break;\n\t\t\t\tcase 'w':\tmacro_string = Macro_WeaponAndAmmo(); break;\n\t\t\t\tcase 'x':\tmacro_string = Macro_PointName(); break;\n\t\t\t\tcase 'X':\tmacro_string = Macro_Took(); break;\n\t\t\t\tcase 'y':\tmacro_string = Macro_PointLocation(); break;\n\t\t\t\tcase 'Y':\tmacro_string = Macro_TookLoc(); break;\n#endif\n\n\t\t\t\tcase 'n':\t//vicinity\n\t\t\t\tcase 'N':\t//hides from you\n\t\t\t\tcase 'g':\t//bonus timers.\n\t\t\t\tcase 'C':\t//colour\n\t\t\t\tcase 'z':\t//nearest waypoint (looking)\n\t\t\t\tcase 'Z':\t//nearest waypoint (moving)\n\t\t\t\tdefault:\n\t\t\t\t\tbuf[i++] = *s++;\n\t\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (i + strlen(macro_string) >= MAX_MACRO_STRING-1)\n\t\t\t\tSys_Error(\"TP_ParseMacroString: macro string length > MAX_MACRO_STRING)\");\n\t\t\tstrcpy (&buf[i], macro_string);\n\t\t\ti += strlen(macro_string);\n\t\t\ts += 2;\t// skip % and letter\n\t\t\tcontinue;\n\t\t}\n\n\t\tbuf[i++] = *s++;\n\t}\n\tbuf[i] = 0;\n\n\treturn buf;\n}\n\n\n/*\n==============\nTP_ParseFunChars\n\nDoesn't check for overflows, so strlen(s) should be < MAX_MACRO_STRING\n==============\n*/\nconst char *TP_ParseFunChars (const char *s)\n{\n\tstatic char\t buf[MAX_MACRO_STRING];\n\tchar\t\t*out = buf;\n\tint\t\t\t c;\n\n\tif (!cl_parseFunChars.ival || com_parseutf8.ival != 0)\n\t\treturn s;\n\n\twhile (*s && out < buf+countof(buf)-1) {\n\t\tif (*s == '$' && s[1] == 'x') {\n\t\t\tint i;\n\t\t\t// check for $x10, $x8a, etc\n\t\t\tc = tolower((int)(unsigned char)s[2]);\n\t\t\tif ( isdigit(c) )\n\t\t\t\ti = (c - (int)'0') << 4;\n\t\t\telse if ( isxdigit(c) )\n\t\t\t\ti = (c - (int)'a' + 10) << 4;\n\t\t\telse goto skip;\n\t\t\tc = tolower((int)(unsigned char)s[3]);\n\t\t\tif ( isdigit(c) )\n\t\t\t\ti += (c - (int)'0');\n\t\t\telse if ( isxdigit(c) )\n\t\t\t\ti += (c - (int)'a' + 10);\n\t\t\telse goto skip;\n\t\t\tif (!i)\n\t\t\t\ti = (int)' ';\n\t\t\t*out++ = (char)i;\n\t\t\ts += 4;\n\t\t\tcontinue;\n\t\t}\n\t\tif (*s == '$' && s[1]) {\n\t\t\tc = 0;\n\t\t\tswitch (s[1]) {\n\t\t\t\tcase '\\\\': c = 0x0D; break;\n\t\t\t\tcase ':': c = 0x0A; break;\n\t\t\t\tcase '[': c = 0x10; break;\n\t\t\t\tcase ']': c = 0x11; break;\n\t\t\t\tcase 'G': c = 0x86; break;\n\t\t\t\tcase 'R': c = 0x87; break;\n\t\t\t\tcase 'Y': c = 0x88; break;\n\t\t\t\tcase 'B': c = 0x89; break;\n\t\t\t\tcase '(': c = 0x80; break;\n\t\t\t\tcase '=': c = 0x81; break;\n\t\t\t\tcase ')': c = 0x82; break;\n\t\t\t\tcase 'a': c = 0x83; break;\n\t\t\t\tcase '<': c = 0x1d; break;\n\t\t\t\tcase '-': c = 0x1e; break;\n\t\t\t\tcase '>': c = 0x1f; break;\n\t\t\t\tcase ',': c = 0x1c; break;\n\t\t\t\tcase '.': c = 0x9c; break;\n\t\t\t\tcase 'b': c = 0x8b; break;\n\t\t\t\tcase 'c':\n\t\t\t\tcase 'd': c = 0x8d; break;\n\t\t\t\tcase '$': c = '$'; break;\n\t\t\t\tcase '^': c = '^'; break;\n\t\t\t}\n\t\t\tif ( isdigit((int)(unsigned char)s[1]) )\n\t\t\t\tc = s[1] - (int)'0' + 0x12;\n\t\t\tif (c) {\n\t\t\t\t*out++ = (char)c;\n\t\t\t\ts += 2;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\nskip:\n\t\t*out++ = *s++;\n\t}\n\t*out = 0;\n\n\treturn buf;\n}\n\n/*\n=============================================================================\n\n\t\t\t\t\t\t\tPROXY .LOC FILES\n\n=============================================================================\n*/\n\ntypedef struct locdata_s {\n\tvec3_t min;\n\tvec3_t max;\n\tchar name[MAX_LOC_NAME];\n} locdata_t;\n\nstatic locdata_t\t*locdata;\t// FIXME: allocate dynamically?\nstatic size_t\t\tloc_numentries;\nstatic size_t\t\tloc_maxentries;\n\n\nstatic void TP_LoadLocFile (char *filename, qbool quiet)\n{\n\tchar\tfullpath[MAX_QPATH];\n\tchar\t*buf, *p;\n\tchar\tline[1024];\n\tint\t\ti, argc;\n\tint\t\terrorcount = 0;\n\tlocdata_t\t*loc;\n\tchar *comma, *space;\n\n\tif (!*filename)\n\t\treturn;\n\n\tQ_snprintfz (fullpath, sizeof(fullpath) - 4, \"locs/%s\", filename);\n\tCOM_DefaultExtension (fullpath, \".loc\", sizeof(fullpath));\n\n\tbuf = (char *) COM_LoadTempFile (fullpath, 0, NULL);\n\tif (!buf)\n\t{\n\t\tif (!quiet)\n\t\t\tCom_Printf (\"Could not load %s\\n\", fullpath);\n\t\treturn;\n\t}\n\n\tloc_numentries = 0;\n\n\t// parse the file\n\t// we rely on the fact that FS_Load*File always appends a 0 at the end\n\tp = buf;\n\twhile (1)\n\t{\n\t\tif (!*p)\n\t\t\tbreak;\t\t// end of file\n\n\t\t// get a line out\n\t\tfor (i = 0; i < sizeof(line)-1; )\n\t\t{\n\t\t\tchar c = *p++;\n\t\t\tif (!c || c == 10)\n\t\t\t\tbreak;\n\t\t\tif (c != 13)\n\t\t\t\tline[i++] = c;\n\t\t}\n\t\tline[i] = 0;\n\n\t\tcomma = strchr(line, ',');\n\t\tspace = strchr(line, ' ');\n\n\t\tif (comma && (comma < space || !space))\n\t\t{\n\t\t\tvec3_t min, max;\n\t\t\t\n\t\t\tmin[0] = strtod(line, &comma);\n\t\t\tif (*comma++ == ',')\n\t\t\t{\n\t\t\t\twhile(*comma == ' ' || *comma == '\\t')\n\t\t\t\t\tcomma++;\n\t\t\t\tmin[1] = strtod(comma, &comma);\n\t\t\t\tif (*comma++ == ',')\n\t\t\t\t{\n\t\t\t\t\twhile(*comma == ' ' || *comma == '\\t')\n\t\t\t\t\t\tcomma++;\n\t\t\t\t\tmin[2] = strtod(comma, &comma);\n\t\t\t\t\tif (*comma++ == ',')\n\t\t\t\t\t{\n\t\t\t\t\t\twhile(*comma == ' ' || *comma == '\\t')\n\t\t\t\t\t\t\tcomma++;\n\t\t\t\t\t\tmax[0] = strtod(comma, &comma);\n\t\t\t\t\t\tif (*comma++ == ',')\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\twhile(*comma == ' ' || *comma == '\\t')\n\t\t\t\t\t\t\t\tcomma++;\n\t\t\t\t\t\t\tmax[1] = strtod(comma, &comma);\n\t\t\t\t\t\t\tif (*comma++ == ',')\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\twhile(*comma == ' ' || *comma == '\\t')\n\t\t\t\t\t\t\t\t\tcomma++;\n\t\t\t\t\t\t\t\tmax[2] = strtod(comma, &comma);\n\t\t\t\t\t\t\t\tif (*comma++ == ',')\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tif (loc_numentries == loc_maxentries)\n\t\t\t\t\t\t\t\t\t\tZ_ReallocElements((void**)&locdata, &loc_maxentries, loc_numentries+64, sizeof(*locdata));\n\t\t\t\t\t\t\t\t\tloc = &locdata[loc_numentries];\n\t\t\t\t\t\t\t\t\tloc_numentries++;\n\n\t\t\t\t\t\t\t\t\twhile(*comma == ' ' || *comma == '\\t')\n\t\t\t\t\t\t\t\t\t\tcomma++;\n\t\t\t\t\t\t\t\t\tif (*comma == '\\\"')\n\t\t\t\t\t\t\t\t\t\tCOM_ParseOut(comma, loc->name, sizeof(loc->name));\n\t\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t\t\tQ_strncpyz(loc->name, comma, sizeof(loc->name));\n\n\t\t\t\t\t\t\t\t\tfor (i = 0; i < 3; i++)\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tif (min[i] > max[i])\n\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\tloc->min[i] = max[i];\n\t\t\t\t\t\t\t\t\t\t\tloc->max[i] = min[i];\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\tloc->min[i] = min[i];\n\t\t\t\t\t\t\t\t\t\t\tloc->max[i] = max[i];\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\terrorcount++;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tCmd_TokenizeString (line, true, false);\n\n\t\t\targc = Cmd_Argc();\n\t\t\tif (!argc)\n\t\t\t\tcontinue;\n\n\t\t\tif (argc < 4)\n\t\t\t{\n\t\t\t\terrorcount++;\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (atoi(Cmd_Argv(0)) == 0 && Cmd_Argv(0)[0] != '0')\n\t\t\t{\n\t\t\t\t// first token is not a number\n\t\t\t\terrorcount++;\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (loc_numentries == loc_maxentries)\n\t\t\t\tZ_ReallocElements((void**)&locdata, &loc_maxentries, loc_numentries+64, sizeof(*locdata));\n\t\t\tloc = &locdata[loc_numentries];\n\t\t\tloc_numentries++;\n\n\t\t\tfor (i = 0; i < 3; i++)\n\t\t\t\tloc->min[i] = loc->max[i] = atoi(Cmd_Argv(i)) / 8.0;\n\n\t\t\tloc->name[0] = 0;\n\t\t\tfor (i = 3; i < argc; i++)\n\t\t\t{\n\t\t\t\tif (i != 3)\n\t\t\t\t\tQ_strncatz (loc->name, \" \", sizeof(loc->name));\n\t\t\t\tQ_strncatz (loc->name, Cmd_Argv(i), sizeof(loc->name));\n\t\t\t}\n\t\t}\n\t}\n\n\tif (!quiet)\n\t\tCom_Printf (\"Loaded %s (%lu points)\\n\", fullpath, (unsigned long)loc_numentries);\n}\n\nstatic void TP_LoadLocFile_f (void)\n{\n\tif (Cmd_Argc() != 2)\n\t{\n\t\tCom_Printf (\"loadloc <filename> : load a loc file\\n\");\n\t\treturn;\n\t}\n\n\tTP_LoadLocFile (Cmd_Argv(1), false);\n}\nqboolean TP_HaveLocations(void)\n{\n\treturn loc_numentries>0;\n}\nchar *TP_LocationName (const vec3_t location)\n{\n\tint\t\ti, j, minnum;\n\tfloat\tdist, mindist;\n\tvec3_t\tvec;\n\tstatic qbool\trecursive;\n\tstatic char buf[MAX_LOC_NAME];\n\tint level;\n\n\tif (!loc_numentries || (cls.state != ca_active))\n\t\treturn tp_name_someplace.string;\n\n\tif (recursive)\n\t\treturn \"\";\n\n\tminnum = 0;\n\tmindist = 9999999;\n\n\tfor (i = 0; i < loc_numentries; i++)\n\t{\n\t\t//clip the point to the box, to find the nearest point within it.\n\t\tfor (j = 0; j < 3; j++)\n\t\t\tvec[j] = bound(locdata[i].min[j], location[j], locdata[i].max[j]);\n\t\tVectorSubtract (location, vec, vec);\n\t\tdist = VectorLength (vec);\n\t\tif (dist < mindist)\n\t\t{\n\t\t\tminnum = i;\n\t\t\tmindist = dist;\n\n\t\t\t//break out if we're actually inside it, to always favour the first.\n\t\t\tif (dist < 0.01)\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\trecursive = true;\n\tlevel = Cmd_ExecLevel;\n\tCmd_ExpandString (locdata[minnum].name, buf, sizeof(buf), &level, false, true, false);\n\trecursive = false;\n\n\treturn buf;\n}\n\n/*\n=============================================================================\n\n\t\t\t\t\t\t\tMESSAGE TRIGGERS\n\n=============================================================================\n*/\n\n// FIXME, we don't provide a way to remove triggers\n// allocated heap memory is not freed when the engine shuts down\n\ntypedef struct msg_trigger_s {\n\tchar\tname[32];\n\tchar\tstring[64];\n\tint\t\tlevel;\n\tstruct msg_trigger_s *next;\n} msg_trigger_t;\n\nstatic msg_trigger_t *msg_triggers;\n\nstatic msg_trigger_t *TP_FindTrigger (char *name)\n{\n\tmsg_trigger_t *t;\n\n\tfor (t=msg_triggers; t; t=t->next)\n\t\tif (!strcmp(t->name, name))\n\t\t\treturn t;\n\n\treturn NULL;\n}\n\n\nstatic void TP_MsgTrigger_f (void)\n{\n\tint\t\tc;\n\tchar\t*name;\n\tmsg_trigger_t\t*trig;\n\n\tif (Cmd_IsInsecure())\n\t\treturn;\n\n\tc = Cmd_Argc();\n\n\tif (c > 5) {\n\t\tCom_Printf (\"msg_trigger <trigger name> \\\"string\\\" [-l <level>]\\n\");\n\t\treturn;\n\t}\n\n\tif (c == 1) {\n\t\tif (!msg_triggers)\n\t\t\tCom_Printf (\"no triggers defined\\n\");\n\t\telse\n\t\tfor (trig=msg_triggers; trig; trig=trig->next)\n\t\t\tCom_Printf (\"%s : \\\"%s\\\"\\n\", trig->name, trig->string);\n\t\treturn;\n\t}\n\n\tname = Cmd_Argv(1);\n\tif (strlen(name) > 31) {\n\t\tCom_Printf (\"trigger name too long\\n\");\n\t\treturn;\n\t}\n\n\tif (c == 2) {\n\t\ttrig = TP_FindTrigger (name);\n\t\tif (trig)\n\t\t\tCom_Printf (\"%s: \\\"%s\\\"\\n\", trig->name, trig->string);\n\t\telse\n\t\t\tCom_Printf (\"trigger \\\"%s\\\" not found\\n\", name);\n\t\treturn;\n\t}\n\n\tif (c >= 3) {\n\t\tif (strlen(Cmd_Argv(2)) >= countof(trig->string)) {\n\t\t\tCom_Printf (\"trigger string too long\\n\");\n\t\t\treturn;\n\t\t}\n\n\t\ttrig = TP_FindTrigger (name);\n\n\t\tif (!trig) {\n\t\t\t// allocate new trigger\n\t\t\ttrig = Z_Malloc (sizeof(msg_trigger_t));\n\t\t\ttrig->next = msg_triggers;\n\t\t\tmsg_triggers = trig;\n\t\t\tstrcpy (trig->name, name);\t// safe (length checked earlier)\n\t\t\ttrig->level = PRINT_HIGH;\n\t\t}\n\n\t\tstrcpy (trig->string, Cmd_Argv(2));\t// safe (length checked earlier)\n\t\tif (c == 5 && !Q_stricmp (Cmd_Argv(3), \"-l\")) {\n\t\t\tif (!strcmp(Cmd_Argv(4), \"t\"))\n\t\t\t\ttrig->level = 4;\n\t\t\telse {\n\t\t\t\ttrig->level = Q_atoi (Cmd_Argv(4));\n\t\t\t\tif ((unsigned)trig->level > PRINT_CHAT)\n\t\t\t\t\ttrig->level = PRINT_HIGH;\n\t\t\t}\n\t\t}\n\t}\n}\n\n\nvoid TP_SearchForMsgTriggers (char *s, int level)\n{\n\tmsg_trigger_t\t*t;\n\tchar *string;\n\n\tif (cls.demoplayback)\n\t\treturn;\n\n\tif (!ruleset_allow_triggers.ival)\n\t\treturn;\n\n\tfor (t=msg_triggers; t; t=t->next)\n\t\tif ((t->level == level || (t->level == 3 && level == 4))\n\t\t\t&& t->string[0] && strstr(s, t->string))\n\t\t{\n\t\t\tif (level == PRINT_CHAT && (\n\t\t\t\tstrstr (s, \"_version\") || strstr (s, \"_system\") ||\n\t\t\t\tstrstr (s, \"_speed\") || strstr (s, \"_modified\") || strstr (s, \"_ruleset\")))\n\t\t\t\tcontinue; \t// don't let llamas fake proxy replies\n\n\t\t\tstring = Cmd_AliasExist (t->name, RESTRICT_LOCAL);\n\t\t\tif (string)\n\t\t\t{\n#ifdef QUAKESTATS\n\t\t\t\tQ_strncpyz(vars.lasttrigger_match, s, sizeof (vars.lasttrigger_match));\n#endif\n\t\t\t\tCbuf_AddText (string, RESTRICT_LOCAL);\n\t\t\t\tCbuf_AddText (\"\\n\", RESTRICT_LOCAL);\n//\t\t\t\tCbuf_ExecuteLevel (RESTRICT_LOCAL);\n\t\t\t}\n\t\t\telse\n\t\t\t\tCom_Printf (\"trigger \\\"%s\\\" has no matching alias\\n\", t->name);\n\t\t}\n}\n\n#ifdef QWSKINS\n/*\n=============================================================================\n\t\t\t\t\t\tTEAMCOLOR & ENEMYCOLOR\n=============================================================================\n*/\n\nunsigned int\t\tcl_teamtopcolor = ~0;\nunsigned int\t\tcl_teambottomcolor = ~0;\nunsigned int\t\tcl_enemytopcolor = ~0;\nunsigned int\t\tcl_enemybottomcolor = ~0;\n\nstatic unsigned int TP_ForceColour(char *col)\n{\n\tfloat rgb[3];\n\tunsigned int bitval;\n\tif (!strcmp(col, \"off\"))\n\t\treturn ~0;\n\tif (!strncmp(col, \"0x\", 2))\n\t{\n\t\tif (strlen(col+2) == 3)\n\t\t{\n\t\t\tbitval = strtoul(col+2, NULL, 16);\n\t\t\tbitval = ((bitval & 0xf00)<<12) | ((bitval & 0x0f0)<<8) | ((bitval & 0x00f)<<4);\n\t\t\tbitval |= 0x01000000;\n\t\t}\n\t\telse\n\t\t\tbitval = 0x01000000 | strtoul(col+2, NULL, 16);\n\t\tif (bitval == ~0)\n\t\t\tbitval = 0x01ffffff;\n\t\treturn bitval;\n\t}\n\tif (!strncmp(col, \"x\", 1))\n\t{\n\t\tif (strlen(col+1) == 3)\n\t\t{\n\t\t\tbitval = strtoul(col+1, NULL, 16);\n\t\t\tbitval = ((bitval & 0xf00)<<12) | ((bitval & 0x0f0)<<8) | ((bitval & 0x00f)<<4)\n\t\t\t\t   | ((bitval & 0xf00)<< 8) | ((bitval & 0x0f0)<<4) | ((bitval & 0x00f)<<0);\n\t\t\tbitval |= 0x01000000;\n\t\t}\n\t\telse\n\t\t\tbitval = 0x01000000 | strtoul(col+1, NULL, 16);\n\t\tif (bitval == ~0)\n\t\t\tbitval = 0x01ffffff;\n\t\treturn bitval;\n\t}\n\tif (strchr(col, ' '))\n\t{\n\t\tSCR_StringToRGB(col, rgb, 1);\n\t\tbitval = ((unsigned char)rgb[0]<<0) | ((unsigned char)rgb[1]<<8) | ((unsigned char)rgb[2]<<16) | (0xff<<24);\n\t\tif (bitval == ~0)\n\t\t\tbitval = 0x01ffffff;\n\t\treturn bitval;\n\t}\n\treturn atoi(col);\n}\n\ncolourised_t *TP_FindColours(char *name)\n{\n\tcolourised_t  *col;\n\tfor (col = cls.colourised; col; col = col->next)\n\t{\n\t\tif (!strncmp(col->name, name, sizeof(col->name)-1))\n\t\t{\n\t\t\treturn col;\n\t\t}\n\t}\n\treturn NULL;\n}\n\nstatic void TP_Colourise_f (void)\n{\n\tint i;\n\tunsigned char *topstr, *botstr;\n\tcolourised_t *col, *last;\n\tif (Cmd_Argc() == 1)\n\t{\n\t\treturn;\n\t}\n\n\tcol = TP_FindColours(Cmd_Argv(1));\n\n\tif (Cmd_Argc() == 2)\n\t{\n\t\tif (col)\n\t\t{\n\t\t\tif (col == cls.colourised)\n\t\t\t\tcls.colourised = col->next;\n\t\t\telse\n\t\t\t{\n\t\t\t\tfor (last = cls.colourised; last; last = last->next)\n\t\t\t\t{\n\t\t\t\t\tif (last->next == col)\n\t\t\t\t\t{\n\t\t\t\t\t\tlast->next = col->next;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tZ_Free(col);\n\t\t}\n\t}\n\telse\n\t{\n\t\tif (!col)\n\t\t{\n\t\t\tcol = Z_Malloc(sizeof(*col));\n\t\t\tcol->next = cls.colourised;\n\t\t\tcls.colourised = col;\n\t\t\tQ_strncpyz(col->name, Cmd_Argv(1), sizeof(col->skin));\n\t\t}\n\n\t\ttopstr = Cmd_Argv(2);\n\t\tbotstr = strchr(topstr, '.');\n\t\tif (botstr)\n\t\t\t*botstr++ = '\\0';\n\t\telse\n\t\t\tbotstr = topstr;\n\n\t\tcol->topcolour = TP_ForceColour(topstr);\n\t\tcol->bottomcolour = TP_ForceColour(botstr);\n\t\tQ_strncpyz(col->skin, Cmd_Argv(3), sizeof(col->skin));\n\t}\n\n\tSkin_FlushPlayers();\n\tfor (i = 0; i < cl.allocated_client_slots; i++)\n\t{\n\t\tcl.players[i].colourised = TP_FindColours(cl.players[i].name);\n\t\tCL_NewTranslation(i);\n\t}\n}\n\nstatic void TP_TeamColor_CB (struct cvar_s *var, char *oldvalue)\n{\n\tunsigned int\ttop, bottom;\n\tint\ti;\n\tchar *n = COM_Parse(var->string);\n\ttop = TP_ForceColour(com_token);\n\tCOM_Parse(n);\n\tif (!*com_token)\n\t\tbottom = top;\n\telse {\n\t\tbottom = TP_ForceColour(com_token);\n\t}\n\n\tcl_teamtopcolor = top;\n\tcl_teambottomcolor = bottom;\n\n\tif (qrenderer != QR_NONE)\t//make sure we have the renderer initialised...\n\t\tfor (i = 0; i < cl.allocated_client_slots; i++)\n\t\t\tCL_NewTranslation(i);\n}\n\nstatic void TP_EnemyColor_CB (struct cvar_s *var, char *oldvalue)\n{\n\tunsigned int\ttop, bottom;\n\tint\ti;\n\tchar *n = COM_Parse(var->string);\n\ttop = TP_ForceColour(com_token);\n\tCOM_Parse(n);\n\tif (!*com_token)\n\t\tbottom = top;\n\telse {\n\t\tbottom = TP_ForceColour(com_token);\n\t}\n\n\tcl_enemytopcolor = top;\n\tcl_enemybottomcolor = bottom;\n\n\tif (qrenderer != QR_NONE)\t//make sure we have the renderer initialised...\n\t\tfor (i = 0; i < cl.allocated_client_slots; i++)\n\t\t\tCL_NewTranslation(i);\n}\n\n\nstatic void QDECL TP_SkinCvar_Callback(struct cvar_s *var, char *oldvalue)\n{\n\tSkin_FlushPlayers();\n}\n\n#endif\n\n//===================================================================\n\nvoid TP_NewMap (void)\n{\n\tstatic char last_map[MAX_QPATH];\n\tchar locname[MAX_QPATH];\n\n#ifdef QUAKESTATS\n\tmemset (&vars, 0, sizeof(vars));\n\tTP_FindModelNumbers ();\n#endif\n\n\t// FIXME, just try to load the loc file no matter what?\n\tif (strcmp(host_mapname.string, last_map))\n\t{\t// map name has changed\n\t\tloc_numentries = 0;\t// clear loc file\n\t\tif (tp_loadlocs.value && cl.allocated_client_slots > 1)// && !cls.demoplayback)\n\t\t{\n\t\t\tQ_snprintfz (locname, sizeof(locname), \"%s.loc\", host_mapname.string);\n\t\t\tTP_LoadLocFile (locname, true);\n\n\t\t\tstrlcpy (last_map, host_mapname.string, sizeof(last_map));\n\t\t}\n\t\telse\n\t\t\tstrlcpy (last_map, \"\", sizeof(last_map));\n\t}\n\n#ifdef QUAKESTATS\n\tTP_UpdateAutoStatus();\n#endif\n\tTP_ExecTrigger (\"f_newmap\", false);\n}\n\n/*\n======================\nTP_CategorizeMessage\n\nreturns a combination of these values:\n0 -- unknown\n1 -- normal\n2 -- team message\n4 -- spectator\n16 -- faked or serverside\nNote that sometimes we can't be sure who really sent the message,\ne.g. when there's a player \"unnamed\" in your team and \"(unnamed)\"\nin the enemy team. The result will be 3 (1+2)\n\nNever returns 2 if we are a spectator.\n\nNow additionally returns player info (NULL if no player detected)\n======================\n*/\nint TP_CategorizeMessage (char *s, int *offset, player_info_t **plr)\n{\n\tint\t\ti, msglen, len;\n\tint\t\tflags;\n\tplayer_info_t\t*player;\n\tchar\t*name;\n\tplayerview_t *pv = &cl.playerview[SP];\n\n\t*offset = 0;\n\t*plr = NULL;\n\n\tflags = TPM_UNKNOWN;\n\tmsglen = strlen(s);\n\tif (!msglen)\n\t\treturn TPM_UNKNOWN;\n\n\tif ((s[0] == '^' && s[1] == '[') || (s[0] == '(' && s[1] == '^' && s[2] == '['))\n\t{\n\t\tchar *end, *info;\n\t\ti = 0;\n\t\tfor(info = s; *info; )\n\t\t{\n\t\t\tif (info[0] == '^' && info[1] == ']')\n\t\t\t\tbreak;\n\t\t\tif (*info == '\\\\')\n\t\t\t\tbreak;\n\t\t\tif (info[0] == '^' && info[1] == '^')\n\t\t\t\tinfo+=2;\n\t\t\telse\n\t\t\t\tinfo++;\n\t\t}\n\t\tfor(end = info; *end; )\n\t\t{\n\t\t\tif (end[0] == '^' && end[1] == ']')\n\t\t\t{\n\t\t\t\t*end = 0;\n\t\t\t\tinfo = Info_ValueForKey(info, \"player\");\n\t\t\t\tif (*info)\n\t\t\t\t\ti = atoi(info)+1;\n\t\t\t\t*end = '^';\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (end[0] == '^' && end[1] == '^')\n\t\t\t\tend+=2;\n\t\t\telse\n\t\t\t\tend++;\n\t\t}\n\t\tif (!*end || i < 1 || i > cl.allocated_client_slots)\n\t\t\treturn TPM_UNKNOWN;\n\t\tif (*s == '(')\n\t\t{\n\t\t\tif (end[2] != ')')\n\t\t\t\treturn TPM_UNKNOWN;\n\t\t\tend+=3;\n\t\t}\n\t\telse\n\t\t\tend+=2;\n\t\tif (*end++ != ':')\n\t\t\treturn TPM_UNKNOWN;\n\t\tif (*end++ != ' ')\n\t\t\treturn TPM_UNKNOWN;\n\t\t*plr = player = &cl.players[i-1];\n\t\t*offset = end - s;\n\n\t\tif (*s == '(')\n\t\t\tflags = TPM_TEAM;\n\t\telse\n\t\t{\n\t\t\tif (player->spectator)\n\t\t\t\tflags |= TPM_SPECTATOR;\n\t\t\telse\n\t\t\t\tflags |= TPM_NORMAL;\n\t\t}\n\t}\n\telse\n\t{\n\t\tfor (i=0, player=cl.players ; i < cl.allocated_client_slots ; i++, player++)\n\t\t{\n\t\t\tname = player->name;\n\t\t\tif (!(*name))\n\t\t\t\tcontinue;\n\t\t\tlen = strlen(name);\n\t\t\t// check messagemode1\n\t\t\tif (len+2 <= msglen && s[len] == ':' && s[len+1] == ' '\t&&\n\t\t\t\t!strncmp(name, s, len))\n\t\t\t{\n\t\t\t\tif (player->spectator)\n\t\t\t\t\tflags |= TPM_SPECTATOR;\n\t\t\t\telse\n\t\t\t\t\tflags |= TPM_NORMAL;\n\t\t\t\t*offset = len + 2;\n\t\t\t\t*plr = player;\n\t\t\t}\n\t\t\t// check messagemode2\n\t\t\telse if (s[0] == '(' && len+4 <= msglen &&\n\t\t\t\t!strncmp(s+len+1, \"): \", 3) &&\n\t\t\t\t!strncmp(name, s+1, len))\n\t\t\t{\n\t\t\t\t// no team messages in teamplay 0, except for our own\n\t\t\t\tif (pv->spectator)\n\t\t\t\t{\n\t\t\t\t\tint track = Cam_TrackNum(pv);\n\t\t\t\t\tif (track>=0 && (i == track || ( cl.teamplay &&\n\t\t\t\t\t\t!strcmp(cl.players[track].team, player->team)) ))\n\t\t\t\t\t{\n\t\t\t\t\t\tflags |= TPM_OBSERVEDTEAM;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tif (i == pv->playernum || ( cl.teamplay &&\n\t\t\t\t\t\t!strcmp(cl.players[pv->playernum].team, player->team)) )\n\t\t\t\t\t{\n\t\t\t\t\t\tflags |= TPM_TEAM;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t*offset = len + 4;\n\t\t\t\t*plr = player;\n\t\t\t}\n\t\t}\n\n//\t\tif (i == cl.allocated_client_slots)\n//\t\t\treturn flags;\n\n\t}\n/*\n\tif (!flags) // search for fake/non player\n\t{\n\t\tif ((name = strstr(s, \": \"))) // use name as temp\n\t\t{\n\t\t\t*offset = (name - s) + 2;\n\t\t\tflags = TPM_FAKED;\n\n\t\t\tif (msglen > 4 && *s == '(' && s[-1] == ')')\n\t\t\t\tflags |= TPM_TEAM;\n\t\t}\n\t}\n*/\n\n\tif (!flags)\n\t{\n\t\tchar *qtv = NULL;\n\t\tif (!strncmp(s, \"#0:qtv_say:#\", 12))\n\t\t{\n\t\t\tqtv = s+11;\n\t\t\tflags = TPM_QTV|TPM_SPECTATOR;\n\t\t}\n\t\telse if (!strncmp(s, \"#0:qtv_say_game:#\", 17))\n\t\t{\n\t\t\tqtv = s+16;\n\t\t\tflags = TPM_QTV|TPM_SPECTATOR;\n\t\t}\n\t\telse if (!strncmp(s, \"#0:qtv_say_team_game:#\", 22))\n\t\t{\n\t\t\tqtv = s+21;\n\t\t\tflags = TPM_QTV|TPM_TEAM|TPM_SPECTATOR;\n\t\t}\n\t\tif (flags)\n\t\t{\n\t\t\t*offset = (qtv - s);\n\t\t\tfor (;;)\n\t\t\t{\n\t\t\t\tchar *sub = qtv;\n\t\t\t\tif (*sub == '#')\n\t\t\t\t{\n\t\t\t\t\tstrtoul(sub+1, &sub, 10);\n\t\t\t\t\tif (*sub++ == ':')\n\t\t\t\t\t{\n\t\t\t\t\t\tqtv = strstr(sub, \": \");\n\t\t\t\t\t\tif (qtv)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t*offset = (sub - s);\n\t\t\t\t\t\t\tqtv += 2;\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn flags;\n}\n\n#ifdef QUAKESTATS\n//===================================================================\n// Pickup triggers\n//\n\n// symbolic names used in tp_took, tp_pickup, tp_point commands\nchar *pknames[] = {\"quad\", \"pent\", \"ring\", \"suit\", \"ra\", \"ya\",\t\"ga\",\n\"mh\", \"health\", \"lg\", \"rl\", \"gl\", \"sng\", \"ng\", \"ssg\", \"pack\",\n\"cells\", \"rockets\", \"nails\", \"shells\", \"flag\",\n\"teammate\", \"enemy\", \"eyes\", \"sentry\", \"disp\", \"runes\"};\n\n#define it_quad\t\t(1 << 0)\n#define it_pent\t\t(1 << 1)\n#define it_ring\t\t(1 << 2)\n#define it_suit\t\t(1 << 3)\n#define it_ra\t\t(1 << 4)\n#define it_ya\t\t(1 << 5)\n#define it_ga\t\t(1 << 6)\n#define it_mh\t\t(1 << 7)\n#define it_health\t(1 << 8)\n#define it_lg\t\t(1 << 9)\n#define it_rl\t\t(1 << 10)\n#define it_gl\t\t(1 << 11)\n#define it_sng\t\t(1 << 12)\n#define it_ng\t\t(1 << 13)\n#define it_ssg\t\t(1 << 14)\n#define it_pack\t\t(1 << 15)\n#define it_cells\t(1 << 16)\n#define it_rockets\t(1 << 17)\n#define it_nails\t(1 << 18)\n#define it_shells\t(1 << 19)\n#define it_flag\t\t(1 << 20)\n#define it_teammate\t(1 << 21)\n#define it_enemy\t(1 << 22)\n#define it_eyes\t\t(1 << 23)\n#define it_sentry   (1 << 24)\n#define it_disp\t\t(1 << 25)\n#define it_runes\t(1 << 26)\n#define NUM_ITEMFLAGS 27\n\n#define it_powerups\t(it_quad|it_pent|it_ring)\n#define it_weapons\t(it_lg|it_rl|it_gl|it_sng|it_ng|it_ssg)\n#define it_armor\t(it_ra|it_ya|it_ga)\n#define it_ammo\t\t(it_cells|it_rockets|it_nails|it_shells)\n#define it_players\t(it_teammate|it_enemy|it_eyes)\n\n#define default_pkflags (it_powerups|it_suit|it_armor|it_weapons|it_mh| \\\n\t\t\t\tit_rockets|it_pack|it_flag)\n\n#define default_tookflags (it_powerups|it_ra|it_ya|it_lg|it_rl|it_mh|it_flag)\n\n#define default_pointflags (it_powerups|it_suit|it_armor|it_mh| \\\n\t\t\t\tit_lg|it_rl|it_gl|it_sng|it_rockets|it_pack|it_flag|it_players)\n\nint pkflags = default_pkflags;\nint tookflags = default_tookflags;\nint pointflags = default_pointflags;\n\nstatic void FlagCommand (int *flags, int defaultflags) {\n\tint i, j, c, flag;\n\tchar *p, str[255] = {0};\n\tqboolean removeflag = false;\n\n\tc = Cmd_Argc ();\n\tif (c == 1)\t{\n\t\tif (!*flags)\n\t\t\tstrcpy (str, \"none\");\n\t\tfor (i = 0 ; i < NUM_ITEMFLAGS ; i++)\n\t\t\tif (*flags & (1 << i)) {\n\t\t\t\tif (*str)\n\t\t\t\t\tQ_strncatz (str, \" \", sizeof(str));\n\t\t\t\tQ_strncatz (str, pknames[i], sizeof(str));\n\t\t\t}\n\t\tCom_Printf (\"%s\\n\", str);\n\t\treturn;\n\t}\n\n\tif (c == 2 && !Q_strcasecmp(Cmd_Argv(1), \"none\")) {\n\t\t*flags = 0;\n\t\treturn;\n\t}\n\n\tif (*Cmd_Argv(1) != '+' && *Cmd_Argv(1) != '-')\n\t\t*flags = 0;\n\n\tfor (i = 1; i < c; i++) {\n\t\tp = Cmd_Argv (i);\n\t\tif (*p == '+') {\n\t\t\tremoveflag = false;\n\t\t\tp++;\n\t\t} else if (*p == '-') {\n\t\t\tremoveflag = true;\n\t\t\tp++;\n\t\t}\n\n\t\tflag = 0;\n\t\tfor (j=0 ; j<NUM_ITEMFLAGS ; j++) {\n\t\t\tif (!Q_strncasecmp (p, pknames[j], 3)) {\n\t\t\t\tflag = 1<<j;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tif (!flag) {\n\t\t\tif (!Q_strcasecmp (p, \"armor\"))\n\t\t\t\tflag = it_armor;\n\t\t\telse if (!Q_strcasecmp (p, \"weapons\"))\n\t\t\t\tflag = it_weapons;\n\t\t\telse if (!Q_strcasecmp (p, \"powerups\"))\n\t\t\t\tflag = it_powerups;\n\t\t\telse if (!Q_strcasecmp (p, \"ammo\"))\n\t\t\t\tflag = it_ammo;\n\t\t\telse if (!Q_strcasecmp (p, \"players\"))\n\t\t\t\tflag = it_players;\n\t\t\telse if (!Q_strcasecmp (p, \"default\"))\n\t\t\t\tflag = defaultflags;\n\t\t\telse if (!Q_strcasecmp (p, \"all\"))\n\t\t\t\tflag = (1<<NUM_ITEMFLAGS)-1;\n\t\t}\n\n\n\t\tif (flags != &pointflags)\n\t\t\tflag &= ~(it_sentry|it_disp|it_players);\n\n\t\tif (removeflag)\n\t\t\t*flags &= ~flag;\n\t\telse\n\t\t\t*flags |= flag;\n\t}\n}\n\nstatic void TP_Took_f (void)\n{\n\tFlagCommand (&tookflags, default_tookflags);\n}\n\nvoid TP_Pickup_f (void)\n{\n\tFlagCommand (&pkflags, default_pkflags);\n}\n\nstatic void TP_Point_f (void)\n{\n\tFlagCommand (&pointflags, default_pointflags);\n}\n\n\n/*\n// FIXME: maybe use sound indexes so we don't have to make strcmp's\n// every time?\n\n#define S_LOCK4\t\t1\t// weapons/lock4.wav\n#define S_PKUP\t\t2\t// weapons/pkup.wav\n#define S_HEALTH25\t3\t// items/health1.wav\n#define S_HEALTH15\t4\t// items/r_item1.wav\n#define S_MHEALTH\t5\t// items/r_item2.wav\n#define S_DAMAGE\t6\t// items/damage.wav\n#define S_EYES\t\t7\t// items/inv1.wav\n#define S_PENT\t\t8\t// items/protect.wav\n#define S_ARMOR\t\t9\t// items/armor1.wav\n\nstatic char *tp_soundnames[] =\n{\n\t\"weapons/lock4.wav\",\n\t\"weapons/pkup.wav\",\n\t\"items/health1.wav\",\n\t\"items/r_item1.wav\",\n\t\"items/r_item2.wav\",\n\t\"items/damage.wav\",\n\t\"items/inv1.wav\",\n\t\"items/protect.wav\"\n\t\"items/armor1.wav\"\n};\n\n#define TP_NUMSOUNDS (sizeof(tp_soundnames)/sizeof(tp_soundnames[0]))\n\nint\tsound_numbers[MAX_SOUNDS];\n\nvoid TP_FindSoundNumbers (void)\n{\n\tint\t\ti, j;\n\tchar\t*s;\n\tfor (i=0 ; i<MAX_SOUNDS ; i++)\n\t{\n\t\ts = &cl.sound_name[i];\n\t\tfor (j=0 ; j<TP_NUMSOUNDS ; j++)\n\t\t\t...\n\t}\n}\n*/\n\ntypedef struct {\n\tint\t\titemflag;\n\tcvar_t\t*cvar;\n\tchar\t*modelname;\n\tvec3_t\toffset;\t\t// offset of model graphics center\n\tfloat\tradius;\t\t// model graphics radius\n\tfloat\trespawntime;// automatic respawn timer for mvds.\n\tint\t\tflags;\t\t// TODO: \"NOPICKUP\" (disp), \"TEAMENEMY\" (flag, disp)\n} item_t;\n\nstatic item_t\ttp_items[] = {\n\t{\tit_quad,\t&tp_name_quad,\t\"progs/quaddama.mdl\",\n\t\t{0, 0, 24},\t25, 60\n\t},\n\t{\tit_pent,\t&tp_name_pent,\t\"progs/invulner.mdl\",\n\t\t{0, 0, 22},\t25, 5*60\n\t},\n\t{\tit_ring,\t&tp_name_ring,\t\"progs/invisibl.mdl\",\n\t\t{0, 0, 16},\t12, 5*60\n\t},\n\t{\tit_suit,\t&tp_name_suit,\t\"progs/suit.mdl\",\n\t\t{0, 0, 24}, 20, 60\n\t},\n\t{\tit_lg,\t\t&tp_name_lg,\t\"progs/g_light.mdl\",\n\t\t{0, 0, 30},\t20, 30\n\t},\n\t{\tit_rl,\t\t&tp_name_rl,\t\"progs/g_rock2.mdl\",\n\t\t{0, 0, 30},\t20, 30\n\t},\n\t{\tit_gl,\t\t&tp_name_gl,\t\"progs/g_rock.mdl\",\n\t\t{0, 0, 30},\t20, 30\n\t},\n\t{\tit_sng,\t\t&tp_name_sng,\t\"progs/g_nail2.mdl\",\n\t\t{0, 0, 30},\t20, 30\n\t},\n\t{\tit_ng,\t\t&tp_name_ng,\t\"progs/g_nail.mdl\",\n\t\t{0, 0, 30},\t20, 30\n\t},\n\t{\tit_ssg,\t\t&tp_name_ssg,\t\"progs/g_shot.mdl\",\n\t\t{0, 0, 30},\t20, 30\n\t},\n\t{\tit_cells,\t&tp_name_cells,\t\"maps/b_batt0.bsp\",\n\t\t{16, 16, 24},\t18, 30\n\t},\n\t{\tit_cells,\t&tp_name_cells,\t\"maps/b_batt1.bsp\",\n\t\t{16, 16, 24},\t18, 30\n\t},\n\t{\tit_rockets,\t&tp_name_rockets,\"maps/b_rock0.bsp\",\n\t\t{8, 8, 20},\t18, 30\n\t},\n\t{\tit_rockets,\t&tp_name_rockets,\"maps/b_rock1.bsp\",\n\t\t{16, 8, 20},\t18, 30\n\t},\n\t{\tit_nails,\t&tp_name_nails,\t\"maps/b_nail0.bsp\",\n\t\t{16, 16, 10},\t18, 30\n\t},\n\t{\tit_nails,\t&tp_name_nails,\t\"maps/b_nail1.bsp\",\n\t\t{16, 16, 10},\t18, 30\n\t},\n\t{\tit_shells,\t&tp_name_shells,\"maps/b_shell0.bsp\",\n\t\t{16, 16, 10},\t18, 30\n\t},\n\t{\tit_shells,\t&tp_name_shells,\"maps/b_shell1.bsp\",\n\t\t{16, 16, 10},\t18, 30\n\t},\n\t{\tit_health,\t&tp_name_health,\"maps/b_bh10.bsp\",\n\t\t{16, 16, 8},\t18, 20\n\t},\n\t{\tit_health,\t&tp_name_health,\"maps/b_bh25.bsp\",\n\t\t{16, 16, 8},\t18, 20\n\t},\n\t{\tit_mh,\t\t&tp_name_mh,\t\"maps/b_bh100.bsp\",\n\t\t{16, 16, 14},\t20, 0\n\t},\n\t{\tit_pack,\t&tp_name_backpack, \"progs/backpack.mdl\",\n\t\t{0, 0, 18},\t18, 0\n\t},\n\t{\tit_flag,\t&tp_name_flag,\t\"progs/tf_flag.mdl\",\n\t\t{0, 0, 14},\t25, 0\n\t},\n\t{\tit_flag,\t&tp_name_flag,\t\"progs/tf_stan.mdl\",\n\t\t{0, 0, 45},\t40, 0\n\t},\n\t{\tit_ra|it_ya|it_ga, &tp_name_armor,\t\"progs/armor.mdl\",\t//generic armour, used only when the skin number if invalid for the other types.\n\t\t{0, 0, 24},\t22, 0\n\t},\n\t{\tit_ga,\t\t&tp_name_ga,\t\t\"progs/armor.mdl\",\n\t\t{0, 0, 24},\t22, 20\n\t},\n\t{\tit_ya,\t\t&tp_name_ya,\t\t\"progs/armor.mdl\",\n\t\t{0, 0, 24},\t22, 20\n\t},\n\t{\tit_ra,\t\t&tp_name_ra,\t\t\"progs/armor.mdl\",\n\t\t{0, 0, 24},\t22, 20\n\t},\n\t{\tit_flag,\t&tp_name_flag,\t\"progs/w_g_key.mdl\",\n\t\t{0, 0, 20},\t18, 0\n\t},\n\t{\tit_flag,\t&tp_name_flag,\t\"progs/w_s_key.mdl\",\n\t\t{0, 0, 20},\t18, 0\n\t},\n\t{\tit_flag,\t&tp_name_flag,\t\"progs/m_g_key.mdl\",\n\t\t{0, 0, 20},\t18, 0\n\t},\n\t{\tit_flag,\t&tp_name_flag,\t\"progs/m_s_key.mdl\",\n\t\t{0, 0, 20},\t18, 0\n\t},\n\t{\tit_flag,\t&tp_name_flag,\t\"progs/b_s_key.mdl\",\n\t\t{0, 0, 20},\t18, 0\n\t},\n\t{\tit_flag,\t&tp_name_flag,\t\"progs/b_g_key.mdl\",\n\t\t{0, 0, 20},\t18, 0\n\t},\n\t{\tit_flag,\t&tp_name_flag,\t\"progs/flag.mdl\",\n\t\t{0, 0, 14},\t25, 0\n\t},\n\t{\tit_runes,\t&tp_name_rune_1,\t\"progs/end1.mdl\",\n\t\t{0, 0, 20},\t18, 0\n\t},\n\t{\tit_runes,\t&tp_name_rune_2,\t\"progs/end2.mdl\",\n\t\t{0, 0, 20},\t18, 0\n\t},\n\t{\tit_runes,\t&tp_name_rune_3,\t\"progs/end3.mdl\",\n\t\t{0, 0, 20},\t18, 0\n\t},\n\t{\tit_runes,\t&tp_name_rune_4,\t\"progs/end4.mdl\",\n\t\t{0, 0, 20},\t18, 0\n\t},\n\t{\tit_sentry, &tp_name_sentry, \"progs/turrgun.mdl\",\n\t\t{0, 0, 23},\t25, 0\n\t},\n\t{\tit_disp, &tp_name_disp,\t\"progs/disp.mdl\",\n\t\t{0, 0, 24},\t25, 0\n\t}\n};\n\n#define NUMITEMS (sizeof(tp_items) / sizeof(tp_items[0]))\n\nstatic item_t\t*model2item[MAX_PRECACHE_MODELS];\n\nstatic void TP_FindModelNumbers (void)\n{\n\tint\t\ti, j;\n\tchar\t*s;\n\titem_t\t*item;\n\n\tfor (i=0 ; i<MAX_PRECACHE_MODELS ; i++) {\n\t\tmodel2item[i] = NULL;\n\t\ts = cl.model_name[i];\n\t\tif (!s)\n\t\t\tcontinue;\n\t\tfor (j=0, item=tp_items ; j<NUMITEMS ; j++, item++)\n\t\t\tif (!strcmp(s, item->modelname))\n\t\t\t{\n\t\t\t\tmodel2item[i] = item;\n\t\t\t\tbreak;\n\t\t\t}\n\t}\n}\n\n// on success, result is non-zero\n// on failure, result is zero\n// for armors, returns skinnum+1 on success\nstatic int FindNearestItem (vec3_t org, int flags, item_t **pitem)\n{\n\tinframe_t\t\t*frame;\n\tpacket_entities_t\t*pak;\n\tentity_state_t\t\t*ent;\n\tint\ti = 0, bestidx = 0, bestskin = 0;\n\tfloat bestdist = 0.0, dist = 0.0;\n\tvec3_t\tv;\n\titem_t\t*item;\n\tentity_state_t *baseline;\n\n\t// look in previous frame\n\tframe = &cl.inframes[cl.oldvalidsequence&UPDATE_MASK];\n\tpak = &frame->packet_entities;\n\tbestdist = 100.0f;\n\tbestidx = 0;\n\t*pitem = NULL;\n\tfor (i=0,ent=pak->entities ; i<pak->num_entities ; i++,ent++)\n\t{\n\t\titem = model2item[ent->modelindex];\n\t\tif (!item)\n\t\t\tcontinue;\n\t\tif ( ! (item->itemflag & flags) )\n\t\t\tcontinue;\n\n\t\tVectorCopy(ent->origin, v);\n\t\tVectorSubtract (v, org, v);\n\t\tVectorAdd (v, item->offset, v);\n\t\tdist = VectorLength (v);\n//\t\tCom_Printf (\"%s %f\\n\", item->modelname, dist);\n\n\t\tif (dist <= bestdist) {\n\t\t\tbestdist = dist;\n\t\t\tbestidx = ent->number;\n\t\t\tbestskin = ent->skinnum;\n\t\t\t*pitem = item;\n\t\t}\n\t}\n\n\tif (!bestidx)\n\tfor (i=1; i<cl_baselines_count ; i++)\n\t{\n\t\tbaseline = &cl_baselines[i];\n\t\titem = model2item[baseline->modelindex];\n\t\tif (!item)\n\t\t\tcontinue;\n\t\tif ( ! (item->itemflag & flags) )\n\t\t\tcontinue;\n\n\t\tVectorCopy(baseline->origin, v);\n\t\tVectorSubtract (v, org, v);\n\t\tVectorAdd (v, item->offset, v);\n\t\tdist = VectorLength (v);\n//\t\tCom_Printf (\"%s %f\\n\", item->modelname, dist);\n\n\t\tif (dist <= bestdist) {\n\t\t\tbestdist = dist;\n\t\t\tbestidx = i;\n\t\t\tbestskin = baseline->skinnum;\n\t\t\t*pitem = item;\n\t\t}\n\t}\n\n\tif (bestidx && (*pitem)->itemflag == it_armor)\n\t\tif (bestskin >= 0 && bestskin <= 3)\n\t\t\t*pitem += bestskin + 1;\n\n\treturn bestidx;\n}\n\nstatic int CountTeammates (void)\n{\n\tint\ti, count;\n\tplayer_info_t\t*player;\n\tchar\t*myteam;\n\n\tif (tp_forceTriggers.ival)\n\t\treturn 1;\n\n\tif (!cl.teamplay)\n\t\treturn 0;\n\n\tcount = 0;\n\tmyteam = cl.players[cl.playerview[SP].playernum].team;\n\tfor (i=0, player=cl.players; i < cl.allocated_client_slots ; i++, player++) {\n\t\tif (player->name[0] && !player->spectator && (i != cl.playerview[SP].playernum)\n\t\t\t\t\t\t\t\t\t&& !strcmp(player->team, myteam))\n\t\t\tcount++;\n\t}\n\n\treturn count;\n}\n\nstatic qboolean CheckTrigger (void)\n{\n\tint\ti, count;\n\tplayer_info_t *player;\n\tchar *myteam;\n\tplayerview_t *pv = &cl.playerview[SP];\n\n\tif (pv->spectator)\n\t\treturn false;\n\n\tif (tp_forceTriggers.ival)\n\t\treturn true;\n\n\tif (!cl.teamplay)\n\t\treturn false;\n\n\tcount = 0;\n\tmyteam = cl.players[pv->playernum].team;\n\tfor (i = 0, player= cl.players; i < cl.allocated_client_slots; i++, player++) {\n\t\tif (player->name[0] && !player->spectator && i != pv->playernum && !strcmp(player->team, myteam))\n\t\t\tcount++;\n\t}\n\n\treturn count;\n}\n\nstatic void ExecTookTrigger_ (char *s, int flag, vec3_t org)\n{\n\tint pkflags_dmm, tookflags_dmm;\n\n\tpkflags_dmm = pkflags;\n\ttookflags_dmm = tookflags;\n\n\tif (!cl.teamfortress && cl.deathmatch >= 1 && cl.deathmatch <= 4) {\n\t\tif (cl.deathmatch == 4) {\n\t\t\tpkflags_dmm &= ~(it_ammo|it_weapons);\n\t\t\ttookflags_dmm &= ~(it_ammo|it_weapons);\n\t\t}\n\t}\n\tif (!((pkflags_dmm|tookflags_dmm) & flag))\n\t\treturn;\n\n\tvars.tooktime = realtime;\n\tstrncpy (vars.tookname, s, sizeof(vars.tookname)-1);\n\tstrncpy (vars.tookloc, TP_LocationName (org), sizeof(vars.tookloc)-1);\n\n\tif ((tookflags_dmm & flag) && CheckTrigger())\n\t\tTP_ExecTrigger (\"f_took\", false);\n}\n\n/*\nvoid TP_GetSimpleItemTexture ()\n{\n}\n*/\n\nstatic void TP_ItemTaken (char *s, int flag, vec3_t org, int entnum, item_t *item, int seat)\n{\n\tif (seat == 0)\n\t\tExecTookTrigger_(s, flag, org);\n\n/*\tif (entnum < cl_baselines_count && cl_baselines[entnum].modelindex && item && item->respawntime && (cl.spectator || cls.demoplayback))\n\t{\n\t\tstruct itemtimer_s *timer = Z_Malloc(sizeof(*timer));\n\t\ttimer->next = cl.itemtimers;\n\t\tcl.itemtimers = timer;\n\t\ttimer->origin[0] = cl_baselines[entnum].origin[0] + item->offset[0];\n\t\ttimer->origin[1] = cl_baselines[entnum].origin[1] + item->offset[1];\n\t\ttimer->origin[2] = cl_baselines[entnum].origin[2];\n\t\ttimer->start = cl.time;\n\t\ttimer->duration = item->respawntime;\n\t\ttimer->end = cl.time + item->respawntime;\n\t\ttimer->radius = item->radius;\n\t\ttimer->entnum = entnum;\n\t}\n*/\n}\n\nvoid TP_ParsePlayerInfo(player_state_t *oldstate, player_state_t *state, player_info_t *info)\n{\n\tplayerview_t *pv = &cl.playerview[SP];\n//\tif (TP_NeedRefreshSkins())\n//\t{\n//\t\tif ((state->effects & (EF_BLUE|EF_RED) ) != (oldstate->effects & (EF_BLUE|EF_RED)))\n//\t\t\tTP_RefreshSkin(info - cl.players);\n//\t}\n\n\tif (!pv->spectator && cl.teamplay && strcmp(info->team, TP_PlayerTeam()))\n\t{\n\t\tqboolean eyes;\n\n\t\teyes = state->modelindex && cl.model_precache[state->modelindex] && !strcmp(cl.model_precache[state->modelindex]->name, \"progs/eyes.mdl\");\n\n\t\tif (state->effects & (EF_BLUE | EF_RED) || eyes)\n\t\t{\n\t\t\tvars.enemy_powerups = 0;\n\t\t\tvars.enemy_powerups_time = realtime;\n\n\t\t\tif (state->effects & EF_BLUE)\n\t\t\tvars.enemy_powerups |= TP_QUAD;\n\t\t\tif (state->effects & EF_RED)\n\t\t\t\tvars.enemy_powerups |= TP_PENT;\n\t\t\tif (eyes)\n\t\t\t\tvars.enemy_powerups |= TP_RING;\n\t\t}\n\t}\n\tif (!pv->spectator && !cl.teamfortress && info - cl.players == pv->playernum)\n\t{\n\t\tif ((state->effects & (QWEF_FLAG1|QWEF_FLAG2)) && !(oldstate->effects & (QWEF_FLAG1|QWEF_FLAG2)))\n\t\t{\n\t\t\tExecTookTrigger_ (tp_name_flag.string, it_flag, cl.inframes[cl.validsequence & UPDATE_MASK].playerstate[pv->playernum].origin);\n\t\t}\n\t\telse if (!(state->effects & (QWEF_FLAG1|QWEF_FLAG2)) && (oldstate->effects & (QWEF_FLAG1|QWEF_FLAG2)))\n\t\t{\n\t\t\tvars.lastdrop_time = realtime;\n\t\t\tstrcpy (vars.lastdroploc, Macro_Location());\n\t\t}\n\t}\n}\n\nvoid TP_CheckPickupSound (char *s, vec3_t org, int seat)\n{\n\tint entnum;\n\titem_t\t*item;\n\tplayerview_t *pv = &cl.playerview[seat];\n\t//if we're spectating, we don't want to do any actual triggers, so pretend it was someone else.\n\tif (pv->spectator)\n\t\tseat = -1;\n\tif (!s)\n\t\treturn;\n\n\t//FIXME: on items/itembk2.wav kill relevant item timer.\n\n\tif (!strcmp(s, \"items/damage.wav\"))\n\t{\n\t\tentnum = FindNearestItem (org, it_quad, &item);\n\t\tTP_ItemTaken (tp_name_quad.string, it_quad, org, entnum, item, seat);\n\t}\n\telse if (!strcmp(s, \"items/protect.wav\"))\n\t{\n\t\tentnum = FindNearestItem (org, it_pent, &item);\n\t\tTP_ItemTaken (tp_name_pent.string, it_pent, org, entnum, item, seat);\n\t}\n\telse if (!strcmp(s, \"items/inv1.wav\"))\n\t{\n\t\tentnum = FindNearestItem (org, it_ring, &item);\n\t\tTP_ItemTaken (tp_name_ring.string, it_ring, org, entnum, item, seat);\n\t}\n\telse if (!strcmp(s, \"items/suit.wav\"))\n\t{\n\t\tentnum = FindNearestItem (org, it_suit, &item);\n\t\tTP_ItemTaken (tp_name_suit.string, it_suit, org, entnum, item, seat);\n\t}\n\telse if (!strcmp(s, \"items/health1.wav\") ||\n\t\t\t !strcmp(s, \"items/r_item1.wav\"))\n\t{\n\t\tentnum = FindNearestItem (org, it_health, &item);\n\t\tTP_ItemTaken (tp_name_health.string, it_health, org, entnum, item, seat);\n\t}\n\telse if (!strcmp(s, \"items/r_item2.wav\"))\n\t{\n\t\tentnum = FindNearestItem (org, it_mh, &item);\n\t\tTP_ItemTaken (tp_name_mh.string, it_mh, org, entnum, item, seat);\n\t}\n\telse\n\t\tgoto more;\n\treturn;\n\nmore:\n\tif (!cl.validsequence || !cl.oldvalidsequence)\n\t\treturn;\n\n\t// weapons\n\tif (!strcmp(s, \"weapons/pkup.wav\"))\n\t{\n\t\tentnum = FindNearestItem (org, it_weapons, &item);\n\t\tif (item)\n\t\t\tTP_ItemTaken (item->cvar->string, item->itemflag, org, entnum, item, seat);\n\t\telse if (seat >= 0)\n\t\t{\n\t\t\t// we don't know what entity caused the sound, try to guess...\n\t\t\tif (vars.stat_framecounts[STAT_ITEMS] == cls.framecount)\n\t\t\t{\n\t\t\t\tif (vars.items & ~vars.olditems & IT_LIGHTNING)\n\t\t\t\t\tTP_ItemTaken (tp_name_lg.string, it_lg, pv->simorg, entnum, item, seat);\n\t\t\t\telse if (vars.items & ~vars.olditems & IT_ROCKET_LAUNCHER)\n\t\t\t\t\tTP_ItemTaken (tp_name_rl.string, it_rl, pv->simorg, entnum, item, seat);\n\t\t\t\telse if (vars.items & ~vars.olditems & IT_GRENADE_LAUNCHER)\n\t\t\t\t\tTP_ItemTaken (tp_name_gl.string, it_gl, pv->simorg, entnum, item, seat);\n\t\t\t\telse if (vars.items & ~vars.olditems & IT_SUPER_NAILGUN)\n\t\t\t\t\tTP_ItemTaken (tp_name_sng.string, it_sng, pv->simorg, entnum, item, seat);\n\t\t\t\telse if (vars.items & ~vars.olditems & IT_NAILGUN)\n\t\t\t\t\tTP_ItemTaken (tp_name_ng.string, it_ng, pv->simorg, entnum, item, seat);\n\t\t\t\telse if (vars.items & ~vars.olditems & IT_SUPER_SHOTGUN)\n\t\t\t\t\tTP_ItemTaken (tp_name_ssg.string, it_ssg, pv->simorg, entnum, item, seat);\n\t\t\t}\n\t\t}\n\t\treturn;\n\t}\n\n\t// armor\n\tif (!strcmp(s, \"items/armor1.wav\"))\n\t{\n\t\titem_t\t*item;\n\t\tqbool armor_updated;\n\n\t\tarmor_updated = (vars.stat_framecounts[STAT_ARMOR] == cls.framecount);\n\t\tentnum = FindNearestItem (org, it_armor, &item);\n\t\tif (item)\n\t\t\tTP_ItemTaken (item->cvar->string, item->itemflag, org, entnum, item, seat);\n\t\telse if (seat >= 0)\n\t\t{\n\t\t\tif (armor_updated && pv->stats[STAT_ARMOR] == 100)\n\t\t\t\tTP_ItemTaken (tp_name_ga.string, it_ga, org, entnum, NULL, seat);\n\t\t\telse if (armor_updated && pv->stats[STAT_ARMOR] == 150)\n\t\t\t\tTP_ItemTaken (tp_name_ya.string, it_ya, org, entnum, NULL, seat);\n\t\t\telse if (armor_updated && pv->stats[STAT_ARMOR] == 200)\n\t\t\t\tTP_ItemTaken (tp_name_ra.string, it_ra, org, entnum, NULL, seat);\n\t\t}\n\t\treturn;\n\t}\n\n\t// backpack or ammo\n\tif (!strcmp (s, \"weapons/lock4.wav\"))\n\t{\n\t\titem_t\t*item;\n\t\tentnum = FindNearestItem (org, it_ammo|it_pack|it_runes, &item);\n\t\tif (!item)\n\t\t\treturn;\n\t\tTP_ItemTaken (item->cvar->string, item->itemflag, org, entnum, item, seat);\n\t}\n}\n\nqboolean R_CullSphere (vec3_t org, float radius);\nstatic qboolean TP_IsItemVisible(item_vis_t *visitem)\t//BE sure that pmove.skipent is set correctly first\n{\n\tvec3_t end, v;\n\ttrace_t trace;\n\n\tif (visitem->dist <= visitem->radius)\n\t\treturn true;\n\n\tif (R_CullSphere(visitem->entorg, visitem->radius))\n\t\treturn false;\n\n\tVectorNegate (visitem->dir, v);\n\tVectorNormalize (v);\n\tVectorMA (visitem->entorg, visitem->radius, v, end);\n\ttrace = PM_TraceLine (visitem->vieworg, end);\n\tif (trace.fraction == 1)\n\t\treturn true;\n\n\tVectorMA (visitem->entorg, visitem->radius, visitem->right, end);\n\tVectorSubtract (visitem->vieworg, end, v);\n\tVectorNormalize (v);\n\tVectorMA (end, visitem->radius, v, end);\n\ttrace = PM_TraceLine (visitem->vieworg, end);\n\tif (trace.fraction == 1)\n\t\treturn true;\n\n\tVectorMA(visitem->entorg, -visitem->radius, visitem->right, end);\n\tVectorSubtract(visitem->vieworg, end, v);\n\tVectorNormalize(v);\n\tVectorMA(end, visitem->radius, v, end);\n\ttrace = PM_TraceLine(visitem->vieworg, end);\n\tif (trace.fraction == 1)\n\t\treturn true;\n\n\tVectorMA(visitem->entorg, visitem->radius, visitem->up, end);\n\tVectorSubtract(visitem->vieworg, end, v);\n\tVectorNormalize(v);\n\tVectorMA (end, visitem->radius, v, end);\n\ttrace = PM_TraceLine(visitem->vieworg, end);\n\tif (trace.fraction == 1)\n\t\treturn true;\n\n\t// use half the radius, otherwise it's possible to see through floor in some places\n\tVectorMA(visitem->entorg, -visitem->radius / 2, visitem->up, end);\n\tVectorSubtract(visitem->vieworg, end, v);\n\tVectorNormalize(v);\n\tVectorMA(end, visitem->radius, v, end);\n\ttrace = PM_TraceLine(visitem->vieworg, end);\n\tif (trace.fraction == 1)\n\t\treturn true;\n\n\treturn false;\n}\n\n//checks to see if a point at org the size of a player is visible or not\nqboolean TP_IsPlayerVisible(vec3_t origin)\n{\n\titem_vis_t visitem;\n\n\tVectorCopy(vpn, visitem.forward);\n\tVectorCopy(vright, visitem.right);\n\tVectorCopy(vup, visitem.up);\n\tVectorCopy(r_origin, visitem.vieworg);\n\n\tVectorCopy (origin, visitem.entorg);\n\tvisitem.entorg[2] += 27;\n\tVectorSubtract (visitem.entorg, visitem.vieworg, visitem.dir);\n\tvisitem.dist = DotProduct (visitem.dir, visitem.forward);\n\tvisitem.radius = 25;\n\n\treturn TP_IsItemVisible(&visitem);\n}\n\nstatic float TP_RankPoint(item_vis_t *visitem)\n{\n\tvec3_t v2, v3;\n\tfloat miss;\n\n\tif (visitem->dist < 10)\n\t\treturn -1;\n\n\tVectorScale (visitem->forward, visitem->dist, v2);\n\tVectorSubtract (v2, visitem->dir, v3);\n\tmiss = VectorLength (v3);\n\tif (miss > 300)\n\t\treturn -1;\n\tif (miss > visitem->dist * 1.7)\n\t\treturn -1;\t\t// over 60 degrees off\n\n\treturn (visitem->dist < 3000.0 / 8.0) ? miss * (visitem->dist * 8.0 * 0.0002f + 0.3f) : miss;\n}\n\nstatic char *Utils_TF_ColorToTeam_Failsafe(int color)\n{\n\tint i, j, teamcounts[8], numteamsseen = 0, best = -1;\n\tchar *teams[MAX_CLIENTS];\n\n\tmemset(teams, 0, sizeof(teams));\n\tmemset(teamcounts, 0, sizeof(teamcounts));\n\n\tfor (i = 0; i < cl.allocated_client_slots; i++)\n\t{\n\t\tif (!cl.players[i].name[0] || cl.players[i].spectator)\n\t\t\tcontinue;\n\t\tif (cl.players[i].rbottomcolor != color)\n\t\t\tcontinue;\n\t\tfor (j = 0; j < numteamsseen; j++)\n\t\t{\n\t\t\tif (!strcmp(cl.players[i].team, teams[j]))\n\t\t\t\tbreak;\n\t\t}\n\t\tif (j == numteamsseen)\n\t\t{\n\t\t\tteams[numteamsseen] = cl.players[i].team;\n\t\t\tteamcounts[numteamsseen] = 1;\n\t\t\tnumteamsseen++;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tteamcounts[j]++;\n\t\t}\n\t}\n\tfor (i = 0; i < numteamsseen; i++)\n\t{\n\t\tif (best == -1 || teamcounts[i] > teamcounts[best])\n\t\t\tbest = i;\n\t}\n\treturn (best == -1) ? \"\" : teams[best];\n}\n\nstatic char *Utils_TF_ColorToTeam(int color)\n{\n\tchar *s;\n\n\tswitch (color)\n\t{\n\t\tcase 13:\n\t\t\tif (*(s = InfoBuf_ValueForKey(&cl.serverinfo, \"team1\")) || *(s = InfoBuf_ValueForKey(&cl.serverinfo, \"t1\")))\n\t\t\t\treturn s;\n\t\t\tbreak;\n\t\tcase 4:\n\t\t\tif (*(s = InfoBuf_ValueForKey(&cl.serverinfo, \"team2\")) || *(s = InfoBuf_ValueForKey(&cl.serverinfo, \"t2\")))\n\t\t\t\treturn s;\n\t\t\tbreak;\n\t\tcase 12:\n\t\t\tif (*(s = InfoBuf_ValueForKey(&cl.serverinfo, \"team3\")) || *(s = InfoBuf_ValueForKey(&cl.serverinfo, \"t3\")))\n\t\t\t\treturn s;\n\t\t\tbreak;\n\t\tcase 11:\n\t\t\tif (*(s = InfoBuf_ValueForKey(&cl.serverinfo, \"team4\")) || *(s = InfoBuf_ValueForKey(&cl.serverinfo, \"t4\")))\n\t\t\t\treturn s;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\treturn \"\";\n\t}\n\treturn Utils_TF_ColorToTeam_Failsafe(color);\n}\n\nstatic void TP_FindPoint (void)\n{\n\tpacket_entities_t *pak;\n\tentity_state_t *ent;\n\tint\ti, j, pointflags_dmm;\n\tfloat best = -1, rank;\n\tentity_state_t *bestent=NULL;\n\tvec3_t ang;\n\titem_t *item, *bestitem = NULL;\n\tplayer_state_t *state, *beststate = NULL;\n\tplayer_info_t *info, *bestinfo = NULL;\n\titem_vis_t visitem;\n\textern cvar_t v_viewheight;\n\tint oldskip = pmove.skipent;\n\tplayerview_t *pv = &cl.playerview[SP];\n\n\tif (vars.pointtime == realtime)\n\t\treturn;\n\n\tif (!cl.validsequence)\n\t\tgoto nothing;\n\n\tpmove.skipent = pv->viewentity;\n\n\tang[0] = pv->viewangles[0]; ang[1] = pv->viewangles[1]; ang[2] = 0;\n\tAngleVectors (ang, visitem.forward, visitem.right, visitem.up);\n\tVectorCopy (pv->simorg, visitem.vieworg);\n\tvisitem.vieworg[2] += 22 + (v_viewheight.value ? bound (-7, v_viewheight.value, 4) : 0);\n\n\tpointflags_dmm = pointflags;\n\tif (!cl.teamfortress && cl.deathmatch >= 1 && cl.deathmatch <= 4)\n\t{\n\t\tif (cl.deathmatch == 4)\n\t\t\tpointflags_dmm &= ~it_ammo;\n\t\tif (cl.deathmatch != 1)\n\t\t\tpointflags_dmm &= ~it_weapons;\n\t}\n\n\tpak = &cl.inframes[cl.validsequence & UPDATE_MASK].packet_entities;\n\tfor (i = 0,ent = pak->entities; i < pak->num_entities; i++, ent++)\n\t{\n\t\titem = model2item[ent->modelindex];\n\t\tif (!item || !(item->itemflag & pointflags_dmm))\n\t\t\tcontinue;\n\t\t// special check for armors\n\t\tif (item->itemflag == (it_ra|it_ya|it_ga))\n\t\t{\n\t\t\titem += 1 + bound(0, ent->skinnum, 2);\n\t\t\tif (!(item->itemflag & pointflags_dmm))\n\t\t\t\tcontinue;\n\t\t}\n\n\t\tVectorAdd (ent->origin, item->offset, visitem.entorg);\n\t\tVectorSubtract (visitem.entorg, visitem.vieworg, visitem.dir);\n\t\tvisitem.dist = DotProduct (visitem.dir, visitem.forward);\n\t\tvisitem.radius = ent->effects & (EF_BLUE|EF_RED|EF_DIMLIGHT|EF_BRIGHTLIGHT) ? 200 : item->radius;\n\n\t\tif ((rank = TP_RankPoint(&visitem)) < 0)\n\t\t\tcontinue;\n\n\t\t// check if we can actually see the object\n\t\tif ((rank < best || best < 0) && TP_IsItemVisible(&visitem))\n\t\t{\n\t\t\tbest = rank;\n\t\t\tbestent = ent;\n\t\t\tbestitem = item;\n\t\t}\n\t}\n\n\tstate = cl.inframes[cl.parsecount & UPDATE_MASK].playerstate;\n\tinfo = cl.players;\n\tfor (j = 0; j < cl.allocated_client_slots; j++, info++, state++)\n\t{\n\t\tif (state->messagenum != cl.parsecount || j == pv->playernum || info->spectator)\n\t\t\tcontinue;\n\n\t\tif (\n\t\t\t(state->modelindex == cl_playerindex && ISDEAD(state->frame)) ||\n\t\t\tstate->modelindex == cl_h_playerindex\n\t\t)\n\t\t\tcontinue;\n\n\t\tVectorCopy (state->origin, visitem.entorg);\n\t\tvisitem.entorg[2] += 30;\n\t\tVectorSubtract (visitem.entorg, visitem.vieworg, visitem.dir);\n\t\tvisitem.dist = DotProduct (visitem.dir, visitem.forward);\n\t\tvisitem.radius = (state->effects & (EF_BLUE|EF_RED|EF_DIMLIGHT|EF_BRIGHTLIGHT) ) ? 200 : 27;\n\n\t\tif ((rank = TP_RankPoint(&visitem)) < 0)\n\t\t\tcontinue;\n\n\t\t// check if we can actually see the object\n\t\tif ((rank < best || best < 0) && TP_IsItemVisible(&visitem))\n\t\t{\n\t\t\tqboolean teammate, eyes = false;\n\n\t\t\teyes = state->modelindex && cl.model_precache[state->modelindex] && !strcmp(cl.model_precache[state->modelindex]->name, \"progs/eyes.mdl\");\n\t\t\tteammate = !!(cl.teamplay && !strcmp(info->team, TP_PlayerTeam()));\n\n\t\t\tif (eyes && !(pointflags_dmm & it_eyes))\n\t\t\t\tcontinue;\n\t\t\telse if (teammate && !(pointflags_dmm & it_teammate))\n\t\t\t\tcontinue;\n\t\t\telse if (!(pointflags_dmm & it_enemy))\n\t\t\t\tcontinue;\n\n\t\t\tbest = rank;\n\t\t\tbestinfo = info;\n\t\t\tbeststate = state;\n\t\t}\n\t}\n\n\tif (best >= 0 && bestinfo)\n\t{\n\t\tqboolean teammate, eyes;\n\t\tchar *name, buf[256] = {0};\n\n\t\teyes = beststate->modelindex && cl.model_precache[beststate->modelindex] && !strcmp(cl.model_precache[beststate->modelindex]->name, \"progs/eyes.mdl\");\n\t\tif (cl.teamfortress)\n\t\t{\n\t\t\tteammate = !strcmp(Utils_TF_ColorToTeam(bestinfo->rbottomcolor), TP_PlayerTeam());\n\n\t\t\tif (eyes)\n\t\t\t\tname = tp_name_eyes.string;\t\t//duck on 2night2\n\t\t\telse if (pv->spectator)\n\t\t\t\tname = bestinfo->name;\n\t\t\telse if (teammate)\n\t\t\t\tname = tp_name_teammate.string[0] ? tp_name_teammate.string : \"teammate\";\n\t\t\telse\n\t\t\t\tname = tp_name_enemy.string;\n\n\t\t\tif (!eyes)\n\t\t\t\tname = va(\"%s%s%s\", name, name[0] ? \" \" : \"\", Skin_To_TFSkin(InfoBuf_ValueForKey(&bestinfo->userinfo, \"skin\")));\n\t\t}\n\t\telse\n\t\t{\n\t\t\tteammate = !!(cl.teamplay && !strcmp(bestinfo->team, TP_PlayerTeam()));\n\n\t\t\tif (eyes)\n\t\t\t\tname = tp_name_eyes.string;\n\t\t\telse if (pv->spectator || (teammate && !tp_name_teammate.string[0]))\n\t\t\t\tname = bestinfo->name;\n\t\t\telse\n\t\t\t\tname = teammate ? tp_name_teammate.string : tp_name_enemy.string;\n\t\t}\n\t\tif (beststate->effects & EF_BLUE)\n\t\t\tQ_strncatz2(buf, tp_name_quaded.string);\n\t\tif (beststate->effects & EF_RED)\n\t\t\tQ_strncatz2(buf, va(\"%s%s\", buf[0] ? \" \" : \"\", tp_name_pented.string));\n\t\tQ_strncatz2(buf, va(\"%s%s\", buf[0] ? \" \" : \"\", name));\n\t\tQ_strncpyz (vars.pointname, buf, sizeof(vars.pointname));\n\t\tQ_strncpyz (vars.pointloc, TP_LocationName (beststate->origin), sizeof(vars.pointloc));\n\n\t\tvars.pointtype = (teammate && !eyes) ? POINT_TYPE_TEAMMATE : POINT_TYPE_ENEMY;\n\t}\n\telse if (best >= 0)\n\t{\n\t\tchar *p;\n\n\t\tif (!bestitem->cvar)\n\t\t{\n\t\t\tp = tp_name_nothing.string;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tp = bestitem->cvar->string;\n\t\t}\n\n\t\tvars.pointtype = (bestitem->itemflag & (it_powerups|it_flag)) ? POINT_TYPE_POWERUP : POINT_TYPE_ITEM;\n\t\tQ_strncpyz (vars.pointname, p, sizeof(vars.pointname));\n\t\tQ_strncpyz (vars.pointloc, TP_LocationName (bestent->origin), sizeof(vars.pointloc));\n\t}\n\telse\n\t{\nnothing:\n\t\tQ_strncpyz (vars.pointname, tp_name_nothing.string, sizeof(vars.pointname));\n\t\tvars.pointloc[0] = 0;\n\t\tvars.pointtype = POINT_TYPE_ITEM;\n\t}\n\tvars.pointtime = realtime;\n\tpmove.skipent = oldskip;\n}\n\nvoid TP_UpdateAutoStatus(void)\n{\n\tchar newstatusbuf[sizeof(vars.autoteamstatus)];\n\tchar *newstatus;\n\tint level;\n\tplayerview_t *pv = &cl.playerview[SP];\n\n\tif (vars.autoteamstatus_time > realtime || !*tp_autostatus.string)\n\t\treturn;\n\tvars.autoteamstatus_time = realtime + 3;\n\n\tlevel = tp_autostatus.restriction;\n\tnewstatus = Cmd_ExpandString(tp_autostatus.string, newstatusbuf, sizeof(newstatusbuf), &level, false, true, true);\n\tnewstatus = TP_ParseMacroString(newstatus);\n\n\tif (!strcmp(newstatus, vars.autoteamstatus))\n\t\treturn;\n\tif (!*vars.autoteamstatus && !vars.health)\n\t{\n\t\tif (cls.state != ca_active)\n\t\t\tstrcpy(vars.autoteamstatus, newstatus);\n\t\treturn;\t//don't start it with a death (stops spamming of locations when we originally connect, before spawning)\n\t}\n\tstrcpy(vars.autoteamstatus, newstatus);\n\n\tif (strchr(tp_autostatus.string, ';'))\n\t\treturn;\t//don't take risks\n\n\tif (tp_autostatus.latched_string)\n\t\treturn;\n\n\tif (pv->spectator)\t//don't spam as spectators, that's just silly\n\t\treturn;\n\tif (!cl.teamplay)\t//don't spam in deathmatch, that's just pointless\n\t\treturn;\n\n\t//the tp code will reexpand it as part of the say team\n\tCbuf_AddText(va(\"say_team $\\\\%s\\n\", tp_autostatus.string), RESTRICT_LOCAL);\n}\n\nvoid TP_StatChanged (int stat, int value)\n{\n\tplayerview_t *pv = &cl.playerview[SP];\n\tint\t\ti;\n\tif (stat == STAT_HEALTH)\n\t{\n\t\tif (value > 0)\n\t\t{\n\t\t\tif (vars.health <= 0)\n\t\t\t{\n\t\t\t\t// we just respawned\n\t\t\t\tvars.respawntrigger_time = realtime;\n\n\t\t\t\tif (!pv->spectator && CountTeammates())\n\t\t\t\t\tTP_ExecTrigger (\"f_respawn\", false);\n\t\t\t}\n\t\t}\n\t\telse if (vars.health > 0)\n\t\t{\t\t// We have just died\n\n\t\t\tvars.droppedweapon = pv->stats[STAT_ACTIVEWEAPON];\n\n\t\t\tvars.deathtrigger_time = realtime;\n\t\t\tstrcpy (vars.lastdeathloc, Macro_Location());\n\n\t\t\tCountNearbyPlayers(true);\n\t\t\tvars.last_numenemies = vars.numenemies;\n\t\t\tvars.last_numfriendlies = vars.numfriendlies;\n\n\t\t\tif (!pv->spectator && CountTeammates())\n\t\t\t{\n\t\t\t\tif (cl.teamfortress && (pv->stats[STAT_ITEMS] & (IT_KEY1|IT_KEY2))\n\t\t\t\t\t&& Cmd_AliasExist(\"f_flagdeath\", RESTRICT_LOCAL))\n\t\t\t\t\tTP_ExecTrigger (\"f_flagdeath\", false);\n\t\t\t\telse\n\t\t\t\t\tTP_ExecTrigger (\"f_death\", false);\n\t\t\t}\n\t\t}\n\t\tvars.health = value;\n\t}\n\telse if (stat == STAT_ITEMS)\n\t{\n\t\ti = value &~ vars.items;\n\n\t\tif (i & (IT_KEY1|IT_KEY2)) {\n\t\t\tif (cl.teamfortress && !pv->spectator)\n\t\t\t{\n\t\t\t\tExecTookTrigger_ (tp_name_flag.string, it_flag,\n\t\t\t\t\t\tcl.inframes[cl.validsequence&UPDATE_MASK].playerstate[pv->playernum].origin);\n\t\t\t}\n\t\t}\n\n\t\tif (!pv->spectator && cl.teamfortress && ~value & vars.items & (IT_KEY1|IT_KEY2))\n\t\t{\n\t\t\tvars.lastdrop_time = realtime;\n\t\t\tstrcpy (vars.lastdroploc, Macro_Location());\n\t\t}\n\n\t\tvars.olditems = vars.items;\n\t\tvars.items = value;\n\t}\n\telse if (stat == STAT_ACTIVEWEAPON)\n\t{\n\t\tif (pv->stats[STAT_ACTIVEWEAPON] != vars.activeweapon)\n\t\t\tTP_ExecTrigger (\"f_weaponchange\", false);\n\t\tvars.activeweapon = pv->stats[STAT_ACTIVEWEAPON];\n\t}\n\tvars.stat_framecounts[stat] = cls.framecount;\n\n\tTP_UpdateAutoStatus();\n}\n#endif\n\n\n/*\n======================\nTP_CheckSoundTrigger\n\nFind and execute sound triggers.\nA sound trigger must be terminated by either a CR or LF.\nReturns true if a sound was found and played\n======================\n*/\nqbool TP_CheckSoundTrigger (char *str)\n{\n\tint\t\ti, j;\n\tint\t\tstart, length;\n\tchar\tsoundname[MAX_QPATH];\n\n\tif (!*str)\n\t\treturn false;\n\n\tif (!tp_soundtrigger.string[0])\n\t\treturn false;\n\n\tfor (i=strlen(str)-1 ; i ; i--)\n\t{\n\t\tif (str[i] != 0x0A && str[i] != 0x0D)\n\t\t\tcontinue;\n\n\t\tfor (j = i-1 ; j >= 0 ; j--)\n\t\t{\n\t\t\t// quick check for chars that cannot be used\n\t\t\t// as sound triggers but might be part of a file name\n\t\t\tif ( isalpha((int)(unsigned char)str[j]) ||\n\t\t\t\t\t isdigit((int)(unsigned char)str[j]) )\n\t\t\t\tcontinue;\t// file name or chat\n\n\t\t\tif (strchr(tp_soundtrigger.string, str[j]))\n\t\t\t{\n\t\t\t\t// this might be a sound trigger\n\n\t\t\t\tstart = j + 1;\n\t\t\t\tlength = i - start;\n\n\t\t\t\tif (!length)\n\t\t\t\t\tbreak;\n\t\t\t\tif (length >= MAX_QPATH)\n\t\t\t\t\tbreak;\n\n\t\t\t\tstrlcpy (soundname, str + start, length + 1);\n\t\t\t\tif (strstr(soundname, \"..\"))\n\t\t\t\t\tbreak;\t// no thank you\n\n\t\t\t\t// clean up the message\n\t\t\t\tstrcpy (str + j, str + i);\n\n\t\t\t\tif (!S_HaveOutput())\n\t\t\t\t\treturn false;\n\n\t\t\t\tCOM_DefaultExtension (soundname, \".wav\", sizeof(soundname));\n\n\t\t\t\t// make sure we have it on disk (FIXME)\n\t\t\t\tif (!FS_FLocateFile (va(\"sound/%s\", soundname), FSLF_IFFOUND, NULL))\n\t\t\t\t\treturn false;\n\n\t\t\t\t// now play the sound\n\t\t\t\tS_LocalSound (soundname);\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\tif (str[j] <= ' ' || strchr(\"\\\"&'*,:;<>?\\\\|\\x7f\", str[j]))\n\t\t\t\tbreak;\t// we don't allow these in a file name\n\t\t}\n\t}\n\n\treturn false;\n}\n\n\n#define MAX_FILTERS 8\n#define MAX_FILTER_LENGTH 4\nstatic char filter_strings[MAX_FILTERS][MAX_FILTER_LENGTH+1];\nstatic int\tnum_filters = 0;\n\n/*\n======================\nTP_FilterMessage\n\nreturns false if the message shouldn't be printed\nmatching filters are stripped from the message\n======================\n*/\nqbool TP_FilterMessage (char *s)\n{\n\tint i, j, len, maxlen;\n\n\tif (!num_filters)\n\t\treturn true;\n\n\tlen = strlen (s);\n\tif (len < 2 || s[len-1] != '\\n' || s[len-2] == '#')\n\t\treturn true;\n\n\tmaxlen = MAX_FILTER_LENGTH + 1;\n\tfor (i=len-2 ; i >= 0 && maxlen > 0 ; i--, maxlen--) {\n\t\tif (s[i] == ' ')\n\t\t\treturn true;\n\t\tif (s[i] == '#')\n\t\t\tbreak;\n\t}\n\tif (i < 0 || !maxlen)\n\t\treturn true;\t// no filter at all\n\n\ts[len-1] = 0;\t// so that strcmp works properly\n\n\tfor (j=0 ; j<num_filters ; j++)\n\t\tif (!strcmp(s + i + 1, filter_strings[j]))\n\t\t{\n\t\t\t// strip the filter from message\n\t\t\tif (i && s[i-1] == ' ')\n\t\t\t{\t// there's a space just before the filter, remove it\n\t\t\t\t// so that soundtriggers like ^blah #att work\n\t\t\t\ts[i-1] = '\\n';\n\t\t\t\ts[i] = 0;\n\t\t\t} else {\n\t\t\t\ts[i] = '\\n';\n\t\t\t\ts[i+1] = 0;\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\n\ts[len-1] = '\\n';\n\treturn false;\t// this message is not for us, don't print it\n}\n\nstatic void TP_MsgFilter_f (void)\n{\n\tint c, i;\n\tchar *s;\n\n\tc = Cmd_Argc ();\n\tif (c == 1) {\n\t\tif (!num_filters) {\n\t\t\tCom_Printf (\"No filters defined\\n\");\n\t\t\treturn;\n\t\t}\n\t\tfor (i=0 ; i<num_filters ; i++)\n\t\t\tCom_Printf (\"%s#%s\", i ? \" \" : \"\", filter_strings[i]);\n\t\tCom_Printf (\"\\n\");\n\t\treturn;\n\t}\n\n\tif (c == 2 && (Cmd_Argv(1)[0] == 0 || !strcmp(Cmd_Argv(1), \"clear\"))) {\n\t\tnum_filters = 0;\n\t\treturn;\n\t}\n\n\tnum_filters = 0;\n\tfor (i=1 ; i < c ; i++) {\n\t\ts = Cmd_Argv(i);\n\t\tif (*s != '#') {\n\t\t\tCom_Printf (\"A filter must start with \\\"#\\\"\\n\");\n\t\t\treturn;\n\t\t}\n\t\tif (strchr(s+1, ' ')) {\n\t\t\tCom_Printf (\"A filter may not contain spaces\\n\");\n\t\t\treturn;\n\t\t}\n\t\tstrlcpy (filter_strings[num_filters], s+1, sizeof(filter_strings[0]));\n\t\tnum_filters++;\n\t\tif (num_filters >= countof(filter_strings))\n\t\t\tbreak;\n\t}\n}\n\nvoid TP_Init (void)\n{\n#define TEAMPLAYVARS\t\"Teamplay Variables\"\n\n\t//register all the TeamPlay cvars.\n#define TP_CVAR(name,def) Cvar_Register (&name,\tTEAMPLAYVARS);\n#define TP_CVARC(name,def,callback) Cvar_Register (&name, TEAMPLAYVARS);\n#define TP_CVARAC(name,def,name2,callback) Cvar_Register (&name, TEAMPLAYVARS);\n#define static\n\tTP_CVARS;\n#undef static\n#undef TP_CVAR\n#undef TP_CVARC\n#undef TP_CVARAC\n\n\tCmd_AddCommand (\"loadloc\", TP_LoadLocFile_f);\n\tCmd_AddCommand (\"filter\", TP_MsgFilter_f);\n\tCmd_AddCommand (\"msg_trigger\", TP_MsgTrigger_f);\n#ifdef QWSKINS\n\tCmd_AddCommand (\"colourise\", TP_Colourise_f);\t//uk\n\tCmd_AddCommand (\"colorize\", TP_Colourise_f);\t//us\n\t//Cmd_AddCommand (\"colorise\", TP_Colourise_f);\t//piss off both.\n#endif\n#ifdef QUAKESTATS\n\tCmd_AddCommand (\"tp_took\", TP_Took_f);\n\tCmd_AddCommand (\"tp_pickup\", TP_Pickup_f);\n\tCmd_AddCommand (\"tp_point\", TP_Point_f);\n#endif\n\n\tTP_InitMacros();\n}\n\n\n\n\n\nqboolean TP_SuppressMessage(char *buf) {\n\tchar *s;\n\tunsigned int seat;\n\n\tfor (s = buf; *s && *s != 0x7f; s++)\n\t\t;\n\n\tif (*s == 0x7f && *(s + 1) == '!')\t{\n\t\t*s++ = '\\n';\n\t\t*s++ = 0;\n\n\t\tif (!cls.demoplayback)\n\t\t\tfor (seat = 0; seat < cl.splitclients; seat++)\n\t\t\t\tif (!cl.playerview[seat].spectator && *s - 'A' == cl.playerview[seat].playernum)\n\t\t\t\t\treturn true;\n\t}\n\treturn false;\n}\n\nvoid CL_PrintChat(player_info_t *plr, char *msg, int plrflags);\n\nvoid CL_Say (qboolean team, char *extra)\n{\n\textern cvar_t cl_fakename;\n\tchar\ttext[2048], sendtext[2048], *s;\n\tplayerview_t *pv = &cl.playerview[CL_TargettedSplit(false)];\n\n\tif (Cmd_Argc() < 2)\n\t{\n\t\tif (team)\n\t\t\tCon_Printf (\"%s <text>: send a team message\\n\", Cmd_Argv(0));\n\t\treturn;\n\t}\n\n\tif (cls.state == ca_disconnected)\n\t{\n\t\tCon_TPrintf (\"Can't \\\"%s\\\", not connected\\n\", Cmd_Argv(0));\n\t\treturn;\n\t}\n\n\tsuppress = false;\n\n\ts = TP_ParseMacroString (Cmd_Args());\n\tQ_strncpyz (text, TP_ParseFunChars (s), sizeof(text));\n\n\tsendtext[0] = 0;\n\tif (team && !pv->spectator && cl_fakename.string[0] &&\n\t\t!strchr(s, '\\x0d') /* explicit $\\ in message overrides cl_fakename */)\n\t{\n\t\tchar buf[1024];\n\t\tint level = Cmd_ExecLevel;\n\t\tCmd_ExpandString (cl_fakename.string, buf, sizeof(buf), &level, false, true, true);\n\t\tstrcpy (buf, TP_ParseMacroString (buf));\n\t\tQ_snprintfz (sendtext, sizeof(sendtext), \"\\x0d%s: \", TP_ParseFunChars(buf));\n\t}\n\n\tstrlcat (sendtext, text, sizeof(sendtext));\n\tif (suppress)\n\t{\n\t\t//print it locally:\n\t\tchar *d;\n\t\tfor (s = sendtext, d = text; *s; s++, d++)\n\t\t{\n\t\t\tif (*s == '\\xff')\t//text that is hidden to us\n\t\t\t{\t//\n\t\t\t\ts++;\n\t\t\t\t*d++ = '^';\n\t\t\t\t*d++ = 's';\n\t\t\t\t*d++ = '^';\n\t\t\t\t*d++ = '&';\n\t\t\t\t*d++ = '4';\n\t\t\t\t*d++ = '0';\n\t\t\t\tif (*s == 'z')\n\t\t\t\t\t*d++ = 'x';\n\t\t\t\telse\n\t\t\t\t\t*d++ = (char)139;\n\n\t\t\t\t*d++ = '^';\n\t\t\t\t*d++ = 'r';\n\t\t\t\td--;\n\n\t\t\t\twhile(*s != '\\xff')\n\t\t\t\t{\n\t\t\t\t\tif (!*s)\n\t\t\t\t\t\tbreak;\n\t\t\t\t\ts++;\n\t\t\t\t}\n\t\t\t\tif (!*s)\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\telse\n\t\t\t\t*d = *s;\n\t\t}\n\t\t*d++ = '\\n';\n\t\t*d = '\\0';\n\n\t\t{\n\t\t\tint plrflags = 0;\n\t\t\tif (team)\n\t\t\t\tplrflags |= 2;\n\n\t\t\tCL_PrintChat(&cl.players[pv->playernum], text, plrflags);\n\t\t}\n\n\t\t//strip out the extra markup\n\t\tfor (s = sendtext, d = sendtext; *s; s++, d++)\n\t\t{\n\t\t\tif (*s == '\\xff')\t//text that is hidden to us\n\t\t\t{\t//\n\t\t\t\ts++;\n\t\t\t\tif (*s == 'z')\n\t\t\t\t\ts++;\n\t\t\t\twhile(*s != '\\xff')\n\t\t\t\t{\n\t\t\t\t\tif (!*s)\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t*d++ = *s++;\n\t\t\t\t}\n\t\t\t\tif (!*s)\n\t\t\t\t\tbreak;\n\t\t\t\td--;\n\t\t\t}\n\n\t\t\telse\n\t\t\t\t*d = *s;\n\t\t}\n\t\t*d = '\\0';\n\n\t\t//mark the message so that we ignore it when we get the echo.\n\t\tstrlcat (sendtext, va(\"\\x7f!%c\", 'A'+pv->playernum), sizeof(sendtext));\n\t}\n\n#ifdef Q2CLIENT\n\tif (cls.netchan.remote_address.prot == NP_KEXLAN && NET_GetConnectionCertificate(cls.sockets, &cls.netchan.remote_address, QCERT_LOBBYSENDCHAT, sendtext, strlen(sendtext))>0)\n\t\treturn;\n#endif\n\n#ifdef Q3CLIENT\n\tif (cls.protocol == CP_QUAKE3)\n\t\tq3->cl.SendClientCommand(\"%s %s%s\", team ? \"say_team\" : \"say\", extra?extra:\"\", sendtext);\n\telse\n#endif\n\t{\n\t\tint split = CL_TargettedSplit(true);\n\t\tif (split >= cl.splitclients)\n\t\t\treturn;\n\t\t//messagemode always adds quotes. the console command never did.\n\t\t//the server is expected to use Cmd_Args and to strip first+last chars if the first is a quote. this is annoying and clumsy for mods to parse.\n#ifdef HAVE_LEGACY\n\t\tif (!dpcompat_console.ival\n#ifdef NQPROT\n\t\t\t\t&& !cls.qex\n#endif\n\t\t\t\t)\n\t\t\tCL_SendSeatClientCommand(true, split, \"%s \\\"%s%s\\\"\", team ? \"say_team\" : \"say\", extra?extra:\"\", sendtext);\n\t\telse\n#endif\n\t\t\tCL_SendSeatClientCommand(true, split, \"%s %s%s\", team ? \"say_team\" : \"say\", extra?extra:\"\", sendtext);\n\t}\n}\n\n\nvoid CL_Say_f (void)\n{\n#ifndef CLIENTONLY\n\tif (isDedicated)\n\t\tSV_ConSay_f();\n\telse\n#endif\n\t\tCL_Say (false, NULL);\n}\n\nvoid CL_SayMe_f (void)\n{\n\tCL_Say (false, \"/me \");\n}\n\nvoid CL_SayTeam_f (void)\n{\n#ifdef QUAKESTATS\n\tvars.autoteamstatus_time = realtime + 3;\n#endif\n\tCL_Say (true, NULL);\n}\n\nqboolean TP_SoundTrigger(char *message)\t//if there is a trigger there, play it. Return true if we found one, stripping off the file (it's neater that way).\n{\n\tchar *strip;\n\tchar *lineend = NULL;\n\tchar soundname[128];\n\tint filter = 0;\n\n\tfor (strip = message+strlen(message)-1; *strip && strip >= message; strip--)\n\t{\n\t\tif (*strip == '#')\n\t\t\tfilter++;\n\t\tif (*strip == ':')\n\t\t\tbreak;\t//if someone says just one word, we can take any tidles in their name to be a voice command\n\t\tif (*strip == '\\n')\n\t\t\tlineend = strip;\n\t\telse if (*strip <= ' ')\n\t\t{\n\t\t\tif (filter == 0 || filter == 1)\t//allow one space in front of a filter.\n\t\t\t{\n\t\t\t\tfilter++;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t\telse if (*strip == '~')\n\t\t{\n\t\t\t//looks like a trigger, whoopie!\n\t\t\tif (lineend-strip > sizeof(soundname)-1)\n\t\t\t{\n\t\t\t\tCon_Printf(\"Sound trigger's file-name was too long\\n\");\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tQ_strncpyz(soundname, strip+1, lineend-strip);\n\t\t\tmemmove(strip, lineend, strlen(lineend)+1);\n\n\t\t\tCbuf_AddText(va(\"play %s\\n\", soundname), RESTRICT_LOCAL);\n\t\t\treturn true;\n\t\t}\n\t}\n\treturn false;\n}\n"
  },
  {
    "path": "engine/common/bothdefs.h",
    "content": "/*\nCopyright (C) 1996-1997 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n*/\n\n#ifndef __BOTHDEFS_H\n#define __BOTHDEFS_H\n\n// release version\n#define FTE_VER_MAJOR 1\n#define FTE_VER_MINOR 7\n\n#if defined(FTE_SDL3) && !defined(FTE_SDL)\t//sdl3 uses different include names, so we need two defines\n\t#define FTE_SDL\t\t//but I'm too lazy to constantly test both.\n#endif\n\n#if defined(__APPLE__) && defined(__MACH__)\n\t#define MACOSX\n#endif\n\n#if defined(__MINGW32_VERSION) || defined(__MINGW__) || defined(__MINGW32__) || defined(__MINGW64__)\n\t#define MINGW\n#endif\n#if !defined(MINGW) && defined(__GNUC__) && defined(_WIN32)\n\t#define MINGW\t//Erm, why is this happening?\n#endif\n\n#ifdef ANDROID\n\t#define NO_PNG\n\t#define NO_JPEG\n\t#define NO_OGG\n#endif\n\n#ifdef _XBOX\n\t#define NO_PNG\n\t#define NO_JPEG\n\t#define NO_OGG\n\t#define NO_ZLIB\n\t#define NOMEDIA\n\t#define NO_FREETYPE\n\t#define HAVE_PACKET\n#endif\n\n#ifndef MULTITHREAD\n\t#if !defined(_WIN32) || defined(FTE_SDL) //win32 is annoying\n\t\t#define NO_MULTITHREAD\n\t#endif\n#endif\n\n#ifdef FTE_TARGET_WEB\n\t//no Sys_LoadLibrary support, so we might as well kill this stuff off.\n\t#define NO_PNG\n\t#define NO_JPEG\n\t#define NO_OGG\n\t#ifndef NO_FREETYPE\n\t\t#define NO_FREETYPE\n\t#endif\n#endif\n\n#ifdef D3DQUAKE\n\t#define D3D9QUAKE\n\t//#define D3D11QUAKE\n\t#undef D3DQUAKE\n#endif\n\n#define STRINGIFY2(s) #s\n#define STRINGIFY(s) STRINGIFY2(s)\n\n#ifndef CONFIG_FILE_NAME\n\t#ifdef HAVE_CONFIG_H\n\t\t#define CONFIG_FILE_NAME config.h\n\t#elif defined(NOLEGACY)\n\t\t#undef NOLEGACY\n\t\t#define CONFIG_FILE_NAME config_nocompat.h\n\t#elif defined(MINIMAL)\n\t\t#define CONFIG_FILE_NAME config_minimal.h\n\t#else\n\t\t#define CONFIG_FILE_NAME config_fteqw.h\n\t#endif\n#endif\n\n#undef MULTITHREAD\n#define HEADLESSQUAKE\t//usable renderers are normally specified via the makefile, but HEADLESS is considered a feature rather than an actual renderer, so usually gets forgotten about...\n\n//yup, C89 allows this (doesn't like C's token concat though).\n#include STRINGIFY(CONFIG_FILE_NAME)\n\n\n#ifndef MSVCLIBSPATH\n\t#ifdef MSVCLIBPATH\n\t\t#define MSVCLIBSPATH STRINGIFY(MSVCLIBPATH)\n\t#elif _MSC_VER == 1200\n\t\t#define MSVCLIBSPATH \"../libs/vc6-libs/\"\n\t#else\n\t\t#define MSVCLIBSPATH \"../libs/\"\n\t#endif\n#endif\n\n#if defined(IMGTOOL) || defined(IQMTOOL)\n\t#undef WEBCLIENT\n\t#undef LOADERTHREAD\n#elif defined(MASTERONLY)\n\t#define SV_MASTER\n\t#undef SUBSERVERS\n\t#undef PLUGINS\n\t#undef HUFFNETWORK\n\t#undef SUPPORT_ICE\n\t#undef WEBCLIENT\n\t#undef MULTITHREAD\n\t#undef LOADERTHREAD\n\t#undef PACKAGEMANAGER\n\t#undef PACKAGE_PK3\n\t#undef PACKAGE_Q1PAK\n\t#undef PACKAGE_DOOMWAD\n\t#undef PACKAGE_VPK\n\t#undef PACKAGE_DZIP\n\t#undef AVAIL_XZDEC\n\t#undef AVAIL_GZDEC\n\t#undef SUBSERVERS\n\t#undef HAVE_LEGACY\n\t#undef IPLOG\n#else\n\t#if defined(SERVERONLY) && defined(CLIENTONLY)\n\t\t#undef CLIENTONLY\t//impossible build. assume the config had CLIENTONLY and they tried building a dedicated server\n\t#endif\n\t#ifndef WEBSVONLY\n\t\t#ifndef CLIENTONLY\n\t\t\t#define HAVE_SERVER\n\t\t#endif\n\t\t#ifndef SERVERONLY\n\t\t\t#define HAVE_CLIENT\n\t\t#endif\n\t#endif\n#endif\n#ifndef NOLEGACY\n\t#define HAVE_LEGACY\n#endif\n\n#ifndef HAVE_SERVER\n\t#undef MVD_RECORDING\n#endif\n\n//software rendering is just too glitchy, don't use it - unless its the only choice.\n#if defined(SWQUAKE) && !defined(_DEBUG) && !defined(__DJGPP__)\n\t#undef SWQUAKE\n#endif\n#if defined(USE_EGL) && !defined(GLQUAKE)\n\t#undef USE_EGL\n#endif\n#if defined(WAYLANDQUAKE) && !(defined(__linux__) && (defined(VKQUAKE) || (defined(GLQUAKE) && defined(USE_EGL))))\n\t#undef WAYLANDQUAKE\n#endif\n\n//include a file to update the various configurations for game-specific configs (hopefully just names)\n#ifdef BRANDING_INC\n\t#include STRINGIFY(BRANDING_INC)\n#endif\n#ifndef DISTRIBUTION\n\t#define DISTRIBUTION \"FTE\"\t//short name used to identify this engine. must be a single word\n#endif\n#ifndef DISTRIBUTIONLONG\n\t#define DISTRIBUTIONLONG \"Forethought Entertainment\"\t//effectively the 'company' name\n#endif\n#ifndef FULLENGINENAME\n\t#define FULLENGINENAME \"FTE QW\"\t//the posh name for the engine, note that 'Quake' is trademarked so we should not be using it here.\n#endif\n#ifndef ENGINEWEBSITE\n\t#define ENGINEWEBSITE \"^8https://^4fte^8.^4triptohell^8.^4info\"\t//url for program\n#endif\n\n#if !defined(_WIN32) || defined(WINRT) || defined(FTE_SDL)\n\t#undef HAVE_SPEECHTOTEXT\n\t#undef AVAIL_MP3_ACM\n\t#undef AVAIL_DSOUND\n\t#undef AVAIL_XAUDIO2\n\t#undef AVAIL_WASAPI\n#endif\n\n//#if !(defined(__linux__) || defined(__CYGWIN__)) || defined(ANDROID)\n//\t#undef HAVE_GNUTLS\n//#endif\n#if !defined(_WIN32) || (defined(_MSC_VER) && (_MSC_VER < 1300))\n\t#undef HAVE_WINSSPI\n#endif\n//subservers only has code for win32 threads and linux\n#if !(defined(FTE_SDL3) ||\\\n\t  (defined(_WIN32) && !defined(FTE_SDL) && !defined(WINRT)) ||\\\n\t  (defined(__linux__) && !defined(ANDROID) && !defined(FTE_SDL))\\\n\t)\n\t#undef SUBSERVERS\n#endif\n\n#ifndef HAVE_MIXER\n\t//disable various sound drivers if we can't use them anyway.\n\t#undef AVAIL_DSOUND\n\t#undef AVAIL_XAUDIO2\n\t#undef AVAIL_WASAPI\n\n\t#undef AUDIO_ALSA\n\t#undef AUDIO_PULSE\n#endif\n\n\n#ifdef NOMEDIA\n\t#undef HAVE_CDPLAYER\t\t//includes cd playback. actual cds. faketracks are supported regardless.\n\t#undef HAVE_JUKEBOX\t\t\t//includes built-in jukebox crap\n\t#undef HAVE_MEDIA_DECODER\t//can play cin/roq, more with plugins\n\t#undef HAVE_MEDIA_ENCODER\t//capture/capturedemo work.\n\t#undef AVAIL_MP3_ACM\t\t//microsoft's Audio Compression Manager api\n\t#undef HAVE_SPEECHTOTEXT\t//windows speech-to-text thing\n#endif\n\n#if defined(_XBOX)\n\t#define D3D8QUAKE\n\t#undef HAVE_TCP\t\t//FIXME\n\t#undef HAVE_PACKET\t//FIXME\n\t#undef SUPPORT_ICE\t//screw that\n\t#undef PLUGINS\t\t//would need LoadLibrary working properly.\n\n\t#undef AVAIL_DINPUT\t//xbox apparently only really does controllers.\n\t#undef AVAIL_DSOUND\t//FIXME\n\t#undef TEXTEDITOR\t//its hard to edit text when you have just a controller (and no onscreen keyboard)\n\t#undef RAGDOLL\t\t//needs a proper physics engine\n\t#undef AVAIL_MP3_ACM\t\t//api not supported\n\t#undef AVAIL_OPENAL\n\t#undef HAVE_SPEECHTOTEXT\t//api not supported\n\t#undef MULTITHREAD\t\t\t//no CreateThread stuff.\n\t#undef SUBSERVERS\t\t\t//single-process.\n\t#undef VOICECHAT\n\t#undef TERRAIN\n\t#undef Q2CLIENT\n\t#undef Q2SERVER\n\t#undef Q3CLIENT\n\t#undef Q3SERVER\n\t#undef HLCLIENT\n\t#undef HLSERVER\n\t#undef VM_Q1\n\t#undef VM_LUA\n\t#undef HALFLIFEMODELS\n\t#undef RUNTIMELIGHTING\n\t#undef HEXEN2\n\t#undef PACKAGE_DOOMWAD\t\n\t#undef MAP_PROC\t\n\t#undef Q1BSPS\n\t#undef Q2BSPS\n\t#undef Q3BSPS\n\t#undef RFBSPS\n\t#undef FTPSERVER\t\t//ftp server\n\t#undef WEBCLIENT\t\t//http client.\n\t#undef FTPCLIENT\t\t//ftp client.\n#endif\n\n#ifdef __DJGPP__\n\t//no bsd sockets library.\n\t#undef HAVE_TCP\n\t#undef HAVE_PACKET\n\t#undef SUPPORT_ICE\n\t//too lazy to deal with no dlopen\n\t#undef PLUGINS\n\t#undef Q2SERVER\n\t#undef Q3SERVER\n\t#undef Q2CLIENT\t//fixme...\n\t#undef Q3CLIENT\t//might as well.\n\t//too lazy to write the code to boot up more cores. dosbox would probably hate it so why bother.\n\t#undef MULTITHREAD\n\t//too lazy to deal with various libraries\n\t#undef VOICECHAT\n\t#undef AVAIL_JPEGLIB\n\t#undef AVAIL_PNGLIB\n\t#undef AVAIL_OGGVORBIS\n#endif\n\n#ifdef FTE_TARGET_WEB\n\t//sandboxing means some stuff CANNOT work...\n\t#undef HAVE_TCP\t\t//websockets are not real tcp.\n\t#undef HAVE_PACKET\t//no udp support\n\n\t//try to trim the fat\n//\t#undef VOICECHAT\t//too lazy to compile opus\n\t#undef HLCLIENT\t\t//dlls...\n\t#undef HLSERVER\t\t//dlls...\n//\t#undef CL_MASTER\t//bah. use the site to specify the servers.\n\t#undef SV_MASTER\t//yeah, because that makes sense in a browser\n\t#undef RAGDOLL\t\t//no ode\n\t#undef TCPCONNECT\t//err...\n\t#undef IRCCONNECT\t//not happening\n\t#if !defined(USE_INTERNAL_BULLET) && !defined(USE_INTERNAL_ODE) && !defined(MODELFMT_GLTF) && !defined(STATIC_EZHUD) && !defined(STATIC_OPENSSL) && !defined(STATIC_Q3)\n\t\t#undef PLUGINS\t\t//pointless\n\t#endif\n\t#undef MAP_PROC\t\t//meh\n//\t#undef HALFLIFEMODELS\t//blurgh\n\t#undef SUPPORT_ICE\t//requires udp, so not usable. webrtc could be used instead, but that logic is out of our hands.\n//\t#undef HAVE_MIXER\t//depend upon openal instead.\n\n\t//extra features stripped to try to reduce memory footprints\n\t#undef RUNTIMELIGHTING\t//too slow anyway (kinda needs threads)\n\t#undef Q2SERVER\t//requires a dll anyway.\n//\t#undef Q2CLIENT //match Q2SERVER (networking is a pain)\n//\t#undef Q3CLIENT\t//no bots, and networking is a pain\n//\t#undef Q3SERVER //match Q3CLIENT\n//\t#undef Q2BSPS\t//emscripten can't cope with bss, leading to increased download time. too lazy to fix.\n//\t#undef Q3BSPS\t//emscripten can't cope with bss, leading to increased download time. too lazy to fix.\n//\t#undef TERRAIN\n//\t#undef PSET_SCRIPT\t//bss+size\n\t#define GLSLONLY\t//pointless having the junk\n\t#define GLESONLY\t//should reduce the conditions a little\n\t#ifndef R_MAX_RECURSE\n\t\t#define R_MAX_RECURSE 2 //less bss\n\t#endif\n//\t#undef RTLIGHTS\n\t#undef HEADLESSQUAKE\n\t#ifndef NO_FREETYPE\n\t#define NO_FREETYPE\n\t#endif\n#endif\n#ifdef WINRT\n\t//microsoft do not support winsock any more.\n\t#undef HAVE_TCP\n\t#undef HAVE_PACKET\n\n\t#undef TCPCONNECT\t//err...\n\t#undef IRCCONNECT\t//not happening\n\t#undef AVAIL_DSOUND\t//yeah, good luck there\n\t#undef AVAIL_DINPUT\t//nope, not supported.\n\t#undef SV_MASTER\t//no socket interface\n\t#undef CL_MASTER\t//no socket interface\n\t#undef MULTITHREAD\n\t#undef HEADLESSQUAKE\n#endif\n#ifdef ANDROID\n\t#define GLESONLY\t//should reduce the conditions a little\n//\t#undef HEADLESSQUAKE\n\t#ifndef NO_FREETYPE\n\t\t#define NO_FREETYPE\n\t#endif\n\t#define NO_OPENAL\n#endif\n#if (defined(_MSC_VER) && (_MSC_VER < 1500)) || defined(FTE_SDL)\n\t#undef AVAIL_WASAPI\t//wasapi is available in the vista sdk, while that's compatible with earlier versions, its not really expected until 2008\n#endif\n\n#if !defined(HAVE_SERVER) && !defined(SV_MASTER)\n\t#undef HAVE_HTTPSV\n#endif\n\n#ifdef NO_MULTITHREAD\n\t#undef MULTITHREAD\n#endif\n#ifndef MULTITHREAD\n\t//database code requires threads to do stuff async.\n\t#undef USE_SQLITE\n\t#undef USE_MYSQL\n\t#undef AUDIO_PULSE\n#endif\n#ifdef NO_LIBRARIES //catch-all...\n#define NO_DIRECTX\n\t#define NO_PNG\n\t#define NO_JPEG\n\t#define NO_ZLIB\n\t#define NO_OGG\n\t#define NO_FREETYPE\n#endif\n#ifdef NO_OPENAL\n\t#undef AVAIL_OPENAL\n#endif\n#ifdef NO_PNG\n\t#undef AVAIL_PNGLIB\n#endif\n#ifdef NO_JPEG\n\t#undef AVAIL_JPEGLIB\n#endif\n#ifdef NO_OGG\n\t#undef AVAIL_OGGVORBIS\n#endif\n#ifdef NO_FREETYPE\n\t#undef AVAIL_FREETYPE\n#endif\n#ifdef NO_ZLIB\n\t#undef AVAIL_ZLIB\n\t#undef AVAIL_PNGLIB\n\t#undef AVAIL_XZDEC\n\t#undef AVAIL_GZDEC\n#endif\n#ifdef NO_GNUTLS\n\t#undef HAVE_GNUTLS\n#endif\n#ifdef NO_WINSSPI\n\t#undef HAVE_WINSSPI\n#endif\n#ifdef NO_OPENGL\n\t#undef GLQUAKE\n\t#undef USE_EGL\n#endif\n\n#if (defined(HAVE_GNUTLS) || defined(HAVE_WINSSPI) || defined(PLUGINS)) && !defined(FTE_TARGET_WEB)\n\t#define HAVE_SSL\n#endif\n#if (defined(HAVE_GNUTLS) || defined(HAVE_WINSSPI) || defined(PLUGINS)) && !defined(FTE_TARGET_WEB)\n\t//FIXME: HAVE_WINSSPI does not work as a server.\n\t//FIXME: advertising dtls without a valid certificate will probably bug out if a client tries to auto-upgrade.\n\t//FIXME: we don't cache server certs\n\t#define HAVE_DTLS\n#endif\n\n#if defined(USE_SQLITE) || defined(USE_MYSQL)\n\t#define SQL\n#endif\n\n#if defined(AVAIL_GZDEC) && (!defined(AVAIL_ZLIB) || defined(NO_ZLIB))\n\t//gzip needs zlib to work (pk3s can still contain non-compressed files)\n\t#undef AVAIL_GZDEC\n#endif\n\n#if defined(RFBSPS) && !defined(Q3BSPS)\n\t#define Q3BSPS\t//rbsp might as well depend upon q3bsp - its the same thing but with more lightstyles (support for which can bog down the renderer a little).\n#endif\n\n#if defined(QWOVERQ3) && !defined(Q3SERVER)\n\t#undef QWOVERQ3\n#endif\n\n#if !defined(NQPROT) || defined(SERVERONLY) || !defined(AVAIL_ZLIB) || defined(DYNAMIC_ZLIB)\n\t#undef PACKAGE_DZIP\n#endif\n\n#if (defined(NOLOADERTHREAD) || !defined(MULTITHREAD)) && defined(LOADERTHREAD)\n\t#undef LOADERTHREAD\n#endif\n\n#ifndef _WIN32\n\t#undef QTERM\t//not supported - FIXME: move to native plugin\n#endif\n\n#if defined(Q3BSPS) && !defined(Q2BSPS)\n//\t#define Q2BSPS\t//FIXME: silently enable that as a dependancy, for now\n#endif\n\n#if (defined(Q2CLIENT) || defined(Q2SERVER))\n\t#ifndef Q2BSPS\n\t\t#error \"Q2 game support without Q2BSP support. doesn't make sense\"\n\t#endif\n\t#if !defined(MD2MODELS) || !defined(SP2MODELS)\n\t\t#error \"Q2 game support without full Q2 model support. doesn't make sense\"\n\t#endif\n#endif\n\n#ifndef AVAIL_ZLIB\n\t#undef SUPPORT_ICE\t//depends upon zlib's crc32 for fingerprinting. I cba writing my own.\n#endif\n\n#ifndef HAVE_TCP\n\t#undef TCPCONNECT\n\t#undef IRCCONNECT\n\t#undef FTPSERVER\t\t//ftp server\n\t#undef FTPCLIENT\t\t//ftp client.\n\t#if !defined(FTE_TARGET_WEB)\n\t\t#undef WEBCLIENT\n\t#endif\n#endif\n#ifndef HAVE_PACKET\n\t#undef SV_MASTER\n\t#ifndef FTE_TARGET_WEB\n\t\t#undef CL_MASTER\t//can use websockets to get a list of usable ws:// or rtc:// servers\n\t#endif\n\t#undef SUPPORT_ICE\t//webrtc takes all control away from us, the implementation is completely different.\n#endif\n\n#ifdef SERVERONLY\t//remove options that don't make sense on only a server\n\t#undef Q2CLIENT\n\t#undef Q3CLIENT\n\t#undef HLCLIENT\n\t#undef VM_UI\n\t#undef VM_CG\n\t#undef TEXTEDITOR\n\t#undef RUNTIMELIGHTING\n\n\t#undef PSET_SCRIPT\n\t#undef PSET_CLASSIC\n\t#undef PSET_DARKPLACES\n#endif\n#ifdef CLIENTONLY\t//remove optional server components that make no sence on a client only build.\n\t#undef Q2SERVER\n\t#undef Q3SERVER\n\t#undef HLSERVER\n\t#undef FTPSERVER\n\t#undef SUBSERVERS\n\t#undef VM_Q1\n\t#undef SQL\n#endif\n\n#ifndef PLUGINS\n\t#undef USE_INTERNAL_BULLET\n\t#undef USE_INTERNAL_ODE\n#endif\n\n\n#if (defined(CSQC_DAT) || !defined(CLIENTONLY)) && (defined(PLUGINS)||defined(USE_INTERNAL_BULLET)||defined(USE_INTERNAL_ODE))\t//use ode only if we have a constant world state, and the library is enbled in some form.\n\t#define USERBE\n#endif\n\n#if  defined(MD1MODELS) || defined(MD2MODELS) || defined(MD3MODELS)\n\t#define NONSKELETALMODELS\n#endif\n#if  defined(ZYMOTICMODELS) || defined(MD5MODELS) || defined(DPMMODELS) || defined(PSKMODELS) || defined(INTERQUAKEMODELS) \n\t#define SKELETALMODELS\t//defined if we have a skeletal model.\n#endif\n#if (defined(CSQC_DAT) || !defined(CLIENTONLY)) && defined(SKELETALMODELS)\n\t#define SKELETALOBJECTS\t//the skeletal objects API is only used if we actually have skeletal models, and gamecode that uses the builtins.\n#endif\n#if !defined(USERBE) || !defined(SKELETALMODELS)\n\t#undef RAGDOLL\t//not possible to ragdoll if we don't have certain other features.\n#endif\n\n#if !defined(RTLIGHTS)\n\t#undef MAP_PROC\t//doom3 maps kinda NEED rtlights to look decent\n#endif\n\n#if !defined(Q3BSPS)\n\t#undef Q3CLIENT //reconsider this (later)\n\t#undef Q3SERVER //reconsider this (later)\n#endif\n#if defined(DEBUG) || defined(_DEBUG)\n\t#undef NOQCDESCRIPTIONS\t//don't disable writing fteextensions.qc in debug builds, otherwise how would you ever build one? :o\n#endif\n\n\n#ifndef Q3CLIENT\n\t#undef VM_CG\t// :(\n\t#undef VM_UI\n#else\n\t#define VM_CG\n\t#define VM_UI\n#endif\n\n#if defined(VM_Q1) || defined(VM_UI) || defined(VM_CG) || defined(Q3SERVER)\n\t#define VM_ANY\n#endif\n\n#if (defined(HAVE_CLIENT) || defined(HAVE_SERVER)) && defined(WEBCLIENT) && defined(PACKAGEMANAGER)\n\t#define MANIFESTDOWNLOADS\n#endif\n\n#if (defined(D3D8QUAKE) || defined(D3D9QUAKE) || defined(D3D11QUAKE)) && !defined(D3DQUAKE)\n\t#define D3DQUAKE\t//shouldn't still matter\n#endif\n\n#define PROTOCOLEXTENSIONS\n\n#ifdef MINIMAL\n\t#define IFMINIMAL(x,y) x\n#else\n\t#define IFMINIMAL(x,y) y\n#endif\n#ifdef FTE_TARGET_WEB\n\t#define IFWEB(x,y) x\n#else\n\t#define IFWEB(x,y) y\n#endif\n\n// defs common to client and server\n\n#ifndef PLATFORM\n\t#if defined(FTE_TARGET_WEB)\n\t\t#define PLATFORM\t\t\"Web\"\n\t\t#define ARCH_CPU_POSTFIX \"web\"\n\t\t#define ARCH_DL_POSTFIX \".wasm\"\n\t#elif defined(_WIN32_WCE)\n\t\t#define PLATFORM\t\t\"WinCE\"\n\t\t#define ARCH_DL_POSTFIX \".dll\"\n\t#elif defined(_WIN32)\n\t\t#if defined(WINRT)\n\t\t\t#define PLATFORM\t\"WinRT\"\t\t/*those poor poor souls. maybe just maybe I'll actually get the tools for a port, its just a shame that I won't be able to release said port*/\n\t\t#elif defined(_XBOX)\n\t\t\t#define PLATFORM\t\"Xbox\"\n\t\t#else\n\t\t\t#define PLATFORM\t\"Win\"\n\t\t#endif\n\t\t#define ARCH_DL_POSTFIX \".dll\"\n\t#elif defined(_WIN16)\n\t\t#define PLATFORM\t\t\"Win16\"\n\t\t#define ARCH_DL_POSTFIX \".dll\"\n\t#elif defined(__CYGWIN__)\n\t\t#define PLATFORM\t\t\"Cygwin\"\t/*technically also windows*/\n\t\t#define ARCH_DL_POSTFIX \".dll\"\n\t#elif defined(ANDROID) || defined(__ANDROID__)\n\t\t#define PLATFORM\t\t\"Android\"\t/*technically also linux*/\n\t#elif defined(__linux__)\n\t\t#define PLATFORM\t\t\"Linux\"\n\t#elif defined(__APPLE__)\n\t\t#include \"TargetConditionals.h\"\n\t\t#if TARGET_IPHONE_SIMULATOR\n\t\t\t #define PLATFORM\t\"iOSSim\"\n\t\t#elif TARGET_OS_IPHONE\n\t\t\t#define PLATFORM\t\"iOS\"\n\t\t#elif TARGET_OS_MAC\n\t\t\t#define PLATFORM\t\"Mac\"\n\t\t#else\n\t\t\t#define PLATFORM\t\"Apple\"\n\t\t#endif\n\t#elif defined(__FreeBSD__)\n\t\t#define PLATFORM\t\"FreeBSD\"\n\t#elif defined(__OpenBSD__)\n\t\t#define PLATFORM\t\"OpenBSD\"\n\t#elif defined(__NetBSD__)\n\t\t#define PLATFORM\t\"NetBSD\"\n\t#elif defined(BSD)\n\t\t#define PLATFORM\t\"BSD\"\n\t#elif defined(__MORPHOS__)\n\t\t#define PLATFORM\t\"MorphOS\"\n\t#elif defined(__amigaos__)\n\t\t#define PLATFORM\t\"AmigaOS\"\n\t#elif defined(MACOSX)\n\t\t#define PLATFORM\t\"MacOS X\"\n\t#elif defined(__DOS__)\n\t\t#define PLATFORM\t\"Dos\"\n\t#else\n\t\t#define PLATFORM\t\"Unknown\"\n\t#endif\n#endif\n\n#ifndef ARCH_DL_POSTFIX\n\t#define ARCH_DL_POSTFIX \".so\"\n#endif\n\n#ifndef ARCH_CPU_POSTFIX\n\t#if defined(_M_AMD64) || defined(__amd64__) || defined(__x86_64__)\n\t\t#ifdef __ILP32__\n\t\t\t#define ARCH_CPU_POSTFIX \"x32\"\t//32bit pointers, with 16 registers.\n\t\t#else\n\t\t\t#ifdef _WIN32\n\t\t\t\t#define ARCH_CPU_POSTFIX \"x64\"\n\t\t\t#else\n\t\t\t\t#define ARCH_CPU_POSTFIX \"amd64\"\n\t\t\t\t#define ARCH_ALTCPU_POSTFIX \"x86_64\"\n\t\t\t#endif\n\t\t#endif\n\t#elif defined(_M_IX86) || defined(__i386__)\n\t\t#define ARCH_CPU_POSTFIX \"x86\"\n\t#elif defined(__powerpc__) || defined(__ppc__)\n\t\t#define ARCH_CPU_POSTFIX \"ppc\"\n\t#elif defined(__aarch64__) || defined(__arm64__)\n\t\t#define ARCH_CPU_POSTFIX \"arm64\"\n\t#elif defined(__arm__)\n\t\t#ifdef __SOFTFP__\n\t\t\t#define ARCH_CPU_POSTFIX \"arm\"\n\t\t#else\n\t\t\t#define ARCH_CPU_POSTFIX \"armhf\"\n\t\t#endif\n\t#else\n\t\t#define ARCH_CPU_POSTFIX \"unk\"\n\t#endif\n#endif\n\n#if defined(_WIN32)\n\t#define FTE_LITTLE_ENDIAN\n#elif defined(__BYTE_ORDER__)\n\t#ifdef __ORDER_BIG_ENDIAN__\n\t\t#if (__BYTE_ORDER__==__ORDER_BIG_ENDIAN__) && (__FLOAT_WORD_ORDER__==__ORDER_BIG_ENDIAN__)\n\t\t\t#define FTE_BIG_ENDIAN\n\t\t#endif\n\t#endif\n\t#ifdef __ORDER_LITTLE_ENDIAN__\n\t\t#if (__BYTE_ORDER__==__ORDER_LITTLE_ENDIAN__) && (__FLOAT_WORD_ORDER__==__ORDER_LITTLE_ENDIAN__)\n\t\t\t#define FTE_LITTLE_ENDIAN\n\t\t#endif\n\t#endif\n#elif defined(__LITTLE_ENDIAN__)\n\t#define FTE_LITTLE_ENDIAN\n#elif defined(__BIG_ENDIAN__)\n\t#define FTE_BIG_ENDIAN\n#endif\n\n#ifdef _MSC_VER\n\t#define VARGS __cdecl\n\t#define MSVCDISABLEWARNINGS\n\t#if _MSC_VER >= 1300\n\t\t#define FTE_DEPRECATED __declspec(deprecated)\n\t\t#ifndef _CRT_SECURE_NO_WARNINGS\n\t\t\t#define _CRT_SECURE_NO_WARNINGS\n\t\t#endif\n\t\t#ifndef _CRT_NONSTDC_NO_WARNINGS\n\t\t\t#define _CRT_NONSTDC_NO_WARNINGS\n\t\t#endif\n\t#endif\n\t#define NORETURN __declspec(noreturn)\n#endif\n#if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1))\n\t#define FTE_DEPRECATED  __attribute__((__deprecated__))\t//no idea about the actual gcc version\n\t#if defined(_WIN32)\n\t\t#include <stdio.h>\n\t\t#ifdef __MINGW_PRINTF_FORMAT\n\t\t\t#define LIKEPRINTF(x) __attribute__((format(__MINGW_PRINTF_FORMAT,x,x+1)))\n\t\t#else\n\t\t\t#define LIKEPRINTF(x) __attribute__((format(ms_printf,x,x+1)))\n\t\t#endif\n\t#else\n\t\t#define LIKEPRINTF(x) __attribute__((format(printf,x,x+1)))\n\t#endif\n#endif\n#if (__GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 5))\n\t#define NORETURN __attribute__((noreturn))\n#endif\n\n//unreachable marks the path leading to it as unreachable too.\n#if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5))\n\t#define FTE_UNREACHABLE __builtin_unreachable()\n#endif\n\n//I'm making my own restrict, because msvc's headers can't cope if I #define restrict to __restrict, and quite possibly other platforms too\n#if __STDC_VERSION__ >= 199901L\n\t#define fte_restrict restrict\n#elif defined(_MSC_VER) && _MSC_VER >= 1400 || __GNUC__ >= 4\n\t#define fte_restrict __restrict\n#else\n\t#define fte_restrict\n#endif\n\n#if _MSC_VER >= 1300\n\t#define FTE_ALIGN(a) __declspec(align(a))\n#elif defined(__clang__)\n\t#define FTE_ALIGN(a) __attribute__((aligned(a)))\n#elif __GNUC__ >= 3\n\t#define FTE_ALIGN(a) __attribute__((aligned(a)))\n#else\n\t#define FTE_ALIGN(a)\n#endif\n\n#if __STDC_VERSION__ >= 201112L\n\t#include <stdalign.h>\n\t#define fte_alignof(type) alignof(qintptr_t)\n#elif _MSC_VER\n\t#define fte_alignof(type) __alignof(qintptr_t)\n#else\n\t#define fte_alignof(type) sizeof(qintptr_t)\n#endif\n\n//WARNING: FTE_CONSTRUCTOR things are unordered.\n#ifdef __cplusplus\n\t//use standard constructors in any c++ code...\n\t#define FTE_CONSTRUCTOR(fn) \\\n\t\tstatic void fn(void);\t\\\n\t\tclass atinit_##fn {atinit_##fn(void){fn();}};\t\\\n\t\tstatic void fn(void)\n#elif _MSC_VER\n    #pragma section(\".CRT$XCU\",read)\n    #if _MSC_VER >= 1500\t//use '/include' so it doesn't get stripped from linker optimisations\n\t\t#define INITIALIZER2_(f,p) \\\n\t\t\tstatic void f(void); \\\n\t\t\t__declspec(allocate(\".CRT$XCU\")) void (*f##_)(void) = f; \\\n\t\t\t__pragma(comment(linker,\"/include:\" p #f \"_\")) \\\n\t\t\tstatic void f(void)\n\t#else\t// '/include' doesn't exist, hope there's no linker optimisations.\n\t\t#define INITIALIZER2_(f,p) \\\n\t\t\tstatic void f(void); \\\n\t\t\t__declspec(allocate(\".CRT$XCU\")) void (*f##_)(void) = f; \\\n\t\t\tstatic void f(void)\n\t#endif\n    #ifdef _WIN64\n        #define INITIALIZER(f) INITIALIZER2_(f,\"\")\n    #else\n        #define INITIALIZER(f) INITIALIZER2_(f,\"_\")\n    #endif\n#else\n\t//assume gcc/clang...\n\t#define FTE_CONSTRUCTOR(fn) \\\n\t\t__attribute__((constructor)) static void fn(void)\n#endif\n\n\n//safeswitch(foo){safedefault: break;}\n//switch, but errors for any omitted enum values despite the presence of a default case.\n//(gcc will generally give warnings without the default, but sometimes you don't have control over the source of your enumeration values)\n//note: android's gcc seems to screw up the pop, instead leaving the warnings enabled, which gets horrendously spammy.\n#if (__GNUC__ >= 4) && !defined(ANDROID)\n\t#define safeswitch\t\\\n\t\t_Pragma(\"GCC diagnostic push\")\t\\\n\t\t_Pragma(\"GCC diagnostic error \\\"-Wswitch-enum\\\"\") \\\n\t\t_Pragma(\"GCC diagnostic error \\\"-Wswitch-default\\\"\") \\\n\t\tswitch\n\t#define safedefault _Pragma(\"GCC diagnostic pop\") default\n#else\n\t#define safeswitch switch\n\t#define safedefault default\n#endif\n\n//fte_inline must only be used in headers, and requires one and ONLY one fte_inlinebody elsewhere.\n//fte_inlinebody must be used on a prototype OUTSIDE of a header.\n//fte_inlinestatic must not be used inside any headers at all.\n#if __STDC_VERSION__ >= 199901L\n\t//C99 specifies that an inline function is used as a hint. there should be an actual body/copy somewhere (extern inline foo).\n\t#define fte_inline inline\t//must have non-line 'int foo();' somewhere\n\t#define fte_inlinebody extern inline\n\t#define fte_inlinestatic static inline\n#elif defined(_MSC_VER)\n\t//msvc will inline like C++. and that's fine.\n\t#define fte_inline __inline //c++ style\n\t#define fte_inlinebody\n\t#define fte_inlinestatic static __inline\n#elif (__GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 5))\n\t//gcc will generally inline where it can - so long as its static. but that doesn't stop it warning\n\t#define fte_inline __attribute__((unused)) static\n\t#define fte_inlinebody static\n\t#if __GNUC__ > 5\n\t\t#define fte_inlinestatic static inline\n\t#else\n\t\t#define fte_inlinestatic static\n\t#endif\n#else\n\t//make it static so we at least don't get errors (might still get warnings. see above)\n\t#define fte_inline static\n\t#define fte_inlinebody static\n\t#define fte_inlinestatic static\n#endif\n\n\n#ifndef FTE_DEPRECATED\n#define FTE_DEPRECATED\n#endif\n#ifndef FTE_UNREACHABLE\n#define FTE_UNREACHABLE\n#endif\n#ifndef LIKEPRINTF\n#define LIKEPRINTF(x)\n#endif\n#ifndef VARGS\n#define VARGS\n#endif\n#ifndef NORETURN\n#define NORETURN\n#endif\n\n#ifdef _WIN32\n#define ZEXPORT VARGS\n#define ZEXPORTVA VARGS\n#endif\n\n#ifdef _DEBUG\n\t#undef FTE_UNREACHABLE\n\t#define FTE_UNREACHABLE Sys_Error(\"Unreachable reached: %s %i\\n\", __FILE__, __LINE__)\n#endif\n\n#ifndef stricmp\n\t#ifdef _WIN32\n\t\t//Windows-specific...\n\t\t#define stricmp _stricmp\n\t\t#define strnicmp _strnicmp\n\t#else\n\t\t//Posix\n\t\t#define stricmp Q_strcasecmp\n\t\t#define strnicmp Q_strncasecmp\n\t#endif\n#endif\n\n#if defined(IQMTOOL) || defined(WEBSVONLY) || defined(FTEPLUGIN)\n\t#ifdef _WIN32\n\t\t//Windows-specific...\n\t\t#define Q_strcasecmp _stricmp\n\t\t#define Q_strncasecmp _strnicmp\n\t#else\n\t\t//Posix\n\t\t#define Q_strcasecmp strcasecmp\n\t\t#define Q_strncasecmp strncasecmp\n\t#endif\n#endif\n\n// !!! if this is changed, it must be changed in d_ifacea.h too !!!\n#define CACHE_SIZE\t32\t\t// used to align key data structures\n\n#define UNUSED(x)\t(x = x)\t// for pesky compiler / lint warnings\n\n// up / down\n#define\tPITCH\t0\n\n// left / right\n#define\tYAW\t\t1\n\n// fall over\n#define\tROLL\t2\n\n\n#define\tMAX_QPATH\t\t128\t\t\t// max length of a quake game pathname\n#define\tMAX_OSPATH\t\t1024\t\t// max length of a filesystem pathname (260 on windows, but needs to be longer for utf8)\n#define OLD_MAX_QPATH\t64\t\t\t// it was baked into various file formats, which is unfortunate.\n\n#define\tON_EPSILON\t\t0.1\t\t\t// point on plane side epsilon\n\n#define\tMAX_NQMSGLEN\t65536\t\t// max length of a reliable message. FIXME: should be 8000 to play safe with proquake\n#define MAX_Q2MSGLEN\t1400\n#define MAX_QWMSGLEN\t1450\n#define MAX_OVERALLMSGLEN\t65536\t// mvdsv sends packets this big\n#define\tMAX_DATAGRAM\t1450\t\t// max length of unreliable message\n#define MAX_Q2DATAGRAM\tMAX_Q2MSGLEN\n#define\tMAX_NQDATAGRAM\t1024\t\t// max length of unreliable message with vanilla nq protocol\n#define MAX_OVERALLDATAGRAM MAX_DATAGRAM\n\n#define MAX_BACKBUFLEN\t1200\n\n#ifdef Q1BSPS\n#define lightstyleindex_t unsigned short\n#else\n#define lightstyleindex_t qbyte\n#endif\n#define INVALID_LIGHTSTYLE ((lightstyleindex_t)(~0u))\t//the style that's invalid, signifying to stop adding more.\n#define INVALID_VLIGHTSTYLE ((qbyte)(~0u))\t//the style that's invalid for verticies, signifying to stop adding more.\n\n//\n// per-level limits\n//\n#ifdef FTE_TARGET_WEB\n#define MAX_EDICTS\t\t((1<<15)-1)\n#else\n#define\tMAX_EDICTS\t\t((1<<22)-1)\t\t\t// expandable up to 22 bits\n//#define\tMAX_EDICTS\t\t((1<<18)-1)\t\t\t// expandable up to 22 bits\n#endif\n\n#define\tMAX_NET_LIGHTSTYLES\t\t(INVALID_LIGHTSTYLE+1)\t\t// 16bit. the last index MAY be used to signify an invalid lightmap in the bsp, but is still valid for rtlights.\n#define MAX_STANDARDLIGHTSTYLES 64\n#define\tMAX_PRECACHE_MODELS\t\t16384\t\t// 14bit.\n#define\tMAX_PRECACHE_SOUNDS\t\t4096\t\t// 14bit.\n#define MAX_SSPARTICLESPRE 1024\t\t\t\t// 14bit. precached particle effect names, for server-side pointparticles/trailparticles.\n#define MAX_VWEP_MODELS 32\n\n#define\tMAX_CSMODELS\t\t2048\t\t\t// these live entirly clientside\n#define MAX_CSPARTICLESPRE\t1024\n\n#define\tSAVEGAME_COMMENT_LENGTH\t39\n\n#define\tMAX_STYLESTRING\t64\n\n#define MAX_Q2EDICTS 1024\n\n//\n// stats are integers communicated to the client by the server\n//\n#define MAX_QW_STATS 32\nenum {\n#ifdef QUAKESTATS\nSTAT_HEALTH\t\t\t= 0,\n//STAT_FRAGS\t\t= 1,\nSTAT_WEAPONMODELI\t= 2,\nSTAT_AMMO\t\t\t= 3,\nSTAT_ARMOR\t\t\t= 4,\nSTAT_WEAPONFRAME\t= 5,\nSTAT_SHELLS\t\t\t= 6,\nSTAT_NAILS\t\t\t= 7,\nSTAT_ROCKETS\t\t= 8,\nSTAT_CELLS\t\t\t= 9,\nSTAT_ACTIVEWEAPON\t= 10,\nSTAT_TOTALSECRETS\t= 11,\nSTAT_TOTALMONSTERS\t= 12,\nSTAT_SECRETS\t\t= 13,\t\t// bumped on client side by svc_foundsecret\nSTAT_MONSTERS\t\t= 14,\t\t// bumped by svc_killedmonster\nSTAT_ITEMS\t\t\t= 15,\nSTAT_VIEWHEIGHT\t\t= 16,\t//same as zquake\nSTAT_TIME\t\t\t= 17,\t//zquake\nSTAT_MATCHSTARTTIME = 18,\n//STAT_UNUSED\t\t= 19,\n#ifdef SIDEVIEWS\nSTAT_VIEW2\t\t\t= 20,\n#endif\nSTAT_VIEWZOOM\t\t= 21, // DP\n#define STAT_VIEWZOOM_SCALE 255\n//STAT_UNUSED\t\t= 22,\n//STAT_UNUSED\t\t= 23,\n//STAT_UNUSED\t\t= 24,\nSTAT_IDEALPITCH\t\t= 25,\t//nq-emu\nSTAT_PUNCHANGLE_X\t= 26,\t//nq-emu\nSTAT_PUNCHANGLE_Y\t= 27,\t//nq-emu\nSTAT_PUNCHANGLE_Z\t= 28,\t//nq-emu\nSTAT_PUNCHVECTOR_X\t= 29,\nSTAT_PUNCHVECTOR_Y\t= 30,\nSTAT_PUNCHVECTOR_Z\t= 31,\n\n#ifdef HEXEN2\n//these stats are used only when running a hexen2 mod/hud, and will never be used for a quake mod/hud/generic code.\nSTAT_H2_LEVEL\t= 32,\t\t\t\t// changes stat bar\nSTAT_H2_INTELLIGENCE,\t\t\t\t// changes stat bar\nSTAT_H2_WISDOM,\t\t\t\t\t\t// changes stat bar\nSTAT_H2_STRENGTH,\t\t\t\t\t// changes stat bar\nSTAT_H2_DEXTERITY,\t\t\t\t\t// changes stat bar\nSTAT_H2_BLUEMANA,\t\t\t\t\t// changes stat bar\nSTAT_H2_GREENMANA,\t\t\t\t\t// changes stat bar\nSTAT_H2_EXPERIENCE,\t\t\t\t\t// changes stat bar\n#define STAT_H2_CNT_FIRST (STAT_H2_CNT_TORCH)\nSTAT_H2_CNT_TORCH,\t\t\t\t\t// changes stat bar\nSTAT_H2_CNT_H_BOOST,\t\t\t\t// changes stat bar\nSTAT_H2_CNT_SH_BOOST,\t\t\t\t// changes stat bar\nSTAT_H2_CNT_MANA_BOOST,\t\t\t\t// changes stat bar\nSTAT_H2_CNT_TELEPORT,\t\t\t\t// changes stat bar\nSTAT_H2_CNT_TOME,\t\t\t\t\t// changes stat bar\nSTAT_H2_CNT_SUMMON,\t\t\t\t\t// changes stat bar\nSTAT_H2_CNT_INVISIBILITY,\t\t\t// changes stat bar\nSTAT_H2_CNT_GLYPH,\t\t\t\t\t// changes stat bar\nSTAT_H2_CNT_HASTE,\t\t\t\t\t// changes stat bar\nSTAT_H2_CNT_BLAST,\t\t\t\t\t// changes stat bar\nSTAT_H2_CNT_POLYMORPH,\t\t\t\t// changes stat bar\nSTAT_H2_CNT_FLIGHT,\t\t\t\t\t// changes stat bar\nSTAT_H2_CNT_CUBEOFFORCE,\t\t\t// changes stat bar\nSTAT_H2_CNT_INVINCIBILITY,\t\t\t// changes stat bar\n#define STAT_H2_CNT_LAST (STAT_H2_CNT_INVINCIBILITY)\n#define STAT_H2_CNT_COUNT (STAT_H2_CNT_LAST+1-STAT_H2_CNT_FIRST)\nSTAT_H2_ARTIFACT_ACTIVE,\nSTAT_H2_ARTIFACT_LOW,\nSTAT_H2_MOVETYPE,\nSTAT_H2_CAMERAMODE,\t//entity\nSTAT_H2_HASTED,\nSTAT_H2_INVENTORY,\nSTAT_H2_RINGS_ACTIVE,\n\nSTAT_H2_RINGS_LOW,\nSTAT_H2_ARMOUR1,\nSTAT_H2_ARMOUR2,\nSTAT_H2_ARMOUR3,\nSTAT_H2_ARMOUR4,\nSTAT_H2_FLIGHT_T,\nSTAT_H2_WATER_T,\nSTAT_H2_TURNING_T,\nSTAT_H2_REGEN_T,\nSTAT_H2_PUZZLE1,\t//string\nSTAT_H2_PUZZLE2,\t//string\nSTAT_H2_PUZZLE3,\t//string\nSTAT_H2_PUZZLE4,\t//string\nSTAT_H2_PUZZLE5,\t//string\nSTAT_H2_PUZZLE6,\t//string\nSTAT_H2_PUZZLE7,\t//string\nSTAT_H2_PUZZLE8,\t//string\nSTAT_H2_MAXHEALTH,\nSTAT_H2_MAXMANA,\nSTAT_H2_FLAGS,\nSTAT_H2_PLAYERCLASS,\n\nSTAT_H2_OBJECTIVE1,\t//integer\nSTAT_H2_OBJECTIVE2,\t//integer\n#endif\n\nSTAT_MOVEVARS_AIRACCEL_QW_STRETCHFACTOR\t\t= 220, // DP\nSTAT_MOVEVARS_AIRCONTROL_PENALTY\t\t\t= 221, // DP\nSTAT_MOVEVARS_AIRSPEEDLIMIT_NONQW \t\t\t= 222, // DP\nSTAT_MOVEVARS_AIRSTRAFEACCEL_QW \t\t\t= 223, // DP\nSTAT_MOVEVARS_AIRCONTROL_POWER\t\t\t\t= 224, // DP\nSTAT_MOVEFLAGS\t\t\t\t\t\t\t\t= 225, // DP\nSTAT_MOVEVARS_WARSOWBUNNY_AIRFORWARDACCEL\t= 226, // DP\nSTAT_MOVEVARS_WARSOWBUNNY_ACCEL\t\t\t\t= 227, // DP\nSTAT_MOVEVARS_WARSOWBUNNY_TOPSPEED\t\t\t= 228, // DP\nSTAT_MOVEVARS_WARSOWBUNNY_TURNACCEL\t\t\t= 229, // DP\nSTAT_MOVEVARS_WARSOWBUNNY_BACKTOSIDERATIO\t= 230, // DP\nSTAT_MOVEVARS_AIRSTOPACCELERATE\t\t\t\t= 231, // DP\nSTAT_MOVEVARS_AIRSTRAFEACCELERATE\t\t\t= 232, // DP\nSTAT_MOVEVARS_MAXAIRSTRAFESPEED\t\t\t\t= 233, // DP\nSTAT_MOVEVARS_AIRCONTROL\t\t\t\t\t= 234, // DP\nSTAT_FRAGLIMIT\t\t\t\t\t\t\t\t= 235, // DP\nSTAT_TIMELIMIT\t\t\t\t\t\t\t\t= 236, // DP\nSTAT_MOVEVARS_WALLFRICTION\t\t\t\t\t= 237, // DP\nSTAT_MOVEVARS_FRICTION\t\t\t\t\t\t= 238, // DP\nSTAT_MOVEVARS_WATERFRICTION\t\t\t\t\t= 239, // DP\nSTAT_MOVEVARS_TICRATE\t\t\t\t\t\t= 240, // DP\nSTAT_MOVEVARS_TIMESCALE\t\t\t\t\t\t= 241, // DP\nSTAT_MOVEVARS_GRAVITY\t\t\t\t\t\t= 242, // DP\nSTAT_MOVEVARS_STOPSPEED\t\t\t\t\t\t= 243, // DP\nSTAT_MOVEVARS_MAXSPEED\t\t\t\t\t\t= 244, // DP\nSTAT_MOVEVARS_SPECTATORMAXSPEED\t\t\t\t= 245, // DP\nSTAT_MOVEVARS_ACCELERATE\t\t\t\t\t= 246, // DP\nSTAT_MOVEVARS_AIRACCELERATE\t\t\t\t\t= 247, // DP\nSTAT_MOVEVARS_WATERACCELERATE\t\t\t\t= 248, // DP\nSTAT_MOVEVARS_ENTGRAVITY\t\t\t\t\t= 249, // DP\nSTAT_MOVEVARS_JUMPVELOCITY\t\t\t\t\t= 250, // DP\nSTAT_MOVEVARS_EDGEFRICTION\t\t\t\t\t= 251, // DP\nSTAT_MOVEVARS_MAXAIRSPEED\t\t\t\t\t= 252, // DP\nSTAT_MOVEVARS_STEPHEIGHT\t\t\t\t\t= 253, // DP\nSTAT_MOVEVARS_AIRACCEL_QW\t\t\t\t\t= 254, // DP\nSTAT_MOVEVARS_AIRACCEL_SIDEWAYS_FRICTION\t= 255, // DP\n#endif\n\tMAX_CL_STATS = 256\n};\n\n#ifdef QUAKESTATS\n//\n// item flags\n//\n#define\tIT_SHOTGUN\t\t\t\t(1u<<0)\n#define\tIT_SUPER_SHOTGUN\t\t(1u<<1)\n#define\tIT_NAILGUN\t\t\t\t(1u<<2)\n#define\tIT_SUPER_NAILGUN\t\t(1u<<3)\n\n#define\tIT_GRENADE_LAUNCHER\t\t(1u<<4)\n#define\tIT_ROCKET_LAUNCHER\t\t(1u<<5)\n#define\tIT_LIGHTNING\t\t\t(1u<<6)\n#define\tIT_SUPER_LIGHTNING\t\t(1u<<7)\n\n#define\tIT_SHELLS\t\t\t\t(1u<<8)\n#define\tIT_NAILS\t\t\t\t(1u<<9)\n#define\tIT_ROCKETS\t\t\t\t(1u<<10)\n#define\tIT_CELLS\t\t\t\t(1u<<11)\n\n#define\tIT_AXE\t\t\t\t\t(1u<<12)\n\n#define\tIT_ARMOR1\t\t\t\t(1u<<13)\n#define\tIT_ARMOR2\t\t\t\t(1u<<14)\n#define\tIT_ARMOR3\t\t\t\t(1u<<15)\n\n#define\tIT_SUPERHEALTH\t\t\t(1u<<16)\n\n#define\tIT_KEY1\t\t\t\t\t(1u<<17)\n#define\tIT_KEY2\t\t\t\t\t(1u<<18)\n\n#define\tIT_INVISIBILITY\t\t\t(1u<<19)\n\n#define\tIT_INVULNERABILITY\t\t(1u<<20)\n#define\tIT_SUIT\t\t\t\t\t(1u<<21)\n#define\tIT_QUAD\t\t\t\t\t(1u<<22)\n\n#define\tIT_SIGIL1\t\t\t\t(1u<<28)\n\n#define\tIT_SIGIL2\t\t\t\t(1u<<29)\n#define\tIT_SIGIL3\t\t\t\t(1u<<30)\n#define\tIT_SIGIL4\t\t\t\t(1u<<31)\n#endif\n\n//\n// print flags\n//\n#define\tPRINT_LOW\t\t\t0\t\t// pickup messages\n#define\tPRINT_MEDIUM\t\t1\t\t// death messages\n#define\tPRINT_HIGH\t\t\t2\t\t// critical messages\n#define\tPRINT_CHAT\t\t\t3\t\t// chat messages\n\n\n\n//split screen stuff\n#ifndef MAX_SPLITS\n#define MAX_SPLITS 1u\t//disabled, but must be defined for sanities sake.\n#endif\n\n\n\n\n//savegame vars\n#define\tSAVEGAME_COMMENT_LENGTH\t\t39\n#define\tSAVEGAME_VERSION_NQ\t\t\t5\n#define\tSAVEGAME_VERSION_QW\t\t\t6\t\t//actually zQuake, but the functional difference is that its qw instead of nq.\n#define\tSAVEGAME_VERSION_FTE_LEG\t667\t\t//found in .sav files. this is for legacy-like saved games with multiple players.\n#define SAVEGAME_VERSION_FTE_HUB\t25000\t//found in .fsv files. includes svs.gametype, so bumps should be large.\n#define CACHEGAME_VERSION_OLD\t\t513\t\t//lame ordering.\n#define CACHEGAME_VERSION_VERBOSE\t514\t\t//saved fields got names, making it more extensible.\n#define CACHEGAME_VERSION_MODSAVED\t515\t\t//qc is responsible for saving all they need to, and restoring it after.\n\n\n#define PM_DEFAULTSTEPHEIGHT\t18\n\n\n#define dem_cmd\t\t\t0\n#define dem_read\t\t1\n#define dem_set\t\t\t2\n#define dem_multiple\t3\n#define\tdem_single\t\t4\n#define dem_stats\t\t5\n#define dem_all\t\t\t6\n\n\n#if 0 //fuck sake, just build it in an older chroot.\n/*\nglibc SUCKS. 64bit glibc is depending upon glibc 2.14 because of some implementation-defined copy direction change that breaks flash.\nor something.\nanyway, the actual interface is the same. the old version might be slower, but when updating glibc generally results in also installing systemd, requiring the new version is NOT an option.\n*/\n#if defined(__GNUC__) && defined(__amd64__) && defined(__linux__) && !defined(FTE_SDL)\n\t#include <features.h>       /* for glibc version */\n\t#if defined(__GLIBC__) && (__GLIBC__ == 2) && (__GLIBC_MINOR__ >= 14)\n\t\t__asm__(\".symver memcpy,memcpy@GLIBC_2.2.5\");\n\t\t__asm__(\".symver memmove,memmove@GLIBC_2.2.5\");\n\t#endif\n\t#if defined(__GLIBC__) && (__GLIBC__ == 2) && (__GLIBC_MINOR__ >= 29)\n\t\t__asm__(\".symver exp,exp@GLIBC_2.2.5\");\n\t\t__asm__(\".symver log,log@GLIBC_2.2.5\");\n\t\t__asm__(\".symver pow,pow@GLIBC_2.2.5\");\n\t#endif\n#endif\n/*end glibc workaround*/\n#endif\n\n#endif\t//ifndef __BOTHDEFS_H\n"
  },
  {
    "path": "engine/common/bspfile.h",
    "content": "/*\nCopyright (C) 1996-1997 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  \n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n*/\n\n\n// upper design bounds\n\n#define\tMAX_MAP_HULLSDQ1\t4\n#define\tMAX_MAP_HULLSDH2\t8\n#define\tMAX_MAP_HULLSM\t\t8\n#define RBSP_STYLESPERSURF\t\t4\n#define Q1Q2BSP_STYLESPERSURF\t4\n#ifdef Q1BSPS\n#define MAXCPULIGHTMAPS\t\t16\t//max lightmaps mixed by the cpu (vanilla q1bsp=4, fte extensions=no real cap, must be >=MAXRLIGHTMAPS)\n#elif defined(Q1BSPS)\n#define MAXCPULIGHTMAPS\t\tQ1Q2BSP_STYLESPERSURF\t//max lightmaps mixed by the cpu (vanilla q1bsp=4, fte extensions=no real cap, must be >=MAXRLIGHTMAPS)\n#else\n#define MAXCPULIGHTMAPS\t\tMAXRLIGHTMAPS\t//max lightmaps mixed by the cpu (vanilla q1bsp=4, fte extensions=no real cap, must be >=MAXRLIGHTMAPS)\n#endif\n\n//#define\tMAX_MAP_MODELS\t\t256\n//#define\tMAX_MAP_BRUSHES\t\t0x8000\n//#define\tMAX_MAP_ENTITIES\t1024\n//#define\tMAX_MAP_ENTSTRING\t65536\n\n//FIXME: make sure that any 16bit indexes are bounded properly\n//FIXME: ensure that we don't get any count*size overflows\n#define\tSANITY_LIMIT(t)\t\t((unsigned int)(0x7fffffffu/sizeof(t)))\t\t//sanity limit for the array, to ensure a 32bit value cannot overflow us.\n//#define\tSANITY_MAX_MAP_PLANES\t\t65536*64\t\t//sanity\n//#define\tSANITY_MAX_MAP_NODES\t\t65536*64\t\t//sanity\n//#define\tSANITY_MAX_MAP_CLIPNODES\t65536*64\t\t//sanity\n//#define\tMAX_MAP_LEAFS\t\t\t\t1\t\t//pvs buffer size. not sanity.\n//#define\tSANITY_MAX_MAP_LEAFS\t\t65536*64\t\t//too many leafs results in massive amounts of ram used for pvs/phs caches.\n//#define\tSANITY_MAX_MAP_VERTS\t\t65536\t\t//sanity\n//#define\tSANITY_MAX_MAP_FACES\t\t65536*64\t\t//sanity\n//#define\tMAX_MAP_MARKSURFACES 65536\t//sanity\n//#define\tMAX_MAP_TEXINFO\t\t4096\t//sanity\n//#define\tMAX_MAP_EDGES\t\t256000\n//#define\tMAX_MAP_SURFEDGES\t512000\n//#define\tMAX_MAP_MIPTEX\t\t0x200000\n//#define\tMAX_MAP_LIGHTING\t0x100000\n//#define\tMAX_MAP_VISIBILITY\t0x200000\n\n#define\tSANITY_MAX_MAP_BRUSHSIDES\t((~0u)/sizeof(q2cbrushside_t))\n\n// key / value pair sizes\n\n#define\tMAX_KEY\t\t32\n#define\tMAX_VALUE\t1024\n\n\n//=============================================================================\n\n#define BSPVERSIONQTEST\t\t\"\\x17\\0\\0\\0\",4\t//qtest\n#define BSPVERSIONPREREL\t\"\\x1C\\0\\0\\0\",4\t//prerelease\n#define BSPVERSION\t\t\t\"\\x1D\\0\\0\\0\",4\t//vanilla\n#define BSPVERSIONHL\t\t\"\\x1E\\0\\0\\0\",4\t//HalfLife support\n#define BSPVERSION_LONG1\t\"2PSB\",4 /*RMQ support (2PSB). 32bits instead of shorts for all but bbox sizes*/\n#define BSPVERSION_LONG2\t\"BSP2\",4 /*BSP2 support. 32bits instead of shorts for everything*/\n#define BSPVERSIONQ64 \t\t\" 46Q\",4 /* Remastered BSP format used for Quake 64 addon */\n\ntypedef struct\n{\n\tunsigned int\t\tfileofs, filelen;\n} lump_t;\n\n#define\tLUMP_ENTITIES\t0\n#define\tLUMP_PLANES\t\t1\n#define\tLUMP_TEXTURES\t2\n#define\tLUMP_VERTEXES\t3\n#define\tLUMP_VISIBILITY\t4\n#define\tLUMP_NODES\t\t5\n#define\tLUMP_TEXINFO\t6\n#define\tLUMP_FACES\t\t7\n#define\tLUMP_LIGHTING\t8\n#define\tLUMP_CLIPNODES\t9\n#define\tLUMP_LEAFS\t\t10\n#define\tLUMP_MARKSURFACES 11\n#define\tLUMP_EDGES\t\t12\n#define\tLUMP_SURFEDGES\t13\n#define\tLUMP_MODELS\t\t14\n\n#define\tHEADER_LUMPS\t15\n\ntypedef struct\n{\n\tfloat\t\tmins[3], maxs[3];\n\tfloat\t\torigin[3];\n\tint\t\t\theadnode[MAX_MAP_HULLSDQ1];\n\tint\t\t\tvisleafs;\t\t// not including the solid leaf 0\n\tint\t\t\tfirstface, numfaces;\n} dq1model_t;\n\ntypedef struct\n{\n\tfloat\t\tmins[3], maxs[3];\n\tfloat\t\torigin[3];\n\tint\t\t\theadnode[MAX_MAP_HULLSDH2];\n\tint\t\t\tvisleafs;\t\t// not including the solid leaf 0\n\tint\t\t\tfirstface, numfaces;\n} dh2model_t;\n\ntypedef struct\n{\n\tint\t\t\tversion;\t\n\tlump_t\t\tlumps[HEADER_LUMPS];\n} dheader_t;\n\ntypedef struct\n{\n\tint\t\t\tnummiptex;\n\tint\t\t\tdataofs[4];\t\t// [nummiptex]\n} dmiptexlump_t;\n\n#define\tMIPLEVELS\t4\ntypedef struct miptex_s\n{\n\tchar\t\tname[16];\n\tunsigned\twidth, height;\n\tunsigned\toffsets[MIPLEVELS];\t\t// four mip maps stored\n} miptex_t;\n\ntypedef struct q64miptex_s\n{\n\tchar\t\tname[16];\n\tunsigned\twidth, height, scale;\n\tunsigned\toffsets[MIPLEVELS];\t\t// four mip maps stored\n} q64miptex_t;\n\n\ntypedef struct\n{\n\tfloat\tpoint[3];\n} dvertex_t;\n\n\n// 0-2 are axial planes\n#define\tPLANE_X\t\t\t0\n#define\tPLANE_Y\t\t\t1\n#define\tPLANE_Z\t\t\t2\n\n// 3-5 are non-axial planes snapped to the nearest\n#define\tPLANE_ANYX\t\t3\n#define\tPLANE_ANYY\t\t4\n#define\tPLANE_ANYZ\t\t5\n\ntypedef struct\n{\n\tfloat\tnormal[3];\n\tfloat\tdist;\n\tint\t\ttype;\t\t// PLANE_X - PLANE_ANYZ ?remove? trivial to regenerate\n} dplane_t;\n\n\nenum q1contents_e\n{\t//q1 and halflife bsp contents values.\n\t//also used for .skin for content forcing.\n\tQ1CONTENTS_EMPTY\t\t= -1,\n\tQ1CONTENTS_SOLID\t\t= -2,\n\tQ1CONTENTS_WATER\t\t= -3,\n\tQ1CONTENTS_SLIME\t\t= -4,\n\tQ1CONTENTS_LAVA\t\t\t= -5,\n\tQ1CONTENTS_SKY\t\t\t= -6,\n//#define HLCONTENTS_ORIGIN\t  -7\t/*not known to engine - origin or something*/\n\tHLCONTENTS_CLIP\t\t\t= -8,\t/*solid to players+monsters, but not tracelines*/\n\tHLCONTENTS_CURRENT_0\t= -9,\t/*moves player*/\n\tHLCONTENTS_CURRENT_90\t= -10,\t/*moves player*/\n\tHLCONTENTS_CURRENT_180\t= -11,\t/*moves player*/\n\tHLCONTENTS_CURRENT_270\t= -12,\t/*moves player*/\n\tHLCONTENTS_CURRENT_UP\t= -13,\t/*moves player*/\n\tHLCONTENTS_CURRENT_DOWN\t= -14,\t/*moves player*/\n\tHLCONTENTS_TRANS\t\t= -15,\t/*empty, but blocks pvs (for opaque non-solid windows, like vanilla-q1 water)*/\n\tQ1CONTENTS_LADDER\t\t= -16,\t/*player can climb up/down*/\n\tQ1CONTENTS_MONSTERCLIP\t= -17,\t/*solid to monster movement*/\n\tQ1CONTENTS_PLAYERCLIP\t= -18,\t/*solid to player movement*/\n\tQ1CONTENTS_CORPSE\t\t= -19,\t/*solid to tracelines but not boxes*/\n};\n\n// !!! if this is changed, it must be changed in asm_i386.h too !!!\ntypedef struct\n{\n\tint\t\t\tplanenum;\n\tshort\t\tchildren[2];\t// negative numbers are -(leafs+1), not nodes\n\tshort\t\tmins[3];\t\t// for sphere culling\n\tshort\t\tmaxs[3];\n\tunsigned short\tfirstface;\n\tunsigned short\tnumfaces;\t// counting both sides\n} dsnode_t;\ntypedef struct\n{\n\tint\t\t\tplanenum;\n\tint\t\t\tchildren[2];\t// negative numbers are -(leafs+1), not nodes\n\tshort\t\tmins[3];\t\t// for sphere culling\n\tshort\t\tmaxs[3];\n\tunsigned int\tfirstface;\n\tunsigned int\tnumfaces;\t// counting both sides\n} dl1node_t;\ntypedef struct\n{\n\tint\t\t\tplanenum;\n\tint\t\t\tchildren[2];\t// negative numbers are -(leafs+1), not nodes\n\tfloat\t\tmins[3];\t\t// for sphere culling\n\tfloat\t\tmaxs[3];\n\tunsigned int\tfirstface;\n\tunsigned int\tnumfaces;\t// counting both sides\n} dl2node_t;\n\ntypedef struct\n{\n\tint\t\t\tplanenum;\n\tshort\t\tchildren[2];\t// negative numbers are contents\n} dsclipnode_t;\ntypedef struct\n{\n\tint\t\t\tplanenum;\n\tint\t\t\tchildren[2];\t// negative numbers are contents\n} dlclipnode_t;\n\ntypedef struct\n{\n\tint\t\t\tplanenum;\n\tint\t\t\tchildren[2];\t// negative numbers are contents\n} mclipnode_t;\n\ntypedef struct texinfo_s\n{\n\tfloat\t\tvecs[2][4];\t\t// [s/t][xyz offset]\n\tint\t\t\tmiptex;\n\tint\t\t\tflags;\n} texinfo_t;\n#define\tTEX_SPECIAL\t\t1\t\t// sky or slime, no lightmap or 256 subdivision\n\n// note that edge 0 is never used, because negative edge nums are used for\n// counterclockwise use of the edge in a face\ntypedef struct\n{\n\tunsigned short\tv[2];\t\t// vertex numbers\n} dsedge_t;\ntypedef struct\n{\n\tunsigned int\tv[2];\t\t// vertex numbers\n} dledge_t;\n\n#ifdef RFBSPS\n#define\tMAXRLIGHTMAPS\t4\t//max lightmaps mixed by the gpu (rbsp=4, otherwise 1)\n#else\n#define\tMAXRLIGHTMAPS\t1\t//max lightmaps mixed by the gpu (rbsp=4, otherwise 1)\n#endif\ntypedef struct\n{\n\tshort\t\tplanenum;\n\tshort\t\tside;\n\n\tint\t\t\tfirstedge;\t\t// we must support > 64k edges\n\tshort\t\tnumedges;\t\n\tshort\t\ttexinfo;\n\n// lighting info\n\tqbyte\t\tstyles[Q1Q2BSP_STYLESPERSURF];\n\tint\t\t\tlightofs;\t\t// start of [numstyles*surfsize] samples\n} dsface_t;\ntypedef struct\n{\n\tint\t\t\tplanenum;\n\tint\t\t\tside;\n\n\tint\t\t\tfirstedge;\t\t// we must support > 64k edges\n\tint\t\t\tnumedges;\t\n\tint\t\t\ttexinfo;\n\n// lighting info\n\tqbyte\t\tstyles[Q1Q2BSP_STYLESPERSURF];\n\tint\t\t\tlightofs;\t\t// start of [numstyles*surfsize] samples\n} dlface_t;\n\n\n\n#define\tAMBIENT_WATER\t0\n#define\tAMBIENT_SKY\t\t1\n#define\tAMBIENT_SLIME\t2\n#define\tAMBIENT_LAVA\t3\n\n#define\tNUM_AMBIENTS\t\t\t4\t\t// automatic ambient sounds\n\n// leaf 0 is the generic CONTENTS_SOLID leaf, used for all solid areas\n// all other leafs need visibility info\ntypedef struct\n{\n\tint\t\t\tcontents;\n\tint\t\t\tvisofs;\t\t\t\t// -1 = no visibility info\n\n\tshort\t\tmins[3];\t\t\t// for frustum culling\n\tshort\t\tmaxs[3];\n\n\tunsigned short\t\tfirstmarksurface;\n\tunsigned short\t\tnummarksurfaces;\n\n\tqbyte\t\tambient_level[NUM_AMBIENTS];\n} dsleaf_t;\ntypedef struct\n{\n\tint\t\t\tcontents;\n\tint\t\t\tvisofs;\t\t\t\t// -1 = no visibility info\n\n\tshort\t\tmins[3];\t\t\t// for frustum culling\n\tshort\t\tmaxs[3];\n\n\tunsigned int\t\tfirstmarksurface;\n\tunsigned int\t\tnummarksurfaces;\n\n\tqbyte\t\tambient_level[NUM_AMBIENTS];\n} dl1leaf_t;\ntypedef struct\n{\n\tint\t\t\tcontents;\n\tint\t\t\tvisofs;\t\t\t\t// -1 = no visibility info\n\n\tfloat\t\tmins[3];\t\t\t// for frustum culling\n\tfloat\t\tmaxs[3];\n\n\tunsigned int\t\tfirstmarksurface;\n\tunsigned int\t\tnummarksurfaces;\n\n\tqbyte\t\tambient_level[NUM_AMBIENTS];\n} dl2leaf_t;\n\n//============================================================================\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n#define\tMIPLEVELS\t4\ntypedef struct q2miptex_s\n{\n\tchar\t\tname[32];\n\tunsigned\twidth, height;\n\tunsigned\toffsets[MIPLEVELS];\t\t// four mip maps stored\n\tchar\t\tanimname[32];\t\t\t// next frame in animation chain\n\tint\t\t\tflags;\n\tint\t\t\tcontents;\n\tint\t\t\tvalue;\n} q2miptex_t;\n\n\n\n/*\n==============================================================================\n\n  .BSP file format\n\n==============================================================================\n*/\n\n#define IDBSPHEADER\t\"IBSP\",4\n\t\t// little-endian \"IBSP\"\n\n#define BSPVERSION_Q2\t38\n#define BSPVERSION_Q2W\t69 \n#define BSPVERSION_Q3\t46\n#define BSPVERSION_RTCW\t47\n#define BSPVERSION_RBSP 1\t//also fbsp(just bigger internal lightmaps)\n\n\n\n// upper design bounds\n// leaffaces, leafbrushes, planes, and verts are still bounded by\n// 16 bit short limits\n#define\tSANITY_MAX_Q2MAP_MODELS\t\tMAX_PRECACHE_MODELS\n//#define\tMAX_Q2MAP_ENTITIES\t2048\n#define SANITY_MAX_MAP_BRUSHES (~0u/sizeof(*out))\n#define\tSANITY_MAX_MAP_LEAFFACES\t262144\t\t//sanity only\n\n#define\tMAX_Q2MAP_AREAS\t\t(MAX_MAP_AREA_BYTES*8)\n#define\tMAX_Q2MAP_AREAPORTALS\t1024\n//#define\tMAX_Q2MAP_VERTS\t\tMAX_MAP_VERTS\n//#define\tMAX_Q2MAP_FACES\t\tMAX_MAP_FACES\n#define\tSANITY_MAX_MAP_LEAFBRUSHES (65536*64)\t\t//used in an array\n//#define\tMAX_Q2MAP_PORTALS\t\t65536\t//unused\n//#define\tMAX_Q2MAP_EDGES\t\t128000\t\t//unused\n//#define\tMAX_Q2MAP_SURFEDGES\t256000\t\t//unused\n//#define\tMAX_Q2MAP_LIGHTING\t0x200000\t//unused\n//#define\tMAX_Q2MAP_VISIBILITY\tMAX_MAP_VISIBILITY\n\n// key / value pair sizes\n\n#define\tMAX_KEY\t\t32\n#define\tMAX_VALUE\t1024\n\n//=============================================================================\n\n\n#define\tQ2LUMP_ENTITIES\t\t0\n#define\tQ2LUMP_PLANES\t\t\t1\n#define\tQ2LUMP_VERTEXES\t\t2\n#define\tQ2LUMP_VISIBILITY\t\t3\n#define\tQ2LUMP_NODES\t\t\t4\n#define\tQ2LUMP_TEXINFO\t\t5\n#define\tQ2LUMP_FACES\t\t\t6\n#define\tQ2LUMP_LIGHTING\t\t7\n#define\tQ2LUMP_LEAFS\t\t\t8\n#define\tQ2LUMP_LEAFFACES\t\t9\n#define\tQ2LUMP_LEAFBRUSHES\t10\n#define\tQ2LUMP_EDGES\t\t\t11\n#define\tQ2LUMP_SURFEDGES\t\t12\n#define\tQ2LUMP_MODELS\t\t\t13\n#define\tQ2LUMP_BRUSHES\t\t14\n#define\tQ2LUMP_BRUSHSIDES\t\t15\n#define\tQ2LUMP_POP\t\t\t16\n#define\tQ2LUMP_AREAS\t\t\t17\n#define\tQ2LUMP_AREAPORTALS\t18\n#define\tQ2HEADER_LUMPS\t\t19\n\nenum Q3LUMP\n{\t\n\tQ3LUMP_ENTITIES\t\t=0,\n\tQ3LUMP_SHADERS\t\t=1,\n\tQ3LUMP_PLANES\t\t=2,\n\tQ3LUMP_NODES\t\t=3,\n\tQ3LUMP_LEAFS\t\t=4,\n\tQ3LUMP_LEAFSURFACES\t=5,\n\tQ3LUMP_LEAFBRUSHES\t=6,\n\tQ3LUMP_MODELS\t\t=7,\n\tQ3LUMP_BRUSHES\t\t=8,\n\tQ3LUMP_BRUSHSIDES\t=9,\n\tQ3LUMP_DRAWVERTS\t=10,\n\tQ3LUMP_DRAWINDEXES\t=11,\n\tQ3LUMP_FOGS\t\t\t=12,\n\tQ3LUMP_SURFACES\t\t=13,\n\tQ3LUMP_LIGHTMAPS\t=14,\n\tQ3LUMP_LIGHTGRID\t=15,\n\tQ3LUMP_VISIBILITY\t=16,\n#ifdef RFBSPS\n\tRBSPLUMP_LIGHTINDEXES=17,\n#endif\n\tQ3LUMPS_TOTAL\n};\n\ntypedef struct\n{\n\tint ident;\n\tint\t\t\tversion;\t\n\tlump_t\t\tlumps[50];\n} q2dheader_t;\n\ntypedef struct\n{\n\tfloat\t\tmins[3], maxs[3];\n\tfloat\t\torigin[3];\t\t// for sounds or lights\n\tint\t\t\theadnode;\n\tint\t\t\tfirstface, numfaces;\t// submodels just draw faces\n\t\t\t\t\t\t\t\t\t\t// without walking the bsp tree\n} q2dmodel_t;\n\ntypedef struct\n{\n\tfloat mins[3];\n\tfloat maxs[3];\n\tint firstsurface;\n\tint num_surfaces;\n\tint firstbrush;\n\tint num_brushes;\n} q3dmodel_t;\n\n\n\n// 0-2 are axial planes\n#define\tPLANE_X\t\t\t0\n#define\tPLANE_Y\t\t\t1\n#define\tPLANE_Z\t\t\t2\n\n// 3-5 are non-axial planes snapped to the nearest\n#define\tPLANE_ANYX\t\t3\n#define\tPLANE_ANYY\t\t4\n#define\tPLANE_ANYZ\t\t5\n\n\n\n\n// contents flags are seperate bits\n// a given brush can contribute multiple content bits\n// multiple brushes can be in a single leaf\n\n#define\tFTECONTENTS_EMPTY\t\t\t0x00000000\n#define\tFTECONTENTS_SOLID\t\t\t0x00000001\n#define FTECONTENTS_WINDOW\t\t\t0x00000002\t//solid to bullets, but not sight/agro\n//q2aux\t\t\t\t\t\t\t\t0x00000004\n#define\tFTECONTENTS_LAVA\t\t\t0x00000008\n#define\tFTECONTENTS_SLIME\t\t\t0x00000010\n#define\tFTECONTENTS_WATER\t\t\t0x00000020\n#define FTECONTENTS_FLUID\t\t\t(FTECONTENTS_WATER|FTECONTENTS_SLIME|FTECONTENTS_LAVA|FTECONTENTS_SKY)\t//sky is a fluid for q1 code.\n//q2mist\t\t\t\t\t\t\t0x00000040\n//q3notteam1\t\t\t\t\t\t0x00000080\n//q3notteam2\t\t\t\t\t\t0x00000100\n//q3nobotclip\t\t\t\t\t\t0x00000200\n//\t\t\t\t\t\t\t\t\t0x00000400\n//\t\t\t\t\t\t\t\t\t0x00000800\n//\t\t\t\t\t\t\t\t\t0x00001000\n//\t\t\t\t\t\t\t\t\t0x00002000\n#define FTECONTENTS_LADDER\t\t\t0x00004000\n//q2areaportal,q3areaportal\t\t\t0x00008000\n#define FTECONTENTS_PLAYERCLIP\t\t0x00010000\n#define FTECONTENTS_MONSTERCLIP\t\t0x00020000\n//q2current0,q3teleporter\t\t\t0x00040000\n//q2current90,q3jumppad\t\t\t\t0x00080000\n//q2current180,q3clusterportal\t\t0x00100000\n//q2current270,q3donotenter\t\t\t0x00200000\n//q2currentup,q3botclip\t\t\t\t0x00400000\n//q2currentdown,q3mover\t\t\t\t0x00800000\n//q2origin,q3origin\t\t\t\t\t0x01000000\t//could define, but normally removed by compiler, so why?\n#define FTECONTENTS_BODY\t\t\t0x02000000\n#define FTECONTENTS_CORPSE\t\t\t0x04000000\n#define FTECONTENTS_DETAIL\t\t\t0x08000000\t//not very useful to us, but used by .map support\n//q2translucent,q3structual\t\t\t0x10000000\n//q2ladder,q3translucent\t\t\t0x20000000\n//q3trigger\t\t\t\t\t\t\t0x40000000\n#define\tFTECONTENTS_SKY/*q3nodrop*/\t0x80000000\n\n// lower bits are stronger, and will eat weaker brushes completely\n#define\tQ2CONTENTS_SOLID\t\tFTECONTENTS_SOLID\t\t//0x00000001\n#define\tQ2CONTENTS_WINDOW\t\t\t\t\t\t\t\t  0x00000002\t\t// translucent, but not watery\n#define\tQ2CONTENTS_AUX\t\t\t\t\t\t\t\t\t  0x00000004\n#define\tQ2CONTENTS_LAVA\t\t\tFTECONTENTS_LAVA\t\t//0x00000008\n#define\tQ2CONTENTS_SLIME\t\tFTECONTENTS_SLIME\t\t//0x00000010\n#define\tQ2CONTENTS_WATER\t\tFTECONTENTS_WATER\t\t//0x00000020\n#define\tQ2CONTENTS_MIST\t\t\t\t\t\t\t\t\t  0x00000040\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t//0x00000080\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t//0x00000100\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t//0x00000200\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t//0x00000400\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t//0x00000800\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t//0x00001000\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t//0x00002000\n\t\t\t\t\t\t\t\t//FTECONTENTS_LADDER\t//0x00004000\n// remaining contents are non-visible, and don't eat brushes\n#define\tQ2CONTENTS_AREAPORTAL\t\t\t\t\t\t\t  0x00008000\n#define\tQ2CONTENTS_PLAYERCLIP\tFTECONTENTS_PLAYERCLIP\t//0x00010000\n#define\tQ2CONTENTS_MONSTERCLIP\tFTECONTENTS_MONSTERCLIP\t//0x00020000\n// currents can be added to any other contents, and may be mixed\n#define\tQ2CONTENTS_CURRENT_0\t\t\t\t\t\t\t  0x00040000\n#define\tQ2CONTENTS_CURRENT_90\t\t\t\t\t\t\t  0x00080000\n#define\tQ2CONTENTS_CURRENT_180\t\t\t\t\t\t\t  0x00100000\n#define\tQ2CONTENTS_CURRENT_270\t\t\t\t\t\t\t  0x00200000\n#define\tQ2CONTENTS_CURRENT_UP\t\t\t\t\t\t\t  0x00400000\n#define\tQ2CONTENTS_CURRENT_DOWN\t\t\t\t\t\t\t  0x00800000\n#define\tQ2CONTENTS_ORIGIN\t\t\t\t\t\t\t\t  0x01000000\t// removed before bsping an entity\n#define\tQ2CONTENTS_MONSTER\t\tFTECONTENTS_BODY\t\t//0x02000000\t// should never be on a brush, only in game\n#define\tQ2CONTENTS_DEADMONSTER\tFTECONTENTS_CORPSE\t\t//0x04000000\n#define\tQ2CONTENTS_DETAIL\t\tFTECONTENTS_DETAIL\t\t// 0x08000000\t// brushes to be added after vis leafs\n#define\tQ2CONTENTS_TRANSLUCENT\t\t\t\t\t\t\t  0x10000000\t// auto set if any surface has trans\n#define\tQ2CONTENTS_LADDER\t\t\t\t\t\t\t\t  0x20000000\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t//0x40000000\n\t\t\t\t\t\t\t\t//FTECONTENTS_SKY\t\t//0x80000000\n\n\n#define\tQ3CONTENTS_SOLID\t\tFTECONTENTS_SOLID\t\t//0x00000001\t// should never be on a brush, only in game\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t//0x00000002\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t//0x00000004\n#define\tQ3CONTENTS_LAVA\t\t\tFTECONTENTS_LAVA\t\t//0x00000008\n#define\tQ3CONTENTS_SLIME\t\tFTECONTENTS_SLIME\t\t//0x00000010\n#define\tQ3CONTENTS_WATER\t\tFTECONTENTS_WATER\t\t//0x00000020\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t//0x00000040\n#define Q3CONTENTS_NOTTEAM1\t\t\t\t\t\t\t\t  0x00000080\n#define Q3CONTENTS_NOTTEAM2\t\t\t\t\t\t\t\t  0x00000100\n#define Q3CONTENTS_NOBOTCLIP\t\t\t\t\t\t\t  0x00000200\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t//0x00000400\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t//0x00000800\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t//0x00001000\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t//0x00002000\n\t\t\t\t\t\t\t\t//FTECONTENTS_LADDER\t//0x00004000\n#define Q3CONTENTS_AREAPORTAL\t\t\t\t\t\t\t  0x00008000\n#define\tQ3CONTENTS_PLAYERCLIP\tFTECONTENTS_PLAYERCLIP\t//0x00010000\n#define\tQ3CONTENTS_MONSTERCLIP\tFTECONTENTS_MONSTERCLIP\t//0x00020000\n#define\tQ3CONTENTS_TELEPORTER\t\t\t\t\t\t\t  0x00040000\n#define\tQ3CONTENTS_JUMPPAD\t\t\t\t\t\t\t\t  0x00080000\n#define Q3CONTENTS_CLUSTERPORTAL\t\t\t\t\t\t  0x00100000\n#define Q3CONTENTS_DONOTENTER\t\t\t\t\t\t\t  0x00200000\n#define Q3CONTENTS_BOTCLIP\t\t\t\t\t\t\t\t  0x00400000\n#define Q3CONTENTS_MOVER\t\t\t\t\t\t\t\t  0x00800000\n#define\tQ3CONTENTS_ORIGIN\t\tQ2CONTENTS_ORIGIN\t\t//0x01000000\n#define\tQ3CONTENTS_BODY\t\t\tFTECONTENTS_BODY\t\t//0x02000000\n#define\tQ3CONTENTS_CORPSE\t\tFTECONTENTS_CORPSE\t\t//0x04000000\n#define\tQ3CONTENTS_DETAIL\t\tFTECONTENTS_DETAIL\t\t//0x08000000\n#define\tQ3CONTENTS_STRUCTURAL\t\t\t\t\t\t\t  0x10000000\n#define Q3CONTENTS_TRANSLUCENT\t\t\t\t\t\t\t  0x20000000\n#define\tQ3CONTENTS_TRIGGER\t\t\t\t\t\t\t\t  0x40000000\n#define\tQ3CONTENTS_NODROP\t\tFTECONTENTS_SKY\t\t\t//0x80000000\n\n//qc compat only. not used internally.\n#define DPCONTENTS_SOLID\t\t1 // hit a bmodel, not a bounding box\n#define DPCONTENTS_WATER\t\t2\n#define DPCONTENTS_SLIME\t\t4\n#define DPCONTENTS_LAVA\t\t\t8\n#define DPCONTENTS_SKY\t\t\t16\n#define DPCONTENTS_BODY\t\t\t32 // hit a bounding box, not a bmodel\n#define DPCONTENTS_CORPSE\t\t64 // hit a SOLID_CORPSE entity\n#define DPCONTENTS_NODROP\t\t128 // an area where backpacks should not spawn\n#define DPCONTENTS_PLAYERCLIP\t256 // blocks player movement\n#define DPCONTENTS_MONSTERCLIP\t512 // blocks monster movement\n#define DPCONTENTS_DONOTENTER\t1024 // AI hint brush\n#define DPCONTENTS_BOTCLIP\t\t2048 // AI hint brush\n#define DPCONTENTS_OPAQUE\t\t4096 // only fully opaque brushes get this (may be useful for line of sight checks)\n\n\n//Texinfo flags - warning: these mix with q3 surface flags\n#define\tTI_LIGHT\t\t0x1\t\t// value will hold the light strength\n\n#define\tTI_SLICK\t\t0x2\t\t// effects game physics\n\n#define\tTI_SKY\t\t\t0x4\t\t// don't draw, but add to skybox\n#define\tTI_WARP\t\t\t0x8\t\t// turbulent water warp\n#define\tTI_TRANS33\t\t0x10\n#define TI_TRANS66\t\t0x20\n#define\tTI_FLOWING\t\t0x40\t// scroll towards angle\n#define\tTI_NODRAW\t\t0x80\t// don't bother referencing the texture\n//#define\tTI_HINT\t\t\t0x100\t// handled by tools, engine can ignore.\n//#define\tTI_SKIP\t\t\t0x200\t// handled by tools, engine can ignore.\n\n//#define TI_KINGPIN_SPECULAR\t0x400\n//#define TI_KINGPIN_DIFFUSE\t0x800\n#define TI_KINGPIN_ALPHATEST\t0x1000\t//regular alphatest\n//#define TI_KINGPIN_MIRROR\t\t0x2000\n//#define TI_KINGPIN_WNDW33\t\t0x4000\n//#define TI_KINGPIN_WNDW64\t\t0x8000\n\n#define TI_Q2EX_ALPHATEST\t(1u<<25)\t//seems to be a thing in other pre q2e engines too.\n#define TI_N64_UV\t\t\t(1u<<28)\t//2 qu per texel instead of 1... (still 16qu per luxel)\n#define TI_N64_SCROLL_X\t\t(1u<<29)\t//scrolls fully right each second.\n#define TI_N64_SCROLL_Y\t\t(1u<<30)\t//scrolls fully down each second.\n#define TI_N64_SCROLL_FLIP\t(1u<<31)\t//reverses the scroll dirs.\n\n//Surface flags\n//#define Q3SURFACEFLAG_NODAMAGE\t0x1\t\t// never give falling damage\n//#define Q3SURFACEFLAG_SLICK\t\t0x2\t\t// effects game physics\n//#define Q3SURFACEFLAG_SKY\t\t\t0x4\t\t// lighting from environment map\n#define\tQ3SURFACEFLAG_LADDER\t\t0x8\n//#define Q3SURFACEFLAG_NOIMPACT\t0x10\t// don't make missile explosions\n//#define Q3SURFACEFLAG_NOMARKS\t\t0x20\t// don't leave missile marks\n//#define Q3SURFACEFLAG_FLESH\t\t0x40\t// make flesh sounds and effects\n//#define Q3SURFACEFLAG_NODRAW\t\t0x80\t// don't generate a drawsurface at all\n//#define Q3SURFACEFLAG_HINT\t\t0x100\t// make a primary bsp splitter\n//#define Q3SURFACEFLAG_SKIP\t\t0x200\t// completely ignore, allowing non-closed brushes\n//#define Q3SURFACEFLAG_NOLIGHTMAP\t0x400\t// surface doesn't need a lightmap\n//#define Q3SURFACEFLAG_POINTLIGHT\t0x800\t// generate lighting info at vertexes\n//#define Q3SURFACEFLAG_METALSTEPS\t0x1000\t// clanking footsteps\n//#define Q3SURFACEFLAG_NOSTEPS\t\t0x2000\t// no footstep sounds\n//#define Q3SURFACEFLAG_NONSOLID\t0x4000\t// don't collide against curves with this set\n//#define Q3SURFACEFLAG_LIGHTFILTER\t0x8000\t// act as a light filter during q3map -light\n//#define Q3SURFACEFLAG_ALPHASHADOW\t0x10000\t// do per-pixel light shadow casting in q3map\n//#define Q3SURFACEFLAG_NODLIGHT\t0x20000\t// don't dlight even if solid (solid lava, skies)\n//#define Q3SURFACEFLAG_DUST\t\t0x40000 // leave a dust trail when walking on this surface\n\n// content masks. Allow q2contents_window in here\n//#define\tMASK_ALL\t\t\t\t(-1)\n#define\tMASK_WORLDSOLID\t\t\t\t(FTECONTENTS_SOLID|FTECONTENTS_WINDOW)\t/*default trace type for something simple that ignores non-bsp stuff*/\n#define\tMASK_POINTSOLID\t\t\t\t(FTECONTENTS_SOLID|FTECONTENTS_WINDOW|FTECONTENTS_BODY)\t/*default trace type for an entity of no size*/\n#define\tMASK_BOXSOLID\t\t\t\t(FTECONTENTS_SOLID|FTECONTENTS_PLAYERCLIP|Q2CONTENTS_WINDOW|FTECONTENTS_BODY) /*default trace type for an entity that does have size*/\n#define\tMASK_PLAYERSOLID\t\t\tMASK_BOXSOLID\n//#define\tMASK_DEADSOLID\t\t\t(Q2CONTENTS_SOLID|Q2CONTENTS_PLAYERCLIP|Q2CONTENTS_WINDOW)\n//#define\tMASK_MONSTERSOLID\t\t(Q2CONTENTS_SOLID|Q2CONTENTS_MONSTERCLIP|Q2CONTENTS_WINDOW|Q2CONTENTS_MONSTER)\n#define\tMASK_WATER\t\t\t\t\t(FTECONTENTS_WATER|FTECONTENTS_LAVA|FTECONTENTS_SLIME)\n//#define\tMASK_OPAQUE\t\t\t\t(Q2CONTENTS_SOLID|Q2CONTENTS_SLIME|Q2CONTENTS_LAVA)\n//#define\tMASK_SHOT\t\t\t\t(Q2CONTENTS_SOLID|Q2CONTENTS_MONSTER|Q2CONTENTS_WINDOW|Q2CONTENTS_DEADMONSTER)\n#define Q2MASK_CURRENT\t\t\t(Q2CONTENTS_CURRENT_0|Q2CONTENTS_CURRENT_90|Q2CONTENTS_CURRENT_180|Q2CONTENTS_CURRENT_270|Q2CONTENTS_CURRENT_UP|Q2CONTENTS_CURRENT_DOWN)\n\n\n\ntypedef struct\n{\n\tint\t\t\tplanenum;\n\tint\t\t\tchildren[2];\t// negative numbers are -(leafs+1), not nodes\n\tshort\t\tmins[3];\t\t// for frustom culling\n\tshort\t\tmaxs[3];\n\tunsigned short\tfirstface;\n\tunsigned short\tnumfaces;\t// counting both sides\n} q2dsnode_t;\n\ntypedef struct\n{\n\tint plane;\n\tint children[2];\n\tint mins[3];\n\tint maxs[3];\n} q3dnode_t;\n\n\ntypedef struct q2texinfo_s\n{\n\tfloat\t\tvecs[2][4];\t\t// [s/t][xyz offset]\n\tint\t\t\tflags;\t\t\t// miptex flags + overrides\n\tint\t\t\tvalue;\t\t\t// light emission, etc\n\tchar\t\ttexture[32];\t// texture name (textures/ *.wal)\n\tint\t\t\tnexttexinfo;\t// for animations, -1 = end of chain\n} q2texinfo_t;\n\n\n\ntypedef struct\n{\n\tint\t\t\t\tcontents;\t\t\t// OR of all brushes (not needed?)\n\n\tshort\t\t\tcluster;\n\tshort\t\t\tarea;\n\n\tshort\t\t\tmins[3];\t\t\t// for frustum culling\n\tshort\t\t\tmaxs[3];\n\n\tunsigned short\tfirstleafface;\n\tunsigned short\tnumleaffaces;\n\n\tunsigned short\tfirstleafbrush;\n\tunsigned short\tnumleafbrushes;\n} q2dsleaf_t;\ntypedef struct\n{\n\tint\t\t\t\tcontents;\t\t\t// OR of all brushes (not needed?)\n\n\tint\t\t\tcluster;\n\tint\t\t\tarea;\n\n\tfloat\t\t\tmins[3];\t\t\t// for frustum culling\n\tfloat\t\t\tmaxs[3];\n\n\tunsigned int\tfirstleafface;\n\tunsigned int\tnumleaffaces;\n\n\tunsigned int\tfirstleafbrush;\n\tunsigned int\tnumleafbrushes;\n} q2dlleaf_t;\n\ntypedef struct\n{\n\tint cluster;\n\tint area;\n\tint mins[3];\n\tint maxs[3];\n\tint firstleafsurface;\n\tint num_leafsurfaces;\n\tint firstleafbrush;\n\tint num_leafbrushes;\n} q3dleaf_t;\n\n\ntypedef struct\n{\n\tunsigned short\tplanenum;\t\t// facing out of the leaf\n\tshort\ttexinfo;\n} q2dsbrushside_t;\ntypedef struct\n{\n\tunsigned int\tplanenum;\t\t// facing out of the leaf\n\tint\ttexinfo;\n} q2dlbrushside_t;\n\ntypedef struct\n{\n\tint planenum;\n\tint texinfo;\n} q3dbrushside_t;\ntypedef struct\n{\n\tint planenum;\n\tint texinfo;\n\tint facenum;\n} rbspbrushside_t;\n\ntypedef struct\n{\n\tint\t\t\tfirstside;\n\tint\t\t\tnumsides;\n\tint\t\t\tcontents;\n} q2dbrush_t;\n\n\ntypedef struct\n{\n\tint firstside;\n\tint num_sides;\n\tint shadernum;\n} q3dbrush_t;\n\n#define\tANGLE_UP\t-1\n#define\tANGLE_DOWN\t-2\n\n\n// the visibility lump consists of a header with a count, then\n// qbyte offsets for the PVS and PHS of each cluster, then the raw\n// compressed bit vectors\n#define\tDVIS_PVS\t0\n#define\tDVIS_PHS\t1\ntypedef struct\n{\n\tint\t\t\tnumclusters;\n\tint\t\t\tbitofs[8][2];\t// bitofs[numclusters][2]\n} q2dvis_t;\n\ntypedef struct\n{\n\tint\t\t\t\tnumclusters;\n\tint\t\t\t\trowsize;\n\tunsigned char\tdata[1];\n} q3dvis_t;\n\n// each area has a list of portals that lead into other areas\n// when portals are closed, other areas may not be visible or\n// hearable even if the vis info says that it should be\ntypedef struct\n{\n\tint\t\tportalnum;\n\tint\t\totherarea;\n} q2dareaportal_t;\n\ntypedef struct\n{\n\tint\t\tnumareaportals;\n\tint\t\tfirstareaportal;\n} q2darea_t;\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\ntypedef struct\n{\n\tchar shadername[OLD_MAX_QPATH];\n\tint surfflags;\n\tint contents;\n} dq3shader_t;\n\ntypedef struct\n{\n\tfloat n[3];\n\tfloat d;\n} Q3PLANE_t;\n\nstruct Q3MODEL\n{\n\tfloat mins[3];\n\tfloat maxs[3];\n\tint firstsurface;\n\tint num_surfaces;\n\tint firstbrush;\n\tint num_brushes;\n};\n\n\ntypedef struct\n{\n\tfloat point[3];\n\tfloat texcoords[2][2];\n\tfloat normal[3];\n\tunsigned char color[4];\n} q3dvertex_t;\n\ntypedef struct\n{\n\tfloat point[3];\n\tfloat stcoords[2];\n\tfloat lmtexcoords[RBSP_STYLESPERSURF][2];\n\tfloat normal[3];\n\tunsigned char color[RBSP_STYLESPERSURF][4];\n} rbspvertex_t;\n\nstruct Q3FOG\n{\n\tchar shadername[OLD_MAX_QPATH] ;\n\tint brushnum;\n\tint visibleside;\n};\n\nenum q3surfacetype\n{\n\tMST_BAD=0,\n\tMST_PLANAR=1,\n\tMST_PATCH=2,\n\tMST_TRIANGLE_SOUP=3,\n\tMST_FLARE=4,\n\tMST_FOLIAGE=5,\t//added in wolf/et\n\tMST_PATCH_FIXED=256 //fte, fixed tessellation. Uses high parts of surf->patchwidth/height. if 0 then uses exact CPs instead.\n};\n\ntypedef struct\n{\n\tint shadernum;\n\tint fognum;\n\tint facetype;\n\tint firstvertex;\n\tint num_vertices;\n\tint firstindex;\n\tint num_indexes;\n\tint lightmapnum;\n\tint lightmap_offs[2];\n\tint lightmap_width;\n\tint lightmap_height;\n\tfloat lightmap_origin[3];\n\tfloat lightmap_vecs[2][3];\n\tfloat normal[3];\n\tint patchwidth;\n\tint patchheight;\n} q3dface_t;\n\ntypedef struct\n{\n\tint shadernum;\n\tint fognum;\n\tint facetype;\n\tint firstvertex;\n\tint num_vertices;\n\tint firstindex;\n\tint num_indexes;\n\tunsigned char lm_styles[RBSP_STYLESPERSURF];\n\tunsigned char vt_styles[RBSP_STYLESPERSURF];\n\tint lightmapnum[RBSP_STYLESPERSURF];\n\tint lightmap_offs[2][RBSP_STYLESPERSURF];\t//yes, weird ordering.\n\tint lightmap_width;\n\tint lightmap_height;\n\tfloat lightmap_origin[3];\n\tfloat lightmap_vecs[2][3];\n\tfloat normal[3];\n\tint patchwidth;\n\tint patchheight;\n} rbspface_t;\n\n#define\tMAX_ENT_LEAFS\t32\ntypedef struct pvscache_s\n{\n\tint\t\t\t\tnum_leafs;\t//negative generally means resort-to-headnode.\n\tunsigned int\tleafnums[MAX_ENT_LEAFS];\n\tint\t\t\t\tareanum;\n\tint\t\t\t\tareanum2;\n\tint\t\t\t\theadnode;\n} pvscache_t;\n"
  },
  {
    "path": "engine/common/cmd.c",
    "content": "/*\nCopyright (C) 1996-1997 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n*/\n// cmd.c -- Quake script command processing module\n\n#include \"quakedef.h\"\n#include \"fs.h\"\n#include \"cl_master.h\"\n\ncvar_t ruleset_allow_in\t\t= CVAR(\"ruleset_allow_in\", \"1\");\ncvar_t rcon_level\t\t\t= CVAR(\"rcon_level\", \"20\");\ncvar_t cmd_maxbuffersize\t= CVAR(\"cmd_maxbuffersize\", \"65536\");\n#ifdef HAVE_LEGACY\ncvar_t dpcompat_set         = CVAR(\"dpcompat_set\", \"0\");\ncvar_t dpcompat_console     = CVARD(\"dpcompat_console\", \"0\", \"Enables hacks to emulate DP's console.\");\n#else\nstatic const cvar_t dpcompat_set = {0};\nstatic const cvar_t dpcompat_console = {0};\n#endif\nint\tCmd_ExecLevel;\nqboolean cmd_didwait;\nqboolean cmd_blockwait;\nstatic qboolean cmd_stuffedcmdline;\n\nvoid Cmd_ForwardToServer (void);\n\n#define\tMAX_ALIAS_NAME\t32\n\ntypedef struct cmdalias_s\n{\n\tstruct cmdalias_s\t*next;\n\tchar\t*value;\n\tint flags;\n\tqbyte execlevel;\n\tqbyte restriction;\n\tchar\tname[1];\n} cmdalias_t;\n\n#define ALIAS_FROMSERVER\t1\n\ncmdalias_t\t*cmd_alias;\n\ncvar_t\tcfg_save_all = CVARFD(\"cfg_save_all\", \"\", CVAR_ARCHIVE|CVAR_NOTFROMSERVER, \"If 1, cfg_save ALWAYS saves all cvars. If 0, cfg_save only ever saves archived cvars. If empty, cfg_save saves all cvars only when an explicit filename was given (ie: when not used internally via quit menu options).\");\ncvar_t\tcfg_save_auto = CVARFD(\"cfg_save_auto\", \"0\", CVAR_ARCHIVE|CVAR_NOTFROMSERVER, \"If 1, the config will automatically be saved and without prompts. If 0, you'll have to save your config manually (possibly via prompts from the quit menu).\");\ncvar_t\tcfg_save_infos = CVARFD(\"cfg_save_infos\", \"1\", CVAR_ARCHIVE|CVAR_NOTFROMSERVER, \"If 1, saves userinfo and serverinfo to configs.\");\ncvar_t\tcfg_save_aliases = CVARFD(\"cfg_save_aliases\", \"1\", CVAR_ARCHIVE|CVAR_NOTFROMSERVER, \"If 1, saves aliases to configs. Note that aliases sent from servers are assumed to be server-specific and are never saved (and are forgotten on map changes, too).\");\ncvar_t\tcfg_save_binds = CVARFD(\"cfg_save_binds\", \"1\", CVAR_ARCHIVE|CVAR_NOTFROMSERVER, \"If 1, saves all key bindings to configs.\");\ncvar_t\tcfg_save_buttons = CVARFD(\"cfg_save_buttons\", \"0\", CVAR_ARCHIVE|CVAR_NOTFROMSERVER, \"If 1, saves the state of things such as +mlook or +forward to configs.\");\n\ncvar_t cl_warncmd\t\t\t= CVARF(\"cl_warncmd\", \"1\", CVAR_NOSAVE|CVAR_NORESET);\ncvar_t cl_aliasoverlap\t\t= CVARFD(\"cl_aliasoverlap\", \"1\", CVAR_NOTFROMSERVER, \"Rename new aliases if they would override cvar names.\");\n\n#ifdef HAVE_CLIENT\ncvar_t tp_disputablemacros\t= CVARF(\"tp_disputablemacros\", \"1\", CVAR_SEMICHEAT);\n#endif\n\n\n//=============================================================================\n\n\n\n\n\n\n\n\n\n\n#define MAX_MACROS 70\n\ntypedef struct {\n\tchar name[32];\n\tchar *(*func) (void);\n\tint disputableintentions;\n} macro_command_t;\n\nstatic macro_command_t macro_commands[MAX_MACROS];\nstatic int macro_count = 0;\n\nvoid Cmd_AddMacro(char *s, char *(*f)(void), int disputableintentions)\n{\n\tint i;\n\tfor (i = 0; i < macro_count; i++)\n\t{\n\t\tif (!strcmp(macro_commands[i].name, s))\n\t\t\tbreak;\n\t}\n\n\tif (i == MAX_MACROS)\n\t\tSys_Error(\"Cmd_AddMacro: macro_count == MAX_MACROS\");\n\n\tQ_strncpyz(macro_commands[i].name, s, sizeof(macro_commands[macro_count].name));\n\tmacro_commands[i].func = f;\n\tmacro_commands[i].disputableintentions = disputableintentions;\n\n\tif (i == macro_count)\n\t\tmacro_count++;\n}\n\nstatic char *TP_MacroString (char *s, int *newaccesslevel, int *len)\n{\n\tint i;\n\tmacro_command_t\t*macro;\n\n\tfor (i = 0; i < macro_count; i++)\n\t{\n\t\tmacro = &macro_commands[i];\n\t\tif (!Q_strcasecmp(s, macro->name))\n\t\t{\n#ifdef HAVE_CLIENT\n\t\t\tif (macro->disputableintentions)\n\t\t\t\tif (!tp_disputablemacros.ival)\n\t\t\t\t\t*newaccesslevel = 0;\n#endif\n\t\t\tif (len)\n\t\t\t\t*len = strlen(macro->name);\n\t\t\treturn macro->func();\n\t\t}\n\t}\n\treturn NULL;\n}\n\nstatic void Cmd_MacroList_f (void)\n{\n\tint\ti;\n\n\tif (!macro_count)\n\t{\n\t\tCon_Printf(\"No macros!\");\n\t\treturn;\n\t}\n\n\tfor (i = 0; i < macro_count; i++)\n\t\tCon_Printf (\"$%s\\n\", macro_commands[i].name);\n}\n\n\nstatic void Cmd_MacroCompletion_c(int argn, const char *partial, struct xcommandargcompletioncb_s *ctx)\n{\n\tsize_t i, len;\n\tconst char *end = partial;\n\tif (*end++ != '$')\n\t\treturn;\n\tif (*end == '{')\n\t\tend++;\n\tlen = strlen(end);\n\n\tfor (i = 0; i < macro_count; i++)\n\t{\n\t\tif (len <= strlen(macro_commands[i].name))\n\t\t\tif (!strncmp(end, macro_commands[i].name, len))\n\t\t\t\tctx->cb(va(\"${%s}\", macro_commands[i].name), NULL, NULL, ctx);\n\t}\n}\n\n\n\n\n\n/*\n=============================================================================\n\n\t\t\t\t\t\tCOMMAND BUFFER\n\n=============================================================================\n*/\n\nstruct {\n\tsizebuf_t\tbuf;\n\tint noclear;\n\tdouble waitattime;\n} cmd_text[RESTRICT_MAX+3+MAX_SPLITS];\t//max is local.\n\t\t\t\t\t\t\t//RESTRICT_MAX+1 is the from sever buffer (max+2 is for second player...)\n\nvoid Cbuf_Waited(void)\n{\n\t//input packet was sent to server, its okay to continue executing stuff like -attack now\n\tcmd_text[RESTRICT_LOCAL].waitattime = 0;\n}\n\n/*\n============\nCmd_Wait_f\n\nCauses execution of the remainder of the command buffer to be delayed until\nnext frame.  This allows commands like:\nbind g \"impulse 5 ; +attack ; wait ; -attack ; impulse 2\"\n============\n*/\nstatic void Cmd_Wait_f (void)\n{\n\tif (cmd_blockwait)\n\t\treturn;\n\n#ifdef HAVE_SERVER\n\tif (cmd_didwait && sv.state)\n\t\tCon_DPrintf(\"waits without server frames\\n\");\n#endif\n\tcmd_didwait = true;\n\tcmd_text[Cmd_ExecLevel].waitattime = realtime;\n}\n\n/*\nlame timers. :s\n*/\ntypedef struct cmdtimer_s {\n\tstruct cmdtimer_s *next;\n\tfloat timer;\n\tint iarg;\n\tvoid(*callback)(int iarg, void *data);\n\tchar data[1];\n} cmdtimer_t;\nstatic cmdtimer_t *cmdtimers;\nstatic void Cmd_ExecuteTimers(void)\n{\n\tcmdtimer_t **link, *t;\n\t//FIXME: we should probably insert these in order instead, then early out.\n\t//really, it depends on just how many we end up with\n\tfor(link = &cmdtimers; (t = *link); )\n\t{\n\t\tif (t->timer < realtime)\n\t\t{\n\t\t\t*link = t->next;\n\t\t\tt->callback(t->iarg, t->data);\n\t\t\tZ_Free(t);\n\t\t}\n\t\telse\n\t\t\tlink = &t->next;\n\t}\n}\nvoid Cmd_AddTimer(float delay, void(*callback)(int iarg, void *data), int iarg, void *data, size_t datasize)\n{\n\tcmdtimer_t *n = Z_Malloc(sizeof(*n) + datasize);\n\tn->iarg = iarg;\n\tn->callback = callback;\n\tmemcpy(n->data, data, datasize);\n\tn->data[datasize] = 0;\t//just in case.\n\n\tn->timer = realtime + delay;\n\n\tFTE_Atomic_Insert(cmdtimers, n, n->next);\n}\nstatic void Cmd_In_Callback(int iarg, void *data)\n{\n\tCbuf_AddText((char*)data, iarg);\n\tCbuf_AddText(\"\\n\", iarg);\n}\nstatic void Cmd_In_f(void)\n{\n\tfloat delay = atof(Cmd_Argv(1));\n\tchar *cmd;\n\tif (Cmd_Argc() < 3)\n\t{\n\t\tCon_Printf(\"%s <seconds to wait> <command to execute>\\n\", Cmd_Argv(0));\n\t\treturn;\n\t}\n\tCmd_ShiftArgs(1, false);\n\tcmd = Cmd_Args();\n\n\tif (ruleset_allow_in.ival || !delay)\n\t\tCmd_AddTimer(delay, Cmd_In_Callback, Cmd_ExecLevel, cmd, strlen(cmd));\n}\n\n/*\n============\nCbuf_Init\n============\n*/\nvoid Cbuf_Init (void)\n{\n\tint level;\n\tfor (level = 0; level <= RESTRICT_MAX+1; level++)\n\t\tcmd_text[level].waitattime = -1;\n}\n\nstatic void Cbuf_WorkerAddText(void *ctx, void *data, size_t a, size_t b)\n{\n\tCbuf_AddText(data, a);\n\tZ_Free(data);\n}\n/*\n============\nCbuf_AddText\n\nAdds command text at the end of the buffer\n============\n*/\nvoid Cbuf_AddText (const char *text, int level)\n{\n\tint\t\tl;\n\n\tif (!Sys_IsMainThread())\n\t{\n\t\tCOM_AddWork(WG_MAIN, Cbuf_WorkerAddText, NULL, Z_StrDup(text), level, 0);\n\t\treturn;\n\t}\n\n\tif (level > sizeof(cmd_text)/sizeof(cmd_text[0]) || level < 0)\n\t{\n\t\tCon_Printf(\"Bad execution level\\n\");\n\t\treturn;\t//reject.\n\t}\n\n\tl = Q_strlen (text);\n\n\tif (!cmd_text[level].buf.maxsize)\n\t{\n\t\tcmd_text[level].buf.data = (qbyte*)BZ_Malloc(8192);\n\t\tcmd_text[level].buf.maxsize = 8192;\n\t}\n\tif (cmd_text[level].buf.cursize + l >= cmd_text[level].buf.maxsize)\n\t{\n\t\tint newmax;\n\n\t\tnewmax = cmd_text[level].buf.maxsize*2;\n\n\t\tif (newmax > cmd_maxbuffersize.ival && cmd_maxbuffersize.ival)\n\t\t{\n\t\t\tCon_TPrintf (\"%s: overflow\\n\", \"Cbuf_AddText\");\n\t\t\treturn;\n\t\t}\n\t\twhile (newmax < cmd_text[level].buf.cursize + l)\n\t\t\tnewmax*=2;\n\t\tcmd_text[level].buf.data = (qbyte*)BZ_Realloc(cmd_text[level].buf.data, newmax);\n\t\tcmd_text[level].buf.maxsize = newmax;\n\t}\n\tSZ_Write (&cmd_text[level].buf, text, Q_strlen (text));\n}\n\n\n/*\n============\nCbuf_InsertText\n\nAdds command text immediately after the current command\nAdds a \\n to the text\nFIXME: actually change the command buffer to do less copying\n============\n*/\nvoid Cbuf_InsertText (const char *text, int level, qboolean addnl)\n{\n\tchar\t*temp;\n\tint\t\ttemplen;\n\n\tif (level > sizeof(cmd_text)/sizeof(cmd_text[0]) || level < 0)\n\t{\n\t\tCon_Printf(\"Bad execution level\\n\");\n\t\treturn;\t//reject.\n\t}\n\n// copy off any commands still remaining in the exec buffer\n\ttemplen = cmd_text[level].buf.cursize;\n\tif (templen)\n\t{\n\t\ttemp = (char*)Z_Malloc (templen+1);\n\t\tQ_memcpy (temp, cmd_text[level].buf.data, templen);\n\t\tSZ_Clear (&cmd_text[level].buf);\n\t}\n\telse\n\t\ttemp = NULL;\t// shut up compiler\n\n// add the entire text of the file\n\tCbuf_AddText (text, level);\n\tif (addnl)\n\t\tCbuf_AddText (\"\\n\", level);\n// add the copied off data\n\tif (templen)\n\t{\n\t\ttemp[templen] = '\\0';\n\t\tCbuf_AddText(temp, level);\n//\t\tSZ_Write (&cmd_text[level].buf, temp, templen);\n\t\tZ_Free (temp);\n\t}\n}\n\nchar *Cbuf_GetNext(int level, qboolean ignoresemicolon)\n{\n\tint\t\ti;\n\tchar\t*text;\n\tint\t\tquotes;\n\tstatic char\tline[1024];\n\nstart:\n\n\ttext = (char *)cmd_text[level].buf.data;\n\n\tquotes = 0;\n\tfor (i=0 ; i< cmd_text[level].buf.cursize ; i++)\n\t{\n\t\tif (text[i] == '\"')\n\t\t\tquotes++;\n\t\tif ( !(quotes&1) &&  text[i] == ';' && !ignoresemicolon)\n\t\t\tbreak;\t// don't break if inside a quoted string\n\t\tif (text[i] == '\\n')\n\t\t\tbreak;\n\t}\n\n\tif (i >= sizeof(line)-1)\n\t{\n\t\tCon_Printf(\"Statement too long\\n\");\n\t\treturn \"\";\n\t}\n\n\n\tmemcpy (line, text, i);\n\tline[i] = 0;\n\n// delete the text from the command buffer and move remaining commands down\n// this is necessary because commands (exec, alias) can insert data at the\n// beginning of the text buffer\n\n\tif (i == cmd_text[level].buf.cursize)\n\t\tcmd_text[level].buf.cursize = 0;\n\telse\n\t{\n\t\ti++;\n\t\tcmd_text[level].buf.cursize -= i;\n\t\tmemmove (text, text+i, cmd_text[level].buf.cursize);\n\t}\n\n//\tCon_Printf(\"Found \\\"%s\\\"\\n\", line);\n\ttext=line;\n\twhile(*text == ' ' || *text == '\\t')\n\t\ttext++;\n\n\tif (!*text)\n\t\tif (cmd_text[level].buf.cursize)\n\t\t\tgoto start;\t//should be a while.\n\n\treturn text;\n}\n\nstatic char *Cbuf_StripText(int level)\t//remove all text in the command buffer and return it (so it can be readded later)\n{\n\tchar *buf;\n\tbuf = (char*)Z_Malloc(cmd_text[level].buf.cursize+1);\n\tQ_memcpy (buf, cmd_text[level].buf.data, cmd_text[level].buf.cursize);\n\tcmd_text[level].buf.cursize = 0;\n\treturn buf;\n}\n\nvoid Cbuf_ExecuteLevel (int level)\n{\n\tint\t\ti;\n\tchar\t*text;\n\tchar\tlinebuf[65536], *line;\n\tqboolean\tcomment;\n\tint\t\tquotes;\n\n\twhile (cmd_text[level].buf.cursize)\n\t{\n\t\tif (cmd_text[level].waitattime == realtime)\n\t\t{\t// skip out while text still remains in buffer, leaving it\n\t\t\t// for next frame\n\t\t\tbreak;\n\t\t}\n\n// find a \\n or ; line break\n\t\ttext = (char *)cmd_text[level].buf.data;\n\n\t\tquotes = false;\n\t\tcomment = false;\n\t\tfor (i=0 ; i< cmd_text[level].buf.cursize ; i++)\n\t\t{\n\t\t\tif (text[i] == '\\n')\n\t\t\t\tbreak;\n\n\t\t\tif (quotes)\n\t\t\t{\n\t\t\t\tif (text[i] == '\"')\n\t\t\t\t{\n\t\t\t\t\tquotes=false;\n\t\t\t\t}\n\t\t\t\tif (text[i] == '\\\\' && quotes==2)\n\t\t\t\t{\n\t\t\t\t\t//skip over both chars if its something embedded.\n\t\t\t\t\tif (text[i+1] == '\\\"' || text[i+1] == '\\\\')\n\t\t\t\t\t{\n\t\t\t\t\t\ti++;\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\telse if (text[i] == '\"')\n\t\t\t{\t//simple quoted string\n\t\t\t\tquotes = true;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\telse if (text[i] == '\\\\' && text[i+1] == '\\\"')\n\t\t\t{\t//escaped quoted string.\n\t\t\t\tquotes = 2;\n\t\t\t\ti++;\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (comment)\n\t\t\t\tcontinue;\n\n\t\t\tif (text[i] == '/' && i+1 < cmd_text[level].buf.cursize && text[i+1] == '/')\n\t\t\t\tcomment = true;\n\t\t\telse if (text[i] == ';')\n\t\t\t\tbreak;\t// don't break if inside a quoted string\n\t\t}\n\n\t\tif (i >= sizeof(linebuf))\n\t\t\tline = malloc(i+1);\t//might leak if the command longjmps. :(\n\t\telse\n\t\t\tline = linebuf;\n\t\tmemcpy (line, text, i);\n\t\tline[i] = 0;\n\n// delete the text from the command buffer and move remaining commands down\n// this is necessary because commands (exec, alias) can insert data at the\n// beginning of the text buffer\n\n\t\tif (i == cmd_text[level].buf.cursize)\n\t\t\tcmd_text[level].buf.cursize = 0;\n\t\telse\n\t\t{\n\t\t\ti++;\n\t\t\tcmd_text[level].buf.cursize -= i;\n\t\t\tmemmove (text, text+i, cmd_text[level].buf.cursize);\n\t\t}\n\n// execute the command line\n\t\tCmd_ExecuteString (line, level);\n\t\tif (line != linebuf)\n\t\t\tfree(line);\n\t}\n}\n\n/*\n============\nCbuf_Execute\n============\n*/\nvoid Cbuf_Execute (void)\n{\n\tint level;\n\n#ifdef HAVE_CLIENT\n\tif (cmd_text[RESTRICT_LOCAL].waitattime && cls.state == ca_active)\n\t{\n\t\t//keep binds blocked until after the next input frame was sent to the server (at which point it will be cleared\n\t\t//this ensures that wait and +attack etc works synchronously, as though your client never even supported network independance! yay... I guess.\n\t\tcmd_text[RESTRICT_LOCAL].waitattime = realtime;\n\t}\n#endif\n\tCmd_ExecuteTimers();\n\n\tfor (level = 0; level < sizeof(cmd_text)/sizeof(cmd_text[0]); level++)\n\t\tif (cmd_text[level].buf.cursize)\n\t\t\tCbuf_ExecuteLevel(level);\n}\n\n/*\n==============================================================================\n\n\t\t\t\t\t\tSCRIPT COMMANDS\n\n==============================================================================\n*/\n\n/*\n===============\nCmd_StuffCmds_f\n\nAdds command line parameters as script statements\nCommands lead with a +, and continue until a - or another +\nquake +prog jctest.qp +cmd amlev1\nquake -nosound +cmd amlev1\n===============\n*/\nvoid Cmd_StuffCmds (void)\n{\n\tint\t\ti;\n\tint\t\ts;\n\tchar\t*text;\n\tconst char\t*arg;\n\tint\t\tpluscmd = false;\n\n\tif (cmd_stuffedcmdline)\n\t\treturn;\t//don't do it multiple times\n\n// build the combined string to parse from\n\ts = 2;\n\tfor (i=1 ; i<com_argc ; i++)\n\t{\n\t\tif (!com_argv[i])\n\t\t\tcontinue;\t\t// NEXTSTEP nulls out -NXHost\n\t\ts += Q_strlen (com_argv[i])*2 + 4;\n\t}\n\tif (!s)\n\t\treturn;\n\n\ttext = (char*)Z_Malloc (s+2);\n\ttext[0] = 0;\n\tfor (i=1 ; i<com_argc ; i++)\n\t{\n\t\tif (!com_argv[i])\n\t\t\tcontinue;\t\t// NEXTSTEP nulls out -NXHost\n\t\targ = com_argv[i];\n\t\tif ((*arg == '+' || *arg == '-') && !(arg[1] >= '0' && arg[1] <= '9'))\t//we only really want the '+foo arg' commands, split by +/- prefixes. if its a -1 or +1 then that's a numerical parameter, not a separate command!\n\t\t{\n\t\t\tif (*text)\n\t\t\t{\n\t\t\t\tQ_strcat (text, \"\\n\");\n\t\t\t\tCbuf_AddText (text, RESTRICT_LOCAL);\n\t\t\t\t*text = 0;\n\t\t\t}\n\t\t\tpluscmd = *arg++ == '+';\n\t\t}\n\n\t\tif (pluscmd)\n\t\t{\n\t\t\tif (*text)\n\t\t\t\tQ_strcat (text, \" \");\n\t\t\tif (strchr(arg, ' ') || strchr(arg, '\\t') || strchr(arg, '@') || strchr(arg, '/') || strchr(arg, '\\\\'))\n\t\t\t\tCOM_QuotedString(arg, text+strlen(text),s-strlen(text), false);\n\t\t\telse\n\t\t\t\tQ_strcat (text,arg);\n\t\t}\n\t}\n\n\tif (*text)\n\t{\n\t\tQ_strcat (text, \"\\n\");\n\t\tCbuf_AddText (text, RESTRICT_LOCAL);\n\t}\n\n\tZ_Free (text);\n\n\tcmd_stuffedcmdline = true;\t//don't add multiple times.\n\n\t//and exec anything added.\n\tCbuf_Execute ();\n}\n\n#if defined(HAVE_LEGACY) && defined(HAVE_CLIENT)\nstatic const char *replacementq1binds =\n\t\"unbindall\\n\"\n\n\t\"bind\t\t`\t\t\ttoggleconsole\\n\"\n\t\"bind\t\tw\t\t\t+forward\\n\"\n\t\"bind\t\ts\t\t\t+back\\n\"\n\t\"bind\t\ta\t\t\t+moveleft\\n\"\n\t\"bind\t\td\t\t\t+moveright\\n\"\n\t\"bind\t\tMOUSE1\t\t+attack\\n\"\n\t\"bind\t\tMOUSE2\t\t+jump\\n\"\n\t\"bind\t\tSPACE\t\t+jump\\n\"\n\t\"bind\t\tv\t\t\t+voip\\n\"\n\t\"bind\t\tt\t\t\tmessagemode2\\n\"\n\t\"bind\t\ty\t\t\tmessagemode\\n\"\n\t\"bind\t\tTAB\t\t\t+showscores\\n\"\n\n\t\"bind\t\te\t\t\t+moveup\\n\"\n\t\"bind\t\tc\t\t\t+movedown\\n\"\n\n\t\"bind\t\tMWHEELUP\timpulse 12\\n\"\n\t\"bind\t\tMWHEELDOWN\timpulse 10\\n\"\n\n\t\"bind\t\tUPARROW\t\t+forward\\n\"\n\t\"bind\t\tDOWNARROW\t+back\\n\"\n\t\"bind\t\tLEFTARROW\t+left\\n\"\n\t\"bind\t\tRIGHTARROW\t+right\\n\"\n\n\t\"bind\t\tLCTRL\t\t+attack\\n\"\n\t\"bind\t\tRCTRL\t\t+attack\\n\"\n\t\"bind\t\tLALT\t\t+strafe\\n\"\n\t\"bind\t\tRALT\t\t+strafe\\n\"\n\t\"bind\t\tLSHIFT\t\t+speed\\n\"\n\t\"bind\t\tRSHIFT\t\t+speed\\n\"\n\n\t\"bind\t\t=\t\t\tsizeup\\n\"\n\t\"bind\t\t-\t\t\tsizedown\\n\"\n\n\t\"bind\t\t1\t\t\timpulse 1\\n\"\n\t\"bind\t\t2\t\t\timpulse 2\\n\"\n\t\"bind\t\t3\t\t\timpulse 3\\n\"\n\t\"bind\t\t4\t\t\timpulse 4\\n\"\n\t\"bind\t\t5\t\t\timpulse 5\\n\"\n\t\"bind\t\t6\t\t\timpulse 6\\n\"\n\t\"bind\t\t7\t\t\timpulse 7\\n\"\n\t\"bind\t\t8\t\t\timpulse 8\\n\"\n//\t\"bind\t\t9\t\t\timpulse 9\\n\"\n//\t\"bind\t\t0\t\t\timpulse 10\\n\"\n\n//\t\"bind\t\tF1\thelp\\n\"\n\t\"bind\t\tF2\tmenu_save\\n\"\n\t\"bind\t\tF3\tmenu_load\\n\"\n\t\"bind\t\tF4\tmenu_options\\n\"\n\t\"bind\t\tF5\tmenu_multiplayer\\n\"\n\t\"bind\t\tF6\tsave quick\\n\"\n//\t\"bind\t\tF7\n//\t\"bind\t\tF8\n\t\"bind\t\tF9\tload quick\\n\"\n\t\"bind\t\tF10\tmenu_quit\\n\"\n//\t\"bind\t\tF11\t+zoom\\n\"\n\t\"bind\t\tF12\tscreenshot\\n\"\n\n\t\"bind\t\tvolup\t\t\\\"if $volume < 0.9 then inc volume 0.1 else if $volume < 1.0 then set volume 1\\\"\\n\"\n\t\"bind\t\tvoldown\t\t\\\"inc volume -0.1; if $volume < 0 then set volume 0\\\"\\n\"\n\t;\nstatic const char *defaulttouchcfg =\n\t\"showpic_removeall\\n\"\n//\t\"sv_aim 0.90\\n\" //quake style, avoid needing to pitch too much\n\t\"showpic touch_moveforward.tga\tfwd\t\t-128\t-112 bm\t32\t32\t+forward\t5\\n\"\n\t\"showpic touch_moveback.tga\t\tback\t-128\t-80\tbm\t32\t32\t+back\t\t5\\n\"\n\t\"showpic touch_moveleft.tga\t\tleft\t-160\t-88\tbm\t32\t32\t+moveleft\t5\\n\"\n\t\"showpic touch_moveright.tga\trght\t-96\t\t-88\tbm\t32\t32\t+moveright\t5\\n\"\n\n\t\"showpic touch_attack.tga\t\tfire\t-160\t-160 bm\t32\t32\t+attack\t\t5\\n\"\n\t\"showpic touch_jump.tga\t\t\tjump\t128\t\t-80\tbm\t32\t32\t+jump\t\t5\\n\"\n\n\t\"showpic touch_weapons.tga\t\tweap\t80\t\t-80\tbm\t32\t32\t+weaponwheel\t5\\n\"\n\t\"showpic touch_menu.tga\t\t\tmenu\t-32\t\t0\ttr\t32\t32\ttogglemenu 10\\n\"\n\t;\n#endif\n\n/*\n===============\nCmd_Exec_f\n===============\n*/\nstatic void Cmd_Exec_f (void)\n{\n\tchar\t*f, *s;\n\tchar\tname[256];\n\tchar\tbuf[512];\n\tflocation_t loc;\n\tqboolean untrusted;\n\tvfsfile_t *file;\n\tsize_t l;\n\tunsigned int level;\n\n\tif (Cmd_Argc () != 2)\n\t{\n\t\tCon_TPrintf (\"exec <filename> : execute a script file\\n\");\n\t\treturn;\n\t}\n\n\n\tif (!strcmp(Cmd_Argv(0), \"cfg_load\"))\n\t{\n\t\tf = Cmd_Argv(1);\n\t\tif (!*f)\n\t\t\tf = fs_manifest->mainconfig;\n\t\tif (!*f)\n\t\t\tf = \"config\";\n\t\tQ_snprintfz(name, sizeof(name)-5, \"configs/%s\", f);\n\t\tCOM_DefaultExtension(name, \".cfg\", sizeof(name));\n\t}\n\telse\n\t{\n\t\tQ_strncpyz(name, Cmd_Argv(1), sizeof(name));\n\t\tif (*fs_manifest->mainconfig && Q_strcasecmp(\"config.cfg\", fs_manifest->mainconfig) && Q_strcasecmp(\"q3config.cfg\", fs_manifest->mainconfig))\n\t\t{\n\t\t\t//fte writes to a different config file from that specified by the quake.rc, to avoid conflicts.\n\t\t\t//so make sure that fte's settings override those from whatever other engine that wrote the legacy config.cfg file.\n\t\t\tif (!strcmp(name, \"config.cfg\") || !strcmp(name, \"q3config.cfg\"))\n\t\t\t{\n\t\t\t\tint cfgdepth = COM_FDepthFile(name, true);\n\t\t\t\tint defdepth = COM_FDepthFile(\"default.cfg\", true);\n\t\t\t\tCbuf_InsertText(va(\"exec %s\", fs_manifest->mainconfig), Cmd_ExecLevel, true);\n\t\t\t\tif (defdepth < cfgdepth && cfgdepth != FDEPTH_MISSING)\n\t\t\t\t{\n\t\t\t\t\tif (cl_warncmd.ival)\n\t\t\t\t\t{\n\t\t\t\t\t\tchar fulldefault[MAX_OSPATH];\n\t\t\t\t\t\tchar fullconfig[MAX_OSPATH];\n\t\t\t\t\t\t*fulldefault = *fullconfig = 0;\n\t\t\t\t\t\tFS_DisplayPath(\"default.cfg\", FS_GAME, fulldefault, sizeof(fulldefault));\n\t\t\t\t\t\tFS_DisplayPath(name, FS_GAME, fullconfig, sizeof(fullconfig));\n\t\t\t\t\t\tCon_Printf(\"Refusing to execute \\\"%s\\\", superceded by %s\\n\", fullconfig, fulldefault);\n\t\t\t\t\t}\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (!strcmp(name, fs_manifest->mainconfig))\n\t\t\t{\n\t\t\t\tint cfgdepth = COM_FDepthFile(name, true);\n\t\t\t\tint defdepth = COM_FDepthFile(\"default.cfg\", true);\n\t\t\t\tif (defdepth < cfgdepth && cfgdepth != FDEPTH_MISSING)\n\t\t\t\t{\n\t\t\t\t\tif (cl_warncmd.ival)\n\t\t\t\t\t{\n\t\t\t\t\t\tchar fulldefault[MAX_OSPATH];\n\t\t\t\t\t\tchar fullconfig[MAX_OSPATH];\n\t\t\t\t\t\t*fulldefault = *fullconfig = 0;\n\t\t\t\t\t\tFS_DisplayPath(\"default.cfg\", FS_GAME, fulldefault, sizeof(fulldefault));\n\t\t\t\t\t\tFS_DisplayPath(name, FS_GAME, fullconfig, sizeof(fullconfig));\n\t\t\t\t\t\tCon_Printf(\"Refusing to execute \\\"%s\\\", superceded by %s\\n\", fullconfig, fulldefault);\n\t\t\t\t\t}\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tif (!strncmp(name, \"../\", 3) || !strncmp(name, \"..\\\\\", 3) || !strncmp(name, \"./\", 2) || !strncmp(name, \".\\\\\", 2))\n\t{\t//filesystem will correctly block this (and more), but it does look dodgy when servers try doing this dodgy shit anyway.\n\t\tif (Cmd_IsInsecure())\n\t\t\tCon_TPrintf (\"%s: %s is an invalid path (from server)\\n\", Cmd_Argv(0), name);\n\t\telse\n\t\t\tCon_TPrintf (\"%s: %s is an invalid path\\n\", Cmd_Argv(0), name);\n\t\treturn;\n\t}\n\n\tif (FS_FLocateFile(name, FSLF_IFFOUND|FSLF_IGNOREPURE, &loc) || FS_FLocateFile(va(\"%s.cfg\", name), FSLF_IFFOUND, &loc))\n\t{\n\t\tfile = FS_OpenReadLocation(name, &loc);\n\t\tif (!file)\n\t\t{\n\t\t\tCon_TPrintf (\"couldn't %s %s. check permissions.\\n\", Cmd_Argv(0), name);\n\t\t\treturn;\n\t\t}\n\n\t\tl = VFS_GETLEN(file);\n\t\tf = BZ_Malloc(l+1);\n\t\tf[l] = 0;\n\t\tVFS_READ(file, f, l);\n\t\tVFS_CLOSE(file);\n\n\t\tuntrusted = !!(loc.search->flags&SPF_UNTRUSTED);\n\t}\n#if defined(HAVE_LEGACY) && defined(HAVE_CLIENT)\n\telse if (!strcmp(name, \"default.cfg\"))\t//the q1 rerelease lacks a default.cfg (which I suppose is kinda handy, but oh well)\n\t{\n\t\tf = Z_StrDup(replacementq1binds);\n\t\tuntrusted = false;\n\t\tl = 0;\n\t}\n\telse if (!strcmp(name, \"touch.cfg\"))\t//auto-execed if they touch a touchscreen.\n\t{\n\t\tf = Z_StrDup(defaulttouchcfg);\n\t\tuntrusted = false;\n\t\tl = 0;\n\t}\n#endif\n\telse\n\t{\n\t\tCon_TPrintf (\"couldn't exec %s\\n\", name);\n\t\treturn;\n\t}\n\tlevel = ((Cmd_FromGamecode() || untrusted) ? RESTRICT_INSECURE : Cmd_ExecLevel);\n\n\tif (cl_warncmd.ival || developer.ival || cvar_watched || dpcompat_console.ival)\n\t{\n\t\tif (loc.search)\n\t\t\tCon_TPrintf (\"execing ^[^7%s\\\\tip\\\\from %s/%s^]\\n\", name, loc.search->logicalpath, name);\n\t\telse\n\t\t\tCon_TPrintf (\"execing %s\\n\", name);\n\t}\n\n\ts = f;\n\tif (s[0] == '\\xef' && s[1] == '\\xbb' && s[2] == '\\xbf')\n\t{\n\t\tCon_DPrintf(\"Ignoring UTF-8 BOM\\n\");\n\t\ts+=3;\n\t}\n\n\tif (!strcmp(name, \"config.cfg\") || !strcmp(name, \"q3config.cfg\") || (*fs_manifest->mainconfig && !strcmp(name, fs_manifest->mainconfig)))\n\t{\n\t\tchar *restart;\n\t\t//if the config is from id1 and the default.cfg was from some mod, make sure the default.cfg overrides the config.\n\t\t//we won't just exec the default instead, because we can at least retain things which are not specified (ie: a few binds)\n\t\tint cfgdepth = COM_FDepthFile(name, true);\n\t\tint defdepth = COM_FDepthFile(\"default.cfg\", true);\n\t\tif (defdepth < cfgdepth)\n\t\t\tCbuf_InsertText(\"exec default.cfg\\n\", level, false);\n\n\t\t//hack to work around the more insideous hacks of other engines.\n\t\t//namely: vid_restart at the end of config.cfg is evil, and NOT desired in FTE as it generally means any saved video settings are wrong.\n\t\trestart = strstr(f, \"\\nvid_restart\");\n\t\tif (restart && (restart[12] == '\\r' || restart[12]=='\\n'))\n\t\t{\t//convert it to a comment so we don't get fucked over by bad configs.\n\t\t\trestart[1] = restart[2] = '/';\n\t\t\tCon_Printf(CON_WARNING \"WARNING: %s came from a different engine\\n\", loc.rawname);\n\t\t}\n\t}\n\n\tif (*loc.rawname)\n\t\tCOM_QuotedString(loc.rawname, buf, sizeof(buf), false);\n\telse if (loc.search)\n\t\tCOM_QuotedString(va(\"%s/%s\", loc.search->logicalpath, name), buf, sizeof(buf), false);\n\telse\n\t\tCOM_QuotedString(name, buf, sizeof(buf), false);\n\n\tif (cvar_watched)\n\t\tCbuf_InsertText (va(\"echo END %s\", buf), level, true);\n\t// don't execute anything if it was from server (either the stuffcmd/localcmd, or the file)\n\tif (!strcmp(name, \"default.cfg\"))\n\t{\n\t\tif (!(Cmd_FromGamecode() || untrusted))\n\t\t\tCbuf_InsertText (\"\\ncvar_lockdefaults 1\\n\", level, false);\n\t\tif (fs_manifest->defaultoverrides)\n\t\t\tCbuf_InsertText (fs_manifest->defaultoverrides, level, false);\n\n#if defined(HAVE_LEGACY) && defined(HAVE_CLIENT)\n\t\tif (l == 1914 && CalcHashInt(&hash_md4, f, l) == 0x2d7b72b9)\n\t\t\ts = (char*)replacementq1binds;\n#ifdef HEXEN2\n\t\telse if (l == 1875 && CalcHashInt(&hash_md4, f, l) == 0x27b4d813)\n\t\t{\t//hexen2 has weird stuff in there. just give it wasd.\n\t\t\ts = va(\n\t\t\t\t\"%s\\n\"\n\t\t\t\t\"bind w +forward\\n\"\n\t\t\t\t\"bind a +moveleft\\n\"\n\t\t\t\t\"bind s +back\\n\"\n\t\t\t\t\"bind d +moveright\\n\"\n\n\t\t\t\t\"bind mouse2 +jump\\n\"\n\t\t\t\t\"bind mouse3 +forward\\n\" //mneh\n\n\t\t\t\t\"bind x +lookup\\n\"\t//moved to x instead of a\n\t\t\t\t\"cl_forwardspeed 400\\n\" //hexen2's autorun state.\n\t\t\t, s);\n\t\t}\n#endif\n#endif\n\t}\n#ifndef QUAKETC\n\t//hack to try to work around nquake's b0rkedness\n\tif (!strncmp(s, \"// This is nQuake's Frogbot config\", 33))\n\t{\n\t\ts = va(\"echo \\\"\"CON_ERROR\"Refusing to exec nQuake's %s\\\"\", buf);\t//otherwise many people with nquake installed will be fucked over whenever they try playing singleplayer\n\t\tCbuf_InsertText (s, level, true);\n\t}\n\telse\n\t{\n\t\tint foundone = 0;\n\t\twhile (!strncmp(s, \"//\", 2))\n\t\t{\n\t\t\tchar *eol = strstr(s, \"\\n\");\n\t\t\tif (eol)\n\t\t\t{\n\t\t\t\t*eol++ = 0;\n\t\t\t\tif (strstr(s, \"nQuake\") || strstr(s, \"N Q U A K E\"))\n\t\t\t\t{\t//this is evil, but if we're running quake then com_parseutf8 will be 0 and we can just convert to quake chars (less text).\n\t\t\t\t\tchar *out = s = eol;\n\t\t\t\t\tconst char *in = s;\n\t\t\t\t\twhile (*in)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (*in == '\\n' && !strncmp(in,\"\\nexec configs/config.cfg\", 24))\n\t\t\t\t\t\t{\t//ezquake writes its configs elsewhere, and nquake stomps on everything in its autoexec.cfg, so we need to try to work around its breakages\n\t\t\t\t\t\t\tmemmove(out, in, 6);out+=6;in+=6;\n\t\t\t\t\t\t\tin += 8;\n\t\t\t\t\t\t\tfoundone++;\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (*in == '^')\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t*out++ = 0x80|*++in;\n\t\t\t\t\t\t\tfoundone++;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\t*out++ = *in;\n\t\t\t\t\t\tin++;\n\t\t\t\t\t}\n\t\t\t\t\t*out = 0;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\ts = eol;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\n\t\t#ifdef HAVE_CLIENT\n\t\tif (!cl_warncmd.ival && foundone && (!strcmp(name, \"quake.rc\") || !strcmp(name, \"default.cfg\") || !strcmp(name, \"autoexec.cfg\")))\n\t\t{\n#if defined(HAVE_LEGACY)\n\t\t\tif (!strcmp(name, \"default.cfg\"))\n\t\t\t{\n\t\t\t\ts = (char*)replacementq1binds;\n\t\t\t\tfoundone = 0;\n\t\t\t}\n\t\t\telse\n#endif\n\t\t\t{\n\t\t\t\tMenu_Prompt(NULL, NULL, va(localtext(\"WARNING: nquake %s file detected. The file has been ignored.\"), name), NULL, NULL, \"Argh\", false);\n\t\t\t\t*s = 0;\n\t\t\t\tfoundone = 0;\n\t\t\t}\n\t\t}\n\t\t#endif\n\n\t\tCbuf_InsertText (s, level, true);\n\t\tif (foundone)\n\t\t\tCbuf_InsertText(va(\"\\necho \\\"\"CON_ERROR\"fixups for nquake config %s: %i replacements\\\"\\n\", buf, foundone), level, false);\n\t}\n#else\n\tCbuf_InsertText (s, level, true);\n#endif\n\tif (cvar_watched)\n\t\tCbuf_InsertText (va(\"echo BEGIN %s\", buf), level, true);\n\tBZ_Free(f);\n\n\tif (level != Cmd_ExecLevel)\n\t\tCbuf_ExecuteLevel(level);\n}\n\nstatic int QDECL CompleteExecList (const char *name, qofs_t flags, time_t mtime, void *parm, searchpathfuncs_t *spath)\n{\n\tstruct xcommandargcompletioncb_s *ctx = parm;\n\tctx->cb(name, NULL, NULL, ctx);\n\treturn true;\n}\nstatic void Cmd_Exec_c(int argn, const char *partial, struct xcommandargcompletioncb_s *ctx)\n{\n\tif (argn == 1)\n\t{\n\t\tCOM_EnumerateFiles(va(\"configs/%s*.cfg\", partial), CompleteExecList, ctx);\n\t\tCOM_EnumerateFiles(va(\"%s*.cfg\", partial), CompleteExecList, ctx);\n\t\tCOM_EnumerateFiles(va(\"%s*.rc\", partial), CompleteExecList, ctx);\n\t}\n}\n\n/*\n===============\nCmd_Echo_f\n\nJust prints the rest of the line to the console\n===============\n*/\nstatic void Cmd_Echo_f (void)\n{\n\tchar text[4096];\n\tchar extext[4096];\n\tconst char *t;\n\tint level = Cmd_ExecLevel;\n\tint\t\ti;\n\t*text = 0;\n\n\tfor (i=1 ; i<Cmd_Argc() ; i++)\n\t{\n\t\tif (i >= 2)\n\t\t\tQ_strncatz(text, \" \", sizeof(text));\n\t\tQ_strncatz(text, Cmd_Argv(i), sizeof(text));\n\t}\n\tQ_strncatz(text, \"\\n\", sizeof(text));\n\n\t//echo text is often quoted, so expand the text again now that we're no longer in quotes.\n\tt = Cmd_ExpandString(text, extext, sizeof(extext), &level, false, !Cmd_IsInsecure()?true:false, true);\n\n#ifndef HAVE_CLIENT\n\tCon_Printf (\"%s\", t);\n#else\n\tt = TP_ParseFunChars(t);\n#ifdef HAVE_LEGACY\n\tCon_PrintFlags (t, ((ezcompat_markup.ival>=2)?PFS_EZQUAKEMARKUP:0), 0);\n#else\n\tCon_PrintFlags (t, 0, 0);\n#endif\n#endif\n}\n\nstatic void Key_Alias_c(int argn, const char *partial, struct xcommandargcompletioncb_s *ctx)\n{\n\tcmdalias_t\t*a;\n\tsize_t len = strlen(partial);\n\tif (argn != 1)\n\t\treturn;\n\tfor (a = cmd_alias ; a ; a=a->next)\n\t{\n\t\tif (!Q_strncasecmp(partial,a->name, len))\n\t\t\tctx->cb(a->name, a->value, NULL, ctx);\n\t}\n}\nstatic void Cmd_ShowAlias_f (void)\n{\n\tcmdalias_t\t*a;\n\tchar *s;\n\n\ts = Cmd_Argv(1);\n\n\t//find it, print it\n\tfor (a = cmd_alias ; a ; a=a->next)\n\t{\n\t\tif (!strcmp(s, a->name))\n\t\t{\n\t\t\tCon_Printf (\"alias %s %s\\n\", a->name, a->value);\n\t\t\treturn;\n\t\t}\n\t}\n\n\tCon_Printf(\"Alias doesn't exist\\n\");\n}\n\n//returns a zoned string.\nchar *Cmd_ParseMultiline(qboolean checkheader)\n{\n\tchar *result;\n\tchar *end;\n\tint in = checkheader?0:1;\n\tchar *s;\n\tresult = NULL;\n\tfor(;;)\n\t{\n\t\ts = Cbuf_GetNext(Cmd_ExecLevel, false);\n\t\tif (!*s)\n\t\t{\n\t\t\tif (in)\n\t\t\t\tCon_Printf(CON_WARNING \"WARNING: Multiline alias was not terminated\\n\");\n\t\t\tbreak;\n\t\t}\n\t\twhile (*s <= ' ' && *s)\n\t\t\ts++;\n\t\tfor (end = s + strlen(s)-1; end >= s && *end <= ' '; end--)\n\t\t\t*end = '\\0';\n\t\tif (!strcmp(s, \"{\"))\n\t\t{\n\t\t\tin++;\n\t\t\tif (in == 1)\n\t\t\t\tcontinue;\t//don't embed the first one in the string, because that would be weird.\n\t\t}\n\t\telse if (!strcmp(s, \"}\"))\n\t\t{\n\t\t\tin--;\n\t\t\tif (!in)\n\t\t\t\tbreak;\t//phew\n\t\t}\n\t\tif (result)\n\t\t{\n\t\t\tchar *newv = (char*)Z_Malloc(strlen(result) + strlen(s) + 2);\n\t\t\tsprintf(newv, \"%s;%s\", result, s);\n\t\t\tZ_Free(result);\n\t\t\tresult = newv;\n\t\t}\n\t\telse\n\t\t\tresult = Z_StrDup(s);\n\t\tif (!in)\n\t\t\tbreak;\n\t}\n\treturn result;\n}\n/*\n===============\nCmd_Alias_f\n\nCreates a new command that executes a command string (possibly ; seperated)\n===============\n*/\n\nstatic void Cmd_Alias_f (void)\n{\n\tcmdalias_t\t*a, *b;\n\tchar\t\tcmd[65536];\n\tint\t\t\ti, c;\n\tchar\t\t*s;\n\tqboolean multiline;\n\n\tif (Cmd_Argc() == 1)\t//list em all.\n\t{\n\t\tif (Cmd_FromGamecode())\n\t\t{\n\t\t\tif (Cmd_ExecLevel==RESTRICT_SERVER)\n\t\t\t{\n\t\t\t\tCon_TPrintf (\"Current alias commands:\\n\");\n\t\t\t\tfor (a = cmd_alias ; a ; a=a->next)\n\t\t\t\t{\n\t\t\t\t\tif (a->flags & ALIAS_FROMSERVER)\n\t\t\t\t\t\tCon_Printf (\"%s : %s\\n\", a->name, a->value);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tCon_TPrintf (\"Current alias commands:\\n\");\n\t\t\tfor (a = cmd_alias ; a ; a=a->next)\n\t\t\t{\n\t/*\t\t\textern int con_linewidth;\n\t\t\t\tif (strlen(a->value)+strlen(a->name)+3 > con_linewidth)\n\t\t\t\t\tCon_Printf (\"%s ...\\n\", a->name);\n\t\t\t\telse*/\n\t\t\t\t\tCon_Printf (\"%s : %s\\n\", a->name, a->value);\n\t\t\t}\n\t\t}\n\t\treturn;\n\t}\n\n\ts = Cmd_Argv(1);\n\tif (!strcmp(s, \"say\"))\t//reject aliasing the say command. We use it as an easy way to warn that our player is cheating.\n\t{\n\t\tCon_TPrintf (\"Refusing to create an alias with the name '%s'\\n\", s);\n\t\treturn;\n\t}\n\n\t// check for overlap with a command\n\tif (Cmd_Exists (s))\n\t{\t//commands always take precedence over aliases (so mods can't clobber 'quit' etc), so creating an alias with one of these names is stupid. always try to rename them.\n\t\tif (Cmd_IsInsecure() && Q_snprintfz(cmd, sizeof(cmd), \"%s_a\", s) < sizeof(cmd))\n\t\t{\n\t\t\tif (Cmd_Exists (cmd))\n\t\t\t{\n\t\t\t\tCon_Printf (S_COLOR_RED\"Can't register alias, %s is a command\\n\", s);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tCon_Printf (S_COLOR_RED\"alias %s: renamed to %s due to command conflict\\n\", s, cmd);\n\t\t\ts = cmd;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tCon_Printf (S_COLOR_RED\"Can't register alias, %s is a command\\n\", s);\n\t\t\treturn;\n\t\t}\n\t}\n\tif (!cl_aliasoverlap.value)\n\t{\t//aliases take precedence over cvars (while cvars can be set via 'set'), so user's choice.\n\t\tif (Cvar_FindVar (s))\n\t\t{\n\t\t\tif (Cmd_IsInsecure() && Q_snprintfz(cmd, sizeof(cmd), \"%s_a\", s) < sizeof(cmd))\n\t\t\t{\n\t\t\t\tCon_Printf (S_COLOR_RED\"alias %s: renamed to %s due to cvar conflict\\n\", s, cmd);\n\t\t\t\ts = cmd;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tCon_Printf (S_COLOR_RED\"alias %s: can't register alias - matches existing cvar\\n\", s);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n\n\t// if the alias already exists, reuse it\n\tfor (a = cmd_alias ; a ; a=a->next)\n\t{\n\t\tif (!strcmp(s, a->name))\n\t\t{\n\t\t\tif ((a->restriction?a->restriction:rcon_level.ival) > Cmd_ExecLevel)\n\t\t\t{\n\t\t\t\tCon_TPrintf (\"Alias is already bound with a higher restriction\\n\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (!stricmp(Cmd_Argv(0), \"newalias\"))\n\t\t\t\treturn;\t//newalias command only registers the alias if it is new, and does not change it if it already exists\n\n\t\t\tZ_Free (a->value);\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tif (!a)\n\t{\n\t\tcmdalias_t **link;\n\n\t\ta = (cmdalias_t*)Z_Malloc (sizeof(cmdalias_t) + strlen(s));\n\t\tstrcpy (a->name, s);\n\t\tfor (link = &cmd_alias; ; link = &(*link)->next)\n\t\t{\n\t\t\tif (!*link || strcmp((*link)->name, s) >= 0)\n\t\t\t{\n\t\t\t\ta->next = *link;\n\t\t\t\t*link = a;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\tif (Cmd_FromGamecode())\n\t\ta->flags |= ALIAS_FROMSERVER;\n\telse\n\t\ta->flags &= ~ALIAS_FROMSERVER;\n\n\tmultiline = false;\n\tif (Cmd_Argc() == 2)\t//check the next statement for being '{'\n\t{\n\t\tchar *line, *end;\n\t\tline = Cbuf_GetNext(Cmd_ExecLevel, false);\n\n\t\twhile(*line <= ' ' && *line)\t//skip leading whitespace.\n\t\t\tline++;\n\n\t\tfor (end = line + strlen(line)-1; end >= line && *end <= ' '; end--)\t//skip trailing\n\t\t\t*end = '\\0';\n\t\tif (!strcmp(line, \"{\"))\n\t\t\tmultiline = true;\n\t\telse\n\t\t\tCbuf_InsertText(line, Cmd_ExecLevel, true);\t//whoops. Stick the trimmed string back in to the cbuf.\n\t}\n\telse if (!strcmp(Cmd_Argv(2), \"{\"))\n\t\tmultiline = true;\n\n\tif (multiline)\n\t{\t//fun! MULTILINE ALIASES!!!!\n\t\ta->value = Cmd_ParseMultiline(false);\n\t}\n\telse\n\t{\n// copy the rest of the command line\n\t\tcmd[0] = 0;\t\t// start out with a null string\n\t\tc = Cmd_Argc();\n\t\tfor (i=2 ; i< c ; i++)\n\t\t{\n\t\t\tstrcat (cmd, Cmd_Argv(i));\n\t\t\tif (i != c-1)\n\t\t\t\tstrcat (cmd, \" \");\n\t\t}\n\n\t\tif (!*cmd && !dpcompat_console.ival)\t//someone wants to wipe it. let them\n\t\t{\n\t\t\tif (a == cmd_alias)\n\t\t\t{\n\t\t\t\tcmd_alias = a->next;\n\t\t\t\tZ_Free(a);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tfor (b = cmd_alias ; b ; b=b->next)\n\t\t\t\t{\n\t\t\t\t\tif (b->next == a)\n\t\t\t\t\t{\n\t\t\t\t\t\tb->next = a->next;\n\t\t\t\t\t\tZ_Free(a);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\ta->value = Z_StrDup (cmd);\n\t}\n\tif (Cmd_FromGamecode())\n\t{\n\t\ta->execlevel = RESTRICT_SERVER;\t//server-set aliases MUST run at the server's level.\n\t\ta->restriction = 1;\t\t\t\t//and be runnable at the user's level\n\t}\n\telse\n\t{\n\t\ta->execlevel = 0;\t//run at users exec level\n\t\ta->restriction = 1;\t//this is possibly a security risk if the admin also changes execlevel\n\t}\n}\n\n#ifdef HAVE_CLIENT\nstatic void Cmd_AliasEdit_f (void)\n{\n\tchar *alias = Cmd_AliasExist(Cmd_Argv(1), RESTRICT_LOCAL);\n\tchar quotedalias[2048];\n\tif (alias)\n\t{\n\t\tCOM_QuotedString(alias, quotedalias, sizeof(quotedalias), false);\n\t\tKey_ConsoleReplace(va(\"alias %s %s\", Cmd_Argv(1), quotedalias));\n\t}\n\telse\n\t\tCon_Printf(\"Not an alias\\n\");\n}\n#endif\n\n/*static void Cmd_DeleteAlias(const char *name)\n{\n\tcmdalias_t\t*a, **link;\n\tfor (link = &cmd_alias; (a = *link); link = &(*link)->next)\n\t{\n\t\tif (!strcmp(a->name, name))\n\t\t{\n\t\t\t*link = a->next;\n\t\t\tZ_Free(a->value);\n\t\t\tZ_Free(a);\n\t\t\treturn;\n\t\t}\n\t}\n}*/\n\nchar *Cmd_AliasExist(const char *name, int restrictionlevel)\n{\n\tcmdalias_t\t*a;\n\t// if the alias already exists, reuse it\n\tfor (a = cmd_alias ; a ; a=a->next)\n\t{\n\t\tif (!strcmp(name, a->name))\n\t\t{\n\t\t\tif ((a->restriction?a->restriction:rcon_level.ival) > restrictionlevel)\n\t\t\t{\n\t\t\t\treturn NULL;\t//not at this level...\n\t\t\t}\n\t\t\treturn a->value;\n\t\t}\n\t}\n\treturn NULL;\n}\n\nstatic void Cmd_AliasLevel_f (void)\n{\n\tcmdalias_t\t*a;\n\tchar *s = Cmd_Argv(1);\n\tint level;\n\tif (Cmd_Argc() < 2 || Cmd_Argc() > 3)\n\t{\n\t\tCon_TPrintf(\"aliaslevel <var> [execlevel]\\n\");\n\t\treturn;\n\t}\n\n\tfor (a = cmd_alias ; a ; a=a->next)\n\t{\n\t\tif (!strcmp(s, a->name))\n\t\t{\n\t\t\tbreak;\n\t\t}\n\t}\n\tif (!a)\n\t{\n\t\tCon_TPrintf(\"Alias not found\\n\");\n\t\treturn;\n\t}\n\n\tif (Cmd_Argc() == 3)\n\t{\n\t\tlevel = atoi(Cmd_Argv(2));\n\t\tif (level > RESTRICT_MAX)\n\t\t{\n\t\t\tlevel = RESTRICT_MAX;\n\t\t}\n\t\telse if (level < RESTRICT_MIN)\n\t\t\tlevel = RESTRICT_MIN;\n\n\t\tif (level > Cmd_ExecLevel || (a->restriction?a->restriction:rcon_level.ival) > Cmd_ExecLevel)\n\t\t{\n\t\t\tCon_TPrintf(\"You arn't allowed to raise a command above your own level\\n\");\n\t\t\treturn;\n\t\t}\n\n\t\ta->execlevel = level;\n\n\t\tif (a->restriction == 1)\n\t\t\tCon_TPrintf(\"WARNING: %s is available to all clients, any client will be able to use it at the new level.\\n\", a->name);\n\t}\n\telse\n\t\tCon_TPrintf(\"alias %s is set to run at the user level of %i\\n\", s, a->execlevel);\n}\n\n//lists commands, also prints restriction level\nstatic void Cmd_AliasList_f (void)\n{\n\tcmdalias_t\t*cmd;\n\tint num=0;\n\tint flags;\n\n\tif (!strcmp(Cmd_Argv(1), \"server\"))\n\t\tflags = ALIAS_FROMSERVER;\n\telse\n\t\tflags = 0;\n\n\tfor (cmd=cmd_alias ; cmd ; cmd=cmd->next)\n\t{\n\t\tif ((cmd->restriction?cmd->restriction:rcon_level.ival) > Cmd_ExecLevel)\n\t\t\tcontinue;\n\t\tif (flags && !(cmd->flags & flags))\n\t\t\tcontinue;\n\t\tif (!num)\n\t\t\tCon_TPrintf(\"Alias list:\\n\");\n\t\tif (cmd->execlevel)\n\t\t\tCon_Printf(S_COLOR_TRANS\"(%2i)(%2i) \"S_COLOR_WHITE\"^[%s\\\\type\\\\/%s^]\\n\", (int)(cmd->restriction?cmd->restriction:rcon_level.ival), cmd->execlevel, cmd->name, cmd->name);\n\t\telse\n\t\t\tCon_Printf(S_COLOR_TRANS \"(%2i)     \"S_COLOR_WHITE\"^[%s\\\\type\\\\/%s^]\\n\", (int)(cmd->restriction?cmd->restriction:rcon_level.ival), cmd->name, cmd->name);\n\t\tnum++;\n\t}\n\tif (num)\n\t\tCon_Printf(\"\\n\");\n}\n\nstatic void Alias_WriteAliases (vfsfile_t *f)\n{\n\tconst char *s;\n\tcmdalias_t\t*cmd;\n\tint num=0;\n\tchar buf[65536];\n\tfor (cmd=cmd_alias ; cmd ; cmd=cmd->next)\n\t{\n//\t\tif ((cmd->restriction?cmd->restriction:rcon_level.ival) > Cmd_ExecLevel)\n//\t\t\tcontinue;\n\t\tif (cmd->flags & ALIAS_FROMSERVER)\n\t\t\tcontinue;\n\t\tif (!num)\n\t\t{\n\t\t\ts = va(\"\\n//////////////////\\n//Aliases\\n\");\n\t\t\tVFS_WRITE(f, s, strlen(s));\n\t\t}\n\t\ts = va(\"alias %s \", cmd->name);\n\t\tVFS_WRITE(f, s, strlen(s));\n\t\ts = COM_QuotedString(cmd->value, buf, sizeof(buf), false);\n\t\tVFS_WRITE(f, s, strlen(s));\n\t\tVFS_WRITE(f, \"\\n\", 1);\n\t\tif (cmd->restriction != 1)\t//1 is default\n\t\t{\n\t\t\ts = va(\"restrict %s %i\\n\", cmd->name, cmd->restriction);\n\t\t\tVFS_WRITE(f, s, strlen(s));\n\t\t}\n\t\tif (cmd->execlevel != 0)\t//0 is default (runs at user's level)\n\t\t{\n\t\t\ts = va(\"aliaslevel %s %i\\n\", cmd->name, cmd->execlevel);\n\t\t\tVFS_WRITE(f, s, strlen(s));\n\t\t}\n\t\tnum++;\n\t}\n}\n\nvoid Alias_WipeStuffedAliases(void)\n{\n\tcmdalias_t\t**link, *cmd;\n\tfor (link=&cmd_alias ; (cmd=*link) ; )\n\t{\n\t\tif (cmd->flags & ALIAS_FROMSERVER)\n\t\t{\n\t\t\t*link = cmd->next;\n\t\t\tZ_Free(cmd->value);\n\t\t\tZ_Free(cmd);\n\t\t}\n\t\telse\n\t\t\tlink=&(*link)->next;\n\t}\n}\n\nvoid Cvar_List_f (void);\nvoid Cvar_Reset_f (void);\nvoid Cvar_ResetAll_f(void);\nvoid Cvar_LockDefaults_f(void);\nvoid Cvar_PurgeDefaults_f(void);\n\n/*\n=============================================================================\n\n\t\t\t\t\tCOMMAND EXECUTION\n\n=============================================================================\n*/\n\ntypedef struct cmd_function_s\n{\n\tstruct cmd_function_s\t*next;\n\tconst char\t\t\t\t*name;\n\tconst char\t\t\t\t*description;\n\txcommand_t\t\t\t\tfunction;\n\txcommandargcompletion_t\targcompletion;\n\n\tqbyte\trestriction;\t//restriction of admin level\n} cmd_function_t;\n\n\n#define\tMAX_ARGS\t\t80\n\nstatic\tint\t\t\tcmd_argc;\nstatic\tchar\t\t*cmd_argv[MAX_ARGS];\nstatic\tchar\t\t*cmd_null_string = \"\";\nstatic\tchar\t\t*cmd_args = NULL, *cmd_args_buf;\n\n\n\nstatic\tcmd_function_t\t*cmd_functions;\t\t// possible commands to execute\n\n/*\n============\nCmd_Argc\n============\n*/\nint\t\tVARGS Cmd_Argc (void)\n{\n\treturn cmd_argc;\n}\n\n/*\n============\nCmd_Argv\n============\n*/\nchar\t*VARGS Cmd_Argv (int arg)\n{\n\tif ( arg >= cmd_argc )\n\t\treturn cmd_null_string;\n\treturn cmd_argv[arg];\n}\n\n/*\n============\nCmd_Args\n\nReturns a single string containing argv(1) to argv(argc()-1)\n============\n*/\n\nchar *VARGS Cmd_Args (void)\n{\n\tif (!cmd_args)\n\t\treturn \"\\0\\0\";\t//fucking hell gcc, I shouldn't need this shit.\n\treturn cmd_args;\n}\n\nvoid Cmd_Args_Set(const char *newargs, size_t len)\n{\n\tif (cmd_args_buf)\n\t\tZ_Free(cmd_args_buf);\n\n\tif (newargs)\n\t{\n\t\tcmd_args_buf = (char*)Z_Malloc (len+1);\n\t\tmemcpy(cmd_args_buf, newargs, len);\n\t\tcmd_args_buf[len] = 0;\n\t\tcmd_args = cmd_args_buf;\n\t}\n\telse\n\t{\n\t\tcmd_args = NULL;\n\t\tcmd_args_buf = NULL;\n\t}\n}\n\n/*\n============\nCmd_ShiftArgs\n\nShifts Cmd_Argv results down one (killing first param)\n============\n*/\nvoid Cmd_ShiftArgs (int ammount, qboolean expandstring)\n{\n\tint arg;\n\twhile (ammount>0 && cmd_argc)\n\t{\n\t\targ=0;\n\t\tcmd_argc--;\n\t\tZ_Free(cmd_argv[0]);\n\t\twhile ( arg < cmd_argc )\n\t\t{\n\t\t\tcmd_argv[arg] = cmd_argv[arg+1];\n\t\t\targ++;\n\t\t}\n\t\tcmd_argv[arg]=NULL;\n\n\t\tammount--;\n\n\t\tif (cmd_args)\n\t\t{\n\t\t\tcmd_args = COM_StringParse(cmd_args, com_token, sizeof(com_token), expandstring, false);\n\t\t\tif (cmd_args)\n\t\t\t\twhile(*cmd_args == ' ' || *cmd_args == '\\t')\n\t\t\t\t\tcmd_args++;\n\t\t}\n\t}\n}\n\nstatic const char *Cmd_ExpandCvar(char *cvarterm, int maxaccesslevel, int *newaccesslevel, qboolean enclosed, int *len)\n{\n\tconst char *ret = NULL;\n\tchar *fixup = NULL, fixval=0, *t;\n\tcvar_t\t*var;\n\tstatic char temp[12];\n\tstatic char quoted[256];\n\tunsigned int\tresult;\n\tint termlen, pl;\n\n\tint quotetype = 0;\n\tconst char *cvarname;\n\n\t//set foo ba\"r; ${foo q} -> ba\\\"r\n\t//set foo bar; ${foo asis} -> ba\"r\n\t//${bar q} -> <EMPTY>\n\t//${bar ?} -> \"\"\n\t//${bar !} -> <ERROR>\n\tfixup = cvarterm+strlen(cvarterm);\n\tfixval = 0;\n\ttermlen = fixup - cvarterm;\n\tif (fixup-cvarterm > 2 && !strncmp(fixup-2, \" ?\", 2))\n\t{\t//force expansion, even if not defined.\n\t\tpl = 2;\n\t\tquotetype = 2;\n\t}\n\telse if (fixup-cvarterm > 2 && !strncmp(fixup-2, \" !\", 2))\n\t{\t//abort if not defined\n\t\tpl = 2;\n\t\tquotetype = 3;\n\t}\n\telse if (fixup-cvarterm > 2 && !strncmp(fixup-2, \" q\", 2))\n\t{\t//escaping it if not empty, otherwise empty.\n\t\tpl = 2;\n\t\tquotetype = 1;\n\t}\n\telse if (fixup-cvarterm > 5 && !strncmp(fixup-5, \" asis\", 5))\n\t{\t//no escaping...\n\t\tpl = 5;\n\t\tquotetype = 0;\n\t}\n\telse\n\t{\n\t\tpl = 0;\n\t\tquotetype = enclosed && dpcompat_console.ival;\t//default to escaping.\n\t}\n\tif (pl)\n\t{\n\t\tfixup -= pl;\n\t\tfixval = *fixup;\n\t\t*fixup = 0;\n\t}\n\telse\n\t\tfixup = NULL;\n\tif (*cvarterm == '$')\n\t\tcvarname = Cmd_ExpandCvar(cvarterm+1, maxaccesslevel, newaccesslevel, false, &pl);\n\telse\n\t\tcvarname = cvarterm;\n\n\tif (!cvarname)\n\t\t;\n\telse if ((result = strtoul(cvarname, &t, 10)), (dpcompat_console.ival||fixval) && (*t == 0 || (*t == '-' && t[1] == 0))) //only expand $0 if its actually ${0} - this avoids conflicting with the $0 macro\n\t{\n\t\tif (*t == '-')\t//pure number with a trailing minus means\n\t\t{\t\t\t\t//args starting after that.\n\t\t\tret = Cmd_Args();\n\t\t\twhile (ret && result-- > 1)\n\t\t\t\tret = COM_StringParse(ret, com_token, sizeof(com_token), false, false);\n\t\t\twhile(ret && (*ret == ' ' || *ret == '\\t'))\n\t\t\t\tret++;\n\t\t}\n\t\telse\t//purely numerical\n\t\t\tret = Cmd_Argv(result);\n\t}\n\telse if (!strcmp(cvarname, \"*\") || !stricmp(cvarname, \"cmd_args\"))\n\t{\n\t\tret = Cmd_Args();\n\t}\n\telse if (!strnicmp(cvarname, \"cmd_argv\", 8))\n\t{\n\t\tret = Cmd_Argv(atoi(cvarname+8));\n\t}\n\telse if (!strcmp(cvarname, \"#\") || !stricmp(cvarname, \"cmd_argc\"))\n\t{\n\t\tQ_snprintfz(temp, sizeof(temp), \"%u\", Cmd_Argc());\n\t\tret = temp;\n\t}\n\telse if ( (var = Cvar_FindVar(cvarname)) != NULL )\n\t{\n\t\tif (var->restriction <= maxaccesslevel && !((var->flags & CVAR_NOUNSAFEEXPAND) && Cmd_IsInsecure()))\n\t\t{\n\t\t\tret = var->string;\n\n\t\t\tif (var->flags & CVAR_TEAMPLAYTAINT)\t//if we're only allowed to expand this for teamplay, then switch access levels\n\t\t\t\t*newaccesslevel = 0;\n\t\t}\n\t}\n\n\tif (fixup)\n\t\t*fixup = fixval;\n\n\tif (quotetype == 3)\n\t{\n\t\tif (ret)\n\t\t\tquotetype = 1;\n\t\telse\n\t\t\treturn NULL;\n\t}\n\telse if (quotetype == 2)\n\t{\n\t\tquotetype = 1;\n\t\tif (!ret)\n\t\t\tret = \"\";\n\t}\n\tif (ret)\n\t\t*len = termlen;\n\n\tif (quotetype)\n\t\tret = COM_QuotedString(ret?ret:\"\", quoted, sizeof(quoted), true);\n\treturn ret;\n}\n\n/*\n================\nCmd_ExpandString\n\nExpands all $cvar expressions to cvar values\nIf not SERVERONLY, also expands $macro expressions\nNote: dest must point to a 1024 byte buffer\n================\n*/\nchar *Cmd_ExpandString (const char *data, char *dest, int destlen, int *accesslevel, qboolean expandargs, qboolean expandcvars, qboolean expandmacros)\n{\n\tunsigned int\tc;\n\tchar\tbuf[255];\n\tint\t\ti, len;\n\tint\t\tquotes = 0;\n\tconst char\t*str;\n\tconst char\t*bestvar;\n\tint\t\tname_length, var_length, best_length;\n\tqboolean striptrailing;\n\tint\t\tmaxaccesslevel = *accesslevel;\n\n\tlen = 0;\n\n\twhile ( (c = *data) != 0)\n\t{\n\t\tif (c == '\"')\n\t\t\tquotes++;\n\n\t\tif (c == '%' && !(quotes&1) && !dpcompat_console.ival && expandargs)\n\t\t{\t//QW262/ezquake does this. kinda annoying.\n\t\t\tchar *end;\n\t\t\tif (data[1] == '%')\n\t\t\t{\n\t\t\t\tstr = \"%\";\n\t\t\t\tdata+=2;\n\t\t\t}\n\t\t\telse if (data[1] == '#')\n\t\t\t{\n\t\t\t\tstr = va(\"\\\"%s\\\"\", Cmd_Args());\n\t\t\t\tdata+=2;\n\t\t\t}\n\t\t\telse if (data[1] == '*')\n\t\t\t{\n\t\t\t\tstr = Cmd_Args();\n\t\t\t\tdata+=2;\n\t\t\t}\n\t\t\telse if ((i=strtol(data+1, &end, 10)) || (end!=data+1&&(!*end||*end==' '||*end=='\\t')))\n\t\t\t{\n\t\t\t\tdata = end;\n\t\t\t\tstr = Cmd_Argv(i);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tdata++;\n\t\t\t\tstr = \"%\";\t//some kind of encoding error\n\t\t\t}\n\n\t\t\t// check buffer size\n\t\t\tif (len + strlen(str) >= destlen-1)\n\t\t\t\tbreak;\n\t\t\tstrcpy(&dest[len], str);\n\t\t\tlen += strlen(str);\n\t\t}\n\t\telse if (c == '$' && (!(quotes&1) || dpcompat_console.ival))\n\t\t{\n\t\t\tdata++;\n\t\t\tif (*data == '$')\n\t\t\t{\t//double-dollar expands to a single dollar.\n\t\t\t\tdata++;\n\t\t\t\tstr = \"$\";\n\t\t\t\tstriptrailing = false;\n\t\t\t\tname_length = 0;\n\t\t\t\tbuf[0] = 0;\n\t\t\t\tbuf[1] = 0;\n\t\t\t}\n\t\t\telse if (*data == '{')\n\t\t\t{\t//${foo} can do some especially weird expansions.\n\t\t\t\tdata++;\n\t\t\t\ti = 0;\n\t\t\t\tbuf[i++] = '{';\n\t\t\t\tstriptrailing = (*data == '-')?true:false;\n\t\t\t\twhile (*data && *data != '}')\n\t\t\t\t{\n\t\t\t\t\tif (i < sizeof(buf)-2)\n\t\t\t\t\t\tbuf[i++] = *data;\n\t\t\t\t\tdata++;\n\t\t\t\t}\n\t\t\t\tbuf[i] = 0;\n\t\t\t\tbestvar = NULL;\n\t\t\t\tif ((str = Cmd_ExpandCvar(buf+1+striptrailing, expandcvars?maxaccesslevel:-999, accesslevel, true, &var_length)))\n\t\t\t\t\tbestvar = str;\n\t\t\t\tif (expandmacros && (str = TP_MacroString (buf+1+striptrailing, accesslevel, &var_length)))\n\t\t\t\t\tbestvar = str;\n\t\t\t\tstr = bestvar;\n\t\t\t\tif (*data == '}')\n\t\t\t\t{\n\t\t\t\t\tdata++;\n\t\t\t\t\tbuf[i++] = '}';\n\t\t\t\t\tbuf[i] = 0;\n\t\t\t\t}\n\t\t\t\tname_length = i;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tstriptrailing = (*data == '-')?true:false;\n\n\t\t\t\t// Copy the text after '$' to a temp buffer\n\t\t\t\ti = 0;\n\t\t\t\tbuf[0] = 0;\n\t\t\t\tbuf[1] = 0;\n\t\t\t\tbestvar = NULL;\n\t\t\t\tvar_length = best_length = 0;\n\t\t\t\twhile((c = *data))\n\t\t\t\t{\n\t\t\t\t\tif (c < ' ' || c == '$')\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tif (c == ' ' && buf[0] != '{')\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdata++;\n\t\t\t\t\tbuf[i++] = c;\n\t\t\t\t\tbuf[i] = 0;\n\t\t\t\t\tif ((str = Cmd_ExpandCvar(buf+striptrailing, expandcvars?maxaccesslevel:-999, accesslevel, false, &var_length)))\n\t\t\t\t\t\tbestvar = str, best_length=var_length;\n\t\t\t\t\tif (expandmacros && (str = TP_MacroString (buf+striptrailing, accesslevel, &var_length)))\n\t\t\t\t\t\tbestvar = str, best_length=var_length;\n\t\t\t\t}\n\n\t\t\t\tif (bestvar)\n\t\t\t\t{\n\t\t\t\t\tstr = bestvar;\n\t\t\t\t\tname_length = best_length;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tstr = NULL;\n\t\t\t\t\tname_length = 0;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (str)\n\t\t\t{\n\t\t\t\t// check buffer size\n\t\t\t\tif (len + strlen(str) >= destlen-1)\n\t\t\t\t\tbreak;\n\n\t\t\t\tstrcpy(&dest[len], str);\n\t\t\t\tlen += strlen(str);\n\t\t\t\ti = name_length;\n\t\t\t\twhile (buf[i])\n\t\t\t\t\tdest[len++] = buf[i++];\n\n\t\t\t\tif (striptrailing && !*str)\n\t\t\t\t\twhile(*data <= ' ' && *data)\n\t\t\t\t\t\tdata++;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t// no matching cvar or macro\n\t\t\t\tdest[len++] = '$';\n\t\t\t\tif (len + strlen(buf) >= destlen-1)\n\t\t\t\t\tbreak;\n\t\t\t\tstrcpy (&dest[len], buf);\n\t\t\t\tlen += strlen(buf);\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tdest[len] = c;\n\t\t\tdata++;\n\t\t\tlen++;\n\t\t\tif (len >= destlen-1)\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\tdest[len] = 0;\n\n\tif (len && dest[len-1] == '\\r')\t//with dos line endings, don't add some pointless \\r char on the end.\n\t\tdest[len-1] = 0;\n\n\treturn dest;\n}\n\n/*\n============\nCmd_TokenizeString\n\nParses the given string into command line tokens, stopping at the \\n\n============\n*/\nconst char *Cmd_TokenizeString (const char *text, qboolean expandmacros, qboolean qctokenize)\n{\n\tint\t\ti;\n\tconst char *args = NULL;\n\n// clear the args from the last string\n\tfor (i=0 ; i<cmd_argc ; i++)\n\t\tZ_Free (cmd_argv[i]);\n\n\tcmd_argc = 0;\n\n\twhile (1)\n\t{\n// skip whitespace up to a \\n\n\t\twhile (*text && (unsigned)*text <= ' ' && *text != '\\n')\n\t\t{\n\t\t\ttext++;\n\t\t}\n\n\t\tif (*text == '\\n')\n\t\t{\t// a newline seperates commands in the buffer\n\t\t\ttext++;\n\t\t\tbreak;\n\t\t}\n\n\t\tif (!*text)\n\t\t\tbreak;\n\n\t\tif (cmd_argc == 1)\n\t\t\targs = text;\n\n\t\ttext = COM_StringParse (text, com_token, sizeof(com_token), expandmacros, qctokenize);\n\t\tif (!text)\n\t\t\tbreak;\n\t\tif (!strcmp(com_token, \"\\n\"))\n\t\t\tbreak;\n\n\t\tif (cmd_argc < MAX_ARGS)\n\t\t{\n\t\t\tcmd_argv[cmd_argc] = (char*)Z_Malloc (Q_strlen(com_token)+1);\n\t\t\tQ_strcpy (cmd_argv[cmd_argc], com_token);\n\t\t\tcmd_argc++;\n\t\t}\n\t}\n\n\tif (args)\n\t{\n\t\tconst char *argsend = text?text:args+strlen(args);\n\t\twhile (argsend > args && (argsend[-1] == '\\n' || argsend[-1] == '\\r'))\n\t\t\targsend--;\n\t\tCmd_Args_Set(args, argsend-args);\n\t}\n\telse\n\t\tCmd_Args_Set(NULL, 0);\n\treturn text;\n}\n\nvoid Cmd_TokenizePunctation (char *text, char *punctuation)\n{\n\tint\t\ti;\n\tchar *args = NULL;\n\n// clear the args from the last string\n\tfor (i=0 ; i<cmd_argc ; i++)\n\t\tZ_Free (cmd_argv[i]);\n\n\tcmd_argc = 0;\n\tCmd_Args_Set(NULL, 0);\n\n\twhile (1)\n\t{\n// skip whitespace up to a \\n\n\t\twhile (*text && (unsigned)*text <= ' ' && *text != '\\n')\n\t\t{\n\t\t\ttext++;\n\t\t}\n\n\t\tif (*text == '\\n')\n\t\t{\t// a newline seperates commands in the buffer\n\t\t\ttext++;\n\t\t\tbreak;\n\t\t}\n\n\t\tif (!*text)\n\t\t\tbreak;\n\n\t\tif (cmd_argc == 1)\n\t\t\targs = text;\n\n\t\ttext = COM_ParseToken (text, punctuation);\n\t\tif (!text)\n\t\t\tbreak;\n\n\t\tif (cmd_argc < MAX_ARGS)\n\t\t{\n\t\t\tcmd_argv[cmd_argc] = (char*)Z_Malloc (Q_strlen(com_token)+1);\n\t\t\tQ_strcpy (cmd_argv[cmd_argc], com_token);\n\t\t\tcmd_argc++;\n\t\t}\n\t}\n\n\tif (args)\n\t{\n\t\tconst char *argsend = text?text:args+strlen(args);\n\t\twhile (argsend > args && (argsend[-1] == '\\n' || argsend[-1] == '\\r'))\n\t\t\targsend--;\n\t\tCmd_Args_Set(args, argsend-args);\n\t}\n}\n\n\n/*\n============\nCmd_AddCommand\n============\n*/\n\nqboolean Cmd_AddCommandAD (const char *cmd_name, xcommand_t function, xcommandargcompletion_t argcompletion, const char *desc)\n{\n\tcmd_function_t\t*cmd;\n\n// fail if the command is a variable name\n\tcvar_t *var = Cvar_FindVar (cmd_name);\n\tif (var && function)\n\t{\n\t\tCon_Printf (\"Cmd_AddCommand: %s already defined as a var\\n\", cmd_name);\n\t\treturn false;\n\t}\n\n// fail if the command already exists\n\tfor (cmd=cmd_functions ; cmd ; cmd=cmd->next)\n\t{\n\t\tif (!Q_strcmp (cmd_name, cmd->name))\n\t\t{\n\t\t\tif (cmd->function == function)\t//happens a lot with q3\n\t\t\t\tCon_DPrintf (\"Cmd_AddCommand: %s already defined\\n\", cmd_name);\n\t\t\telse\n\t\t\t{\n\t\t\t\tCon_Printf (\"Cmd_AddCommand: %s already defined\\n\", cmd_name);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tcmd = (cmd_function_t*)Z_Malloc (sizeof(cmd_function_t)+strlen(cmd_name)+1);\n\tcmd->name = (char*)(cmd+1);\n\tstrcpy((char*)(cmd+1), cmd_name);\n\tcmd->argcompletion = argcompletion;\n\tcmd->description = desc;\n\tcmd->function = function;\n\tcmd->next = cmd_functions;\n\tcmd->restriction = 0;\n\tcmd_functions = cmd;\n\n\treturn true;\n}\n\nqboolean Cmd_AddCommandD (const char *cmd_name, xcommand_t function, const char *desc)\n{\n\treturn Cmd_AddCommandAD(cmd_name, function, NULL, desc);\n}\nqboolean Cmd_AddCommand (const char *cmd_name, xcommand_t function)\n{\n\treturn Cmd_AddCommandAD(cmd_name, function, NULL, NULL);\n}\n\nvoid\tCmd_RemoveCommand (const char *cmd_name)\n{\n\tcmd_function_t\t*cmd, **back;\n\n\tback = &cmd_functions;\n\twhile (1)\n\t{\n\t\tcmd = *back;\n\t\tif (!cmd)\n\t\t{\n//\t\t\tCon_Printf (\"Cmd_RemoveCommand: %s not added\\n\", cmd_name);\n\t\t\treturn;\n\t\t}\n\t\tif (!strcmp (cmd_name, cmd->name))\n\t\t{\n\t\t\t*back = cmd->next;\n\t\t\tZ_Free (cmd);\n\t\t\treturn;\n\t\t}\n\t\tback = &cmd->next;\n\t}\n}\nvoid\tCmd_RemoveCommands (xcommand_t function)\n{\n\tcmd_function_t\t*cmd, **back;\n\n\tfor (back = &cmd_functions; (cmd = *back); )\n\t{\n\t\tif (cmd->function == function)\n\t\t{\n\t\t\t*back = cmd->next;\n\t\t\tZ_Free (cmd);\n\t\t\tcontinue;\n\t\t}\n\t\tback = &cmd->next;\n\t}\n}\n\nstatic void Cmd_RestrictCommand_f (void)\n{\n\tcmdalias_t *a;\n\tcvar_t *v;\n\tcmd_function_t\t*cmd;\n\tchar *cmd_name = Cmd_Argv(1);\n\tint level;\n\n\tif (Cmd_Argc() != 3 && Cmd_Argc() != 2)\n\t{\n\t\tCon_Printf(\"restrict <commandname> [level]\\n\");\n\t\treturn;\n\t}\n\n\tif (Cmd_Argc() > 2)\n\t{\n\t\tlevel = atoi(Cmd_Argv(2));\n\t\tif (level > RESTRICT_MAX)\n\t\t{\n\t\t\tlevel = RESTRICT_MAX;\n\t\t\tif (level > Cmd_ExecLevel)\n\t\t\t{\n\t\t\t\tCon_TPrintf(\"You arn't allowed to raise a command above your own level\\n\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\telse if (level < RESTRICT_MIN)\n\t\t\tlevel = RESTRICT_MIN;\n\t}\n\telse level = 0;\n//commands\n\tfor (cmd=cmd_functions ; cmd ; cmd=cmd->next)\n\t{\n\t\tif (!Q_strcmp (cmd_name, cmd->name))\n\t\t{\n\t\t\tif (Cmd_Argc() == 2)\n\t\t\t{\n\t\t\t\tif (cmd->restriction)\n\t\t\t\t\tCon_TPrintf (\"%s is restricted to %i\\n\", cmd_name, (int)cmd->restriction);\n\t\t\t\telse\n\t\t\t\t\tCon_TPrintf (\"%s is restricted to rcon_level (%i)\\n\", cmd_name, rcon_level.ival);\n\t\t\t}\n\t\t\telse if ((cmd->restriction?cmd->restriction:rcon_level.ival) > Cmd_ExecLevel)\n\t\t\t\tCon_TPrintf(\"You arn't allowed to alter a level above your own\\n\");\n\t\t\telse\n\t\t\t\tcmd->restriction = level;\n\t\t\treturn;\n\t\t}\n\t}\n\n//cvars\n\tv = Cvar_FindVar(cmd_name);\n\tif (v)\n\t{\n\t\tif (Cmd_Argc() == 2)\n\t\t{\n\t\t\tif (v->restriction)\n\t\t\t\tCon_TPrintf (\"%s is restricted to %i\\n\", cmd_name, (int)v->restriction);\n\t\t\telse\n\t\t\t\tCon_TPrintf (\"%s is restricted to rcon_level (%i)\\n\", cmd_name, rcon_level.ival);\n\t\t}\n\t\telse if ((v->restriction?v->restriction:rcon_level.ival) > Cmd_ExecLevel)\n\t\t\tCon_TPrintf(\"You arn't allowed to alter a level above your own\\n\");\n\t\telse\n\t\t\tv->restriction = level;\n\n\t\treturn;\n\t}\n\n\t// check alias\n\tfor (a=cmd_alias ; a ; a=a->next)\n\t{\n\t\tif (!Q_strcasecmp (cmd_name, a->name))\n\t\t{\n\t\t\tif (Cmd_Argc() == 2)\n\t\t\t{\n\t\t\t\tif (a->restriction)\n\t\t\t\t\tCon_TPrintf (\"%s is restricted to %i\\n\", cmd_name, (int)a->restriction);\n\t\t\t\telse\n\t\t\t\t\tCon_TPrintf (\"%s is restricted to rcon_level (%i)\\n\", cmd_name, rcon_level.ival);\n\t\t\t}\n\t\t\telse if ((a->restriction?a->restriction:rcon_level.ival) > Cmd_ExecLevel)\n\t\t\t\tCon_TPrintf(\"You arn't allowed to alter a level above your own\\n\");\n\t\t\telse\n\t\t\t\ta->restriction = level;\n\t\t\treturn;\n\t\t}\n\t}\n\n\tCon_TPrintf (\"restrict: %s not defined\\n\", cmd_name);\n\treturn;\n}\n\nvoid Cmd_EnumerateLevel(int level, char *buf, size_t bufsize)\n{\n\tcmdalias_t *a;\n\tcmd_function_t *cmds;\n\tint cmdlevel;\n\t*buf = 0;\n\tfor (cmds = cmd_functions; cmds; cmds=cmds->next)\n\t{\n\t\tcmdlevel = cmds->restriction?cmds->restriction:rcon_level.ival;\n\n\t\tif (level == cmdlevel)\n\t\t{\n\t\t\tif (*buf)\n\t\t\t\tQ_strncatz(buf, \"\\t\", bufsize);\n\t\t\tQ_strncatz(buf, cmds->name, bufsize);\n\t\t}\n\t}\n\tfor (a=cmd_alias ; a ; a=a->next)\n\t{\n\t\tcmdlevel = a->restriction?a->restriction:rcon_level.ival;\n\n\t\tif (level == cmdlevel)\n\t\t{\n\t\t\tif (*buf)\n\t\t\t\tQ_strncatz(buf, \"\\t\", bufsize);\n\t\t\tQ_strncatz(buf, a->name, bufsize);\n\t\t}\n\t}\n}\n\nint Cmd_Level(const char *name)\n{\n\tcmdalias_t *a;\n\tcmd_function_t *cmds;\n\tfor (cmds = cmd_functions; cmds; cmds=cmds->next)\n\t{\n\t\tif (!strcmp(cmds->name, name))\n\t\t{\n\t\t\treturn cmds->restriction?cmds->restriction:rcon_level.ival;\n\t\t}\n\t}\n\tfor (a=cmd_alias ; a ; a=a->next)\n\t{\n\t\tif (!strcmp(a->name, name))\n\t\t{\n\t\t\treturn a->restriction?a->restriction:rcon_level.ival;\n\t\t}\n\t}\n\treturn -1;\n}\n\n/*\n============\nCmd_Exists\n============\n*/\nqboolean\tCmd_Exists (const char *cmd_name)\n{\n\tcmd_function_t\t*cmd;\n\n\tfor (cmd=cmd_functions ; cmd ; cmd=cmd->next)\n\t{\n\t\tif (!Q_strcmp (cmd_name,cmd->name))\n\t\t\treturn true;\n\t}\n\n\treturn false;\n}\n\n/*\n============\nCmd_Exists\n============\n*/\nconst char *Cmd_Describe (const char *cmd_name)\n{\n\tcmd_function_t\t*cmd;\n\n\tfor (cmd=cmd_functions ; cmd ; cmd=cmd->next)\n\t{\n\t\tif (!Q_strcmp (cmd_name,cmd->name))\n\t\t\treturn cmd->description;\n\t}\n\n\treturn NULL;\n}\n\n\n/*\n============\nCmd_CompleteCommand\n============\n*/\n\n\nstruct cmdargcompletion_ctx_s\n{\n\tstruct xcommandargcompletioncb_s cb;\n\tcmd_function_t *cmd;\n\tconst char *prefix;\n\tsize_t prefixlen;\n\tqboolean quoted;\n\tcmd_completion_t *res;\n\tconst char *desc;\n};\nfte_inlinestatic int Q_tolower(char c)\n{\n\tif (c >= 'a' && c <= 'z')\n\t\tc -= ('a' - 'A');\n\treturn c;\n}\nstatic void Cmd_Complete_CheckArg(const char *value, const char *desc, const char *repl, struct xcommandargcompletioncb_s *vctx)\t//compare cumulative strings and join the result\n{\n\tstruct cmdargcompletion_ctx_s *ctx = (struct cmdargcompletion_ctx_s*)vctx;\n\tcmd_completion_t *res = ctx->res;\n\tchar *text;\n\n\tconst char *c;\n\tchar *p;\n\tchar quoted[8192];\n\n\tif (!desc && ctx->desc)\t//if no arg desc, use the command's.\n\t\tdesc = localtext(ctx->desc);\n\n\tif (strchr(value, ' ') || strchr(value, '\\t') || strchr(value, '\\\"') || strchr(value, '\\r') || strchr(value, '\\n'))\n\t{\n\t\tif (/*ctx->prefix[ctx->prefixlen] &&*/ !ctx->quoted)\t//FIXME... Figure out some way to insert quotes earlier in the completion without it bugging out\n\t\t\treturn;\n\t}\n\n\tif (ctx->quoted)\n\t{\n\t\tvalue = COM_QuotedString(value, quoted, sizeof(quoted), false);\n\t\tvalue++;\n\t}\n\n\tif (!res->guessed)\n\t{\n\t\ttext = BZ_Malloc(ctx->prefixlen + strlen(value) + 1);\n\t\tmemcpy(text, ctx->prefix, ctx->prefixlen);\n\t\tstrcpy(text+ctx->prefixlen, value);\n\t\tres->guessed = text;\n\t}\n\telse\n\t{\n\t\tfor (p = res->guessed, c = ctx->prefix; *p && c < ctx->prefix+ctx->prefixlen; p++, c++)\n\t\t{\n\t\t\tif (Q_tolower(*p) != Q_tolower(*c))\n\t\t\t{\n\t\t\t\t*p = 0;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (c == ctx->prefix+ctx->prefixlen)\n\t\t\tfor (c = value; *p; p++, c++)\n\t\t\t{\n\t\t\t\tif (Q_tolower(*p) != Q_tolower(*c))\n\t\t\t\t{\n\t\t\t\t\t*p = 0;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tif (!*c)\n\t\t\t\t\tbreak;\n\t\t\t}\n\t}\n\t\n\n\tif (res->num == countof(res->completions))\n\t{\n\t\tres->extra++;\n\t\treturn;\t//no more space for more options\n\t}\n\n\ttext = BZ_Malloc(ctx->prefixlen + strlen(value) + 1);\n\tmemcpy(text, ctx->prefix, ctx->prefixlen);\n\tstrcpy(text+ctx->prefixlen, value);\n\n\tif (repl)\n\t{\n\t\tp = BZ_Malloc(ctx->prefixlen + strlen(repl) + 2);\n\t\tmemcpy(p, ctx->prefix, ctx->prefixlen);\n\t\tstrcpy(p+ctx->prefixlen, repl);\n\t\tif (value == quoted+1)\n\t\t\tQ_strcat(p, \"\\\"\");\n\t\trepl = p;\n\t}\n\n\tres->completions[res->num].text_alloced = true;\n\tres->completions[res->num].text = text;\n\tres->completions[res->num].desc_alloced = true;\n\tres->completions[res->num].desc = desc?Z_StrDup(desc):NULL;\n\tres->completions[res->num].repl = repl;\n\tres->num++;\n}\n\nstatic void Cmd_Complete_Check(const char *check, cmd_completion_t *res, const char *desc)\t//compare cumulative strings and join the result\n{\n\tconst char *c;\n\tchar *p;\n\tif (!res->guessed)\n\t\tres->guessed = Z_StrDup(check);\n\telse for (p = res->guessed, c = check; *p; p++, c++)\n\t{\t//we need to do this stuff here because we're not always tracking all of them.\n\t\tif (Q_tolower(*p) != Q_tolower(*c))\n\t\t{\n\t\t\t*p = 0;\n\t\t\tbreak;\n\t\t}\n\t\t\n\t\tif (!*c)\n\t\t\tbreak;\n\t}\n\n\tif (res->num == countof(res->completions))\n\t{\n\t\tres->extra++;\n\t\treturn;\t//no more space for more options\n\t}\n\n\tres->completions[res->num].text_alloced = false;\n\tres->completions[res->num].text = check;\n\tres->completions[res->num].desc_alloced = false;\n\tres->completions[res->num].desc = desc;\n\tres->completions[res->num].repl = NULL;\n\tres->num++;\n}\nstatic void Cmd_Complete_End(cmd_completion_t *c)\n{\n\tsize_t u;\n\tfor (u = 0; u < c->num; u++)\n\t{\n\t\tif (c->completions[u].text_alloced)\n\t\t\tZ_Free((char*)c->completions[u].text);\n\t\tc->completions[u].text_alloced = false;\n\t\tc->completions[u].text = NULL;\n\n\t\tif (c->completions[u].desc_alloced)\n\t\t\tZ_Free((char*)c->completions[u].desc);\n\t\tc->completions[u].desc_alloced = false;\n\t\tc->completions[u].desc = NULL;\n\n\t\tc->completions[u].repl = NULL;\n\t}\n\tc->num = 0;\n\tc->extra = 0;\n\tZ_Free(c->guessed);\n\tc->guessed = NULL;\n\tZ_Free(c->partial);\n\tc->partial = NULL;\n}\nstatic int QDECL Cmd_Complete_Sort(const void *a, const void *b)\n{\t//FIXME: its possible that they're equal (eg: filesystem searches). we should strip one in that case, but gah.\n\tconst struct cmd_completion_opt_s *c1 = a, *c2 = b;\n\treturn Q_strcasecmp(c1->text, c2->text);\n}\ncmd_completion_t *Cmd_Complete(const char *partial, qboolean caseinsens)\n{\n\textern cvar_group_t *cvar_groups;\n\tcmd_function_t\t*cmd;\n\tint\t\t\t\tlen;\n\tcmdalias_t\t\t*a;\n\n\tcvar_group_t\t*grp;\n\tcvar_t\t\t*cvar;\n\tconst char *sp, *e;\n\tqboolean quoted = false;\n\tint arg = 0;\n\n\tstatic cmd_completion_t c;\n\n\tif (!partial)\n\t{\n\t\tCmd_Complete_End(&c);\n\t\treturn NULL;\n\t}\n\n\tif ((c.partial && !strcmp(partial, c.partial)) && c.caseinsens == caseinsens)\n\t\treturn &c;\t//still valid.\n\tCmd_Complete_End(&c);\n\tc.partial = Z_StrDup(partial);\n\tc.caseinsens = caseinsens;\n\n\tlen = 0;\n\tfor(e = partial;;)\n\t{\n\t\tsp = e;\t//the start of where we're trying to complete...\n\t\twhile (*sp == ' ' || *sp == '\\t')\n\t\t\tsp++;\t//leading spaces are annoying...\n\t\te = COM_Parse(sp);\n\t\tif (!arg && e)\n\t\t\tlen = e - partial;\n\t\tif (e && (*e == ' ' || *e == '\\t'))\n\t\t{\t//there seems to be whitespace after it.\n\t\t\targ++;\n\t\t\twhile (*sp == ' ' || *sp == '\\t')\n\t\t\t\tsp++;\n\t\t\t//try to handle quotes\n\t\t\tif (*sp == '\\\\' && sp[1] == '\\\"')\n\t\t\t{\n\t\t\t\tsp+=2;\n\t\t\t\tquoted = true;\n\t\t\t}\n\t\t\telse if (*sp == '\\\"')\n\t\t\t{\n\t\t\t\tsp++;\n\t\t\t\tquoted = true;\n\t\t\t}\n\t\t\telse\n\t\t\t\tquoted = false;\n\t\t}\n\t\telse\n\t\t\tbreak;\n\t}\n\n//\tif (len)\n\t{\n\t\tif (caseinsens)\n\t\t{\n\t\t\tfor (cmd=cmd_functions ; cmd ; cmd=cmd->next)\n\t\t\t\tif (!Q_strncasecmp (partial,cmd->name, len) && (!partial[len] || strlen(cmd->name) == len))\n\t\t\t\t{\n\t\t\t\t\tif (arg)\n\t\t\t\t\t{\n\t\t\t\t\t\tstruct cmdargcompletion_ctx_s ctx;\n\t\t\t\t\t\tctx.cb.cb = Cmd_Complete_CheckArg;\n\t\t\t\t\t\tctx.cmd = cmd;\n\t\t\t\t\t\tctx.prefix = partial;\n\t\t\t\t\t\tctx.prefixlen = sp-partial;\n\t\t\t\t\t\tctx.res = &c;\n\t\t\t\t\t\tctx.desc = cmd->description;\n\t\t\t\t\t\tctx.quoted = quoted;\n\n\t\t\t\t\t\tCmd_MacroCompletion_c(arg, sp, &ctx.cb);\n\t\t\t\t\t\tif (cmd->argcompletion)\n\t\t\t\t\t\t\tcmd->argcompletion(arg, sp, &ctx.cb);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\tCmd_Complete_Check(cmd->name, &c, cmd->description);\n\t\t\t\t}\n\t\t\tfor (a=cmd_alias ; a ; a=a->next)\n\t\t\t\tif (!Q_strncasecmp (partial, a->name, len) && (!partial[len] || strlen(a->name) == len))\n\t\t\t\t\tCmd_Complete_Check(a->name, &c, a->value);\n\t\t\tfor (grp=cvar_groups ; grp ; grp=grp->next)\n\t\t\tfor (cvar=grp->cvars ; cvar ; cvar=cvar->next)\n\t\t\t{\n\t\t\t\tif (!Q_strncasecmp (partial,cvar->name, len) && (!partial[len] || strlen(cvar->name) == len))\n\t\t\t\t\tCmd_Complete_Check(cvar->name, &c, cvar->description);\n\t\t\t\tif (cvar->name2 && !Q_strncasecmp (partial,cvar->name2, len) && (!partial[len] || strlen(cvar->name2) == len))\n\t\t\t\t\tCmd_Complete_Check(cvar->name2, &c, cvar->description);\n\t\t\t}\n\n\t\t}\n\t\telse\n\t\t{\n\t\t\tfor (cmd=cmd_functions ; cmd ; cmd=cmd->next)\n\t\t\t\tif (!Q_strncmp (partial,cmd->name, len) && (!partial[len] || strlen(cmd->name) == len))\n\t\t\t\t\tCmd_Complete_Check(cmd->name, &c, cmd->description);\n\t\t\tfor (a=cmd_alias ; a ; a=a->next)\n\t\t\t\tif (!Q_strncmp (partial, a->name, len) && (!partial[len] || strlen(a->name) == len))\n\t\t\t\t\tCmd_Complete_Check(a->name, &c, \"\");\n\t\t\tfor (grp=cvar_groups ; grp ; grp=grp->next)\n\t\t\tfor (cvar=grp->cvars ; cvar ; cvar=cvar->next)\n\t\t\t{\n\t\t\t\tif (!Q_strncmp (partial,cvar->name, len) && (!partial[len] || strlen(cvar->name) == len))\n\t\t\t\t\tCmd_Complete_Check(cvar->name, &c, cvar->description);\n\t\t\t\tif (cvar->name2 && !Q_strncmp (partial,cvar->name2, len) && (!partial[len] || strlen(cvar->name2) == len))\n\t\t\t\t\tCmd_Complete_Check(cvar->name2, &c, cvar->description);\n\t\t\t}\n\t\t}\n\t}\n\n\t//quickly sort the completions. this is primarily so that the first item is the shortest, but whatever.\n\tqsort(c.completions, c.num, sizeof(c.completions[0]), Cmd_Complete_Sort);\n\treturn &c;\n}\n\nchar *Cmd_CompleteCommand (const char *partial, qboolean fullonly, qboolean caseinsens, int matchnum, const char **descptr)\n{\n\tcmd_completion_t *c = Cmd_Complete(partial, caseinsens);\n\tconst char *text = NULL, *desc = NULL;\n\n\tif (matchnum < 0)\n\t{\t//completes only if there's an EXACT match.\n\t\tfor (matchnum = 0; matchnum < c->num; matchnum++)\n\t\t{\n\t\t\tif (!strcmp(partial, c->completions[matchnum].text))\n\t\t\t{\n\t\t\t\ttext = c->completions[matchnum].text;\n\t\t\t\tdesc = c->completions[matchnum].desc;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\telse if (!matchnum)\n\t{\t//returns the longest-common-denominator of all the matches\n\t\ttext = c->guessed;\n\t\tdesc = ((c->num==1)?c->completions[matchnum].desc:NULL);\n\t}\n\telse\n\t{\n\t\tmatchnum--;\n\t\tif (matchnum < c->num)\n\t\t{\n\t\t\ttext = c->completions[matchnum].text;\n\t\t\tdesc = c->completions[matchnum].desc;\n\t\t}\n\t}\n\n\tif (descptr)\n\t\t*descptr = desc;\n\treturn (char*)text;\n}\n\n\n//lists commands, also prints restriction level\nstatic void Cmd_List_f (void)\n{\n\tcmd_function_t\t*cmd;\n\tint num=0;\n\tconst char *search = (Cmd_Argc()>1)?Cmd_Argv(1):NULL;\n\tfor (cmd=cmd_functions ; cmd ; cmd=cmd->next)\n\t{\n\t\tif ((cmd->restriction?cmd->restriction:rcon_level.ival) > Cmd_ExecLevel)\n\t\t\tcontinue;\n\n\t\tif (search)\n\t\t\tif (!wildcmp(search, cmd->name))\n\t\t\t\tcontinue;\t//nope, no match\n\n\t\tif (!num)\n\t\t\tCon_TPrintf(\"Command list:\\n\");\n\t\tCon_Printf(\"(%2i) %s\\n\", (int)(cmd->restriction?cmd->restriction:rcon_level.ival), cmd->name);\n\t\tnum++;\n\t}\n\tif (num)\n\t\tCon_Printf(\"\\n\");\n}\n\n//I'm not personally keen on this name, but its somewhat standard in both DP and suse (which lh uses, hence why DP uses that name). oh well.\nstatic void Cmd_Apropos_f (void)\n{\n\textern cvar_group_t *cvar_groups;\n\tcmd_function_t\t*cmd;\n\tcvar_group_t\t*grp;\n\tcvar_t\t*var;\n\tchar *name;\n\tchar escapedvalue[1024];\n\tchar latchedvalue[1024];\n\tchar *query = Cmd_Argv(1);\n\tconst char *d;\n\n\tfor (grp=cvar_groups ; grp ; grp=grp->next)\n\tfor (var=grp->cvars ; var ; var=var->next)\n\t{\n\t\td = var->description?localtext(var->description):NULL;\n\t\tif (var->name && Q_strcasestr(var->name, query))\n\t\t\tname = var->name;\n\t\telse if (var->name2 && Q_strcasestr(var->name2, query))\n\t\t\tname = var->name2;\n\t\telse if (d && Q_strcasestr(d, query))\n\t\t\tname = var->name;\n\t\telse\n\t\t\tcontinue;\n\t\t\n\t\tCOM_QuotedString(var->string, escapedvalue, sizeof(escapedvalue), false);\n\n\t\tif (var->latched_string)\n\t\t{\n\t\t\tCOM_QuotedString(var->latched_string, latchedvalue, sizeof(latchedvalue), false);\n\t\t\tif (d)\n\t\t\t\tCon_TPrintf(\"cvar ^[^2%s\\\\type\\\\%s^]: %s (effective %s): ^3%s\\n\", name,name, latchedvalue, escapedvalue, d);\n\t\t\telse\n\t\t\t\tCon_TPrintf(\"cvar ^[^2%s\\\\type\\\\%s^]: %s (effective %s): ^3no description\\n\", name,name, latchedvalue, escapedvalue);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (d)\n\t\t\t\tCon_TPrintf(\"cvar ^[^2%s\\\\type\\\\%s^]: %s : ^3%s\\n\", name,name, escapedvalue, d);\n\t\t\telse\n\t\t\t\tCon_TPrintf(\"cvar ^[^2%s\\\\type\\\\%s^]: %s : ^3no description\\n\", name,name, escapedvalue);\n\t\t}\n\t}\n\n\tfor (cmd=cmd_functions ; cmd ; cmd=cmd->next)\n\t{\n\t\td = cmd->description?localtext(cmd->description):NULL;\n\t\tif (cmd->name && Q_strcasestr(cmd->name, query))\n\t\t\t;\n\t\telse if (d && strstr(d, query))\n\t\t\t;\n\t\telse\n\t\t\tcontinue;\n\n\t\tif (d)\n\t\t\tCon_TPrintf(\"command ^[^2%s\\\\type\\\\%s^]: ^3%s\\n\", cmd->name,cmd->name, d);\n\t\telse\n\t\t\tCon_TPrintf(\"command ^[^2%s\\\\type\\\\%s^]: ^3no description\\n\", cmd->name,cmd->name);\n\t}\n\t//FIXME: add aliases.\n}\n\n#ifdef HAVE_CLIENT\t\t// FIXME\n/*\n===================\nCmd_ForwardToServer\n\nadds the current command line as a clc_stringcmd to the client message.\nthings like godmode, noclip, etc, are commands directed to the server,\nso when they are typed in at the console, they will need to be forwarded.\n===================\n*/\nvoid Cmd_ForwardToServer (void)\n{\n\tint sp;\n\tif (cls.state == ca_disconnected)\n\t{\n\t\tif (cl_warncmd.ival)\n\t\t\tCon_TPrintf (\"Can't \\\"%s\\\", not connected\\n\", Cmd_Argv(0));\n\t\treturn;\n\t}\n\n\tif (cls.demoplayback)\n\t\treturn;\t\t// not really connected\n\n#ifdef Q3CLIENT\n\tif (cls.protocol == CP_QUAKE3)\n\t{\n\t\tq3->cl.SendClientCommand(\"%s %s\", Cmd_Argv(0), Cmd_Args());\n\t\treturn;\n\t}\n#endif\n\n\tsp = CL_TargettedSplit(false);\n\tif (Cmd_Argc() > 1)\n\t\tCL_SendSeatClientCommand(true, sp, \"%s %s\", Cmd_Argv(0), Cmd_Args());\n\telse\n\t\tCL_SendSeatClientCommand(true, sp, \"%s\", Cmd_Argv(0));\n}\n\n// don't forward the first argument\nstatic void Cmd_ForwardToServer_f (void)\n{\n\tif (cls.state == ca_disconnected)\n\t{\n\t\tCon_TPrintf (\"Can't \\\"%s\\\", not connected\\n\", Cmd_Argv(0));\n\t\treturn;\n\t}\n\n#ifdef IMAGEFMT_PCX\n\tif (Q_strcasecmp(Cmd_Argv(1), \"snap\") == 0 && cls.protocol == CP_QUAKEWORLD)\n\t{\n\t\tif (SCR_RSShot())\n\t\t\treturn;\n\t}\n#endif\n#ifdef NQPROT\n\tif (Q_strcasecmp(Cmd_Argv(1), \"protocols\") == 0 && cls.protocol == CP_NETQUAKE)\n\t{\n\t\tCL_SendClientCommand(true, \"protocols %#x %#x %#x %#x %#x\", PROTOCOL_VERSION_RMQ, PROTOCOL_VERSION_FITZ, PROTOCOL_VERSION_BJP3, PROTOCOL_VERSION_BJP2, PROTOCOL_VERSION_DP7);\n\t\treturn;\n\t}\n#endif\n\tif (Q_strcasecmp(Cmd_Argv(1), \"pext\") == 0 && (cls.protocol != CP_NETQUAKE || cls.fteprotocolextensions2 || cls.protocol_nq != CPNQ_ID || cls.proquake_angles_hack || cls.netchan.remote_address.type != NA_LOOPBACK))\n\t{\t//don't send any extension flags this if we're using cl_loopbackprotocol nqid, purely for a compat test.\n\t\t//if you want to record compat-demos, disable extensions instead.\n\t\tunsigned int\tfp1 = Net_PextMask(PROTOCOL_VERSION_FTE1, cls.protocol == CP_NETQUAKE),\n\t\t\t\t\t\tfp2 = Net_PextMask(PROTOCOL_VERSION_FTE2, cls.protocol == CP_NETQUAKE),\n\t\t\t\t\t\tez1 = Net_PextMask(PROTOCOL_VERSION_EZQUAKE1, cls.protocol == CP_NETQUAKE) & EZPEXT1_CLIENTADVERTISE;\n\t\textern cvar_t cl_nopext;\n\t\tchar line[256];\n\t\tif (cl_nopext.ival)\n\t\t{\n\t\t\tfp1 = 0;\n\t\t\tfp2 = 0;\n\t\t}\n\t\tQ_strncpyz(line, \"pext\", sizeof(line));\n\t\tif (fp1)\n\t\t\tQ_strncatz(line, va(\" %#x %#x\", PROTOCOL_VERSION_FTE1, fp1), sizeof(line));\n\t\tif (fp2)\n\t\t\tQ_strncatz(line, va(\" %#x %#x\", PROTOCOL_VERSION_FTE2, fp2), sizeof(line));\n\t\tif (ez1)\n\t\t\tQ_strncatz(line, va(\" %#x %#x\", PROTOCOL_VERSION_EZQUAKE1, ez1), sizeof(line));\n\t\tCL_SendClientCommand(true, \"%s\", line);\n\t\treturn;\n\t}\n\tif (Q_strcasecmp(Cmd_Argv(1), \"ptrack\") == 0)\n\t{\n\t\tplayerview_t *pv = &cl.playerview[CL_TargettedSplit(false)];\n\t\tif (!*Cmd_Argv(2))\n\t\t\tCam_Unlock(pv);\n\t\telse\n\t\t\tCam_Lock(pv, atoi(Cmd_Argv(2)));\n\t\treturn;\n\t}\n\n\tif (Cmd_Argc() > 1)\n\t{\n\t\tint split = CL_TargettedSplit(false);\n\t\tCL_SendSeatClientCommand(true, split, \"%s\", Cmd_Args());\n\t}\n}\n#else\nvoid Cmd_ForwardToServer (void)\n{\n}\n#endif\n\n\n\nstatic void\tCmd_FindForExecution (const char *name, int level, cmd_function_t **foundcmd, cmdalias_t **foundalias, cvar_t **foundcvar)\n{\n\t//WARNING: PF_checkcommand should match the order.\n\tcmd_function_t\t*cmd;\n\tcmdalias_t\t\t*a;\n\n\t*foundcmd = NULL;\n\t*foundalias = NULL;\n\t*foundcvar = NULL;\n\n\tfor (cmd=cmd_functions ; cmd ; cmd=cmd->next)\n\t{\n\t\tif (!Q_strcasecmp (name, cmd->name))\n\t\t{\n\t\t\t*foundcmd = cmd;\n\t\t\tif (!strcmp (name, cmd->name))\n\t\t\t\tbreak;\t//don't keep looking for others when we get an exact match.\n\t\t}\n\t}\n\tcmd = *foundcmd;\n\tif (!cmd)\n\t\t;\n\telse if (level == RESTRICT_TEAMPLAY)\n\t{\t//extra weirdness so that teamplay macros can only execute certain known commands\n\t\tstatic char *tpcmds[] =\n\t\t{\n\t\t\t\"if\", \"wait\",\t\t\t\t\t\t/*would be nice to include alias in here*/\n\t\t\t\"say\", \"say_team\", \"echo\",\t\t\t/*display stuff, because it would be useless otherwise*/\n\t\t\t\"set_tp\", \"set\", \"set_calc\", \"inc\",\t/*because scripting variables is fun. not.*/\n\t\t\t\"tp_point\", \"tp_pickup\", \"tp_took\"\t/*updates what the $took etc macros are allowed to generate*/\n\t\t};\n\t\tsize_t i;\n\t\tfor (i = 0; i < countof(tpcmds); i++)\n\t\t\tif (!strcmp(cmd->name, tpcmds[i]))\n\t\t\t\tbreak;\n\t\tif (i == countof(tpcmds))\n\t\t\t*foundcmd = NULL;\n\t\telse if (cmd->restriction && cmd->restriction > 0)\n\t\t{\n\t\t\t//warning, these commands would normally be considered to be run at restrict_local, but they're running at a much lower level\n\t\t\t//which means that if there's ANY restriction on them then they'll fail.\n\t\t\t//this means we have to ignore the default restriction levels and just do it anyway.\n\t\t\tCon_TPrintf(\"'%s' was restricted.\\n\", cmd_argv[0]);\n\t\t\t*foundcmd = NULL;\n\t\t}\n\t}\n\telse if ((cmd->restriction?cmd->restriction:rcon_level.ival) > level)\n\t{\n\t\tCon_TPrintf(\"cmd '%s' was restricted.\\n\", name);\n\t\t*foundcmd = NULL;\n\t}\n\n// check alias\n\tfor (a=cmd_alias ; a ; a=a->next)\n\t{\n\t\tif (!Q_strcasecmp (cmd_argv[0], a->name))\n\t\t{\n\t\t\t//teamplay restrictions block any execlevel elevations, so the contents are what matter\n\t\t\t//(there's no reason to restrict aliases other than for exec level promotion)\n\t\t\tif (level!=RESTRICT_TEAMPLAY)\n\t\t\tif ((a->restriction?a->restriction:rcon_level.ival) > level)\n\t\t\t{\n\t\t\t\tCon_TPrintf(\"alias '%s' was restricted.\\n\", cmd_argv[0]);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t*foundalias = a;\n\t\t\tif (!strcmp (name, a->name))\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n// check cvars\n\t*foundcvar = Cvar_FindVar(name);\n}\n\n/*\n============\nCmd_ExecuteString\n\nA complete command line has been parsed, so try to execute it\nFIXME: lookupnoadd the token to speed search?\n============\n*/\nvoid Cmd_ExecuteString (const char *text, int level)\n{\n\t//WARNING: PF_checkcommand should match the order.\n\tcmd_function_t\t*cmd;\n\tcmdalias_t\t\t*a;\n\tcvar_t\t\t\t*var;\n\tint olev = Cmd_ExecLevel;\n\n\tchar dest[65536];\n\n\twhile (*text == ' ' || *text == '\\n')\n\t\ttext++;\n\tif (dpcompat_console.ival && !strncmp(text, \"alias\", 5) && (text[5] == ' ' || text[5] == '\\t'))\n\t\t;\t//certain commands don't get pre-expanded in dp. evil hack. quote them to pre-expand anyway. double evil.\n\telse\n\t\ttext = Cmd_ExpandString(text, dest, sizeof(dest), &level, false, true/*!Cmd_IsInsecure()?true:false*/, true);\n\tCmd_TokenizeString (text, (level == RESTRICT_LOCAL&&!dpcompat_console.ival)?true:false, false);\n\n// execute the command line\n\tif (!Cmd_Argc())\n\t\treturn;\t\t// no tokens\n\n\tCmd_FindForExecution (cmd_argv[0], level, &cmd, &a, &var);\n\n//check (explicit) functions\n\tif (cmd && cmd->function)\n\t{\n\t\tCmd_ExecLevel = level;\n\t\tcmd->function();\n\t\tCmd_ExecLevel = olev;\n\t\treturn;\n\t}\n\n\t//priority is cmd>alias>cvar\n\t//but this means that user aliases can override cvars\n\t//which means users can use aliases to block cvar access, aka cheat.\n\t//so favour the cvar when its a server command (unless the alias was also created by the server)\n\tif (a && (!var || Cmd_ExecLevel<RESTRICT_SERVER || a->flags&ALIAS_FROMSERVER))\n\t{\n\t\tint execlevel;\n\n#ifdef HAVE_CLIENT\t//an emergency escape mechansim, to avoid infinatly recursing aliases.\n\t\textern unsigned int con_splitmodifier;\n\n\t\tif (keydown[K_SHIFT] && (keydown[K_LCTRL]||keydown[K_RCTRL]) && (keydown[K_LALT]||keydown[K_RALT]) && !isDedicated)\n\t\t\treturn;\n#endif\n\n\t\tCmd_ExecLevel = level;\n\n\t\tif (level == RESTRICT_TEAMPLAY)\n\t\t\texeclevel = level;\t//teamplay aliases can't let the user's settings promote them out of their restrictions.\n\t\telse\n\t\t{\n\t\t\tif (a->execlevel)\n\t\t\t\texeclevel = a->execlevel;\n\t\t\telse\n\t\t\t\texeclevel = level;\n\t\t}\n\n\t\t// if the alias value is a command or cvar and\n\t\t// the alias is called with parameters, add them\n\t\t//unless we're mimicing dp, or the alias has explicit expansions (or macros) in which case it can do its own damn args\n\t\tif (dpcompat_console.ival)\n\t\t{\t//defective double escaping. the following line should sum it up nicely...\n\t\t\t//set foo 3; alias test \"set foo 2; echo $foo==1\"; set foo 1; test\n\t\t\tchar *ignoringquoteswasstupid;\n\t\t\tCmd_ExpandString(a->value, dest, sizeof(dest), &execlevel, true, !Cmd_IsInsecure()?true:false, true);\n\t\t\tfor (ignoringquoteswasstupid = dest; *ignoringquoteswasstupid; )\n\t\t\t{\t//double up dollars, to prevent expansion when its actually execed.\n\t\t\t\tif (*ignoringquoteswasstupid == '$')\n\t\t\t\t{\n\t\t\t\t\tmemmove(ignoringquoteswasstupid+1, ignoringquoteswasstupid, strlen(ignoringquoteswasstupid)+1);\n\t\t\t\t\tignoringquoteswasstupid++;\n\t\t\t\t}\n\t\t\t\tignoringquoteswasstupid++;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\t//more sane (and ezquake-like)\n\t\t\t//set foo 3; alias test \"set foo 2; echo $foo==2\"; set foo 1; test\n\t\t\t//alias test \"echo Args were $qt${* q}$qt\"; set foo 1; test Test Args Here\n\t\t\tCmd_ExpandString(a->value, dest, sizeof(dest), &execlevel, true, false, false);\t//expand args, but not other stuff.\n\t\t}\n\t\tif ((a->restriction?a->restriction:rcon_level.ival) > execlevel)\n\t\t\treturn;\t//we expanded something it wasn't meant to see.\n\n\t\tCbuf_InsertText (\"\\n\", execlevel, false);\n\t\tif (!dpcompat_console.ival)\n\t\t{\n\t\t\tif (Cmd_Argc() > 1 && (!strncmp(a->value, \"cmd \", 4) || (!strchr(a->value, ' ') && !strchr(a->value, '\\t')\t&&\n\t\t\t\t(Cvar_FindVar(a->value) || (Cmd_Exists(a->value) && a->value[0] != '+' && a->value[0] != '-'))))\n\t\t\t\t)\n\t\t\t{\n\t\t\t\tCbuf_InsertText (Cmd_Args(), execlevel, false);\n\t\t\t\tCbuf_InsertText (\" \", execlevel, false);\n\t\t\t}\n\t\t}\n\t\tCbuf_InsertText (dest, execlevel, false);\n\n#ifdef HAVE_CLIENT\n\t\tif (con_splitmodifier > 0)\n\t\t{\t//if the alias was execed via p1/p2 etc, make sure that propagates properly (at least for simple aliases like impulses)\n\t\t\t//fixme: should probably prefix each line. that may have different issues however.\n\t\t\t//don't need to care about + etc\n\t\t\tCbuf_InsertText (va(\"p %i \", con_splitmodifier), execlevel, false);\n\t\t}\n#endif\n\n\t\tCon_DPrintf(\"Execing alias %s ^3%s:\\n^1%s\\n^2%s\\n\", a->name, Cmd_Args(), a->value, dest);\n\t\tCmd_ExecLevel = olev;\n\t\treturn;\n\t}\n\n// check cvars\n\tCmd_ExecLevel = level;\n\tif (!cmd && Cvar_Command (var, level))\n\t\t;\n#if defined(CSQC_DAT) && defined(HAVE_CLIENT)\n\telse if (CSQC_ConsoleCommand(-1, text))\n\t\t;\n#endif\n#if defined(MENU_DAT) && defined(HAVE_CLIENT)\n\telse if (MP_ConsoleCommand(text))\n\t\t;\t//let the csqc handle it if it wants.\n#endif\n#if defined(MENU_NATIVECODE) && defined(HAVE_CLIENT)\n\tif (mn_entry && mn_entry->ConsoleCommand(text, cmd_argc, (char const*const*)cmd_argv))\n\t\t;\n#endif\n\n#ifdef HAVE_SERVER\n\telse if (sv.state && PR_ConsoleCmd(text))\n\t\t;\n#endif\n\n#if defined(VM_CG) && defined(HAVE_CLIENT)\n\telse if (q3 && q3->cg.ConsoleCommand())\n\t\t;\n#endif\n#if defined(Q3SERVER) && defined(HAVE_SERVER)\n\telse if (q3 && q3->sv.ConsoleCommand())\n\t\t;\n#endif\n#if defined(VM_UI) && defined(HAVE_CLIENT)\n\telse if (q3 && q3->ui.ConsoleCommand())\n\t\t;\n#endif\n\telse if (cmd\n#if defined(Q2CLIENT) && defined(HAVE_CLIENT)\n\t\t|| (cls.state!=ca_disconnected && (cls.protocol == CP_QUAKE2 || cls.protocol == CP_QUAKE3))\n#endif\n\t\t)\n\t{\t//q2 servers convert unknown commands to text.\n\t\tCmd_ForwardToServer();\n\t}\n\telse if ((cl_warncmd.value && level <= RESTRICT_LOCAL) || developer.value)\n\t\tCon_TPrintf (\"Unknown command \\\"%s\\\"\\n\", Cmd_Argv(0));\n\tCmd_ExecLevel = olev;\n}\n\n\n/*\n================\nCmd_CheckParm\n\nReturns the position (1 to argc-1) in the command's argument list\nwhere the given parameter apears, or 0 if not present\n================\n*/\nint Cmd_CheckParm (const char *parm)\n{\n\tint i;\n\n\tif (!parm)\n\t\tSys_Error (\"Cmd_CheckParm: NULL\");\n\n\tfor (i = 1; i < Cmd_Argc (); i++)\n\t\tif (! Q_strcasecmp (parm, Cmd_Argv (i)))\n\t\t\treturn i;\n\n\treturn 0;\n}\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\ntypedef struct tempstack_s{\n\tstruct tempstack_s *next;\n\tchar str[1];\n} tempstack_t;\nstatic tempstack_t *ifstack;\n\nstatic void If_Token_Clear (tempstack_t *mark)\n{\n\ttempstack_t *ois;\n\twhile(ifstack)\n\t{\n\t\tif (ifstack == mark)\n\t\t\tbreak;\n\t\tois = ifstack;\n\t\tifstack = ifstack->next;\n\t\tZ_Free(ois);\n\t}\n}\n\nstatic tempstack_t *If_Token_GetMark (void)\n{\n\treturn ifstack;\n}\n\n\nstatic const char *retstring(const char *s)\n{\n\ttempstack_t *ret;\n\tret = (tempstack_t*)Z_Malloc(sizeof(tempstack_t)+strlen(s));\n\tret->next = ifstack;\n\tifstack=ret;\n\tstrcpy(ret->str, s);\n\treturn ret->str;\n}\nstatic const char *retint(int f)\n{\n\tchar s[1024];\n\ttempstack_t *ret;\n\tif (!f)\n\t\treturn \"\";\n\tsprintf(s, \"%d\", f);\n\tret = (tempstack_t*)Z_Malloc(sizeof(tempstack_t)+strlen(s));\n\tret->next = ifstack;\n\tifstack=ret;\n\tstrcpy(ret->str, s);\n\treturn ret->str;\n}\nstatic const char *retbool(qboolean b)\n{\n\tif (b)\n\t\treturn \"1\";\n\treturn \"\";\n}\nstatic const char *retfloat(float f)\n{\n\tchar s[1024];\n\ttempstack_t *ret;\n\tif (!f)\n\t\treturn \"\";\n\tsprintf(s, \"%g\", f);\n\tret = (tempstack_t*)Z_Malloc(sizeof(tempstack_t)+strlen(s));\n\tret->next = ifstack;\n\tifstack=ret;\n\tstrcpy(ret->str, s);\n\treturn ret->str;\n}\nstatic qboolean is_numeric (const char *c)\n{\n\treturn (*c >= '0' && *c <= '9') ||\n\t\t((*c == '-' || *c == '+') && (c[1] == '.' || (c[1]>='0' && c[1]<='9'))) ||\n\t\t(*c == '.' && (c[1]>='0' && c[1]<='9'))?true:false;\n}\nstatic qboolean is_true (const char *c)\n{\n\tif (is_numeric(c))\n\t\treturn !!atof(c);\n\tif (!Q_strcasecmp(c, \"true\") || !Q_strcasecmp(c, \"yes\"))\n\t\treturn true;\n\tif (!Q_strcasecmp(c, \"false\") || !Q_strcasecmp(c, \"no\") || !Q_strcasecmp(c, \"null\") || !Q_strcasecmp(c, \"nil\"))\n\t\treturn false;\n\treturn !!*c;\n}\n#define IF_PRI_MAX 12\n#define IFPUNCT \"(,{})~\\':;=!><&|+*/-\"\nstatic const char *If_Token(const char *func, const char **end, int pri);\nstatic const char *If_Token_Term(const char *func, const char **end)\n{\n\tconst char *s, *s2;\n\tcvar_t *var;\n\tint level;\n\twhile(*func <= ' ' && *func)\n\t\tfunc++;\n\n\tif (*func == '\\'')\n\t{\n\t\tchar *o = com_token;\n\t\tfunc++;\n\t\twhile (*func)\n\t\t{\n\t\t\tif (*func == '\\'')\n\t\t\t{\n\t\t\t\tfunc++;\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tif (o < com_token + sizeof(com_token)-1)\n\t\t\t\t*o++ = *func;\n\t\t\tfunc++;\n\t\t}\n\t\t*o = 0;\n\t\ts = func;\n\t}\n\telse\n\t\ts = COM_ParseToken(func, IFPUNCT);\n\n\tif (*com_token == '(')\n\t{\n\t\ts2 = s;\n\t\tlevel=1;\n\t\twhile (*s2)\n\t\t{\n\t\t\tif (*s2 == ')')\n\t\t\t{\n\t\t\t\tlevel--;\n\t\t\t\tif (!level)\n\t\t\t\t{\n\t\t\t\t\ts2++;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (*s2 == '(')\n\t\t\t\tlevel++;\n\t\t\ts2++;\n\t\t}\n\t\tif (!level)\n\t\t{\n\t\t\tchar *t = malloc(s2-s+1);\n\t\t\tmemcpy(t, s, s2-s);\n\t\t\tt[s2-s-((s2==s)?0:1)] = 0;\n\t\t\tfunc = If_Token(t, end, IF_PRI_MAX);\n\t\t\tfree(t);\n\t\t}\n\t\telse\n\t\t\tfunc = If_Token(s, end, IF_PRI_MAX);\n\t\t*end = s2;\n\t\ts = *end;\n\t\ts2 = func;\n\t}\n\telse if (*com_token == '!')\n\t{\n\t\tfunc = If_Token(s, &s, 0);\n\t\ts2 = retbool(!is_true(func));\n\t}\n\telse if (*com_token == '~')\n\t{\n\t\tfunc = If_Token(s, &s, 0);\n\t\ts2 = retbool(~atoi(func));\n\t}\n\telse if (*com_token == '-')\n\t{\n\t\tfunc = If_Token(s, &s, 0);\n\t\ts2 = retfloat(-atof(func));\n\t}\n\telse if (!strcmp(com_token, \"int\"))\n\t{\n\t\tfunc = If_Token(s, &s, 0);\n\t\ts2 = retint(atoi(func));\n\t}\n\telse if (!strcmp(com_token, \"strlen\"))\n\t{\n\t\tfunc = If_Token(s, &s, 0);\n\t\ts2 = retfloat(strlen(func));\n\t}\n\telse if (!strcmp(com_token, \"eval\"))\n\t{\n\t\t//read the stuff to the right\n\t\tfunc = If_Token(s, &s, IF_PRI_MAX);\n\t\t//and evaluate it\n\t\ts2 = If_Token(func, &func, IF_PRI_MAX);\n\t}\n\telse if (!strcmp(com_token, \"defined\"))\t//functions\n\t{\n\t\ts = COM_ParseToken(s, IFPUNCT);\n\t\tvar = Cvar_FindVar(com_token);\n\t\t*end = s;\n\t\ts2 = retbool(var != NULL);\n\t}\n\telse if (!strcmp(com_token, \"random\"))\n\t{\n\t\ts2 = retfloat((rand()&0x7fff) / (float)0x7fff);\n\t}\n\telse if (!strcmp(com_token, \"vid\"))\t//mostly for use with the menu system.\n\t{\n\t\ts = COM_ParseToken(s, IFPUNCT);\n#ifdef HAVE_CLIENT\n\t\tif (qrenderer == QR_NONE)\n\t\t\ts2 = \"\";\n\t\telse if (!strcmp(com_token, \"width\"))\n\t\t\ts2 = retint(vid.width);\n\t\telse if (!strcmp(com_token, \"height\"))\n\t\t\ts2 = retint(vid.height);\n\t\telse\n#endif\n\t\t\ts2 = \"\";\n\t}\n\telse\n\t{\n\t\tif (*com_token == '$')\n\t\t\tvar = Cvar_FindVar(com_token+1);\n\t\telse\n\t\t\tvar = Cvar_FindVar(com_token);\t//for consistancy.\n\t\tif (var)\n\t\t{\n\t\t\tif ((var->restriction?var->restriction:rcon_level.ival) > Cmd_ExecLevel)\n\t\t\t{\n\t\t\t\tCon_Printf(\"Console script attempted to read restricted cvar %s\\n\", var->name);\n\t\t\t\ts2 = \"RESTRICTED\";\n\t\t\t}\n\t\t\telse\n\t\t\t\ts2 = var->string;\n\t\t}\n\t\telse\n\t\t\ts2 = retstring(com_token);\n\t}\n\n\t*end = s;\n\n\treturn s2;\n}\nenum\n{\n\tIFOP_CAT,\n\tIFOP_MUL,\n\tIFOP_DIV,\n\tIFOP_MOD,\n\tIFOP_ADD,\n\tIFOP_SUB,\n\tIFOP_SHL,\n\tIFOP_SHR,\n\tIFOP_ISIN,\n\tIFOP_ISNOTIN,\n\tIFOP_LT,\n\tIFOP_LE,\n\tIFOP_GT,\n\tIFOP_GE,\n\tIFOP_EQ,\n\tIFOP_NE,\n\tIFOP_BA,\n\tIFOP_XOR,\n\tIFOP_BO,\n\tIFOP_LA,\n\tIFOP_LO\n};\nstatic const struct\n{\n\tint opnamelen;\n\tconst char *opname;\n\tint pri;\n\tint op;\n} ifops[] =\n{\n\t{3,\t\"cat\",\t3,\tIFOP_CAT},\n\t{1, \"*\",\t3,\tIFOP_MUL},\n\t{3, \"mul\", 3,\tIFOP_MUL},\n\t{1, \"/\",\t3,\tIFOP_DIV},\n\t{3,\t\"div\",\t3,\tIFOP_DIV},\n\t{1,\t\"%\",\t3,\tIFOP_MOD},\n\t{3,\t\"mod\",\t3,\tIFOP_MOD},\n\t{1,\t\"+\",\t4,\tIFOP_ADD},\n\t{3,\t\"add\",\t4,\tIFOP_ADD},\n\t{1,\t\"-\",\t4,\tIFOP_SUB},\n\t{3,\t\"sub\",\t4,\tIFOP_SUB},\n\t{2,\t\"<<\",\t5,\tIFOP_SHL},\n\t{2,\t\">>\",\t5,\tIFOP_SHR},\n\t{4,\t\"isin\",5,\tIFOP_ISIN},\t\t//fuhquake\n\t{5,\t\"!isin\",5,\tIFOP_ISNOTIN},\t//fuhquake\n\t{2,\t\"<=\",\t6,\tIFOP_LE},\n\t{1,\t\"<\",\t6,\tIFOP_LT},\n\t{2,\t\">=\",\t6,\tIFOP_GE},\n\t{1,\t\">\",\t6,\tIFOP_GT},\n\t{2,\t\"==\",\t7,\tIFOP_EQ},\n\t{1,\t\"=\",\t7,\tIFOP_EQ},\n\t{5,\t\"equal\",7,\tIFOP_EQ},\t//qw262\n\t{2,\t\"!=\",\t7,\tIFOP_NE},\n\t{2,\t\"&&\",\t11,\tIFOP_LA},\n\t{1,\t\"&\",\t8,\tIFOP_BA},\n\t{3,\t\"and\",\t8,\tIFOP_BA},\t//qw262\n\t{1,\t\"^\",\t9,\tIFOP_XOR},\n\t{3,\t\"xor\",\t8,\tIFOP_XOR},\t//qw262\n\t{2,\t\"||\",\t12,\tIFOP_LO},\n\t{1,\t\"|\",\t10,\tIFOP_BO},\n\t{2,\t\"or\",\t10,\tIFOP_BO}\t//qw262\n};\nstatic const char *If_Operator(int op, const char *left, const char *right)\n{\n\tint r;\n\tswitch(op)\n\t{\n\tcase IFOP_CAT:\n\t\treturn retstring(va(\"%s%s\", left, right));\n\tcase IFOP_MUL:\n\t\treturn retfloat(atof(left)*atof(right));\n\tcase IFOP_DIV:\n\t\treturn retfloat(atof(left)/atof(right));\n\tcase IFOP_MOD:\n\t\tr = atoi(right);\n\t\tif (r)\n\t\t\treturn retfloat(atoi(left)%r);\n\t\telse\n\t\t\treturn retfloat(0);\n\tcase IFOP_ADD:\n\t\treturn retfloat(atof(left)+atof(right));\n\tcase IFOP_SUB:\n\t\treturn retfloat(atof(left)-atof(right));\n\tcase IFOP_SHL:\n\t\treturn retfloat(atoi(left)<<atoi(right));\n\tcase IFOP_SHR:\n\t\treturn retfloat(atoi(left)>>atoi(right));\n\tcase IFOP_ISIN:\n\t\treturn retfloat(!!strstr(right, left));\n\tcase IFOP_ISNOTIN:\n\t\treturn retfloat(!strstr(right, left));\n\tcase IFOP_LT:\n\t\treturn retfloat(atof(left)<atof(right));\n\tcase IFOP_LE:\n\t\treturn retfloat(atof(left)<=atof(right));\n\tcase IFOP_GT:\n\t\treturn retfloat(atof(left)>atof(right));\n\tcase IFOP_GE:\n\t\treturn retfloat(atof(left)>=atof(right));\n\tcase IFOP_EQ:\n\t\tif (is_numeric(left) && is_numeric(right))\n\t\t\treturn retfloat(atof(left) == atof(right));\n\t\telse\n\t\t\treturn retfloat(!strcmp(left, right));\n\tcase IFOP_NE:\n\t\tif (is_numeric(left) && is_numeric(right))\n\t\t\treturn retfloat(atof(left) != atof(right));\n\t\telse\n\t\t\treturn retfloat(!!strcmp(left, right));\n\tcase IFOP_BA:\n\t\treturn retfloat(atoi(left)&atoi(right));\n\tcase IFOP_XOR:\n\t\treturn retfloat(atoi(left)^atoi(right));\n\tcase IFOP_BO:\n\t\treturn retfloat(atoi(left)|atoi(right));\n\tcase IFOP_LA:\n\t\treturn retfloat(is_true(left)&&is_true(right));\n\tcase IFOP_LO:\n\t\treturn retfloat(is_true(left)||is_true(right));\n\tdefault:\n\t\treturn retfloat(0);\n\t}\n}\nstatic const char *If_Token(const char *func, const char **end, int pri)\n{\n\tconst char *s, *s2;\n\tint i;\n\n\tif (pri > 0)\n\t\ts2 = If_Token(func, &s, pri-1);\n\telse\n\t\ts2 = If_Token_Term(func, &s);\n\t*end = s;\n\n\tif (s)\n\t{\n\t\twhile (*s == ' ' || *s == '\\t')\n\t\t\ts++;\n\n\t\tfor (i = 0; i < countof(ifops); i++)\n\t\t{\n\t\t\tif (!strncmp(s, ifops[i].opname, ifops[i].opnamelen))\n\t\t\t{\n\t\t\t\tif (pri == ifops[i].pri)\n\t\t\t\t{\n\t\t\t\t\ts = If_Token(s + ifops[i].opnamelen, end, pri);\n\t\t\t\t\ts2 = If_Operator(ifops[i].op, s2, s);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\treturn s2;\n}\n\nqboolean If_EvaluateBoolean(const char *text, int restriction)\n{\n\tqboolean ret;\n\tconst char *end;\n\ttempstack_t *ts = If_Token_GetMark();\n\tint restore = Cmd_ExecLevel;\n\tCmd_ExecLevel = restriction;\n\ttext = If_Token(text, &end, IF_PRI_MAX);\n\tret = is_true(text);\n\tIf_Token_Clear(ts);\n\tCmd_ExecLevel = restore;\n\treturn ret;\n}\n\nstatic void Cbuf_ExecBlock(int level)\n{\n\tchar *remainingcbuf;\n\tchar *exectext = NULL;\n\tchar *line, *end;\n\tline = Cbuf_GetNext(level, false);\n\n\twhile(*line <= ' ' && *line)\t//skip leading whitespace.\n\t\tline++;\n\n\tfor (end = line + strlen(line)-1; end >= line && *end <= ' '; end--)\t//skip trailing\n\t\t*end = '\\0';\n\n\tif (!strcmp(line, \"{\"))\t//multiline block\n\t{\n\t\tint indent = 1;\n\n\t\tfor(;;)\n\t\t{\n\t\t\tline = Cbuf_GetNext(level, false);\n\n\t\t\twhile(*line <= ' ' && *line)\t//skip leading whitespace.\n\t\t\t\tline++;\n\n\t\t\tfor (end = line + strlen(line)-1; end >= line && *end <= ' '; end--)\t//skip trailing\n\t\t\t\t*end = '\\0';\n\n\t\t\tif (!strcmp(line, \"{\"))\n\t\t\t\tindent++;\n\t\t\telse if (!strcmp(line, \"}\"))\n\t\t\t{\n\t\t\t\tindent--;\n\t\t\t\tif (!indent)\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\telse if (!*line)\n\t\t\t{\n\t\t\t\tCon_Printf(\"Unterminated block\\n\");\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tif (exectext)\n\t\t\t{\n\t\t\t\tchar *newv;\n\t\t\t\tnewv = (char*)Z_Malloc(strlen(exectext) + strlen(line) + 2);\n\t\t\t\tsprintf(newv, \"%s;%s\", exectext, line);\n\t\t\t\tZ_Free(exectext);\n\t\t\t\texectext = newv;\n\t\t\t}\n\t\t\telse\n\t\t\t\texectext = Z_StrDup(line);\n//\t\t\tCon_Printf(\"Exec \\\"%s\\\"\\n\", line);\n\t\t}\n\t}\n\telse\n\t{\n\t\texectext = Z_StrDup(line);\n//\t\tCon_Printf(\"Exec \\\"%s\\\"\\n\", line);\n\t}\n\tremainingcbuf = Cbuf_StripText(level);\t//this craziness is to prevent an if } from breaking the entire con text\n\tCbuf_AddText(exectext, level);\n\tZ_Free(exectext);\n\tCbuf_ExecuteLevel(level);\n\tCbuf_AddText(remainingcbuf, level);\n\tZ_Free(remainingcbuf);\n}\n\nstatic void Cbuf_SkipBlock(int level)\n{\n\tchar *line, *end;\n\tline = Cbuf_GetNext(level, false);\n\n\twhile(*line <= ' ' && *line)\t//skip leading whitespace.\n\t\tline++;\n\n\tfor (end = line + strlen(line)-1; end >= line && *end <= ' '; end--)\t//skip trailing\n\t\t*end = '\\0';\n\n\tif (!strcmp(line, \"{\"))\t//multiline block\n\t{\n\t\tint indent = 1;\n\n\t\tfor(;;)\n\t\t{\n\t\t\tline = Cbuf_GetNext(level, false);\n\n\t\t\twhile(*line <= ' ' && *line)\t//skip leading whitespace.\n\t\t\t\tline++;\n\n\t\t\tfor (end = line + strlen(line)-1; end >= line && *end <= ' '; end--)\t//skip trailing\n\t\t\t\t*end = '\\0';\n\n\t\t\tif (!strcmp(line, \"{\"))\n\t\t\t\tindent++;\n\t\t\telse if (!strcmp(line, \"}\"))\n\t\t\t{\n\t\t\t\tindent--;\n\t\t\t\tif (!indent)\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\telse if (!*line)\n\t\t\t{\n\t\t\t\tCon_Printf(\"Unterminated block\\n\");\n\t\t\t\tbreak;\n\t\t\t}\n//\t\t\tCon_Printf(\"Skip \\\"%s\\\"\\n\", line);\n\t\t}\n\t}\n//\telse\n//\t\tCon_Printf(\"Skip \\\"%s\\\"\\n\", line);\n}\n\nvoid Cmd_if_f(void)\n{\n\tchar *text = Cmd_Args();\n\tconst char *ret;\n\tchar *end;\n\tchar *ws;\n\tint level;\n\tqboolean trueblock=false;\n\ttempstack_t *ts;\n\n\tif (Cmd_Argc()==1)\n\t{\n\t\tCon_TPrintf(\"if <condition> <statement> [elseif <condition> <statement>] [...] [else <statement>]\\n\");\n\t\treturn;\n\t}\n\n\tts = If_Token_GetMark();\n\tlevel = Cmd_ExecLevel;\n\nelseif:\n//\tCon_Printf(\"if %s\\n\", text);\n\tret = If_Token(text, (const char **)&end, IF_PRI_MAX);\n\tif (!end)\n\t{\n\t\tCon_TPrintf(\"Not terminated\\n\");\n\t\tIf_Token_Clear(ts);\n\t\treturn;\n\t}\n\nskipws:\n\twhile(*end == ' ' || *end == '\\t')\t//skip leading whitespace.\n\t\tend++;\n\n\tfor (ws = end + strlen(end)-1; ws >= end && *ws <= ' '; ws--)\t//skip trailing\n\t\t*ws = '\\0';\n\n\tif (!strncmp(end, \"then\", 4))\t//sigh... trying to make fuhquake's ifs work.\n\t{\n\t\tend+=4;\n\t\tgoto skipws;\n\t}\n\n\twhile (*end == ' ' || *end == '\\t')\n\t\tend++;\n\n\tif (!*end)\n\t{\n\t\tif (is_true(ret))\t//equation was true.\n\t\t{\n\t\t\ttrueblock = true;\n\t\t\tCbuf_ExecBlock(level);\n\t\t}\n\t\telse\t//equation was false.\n\t\t{\nskipblock:\n\t\t\tCbuf_SkipBlock(level);\n\t\t}\n\t\tend = Cbuf_GetNext(level, false);\n\t\twhile(*end <= ' ' && *end)\n\t\t\tend++;\n\t\tif (!strncmp(end, \"else\", 4))\n\t\t{\n\t\t\tend+=4;\n\t\t\twhile(*end <= ' ' && *end)\t//skip leading whitespace.\n\t\t\t\tend++;\n\n\t\t\tif (!strncmp(end, \"if\", 2))\n\t\t\t{\n\t\t\t\ttext = end + 2;\n\t\t\t\tif (trueblock)\n\t\t\t\t\tgoto skipblock;\t//we've had our true, all others are assumed to be false.\n\t\t\t\telse\n\t\t\t\t\tgoto elseif;\t//and have another go.\n\t\t\t}\n\t\t\telse\n\t\t\t{\t//we got an else. This is the last block. Don't go through the normal way, cos that would let us follow up with a second else.\n\t\t\t\tif (trueblock)\n\t\t\t\t\tCbuf_SkipBlock(level);\n\t\t\t\telse\n\t\t\t\t\tCbuf_ExecBlock(level);\n\n\t\t\t\tIf_Token_Clear(ts);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\t//whoops. Too far.\n\t\tCbuf_InsertText(end, level, true);\n\t\tIf_Token_Clear(ts);\n\t\treturn;\n\t}\n\n\ttext = strstr(end, \"else\");\n\tif (ret && *ret)\n\t{\n\t\tif (text)\t//don't bother execing the else bit...\n\t\t\t*text = '\\0';\n\t\tCbuf_InsertText(end, level, true);\n\t}\n\telse\n\t{\n\t\tif (text)\n\t\t\tCbuf_InsertText(text+4, level, true);\t//ironically, this will do elseif...\n\t}\n\n\tIf_Token_Clear(ts);\n}\n\nstatic void Cmd_Vstr_f( void )\n{\n\tchar\t*v;\n\n\tif (Cmd_Argc () != 2)\n\t{\n\t\tCon_Printf (\"vstr <variablename> : execute a variable command\\n\");\n\t\treturn;\n\t}\n\n\tv = Cvar_VariableString(Cmd_Argv(1));\n\tCbuf_InsertText(v, Cmd_ExecLevel, true);\n}\n\nstatic void Cmd_toggle_f(void)\n{\n\tcvar_t *v;\n\tif (Cmd_Argc()<2)\n\t{\n\t\tCon_Printf(\"missing cvar name\\n\");\n\t\treturn;\n\t}\n\tv = Cvar_Get(Cmd_Argv(1), \"0\", 0, \"Custom variables\");\n\tif (!v)\n\t\treturn;\n\n\tif (Cmd_Argc() >= 3)\n\t{\n\t\tconst char *newval = Cmd_Argv(2);\n\t\tconst char *defval = (Cmd_Argc()>3)?Cmd_Argv(3):v->defaultstr;\n\t\tif (!strcmp(newval, v->string))\n\t\t\tCvar_Set(v, defval);\n\t\telse\n\t\t\tCvar_Set(v, newval);\n\t}\n\telse\n\t{\n\t\tif (v->value)\n\t\t\tCvar_Set(v, \"0\");\n\t\telse\n\t\t\tCvar_Set(v, \"1\");\n\t}\n}\n\nstatic void Cmd_Set_c(int argn, const char *partial, struct xcommandargcompletioncb_s *ctx)\n{\n\textern cvar_group_t *cvar_groups;\n\tsize_t len = strlen(partial);\n\tcvar_group_t\t*grp;\n\tcvar_t\t*var;\n\tif (argn != 1)\n\t\treturn;\n\tfor (grp=cvar_groups ; grp ; grp=grp->next)\n\t\tfor (var=grp->cvars ; var ; var=var->next)\n\t\t{\n\t\t\tif (!Q_strncasecmp (partial, var->name, len))\n\t\t\t\tctx->cb(var->name, var->description, NULL, ctx);\n\t\t\telse if (var->name2 && !Q_strncasecmp (partial, var->name2, len))\n\t\t\t\tctx->cb(var->name2, var->description, NULL, ctx);\n\t\t}\n}\n\nstatic void Cmd_set_f(void)\n{\n\tvoid *mark;\n\tcvar_t *var;\n\tconst char *end;\n\tconst char *text;\n\tint forceflags = 0;\n\tqboolean docalc;\n\tchar name[256];\n\tconst char *desc = NULL;\n\n\tif (!strcmp(Cmd_Argv(0), \"set_calc\") || !strcmp(Cmd_Argv(0), \"seta_calc\"))\n\t\tdocalc = true;\n\telse\n\t\tdocalc = false;\n\n\tif (Cmd_Argc()<3)\n\t{\n\t\tif (docalc)\n\t\t\tCon_TPrintf(\"%s %s <equation>\\n\", Cmd_Argv(0), *Cmd_Argv(1)?Cmd_Argv(1):\"<var>\");\n\t\telse if (!strcmp(Cmd_Argv(0), \"setfl\"))\n\t\t\tCon_TPrintf(\"%s %s <value> <a|u|s>\\n\", Cmd_Argv(0), *Cmd_Argv(1)?Cmd_Argv(1):\"<var>\");\n\t\telse\n\t\t\tCon_TPrintf(\"%s %s <value>\\n\", Cmd_Argv(0), *Cmd_Argv(1)?Cmd_Argv(1):\"<var>\");\n\t\treturn;\n\t}\n\n\tif (!strncmp(Cmd_Argv(0), \"seta\", 4) && !Cmd_FromGamecode())\n\t\tforceflags |= CVAR_ARCHIVE;\n\n\tQ_strncpyz(name, Cmd_Argv(1), sizeof(name));\n\n\tif (!strcmp(Cmd_Argv(0), \"setfl\") || Cmd_FromGamecode())\t//AARGHHHH!!! Q2 set command is different\n\t{\n\t\ttext = Cmd_Argv(3);\n\t\twhile(*text)\n\t\t{\n\t\t\tswitch(*text++)\n\t\t\t{\n\t\t\tcase 'u':\n\t\t\t\tforceflags |= CVAR_USERINFO;\n\t\t\t\tbreak;\n\t\t\tcase 's':\n\t\t\t\tforceflags |= CVAR_SERVERINFO;\n\t\t\t\tbreak;\n\t\t\tcase 'a':\n\t\t\t\tforceflags |= CVAR_ARCHIVE;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\ttext = Cmd_Argv(2);\n\n\t\tif (Cmd_Argc()>=5)\n\t\t\tdesc = Cmd_Argv(4);\n\t}\n\telse if (dpcompat_set.ival && !docalc)\n\t{\n\t\ttext = Cmd_Argv(2);\n\t\tif (Cmd_Argc()>=4)\n\t\t\tdesc = Cmd_Argv(3);\n\t}\n\telse\n\t{\n\t\tCmd_ShiftArgs(1, false);\n\t\ttext = Cmd_Args();\n\t\tif (!docalc && Cmd_Argc()==2 && (*text == '\\\"' || (*text == '\\\\' && text[1] == '\\\"')))\t//if it's already quoted, dequote it, and ignore trailing stuff, for q2/q3 compatability\n\t\t{\n\t\t\tdesc = COM_StringParse (text, com_token, sizeof(com_token), false, false);\n\t\t\twhile (*desc == ' ' || *desc == '\\t')\n\t\t\t\tdesc++;\n\t\t\tif (desc[0] == '/' && desc[1] == '/')\n\t\t\t{\n\t\t\t\tdesc+=2;\n\t\t\t\twhile (*desc == ' ' || *desc == '\\t')\n\t\t\t\t\tdesc++;\n\t\t\t\tend = desc + strlen(desc);\n\t\t\t\twhile (end > desc)\n\t\t\t\t{\n\t\t\t\t\tend--;\n\t\t\t\t\tif (*end == ' ' || *end == '\\t' || *end == '\\r')\n\t\t\t\t\t\t*(char*)end = 0;\n\t\t\t\t\telse\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t\tdesc = NULL;\n\t\t\ttext = Cmd_Argv(1);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tdesc = strstr(text, \"//\");\n\t\t\tif (desc)\n\t\t\t\tend = desc;\n\t\t\telse\n\t\t\t\tend = text+strlen(text);\n\t\t\tend--;\n\t\t\twhile (end >= text)\n\t\t\t{\n\t\t\t\tif (*end == ' ' || *end == '\\t' || *end == '\\r')\n\t\t\t\t\tend--;\n\t\t\t\telse\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tend++;\n\t\t\t*(char*)end = 0;\n\t\t\tif (desc)\n\t\t\t{\n\t\t\t\tdesc+=2;\n\t\t\t\twhile(*desc == ' ' || *desc == '\\t')\n\t\t\t\t\tdesc++;\n\t\t\t\tend = desc + strlen(desc);\n\t\t\t\twhile (end > desc)\n\t\t\t\t{\n\t\t\t\t\tend--;\n\t\t\t\t\tif (*end == ' ' || *end == '\\t' || *end == '\\r')\n\t\t\t\t\t\t*(char*)end = 0;\n\t\t\t\t\telse\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t//fixme: should peek onto the next line to see if that's an indented // too, or something.\n\t\tforceflags |= 0;\n\t}\n\n\tvar = Cvar_Get2 (name, text, CVAR_TEAMPLAYTAINT|forceflags, desc, \"Custom variables\");\n\n\tmark = If_Token_GetMark();\n\n\tif (var)\n\t{\n\t\tif (var->flags & CVAR_NOTFROMSERVER && Cmd_IsInsecure())\n\t\t{\n\t\t\tCon_Printf (\"Server tried setting %s cvar\\n\", var->name);\n\t\t\treturn;\n\t\t}\n\t\tif (var->flags & CVAR_NOSET)\n\t\t{\n\t\t\tCon_Printf (\"variable %s is readonly\\n\", var->name);\n\t\t\treturn;\n\t\t}\n\t\tif (var->flags & CVAR_NOUNSAFEEXPAND)\n\t\t\tforceflags &= ~(CVAR_USERINFO|CVAR_SERVERINFO);\n\n\t\tif (Cmd_FromGamecode())\n\t\t{\n\t\t\tif (forceflags)\n\t\t\t{\n\t\t\t\tvar->flags &=~(CVAR_USERINFO|CVAR_SERVERINFO);\n\t\t\t\tvar->flags |= forceflags;\n\t\t\t}\n\t\t\tCvar_LockFromServer(var, text);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (docalc)\n\t\t\t\ttext = If_Token(text, &end, IF_PRI_MAX);\n\t\t\tvar->flags |= CVAR_USERCREATED | forceflags;\n\t\t\tCvar_Set(var, text);\n\n\t\t\tif (Cmd_ExecLevel == RESTRICT_TEAMPLAY)\n\t\t\t\tvar->flags |= CVAR_TEAMPLAYTAINT;\n\t\t}\n\t}\n\telse\n\t{\n\t\tif (docalc)\n\t\t\ttext = If_Token(text, &end, IF_PRI_MAX);\n\t\tif (Cmd_FromGamecode())\n\t\t{\n\t\t\tvar = Cvar_Get(Cmd_Argv(1), \"\", 0, \"Game variables\");\n\t\t\tif (var)\n\t\t\t\tCvar_LockFromServer(var, text);\n\t\t}\n\t\telse\n\t\t\tvar = Cvar_Get(Cmd_Argv(1), text, CVAR_USERCREATED, \"User variables\");\n\n\t\tif (var)\n\t\t\tvar->flags |= forceflags;\n\t}\n\n\tIf_Token_Clear(mark);\n}\n\nstatic void Cvar_Inc_f (void)\n{\n\tint c;\n\tcvar_t *var;\n\tfloat delta;\n\n\tc = Cmd_Argc();\n\tif (c != 2 && c != 3)\n\t{\n\t\tCon_Printf (\"inc <cvar> [value]\\n\");\n\t\treturn;\n\t}\n\n\tvar = Cvar_FindVar (Cmd_Argv(1));\n\tif (!var)\n\t{\n\t\tCon_Printf (\"Unknown variable \\\"%s\\\"\\n\", Cmd_Argv(1));\n\t\treturn;\n\t}\n\tif (var->flags & CVAR_NOTFROMSERVER && Cmd_IsInsecure())\n\t{\n\t\tCon_Printf (\"Server tried setting %s cvar\\n\", var->name);\n\t\treturn;\n\t}\n\n\n\tdelta = (c == 3) ? atof (Cmd_Argv(2)) : 1;\n\n\tif (Cmd_ExecLevel == RESTRICT_TEAMPLAY || (var->flags & CVAR_TEAMPLAYTAINT))\n\t{\n\t\tCvar_SetValue (var, var->value + delta);\n\t\tvar->flags |= CVAR_TEAMPLAYTAINT;\n\t}\n\telse\n\t\tCvar_SetValue (var, var->value + delta);\n}\n\nvoid Cvar_ParseWatches(void)\n{\n\tconst char *cvarname;\n\tint i;\n\tcvar_t *var;\n\tfor (i=1 ; i<com_argc-1 ; i++)\n\t{\n\t\tif (!com_argv[i] || strcmp(com_argv[i], \"-watch\"))\n\t\t\tcontinue;\t\t// NEXTSTEP sometimes clears appkit vars.\n\t\tcvarname = com_argv[i+1];\n\t\tif (!cvarname)\n\t\t\tcontinue;\n\n\t\tvar = Cvar_FindVar (cvarname);\n\t\tif (!var)\n\t\t{\n\t\t\tCon_Printf (\"cvar \\\"%s\\\" is not defined yet\\n\", cvarname);\n\t\t\tcontinue;\n\t\t}\n\t\tvar->flags |= CVAR_WATCHED;\n\t\tcvar_watched = true;\n\n\t\ti++;\n\t}\n}\n\nstatic void Cvar_Watch_f(void)\n{\n\tchar *cvarname = Cmd_Argv(1);\n\tcvar_t *var;\n\tcvar_group_t *grp;\n\textern cvar_group_t *cvar_groups;\n\n\tif (!strcmp(cvarname, \"\"))\n\t{\n\t\tfor (grp=cvar_groups ; grp ; grp=grp->next)\n\t\tfor (var=grp->cvars ; var ; var=var->next)\n\t\t{\n\t\t\tif (var->flags & CVAR_WATCHED)\n\t\t\t\tCon_Printf(\"Watching %s\\n\", var->name);\n\t\t}\n\t\treturn;\n\t}\n\telse if (!strcmp(cvarname, \"off\"))\n\t{\n\t\tcvar_watched = false;\n\t\tCon_Printf(\"Disabling all cvar watches\\n\");\n\t\tfor (grp=cvar_groups ; grp ; grp=grp->next)\n\t\tfor (var=grp->cvars ; var ; var=var->next)\n\t\t\tvar->flags &= ~CVAR_WATCHED;\n\t\treturn;\n\t}\n\telse if (!strcmp(cvarname, \"all\"))\n\t{\n\t\tcvar_watched = 2;\n\t\tCon_Printf(\"Notifying for ALL cvar changes\\n\");\n\t\treturn;\n\t}\n\telse\n\t{\n\t\tvar = Cvar_FindVar (cvarname);\n\t\tif (!var)\n\t\t{\n\t\t\tCon_Printf (\"cvar \\\"%s\\\" is not defined yet\\n\", cvarname);\n\t\t\treturn;\n\t\t}\n\t\tvar->flags |= CVAR_WATCHED;\n\t\tcvar_watched |= true;\n\t}\n}\n\nstatic void Cmd_WriteConfig_f(void)\n{\n\tchar curtime[256];\n\tvfsfile_t *f;\n\tchar *filename;\n\tchar fname[MAX_QPATH];\n\tchar displayname[MAX_OSPATH];\n\tqboolean all = true;\n\tqboolean nohidden = false;\n\n\t//special variation that only saves if an archived cvar was actually modified.\n\tif (!Q_strcasecmp(Cmd_Argv(0), \"cfg_save_ifmodified\"))\n\t\tif (!Cvar_UnsavedArchive())\n\t\t\treturn;\n\n\tfilename = Cmd_Argv(1);\n\tif (!*filename)\n\t{\n\t\tif (*fs_manifest->mainconfig)\n\t\t\tQ_strncpyz(fname, fs_manifest->mainconfig, sizeof(fname));\n\t\telse\n\t\t\tQ_strncpyz(fname, \"config.cfg\", sizeof(fname));\t//write SOMETHING.\n\n#if defined(CL_MASTER) && defined(HAVE_CLIENT)\n\t\tMasterInfo_WriteServers();\n#endif\n\n\t\tf = FS_OpenWithFriends(fname, displayname, sizeof(displayname), 4, \"quake.rc\", \"hexen.rc\", \"*.cfg\", \"configs/*.cfg\", \"dlcache/*.pk3*\");\n\n\t\tall = cfg_save_all.ival;\n\t}\n\telse if (!Q_strcasecmp(Cmd_Argv(0), \"saveconfig\"))\n\t{\n\t\t//dpcompat: this variation allows writing to any path. at least force the extension.\n\t\tQ_snprintfz(fname, sizeof(fname), \"%s\", filename);\n\t\tCOM_RequireExtension(fname, \".cfg\", sizeof(fname));\n\n\t\tif (!strncmp(fname, \"data/\", 5))\n\t\t\tnohidden = true;\t//we're writing to the data/ dir, which mods may potentially read. don't write any settings they're not allowed to see.\n\t\telse if (Cmd_IsInsecure())\n\t\t{\n\t\t\tCon_Printf (\"%s %s: not allowed\\n\", Cmd_Argv(0), Cmd_Args());\n\t\t\treturn;\n\t\t}\n\n\t\tFS_DisplayPath(fname, FS_BASEGAMEONLY, displayname, sizeof(displayname));\n\t\tFS_CreatePath(fname, FS_BASEGAMEONLY);\n\t\tf = FS_OpenVFS(fname, \"wbp\", FS_BASEGAMEONLY);\n\n\t\tall = cfg_save_all.ival;// || !*cfg_save_all.string;\n\t}\n\telse\n\t{\n\t\tif (Cmd_IsInsecure())\n\t\t{\n\t\t\tCon_Printf (\"%s %s: not allowed\\n\", Cmd_Argv(0), Cmd_Args());\n\t\t\treturn;\n\t\t}\n\n\t\tif (strstr(filename, \"..\"))\n\t\t{\n\t\t\tCon_Printf (CON_ERROR \"Couldn't write config %s\\n\",filename);\n\t\t\treturn;\n\t\t}\n\t\tQ_snprintfz(fname, sizeof(fname), \"configs/%s\", filename);\n\t\tCOM_DefaultExtension(fname, \".cfg\", sizeof(fname));\n\n\t\tFS_DisplayPath(fname, FS_BASEGAMEONLY, displayname, sizeof(displayname));\n\t\tFS_CreatePath(fname, FS_BASEGAMEONLY);\n\t\tf = FS_OpenVFS(fname, \"wbp\", FS_BASEGAMEONLY);\n\n\t\tall = cfg_save_all.ival || !*cfg_save_all.string;\n\t}\n\tif (!f)\n\t{\n\t\tCon_Printf (CON_ERROR \"Couldn't write config %s\\n\", displayname);\n\t\treturn;\n\t}\n\n\t{\n\t\ttime_t t = time(NULL);\n\t\tstrftime(curtime, sizeof(curtime), \"%Y-%m-%d %H:%M\", localtime(&t));\n\t}\n\tVFS_PRINTF(f, \"// %s config file (%s)\\n\\n\", *fs_gamename.string?fs_gamename.string:FULLENGINENAME, curtime);\n#ifdef HAVE_CLIENT\n\tif (cfg_save_binds.ival)\n\t\tKey_WriteBindings (f);\n\tif (cfg_save_buttons.ival)\n\t\tIN_WriteButtons(f, all);\n\t\n\t#ifdef HAVE_CDPLAYER\n//\t\tif (cfg_save_cdtracks.ival)\n\t\t\tMedia_SaveTracks(f);\n\t#endif\n\tif (cfg_save_infos.ival)\n\t\tCL_SaveInfo(f);\n#else\n\tVFS_WRITE(f, \"// Dedicated Server config\\n\\n\", 28);\n#endif\n#ifdef HAVE_SERVER\n\tif (cfg_save_infos.ival)\n\t\tSV_SaveInfos(f);\n#else\n\tVFS_WRITE(f, \"// no local/server infos\\n\\n\", 26);\n#endif\n\tif (cfg_save_aliases.ival)\n\t\tAlias_WriteAliases (f);\n\tCvar_WriteVariables (f, all, nohidden);\n\tVFS_CLOSE(f);\n\n\tCvar_Saved();\n\n\tCon_Printf (\"Wrote %s\\n\",displayname);\n}\n\nstatic void Cmd_Reset_f(void)\n{\n\t//FIXME: this should reset to _engine_ defaults (with fmf overrides)\n\tCbuf_InsertText(\"cvarreset *\\n\", RESTRICT_LOCAL, false);\n\t//FIXME: wipe aliases\n\t//FIXME: reset + commands\n\t//FIXME: destroy custom cvars (unless there's a live pointer still... which is not tracked properly, bah)\n//\tCbuf_InsertText(\"exec default.cfg\\n\", RESTRICT_LOCAL, false);\n}\n\n#ifdef HAVE_CLIENT\n// dumps current console contents to a text file\nstatic void Cmd_Condump_f(void)\n{\n\tconsole_t *c = Con_GetMain();\n\tvfsfile_t *f;\n\tchar *filename;\n\tchar line[8192];\n\n\tif (!c)\n\t{\n\t\tCon_Printf (\"No console to dump.\\n\");\n\t\treturn;\n\t}\n\n\tif (Cmd_IsInsecure()) // don't allow insecure level execute this\n\t\treturn;\n\n\tfilename = Cmd_Argv(1);\n\tif (!*filename)\n\t\tfilename = \"condump\";\n\n\tfilename = va(\"%s\", filename);\n\tCOM_DefaultExtension(filename, \".txt\", MAX_QPATH);\n\n\tf = FS_OpenVFS (filename, \"wb\", FS_GAME);\n\tif (!f)\n\t{\n\t\tCon_Printf (CON_ERROR \"Couldn't write console dump %s\\n\",filename);\n\t\treturn;\n\t}\n\n\t// print out current contents of console\n\t// stripping out starting blank lines and blank spaces\n\t{\n\t\tconline_t *l;\n\t\tconchar_t *t;\n\t\tfor (l = c->oldest; l; l = l->newer)\n\t\t{\n\t\t\tt = (conchar_t*)(l+1);\n\t\t\tCOM_DeFunString(t, t + l->length, line, sizeof(line), true, !!(c->parseflags & PFS_FORCEUTF8));\n\t\t\tVFS_WRITE(f, line, strlen(line));\n\t\t\tVFS_WRITE(f, \"\\n\", 1);\n\t\t}\n\t}\n\n\tVFS_CLOSE(f);\n\n\tCon_Printf (\"Dumped console to %s\\n\",filename);\n}\n#endif\n\nvoid Cmd_Shutdown(void)\n{\n\tcmd_function_t *c;\n\tcmdalias_t *a;\n\tint i;\n\n\t//make sure we get no other execution\n\tint level;\n\tfor (level = 0; level < sizeof(cmd_text)/sizeof(cmd_text[0]); level++)\n\t{\n\t\tSZ_Clear (&cmd_text[level].buf);\n\n\t\tif (cmd_text[level].buf.data)\n\t\t{\n\t\t\tBZ_Free(cmd_text[level].buf.data);\n\t\t\tcmd_text[level].buf.data = NULL;\n\t\t\tcmd_text[level].buf.maxsize = 0;\n\t\t}\n\t}\n\n\tCmd_Complete(NULL, false);\t//NULL frees any cached results without generating new ones.\n\n\twhile(cmd_functions)\n\t{\n\t\tc = cmd_functions;\n\t\tcmd_functions = c->next;\n\t\tZ_Free(c);\n\t}\n\twhile(cmd_alias)\n\t{\n\t\ta = cmd_alias;\n\t\tcmd_alias = a->next;\n\t\tZ_Free(a->value);\n\t\tZ_Free(a);\n\t}\n\n\tfor (i=0 ; i<cmd_argc ; i++)\n\t\tZ_Free (cmd_argv[i]);\n\tZ_Free(cmd_args_buf);\n\tcmd_argc = 0;\n\tcmd_args_buf = NULL;\n}\n\n\nstatic char\tmacro_buf[256] = \"\";\nstatic char *Macro_Time (void)\n{\n\ttime_t\t\tt;\n\tstruct tm\t*ptm;\n\n\ttime (&t);\n\tptm = localtime (&t);\n\tif (!ptm)\n\t\treturn \"#bad date#\";\n\tstrftime (macro_buf, sizeof(macro_buf)-1, \"%H:%M\", ptm);\n\treturn macro_buf;\n}\nstatic char *Macro_UKDate (void)\t//and much but not all of EU\n{\n\ttime_t\t\tt;\n\tstruct tm\t*ptm;\n\n\ttime (&t);\n\tptm = localtime (&t);\n\tif (!ptm)\n\t\treturn \"#bad date#\";\n\tstrftime (macro_buf, sizeof(macro_buf)-1, \"%d.%m.%Y\", ptm);\n\treturn macro_buf;\n}\nstatic char *Macro_USDate (void)\t//and much but not all of EU\n{\n\ttime_t\t\tt;\n\tstruct tm\t*ptm;\n\n\ttime (&t);\n\tptm = localtime (&t);\n\tif (!ptm)\n\t\treturn \"#bad date#\";\n\tstrftime (macro_buf, sizeof(macro_buf)-1, \"%m.%d.%Y\", ptm);\n\treturn macro_buf;\n}\nstatic char *Macro_ProperDate (void)\t//americans get it wrong. besides, this is more easily sortable for filenames etc\n{\n\ttime_t\t\tt;\n\tstruct tm\t*ptm;\n\n\ttime (&t);\n\tptm = localtime (&t);\n\tif (!ptm)\n\t\treturn \"#bad date#\";\n\tstrftime (macro_buf, sizeof(macro_buf)-1, \"%Y-%m-%d\", ptm);\n\treturn macro_buf;\n}\nstatic char *Macro_Version (void)\n{\n\t/*\tyou probably don't need date, but it's included as this is likly to be used by\n\t\tq2 servers checking for cheats. */\n\treturn va(\"%.2f %s\", 2.57, version_string());\n}\nstatic char *Macro_Dedicated (void)\n{\n\tif (isDedicated)\n\t\treturn \"1\";\n\telse\n\t\treturn \"0\";\n}\nstatic char *Macro_Quote (void)\n{\n\treturn \"\\\"\";\n}\n\nstatic char *Macro_Random(void)\n{\n\tQ_snprintfz(macro_buf, sizeof(macro_buf), \"%u\", rand());\n\treturn macro_buf;\n}\n\n/*\n============\nCmd_Init\n============\n*/\nvoid Cmd_Init (void)\n{\n\tcmd_stuffedcmdline = false;\n\tmacro_count = 0;\t\n//\n// register our commands\n//\n\tCmd_AddCommandAD (\"cfg_save\",Cmd_WriteConfig_f, Cmd_Exec_c, NULL);\n\tCmd_AddCommandAD (\"cfg_save_ifmodified\",Cmd_WriteConfig_f, Cmd_Exec_c, NULL);\n\tCmd_AddCommandAD (\"saveconfig\",Cmd_WriteConfig_f, Cmd_Exec_c, NULL);\t//for dpcompat\n\n\tCmd_AddCommandAD (\"cfg_load\",Cmd_Exec_f, Cmd_Exec_c, NULL);\n\tCmd_AddCommand (\"cfg_reset\",Cmd_Reset_f);\t//ezquake\n\tCmd_AddCommand (\"resetcfg\",Cmd_Reset_f);\t//QS\n\n\tCmd_AddCommandAD (\"exec\",Cmd_Exec_f, Cmd_Exec_c, NULL);\n\tCmd_AddCommand (\"echo\",Cmd_Echo_f);\n\tCmd_AddCommand (\"alias\",Cmd_Alias_f);\n\tCmd_AddCommand (\"newalias\",Cmd_Alias_f);\n\tCmd_AddCommand (\"wait\", Cmd_Wait_f);\n#ifdef HAVE_CLIENT\n\tCmd_AddCommand (\"cmd\", Cmd_ForwardToServer_f);\n\tCmd_AddCommand (\"condump\", Cmd_Condump_f);\n\tCmd_AddCommandAD (\"aliasedit\", Cmd_AliasEdit_f, Key_Alias_c, NULL);\n#endif\n\tCmd_AddCommand (\"restrict\", Cmd_RestrictCommand_f);\n\tCmd_AddCommandAD (\"aliaslevel\", Cmd_AliasLevel_f, Key_Alias_c, NULL);\n\n\tCmd_AddCommandAD (\"showalias\", Cmd_ShowAlias_f, Key_Alias_c, NULL);\n\n\tCmd_AddCommandAD (\"toggle\", Cmd_toggle_f, Cmd_Set_c, \"Toggles a cvar between two values\\ntoggle CVARNAME [newval [altval]]\");\n\tCmd_AddCommandAD (\"set\", Cmd_set_f, Cmd_Set_c, \"Changes the current value of the named cvar, creating it if it doesn't yet exist.\");\n\tCmd_AddCommandAD (\"setfl\", Cmd_set_f, Cmd_Set_c, \"Changes the current value of the named cvar, creating it if it doesn't yet exist. The third arg allows setting cvar flags and should be u, s, or a. This command should normally be used only inside default.cfg.\");\n\tCmd_AddCommandAD (\"set_calc\", Cmd_set_f, Cmd_Set_c, \"Sets the named cvar to the result of a (complex) expression.\");\n#ifndef QUAKETC\n\tCmd_AddCommandAD (\"set_tp\", Cmd_set_f, Cmd_Set_c, \"Identical to set. Included for compatibility with ezquake where ruleset restrictions on macros are on commands rather than cvars.\");\n#endif\n\tCmd_AddCommandAD (\"seta\", Cmd_set_f, Cmd_Set_c, \"Changes the current value of the named cvar, creating it if it doesn't yet exist. Also forces the archive flag so that the cvar will always be written into any saved configs.\");\n\tCmd_AddCommandAD (\"seta_calc\", Cmd_set_f, Cmd_Set_c, \"Sets the named cvar to the result of a (complex) expression. Also forces the archive flag so that the cvar will always be written into any saved configs.\");\n\tCmd_AddCommandD (\"vstr\", Cmd_Vstr_f, \"Executes the string value of the cvar, much like if it were an alias. For compatibility with q3.\");\n\tCmd_AddCommandAD (\"inc\", Cvar_Inc_f, Cmd_Set_c, \"Adds a value to the named cvar. Use a negative value if you wish to decrease the cvar's value.\");\n\tCmd_AddCommandD (\"if\", Cmd_if_f, \"For conditionally executing console commands.\");\n\n\tCmd_AddCommand (\"cmdlist\", Cmd_List_f);\n\tCmd_AddCommand (\"aliaslist\", Cmd_AliasList_f);\n\tCmd_AddCommandD (\"macrolist\", Cmd_MacroList_f, \"Lists all available $macro expansions.\");\n\tCmd_AddCommandD (\"cvarlist\", Cvar_List_f, \"Lists all cvars. eg, 'cvarlist -cvd *' can be used to list all cvars with a value other than the mod's default.\");\n\tCmd_AddCommandD (\"cvarreset\", Cvar_Reset_f, \"Resets the named cvar to its default value.\");\n\tCmd_AddCommandD (\"cvarwatch\", Cvar_Watch_f, \"Prints a notification when the named cvar is changed. Also displays the start/end of configs. Alternatively, use '-watch foo' on the commandline.\");\n\tCmd_AddCommand (\"cvar_lockdefaults\", Cvar_LockDefaults_f);\n\tCmd_AddCommandD (\"cvar_purgedefaults\", Cvar_PurgeDefaults_f, \"Resets all cvar defaults to back to the engine's default. Does not change their active value.\");\n\tCmd_AddCommandD (\"cvar_resettodefaults_nosaveonly\", Cvar_ResetAll_f, \"Resets all unsaved cvars to the mod's default value.\");\n\tCmd_AddCommandD (\"cvar_resettodefaults_saveonly\", Cvar_ResetAll_f, \"Resets all saved cvars to the mod's default value (without changing unsaved cvars).\");\n\tCmd_AddCommandD (\"cvar_resettodefaults_all\", Cvar_ResetAll_f, \"Resets all cvars to the mod's default value.\");\n\n\tCmd_AddCommandD (\"apropos\", Cmd_Apropos_f, \"Lists all cvars or commands with the specified substring somewhere in their name or descrition.\");\n\tCmd_AddCommandD (\"find\", Cmd_Apropos_f, \"Lists all cvars or commands with the specified substring somewhere in their name or descrition.\");\n\n\tCmd_AddMacro(\"random\", Macro_Random, true);\n\tCmd_AddMacro(\"time\", Macro_Time, true);\n\tCmd_AddMacro(\"ukdate\", Macro_UKDate, false);\n\tCmd_AddMacro(\"usdate\", Macro_USDate, false);\n\tCmd_AddMacro(\"date\", Macro_ProperDate, false);\n\tCmd_AddMacro(\"version\", Macro_Version, false);\n\tCmd_AddMacro(\"qt\", Macro_Quote, false);\n\tCmd_AddMacro(\"dedicated\", Macro_Dedicated, false);\n\n#ifdef HAVE_CLIENT\n\tCvar_Register(&tp_disputablemacros, \"Teamplay\");\n#endif\n\n\tCvar_Register(&ruleset_allow_in, \"Console\");\n\tCmd_AddCommandD (\"in\", Cmd_In_f, \"Issues the given command after a time delay. Disabled if ruleset_allow_in is 0.\");\n\n#ifdef HAVE_LEGACY\n\tCmd_AddCommandD (\"defer\", Cmd_In_f, \"Issues the given command after a time delay. Disabled if ruleset_allow_in is 0.\");\n\tCvar_Register(&dpcompat_set, \"Darkplaces compatibility\");\n\tCvar_Register(&dpcompat_console, \"Darkplaces compatibility\");\n#endif\n\tCvar_Register (&cl_warncmd, \"Warnings\");\n\tCvar_Register (&cfg_save_all, \"client operation options\");\n\tCvar_Register (&cfg_save_auto, \"client operation options\");\n\tCvar_Register (&cfg_save_infos, \"client operation options\");\n\tCvar_Register (&cfg_save_aliases, \"client operation options\");\n\tCvar_Register (&cfg_save_binds, \"client operation options\");\n\tCvar_Register (&cfg_save_buttons, \"client operation options\");\n\n#ifdef HAVE_CLIENT\n\trcon_level.ival = atof(rcon_level.enginevalue);\t//client is restricted to not be allowed to change restrictions.\n#else\n\tCvar_Register(&rcon_level, \"Access controls\");\t\t//server gains versatility.\n#endif\n\trcon_level.restriction = RESTRICT_MAX;\t//default. Don't let anyone change this too easily.\n\tcmd_maxbuffersize.restriction = RESTRICT_MAX;\t//filling this causes a loop for quite some time.\n\n\tCvar_Register(&cl_aliasoverlap, \"Console\");\n\t//FIXME: go through quake.rc and parameters looking for sets and setas and setting them now.\n}\n\n"
  },
  {
    "path": "engine/common/cmd.h",
    "content": "/*\nCopyright (C) 1996-1997 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  \n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n*/\n\n// cmd.h -- Command buffer and command execution\n\n//===========================================================================\n\n/*FIXME: rewrite this to use something like the following\ntypedef struct\n{\n   \tqbyte groups:27;\n   \tqbyte cvarlatch:1;\t//latches cvars so the user cannot change them till next map. should be RESTRICT_LOCAL to avoid users cheating by restrict-to-block server commands.\n\tqbyte seat:4;\t\t//for splitscreen binds etc. 1 based! 0 uses in_forceseat/first.\n} cmdaccess_t;\nenum {\n//core groups\n\tCAG_OWNER\t\t= 1<<0,\t//commands that came from the keyboard (or stdin).\n\tCAG_MENUQC\t\t= 1<<1,\t//menuqc (mostly allowed to do stuff - but often also has CAG_DOWNLOADED)\n\tCAG_CSQC\t\t= 1<<2,\t//csqc localcmds, nearly always has CAG_DOWNLOADED, so a load of denies\n\tCAG_SSQC\t\t= 1<<3,\t//ssqc localcmds, able to poke quite a lot of things, generally unrestricted.\n\tCAG_SERVER\t\t= 1<<4,\t//ssqc stuffcmds, typically same access as csqc\n//special groups:\n\tCAG_DOWNLOADED\t= 1<<5,\t//for execs that read from a downloaded(untrusted) package. also flagged by qc modules if they were from such packages (so usually included from csqc).\n\tCAG_SCRIPT\t\t= 1<<6,\t//added for binds (with more than one command), or for aliases/configs. exists for +lookdown's denies (using explicit checks, to avoid cheat bypasses).\n//custom groups:\n\tCAG_RCON\t\t= 1<<7,\t//commands that came from rcon.\n\n//groups of groups, for default masks or special denies\n\tCAG_DEFAULTALLOW = CAG_OWNER|CAG_MENUQC|CAG_CSQC|CAG_SSQC|CAG_SERVER|CAG_RCON,\n\tCAG_INSECURE\t= CAG_DOWNLOADED|CAG_SERVER|CAG_CSQC,\t//csqc included mostly for consistency.\n\n\t//\tuserN: defaults to no commands\n\t//\tvip: BAN_VIP users\n\t//\tmapper: BAN_MAPPER users\n};\n\n\t//stuff can be accessed when:\n\t//\tif ((command.allows&cbuf.groups) && !(command.denies&cbuf.groups)) accessisallowed;\n\t//cvars:\n\t//\tseparate allow-read, deny-read, allow-write, deny-write masks (set according to the older flags, to keep things simple)\n\t//aliases:\n\t//\talias execution ors in the group(s) that created it (and 'script'). some execution chains could end up with a LOT of groups... FIXME: is that a problem? just don't put potentially restricted things in aliases?\n\t//binds:\n\t//\talso ors the creator's group - no `bind w doevil` and waiting.\n\t//multiple cbufs:\n\t//\tssqc still has a dedicated cbuf, so that wait commands cause THAT cbuf to wait, not others.\n\t//\trcon+readcmd have a separate cbuf, to try to isolate prints\n\t//seats:\n\t//\tthere's no security needed between seats, but server should maybe be blocked from seat switching commands (acting only as asserts), to catch bugs.\n\t//\n\t//'p2 nameofalias' sets seat to 2, overriding in_forceseat.\n\t//+lookup etc is blocked when allow_scripts==0 and indirect is set.\ntypedef struct\n{\n\tcmdpermissions_t p; //access rights\n} cmdstate_t;\nvoid Cbuf_AddText(const char *text, qboolean addnl, qboolean insert); //null for local? all commands must be \\n terminated.\nchar *Cbuf_GetNext(cmdpermissions_t permissions, qboolean ignoresemicolon);\nvoid Cbuf_Execute(cbuf_t *cbuf);\nvoid Cmd_ExecuteString (const char *text, const cmdstate_t *cstate);\n\n//rcon can use a private cbuf, allowing it to parse properly despite level changes\n//cbuf must internally track cmdpermissions_t of different blocks. inserstions/additions may merge, others force \\n termination (network can do its own buffering).\n\ntypedef void (*xcommand_t) (const cmdstate_t *cstate);\n*/\n\n/*\n\nAny number of commands can be added in a frame, from several different sources.\nMost commands come from either keybindings or console line input, but remote\nservers can also send across commands and entire text files can be execed.\n\nThe + command line options are also added to the command buffer.\n\nThe game starts with a Cbuf_AddText (\"exec quake.rc\\n\"); Cbuf_Execute ();\n\n*/\n\nvoid Cbuf_Waited(void);\n\nvoid Cbuf_Init (void);\n// allocates an initial text buffer that will grow as needed\n\nvoid Cbuf_AddText (const char *text, int level);\n// as new commands are generated from the console or keybindings,\n// the text is added to the end of the command buffer.\n\nvoid Cbuf_InsertText (const char *text, int level, qboolean addnl);\n// when a command wants to issue other commands immediately, the text is\n// inserted at the beginning of the buffer, before any remaining unexecuted\n// commands.\n\nchar *Cbuf_GetNext(int level, qboolean ignoresemicolon);\n\nvoid Cbuf_Execute (void);\n// Pulls off \\n terminated lines of text from the command buffer and sends\n// them through Cmd_ExecuteString.  Stops when the buffer is empty.\n// Normally called once per frame, but may be explicitly invoked.\n// Do not call inside a command function!\n\nextern qboolean cmd_blockwait;\nvoid Cbuf_ExecuteLevel(int level);\n//executes only a single cbuf level. can be used to restrict cbuf execution to some 'safe' set of commands, so there are no surprise 'map' commands.\n//will not magically make all commands safe to exec, but will prevent user commands slipping in too.\n\n//===========================================================================\n\n/*\n\nCommand execution takes a null terminated string, breaks it into tokens,\nthen searches for a command or variable that matches the first token.\n\n*/\n\ntypedef void (*xcommand_t) (void);\nstruct xcommandargcompletioncb_s\n{\n\t//if repl is specified, then that is the text that will be used if this is the sole autocomplete, to complete using strings that are not actually valid.\n\tvoid(*cb)(const char *arg, const char *desc, const char *repl, struct xcommandargcompletioncb_s *ctx);\n\t//private stuff follows.\n};\ntypedef void (*xcommandargcompletion_t)(int argn, const char *partial, struct xcommandargcompletioncb_s *ctx);\n\nint Cmd_Level(const char *name);\nvoid Cmd_EnumerateLevel(int level, char *buf, size_t bufsize);\n\nvoid\tCmd_Init (void);\nvoid\tCmd_Shutdown(void);\nvoid\tCmd_StuffCmds (void);\n\nvoid\tCmd_RemoveCommands (xcommand_t function);\t//unregister all commands that use the same function. for wrappers and stuff.\nvoid\tCmd_RemoveCommand (const char *cmd_name);\nqboolean\tCmd_AddCommand (const char *cmd_name, xcommand_t function);\nqboolean\tCmd_AddCommandD (const char *cmd_name, xcommand_t function, const char *description);\nqboolean\tCmd_AddCommandAD (const char *cmd_name, xcommand_t function, xcommandargcompletion_t argcomplete, const char *description);\n// called by the init functions of other parts of the program to\n// register commands and functions to call for them.\n// The cmd_name is referenced later, so it should not be in temp memory\n// if function is NULL, the command will be forwarded to the server\n// as a clc_stringcmd instead of executed locally\n\nqboolean Cmd_Exists (const char *cmd_name);\nchar *Cmd_AliasExist(const char *name, int restrictionlevel);\n// used by the cvar code to check for cvar / command name overlap\n\nconst char *Cmd_Describe (const char *cmd_name);\n\ntypedef struct\n{\n\tchar *guessed;\t//this is the COMPLETED partial.\n\tchar *partial;\t//the requested string that we completed\n\tqboolean caseinsens;\n\tsize_t num, extra;\t//valid count, and ommitted count (if we were too lazy to find more)\n\tstruct cmd_completion_opt_s {\n\t\tqboolean text_alloced:1;\n\t\tqboolean desc_alloced:1;\n\t\tconst char *text;\n\t\tconst char *repl;\t//used for sole matches\n\t\tconst char *desc;\n\t} completions[50];\n} cmd_completion_t;\ncmd_completion_t *Cmd_Complete(const char *partial, qboolean caseinsens);\t//calculates and caches info.\n\n//these should probably be removed some time\nchar *Cmd_CompleteCommand (const char *partial, qboolean fullonly, qboolean caseinsens, int matchnum, const char **descptr);\nqboolean Cmd_IsCommand (const char *line);\n// attempts to match a partial command for automatic command line completion\n// returns NULL if nothing fits\n\nint\t\tVARGS Cmd_Argc (void);\nchar\t*VARGS Cmd_Argv (int arg);\nchar\t*VARGS Cmd_Args (void);\nextern int\tCmd_ExecLevel;\n\n//if checkheader is false, an opening { is expected to already have been parsed.\n//otherwise returns the contents of the block much like c.\n//returns a zoned string.\nchar *Cmd_ParseMultiline(qboolean checkheader);\n\nextern cvar_t cmd_gamecodelevel, cmd_allowaccess;\n// The functions that execute commands get their parameters with these\n// functions. Cmd_Argv () will return an empty string, not a NULL\n// if arg > argc, so string operations are always safe.\n\nint Cmd_CheckParm (const char *parm);\n// Returns the position (1 to argc-1) in the command's argument list\n// where the given parameter apears, or 0 if not present\n\nchar *Cmd_AliasExist(const char *name, int restrictionlevel);\nvoid Alias_WipeStuffedAliases(void);\n\nvoid Cmd_AddMacro(char *s, char *(*f)(void), int disputableintentions);\n#define Cmd_AddMacroD(s,f,unsafe,desc) Cmd_AddMacro(s,f,unsafe)\n\nvoid Cmd_TokenizePunctation (char *text, char *punctuation);\nconst char *Cmd_TokenizeString (const char *text, qboolean expandmacros, qboolean qctokenize);\n// Takes a null terminated string.  Does not need to be /n terminated.\n// breaks the string up into arg tokens.\n\nvoid\tCmd_ExecuteString (const char *text, int restrictionlevel);\n\nvoid Cmd_Args_Set(const char *newargs, size_t len);\n\n//higher levels have greater access, BUT BE SURE TO CHECK Cmd_IsInsecure()\n#define RESTRICT_MAX_TOTAL  31\n#define RESTRICT_MAX_USER\t29\n#define RESTRICT_DEFAULT\t20\n#define RESTRICT_MIN\t\t1\n#define RESTRICT_TEAMPLAY\t0\t//this is blocked from everything but aliases.\n\n#define RESTRICT_MAX RESTRICT_MAX_USER\n\n#define RESTRICT_LOCAL\t\t(RESTRICT_MAX)\t\t//commands typed at the console\n#define RESTRICT_INSECURE\t(RESTRICT_MAX+1)\t//commands from csqc or untrusted sources (really should be a separate flag, requires cbuf rewrite)\n#define RESTRICT_SERVER\t\t(RESTRICT_MAX+2)\t\t//commands from ssqc (untrusted, but allowed to lock cvars)\n#define RESTRICT_SERVERSEAT(x) (RESTRICT_SERVER+x)\n#define RESTRICT_RCON\trcon_level.ival\n//#define RESTRICT_SSQC\tRESTRICT_MAX-2\n\n#define Cmd_FromGamecode() (Cmd_ExecLevel>=RESTRICT_SERVER)\t//cheat provention. block cheats if its not fromgamecode\n#define Cmd_IsInsecure() (Cmd_ExecLevel>=RESTRICT_INSECURE)\t//prevention from the server from breaking/crashing/wiping us. if this returns true, block file access etc.\n\n// Parses a single line of text into arguments and tries to execute it\n// as if it was typed at the console\n\nvoid\tCmd_ForwardToServer (void);\n// adds the current command line as a clc_stringcmd to the client message.\n// things like godmode, noclip, etc, are commands directed to the server,\n// so when they are typed in at the console, they will need to be forwarded.\n\nqboolean Cmd_FilterMessage (char *message, qboolean sameteam);\nvoid Cmd_MessageTrigger (char *message, int type);\n\nvoid Cmd_ShiftArgs (int ammount, qboolean expandstring);\n\nconst char *TP_ParseFunChars (const char *s);\nchar *Cmd_ExpandString (const char *data, char *dest, int destlen, int *accesslevel, qboolean expandargs, qboolean expandcvars, qboolean expandmacros);\nqboolean If_EvaluateBoolean(const char *text, int restriction);\n\nextern cvar_t rcon_level;\n\nvoid Cmd_AddTimer(float delay, void(*callback)(int iarg, void *data), int iarg, void *data, size_t datasize); //wrong place, gah\n\n"
  },
  {
    "path": "engine/common/com_bih.c",
    "content": "#include \"quakedef.h\"\n#ifndef SERVERONLY\n#include \"glquake.h\"\n#endif\n#include \"com_mesh.h\"\n#include \"com_bih.h\"\n\n//BIH traces are capable of checking each object only once, thus our collision structures can be fully const.\n//this also allows traces to be threaded, if we can avoid the temptation to (ab)use globals.\n//so don't use any globals!\n\nstruct bihnode_s\n{\n\t//in a bih tree there are two values per node instead of a kd-tree's single midpoint\n\t//this allows the two sides to overlap, which prevents the need to chop large objects into multiple leafs\n\t//(it also allows gaps in the middle, which can further skip recursion)\n\tenum bihtype_e type;\n\tunion\n\t{\n\t\tstruct{\n\t\t\tint firstchild;\n\t\t\tint numchildren;\n\t\t} group;\n#ifdef BIH_USEBVH\n\t\tstruct{\n\t\t\tint firstchild;\n\t\t\tvec3_t min, max;\n\t\t\tfloat cmin;\n\t\t\tfloat cmax;\n\t\t} bvhnode;\n#endif\n#ifdef BIH_USEBIH\n\t\tstruct{\n\t\t\tint firstchild;\n\t\t\tfloat cmin[2];\n\t\t\tfloat cmax[2];\n\t\t} bihnode;\n#endif\n\t\tstruct bihdata_s data;\n\t};\n};\nstruct bihbox_s {\n\tvec3_t min;\n\tvec3_t max;\n};\nstruct bihtrace_s\n{\n\tstruct bihbox_s bounds;\n\tstruct bihbox_s size;\n\tvec3_t expand;\n\tvec3_t up;\t//capsule's upwards direction\n\tvec3_t capsulesize;\t//radius, up, down\n\tqboolean negativedir[3];\n\n\tenum {\n\t\tshape_ispoint,\n\t\tshape_isbox,\n\t\tshape_iscapsule,\n\t} shape;\n\tunsigned int hitcontents;\n\tvec3_t startpos;\t//bounds.[min|max]\n\tvec3_t totalmove;\n\tvec3_t endpos;\t//bounds.[min|max]\n\ttrace_t trace;\n};\n\nstatic const q2mapsurface_t\tnullsurface;\n\nstatic qboolean BIH_BoundsIntersect (const vec3_t mins1, const vec3_t maxs1, const vec3_t mins2, const vec3_t maxs2)\n{\n\treturn (mins1[0] <= maxs2[0] && mins1[1] <= maxs2[1] && mins1[2] <= maxs2[2] &&\n\t\t maxs1[0] >= mins2[0] && maxs1[1] >= mins2[1] && maxs1[2] >= mins2[2]);\n}\n\n#define PlaneDiff(point,plane) (((plane)->type < 3 ? (point)[(plane)->type] : DotProduct((point), (plane)->normal)) - (plane)->dist)\n\n#define boxdist(dist,plane)\t\\\n\t\tdefault:\t\t\t\\\n\t\tcase shape_isbox:\t\\\n\t\t\t/* FIXME: needs special case for axial */\t\\\n\t\t\tfor (j=0 ; j<3 ; j++)\t\\\n\t\t\t{\t\\\n\t\t\t\tif (plane->normal[j] < 0)\t\\\n\t\t\t\t\tofs[j] = tr->size.max[j];\t\\\n\t\t\t\telse\t\\\n\t\t\t\t\tofs[j] = tr->size.min[j];\t\\\n\t\t\t}\t\\\n\t\t\tdist = DotProduct (ofs, plane->normal);\t\\\n\t\t\tdist = plane->dist - dist;\t\\\n\t\t\tbreak;\n#define capsuledist(dist,plane)\t\t\t\t\t\\\n\t\tcase shape_iscapsule:\t\t\t\t\t\t\t\t\\\n\t\t\tdist = DotProduct(tr->up, plane->normal);\t\t\\\n\t\t\tdist = dist*(tr->capsulesize[(dist<0)?1:2]) - tr->capsulesize[0];\t\\\n\t\t\tdist = plane->dist - dist;\t\t\t\t\t\t\\\n\t\t\tbreak;\n#define pointdist(dist,plane)\t\\\n\t\tcase shape_ispoint:\t\t\\\n\t\t\tdist = plane->dist;\t\\\n\t\t\tbreak;\n#define calcdist(dist,plane) switch(tr->shape)\t{\t\\\n\t\tboxdist(dist,plane)\t\\\n\t\tcapsuledist(dist,plane)\t\\\n\t\tpointdist(dist,plane)\t\\\n\t\t}\n\n#define\tDIST_EPSILON\t(0.03125)\n/*static void BIH_ClipBoxToPlanes (struct bihtrace_s *fte_restrict tr, vec3_t plmins, vec3_t plmaxs, const mplane_t *plane, int numplanes, const q2csurface_t *surf)\n{\n\tint\t\t\ti, j;\n\tconst mplane_t\t*clipplane;\n\tfloat\t\tdist;\n\tfloat\t\tenterfrac, leavefrac;\n\tvec3_t\t\tofs;\n\tfloat\t\td1, d2;\n\tqboolean\tgetout, startout;\n\tfloat\t\tf;\n\tstatic const mplane_t\tbboxplanes[6] = //we change the dist, but nothing else\n\t{\n\t\t{{1, 0, 0}},\n\t\t{{0, 1, 0}},\n\t\t{{0, 0, 1}},\n\t\t{{-1, 0, 0}},\n\t\t{{0, -1, 0}},\n\t\t{{0, 0, -1}},\n\t};\n\tsize_t u;\n\n\tfloat nearfrac=0;\n\tenterfrac = -1;\n\tleavefrac = 2;\n\tclipplane = NULL;\n\n\tgetout = false;\n\tstartout = false;\n\n\tfor (i=0 ; i<numplanes ; i++, plane++)\n\t{\n\t\tcalcdist(dist, plane)\n\n\t\td1 = DotProduct (tr->startpos, plane->normal) - dist;\n\t\td2 = DotProduct (tr->endpos, plane->normal) - dist;\n\n\t\tif (d2 > 0)\n\t\t\tgetout = true;\t// endpoint is not in solid\n\t\tif (d1 > 0)\n\t\t\tstartout = true;\n\n\t\t// if completely in front of face, no intersection\n\t\tif (d1 > 0 && d2 >= d1)\n\t\t\treturn;\n\n\t\tif (d1 <= 0 && d2 <= 0)\n\t\t\tcontinue;\n\n\t\t// crosses face\n\t\tif (d1 > d2)\n\t\t{\t// enter\n\t\t\tf = (d1) / (d1-d2);\n\t\t\tif (f > enterfrac)\n\t\t\t{\n\t\t\t\tenterfrac = f;\n\t\t\t\tnearfrac = (d1-DIST_EPSILON) / (d1-d2);\n\t\t\t\tclipplane = plane;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\t// leave\n\t\t\tf = (d1) / (d1-d2);\n\t\t\tif (f < leavefrac)\n\t\t\t\tleavefrac = f;\n\t\t}\n\t}\n\n\tif (tr->shape)\t//bevel the brush axially (to match the player's bbox), in case that wasn't already done\n\tfor (i=0, plane = bboxplanes; i<countof(bboxplanes) ; i++, plane++)\n\t{\n\t\tif (i < 3)\n\t\t{\t//positive normal\n\t\t\tdist = tr->size.min[i];\n\t\t\tdist = plmaxs[i] - dist;\n\t\t\td1 = tr->startpos[i] - dist;\n\t\t\td2 = tr->endpos[i] - dist;\n\t\t}\n\t\telse\n\t\t{\t//negative normal\n\t\t\tj = i-3;\n\t\t\tdist = -tr->size.max[j];\n\t\t\tdist = -plmins[j] - dist;\n\t\t\td1 = -tr->startpos[j] - dist;\n\t\t\td2 = -tr->endpos[j] - dist;\n\t\t}\n\n\t\tif (d2 > 0)\n\t\t\tgetout = true;\t// endpoint is not in solid\n\t\tif (d1 > 0)\n\t\t\tstartout = true;\n\n\t\t// if completely in front of face, no intersection\n\t\tif (d1 > 0 && d2 >= d1)\n\t\t\treturn;\n\n\t\tif (d1 <= 0 && d2 <= 0)\n\t\t\tcontinue;\n\n\t\t// crosses face\n\t\tif (d1 > d2)\n\t\t{\t// enter\n\t\t\tf = (d1) / (d1-d2);\n\t\t\tif (f > enterfrac)\n\t\t\t{\n\t\t\t\tenterfrac = f;\n\t\t\t\tnearfrac = (d1-DIST_EPSILON) / (d1-d2);\n\t\t\t\tclipplane = plane;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\t// leave\n\t\t\tf = (d1) / (d1-d2);\n\t\t\tif (f < leavefrac)\n\t\t\t\tleavefrac = f;\n\t\t}\n\t}\n\n\tif (!startout)\n\t{\t// original point was inside brush\n\t\ttr->trace.startsolid = true;\n\t\tif (!getout)\n\t\t\ttr->trace.allsolid = true;\n\t\treturn;\n\t}\n\tif (enterfrac <= leavefrac)\n\t{\n\t\tif (enterfrac > -1 && enterfrac <= tr->trace.truefraction)\n\t\t{\n\t\t\tif (enterfrac < 0)\n\t\t\t\tenterfrac = 0;\n\n\t\t\ttr->trace.fraction = nearfrac;\n\t\t\ttr->trace.truefraction = enterfrac;\n\n\t\t\tif ((u=clipplane-bboxplanes) < countof(bboxplanes))\t//hit one of the bbox planes. get the proper plane dist.\n\t\t\t\ttr->trace.plane.dist = (u < 3)?plmaxs[u]:-plmins[u-3];\n\t\t\telse\n\t\t\t\ttr->trace.plane.dist = clipplane->dist;\n\t\t\tVectorCopy(clipplane->normal, tr->trace.plane.normal);\n\t\t\ttr->trace.surface = surf;\n\t\t\ttr->trace.contents = surf->value;\n\t\t}\n\t}\n}*/\nstatic void BIH_ClipToTriangle(struct bihtrace_s *fte_restrict tr, const struct bihdata_s *info)\n{\n\tint i, j;\n\tfloat *p1, *p2, *p3;\n\tvec3_t edge1, edge2, edge3;\n\tmplane_t planes[5];\n\tconst mplane_t *plane;\n\tvec3_t tmins, tmaxs;\n\n\tconst mplane_t\t*clipplane;\n\tfloat\t\tdist;\n\tfloat\t\tenterfrac, leavefrac, nearfrac;\n\tvec3_t\t\tofs;\n\tfloat\t\td1, d2;\n\tqboolean\tgetout, startout;\n\tfloat\t\tf;\n\tstatic const mplane_t\tbboxplanes[6] =\n\t{\n\t\t{{1, 0, 0}},\n\t\t{{0, 1, 0}},\n\t\t{{0, 0, 1}},\n\t\t{{-1, 0, 0}},\n\t\t{{0, -1, 0}},\n\t\t{{0, 0, -1}},\n\t};\n\tsize_t u;\n\n\tp1 = info->tri.xyz[info->tri.indexes[0]];\n\tp2 = info->tri.xyz[info->tri.indexes[1]];\n\tp3 = info->tri.xyz[info->tri.indexes[2]];\n\n\t//determine the triangle extents, and skip the triangle if we're completely out of bounds\n\tfor (j = 0; j < 3; j++)\n\t{\n\t\ttmins[j] = p1[j];\n\t\tif (tmins[j] > p2[j])\n\t\t\ttmins[j] = p2[j];\n\t\tif (tmins[j] > p3[j])\n\t\t\ttmins[j] = p3[j];\n\t\tif (tr->bounds.max[j]+(1/8.f) < tmins[j])\n\t\t\treturn;\n\t\ttmaxs[j] = p1[j];\n\t\tif (tmaxs[j] < p2[j])\n\t\t\ttmaxs[j] = p2[j];\n\t\tif (tmaxs[j] < p3[j])\n\t\t\ttmaxs[j] = p3[j];\n\t\tif (tr->bounds.min[j]-(1/8.f) > tmaxs[j])\n\t\t\treturn;\n\t}\n\n\tVectorSubtract(p1, p2, edge1);\n\tVectorSubtract(p3, p2, edge2);\n\tVectorSubtract(p1, p3, edge3);\n\tCrossProduct(edge1, edge2, planes[0].normal);\n\tVectorNormalize(planes[0].normal);\n\tplanes[0].dist = DotProduct(p1, planes[0].normal);\n\tVectorNegate(planes[0].normal, planes[1].normal);\n\tplanes[1].dist = -planes[0].dist + 4;\n\n\t//determine edges\n\t//FIXME: use adjacency info\n\tCrossProduct(edge1, planes[0].normal, planes[2].normal);\n\tVectorNormalize(planes[2].normal);\n\tplanes[2].dist = DotProduct(p2, planes[2].normal);\n\n\tCrossProduct(planes[0].normal, edge2, planes[3].normal);\n\tVectorNormalize(planes[3].normal);\n\tplanes[3].dist = DotProduct(p3, planes[3].normal);\n\n\tCrossProduct(planes[0].normal, edge3, planes[4].normal);\n\tVectorNormalize(planes[4].normal);\n\tplanes[4].dist = DotProduct(p1, planes[4].normal);\n\n\tnearfrac=0;\n\tenterfrac = -1;\n\tleavefrac = 2;\n\tclipplane = NULL;\n\n\tgetout = false;\n\tstartout = false;\n\n\tfor (i=0, plane = planes ; i<countof(planes) ; i++, plane++)\n\t{\n\t\tcalcdist(dist, plane)\n\n\t\td1 = DotProduct (tr->startpos, plane->normal) - dist;\n\t\td2 = DotProduct (tr->endpos, plane->normal) - dist;\n\n\t\tif (d2 > 0)\n\t\t\tgetout = true;\t// endpoint is not in solid\n\t\tif (d1 > 0)\n\t\t\tstartout = true;\n\n\t\t// if completely in front of face, no intersection\n\t\tif (d1 > 0 && d2 >= d1)\n\t\t\treturn;\n\n\t\tif (d1 <= 0 && d2 <= 0)\n\t\t\tcontinue;\n\n\t\t// crosses face\n\t\tif (d1 > d2)\n\t\t{\t// enter\n\t\t\tf = (d1) / (d1-d2);\n\t\t\tif (f > enterfrac)\n\t\t\t{\n\t\t\t\tenterfrac = f;\n\t\t\t\tnearfrac = (d1-DIST_EPSILON) / (d1-d2);\n\t\t\t\tclipplane = plane;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\t// leave\n\t\t\tf = (d1) / (d1-d2);\n\t\t\tif (f < leavefrac)\n\t\t\t\tleavefrac = f;\n\t\t}\n\t}\n\n\tif (tr->shape)\t//bevel the brush axially (to match the player's bbox), in case that wasn't already done\n\tfor (i=0, plane = bboxplanes; i<countof(bboxplanes) ; i++, plane++)\n\t{\n\t\tif (i < 3)\n\t\t{\t//positive normal\n\t\t\tdist = tr->size.min[i];\n\t\t\tdist = tmaxs[i] - dist;\n\t\t\td1 = tr->startpos[i] - dist;\n\t\t\td2 = tr->endpos[i] - dist;\n\t\t}\n\t\telse\n\t\t{\t//negative normal\n\t\t\tj = i-3;\n\t\t\tdist = -tr->size.max[j];\n\t\t\tdist = -tmins[j] - dist;\n\t\t\td1 = -tr->startpos[j] - dist;\n\t\t\td2 = -tr->endpos[j] - dist;\n\t\t}\n\n\t\tif (d2 > 0)\n\t\t\tgetout = true;\t// endpoint is not in solid\n\t\tif (d1 > 0)\n\t\t\tstartout = true;\n\n\t\t// if completely in front of face, no intersection\n\t\tif (d1 > 0 && d2 >= d1)\n\t\t\treturn;\n\n\t\tif (d1 <= 0 && d2 <= 0)\n\t\t\tcontinue;\n\n\t\t// crosses face\n\t\tif (d1 > d2)\n\t\t{\t// enter\n\t\t\tf = (d1) / (d1-d2);\n\t\t\tif (f > enterfrac)\n\t\t\t{\n\t\t\t\tenterfrac = f;\n\t\t\t\tnearfrac = (d1-DIST_EPSILON) / (d1-d2);\n\t\t\t\tclipplane = plane;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\t// leave\n\t\t\tf = (d1) / (d1-d2);\n\t\t\tif (f < leavefrac)\n\t\t\t\tleavefrac = f;\n\t\t}\n\t}\n\n\tif (!startout)\n\t{\t// original point was inside brush\n\t\ttr->trace.startsolid = true;\n\t\tif (!getout)\n\t\t\ttr->trace.allsolid = true;\n\t\treturn;\n\t}\n\tif (enterfrac <= leavefrac)\n\t{\n\t\tif (enterfrac > -1 && enterfrac <= tr->trace.truefraction)\n\t\t{\n\t\t\tif (enterfrac < 0)\n\t\t\t\tenterfrac = 0;\n\n\t\t\ttr->trace.fraction = nearfrac;\n\t\t\ttr->trace.truefraction = enterfrac;\n\n\t\t\tif ((u=clipplane-bboxplanes) < countof(bboxplanes))\t//hit one of the bbox planes. get the proper plane dist.\n\t\t\t\ttr->trace.plane.dist = (u < 3)?tmaxs[u]:-tmins[u-3];\n\t\t\telse\n\t\t\t\ttr->trace.plane.dist = clipplane->dist;\n\t\t\tVectorCopy(clipplane->normal, tr->trace.plane.normal);\n\t\t\ttr->trace.surface = &nullsurface.c;\n\t\t\ttr->trace.contents = info->contents;\n\t\t}\n\t}\n}\n\nstatic void BIH_TestToTriangle(struct bihtrace_s *fte_restrict tr, const struct bihdata_s *info)\n{\n\tint j;\n\tfloat *p1, *p2, *p3;\n\tvec3_t edge1, edge2, edge3;\n\tmplane_t planes[5];\n\tconst mplane_t *plane;\n\tvec3_t tmins, tmaxs;\n\n\tint\t\t\ti;\n\tfloat\t\tdist;\n\tvec3_t\t\tofs;\n\tfloat\t\td1;\n\tstatic const mplane_t\tbboxplanes[6] = //we change the dist, but nothing else\n\t{\n\t\t{{1, 0, 0}},\n\t\t{{0, 1, 0}},\n\t\t{{0, 0, 1}},\n\t\t{{-1, 0, 0}},\n\t\t{{0, -1, 0}},\n\t\t{{0, 0, -1}},\n\t};\n\n\tp1 = info->tri.xyz[info->tri.indexes[0]];\n\tp2 = info->tri.xyz[info->tri.indexes[1]];\n\tp3 = info->tri.xyz[info->tri.indexes[2]];\n\n\t//determine the triangle extents, and skip the triangle if we're completely out of bounds\n\tfor (j = 0; j < 3; j++)\n\t{\n\t\ttmins[j] = p1[j];\n\t\tif (tmins[j] > p2[j])\n\t\t\ttmins[j] = p2[j];\n\t\tif (tmins[j] > p3[j])\n\t\t\ttmins[j] = p3[j];\n\t\tif (tr->bounds.max[j]+(1/8.f) < tmins[j])\n\t\t\treturn;\n\t\ttmaxs[j] = p1[j];\n\t\tif (tmaxs[j] < p2[j])\n\t\t\ttmaxs[j] = p2[j];\n\t\tif (tmaxs[j] < p3[j])\n\t\t\ttmaxs[j] = p3[j];\n\t\tif (tr->bounds.min[j]-(1/8.f) > tmaxs[j])\n\t\t\treturn;\n\t}\n\n\tVectorSubtract(p1, p2, edge1);\n\tVectorSubtract(p3, p2, edge2);\n\tVectorSubtract(p1, p3, edge3);\n\tCrossProduct(edge1, edge2, planes[0].normal);\n\tVectorNormalize(planes[0].normal);\n\tplanes[0].dist = DotProduct(p1, planes[0].normal);\n\tVectorNegate(planes[0].normal, planes[1].normal);\n\tplanes[1].dist = -planes[0].dist + 4;\n\n\t//determine edges\n\t//FIXME: use adjacency info\n\tCrossProduct(edge1, planes[0].normal, planes[2].normal);\n\tVectorNormalize(planes[2].normal);\n\tplanes[2].dist = DotProduct(p2, planes[2].normal);\n\n\tCrossProduct(planes[0].normal, edge2, planes[3].normal);\n\tVectorNormalize(planes[3].normal);\n\tplanes[3].dist = DotProduct(p3, planes[3].normal);\n\n\tCrossProduct(planes[0].normal, edge3, planes[4].normal);\n\tVectorNormalize(planes[4].normal);\n\tplanes[4].dist = DotProduct(p1, planes[4].normal);\n\n\tfor (i=0, plane = planes; i<countof(planes) ; i++, plane++)\n\t{\n\t\tcalcdist(dist, plane)\n\t\td1 = DotProduct (tr->startpos, plane->normal) - dist;\n\t\tif (d1 > 0)\n\t\t\treturn;\n\t}\n\n\tif (tr->shape)\t//bevel the brush axially (to match the player's bbox), in case that wasn't already done\n\tfor (i=0, plane = bboxplanes; i<countof(bboxplanes) ; i++, plane++)\n\t{\n\t\tif (i < 3)\n\t\t{\t//positive normal\n\t\t\tdist = tr->size.min[i];\n\t\t\tdist = tmaxs[i] - dist;\n\t\t\td1 = tr->startpos[i] - dist;\n\t\t}\n\t\telse\n\t\t{\t//negative normal\n\t\t\tj = i-3;\n\t\t\tdist = -tr->size.max[j];\n\t\t\tdist = -tmins[j] - dist;\n\t\t\td1 = -tr->startpos[j] - dist;\n\t\t}\n\n\t\t// if completely in front of face, no intersection\n\t\tif (d1 > 0)\n\t\t\treturn;\n\t}\n\n\ttr->trace.startsolid = tr->trace.allsolid = true;\n\ttr->trace.contents |= info->contents;\n}\n\n#if defined(Q2BSPS) || defined(Q3BSPS)\nstatic void BIH_ClipBoxToBrush (struct bihtrace_s *fte_restrict tr, const q2cbrush_t *brush)\n{\n\tint\t\t\ti, j;\n\tmplane_t\t*plane, *clipplane;\n\tfloat\t\tdist;\n\tfloat\t\tenterfrac, leavefrac;\n\tvec3_t\t\tofs;\n\tfloat\t\td1, d2;\n\tqboolean\tgetout, startout;\n\tfloat\t\tf;\n\tq2cbrushside_t\t*side, *leadside;\n\n\tfloat nearfrac=0;\n\tenterfrac = -1;\n\tleavefrac = 2;\n\tclipplane = NULL;\n\n\tif (!brush->numsides)\n\t\treturn;\n\n\tgetout = false;\n\tstartout = false;\n\tleadside = NULL;\n\n\tfor (i=0 ; i<brush->numsides ; i++)\n\t{\n\t\tside = brush->brushside+i;\n\t\tplane = side->plane;\n\n\t\tcalcdist(dist, plane)\n\t\td1 = DotProduct (tr->startpos, plane->normal) - dist;\n\t\td2 = DotProduct (tr->endpos, plane->normal) - dist;\n\n\t\tif (d2 > 0)\n\t\t\tgetout = true;\t// endpoint is not in solid\n\t\tif (d1 > 0)\n\t\t\tstartout = true;\n\n\t\t// if completely in front of face, no intersection\n\t\tif (d1 > 0 && d2 >= d1)\n\t\t\treturn;\n\n\t\tif (d1 <= 0 && d2 <= 0)\n\t\t\tcontinue;\n\n\t\t// crosses face\n\t\tif (d1 > d2)\n\t\t{\t// enter\n\t\t\tf = (d1) / (d1-d2);\n\t\t\tif (f > enterfrac)\n\t\t\t{\n\t\t\t\tenterfrac = f;\n\t\t\t\tnearfrac = (d1-DIST_EPSILON) / (d1-d2);\n\t\t\t\tclipplane = plane;\n\t\t\t\tleadside = side;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\t// leave\n\t\t\tf = (d1) / (d1-d2);\n\t\t\tif (f < leavefrac)\n\t\t\t\tleavefrac = f;\n\t\t}\n\t}\n\n\tif (!startout)\n\t{\t// original point was inside brush\n\t\ttr->trace.startsolid = true;\n\t\tif (!getout)\n\t\t\ttr->trace.allsolid = true;\n\t\treturn;\n\t}\n\tif (enterfrac <= leavefrac)\n\t{\n\t\tif (enterfrac > -1 && enterfrac <= tr->trace.truefraction)\n\t\t{\n\t\t\tif (enterfrac < 0)\n\t\t\t\tenterfrac = 0;\n\n\t\t\ttr->trace.fraction = nearfrac;\n\t\t\ttr->trace.truefraction = enterfrac;\n\n\t\t\ttr->trace.plane.dist = clipplane->dist;\n\t\t\tVectorCopy(clipplane->normal, tr->trace.plane.normal);\n\t\t\ttr->trace.surface = &(leadside->surface->c);\n\t\t\ttr->trace.contents = brush->contents;\n\t\t}\n\t}\n}\nstatic void BIH_TestBoxInBrush (struct bihtrace_s *fte_restrict tr, q2cbrush_t *brush)\n{\n\tint\t\t\ti, j;\n\tmplane_t\t*plane;\n\tfloat\t\tdist;\n\tvec3_t\t\tofs;\n\tfloat\t\td1;\n\tq2cbrushside_t\t*side;\n\n\tif (!brush->numsides)\n\t\treturn;\n\n\tfor (i=0 ; i<brush->numsides ; i++)\n\t{\n\t\tside = brush->brushside+i;\n\t\tplane = side->plane;\n\n\t\tcalcdist(dist, plane)\n\t\td1 = DotProduct (tr->startpos, plane->normal) - dist;\n\n\t\t// if completely in front of face, no intersection\n\t\tif (d1 > 0)\n\t\t\treturn;\n\t}\n\n\t// inside this brush\n\ttr->trace.startsolid = tr->trace.allsolid = true;\n\ttr->trace.contents |= brush->contents;\n}\n#endif\n\n#ifdef Q3BSPS\nstatic void BIH_ClipBoxToPatch (struct bihtrace_s *fte_restrict tr, q2cbrush_t *brush)\n{\n\tint\t\t\ti, j;\n\tmplane_t\t*plane, *clipplane;\n\tfloat\t\tenterfrac, leavefrac, nearfrac = 0;\n\tvec3_t\t\tofs;\n\tfloat\t\td1, d2;\n\tfloat dist;\n\tqboolean\tstartout;\n\tfloat\t\tf;\n\tq2cbrushside_t\t*side, *leadside;\n\n\tif (!brush->numsides)\n\t\treturn;\n\n\tenterfrac = -1;\n\tleavefrac = 2;\n\tclipplane = NULL;\n\tstartout = false;\n\tleadside = NULL;\n\n\tfor (i=0 ; i<brush->numsides ; i++)\n\t{\n\t\tside = brush->brushside+i;\n\t\tplane = side->plane;\n\n\t\tcalcdist(dist, plane)\n\t\td1 = DotProduct (tr->startpos, plane->normal) - dist;\n\t\td2 = DotProduct (tr->endpos, plane->normal) - dist;\n\n\t\t// if completely in front of face, no intersection\n\t\tif (d1 > 0 && d2 >= d1)\n\t\t\treturn;\n\n\t\tif (d1 > 0)\n\t\t\tstartout = true;\n\n\t\tif (d1 <= 0 && d2 <= 0)\n\t\t\tcontinue;\n\n\t\t// crosses face\n\t\tif (d1 > d2)\n\t\t{\t// enter\n\t\t\tf = (d1) / (d1-d2);\n\t\t\tif (f > enterfrac)\n\t\t\t{\n\t\t\t\tenterfrac = f;\n\t\t\t\tnearfrac = (d1-DIST_EPSILON) / (d1-d2);\n\t\t\t\tclipplane = plane;\n\t\t\t\tleadside = side;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\t// leave\n\t\t\tf = (d1) / (d1-d2);\n\t\t\tif (f < leavefrac)\n\t\t\t\tleavefrac = f;\n\t\t}\n\t}\n\n\tif (!startout)\n\t{\n\t\ttr->trace.startsolid = true;\n\t\treturn;\t\t// original point is inside the patch\n\t}\n\n\tif (nearfrac <= leavefrac)\n\t{\n\t\tif (leadside && leadside->surface\n\t\t\t&& enterfrac <= tr->trace.truefraction)\n\t\t{\n\t\t\tif (enterfrac < 0)\n\t\t\t\tenterfrac = 0;\n\t\t\ttr->trace.truefraction = enterfrac;\n\t\t\ttr->trace.fraction = nearfrac;\n\t\t\ttr->trace.plane.dist = clipplane->dist;\n\t\t\tVectorCopy(clipplane->normal, tr->trace.plane.normal);\n\t\t\ttr->trace.surface = &leadside->surface->c;\n\t\t\ttr->trace.contents = brush->contents;\n\t\t}\n\t\telse if (enterfrac < tr->trace.truefraction)\n\t\t\tleavefrac=0;\n\t}\n}\nstatic void BIH_TestBoxInPatch (struct bihtrace_s *fte_restrict tr, q2cbrush_t *brush)\n{\n\tint\t\t\ti, j;\n\tmplane_t\t*plane;\n\tvec3_t\t\tofs, ofs2;\n\tfloat dist, thickness;\n\tfloat\t\td1;\n\tq2cbrushside_t\t*side;\n\n\tif (!brush->numsides)\n\t\treturn;\n\n\ti = 0;\t//front plane\n\t{\n\t\tside = brush->brushside+i;\n\t\tplane = side->plane;\n\n\t\tswitch(tr->shape)\n\t\t{\n\t\tdefault:\n\t\tcase shape_isbox:\n\t\t\tfor (j=0 ; j<3 ; j++)\n\t\t\t{\n\t\t\t\tif (plane->normal[j] < 0)\n\t\t\t\t\tofs[j] = tr->size.max[j], ofs2[j] = tr->size.min[j];\n\t\t\t\telse\n\t\t\t\t\tofs[j] = tr->size.min[j], ofs2[j] = tr->size.max[j];\n\t\t\t}\n\n\t\t\tdist = DotProduct (ofs, plane->normal);\n\t\t\tthickness = DotProduct (ofs2, plane->normal)-dist;\n\t\t\tdist = plane->dist - dist;\n\t\t\tbreak;\n\t\tcase shape_iscapsule:\n\t\t\tdist = DotProduct(tr->up, plane->normal);\n\t\t\tthickness = dist*(tr->capsulesize[(dist<0)?2:1]) + tr->capsulesize[0]*2;\n\t\t\tdist = dist*(tr->capsulesize[(dist<0)?1:2]) - tr->capsulesize[0];\n\t\t\tdist = plane->dist - dist;\n\t\t\tbreak;\n\t\tcase shape_ispoint:\n\t\t\tdist = plane->dist;\n\t\t\tthickness = 0;\n\t\t\tbreak;\n\t\t}\n\n\t\td1 = DotProduct (tr->startpos, plane->normal) - dist;\n\n\t\t// if completely in front of face, no intersection\n\t\tif (d1 > 0)\n\t\t\treturn;\n\n\t\t//point is behind the front plane, so no real intersection.\n\t\tif (thickness < 0.25)\n\t\t\tthickness = 0.25; //FIXME: patches should probably be infinitely thin, but that makes stuff messy.\n\t\tif (d1 < -thickness)\n\t\t\treturn;\n\t}\n\n\tfor (i=1 ; i<brush->numsides ; i++)\n\t{\n\t\tside = brush->brushside+i;\n\t\tplane = side->plane;\n\n\t\tcalcdist(dist, plane)\n\t\td1 = DotProduct (tr->startpos, plane->normal) - dist;\n\n\t\t// if completely in front of face, no intersection\n\t\tif (d1 > 0)\n\t\t\treturn;\n\t}\n\n\t// inside this patch\n\ttr->trace.startsolid = tr->trace.allsolid = true;\n\ttr->trace.contents = brush->contents;\n}\n#endif\n\n\nstatic void BIH_RecursiveTrace (struct bihtrace_s *fte_restrict tr, const struct bihnode_s *fte_restrict node, const struct bihbox_s *fte_restrict movesubbounds, const struct bihbox_s *fte_restrict nodebox)\n{\n\t//if the tree were 1d, we wouldn't need to be so careful with the bounds, but if the trace is long then we want to avoid hitting all surfaces within that entire-map-encompassing move aabb\n\tswitch(node->type)\n\t{\t//leaf\n#if defined(Q2BSPS) || defined(Q3BSPS)\n\tcase BIH_BRUSH:\n\t\tif (node->data.contents & tr->hitcontents)\n\t\t{\n\t\t\tq2cbrush_t *b = node->data.brush;\n\t\t\tif (BIH_BoundsIntersect(b->absmins, b->absmaxs, movesubbounds->min, movesubbounds->max))\n\t\t\t\tBIH_ClipBoxToBrush (tr, b);\n\t\t}\n\t\treturn;\n#endif\n#ifdef Q3BSPS\n\tcase BIH_PATCHBRUSH:\n\t\tif (node->data.contents & tr->hitcontents)\n\t\t{\n\t\t\tq2cbrush_t *b = node->data.patchbrush;\n\t\t\tif (BIH_BoundsIntersect(b->absmins, b->absmaxs, movesubbounds->min, movesubbounds->max))\n\t\t\t\tBIH_ClipBoxToPatch (tr, b);\n\t\t}\n\t\treturn;\n\tcase BIH_TRISOUP:\n\t\t/*if (node->data.contents & tr->hitcontents)\n\t\t{\n\t\t\tq3cmesh_t *cmesh = node->data.cmesh;\n\t\t\tif (BIH_BoundsIntersect(cmesh->absmins, cmesh->absmaxs, movesubbounds->min, movesubbounds->max))\n\t\t\t\tMod_Trace_Trisoup_(cmesh->xyz_array, cmesh->indicies, cmesh->numincidies, trace_start, trace_end, trace_mins, trace_maxs, &trace_trace, &cmesh->surface->c);\n\t\t}*/\n\t\treturn;\n#endif\n\tcase BIH_TRIANGLE:\n\t\tif (node->data.contents & tr->hitcontents)\n\t\t\tBIH_ClipToTriangle(tr, &node->data);\n\t\treturn;\n\tcase BIH_MODEL:\n\t\t{\n\t\t\ttrace_t sub;\n\t\t\tvec3_t start_l;\n\t\t\tvec3_t end_l;\n\n\t\t\tmodel_t *submod = node->data.mesh.model;\n\n\t\t\tif (submod->loadstate != MLS_LOADED)\n\t\t\t{\n\t\t\t\tstatic float throttle;\n\t\t\t\tCOM_AssertMainThread(\"BIH_RecursiveTrace embedded model reloading\");\n\t\t\t\tif (submod->loadstate == MLS_NOTLOADED)\t//pull it back in if it was flushed.\n\t\t\t\tMod_LoadModel(submod, MLV_WARN);\n\t\t\t\twhile(submod->loadstate == MLS_LOADING)\n\t\t\t\t\tCOM_WorkerPartialSync(submod, &submod->loadstate, MLS_LOADING);\n\t\t\t\tif (submod->loadstate != MLS_LOADED)\n\t\t\t\t{\n\t\t\t\t\tCon_ThrottlePrintf(&throttle, 1, \"BIH: embedded model %s failed to load\\n\", submod->name);\n\t\t\t\t\treturn; //something bad happened...\n\t\t\t\t}\n\t\t\t\tCon_DPrintf(\"BIH: embedded model ^[%s\\\\modelviewer\\\\%s^] now loading\\n\", submod->name,submod->name);\n\t\t\t}\n\n\t\t\tVectorSubtract (tr->startpos, node->data.mesh.tr->origin, start_l);\n\t\t\tVectorSubtract (tr->endpos, node->data.mesh.tr->origin, end_l);\n\t\t\tsubmod->funcs.NativeTrace(submod, 0, NULLFRAMESTATE, node->data.mesh.tr->axis, start_l, end_l, tr->size.min, tr->size.max, tr->shape==shape_iscapsule, tr->hitcontents, &sub);\n\n\t\t\tif (sub.truefraction < tr->trace.truefraction)\n\t\t\t{\n\t\t\t\ttr->trace.truefraction = sub.truefraction;\n\t\t\t\ttr->trace.fraction = sub.fraction;\n\t\t\t\ttr->trace.plane.dist = sub.plane.dist;\n\t\t\t\tVectorCopy(sub.plane.normal, tr->trace.plane.normal);\n\t\t\t\ttr->trace.surface = sub.surface;\n\t\t\t\ttr->trace.contents = sub.contents;\n\t\t\t\ttr->trace.startsolid |= sub.startsolid;\n\t\t\t\ttr->trace.allsolid = sub.allsolid;\n\t\t\t\tVectorAdd (sub.endpos, node->data.mesh.tr->origin, tr->trace.endpos);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\ttr->trace.startsolid |= sub.startsolid;\n\t\t\t\ttr->trace.allsolid &= sub.allsolid;\n\t\t\t}\n\t\t}\n\t\treturn;\n\tcase BIH_GROUP:\n\t\t{\n\t\t\tint i;\n\t\t\tfor (i = 0; i < node->group.numchildren; i++)\n\t\t\t\tBIH_RecursiveTrace(tr, node+node->group.firstchild+i, movesubbounds, nodebox);\n\t\t}\n\t\treturn;\n#ifdef BIH_USEBIH\n\tcase BIH_X:\n\tcase BIH_Y:\n\tcase BIH_Z:\n\t\t{\n\t\t\tstruct bihbox_s bounds;\n\t\t\tstruct bihbox_s newbounds;\n\t\t\tfloat distnear, distfar, nearfrac, farfrac, min, max;\n\t\t\tunsigned int axis = node->type-BIH_X, child, a, s;\n\t\t\tvec3_t points[2];\n\n\t\t\tif (!tr->totalmove[axis])\n\t\t\t{\t//doesn't move with respect to this axis. don't allow infinities.\n\t\t\t\tfor (child = 0; child < 2; child++)\n\t\t\t\t{\t//only recurse if we are actually within the child\n\t\t\t\t\tmin = node->bihnode.cmin[child] - tr->expand[axis];\n\t\t\t\t\tmax = node->bihnode.cmax[child] + tr->expand[axis];\n\t\t\t\t\tif (min <= tr->startpos[axis] && tr->startpos[axis] <= max)\n\t\t\t\t\t{\n\t\t\t\t\t\tbounds = *nodebox;\n\t\t\t\t\t\tbounds.min[axis] = min;\n\t\t\t\t\t\tbounds.max[axis] = max;\n\t\t\t\t\t\tBIH_RecursiveTrace(tr, node+node->bihnode.firstchild+child, movesubbounds, &bounds);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (tr->negativedir[axis])\n\t\t\t{\t//trace goes from right to left so favour the right.\n\t\t\t\tbounds = *nodebox;\n\t\t\t\tfor (child = 2; child-- > 0;)\n\t\t\t\t{\n\t\t\t\t\tbounds.min[axis] = node->bihnode.cmin[child] - tr->expand[axis];\n\t\t\t\t\tbounds.max[axis] = node->bihnode.cmax[child] + tr->expand[axis];\t//expand the bounds according to the player's size\n\n\t\t\t\t\tif (!BIH_BoundsIntersect(movesubbounds->min, movesubbounds->max, bounds.min, bounds.max))\n\t\t\t\t\t\tcontinue;\n//\t\t\t\t\tif (movesubbounds->max[axis] < bounds.min[axis])\n//\t\t\t\t\t\tcontinue;\t//(clipped) move bounds is outside this child\n//\t\t\t\t\tif (bounds.max[axis] < movesubbounds->min[axis])\n//\t\t\t\t\t\tcontinue;\t//(clipped) move bounds is outside this child\n\n\t\t\t\t\tdistnear = bounds.max[axis] - tr->startpos[axis];\n\t\t\t\t\tnearfrac = distnear/tr->totalmove[axis];\n\t\t\t\t\tif (nearfrac <= tr->trace.truefraction)\n\t\t\t\t\t{\n\t\t\t\t\t\tVectorMA(tr->startpos, nearfrac, tr->totalmove, points[0]);\t//clip the new movebounds (this is more to clip the other axis too)\n\t\t\t\t\t\tdistfar = bounds.min[axis] - tr->startpos[axis];\n\t\t\t\t\t\tfarfrac = distfar/tr->totalmove[axis];\n\t\t\t\t\t\tVectorMA(tr->startpos, farfrac, tr->totalmove, points[1]);\t//clip the new movebounds (this is more to clip the other axis too)\n\n\t\t\t\t\t\tfor (a = 0; a < 3; a++)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ts = points[0][a] > points[1][a];\n\t\t\t\t\t\t\tnewbounds.min[a] = max(movesubbounds->min[a], points[s][a] - tr->expand[a]);\n\t\t\t\t\t\t\tnewbounds.max[a] = min(movesubbounds->max[a], points[!s][a] + tr->expand[a]);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tBIH_RecursiveTrace(tr, node+node->bihnode.firstchild+child, &newbounds, &bounds);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\t//trace goes from left to right\n\t\t\t\tbounds = *nodebox;\n\t\t\t\tfor (child = 0; child < 2; child++)\n\t\t\t\t{\n\t\t\t\t\tbounds.min[axis] = node->bihnode.cmin[child] - tr->expand[axis];\n\t\t\t\t\tbounds.max[axis] = node->bihnode.cmax[child] + tr->expand[axis];\t//expand the bounds according to the player's size\n\n\t\t\t\t\tif (!BIH_BoundsIntersect(movesubbounds->min, movesubbounds->max, bounds.min, bounds.max))\n\t\t\t\t\t\tcontinue;\n//\t\t\t\t\tif (movesubbounds->max[axis] < bounds.min[axis])\n//\t\t\t\t\t\tcontinue;\t//(clipped) move bounds is outside this child\n//\t\t\t\t\tif (bounds.max[axis] < movesubbounds->min[axis])\n//\t\t\t\t\t\tcontinue;\t//(clipped) move bounds is outside this child\n\n\t\t\t\t\tdistnear = bounds.min[axis] - tr->startpos[axis];\n\t\t\t\t\tnearfrac = distnear/tr->totalmove[axis];\n\t\t\t\t\tif (nearfrac <= tr->trace.truefraction)\n\t\t\t\t\t{\n\t\t\t\t\t\tVectorMA(tr->startpos, nearfrac, tr->totalmove, points[0]);\t//clip the new movebounds (this is more to clip the other axis too)\n\t\t\t\t\t\tdistfar = bounds.max[axis] - tr->startpos[axis];\n\t\t\t\t\t\tfarfrac = distfar/tr->totalmove[axis];\n\t\t\t\t\t\tVectorMA(tr->startpos, farfrac, tr->totalmove, points[1]);\t//clip the new movebounds (this is more to clip the other axis too)\n\n\t\t\t\t\t\tfor (a = 0; a < 3; a++)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ts = points[0][a] > points[1][a];\n\t\t\t\t\t\t\tnewbounds.min[a] = max(movesubbounds->min[a], points[s][a] - tr->expand[a]);\n\t\t\t\t\t\t\tnewbounds.max[a] = min(movesubbounds->max[a], points[!s][a] + tr->expand[a]);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tBIH_RecursiveTrace(tr, node+node->bihnode.firstchild+child, &newbounds, &bounds);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn;\n#endif\n#ifdef BIH_USEBVH\n\tcase BVH_X:\n\tcase BVH_Y:\n\tcase BVH_Z:\n\t\t{\n\t\t\tstruct bihbox_s bounds;\n\t\t\tstruct bihbox_s newbounds;\n\t\t\tfloat distnear, distfar, nearfrac, farfrac, min, max;\n\t\t\tunsigned int axis = node->type-BVH_X, child, a, s;\n\t\t\tvec3_t points[2];\n\n\t\t\tif (!tr->totalmove[axis])\n\t\t\t{\t//doesn't move with respect to this axis. don't allow infinities.\n\t\t\t\tfor (child = 0; child < 2; child++)\n\t\t\t\t{\t//only recurse if we are actually within the child\n\t\t\t\t\tif (child == 0)\n\t\t\t\t\t{\n\t\t\t\t\t\tmin = node->bvhnode.min[axis] - tr->expand[axis];\n\t\t\t\t\t\tmax = node->bvhnode.cmax + tr->expand[axis];\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tmin = node->bvhnode.cmin - tr->expand[axis];\n\t\t\t\t\t\tmax = node->bvhnode.max[axis] + tr->expand[axis];\n\t\t\t\t\t}\n\t\t\t\t\tif (min <= tr->startpos[axis] && tr->startpos[axis] <= max)\n\t\t\t\t\t{\n\t\t\t\t\t\tVectorCopy(node->bvhnode.min, bounds.min);\n\t\t\t\t\t\tVectorCopy(node->bvhnode.max, bounds.max);\n\t\t\t\t\t\tbounds.min[axis] = min;\n\t\t\t\t\t\tbounds.max[axis] = max;\n\t\t\t\t\t\tCM_RecursiveBIHTrace(tr, node+node->bvhnode.firstchild+child, movesubbounds, &bounds);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (tr->negativedir[axis])\n\t\t\t{\t//trace goes from right to left so favour the right.\n\t\t\t\tVectorCopy(node->bvhnode.min, bounds.min);\n\t\t\t\tVectorCopy(node->bvhnode.max, bounds.max);\n\t\t\t\tfor (child = 2; child-- > 0;)\n\t\t\t\t{\n\t\t\t\t\tif (child == 0)\n\t\t\t\t\t{\n\t\t\t\t\t\tbounds.min[axis] = node->bvhnode.min[axis] - tr->expand[axis];\n\t\t\t\t\t\tbounds.max[axis] = node->bvhnode.cmax + tr->expand[axis];\t//expand the bounds according to the player's size\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tbounds.min[axis] = node->bvhnode.cmin - tr->expand[axis];\n\t\t\t\t\t\tbounds.max[axis] = node->bvhnode.max[axis] + tr->expand[axis];\t//expand the bounds according to the player's size\n\t\t\t\t\t}\n\n\t\t\t\t\tif (!BIH_BoundsIntersect(movesubbounds->min, movesubbounds->max, bounds.min, bounds.max))\n\t\t\t\t\t\tcontinue;\n//\t\t\t\t\tif (movesubbounds->max[axis] < bounds.min[axis])\n//\t\t\t\t\t\tcontinue;\t//(clipped) move bounds is outside this child\n//\t\t\t\t\tif (bounds.max[axis] < movesubbounds->min[axis])\n//\t\t\t\t\t\tcontinue;\t//(clipped) move bounds is outside this child\n\n\t\t\t\t\tdistnear = bounds.max[axis] - tr->startpos[axis];\n\t\t\t\t\tnearfrac = (distnear+DIST_EPSILON)/tr->totalmove[axis];\n\t\t\t\t\tif (nearfrac <= trace_truefraction)\n\t\t\t\t\t{\n\t\t\t\t\t\tVectorMA(tr->startpos, nearfrac, tr->totalmove, points[0]);\t//clip the new movebounds (this is more to clip the other axis too)\n\t\t\t\t\t\tdistfar = bounds.min[axis] - tr->startpos[axis];\n\t\t\t\t\t\tfarfrac = (distfar-DIST_EPSILON)/tr->totalmove[axis];\n\t\t\t\t\t\tVectorMA(tr->startpos, farfrac, tr->totalmove, points[1]);\t//clip the new movebounds (this is more to clip the other axis too)\n\n\t\t\t\t\t\tfor (a = 0; a < 3; a++)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ts = points[0][a] > points[1][a];\n\t\t\t\t\t\t\tnewbounds.min[a] = max(movesubbounds->min[a], points[s][a] - tr->expand[a]);\n\t\t\t\t\t\t\tnewbounds.max[a] = min(movesubbounds->max[a], points[!s][a] + tr->expand[a]);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tCM_RecursiveBIHTrace(tr, node+node->bvhnode.firstchild+child, &newbounds, &bounds);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\t//trace goes from left to right\n\t\t\t\tVectorCopy(node->bvhnode.min, bounds.min);\n\t\t\t\tVectorCopy(node->bvhnode.max, bounds.max);\n\t\t\t\tfor (child = 0; child < 2; child++)\n\t\t\t\t{\n\t\t\t\t\tif (child == 0)\n\t\t\t\t\t{\n\t\t\t\t\t\tbounds.min[axis] = node->bvhnode.min[axis] - tr->expand[axis];\n\t\t\t\t\t\tbounds.max[axis] = node->bvhnode.cmax + tr->expand[axis];\t//expand the bounds according to the player's size\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tbounds.min[axis] = node->bvhnode.cmin - tr->expand[axis];\n\t\t\t\t\t\tbounds.max[axis] = node->bvhnode.max[axis] + tr->expand[axis];\t//expand the bounds according to the player's size\n\t\t\t\t\t}\n\n\t\t\t\t\tif (!BIH_BoundsIntersect(movesubbounds->min, movesubbounds->max, bounds.min, bounds.max))\n\t\t\t\t\t\tcontinue;\n//\t\t\t\t\tif (movesubbounds->max[axis] < bounds.min[axis])\n//\t\t\t\t\t\tcontinue;\t//(clipped) move bounds is outside this child\n//\t\t\t\t\tif (bounds.max[axis] < movesubbounds->min[axis])\n//\t\t\t\t\t\tcontinue;\t//(clipped) move bounds is outside this child\n\n\t\t\t\t\tdistnear = bounds.min[axis] - tr->startpos[axis];\n\t\t\t\t\tnearfrac = (distnear-DIST_EPSILON)/tr->totalmove[axis];\n\t\t\t\t\tif (nearfrac <= trace_truefraction)\n\t\t\t\t\t{\n\t\t\t\t\t\tVectorMA(tr->startpos, nearfrac, tr->totalmove, points[0]);\t//clip the new movebounds (this is more to clip the other axis too)\n\t\t\t\t\t\tdistfar = bounds.max[axis] - tr->startpos[axis];\n\t\t\t\t\t\tfarfrac = (distfar+DIST_EPSILON)/tr->totalmove[axis];\n\t\t\t\t\t\tVectorMA(tr->startpos, farfrac, tr->totalmove, points[1]);\t//clip the new movebounds (this is more to clip the other axis too)\n\n\t\t\t\t\t\tfor (a = 0; a < 3; a++)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ts = points[0][a] > points[1][a];\n\t\t\t\t\t\t\tnewbounds.min[a] = max(movesubbounds->min[a], points[s][a] - tr->expand[a]);\n\t\t\t\t\t\t\tnewbounds.max[a] = min(movesubbounds->max[a], points[!s][a] + tr->expand[a]);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tCM_RecursiveBIHTrace(tr, node+node->bvhnode.firstchild+child, &newbounds, &bounds);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn;\n#endif\n\t}\n\tFTE_UNREACHABLE;\n}\n\n//tracebox-with-no-movement, can be a little faster.\nstatic void BIH_RecursiveTest (struct bihtrace_s *fte_restrict tr, const struct bihnode_s *fte_restrict node)\n{\n\t//with BIH, its possible for a large child node to have a box larger than its sibling.\n\tswitch(node->type)\n\t{\n#if defined(Q2BSPS) || defined(Q3BSPS)\n\tcase BIH_BRUSH:\n\t\tif (node->data.contents & tr->hitcontents)\n\t\t{\n\t\t\tq2cbrush_t *b = node->data.brush;\n//\t\t\tif (BIH_BoundsIntersect(tr->bounds.min, tr->bounds.max, b->absmins, b->absmaxs))\n\t\t\t\tBIH_TestBoxInBrush (tr, b);\n\t\t}\n\t\treturn;\n#endif\n#ifdef Q3BSPS\n\tcase BIH_PATCHBRUSH:\n\t\tif (node->data.contents & tr->hitcontents)\n\t\t{\n\t\t\tq2cbrush_t *b = node->data.patchbrush;\n//\t\t\tif (BIH_BoundsIntersect(tr->bounds.min, tr->bounds.max, b->absmins, b->absmaxs))\n\t\t\t\tBIH_TestBoxInPatch (tr, b);\n\t\t}\n\t\treturn;\n\tcase BIH_TRISOUP:\n\t\t/*if (node->data.contents & tr->hitcontents)\n//\t\t\tif (BIH_BoundsIntersect(cmesh->absmins, cmesh->absmaxs, tr->bounds.min, tr->bounds.max))\n\t\t\t{\n\t\t\t\tq3cmesh_t *cmesh = node->data.cmesh;\n\t\t\t\tMod_Trace_Trisoup_(cmesh->xyz_array, cmesh->indicies, cmesh->numincidies, trace_start, trace_end, trace_mins, trace_maxs, &trace_trace, &cmesh->surface->c);\n\t\t\t}*/\n\t\treturn;\n#endif\n\tcase BIH_TRIANGLE:\n\t\tif (node->data.contents & tr->hitcontents)\n\t\t\tBIH_TestToTriangle(tr, &node->data);\n\t\treturn;\n\tcase BIH_MODEL:\n\t\t{\t//lame...\n\t\t\ttrace_t sub;\n\t\t\tvec3_t start_l;\n\t\t\tvec3_t end_l;\n\n\t\t\tVectorSubtract (tr->startpos, node->data.mesh.tr->origin, start_l);\n\t\t\tVectorSubtract (tr->endpos, node->data.mesh.tr->origin, end_l);\n\t\t\tnode->data.mesh.model->funcs.NativeTrace(node->data.mesh.model, 0, NULLFRAMESTATE, node->data.mesh.tr->axis, start_l, end_l, tr->size.min, tr->size.max, tr->shape==shape_iscapsule, tr->hitcontents, &sub);\n\n\t\t\tif (sub.truefraction < tr->trace.truefraction)\n\t\t\t{\n\t\t\t\ttr->trace.truefraction = sub.truefraction;\n\t\t\t\ttr->trace.fraction = sub.fraction;\n\t\t\t\ttr->trace.plane.dist = sub.plane.dist;\n\t\t\t\tVectorCopy(sub.plane.normal, tr->trace.plane.normal);\n\t\t\t\ttr->trace.surface = sub.surface;\n\t\t\t\ttr->trace.contents = sub.contents;\n\t\t\t\ttr->trace.startsolid |= sub.startsolid;\n\t\t\t\ttr->trace.allsolid = sub.allsolid;\n\t\t\t\tVectorAdd (sub.endpos, node->data.mesh.tr->origin, tr->trace.endpos);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\ttr->trace.startsolid |= sub.startsolid;\n\t\t\t\ttr->trace.allsolid &= sub.allsolid;\n\t\t\t}\n\t\t}\n\t\treturn;\n\tcase BIH_GROUP:\n\t\t{\n\t\t\tint i;\n\t\t\tfor (i = 0; i < node->group.numchildren; i++)\n\t\t\t{\n\t\t\t\tBIH_RecursiveTest(tr, node+node->group.firstchild+i);\n\t\t\t\tif (tr->trace.allsolid)\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\treturn;\n#ifdef BIH_USEBIH\n\tcase BIH_X:\n\tcase BIH_Y:\n\tcase BIH_Z:\n\t\t{\t//node (x y or z)\n\t\t\tfloat min; float max;\n\t\t\tint axis = node->type - BIH_X;\n\t\t\tmin = node->bihnode.cmin[0] - tr->expand[axis];\n\t\t\tmax = node->bihnode.cmax[0] + tr->expand[axis];\t//expand the bounds according to the player's size\n\n\t\t\t//the point can potentially be within both children, or neither.\n\t\t\t//it doesn't really matter which order we walk the tree, just be sure to do it efficiently.\n\t\t\tif (min <= tr->startpos[axis] && tr->startpos[axis] <= max)\n\t\t\t{\n\t\t\t\tBIH_RecursiveTest(tr, node+node->bihnode.firstchild+0);\n\t\t\t\tif (tr->trace.allsolid)\n\t\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tmin = node->bihnode.cmin[1] - tr->expand[axis];\n\t\t\tmax = node->bihnode.cmax[1] + tr->expand[axis];\n\t\t\tif (min <= tr->startpos[axis] && tr->startpos[axis] <= max)\n\t\t\t\tBIH_RecursiveTest(tr, node+node->bihnode.firstchild+1);\n\t\t}\n\t\treturn;\n#endif\n#ifdef BIH_USEBVH\n\tcase BVH_X:\n\tcase BVH_Y:\n\tcase BVH_Z:\n\t\t{\t//node (x y or z)\n\t\t\tfloat min; float max;\n\t\t\tint axis = node->type - BVH_X;\n\t\t\tmin = node->bvhnode.min[axis] - tr->expand[axis];\n\t\t\tmax = node->bvhnode.cmax + tr->expand[axis];\t//expand the bounds according to the player's size\n\n\t\t\t//the point can potentially be within both children, or neither.\n\t\t\t//it doesn't really matter which order we walk the tree, just be sure to do it efficiently.\n\t\t\tif (min <= tr->startpos[axis] && tr->startpos[axis] <= max)\n\t\t\t{\n\t\t\t\tCM_RecursiveBIHTest(tr, node+node->bvhnode.firstchild+0);\n\t\t\t\tif (trace_trace.allsolid)\n\t\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tmin = node->bvhnode.cmin - tr->expand[axis];\n\t\t\tmax = node->bvhnode.max[axis] + tr->expand[axis];\n\t\t\tif (min <= tr->startpos[axis] && tr->startpos[axis] <= max)\n\t\t\t\tCM_RecursiveBIHTest(tr, node+node->bvhnode.firstchild+1);\n\t\t}\n\t\treturn;\n#endif\n\t}\n\tFTE_UNREACHABLE;\n}\nstatic qboolean BIH_Trace(model_t *model, int forcehullnum, const framestate_t *framestate, const vec3_t axis[3], const vec3_t start, const vec3_t end, const vec3_t mins, const vec3_t maxs, qboolean capsule, unsigned int contents, trace_t *out_trace)\n{\n\tint\t\ti;\n\tvec3_t point;\n\tstruct bihtrace_s tr;\n\n\tif (axis)\n\t{\t//rotate everything\n\t\tVectorSet(tr.startpos, DotProduct(start, axis[0]), DotProduct(start, axis[1]), DotProduct(start, axis[2]));\n\t\tVectorSet(tr.endpos,   DotProduct(end,   axis[0]), DotProduct(end,   axis[1]), DotProduct(end,   axis[2]));\n\t\tVectorSet(tr.up, axis[0][2], -axis[1][2], axis[2][2]);\n\t}\n\telse\n\t{\t//axial bboxes. woo.\n\t\tVectorCopy(start, tr.startpos);\n\t\tVectorCopy(end, tr.endpos);\n\t\tVectorSet(tr.up, 0, 0, 1);\n\t}\n\n\n\t// fill in a default trace\n\tmemset (&tr.trace, 0, sizeof(tr.trace));\n\ttr.trace.fraction = tr.trace.truefraction = 1;\n\ttr.trace.surface = &(nullsurface.c);\n\n\tif (model)\t// map is loaded...\n\t{\n\t\ttr.hitcontents = contents;\n\t\tVectorCopy (mins, tr.size.min);\n\t\tVectorCopy (maxs, tr.size.max);\n\n\t\tif (1)\t//center the point of the trace in the middle...\n\t\t{\n\t\t\tVectorAdd(tr.size.max, tr.size.min, point);\n\t\t\tVectorScale(point, 0.5, point);\n\n\t\t\tVectorAdd(tr.startpos, point, tr.startpos);\n\t\t\tVectorAdd(tr.endpos, point, tr.endpos);\n\t\t\tVectorSubtract(tr.size.min, point, tr.size.min);\n\t\t\tVectorSubtract(tr.size.max, point, tr.size.max);\n\t\t}\n\n\n\n\t\t// build a bounding box of the entire move (for patches)\n\t\tClearBounds (tr.bounds.min, tr.bounds.max);\n\n\t\t//determine the type of trace that we're going to use, and the max extents\n\t\tif (tr.size.min[0] == 0 && tr.size.min[1] == 0 && tr.size.min[2] == 0 && tr.size.max[0] == 0 && tr.size.max[1] == 0 && tr.size.max[2] == 0)\n\t\t{\n\t\t\ttr.shape = shape_ispoint;\n\t\t\tVectorSet (tr.expand, 1/32.0, 1/32.0, 1/32.0);\n\t\t\t//acedemic\n\t\t\tAddPointToBounds (tr.startpos, tr.bounds.min, tr.bounds.max);\n\t\t\tAddPointToBounds (tr.endpos, tr.bounds.min, tr.bounds.max);\n\t\t}\n\t\telse if (capsule)\n\t\t{\n\t\t\tfloat ext;\n\t\t\ttr.shape = shape_iscapsule;\n\t\t\t//determine the capsule sizes\n\t\t\ttr.capsulesize[0] = ((tr.size.max[0]-tr.size.min[0]) + (tr.size.max[1]-tr.size.min[1]))/4.0;\n\t\t\ttr.capsulesize[1] = tr.size.max[2];\n\t\t\ttr.capsulesize[2] = tr.size.min[2];\n\t\t\t//make sure the mins_z/maxs_z isn't screwed.\n\t//\t\tif (tr.capsulesize[1]-tr.capsulesize[2] < tr.capsulesize[0])\n\t//\t\t\ttr.capsulesize[1] = tr.capsulesize[0]+tr.capsulesize[2];\n\t\t\text = (tr.capsulesize[1] > -tr.capsulesize[2])?tr.capsulesize[1]:-tr.capsulesize[2];\n\t\t\ttr.capsulesize[1] -= tr.capsulesize[0];\n\t\t\ttr.capsulesize[2] += tr.capsulesize[0];\n\t\t\ttr.expand[0] = ext+1;\n\t\t\ttr.expand[1] = ext+1;\n\t\t\ttr.expand[2] = ext+1;\n\n\t\t\t//determine the total range\n\t\t\tVectorSubtract (tr.startpos, tr.expand, point);\n\t\t\tAddPointToBounds (point, tr.bounds.min, tr.bounds.max);\n\t\t\tVectorAdd (tr.startpos, tr.expand, point);\n\t\t\tAddPointToBounds (point, tr.bounds.min, tr.bounds.max);\n\t\t\tVectorSubtract (tr.endpos, tr.expand, point);\n\t\t\tAddPointToBounds (point, tr.bounds.min, tr.bounds.max);\n\t\t\tVectorAdd (tr.endpos, tr.expand, point);\n\t\t\tAddPointToBounds (point, tr.bounds.min, tr.bounds.max);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tVectorAdd (tr.startpos, tr.size.min, point);\n\t\t\tAddPointToBounds (point, tr.bounds.min, tr.bounds.max);\n\t\t\tVectorAdd (tr.startpos, tr.size.max, point);\n\t\t\tAddPointToBounds (point, tr.bounds.min, tr.bounds.max);\n\t\t\tVectorAdd (tr.endpos, tr.size.min, point);\n\t\t\tAddPointToBounds (point, tr.bounds.min, tr.bounds.max);\n\t\t\tVectorAdd (tr.endpos, tr.size.max, point);\n\t\t\tAddPointToBounds (point, tr.bounds.min, tr.bounds.max);\n\n\t\t\ttr.shape = shape_isbox;\n\t\t\ttr.expand[0] = ((-tr.size.min[0] > tr.size.max[0]) ? -tr.size.min[0] : tr.size.max[0])+1;\n\t\t\ttr.expand[1] = ((-tr.size.min[1] > tr.size.max[1]) ? -tr.size.min[1] : tr.size.max[1])+1;\n\t\t\ttr.expand[2] = ((-tr.size.min[2] > tr.size.max[2]) ? -tr.size.min[2] : tr.size.max[2])+1;\n\t\t}\n\n\t\ttr.bounds.min[0] -= 1.0;\n\t\ttr.bounds.min[1] -= 1.0;\n\t\ttr.bounds.min[2] -= 1.0;\n\t\ttr.bounds.max[0] += 1.0;\n\t\ttr.bounds.max[1] += 1.0;\n\t\ttr.bounds.max[2] += 1.0;\n\n\n\t\tfor (i = 0; i < 3; i++)\n\t\t\ttr.negativedir[i] = (tr.endpos[i] - tr.startpos[i]) < 0;\n\t\tVectorSubtract(tr.endpos, tr.startpos, tr.totalmove);\n\t\tif (tr.startpos[0] == tr.endpos[0] && tr.startpos[1] == tr.endpos[1] && tr.startpos[2] == tr.endpos[2])\n\t\t\tBIH_RecursiveTest(&tr, model->cnodes);\n\t\telse\n\t\t{\n\t\t\tstruct bihbox_s worldsize;\n\t\t\tVectorCopy(model->mins, worldsize.min);\n\t\t\tVectorCopy(model->maxs, worldsize.max);\n\t\t\tBIH_RecursiveTrace(&tr, model->cnodes, &tr.bounds, &worldsize);\n\t\t}\n\n\t\tif (tr.trace.fraction<0)\n\t\t\ttr.trace.fraction=0;\n\t}\n\n\t*out_trace = tr.trace;\n#ifdef TERRAIN\n\tif (model->terrain)\n\t{\t//terrain is weird.\n\t\ttrace_t hmt;\n\t\tHeightmap_Trace(model, forcehullnum, framestate, NULL, tr.startpos, tr.endpos, mins, maxs, capsule, contents, &hmt);\n\t\tif (hmt.fraction < out_trace->fraction)\n\t\t\t*out_trace = hmt;\n\t}\n#endif\n\n\tif (out_trace->fraction == 1)\n\t{\n\t\tVectorCopy (end, out_trace->endpos);\n\t}\n\telse\n\t{\n\t\tVectorInterpolate(start, out_trace->fraction, end, out_trace->endpos);\t//too lazy to compute the endpos for each impact\n\t\tif (axis)\n\t\t{\n\t\t\tvec3_t iaxis[3];\n\t\t\tvec3_t norm;\n\t\t\tMatrix3x3_RM_Invert_Simple((const void *)axis, iaxis);\n\t\t\tVectorCopy(out_trace->plane.normal, norm);\n\t\t\tout_trace->plane.normal[0] = DotProduct(norm, iaxis[0]);\n\t\t\tout_trace->plane.normal[1] = DotProduct(norm, iaxis[1]);\n\t\t\tout_trace->plane.normal[2] = DotProduct(norm, iaxis[2]);\n\n\t\t\t/*just interpolate it, its easier than inverse matrix rotations*/\n\t\t\tVectorInterpolate(start, out_trace->fraction, end, out_trace->endpos);\n\t\t}\n\t}\n\treturn out_trace->fraction != 1;\n}\n\n//simplest form. no movement, no size.\nunsigned int BIH_TestContents (const struct bihnode_s *fte_restrict node, const vec3_t p)\n{\nrestart:\n\tswitch(node->type)\n\t{\t//leaf\n#if defined(Q2BSPS) || defined(Q3BSPS)\n\tcase BIH_BRUSH:\n\t\t{\n\t\t\tq2cbrush_t *b = node->data.brush;\n\t\t\tq2cbrushside_t *brushside = b->brushside;\n\t\t\tsize_t j;\n\t\t\tif (!BIH_BoundsIntersect(p, p, b->absmins, b->absmaxs))\n\t\t\t\treturn 0;\n\n\t\t\tfor ( j = 0; j < b->numsides; j++, brushside++ )\n\t\t\t{\n\t\t\t\tif ( PlaneDiff (p, brushside->plane) > 0 )\n\t\t\t\t\treturn 0;\n\t\t\t}\n\t\t\treturn b->contents;\t//inside all planes\n\t\t}\n#endif\n#ifdef Q3BSPS\n\tcase BIH_PATCHBRUSH:\n\t\t{\t//patches have no contents...\n\t\t\treturn 0;\n\t\t}\n\tcase BIH_TRISOUP:\n\t\t{\n\t\t\t//trisoup has no contents... depending upon epsilons would be crazy.\n\t\t\treturn 0;\n\t\t}\n#endif\n\tcase BIH_TRIANGLE:\n\t\treturn 0;\n\tcase BIH_MODEL:\n\t\t{\n\t\t\tvec3_t pos;\n\t\t\tVectorSubtract (p, node->data.mesh.tr->origin, pos);\n\t\t\treturn node->data.mesh.model->funcs.PointContents(node->data.mesh.model, node->data.mesh.tr->axis, pos);\n\t\t}\n\tcase BIH_GROUP:\n\t\t{\n\t\t\tint i;\n\t\t\tunsigned int contents = 0;\n\t\t\tfor (i = 0; i < node->group.numchildren; i++)\n\t\t\t\tcontents |= BIH_TestContents(node+node->group.firstchild+i, p);\n\t\t\treturn contents;\n\t\t}\n#ifdef BIH_USEBIH\n\tcase BIH_X:\n\tcase BIH_Y:\n\tcase BIH_Z:\n\t\t{\t//node (x y or z)\n\t\t\tunsigned int axis = node->type - BIH_X;\n\n\t\t\t//the point can potentially be within both children, or neither.\n\t\t\t//it doesn't really matter which order we walk the tree, just be sure to do it efficiently.\n\t\t\tif (node->bihnode.cmin[0] <= p[axis] && p[axis] <= node->bihnode.cmax[0])\n\t\t\t{\n\t\t\t\tif (node->bihnode.cmin[1] <= p[axis] && p[axis] <= node->bihnode.cmax[1])\n\t\t\t\t{\t//need to walk both\n\t\t\t\t\treturn\n\t\t\t\t\t\tBIH_TestContents(node+node->bihnode.firstchild+0, p) |\n\t\t\t\t\t\tBIH_TestContents(node+node->bihnode.firstchild+1, p);\n\t\t\t\t}\n\t\t\t\t//only need the left side.\n\t\t\t\tnode = node+node->bihnode.firstchild+0;\n\t\t\t\tgoto restart;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (node->bihnode.cmin[1] <= p[axis] && p[axis] <= node->bihnode.cmax[1])\n\t\t\t\t\t;\n\t\t\t\telse\n\t\t\t\t\treturn 0;\t//walk neither.\n\t\t\t\t//only need to walk the right\n\t\t\t\tnode = node+node->bihnode.firstchild+1;\n\t\t\t\tgoto restart;\n\n\t\t\t}\n\t\t}\n#endif\n#ifdef BIH_USEBVH\n\tcase BVH_X:\n\tcase BVH_Y:\n\tcase BVH_Z:\n\t\t{\t//node (x y or z)\n\t\t\tunsigned int contents;\n\t\t\tunsigned int axis = node->type - BVH_X;\n\n\t\t\t//the point can potentially be within both children, or neither.\n\t\t\t//it doesn't really matter which order we walk the tree, just be sure to do it efficiently.\n\t\t\tif (node->bvhnode.min[axis] <= p[axis] && p[axis] <= node->bvhnode.cmax)\n\t\t\t\tcontents = BIH_TestContents(node+node->bvhnode.firstchild+0, p);\n\t\t\telse\n\t\t\t\tcontents = 0;\n\n\t\t\tif (node->bvhnode.cmin <= p[axis] && p[axis] <= node->bvhnode.max[axis])\n\t\t\t\tcontents |= BIH_TestContents(node+node->bvhnode.firstchild+1, p);\n\t\t\treturn contents;\n\t\t}\n#endif\n\t}\n\tFTE_UNREACHABLE;\n\treturn 0;\n}\nstatic unsigned int BIH_PointContents(struct model_s *mod, const vec3_t axis[3], const vec3_t p)\n{\n\tunsigned int contents;\n\tvec3_t n;\n\tif (axis)\n\t{\n\t\tVectorSet(n, DotProduct(p, axis[0]), DotProduct(p, axis[1]), DotProduct(p, axis[2]));\n\t\tp = n;\n\t}\n\tcontents = BIH_TestContents (mod->cnodes, p);\n#ifdef TERRAIN\n\tif (mod->terrain)\n\t\tcontents |= Heightmap_PointContents(mod, NULL, p);\n#endif\n\treturn contents;\n}\n\nstatic unsigned int BIH_NativeContents(struct model_s *mod, int hulloverride, const framestate_t *framestate, const vec3_t axis[3], const vec3_t p, const vec3_t mins, const vec3_t maxs)\n{\t//we don't support boxcontents... sorry.\n\tunsigned int contents;\n\tvec3_t n;\n\tif (axis)\n\t{\n\t\tVectorSet(n, DotProduct(p, axis[0]), DotProduct(p, axis[1]), DotProduct(p, axis[2]));\n\t\tp = n;\n\t}\n\tcontents = BIH_TestContents (mod->cnodes, p);\n#ifdef TERRAIN\n\tif (mod->terrain)\n\t\tcontents |= Heightmap_PointContents(mod, NULL, p);\n#endif\n\treturn contents;\n}\n\n\n\n\n\n\n\n\n\n#if defined(BIH_USEBIH) || defined(BIH_USEBVH)\nstatic int QDECL BIH_Sort_X (const void *va, const void *vb)\n{\n\tconst struct bihleaf_s *a = va, *b = vb;\n\tfloat am = a->maxs[0]+a->mins[0];\n\tfloat bm = b->maxs[0]+b->mins[0];\n\tif (am == bm)\n\t\treturn 0;\n\treturn am > bm;\n}\nstatic int QDECL BIH_Sort_Y (const void *va, const void *vb)\n{\n\tconst struct bihleaf_s *a = va, *b = vb;\n\tfloat am = a->maxs[1]+a->mins[1];\n\tfloat bm = b->maxs[1]+b->mins[1];\n\tif (am == bm)\n\t\treturn 0;\n\treturn am > bm;\n}\nstatic int QDECL BIH_Sort_Z (const void *va, const void *vb)\n{\n\tconst struct bihleaf_s *a = va, *b = vb;\n\tfloat am = a->maxs[2]+a->mins[2];\n\tfloat bm = b->maxs[2]+b->mins[2];\n\tif (am == bm)\n\t\treturn 0;\n\treturn am > bm;\n}\n#endif\nstatic struct bihbox_s BIH_BuildNode (struct bihnode_s *node, struct bihnode_s **freenodes, struct bihleaf_s *leafs, size_t numleafs)\n{\n\tstruct bihbox_s bounds;\n\tif (numleafs == 1)\t//the leaf just gives the brush pointer.\n\t{\n\t\tsize_t i;\n\t\tVectorCopy(leafs[0].mins, bounds.min);\n\t\tVectorCopy(leafs[0].maxs, bounds.max);\n\t\tnode->type = leafs[0].type;\n\t\tnode->data = leafs[0].data;\n\n\t\t//expand by 1qu, to avoid precision issues.\n\t\tfor (i = 0; i < 3; i++)\n\t\t{\n\t\t\tbounds.min[i] -= 1;\n\t\t\tbounds.max[i] += 1;\n\t\t}\n\t}\n#ifdef BIH_USEBIH\n\telse if (numleafs >= 8)\t//the leaf just gives the brush pointer.\n\t{\n\t\tsize_t i, j;\n\t\tsize_t numleft = numleafs / 2;\t//this ends up splitting at the median point.\n\t\tsize_t numright = numleafs - numleft;\n\t\tstruct bihbox_s left, right;\n\t\tstruct bihnode_s *cnodes;\n\t\tstatic int (QDECL *sorts[3]) (const void *va, const void *vb) = {BIH_Sort_X, BIH_Sort_Y, BIH_Sort_Z};\n\t\tVectorCopy(leafs[0].mins, bounds.min);\n\t\tVectorCopy(leafs[0].maxs, bounds.max);\n\t\tfor (i = 1; i < numleafs; i++)\n\t\t{\n\t\t\tfor(j = 0; j < 3; j++)\n\t\t\t{\n\t\t\t\tif (bounds.min[j] > leafs[i].mins[j])\n\t\t\t\t\tbounds.min[j] = leafs[i].mins[j];\n\t\t\t\tif (bounds.max[j] < leafs[i].maxs[j])\n\t\t\t\t\tbounds.max[j] = leafs[i].maxs[j];\n\t\t\t}\n\t\t}\n#if 1\n\t\t{\t//balanced by counts\n\t\t\tvec3_t mid;\n\t\t\tint onleft[3], onright[3], weight[3];\n\t\t\tVectorAvg(bounds.max, bounds.min, mid);\n\t\t\tVectorClear(onleft);\n\t\t\tVectorClear(onright);\n\t\t\tfor (i = 0; i < numleafs; i++)\n\t\t\t{\n\t\t\t\tfor (j = 0; j < 3; j++)\n\t\t\t\t{\t//ignore leafs that split the node.\n\t\t\t\t\tif (leafs[i].maxs[j] < mid[j])\n\t\t\t\t\t\tonleft[j]++;\n\t\t\t\t\tif (mid[j] > leafs[i].mins[j])\n\t\t\t\t\t\tonright[j]++;\n\t\t\t\t}\n\t\t\t}\n\t\t\tfor (j = 0; j < 3; j++)\n\t\t\t\tweight[j] = onleft[j]+onright[j] - abs(onleft[j]-onright[j]);\n\t\t\t//pick the most balanced.\n\t\t\tif (weight[0] > weight[1] && weight[0] > weight[2])\n\t\t\t\tnode->type = BIH_X;\n\t\t\telse if (weight[1] > weight[2])\n\t\t\t\tnode->type = BIH_Y;\n\t\t\telse\n\t\t\t\tnode->type = BIH_Z;\n\t\t}\n#else\n\t\t{\t//balanced by volume\n\t\t\tvec3_t size;\n\t\t\tVectorSubtract(bounds.max, bounds.min, size);\n\t\t\tif (size[0] > size[1] && size[0] > size[2])\n\t\t\t\tnode->type = BIH_X;\n\t\t\telse if (size[1] > size[2])\n\t\t\t\tnode->type = BIH_Y;\n\t\t\telse\n\t\t\t\tnode->type = BIH_Z;*/\n\t\t}\n#endif\n\t\tqsort(leafs, numleafs, sizeof(*leafs), sorts[node->type-BIH_X]);\n\n\t\tcnodes = *freenodes;\n\t\t*freenodes += 2;\n\t\tnode->bihnode.firstchild = cnodes - node;\n\t\tleft = BIH_BuildNode (cnodes+0, freenodes, leafs, numleft);\n\t\tright = BIH_BuildNode (cnodes+1, freenodes, &leafs[numleft], numright);\n\n\t\tnode->bihnode.cmin[0] = left.min[node->type-BIH_X];\n\t\tnode->bihnode.cmax[0] = left.max[node->type-BIH_X];\n\t\tnode->bihnode.cmin[1] = right.min[node->type-BIH_X];\n\t\tnode->bihnode.cmax[1] = right.max[node->type-BIH_X];\n\n\t\tbounds = left;\n\t\tAddPointToBounds(right.min, bounds.min, bounds.max);\n\t\tAddPointToBounds(right.max, bounds.min, bounds.max);\n\t}\n#endif\n#ifdef BIH_USEBVH\n\telse if (numleafs >= 8)\t//the leaf just gives the brush pointer.\n\t{\n\t\tsize_t i, j;\n\t\tsize_t numleft = numleafs / 2;\t//this ends up splitting at the median point.\n\t\tsize_t numright = numleafs - numleft;\n\t\tstruct bihbox_s left, right;\n\t\tstruct bihnode_s *cnodes;\n\t\tstatic int (QDECL *sorts[3]) (const void *va, const void *vb) = {CM_SortBIH_X, CM_SortBIH_Y, CM_SortBIH_Z};\n\t\tVectorCopy(leafs[0].mins, bounds.min);\n\t\tVectorCopy(leafs[0].maxs, bounds.max);\n\t\tfor (i = 1; i < numleafs; i++)\n\t\t{\n\t\t\tfor(j = 0; j < 3; j++)\n\t\t\t{\n\t\t\t\tif (bounds.min[j] > leafs[i].mins[j])\n\t\t\t\t\tbounds.min[j] = leafs[i].mins[j];\n\t\t\t\tif (bounds.max[j] < leafs[i].maxs[j])\n\t\t\t\t\tbounds.max[j] = leafs[i].maxs[j];\n\t\t\t}\n\t\t}\n#if 1\n\t\t{\t//balanced by counts\n\t\t\tvec3_t mid;\n\t\t\tint onleft[3], onright[3], weight[3];\n\t\t\tVectorAvg(bounds.max, bounds.min, mid);\n\t\t\tVectorClear(onleft);\n\t\t\tVectorClear(onright);\n\t\t\tfor (i = 0; i < numleafs; i++)\n\t\t\t{\n\t\t\t\tfor (j = 0; j < 3; j++)\n\t\t\t\t{\t//ignore leafs that split the node.\n\t\t\t\t\tif (leafs[i].maxs[j] < mid[j])\n\t\t\t\t\t\tonleft[j]++;\n\t\t\t\t\tif (mid[j] > leafs[i].mins[j])\n\t\t\t\t\t\tonright[j]++;\n\t\t\t\t}\n\t\t\t}\n\t\t\tfor (j = 0; j < 3; j++)\n\t\t\t\tweight[j] = onleft[j]+onright[j] - abs(onleft[j]-onright[j]);\n\t\t\t//pick the most balanced.\n\t\t\tif (weight[0] > weight[1] && weight[0] > weight[2])\n\t\t\t\tnode->type = BVH_X;\n\t\t\telse if (weight[1] > weight[2])\n\t\t\t\tnode->type = BVH_Y;\n\t\t\telse\n\t\t\t\tnode->type = BVH_Z;\n\t\t}\n#else\n\t\t{\t//balanced by volume\n\t\t\tvec3_t size;\n\t\t\tVectorSubtract(bounds.max, bounds.min, size);\n\t\t\tif (size[0] > size[1] && size[0] > size[2])\n\t\t\t\tnode->type = BVH_X;\n\t\t\telse if (size[1] > size[2])\n\t\t\t\tnode->type = BVH_Y;\n\t\t\telse\n\t\t\t\tnode->type = BVH_Z;*/\n\t\t}\n#endif\n\t\tqsort(leafs, numleafs, sizeof(*leafs), sorts[node->type-BVH_X]);\n\n\t\tcnodes = *freenodes;\n\t\t*freenodes += 2;\n\t\tnode->bvhnode.firstchild = cnodes - node;\n\t\tleft = BIH_BuildNode (cnodes+0, freenodes, leafs, numleft);\n\t\tright = BIH_BuildNode (cnodes+1, freenodes, &leafs[numleft], numright);\n\n\t\tnode->bvhnode.min[0] = min(left.min[0], right.min[0]);\n\t\tnode->bvhnode.min[1] = min(left.min[1], right.min[1]);\n\t\tnode->bvhnode.min[2] = min(left.min[2], right.min[2]);\n\t\tnode->bvhnode.cmax = left.max[node->type-BVH_X];\n\t\tnode->bvhnode.cmin = right.min[node->type-BVH_X];\n\t\tnode->bvhnode.max[0] = max(left.max[0], right.max[0]);\n\t\tnode->bvhnode.max[1] = max(left.max[1], right.max[1]);\n\t\tnode->bvhnode.max[2] = max(left.max[2], right.max[2]);\n\n\t\tbounds = left;\n\t\tAddPointToBounds(right.min, bounds.min, bounds.max);\n\t\tAddPointToBounds(right.max, bounds.min, bounds.max);\n\t}\n#endif\n\telse\n\t{\n\t\tstruct bihnode_s *cnodes;\n\t\tstruct bihbox_s cb;\n\t\tsize_t i;\n\t\tnode->type = BIH_GROUP;\n\n\t\tcnodes = *freenodes;\n\t\t*freenodes += numleafs;\n\t\tnode->group.firstchild = cnodes - node;\n\t\tnode->group.numchildren = numleafs;\n\n\t\tbounds = BIH_BuildNode(cnodes+0, freenodes, leafs+0, 1);\n\t\tfor (i = 1; i < numleafs; i++)\n\t\t{\n\t\t\tcb = BIH_BuildNode(cnodes+i, freenodes, leafs+i, 1);\n\t\t\tAddPointToBounds(cb.min, bounds.min, bounds.max);\n\t\t\tAddPointToBounds(cb.max, bounds.min, bounds.max);\n\t\t}\n\t}\n\treturn bounds;\n}\n\nvoid BIH_Build (model_t *mod, struct bihleaf_s *leafs, size_t numleafs)\n{\n\tsize_t numnodes;\n\tstruct bihnode_s *nodes, *tmpnodes;\n\n\tif (!numleafs)\n\t{\t//if we don't actually have anything solid, we still need SOMETHING so we don't crash.\n\t\t//we can just use an empty group node for that.\n\t\tnodes = ZG_Malloc(&mod->memgroup, sizeof(*nodes));\n\t\tnodes->type = BIH_GROUP;\n\t\tnodes->group.numchildren = 0;\n\t}\n\telse\n\t{\n\t\tnumnodes = numleafs*2-1;\n\t\tnodes = ZG_Malloc(&mod->memgroup, sizeof(*nodes)*numnodes);\n\n\t\ttmpnodes = nodes+1;\n\t\tBIH_BuildNode(nodes, &tmpnodes, leafs, numleafs);\n\t\tif (tmpnodes > nodes+numnodes)\n\t\t\tSys_Error(\"CM_BuildBIH: generated wrong number of nodes\");\n\t}\n\tmod->cnodes = nodes;\n\tmod->funcs.NativeTrace\t\t\t= BIH_Trace;\n\tmod->funcs.PointContents\t\t= BIH_PointContents;\n\tmod->funcs.NativeContents\t\t= BIH_NativeContents;\n}\n#ifdef SKELETALMODELS\nvoid BIH_BuildAlias (model_t *mod, galiasinfo_t *meshes)\n{\n\tsize_t numleafs, i;\n\tstruct bihleaf_s *leafs, *leaf;\n\tgaliasinfo_t *submesh;\n\n\tnumleafs = 0;\n\tfor (submesh = meshes; submesh; submesh = submesh->nextsurf)\n\t\tnumleafs+=submesh->numindexes/3;\n\tleaf = leafs = BZ_Malloc(sizeof(*leafs)*numleafs);\n\tfor (submesh = meshes; submesh; submesh = submesh->nextsurf)\n\t{\n\t\tfor (i = 0; i < submesh->numindexes; i+=3)\n\t\t{\n\t\t\tvec_t *v1,*v2,*v3;\n\t\t\tleaf->type = BIH_TRIANGLE;\n\t\t\tleaf->data.contents = submesh->contents;\n\t\t\tleaf->data.tri.indexes = submesh->ofs_indexes+i;\n\t\t\tleaf->data.tri.xyz = submesh->ofs_skel_xyz;\n\n\t\t\tv1 = leaf->data.tri.xyz[leaf->data.tri.indexes[0]];\n\t\t\tv2 = leaf->data.tri.xyz[leaf->data.tri.indexes[1]];\n\t\t\tv3 = leaf->data.tri.xyz[leaf->data.tri.indexes[2]];\n\n\t\t\tVectorCopy(v1, leaf->mins);\n\t\t\tVectorCopy(v1, leaf->maxs);\n\t\t\tAddPointToBounds(v2, leaf->mins, leaf->maxs);\n\t\t\tAddPointToBounds(v3, leaf->mins, leaf->maxs);\n\t\t\tleaf++;\n\t\t}\n\t}\n\tBIH_Build(mod, leafs, leaf-leafs);\n}\n#endif\n"
  },
  {
    "path": "engine/common/com_bih.h",
    "content": "#if 1\n\t#define BIH_USEBIH\n#else\n\t#define BIH_USEBVH\n#endif\n\ntypedef struct q2mapsurface_s  // used internally due to name len probs //ZOID\n{\n\tq2csurface_t\tc;\n\tchar\t\trname[32];\n} q2mapsurface_t;\ntypedef struct\n{\n\tmplane_t\t*plane;\n\tq2mapsurface_t\t*surface;\n} q2cbrushside_t;\ntypedef struct q2cbrush_s\n{\n\tint\t\t\tcheckcount;\t\t// to avoid repeated testings\n\tint\t\t\tcontents;\n\tvec3_t\t\tabsmins;\n\tvec3_t\t\tabsmaxs;\n\tint\t\t\tnumsides;\n\tq2cbrushside_t *brushside;\n} q2cbrush_t;\ntypedef struct\n{\n\tvec3_t\t\tabsmins, absmaxs;\n\n\tvecV_t\t\t*xyz_array;\n\tsize_t numverts;\n\tindex_t\t\t*indicies;\n\tsize_t numincidies;\n\n\tq2mapsurface_t\t*surface;\n\tint\t\t\tcheckcount;\t\t// to avoid repeated testings\n} q3cmesh_t;\ntypedef struct\n{\n\tvec3_t\t\tabsmins, absmaxs;\n\n\tint\t\t\tnumfacets;\n\tq2cbrush_t\t*facets;\n\tq2mapsurface_t\t*surface;\n\tint\t\t\tcheckcount;\t\t// to avoid repeated testings\n} q3cpatch_t;\n\n\nenum bihtype_e\n{\n\t//node types\n#ifdef BIH_USEBIH\n\tBIH_X,\n\tBIH_Y,\n\tBIH_Z,\n#endif\n#ifdef BIH_USEBVH\n\tBVH_X,\n\tBVH_Y,\n\tBVH_Z,\n#endif\n\tBIH_GROUP,\n\n\n\t//leaf types\n#if defined(Q2BSPS) || defined(Q3BSPS)\n\tBIH_BRUSH,\n#endif\n#ifdef Q3BSPS\n\tBIH_PATCHBRUSH,\n\tBIH_TRISOUP,\n#endif\n\tBIH_TRIANGLE,\n\tBIH_MODEL,\n};\nstruct bihdata_s\n{\n\tunsigned int contents;\n\tunion {\n#if defined(Q2BSPS) || defined(Q3BSPS)\n\t\tq2cbrush_t *brush;\n#endif\n#ifdef Q3BSPS\n\t\tq2cbrush_t *patchbrush;\n\t\tq3cmesh_t *cmesh;\n#endif\n\t\tstruct {\n//\t\t\tvec3_t norm;\n\t\t\tindex_t *indexes;\t//might be better to just bake 3 indexes instead of using a pointer to them\n\t\t\tvecV_t *xyz;\n\t\t} tri;\n\t\tstruct {\n\t\t\tmodel_t *model;\n\t\t\tstruct bihtransform_s\n\t\t\t{\n\t\t\t\tvec3_t axis[3];\n\t\t\t\tvec3_t origin;\n\t\t\t} *tr;\n\t\t} mesh;\n\t};\n};\nstruct bihleaf_s\n{\n\tenum bihtype_e type;\n\tvec3_t mins;\n\tvec3_t maxs;\n\tstruct bihdata_s data;\n};\n\nstruct galiasinfo_s;\n\n//generates a BIH tree and updates mod->funcs.NativeTrace|NativeContents funcs\nvoid BIH_Build (model_t *mod, struct bihleaf_s *items, size_t numitems);\nvoid BIH_BuildAlias (model_t *mod, struct galiasinfo_s *meshes);\n"
  },
  {
    "path": "engine/common/com_mesh.c",
    "content": "#include \"quakedef.h\"\n\n#include \"com_bih.h\"\n#include \"com_mesh.h\"\n\n#ifdef __F16C__\n#ifdef _MSC_VER\n\t#include <intrin.h>\n#else\n\t#include <x86intrin.h>\n#endif\n#endif\n\n//#include <x86intrin.h>\n\n//small helper to aid compiling.\n#if defined(SKELETALMODELS) || defined(MD3MODELS)\n\t#define SKELORTAGS\n#endif\n\nqboolean\t\tr_loadbumpmapping;\nextern cvar_t r_noframegrouplerp;\ncvar_t r_lerpmuzzlehack\t\t\t\t\t\t= CVARF  (\"r_lerpmuzzlehack\", \"1\", CVAR_ARCHIVE);\n#ifdef MD1MODELS\ncvar_t mod_h2holey_bugged\t\t\t\t\t= CVARD (\"mod_h2holey_bugged\", \"0\", \"Hexen2's holey-model flag uses index 0 as transparent (and additionally 255 in gl, due to a bug). GLQuake engines tend to have bugs that use ONLY index 255, resulting in a significant compatibility issue that can be resolved only with this shitty cvar hack.\");\ncvar_t mod_halftexel\t\t\t\t\t\t= CVARD (\"mod_halftexel\", \"1\", \"Offset texture coords by a half-texel, for compatibility with glquake and the majority of engine forks.\");\ncvar_t mod_nomipmap\t\t\t\t\t\t\t= CVARD (\"mod_nomipmap\", \"0\", \"Disables the use of mipmaps on quake1 mdls, consistent with its original software renderer.\");\n#endif\n#ifdef MODELFMT_OBJ\ncvar_t mod_obj_orientation\t\t\t\t\t= CVARD(\"mod_obj_orientation\", \"1\", \"Controls how the model's axis are interpreted.\\n0: x=forward, z=up (Quake)\\n1: x=forward, y=up\\n2: z=forward, y=up\");\n#endif\n#ifdef MD5MODELS\ncvar_t mod_md5_singleanimation\t\t\t\t= CVARD(\"mod_md5_singleanimation\", \"1\", \"When loading an md5mesh file, also attempt to load an .md5anim file, and unpack it into individual poses. Use 0 for mods that will be precaching their own md5anims for use with skeletal objects.\");\n#endif\nstatic void QDECL r_meshpitch_callback(cvar_t *var, char *oldvalue)\n{\n\tif (!strcmp(var->string, \"-1\") || !strcmp(var->string, \"1\"))\n\t\treturn;\n\tif (var->value <= 0)\n\t\tCvar_ForceSet(var, \"-1\");\n\telse\n\t\tCvar_ForceSet(var, \"1\");\n}\n#ifndef HAVE_LEGACY\ncvar_t r_meshpitch\t\t\t\t\t\t\t= CVARCD\t(\"r_meshpitch\", \"1\", r_meshpitch_callback, \"Specifies the direction of the pitch angle on mesh models formats, also affects gamecode, so do not change from its default.\");\n#else\ncvar_t r_meshpitch\t\t\t\t\t\t\t= CVARCD\t(\"r_meshpitch\", \"-1\", r_meshpitch_callback, \"Specifies the direction of the pitch angle on mesh models formats, Quake compatibility requires -1.\");\n#endif\ncvar_t r_meshroll\t\t\t\t\t\t\t= CVARCD\t(\"r_meshroll\", \"1\", r_meshpitch_callback, \"Specifies the direction of the roll angle on mesh models formats, also affects gamecode, so do not change from its default.\");\ncvar_t dpcompat_skinfiles\t\t\t\t\t= CVARD\t(\"dpcompat_skinfiles\", \"0\", \"When set, uses a nodraw shader for any unmentioned surfaces.\");\n\n#ifdef HAVE_CLIENT\nstatic void Mod_UpdateCRC(void *ctx, void *data, size_t a, size_t b)\n{\n\tchar st[40];\n\tQ_snprintfz(st, sizeof(st), \"%d\", (int) a);\n\tif (strcmp(st, InfoBuf_ValueForKey(&cls.userinfo[0], ctx)))\n\t{\n\t\tInfoBuf_SetKey(&cls.userinfo[0], ctx, st);\n\t\tif (cls.state >= ca_connected && (cls.protocol == CP_QUAKEWORLD || (cls.fteprotocolextensions2 & PEXT2_PREDINFO)))\n\t\t\tCL_SendClientCommand(true, \"setinfo %s %s\", (char*)ctx, st);\n\t}\n}\n#endif\n\n//Common loader function.\nqboolean Mod_DoCRC(model_t *mod, char *buffer, int buffersize)\n{\n#ifdef HAVE_CLIENT\n\t//we've got to have this bit\n\tif (mod->engineflags & MDLF_DOCRC)\n\t{\n\t\tunsigned int crc = CalcHashInt(&hash_crc16, buffer, buffersize);\n\t\tif (!(mod->engineflags & MDLF_PLAYER))\n\t\t{\t//eyes\n\t\t\tmod->tainted = (crc != 6967);\n\t\t}\n\t\tCOM_AddWork(WG_MAIN, Mod_UpdateCRC, (mod->engineflags & MDLF_PLAYER) ? pmodel_name : emodel_name, NULL, crc, 0);\n\t}\n\treturn Ruleset_FileLoaded(mod->publicname, buffer, buffersize);\n#else\n\treturn true;\n#endif\n}\n\nextern cvar_t gl_part_flame, r_fullbrightSkins, r_fb_models;\nextern cvar_t r_noaliasshadows;\n//extern cvar_t r_skin_overlays;\nextern cvar_t mod_md3flags;\n\n\n#ifdef HAVE_CLIENT\ntypedef struct\n{\n\tchar *name;\n\tfloat furthestallowedextremety;\t//this field is the combined max-min square, added together\n\t\t\t\t\t\t\t\t\t//note that while this allows you to move models about a little, you cannot resize the visible part\n} clampedmodel_t;\n\n//these should be rounded up slightly.\n//really this is only to catch spiked models. This doesn't prevent more visible models, just bigger ones.\nstatic clampedmodel_t clampedmodel[] = {\n\t{\"maps/b_bh100.bsp\", 3440},\n\t{\"progs/player.mdl\", 22497},\n\t{\"progs/eyes.mdl\", 755},\n\t{\"progs/gib1.mdl\", 374},\n\t{\"progs/gib2.mdl\", 1779},\n\t{\"progs/gib3.mdl\", 2066},\n\t{\"progs/bolt2.mdl\", 1160},\n\t{\"progs/end1.mdl\", 764},\n\t{\"progs/end2.mdl\", 981},\n\t{\"progs/end3.mdl\", 851},\n\t{\"progs/end4.mdl\", 903},\n\t{\"progs/g_shot.mdl\", 3444},\n\t{\"progs/g_nail.mdl\", 2234},\n\t{\"progs/g_nail2.mdl\", 3660},\n\t{\"progs/g_rock.mdl\", 3441},\n\t{\"progs/g_rock2.mdl\", 3660},\n\t{\"progs/g_light.mdl\", 2698},\n\t{\"progs/invisibl.mdl\", 196},\n\t{\"progs/quaddama.mdl\", 2353},\n\t{\"progs/invulner.mdl\", 2746},\n\t{\"progs/suit.mdl\", 3057},\n\t{\"progs/missile.mdl\", 416},\n\t{\"progs/grenade.mdl\", 473},\n\t{\"progs/spike.mdl\", 112},\n\t{\"progs/s_spike.mdl\", 112},\n\t{\"progs/backpack.mdl\", 1117},\n\t{\"progs/armor.mdl\", 2919},\n\t{\"progs/s_bubble.spr\", 100},\n\t{\"progs/s_explod.spr\", 1000},\n\n\t//and now TF models\n#ifdef warningmsg\n#pragma warningmsg(\"FIXME: these are placeholders\")\n#endif\n\t{\"progs/disp.mdl\", 3000},\n\t{\"progs/tf_flag.mdl\", 3000},\n\t{\"progs/tf_stan.mdl\", 3000},\n\t{\"progs/turrbase.mdl\", 3000},\n\t{\"progs/turrgun.mdl\", 3000}\n};\n#endif\n\n\n\n\n\n\n\n\nvoid QDECL Mod_AccumulateTextureVectors(vecV_t *const vc, vec2_t *const tc, vec3_t *nv, vec3_t *sv, vec3_t *tv, const index_t *idx, int numidx, qboolean calcnorms)\n{\n\tint i;\n\tconst float *v0, *v1, *v2;\n\tconst float *tc0, *tc1, *tc2;\n\n\tvec3_t d1, d2;\n\tfloat td1, td2;\n\n\tvec3_t norm, t, s;\n\tvec3_t temp;\n\n\tfor (i = 0; i < numidx; i += 3)\n\t{\n\t\t//this is the stuff we're working from\n\t\tv0 = vc[idx[i+0]];\n\t\tv1 = vc[idx[i+1]];\n\t\tv2 = vc[idx[i+2]];\n\t\ttc0 = tc[idx[i+0]];\n\t\ttc1 = tc[idx[i+1]];\n\t\ttc2 = tc[idx[i+2]];\n\n\t\t//calc perpendicular directions\n\t\tVectorSubtract(v1, v0, d1);\n\t\tVectorSubtract(v2, v0, d2);\n\n\t\t//calculate s as the pependicular of the t dir\n\t\ttd1 = tc1[1] - tc0[1];\n\t\ttd2 = tc2[1] - tc0[1];\n\t\ts[0] = td1 * d2[0] - td2 * d1[0];\n\t\ts[1] = td1 * d2[1] - td2 * d1[1];\n\t\ts[2] = td1 * d2[2] - td2 * d1[2];\n\n\t\t//calculate t as the pependicular of the s dir\n\t\ttd1 = tc1[0] - tc0[0];\n\t\ttd2 = tc2[0] - tc0[0];\n\t\tt[0] = td1 * d2[0] - td2 * d1[0];\n\t\tt[1] = td1 * d2[1] - td2 * d1[1];\n\t\tt[2] = td1 * d2[2] - td2 * d1[2];\n\n\t\t//the surface might be a back face and thus textured backwards\n\t\t//calc the normal twice and compare.\n\t\tnorm[0] = d2[1] * d1[2] - d2[2] * d1[1];\n\t\tnorm[1] = d2[2] * d1[0] - d2[0] * d1[2];\n\t\tnorm[2] = d2[0] * d1[1] - d2[1] * d1[0];\n\t\tCrossProduct(t, s, temp);\n\t\tif (DotProduct(temp, norm) < 0)\n\t\t{\n\t\t\tVectorNegate(s, s);\n\t\t\tVectorNegate(t, t);\n\t\t}\n\n\t\t//and we're done, accumulate the result\n\t\tif (calcnorms)\n\t\t{\n\t\t\tVectorAdd(nv[idx[i+0]], norm, nv[idx[i+0]]);\n\t\t\tVectorAdd(nv[idx[i+1]], norm, nv[idx[i+1]]);\n\t\t\tVectorAdd(nv[idx[i+2]], norm, nv[idx[i+2]]);\n\t\t}\n\n\t\tVectorAdd(sv[idx[i+0]], s, sv[idx[i+0]]);\n\t\tVectorAdd(sv[idx[i+1]], s, sv[idx[i+1]]);\n\t\tVectorAdd(sv[idx[i+2]], s, sv[idx[i+2]]);\n\n\t\tVectorAdd(tv[idx[i+0]], t, tv[idx[i+0]]);\n\t\tVectorAdd(tv[idx[i+1]], t, tv[idx[i+1]]);\n\t\tVectorAdd(tv[idx[i+2]], t, tv[idx[i+2]]);\n\t}\n}\n\nvoid Mod_AccumulateMeshTextureVectors(mesh_t *m)\n{\n\tMod_AccumulateTextureVectors(m->xyz_array, m->st_array, m->normals_array, m->snormals_array, m->tnormals_array, m->indexes, m->numindexes, false);\n}\n\nvoid QDECL Mod_NormaliseTextureVectors(vec3_t *n, vec3_t *s, vec3_t *t, int v, qboolean calcnorms)\n{\n\tint i;\n\tfloat f;\n\tvec3_t tmp;\n\n\tfor (i = 0; i < v; i++)\n\t{\n\t\tif (calcnorms)\n\t\t\tVectorNormalize(n[i]);\n\n\t\t//strip away any variance against the normal to keep it perpendicular, then normalize\n\t\tf = -DotProduct(s[i], n[i]);\n\t\tVectorMA(s[i], f, n[i], tmp);\n\t\tVectorNormalize2(tmp, s[i]);\n\n\t\tf = -DotProduct(t[i], n[i]);\n\t\tVectorMA(t[i], f, n[i], tmp);\n\t\tVectorNormalize2(tmp, t[i]);\n\t}\n}\n\n\n\n#ifdef SKELETALMODELS\n\n/*like GenMatrixPosQuat4Scale, but guess the quat.w*/\nstatic void GenMatrixPosQuat3Scale(vec3_t const pos, vec3_t const quat3, vec3_t const scale, float result[12])\n{\n\tvec4_t quat4;\n\tfloat term = 1 - DotProduct(quat3, quat3);\n\tif (term < 0)\n\t\tquat4[3] = 0;\n\telse\n\t\tquat4[3] = - (float) sqrt(term);\n\tVectorCopy(quat3, quat4);\n\tGenMatrixPosQuat4Scale(pos, quat4, scale, result);\n}\n\n#ifdef MD5MODELS\nstatic void GenMatrix(float x, float y, float z, float qx, float qy, float qz, float result[12])\n{\n\tfloat qw;\n\t{\t//figure out qw\n\t\tfloat term = 1 - (qx*qx) - (qy*qy) - (qz*qz);\n\t\tif (term < 0)\n\t\t\tqw = 0;\n\t\telse\n\t\t\tqw = - (float) sqrt(term);\n\t}\n\n\t{\t//generate the matrix\n\t\t/*\n\t\tfloat xx      = qx * qx;\n\t\tfloat xy      = qx * qy;\n\t\tfloat xz      = qx * qz;\n\t\tfloat xw      = qx * qw;\n\t\tfloat yy      = qy * qy;\n\t\tfloat yz      = qy * qz;\n\t\tfloat yw      = qy * qw;\n\t\tfloat zz      = qz * qz;\n\t\tfloat zw      = qz * qw;\n\t\tresult[0*4+0]  = 1 - 2 * ( yy + zz );\n\t\tresult[0*4+1]  =     2 * ( xy - zw );\n\t\tresult[0*4+2]  =     2 * ( xz + yw );\n\t\tresult[0*4+3]  =     x;\n\t\tresult[1*4+0]  =     2 * ( xy + zw );\n\t\tresult[1*4+1]  = 1 - 2 * ( xx + zz );\n\t\tresult[1*4+2]  =     2 * ( yz - xw );\n\t\tresult[1*4+3]  =     y;\n\t\tresult[2*4+0]  =     2 * ( xz - yw );\n\t\tresult[2*4+1]  =     2 * ( yz + xw );\n\t\tresult[2*4+2] = 1 - 2 * ( xx + yy );\n\t\tresult[2*4+3]  =     z;\n\t\t*/\n\n\t\t   float xx, xy, xz, xw, yy, yz, yw, zz, zw;\n\t\t   float x2, y2, z2;\n\t\t   x2 = qx + qx;\n\t\t   y2 = qy + qy;\n\t\t   z2 = qz + qz;\n\n\t\t   xx = qx * x2;   xy = qx * y2;   xz = qx * z2;\n\t\t   yy = qy * y2;   yz = qy * z2;   zz = qz * z2;\n\t\t   xw = qw * x2;   yw = qw * y2;   zw = qw * z2;\n\n\t\t   result[0*4+0] = 1.0f - (yy + zz);\n\t\t   result[1*4+0] = xy + zw;\n\t\t   result[2*4+0] = xz - yw;\n\n\t\t   result[0*4+1] = xy - zw;\n\t\t   result[1*4+1] = 1.0f - (xx + zz);\n\t\t   result[2*4+1] = yz + xw;\n\n\t\t   result[0*4+2] = xz + yw;\n\t\t   result[1*4+2] = yz - xw;\n\t\t   result[2*4+2] = 1.0f - (xx + yy);\n\n\t\t   result[0*4+3]  =     x;\n\t\t   result[1*4+3]  =     y;\n\t\t   result[2*4+3]  =     z;\n\t}\n}\n#endif\n\n#ifdef PSKMODELS\nstatic void PSKGenMatrix(float x, float y, float z, float qx, float qy, float qz, float qw, float result[12])\n{\n\tfloat xx, xy, xz, xw, yy, yz, yw, zz, zw;\n\tfloat x2, y2, z2;\n\tx2 = qx + qx;\n\ty2 = qy + qy;\n\tz2 = qz + qz;\n\n\txx = qx * x2;   xy = qx * y2;   xz = qx * z2;\n\tyy = qy * y2;   yz = qy * z2;   zz = qz * z2;\n\txw = qw * x2;   yw = qw * y2;   zw = qw * z2;\n\n\tresult[0*4+0] = 1.0f - (yy + zz);\n\tresult[1*4+0] = xy + zw;\n\tresult[2*4+0] = xz - yw;\n\n\tresult[0*4+1] = xy - zw;\n\tresult[1*4+1] = 1.0f - (xx + zz);\n\tresult[2*4+1] = yz + xw;\n\n\tresult[0*4+2] = xz + yw;\n\tresult[1*4+2] = yz - xw;\n\tresult[2*4+2] = 1.0f - (xx + yy);\n\n\tresult[0*4+3]  =     x;\n\tresult[1*4+3]  =     y;\n\tresult[2*4+3]  =     z;\n}\n#endif\n\n/*transforms some skeletal vecV_t values*/\nstatic void Alias_TransformVerticies_V(const float *bonepose, int vertcount, boneidx_t *bidx, float *weights, float *xyzin, float *fte_restrict xyzout)\n{\n#if 1\n\tint i, j;\n\tconst float *matrix, *matrix1;\n\tfloat mat[12];\n\tfor (i = 0; i < vertcount; i++, bidx+=4, weights+=4)\n\t{\n\t\tmatrix = &bonepose[12*bidx[0]];\n\t\tif (weights[1])\n\t\t{\n\t\t\tmatrix1 = &bonepose[12*bidx[1]];\n\t\t\tfor (j = 0; j < 12; j++)\n\t\t\t\tmat[j] = (weights[0] * matrix[j]) + (weights[1] * matrix1[j]);\n\t\t\tif (weights[2])\n\t\t\t{\n\t\t\t\tmatrix = &bonepose[12*bidx[2]];\n\t\t\t\tfor (j = 0; j < 12; j++)\n\t\t\t\t\tmat[j] += weights[2] * matrix[j];\n\t\t\t\tif (weights[3])\n\t\t\t\t{\n\t\t\t\t\tmatrix = &bonepose[12*bidx[3]];\n\t\t\t\t\tfor (j = 0; j < 12; j++)\n\t\t\t\t\t\tmat[j] += weights[3] * matrix[j];\n\t\t\t\t}\n\t\t\t}\n\t\t\tmatrix = mat;\n\t\t}\n\t\t//NOTE: else we assume that weights[0] is 1.\n\n\t\txyzout[0] = (xyzin[0] * matrix[0] + xyzin[1] * matrix[1] + xyzin[2] * matrix[ 2] + matrix[ 3]);\n\t\txyzout[1] = (xyzin[0] * matrix[4] + xyzin[1] * matrix[5] + xyzin[2] * matrix[ 6] + matrix[ 7]);\n\t\txyzout[2] = (xyzin[0] * matrix[8] + xyzin[1] * matrix[9] + xyzin[2] * matrix[10] + matrix[11]);\n\t\txyzout+=sizeof(vecV_t)/sizeof(vec_t);\n\t\txyzin+=sizeof(vecV_t)/sizeof(vec_t);\n\t}\n#else\n\tint i;\n\tconst float *matrix;\n\tfor (i = 0; i < vertcount; i++, bidx+=4, weights+=4)\n\t{\n\t\tmatrix = &bonepose[12*bidx[0]];\n\t\txyzout[0] = weights[0] * (xyzin[0] * matrix[0] + xyzin[1] * matrix[1] + xyzin[2] * matrix[ 2] + matrix[ 3]);\n\t\txyzout[1] = weights[0] * (xyzin[0] * matrix[4] + xyzin[1] * matrix[5] + xyzin[2] * matrix[ 6] + matrix[ 7]);\n\t\txyzout[2] = weights[0] * (xyzin[0] * matrix[8] + xyzin[1] * matrix[9] + xyzin[2] * matrix[10] + matrix[11]);\n\n\t\tif (weights[1])\n\t\t{\n\t\t\tmatrix = &bonepose[12*bidx[1]];\n\t\t\txyzout[0] += weights[1] * (xyzin[0] * matrix[0] + xyzin[1] * matrix[1] + xyzin[2] * matrix[ 2] + matrix[ 3]);\n\t\t\txyzout[1] += weights[1] * (xyzin[0] * matrix[4] + xyzin[1] * matrix[5] + xyzin[2] * matrix[ 6] + matrix[ 7]);\n\t\t\txyzout[2] += weights[1] * (xyzin[0] * matrix[8] + xyzin[1] * matrix[9] + xyzin[2] * matrix[10] + matrix[11]);\n\n\t\t\tif (weights[2])\n\t\t\t{\n\t\t\t\tmatrix = &bonepose[12*bidx[2]];\n\t\t\t\txyzout[0] += weights[2] * (xyzin[0] * matrix[0] + xyzin[1] * matrix[1] + xyzin[2] * matrix[ 2] + matrix[ 3]);\n\t\t\t\txyzout[1] += weights[2] * (xyzin[0] * matrix[4] + xyzin[1] * matrix[5] + xyzin[2] * matrix[ 6] + matrix[ 7]);\n\t\t\t\txyzout[2] += weights[2] * (xyzin[0] * matrix[8] + xyzin[1] * matrix[9] + xyzin[2] * matrix[10] + matrix[11]);\n\n\t\t\t\tif (weights[3])\n\t\t\t\t{\n\t\t\t\t\tmatrix = &bonepose[12*bidx[3]];\n\t\t\t\t\txyzout[0] += weights[3] * (xyzin[0] * matrix[0] + xyzin[1] * matrix[1] + xyzin[2] * matrix[ 2] + matrix[ 3]);\n\t\t\t\t\txyzout[1] += weights[3] * (xyzin[0] * matrix[4] + xyzin[1] * matrix[5] + xyzin[2] * matrix[ 6] + matrix[ 7]);\n\t\t\t\t\txyzout[2] += weights[3] * (xyzin[0] * matrix[8] + xyzin[1] * matrix[9] + xyzin[2] * matrix[10] + matrix[11]);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\txyzout+=sizeof(vecV_t)/sizeof(vec_t);\n\t\txyzin+=sizeof(vecV_t)/sizeof(vec_t);\n\t}\n#endif\n}\n\n/*transforms some skeletal vecV_t values*/\nstatic void Alias_TransformVerticies_VN(const float *bonepose, int vertcount, const boneidx_t *bidx, float *weights,\n\t\t\t\t\t\t\t\t\t\tconst float *xyzin, float *fte_restrict xyzout,\n\t\t\t\t\t\t\t\t\t\tconst float *normin, float *fte_restrict normout)\n{\n\tint i, j;\n\tconst float *matrix, *matrix1;\n\tfloat mat[12];\n\tfor (i = 0; i < vertcount; i++, \n\t\txyzout+=sizeof(vecV_t)/sizeof(vec_t), xyzin+=sizeof(vecV_t)/sizeof(vec_t),\n\t\tnormout+=sizeof(vec3_t)/sizeof(vec_t), normin+=sizeof(vec3_t)/sizeof(vec_t),\n\t\tbidx+=4, weights+=4)\n\t{\n\t\tmatrix = &bonepose[12*bidx[0]];\n\t\tif (weights[1])\n\t\t{\n\t\t\tmatrix1 = &bonepose[12*bidx[1]];\n\t\t\tfor (j = 0; j < 12; j++)\n\t\t\t\tmat[j] = (weights[0] * matrix[j]) + (weights[1] * matrix1[j]);\n\t\t\tif (weights[2])\n\t\t\t{\n\t\t\t\tmatrix = &bonepose[12*bidx[2]];\n\t\t\t\tfor (j = 0; j < 12; j++)\n\t\t\t\t\tmat[j] += weights[2] * matrix[j];\n\t\t\t\tif (weights[3])\n\t\t\t\t{\n\t\t\t\t\tmatrix = &bonepose[12*bidx[3]];\n\t\t\t\t\tfor (j = 0; j < 12; j++)\n\t\t\t\t\t\tmat[j] += weights[3] * matrix[j];\n\t\t\t\t}\n\t\t\t}\n\t\t\tmatrix = mat;\n\t\t}\n\n\t\txyzout[0] = (xyzin[0] * matrix[0] + xyzin[1] * matrix[1] + xyzin[2] * matrix[ 2] + matrix[ 3]);\n\t\txyzout[1] = (xyzin[0] * matrix[4] + xyzin[1] * matrix[5] + xyzin[2] * matrix[ 6] + matrix[ 7]);\n\t\txyzout[2] = (xyzin[0] * matrix[8] + xyzin[1] * matrix[9] + xyzin[2] * matrix[10] + matrix[11]);\n\n\t\tnormout[0] = (normin[0] * matrix[0] + normin[1] * matrix[1] + normin[2] * matrix[ 2]);\n\t\tnormout[1] = (normin[0] * matrix[4] + normin[1] * matrix[5] + normin[2] * matrix[ 6]);\n\t\tnormout[2] = (normin[0] * matrix[8] + normin[1] * matrix[9] + normin[2] * matrix[10]);\n\t}\n}\n\n/*transforms some skeletal vecV_t values*/\nstatic void Alias_TransformVerticies_VNST(const float *bonepose, int vertcount, const boneidx_t *bidx, const float *weights,\n\t\t\t\t\t\t\t\t\t\tconst float *xyzin, float *fte_restrict xyzout,\n\t\t\t\t\t\t\t\t\t\tconst float *normin, float *fte_restrict normout,\n\t\t\t\t\t\t\t\t\t\tconst float *sdirin, float *fte_restrict sdirout,\n\t\t\t\t\t\t\t\t\t\tconst float *tdirin, float *fte_restrict tdirout)\n{\n\tint i, j;\n\tconst float *matrix, *matrix1;\n\tfloat mat[12];\n\tfor (i = 0; i < vertcount; i++, bidx+=4, weights+=4)\n\t{\n\t\tmatrix = &bonepose[12*bidx[0]];\n\t\tif (weights[1])\n\t\t{\n\t\t\tmatrix1 = &bonepose[12*bidx[1]];\n\t\t\tfor (j = 0; j < 12; j++)\n\t\t\t\tmat[j] = (weights[0] * matrix[j]) + (weights[1] * matrix1[j]);\n\t\t\tif (weights[2])\n\t\t\t{\n\t\t\t\tmatrix = &bonepose[12*bidx[2]];\n\t\t\t\tfor (j = 0; j < 12; j++)\n\t\t\t\t\tmat[j] += weights[2] * matrix[j];\n\t\t\t\tif (weights[3])\n\t\t\t\t{\n\t\t\t\t\tmatrix = &bonepose[12*bidx[3]];\n\t\t\t\t\tfor (j = 0; j < 12; j++)\n\t\t\t\t\t\tmat[j] += weights[3] * matrix[j];\n\t\t\t\t}\n\t\t\t}\n\t\t\tmatrix = mat;\n\t\t}\n\n\t\txyzout[0] = (xyzin[0] * matrix[0] + xyzin[1] * matrix[1] + xyzin[2] * matrix[ 2] + matrix[ 3]);\n\t\txyzout[1] = (xyzin[0] * matrix[4] + xyzin[1] * matrix[5] + xyzin[2] * matrix[ 6] + matrix[ 7]);\n\t\txyzout[2] = (xyzin[0] * matrix[8] + xyzin[1] * matrix[9] + xyzin[2] * matrix[10] + matrix[11]);\n\t\txyzout+=sizeof(vecV_t)/sizeof(vec_t);\n\t\txyzin+=sizeof(vecV_t)/sizeof(vec_t);\n\n\t\tnormout[0] = (normin[0] * matrix[0] + normin[1] * matrix[1] + normin[2] * matrix[ 2]);\n\t\tnormout[1] = (normin[0] * matrix[4] + normin[1] * matrix[5] + normin[2] * matrix[ 6]);\n\t\tnormout[2] = (normin[0] * matrix[8] + normin[1] * matrix[9] + normin[2] * matrix[10]);\n\t\tnormout+=sizeof(vec3_t)/sizeof(vec_t);\n\t\tnormin+=sizeof(vec3_t)/sizeof(vec_t);\n\n\t\tsdirout[0] = (sdirin[0] * matrix[0] + sdirin[1] * matrix[1] + sdirin[2] * matrix[ 2]);\n\t\tsdirout[1] = (sdirin[0] * matrix[4] + sdirin[1] * matrix[5] + sdirin[2] * matrix[ 6]);\n\t\tsdirout[2] = (sdirin[0] * matrix[8] + sdirin[1] * matrix[9] + sdirin[2] * matrix[10]);\n\t\tsdirout+=sizeof(vec3_t)/sizeof(vec_t);\n\t\tsdirin+=sizeof(vec3_t)/sizeof(vec_t);\n\n\t\ttdirout[0] = (tdirin[0] * matrix[0] + tdirin[1] * matrix[1] + tdirin[2] * matrix[ 2]);\n\t\ttdirout[1] = (tdirin[0] * matrix[4] + tdirin[1] * matrix[5] + tdirin[2] * matrix[ 6]);\n\t\ttdirout[2] = (tdirin[0] * matrix[8] + tdirin[1] * matrix[9] + tdirin[2] * matrix[10]);\n\t\ttdirout+=sizeof(vec3_t)/sizeof(vec_t);\n\t\ttdirin+=sizeof(vec3_t)/sizeof(vec_t);\n\t}\n}\n\n//converts one entire frame to another skeleton type\n//only writes to destbuffer if absolutely needed\nstatic const float *Alias_ConvertBoneData(skeltype_t sourcetype, const float *sourcedata, size_t bonecount, galiasbone_t *bones, skeltype_t desttype, float *destbuffer, float *destbufferalt, size_t destbonecount)\n{\n\tint i;\n\tif (sourcetype == desttype)\n\t\treturn sourcedata;\n\n\t//everything can be converted up to SKEL_INVERSE_ABSOLUTE and back.\n\t//this means that everything can be converted to everything else, but it might take lots of individual transforms.\n\t//a->ia\n\t//r->a->ia\n\t//a->r\n\t//ia->ir\n\t//ir->ia\n\t//r->a->ia->ir\n\t//a->ia->ir\n\n\tif (bonecount > destbonecount || bonecount > MAX_BONES)\n\t\tSys_Error(\"Alias_ConvertBoneData: too many bones %\"PRIuSIZE\">%\"PRIuSIZE\"\\n\", bonecount, destbonecount);\n\n\t//r(->a)->ia(->ir)\n\tif (desttype == SKEL_INVERSE_RELATIVE && sourcetype == SKEL_RELATIVE)\n\t{\n\t\t//for this conversion, we need absolute data.\n\t\t//this is not an efficient operation.\n\t\tsourcedata = Alias_ConvertBoneData(sourcetype, sourcedata, bonecount, bones, SKEL_ABSOLUTE, destbuffer, destbufferalt, destbonecount);\n\t\tsourcetype = SKEL_INVERSE_ABSOLUTE;\n\t}\n\t//ir->ia(->a->r)\n\t//ir->ia(->a)\n\t//a->ia(->ir)\n\tif ((desttype == SKEL_ABSOLUTE && sourcetype == SKEL_INVERSE_RELATIVE) ||\n\t\t(desttype == SKEL_RELATIVE && sourcetype == SKEL_INVERSE_RELATIVE) ||\n\t\t(desttype == SKEL_INVERSE_RELATIVE && sourcetype == SKEL_ABSOLUTE))\n\t{\n\t\t//for this conversion, we need absolute data.\n\t\t//this is not an efficient operation.\n\t\tsourcedata = Alias_ConvertBoneData(sourcetype, sourcedata, bonecount, bones, SKEL_INVERSE_ABSOLUTE, destbuffer, destbufferalt, destbonecount);\n\t\tsourcetype = SKEL_INVERSE_ABSOLUTE;\n\t}\n\n\t//r->a\n\t//r->a(->ia)\n\t//ir->ia\n\tif ((sourcetype == SKEL_RELATIVE && (desttype == SKEL_ABSOLUTE || desttype == SKEL_INVERSE_ABSOLUTE)) ||\n\t\t(sourcetype == SKEL_INVERSE_RELATIVE && desttype == SKEL_INVERSE_ABSOLUTE))\n\t{\n\t\tfloat *dest = (sourcedata == destbuffer)?destbufferalt:destbuffer;\n\t\t/*needs to be an absolute skeleton*/\n\t\tfor (i = 0; i < bonecount; i++)\n\t\t{\n\t\t\tif (bones[i].parent >= 0)\n\t\t\t\tR_ConcatTransforms((const void*)(dest + bones[i].parent*12), (const void*)(sourcedata+i*12), (void*)(dest+i*12));\n\t\t\telse\n\t\t\t{\n\t\t\t\tVector4Copy(sourcedata+i*12+0, dest+i*12+0);\n\t\t\t\tVector4Copy(sourcedata+i*12+4, dest+i*12+4);\n\t\t\t\tVector4Copy(sourcedata+i*12+8, dest+i*12+8);\n\t\t\t}\n\t\t}\n\t\tsourcedata = dest;\n\t\tif (sourcetype == SKEL_INVERSE_RELATIVE)\n\t\t\tsourcetype = SKEL_INVERSE_ABSOLUTE;\n\t\telse\n\t\t\tsourcetype = SKEL_ABSOLUTE;\n\t}\n\n\t//ia->a(->r)\n\t//ia->a\n\tif ((desttype == SKEL_RELATIVE || desttype == SKEL_ABSOLUTE) && sourcetype == SKEL_INVERSE_ABSOLUTE)\n\t{\n\t\tfloat iim[12];\n\t\tfloat *dest = (sourcedata == destbuffer)?destbufferalt:destbuffer;\n\t\tfor (i = 0; i < bonecount; i++)\n\t\t{\n\t\t\tMatrix3x4_Invert_Simple(bones[i].inverse, iim);\n\t\t\tR_ConcatTransforms((const void*)(sourcedata + i*12), (const void*)iim, (void*)(dest + i*12));\n\t\t}\n\t\tsourcedata = dest;\n\t\tsourcetype = SKEL_ABSOLUTE;\n\t}\n\n\t//ia->ir\n\t//a->r\n\tif ((desttype == SKEL_RELATIVE && sourcetype == SKEL_ABSOLUTE) ||\n\t\t(desttype == SKEL_INVERSE_RELATIVE && sourcetype == SKEL_INVERSE_ABSOLUTE))\n\t{\n\t\tfloat ip[12];\n\t\tfloat *dest = (sourcedata == destbuffer)?destbufferalt:destbuffer;\n\t\tfor (i = 0; i < bonecount; i++)\n\t\t{\n\t\t\tif (bones[i].parent >= 0)\n\t\t\t{\n\t\t\t\tMatrix3x4_Invert_Simple(sourcedata+bones[i].parent*12, ip);\n\t\t\t\tR_ConcatTransforms((const void*)ip, (const void*)(sourcedata+i*12), (void*)(dest+i*12));\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tVector4Copy(sourcedata+i*12+0, dest+i*12+0);\n\t\t\t\tVector4Copy(sourcedata+i*12+4, dest+i*12+4);\n\t\t\t\tVector4Copy(sourcedata+i*12+8, dest+i*12+8);\n\t\t\t}\n\t\t}\n\t\tsourcedata = dest;\n\t\tif (sourcetype == SKEL_INVERSE_ABSOLUTE)\n\t\t\tsourcetype = SKEL_INVERSE_RELATIVE;\n\t\telse\n\t\t\tsourcetype = SKEL_RELATIVE;\n\t}\n\n\t//a->ia\n\tif (desttype == SKEL_INVERSE_ABSOLUTE && sourcetype == SKEL_ABSOLUTE)\n\t{\n\t\tfloat *dest = (sourcedata == destbuffer)?destbufferalt:destbuffer;\n\t\tfor (i = 0; i < bonecount; i++)\n\t\t\tR_ConcatTransforms((const void*)(sourcedata + i*12), (const void*)(bones[i].inverse), (void*)(dest + i*12));\n\t\tsourcedata = dest;\n\t\tsourcetype = SKEL_INVERSE_ABSOLUTE;\n\t}\n\t\n\tif (sourcetype == SKEL_IDENTITY)\n\t{\t//we can 'convert' identity matricies to anything. but we only want to do this when everything else is bad, because there really is no info here\n\t\tfloat *dest = (sourcedata == destbuffer)?destbufferalt:destbuffer;\n\t\tmemset(dest, 0, bonecount*12*sizeof(float));\n\t\tfor (i = 0; i < bonecount; i++)\n\t\t{\t//is this right? does it matter?\n\t\t\tdest[i*12+0] = 1;\n\t\t\tdest[i*12+5] = 1;\n\t\t\tdest[i*12+10] = 1;\n\t\t}\n\t\tsourcedata = dest;\n\t\tsourcetype = desttype;\t//panic\n\t}\n\n\tif (sourcetype != desttype)\n\t\tSys_Error(\"Alias_ConvertBoneData: %i->%i not supported\\n\", (int)sourcetype, (int)desttype);\n\n\treturn sourcedata;\n}\n/*\nconverts the bone data from source to dest.\nuses parent bone info, so don't try to offset for a first bone.\nALWAYS writes dest. Don't force it if you don't want to waste cycles when no conversion is actually needed.\ndestbonecount is to catch errors, its otherwise ignored for now. no identity padding.\n*/\nvoid QDECL Alias_ForceConvertBoneData(skeltype_t sourcetype, const float *sourcedata, size_t bonecount, galiasbone_t *bones, skeltype_t desttype, float *destbuffer, size_t destbonecount)\n{\n\tfloat altbuffer[MAX_BONES*12];\n\tconst float *buf = Alias_ConvertBoneData(sourcetype, sourcedata, bonecount, bones, desttype, destbuffer, altbuffer, destbonecount);\n\tif (buf != destbuffer)\n\t{\n\t\t//Alias_ConvertBoneData successfully managed to avoid doing any work. bah.\n\t\tmemcpy(destbuffer, buf, bonecount*12*sizeof(float));\n\t}\n}\n\n#endif\n\n\n\n\n\n#if 1\n\nstruct\n{\n\tint numcoords;\n\tvecV_t *coords;\n\n\tint numnorm;\n\tvec3_t *norm;\n\n\tint bonegroup;\n\tint vertgroup;\n\tentity_t *ent;\n\n#ifdef SKELETALMODELS\n\tboneidx_t *bonemap; //force the renderer to forget the current entity when this changes\n\tfloat gpubones[MAX_BONES*12]; //temp storage for multi-surface models with too many bones.\n\tfloat boneposebuffer1[MAX_BONES*12];\n\tfloat boneposebuffer2[MAX_BONES*12];\n\tskeltype_t bonecachetype;\n\tconst float *usebonepose;\n\tint bonecount;\n#endif\n\tqboolean usebones;\n\n\tvecV_t *acoords1;\n\tvecV_t *acoords2;\n\tvec3_t *anorm;\n\tvec3_t *anorms;\n\tvec3_t *anormt;\n\tfloat lerp;\n\n\tvbo_t vbo;\n\tvbo_t *vbop;\n} meshcache;\n\n//#define SSE_INTRINSICS\n#ifdef SSE_INTRINSICS\n#include <xmmintrin.h>\n#endif\n\n#ifndef SERVERONLY\n#ifdef D3DQUAKE\nvoid R_LightArraysByte_BGR(const entity_t *entity, vecV_t *coords, byte_vec4_t *colours, int vertcount, vec3_t *normals, qboolean usecolourmod)\n{\n\tint i;\n\tint c;\n\tfloat l;\n\n\tbyte_vec4_t ambientlightb;\n\tbyte_vec4_t shadelightb;\n\tconst float *lightdir = entity->light_dir;\n\n\tfor (i = 0; i < 3; i++)\n\t{\n\t\tl = entity->light_avg[2-i]*255;\n\t\tambientlightb[i] = bound(0, l, 255);\n\t\tl = entity->light_range[2-i]*255;\n\t\tshadelightb[i] = bound(0, l, 255);\n\t}\n\n\tif (!normals || (ambientlightb[0] == shadelightb[0] && ambientlightb[1] == shadelightb[1] && ambientlightb[2] == shadelightb[2]))\n\t{\n\t\tfor (i = vertcount-1; i >= 0; i--)\n\t\t{\n\t\t\t*(int*)colours[i] = *(int*)ambientlightb;\n//\t\t\tcolours[i][0] = ambientlightb[0];\n//\t\t\tcolours[i][1] = ambientlightb[1];\n//\t\t\tcolours[i][2] = ambientlightb[2];\n\t\t}\n\t}\n\telse\n\t{\n\t\tfor (i = vertcount-1; i >= 0; i--)\n\t\t{\n\t\t\tl = DotProduct(normals[i], lightdir);\n\t\t\tc = l*shadelightb[0];\n\t\t\tc += ambientlightb[0];\n\t\t\tcolours[i][0] = bound(0, c, 255);\n\t\t\tc = l*shadelightb[1];\n\t\t\tc += ambientlightb[1];\n\t\t\tcolours[i][1] = bound(0, c, 255);\n\t\t\tc = l*shadelightb[2];\n\t\t\tc += ambientlightb[2];\n\t\t\tcolours[i][2] = bound(0, c, 255);\n\t\t}\n\t}\n}\n#endif\n\nvoid R_LightArrays(const entity_t *entity, vecV_t *coords, avec4_t *colours, int vertcount, vec3_t *normals, float scale, qboolean colormod)\n{\n\textern cvar_t r_vertexdlights;\n\tint i;\n\tfloat l;\n\n\t//float *lightdir = currententity->light_dir; //unused variable\n\n\tif (!normals || (!entity->light_range[0] && !entity->light_range[1] && !entity->light_range[2]))\n\t{\n\t\tvec3_t val;\n\t\tVectorCopy(entity->light_avg, val);\n\t\tif (colormod)\n\t\t\tVectorMul(val, entity->shaderRGBAf, val);\n\n\t\tfor (i = vertcount-1; i >= 0; i--)\n\t\t{\n\t\t\tVectorCopy(val, colours[i]);\n\t\t}\n\t}\n\telse\n\t{\n\t\tavec3_t la, lr;\n\t\tVectorScale(entity->light_avg, scale, la);\n\t\tVectorScale(entity->light_range, scale, lr);\n\t\tif (colormod)\n\t\t{\n\t\t\tVectorMul(la, entity->shaderRGBAf, la);\n\t\t\tVectorMul(lr, entity->shaderRGBAf, lr);\n\t\t}\n#ifdef SSE_INTRINSICS\n\t\t__m128 va, vs, vl, vr;\n\t\tva = _mm_load_ps(ambientlight);\n\t\tvs = _mm_load_ps(shadelight);\n\t\tva.m128_f32[3] = 0;\n\t\tvs.m128_f32[3] = 1;\n#endif\n\t\t/*dotproduct will return a value between 1 and -1, so increase the ambient to be correct for normals facing away from the light*/\n\t\tfor (i = vertcount-1; i >= 0; i--)\n\t\t{\n\t\t\tl = DotProduct(normals[i], entity->light_dir);\n\t#ifdef SSE_INTRINSICS\n\t\t\tif (l < 0)\n\t\t\t{\n\t\t\t\t_mm_storeu_ps(colours[i], va);\n\t\t\t\t//stomp on colour[i][3] (will be set to 1)\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tvl = _mm_load1_ps(&l);\n\t\t\t\tvr = _mm_mul_ss(va,vl);\n\t\t\t\tvr = _mm_add_ss(vr,vs);\n\n\t\t\t\t_mm_storeu_ps(colours[i], vr);\n\t\t\t\t//stomp on colour[i][3] (will be set to 1)\n\t\t\t}\n\t#else\n\t\t\tif (l < 0)\n\t\t\t{\t//don't over-shade the dark side of the mesh.\n\t\t\t\tcolours[i][0] = la[0];\n\t\t\t\tcolours[i][1] = la[1];\n\t\t\t\tcolours[i][2] = la[2];\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tcolours[i][0] = l*lr[0]+la[0];\n\t\t\t\tcolours[i][1] = l*lr[1]+la[1];\n\t\t\t\tcolours[i][2] = l*lr[2]+la[2];\n\t\t\t}\n\t#endif\n\t\t}\n\t}\n\n\tif (r_vertexdlights.ival && r_dynamic.ival > 0 && normals)\n\t{\n\t\tunsigned int lno, v;\n\t\tvec3_t dir, rel;\n\t\tfloat dot, d, a;\n\t\t//don't include world lights\n\t\tfor (lno = rtlights_first; lno < RTL_FIRST; lno++)\n\t\t{\n\t\t\tif (cl_dlights[lno].radius && (cl_dlights[lno].flags & LFLAG_LIGHTMAP))\n\t\t\t{\n\t\t\t\tVectorSubtract (cl_dlights[lno].origin,\n\t\t\t\t\t\t\t\tentity->origin,\n\t\t\t\t\t\t\t\tdir);\n\t\t\t\tif (Length(dir)>cl_dlights[lno].radius+256)\t//far out man!\n\t\t\t\t\tcontinue;\n\n\t\t\t\trel[0] = -DotProduct(dir, entity->axis[0]);\n\t\t\t\trel[1] = -DotProduct(dir, entity->axis[1]);\n\t\t\t\trel[2] = -DotProduct(dir, entity->axis[2]);\n\n\t\t\t\tfor (v = 0; v < vertcount; v++)\n\t\t\t\t{\n\t\t\t\t\tVectorSubtract(coords[v], rel, dir);\n\t\t\t\t\tdot = DotProduct(dir, normals[v]);\n\t\t\t\t\tif (dot>0)\n\t\t\t\t\t{\n\t\t\t\t\t\td = DotProduct(dir, dir);\n\t\t\t\t\t\ta = 1/d;\n\t\t\t\t\t\tif (a>0)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ta *= 10000000*dot/sqrt(d);\n\t\t\t\t\t\t\tcolours[v][0] += a*cl_dlights[lno].color[0];\n\t\t\t\t\t\t\tcolours[v][1] += a*cl_dlights[lno].color[1];\n\t\t\t\t\t\t\tcolours[v][2] += a*cl_dlights[lno].color[2];\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n#ifdef NONSKELETALMODELS\nstatic void R_LerpFrames(mesh_t *mesh, galiaspose_t *p1, galiaspose_t *p2, float lerp, float expand, float lerpcutoff)\n{\n\textern cvar_t r_nolerp; // r_nolightdir is unused\n\tfloat blerp = 1-lerp;\n\tint i;\n\tvecV_t *p1v = p1->ofsverts, *p2v = p2->ofsverts;\n\tvec3_t *p1n = p1->ofsnormals, *p2n = p2->ofsnormals;\n\tvec3_t *p1s = p1->ofssvector, *p2s = p2->ofssvector;\n\tvec3_t *p1t = p1->ofstvector, *p2t = p2->ofstvector;\n\n\tmesh->snormals_array = blerp>0.5?p2s:p1s;\t\t//never lerp\n\tmesh->tnormals_array = blerp>0.5?p2t:p1t;\t\t//never lerp\n\tmesh->colors4f_array[0] = NULL;\t//not generated\n\n\tif (p1v == p2v || r_nolerp.value || !blerp)\n\t{\n\t\tmesh->normals_array = p1n;\n\t\tmesh->snormals_array = p1s;\n\t\tmesh->tnormals_array = p1t;\n\n\t\tif (expand)\n\t\t{\n\t\t\tvecV_t *oxyz = mesh->xyz_array;\n\t\t\tfor (i = 0; i < mesh->numvertexes; i++)\n\t\t\t{\n\t\t\t\toxyz[i][0] = p1v[i][0] + p1n[i][0]*expand;\n\t\t\t\toxyz[i][1] = p1v[i][1] + p1n[i][1]*expand;\n\t\t\t\toxyz[i][2] = p1v[i][2] + p1n[i][2]*expand;\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\telse\n\t\t\tmesh->xyz_array = p1v;\n\t}\n\telse\n\t{\n\t\tvecV_t *oxyz = mesh->xyz_array;\n\t\tvec3_t *onorm = mesh->normals_array;\n\t\tif (lerpcutoff)\n\t\t{\n\t\t\tvec3_t d;\n\t\t\tlerpcutoff *= lerpcutoff;\n\t\t\tfor (i = 0; i < mesh->numvertexes; i++)\n\t\t\t{\n\t\t\t\tVectorSubtract(p2v[i], p1v[i], d);\n\t\t\t\tif (DotProduct(d, d) > lerpcutoff)\n\t\t\t\t{\n\t\t\t\t\t//just use the current frame if we're over the lerp threshold.\n\t\t\t\t\t//these verts are considered to have teleported.\n\t\t\t\t\tonorm[i][0] = p2n[i][0];\n\t\t\t\t\tonorm[i][1] = p2n[i][1];\n\t\t\t\t\tonorm[i][2] = p2n[i][2];\n\n\t\t\t\t\toxyz[i][0] = p2v[i][0];\n\t\t\t\t\toxyz[i][1] = p2v[i][1];\n\t\t\t\t\toxyz[i][2] = p2v[i][2];\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tonorm[i][0] = p1n[i][0]*lerp + p2n[i][0]*blerp;\n\t\t\t\t\tonorm[i][1] = p1n[i][1]*lerp + p2n[i][1]*blerp;\n\t\t\t\t\tonorm[i][2] = p1n[i][2]*lerp + p2n[i][2]*blerp;\n\n\t\t\t\t\toxyz[i][0] = p1v[i][0]*lerp + p2v[i][0]*blerp;\n\t\t\t\t\toxyz[i][1] = p1v[i][1]*lerp + p2v[i][1]*blerp;\n\t\t\t\t\toxyz[i][2] = p1v[i][2]*lerp + p2v[i][2]*blerp;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tfor (i = 0; i < mesh->numvertexes; i++)\n\t\t\t{\n\t\t\t\tonorm[i][0] = p1n[i][0]*lerp + p2n[i][0]*blerp;\n\t\t\t\tonorm[i][1] = p1n[i][1]*lerp + p2n[i][1]*blerp;\n\t\t\t\tonorm[i][2] = p1n[i][2]*lerp + p2n[i][2]*blerp;\n\n\t\t\t\toxyz[i][0] = p1v[i][0]*lerp + p2v[i][0]*blerp;\n\t\t\t\toxyz[i][1] = p1v[i][1]*lerp + p2v[i][1]*blerp;\n\t\t\t\toxyz[i][2] = p1v[i][2]*lerp + p2v[i][2]*blerp;\n\t\t\t}\n\t\t}\n\n\t\tif (expand)\n\t\t{\n\t\t\tfor (i = 0; i < mesh->numvertexes; i++)\n\t\t\t{\n\t\t\t\toxyz[i][0] += onorm[i][0]*expand;\n\t\t\t\toxyz[i][1] += onorm[i][1]*expand;\n\t\t\t\toxyz[i][2] += onorm[i][2]*expand;\n\t\t\t}\n\t\t}\n\t}\n}\n#endif\n#endif\n#endif\n\n#ifdef SKELETALMODELS\n/*\n\treturns the up-to-8 skeletal bone poses to blend together.\n\treturn value is the number of blends that are actually live.\n*/\ntypedef struct\n{\n\tskeltype_t\tskeltype;\t//the skeletal type of this bone block. all blocks should have the same result or the whole thing is unusable or whatever.\n\tint\t\t\tfirstbone;\t//first bone of interest\n\tint\t\t\tendbone;\t//the first bone of the next group (ie: if first is 0, this is the count)\n\tint\t\t\tlerpcount;\t//number of pose+frac entries.\n\tfloat\t\tfrac[FRAME_BLENDS*2];\t//weight of this animation (1 if lerpcount is 1)\n\tfloat\t\t*pose[FRAME_BLENDS*2];\t//pointer to the raw frame data for bone 0.\n\tvoid\t\t*needsfree[FRAME_BLENDS*2];\n} skellerps_t;\nstatic qboolean Alias_BuildSkelLerps(skellerps_t *lerps, const struct framestateregion_s *fs, const galiasbone_t *boneinf, int numbones, const galiasinfo_t *inf)\n{\n\tint frame1;\t//signed, because frametime might be negative...\n\tint frame2;\n\tfloat mlerp;\t//minor lerp, poses within a group.\n\tint l = 0;\n\tgaliasanimation_t *g;\n\tunsigned int b;\n\tfloat totalweight = 0, dropweight = 0;\n#ifndef SERVERONLY\n\textern cvar_t r_nolerp;\n#endif\n\n\tlerps->skeltype = SKEL_IDENTITY;\t//sometimes nothing else is valid.\n\n\tfor (b = 0; b < FRAME_BLENDS; b++)\n\t{\n\t\tif (fs->lerpweight[b])\n\t\t{\n\t\t\tunsigned int frame = fs->frame[b];\n\t\t\tfloat time = fs->frametime[b];\n\t\t\tif (frame >= inf->numanimations)\n\t\t\t{\n\t\t\t\tif (inf->numanimations)\n\t\t\t\t\tframe = 0;\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tdropweight += fs->lerpweight[b];\n\t\t\t\t\tcontinue;//frame = (unsigned)frame%inf->groups;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tg = &inf->ofsanimations[frame];\n\n\t\t\tif (lerps->skeltype == SKEL_IDENTITY)\n\t\t\t\tlerps->skeltype = g->skeltype;\n\t\t\telse if (lerps->skeltype != g->skeltype)\n\t\t\t{\n\t\t\t\tdropweight += fs->lerpweight[b];\n\t\t\t\tcontinue;\t//oops, can't cope with mixed blend types\n\t\t\t}\n\n\t\t\tif (g->GetRawBones)\n\t\t\t{\n\t\t\t\tlerps->frac[l] = fs->lerpweight[b];\n\t\t\t\tlerps->needsfree[l] = BZ_Malloc(sizeof(float)*12*numbones);\n\t\t\t\tlerps->pose[l] = g->GetRawBones(inf, g, time, lerps->needsfree[l], boneinf, numbones);\n\t\t\t\tif (lerps->pose[l])\n\t\t\t\t\tl++;\n\t\t\t\telse\n\t\t\t\t\tdropweight += lerps->frac[l];\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (!g->numposes)\n\t\t\t{\n\t\t\t\tdropweight += fs->lerpweight[b];\n\t\t\t\tcontinue;\t//err...\n\t\t\t}\n\n\t\t\tmlerp = time*g->rate;\n\t\t\tframe1=floor(mlerp);\n\t\t\tframe2=frame1+1;\n\t\t\tmlerp-=frame1;\n\t\t\tif (g->loop)\n\t\t\t{\t//loop normally.\n\t\t\t\tframe1=frame1%g->numposes;\n\t\t\t\tframe2=frame2%g->numposes;\n\t\t\t\tif (frame1 < 0)\n\t\t\t\t\tframe1 += g->numposes;\n\t\t\t\tif (frame2 < 0)\n\t\t\t\t\tframe2 += g->numposes;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tframe1=bound(0, frame1, g->numposes-1);\n\t\t\t\tframe2=bound(0, frame2, g->numposes-1);\n\t\t\t}\n\n\t\t\tif (frame1 == frame2)\n\t\t\t\tmlerp = 0;\n\t\t\telse if (r_noframegrouplerp.ival)\n\t\t\t\tmlerp = (mlerp>0.5)?1:0;\n\t\t\tlerps->frac[l] = (1-mlerp)*fs->lerpweight[b];\n\t\t\tif (lerps->frac[l]>0)\n\t\t\t{\n\t\t\t\ttotalweight += lerps->frac[l];\n\t\t\t\tlerps->needsfree[l] = NULL;\n\t\t\t\tlerps->pose[l++] = (float*)g->boneofs + inf->numbones*12*frame1;\n\t\t\t}\n\t\t\tlerps->frac[l] = (mlerp)*fs->lerpweight[b];\n\t\t\tif (lerps->frac[l]>0)\n\t\t\t{\n\t\t\t\ttotalweight += lerps->frac[l];\n\t\t\t\tlerps->needsfree[l] = NULL;\n\t\t\t\tlerps->pose[l++] = (float*)g->boneofs + inf->numbones*12*frame2;\n\t\t\t}\n\t\t}\n\t}\n\n#ifndef SERVERONLY\n\tif (r_nolerp.ival && l > 1)\n\t{\t//when lerping is completely disabled, find the strongest influence\n\t\tframe1 = 0;\n\t\tmlerp = lerps->frac[0];\n\t\tfor (b = 1; b < l; b++)\n\t\t{\n\t\t\tif (lerps->frac[b] > mlerp)\n\t\t\t{\n\t\t\t\tframe1 = b;\n\t\t\t\tmlerp = lerps->frac[b];\n\t\t\t}\n\t\t}\n\t\tlerps->frac[0] = totalweight+dropweight;\n\t\tlerps->pose[0] = lerps->pose[frame1];\n\t\tl = 1;\n\t}\n\telse\n#endif\n\t\tif (l && totalweight && dropweight)\n\t{\t//don't rescale if some animation got dropped.\n\t\ttotalweight = (totalweight+dropweight) / totalweight;\n\t\tfor (b = 0; b < l; b++)\n\t\t{\n\t\t\tlerps->frac[b] *= totalweight;\n\t\t}\n\t}\n\n\tlerps->lerpcount = l;\n\treturn l > 0;\n}\n/*\nfinds the various blend info. returns number of bone blocks used.\n*/\nstatic int Alias_FindRawSkelData(galiasinfo_t *inf, const framestate_t *fstate, skellerps_t *lerps, size_t firstbone, size_t lastbone, const galiasbone_t *boneinfo)\n{\n\tint bonegroup;\n\tint cbone = 0;\n\tint endbone;\n\tint numbonegroups=0;\n\n\tif (lastbone > inf->numbones)\n\t\tlastbone = inf->numbones;\n\n\tfor (bonegroup = 0; bonegroup < FS_COUNT; bonegroup++)\n\t{\n\t\tendbone = fstate->g[bonegroup].endbone;\n\t\tif (bonegroup == FS_COUNT-1)\n\t\t\tendbone = MAX_BONES;\n\n\t\tif (cbone <= firstbone || endbone > lastbone)\n\t\t{\n\t\t\tlerps->firstbone = max(cbone, firstbone);\n\t\t\tlerps->endbone = max(lerps->firstbone, min(endbone, lastbone));\n\t\t\tif (lerps->firstbone == lerps->endbone)\n\t\t\t\tcontinue;\n\n\t\t\tif (!inf->numanimations || !Alias_BuildSkelLerps(lerps, &fstate->g[bonegroup], boneinfo, lerps->endbone, inf))\t//if there's no animations in this model, use the base pose instead.\n\t\t\t{\n\t\t\t\tif (!inf->baseframeofs)\n\t\t\t\t\tcontinue;\t//nope, not happening.\n\t\t\t\tlerps->skeltype = SKEL_ABSOLUTE;\n\t\t\t\tlerps->needsfree[0] = NULL;\n\t\t\t\tlerps->frac[0] = 1;\n\t\t\t\tlerps->pose[0] = inf->baseframeofs;\n\t\t\t\tlerps->lerpcount = 1;\n\t\t\t}\n\t\t\tnumbonegroups++;\n\t\t\tlerps++;\n\t\t}\n\t\tcbone = endbone;\n\t}\n\treturn numbonegroups;\n}\n/*\n\tretrieves the raw bone data for a current frame state.\n\tignores poses that don't match the desired skeltype\n\tignores skeletal objects.\n\treturn value is the lastbone argument, or less if the model simply doesn't have that many bones.\n\t_always_ writes into result\n*/\nstatic int Alias_BlendBoneData(galiasinfo_t *inf, const framestate_t *fstate, float *result, skeltype_t skeltype, int firstbone, int lastbone, const galiasbone_t *boneinfo)\n{\n\tskellerps_t lerps[FS_COUNT], *lerp;\n\tsize_t bone, endbone = 0;\n\tsize_t numgroups = Alias_FindRawSkelData(inf, fstate, lerps, firstbone, lastbone, boneinfo);\n\n\tfloat *pose, *matrix;\n\tint k, b;\n\n\tfor (lerp = lerps; numgroups--; lerp++)\n\t{\n\t\tif (lerp->skeltype == skeltype)\n\t\t{\n\t\t\tbone = lerp->firstbone;\n\t\t\tendbone = lerp->endbone;\n\t\t\tif (lerp->lerpcount == 1 && lerp->frac[0] == 1)\n\t\t\t\tmemcpy(result+bone*12, lerp->pose[0]+bone*12, (endbone-bone)*12*sizeof(float));\n\t\t\telse\n\t\t\t{\n\t\t\t\t//blend each influence\n\t\t\t\tfor (; bone < endbone; bone++)\n\t\t\t\t{\n\t\t\t\t\tpose = result + 12*bone;\n\t\t\t\t\t//set up the per-bone transform matrix\n\t\t\t\t\tmatrix = lerps->pose[0] + bone*12;\n\t\t\t\t\tfor (k = 0;k < 12;k++)\n\t\t\t\t\t\tpose[k] = matrix[k] * lerp->frac[0];\n\t\t\t\t\tfor (b = 1;b < lerp->lerpcount;b++)\n\t\t\t\t\t{\n\t\t\t\t\t\tmatrix = lerps->pose[b] + bone*12;\n\n\t\t\t\t\t\tfor (k = 0;k < 12;k++)\n\t\t\t\t\t\t\tpose[k] += matrix[k] * lerp->frac[b];\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tfor (k = 0; k < lerp->lerpcount; k++)\n\t\t\tBZ_Free(lerp->needsfree[k]);\n\t}\n\treturn endbone;\n}\n\n/*retrieves the bone data.\nonly writes targetbuffer if needed. the return value is the only real buffer result.\nassumes that all blended types are the same. probably buggy, but meh.\n*/\nstatic const float *Alias_GetBoneInformation(galiasinfo_t *inf, const framestate_t *framestate, skeltype_t targettype, float *targetbuffer, float *targetbufferalt, size_t numbones, const galiasbone_t *boneinfo)\n{\n\tskellerps_t lerps[FS_COUNT], *lerp;\n\tsize_t numgroups;\n\tsize_t bone, endbone;\n\tconst float *ret;\n\n\tlerps[0].skeltype = SKEL_IDENTITY; //just in case.\n#ifdef SKELETALOBJECTS\n\tif (framestate->bonestate && framestate->bonecount >= numbones)\n\t{\n\t\tlerps[0].skeltype = framestate->skeltype;\n\t\tlerps[0].firstbone = 0;\n\t\tlerps[0].endbone = framestate->bonecount;\n\t\tlerps[0].pose[0] = framestate->bonestate;\n\t\tlerps[0].frac[0] = 1;\n\t\tlerps[0].needsfree[0] = NULL;\n\t\tlerps[0].lerpcount = 1;\n\t\tnumgroups = 1;\n\t}\n\telse\n#endif\n\t{\n\t\tnumgroups = Alias_FindRawSkelData(inf, framestate, lerps, 0, numbones, boneinfo);\n\t}\n\n\t//try to return data in-place.\n\tif (numgroups==1 && lerps[0].lerpcount == 1)\n\t{\n\t\tret = Alias_ConvertBoneData(lerps[0].skeltype, lerps[0].pose[0], min(lerps[0].endbone, inf->numbones), inf->ofsbones, targettype, targetbuffer, targetbufferalt, numbones);\n\t\tif (ret == lerps[0].needsfree[0])\n\t\t{\t//bum\n\t\t\tmemcpy(targetbuffer, ret, sizeof(float)*min(lerps[0].endbone, numbones)*12);\n\t\t\tret = targetbuffer;\n\t\t}\n\t\tBZ_Free(lerps[0].needsfree[0]);\n\t\treturn ret;\n\t}\n\n\tfor (lerp = lerps; numgroups--; lerp++)\n\t{\n\t\tbone = lerp->firstbone;\n\t\tendbone = lerp->endbone;\n\t\tswitch(lerp->lerpcount)\n\t\t{\n\t\tcase 2:\n\t\t\t{\n\t\t\t\tint k;\n\t\t\t\tfloat *out = targetbuffer + bone*12;\n\t\t\t\tfloat *pose1 = lerp->pose[0] + bone*12, *pose2 = lerp->pose[1] + bone*12;\n\t\t\t\tfloat frac1 = lerp->frac[0], frac2 = lerp->frac[1];\n\t\t\t\tfor (; bone < endbone; bone++, out+=12, pose1+=12, pose2+=12)\n\t\t\t\t{\n\t\t\t\t\tfor (k = 0; k < 12; k++)\t//please please unroll!\n\t\t\t\t\t\tout[k] = (pose1[k]*frac1) + (frac2*pose2[k]);\n\t\t\t\t}\n\t\t\t\tBZ_Free(lerp->needsfree[0]);\n\t\t\t\tBZ_Free(lerp->needsfree[1]);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 3:\n\t\t\t{\n\t\t\t\tint k;\n\t\t\t\tfloat *out = targetbuffer + bone*12;\n\t\t\t\tfloat *pose1 = lerp->pose[0] + bone*12, *pose2 = lerp->pose[1] + bone*12, *pose3 = lerp->pose[2] + bone*12;\n\t\t\t\tfloat frac1 = lerp->frac[0], frac2 = lerp->frac[1], frac3 = lerp->frac[2];\n\t\t\t\tfor (; bone < endbone; bone++, out+=12, pose1+=12, pose2+=12, pose3+=12)\n\t\t\t\t{\n\t\t\t\t\tfor (k = 0; k < 12; k++)\t//please please unroll!\n\t\t\t\t\t\tout[k] = (pose1[k]*frac1) + (frac2*pose2[k]) + (pose3[k]*frac3);\n\t\t\t\t}\n\t\t\t\tBZ_Free(lerp->needsfree[0]);\n\t\t\t\tBZ_Free(lerp->needsfree[1]);\n\t\t\t\tBZ_Free(lerp->needsfree[2]);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 4:\n\t\t\t{\n\t\t\t\tint k;\n\t\t\t\tfloat *out = targetbuffer + bone*12;\n\t\t\t\tfloat *pose1 = lerp->pose[0] + bone*12, *pose2 = lerp->pose[1] + bone*12, *pose3 = lerp->pose[2] + bone*12, *pose4 = lerp->pose[3] + bone*12;\n\t\t\t\tfloat frac1 = lerp->frac[0], frac2 = lerp->frac[1], frac3 = lerp->frac[2], frac4 = lerp->frac[3];\n\t\t\t\tfor (; bone < endbone; bone++, out+=12, pose1+=12, pose2+=12, pose3+=12, pose4+=12)\n\t\t\t\t{\n\t\t\t\t\tfor (k = 0; k < 12; k++)\t//please please unroll!\n\t\t\t\t\t\tout[k] = (pose1[k]*frac1) + (frac2*pose2[k]) + (pose3[k]*frac3) + (frac4*pose4[k]);\n\t\t\t\t}\n\t\t\t\tBZ_Free(lerp->needsfree[0]);\n\t\t\t\tBZ_Free(lerp->needsfree[1]);\n\t\t\t\tBZ_Free(lerp->needsfree[2]);\n\t\t\t\tBZ_Free(lerp->needsfree[3]);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 0:\n\t\tcase 1:\t//the weight will usually be 1, which won't take this path.\n\t\tdefault:\n\t\t\t{\t//the generic slow path.\n\t\t\t\tint k, i, b;\n\t\t\t\tfloat *out, *pose, frac;\n\t\t\t\tfor (i = 0; i < lerp->lerpcount; i++)\n\t\t\t\t{\n\t\t\t\t\tout = targetbuffer + bone*12;\n\t\t\t\t\tpose = lerp->pose[i] + bone*12;\n\t\t\t\t\tfrac = lerp->frac[i];\n\t\t\t\t\tif (!i)\n\t\t\t\t\t{\t//first influence shouldn't add, saving us a memcpy.\n\t\t\t\t\t\tfor (b = bone; b < endbone; b++, out+=12, pose+=12)\n\t\t\t\t\t\t\tfor (k = 0; k < 12; k++)\n\t\t\t\t\t\t\t\tout[k] = (pose[k]*frac);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tfor (b = bone; b < endbone; b++, out+=12, pose+=12)\n\t\t\t\t\t\t\tfor (k = 0; k < 12; k++)\n\t\t\t\t\t\t\t\tout[k] += (pose[k]*frac);\n\t\t\t\t\t}\n\t\t\t\t\tBZ_Free(lerp->needsfree[i]);\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t}\n\n\treturn Alias_ConvertBoneData(lerps[0].skeltype, targetbuffer, inf->numbones, inf->ofsbones, targettype, targetbuffer, targetbufferalt, numbones);\n}\n\nstatic void Alias_BuildSkeletalMesh(mesh_t *mesh, framestate_t *framestate, galiasinfo_t *inf)\n{\n\tboneidx_t *fte_restrict bidx = inf->ofs_skel_idx[0];\n\tfloat *fte_restrict weight = inf->ofs_skel_weight[0];\n\tconst float *morphweights;\n\n\tif (meshcache.bonecachetype != SKEL_INVERSE_ABSOLUTE)\n\t\tmeshcache.usebonepose = Alias_GetBoneInformation(inf, framestate, meshcache.bonecachetype=SKEL_INVERSE_ABSOLUTE, meshcache.boneposebuffer1, meshcache.boneposebuffer2, inf->numbones, NULL);\n\n\tmorphweights = inf->AnimateMorphs?inf->AnimateMorphs(inf, framestate, alloca(sizeof(*morphweights)*inf->nummorphs)):NULL;\n\tif (morphweights)\n\t{\n\t\tsize_t m,v;\n\t\tfloat w;\n\t\tvecV_t *xyz = alloca(sizeof(*xyz)*inf->numverts), *inxyz;\n\t\tvec3_t *norm = alloca(sizeof(*norm)*inf->numverts), *innorm;\n\t\tvec3_t *sdir = alloca(sizeof(*sdir)*inf->numverts), *insdir;\n\t\tvec3_t *tdir = alloca(sizeof(*tdir)*inf->numverts), *intdir;\n\t\tmemcpy(xyz, inf->ofs_skel_xyz, sizeof(*xyz)*inf->numverts);\n\t\tmemcpy(norm, inf->ofs_skel_norm, sizeof(*norm)*inf->numverts);\n\t\tmemcpy(sdir, inf->ofs_skel_svect, sizeof(*sdir)*inf->numverts);\n\t\tmemcpy(tdir, inf->ofs_skel_tvect, sizeof(*tdir)*inf->numverts);\n\t\tfor (m = 0; m < inf->nummorphs; m++)\n\t\t{\n\t\t\tif (morphweights[m] <= 0)\n\t\t\t\tcontinue;\n\t\t\tinxyz = inf->ofs_skel_xyz + (m+1)*inf->numverts;\n\t\t\tinnorm = inf->ofs_skel_norm + (m+1)*inf->numverts;\n\t\t\tinsdir = inf->ofs_skel_svect + (m+1)*inf->numverts;\n\t\t\tintdir = inf->ofs_skel_tvect + (m+1)*inf->numverts;\n\t\t\tw = morphweights[m];\n\t\t\tfor (v = 0; v < inf->numverts; v++)\n\t\t\t{\n\t\t\t\tVectorMA(xyz[v], w, inxyz[v], xyz[v]);\n\t\t\t\tVectorMA(norm[v], w, innorm[v], norm[v]);\n\t\t\t\tVectorMA(sdir[v], w, insdir[v], sdir[v]);\n\t\t\t\tVectorMA(tdir[v], w, intdir[v], tdir[v]);\n\t\t\t}\n\t\t}\n\n\t\t//right, now do the bones thing.\n\t\tAlias_TransformVerticies_VNST(meshcache.usebonepose, inf->numverts, bidx, weight,\n\t\t\t\txyz[0], mesh->xyz_array[0],\n\t\t\t\tnorm[0], mesh->normals_array[0],\n\t\t\t\tsdir[0], mesh->snormals_array[0],\n\t\t\t\ttdir[0], mesh->tnormals_array[0]\n\t\t\t\t);\n\t}\n\telse if ((1))\n\t\tAlias_TransformVerticies_VNST(meshcache.usebonepose, inf->numverts, bidx, weight, \n\t\t\t\tinf->ofs_skel_xyz[0], mesh->xyz_array[0],\n\t\t\t\tinf->ofs_skel_norm[0], mesh->normals_array[0],\n\t\t\t\tinf->ofs_skel_svect[0], mesh->snormals_array[0],\n\t\t\t\tinf->ofs_skel_tvect[0], mesh->tnormals_array[0]\n\t\t\t\t);\n\telse\n\t\tAlias_TransformVerticies_VN(meshcache.usebonepose, inf->numverts, bidx, weight,\n\t\t\t\tinf->ofs_skel_xyz[0], mesh->xyz_array[0],\n\t\t\t\tinf->ofs_skel_norm[0], mesh->normals_array[0]\n\t\t\t\t);\n}\n\n#if defined(MD5MODELS) || defined(ZYMOTICMODELS) || defined(DPMMODELS)\nstatic int QDECL sortweights(const void *v1, const void *v2)\t//helper for Alias_BuildGPUWeights\n{\n\tconst galisskeletaltransforms_t *w1=v1, *w2=v2;\n\tif (w1->vertexindex - w2->vertexindex)\n\t\treturn w1->vertexindex - w2->vertexindex;\n\tif (w1->org[3] > w2->org[3])\n\t\treturn -1;\n\tif (w1->org[3] < w2->org[3])\n\t\treturn 1;\n\treturn 0;\n}\n//takes old-style vertex transforms and tries to generate something more friendly for GPUs, limited to only 4 influences per vertex\nstatic void Alias_BuildGPUWeights(model_t *mod, galiasinfo_t *inf, size_t num_trans, galisskeletaltransforms_t *trans, qboolean calcnorms)\n{\n\tsize_t i, j, v;\n\tdouble strength;\n\tconst float *matrix, *basepose;\n\tconst galisskeletaltransforms_t *t;\n\n\tfloat buffer[MAX_BONES*12];\n\tfloat bufferalt[MAX_BONES*12];\n\n\t//first sort the weights by the verticies, then by strength. this is probably already done, but whatever.\n\tqsort(trans, num_trans, sizeof(*trans), sortweights);\n\n\tinf->ofs_skel_xyz = ZG_Malloc(&mod->memgroup, sizeof(*inf->ofs_skel_xyz) * inf->numverts);\n\tinf->ofs_skel_norm = ZG_Malloc(&mod->memgroup, sizeof(*inf->ofs_skel_norm) * inf->numverts);\n\tinf->ofs_skel_svect = ZG_Malloc(&mod->memgroup, sizeof(*inf->ofs_skel_svect) * inf->numverts);\n\tinf->ofs_skel_tvect = ZG_Malloc(&mod->memgroup, sizeof(*inf->ofs_skel_tvect) * inf->numverts);\n\tinf->ofs_skel_idx = ZG_Malloc(&mod->memgroup, sizeof(*inf->ofs_skel_idx) * inf->numverts);\n\tinf->ofs_skel_weight = ZG_Malloc(&mod->memgroup, sizeof(*inf->ofs_skel_weight) * inf->numverts);\n\n\t//make sure we have a base pose for all animations to be relative to. first animation's first pose if there isn't an explicit one\n\tbasepose = inf->baseframeofs;\n\tif (!basepose)\n\t{\n\t\tif (!inf->numanimations || !inf->ofsanimations[0].boneofs)\n\t\t{\n\t\t\tCon_DPrintf(\"Alias_BuildGPUWeights: no base pose\\n\");\n\t\t\treturn;\t//its fucked jim\n\t\t}\n\t\tbasepose = Alias_ConvertBoneData(inf->ofsanimations[0].skeltype, inf->ofsanimations[0].boneofs, inf->numbones, inf->ofsbones, SKEL_ABSOLUTE, buffer, bufferalt, MAX_BONES);\n\t}\n\n\t//make sure we have bone inversions\n\tfor (i = 0; i < inf->numbones; i++)\n\t{\n\t\tMatrix3x4_Invert_Simple(basepose+i*12, inf->ofsbones[i].inverse);\n\t}\n\n\t//validate the indicies, because we can.\n\tfor (i = 0; i < inf->numindexes; i++)\n\t{\n\t\tif (inf->ofs_indexes[i] >= inf->numverts)\n\t\t\tCon_DPrintf(\"Alias_BuildGPUWeights: bad index\\n\");\n\t}\n\n\t//walk the (sorted) transforms and calculate the proper position for each vert, and the strongest influences too.\n\tfor (i = 0; i < num_trans; i++)\n\t{\n\t\tt = &trans[i];\n\t\tv = t->vertexindex;\n\t\tif (v >= inf->numverts || t->boneindex >= inf->numbones)\n\t\t{\n\t\t\tCon_DPrintf(\"Alias_BuildGPUWeights: bad vertex\\n\");\n\t\t\tcontinue;\n\t\t}\n\t\tmatrix = basepose + t->boneindex*12;\n\n\t\t//calculate the correct position in the base pose\n\t\tinf->ofs_skel_xyz[v][0] += t->org[0] * matrix[0] + t->org[1] * matrix[1] + t->org[2] * matrix[ 2] + t->org[3] * matrix[ 3];\n\t\tinf->ofs_skel_xyz[v][1] += t->org[0] * matrix[4] + t->org[1] * matrix[5] + t->org[2] * matrix[ 6] + t->org[3] * matrix[ 7];\n\t\tinf->ofs_skel_xyz[v][2] += t->org[0] * matrix[8] + t->org[1] * matrix[9] + t->org[2] * matrix[10] + t->org[3] * matrix[11];\n#ifndef SERVERONLY\n\t\tif (!calcnorms)\n\t\t{\n\t\t\tinf->ofs_skel_norm[v][0] += t->normal[0] * matrix[0] + t->normal[1] * matrix[1] + t->normal[2] * matrix[ 2];\n\t\t\tinf->ofs_skel_norm[v][1] += t->normal[0] * matrix[4] + t->normal[1] * matrix[5] + t->normal[2] * matrix[ 6];\n\t\t\tinf->ofs_skel_norm[v][2] += t->normal[0] * matrix[8] + t->normal[1] * matrix[9] + t->normal[2] * matrix[10];\n\t\t}\n#endif\n\n\t\t//we sorted them so we're guarenteed to see the highest influences first.\n\t\tfor (j = 0; j < 4; j++)\n\t\t{\n\t\t\tif (!inf->ofs_skel_weight[v][j])\n\t\t\t{\n\t\t\t\tinf->ofs_skel_weight[v][j] = t->org[3];\n\t\t\t\tfor (; j < 4; j++)\t//be nicer on cache, if necessary\n\t\t\t\t\tinf->ofs_skel_idx[v][j] = t->boneindex;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\t//weights should add up to 1, but might not if we had too many influences. make sure they don't move, at least in their base pose.\n\tfor (v = 0; v < inf->numverts; v++)\n\t{\n\t\tstrength = inf->ofs_skel_weight[v][0] + inf->ofs_skel_weight[v][1] + inf->ofs_skel_weight[v][2] + inf->ofs_skel_weight[v][3];\n\t\tif (strength && strength != 1)\n\t\t{\n\t\t\tstrength = 1/strength;\n\t\t\tVector4Scale(inf->ofs_skel_weight[v], strength, inf->ofs_skel_weight[v]);\n\t\t}\n\t}\n\n#ifndef SERVERONLY\n\tMod_AccumulateTextureVectors(inf->ofs_skel_xyz, inf->ofs_st_array, inf->ofs_skel_norm, inf->ofs_skel_svect, inf->ofs_skel_tvect, inf->ofs_indexes, inf->numindexes, calcnorms);\n\tMod_NormaliseTextureVectors(inf->ofs_skel_norm, inf->ofs_skel_svect, inf->ofs_skel_tvect, inf->numverts, calcnorms);\n#endif\n}\n#endif\n\n#ifndef SERVERONLY\nstatic void Alias_DrawSkeletalBones(galiasbone_t *bones, float const*bonepose, int bonecount, int basebone)\n{\n\tscenetris_t *t;\n\tint flags = BEF_NODLIGHT|BEF_NOSHADOWS|BEF_LINES;\n\tshader_t *shader;\n\tint i, p;\n\textern\tentity_t\t*currententity;\n\tindex_t *indexes;\n\tvecV_t *verts;\n\tvec4_t *colours;\n\tvec2_t *texcoords;\n\tint numindexes = 0;\n\tmesh_t bonemesh, *m;\n\tbatch_t b;\n\n\t//this shader lookup might get pricy.\n\tshader = R_RegisterShader(\"shader_draw_line\", SUF_NONE,\n\t\t\"{\\n\"\n\t\t\t\"program defaultfill\\n\"\n\t\t\t\"{\\n\"\n\t\t\t\t\"map $whiteimage\\n\"\n\t\t\t\t\"rgbgen exactvertex\\n\"\n\t\t\t\t\"alphagen vertex\\n\"\n\t\t\t\t\"blendfunc blend\\n\"\n\t\t\t\"}\\n\"\n\t\t\"}\\n\");\n\n\tif (cl_numstris && cl_stris[cl_numstris-1].shader == shader && cl_stris[cl_numstris-1].flags == flags)\n\t\tt = &cl_stris[cl_numstris-1];\n\telse\n\t{\n\t\tif (cl_numstris == cl_maxstris)\n\t\t{\n\t\t\tcl_maxstris += 8;\n\t\t\tcl_stris = BZ_Realloc(cl_stris, sizeof(*cl_stris)*cl_maxstris);\n\t\t}\n\t\tt = &cl_stris[cl_numstris++];\n\t\tt->shader = shader;\n\t\tt->numidx = 0;\n\t\tt->numvert = 0;\n\t\tt->firstidx = cl_numstrisidx;\n\t\tt->firstvert = cl_numstrisvert;\n\t\tt->flags = flags;\n\t}\n\tif (cl_numstrisvert + bonecount*2 > cl_maxstrisvert)\n\t\tcl_stris_ExpandVerts(cl_numstrisvert + bonecount*2);\n\tif (cl_maxstrisidx < cl_numstrisidx+bonecount*2)\n\t{\n\t\tcl_maxstrisidx = cl_numstrisidx+bonecount*2;\n\t\tcl_strisidx = BZ_Realloc(cl_strisidx, sizeof(*cl_strisidx)*cl_maxstrisidx);\n\t}\n\n\tverts = alloca(sizeof(*verts)*bonecount);\n\tcolours = alloca(sizeof(*colours)*bonecount);\n\ttexcoords = alloca(sizeof(*texcoords)*bonecount);\n\tindexes = alloca(sizeof(*indexes)*bonecount*2);\n\tnumindexes = 0;\n\n\tfor (i = 0; i < bonecount; i++)\n\t{\n\t\t//fixme: transform by model matrix\n\t\tverts[i][0] = bonepose[i*12+3];\n\t\tverts[i][1] = bonepose[i*12+7];\n\t\tverts[i][2] = bonepose[i*12+11];\n\t\ttexcoords[i][0] = 0;\n\t\ttexcoords[i][1] = 0;\n\t\tcolours[i][0] = (i < basebone)?0:1;\n\t\tcolours[i][1] = (i < basebone)?0:0;\n\t\tcolours[i][2] = (i < basebone)?1:0;\n\t\tcolours[i][3] = 1;\n\n\t\tp = bones[i].parent;\n\t\tif (p < 0)\n\t\t\tp = i;\n\t\tindexes[numindexes++] = i;\n\t\tindexes[numindexes++] = p;\n\t}\n\n\tmemset(&bonemesh, 0, sizeof(bonemesh));\n\tbonemesh.indexes = indexes;\n\tbonemesh.st_array = texcoords;\n\tbonemesh.lmst_array[0] = texcoords;\n\tbonemesh.xyz_array = verts;\n\tbonemesh.colors4f_array[0] = colours;\n\tbonemesh.numindexes = numindexes;\n\tbonemesh.numvertexes = bonecount;\n\tm = &bonemesh;\n\n//FIXME: We should use the skybox clipping code and split the sphere into 6 sides.\n\tmemset(&b, 0, sizeof(b));\n\tb.flags = flags;\n\tb.meshes = 1;\n\tb.firstmesh = 0;\n\tb.mesh = &m;\n\tb.ent = currententity;\n\tb.shader = shader;\n\tb.skin = NULL;\n\tb.texture = NULL;\n\tb.vbo = NULL;\n\tBE_SubmitBatch(&b);\n}\n#endif\t//!SERVERONLY\n#endif\t//SKELETALMODELS\n\nvoid Alias_FlushCache(void)\n{\n\tmeshcache.ent = NULL;\n}\n\nvoid Alias_Shutdown(void)\n{\n\tif (meshcache.norm)\n\t\tBZ_Free(meshcache.norm);\n\tmeshcache.norm = NULL;\n\tmeshcache.numnorm = 0;\n\n\tif (meshcache.coords)\n\t\tBZ_Free(meshcache.coords);\n\tmeshcache.coords = NULL;\n\tmeshcache.numcoords = 0;\n}\n\nqboolean Alias_GAliasBuildMesh(mesh_t *mesh, vbo_t **vbop, galiasinfo_t *inf, int surfnum, entity_t *e, qboolean usebones)\n{\n#ifndef SERVERONLY\n\textern cvar_t r_nolerp;\n#endif\n\n#ifdef SKELETALMODELS\n\tqboolean bytecolours = false;\n#endif\n\n\tif (!inf->numanimations)\n\t{\n#ifdef SKELETALMODELS\n\t\tif (inf->ofs_skel_xyz)\n\t\t{}\n\t\telse\n#endif\n\t\t{\n\t\t\tCon_DPrintf(\"Model with no frames (%s)\\n\", e->model->name);\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tif (meshcache.numnorm < inf->numverts)\n\t{\n\t\tif (meshcache.norm)\n\t\t\tBZ_Free(meshcache.norm);\n\t\tmeshcache.norm = BZ_Malloc(sizeof(*meshcache.norm)*inf->numverts*3);\n\t\tmeshcache.numnorm = inf->numverts;\n\t}\n\tif (meshcache.numcoords < inf->numverts)\n\t{\n\t\tif (meshcache.coords)\n\t\t\tBZ_Free(meshcache.coords);\n\t\tmeshcache.coords = BZ_Malloc(sizeof(*meshcache.coords)*inf->numverts);\n\t\tmemset(meshcache.coords, 0, sizeof(*meshcache.coords)*inf->numverts);\t//vecV_t is often uninitialised.\n\t\tmeshcache.numcoords = inf->numverts;\n\t}\n\n\tmesh->numvertexes = inf->numverts;\n\tmesh->indexes = inf->ofs_indexes;\n\tmesh->numindexes = inf->numindexes;\n\tmesh->numbones = 0;\n\n#ifndef SERVERONLY\n\tmesh->colors4f_array[0] = inf->ofs_rgbaf;\n\tmesh->colors4b_array = inf->ofs_rgbaub;\n#ifdef SKELETALMODELS\n\tbytecolours = !!inf->ofs_rgbaub;\n#endif\n\tmesh->st_array = inf->ofs_st_array;\n\tmesh->lmst_array[0] = inf->ofs_lmst_array; //some formats allow for two.\n#endif\n\tmesh->trneighbors = inf->ofs_trineighbours;\n\n#if defined(SKELETALMODELS) && !defined(SERVERONLY)\n\tif (!inf->numbones)\n\t\tusebones = false;\n\telse if (inf->ofs_skel_xyz && !inf->ofs_skel_weight)\n\t\tusebones = false;\n\telse if (e->fatness || !inf->ofs_skel_idx || (!inf->mappedbones && inf->numbones > sh_config.max_gpu_bones) || inf->nummorphs)\n#endif\n\t\tusebones = false;\n\n\tif (meshcache.ent == e)\n\t{\n\t\tif (meshcache.vertgroup == inf->shares_verts && meshcache.ent == e && usebones == meshcache.usebones)\n\t\t{\n\t\t\tmesh->xyz_blendw[0] = meshcache.lerp;\n\t\t\tmesh->xyz_blendw[1] = 1-meshcache.lerp;\n\t\t\tmesh->xyz_array = meshcache.acoords1;\n\t\t\tmesh->xyz2_array = meshcache.acoords2;\n\t\t\tmesh->normals_array = meshcache.anorm;\n\t\t\tmesh->snormals_array = meshcache.anorms;\n\t\t\tmesh->tnormals_array = meshcache.anormt;\n\t\t\tif (vbop)\n\t\t\t{\n\t\t\t\t*vbop = meshcache.vbop;\n\t\t\t\tmeshcache.vbo.indicies = inf->vboindicies;\n\t\t\t\tmeshcache.vbo.indexcount = inf->numindexes;\n\t\t\t}\n\n#ifndef SKELETALMODELS\n\t\t\treturn false;\n\t\t}\n\t}\n#else\n\t\t\tif (usebones)\n\t\t\t{\n\t\t\t\tmesh->bonenums = inf->ofs_skel_idx;\n\t\t\t\tmesh->boneweights = inf->ofs_skel_weight;\n\t\t\t\tmesh->bones = meshcache.usebonepose;\n\t\t\t\tmesh->numbones = inf->numbones;\n\t\t\t}\t\n#ifndef SERVERONLY\n\t\t\tif (meshcache.bonemap != inf->bonemap)\n\t\t\t{\n\t\t\t\tmeshcache.bonemap = inf->bonemap;\n\t\t\t\tBE_SelectEntity(e);\n\t\t\t}\n\t\t\tif (inf->mappedbones)\n\t\t\t{\n\t\t\t\tint i;\n\t\t\t\tfor (i = 0; i < inf->mappedbones; i++)\n\t\t\t\t\tmemcpy(meshcache.gpubones + i*12, meshcache.usebonepose + inf->bonemap[i]*12, sizeof(float)*12);\n\t\t\t\tmeshcache.vbo.numbones = inf->mappedbones;\n\t\t\t\tmeshcache.vbo.bones = meshcache.gpubones;\n\t\t\t}\n#endif\n\t\t\treturn false;\t//don't generate the new vertex positions. We still have them all.\n\t\t}\n\t\tif (meshcache.bonegroup != inf->shares_bones)\n\t\t{\n\t\t\tmeshcache.usebonepose = NULL;\n\t\t\tmeshcache.bonecachetype = -1;\n\t\t}\n\t}\n\telse\n\t{\n\t\tmeshcache.usebonepose = NULL;\n\t\tmeshcache.bonecachetype = -1;\n\t}\n\tmeshcache.bonegroup = inf->shares_bones;\n#endif\n\tmeshcache.vertgroup = inf->shares_verts;\n\tmeshcache.ent = e;\n\n\n#if defined(_DEBUG) && FRAME_BLENDS == 4\n\tif (!e->framestate.g[FS_REG].lerpweight[0] && !e->framestate.g[FS_REG].lerpweight[1] && !e->framestate.g[FS_REG].lerpweight[2] && !e->framestate.g[FS_REG].lerpweight[3])\n\t\tCon_Printf(\"Entity with no lerp info\\n\");\n#endif\n\n\n#ifndef SERVERONLY\n\tmesh->trneighbors = inf->ofs_trineighbours;\n\tmesh->normals_array = meshcache.norm;\n\tmesh->snormals_array = meshcache.norm+meshcache.numnorm;\n\tmesh->tnormals_array = meshcache.norm+meshcache.numnorm*2;\n#endif\n\tmesh->xyz_array = meshcache.coords;\n\n//we don't support meshes with one pose skeletal and another not.\n//we don't support meshes with one group skeletal and another not.\n\n#ifdef SKELETALMODELS\n\tmeshcache.vbop = NULL;\n\tif (vbop)\n\t\t*vbop = NULL;\n\tif (inf->ofs_skel_xyz && !inf->ofs_skel_weight)\n\t{\n\t\t//if we have skeletal xyz info, but no skeletal weights, then its a partial model that cannot possibly be animated.\n\t\tmeshcache.usebonepose = NULL;\n\t\tmeshcache.bonecachetype = -1;\n\t\tmesh->xyz_array = inf->ofs_skel_xyz;\n\t\tmesh->xyz2_array = NULL;\n\t\tmesh->normals_array = inf->ofs_skel_norm;\n\t\tmesh->snormals_array = inf->ofs_skel_svect;\n\t\tmesh->tnormals_array = inf->ofs_skel_tvect;\n\n\t\tif (vbop)\n\t\t{\n\t\t\tmeshcache.vbo.indicies = inf->vboindicies;\n\t\t\tmeshcache.vbo.indexcount = inf->numindexes;\n\t\t\tmeshcache.vbo.vertcount = inf->numverts;\n\t\t\tmeshcache.vbo.texcoord = inf->vbotexcoords;\n\t\t\tmeshcache.vbo.lmcoord[0] = inf->vbolmtexcoords;\n\t\t\tmeshcache.vbo.coord = inf->vbo_skel_verts;\n\t\t\tmemset(&meshcache.vbo.coord2, 0, sizeof(meshcache.vbo.coord2));\n\t\t\tmeshcache.vbo.normals = inf->vbo_skel_normals;\n\t\t\tmeshcache.vbo.svector = inf->vbo_skel_svector;\n\t\t\tmeshcache.vbo.tvector = inf->vbo_skel_tvector;\n\t\t\tmeshcache.vbo.colours[0] = inf->vborgba;\n\t\t\tmeshcache.vbo.colours_bytes = bytecolours;\n\t\t\tmemset(&meshcache.vbo.bonenums, 0, sizeof(meshcache.vbo.bonenums));\n\t\t\tmemset(&meshcache.vbo.boneweights, 0, sizeof(meshcache.vbo.boneweights));\n\t\t\tmeshcache.vbo.numbones = 0;\n\t\t\tmeshcache.vbo.bones = NULL;\n\t\t\tif (meshcache.vbo.indicies.sysptr)\n\t\t\t\t*vbop = meshcache.vbop = &meshcache.vbo;\n\t\t}\n\t}\n\telse if (inf->numbones)\n\t{\n\t\tmesh->xyz2_array = NULL;\t//skeltal animations blend bones, not verticies.\n\n\t\tif (!usebones)\n\t\t{\n\t\t\tif (inf->numindexes)\n\t\t\t{\n\t\t\t\t//software bone animation\n\t\t\t\t//there are two ways to animate a skeleton\n\t\t\t\tAlias_BuildSkeletalMesh(mesh, &e->framestate, inf);\n\n#ifdef PEXT_FATNESS\n\t\t\t\tif (e->fatness)\n\t\t\t\t{\n\t\t\t\t\tint i;\n\t\t\t\t\tfor (i = 0; i < mesh->numvertexes; i++)\n\t\t\t\t\t{\n\t\t\t\t\t\tVectorMA(mesh->xyz_array[i], e->fatness, mesh->normals_array[i], meshcache.coords[i]);\n\t\t\t\t\t}\n\t\t\t\t\tmesh->xyz_array = meshcache.coords;\n\t\t\t\t}\n#endif\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (meshcache.bonecachetype != SKEL_ABSOLUTE)\n\t\t\t\t\tmeshcache.usebonepose = Alias_GetBoneInformation(inf, &e->framestate, meshcache.bonecachetype=SKEL_ABSOLUTE, meshcache.boneposebuffer1, meshcache.boneposebuffer2, inf->numbones, NULL);\n#ifndef SERVERONLY\n\t\t\t\tif (inf->shares_bones != surfnum && qrenderer)\n\t\t\t\t\tAlias_DrawSkeletalBones(inf->ofsbones, (const float *)meshcache.usebonepose, inf->numbones, e->framestate.g[0].endbone);\n#endif\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (meshcache.bonecachetype != SKEL_INVERSE_ABSOLUTE)\n\t\t\t\tmeshcache.usebonepose = Alias_GetBoneInformation(inf, &e->framestate, meshcache.bonecachetype=SKEL_INVERSE_ABSOLUTE, meshcache.boneposebuffer1, meshcache.boneposebuffer2, inf->numbones, NULL);\n\n\t\t\t//hardware bone animation\n\t\t\tmesh->xyz_array = inf->ofs_skel_xyz;\n\t\t\tmesh->normals_array = inf->ofs_skel_norm;\n\t\t\tmesh->snormals_array = inf->ofs_skel_svect;\n\t\t\tmesh->tnormals_array = inf->ofs_skel_tvect;\n\n\t\t\tif (vbop)\n\t\t\t{\n\t\t\t\tmeshcache.vbo.indicies = inf->vboindicies;\n\t\t\t\tmeshcache.vbo.indexcount = inf->numindexes;\n\t\t\t\tmeshcache.vbo.vertcount = inf->numverts;\n\t\t\t\tmeshcache.vbo.texcoord = inf->vbotexcoords;\n\t\t\t\tmeshcache.vbo.lmcoord[0] = inf->vbolmtexcoords;\n\t\t\t\tmeshcache.vbo.coord = inf->vbo_skel_verts;\n\t\t\t\tmemset(&meshcache.vbo.coord2, 0, sizeof(meshcache.vbo.coord2));\n\t\t\t\tmeshcache.vbo.normals = inf->vbo_skel_normals;\n\t\t\t\tmeshcache.vbo.svector = inf->vbo_skel_svector;\n\t\t\t\tmeshcache.vbo.tvector = inf->vbo_skel_tvector;\n\t\t\t\tmeshcache.vbo.colours[0] = inf->vborgba;\n\t\t\t\tmeshcache.vbo.colours_bytes = bytecolours;\n\t\t\t\tmeshcache.vbo.bonenums = inf->vbo_skel_bonenum;\n\t\t\t\tmeshcache.vbo.boneweights = inf->vbo_skel_bweight;\n\t\t\t\tmeshcache.vbo.numbones = inf->numbones;\n\t\t\t\tmeshcache.vbo.bones = meshcache.usebonepose;\n\t\t\t\tif (meshcache.vbo.indicies.sysptr)\n\t\t\t\t\t*vbop = meshcache.vbop = &meshcache.vbo;\n\t\t\t}\n\t\t}\n\t}\n\telse\n#endif\n\t{\n#ifndef NONSKELETALMODELS\n\t\tmemset(mesh, 0, sizeof(*mesh));\n\t\t*vbop = NULL;\n\t\treturn false;\n#else\n#ifndef SERVERONLY\n\t\tfloat lerpcutoff;\n#endif\n\t\tgaliasanimation_t *g1, *g2;\n\t\tint frame1;\n\t\tint frame2;\n\t\tfloat lerp;\n\t\tfloat fg1time;\n\t\t//float fg2time;\n\t\tstatic float printtimer;\n\n#if defined(_DEBUG) && FRAME_BLENDS != 2\n\t\tif (e->framestate.g[FS_REG].lerpweight[2] || e->framestate.g[FS_REG].lerpweight[3])\n\t\t\tCon_ThrottlePrintf(&printtimer, 1, \"Alias_GAliasBuildMesh(%s): non-skeletal animation only supports two animations\\n\", e->model->name);\n#endif\n\n\t\t//FIXME: replace most of this logic with Alias_BuildSkelLerps\n\n\t\tframe1 = e->framestate.g[FS_REG].frame[0];\n\t\tframe2 = e->framestate.g[FS_REG].frame[1];\n\t\tlerp = e->framestate.g[FS_REG].lerpweight[1];\t//FIXME\n\t\tfg1time = e->framestate.g[FS_REG].frametime[0];\n\t\t//fg2time = e->framestate.g[FS_REG].frametime[1];\n\n\t\tif (frame1 < 0)\n\t\t{\n\t\t\tCon_ThrottlePrintf(&printtimer, 1, \"Negative frame (%s)\\n\", e->model->name);\n\t\t\tframe1 = 0;\n\t\t}\n\t\tif (frame2 < 0)\n\t\t{\n\t\t\tCon_ThrottlePrintf(&printtimer, 1, \"Negative frame (%s)\\n\", e->model->name);\n\t\t\tframe2 = frame1;\n\t\t}\n\t\tif (frame1 >= inf->numanimations)\n\t\t{\n\t\t\tCon_ThrottlePrintf(&printtimer, 1, \"Too high frame %i (%s)\\n\", frame1, e->model->name);\n\t\t\tframe1 %= inf->numanimations;\n\t\t}\n\t\tif (frame2 >= inf->numanimations)\n\t\t{\n \t\t\tCon_ThrottlePrintf(&printtimer, 1, \"Too high frame %i (%s)\\n\", frame2, e->model->name);\n\t\t\tframe2 %= inf->numanimations;\n\t\t}\n\n\t\tif (lerp <= 0)\n\t\t\tframe2 = frame1;\n\t\telse  if (lerp >= 1)\n\t\t\tframe1 = frame2;\n\n\t\tg1 = &inf->ofsanimations[frame1];\n\t\tg2 = &inf->ofsanimations[frame2];\n\n\t\tif (!inf->numanimations || !g1->numposes || !g2->numposes)\n\t\t{\n\t\t\tCon_ThrottlePrintf(&printtimer, 1, \"Invalid animation data on entity with model %s\\n\", e->model->name);\n\t\t\t//no animation data. panic!\n\t\t\tmemset(mesh, 0, sizeof(*mesh));\n\t\t\t*vbop = NULL;\n\t\t\treturn false;\n\t\t}\n\n\t\tif (g1 == g2)\t//lerping within group is only done if not changing group\n\t\t{\n\t\t\tlerp = fg1time*g1->rate;\n\t\t\tframe1=floor(lerp);\n\t\t\tframe2=frame1+1;\n\t\t\tlerp-=frame1;\n\t\t\tif (r_noframegrouplerp.ival || (e->model->engineflags&MDLF_NOLERP))\n\t\t\t\tlerp = 0;\n\t\t\tif (g1->loop)\n\t\t\t{\n\t\t\t\tframe1=frame1%g1->numposes;if (frame1 < 0)frame1 += g1->numposes;\n\t\t\t\tframe2=frame2%g1->numposes;if (frame2 < 0)frame2 += g1->numposes;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tframe1=bound(0, frame1, g1->numposes-1);\n\t\t\t\tframe2=bound(0, frame2, g1->numposes-1);\n\t\t\t}\n\t\t}\n\t\telse\t//don't bother with a four way lerp. Yeah, this will produce jerkyness with models with just framegroups.\n\t\t{\n\t\t\tif (e->model->engineflags&MDLF_NOLERP)\n\t\t\t{\n\t\t\t\tif (lerp > 0.5)\n\t\t\t\t\tg2 = g1;\n\t\t\t\telse\n\t\t\t\t\tg1 = g2;\n\t\t\t}\n\t\t\t//FIXME: find the two poses with the strongest influence.\n\t\t\tframe1=0;\n\t\t\tframe2=0;\n\t\t}\n\n#ifndef SERVERONLY\n\t\tlerpcutoff = inf->lerpcutoff * r_lerpmuzzlehack.value;\n\t\tif (Sh_StencilShadowsActive() || e->fatness || lerpcutoff)\n\t\t{\n\t\t\tmemset(&meshcache.vbo.coord2, 0, sizeof(meshcache.vbo.coord2));\n\t\t\tmesh->xyz2_array = NULL;\n\t\t\tmesh->xyz_blendw[0] = 1;\n\t\t\tmesh->xyz_blendw[1] = 0;\n\t\t\tR_LerpFrames(mesh,\t&g1->poseofs[frame1], &g2->poseofs[frame2], 1-lerp, e->fatness, lerpcutoff);\n\t\t}\n\t\telse\n#endif\n\t\t{\n\t\t\tgaliaspose_t *p1 = &g1->poseofs[frame1];\n#ifndef SERVERONLY\n\t\t\tgaliaspose_t *p2 = &g2->poseofs[frame2];\n#endif\n\n\t\t\tmeshcache.vbo.indicies = inf->vboindicies;\n\t\t\tmeshcache.vbo.indexcount = inf->numindexes;\n\t\t\tmeshcache.vbo.vertcount = inf->numverts;\n\t\t\tmeshcache.vbo.texcoord = inf->vbotexcoords;\n\t\t\tmeshcache.vbo.lmcoord[0] = inf->vbolmtexcoords;\n\n#ifdef SKELETALMODELS\n\t\t\tmemset(&meshcache.vbo.bonenums, 0, sizeof(meshcache.vbo.bonenums));\n\t\t\tmemset(&meshcache.vbo.boneweights, 0, sizeof(meshcache.vbo.boneweights));\n\t\t\tmeshcache.vbo.numbones = 0;\n\t\t\tmeshcache.vbo.bones = 0;\n#endif\n\n#ifdef SERVERONLY\n\t\t\tmesh->xyz_array = p1->ofsverts;\n\t\t\tmesh->xyz2_array = NULL;\n#else\n\t\t\tmesh->normals_array = p1->ofsnormals;\n\t\t\tmesh->snormals_array = p1->ofssvector;\n\t\t\tmesh->tnormals_array = p1->ofstvector;\n\n\t\t\tmeshcache.vbo.normals = p1->vbonormals;\n\t\t\tmeshcache.vbo.svector = p1->vbosvector;\n\t\t\tmeshcache.vbo.tvector = p1->vbotvector;\n\t\t\tmemset(&meshcache.vbo.colours[0], 0, sizeof(meshcache.vbo.colours[0]));\n\n\t\t\tif (p1 == p2 || r_nolerp.ival)\n\t\t\t{\n\t\t\t\tmeshcache.vbo.coord = p1->vboverts;\n\t\t\t\tmemset(&meshcache.vbo.coord2, 0, sizeof(meshcache.vbo.coord2));\n\t\t\t\tmesh->xyz_array = p1->ofsverts;\n\t\t\t\tmesh->xyz2_array = NULL;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tmeshcache.vbo.coord = p1->vboverts;\n\t\t\t\tmeshcache.vbo.coord2 = p2->vboverts;\n\t\t\t\tmesh->xyz_blendw[0] = 1-lerp;\n\t\t\t\tmesh->xyz_blendw[1] = lerp;\n\t\t\t\tmesh->xyz_array = p1->ofsverts;\n\t\t\t\tmesh->xyz2_array = p2->ofsverts;\n\t\t\t}\n#endif\n\t\t\tif (vbop && meshcache.vbo.indicies.sysptr)\n\t\t\t\t*vbop = meshcache.vbop = &meshcache.vbo;\n\t\t}\n#endif\n\t}\n\n\tmeshcache.vbo.vao = 0;\n\tmeshcache.vbo.vaodynamic = ~0;\n\tmeshcache.vbo.vaoenabled = 0;\n\tmeshcache.acoords1 = mesh->xyz_array;\n\tmeshcache.acoords2 = mesh->xyz2_array;\n\tmeshcache.anorm = mesh->normals_array;\n\tmeshcache.anorms = mesh->snormals_array;\n\tmeshcache.anormt = mesh->tnormals_array;\n\tmeshcache.lerp = mesh->xyz_blendw[0];\n\tif (vbop)\n\t\tmeshcache.vbop = *vbop;\n\n#ifdef SKELETALMODELS\n\tmeshcache.usebones = usebones;\n\tif (usebones)\n\t{\n\t\tmesh->bonenums = inf->ofs_skel_idx;\n\t\tmesh->boneweights = inf->ofs_skel_weight;\n\t\tmesh->bones = meshcache.usebonepose;\n\t\tmesh->numbones = inf->numbones;\n#ifndef SERVERONLY\n\t\tif (meshcache.bonemap != inf->bonemap)\n\t\t{\n\t\t\tmeshcache.bonemap = inf->bonemap;\n\t\t\tBE_SelectEntity(e);\n\t\t}\n\t\tif (inf->mappedbones)\n\t\t{\n\t\t\tint i;\n\t\t\tfor (i = 0; i < inf->mappedbones; i++)\n\t\t\t\tmemcpy(meshcache.gpubones + i*12, meshcache.usebonepose + inf->bonemap[i]*12, sizeof(float)*12);\n\t\t\tmeshcache.vbo.numbones = inf->mappedbones;\n\t\t\tmeshcache.vbo.bones = meshcache.gpubones;\n\t\t}\n#endif\n\t}\n#endif\n\n\treturn true;\t//to allow the mesh to be dlighted.\n}\n\n#ifndef SERVERONLY\n//used by the modelviewer.\n//mode 0: wireframe\n//mode 1: normal pegs\n//mode 2: 2d projection.\nvoid Mod_AddSingleSurface(entity_t *ent, int surfaceidx, shader_t *shader, int mode)\n{\n\tscenetris_t *t;\n\tvecV_t *posedata = NULL;\n\tvec3_t *normdata = NULL, tmp;\n\tint i, j;\n\n\tbatch_t *batches[SHADER_SORT_COUNT], *b;\n\tint s;\n\tmesh_t *m;\n\tunsigned int meshidx;\n\n\tif (!ent->model || ent->model->loadstate != MLS_LOADED)\n\t\treturn;\n\n\tmemset(batches, 0, sizeof(batches));\n\tr_refdef.frustum_numplanes = 0;\n\tswitch(ent->model->type)\n\t{\n\tcase mod_alias:\n\t\tR_GAlias_GenerateBatches(ent, batches);\n\t\tbreak;\n\n#ifdef HALFLIFEMODELS\n\tcase mod_halflife:\n\t\tR_HalfLife_GenerateBatches(ent, batches);\n\t\tbreak;\n#endif\n\tdefault:\n\t\treturn;\n\t}\n\n\tfor (s = 0; s < countof(batches); s++)\n\t{\n\t\tif (!batches[s])\n\t\t\tcontinue;\n\t\tfor (b = batches[s]; b; b = b->next)\n\t\t{\n\t\t\tif (b->buildmeshes)\n\t\t\t\tb->buildmeshes(b);\n\n\t\t\tfor (meshidx = b->firstmesh; meshidx < b->meshes; meshidx++)\n\t\t\t{\n\t\t\t\tif (surfaceidx < 0)\n\t\t\t\t{\t//only draw meshes that have an actual contents value (collision data)\n\t\t\t\t\t//FIXME: implement.\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\t//only draw the mesh that's actually selected.\n\t\t\t\t\tif (b->user.alias.surfrefs[meshidx] != surfaceidx)\n\t\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tm = b->mesh[meshidx];\n\n\t\t\t\tif (mode == 2 && m->st_array)\n\t\t\t\t{\t//2d wireframe (using texture coords instead of modelspace)\n\t\t\t\t\tif (cl_numstris == cl_maxstris)\n\t\t\t\t\t{\n\t\t\t\t\t\tcl_maxstris+=8;\n\t\t\t\t\t\tcl_stris = BZ_Realloc(cl_stris, sizeof(*cl_stris)*cl_maxstris);\n\t\t\t\t\t}\n\t\t\t\t\tt = &cl_stris[cl_numstris++];\n\t\t\t\t\tt->shader = shader;\n\t\t\t\t\tt->flags = BEF_LINES;\n\t\t\t\t\tt->firstidx = cl_numstrisidx;\n\t\t\t\t\tt->firstvert = cl_numstrisvert;\n\t\t\t\t\tif (t->flags&BEF_LINES)\n\t\t\t\t\t\tt->numidx = m->numindexes*2;\n\t\t\t\t\telse\n\t\t\t\t\t\tt->numidx = m->numindexes;\n\t\t\t\t\tt->numvert = m->numvertexes;\n\n\t\t\t\t\tif (cl_numstrisidx+t->numidx > cl_maxstrisidx)\n\t\t\t\t\t{\n\t\t\t\t\t\tcl_maxstrisidx=cl_numstrisidx+t->numidx;\n\t\t\t\t\t\tcl_strisidx = BZ_Realloc(cl_strisidx, sizeof(*cl_strisidx)*cl_maxstrisidx);\n\t\t\t\t\t}\n\t\t\t\t\tif (cl_numstrisvert+m->numvertexes > cl_maxstrisvert)\n\t\t\t\t\t\tcl_stris_ExpandVerts(cl_numstrisvert+m->numvertexes);\n\t\t\t\t\tfor (i = 0; i < m->numvertexes; i++)\n\t\t\t\t\t{\n\t\t\t\t\t\tVectorMA(vec3_origin,\tm->st_array[i][0],\tent->axis[0], tmp);\n\t\t\t\t\t\tVectorMA(tmp,\t\t\tm->st_array[i][1],\tent->axis[1], tmp);\n\t\t\t\t\t\tVectorMA(tmp,\t\t\t0,\t\t\t\t\tent->axis[2], tmp);\n\t\t\t\t\t\tVectorMA(ent->origin,\tent->scale,\t\t\ttmp,\t\t  cl_strisvertv[t->firstvert+i]);\n\n\t\t\t\t\t\tVector2Set(cl_strisvertt[t->firstvert+i], 0.5, 0.5);\n\t\t\t\t\t\tVector4Set(cl_strisvertc[t->firstvert+i], 1, 1, 1, 0.1);\n\t\t\t\t\t}\n\t\t\t\t\tif (t->flags&BEF_LINES)\n\t\t\t\t\t{\n\t\t\t\t\t\tfor (i = 0; i < m->numindexes; i+=3)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcl_strisidx[cl_numstrisidx++] = m->indexes[i+0];\n\t\t\t\t\t\t\tcl_strisidx[cl_numstrisidx++] = m->indexes[i+1];\n\t\t\t\t\t\t\tcl_strisidx[cl_numstrisidx++] = m->indexes[i+1];\n\t\t\t\t\t\t\tcl_strisidx[cl_numstrisidx++] = m->indexes[i+2];\n\t\t\t\t\t\t\tcl_strisidx[cl_numstrisidx++] = m->indexes[i+2];\n\t\t\t\t\t\t\tcl_strisidx[cl_numstrisidx++] = m->indexes[i+0];\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tfor (i = 0; i < m->numindexes; i++)\n\t\t\t\t\t\t\tcl_strisidx[cl_numstrisidx+i] = m->indexes[i];\n\t\t\t\t\t\tcl_numstrisidx += m->numindexes;\n\t\t\t\t\t}\n\t\t\t\t\tcl_numstrisvert += m->numvertexes;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tposedata = m->xyz_array;\n\t\t\t\tnormdata = (mode==1)?m->normals_array:NULL;\n#ifdef SKELETALMODELS\n\t\t\t\tif (m->numbones)\n\t\t\t\t{\t//intended shader might have caused it to use skeletal stuff.\n\t\t\t\t\t//we're too lame for that though.\n\t\t\t\t\tposedata = alloca(m->numvertexes*sizeof(vecV_t));\n\t\t\t\t\tif (normdata)\n\t\t\t\t\t{\n\t\t\t\t\t\tnormdata = alloca(m->numvertexes*sizeof(vec3_t));\n\t\t\t\t\t\tAlias_TransformVerticies_VN(m->bones, m->numvertexes, m->bonenums[0], m->boneweights[0],\tm->xyz_array[0], posedata[0], m->normals_array[0], normdata[0]);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\tAlias_TransformVerticies_V(m->bones, m->numvertexes, m->bonenums[0], m->boneweights[0],\t\tm->xyz_array[0], posedata[0]);\n\t\t\t\t}\n\t\t\t\telse\n#endif\n\t\t\t\t{\n\t\t\t\t\tif (m->xyz_blendw[1] == 1 && m->xyz2_array)\n\t\t\t\t\t\tposedata = m->xyz2_array;\n\t\t\t\t\telse if (m->xyz_blendw[0] != 1 && m->xyz2_array)\n\t\t\t\t\t{\n\t\t\t\t\t\tposedata = alloca(m->numvertexes*sizeof(vecV_t));\n\t\t\t\t\t\tfor (i = 0; i < m->numvertexes; i++)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tfor (j = 0; j < 3; j++)\n\t\t\t\t\t\t\t\tposedata[i][j] =\tm->xyz_array[i][j] * m->xyz_blendw[0] +\n\t\t\t\t\t\t\t\t\t\t\t\t\tm->xyz2_array[i][j] * m->xyz_blendw[1];\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\tposedata = m->xyz_array;\n\t\t\t\t}\n\t\t\t\tif (normdata)\n\t\t\t\t{\t//show small pegs at each vertex\n\t\t\t\t\tif (cl_numstris == cl_maxstris)\n\t\t\t\t\t{\n\t\t\t\t\t\tcl_maxstris+=8;\n\t\t\t\t\t\tcl_stris = BZ_Realloc(cl_stris, sizeof(*cl_stris)*cl_maxstris);\n\t\t\t\t\t}\n\t\t\t\t\tt = &cl_stris[cl_numstris++];\n\t\t\t\t\tt->shader = shader;\n\t\t\t\t\tt->flags = BEF_LINES;\n\t\t\t\t\tt->firstidx = cl_numstrisidx;\n\t\t\t\t\tt->firstvert = cl_numstrisvert;\n\t\t\t\t\tt->numidx = t->numvert = m->numvertexes*2;\n\n\t\t\t\t\tif (cl_numstrisidx+t->numidx > cl_maxstrisidx)\n\t\t\t\t\t{\n\t\t\t\t\t\tcl_maxstrisidx=cl_numstrisidx+t->numidx;\n\t\t\t\t\t\tcl_strisidx = BZ_Realloc(cl_strisidx, sizeof(*cl_strisidx)*cl_maxstrisidx);\n\t\t\t\t\t}\n\t\t\t\t\tif (cl_numstrisvert+t->numvert > cl_maxstrisvert)\n\t\t\t\t\t\tcl_stris_ExpandVerts(cl_numstrisvert+t->numvert);\n\t\t\t\t\tfor (i = 0; i < m->numvertexes; i++)\n\t\t\t\t\t{\n\t\t\t\t\t\tVectorMA(vec3_origin,\tposedata[i][0], ent->axis[0], tmp);\n\t\t\t\t\t\tVectorMA(tmp,\t\t\tposedata[i][1], ent->axis[1], tmp);\n\t\t\t\t\t\tVectorMA(tmp,\t\t\tposedata[i][2], ent->axis[2], tmp);\n\t\t\t\t\t\tVectorMA(ent->origin,\tent->scale,\t\ttmp,\t\t  cl_strisvertv[t->firstvert+i*2+0]);\n\n\t\t\t\t\t\tVectorMA(tmp,\t\t\tnormdata[i][0], ent->axis[0], tmp);\n\t\t\t\t\t\tVectorMA(tmp,\t\t\tnormdata[i][1], ent->axis[1], tmp);\n\t\t\t\t\t\tVectorMA(tmp,\t\t\tnormdata[i][2], ent->axis[2], tmp);\n\t\t\t\t\t\tVectorMA(ent->origin,\tent->scale,\t\ttmp,\t\t  cl_strisvertv[t->firstvert+i*2+1]);\n\n\t\t\t\t\t\tVector2Set(cl_strisvertt[t->firstvert+i*2+0], 0.0, 0.0);\n\t\t\t\t\t\tVector2Set(cl_strisvertt[t->firstvert+i*2+1], 1.0, 1.0);\n\t\t\t\t\t\tVector4Set(cl_strisvertc[t->firstvert+i*2+0], 0, 0, 1, 1);\n\t\t\t\t\t\tVector4Set(cl_strisvertc[t->firstvert+i*2+1], 0, 0, 1, 1);\n\n\t\t\t\t\t\tcl_strisidx[cl_numstrisidx+i*2+0] = i*2+0;\n\t\t\t\t\t\tcl_strisidx[cl_numstrisidx+i*2+1] = i*2+1;\n\t\t\t\t\t}\n\t\t\t\t\tcl_numstrisidx += i*2;\n\t\t\t\t\tcl_numstrisvert += i*2;\n\t\t\t\t}\n\t\t\t\telse if (mode == 0)\n\t\t\t\t{\t//regular wireframe\n\t\t\t\t\tif (cl_numstris == cl_maxstris)\n\t\t\t\t\t{\n\t\t\t\t\t\tcl_maxstris+=8;\n\t\t\t\t\t\tcl_stris = BZ_Realloc(cl_stris, sizeof(*cl_stris)*cl_maxstris);\n\t\t\t\t\t}\n\t\t\t\t\tt = &cl_stris[cl_numstris++];\n\t\t\t\t\tt->shader = shader;\n\t\t\t\t\tt->flags = 0;//BEF_LINES;\n\t\t\t\t\tt->firstidx = cl_numstrisidx;\n\t\t\t\t\tt->firstvert = cl_numstrisvert;\n\t\t\t\t\tif (t->flags&BEF_LINES)\n\t\t\t\t\t\tt->numidx = m->numindexes*2;\n\t\t\t\t\telse\n\t\t\t\t\t\tt->numidx = m->numindexes;\n\t\t\t\t\tt->numvert = m->numvertexes;\n\n\t\t\t\t\tif (cl_numstrisidx+t->numidx > cl_maxstrisidx)\n\t\t\t\t\t{\n\t\t\t\t\t\tcl_maxstrisidx=cl_numstrisidx+t->numidx;\n\t\t\t\t\t\tcl_strisidx = BZ_Realloc(cl_strisidx, sizeof(*cl_strisidx)*cl_maxstrisidx);\n\t\t\t\t\t}\n\t\t\t\t\tif (cl_numstrisvert+m->numvertexes > cl_maxstrisvert)\n\t\t\t\t\t\tcl_stris_ExpandVerts(cl_numstrisvert+m->numvertexes);\n\t\t\t\t\tfor (i = 0; i < m->numvertexes; i++)\n\t\t\t\t\t{\n\t\t\t\t\t\tVectorMA(vec3_origin,\tposedata[i][0], ent->axis[0], tmp);\n\t\t\t\t\t\tVectorMA(tmp,\t\t\tposedata[i][1], ent->axis[1], tmp);\n\t\t\t\t\t\tVectorMA(tmp,\t\t\tposedata[i][2], ent->axis[2], tmp);\n\t\t\t\t\t\tVectorMA(ent->origin,\tent->scale,\t\ttmp,\t\t  cl_strisvertv[t->firstvert+i]);\n\n\t\t\t\t\t\tVector2Set(cl_strisvertt[t->firstvert+i], 0.5, 0.5);\n\t\t\t\t\t\tVector4Set(cl_strisvertc[t->firstvert+i], 1, 1, 1, 0.1);\n\t\t\t\t\t}\n\t\t\t\t\tif (t->flags&BEF_LINES)\n\t\t\t\t\t{\n\t\t\t\t\t\tfor (i = 0; i < m->numindexes; i+=3)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcl_strisidx[cl_numstrisidx++] = m->indexes[i+0];\n\t\t\t\t\t\t\tcl_strisidx[cl_numstrisidx++] = m->indexes[i+1];\n\t\t\t\t\t\t\tcl_strisidx[cl_numstrisidx++] = m->indexes[i+1];\n\t\t\t\t\t\t\tcl_strisidx[cl_numstrisidx++] = m->indexes[i+2];\n\t\t\t\t\t\t\tcl_strisidx[cl_numstrisidx++] = m->indexes[i+2];\n\t\t\t\t\t\t\tcl_strisidx[cl_numstrisidx++] = m->indexes[i+0];\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tfor (i = 0; i < m->numindexes; i++)\n\t\t\t\t\t\t\tcl_strisidx[cl_numstrisidx+i] = m->indexes[i];\n\t\t\t\t\t\tcl_numstrisidx += m->numindexes;\n\t\t\t\t\t}\n\t\t\t\t\tcl_numstrisvert += m->numvertexes;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n#endif\n\n\nstatic float PlaneNearest(const vec3_t normal, const vec3_t mins, const vec3_t maxs)\n{\n\tfloat result;\n#if 0\n\tresult  = fabs(normal[0] * maxs[0]);\n\tresult += fabs(normal[1] * maxs[1]);\n\tresult += fabs(normal[2] * maxs[2]);\n#elif 0\n\tresult  = normal[0] * ((normal[0] > 0)?-16:16);\n\tresult += normal[1] * ((normal[1] > 0)?-16:16);\n\tresult += normal[2] * ((normal[2] > 0)?-24:32);\n#else\n\tresult  = normal[0] * ((normal[0] > 0)?mins[0]:maxs[0]);\n\tresult += normal[1] * ((normal[1] > 0)?mins[1]:maxs[1]);\n\tresult += normal[2] * ((normal[2] > 0)?mins[2]:maxs[2]);\n#endif\n\treturn result;\n}\n\nvoid CLQ1_DrawLine(shader_t *shader, vec3_t v1, vec3_t v2, float r, float g, float b, float a);\nstatic qboolean Mod_Trace_Trisoup(vecV_t *posedata, index_t *indexes, int numindexes, const vec3_t start, const vec3_t end, const vec3_t mins, const vec3_t maxs, trace_t *fte_restrict trace)\n{\n\tqboolean impacted = false;\n\tint i, j;\n\n\tfloat *p1, *p2, *p3;\n\tvec3_t edge1, edge2, edge3;\n\tvec3_t normal;\n\tvec3_t edgenormal;\n\n\tfloat planedist;\n\tfloat diststart, distend;\n\tfloat mn,mx;\n\tfloat extend;\n\n\tfloat frac;\n\n\tvec3_t impactpoint;\n\n\tfor (i = 0; i < numindexes; i+=3)\n\t{\n\t\tp1 = posedata[indexes[i+0]];\n\t\tp2 = posedata[indexes[i+1]];\n\t\tp3 = posedata[indexes[i+2]];\n\n#if 0\n\t\t{\n\t\t\tshader_t *lineshader = R_RegisterShader(\"lineshader\", SUF_NONE,\n\t\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\t\"polygonoffset\\n\"\n\t\t\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\t\t\"map $whiteimage\\n\"\n\t\t\t\t\t\t\t\t\"blendfunc add\\n\"\n\t\t\t\t\t\t\t\t\"rgbgen vertex\\n\"\n\t\t\t\t\t\t\t\t\"alphagen vertex\\n\"\n\t\t\t\t\t\t\t\"}\\n\"\n\t\t\t\t\t\t\"}\\n\");\n\t\t\tVectorAdd(p1, r_refdef.pvsorigin, edge1);\n\t\t\tVectorAdd(p2, r_refdef.pvsorigin, edge2);\n\t\t\tVectorAdd(p3, r_refdef.pvsorigin, edge3);\n\t\t\tCLQ1_DrawLine(lineshader, edge1, edge2, 0, 1, 0, 1);\n\t\t\tCLQ1_DrawLine(lineshader, edge2, edge3, 0, 1, 0, 1);\n\t\t\tCLQ1_DrawLine(lineshader, edge3, edge1, 0, 1, 0, 1);\n\t\t}\n#endif\n\n\t\tVectorSubtract(p1, p2, edge1);\n\t\tVectorSubtract(p3, p2, edge2);\n\t\tCrossProduct(edge1, edge2, normal);\n\t\tVectorNormalize(normal);\n\n\t\t//degenerate triangle\n\t\tif (!normal[0] && !normal[1] && !normal[2])\n\t\t\tcontinue;\n\n\t\t//debugging\n//\t\tif (normal[2] != 1)\n//\t\t\tcontinue;\n\n#define\tDIST_EPSILON\t(0.03125)\n#define DIST_SOLID\t\t(3/8.0)\t//the plane must be at least this thick, or player prediction will try jittering through it to correct the player's origin\n\t\textend = PlaneNearest(normal, mins, maxs);\n\t\tplanedist = DotProduct(p1, normal)-extend;\n\t\tdiststart = DotProduct(start, normal);\n\t\tif (diststart/*+extend+DIST_SOLID*/ < planedist)\n\t\t\tcontinue;\t//start on back side (or slightly inside).\n\t\tdistend = DotProduct(end, normal);\n\t\tif (distend > planedist)\n\t\t\tcontinue;\t//end on front side.\n\n\t\t//figure out the precise frac\n\t\tif (diststart > planedist)\n\t\t{\n\t\t\t//if we're not stuck inside it\n\t\t\tif (distend >= diststart)\n\t\t\t\tcontinue;\t//trace moves away from or along the surface. don't block the trace if we're sliding along the front of it.\n\t\t}\n\t\tfrac = (diststart - planedist) / (diststart-distend);\n\t\tif (frac >= trace->truefraction)\t//already found one closer.\n\t\t\tcontinue;\n\n\t\t//an impact outside of the surface's bounding box (expanded by the trace bbox) is not a valid impact.\n\t\t//this solves extrusion issues.\n\t\tfor (j = 0; j < 3; j++)\n\t\t{\n\t\t\timpactpoint[j] = start[j] + frac*(end[j] - start[j]);\n\t\t\t//make sure the impact point is within the triangle's bbox.\n\t\t\t//primarily, this serves to prevent the edge extruding off to infinity or so\n\t\t\tmx = mn = p1[j];\n\t\t\tif (mn > p2[j])\n\t\t\t\tmn = p2[j];\n\t\t\tif (mx < p2[j])\n\t\t\t\tmx = p2[j];\n\t\t\tif (mn > p3[j])\n\t\t\t\tmn = p3[j];\n\t\t\tif (mx < p3[j])\n\t\t\t\tmx = p3[j];\n\t\t\tmx-=mins[j]-DIST_EPSILON;\n\t\t\tmn-=maxs[j]+DIST_EPSILON;\n\t\t\tif (impactpoint[j] > mx)\n\t\t\t\tbreak;\n\t\t\tif (impactpoint[j] < mn)\n\t\t\t\tbreak;\n\t\t}\n\t\tif (j < 3)\n\t\t\tcontinue;\n\n\n\t\t//make sure the impact point is actually within the triangle\n\t\tCrossProduct(edge1, normal, edgenormal);\n\t\tVectorNormalize(edgenormal);\n\t\tif (DotProduct(impactpoint, edgenormal) > DotProduct(p2, edgenormal)-PlaneNearest(edgenormal, mins, maxs)+DIST_EPSILON)\n\t\t\tcontinue;\n\n\t\tCrossProduct(normal, edge2, edgenormal);\n\t\tVectorNormalize(edgenormal);\n\t\tif (DotProduct(impactpoint, edgenormal) > DotProduct(p3, edgenormal)-PlaneNearest(edgenormal, mins, maxs)+DIST_EPSILON)\n\t\t\tcontinue;\n\n\t\tVectorSubtract(p1, p3, edge3);\n\t\tCrossProduct(normal, edge3, edgenormal);\n\t\tVectorNormalize(edgenormal);\n\t\tif (DotProduct(impactpoint, edgenormal) > DotProduct(p1, edgenormal)-PlaneNearest(edgenormal, mins, maxs)+DIST_EPSILON)\n\t\t\tcontinue;\n\n\t\t//okay, its a valid impact\n\t\ttrace->truefraction = frac;\n\n\t\t//move back from the impact point. this should keep the point slightly outside of the solid triangle.\n\t\tfrac = (diststart - (planedist+DIST_EPSILON)) / (diststart-distend);\n\t\tif (frac < 0)\n\t\t{\t//we're inside, apparently\n\t\t\ttrace->startsolid = trace->allsolid = (diststart < planedist);\n\t\t\ttrace->fraction = 0;\n\t\t\tVectorCopy(start, trace->endpos);\n\t\t}\n\t\telse\n\t\t{\n\t\t\t//we made progress\n\t\t\ttrace->fraction = frac;\n\t\t\ttrace->endpos[0] = start[0] + frac*(end[0] - start[0]);\n\t\t\ttrace->endpos[1] = start[1] + frac*(end[1] - start[1]);\n\t\t\ttrace->endpos[2] = start[2] + frac*(end[2] - start[2]);\n\t\t}\n\t\tVectorCopy(normal, trace->plane.normal);\n\t\ttrace->plane.dist = planedist;\n\t\ttrace->triangle_id = 1+i/3;\n\t\timpacted = true;\n\n//\t\tif (fabs(normal[0]) != 1 && fabs(normal[1]) != 1 && fabs(normal[2]) != 1)\n//\t\t\tCon_Printf(\"Non-axial impact\\n\");\n\n#if 0\n\t\t{\n\t\t\tshader_t *lineshader = R_RegisterShader(\"lineshader\", SUF_NONE,\n\t\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\t\"polygonoffset\\n\"\n\t\t\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\t\t\"map $whiteimage\\n\"\n\t\t\t\t\t\t\t\t\"blendfunc add\\n\"\n\t\t\t\t\t\t\t\t\"rgbgen vertex\\n\"\n\t\t\t\t\t\t\t\t\"alphagen vertex\\n\"\n\t\t\t\t\t\t\t\"}\\n\"\n\t\t\t\t\t\t\"}\\n\");\n\t\t\tVectorAdd(p1, r_refdef.pvsorigin, edge1);\n\t\t\tVectorAdd(p2, r_refdef.pvsorigin, edge2);\n\t\t\tVectorAdd(p3, r_refdef.pvsorigin, edge3);\n\t\t\tCLQ1_DrawLine(lineshader, edge1, edge2, 0, 1, 0, 1);\n\t\t\tCLQ1_DrawLine(lineshader, edge2, edge3, 0, 1, 0, 1);\n\t\t\tCLQ1_DrawLine(lineshader, edge3, edge1, 0, 1, 0, 1);\n\t\t}\n#endif\n\t}\n\treturn impacted;\n}\n\n//The whole reason why model loading is supported in the server.\nstatic qboolean Mod_Trace(model_t *model, int forcehullnum, const framestate_t *framestate, const vec3_t axis[3], const vec3_t start, const vec3_t end, const vec3_t mins, const vec3_t maxs, qboolean capsule, unsigned int contentsmask, trace_t *trace)\n{\n\tgaliasinfo_t *mod = Mod_Extradata(model);\n\n//\tfloat temp;\n\n\tvecV_t *posedata = NULL;\n\tindex_t *indexes;\n\tint surfnum = 0;\n#ifdef SKELETALMODELS\n\tint cursurfnum = -1, curbonesurf = -1;\n\tfloat buffer[MAX_BONES*12];\n\tfloat bufferalt[MAX_BONES*12];\n\tconst float *bonepose = NULL;\n#endif\n\n\tvec3_t start_l, end_l;\n\n\tif (axis)\n\t{\n\t\tstart_l[0] = DotProduct(start, axis[0]);\n\t\tstart_l[1] = DotProduct(start, axis[1]);\n\t\tstart_l[2] = DotProduct(start, axis[2]);\n\t\tend_l[0] = DotProduct(end, axis[0]);\n\t\tend_l[1] = DotProduct(end, axis[1]);\n\t\tend_l[2] = DotProduct(end, axis[2]);\n\t}\n\telse\n\t{\n\t\tVectorCopy(start, start_l);\n\t\tVectorCopy(end, end_l);\n\t}\n\n\ttrace->fraction = trace->truefraction = 1;\n\n\tfor(; mod; mod = mod->nextsurf, surfnum++)\n\t{\n\t\tif (!(mod->contents & contentsmask))\n\t\t\tcontinue;\t//this mesh isn't solid to the trace\n\n\t\tindexes = mod->ofs_indexes;\n#ifdef SKELETALMODELS\n\t\tif (mod->ofs_skel_xyz)\n\t\t{\n\t\t\tif (!mod->ofs_skel_idx || !framestate || !mod->numbones)\n\t\t\t\tposedata = mod->ofs_skel_xyz;\t//if there's no weights, don't try animating anything.\n\t\t\telse if (mod->shares_verts != cursurfnum || !posedata)\n\t\t\t{\n\t\t\t\tcursurfnum = mod->shares_verts;\n\t\t\t\tif (curbonesurf != mod->shares_bones)\n\t\t\t\t{\n\t\t\t\t\tcurbonesurf = mod->shares_bones;\n\t\t\t\t\tbonepose = Alias_GetBoneInformation(mod, framestate, SKEL_INVERSE_ABSOLUTE, buffer, bufferalt, MAX_BONES, NULL);\n\t\t\t\t}\n\t\t\t\tposedata = alloca(mod->numverts*sizeof(vecV_t));\n\t\t\t\tAlias_TransformVerticies_V(bonepose, mod->numverts, mod->ofs_skel_idx[0], mod->ofs_skel_weight[0], mod->ofs_skel_xyz[0], posedata[0]);\n\t\t\t}\n\t\t\t//else posedata = posedata;\n\t\t}\n\t\telse \n#endif\n#ifndef NONSKELETALMODELS\n\t\t\tcontinue;\n#else\n\t\tif (!mod->numanimations)\n\t\t\tcontinue;\n\t\telse\n\t\t{\n\t\t\tgaliaspose_t *pose;\n\t\t\tgaliasanimation_t *group = mod->ofsanimations;\n\t\t\tif (framestate)\n\t\t\t\tgroup += framestate->g[FS_REG].frame[0] % mod->numanimations;\n\t\t\t//FIXME: no support for frame blending.\n\t\t\tif (!group->numposes)\n\t\t\t\tcontinue;\n\t\t\tpose = group->poseofs;\n\t\t\tif (!pose)\n\t\t\t\tcontinue;\t//error...\n\t\t\tif (framestate)\n\t\t\t\tpose += (int)(framestate->g[FS_REG].frametime[0] * group->rate)%group->numposes;\n\t\t\tposedata = pose->ofsverts;\n\t\t}\n#endif\n\n\t\tif (Mod_Trace_Trisoup(posedata, indexes, mod->numindexes, start_l, end_l, mins, maxs, trace))\n\t\t{\n\t\t\ttrace->contents = mod->contents;\n\t\t\ttrace->surface = &mod->csurface;\n\t\t\ttrace->surface_id = mod->surfaceid;\n\t\t\ttrace->bone_id = 0;\n#ifdef SKELETALMODELS\n\t\t\tif (mod->ofs_skel_weight)\n\t\t\t{\t//fixme: would be better to consider the distance to the vertex too. cartesian coord stuff etc.\n\t\t\t\tunsigned int v, w, i;\n\t\t\t\tfloat bw = 0;\n\t\t\t\tfor (i = 0; i < 3; i++)\n\t\t\t\t{\n\t\t\t\t\tfor (v = indexes[(trace->triangle_id-1)*3+i], w = 0; w < 4; w++)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (bw < mod->ofs_skel_weight[v][w])\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tbw = mod->ofs_skel_weight[v][w];\n\t\t\t\t\t\t\ttrace->bone_id = 1 + mod->ofs_skel_idx[v][w];\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n#endif\n\t\t\tif (axis)\n\t\t\t{\n\t\t\t\tvec3_t iaxis[3];\n\t\t\t\tvec3_t norm;\n\t\t\t\tMatrix3x3_RM_Invert_Simple((const void *)axis, iaxis);\n\t\t\t\tVectorCopy(trace->plane.normal, norm);\n\t\t\t\ttrace->plane.normal[0] = DotProduct(norm, iaxis[0]);\n\t\t\t\ttrace->plane.normal[1] = DotProduct(norm, iaxis[1]);\n\t\t\t\ttrace->plane.normal[2] = DotProduct(norm, iaxis[2]);\n\n//\t\t\t\tfrac = traceinfo.truefraction;\n\t\t\t\t/*\n\t\t\t\tdiststart = DotProduct(traceinfo.start, trace->plane.normal);\n\t\t\t\tdistend = DotProduct(traceinfo.end, trace->plane.normal);\n\t\t\t\tif (diststart == distend)\n\t\t\t\t\tfrac = 0;\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tfrac = (diststart - trace->plane.dist) / (diststart-distend);\n\t\t\t\t\tif (frac < 0)\n\t\t\t\t\t\tfrac = 0;\n\t\t\t\t\telse if (frac > 1)\n\t\t\t\t\t\tfrac = 1;\n\t\t\t\t}*/\n\n\t\t\t\t/*okay, this is where it hits this plane*/\n\t\t\t\ttrace->endpos[0] = start[0] + trace->fraction*(end[0] - start[0]);\n\t\t\t\ttrace->endpos[1] = start[1] + trace->fraction*(end[1] - start[1]);\n\t\t\t\ttrace->endpos[2] = start[2] + trace->fraction*(end[2] - start[2]);\n\t\t\t}\n\t\t}\n\t}\n\n\ttrace->allsolid = false;\n\n\treturn trace->fraction != 1;\n}\n\nstatic unsigned int Mod_Mesh_PointContents(struct model_s *model, const vec3_t axis[3], const vec3_t p)\n{\t//trisoup doesn't have any actual volumes, thus we can't report anything...\n\treturn 0;\n}\nstatic int Mod_Mesh_ClusterForPoint(struct model_s *model, const vec3_t point, int *areaout)\n{\t//trisoup doesn't have any actual pvs, thus we can't report anything...\n\tif (areaout)\n\t\t*areaout = 0;\n\treturn -1;\n}\nstatic qbyte *Mod_Mesh_ClusterPVS(struct model_s *model, int cluster, pvsbuffer_t *pvsbuffer, pvsmerge_t merge)\n{\t//trisoup doesn't have any actual pvs, thus we can't report anything...\n\treturn NULL;\n}\nstatic unsigned int Mod_Mesh_FatPVS(struct model_s *model, const vec3_t org, pvsbuffer_t *pvsbuffer, qboolean merge)\n{\t//trisoup doesn't have any actual pvs, thus we can't report anything...\n\treturn 0;\n}\nqboolean Mod_Mesh_EdictInFatPVS(struct model_s *model, const struct pvscache_s *edict, const qbyte *pvs, const int *areas)\n{\t//trisoup doesn't have any actual pvs, thus we always report visible\n\treturn true;\n}\nvoid Mod_Mesh_FindTouchedLeafs(struct model_s *model, struct pvscache_s *ent, const vec3_t cullmins, const vec3_t cullmaxs)\n{\n}\nstatic void Mod_Mesh_LightPointValues(struct model_s *model, const vec3_t point, vec3_t res_diffuse, vec3_t res_ambient, vec3_t res_dir)\n{\t//trisoup doesn't have any actual pvs, thus we can't report anything...\n\tVectorSet(res_diffuse, 255,255,255);\n\tVectorSet(res_ambient, 128,128,128);\n\tVectorSet(res_dir, 0,0,1);\n}\nstatic void Mod_SetMeshModelFuncs(model_t *mod, qboolean isstatic)\n{\n\tif (!isstatic)\n\t{\n\t\tmod->funcs.NativeTrace = Mod_Trace;\n\t\treturn;\n\t}\n\n//\tvoid (*PurgeModel) (struct model_s *mod);\n\n\tmod->funcs.PointContents = Mod_Mesh_PointContents;\n//\tunsigned int (*BoxContents)\t\t(struct model_s *model, int hulloverride, const framestate_t *framestate, const vec3_t axis[3], const vec3_t p, const vec3_t mins, const vec3_t maxs);\n\n\t//deals with whatever is native for the bsp (gamecode is expected to distinguish this).\n\tmod->funcs.NativeTrace = Mod_Trace;\n//\tunsigned int (*NativeContents)(struct model_s *model, int hulloverride, const framestate_t *framestate, const vec3_t axis[3], const vec3_t p, const vec3_t mins, const vec3_t maxs);\n\n\tmod->funcs.FatPVS = Mod_Mesh_FatPVS;\n\tmod->funcs.EdictInFatPVS = Mod_Mesh_EdictInFatPVS;\n\tmod->funcs.FindTouchedLeafs = Mod_Mesh_FindTouchedLeafs;\n\n\tmod->funcs.LightPointValues = Mod_Mesh_LightPointValues;\n//\tvoid (*StainNode)\t\t\t(struct mnode_s *node, float *parms);\n//\tvoid (*MarkLights)\t\t\t(struct dlight_s *light, dlightbitmask_t bit, struct mnode_s *node);\n\n\tmod->funcs.ClusterForPoint = Mod_Mesh_ClusterForPoint;\n\tmod->funcs.ClusterPVS = Mod_Mesh_ClusterPVS;\n//\tqbyte *(*ClustersInSphere)\t(struct model_s *model, const vec3_t point, float radius, pvsbuffer_t *pvsbuffer, const qbyte *fte_restrict unionwith);\n\n\tBIH_BuildAlias(mod, mod->meshinfo);\n}\n\n\nstatic void Mod_ClampModelSize(model_t *mod)\n{\n#ifndef SERVERONLY\n\tint i;\n\n\tfloat rad=0, axis;\n\taxis = (mod->maxs[0] - mod->mins[0]);\n\trad += axis*axis;\n\taxis = (mod->maxs[1] - mod->mins[1]);\n\trad += axis*axis;\n\taxis = (mod->maxs[2] - mod->mins[2]);\n\trad += axis*axis;\n\n\tmod->tainted = false;\n\tif (mod->engineflags & MDLF_DOCRC)\n\t{\n\t\tif (!strcmp(mod->name, \"progs/eyes.mdl\"))\n\t\t{\t//this is checked elsewhere to make sure the crc matches (this is to make sure the crc check was actually called)\n\t\t\tif (mod->type != mod_alias || mod->fromgame != fg_quake || mod->flags)\n\t\t\t\tmod->tainted = true;\n\t\t}\n\t}\n\n\tmod->clampscale = 1;\n\tfor (i = 0; i < sizeof(clampedmodel)/sizeof(clampedmodel[0]); i++)\n\t{\n\t\tif (!strcmp(mod->name, clampedmodel[i].name))\n\t\t{\n\t\t\tif (rad > clampedmodel[i].furthestallowedextremety)\n\t\t\t{\n\t\t\t\taxis = clampedmodel[i].furthestallowedextremety;\n\t\t\t\tmod->clampscale = axis/rad;\n\t\t\t\tCon_DPrintf(\"\\\"%s\\\" will be clamped.\\n\", mod->name);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t}\n\n//\tCon_DPrintf(\"Don't know what size to clamp \\\"%s\\\" to (size:%f).\\n\", mod->name, rad);\n#endif\n}\n\n#ifdef NONSKELETALMODELS\n#ifndef SERVERONLY\nstatic int R_FindTriangleWithEdge (index_t *indexes, int numtris, int start, int end, int ignore)\n{\n\tint i;\n\tint match, count;\n\n\tcount = 0;\n\tmatch = -1;\n\n\tfor (i = 0; i < numtris; i++, indexes += 3)\n\t{\n\t\tif ( (indexes[0] == start && indexes[1] == end)\n\t\t\t|| (indexes[1] == start && indexes[2] == end)\n\t\t\t|| (indexes[2] == start && indexes[0] == end) ) {\n\t\t\tif (i != ignore)\n\t\t\t\tmatch = i;\n\t\t\tcount++;\n\t\t} else if ( (indexes[1] == start && indexes[0] == end)\n\t\t\t|| (indexes[2] == start && indexes[1] == end)\n\t\t\t|| (indexes[0] == start && indexes[2] == end) ) {\n\t\t\tcount++;\n\t\t}\n\t}\n\n\t// detect edges shared by three triangles and make them seams\n\tif (count > 2)\n\t\tmatch = -1;\n\n\treturn match;\n}\n#endif\n\nstatic void Mod_CompileTriangleNeighbours(model_t *loadmodel, galiasinfo_t *galias)\n{\n#ifndef SERVERONLY\n\tif (Sh_StencilShadowsActive())\n\t{\n\t\tint i, *n;\n\t\tindex_t *index;\n\t\tindex_t *indexes = galias->ofs_indexes;\n\t\tint numtris = galias->numindexes/3;\n\t\tint *neighbours;\n\t\tneighbours = ZG_Malloc(&loadmodel->memgroup, sizeof(int)*numtris*3);\n\t\tgalias->ofs_trineighbours = neighbours;\n\n\t\tfor (i = 0, index = indexes, n = neighbours; i < numtris; i++, index += 3, n += 3)\n\t\t{\n\t\t\tn[0] = R_FindTriangleWithEdge (indexes, numtris, index[1], index[0], i);\n\t\t\tn[1] = R_FindTriangleWithEdge (indexes, numtris, index[2], index[1], i);\n\t\t\tn[2] = R_FindTriangleWithEdge (indexes, numtris, index[0], index[2], i);\n\t\t}\n\t}\n#endif\n}\n#endif\n\n#define MAX_FRAMEINFO_POSES 256\n\ntypedef struct\n{\n\tunsigned int poses[MAX_FRAMEINFO_POSES];\n\tqboolean posesarray;\n\tunsigned int firstpose;\n\tunsigned int posecount;\n\tfloat fps;\n\tqboolean loop;\n\tint action;\n\tgaliasevent_t *events;\n\tfloat actionweight;\n\tchar name[MAX_QPATH];\n} frameinfo_t;\nstatic frameinfo_t *ParseFrameInfo(model_t *mod, int *numgroups)\n{\n\tconst char *modelname = mod->name;\n\n\tint count = 0;\n\tint maxcount = 0;\n\tchar *line, *eol;\n\tchar *file;\n\tframeinfo_t *frames = NULL;\n\tchar fname[MAX_QPATH];\n\tchar tok[MAX_FRAMEINFO_POSES * 4];\n\tsize_t fsize;\n\tcom_tokentype_t ttype;\n\tjson_t *rootjson;\n\tQ_snprintfz(fname, sizeof(fname), \"%s.framegroups\", modelname);\n\tline = file = FS_LoadMallocFile(fname, &fsize);\n\tif (!file)\n\t\treturn NULL;\n\n\trootjson = JSON_Parse(file);\t//must be a fully valid json file, so any space-separated tokens from the dp format will return NULL here just fine.\n\tif (rootjson)\n\t{\n\t\tjson_t *framegroups = JSON_FindChild(rootjson, \"framegroups\");\n\t\tmaxcount = JSON_GetCount(framegroups);\n\t\tframes = realloc(frames, sizeof(*frames)*maxcount);\n\t\tfor(count = 0; count < maxcount; count++)\n\t\t{\n\t\t\tgaliasevent_t *ev, **link;\n\t\t\tchar eventdata[65536];\n\t\t\tunsigned int posecount;\n\t\t\tjson_t *arr, *c;\n\t\t\tjson_t *in = JSON_GetIndexed(framegroups, count);\n\t\t\tif (!in)\n\t\t\t\tbreak;\t//erk? shouldn't really happen. not an issue though.\n\n\t\t\tframes[count].firstpose = JSON_GetInteger(in, \"firstpose\", 0);\n\t\t\tframes[count].posecount = JSON_GetInteger(in, \"numposes\", 1);\n\t\t\tframes[count].posesarray = false;\n\t\t\tarr = JSON_FindChild(in, \"poses\");\n\t\t\tif (arr)\n\t\t\t{\t//override with explicit poses, if specified.\n\t\t\t\tfor (posecount = 0; posecount < countof(frames[count].poses); posecount++)\n\t\t\t\t{\n\t\t\t\t\tc = JSON_GetIndexed(arr, posecount);\n\t\t\t\t\tif (!c)\t//ran out of elements...\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tframes[count].poses[posecount] = JSON_GetUInteger(c, NULL, 0);\n\t\t\t\t}\n\t\t\t\tif (posecount>0)\n\t\t\t\t{\n\t\t\t\t\tframes[count].posecount = posecount;\n\t\t\t\t\tframes[count].posesarray = true;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tframes[count].fps = JSON_GetFloat(in, \"fps\", 20);\n\t\t\tif (frames[count].fps <= 0)\n\t\t\t\tframes[count].fps = 10;\n\t\t\tframes[count].loop = JSON_GetUInteger(in, \"loop\", false);\n\n\t\t\tQ_snprintfz(frames[count].name,sizeof(frames[count].name), \"%s[%d]\", fname, count);\n\t\t\tJSON_GetString(in, \"name\", frames[count].name, sizeof(frames[count].name), NULL);\n\n\t\t\tframes[count].action = JSON_GetInteger(in, \"action\", -1);\n\t\t\tframes[count].actionweight = JSON_GetFloat(in, \"actionweight\", 0);\n\t\t\tframes[count].events = NULL;\n\n\t\t\tarr = JSON_FindChild(in, \"events\");\n\t\t\tfor (posecount = 0; (c=JSON_GetIndexed(arr, posecount))!=NULL; posecount++)\n\t\t\t{\n\t\t\t\t*eventdata = 0;\n\t\t\t\tJSON_GetString(c, \"value\", eventdata,sizeof(eventdata), NULL);\n\t\t\t\tev = ZG_Malloc(&mod->memgroup, sizeof(*ev) + strlen(eventdata)+1);\n\t\t\t\tev->code = JSON_GetInteger(c, \"code\", 0);\n\t\t\t\tev->timestamp = JSON_GetFloat(c, \"timestamp\", JSON_GetFloat(c, \"pose\", 0)/frames[count].fps);\n\t\t\t\tev->data = strcpy((char*)(ev+1), eventdata);\n\n\t\t\t\tlink = &frames[count].events;\n\t\t\t\twhile (*link && (*link)->timestamp <= ev->timestamp)\n\t\t\t\t\tlink = &(*link)->next;\n\t\t\t\tev->next = *link;\n\t\t\t\t*link = ev;\n\t\t\t}\n\t\t}\n\n\t\tmod->flags = JSON_GetUInteger(rootjson, \"modelflags\", mod->flags);\n\n\n\t\tJSON_Destroy(rootjson);\n\t}\n\telse while(line && *line)\n\t{\n\t\tunsigned int posecount = 0;\n\n\t\teol = strchr(line, '\\n');\n\t\tif (eol)\n\t\t\t*eol = 0;\n\t\t\t\n\t\tif (count == maxcount)\n\t\t{\n\t\t\tmaxcount += 32;\n\t\t\tframes = realloc(frames, sizeof(*frames)*maxcount);\n\t\t}\n\n\t\tline = COM_ParseOut(line, tok, sizeof(tok));\n\t\t// Check if firstpose is actually a sequence of comma separated poses, e.g.: 42,43,44,43,42\n\t\tif (strchr(tok, ','))\n\t\t{\n\t\t\tchar pose[64], *ptok = tok;\n\n\t\t\tfor (; posecount < MAX_FRAMEINFO_POSES; posecount++)\n\t\t\t{\n\t\t\t\tptok = COM_ParseStringSetSep(ptok, ',', pose, sizeof(pose));\n\t\t\t\tif (!pose[0])\n\t\t\t\t\tbreak;\n\t\t\t\tframes[count].poses[posecount] = atoi(pose);\n\t\t\t}\n\t\t}\n\t\tframes[count].posesarray = !!posecount;\n\t\tframes[count].firstpose = posecount ? 0 : atoi(tok);\n\t\tline = COM_ParseOut(line, tok, sizeof(tok));\n\t\tframes[count].posecount = posecount ? posecount : atoi(tok);\n\t\tline = COM_ParseOut(line, tok, sizeof(tok));\n\t\tframes[count].fps = atof(tok);\n\t\tline = COM_ParseOut(line, tok, sizeof(tok));\n\t\tif (!strcmp(tok, \"true\") || !strcmp(tok, \"yes\") || !strcmp(tok, \"on\"))\n\t\t\tframes[count].loop = true;\n\t\telse\n\t\t\tframes[count].loop = !!atoi(tok);\n\n\t\tframes[count].events = NULL;\n\t\tframes[count].action = -1;\n\t\tframes[count].actionweight = 0;\n\t\tQ_snprintfz(frames[count].name, sizeof(frames[count].name), \"groupified_%d_anim\", count);\t//to match DP. frameforname cares.\n\n\t\tline = COM_ParseType(line, tok, sizeof(tok), &ttype);\n\t\tif (ttype != TTP_EOF)\n\t\t{\n\t\t\tQ_strncpyz(frames[count].name, tok, sizeof(frames[count].name));\n\t\t\tline = COM_ParseType(line, tok, sizeof(tok), &ttype);\n\t\t}\n\t\tif (ttype != TTP_EOF)\n\t\t{\n\t\t\tframes[count].action = atoi(tok);\n\t\t\tline = COM_ParseType(line, tok, sizeof(tok), &ttype);\n\t\t}\n\t\tif (ttype != TTP_EOF)\n\t\t\tframes[count].actionweight = atoi(tok);\n\n\t\tif (frames[count].posecount>0 && frames[count].fps)\n\t\t\tcount++;\n\n\t\tif (eol)\n\t\t\tline = eol+1;\n\t\telse\n\t\t\tbreak;\n\t}\n\tBZ_Free(file);\n\n\t*numgroups = count;\n\tif (!count)\n\t{\n\t\tfree(frames);\n\t\tframes = NULL;\n\t}\n\treturn frames;\n}\n\n//parses a foo.mdl.events file and inserts the events into the relevant animations\nstatic void Mod_InsertEvent(zonegroup_t *mem, galiasanimation_t *anims, unsigned int numanimations, unsigned int eventanimation, float eventpose, int eventcode, const char *eventdata)\n{\n\tgaliasevent_t *ev, **link;\n\tif (eventanimation >= numanimations)\n\t{\n\t\tCon_Printf(\"Mod_InsertEvent: invalid frame index\\n\");\n\t\treturn;\n\t}\n\tev = ZG_Malloc(mem, sizeof(*ev) + strlen(eventdata)+1);\n\tev->data = (char*)(ev+1);\n\n\tev->timestamp = eventpose;\n\tev->timestamp /= anims[eventanimation].rate;\n\tev->code = eventcode;\n\tstrcpy(ev->data, eventdata);\n\tlink = &anims[eventanimation].events;\n\twhile (*link && (*link)->timestamp <= ev->timestamp)\n\t\tlink = &(*link)->next;\n\tev->next = *link;\n\t*link = ev;\n}\nstatic qboolean Mod_ParseModelEvents(model_t *mod, galiasanimation_t *anims, unsigned int numanimations)\n{\n\tunsigned int anim;\n\tfloat pose;\n\tint eventcode;\n\n\tconst char *modelname = mod->name;\n\tzonegroup_t *mem = &mod->memgroup;\n\tchar fname[MAX_QPATH], tok[2048];\n\tsize_t fsize;\n\tchar *line, *file, *eol;\n\tQ_snprintfz(fname, sizeof(fname), \"%s.events\", modelname);\n\tline = file = FS_LoadMallocFile(fname, &fsize);\n\tif (!file)\n\t\treturn false;\n\twhile(line && *line)\n\t{\n\t\teol = strchr(line, '\\n');\n\t\tif (eol)\n\t\t\t*eol = 0;\n\n\t\tline = COM_ParseOut(line, tok, sizeof(tok));\n\t\tanim = strtoul(tok, NULL, 0);\n\t\tline = COM_ParseOut(line, tok, sizeof(tok));\n\t\tpose = strtod(tok, NULL);\n\t\tline = COM_ParseOut(line, tok, sizeof(tok));\n\t\teventcode = (long)strtol(tok, NULL, 0);\n\t\tline = COM_ParseOut(line, tok, sizeof(tok));\n\t\tMod_InsertEvent(mem, anims, numanimations, anim, pose, eventcode, tok);\n\n\t\tif (eol)\n\t\t\tline = eol+1;\n\t\telse\n\t\t\tbreak;\n\t}\n\tBZ_Free(file);\n\treturn true;\n}\n\nstatic void Mod_DefaultMesh(galiasinfo_t *galias, const char *name, unsigned int index)\n{\n\tQ_strncpyz(galias->surfacename, name, sizeof(galias->surfacename));\n\tQ_strncpyz(galias->csurface.name, COM_SkipPath(name), sizeof(galias->csurface.name));\n\tgalias->contents = FTECONTENTS_BODY;\n\tgalias->geomset = ~0;\t//invalid set = always visible\n\tgalias->geomid = 0;\n\tgalias->mindist = 0;\n\tgalias->maxdist = 0;\n\tgalias->surfaceid = index+1;\n}\n\nvoid Mod_DestroyMesh(galiasinfo_t *galias)\n{\n#ifndef SERVERONLY\n\tif (!qrenderer || !BE_VBO_Destroy)\n\t\treturn;\n\twhile(galias)\n\t{\n\t\tBE_VBO_Destroy(&galias->vbotexcoords, galias->vbomem);\n\t\tBE_VBO_Destroy(&galias->vboindicies, galias->ebomem);\n\t\tgalias = galias->nextsurf;\n\t}\n#endif\n}\n\n#ifndef SERVERONLY\nstatic void Mod_GenerateMeshVBO(model_t *mod, galiasinfo_t *galias)\n//vec3_t *vc, vec2_t *tc, vec3_t *nv, vec3_t *sv, vec3_t *tv, index_t *idx, int numidx, int numverts)\n{\n#ifdef NONSKELETALMODELS\n\tint i, p;\n\tgaliasanimation_t *group = galias->ofsanimations;\n#endif\n\tint vbospace = 0;\n\tvbobctx_t vboctx;\n\n\t//don't fail on dedicated servers\n\tif (!BE_VBO_Begin || !galias->numverts)\n\t\treturn;\n\n\t//determine the amount of space we need for our vbos.\n\tif (galias->ofs_st_array)\n\t\tvbospace += sizeof(*galias->ofs_st_array) * galias->numverts;\n\tif (galias->ofs_lmst_array)\n\t\tvbospace += sizeof(*galias->ofs_lmst_array) * galias->numverts;\n\tif (galias->ofs_rgbaf)\n\t\tvbospace += sizeof(*galias->ofs_rgbaf) * galias->numverts;\n\telse if (galias->ofs_rgbaub)\n\t\tvbospace += sizeof(*galias->ofs_rgbaub) * galias->numverts;\n#ifdef SKELETALMODELS\n\tif (galias->ofs_skel_xyz)\n\t\tvbospace += sizeof(*galias->ofs_skel_xyz) * galias->numverts;\n\tif (galias->ofs_skel_norm)\n\t\tvbospace += sizeof(*galias->ofs_skel_norm) * galias->numverts;\n\tif (galias->ofs_skel_svect)\n\t\tvbospace += sizeof(*galias->ofs_skel_svect) * galias->numverts;\n\tif (galias->ofs_skel_tvect)\n\t\tvbospace += sizeof(*galias->ofs_skel_tvect) * galias->numverts;\n\tif (galias->ofs_skel_idx)\n\t\tvbospace += sizeof(*galias->ofs_skel_idx) * galias->numverts;\n\tif (galias->ofs_skel_weight)\n\t\tvbospace += sizeof(*galias->ofs_skel_weight) * galias->numverts;\n#endif\n#ifdef NONSKELETALMODELS\n\tfor (i = 0; i < galias->numanimations; i++)\n\t{\n\t\tif (group[i].poseofs)\n\t\t\tvbospace += group[i].numposes * galias->numverts * (sizeof(vecV_t)+sizeof(vec3_t)*3);\n\t}\n#endif\n\n\tBE_VBO_Begin(&vboctx, vbospace);\n\tif (galias->ofs_st_array)\n\t\tBE_VBO_Data(&vboctx, galias->ofs_st_array, sizeof(*galias->ofs_st_array) * galias->numverts, &galias->vbotexcoords);\n\tif (galias->ofs_lmst_array)\n\t\tBE_VBO_Data(&vboctx, galias->ofs_lmst_array, sizeof(*galias->ofs_lmst_array) * galias->numverts, &galias->vbolmtexcoords);\n\tif (galias->ofs_rgbaf)\n\t\tBE_VBO_Data(&vboctx, galias->ofs_rgbaf, sizeof(*galias->ofs_rgbaf) * galias->numverts, &galias->vborgba);\n\telse if (galias->ofs_rgbaub)\n\t\tBE_VBO_Data(&vboctx, galias->ofs_rgbaub, sizeof(*galias->ofs_rgbaub) * galias->numverts, &galias->vborgba);\n#ifdef SKELETALMODELS\n\tif (galias->ofs_skel_xyz)\n\t\tBE_VBO_Data(&vboctx, galias->ofs_skel_xyz, sizeof(*galias->ofs_skel_xyz) * galias->numverts, &galias->vbo_skel_verts);\n\tif (galias->ofs_skel_norm)\n\t\tBE_VBO_Data(&vboctx, galias->ofs_skel_norm, sizeof(*galias->ofs_skel_norm) * galias->numverts, &galias->vbo_skel_normals);\n\tif (galias->ofs_skel_svect)\n\t\tBE_VBO_Data(&vboctx, galias->ofs_skel_svect, sizeof(*galias->ofs_skel_svect) * galias->numverts, &galias->vbo_skel_svector);\n\tif (galias->ofs_skel_tvect)\n\t\tBE_VBO_Data(&vboctx, galias->ofs_skel_tvect, sizeof(*galias->ofs_skel_tvect) * galias->numverts, &galias->vbo_skel_tvector);\n\tif (!galias->mappedbones && galias->numbones > sh_config.max_gpu_bones && galias->ofs_skel_idx && sh_config.max_gpu_bones)\n\t{\t//if we're using gpu bones, then its possible that we're trying to load a model with more bones than the gpu supports\n\t\t//to work around this (and get performance back), each surface has a gpu->cpu table so that bones not used on a mesh don't cause it to need to use a software fallback\n\t\tqboolean *seen = alloca(sizeof(*seen) * galias->numbones);\n\t\tint j, k;\n\t\tmemset(seen, 0, sizeof(*seen) * galias->numbones);\n\t\tfor (j = 0; j < galias->numverts; j++)\n\t\t\tfor (k = 0; k < 4; k++)\n\t\t\t{\n\t\t\t\tif (galias->ofs_skel_weight[j][k])\n\t\t\t\t\tseen[galias->ofs_skel_idx[j][k]] = true;\n\t\t\t}\n\n\t\tfor (j = 0, k = 0; j < galias->numbones; j++)\n\t\t{\n\t\t\tif (seen[j])\n\t\t\t\tk++;\n\t\t}\n\t\tif (k < sh_config.max_gpu_bones)\n\t\t{\t//okay, we can hardware accelerate that.\n\t\t\tgalias->bonemap = ZG_Malloc(&mod->memgroup, sizeof(*galias->bonemap)*sh_config.max_gpu_bones);\n\t\t\tgalias->mappedbones = 0;\n\t\t\tfor (j = 0; j < galias->numbones; j++)\n\t\t\t{\n\t\t\t\tif (seen[j])\n\t\t\t\t\tgalias->bonemap[galias->mappedbones++] = j;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t\tCon_DPrintf(CON_WARNING\"PERF: \\\"%s\\\":\\\"%s\\\" exceeds gpu bone limit and will be software-skinned - %i > %i\\n\", mod->name, galias->surfacename, k, sh_config.max_gpu_bones);\n\t}\n\tif (galias->mappedbones)\n\t{\n\t\tboneidx_t *remaps = alloca(sizeof(*remaps) * galias->numbones);\n\t\tbone_vec4_t *bones = alloca(sizeof(*bones) * galias->numverts);\n\t\tint j, k;\n\n\t\t//our remap table is gpu->cpu, but we need cpu->gpu here\n\t\tfor (j = 0; j < galias->numbones; j++)\n\t\t\tremaps[j] = 0;\t//errors.\n\t\tfor (j = 0; j < galias->mappedbones; j++)\n\t\t\tremaps[galias->bonemap[j]] = j;\n\t\t//now remap them all\n\t\tfor (j = 0; j < galias->numverts; j++)\n\t\t\tfor (k = 0; k < 4; k++)\n\t\t\t\tbones[j][k] = remaps[galias->ofs_skel_idx[j][k]];\n\n\t\t//and we can upload\n\t\tif (galias->ofs_skel_idx)\n\t\t\tBE_VBO_Data(&vboctx, bones, sizeof(*bones) * galias->numverts, &galias->vbo_skel_bonenum);\n\t\tif (galias->ofs_skel_weight)\n\t\t\tBE_VBO_Data(&vboctx, galias->ofs_skel_weight, sizeof(*galias->ofs_skel_weight) * galias->numverts, &galias->vbo_skel_bweight);\n\t}\n\telse\n\t{\n\t\tif (galias->ofs_skel_idx)\n\t\t\tBE_VBO_Data(&vboctx, galias->ofs_skel_idx, sizeof(*galias->ofs_skel_idx) * galias->numverts, &galias->vbo_skel_bonenum);\n\t\tif (galias->ofs_skel_weight)\n\t\t\tBE_VBO_Data(&vboctx, galias->ofs_skel_weight, sizeof(*galias->ofs_skel_weight) * galias->numverts, &galias->vbo_skel_bweight);\n\t}\n#endif\n#ifdef NONSKELETALMODELS\n\tfor (i = 0; i < galias->numanimations; i++)\n\t{\n\t\tgaliaspose_t *pose = group[i].poseofs;\n\t\tif (pose)\n\t\t\tfor (p = 0; p < group[i].numposes; p++, pose++)\n\t\t\t{\n\t\t\t\tBE_VBO_Data(&vboctx, pose->ofsverts, sizeof(*pose->ofsverts) * galias->numverts, &pose->vboverts);\n\t\t\t\tBE_VBO_Data(&vboctx, pose->ofsnormals, sizeof(*pose->ofsnormals) * galias->numverts, &pose->vbonormals);\n\t\t\t\tif (pose->ofssvector)\n\t\t\t\t\tBE_VBO_Data(&vboctx, pose->ofssvector, sizeof(*pose->ofssvector) * galias->numverts, &pose->vbosvector);\n\t\t\t\tif (pose->ofstvector)\n\t\t\t\t\tBE_VBO_Data(&vboctx, pose->ofstvector, sizeof(*pose->ofstvector) * galias->numverts, &pose->vbotvector);\n\t\t\t}\n\t}\n#endif\n\tBE_VBO_Finish(&vboctx, galias->ofs_indexes, sizeof(*galias->ofs_indexes) * galias->numindexes, &galias->vboindicies, &galias->vbomem, &galias->ebomem);\n}\n#endif\n\n\n#ifdef NONSKELETALMODELS\n//called for non-skeletal model formats.\nstatic void Mod_BuildTextureVectors(galiasinfo_t *galias)\n//vec3_t *vc, vec2_t *tc, vec3_t *nv, vec3_t *sv, vec3_t *tv, index_t *idx, int numidx, int numverts)\n{\n#ifndef SERVERONLY\n\tint i, p;\n\tgaliasanimation_t *group;\n\tgaliaspose_t *pose;\n\tvecV_t *vc;\n\tvec3_t *nv, *sv, *tv;\n\tvec2_t *tc;\n\tindex_t *idx;\n\n\t//don't fail on dedicated servers\n\tif (!qrenderer || !BE_VBO_Begin)\n\t\treturn;\n\n\tidx = galias->ofs_indexes;\n\ttc = galias->ofs_st_array;\n\tgroup = galias->ofsanimations;\n\n\tfor (i = 0; i < galias->numanimations; i++, group++)\n\t{\n\t\tpose = group->poseofs;\n\t\tfor (p = 0; p < group->numposes; p++, pose++)\n\t\t{\n\t\t\tvc = pose->ofsverts;\n\t\t\tnv = pose->ofsnormals;\n\t\t\tif (pose->ofssvector != 0 && pose->ofstvector != 0)\n\t\t\t{\n\t\t\t\tsv = pose->ofssvector;\n\t\t\t\ttv = pose->ofstvector;\n\n\t\t\t\tMod_AccumulateTextureVectors(vc, tc, nv, sv, tv, idx, galias->numindexes, false);\n\t\t\t\tMod_NormaliseTextureVectors(nv, sv, tv, galias->numverts, false);\n\t\t\t}\n\t\t}\n\t}\n#endif\n}\n#endif\n\n#ifndef SERVERONLY\n//looks for foo.md3_0.skin files, for dp compat\n//also try foo_0.skin, because people appear to use that too. *sigh*.\nstatic int Mod_CountSkinFiles(model_t *mod)\n{\n\tint i;\n\tchar skinfilename[MAX_QPATH];\n\tchar *modelname = mod->name;\n\t//try and add numbered skins, and then try fixed names.\n\tfor (i = 0; ; i++)\n\t{\n\t\tQ_snprintfz(skinfilename, sizeof(skinfilename), \"%s_%i.skin\", modelname, i);\n\t\tif (!COM_FCheckExists(skinfilename))\n\t\t{\n\t\t\tCOM_StripExtension(modelname, skinfilename, sizeof(skinfilename));\n\t\t\tQ_snprintfz(skinfilename+strlen(skinfilename), sizeof(skinfilename)-strlen(skinfilename), \"_%i.skin\", i);\n\t\t\tif (!COM_FCheckExists(skinfilename))\n\t\t\t\tbreak;\n\t\t}\n\t}\n\treturn i;\n}\n\nvoid Mod_LoadAliasShaders(model_t *mod)\n{\n\tgaliasinfo_t *ai = mod->meshinfo;\n\tgaliasskin_t *s;\n\tskinframe_t *f;\n\tint i, j, k;\n\tint numskins;\n\n\tunsigned int loadflags;\n\tunsigned int imageflags;\n\tchar basename[32];\n\tchar alttexpath[MAX_QPATH];\n\tuploadfmt_t skintranstype;\n\tchar *slash;\n#ifdef MD1MODELS\n#ifdef HEXEN2\n\tif (mod->flags & MFH2_TRANSPARENT)\n\t\tskintranstype = TF_H2_T7G1;\t//hexen2\n\telse\n#endif\n\t\tif (mod->flags & MFH2_HOLEY)\n\t{\n\t\t//in hexen2, the official value is 0.\n\t\t//hexen2's GL renderer ONLY also has a bug that ADDITIONALLY translates index 255. this is the 'normal' behaviour.\n\t\t//quakespasm has a bug that translates ONLY 255 and ignores 0. massive fuckup, when index 0 is more commonly used (and is also a dupe anyway, with q1's palette).\n\t\tskintranstype = mod_h2holey_bugged.ival?TF_TRANS8:TF_H2_TRANS8_0;\t//hexen2\n\t}\n#ifdef HEXEN2\n\telse if (mod->flags & MFH2_SPECIAL_TRANS)\n\t\tskintranstype = TF_H2_T4A4;\t//hexen2\n#endif\n\telse\n#endif\n\t\tskintranstype = TF_SOLID8;\n\n\tCOM_FileBase(mod->name, basename, sizeof(basename));\n\n\timageflags = 0;\n\tif (mod->engineflags & MDLF_NOTREPLACEMENTS)\n\t{\n\t\truleset_allow_sensitive_texture_replacements.flags |= CVAR_RENDERERLATCH;\n\t\tif (!ruleset_allow_sensitive_texture_replacements.ival)\n\t\t\timageflags |= IF_NOREPLACE;\n\t}\n#ifdef MD1MODELS\n\tif (mod->fromgame == fg_quake && mod_nomipmap.ival)\n\t\timageflags |= IF_NOMIPMAP;\n#endif\n\n\tslash = COM_SkipPath(mod->name);\n\tif (slash != mod->name && slash-mod->name < sizeof(alttexpath))\n\t{\n\t\tslash--;\n\t\tmemcpy(alttexpath, mod->name, slash-mod->name);\n\t\tQ_strncpyz(alttexpath+(slash-mod->name), \":models\", sizeof(alttexpath)-(slash-mod->name));\t//fuhquake compat\n\t\tslash++;\n\t}\n\telse\n\t{\n\t\tslash = mod->name;\n\t\tstrcpy(alttexpath, \"models\");\t//fuhquake compat\n\t}\n\n\n\n\tfor (ai = mod->meshinfo, numskins = 0; ai; ai = ai->nextsurf)\n\t{\n\t\tif (numskins < ai->numskins)\n\t\t\tnumskins = ai->numskins;\n\t\tMod_GenerateMeshVBO(mod, ai);\t//FIXME: shares verts\n\t}\n\tfor (i = 0; i < numskins; i++)\n\t{\n\t\tskinid_t skinid;\n\t\tskinfile_t *skinfile;\n\t\tchar *filedata;\n\t\tchar skinfilename[MAX_QPATH];\n\t\tchar *modelname = mod->name;\n\n\t\tskinid = 0;\n\t\tskinfile = NULL;\n\t\tif (qrenderer != QR_NONE)\n\t\t{\n\t\t\tQ_snprintfz(skinfilename, sizeof(skinfilename), \"%s_%i.skin\", modelname, i);\n\t\t\tfiledata = FS_LoadMallocFile(skinfilename, NULL);\n\t\t\tif (!filedata)\n\t\t\t{\n\t\t\t\tCOM_StripExtension(modelname, skinfilename, sizeof(skinfilename));\n\t\t\t\tQ_snprintfz(skinfilename+strlen(skinfilename), sizeof(skinfilename)-strlen(skinfilename), \"_%i.skin\", i);\n\t\t\t\tfiledata = FS_LoadMallocFile(skinfilename, NULL);\n\t\t\t}\n\t\t\tif (filedata)\n\t\t\t{\n\t\t\t\tskinid = Mod_ReadSkinFile(skinfilename, filedata);\n\t\t\t\tZ_Free(filedata);\n\t\t\t\tskinfile = Mod_LookupSkin(skinid);\n\t\t\t}\n\t\t}\n\n\t\tfor (ai = mod->meshinfo; ai; ai = ai->nextsurf)\n\t\t{\n\t\t\tif (i >= ai->numskins)\n\t\t\t\tcontinue;\n\n\t\t\ts = ai->ofsskins+i;\n\t\t\tfor (j = 0, f = s->frame; j < s->numframes; j++, f++)\n\t\t\t{\n\t\t\t\tif (j == 0 && skinfile)\n\t\t\t\t{\n\t\t\t\t\t//check if this skinfile has a mapping.\n\t\t\t\t\tfor (k = 0; k < skinfile->nummappings; k++)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (!strcmp(ai->surfacename, skinfile->mappings[k].surface))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tskinfile->mappings[k].shader->uses++;\t//so it doesn't blow up when the skin gets freed.\n\t\t\t\t\t\t\tf->shader = skinfile->mappings[k].shader;\n\t\t\t\t\t\t\tf->texnums = skinfile->mappings[k].texnums;\n\t\t\t\t\t\t\tskinfile->mappings[k].needsfree = 0;\t//don't free any composed texture. it'll live on as part of the model.\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tf->shader = NULL;\n\t\t\t\tif (!f->shader)\n\t\t\t\t{\n\t\t\t\t\tif (!f->defaultshader)\n\t\t\t\t\t{\n\t\t\t\t\t\tif ((ai->csurface.flags & 0x80) || dpcompat_skinfiles.ival)\t//nodraw\n\t\t\t\t\t\t\tf->shader = R_RegisterShader(f->shadername, SUF_NONE,\t\"{\\nsurfaceparm nodraw\\nsurfaceparm nodlight\\nsurfaceparm nomarks\\nsurfaceparm noshadows\\n}\\n\");\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tf->shader = R_RegisterSkin(mod, f->shadername);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\tf->shader = R_RegisterShader(f->shadername, SUF_NONE, f->defaultshader);\n\t\t\t\t}\n\n\t\t\t\tif (f->texels)\n\t\t\t\t{\n\t\t\t\t\tloadflags = SHADER_HASPALETTED | SHADER_HASDIFFUSE | SHADER_HASGLOSS | SHADER_HASTOPBOTTOM;\n\t\t\t\t\tif (r_fb_models.ival)\n\t\t\t\t\t\tloadflags |= SHADER_HASFULLBRIGHT;\n\t\t\t\t\tif (r_loadbumpmapping)\n\t\t\t\t\t\tloadflags |= SHADER_HASNORMALMAP;\n\t\t\t\t\tR_BuildLegacyTexnums(f->shader, basename, alttexpath, loadflags, imageflags, skintranstype, s->skinwidth, s->skinheight, f->texels, host_basepal);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tR_BuildDefaultTexnums(&f->texnums, f->shader, 0);\n\t\t\t}\n\t\t}\n\t\tMod_WipeSkin(skinid, false);\n\t}\n}\n#endif\n\n\n\n\n\n\n\n\n\n\n\n\n//Q1 model loading\n#ifdef MD1MODELS\n#define NUMVERTEXNORMALS\t162\nextern float\tr_avertexnormals[NUMVERTEXNORMALS][3];\n// mdltype 0 = q1, 1 = qtest, 2 = rapo/h2\n\nstatic void Q1MDL_LoadPose(galiasinfo_t *galias, dmdl_t *pq1inmodel, vecV_t *verts, vec3_t *normals, vec3_t *svec, vec3_t *tvec, dtrivertx_t *pinframe, int *seamremaps, int mdltype, unsigned int bbox[6])\n{\n\tint j;\n#ifdef _DEBUG\n\tbbox[0] = bbox[1] = bbox[2] = ~0;\n\tbbox[3] = bbox[4] = bbox[5] = 0;\n#endif\n\n#ifdef HEXEN2\n\tif (mdltype == 2)\n\t{\n\t\tfor (j = 0; j < galias->numverts; j++)\n\t\t{\n\t\t\tverts[j][0] = pinframe[seamremaps[j]].v[0]*pq1inmodel->scale[0]+pq1inmodel->scale_origin[0];\n\t\t\tverts[j][1] = pinframe[seamremaps[j]].v[1]*pq1inmodel->scale[1]+pq1inmodel->scale_origin[1];\n\t\t\tverts[j][2] = pinframe[seamremaps[j]].v[2]*pq1inmodel->scale[2]+pq1inmodel->scale_origin[2];\n#ifndef SERVERONLY\n\t\t\tVectorCopy(r_avertexnormals[pinframe[seamremaps[j]].lightnormalindex], normals[j]);\n#endif\n\t\t}\n\t}\n\telse\n#endif\n\t{\n\t\tfor (j = 0; j < pq1inmodel->numverts; j++)\n\t\t{\n#ifdef _DEBUG\n\t\t\tbbox[0] = min(bbox[0], pinframe[j].v[0]);\n\t\t\tbbox[1] = min(bbox[1], pinframe[j].v[1]);\n\t\t\tbbox[2] = min(bbox[2], pinframe[j].v[2]);\n\t\t\tbbox[3] = max(bbox[3], pinframe[j].v[0]);\n\t\t\tbbox[4] = max(bbox[4], pinframe[j].v[1]);\n\t\t\tbbox[5] = max(bbox[5], pinframe[j].v[2]);\n#endif\n\t\t\tverts[j][0] = pinframe[j].v[0]*pq1inmodel->scale[0]+pq1inmodel->scale_origin[0];\n\t\t\tverts[j][1] = pinframe[j].v[1]*pq1inmodel->scale[1]+pq1inmodel->scale_origin[1];\n\t\t\tverts[j][2] = pinframe[j].v[2]*pq1inmodel->scale[2]+pq1inmodel->scale_origin[2];\n#ifndef SERVERONLY\n\t\t\tVectorCopy(r_avertexnormals[pinframe[j].lightnormalindex], normals[j]);\n#endif\n\t\t\tif (seamremaps[j] != j)\n\t\t\t{\n\t\t\t\tVectorCopy(verts[j], verts[seamremaps[j]]);\n#ifndef SERVERONLY\n\t\t\t\tVectorCopy(normals[j], normals[seamremaps[j]]);\n#endif\n\t\t\t}\n\t\t}\n\t}\n}\nstatic void Q1MDL_LoadPoseQF16(galiasinfo_t *galias, dmdl_t *pq1inmodel, vecV_t *verts, vec3_t *normals, vec3_t *svec, vec3_t *tvec, dtrivertx_t *pinframe, int *seamremaps, int mdltype)\n{\n\t//quakeforge's MD16 format has regular 8bit stuff, trailed by an extra low-order set of the verts providing the extra 8bits of precision.\n\t//its worth noting that the model could be rendered using the high-order parts only, if your software renderer only supports that or whatever.\n\tdtrivertx_t *pinframelow =  pinframe + pq1inmodel->numverts;\n\tint j;\n\tvec3_t exscale;\n\tVectorScale(pq1inmodel->scale, 1.0/256, exscale);\n\tfor (j = 0; j < pq1inmodel->numverts; j++)\n\t{\n\t\tverts[j][0] = pinframe[j].v[0]*pq1inmodel->scale[0] + pinframelow[j].v[0]*exscale[0] + pq1inmodel->scale_origin[0];\n\t\tverts[j][1] = pinframe[j].v[1]*pq1inmodel->scale[1] + pinframelow[j].v[1]*exscale[1] + pq1inmodel->scale_origin[1];\n\t\tverts[j][2] = pinframe[j].v[2]*pq1inmodel->scale[2] + pinframelow[j].v[2]*exscale[2] + pq1inmodel->scale_origin[2];\n#ifndef SERVERONLY\n\t\tVectorCopy(r_avertexnormals[pinframe[j].lightnormalindex], normals[j]);\n#endif\n\t\tif (seamremaps[j] != j)\n\t\t{\n\t\t\tVectorCopy(verts[j], verts[seamremaps[j]]);\n#ifndef SERVERONLY\n\t\t\tVectorCopy(normals[j], normals[seamremaps[j]]);\n#endif\n\t\t}\n\t}\n}\nstatic void *Q1MDL_LoadFrameGroup (galiasinfo_t *galias, dmdl_t *pq1inmodel, model_t *loadmodel, daliasframetype_t *pframetype, int *seamremaps, int mdltype)\n{\n\tgaliaspose_t *pose;\n\tgaliasanimation_t *frame = galias->ofsanimations;\n\tdtrivertx_t\t\t*pinframe;\n\tdaliasframe_t *frameinfo;\n\tint\t\t\t\ti, k;\n\tdaliasgroup_t *ingroup;\n\tdaliasinterval_t *intervals;\n\tfloat sinter;\n\n\tvec3_t *normals, *svec, *tvec;\n\tvecV_t *verts;\n\tint aliasframesize = (mdltype == 1) ? sizeof(daliasframe_t)-16 : sizeof(daliasframe_t);\n\n\tunsigned int bbox[6];\n\n#ifdef SERVERONLY\n\tnormals = NULL;\n\tsvec = NULL;\n\ttvec = NULL;\n#endif\n\n\tfor (i = 0; i < pq1inmodel->numframes; i++)\n\t{\n\t\tframe->action = -1;\n\t\tframe->actionweight = 0;\n\t\tswitch(LittleLong(pframetype->type))\n\t\t{\n\t\tcase ALIAS_SINGLE:\n\t\t\tframeinfo = (daliasframe_t*)((char *)(pframetype+1)); // qtest aliasframe is a subset\n\t\t\tpinframe = (dtrivertx_t*)((char*)frameinfo+aliasframesize);\n#ifndef SERVERONLY\n\t\t\tpose = (galiaspose_t *)ZG_Malloc(&loadmodel->memgroup, sizeof(galiaspose_t) + (sizeof(vecV_t)+sizeof(vec3_t)*3)*galias->numverts);\n#else\n\t\t\tpose = (galiaspose_t *)ZG_Malloc(&loadmodel->memgroup, sizeof(galiaspose_t) + (sizeof(vecV_t))*galias->numverts);\n#endif\n\t\t\tframe->poseofs = pose;\n\t\t\tframe->numposes = 1;\n\t\t\tgalias->numanimations++;\n\n\t\t\tif (mdltype == 1)\n\t\t\t\tframe->name[0] = '\\0';\n\t\t\telse\n\t\t\t\tQ_strncpyz(frame->name, frameinfo->name, sizeof(frame->name));\n\n\t\t\tverts = (vecV_t *)(pose+1);\n\t\t\tpose->ofsverts = verts;\n#ifndef SERVERONLY\n\t\t\tnormals = (vec3_t*)&verts[galias->numverts];\n\t\t\tsvec = &normals[galias->numverts];\n\t\t\ttvec = &svec[galias->numverts];\n\t\t\tpose->ofsnormals = normals;\n\t\t\tpose->ofssvector = svec;\n\t\t\tpose->ofstvector = tvec;\n#endif\n\n\t\t\tif (mdltype & 16)\n\t\t\t{\n\t\t\t\tQ1MDL_LoadPoseQF16(galias, pq1inmodel, verts, normals, svec, tvec, pinframe, seamremaps, mdltype);\n\t\t\t\tpframetype = (daliasframetype_t *)&pinframe[pq1inmodel->numverts*2];\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tQ1MDL_LoadPose(galias, pq1inmodel, verts, normals, svec, tvec, pinframe, seamremaps, mdltype, bbox);\n\t\t\t\tpframetype = (daliasframetype_t *)&pinframe[pq1inmodel->numverts];\n\n#ifdef _DEBUG\n\t\t\t\tif ((bbox[3] > frameinfo->bboxmax.v[0] || bbox[4] > frameinfo->bboxmax.v[1] || bbox[5] > frameinfo->bboxmax.v[2] ||\n\t\t\t\t\tbbox[0] < frameinfo->bboxmin.v[0] || bbox[1] < frameinfo->bboxmin.v[1] || bbox[2] < frameinfo->bboxmin.v[2]) && !galias->warned)\n#else\n\t\t\t\tif (galias->numverts && pinframe[0].v[2] > frameinfo->bboxmax.v[2] && !galias->warned)\n#endif\n\t\t\t\t{\n\t\t\t\t\tCon_DPrintf(CON_WARNING\"%s has incorrect frame bounds\\n\", loadmodel->name);\n\t\t\t\t\tgalias->warned = true;\n\t\t\t\t}\n\t\t\t}\n\n\n//\t\t\tGL_GenerateNormals((float*)verts, (float*)normals, (int *)((char *)galias + galias->ofs_indexes), galias->numindexes/3, galias->numverts);\n\n\t\t\tbreak;\n\n\t\tcase ALIAS_GROUP:\n\t\tcase ALIAS_GROUP_SWAPPED: // prerelease\n\t\t\tingroup = (daliasgroup_t *)(pframetype+1);\n\n\t\t\tframe->numposes = LittleLong(ingroup->numframes);\n#ifdef SERVERONLY\n\t\t\tpose = (galiaspose_t *)ZG_Malloc(&loadmodel->memgroup, frame->numposes*(sizeof(galiaspose_t) + sizeof(vecV_t)*galias->numverts));\n\t\t\tverts = (vecV_t *)(pose+frame->numposes);\n#else\n\t\t\tpose = (galiaspose_t *)ZG_Malloc(&loadmodel->memgroup, frame->numposes*(sizeof(galiaspose_t) + (sizeof(vecV_t)+sizeof(vec3_t)*3)*galias->numverts));\n\t\t\tverts = (vecV_t *)(pose+frame->numposes);\n\t\t\tnormals = (vec3_t*)&verts[galias->numverts];\n\t\t\tsvec = &normals[galias->numverts];\n\t\t\ttvec = &svec[galias->numverts];\n#endif\n\n\t\t\tframe->poseofs = pose;\n\t\t\tframe->loop = true;\n\t\t\tgalias->numanimations++;\n\n\t\t\tintervals = (daliasinterval_t *)(ingroup+1);\n\t\t\tif (frame->numposes == 0)\n\t\t\t\tframe->rate = 10;\n\t\t\telse\n\t\t\t{\n\t\t\t\tsinter = LittleFloat(intervals->interval);\n\t\t\t\tif (sinter <= 0)\n\t\t\t\t\tsinter = 0.1;\n\t\t\t\tframe->rate = 1/sinter;\n\t\t\t}\n\n\t\t\tpinframe = (dtrivertx_t *)(intervals+frame->numposes);\n\t\t\tfor (k = 0; k < frame->numposes; k++)\n\t\t\t{\n\t\t\t\tpose->ofsverts = verts;\n#ifndef SERVERONLY\n\t\t\t\tpose->ofsnormals = normals;\n\t\t\t\tpose->ofssvector = svec;\n\t\t\t\tpose->ofstvector = tvec;\n#endif\n\n\t\t\t\tframeinfo = (daliasframe_t*)pinframe;\n\t\t\t\tpinframe = (dtrivertx_t *)((char *)frameinfo + aliasframesize);\n\n\t\t\t\tif (k == 0)\n\t\t\t\t{\n\t\t\t\t\tif (mdltype == 1)\n\t\t\t\t\t\tframe->name[0] = '\\0';\n\t\t\t\t\telse\n\t\t\t\t\t\tQ_strncpyz(frame->name, frameinfo->name, sizeof(frame->name));\n\t\t\t\t}\n\n\t\t\t\tif (mdltype & 16)\n\t\t\t\t{\n\t\t\t\t\tQ1MDL_LoadPoseQF16(galias, pq1inmodel, verts, normals, svec, tvec, pinframe, seamremaps, mdltype);\n\t\t\t\t\tpinframe += pq1inmodel->numverts*2;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tQ1MDL_LoadPose(galias, pq1inmodel, verts, normals, svec, tvec, pinframe, seamremaps, mdltype, bbox);\n\n#ifdef _DEBUG\n\t\t\t\t\tif ((bbox[3] > frameinfo->bboxmax.v[0] || bbox[4] > frameinfo->bboxmax.v[1] || bbox[5] > frameinfo->bboxmax.v[2] ||\n\t\t\t\t\t\tbbox[0] < frameinfo->bboxmin.v[0] || bbox[1] < frameinfo->bboxmin.v[1] || bbox[2] < frameinfo->bboxmin.v[2] ||\n#else\n\t\t\t\t\tif (galias->numverts && (pinframe[0].v[2] > frameinfo->bboxmax.v[2] ||\n#endif\n\t\t\t\t\t\tframeinfo->bboxmin.v[0] < ingroup->bboxmin.v[0] || frameinfo->bboxmin.v[1] < ingroup->bboxmin.v[1] || frameinfo->bboxmin.v[2] < ingroup->bboxmin.v[2] ||\n\t\t\t\t\t\tframeinfo->bboxmax.v[0] > ingroup->bboxmax.v[0] || frameinfo->bboxmax.v[1] > ingroup->bboxmax.v[1] || frameinfo->bboxmax.v[2] > ingroup->bboxmax.v[2]) && !galias->warned)\n\t\t\t\t\t{\n\t\t\t\t\t\tCon_DPrintf(CON_WARNING\"%s has incorrect frame bounds\\n\", loadmodel->name);\n\t\t\t\t\t\tgalias->warned = true;\n\t\t\t\t\t}\n\t\t\t\t\tpinframe += pq1inmodel->numverts;\n\t\t\t\t}\n\n#ifndef SERVERONLY\n\t\t\t\tverts = (vecV_t*)&tvec[galias->numverts];\n\t\t\t\tnormals = (vec3_t*)&verts[galias->numverts];\n\t\t\t\tsvec = &normals[galias->numverts];\n\t\t\t\ttvec = &svec[galias->numverts];\n#else\n\t\t\t\tverts = &verts[galias->numverts];\n#endif\n\t\t\t\tpose++;\n\t\t\t}\n\n//\t\t\tGL_GenerateNormals((float*)verts, (float*)normals, (int *)((char *)galias + galias->ofs_indexes), galias->numindexes/3, galias->numverts);\n\n\t\t\tpframetype = (daliasframetype_t *)pinframe;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tCon_Printf(CON_ERROR \"Bad frame type in %s\\n\", loadmodel->name);\n\t\t\treturn NULL;\n\t\t}\n\t\tframe++;\n\t}\n\treturn pframetype;\n}\n\n//greatly reduced version of Q1_LoadSkins\n//just skips over the data\nstatic void *Q1MDL_LoadSkins_SV (galiasinfo_t *galias, dmdl_t *pq1inmodel, daliasskintype_t *pskintype, unsigned int skintranstype)\n{\n\tint i;\n\tint s;\n\tint *count;\n\tfloat *intervals;\n\tqbyte *data;\n\n\ts = pq1inmodel->skinwidth*pq1inmodel->skinheight;\n\tfor (i = 0; i < pq1inmodel->numskins; i++)\n\t{\n\t\tswitch(LittleLong(pskintype->type))\n\t\t{\n\t\tcase ALIAS_SKIN_SINGLE:\n\t\t\tpskintype = (daliasskintype_t *)((char *)(pskintype+1)+s);\n\t\t\tbreak;\n\n\t\tdefault:\n\t\t\tcount = (int *)(pskintype+1);\n\t\t\tintervals = (float *)(count+1);\n\t\t\tdata = (qbyte *)(intervals + LittleLong(*count));\n\t\t\tdata += s*LittleLong(*count);\n\t\t\tpskintype = (daliasskintype_t *)data;\n\t\t\tbreak;\n\t\t}\n\t}\n\tgalias->numskins=pq1inmodel->numskins;\n\treturn pskintype;\n}\n\n#ifndef SERVERONLY\nstatic cvar_t dpcompat_nofloodfill = CVARD(\"dpcompat_nofloodfill\", \"0\", \"Disables the q1mdl floodfill. Setting this to 1 may result in blue seams on the vanilla quake models.\");\n/*\n=================\nMod_FloodFillSkin\n\nFill background pixels so mipmapping doesn't have haloes - Ed\n=================\n*/\n\ntypedef struct\n{\n\tshort\t\tx, y;\n} floodfill_t;\n\n// must be a power of 2\n#define FLOODFILL_FIFO_SIZE 0x1000\n#define FLOODFILL_FIFO_MASK (FLOODFILL_FIFO_SIZE - 1)\n\n#define FLOODFILL_STEP( off, dx, dy ) \\\ndo{ \\\n\tif (pos[off] == fillcolor) \\\n\t{ \\\n\t\tpos[off] = 255; \\\n\t\tfifo[inpt].x = x + (dx), fifo[inpt].y = y + (dy); \\\n\t\tinpt = (inpt + 1) & FLOODFILL_FIFO_MASK; \\\n\t} \\\n\telse if (pos[off] != 255) fdc = pos[off]; \\\n}while(0)\n\nstatic void Mod_FloodFillSkin( qbyte *skin, int skinwidth, int skinheight )\n{\n\tqbyte\t\t\t\tfillcolor = *skin; // assume this is the pixel to fill\n\tfloodfill_t\t\t\tfifo[FLOODFILL_FIFO_SIZE];\n\tint\t\t\t\t\tinpt = 0, outpt = 0;\n\tint\t\t\t\t\tfilledcolor = -1;\n\tint\t\t\t\t\ti;\n\n\tif (dpcompat_nofloodfill.ival || skinwidth > 0x7fffu || skinheight > 0x7fffu)\n\t\treturn;\n\n\tif (filledcolor == -1)\n\t{\n\t\tfilledcolor = 0;\n\t\t// attempt to find opaque black\n\t\tfor (i = 0; i < 256; ++i)\n\t\t\tif (d_8to24rgbtable[i] == (255 << 0)) // alpha 1.0\n\t\t\t{\n\t\t\t\tfilledcolor = i;\n\t\t\t\tbreak;\n\t\t\t}\n\t}\n\n\t// can't fill to filled color or to transparent color (used as visited marker)\n\tif ((fillcolor == filledcolor) || (fillcolor == 255))\n\t{\n\t\t//printf( \"not filling skin from %d to %d\\n\", fillcolor, filledcolor );\n\t\treturn;\n\t}\n\n\tfifo[inpt].x = 0, fifo[inpt].y = 0;\n\tinpt = (inpt + 1) & FLOODFILL_FIFO_MASK;\n\n\twhile (outpt != inpt)\n\t{\n\t\tint\t\t\tx = fifo[outpt].x, y = fifo[outpt].y;\n\t\tint\t\t\tfdc = filledcolor;\n\t\tqbyte\t\t*pos = &skin[x + skinwidth * y];\n\n\t\toutpt = (outpt + 1) & FLOODFILL_FIFO_MASK;\n\n\t\tif (x > 0)\t\t\t\tFLOODFILL_STEP( -1, -1, 0 );\n\t\tif (x < skinwidth - 1)\tFLOODFILL_STEP( 1, 1, 0 );\n\t\tif (y > 0)\t\t\t\tFLOODFILL_STEP( -skinwidth, 0, -1 );\n\t\tif (y < skinheight - 1)\tFLOODFILL_STEP( skinwidth, 0, 1 );\n\t\tskin[x + skinwidth * y] = fdc;\n\t}\n}\n\nstatic void *Q1MDL_LoadSkins_GL (galiasinfo_t *galias, dmdl_t *pq1inmodel, model_t *loadmodel, daliasskintype_t *pskintype, uploadfmt_t skintranstype)\n{\n\tskinframe_t *frames;\n\tint i;\n\tint s, t;\n\tfloat sinter;\n\tdaliasskingroup_t *count;\n\tdaliasskininterval_t *intervals;\n\tqbyte *data, *saved;\n\tgaliasskin_t *outskin = galias->ofsskins;\n\n\ts = pq1inmodel->skinwidth*pq1inmodel->skinheight;\n\tfor (i = 0; i < pq1inmodel->numskins; i++)\n\t{\n\t\tswitch(LittleLong(pskintype->type))\n\t\t{\n\t\tcase ALIAS_SKIN_SINGLE:\n\t\t\toutskin->skinwidth = pq1inmodel->skinwidth;\n\t\t\toutskin->skinheight = pq1inmodel->skinheight;\n\n//but only preload it if we have no replacement.\n\t\t\toutskin->numframes=1;\n\n\t\t\t//the actual texture gets loaded after the shader.\n\t\t\tframes = ZG_Malloc(&loadmodel->memgroup, sizeof(*frames)+s);\n\t\t\tsaved = (qbyte*)(frames+1);\n\t\t\tframes[0].texels = saved;\n\t\t\tmemcpy(saved, pskintype+1, s);\n\t\t\tif (i == 0) //Vanilla bug: ONLY skin 0 is flood-filled (the vanilla code operates on a cached 'skin' variable that does NOT get updated between skins reflooding skin 0). We still don't like flood fills either. Hexen2 has the same issue.\n\t\t\t\tMod_FloodFillSkin(saved, outskin->skinwidth, outskin->skinheight);\n\n\t\t\tQ_snprintfz(frames[0].shadername, sizeof(frames[0].shadername), \"%s_%i.lmp\", loadmodel->name, i);\n\t\t\tframes[0].shader = NULL;\n\t\t\tframes[0].defaultshader = NULL;\n\t\t\toutskin->frame = frames;\n\n\t\t\tswitch(skintranstype)\n\t\t\t{\n\t\t\tdefault:\t//urk\n\t\t\tcase TF_SOLID8:\n\t\t\t\tframes[0].defaultshader = NULL;\t//default skin...\n\t\t\t\tbreak;\n\t\t\tcase TF_H2_T7G1:\n\t\t\t\tframes[0].defaultshader =\n\t\t\t\t\t\"{\\n\"\n//\t\t\t\t\t\t\"program defaultskin\\n\"\n\t\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\t\"map $diffuse\\n\"\n\t\t\t\t\t\t\t\"blendfunc gl_src_alpha gl_one_minus_src_alpha\\n\"\n\t\t\t\t\t\t\t\"alphagen entity\\n\"\n\t\t\t\t\t\t\t\"rgbgen lightingDiffuse\\n\"\n\t\t\t\t\t\t\t\"depthwrite\\n\"\n\t\t\t\t\t\t\"}\\n\"\n\t\t\t\t\t\"}\\n\";\n\t\t\t\tbreak;\n\t\t\tcase TF_H2_TRANS8_0:\n\t\t\t\tframes[0].defaultshader =\n\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\"glslprogram defaultskin#MASK=0.5#MASKLT\\n\"\n\t\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\t\"map $diffuse\\n\"\n\t\t\t\t\t\t\t\"blendfunc gl_src_alpha gl_one_minus_src_alpha\\n\"\n\t\t\t\t\t\t\t\"alphafunc ge128\\n\"\n\t\t\t\t\t\t\t\"rgbgen lightingDiffuse\\n\"\n\t\t\t\t\t\t\t\"alphagen entity\\n\"\n\t\t\t\t\t\t\t\"depthwrite\\n\"\n\t\t\t\t\t\t\"}\\n\"\n\t\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\t\"map $loweroverlay\\n\"\n\t\t\t\t\t\t\t\"rgbgen bottomcolor\\n\"\n\t\t\t\t\t\t\t\"blendfunc gl_src_alpha gl_one\\n\"\n\t\t\t\t\t\t\"}\\n\"\n\t\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\t\"map $upperoverlay\\n\"\n\t\t\t\t\t\t\t\"rgbgen topcolor\\n\"\n\t\t\t\t\t\t\t\"blendfunc gl_src_alpha gl_one\\n\"\n\t\t\t\t\t\t\"}\\n\"\n\t\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\t\"map $fullbright\\n\"\n\t\t\t\t\t\t\t\"blendfunc add\\n\"\n\t\t\t\t\t\t\"}\\n\"\n\t\t\t\t\t\"}\\n\";\n\t\t\t\tbreak;\n\t\t\tcase TF_H2_T4A4:\n\t\t\t\tframes[0].defaultshader =\n\t\t\t\t\t\"{\\n\"\n//\t\t\t\t\t\t\"program defaultskin\\n\"\n\t\t\t\t\t\t\"cull disable\\n\"\n\t\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\t\"map $diffuse\\n\"\n\t\t\t\t\t\t\t\"blendfunc gl_one_minus_src_alpha gl_src_alpha\\n\"\n\t\t\t\t\t\t\t\"alphagen entity\\n\"\n\t\t\t\t\t\t\t\"rgbgen lightingDiffuse\\n\"\n\t\t\t\t\t\t\t\"depthwrite\\n\"\n\t\t\t\t\t\t\"}\\n\"\n\t\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\t\"map $loweroverlay\\n\"\n\t\t\t\t\t\t\t\"rgbgen bottomcolor\\n\"\n\t\t\t\t\t\t\t\"blendfunc gl_src_alpha gl_one\\n\"\n\t\t\t\t\t\t\"}\\n\"\n\t\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\t\"map $upperoverlay\\n\"\n\t\t\t\t\t\t\t\"rgbgen topcolor\\n\"\n\t\t\t\t\t\t\t\"blendfunc gl_src_alpha gl_one\\n\"\n\t\t\t\t\t\t\"}\\n\"\n\t\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\t\"map $fullbright\\n\"\n\t\t\t\t\t\t\t\"blendfunc add\\n\"\n\t\t\t\t\t\t\"}\\n\"\n\t\t\t\t\t\"}\\n\";\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tpskintype = (daliasskintype_t *)((char *)(pskintype+1)+s);\n\t\t\tbreak;\n\n\t\tdefault:\n\t\t\toutskin->skinwidth = pq1inmodel->skinwidth;\n\t\t\toutskin->skinheight = pq1inmodel->skinheight;\n\t\t\tcount = (daliasskingroup_t*)(pskintype+1);\n\t\t\tintervals = (daliasskininterval_t *)(count+1);\n\t\t\toutskin->numframes = LittleLong(count->numskins);\n\t\t\tdata = (qbyte *)(intervals + outskin->numframes);\n\t\t\tframes = ZG_Malloc(&loadmodel->memgroup, sizeof(*frames)*outskin->numframes);\n\t\t\toutskin->frame = frames;\n\t\t\tsinter = LittleFloat(intervals[0].interval);\n\t\t\tif (sinter <= 0)\n\t\t\t\tsinter = 0.1;\n\t\t\toutskin->skinspeed = 1/sinter;\n\n\t\t\tfor (t = 0; t < outskin->numframes; t++,data+=s)\n\t\t\t{\n\t\t\t\tframes[t].texels = ZG_Malloc(&loadmodel->memgroup, s);\n\t\t\t\tmemcpy(frames[t].texels, data, s);\n\t\t\t\t//other engines apparently don't flood fill. because flood filling is horrible, we won't either.\n\t\t\t\t//Mod_FloodFillSkin(frames[t].texels, outskin->skinwidth, outskin->skinheight);\n\n\t\t\t\tQ_snprintfz(frames[t].shadername, sizeof(frames[t].shadername), \"%s_%i_%i.lmp\", loadmodel->name, i, t);\n\t\t\t\tframes[t].defaultshader = NULL;\n\t\t\t}\n\t\t\tpskintype = (daliasskintype_t *)data;\n\t\t\tbreak;\n\t\t}\n\t\toutskin++;\n\t}\n\tgalias->numskins=pq1inmodel->numskins;\n\treturn pskintype;\n}\n#endif\n\nstatic void Mesh_HandleFramegroupsFile(model_t *mod, galiasinfo_t *galias)\n{\t//use ONLY with vertex models.\n\tunsigned int numanims, a, p, g, oldnumanims = galias->numanimations, targpose;\n\tgaliasanimation_t *o, *oldanims = galias->ofsanimations, *frame;\n\tframeinfo_t *framegroups = ParseFrameInfo(mod, &numanims);\n\tif (framegroups)\n\t{\n\t\tgalias->ofsanimations = o = ZG_Malloc(&mod->memgroup, sizeof(*galias->ofsanimations) * numanims);\n\t\tfor (a = 0; a < numanims; a++, o++)\n\t\t{\n\t\t\to->poseofs = ZG_Malloc(&mod->memgroup, sizeof(*o->poseofs) * framegroups[a].posecount);\n\t\t\tfor (p = 0; p < framegroups[a].posecount; p++)\n\t\t\t{\n\t\t\t\tif (framegroups[a].posesarray)\n\t\t\t\t\ttargpose = framegroups[a].poses[p];\n\t\t\t\telse\n\t\t\t\t\ttargpose = framegroups[a].firstpose + p;\n\t\t\t\tfor (g = 0, frame = oldanims; g < oldnumanims; g++, frame++)\n\t\t\t\t{\n\t\t\t\t\tif (targpose < frame->numposes)\n\t\t\t\t\t\tbreak;\n\t\t\t\t\ttargpose -= frame->numposes;\n\t\t\t\t}\n\t\t\t\tif (g == oldnumanims)\n\t\t\t\t\tbreak;\n\t\t\t\to->poseofs[p] = frame->poseofs[targpose];\n\t\t\t}\n\t\t\to->numposes = p;\n\t\t\to->rate = framegroups[a].fps;\n\t\t\to->loop = framegroups[a].loop;\n\t\t\to->events = framegroups[a].events;\n\t\t\to->action = framegroups[a].action;\n\t\t\to->actionweight = framegroups[a].actionweight;\n\t\t\tQ_strncpyz(o->name, framegroups[a].name, sizeof(o->name));\n\t\t}\n\t\tgalias->numanimations = numanims;\n\t\tfree(framegroups);\n\t}\n}\n\nstatic qboolean QDECL Mod_LoadQ1Model (model_t *mod, void *buffer, size_t fsize)\n{\n#ifndef SERVERONLY\n\tvec2_t *st_array;\n\tint j;\n\tfloat halftexel = mod_halftexel.ival?0.5:0;\n#endif\n\tint version;\n\tint i, onseams;\n\tdstvert_t *pinstverts;\n\tdtriangle_t *pinq1triangles;\n\tint *seamremap;\n\tindex_t *indexes;\n\tdaliasskintype_t *skinstart;\n\tuploadfmt_t skintranstype;\n\tqboolean sixteenbit;\n\n\tint size;\n\tunsigned int hdrsize;\n\tvoid *end;\n\tqboolean qtest = false;\n#ifdef HEXEN2\n\tdh2triangle_t *pinh2triangles;\n\tqboolean rapo = false;\n#endif\n\tgaliasinfo_t *galias;\n\tdmdl_t *pq1inmodel = (dmdl_t *)buffer;\n\n\thdrsize = sizeof(dmdl_t) - sizeof(int);\n\n\tmod->engineflags |= MDLF_NEEDOVERBRIGHT;\n\n\tsixteenbit = pq1inmodel->ident == LittleLong(('6'<<24)+('1'<<16)+('D'<<8)+'M');\t//quakeforge's 16bit mdls\n\n\tversion = LittleLong(pq1inmodel->version);\n\tif (version == QTESTALIAS_VERSION && !sixteenbit)\n\t{\n\t\thdrsize = (size_t)&((dmdl_t*)NULL)->flags;\n\t\tqtest = true;\n\t}\n#ifdef HEXEN2\n\telse if (version == 50 && !sixteenbit)\n\t{\n\t\thdrsize = sizeof(dmdl_t);\n\t\trapo = true;\n\t}\n#endif\n\telse if (version != ALIAS_VERSION)\n\t{\n\t\tCon_Printf (CON_ERROR \"%s has wrong version number (%i should be %i)\\n\",\n\t\t\t\t mod->name, version, ALIAS_VERSION);\n\t\treturn false;\n\t}\n\n\tseamremap = (int*)pq1inmodel;\t//I like overloading locals.\n\n\ti = hdrsize/4 - 1;\n\n\tfor (; i >= 0; i--)\n\t\tseamremap[i] = LittleLong(seamremap[i]);\n\n\tif (pq1inmodel->numframes < 1 ||\n\t\tpq1inmodel->numskins < 1 ||\n\t\tpq1inmodel->numtris < 1 ||\n\t\tpq1inmodel->numverts < 3 ||\n\t\tpq1inmodel->skinheight < 1 ||\n\t\tpq1inmodel->skinwidth < 1)\n\t{\n\t\tCon_Printf(CON_ERROR \"Model %s has an invalid quantity\\n\", mod->name);\n\t\treturn false;\n\t}\n\n\tif (qtest)\n\t\tmod->flags = 0; // Qtest has no flags in header\n\telse\n\t\tmod->flags = pq1inmodel->flags;\n\n\tsize = sizeof(galiasinfo_t)\n#ifndef SERVERONLY\n\t\t+ pq1inmodel->numskins*sizeof(galiasskin_t)\n#endif\n\t\t+ pq1inmodel->numframes*sizeof(galiasanimation_t);\n\n\tgalias = ZG_Malloc(&mod->memgroup, size);\n\tMod_DefaultMesh(galias, mod->name, 0);\n\tgalias->ofsanimations = (galiasanimation_t*)(galias+1);\n#ifndef SERVERONLY\n\tgalias->ofsskins = (galiasskin_t*)(galias->ofsanimations+pq1inmodel->numframes);\n#endif\n\tgalias->nextsurf = 0;\n\n\tmod->numframes = pq1inmodel->numframes;\n\n//skins\n\tskinstart = (daliasskintype_t *)((char*)pq1inmodel+hdrsize);\n\n#ifdef HEXEN2\n\tif( mod->flags & MFH2_TRANSPARENT )\n\t\tskintranstype = TF_H2_T7G1;\t//hexen2\n\telse\n#endif\n\t if( mod->flags & MFH2_HOLEY )\n\t\tskintranstype = TF_H2_TRANS8_0;\t//hexen2\n#ifdef HEXEN2\n\telse if( mod->flags & MFH2_SPECIAL_TRANS )\n\t\tskintranstype = TF_H2_T4A4;\t//hexen2\n#endif\n\telse\n\t\tskintranstype = TF_SOLID8;\n\n\tswitch(qrenderer)\n\t{\n\tdefault:\n#ifndef SERVERONLY\n\t\tpinstverts = (dstvert_t *)Q1MDL_LoadSkins_GL(galias, pq1inmodel, mod, skinstart, skintranstype);\n\t\tbreak;\n#endif\n\tcase QR_NONE:\n\t\tpinstverts = (dstvert_t *)Q1MDL_LoadSkins_SV(galias, pq1inmodel, skinstart, skintranstype);\n\t\tbreak;\n\t}\n\n#ifdef HEXEN2\n\tif (rapo)\n\t{\n\t\t/*each triangle can use one coord and one st, for each vert, that's a lot of combinations*/\n#ifdef SERVERONLY\n\t\t/*separate st + vert lists*/\n\t\tpinh2triangles = (dh2triangle_t *)&pinstverts[pq1inmodel->num_st];\n\n\t\tseamremap = BZ_Malloc(sizeof(*seamremap)*pq1inmodel->numtris*3);\n\n\t\tgalias->numverts = pq1inmodel->numverts;\n\t\tgalias->numindexes = pq1inmodel->numtris*3;\n\t\tindexes = ZG_Malloc(&mod->memgroup, galias->numindexes*sizeof(*indexes));\n\t\tgalias->ofs_indexes = indexes;\n\t\tfor (i = 0; i < pq1inmodel->numverts; i++)\n\t\t\tseamremap[i] = i;\n\t\tfor (i = 0; i < pq1inmodel->numtris; i++)\n\t\t{\n\t\t\tindexes[i*3+0] = LittleShort(pinh2triangles[i].vertindex[0]);\n\t\t\tindexes[i*3+1] = LittleShort(pinh2triangles[i].vertindex[1]);\n\t\t\tindexes[i*3+2] = LittleShort(pinh2triangles[i].vertindex[2]);\n\t\t}\n#else\n\t\tint t, v, k;\n\t\tint *stremap;\n\t\t/*separate st + vert lists*/\n\t\tpinh2triangles = (dh2triangle_t *)&pinstverts[pq1inmodel->num_st];\n\n\t\tseamremap = BZ_Malloc(sizeof(int)*pq1inmodel->numtris*6);\n\t\tstremap = seamremap + pq1inmodel->numtris*3;\n\n\t\t/*output the indicies as we figure out which verts we want*/\n\t\tgalias->numindexes = pq1inmodel->numtris*3;\n\t\tindexes = ZG_Malloc(&mod->memgroup, galias->numindexes*sizeof(*indexes));\n\t\tgalias->ofs_indexes = indexes;\n\t\tfor (i = 0; i < pq1inmodel->numtris; i++)\n\t\t{\n\t\t\tfor (j = 0; j < 3; j++)\n\t\t\t{\n\t\t\t\tv = LittleShort(pinh2triangles[i].vertindex[j]);\n\t\t\t\tt = LittleShort(pinh2triangles[i].stindex[j]);\n\t\t\t\tif (pinstverts[t].onseam && !pinh2triangles[i].facesfront)\n\t\t\t\t\tt += pq1inmodel->num_st;\n\t\t\t \tfor (k = 0; k < galias->numverts; k++) /*big fatoff slow loop*/\n\t\t\t\t{\n\t\t\t\t\tif (stremap[k] == t && seamremap[k] == v)\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tif (k == galias->numverts)\n\t\t\t\t{\n\t\t\t\t\tgalias->numverts++;\n\t\t\t\t\tstremap[k] = t;\n\t\t\t\t\tseamremap[k] = v;\n\t\t\t\t}\n\t\t\t\tindexes[i*3+j] = k;\n\t\t\t}\n\t\t}\n\n\t\tst_array = ZG_Malloc(&mod->memgroup, sizeof(*st_array)*(galias->numverts));\n\t\tgalias->ofs_st_array = st_array;\n\t\t/*generate our st_array now we know which vertexes we want*/\n\t\tfor (k = 0; k < galias->numverts; k++)\n\t\t{\n\t\t\tif (stremap[k] > pq1inmodel->num_st)\n\t\t\t{\t/*onseam verts? shrink the index, and add half a texture width to the s coord*/\n\t\t\t\tst_array[k][0] = 0.5+(LittleLong(pinstverts[stremap[k]-pq1inmodel->num_st].s)+halftexel)/(float)pq1inmodel->skinwidth;\n\t\t\t\tst_array[k][1] = (LittleLong(pinstverts[stremap[k]-pq1inmodel->num_st].t)+halftexel)/(float)pq1inmodel->skinheight;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tst_array[k][0] = (LittleLong(pinstverts[stremap[k]].s)+halftexel)/(float)pq1inmodel->skinwidth;\n\t\t\t\tst_array[k][1] = (LittleLong(pinstverts[stremap[k]].t)+halftexel)/(float)pq1inmodel->skinheight;\n\t\t\t}\n\t\t}\n#endif\n\t\tend = &pinh2triangles[pq1inmodel->numtris];\n\n\t\tif (Q1MDL_LoadFrameGroup(galias, pq1inmodel, mod, (daliasframetype_t *)end, seamremap, 2) == NULL)\n\t\t{\n\t\t\tBZ_Free(seamremap);\n\t\t\tZG_FreeGroup(&mod->memgroup);\n\t\t\treturn false;\n\t\t}\n\n\t\tBZ_Free(seamremap);\n\t}\n\telse\n#endif\n\t{\n\t\t/*onseam means +=skinwidth/2\n\t\tverticies that are marked as onseam potentially generate two output verticies.\n\t\tthe triangle chooses which side based upon its 'onseam' field.\n\t\t*/\n\n\t\t//count number of verts that are onseam.\n\t\tfor (onseams=0,i = 0; i < pq1inmodel->numverts; i++)\n\t\t{\n\t\t\tif (pinstverts[i].onseam)\n\t\t\t\tonseams++;\n\t\t}\n\t\tseamremap = BZ_Malloc(sizeof(*seamremap)*pq1inmodel->numverts);\n\n\t\tgalias->numverts = pq1inmodel->numverts+onseams;\n\n\t\t//st\n#ifndef SERVERONLY\n\t\tst_array = ZG_Malloc(&mod->memgroup, sizeof(*st_array)*(pq1inmodel->numverts+onseams));\n\t\tgalias->ofs_st_array = st_array;\n\t\tfor (j=pq1inmodel->numverts,i = 0; i < pq1inmodel->numverts; i++)\n\t\t{\n\t\t\tst_array[i][0] = (LittleLong(pinstverts[i].s)+halftexel)/(float)pq1inmodel->skinwidth;\n\t\t\tst_array[i][1] = (LittleLong(pinstverts[i].t)+halftexel)/(float)pq1inmodel->skinheight;\n\n\t\t\tif (pinstverts[i].onseam)\n\t\t\t{\n\t\t\t\tif (pinstverts[i].onseam != 0x20 && !galias->warned)\n\t\t\t\t{\n\t\t\t\t\tCon_DPrintf(CON_WARNING \"Model %s has an invalid seam flag, which may crash software-rendered engines\\n\", mod->name);\n\t\t\t\t\t//1 == ALIAS_LEFT_CLIP\n\t\t\t\t\tgalias->warned = true;\n\t\t\t\t}\n\t\t\t\tst_array[j][0] = st_array[i][0]+0.5;\n\t\t\t\tst_array[j][1] = st_array[i][1];\n\t\t\t\tseamremap[i] = j;\n\t\t\t\tj++;\n\t\t\t}\n\t\t\telse\n\t\t\t\tseamremap[i] = i;\n\t\t}\n#else\n\t\tfor (i = 0; i < pq1inmodel->numverts; i++)\n\t\t{\n\t\t\tseamremap[i] = i;\n\t\t}\n#endif\n\n\t\t//trianglelists;\n\t\tpinq1triangles = (dtriangle_t *)&pinstverts[pq1inmodel->numverts];\n\n\t\tgalias->numindexes = pq1inmodel->numtris*3;\n\t\tindexes = ZG_Malloc(&mod->memgroup, galias->numindexes*sizeof(*indexes));\n\t\tgalias->ofs_indexes = indexes;\n\t\tfor (i=0 ; i<pq1inmodel->numtris ; i++)\n\t\t{\n\t\t\tunsigned int v1 = LittleLong(pinq1triangles[i].vertindex[0]);\n\t\t\tunsigned int v2 = LittleLong(pinq1triangles[i].vertindex[1]);\n\t\t\tunsigned int v3 = LittleLong(pinq1triangles[i].vertindex[2]);\n\t\t\tif (v1 >= pq1inmodel->numverts || v2 >= pq1inmodel->numverts || v3 >= pq1inmodel->numverts)\n\t\t\t{\n\t\t\t\tCon_DPrintf(CON_ERROR\"%s has invalid triangle (%u %u %u > %u)\\n\", mod->name, v1, v2, v3, pq1inmodel->numverts);\n\t\t\t\tv1 = v2 = v3 = 0;\n\t\t\t}\n\t\t\tif (!pinq1triangles[i].facesfront)\n\t\t\t{\n\t\t\t\tindexes[i*3+0] = seamremap[v1];\n\t\t\t\tindexes[i*3+1] = seamremap[v2];\n\t\t\t\tindexes[i*3+2] = seamremap[v3];\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tindexes[i*3+0] = v1;\n\t\t\t\tindexes[i*3+1] = v2;\n\t\t\t\tindexes[i*3+2] = v3;\n\t\t\t}\n\t\t}\n\t\tend = &pinq1triangles[pq1inmodel->numtris];\n\n\t\t//frames\n\t\tif (Q1MDL_LoadFrameGroup(galias, pq1inmodel, mod, (daliasframetype_t *)end, seamremap, (sixteenbit?16:0) | (qtest?1:0)) == NULL)\n\t\t{\n\t\t\tBZ_Free(seamremap);\n\t\t\tZG_FreeGroup(&mod->memgroup);\n\t\t\treturn false;\n\t\t}\n\t\tBZ_Free(seamremap);\n\t}\n\n\n\tMod_CompileTriangleNeighbours(mod, galias);\n\tMod_BuildTextureVectors(galias);\n\n#if 0\t//fast (somewhat inaccurate) way. some exporters will bias all geometry weirdly (often ensuring 0 0 0 is in the bounds, resulting in unfortunate biases.)\n\tVectorCopy (pq1inmodel->scale_origin, mod->mins);\n\tVectorMA (mod->mins, 255, pq1inmodel->scale, mod->maxs);\n#else\n\tClearBounds(mod->mins, mod->maxs);\n\tfor (i = 0; i < galias->numanimations; i++)\n\t{\n\t\tgaliasanimation_t *a = galias->ofsanimations;\n\t\tvecV_t *v;\n\t\tsize_t j, k;\n\t\tfor (j = 0; j < a->numposes; j++)\n\t\t{\n\t\t\tv = a->poseofs[j].ofsverts;\n\t\t\tfor (k = 0; k < galias->numverts; k++)\n\t\t\t\tAddPointToBounds(v[k], mod->mins, mod->maxs);\n\t\t}\n\t}\n\tif (mod->maxs[0] < mod->mins[0])\t//no points? o.O\n\t\tAddPointToBounds(vec3_origin, mod->mins, mod->maxs);\n#endif\n\n\tmod->type = mod_alias;\n\tMod_ClampModelSize(mod);\n\tMesh_HandleFramegroupsFile(mod, galias);\n\tMod_ParseModelEvents(mod, galias->ofsanimations, galias->numanimations);\n\n\tmod->numframes = galias->numanimations;\n\tmod->meshinfo = galias;\n\n\tmod->funcs.NativeTrace = Mod_Trace;\n\n\tif (!strcmp(mod->name, \"progs/v_shot.mdl\"))\n\t\tgalias->lerpcutoff = 20;\n\telse if (!strcmp(mod->name, \"progs/v_shot2.mdl\"))\n\t\tgalias->lerpcutoff = 20;\n\telse if (!strcmp(mod->name, \"progs/v_nail.mdl\"))\n\t\tgalias->lerpcutoff = 7;\n\telse if (!strcmp(mod->name, \"progs/v_nail2.mdl\"))\n\t\tgalias->lerpcutoff = 6;\n\telse if (!strcmp(mod->name, \"progs/v_rock.mdl\"))\n\t\tgalias->lerpcutoff = 30;\n\telse if (!strcmp(mod->name, \"progs/v_rock2.mdl\"))\n\t\tgalias->lerpcutoff = 30;\n\telse if (!strcmp(mod->name, \"progs/v_light.mdl\"))\n\t\tgalias->lerpcutoff = 30;\n#ifdef HEXEN2\n\tif ((mod->flags == MF_ROCKET) && !strncmp(mod->name, \"models/sflesh\", 13))\n\t\tmod->flags = MFH2_SPIDERBLOOD;\n#endif\n\n\treturn true;\n}\n\nstatic int Mod_ReadFlagsFromMD1(char *name, int md3version)\n{\n\tint result = 0;\n\tsize_t fsize;\n\tdmdl_t\t\t\t\t*pinmodel;\n\tchar fname[MAX_QPATH];\n\tCOM_StripExtension(name, fname, sizeof(fname));\n\tCOM_DefaultExtension(fname, \".mdl\", sizeof(fname));\n\n\tif (!strcmp(name, fname))\t//md3 renamed as mdl\n\t{\n\t\tCOM_StripExtension(name, fname, sizeof(fname));\t//seeing as the md3 is named over the mdl,\n\t\tCOM_DefaultExtension(fname, \".md1\", sizeof(fname));//read from a file with md1 (one, not an ell)\n\t}\n\n\tpinmodel = (dmdl_t *)FS_LoadMallocFile(fname, &fsize);\n\tif (pinmodel)\n\t{\n\t\tif (fsize >= sizeof(dmdl_t) && !memcmp(&pinmodel->ident, IDPOLYHEADER))\n\t\t\tif (LittleLong(pinmodel->version) == ALIAS_VERSION)\n\t\t\t\tresult = LittleLong(pinmodel->flags);\n\t\tBZ_Free(pinmodel);\n\t}\n\treturn result;\n}\n#else\nint Mod_ReadFlagsFromMD1(char *name, int md3version)\n{\n\treturn 0;\n}\n#endif\n\n#ifdef MD2MODELS\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n//Q2 model loading\n\ntypedef struct\n{\n\tfloat\t\tscale[3];\t// multiply qbyte verts by this\n\tfloat\t\ttranslate[3];\t// then add this\n\tchar\t\tname[16];\t// frame name from grabbing\n\tdtrivertx_t\tverts[1];\t// variable sized\n} dmd2aliasframe_t;\n\n//static galiasinfo_t *galias;\n//static md2_t *pq2inmodel;\n#define Q2NUMVERTEXNORMALS\t162\nextern vec3_t\tbytedirs[Q2NUMVERTEXNORMALS];\n\nstatic void Q2MD2_LoadSkins(galiasinfo_t *galias, model_t *mod, md2_t *pq2inmodel, char *skins)\n{\n#ifndef SERVERONLY\n\tint i;\n\tskinframe_t *frames;\n\tgaliasskin_t *outskin = galias->ofsskins;\n\n\tfor (i = 0; i < LittleLong(pq2inmodel->num_skins); i++, outskin++)\n\t{\n\t\tframes = ZG_Malloc(&mod->memgroup, sizeof(*frames));\n\t\toutskin->frame = frames;\n\t\toutskin->numframes=1;\n\n\t\tCOM_CleanUpPath(skins);\t//blooming tanks.\n\t\tQ_strncpyz(frames->shadername, skins, sizeof(frames->shadername));\n\n\t\toutskin->skinwidth = 0;\n\t\toutskin->skinheight = 0;\n\t\toutskin->skinspeed = 0;\n\n\t\tskins += MD2MAX_SKINNAME;\n\t}\n#endif\n\tgalias->numskins = LittleLong(pq2inmodel->num_skins);\n\n\t/*\n#ifndef SERVERONLY\n\toutskin = (galiasskin_t *)((char *)galias + galias->ofsskins);\n\toutskin += galias->numskins - 1;\n\tif (galias->numskins)\n\t{\n\t\tif (*(shader_t**)((char *)outskin + outskin->ofstexnums))\n\t\t\treturn;\n\n\t\tgalias->numskins--;\n\t}\n#endif\n\t*/\n}\n\n#define MD2_MAX_TRIANGLES 4096\nstatic qboolean QDECL Mod_LoadQ2Model (model_t *mod, void *buffer, size_t fsize)\n{\n#ifndef SERVERONLY\n\tdmd2stvert_t *pinstverts;\n\tvec2_t *st_array;\n\tvec3_t *normals;\n#endif\n\tmd2_t *pq2inmodel;\n\n\tint version;\n\tint i, j;\n\tdmd2triangle_t *pintri;\n\tindex_t *indexes;\n\tint numindexes;\n\n\tvec3_t min;\n\tvec3_t max;\n\n\tgaliaspose_t *pose;\n\tgaliasanimation_t *poutframe;\n\tdmd2aliasframe_t *pinframe;\n\tint framesize;\n\tvecV_t *verts;\n\n\tint\t\tindremap[MD2_MAX_TRIANGLES*3];\n\tunsigned short\t\tptempindex[MD2_MAX_TRIANGLES*3], ptempstindex[MD2_MAX_TRIANGLES*3];\n\n\tint numverts;\n\n\tint size;\n\tgaliasinfo_t *galias;\n\n\tmod->engineflags |= MDLF_NEEDOVERBRIGHT;\n\n\tpq2inmodel = (md2_t *)buffer;\n\n\tversion = LittleLong (pq2inmodel->version);\n\tif (version != MD2ALIAS_VERSION)\n\t{\n\t\tCon_Printf (CON_ERROR \"%s has wrong version number (%i should be %i)\\n\",\n\t\t\t\t mod->name, version, MD2ALIAS_VERSION);\n\t\treturn false;\n\t}\n\n\tif (LittleLong(pq2inmodel->num_frames) < 1 ||\n\t\tLittleLong(pq2inmodel->num_skins) < 0 ||\n\t\tLittleLong(pq2inmodel->num_tris) < 1 ||\n\t\tLittleLong(pq2inmodel->num_xyz) < 3 ||\n\t\tLittleLong(pq2inmodel->num_st) < 3 ||\n\t\tLittleLong(pq2inmodel->skinheight) < 1 ||\n\t\tLittleLong(pq2inmodel->skinwidth) < 1)\n\t{\n\t\tCon_Printf(CON_ERROR \"Model %s has an invalid quantity\\n\", mod->name);\n\t\treturn false;\n\t}\n\n\tmod->flags = 0;\n\n\tmod->numframes = LittleLong(pq2inmodel->num_frames);\n\n\tsize = sizeof(galiasinfo_t)\n#ifndef SERVERONLY\n\t\t+ LittleLong(pq2inmodel->num_skins)*sizeof(galiasskin_t)\n#endif\n\t\t+ LittleLong(pq2inmodel->num_frames)*sizeof(galiasanimation_t);\n\n\tgalias = ZG_Malloc(&mod->memgroup, size);\n\tMod_DefaultMesh(galias, mod->name, 0);\n\tgalias->ofsanimations = (galiasanimation_t*)(galias+1);\n#ifndef SERVERONLY\n\tgalias->ofsskins = (galiasskin_t*)(galias->ofsanimations + LittleLong(pq2inmodel->num_frames));\n#endif\n\tgalias->nextsurf = 0;\n\n//skins\n\tQ2MD2_LoadSkins(galias, mod, pq2inmodel, ((char *)pq2inmodel+LittleLong(pq2inmodel->ofs_skins)));\n\n\t//trianglelists;\n\tpintri = (dmd2triangle_t *)((char *)pq2inmodel + LittleLong(pq2inmodel->ofs_tris));\n\n\n\tfor (i=0 ; i<LittleLong(pq2inmodel->num_tris) ; i++, pintri++)\n\t{\n\t\tfor (j=0 ; j<3 ; j++)\n\t\t{\n\t\t\tptempindex[i*3+j] = ( unsigned short )LittleShort ( pintri->xyz_index[j] );\n\t\t\tptempstindex[i*3+j] = ( unsigned short )LittleShort ( pintri->st_index[j] );\n\t\t}\n\t}\n\n\tnumindexes = galias->numindexes = LittleLong(pq2inmodel->num_tris)*3;\n\tindexes = ZG_Malloc(&mod->memgroup, galias->numindexes*sizeof(*indexes));\n\tgalias->ofs_indexes = indexes;\n\tmemset ( indremap, -1, sizeof(indremap) );\n\tnumverts=0;\n\n\tfor ( i = 0; i < numindexes; i++ )\n\t{\n\t\tif ( indremap[i] != -1 ) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tfor ( j = 0; j < numindexes; j++ )\n\t\t{\n\t\t\tif ( j == i ) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif ( (ptempindex[i] == ptempindex[j]) && (ptempstindex[i] == ptempstindex[j]) ) {\n\t\t\t\tindremap[j] = i;\n\t\t\t}\n\t\t}\n\t}\n\n\t// count unique vertexes\n\tfor ( i = 0; i < numindexes; i++ )\n\t{\n\t\tif ( indremap[i] != -1 ) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tindexes[i] = numverts++;\n\t\tindremap[i] = i;\n\t}\n\n//\tCon_DPrintf ( \"%s: remapped %i verts to %i\\n\", mod->name, LittleLong(pq2inmodel->num_xyz), numverts );\n\n\tgalias->numverts = numverts;\n\n\t// remap remaining indexes\n\tfor ( i = 0; i < numindexes; i++ )\n\t{\n\t\tif ( indremap[i] != i ) {\n\t\t\tindexes[i] = indexes[indremap[i]];\n\t\t}\n\t}\n\n// s and t vertices\n#ifndef SERVERONLY\n\tpinstverts = ( dmd2stvert_t * ) ( ( qbyte * )pq2inmodel + LittleLong (pq2inmodel->ofs_st) );\n\tst_array = ZG_Malloc(&mod->memgroup, sizeof(*st_array)*(numverts));\n\tgalias->ofs_st_array = st_array;\n\n\tfor (j=0 ; j<numindexes; j++)\n\t{\n\t\tst_array[indexes[j]][0] = (float)(((double)LittleShort (pinstverts[ptempstindex[indremap[j]]].s) + 0.5f) /LittleLong(pq2inmodel->skinwidth));\n\t\tst_array[indexes[j]][1] = (float)(((double)LittleShort (pinstverts[ptempstindex[indremap[j]]].t) + 0.5f) /LittleLong(pq2inmodel->skinheight));\n\t}\n#endif\n\n\t//frames\n\tClearBounds ( mod->mins, mod->maxs );\n\n\tpoutframe = galias->ofsanimations;\n\tframesize = LittleLong (pq2inmodel->framesize);\n\tfor (i=0 ; i<LittleLong(pq2inmodel->num_frames) ; i++)\n\t{\n\t\tsize = sizeof(galiaspose_t) + sizeof(vecV_t)*numverts;\n#ifndef SERVERONLY\n\t\tsize += 3*sizeof(vec3_t)*numverts;\n#endif\n\t\tpose = (galiaspose_t *)ZG_Malloc(&mod->memgroup, size);\n\t\tpoutframe->poseofs = pose;\n\t\tpoutframe->numposes = 1;\n\t\tgalias->numanimations++;\n\n\t\tverts = (vecV_t *)(pose+1);\n\t\tpose->ofsverts = verts;\n#ifndef SERVERONLY\n\t\tnormals = (vec3_t*)&verts[galias->numverts];\n\t\tpose->ofsnormals = normals;\n\n\t\tpose->ofssvector = &normals[galias->numverts];\n\t\tpose->ofstvector = &normals[galias->numverts*2];\n#endif\n\n\n\t\tpinframe = ( dmd2aliasframe_t * )( ( qbyte * )pq2inmodel + LittleLong (pq2inmodel->ofs_frames) + i * framesize );\n\t\tQ_strncpyz(poutframe->name, pinframe->name, sizeof(poutframe->name));\n\n\t\tfor (j=0 ; j<3 ; j++)\n\t\t{\n\t\t\tpose->scale[j] = LittleFloat (pinframe->scale[j]);\n\t\t\tpose->scale_origin[j] = LittleFloat (pinframe->translate[j]);\n\t\t}\n\n\t\tfor (j=0 ; j<numindexes; j++)\n\t\t{\n\t\t\t// verts are all 8 bit, so no swapping needed\n\t\t\tverts[indexes[j]][0] = pose->scale_origin[0]+pose->scale[0]*pinframe->verts[ptempindex[indremap[j]]].v[0];\n\t\t\tverts[indexes[j]][1] = pose->scale_origin[1]+pose->scale[1]*pinframe->verts[ptempindex[indremap[j]]].v[1];\n\t\t\tverts[indexes[j]][2] = pose->scale_origin[2]+pose->scale[2]*pinframe->verts[ptempindex[indremap[j]]].v[2];\n#ifndef SERVERONLY\n\t\t\tVectorCopy(bytedirs[pinframe->verts[ptempindex[indremap[j]]].lightnormalindex], normals[indexes[j]]);\n#endif\n\t\t}\n\n//\t\tMod_AliasCalculateVertexNormals ( numindexes, poutindex, numverts, poutvertex, qfalse );\n\n\t\tVectorCopy ( pose->scale_origin, min );\n\t\tVectorMA ( pose->scale_origin, 255, pose->scale, max );\n\n//\t\tpoutframe->radius = RadiusFromBounds ( min, max );\n\n//\t\tmod->radius = max ( mod->radius, poutframe->radius );\n\t\tAddPointToBounds ( min, mod->mins, mod->maxs );\n\t\tAddPointToBounds ( max, mod->mins, mod->maxs );\n\n//\t\tGL_GenerateNormals((float*)verts, (float*)normals, indexes, numindexes/3, numverts);\n\n\t\tpoutframe++;\n\t}\n\n\n\n\tMod_CompileTriangleNeighbours(mod, galias);\n\tMod_BuildTextureVectors(galias);\n\t/*\n\tVectorCopy (pq2inmodel->scale_origin, mod->mins);\n\tVectorMA (mod->mins, 255, pq2inmodel->scale, mod->maxs);\n\t*/\n\n\tMod_ClampModelSize(mod);\n\tMesh_HandleFramegroupsFile(mod, galias);\n\tMod_ParseModelEvents(mod, galias->ofsanimations, galias->numanimations);\n\n\tmod->meshinfo = galias;\n\tmod->numframes = galias->numanimations;\n\tmod->type = mod_alias;\n\n\tmod->funcs.NativeTrace = Mod_Trace;\n\treturn true;\n}\n\n#endif\n\n#ifdef MODELFMT_MDX\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n//Kingpin model loading\n//basically md2, but with toggleable subobjects and object bounding boxes (fixme: use those for hitmesh instead of the mesh itself)\n\ntypedef struct\n{\n\tfloat\t\tscale[3];\t// multiply qbyte verts by this\n\tfloat\t\ttranslate[3];\t// then add this\n\tchar\t\tname[16];\t// frame name from grabbing\n\tdtrivertx_t\tverts[1];\t// variable sized\n} dmdxframe_t;\n\ntypedef struct\n{\n\tint cmd;\n\tint obj;\n\tstruct dmdxcommandvert_s\n\t{\n\t\tfloat s, t;\n\t\tint origvert;\n\t} vert[1];\n} dmdxcommand_t;\n\ntypedef struct\n{\n   int magic;\n   int version;\n   int skinWidth;\n   int skinHeight;\n   int frameSize;\n   int numSkins;\n   int numVertices;\n   int numTriangles_Unusable;\n   int numGlCommands;\n   int numFrames;\n   int numSfxDefines_Unsupported;\n   int numSfxEntries_Unsupported;\n   int numSubObjects;\n   int offsetSkins;\n   int offsetTriangles_Unusable;\n   int offsetFrames;\n   int offsetGlCommands;\n   int offsetVertexInfo_Whatever;\n   int offsetSfxDefines_Unsupported;\n   int offsetSfxEntries_Unsupported;\n   int offsetBBoxFrames_HitmeshInstead;\n   int offsetMaybeST_Bugged;\n   int offsetEnd;\n} dmdxheader_t;\n\n#define MDX_IDENT \"IDPX\",4\n#define MDX_VERSION 4\n\n#define Q2NUMVERTEXNORMALS\t162\nextern vec3_t\tbytedirs[Q2NUMVERTEXNORMALS];\n\n#ifndef SERVERONLY\nstatic void MDX_LoadSkins(galiasinfo_t *galias, model_t *mod, dmdxheader_t *pinmodel, char *skins)\n{\n\tint i;\n\tskinframe_t *frames;\n\tgaliasskin_t *outskin = galias->ofsskins;\n\n\tfor (i = 0; i < LittleLong(pinmodel->numSkins); i++, outskin++)\n\t{\n\t\tframes = ZG_Malloc(&mod->memgroup, sizeof(*frames));\n\t\toutskin->frame = frames;\n\t\toutskin->numframes=1;\n\n\t\tCOM_CleanUpPath(skins);\t//blooming tanks.\n\t\tQ_strncpyz(frames->shadername, skins, sizeof(frames->shadername));\n\n\t\toutskin->skinwidth = 0;\n\t\toutskin->skinheight = 0;\n\t\toutskin->skinspeed = 0;\n\n\t\tskins += MD2MAX_SKINNAME;\n\t}\n\tgalias->numskins = LittleLong(pinmodel->numSkins);\n}\n#endif\n\n#define MDX_MAX_TRIANGLES 4096\n#define MDX_MAX_VERTS MDX_MAX_TRIANGLES*3\nstatic int MDX_MatchVert(struct dmdxcommandvert_s *list, int *count, struct dmdxcommandvert_s *newvert)\n{\n\tint i;\n\tfor (i = 0; i < *count; i++)\n\t{\n\t\tif (list[i].origvert == newvert->origvert &&\n\t\t\tlist[i].s == newvert->s &&\n\t\t\tlist[i].t == newvert->t)\n\t\t\treturn i;\t//its a dupe!\n\t}\n\tif (i == MDX_MAX_VERTS)\n\t\treturn 0;\n\tlist[i] = *newvert;\n\t*count+=1;\n\treturn i;\n}\n\nstatic qboolean QDECL Mod_LoadKingpinModel (model_t *mod, void *buffer, size_t fsize)\n{\n#ifndef SERVERONLY\n\tvec2_t *st_array;\n\tvec3_t *normals;\n#endif\n\tdmdxheader_t *pinmodel;\n\n\tint version;\n\tint i, j, subobj;\n\tindex_t *indexes;\n\n\tvec3_t min;\n\tvec3_t max;\n\n\tgaliaspose_t *pose;\n\tgaliasanimation_t *poutframe;\n\tdmdxframe_t *pinframe;\n\tint framesize;\n\tvecV_t *verts;\n\tdmdxcommand_t *pincmd, *pincmdend;\n\n\tstruct dmdxcommandvert_s tmpvert[MDX_MAX_VERTS];\n\tint numverts;\n\tstruct\n\t{\n\t\tint newidx[3];\n\t} tri[MDX_MAX_TRIANGLES];\n\tint numtri;\n\n\tint size;\n\tgaliasinfo_t *galias, *root;\n\n\tmod->engineflags |= MDLF_NEEDOVERBRIGHT;\n\n\tpinmodel = (dmdxheader_t *)buffer;\n\n\tif (fsize < sizeof(*pinmodel))\n\t{\n\t\tCon_Printf (CON_ERROR \"%s is truncated\\n\", mod->name);\n\t\treturn false;\n\t}\n\tversion = LittleLong (pinmodel->version);\n\tif (version != MDX_VERSION)\n\t{\n\t\tCon_Printf (CON_ERROR \"%s has wrong version number (%i should be %i)\\n\",\n\t\t\t\t mod->name, version, MDX_VERSION);\n\t\treturn false;\n\t}\n\tif (LittleLong(pinmodel->offsetEnd) != fsize)\n\t{\n\t\tCon_Printf (CON_ERROR \"%s is truncated\\n\", mod->name);\n\t\treturn false;\n\t}\n\n\tif (LittleLong(pinmodel->numFrames) < 1 ||\n\t\tLittleLong(pinmodel->numSkins) < 0 ||\n\t\tLittleLong(pinmodel->numTriangles_Unusable) < 1 ||\n\t\tLittleLong(pinmodel->numVertices) < 3 ||\n\t\tLittleLong(pinmodel->skinHeight) < 1 ||\n\t\tLittleLong(pinmodel->skinWidth) < 1 ||\n\t\tLittleLong(pinmodel->numSubObjects) < 1)\n\t{\n\t\tCon_Printf(CON_ERROR \"Model %s has an invalid quantity\\n\", mod->name);\n\t\treturn false;\n\t}\n\n\tmod->flags = 0;\n\n\tmod->numframes = LittleLong(pinmodel->numFrames);\n\n\tsize = sizeof(galiasinfo_t)*pinmodel->numSubObjects\n\t\t+ LittleLong(pinmodel->numFrames)*sizeof(galiasanimation_t)*pinmodel->numSubObjects\n#ifndef SERVERONLY\n\t\t+ LittleLong(pinmodel->numSkins)*sizeof(galiasskin_t)\n#endif\n\t\t;\n\n\troot = galias = ZG_Malloc(&mod->memgroup, size);\n\n#ifndef SERVERONLY\n//skins\n\tgalias->ofsskins = (galiasskin_t*)((galiasanimation_t*)(galias+pinmodel->numSubObjects) + LittleLong(pinmodel->numFrames)*pinmodel->numSubObjects);\n\tMDX_LoadSkins(galias, mod, pinmodel, ((char *)pinmodel+LittleLong(pinmodel->offsetSkins)));\n#else\n\tgalias->numskins = LittleLong(pinmodel->numSkins);\n#endif\n\n\tClearBounds (mod->mins, mod->maxs);\n\tfor (subobj = 0; subobj < pinmodel->numSubObjects; subobj++)\n\t{\n\t\tgalias = &root[subobj];\n\t\tMod_DefaultMesh(galias, mod->name, subobj);\n\t\tgalias->ofsanimations = (galiasanimation_t*)(root+pinmodel->numSubObjects) + subobj*pinmodel->numFrames;\n\t\tgalias->ofsskins = root->ofsskins;\n\t\tgalias->numskins = root->numskins;\n\t\tgalias->shares_verts = subobj;\n\t\tif (subobj > 0)\n\t\t\troot[subobj-1].nextsurf = galias;\n\n\t\t//process the strips+fans, and split the verts into the appropriate submesh\n\t\tpincmd = (dmdxcommand_t *)((char *)pinmodel + LittleLong(pinmodel->offsetGlCommands));\n\t\tpincmdend = (dmdxcommand_t *)((char *)pinmodel + LittleLong(pinmodel->offsetGlCommands) + LittleLong(pinmodel->numGlCommands)*4);\n\t\tnumverts = 0;\n\t\tnumtri = 0;\n\t\twhile (pincmd < pincmdend)\n\t\t{\n\t\t\tint n = LittleLong(pincmd->cmd);\n\t\t\tif (!n)\n\t\t\t\tbreak; //no more commands\n\t\t\tif (n < 0)\n\t\t\t{\t//fan club\n\t\t\t\tn = -n;\n\t\t\t\tif (n > 2 && LittleLong(pincmd->obj) == subobj)\n\t\t\t\t{\n\t\t\t\t\tint first = MDX_MatchVert(tmpvert, &numverts, &pincmd->vert[0]);\n\t\t\t\t\tint prev = MDX_MatchVert(tmpvert, &numverts, &pincmd->vert[1]);\n\t\t\t\t\tfor (i = 2; i < n && numtri < countof(tri); i++)\n\t\t\t\t\t{\n\t\t\t\t\t\ttri[numtri].newidx[0] = first;\n\t\t\t\t\t\ttri[numtri].newidx[1] = prev;\n\t\t\t\t\t\ttri[numtri].newidx[2] = prev = MDX_MatchVert(tmpvert, &numverts, &pincmd->vert[i]);\n\t\t\t\t\t\tnumtri++;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\t//stripper\n\t\t\t\tif (n > 2 && LittleLong(pincmd->obj) == subobj)\n\t\t\t\t{\n\t\t\t\t\tint first = MDX_MatchVert(tmpvert, &numverts, &pincmd->vert[0]);\n\t\t\t\t\tint prev = MDX_MatchVert(tmpvert, &numverts, &pincmd->vert[1]);\n\t\t\t\t\tfor (i = 2; i < n && numtri < countof(tri); i++)\n\t\t\t\t\t{\n\t\t\t\t\t\ttri[numtri].newidx[2] = MDX_MatchVert(tmpvert, &numverts, &pincmd->vert[i]);\n\t\t\t\t\t\tif (i&1)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttri[numtri].newidx[0] = prev;\n\t\t\t\t\t\t\ttri[numtri].newidx[1] = first;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttri[numtri].newidx[0] = first;\n\t\t\t\t\t\t\ttri[numtri].newidx[1] = prev;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tfirst = prev;\n\t\t\t\t\t\tprev = tri[numtri].newidx[2];\n\n\t\t\t\t\t\tif (tri[numtri].newidx[0] == tri[numtri].newidx[1] ||\n\t\t\t\t\t\t\ttri[numtri].newidx[0] == tri[numtri].newidx[2] ||\n\t\t\t\t\t\t\ttri[numtri].newidx[1] == tri[numtri].newidx[2])\n\t\t\t\t\t\t\tcontinue;\t//degenerate... I doubt we'll see any though.\n\t\t\t\t\t\tnumtri++;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tpincmd = (dmdxcommand_t*)&pincmd->vert[n];\n\t\t}\n\t\tfor (i = 0; i < numverts; i++)\n\t\t{\t//might as well byteswap that stuff now...\n\t\t\ttmpvert[i].origvert = LittleLong(tmpvert[i].origvert);\n\t\t\ttmpvert[i].s = LittleFloat(tmpvert[i].s);\n\t\t\ttmpvert[i].t = LittleFloat(tmpvert[i].t);\n\t\t}\n\n\t\tgalias->numverts = numverts;\n\t\tgalias->numindexes = numtri*3;\n\t\tindexes = ZG_Malloc(&mod->memgroup, galias->numindexes*sizeof(*indexes));\n\t\tgalias->ofs_indexes = indexes;\n\n\t\tfor (i = 0; i < numtri; i++, indexes+=3)\n\t\t{\n\t\t\tindexes[0] = tri[i].newidx[0];\n\t\t\tindexes[1] = tri[i].newidx[1];\n\t\t\tindexes[2] = tri[i].newidx[2];\n\t\t}\n\n\t\t// s and t vertices\n#ifndef SERVERONLY\n\t\tst_array = ZG_Malloc(&mod->memgroup, sizeof(*st_array)*(numverts));\n\t\tgalias->ofs_st_array = st_array;\n\n\t\tfor (j=0 ; j<numverts; j++)\n\t\t{\n\t\t\tst_array[j][0] = tmpvert[j].s;\n\t\t\tst_array[j][1] = tmpvert[j].t;\n\t\t}\n#endif\n\n\t\t//frames\n\t\tpoutframe = galias->ofsanimations;\n\t\tframesize = LittleLong (pinmodel->frameSize);\n\n\t\tsize = sizeof(galiaspose_t) + sizeof(vecV_t)*numverts;\n#ifndef SERVERONLY\n\t\tsize += 3*sizeof(vec3_t)*numverts;\n#endif\n\t\tsize *= pinmodel->numFrames;\n\t\tpose = (galiaspose_t *)ZG_Malloc(&mod->memgroup, size);\n\t\tverts = (vecV_t*)(pose+pinmodel->numFrames);\n#ifndef SERVERONLY\n\t\tnormals = (vec3_t*)(verts+pinmodel->numFrames*numverts);\n#endif\n\n\t\tfor (i=0 ; i<LittleLong(pinmodel->numFrames) ; i++)\n\t\t{\n\t\t\tpoutframe->poseofs = pose;\n\t\t\tpoutframe->numposes = 1;\n\t\t\tpoutframe->action = -1;\n\t\t\tpoutframe->actionweight = 0;\n\t\t\tgalias->numanimations++;\n\n#ifndef SERVERONLY\n\t\t\tpose->ofsnormals = normals;\n\t\t\tpose->ofssvector = &normals[galias->numverts];\n\t\t\tpose->ofstvector = &normals[galias->numverts*2];\n#endif\n\n\t\t\tpinframe = ( dmdxframe_t * )( ( qbyte * )pinmodel + LittleLong (pinmodel->offsetFrames) + i * framesize );\n\t\t\tQ_strncpyz(poutframe->name, pinframe->name, sizeof(poutframe->name));\n\n\t\t\tfor (j=0 ; j<3 ; j++)\n\t\t\t{\n\t\t\t\tpose->scale[j] = LittleFloat (pinframe->scale[j]);\n\t\t\t\tpose->scale_origin[j] = LittleFloat (pinframe->translate[j]);\n\t\t\t}\n\n\t\t\tpose->ofsverts = verts;\n\t\t\tfor (j=0 ; j<numverts; j++)\n\t\t\t{\n\t\t\t\t// verts are all 8 bit, so no swapping needed\n\t\t\t\tverts[j][0] = pose->scale_origin[0]+pose->scale[0]*pinframe->verts[tmpvert[j].origvert].v[0];\n\t\t\t\tverts[j][1] = pose->scale_origin[1]+pose->scale[1]*pinframe->verts[tmpvert[j].origvert].v[1];\n\t\t\t\tverts[j][2] = pose->scale_origin[2]+pose->scale[2]*pinframe->verts[tmpvert[j].origvert].v[2];\n#ifndef SERVERONLY\n\t\t\t\tVectorCopy(bytedirs[pinframe->verts[tmpvert[j].origvert].lightnormalindex], normals[j]);\n#endif\n\t\t\t}\n\n\t\t\tVectorCopy ( pose->scale_origin, min );\n\t\t\tVectorMA ( pose->scale_origin, 255, pose->scale, max );\n\n\t\t\tAddPointToBounds ( min, mod->mins, mod->maxs );\n\t\t\tAddPointToBounds ( max, mod->mins, mod->maxs );\n\n\t\t\tpoutframe++;\n\t\t\tpose++;\n\t\t\tverts += numverts;\n#ifndef SERVERONLY\n\t\t\tnormals += numverts*3;\n#endif\n\t\t}\n\n\t\tMod_CompileTriangleNeighbours(mod, galias);\n\t\tMod_BuildTextureVectors(galias);\n\t}\n\n\tmod->radius = RadiusFromBounds(mod->mins, mod->maxs);\n\tMod_ClampModelSize(mod);\n\tMesh_HandleFramegroupsFile(mod, galias);\n\tMod_ParseModelEvents(mod, root->ofsanimations, root->numanimations);\n\n\tmod->meshinfo = root;\n\tmod->numframes = root->numanimations;\n\tmod->type = mod_alias;\n\n\tmod->funcs.NativeTrace = Mod_Trace;\n\n\treturn true;\n}\n#endif\n\n\n\n\n\n\n\nint Mod_GetNumBones(model_t *model, qboolean allowtags)\n{\n#ifdef SKELORTAGS\n\tif (model && model->type == mod_alias)\n\t{\n\t\tgaliasinfo_t *inf = Mod_Extradata(model);\n#ifdef SKELETALMODELS\n\t\tif (inf->numbones)\n\t\t\treturn inf->numbones;\n\t\telse\n#endif\n#ifdef MD3MODELS\n\t\t\tif (allowtags)\n\t\t\treturn inf->numtags;\n#endif\n\t\treturn 0;\n\t}\n#endif\n#ifdef HALFLIFEMODELS\n\tif (model && model->type == mod_halflife)\n\t\treturn HLMDL_GetNumBones(model, allowtags);\n#endif\n\treturn 0;\n}\n\nint Mod_GetBoneRelations(model_t *model, int firstbone, int lastbone, const galiasbone_t *boneinfo, const framestate_t *fstate, float *result)\n{\n#ifdef SKELETALMODELS\n\tif (model && model->type == mod_alias)\n\t\treturn Alias_BlendBoneData(Mod_Extradata(model), fstate, result, SKEL_RELATIVE, firstbone, lastbone, boneinfo);\n#endif\n#ifdef HALFLIFEMODELS\n\tif (model && model->type == mod_halflife)\n\t\treturn HLMDL_GetBoneData(model, firstbone, lastbone, fstate, result);\n#endif\n\treturn 0;\n}\n\ngaliasbone_t *Mod_GetBoneInfo(model_t *model, int *numbones)\n{\n#ifdef SKELETALMODELS\n\tif (model && model->type == mod_alias)\n\t{\n\t\tgaliasinfo_t *inf = Mod_Extradata(model);\n\t\t*numbones = inf->numbones;\n\t\treturn inf->ofsbones;\n\t}\n#endif\n#ifdef HALFLIFEMODELS\n\tif (model && model->type == mod_halflife)\n\t{\n\t\thlmodel_t *hlmod = Mod_Extradata(model);\n\t\t*numbones = hlmod->header->numbones;\n\t\treturn hlmod->compatbones;\n\t}\n#endif\n\t*numbones = 0;\n\treturn NULL;\n}\n\nint Mod_GetBoneParent(model_t *model, int bonenum)\n{\n#ifdef SKELETALMODELS\n\tif (model && model->type == mod_alias)\n\t{\n\t\tgaliasbone_t *bone;\n\t\tgaliasinfo_t *inf;\n\t\tinf = Mod_Extradata(model);\n\n\n\t\tbonenum--;\n\t\tif ((unsigned int)bonenum >= inf->numbones)\n\t\t\treturn 0;\t//no parent\n\t\tbone = inf->ofsbones;\n\t\treturn bone[bonenum].parent+1;\n\t}\n#endif\n#ifdef HALFLIFEMODELS\n\tif (model && model->type == mod_halflife)\n\t\treturn HLMDL_GetBoneParent(model, bonenum-1)+1;\n#endif\n\treturn 0;\n}\n\nconst char *Mod_GetBoneName(model_t *model, int bonenum)\n{\n#ifdef SKELETALMODELS\n\tif (model && model->type == mod_alias)\n\t{\n\t\tgaliasbone_t *bone;\n\t\tgaliasinfo_t *inf;\n\t\tinf = Mod_Extradata(model);\n\n\n\t\tbonenum--;\n\t\tif ((unsigned int)bonenum >= inf->numbones)\n\t\t\treturn 0;\t//no parent\n\t\tbone = inf->ofsbones;\n\t\treturn bone[bonenum].name;\n\t}\n#endif\n#ifdef HALFLIFEMODELS\n\tif (model && model->type == mod_halflife)\n\t\treturn HLMDL_GetBoneName(model, bonenum-1);\n#endif\n\treturn 0;\n}\n\nqboolean Mod_GetTag(model_t *model, int tagnum, framestate_t *fstate, float *result)\n{\n#ifdef HALFLIFEMODELS\n\tif (model && model->type == mod_halflife)\n\t{\n\t\tint numbones = Mod_GetNumBones(model, true);\n\t\tif (tagnum > 0 && tagnum <= numbones)\n\t\t{\n\t\t\tfloat relatives[MAX_BONES*12];\n\t\t\tfloat tempmatrix[12];\t\t\t//flipped between this and bonematrix\n\t\t\tfloat *matrix;\t//the matrix for a single bone in a single pose.\n\t\t\tfloat *lerps = relatives;\n\t\t\tint k;\n\n\t\t\tif (tagnum <= 0 || tagnum > numbones)\n\t\t\t\treturn false;\n\t\t\ttagnum--;\t//tagnum 0 is 'use my angles/org'\n\n\t\t\t//data comes from skeletal object, if possible\n\t\t\tif (fstate->bonestate)\n\t\t\t{\n\t\t\t\tnumbones = fstate->bonecount;\n\t\t\t\tlerps = fstate->bonestate;\n\n\t\t\t\tif (tagnum >= numbones)\n\t\t\t\t\treturn false;\n\n\t\t\t\tif (fstate->skeltype == SKEL_ABSOLUTE)\n\t\t\t\t{\t//can just directly read it, woo.\n\t\t\t\t\tmemcpy(result, fstate->bonestate + 12 * tagnum, 12*sizeof(*result));\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse //try getting the data from the frame state\n\t\t\t{\n\t\t\t\tnumbones = Mod_GetBoneRelations(model, 0, tagnum+1, NULL, fstate, relatives);\n\t\t\t\tlerps = relatives;\n\t\t\t}\n\n\t\t\t//set up the identity matrix\n\t\t\tfor (k = 0;k < 12;k++)\n\t\t\t\tresult[k] = 0;\n\t\t\tresult[0] = 1;\n\t\t\tresult[5] = 1;\n\t\t\tresult[10] = 1;\n\t\t\tif (tagnum >= numbones)\n\t\t\t\ttagnum = HLMDL_GetAttachment(model, tagnum-numbones, result);\n\t\t\twhile(tagnum >= 0)\n\t\t\t{\n\t\t\t\t//set up the per-bone transform matrix\n\t\t\t\tmatrix = lerps + tagnum*12;\n\t\t\t\tmemcpy(tempmatrix, result, sizeof(tempmatrix));\n\t\t\t\tR_ConcatTransforms((void*)matrix, (void*)tempmatrix, (void*)result);\n\t\t\t\ttagnum = Mod_GetBoneParent(model, tagnum+1)-1;\n\t\t\t}\n\n\t\t\treturn true;\n\t\t}\n\t}\n#endif\n\n#ifdef SKELORTAGS\n\tif (model && model->type == mod_alias)\n\t{\n\t\tgaliasinfo_t *inf = Mod_Extradata(model);\n#ifdef SKELETALMODELS\n\t\tif (inf->numbones)\n\t\t{\n\t\t\tgaliasbone_t *bone = inf->ofsbones;\n\n\t\t\tfloat tempmatrix[12];\t\t\t//flipped between this and bonematrix\n\t\t\tfloat *matrix;\t//the matrix for a single bone in a single pose.\n\t\t\tfloat m[12];\t//combined interpolated version of 'matrix'.\n\t\t\tint b, k;\t//counters\n\n\t\t\tint numbonegroups = 0;\n\t\t\tskellerps_t lerps[FS_COUNT], *lerp;\n\n\t\t\tif (tagnum <= 0 || tagnum > inf->numbones)\n\t\t\t\treturn false;\n\t\t\ttagnum--;\t//tagnum 0 is 'use my angles/org'\n\n\t\t\t//data comes from skeletal object, if possible\n\t\t\tif (!numbonegroups && fstate->bonestate)\n\t\t\t{\n\t\t\t\tif (tagnum >= fstate->bonecount)\n\t\t\t\t\treturn false;\n\n\t\t\t\tif (fstate->skeltype == SKEL_ABSOLUTE)\n\t\t\t\t{\t//can just directly read it, woo.\n\t\t\t\t\tmemcpy(result, fstate->bonestate + 12 * tagnum, 12*sizeof(*result));\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\n\t\t\t\tlerps[0].pose[0] = fstate->bonestate;\n\t\t\t\tlerps[0].frac[0] = 1;\n\t\t\t\tlerps[0].needsfree[0] = 0;\n\t\t\t\tlerps[0].lerpcount = 1;\n\t\t\t\tlerps[0].firstbone = 0;\n\t\t\t\tlerps[0].endbone = fstate->bonecount;\n\t\t\t\tlerps[0].skeltype = fstate->skeltype;\n\t\t\t\tnumbonegroups = 1;\n\t\t\t}\n\n\t\t\t//try getting the data from the frame state\n\t\t\tif (!numbonegroups)\n\t\t\t\tnumbonegroups = Alias_FindRawSkelData(inf, fstate, lerps, 0, inf->numbones, NULL);\n\n\t\t\t//try base pose?\n\t\t\tif (!numbonegroups && inf->baseframeofs)\n\t\t\t{\n\t\t\t\tlerps[0].pose[0] = inf->baseframeofs;\n\t\t\t\tlerps[0].frac[0] = 1;\n\t\t\t\tlerps[0].needsfree[0] = 0;\n\t\t\t\tlerps[0].lerpcount = 1;\n\t\t\t\tlerps[0].firstbone = 0;\n\t\t\t\tlerps[0].endbone = inf->numbones;\n\t\t\t\tlerps[0].skeltype = SKEL_ABSOLUTE;\n\t\t\t\tnumbonegroups = 1;\n\t\t\t}\n\n\t\t\t//make sure it was all okay.\n\t\t\tif (!numbonegroups || tagnum >= lerps[numbonegroups-1].endbone)\n\t\t\t\treturn false;\n\n\t\t\t//set up the identity matrix\n\t\t\tfor (k = 0;k < 12;k++)\n\t\t\t\tresult[k] = 0;\n\t\t\tresult[0] = 1;\n\t\t\tresult[5] = 1;\n\t\t\tresult[10] = 1;\n\t\t\twhile(tagnum >= 0)\n\t\t\t{\n\t\t\t\tfor (lerp = lerps; tagnum < lerp->firstbone; lerp++)\n\t\t\t\t\t;\n\t\t\t\t//set up the per-bone transform matrix\n\t\t\t\tmatrix = lerp->pose[0] + tagnum*12;\n\t\t\t\tfor (k = 0;k < 12;k++)\n\t\t\t\t\tm[k] = matrix[k] * lerp->frac[0];\n\t\t\t\tfor (b = 1;b < lerp->lerpcount;b++)\n\t\t\t\t{\n\t\t\t\t\tmatrix = lerp->pose[b] + tagnum*12;\n\t\t\t\t\tfor (k = 0;k < 12;k++)\n\t\t\t\t\t\tm[k] += matrix[k] * lerp->frac[b];\n\t\t\t\t}\n\n\t\t\t\tif (lerp->skeltype == SKEL_ABSOLUTE)\n\t\t\t\t{\n\t\t\t\t\tmemcpy(result, m, sizeof(tempmatrix));\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tmemcpy(tempmatrix, result, sizeof(tempmatrix));\n\t\t\t\tR_ConcatTransforms((void*)m, (void*)tempmatrix, (void*)result);\n\n\t\t\t\ttagnum = bone[tagnum].parent;\n\t\t\t}\n\n\t\t\tfor (b = 0; b < numbonegroups; b++)\n\t\t\t\tfor (k = 0; k < lerps[b].lerpcount; k++)\n\t\t\t\t\tBZ_Free(lerps[b].needsfree[k]);\n\t\t\treturn true;\n\t\t}\n#endif\n#ifdef MD3MODELS\n\t\tif (inf->numtags)\n\t\t{\n\t\t\tmd3tag_t *t1, *t2;\n\n\t\t\tint frame1, frame2;\n\t\t\t//float f1time, f2time;\t//tags/md3s don't support framegroups.\n\t\t\tfloat f2ness;\n\n#if FRAME_BLENDS != 2\n\t\t\tif (fstate->g[FS_REG].lerpweight[2] || fstate->g[FS_REG].lerpweight[3])\n\t\t\t{\n\t\t\t\tstatic float printtimer;\n\t\t\t\tCon_ThrottlePrintf(&printtimer, 1, \"Mod_GetTag(%s): non-skeletal animation only supports two animations\\n\", model->name);\n\t\t\t}\n#endif\n\n\t\t\tframe1 = fstate->g[FS_REG].frame[0];\n\t\t\tframe2 = fstate->g[FS_REG].frame[1];\n\t\t\t//f1time = fstate->g[FS_REG].frametime[0];\n\t\t\t//f2time = fstate->g[FS_REG].frametime[1];\n\t\t\tf2ness = fstate->g[FS_REG].lerpweight[1];\n\n\t\t\tif (tagnum <= 0 || tagnum > inf->numtags)\n\t\t\t\treturn false;\n\t\t\tif (frame1 < 0)\n\t\t\t\treturn false;\n\t\t\tif (frame1 >= inf->numtagframes)\n\t\t\t\tframe1 = inf->numtagframes - 1;\n\t\t\tif (frame2 < 0 || frame2 >= inf->numtagframes)\n\t\t\t\tframe2 = frame1;\n\t\t\ttagnum--;\t//tagnum 0 is 'use my angles/org'\n\n\t\t\tt1 = inf->ofstags;\n\t\t\tt1 += tagnum;\n\t\t\tt1 += inf->numtags*frame1;\n\n\t\t\tt2 = inf->ofstags;\n\t\t\tt2 += tagnum;\n\t\t\tt2 += inf->numtags*frame2;\n\n\t\t\tif (t1 == t2)\n\t\t\t{\n\t\t\t\tresult[0]\t= t1->ang[0][0];\n\t\t\t\tresult[1]\t= t1->ang[0][1];\n\t\t\t\tresult[2]\t= t1->ang[0][2];\n\t\t\t\tresult[3]\t= t1->org[0];\n\t\t\t\tresult[4]\t= t1->ang[1][0];\n\t\t\t\tresult[5]\t= t1->ang[1][1];\n\t\t\t\tresult[6]\t= t1->ang[1][2];\n\t\t\t\tresult[7]\t= t1->org[1];\n\t\t\t\tresult[8]\t= t1->ang[2][0];\n\t\t\t\tresult[9]\t= t1->ang[2][1];\n\t\t\t\tresult[10]\t= t1->ang[2][2];\n\t\t\t\tresult[11]\t= t1->org[2];\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tfloat f1ness = 1-f2ness;\n\t\t\t\tresult[0]\t= t1->ang[0][0]*f1ness\t+ t2->ang[0][0]*f2ness;\n\t\t\t\tresult[1]\t= t1->ang[0][1]*f1ness\t+ t2->ang[0][1]*f2ness;\n\t\t\t\tresult[2]\t= t1->ang[0][2]*f1ness\t+ t2->ang[0][2]*f2ness;\n\t\t\t\tresult[3]\t= t1->org[0]*f1ness\t\t+ t2->org[0]*f2ness;\n\t\t\t\tresult[4]\t= t1->ang[1][0]*f1ness\t+ t2->ang[1][0]*f2ness;\n\t\t\t\tresult[5]\t= t1->ang[1][1]*f1ness\t+ t2->ang[1][1]*f2ness;\n\t\t\t\tresult[6]\t= t1->ang[1][2]*f1ness\t+ t2->ang[1][2]*f2ness;\n\t\t\t\tresult[7]\t= t1->org[1]*f1ness\t\t+ t2->org[1]*f2ness;\n\t\t\t\tresult[8]\t= t1->ang[2][0]*f1ness\t+ t2->ang[2][0]*f2ness;\n\t\t\t\tresult[9]\t= t1->ang[2][1]*f1ness\t+ t2->ang[2][1]*f2ness;\n\t\t\t\tresult[10]\t= t1->ang[2][2]*f1ness\t+ t2->ang[2][2]*f2ness;\n\t\t\t\tresult[11]\t= t1->org[2]*f1ness\t\t+ t2->org[2]*f2ness;\n\t\t\t}\n\n\t\t\tVectorNormalize(result);\n\t\t\tVectorNormalize(result+4);\n\t\t\tVectorNormalize(result+8);\n\n\t\t\treturn true;\n\t\t}\n#endif\n\t}\n#endif\n\treturn false;\n}\n\nint Mod_TagNumForName(model_t *model, const char *name, int firsttag)\n{\n#ifdef SKELORTAGS\n\tint i;\n\tgaliasinfo_t *inf;\n\n\tif (!model)\n\t\treturn 0;\n\tif (model->loadstate != MLS_LOADED)\n\t{\n\t\tif (model->loadstate == MLS_NOTLOADED)\n\t\t\tMod_LoadModel(model, MLV_SILENT);\n\t\tif (model->loadstate == MLS_LOADING)\n\t\t\tCOM_WorkerPartialSync(model, &model->loadstate, MLS_LOADING);\n\t\tif (model->loadstate != MLS_LOADED)\n\t\t\treturn 0;\n\t}\n\n#ifdef HALFLIFEMODELS\n\tif (model->type == mod_halflife)\n\t\treturn HLMDL_BoneForName(model, name);\n#endif\n\tif (model->type == mod_alias)\n\t{\n\t\tinf = Mod_Extradata(model);\n\n#ifdef SKELETALMODELS\n\t\tif (inf->numbones)\n\t\t{\n\t\t\tgaliasbone_t *b;\n\t\t\tb = inf->ofsbones;\n\t\t\tfor (i = firsttag; i < inf->numbones; i++)\n\t\t\t{\n\t\t\t\tif (!strcmp(b[i].name, name))\n\t\t\t\t\treturn i+1;\n\t\t\t}\n\t\t}\n#endif\n#ifdef MD3MODELS\n\t\tif (inf->numtags)\n\t\t{\n\t\t\tmd3tag_t *t = inf->ofstags;\n\t\t\tfor (i = firsttag; i < inf->numtags; i++)\n\t\t\t{\n\t\t\t\tif (!strcmp(t[i].name, name))\n\t\t\t\t\treturn i+1;\n\t\t\t}\n\t\t}\n#endif\n\t\treturn 0;\n\t}\n#endif\n\treturn 0;\n}\n\nint Mod_FrameNumForName(model_t *model, int surfaceidx, const char *name)\n{\n\tgaliasanimation_t *group;\n\tgaliasinfo_t *inf;\n\tint i;\n\n\tif (!model)\n\t\treturn -1;\n#ifdef HALFLIFEMODELS\n\tif (model->type == mod_halflife)\n\t\treturn HLMDL_FrameForName(model, name);\n#endif\n\tif (model->type != mod_alias)\n\t\treturn 0;\n\n\tinf = Mod_Extradata(model);\n\n\twhile(surfaceidx-->0 && inf)\n\t\tinf = inf->nextsurf;\n\tif (inf)\n\t{\n\t\tgroup = inf->ofsanimations;\n\t\tfor (i = 0; i < inf->numanimations; i++, group++)\n\t\t{\n\t\t\tif (!strcmp(group->name, name))\n\t\t\t\treturn i;\n\t\t}\n\t}\n\treturn -1;\n}\n\nint Mod_FrameNumForAction(model_t *model, int surfaceidx, int actionid)\n{\n\tgaliasanimation_t *group;\n\tgaliasinfo_t *inf;\n\tint i;\n\tfloat weight;\n\n\tif (!model)\n\t\treturn -1;\n#ifdef HALFLIFEMODELS\n\tif (model->type == mod_halflife)\n\t\treturn HLMDL_FrameForAction(model, actionid);\n#endif\n\tif (model->type != mod_alias)\n\t\treturn -1;\n\n\tinf = Mod_Extradata(model);\n\n\twhile(surfaceidx-->0 && inf)\n\t\tinf = inf->nextsurf;\n\tif (inf)\n\t{\n\t\tfor (i = 0, weight = 0, group = inf->ofsanimations; i < inf->numanimations; i++, group++)\n\t\t{\n\t\t\tif (group->action == actionid)\n\t\t\t\tweight += group->actionweight;\n\t\t}\n\t\tweight *= frandom();\n\t\tfor (i = 0, group = inf->ofsanimations; i < inf->numanimations; i++, group++)\n\t\t{\n\t\t\tif (group->action == actionid)\n\t\t\t{\n\t\t\t\tif (weight <= group->actionweight)\n\t\t\t\t\treturn i;\n\t\t\t\tweight -= group->actionweight;\n\t\t\t}\n\t\t}\n\t}\n\treturn -1;\n}\n\n\nqboolean Mod_GetModelEvent(model_t *model, int animation, int eventidx, float *timestamp, int *eventcode, char **eventdata)\n{\n\tif (!model)\n\t\treturn false;\n\tif (model->type == mod_alias)\n\t{\n\t\tgaliasinfo_t *inf = Mod_Extradata(model);\n\t\tif (inf && animation >= 0 && animation < inf->numanimations)\n\t\t{\n\t\t\tgaliasanimation_t *anim = inf->ofsanimations + animation;\n\t\t\tgaliasevent_t *ev = anim->events;\n\t\t\twhile (eventidx-->0 && ev)\n\t\t\t\tev = ev->next;\n\t\t\tif (ev)\n\t\t\t{\n\t\t\t\t*timestamp = ev->timestamp;\n\t\t\t\t*eventcode = ev->code;\n\t\t\t\t*eventdata = ev->data;\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t}\n#ifdef HALFLIFEMODELS\n\tif (model->type == mod_halflife)\n\t\treturn HLMDL_GetModelEvent(model, animation, eventidx, timestamp, eventcode, eventdata);\n#endif\n\treturn false;\n}\n\n#ifndef SERVERONLY\nint Mod_SkinNumForName(model_t *model, int surfaceidx, const char *name)\n{\n\tint i;\n\tgaliasinfo_t *inf;\n\tgaliasskin_t *skin;\n\n\tif (!model || model->loadstate != MLS_LOADED)\n\t{\n\t\tif (model && model->loadstate == MLS_NOTLOADED)\n\t\t\tMod_LoadModel(model, MLV_SILENT);\n\t\tif (model && model->loadstate == MLS_LOADING)\n\t\t\tCOM_WorkerPartialSync(model, &model->loadstate, MLS_LOADING);\n\t\tif (!model || model->loadstate != MLS_LOADED || model->type != mod_alias)\n\t\t\treturn -1;\n\t}\n\tinf = Mod_Extradata(model);\n\n\twhile(surfaceidx-->0 && inf)\n\t\tinf = inf->nextsurf;\n\tif (inf)\n\t{\n\t\tskin = inf->ofsskins;\n\t\tfor (i = 0; i < inf->numskins; i++, skin++)\n\t\t{\n\t\t\tif (!strcmp(skin->name, name))\n\t\t\t\treturn i;\n\t\t}\n\t}\n\treturn -1;\n}\n#endif\n\nconst char *Mod_FrameNameForNum(model_t *model, int surfaceidx, int num)\n{\n\tgaliasanimation_t *group;\n\tgaliasinfo_t *inf;\n\n\tif (!model || model->loadstate != MLS_LOADED)\n\t{\n\t\tif (model && model->loadstate == MLS_NOTLOADED)\n\t\t\tMod_LoadModel(model, MLV_SILENT);\n\t\tif (model && model->loadstate == MLS_LOADING)\n\t\t\tCOM_WorkerPartialSync(model, &model->loadstate, MLS_LOADING);\n\t\tif (!model || model->loadstate != MLS_LOADED)\n\t\t\treturn NULL;\n\t}\n\tif (model->type == mod_alias)\n\t{\n\t\tinf = Mod_Extradata(model);\n\n\t\twhile(surfaceidx-->0 && inf)\n\t\t\tinf = inf->nextsurf;\n\n\t\tif (!inf || (unsigned)num >= (unsigned)inf->numanimations)\n\t\t\treturn NULL;\n\t\tgroup = inf->ofsanimations;\n\t\treturn group[num].name;\n\t}\n#ifdef HALFLIFEMODELS\n\tif (model->type == mod_halflife)\n\t\treturn HLMDL_FrameNameForNum(model, surfaceidx, num);\n#endif\n\treturn NULL;\n}\n\nqboolean Mod_FrameInfoForNum(model_t *model, int surfaceidx, int num, char **name, int *numframes, float *duration, qboolean *loop, int *act)\n{\n\tgaliasanimation_t *group;\n\tgaliasinfo_t *inf;\n\n\tif (!model || model->loadstate != MLS_LOADED)\n\t{\n\t\tif (model && model->loadstate == MLS_NOTLOADED)\n\t\t\tMod_LoadModel(model, MLV_SILENT);\n\t\tif (model && model->loadstate == MLS_LOADING)\n\t\t\tCOM_WorkerPartialSync(model, &model->loadstate, MLS_LOADING);\n\t\tif (!model || model->loadstate != MLS_LOADED)\n\t\t\treturn false;\n\t}\n\tif (model->type == mod_alias)\n\t{\n\t\tinf = Mod_Extradata(model);\n\n\t\twhile(surfaceidx-->0 && inf)\n\t\t\tinf = inf->nextsurf;\n\n\t\tif (!inf || num >= inf->numanimations)\n\t\t\treturn false;\n\t\tgroup = inf->ofsanimations;\n\t\tgroup += num;\n\n\t\t*name = group->name;\n\t\t*numframes = group->numposes;\n\t\t*loop = group->loop;\n\t\t*duration = group->numposes/group->rate;\n\t\t*act = group->action;\n\t\treturn true;\n\t}\n#ifdef HALFLIFEMODELS\n\tif (model->type == mod_halflife)\n\t\treturn HLMDL_FrameInfoForNum(model, surfaceidx, num, name, numframes, duration, loop, act);\n#endif\n\treturn false;\n}\n\n#ifndef SERVERONLY\nshader_t *Mod_ShaderForSkin(model_t *model, int surfaceidx, int num, float time, texnums_t **out_texnums)\n{\n\tgaliasinfo_t *inf;\n\tgaliasskin_t *skin;\n\tskinframe_t *skinframe;\n\n\t*out_texnums = NULL;\n\n\tif (!model || model->loadstate != MLS_LOADED)\n\t{\n\t\tif (model && model->loadstate == MLS_NOTLOADED)\n\t\t\tMod_LoadModel(model, MLV_SILENT);\n\t\tif (model && model->loadstate == MLS_LOADING)\n\t\t\tCOM_WorkerPartialSync(model, &model->loadstate, MLS_LOADING);\n\t\tif (!model || model->loadstate != MLS_LOADED)\n\t\t\treturn NULL;\n\t}\n\n\tswitch(model->type)\n\t{\n\tcase mod_brush:\n\t\tif (surfaceidx < model->numtextures && !num)\n\t\t\treturn model->textures[surfaceidx]->shader;\n\t\treturn NULL;\n\n\tcase mod_alias:\n\t\tinf = Mod_Extradata(model);\n\n\t\twhile(surfaceidx-->0 && inf)\n\t\t\tinf = inf->nextsurf;\n\n\t\tif (!inf || num >= inf->numskins)\n\t\t\treturn NULL;\n\t\tskin = inf->ofsskins;\n\t\tskin += num;\n\t\tskinframe = skin->frame;\n\t\tif (skin->numframes)\n\t\t\tskinframe += (int)(time*skin->skinspeed)%skin->numframes;\n\t\t*out_texnums = &skinframe->texnums;\n\t\treturn skinframe->shader;\n\tdefault:\n\t\treturn NULL;\n\t}\n}\n#endif\nconst char *Mod_SkinNameForNum(model_t *model, int surfaceidx, int num)\n{\n#ifdef SERVERONLY\n\treturn NULL;\n#else\n\tgaliasinfo_t *inf;\n\tgaliasskin_t *skin;\n\n\tif (!model || model->loadstate != MLS_LOADED)\n\t{\n\t\tif (model && model->loadstate == MLS_NOTLOADED)\n\t\t\tMod_LoadModel(model, MLV_SILENT);\n\t\tif (model && model->loadstate == MLS_LOADING)\n\t\t\tCOM_WorkerPartialSync(model, &model->loadstate, MLS_LOADING);\n\t\tif (!model || model->loadstate != MLS_LOADED)\n\t\t\treturn NULL;\n\t}\n\n\tswitch(model->type)\n\t{\n\tcase mod_brush:\n\t\tif (surfaceidx < model->numtextures && !num)\n\t\t\treturn \"\";\n\t\treturn NULL;\n\tcase mod_alias:\n\t\tinf = Mod_Extradata(model);\n\n\t\twhile(surfaceidx-->0 && inf)\n\t\t\tinf = inf->nextsurf;\n\t\tif (!inf || num >= inf->numskins)\n\t\t\treturn NULL;\n\t\tskin = inf->ofsskins;\n\t//\tif (!*skin[num].name)\n\t//\t\treturn skin[num].frame[0].shadername;\n\t//\telse\n\t\t\treturn skin[num].name;\n\tdefault:\n\t\treturn NULL;\n\t}\n#endif\n}\n\nconst char *Mod_SurfaceNameForNum(model_t *model, int num)\n{\n#ifdef SERVERONLY\n\treturn NULL;\n#else\n\tgaliasinfo_t *inf;\n\n\tif (!model || model->loadstate != MLS_LOADED)\n\t{\n\t\tif (model && model->loadstate == MLS_NOTLOADED)\n\t\t\tMod_LoadModel(model, MLV_SILENT);\n\t\tif (model && model->loadstate == MLS_LOADING)\n\t\t\tCOM_WorkerPartialSync(model, &model->loadstate, MLS_LOADING);\n\t\tif (!model || model->loadstate != MLS_LOADED)\n\t\t\treturn NULL;\n\t}\n\n\tswitch(model->type)\n\t{\n\tcase mod_brush:\n\t\tif (model->type == mod_brush && num < model->numtextures)\n\t\t\treturn model->textures[num]->name;\n\t\treturn NULL;\n\tcase mod_halflife:\n\t\treturn NULL;\n\tcase mod_alias:\n\t\tinf = Mod_Extradata(model);\n\n\t\twhile(num-->0 && inf)\n\t\t\tinf = inf->nextsurf;\n\t\tif (inf)\n\t\t\treturn inf->surfacename;\n\t\telse\n\t\t\treturn NULL;\n\tcase mod_sprite:\n\tcase mod_dummy:\n\tcase mod_heightmap:\n\tdefault:\n\t\treturn NULL;\n\t}\n#endif\n}\n\nfloat Mod_GetFrameDuration(model_t *model, int surfaceidx, int frameno)\n{\n\tgaliasinfo_t *inf;\n\tgaliasanimation_t *group;\n\n#ifdef HALFLIFEMODELS\n\tif (model && model->type == mod_halflife) {\n\t\tint unused;\n\t\tfloat duration;\n\t\tchar *name;\n\t\tqboolean loop;\n\t\tint act;\n\t\tHLMDL_FrameInfoForNum(model, surfaceidx, frameno, &name, &unused, &duration, &loop, &act);\n\t\treturn duration;\n\t}\n#endif\n\t\n\tif (!model || model->type != mod_alias)\n\t\treturn 0;\n\n\tinf = Mod_Extradata(model);\n\t\n\twhile(surfaceidx-->0 && inf)\n\t\tinf = inf->nextsurf;\n\n\tif (inf)\n\t{\n\t\tgroup = inf->ofsanimations;\n\t\tif (frameno >= 0 && frameno < inf->numanimations)\n\t\t{\n\t\t\tgroup += frameno;\n\t\t\treturn group->numposes/group->rate;\n\t\t}\n\t}\n\treturn 0;\n}\n\nint Mod_GetFrameCount(struct model_s *model)\n{\n\tif (!model)\n\t\treturn 0;\n\treturn model->numframes;\n}\n\n\n#ifdef MD3MODELS\n\n//structures from Tenebrae\ntypedef struct {\n\tint\t\t\tident;\n\tint\t\t\tversion;\n\n\tchar\t\tname[64];\n\n\tint\t\t\tflags;\t//Does anyone know what these are?\n\n\tint\t\t\tnumFrames;\n\tint\t\t\tnumTags;\n\tint\t\t\tnumSurfaces;\n\n\tint\t\t\tnumSkins;\n\n\tint\t\t\tofsFrames;\n\tint\t\t\tofsTags;\n\tint\t\t\tofsSurfaces;\n\tint\t\t\tofsEnd;\n} md3Header_t;\n\n//then has header->numFrames of these at header->ofs_Frames\ntypedef struct md3Frame_s {\n\tvec3_t\t\tbounds[2];\n\tvec3_t\t\tlocalOrigin;\n\tfloat\t\tradius;\n\tchar\t\tname[16];\n} md3Frame_t;\n\n//there are header->numSurfaces of these at header->ofsSurfaces, following from ofsEnd\ntypedef struct {\n\tint\t\tident;\t\t\t\t//\n\n\tchar\tname[64];\t// polyset name\n\n\tint\t\tflags;\n\tint\t\tnumFrames;\t\t\t// all surfaces in a model should have the same\n\n\tint\t\tnumShaders;\t\t\t// all surfaces in a model should have the same\n\tint\t\tnumVerts;\n\n\tint\t\tnumTriangles;\n\tint\t\tofsTriangles;\n\n\tint\t\tofsShaders;\t\t\t// offset from start of md3Surface_t\n\tint\t\tofsSt;\t\t\t\t// texture coords are common for all frames\n\tint\t\tofsXyzNormals;\t\t// numVerts * numFrames\n\n\tint\t\tofsEnd;\t\t\t\t// next surface follows\n} md3Surface_t;\n\n//at surf+surf->ofsXyzNormals\ntypedef struct {\n\tshort\t\txyz[3];\n\tqbyte\t\tlatlong[2];\n} md3XyzNormal_t;\n\n//surf->numTriangles at surf+surf->ofsTriangles\ntypedef struct {\n\tint\t\t\tindexes[3];\n} md3Triangle_t;\n\n//surf->numVerts at surf+surf->ofsSt\ntypedef struct {\n\tfloat\t\ts;\n\tfloat\t\tt;\n} md3St_t;\n\ntypedef struct {\n\tchar\t\t\tname[64];\n\tint\t\t\t\tshaderIndex;\n} md3Shader_t;\n//End of Tenebrae 'assistance'\n\n//q3 loads foo.md3, foo_1.md3, foo_2.md3 etc\nstatic galiasinfo_t *Mod_LoadQ3ModelLod(model_t *mod, int *surfcount, void *buffer, size_t fsize)\n{\n#ifndef SERVERONLY\n\tgaliasskin_t\t*skin;\n\tskinframe_t\t*frames;\n\tfloat lat, lng;\n\tmd3St_t\t\t\t*inst;\n\tvec3_t *normals;\n\tvec3_t *svector;\n\tvec3_t *tvector;\n\tvec2_t *st_array;\n\tmd3Shader_t\t\t*inshader;\n\tint externalskins;\n#endif\n//\tint version;\n\tint s, i, j, d;\n\n\tindex_t *indexes;\n\n\tvec3_t min;\n\tvec3_t max;\n\n\tgaliaspose_t *pose;\n\tgaliasanimation_t *group;\n\n\tvecV_t *verts;\n\n\tmd3Triangle_t\t*intris;\n\tmd3XyzNormal_t\t*invert;\n\tgaliasinfo_t\t*first = NULL;\n\tgaliasinfo_t\t**surflink = &first;\n\n\n\tint size;\n\n\tmd3Header_t\t\t*header;\n\tmd3Surface_t\t*surf;\n\tgaliasinfo_t\t*galias;\n\tunsigned int\tnumposes, numverts;\n\n\tframeinfo_t\t\t*framegroups;\n\tunsigned int\tnumgroups;\n\n\theader = buffer;\n\n//\tif (header->version != sdfs)\n//\t\tSys_Error(\"GL_LoadQ3Model: Bad version\\n\");\n\n#ifndef SERVERONLY\n\texternalskins = Mod_CountSkinFiles(mod);\n#endif\n\n\tframegroups = ParseFrameInfo(mod, &numgroups);\n\n\tClearBounds(min, max);\n\n\tsurf = (md3Surface_t *)((qbyte *)header + LittleLong(header->ofsSurfaces));\n\tfor (s = 0; s < LittleLong(header->numSurfaces); s++, *surfcount+=1)\n\t{\n\t\tif (memcmp(&surf->ident, MD3_IDENT))\n\t\t\tCon_Printf(CON_WARNING \"Warning: md3 sub-surface doesn't match ident\\n\");\n\n\t\tif (!framegroups)\n\t\t\tnumgroups = LittleLong(header->numFrames);\n\t\tnumposes = LittleLong(surf->numFrames);\n\t\tnumverts = LittleLong(surf->numVerts);\n\n\t\tsize = sizeof(galiasinfo_t) + sizeof(galiasanimation_t)*numgroups;\n\t\tgalias = ZG_Malloc(&mod->memgroup, size);\n\t\tMod_DefaultMesh(galias, surf->name, s);\n\t\tgalias->ofsanimations = group = (galiasanimation_t*)(galias+1);\t//frame groups\n\t\tgalias->numanimations = numgroups;\n\t\tgalias->numverts = numverts;\n\t\tgalias->numindexes = LittleLong(surf->numTriangles)*3;\n\t\tgalias->shares_verts = *surfcount; //with itself, so no sharing.\n\n\t\tif (!first)\n\t\t\tfirst = galias;\n\t\t*surflink = galias;\n\t\tsurflink = &galias->nextsurf;\n\t\tgalias->nextsurf = NULL;\n\n\t\t//load the texcoords\n#ifndef SERVERONLY\n\t\tst_array = ZG_Malloc(&mod->memgroup, sizeof(vec2_t)*galias->numindexes);\n\t\tgalias->ofs_st_array = st_array;\n\t\tinst = (md3St_t*)((qbyte*)surf + LittleLong(surf->ofsSt));\n\t\tfor (i = 0; i < galias->numverts; i++)\n\t\t{\n\t\t\tst_array[i][0] = LittleFloat(inst[i].s);\n\t\t\tst_array[i][1] = LittleFloat(inst[i].t);\n\t\t}\n#endif\n\n\t\t//load the index data\n\t\tindexes = ZG_Malloc(&mod->memgroup, sizeof(*indexes)*galias->numindexes);\n\t\tgalias->ofs_indexes = indexes;\n\t\tintris = (md3Triangle_t *)((qbyte*)surf + LittleLong(surf->ofsTriangles));\n\t\tfor (i = 0; i < LittleLong(surf->numTriangles); i++)\n\t\t{\n\t\t\tindexes[i*3+0] = LittleLong(intris[i].indexes[0]);\n\t\t\tindexes[i*3+1] = LittleLong(intris[i].indexes[1]);\n\t\t\tindexes[i*3+2] = LittleLong(intris[i].indexes[2]);\n\n\t\t\tif (indexes[i*3+0] >= numverts || indexes[i*3+1] >= numverts || indexes[i*3+2] >= numverts)\n\t\t\t{\n\t\t\t\tCon_Printf(CON_WARNING \"Warning: surface %s has invalid vertex indexes\\n\", galias->surfacename);\n\t\t\t\tindexes[i*3+0] = indexes[i*3+1] = indexes[i*3+2] = 0;\n\t\t\t}\n\t\t}\n\n\t\t//figure out where we're putting the pose data\n\t\tsize = sizeof(galiaspose_t) + sizeof(vecV_t)*numverts;\n#ifndef SERVERONLY\n\t\tsize += 3*sizeof(vec3_t)*numverts;\n#endif\n\t\tsize *= numposes;\n\t\tpose = (galiaspose_t *)ZG_Malloc(&mod->memgroup, size);\n\t\tverts = (vecV_t*)(pose+numposes);\n#ifndef SERVERONLY\n\t\tnormals = (vec3_t*)(verts + numverts*numposes);\n\t\tsvector = (vec3_t*)(normals + numverts*numposes);\n\t\ttvector = (vec3_t*)(svector + numverts*numposes);\n#endif\n\n\t\t//load in that per-pose data\n\t\tinvert = (md3XyzNormal_t *)((qbyte*)surf + LittleLong(surf->ofsXyzNormals));\n\t\tfor (i = 0; i < numposes; i++)\n\t\t{\n\t\t\tfor (j = 0; j < numverts; j++)\n\t\t\t{\n#ifndef SERVERONLY\n\t\t\t\tlat = (float)invert[j].latlong[0] * (2 * M_PI)*(1.0 / 255.0);\n\t\t\t\tlng = (float)invert[j].latlong[1] * (2 * M_PI)*(1.0 / 255.0);\n\t\t\t\tnormals[j][0] = cos ( lng ) * sin ( lat );\n\t\t\t\tnormals[j][1] = sin ( lng ) * sin ( lat );\n\t\t\t\tnormals[j][2] = cos ( lat );\n#endif\n\t\t\t\tfor (d = 0; d < 3; d++)\n\t\t\t\t{\n\t\t\t\t\tverts[j][d] = LittleShort(invert[j].xyz[d])/64.0f;\n\t\t\t\t\tif (verts[j][d]<min[d])\n\t\t\t\t\t\tmin[d] = verts[j][d];\n\t\t\t\t\tif (verts[j][d]>max[d])\n\t\t\t\t\t\tmax[d] = verts[j][d];\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tpose->scale[0] = 1;\n\t\t\tpose->scale[1] = 1;\n\t\t\tpose->scale[2] = 1;\n\n\t\t\tpose->scale_origin[0] = 0;\n\t\t\tpose->scale_origin[1] = 0;\n\t\t\tpose->scale_origin[2] = 0;\n\n\t\t\tinvert += LittleLong(surf->numVerts);\n\n\t\t\tpose->ofsverts = verts;\n\t\t\tverts += numverts;\n#ifndef SERVERONLY\n\t\t\tpose->ofsnormals = normals;\n\t\t\tnormals += numverts;\n\t\t\tpose->ofssvector = svector;\n\t\t\tsvector += numverts;\n\t\t\tpose->ofstvector = tvector;\n\t\t\ttvector += numverts;\n#endif\n\t\t\tpose++;\n\t\t}\n\t\tpose -= numposes;\n\n\t\tif (framegroups)\n\t\t{\t//group the poses into animations.\n\t\t\tfor (i = 0; i < numgroups; i++)\n\t\t\t{\n\t\t\t\tQ_snprintfz(group->name, sizeof(group->name), \"%s\", framegroups[i].name);\n\n\t\t\t\tif (framegroups[i].posesarray)\n\t\t\t\t{\n\t\t\t\t\tunsigned int p, targpose;\n\t\t\t\t\tgroup->poseofs = ZG_Malloc(&mod->memgroup, sizeof(*group->poseofs) * framegroups[i].posecount);\n\t\t\t\t\tgroup->numposes = 0;\n\t\t\t\t\tfor (p = 0; p < framegroups[i].posecount; p++)\n\t\t\t\t\t{\n\t\t\t\t\t\ttargpose = framegroups[i].poses[p];\n\t\t\t\t\t\tif (targpose < numposes)\n\t\t\t\t\t\t\tgroup->poseofs[group->numposes++] = pose[targpose];\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tint first = framegroups[i].firstpose, count = framegroups[i].posecount;\n\t\t\t\t\tif (first >= numposes)\t//bound the numbers.\n\t\t\t\t\t\tfirst = numposes-1;\n\t\t\t\t\tif (first < 0)\n\t\t\t\t\t\tfirst = 0;\n\t\t\t\t\tif (count > numposes-first)\n\t\t\t\t\t\tcount = numposes-first;\n\t\t\t\t\tif (count < 0)\n\t\t\t\t\t\tcount = 0;\n\t\t\t\t\tgroup->poseofs = pose + first;\n\t\t\t\t\tgroup->numposes = count;\n\t\t\t\t}\n\n\t\t\t\tgroup->rate = framegroups[i].fps;\n\t\t\t\tgroup->loop = framegroups[i].loop;\n\t\t\t\tgroup->events = framegroups[i].events;\n\t\t\t\tgroup->action = framegroups[i].action;\n\t\t\t\tgroup->actionweight = framegroups[i].actionweight;\n\t\t\t\tgroup++;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\t//raw poses, no animations.\n\t\t\tfor (i = 0; i < numgroups; i++)\n\t\t\t{\n\t\t\t\tQ_snprintfz(group->name, sizeof(group->name), \"frame%i\", i);\n\t\t\t\tgroup->numposes = 1;\n\t\t\t\tgroup->rate = 1;\n\t\t\t\tgroup->poseofs = pose + i;\n\t\t\t\tgroup->loop = false;\n\t\t\t\tgroup->events = NULL;\n\t\t\t\tgroup->action = -1;\n\t\t\t\tgroup->actionweight = 0;\n\t\t\t\tgroup++;\n\t\t\t}\n\t\t}\n\n#ifndef SERVERONLY\n\t\tif (externalskins<LittleLong(surf->numShaders))\n\t\t\texternalskins = LittleLong(surf->numShaders);\n\t\tif (externalskins)\n\t\t{\n\t\t\tskin = ZG_Malloc(&mod->memgroup, (externalskins)*((sizeof(galiasskin_t)+sizeof(skinframe_t))));\n\t\t\tgalias->ofsskins = skin;\n\t\t\tframes = (skinframe_t *)(skin + externalskins);\n\t\t\tinshader = (md3Shader_t *)((qbyte *)surf + LittleLong(surf->ofsShaders));\n\t\t\tfor (i = 0; i < externalskins; i++)\n\t\t\t{\n\t\t\t\tskin->numframes = 1;\n\t\t\t\tskin->frame = &frames[i];\n\t\t\t\tskin->skinwidth = 0;\n\t\t\t\tskin->skinheight = 0;\n\t\t\t\tskin->skinspeed = 0;\n\n\t\t\t\tif (i >= LittleLong(surf->numShaders))\n\t\t\t\t\tQ_strncpyz(frames->shadername, \"\", sizeof(frames->shadername));\t//this shouldn't be possible\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tQ_strncpyz(frames->shadername, inshader->name, sizeof(frames->shadername));\n\t\t\t\t\tQ_strncpyz(skin->name, inshader->name, sizeof(frames->shadername));\n\t\t\t\t}\n\n\t\t\t\tinshader++;\n\t\t\t\tskin++;\n\t\t\t}\n\t\t\tgalias->numskins = i;\n\t\t}\n#endif\n\n\t\tMod_CompileTriangleNeighbours (mod, galias);\n\t\tMod_BuildTextureVectors(galias);\n\n\t\t//if the surfacename is eg _1 then strip that off.\n\t\t//this works around q3data requiring different surface names in higher lod levels (and matches q3).\n\t\tsize = strlen(galias->surfacename);\n\t\tif (size>2 && galias->surfacename[size-2] == '_')\n\t\t\tgalias->surfacename[size-2] = 0;\n\n\t\tsurf = (md3Surface_t *)((qbyte *)surf + LittleLong(surf->ofsEnd));\n\t}\n\n\tAddPointToBounds(min, mod->mins, mod->maxs);\n\tAddPointToBounds(max, mod->mins, mod->maxs);\n\tfree(framegroups);\n\treturn first;\n}\nstatic qboolean QDECL Mod_LoadQ3Model(model_t *mod, void *buffer, size_t fsize)\n{\n//\tint version;\n\n\tgaliasinfo_t\t*root;\n\tmd3Header_t\t\t*header;\n\n\tint lod;\n\tint surfcount = 0;\n\n\theader = buffer;\n\n//\tif (header->version != sdfs)\n//\t\tSys_Error(\"GL_LoadQ3Model: Bad version\\n\");\n\n\troot = NULL;\n\n\tClearBounds(mod->mins, mod->maxs);\n\n\tmod->meshinfo = Mod_LoadQ3ModelLod(mod, &surfcount, buffer, fsize);\n\tif (mod->meshinfo)\n\t{\n\t\tconst char *ext = COM_GetFileExtension(mod->name, NULL);\n\t\tif (*ext == '.' && ext-mod->name < MAX_QPATH)\n\t\t{\n\t\t\tgaliasinfo_t *sublod;\n\t\t\tsublod = mod->meshinfo;\n\t\t\tfor(lod = 1; lod > 0; lod++)\n\t\t\t{\n\t\t\t\tsize_t s;\n\t\t\t\tchar *f;\n\t\t\t\tchar basename[MAX_QPATH];\n\t\t\t\tchar lodname[MAX_QPATH];\n\t\t\t\tmemcpy(basename, mod->name, ext-mod->name);\n\t\t\t\tbasename[ext-mod->name] = 0;\n\t\t\t\tQ_snprintfz(lodname, sizeof(lodname), \"%s_%i%s\", basename, lod, ext);\n\t\t\t\tf = FS_LoadMallocFile(lodname, &s);\n\t\t\t\tif (!f)\n\t\t\t\t\tbreak;\n\t\t\t\troot = sublod;\n\t\t\t\tsublod = Mod_LoadQ3ModelLod(mod, &surfcount, f, s);\n\t\t\t\tif (sublod)\n\t\t\t\t{\n\t\t\t\t\tfor(;;)\n\t\t\t\t\t{\n\t\t\t\t\t\troot->maxdist = lod;\n\t\t\t\t\t\tif (!root->nextsurf)\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\troot = root->nextsurf;\n\t\t\t\t\t}\n\t\t\t\t\troot->nextsurf = sublod;\n\t\t\t\t\tfor (root = sublod; root; root = root->nextsurf)\n\t\t\t\t\t\troot->mindist = lod;\n\t\t\t\t\tmod->maxlod = lod+1;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tlod = -1;\n\t\t\t\tFS_FreeFile(f);\n\t\t\t}\n\t\t}\n\t}\n\n\troot = mod->meshinfo;\n\tif (!root)\n\t{\n\t\troot = ZG_Malloc(&mod->memgroup, sizeof(galiasinfo_t));\n\t\tMod_DefaultMesh(root, mod->name, 0);\n\t}\n\n\troot->numtagframes = LittleLong(header->numFrames);\n\troot->numtags = LittleLong(header->numTags);\n\troot->ofstags = ZG_Malloc(&mod->memgroup, LittleLong(header->numTags)*sizeof(md3tag_t)*LittleLong(header->numFrames));\n\n\t{\n\t\tmd3tag_t *src;\n\t\tmd3tag_t *dst;\n\t\tint s, i, j;\n\n\t\tsrc = (md3tag_t *)((char*)header+LittleLong(header->ofsTags));\n\t\tdst = root->ofstags;\n\t\tfor(i=0;i<LittleLong(header->numTags)*LittleLong(header->numFrames);i++)\n\t\t{\n\t\t\tmemcpy(dst->name, src->name, sizeof(dst->name));\n\t\t\tfor(j=0;j<3;j++)\n\t\t\t{\n\t\t\t\tdst->org[j] = LittleFloat(src->org[j]);\n\t\t\t}\n\n\t\t\tfor(j=0;j<3;j++)\n\t\t\t{\n\t\t\t\tfor(s=0;s<3;s++)\n\t\t\t\t{\n\t\t\t\t\tdst->ang[j][s] = LittleFloat(src->ang[j][s]);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tsrc++;\n\t\t\tdst++;\n\t\t}\n\t}\n\n\tmod->radius = RadiusFromBounds(mod->mins, mod->maxs);\n\n#ifndef SERVERONLY\n\tif (mod_md3flags.value)\n\t\tmod->flags = LittleLong(header->flags);\n\telse\n#endif\n\t\tmod->flags = 0;\n\tif (!mod->flags)\n\t\tmod->flags = Mod_ReadFlagsFromMD1(mod->name, 0);\n\n\tMod_ClampModelSize(mod);\n\tMod_ParseModelEvents(mod, root->ofsanimations, root->numanimations);\n\n\tmod->type = mod_alias;\n\tmod->numframes = root->numanimations;\n\tmod->meshinfo = root;\n\n\tmod->funcs.NativeTrace = Mod_Trace;\n\n\treturn true;\n}\n#endif\n\n\n\n\n\n\n\n\n\n\n#ifdef ZYMOTICMODELS\n\n\ntypedef struct zymlump_s\n{\n\tint start;\n\tint length;\n} zymlump_t;\n\ntypedef struct zymtype1header_s\n{\n\tchar id[12]; // \"ZYMOTICMODEL\", length 12, no termination\n\tint type; // 0 (vertex morph) 1 (skeletal pose) or 2 (skeletal scripted)\n\tint filesize; // size of entire model file\n\tfloat mins[3], maxs[3], radius; // for clipping uses\n\tint numverts;\n\tint numtris;\n\tint numsurfaces;\n\tint numbones; // this may be zero in the vertex morph format (undecided)\n\tint numscenes; // 0 in skeletal scripted models\n\n// skeletal pose header\n\t// lump offsets are relative to the file\n\tzymlump_t lump_scenes; // zymscene_t scene[numscenes]; // name and other information for each scene (see zymscene struct)\n\tzymlump_t lump_poses; // float pose[numposes][numbones][6]; // animation data\n\tzymlump_t lump_bones; // zymbone_t bone[numbones];\n\tzymlump_t lump_vertbonecounts; // int vertbonecounts[numvertices]; // how many bones influence each vertex (separate mainly to make this compress better)\n\tzymlump_t lump_verts; // zymvertex_t vert[numvertices]; // see vertex struct\n\tzymlump_t lump_texcoords; // float texcoords[numvertices][2];\n\tzymlump_t lump_render; // int renderlist[rendersize]; // sorted by shader with run lengths (int count), shaders are sequentially used, each run can be used with glDrawElements (each triangle is 3 int indices)\n\tzymlump_t lump_surfnames; // char shadername[numsurfaces][32]; // shaders used on this model\n\tzymlump_t lump_trizone; // byte trizone[numtris]; // see trizone explanation\n} zymtype1header_t;\n\ntypedef struct zymbone_s\n{\n\tchar name[32];\n\tint flags;\n\tint parent; // parent bone number\n} zymbone_t;\n\ntypedef struct zymscene_s\n{\n\tchar name[32];\n\tfloat mins[3], maxs[3], radius; // for clipping\n\tfloat framerate; // the scene will animate at this framerate (in frames per second)\n\tint flags;\n\tint start, length; // range of poses\n} zymscene_t;\n#define ZYMSCENEFLAG_NOLOOP 1\n\ntypedef struct zymvertex_s\n{\n\tint bonenum;\n\tfloat origin[3];\n} zymvertex_t;\n\n//this can generate multiple meshes (one for each shader).\n//but only one set of transforms are ever generated.\nstatic qboolean QDECL Mod_LoadZymoticModel(model_t *mod, void *buffer, size_t fsize)\n{\n#ifndef SERVERONLY\n\tgaliasskin_t *skin;\n\tskinframe_t *skinframe;\n\tint skinfiles;\n\tint j;\n#endif\n\n\tint i;\n\n\tzymtype1header_t *header;\n\tgaliasinfo_t *root;\n\n\tsize_t numtransforms;\n\tgalisskeletaltransforms_t *transforms;\n\tzymvertex_t\t*intrans;\n\n\tgaliasbone_t *bone;\n\tzymbone_t *inbone;\n\tint v;\n\tfloat multiplier;\n\tfloat *matrix, *inmatrix;\n\n\tvec2_t *stcoords;\n\tvec2_t *inst;\n\n\tint *vertbonecounts;\n\n\tgaliasanimation_t *grp;\n\tzymscene_t *inscene;\n\n\tint *renderlist, count;\n\tindex_t *indexes;\n\n\tchar *surfname;\n\n\theader = buffer;\n\n\tif (memcmp(header->id, \"ZYMOTICMODEL\", 12))\n\t{\n\t\tCon_Printf(\"Mod_LoadZymoticModel: %s, doesn't appear to BE a zymotic!\\n\", mod->name);\n\t\treturn false;\n\t}\n\n\tif (BigLong(header->type) != 1)\n\t{\n\t\tCon_Printf(\"Mod_LoadZymoticModel: %s, only type 1 is supported\\n\", mod->name);\n\t\treturn false;\n\t}\n\n\tfor (i = 0; i < sizeof(zymtype1header_t)/4; i++)\n\t\t((int*)header)[i] = BigLong(((int*)header)[i]);\n\n\tif (!header->numverts)\n\t{\n\t\tCon_Printf(\"Mod_LoadZymoticModel: %s, no vertexes\\n\", mod->name);\n\t\treturn false;\n\t}\n\n\tif (!header->numsurfaces)\n\t{\n\t\tCon_Printf(\"Mod_LoadZymoticModel: %s, no surfaces\\n\", mod->name);\n\t\treturn false;\n\t}\n\n\tVectorCopy(header->mins, mod->mins);\n\tVectorCopy(header->maxs, mod->maxs);\n\n\troot = ZG_Malloc(&mod->memgroup, sizeof(galiasinfo_t)*header->numsurfaces);\n\n\tnumtransforms = header->lump_verts.length/sizeof(zymvertex_t);\n\ttransforms = Z_Malloc(numtransforms*sizeof(*transforms));\n\n\tvertbonecounts = (int *)((char*)header + header->lump_vertbonecounts.start);\n\tintrans = (zymvertex_t *)((char*)header + header->lump_verts.start);\n\n\tvertbonecounts[0] = BigLong(vertbonecounts[0]);\n\tmultiplier = 1.0f / vertbonecounts[0];\n\tfor (i = 0, v=0; i < numtransforms; i++)\n\t{\n\t\twhile(!vertbonecounts[v])\n\t\t{\n\t\t\tv++;\n\t\t\tif (v == header->numverts)\n\t\t\t{\n\t\t\t\tCon_Printf(\"Mod_LoadZymoticModel: %s, too many transformations\\n\", mod->name);\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tvertbonecounts[v] = BigLong(vertbonecounts[v]);\n\t\t\tmultiplier = 1.0f / vertbonecounts[v];\n\t\t}\n\t\ttransforms[i].vertexindex = v;\n\t\ttransforms[i].boneindex = BigLong(intrans[i].bonenum);\n\t\ttransforms[i].org[0] = multiplier*BigFloat(intrans[i].origin[0]);\n\t\ttransforms[i].org[1] = multiplier*BigFloat(intrans[i].origin[1]);\n\t\ttransforms[i].org[2] = multiplier*BigFloat(intrans[i].origin[2]);\n\t\ttransforms[i].org[3] = multiplier*1;\n\t\tvertbonecounts[v]--;\n\t}\n\tif (intrans != (zymvertex_t *)((char*)header + header->lump_verts.start))\n\t{\n\t\tCon_Printf(CON_ERROR \"%s, Vertex transforms list appears corrupt.\\n\", mod->name);\n\t\treturn false;\n\t}\n\tif (vertbonecounts != (int *)((char*)header + header->lump_vertbonecounts.start))\n\t{\n\t\tCon_Printf(CON_ERROR \"%s, Vertex bone counts list appears corrupt.\\n\", mod->name);\n\t\treturn false;\n\t}\n\n\troot->numverts = v+1;\n\n\troot->numbones = header->numbones;\n\tbone = ZG_Malloc(&mod->memgroup, numtransforms*sizeof(*transforms));\n\tinbone = (zymbone_t*)((char*)header + header->lump_bones.start);\n\tfor (i = 0; i < root->numbones; i++)\n\t{\n\t\tQ_strncpyz(bone[i].name, inbone[i].name, sizeof(bone[i].name));\n\t\tbone[i].parent = BigLong(inbone[i].parent);\n\t}\n\troot->ofsbones = bone;\n\n\trenderlist = (int*)((char*)header + header->lump_render.start);\n\tfor (i = 0;i < header->numsurfaces; i++)\n\t{\n\t\tcount = BigLong(*renderlist++);\n\t\tcount *= 3;\n\t\tindexes = ZG_Malloc(&mod->memgroup, count*sizeof(*indexes));\n\t\troot[i].ofs_indexes = indexes;\n\t\troot[i].numindexes = count;\n\t\twhile(count)\n\t\t{\t//invert\n\t\t\tindexes[count-1] = BigLong(renderlist[count-3]);\n\t\t\tindexes[count-2] = BigLong(renderlist[count-2]);\n\t\t\tindexes[count-3] = BigLong(renderlist[count-1]);\n\t\t\tcount-=3;\n\t\t}\n\t\trenderlist += root[i].numindexes;\n\t}\n\tif (renderlist != (int*)((char*)header + header->lump_render.start + header->lump_render.length))\n\t{\n\t\tCon_Printf(CON_ERROR \"%s, render list appears corrupt.\\n\", mod->name);\n\t\treturn false;\n\t}\n\n\tgrp = ZG_Malloc(&mod->memgroup, sizeof(*grp)*header->numscenes*header->numsurfaces);\n\tmatrix = ZG_Malloc(&mod->memgroup, header->lump_poses.length);\n\tinmatrix = (float*)((char*)header + header->lump_poses.start);\n\tfor (i = 0; i < header->lump_poses.length/4; i++)\n\t\tmatrix[i] = BigFloat(inmatrix[i]);\n\tinscene = (zymscene_t*)((char*)header + header->lump_scenes.start);\n\tsurfname = ((char*)header + header->lump_surfnames.start);\n\n\tstcoords = ZG_Malloc(&mod->memgroup, root[0].numverts*sizeof(vec2_t));\n\tinst = (vec2_t *)((char *)header + header->lump_texcoords.start);\n\tfor (i = 0; i < header->lump_texcoords.length/8; i++)\n\t{\n\t\tstcoords[i][0] = BigFloat(inst[i][0]);\n\t\tstcoords[i][1] = 1-BigFloat(inst[i][1]);\t//hmm. upside down skin coords?\n\t}\n\n#ifndef SERVERONLY\n\tskinfiles = Mod_CountSkinFiles(mod);\n\tif (skinfiles < 1)\n\t\tskinfiles = 1;\n\n\tskin = ZG_Malloc(&mod->memgroup, (sizeof(galiasskin_t)+sizeof(skinframe_t))*skinfiles*header->numsurfaces);\n\tskinframe = (skinframe_t*)(skin+skinfiles*header->numsurfaces);\n#endif\n\n\tfor (i = 0; i < header->numsurfaces; i++, surfname+=32)\n\t{\n\t\tMod_DefaultMesh(&root[i], surfname, i);\n\t\troot[i].numanimations = header->numscenes;\n\t\troot[i].ofsanimations = grp;\n\n#ifdef SERVERONLY\n\t\troot[i].numskins = 1;\n#else\n\t\troot[i].ofs_st_array = stcoords;\n\t\troot[i].numskins = skinfiles;\n\t\troot[i].ofsskins = skin;\n\n\t\tfor (j = 0; j < skinfiles; j++)\n\t\t{\n\t\t\tskin->skinwidth = 1;\n\t\t\tskin->skinheight = 1;\n\t\t\tskin->skinspeed = 10; /*something to avoid div-by-0*/\n\t\t\tskin->numframes = 1;\t//non-sequenced skins.\n\t\t\tskin->frame = skinframe;\n\t\t\tskin++;\n\n\t\t\tQ_strncpyz(skinframe->shadername, surfname, sizeof(skinframe->shadername));\n\t\t\tskinframe++;\n\t\t}\n#endif\n\t}\n\n\n\tfor (i = 0; i < header->numscenes; i++, grp++, inscene++)\n\t{\n\t\tQ_strncpyz(grp->name, inscene->name, sizeof(grp->name));\n\n\t\tgrp->skeltype = SKEL_RELATIVE;\n\t\tgrp->rate = BigFloat(inscene->framerate);\n\t\tgrp->loop = !(BigLong(inscene->flags) & ZYMSCENEFLAG_NOLOOP);\n\t\tgrp->numposes = BigLong(inscene->length);\n\t\tgrp->boneofs = matrix + BigLong(inscene->start)*12*root->numbones;\n\t\tgrp->action = -1;\n\t\tgrp->actionweight = 0;\n\t}\n\n\tif (inscene != (zymscene_t*)((char*)header + header->lump_scenes.start+header->lump_scenes.length))\n\t{\n\t\tCon_Printf(CON_ERROR \"%s, scene list appears corrupt.\\n\", mod->name);\n\t\treturn false;\n\t}\n\n\tAlias_BuildGPUWeights(mod, root, numtransforms, transforms, true);\n\n\tfor (i = 0; i < header->numsurfaces-1; i++)\n\t\troot[i].nextsurf = &root[i+1];\n\tfor (i = 1; i < header->numsurfaces; i++)\n\t{\n\t\troot[i].ofs_skel_xyz = root[0].ofs_skel_xyz;\n\t\troot[i].ofs_skel_norm = root[0].ofs_skel_norm;\n\t\troot[i].ofs_skel_svect = root[0].ofs_skel_svect;\n\t\troot[i].ofs_skel_tvect = root[0].ofs_skel_tvect;\n\t\troot[i].ofs_skel_idx = root[0].ofs_skel_idx;\n\t\troot[i].ofs_skel_weight = root[0].ofs_skel_weight;\n\n\t\troot[i].shares_verts = 0;\n\t\troot[i].numbones = root[0].numbones;\n\t\troot[i].numverts = root[0].numverts;\n\n\t\troot[i].ofsbones = root[0].ofsbones;\n\t}\n\n\tZ_Free(transforms);\n\n\tmod->flags = Mod_ReadFlagsFromMD1(mod->name, 0);\t//file replacement - inherit flags from any defunc mdl files.\n\n\tMod_ClampModelSize(mod);\n\tMod_ParseModelEvents(mod, root->ofsanimations, root->numanimations);\n\n\n\tmod->meshinfo = root;\n\tmod->numframes = root->numanimations;\n\tmod->type = mod_alias;\n\n\tmod->funcs.NativeTrace = Mod_Trace;\n\n\treturn true;\n}\n#endif //ZYMOTICMODELS\n\n\n///////////////////////////////////////////////////////////////\n//psk\n#ifdef PSKMODELS\n/*Typedefs copied from DarkPlaces*/\nextern cvar_t dpcompat_psa_ungroup;\n\ntypedef struct pskchunk_s\n{\n\t// id is one of the following:\n\t// .psk:\n\t// ACTRHEAD (recordsize = 0, numrecords = 0)\n\t// PNTS0000 (recordsize = 12, pskpnts_t)\n\t// VTXW0000 (recordsize = 16, pskvtxw_t)\n\t// FACE0000 (recordsize = 12, pskface_t)\n\t// MATT0000 (recordsize = 88, pskmatt_t)\n\t// REFSKELT (recordsize = 120, pskboneinfo_t)\n\t// RAWWEIGHTS (recordsize = 12, pskrawweights_t)\n\t// .psa:\n\t// ANIMHEAD (recordsize = 0, numrecords = 0)\n\t// BONENAMES (recordsize = 120, pskboneinfo_t)\n\t// ANIMINFO (recordsize = 168, pskaniminfo_t)\n\t// ANIMKEYS (recordsize = 32, pskanimkeys_t)\n\tchar id[20];\n\t// in .psk always 0x1e83b9\n\t// in .psa always 0x2e\n\tint version;\n\tint recordsize;\n\tint numrecords;\n} pskchunk_t;\n\ntypedef struct pskpnts_s\n{\n\tfloat origin[3];\n} pskpnts_t;\n\ntypedef struct pskvtxw_s\n{\n\tunsigned short pntsindex; // index into PNTS0000 chunk\n\tunsigned char unknown1[2]; // seems to be garbage\n\tfloat texcoord[2];\n\tunsigned char mattindex; // index into MATT0000 chunk\n\tunsigned char unknown2; // always 0?\n\tunsigned char unknown3[2]; // seems to be garbage\n} pskvtxw_t;\n\ntypedef struct pskface_s\n{\n\tunsigned short vtxwindex[3]; // triangle\n\tunsigned char mattindex; // index into MATT0000 chunk\n\tunsigned char unknown; // seems to be garbage\n\tunsigned int group; // faces seem to be grouped, possibly for smoothing?\n} pskface_t;\n\ntypedef struct pskmatt_s\n{\n\tchar name[64];\n\tint unknown[6]; // observed 0 0 0 0 5 0\n} pskmatt_t;\n\ntypedef struct pskpose_s\n{\n\tfloat quat[4];\n\tfloat origin[3];\n\tfloat unknown; // probably a float, always seems to be 0\n\tfloat size[3];\n} pskpose_t;\n\ntypedef struct pskboneinfo_s\n{\n\tchar name[64];\n\tint unknown1;\n\tint numchildren;\n\tint parent; // root bones have 0 here\n\tpskpose_t basepose;\n} pskboneinfo_t;\n\ntypedef struct pskrawweights_s\n{\n\tfloat weight;\n\tint pntsindex;\n\tint boneindex;\n} pskrawweights_t;\n\ntypedef struct pskaniminfo_s\n{\n\tchar name[64];\n\tchar group[64];\n\tint numbones;\n\tint unknown1;\n\tint unknown2;\n\tint unknown3;\n\tfloat unknown4;\n\tfloat playtime; // not really needed\n\tfloat fps; // frames per second\n\tint unknown5;\n\tint firstframe;\n\tint numframes;\n\t// firstanimkeys = (firstframe + frameindex) * numbones\n} pskaniminfo_t;\n\ntypedef struct pskanimkeys_s\n{\n\tfloat origin[3];\n\tfloat quat[4];\n\tfloat frametime;\n} pskanimkeys_t;\n\n\nstatic qboolean QDECL Mod_LoadPSKModel(model_t *mod, void *buffer, size_t fsize)\n{\n\tpskchunk_t *chunk;\n\tunsigned int pos = 0;\n\tunsigned int i, j;\n\tqboolean fail = false;\n\tchar basename[MAX_QPATH];\n\tchar psaname[MAX_QPATH];\n\n\tgaliasinfo_t *gmdl;\n#ifndef SERVERONLY\n\tvec2_t *stcoord;\n\tgaliasskin_t *skin;\n\tskinframe_t *sframes;\n#endif\n\tgaliasbone_t *bones;\n\tgaliasanimation_t *group;\n\tfloat *animmatrix, *basematrix;\n\tindex_t *indexes;\n\tint bonemap[MAX_BONES];\n\tchar *e;\n\tsize_t psasize;\n\tvoid *psabuffer;\n\n\tpskpnts_t *pnts = NULL;\n\tpskvtxw_t *vtxw = NULL;\n\tpskface_t *face = NULL;\n\tpskmatt_t *matt = NULL;\n\tpskboneinfo_t *boneinfo = NULL;\n\tpskrawweights_t *rawweights = NULL;\n\tunsigned int num_pnts, num_vtxw=0, num_face=0, num_matt = 0, num_boneinfo=0, num_rawweights=0;\n\n\tpskaniminfo_t *animinfo = NULL;\n\tpskanimkeys_t *animkeys = NULL;\n\tunsigned int num_animinfo=0, num_animkeys=0;\n\n\tvecV_t *skel_xyz;\n\tvec3_t *skel_norm, *skel_svect, *skel_tvect;\n\tbone_vec4_t *skel_idx;\n\tvec4_t *skel_weights;\n\n\t/*load the psk*/\n\twhile (pos < fsize && !fail)\n\t{\n\t\tchunk = (pskchunk_t*)((char*)buffer + pos);\n\t\tchunk->version = LittleLong(chunk->version);\n\t\tchunk->recordsize = LittleLong(chunk->recordsize);\n\t\tchunk->numrecords = LittleLong(chunk->numrecords);\n\n\t\tpos += sizeof(*chunk);\n\n\t\tif (!strcmp(\"ACTRHEAD\", chunk->id) && chunk->recordsize == 0 && chunk->numrecords == 0)\n\t\t{\n\t\t}\n\t\telse if (!strcmp(\"PNTS0000\", chunk->id) && chunk->recordsize == sizeof(pskpnts_t))\n\t\t{\n\t\t\tnum_pnts = chunk->numrecords;\n\t\t\tpnts = (pskpnts_t*)((char*)buffer + pos);\n\t\t\tpos += chunk->recordsize * chunk->numrecords;\n\n\t\t\tfor (i = 0; i < num_pnts; i++)\n\t\t\t{\n\t\t\t\tpnts[i].origin[0] = LittleFloat(pnts[i].origin[0]);\n\t\t\t\tpnts[i].origin[1] = LittleFloat(pnts[i].origin[1]);\n\t\t\t\tpnts[i].origin[2] = LittleFloat(pnts[i].origin[2]);\n\t\t\t}\n\t\t}\n\t\telse if (!strcmp(\"VTXW0000\", chunk->id) && chunk->recordsize == sizeof(pskvtxw_t))\n\t\t{\n\t\t\tnum_vtxw = chunk->numrecords;\n\t\t\tvtxw = (pskvtxw_t*)((char*)buffer + pos);\n\t\t\tpos += chunk->recordsize * chunk->numrecords;\n\n\t\t\tfor (i = 0; i < num_vtxw; i++)\n\t\t\t{\n\t\t\t\tvtxw[i].pntsindex = LittleShort(vtxw[i].pntsindex);\n\t\t\t\tvtxw[i].texcoord[0] = LittleFloat(vtxw[i].texcoord[0]);\n\t\t\t\tvtxw[i].texcoord[1] = LittleFloat(vtxw[i].texcoord[1]);\n\t\t\t}\n\t\t}\n\t\telse if (!strcmp(\"FACE0000\", chunk->id) && chunk->recordsize == sizeof(pskface_t))\n\t\t{\n\t\t\tnum_face = chunk->numrecords;\n\t\t\tface = (pskface_t*)((char*)buffer + pos);\n\t\t\tpos += chunk->recordsize * chunk->numrecords;\n\n\t\t\tfor (i = 0; i < num_face; i++)\n\t\t\t{\n\t\t\t\tface[i].vtxwindex[0] = LittleShort(face[i].vtxwindex[0]);\n\t\t\t\tface[i].vtxwindex[1] = LittleShort(face[i].vtxwindex[1]);\n\t\t\t\tface[i].vtxwindex[2] = LittleShort(face[i].vtxwindex[2]);\n\t\t\t}\n\t\t}\n\t\telse if (!strcmp(\"MATT0000\", chunk->id) && chunk->recordsize == sizeof(pskmatt_t))\n\t\t{\n\t\t\tnum_matt = chunk->numrecords;\n\t\t\tmatt = (pskmatt_t*)((char*)buffer + pos);\n\t\t\tpos += chunk->recordsize * chunk->numrecords;\n\t\t}\n\t\telse if (!strcmp(\"REFSKELT\", chunk->id) && chunk->recordsize == sizeof(pskboneinfo_t))\n\t\t{\n\t\t\tnum_boneinfo = chunk->numrecords;\n\t\t\tboneinfo = (pskboneinfo_t*)((char*)buffer + pos);\n\t\t\tpos += chunk->recordsize * chunk->numrecords;\n\n\t\t\tfor (i = 0; i < num_boneinfo; i++)\n\t\t\t{\n\t\t\t\tboneinfo[i].parent = LittleLong(boneinfo[i].parent);\n\t\t\t\tboneinfo[i].basepose.origin[0] = LittleFloat(boneinfo[i].basepose.origin[0]);\n\t\t\t\tboneinfo[i].basepose.origin[1] = LittleFloat(boneinfo[i].basepose.origin[1]);\n\t\t\t\tboneinfo[i].basepose.origin[2] = LittleFloat(boneinfo[i].basepose.origin[2]);\n\t\t\t\tboneinfo[i].basepose.quat[0] = LittleFloat(boneinfo[i].basepose.quat[0]);\n\t\t\t\tboneinfo[i].basepose.quat[1] = LittleFloat(boneinfo[i].basepose.quat[1]);\n\t\t\t\tboneinfo[i].basepose.quat[2] = LittleFloat(boneinfo[i].basepose.quat[2]);\n\t\t\t\tboneinfo[i].basepose.quat[3] = LittleFloat(boneinfo[i].basepose.quat[3]);\n\t\t\t\tboneinfo[i].basepose.size[0] = LittleFloat(boneinfo[i].basepose.size[0]);\n\t\t\t\tboneinfo[i].basepose.size[1] = LittleFloat(boneinfo[i].basepose.size[1]);\n\t\t\t\tboneinfo[i].basepose.size[2] = LittleFloat(boneinfo[i].basepose.size[2]);\n\n\t\t\t\t/*not sure if this is needed, but mimic DP*/\n\t\t\t\tif (i)\n\t\t\t\t{\n\t\t\t\t\tboneinfo[i].basepose.quat[0] *= -1;\n\t\t\t\t\tboneinfo[i].basepose.quat[2] *= -1;\n\t\t\t\t}\n\t\t\t\tboneinfo[i].basepose.quat[1] *= -1;\n\t\t\t}\n\t\t}\n\t\telse if (!strcmp(\"RAWWEIGHTS\", chunk->id) && chunk->recordsize == sizeof(pskrawweights_t))\n\t\t{\n\t\t\tnum_rawweights = chunk->numrecords;\n\t\t\trawweights = (pskrawweights_t*)((char*)buffer + pos);\n\t\t\tpos += chunk->recordsize * chunk->numrecords;\n\n\t\t\tfor (i = 0; i < num_rawweights; i++)\n\t\t\t{\n\t\t\t\trawweights[i].boneindex = LittleLong(rawweights[i].boneindex);\n\t\t\t\trawweights[i].pntsindex = LittleLong(rawweights[i].pntsindex);\n\t\t\t\trawweights[i].weight = LittleFloat(rawweights[i].weight);\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tCon_Printf(CON_ERROR \"%s has unsupported chunk %s of %i size with version %i.\\n\", mod->name, chunk->id, chunk->recordsize, chunk->version);\n\t\t\tfail = true;\n\t\t}\n\t}\n\n\tif (!num_matt)\n\t\tfail = true;\n\n\tif (!pnts || !vtxw || !face || !matt || !boneinfo || !rawweights)\n\t\tfail = true;\n\n\t/*attempt to load a psa file. don't die if we can't find one*/\n\tCOM_StripExtension(mod->name, psaname, sizeof(psaname));\n\tQ_strncatz(psaname, \".psa\", sizeof(psaname));\n\tbuffer = NULL;//test\n\tpsabuffer = FS_LoadMallocFile(psaname, &psasize);\n\tif (psabuffer)\n\t{\n\t\tpos = 0;\n\t\twhile (pos < psasize && !fail)\n\t\t{\n\t\t\tchunk = (pskchunk_t*)((char*)psabuffer + pos);\n\t\t\tchunk->version = LittleLong(chunk->version);\n\t\t\tchunk->recordsize = LittleLong(chunk->recordsize);\n\t\t\tchunk->numrecords = LittleLong(chunk->numrecords);\n\n\t\t\tpos += sizeof(*chunk);\n\n\t\t\tif (!strcmp(\"ANIMHEAD\", chunk->id) && chunk->recordsize == 0 && chunk->numrecords == 0)\n\t\t\t{\n\t\t\t}\n\t\t\telse if (!strcmp(\"BONENAMES\", chunk->id) && chunk->recordsize == sizeof(pskboneinfo_t))\n\t\t\t{\n\t\t\t\t/*parsed purely to ensure that the bones match the main model*/\n\t\t\t\tpskboneinfo_t *animbones = (pskboneinfo_t*)((char*)psabuffer + pos);\n\t\t\t\tpos += chunk->recordsize * chunk->numrecords;\n\t\t\t\tif (num_boneinfo != chunk->numrecords)\n\t\t\t\t{\n\t\t\t\t\tfail = true;\n\t\t\t\t\tCon_Printf(\"PSK/PSA bone counts do not match\\n\");\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tfor (i = 0; i < num_boneinfo; i++)\n\t\t\t\t\t{\n\t\t\t\t\t\t/*assumption: 1:1 mapping will be common*/\n\t\t\t\t\t\tif (!strcmp(boneinfo[i].name, animbones[i].name))\n\t\t\t\t\t\t\tbonemap[i] = i;\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t/*non 1:1 mapping*/\n\t\t\t\t\t\t\tfor (j = 0; j < chunk->numrecords; j++)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tif (!strcmp(boneinfo[i].name, animbones[j].name))\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tbonemap[i] = j;\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (j == chunk->numrecords)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tfail = true;\n\t\t\t\t\t\t\t\tCon_Printf(\"PSK bone %s does not exist in PSA %s\\n\", boneinfo[i].name, basename);\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (!strcmp(\"ANIMINFO\", chunk->id) && chunk->recordsize == sizeof(pskaniminfo_t))\n\t\t\t{\n\t\t\t\tnum_animinfo = chunk->numrecords;\n\t\t\t\taniminfo = (pskaniminfo_t*)((char*)psabuffer + pos);\n\t\t\t\tpos += chunk->recordsize * chunk->numrecords;\n\n\t\t\t\tfor (i = 0; i < num_animinfo; i++)\n\t\t\t\t{\n\t\t\t\t\taniminfo[i].firstframe = LittleLong(animinfo[i].firstframe);\n\t\t\t\t\taniminfo[i].numframes = LittleLong(animinfo[i].numframes);\n\t\t\t\t\taniminfo[i].numbones = LittleLong(animinfo[i].numbones);\n\t\t\t\t\taniminfo[i].fps = LittleFloat(animinfo[i].fps);\n\t\t\t\t\taniminfo[i].playtime = LittleFloat(animinfo[i].playtime);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (!strcmp(\"ANIMKEYS\", chunk->id) && chunk->recordsize == sizeof(pskanimkeys_t))\n\t\t\t{\n\t\t\t\tnum_animkeys = chunk->numrecords;\n\t\t\t\tanimkeys = (pskanimkeys_t*)((char*)psabuffer + pos);\n\t\t\t\tpos += chunk->recordsize * chunk->numrecords;\n\n\t\t\t\tfor (i = 0; i < num_animkeys; i++)\n\t\t\t\t{\n\t\t\t\t\tanimkeys[i].origin[0] = LittleFloat(animkeys[i].origin[0]);\n\t\t\t\t\tanimkeys[i].origin[1] = LittleFloat(animkeys[i].origin[1]);\n\t\t\t\t\tanimkeys[i].origin[2] = LittleFloat(animkeys[i].origin[2]);\n\t\t\t\t\tanimkeys[i].quat[0] = LittleFloat(animkeys[i].quat[0]);\n\t\t\t\t\tanimkeys[i].quat[1] = LittleFloat(animkeys[i].quat[1]);\n\t\t\t\t\tanimkeys[i].quat[2] = LittleFloat(animkeys[i].quat[2]);\n\t\t\t\t\tanimkeys[i].quat[3] = LittleFloat(animkeys[i].quat[3]);\n\n\t\t\t\t\t/*not sure if this is needed, but mimic DP*/\n\t\t\t\t\tif (i%num_boneinfo)\n\t\t\t\t\t{\n\t\t\t\t\t\tanimkeys[i].quat[0] *= -1;\n\t\t\t\t\t\tanimkeys[i].quat[2] *= -1;\n\t\t\t\t\t}\n\t\t\t\t\tanimkeys[i].quat[1] *= -1;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (!strcmp(\"SCALEKEYS\", chunk->id) && chunk->recordsize == 16)\n\t\t\t{\n\t\t\t\tpos += chunk->recordsize * chunk->numrecords;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tCon_Printf(CON_ERROR \"%s has unsupported chunk %s of %i size with version %i.\\n\", va(\"%s.psa\", basename), chunk->id, chunk->recordsize, chunk->version);\n\t\t\t\tfail = true;\n\t\t\t}\n\t\t}\n\t\tif (fail)\n\t\t{\n\t\t\taniminfo = NULL;\n\t\t\tnum_animinfo = 0;\n\t\t\tanimkeys = NULL;\n\t\t\tnum_animkeys = 0;\n\t\t\tfail = false;\n\t\t}\n\t}\n\n\tif (fail)\n\t{\n\t\tBZ_Free(psabuffer);\n\t\treturn false;\n\t}\n\n\tgmdl = ZG_Malloc(&mod->memgroup, sizeof(*gmdl)*num_matt);\n\n\t/*bones!*/\n\tbones = ZG_Malloc(&mod->memgroup, sizeof(galiasbone_t) * num_boneinfo);\n\tfor (i = 0; i < num_boneinfo; i++)\n\t{\n\t\tQ_strncpyz(bones[i].name, boneinfo[i].name, sizeof(bones[i].name));\n\t\te = bones[i].name + strlen(bones[i].name);\n\t\twhile(e > bones[i].name && e[-1] == ' ')\n\t\t\t*--e = 0;\n\t\tbones[i].parent = boneinfo[i].parent;\n\t\tif (i == 0 && bones[i].parent == 0)\n\t\t\tbones[i].parent = -1;\n\t\telse if (bones[i].parent >= i || bones[i].parent < -1)\n\t\t{\n\t\t\tCon_Printf(\"Invalid bones\\n\");\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tbasematrix = ZG_Malloc(&mod->memgroup, num_boneinfo*sizeof(float)*12);\n\tfor (i = 0; i < num_boneinfo; i++)\n\t{\n\t\tfloat tmp[12];\n\t\tPSKGenMatrix(\n\t\t\tboneinfo[i].basepose.origin[0], boneinfo[i].basepose.origin[1], boneinfo[i].basepose.origin[2],\n\t\t\tboneinfo[i].basepose.quat[0],   boneinfo[i].basepose.quat[1],   boneinfo[i].basepose.quat[2], boneinfo[i].basepose.quat[3],\n\t\t\ttmp);\n\t\tif (bones[i].parent < 0)\n\t\t\tmemcpy(basematrix + i*12, tmp, sizeof(float)*12);\n\t\telse\n\t\t\tR_ConcatTransforms((void*)(basematrix + bones[i].parent*12), (void*)tmp, (void*)(basematrix+i*12));\n\t}\n\n\tfor (i = 0; i < num_boneinfo; i++)\n\t{\n\t\tMatrix3x4_Invert_Simple(basematrix+i*12, bones[i].inverse);\n\t}\n\n\tskel_xyz = ZG_Malloc(&mod->memgroup, sizeof(*skel_xyz) * num_vtxw);\n\tskel_norm = ZG_Malloc(&mod->memgroup, sizeof(*skel_norm) * num_vtxw);\n\tskel_svect = ZG_Malloc(&mod->memgroup, sizeof(*skel_svect) * num_vtxw);\n\tskel_tvect = ZG_Malloc(&mod->memgroup, sizeof(*skel_tvect) * num_vtxw);\n\tskel_idx = ZG_Malloc(&mod->memgroup, sizeof(*skel_idx) * num_vtxw);\n\tskel_weights = ZG_Malloc(&mod->memgroup, sizeof(*skel_weights) * num_vtxw);\n\tfor (j = 0; j < 4; j++)\n\t\tskel_idx[i][j] = ~0;\n\tfor (i = 0; i < num_vtxw; i++)\n\t{\n\t\tfloat t;\n\t\tfor (j = 0; j < num_rawweights; j++)\n\t\t{\n\t\t\tif (rawweights[j].pntsindex == vtxw[i].pntsindex)\n\t\t\t{\n\t\t\t\tint in, lin = -1;\n\t\t\t\tfloat liv = rawweights[j].weight;\n\t\t\t\tfor (in = 0; in < 4; in++)\n\t\t\t\t{\n\t\t\t\t\tif (liv > skel_weights[i][in])\n\t\t\t\t\t{\n\t\t\t\t\t\tliv = skel_weights[i][in];\n\t\t\t\t\t\tlin = in;\n\t\t\t\t\t\tif (!liv)\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (lin >= 0)\n\t\t\t\t{\n\t\t\t\t\tskel_idx[i][lin] = rawweights[j].boneindex;\n\t\t\t\t\tskel_weights[i][lin] = rawweights[j].weight;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tt = 0;\n\t\tfor (j = 0; j < 4; j++)\n\t\t\tt += skel_weights[i][j];\n\t\tif (t != 1)\n\t\t\tfor (j = 0; j < 4; j++)\n\t\t\t\tskel_weights[i][j] *= 1/t;\n\n\t\tskel_xyz[i][0] = pnts[vtxw[i].pntsindex].origin[0];\n\t\tskel_xyz[i][1] = pnts[vtxw[i].pntsindex].origin[1];\n\t\tskel_xyz[i][2] = pnts[vtxw[i].pntsindex].origin[2];\n\t}\n\n#ifndef SERVERONLY\n\t/*st coords, all share the same list*/\n\tstcoord = ZG_Malloc(&mod->memgroup, sizeof(vec2_t)*num_vtxw);\n\tfor (i = 0; i < num_vtxw; i++)\n\t{\n\t\tstcoord[i][0] = vtxw[i].texcoord[0];\n\t\tstcoord[i][1] = vtxw[i].texcoord[1];\n\t}\n#endif\n\n\t/*allocate faces in a single block, as we at least know an upper bound*/\n\tindexes = ZG_Malloc(&mod->memgroup, sizeof(index_t)*num_face*3);\n\n\tif (animinfo && animkeys)\n\t{\n\t\tint numgroups = 0;\n\t\tframeinfo_t *frameinfo = ParseFrameInfo(mod, &numgroups);\n\t\tif (numgroups)\n\t\t{\n\t\t\t/*externally supplied listing of frames. ignore all framegroups in the model and use only the pose info*/\n\t\t\tgroup = ZG_Malloc(&mod->memgroup, sizeof(galiasanimation_t)*numgroups + num_animkeys*sizeof(float)*12);\n\t\t\tanimmatrix = (float*)(group+numgroups);\n\t\t\tfor (j = 0; j < numgroups; j++)\n\t\t\t{\n\t\t\t\t/*bound check*/\n\t\t\t\tif (frameinfo[j].posesarray)\n\t\t\t\t\tCon_Printf(CON_WARNING\"Mod_LoadPSKModel(%s): framegroup[%i] poses array not suppported\\n\", mod->name, j);\n\t\t\t\tif (frameinfo[j].firstpose+frameinfo[j].posecount > num_animkeys)\n\t\t\t\t\tframeinfo[j].posecount = num_animkeys - frameinfo[j].firstpose;\n\t\t\t\tif (frameinfo[j].firstpose >= num_animkeys)\n\t\t\t\t{\n\t\t\t\t\tframeinfo[j].firstpose = 0;\n\t\t\t\t\tframeinfo[j].posecount = 1;\n\t\t\t\t}\n\n\t\t\t\tgroup[j].boneofs = animmatrix + 12*num_boneinfo*frameinfo[j].firstpose;\n\t\t\t\tgroup[j].numposes = frameinfo[j].posecount;\n\t\t\t\tif (*frameinfo[j].name)\n\t\t\t\t\tQ_snprintfz(group[j].name, sizeof(group[j].name), \"%s\", frameinfo[j].name);\n\t\t\t\telse\n\t\t\t\t\tQ_snprintfz(group[j].name, sizeof(group[j].name), \"frame_%i\", j);\n\t\t\t\tgroup[j].loop = frameinfo[j].loop;\n\t\t\t\tgroup[j].rate = frameinfo[j].fps;\n\t\t\t\tgroup[j].skeltype = SKEL_RELATIVE;\n\t\t\t\tgroup[j].events = frameinfo[j].events;\n\t\t\t\tgroup[j].action = frameinfo[j].action;\n\t\t\t\tgroup[j].actionweight = frameinfo[j].actionweight;\n\t\t\t}\n\t\t\tnum_animinfo = numgroups;\n\t\t}\n#ifndef HAVE_LEGACY\n\t\telse if (dpcompat_psa_ungroup.ival)\n\t\t{\n\t\t\t/*unpack each frame of each animation to be a separate framegroup*/\n\t\t\tunsigned int iframe;\t/*individual frame count*/\n\t\t\tiframe = 0;\n\t\t\tfor (i = 0; i < num_animinfo; i++)\n\t\t\t\tiframe += animinfo[i].numframes;\n\t\t\tgroup = ZG_Malloc(&mod->memgroup, sizeof(galiasanimation_t)*iframe + num_animkeys*sizeof(float)*12);\n\t\t\tanimmatrix = (float*)(group+iframe);\n\t\t\tiframe = 0;\n\t\t\tfor (j = 0; j < num_animinfo; j++)\n\t\t\t{\n\t\t\t\tfor (i = 0; i < animinfo[j].numframes; i++)\n\t\t\t\t{\n\t\t\t\t\tgroup[iframe].boneofs = animmatrix + 12*num_boneinfo*(animinfo[j].firstframe+i);\n\t\t\t\t\tgroup[iframe].numposes = 1;\n\t\t\t\t\tQ_snprintfz(group[iframe].name, sizeof(group[iframe].name), \"%s_%i\", animinfo[j].name, i);\n\t\t\t\t\tgroup[iframe].loop = true;\n\t\t\t\t\tgroup[iframe].rate = animinfo[j].fps;\n\t\t\t\t\tgroup[iframe].skeltype = SKEL_RELATIVE;\n\t\t\t\t\tgroup[iframe].action = -1;\n\t\t\t\t\tgroup[iframe].actionweight = 0;\n\t\t\t\t\tiframe++;\n\t\t\t\t}\n\t\t\t}\n\t\t\tnum_animinfo = iframe;\n\t\t}\n#endif\n\t\telse\n\t\t{\n\t\t\t/*keep each framegroup as a group*/\n\t\t\tgroup = ZG_Malloc(&mod->memgroup, sizeof(galiasanimation_t)*num_animinfo + num_animkeys*sizeof(float)*12);\n\t\t\tanimmatrix = (float*)(group+num_animinfo);\n\t\t\tfor (i = 0; i < num_animinfo; i++)\n\t\t\t{\n\t\t\t\tgroup[i].boneofs = animmatrix + 12*num_boneinfo*animinfo[i].firstframe;\n\t\t\t\tgroup[i].numposes = animinfo[i].numframes;\n\t\t\t\tQ_strncpyz(group[i].name, animinfo[i].name, sizeof(group[i].name));\n\t\t\t\tgroup[i].loop = true;\n\t\t\t\tgroup[i].rate = animinfo[i].fps;\n\t\t\t\tgroup[i].skeltype = SKEL_RELATIVE;\n\t\t\t\tgroup[i].action = -1;\n\t\t\t\tgroup[i].actionweight = 0;\n\t\t\t}\n\t\t}\n\t\tfor (j = 0; j < num_animkeys; j += num_boneinfo)\n\t\t{\n\t\t\tpskanimkeys_t *sb;\n\t\t\tfor (i = 0; i < num_boneinfo; i++)\n\t\t\t{\n\t\t\t\tsb = &animkeys[j + bonemap[i]];\n\t\t\t\tPSKGenMatrix(\n\t\t\t\t\tsb->origin[0], sb->origin[1], sb->origin[2],\n\t\t\t\t\tsb->quat[0],   sb->quat[1],   sb->quat[2],   sb->quat[3],\n\t\t\t\t\tanimmatrix + (j+i)*12);\n\t\t\t}\n\t\t}\n\t\tfree(frameinfo);\n\t}\n\telse\n\t{\n\t\tnum_animinfo = 1;\n\t\t/*build a base pose*/\n\t\tgroup = ZG_Malloc(&mod->memgroup, sizeof(galiasanimation_t) + num_boneinfo*sizeof(float)*12);\n\t\tanimmatrix = basematrix;\n\t\tgroup->boneofs = animmatrix;\n\t\tgroup->numposes = 1;\n\t\tstrcpy(group->name, \"base\");\n\t\tgroup->loop = true;\n\t\tgroup->rate = 10;\n\t\tgroup->skeltype = SKEL_ABSOLUTE;\n\t\tgroup->action = -1;\n\t\tgroup->actionweight = 0;\n\t}\n\n\n#ifndef SERVERONLY\n\t//client builds need skin info\n\tskin = ZG_Malloc(&mod->memgroup, num_matt * (sizeof(galiasskin_t) + sizeof(skinframe_t)));\n\tsframes = (skinframe_t*)(skin + num_matt);\n\tfor (i = 0; i < num_matt; i++, skin++)\n\t{\n\t\tskin->frame = &sframes[i];\n\t\tskin->numframes = 1;\n\t\tskin->skinspeed = 10;\n\t\tQ_strncpyz(skin->name, matt[i].name, sizeof(skin->name));\n\t\tQ_strncpyz(sframes[i].shadername, matt[i].name, sizeof(sframes[i].shadername));\n\t\tsframes[i].shader = NULL;\n\n\t\tgmdl[i].ofsskins = skin;\n\t\tgmdl[i].numskins = 1;\n\n\t\tgmdl[i].ofs_st_array = stcoord;\n\t\tgmdl[i].numverts = num_vtxw;\n#else\n\t//server-only builds\n\tfor (i = 0; i < num_matt; i++)\n\t{\n#endif\n\t\t//common to all builds\n\t\tMod_DefaultMesh(&gmdl[i], mod->name, i);\n\n\t\tgmdl[i].ofsanimations = group;\n\t\tgmdl[i].numanimations = num_animinfo;\n\n\t\tgmdl[i].numindexes = 0;\n\t\tfor (j = 0; j < num_face; j++)\n\t\t{\n\t\t\tif (face[j].mattindex%num_matt == i)\n\t\t\t{\n\t\t\t\tindexes[gmdl[i].numindexes+0] = face[j].vtxwindex[0];\n\t\t\t\tindexes[gmdl[i].numindexes+1] = face[j].vtxwindex[1];\n\t\t\t\tindexes[gmdl[i].numindexes+2] = face[j].vtxwindex[2];\n\t\t\t\tgmdl[i].numindexes += 3;\n\t\t\t}\n\t\t}\n\t\tgmdl[i].ofs_indexes = indexes;\n\t\tindexes += gmdl[i].numindexes;\n\n\t\t//all surfaces share bones\n\t\tgmdl[i].baseframeofs = basematrix;\n\t\tgmdl[i].ofsbones = bones;\n\t\tgmdl[i].numbones = num_boneinfo;\n\t\tgmdl[i].shares_bones = 0;\n\n\t\t//all surfaces share verts, but not indexes.\n\t\tgmdl[i].ofs_skel_idx = skel_idx;\n\t\tgmdl[i].ofs_skel_weight = skel_weights;\n\t\tgmdl[i].ofs_skel_xyz = skel_xyz;\n\t\tgmdl[i].ofs_skel_norm = skel_norm;\n\t\tgmdl[i].ofs_skel_svect = skel_svect;\n\t\tgmdl[i].ofs_skel_tvect = skel_tvect;\n\t\tgmdl[i].shares_verts = 0;\n\n\t\tgmdl[i].nextsurf = (i != num_matt-1)?&gmdl[i+1]:NULL;\n\t}\n\n#ifndef SERVERONLY\n\tMod_AccumulateTextureVectors(skel_xyz, stcoord, skel_norm, skel_svect, skel_tvect, gmdl[0].ofs_indexes, indexes-gmdl[0].ofs_indexes, true);\n\tMod_NormaliseTextureVectors(skel_norm, skel_svect, skel_tvect, num_vtxw, true);\n#endif\n\n\tBZ_Free(psabuffer);\n\tif (fail)\n\t{\n\t\treturn false;\n\t}\n\n\n//\tmod->radius = Alias_CalculateSkeletalNormals(gmdl);\n\n\tmod->mins[0] = mod->mins[1] = mod->mins[2] = -mod->radius;\n\tmod->maxs[0] = mod->maxs[1] = mod->maxs[2] = mod->radius;\n\n\tmod->flags = Mod_ReadFlagsFromMD1(mod->name, 0);\t//file replacement - inherit flags from any defunc mdl files.\n\n\tMod_ClampModelSize(mod);\n\tMod_ParseModelEvents(mod, gmdl->ofsanimations, gmdl->numanimations);\n\n\n\tmod->meshinfo = gmdl;\n\tmod->numframes = gmdl->numanimations;\n\tmod->type = mod_alias;\n\tmod->funcs.NativeTrace = Mod_Trace;\n\treturn true;\n}\n\n#endif\n\n\n\n\n\n//////////////////////////////////////////////////////////////\n//dpm\n#ifdef DPMMODELS\n\n// header for the entire file\ntypedef struct dpmheader_s\n{\n\tchar id[16]; // \"DARKPLACESMODEL\\0\", length 16\n\tunsigned int type; // 2 (hierarchical skeletal pose)\n\tunsigned int filesize; // size of entire model file\n\tfloat mins[3], maxs[3], yawradius, allradius; // for clipping uses\n\n\t// these offsets are relative to the file\n\tunsigned int num_bones;\n\tunsigned int num_meshs;\n\tunsigned int num_frames;\n\tunsigned int ofs_bones; // dpmbone_t bone[num_bones];\n\tunsigned int ofs_meshs; // dpmmesh_t mesh[num_meshs];\n\tunsigned int ofs_frames; // dpmframe_t frame[num_frames];\n} dpmheader_t;\n\n// there may be more than one of these\ntypedef struct dpmmesh_s\n{\n\t// these offsets are relative to the file\n\tchar shadername[32]; // name of the shader to use\n\tunsigned int num_verts;\n\tunsigned int num_tris;\n\tunsigned int ofs_verts; // dpmvertex_t vert[numvertices]; // see vertex struct\n\tunsigned int ofs_texcoords; // float texcoords[numvertices][2];\n\tunsigned int ofs_indices; // unsigned int indices[numtris*3]; // designed for glDrawElements (each triangle is 3 unsigned int indices)\n\tunsigned int ofs_groupids; // unsigned int groupids[numtris]; // the meaning of these values is entirely up to the gamecode and modeler\n} dpmmesh_t;\n\n// if set on a bone, it must be protected from removal\n#define DPMBONEFLAG_ATTACHMENT 1\n\n// one per bone\ntypedef struct dpmbone_s\n{\n\t// name examples: upperleftarm leftfinger1 leftfinger2 hand, etc\n\tchar name[32];\n\t// parent bone number\n\tsigned int parent;\n\t// flags for the bone\n\tunsigned int flags;\n} dpmbone_t;\n\n// a bonepose matrix is intended to be used like this:\n// (n = output vertex, v = input vertex, m = matrix, f = influence)\n// n[0] = v[0] * m[0][0] + v[1] * m[0][1] + v[2] * m[0][2] + f * m[0][3];\n// n[1] = v[0] * m[1][0] + v[1] * m[1][1] + v[2] * m[1][2] + f * m[1][3];\n// n[2] = v[0] * m[2][0] + v[1] * m[2][1] + v[2] * m[2][2] + f * m[2][3];\ntypedef struct dpmbonepose_s\n{\n\tfloat matrix[3][4];\n} dpmbonepose_t;\n\n// immediately followed by bone positions for the frame\ntypedef struct dpmframe_s\n{\n\t// name examples: idle_1 idle_2 idle_3 shoot_1 shoot_2 shoot_3, etc\n\tchar name[32];\n\tfloat mins[3], maxs[3], yawradius, allradius;\n\tint ofs_bonepositions; // dpmbonepose_t bonepositions[bones];\n} dpmframe_t;\n\n// one or more of these per vertex\ntypedef struct dpmbonevert_s\n{\n\tfloat origin[3]; // vertex location (these blend)\n\tfloat influence; // influence fraction (these must add up to 1)\n\tfloat normal[3]; // surface normal (these blend)\n\tunsigned int bonenum; // number of the bone\n} dpmbonevert_t;\n\n// variable size, parsed sequentially\ntypedef struct dpmvertex_s\n{\n\tunsigned int numbones;\n\t// immediately followed by 1 or more dpmbonevert_t structures\n} dpmvertex_t;\n\nstatic qboolean QDECL Mod_LoadDarkPlacesModel(model_t *mod, void *buffer, size_t fsize)\n{\n#ifndef SERVERONLY\n\tgaliasskin_t *skin;\n\tskinframe_t *skinframe;\n\tint skinfiles;\n\tfloat *inst;\n\tfloat *outst;\n#endif\n\n\tint i, j, k;\n\n\tdpmheader_t *header;\n\tgaliasinfo_t *root, *m;\n\tdpmmesh_t *mesh;\n\tdpmvertex_t *vert;\n\tdpmbonevert_t *bonevert;\n\n\tgalisskeletaltransforms_t *transforms, *firsttransform;\n\n\tgaliasbone_t *outbone;\n\tdpmbone_t *inbone;\n\n\tfloat *outposedata;\n\tgaliasanimation_t *outgroups;\n\tfloat *inposedata;\n\tdpmframe_t *inframes;\n\n\tunsigned int *index;\tindex_t *outdex;\t// groan...\n\n\tint numtransforms;\n\n\tint numgroups;\n\tframeinfo_t *framegroups;\n\n\theader = buffer;\n\n\tif (memcmp(header->id, \"DARKPLACESMODEL\\0\", 16))\n\t{\n\t\tCon_Printf(CON_ERROR \"Mod_LoadDarkPlacesModel: %s, doesn't appear to be a darkplaces model!\\n\", mod->name);\n\t\treturn false;\n\t}\n\n\tif (BigLong(header->type) != 2)\n\t{\n\t\tCon_Printf(CON_ERROR \"Mod_LoadDarkPlacesModel: %s, only type 2 is supported\\n\", mod->name);\n\t\treturn false;\n\t}\n\n\tfor (i = 0; i < sizeof(dpmheader_t)/4; i++)\n\t\t((int*)header)[i] = BigLong(((int*)header)[i]);\n\n\tif (!header->num_bones)\n\t{\n\t\tCon_Printf(CON_ERROR \"Mod_LoadDarkPlacesModel: %s, no bones\\n\", mod->name);\n\t\treturn false;\n\t}\n\tif (!header->num_frames)\n\t{\n\t\tCon_Printf(CON_ERROR \"Mod_LoadDarkPlacesModel: %s, no frames\\n\", mod->name);\n\t\treturn false;\n\t}\n\tif (!header->num_meshs)\n\t{\n\t\tCon_Printf(CON_ERROR \"Mod_LoadDarkPlacesModel: %s, no surfaces\\n\", mod->name);\n\t\treturn false;\n\t}\n\n\n\tVectorCopy(header->mins, mod->mins);\n\tVectorCopy(header->maxs, mod->maxs);\n\n\troot = ZG_Malloc(&mod->memgroup, sizeof(galiasinfo_t)*header->num_meshs);\n\n\toutbone = ZG_Malloc(&mod->memgroup, sizeof(galiasbone_t)*header->num_bones);\n\tinbone = (dpmbone_t*)((char*)buffer + header->ofs_bones);\n\tfor (i = 0; i < header->num_bones; i++)\n\t{\n\t\toutbone[i].parent = BigLong(inbone[i].parent);\n\t\tif (outbone[i].parent >= i || outbone[i].parent < -1)\n\t\t{\n\t\t\tCon_Printf(CON_ERROR \"Mod_LoadDarkPlacesModel: bad bone index in %s\\n\", mod->name);\n\t\t\treturn false;\n\t\t}\n\n\t\tQ_strncpyz(outbone[i].name, inbone[i].name, sizeof(outbone[i].name));\n\t\t//throw away the flags.\n\t}\n\n\tframegroups = ParseFrameInfo(mod, &numgroups);\n\tif (!framegroups)\n\t{\t//use the dpm's poses directly.\n\t\tnumgroups = header->num_frames;\n\t\toutgroups = ZG_Malloc(&mod->memgroup, sizeof(galiasanimation_t)*numgroups + sizeof(float)*header->num_frames*header->num_bones*12);\n\t\toutposedata = (float*)(outgroups+numgroups);\n\n\t\tinframes = (dpmframe_t*)((char*)buffer + header->ofs_frames);\n\t\tfor (i = 0; i < numgroups; i++)\n\t\t{\n\t\t\tinframes[i].ofs_bonepositions = BigLong(inframes[i].ofs_bonepositions);\n\t\t\tinframes[i].allradius = BigLong(inframes[i].allradius);\n\t\t\tinframes[i].yawradius = BigLong(inframes[i].yawradius);\n\t\t\tinframes[i].mins[0] = BigLong(inframes[i].mins[0]);\n\t\t\tinframes[i].mins[1] = BigLong(inframes[i].mins[1]);\n\t\t\tinframes[i].mins[2] = BigLong(inframes[i].mins[2]);\n\t\t\tinframes[i].maxs[0] = BigLong(inframes[i].maxs[0]);\n\t\t\tinframes[i].maxs[1] = BigLong(inframes[i].maxs[1]);\n\t\t\tinframes[i].maxs[2] = BigLong(inframes[i].maxs[2]);\n\n\t\t\tQ_strncpyz(outgroups[i].name, inframes[i].name, sizeof(outgroups[i].name));\n\n\t\t\toutgroups[i].rate = 10;\n\t\t\toutgroups[i].numposes = 1;\n\t\t\toutgroups[i].skeltype = SKEL_RELATIVE;\n\t\t\toutgroups[i].boneofs = outposedata;\n\t\t\toutgroups[i].action = -1;\n\t\t\toutgroups[i].actionweight = 0;\n\n\t\t\tinposedata = (float*)((char*)buffer + inframes[i].ofs_bonepositions);\n\t\t\tfor (j = 0; j < header->num_bones*12; j++)\n\t\t\t\t*outposedata++ = BigFloat(*inposedata++);\n\t\t}\n\t}\n\telse\n\t{\t//we read a .framegroups file to remap everything.\n\t\toutgroups = ZG_Malloc(&mod->memgroup, sizeof(galiasanimation_t)*numgroups + sizeof(float)*header->num_frames*header->num_bones*12);\n\t\toutposedata = (float*)(outgroups+numgroups);\n\n\t\tinframes = (dpmframe_t*)((char*)buffer + header->ofs_frames);\n\t\tfor (i = 0; i < header->num_frames; i++)\n\t\t{\n\t\t\tinposedata = (float*)((char*)buffer + BigLong(inframes[i].ofs_bonepositions));\n\t\t\tfor (j = 0; j < header->num_bones*12; j++)\n\t\t\t\t*outposedata++ = BigFloat(*inposedata++);\n\t\t}\n\t\toutposedata -= header->num_frames*header->num_bones*12;\n\n\t\tfor (i = 0; i < numgroups; i++)\n\t\t{\n\t\t\tint firstpose = framegroups[i].firstpose, numposes = framegroups[i].posecount;\n\t\t\tif (firstpose >= header->num_frames)\n\t\t\t\tfirstpose = header->num_frames-1;\n\t\t\tif (firstpose < 0)\n\t\t\t\tfirstpose = 0;\n\t\t\tif (numposes < 0)\n\t\t\t\tnumposes = 0;\n\t\t\tif (firstpose + numposes > header->num_frames)\n\t\t\t\tnumposes = header->num_frames - firstpose;\n\t\t\tif (framegroups[i].posesarray)\n\t\t\t\tCon_Printf(CON_WARNING\"Mod_LoadDarkPlacesModel(%s): No support for explicit pose lists\\n\", mod->name);\n\t\t\toutgroups[i].skeltype = SKEL_RELATIVE;\n\t\t\toutgroups[i].boneofs = outposedata + firstpose*header->num_bones*12;\n\t\t\toutgroups[i].numposes = numposes;\n\t\t\toutgroups[i].loop = framegroups[i].loop;\n\t\t\toutgroups[i].rate = framegroups[i].fps;\n\t\t\toutgroups[i].events = framegroups[i].events;\n\t\t\toutgroups[i].action = framegroups[i].action;\n\t\t\toutgroups[i].actionweight = framegroups[i].actionweight;\n\t\t\tQ_strncpyz(outgroups[i].name, framegroups[i].name, sizeof(outgroups[i].name));\n\t\t}\n\t}\n\n\n#ifndef SERVERONLY\n\tskinfiles = Mod_CountSkinFiles(mod);\n\tif (skinfiles < 1)\n\t\tskinfiles = 1;\n#endif\n\n\tmesh = (dpmmesh_t*)((char*)buffer + header->ofs_meshs);\n\tfor (i = 0; i < header->num_meshs; i++, mesh++)\n\t{\n\t\t\tm = &root[i];\n\t\tMod_DefaultMesh(m, mesh->shadername, i);\n\t\tif (i < header->num_meshs-1)\n\t\t\tm->nextsurf = &root[i+1];\n\t\tm->shares_bones = 0;\n\n\t\tm->ofsbones = outbone;\n\t\tm->numbones = header->num_bones;\n\n\t\tm->numanimations = numgroups;\n\t\tm->ofsanimations = outgroups;\n\n#ifdef SERVERONLY\n\t\tm->numskins = 1;\n#else\n\t\tm->numskins = skinfiles;\n\n\t\tskin = ZG_Malloc(&mod->memgroup, (sizeof(galiasskin_t)+sizeof(skinframe_t))*skinfiles);\n\t\tskinframe = (skinframe_t*)(skin+skinfiles);\n\t\tfor (j = 0; j < skinfiles; j++, skinframe++)\n\t\t{\n\t\t\tskin[j].numframes = 1;\t//non-sequenced skins.\n\t\t\tskin[j].frame = skinframe;\n\t\t\tskin[j].skinwidth = 1;\n\t\t\tskin[j].skinheight = 1;\n\t\t\tskin[j].skinspeed = 10; /*something to avoid div by 0*/\n\n\t\t\tif (!j)\n\t\t\t{\n\t\t\t\tQ_strncpyz(skin[j].name, mesh->shadername, sizeof(skin[j].name));\n\t\t\t\tQ_strncpyz(skinframe->shadername, mesh->shadername, sizeof(skin[j].name));\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tQ_strncpyz(skin[j].name, \"\", sizeof(skin[j].name));\n\t\t\t\tQ_strncpyz(skinframe->shadername, \"\", sizeof(skin[j].name));\n\t\t\t}\n\n\t\t}\n\n\t\tm->ofsskins = skin;\n#endif\n\n\n\t\tmesh->num_verts = BigLong(mesh->num_verts);\n\t\tmesh->num_tris = BigLong(mesh->num_tris);\n\t\tmesh->ofs_verts = BigLong(mesh->ofs_verts);\n\t\tmesh->ofs_texcoords = BigLong(mesh->ofs_texcoords);\n\t\tmesh->ofs_indices = BigLong(mesh->ofs_indices);\n\t\tmesh->ofs_groupids = BigLong(mesh->ofs_groupids);\n\n\t\tindex = (unsigned int*)((char*)buffer + mesh->ofs_indices);\n\t\toutdex = ZG_Malloc(&mod->memgroup, mesh->num_tris*3*sizeof(index_t));\n\t\tm->ofs_indexes = outdex;\n\t\tm->numindexes = mesh->num_tris*3;\n\t\tfor (j = 0; j < mesh->num_tris; j++, index += 3, outdex += 3)\n\t\t{\n\t\t\toutdex[0] = BigLong(index[2]);\n\t\t\toutdex[1] = BigLong(index[1]);\n\t\t\toutdex[2] = BigLong(index[0]);\n\t\t}\n\n\n\t\tnumtransforms = 0;\n\t\t//count and byteswap the transformations\n\t\tvert = (dpmvertex_t*)((char *)buffer+mesh->ofs_verts);\n\t\tfor (j = 0; j < mesh->num_verts; j++)\n\t\t{\n\t\t\tvert->numbones = BigLong(vert->numbones);\n\t\t\tnumtransforms += vert->numbones;\n\t\t\tbonevert = (dpmbonevert_t*)(vert+1);\n\t\t\tvert = (dpmvertex_t*)(bonevert+vert->numbones);\n\t\t}\n\n\t\tm = &root[i];\n\t\tm->shares_verts = i;\n\t\tm->shares_bones = 0;\n#ifndef SERVERONLY\n\t\toutst = ZG_Malloc(&mod->memgroup, mesh->num_verts*sizeof(vec2_t));\n\t\tm->ofs_st_array = (vec2_t*)outst;\n\t\tm->numverts = mesh->num_verts;\n\t\tinst = (float*)((char*)buffer + mesh->ofs_texcoords);\n\t\tfor (j = 0; j < mesh->num_verts; j++, outst+=2, inst+=2)\n\t\t{\n\t\t\toutst[0] = BigFloat(inst[0]);\n\t\t\toutst[1] = BigFloat(inst[1]);\n\t\t}\n#endif\n\n\n\t\tfirsttransform = transforms = Z_Malloc(numtransforms*sizeof(galisskeletaltransforms_t));\n\t\t//build the transform list.\n\t\tvert = (dpmvertex_t*)((char *)buffer+mesh->ofs_verts);\n\t\tfor (j = 0; j < mesh->num_verts; j++)\n\t\t{\n\t\t\tbonevert = (dpmbonevert_t*)(vert+1);\n\t\t\tfor (k = 0; k < vert->numbones; k++, bonevert++, transforms++)\n\t\t\t{\n\t\t\t\ttransforms->boneindex = BigLong(bonevert->bonenum);\n\t\t\t\ttransforms->vertexindex = j;\n\t\t\t\ttransforms->org[0] = BigFloat(bonevert->origin[0]);\n\t\t\t\ttransforms->org[1] = BigFloat(bonevert->origin[1]);\n\t\t\t\ttransforms->org[2] = BigFloat(bonevert->origin[2]);\n\t\t\t\ttransforms->org[3] = BigFloat(bonevert->influence);\n#ifndef SERVERONLY\n\t\t\t\ttransforms->normal[0] = BigFloat(bonevert->normal[0]);\n\t\t\t\ttransforms->normal[1] = BigFloat(bonevert->normal[1]);\n\t\t\t\ttransforms->normal[2] = BigFloat(bonevert->normal[2]);\n#endif\n\t\t\t}\n\t\t\tvert = (dpmvertex_t*)bonevert;\n\t\t}\n\t\tAlias_BuildGPUWeights(mod, m, numtransforms, firsttransform, false);\n\t\tZ_Free(firsttransform);\n\t}\n\n\tmod->flags = Mod_ReadFlagsFromMD1(mod->name, 0);\t//file replacement - inherit flags from any defunc mdl files.\n\n\tMod_ClampModelSize(mod);\n\tMod_ParseModelEvents(mod, root->ofsanimations, root->numanimations);\n\n\tmod->meshinfo = root;\n\tmod->numframes = root->numanimations;\n\tmod->type = mod_alias;\n\tmod->funcs.NativeTrace = Mod_Trace;\n\n\tfree(framegroups);\n\n\treturn true;\n}\n#endif\t//DPMMODELS\n\n\n#ifdef INTERQUAKEMODELS\n#define IQM_MAGIC \"INTERQUAKEMODEL\"\n#define IQM_VERSION1 1\n#define IQM_VERSION2 2\n\nstruct iqmheader\n{\n\tchar magic[16];\n\tunsigned int version;\n\tunsigned int filesize;\n\tunsigned int flags;\n\tunsigned int num_text, ofs_text;\n\tunsigned int num_meshes, ofs_meshes;\n\tunsigned int num_vertexarrays, num_vertexes, ofs_vertexarrays;\n\tunsigned int num_triangles, ofs_triangles, ofs_adjacency;\n\tunsigned int num_joints, ofs_joints;\n\tunsigned int num_poses, ofs_poses;\n\tunsigned int num_anims, ofs_anims;\n\tunsigned int num_frames, num_framechannels, ofs_frames, ofs_bounds;\n\tunsigned int num_comment, ofs_comment;\n\tunsigned int num_extensions, ofs_extensions;\n};\n\nstruct iqmmesh\n{\n\tunsigned int name;\n\tunsigned int material;\n\tunsigned int first_vertex, num_vertexes;\n\tunsigned int first_triangle, num_triangles;\n};\n\nenum\n{\n\tIQM_POSITION     = 0,\n\tIQM_TEXCOORD     = 1,\n\tIQM_NORMAL       = 2,\n\tIQM_TANGENT      = 3,\n\tIQM_BLENDINDEXES = 4,\n\tIQM_BLENDWEIGHTS = 5,\n\tIQM_COLOR        = 6,\n\tIQM_CUSTOM       = 0x10\n};\n\nenum\n{\n\tIQM_BYTE   = 0,\n\tIQM_UBYTE  = 1,\n\tIQM_SHORT  = 2,\n\tIQM_USHORT = 3,\n\tIQM_INT    = 4,\n\tIQM_UINT   = 5,\n\tIQM_HALF   = 6,\n\tIQM_FLOAT  = 7,\n\tIQM_DOUBLE = 8,\n};\n\nstruct iqmtriangle\n{\n\tunsigned int vertex[3];\n};\n\nstruct iqmjoint1\n{\n\tunsigned int name;\n\tint parent;\n\tfloat translate[3], rotate[3], scale[3];\n};\nstruct iqmjoint2\n{\n\tunsigned int name;\n\tint parent;\n\tfloat translate[3], rotate[4], scale[3];\n};\n\nstruct iqmpose1\n{\n\tint parent;\n\tunsigned int mask;\n\tfloat channeloffset[9];\n\tfloat channelscale[9];\n};\nstruct iqmpose2\n{\n\tint parent;\n\tunsigned int mask;\n\tfloat channeloffset[10];\n\tfloat channelscale[10];\n};\n\nstruct iqmanim\n{\n\tunsigned int name;\n\tunsigned int first_frame, num_frames;\n\tfloat framerate;\n\tunsigned int flags;\n};\n\nenum\n{\n\tIQM_LOOP = 1<<0\n};\n\nstruct iqmvertexarray\n{\n\tunsigned int type;\n\tunsigned int flags;\n\tunsigned int format;\n\tunsigned int size;\n\tunsigned int offset;\n};\n\nstruct iqmbounds\n{\n\tfloat bbmin[3], bbmax[3];\n\tfloat xyradius, radius;\n};\n\nstruct iqmextension\n{\n\tunsigned int name;\n\tunsigned int num_data, ofs_data;\n\tunsigned int ofs_extensions; // pointer to next extension. wtf is up with this? how is this not redundant due to ofs_data?\n};\n\n\nstruct iqmext_fte_mesh\n{\n\tunsigned int contents;\t\t//default CONTENTS_BODY\n\tunsigned int surfaceflags;\t//propagates to trace_surfaceflags\n\tunsigned int surfaceid;\t\t//the body reported to qc via trace_surface\n\tunsigned int geomset;\n\tunsigned int geomid;\n\tfloat\tmindist;\n\tfloat\tmaxdist;\n};\nstruct iqmext_fte_event\n{\n\tunsigned int anim;\n\tfloat timestamp;\n\tunsigned int evcode;\n\tunsigned int evdata_str;\t//stringtable\n};\n//skin lump is made of 3 parts\nstruct iqmext_fte_skin\n{\n\tunsigned int numskinframes;\n\tunsigned int nummeshskins;\n\t//unsigned int numskins[nummeshes];\n\t//iqmext_fte_skin_skinframe[numskinframes];\n\t//iqmext_fte_skin_meshskin mesh0[numskins[0]];\n\t//iqmext_fte_skin_meshskin mesh1[numskins[1]]; etc\n};\nstruct iqmext_fte_skin_skinframe\n{\t//as many as needed\n\tunsigned int material_idx;\n\tunsigned int shadertext_idx;\n};\nstruct iqmext_fte_skin_meshskin\n{\n\tunsigned int firstframe;\t//index into skinframes\n\tunsigned int countframes;\t//skinframes\n\tfloat interval;\n};\n/*struct iqmext_fte_shader\n{\n\tunsigned int material;\n\tunsigned int defaultshaderbody;\n\tunsigned int ofs_data;\t\t//rgba or 8bit, depending on whether a palette is specified\n\tunsigned int width;\t\t\t//if ofs_data\n\tunsigned int height;\t\t//if ofs_data\n\tunsigned int ofs_palette;\t//rgba*256. colourmapped if exactly matches quake's palette\n};\n//struct iqmext_fte_ragdoll; // pure text\nstruct iqmext_fte_bone\n{\n\tunsigned int bonegroup;\n};*/\n\n/*\ngalisskeletaltransforms_t *IQM_ImportTransforms(int *resultcount, int inverts, float *vpos, float *tcoord, float *vnorm, float *vtang, unsigned char *vbone, unsigned char *vweight)\n{\n\tgalisskeletaltransforms_t *t, *r;\n\tunsigned int num_t = 0;\n\tunsigned int v, j;\n\tfor (v = 0; v < inverts*4; v++)\n\t{\n\t\tif (vweight[v])\n\t\t\tnum_t++;\n\t}\n\tt = r = Hunk_Alloc(sizeof(*r)*num_t);\n\tfor (v = 0; v < inverts; v++)\n\t{\n\t\tfor (j = 0; j < 4; j++)\n\t\t{\n\t\t\tif (vweight[(v<<2)+j])\n\t\t\t{\n\t\t\t\tt->boneindex = vbone[(v<<2)+j];\n\t\t\t\tt->vertexindex = v;\n\t\t\t\tVectorScale(vpos, vweight[(v<<2)+j]/255.0, t->org);\n\t\t\t\tVectorScale(vnorm, vweight[(v<<2)+j]/255.0, t->normal);\n\t\t\t\tt++;\n\t\t\t}\n\t\t}\n\t}\n\treturn r;\n}\n*/\n\nstatic qboolean IQM_ImportArray4Bone(const qbyte *fte_restrict base, const struct iqmvertexarray *fte_restrict src, bone_vec4_t *fte_restrict out, size_t count, unsigned int maxval)\n{\n\tsize_t i;\n\tunsigned int j;\n\tunsigned int sz = LittleLong(src->size);\n\tunsigned int fmt = LittleLong(src->format);\n\tunsigned int offset = LittleLong(src->offset);\n\tqboolean invalid = false;\n\tmaxval = min(MAX_BONES,maxval);\t//output is bytes.\n\tif (!offset)\n\t{\n\t\tsz = 0;\n\t\tfmt = IQM_UBYTE;\n\t}\n\tswitch(fmt)\n\t{\n\tdefault:\n\t\tsz = 0;\n\t\tinvalid = true;\n\t\tbreak;\n\tcase IQM_BYTE:\t//FIXME: should be signed, but this makes no sense for our uses\n\tcase IQM_UBYTE:\n\t\t{\n\t\t\tconst qbyte *in = (const qbyte*)(base+offset);\n\t\t\t/*if (sz == 4)\n\t\t\t\tmemcpy(out, in, count * sizeof(*out));\t//the fast path.\n\t\t\telse*/ for (i = 0; i < count; i++)\n\t\t\t{\n\t\t\t\tfor (j = 0; j < 4 && j < sz; j++)\n\t\t\t\t{\n\t\t\t\t\tif (in[i*sz+j] >= maxval)\n\t\t\t\t\t{\n\t\t\t\t\t\tout[i][j] = 0;\n\t\t\t\t\t\tinvalid = true;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\tout[i][j] = in[i*sz+j];\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tbreak;\n\tcase IQM_SHORT://FIXME: should be signed, but this makes no sense for our uses\n\tcase IQM_USHORT:\n\t\t{\n\t\t\tconst unsigned short *in = (const unsigned short*)(base+offset);\n\t\t\tfor (i = 0; i < count; i++)\n\t\t\t{\n\t\t\t\tfor (j = 0; j < 4 && j < sz; j++)\n\t\t\t\t{\n\t\t\t\t\tif (in[i*sz+j] >= maxval)\n\t\t\t\t\t{\n\t\t\t\t\t\tout[i][j] = 0;\n\t\t\t\t\t\tinvalid = true;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\tout[i][j] = in[i*sz+j];\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tbreak;\n\tcase IQM_INT://FIXME: should be signed, but this makes no sense for our uses\n\tcase IQM_UINT:\n\t\t{\n\t\t\tconst unsigned int *in = (const unsigned int*)(base+offset);\n\t\t\tfor (i = 0; i < count; i++)\n\t\t\t{\n\t\t\t\tfor (j = 0; j < 4 && j < sz; j++)\n\t\t\t\t{\n\t\t\t\t\tif (in[i*sz+j] >= maxval)\n\t\t\t\t\t{\n\t\t\t\t\t\tout[i][j] = 0;\n\t\t\t\t\t\tinvalid = true;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\tout[i][j] = in[i*sz+j];\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tbreak;\n\t//float types don't really make sense\n\t}\n\n\t//if there were not enough elements, pad it.\n\tif (sz < 4)\n\t{\n\t\tfor (i = 0; i < count; i++)\n\t\t{\n\t\t\tfor (j = sz; j < 4; j++)\n\t\t\t\tout[i][j] = 0;\n\t\t}\n\t}\n\n\treturn !invalid;\n}\nstatic void IQM_ImportArrayF(const qbyte *fte_restrict base, const struct iqmvertexarray *fte_restrict src, float *fte_restrict out, size_t e, size_t count, float *def)\n{\n\tsize_t i;\n\tunsigned int j;\n\tunsigned int sz = LittleLong(src->size);\n\tunsigned int fmt = LittleLong(src->format);\n\tunsigned int offset = LittleLong(src->offset);\n\tif (!offset)\n\t{\n\t\tsz = 0;\n\t\tfmt = IQM_FLOAT;\n\t}\n\tswitch(fmt)\n\t{\n\tdefault:\n\t\tsz = 0;\n\t\tbreak;\n\tcase IQM_BYTE:\t//negatives not handled properly\n\t\t{\n\t\t\tconst signed char *in = (const signed char*)(base+offset);\n\t\t\tfor (i = 0; i < count; i++)\n\t\t\t{\n\t\t\t\tfor (j = 0; j < e && j < sz; j++)\n\t\t\t\t\tout[i*e+j] = in[i*sz+j] * (1.0/127);\n\t\t\t}\n\t\t}\n\t\tbreak;\n\tcase IQM_UBYTE:\n\t\t{\n\t\t\tconst qbyte *in = (const qbyte*)(base+offset);\n\t\t\tfor (i = 0; i < count; i++)\n\t\t\t{\n\t\t\t\tfor (j = 0; j < e && j < sz; j++)\n\t\t\t\t\tout[i*e+j] = in[i*sz+j] * (1.0/255);\n\t\t\t}\n\t\t}\n\t\tbreak;\n\tcase IQM_SHORT:\t//negatives not handled properly\n\t\t{\n\t\t\tconst signed short *in = (const signed short*)(base+offset);\n\t\t\tfor (i = 0; i < count; i++)\n\t\t\t{\n\t\t\t\tfor (j = 0; j < e && j < sz; j++)\n\t\t\t\t\tout[i*e+j] = in[i*sz+j] * (1.0/32767);\n\t\t\t}\n\t\t}\n\t\tbreak;\n\tcase IQM_USHORT:\n\t\t{\n\t\t\tconst unsigned short *in = (const unsigned short*)(base+offset);\n\t\t\tfor (i = 0; i < count; i++)\n\t\t\t{\n\t\t\t\tfor (j = 0; j < e && j < sz; j++)\n\t\t\t\t\tout[i*e+j] = in[i*sz+j] * (1.0/65535);\n\t\t\t}\n\t\t}\n\t\tbreak;\n\tcase IQM_INT:\t//negatives not handled properly\n\t\t{\n\t\t\tconst signed int *in = (const signed int*)(base+offset);\n\t\t\tfor (i = 0; i < count; i++)\n\t\t\t{\n\t\t\t\tfor (j = 0; j < e && j < sz; j++)\n\t\t\t\t\tout[i*e+j] = in[i*sz+j]/(float)0x7fffffff;\n\t\t\t}\n\t\t}\n\t\tbreak;\n\tcase IQM_UINT:\n\t\t{\n\t\t\tconst unsigned int *in = (const unsigned int*)(base+offset);\n\t\t\tfor (i = 0; i < count; i++)\n\t\t\t{\n\t\t\t\tfor (j = 0; j < e && j < sz; j++)\n\t\t\t\t\tout[i*e+j] = in[i*sz+j]/(float)0xffffffffu;\n\t\t\t}\n\t\t}\n\t\tbreak;\n\n\tcase IQM_HALF:\n#ifdef __F16C__\n\t\t{\t//x86 intrinsics\n\t\t\tconst unsigned short *in = (const unsigned short*)(base+offset);\n\t\t\tfor (i = 0; i < count; i++)\n\t\t\t{\n\t\t\t\tfor (j = 0; j < e && j < sz; j++)\n\t\t\t\t\tout[i*e+j] = _cvtsh_ss(in[i*sz+j]);\n\t\t\t}\n\t\t}\n#elif 0\n\t\t{\n\t\t\tconst _Float16 *in = (const _Float16*)(base+offset);\n\t\t\tfor (i = 0; i < count; i++)\n\t\t\t{\n\t\t\t\tfor (j = 0; j < e && j < sz; j++)\n\t\t\t\t\tout[i*e+j] = in[i*sz+j];\n\t\t\t}\n\t\t}\n#else\n\t\tsz = 0;\n#endif\n\t\tbreak;\n\tcase IQM_FLOAT:\n\t\t{\n\t\t\tconst float *in = (const float*)(base+offset);\n\t\t\tif (e == sz)\n\t\t\t\tmemcpy(out, in, e * sizeof(float) * count);\n\t\t\telse for (i = 0; i < count; i++)\n\t\t\t{\n\t\t\t\tfor (j = 0; j < e && j < sz; j++)\n\t\t\t\t\tout[i*e+j] = in[i*sz+j];\n\t\t\t}\n\t\t}\n\t\tbreak;\n\tcase IQM_DOUBLE:\n\t\t{\n\t\t\tconst double *in = (const double*)(base+offset);\n\t\t\tfor (i = 0; i < count; i++)\n\t\t\t{\n\t\t\t\tfor (j = 0; j < e && j < sz; j++)\n\t\t\t\t\tout[i*e+j] = in[i*sz+j];\n\t\t\t}\n\t\t}\n\t\tbreak;\n\t}\n\n\t//if there were not enough elements, pad it.\n\tif (sz < e)\n\t{\n\t\tfor (i = 0; i < count; i++)\n\t\t{\n\t\t\tfor (j = sz; j < e; j++)\n\t\t\t\tout[i*e+j] = def[j];\n\t\t}\n\t}\n}\n\nstatic const void *IQM_FindExtension(const char *buffer, size_t buffersize, const char *extname, int index, size_t *extsize)\n{\n\tconst struct iqmheader *h = (const struct iqmheader *)buffer;\n\tconst char *strings = buffer + h->ofs_text;\n\tconst struct iqmextension *ext;\n\tint i;\n\tfor (i = 0, ext = (const struct iqmextension*)(buffer + h->ofs_extensions); i < h->num_extensions; i++, ext = (const struct iqmextension*)(buffer + ext->ofs_extensions))\n\t{\n\t\tif ((const char*)ext > buffer+buffersize || ext->name > h->num_text || ext->ofs_data+ext->num_data>buffersize)\n\t\t\tbreak;\n\t\tif (!Q_strcasecmp(strings + ext->name, extname) && index-->=0)\n\t\t{\n\t\t\t*extsize = ext->num_data;\n\t\t\treturn buffer + ext->ofs_data;\n\t\t}\n\t}\n\t*extsize = 0;\n\treturn NULL;\n}\n\nstatic void Mod_CleanWeights(const char *modelname, size_t numverts, vec4_t *oweight, bone_vec4_t *oindex)\n{\t//some IQMs lack weight values, apparently.\n\tint j, v;\n\tqboolean problemfound = false;\n\tfor (v = 0; v < numverts; v++)\n\t{\n\t\tfloat t = oweight[v][0]+oweight[v][1]+oweight[v][2]+oweight[v][3];\n\t\tif (!t)\n\t\t{\n\t\t\tproblemfound = true;\n\t\t\tVector4Set(oweight[v], 1, 0, 0, 0);\n\t\t}\n\t\telse if (t < 0.99 || t > 1.01)\n\t\t\tVector4Scale(oweight[v], 1/t, oweight[v]);\n\n\t\t//compact any omitted weights...\n\t\tfor(j = 3; j > 0; )\n\t\t{\n\t\t\tif (oweight[v][j] && !oweight[v][j-1])\n\t\t\t{\n\t\t\t\tproblemfound = true;\n\t\t\t\toweight[v][j-1] = oweight[v][j];\n\t\t\t\toindex[v][j-1] = oindex[v][j];\n\t\t\t\toweight[v][j] = 0;\n\t\t\t\tj++; //bubble back up\n\t\t\t}\n\t\t\telse\n\t\t\t\tj--;\n\t\t}\n\t}\n\tif (problemfound)\n\t\tCon_DPrintf(CON_ERROR\"%s has invalid vertex weights. Verticies will probably be attached to the wrong bones\\n\", modelname);\n}\n\nstruct iqmstrings_s\n{\n\tconst char *base;\n\tsize_t size;\n};\nstatic const char *Mod_IQMString(struct iqmstrings_s *strings, int offset)\n{\n\tif (offset < 0 || offset >= strings->size)\n\t\treturn \"<BADSTRING>\";\n\treturn strings->base + offset;\n}\nstatic galiasinfo_t *Mod_ParseIQMMeshModel(model_t *mod, const char *buffer, size_t fsize)\n{\n\tconst struct iqmheader *h = (const struct iqmheader *)buffer;\n\tconst struct iqmmesh *mesh;\n\tconst struct iqmvertexarray *varray;\n\tconst struct iqmtriangle *tris;\n\tconst struct iqmanim *anim;\n\tconst struct iqmext_fte_mesh *ftemesh;\n\tconst struct iqmext_fte_event *fteevents;\n\tstruct iqmstrings_s strings;\n\n\tconst unsigned int *fteskincount;\n\tconst struct iqmext_fte_skin_meshskin *fteskins;\n\tconst struct iqmext_fte_skin_skinframe *fteskinframes;\n\n\tunsigned int i, j, t, numtris, numverts, firstvert, firsttri;\n\tsize_t extsize;\n\n\tconst float *vtang = NULL;\n\tstruct iqmvertexarray vpos = {0}, vnorm = {0}, vtcoord = {0}, vbone = {0}, vweight = {0}, vrgba = {0};\n\tunsigned int type, fmt, size, offset;\n\tconst unsigned short *framedata;\n\tvec4_t defaultcolour = {1,1,1,1};\n\tvec4_t defaultweight = {0,0,0,0};\n\tvec4_t defaultvert = {0,0,0,1};\n\n\tconst struct iqmbounds\t*inbounds;\n\n\tint memsize;\n\tqbyte *obase=NULL;\n\tvecV_t *opos=NULL;\n\tvec3_t *onorm1=NULL, *onorm2=NULL, *onorm3=NULL;\n\tvec4_t *oweight=NULL;\n\tbone_vec4_t *oindex=NULL;\n\tfloat *opose=NULL,*oposebase=NULL;\n\tvec2_t *otcoords = NULL;\n\tvec4_t *orgbaf = NULL;\n\n\n\tgaliasinfo_t *gai=NULL;\n#ifndef SERVERONLY\n\tint skinfiles;\n#endif\n\tgaliasanimation_t *fgroup=NULL;\n\tgaliasbone_t *bones = NULL;\n\tindex_t *idx;\n\tqboolean noweights;\n\tframeinfo_t *framegroups;\n\tint numgroups;\n\tqboolean fuckedevents = false;\n\n\tif (memcmp(h->magic, IQM_MAGIC, sizeof(h->magic)))\n\t{\n\t\tCon_Printf(\"%s: format not recognised\\n\", mod->name);\n\t\treturn NULL;\n\t}\n\tif (h->version != IQM_VERSION1 && h->version != IQM_VERSION2)\n\t{\n\t\tCon_Printf(\"%s: unsupported IQM version\\n\", mod->name);\n\t\treturn NULL;\n\t}\n\tif (h->filesize != fsize)\n\t{\n\t\tCon_Printf(\"%s: size (%u != %\"PRIuSIZE\")\\n\", mod->name, h->filesize, fsize);\n\t\treturn NULL;\n\t}\n\n\tvarray = (const struct iqmvertexarray*)(buffer + h->ofs_vertexarrays);\n\tfor (i = 0; i < h->num_vertexarrays; i++)\n\t{\n\t\ttype = LittleLong(varray[i].type);\n\t\tfmt = LittleLong(varray[i].format);\n\t\tsize = LittleLong(varray[i].size);\n\t\toffset = LittleLong(varray[i].offset);\n\t\tif (type == IQM_POSITION)\n\t\t\tvpos = varray[i];\n\t\telse if (type == IQM_TEXCOORD)\n\t\t\tvtcoord = varray[i];\n\t\telse if (type == IQM_NORMAL)\n\t\t\tvnorm = varray[i];\n\t\telse if (type == IQM_TANGENT && fmt == IQM_FLOAT && size == 4) /*yup, 4, extra is side, for the bitangent*/\n\t\t\tvtang = (const float*)(buffer + offset);\n\t\telse if (type == IQM_BLENDINDEXES)\n\t\t\tvbone = varray[i];\n\t\telse if (type == IQM_BLENDWEIGHTS)\n\t\t\tvweight = varray[i];\n\t\telse if (type == IQM_COLOR)\n\t\t\tvrgba = varray[i];\n\t\telse\n\t\t\tCon_Printf(\"Unrecognised iqm info (type=%i, fmt=%i, size=%i)\\n\", type, fmt, size);\n\t}\n\n\t/*if (!h->num_meshes)\n\t{\n\t\tCon_Printf(\"%s: IQM has no meshes\\n\", mod->name);\n\t\treturn NULL;\n\t}*/\n\n\t//a mesh must contain vertex coords or its not much of a mesh.\n\t//we also require texcoords because we can.\n\t//we don't require normals\n\t//we don't require weights, but such models won't animate.\n\tif (h->num_vertexes > 0 && (!vpos.offset || !vtcoord.offset))\n\t{\n\t\tCon_Printf(\"%s is missing vertex array data\\n\", mod->name);\n\t\treturn NULL;\n\t}\n\tnoweights = !vbone.offset || !vweight.offset;\n\tif (!h->num_meshes)\n\t\tnoweights = true;\n\telse if (noweights)\n\t{\n\t\tif (h->num_frames || h->num_anims || h->num_joints)\n\t\t{\n\t\t\tCon_Printf(\"%s: animated IQM lacks bone weights\\n\", mod->name);\n\t\t\treturn NULL;\n\t\t}\n\t}\n\n\tif (h->num_joints > MAX_BONES)\n\t{\n\t\tCon_Printf(\"%s: IQM has %u joints, max supported is %u.\\n\", mod->name, h->num_joints, MAX_BONES);\n\t\treturn NULL;\n\t}\n\tif (h->num_poses > MAX_BONES)\n\t{\n\t\tCon_Printf(\"%s: IQM has %u bones, max supported is %u.\\n\", mod->name, h->num_joints, MAX_BONES);\n\t\treturn NULL;\n\t}\n\tif (h->num_joints && h->num_poses && h->num_joints != h->num_poses)\n\t{\n\t\tCon_Printf(\"%s: IQM has mismatched joints (%i vs %i).\\n\", mod->name, h->num_joints, h->num_poses);\n\t\treturn NULL;\n\t}\n\tif (h->num_meshes && !noweights && !h->num_joints)\n\t{\n\t\tCon_Printf(\"%s: mesh IQM has no joints.\\n\", mod->name);\n\t\treturn NULL;\n\t}\n\tif (h->num_frames && !h->num_poses)\n\t{\n\t\tCon_Printf(\"%s: animated IQM has no poses.\\n\", mod->name);\n\t\treturn NULL;\n\t}\n\n\tstrings.base = buffer + h->ofs_text;\n\tstrings.size = h->num_text;\n\n\t/*try to completely disregard all the info the creator carefully added to their model...*/\n\tnumgroups = 0;\n\tframegroups = NULL;\n\tif (!numgroups)\n\t\tframegroups = ParseFrameInfo(mod, &numgroups);\n\tif (!numgroups && h->num_anims)\n\t{\n\t\t/*use the model's framegroups*/\n\t\tnumgroups = h->num_anims;\n\t\tframegroups = malloc(sizeof(*framegroups)*numgroups);\n\n\t\tanim = (const struct iqmanim*)(buffer + h->ofs_anims);\n\t\tfor (i = 0; i < numgroups; i++)\n\t\t{\n\t\t\tframegroups[i].posesarray = false;\n\t\t\tframegroups[i].firstpose = LittleLong(anim[i].first_frame);\n\t\t\tframegroups[i].posecount = LittleLong(anim[i].num_frames);\n\t\t\tframegroups[i].fps = LittleFloat(anim[i].framerate);\n\t\t\tframegroups[i].loop = !!(LittleLong(anim[i].flags) & IQM_LOOP);\n\t\t\tframegroups[i].events = NULL;\n\t\t\tframegroups[i].action = -1;\n\t\t\tframegroups[i].actionweight = 0;\n\t\t\tQ_strncpyz(framegroups[i].name, Mod_IQMString(&strings, anim[i].name), sizeof(fgroup[i].name));\n\t\t}\n\t}\n\telse\n\t\tfuckedevents = true;\t//we're not using the animation data from the model, so ignore any events because they won't make sense any more.\n\tif (!numgroups && !noweights && h->num_joints)\n\t{\t/*base frame only*/\n\t\tnumgroups = 1;\n\t\tframegroups = malloc(sizeof(*framegroups));\n\t\tframegroups->posesarray = false;\n\t\tframegroups->firstpose = -1;\n\t\tframegroups->posecount = 1;\n\t\tframegroups->fps = 10;\n\t\tframegroups->loop = 1;\n\t\tframegroups->events = NULL;\n\t\tframegroups->action = -1;\n\t\tframegroups->actionweight = 0;\n\t\tstrcpy(framegroups->name, \"base\");\n\t}\n\n\tmesh = (const struct iqmmesh*)(buffer + h->ofs_meshes);\n\n#ifndef SERVERONLY\n\tskinfiles = Mod_CountSkinFiles(mod);\n\tif (skinfiles < 1)\n\t\tskinfiles = 1;\t//iqms have 1 skin and one skin only and always. make sure its loaded.\n#endif\n\n\t/*allocate a nice big block of memory and figure out where stuff is*/\n\t/*run through twice, so things are consistant*/\n#define dalloc(o,count) do{o = (void*)(obase+memsize); memsize += sizeof(*o)*(count);}while(0)\n\tfor (i = 0, memsize = 0, obase = NULL; i < 2; i++)\n\t{\n\t\tif (i)\n\t\t\tobase = ZG_Malloc(&mod->memgroup, memsize);\n\t\tmemsize = 0;\n\t\tdalloc(gai, max(1,h->num_meshes));\n\t\tdalloc(bones, h->num_joints);\n\t\tdalloc(opos, h->num_vertexes);\n\t\tdalloc(onorm1, h->num_vertexes);\n\t\tdalloc(onorm2, h->num_vertexes);\n\t\tdalloc(onorm3, h->num_vertexes);\n\t\tif (!noweights)\n\t\t{\n\t\t\tdalloc(oindex, h->num_vertexes);\n\t\t\tdalloc(oweight, h->num_vertexes);\n\t\t}\n\t\telse\n\t\t{\n\t\t\toindex = NULL;\n\t\t\toweight = NULL;\n\t\t}\n#ifndef SERVERONLY\n\t\tif (vtcoord.offset)\n\t\t\tdalloc(otcoords, h->num_vertexes);\n\t\telse\n\t\t\totcoords = NULL;\n\t\tif (vrgba.offset)\n\t\t\tdalloc(orgbaf, h->num_vertexes);\n\t\telse\n\t\t\torgbaf = NULL;\n#endif\n\t\tdalloc(fgroup, numgroups);\n\t\tdalloc(oposebase, 12*h->num_joints);\n\t\tdalloc(opose, 12*(h->num_poses*h->num_frames));\n\t}\n#undef dalloc\n\n//no code to load animations or bones\n\tframedata = (const unsigned short*)(buffer + h->ofs_frames);\n\n\t/*Version 1 supports only normalized quaternions, version 2 uses complete quaternions. Some struct sizes change for this, otherwise functionally identical.*/\n\tif (h->version == IQM_VERSION1)\n\t{\n\t\tconst struct iqmpose1 *p, *ipose = (const struct iqmpose1*)(buffer + h->ofs_poses);\n\t\tconst struct iqmjoint1 *ijoint = (const struct iqmjoint1*)(buffer + h->ofs_joints);\n\t\tvec3_t pos;\n\t\tvec4_t quat;\n\t\tvec3_t scale;\n\t\tfloat mat[12];\n\n\t\t//joint info (mesh)\n\t\tfor (i = 0; i < h->num_joints; i++)\n\t\t{\n\t\t\tQ_strncpyz(bones[i].name, Mod_IQMString(&strings, ijoint[i].name), sizeof(bones[i].name));\n\t\t\tbones[i].parent = ijoint[i].parent;\n\n\t\t\tGenMatrixPosQuat3Scale(ijoint[i].translate, ijoint[i].rotate, ijoint[i].scale, mat);\n\n\t\t\tif (ijoint[i].parent >= 0)\n\t\t\t\tMatrix3x4_Multiply(mat, &oposebase[ijoint[i].parent*12], &oposebase[i*12]);\n\t\t\telse\n\t\t\t\tmemcpy(&oposebase[i*12], mat, sizeof(mat));\n\t\t\tMatrix3x4_Invert_Simple(&oposebase[i*12], bones[i].inverse);\n\t\t}\n\n\t\t//pose info (anim)\n\t\tfor (i = 0; i < h->num_frames; i++)\n\t\t{\n\t\t\tfor (j = 0, p = ipose; j < h->num_poses; j++, p++)\n\t\t\t{\n\t\t\t\tpos[0]   = p->channeloffset[0]; if (p->mask &   1) pos[0]   += *framedata++ * p->channelscale[0];\n\t\t\t\tpos[1]   = p->channeloffset[1]; if (p->mask &   2) pos[1]   += *framedata++ * p->channelscale[1];\n\t\t\t\tpos[2]   = p->channeloffset[2]; if (p->mask &   4) pos[2]   += *framedata++ * p->channelscale[2];\n\t\t\t\tquat[0]  = p->channeloffset[3]; if (p->mask &   8) quat[0]  += *framedata++ * p->channelscale[3];\n\t\t\t\tquat[1]  = p->channeloffset[4]; if (p->mask &  16) quat[1]  += *framedata++ * p->channelscale[4];\n\t\t\t\tquat[2]  = p->channeloffset[5]; if (p->mask &  32) quat[2]  += *framedata++ * p->channelscale[5];\n\t\t\t\tscale[0] = p->channeloffset[6]; if (p->mask &  64) scale[0] += *framedata++ * p->channelscale[6];\n\t\t\t\tscale[1] = p->channeloffset[7]; if (p->mask & 128) scale[1] += *framedata++ * p->channelscale[7];\n\t\t\t\tscale[2] = p->channeloffset[8]; if (p->mask & 256) scale[2] += *framedata++ * p->channelscale[8];\n\n\t\t\t\tquat[3] = -sqrt(max(1.0 - pow(VectorLength(quat),2), 0.0));\n\n\t\t\t\tGenMatrixPosQuat3Scale(pos, quat, scale, &opose[(i*h->num_poses+j)*12]);\n\t\t\t}\n\t\t}\n\n\t\tif (framedata != (const unsigned short*)(buffer + h->ofs_frames) + h->num_framechannels*h->num_frames)\n\t\t\tCon_Printf(\"%s: Incorrect number of framechannels found\\n\", mod->name);\n\t}\n\telse\n\t{\n\t\tconst struct iqmpose2 *p, *ipose = (const struct iqmpose2*)(buffer + h->ofs_poses);\n\t\tconst struct iqmjoint2 *ijoint = (const struct iqmjoint2*)(buffer + h->ofs_joints);\n\t\tvec3_t pos;\n\t\tvec4_t quat;\n\t\tvec3_t scale;\n\t\tfloat mat[12];\n\t\tint fc;\n\n\t\t//joint info (mesh)\n\t\tfor (i = 0; i < h->num_joints; i++)\n\t\t{\n\t\t\tQ_strncpyz(bones[i].name, Mod_IQMString(&strings, ijoint[i].name), sizeof(bones[i].name));\n\n\t\t\tGenMatrixPosQuat4Scale(ijoint[i].translate, ijoint[i].rotate, ijoint[i].scale, mat);\n\n\t\t\tif (ijoint[i].parent >= 0 && ijoint[i].parent < i)\n\t\t\t{\n\t\t\t\tbones[i].parent = ijoint[i].parent;\n\t\t\t\tMatrix3x4_Multiply(mat, &oposebase[ijoint[i].parent*12], &oposebase[i*12]);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tmemcpy(&oposebase[i*12], mat, sizeof(mat));\n\t\t\t\tbones[i].parent = -1;\n\t\t\t}\n\t\t\tMatrix3x4_Invert_Simple(&oposebase[i*12], bones[i].inverse);\n\t\t}\n\n\t\tfor (fc = 0, j = 0, p = ipose; j < h->num_poses; j++, p++)\n\t\t{\n\t\t\tfor (i = 0; i < 10; i++)\n\t\t\t\tif (p->mask & (1<<i))\n\t\t\t\t\tfc++;\n\t\t}\n\t\tif (fc != h->num_framechannels)\n\t\t\tCon_Printf(\"%s: Incorrect number of framechannels found (%i, expected %i)\\n\", mod->name, h->num_framechannels, fc);\n\t\telse\n\n\t\t//pose info (anim)\n\t\tfor (i = 0; i < h->num_frames; i++)\n\t\t{\n\t\t\tfor (j = 0, p = ipose; j < h->num_poses; j++, p++)\n\t\t\t{\n\t\t\t\tpos[0]   = p->channeloffset[0]; if (p->mask &   1) pos[0]   += *framedata++ * p->channelscale[0];\n\t\t\t\tpos[1]   = p->channeloffset[1]; if (p->mask &   2) pos[1]   += *framedata++ * p->channelscale[1];\n\t\t\t\tpos[2]   = p->channeloffset[2]; if (p->mask &   4) pos[2]   += *framedata++ * p->channelscale[2];\n\t\t\t\tquat[0]  = p->channeloffset[3]; if (p->mask &   8) quat[0]  += *framedata++ * p->channelscale[3];\n\t\t\t\tquat[1]  = p->channeloffset[4]; if (p->mask &  16) quat[1]  += *framedata++ * p->channelscale[4];\n\t\t\t\tquat[2]  = p->channeloffset[5]; if (p->mask &  32) quat[2]  += *framedata++ * p->channelscale[5];\n\t\t\t\tquat[3]  = p->channeloffset[6]; if (p->mask &  64) quat[3]  += *framedata++ * p->channelscale[6];\n\t\t\t\tscale[0] = p->channeloffset[7]; if (p->mask & 128) scale[0] += *framedata++ * p->channelscale[7];\n\t\t\t\tscale[1] = p->channeloffset[8]; if (p->mask & 256) scale[1] += *framedata++ * p->channelscale[8];\n\t\t\t\tscale[2] = p->channeloffset[9]; if (p->mask & 512) scale[2] += *framedata++ * p->channelscale[9];\n\n\t\t\t\tGenMatrixPosQuat4Scale(pos, quat, scale, &opose[(i*h->num_poses+j)*12]);\n\t\t\t}\n\t\t}\n\t}\n\n\t//now generate the animations.\n\tfor (i = 0; i < numgroups; i++)\n\t{\n\t\tif (framegroups[i].posesarray)\n\t\t\tCon_Printf(CON_WARNING\"Mod_ParseIQMMeshModel(%s): framegroup[%i] poses array not suppported\\n\", mod->name, i);\n\t\tif (framegroups[i].firstpose + framegroups[i].posecount > h->num_frames)\n\t\t\tframegroups[i].posecount = h->num_frames - framegroups[i].firstpose;\n\t\tif (framegroups[i].firstpose >= h->num_frames)\n\t\t{\n\t\t\t//invalid/basepose.\n\t\t\tfgroup[i].skeltype = SKEL_ABSOLUTE;\n\t\t\tfgroup[i].boneofs = oposebase;\n\t\t\tfgroup[i].numposes = 1;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tfgroup[i].skeltype = SKEL_RELATIVE;\n\t\t\tfgroup[i].boneofs = opose + framegroups[i].firstpose*12*h->num_poses;\n\t\t\tfgroup[i].numposes = framegroups[i].posecount;\n\t\t}\n\n\t\tfgroup[i].loop = framegroups[i].loop;\n\t\tfgroup[i].rate = framegroups[i].fps;\n\t\tQ_strncpyz(fgroup[i].name, framegroups[i].name, sizeof(fgroup[i].name));\n\t\n\t\tif (fgroup[i].rate <= 0)\n\t\t\tfgroup[i].rate = 10;\n\n\t\tfgroup[i].events = framegroups[i].events;\n\t\tfgroup[i].action = framegroups[i].action;\n\t\tfgroup[i].actionweight = framegroups[i].actionweight;\n\t}\n\tfree(framegroups);\n\n\tif (fuckedevents)\n\t{\n\t\tfteevents = NULL;\n\t\textsize = 0;\n\t}\n\telse\n\t\tfteevents = IQM_FindExtension(buffer, fsize, \"FTE_EVENT\", 0, &extsize);\n\tif (fteevents && !(extsize % sizeof(*fteevents)))\n\t{\n\t\tgaliasevent_t *oevent, **link;\n\t\textsize /= sizeof(*fteevents);\n\t\toevent = ZG_Malloc(&mod->memgroup, sizeof(*oevent)*extsize);\n\t\tfor (; extsize>0; extsize--, fteevents++,oevent++)\n\t\t{\n\t\t\toevent->timestamp = fteevents->timestamp;\n\t\t\toevent->code = fteevents->evcode;\n\t\t\toevent->data = ZG_Malloc(&mod->memgroup, strlen(Mod_IQMString(&strings, fteevents->evdata_str))+1);\n\t\t\tstrcpy(oevent->data, Mod_IQMString(&strings, fteevents->evdata_str));\n\t\t\tlink = &fgroup[fteevents->anim].events;\n\t\t\twhile (*link && (*link)->timestamp <= oevent->timestamp)\n\t\t\t\tlink = &(*link)->next;\n\t\t\toevent->next = *link;\n\t\t\t*link = oevent;\n\t\t}\n\t}\n\n\t//determine the bounds\n\tinbounds = (const struct iqmbounds*)(buffer + h->ofs_bounds);\n\tif (h->ofs_bounds)\n\t{\n\t\tfor (i = 0; i < h->num_frames; i++)\n\t\t{\n\t\t\tvec3_t mins, maxs;\n\t\t\tmins[0] = LittleFloat(inbounds[i].bbmin[0]);\n\t\t\tmins[1] = LittleFloat(inbounds[i].bbmin[1]);\n\t\t\tmins[2] = LittleFloat(inbounds[i].bbmin[2]);\n\t\t\tAddPointToBounds(mins, mod->mins, mod->maxs);\n\t\t\tmaxs[0] = LittleFloat(inbounds[i].bbmax[0]);\n\t\t\tmaxs[1] = LittleFloat(inbounds[i].bbmax[1]);\n\t\t\tmaxs[2] = LittleFloat(inbounds[i].bbmax[2]);\n\t\t\tAddPointToBounds(maxs, mod->mins, mod->maxs);\n\t\t}\n\t}\n\n\n\n\tftemesh = IQM_FindExtension(buffer, fsize, \"FTE_MESH\", 0, &extsize);\n\tif (!extsize || extsize != sizeof(*ftemesh)*h->num_meshes)\n\t\tftemesh = NULL;\t//erk.\n\n\tfteskincount = IQM_FindExtension(buffer, fsize, \"FTE_SKINS\", 0, &extsize);\n\tif (extsize >= sizeof(unsigned int)*(2+h->num_meshes) && extsize == sizeof(struct iqmext_fte_skin) + sizeof(unsigned int)*h->num_meshes + LittleLong(fteskincount[0])*sizeof(*fteskinframes) + LittleLong(fteskincount[1])*sizeof(*fteskins))\n\t{\n\t\tunsigned int numskinframes = LittleLong(*fteskincount++);\n\t\t/*unsigned int numskins = LittleLong(* */fteskincount++;\n\n\t\tfteskinframes = (const struct iqmext_fte_skin_skinframe*)(fteskincount+h->num_meshes);\n\t\tfteskins = (const struct iqmext_fte_skin_meshskin*)(fteskinframes+numskinframes);\n\t}\n\telse fteskincount = NULL, fteskins = NULL, fteskinframes = NULL;\n\n\tfor (i = 0; i < max(1, h->num_meshes); i++)\n\t{\n\t\tif (h->num_meshes)\n\t\t{\n\t\t\tMod_DefaultMesh(&gai[i], Mod_IQMString(&strings, mesh[i].name), i);\n\t\t\tfirstvert = LittleLong(mesh[i].first_vertex);\n\t\t\tnumverts = LittleLong(mesh[i].num_vertexes);\n\t\t\tnumtris = LittleLong(mesh[i].num_triangles);\n\t\t\tfirsttri = LittleLong(mesh[i].first_triangle);\n\t\t}\n\t\telse\n\t\t{\t//animation-only models still need a place-holder mesh.\n\t\t\tfirstvert = 0;\n\t\t\tnumverts = 0;\n\t\t\tnumtris = 0;\n\t\t\tfirsttri = 0;\n\t\t}\n\n\t\tgai[i].nextsurf = &gai[i+1];\n\n\t\t/*animation info*/\n\t\tgai[i].shares_bones = 0;\n\t\tgai[i].numbones = h->num_joints?h->num_joints:h->num_poses;\n\t\tgai[i].ofsbones = bones;\n\t\tgai[i].numanimations = numgroups;\n\t\tgai[i].ofsanimations = fgroup;\n\n#ifndef SERVERONLY\n\t\t/*colours*/\n\t\tgai[i].ofs_rgbaf = orgbaf?(orgbaf+firstvert):NULL;\n\t\tgai[i].ofs_rgbaub = NULL;\n\t\t/*texture coords*/\n\t\tgai[i].ofs_st_array = (otcoords+firstvert);\n\t\t/*skins*/\n\t\tif (h->num_meshes)\n\t\t{\n\t\t\tgaliasskin_t *skin;\n\t\t\tskinframe_t *skinframe;\n\t\t\tunsigned int iqmskins = fteskincount?LittleLong(*fteskincount++):0;\n\t\t\tgai[i].numskins = max(iqmskins,skinfiles);\n\t\t\tgai[i].ofsskins = skin = ZG_Malloc(&mod->memgroup, sizeof(*gai[i].ofsskins)*gai[i].numskins);\n\n\t\t\tfor (j = 0; j < gai[i].numskins; j++, skin++)\n\t\t\t{\n\t\t\t\tconst struct iqmext_fte_skin_skinframe *sf;\n\t\t\t\tif (j < iqmskins)\n\t\t\t\t{\n\t\t\t\t\tsf = fteskinframes + LittleLong(fteskins->firstframe);\n\t\t\t\t\tfteskins++;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tsf = NULL;\n\n\t\t\t\tskin->skinwidth = 1;\n\t\t\t\tskin->skinheight = 1;\n\t\t\t\tQ_snprintfz(gai[i].ofsskins[j].name, sizeof(gai[i].ofsskins[j].name), \"%i\", j);\n\t\t\t\tif (sf)\n\t\t\t\t{\n\t\t\t\t\tskin->skinspeed = 1.0/LittleFloat(fteskins[-1].interval); /*something to avoid div by 0*/\n\t\t\t\t\tskin->numframes = LittleLong(fteskins[-1].countframes);\t//non-sequenced skins.\n\n\t\t\t\t\tif (!skin->numframes)\n\t\t\t\t\t\tcontinue;\n\n\t\t\t\t\tskin->frame = ZG_Malloc(&mod->memgroup, sizeof(*skin->frame)*skin->numframes);\n\t\t\t\t\tfor (t = 0; t < skin->numframes; t++, sf++)\n\t\t\t\t\t{\n\t\t\t\t\t\tQ_strncpyz(skin->frame[t].shadername, Mod_IQMString(&strings, sf->material_idx), sizeof(skin->frame[t].shadername));\n\t\t\t\t\t\tif (sf->shadertext_idx && sf->shadertext_idx<h->num_text)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tconst char *stxt = Mod_IQMString(&strings, sf->shadertext_idx);\n\t\t\t\t\t\t\tskin->frame[t].defaultshader = strcpy(ZG_Malloc(&mod->memgroup, strlen(stxt)+1), stxt);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tskin->skinspeed = 10;\t//something to avoid div by 0\n\t\t\t\t\tskin->numframes = 1;\t//non-sequenced skins.\n\n\t\t\t\t\tskin->frame = skinframe = ZG_Malloc(&mod->memgroup, sizeof(*skin->frame)*skin->numframes);\n\t\t\t\t\tQ_strncpyz(skinframe->shadername, Mod_IQMString(&strings, mesh[i].material), sizeof(skinframe->shadername));\n\t\t\t\t}\n\t\t\t}\n\t\t\tgai[i].numskins = skin-gai[i].ofsskins;\n\t\t}\n#endif\n\n\t\ttris = (const struct iqmtriangle*)(buffer + LittleLong(h->ofs_triangles));\n\t\ttris += firsttri;\n\t\tidx = ZG_Malloc(&mod->memgroup, sizeof(*idx)*numtris*3);\n\t\tgai[i].ofs_indexes = idx;\n\t\tfor (t = 0; t < numtris; t++)\n\t\t{\n\t\t\tunsigned int a,b,c;\n\t\t\ta = LittleLong(tris[t].vertex[0]) - firstvert;\n\t\t\tb = LittleLong(tris[t].vertex[1]) - firstvert;\n\t\t\tc = LittleLong(tris[t].vertex[2]) - firstvert;\n\t\t\tif (a > MAX_INDICIES || b > MAX_INDICIES || c > MAX_INDICIES)\n\t\t\t\tcontinue;\t//we can't handle this triangle properly.\n\t\t\t*idx++ = a;\n\t\t\t*idx++ = b;\n\t\t\t*idx++ = c;\n\t\t}\n\t\tgai[i].numindexes = idx - gai[i].ofs_indexes;\n\t\tif (gai[i].numindexes != numtris*3)\n\t\t\tCon_Printf(\"%s(%s|%s): Dropped %u of %u triangles due to index size limit\\n\", mod->name, gai[i].surfacename,Mod_IQMString(&strings, mesh[i].material), numtris-gai[i].numindexes/3, numtris);\n\n\t\t/*verts*/\n\t\tgai[i].shares_verts = i;\n\t\tgai[i].numverts = numverts;\n\t\tgai[i].ofs_skel_xyz = (opos+firstvert);\n\t\tgai[i].ofs_skel_norm = (onorm1+firstvert);\n\t\tgai[i].ofs_skel_svect = (onorm2+firstvert);\n\t\tgai[i].ofs_skel_tvect = (onorm3+firstvert);\n\t\tgai[i].ofs_skel_idx = oindex?(oindex+firstvert):NULL;\n\t\tgai[i].ofs_skel_weight = oweight?(oweight+firstvert):NULL;\n\n\t\tif (ftemesh)\n\t\t{\t//if we have extensions, then lets use them!\n\t\t\tgai[i].geomset = ftemesh[i].geomset;\n\t\t\tgai[i].geomid = ftemesh[i].geomid;\n\t\t\tgai[i].contents = ftemesh[i].contents;\n\t\t\tgai[i].csurface.flags = ftemesh[i].surfaceflags;\n\t\t\tgai[i].surfaceid = ftemesh[i].surfaceid;\n\t\t\tgai[i].mindist = ftemesh[i].mindist;\n\t\t\tgai[i].maxdist = ftemesh[i].maxdist;\n\n\t\t\tmod->maxlod = max(mod->maxlod, gai[i].mindist);\n\t\t\tmod->maxlod = max(mod->maxlod, gai[i].maxdist);\n\t\t}\n\t}\n\tgai[i-1].nextsurf = NULL;\n\tif (!noweights)\n\t{\n\t\tif (!IQM_ImportArray4Bone(buffer, &vbone, oindex, h->num_vertexes, h->num_joints))\n\t\t\tCon_DPrintf(CON_WARNING \"Invalid bone indexes detected inside %s\\n\", mod->name);\n\t\tIQM_ImportArrayF(buffer, &vweight, (float*)oweight, 4, h->num_vertexes, defaultweight);\n\t\tMod_CleanWeights(mod->name, h->num_vertexes, oweight, oindex);\n\t}\n\n\tif (otcoords)\n\t\tIQM_ImportArrayF(buffer, &vtcoord, (float*)otcoords, 2, h->num_vertexes, defaultweight);\n\tif (orgbaf)\n\t\tIQM_ImportArrayF(buffer, &vrgba, (float*)orgbaf, 4, h->num_vertexes, defaultcolour);\n\n\tIQM_ImportArrayF(buffer, &vnorm, (float*)onorm1, 3, h->num_vertexes, defaultcolour);\n\tIQM_ImportArrayF(buffer, &vpos, (float*)opos, sizeof(opos[0])/sizeof(float), h->num_vertexes, defaultvert);\n\n\tif (!h->ofs_bounds || !h->num_frames)\n\t\tfor (i = 0; i < h->num_vertexes; i++)\n\t\t\tAddPointToBounds(opos[i], mod->mins, mod->maxs);\n\n\tif (vnorm.offset && vtang)\n\t{\n\t\tfor (i = 0; i < h->num_vertexes; i++)\n\t\t{\n\t\t\tVectorCopy(vtang+i*4, onorm2[i]);\n\t\t\tif(LittleFloat(vtang[i*4 + 3]) < 0)\n\t\t\t\tCrossProduct(onorm2[i], onorm1[i], onorm3[i]);\n\t\t\telse\n\t\t\t\tCrossProduct(onorm1[i], onorm2[i], onorm3[i]);\n\t\t}\n\t}\n#ifndef SERVERONLY\t//hopefully dedicated servers won't need this too often...\n\telse if (h->num_vertexes)\n\t{\t//make something up\n\t\tfor (i = 0; i < h->num_meshes; i++)\n\t\t{\n\t\t\tMod_AccumulateTextureVectors(gai[i].ofs_skel_xyz, gai[i].ofs_st_array, gai[i].ofs_skel_norm, gai[i].ofs_skel_svect, gai[i].ofs_skel_tvect, gai[i].ofs_indexes, gai[i].numindexes, false);\n\t\t}\n\t\tfor (i = 0; i < h->num_meshes; i++)\n\t\t{\n\t\t\tMod_NormaliseTextureVectors(gai[i].ofs_skel_norm, gai[i].ofs_skel_svect, gai[i].ofs_skel_tvect, gai[i].numverts, false);\n\t\t}\n\t}\n#endif\n\n\treturn gai;\n}\n\nstatic qboolean QDECL Mod_LoadInterQuakeModel(model_t *mod, void *buffer, size_t fsize)\n{\n\tgaliasinfo_t *root;\n\tstruct iqmheader *h = (struct iqmheader *)buffer;\n\n\tClearBounds(mod->mins, mod->maxs);\n\n\troot = Mod_ParseIQMMeshModel(mod, buffer, fsize);\n\tif (!root)\n\t{\n\t\treturn false;\n\t}\n\n\tmod->flags = h->flags;\n\n\tmod->radius = RadiusFromBounds(mod->mins, mod->maxs);\n\n\tMod_ClampModelSize(mod);\n\tMod_ParseModelEvents(mod, root->ofsanimations, root->numanimations);\n\n\tmod->numframes = root->numanimations;\n\tmod->meshinfo = root;\n\tmod->type = mod_alias;\n\tMod_SetMeshModelFuncs(mod, !root->ofs_skel_idx&&!root->ofs_skel_weight);\n\n\treturn true;\n}\n#endif\n\n\n\n\n\n#ifdef MD5MODELS\n\nstatic qboolean Mod_ParseMD5Anim(model_t *mod, char *buffer, galiasinfo_t *prototype, void**poseofs, galiasanimation_t *gat)\n{\n#define MD5ERROR0PARAM(x) do{ Con_Printf(CON_ERROR x \"\\n\"); return false; }while(0)\n#define MD5ERROR1PARAM(x, y) do{ Con_Printf(CON_ERROR x \"\\n\", y); return false; }while(0)\n#define EXPECT(x) do{buffer = COM_ParseOut(buffer, token, sizeof(token)); if (strcmp(token, x)) MD5ERROR1PARAM(\"MD5ANIM: expected %s\", x);}while(0)\n\tunsigned int i, j;\n\n\tgaliasanimation_t grp;\n\n\tunsigned int parent;\n\tunsigned int numframes;\n\tunsigned int numjoints;\n\tfloat framespersecond;\n\tunsigned int numanimatedparts;\n\tgaliasbone_t *bonelist;\n\n\tunsigned char *boneflags;\n\tunsigned int *firstanimatedcomponents;\n\n\tfloat *animatedcomponents;\n\tfloat *baseframe;\t//6 components.\n\tfloat *posedata;\n\tfloat tx, ty, tz, qx, qy, qz;\n\tint fac, flags;\n\tfloat f;\n\tchar token[8192];\n\n\tEXPECT(\"MD5Version\");\n\tEXPECT(\"10\");\n\n\tEXPECT(\"commandline\");\n\tbuffer = COM_ParseOut(buffer, token, sizeof(token));\n\n\tEXPECT(\"numFrames\");\n\tbuffer = COM_ParseOut(buffer, token, sizeof(token));\n\tnumframes = atoi(token);\n\n\tEXPECT(\"numJoints\");\n\tbuffer = COM_ParseOut(buffer, token, sizeof(token));\n\tnumjoints = atoi(token);\n\n\tEXPECT(\"frameRate\");\n\tbuffer = COM_ParseOut(buffer, token, sizeof(token));\n\tframespersecond = atof(token);\n\n\tEXPECT(\"numAnimatedComponents\");\n\tbuffer = COM_ParseOut(buffer, token, sizeof(token));\n\tnumanimatedparts = atoi(token);\n\n\tfirstanimatedcomponents = BZ_Malloc(sizeof(int)*numjoints);\n\tanimatedcomponents = BZ_Malloc(sizeof(float)*numanimatedparts);\n\tboneflags = BZ_Malloc(sizeof(unsigned char)*numjoints);\n\tbaseframe = BZ_Malloc(sizeof(float)*12*numjoints);\n\n\t*poseofs = posedata = ZG_Malloc(&mod->memgroup, sizeof(float)*12*numjoints*numframes);\n\n\tif (!prototype->baseframeofs)\n\t\t prototype->baseframeofs = posedata;\n\n\tif (prototype->numbones)\n\t{\n\t\tif (prototype->numbones != numjoints)\n\t\t\tMD5ERROR0PARAM(\"MD5ANIM: number of bones doesn't match\");\n\t\tbonelist = prototype->ofsbones;\n\t}\n\telse\n\t{\n\t\tbonelist = ZG_Malloc(&mod->memgroup, sizeof(galiasbone_t)*numjoints);\n\t\tprototype->ofsbones = bonelist;\n\t}\n\n\tEXPECT(\"hierarchy\");\n\tEXPECT(\"{\");\n\tfor (i = 0; i < numjoints; i++, bonelist++)\n\t{\n\t\tbuffer = COM_ParseOut(buffer, token, sizeof(token));\n\t\tif (prototype->numbones)\n\t\t{\n\t\t\tif (strcmp(bonelist->name, token))\n\t\t\t\tMD5ERROR1PARAM(\"MD5ANIM: bone name doesn't match (%s)\", token);\n\t\t}\n\t\telse\n\t\t\tQ_strncpyz(bonelist->name, token, sizeof(bonelist->name));\n\t\tbuffer = COM_ParseOut(buffer, token, sizeof(token));\n\t\tparent = atoi(token);\n\t\tif (prototype->numbones)\n\t\t{\n\t\t\tif (bonelist->parent != parent)\n\t\t\t\tMD5ERROR1PARAM(\"MD5ANIM: bone name doesn't match (%s)\", token);\n\t\t}\n\t\telse\n\t\t\tbonelist->parent = parent;\n\n\t\tbuffer = COM_ParseOut(buffer, token, sizeof(token));\n\t\tboneflags[i] = atoi(token);\n\t\tbuffer = COM_ParseOut(buffer, token, sizeof(token));\n\t\tfirstanimatedcomponents[i] = atoi(token);\n\t}\n\tEXPECT(\"}\");\n\n\tif (!prototype->numbones)\n\t\tprototype->numbones = numjoints;\n\n\tEXPECT(\"bounds\");\n\tEXPECT(\"{\");\n\tfor (i = 0; i < numframes; i++)\n\t{\n\t\tEXPECT(\"(\");\n\t\tbuffer = COM_ParseOut(buffer, token, sizeof(token));f=atoi(token);\n\t\tif (f < mod->mins[0]) mod->mins[0] = f;\n\t\tbuffer = COM_ParseOut(buffer, token, sizeof(token));f=atoi(token);\n\t\tif (f < mod->mins[1]) mod->mins[1] = f;\n\t\tbuffer = COM_ParseOut(buffer, token, sizeof(token));f=atoi(token);\n\t\tif (f < mod->mins[2]) mod->mins[2] = f;\n\t\tEXPECT(\")\");\n\t\tEXPECT(\"(\");\n\t\tbuffer = COM_ParseOut(buffer, token, sizeof(token));f=atoi(token);\n\t\tif (f > mod->maxs[0]) mod->maxs[0] = f;\n\t\tbuffer = COM_ParseOut(buffer, token, sizeof(token));f=atoi(token);\n\t\tif (f > mod->maxs[1]) mod->maxs[1] = f;\n\t\tbuffer = COM_ParseOut(buffer, token, sizeof(token));f=atoi(token);\n\t\tif (f > mod->maxs[2]) mod->maxs[2] = f;\n\t\tEXPECT(\")\");\n\t}\n\tEXPECT(\"}\");\n\n\tEXPECT(\"baseframe\");\n\tEXPECT(\"{\");\n\tfor (i = 0; i < numjoints; i++)\n\t{\n\t\tEXPECT(\"(\");\n\t\tbuffer = COM_ParseOut(buffer, token, sizeof(token));\n\t\tbaseframe[i*6+0] = atof(token);\n\t\tbuffer = COM_ParseOut(buffer, token, sizeof(token));\n\t\tbaseframe[i*6+1] = atof(token);\n\t\tbuffer = COM_ParseOut(buffer, token, sizeof(token));\n\t\tbaseframe[i*6+2] = atof(token);\n\t\tEXPECT(\")\");\n\t\tEXPECT(\"(\");\n\t\tbuffer = COM_ParseOut(buffer, token, sizeof(token));\n\t\tbaseframe[i*6+3] = atof(token);\n\t\tbuffer = COM_ParseOut(buffer, token, sizeof(token));\n\t\tbaseframe[i*6+4] = atof(token);\n\t\tbuffer = COM_ParseOut(buffer, token, sizeof(token));\n\t\tbaseframe[i*6+5] = atof(token);\n\t\tEXPECT(\")\");\n\t}\n\tEXPECT(\"}\");\n\n\tfor (i = 0; i < numframes; i++)\n\t{\n\t\tEXPECT(\"frame\");\n\t\tbuffer = COM_ParseOut(buffer, token, sizeof(token));\n\t\tif (atoi(token) != i)\n\t\t\tMD5ERROR1PARAM(\"MD5ANIM: expected frame %i\", i);\n\t\tEXPECT(\"{\");\n\t\tfor (j = 0; j < numanimatedparts; j++)\n\t\t{\n\t\t\tbuffer = COM_ParseOut(buffer, token, sizeof(token));\n\t\t\tanimatedcomponents[j] = atof(token);\n\t\t}\n\t\tEXPECT(\"}\");\n\n\t\tfor (j = 0; j < numjoints; j++)\n\t\t{\n\t\t\tfac = firstanimatedcomponents[j];\n\t\t\tflags = boneflags[j];\n\n\t\t\tif (flags&1)\n\t\t\t\ttx = animatedcomponents[fac++];\n\t\t\telse\n\t\t\t\ttx = baseframe[j*6+0];\n\t\t\tif (flags&2)\n\t\t\t\tty = animatedcomponents[fac++];\n\t\t\telse\n\t\t\t\tty = baseframe[j*6+1];\n\t\t\tif (flags&4)\n\t\t\t\ttz = animatedcomponents[fac++];\n\t\t\telse\n\t\t\t\ttz = baseframe[j*6+2];\n\t\t\tif (flags&8)\n\t\t\t\tqx = animatedcomponents[fac++];\n\t\t\telse\n\t\t\t\tqx = baseframe[j*6+3];\n\t\t\tif (flags&16)\n\t\t\t\tqy = animatedcomponents[fac++];\n\t\t\telse\n\t\t\t\tqy = baseframe[j*6+4];\n\t\t\tif (flags&32)\n\t\t\t\tqz = animatedcomponents[fac++];\n\t\t\telse\n\t\t\t\tqz = baseframe[j*6+5];\n\n\t\t\tGenMatrix(tx, ty, tz, qx, qy, qz, posedata+12*(j+numjoints*i));\n\t\t}\n\t}\n\n\tBZ_Free(firstanimatedcomponents);\n\tBZ_Free(animatedcomponents);\n\tBZ_Free(boneflags);\n\tBZ_Free(baseframe);\n\n\tmemset(&grp, 0, sizeof(grp));\n\tQ_strncpyz(grp.name, \"\", sizeof(grp.name));\n\tgrp.skeltype = SKEL_RELATIVE;\n\tgrp.numposes = numframes;\n\tgrp.rate = framespersecond;\n\tgrp.loop = true;\n\tgrp.boneofs = *poseofs;\n\n\t*gat = grp;\n\treturn true;\n#undef MD5ERROR0PARAM\n#undef MD5ERROR1PARAM\n#undef EXPECT\n}\n\nstatic galiasinfo_t *Mod_ParseMD5MeshModel(model_t *mod, char *buffer, char *modname)\n{\n#define MD5ERROR0PARAM(x) do{ Con_Printf(CON_ERROR x \"\\n\"); return NULL; }while(0)\n#define MD5ERROR1PARAM(x, y) do{ Con_Printf(CON_ERROR x \"\\n\", y); return NULL; }while(0)\n#define EXPECT(x) do{buffer = COM_ParseOut(buffer, token, sizeof(token)); if (strcmp(token, x)) Sys_Error(\"MD5MESH: expected %s\", x);}while(0)\n\tint numjoints = 0;\n\tint nummeshes = 0;\n\tqboolean foundjoints = false;\n\tint i;\n\n\tgaliasbone_t *bones = NULL;\n\tgaliasanimation_t *pose = NULL;\n\tgaliasinfo_t *inf, *root, *lastsurf;\n\tfloat *posedata;\n#ifndef SERVERONLY\n\tgaliasskin_t *skin;\n\tskinframe_t *frames;\n#endif\n\tchar *filestart = buffer;\n\tchar token[1024];\n\n\tfloat x, y, z, qx, qy, qz;\n\n\tbuffer = COM_ParseOut(buffer, token, sizeof(token));\n\tif (strcmp(token, \"MD5Version\"))\n\t\tMD5ERROR0PARAM(\"MD5 model without MD5Version identifier first\");\n\n\tbuffer = COM_ParseOut(buffer, token, sizeof(token));\n\tif (atoi(token) != 10)\n\t\tMD5ERROR0PARAM(\"MD5 model with unsupported MD5Version\");\n\n\n\troot = ZG_Malloc(&mod->memgroup, sizeof(galiasinfo_t));\n\tlastsurf = NULL;\n\n\tfor(;;)\n\t{\n\t\tbuffer = COM_ParseOut(buffer, token, sizeof(token));\n\t\tif (!buffer)\n\t\t\tbreak;\n\n\t\tif (!strcmp(token, \"numFrames\"))\n\t\t{\n\t\t\tvoid *poseofs;\n\t\t\tgaliasanimation_t *grp = ZG_Malloc(&mod->memgroup, sizeof(galiasanimation_t));\n\t\t\tMod_ParseMD5Anim(mod, filestart, root, &poseofs, grp);\n\t\t\troot->ofsanimations = grp;\n\t\t\troot->numanimations = 1;\n\t\t\tgrp->poseofs = poseofs;\n\t\t\treturn root;\n\t\t}\n\t\telse if (!strcmp(token, \"commandline\"))\n\t\t{\t//we don't need this\n\t\t\tbuffer = strchr(buffer, '\\\"');\n\t\t\tbuffer = strchr((char*)buffer+1, '\\\"')+1;\n//\t\t\tbuffer = COM_Parse(buffer);\n\t\t}\n\t\telse if (!strcmp(token, \"numJoints\"))\n\t\t{\n\t\t\tif (numjoints)\n\t\t\t\tMD5ERROR0PARAM(\"MD5MESH: numJoints was already declared\");\n\t\t\tbuffer = COM_ParseOut(buffer, token, sizeof(token));\n\t\t\tnumjoints = atoi(token);\n\t\t\tif (numjoints <= 0)\n\t\t\t\tMD5ERROR0PARAM(\"MD5MESH: Needs some joints\");\n\t\t}\n\t\telse if (!strcmp(token, \"numMeshes\"))\n\t\t{\n\t\t\tif (nummeshes)\n\t\t\t\tMD5ERROR0PARAM(\"MD5MESH: numMeshes was already declared\");\n\t\t\tbuffer = COM_ParseOut(buffer, token, sizeof(token));\n\t\t\tnummeshes = atoi(token);\n\t\t\tif (nummeshes <= 0)\n\t\t\t\tMD5ERROR0PARAM(\"MD5MESH: Needs some meshes\");\n\t\t}\n\t\telse if (!strcmp(token, \"joints\"))\n\t\t{\n\t\t\tif (foundjoints)\n\t\t\t\tMD5ERROR0PARAM(\"MD5MESH: Duplicate joints section\");\n\t\t\tfoundjoints=true;\n\t\t\tif (!numjoints)\n\t\t\t\tMD5ERROR0PARAM(\"MD5MESH: joints section before (or without) numjoints\");\n\n\t\t\tbones = ZG_Malloc(&mod->memgroup, sizeof(*bones) * numjoints);\n\t\t\tpose = ZG_Malloc(&mod->memgroup, sizeof(galiasanimation_t));\n\t\t\tposedata = ZG_Malloc(&mod->memgroup, sizeof(float)*12 * numjoints);\n\t\t\tpose->skeltype = SKEL_ABSOLUTE;\n\t\t\tpose->rate = 1;\n\t\t\tpose->numposes = 1;\n\t\t\tpose->boneofs = posedata;\n\n\t\t\troot->baseframeofs = posedata;\n\n\t\t\tQ_strncpyz(pose->name, \"base\", sizeof(pose->name));\n\n\t\t\tEXPECT(\"{\");\n\t\t\t//\"name\" parent (x y z) (s t u)\n\t\t\t//stu are a normalized quaternion, which we will convert to a 3*4 matrix for no apparent reason\n\n\t\t\tfor (i = 0; i < numjoints; i++)\n\t\t\t{\n\t\t\t\tbuffer = COM_ParseOut(buffer, token, sizeof(token));\n\t\t\t\tQ_strncpyz(bones[i].name, token, sizeof(bones[i].name));\n\t\t\t\tbuffer = COM_ParseOut(buffer, token, sizeof(token));\n\t\t\t\tbones[i].parent = atoi(token);\n\t\t\t\tif (bones[i].parent >= i)\n\t\t\t\t\tMD5ERROR0PARAM(\"MD5MESH: joints parent's must be lower\");\n\t\t\t\tif ((bones[i].parent < 0 && i) || (!i && bones[i].parent!=-1))\n\t\t\t\t\tMD5ERROR0PARAM(\"MD5MESH: Only the root joint may have a negative parent\");\n\n\t\t\t\tEXPECT(\"(\");\n\t\t\t\tbuffer = COM_ParseOut(buffer, token, sizeof(token));\n\t\t\t\tx = atof(token);\n\t\t\t\tbuffer = COM_ParseOut(buffer, token, sizeof(token));\n\t\t\t\ty = atof(token);\n\t\t\t\tbuffer = COM_ParseOut(buffer, token, sizeof(token));\n\t\t\t\tz = atof(token);\n\t\t\t\tEXPECT(\")\");\n\t\t\t\tEXPECT(\"(\");\n\t\t\t\tbuffer = COM_ParseOut(buffer, token, sizeof(token));\n\t\t\t\tqx = atof(token);\n\t\t\t\tbuffer = COM_ParseOut(buffer, token, sizeof(token));\n\t\t\t\tqy = atof(token);\n\t\t\t\tbuffer = COM_ParseOut(buffer, token, sizeof(token));\n\t\t\t\tqz = atof(token);\n\t\t\t\tEXPECT(\")\");\n\t\t\t\tGenMatrix(x, y, z, qx, qy, qz, posedata+i*12);\n\t\t\t}\n\t\t\tEXPECT(\"}\");\n\t\t}\n\t\telse if (!strcmp(token, \"mesh\"))\n\t\t{\n\t\t\tint numverts = 0;\n\t\t\tint numweights = 0;\n\t\t\tint numtris = 0;\n\n\t\t\tint num;\n\t\t\tint vnum;\n\n\t\t\tint numusableweights = 0;\n\t\t\tint *firstweightlist = NULL;\n\t\t\tint *numweightslist = NULL;\n\n\t\t\tgalisskeletaltransforms_t *trans;\n#ifdef HAVE_CLIENT\n\t\t\tfloat *stcoord = NULL;\n\t\t\tunsigned int numskins;\n#endif\n\t\t\tindex_t *indexes = NULL;\n\t\t\tfloat w;\n\n\t\t\tvec4_t *rawweight = NULL;\n\t\t\tint *rawweightbone = NULL;\n\n\n\t\t\tif (!nummeshes)\n\t\t\t\tMD5ERROR0PARAM(\"MD5MESH: mesh section before (or without) nummeshes\");\n\t\t\tif (!foundjoints || !bones || !pose)\n\t\t\t\tMD5ERROR0PARAM(\"MD5MESH: mesh must come after joints\");\n\n\t\t\tif (!lastsurf)\n\t\t\t{\n\t\t\t\tlastsurf = root;\n\t\t\t\tinf = root;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tinf = ZG_Malloc(&mod->memgroup, sizeof(*inf));\n\t\t\t\tinf->shares_verts = lastsurf->shares_verts+1;\n\t\t\t\tlastsurf->nextsurf = inf;\n\t\t\t\tlastsurf = inf;\n\t\t\t}\n\t\t\tMod_DefaultMesh(inf, COM_SkipPath(mod->name), 0);\n\n\t\t\tinf->shares_bones = 0;\n\t\t\tinf->ofsbones = bones;\n\t\t\tinf->numbones = numjoints;\n\t\t\tinf->numanimations = 1;\n\t\t\tinf->ofsanimations = pose;\n\t\t\tinf->baseframeofs = pose->boneofs;\n\n#ifdef HAVE_CLIENT\n\t\t\tskin = ZG_Malloc(&mod->memgroup, sizeof(*skin));\n\t\t\tframes = ZG_Malloc(&mod->memgroup, sizeof(*frames));\n\t\t\tinf->numskins = 1;\n\t\t\tinf->ofsskins = skin;\n\t\t\tskin->numframes = 1;\n\t\t\tskin->skinspeed = 1;\n\t\t\tskin->frame = frames;\n\t\t\tnumskins = 0;\n#endif\n\t\t\tEXPECT(\"{\");\n\t\t\tfor(;;)\n\t\t\t{\n\t\t\t\tbuffer = COM_ParseOut(buffer, token, sizeof(token));\n\t\t\t\tif (!buffer)\n\t\t\t\t\tMD5ERROR0PARAM(\"MD5MESH: unexpected eof\");\n\n\t\t\t\tif (!strcmp(token, \"shader\"))\n\t\t\t\t{\n\t\t\t\t\tbuffer = COM_ParseOut(buffer, token, sizeof(token));\n#ifdef HAVE_CLIENT\n\t\t\t\t\tif (!strchr(token, '/'))\n\t\t\t\t\t{\t//hack to try to deal with the rerelease using md5s for things that don't actually support skins properly.\n\t\t\t\t\t\tchar texbase[MAX_QPATH];\n\t\t\t\t\t\tchar texname[MAX_QPATH];\n\t\t\t\t\t\tQ_strncpyz(texbase, mod->name, sizeof(frames[0].shadername));\n\t\t\t\t\t\t*COM_SkipPath(texbase) = 0;\n\t\t\t\t\t\tQ_strncatz(texbase, token, sizeof(texbase));\n\t\t\t\t\t\tfor (numskins = 0; ; numskins++)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tQ_snprintfz(texname, sizeof(texname), \"%s_%02d_%02d.lmp\", texbase, numskins, 0);\n\t\t\t\t\t\t\tif (!COM_FCheckExists(texname))\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (numskins)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tskin = ZG_Malloc(&mod->memgroup, sizeof(*skin)*numskins);\n\t\t\t\t\t\t\tinf->numskins = numskins;\n\t\t\t\t\t\t\tinf->ofsskins = skin;\n\t\t\t\t\t\t\tfor (num = 0; num < numskins; num++, skin++)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tskin->skinspeed = 5;\t//match bsp anim speed.\n\t\t\t\t\t\t\t\tfor (skin->numframes = 1; ; skin->numframes++)\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tQ_snprintfz(texname, sizeof(texname), \"%s_%02d_%02d.lmp\", texbase, num, skin->numframes);\n\t\t\t\t\t\t\t\t\tif (!COM_FCheckExists(texname))\n\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tframes = ZG_Malloc(&mod->memgroup, sizeof(*frames)*skin->numframes);\n\t\t\t\t\t\t\t\tskin->frame = frames;\n\t\t\t\t\t\t\t\tfor (vnum = 0; vnum < skin->numframes; vnum++)\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tsize_t fsize=0, w, h;\n\t\t\t\t\t\t\t\t\tqbyte *img;\n\t\t\t\t\t\t\t\t\tQ_snprintfz(frames[vnum].shadername, sizeof(frames[vnum].shadername), \"%s_%02d_%02d.lmp\", texbase, num, vnum);\n\n\t\t\t\t\t\t\t\t\t//extra stuff to make sure we can colourmap the 8bit data without needing _upper etc images.\n\t\t\t\t\t\t\t\t\timg = FS_LoadMallocGroupFile(&mod->memgroup, frames[vnum].shadername, &fsize, false);\n\t\t\t\t\t\t\t\t\tif (img && fsize >= 8)\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tw = (img[0]<<0)|(img[1]<<8)|(img[2]<<16)|(img[3]<<24);\n\t\t\t\t\t\t\t\t\t\th = (img[4]<<0)|(img[5]<<8)|(img[6]<<16)|(img[7]<<24);\n\t\t\t\t\t\t\t\t\t\tif (fsize == 8+w*h && (vnum == 0 || (w==skin->skinwidth&&h==skin->skinheight)))\n\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\tskin->skinwidth = w;\n\t\t\t\t\t\t\t\t\t\t\tskin->skinheight = h;\n\t\t\t\t\t\t\t\t\t\t\tframes[vnum].texels = img+8;\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t\t\t\tZG_Free(&mod->memgroup, img);\t//something's screwy, don't leave the wasted memory lying around.\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n#ifdef MD2MODELS\n\t\t\t\t\tif (!numskins && strcmp(mod->publicname, mod->name))\n\t\t\t\t\t{\t//try grabbing the names from an original md2 file.\n\t\t\t\t\t\tsize_t sz;\n\t\t\t\t\t\tmd2_t *md2 = FS_LoadMallocFile(mod->publicname, &sz);\n\t\t\t\t\t\tif (md2)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (!memcmp(md2, MD2IDALIASHEADER) && md2->version == LittleLong(MD2ALIAS_VERSION))\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tchar *p;\n\t\t\t\t\t\t\t\tconst char *str = (const char*)md2 + LittleLong(md2->ofs_skins);\n\t\t\t\t\t\t\t\tnumskins = LittleLong(md2->num_skins);\n\t\t\t\t\t\t\t\tif (str + numskins*MD2MAX_SKINNAME>(char*)md2+sz)\n\t\t\t\t\t\t\t\t\tnumskins = 0;\n\n\t\t\t\t\t\t\t\tskin = ZG_Malloc(&mod->memgroup, sizeof(*skin)*numskins);\n\t\t\t\t\t\t\t\tinf->numskins = numskins;\n\t\t\t\t\t\t\t\tinf->ofsskins = skin;\n\t\t\t\t\t\t\t\tfor (num = 0; num < numskins; num++, skin++, str += MD2MAX_SKINNAME)\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tskin->skinspeed = 5;\t//match bsp anim speed.\n\t\t\t\t\t\t\t\t\tskin->numframes = 1;\n\n\t\t\t\t\t\t\t\t\tframes = ZG_Malloc(&mod->memgroup, sizeof(*frames)*skin->numframes);\n\t\t\t\t\t\t\t\t\tskin->frame = frames;\n\n\t\t\t\t\t\t\t\t\tp = COM_SkipPath(mod->publicname);\n\t\t\t\t\t\t\t\t\tif (!strncmp(str, mod->publicname, p-mod->publicname))\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tQ_snprintfz(frames[0].shadername, sizeof(frames[0].shadername), \"%s\", mod->name);\n\t\t\t\t\t\t\t\t\t\t*COM_SkipPath(frames[0].shadername) = 0;\n\t\t\t\t\t\t\t\t\t\tQ_strncatz(frames[0].shadername, str+(p-mod->publicname), sizeof(frames[0].shadername));\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t\t\tQ_snprintfz(frames[0].shadername, sizeof(frames[0].shadername), \"%s\", str);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tFS_FreeFile(md2);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n#endif\n\t\t\t\t\tif (!numskins)\n\t\t\t\t\t\t//FIXME: we probably want to support multiple skins some time\n\t\t\t\t\t\tQ_strncpyz(frames[0].shadername, token, sizeof(frames[0].shadername));\n#endif\n\t\t\t\t}\n\t\t\t\telse if (!strcmp(token, \"numverts\"))\n\t\t\t\t{\n\t\t\t\t\tif (numverts)\n\t\t\t\t\t\tMD5ERROR0PARAM(\"MD5MESH: numverts was already specified\");\n\t\t\t\t\tbuffer = COM_ParseOut(buffer, token, sizeof(token));\n\t\t\t\t\tnumverts = atoi(token);\n\t\t\t\t\tif (numverts < 0)\n\t\t\t\t\t\tMD5ERROR0PARAM(\"MD5MESH: numverts cannot be negative\");\n\n\t\t\t\t\tfirstweightlist = Z_Malloc(sizeof(*firstweightlist) * numverts);\n\t\t\t\t\tnumweightslist = Z_Malloc(sizeof(*numweightslist) * numverts);\n#ifdef HAVE_CLIENT\n\t\t\t\t\tstcoord = ZG_Malloc(&mod->memgroup, sizeof(float)*2*numverts);\n\t\t\t\t\tinf->ofs_st_array = (vec2_t*)stcoord;\n\t\t\t\t\tinf->numverts = numverts;\n#endif\n\t\t\t\t}\n\t\t\t\telse if (!strcmp(token, \"vert\"))\n\t\t\t\t{\t//vert num ( s t ) firstweight numweights\n\n\t\t\t\t\tbuffer = COM_ParseOut(buffer, token, sizeof(token));\n\t\t\t\t\tnum = atoi(token);\n\t\t\t\t\tif (num < 0 || num >= numverts)\n\t\t\t\t\t\tMD5ERROR0PARAM(\"MD5MESH: vertex out of range\");\n\n\t\t\t\t\tEXPECT(\"(\");\n\t\t\t\t\tbuffer = COM_ParseOut(buffer, token, sizeof(token));\n#ifdef HAVE_CLIENT\n\t\t\t\t\tif (!stcoord)\n\t\t\t\t\t\tMD5ERROR0PARAM(\"MD5MESH: vertex out of range\");\n\t\t\t\t\tstcoord[num*2+0] = atof(token);\n#endif\n\t\t\t\t\tbuffer = COM_ParseOut(buffer, token, sizeof(token));\n#ifdef HAVE_CLIENT\n\t\t\t\t\tstcoord[num*2+1] = atof(token);\n#endif\n\t\t\t\t\tEXPECT(\")\");\n\t\t\t\t\tbuffer = COM_ParseOut(buffer, token, sizeof(token));\n\t\t\t\t\tfirstweightlist[num] = atoi(token);\n\t\t\t\t\tbuffer = COM_ParseOut(buffer, token, sizeof(token));\n\t\t\t\t\tnumweightslist[num] = atoi(token);\n\n\t\t\t\t\tnumusableweights += numweightslist[num];\n\t\t\t\t}\n\t\t\t\telse if (!strcmp(token, \"numtris\"))\n\t\t\t\t{\n\t\t\t\t\tif (numtris)\n\t\t\t\t\t\tMD5ERROR0PARAM(\"MD5MESH: numtris was already specified\");\n\t\t\t\t\tbuffer = COM_ParseOut(buffer, token, sizeof(token));\n\t\t\t\t\tnumtris = atoi(token);\n\t\t\t\t\tif (numtris < 0)\n\t\t\t\t\t\tMD5ERROR0PARAM(\"MD5MESH: numverts cannot be negative\");\n\n\t\t\t\t\tindexes = ZG_Malloc(&mod->memgroup, sizeof(int)*3*numtris);\n\t\t\t\t\tinf->ofs_indexes = indexes;\n\t\t\t\t\tinf->numindexes = numtris*3;\n\t\t\t\t}\n\t\t\t\telse if (!strcmp(token, \"tri\"))\n\t\t\t\t{\n\t\t\t\t\tbuffer = COM_ParseOut(buffer, token, sizeof(token));\n\t\t\t\t\tnum = atoi(token);\n\t\t\t\t\tif (num < 0 || num >= numtris)\n\t\t\t\t\t\tMD5ERROR0PARAM(\"MD5MESH: vertex out of range\");\n\n\t\t\t\t\tbuffer = COM_ParseOut(buffer, token, sizeof(token));\n\t\t\t\t\tindexes[num*3+0] = atoi(token);\n\t\t\t\t\tbuffer = COM_ParseOut(buffer, token, sizeof(token));\n\t\t\t\t\tindexes[num*3+1] = atoi(token);\n\t\t\t\t\tbuffer = COM_ParseOut(buffer, token, sizeof(token));\n\t\t\t\t\tindexes[num*3+2] = atoi(token);\n\t\t\t\t}\n\t\t\t\telse if (!strcmp(token, \"numweights\"))\n\t\t\t\t{\n\t\t\t\t\tif (numweights)\n\t\t\t\t\t\tMD5ERROR0PARAM(\"MD5MESH: numweights was already specified\");\n\t\t\t\t\tbuffer = COM_ParseOut(buffer, token, sizeof(token));\n\t\t\t\t\tnumweights = atoi(token);\n\n\t\t\t\t\trawweight = Z_Malloc(sizeof(*rawweight)*numweights);\n\t\t\t\t\trawweightbone = Z_Malloc(sizeof(*rawweightbone)*numweights);\n\t\t\t\t}\n\t\t\t\telse if (!strcmp(token, \"weight\"))\n\t\t\t\t{\n\t\t\t\t\t//weight num bone scale ( x y z )\n\t\t\t\t\tbuffer = COM_ParseOut(buffer, token, sizeof(token));\n\t\t\t\t\tnum = atoi(token);\n\t\t\t\t\tif (num < 0 || num >= numweights)\n\t\t\t\t\t\tMD5ERROR0PARAM(\"MD5MESH: weight out of range\");\n\n\t\t\t\t\tbuffer = COM_ParseOut(buffer, token, sizeof(token));\n\t\t\t\t\trawweightbone[num] = atoi(token);\n\t\t\t\t\tif (rawweightbone[num] < 0 || rawweightbone[num] >= numjoints)\n\t\t\t\t\t\tMD5ERROR0PARAM(\"MD5MESH: weight specifies bad bone\");\n\t\t\t\t\tbuffer = COM_ParseOut(buffer, token, sizeof(token));\n\t\t\t\t\tw = atof(token);\n\n\t\t\t\t\tEXPECT(\"(\");\n\t\t\t\t\tbuffer = COM_ParseOut(buffer, token, sizeof(token));\n\t\t\t\t\trawweight[num][0] = w*atof(token);\n\t\t\t\t\tbuffer = COM_ParseOut(buffer, token, sizeof(token));\n\t\t\t\t\trawweight[num][1] = w*atof(token);\n\t\t\t\t\tbuffer = COM_ParseOut(buffer, token, sizeof(token));\n\t\t\t\t\trawweight[num][2] = w*atof(token);\n\t\t\t\t\tEXPECT(\")\");\n\t\t\t\t\trawweight[num][3] = w;\n\t\t\t\t}\n\t\t\t\telse if (!strcmp(token, \"}\"))\n\t\t\t\t\tbreak;\n\t\t\t\telse\n\t\t\t\t\tMD5ERROR1PARAM(\"MD5MESH: Unrecognised token inside mesh (%s)\", token);\n\n\t\t\t}\n\n\t\t\ttrans = Z_Malloc(sizeof(*trans)*numusableweights);\n\n\t\t\tfor (num = 0, vnum = 0; num < numverts; num++)\n\t\t\t{\n\t\t\t\tif (numweightslist[num] <= 0)\n\t\t\t\t\tMD5ERROR0PARAM(\"MD5MESH: weights not set on vertex\");\n\t\t\t\twhile(numweightslist[num])\n\t\t\t\t{\n\t\t\t\t\ttrans[vnum].vertexindex = num;\n\t\t\t\t\ttrans[vnum].boneindex = rawweightbone[firstweightlist[num]];\n\t\t\t\t\ttrans[vnum].org[0] = rawweight[firstweightlist[num]][0];\n\t\t\t\t\ttrans[vnum].org[1] = rawweight[firstweightlist[num]][1];\n\t\t\t\t\ttrans[vnum].org[2] = rawweight[firstweightlist[num]][2];\n\t\t\t\t\ttrans[vnum].org[3] = rawweight[firstweightlist[num]][3];\n\t\t\t\t\tvnum++;\n\t\t\t\t\tfirstweightlist[num]++;\n\t\t\t\t\tnumweightslist[num]--;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tAlias_BuildGPUWeights(mod, inf, vnum, trans, true);\n\t\t\tZ_Free(trans);\n\n\n\t\t\tfor (i = 0; i < inf->numverts; i++)\n\t\t\t\tAddPointToBounds(inf->ofs_skel_xyz[i], mod->mins, mod->maxs);\n\n\t\t\tif (firstweightlist)\n\t\t\t\tZ_Free(firstweightlist);\n\t\t\tif (numweightslist)\n\t\t\t\tZ_Free(numweightslist);\n\t\t\tif (rawweight)\n\t\t\t\tZ_Free(rawweight);\n\t\t\tif (rawweightbone)\n\t\t\t\tZ_Free(rawweightbone);\n\t\t}\n\t\telse\n\t\t\tMD5ERROR1PARAM(\"Unrecognised token in MD5 model (%s)\", token);\n\t}\n\n\tif (!lastsurf)\n\t\tMD5ERROR0PARAM(\"MD5MESH: No meshes\");\n\n//\tAlias_CalculateSkeletalNormals(root);\n\n\treturn root;\n#undef MD5ERROR0PARAM\n#undef MD5ERROR1PARAM\n#undef EXPECT\n}\n\nstatic qboolean QDECL Mod_LoadMD5MeshModel(model_t *mod, void *buffer, size_t fsize)\n{\n\tgaliasinfo_t *root;\n\tchar animname[MAX_QPATH];\n\tvoid *animfile;\n\n\troot = Mod_ParseMD5MeshModel(mod, buffer, mod->name);\n\tif (root == NULL)\n\t{\n\t\treturn false;\n\t}\n\n\tif (mod_md5_singleanimation.ival && root->numanimations==1 && root->ofsanimations[0].skeltype==SKEL_ABSOLUTE)\t//make sure there's only the base pose...\n\t{\n\t\tCOM_StripAllExtensions(mod->name, animname, sizeof(animname));\n\t\tQ_strncatz(animname, \".md5anim\", sizeof(animname));\n\t\tanimfile = FS_LoadMallocFile(animname, NULL);\n\t\tif (animfile)\t//FIXME: make non fatal somehow..\n\t\t{\n\t\t\tgaliasinfo_t *surf;\n\t\t\tvoid *np = NULL;\n\t\t\tgaliasanimation_t ng;\n\t\t\tif (Mod_ParseMD5Anim(mod, animfile, root, &np, &ng) && ng.numposes>0)\n\t\t\t{\n\t\t\t\tgaliasanimation_t *a;\n\t\t\t\tint i;\n\t\t\t\troot->numanimations = ng.numposes;\n\t\t\t\troot->ofsanimations = ZG_Malloc(&mod->memgroup, ng.numposes*sizeof(*root->ofsanimations));\n\n\t\t\t\t//pull out each frame individually\n\t\t\t\tfor (i = 0; i < ng.numposes; i++)\n\t\t\t\t{\n\t\t\t\t\ta = &root->ofsanimations[i];\n\t\t\t\t\ta->skeltype = ng.skeltype;\n\t\t\t\t\ta->loop = false;\n\t\t\t\t\ta->numposes = 1;\n\t\t\t\t\ta->rate = 10;\n\t\t\t\t\ta->boneofs = (float*)np + i*12*root->numbones;\n\t\t\t\t\tQ_snprintfz(a->name, sizeof(a->name), \"%s_%i\", animname, i);\n\t\t\t\t}\n\n\t\t\t\tfor(surf = root;(surf = surf->nextsurf);)\n\t\t\t\t{\n\t\t\t\t\tsurf->ofsanimations = root->ofsanimations;\n\t\t\t\t\tsurf->numanimations = root->numanimations;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tZ_Free(animfile);\n\t\t}\n\t}\n\n\tmod->flags = Mod_ReadFlagsFromMD1(mod->name, 0);\t//file replacement - inherit flags from any defunc mdl files.\n\n\tMod_ClampModelSize(mod);\n\tMod_ParseModelEvents(mod, root->ofsanimations, root->numanimations);\n\n\tmod->type = mod_alias;\n\tmod->numframes = root->numanimations;\n\tmod->meshinfo = root;\n\n\tmod->funcs.NativeTrace = Mod_Trace;\n\treturn true;\n}\n\n/*\nEXTERNALANIM\n\n//File that specifies md5 model/anim stuff.\n\nmodel test/imp.md5mesh\n\ngroup test/idle1.md5anim\nclampgroup test/idle1.md5anim\nframes test/idle1.md5anim\n\n*/\nstatic qboolean QDECL Mod_LoadCompositeAnim(model_t *mod, void *buffer, size_t fsize)\n{\n\tint i;\n\n\tchar *file;\n\tgaliasinfo_t *root = NULL, *surf;\n\tint numgroups = 0;\n\tgaliasanimation_t *grouplist = NULL;\n\tgaliasanimation_t *newgroup = NULL;\n\tfloat **poseofs;\n\tchar com_token[8192];\n\n\tbuffer = COM_Parse(buffer);\n\tif (strcmp(com_token, \"EXTERNALANIM\"))\n\t{\n\t\tCon_Printf (CON_ERROR \"EXTERNALANIM: header is not compleate (%s)\\n\", mod->name);\n\t\treturn false;\n\t}\n\n\tbuffer = COM_Parse(buffer);\n\tif (!strcmp(com_token, \"model\"))\n\t{\n\t\tbuffer = COM_Parse(buffer);\n\t\tfile = COM_LoadTempMoreFile(com_token, NULL);\n\n\t\tif (!file)\t//FIXME: make non fatal somehow..\n\t\t{\n\t\t\tCon_Printf(CON_ERROR \"Couldn't open %s (from %s)\\n\", com_token, mod->name);\n\t\t\treturn false;\n\t\t}\n\n\t\troot = Mod_ParseMD5MeshModel(mod, file, mod->name);\n\t\tif (root == NULL)\n\t\t{\n\t\t\treturn false;\n\t\t}\n\t\tnewgroup = root->ofsanimations;\n\n\t\tgrouplist = BZ_Malloc(sizeof(galiasanimation_t)*(numgroups+root->numanimations));\n\t\tmemcpy(grouplist, newgroup, sizeof(galiasanimation_t)*(numgroups+root->numanimations));\n\t\tposeofs = BZ_Malloc(sizeof(galiasanimation_t)*(numgroups+root->numanimations));\n\t\tfor (i = 0; i < root->numanimations; i++)\n\t\t{\n\t\t\tgrouplist[numgroups] = newgroup[i];\n\t\t\tposeofs[numgroups] = newgroup[i].boneofs;\n\t\t\tnumgroups++;\n\t\t}\n\t}\n\telse\n\t{\n\t\tCon_Printf (CON_ERROR \"EXTERNALANIM: model must be defined immediatly after the header\\n\");\n\t\treturn false;\n\t}\n\n\tfor (;;)\n\t{\n\t\tbuffer = COM_Parse(buffer);\n\t\tif (!buffer)\n\t\t\tbreak;\n\n\t\tif (!strcmp(com_token, \"group\"))\n\t\t{\n\t\t\tgrouplist = BZ_Realloc(grouplist, sizeof(galiasanimation_t)*(numgroups+1));\n\t\t\tposeofs = BZ_Realloc(poseofs, sizeof(*poseofs)*(numgroups+1));\n\t\t\tbuffer = COM_Parse(buffer);\n\t\t\tfile = COM_LoadTempMoreFile(com_token, NULL);\n\t\t\tif (file)\t//FIXME: make non fatal somehow..\n\t\t\t{\n\t\t\t\tchar namebkup[MAX_QPATH];\n\t\t\t\tQ_strncpyz(namebkup, com_token, sizeof(namebkup));\n\t\t\t\tif (!Mod_ParseMD5Anim(mod, file, root, (void**)&poseofs[numgroups], &grouplist[numgroups]))\n\t\t\t\t{\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\tQ_strncpyz(grouplist[numgroups].name, namebkup, sizeof(grouplist[numgroups].name));\n\t\t\t\tnumgroups++;\n\t\t\t}\n\t\t}\n\t\telse if (!strcmp(com_token, \"clampgroup\"))\n\t\t{\n\t\t\tgrouplist = BZ_Realloc(grouplist, sizeof(galiasanimation_t)*(numgroups+1));\n\t\t\tposeofs = BZ_Realloc(poseofs, sizeof(*poseofs)*(numgroups+1));\n\t\t\tbuffer = COM_Parse(buffer);\n\t\t\tfile = COM_LoadTempMoreFile(com_token, NULL);\n\t\t\tif (file)\t//FIXME: make non fatal somehow..\n\t\t\t{\n\t\t\t\tchar namebkup[MAX_QPATH];\n\t\t\t\tQ_strncpyz(namebkup, com_token, sizeof(namebkup));\n\t\t\t\tif (!Mod_ParseMD5Anim(mod, file, root, (void**)&poseofs[numgroups], &grouplist[numgroups]))\n\t\t\t\t{\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\tQ_strncpyz(grouplist[numgroups].name, namebkup, sizeof(grouplist[numgroups].name));\n\t\t\t\tgrouplist[numgroups].loop = false;\n\t\t\t\tnumgroups++;\n\t\t\t}\n\t\t}\n\t\telse if (!strcmp(com_token, \"frames\"))\n\t\t{\n\t\t\tgaliasanimation_t ng;\n\t\t\tvoid *np;\n\n\t\t\tbuffer = COM_Parse(buffer);\n\t\t\tfile = COM_LoadTempMoreFile(com_token, NULL);\n\t\t\tif (file)\t//FIXME: make non fatal somehow..\n\t\t\t{\n\t\t\t\tchar namebkup[MAX_QPATH];\n\t\t\t\tQ_strncpyz(namebkup, com_token, sizeof(namebkup));\n\t\t\t\tif (!Mod_ParseMD5Anim(mod, file, root, &np, &ng))\n\t\t\t\t{\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\n\t\t\t\tgrouplist = BZ_Realloc(grouplist, sizeof(galiasanimation_t)*(numgroups+ng.numposes));\n\t\t\t\tposeofs = BZ_Realloc(poseofs, sizeof(*poseofs)*(numgroups+ng.numposes));\n\n\t\t\t\t//pull out each frame individually\n\t\t\t\tfor (i = 0; i < ng.numposes; i++)\n\t\t\t\t{\n\t\t\t\t\tgrouplist[numgroups].skeltype = ng.skeltype;\n\t\t\t\t\tgrouplist[numgroups].loop = false;\n\t\t\t\t\tgrouplist[numgroups].numposes = 1;\n\t\t\t\t\tgrouplist[numgroups].rate = 24;\n\t\t\t\t\tposeofs[numgroups] = (float*)np + i*12*root->numbones;\n\t\t\t\t\tQ_snprintfz(grouplist[numgroups].name, sizeof(grouplist[numgroups].name), \"%s%i\", namebkup, i);\n\t\t\t\t\tQ_strncpyz(grouplist[numgroups].name, namebkup, sizeof(grouplist[numgroups].name));\n\t\t\t\t\tgrouplist[numgroups].loop = false;\n\t\t\t\t\tnumgroups++;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tCon_Printf(CON_ERROR \"EXTERNALANIM: unrecognised token (%s)\\n\", mod->name);\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tnewgroup = grouplist;\n\tgrouplist = ZG_Malloc(&mod->memgroup, sizeof(galiasanimation_t)*numgroups);\n\tfor(surf = root;;)\n\t{\n\t\tsurf->ofsanimations = grouplist;\n\t\tsurf->numanimations = numgroups;\n\t\tif (!surf->nextsurf)\n\t\t\tbreak;\n\t\tsurf = surf->nextsurf;\n\t}\n\tfor (i = 0; i < numgroups; i++)\n\t{\n\t\tgrouplist[i] = newgroup[i];\n\t\tgrouplist[i].boneofs = poseofs[i];\n\t}\n\n\tmod->flags = Mod_ReadFlagsFromMD1(mod->name, 0);\t//file replacement - inherit flags from any defunc mdl files.\n\n\tMod_ClampModelSize(mod);\n\tMod_ParseModelEvents(mod, root->ofsanimations, root->numanimations);\n\n\tmod->type = mod_alias;\n\tmod->numframes = root->numanimations;\n\tmod->meshinfo = root;\n\n\tmod->funcs.NativeTrace = Mod_Trace;\n\treturn true;\n}\n\n#endif //MD5MODELS\n\n#ifdef MODELFMT_OBJ\n#include <ctype.h>\nstruct objvert { size_t attrib[3]; };\n\nstruct objbuf_s { char *ptr; char *end; };\nstatic char *obj_getline(struct objbuf_s *vf, char *buffer, size_t buflen)\n{\n\tchar in;\n\tchar *out = buffer;\n\tsize_t len;\n\tif (buflen <= 1)\n\t\treturn NULL;\n\tlen = buflen-1;\n\twhile (len > 0)\n\t{\n\t\tif (vf->ptr == vf->end)\n\t\t{\n\t\t\tif (len == buflen-1)\n\t\t\t\treturn NULL;\n\t\t\t*out = '\\0';\n\t\t\treturn buffer;\n\t\t}\n\t\tin = *vf->ptr++;\n\t\tif (in == '\\n')\n\t\t\tbreak;\n\t\t*out++ = in;\n\t\tlen--;\n\t}\n\t*out = '\\0';\n\n\t//if there's a trailing \\r, strip it.\n\tif (out > buffer)\n\t\tif (out[-1] == '\\r')\n\t\t\tout[-1] = 0;\n\n\treturn buffer;\n}\n\nstruct objattrib_s {\n\tsize_t length;\n\tsize_t maxlength;\n\tfloat *data;\n};\nstatic qboolean parseobjvert(char *s, struct objattrib_s *out, int reorient)\n{\n\tint i;\n\tfloat *v;\n\tchar *n;\n\tif (out->length == out->maxlength)\n\t\tZ_ReallocElements((void**)&out->data,&out->maxlength,out->length+1024,3*sizeof(float));\n\tv = out->data+out->length*3;\n\tout->length++;\n\twhile(isalpha(*s)) s++;\n\tfor (i = 0; i < 3;)\n\t{\n\t\tv[i] = strtod(s, &n);\n\t\tif (n==s)\n\t\t\treturn false;\n\t\ts = n;\n\t\twhile(isspace(*s)) s++;\n\t\ti++;\n\t\tif(!*s) break;\n\t}\n\tfor (; i < 3; i++)\n\t\tv[i] = 0;\n\n\tif (reorient)\n\t{\n\t\tif (reorient == 1)\n\t\t{\t//xzy - DP's orientation.\n\t\t\tfloat z = v[2];\n\t\t\tv[2] = v[1];\n\t\t\tv[1] = z;\n\t\t}\n\t\telse\n\t\t{\t//zxy - match iqmtool (doesn't need negative scaling)\n\t\t\tfloat z = v[2];\n\t\t\tv[2] = v[1];\n\t\t\tv[1] = v[0];\n\t\t\tv[0] = z;\n\t\t}\n\t}\n\treturn true;\n}\n\nstatic galiasinfo_t *Obj_FinishFace(model_t *mod, galiasinfo_t *m, struct objattrib_s *attribs, struct objvert *vert, size_t numverts, index_t *indexes, size_t *numelements)\n{\t//this is really lame. not optimised and I don't care.\n\tqboolean calcnorms = false;\n\tsize_t i;\n\tif (m && numverts < MAX_INDICIES)\n\t{\n\t\tm->ofs_skel_xyz = ZG_Malloc(&mod->memgroup, sizeof(*m->ofs_skel_xyz)*numverts);\n\t\tm->ofs_st_array = ZG_Malloc(&mod->memgroup, sizeof(*m->ofs_st_array)*numverts);\n\t\tm->ofs_skel_norm = ZG_Malloc(&mod->memgroup, sizeof(*m->ofs_skel_norm)*numverts);\n\t\tm->ofs_skel_svect = ZG_Malloc(&mod->memgroup, sizeof(*m->ofs_skel_svect)*numverts);\n\t\tm->ofs_skel_tvect = ZG_Malloc(&mod->memgroup, sizeof(*m->ofs_skel_tvect)*numverts);\n\n\t\tfor (i = 0; i < numverts; i++)\n\t\t{\n\t\t\tif (vert[i].attrib[0] >= attribs[0].length)\n\t\t\t\tVectorClear(m->ofs_skel_xyz[i]);\n\t\t\telse\n\t\t\t\tVectorCopy(attribs[0].data+3*vert[i].attrib[0], m->ofs_skel_xyz[i]);\n\t\t\tAddPointToBounds(m->ofs_skel_xyz[i], mod->mins, mod->maxs);\n\n\t\t\tif (vert[i].attrib[1] >= attribs[1].length)\n\t\t\t\tVector2Clear(m->ofs_st_array[i]);\n\t\t\telse\n\t\t\t\tVector2Copy(attribs[1].data+3*vert[i].attrib[1], m->ofs_st_array[i]);\n\t\t\tm->ofs_st_array[i][1] = 1-m->ofs_st_array[i][1]; //flip y coords.\n\n\t\t\tif (vert[i].attrib[2] >= attribs[2].length)\n\t\t\t{\n\t\t\t\tVectorClear(m->ofs_skel_norm[i]);\n\t\t\t\tcalcnorms = true;\n\t\t\t}\n\t\t\telse\n\t\t\t\tVectorCopy(attribs[2].data+3*vert[i].attrib[2], m->ofs_skel_norm[i]);\n\t\t}\n\t\tm->numverts = i;\n\n\t\tm->ofs_indexes = ZG_Malloc(&mod->memgroup, sizeof(*m->ofs_indexes)**numelements);\n\t\tmemcpy(m->ofs_indexes, indexes, sizeof(*m->ofs_indexes)**numelements);\n\t\tm->numindexes = *numelements;\n\n\t\t//calc tangents.\n\t\tMod_AccumulateTextureVectors(m->ofs_skel_xyz, m->ofs_st_array, m->ofs_skel_norm, m->ofs_skel_svect, m->ofs_skel_tvect, m->ofs_indexes, m->numindexes, calcnorms);\n\t\tMod_NormaliseTextureVectors(m->ofs_skel_norm, m->ofs_skel_svect, m->ofs_skel_tvect, m->numverts, calcnorms);\n\n\t\t*numelements = 0;\n\t}\n\treturn NULL;\n}\n\nstatic qboolean QDECL Mod_LoadObjModel(model_t *mod, void *buffer, size_t fsize)\n{\n\tstruct objbuf_s f = {buffer, (qbyte*)buffer+fsize};\n\tstruct objattrib_s attrib[3] = {{0},{0},{0}};\n\tchar buf[512];\n\tchar *meshname = NULL, *matname = NULL;\n\tgaliasinfo_t *m = NULL, **link = (galiasinfo_t**)&mod->meshinfo;\n\n\tsize_t numverts = 0;\n\tsize_t maxverts = 0;\n\tstruct objvert *vert = NULL, defaultvert={{-1,-1,-2}};\n\n\tsize_t numelems = 0;\n\tsize_t maxelems = 0;\n\tindex_t *elem = NULL;\n\n\tqboolean badinput = false;\n\tint meshidx = 0;\n\tint reorient = mod_obj_orientation.ival;\n\n\tClearBounds(mod->mins, mod->maxs);\n\n\twhile(!badinput && obj_getline(&f, buf, sizeof(buf)))\n\t{\n\t\tchar *c = buf;\n\t\twhile(isspace(*c)) c++;\n\t\tswitch(*c)\n\t\t{\n\t\t\tcase '#': continue;\n\t\t\tcase 'v':\n\t\t\t\tif(isspace(c[1])) badinput |= !parseobjvert(c, &attrib[0], reorient);\n\t\t\t\telse if(c[1]=='t') badinput |= !parseobjvert(c, &attrib[1], false);\n\t\t\t\telse if(c[1]=='n') badinput |= !parseobjvert(c, &attrib[2], reorient);\n\t\t\t\tbreak;\n\t\t\tcase 'g':\n\t\t\t{\n\t\t\t\tchar *name;\n\t\t\t\tsize_t namelen;\n\t\t\t\twhile(isalpha(*c)) c++;\n\t\t\t\twhile(isspace(*c)) c++;\n\t\t\t\tname = c;\n\t\t\t\tnamelen = strlen(name);\n\t\t\t\twhile(namelen > 0 && isspace(name[namelen-1])) namelen--;\n\t\t\t\tZ_Free(meshname);\n\t\t\t\tmeshname = Z_Malloc(namelen+1);\n\t\t\t\tmemcpy(meshname, name, namelen);\n\t\t\t\tmeshname[namelen] = 0;\n\n\t\t\t\tm = Obj_FinishFace(mod, m, attrib, vert, numverts, elem, &numelems);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase 'u':\n\t\t\t{\n\t\t\t\tchar *name;\n\t\t\t\tsize_t namelen;\n\t\t\t\tif(strncmp(c, \"usemtl\", 6)) continue;\n\t\t\t\twhile(isalpha(*c)) c++;\n\t\t\t\twhile(isspace(*c)) c++;\n\t\t\t\tname = c;\n\t\t\t\tnamelen = strlen(name);\n\t\t\t\twhile(namelen > 0 && isspace(name[namelen-1])) namelen--;\n\n\t\t\t\tif (!matname || strncmp(matname, name, namelen)||matname[namelen])\n\t\t\t\t{\n\t\t\t\t\tZ_Free(matname);\n\t\t\t\t\tmatname = Z_Malloc(namelen+1);\n\t\t\t\t\tmemcpy(matname, name, namelen);\n\t\t\t\t\tmatname[namelen] = 0;\n\n\t\t\t\t\tm = Obj_FinishFace(mod, m, attrib, vert, numverts, elem, &numelems);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase 's':\n\t\t\t{\n\t\t\t\tif(!isspace(c[1])) continue;\n\t\t\t\twhile(isalpha(*c)) c++;\n\t\t\t\twhile(isspace(*c)) c++;\n\n\t\t\t\t//make sure that these verts are not merged, ensuring that they get smoothed.\n\t\t\t\t//different texture coords will still have discontinuities though.\n\t\t\t\tdefaultvert.attrib[2] = -1-strtol(c, &c, 10);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase 'f':\n\t\t\t{\n\t\t\t\tsize_t i, v = 0;\n\t\t\t\tstruct objvert vkey={0};\n\t\t\t\tindex_t first=0, prev=0, cur=0;\n\n\t\t\t\t//only generate a new mesh if something actually changed.\n\t\t\t\tif (!m)\n\t\t\t\t{\n#ifdef HAVE_CLIENT\n\t\t\t\t\tgaliasskin_t *skin;\n\t\t\t\t\tskinframe_t *sframe;\n\t\t\t\t\tm = ZG_Malloc(&mod->memgroup, sizeof(*m)+sizeof(*skin)+sizeof(*sframe));\n#else\n\t\t\t\t\tm = ZG_Malloc(&mod->memgroup, sizeof(*m));\n#endif\n\t\t\t\t\t*link = m;\n\t\t\t\t\tlink = &m->nextsurf;\n\t\t\t\t\tm->shares_verts = meshidx;\n\t\t\t\t\tMod_DefaultMesh(m, COM_SkipPath(mod->name), meshidx++);\n\n\t\t\t\t\tQ_strncpyz(m->surfacename, meshname?meshname:\"\", sizeof(m->surfacename));\n\n#ifdef HAVE_CLIENT\n\t\t\t\t\tskin = (void*)(m+1);\n\t\t\t\t\tsframe = (void*)(skin+1);\n\t\t\t\t\tskin->frame = sframe;\n\t\t\t\t\tskin->numframes = 1;\n\t\t\t\t\tskin->skinspeed = 10;\n\t\t\t\t\tQ_strncpyz(skin->name, matname?matname:\"\", sizeof(skin->name));\n\t\t\t\t\tQ_strncpyz(sframe->shadername, matname?matname:\"\", sizeof(sframe->shadername));\n\t\t\t\t\tsframe->shader = NULL;\n\n\t\t\t\t\tm->ofsskins = skin;\n\t\t\t\t\tm->numskins = 1;\n#endif\n\t\t\t\t}\n\n\t\t\t\twhile(isalpha(*c)) c++;\n\t\t\t\tfor(;;)\n\t\t\t\t{\n\t\t\t\t\twhile(isspace(*c)) c++;\n\t\t\t\t\tif(!*c) break;\n\n\t\t\t\t\tfor (i = 0; i < countof(vkey.attrib); )\n\t\t\t\t\t{\n\t\t\t\t\t\tchar *n;\n\t\t\t\t\t\tlong v;\n\t\t\t\t\t\tv = strtol(c, &n, 10);\n\t\t\t\t\t\tif (c == n) {badinput = true; break;} //not a number if we read nothing!\n\t\t\t\t\t\tif (v < 0)\n\t\t\t\t\t\t\tvkey.attrib[i] = attrib[i].length + v;\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tvkey.attrib[i] = v - 1; //0 is index-not-specified.\n\t\t\t\t\t\ti++;\n\t\t\t\t\t\tc = n;\n\t\t\t\t\t\tif(*c!='/') break;\n\t\t\t\t\t\tc++;\n\t\t\t\t\t}\n\t\t\t\t\tfor (; i < countof(vkey.attrib); i++)\n\t\t\t\t\t\tvkey.attrib[i] = defaultvert.attrib[i];\n\n\t\t\t\t\t//figure out the verts, to avoid dupes\n\t\t\t\t\tfor (cur = 0; cur < numverts; cur++)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (vert[cur].attrib[0] == vkey.attrib[0] &&\n\t\t\t\t\t\t\tvert[cur].attrib[1] == vkey.attrib[1] &&\n\t\t\t\t\t\t\tvert[cur].attrib[2] == vkey.attrib[2] && vkey.attrib[2]!=-1)\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tif (cur == numverts)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (numverts == maxverts)\n\t\t\t\t\t\t\tZ_ReallocElements((void**)&vert,&maxverts,numverts+1024,sizeof(*vert));\n\t\t\t\t\t\tvert[numverts++] = vkey;\n\t\t\t\t\t}\n\n\t\t\t\t\t//spew out the trifan\n\t\t\t\t\tif (v == 0)\n\t\t\t\t\t\tfirst = cur;\n\t\t\t\t\telse if (v > 1)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (numelems+3 >= maxelems)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (numelems >= 65535)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tbadinput = true;\n\t\t\t\t\t\t\t\tbreak;\t//don't depend upon the OOM killer... it kills everything else too\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tZ_ReallocElements((void**)&elem,&maxelems,numelems+1024,sizeof(*elem));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (reorient == 1)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\telem[numelems++] = first;\n\t\t\t\t\t\t\telem[numelems++] = prev;\n\t\t\t\t\t\t\telem[numelems++] = cur;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\telem[numelems++] = cur;\n\t\t\t\t\t\t\telem[numelems++] = prev;\n\t\t\t\t\t\t\telem[numelems++] = first;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tprev = cur;\n\t\t\t\t\tv++;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\tm = Obj_FinishFace(mod, m, attrib, vert, numverts, elem, &numelems);\n\n\tZ_Free(vert);\n\tZ_Free(elem);\n\tZ_Free(attrib[0].data);\n\tZ_Free(attrib[1].data);\n\tZ_Free(attrib[2].data);\n\n\tif (badinput)\n\t{\t//fail the load.\n\t\tCon_Printf(CON_WARNING \"File \\\"%s\\\" with .obj extension does not appear to be an .obj file\\n\", mod->name);\n\t\tmod->meshinfo = NULL;\n\t\treturn false;\n\t}\n\n\tMod_ClampModelSize(mod);\n\tMod_ParseModelEvents(mod, NULL, 0);\n\tmod->flags = 0;\n\tmod->type = mod_alias;\n\tmod->numframes = 0;\n\tMod_SetMeshModelFuncs(mod, true);\n\n//\tCon_Printf(CON_WARNING \"%s: multi-surface obj files are unoptimised\\n\", mod->name);\n\treturn !!mod->meshinfo;\n}\n#endif\n\n\nvoid Alias_Register(void)\n{\n#ifdef MD1MODELS\n#ifndef SERVERONLY\n\tCvar_Register(&dpcompat_nofloodfill, NULL);\n#endif\n\tMod_RegisterModelFormatMagic(NULL, \"Quake1 Model (mdl)\",\t\t\t\tIDPOLYHEADER,\t\t\t\t\t\t\tMod_LoadQ1Model);\n\tMod_RegisterModelFormatMagic(NULL, \"QuakeForge 16bit Model\",\t\t\t\"MD16\",4,\t\t\t\t\t\t\t\tMod_LoadQ1Model);\n#ifdef HEXEN2\n\tMod_RegisterModelFormatMagic(NULL, \"Hexen2 Model (mdl)\",\t\t\t\tRAPOLYHEADER,\t\t\t\t\t\t\tMod_LoadQ1Model);\n#endif\n#endif\n#ifdef MD2MODELS\n\tMod_RegisterModelFormatMagic(NULL, \"Quake2 Model (md2)\",\t\t\t\tMD2IDALIASHEADER,\t\t\t\t\t\tMod_LoadQ2Model);\n#endif\n#ifdef MODELFMT_MDX\n\tMod_RegisterModelFormatMagic(NULL, \"Kingpin Model (mdx)\",\t\t\t\tMDX_IDENT,\t\t\t\t\t\t\t\tMod_LoadKingpinModel);\n#endif\n#ifdef MD3MODELS\n\tMod_RegisterModelFormatMagic(NULL, \"Quake3 Model (md3)\",\t\t\t\tMD3_IDENT,\t\t\t\t\t\t\t\tMod_LoadQ3Model);\n#endif\n#ifdef HALFLIFEMODELS\n\tMod_RegisterModelFormatMagic(NULL, \"Half-Life Model (mdl)\",\t\t\t\t\"IDST\\x0a\\0\\0\\0\",8,\t\t\t\t\t\tMod_LoadHLModel);\n#endif\n\n#ifdef ZYMOTICMODELS\n\tMod_RegisterModelFormatMagic(NULL, \"Zymotic Model (zym)\",\t\t\t\t\"ZYMOTICMODEL\",12,\t\t\t\t\t\tMod_LoadZymoticModel);\n#endif\n#ifdef DPMMODELS\n\tMod_RegisterModelFormatMagic(NULL, \"DarkPlaces Model (dpm)\",\t\t\t\"DARKPLACESMODEL\\0\",16,\t\t\t\t\tMod_LoadDarkPlacesModel);\n#endif\n#ifdef PSKMODELS\n\tMod_RegisterModelFormatMagic(NULL, \"Unreal Interchange Model (psk)\",\t\"ACTR\",4,\t\t\t\t\t\t\t\tMod_LoadPSKModel);\n#endif\n#ifdef INTERQUAKEMODELS\n\tMod_RegisterModelFormatMagic(NULL, \"Inter-Quake Model (iqm)\",\t\t\t\"INTERQUAKEMODEL\\0\",16,\t\t\t\t\tMod_LoadInterQuakeModel);\n#endif\n#ifdef MD5MODELS\n\tCvar_Register(&mod_md5_singleanimation, NULL);\n\tMod_RegisterModelFormatText(NULL, \"MD5 Mesh/Anim (md5mesh)\",\t\t\t\"MD5Version\",\t\t\t\t\t\t\tMod_LoadMD5MeshModel);\n\tMod_RegisterModelFormatText(NULL, \"External Anim\",\t\t\t\t\t\t\"EXTERNALANIM\",\t\t\t\t\t\t\tMod_LoadCompositeAnim);\n#endif\n\n\n#ifdef MODELFMT_OBJ\n\tMod_RegisterModelFormatText(NULL, \"Wavefront Object (obj)\",\t\t\t\t\".obj\",\t\t\t\t\t\t\t\t\tMod_LoadObjModel);\n\tCvar_Register(&mod_obj_orientation, NULL);\n#endif\n\n#ifndef SERVERONLY\n\tCvar_Register(&dpcompat_skinfiles, NULL);\n#endif\n}\n"
  },
  {
    "path": "engine/common/com_mesh.h",
    "content": "#ifndef COM_MESH_H\n#define COM_MESH_H\n\n#include \"quakedef.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include \"hash.h\"\n#include \"shader.h\"\n\n#ifdef SKELETALMODELS\n#include <stdlib.h>\n#endif\n\n#ifdef HALFLIFEMODELS\n#include \"model_hl.h\"\n#endif\n\nstruct galiasinfo_s; //per-surface info.\n\n#ifdef NONSKELETALMODELS\n//a single pose within an animation (note: always refered to via a framegroup, even if there's only one frame in that group).\ntypedef struct\n{\n\tvecV_t *ofsverts;\n#ifndef SERVERONLY\n\tvec3_t *ofsnormals;\n\tvec3_t *ofstvector;\n\tvec3_t *ofssvector;\n\n\tvboarray_t vboverts;\n\tvboarray_t vbonormals;\n\tvboarray_t vbosvector;\n\tvboarray_t vbotvector;\n#endif\n\n\tvec3_t\t\tscale;\n\tvec3_t\t\tscale_origin;\n} galiaspose_t;\n#endif\n\ntypedef struct galiasevent_s\n{\n\tstruct galiasevent_s *next;\n\tfloat timestamp;\n\tint code;\n\tchar *data;\n} galiasevent_t;\n\ntypedef struct galiasrefpose_s\n{\n\tvec4_t quat;\n\tvec3_t org;\n\tvec3_t scale;\n} galiasrefpose_t;\n\n//a frame group (aka: animation)\ntypedef struct galiasanimation_s\n{\n#ifdef SKELETALMODELS\n\tskeltype_t skeltype;\t//for models with transforms, states that bones need to be transformed from their parent.\n\t\t\t\t\t\t\t//this is actually bad, and can result in bones shortening as they interpolate.\n\tfloat *(QDECL *GetRawBones)(const struct galiasinfo_s *mesh, const struct galiasanimation_s *a, float time, float *bonematrixstorage, const struct galiasbone_s *boneinf, int numbones);\n\tvoid *boneofs;\t//numposes*12*numbones\n#endif\n\tqboolean loop;\n\tint numposes;\n\t//float *poseendtime;\t//first starts at 0, anim duration is poseendtime[numposes-1]\n\tfloat rate;\t\t\t\t//average framerate of animation.\n\tint action;\t\t\t\t//-1 for none.\n\tfloat actionweight;\n#ifdef NONSKELETALMODELS\n\tgaliaspose_t *poseofs;\n#endif\n\tgaliasevent_t *events;\n\tchar name[64];\n} galiasanimation_t;\n\ntypedef struct galiasbone_s galiasbone_t;\n#ifdef SKELETALMODELS\nstruct galiasbone_s\n{\n\tchar name[64];\n#if MAX_BONES>32767\n\tint parent;\n#else\n\tshort parent;\n#endif\n\tunsigned char group;\n//\tfloat radius;\n\tfloat inverse[12];\n\tgaliasrefpose_t ref;\n};\n\ntypedef struct\n{\n\t//should be load-time only\n\t//use of this prevents the use of glsl acceleration. the framerate loss is of the order of 90%\n\t//skeletal poses refer to this.\n\tint vertexindex;\n\tint boneindex;\n\tvec4_t org;\n#ifndef SERVERONLY\n\tvec3_t normal;\n#endif\n} galisskeletaltransforms_t;\n#endif\n\n//we can't be bothered with animating skins.\n//We'll load up to four of them but after that you're on your own\n#ifndef SERVERONLY\ntypedef struct\n{\n\tshader_t *shader;\n\tqbyte *texels;\t//this is 8bit for frame 0 only. only valid in q1 models without replacement textures, used for colourising player skins.\n\tconst char *defaultshader;\n\tchar shadername[MAX_QPATH];\n\ttexnums_t texnums;\n} skinframe_t;\nstruct galiasskin_s\n{\n\tint skinwidth;\n\tint skinheight;\n\tfloat skinspeed;\n\tint numframes;\n\tskinframe_t *frame;\n\tchar name[MAX_QPATH];\n};\n\ntypedef struct\n{\n\tchar name[MAX_QPATH];\n\ttexnums_t texnum;\n\tunsigned int tcolour;\n\tunsigned int bcolour;\n\tunsigned int pclass;\n\tint skinnum;\n\tunsigned int subframe;\n\tbucket_t bucket;\n} galiascolourmapped_t;\n#endif\ntypedef struct galiasskin_s galiasskin_t;\n\ntypedef struct\n{\n\tchar name[64];\n\tvec3_t org;\n\tfloat ang[3][3];\n} md3tag_t;\n\ntypedef struct galiasinfo_s\n{\n\tchar surfacename[MAX_QPATH];\n\tunsigned short geomset;\n\tunsigned short geomid;\n\tindex_t *ofs_indexes;\n\tint numindexes;\n\n\t//for hitmodel\n\tunsigned int contents;\t\t//default CONTENTS_BODY\n\tq2csurface_t csurface;\t\t//flags, and also collision name, if useful...\n\tunsigned int surfaceid;\t\t//the body reported to qc via trace_surface\n\n\tfloat\tmindist;\n\tfloat\tmaxdist;\n\n\tint *ofs_trineighbours;\n\tfloat lerpcutoff;\t//hack. should probably be part of the entity structure, but I really don't want new models (and thus code) to have access to this ugly inefficient hack. make your models properly in the first place.\n\n\tint numskins;\n//#ifndef SERVERONLY\n\tgaliasskin_t *ofsskins;\n//#endif\n\n\tint shares_verts;\t//used with models with two shaders using the same vertex. set to the surface number to inherit from (or itself).\n\tint shares_bones;\t//use last mesh's bones. set to the surface number to inherit from (or itself).\n\n\tint numverts;\n\n//#ifndef SERVERONLY\n\tvec2_t *ofs_st_array;\n\tvec2_t *ofs_lmst_array;\n\tvec4_t *ofs_rgbaf;\n\tbyte_vec4_t *ofs_rgbaub;\n//#endif\n\n\tint numanimations;\n\tgaliasanimation_t *ofsanimations;\n\n\tstruct galiasinfo_s *nextsurf;\n\n#ifdef SKELETALMODELS\n\tboneidx_t *bonemap;\t\t//filled in automatically if our mesh has more gpu bones than we can support\n\tunsigned int mappedbones;\t//number of private per-mesh bones.\n\tunsigned int nummorphs;\t//extra data after the xyz/norm/stvect arrays\n\tconst float *(QDECL *AnimateMorphs)(const struct galiasinfo_s *surf, const framestate_t *framestate, float *resultmorphs);\t//returns a float weight[nummorphs] array (base verts have an implicit weight of 1, so these are purely additive)\n\tint meshrootbone;\t\t//unused by engine. for loader callbacks to (ab)use\n\n\tfloat *baseframeofs;\t/*non-heirachical*/\n\tint numbones;\n\tgaliasbone_t *ofsbones;\n\n\tvecV_t *ofs_skel_xyz;\n\tvec3_t *ofs_skel_norm;\n\tvec3_t *ofs_skel_svect;\n\tvec3_t *ofs_skel_tvect;\n\tbone_vec4_t *ofs_skel_idx;\n\tvec4_t *ofs_skel_weight;\n\n\tvboarray_t vbo_skel_verts;\n\tvboarray_t vbo_skel_normals;\n\tvboarray_t vbo_skel_svector;\n\tvboarray_t vbo_skel_tvector;\n\tvboarray_t vbo_skel_bonenum;\n\tvboarray_t vbo_skel_bweight;\n#endif\n\tvboarray_t vboindicies;\n\tvboarray_t vbotexcoords;\n\tvboarray_t vbolmtexcoords;\n\tvboarray_t vborgba;\t//yeah, just you try reading THAT as an actual word.\n\tvoid *vbomem;\n\tvoid *ebomem;\n\n//these exist only in the root mesh.\n#ifdef MD3MODELS\n\tint numtagframes;\n\tint numtags;\n\tmd3tag_t *ofstags;\n#else\n\tFTE_DEPRECATED int numtagframes;\n\tFTE_DEPRECATED int numtags;\n\tFTE_DEPRECATED md3tag_t *ofstags;\n#endif\n\n\tvoid *ctx;\t\t\t\t//loader-specific stuff. must be ZG_Malloced if it lasts beyond the loader.\n\tunsigned int warned;\t//passed around at load time, so we don't spam warnings\n} galiasinfo_t;\n\nstruct terrainfuncs_s;\nstruct bihleaf_s;\ntypedef struct modplugfuncs_s\n{\n\tint version;\n\n\t//format handling\n\tint (QDECL *RegisterModelFormatText)(const char *formatname, char *magictext, qboolean (QDECL *load) (struct model_s *mod, void *buffer, size_t fsize));\n\tint (QDECL *RegisterModelFormatMagic)(const char *formatname, qbyte *magic,size_t magicsize, qboolean (QDECL *load) (struct model_s *mod, void *buffer, size_t fsize));\n\tvoid (QDECL *UnRegisterModelFormat)(int idx);\n\tvoid (QDECL *UnRegisterAllModelFormats)(void);\n\n\t//util\n\tvoid (QDECL *StripExtension) (const char *in, char *out, int outlen);\n\n\t//matrix maths\n\tvoid (QDECL *ConcatTransforms) (const float in1[3][4], const float in2[3][4], float out[3][4]);\n\tvoid (QDECL *M3x4_Invert) (const float *in1, float *out);\n\tvoid (QDECL *VectorAngles)(const float *forward, const float *up, float *result, qboolean meshpitch);\n\tvoid (QDECL *AngleVectors)(const vec3_t angles, vec3_t forward, vec3_t right, vec3_t up);\n\tvoid (QDECL *GenMatrixPosQuat4Scale)(const vec3_t pos, const vec4_t quat, const vec3_t scale, float result[12]);\n\tvoid (QDECL *QuaternionSlerp)(const vec4_t p, vec4_t q, float t, vec4_t qt);\n\n\t//bone stuff\n\tvoid (QDECL *ForceConvertBoneData)(skeltype_t sourcetype, const float *sourcedata, size_t bonecount, galiasbone_t *bones, skeltype_t desttype, float *destbuffer, size_t destbonecount);\n\n\t//texturing\n\timage_t *(QDECL *GetTexture)(const char *identifier, const char *subpath, unsigned int flags, void *fallbackdata, void *fallbackpalette, int fallbackwidth, int fallbackheight, uploadfmt_t fallbackfmt);\n\tvoid (QDECL *AccumulateTextureVectors)(vecV_t *const vc, vec2_t *const tc, vec3_t *nv, vec3_t *sv, vec3_t *tv, const index_t *idx, int numidx, qboolean calcnorms);\n\tvoid (QDECL *NormaliseTextureVectors)(vec3_t *n, vec3_t *s, vec3_t *t, int v, qboolean calcnorms);\n\n\tmodel_t *(QDECL *GetModel)(const char *identifier, enum mlverbosity_e verbosity);\n\tmodel_t *(QDECL *BeginSubmodelLoad)(const char *identifier);\n\tqboolean (*LoadEntities)(struct model_s *mod, const char *entdata, size_t entdatasize);\n\tvoid (*LoadMapArchive)(struct model_s *mod, void *archivedata, size_t archivesize);\n\tvoid (*BIH_Build) (struct model_s *mod, struct bihleaf_s *leafs, size_t numleafs);\n\tvoid (*BIH_BuildAlias) (struct model_s *mod, galiasinfo_t *meshes);\n\tsize_t (*ClipPlaneToBrush)(vecV_t *points, size_t maxpoints, void *planes, size_t planestride, size_t numplanes, vec4_t face);\n\tshader_t *(*RegisterBasicShader)(struct model_s *mod, const char *texname, unsigned int usageflags, const char *shadertext, uploadfmt_t pixelfmt, unsigned int width, unsigned int height, void *pixeldata, void *palettedata);\n\tvoid (*Batches_Build)(struct model_s *mod, builddata_t *bd);\n\tvoid (*RenderDynamicLightmaps) (struct msurface_s *surf);\n\tentity_t *(*NewSceneEntity) (void);\n\tvoid (*EndSubmodelLoad)(struct model_s *submod, int modelloadstate);\n\t#define plugmodfuncs_name_idxpostfix \"_IDX\" STRINGIFY(sizeof_index_t)\n#define plugmodfuncs_name \"Models\" plugmodfuncs_name_idxpostfix\n} plugmodfuncs_t;\n#define MODPLUGFUNCS_VERSION 4\n\n#ifdef SKELETALMODELS\nvoid Alias_TransformVerticies(float *bonepose, galisskeletaltransforms_t *weights, int numweights, vecV_t *xyzout, vec3_t *normout);\nvoid QDECL Alias_ForceConvertBoneData(skeltype_t sourcetype, const float *sourcedata, size_t bonecount, galiasbone_t *bones, skeltype_t desttype, float *destbuffer, size_t destbonecount);\n#endif\nqboolean Alias_GAliasBuildMesh(mesh_t *mesh, vbo_t **vbop, galiasinfo_t *inf, int surfnum, entity_t *e, qboolean allowskel);\nvoid Mod_DestroyMesh(galiasinfo_t *galias);\nvoid Alias_FlushCache(void);\nvoid Alias_Shutdown(void);\nvoid Alias_Register(void);\nshader_t *Mod_ShaderForSkin(model_t *model, int surfaceidx, int num, float time, texnums_t **out_texnums);\nconst char *Mod_SkinNameForNum(model_t *model, int surfaceidx, int num);\nconst char *Mod_SurfaceNameForNum(model_t *model, int num);\nconst char *Mod_FrameNameForNum(model_t *model, int surfaceidx, int num);\nconst char *Mod_SkinNameForNum(model_t *model, int surfaceidx, int num);\nqboolean Mod_FrameInfoForNum(model_t *model, int surfaceidx, int num, char **name, int *numframes, float *duration, qboolean *loop, int *act);\n\nqboolean Mod_DoCRC(model_t *mod, char *buffer, int buffersize);\n\nvoid QDECL Mod_AccumulateTextureVectors(vecV_t *const vc, vec2_t *const tc, vec3_t *nv, vec3_t *sv, vec3_t *tv, const index_t *idx, int numidx, qboolean calcnorms);\nvoid Mod_AccumulateMeshTextureVectors(mesh_t *mesh);\nvoid QDECL Mod_NormaliseTextureVectors(vec3_t *n, vec3_t *s, vec3_t *t, int v, qboolean calcnorms);\nvoid R_Generate_Mesh_ST_Vectors(mesh_t *mesh);\n\n#ifdef __cplusplus\n};\n#endif\n#endif //COM_MESH_H\n"
  },
  {
    "path": "engine/common/com_phys_bullet.cpp",
    "content": "#include \"quakedef.h\"\r\n\r\n#ifdef USE_INTERNAL_BULLET\r\n#define FTEENGINE\r\n#undef FTEPLUGIN\r\n#include \"../../plugins/bullet/bulletplug.cpp\"\r\n#endif"
  },
  {
    "path": "engine/common/com_phys_ode.c",
    "content": "/*\r\nCopyright (C) 1996-1997 Id Software, Inc.\r\n\r\nThis program is free software; you can redistribute it and/or\r\nmodify it under the terms of the GNU General Public License\r\nas published by the Free Software Foundation; either version 2\r\nof the License, or (at your option) any later version.\r\n\r\nThis program is distributed in the hope that it will be useful,\r\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\r\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\r\n\r\nSee the GNU General Public License for more details.\r\n\r\nYou should have received a copy of the GNU General Public License\r\nalong with this program; if not, write to the Free Software\r\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\r\n\r\n*/\r\n\r\n/*\r\n\tODE physics engine code\r\n\tThis code is ported from DarkPlaces svn commit 9370\r\n\tOriginally written by LordHavoc.\r\n*/\r\n\r\n//if we're not building as an fte-specific plugin, we must be being built as part of the fte engine itself.\r\n//(no, we don't want to act as a plugin for ezquake...)\r\n#ifndef FTEPLUGIN\r\n#define FTEPLUGIN\r\n#define Plug_Init Plug_ODE_Init\r\n#endif\r\n\r\n#include \"../../plugins/plugin.h\"\r\n#include \"../../plugins/engine.h\"\r\n\r\n#define DEG2RAD(d) (d * M_PI * (1/180.0f))\r\n#define RAD2DEG(d) ((d*180) / M_PI)\r\n\r\n#ifdef USERBE\r\n\r\n#include \"pr_common.h\"\r\n\r\n#ifndef FTEENGINE\r\n#define BZ_Malloc malloc\r\n#define BZ_Free free\r\n#define Z_Free BZ_Free\r\n#define VectorCompare VectorComparestatic\r\nstatic int VectorCompare (const vec3_t v1, const vec3_t v2)\r\n{\r\n\tint\t\ti;\r\n\tfor (i=0 ; i<3 ; i++)\r\n\t\tif (v1[i] != v2[i])\r\n\t\t\treturn 0;\r\n\treturn 1;\r\n}\r\n\r\ncvar_t *cvar_r_meshpitch;\r\ncvar_t *cvar_r_meshroll;\r\n#endif\r\n\r\nstatic rbeplugfuncs_t *rbefuncs;\r\n\r\n//============================================================================\r\n// physics engine support\r\n//============================================================================\r\n\r\n//#ifndef ODE_STATIC\r\n//#define ODE_DYNAMIC 1\r\n//#endif\r\n\r\n//ODE's headers provide version info only as a string, so we don' know when things are deprecated or not.\r\n//this then fucks us over when we try using -Werror\r\n//so until ODE changes its ways, we'll just have to make assumptions and ignore those warnings.\r\n#pragma GCC diagnostic ignored \"-Wdeprecated-declarations\"\r\n#define ODEVERSION MAKE2VER(0,15)\r\n#define MAKE2VER(maj,min) (((maj)<<8)|(min))\r\n\r\n// LordHavoc: this large chunk of definitions comes from the ODE library\r\n// include files.\r\n#ifdef ODE_STATIC\r\n#undef ODE_DYNAMIC\r\n#include \"ode/ode.h\"\r\n#ifdef dSINGLE\r\n#pragma warningmsg(\"note: ODE headers configured ODE for single-precision. this is not commonly tested and may result in ODE rashing out due to limited precision.\")\r\n#endif\r\n#else\r\n#ifndef ODE_DYNAMIC\r\n#define ODE_DYNAMIC\r\n#endif\r\n#ifdef WINAPI\r\n// ODE does not use WINAPI\r\n#define ODE_API VARGS /*vargs because fte likes to be compiled fastcall (vargs is defined as cdecl...)*/\r\n#else\r\n#define ODE_API VARGS\r\n#endif\r\n\r\n#define DEG2RAD(d) (d * M_PI * (1/180.0f))\r\n#define RAD2DEG(d) ((d*180) / M_PI)\r\n\r\n// note: dynamic builds of ODE tend to be double precision, this is not used\r\n// for static builds\r\ntypedef double dReal;\r\n\r\ntypedef dReal dVector3[4];\r\ntypedef dReal dVector4[4];\r\ntypedef dReal dMatrix3[4*3];\r\ntypedef dReal dMatrix4[4*4];\r\ntypedef dReal dMatrix6[8*6];\r\ntypedef dReal dQuaternion[4];\r\n\r\nstruct dxWorld;\t\t/* dynamics world */\r\nstruct dxSpace;\t\t/* collision space */\r\nstruct dxBody;\t\t/* rigid body (dynamics object) */\r\nstruct dxGeom;\t\t/* geometry (collision object) */\r\nstruct dxJoint;\r\nstruct dxJointNode;\r\nstruct dxJointGroup;\r\nstruct dxTriMeshData;\r\n\r\n#define dInfinity 3.402823466e+38f\r\n\r\ntypedef struct dxWorld *dWorldID;\r\ntypedef struct dxSpace *dSpaceID;\r\ntypedef struct dxBody *dBodyID;\r\ntypedef struct dxGeom *dGeomID;\r\ntypedef struct dxJoint *dJointID;\r\ntypedef struct dxJointGroup *dJointGroupID;\r\ntypedef struct dxTriMeshData *dTriMeshDataID;\r\n\r\ntypedef struct dJointFeedback\r\n{\r\n\tdVector3 f1;\t\t/* force applied to body 1 */\r\n\tdVector3 t1;\t\t/* torque applied to body 1 */\r\n\tdVector3 f2;\t\t/* force applied to body 2 */\r\n\tdVector3 t2;\t\t/* torque applied to body 2 */\r\n}\r\ndJointFeedback;\r\n\r\ntypedef enum dJointType\r\n{\r\n\tdJointTypeNone = 0,\r\n\tdJointTypeBall,\r\n\tdJointTypeHinge,\r\n\tdJointTypeSlider,\r\n\tdJointTypeContact,\r\n\tdJointTypeUniversal,\r\n\tdJointTypeHinge2,\r\n\tdJointTypeFixed,\r\n\tdJointTypeNull,\r\n\tdJointTypeAMotor,\r\n\tdJointTypeLMotor,\r\n\tdJointTypePlane2D,\r\n\tdJointTypePR,\r\n\tdJointTypePU,\r\n\tdJointTypePiston\r\n}\r\ndJointType;\r\n\r\n#define D_ALL_PARAM_NAMES(start) \\\r\n  /* parameters for limits and motors */ \\\r\n  dParamLoStop = start, \\\r\n  dParamHiStop, \\\r\n  dParamVel, \\\r\n  dParamFMax, \\\r\n  dParamFudgeFactor, \\\r\n  dParamBounce, \\\r\n  dParamCFM, \\\r\n  dParamStopERP, \\\r\n  dParamStopCFM, \\\r\n  /* parameters for suspension */ \\\r\n  dParamSuspensionERP, \\\r\n  dParamSuspensionCFM, \\\r\n  dParamERP, \\\r\n\r\n#define D_ALL_PARAM_NAMES_X(start,x) \\\r\n  /* parameters for limits and motors */ \\\r\n  dParamLoStop ## x = start, \\\r\n  dParamHiStop ## x, \\\r\n  dParamVel ## x, \\\r\n  dParamFMax ## x, \\\r\n  dParamFudgeFactor ## x, \\\r\n  dParamBounce ## x, \\\r\n  dParamCFM ## x, \\\r\n  dParamStopERP ## x, \\\r\n  dParamStopCFM ## x, \\\r\n  /* parameters for suspension */ \\\r\n  dParamSuspensionERP ## x, \\\r\n  dParamSuspensionCFM ## x, \\\r\n  dParamERP ## x,\r\n\r\nenum {\r\n  D_ALL_PARAM_NAMES(0)\r\n  D_ALL_PARAM_NAMES_X(0x100,2)\r\n  D_ALL_PARAM_NAMES_X(0x200,3)\r\n\r\n  /* add a multiple of this constant to the basic parameter numbers to get\r\n   * the parameters for the second, third etc axes.\r\n   */\r\n  dParamGroup=0x100\r\n};\r\n\r\ntypedef struct dMass\r\n{\r\n\tdReal mass;\r\n\tdVector3 c;\r\n\tdMatrix3 I;\r\n}\r\ndMass;\r\n\r\nenum\r\n{\r\n\tdContactMu2\t\t\t= 0x001,\r\n\tdContactFDir1\t\t= 0x002,\r\n\tdContactBounce\t\t= 0x004,\r\n\tdContactSoftERP\t\t= 0x008,\r\n\tdContactSoftCFM\t\t= 0x010,\r\n\tdContactMotion1\t\t= 0x020,\r\n\tdContactMotion2\t\t= 0x040,\r\n\tdContactMotionN\t\t= 0x080,\r\n\tdContactSlip1\t\t= 0x100,\r\n\tdContactSlip2\t\t= 0x200,\r\n\t\r\n\tdContactApprox0\t\t= 0x0000,\r\n\tdContactApprox1_1\t= 0x1000,\r\n\tdContactApprox1_2\t= 0x2000,\r\n\tdContactApprox1\t\t= 0x3000\r\n};\r\n\r\ntypedef struct dSurfaceParameters\r\n{\r\n\t/* must always be defined */\r\n\tint mode;\r\n\tdReal mu;\r\n\r\n\t/* only defined if the corresponding flag is set in mode */\r\n\tdReal mu2;\r\n\tdReal bounce;\r\n\tdReal bounce_vel;\r\n\tdReal soft_erp;\r\n\tdReal soft_cfm;\r\n\tdReal motion1,motion2,motionN;\r\n\tdReal slip1,slip2;\r\n} dSurfaceParameters;\r\n\r\ntypedef struct dContactGeom\r\n{\r\n\tdVector3 pos;          ///< contact position\r\n\tdVector3 normal;       ///< normal vector\r\n\tdReal depth;           ///< penetration depth\r\n\tdGeomID g1,g2;         ///< the colliding geoms\r\n\tint side1,side2;       ///< (to be documented)\r\n}\r\ndContactGeom;\r\n\r\ntypedef struct dContact\r\n{\r\n\tdSurfaceParameters surface;\r\n\tdContactGeom geom;\r\n\tdVector3 fdir1;\r\n}\r\ndContact;\r\n\r\ntypedef void VARGS dNearCallback (void *data, dGeomID o1, dGeomID o2);\r\n\r\n// SAP\r\n// Order XZY or ZXY usually works best, if your Y is up.\r\n#define dSAP_AXES_XYZ  ((0)|(1<<2)|(2<<4))\r\n#define dSAP_AXES_XZY  ((0)|(2<<2)|(1<<4))\r\n#define dSAP_AXES_YXZ  ((1)|(0<<2)|(2<<4))\r\n#define dSAP_AXES_YZX  ((1)|(2<<2)|(0<<4))\r\n#define dSAP_AXES_ZXY  ((2)|(0<<2)|(1<<4))\r\n#define dSAP_AXES_ZYX  ((2)|(1<<2)|(0<<4))\r\n\r\n//const char*     (ODE_API *dGetConfiguration)(void);\r\nint             (ODE_API *dCheckConfiguration)( const char* token );\r\nint             (ODE_API *dInitODE)(void);\r\n//int             (ODE_API *dInitODE2)(unsigned int uiInitFlags);\r\n//int             (ODE_API *dAllocateODEDataForThread)(unsigned int uiAllocateFlags);\r\n//void            (ODE_API *dCleanupODEAllDataForThread)(void);\r\nvoid            (ODE_API *dCloseODE)(void);\r\n\r\n//int             (ODE_API *dMassCheck)(const dMass *m);\r\n//void            (ODE_API *dMassSetZero)(dMass *);\r\n//void            (ODE_API *dMassSetParameters)(dMass *, dReal themass, dReal cgx, dReal cgy, dReal cgz, dReal I11, dReal I22, dReal I33, dReal I12, dReal I13, dReal I23);\r\n//void            (ODE_API *dMassSetSphere)(dMass *, dReal density, dReal radius);\r\nvoid            (ODE_API *dMassSetSphereTotal)(dMass *, dReal total_mass, dReal radius);\r\n//void            (ODE_API *dMassSetCapsule)(dMass *, dReal density, int direction, dReal radius, dReal length);\r\nvoid            (ODE_API *dMassSetCapsuleTotal)(dMass *, dReal total_mass, int direction, dReal radius, dReal length);\r\n//void            (ODE_API *dMassSetCylinder)(dMass *, dReal density, int direction, dReal radius, dReal length);\r\nvoid            (ODE_API *dMassSetCylinderTotal)(dMass *, dReal total_mass, int direction, dReal radius, dReal length);\r\n//void            (ODE_API *dMassSetBox)(dMass *, dReal density, dReal lx, dReal ly, dReal lz);\r\nvoid            (ODE_API *dMassSetBoxTotal)(dMass *, dReal total_mass, dReal lx, dReal ly, dReal lz);\r\n//void            (ODE_API *dMassSetTrimesh)(dMass *, dReal density, dGeomID g);\r\n//void            (ODE_API *dMassSetTrimeshTotal)(dMass *m, dReal total_mass, dGeomID g);\r\n//void            (ODE_API *dMassAdjust)(dMass *, dReal newmass);\r\n//void            (ODE_API *dMassTranslate)(dMass *, dReal x, dReal y, dReal z);\r\n//void            (ODE_API *dMassRotate)(dMass *, const dMatrix3 R);\r\n//void            (ODE_API *dMassAdd)(dMass *a, const dMass *b);\r\n//\r\ndWorldID        (ODE_API *dWorldCreate)(void);\r\nvoid            (ODE_API *dWorldDestroy)(dWorldID world);\r\nvoid            (ODE_API *dWorldSetGravity)(dWorldID, dReal x, dReal y, dReal z);\r\nvoid            (ODE_API *dWorldGetGravity)(dWorldID, dVector3 gravity);\r\nvoid            (ODE_API *dWorldSetERP)(dWorldID, dReal erp);\r\n//dReal           (ODE_API *dWorldGetERP)(dWorldID);\r\nvoid            (ODE_API *dWorldSetCFM)(dWorldID, dReal cfm);\r\n//dReal           (ODE_API *dWorldGetCFM)(dWorldID);\r\nvoid            (ODE_API *dWorldStep)(dWorldID, dReal stepsize);\r\n//void            (ODE_API *dWorldImpulseToForce)(dWorldID, dReal stepsize, dReal ix, dReal iy, dReal iz, dVector3 force);\r\nvoid            (ODE_API *dWorldQuickStep)(dWorldID w, dReal stepsize);\r\nvoid            (ODE_API *dWorldSetQuickStepNumIterations)(dWorldID, int num);\r\n//int             (ODE_API *dWorldGetQuickStepNumIterations)(dWorldID);\r\n//void            (ODE_API *dWorldSetQuickStepW)(dWorldID, dReal over_relaxation);\r\n//dReal           (ODE_API *dWorldGetQuickStepW)(dWorldID);\r\n//void            (ODE_API *dWorldSetContactMaxCorrectingVel)(dWorldID, dReal vel);\r\n//dReal           (ODE_API *dWorldGetContactMaxCorrectingVel)(dWorldID);\r\nvoid            (ODE_API *dWorldSetContactSurfaceLayer)(dWorldID, dReal depth);\r\n//dReal           (ODE_API *dWorldGetContactSurfaceLayer)(dWorldID);\r\n//void            (ODE_API *dWorldStepFast1)(dWorldID, dReal stepsize, int maxiterations);\r\n//void            (ODE_API *dWorldSetAutoEnableDepthSF1)(dWorldID, int autoEnableDepth);\r\n//int             (ODE_API *dWorldGetAutoEnableDepthSF1)(dWorldID);\r\n//dReal           (ODE_API *dWorldGetAutoDisableLinearThreshold)(dWorldID);\r\nvoid            (ODE_API *dWorldSetAutoDisableLinearThreshold)(dWorldID, dReal linear_threshold);\r\n//dReal           (ODE_API *dWorldGetAutoDisableAngularThreshold)(dWorldID);\r\nvoid            (ODE_API *dWorldSetAutoDisableAngularThreshold)(dWorldID, dReal angular_threshold);\r\n//dReal           (ODE_API *dWorldGetAutoDisableLinearAverageThreshold)(dWorldID);\r\n//void            (ODE_API *dWorldSetAutoDisableLinearAverageThreshold)(dWorldID, dReal linear_average_threshold);\r\n//dReal           (ODE_API *dWorldGetAutoDisableAngularAverageThreshold)(dWorldID);\r\n//void            (ODE_API *dWorldSetAutoDisableAngularAverageThreshold)(dWorldID, dReal angular_average_threshold);\r\n//int             (ODE_API *dWorldGetAutoDisableAverageSamplesCount)(dWorldID);\r\nvoid            (ODE_API *dWorldSetAutoDisableAverageSamplesCount)(dWorldID, unsigned int average_samples_count );\r\n//int             (ODE_API *dWorldGetAutoDisableSteps)(dWorldID);\r\nvoid            (ODE_API *dWorldSetAutoDisableSteps)(dWorldID, int steps);\r\n//dReal           (ODE_API *dWorldGetAutoDisableTime)(dWorldID);\r\nvoid            (ODE_API *dWorldSetAutoDisableTime)(dWorldID, dReal time);\r\n//int             (ODE_API *dWorldGetAutoDisableFlag)(dWorldID);\r\nvoid            (ODE_API *dWorldSetAutoDisableFlag)(dWorldID, int do_auto_disable);\r\n//dReal           (ODE_API *dWorldGetLinearDampingThreshold)(dWorldID w);\r\nvoid            (ODE_API *dWorldSetLinearDampingThreshold)(dWorldID w, dReal threshold);\r\n//dReal           (ODE_API *dWorldGetAngularDampingThreshold)(dWorldID w);\r\nvoid            (ODE_API *dWorldSetAngularDampingThreshold)(dWorldID w, dReal threshold);\r\n//dReal           (ODE_API *dWorldGetLinearDamping)(dWorldID w);\r\nvoid            (ODE_API *dWorldSetLinearDamping)(dWorldID w, dReal scale);\r\n//dReal           (ODE_API *dWorldGetAngularDamping)(dWorldID w);\r\nvoid            (ODE_API *dWorldSetAngularDamping)(dWorldID w, dReal scale);\r\n//void            (ODE_API *dWorldSetDamping)(dWorldID w, dReal linear_scale, dReal angular_scale);\r\n//dReal           (ODE_API *dWorldGetMaxAngularSpeed)(dWorldID w);\r\n//void            (ODE_API *dWorldSetMaxAngularSpeed)(dWorldID w, dReal max_speed);\r\n//dReal           (ODE_API *dBodyGetAutoDisableLinearThreshold)(dBodyID);\r\n//void            (ODE_API *dBodySetAutoDisableLinearThreshold)(dBodyID, dReal linear_average_threshold);\r\n//dReal           (ODE_API *dBodyGetAutoDisableAngularThreshold)(dBodyID);\r\n//void            (ODE_API *dBodySetAutoDisableAngularThreshold)(dBodyID, dReal angular_average_threshold);\r\n//int             (ODE_API *dBodyGetAutoDisableAverageSamplesCount)(dBodyID);\r\n//void            (ODE_API *dBodySetAutoDisableAverageSamplesCount)(dBodyID, unsigned int average_samples_count);\r\n//int             (ODE_API *dBodyGetAutoDisableSteps)(dBodyID);\r\n//void            (ODE_API *dBodySetAutoDisableSteps)(dBodyID, int steps);\r\n//dReal           (ODE_API *dBodyGetAutoDisableTime)(dBodyID);\r\n//void            (ODE_API *dBodySetAutoDisableTime)(dBodyID, dReal time);\r\n//int             (ODE_API *dBodyGetAutoDisableFlag)(dBodyID);\r\n//void            (ODE_API *dBodySetAutoDisableFlag)(dBodyID, int do_auto_disable);\r\n//void            (ODE_API *dBodySetAutoDisableDefaults)(dBodyID);\r\n//dWorldID        (ODE_API *dBodyGetWorld)(dBodyID);\r\ndBodyID         (ODE_API *dBodyCreate)(dWorldID);\r\nvoid            (ODE_API *dBodyDestroy)(dBodyID);\r\nvoid            (ODE_API *dBodySetData)(dBodyID, void *data);\r\nvoid *          (ODE_API *dBodyGetData)(dBodyID);\r\nvoid            (ODE_API *dBodySetPosition)(dBodyID, dReal x, dReal y, dReal z);\r\nvoid            (ODE_API *dBodySetRotation)(dBodyID, const dMatrix3 R);\r\n//void            (ODE_API *dBodySetQuaternion)(dBodyID, const dQuaternion q);\r\nvoid            (ODE_API *dBodySetLinearVel)(dBodyID, dReal x, dReal y, dReal z);\r\nvoid            (ODE_API *dBodySetAngularVel)(dBodyID, dReal x, dReal y, dReal z);\r\nconst dReal *   (ODE_API *dBodyGetPosition)(dBodyID);\r\n//void            (ODE_API *dBodyCopyPosition)(dBodyID body, dVector3 pos);\r\nconst dReal *   (ODE_API *dBodyGetRotation)(dBodyID);\r\n//void            (ODE_API *dBodyCopyRotation)(dBodyID, dMatrix3 R);\r\n//const dReal *   (ODE_API *dBodyGetQuaternion)(dBodyID);\r\n//void            (ODE_API *dBodyCopyQuaternion)(dBodyID body, dQuaternion quat);\r\nconst dReal *   (ODE_API *dBodyGetLinearVel)(dBodyID);\r\nconst dReal *   (ODE_API *dBodyGetAngularVel)(dBodyID);\r\nvoid            (ODE_API *dBodySetMass)(dBodyID, const dMass *mass);\r\n//void            (ODE_API *dBodyGetMass)(dBodyID, dMass *mass);\r\n//void            (ODE_API *dBodyAddForce)(dBodyID, dReal fx, dReal fy, dReal fz);\r\nvoid            (ODE_API *dBodyAddTorque)(dBodyID, dReal fx, dReal fy, dReal fz);\r\n//void            (ODE_API *dBodyAddRelForce)(dBodyID, dReal fx, dReal fy, dReal fz);\r\n//void            (ODE_API *dBodyAddRelTorque)(dBodyID, dReal fx, dReal fy, dReal fz);\r\nvoid            (ODE_API *dBodyAddForceAtPos)(dBodyID, dReal fx, dReal fy, dReal fz, dReal px, dReal py, dReal pz);\r\n//void            (ODE_API *dBodyAddForceAtRelPos)(dBodyID, dReal fx, dReal fy, dReal fz, dReal px, dReal py, dReal pz);\r\n//void            (ODE_API *dBodyAddRelForceAtPos)(dBodyID, dReal fx, dReal fy, dReal fz, dReal px, dReal py, dReal pz);\r\n//void            (ODE_API *dBodyAddRelForceAtRelPos)(dBodyID, dReal fx, dReal fy, dReal fz, dReal px, dReal py, dReal pz);\r\n//const dReal *   (ODE_API *dBodyGetForce)(dBodyID);\r\n//const dReal *   (ODE_API *dBodyGetTorque)(dBodyID);\r\n//void            (ODE_API *dBodySetForce)(dBodyID b, dReal x, dReal y, dReal z);\r\n//void            (ODE_API *dBodySetTorque)(dBodyID b, dReal x, dReal y, dReal z);\r\n//void            (ODE_API *dBodyGetRelPointPos)(dBodyID, dReal px, dReal py, dReal pz, dVector3 result);\r\n//void            (ODE_API *dBodyGetRelPointVel)(dBodyID, dReal px, dReal py, dReal pz, dVector3 result);\r\n//void            (ODE_API *dBodyGetPointVel)(dBodyID, dReal px, dReal py, dReal pz, dVector3 result);\r\n//void            (ODE_API *dBodyGetPosRelPoint)(dBodyID, dReal px, dReal py, dReal pz, dVector3 result);\r\n//void            (ODE_API *dBodyVectorToWorld)(dBodyID, dReal px, dReal py, dReal pz, dVector3 result);\r\n//void            (ODE_API *dBodyVectorFromWorld)(dBodyID, dReal px, dReal py, dReal pz, dVector3 result);\r\n//void            (ODE_API *dBodySetFiniteRotationMode)(dBodyID, int mode);\r\n//void            (ODE_API *dBodySetFiniteRotationAxis)(dBodyID, dReal x, dReal y, dReal z);\r\n//int             (ODE_API *dBodyGetFiniteRotationMode)(dBodyID);\r\n//void            (ODE_API *dBodyGetFiniteRotationAxis)(dBodyID, dVector3 result);\r\nint             (ODE_API *dBodyGetNumJoints)(dBodyID b);\r\ndJointID        (ODE_API *dBodyGetJoint)(dBodyID, int index);\r\n//void            (ODE_API *dBodySetDynamic)(dBodyID);\r\n//void            (ODE_API *dBodySetKinematic)(dBodyID);\r\n//int             (ODE_API *dBodyIsKinematic)(dBodyID);\r\nvoid            (ODE_API *dBodyEnable)(dBodyID);\r\nvoid            (ODE_API *dBodyDisable)(dBodyID);\r\n//int             (ODE_API *dBodyIsEnabled)(dBodyID);\r\nvoid            (ODE_API *dBodySetGravityMode)(dBodyID b, int mode);\r\nint             (ODE_API *dBodyGetGravityMode)(dBodyID b);\r\n//void            (*dBodySetMovedCallback)(dBodyID b, void(ODE_API *callback)(dBodyID));\r\n//dGeomID         (ODE_API *dBodyGetFirstGeom)(dBodyID b);\r\n//dGeomID         (ODE_API *dBodyGetNextGeom)(dGeomID g);\r\n//void            (ODE_API *dBodySetDampingDefaults)(dBodyID b);\r\n//dReal           (ODE_API *dBodyGetLinearDamping)(dBodyID b);\r\nvoid            (ODE_API *dBodySetLinearDamping)(dBodyID b, dReal scale);\r\n//dReal           (ODE_API *dBodyGetAngularDamping)(dBodyID b);\r\nvoid            (ODE_API *dBodySetAngularDamping)(dBodyID b, dReal scale);\r\n//void            (ODE_API *dBodySetDamping)(dBodyID b, dReal linear_scale, dReal angular_scale);\r\n//dReal           (ODE_API *dBodyGetLinearDampingThreshold)(dBodyID b);\r\n//void            (ODE_API *dBodySetLinearDampingThreshold)(dBodyID b, dReal threshold);\r\n//dReal           (ODE_API *dBodyGetAngularDampingThreshold)(dBodyID b);\r\n//void            (ODE_API *dBodySetAngularDampingThreshold)(dBodyID b, dReal threshold);\r\n//dReal           (ODE_API *dBodyGetMaxAngularSpeed)(dBodyID b);\r\nvoid            (ODE_API *dBodySetMaxAngularSpeed)(dBodyID b, dReal max_speed);\r\n//int             (ODE_API *dBodyGetGyroscopicMode)(dBodyID b);\r\n//void            (ODE_API *dBodySetGyroscopicMode)(dBodyID b, int enabled);\r\ndJointID        (ODE_API *dJointCreateBall)(dWorldID, dJointGroupID);\r\ndJointID        (ODE_API *dJointCreateHinge)(dWorldID, dJointGroupID);\r\ndJointID        (ODE_API *dJointCreateSlider)(dWorldID, dJointGroupID);\r\ndJointID        (ODE_API *dJointCreateContact)(dWorldID, dJointGroupID, const dContact *);\r\ndJointID        (ODE_API *dJointCreateHinge2)(dWorldID, dJointGroupID);\r\ndJointID        (ODE_API *dJointCreateUniversal)(dWorldID, dJointGroupID);\r\n//dJointID        (ODE_API *dJointCreatePR)(dWorldID, dJointGroupID);\r\n//dJointID        (ODE_API *dJointCreatePU)(dWorldID, dJointGroupID);\r\n//dJointID        (ODE_API *dJointCreatePiston)(dWorldID, dJointGroupID);\r\ndJointID        (ODE_API *dJointCreateFixed)(dWorldID, dJointGroupID);\r\n//dJointID        (ODE_API *dJointCreateNull)(dWorldID, dJointGroupID);\r\n//dJointID        (ODE_API *dJointCreateAMotor)(dWorldID, dJointGroupID);\r\n//dJointID        (ODE_API *dJointCreateLMotor)(dWorldID, dJointGroupID);\r\n//dJointID        (ODE_API *dJointCreatePlane2D)(dWorldID, dJointGroupID);\r\nvoid            (ODE_API *dJointDestroy)(dJointID);\r\ndJointGroupID   (ODE_API *dJointGroupCreate)(int max_size);\r\nvoid            (ODE_API *dJointGroupDestroy)(dJointGroupID);\r\nvoid            (ODE_API *dJointGroupEmpty)(dJointGroupID);\r\n//int             (ODE_API *dJointGetNumBodies)(dJointID);\r\nvoid            (ODE_API *dJointAttach)(dJointID, dBodyID body1, dBodyID body2);\r\nvoid            (ODE_API *dJointEnable)(dJointID);\r\nvoid            (ODE_API *dJointDisable)(dJointID);\r\n//int             (ODE_API *dJointIsEnabled)(dJointID);\r\nvoid            (ODE_API *dJointSetData)(dJointID, void *data);\r\nvoid *          (ODE_API *dJointGetData)(dJointID);\r\n//dJointType      (ODE_API *dJointGetType)(dJointID);\r\ndBodyID         (ODE_API *dJointGetBody)(dJointID, int index);\r\n//void            (ODE_API *dJointSetFeedback)(dJointID, dJointFeedback *);\r\n//dJointFeedback *(ODE_API *dJointGetFeedback)(dJointID);\r\nvoid            (ODE_API *dJointSetBallAnchor)(dJointID, dReal x, dReal y, dReal z);\r\n//void            (ODE_API *dJointSetBallAnchor2)(dJointID, dReal x, dReal y, dReal z);\r\nvoid            (ODE_API *dJointSetBallParam)(dJointID, int parameter, dReal value);\r\nvoid            (ODE_API *dJointSetHingeAnchor)(dJointID, dReal x, dReal y, dReal z);\r\n//void            (ODE_API *dJointSetHingeAnchorDelta)(dJointID, dReal x, dReal y, dReal z, dReal ax, dReal ay, dReal az);\r\nvoid            (ODE_API *dJointSetHingeAxis)(dJointID, dReal x, dReal y, dReal z);\r\n//void            (ODE_API *dJointSetHingeAxisOffset)(dJointID j, dReal x, dReal y, dReal z, dReal angle);\r\nvoid            (ODE_API *dJointSetHingeParam)(dJointID, int parameter, dReal value);\r\n//void            (ODE_API *dJointAddHingeTorque)(dJointID joint, dReal torque);\r\nvoid            (ODE_API *dJointSetSliderAxis)(dJointID, dReal x, dReal y, dReal z);\r\n//void            (ODE_API *dJointSetSliderAxisDelta)(dJointID, dReal x, dReal y, dReal z, dReal ax, dReal ay, dReal az);\r\nvoid            (ODE_API *dJointSetSliderParam)(dJointID, int parameter, dReal value);\r\n//void            (ODE_API *dJointAddSliderForce)(dJointID joint, dReal force);\r\nvoid            (ODE_API *dJointSetHinge2Anchor)(dJointID, dReal x, dReal y, dReal z);\r\nvoid            (ODE_API *dJointSetHinge2Axis1)(dJointID, dReal x, dReal y, dReal z);\r\nvoid            (ODE_API *dJointSetHinge2Axis2)(dJointID, dReal x, dReal y, dReal z);\r\nvoid            (ODE_API *dJointSetHinge2Param)(dJointID, int parameter, dReal value);\r\n//void            (ODE_API *dJointAddHinge2Torques)(dJointID joint, dReal torque1, dReal torque2);\r\nvoid            (ODE_API *dJointSetUniversalAnchor)(dJointID, dReal x, dReal y, dReal z);\r\nvoid            (ODE_API *dJointSetUniversalAxis1)(dJointID, dReal x, dReal y, dReal z);\r\n//void            (ODE_API *dJointSetUniversalAxis1Offset)(dJointID, dReal x, dReal y, dReal z, dReal offset1, dReal offset2);\r\nvoid            (ODE_API *dJointSetUniversalAxis2)(dJointID, dReal x, dReal y, dReal z);\r\n//void            (ODE_API *dJointSetUniversalAxis2Offset)(dJointID, dReal x, dReal y, dReal z, dReal offset1, dReal offset2);\r\nvoid            (ODE_API *dJointSetUniversalParam)(dJointID, int parameter, dReal value);\r\n//void            (ODE_API *dJointAddUniversalTorques)(dJointID joint, dReal torque1, dReal torque2);\r\n//void            (ODE_API *dJointSetPRAnchor)(dJointID, dReal x, dReal y, dReal z);\r\n//void            (ODE_API *dJointSetPRAxis1)(dJointID, dReal x, dReal y, dReal z);\r\n//void            (ODE_API *dJointSetPRAxis2)(dJointID, dReal x, dReal y, dReal z);\r\n//void            (ODE_API *dJointSetPRParam)(dJointID, int parameter, dReal value);\r\n//void            (ODE_API *dJointAddPRTorque)(dJointID j, dReal torque);\r\n//void            (ODE_API *dJointSetPUAnchor)(dJointID, dReal x, dReal y, dReal z);\r\n//void            (ODE_API *dJointSetPUAnchorOffset)(dJointID, dReal x, dReal y, dReal z, dReal dx, dReal dy, dReal dz);\r\n//void            (ODE_API *dJointSetPUAxis1)(dJointID, dReal x, dReal y, dReal z);\r\n//void            (ODE_API *dJointSetPUAxis2)(dJointID, dReal x, dReal y, dReal z);\r\n//void            (ODE_API *dJointSetPUAxis3)(dJointID, dReal x, dReal y, dReal z);\r\n//void            (ODE_API *dJointSetPUAxisP)(dJointID id, dReal x, dReal y, dReal z);\r\n//void            (ODE_API *dJointSetPUParam)(dJointID, int parameter, dReal value);\r\n//void            (ODE_API *dJointAddPUTorque)(dJointID j, dReal torque);\r\n//void            (ODE_API *dJointSetPistonAnchor)(dJointID, dReal x, dReal y, dReal z);\r\n//void            (ODE_API *dJointSetPistonAnchorOffset)(dJointID j, dReal x, dReal y, dReal z, dReal dx, dReal dy, dReal dz);\r\n//void            (ODE_API *dJointSetPistonParam)(dJointID, int parameter, dReal value);\r\n//void            (ODE_API *dJointAddPistonForce)(dJointID joint, dReal force);\r\nvoid            (ODE_API *dJointSetFixed)(dJointID);\r\n//void            (ODE_API *dJointSetFixedParam)(dJointID, int parameter, dReal value);\r\n//void            (ODE_API *dJointSetAMotorNumAxes)(dJointID, int num);\r\n//void            (ODE_API *dJointSetAMotorAxis)(dJointID, int anum, int rel, dReal x, dReal y, dReal z);\r\n//void            (ODE_API *dJointSetAMotorAngle)(dJointID, int anum, dReal angle);\r\n//void            (ODE_API *dJointSetAMotorParam)(dJointID, int parameter, dReal value);\r\n//void            (ODE_API *dJointSetAMotorMode)(dJointID, int mode);\r\n//void            (ODE_API *dJointAddAMotorTorques)(dJointID, dReal torque1, dReal torque2, dReal torque3);\r\n//void            (ODE_API *dJointSetLMotorNumAxes)(dJointID, int num);\r\n//void            (ODE_API *dJointSetLMotorAxis)(dJointID, int anum, int rel, dReal x, dReal y, dReal z);\r\n//void            (ODE_API *dJointSetLMotorParam)(dJointID, int parameter, dReal value);\r\n//void            (ODE_API *dJointSetPlane2DXParam)(dJointID, int parameter, dReal value);\r\n//void            (ODE_API *dJointSetPlane2DYParam)(dJointID, int parameter, dReal value);\r\n//void            (ODE_API *dJointSetPlane2DAngleParam)(dJointID, int parameter, dReal value);\r\nvoid            (ODE_API *dJointGetBallAnchor)(dJointID, dVector3 result);\r\n//void            (ODE_API *dJointGetBallAnchor2)(dJointID, dVector3 result);\r\n//dReal           (ODE_API *dJointGetBallParam)(dJointID, int parameter);\r\nvoid            (ODE_API *dJointGetHingeAnchor)(dJointID, dVector3 result);\r\n//void            (ODE_API *dJointGetHingeAnchor2)(dJointID, dVector3 result);\r\nvoid            (ODE_API *dJointGetHingeAxis)(dJointID, dVector3 result);\r\n//dReal           (ODE_API *dJointGetHingeParam)(dJointID, int parameter);\r\n//dReal           (ODE_API *dJointGetHingeAngle)(dJointID);\r\n//dReal           (ODE_API *dJointGetHingeAngleRate)(dJointID);\r\n//dReal           (ODE_API *dJointGetSliderPosition)(dJointID);\r\n//dReal           (ODE_API *dJointGetSliderPositionRate)(dJointID);\r\nvoid            (ODE_API *dJointGetSliderAxis)(dJointID, dVector3 result);\r\n//dReal           (ODE_API *dJointGetSliderParam)(dJointID, int parameter);\r\nvoid            (ODE_API *dJointGetHinge2Anchor)(dJointID, dVector3 result);\r\n//void            (ODE_API *dJointGetHinge2Anchor2)(dJointID, dVector3 result);\r\nvoid            (ODE_API *dJointGetHinge2Axis1)(dJointID, dVector3 result);\r\nvoid            (ODE_API *dJointGetHinge2Axis2)(dJointID, dVector3 result);\r\n//dReal           (ODE_API *dJointGetHinge2Param)(dJointID, int parameter);\r\n//dReal           (ODE_API *dJointGetHinge2Angle1)(dJointID);\r\n//dReal           (ODE_API *dJointGetHinge2Angle1Rate)(dJointID);\r\n//dReal           (ODE_API *dJointGetHinge2Angle2Rate)(dJointID);\r\nvoid            (ODE_API *dJointGetUniversalAnchor)(dJointID, dVector3 result);\r\n//void            (ODE_API *dJointGetUniversalAnchor2)(dJointID, dVector3 result);\r\nvoid            (ODE_API *dJointGetUniversalAxis1)(dJointID, dVector3 result);\r\nvoid            (ODE_API *dJointGetUniversalAxis2)(dJointID, dVector3 result);\r\n//dReal           (ODE_API *dJointGetUniversalParam)(dJointID, int parameter);\r\n//void            (ODE_API *dJointGetUniversalAngles)(dJointID, dReal *angle1, dReal *angle2);\r\n//dReal           (ODE_API *dJointGetUniversalAngle1)(dJointID);\r\n//dReal           (ODE_API *dJointGetUniversalAngle2)(dJointID);\r\n//dReal           (ODE_API *dJointGetUniversalAngle1Rate)(dJointID);\r\n//dReal           (ODE_API *dJointGetUniversalAngle2Rate)(dJointID);\r\n//void            (ODE_API *dJointGetPRAnchor)(dJointID, dVector3 result);\r\n//dReal           (ODE_API *dJointGetPRPosition)(dJointID);\r\n//dReal           (ODE_API *dJointGetPRPositionRate)(dJointID);\r\n//dReal           (ODE_API *dJointGetPRAngle)(dJointID);\r\n//dReal           (ODE_API *dJointGetPRAngleRate)(dJointID);\r\n//void            (ODE_API *dJointGetPRAxis1)(dJointID, dVector3 result);\r\n//void            (ODE_API *dJointGetPRAxis2)(dJointID, dVector3 result);\r\n//dReal           (ODE_API *dJointGetPRParam)(dJointID, int parameter);\r\n//void            (ODE_API *dJointGetPUAnchor)(dJointID, dVector3 result);\r\n//dReal           (ODE_API *dJointGetPUPosition)(dJointID);\r\n//dReal           (ODE_API *dJointGetPUPositionRate)(dJointID);\r\n//void            (ODE_API *dJointGetPUAxis1)(dJointID, dVector3 result);\r\n//void            (ODE_API *dJointGetPUAxis2)(dJointID, dVector3 result);\r\n//void            (ODE_API *dJointGetPUAxis3)(dJointID, dVector3 result);\r\n//void            (ODE_API *dJointGetPUAxisP)(dJointID id, dVector3 result);\r\n//void            (ODE_API *dJointGetPUAngles)(dJointID, dReal *angle1, dReal *angle2);\r\n//dReal           (ODE_API *dJointGetPUAngle1)(dJointID);\r\n//dReal           (ODE_API *dJointGetPUAngle1Rate)(dJointID);\r\n//dReal           (ODE_API *dJointGetPUAngle2)(dJointID);\r\n//dReal           (ODE_API *dJointGetPUAngle2Rate)(dJointID);\r\n//dReal           (ODE_API *dJointGetPUParam)(dJointID, int parameter);\r\n//dReal           (ODE_API *dJointGetPistonPosition)(dJointID);\r\n//dReal           (ODE_API *dJointGetPistonPositionRate)(dJointID);\r\n//dReal           (ODE_API *dJointGetPistonAngle)(dJointID);\r\n//dReal           (ODE_API *dJointGetPistonAngleRate)(dJointID);\r\n//void            (ODE_API *dJointGetPistonAnchor)(dJointID, dVector3 result);\r\n//void            (ODE_API *dJointGetPistonAnchor2)(dJointID, dVector3 result);\r\n//void            (ODE_API *dJointGetPistonAxis)(dJointID, dVector3 result);\r\n//dReal           (ODE_API *dJointGetPistonParam)(dJointID, int parameter);\r\n//int             (ODE_API *dJointGetAMotorNumAxes)(dJointID);\r\n//void            (ODE_API *dJointGetAMotorAxis)(dJointID, int anum, dVector3 result);\r\n//int             (ODE_API *dJointGetAMotorAxisRel)(dJointID, int anum);\r\n//dReal           (ODE_API *dJointGetAMotorAngle)(dJointID, int anum);\r\n//dReal           (ODE_API *dJointGetAMotorAngleRate)(dJointID, int anum);\r\n//dReal           (ODE_API *dJointGetAMotorParam)(dJointID, int parameter);\r\n//int             (ODE_API *dJointGetAMotorMode)(dJointID);\r\n//int             (ODE_API *dJointGetLMotorNumAxes)(dJointID);\r\n//void            (ODE_API *dJointGetLMotorAxis)(dJointID, int anum, dVector3 result);\r\n//dReal           (ODE_API *dJointGetLMotorParam)(dJointID, int parameter);\r\n//dReal           (ODE_API *dJointGetFixedParam)(dJointID, int parameter);\r\n//dJointID        (ODE_API *dConnectingJoint)(dBodyID, dBodyID);\r\n//int             (ODE_API *dConnectingJointList)(dBodyID, dBodyID, dJointID*);\r\nint             (ODE_API *dAreConnected)(dBodyID, dBodyID);\r\nint             (ODE_API *dAreConnectedExcluding)(dBodyID body1, dBodyID body2, int joint_type);\r\n//\r\ndSpaceID        (ODE_API *dSimpleSpaceCreate)(dSpaceID space);\r\ndSpaceID        (ODE_API *dHashSpaceCreate)(dSpaceID space);\r\ndSpaceID        (ODE_API *dQuadTreeSpaceCreate)(dSpaceID space, const dVector3 Center, const dVector3 Extents, int Depth);\r\n//dSpaceID        (ODE_API *dSweepAndPruneSpaceCreate)( dSpaceID space, int axisorder );\r\nvoid            (ODE_API *dSpaceDestroy)(dSpaceID);\r\n//void            (ODE_API *dHashSpaceSetLevels)(dSpaceID space, int minlevel, int maxlevel);\r\n//void            (ODE_API *dHashSpaceGetLevels)(dSpaceID space, int *minlevel, int *maxlevel);\r\n//void            (ODE_API *dSpaceSetCleanup)(dSpaceID space, int mode);\r\n//int             (ODE_API *dSpaceGetCleanup)(dSpaceID space);\r\n//void            (ODE_API *dSpaceSetSublevel)(dSpaceID space, int sublevel);\r\n//int             (ODE_API *dSpaceGetSublevel)(dSpaceID space);\r\n//void            (ODE_API *dSpaceSetManualCleanup)(dSpaceID space, int mode);\r\n//int             (ODE_API *dSpaceGetManualCleanup)(dSpaceID space);\r\n//void            (ODE_API *dSpaceAdd)(dSpaceID, dGeomID);\r\n//void            (ODE_API *dSpaceRemove)(dSpaceID, dGeomID);\r\n//int             (ODE_API *dSpaceQuery)(dSpaceID, dGeomID);\r\n//void            (ODE_API *dSpaceClean)(dSpaceID);\r\n//int             (ODE_API *dSpaceGetNumGeoms)(dSpaceID);\r\n//dGeomID         (ODE_API *dSpaceGetGeom)(dSpaceID, int i);\r\n//int             (ODE_API *dSpaceGetClass)(dSpaceID space);\r\n//\r\nvoid            (ODE_API *dGeomDestroy)(dGeomID geom);\r\nvoid            (ODE_API *dGeomSetData)(dGeomID geom, void* data);\r\nvoid *          (ODE_API *dGeomGetData)(dGeomID geom);\r\nvoid            (ODE_API *dGeomSetBody)(dGeomID geom, dBodyID body);\r\ndBodyID         (ODE_API *dGeomGetBody)(dGeomID geom);\r\nvoid            (ODE_API *dGeomSetPosition)(dGeomID geom, dReal x, dReal y, dReal z);\r\nvoid            (ODE_API *dGeomSetRotation)(dGeomID geom, const dMatrix3 R);\r\n//void            (ODE_API *dGeomSetQuaternion)(dGeomID geom, const dQuaternion Q);\r\n//const dReal *   (ODE_API *dGeomGetPosition)(dGeomID geom);\r\n//void            (ODE_API *dGeomCopyPosition)(dGeomID geom, dVector3 pos);\r\n//const dReal *   (ODE_API *dGeomGetRotation)(dGeomID geom);\r\n//void            (ODE_API *dGeomCopyRotation)(dGeomID geom, dMatrix3 R);\r\n//void            (ODE_API *dGeomGetQuaternion)(dGeomID geom, dQuaternion result);\r\n//void            (ODE_API *dGeomGetAABB)(dGeomID geom, dReal aabb[6]);\r\nint             (ODE_API *dGeomIsSpace)(dGeomID geom);\r\n//dSpaceID        (ODE_API *dGeomGetSpace)(dGeomID);\r\n//int             (ODE_API *dGeomGetClass)(dGeomID geom);\r\n//void            (ODE_API *dGeomSetCategoryBits)(dGeomID geom, unsigned long bits);\r\n//void            (ODE_API *dGeomSetCollideBits)(dGeomID geom, unsigned long bits);\r\n//unsigned long   (ODE_API *dGeomGetCategoryBits)(dGeomID);\r\n//unsigned long   (ODE_API *dGeomGetCollideBits)(dGeomID);\r\n//void            (ODE_API *dGeomEnable)(dGeomID geom);\r\n//void            (ODE_API *dGeomDisable)(dGeomID geom);\r\n//int             (ODE_API *dGeomIsEnabled)(dGeomID geom);\r\n//void            (ODE_API *dGeomSetOffsetPosition)(dGeomID geom, dReal x, dReal y, dReal z);\r\n//void            (ODE_API *dGeomSetOffsetRotation)(dGeomID geom, const dMatrix3 R);\r\n//void            (ODE_API *dGeomSetOffsetQuaternion)(dGeomID geom, const dQuaternion Q);\r\n//void            (ODE_API *dGeomSetOffsetWorldPosition)(dGeomID geom, dReal x, dReal y, dReal z);\r\n//void            (ODE_API *dGeomSetOffsetWorldRotation)(dGeomID geom, const dMatrix3 R);\r\n//void            (ODE_API *dGeomSetOffsetWorldQuaternion)(dGeomID geom, const dQuaternion);\r\n//void            (ODE_API *dGeomClearOffset)(dGeomID geom);\r\n//int             (ODE_API *dGeomIsOffset)(dGeomID geom);\r\n//const dReal *   (ODE_API *dGeomGetOffsetPosition)(dGeomID geom);\r\n//void            (ODE_API *dGeomCopyOffsetPosition)(dGeomID geom, dVector3 pos);\r\n//const dReal *   (ODE_API *dGeomGetOffsetRotation)(dGeomID geom);\r\n//void            (ODE_API *dGeomCopyOffsetRotation)(dGeomID geom, dMatrix3 R);\r\n//void            (ODE_API *dGeomGetOffsetQuaternion)(dGeomID geom, dQuaternion result);\r\nint             (ODE_API *dCollide)(dGeomID o1, dGeomID o2, int flags, dContactGeom *contact, int skip);\r\n//\r\nvoid            (ODE_API *dSpaceCollide)(dSpaceID space, void *data, dNearCallback *callback);\r\nvoid            (ODE_API *dSpaceCollide2)(dGeomID space1, dGeomID space2, void *data, dNearCallback *callback);\r\n//\r\ndGeomID         (ODE_API *dCreateSphere)(dSpaceID space, dReal radius);\r\n//void            (ODE_API *dGeomSphereSetRadius)(dGeomID sphere, dReal radius);\r\n//dReal           (ODE_API *dGeomSphereGetRadius)(dGeomID sphere);\r\n//dReal           (ODE_API *dGeomSpherePointDepth)(dGeomID sphere, dReal x, dReal y, dReal z);\r\n//\r\n//dGeomID         (ODE_API *dCreateConvex)(dSpaceID space, dReal *_planes, unsigned int _planecount, dReal *_points, unsigned int _pointcount,unsigned int *_polygons);\r\n//void            (ODE_API *dGeomSetConvex)(dGeomID g, dReal *_planes, unsigned int _count, dReal *_points, unsigned int _pointcount,unsigned int *_polygons);\r\n//\r\ndGeomID         (ODE_API *dCreateBox)(dSpaceID space, dReal lx, dReal ly, dReal lz);\r\n//void            (ODE_API *dGeomBoxSetLengths)(dGeomID box, dReal lx, dReal ly, dReal lz);\r\n//void            (ODE_API *dGeomBoxGetLengths)(dGeomID box, dVector3 result);\r\n//dReal           (ODE_API *dGeomBoxPointDepth)(dGeomID box, dReal x, dReal y, dReal z);\r\n//dReal           (ODE_API *dGeomBoxPointDepth)(dGeomID box, dReal x, dReal y, dReal z);\r\n//\r\n//dGeomID         (ODE_API *dCreatePlane)(dSpaceID space, dReal a, dReal b, dReal c, dReal d);\r\n//void            (ODE_API *dGeomPlaneSetParams)(dGeomID plane, dReal a, dReal b, dReal c, dReal d);\r\n//void            (ODE_API *dGeomPlaneGetParams)(dGeomID plane, dVector4 result);\r\n//dReal           (ODE_API *dGeomPlanePointDepth)(dGeomID plane, dReal x, dReal y, dReal z);\r\n//\r\ndGeomID         (ODE_API *dCreateCapsule)(dSpaceID space, dReal radius, dReal length);\r\n//void            (ODE_API *dGeomCapsuleSetParams)(dGeomID ccylinder, dReal radius, dReal length);\r\n//void            (ODE_API *dGeomCapsuleGetParams)(dGeomID ccylinder, dReal *radius, dReal *length);\r\n//dReal           (ODE_API *dGeomCapsulePointDepth)(dGeomID ccylinder, dReal x, dReal y, dReal z);\r\n//\r\ndGeomID         (ODE_API *dCreateCylinder)(dSpaceID space, dReal radius, dReal length);\r\n//void            (ODE_API *dGeomCylinderSetParams)(dGeomID cylinder, dReal radius, dReal length);\r\n//void            (ODE_API *dGeomCylinderGetParams)(dGeomID cylinder, dReal *radius, dReal *length);\r\n//\r\n//dGeomID         (ODE_API *dCreateRay)(dSpaceID space, dReal length);\r\n//void            (ODE_API *dGeomRaySetLength)(dGeomID ray, dReal length);\r\n//dReal           (ODE_API *dGeomRayGetLength)(dGeomID ray);\r\n//void            (ODE_API *dGeomRaySet)(dGeomID ray, dReal px, dReal py, dReal pz, dReal dx, dReal dy, dReal dz);\r\n//void            (ODE_API *dGeomRayGet)(dGeomID ray, dVector3 start, dVector3 dir);\r\n//\r\ndGeomID         (ODE_API *dCreateGeomTransform)(dSpaceID space);\r\nvoid            (ODE_API *dGeomTransformSetGeom)(dGeomID g, dGeomID obj);\r\n//dGeomID         (ODE_API *dGeomTransformGetGeom)(dGeomID g);\r\nvoid            (ODE_API *dGeomTransformSetCleanup)(dGeomID g, int mode);\r\n//int             (ODE_API *dGeomTransformGetCleanup)(dGeomID g);\r\n//void            (ODE_API *dGeomTransformSetInfo)(dGeomID g, int mode);\r\n//int             (ODE_API *dGeomTransformGetInfo)(dGeomID g);\r\n\r\nenum { TRIMESH_FACE_NORMALS };\r\ntypedef int dTriCallback(dGeomID TriMesh, dGeomID RefObject, int TriangleIndex);\r\ntypedef void dTriArrayCallback(dGeomID TriMesh, dGeomID RefObject, const int* TriIndices, int TriCount);\r\ntypedef int dTriRayCallback(dGeomID TriMesh, dGeomID Ray, int TriangleIndex, dReal u, dReal v);\r\ntypedef int dTriTriMergeCallback(dGeomID TriMesh, int FirstTriangleIndex, int SecondTriangleIndex);\r\n\r\ndTriMeshDataID  (ODE_API *dGeomTriMeshDataCreate)(void);\r\nvoid            (ODE_API *dGeomTriMeshDataDestroy)(dTriMeshDataID g);\r\n//void            (ODE_API *dGeomTriMeshDataSet)(dTriMeshDataID g, int data_id, void* in_data);\r\n//void*           (ODE_API *dGeomTriMeshDataGet)(dTriMeshDataID g, int data_id);\r\n//void            (*dGeomTriMeshSetLastTransform)( (ODE_API *dGeomID g, dMatrix4 last_trans );\r\n//dReal*          (*dGeomTriMeshGetLastTransform)( (ODE_API *dGeomID g );\r\nvoid            (ODE_API *dGeomTriMeshDataBuildSingle)(dTriMeshDataID g, const void* Vertices, int VertexStride, int VertexCount,  const void* Indices, int IndexCount, int TriStride);\r\n//void            (ODE_API *dGeomTriMeshDataBuildSingle1)(dTriMeshDataID g, const void* Vertices, int VertexStride, int VertexCount,  const void* Indices, int IndexCount, int TriStride, const void* Normals);\r\n//void            (ODE_API *dGeomTriMeshDataBuildDouble)(dTriMeshDataID g,  const void* Vertices,  int VertexStride, int VertexCount,  const void* Indices, int IndexCount, int TriStride);\r\n//void            (ODE_API *dGeomTriMeshDataBuildDouble1)(dTriMeshDataID g,  const void* Vertices,  int VertexStride, int VertexCount,  const void* Indices, int IndexCount, int TriStride, const void* Normals);\r\n//void            (ODE_API *dGeomTriMeshDataBuildSimple)(dTriMeshDataID g, const dReal* Vertices, int VertexCount, const dTriIndex* Indices, int IndexCount);\r\n//void            (ODE_API *dGeomTriMeshDataBuildSimple1)(dTriMeshDataID g, const dReal* Vertices, int VertexCount, const dTriIndex* Indices, int IndexCount, const int* Normals);\r\n//void            (ODE_API *dGeomTriMeshDataPreprocess)(dTriMeshDataID g);\r\n//void            (ODE_API *dGeomTriMeshDataGetBuffer)(dTriMeshDataID g, unsigned char** buf, int* bufLen);\r\n//void            (ODE_API *dGeomTriMeshDataSetBuffer)(dTriMeshDataID g, unsigned char* buf);\r\n//void            (ODE_API *dGeomTriMeshSetCallback)(dGeomID g, dTriCallback* Callback);\r\n//dTriCallback*   (ODE_API *dGeomTriMeshGetCallback)(dGeomID g);\r\n//void            (ODE_API *dGeomTriMeshSetArrayCallback)(dGeomID g, dTriArrayCallback* ArrayCallback);\r\n//dTriArrayCallback* (ODE_API *dGeomTriMeshGetArrayCallback)(dGeomID g);\r\n//void            (ODE_API *dGeomTriMeshSetRayCallback)(dGeomID g, dTriRayCallback* Callback);\r\n//dTriRayCallback* (ODE_API *dGeomTriMeshGetRayCallback)(dGeomID g);\r\n//void            (ODE_API *dGeomTriMeshSetTriMergeCallback)(dGeomID g, dTriTriMergeCallback* Callback);\r\n//dTriTriMergeCallback* (ODE_API *dGeomTriMeshGetTriMergeCallback)(dGeomID g);\r\ndGeomID         (ODE_API *dCreateTriMesh)(dSpaceID space, dTriMeshDataID Data, dTriCallback* Callback, dTriArrayCallback* ArrayCallback, dTriRayCallback* RayCallback);\r\n//void            (ODE_API *dGeomTriMeshSetData)(dGeomID g, dTriMeshDataID Data);\r\n//dTriMeshDataID  (ODE_API *dGeomTriMeshGetData)(dGeomID g);\r\n//void            (ODE_API *dGeomTriMeshEnableTC)(dGeomID g, int geomClass, int enable);\r\n//int             (ODE_API *dGeomTriMeshIsTCEnabled)(dGeomID g, int geomClass);\r\n//void            (ODE_API *dGeomTriMeshClearTCCache)(dGeomID g);\r\n//dTriMeshDataID  (ODE_API *dGeomTriMeshGetTriMeshDataID)(dGeomID g);\r\n//void            (ODE_API *dGeomTriMeshGetTriangle)(dGeomID g, int Index, dVector3* v0, dVector3* v1, dVector3* v2);\r\n//void            (ODE_API *dGeomTriMeshGetPoint)(dGeomID g, int Index, dReal u, dReal v, dVector3 Out);\r\n//int             (ODE_API *dGeomTriMeshGetTriangleCount )(dGeomID g);\r\n//void            (ODE_API *dGeomTriMeshDataUpdate)(dTriMeshDataID g);\r\n\r\ntypedef void dMessageFunction (int errnum, const char *msg, va_list ap);\r\nvoid (ODE_API  *dSetErrorHandler) (dMessageFunction *fn);\r\nvoid (ODE_API  *dSetDebugHandler) (dMessageFunction *fn);\r\nvoid (ODE_API  *dSetMessageHandler) (dMessageFunction *fn);\r\n\r\nstatic dllfunction_t odefuncs[] =\r\n{\r\n//\t{\"dGetConfiguration\",\t\t\t\t\t\t\t(void **) &dGetConfiguration},\r\n\t{(void **) &dCheckConfiguration,\t\t\t\t\"dCheckConfiguration\"},\r\n\t{(void **) &dInitODE,\t\t\t\t\t\t\t\"dInitODE\"},\r\n//\t{\"dInitODE2\",\t\t\t\t\t\t\t\t\t(void **) &dInitODE2},\r\n//\t{\"dAllocateODEDataForThread\",\t\t\t\t\t(void **) &dAllocateODEDataForThread},\r\n//\t{\"dCleanupODEAllDataForThread\",\t\t\t\t\t(void **) &dCleanupODEAllDataForThread},\r\n\t{(void **) &dCloseODE,\t\t\t\t\t\t\t\"dCloseODE\"},\r\n//\t{\"dMassCheck\",\t\t\t\t\t\t\t\t\t(void **) &dMassCheck},\r\n//\t{\"dMassSetZero\",\t\t\t\t\t\t\t\t(void **) &dMassSetZero},\r\n//\t{\"dMassSetParameters\",\t\t\t\t\t\t\t(void **) &dMassSetParameters},\r\n//\t{\"dMassSetSphere\",\t\t\t\t\t\t\t\t(void **) &dMassSetSphere},\r\n\t{(void **) &dMassSetSphereTotal,\t\t\t\t\"dMassSetSphereTotal\"},\r\n//\t{\"dMassSetCapsule\",\t\t\t\t\t\t\t\t(void **) &dMassSetCapsule},\r\n\t{(void **) &dMassSetCapsuleTotal,\t\t\t\t\"dMassSetCapsuleTotal\"},\r\n//\t{\"dMassSetCylinder\",\t\t\t\t\t\t\t(void **) &dMassSetCylinder},\r\n\t{(void **) &dMassSetCylinderTotal,\t\t\t\t\"dMassSetCylinderTotal\"},\r\n//\t{\"dMassSetBox\",\t\t\t\t\t\t\t\t\t(void **) &dMassSetBox},\r\n\t{(void **) &dMassSetBoxTotal,\t\t\t\t\t\"dMassSetBoxTotal\"},\r\n//\t{\"dMassSetTrimesh\",\t\t\t\t\t\t\t\t(void **) &dMassSetTrimesh},\r\n//\t{\"dMassSetTrimeshTotal\",\t\t\t\t\t\t(void **) &dMassSetTrimeshTotal},\r\n//\t{\"dMassAdjust\",\t\t\t\t\t\t\t\t\t(void **) &dMassAdjust},\r\n//\t{\"dMassTranslate\",\t\t\t\t\t\t\t\t(void **) &dMassTranslate},\r\n//\t{\"dMassRotate\",\t\t\t\t\t\t\t\t\t(void **) &dMassRotate},\r\n//\t{\"dMassAdd\",\t\t\t\t\t\t\t\t\t(void **) &dMassAdd},\r\n\r\n\t{(void **) &dWorldCreate,\t\t\t\t\t\t\"dWorldCreate\"},\r\n\t{(void **) &dWorldDestroy,\t\t\t\t\t\t\"dWorldDestroy\"},\r\n\t{(void **) &dWorldSetGravity,\t\t\t\t\t\"dWorldSetGravity\"},\r\n\t{(void **) &dWorldGetGravity,\t\t\t\t\t\"dWorldGetGravity\"},\r\n\t{(void **) &dWorldSetERP,\t\t\t\t\t\t\"dWorldSetERP\"},\r\n//\t{\"dWorldGetERP\",\t\t\t\t\t\t\t\t(void **) &dWorldGetERP},\r\n\t{(void **) &dWorldSetCFM,\t\t\t\t\t\t\"dWorldSetCFM\"},\r\n//\t{\"dWorldGetCFM\",\t\t\t\t\t\t\t\t(void **) &dWorldGetCFM},\r\n\t{(void **) &dWorldStep,\t\t\t\t\t\t\t\"dWorldStep\"},\r\n//\t{\"dWorldImpulseToForce\",\t\t\t\t\t\t(void **) &dWorldImpulseToForce},\r\n\t{(void **) &dWorldQuickStep,\t\t\t\t\t\"dWorldQuickStep\"},\r\n\t{(void **) &dWorldSetQuickStepNumIterations,\t\"dWorldSetQuickStepNumIterations\"},\r\n//\t{\"dWorldGetQuickStepNumIterations\",\t\t\t\t(void **) &dWorldGetQuickStepNumIterations},\r\n//\t{\"dWorldSetQuickStepW\",\t\t\t\t\t\t\t(void **) &dWorldSetQuickStepW},\r\n//\t{\"dWorldGetQuickStepW\",\t\t\t\t\t\t\t(void **) &dWorldGetQuickStepW},\r\n//\t{\"dWorldSetContactMaxCorrectingVel\",\t\t\t(void **) &dWorldSetContactMaxCorrectingVel},\r\n//\t{\"dWorldGetContactMaxCorrectingVel\",\t\t\t(void **) &dWorldGetContactMaxCorrectingVel},\r\n\t{(void **) &dWorldSetContactSurfaceLayer,\t\t\"dWorldSetContactSurfaceLayer\"},\r\n//\t{\"dWorldGetContactSurfaceLayer\",\t\t\t\t(void **) &dWorldGetContactSurfaceLayer},\r\n//\t{(void **) &dWorldStepFast1,\t\t\t\t\t\"dWorldStepFast1\"},\r\n//\t{\"dWorldSetAutoEnableDepthSF1\",\t\t\t\t\t(void **) &dWorldSetAutoEnableDepthSF1},\r\n//\t{\"dWorldGetAutoEnableDepthSF1\",\t\t\t\t\t(void **) &dWorldGetAutoEnableDepthSF1},\r\n//\t{\"dWorldGetAutoDisableLinearThreshold\",\t\t\t(void **) &dWorldGetAutoDisableLinearThreshold},\r\n\t{(void **) &dWorldSetAutoDisableLinearThreshold,\"dWorldSetAutoDisableLinearThreshold\"},\r\n//\t{\"dWorldGetAutoDisableAngularThreshold\",\t\t(void **) &dWorldGetAutoDisableAngularThreshold},\r\n\t{(void **) &dWorldSetAutoDisableAngularThreshold,\"dWorldSetAutoDisableAngularThreshold\"},\r\n//\t{\"dWorldGetAutoDisableLinearAverageThreshold\",\t(void **) &dWorldGetAutoDisableLinearAverageThreshold},\r\n//\t{\"dWorldSetAutoDisableLinearAverageThreshold\",\t(void **) &dWorldSetAutoDisableLinearAverageThreshold},\r\n//\t{\"dWorldGetAutoDisableAngularAverageThreshold\",\t(void **) &dWorldGetAutoDisableAngularAverageThreshold},\r\n//\t{\"dWorldSetAutoDisableAngularAverageThreshold\",\t(void **) &dWorldSetAutoDisableAngularAverageThreshold},\r\n//\t{\"dWorldGetAutoDisableAverageSamplesCount\",\t\t(void **) &dWorldGetAutoDisableAverageSamplesCount},\r\n\t{(void **)&dWorldSetAutoDisableAverageSamplesCount,\t\t\"dWorldSetAutoDisableAverageSamplesCount\"},\r\n//\t{\"dWorldGetAutoDisableSteps\",\t\t\t\t\t(void **) &dWorldGetAutoDisableSteps},\r\n\t{(void **) &dWorldSetAutoDisableSteps,\t\t\t\"dWorldSetAutoDisableSteps\"},\r\n//\t{\"dWorldGetAutoDisableTime\",\t\t\t\t\t(void **) &dWorldGetAutoDisableTime},\r\n\t{(void **) &dWorldSetAutoDisableTime,\t\t\t\"dWorldSetAutoDisableTime\"},\r\n//\t{\"dWorldGetAutoDisableFlag\",\t\t\t\t\t(void **) &dWorldGetAutoDisableFlag},\r\n\t{(void **) &dWorldSetAutoDisableFlag,\t\t\t\"dWorldSetAutoDisableFlag\"},\r\n//\t{\"dWorldGetLinearDampingThreshold\",\t\t\t\t(void **) &dWorldGetLinearDampingThreshold},\r\n\t{(void **) &dWorldSetLinearDampingThreshold,\t\"dWorldSetLinearDampingThreshold\"},\r\n//\t{\"dWorldGetAngularDampingThreshold\",\t\t\t(void **) &dWorldGetAngularDampingThreshold},\r\n\t{(void **) &dWorldSetAngularDampingThreshold,\t\"dWorldSetAngularDampingThreshold\"},\r\n//\t{\"dWorldGetLinearDamping\",\t\t\t\t\t\t(void **) &dWorldGetLinearDamping},\r\n\t{(void **) &dWorldSetLinearDamping,\t\t\t\t\"dWorldSetLinearDamping\"},\r\n//\t{\"dWorldGetAngularDamping\",\t\t\t\t\t\t(void **) &dWorldGetAngularDamping},\r\n\t{(void **) &dWorldSetAngularDamping,\t\t\t\"dWorldSetAngularDamping\"},\r\n//\t{(void **) &dWorldSetDamping,\t\t\t\t\t\"dWorldSetDamping\"},\r\n//\t{\"dWorldGetMaxAngularSpeed\",\t\t\t\t\t(void **) &dWorldGetMaxAngularSpeed},\r\n//\t{\"dWorldSetMaxAngularSpeed\",\t\t\t\t\t(void **) &dWorldSetMaxAngularSpeed},\r\n//\t{\"dBodyGetAutoDisableLinearThreshold\",\t\t\t(void **) &dBodyGetAutoDisableLinearThreshold},\r\n//\t{\"dBodySetAutoDisableLinearThreshold\",\t\t\t(void **) &dBodySetAutoDisableLinearThreshold},\r\n//\t{\"dBodyGetAutoDisableAngularThreshold\",\t\t\t(void **) &dBodyGetAutoDisableAngularThreshold},\r\n//\t{\"dBodySetAutoDisableAngularThreshold\",\t\t\t(void **) &dBodySetAutoDisableAngularThreshold},\r\n//\t{\"dBodyGetAutoDisableAverageSamplesCount\",\t\t(void **) &dBodyGetAutoDisableAverageSamplesCount},\r\n//\t{\"dBodySetAutoDisableAverageSamplesCount\",\t\t(void **) &dBodySetAutoDisableAverageSamplesCount},\r\n//\t{\"dBodyGetAutoDisableSteps\",\t\t\t\t\t(void **) &dBodyGetAutoDisableSteps},\r\n//\t{\"dBodySetAutoDisableSteps\",\t\t\t\t\t(void **) &dBodySetAutoDisableSteps},\r\n//\t{\"dBodyGetAutoDisableTime\",\t\t\t\t\t\t(void **) &dBodyGetAutoDisableTime},\r\n//\t{\"dBodySetAutoDisableTime\",\t\t\t\t\t\t(void **) &dBodySetAutoDisableTime},\r\n//\t{\"dBodyGetAutoDisableFlag\",\t\t\t\t\t\t(void **) &dBodyGetAutoDisableFlag},\r\n//\t{\"dBodySetAutoDisableFlag\",\t\t\t\t\t\t(void **) &dBodySetAutoDisableFlag},\r\n//\t{\"dBodySetAutoDisableDefaults\",\t\t\t\t\t(void **) &dBodySetAutoDisableDefaults},\r\n//\t{\"dBodyGetWorld\",\t\t\t\t\t\t\t\t(void **) &dBodyGetWorld},\r\n\t{(void **) &dBodyCreate,\t\t\t\t\t\t\"dBodyCreate\"},\r\n\t{(void **) &dBodyDestroy,\t\t\t\t\t\t\"dBodyDestroy\"},\r\n\t{(void **) &dBodySetData,\t\t\t\t\t\t\"dBodySetData\"},\r\n\t{(void **) &dBodyGetData,\t\t\t\t\t\t\"dBodyGetData\"},\r\n\t{(void **) &dBodySetPosition,\t\t\t\t\t\"dBodySetPosition\"},\r\n\t{(void **) &dBodySetRotation,\t\t\t\t\t\"dBodySetRotation\"},\r\n//\t{\"dBodySetQuaternion\",\t\t\t\t\t\t\t(void **) &dBodySetQuaternion},\r\n\t{(void **) &dBodySetLinearVel,\t\t\t\t\t\"dBodySetLinearVel\"},\r\n\t{(void **) &dBodySetAngularVel,\t\t\t\t\t\"dBodySetAngularVel\"},\r\n\t{(void **) &dBodyGetPosition,\t\t\t\t\t\"dBodyGetPosition\"},\r\n//\t{\"dBodyCopyPosition\",\t\t\t\t\t\t\t(void **) &dBodyCopyPosition},\r\n\t{(void **) &dBodyGetRotation,\t\t\t\t\t\"dBodyGetRotation\"},\r\n//\t{\"dBodyCopyRotation\",\t\t\t\t\t\t\t(void **) &dBodyCopyRotation},\r\n//\t{\"dBodyGetQuaternion\",\t\t\t\t\t\t\t(void **) &dBodyGetQuaternion},\r\n//\t{\"dBodyCopyQuaternion\",\t\t\t\t\t\t\t(void **) &dBodyCopyQuaternion},\r\n\t{(void **) &dBodyGetLinearVel,\t\t\t\t\t\"dBodyGetLinearVel\"},\r\n\t{(void **) &dBodyGetAngularVel,\t\t\t\t\t\"dBodyGetAngularVel\"},\r\n\t{(void **) &dBodySetMass,\t\t\t\t\t\t\"dBodySetMass\"},\r\n//\t{\"dBodyGetMass\",\t\t\t\t\t\t\t\t(void **) &dBodyGetMass},\r\n//\t{\"dBodyAddForce\",\t\t\t\t\t\t\t\t(void **) &dBodyAddForce},\r\n\t{(void **) &dBodyAddTorque,\t\t\t\t\t\t\"dBodyAddTorque\"},\r\n//\t{\"dBodyAddRelForce\",\t\t\t\t\t\t\t(void **) &dBodyAddRelForce},\r\n//\t{\"dBodyAddRelTorque\",\t\t\t\t\t\t\t(void **) &dBodyAddRelTorque},\r\n\t{(void **) &dBodyAddForceAtPos,\t\t\t\t\t\"dBodyAddForceAtPos\"},\r\n//\t{\"dBodyAddForceAtRelPos\",\t\t\t\t\t\t(void **) &dBodyAddForceAtRelPos},\r\n//\t{\"dBodyAddRelForceAtPos\",\t\t\t\t\t\t(void **) &dBodyAddRelForceAtPos},\r\n//\t{\"dBodyAddRelForceAtRelPos\",\t\t\t\t\t(void **) &dBodyAddRelForceAtRelPos},\r\n//\t{\"dBodyGetForce\",\t\t\t\t\t\t\t\t(void **) &dBodyGetForce},\r\n//\t{\"dBodyGetTorque\",\t\t\t\t\t\t\t\t(void **) &dBodyGetTorque},\r\n//\t{\"dBodySetForce\",\t\t\t\t\t\t\t\t(void **) &dBodySetForce},\r\n//\t{\"dBodySetTorque\",\t\t\t\t\t\t\t\t(void **) &dBodySetTorque},\r\n//\t{\"dBodyGetRelPointPos\",\t\t\t\t\t\t\t(void **) &dBodyGetRelPointPos},\r\n//\t{\"dBodyGetRelPointVel\",\t\t\t\t\t\t\t(void **) &dBodyGetRelPointVel},\r\n//\t{\"dBodyGetPointVel\",\t\t\t\t\t\t\t(void **) &dBodyGetPointVel},\r\n//\t{\"dBodyGetPosRelPoint\",\t\t\t\t\t\t\t(void **) &dBodyGetPosRelPoint},\r\n//\t{\"dBodyVectorToWorld\",\t\t\t\t\t\t\t(void **) &dBodyVectorToWorld},\r\n//\t{\"dBodyVectorFromWorld\",\t\t\t\t\t\t(void **) &dBodyVectorFromWorld},\r\n//\t{\"dBodySetFiniteRotationMode\",\t\t\t\t\t(void **) &dBodySetFiniteRotationMode},\r\n//\t{\"dBodySetFiniteRotationAxis\",\t\t\t\t\t(void **) &dBodySetFiniteRotationAxis},\r\n//\t{\"dBodyGetFiniteRotationMode\",\t\t\t\t\t(void **) &dBodyGetFiniteRotationMode},\r\n//\t{\"dBodyGetFiniteRotationAxis\",\t\t\t\t\t(void **) &dBodyGetFiniteRotationAxis},\r\n\t{(void **) &dBodyGetNumJoints,\t\t\t\t\t\"dBodyGetNumJoints\"},\r\n\t{(void **) &dBodyGetJoint,\t\t\t\t\t\t\"dBodyGetJoint\"},\r\n//\t{\"dBodySetDynamic\",\t\t\t\t\t\t\t\t(void **) &dBodySetDynamic},\r\n//\t{\"dBodySetKinematic\",\t\t\t\t\t\t\t(void **) &dBodySetKinematic},\r\n//\t{\"dBodyIsKinematic\",\t\t\t\t\t\t\t(void **) &dBodyIsKinematic},\r\n\t{(void **) &dBodyEnable,\t\t\t\t\t\t\"dBodyEnable\"},\r\n\t{(void **) &dBodyDisable,\t\t\t\t\t\t\"dBodyDisable\"},\r\n//\t{\"dBodyIsEnabled\",\t\t\t\t\t\t\t\t(void **) &dBodyIsEnabled},\r\n\t{(void **) &dBodySetGravityMode,\t\t\t\t\"dBodySetGravityMode\"},\r\n\t{(void **) &dBodyGetGravityMode,\t\t\t\t\"dBodyGetGravityMode\"},\r\n//\t{\"dBodySetMovedCallback\",\t\t\t\t\t\t(void **) &dBodySetMovedCallback},\r\n//\t{\"dBodyGetFirstGeom\",\t\t\t\t\t\t\t(void **) &dBodyGetFirstGeom},\r\n//\t{\"dBodyGetNextGeom\",\t\t\t\t\t\t\t(void **) &dBodyGetNextGeom},\r\n//\t{\"dBodySetDampingDefaults\",\t\t\t\t\t\t(void **) &dBodySetDampingDefaults},\r\n//\t{\"dBodyGetLinearDamping\",\t\t\t\t\t\t(void **) &dBodyGetLinearDamping},\r\n\t{\"dBodySetLinearDamping\",\t\t\t\t\t\t(void **) &dBodySetLinearDamping},\r\n//\t{\"dBodyGetAngularDamping\",\t\t\t\t\t\t(void **) &dBodyGetAngularDamping},\r\n\t{\"dBodySetAngularDamping\",\t\t\t\t\t\t(void **) &dBodySetAngularDamping},\r\n//\t{\"dBodySetDamping\",\t\t\t\t\t\t\t\t(void **) &dBodySetDamping},\r\n//\t{\"dBodyGetLinearDampingThreshold\",\t\t\t\t(void **) &dBodyGetLinearDampingThreshold},\r\n//\t{\"dBodySetLinearDampingThreshold\",\t\t\t\t(void **) &dBodySetLinearDampingThreshold},\r\n//\t{\"dBodyGetAngularDampingThreshold\",\t\t\t\t(void **) &dBodyGetAngularDampingThreshold},\r\n//\t{\"dBodySetAngularDampingThreshold\",\t\t\t\t(void **) &dBodySetAngularDampingThreshold},\r\n//\t{\"dBodyGetMaxAngularSpeed\",\t\t\t\t\t\t(void **) &dBodyGetMaxAngularSpeed},\r\n\t{\"dBodySetMaxAngularSpeed\",\t\t\t\t\t\t(void **) &dBodySetMaxAngularSpeed},\r\n//\t{\"dBodyGetGyroscopicMode\",\t\t\t\t\t\t(void **) &dBodyGetGyroscopicMode},\r\n//\t{\"dBodySetGyroscopicMode\",\t\t\t\t\t\t(void **) &dBodySetGyroscopicMode},\r\n\t{(void **) &dJointCreateBall,\t\t\t\t\t\"dJointCreateBall\"},\r\n\t{(void **) &dJointCreateHinge,\t\t\t\t\t\"dJointCreateHinge\"},\r\n\t{(void **) &dJointCreateSlider,\t\t\t\t\t\"dJointCreateSlider\"},\r\n\t{(void **) &dJointCreateContact,\t\t\t\t\"dJointCreateContact\"},\r\n\t{(void **) &dJointCreateHinge2,\t\t\t\t\t\"dJointCreateHinge2\"},\r\n\t{(void **) &dJointCreateUniversal,\t\t\t\t\"dJointCreateUniversal\"},\r\n//\t{\"dJointCreatePR\",\t\t\t\t\t\t\t\t(void **) &dJointCreatePR},\r\n//\t{\"dJointCreatePU\",\t\t\t\t\t\t\t\t(void **) &dJointCreatePU},\r\n//\t{\"dJointCreatePiston\",\t\t\t\t\t\t\t(void **) &dJointCreatePiston},\r\n\t{(void **) &dJointCreateFixed,\t\t\t\t\t\"dJointCreateFixed\"},\r\n//\t{\"dJointCreateNull\",\t\t\t\t\t\t\t(void **) &dJointCreateNull},\r\n//\t{\"dJointCreateAMotor\",\t\t\t\t\t\t\t(void **) &dJointCreateAMotor},\r\n//\t{\"dJointCreateLMotor\",\t\t\t\t\t\t\t(void **) &dJointCreateLMotor},\r\n//\t{\"dJointCreatePlane2D\",\t\t\t\t\t\t\t(void **) &dJointCreatePlane2D},\r\n\t{(void **) &dJointDestroy,\t\t\t\t\t\t\"dJointDestroy\"},\r\n\t{(void **) &dJointGroupCreate,\t\t\t\t\t\"dJointGroupCreate\"},\r\n\t{(void **) &dJointGroupDestroy,\t\t\t\t\t\"dJointGroupDestroy\"},\r\n\t{(void **) &dJointGroupEmpty,\t\t\t\t\t\"dJointGroupEmpty\"},\r\n//\t{\"dJointGetNumBodies\",\t\t\t\t\t\t\t(void **) &dJointGetNumBodies},\r\n\t{(void **) &dJointAttach,\t\t\t\t\t\t\"dJointAttach\"},\r\n\t{(void **) &dJointEnable,\t\t\t\t\t\t\"dJointEnable\"},\r\n\t{(void **) &dJointDisable,\t\t\t\t\t\t\"dJointDisable\"},\r\n//\t{\"dJointIsEnabled\",\t\t\t\t\t\t\t\t(void **) &dJointIsEnabled},\r\n\t{(void **) &dJointSetData,\t\t\t\t\t\t\"dJointSetData\"},\r\n\t{(void **) &dJointGetData,\t\t\t\t\t\t\"dJointGetData\"},\r\n//\t{\"dJointGetType\",\t\t\t\t\t\t\t\t(void **) &dJointGetType},\r\n\t{(void **) &dJointGetBody,\t\t\t\t\t\t\"dJointGetBody\"},\r\n//\t{\"dJointSetFeedback\",\t\t\t\t\t\t\t(void **) &dJointSetFeedback},\r\n//\t{\"dJointGetFeedback\",\t\t\t\t\t\t\t(void **) &dJointGetFeedback},\r\n\t{(void **) &dJointSetBallAnchor,\t\t\t\t\"dJointSetBallAnchor\"},\r\n//\t{\"dJointSetBallAnchor2\",\t\t\t\t\t\t(void **) &dJointSetBallAnchor2},\r\n\t{(void **) &dJointSetBallParam,\t\t\t\t\t\"dJointSetBallParam\"},\r\n\t{(void **) &dJointSetHingeAnchor,\t\t\t\t\"dJointSetHingeAnchor\"},\r\n//\t{\"dJointSetHingeAnchorDelta\",\t\t\t\t\t(void **) &dJointSetHingeAnchorDelta},\r\n\t{(void **) &dJointSetHingeAxis,\t\t\t\t\t\"dJointSetHingeAxis\"},\r\n//\t{\"dJointSetHingeAxisOffset\",\t\t\t\t\t(void **) &dJointSetHingeAxisOffset},\r\n\t{(void **) &dJointSetHingeParam,\t\t\t\t\"dJointSetHingeParam\"},\r\n//\t{\"dJointAddHingeTorque\",\t\t\t\t\t\t(void **) &dJointAddHingeTorque},\r\n\t{(void **) &dJointSetSliderAxis,\t\t\t\t\"dJointSetSliderAxis\"},\r\n//\t{\"dJointSetSliderAxisDelta\",\t\t\t\t\t(void **) &dJointSetSliderAxisDelta},\r\n\t{(void **) &dJointSetSliderParam,\t\t\t\t\"dJointSetSliderParam\"},\r\n//\t{\"dJointAddSliderForce\",\t\t\t\t\t\t(void **) &dJointAddSliderForce},\r\n\t{(void **) &dJointSetHinge2Anchor,\t\t\t\t\"dJointSetHinge2Anchor\"},\r\n\t{(void **) &dJointSetHinge2Axis1,\t\t\t\t\"dJointSetHinge2Axis1\"},\r\n\t{(void **) &dJointSetHinge2Axis2,\t\t\t\t\"dJointSetHinge2Axis2\"},\r\n\t{(void **) &dJointSetHinge2Param,\t\t\t\t\"dJointSetHinge2Param\"},\r\n//\t{\"dJointAddHinge2Torques\",\t\t\t\t\t\t(void **) &dJointAddHinge2Torques},\r\n\t{(void **) &dJointSetUniversalAnchor,\t\t\t\"dJointSetUniversalAnchor\"},\r\n\t{(void **) &dJointSetUniversalAxis1,\t\t\t\"dJointSetUniversalAxis1\"},\r\n//\t{\"dJointSetUniversalAxis1Offset\",\t\t\t\t(void **) &dJointSetUniversalAxis1Offset},\r\n\t{(void **) &dJointSetUniversalAxis2,\t\t\t\"dJointSetUniversalAxis2\"},\r\n//\t{\"dJointSetUniversalAxis2Offset\",\t\t\t\t(void **) &dJointSetUniversalAxis2Offset},\r\n\t{(void **) &dJointSetUniversalParam,\t\t\t\"dJointSetUniversalParam\"},\r\n//\t{\"dJointAddUniversalTorques\",\t\t\t\t\t(void **) &dJointAddUniversalTorques},\r\n//\t{\"dJointSetPRAnchor\",\t\t\t\t\t\t\t(void **) &dJointSetPRAnchor},\r\n//\t{\"dJointSetPRAxis1\",\t\t\t\t\t\t\t(void **) &dJointSetPRAxis1},\r\n//\t{\"dJointSetPRAxis2\",\t\t\t\t\t\t\t(void **) &dJointSetPRAxis2},\r\n//\t{\"dJointSetPRParam\",\t\t\t\t\t\t\t(void **) &dJointSetPRParam},\r\n//\t{\"dJointAddPRTorque\",\t\t\t\t\t\t\t(void **) &dJointAddPRTorque},\r\n//\t{\"dJointSetPUAnchor\",\t\t\t\t\t\t\t(void **) &dJointSetPUAnchor},\r\n//\t{\"dJointSetPUAnchorOffset\",\t\t\t\t\t\t(void **) &dJointSetPUAnchorOffset},\r\n//\t{\"dJointSetPUAxis1\",\t\t\t\t\t\t\t(void **) &dJointSetPUAxis1},\r\n//\t{\"dJointSetPUAxis2\",\t\t\t\t\t\t\t(void **) &dJointSetPUAxis2},\r\n//\t{\"dJointSetPUAxis3\",\t\t\t\t\t\t\t(void **) &dJointSetPUAxis3},\r\n//\t{\"dJointSetPUAxisP\",\t\t\t\t\t\t\t(void **) &dJointSetPUAxisP},\r\n//\t{\"dJointSetPUParam\",\t\t\t\t\t\t\t(void **) &dJointSetPUParam},\r\n//\t{\"dJointAddPUTorque\",\t\t\t\t\t\t\t(void **) &dJointAddPUTorque},\r\n//\t{\"dJointSetPistonAnchor\",\t\t\t\t\t\t(void **) &dJointSetPistonAnchor},\r\n//\t{\"dJointSetPistonAnchorOffset\",\t\t\t\t\t(void **) &dJointSetPistonAnchorOffset},\r\n//\t{\"dJointSetPistonParam\",\t\t\t\t\t\t(void **) &dJointSetPistonParam},\r\n//\t{\"dJointAddPistonForce\",\t\t\t\t\t\t(void **) &dJointAddPistonForce},\r\n\t{(void **) &dJointSetFixed,\t\t\t\t\t\t\"dJointSetFixed\"},\r\n//\t{\"dJointSetFixedParam\",\t\t\t\t\t\t\t(void **) &dJointSetFixedParam},\r\n//\t{\"dJointSetAMotorNumAxes\",\t\t\t\t\t\t(void **) &dJointSetAMotorNumAxes},\r\n//\t{\"dJointSetAMotorAxis\",\t\t\t\t\t\t\t(void **) &dJointSetAMotorAxis},\r\n//\t{\"dJointSetAMotorAngle\",\t\t\t\t\t\t(void **) &dJointSetAMotorAngle},\r\n//\t{\"dJointSetAMotorParam\",\t\t\t\t\t\t(void **) &dJointSetAMotorParam},\r\n//\t{\"dJointSetAMotorMode\",\t\t\t\t\t\t\t(void **) &dJointSetAMotorMode},\r\n//\t{\"dJointAddAMotorTorques\",\t\t\t\t\t\t(void **) &dJointAddAMotorTorques},\r\n//\t{\"dJointSetLMotorNumAxes\",\t\t\t\t\t\t(void **) &dJointSetLMotorNumAxes},\r\n//\t{\"dJointSetLMotorAxis\",\t\t\t\t\t\t\t(void **) &dJointSetLMotorAxis},\r\n//\t{\"dJointSetLMotorParam\",\t\t\t\t\t\t(void **) &dJointSetLMotorParam},\r\n//\t{\"dJointSetPlane2DXParam\",\t\t\t\t\t\t(void **) &dJointSetPlane2DXParam},\r\n//\t{\"dJointSetPlane2DYParam\",\t\t\t\t\t\t(void **) &dJointSetPlane2DYParam},\r\n//\t{\"dJointSetPlane2DAngleParam\",\t\t\t\t\t(void **) &dJointSetPlane2DAngleParam},\r\n\t{(void **) &dJointGetBallAnchor,\t\t\t\t\"dJointGetBallAnchor\"},\r\n//\t{\"dJointGetBallAnchor2\",\t\t\t\t\t\t(void **) &dJointGetBallAnchor2},\r\n//\t{\"dJointGetBallParam\",\t\t\t\t\t\t\t(void **) &dJointGetBallParam},\r\n\t{(void **) &dJointGetHingeAnchor,\t\t\t\t\"dJointGetHingeAnchor\"},\r\n//\t{\"dJointGetHingeAnchor2\",\t\t\t\t\t\t(void **) &dJointGetHingeAnchor2},\r\n\t{(void **) &dJointGetHingeAxis,\t\t\t\t\t\"dJointGetHingeAxis\"},\r\n//\t{\"dJointGetHingeParam\",\t\t\t\t\t\t\t(void **) &dJointGetHingeParam},\r\n//\t{\"dJointGetHingeAngle\",\t\t\t\t\t\t\t(void **) &dJointGetHingeAngle},\r\n//\t{\"dJointGetHingeAngleRate\",\t\t\t\t\t\t(void **) &dJointGetHingeAngleRate},\r\n//\t{\"dJointGetSliderPosition\",\t\t\t\t\t\t(void **) &dJointGetSliderPosition},\r\n//\t{\"dJointGetSliderPositionRate\",\t\t\t\t\t(void **) &dJointGetSliderPositionRate},\r\n\t{(void **) &dJointGetSliderAxis,\t\t\t\t\"dJointGetSliderAxis\"},\r\n//\t{\"dJointGetSliderParam\",\t\t\t\t\t\t(void **) &dJointGetSliderParam},\r\n//\t{\"dJointGetHinge2Anchor\",\t\t\t\t\t\t(void **) &dJointGetHinge2Anchor},\r\n//\t{\"dJointGetHinge2Anchor2\",\t\t\t\t\t\t(void **) &dJointGetHinge2Anchor2},\r\n\t{(void **) &dJointGetHinge2Axis1,\t\t\t\t\"dJointGetHinge2Axis1\"},\r\n\t{(void **) &dJointGetHinge2Axis2,\t\t\t\t\"dJointGetHinge2Axis2\"},\r\n//\t{\"dJointGetHinge2Param\",\t\t\t\t\t\t(void **) &dJointGetHinge2Param},\r\n//\t{\"dJointGetHinge2Angle1\",\t\t\t\t\t\t(void **) &dJointGetHinge2Angle1},\r\n//\t{\"dJointGetHinge2Angle1Rate\",\t\t\t\t\t(void **) &dJointGetHinge2Angle1Rate},\r\n//\t{\"dJointGetHinge2Angle2Rate\",\t\t\t\t\t(void **) &dJointGetHinge2Angle2Rate},\r\n\t{(void **) &dJointGetUniversalAnchor,\t\t\t\"dJointGetUniversalAnchor\"},\r\n//\t{\"dJointGetUniversalAnchor2\",\t\t\t\t\t(void **) &dJointGetUniversalAnchor2},\r\n\t{(void **) &dJointGetUniversalAxis1,\t\t\t\"dJointGetUniversalAxis1\"},\r\n\t{(void **) &dJointGetUniversalAxis2,\t\t\t\"dJointGetUniversalAxis2\"},\r\n//\t{\"dJointGetUniversalParam\",\t\t\t\t\t\t(void **) &dJointGetUniversalParam},\r\n//\t{\"dJointGetUniversalAngles\",\t\t\t\t\t(void **) &dJointGetUniversalAngles},\r\n//\t{\"dJointGetUniversalAngle1\",\t\t\t\t\t(void **) &dJointGetUniversalAngle1},\r\n//\t{\"dJointGetUniversalAngle2\",\t\t\t\t\t(void **) &dJointGetUniversalAngle2},\r\n//\t{\"dJointGetUniversalAngle1Rate\",\t\t\t\t(void **) &dJointGetUniversalAngle1Rate},\r\n//\t{\"dJointGetUniversalAngle2Rate\",\t\t\t\t(void **) &dJointGetUniversalAngle2Rate},\r\n//\t{\"dJointGetPRAnchor\",\t\t\t\t\t\t\t(void **) &dJointGetPRAnchor},\r\n//\t{\"dJointGetPRPosition\",\t\t\t\t\t\t\t(void **) &dJointGetPRPosition},\r\n//\t{\"dJointGetPRPositionRate\",\t\t\t\t\t\t(void **) &dJointGetPRPositionRate},\r\n//\t{\"dJointGetPRAngle\",\t\t\t\t\t\t\t(void **) &dJointGetPRAngle},\r\n//\t{\"dJointGetPRAngleRate\",\t\t\t\t\t\t(void **) &dJointGetPRAngleRate},\r\n//\t{\"dJointGetPRAxis1\",\t\t\t\t\t\t\t(void **) &dJointGetPRAxis1},\r\n//\t{\"dJointGetPRAxis2\",\t\t\t\t\t\t\t(void **) &dJointGetPRAxis2},\r\n//\t{\"dJointGetPRParam\",\t\t\t\t\t\t\t(void **) &dJointGetPRParam},\r\n//\t{\"dJointGetPUAnchor\",\t\t\t\t\t\t\t(void **) &dJointGetPUAnchor},\r\n//\t{\"dJointGetPUPosition\",\t\t\t\t\t\t\t(void **) &dJointGetPUPosition},\r\n//\t{\"dJointGetPUPositionRate\",\t\t\t\t\t\t(void **) &dJointGetPUPositionRate},\r\n//\t{\"dJointGetPUAxis1\",\t\t\t\t\t\t\t(void **) &dJointGetPUAxis1},\r\n//\t{\"dJointGetPUAxis2\",\t\t\t\t\t\t\t(void **) &dJointGetPUAxis2},\r\n//\t{\"dJointGetPUAxis3\",\t\t\t\t\t\t\t(void **) &dJointGetPUAxis3},\r\n//\t{\"dJointGetPUAxisP\",\t\t\t\t\t\t\t(void **) &dJointGetPUAxisP},\r\n//\t{\"dJointGetPUAngles\",\t\t\t\t\t\t\t(void **) &dJointGetPUAngles},\r\n//\t{\"dJointGetPUAngle1\",\t\t\t\t\t\t\t(void **) &dJointGetPUAngle1},\r\n//\t{\"dJointGetPUAngle1Rate\",\t\t\t\t\t\t(void **) &dJointGetPUAngle1Rate},\r\n//\t{\"dJointGetPUAngle2\",\t\t\t\t\t\t\t(void **) &dJointGetPUAngle2},\r\n//\t{\"dJointGetPUAngle2Rate\",\t\t\t\t\t\t(void **) &dJointGetPUAngle2Rate},\r\n//\t{\"dJointGetPUParam\",\t\t\t\t\t\t\t(void **) &dJointGetPUParam},\r\n//\t{\"dJointGetPistonPosition\",\t\t\t\t\t\t(void **) &dJointGetPistonPosition},\r\n//\t{\"dJointGetPistonPositionRate\",\t\t\t\t\t(void **) &dJointGetPistonPositionRate},\r\n//\t{\"dJointGetPistonAngle\",\t\t\t\t\t\t(void **) &dJointGetPistonAngle},\r\n//\t{\"dJointGetPistonAngleRate\",\t\t\t\t\t(void **) &dJointGetPistonAngleRate},\r\n//\t{\"dJointGetPistonAnchor\",\t\t\t\t\t\t(void **) &dJointGetPistonAnchor},\r\n//\t{\"dJointGetPistonAnchor2\",\t\t\t\t\t\t(void **) &dJointGetPistonAnchor2},\r\n//\t{\"dJointGetPistonAxis\",\t\t\t\t\t\t\t(void **) &dJointGetPistonAxis},\r\n//\t{\"dJointGetPistonParam\",\t\t\t\t\t\t(void **) &dJointGetPistonParam},\r\n//\t{\"dJointGetAMotorNumAxes\",\t\t\t\t\t\t(void **) &dJointGetAMotorNumAxes},\r\n//\t{\"dJointGetAMotorAxis\",\t\t\t\t\t\t\t(void **) &dJointGetAMotorAxis},\r\n//\t{\"dJointGetAMotorAxisRel\",\t\t\t\t\t\t(void **) &dJointGetAMotorAxisRel},\r\n//\t{\"dJointGetAMotorAngle\",\t\t\t\t\t\t(void **) &dJointGetAMotorAngle},\r\n//\t{\"dJointGetAMotorAngleRate\",\t\t\t\t\t(void **) &dJointGetAMotorAngleRate},\r\n//\t{\"dJointGetAMotorParam\",\t\t\t\t\t\t(void **) &dJointGetAMotorParam},\r\n//\t{\"dJointGetAMotorMode\",\t\t\t\t\t\t\t(void **) &dJointGetAMotorMode},\r\n//\t{\"dJointGetLMotorNumAxes\",\t\t\t\t\t\t(void **) &dJointGetLMotorNumAxes},\r\n//\t{\"dJointGetLMotorAxis\",\t\t\t\t\t\t\t(void **) &dJointGetLMotorAxis},\r\n//\t{\"dJointGetLMotorParam\",\t\t\t\t\t\t(void **) &dJointGetLMotorParam},\r\n//\t{\"dJointGetFixedParam\",\t\t\t\t\t\t\t(void **) &dJointGetFixedParam},\r\n//\t{\"dConnectingJoint\",\t\t\t\t\t\t\t(void **) &dConnectingJoint},\r\n//\t{\"dConnectingJointList\",\t\t\t\t\t\t(void **) &dConnectingJointList},\r\n\t{(void **) &dAreConnected,\t\t\t\t\t\t\"dAreConnected\"},\r\n\t{(void **) &dAreConnectedExcluding,\t\t\t\t\"dAreConnectedExcluding\"},\r\n\t{(void **) &dSimpleSpaceCreate,\t\t\t\t\t\"dSimpleSpaceCreate\"},\r\n\t{(void **) &dHashSpaceCreate,\t\t\t\t\t\"dHashSpaceCreate\"},\r\n\t{(void **) &dQuadTreeSpaceCreate,\t\t\t\t\"dQuadTreeSpaceCreate\"},\r\n//\t{\"dSweepAndPruneSpaceCreate\",\t\t\t\t\t(void **) &dSweepAndPruneSpaceCreate},\r\n\t{(void **) &dSpaceDestroy,\t\t\t\t\t\t\"dSpaceDestroy\"},\r\n//\t{\"dHashSpaceSetLevels\",\t\t\t\t\t\t\t(void **) &dHashSpaceSetLevels},\r\n//\t{\"dHashSpaceGetLevels\",\t\t\t\t\t\t\t(void **) &dHashSpaceGetLevels},\r\n//\t{\"dSpaceSetCleanup\",\t\t\t\t\t\t\t(void **) &dSpaceSetCleanup},\r\n//\t{\"dSpaceGetCleanup\",\t\t\t\t\t\t\t(void **) &dSpaceGetCleanup},\r\n//\t{\"dSpaceSetSublevel\",\t\t\t\t\t\t\t(void **) &dSpaceSetSublevel},\r\n//\t{\"dSpaceGetSublevel\",\t\t\t\t\t\t\t(void **) &dSpaceGetSublevel},\r\n//\t{\"dSpaceSetManualCleanup\",\t\t\t\t\t\t(void **) &dSpaceSetManualCleanup},\r\n//\t{\"dSpaceGetManualCleanup\",\t\t\t\t\t\t(void **) &dSpaceGetManualCleanup},\r\n//\t{\"dSpaceAdd\",\t\t\t\t\t\t\t\t\t(void **) &dSpaceAdd},\r\n//\t{\"dSpaceRemove\",\t\t\t\t\t\t\t\t(void **) &dSpaceRemove},\r\n//\t{\"dSpaceQuery\",\t\t\t\t\t\t\t\t\t(void **) &dSpaceQuery},\r\n//\t{\"dSpaceClean\",\t\t\t\t\t\t\t\t\t(void **) &dSpaceClean},\r\n//\t{\"dSpaceGetNumGeoms\",\t\t\t\t\t\t\t(void **) &dSpaceGetNumGeoms},\r\n//\t{\"dSpaceGetGeom\",\t\t\t\t\t\t\t\t(void **) &dSpaceGetGeom},\r\n//\t{\"dSpaceGetClass\",\t\t\t\t\t\t\t\t(void **) &dSpaceGetClass},\r\n\t{(void **) &dGeomDestroy,\t\t\t\t\t\t\"dGeomDestroy\"},\r\n\t{(void **) &dGeomSetData,\t\t\t\t\t\t\"dGeomSetData\"},\r\n\t{(void **) &dGeomGetData,\t\t\t\t\t\t\"dGeomGetData\"},\r\n\t{(void **) &dGeomSetBody,\t\t\t\t\t\t\"dGeomSetBody\"},\r\n\t{(void **) &dGeomGetBody,\t\t\t\t\t\t\"dGeomGetBody\"},\r\n\t{(void **) &dGeomSetPosition,\t\t\t\t\t\"dGeomSetPosition\"},\r\n\t{(void **) &dGeomSetRotation,\t\t\t\t\t\"dGeomSetRotation\"},\r\n//\t{\"dGeomSetQuaternion\",\t\t\t\t\t\t\t(void **) &dGeomSetQuaternion},\r\n//\t{\"dGeomGetPosition\",\t\t\t\t\t\t\t(void **) &dGeomGetPosition},\r\n//\t{\"dGeomCopyPosition\",\t\t\t\t\t\t\t(void **) &dGeomCopyPosition},\r\n//\t{\"dGeomGetRotation\",\t\t\t\t\t\t\t(void **) &dGeomGetRotation},\r\n//\t{\"dGeomCopyRotation\",\t\t\t\t\t\t\t(void **) &dGeomCopyRotation},\r\n//\t{\"dGeomGetQuaternion\",\t\t\t\t\t\t\t(void **) &dGeomGetQuaternion},\r\n//\t{\"dGeomGetAABB\",\t\t\t\t\t\t\t\t(void **) &dGeomGetAABB},\r\n\t{(void **) &dGeomIsSpace,\t\t\t\t\t\t\"dGeomIsSpace\"},\r\n//\t{\"dGeomGetSpace\",\t\t\t\t\t\t\t\t(void **) &dGeomGetSpace},\r\n//\t{\"dGeomGetClass\",\t\t\t\t\t\t\t\t(void **) &dGeomGetClass},\r\n//\t{\"dGeomSetCategoryBits\",\t\t\t\t\t\t(void **) &dGeomSetCategoryBits},\r\n//\t{\"dGeomSetCollideBits\",\t\t\t\t\t\t\t(void **) &dGeomSetCollideBits},\r\n//\t{\"dGeomGetCategoryBits\",\t\t\t\t\t\t(void **) &dGeomGetCategoryBits},\r\n//\t{\"dGeomGetCollideBits\",\t\t\t\t\t\t\t(void **) &dGeomGetCollideBits},\r\n//\t{\"dGeomEnable\",\t\t\t\t\t\t\t\t\t(void **) &dGeomEnable},\r\n//\t{\"dGeomDisable\",\t\t\t\t\t\t\t\t(void **) &dGeomDisable},\r\n//\t{\"dGeomIsEnabled\",\t\t\t\t\t\t\t\t(void **) &dGeomIsEnabled},\r\n//\t{\"dGeomSetOffsetPosition\",\t\t\t\t\t\t(void **) &dGeomSetOffsetPosition},\r\n//\t{\"dGeomSetOffsetRotation\",\t\t\t\t\t\t(void **) &dGeomSetOffsetRotation},\r\n//\t{\"dGeomSetOffsetQuaternion\",\t\t\t\t\t(void **) &dGeomSetOffsetQuaternion},\r\n//\t{\"dGeomSetOffsetWorldPosition\",\t\t\t\t\t(void **) &dGeomSetOffsetWorldPosition},\r\n//\t{\"dGeomSetOffsetWorldRotation\",\t\t\t\t\t(void **) &dGeomSetOffsetWorldRotation},\r\n//\t{\"dGeomSetOffsetWorldQuaternion\",\t\t\t\t(void **) &dGeomSetOffsetWorldQuaternion},\r\n//\t{\"dGeomClearOffset\",\t\t\t\t\t\t\t(void **) &dGeomClearOffset},\r\n//\t{\"dGeomIsOffset\",\t\t\t\t\t\t\t\t(void **) &dGeomIsOffset},\r\n//\t{\"dGeomGetOffsetPosition\",\t\t\t\t\t\t(void **) &dGeomGetOffsetPosition},\r\n//\t{\"dGeomCopyOffsetPosition\",\t\t\t\t\t\t(void **) &dGeomCopyOffsetPosition},\r\n//\t{\"dGeomGetOffsetRotation\",\t\t\t\t\t\t(void **) &dGeomGetOffsetRotation},\r\n//\t{\"dGeomCopyOffsetRotation\",\t\t\t\t\t\t(void **) &dGeomCopyOffsetRotation},\r\n//\t{\"dGeomGetOffsetQuaternion\",\t\t\t\t\t(void **) &dGeomGetOffsetQuaternion},\r\n\t{(void **) &dCollide,\t\t\t\t\t\t\t\"dCollide\"},\r\n\t{(void **) &dSpaceCollide,\t\t\t\t\t\t\"dSpaceCollide\"},\r\n\t{(void **) &dSpaceCollide2,\t\t\t\t\t\t\"dSpaceCollide2\"},\r\n\t{(void **) &dCreateSphere,\t\t\t\t\t\t\"dCreateSphere\"},\r\n//\t{\"dGeomSphereSetRadius\",\t\t\t\t\t\t(void **) &dGeomSphereSetRadius},\r\n//\t{\"dGeomSphereGetRadius\",\t\t\t\t\t\t(void **) &dGeomSphereGetRadius},\r\n//\t{\"dGeomSpherePointDepth\",\t\t\t\t\t\t(void **) &dGeomSpherePointDepth},\r\n//\t{\"dCreateConvex\",\t\t\t\t\t\t\t\t(void **) &dCreateConvex},\r\n//\t{\"dGeomSetConvex\",\t\t\t\t\t\t\t\t(void **) &dGeomSetConvex},\r\n\t{(void **) &dCreateBox,\t\t\t\t\t\t\t\"dCreateBox\"},\r\n//\t{\"dGeomBoxSetLengths\",\t\t\t\t\t\t\t(void **) &dGeomBoxSetLengths},\r\n//\t{\"dGeomBoxGetLengths\",\t\t\t\t\t\t\t(void **) &dGeomBoxGetLengths},\r\n//\t{\"dGeomBoxPointDepth\",\t\t\t\t\t\t\t(void **) &dGeomBoxPointDepth},\r\n//\t{\"dGeomBoxPointDepth\",\t\t\t\t\t\t\t(void **) &dGeomBoxPointDepth},\r\n//\t{\"dCreatePlane\",\t\t\t\t\t\t\t\t(void **) &dCreatePlane},\r\n//\t{\"dGeomPlaneSetParams\",\t\t\t\t\t\t\t(void **) &dGeomPlaneSetParams},\r\n//\t{\"dGeomPlaneGetParams\",\t\t\t\t\t\t\t(void **) &dGeomPlaneGetParams},\r\n//\t{\"dGeomPlanePointDepth\",\t\t\t\t\t\t(void **) &dGeomPlanePointDepth},\r\n\t{(void **) &dCreateCapsule,\t\t\t\t\t\t\"dCreateCapsule\"},\r\n//\t{\"dGeomCapsuleSetParams\",\t\t\t\t\t\t(void **) &dGeomCapsuleSetParams},\r\n//\t{\"dGeomCapsuleGetParams\",\t\t\t\t\t\t(void **) &dGeomCapsuleGetParams},\r\n//\t{\"dGeomCapsulePointDepth\",\t\t\t\t\t\t(void **) &dGeomCapsulePointDepth},\r\n\t{(void **) &dCreateCylinder,\t\t\t\t\t\"dCreateCylinder\"},\r\n//\t{\"dGeomCylinderSetParams\",\t\t\t\t\t\t(void **) &dGeomCylinderSetParams},\r\n//\t{\"dGeomCylinderGetParams\",\t\t\t\t\t\t(void **) &dGeomCylinderGetParams},\r\n//\t{\"dCreateRay\",\t\t\t\t\t\t\t\t\t(void **) &dCreateRay},\r\n//\t{\"dGeomRaySetLength\",\t\t\t\t\t\t\t(void **) &dGeomRaySetLength},\r\n//\t{\"dGeomRayGetLength\",\t\t\t\t\t\t\t(void **) &dGeomRayGetLength},\r\n//\t{\"dGeomRaySet\",\t\t\t\t\t\t\t\t\t(void **) &dGeomRaySet},\r\n//\t{\"dGeomRayGet\",\t\t\t\t\t\t\t\t\t(void **) &dGeomRayGet},\r\n\t{(void **) &dCreateGeomTransform,\t\t\t\t\"dCreateGeomTransform\"},\r\n\t{(void **) &dGeomTransformSetGeom,\t\t\t\t\"dGeomTransformSetGeom\"},\r\n//\t{\"dGeomTransformGetGeom\",\t\t\t\t\t\t(void **) &dGeomTransformGetGeom},\r\n\t{(void **) &dGeomTransformSetCleanup,\t\t\t\"dGeomTransformSetCleanup\"},\r\n//\t{\"dGeomTransformGetCleanup\",\t\t\t\t\t(void **) &dGeomTransformGetCleanup},\r\n//\t{\"dGeomTransformSetInfo\",\t\t\t\t\t\t(void **) &dGeomTransformSetInfo},\r\n//\t{\"dGeomTransformGetInfo\",\t\t\t\t\t\t(void **) &dGeomTransformGetInfo},\r\n\t{(void **) &dGeomTriMeshDataCreate,\t\t\t\t\"dGeomTriMeshDataCreate\"},\r\n\t{(void **) &dGeomTriMeshDataDestroy,\t\t\t\"dGeomTriMeshDataDestroy\"},\r\n//\t{\"dGeomTriMeshDataSet\",                         (void **) &dGeomTriMeshDataSet},\r\n//\t{\"dGeomTriMeshDataGet\",                         (void **) &dGeomTriMeshDataGet},\r\n//\t{\"dGeomTriMeshSetLastTransform\",                (void **) &dGeomTriMeshSetLastTransform},\r\n//\t{\"dGeomTriMeshGetLastTransform\",                (void **) &dGeomTriMeshGetLastTransform},\r\n\t{(void **) &dGeomTriMeshDataBuildSingle,\t\t\"dGeomTriMeshDataBuildSingle\"},\r\n//\t{\"dGeomTriMeshDataBuildSingle1\",                (void **) &dGeomTriMeshDataBuildSingle1},\r\n//\t{\"dGeomTriMeshDataBuildDouble\",                 (void **) &dGeomTriMeshDataBuildDouble},\r\n//\t{\"dGeomTriMeshDataBuildDouble1\",                (void **) &dGeomTriMeshDataBuildDouble1},\r\n//\t{\"dGeomTriMeshDataBuildSimple\",                 (void **) &dGeomTriMeshDataBuildSimple},\r\n//\t{\"dGeomTriMeshDataBuildSimple1\",                (void **) &dGeomTriMeshDataBuildSimple1},\r\n//\t{\"dGeomTriMeshDataPreprocess\",                  (void **) &dGeomTriMeshDataPreprocess},\r\n//\t{\"dGeomTriMeshDataGetBuffer\",                   (void **) &dGeomTriMeshDataGetBuffer},\r\n//\t{\"dGeomTriMeshDataSetBuffer\",                   (void **) &dGeomTriMeshDataSetBuffer},\r\n//\t{\"dGeomTriMeshSetCallback\",                     (void **) &dGeomTriMeshSetCallback},\r\n//\t{\"dGeomTriMeshGetCallback\",                     (void **) &dGeomTriMeshGetCallback},\r\n//\t{\"dGeomTriMeshSetArrayCallback\",                (void **) &dGeomTriMeshSetArrayCallback},\r\n//\t{\"dGeomTriMeshGetArrayCallback\",                (void **) &dGeomTriMeshGetArrayCallback},\r\n//\t{\"dGeomTriMeshSetRayCallback\",                  (void **) &dGeomTriMeshSetRayCallback},\r\n//\t{\"dGeomTriMeshGetRayCallback\",                  (void **) &dGeomTriMeshGetRayCallback},\r\n//\t{\"dGeomTriMeshSetTriMergeCallback\",             (void **) &dGeomTriMeshSetTriMergeCallback},\r\n//\t{\"dGeomTriMeshGetTriMergeCallback\",             (void **) &dGeomTriMeshGetTriMergeCallback},\r\n\t{(void **) &dCreateTriMesh,\t\t\t\t\t\t\"dCreateTriMesh\"},\r\n//\t{\"dGeomTriMeshSetData\",                         (void **) &dGeomTriMeshSetData},\r\n//\t{\"dGeomTriMeshGetData\",                         (void **) &dGeomTriMeshGetData},\r\n//\t{\"dGeomTriMeshEnableTC\",                        (void **) &dGeomTriMeshEnableTC},\r\n//\t{\"dGeomTriMeshIsTCEnabled\",                     (void **) &dGeomTriMeshIsTCEnabled},\r\n//\t{\"dGeomTriMeshClearTCCache\",                    (void **) &dGeomTriMeshClearTCCache},\r\n//\t{\"dGeomTriMeshGetTriMeshDataID\",                (void **) &dGeomTriMeshGetTriMeshDataID},\r\n//\t{\"dGeomTriMeshGetTriangle\",                     (void **) &dGeomTriMeshGetTriangle},\r\n//\t{\"dGeomTriMeshGetPoint\",                        (void **) &dGeomTriMeshGetPoint},\r\n//\t{\"dGeomTriMeshGetTriangleCount\",                (void **) &dGeomTriMeshGetTriangleCount},\r\n//\t{\"dGeomTriMeshDataUpdate\",                      (void **) &dGeomTriMeshDataUpdate},\r\n\r\n\t{(void **) &dSetErrorHandler,\t\t\t\t\t\"dSetErrorHandler\"},\r\n\t{(void **) &dSetDebugHandler,\t\t\t\t\t\"dSetDebugHandler\"},\r\n\t{(void **) &dSetMessageHandler,\t\t\t\t\t\"dSetMessageHandler\"},\r\n\t{NULL, NULL}\r\n};\r\n\r\n// Handle for ODE DLL\r\nstatic dllhandle_t *ode_dll = NULL;\r\n#endif\r\n\r\nstatic cvar_t *physics_ode_quadtree_depth;\r\nstatic cvar_t *physics_ode_contactsurfacelayer;\r\nstatic cvar_t *physics_ode_worldquickstep;\r\nstatic cvar_t *physics_ode_worldquickstep_iterations;\r\nstatic cvar_t *physics_ode_contact_mu;\r\nstatic cvar_t *physics_ode_contact_erp;\r\nstatic cvar_t *physics_ode_contact_cfm;\r\nstatic cvar_t *physics_ode_world_damping;\r\nstatic cvar_t *physics_ode_world_damping_linear;\r\nstatic cvar_t *physics_ode_world_damping_linear_threshold;\r\nstatic cvar_t *physics_ode_world_damping_angular;\r\nstatic cvar_t *physics_ode_world_damping_angular_threshold;\r\nstatic cvar_t *physics_ode_world_erp;\r\nstatic cvar_t *physics_ode_world_cfm;\r\nstatic cvar_t *physics_ode_iterationsperframe;\r\nstatic cvar_t *physics_ode_movelimit;\r\nstatic cvar_t *physics_ode_spinlimit;\r\nstatic cvar_t *physics_ode_autodisable;\r\nstatic cvar_t *physics_ode_autodisable_steps;\r\nstatic cvar_t *physics_ode_autodisable_time;\r\nstatic cvar_t *physics_ode_autodisable_threshold_linear;\r\nstatic cvar_t *physics_ode_autodisable_threshold_angular;\r\nstatic cvar_t *physics_ode_autodisable_threshold_samples;\r\nstatic cvar_t *physics_ode_maxspeed;\r\n\r\nstruct odectx_s\r\n{\r\n\trigidbodyengine_t pub;\r\n\r\n\tqboolean hasextraobjs;\r\n\tdWorldID dworld;\r\n\tvoid *space;\r\n\tvoid *contactgroup;\r\n\t// number of constraint solver iterations to use (for dWorldStepFast)\r\n\tint iterations;\r\n\t// actual step (server frametime / ode_iterations)\r\n\tvec_t step;\r\n\t// max velocity for a 1-unit radius object at current step to prevent\r\n\t// missed collisions\r\n\tvec_t movelimit;\r\n\trbecommandqueue_t *cmdqueuehead;\r\n\trbecommandqueue_t *cmdqueuetail;\r\n};\r\n\r\n\r\nstatic void World_ODE_RunCmd(world_t *world, rbecommandqueue_t *cmd);\r\n\r\n#ifdef _WIN32\r\n\t#undef vsnprintf\r\n\t#undef _vsnprintf\r\n\t#define vsnprintf(s,l,f,a) _vsnprintf(s,l,f,a);string[sizeof(string)-1] = 0;\r\n#endif\r\n\r\nstatic void MyODEErrorHandler (int errnum, const char *msg, va_list ap)\r\n{\r\n\tchar\t\tstring[1024];\r\n\tvsnprintf (string,sizeof(string), msg, ap);\r\n\tstring[sizeof(string)-1] = 0;\r\n\tSys_Errorf(\"ODE ERROR %i: %s\", errnum, string);\r\n}\r\nstatic void MyODEMessageHandler (int errnum, const char *msg, va_list ap)\r\n{\r\n\tchar\t\tstring[1024];\r\n\tvsnprintf (string,sizeof(string), msg, ap);\r\n\tstring[sizeof(string)-1] = 0;\r\n\tCon_Printf(\"ODE Message %i: %s\\n\", errnum, string);\r\n}\r\n\r\nstatic qboolean World_ODE_Init(void)\r\n{\r\n#ifdef ODE_DYNAMIC\r\n\tconst char* dllname =\r\n\t{\r\n# if defined(_WIN64)\r\n\t\t\"libode1\"\r\n# elif defined(_WIN32)\r\n\t\t\"ode_double\"\r\n# elif defined(MACOSX)\r\n\t\t\"libode.1.dylib\"\r\n# else\r\n\t\t\"libode.so.1\"\r\n# endif\r\n\t};\r\n#endif\r\n\r\n\tphysics_ode_quadtree_depth\t\t\t\t\t= cvarfuncs->GetNVFDG(\"physics_ode_quadtree_depth\",\t\t\t\t\t\"5\",\t0,\t\"desired subdivision level of quadtree culling space\",\t\t\t\t\"ODE Physics Library\");\r\n\tphysics_ode_contactsurfacelayer\t\t\t\t= cvarfuncs->GetNVFDG(\"physics_ode_contactsurfacelayer\",\t\t\t\t\"0\",\t0,\t\"allows objects to overlap this many units to reduce jitter\",\t\t\"ODE Physics Library\");\r\n\tphysics_ode_worldquickstep\t\t\t\t\t= cvarfuncs->GetNVFDG(\"physics_ode_worldquickstep\",\t\t\t\t\t\"1\",\t0,\t\"use dWorldQuickStep rather than dWorldStep\",\t\t\t\t\t\t\"ODE Physics Library\");\r\n\tphysics_ode_worldquickstep_iterations\t\t= cvarfuncs->GetNVFDG(\"physics_ode_worldquickstep_iterations\",\t\t\"20\",\t0,\t\"parameter to dWorldQuickStep\",\t\t\t\t\t\t\t\t\t\t\"ODE Physics Library\");\r\n\tphysics_ode_contact_mu\t\t\t\t\t\t= cvarfuncs->GetNVFDG(\"physics_ode_contact_mu\",\t\t\t\t\t\t\"1\",\t0,\t\"contact solver mu parameter - friction pyramid approximation 1 (see ODE User Guide)\",\t\"ODE Physics Library\");\r\n\tphysics_ode_contact_erp\t\t\t\t\t\t= cvarfuncs->GetNVFDG(\"physics_ode_contact_erp\",\t\t\t\t\t\t\"0.96\",\t0,\t\"contact solver erp parameter - Error Restitution Percent (see ODE User Guide)\",\t\t\"ODE Physics Library\");\r\n\tphysics_ode_contact_cfm\t\t\t\t\t\t= cvarfuncs->GetNVFDG(\"physics_ode_contact_cfm\",\t\t\t\t\t\t\"0\",\t0,\t\"contact solver cfm parameter - Constraint Force Mixing (see ODE User Guide)\",\t\t\t\"ODE Physics Library\");\r\n\tphysics_ode_world_damping\t\t\t\t\t= cvarfuncs->GetNVFDG(\"physics_ode_world_damping\",\t\t\t\t\t\"1\",\t0,\t\"enabled damping scale (see ODE User Guide), this scales all damping values, be aware that behavior depends of step type\",\t\"ODE Physics Library\");\r\n\tphysics_ode_world_damping_linear\t\t\t= cvarfuncs->GetNVFDG(\"physics_ode_world_damping_linear\",\t\t\t\"-1\",0,\t\"world linear damping scale (see ODE User Guide); use defaults when set to -1\",\t\t\t\"ODE Physics Library\");\r\n\tphysics_ode_world_damping_linear_threshold\t= cvarfuncs->GetNVFDG(\"physics_ode_world_damping_linear_threshold\",\t\"-1\",\t0,\t\"world linear damping threshold (see ODE User Guide); use defaults when set to -1\",\t\t\"ODE Physics Library\");\r\n\tphysics_ode_world_damping_angular\t\t\t= cvarfuncs->GetNVFDG(\"physics_ode_world_damping_angular\",\t\t\t\"-1\",0,\t\"world angular damping scale (see ODE User Guide); use defaults when set to -1\",\t\t\"ODE Physics Library\");\r\n\tphysics_ode_world_damping_angular_threshold\t= cvarfuncs->GetNVFDG(\"physics_ode_world_damping_angular_threshold\",\t\"-1\",\t0,\t\"world angular damping threshold (see ODE User Guide); use defaults when set to -1\",\t\"ODE Physics Library\");\r\n\tphysics_ode_world_erp\t\t\t\t\t\t= cvarfuncs->GetNVFDG(\"physics_ode_world_erp\",\t\t\t\t\t\t\"-1\",\t0,\t\"world solver erp parameter - Error Restitution Percent (see ODE User Guide); use defaults when set to -1\",\t\t\t\"ODE Physics Library\");\r\n\tphysics_ode_world_cfm\t\t\t\t\t\t= cvarfuncs->GetNVFDG(\"physics_ode_world_cfm\",\t\t\t\t\t\t\"-1\",\t0,\t\"world solver cfm parameter - Constraint Force Mixing (see ODE User Guide); not touched when -1\",\t\t\t\t\t\"ODE Physics Library\");\r\n\tphysics_ode_iterationsperframe\t\t\t\t= cvarfuncs->GetNVFDG(\"physics_ode_iterationsperframe\",\t\t\t\t\"4\",\t0,\t\"divisor for time step, runs multiple physics steps per frame\",\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"ODE Physics Library\");\r\n\tphysics_ode_movelimit\t\t\t\t\t\t= cvarfuncs->GetNVFDG(\"physics_ode_movelimit\",\t\t\t\t\t\t\"0.5\",\t0,\t\"clamp velocity if a single move would exceed this percentage of object thickness, to prevent flying through walls\",\"ODE Physics Library\");\r\n\tphysics_ode_spinlimit\t\t\t\t\t\t= cvarfuncs->GetNVFDG(\"physics_ode_spinlimit\",\t\t\t\t\t\t\"10000\",0,\t\"reset spin velocity if it gets too large\",\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"ODE Physics Library\");\r\n\tphysics_ode_maxspeed\t\t\t\t\t\t= cvarfuncs->GetNVFDG(\"physics_ode_maxspeed\",\t\t\t\t\t\t\"0\",\t0,\t\"clamp absolute velocity\",\"ODE Physics Library\");\r\n\tphysics_ode_autodisable\t\t\t\t\t\t= cvarfuncs->GetNVFDG(\"physics_ode_autodisable\",\t\t\t\t\t\t\"1\",\t0,\t\"automatic disabling of objects which dont move for long period of time, makes object stacking a lot faster\",\t\t\"ODE Physics Library\");\r\n\tphysics_ode_autodisable_steps\t\t\t\t= cvarfuncs->GetNVFDG(\"physics_ode_autodisable_steps\",\t\t\t\t\"10\",\t0,\t\"how many steps object should be dormant to be autodisabled\",\t\t\"ODE Physics Library\");\r\n\tphysics_ode_autodisable_time\t\t\t\t= cvarfuncs->GetNVFDG(\"physics_ode_autodisable_time\",\t\t\t\t\t\"0\",\t0,\t\"how many seconds object should be dormant to be autodisabled\",\t\t\"ODE Physics Library\");\r\n\tphysics_ode_autodisable_threshold_linear\t= cvarfuncs->GetNVFDG(\"physics_ode_autodisable_threshold_linear\",\t\t\"0.2\",\t0,\t\"body will be disabled if it's linear move below this value\",\t\t\"ODE Physics Library\");\r\n\tphysics_ode_autodisable_threshold_angular\t= cvarfuncs->GetNVFDG(\"physics_ode_autodisable_threshold_angular\",\t\"0.3\",\t0,\t\"body will be disabled if it's angular move below this value\",\t\t\"ODE Physics Library\");\r\n\tphysics_ode_autodisable_threshold_samples\t= cvarfuncs->GetNVFDG(\"physics_ode_autodisable_threshold_samples\",\t\"5\",\t0,\t\"average threshold with this number of samples\",\t\t\t\t\t\"ODE Physics Library\");\r\n\r\n#ifdef ODE_DYNAMIC\r\n\t// Load the DLL\r\n\tode_dll = plugfuncs->LoadDLL(dllname, odefuncs);\r\n\tif (ode_dll)\r\n#endif\r\n\t{\r\n\t\tdInitODE();\r\n//\t\tdInitODE2(0);\r\n#ifdef ODE_DYNAMIC\r\n# ifdef dSINGLE\r\n\t\tif (!dCheckConfiguration(\"ODE_single_precision\"))\r\n# else\r\n\t\tif (!dCheckConfiguration(\"ODE_double_precision\"))\r\n# endif\r\n\t\t{\r\n# ifdef dSINGLE\r\n\t\t\tCon_Printf(\"ode library not compiled for single precision - incompatible!  Not using ODE physics.\\n\");\r\n# else\r\n\t\t\tCon_Printf(\"ode library not compiled for double precision - incompatible!  Not using ODE physics.\\n\");\r\n# endif\r\n\t\t\tplugfuncs->CloseDLL(ode_dll);\r\n\t\t\tode_dll = NULL;\r\n\t\t}\r\n#endif\r\n\t}\r\n\r\n#ifdef ODE_DYNAMIC\r\n\tif (!ode_dll)\r\n\t{\r\n\t\tCon_Printf(\"ODE plugin failed: \\\"%s\\\" library missing.\\n\", dllname);\r\n\t\treturn false;\r\n\t}\r\n#endif\r\n\r\n\tdSetErrorHandler(MyODEErrorHandler);\t//ode will display a messagebox (which probably won't have focus/grabs) and then crash, which messes up all sorts of things like gamma.\r\n\tdSetDebugHandler(MyODEErrorHandler);\t//both are fatal.\r\n\tdSetMessageHandler(MyODEMessageHandler);//merely a print.\r\n\treturn true;\r\n}\r\n\r\nstatic void World_ODE_Shutdown(void)\r\n{\r\n#ifdef ODE_DYNAMIC\r\n\tif (ode_dll)\r\n#endif\r\n\t{\r\n\t\tdCloseODE();\r\n#ifdef ODE_DYNAMIC\r\n\t\tplugfuncs->CloseDLL(ode_dll);\r\n\t\tode_dll = NULL;\r\n#endif\r\n\t}\r\n}\r\n\r\nstatic void QDECL World_ODE_End(world_t *world)\r\n{\r\n\tstruct odectx_s *ctx = (struct odectx_s*)world->rbe;\r\n\tworld->rbe = NULL;\r\n\tdWorldDestroy(ctx->dworld);\r\n\tdSpaceDestroy(ctx->space);\r\n\tdJointGroupDestroy(ctx->contactgroup);\r\n\tZ_Free(ctx);\r\n}\r\n\r\nstatic void QDECL World_ODE_RemoveJointFromEntity(world_t *world, wedict_t *ed)\r\n{\r\n\ted->rbe.joint_type = 0;\r\n\tif(ed->rbe.joint.joint)\r\n\t\tdJointDestroy((dJointID)ed->rbe.joint.joint);\r\n\ted->rbe.joint.joint = NULL;\r\n}\r\n\r\nstatic void QDECL World_ODE_RemoveFromEntity(world_t *world, wedict_t *ed)\r\n{\r\n\tif (!ed->rbe.physics)\r\n\t\treturn;\r\n\r\n\t// entity is not physics controlled, free any physics data\r\n\ted->rbe.physics = false;\r\n\tif (ed->rbe.body.geom)\r\n\t\tdGeomDestroy((dGeomID)ed->rbe.body.geom);\r\n\ted->rbe.body.geom = NULL;\r\n\tif (ed->rbe.body.body)\r\n\t{\r\n\t\tdJointID j;\r\n\t\tdBodyID b1, b2;\r\n\t\twedict_t *ed2;\r\n\t\twhile(dBodyGetNumJoints((dBodyID)ed->rbe.body.body))\r\n\t\t{\r\n\t\t\tj = dBodyGetJoint((dBodyID)ed->rbe.body.body, 0);\r\n\t\t\ted2 = (wedict_t *) dJointGetData(j);\r\n\t\t\tb1 = dJointGetBody(j, 0);\r\n\t\t\tb2 = dJointGetBody(j, 1);\r\n\t\t\tif(b1 == (dBodyID)ed->rbe.body.body)\r\n\t\t\t{\r\n\t\t\t\tb1 = 0;\r\n\t\t\t\ted2->rbe.joint_enemy = 0;\r\n\t\t\t}\r\n\t\t\tif(b2 == (dBodyID)ed->rbe.body.body)\r\n\t\t\t{\r\n\t\t\t\tb2 = 0;\r\n\t\t\t\ted2->rbe.joint_aiment = 0;\r\n\t\t\t}\r\n\t\t\tdJointAttach(j, b1, b2);\r\n\t\t}\r\n\t\tdBodyDestroy((dBodyID)ed->rbe.body.body);\r\n\t}\r\n\ted->rbe.body.body = NULL;\r\n\r\n\trbefuncs->ReleaseCollisionMesh(ed);\r\n\tif(ed->rbe.massbuf)\r\n\t\tBZ_Free(ed->rbe.massbuf);\r\n\ted->rbe.massbuf = NULL;\r\n}\r\n\r\nstatic void World_ODE_Frame_BodyToEntity(world_t *world, wedict_t *ed)\r\n{\r\n\tmodel_t *model;\r\n\tconst dReal *avel;\r\n\tconst dReal *o;\r\n\tconst dReal *r; // for some reason dBodyGetRotation returns a [3][4] matrix\r\n\tconst dReal *vel;\r\n\tdBodyID body = (dBodyID)ed->rbe.body.body;\r\n\tint movetype;\r\n\tfloat bodymatrix[16];\r\n\tfloat entitymatrix[16];\r\n\tvec3_t angles;\r\n\tvec3_t avelocity;\r\n\tvec3_t forward, left, up;\r\n\tvec3_t origin;\r\n\tvec3_t spinvelocity;\r\n\tvec3_t velocity;\r\n\tif (!body)\r\n\t\treturn;\r\n\r\n\tmovetype = (int)ed->v->movetype;\r\n\tif (movetype != MOVETYPE_PHYSICS)\r\n\t{\r\n\t\tswitch((int)ed->xv->jointtype)\r\n\t\t{\r\n\t\t\t// TODO feed back data from physics\r\n\t\t\tcase JOINTTYPE_POINT:\r\n\t\t\t\tbreak;\r\n\t\t\tcase JOINTTYPE_HINGE:\r\n\t\t\t\tbreak;\r\n\t\t\tcase JOINTTYPE_SLIDER:\r\n\t\t\t\tbreak;\r\n\t\t\tcase JOINTTYPE_UNIVERSAL:\r\n\t\t\t\tbreak;\r\n\t\t\tcase JOINTTYPE_HINGE2:\r\n\t\t\t\tbreak;\r\n\t\t\tcase JOINTTYPE_FIXED:\r\n\t\t\t\tbreak;\r\n\t\t}\r\n\t\treturn;\r\n\t}\r\n\t// store the physics engine data into the entity\r\n\to = dBodyGetPosition(body);\r\n\tr = dBodyGetRotation(body);\r\n\tvel = dBodyGetLinearVel(body);\r\n\tavel = dBodyGetAngularVel(body);\r\n\tVectorCopy(o, origin);\r\n\tforward[0] = r[0];\r\n\tforward[1] = r[4];\r\n\tforward[2] = r[8];\r\n\tleft[0] = r[1];\r\n\tleft[1] = r[5];\r\n\tleft[2] = r[9];\r\n\tup[0] = r[2];\r\n\tup[1] = r[6];\r\n\tup[2] = r[10];\r\n\tVectorCopy(vel, velocity);\r\n\tVectorCopy(avel, spinvelocity);\r\n\tMatrix4x4_RM_FromVectors(bodymatrix, forward, left, up, origin);\r\n\tMatrix4_Multiply(ed->rbe.offsetimatrix, bodymatrix, entitymatrix);\r\n\tMatrix3x4_RM_ToVectors(entitymatrix, forward, left, up, origin);\r\n\r\n\tVectorAngles(forward, up, angles, false);\r\n\tavelocity[PITCH] = RAD2DEG(spinvelocity[PITCH]);\r\n\tavelocity[YAW] = RAD2DEG(spinvelocity[ROLL]);\r\n\tavelocity[ROLL] = RAD2DEG(spinvelocity[YAW]);\r\n\r\n\tif (ed->v->modelindex)\r\n\t{\r\n\t\tmodel = world->Get_CModel(world, ed->v->modelindex);\r\n\t\tif (!model || model->type == mod_alias)\r\n\t\t{\r\n\t\t\tangles[PITCH] *= r_meshpitch.value;\r\n\t\t\tavelocity[PITCH] *= r_meshpitch.value;\r\n\t\t}\r\n\t}\r\n\r\n\tVectorCopy(origin, ed->v->origin);\r\n\tVectorCopy(velocity, ed->v->velocity);\r\n\t//vVectorCopy(forward, ed->xv->axis_forward);\r\n\t//VectorCopy(left, ed->xv->axis_left);\r\n\t//VectorCopy(up, ed->xv->axis_up);\r\n\t//VectorCopy(spinvelocity, ed->xv->spinvelocity);\r\n\tVectorCopy(angles, ed->v->angles);\r\n\tVectorCopy(avelocity, ed->v->avelocity);\r\n\r\n\t// values for BodyFromEntity to check if the qc modified anything later\r\n\tVectorCopy(origin, ed->rbe.origin);\r\n\tVectorCopy(velocity, ed->rbe.velocity);\r\n\tVectorCopy(angles, ed->rbe.angles);\r\n\tVectorCopy(avelocity, ed->rbe.avelocity);\r\n\ted->rbe.gravity = dBodyGetGravityMode(body);\r\n\r\n\trbefuncs->LinkEdict(world, ed, true);\r\n}\r\n\r\nstatic int ragGroups = 0;\r\nstatic void World_ODE_Frame_JointFromEntity(world_t *world, wedict_t *ed)\r\n{\r\n\tstruct odectx_s *ctx = (struct odectx_s*)world->rbe;\r\n\tdJointID j = 0;\r\n\tdBodyID b1 = 0;\r\n\tdBodyID b2 = 0;\r\n\tdJointGroupID jgid = 0;\r\n\tint movetype = 0;\r\n\tint jointgroup = 0;\r\n\tint jointtype = 0;\r\n\tint enemy = 0, aiment = 0;\r\n\twedict_t *o;\r\n\tvec3_t origin, velocity, angles, forward, left, up, movedir;\r\n\tvec_t CFM, ERP, FMax, Stop, Vel;\r\n\tVectorClear(origin);\r\n\tVectorClear(velocity);\r\n\tVectorClear(angles);\r\n\tVectorClear(movedir);\r\n\tmovetype = (int)ed->v->movetype;\r\n\tjointtype = (int)ed->xv->jointtype;\r\n\tenemy = ed->v->enemy;\r\n\taiment = ed->v->aiment;\r\n\tjointgroup = ed->xv->jointgroup;\r\n\tVectorCopy(ed->v->origin, origin);\r\n\tVectorCopy(ed->v->velocity, velocity);\r\n\tVectorCopy(ed->v->angles, angles);\r\n\tVectorCopy(ed->v->movedir, movedir);\r\n\tif(movetype == MOVETYPE_PHYSICS)\r\n\t\tjointtype = 0; // can't have both\r\n\r\n\to = (wedict_t*)PROG_TO_EDICT(world->progs, enemy);\r\n\tif(ED_ISFREE(o) || o->rbe.body.body == 0)\r\n\t\tenemy = 0;\r\n\to = (wedict_t*)PROG_TO_EDICT(world->progs, aiment);\r\n\tif(ED_ISFREE(o) || o->rbe.body.body == 0)\r\n\t\taiment = 0;\r\n\t// see http://www.ode.org/old_list_archives/2006-January/017614.html\r\n\t// we want to set ERP? make it fps independent and work like a spring constant\r\n\t// note: if movedir[2] is 0, it becomes ERP = 1, CFM = 1.0 / (H * K)\r\n\tif(movedir[0] > 0 && movedir[1] > 0)\r\n\t{\r\n\t\tfloat K = movedir[0];\r\n\t\tfloat D = movedir[1];\r\n\t\tfloat R = 2.0 * D * sqrt(K); // we assume D is premultiplied by sqrt(sprungMass)\r\n\t\tCFM = 1.0 / (ctx->step * K + R); // always > 0\r\n\t\tERP = ctx->step * K * CFM;\r\n\t\tVel = 0;\r\n\t\tFMax = 0;\r\n\t\tStop = movedir[2];\r\n\t}\r\n\telse if(movedir[1] < 0)\r\n\t{\r\n\t\tCFM = 0;\r\n\t\tERP = 0;\r\n\t\tVel = movedir[0];\r\n\t\tFMax = -movedir[1]; // TODO do we need to multiply with world.physics.ode_step?\r\n\t\tStop = movedir[2] > 0 ? movedir[2] : dInfinity;\r\n\t}\r\n\telse // movedir[0] > 0, movedir[1] == 0 or movedir[0] < 0, movedir[1] >= 0\r\n\t{\r\n\t\tCFM = 0;\r\n\t\tERP = 0;\r\n\t\tVel = 0;\r\n\t\tFMax = 0;\r\n\t\tStop = dInfinity;\r\n\t}\r\n\tif(jointtype == ed->rbe.joint_type && VectorCompare(origin, ed->rbe.joint_origin) && VectorCompare(velocity, ed->rbe.joint_velocity) && VectorCompare(angles, ed->rbe.joint_angles) && enemy == ed->rbe.joint_enemy && aiment == ed->rbe.joint_aiment && VectorCompare(movedir, ed->rbe.joint_movedir))\r\n\t\treturn; // nothing to do\r\n\r\n\t/* we're part of a joint group */\r\n\tif (jointgroup > 0) {\r\n\t\t/* we're unaware of it, let's create it */\r\n\t\tif (jointgroup > ragGroups) {\r\n\t\t\tjgid = dJointGroupCreate(0);\r\n\t\t\tragGroups = jointgroup;\r\n\t\t} else {\r\n\t\t\tjgid = (dJointGroupID)jointgroup;\r\n\t\t}\r\n\r\n\t\ted->rbe.jointgroup = (int)jgid;\r\n\t}\r\n\r\n\tAngleVectorsFLU(angles, forward, left, up);\r\n\tswitch(jointtype)\r\n\t{\r\n\t\tcase JOINTTYPE_POINT:\r\n\t\t\tj = dJointCreateBall(ctx->dworld, jgid);\r\n\t\t\tbreak;\r\n\t\tcase JOINTTYPE_HINGE:\r\n\t\t\tj = dJointCreateHinge(ctx->dworld, jgid);\r\n\t\t\tbreak;\r\n\t\tcase JOINTTYPE_SLIDER:\r\n\t\t\tj = dJointCreateSlider(ctx->dworld, jgid);\r\n\t\t\tbreak;\r\n\t\tcase JOINTTYPE_UNIVERSAL:\r\n\t\t\tj = dJointCreateUniversal(ctx->dworld, jgid);\r\n\t\t\tbreak;\r\n\t\tcase JOINTTYPE_HINGE2:\r\n\t\t\tj = dJointCreateHinge2(ctx->dworld, jgid);\r\n\t\t\tbreak;\r\n\t\tcase JOINTTYPE_FIXED:\r\n\t\t\tj = dJointCreateFixed(ctx->dworld, jgid);\r\n\t\t\tbreak;\r\n\t\tcase 0:\r\n\t\tdefault:\r\n\t\t\t// no joint\r\n\t\t\tj = 0;\r\n\t\t\tbreak;\r\n\t}\r\n\tif(ed->rbe.joint.joint)\r\n\t{\r\n\t\t//Con_Printf(\"deleted old joint %i\\n\", (int) (ed - prog->edicts));\r\n\t\tdJointAttach(ed->rbe.joint.joint, 0, 0);\r\n\t\tdJointDestroy(ed->rbe.joint.joint);\r\n\t}\r\n\ted->rbe.joint.joint = (void *) j;\r\n\ted->rbe.joint_type = jointtype;\r\n\ted->rbe.joint_enemy = enemy;\r\n\ted->rbe.joint_aiment = aiment;\r\n\tVectorCopy(origin, ed->rbe.joint_origin);\r\n\tVectorCopy(velocity, ed->rbe.joint_velocity);\r\n\tVectorCopy(angles, ed->rbe.joint_angles);\r\n\tVectorCopy(movedir, ed->rbe.joint_movedir);\r\n\tif(j)\r\n\t{\r\n\t\t//Con_Printf(\"made new joint %i\\n\", (int) (ed - prog->edicts));\r\n\t\tdJointSetData(j, (void *) ed);\r\n\t\tif(enemy)\r\n\t\t\tb1 = (dBodyID)((WEDICT_NUM_UB(world->progs, enemy))->rbe.body.body);\r\n\t\tif(aiment)\r\n\t\t\tb2 = (dBodyID)((WEDICT_NUM_UB(world->progs, aiment))->rbe.body.body);\r\n\t\tdJointAttach(j, b1, b2);\r\n\r\n\t\tswitch(jointtype)\r\n\t\t{\r\n\t\t\tcase JOINTTYPE_POINT:\r\n\t\t\t\tdJointSetBallAnchor(j, origin[0], origin[1], origin[2]);\r\n\t\t\t\tdJointSetBallAnchor2(j, velocity[0], velocity[1], velocity[2]);\r\n\t\t\t\tbreak;\r\n\t\t\tcase JOINTTYPE_HINGE:\r\n\t\t\t\tdJointSetHingeAnchor(j, origin[0], origin[1], origin[2]);\r\n\t\t\t\tdJointSetHingeAxis(j, forward[0], forward[1], forward[2]);\r\n\t\t\t\tdJointSetHingeParam(j, dParamFMax, FMax);\r\n\t\t\t\tdJointSetHingeParam(j, dParamHiStop, Stop);\t\r\n\t\t\t\tdJointSetHingeParam(j, dParamLoStop, -Stop);\r\n\t\t\t\tdJointSetHingeParam(j, dParamStopCFM, CFM);\r\n\t\t\t\tdJointSetHingeParam(j, dParamStopERP, ERP);\r\n\t\t\t\tdJointSetHingeParam(j, dParamVel, Vel);\r\n\t\t\t\tbreak;\r\n\t\t\tcase JOINTTYPE_SLIDER:\r\n\t\t\t\tdJointSetSliderAxis(j, forward[0], forward[1], forward[2]);\r\n\t\t\t\tdJointSetSliderParam(j, dParamFMax, FMax);\r\n\t\t\t\tdJointSetSliderParam(j, dParamHiStop, Stop);\r\n\t\t\t\tdJointSetSliderParam(j, dParamLoStop, -Stop);\r\n\t\t\t\tdJointSetSliderParam(j, dParamStopCFM, CFM);\r\n\t\t\t\tdJointSetSliderParam(j, dParamStopERP, ERP);\r\n\t\t\t\tdJointSetSliderParam(j, dParamVel, Vel);\r\n\t\t\t\tbreak;\r\n\t\t\tcase JOINTTYPE_UNIVERSAL:\r\n\t\t\t\tdJointSetUniversalAnchor(j, origin[0], origin[1], origin[2]);\r\n\t\t\t\tdJointSetUniversalAxis1(j, forward[0], forward[1], forward[2]);\r\n\t\t\t\tdJointSetUniversalAxis2(j, up[0], up[1], up[2]);\r\n\t\t\t\tdJointSetUniversalParam(j, dParamFMax, FMax);\r\n\t\t\t\tdJointSetUniversalParam(j, dParamHiStop, Stop);\r\n\t\t\t\tdJointSetUniversalParam(j, dParamLoStop, -Stop);\r\n\t\t\t\tdJointSetUniversalParam(j, dParamStopCFM, CFM);\r\n\t\t\t\tdJointSetUniversalParam(j, dParamStopERP, ERP);\r\n\t\t\t\tdJointSetUniversalParam(j, dParamVel, Vel);\r\n\t\t\t\tdJointSetUniversalParam(j, dParamFMax2, FMax);\r\n\t\t\t\tdJointSetUniversalParam(j, dParamHiStop2, Stop);\r\n\t\t\t\tdJointSetUniversalParam(j, dParamLoStop2, -Stop);\r\n\t\t\t\tdJointSetUniversalParam(j, dParamStopCFM2, CFM);\r\n\t\t\t\tdJointSetUniversalParam(j, dParamStopERP2, ERP);\r\n\t\t\t\tdJointSetUniversalParam(j, dParamVel2, Vel);\r\n\t\t\t\tbreak;\r\n\t\t\tcase JOINTTYPE_HINGE2:\r\n\t\t\t\tdJointSetHinge2Anchor(j, origin[0], origin[1], origin[2]);\t\t\t\r\n#if ODEVERSION>=MAKE2VER(0,16)\r\n\t\t\t\t{\r\n\t\t\t\t\tdReal a1[]={forward[0], forward[1], forward[2]}, a2[]={velocity[0], velocity[1], velocity[2]};\r\n\t\t\t\t\tdJointSetHinge2Axes(j, a1, a2);\r\n\t\t\t\t}\r\n#else\r\n\t\t\t\tdJointSetHinge2Axis1(j, forward[0], forward[1], forward[2]);\r\n\t\t\t\tdJointSetHinge2Axis2(j, velocity[0], velocity[1], velocity[2]);\r\n#endif\r\n\r\n\t\t\t\tdJointSetHinge2Param(j, dParamFMax, FMax);\r\n\t\t\t\tdJointSetHinge2Param(j, dParamHiStop, Stop);\r\n\t\t\t\tdJointSetHinge2Param(j, dParamLoStop, -Stop);\r\n\t\t\t\tdJointSetHinge2Param(j, dParamStopCFM, CFM);\r\n\t\t\t\tdJointSetHinge2Param(j, dParamStopERP, ERP);\r\n\t\t\t\tdJointSetHinge2Param(j, dParamVel, Vel);\r\n\t\t\t\tdJointSetHinge2Param(j, dParamFMax2, FMax);\r\n\t\t\t\tdJointSetHinge2Param(j, dParamHiStop2, Stop);\r\n\t\t\t\tdJointSetHinge2Param(j, dParamLoStop2, -Stop);\r\n\t\t\t\tdJointSetHinge2Param(j, dParamStopCFM2, CFM);\r\n\t\t\t\tdJointSetHinge2Param(j, dParamStopERP2, ERP);\r\n\t\t\t\tdJointSetHinge2Param(j, dParamVel2, Vel);\r\n\t\t\t\tbreak;\r\n\t\t\tcase JOINTTYPE_FIXED:\r\n\t\t\t\tdJointSetFixed(j);\r\n\t\t\t\tbreak;\r\n\t\t\tcase 0:\r\n\t\t\tdefault:\r\n\t\t\t\tbreak;\r\n\t\t}\r\n\t}\r\n}\r\n\r\nstatic qboolean QDECL World_ODE_RagMatrixToBody(rbebody_t *bodyptr, float *mat)\r\n{\r\n\tdVector3 r[3];\r\n\r\n\tr[0][0] = mat[0];\r\n\tr[0][1] = mat[1];\r\n\tr[0][2] = mat[2];\r\n\tr[1][0] = mat[4];\r\n\tr[1][1] = mat[5];\r\n\tr[1][2] = mat[6];\r\n\tr[2][0] = mat[8];\r\n\tr[2][1] = mat[9];\r\n\tr[2][2] = mat[10];\r\n\r\n\tdBodySetPosition(bodyptr->body, mat[3], mat[7], mat[11]);\r\n\tdBodySetRotation(bodyptr->body, r[0]);\r\n\tdBodySetLinearVel(bodyptr->body, 0, 0, 0);\r\n\tdBodySetAngularVel(bodyptr->body, 0, 0, 0);\r\n\r\n\treturn true;\r\n}\r\nstatic qboolean QDECL World_ODE_RagCreateBody(world_t *world, rbebody_t *bodyptr, rbebodyinfo_t *bodyinfo, float *mat, wedict_t *ent)\r\n{\r\n\tstruct odectx_s *ctx = (struct odectx_s*)world->rbe;\r\n\tdMass mass;\r\n\tfloat radius;\r\n\tctx->hasextraobjs = true;\r\n\t\r\n\tswitch(bodyinfo->geomshape)\r\n\t{\r\n\tcase GEOMTYPE_CAPSULE:\r\n\t\tradius = (bodyinfo->dimensions[0] + bodyinfo->dimensions[1]) * 0.5;\r\n\t\tbodyptr->geom = (void *)dCreateCapsule(ctx->space, radius, bodyinfo->dimensions[2]);\r\n\t\tdMassSetCapsuleTotal(&mass, bodyinfo->mass, 3, radius, bodyinfo->dimensions[2]);\r\n\t\t//aligned along the geom's local z axis\r\n\t\tbreak;\r\n\tcase GEOMTYPE_SPHERE:\r\n\t\t//radius\r\n\t\tradius = (bodyinfo->dimensions[0] + bodyinfo->dimensions[1] + bodyinfo->dimensions[2]) / 3;\r\n\t\tbodyptr->geom = dCreateSphere(ctx->space, radius);\r\n\t\tdMassSetSphereTotal(&mass, bodyinfo->mass, radius);\r\n\t\t//aligned along the geom's local z axis\r\n\t\tbreak;\r\n\tcase GEOMTYPE_CYLINDER:\r\n\t\t//radius, length\r\n\t\tradius = (bodyinfo->dimensions[0] + bodyinfo->dimensions[1]) * 0.5;\r\n\t\tbodyptr->geom = dCreateCylinder(ctx->space, radius, bodyinfo->dimensions[2]);\r\n\t\tdMassSetCylinderTotal(&mass, bodyinfo->mass, 3, radius, bodyinfo->dimensions[2]);\r\n\t\t//alignment is irreleevnt, thouse I suppose it might be scaled wierdly.\r\n\t\tbreak;\r\n\tdefault:\r\n\tcase GEOMTYPE_BOX:\r\n\t\t//diameter\r\n\t\tbodyptr->geom = dCreateBox(ctx->space, bodyinfo->dimensions[0], bodyinfo->dimensions[1], bodyinfo->dimensions[2]);\r\n\t\tdMassSetBoxTotal(&mass, bodyinfo->mass, bodyinfo->dimensions[0], bodyinfo->dimensions[1], bodyinfo->dimensions[2]);\r\n\t\t//monkey\r\n\t\tbreak;\r\n\t}\r\n\tbodyptr->body = dBodyCreate(ctx->dworld);\r\n\tdBodySetMass(bodyptr->body, &mass);\r\n\tdGeomSetBody(bodyptr->geom, bodyptr->body);\r\n\tdGeomSetData(bodyptr->geom, (void*)ent);\r\n\r\n\treturn World_ODE_RagMatrixToBody(bodyptr, mat);\r\n}\r\n\r\nstatic void QDECL World_ODE_RagMatrixFromJoint(rbejoint_t *joint, rbejointinfo_t *info, float *mat)\r\n{\r\n\tdVector3 dr3;\r\n\tswitch(info->type)\r\n\t{\r\n\tcase JOINTTYPE_POINT:\r\n\t\tdJointGetBallAnchor(joint->joint, dr3);\r\n\t\tmat[3] = dr3[0];\r\n\t\tmat[7] = dr3[1];\r\n\t\tmat[11] = dr3[2];\r\n\t\tVectorClear(mat+4);\r\n\t\tVectorClear(mat+8);\r\n\t\tbreak;\r\n\r\n\tcase JOINTTYPE_HINGE:\r\n\t\tdJointGetHingeAnchor(joint->joint, dr3);\r\n\t\tmat[3] = dr3[0];\r\n\t\tmat[7] = dr3[1];\r\n\t\tmat[11] = dr3[2];\r\n\r\n\t\tdJointGetHingeAxis(joint->joint, dr3);\r\n\t\tVectorCopy(dr3, mat+4);\r\n\t\tVectorClear(mat+8);\r\n\r\n\t\tCrossProduct(mat+4, mat+8, mat+0);\r\n\t\treturn;\r\n\t\tbreak;\r\n\tcase JOINTTYPE_HINGE2:\r\n\t\tdJointGetHinge2Anchor(joint->joint, dr3);\r\n\t\tmat[3] = dr3[0];\r\n\t\tmat[7] = dr3[1];\r\n\t\tmat[11] = dr3[2];\r\n\r\n\t\tdJointGetHinge2Axis1(joint->joint, dr3);\r\n\t\tVectorCopy(dr3, mat+4);\r\n\t\tdJointGetHinge2Axis2(joint->joint, dr3);\r\n\t\tVectorCopy(dr3, mat+8);\r\n\t\tbreak;\r\n\r\n\tcase JOINTTYPE_SLIDER:\r\n\t\t//no anchor point...\r\n\t\t//get the two bodies and average their origin for a somewhat usable representation of an anchor.\r\n\t\t{\r\n\t\t\tconst dReal *p1, *p2;\r\n\t\t\tdReal n[3];\r\n\t\t\tdBodyID b1 = dJointGetBody(joint->joint, 0), b2 = dJointGetBody(joint->joint, 1);\r\n\t\t\tif (b1)\r\n\t\t\t\tp1 = dBodyGetPosition(b1);\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tp1 = n;\r\n\t\t\t\tVectorClear(n);\r\n\t\t\t}\r\n\t\t\tif (b2)\r\n\t\t\t\tp2 = dBodyGetPosition(b2);\r\n\t\t\telse\r\n\t\t\t\tp2 = p1;\r\n\t\t\tdJointGetSliderAxis(joint->joint, dr3 + 0);\r\n\t\t\tVectorInterpolate(p1, 0.5, p2, dr3);\r\n\t\t\tmat[3] = dr3[0];\r\n\t\t\tmat[7] = dr3[1];\r\n\t\t\tmat[11] = dr3[2];\r\n\r\n\t\t\tVectorClear(mat+4);\r\n\t\t\tVectorClear(mat+8);\r\n\t\t}\r\n\t\tbreak;\r\n\r\n\tcase JOINTTYPE_UNIVERSAL:\r\n\t\tdJointGetUniversalAnchor(joint->joint, dr3);\r\n\t\tmat[3] = dr3[0];\r\n\t\tmat[7] = dr3[1];\r\n\t\tmat[11] = dr3[2];\r\n\r\n\t\tdJointGetUniversalAxis1(joint->joint, dr3);\r\n\t\tVectorCopy(dr3, mat+4);\r\n\t\tdJointGetUniversalAxis2(joint->joint, dr3);\r\n\t\tVectorCopy(dr3, mat+8);\r\n\r\n\t\tCrossProduct(mat+4, mat+8, mat+0);\r\n\t\treturn;\r\n\t\tbreak;\r\n\t}\r\n\tAngleVectorsFLU(vec3_origin, mat+0, mat+4, mat+8);\r\n}\r\n\r\nstatic void QDECL World_ODE_RagMatrixFromBody(world_t *world, rbebody_t *bodyptr, float *mat)\r\n{\r\n\tconst dReal *o = dBodyGetPosition(bodyptr->body);\r\n\tconst dReal *r = dBodyGetRotation(bodyptr->body);\r\n\tmat[0] = r[0];\r\n\tmat[1] = r[1];\r\n\tmat[2] = r[2];\r\n\tmat[3] = o[0];\r\n\r\n\tmat[4] = r[4];\r\n\tmat[5] = r[5];\r\n\tmat[6] = r[6];\r\n\tmat[7] = o[1];\r\n\r\n\tmat[8] = r[8];\r\n\tmat[9] = r[9];\r\n\tmat[10] = r[10];\r\n\tmat[11] = o[2];\r\n}\r\nstatic void QDECL World_ODE_RagEnableJoint(rbejoint_t *joint, qboolean enabled)\r\n{\r\n\tif (enabled)\r\n\t\tdJointEnable(joint->joint);\r\n\telse\r\n\t\tdJointDisable(joint->joint);\r\n}\r\nstatic void QDECL World_ODE_RagCreateJoint(world_t *world, rbejoint_t *joint, rbejointinfo_t *info, rbebody_t *body1, rbebody_t *body2, vec3_t aaa2[3])\r\n{\r\n\tstruct odectx_s *ctx = (struct odectx_s*)world->rbe;\r\n\tswitch(info->type)\r\n\t{\r\n\t\tcase JOINTTYPE_POINT:\r\n\t\t\tjoint->joint = dJointCreateBall(ctx->dworld, 0);\r\n\t\t\tbreak;\r\n\t\tcase JOINTTYPE_HINGE:\r\n\t\t\tjoint->joint = dJointCreateHinge(ctx->dworld, 0);\r\n\t\t\tbreak;\r\n\t\tcase JOINTTYPE_SLIDER:\r\n\t\t\tjoint->joint = dJointCreateSlider(ctx->dworld, 0);\r\n\t\t\tbreak;\r\n\t\tcase JOINTTYPE_UNIVERSAL:\r\n\t\t\tjoint->joint = dJointCreateUniversal(ctx->dworld, 0);\r\n\t\t\tbreak;\r\n\t\tcase JOINTTYPE_HINGE2:\r\n\t\t\tjoint->joint = dJointCreateHinge2(ctx->dworld, 0);\r\n\t\t\tbreak;\r\n\t\tcase JOINTTYPE_FIXED:\r\n\t\t\tjoint->joint = dJointCreateFixed(ctx->dworld, 0);\r\n\t\t\tbreak;\r\n\t\tdefault:\r\n\t\t\tjoint->joint = NULL;\r\n\t\t\tbreak;\r\n\t}\r\n\tif (joint->joint)\r\n\t{\r\n\t\t//Con_Printf(\"made new joint %i\\n\", (int) (ed - prog->edicts));\r\n//\t\tdJointSetData(joint->ode_joint, NULL);\r\n\t\tdJointAttach(joint->joint, body1?body1->body:NULL, body2?body2->body:NULL);\r\n\r\n\t\tswitch(info->type)\r\n\t\t{\r\n\t\t\tcase JOINTTYPE_POINT:\r\n\t\t\t\tdJointSetBallAnchor(joint->joint, aaa2[0][0], aaa2[0][1], aaa2[0][2]);\r\n\t\t\t\tbreak;\r\n\t\t\tcase JOINTTYPE_HINGE:\r\n\t\t\t\tdJointSetHingeAnchor(joint->joint, aaa2[0][0], aaa2[0][1], aaa2[0][2]);\r\n\t\t\t\tdJointSetHingeAxis(joint->joint, aaa2[1][0], aaa2[1][1], aaa2[1][2]);\r\n\t\t\t\tdJointSetHingeParam(joint->joint, dParamFMax, info->FMax);\r\n\t\t\t\tdJointSetHingeParam(joint->joint, dParamHiStop, info->HiStop);\t\r\n\t\t\t\tdJointSetHingeParam(joint->joint, dParamLoStop, info->LoStop);\r\n\t\t\t\tdJointSetHingeParam(joint->joint, dParamStopCFM, info->CFM);\r\n\t\t\t\tdJointSetHingeParam(joint->joint, dParamStopERP, info->ERP);\r\n\t\t\t\tdJointSetHingeParam(joint->joint, dParamVel, info->Vel);\r\n\t\t\t\tbreak;\r\n\t\t\tcase JOINTTYPE_SLIDER:\r\n\t\t\t\tdJointSetSliderAxis(joint->joint, aaa2[1][0], aaa2[1][1], aaa2[1][2]);\r\n\t\t\t\tdJointSetSliderParam(joint->joint, dParamFMax, info->FMax);\r\n\t\t\t\tdJointSetSliderParam(joint->joint, dParamHiStop, info->HiStop);\r\n\t\t\t\tdJointSetSliderParam(joint->joint, dParamLoStop, info->LoStop);\r\n\t\t\t\tdJointSetSliderParam(joint->joint, dParamStopCFM, info->CFM);\r\n\t\t\t\tdJointSetSliderParam(joint->joint, dParamStopERP, info->ERP);\r\n\t\t\t\tdJointSetSliderParam(joint->joint, dParamVel, info->Vel);\r\n\t\t\t\tbreak;\r\n\t\t\tcase JOINTTYPE_UNIVERSAL:\r\n\t\t\t\tdJointSetUniversalAnchor(joint->joint, aaa2[0][0], aaa2[0][1], aaa2[0][2]);\r\n\t\t\t\tdJointSetUniversalAxis1(joint->joint, aaa2[1][0], aaa2[1][1], aaa2[1][2]);\r\n\t\t\t\tdJointSetUniversalAxis2(joint->joint, aaa2[2][0], aaa2[2][1], aaa2[2][2]);\r\n\t\t\t\tdJointSetUniversalParam(joint->joint, dParamFMax, info->FMax);\r\n\t\t\t\tdJointSetUniversalParam(joint->joint, dParamHiStop, info->HiStop);\r\n\t\t\t\tdJointSetUniversalParam(joint->joint, dParamLoStop, info->LoStop);\r\n\t\t\t\tdJointSetUniversalParam(joint->joint, dParamStopCFM, info->CFM);\r\n\t\t\t\tdJointSetUniversalParam(joint->joint, dParamStopERP, info->ERP);\r\n\t\t\t\tdJointSetUniversalParam(joint->joint, dParamVel, info->Vel);\r\n\t\t\t\tdJointSetUniversalParam(joint->joint, dParamFMax2, info->FMax2);\r\n\t\t\t\tdJointSetUniversalParam(joint->joint, dParamHiStop2, info->HiStop2);\r\n\t\t\t\tdJointSetUniversalParam(joint->joint, dParamLoStop2, info->LoStop2);\r\n\t\t\t\tdJointSetUniversalParam(joint->joint, dParamStopCFM2, info->CFM2);\r\n\t\t\t\tdJointSetUniversalParam(joint->joint, dParamStopERP2, info->ERP2);\r\n\t\t\t\tdJointSetUniversalParam(joint->joint, dParamVel2, info->Vel2);\r\n\t\t\t\tbreak;\r\n\t\t\tcase JOINTTYPE_HINGE2:\r\n\t\t\t\tdJointSetHinge2Anchor(joint->joint, aaa2[0][0], aaa2[0][1], aaa2[0][2]);\r\n#if ODEVERSION>=MAKE2VER(0,16)\r\n\t\t\t\t{\r\n\t\t\t\t\tdReal a1[]={aaa2[1][0], aaa2[1][1], aaa2[1][2]}, a2[]={aaa2[2][0], aaa2[2][1], aaa2[2][2]};\r\n\t\t\t\t\tdJointSetHinge2Axes(joint->joint, a1, a2);\r\n\t\t\t\t}\r\n#else\r\n\t\t\t\tdJointSetHinge2Axis1(joint->joint, aaa2[1][0], aaa2[1][1], aaa2[1][2]);\r\n\t\t\t\tdJointSetHinge2Axis2(joint->joint, aaa2[2][0], aaa2[2][1], aaa2[2][2]);\r\n#endif\r\n\t\t\t\tdJointSetHinge2Param(joint->joint, dParamFMax, info->FMax);\r\n\t\t\t\tdJointSetHinge2Param(joint->joint, dParamHiStop, info->HiStop);\r\n\t\t\t\tdJointSetHinge2Param(joint->joint, dParamLoStop, info->LoStop);\r\n\t\t\t\tdJointSetHinge2Param(joint->joint, dParamStopCFM, info->CFM);\r\n\t\t\t\tdJointSetHinge2Param(joint->joint, dParamStopERP, info->ERP);\r\n\t\t\t\tdJointSetHinge2Param(joint->joint, dParamVel, info->Vel);\r\n\t\t\t\tdJointSetHinge2Param(joint->joint, dParamFMax2, info->FMax2);\r\n\t\t\t\tdJointSetHinge2Param(joint->joint, dParamHiStop2, info->HiStop2);\r\n\t\t\t\tdJointSetHinge2Param(joint->joint, dParamLoStop2, info->LoStop2);\r\n\t\t\t\tdJointSetHinge2Param(joint->joint, dParamStopCFM2, info->CFM2);\r\n\t\t\t\tdJointSetHinge2Param(joint->joint, dParamStopERP2, info->ERP2);\r\n\t\t\t\tdJointSetHinge2Param(joint->joint, dParamVel2, info->Vel2);\r\n\t\t\t\tbreak;\r\n\t\t\tcase JOINTTYPE_FIXED:\r\n\t\t\t\tdJointSetFixed(joint->joint);\r\n\t\t\t\tbreak;\r\n\t\t}\r\n\t}\r\n}\r\n\r\nstatic void QDECL World_ODE_RagDestroyBody(world_t *world, rbebody_t *bodyptr)\r\n{\r\n\tif (bodyptr->geom)\r\n\t\tdGeomDestroy(bodyptr->geom);\r\n\tbodyptr->geom = NULL;\r\n\tif (bodyptr->body)\r\n\t\tdBodyDestroy(bodyptr->body);\r\n\tbodyptr->body = NULL;\r\n}\r\n\r\nstatic void QDECL World_ODE_RagDestroyJoint(world_t *world, rbejoint_t *joint)\r\n{\r\n\tif (joint->joint)\r\n\t\tdJointDestroy(joint->joint);\r\n\tjoint->joint = NULL;\r\n}\r\n\r\nstatic void World_ODE_Frame_BodyFromEntity(world_t *world, wedict_t *ed)\r\n{\r\n\tstruct odectx_s *ctx = (struct odectx_s*)world->rbe;\r\n\tdBodyID body = (dBodyID)ed->rbe.body.body;\r\n\tdMass mass;\r\n\tfloat test;\r\n\tvoid *dataID;\r\n\tmodel_t *model;\r\n\tint axisindex;\r\n\tint modelindex = 0;\r\n\tint movetype = MOVETYPE_NONE;\r\n\tint solid = SOLID_NOT;\r\n\tint geomtype = GEOMTYPE_SOLID;\r\n\tqboolean modified = false;\r\n\tvec3_t angles;\r\n\tvec3_t avelocity;\r\n\tvec3_t entmaxs;\r\n\tvec3_t entmins;\r\n\tvec3_t forward;\r\n\tvec3_t geomcenter;\r\n\tvec3_t geomsize;\r\n\tvec3_t left;\r\n\tvec3_t origin;\r\n\tvec3_t spinvelocity;\r\n\tvec3_t up;\r\n\tvec3_t velocity;\r\n\tvec_t f;\r\n\tvec_t length;\r\n\tvec_t massval = 1.0f;\r\n\tdReal dampLinear;\r\n\tdReal dampAngular;\r\n\tdReal maxAngularSpeed;\r\n\tvec_t movelimit;\r\n\tvec_t radius;\r\n\tvec_t scale;\r\n\tvec_t spinlimit;\r\n\tqboolean gravity;\r\n#ifdef ODE_DYNAMIC\r\n\tif (!ode_dll)\r\n\t\treturn;\r\n#endif\r\n\tgeomtype = (int)ed->xv->geomtype;\r\n\tsolid = (int)ed->v->solid;\r\n\tmovetype = (int)ed->v->movetype;\r\n\tscale = ed->xv->scale?ed->xv->scale:1;\r\n\tdampLinear = (ed->xv->damp_linear >= 0.0f) ? ed->xv->damp_linear : 0.0f;\r\n\tdampAngular = (ed->xv->damp_angular >= 0.0f) ? ed->xv->damp_angular : 0.0f;\r\n\tmaxAngularSpeed = (ed->xv->max_angular > 0.0f) ? ed->xv->max_angular : dInfinity;\r\n\tmodelindex = 0;\r\n\tmodel = NULL;\r\n\r\n\tif (!geomtype)\r\n\t{\r\n\t\tswitch((int)ed->v->solid)\r\n\t\t{\r\n\t\tcase SOLID_NOT:\t\t\t\tgeomtype = GEOMTYPE_NONE;\t\tbreak;\r\n\t\tcase SOLID_TRIGGER:\t\t\tgeomtype = GEOMTYPE_NONE;\t\tbreak;\r\n\t\tcase SOLID_BSP:\t\t\t\tgeomtype = GEOMTYPE_TRIMESH;\tbreak;\r\n\t\tcase SOLID_PHYSICS_TRIMESH:\tgeomtype = GEOMTYPE_TRIMESH;\tbreak;\r\n\t\tcase SOLID_PHYSICS_BOX:\t\tgeomtype = GEOMTYPE_BOX;\t\tbreak;\r\n\t\tcase SOLID_PHYSICS_SPHERE:\tgeomtype = GEOMTYPE_SPHERE;\t\tbreak;\r\n\t\tcase SOLID_PHYSICS_CAPSULE:\tgeomtype = GEOMTYPE_CAPSULE;\tbreak;\r\n\t\tcase SOLID_PHYSICS_CYLINDER:geomtype = GEOMTYPE_CYLINDER;\tbreak;\r\n\t\tdefault:\t\t\t\t\tgeomtype = GEOMTYPE_BOX;\t\tbreak;\r\n\t\t}\r\n\t}\r\n\r\n\tswitch(geomtype)\r\n\t{\r\n\tcase GEOMTYPE_TRIMESH:\r\n\t\tmodelindex = (int)ed->v->modelindex;\r\n\t\tmodel = world->Get_CModel(world, modelindex);\r\n\t\tif (model)\r\n\t\t{\r\n\t\t\tVectorScale(model->mins, scale, entmins);\r\n\t\t\tVectorScale(model->maxs, scale, entmaxs);\r\n\t\t\tif (ed->xv->mass)\r\n\t\t\t\tmassval = ed->xv->mass;\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tVectorClear(entmins);\r\n\t\t\tVectorClear(entmaxs);\r\n\t\t\tmodelindex = 0;\r\n\t\t\tmassval = 1.0f;\r\n\t\t}\r\n\t\tbreak;\r\n\tcase GEOMTYPE_BOX:\r\n\tcase GEOMTYPE_SPHERE:\r\n\tcase GEOMTYPE_CAPSULE:\r\n\tcase GEOMTYPE_CAPSULE_X:\r\n\tcase GEOMTYPE_CAPSULE_Y:\r\n\tcase GEOMTYPE_CAPSULE_Z:\r\n\tcase GEOMTYPE_CYLINDER:\r\n\tcase GEOMTYPE_CYLINDER_X:\r\n\tcase GEOMTYPE_CYLINDER_Y:\r\n\tcase GEOMTYPE_CYLINDER_Z:\r\n\t\tVectorCopy(ed->v->mins, entmins);\r\n\t\tVectorCopy(ed->v->maxs, entmaxs);\r\n\t\tif (ed->xv->mass)\r\n\t\t\tmassval = ed->xv->mass;\r\n\t\tbreak;\r\n\tdefault:\r\n//\tcase GEOMTYPE_NONE:\r\n\t\tif (ed->rbe.physics)\r\n\t\t\tWorld_ODE_RemoveFromEntity(world, ed);\r\n\t\treturn;\r\n\t}\r\n\r\n\tVectorSubtract(entmaxs, entmins, geomsize);\r\n\tif (DotProduct(geomsize,geomsize) == 0)\r\n\t{\r\n\t\t// we don't allow point-size physics objects...\r\n\t\tif (ed->rbe.physics)\r\n\t\t\tWorld_ODE_RemoveFromEntity(world, ed);\r\n\t\treturn;\r\n\t}\r\n\r\n\tif (movetype != MOVETYPE_PHYSICS)\r\n\t\tmassval = 1.0f;\r\n\r\n\t// check if we need to create or replace the geom\r\n\tif (!ed->rbe.physics\r\n\t || !VectorCompare(ed->rbe.mins, entmins)\r\n\t || !VectorCompare(ed->rbe.maxs, entmaxs)\r\n\t || ed->rbe.mass != massval\r\n\t || ed->rbe.modelindex != modelindex)\r\n\t{\r\n\t\tmodified = true;\r\n\t\tWorld_ODE_RemoveFromEntity(world, ed);\r\n\t\ted->rbe.physics = true;\r\n\t\tVectorCopy(entmins, ed->rbe.mins);\r\n\t\tVectorCopy(entmaxs, ed->rbe.maxs);\r\n\t\ted->rbe.mass = massval;\r\n\t\ted->rbe.modelindex = modelindex;\r\n\t\tVectorAvg(entmins, entmaxs, geomcenter);\r\n\t\ted->rbe.movelimit = min(geomsize[0], min(geomsize[1], geomsize[2]));\r\n\r\n\t\tif (massval * geomsize[0] * geomsize[1] * geomsize[2] == 0)\r\n\t\t{\r\n\t\t\tif (movetype == MOVETYPE_PHYSICS)\r\n\t\t\t\tCon_Printf(\"entity %i (classname %s) .mass * .size_x * .size_y * .size_z == 0\\n\", NUM_FOR_EDICT(world->progs, (edict_t*)ed), PR_GetString(world->progs, ed->v->classname));\r\n\t\t\tmassval = 1.0f;\r\n\t\t\tVectorSet(geomsize, 1.0f, 1.0f, 1.0f);\r\n\t\t}\r\n\r\n\t\tswitch(geomtype)\r\n\t\t{\r\n\t\tcase GEOMTYPE_TRIMESH:\r\n\t\t\tMatrix4x4_Identity(ed->rbe.offsetmatrix);\r\n\t\t\ted->rbe.body.geom = NULL;\r\n\t\t\tif (!model)\r\n\t\t\t{\r\n\t\t\t\tCon_Printf(\"entity %i (classname %s) has no model\\n\", NUM_FOR_EDICT(world->progs, (edict_t*)ed), PR_GetString(world->progs, ed->v->classname));\r\n\t\t\t\tif (ed->rbe.physics)\r\n\t\t\t\t\tWorld_ODE_RemoveFromEntity(world, ed);\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\t\t\tif (!rbefuncs->GenerateCollisionMesh(world, model, ed, geomcenter))\r\n\t\t\t{\r\n\t\t\t\tif (ed->rbe.physics)\r\n\t\t\t\t\tWorld_ODE_RemoveFromEntity(world, ed);\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\tMatrix4x4_RM_CreateTranslate(ed->rbe.offsetmatrix, geomcenter[0], geomcenter[1], geomcenter[2]);\r\n\t\t\t// now create the geom\r\n\t\t\tdataID = dGeomTriMeshDataCreate();\r\n\t\t\tdGeomTriMeshDataBuildSingle(dataID, (void*)ed->rbe.vertex3f, sizeof(float[3]), ed->rbe.numvertices, ed->rbe.element3i, ed->rbe.numtriangles*3, sizeof(int[3]));\r\n\t\t\ted->rbe.body.geom = (void *)dCreateTriMesh(ctx->space, dataID, NULL, NULL, NULL);\r\n\t\t\tdMassSetBoxTotal(&mass, massval, geomsize[0], geomsize[1], geomsize[2]);\r\n\t\t\tbreak;\r\n\t\tcase GEOMTYPE_BOX:\r\n\t\t\tMatrix4x4_RM_CreateTranslate(ed->rbe.offsetmatrix, geomcenter[0], geomcenter[1], geomcenter[2]);\r\n\t\t\ted->rbe.body.geom = (void *)dCreateBox(ctx->space, geomsize[0], geomsize[1], geomsize[2]);\r\n\t\t\tdMassSetBoxTotal(&mass, massval, geomsize[0], geomsize[1], geomsize[2]);\r\n\t\t\tbreak;\r\n\t\tcase GEOMTYPE_SPHERE:\r\n\t\t\tMatrix4x4_RM_CreateTranslate(ed->rbe.offsetmatrix, geomcenter[0], geomcenter[1], geomcenter[2]);\r\n\t\t\ted->rbe.body.geom = (void *)dCreateSphere(ctx->space, geomsize[0] * 0.5f);\r\n\t\t\tdMassSetSphereTotal(&mass, massval, geomsize[0] * 0.5f);\r\n\t\t\tbreak;\r\n\t\tcase GEOMTYPE_CAPSULE:\r\n\t\tcase GEOMTYPE_CAPSULE_X:\r\n\t\tcase GEOMTYPE_CAPSULE_Y:\r\n\t\tcase GEOMTYPE_CAPSULE_Z:\r\n\t\t\tif (geomtype == GEOMTYPE_CAPSULE)\r\n\t\t\t{\r\n\t\t\t\taxisindex = 0;\r\n\t\t\t\tif (geomsize[axisindex] < geomsize[1])\r\n\t\t\t\t\taxisindex = 1;\r\n\t\t\t\tif (geomsize[axisindex] < geomsize[2])\r\n\t\t\t\t\taxisindex = 2;\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t\taxisindex = geomtype-GEOMTYPE_CAPSULE_X;\r\n\t\t\t// the qc gives us 3 axis radius, the longest axis is the capsule\r\n\t\t\t// axis, since ODE doesn't like this idea we have to create a\r\n\t\t\t// capsule which uses the standard orientation, and apply a\r\n\t\t\t// transform to it\r\n\t\t\tif (axisindex == 0)\r\n\t\t\t{\r\n\t\t\t\tMatrix4x4_CM_ModelMatrix(ed->rbe.offsetmatrix, geomcenter[0], geomcenter[1], geomcenter[2], 0, 0, 90, 1);\r\n\t\t\t\tradius = min(geomsize[1], geomsize[2]) * 0.5f;\r\n\t\t\t}\r\n\t\t\telse if (axisindex == 1)\r\n\t\t\t{\r\n\t\t\t\tMatrix4x4_CM_ModelMatrix(ed->rbe.offsetmatrix, geomcenter[0], geomcenter[1], geomcenter[2], 90, 0, 0, 1);\r\n\t\t\t\tradius = min(geomsize[0], geomsize[2]) * 0.5f;\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tMatrix4x4_CM_ModelMatrix(ed->rbe.offsetmatrix, geomcenter[0], geomcenter[1], geomcenter[2], 0, 0, 0, 1);\r\n\t\t\t\tradius = min(geomsize[0], geomsize[1]) * 0.5f;\r\n\t\t\t}\r\n\t\t\tlength = geomsize[axisindex] - radius*2;\r\n\t\t\tif (length <= 0)\r\n\t\t\t{\r\n\t\t\t\tradius -= (1 - length)*0.5;\r\n\t\t\t\tlength = 1;\r\n\t\t\t}\r\n\t\t\t// because we want to support more than one axisindex, we have to\r\n\t\t\t// create a transform, and turn on its cleanup setting (which will\r\n\t\t\t// cause the child to be destroyed when it is destroyed)\r\n\t\t\ted->rbe.body.geom = (void *)dCreateCapsule(ctx->space, radius, length);\r\n\t\t\tdMassSetCapsuleTotal(&mass, massval, axisindex+1, radius, length);\r\n\t\t\tbreak;\r\n\t\tcase GEOMTYPE_CYLINDER:\r\n\t\tcase GEOMTYPE_CYLINDER_X:\r\n\t\tcase GEOMTYPE_CYLINDER_Y:\r\n\t\tcase GEOMTYPE_CYLINDER_Z:\r\n\t\t\tif (geomtype == GEOMTYPE_CYLINDER)\r\n\t\t\t{\r\n\t\t\t\taxisindex = 0;\r\n\t\t\t\tif (geomsize[axisindex] < geomsize[1])\r\n\t\t\t\t\taxisindex = 1;\r\n\t\t\t\tif (geomsize[axisindex] < geomsize[2])\r\n\t\t\t\t\taxisindex = 2;\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t\taxisindex = geomtype-GEOMTYPE_CYLINDER_X;\r\n\t\t\t// the qc gives us 3 axis radius, the longest axis is the capsule\r\n\t\t\t// axis, since ODE doesn't like this idea we have to create a\r\n\t\t\t// capsule which uses the standard orientation, and apply a\r\n\t\t\t// transform to it\r\n\t\t\tif (axisindex == 0)\r\n\t\t\t{\r\n\t\t\t\tMatrix4x4_CM_ModelMatrix(ed->rbe.offsetmatrix, geomcenter[0], geomcenter[1], geomcenter[2], 0, 0, 90, 1);\r\n\t\t\t\tradius = min(geomsize[1], geomsize[2]) * 0.5f;\r\n\t\t\t}\r\n\t\t\telse if (axisindex == 1)\r\n\t\t\t{\r\n\t\t\t\tMatrix4x4_CM_ModelMatrix(ed->rbe.offsetmatrix, geomcenter[0], geomcenter[1], geomcenter[2], 90, 0, 0, 1);\r\n\t\t\t\tradius = min(geomsize[0], geomsize[2]) * 0.5f;\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tMatrix4x4_CM_ModelMatrix(ed->rbe.offsetmatrix, geomcenter[0], geomcenter[1], geomcenter[2], 0, 0, 0, 1);\r\n\t\t\t\tradius = min(geomsize[0], geomsize[1]) * 0.5f;\r\n\t\t\t}\r\n\t\t\tlength = geomsize[axisindex] - radius*2;\r\n\t\t\tif (length <= 0)\r\n\t\t\t{\r\n\t\t\t\tradius -= (1 - length)*0.5;\r\n\t\t\t\tlength = 1;\r\n\t\t\t}\r\n\t\t\t// because we want to support more than one axisindex, we have to\r\n\t\t\t// create a transform, and turn on its cleanup setting (which will\r\n\t\t\t// cause the child to be destroyed when it is destroyed)\r\n\t\t\ted->rbe.body.geom = (void *)dCreateCylinder(ctx->space, radius, length);\r\n\t\t\tdMassSetCylinderTotal(&mass, massval, axisindex+1, radius, length);\r\n\t\t\tbreak;\r\n\t\tdefault:\r\n\t\t\tSys_Errorf(\"World_ODE_BodyFromEntity: unrecognized solid value %i was accepted by filter\\n\", solid);\r\n\t\t}\r\n\t\tMatrix3x4_InvertTo4x4_Simple(ed->rbe.offsetmatrix, ed->rbe.offsetimatrix);\r\n\t\ted->rbe.massbuf = BZ_Malloc(sizeof(dMass));\r\n\t\tmemcpy(ed->rbe.massbuf, &mass, sizeof(dMass));\r\n\t}\r\n\r\n\tif(ed->rbe.body.geom)\r\n\t\tdGeomSetData(ed->rbe.body.geom, (void*)ed);\r\n\tif (movetype == MOVETYPE_PHYSICS && ed->rbe.body.geom)\r\n\t{\r\n\t\tif (ed->rbe.body.body == NULL)\r\n\t\t{\r\n\t\t\ted->rbe.body.body = (void *)(body = dBodyCreate(ctx->dworld));\r\n\t\t\tdGeomSetBody(ed->rbe.body.geom, body);\r\n\t\t\tdBodySetData(body, (void*)ed);\r\n\t\t\tdBodySetMass(body, (dMass *) ed->rbe.massbuf);\r\n\t\t\tmodified = true;\r\n\t\t}\r\n\t}\r\n\telse\r\n\t{\r\n\t\tif (ed->rbe.body.body != NULL)\r\n\t\t{\r\n\t\t\tif(ed->rbe.body.geom)\r\n\t\t\t\tdGeomSetBody(ed->rbe.body.geom, 0);\r\n\t\t\tdBodyDestroy((dBodyID) ed->rbe.body.body);\r\n\t\t\ted->rbe.body.body = NULL;\r\n\t\t\tmodified = true;\r\n\t\t}\r\n\t}\r\n\r\n\t// get current data from entity\r\n\tgravity = true;\r\n\tVectorCopy(ed->v->origin, origin);\r\n\tVectorCopy(ed->v->velocity, velocity);\r\n\t//val = PRVM_EDICTFIELDVALUE(ed, prog->fieldoffsets.axis_forward);if (val) VectorCopy(val->vector, forward); else VectorClear(forward);\r\n\t//val = PRVM_EDICTFIELDVALUE(ed, prog->fieldoffsets.axis_left);if (val) VectorCopy(val->vector, left); else VectorClear(left);\r\n\t//val = PRVM_EDICTFIELDVALUE(ed, prog->fieldoffsets.axis_up);if (val) VectorCopy(val->vector, up); else VectorClear(up);\r\n\t//val = PRVM_EDICTFIELDVALUE(ed, prog->fieldoffsets.spinvelocity);if (val) VectorCopy(val->vector, spinvelocity); else VectorClear(spinvelocity);\r\n\tVectorCopy(ed->v->angles, angles);\r\n\tVectorCopy(ed->v->avelocity, avelocity);\r\n\tif (ed == world->edicts || (ed->xv->gravity && ed->xv->gravity <= 0.01))\r\n\t\tgravity = false;\r\n\r\n\t// compatibility for legacy entities\r\n//\tif (!DotProduct(forward,forward) || solid == SOLID_BSP)\r\n\t{\r\n\t\tvec3_t qangles, qavelocity;\r\n\t\tVectorCopy(angles, qangles);\r\n\t\tVectorCopy(avelocity, qavelocity);\r\n\t\r\n\t\tif (ed->v->modelindex)\r\n\t\t{\r\n\t\t\tmodel = world->Get_CModel(world, ed->v->modelindex);\r\n\t\t\tif (!model || model->type == mod_alias)\r\n\t\t\t{\r\n\t\t\t\tqangles[PITCH] *= r_meshpitch.value;\r\n\t\t\t\tqavelocity[PITCH] *= r_meshpitch.value;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tAngleVectorsFLU(qangles, forward, left, up);\r\n\t\t// convert single-axis rotations in avelocity to spinvelocity\r\n\t\t// FIXME: untested math - check signs\r\n\t\tVectorSet(spinvelocity, DEG2RAD(qavelocity[PITCH]), DEG2RAD(qavelocity[ROLL]), DEG2RAD(qavelocity[YAW]));\r\n\t}\r\n\r\n\t// compatibility for legacy entities\r\n\tswitch (solid)\r\n\t{\r\n\tcase SOLID_BBOX:\r\n\tcase SOLID_SLIDEBOX:\r\n\tcase SOLID_CORPSE:\r\n\t\tVectorSet(forward, 1, 0, 0);\r\n\t\tVectorSet(left, 0, 1, 0);\r\n\t\tVectorSet(up, 0, 0, 1);\r\n\t\tVectorSet(spinvelocity, 0, 0, 0);\r\n\t\tbreak;\r\n\t}\r\n\r\n\r\n\t// we must prevent NANs...\r\n\ttest = DotProduct(origin,origin) + DotProduct(forward,forward) + DotProduct(left,left) + DotProduct(up,up) + DotProduct(velocity,velocity) + DotProduct(spinvelocity,spinvelocity);\r\n\tif (IS_NAN(test))\r\n\t{\r\n\t\tmodified = true;\r\n\t\t//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_EDICTFIELDVALUE(ed, prog->fieldoffsets.classname)->string), 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]);\r\n\t\tCon_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\", NUM_FOR_EDICT(world->progs, (edict_t*)ed), PR_GetString(world->progs, ed->v->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]);\r\n\t\ttest = DotProduct(origin,origin);\r\n\t\tif (IS_NAN(test))\r\n\t\t\tVectorClear(origin);\r\n\t\ttest = DotProduct(forward,forward) * DotProduct(left,left) * DotProduct(up,up);\r\n\t\tif (IS_NAN(test))\r\n\t\t{\r\n\t\t\tVectorSet(angles, 0, 0, 0);\r\n\t\t\tVectorSet(forward, 1, 0, 0);\r\n\t\t\tVectorSet(left, 0, 1, 0);\r\n\t\t\tVectorSet(up, 0, 0, 1);\r\n\t\t}\r\n\t\ttest = DotProduct(velocity,velocity);\r\n\t\tif (IS_NAN(test))\r\n\t\t\tVectorClear(velocity);\r\n\t\ttest = DotProduct(spinvelocity,spinvelocity);\r\n\t\tif (IS_NAN(test))\r\n\t\t{\r\n\t\t\tVectorClear(avelocity);\r\n\t\t\tVectorClear(spinvelocity);\r\n\t\t}\r\n\t}\r\n\r\n\t// check if the qc edited any position data\r\n\tif (!VectorCompare(origin, ed->rbe.origin)\r\n\t || !VectorCompare(velocity, ed->rbe.velocity)\r\n\t || !VectorCompare(angles, ed->rbe.angles)\r\n\t || !VectorCompare(avelocity, ed->rbe.avelocity)\r\n\t || gravity != ed->rbe.gravity)\r\n\t\tmodified = true;\r\n\r\n\t// store the qc values into the physics engine\r\n\tbody = ed->rbe.body.body;\r\n\tif (modified && ed->rbe.body.geom)\r\n\t{\r\n\t\tdVector3 r[3];\r\n\t\tfloat entitymatrix[16];\r\n\t\tfloat bodymatrix[16];\r\n\r\n#if 0\r\n\t\tCon_Printf(\"entity %i got changed by QC\\n\", (int) (ed - prog->edicts));\r\n\t\tif(!VectorCompare(origin, ed->rbe.origin))\r\n\t\t\tCon_Printf(\"  origin: %f %f %f -> %f %f %f\\n\", ed->rbe.origin[0], ed->rbe.origin[1], ed->rbe.origin[2], origin[0], origin[1], origin[2]);\r\n\t\tif(!VectorCompare(velocity, ed->rbe.velocity))\r\n\t\t\tCon_Printf(\"  velocity: %f %f %f -> %f %f %f\\n\", ed->rbe.velocity[0], ed->rbe.velocity[1], ed->rbe.velocity[2], velocity[0], velocity[1], velocity[2]);\r\n\t\tif(!VectorCompare(angles, ed->rbe.angles))\r\n\t\t\tCon_Printf(\"  angles: %f %f %f -> %f %f %f\\n\", ed->rbe.angles[0], ed->rbe.angles[1], ed->rbe.angles[2], angles[0], angles[1], angles[2]);\r\n\t\tif(!VectorCompare(avelocity, ed->rbe.avelocity))\r\n\t\t\tCon_Printf(\"  avelocity: %f %f %f -> %f %f %f\\n\", ed->rbe.avelocity[0], ed->rbe.avelocity[1], ed->rbe.avelocity[2], avelocity[0], avelocity[1], avelocity[2]);\r\n\t\tif(gravity != ed->rbe.gravity)\r\n\t\t\tCon_Printf(\"  gravity: %i -> %i\\n\", ed->ide.ode_gravity, gravity);\r\n#endif\r\n\r\n\t\t// values for BodyFromEntity to check if the qc modified anything later\r\n\t\tVectorCopy(origin, ed->rbe.origin);\r\n\t\tVectorCopy(velocity, ed->rbe.velocity);\r\n\t\tVectorCopy(angles, ed->rbe.angles);\r\n\t\tVectorCopy(avelocity, ed->rbe.avelocity);\r\n\t\ted->rbe.gravity = gravity;\r\n\r\n\t\tMatrix4x4_RM_FromVectors(entitymatrix, forward, left, up, origin);\r\n\t\tMatrix4_Multiply(ed->rbe.offsetmatrix, entitymatrix, bodymatrix);\r\n\t\tMatrix3x4_RM_ToVectors(bodymatrix, forward, left, up, origin);\r\n\t\tr[0][0] = forward[0];\r\n\t\tr[1][0] = forward[1];\r\n\t\tr[2][0] = forward[2];\r\n\t\tr[0][1] = left[0];\r\n\t\tr[1][1] = left[1];\r\n\t\tr[2][1] = left[2];\r\n\t\tr[0][2] = up[0];\r\n\t\tr[1][2] = up[1];\r\n\t\tr[2][2] = up[2];\r\n\t\tif(body)\r\n\t\t{\r\n\t\t\tif(movetype == MOVETYPE_PHYSICS)\r\n\t\t\t{\r\n\t\t\t\tdGeomSetBody(ed->rbe.body.geom, body);\r\n\t\t\t\tdBodySetPosition(body, origin[0], origin[1], origin[2]);\r\n\t\t\t\tdBodySetRotation(body, r[0]);\r\n\t\t\t\tdBodySetLinearVel(body, velocity[0], velocity[1], velocity[2]);\r\n\t\t\t\tdBodySetAngularVel(body, spinvelocity[0], spinvelocity[1], spinvelocity[2]);\r\n\t\t\t\tdBodySetGravityMode(body, gravity);\r\n\t\t\t\tdBodySetLinearDamping(body, dampLinear);\r\n\t\t\t\tdBodySetAngularDamping(body, dampAngular);\r\n\t\t\t\tdBodySetMaxAngularSpeed(body, maxAngularSpeed);\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tdGeomSetBody(ed->rbe.body.geom, body);\r\n\t\t\t\tdBodySetPosition(body, origin[0], origin[1], origin[2]);\r\n\t\t\t\tdBodySetRotation(body, r[0]);\r\n\t\t\t\tdBodySetLinearVel(body, velocity[0], velocity[1], velocity[2]);\r\n\t\t\t\tdBodySetAngularVel(body, spinvelocity[0], spinvelocity[1], spinvelocity[2]);\r\n\t\t\t\tdBodySetGravityMode(body, gravity);\r\n\t\t\t\tdBodySetLinearDamping(body, dampLinear);\r\n\t\t\t\tdBodySetAngularDamping(body, dampAngular);\r\n\t\t\t\tdBodySetMaxAngularSpeed(body, maxAngularSpeed);\r\n\t\t\t\tdGeomSetBody(ed->rbe.body.geom, 0);\r\n\t\t\t}\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\t// no body... then let's adjust the parameters of the geom directly\r\n\t\t\tdGeomSetBody(ed->rbe.body.geom, 0); // just in case we previously HAD a body (which should never happen)\r\n\t\t\tdGeomSetPosition(ed->rbe.body.geom, origin[0], origin[1], origin[2]);\r\n\t\t\tdGeomSetRotation(ed->rbe.body.geom, r[0]);\r\n\t\t}\r\n\t}\r\n\r\n\tif(body)\r\n\t{\r\n\t\t// limit movement speed to prevent missed collisions at high speed\r\n\t\tconst dReal *ovelocity = dBodyGetLinearVel(body);\r\n\t\tconst dReal *ospinvelocity = dBodyGetAngularVel(body);\r\n\r\n\t\t// simpler clamp as a last resort\r\n\t\tif (physics_ode_maxspeed->value > 0)\r\n\t\t{\r\n\t\t\tfloat highestAxis = physics_ode_maxspeed->value;\r\n\t\t\tfloat scaleDiff = 1.0;\r\n\r\n\t\t\tfor (int i = 0; i < 3; i++)\r\n\t\t\t{\r\n\t\t\t\tif (velocity[i] > highestAxis)\r\n\t\t\t\t\thighestAxis = velocity[i];\r\n\r\n\t\t\t\tif (velocity[i] < -highestAxis)\r\n\t\t\t\t\thighestAxis = -velocity[i];\r\n\t\t\t}\r\n\r\n\t\t\tscaleDiff = (physics_ode_maxspeed->value / highestAxis);\r\n\r\n\t\t\t// if we should scale it down...\r\n\t\t\tif (scaleDiff < 1.0f) {\r\n\t\t\t\tVectorScale(ovelocity, scaleDiff, velocity);\r\n\t\t\t\tdBodySetLinearVel(body, velocity[0], velocity[1], velocity[2]);\r\n\t\t\t}\r\n\t\t} else {\r\n\t\t\t// this is probably more expensive\r\n\t\t\tmovelimit = ctx->movelimit * ctx->movelimit;\r\n\r\n\t\t\ttest = DotProduct(ovelocity,ovelocity);\r\n\t\t\tif (test > movelimit*movelimit)\r\n\t\t\t{\r\n\t\t\t\t// scale down linear velocity to the movelimit\r\n\t\t\t\t// scale down angular velocity the same amount for consistency\r\n\t\t\t\tf = movelimit / sqrt(test);\r\n\t\t\t\tVectorScale(ovelocity, f, velocity);\r\n\t\t\t\tVectorScale(ospinvelocity, f, spinvelocity);\r\n\t\t\t\tdBodySetLinearVel(body, velocity[0], velocity[1], velocity[2]);\r\n\t\t\t\tdBodySetAngularVel(body, spinvelocity[0], spinvelocity[1], spinvelocity[2]);\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t// make sure the angular velocity is not exploding\r\n\t\tspinlimit = physics_ode_spinlimit->value;\r\n\t\ttest = DotProduct(ospinvelocity,ospinvelocity);\r\n\t\tif (test > spinlimit)\r\n\t\t{\r\n\t\t\tdBodySetAngularVel(body, 0, 0, 0);\r\n\t\t}\r\n\t}\r\n}\r\n\r\n#define MAX_CONTACTS 16\r\nstatic void VARGS nearCallback (void *data, dGeomID o1, dGeomID o2)\r\n{\r\n\tworld_t *world = (world_t *)data;\r\n\tstruct odectx_s *ctx = (struct odectx_s*)world->rbe;\r\n\tdContact contact[MAX_CONTACTS]; // max contacts per collision pair\r\n\tdBodyID b1;\r\n\tdBodyID b2;\r\n\tdJointID c;\r\n\tint i;\r\n\tint numcontacts;\r\n\r\n\tfloat bouncefactor1 = 0.0f;\r\n\tfloat bouncestop1 = 60.0f / 800.0f;\r\n\tfloat bouncefactor2 = 0.0f;\r\n\tfloat bouncestop2 = 60.0f / 800.0f;\r\n\tfloat dampLinear1 = 1.0f;\r\n\tfloat dampAngular1 = 1.0f;\r\n\tfloat dampLinear2 = 1.0f;\r\n\tfloat dampAngular2 = 1.0f;\r\n\tfloat erp;\r\n\tdVector3 grav;\r\n\twedict_t *ed1, *ed2;\r\n\r\n\tif (dGeomIsSpace(o1) || dGeomIsSpace(o2))\r\n\t{\r\n\t\t// colliding a space with something\r\n\t\tdSpaceCollide2(o1, o2, data, &nearCallback);\r\n\t\t// Note we do not want to test intersections within a space,\r\n\t\t// only between spaces.\r\n\t\t//if (dGeomIsSpace(o1)) dSpaceCollide(o1, data, &nearCallback);\r\n\t\t//if (dGeomIsSpace(o2)) dSpaceCollide(o2, data, &nearCallback);\r\n\t\treturn;\r\n\t}\r\n\r\n\tb1 = dGeomGetBody(o1);\r\n\tb2 = dGeomGetBody(o2);\r\n\r\n\t// at least one object has to be using MOVETYPE_PHYSICS or we just don't care\r\n\tif (!b1 && !b2)\r\n\t\treturn;\r\n\r\n\t// exit without doing anything if the two bodies are connected by a joint\r\n\tif (b1 && b2 && dAreConnectedExcluding(b1, b2, dJointTypeContact))\r\n\t\treturn;\r\n\r\n\ted1 = (wedict_t *) dGeomGetData(o1);\r\n\ted2 = (wedict_t *) dGeomGetData(o2);\r\n\tif (ed1 == ed2 && ed1)\r\n\t{\r\n\t\t//ragdolls don't make contact with the bbox of the doll entity\r\n\t\t//the origional entity should probably not be solid anyway.\r\n\t\t//these bodies should probably not collide against bboxes of other entities with ragdolls either, but meh.\r\n\t\tif (ed1->rbe.body.body == b1 || ed2->rbe.body.body == b2)\r\n\t\t\treturn;\r\n\t}\r\n\tif(!ed1 || ED_ISFREE(ed1))\r\n\t\ted1 = world->edicts;\r\n\tif(!ed2 || ED_ISFREE(ed2))\r\n\t\ted2 = world->edicts;\r\n\r\n\t//non-solid things can still interact with pushers, but not other stuff.\r\n\tif (!ed1->v->solid && ed2->v->solid != SOLID_BSP)\r\n\t\treturn;\r\n\tif (!ed2->v->solid && ed1->v->solid != SOLID_BSP)\r\n\t\treturn;\r\n\r\n\tif (ed1->entnum && ed2->entnum)\r\n\t{\r\n\t\tif (!((int)ed2->xv->dimension_solid & (int)ed1->xv->dimension_hit))\r\n\t\t\treturn;\r\n\t\tif (!((int)ed1->xv->dimension_solid & (int)ed2->xv->dimension_hit))\r\n\t\t\treturn;\r\n\t}\r\n\r\n\t// generate contact points between the two non-space geoms\r\n\tnumcontacts = dCollide(o1, o2, MAX_CONTACTS, &(contact[0].geom), sizeof(contact[0]));\r\n\tif (numcontacts)\r\n\t{\r\n\t\ttrace_t contactTrace;\r\n\t\tmemset ( &contactTrace, 0, sizeof ( trace_t ) );\r\n\t\tcontactTrace.endpos[0] = contact[0].geom.pos[0];\r\n\t\tcontactTrace.endpos[1] = contact[0].geom.pos[1];\r\n\t\tcontactTrace.endpos[2] = contact[0].geom.pos[2];\r\n\t\tcontactTrace.plane.normal[0] = contact[0].geom.normal[0];\r\n\t\tcontactTrace.plane.normal[1] = contact[0].geom.normal[1];\r\n\t\tcontactTrace.plane.normal[2] = contact[0].geom.normal[2];\r\n\r\n\t\tif(ed1 && ed1->v->touch)\r\n\t\t{\r\n\t\t\tworld->Event_Touch(world, ed1, ed2, &contactTrace);\r\n\t\t}\r\n\t\tif(ed2 && ed2->v->touch)\r\n\t\t{\r\n\t\t\tworld->Event_Touch(world, ed2, ed1, &contactTrace);\r\n\t\t}\r\n\r\n\t\t/* if either ent killed itself, don't collide */\r\n\t\tif ((ed1&&ED_ISFREE(ed1)) || (ed2&&ED_ISFREE(ed2)))\r\n\t\t\treturn;\r\n\t}\r\n\r\n\tif(ed1)\r\n\t{\r\n\t\tif (ed1->xv->bouncefactor)\r\n\t\t\tbouncefactor1 = ed1->xv->bouncefactor;\r\n\r\n\t\tif (ed1->xv->bouncestop)\r\n\t\t\tbouncestop1 = ed1->xv->bouncestop;\r\n\t}\r\n\r\n\tif(ed2)\r\n\t{\r\n\t\tif (ed2->xv->bouncefactor)\r\n\t\t\tbouncefactor2 = ed2->xv->bouncefactor;\r\n\r\n\t\tif (ed2->xv->bouncestop)\r\n\t\t\tbouncestop2 = ed2->xv->bouncestop;\r\n\t}\r\n\r\n\tif ((ed2->entnum&&ed1->v->owner == ed2->entnum) || (ed1->entnum&&ed2->v->owner == ed1->entnum))\r\n\t\treturn;\r\n\r\n\t// merge bounce factors and bounce stop\r\n\tif(bouncefactor2 > 0)\r\n\t{\r\n\t\tif(bouncefactor1 > 0)\r\n\t\t{\r\n\t\t\t// TODO possibly better logic to merge bounce factor data?\r\n\t\t\tif(bouncestop2 < bouncestop1)\r\n\t\t\t\tbouncestop1 = bouncestop2;\r\n\t\t\tif(bouncefactor2 > bouncefactor1)\r\n\t\t\t\tbouncefactor1 = bouncefactor2;\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tbouncestop1 = bouncestop2;\r\n\t\t\tbouncefactor1 = bouncefactor2;\r\n\t\t}\r\n\t}\r\n\tdWorldGetGravity(ctx->dworld, grav);\r\n\tbouncestop1 *= fabs(grav[2]);\r\n\r\n\terp = (DotProduct(ed1->v->velocity, ed1->v->velocity) > DotProduct(ed2->v->velocity, ed2->v->velocity)) ? ed1->xv->erp : ed2->xv->erp;\r\n\r\n\t// add these contact points to the simulation\r\n\tfor (i = 0;i < numcontacts;i++)\r\n\t{\r\n\t\tcontact[i].surface.mode =\tdContactApprox1 |\r\n\t\t\t\t\t\t\t\t\t(physics_ode_contact_erp->value != -1 ? dContactSoftERP : 0) |\r\n\t\t\t\t\t\t\t\t\t(physics_ode_contact_cfm->value != -1 ? dContactSoftCFM : 0) |\r\n\t\t\t\t\t\t\t\t\t(bouncefactor1 > 0 ? dContactBounce : 0);\r\n\r\n\t\tif (physics_ode_contact_mu->value != -1) {\r\n\t\t\tcontact[i].surface.mu = physics_ode_contact_mu->value;\r\n\r\n\t\t\tif (ed1->xv->friction)\r\n\t\t\t\tcontact[i].surface.mu *= ed1->xv->friction;\r\n\t\t\tif (ed2->xv->friction)\r\n\t\t\t\tcontact[i].surface.mu *= ed2->xv->friction;\r\n\t\t} else {\r\n\t\t\tcontact[i].surface.mu = dInfinity;\r\n\t\t}\r\n\r\n\t\tcontact[i].surface.mu2 = 0;\r\n\t\tcontact[i].surface.soft_erp = physics_ode_contact_erp->value + erp;\r\n\t\tcontact[i].surface.soft_cfm = physics_ode_contact_cfm->value;\r\n\t\tcontact[i].surface.bounce = bouncefactor1;\r\n\t\tcontact[i].surface.bounce_vel = bouncestop1;\r\n\r\n\t\tc = dJointCreateContact(ctx->dworld, ctx->contactgroup, contact + i);\r\n\t\tdJointAttach(c, b1, b2);\r\n\t}\r\n}\r\n\r\nstatic void QDECL World_ODE_Frame(world_t *world, double frametime, double gravity)\r\n{\r\n\tstruct odectx_s *ctx = (struct odectx_s*)world->rbe;\r\n\tif (world->rbe_hasphysicsents || ctx->hasextraobjs)\r\n\t{\r\n\t\tint i;\r\n\t\twedict_t *ed;\r\n\r\n\t\tctx->iterations = bound(1, physics_ode_iterationsperframe->ival, 1000);\r\n\t\tctx->step = frametime / ctx->iterations;\r\n\t\tctx->movelimit = physics_ode_movelimit->value / ctx->step;\r\n\r\n\t\tif (world->rbe_hasphysicsents || ctx->hasextraobjs)\r\n\t\t{\r\n\t\t\t// copy physics properties from entities to physics engine\r\n\t\t\tfor (i = 0;i < world->num_edicts;i++)\r\n\t\t\t{\r\n\t\t\t\ted = (wedict_t*)EDICT_NUM_PB(world->progs, i);\r\n\t\t\t\tif (!ED_ISFREE(ed))\r\n\t\t\t\t\tWorld_ODE_Frame_BodyFromEntity(world, ed);\r\n\t\t\t}\r\n\t\t\t// oh, and it must be called after all bodies were created\r\n\t\t\tfor (i = 0;i < world->num_edicts;i++)\r\n\t\t\t{\r\n\t\t\t\ted = (wedict_t*)EDICT_NUM_PB(world->progs, i);\r\n\t\t\t\tif (!ED_ISFREE(ed))\r\n\t\t\t\t\tWorld_ODE_Frame_JointFromEntity(world, ed);\r\n\t\t\t}\r\n\t\t\twhile(ctx->cmdqueuehead)\r\n\t\t\t{\r\n\t\t\t\trbecommandqueue_t *cmd = ctx->cmdqueuehead;\r\n\t\t\t\tctx->cmdqueuehead = cmd->next;\r\n\t\t\t\tif (!cmd->next)\r\n\t\t\t\t\tctx->cmdqueuetail = NULL;\r\n\t\t\t\tWorld_ODE_RunCmd(world, cmd);\r\n\t\t\t\tZ_Free(cmd);\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tfor (i = 0;i < ctx->iterations;i++)\r\n\t\t{\r\n\t\t\t// set the gravity\r\n\t\t\tdWorldSetGravity(ctx->dworld, 0, 0, -gravity);\r\n\t\t\t// set the tolerance for closeness of objects\r\n\t\t\tdWorldSetContactSurfaceLayer(ctx->dworld, max(0, physics_ode_contactsurfacelayer->value));\r\n\r\n\t\t\t// run collisions for the current world state, creating JointGroup\r\n\t\t\tdSpaceCollide(ctx->space, (void *)world, nearCallback);\r\n\r\n\t\t\t// run physics (move objects, calculate new velocities)\r\n\t\t\tif (physics_ode_worldquickstep->ival)\r\n\t\t\t{\r\n\t\t\t\tdWorldSetQuickStepNumIterations(ctx->dworld, bound(1, physics_ode_worldquickstep_iterations->ival, 200));\r\n\t\t\t\tdWorldQuickStep(ctx->dworld, ctx->step);\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t\tdWorldStep(ctx->dworld, ctx->step);\r\n\r\n\t\t\t// clear the JointGroup now that we're done with it\r\n\t\t\tdJointGroupEmpty(ctx->contactgroup);\r\n\t\t}\r\n\r\n\t\tif (world->rbe_hasphysicsents)\r\n\t\t{\r\n\t\t\t// copy physics properties from physics engine to entities\r\n\t\t\tfor (i = 1;i < world->num_edicts;i++)\r\n\t\t\t{\r\n\t\t\t\ted = (wedict_t*)EDICT_NUM_PB(world->progs, i);\r\n\t\t\t\tif (!ED_ISFREE(ed))\r\n\t\t\t\t\tWorld_ODE_Frame_BodyToEntity(world, ed);\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n}\r\n\r\nstatic void QDECL World_ODE_PushCommand(world_t *world, rbecommandqueue_t *val)\r\n{\r\n\tstruct odectx_s *ctx = (struct odectx_s*)world->rbe;\r\n\trbecommandqueue_t *cmd = (rbecommandqueue_t*)BZ_Malloc(sizeof(*cmd));\r\n\tworld->rbe_hasphysicsents = qtrue;\t//just in case.\r\n\tmemcpy(cmd, val, sizeof(*cmd));\r\n\tcmd->next = NULL;\r\n\t//add on the end of the queue, so that order is preserved.\r\n\tif (ctx->cmdqueuehead)\r\n\t{\r\n\t\trbecommandqueue_t *ot = ctx->cmdqueuetail;\r\n\t\tot->next = ctx->cmdqueuetail = cmd;\r\n\t}\r\n\telse\r\n\t\tctx->cmdqueuetail = ctx->cmdqueuehead = cmd;\r\n}\r\n\r\nstatic void QDECL World_ODE_Start(world_t *world)\r\n{\r\n\tstruct odectx_s *ctx;\r\n\tdVector3 center, extents;\r\n\tif (world->rbe)\r\n\t\treturn;\r\n\r\n#ifdef ODE_DYNAMIC\r\n\tif (!ode_dll)\r\n\t\treturn;\r\n#endif\r\n\r\n\tctx = BZ_Malloc(sizeof(*ctx));\r\n\tmemset(ctx, 0, sizeof(*ctx));\r\n\tworld->rbe = &ctx->pub;\r\n\r\n#ifndef FTEENGINE\r\n\tr_meshpitch.value = cvarfuncs->GetFloat(\"r_meshpitch\");\r\n\tr_meshroll.value = cvarfuncs->GetFloat(\"r_meshroll\");\r\n#endif\r\n\r\n\tVectorAvg(world->worldmodel->mins, world->worldmodel->maxs, center);\r\n\tVectorSubtract(world->worldmodel->maxs, center, extents);\r\n\tctx->dworld = dWorldCreate();\r\n\tctx->space = dQuadTreeSpaceCreate(NULL, center, extents, bound(1, cvarfuncs->GetFloat(\"physics_ode_quadtree_depth\"), 10));\r\n\tctx->contactgroup = dJointGroupCreate(0);\r\n\r\n\tctx->pub.End\t\t\t\t\t= World_ODE_End;\r\n\tctx->pub.RemoveJointFromEntity\t= World_ODE_RemoveJointFromEntity;\r\n\tctx->pub.RemoveFromEntity\t\t= World_ODE_RemoveFromEntity;\r\n\tctx->pub.RagMatrixToBody\t\t= World_ODE_RagMatrixToBody;\r\n\tctx->pub.RagCreateBody\t\t\t= World_ODE_RagCreateBody;\r\n\tctx->pub.RagMatrixFromJoint\t\t= World_ODE_RagMatrixFromJoint;\r\n\tctx->pub.RagMatrixFromBody\t\t= World_ODE_RagMatrixFromBody;\r\n\tctx->pub.RagEnableJoint\t\t\t= World_ODE_RagEnableJoint;\r\n\tctx->pub.RagCreateJoint\t\t\t= World_ODE_RagCreateJoint;\r\n\tctx->pub.RagDestroyBody\t\t\t= World_ODE_RagDestroyBody;\r\n\tctx->pub.RagDestroyJoint\t\t= World_ODE_RagDestroyJoint;\r\n\tctx->pub.RunFrame\t\t\t\t= World_ODE_Frame;\r\n\tctx->pub.PushCommand\t\t\t= World_ODE_PushCommand;\r\n\r\n\tif(physics_ode_world_erp->value >= 0)\r\n\t\tdWorldSetERP(ctx->dworld, physics_ode_world_erp->value);\r\n\tif(physics_ode_world_cfm->value >= 0)\r\n\t\tdWorldSetCFM(ctx->dworld, physics_ode_world_cfm->value);\r\n\tif (physics_ode_world_damping->value)\r\n\t{\r\n\t\tdWorldSetLinearDamping(ctx->dworld, (physics_ode_world_damping_linear->value >= 0) ? (physics_ode_world_damping_linear->value * physics_ode_world_damping->value) : 0);\r\n\t\tdWorldSetLinearDampingThreshold(ctx->dworld, (physics_ode_world_damping_linear_threshold->value >= 0) ? (physics_ode_world_damping_linear_threshold->value * physics_ode_world_damping->value) : 0);\r\n\t\tdWorldSetAngularDamping(ctx->dworld, (physics_ode_world_damping_angular->value >= 0) ? (physics_ode_world_damping_angular->value * physics_ode_world_damping->value) : 0);\r\n\t\tdWorldSetAngularDampingThreshold(ctx->dworld, (physics_ode_world_damping_angular_threshold->value >= 0) ? (physics_ode_world_damping_angular_threshold->value * physics_ode_world_damping->value) : 0);\r\n\t}\r\n\telse\r\n\t{\r\n\t\tdWorldSetLinearDamping(ctx->dworld, 0);\r\n\t\tdWorldSetLinearDampingThreshold(ctx->dworld, 0);\r\n\t\tdWorldSetAngularDamping(ctx->dworld, 0);\r\n\t\tdWorldSetAngularDampingThreshold(ctx->dworld, 0);\r\n\t}\r\n\tif (physics_ode_autodisable->ival)\r\n\t{\r\n\t\tdWorldSetAutoDisableSteps(ctx->dworld, bound(1, physics_ode_autodisable_steps->ival, 100)); \r\n\t\tdWorldSetAutoDisableTime(ctx->dworld, physics_ode_autodisable_time->value);\r\n\t\tdWorldSetAutoDisableAverageSamplesCount(ctx->dworld, bound(1, physics_ode_autodisable_threshold_samples->ival, 100));\r\n\t\tdWorldSetAutoDisableLinearThreshold(ctx->dworld, physics_ode_autodisable_threshold_linear->value); \r\n\t\tdWorldSetAutoDisableAngularThreshold(ctx->dworld, physics_ode_autodisable_threshold_angular->value); \r\n\t\tdWorldSetAutoDisableFlag (ctx->dworld, true);\r\n\t}\r\n\telse\r\n\t\tdWorldSetAutoDisableFlag (ctx->dworld, false);\r\n}\r\n\r\n\r\nstatic void World_ODE_RunCmd(world_t *world, rbecommandqueue_t *cmd)\r\n{\r\n\tswitch(cmd->command)\r\n\t{\r\n\tcase RBECMD_ENABLE:\r\n\t\tif (cmd->edict->rbe.body.body)\r\n\t\t\tdBodyEnable(cmd->edict->rbe.body.body);\r\n\t\tbreak;\r\n\tcase RBECMD_DISABLE:\r\n\t\tif (cmd->edict->rbe.body.body)\r\n\t\t\tdBodyDisable(cmd->edict->rbe.body.body);\r\n\t\tbreak;\r\n\tcase RBECMD_FORCE:\r\n\t\tif (cmd->edict->rbe.body.body)\r\n\t\t{\r\n\t\t\tdBodyEnable(cmd->edict->rbe.body.body);\r\n\t\t\tdBodyAddForceAtPos(cmd->edict->rbe.body.body, cmd->v1[0], cmd->v1[1], cmd->v1[2], cmd->v2[0], cmd->v2[1], cmd->v2[2]);\r\n\t\t}\r\n\t\tbreak;\r\n\tcase RBECMD_TORQUE:\r\n\t\tif (cmd->edict->rbe.body.body)\r\n\t\t{\r\n\t\t\tdBodyEnable(cmd->edict->rbe.body.body);\r\n\t\t\tdBodyAddTorque(cmd->edict->rbe.body.body, cmd->v1[0], cmd->v1[1], cmd->v1[2]);\r\n\t\t}\r\n\t\tbreak;\r\n\t}\r\n}\r\n\r\nstatic void QDECL Plug_ODE_Shutdown(void)\r\n{\r\n\tif (rbefuncs)\r\n\t\trbefuncs->UnregisterPhysicsEngine(\"ODE\");\r\n\tWorld_ODE_Shutdown();\r\n}\r\n\r\nqboolean Plug_Init(void)\r\n{\r\n\trbefuncs = plugfuncs->GetEngineInterface(\"RBE\", sizeof(*rbefuncs));\r\n#ifndef FTEENGINE\r\n\tcvar_r_meshpitch = cvarfuncs->GetNVFDG(\"r_meshpitch\", \"1\", 0, NULL, NULL);\r\n\tcvar_r_meshroll = cvarfuncs->GetNVFDG(\"r_meshroll\", \"1\", 0, NULL, NULL);\r\n#endif\r\n\tif (rbefuncs && (\trbefuncs->version < RBEPLUGFUNCS_VERSION ||\r\n\t\t\t\t\t\trbefuncs->wedictsize != sizeof(wedict_t)))\r\n\t\trbefuncs = NULL;\r\n\tif (!rbefuncs)\r\n\t{\r\n\t\tCon_Printf(\"ODE plugin failed: Engine is incompatible.\\n\");\r\n\t\treturn false;\r\n\t}\r\n\r\n\tif (!rbefuncs || !rbefuncs->RegisterPhysicsEngine)\r\n\t\tCon_Printf(\"ODE plugin failed: Engine doesn't support physics engine plugins.\\n\");\r\n\telse if (!rbefuncs->RegisterPhysicsEngine(\"ODE\", World_ODE_Start))\r\n\t\tCon_Printf(\"ODE plugin failed: Engine already has a physics plugin active.\\n\");\r\n\telse\r\n\t{\r\n\t\tif (!World_ODE_Init())\r\n\t\t{\r\n\t\t\trbefuncs->UnregisterPhysicsEngine(\"ODE\");\r\n\t\t\treturn false;\r\n\t\t}\r\n\t\tplugfuncs->ExportFunction(\"Shutdown\", Plug_ODE_Shutdown);\r\n\t\treturn true;\r\n\t}\r\n\treturn false;\r\n}\r\n#endif\r\n\r\n"
  },
  {
    "path": "engine/common/common.c",
    "content": "/*\nCopyright (C) 1996-1997 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n*/\n// common.c -- misc functions used in client and server\n\n#include \"quakedef.h\"\n\n#include <wctype.h>\n#include <ctype.h>\n#include <errno.h>\n\n#if defined(__EMSCRIPTEN__) && !defined(__EMSCRIPTEN_major__) //ffs\n#include <emscripten/version.h>\n#endif\n\nqboolean sys_nounload;\ndouble\t\thost_frametime;\ndouble\t\trealtime;\t\t\t\t// without any filtering or bounding\nqboolean\thost_initialized;\t\t// true if into command execution (compatability)\nquakeparms_t host_parms;\n\n\n//by adding 'extern' to one definition of a function in a translation unit, then the definition in that TU is NOT considered an inline definition. meaning non-inlined references in other TUs can link to it instead of their own if needed.\nfte_inlinebody conchar_t *Font_Decode(conchar_t *start, unsigned int *codeflags, unsigned int *codepoint);\nfte_inlinebody float M_SRGBToLinear(float x, float mag);\nfte_inlinebody float M_LinearToSRGB(float x, float mag);\n\n\n// These 4 libraries required for the version command\n\n#ifdef AVAIL_ZLIB\n\t#include <zlib.h>\n#endif\n#if defined(FTE_SDL3)\n\t#include <SDL3/SDL.h>\n#elif defined(FTE_SDL)\n\t#include <SDL.h>\n#endif\n\nconst usercmd_t nullcmd; // guarenteed to be zero\n\nentity_state_t nullentitystate;\t//this is the default state\n\nstatic char\t*safeargvs[] =\n\t{\"-stdvid\", \"-nolan\", \"-nosound\", \"-nocdaudio\", \"-nojoy\", \"-nomouse\", \"-nohome\", \"-window\"};\n\nstatic const char\t*largv[MAX_NUM_ARGVS + countof(safeargvs) + 1];\nstatic char\t*argvdummy = \" \";\n\n#ifdef CRAZYDEBUGGING\ncvar_t\tdeveloper = CVAR(\"developer\",\"1\");\n#else\ncvar_t\tdeveloper = CVARD(\"developer\",\"0\", \"Enables the spewing of additional developer/debugging messages. 2 will give even more spam, much of it unwanted.\");\n#endif\n\ncvar_t\tregistered\t\t\t\t= CVARD(\"registered\",\"0\",\"Set if quake's pak1.pak is available\");\ncvar_t\tgameversion\t\t\t\t= CVARFD(\"gameversion\",\"\", CVAR_SERVERINFO, \"gamecode version for server browsers\");\ncvar_t\tgameversion_min\t\t\t= CVARD(\"gameversion_min\",\"\", \"gamecode version for server browsers\");\ncvar_t\tgameversion_max\t\t\t= CVARD(\"gameversion_max\",\"\", \"gamecode version for server browsers\");\n#ifndef SVNREVISION\nstatic cvar_t\tpr_engine\t\t= CVARFD(\"pr_engine\",DISTRIBUTION\" -\", CVAR_NOSAVE, \"This cvar exists so that the menuqc is able to determine which engine-specific settings/values to list/suggest. It must not be used to detect formal QC extensions/builtins. Use checkextension/checkbuiltin/checkcommand for that.\");\n#else\nstatic cvar_t\tpr_engine\t\t= CVARFD(\"pr_engine\",DISTRIBUTION\" \"STRINGIFY(SVNREVISION), CVAR_NOSAVE, \"This cvar exists so that the menuqc is able to determine which engine-specific settings/values to list/suggest. It must not be used to detect formal QC extensions/builtins. Use checkextension/checkbuiltin/checkcommand for that.\");\n#endif\ncvar_t\tfs_gamename\t\t\t\t= CVARAD(\"com_fullgamename\", NULL, \"fs_gamename\", \"The filesystem is trying to run this game\");\ncvar_t\tcom_protocolname\t\t= CVARAD(\"com_protocolname\", NULL, \"com_gamename\", \"The protocol game name used for dpmaster queries. For compatibility with DP, you can set this to 'DarkPlaces-Quake' in order to be listed in DP's master server, and to list DP servers.\");\ncvar_t\tcom_protocolversion\t\t= CVARAD(\"com_protocolversion\", \"3\", NULL, \"The protocol version used for dpmaster queries.\");\t//3 as strong default for compat with DP which uses its netchan rather than protocol version here, even if our QW protocol uses different versions entirely. really it only matters for master servers.\ncvar_t\tcom_parseutf8\t\t\t= CVARD(\"com_parseutf8\", \"1\", \"Interpret console messages/playernames/etc as UTF-8. Requires special fonts. -1=iso 8859-1. 0=quakeascii(chat uses high chars). 1=utf8, revert to ascii on decode errors. 2=utf8 ignoring errors\");\t//1 parse. 2 parse, but stop parsing that string if a char was malformed.\ncvar_t\tcom_highlightcolor\t\t= CVARD(\"com_highlightcolor\", STRINGIFY(COLOR_RED), \"ANSI colour to be used for highlighted text, used when com_parseutf8 is active.\");\ncvar_t\tcom_gamedirnativecode\t= CVARFD(\"com_gamedirnativecode\", \"0\", CVAR_NOTFROMSERVER, FULLENGINENAME\" blocks all downloads of files with a .dll or .so extension, however other engines (eg: ezquake and fodquake) do not - this omission can be used to trigger delayed eremote exploits in any engine (including \"DISTRIBUTION\") which is later run from the same gamedir.\\nQuake2, Quake3(when debugging), and KTX typically run native gamecode from within gamedirs, so if you wish to run any of these games you will need to ensure this cvar is changed to 1, as well as ensure that you don't run unsafe clients.\");\ncvar_t\tsys_platform\t\t\t= CVAR(\"sys_platform\", PLATFORM);\ncvar_t\thost_mapname\t\t\t= CVARAFD(\"mapname\", \"\", \"host_mapname\", 0, \"Cvar that holds the short name of the current map, for scripting type stuff\");\n#ifdef HAVE_LEGACY\ncvar_t\tezcompat_markup\t\t\t= CVARD(\"ezcompat_markup\", \"1\", \"Attempt compatibility with ezquake's text markup.0: disabled.\\n1: Handle markup ampersand markup.\\n2: Handle chevron markup (only in echo commands, for config compat, because its just too unreliable otherwise).\");\ncvar_t\tpm_noround\t\t\t\t= CVARD(\"pm_noround\", \"0\", \"Disables player prediction snapping, in a way that cannot be reliably predicted but may be needed to avoid map bugs.\");\ncvar_t\tscr_usekfont\t\t\t= CVARD(\"scr_usekfont\"/*kex*/, \"0\", \"Exists for compat with the quake rerelease, changing the behaviour of QC's sprint/bprint/centerprint builtins.\");\n#endif\n\nqboolean\tcom_modified;\t// set true if using non-id files\n\nqboolean\t\tstatic_registered = true;\t// only for startup check, then set\n\nqboolean\t\tmsg_suppress_1 = false;\nint\t\t\t\tisPlugin;\t//if 2, we qcdebug to external program\nqboolean\t\twantquit;\n\n\n// if a packfile directory differs from this, it is assumed to be hacked\n#define\tPAK0_COUNT\t\t339\n#define\tPAK0_CRC\t\t52883\n\n#ifdef NQPROT\nqboolean\t\tstandard_quake = true;\t//unfortunately, the vanilla NQ protocol(and 666) subtly changes when -rogue or -hipnotic are used (and by extension -quoth). QW/FTE protocols don't not need to care, but compat...\n#endif\n\n/*\n\n\nAll of Quake's data access is through a hierchal file system, but the contents of the file system can be transparently merged from several sources.\n\nThe \"base directory\" is the path to the directory holding the quake.exe and all game directories.  The sys_* files pass this to host_init in quakeparms_t->basedir.  This can be overridden with the \"-basedir\" command line parm to allow code debugging in a different directory.  The base directory is\nonly used during filesystem initialization.\n\nThe \"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 precacution against having a malicious server instruct clients to write files over areas they shouldn't.\n\nThe \"cache directory\" is only used during development to save network bandwidth, especially over ISDN / T1 lines.  If there is a cache directory\nspecified, when a file is found by the normal search path, it will be mirrored\ninto the cache directory, then opened there.\n\n*/\n\n//============================================================================\n\n\n// ClearLink is used for new headnodes\nvoid ClearLink (link_t *l)\n{\n\tl->prev = l->next = l;\n}\n\nvoid RemoveLink (link_t *l)\n{\n\tl->next->prev = l->prev;\n\tl->prev->next = l->next;\n}\n\nvoid InsertLinkBefore (link_t *l, link_t *before)\n{\n\tl->next = before;\n\tl->prev = before->prev;\n\tl->prev->next = l;\n\tl->next->prev = l;\n}\nvoid InsertLinkAfter (link_t *l, link_t *after)\n{\n\tl->next = after->next;\n\tl->prev = after;\n\tl->prev->next = l;\n\tl->next->prev = l;\n}\n\n/*\n============================================================================\n\n\t\t\t\t\tLIBRARY REPLACEMENT FUNCTIONS\n\n============================================================================\n*/\n\nvoid QDECL Q_strncpyz(char *d, const char *s, int n)\n{\n\tint i;\n\tn--;\n\tif (n < 0)\n\t\treturn;\t//this could be an error\n\n\tfor (i=0; *s; i++)\n\t{\n\t\tif (i == n)\n\t\t\tbreak;\n\t\t*d++ = *s++;\n\t}\n\t*d='\\0';\n}\n\n//returns true on truncation\nqboolean VARGS Q_vsnprintfz (char *dest, size_t size, const char *fmt, va_list argptr)\n{\n\tsize_t ret;\n#ifdef _WIN32\n\t//doesn't null terminate.\n\t//returns -1 on truncation\n\tret = _vsnprintf (dest, size, fmt, argptr);\n\tdest[size-1] = 0;\t//shitty paranoia\n#else\n\t//always null terminates.\n\t//returns length regardless of truncation.\n\tret = vsnprintf (dest, size, fmt, argptr);\n#endif\n#ifdef _DEBUG\n\tif (ret>=size)\n\t\tSys_Error(\"Q_vsnprintfz: Truncation\\n\");\n#endif\n\t//if ret is -1 (windows oversize, or general error) then it'll be treated as unsigned so really long. this makes the following check quite simple.\n\treturn ret>=size;\n}\n\n//windows/linux have inconsistant snprintf\n//this is an attempt to get them consistant and safe\n//size is the total size of the buffer\n//returns true on overflow (will be truncated).\nqboolean VARGS Q_snprintfz (char *dest, size_t size, const char *fmt, ...)\n{\n\tva_list\t\targptr;\n\tsize_t ret;\n\n\tva_start (argptr, fmt);\n#ifdef _WIN32\n\t//doesn't null terminate.\n\t//returns -1 on truncation\n\tret = _vsnprintf (dest, size, fmt, argptr);\n\tdest[size-1] = 0;\t//shitty paranoia\n#else\n\t//always null terminates.\n\t//returns length regardless of truncation.\n\tret = vsnprintf (dest, size, fmt, argptr);\n#endif\n\tva_end (argptr);\n#ifdef _DEBUG\n\tif (ret>=size)\n\t\tSys_Error(\"Q_vsnprintfz: Truncation\\n\");\n#endif\n\t//if ret is -1 (windows oversize, or general error) then it'll be treated as unsigned so really long. this makes the following check quite simple.\n\treturn ret>=size;\n}\n\n\n#if 0\nvoid Q_memset (void *dest, int fill, int count)\n{\n\tint\t\ti;\n\n\tif ( (((long)dest | count) & 3) == 0)\n\t{\n\t\tcount >>= 2;\n\t\tfill = fill | (fill<<8) | (fill<<16) | (fill<<24);\n\t\tfor (i=0 ; i<count ; i++)\n\t\t\t((int *)dest)[i] = fill;\n\t}\n\telse\n\t\tfor (i=0 ; i<count ; i++)\n\t\t\t((qbyte *)dest)[i] = fill;\n}\n\nvoid Q_memcpy (void *dest, void *src, int count)\n{\n\tint\t\ti;\n\n\tif (( ( (long)dest | (long)src | count) & 3) == 0 )\n\t{\n\t\tcount>>=2;\n\t\tfor (i=0 ; i<count ; i++)\n\t\t\t((int *)dest)[i] = ((int *)src)[i];\n\t}\n\telse\n\t\tfor (i=0 ; i<count ; i++)\n\t\t\t((qbyte *)dest)[i] = ((qbyte *)src)[i];\n}\n\nint Q_memcmp (void *m1, void *m2, int count)\n{\n\twhile(count)\n\t{\n\t\tcount--;\n\t\tif (((qbyte *)m1)[count] != ((qbyte *)m2)[count])\n\t\t\treturn -1;\n\t}\n\treturn 0;\n}\n\nvoid Q_strcpy (char *dest, char *src)\n{\n\twhile (*src)\n\t{\n\t\t*dest++ = *src++;\n\t}\n\t*dest++ = 0;\n}\n\nvoid Q_strncpy (char *dest, char *src, int count)\n{\n\twhile (*src && count--)\n\t{\n\t\t*dest++ = *src++;\n\t}\n\tif (count)\n\t\t*dest++ = 0;\n}\n\nint Q_strlen (char *str)\n{\n\tint\t\tcount;\n\n\tcount = 0;\n\twhile (str[count])\n\t\tcount++;\n\n\treturn count;\n}\n\nchar *Q_strrchr(char *s, char c)\n{\n\tint len = Q_strlen(s);\n\ts += len;\n\twhile (len--)\n\t\tif (*--s == c) return s;\n\treturn 0;\n}\n\nvoid Q_strcat (char *dest, char *src)\n{\n\tdest += Q_strlen(dest);\n\tQ_strcpy (dest, src);\n}\n\nint Q_strcmp (char *s1, char *s2)\n{\n\twhile (1)\n\t{\n\t\tif (*s1 != *s2)\n\t\t\treturn -1;\t\t// strings not equal\n\t\tif (!*s1)\n\t\t\treturn 0;\t\t// strings are equal\n\t\ts1++;\n\t\ts2++;\n\t}\n\n\treturn -1;\n}\n\nint Q_strncmp (char *s1, char *s2, int count)\n{\n\twhile (1)\n\t{\n\t\tif (!count--)\n\t\t\treturn 0;\n\t\tif (*s1 != *s2)\n\t\t\treturn -1;\t\t// strings not equal\n\t\tif (!*s1)\n\t\t\treturn 0;\t\t// strings are equal\n\t\ts1++;\n\t\ts2++;\n\t}\n\n\treturn -1;\n}\n\n#endif\n\n//case comparisons are specific to ascii only, so this should be 'safe' for utf-8 strings too.\nint Q_strncasecmp (const char *s1, const char *s2, int n)\n{\n\tint\t\tc1, c2;\n\n\twhile (1)\n\t{\n\t\tc1 = *s1++;\n\t\tc2 = *s2++;\n\n\t\tif (!n--)\n\t\t\treturn 0;\t\t// strings are equal until end point\n\n\t\tif (c1 != c2)\n\t\t{\n\t\t\tif (c1 >= 'a' && c1 <= 'z')\n\t\t\t\tc1 -= ('a' - 'A');\n\t\t\tif (c2 >= 'a' && c2 <= 'z')\n\t\t\t\tc2 -= ('a' - 'A');\n\t\t\tif (c1 != c2)\n\t\t\t{\t// strings not equal\n\t\t\t\tif (c1 > c2)\n\t\t\t\t\treturn 1;\t\t// strings not equal\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t}\n\t\tif (!c1)\n\t\t\treturn 0;\t\t// strings are equal\n//\t\ts1++;\n//\t\ts2++;\n\t}\n\n\treturn -1;\n}\n\nint Q_strcasecmp (const char *s1, const char *s2)\n{\n\treturn Q_strncasecmp (s1, s2, 0x7fffffff);\n}\nint QDECL Q_stricmp (const char *s1, const char *s2)\n{\n\treturn Q_strncasecmp (s1, s2, 0x7fffffff);\n}\nint Q_strstopcasecmp(const char *s1start, const char *s1end, const char *s2)\n{\t//safer version of strncasecmp, where s1 is the one with the length, and must exactly match s2 (which is null terminated and probably an immediate.\n\t//return value isn't suitable for sorting.\n\tif (s1end - s1start != strlen(s2))\n\t\treturn -1;\n\treturn Q_strncasecmp (s1start, s2, s1end - s1start);\n}\n\nchar *Q_strcasestr(const char *haystack, const char *needle)\n{\n\tint c1, c2, c2f;\n\tint i;\n\tc2f = *needle;\n\tif (c2f >= 'a' && c2f <= 'z')\n\t\tc2f -= ('a' - 'A');\n\tif (!c2f)\n\t\treturn (char*)haystack;\n\twhile (1)\n\t{\n\t\tc1 = *haystack;\n\t\tif (!c1)\n\t\t\treturn NULL;\n\t\tif (c1 >= 'a' && c1 <= 'z')\n\t\t\tc1 -= ('a' - 'A');\n\t\tif (c1 == c2f)\n\t\t{\n\t\t\tfor (i = 1; ; i++)\n\t\t\t{\n\t\t\t\tc1 = haystack[i];\n\t\t\t\tc2 = needle[i];\n\t\t\t\tif (c1 >= 'a' && c1 <= 'z')\n\t\t\t\t\tc1 -= ('a' - 'A');\n\t\t\t\tif (c2 >= 'a' && c2 <= 'z')\n\t\t\t\t\tc2 -= ('a' - 'A');\n\t\t\t\tif (!c2)\n\t\t\t\t\treturn (char*)haystack;\t//end of needle means we found a complete match\n\t\t\t\tif (!c1)\t//end of haystack means we can't possibly find needle in it any more\n\t\t\t\t\treturn NULL;\n\t\t\t\tif (c1 != c2)\t//mismatch means no match starting at haystack[0]\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\thaystack++;\n\t}\n\treturn NULL;\t//didn't find it\n}\n\nvoid VARGS Com_sprintf(char *buffer, int size, const char *format, ...)\n{\n\tva_list\t\targptr;\n\n\tva_start (argptr, format);\n\tQ_vsnprintfz (buffer, size, format, argptr);\n\tva_end (argptr);\n}\nvoid\tQDECL Com_Error( int level, const char *error, ... )\n{\n\tSys_Error(\"%s\", error);\n}\n\nchar *Q_strlwr(char *s)\n{\n\tchar *ret=s;\n\twhile(*s)\n\t{\n\t\tif (*s >= 'A' && *s <= 'Z')\n\t\t\t*s=*s-'A'+'a';\n\t\ts++;\n\t}\n\n\treturn ret;\n}\n\nfte_inlinestatic char Q_tolower(char c)\n{\n\tif (c >= 'A' && c <= 'Z')\n\t\treturn c-'A'+'a';\n\treturn c;\n}\nint wildcmp(const char *wild, const char *string)\n{\n/*\n\twhile ((*string) && (*wild != '*'))\n\t{\n\t\tif ((*wild != *string) && (*wild != '?'))\n\t\t{\n\t\t\treturn 0;\n\t\t}\n\t\twild++;\n\t\tstring++;\n\t}\n*/\n\twhile (*string)\n\t{\n\t\tif (*wild == '*')\n\t\t{\n\t\t\tif (*string == '/' || *string == '\\\\')\n\t\t\t{\n\t\t\t\t//* terminates if we get a match on the char following it, or if its a \\ or / char\n\t\t\t\twild++;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (wildcmp(wild+1, string))\n\t\t\t\treturn true;\n\t\t\tstring++;\n\t\t}\n\t\telse if ((Q_tolower(*wild) == Q_tolower(*string)) || (*wild == '?'))\n\t\t{\n\t\t\t//this char matches\n\t\t\twild++;\n\t\t\tstring++;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t//failure\n\t\t\treturn false;\n\t\t}\n\t}\n\n\twhile (*wild == '*')\n\t{\n\t\twild++;\n\t}\n\treturn !*wild;\n}\n\n// Q_ftoa: convert IEEE 754 float to a base-10 string with \"infinite\" decimal places\nvoid Q_ftoa(char *str, float in)\n{\n\tunsigned int i = *((int *)&in);\n\n\tint signbit = (i & 0x80000000) >> 31;\n\tint exp = (signed int)((i & 0x7F800000) >> 23) - 127;\n\tint mantissa = (i & 0x007FFFFF);\n\n\tif (exp == 128) // 255(NaN/Infinity bits) - 127(bias)\n\t{\n\t\tif (signbit)\n\t\t{\n\t\t\t*str = '-';\n\t\t\tstr++;\n\t\t}\n\t\tif (mantissa == 0) // infinity\n\t\t\tstrcpy(str, \"1.#INF\");\n\t\telse // NaN or indeterminate\n\t\t\tstrcpy(str, \"1.#NAN\");\n\t\treturn;\n\t}\n\n\texp = -exp;\n\texp = (int)(exp * 0.30102999957f); // convert base 2 to base 10\n\texp += 8;\n\n\tif (exp <= 0)\n\t\tsprintf(str, \"%.0f\", in);\n\telse\n\t{\n\t\tchar tstr[32];\n\t\tchar *lsig = str - 1;\n\t\tsprintf(tstr, \"%%.%if\", exp);\n\t\tsprintf(str, tstr, in);\n\t\t// find last significant digit and trim\n\t\twhile (*str)\n\t\t{\n\t\t\tif (*str >= '1' && *str <= '9')\n\t\t\t\tlsig = str;\n\t\t\telse if (*str == '.')\n\t\t\t\tlsig = str - 1;\n\t\t\tstr++;\n\t\t}\n\t\tlsig[1] = '\\0';\n\t}\n}\n\nstatic int dehex(int i)\n{\n\tif      (i >= '0' && i <= '9')\n\t\treturn (i-'0');\n\telse if (i >= 'A' && i <= 'F')\n\t\treturn (i-'A'+10);\n\telse\n\t\treturn (i-'a'+10);\n}\n\nint Q_atoi (const char *str)\n{\n\tint\t\tval;\n\tint\t\tsign;\n\tint\t\tc;\n\n\tif (*str == '-')\n\t{\n\t\tsign = -1;\n\t\tstr++;\n\t}\n\telse\n\t\tsign = 1;\n\n\tval = 0;\n\n//\n// check for hex\n//\n\tif (str[0] == '0' && (str[1] == 'x' || str[1] == 'X') )\n\t{\n\t\tstr += 2;\n\t\twhile (1)\n\t\t{\n\t\t\tc = *str++;\n\t\t\tif (c >= '0' && c <= '9')\n\t\t\t\tval = (val<<4) + c - '0';\n\t\t\telse if (c >= 'a' && c <= 'f')\n\t\t\t\tval = (val<<4) + c - 'a' + 10;\n\t\t\telse if (c >= 'A' && c <= 'F')\n\t\t\t\tval = (val<<4) + c - 'A' + 10;\n\t\t\telse\n\t\t\t\treturn val*sign;\n\t\t}\n\t}\n\n//\n// check for character\n//\n\tif (str[0] == '\\'')\n\t{\n\t\treturn sign * str[1];\n\t}\n\n//\n// assume decimal\n//\n\twhile (1)\n\t{\n\t\tc = *str++;\n\t\tif (c <'0' || c > '9')\n\t\t\treturn val*sign;\n\t\tval = val*10 + c - '0';\n\t}\n\n\treturn 0;\n}\n\n\nfloat Q_atof (const char *str)\n{\n\tdouble\tval;\n\tint\t\tsign;\n\tint\t\tc;\n\tint\t\tdecimal, total;\n\n\twhile(*str == ' ')\n\t\tstr++;\n\n\tif (*str == '-')\n\t{\n\t\tsign = -1;\n\t\tstr++;\n\t}\n\telse\n\t\tsign = 1;\n\n\tval = 0;\n\n//\n// check for hex\n//\n\tif (str[0] == '0' && (str[1] == 'x' || str[1] == 'X') )\n\t{\n\t\tstr += 2;\n\t\twhile (1)\n\t\t{\n\t\t\tc = *str++;\n\t\t\tif (c >= '0' && c <= '9')\n\t\t\t\tval = (val*16) + c - '0';\n\t\t\telse if (c >= 'a' && c <= 'f')\n\t\t\t\tval = (val*16) + c - 'a' + 10;\n\t\t\telse if (c >= 'A' && c <= 'F')\n\t\t\t\tval = (val*16) + c - 'A' + 10;\n\t\t\telse\n\t\t\t\treturn val*sign;\n\t\t}\n\t}\n\n//\n// check for character\n//\n\tif (str[0] == '\\'')\n\t{\n\t\treturn sign * str[1];\n\t}\n\n//\n// assume decimal\n//\n\tdecimal = -1;\n\ttotal = 0;\n\twhile (1)\n\t{\n\t\tc = *str++;\n\t\tif (c == '.')\n\t\t{\n\t\t\tdecimal = total;\n\t\t\tcontinue;\n\t\t}\n\t\tif (c <'0' || c > '9')\n\t\t\tbreak;\n\t\tval = val*10 + c - '0';\n\t\ttotal++;\n\t}\n\n\tif (decimal == -1)\n\t\treturn val*sign;\n\twhile (total > decimal)\n\t{\n\t\tval /= 10;\n\t\ttotal--;\n\t}\n\n\treturn val*sign;\n}\n\n/*\nattempts to remove leet strange chars from a name\nthe resulting string is not intended to be visible to humans, but this functions results can be matched against each other.\n*/\nvoid deleetstring(char *result, const char *leet)\n{\n\tchar *s = result;\n\tconst unsigned char *s2 = (const unsigned char*)leet;\n\twhile(*s2)\n\t{\n\t\tif (*s2 == 0xff)\n\t\t{\n\t\t\ts2++;\n\t\t\tcontinue;\n\t\t}\n\t\tif (*s2 >= 0xa0)\n\t\t\t*s = *s2 & ~128;\n\t\telse\n\t\t\t*s = *s2;\n\t\ts2++;\n\t\tif (*s == '3')\n\t\t\t*s = 'e';\n\t\telse if (*s == '4')\n\t\t\t*s = 'a';\n\t\telse if (*s == '0')\n\t\t\t*s = 'o';\n\t\telse if (*s == '1' || *s == '7')\n\t\t\t*s = 'l';\n\t\telse if (*s >= 18 && *s < 27)\n\t\t\t*s = *s - 18 + '0';\n\t\telse if (*s >= 'A' && *s <= 'Z')\n\t\t\t*s = *s - 'A' + 'a';\n\t\telse if (*s == '_' || *s == ' ' || *s == '~')\n\t\t\tcontinue;\n\t\ts++;\n\t}\n\t*s = '\\0';\n}\n\n\n/*\n============================================================================\n\n\t\t\t\t\tqbyte ORDER FUNCTIONS\n\n============================================================================\n*/\n\n#if !defined(FTE_BIG_ENDIAN) && !defined(FTE_LITTLE_ENDIAN)\nqboolean\tbigendian;\n\nshort\t\t(*BigShort)\t\t(short l);\nshort\t\t(*LittleShort)\t(short l);\nint\t\t\t(*BigLong)\t\t(int l);\nint\t\t\t(*LittleLong)\t(int l);\nqint64_t\t(*BigI64)\t\t(qint64_t l);\nqint64_t\t(*LittleI64)\t(qint64_t l);\nfloat\t\t(*BigFloat)\t\t(float l);\nfloat\t\t(*LittleFloat)\t(float l);\n\nstatic short\tShortNoSwap (short l)\t{\treturn l;\t}\nstatic int\t\tLongNoSwap (int l)\t\t{\treturn l;\t}\nstatic qint64_t\tI64NoSwap (qint64_t l)\t{\treturn l;\t}\nstatic float\tFloatNoSwap (float f)\t{\treturn f;\t}\n#endif\n\nshort\t\tShortSwap\t(short l)\n{\n\treturn\t((l>> 8)&0x00ff)|\n\t\t\t((l<< 8)&0xff00);\n}\nint\t\t\tLongSwap\t(int l)\n{\n\treturn\t((l>>24)&0x000000ff)|\n\t\t\t((l>> 8)&0x0000ff00)|\n\t\t\t((l<< 8)&0x00ff0000)|\n\t\t\t((l<<24)&0xff000000);\n}\nqint64_t    I64Swap\t\t(qint64_t l)\n{\n\treturn\t((l>>56)&        0x000000ff)|\n\t\t\t((l>>40)&        0x0000ff00)|\n\t\t\t((l>>24)&        0x00ff0000)|\n\t\t\t((l>> 8)&        0xff000000)|\n\t\t\t((l<< 8)&0x000000ff00000000)|\n\t\t\t((l<<24)&0x0000ff0000000000)|\n\t\t\t((l<<40)&0x00ff000000000000)|\n\t\t\t((l<<56)&0xff00000000000000);\n}\nfloat FloatSwap (float f)\n{\n\tunion\n\t{\n\t\tfloat\tf;\n\t\tqbyte\tb[4];\n\t} dat1, dat2;\n\n\n\tdat1.f = f;\n\tdat2.b[0] = dat1.b[3];\n\tdat2.b[1] = dat1.b[2];\n\tdat2.b[2] = dat1.b[1];\n\tdat2.b[3] = dat1.b[0];\n\treturn dat2.f;\n}\n\nvoid COM_SwapLittleShortBlock (short *s, int size)\n{\n\tif (size <= 0)\n\t\treturn;\n\n\tif (!bigendian)\n\t\treturn;\n\n\twhile (size)\n\t{\n\t\t*s = ShortSwap(*s);\n\t\ts++;\n\t\tsize--;\n\t}\n}\n\nvoid COM_CharBias (signed char *c, int size)\n{\n\tif (size <= 0)\n\t\treturn;\n\n\twhile (size)\n\t{\n\t\t*c = (*(unsigned char *)c) - 128;\n\t\tc++;\n\t\tsize--;\n\t}\n}\n\n/*\n==============================================================================\n\n\t\t\tMESSAGE IO FUNCTIONS\n\nHandles qbyte ordering and avoids alignment errors\n==============================================================================\n*/\n\n//\n// writing functions\n//\n\nvoid MSG_BeginWriting (sizebuf_t *msg, struct netprim_s prim, void *bufferstorage, size_t buffersize)\n{\n\tif (bufferstorage || buffersize)\n\t{\t//otherwise just clear it.\n\t\tmsg->data = bufferstorage;\n\t\tmsg->maxsize = buffersize;\n\t}\n\tmsg->overflowed = false;\n\tmsg->cursize = 0;\n\tmsg->currentbit = 0;\n\tmsg->packing = SZ_RAWBYTES;\n\tmsg->prim = prim;\n\tmsg->allowoverflow = false;\n}\n\nstatic void MSG_WriteRawBytes(sizebuf_t *msg, int value, int bits)\n{\n\tqbyte\t*buf;\n\n\tif (bits <= 8)\n\t{\n\t\tbuf = SZ_GetSpace(msg, 1);\n\t\tbuf[0] = value;\n\t}\n\telse if (bits <= 16)\n\t{\n\t\tbuf = SZ_GetSpace(msg, 2);\n\t\tbuf[0] = value & 0xFF;\n\t\tbuf[1] = value >> 8;\n\t}\n\telse //if (bits <= 32)\n\t{\n\t\tbuf = SZ_GetSpace(msg, 4);\n\t\tbuf[0] = value & 0xFF;\n\t\tbuf[1] = (value >> 8) & 0xFF;\n\t\tbuf[2] = (value >> 16) & 0xFF;\n\t\tbuf[3] = value >> 24;\n\t}\n}\n\nstatic void MSG_WriteRawBits(sizebuf_t *msg, int value, int bits)\n{\n\tint i;\n\tfor (i=0; i<bits; )\n\t{\n\t\tif (!(msg->currentbit&7))\n\t\t{\t//we need another byte now...\n\t\t\tmsg->cursize++;\n\t\t\tif (bits >= 8)\n\t\t\t{\t//splurge an entire byte\n\t\t\t\tmsg->data[msg->currentbit>>3] = (value>>i)&0xff;\n\t\t\t\ti += 8;\n\t\t\t\tmsg->currentbit += 8;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t//clear it for the following 8 bits to splurge\n\t\t\tmsg->data[msg->currentbit>>3] = 0;\n\t\t}\n\t\tmsg->data[msg->currentbit>>3] |= ((value>>i)&1) << (msg->currentbit & 7);\n\t\tmsg->currentbit++;\n\t\ti++;\n\t}\n}\n\n#ifdef HUFFNETWORK\nstatic void MSG_WriteHuffBits(sizebuf_t *msg, int value, int bits)\n{\n\tint\t\tremaining;\n\tint\t\ti;\n\n\tvalue &= 0xFFFFFFFFu >> (32 - bits);\n\tremaining = bits & 7;\n\n\tfor( i=0; i<remaining ; i++ )\n\t{\n\t\tif( !(msg->currentbit & 7) )\n\t\t{\n\t\t\tmsg->data[msg->currentbit >> 3] = 0;\n\t\t}\n\t\tmsg->data[msg->currentbit >> 3] |= (value & 1) << (msg->currentbit & 7);\n\t\tmsg->currentbit++;\n\t\tvalue >>= 1;\n\t}\n\tbits -= remaining;\n\n\tif( bits > 0 )\n\t{\n\t\tfor( i=0 ; i<(bits+7)>>3 ; i++ )\n\t\t{\n\t\t\tHuff_EmitByte( value & 255, msg->data, &msg->currentbit );\n\t\t\tvalue >>= 8;\n\t\t}\n\t}\n\n\tmsg->cursize = (msg->currentbit >> 3) + 1;\n}\n#endif\n\n/*\n============\nMSG_WriteBits\n============\n*/\nvoid MSG_WriteBits(sizebuf_t *msg, int value, int bits)\n{\n\tif( !bits || bits < -31 || bits > 32 )\n\t\tSys_Error(\"MSG_WriteBits: bad bits %i\", bits);\n\n\tif (bits < 0)\n\t{\t//negative means sign extension on reading\n\t\tif (value & (1u<<(bits-1)))\n\t\t\tvalue |= ~1u<<bits;\t//sign extend, just in case it matters (rawbytes with bits < n*8)...\n\t\tbits = -bits;\n\t}\n\n\tswitch( msg->packing )\n\t{\n\tdefault:\n\tcase SZ_BAD:\n\t\tSys_Error(\"MSG_WriteBits: bad msg->packing %i\", msg->packing );\n\t\tbreak;\n\tcase SZ_RAWBYTES:\n\t\tMSG_WriteRawBytes( msg, value, bits );\n\t\tbreak;\n\tcase SZ_RAWBITS:\n\t\tMSG_WriteRawBits( msg, value, bits );\n\t\tbreak;\n#ifdef HUFFNETWORK\n\tcase SZ_HUFFMAN:\n\t\tif( msg->maxsize - msg->cursize < 4 )\n\t\t{\n\t\t\tif (!msg->allowoverflow)\n\t\t\tmsg->overflowed = true;\n\t\t\treturn;\n\t\t}\n\t\tMSG_WriteHuffBits( msg, value, bits );\n\t\tbreak;\n#endif\n\t}\n}\n\nvoid MSG_WriteChar (sizebuf_t *sb, int c)\n{\n\tqbyte\t*buf;\n\n#ifdef PARANOID\n\tif (c < -128 || c > 127)\n\t\tSys_Error (\"MSG_WriteChar: range error\");\n#endif\n\n\tbuf = (qbyte*)SZ_GetSpace (sb, 1);\n\tbuf[0] = c;\n}\n\nvoid MSG_WriteByte (sizebuf_t *sb, int c)\n{\n\tqbyte\t*buf;\n\n#ifdef PARANOID\n\tif (c < 0 || c > 255)\n\t\tSys_Error (\"MSG_WriteByte: range error\");\n#endif\n\n\tbuf = (qbyte*)SZ_GetSpace (sb, 1);\n\tbuf[0] = c&0xff;\n}\n\nvoid MSG_WriteShort (sizebuf_t *sb, int c)\n{\n\tqbyte\t*buf;\n\n#ifdef PARANOID\n\tif (c < ((short)0x8000) || c > (short)0x7fff)\n\t\tSys_Error (\"MSG_WriteShort: range error\");\n#endif\n\n\tbuf = (qbyte*)SZ_GetSpace (sb, 2);\n\tbuf[0] = c&0xff;\n\tbuf[1] = (c>>8)&0xff;\n}\n\nvoid MSG_WriteLong (sizebuf_t *sb, int c)\n{\n\tqbyte\t*buf;\n\n\tbuf = (qbyte*)SZ_GetSpace (sb, 4);\n\tbuf[0] = c&0xff;\n\tbuf[1] = (c>>8)&0xff;\n\tbuf[2] = (c>>16)&0xff;\n\tbuf[3] = (c>>24)&0xff;\n}\nvoid MSG_WriteULEB128 (sizebuf_t *sb, quint64_t c)\n{\n\tqbyte b;\n\tfor(;;)\n\t{\n\t\tb = c&0x7f;\n\t\tc>>=7;\n\t\tif (!c)\n\t\t\tbreak;\n\t\tMSG_WriteByte(sb, b|0x80);\n\t}\n\tMSG_WriteByte(sb, b);\n}\n/*void MSG_WriteSLEB128 (sizebuf_t *sb, qint64_t c)\n{\n\tqbyte b;\n\tfor(;;)\n\t{\n\t\tb = c&0x7f;\n\t\tc>>=7;\n\t\tif ((c==0 && (b&64)==0) || (c==-1 && (b&64)!=0))\n\t\t\tbreak;\n\t\tMSG_WriteByte(sb, b|0x80);\n\t}\n\tMSG_WriteByte(sb, b);\n}*/\nvoid MSG_WriteSignedQEX (sizebuf_t *sb, qint64_t c)\n{\n\tif (c < 0)\n\t\tMSG_WriteULEB128(sb, ((quint64_t)(-1-c)<<1)|1);\n\telse\n\t\tMSG_WriteULEB128(sb, c<<1);\n}\nvoid MSG_WriteUInt64 (sizebuf_t *sb, quint64_t c)\n{\t//0* 10*,*, 110*,*,* etc, up to 0xff followed by 8 continuation bytes\n\tqbyte *buf;\n\tint b = 0;\n\tquint64_t l = 128;\n\twhile (c > l-1u && b < 8)\n\t{\t//count the extra bytes we need\n\t\tb++;\n\t\tl <<= 7;\t//each byte we add gains 8 bits, but we spend one on length.\n\t}\n\tbuf = (qbyte*)SZ_GetSpace (sb, 1+b);\n\t*buf++ = 0xffu<<(8-b) | (c >> (b*8));\n\twhile(b --> 0)\n\t\t*buf++ = (c >> (b*8))&0xff;\n}\nvoid MSG_WriteInt64 (sizebuf_t *sb, qint64_t c)\n{\t//move the sign bit into the low bit and avoid sign extension for more efficient length coding.\n\tif (c < 0)\n\t\tMSG_WriteUInt64(sb, ((quint64_t)(-1-c)<<1)|1);\n\telse\n\t\tMSG_WriteUInt64(sb, c<<1);\n}\n\nvoid MSG_WriteFloat (sizebuf_t *sb, float f)\n{\n\tunion\n\t{\n\t\tfloat\tf;\n\t\tint\tl;\n\t} dat;\n\n\n\tdat.f = f;\n\tdat.l = LittleLong (dat.l);\n\n\tSZ_Write (sb, &dat.l, 4);\n}\nvoid MSG_WriteDouble (sizebuf_t *sb, double f)\n{\n\tunion\n\t{\n\t\tdouble\tf;\n\t\tquint64_t l;\n\t} dat = {f};\n\tquint64_t c = dat.l;\n\tqbyte\t*buf;\n\n\tbuf = (qbyte*)SZ_GetSpace (sb, 8);\n\tbuf[0] = (c>> 0)&0xff;\n\tbuf[1] = (c>> 8)&0xff;\n\tbuf[2] = (c>>16)&0xff;\n\tbuf[3] = (c>>24)&0xff;\n\tbuf[4] = (c>>32)&0xff;\n\tbuf[5] = (c>>40)&0xff;\n\tbuf[6] = (c>>48)&0xff;\n\tbuf[7] = (c>>56)&0xff;\n}\n\nvoid MSG_WriteString (sizebuf_t *sb, const char *s)\n{\n\tif (!s)\n\t\tSZ_Write (sb, \"\", 1);\n\telse\n\t\tSZ_Write (sb, s, Q_strlen(s)+1);\n}\n\nvec_t MSG_FromCoord(coorddata c, int type)\n{\n\tswitch(type)\n\t{\n\tcase COORDTYPE_FIXED_13_3:\t//encode 1/8th precision, giving -4096 to 4096 map sizes\n\t\treturn LittleShort(c.b2)/8.0f;\n\tcase COORDTYPE_FIXED_16_8:\n\t\treturn LittleShort(c.b2) + (((unsigned char*)c.b)[2] * (1/255.0)); /*FIXME: RMQe uses 255, should be 256*/\n\tcase COORDTYPE_FIXED_28_4:\n\t\treturn LittleLong(c.b4)/16.0f;\n\tcase COORDTYPE_FLOAT_32:\n\t\treturn LittleFloat(c.f);\n\tdefault:\n\t\tSys_Error(\"MSG_ToCoord: not a sane coordsize\");\n\t\treturn 0;\n\t}\n}\ncoorddata MSG_ToCoord(float f, int type)\t//return value should be treated as (char*)&ret;\n{\n\tcoorddata r;\n\tswitch(type)\n\t{\n\tcase COORDTYPE_FIXED_13_3:\n\t\tr.b4 = 0;\n\t\tif (f >= 0)\n\t\t\tr.b2 = LittleShort((short)(f*8+0.5f));\n\t\telse\n\t\t\tr.b2 = LittleShort((short)(f*8-0.5f));\n\t\tbreak;\n\tcase COORDTYPE_FIXED_16_8:\n\t\tr.b2 = LittleShort((short)f);\n\t\tr.b[2] = (int)(f*255)%255;\n\t\tr.b[3] = 0;\n\t\tbreak;\n\tcase COORDTYPE_FIXED_28_4:\n\t\tif (f >= 0)\n\t\t\tr.b4 = LittleLong((short)(f*16+0.5f));\n\t\telse\n\t\t\tr.b4 = LittleLong((short)(f*16-0.5f));\n\t\tbreak;\n\tcase COORDTYPE_FLOAT_32:\n\t\tr.f = LittleFloat(f);\n\t\tbreak;\n\tdefault:\n\t\tSys_Error(\"MSG_ToCoord: not a sane coordsize\");\n\t\tr.b4 = 0;\n\t}\n\n\treturn r;\n}\n\ncoorddata MSG_ToAngle(float f, int bytes)\t//return value is NOT byteswapped.\n{\n\tcoorddata r;\n\tswitch(bytes)\n\t{\n\tcase 1:\n\t\tr.b4 = 0;\n\t\tif (f >= 0)\n\t\t\tr.b[0] = (int)(f*(256.0f/360.0f) + 0.5f) & 255;\n\t\telse\n\t\t\tr.b[0] = (int)(f*(256.0f/360.0f) - 0.5f) & 255;\n\t\tbreak;\n\tcase 2:\n\t\tr.b4 = 0;\n\t\tif (f >= 0)\n\t\t\tr.b2 = LittleShort((int)(f*(65536.0f/360.0f) + 0.5f) & 65535);\n\t\telse\n\t\t\tr.b2 = LittleShort((int)(f*(65536.0f/360.0f) - 0.5f) & 65535);\n\t\tbreak;\n\tcase 4:\n\t\tr.f = LittleFloat(f);\n\t\tbreak;\n\tdefault:\n\t\tSys_Error(\"MSG_ToCoord: not a sane coordsize\");\n\t\tr.b4 = 0;\n\t}\n\n\treturn r;\n}\n\nvoid MSG_WriteCoord (sizebuf_t *sb, float f)\n{\n\tcoorddata i = MSG_ToCoord(f, sb->prim.coordtype);\n\tSZ_Write (sb, (void*)&i, sb->prim.coordtype&COORDTYPE_SIZE_MASK);\n}\n\nvoid MSG_WriteAngle16 (sizebuf_t *sb, float f)\n{\n\tif (f >= 0)\n\t\tMSG_WriteShort (sb, (int)(f*(65536.0f/360.0f) + 0.5f) & 65535);\n\telse\n\t\tMSG_WriteShort (sb, (int)(f*(65536.0f/360.0f) - 0.5f) & 65535);\n}\nvoid MSG_WriteAngle8 (sizebuf_t *sb, float f)\n{\n\tif (f >= 0)\n\t\tMSG_WriteByte (sb, (int)(f*(256.0f/360.0f) + 0.5f) & 255);\n\telse\n\t\tMSG_WriteByte (sb, (int)(f*(256.0f/360.0f) - 0.5f) & 255);\n}\n\nvoid MSG_WriteAngle (sizebuf_t *sb, float f)\n{\n\tif (sb->prim.anglesize==2)\n\t\tMSG_WriteAngle16(sb, f);\n\telse if (sb->prim.anglesize==4)\n\t\tMSG_WriteFloat(sb, f);\n\telse if (sb->prim.anglesize==1)\n\t\tMSG_WriteAngle8 (sb, f);\n\telse\n\t\tSys_Error(\"MSG_WriteAngle: undefined network primitive size\");\n}\n\n#if defined(HAVE_CLIENT) || defined(HAVE_SERVER)\nint MSG_ReadSize16 (sizebuf_t *sb)\n{\n\tunsigned short ssolid = MSG_ReadShort();\n\tif (ssolid == ES_SOLID_BSP)\n\t\treturn ssolid;\n\telse\n\t{\n\t\tint solid = (((ssolid>>7) & 0x1F8) - 32+32768)<<16;\t/*up can be negative*/\n\t\tsolid|= ((ssolid & 0x1F)<<3);\n\t\tsolid|= ((ssolid & 0x3E0)<<6);\n\t\treturn solid;\n\t}\n}\nvoid MSG_WriteSize16 (sizebuf_t *sb, unsigned int sz)\n{\n\tif (sz == ES_SOLID_BSP)\n\t\tMSG_WriteShort(sb, ES_SOLID_BSP);\n\telse if (sz)\n\t{\n\t\t//decode the 32bit version and recode it.\n\t\tint x = sz & 255;\n\t\tint zd = (sz >> 8) & 255;\n\t\tint zu = ((sz >> 16) & 65535) - 32768;\n\t\tMSG_WriteShort(sb, \n\t\t\t((x>>3)<<0) |\n\t\t\t((zd>>3)<<5) |\n\t\t\t(((zu+32)>>3)<<10));\n\t}\n\telse\n\t\tMSG_WriteShort(sb, 0);\n}\nvoid COM_DecodeSize(int solid, float *mins, float *maxs)\n{\n#if 0\n\t//q2e 32bit-encoding\n\tint x,y,d,u;\n\tx = (solid>>0)&0xff;\n\ty = (solid>>8)&0xff;\n\td = (solid>>16)&0xff;\n\tu = (solid>>24)&0xff;\n\n\tmins[0] = -x; maxs[0] = x;\n\tmins[1] = -y; maxs[1] = y;\n\tmins[2] = -d; maxs[2] = u - 32;\n#elif 1\n\t//r1q2/q2pro 32bit-encoding\n\tmaxs[0] = maxs[1] = solid & 255;\n\tmins[0] = mins[1] = -maxs[0];\n\tmins[2] = -((solid>>8) & 255);\n\tmaxs[2] = ((solid>>16) & 65535) - 32768;\n#else\n\t//classic q2 16-bit encoding.\n\tmaxs[0] = maxs[1] = 8*(solid & 31);\n\tmins[0] = mins[1] = -maxs[0];\n\tmins[2] = -8*((solid>>5) & 31);\n\tmaxs[2] = 8*((solid>>10) & 63) - 32;\n#endif\n}\nint COM_EncodeSize(const float *mins, const float *maxs)\n{\n\tint solid;\n#if 1\n\tsolid = bound(0, (int)-mins[0], 255);\n\tsolid |= bound(0, (int)-mins[2], 255)<<8;\n\tsolid |= bound(0, (int)((maxs[2]+32768)), 65535)<<16;\t/*up can be negative*/;\n\tif (solid == 0x80000000)\n\t\tsolid = 0;\t//point sized stuff should just be non-solid. you'll thank me for splitscreens.\n#else\n\t//vanilla q2\n\tsolid = bound(0, (int)-mins[0]/8, 31);\n\tsolid |= bound(0, (int)-mins[2]/8, 31)<<5;\n\tsolid |= bound(0, (int)((maxs[2]+32)/8), 63)<<10;\t/*up can be negative*/;\n\tif (solid == 4096)\n\t\tsolid = 0;\t//point sized stuff should just be non-solid. you'll thank me for splitscreens.\n#endif\n\treturn solid;\n}\n\nvoid MSG_WriteEntity(sizebuf_t *sb, unsigned int entnum)\n{\n\tif (entnum > MAX_EDICTS)\n\t\tHost_EndGame(\"index %#x is not a valid entity\\n\", entnum);\n\n\tif (entnum >= 0x8000)\n\t{\n\t\tMSG_WriteShort(sb, (entnum>>8) | 0x8000);\n\t\tMSG_WriteByte(sb, entnum & 0xff);\n\t}\n\telse\n\t\tMSG_WriteShort(sb, entnum);\n}\nunsigned int MSG_ReadBigEntity(void)\n{\n\tunsigned int num;\n\tnum = MSG_ReadShort();\n\tif (num & 0x8000)\n\t{\n\t\tnum = (num & 0x7fff) << 8;\n\t\tnum |= MSG_ReadByte();\n\t}\n\treturn num;\n}\n#endif\n\n//we use the high bit of the entity number to state that this is a large entity.\n#ifdef HAVE_SERVER\nunsigned int MSGSV_ReadEntity(client_t *fromclient)\n{\n\tunsigned int num;\n\tif (fromclient->fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS)\n\t\tnum = MSG_ReadBigEntity();\n\telse\n\t\tnum = (unsigned short)(short)MSG_ReadShort();\n\tif (num >= sv.world.max_edicts)\n\t{\n\t\tCon_Printf(\"client %s sent invalid entity\\n\", fromclient->name);\n\t\tfromclient->drop = true;\n\t\treturn 0;\n\t}\n\treturn num;\n}\n#endif\n#ifdef HAVE_CLIENT\nunsigned int MSGCL_ReadEntity(void)\n{\n\tunsigned int num;\n\tif (cls.fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS)\n\t\tnum = MSG_ReadBigEntity();\n\telse\n\t\tnum = (unsigned short)(short)MSG_ReadShort();\n\treturn num;\n}\n#endif\n\n#if defined(Q2CLIENT) && defined(HAVE_CLIENT)\nvoid MSGQ2EX_WriteDeltaUsercmd (sizebuf_t *buf, const usercmd_t *from, const usercmd_t *cmd)\n{\n\tunsigned int  bits = 0;\n//\tunsigned char buttons = 0;\n\tif (cmd->angles[0] != from->angles[0])\n\t\tbits |= Q2CM_ANGLE1;\n\tif (cmd->angles[1] != from->angles[1])\n\t\tbits |= Q2CM_ANGLE2;\n\tif (cmd->angles[2] != from->angles[2])\n\t\tbits |= Q2CM_ANGLE3;\n\tif (cmd->forwardmove != from->forwardmove)\n\t\tbits |= Q2CM_FORWARD;\n\tif (cmd->sidemove != from->sidemove)\n\t\tbits |= Q2CM_SIDE;\n//\tif (cmd->upmove != from->upmove)\n//\t\tbits |= Q2CM_UP;\n\tif (cmd->buttons != from->buttons)\n\t\tbits |= Q2CM_BUTTONS;\n//\tif (cmd->impulse != from->impulse)\n//\t\tbits |= Q2CM_IMPULSE;\n\n\tMSG_WriteByte (buf, bits);\n\n\tif (bits & Q2CM_ANGLE1)\n\t\tMSG_WriteFloat (buf, SHORT2ANGLE(cmd->angles[0]));\n\tif (bits & Q2CM_ANGLE2)\n\t\tMSG_WriteFloat (buf, SHORT2ANGLE(cmd->angles[1]));\n\tif (bits & Q2CM_ANGLE3)\n\t\tMSG_WriteFloat (buf, SHORT2ANGLE(cmd->angles[2]));\n\n\tif (bits & Q2CM_FORWARD)\n\t\tMSG_WriteFloat (buf, cmd->forwardmove);\n\tif (bits & Q2CM_SIDE)\n\t\tMSG_WriteFloat (buf, cmd->sidemove);\n//\tif (bits & Q2CM_UP)\n//\t\tMSG_WriteFloat (buf, cmd->upmove);\n\n\tif (bits & Q2CM_BUTTONS)\n\t\tMSG_WriteByte (buf, cmd->buttons);\n\t//if (bits & Q2CM_IMPULSE)\n//\t\tMSG_WriteByte (buf, cmd->impulse);\n\tMSG_WriteByte (buf, bound(0, cmd->msec, 250));\t//clamp msecs to 250, because r1q2 likes kicking us if we stall for any reason\n\n//\tMSG_WriteByte (buf, cmd->lightlevel);\n}\nvoid MSGQ2_WriteDeltaUsercmd (sizebuf_t *buf, const usercmd_t *from, const usercmd_t *cmd)\n{\n\tunsigned int  bits = 0;\n\tunsigned char buttons = 0;\n\tif (cmd->angles[0] != from->angles[0])\n\t\tbits |= Q2CM_ANGLE1;\n\tif (cmd->angles[1] != from->angles[1])\n\t\tbits |= Q2CM_ANGLE2;\n\tif (cmd->angles[2] != from->angles[2])\n\t\tbits |= Q2CM_ANGLE3;\n\tif (cmd->forwardmove != from->forwardmove)\n\t\tbits |= Q2CM_FORWARD;\n\tif (cmd->sidemove != from->sidemove)\n\t\tbits |= Q2CM_SIDE;\n\tif (cmd->upmove != from->upmove)\n\t\tbits |= Q2CM_UP;\n\tif (cmd->buttons != from->buttons)\n\t\tbits |= Q2CM_BUTTONS;\n\tif (cmd->impulse != from->impulse)\n\t\tbits |= Q2CM_IMPULSE;\n\n\n\tif (buf->prim.flags & NPQ2_R1Q2_UCMD)\n\t{\n\t\tif (bits & Q2CM_ANGLE1)\n\t\t\tbuttons = cmd->buttons & (1|2|128);\t//attack, jump, any.\n\t\tif ((bits & Q2CM_FORWARD) && !(cmd->forwardmove % 5) && abs(cmd->forwardmove/5) < 128)\n\t\t\tbuttons |= R1Q2_BUTTON_BYTE_FORWARD;\n\t\tif ((bits & Q2CM_SIDE) && !(cmd->sidemove % 5) && abs(cmd->sidemove/5) < 128)\n\t\t\tbuttons |= R1Q2_BUTTON_BYTE_SIDE;\n\t\tif ((bits & Q2CM_UP) && !(cmd->upmove % 5) && abs(cmd->upmove/5) < 128)\n\t\t\tbuttons |= R1Q2_BUTTON_BYTE_UP;\n\t\tif ((bits & Q2CM_ANGLE1) && !(cmd->angles[0] % 64) && abs(cmd->angles[0] / 64) < 128)\n\t\t\tbuttons |= R1Q2_BUTTON_BYTE_ANGLE1;\n\t\tif ((bits & Q2CM_ANGLE2) && !(cmd->angles[1] % 256))\n\t\t\tbuttons |= R1Q2_BUTTON_BYTE_ANGLE2;\n\t\tif (buttons & (R1Q2_BUTTON_BYTE_FORWARD|R1Q2_BUTTON_BYTE_SIDE|R1Q2_BUTTON_BYTE_UP|R1Q2_BUTTON_BYTE_ANGLE1|R1Q2_BUTTON_BYTE_ANGLE2))\n\t\t\tbits |= Q2CM_BUTTONS;\n\t}\n\n\tMSG_WriteByte (buf, bits);\n\tif (buf->prim.flags & NPQ2_R1Q2_UCMD)\n\t{\n\t\tif (bits & Q2CM_BUTTONS)\n\t\t\tMSG_WriteByte (buf, buttons);\n\t}\n\n\tif (bits & Q2CM_ANGLE1)\n\t{\n\t\tif (buttons & R1Q2_BUTTON_BYTE_ANGLE1)\n\t\t\tMSG_WriteChar (buf, cmd->angles[0] / 64);\n\t\telse\n\t\t\tMSG_WriteShort (buf, cmd->angles[0]);\n\t}\n\tif (bits & Q2CM_ANGLE2)\n\t{\n\t\tif (buttons & R1Q2_BUTTON_BYTE_ANGLE2)\n\t\t\tMSG_WriteChar (buf, cmd->angles[1] / 256);\n\t\telse\n\t\t\tMSG_WriteShort (buf, cmd->angles[1]);\n\t}\n\tif (bits & Q2CM_ANGLE3)\n\t\tMSG_WriteShort (buf, cmd->angles[2]);\n\n\tif (bits & Q2CM_FORWARD)\n\t{\n\t\tif (buttons & R1Q2_BUTTON_BYTE_FORWARD)\n\t\t\tMSG_WriteChar (buf, cmd->forwardmove/5);\n\t\telse\n\t\t\tMSG_WriteShort (buf, cmd->forwardmove);\n\t}\n\tif (bits & Q2CM_SIDE)\n\t{\n\t\tif (buttons & R1Q2_BUTTON_BYTE_SIDE)\n\t\t\tMSG_WriteChar (buf, cmd->sidemove/5);\n\t\telse\n\t\t\tMSG_WriteShort (buf, cmd->sidemove);\n\t}\n\tif (bits & Q2CM_UP)\n\t{\n\t\tif (buttons & R1Q2_BUTTON_BYTE_UP)\n\t\t\tMSG_WriteChar (buf, cmd->upmove/5);\n\t\telse\n\t\t\tMSG_WriteShort (buf, cmd->upmove);\n\t}\n\n\tif (!(buf->prim.flags & NPQ2_R1Q2_UCMD))\n\t{\n\t\tif (bits & Q2CM_BUTTONS)\n\t\t\tMSG_WriteByte (buf, cmd->buttons);\n\t}\n\tif (bits & Q2CM_IMPULSE)\n\t\tMSG_WriteByte (buf, cmd->impulse);\n\tMSG_WriteByte (buf, bound(0, cmd->msec, 250));\t//clamp msecs to 250, because r1q2 likes kicking us if we stall for any reason\n\n\tMSG_WriteByte (buf, cmd->lightlevel);\n}\n#endif\n\n#define\tUC_ANGLE1\t\t(1<<0)\n#define\tUC_ANGLE2\t\t(1<<1)\n#define\tUC_ANGLE3\t\t(1<<2)\n#define\tUC_FORWARD\t\t(1<<3)\n#define\tUC_RIGHT\t\t(1<<4)\n#define\tUC_BUTTONS\t\t(1<<5)\n#define\tUC_IMPULSE\t\t(1<<6)\n\n#define\tUC_UP\t\t\t(1<<7)\t//split from forward/right because its rare, and this avoids sending an extra byte.\n#define UC_ABSANG\t\t(1<<8)\t//angle values are shorts\n#define UC_BIGMOVES\t\t(1<<9)\t//fwd/left/up are shorts, rather than a fith.\n#define\tUC_WEAPON\t\t(1<<10)\n#define\tUC_CURSORFLDS\t(1<<11)\t//lots of data in one.\n#define\tUC_LIGHTLEV\t\t(1<<12)\n#define\tUC_VR_HEAD\t\t(1<<13)\n\n#define\tUC_VR_RIGHT\t\t(1<<14)\n#define\tUC_VR_LEFT\t\t(1<<15)\n//#define\tUC_UNUSED\t\t(1<<16)\n//#define\tUC_UNUSED\t\t(1<<17)\n#define\tUC_MSEC_DEBUG\t\t(1<<18)\t//FIXME: temporary\n//#define\tUC_UNUSED\t\t(1<<19)\n//#define\tUC_UNUSED\t\t(1<<20)\n\n//#define\tUC_UNUSED\t\t(1<<21)\n//#define\tUC_UNUSED\t\t(1<<22)\n//#define\tUC_UNUSED\t\t(1<<23)\n//#define\tUC_UNUSED\t\t(1<<24)\n//#define\tUC_UNUSED\t\t(1<<25)\n//#define\tUC_UNUSED\t\t(1<<26)\n//#define\tUC_UNUSED\t\t(1<<27)\n\n//#define\tUC_UNUSED\t\t(1<<28)\n//#define\tUC_UNUSED\t\t(1<<29)\n//#define\tUC_UNUSED\t\t(1<<30)\n//#define\tUC_UNUSED\t\t(1<<31)\n#define UC_UNSUPPORTED (~(UC_ANGLE1 | UC_ANGLE2 | UC_ANGLE3 | UC_FORWARD | UC_RIGHT | UC_BUTTONS | UC_IMPULSE | UC_UP | UC_ABSANG | UC_BIGMOVES | UC_WEAPON | UC_CURSORFLDS | UC_LIGHTLEV | UC_VR_HEAD | UC_VR_RIGHT | UC_VR_LEFT | UC_MSEC_DEBUG))\n\n#define UC_VR_STATUS\t\t(1<<0)\n#define UC_VR_ANG\t\t\t(1<<1)\n#define UC_VR_AVEL\t\t\t(1<<2)\n#define UC_VR_ORG\t\t\t(1<<3)\n#define UC_VR_VEL\t\t\t(1<<4)\n#define UC_VR_WEAPON\t\t(1<<5)\n\n#ifdef HAVE_CLIENT\nfte_inlinestatic qboolean MSG_CompareVR(int i, const usercmd_t *from, const usercmd_t *cmd)\n{\n\tif (cmd->vr[i].status != from->vr[i].status)\n\t\treturn true;\n\treturn\n\t\t(cmd->vr[i].angles[0] != from->vr[i].angles[0]||cmd->vr[i].angles[1] != from->vr[i].angles[1]||cmd->vr[i].angles[2] != from->vr[i].angles[2])|\n\t\t(cmd->vr[i].avelocity[0] != from->vr[i].avelocity[0]||cmd->vr[i].avelocity[1] != from->vr[i].avelocity[1]||cmd->vr[i].avelocity[2] != from->vr[i].avelocity[2])|\n\t\t(cmd->vr[i].origin[0] != from->vr[i].angles[0]||cmd->vr[i].origin[1] != from->vr[i].origin[1]||cmd->vr[i].origin[2] != from->vr[i].origin[2])|\n\t\t(cmd->vr[i].velocity[0] != from->vr[i].velocity[0]||cmd->vr[i].velocity[1] != from->vr[i].velocity[1]||cmd->vr[i].velocity[2] != from->vr[i].velocity[2]);\n}\nstatic  void MSG_WriteVR(int i, sizebuf_t *buf, const usercmd_t *from, const usercmd_t *cmd)\n{\n\tunsigned int bits = 0;\n\tif (cmd->vr[i].status != from->vr[i].status)\n\t\tbits |= UC_VR_STATUS;\n\tif (cmd->vr[i].angles[0] != from->vr[i].angles[0] || cmd->vr[i].angles[1] != from->vr[i].angles[1] || cmd->vr[i].angles[2] != from->vr[i].angles[2])\n\t\tbits |= UC_VR_ANG;\n\tif (cmd->vr[i].avelocity[0] != from->vr[i].avelocity[0] || cmd->vr[i].avelocity[1] != from->vr[i].avelocity[1] || cmd->vr[i].avelocity[2] != from->vr[i].avelocity[2])\n\t\tbits |= UC_VR_AVEL;\n\tif (cmd->vr[i].origin[0] != from->vr[i].origin[0] || cmd->vr[i].origin[1] != from->vr[i].origin[1] || cmd->vr[i].origin[2] != from->vr[i].origin[2])\n\t\tbits |= UC_VR_ORG;\n\tif (cmd->vr[i].velocity[0] != from->vr[i].velocity[0] || cmd->vr[i].velocity[1] != from->vr[i].velocity[1] || cmd->vr[i].velocity[2] != from->vr[i].velocity[2])\n\t\tbits |= UC_VR_VEL;\n\tif (cmd->vr[i].weapon != from->vr[i].weapon)\n\t\tbits |= UC_VR_WEAPON;\n\n\tMSG_WriteUInt64(buf, bits);\n\tif (bits & UC_VR_STATUS)\n\t\tMSG_WriteUInt64(buf, cmd->vr[i].status);\n\tif (bits & UC_VR_ANG)\n\t{\n\t\tMSG_WriteShort(buf, cmd->vr[i].angles[0]);\n\t\tMSG_WriteShort(buf, cmd->vr[i].angles[1]);\n\t\tMSG_WriteShort(buf, cmd->vr[i].angles[2]);\n\t}\n\tif (bits & UC_VR_AVEL)\n\t{\n\t\tMSG_WriteShort(buf, cmd->vr[i].avelocity[0]);\n\t\tMSG_WriteShort(buf, cmd->vr[i].avelocity[1]);\n\t\tMSG_WriteShort(buf, cmd->vr[i].avelocity[2]);\n\t}\n\tif (bits & UC_VR_ORG)\n\t{\n\t\tMSG_WriteFloat(buf, cmd->vr[i].origin[0]);\n\t\tMSG_WriteFloat(buf, cmd->vr[i].origin[1]);\n\t\tMSG_WriteFloat(buf, cmd->vr[i].origin[2]);\n\t}\n\tif (bits & UC_VR_VEL)\n\t{\n\t\tMSG_WriteFloat(buf, cmd->vr[i].velocity[0]);\n\t\tMSG_WriteFloat(buf, cmd->vr[i].velocity[1]);\n\t\tMSG_WriteFloat(buf, cmd->vr[i].velocity[2]);\n\t}\n\tif (bits & UC_VR_WEAPON)\n\t\tMSG_WriteUInt64(buf, cmd->vr[i].weapon);\n}\nvoid MSGFTE_WriteDeltaUsercmd (sizebuf_t *buf, const short baseangles[3], const usercmd_t *from, const usercmd_t *cmd)\n{\n\tunsigned int\t\tbits = 0;\n\tint i;\n\tshort d;\n\n//\n// send the movement message\n//\n\tfor (i = 0; i < 3; i++)\n\t{\n\t\td = cmd->angles[i]-from->angles[i];\n\t\tif (d)\n\t\t{\n\t\t\tbits |= UC_ANGLE1<<i;\n\t\t\tif (d < -128 || d > 127)\n\t\t\t\tbits |= UC_ABSANG;\t//can't delta it.\n\t\t}\n\t}\n\tif (cmd->forwardmove != from->forwardmove)\n\t{\n\t\tbits |= UC_FORWARD;\n\t\tif ((cmd->forwardmove%5) || cmd->forwardmove > 127*5 || cmd->forwardmove < -128*5)\n\t\t\tbits |= UC_BIGMOVES;\t//can't compact it.\n\t}\n\tif (cmd->sidemove != from->sidemove)\n\t{\n\t\tbits |= UC_RIGHT;\n\t\tif ((cmd->sidemove%5) || cmd->sidemove > 127*5 || cmd->sidemove < -128*5)\n\t\t\tbits |= UC_BIGMOVES;\t//can't compact it.\n\t}\n\tif (cmd->upmove != from->upmove)\n\t{\n\t\tbits |= UC_UP;\n\t\tif ((cmd->upmove%5) || cmd->upmove > 127*5 || cmd->upmove < -128*5)\n\t\t\tbits |= UC_BIGMOVES;\t//can't compact it.\n\t}\n\n\tif (cmd->buttons != from->buttons)\n\t\tbits |= UC_BUTTONS;\n\tif (cmd->buttons != from->buttons)\n\t\tbits |= UC_WEAPON;\n\tif (cmd->impulse != from->impulse)\n\t\tbits |= UC_IMPULSE;\n\tif (cmd->lightlevel != from->lightlevel)\n\t\tbits |= UC_LIGHTLEV;\n\n\tif (cmd->cursor_screen[0] != from->cursor_screen[0] || cmd->cursor_screen[1] != from->cursor_screen[1] ||\n\t\tcmd->cursor_start[0] != from->cursor_start[0] || cmd->cursor_start[1] != from->cursor_start[1] || cmd->cursor_start[2] != from->cursor_start[2] ||\n\t\tcmd->cursor_impact[0] != from->cursor_impact[0] || cmd->cursor_impact[1] != from->cursor_impact[1] || cmd->cursor_impact[2] != from->cursor_impact[2] ||\n\t\tcmd->cursor_entitynumber != from->cursor_entitynumber)\n\t\tbits |= UC_CURSORFLDS;\n\n\tif (MSG_CompareVR(VRDEV_HEAD, from, cmd))\n\t\tbits |= UC_VR_HEAD;\n\tif (MSG_CompareVR(VRDEV_RIGHT, from, cmd))\n\t\tbits |= UC_VR_RIGHT;\n\tif (MSG_CompareVR(VRDEV_LEFT, from, cmd))\n\t\tbits |= UC_VR_LEFT;\n\n#ifdef _DEBUG\n\tif (developer.ival)\n\t\tbits |= UC_MSEC_DEBUG;\n#endif\n\n\t//NOTE: WriteUInt64 actually uses some length coding, so its not quite as bloated as it looks.\n\tMSG_WriteUInt64(buf, bits);\n\n\tMSG_WriteUInt64(buf, cmd->servertime-from->servertime);\n\tif (bits & UC_MSEC_DEBUG)\n\t\tMSG_WriteUInt64(buf, cmd->msec);\n\tfor (i = 0; i < 3; i++)\n\t{\n\t\tif (bits & (UC_ANGLE1<<i))\n\t\t{\n\t\t\tif (bits & UC_ABSANG)\n\t\t\t\tMSG_WriteShort(buf, cmd->angles[i]-baseangles[i]);\n\t\t\telse\n\t\t\t\tMSG_WriteChar(buf, cmd->angles[i]-from->angles[i]);\n\t\t}\n\t}\n\tif (bits & UC_FORWARD)\n\t{\n\t\tif (bits & UC_BIGMOVES)\n\t\t\tMSG_WriteInt64(buf, cmd->forwardmove);\n\t\telse\n\t\t\tMSG_WriteChar(buf, cmd->forwardmove/5);\n\t}\n\tif (bits & UC_RIGHT)\n\t{\n\t\tif (bits & UC_BIGMOVES)\n\t\t\tMSG_WriteInt64(buf, cmd->sidemove);\n\t\telse\n\t\t\tMSG_WriteChar(buf, cmd->sidemove/5);\n\t}\n\tif (bits & UC_UP)\n\t{\n\t\tif (bits & UC_BIGMOVES)\n\t\t\tMSG_WriteInt64(buf, cmd->upmove);\n\t\telse\n\t\t\tMSG_WriteChar(buf, cmd->upmove/5);\n\t}\n\n\n\tif (bits & UC_BUTTONS)\n\t\tMSG_WriteUInt64 (buf, cmd->buttons);\n\tif (bits & UC_IMPULSE)\n\t\tMSG_WriteUInt64 (buf, cmd->impulse);\n\tif (bits & UC_WEAPON)\n\t\tMSG_WriteUInt64 (buf, cmd->weapon);\n\tif (bits & UC_CURSORFLDS)\n\t{\n\t\t//prydon cursor crap. kinda bloated.\n\t\tMSG_WriteShort(buf, cmd->cursor_screen[0] * 32767);\n\t\tMSG_WriteShort(buf, cmd->cursor_screen[1] * 32767);\n\t\tMSG_WriteFloat(buf, cmd->cursor_start[0]);\t//avoiding WriteAngle/WriteCoord means we can avoid netprim size difference issues.\n\t\tMSG_WriteFloat(buf, cmd->cursor_start[1]);\n\t\tMSG_WriteFloat(buf, cmd->cursor_start[2]);\n\t\tMSG_WriteFloat(buf, cmd->cursor_impact[0]);\n\t\tMSG_WriteFloat(buf, cmd->cursor_impact[1]);\n\t\tMSG_WriteFloat(buf, cmd->cursor_impact[2]);\n\t\tMSG_WriteEntity(buf, cmd->cursor_entitynumber);\n\t}\n\tif (bits & UC_LIGHTLEV)\n\t\tMSG_WriteUInt64 (buf, cmd->lightlevel);\t//yay hdr?\n\n\tif (bits & UC_VR_HEAD)\n\t\tMSG_WriteVR(VRDEV_HEAD, buf, from, cmd);\n\tif (bits & UC_VR_RIGHT)\n\t\tMSG_WriteVR(VRDEV_RIGHT, buf, from, cmd);\n\tif (bits & UC_VR_LEFT)\n\t\tMSG_WriteVR(VRDEV_LEFT, buf, from, cmd);\n}\n#endif\n#ifdef HAVE_SERVER\nstatic void MSG_ReadVR(int i, usercmd_t *cmd)\n{\n\tquint64_t bits = MSG_ReadUInt64();\n\tif (bits & UC_VR_STATUS)\n\t\tcmd->vr[i].status = MSG_ReadUInt64();\n\tif (bits & UC_VR_ANG)\n\t{\n\t\tcmd->vr[i].angles[0] = MSG_ReadShort();\n\t\tcmd->vr[i].angles[1] = MSG_ReadShort();\n\t\tcmd->vr[i].angles[2] = MSG_ReadShort();\n\t}\n\tif (bits & UC_VR_AVEL)\n\t{\n\t\tcmd->vr[i].avelocity[0] = MSG_ReadShort();\n\t\tcmd->vr[i].avelocity[1] = MSG_ReadShort();\n\t\tcmd->vr[i].avelocity[2] = MSG_ReadShort();\n\t}\n\tif (bits & UC_VR_ORG)\n\t{\n\t\tcmd->vr[i].origin[0] = MSG_ReadFloat();\n\t\tcmd->vr[i].origin[1] = MSG_ReadFloat();\n\t\tcmd->vr[i].origin[2] = MSG_ReadFloat();\n\t}\n\tif (bits & UC_VR_VEL)\n\t{\n\t\tcmd->vr[i].velocity[0] = MSG_ReadFloat();\n\t\tcmd->vr[i].velocity[1] = MSG_ReadFloat();\n\t\tcmd->vr[i].velocity[2] = MSG_ReadFloat();\n\t}\n\tif (bits & UC_VR_WEAPON)\n\t\tcmd->vr[i].weapon = MSG_ReadUInt64();\n}\nvoid MSGFTE_ReadDeltaUsercmd (const usercmd_t *from, usercmd_t *cmd)\n{\n\tint i;\n\tunsigned int bits;\n\n\tbits = MSG_ReadUInt64();\n\n\tif (bits & UC_UNSUPPORTED)\n\t{\n\t\tif (!msg_badread)\n\t\t\tCon_Printf(\"MSG_ReadDeltaUsercmdNew: Unsupported bits (%#x)\\n\", bits&UC_UNSUPPORTED);\n\t\tmsg_badread = true;\n\t\treturn;\n\t}\n\t*cmd = *from;\n\tcmd->servertime = from->servertime+MSG_ReadUInt64();\n\tcmd->fservertime = cmd->servertime/1000.0;\n\tif (bits & UC_MSEC_DEBUG)\n\t\tcmd->msec = MSG_ReadUInt64();\t//for debugging only. only sent when developer 1, for now.\n\telse\n\t\tcmd->msec = 0;\t//no info...\n\tfor (i = 0; i < 3; i++)\n\t{\n\t\tif (bits & (UC_ANGLE1<<i))\n\t\t{\n\t\t\tif (bits & UC_ABSANG)\n\t\t\t\tcmd->angles[i] = MSG_ReadShort();\n\t\t\telse\n\t\t\t\tcmd->angles[i] = from->angles[i]+MSG_ReadChar();\n\t\t}\n\t}\n\tif (bits & UC_FORWARD)\n\t{\n\t\tif (bits & UC_BIGMOVES)\n\t\t\tcmd->forwardmove = MSG_ReadInt64();\n\t\telse\n\t\t\tcmd->forwardmove = MSG_ReadChar()*5;\n\t}\n\tif (bits & UC_RIGHT)\n\t{\n\t\tif (bits & UC_BIGMOVES)\n\t\t\tcmd->sidemove = MSG_ReadInt64();\n\t\telse\n\t\t\tcmd->sidemove = MSG_ReadChar()*5;\n\t}\n\tif (bits & UC_UP)\n\t{\n\t\tif (bits & UC_BIGMOVES)\n\t\t\tcmd->upmove = MSG_ReadInt64();\n\t\telse\n\t\t\tcmd->upmove = MSG_ReadChar()*5;\n\t}\n\n\tif (bits & UC_BUTTONS)\n\t\tcmd->buttons = MSG_ReadUInt64();\n\tif (bits & UC_IMPULSE)\n\t\tcmd->impulse = MSG_ReadUInt64();\n\tif (bits & UC_WEAPON)\n\t\tcmd->weapon = MSG_ReadUInt64();\n\tif (bits & UC_CURSORFLDS)\n\t{\t//prydon cursor crap. kinda bloated.\n\t\tcmd->cursor_screen[0] = MSG_ReadShort() / 32767.0;\n\t\tcmd->cursor_screen[1] = MSG_ReadShort() / 32767.0;\n\t\tcmd->cursor_start[0] = MSG_ReadFloat();\t//avoiding WriteAngle/WriteCoord means we can avoid netprim size difference issues.\n\t\tcmd->cursor_start[1] = MSG_ReadFloat();\n\t\tcmd->cursor_start[2] = MSG_ReadFloat();\n\t\tcmd->cursor_impact[0] = MSG_ReadFloat();\n\t\tcmd->cursor_impact[1] = MSG_ReadFloat();\n\t\tcmd->cursor_impact[2] = MSG_ReadFloat();\n\t\tcmd->cursor_entitynumber = MSG_ReadBigEntity();\n\t}\n\tif (bits & UC_LIGHTLEV)\n\t\tcmd->lightlevel = MSG_ReadUInt64();\n\tif (bits & UC_VR_HEAD)\n\t\tMSG_ReadVR(VRDEV_HEAD, cmd);\n\tif (bits & UC_VR_RIGHT)\n\t\tMSG_ReadVR(VRDEV_RIGHT, cmd);\n\tif (bits & UC_VR_LEFT)\n\t\tMSG_ReadVR(VRDEV_LEFT, cmd);\n}\n#endif\nvoid MSGQW_WriteDeltaUsercmd (sizebuf_t *buf, const usercmd_t *from, const usercmd_t *cmd)\n{\n\tint\t\tbits;\n\n//\n// send the movement message\n//\n\tbits = 0;\n#if defined(Q2CLIENT) && defined(HAVE_CLIENT)\n\tif (cls_state && cls.protocol == CP_QUAKE2)\n\t\tMSGQ2_WriteDeltaUsercmd(buf, from, cmd);\n\telse\n#endif\n\t{\n\t\tif (cmd->angles[0] != from->angles[0])\n\t\t\tbits |= CM_ANGLE1;\n\t\tif (cmd->angles[1] != from->angles[1])\n\t\t\tbits |= CM_ANGLE2;\n\t\tif (cmd->angles[2] != from->angles[2])\n\t\t\tbits |= CM_ANGLE3;\n\t\tif (cmd->forwardmove != from->forwardmove)\n\t\t\tbits |= CM_FORWARD;\n\t\tif (cmd->sidemove != from->sidemove)\n\t\t\tbits |= CM_SIDE;\n\t\tif (cmd->upmove != from->upmove)\n\t\t\tbits |= CM_UP;\n\t\tif (cmd->buttons != from->buttons)\n\t\t\tbits |= CM_BUTTONS;\n\t\tif (cmd->impulse != from->impulse)\n\t\t\tbits |= CM_IMPULSE;\n\n\t\tMSG_WriteByte (buf, bits);\n\n\t\tif (bits & CM_ANGLE1)\n\t\t\tMSG_WriteShort (buf, cmd->angles[0]);\n\t\tif (bits & CM_ANGLE2)\n\t\t\tMSG_WriteShort (buf, cmd->angles[1]);\n\t\tif (bits & CM_ANGLE3)\n\t\t\tMSG_WriteShort (buf, cmd->angles[2]);\n\n\t\tif (bits & CM_FORWARD)\n\t\t\tMSG_WriteShort (buf, cmd->forwardmove);\n\t\tif (bits & CM_SIDE)\n\t\t\tMSG_WriteShort (buf, cmd->sidemove);\n\t\tif (bits & CM_UP)\n\t\t\tMSG_WriteShort (buf, cmd->upmove);\n\n\t\tif (bits & CM_BUTTONS)\n\t\t\tMSG_WriteByte (buf, cmd->buttons);\n\t\tif (bits & CM_IMPULSE)\n\t\t\tMSG_WriteByte (buf, cmd->impulse);\n\t\tMSG_WriteByte (buf, bound(0, cmd->msec, 255));\n\t}\n}\n\n#ifdef HAVE_CLIENT\nvoid MSGCL_WriteDeltaUsercmd (sizebuf_t *buf, const usercmd_t *from, const usercmd_t *cmd)\n{\n#if defined(Q2CLIENT)\n\tif (cls_state && cls.protocol == CP_QUAKE2)\n\t{\n\t\tif (cls.protocol_q2 == PROTOCOL_VERSION_Q2EX)\n\t\t\tMSGQ2EX_WriteDeltaUsercmd(buf, from, cmd);\n\t\telse\n\t\t\tMSGQ2_WriteDeltaUsercmd(buf, from, cmd);\n\t}\n\telse\n#endif\n\t\tMSGQW_WriteDeltaUsercmd(buf, from, cmd);\n}\n#endif\n\n//\n// reading functions\n//\nqboolean\tmsg_badread;\nstruct netprim_s msg_nullnetprim;\nsizebuf_t\t*msg_readmsg;\n\nvoid MSG_BeginReading (sizebuf_t *sb, struct netprim_s prim)\n{\n\tmsg_readmsg = sb;\n\tmsg_badread = false;\n\tsb->currentbit = 0;\n\tsb->packing = SZ_RAWBYTES;\n\tsb->prim = prim;\n}\n\nvoid MSG_ChangePrimitives(struct netprim_s prim)\n{\n\tnet_message.prim = prim;\n}\n\nint MSG_GetReadCount(void)\n{\n\treturn msg_readmsg->currentbit>>3;\n}\n\n\n/*\n============\nMSG_ReadRawBytes\n============\n*/\nstatic int MSG_ReadRawBytes(sizebuf_t *msg, int bits)\n{\n\tint bitmask = 0;\n\tunsigned int readcount = msg->currentbit>>3;\n\n\tif (readcount + (bits>>3) >= msg->cursize)\n\t{\n\t\tmsg_badread = true;\n\t\tmsg->currentbit += bits;\n\t\treturn -1;\n\t}\n\n\tif (bits <= 8)\n\t{\n\t\tbitmask = (unsigned char)msg->data[readcount];\n\t\tmsg->currentbit += 8;\n\t}\n\telse if (bits <= 16)\n\t{\n\t\tbitmask = (unsigned short)(msg->data[readcount]\n\t\t\t+ (msg->data[readcount+1] << 8));\n\t\tmsg->currentbit += 16;\n\t}\n\telse if (bits <= 32)\n\t{\n\t\tbitmask = msg->data[readcount]\n\t\t\t+ (msg->data[readcount+1] << 8)\n\t\t\t+ (msg->data[readcount+2] << 16)\n\t\t\t+ (msg->data[readcount+3] << 24);\n\t\tmsg->currentbit += 32;\n\t}\n\n\treturn bitmask;\n}\n\n/*\n============\nMSG_ReadRawBits\n============\n*/\nstatic int MSG_ReadRawBits(sizebuf_t *msg, int bits)\n {\n\tint i;\n\tint val;\n\tint bitmask = 0;\n\n\tif (msg->currentbit + bits > (msg->cursize<<3))\n\t{\n\t\tmsg_badread = true;\n\t\tmsg->currentbit = msg->cursize<<3;\n\t\treturn -1;\n\t}\n\n\tfor(i=0 ; i<bits ; i++)\n\t{\n\t\tval = msg->data[msg->currentbit >> 3] >> (msg->currentbit & 7);\n\t\tmsg->currentbit++;\n\t\tbitmask |= (val & 1) << i;\n\t}\n\n\treturn bitmask;\n}\n\n#ifdef HUFFNETWORK\n/*\n============\nMSG_ReadHuffBits\n============\n*/\nstatic int MSG_ReadHuffBits(sizebuf_t *msg, int bits)\n{\n\tint i;\n\tint val;\n\tint bitmask;\n\tint remaining = bits & 7;\n\n\tbitmask = MSG_ReadRawBits(msg, remaining);\n\n\tfor (i=0 ; i<bits-remaining ; i+=8)\n\t{\n\t\tval = Huff_GetByte(msg->data, &msg->currentbit);\n\t\tbitmask |= val << (i + remaining);\n\t}\n\n\tif (msg->currentbit > (msg->cursize<<3))\n\t{\n\t\tmsg_badread = true;\n\t\tmsg->currentbit = msg->cursize<<3;\n\t\treturn -1;\n\t}\n\n\treturn bitmask;\n}\n#endif\n\nint MSG_ReadBits(int bits)\n{\n\tint bitmask = 0;\n\tqboolean extend = false;\n\n#ifdef PARANOID\n\tif (!bits || bits < -31 || bits > 32)\n\t\tHost_EndGame(\"MSG_ReadBits: bad bits %i\", bits );\n#endif\n\n\tif (bits < 0)\n\t{\n\t\tbits = -bits;\n\t\textend = true;\n\t}\n\n\tswitch(msg_readmsg->packing)\n\t{\n\tdefault:\n\tcase SZ_BAD:\n\t\tSys_Error(\"MSG_ReadBits: bad msg_readmsg->packing\");\n\t\tbreak;\n\tcase SZ_RAWBYTES:\n\t\tbitmask = MSG_ReadRawBytes(msg_readmsg, bits);\n\t\tbreak;\n\tcase SZ_RAWBITS:\n\t\tbitmask = MSG_ReadRawBits(msg_readmsg, bits);\n\t\tbreak;\n#ifdef HUFFNETWORK\n\tcase SZ_HUFFMAN:\n\t\tbitmask = MSG_ReadHuffBits(msg_readmsg, bits);\n\t\tbreak;\n#endif\n\t}\n\n\tif (extend)\n\t{\n\t\tif(bitmask & (1 << (bits - 1)))\n\t\t{\n\t\t\tbitmask |= ~((1 << bits) - 1);\n\t\t}\n\t}\n\n\treturn bitmask;\n}\n\nvoid MSG_ReadSkip(int bytes)\n{\n\tif (msg_readmsg->packing!=SZ_RAWBYTES)\n\t{\n\t\twhile (bytes > 4)\n\t\t{\n\t\t\tMSG_ReadBits(32);\n\t\t\tbytes-=4;\n\t\t}\n\t\twhile (bytes > 0)\n\t\t{\n\t\t\tMSG_ReadBits(8);\n\t\t\tbytes--;\n\t\t}\n\t}\n\tmsg_readmsg->currentbit += bytes<<3;\n\tif (msg_readmsg->currentbit < 0)\n\t{\n\t\tmsg_readmsg->currentbit = 0;\n\t\tmsg_badread = true;\n\t\treturn;\n\t}\n\tif (msg_readmsg->currentbit > msg_readmsg->cursize<<3)\n\t{\n\t\tmsg_readmsg->currentbit = msg_readmsg->cursize<<3;\n\t\tmsg_badread = true;\n\t\treturn;\n\t}\n}\n\n\n// returns -1 and sets msg_badread if no more characters are available\nint MSG_ReadChar (void)\n{\n\tint\tc;\n\tunsigned int msg_readcount;\n\n\tif (msg_readmsg->packing!=SZ_RAWBYTES)\n\t\treturn MSG_ReadBits(-8);\n\n\tmsg_readcount = msg_readmsg->currentbit>>3;\n\tif (msg_readcount+1 > msg_readmsg->cursize)\n\t{\n\t\tmsg_badread = true;\n\t\treturn -1;\n\t}\n\n\tc = (signed char)msg_readmsg->data[msg_readcount];\n\tmsg_readcount++;\n\tmsg_readmsg->currentbit = msg_readcount<<3;\n\n\treturn c;\n}\n\nint MSG_PeekByte(void)\n{\n\tunsigned int msg_readcount;\n\n\tif (msg_readmsg->packing!=SZ_RAWBYTES)\n\t\treturn -1;\n\n\tmsg_readcount = msg_readmsg->currentbit>>3;\n\tif (msg_readcount+1 > msg_readmsg->cursize)\n\t\treturn -1;\n\n\treturn (unsigned char)msg_readmsg->data[msg_readcount];\n}\n\nint MSG_ReadByte (void)\n{\n\tunsigned char\tc;\n\tunsigned int msg_readcount;\n\n\tif (msg_readmsg->packing!=SZ_RAWBYTES)\n\t\treturn MSG_ReadBits(8);\n\n\tmsg_readcount = msg_readmsg->currentbit>>3;\n\tif (msg_readcount+1 > msg_readmsg->cursize)\n\t{\n\t\tmsg_badread = true;\n\t\treturn -1;\n\t}\n\n\tc = (unsigned char)msg_readmsg->data[msg_readcount];\n\tmsg_readcount++;\n\tmsg_readmsg->currentbit = msg_readcount<<3;\n\n\treturn c;\n}\n\nint MSG_ReadShort (void)\n{\n\tint\tc;\n\tunsigned int msg_readcount;\n\n\tif (msg_readmsg->packing!=SZ_RAWBYTES)\n\t\treturn (short)MSG_ReadBits(16);\n\n\tmsg_readcount = msg_readmsg->currentbit>>3;\n\tif (msg_readcount+2 > msg_readmsg->cursize)\n\t{\n\t\tmsg_badread = true;\n\t\treturn -1;\n\t}\n\n\tc = (short)(msg_readmsg->data[msg_readcount]\n\t+ (msg_readmsg->data[msg_readcount+1]<<8));\n\n\tmsg_readcount += 2;\n\tmsg_readmsg->currentbit = msg_readcount<<3;\n\n\treturn c;\n}\n\nint MSG_ReadUInt16 (void)\n{\n\tint\tc;\n\tunsigned int msg_readcount;\n\n\tif (msg_readmsg->packing!=SZ_RAWBYTES)\n\t\treturn (short)MSG_ReadBits(16);\n\n\tmsg_readcount = msg_readmsg->currentbit>>3;\n\tif (msg_readcount+2 > msg_readmsg->cursize)\n\t{\n\t\tmsg_badread = true;\n\t\treturn -1;\n\t}\n\n\tc = (unsigned short)(msg_readmsg->data[msg_readcount]\n\t+ (msg_readmsg->data[msg_readcount+1]<<8));\n\n\tmsg_readcount += 2;\n\tmsg_readmsg->currentbit = msg_readcount<<3;\n\n\treturn c;\n}\n\nint MSG_ReadLong (void)\n{\n\tint\tc;\n\tunsigned int msg_readcount;\n\n\tif (msg_readmsg->packing!=SZ_RAWBYTES)\n\t\treturn (int)MSG_ReadBits(32);\n\n\tmsg_readcount = msg_readmsg->currentbit>>3;\n\tif (msg_readcount+4 > msg_readmsg->cursize)\n\t{\n\t\tmsg_badread = true;\n\t\treturn -1;\n\t}\n\n\tc = msg_readmsg->data[msg_readcount]\n\t+ (msg_readmsg->data[msg_readcount+1]<<8)\n\t+ (msg_readmsg->data[msg_readcount+2]<<16)\n\t+ (msg_readmsg->data[msg_readcount+3]<<24);\n\n\tmsg_readcount += 4;\n\tmsg_readmsg->currentbit = msg_readcount<<3;\n\n\treturn c;\n}\nquint64_t MSG_ReadULEB128 (void)\n{\n\tquint64_t r = 0;\n\tqbyte b, o=0;\n\twhile (!msg_badread)\n\t{\n\t\tb = MSG_ReadByte();\n\t\tr |= (b&0x7f)<<o;\n\t\to+=7;\n\t\tif (!(b & 0x80))\n\t\t\tbreak;\n\t}\n\treturn r;\n}\nqint64_t MSG_ReadSignedQEX (void)\n{\t//this is not signed leb128 (which would normally just sign-extend)\n\tquint64_t c = MSG_ReadULEB128();\n\tif (c&1)\n\t\treturn -1-(qint64_t)(c>>1);\n\telse\n\t\treturn (qint64_t)(c>>1);\n}\nquint64_t MSG_ReadUInt64 (void)\n{\t//0* 10*,*, 110*,*,* etc, up to 0xff followed by 8 continuation bytes\n\tqbyte l=0x80, v, b = 0;\n\tquint64_t r;\n\tv = MSG_ReadByte();\n\tfor (; v&l; l>>=1)\n\t{\n\t\tv-=l;\n\t\tb++;\n\t}\n\tr = (quint64_t)v<<(b*8);\n\twhile(b --> 0)\n\t\tr |= (quint64_t)MSG_ReadByte()<<(b*8);\n\treturn r;\n}\nqint64_t MSG_ReadInt64 (void)\n{\t//we do some fancy bit recoding for more efficient length coding.\n\tquint64_t c = MSG_ReadUInt64();\n\tif (c&1)\n\t\treturn -1-(qint64_t)(c>>1);\n\telse\n\t\treturn (qint64_t)(c>>1);\n}\n\nfloat MSG_ReadFloat (void)\n{\n\tunion\n\t{\n\t\tqbyte\tb[4];\n\t\tfloat\tf;\n\t\tint\tl;\n\t} dat;\n\tunsigned int msg_readcount;\n\n\tif (msg_readmsg->packing!=SZ_RAWBYTES)\n\t{\n\t\tdat.l = MSG_ReadBits(32);\n\t\treturn dat.f;\n\t}\n\n\tmsg_readcount = msg_readmsg->currentbit>>3;\n\tif (msg_readcount+4 > msg_readmsg->cursize)\n\t{\n\t\tmsg_badread = true;\n\t\treturn -1;\n\t}\n\n\tdat.b[0] =\tmsg_readmsg->data[msg_readcount];\n\tdat.b[1] =\tmsg_readmsg->data[msg_readcount+1];\n\tdat.b[2] =\tmsg_readmsg->data[msg_readcount+2];\n\tdat.b[3] =\tmsg_readmsg->data[msg_readcount+3];\n\tmsg_readcount += 4;\n\tmsg_readmsg->currentbit = msg_readcount<<3;\n\n\tif (bigendian)\n\t\tdat.l = LittleLong (dat.l);\n\n\treturn dat.f;\n}\ndouble MSG_ReadDouble (void)\n{\n\tunion\n\t{\n\t\tquint64_t l;\n\t\tdouble\tf;\n\t} dat;\n\tunsigned int msg_readcount = msg_readmsg->currentbit>>3;\n\n\tif (msg_readcount+8 > net_message.cursize)\n\t{\n\t\tmsg_badread = true;\n\t\treturn -1;\n\t}\n\n\tdat.l = (           net_message.data[msg_readcount+0]<< 0)|\n\t\t\t(           net_message.data[msg_readcount+1]<< 8)|\n\t\t\t(           net_message.data[msg_readcount+2]<<16)|\n\t\t\t(           net_message.data[msg_readcount+3]<<24)|\n\t\t\t((quint64_t)net_message.data[msg_readcount+4]<<32)|\n\t\t\t((quint64_t)net_message.data[msg_readcount+5]<<40)|\n\t\t\t((quint64_t)net_message.data[msg_readcount+6]<<48)|\n\t\t\t((quint64_t)net_message.data[msg_readcount+7]<<56);\n\tmsg_readcount += 8;\n\n\treturn dat.f;\n}\n\nchar *MSG_ReadStringBuffer (char *out, size_t outsize)\n{\n\tint\t\tl,c;\n\n\tl = 0;\n\tdo\n\t{\n\t\tc = MSG_ReadChar ();\n\t\tif (msg_badread || c == 0)\n\t\t\tbreak;\n\t\tout[l] = c;\n\t\tl++;\n\t} while (l < outsize-1);\n\n\tout[l] = 0;\n\n\treturn out;\n}\nchar *MSG_ReadString (void)\n{\n\tstatic char\tstring[65536];\n\tint\t\tl,c;\n\n\tl = 0;\n\tfor(;;)\n\t{\n\t\tc = MSG_ReadChar ();\n\t\tif (msg_badread || c == 0)\n\t\t\tbreak;\n\t\tif (l < sizeof(string)-1)\n\t\t\tstring[l++] = c;\n\t\telse\n\t\t\tmsg_badread = true;\n\t}\n\n\tstring[l] = 0;\n\n\treturn string;\n}\n\nchar *MSG_ReadStringLine (void)\n{\n\tstatic char\tstring[2048];\n\tint\t\tl,c;\n\n\tl = 0;\n\tdo\n\t{\n\t\tc = MSG_ReadChar ();\n\t\tif (msg_badread || c == 0 || c == '\\n')\n\t\t\tbreak;\n\t\tstring[l] = c;\n\t\tl++;\n\t} while (l < sizeof(string)-1);\n\n\tstring[l] = 0;\n\n\treturn string;\n}\n\nfloat MSG_ReadCoord (void)\n{\n\tcoorddata c = {{0}};\n\tunsigned char coordtype = msg_readmsg->prim.coordtype;\n\tif (coordtype == COORDTYPE_UNDEFINED)\n\t{\n\t\tstatic float throttle;\n\t\tCon_ThrottlePrintf(&throttle, 0, CON_WARNING\"MSG_ReadCoord: primitives not yet configured. assuming 13.3\\n\");\n\t\tcoordtype = COORDTYPE_FIXED_13_3;\n\t}\n\tif ((coordtype&COORDTYPE_SIZE_MASK)>sizeof(c))\n\t\treturn 0;\n\tMSG_ReadData(c.b, coordtype&COORDTYPE_SIZE_MASK);\n\treturn MSG_FromCoord(c, coordtype);\n}\nfloat MSG_ReadCoordFloat (void)\n{\n\tcoorddata c = {{0}};\n\tMSG_ReadData(c.b, COORDTYPE_FLOAT_32&COORDTYPE_SIZE_MASK);\n\treturn MSG_FromCoord(c, COORDTYPE_FLOAT_32);\n}\n\nvoid MSG_ReadPos (float *pos)\n{\n\tpos[0] = MSG_ReadCoord();\n\tpos[1] = MSG_ReadCoord();\n\tpos[2] = MSG_ReadCoord();\n}\n\n#if 1//defined(Q2SERVER) || !defined(SERVERONLY)\n#define Q2NUMVERTEXNORMALS\t162\nvec3_t\tbytedirs[Q2NUMVERTEXNORMALS] =\n{\n#include \"../client/q2anorms.h\"\n};\n#endif\n#ifdef HAVE_CLIENT\nvoid MSG_ReadDir (float *dir)\n{\n\tint\t\tb;\n\n\tb = MSG_ReadByte ();\n\tif (b >= Q2NUMVERTEXNORMALS)\n\t{\n\t\tCL_DumpPacket();\n\t\tHost_EndGame (\"MSG_ReadDir: out of range\");\n\t}\n\tVectorCopy (bytedirs[b], dir);\n}\n#endif\n#if 1//def Q2SERVER\nvoid MSG_WriteDir (sizebuf_t *sb, float dir[3])\n{\n\tint\t\ti, best;\n\tfloat\td, bestd;\n\n\tif (!dir)\n\t{\n\t\tMSG_WriteByte (sb, 0);\n\t\treturn;\n\t}\n\n\tbestd = 0;\n\tbest = 0;\n\tfor (i=0 ; i<Q2NUMVERTEXNORMALS ; i++)\n\t{\n\t\td = DotProduct (dir, bytedirs[i]);\n\t\tif (d > bestd)\n\t\t{\n\t\t\tbestd = d;\n\t\t\tbest = i;\n\t\t}\n\t}\n\tMSG_WriteByte (sb, best);\n}\n#endif\n\nfloat MSG_ReadAngle16 (void)\n{\n\treturn MSG_ReadShort() * (360.0/65536);\n}\nfloat MSG_ReadAngle (void)\n{\n\tint sz = msg_readmsg->prim.anglesize;\n\tif (!sz)\n\t{\n\t\tstatic float throttle;\n\t\tCon_ThrottlePrintf(&throttle, 0, CON_WARNING\"MSG_ReadAngle: primitives not yet configured. assuming 8 bit\\n\");\n\t\tsz = 1;\n\t}\n\tswitch(sz)\n\t{\n\tcase 2:\n\t\treturn MSG_ReadAngle16();\n\tcase 4:\n\t\treturn MSG_ReadFloat();\n\tcase 1:\n\t\treturn MSG_ReadChar() * (360.0/256);\n\tdefault:\n\t\tSys_Error(\"Bad angle size\\n\");\n\t\treturn 0;\n\t}\n}\n\nvoid MSGQW_ReadDeltaUsercmd (const usercmd_t *from, usercmd_t *move, int protover)\n{\n\tint bits;\n\n\tmemcpy (move, from, sizeof(*move));\n\n\tbits = MSG_ReadByte ();\n\n\tif (protover <= 26)\n\t{\n\t\tif (bits & CM_ANGLE1)\n\t\t\tmove->angles[0] = MSG_ReadShort();\n\t\tif (1)\n\t\t\tmove->angles[1] = MSG_ReadShort();\n\t\tif (bits & CM_ANGLE3)\n\t\t\tmove->angles[2] = MSG_ReadShort();\n\n\t\tif (bits & CM_FORWARD)\n\t\t\tmove->forwardmove = MSG_ReadByte()<<3;\n\t\tif (bits & CM_SIDE)\n\t\t\tmove->sidemove = MSG_ReadByte()<<3;\n\t\tif (bits & CM_UP)\n\t\t\tmove->upmove = MSG_ReadByte()<<3;\n\n\t\t// read buttons\n\t\tif (bits & CM_BUTTONS)\n\t\t\tmove->buttons = MSG_ReadByte();\n\n\t\tif (bits & CM_IMPULSE)\n\t\t\tmove->impulse = MSG_ReadByte();\n\n// read time to run command\n\t\tif (bits & CM_ANGLE2)\n\t\t\tmove->msec = MSG_ReadByte();\n\t}\n\telse\n\t{\n// read current angles\n\t\tif (bits & CM_ANGLE1)\n\t\t\tmove->angles[0] = MSG_ReadShort();\n\t\tif (bits & CM_ANGLE2)\n\t\t\tmove->angles[1] = MSG_ReadShort();\n\t\tif (bits & CM_ANGLE3)\n\t\t\tmove->angles[2] = MSG_ReadShort();\n\n// read movement\n\t\tif (bits & CM_FORWARD)\n\t\t\tmove->forwardmove = MSG_ReadShort();\n\t\tif (bits & CM_SIDE)\n\t\t\tmove->sidemove = MSG_ReadShort();\n\t\tif (bits & CM_UP)\n\t\t\tmove->upmove = MSG_ReadShort();\n\n// read buttons\n\t\tif (bits & CM_BUTTONS)\n\t\t\tmove->buttons = MSG_ReadByte();\n\n\t\tif (bits & CM_IMPULSE)\n\t\t\tmove->impulse = MSG_ReadByte();\n\n// read time to run command\n\t\tmove->msec = MSG_ReadByte();\n\t}\n}\n\n#ifdef HAVE_SERVER\nvoid MSGQ2_ReadDeltaUsercmd (client_t *cl, const usercmd_t *from, usercmd_t *move)\n{\n\tint bits;\n\tunsigned int buttons = 0;\n\n\tmemcpy (move, from, sizeof(*move));\n\n\tbits = MSG_ReadByte ();\n\n\tif (msg_readmsg->prim.flags & NPQ2_R1Q2_UCMD)\n\t\tbuttons = MSG_ReadByte();\n\n\tif (cl->protocol == SCP_QUAKE2EX)\n\t{\n\t\tif (bits & Q2CM_ANGLE1)\n\t\t\tmove->angles[0] = ANGLE2SHORT(MSG_ReadFloat());\n\t\tif (bits & Q2CM_ANGLE2)\n\t\t\tmove->angles[1] = ANGLE2SHORT(MSG_ReadFloat());\n\t\tif (bits & Q2CM_ANGLE3)\n\t\t\tmove->angles[2] = ANGLE2SHORT(MSG_ReadFloat());\n\n\t\t// read movement\n\t\tif (bits & Q2CM_FORWARD)\n\t\t\tmove->forwardmove = MSG_ReadFloat ();\n\t\tif (bits & Q2CM_SIDE)\n\t\t\tmove->sidemove = MSG_ReadFloat();\n\t\tif (bits & Q2CM_UP)\n\t\t\tCon_DPrintf(\"Q2CM_UP unexpected\\n\");\n\n\t\tif (bits & Q2CM_BUTTONS)\n\t\t\tmove->buttons = MSG_ReadByte ();\n\n\t\tif (bits & Q2CM_IMPULSE/*repurposed*/)\n\t\t\tmove->sequence = MSG_ReadLong();\n\n\t\tif (move->buttons & (1<<3))\n\t\t\tmove->upmove = 200;\n\t\telse if (move->buttons & (1<<4))\n\t\t\tmove->upmove = -200;\n\t}\n\telse\n\t{\n\t\t// read current angles\n\t\tif (bits & Q2CM_ANGLE1)\n\t\t{\n\t\t\tif (buttons & R1Q2_BUTTON_BYTE_ANGLE1)\n\t\t\t\tmove->angles[0] = MSG_ReadChar ()*64;\n\t\t\telse\n\t\t\t\tmove->angles[0] = MSG_ReadShort ();\n\t\t}\n\t\tif (bits & Q2CM_ANGLE2)\n\t\t{\n\t\t\tif (buttons & R1Q2_BUTTON_BYTE_ANGLE2)\n\t\t\t\tmove->angles[1] = MSG_ReadChar ()*256;\n\t\t\telse\n\t\t\t\tmove->angles[1] = MSG_ReadShort ();\n\t\t}\n\t\tif (bits & Q2CM_ANGLE3)\n\t\t\tmove->angles[2] = MSG_ReadShort ();\n\n\t\t// read movement\n\t\tif (bits & Q2CM_FORWARD)\n\t\t{\n\t\t\tif (buttons & R1Q2_BUTTON_BYTE_FORWARD)\n\t\t\t\tmove->forwardmove = MSG_ReadChar ()*5;\n\t\t\telse\n\t\t\t\tmove->forwardmove = MSG_ReadShort ();\n\t\t}\n\t\tif (bits & Q2CM_SIDE)\n\t\t{\n\t\t\tif (buttons & R1Q2_BUTTON_BYTE_SIDE)\n\t\t\t\tmove->sidemove = MSG_ReadChar ()*5;\n\t\t\telse\n\t\t\t\tmove->sidemove = MSG_ReadShort ();\n\t\t}\n\t\tif (bits & Q2CM_UP)\n\t\t{\n\t\t\tif (buttons & R1Q2_BUTTON_BYTE_UP)\n\t\t\t\tmove->upmove = MSG_ReadChar ()*5;\n\t\t\telse\n\t\t\t\tmove->upmove = MSG_ReadShort ();\n\t\t}\n\n\t\t// read buttons\n\t\tif (bits & Q2CM_BUTTONS)\n\t\t{\n\t\t\tif (msg_readmsg->prim.flags & NPQ2_R1Q2_UCMD)\n\t\t\t\tmove->buttons = buttons & (1|2|128);\t//only use the bits that are actually buttons, so gamecode can't get excited despite being crippled by this.\n\t\t\telse\n\t\t\t\tmove->buttons = MSG_ReadByte ();\n\t\t}\n\n\t\tif (bits & Q2CM_IMPULSE)\n\t\t\tmove->impulse = MSG_ReadByte ();\n\t}\n\n\t// read time to run command\n\tmove->msec = MSG_ReadByte ();\n\n\tif (cl->protocol == SCP_QUAKE2EX)\n\t\tmove->lightlevel = 255; //light level removed.\n\telse\n\t\tmove->lightlevel = MSG_ReadByte ();\n}\n#endif\n\nvoid MSG_ReadData (void *data, int len)\n{\n\tint\t\ti;\n\n\tfor (i=0 ; i<len ; i++)\n\t\t((qbyte *)data)[i] = MSG_ReadByte ();\n}\n\n\n//===========================================================================\n\nvoid SZ_Clear (sizebuf_t *buf)\n{\n\tbuf->cursize = 0;\n\tbuf->overflowed = false;\n}\n\nvoid *SZ_GetSpace (sizebuf_t *buf, int length)\n{\n\tvoid\t*data;\n\n\tif (buf->cursize + length > buf->maxsize)\n\t{\n\t\tif (!buf->allowoverflow)\n\t\t\tSys_Error (\"SZ_GetSpace: overflow without allowoverflow set (%d)\", buf->maxsize);\n\n\t\tSys_Printf (\"SZ_GetSpace: overflow (%i+%i bytes of %i)\\n\", buf->cursize, length, buf->maxsize);\t// because Con_Printf may be redirected\n\t\tSZ_Clear (buf);\n\t\tbuf->overflowed = true;\n\t}\n\n\tdata = buf->data + buf->cursize;\n\tbuf->cursize += length;\n\n\treturn data;\n}\n\nvoid SZ_Write (sizebuf_t *buf, const void *data, int length)\n{\n\tQ_memcpy (SZ_GetSpace(buf,length),data,length);\n}\n\nvoid SZ_Print (sizebuf_t *buf, const char *data)\n{\n\tint\t\tlen;\n\n\tlen = Q_strlen(data)+1;\n\n\tif (!buf->cursize || buf->data[buf->cursize-1])\n\t\tQ_memcpy ((qbyte *)SZ_GetSpace(buf, len),data,len); // no trailing 0\n\telse\n\t{\n\t\tqbyte *msg;\n\t\tmsg = (qbyte*)SZ_GetSpace(buf, len-1);\n\t\tif (msg == buf->data)\t//whoops. SZ_GetSpace can return buf->data if it overflowed.\n\t\t\tmsg++;\n\t\tQ_memcpy (msg-1,data,len); // write over trailing 0\n\t}\n}\n\n\n//============================================================================\n\nqboolean COM_TrimString(char *str, char *buffer, int buffersize)\n{\n\tint i;\n\tif (buffersize <= 0)\n\t{\n\t\tSys_Error(\"COM_TrimString: no buffer\\n\");\n\t\treturn false;\n\t}\n\n\twhile (*str <= ' ' && *str>'\\0')\n\t\tstr++;\n\n\tfor (i = 0; ; i++)\n\t{\n\t\tif (i == buffersize-1)\n\t\t{\n\t\t\tbuffer[i] = '\\0';\n\t\t\treturn false;\n\t\t}\n\t\tif (*str <= ' ')\n\t\t\tbreak;\n\t\tbuffer[i] = *str++;\n\t}\n\tbuffer[i] = '\\0';\n\treturn true;\n}\n\n/*\n============\nCOM_SkipPath\n============\n*/\nchar *COM_SkipPath (const char *pathname)\n{\n\tconst char\t*last;\n\n\tlast = pathname;\n\twhile (*pathname)\n\t{\n\t\tif (*pathname=='/' || *pathname == '\\\\')\n\t\t\tlast = pathname+1;\n\t\tpathname++;\n\t}\n\treturn (char *)last;\n}\n\n/*\n============\nCOM_StripExtension\n============\n*/\nvoid QDECL COM_StripExtension (const char *in, char *out, int outlen)\n{\n\tchar *s;\n\n\tif (out != in)\t//optimisation, most calls use the same buffer\n\t\tQ_strncpyz(out, in, outlen);\n\n\ts = out+strlen(out);\n\n\twhile(*s != '/' && s != out)\n\t{\n\t\tif (*s == '.')\n\t\t{\n\t\t\t*s = 0;\n\n\t\t\t//some extensions don't really count, strip the next one too...\n\t\t\tif (!strcmp(s+1,\"gz\") || !strcmp(s+1,\"xz\"))\n\t\t\t\t;\n\t\t\telse\n\t\t\t\tbreak;\n\t\t}\n\n\t\ts--;\n\t}\n}\n\nvoid COM_StripAllExtensions (const char *in, char *out, int outlen)\n{\n\tchar *s;\n\n\tif (out != in)\n\t\tQ_strncpyz(out, in, outlen);\n\n\ts = out+strlen(out);\n\n\twhile(*s != '/' && s != out)\n\t{\n\t\tif (*s == '.')\n\t\t{\n\t\t\t*s = 0;\n\t\t}\n\n\t\ts--;\n\t}\n}\n\n/*\n============\nCOM_FileExtension\n============\n*/\nchar *COM_FileExtension (const char *in, char *result, size_t sizeofresult)\n{\n\tint\t\ti;\n\tconst char *dot;\n\n\tfor (dot = in + strlen(in); dot >= in && *dot != '.' && *dot != '/' && *dot != '\\\\'; dot--)\n\t\t;\n\tif (dot < in || *dot != '.')\n\t{\n\t\t*result = 0;\n\t\treturn result;\n\t}\n\tin = dot;\n\n\tin++;\n\tfor (i=0 ; i<sizeofresult-1 && *in ; i++,in++)\n\t\tresult[i] = *in;\n\tresult[i] = 0;\n\treturn result;\n}\n\n//returns a pointer to the extension text, including the dot\n//term is the end of the string (or null, to make things easy). if its a previous (non-empty) return value, then you can scan backwards to skip .gz or whatever extra postfixes.\nconst char *COM_GetFileExtension (const char *in, const char *term)\n{\n\tconst char *dot;\n\n\tif (!term)\n\t\tterm = in + strlen(in);\n\n\tfor (dot = term-1; dot >= in && *dot != '/' && *dot != '\\\\'; dot--)\n\t{\n\t\tif (*dot == '.')\n\t\t\treturn dot;\n\t}\n\treturn \"\";\n}\n\n//Quake 2's tank model has a borked skin (or two).\nvoid COM_CleanUpPath(char *str)\n{\n\tchar *dots;\n\tchar *slash;\n\tint criticize = 0;\n\tfor (dots = str; *dots; dots++)\n\t{\n\t\t/*if (*dots >= 'A' && *dots <= 'Z')\n\t\t{\n\t\t\t*dots = *dots - 'A' + 'a';\n\t\t\tcriticize = 1;\n\t\t}\n\t\telse */if (*dots == '\\\\')\n\t\t{\n\t\t\t*dots = '/';\n\t\t\tcriticize = 2;\n\t\t}\n\t}\n\twhile ((dots = strstr(str, \"..\")))\n\t{\n\t\tcriticize = 0;\n\t\tfor (slash = dots-1; slash >= str; slash--)\n\t\t{\n\t\t\tif (*slash == '/')\n\t\t\t{\n\t\t\t\tmemmove(slash, dots+2, strlen(dots+2)+1);\n\t\t\t\tcriticize = 3;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (criticize != 3)\n\t\t{\n\t\t\tmemmove(dots, dots+2, strlen(dots+2)+1);\n\t\t\tcriticize = 3;\n\t\t}\n\t}\n\twhile(*str == '/')\n\t{\n\t\tmemmove(str, str+1, strlen(str+1)+1);\n\t\tcriticize = 4;\n\t}\n/*\tif(criticize)\n\t{\n\t\tif (criticize == 1)\t//not a biggy, so not red.\n\t\t\tCon_Printf(\"Please fix file case on your files\\n\");\n\t\telse if (criticize == 2)\t//you're evil.\n\t\t\tCon_Printf(\"^1NEVER use backslash in a quake filename (we like portability)\\n\");\n\t\telse if (criticize == 3)\t//compleatly stupid. The main reason why this function exists. Quake2 does it!\n\t\t\tCon_Printf(\"You realise that relative paths are a waste of space?\\n\");\n\t\telse if (criticize == 4)\t//AAAAHHHHH! (consider sys_error instead)\n\t\t\tCon_Printf(\"^1AAAAAAAHHHH! An absolute path!\\n\");\n\t}\n*/\n}\n\n/*\n============\nCOM_FileBase\n============\n*/\nvoid COM_FileBase (const char *in, char *out, int outlen)\n{\n\tconst char *s, *s2;\n\n\ts = in + strlen(in) - 1;\n\n\twhile (s > in)\n\t{\n\t\tif ((*s == '.'&&strcmp(s+1,\"gz\")&&strcmp(s+1,\"xz\")) || *s == '/')\n\t\t\tbreak;\n\t\ts--;\n\t}\n\n\tfor (s2 = s ; s2 > in && *s2 && *s2 != '/' ; s2--)\n\t\t;\n\n\tif (s-s2 < 2)\n\t{\n\t\tif (s == s2)\n\t\t\tQ_strncpyz(out, in, outlen);\n\t\telse\n\t\t\tQ_strncpyz(out,\"?model?\", outlen);\n\t}\n\telse\n\t{\n\t\ts--;\n\t\toutlen--;\n\t\tif (outlen > s-s2)\n\t\t\toutlen = s-s2;\n\t\tQ_strncpyS (out,s2+1, outlen);\n\t\tout[outlen] = 0;\n\t}\n}\n\n\n/*\n==================\nCOM_DefaultExtension\n==================\n*/\nvoid COM_DefaultExtension (char *path, const char *extension, int maxlen)\n{\n\tchar    *src;\n//\n// if path doesn't have a .EXT, append extension\n// (extension should include the .)\n//\n\tsrc = path + strlen(path) - 1;\n\n\twhile (src > path && *src != '/')\n\t{\n\t\tif (*src == '.')\n\t\t\treturn;                 // it has an extension\n\t\tsrc--;\n\t}\n\n\tif (*extension != '.')\n\t\tQ_strncatz (path, \".\", maxlen);\n\tQ_strncatz (path, extension, maxlen);\n}\n\n//adds .ext only if it isn't already present (either case).\n//extension *must* contain a leading . as this is really a requiresuffix rather than an actual extension\n//returns false if truncated. will otherwise still succeed.\nqboolean COM_RequireExtension(char *path, const char *extension, int maxlen)\n{\n\tqboolean okay = true;\n\tint plen = strlen(path);\n\tint elen = strlen(extension);\n\n\t//check if its aready suffixed\n\tif (plen >= elen)\n\t{\n\t\tif (!Q_strcasecmp(path+plen-elen, extension))\n\t\t\treturn okay;\n\t}\n\n\t//truncate if required\n\tif (plen+1+elen > maxlen)\n\t{\n\t\tif (elen+1 > maxlen)\n\t\t\tSys_Error(\"extension longer than path buffer\");\n\t\tokay = false;\n\t\tplen = maxlen - 1+elen;\n\t}\n\n\t//do the copy\n\twhile(*extension)\n\t\tpath[plen++] = *extension++;\n\tpath[plen] = 0;\n\treturn okay;\n}\n\n//errors:\n//1 sequence error\n//2 over-long\n//3 invalid unicode char\n//4 invalid utf-16 lead/high surrogate\n//5 invalid utf-16 tail/low surrogate\nunsigned int utf8_decode(int *error, const void *in, char const**out)\n{\n\t//uc is the output unicode char\n\tunsigned int uc = 0xfffdu;\t//replacement character\n\t//l is the length\n\tunsigned int l = 1;\n\tconst unsigned char *str = in;\n\n\tif ((*str & 0xe0) == 0xc0)\n\t{\n\t\tif ((str[1] & 0xc0) == 0x80)\n\t\t{\n\t\t\tl = 2;\n\t\t\tuc = ((str[0] & 0x1f)<<6) | (str[1] & 0x3f);\n\t\t\tif (!uc || uc >= (1u<<7))\t//allow modified utf-8\n\t\t\t\t*error = 0;\n\t\t\telse\n\t\t\t\t*error = 2;\n\t\t}\n\t\telse *error = 1;\n\t}\n\telse if ((*str & 0xf0) == 0xe0)\n\t{\n\t\tif ((str[1] & 0xc0) == 0x80 && (str[2] & 0xc0) == 0x80)\n\t\t{\n\t\t\tl = 3;\n\t\t\tuc = ((str[0] & 0x0f)<<12) | ((str[1] & 0x3f)<<6) | ((str[2] & 0x3f)<<0);\n\t\t\tif (uc >= (1u<<11))\n\t\t\t\t*error = 0;\n\t\t\telse\n\t\t\t\t*error = 2;\n\t\t}\n\t\telse *error = 1;\n\t}\n\telse if ((*str & 0xf8) == 0xf0)\n\t{\n\t\tif ((str[1] & 0xc0) == 0x80 && (str[2] & 0xc0) == 0x80 && (str[3] & 0xc0) == 0x80)\n\t\t{\n\t\t\tl = 4;\n\t\t\tuc = ((str[0] & 0x07)<<18) | ((str[1] & 0x3f)<<12) | ((str[2] & 0x3f)<<6) | ((str[3] & 0x3f)<<0);\n\t\t\tif (uc >= (1u<<16))\n\t\t\t\t*error = 0;\n\t\t\telse\n\t\t\t\t*error = 2;\n\t\t}\n\t\telse *error = 1;\n\t}\n\telse if ((*str & 0xfc) == 0xf8)\n\t{\n\t\tif ((str[1] & 0xc0) == 0x80 && (str[2] & 0xc0) == 0x80 && (str[3] & 0xc0) == 0x80 && (str[4] & 0xc0) == 0x80)\n\t\t{\n\t\t\tl = 5;\n\t\t\tuc = ((str[0] & 0x03)<<24) | ((str[1] & 0x3f)<<18) | ((str[2] & 0x3f)<<12) | ((str[3] & 0x3f)<<6) | ((str[4] & 0x3f)<<0);\n\t\t\tif (uc >= (1u<<21))\n\t\t\t\t*error = 0;\n\t\t\telse\n\t\t\t\t*error = 2;\n\t\t}\n\t\telse *error = 1;\n\t}\n\telse if ((*str & 0xfe) == 0xfc)\n\t{\n\t\t//six bytes\n\t\tif ((str[1] & 0xc0) == 0x80 && (str[2] & 0xc0) == 0x80 && (str[3] & 0xc0) == 0x80 && (str[4] & 0xc0) == 0x80)\n\t\t{\n\t\t\tl = 6;\n\t\t\tuc = ((str[0] & 0x01)<<30) | ((str[1] & 0x3f)<<24) | ((str[2] & 0x3f)<<18) | ((str[3] & 0x3f)<<12) | ((str[4] & 0x3f)<<6) | ((str[5] & 0x3f)<<0);\n\t\t\tif (uc >= (1u<<26))\n\t\t\t\t*error = 0;\n\t\t\telse\n\t\t\t\t*error = 2;\n\t\t}\n\t\telse *error = 1;\n\t}\n\t//0xfe and 0xff, while plausable leading bytes, are not permitted.\n#if 0\n\telse if ((*str & 0xff) == 0xfe)\n\t{\n\t\tif ((str[1] & 0xc0) == 0x80 && (str[2] & 0xc0) == 0x80 && (str[3] & 0xc0) == 0x80 && (str[4] & 0xc0) == 0x80)\n\t\t{\n\t\t\tl = 7;\n\t\t\tuc = 0 | ((str[1] & 0x3f)<<30) | ((str[2] & 0x3f)<<24) | ((str[3] & 0x3f)<<18) | ((str[4] & 0x3f)<<12) | ((str[5] & 0x3f)<<6) | ((str[6] & 0x3f)<<0);\n\t\t\tif (uc >= (1u<<31))\n\t\t\t\t*error = 0;\n\t\t\telse\n\t\t\t\t*error = 2;\n\t\t}\n\t\telse *error = 1;\n\t}\n\telse if ((*str & 0xff) == 0xff)\n\t{\n\t\tif ((str[1] & 0xc0) == 0x80 && (str[2] & 0xc0) == 0x80 && (str[3] & 0xc0) == 0x80 && (str[4] & 0xc0) == 0x80)\n\t\t{\n\t\t\tl = 8;\n\t\t\tuc = 0 | ((str[1] & 0x3f)<<36) | ((str[2] & 0x3f)<<30) | ((str[3] & 0x3f)<<24) | ((str[4] & 0x3f)<<18) | ((str[5] & 0x3f)<<12) | ((str[6] & 0x3f)<<6) | ((str[7] & 0x3f)<<0);\n\t\t\tif (uc >= (1llu<<36))\n\t\t\t\t*error = false;\n\t\t\telse\n\t\t\t\t*error = 2;\n\t\t}\n\t\telse *error = 1;\n\t}\n#endif\n\telse if (*str & 0x80)\n\t{\n\t\t//sequence error\n\t\t*error = 1;\n\t\tuc = 0xe000u + *str;\n\t}\n\telse \n\t{\n\t\t//ascii char\n\t\t*error = 0;\n\t\tuc = *str;\n\t}\n\n\t*out = (const void*)(str + l);\n\n\tif (!*error)\n\t{\n\t\t//try to deal with surrogates by decoding the low if we see a high.\n\t\tif (uc >= 0xd800u && uc < 0xdc00u)\n\t\t{\n#if 1\n\t\t\t//cesu-8\n\t\t\tconst char *lowend;\n\t\t\tunsigned int lowsur = utf8_decode(error, str + l, &lowend);\n\t\t\tif (*error == 4)\n\t\t\t{\n\t\t\t\t*out = lowend;\n\t\t\t\tuc = (((uc&0x3ffu) << 10) | (lowsur&0x3ffu)) + 0x10000;\n\t\t\t\t*error = false;\n\t\t\t}\n\t\t\telse\n#endif\n\t\t\t{\n\t\t\t\t*error = 3;\t//bad - lead surrogate without tail.\n\t\t\t}\n\t\t}\n\t\tif (uc >= 0xdc00u && uc < 0xe000u)\n\t\t\t*error = 4;\t//bad - tail surrogate\n\n\t\t//these are meant to be illegal too\n\t\tif (uc == 0xfffeu || uc == 0xffffu || uc > 0x10ffffu)\n\t\t\t*error = 2;\t//illegal code\n\t}\n\n\treturn uc;\n}\n\nunsigned int unicode_decode(int *error, const void *in, char const**out, qboolean markup)\n{\n\tunsigned int charcode;\n\tif (markup && ((char*)in)[0] == '^' && ((char*)in)[1] == 'U' && ishexcode(((char*)in)[2]) && ishexcode(((char*)in)[3]) && ishexcode(((char*)in)[4]) && ishexcode(((char*)in)[5]))\n\t{\n\t\t*error = 0;\n\t\t*out = (char*)in + 6;\n\t\tcharcode = (dehex(((char*)in)[2]) << 12) | (dehex(((char*)in)[3]) << 8) | (dehex(((char*)in)[4]) << 4) | (dehex(((char*)in)[5]) << 0);\n\t}\n\telse if (markup && ((char*)in)[0] == '^' && ((char*)in)[1] == '{')\n\t{\n\t\t*error = 0;\n\t\t*out = (char*)in + 2;\n\t\tcharcode = 0;\n\t\twhile (ishexcode(**out))\n\t\t{\n\t\t\tcharcode <<= 4;\n\t\t\tcharcode |= dehex(**out);\n\t\t\t*out+=1;\n\t\t}\n\t\tif (**out == '}')\n\t\t\t*out+=1;\n\t}\n\telse if (com_parseutf8.ival > 0)\n\t\tcharcode = utf8_decode(error, in, out);\n\telse if (com_parseutf8.ival)\n\t{\n\t\t*error = 0;\n\t\tcharcode = *(unsigned char*)in;\t//iso8859-1\n\t\t*out = (char*)in + 1;\n\t}\n\telse\n\t{\t//quake\n\t\t*error = 0;\n\t\tcharcode = *(unsigned char*)in;\n\t\tif (charcode && charcode != '\\n' && charcode != '\\t' && charcode != '\\r' && (charcode < ' ' || charcode > 127))\n\t\t\tcharcode |= 0xe000;\n\t\t*out = (char*)in + 1;\n\t}\n\n\treturn charcode;\n}\n\nunsigned int utf8_encode(void *out, unsigned int unicode, int maxlen)\n{\n\tunsigned int bcount = 1;\n\tunsigned int lim = 0x80;\n\tunsigned int shift;\n\tif (!unicode)\n\t{\t//modified utf-8 encodes encapsulated nulls as over-long.\n\t\tbcount = 2;\n\t}\n\telse\n\t{\n\t\twhile (unicode >= lim)\n\t\t{\n\t\t\tif (bcount == 1)\n\t\t\t\tlim <<= 4;\n\t\t\telse if (bcount < 7)\n\t\t\t\tlim <<= 5;\n\t\t\telse\n\t\t\t\tlim <<= 6;\n\t\t\tbcount++;\n\t\t}\n\t}\n\n\t//error if needed\n\tif (maxlen < bcount)\n\t\treturn 0;\n\n\t//output it.\n\tif (bcount == 1)\n\t{\n\t\t*((unsigned char *)out) = (unsigned char)(unicode&0x7f);\n\t\tout = (char*)out + 1;\n\t}\n\telse\n\t{\n\t\tshift = bcount*6;\n\t\tshift = shift-6;\n\t\t*((unsigned char *)out) = (unsigned char)((unicode>>shift)&(0x0000007f>>bcount)) | ((0xffffff00 >> bcount) & 0xff);\n\t\tout = (char*)out + 1;\n\t\tdo\n\t\t{\n\t\t\tshift = shift-6;\n\t\t\t*((unsigned char *)out) = (unsigned char)((unicode>>shift)&0x3f) | 0x80;\n\t\t\tout = (char*)out + 1;\n\t\t}\n\t\twhile(shift);\n\t}\n\treturn bcount;\n}\n\nunsigned int qchar_encode(char *out, unsigned int unicode, int maxlen, qboolean markup)\n{\n\tstatic const char hex[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};\n\t//FIXME: is it a bug that we can't distinguish between true ascii and 0xe0XX ?\n\t//ntrv are considered special by parsefunstring and are not remapped back to the quake glyphs, so try to keep them as quake glyphs where possible\n\tif (((unicode >= 32 || unicode == '\\n' || unicode == '\\t' || unicode == '\\r') && unicode < 128) || (unicode >= 0xe000 && unicode <= 0xe0ff && unicode != (0xe000|'\\n') && unicode != (0xe000|'\\t') && unicode != (0xe000|'\\r') && unicode != (0xe000|'\\v')))\n\t{\t//quake compatible chars\n\t\tif (maxlen < 1)\n\t\t\treturn 0;\n\t\t*out++ = unicode & 0xff;\n\t\treturn 1;\n\t}\n\telse if (!markup)\n\t{\n\t\tif (maxlen < 1)\n\t\t\treturn 0;\n\t\t*out++ = '?';\n\t\treturn 1;\n\t}\n\telse if (unicode > 0xffff)\n\t{\t//chars longer than 16 bits\n\t\tchar *o = out;\n\t\tif (maxlen < 11)\n\t\t\treturn 0;\n\t\t*out++ = '^';\n\t\t*out++ = '{';\n\t\tif (unicode > 0xfffffff)\n\t\t\t*out++ = hex[(unicode>>28)&15];\n\t\tif (unicode > 0xffffff)\n\t\t\t*out++ = hex[(unicode>>24)&15];\n\t\tif (unicode > 0xfffff)\n\t\t\t*out++ = hex[(unicode>>20)&15];\n\t\tif (unicode > 0xffff)\n\t\t\t*out++ = hex[(unicode>>16)&15];\n\t\tif (unicode > 0xfff)\n\t\t\t*out++ = hex[(unicode>>12)&15];\n\t\tif (unicode > 0xff)\n\t\t\t*out++ = hex[(unicode>>8)&15];\n\t\tif (unicode > 0xf)\n\t\t\t*out++ = hex[(unicode>>4)&15];\n\t\tif (unicode > 0x0)\n\t\t\t*out++ = hex[(unicode>>0)&15];\n\t\t*out++ = '}';\n\t\treturn out - o;\n\t}\n\telse\n\t{\t//16bit chars\n\t\tif (maxlen < 6)\n\t\t\treturn 0;\n\t\t*out++ = '^';\n\t\t*out++ = 'U';\n\t\t*out++ = hex[(unicode>>12)&15];\n\t\t*out++ = hex[(unicode>>8)&15];\n\t\t*out++ = hex[(unicode>>4)&15];\n\t\t*out++ = hex[(unicode>>0)&15];\n\t\treturn 6;\n\t}\n}\n\nunsigned int iso88591_encode(char *out, unsigned int unicode, int maxlen, qboolean markup)\n{\n\tstatic const char hex[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};\n\tif (unicode < 256 || (unicode >= 0xe020 && unicode < 0xe080))\n\t{\t//iso8859-1 compatible chars\n\t\tif (maxlen < 1)\n\t\t\treturn 0;\n\t\t*out++ = unicode;\n\t\treturn 1;\n\t}\n\telse if (!markup)\n\t{\n\t\tif (maxlen < 1)\n\t\t\treturn 0;\n\t\t*out++ = '?';\n\t\treturn 1;\n\t}\n\telse if (unicode > 0xffff)\n\t{\t//chars longer than 16 bits\n\t\tchar *o = out;\n\t\tif (maxlen < 11)\n\t\t\treturn 0;\n\t\t*out++ = '^';\n\t\t*out++ = '{';\n\t\tif (unicode > 0xfffffff)\n\t\t\t*out++ = hex[(unicode>>28)&15];\n\t\tif (unicode > 0xffffff)\n\t\t\t*out++ = hex[(unicode>>24)&15];\n\t\tif (unicode > 0xfffff)\n\t\t\t*out++ = hex[(unicode>>20)&15];\n\t\tif (unicode > 0xffff)\n\t\t\t*out++ = hex[(unicode>>16)&15];\n\t\tif (unicode > 0xfff)\n\t\t\t*out++ = hex[(unicode>>12)&15];\n\t\tif (unicode > 0xff)\n\t\t\t*out++ = hex[(unicode>>8)&15];\n\t\tif (unicode > 0xf)\n\t\t\t*out++ = hex[(unicode>>4)&15];\n\t\tif (unicode > 0x0)\n\t\t\t*out++ = hex[(unicode>>0)&15];\n\t\t*out++ = '}';\n\t\treturn out - o;\n\t}\n\telse\n\t{\t//16bit chars\n\t\tif (maxlen < 6)\n\t\t\treturn 0;\n\t\t*out++ = '^';\n\t\t*out++ = 'U';\n\t\t*out++ = hex[(unicode>>12)&15];\n\t\t*out++ = hex[(unicode>>8)&15];\n\t\t*out++ = hex[(unicode>>4)&15];\n\t\t*out++ = hex[(unicode>>0)&15];\n\t\treturn 6;\n\t}\n}\n\nunsigned int unicode_encode(char *out, unsigned int unicode, int maxlen, qboolean markup)\n{\n\tif (com_parseutf8.ival > 0)\n\t\treturn utf8_encode(out, unicode, maxlen);\n\telse if (com_parseutf8.ival)\n\t\treturn iso88591_encode(out, unicode, maxlen, markup);\n\telse\n\t\treturn qchar_encode(out, unicode, maxlen, markup);\n}\n\n//char-based strlen.\nunsigned int unicode_charcount(const char *in, size_t buffersize, qboolean markup)\n{\n\tint error;\n\tconst char *end = in + buffersize;\n\tint chars = 0;\n\tfor(chars = 0; in < end && *in; chars+=1)\n\t{\n\t\tunicode_decode(&error, in, &in, markup);\n\n\t\tif (in > end)\n\t\t\tbreak;\t//exceeded buffer size uncleanly\n\t}\n\treturn chars;\n}\n\n//handy hacky function.\nunsigned int unicode_byteofsfromcharofs(const char *str, unsigned int charofs, qboolean markup)\n{\n\tconst char *in = str;\n\tint error;\n\tint chars;\n\tfor(chars = 0; *in; chars+=1)\n\t{\n\t\tif (chars >= charofs)\n\t\t\treturn in - str;\n\n\t\tunicode_decode(&error, in, &in, markup);\n\t}\n\treturn in - str;\n}\n//handy hacky function.\nunsigned int unicode_charofsfrombyteofs(const char *str, unsigned int byteofs, qboolean markup)\n{\n\tint error;\n\tconst char *end = str + byteofs;\n\tint chars = 0;\n\tfor(chars = 0; str < end && *str; chars+=1)\n\t{\n\t\tunicode_decode(&error, str, &str, markup);\n\n\t\tif (str > end)\n\t\t\tbreak;\t//exceeded buffer size uncleanly\n\t}\n\treturn chars;\n}\nvoid unicode_strpad(char *out, size_t outsize, const char *in, qboolean leftalign, size_t minwidth, size_t maxwidth, qboolean markup)\n{\n\tif(com_parseutf8.ival <= 0 && !markup)\n\t{\n\t\tQ_snprintfz(out, outsize, \"%*.*s\", leftalign ? -(int) minwidth : (int) minwidth, (int) maxwidth, in);\n\t}\n\telse\n\t{\n\t\tsize_t l = unicode_byteofsfromcharofs(in, maxwidth, markup);\n\t\tsize_t actual_width = unicode_charcount(in, l, markup);\n\t\tint pad = (int)((actual_width >= minwidth) ? 0 : (minwidth - actual_width));\n\t\tint prec = (int)l;\n\t\tint lpad = leftalign ? 0 : pad;\n\t\tint rpad = leftalign ? pad : 0;\n\t\tQ_snprintfz(out, outsize, \"%*s%.*s%*s\", lpad, \"\", prec, in, rpad, \"\");\n\t}\n}\n\n\n#if defined(FTE_TARGET_WEB) || defined(__DJGPP__)\n//targets that don't support towupper/towlower...\n#define towupper Q_towupper\n#define towlower Q_towlower\nint towupper(int c)\n{\n\tif (c < 128)\n\t\treturn toupper(c);\n\treturn c;\n}\nint towlower(int c)\n{\n\tif (c < 128)\n\t\treturn tolower(c);\n\treturn c;\n}\n#endif\n\nsize_t unicode_strtoupper(const char *in, char *out, size_t outsize, qboolean markup)\n{\n\t//warning: towupper is locale-specific (eg: turkish has both I and dotted-I and thus i should transform to dotted-I rather than to I).\n\t//also it can't easily cope with accent prefixes.\n\tint error;\n\tunsigned int c;\n\tsize_t l = 0;\n\toutsize -= 1;\n\n\twhile(*in)\n\t{\n\t\tc = unicode_decode(&error, in, &in, markup);\n\t\tif (c >= 0xe020 && c <= 0xe07f)\t//quake-char-aware.\n\t\t\tc = towupper(c & 0x7f) + (c & 0xff80);\n\t\telse\n\t\t\tc = towupper(c);\n\t\tl = unicode_encode(out, c, outsize - l, markup);\n\t\tout += l;\n\t}\n\t*out = 0;\n\n\treturn l;\n}\n\nsize_t unicode_strtolower(const char *in, char *out, size_t outsize, qboolean markup)\n{\n\t//warning: towlower is locale-specific (eg: turkish has both i and dotless-i and thus I should transform to dotless-i rather than to i).\n\t//also it can't easily cope with accent prefixes.\n\tint error;\n\tunsigned int c;\n\tsize_t l = 0;\n\toutsize -= 1;\n\n\twhile(*in)\n\t{\n\t\tc = unicode_decode(&error, in, &in, markup);\n\t\tif (c >= 0xe020 && c <= 0xe07f)\t//quake-char-aware.\n\t\t\tc = towlower(c & 0x7f) + (c & 0xff80);\n\t\telse\n\t\t\tc = towlower(c);\n\t\tl = unicode_encode(out, c, outsize - l, markup);\n\t\tout += l;\n\t}\n\t*out = 0;\n\n\treturn l;\n}\n\n///=====================================\n\n// This is the standard RGBI palette used in CGA text mode\nconsolecolours_t consolecolours[MAXCONCOLOURS] = {\n\t{0,    0,    0   }, // black\n\t{0,    0,    0.67}, // blue\n\t{0,    0.67, 0   }, // green\n\t{0,    0.67, 0.67}, // cyan\n\t{0.67, 0,    0   }, // red\n\t{0.67, 0,    0.67}, // magenta\n\t{0.67, 0.33, 0   }, // brown\n\t{0.67, 0.67, 0.67}, // light gray\n\t{0.33, 0.33, 0.33}, // dark gray\n\t{0.33, 0.33, 1   }, // light blue\n\t{0.33, 1,    0.33}, // light green\n\t{0.33, 1,    1   }, // light cyan\n\t{1,    0.33, 0.33}, // light red\n\t{1,    0.33, 1   }, // light magenta\n\t{1,    1,    0.33}, // yellow\n\t{1,    1,    1   }  // white\n};\n\n// This is for remapping the Q3 color codes to character masks, including ^9\n// if using this table, make sure the truecolour flag is disabled first.\nconchar_t q3codemasks[MAXQ3COLOURS] = {\n\tCOLOR_BLACK\t\t<< CON_FGSHIFT,\t// 0, black\n\tCOLOR_RED\t\t<< CON_FGSHIFT,\t// 1, red\n\tCOLOR_GREEN\t\t<< CON_FGSHIFT,\t// 2, green\n\tCOLOR_YELLOW\t<< CON_FGSHIFT,\t// 3, yellow\n\tCOLOR_BLUE\t\t<< CON_FGSHIFT,\t// 4, blue\n\tCOLOR_CYAN\t\t<< CON_FGSHIFT,\t// 5, cyan\n\tCOLOR_MAGENTA\t<< CON_FGSHIFT,\t// 6, magenta\n\tCOLOR_WHITE\t\t<< CON_FGSHIFT,\t// 7, white\n\t(COLOR_WHITE\t<< CON_FGSHIFT)|CON_HALFALPHA,\t// 8, half-alpha white (BX_COLOREDTEXT)\n\tCOLOR_GREY\t\t<< CON_FGSHIFT\t// 9, \"half-intensity\" (BX_COLOREDTEXT)\n};\n\n//Converts a conchar_t string into a char string. returns the null terminator. pass NULL for stop to calc it\nchar *COM_DeFunString(conchar_t *str, conchar_t *stop, char *out, int outsize, qboolean ignoreflags, qboolean forceutf8)\n{\n\tstatic char tohex[16] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};\n\tunsigned int codeflags, codepoint;\n\tif (!stop)\n\t{\n\t\tfor (stop = str; *stop; stop++)\n\t\t\t;\n\t}\n#ifdef _DEBUG\n\tif (!outsize)\n\t\tSys_Error(\"COM_DeFunString given outsize=0\");\n#endif\n\toutsize--;\n\n\t/*if (ignoreflags)\n\t{\n\t\twhile(str < stop)\n\t\t{\n\t\t\tif (!--outsize)\n\t\t\t\tbreak;\n\t\t\t*out++ = (unsigned char)(*str++&255);\n\t\t}\n\t\t*out = 0;\n\t}\n\telse*/\n\t{\n\t\tunsigned int fl, d;\n\t\tunsigned int c;\n\t\tint prelinkflags = CON_WHITEMASK;\t//if used, its already an error.\n\t\t//FIXME: TEST!\n\n\t\tfl = CON_WHITEMASK;\n\t\twhile(str <= stop)\n\t\t{\n\t\t\tif (str == stop)\n\t\t\t{\n\t\t\t\tcodeflags = CON_WHITEMASK;\n\t\t\t\tcodepoint = 0;\n\t\t\t\tstr++;\n\t\t\t}\n\t\t\telse\n\t\t\t\tstr = Font_Decode(str, &codeflags, &codepoint);\n\t\t\tif ((codeflags & CON_HIDDEN) && ignoreflags)\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (codeflags == (CON_LINKSPECIAL | CON_HIDDEN) && codepoint == '[')\n\t\t\t{\n\t\t\t\tif (!ignoreflags)\n\t\t\t\t{\n\t\t\t\t\tif (outsize<=2)\n\t\t\t\t\t\tbreak;\n\t\t\t\t\toutsize -= 2;\n\t\t\t\t\t*out++ = '^';\n\t\t\t\t\t*out++ = '[';\n\t\t\t\t}\n\t\t\t\tprelinkflags = fl;\n\t\t\t\tfl = COLOR_RED << CON_FGSHIFT;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\telse if (codeflags == (CON_LINKSPECIAL | CON_HIDDEN) && codepoint == ']')\n\t\t\t{\n\t\t\t\tif (!ignoreflags)\n\t\t\t\t{\n\t\t\t\t\tif (outsize<=2)\n\t\t\t\t\t\tbreak;\n\t\t\t\t\toutsize -= 2;\n\t\t\t\t\t*out++ = '^';\n\t\t\t\t\t*out++ = ']';\n\t\t\t\t}\n\t\t\t\tfl = prelinkflags;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\telse if (codeflags != fl && !ignoreflags)\n\t\t\t{\n\t\t\t\td = fl^codeflags;\n\t\t\t\tif (d & CON_BLINKTEXT)\n\t\t\t\t{\n\t\t\t\t\tif (outsize<=2)\n\t\t\t\t\t\tbreak;\n\t\t\t\t\toutsize -= 2;\n\t\t\t\t\t*out++ = '^';\n\t\t\t\t\t*out++ = 'b';\n\t\t\t\t}\n\t\t\t\tif (d & CON_2NDCHARSETTEXT)\n\t\t\t\t{\t//FIXME: convert to quake glyphs...\n\t\t\t\t\tif (!com_parseutf8.ival && !forceutf8 && codepoint >= 32 && codepoint <= 127 && (codeflags&CON_2NDCHARSETTEXT))\n\t\t\t\t\t{\t//strip the flag and encode it in private use (so it gets encoded as quake-compatible)\n\t\t\t\t\t\tcodeflags &= ~CON_2NDCHARSETTEXT;\n\t\t\t\t\t\tcodepoint |= 0xe080;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tif (outsize<=2)\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\toutsize -= 2;\n\t\t\t\t\t\t*out++ = '^';\n\t\t\t\t\t\t*out++ = 'a';\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (codeflags & CON_RICHFORECOLOUR)\n\t\t\t\t{\n\t\t\t\t\tif (d & (CON_RICHFORECOLOUR|CON_RICHFOREMASK))\n\t\t\t\t\t{\n\t\t\t\t\t\tif (outsize<=5)\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\toutsize -= 5;\n\t\t\t\t\t\t*out++ = '^';\n\t\t\t\t\t\t*out++ = 'x';\n\t\t\t\t\t\t*out++ = tohex[(codeflags>>CON_RICHRSHIFT)&15];\n\t\t\t\t\t\t*out++ = tohex[(codeflags>>CON_RICHGSHIFT)&15];\n\t\t\t\t\t\t*out++ = tohex[(codeflags>>CON_RICHBSHIFT)&15];\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tif (d & (CON_RICHFORECOLOUR | CON_FGMASK | CON_BGMASK | CON_NONCLEARBG))\n\t\t\t\t\t{\n\t\t\t\t\t\tstatic char q3[16] = {\t'0', 0,   0,   0,\n\t\t\t\t\t\t\t\t\t\t\t\t0,   0,   0,   0,\n\t\t\t\t\t\t\t\t\t\t\t\t0,\t '4', '2', '5',\n\t\t\t\t\t\t\t\t\t\t\t\t'1', '6', '3', '7'};\n\t\t\t\t\t\tif (d & CON_RICHFORECOLOUR)\n\t\t\t\t\t\t\td = (d&~CON_RICHFOREMASK) | (CON_WHITEMASK&CON_RICHFOREMASK);\n\t\t\t\t\t\tif (!(d & (CON_BGMASK | CON_NONCLEARBG)) && q3[(codeflags & CON_FGMASK) >> CON_FGSHIFT])\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (outsize<=2)\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\toutsize -= 2;\n\n\t\t\t\t\t\t\td = codeflags;\n\t\t\t\t\t\t\t*out++ = '^';\n\t\t\t\t\t\t\t*out++ = q3[(codeflags & CON_FGMASK) >> CON_FGSHIFT];\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (outsize<=4)\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\toutsize -= 4;\n\n\t\t\t\t\t\t\t*out++ = '^';\n\t\t\t\t\t\t\t*out++ = '&';\n\t\t\t\t\t\t\tif ((codeflags & CON_FGMASK) == CON_WHITEMASK)\n\t\t\t\t\t\t\t\t*out = '-';\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t*out = tohex[(codeflags>>24)&0xf];\n\t\t\t\t\t\t\tout++;\n\t\t\t\t\t\t\tif (codeflags & CON_NONCLEARBG)\n\t\t\t\t\t\t\t\t*out = tohex[(codeflags>>28)&0xf];\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t*out = '-';\n\t\t\t\t\t\t\tout++;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif (d & CON_HALFALPHA)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (outsize<=2)\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\toutsize -= 2;\n\t\t\t\t\t\t*out++ = '^';\n\t\t\t\t\t\t*out++ = 'h';\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tfl = codeflags;\n\t\t\t}\n\n\t\t\t//don't magically show hidden text\n\t\t\tif (ignoreflags && (codeflags & CON_HIDDEN))\n\t\t\t\tcontinue;\n\n\t\t\tif (str > stop)\n\t\t\t\tbreak;\n\n\t\t\tif (forceutf8)\n\t\t\t\tc = utf8_encode(out, codepoint, outsize-1);\n\t\t\telse\n\t\t\t\tc = unicode_encode(out, codepoint, outsize-1, !ignoreflags);\n\t\t\tif (!c)\n\t\t\t\tbreak;\n\t\t\toutsize -= c;\n\t\t\tout += c;\n\t\t}\n\t\t*out = 0;\n\t}\n\treturn out;\n}\n\n#ifdef HAVE_LEGACY\nstatic unsigned int koi2wc (unsigned char uc)\n{\n\tstatic const char koi2wc_table[64] =\n\t{\n\t\t\t0x4e,0x30,0x31,0x46,0x34,0x35,0x44,0x33,0x45,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,\n\t\t\t0x3f,0x4f,0x40,0x41,0x42,0x43,0x36,0x32,0x4c,0x4b,0x37,0x48,0x4d,0x49,0x47,0x4a,\n\t\t\t0x2e,0x10,0x11,0x26,0x14,0x15,0x24,0x13,0x25,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,\n\t\t\t0x1f,0x2f,0x20,0x21,0x22,0x23,0x16,0x12,0x2c,0x2b,0x17,0x28,0x2d,0x29,0x27,0x2a\n\t};\n\tif (uc >= 192 /* && (unsigned char)c <= 255 */)\n\t\treturn koi2wc_table[uc - 192] + 0x400;\n\telse if (uc == '#' + 128)\n\t\treturn 0x0451;\t// russian small yo\n\telse if (uc == '3' + 128)\n\t\treturn 0x0401;\t// russian capital yo\n\telse if (uc == '4' + 128)\n\t\treturn 0x0404;\t// ukrainian capital round E\n\telse if (uc == '$' + 128)\n\t\treturn 0x0454;\t// ukrainian small round E\n\telse if (uc == '6' + 128)\n\t\treturn 0x0406;\t// ukrainian capital I\n\telse if (uc == '&' + 128)\n\t\treturn 0x0456;\t// ukrainian small i\n\telse if (uc == '7' + 128)\n\t\treturn 0x0407;\t// ukrainian capital I with two dots\n\telse if (uc == '\\'' + 128)\n\t\treturn 0x0457;\t// ukrainian small i with two dots\n\telse if (uc == '>' + 128)\n\t\treturn 0x040e;\t// belarusian Y\n\telse if (uc == '.' + 128)\n\t\treturn 0x045e;\t// belarusian y\n\telse if (uc == '/' + 128)\n\t\treturn 0x042a;\t// russian capital hard sign\n\telse\n\t\treturn uc;\n}\n#endif\n\nenum\n{\n\tBIDI_NEUTRAL,\n\tBIDI_LTR,\n\tBIDI_RTL,\n};\nstatic char *bidi_chartype;\nstatic unsigned int bidi_charcount;\n\n\n//semi-colon delimited tokens, without whitespace awareness\nchar *COM_ParseStringSetSep (const char *data, char sep, char *out, size_t outsize)\n{\n\tint\tc;\n\tsize_t\tlen;\n\n\tif (out == com_token)\n\t\tCOM_AssertMainThread(\"COM_ParseStringSetSep\");\n\n\tlen = 0;\n\tout[0] = 0;\n\n\tif (data)\n\tfor (;*data;)\n\t{\n\t\tif (len >= outsize-1)\n\t\t{\n\t\t\tout[len] = 0;\n\t\t\treturn (char*)data;\n\t\t}\n\t\tc = *data++;\n\t\tif (c == sep)\n\t\t\tbreak;\n\t\tout[len++] = c;\n\t}\n\n\tout[len] = 0;\n\treturn (char*)data;\n}\nvoid COM_BiDi_Shutdown(void)\n{\n\tbidi_charcount = 0;\n\tBZ_Free(bidi_chartype);\n\tbidi_chartype = NULL;\n}\nstatic void COM_BiDi_Setup(void)\n{\n\tchar *file;\n\tchar *line;\n\tchar *end;\n\tchar *tok;\n\tunsigned int c;\n\tqofs_t size;\n\n\tCOM_AssertMainThread(\"COM_ParseToken\");\n\n\tfile = FS_MallocFile(\"bidi.dat\", FS_ROOT, &size);\n\tif (file)\n\t{\n\t\tbidi_chartype = file;\n\t\tbidi_charcount = size;\n\t\treturn;\n\t}\n\n\tfile = FS_MallocFile(\"UnicodeData.txt\", FS_ROOT, NULL);\n\tif (!file)\n\t\treturn;\n\n\tbidi_charcount = 0xffff;\n\tbidi_chartype = BZ_Malloc(bidi_charcount);\n\tif (!bidi_chartype)\n\t\tbidi_charcount = 0;\n\telse\n\t{\n\t\tfor (c = 0; c < bidi_charcount; c++)\n\t\t\tbidi_chartype[c] = BIDI_NEUTRAL;\n\t\tfor(line = file; line; line = end)\n\t\t{\n\t\t\tend = strchr(line, '\\n');\n\t\t\tif (end)\n\t\t\t\t*end++ = 0;\n\n\t\t\ttok = COM_ParseStringSetSep(line,';', com_token, sizeof(com_token));\t//number\n\t\t\tc = strtoul(com_token, NULL, 16);\n\t\t\ttok = COM_ParseStringSetSep(tok,';', com_token, sizeof(com_token));\t//name\n\t\t\ttok = COM_ParseStringSetSep(tok,';', com_token, sizeof(com_token));\t//class?\n\t\t\ttok = COM_ParseStringSetSep(tok,';', com_token, sizeof(com_token));\t//?\n\t\t\ttok = COM_ParseStringSetSep(tok,';', com_token, sizeof(com_token));\t//bidi\n\t\t\tif (c < bidi_charcount)\n\t\t\t{\n\t\t\t\tif (!Q_strcasecmp(com_token, \"R\") || !Q_strcasecmp(com_token, \"AL\"))\n\t\t\t\t\tbidi_chartype[c] = BIDI_RTL;\n\t\t\t\telse if (!Q_strcasecmp(com_token, \"L\"))\n\t\t\t\t\tbidi_chartype[c] = BIDI_LTR;\n\t\t\t\telse\n\t\t\t\t\tbidi_chartype[c] = BIDI_NEUTRAL;\n\t\t\t}\n\t\t}\n\n\t\t//trim\n\t\twhile(bidi_charcount>0 && bidi_chartype[bidi_charcount-1] == BIDI_NEUTRAL)\n\t\t\tbidi_charcount--;\n\t\tFS_WriteFile(\"bidi.dat\", bidi_chartype, bidi_charcount, FS_ROOT);\n\t}\n\tBZ_Free(file);\n}\n//bi-direction text is fun.\n//the text is specified in input order. the first in the string is the first entered on the keyboard.\n//this makes switching direction mid-line quite awkward. so lets hope you don't do that too often, mmkay?\nstatic void COM_BiDi_Parse(conchar_t *fte_restrict start, size_t length)\n{\n\tchar fl[2048], next, run, prev, para = BIDI_LTR;\n\tsize_t i, runstart, j, k;\n\tunsigned int c;\n\tconchar_t swap;\n\tif (!bidi_charcount || !length || length > sizeof(fl))\n\t\treturn;\n\n\tfor (i = 0; i < length; i++)\n\t{\n\t\tc = start[i] & CON_CHARMASK;\n\t\tif (c >= bidi_charcount)\n\t\t\tfl[i] = BIDI_NEUTRAL;\n\t\telse\n\t\t\tfl[i] = bidi_chartype[c];\n\t}\n\t\n\t//de-neutralise it\n\tprev = fl[0];\n\tfor (i = 0; i < length; )\n\t{\n\t\tif (fl[i] == BIDI_NEUTRAL)\n\t\t{\n\t\t\tnext = prev;\t//trailing weak chars can just use the first side\n\t\t\tfor (runstart = i; i < length; i++)\n\t\t\t{\n\t\t\t\tnext = fl[i];\n\t\t\t\tif (next != BIDI_NEUTRAL)\n\t\t\t\t{\n\t\t\t\t\ti--;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\t//this can happen if the only text is neutral\n\t\t\tif (prev == BIDI_NEUTRAL)\n\t\t\t\trun = next;\n\t\t\t//if the strong cars are the same direction on both side, we can just use that direction\n\t\t\telse if (prev == next)\n\t\t\t\trun = prev;\n\t\t\t//if the strong chars differ, we revert to the paragraph's direction.\n\t\t\telse\n\t\t\t\trun = para;\n\n\t\t\twhile(runstart <= i)\n\t\t\t\tfl[runstart++] = run;\n\t\t\ti++;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tprev = fl[i];\n\t\t\ti++;\n\t\t}\n\t}\n\n\tfor (run = para, runstart = 0, i = 0; i <= length; i++) \n\t{\n\t\tif (i >= length)\n\t\t\tnext = para;\n\t\telse\n\t\t\tnext = fl[i];\n\t\tif (next != run)\n\t\t{\n\t\t\tif (run == BIDI_NEUTRAL)\n\t\t\t\tbreak;\n\t\t\tif (run == BIDI_RTL)\n\t\t\t{\t//now swap the rtl text\n\t\t\t\tk = (i-runstart)/2;\n\t\t\t\tfor (j = 0; j < k; j++)\n\t\t\t\t{\n\t\t\t\t\t//FIXME: ( -> ) and vice versa.\n\t\t\t\t\tswap = start[runstart+j];\n\t\t\t\t\tstart[runstart+j] = start[i-j-1];\n\t\t\t\t\tstart[i-j-1] = swap;\n\t\t\t\t}\n\t\t\t}\n\t\t\trun = next;\n\t\t\trunstart = i;\n\t\t}\n\t}\n}\n\n//Takes a q3-style fun string, and returns an expanded string-with-flags (actual return value is the null terminator)\n//outsize parameter is in _BYTES_ (so sizeof is safe).\nconchar_t *COM_ParseFunString(conchar_t defaultflags, const char *str, conchar_t *out, int outsize, int flags)\n{\n\tconchar_t extstack[4];\n\tint extstackdepth = 0;\n\tunsigned int uc;\n\tint utf8 = com_parseutf8.ival;\n\tconchar_t linkinitflags = CON_WHITEMASK;/*doesn't need the init, but msvc is stupid*/\n\tqboolean keepmarkup = !!(flags & PFS_KEEPMARKUP);\n\tqboolean linkkeep = keepmarkup;\n\tqboolean ezquakemess = false;\n\tconchar_t *linkstart = NULL;\n\n\tconchar_t ext;\n\tconchar_t *oldout = out;\n#ifdef HAVE_LEGACY\n\textern cvar_t dpcompat_console;\n\textern cvar_t ezcompat_markup;\n\n\tif (flags & PFS_EZQUAKEMARKUP)\n\t{\n\t\tezquakemess = true;\n\t\tutf8 = 0;\n\t}\n#endif\n\tif (flags & PFS_FORCEUTF8)\n\t\tutf8 = 2;\n\n\toutsize /= sizeof(conchar_t);\n\tif (!outsize)\n\t\treturn out;\n\t//then outsize is decremented then checked before each write, so the trailing null has space\n\n#if 0\n\twhile(*str)\n\t{\n\t\t*out++ = CON_WHITEMASK|(unsigned char)*str++;\n\t}\n\t*out = 0;\n\treturn out;\n#endif\n\n\tif (*str == 1 || *str == 2\n#ifdef HAVE_LEGACY\n\t\t|| (*str == 3 && dpcompat_console.ival)\n#endif\n\t\t)\n\t{\n\t\tdefaultflags ^= CON_2NDCHARSETTEXT;\n\t\tstr++;\n\t}\n\n\text = defaultflags;\n\n\twhile(*str)\n\t{\n\t\tif ((*str & 0x80) && utf8 > 0)\n\t\t{\t//check for utf-8\n\t\t\tint decodeerror;\n\t\t\tconst char *end;\n\t\t\tuc = utf8_decode(&decodeerror, str, &end);\n\t\t\tif (decodeerror && !(utf8 & 2))\n\t\t\t{\n\t\t\t\tutf8 &= ~1;\n\t\t\t\t//malformed encoding we just drop through and stop trying to decode.\n\t\t\t\t//if its just a malformed or overlong string, we end up with a chunk of 'red' chars.\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (uc > 0x10ffff)\n\t\t\t\t\tuc = 0xfffd;\n\t\t\t\tif (!--outsize)\n\t\t\t\t\tbreak;\n\n\t\t\t\tif (uc > 0xffff)\n\t\t\t\t{\n\t\t\t\t\tif (!--outsize)\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t*out++ = uc>>16 | CON_LONGCHAR | (ext & CON_HIDDEN);\n\t\t\t\t\tuc &= 0xffff;\n\t\t\t\t}\n\t\t\t\t*out++ = uc | ext;\n\t\t\t\tstr = end;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\t\tif (ezquakemess && *str == '^')\n\t\t{\n\t\t\tstr++;\n\t\t\tuc = (unsigned char)(*str++);\n\t\t\t*out++ = (uc | ext) ^ CON_2NDCHARSETTEXT;\n\t\t\tcontinue;\n\t\t}\n\t\telse if (*str == '^' && !(flags & PFS_NOMARKUP))\n\t\t{\n\t\t\tif (str[1] >= '0' && str[1] <= '9')\n\t\t\t{\t//q3 colour codes\n\t\t\t\tif (ext & CON_RICHFORECOLOUR)\n\t\t\t\t\text = (COLOR_WHITE << CON_FGSHIFT) | (ext&~(CON_RICHFOREMASK|CON_RICHFORECOLOUR));\n\t\t\t\text = q3codemasks[str[1]-'0'] | (ext&~(CON_WHITEMASK|CON_HALFALPHA)); //change colour only.\n\t\t\t}\n\t\t\telse if (str[1] == '&') // extended code\n\t\t\t{\n\t\t\t\tif (isextendedcode(str[2]) && isextendedcode(str[3]))\n\t\t\t\t{\n\t\t\t\t\tif (ext & CON_RICHFORECOLOUR)\n\t\t\t\t\t\text = (COLOR_WHITE << CON_FGSHIFT) | (ext&~(CON_RICHFOREMASK|CON_RICHFORECOLOUR));\n\n\t\t\t\t\t// foreground char\n\t\t\t\t\tif (str[2] == '-') // default for FG\n\t\t\t\t\t\text = (COLOR_WHITE << CON_FGSHIFT) | (ext&~CON_FGMASK);\n\t\t\t\t\telse if (str[2] >= 'A')\n\t\t\t\t\t\text = ((str[2] - ('A' - 10)) << CON_FGSHIFT) | (ext&~CON_FGMASK);\n\t\t\t\t\telse\n\t\t\t\t\t\text = ((str[2] - '0') << CON_FGSHIFT) | (ext&~CON_FGMASK);\n\t\t\t\t\t// background char\n\t\t\t\t\tif (str[3] == '-') // default (clear) for BG\n\t\t\t\t\t\text &= ~CON_BGMASK & ~CON_NONCLEARBG;\n\t\t\t\t\telse if (str[3] >= 'A')\n\t\t\t\t\t\text = ((str[3] - ('A' - 10)) << CON_BGSHIFT) | (ext&~CON_BGMASK) | CON_NONCLEARBG;\n\t\t\t\t\telse\n\t\t\t\t\t\text = ((str[3] - '0') << CON_BGSHIFT) | (ext&~CON_BGMASK) | CON_NONCLEARBG;\n\n\t\t\t\t\tif (!keepmarkup)\n\t\t\t\t\t{\n\t\t\t\t\t\tstr += 4;\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t// else invalid code\n\t\t\t\tgoto messedup;\n\t\t\t}\n\t\t\telse if (str[1] == '[' && !linkstart)\n\t\t\t{\n\t\t\t\tif (keepmarkup)\n\t\t\t\t{\n\t\t\t\t\tif (!--outsize)\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t*out++ = '^' | CON_HIDDEN;\n\t\t\t\t}\n\t\t\t\tif (!--outsize)\n\t\t\t\t\tbreak;\n\n\t\t\t\t//preserved flags and reset to white. links must contain their own colours.\n\t\t\t\tlinkinitflags = ext;\n\t\t\t\text = COLOR_RED << CON_FGSHIFT;\n\t\t\t\tif (!(linkinitflags & CON_RICHFORECOLOUR))\n\t\t\t\t\text |= linkinitflags & (CON_NONCLEARBG|CON_HALFALPHA|CON_BGMASK);\n\t\t\t\tlinkstart = out;\n\t\t\t\t*out++ = '[';\n\n\t\t\t\t//never keep the markup\n\t\t\t\tlinkkeep = keepmarkup;\n\t\t\t\tkeepmarkup = false;\n\t\t\t\tstr+=2;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\telse if (str[1] == ']')\n\t\t\t{\n\t\t\t\tif (keepmarkup)\n\t\t\t\t{\n\t\t\t\t\tif (!--outsize)\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t*out++ = '^' | CON_HIDDEN;\n\t\t\t\t}\n\n\t\t\t\tif (!--outsize)\n\t\t\t\t\tbreak;\n\t\t\t\tif (linkstart)\n\t\t\t\t{\n\t\t\t\t\t*out++ = ']'|CON_HIDDEN|CON_LINKSPECIAL;\n\t\t\t\t\t\n\t\t\t\t\t//its a valid link, so we can hide it all now\n\t\t\t\t\t*linkstart++ |= CON_HIDDEN|CON_LINKSPECIAL;\t//leading [ is hidden\n\t\t\t\t\twhile(linkstart < out-1 && (*linkstart&CON_CHARMASK) != '\\\\')\t//link text is NOT hidden\n\t\t\t\t\t\tlinkstart++;\n\t\t\t\t\twhile(linkstart < out)\t//but the infostring behind it is, as well as the terminator\n\t\t\t\t\t\t*linkstart++ |= CON_HIDDEN;\n\n\t\t\t\t\t//reset colours to how they used to be\n\t\t\t\t\text = linkinitflags;\n\t\t\t\t\tlinkstart = NULL;\n\t\t\t\t\tkeepmarkup = linkkeep;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\t*out++ = ']'|CON_LINKSPECIAL;\n\n\t\t\t\t//never keep the markup\n\t\t\t\tstr+=2;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\telse if (str[1] == '`' && str[2] == 'u' && str[3] == '8' && str[4] == ':' && !keepmarkup)\n\t\t\t{\n\t\t\t\tint l;\n\t\t\t\tchar temp[1024];\n\t\t\t\tstr += 5;\n\t\t\t\twhile(*str)\n\t\t\t\t{\n\t\t\t\t\tl = 0;\n\t\t\t\t\twhile (*str && l < sizeof(temp)-32 && !(str[0] == '`' && str[1] == '='))\n\t\t\t\t\t\ttemp[l++] = *str++;\n\t\t\t\t\t//recurse\n\t\t\t\t\ttemp[l] = 0;\n\t\t\t\t\tl = COM_ParseFunString(ext, temp, out, outsize, PFS_FORCEUTF8) - out;\n\t\t\t\t\toutsize -= l;\n\t\t\t\t\tout += l;\n\t\t\t\t\tif (str[0] == '`' && str[1] == '=')\n\t\t\t\t\t{\n\t\t\t\t\t\tstr+=2;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\telse if (str[1] == 'b')\n\t\t\t{\n\t\t\t\text ^= CON_BLINKTEXT;\n\t\t\t}\n\t\t\telse if (str[1] == 'd')\n\t\t\t{\n\t\t\t\tif (linkstart)\n\t\t\t\t\text = COLOR_RED << CON_FGSHIFT;\n\t\t\t\telse\n\t\t\t\t\text = defaultflags;\n\t\t\t}\n\t\t\telse if (str[1] == 'm'||str[1] == 'a')\n\t\t\t\text ^= CON_2NDCHARSETTEXT;\n\t\t\telse if (str[1] == 'h')\n\t\t\t\text ^= CON_HALFALPHA;\n\t\t\telse if (str[1] == 's')\t//store on stack (it's great for names)\n\t\t\t{\n\t\t\t\tif (extstackdepth < sizeof(extstack)/sizeof(extstack[0]))\n\t\t\t\t{\n\t\t\t\t\textstack[extstackdepth] = ext;\n\t\t\t\t\textstackdepth++;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (str[1] == 'r')\t//restore from stack (it's great for names)\n\t\t\t{\n\t\t\t\tif (extstackdepth)\n\t\t\t\t{\n\t\t\t\t\textstackdepth--;\n\t\t\t\t\text = extstack[extstackdepth];\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (str[1] == 'U')\t//unicode (16bit) char ^Uxxxx\n\t\t\t{\n\t\t\t\tif (!keepmarkup)\n\t\t\t\t{\n\t\t\t\t\tuc = 0;\n\t\t\t\t\tuc |= dehex(str[2])<<12;\n\t\t\t\t\tuc |= dehex(str[3])<<8;\n\t\t\t\t\tuc |= dehex(str[4])<<4;\n\t\t\t\t\tuc |= dehex(str[5])<<0;\n\n\t\t\t\t\tif (!--outsize)\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t*out++ = uc | ext;\n\t\t\t\t\tstr += 6;\n\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (str[1] == '{')\t//unicode (Xbit) char ^{xxxx}\n\t\t\t{\n\t\t\t\tif (!keepmarkup)\n\t\t\t\t{\n\t\t\t\t\tint len;\n\t\t\t\t\tuc = 0;\n\t\t\t\t\tfor (len = 2; ishexcode(str[len]); len++)\n\t\t\t\t\t{\n\t\t\t\t\t\tuc <<= 4;\n\t\t\t\t\t\tuc |= dehex(str[len]);\n\t\t\t\t\t}\n\n\t\t\t\t\t//and eat the close too. oh god I hope its there.\n\t\t\t\t\tif (str[len] == '}')\n\t\t\t\t\t\tlen++;\n\n\t\t\t\t\tif (uc > 0x10ffff)\t//utf-16 imposes a limit on standard unicode codepoints (any encoding)\n\t\t\t\t\t\tuc = 0xfffd;\n\n\t\t\t\t\tif (!--outsize)\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tif (uc > 0xffff)\t//utf-16 imposes a limit on standard unicode codepoints (any encoding)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (!--outsize)\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t*out++ = uc>>16 | CON_LONGCHAR | (ext & CON_HIDDEN);\n\t\t\t\t\t\tuc &= 0xffff;\n\t\t\t\t\t}\n\t\t\t\t\t*out++ = uc | ext;\n\t\t\t\t\tstr += len;\n\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (str[1] == 'x')\t//RGB colours\n\t\t\t{\n\t\t\t\tif (ishexcode(str[2]) && ishexcode(str[3]) && ishexcode(str[4]))\n\t\t\t\t{\n\t\t\t\t\tint r, g, b;\n\t\t\t\t\tr = dehex(str[2]);\n\t\t\t\t\tg = dehex(str[3]);\n\t\t\t\t\tb = dehex(str[4]);\n\n\t\t\t\t\text = (ext & ~CON_RICHFOREMASK) | CON_RICHFORECOLOUR;\n\t\t\t\t\text |= r<<CON_RICHRSHIFT;\n\t\t\t\t\text |= g<<CON_RICHGSHIFT;\n\t\t\t\t\text |= b<<CON_RICHBSHIFT;\n\n\t\t\t\t\tif (!keepmarkup)\n\t\t\t\t\t{\n\t\t\t\t\t\tstr += 5;\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (str[1] == '^')\n\t\t\t{\n\t\t\t\tif (keepmarkup)\n\t\t\t\t{\n\t\t\t\t\tif (!--outsize)\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t*out++ = (unsigned char)(*str) | ext;\n\t\t\t\t}\n\t\t\t\tstr++;\n\n\t\t\t\tif (*str)\n\t\t\t\t\tgoto messedup;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tgoto messedup;\n\t\t\t}\n\n\t\t\tif (!keepmarkup)\n\t\t\t{\n\t\t\t\tstr+=2;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n#ifdef HAVE_LEGACY\n\t\telse if (*str == '&' && str[1] == 'c' && !(flags & PFS_NOMARKUP) && ezcompat_markup.ival)\n\t\t{\n\t\t\t// ezQuake color codes\n\n\t\t\tif (ishexcode(str[2]) && ishexcode(str[3]) && ishexcode(str[4]))\n\t\t\t{\n\t\t\t\tint r, g, b;\n\t\t\t\tr = dehex(str[2]);\n\t\t\t\tg = dehex(str[3]);\n\t\t\t\tb = dehex(str[4]);\n\n\t\t\t\text = (ext & ~CON_RICHFOREMASK) | CON_RICHFORECOLOUR;\n\t\t\t\text |= r<<CON_RICHRSHIFT;\n\t\t\t\text |= g<<CON_RICHGSHIFT;\n\t\t\t\text |= b<<CON_RICHBSHIFT;\n\n\t\t\t\tif (!keepmarkup)\n\t\t\t\t{\n\t\t\t\t\tstr += 5;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse if (*str == '&' && str[1] == 'r' && !(flags & PFS_NOMARKUP) && ezcompat_markup.ival)\n\t\t{\n\t\t\t//ezquake revert\n\t\t\text = (COLOR_WHITE << CON_FGSHIFT) | (ext&~(CON_RICHFOREMASK|CON_RICHFORECOLOUR));\n\t\t\tif (!keepmarkup)\n\t\t\t{\n\t\t\t\tstr+=2;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\t\telse if (str[0] == '=' && str[1] == '`' && str[2] == 'k' && str[3] == '8' && str[4] == ':' && !keepmarkup && ezcompat_markup.ival)\n\t\t{\n\t\t\t//ezquake compat: koi8 compat for crazy russian people.\n\t\t\t//we parse for compat but don't generate (they'll see utf-8 from us).\n\t\t\t//this code can just recurse. saves affecting the rest of the code with weird encodings.\n\t\t\tint l;\n\t\t\tchar temp[1024];\n\t\t\tstr += 5;\n\t\t\twhile(*str)\n\t\t\t{\n\t\t\t\tl = 0;\n\t\t\t\twhile (*str && l < sizeof(temp)-32 && !(str[0] == '`' && str[1] == '='))\n\t\t\t\t\tl += utf8_encode(temp+l, koi2wc(*str++), sizeof(temp)-1);\n\t\t\t\t//recurse\n\t\t\t\ttemp[l] = 0;\n\t\t\t\tl = COM_ParseFunString(ext, temp, out, outsize, PFS_FORCEUTF8) - out;\n\t\t\t\toutsize -= l;\n\t\t\t\tout += l;\n\t\t\t\tif (str[0] == '`' && str[1] == '=')\n\t\t\t\t{\n\t\t\t\t\tstr+=2;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n#endif\n\n/*\n\t\telse if ((str[0] == 'h' && str[1] == 't' && str[2] == 't' && str[3] == 'p' && str[4] == ':' && !linkstart && !(flags & (PFS_NOMARKUP|PFS_KEEPMARKUP))) ||\n\t\t\t\t(str[0] == 'h' && str[1] == 't' && str[2] == 't' && str[3] == 'p' && str[4] == 's' && str[5] == ':' && !linkstart && !(flags & (PFS_NOMARKUP|PFS_KEEPMARKUP))))\n\t\t{\n\t\t\t//this code can just recurse. saves affecting the rest of the code with weird encodings.\n\t\t\tint l;\n\t\t\tchar temp[1024];\n\t\t\tconchar_t *ls, *le;\n\t\t\tl = 0;\n\t\t\twhile (*str && l < sizeof(temp)-32 && (\n\t\t\t\t\t(*str >= 'a' && *str <= 'z') ||\n\t\t\t\t\t(*str >= 'A' && *str <= 'Z') ||\n\t\t\t\t\t(*str >= '0' && *str <= '9') ||\n\t\t\t\t\t*str == '.' || *str == '/' || *str == '&' || *str == '=' || *str == '_' || *str == '%' || *str == '?' || *str == ':'))\n\t\t\t\tl += utf8_encode(temp+l, *str++, sizeof(temp)-1);\n\t\t\t//recurse\n\t\t\ttemp[l] = 0;\n\n\t\t\tif (!--outsize)\n\t\t\t\tbreak;\n\t\t\t*out++ = CON_LINKSTART;\n\t\t\tls = out;\n\t\t\tl = COM_ParseFunString(COLOR_BLUE << CON_FGSHIFT, temp, out, outsize, PFS_FORCEUTF8|PFS_NOMARKUP) - out;\n\t\t\toutsize -= l;\n\t\t\tout += l;\n\t\t\tle = out;\n\n\t\t\t*out++ = '\\\\' | CON_HIDDEN;\n\t\t\t*out++ = 'u' | CON_HIDDEN;\n\t\t\t*out++ = 'r' | CON_HIDDEN;\n\t\t\t*out++ = 'l' | CON_HIDDEN;\n\t\t\t*out++ = '\\\\' | CON_HIDDEN;\n\t\t\twhile (ls < le)\n\t\t\t\t*out++ = (*ls++ & CON_CHARMASK) | CON_HIDDEN;\n\t\t\t*out++ = CON_LINKEND;\n\n\t\t\tif (!--outsize)\n\t\t\t\tbreak;\n\t\t\t*out++ = CON_LINKEND;\n\t\t\tcontinue;\n\t\t}\n*/\nmessedup:\n\t\tif (!--outsize)\n\t\t\tbreak;\n\t\tuc = (unsigned char)(*str++);\n\t\tif (utf8)\n\t\t{\n\t\t\t//utf8/iso8859-1 has it easy.\n\t\t\t*out++ = uc | ext;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (uc == '\\n' || uc == '\\r' || uc == '\\t' || uc == '\\v' || uc == ' ')\n\t\t\t\t*out++ = uc | ext;\n\t\t\telse if (uc >= 32 && uc < 127)\n\t\t\t\t*out++ = uc | ext;\n\t\t\telse if (uc >= 0x80+32 && uc <= 0xff)\t//anything using high chars is ascii, with the second charset\n\t\t\t\t*out++ = ((uc&127) | ext) | CON_2NDCHARSETTEXT;\n\t\t\telse\t//(other) control chars are regular printables in quake, and are not ascii. These ALWAYS use the bitmap/fallback font.\n\t\t\t\t*out++ = uc | ext | 0xe000;\n\t\t}\n\t}\n\t*out = 0;\n\n\tCOM_BiDi_Parse(oldout, out - oldout);\n\treturn out;\n}\n\n//remaps conchar_t character values to something valid in unicode, such that it is likely to be printable with standard char sets.\n//unicode-to-ascii is not provided. you're expected to utf-8 the result or something.\n//does not handle colour codes or hidden chars. add your own escape sequences if you need that.\n//does not guarentee removal of control codes if eg the code was specified as an explicit unicode char.\nunsigned int COM_DeQuake(unsigned int chr)\n{\n\t/*only this range are quake chars*/\n\tif (chr >= 0xe000 && chr < 0xe100)\n\t{\n\t\tchr &= 0xff;\n\t\tif (chr >= 146 && chr < 156)\n\t\t\tchr = chr - 146 + '0';\n\t\tif (chr >= 0x12 && chr <= 0x1b)\n\t\t\tchr = chr - 0x12 + '0';\n\t\tif (chr == 143)\n\t\t\tchr = '.';\n\t\tif (chr == 128 || chr == 129 || chr == 130 || chr == 157 || chr == 158 || chr == 159)\n\t\t\tchr = '-';\n\t\tif (chr >= 128)\n\t\t\tchr -= 128;\n\t\tif (chr == 16)\n\t\t\tchr = '[';\n\t\tif (chr == 17)\n\t\t\tchr = ']';\n\t\tif (chr == 0x1c)\n\t\t\tchr = 249;\n\t}\n\t/*this range contains pictograms*/\n\tif (chr >= 0xe100 && chr < 0xe200)\n\t{\n\t\tchr = '?';\n\t}\n\treturn chr;\n}\n\n//============================================================================\n\n#define TOKENSIZE sizeof(com_token)\nchar\t\tcom_token[TOKENSIZE];\nint\t\tcom_argc;\nconst char\t**com_argv;\n\ncom_tokentype_t com_tokentype;\n\n\n/*\n==============\nCOM_Parse\n\nParse a token out of a string\n==============\n*/\n#ifndef COM_Parse\nchar *COM_Parse (const char *data)\n{\n\tint\t\tc;\n\tint\t\tlen;\n\n\tif (out == com_token)\n\t\tCOM_AssertMainThread(\"COM_ParseOut: com_token\");\n\n\tlen = 0;\n\tcom_token[0] = 0;\n\n\tif (!data)\n\t\treturn NULL;\n\n// skip whitespace\nskipwhite:\n\twhile ( (c = *data) <= ' ')\n\t{\n\t\tif (c == 0)\n\t\t\treturn NULL;\t\t\t// end of file;\n\t\tdata++;\n\t}\n\n// skip // comments\n\tif (c=='/')\n\t{\n\t\tif (data[1] == '/')\n\t\t{\n\t\t\twhile (*data && *data != '\\n')\n\t\t\t\tdata++;\n\t\t\tgoto skipwhite;\n\t\t}\n\t}\n\n\n// handle quoted strings specially\n\tif (c == '\\\"')\n\t{\n\t\tdata++;\n\t\twhile (1)\n\t\t{\n\t\t\tif (len >= TOKENSIZE-1)\n\t\t\t\treturn (char*)data;\n\n\t\t\tc = *data++;\n\t\t\tif (c=='\\\"' || !c)\n\t\t\t{\n\t\t\t\tcom_token[len] = 0;\n\t\t\t\treturn (char*)data;\n\t\t\t}\n\t\t\tcom_token[len] = c;\n\t\t\tlen++;\n\t\t}\n\t}\n\n// parse a regular word\n\tdo\n\t{\n\t\tif (len >= TOKENSIZE-1)\n\t\t\treturn (char*)data;\n\n\t\tcom_token[len] = c;\n\t\tdata++;\n\t\tlen++;\n\t\tc = *data;\n\t} while (c>32);\n\n\tcom_token[len] = 0;\n\treturn (char*)data;\n}\n#endif\n\n//semi-colon delimited tokens\nchar *COM_ParseStringSet (const char *data, char *out, size_t outsize)\n{\n\tint\tc;\n\tint\tlen;\n\n\tif (out == com_token)\n\t\tCOM_AssertMainThread(\"COM_ParseOut: com_token\");\n\n\tlen = 0;\n\tout[0] = 0;\n\n\tif (!data)\n\t\treturn NULL;\n\n// skip whitespace and semicolons\n\twhile ( (c = *data) <= ' ' || c == ';' )\n\t{\n\t\tif (c == 0)\n\t\t\treturn NULL;\t\t\t// end of file;\n\t\tdata++;\n\t}\n\n\tif (*data == '\\\"')\n\t{\n\t\treturn COM_ParseCString(data, out, outsize, NULL);\n\t}\n\n// parse a regular word\n\tdo\n\t{\n\t\tif (len >= outsize-1)\n\t\t{\n\t\t\tout[len] = 0;\n\t\t\treturn (char*)data;\n\t\t}\n\n\t\tout[len] = c;\n\t\tdata++;\n\t\tlen++;\n\t\tc = *(unsigned char*)data;\n\t} while (c>32 && c != ';');\n\n\tout[len] = 0;\n\treturn (char*)data;\n}\n\n\nchar *COM_ParseType (const char *data, char *out, size_t outlen, com_tokentype_t *toktype)\n{\n\tint\t\tc;\n\tint\t\tlen;\n\n\tif (out == com_token)\n\t\tCOM_AssertMainThread(\"COM_ParseOut: com_token\");\n\n\tlen = 0;\n\tout[0] = 0;\n\tif (toktype)\n\t\t*toktype = TTP_EOF;\n\n\tif (!data)\n\t\treturn NULL;\n\n// skip whitespace\nskipwhite:\n\twhile ( (c = *data) <= ' ')\n\t{\n\t\tif (c == 0)\n\t\t\treturn NULL;\t\t\t// end of file;\n\t\tdata++;\n\t}\n\n// skip // comments\n\tif (c=='/')\n\t{\n\t\tif (data[1] == '/')\n\t\t{\n\t\t\twhile (*data && *data != '\\n')\n\t\t\t\tdata++;\n\t\t\tgoto skipwhite;\n\t\t}\n\t}\n\n//skip / * comments\n\tif (c == '/' && data[1] == '*')\n\t{\n\t\tdata+=2;\n\t\twhile(*data)\n\t\t{\n\t\t\tif (*data == '*' && data[1] == '/')\n\t\t\t{\n\t\t\t\tdata+=2;\n\t\t\t\tgoto skipwhite;\n\t\t\t}\n\t\t\tdata++;\n\t\t}\n\t\tgoto skipwhite;\n\t}\n\n// handle quoted strings specially\n\tif (c == '\\\"')\n\t{\n\t\tif (toktype)\n\t\t\t*toktype = TTP_STRING;\n\n\t\tdata++;\n\t\twhile (1)\n\t\t{\n\t\t\tif (len >= outlen-1)\n\t\t\t{\n\t\t\t\tout[len] = 0;\n\t\t\t\treturn (char*)data;\n\t\t\t}\n\n\t\t\tc = *data++;\n\t\t\tif (c=='\\\"' || !c)\n\t\t\t{\n\t\t\t\tout[len] = 0;\n\t\t\t\treturn (char*)data;\n\t\t\t}\n\t\t\tout[len] = c;\n\t\t\tlen++;\n\t\t}\n\t}\n\n// parse a regular word\n\tif (toktype)\n\t\t*toktype = TTP_RAWTOKEN;\n\tdo\n\t{\n\t\tif (len >= outlen-1)\n\t\t{\n\t\t\tout[len] = 0;\n\t\t\treturn (char*)data;\n\t\t}\n\n\t\tout[len] = c;\n\t\tdata++;\n\t\tlen++;\n\t\tc = *data;\n\t} while (c>32);\n\n\tout[len] = 0;\n\treturn (char*)data;\n}\n\n//same as COM_Parse, but parses two quotes next to each other as a single quote as part of the string\nchar *COM_StringParse (const char *data, char *token, unsigned int tokenlen, qboolean expandmacros, qboolean qctokenize)\n{\n#ifdef HAVE_LEGACY\n\textern cvar_t dpcompat_console;\n#endif\n\tint\t\tc;\n\tint\t\tlen;\n\tchar *s;\n\n\tif (token == com_token)\n\t\tCOM_AssertMainThread(\"COM_StringParse: com_token\");\n\n\tlen = 0;\n\ttoken[0] = 0;\n\n\tif (!data)\n\t\treturn NULL;\n\n// skip whitespace\nskipwhite:\n\twhile ( (c = *data), (unsigned)c <= ' ' && c != '\\n')\n\t{\n\t\tif (c == 0)\n\t\t\treturn NULL;\t\t\t// end of file;\n\t\tdata++;\n\t}\n\tif (c == '\\n')\n\t{\n\t\ttoken[len++] = c;\n\t\ttoken[len] = 0;\n\t\treturn (char*)data+1;\n\t}\n\n// skip // comments\n\tif (c=='/')\n\t{\n\t\tif (data[1] == '/')\n\t\t{\n\t\t\twhile (*data && *data != '\\n')\n\t\t\t\tdata++;\n\t\t\tgoto skipwhite;\n\t\t}\n\t}\n\n//skip / * comments\n\tif (c == '/' && data[1] == '*' && !qctokenize)\n\t{\n\t\tdata+=2;\n\t\twhile(*data)\n\t\t{\n\t\t\tif (*data == '*' && data[1] == '/')\n\t\t\t{\n\t\t\t\tdata+=2;\n\t\t\t\tgoto skipwhite;\n\t\t\t}\n\t\t\tdata++;\n\t\t}\n\t\tgoto skipwhite;\n\t}\n\n\tif (c == '\\\\' && data[1] == '\\\"')\n\t{\n\t\treturn COM_ParseCString(data+1, token, tokenlen, NULL);\n\t}\n\n// handle quoted strings specially\n\tif (c == '\\\"')\n\t{\n\t\tdata++;\n#ifdef HAVE_LEGACY\n\t\tif (dpcompat_console.ival)\n\t\t{\n\t\t\twhile (1)\n\t\t\t{\n\t\t\t\tif (len >= tokenlen-1)\n\t\t\t\t{\n\t\t\t\t\ttoken[len] = '\\0';\n\t\t\t\t\treturn (char*)data;\n\t\t\t\t}\n\n\t\t\t\tc = *data++;\n\t\t\t\tif (c=='\\\\' && (*data == '\\\"' || *data == '\\\\'))\n\t\t\t\t\tc = *data++;\t//eat limited escaping inside strings.\n\t\t\t\telse if (c=='\\\"')\n\t\t\t\t{\n\t\t\t\t\ttoken[len] = 0;\n\t\t\t\t\treturn (char*)data;\n\t\t\t\t}\n\t\t\t\telse if (!c)\n\t\t\t\t{\n\t\t\t\t\ttoken[len] = 0;\n\t\t\t\t\treturn (char*)data-1;\n\t\t\t\t}\n\t\t\t\ttoken[len] = c;\n\t\t\t\tlen++;\n\t\t\t}\n\t\t}\n\t\telse\n#endif\n\t\t{\n\t\t\twhile (1)\n\t\t\t{\n\t\t\t\tif (len >= tokenlen-1)\n\t\t\t\t{\n\t\t\t\t\ttoken[len] = '\\0';\n\t\t\t\t\treturn (char*)data;\n\t\t\t\t}\n\n\t\t\t\tc = *data++;\n\t\t\t\tif (c=='\\\"')\n\t\t\t\t{\n\t\t\t\t\tc = *(data);\n\t\t\t\t\tif (c!='\\\"')\n\t\t\t\t\t{\n\t\t\t\t\t\ttoken[len] = 0;\n\t\t\t\t\t\treturn (char*)data;\n\t\t\t\t\t}\n\t\t\t\t\tdata++;\n\t\t\t\t}\n\t\t\t\tif (!c)\n\t\t\t\t{\n\t\t\t\t\ttoken[len] = 0;\n\t\t\t\t\treturn (char*)data-1;\n\t\t\t\t}\n\t\t\t\ttoken[len] = c;\n\t\t\t\tlen++;\n\t\t\t}\n\t\t}\n\t}\n\n\t// handle quoted strings specially\n\tif (c == '\\'' && qctokenize)\n\t{\n\t\tdata++;\n\t\twhile (1)\n\t\t{\n\t\t\tif (len >= tokenlen-1)\n\t\t\t{\n\t\t\t\ttoken[len] = '\\0';\n\t\t\t\treturn (char*)data;\n\t\t\t}\n\n\n\t\t\tc = *data++;\n\t\t\tif (c=='\\'')\n\t\t\t{\n\t\t\t\tc = *(data);\n\t\t\t\tif (c!='\\'')\n\t\t\t\t{\n\t\t\t\t\ttoken[len] = 0;\n\t\t\t\t\treturn (char*)data;\n\t\t\t\t}\n\t\t\t\twhile (c=='\\'')\n\t\t\t\t{\n\t\t\t\t\ttoken[len] = c;\n\t\t\t\t\tlen++;\n\t\t\t\t\tdata++;\n\t\t\t\t\tc = *(data+1);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!c)\n\t\t\t{\n\t\t\t\ttoken[len] = 0;\n\t\t\t\treturn (char*)data;\n\t\t\t}\n\t\t\ttoken[len] = c;\n\t\t\tlen++;\n\t\t}\n\t}\n\n\tif (qctokenize && (c == '\\n' || c == '{' || c == '}' || c == ')' || c == '(' || c == ']' || c == '[' || c == '\\'' || c == ':' || c == ',' || c == ';'))\n\t{\n\t\t// single character\n\t\ttoken[len++] = c;\n\t\ttoken[len] = 0;\n\t\treturn (char*)data+1;\n\t}\n\n// parse a regular word\n\tdo\n\t{\n\t\tif (len >= tokenlen-1)\n\t\t{\n\t\t\ttoken[len] = '\\0';\n\t\t\treturn (char*)data;\n\t\t}\n\n\t\ttoken[len] = c;\n\t\tdata++;\n\t\tlen++;\n\t\tc = *data;\n\t} while ((unsigned)c>32 && !(qctokenize && (c == '\\n' || c == '{' || c == '}' || c == ')' || c == '(' || c == ']' || c == '[' || c == '\\'' || c == ':' || c == ',' || c == ';')));\n\n\ttoken[len] = 0;\n\n\tif (!expandmacros)\n\t\treturn (char*)data;\n\n\t//now we check for macros.\n\tfor (s = token, c= 0; c < len; c++, s++)\t//this isn't a quoted token by the way.\n\t{\n\t\tif (*s == '$')\n\t\t{\n\t\t\tcvar_t *macro;\n\t\t\tchar name[64];\n\t\t\tint i;\n\n\t\t\tfor (i = 1; i < sizeof(name); i++)\n\t\t\t{\n\t\t\t\tif (((unsigned char*)s)[i] <= ' ' || s[i] == '$')\n\t\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tQ_strncpyz(name, s+1, i);\n\t\t\ti-=1;\n\n\t\t\tmacro = Cvar_FindVar(name);\n\t\t\tif (macro)\t//got one...\n\t\t\t{\n\t\t\t\tif (len+strlen(macro->string)-(i+1) >= tokenlen-1)\t//give up.\n\t\t\t\t{\n\t\t\t\t\ttoken[len] = '\\0';\n\t\t\t\t\treturn (char*)data;\n\t\t\t\t}\n\t\t\t\tmemmove(s+strlen(macro->string), s+i+1, len-c-i);\n\t\t\t\tmemcpy(s, macro->string, strlen(macro->string));\n\t\t\t\ts+=strlen(macro->string);\n\t\t\t\tlen+=strlen(macro->string)-(i+1);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn (char*)data;\n}\n\n#define DEFAULT_PUNCTUATION \"(,{})(\\':;=!><&|+\"\nchar *COM_ParseTokenOut (const char *data, const char *punctuation, char *token, size_t tokenlen, com_tokentype_t *tokentype)\n{\n\tint\t\tc;\n\tsize_t\tlen;\n\n\tif (!punctuation)\n\t\tpunctuation = DEFAULT_PUNCTUATION;\n\n\tif (token == com_token || tokentype == &com_tokentype)\n\t\tCOM_AssertMainThread(\"COM_ParseTokenOut: com_token\");\n\n\tlen = 0;\n\ttoken[0] = 0;\n\n\tif (!data)\n\t{\n\t\tif (tokentype)\n\t\t\t*tokentype = TTP_EOF;\n\t\treturn NULL;\n\t}\n\n// skip whitespace\n//line endings count as whitespace only if we can report the token type.\nskipwhite:\n\twhile ( (c = *(unsigned char*)data) <= ' ' && ((c != '\\r' && c != '\\n') || !tokentype))\n\t{\n\t\tif (c == 0)\n\t\t{\n\t\t\tif (tokentype)\n\t\t\t\t*tokentype = TTP_EOF;\n\t\t\treturn NULL;\t\t\t// end of file;\n\t\t}\n\t\tdata++;\n\t}\n\n\t//if windows, ignore the \\r.\n\tif (c == '\\r' && data[1] == '\\n')\n\t\tc = *(unsigned char*)data++;\n\n\tif (c == '\\r' || c == '\\n')\n\t{\n\t\tif (tokentype)\n\t\t\t*tokentype = TTP_LINEENDING;\n\t\ttoken[0] = '\\n';\n\t\ttoken[1] = '\\0';\n\t\tdata++;\n\t\treturn (char*)data;\n\t}\n\n// skip comments\n\tif (c=='/')\n\t{\n\t\tif (data[1] == '/')\n\t\t{\t// style comments\n\t\t\twhile (*data && *data != '\\n')\n\t\t\t\tdata++;\n\t\t\tgoto skipwhite;\n\t\t}\n\t\telse if (data[1] == '*')\n\t\t{\t/* style comments */\n\t\t\tdata+=2;\n\t\t\twhile (*data && (*data != '*' || data[1] != '/'))\n\t\t\t\tdata++;\n\t\t\tif (*data)\n\t\t\t\tdata++;\n\t\t\tif (*data)\n\t\t\t\tdata++;\n\t\t\tgoto skipwhite;\n\t\t}\n\t}\n\n// handle quoted strings specially\n\tif (c == '\\\"')\n\t{\n\t\tif (tokentype)\n\t\t\t*tokentype = TTP_STRING;\n\t\tdata++;\n\t\twhile (1)\n\t\t{\n\t\t\tif (len >= tokenlen-1)\n\t\t\t{\n\t\t\t\ttoken[len] = '\\0';\n\t\t\t\treturn (char*)data;\n\t\t\t}\n\t\t\tc = *data++;\n\t\t\tif (c=='\\\"' || !c)\n\t\t\t{\n\t\t\t\ttoken[len] = 0;\n\t\t\t\treturn (char*)data;\n\t\t\t}\n\t\t\ttoken[len] = c;\n\t\t\tlen++;\n\t\t}\n\t}\n\tif (c == '\\\\' && data[1] == '\\\"')\n\t{\n\t\tif (tokentype)\n\t\t\t*tokentype = TTP_STRING;\n\t\treturn COM_ParseCString(data+1, token, tokenlen, NULL);\n\t}\n\n// parse single characters\n\tif (strchr(punctuation, c))\n\t{\n\t\ttoken[len] = c;\n\t\tlen++;\n\t\ttoken[len] = 0;\n\t\tif (tokentype)\n\t\t\t*tokentype = TTP_PUNCTUATION;\n\t\treturn (char*)(data+1);\n\t}\n\n// parse a regular word\n\tdo\n\t{\n\t\tif (len >= tokenlen-1)\n\t\t\tbreak;\n\t\ttoken[len] = c;\n\t\tdata++;\n\t\tlen++;\n\t\tc = *data;\n\t\tif (strchr(punctuation, c))\n\t\t\tbreak;\n\t} while (c>32);\n\n\ttoken[len] = 0;\n\tif (tokentype)\n\t\t*tokentype = TTP_RAWTOKEN;\n\treturn (char*)data;\n}\n\n//escape a string so that COM_Parse will give the same string.\n//maximum expansion is strlen(string)*2+4 (includes null terminator)\nconst char *COM_QuotedString(const char *string, char *buf, int buflen, qboolean omitquotes)\n{\n#ifdef HAVE_LEGACY\n\textern cvar_t dpcompat_console;\n#else\n\tstatic const cvar_t dpcompat_console = {0};\n#endif\n\tconst char *result = buf;\n\tif (strchr(string, '\\r') || strchr(string, '\\n') || (!dpcompat_console.ival && strchr(string, '\\\"')))\n\t{\t//strings of the form \\\"foo\" can contain c-style escapes, including for newlines etc.\n\t\t//it might be fancy to ALWAYS escape non-ascii chars too, but mneh\n\t\tif (!omitquotes)\n\t\t{\n\t\t\t*buf++ = '\\\\';\t//prefix so the reader knows its a quoted string.\n\t\t\t*buf++ = '\\\"';\t//opening quote\n\t\t\tbuflen -= 4;\n\t\t}\n\t\telse\n\t\t\tbuflen -= 1;\n\t\twhile(*string && buflen >= 2)\n\t\t{\n\t\t\tswitch(*string)\n\t\t\t{\n\t\t\tcase '\\n':\n\t\t\t\t*buf++ = '\\\\';\n\t\t\t\t*buf++ = 'n';\n\t\t\t\tbreak;\n\t\t\tcase '\\r':\n\t\t\t\t*buf++ = '\\\\';\n\t\t\t\t*buf++ = 'r';\n\t\t\t\tbreak;\n\t\t\tcase '\\t':\n\t\t\t\t*buf++ = '\\\\';\n\t\t\t\t*buf++ = 't';\n\t\t\t\tbreak;\n\t\t\tcase '\\'':\n\t\t\t\t*buf++ = '\\\\';\n\t\t\t\t*buf++ = '\\'';\n\t\t\t\tbreak;\n\t\t\tcase '\\\"':\n\t\t\t\t*buf++ = '\\\\';\n\t\t\t\t*buf++ = '\\\"';\n\t\t\t\tbreak;\n\t\t\tcase '\\\\':\n\t\t\t\t*buf++ = '\\\\';\n\t\t\t\t*buf++ = '\\\\';\n\t\t\t\tbreak;\n\t\t\tcase '$':\n\t\t\t\t*buf++ = '\\\\';\n\t\t\t\t*buf++ = '$';\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\t*buf++ = *string++;\n\t\t\t\tbuflen--;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tbuflen -= 2;\n\t\t\tstring++;\n\t\t}\n\t\tif (!omitquotes)\n\t\t\t*buf++ = '\\\"';\t//closing quote\n\t\t*buf++ = 0;\n\t\treturn result;\n\t}\n\telse\n\t{\n\t\tif (!omitquotes)\n\t\t{\n\t\t\t*buf++ = '\\\"';\t//opening quote\n\t\t\tbuflen -= 3;\n\t\t}\n\t\telse\n\t\t\tbuflen -= 1;\n\t\tif (dpcompat_console.ival)\n\t\t{\t//dp escapes \\\\ and \\\", but nothing else.\n\t\t\t//so no new-lines etc\n\t\t\twhile(*string && buflen >= 2)\n\t\t\t{\n\t\t\t\tif (*string == '\\\\' || *string == '\\\"')\n\t\t\t\t{\n\t\t\t\t\t*buf++ = '\\\\';\n\t\t\t\t\tbuflen--;\n\t\t\t\t}\n\t\t\t\t*buf++ = *string++;\n\t\t\t\tbuflen--;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\t//vanilla quake's console doesn't support any escapes.\n\t\t\twhile(*string && buflen >= 1)\n\t\t\t{\n\t\t\t\t*buf++ = *string++;\n\t\t\t\tbuflen--;\n\t\t\t}\n\t\t}\n\t\tif (!omitquotes)\n\t\t\t*buf++ = '\\\"';\t//closing quote\n\t\t*buf++ = 0;\n\t\treturn result;\n\t}\n}\n\nchar *COM_ParseCString (const char *data, char *token, size_t sizeoftoken, size_t *lengthwritten)\n{\n\tint\t\tc;\n\tsize_t\t\tlen;\n\n\tlen = 0;\n\ttoken[0] = 0;\n\n\tif (token == com_token)\n\t\tCOM_AssertMainThread(\"COM_ParseCString: com_token\");\n\n\tif (lengthwritten)\n\t\t*lengthwritten = 0;\n\n\tif (!data)\n\t\treturn NULL;\n\n// skip whitespace\nskipwhite:\n\twhile ( (c = *data) <= ' ')\n\t{\n\t\tif (c == 0)\n\t\t\treturn NULL;\t\t\t// end of file;\n\t\tdata++;\n\t}\n\n// skip // comments\n\tif (c=='/')\n\t{\n\t\tif (data[1] == '/')\n\t\t{\n\t\t\twhile (*data && *data != '\\n')\n\t\t\t\tdata++;\n\t\t\tgoto skipwhite;\n\t\t}\n\t}\n\n\n// handle quoted strings specially\n\tif (c == '\\\"')\n\t{\n\t\tdata++;\n\t\twhile (1)\n\t\t{\n\t\t\tif (len >= sizeoftoken-2)\n\t\t\t{\n\t\t\t\ttoken[len] = '\\0';\n\t\t\t\tif (lengthwritten)\n\t\t\t\t\t*lengthwritten = len;\n\t\t\t\treturn (char*)data;\n\t\t\t}\n\n\t\t\tc = *data++;\n\t\t\tif (!c)\n\t\t\t{\n\t\t\t\ttoken[len] = 0;\n\t\t\t\tif (lengthwritten)\n\t\t\t\t\t*lengthwritten = len;\n\t\t\t\treturn (char*)data-1;\n\t\t\t}\n\t\t\tif (c == '\\\\')\n\t\t\t{\n\t\t\t\tc = *data++;\n\t\t\t\tswitch(c)\n\t\t\t\t{\n\t\t\t\tcase '\\r':\n\t\t\t\t\tif (*data == '\\n')\n\t\t\t\t\t\tdata++;\n\t\t\t\tcase '\\n':\n\t\t\t\t\tcontinue;\n\t\t\t\tcase 'n':\n\t\t\t\t\tc = '\\n';\n\t\t\t\t\tbreak;\n\t\t\t\tcase 't':\n\t\t\t\t\tc = '\\t';\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'r':\n\t\t\t\t\tc = '\\r';\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'x':\n\t\t\t\t\tc = 0;\n\t\t\t\t\tif (ishexcode(*data))\n\t\t\t\t\t\tc |= dehex(*data++);\n\t\t\t\t\tif (ishexcode(*data))\n\t\t\t\t\t{\n\t\t\t\t\t\tc <<= 4;\n\t\t\t\t\t\tc |= dehex(*data++);\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase '$':\n\t\t\t\tcase '\\\\':\n\t\t\t\tcase '\\'':\n\t\t\t\t\tbreak;\n\t\t\t\tcase '\"':\n\t\t\t\t\tc = '\"';\n\t\t\t\t\ttoken[len] = c;\n\t\t\t\t\tlen++;\n\t\t\t\t\tcontinue;\n\t\t\t\tdefault:\n\t\t\t\t\tc = '?';\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (c=='\\\"' || !c)\n\t\t\t{\n\t\t\t\ttoken[len] = 0;\n\t\t\t\tif (lengthwritten)\n\t\t\t\t\t*lengthwritten = len;\n\t\t\t\treturn (char*)data;\n\t\t\t}\n\t\t\ttoken[len] = c;\n\t\t\tlen++;\n\t\t}\n\t}\n\n// parse a regular word\n\tdo\n\t{\n\t\tif (len >= sizeoftoken-1)\n\t\t\tbreak;\n\t\ttoken[len] = c;\n\t\tdata++;\n\t\tlen++;\n\t\tc = *data;\n\t} while (c>32);\n\n\ttoken[len] = 0;\n\tif (lengthwritten)\n\t\t*lengthwritten = len;\n\treturn (char*)data;\n}\n\n\n/*\n================\nCOM_CheckParm\n\nReturns the position (1 to argc-1) in the program's argument list\nwhere the given parameter apears, or 0 if not present\n================\n*/\n\nint COM_CheckNextParm (const char *parm, int last)\n{\n\tint i = last+1;\n\n\tfor ( ; i<com_argc ; i++)\n\t{\n\t\tif (!com_argv[i])\n\t\t\tcontinue;\t\t// NEXTSTEP sometimes clears appkit vars.\n\t\tif (!Q_strcmp (parm,com_argv[i]))\n\t\t\treturn i;\n\t}\n\n\treturn 0;\n}\n\nint COM_CheckParm (const char *parm)\n{\n\treturn COM_CheckNextParm(parm, 0);\n}\n\n/*\n===============\nCOM_ParsePlusSets\n\nLooks for +set blah blah on the commandline, and creates cvars so that engine\nfunctions may use the cvar before anything's loaded.\nThis isn't really needed, but might make some thing nicer.\n===============\n*/\nvoid COM_ParsePlusSets (qboolean docbuf)\n{\n\tint i;\n\tint c;\n\tfor (i=1 ; i<com_argc-2 ; i++)\n\t{\n\t\tif (!com_argv[i])\n\t\t\tcontinue;\t\t// NEXTSTEP sometimes clears appkit vars.\n\t\tfor (c = 1; i+c < com_argc && com_argv[i+c] && *com_argv[i+c] != '-' && *com_argv[i+c] != '+'; c++)\n\t\t\t;\n\n\t\tif (docbuf)\n\t\t{\n\t\t\tif (c == 3 && (!strcmp(com_argv[i], \"+set\") || !strcmp(com_argv[i], \"+seta\") ||\n\t\t\t\t!strcmp(com_argv[i], \"-set\") || !strcmp(com_argv[i], \"-seta\")))\n\t\t\t{\n\t\t\t\tchar buf[8192];\n\t\t\t\tCbuf_AddText(com_argv[i]+1, RESTRICT_LOCAL);\n\t\t\t\tCbuf_AddText(\" \", RESTRICT_LOCAL);\n\t\t\t\tCbuf_AddText(COM_QuotedString(com_argv[i+1], buf, sizeof(buf), false), RESTRICT_LOCAL);\n\t\t\t\tCbuf_AddText(\" \", RESTRICT_LOCAL);\n\t\t\t\tCbuf_AddText(COM_QuotedString(com_argv[i+2], buf, sizeof(buf), false), RESTRICT_LOCAL);\n\t\t\t\tCbuf_AddText(\"\\n\", RESTRICT_LOCAL);\n\t\t\t}\n\t\t\telse if (c == 2 && !strcmp(com_argv[i], \"-exec\"))\n\t\t\t{\n\t\t\t\tchar buf[8192];\n\t\t\t\tCbuf_AddText(com_argv[i]+1, RESTRICT_LOCAL);\n\t\t\t\tCbuf_AddText(\" \", RESTRICT_LOCAL);\n\t\t\t\tCbuf_AddText(COM_QuotedString(com_argv[i+1], buf, sizeof(buf), false), RESTRICT_LOCAL);\n\t\t\t\tCbuf_AddText(\"\\n\", RESTRICT_LOCAL);\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (c == 3 && (!strcmp(com_argv[i], \"+set\") || !strcmp(com_argv[i], \"+seta\")))\n\t\t\t{\n#if defined(Q2CLIENT) || defined(Q2SERVER)\n\t\t\t\tif (!strcmp(\"basedir\", com_argv[i+1]))\n\t\t\t\t\thost_parms.basedir = com_argv[i+2];\n\t\t\t\telse\n#endif\n\t\t\t\t\tCvar_Get(com_argv[i+1], com_argv[i+2], (!strcmp(com_argv[i], \"+seta\"))?CVAR_ARCHIVE:0, \"Cvars set on commandline\");\n\t\t\t}\n\t\t}\n\t\ti += c-1;\n\t}\n}\n\nvoid Cvar_DefaultFree(char *str);\n/*\n================\nCOM_CheckRegistered\n\nLooks for the pop.txt file and verifies it.\nSets the \"registered\" cvar.\nImmediately exits out if an alternate game was attempted to be started without\nbeing registered.\n================\n*/\nvoid COM_CheckRegistered (void)\n{\n\tchar *newdef;\n\tvfsfile_t\t*h;\n\n\th = FS_OpenVFS(\"gfx/pop.lmp\", \"rb\", FS_GAME);\n\n\tif (h)\n\t{\n\t\tstatic_registered = true;\n\t\tVFS_CLOSE(h);\n\t}\n\telse\n\t\tstatic_registered = false;\n\n\n\tnewdef = static_registered?\"1\":\"0\";\n\n\tif (strcmp(registered.enginevalue, newdef))\n\t{\n\t\tif (registered.defaultstr != registered.enginevalue)\n\t\t{\n\t\t\tCvar_DefaultFree(registered.defaultstr);\n\t\t\tregistered.defaultstr = NULL;\n\t\t}\n\t\tregistered.enginevalue = newdef;\n\t\tregistered.defaultstr = newdef;\n\t\tCvar_ForceSet(&registered, newdef);\n\t\tif (static_registered)\n\t\t\tCon_TPrintf (\"Playing registered version.\\n\");\n\t}\n}\n\n\n\n/*\n================\nCOM_InitArgv\n================\n*/\nvoid COM_InitArgv (int argc, const char **argv)\t//not allowed to tprint\n{\n\tqboolean\tsafe;\n\tint\t\t\ti;\n\n#if !defined(FTE_TARGET_WEB)\n\tFILE *f;\n\n\tif (argv && argv[0])\n\t\tf = fopen(va(\"%s_p.txt\", argv[0]), \"rb\");\n\telse\n\t\tf = NULL;\n\tif (f)\n\t{\n\t\tsize_t result;\n\t\tchar *buffer;\n\t\tint len;\n\t\tfseek(f, 0, SEEK_END);\n\t\tlen = ftell(f);\n\t\tfseek(f, 0, SEEK_SET);\n\n\t\tbuffer = (char*)malloc(len+1);\n\t\tresult = fread(buffer, 1, len, f); // do something with result\n\n\t\tif (result != len)\n\t\t\tCon_Printf(\"COM_InitArgv() fread: Filename: %s, expected %i, result was %u (%s)\\n\",va(\"%s_p.txt\", argv[0]),len,(unsigned int)result,strerror(errno));\n\n\t\tbuffer[len] = '\\0';\n\n\t\twhile (*buffer && (argc < MAX_NUM_ARGVS))\n\t\t{\n\t\t\twhile (*buffer && ((*buffer <= 32) || (*buffer > 126)))\n\t\t\t\tbuffer++;\n\n\t\t\tif (*buffer)\n\t\t\t{\n\t\t\t\targv[argc] = buffer;\n\t\t\t\targc++;\n\n\t\t\t\twhile (*buffer && ((*buffer > 32) && (*buffer <= 126)))\n\t\t\t\t\tbuffer++;\n\n\t\t\t\tif (*buffer)\n\t\t\t\t{\n\t\t\t\t\t*buffer = 0;\n\t\t\t\t\tbuffer++;\n\t\t\t\t}\n\n\t\t\t}\n\t\t}\n\n\n\t\tfclose(f);\n\t}\n#endif\n\n\tsafe = false;\n\n\tfor (com_argc=0 ; (com_argc<MAX_NUM_ARGVS) && (com_argc < argc) ;\n\t\t com_argc++)\n\t{\n\t\tlargv[com_argc] = argv[com_argc];\n\t\tif (!Q_strcmp (\"-safe\", argv[com_argc]))\n\t\t\tsafe = true;\n\t}\n\n\tif (safe)\n\t{\n\t// force all the safe-mode switches. Note that we reserved extra space in\n\t// case we need to add these, so we don't need an overflow check\n\t\tfor (i=0 ; i<countof(safeargvs) ; i++)\n\t\t{\n\t\t\tlargv[com_argc] = safeargvs[i];\n\t\t\tcom_argc++;\n\t\t}\n\t}\n\n\tlargv[com_argc] = argvdummy;\n\tcom_argv = largv;\n}\n\n/*\n================\nCOM_AddParm\n\nAdds the given string at the end of the current argument list\n================\n*/\nvoid COM_AddParm (const char *parm)\n{\n\tlargv[com_argc++] = parm;\n}\n\n/*\n=======================\nCOM_Version_f\n======================\n*/\nstatic void COM_Version_f (void)\n{\n\tCon_Printf(\"\\n\");\n\tCon_Printf(\"^&F0%s\\n\", FULLENGINENAME);\n\tCon_Printf(\"^4\"ENGINEWEBSITE\"\\n\");\n\tCon_Printf(\"%s\\n\", version_string());\n\n#ifdef FTE_BRANCH\n\tCon_Printf(\"^3Branch:^7 \"STRINGIFY(FTE_BRANCH)\"\\n\");\n\tCon_Printf(\"^3Revision:^7 %s - %s\\n\",STRINGIFY(SVNREVISION), STRINGIFY(SVNDATE));\n#elif defined(SVNREVISION) && defined(SVNDATE)\n\tif (!strncmp(STRINGIFY(SVNREVISION), \"git-\", 4))\n\t\tCon_Printf(\"^3GIT Revision:^7 %s - %s\\n\",STRINGIFY(SVNREVISION), STRINGIFY(SVNDATE));\n\telse\n\t\tCon_Printf(\"^3SVN Revision:^7 %s - %s\\n\",STRINGIFY(SVNREVISION), STRINGIFY(SVNDATE));\n#else\n\tCon_TPrintf (\"^3Exe:^7 %s %s\\n\", __DATE__, __TIME__);\n#ifdef SVNREVISION\n\tif (!strncmp(STRINGIFY(SVNREVISION), \"git-\", 4))\n\t\tCon_Printf(\"^3GIT Revision:^7 %s\\n\",STRINGIFY(SVNREVISION));\n\telse if (strcmp(STRINGIFY(SVNREVISION), \"-\"))\n\t\tCon_Printf(\"^3SVN Revision:^7 %s\\n\",STRINGIFY(SVNREVISION));\n#endif\n#endif\n#ifdef CONFIG_FILE_NAME\n\tCon_Printf(\"^3Build config:^7 %s\\n\\n\", COM_SkipPath(STRINGIFY(CONFIG_FILE_NAME)));\n#endif\n\n\tCon_Printf(\"^3Build type:^7\");\n#ifdef MINIMAL\n\tCon_Printf(\"minimal\\n\");\n#endif\n#ifdef CLIENTONLY\n\tCon_Printf(\" client-only\\n\");\n#endif\n#ifdef SERVERONLY\n\tCon_Printf(\" dedicated\\n\");\n#endif\n#ifdef _DEBUG\n\tCon_Printf(\" debug\");\n#else\n\tCon_Printf(\" release\");\n#endif\n\tCon_Printf(\"\\n\");\n\n#if defined(FTE_SDL3)\n\t{\n\t\tint ver = SDL_GetVersion();\n\t\tCon_Printf(\"^3SDL version:^9 %d.%d.%d -> ^7%d.%d.%d\\n\", SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_MICRO_VERSION, SDL_VERSIONNUM_MAJOR(ver), SDL_VERSIONNUM_MINOR(ver), SDL_VERSIONNUM_MICRO(ver));\n\t}\n#elif defined(FTE_SDL)\n\tCon_Printf(\"^3SDL version:^7 %d.%d.%d\\n\", SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_PATCHLEVEL);\n#endif\n\n// Don't print both as a 64bit MinGW built client\n#if defined(__MINGW32__)\n\tCon_Printf(\"Compiled with MinGW32/64 version: %i.%i\\n\",__MINGW32_MAJOR_VERSION, __MINGW32_MINOR_VERSION);\n#endif\n\n#ifdef __CYGWIN__\n\tCon_Printf(\"Compiled with Cygwin\\n\");\n#endif\n\n#ifdef FTE_TARGET_WEB\n\tCon_Printf(\"Compiled with emscripten %i.%i.%i\\n\", __EMSCRIPTEN_major__, __EMSCRIPTEN_minor__, __EMSCRIPTEN_tiny__);\n#endif\n\n#ifdef __clang__\n\tCon_Printf(\"^3Compiler:^7 clang %i.%i.%i (%s)\\n\",__clang_major__, __clang_minor__, __clang_patchlevel__, __VERSION__);\n#elif defined(__GNUC__)\n\tCon_Printf(\"^3Compiler:^7 GCC %i.%i.%i (%s)\\n\",__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__, __VERSION__);\n\n\t#ifdef __OPTIMIZE__\n\t\t#ifdef __OPTIMIZE_SIZE__\n\t\t\tCon_Printf(\"Optimized for size\\n\");\n\t\t#else\n\t\t\tCon_Printf(\"Optimized for speed\\n\");\n\t\t#endif\n\t#endif\n\n\t#ifdef __NO_INLINE__\n\t\tCon_Printf(\"^3GCC Optimization:^7 Functions currently not inlined into their callers\\n\");\n\t#else\n\t\tCon_Printf(\"^3GCC Optimization:^7 Functions currently inlined into their callers\\n\");\n\t#endif\n#elif defined(_MSC_VER)\n\tif (_MSC_VER == 600) {\t\tCon_Printf(\t\"^3Compiler:^7 C Compiler version 6.0\\n\"); }\n\telse if (_MSC_VER == 700) { Con_Printf(\t\"^3Compiler:^7 C/C++ compiler version 7.0\\n\"); }\n\telse if (_MSC_VER == 800) { Con_Printf(\t\"^3Compiler:^7 Visual C++, Windows, version 1.0 or Visual C++, 32-bit, version 1.0\\n\"); }\n\telse if (_MSC_VER == 900) { Con_Printf(\t\"^3Compiler:^7 Visual C++, Windows, version 2.0 or Visual C++, 32-bit, version 2.x\\n\"); }\n\telse if (_MSC_VER == 1000) { Con_Printf(\"^3Compiler:^7 Visual C++, 32-bit, version 4.0\\n\"); }\n\telse if (_MSC_VER == 1020) { Con_Printf(\"^3Compiler:^7 Visual C++, 32-bit, version 4.2\\n\"); }\n\telse if (_MSC_VER == 1100) { Con_Printf(\"^3Compiler:^7 Visual C++, 32-bit, version 5.0\\n\"); }\n\telse if (_MSC_VER == 1200) { Con_Printf(\"^3Compiler:^7 Visual C++, 32-bit, version 6.0\\n\"); }\n\telse if (_MSC_VER == 1300) { Con_Printf(\"^3Compiler:^7 Visual C++, version 7.0\\n\"); }\n\telse if (_MSC_VER == 1310) { Con_Printf(\"^3Compiler:^7 Visual C++ 2003, version 7.1\\n\"); }\n\telse if (_MSC_VER == 1400) { Con_Printf(\"^3Compiler:^7 Visual C++ 2005, version 8.0\\n\"); }\n\telse if (_MSC_VER == 1500) { Con_Printf(\"^3Compiler:^7 Visual C++ 2008, version 9.0\\n\"); }\n\telse if (_MSC_VER == 1600) { Con_Printf(\"^3Compiler:^7 Visual C++ 2010, version 10.0\\n\"); }\n\telse if (_MSC_VER == 1700) { Con_Printf(\"^3Compiler:^7 Visual C++ 2012, version 11.0\\n\"); }\n\telse if (_MSC_VER == 1800) { Con_Printf(\"^3Compiler:^7 Visual C++ 2013, version 12.0\\n\"); }\n\telse if (_MSC_VER == 1900) { Con_Printf(\"^3Compiler:^7 Visual C++ 2015, version 14.0\\n\"); }\n\telse if (_MSC_VER >= 1910 && _MSC_VER < 1920) { Con_Printf(\"^3Compiler:^7 Visual C++ 2017, version 14.1x\\n\"); }\n\telse if (_MSC_VER >= 1920 && _MSC_VER < 1930) { Con_Printf(\"^3Compiler:^7 Visual C++ 2019, version 14.2x\\n\"); }\n\telse\n\t{\n#ifdef _MSC_BUILD\n\t\tCon_Printf(\"^3Compiler:^7 Unknown Microsoft C++ compiler: %i %i %i\\n\",_MSC_VER, _MSC_FULL_VER, _MSC_BUILD);\n#else\n\t\tCon_Printf(\"^3Compiler:^7 Unknown Microsoft C++ compiler: %i %i\\n\",_MSC_VER, _MSC_FULL_VER);\n#endif\n\t}\n#endif\n\n\tCon_Printf(\"^3CPU Arch:^7 \" PLATFORM \" \" ARCH_CPU_POSTFIX\n#ifdef ARCH_ALTCPU_POSTFIX\n\t\t\"/\"ARCH_ALTCPU_POSTFIX\n#endif\n\t);\n#ifdef __AVX512F__\n\tCon_Printf(\" AVX512\");\n#elif defined(__AVX2__)\n\tCon_Printf(\" AVX2\");\n#elif defined (__AVX__)\n\tCon_Printf(\" AVX\");\n#elif defined (__SSE4_2__)\n\tCon_Printf(\" SSE4.2\");\n#elif defined (__SSE4_1__)\n\tCon_Printf(\" SSE4.1\");\n#elif defined (__SSE3__)\n\tCon_Printf(\" SSE3\");\n#elif defined(_M_IX86_FP) && _M_IX86_FP == 2 \t//32bit only - always enabled for amd64\n\tCon_Printf(\" SSE2\");\n#elif defined(_M_IX86_FP) && _M_IX86_FP == 1 \t//32bit only - always enabled for amd64\n\tCon_Printf(\" SSE\");\n#elif defined(_M_IX86_FP) && _M_IX86_FP == 0 \t//32bit only - always enabled for amd64\n\tCon_Printf(\" x87\");\n#endif\n\tCon_Printf(\"\\n\");\n\n#ifdef _M_IX86\n\tCon_Printf(\"^3x86 optimized for:^7 \");\n\n\tif (_M_IX86 == 600) { Con_Printf(\"Blend or Pentium Pro, Pentium II and Pentium III\"); }\n\telse if (_M_IX86 == 500) { Con_Printf(\"Pentium\"); }\n\telse if (_M_IX86 == 400) { Con_Printf(\"486\"); }\n\telse if (_M_IX86 == 300) { Con_Printf(\"386\"); }\n\telse\n\t{\n\t\tCon_Printf(\"Unknown (%i)\\n\",_M_IX86);\n\t}\n\n\tCon_Printf(\"\\n\");\n#endif\n\n#ifdef HAVE_CLIENT\n\tCon_Printf(\"^3Renderers:^7\");\n#ifdef GLQUAKE\n#ifdef GLESONLY\n\t#ifdef FTE_TARGET_WEB\t//shuld we be just asking the video code for a list?...\n\t\tCon_Printf(\" WebGL\");\n\t#else\n\t\tCon_Printf(\" OpenGLES\");\n\t#endif\n#else\n\tCon_Printf(\" OpenGL\");\n#endif\n#ifdef GLSLONLY\n\tCon_Printf(\"(GLSL)\");\n#endif\n#endif\n#ifdef VKQUAKE\n\tCon_Printf(\" Vulkan\");\n#endif\n#ifdef D3D9QUAKE\n\tCon_Printf(\" Direct3D9\");\n#endif\n#ifdef D3D11QUAKE\n\tCon_Printf(\" Direct3D11\");\n#endif\n#ifdef SWQUAKE\n\tCon_Printf(\" Software\");\n#endif\n\tCon_Printf(\"\\n\");\n#endif\n\n#ifdef MULTITHREAD\n#ifdef LOADERTHREAD\n\tCon_Printf(\"^3multithreading:^7 enabled (loader enabled)\\n\");\n#else\n\tCon_Printf(\"^3multithreading:^7 enabled (no loader)\\n\");\n#endif\n#else\n\tCon_Printf(\"^3multithreading^7: disabled\\n\");\n#endif\n\n\t//print out which libraries are disabled\n\tCon_Printf(\"^3Compression:^7\");\n#ifdef AVAIL_ZLIB\n\tCon_Printf(\" zlib^h(\"\n#ifdef ZLIB_STATIC\n\t\t\t\"static, \"\n#endif\n\t\t\t\"%s)^h\", ZLIB_VERSION);\n#endif\n#ifdef AVAIL_BZLIB\n\tCon_Printf(\" bzlib\"\n\t\t#ifdef BZLIB_STATIC\n\t\t\t\"^h(static)^h\"\n\t\t#endif\n\t\t);\n#endif\n\tCon_Printf(\"\\n\");\n\n#ifdef HAVE_CLIENT\n\tImage_PrintInputFormatVersions();\n\n\tCon_Printf(\"^3VoiceChat:^7\");\n\t#if !defined(VOICECHAT)\n\t\tCon_Printf(\" disabled\");\n\t#else\n\t\t#ifdef HAVE_SPEEX\n\t\t\t#ifdef SPEEX_STATIC\n\t\t\t\tCon_Printf(\" speex\");\n\t\t\t\tCon_DPrintf(\"^h(static)\");\n\t\t\t#else\n\t\t\t\tCon_Printf(\" speex^h(dynamic)\");\n\t\t\t#endif\n\t\t#endif\n\t\t#ifdef HAVE_OPUS\n\t\t\t#ifdef OPUS_STATIC\n\t\t\t\tCon_Printf(\" opus\");\n\t\t\t\tCon_DPrintf(\"^h(static)\");\n\t\t\t#else\n\t\t\t\tCon_Printf(\" opus^h(dynamic)\");\n\t\t\t#endif\n\t\t#endif\n\t#endif\n\tCon_Printf(\"\\n\");\n\n\tCon_Printf(\"^3Audio Decoders:^7\");\n\t#ifdef FTE_TARGET_WEB\n\t\tCon_Printf(\" Browser\");\n\t#endif\n\t#ifndef AVAIL_OGGVORBIS\n\t\tCon_DPrintf(\" ^h(disabled: Ogg Vorbis)^7\");\n\t#elif defined(LIBVORBISFILE_STATIC)\n\t\tCon_Printf(\" Ogg-Vorbis\");\n\t\tCon_DPrintf(\"^h(static)\");\n\t#else\n\t\tCon_Printf(\" Ogg-Vorbis^h(dynamic)\");\n\t#endif\n\t#if defined(AVAIL_MP3_ACM)\n\t\tCon_Printf(\" mp3(system)\");\n\t#endif\n\tCon_Printf(\"\\n\");\n#endif\n\n#ifdef SQL\n\tCon_Printf(\"^3Databases:^7\");\n\t#ifdef USE_MYSQL\n\t\tCon_Printf(\" mySQL^h(dynamic)\");\n\t#else\n\t\tCon_DPrintf(\" ^h(disabled: mySQL)^7\");\n\t#endif\n\t#ifdef USE_SQLITE\n\t\tCon_Printf(\" sqlite^h(dynamic)\");\n\t#else\n\t\tCon_DPrintf(\" ^h(disabled: sqlite)^7\");\n\t#endif\n\tCon_Printf(\"\\n\");\n#endif\n\n\tCon_Printf(\"^3Misc:^7\");\n#ifdef SUBSERVERS\n\tCon_Printf(\" mapcluster\");\n#else\n\tCon_DPrintf(\" ^h(disabled: mapcluster)^7\");\n#endif\n#ifdef HAVE_SERVER\n#ifdef AVAIL_FREETYPE\n\t#ifdef FREETYPE_STATIC\n\t\tCon_Printf(\" freetype2\");\n\t\tCon_DPrintf(\"^h(static)\");\n\t#else\n\t\tCon_Printf(\" freetype2^h(dynamic)\");\n\t#endif\n#else\n\tCon_DPrintf(\" ^h(disabled: freetype2)^7\");\n#endif\n#ifdef AVAIL_OPENAL\n\t#ifdef FTE_TARGET_WEB\n\t\tCon_Printf(\" WebAudio\");\n\t\tCon_DPrintf(\"^h(static)\");\n\t#else\n\t\tCon_Printf(\" OpenAL^h(dynamic)\");\n\t#endif\n#else\n\tCon_DPrintf(\" ^h(disabled: openal)^7\");\n#endif\n#endif\n#ifdef USE_INTERNAL_BULLET\n\tCon_Printf(\" bullet\");\n#endif\n#ifdef ENGINE_ROUTING\n\tCon_Printf(\" routing\");\n#endif\n#ifdef QCJIT\n\tCon_Printf(\" qcjit\");\n#endif\n\tCon_Printf(\"\\n\");\n\n#ifdef _WIN32\n\t#ifndef AVAIL_DINPUT\n\t\tCon_DPrintf(\"DirectInput disabled\\n\");\n\t#endif\n\t#ifndef AVAIL_DSOUND\n\t\tCon_DPrintf(\"DirectSound disabled\\n\");\n\t#endif\n#endif\n\n#if defined(HAVE_SERVER) || defined(HAVE_CLIENT)\n\tCon_Printf(\"^3Games:^7\");\n#if defined(Q3SERVER) && defined(Q3CLIENT)\n\t#ifdef BOTLIB_STATIC\n\t\tCon_Printf(\" Quake3\");\n\t#else\n\t\tCon_Printf(\" Quake3^h(dynamic)^h\");\n\t#endif\n#elif defined(Q3SERVER)\n\t#ifdef BOTLIB_STATIC\n\t\tCon_Printf(\" Quake3(server)\");\n\t#else\n\t\tCon_Printf(\" Quake3(server,dynamic)\");\n\t#endif\n#elif defined(Q3CLIENT)\n\tCon_Printf(\" Quake3(client)\");\n#elif defined(Q3BSPS)\n\tCon_DPrintf(\" ^hQuake3(bsp only)^7\");\n#else\n\tCon_DPrintf(\" ^h(disabled: Quake3)^7\");\n#endif\n#if defined(Q2SERVER) && defined(Q2CLIENT)\n\tCon_Printf(\" Quake2\");\n#elif defined(Q2SERVER)\n\tCon_Printf(\" Quake2(server)\");\n#elif defined(Q2CLIENT)\n\tCon_Printf(\" Quake2(client)\");\n#elif defined(Q2BSPS)\n\tCon_DPrintf(\" ^hQuake2(bsp only)^7\");\n#else\n\tCon_DPrintf(\" ^h(disabled: Quake2)^7\");\n#endif\n#if defined(HEXEN2)\n\tCon_Printf(\" Hexen2\");\n#else\n\tCon_DPrintf(\" ^h(disabled: Hexen2)^7\");\n#endif\n#if defined(NQPROT)\n\tCon_Printf(\" NetQuake\");\n#else\n\tCon_DPrintf(\" ^h(disabled: NetQuake)\");\n#endif\n#if defined(VM_Q1)\n\tCon_Printf(\" ssq1qvm\");\n#endif\n#if defined(VM_LUA)\n\tCon_Printf(\" ssq1lua^h(dynamic)\");\n#endif\n#if defined(MENU_DAT)\n\tCon_Printf(\" menuqc\");\n#endif\n#if defined(MENU_NATIVECODE)\n\tCon_Printf(\" nmenu\");\n#endif\n#if defined(CSQC_DAT)\n\tCon_Printf(\" csqc\");\n#endif\n#ifdef HAVE_SERVER\n\tCon_Printf(\" ssqc\");\n#endif\n\tCon_Printf(\"\\n\");\n#endif\n\n\tCon_Printf(\"^3Networking:^7\");\n#ifdef WEBCLIENT\n\tCon_Printf(\" HTTPClient\");\n#endif\n#ifdef HAVE_HTTPSV\n\tCon_Printf(\" HTTPServer\");\n#endif\n#ifdef FTPSERVER\n\tCon_Printf(\" FTPServer\");\n#endif\n#if (defined(SUPPORT_ICE)&&defined(HAVE_DTLS)) || defined(FTE_TARGET_WEB)\n\tCon_Printf(\" WebRTC\");\n#elif defined(SUPPORT_ICE)\n\tCon_Printf(\" ICE\");\n#endif\n#ifdef FTE_TARGET_WEB\n\tCon_Printf(\" WebSocket/WSS\");\n#else\n\t#if defined(HAVE_TCP)\n\t\t#ifdef TCPCONNECT\n\t\t\tCon_Printf(\" TCPConnect\");\n\t\t#endif\n\t#else\n\t\tCon_Printf(\" ^h(disabled: TCP)\");\n\t#endif\n#endif\n#ifdef HAVE_GNUTLS             //on linux\n\tCon_Printf(\" GnuTLS\");\n#endif\n#ifdef HAVE_WINSSPI            //on windows\n\tCon_Printf(\" WINSSPI\");\n#endif\n\tCon_Printf(\"\\n\");\n}\n\n#ifdef _DEBUG\nstatic void COM_LoopMe_f(void)\n{\n\twhile(1)\n\t\t;\n}\nstatic void COM_CrashMe_f(void)\n{\n\tint *crashaddr = (int*)0x05;\n\n\t*crashaddr = 0;\n}\n\nstatic void COM_ErrorMe_f(void)\n{\n\tSys_Error(\"\\\"errorme\\\" command used\");\n}\n#endif\n\n\n\n#ifdef LOADERTHREAD\nstatic void QDECL COM_WorkerCount_Change(cvar_t *var, char *oldvalue);\ncvar_t worker_flush = CVARD(\"worker_flush\", \"1\", \"If set, process the entire load queue, loading stuff faster but at the risk of stalling the main thread.\");\nstatic cvar_t worker_count = CVARFCD(\"worker_count\", \"\", CVAR_NOTFROMSERVER, COM_WorkerCount_Change, \"Specifies the number of worker threads to utilise.\");\nstatic cvar_t worker_sleeptime = CVARFD(\"worker_sleeptime\", \"0\", CVAR_NOTFROMSERVER, \"Causes workers to sleep for a period of time after each job.\");\n\n#define WORKERTHREADS 16\t//max\n/*multithreading worker thread stuff*/\nvoid *com_resourcemutex;\nstatic int com_liveworkers[WG_COUNT];\nstatic void *com_workercondition[WG_COUNT];\nint com_hadwork[WG_COUNT];\nstatic volatile int com_workeracksequence;\nstatic struct com_worker_s\n{\n\tvoid *thread;\n\tvolatile enum {\n\t\tWR_NONE,\n\t\tWR_DIE,\n\t\tWR_ACK\t//updates ackseq to com_workeracksequence and sends a signal to WG_MAIN\n\t} request;\n\tvolatile int ackseq;\n} com_worker[WORKERTHREADS];\nqboolean com_workererror;\nstatic struct com_work_s\n{\n\tstruct com_work_s *next;\n\tvoid(*func)(void *ctx, void *data, size_t a, size_t b);\n\tvoid *ctx;\n\tvoid *data;\n\tsize_t a;\n\tsize_t b;\n} *com_work_head[WG_COUNT], *com_work_tail[WG_COUNT];\nunsigned int COM_HasWorkers(wgroup_t tg)\n{\t//simply returns if adding work will block or not (and a hint for how many jobs should be queued at once).\n\treturn com_liveworkers[tg];\n}\n//return if there's *any* loading that needs to be done anywhere.\nqboolean COM_HasWork(void)\n{\n\tunsigned int i;\n\tfor (i = 0; i < WG_COUNT; i++)\n\t{\n\t\tif (com_work_head[i])\n\t\t\treturn true;\n\t}\n\treturn false;\n}\nvoid COM_InsertWork(wgroup_t tg, void(*func)(void *ctx, void *data, size_t a, size_t b), void *ctx, void *data, size_t a, size_t b)\n{\n\tstruct com_work_s *work;\n\n\tif (tg >= WG_COUNT)\n\t\treturn;\n\n\t//no worker there, just do it immediately on this thread instead of pushing it to the worker.\n\tif (!com_liveworkers[tg] || (tg!=WG_MAIN && com_workererror))\n\t{\n\t\tfunc(ctx, data, a, b);\n\t\treturn;\n\t}\n\n\t//build the work\n\twork = Z_Malloc(sizeof(*work));\n\twork->func = func;\n\twork->ctx = ctx;\n\twork->data = data;\n\twork->a = a;\n\twork->b = b;\n\n\t//queue it (fifo)\n\tSys_LockConditional(com_workercondition[tg]);\n\twork->next = com_work_head[tg];\n\tif (!com_work_tail[tg])\n\t\tcom_work_tail[tg] = work;\n\tcom_work_head[tg] = work;\n\n//\tSys_Printf(\"%x: Queued work %p (%s)\\n\", thread, work->ctx, work->ctx?(char*)work->ctx:\"?\");\n\n\tSys_ConditionSignal(com_workercondition[tg]);\n\tSys_UnlockConditional(com_workercondition[tg]);\n}\nvoid COM_AddWork(wgroup_t tg, void(*func)(void *ctx, void *data, size_t a, size_t b), void *ctx, void *data, size_t a, size_t b)\n{\n\tstruct com_work_s *work;\n\n\tif (tg >= WG_COUNT)\n\t\treturn;\n\n\t//no worker there, just do it immediately on this thread instead of pushing it to the worker.\n\tif (!com_liveworkers[tg] || (tg!=WG_MAIN && com_workererror))\n\t{\n\t\tfunc(ctx, data, a, b);\n\t\treturn;\n\t}\n\n\t//build the work\n\twork = Z_Malloc(sizeof(*work));\n\twork->func = func;\n\twork->ctx = ctx;\n\twork->data = data;\n\twork->a = a;\n\twork->b = b;\n\n\t//queue it (fifo)\n\tSys_LockConditional(com_workercondition[tg]);\n\tif (com_work_tail[tg])\n\t{\n\t\tcom_work_tail[tg]->next = work;\n\t\tcom_work_tail[tg] = work;\n\t}\n\telse\n\t\tcom_work_head[tg] = com_work_tail[tg] = work;\n\n//\tSys_Printf(\"%x: Queued work %p (%s)\\n\", thread, work->ctx, work->ctx?(char*)work->ctx:\"?\");\n\n\tSys_ConditionSignal(com_workercondition[tg]);\n\tSys_UnlockConditional(com_workercondition[tg]);\n}\n\n/*static void COM_PrintWork(void)\n{\n\tstruct com_work_s *work;\n\tint tg;\n\tSys_Printf(\"--------- BEGIN WORKER LIST ---------\\n\");\n\tfor (tg = 0; tg < WG_COUNT; tg++)\n\t{\n\t\tSys_LockConditional(com_workercondition[tg]);\n\t\twork = com_work_head[tg];\n\t\twhile (work)\n\t\t{\n\t\t\tSys_Printf(\"group%i: %s\\n\", tg, (char*)work->ctx);\n\t\t\twork = work->next;\n\t\t}\n\t\tSys_UnlockConditional(com_workercondition[tg]);\n\t}\n}*/\n\n//leavelocked = false == poll mode.\n//leavelocked = true == safe sleeping\nqboolean COM_DoWork(int tg, qboolean leavelocked)\n{\n\tstruct com_work_s *work;\n\tif (tg >= WG_COUNT)\n\t\treturn false;\n\tif (!leavelocked)\n\t{\n\t\t//skip the locks if it looks like we can be lazy.\n\t\tif (!com_work_head[tg])\n\t\t\treturn false;\n\t\tSys_LockConditional(com_workercondition[tg]);\n\t}\n\twork = com_work_head[tg];\n\tif (work)\n\t\tcom_work_head[tg] = work->next;\n\tif (!com_work_head[tg])\n\t\tcom_work_head[tg] = com_work_tail[tg] = NULL;\n\n\tif (work)\n\t{\n\t\tcom_hadwork[tg]++;\n//\t\tSys_Printf(\"%x: Doing work %p (%s)\\n\", thread, work->ctx, work->ctx?(char*)work->ctx:\"?\");\n\t\tSys_UnlockConditional(com_workercondition[tg]);\n\n\t\twork->func(work->ctx, work->data, work->a, work->b);\n\t\tZ_Free(work);\n\n\t\tif (leavelocked)\n\t\t\tSys_LockConditional(com_workercondition[tg]);\n\n\t\treturn true;\t//did something, check again\n\t}\n\n\tif (!leavelocked)\n\t\tSys_UnlockConditional(com_workercondition[tg]);\n\n\t//nothing going on, if leavelocked then noone can add anything until we sleep.\n\treturn false;\n}\n/*static void COM_WorkerSync_ThreadAck(void *ctx, void *data, size_t a, size_t b)\n{\n\tint us;\n\tint *ackbuf = ctx;\n\n\tSys_LockConditional(com_workercondition[WG_MAIN]);\n\t//find out which worker we are, and flag ourselves as having acked the main thread to clean us up\n\tfor (us = 0; us < WORKERTHREADS; us++)\n\t{\n\t\tif (com_worker[us].thread && Sys_IsThread(com_worker[us].thread))\n\t\t{\n\t\t\tackbuf[us] = true;\n\t\t\tbreak;\n\t\t}\n\t}\n\t*(int*)data += 1;\n\t//and tell the main thread it can stop being idle now\n\tSys_ConditionSignal(com_workercondition[WG_MAIN]);\n\tSys_UnlockConditional(com_workercondition[WG_MAIN]);\n}\n*/\n/*static void COM_WorkerSync_SignalMain(void *ctx, void *data, size_t a, size_t b)\n{\n\tSys_LockConditional(com_workercondition[a]);\n\tcom_workerdone[a] = true;\n\tSys_ConditionSignal(com_workercondition[a]);\n\tSys_UnlockConditional(com_workercondition[a]);\n}*/\nstatic void COM_WorkerSync_WorkerStopped(void *ctx, void *data, size_t a, size_t b)\n{\n\tstruct com_worker_s *thread = ctx;\n\tif (thread->thread)\n\t{\n\t\t//the worker signaled us then stopped looping\n\t\tSys_WaitOnThread(thread->thread);\n\t\tthread->thread = NULL;\n\n\t\tSys_LockConditional(com_workercondition[b]);\n\t\tcom_liveworkers[b] -= 1;\n\t\tSys_UnlockConditional(com_workercondition[b]);\n\t}\n\telse\n\t\tCon_Printf(\"worker thread died twice?\\n\");\n\n\t//if that was the last thread, make sure any work pending for that group is completed.\n\tif (!com_liveworkers[b])\n\t{\n\t\twhile(COM_DoWork(b, false))\n\t\t\t;\n\t}\n}\nstatic int COM_WorkerThread(void *arg)\n{\n\tstruct com_worker_s *thread = arg;\n\tint group = WG_LOADER;\n\tSys_LockConditional(com_workercondition[group]);\n\tcom_liveworkers[group]++;\n\tfor(;;)\n\t{\n\t\twhile(COM_DoWork(group, true))\n\t\t{\n\t\t\tif (thread->request == WR_DIE)\n\t\t\t\tbreak;\n\t\t\tif (worker_sleeptime.value)\n\t\t\t{\n\t\t\t\tSys_UnlockConditional(com_workercondition[group]);\n\t\t\t\tSys_Sleep(worker_sleeptime.value);\n\t\t\t\tSys_LockConditional(com_workercondition[group]);\n\t\t\t}\n\t\t}\n\t\tif (thread->request)\t//flagged from some work\n\t\t{\n\t\t\tif (thread->request == WR_DIE)\n\t\t\t\tbreak;\n\t\t\tif (thread->request == WR_ACK)\n\t\t\t{\n\t\t\t\tthread->request = WR_NONE;\n\t\t\t\tthread->ackseq = com_workeracksequence;\n\t\t\t\tSys_UnlockConditional(com_workercondition[group]);\n\t\t\t\tSys_LockConditional(com_workercondition[WG_MAIN]);\n\t\t\t\tSys_ConditionBroadcast(com_workercondition[WG_MAIN]); //try to wake up whoever wanted us to ack them\n\t\t\t\tSys_UnlockConditional(com_workercondition[WG_MAIN]);\n\t\t\t\tSys_LockConditional(com_workercondition[group]);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\t\telse if (!Sys_ConditionWait(com_workercondition[group]))\n\t\t\tbreak;\n\t}\n\tSys_UnlockConditional(com_workercondition[group]);\n\n\t//and wake up main thread to clean up our handle\n\tCOM_AddWork(WG_MAIN, COM_WorkerSync_WorkerStopped, thread, NULL, 0, group);\n\treturn 0;\n}\nstatic void Sys_ErrorThread(void *ctx, void *data, size_t a, size_t b)\n{\n\tif (ctx)\n\t\tCOM_WorkerSync_WorkerStopped(ctx, NULL, a, b);\n\n\t//posted to main thread from a worker.\n\tSys_Error(\"%s\", (const char*)data);\n}\nvoid COM_WorkerAbort(char *message)\n{\n\tint group = -1;\n\tint us;\n\tif (Sys_IsMainThread())\n\t\treturn;\n\tcom_workererror = true;\n\n\tif (!com_workercondition[WG_MAIN])\n\t\treturn;\t//Sys_IsMainThread was probably called too early...\n\n\t//find out which worker we are, and tell the main thread to clean us up\n\tfor (us = 0; us < WORKERTHREADS; us++)\n\t\tif (com_worker[us].thread && Sys_IsThread(com_worker[us].thread))\n\t\t{\n\t\t\tgroup = WG_LOADER;\n\t\t\tCOM_InsertWork(WG_MAIN, Sys_ErrorThread, &com_worker[us], Z_StrDup(message), 0, group);\n\t\t\tbreak;\n\t\t}\n\n\tif (us == WORKERTHREADS)\t//don't know who it was.\n\t\tCOM_AddWork(WG_MAIN, Sys_ErrorThread, NULL, Z_StrDup(message), 0, 0);\n\n\tSys_ThreadAbort();\n}\n\n#ifndef COM_AssertMainThread\nvoid COM_AssertMainThread(const char *msg)\n{\n\tif (com_resourcemutex && !Sys_IsMainThread())\n\t{\n\t\tSys_Error(\"Not on main thread: %s\", msg);\n\t}\n}\n#endif\nvoid COM_DestroyWorkerThread(void)\n{\n\tint i;\n\tif (!com_resourcemutex)\n\t\treturn;\n//\tcom_workererror = false;\n\tSys_LockConditional(com_workercondition[WG_LOADER]);\n\tfor (i = 0; i < WORKERTHREADS; i++)\n\t\tcom_worker[i].request = WR_DIE;\t//flag them all to die\n\tSys_ConditionBroadcast(com_workercondition[WG_LOADER]);\t//and make sure they ALL wake up\n\tSys_UnlockConditional(com_workercondition[WG_LOADER]);\n\n\twhile(COM_DoWork(WG_LOADER, false))\t//finish any work that got posted to it that it neglected to finish.\n\t\t;\n\tCOM_WorkerFullSync();\n\twhile(COM_DoWork(WG_MAIN, false))\n\t\t;\n\n\tfor (i = 0; i < WG_COUNT; i++)\n\t{\n\t\tif (com_workercondition[i])\n\t\t\tSys_DestroyConditional(com_workercondition[i]);\n\t\tcom_workercondition[i] = NULL;\n\t}\n\n\tSys_DestroyMutex(com_resourcemutex);\n\tcom_resourcemutex = NULL;\n}\n\n//Dangerous: stops workers WITHOUT flushing their queue. Be SURE to 'unlock' to start them up again.\nvoid COM_WorkerLock(void)\n{\n#define NOFLUSH 0x40000000\n\tint i;\n\tif (!com_liveworkers[WG_LOADER])\n\t\treturn;\t//nothing to do.\n\n\t//don't let liveworkers become 0 (so the main thread doesn't flush any pending work) and ask workers to die\n\tSys_LockConditional(com_workercondition[WG_LOADER]);\n\tcom_liveworkers[WG_LOADER] |= NOFLUSH;\n\tfor (i = 0; i < WORKERTHREADS; i++)\n\t\tcom_worker[i].request = WR_DIE;\t//flag them all to die\n\tSys_ConditionBroadcast(com_workercondition[WG_LOADER]);\t//and make sure they ALL wake up to check their new death values.\n\tSys_UnlockConditional(com_workercondition[WG_LOADER]);\n\n\t//wait for the workers to stop (leaving their work, because of our fake worker)\n\twhile((com_liveworkers[WG_LOADER]&~NOFLUSH)>0)\n\t{\n\t\tif (!COM_DoWork(WG_MAIN, false))\t//need to check this to know they're done.\n\t\t\tCOM_DoWork(WG_LOADER, false);\t//might as well, while we're waiting.\n\t}\n\n\t//remove our flush-blocker now...\n\tSys_LockConditional(com_workercondition[WG_LOADER]);\n\tcom_liveworkers[WG_LOADER] &= ~NOFLUSH;\n\tSys_UnlockConditional(com_workercondition[WG_LOADER]);\n}\n//called after COM_WorkerLock\nvoid COM_WorkerUnlock(void)\n{\n\tqboolean restarted = false;\n\tint i;\n\tfor (i = 0; i < WORKERTHREADS; i++)\n\t{\n\t\tif (i >= worker_count.ival)\n\t\t\tcontinue;\t//worker stays dead\n\n\t\t//lower thread indexes need to be (re)created\n\t\tif (!com_worker[i].thread)\n\t\t{\n\t\t\tcom_worker[i].request = WR_NONE;\n\t\t\tcom_worker[i].thread = Sys_CreateThread(va(\"loadworker_%i\", i), COM_WorkerThread, &com_worker[i], 0, 256*1024);\n\t\t\tif (com_worker[i].thread)\n\t\t\t\trestarted = true;\n\t\t}\n\t}\n\n\tif (!restarted)\n\t\twhile (COM_DoWork(WG_LOADER, false))\n\t\t\t;\n}\n\n//fully flushes ALL pending work.\nvoid COM_WorkerFullSync(void)\n{\n\tqboolean repeat;\n\tint i;\n\n\twhile(COM_DoWork(WG_MAIN, false))\n\t\t;\n\n\tif (!com_liveworkers[WG_LOADER])\n\t\treturn;\n\n\tcom_workeracksequence++;\n\n\tSys_LockConditional(com_workercondition[WG_MAIN]);\n\tdo\n\t{\n\t\tif (!COM_HasWork())\n\t\t{\n\t\t\tSys_UnlockConditional(com_workercondition[WG_MAIN]);\n\t\t\tSys_LockConditional(com_workercondition[WG_LOADER]);\n\t\t\trepeat = false;\n\t\t\tfor (i = 0; i < WORKERTHREADS; i++)\n\t\t\t{\n\t\t\t\tif (com_worker[i].ackseq != com_workeracksequence && com_worker[i].request == WR_NONE)\n\t\t\t\t{\n\t\t\t\t\tcom_worker[i].request = WR_ACK;\n\t\t\t\t\trepeat = true;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (repeat)\t//we're unable to signal a specific thread due to only having one condition. oh well. WAKE UP GUYS!\n\t\t\t\tSys_ConditionBroadcast(com_workercondition[WG_LOADER]);\n\t\t\tSys_UnlockConditional(com_workercondition[WG_LOADER]);\n\t\t\tSys_LockConditional(com_workercondition[WG_MAIN]);\n\t\t}\n\n\t\trepeat = COM_DoWork(WG_MAIN, true);\n\n\t\tif (repeat)\n\t\t{\t//if we just did something, we may have posted something new to a worker... bum.\n\t\t\tcom_workeracksequence++;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tfor (i = 0; i < WORKERTHREADS; i++)\n\t\t\t{\n\t\t\t\tif (com_worker[i].thread && com_worker[i].ackseq != com_workeracksequence)\n\t\t\t\t\trepeat = true;\n\t\t\t}\n\t\t\tif (repeat)\n\t\t\t\tSys_ConditionWait(com_workercondition[WG_MAIN]);\n\t\t}\n\t\tif (com_workererror)\n\t\t\tbreak;\n\t} while(repeat);\n\tSys_UnlockConditional(com_workercondition[WG_MAIN]);\n}\n\n//main thread wants a specific object to be prioritised.\n//an ancestor of the work must be pending on either the main thread or the worker thread.\n//typically the worker gives us a signal to handle the final activation of the object.\n//the address should be the load status. the value is the current value.\n//the work that we're waiting for will be considered complete when the address is no longer set to value.\nvoid COM_WorkerPartialSync(void *priorityctx, int *address, int value)\n{\n\tstruct com_work_s **link, *work, *prev;\n//\tdouble time1 = Sys_DoubleTime();\n\n//\tCon_Printf(\"waiting for %p %s\\n\", priorityctx, priorityctx);\n\n\tCOM_DoWork(WG_MAIN, false);\n\n\t//boost the priority of the object that we're waiting for on the other thread, if we can find it.\n\t//this avoids waiting for everything.\n\t//if we can't find it, then its probably currently being processed anyway.\n\t//main thread is meant to do all loadstate value changes anyway, ensuring that we're woken up properly in this case.\n\tif (priorityctx)\n\t{\n\t\tunsigned int grp;\n\t\tqboolean found = false;\n\t\tfor (grp = WG_LOADER; grp < WG_MAIN && !found; grp++)\n\t\t{\n\t\t\tSys_LockConditional(com_workercondition[grp]);\n\t\t\tfor (link = &com_work_head[grp], work = NULL; *link; link = &(*link)->next)\n\t\t\t{\n\t\t\t\tprev = work;\n\t\t\t\twork = *link;\n\t\t\t\tif (work->ctx == priorityctx)\n\t\t\t\t{\t//unlink it\n\n\t\t\t\t\t*link = work->next;\n\t\t\t\t\tif (!work->next)\n\t\t\t\t\t\tcom_work_tail[grp] = prev;\n\t\t\t\t\t//link it in at the head, so its the next thing seen.\n\t\t\t\t\twork->next = com_work_head[grp];\n\t\t\t\t\tcom_work_head[grp] = work;\n\t\t\t\t\tif (!work->next)\n\t\t\t\t\t\tcom_work_tail[grp] = work;\n\t\t\t\t\tfound = true;\n\n\t\t\t\t\tbreak;\t//found it, nothing else to do.\n\t\t\t\t}\n\t\t\t}\n\t\t\t//we've not actually added any work, so no need to signal\n\t\t\tSys_UnlockConditional(com_workercondition[grp]);\n\t\t}\n\t\tif (!found)\n\t\t{\n\t\t\twhile(COM_DoWork(WG_MAIN, false))\n\t\t\t{\n\t\t\t\t//give up as soon as we're done\n\t\t\t\tif (*address != value)\n\t\t\t\t\treturn;\n\t\t\t}\n//\t\t\tCon_Printf(\"Might be in for a long wait for %s\\n\", (char*)priorityctx);\n\t\t}\n\t}\n\n\tSys_LockConditional(com_workercondition[WG_MAIN]);\n\tdo\n\t{\n\t\tif (com_workererror)\n\t\t\tbreak;\n\t\twhile(COM_DoWork(WG_MAIN, true))\n\t\t{\n\t\t\t//give up as soon as we're done\n\t\t\tif (*address != value)\n\t\t\t\tbreak;\n\t\t}\n\t\t//if our object's state has changed, we're done\n\t\tif (*address != value)\n\t\t\tbreak;\n\t} while (Sys_ConditionWait(com_workercondition[WG_MAIN]));\n\tSys_UnlockConditional(com_workercondition[WG_MAIN]);\n\n//\tCon_Printf(\"Waited %f for %s\\n\", Sys_DoubleTime() - time1, priorityctx);\n}\n\nstatic void COM_WorkerPong(void *ctx, void *data, size_t a, size_t b)\n{\n\tdouble *timestamp = data;\n\tCon_Printf(\"Ping: %g\\n\", Sys_DoubleTime() - *timestamp);\n\tZ_Free(timestamp);\n}\nstatic void COM_WorkerPing(void *ctx, void *data, size_t a, size_t b)\n{\n\tCOM_AddWork(WG_MAIN, COM_WorkerPong, ctx, data, 0, 0);\n}\nstatic void COM_WorkerTest_f(void)\n{\n\tdouble *timestamp = Z_Malloc(sizeof(*timestamp));\n\t*timestamp = Sys_DoubleTime();\n\tCOM_AddWork(WG_LOADER, COM_WorkerPing, NULL, timestamp, 0, 0);\n}\nstatic void COM_WorkerStatus_f(void)\n{\n\tstruct com_work_s *work;\n\tint i, count;\n\tfor (i = 0, count = 0; i < WORKERTHREADS; i++)\n\t{\n\t\tif (com_worker[i].thread)\n\t\t\tcount++;\n\t}\n\tCon_Printf(\"%i workers live\\n\", count);\n\n\tSys_LockConditional(com_workercondition[WG_LOADER]);\n\tfor (count = 0, work = com_work_head[WG_LOADER]; work; work = work->next)\n\t\tcount++;\n\tSys_UnlockConditional(com_workercondition[WG_LOADER]);\n\tCon_Printf(\"%i pending tasks\\n\", count);\n}\n\nstatic void QDECL COM_WorkerCount_Change(cvar_t *var, char *oldvalue)\n{\n\tint i, count = var->ival;\n\n\tif (!*var->string)\n\t{\n\t\tcount = var->ival = 4;\n\t}\n\n\t//try to respond to any kill requests now, so we don't get surprised by the cvar changing too often.\n\twhile(COM_DoWork(WG_MAIN, false))\n\t\t;\n\n\tfor (i = 0; i < WORKERTHREADS; i++)\n\t{\n\t\tif (i >= count)\n\t\t{\n\t\t\t//higher thread indexes need to die.\n\t\t\tcom_worker[i].request = WR_DIE;\t//flag them all to die\n\t\t}\n\t\telse\n\t\t{\n\t\t\t//lower thread indexes need to be created\n\t\t\tif (!com_worker[i].thread)\n\t\t\t{\n\t\t\t\tcom_worker[i].request = WR_NONE;\n\t\t\t\tcom_worker[i].thread = Sys_CreateThread(va(\"loadworker_%i\", i), COM_WorkerThread, &com_worker[i], 0, 256*1024);\n\t\t\t}\n\t\t}\n\t}\n\tSys_ConditionBroadcast(com_workercondition[WG_LOADER]);\t//and make sure they ALL wake up to check their new death values.\n}\nstatic void COM_InitWorkerThread(void)\n{\n\tint i;\n\n\t//in theory, we could run multiple workers, signalling a different one in turn for each bit of work.\n\tcom_resourcemutex = Sys_CreateMutex();\n\tfor (i = 0; i < WG_COUNT; i++)\n\t{\n\t\tcom_workercondition[i] = Sys_CreateConditional();\n\t}\n\tcom_liveworkers[WG_MAIN] = 1;\n\n\t//technically its ready now...\n\n\tif (COM_CheckParm(\"-noworker\") || COM_CheckParm(\"-noworkers\"))\n\t{\n\t\tworker_count.enginevalue = \"0\";\n\t\tworker_count.flags |= CVAR_NOSET;\n\t}\n\tCvar_Register(&worker_count, NULL);\n\n\tCmd_AddCommand (\"worker_test\", COM_WorkerTest_f);\n\tCmd_AddCommand (\"worker_status\", COM_WorkerStatus_f);\n\tCvar_Register(&worker_flush, NULL);\n\tCvar_Register(&worker_sleeptime, NULL);\n\tCvar_ForceCallback(&worker_count);\n}\n\nqboolean FTE_AtomicPtr_ConditionalReplace(qint32_t *ptr, qint32_t old, qint32_t new)\n{\n\tSys_LockMutex(com_resourcemutex);\n\tif (*ptr == old)\n\t{\n\t\t*ptr = new;\n\t\tSys_UnlockMutex(com_resourcemutex);\n\t\treturn true;\n\t}\n\tSys_UnlockMutex(com_resourcemutex);\n\treturn false;\n}\n\nqint32_t FTE_Atomic32Mutex_Add(qint32_t *ptr, qint32_t change)\n{\n\tqint32_t r;\n\tSys_LockMutex(com_resourcemutex);\n\tr = (*ptr += change);\n\tSys_UnlockMutex(com_resourcemutex);\n\treturn r;\n}\n#else\nqint32_t FTE_Atomic32Mutex_Add(qint32_t *ptr, qint32_t change)\n{\n\tqint32_t r;\n\tr = (*ptr += change);\n\treturn r;\n}\nqboolean FTE_AtomicPtr_ConditionalReplace(qint32_t *ptr, qint32_t old, qint32_t new)\n{\t//hope it ain't threaded\n\tif (*ptr == old)\n\t{\n\t\t*ptr = new;\n\t\treturn true;\n\t}\n\treturn false;\n}\n#endif\n\n/*\n================\nCOM_Init\n================\n*/\nvoid COM_Init (void)\n{\n#if !defined(FTE_BIG_ENDIAN) && !defined(FTE_LITTLE_ENDIAN)\n// set the qbyte swapping variables in a portable manner\n\tqbyte\tswaptest[2] = {1,0};\n\tif ( *(short *)swaptest == 1)\n\t{\n\t\tbigendian = false;\n\t\tBigShort = ShortSwap;\n\t\tLittleShort = ShortNoSwap;\n\t\tBigLong = LongSwap;\n\t\tLittleLong = LongNoSwap;\n\t\tBigI64 = I64Swap;\n\t\tLittleI64 = I64NoSwap;\n\t\tBigFloat = FloatSwap;\n\t\tLittleFloat = FloatNoSwap;\n\t}\n\telse\n\t{\n\t\tbigendian = true;\n\t\tBigShort = ShortNoSwap;\n\t\tLittleShort = ShortSwap;\n\t\tBigLong = LongNoSwap;\n\t\tLittleLong = LongSwap;\n\t\tBigI64 = I64NoSwap;\n\t\tLittleI64 = I64Swap;\n\t\tBigFloat = FloatNoSwap;\n\t\tLittleFloat = FloatSwap;\n\t}\n#endif\n\n\twantquit = false;\n\n\t//random should be random from the start...\n\tsrand(time(0));\n\n#ifdef LOADERTHREAD\n\tCOM_InitWorkerThread();\n#endif\n\n#ifdef PACKAGEMANAGER\n\tCmd_AddCommandD(\"pkg\", PM_Command_f,\t\t\"Provides a way to install / list / disable / purge packages via the console.\");\n#endif\n\tCmd_AddCommandD(\"version\", COM_Version_f,\t\"Reports engine revision and optional compile-time settings.\");\t//prints the pak or whatever where this file can be found.\n\n#ifdef _DEBUG\n\tCmd_AddCommand (\"loopme\", COM_LoopMe_f);\n\tCmd_AddCommand (\"crashme\", COM_CrashMe_f);\n\tCmd_AddCommand (\"errorme\", COM_ErrorMe_f);\n#endif\n\tCOM_InitFilesystem ();\n\n\tCvar_Register (&host_mapname, \"Scripting\");\n\tCvar_Register (&developer, \"Debugging\");\n\tCvar_Register (&sys_platform, \"Gamecode\");\n\tCvar_Register (&pr_engine, \"Gamecode\");\n\tCvar_Register (&registered, \"Copy protection\");\n\tCvar_Register (&gameversion, \"Gamecode\");\n\tCvar_Register (&gameversion_min, \"Gamecode\");\n\tCvar_Register (&gameversion_max, \"Gamecode\");\n\tCvar_Register (&com_gamedirnativecode, \"Gamecode\");\n\tCvar_Register (&com_parseutf8, \"Internationalisation\");\n#ifdef HAVE_LEGACY\n\tCvar_Register (&scr_usekfont, NULL);\n\tCvar_Register (&ezcompat_markup, NULL);\n\tCvar_Register (&pm_noround, NULL);\n#endif\n\tCvar_Register (&com_highlightcolor, \"Internationalisation\");\n\tcom_parseutf8.ival = 1;\n\n\tTranslateInit();\n\n\tCOM_BiDi_Setup();\n\n\n\tnullentitystate.hexen2flags = SCALE_ORIGIN_ORIGIN;\n\tnullentitystate.colormod[0] = 32;\n\tnullentitystate.colormod[1] = 32;\n\tnullentitystate.colormod[2] = 32;\n\tnullentitystate.glowmod[0] = 32;\n\tnullentitystate.glowmod[1] = 32;\n\tnullentitystate.glowmod[2] = 32;\n\tnullentitystate.trans = 255;\n\tnullentitystate.scale = 16;\n\tnullentitystate.solidsize = 0;//ES_SOLID_BSP;\n}\n\nvoid COM_Shutdown (void)\n{\n#ifdef LOADERTHREAD\n\tCOM_DestroyWorkerThread();\n#endif\n\tCOM_BiDi_Shutdown();\n\tFS_Shutdown();\n}\n\n/*\n============\nva\n\ndoes a varargs printf into a temp buffer, so I don't need to have\nvarargs versions of all text functions.\nFIXME: make this buffer size safe someday\n============\n*/\nchar\t*VARGS va(const char *format, ...)\n{\n#define VA_BUFFERS 2 //power of two\n#define VA_BUFFER_SIZE 8192\n\tva_list\t\targptr;\n\tstatic char\t\tstring[VA_BUFFERS][VA_BUFFER_SIZE];\n\tstatic int bufnum;\n\n\tCOM_AssertMainThread(\"va\");\n\n\tbufnum++;\n\tbufnum &= (VA_BUFFERS-1);\n\n\tva_start (argptr, format);\n\tvsnprintf (string[bufnum],sizeof(string[bufnum])-1, format,argptr);\n\tva_end (argptr);\n\n\treturn string[bufnum];\n}\n\n#if defined(HAVE_CLIENT) || defined(HAVE_SERVER)\n#ifdef NQPROT\n//for compat with dpp7 protocols, or dp gamecode that neglects to properly precache particles.\nvoid COM_Effectinfo_Enumerate(int (*cb)(const char *pname))\n{\n\tint i;\n\tchar *f, *buf;\n\tstatic const char *dpnames[] =\n\t{\n\t\t\"TE_GUNSHOT\",\n\t\t\"TE_GUNSHOTQUAD\",\n\t\t\"TE_SPIKE\",\n\t\t\"TE_SPIKEQUAD\",\n\t\t\"TE_SUPERSPIKE\",\n\t\t\"TE_SUPERSPIKEQUAD\",\n\t\t\"TE_WIZSPIKE\",\n\t\t\"TE_KNIGHTSPIKE\",\n\t\t\"TE_EXPLOSION\",\n\t\t\"TE_EXPLOSIONQUAD\",\n\t\t\"TE_TAREXPLOSION\",\n\t\t\"TE_TELEPORT\",\n\t\t\"TE_LAVASPLASH\",\n\t\t\"TE_SMALLFLASH\",\n\t\t\"TE_FLAMEJET\",\n\t\t\"EF_FLAME\",\n\t\t\"TE_BLOOD\",\n\t\t\"TE_SPARK\",\n\t\t\"TE_PLASMABURN\",\n\t\t\"TE_TEI_G3\",\n\t\t\"TE_TEI_SMOKE\",\n\t\t\"TE_TEI_BIGEXPLOSION\",\n\t\t\"TE_TEI_PLASMAHIT\",\n\t\t\"EF_STARDUST\",\n\t\t\"TR_ROCKET\",\n\t\t\"TR_GRENADE\",\n\t\t\"TR_BLOOD\",\n\t\t\"TR_WIZSPIKE\",\n\t\t\"TR_SLIGHTBLOOD\",\n\t\t\"TR_KNIGHTSPIKE\",\n\t\t\"TR_VORESPIKE\",\n\t\t\"TR_NEHAHRASMOKE\",\n\t\t\"TR_NEXUIZPLASMA\",\n\t\t\"TR_GLOWTRAIL\",\n\t\t\"SVC_PARTICLE\",\n\t\tNULL\n\t};\n\n\tFS_LoadFile(\"effectinfo.txt\", (void **)&f);\n\tif (!f)\n\t\treturn;\n\n\tfor (i = 0; dpnames[i]; i++)\n\t\tcb(dpnames[i]);\n\n\tbuf = f;\n\twhile (f && *f)\n\t{\n\t\tf = COM_ParseToken(f, NULL);\n\t\tif (strcmp(com_token, \"\\n\"))\n\t\t{\n\t\t\tif (!strcmp(com_token, \"effect\"))\n\t\t\t{\n\t\t\t\tf = COM_ParseToken(f, NULL);\n\t\t\t\tcb(com_token);\n\t\t\t}\n\n\t\t\tdo\n\t\t\t{\n\t\t\t\tf = COM_ParseToken(f, NULL);\n\t\t\t} while(f && *f && strcmp(com_token, \"\\n\"));\n\t\t}\n\t}\n\tFS_FreeFile(buf);\n}\n#endif\n\n/*************************************************************************/\n\n/*remaps map checksums from known non-cheat GPL maps to authentic id1 maps.*/\nunsigned int COM_RemapMapChecksum(model_t *model, unsigned int checksum)\n{\n#ifdef HAVE_LEGACY\n\tstatic const struct {\n\t\tconst char *name;\n\t\tunsigned int gpl2;\n//\t\tunsigned int id11;\n\t\tunsigned int id12;\n\t} sums[] =\n\t{\n\t\t{\"maps/start.bsp\",\t0xDC03BAF3,\t/*0x2A9A3763,*/\t0x1D69847B},\n\n\t\t{\"maps/e1m1.bsp\",\t0xB7B19924,\t/*0x1F392B02,*/\t0xAD07D882},\n\t\t{\"maps/e1m2.bsp\",\t0x80CD279B,\t/*0x5D140D24,*/\t0x67100127},\n\t\t{\"maps/e1m3.bsp\",\t0x1F632D93,\t/*0x3C20FA2E,*/\t0x3546324A},\n\t\t{\"maps/e1m4.bsp\",\t0xB75BC1B8,\t/*0xE5A522CE,*/\t0xEDDA0675},\n\t\t{\"maps/e1m5.bsp\",\t0x65DEA50B,\t/*0x6EA3A1CB,*/\t0xA82C1C8A},\n\t\t{\"maps/e1m6.bsp\",\t0x3C76263E,\t/*0x4DC4FFC4,*/\t0x2C0028E3},\n\t\t{\"maps/e1m7.bsp\",\t0x51FAD6A8,\t/*0xACBF5564,*/\t0x97D6FB1A},\n\t\t{\"maps/e1m8.bsp\",\t0x57A436A8,\t/*0xF63C8EE5,*/\t0x04B6E741},\n\n\t\t{\"maps/e2m1.bsp\",\t0x992B120D,\t/*0xD0732BA6,*/\t0xDCF57032},\n\t\t{\"maps/e2m2.bsp\",\t0xA23126C5,\t/*0xEACA9423,*/\t0xAF961D4D},\n\t\t{\"maps/e2m3.bsp\",\t0x0956602E,\t/*0x47B46758,*/\t0xFC992551},\n\t\t{\"maps/e2m4.bsp\",\t0xA4CDDCC6,\t/*0x9EDD4CE8,*/\t0xC3169BC9},\n\t\t{\"maps/e3m5.bsp\",\t0xDC98420F,\t/*0xAC371E07,*/\t0x917A0631},\n\t\t{\"maps/e2m6.bsp\",\t0x3E1AA34D,\t/*0x22CD3B7B,*/\t0x91A33B81},\n\t\t{\"maps/e2m7.bsp\",\t0xA1A37724,\t/*0x6C1F85F2,*/\t0x7A3FE018},\n\n\t\t{\"maps/e3m1.bsp\",\t0xBD5A7A83,\t/*0xE4BE9A0B,*/\t0x90B20D21},\n\t\t{\"maps/e3m2.bsp\",\t0xE4043D8E,\t/*0x2B1EC056,*/\t0x9C6C7538},\n\t\t{\"maps/e3m3.bsp\",\t0xEE12BAC9,\t/*0xDFCFCB78,*/\t0xC3D05D18},\n\t\t{\"maps/e3m4.bsp\",\t0xF33D954A,\t/*0x42003651,*/\t0xB1790CB8},\n\t\t{\"maps/e3m5.bsp\",\t0xDC98420F,\t/*0xAC371E07,*/\t0x917A0631},\n\t\t{\"maps/e3m6.bsp\",\t0x9CC8F9BC,\t/*0x6139434A,*/\t0x2DC17DF8},\n\t\t{\"maps/e3m7.bsp\",\t0x2E8DE70A,\t/*0xA5CF7110,*/\t0x1039C1B1},\n\n\t\t{\"maps/e4m1.bsp\",\t0x5C4CDD45,\t/*0x4AC23D4C,*/\t0xBBF06350},\n\t\t{\"maps/e4m2.bsp\",\t0xAC84C40A,\t/*0x057FACCC,*/\t0xFFF8CB18},\n\t\t{\"maps/e4m3.bsp\",\t0xB6A519E2,\t/*0x74E93DDD,*/\t0x59BEF08C},\n\t\t{\"maps/e4m4.bsp\",\t0x3233C45C,\t/*0xE9A7693C,*/\t0x2D3B183F},\n\t\t{\"maps/e4m5.bsp\",\t0xE5D3E4DD,\t/*0x17315A00,*/\t0x699CE7F4},\n\t\t{\"maps/e4m6.bsp\",\t0x5A7B37C0,\t/*0x6636A6B8,*/\t0x0620FF98},\n\t\t{\"maps/e4m7.bsp\",\t0xE9497085,\t/*0xDD1C14E2,*/\t0x9DEC01AC},\n\t\t{\"maps/e4m8.bsp\",\t0x325A2B54,\t/*0x3F6274D5,*/\t0x3CB46C57},\n\n\t\t{\"maps/dm1.bsp\",\t0x7D37618E,\t/*0xA3B80B3A,*/\t0xC5C7DAB3},\t//you should be able to use aquashark's untextured maps.\n\t\t{\"maps/dm2.bsp\",\t0x7B337440,\t/*0x1763B3DA,*/\t0x65F63634},\n\t\t{\"maps/dm3.bsp\",\t0x912781AE,\t/*0x7AC99CDE,*/\t0x15E20DF8},\n\t\t{\"maps/dm4.bsp\",\t0xC374DF89,\t/*0x13799D1F,*/\t0x9C6FE4BF},\n\t\t{\"maps/dm5.bsp\",\t0x77CA7CE5,\t/*0x2DB66BBC,*/\t0xB02D48FD},\n\t\t{\"maps/dm6.bsp\",\t0x200C8B5D,\t/*0x0EBB386D,*/\t0x5208DA2B},\n\n\t\t{\"maps/end.bsp\",\t0xF89B12AE,\t/*0xA66198D8,*/\t0xBBD4B4A5},\t//unmodified gpl version (with the extra room)\n\t\t{\"maps/end.bsp\",\t0x924F4D33,\t/*0xA66198D8,*/\t0xBBD4B4A5}, \t//aquashark's gpl version (with the extra room removed)\n\n\t\t//re-release maps. they are not 100% identical,\n\t\t//but they're generally close enough and its confusing to get kicked for having the official maps.\n\t\t//expect minor prediction issues in a few places.\n\t\t{\"maps/start.bsp\",\t0x49A92170,\t/*0x2A9A3763,*/\t0x1D69847B},\n\t\t{\"maps/e1m1.bsp\",\t0xA1937AD5,\t/*0x1F392B02,*/\t0xAD07D882},\n\t\t{\"maps/e1m2.bsp\",\t0x65BC436B,\t/*0x5D140D24,*/\t0x67100127},\n\t\t{\"maps/e1m3.bsp\",\t0x7A4FE4F2,\t/*0x3C20FA2E,*/\t0x3546324A},\n\t\t{\"maps/e1m4.bsp\",\t0xEC07DCB0,\t/*0xE5A522CE,*/\t0xEDDA0675},\n//buggy\t{\"maps/e1m5.bsp\",\t0xAD138551,\t/*0x6EA3A1CB,*/\t0xA82C1C8A},\n\t\t{\"maps/e1m6.bsp\",\t0xA732C2E4,\t/*0x4DC4FFC4,*/\t0x2C0028E3},\n\t\t{\"maps/e1m7.bsp\",\t0x9318DDF3,\t/*0xACBF5564,*/\t0x97D6FB1A},\n\t\t{\"maps/e1m8.bsp\",\t0x0E858BF7,\t/*0xF63C8EE5,*/\t0x04B6E741},\n\t\t{\"maps/e2m1.bsp\",\t0xCB350590,\t/*0xD0732BA6,*/\t0xDCF57032},\n\t\t{\"maps/e2m2.bsp\",\t0x045DC982,\t/*0xEACA9423,*/\t0xAF961D4D},\n\t\t{\"maps/e2m3.bsp\",\t0x4E14A67D,\t/*0x47B46758,*/\t0xFC992551},\n\t\t{\"maps/e2m4.bsp\",\t0x5366D18C,\t/*0x9EDD4CE8,*/\t0xC3169BC9},\n\t\t{\"maps/e3m5.bsp\",\t0x94086C83,\t/*0xAC371E07,*/\t0x917A0631},\n//start\t{\"maps/e2m6.bsp\",\t0x460E3FE2,\t/*0x22CD3B7B,*/\t0x91A33B81},\n\t\t{\"maps/e2m7.bsp\",\t0xB7477F61,\t/*0x6C1F85F2,*/\t0x7A3FE018},\n\t\t{\"maps/e3m1.bsp\",\t0xBC433495,\t/*0xE4BE9A0B,*/\t0x90B20D21},\n\t\t{\"maps/e3m2.bsp\",\t0x63E72C4D,\t/*0x2B1EC056,*/\t0x9C6C7538},\n\t\t{\"maps/e3m3.bsp\",\t0x8DD3DF69,\t/*0xDFCFCB78,*/\t0xC3D05D18},\n\t\t{\"maps/e3m4.bsp\",\t0xD41DD779,\t/*0x42003651,*/\t0xB1790CB8},\n\t\t{\"maps/e3m5.bsp\",\t0x1EAA53D8,\t/*0xAC371E07,*/\t0x917A0631},\n\t\t{\"maps/e3m6.bsp\",\t0xEFB7B728,\t/*0x6139434A,*/\t0x2DC17DF8},\n\t\t{\"maps/e3m7.bsp\",\t0x7A46C0EA,\t/*0xA5CF7110,*/\t0x1039C1B1},\n\t\t{\"maps/e4m1.bsp\",\t0x9AF0885B,\t/*0x4AC23D4C,*/\t0xBBF06350},\n\t\t{\"maps/e4m2.bsp\",\t0x8E947D06,\t/*0x057FACCC,*/\t0xFFF8CB18},\n\t\t{\"maps/e4m3.bsp\",\t0x134BCDEE,\t/*0x74E93DDD,*/\t0x59BEF08C},\n\t\t{\"maps/e4m4.bsp\",\t0xBDB41FF0,\t/*0xE9A7693C,*/\t0x2D3B183F},\n\t\t{\"maps/e4m5.bsp\",\t0xC1F0D4C6,\t/*0x17315A00,*/\t0x699CE7F4},\n\t\t{\"maps/e4m6.bsp\",\t0x286A9410,\t/*0x6636A6B8,*/\t0x0620FF98},\n\t\t{\"maps/e4m7.bsp\",\t0xB769356B,\t/*0xDD1C14E2,*/\t0x9DEC01AC},\n//\t\t{\"maps/e4m8.bsp\",\t0xA62A7AEB,\t/*0x3F6274D5,*/\t0x3CB46C57},\n//\t\t{\"maps/dm1.bsp\",\t0x6E4C13E6,\t/*0xA3B80B3A,*/\t0xC5C7DAB3},\n\t\t{\"maps/dm2.bsp\",\t0x725B277D,\t/*0x1763B3DA,*/\t0x65F63634},\n\t\t{\"maps/dm3.bsp\",\t0xB1DD97B1,\t/*0x7AC99CDE,*/\t0x15E20DF8},\n\t\t{\"maps/dm4.bsp\",\t0x76A592A0,\t/*0x13799D1F,*/\t0x9C6FE4BF},\n\t\t{\"maps/dm5.bsp\",\t0xD651996F,\t/*0x2DB66BBC,*/\t0xB02D48FD},\n\t\t{\"maps/dm6.bsp\",\t0x33F7D9C9,\t/*0x0EBB386D,*/\t0x5208DA2B},\n//\t\t{\"maps/end.bsp\",\t0x3C87824B,\t/*0xA66198D8,*/\t0xBBD4B4A5},\n\n\t\t//Quake2 Rerelease.\n\t\t//These are listed to remap the rerelease's crc32 checksums from the vanilla maps to the rerelease, so q2e can connect to regular servers.\n\t\t//the reverse is not a concern as there's too many translation errors that way anyway.\n\t\t//yes this is kinda backwards vs the q1 stuff above.\n\n\t\t//MAP NAME\t\t\t\tVAN+fmd4\tVAN+crc32\tREMST+fmd4 REMST+crc32\n//base12/pak0\n//\t\t{\"maps/base1.bsp\",\t\t/*0x0,*/ 0xc49cac93, /*0x0,*/ 0x8939212b},\t//expanded area and some wallhacky bits and areaportal screwups\n\t\t//{\"maps/base2.bsp\",\t\t/*0x0,*/ 0x49ed539e, /*0x0,*/ 0xda61be7b},\n\t\t//{\"maps/base3.bsp\",\t\t/*0x0,*/ 0xf29055e4, /*0x0,*/ 0x13ac20a1},\n\t\t//{\"maps/biggun.bsp\",\t\t/*0x0,*/ 0x0b95a4ae, /*0x0,*/ 0x7cae003f},\n\t\t//{\"maps/boss1.bsp\",\t\t/*0x0,*/ 0x7a52514a, /*0x0,*/ 0xeeff1917},\n\t\t//{\"maps/boss2.bsp\",\t\t/*0x0,*/ 0x81ac4371, /*0x0,*/ 0x7b6392d6},\n\t\t//{\"maps/bunk1.bsp\",\t\t/*0x0,*/ 0xa37f1a6d, /*0x0,*/ 0xe529ac52},\n\t\t//{\"maps/city1.bsp\",\t\t/*0x0,*/ 0x4fd6239b, /*0x0,*/ 0x8876eb6a},\n\t\t//{\"maps/city2.bsp\",\t\t/*0x0,*/ 0x6f7768d8, /*0x0,*/ 0xd0797f11},\n\t\t//{\"maps/city3.bsp\",\t\t/*0x0,*/ 0x3809bd4e, /*0x0,*/ 0xb7135bdd},\n\t\t//{\"maps/command.bsp\",\t/*0x0,*/ 0x80d7b67c, /*0x0,*/ 0x66e404f5},\n\t\t//{\"maps/cool1.bsp\",\t\t/*0x0,*/ 0xeabfd863, /*0x0,*/ 0xbbe51fc9},\n\t\t//{\"maps/fact1.bsp\",\t\t/*0x0,*/ 0xfba460e5, /*0x0,*/ 0xf9bf8d9f},\n\t\t//{\"maps/fact2.bsp\",\t\t/*0x0,*/ 0x6655c1a5, /*0x0,*/ 0xb2cc8d37},\n\t\t//{\"maps/fact3.bsp\",\t\t/*0x0,*/ 0xabe9c595, /*0x0,*/ 0xb64f87d4},\n\t\t//{\"maps/hangar1.bsp\",\t/*0x0,*/ 0x5893e66d, /*0x0,*/ 0x1334d867},\n\t\t//{\"maps/hangar2.bsp\",\t/*0x0,*/ 0x933c0a95, /*0x0,*/ 0x077e9ba3},\n\t\t//{\"maps/jail1.bsp\",\t\t/*0x0,*/ 0xee5e64ec, /*0x0,*/ 0x158a1107},\n\t\t//{\"maps/jail2.bsp\",\t\t/*0x0,*/ 0x154ae1bf, /*0x0,*/ 0x2921f388},\n\t\t//{\"maps/jail3.bsp\",\t\t/*0x0,*/ 0x8f3de333, /*0x0,*/ 0x32554a02},\n\t\t//{\"maps/jail4.bsp\",\t\t/*0x0,*/ 0xe66ec160, /*0x0,*/ 0xb1013e3b},\n\t\t//{\"maps/jail5.bsp\",\t\t/*0x0,*/ 0x3af55df6, /*0x0,*/ 0xbd077c5b},\n\t\t//{\"maps/lab.bsp\",\t\t/*0x0,*/ 0xf2bc9c9a, /*0x0,*/ 0x4eea0f97},\n\t\t//{\"maps/mine1.bsp\",\t\t/*0x0,*/ 0x31db729e, /*0x0,*/ 0x5fc5f3cf},\n\t\t//{\"maps/mine2.bsp\",\t\t/*0x0,*/ 0xf8c3c330, /*0x0,*/ 0x35eacacb},\n\t\t//{\"maps/mine3.bsp\",\t\t/*0x0,*/ 0x1ba1ba64, /*0x0,*/ 0x6c8e3f66},\n\t\t//{\"maps/mine4.bsp\",\t\t/*0x0,*/ 0x6d4d9c98, /*0x0,*/ 0x48a30d5d},\n\t\t//{\"maps/mintro.bsp\",\t\t/*0x0,*/ 0x15bcf39c, /*0x0,*/ 0xfb72d23f},\n\t\t//{\"maps/power1.bsp\",\t\t/*0x0,*/ 0x6e9407f7, /*0x0,*/ 0x8d15b695},\n\t\t//{\"maps/power2.bsp\",\t\t/*0x0,*/ 0xd64d63b5, /*0x0,*/ 0xcf9fd9f5},\n\t\t//{\"maps/security.bsp\",\t/*0x0,*/ 0xc75d06ff, /*0x0,*/ 0xcb7a0229},\n\t\t//{\"maps/space.bsp\",\t\t/*0x0,*/ 0xe9b1ca59, /*0x0,*/ 0x34678329},\n\t\t//{\"maps/strike.bsp\",\t\t/*0x0,*/ 0x373acf3b, /*0x0,*/ 0x1e6aa8da},\n\t\t//{\"maps/train.bsp\",\t\t/*0x0,*/ 0x339a6bf3, /*0x0,*/ 0x1c6b7d5c},\n\t\t//{\"maps/ware1.bsp\",\t\t/*0x0,*/ 0x039d00bc, /*0x0,*/ 0x34713cb1},\n\t\t//{\"maps/ware2.bsp\",\t\t/*0x0,*/ 0xe25b761e, /*0x0,*/ 0x00c06d11},\n\t\t//{\"maps/waste1.bsp\",\t\t/*0x0,*/ 0x14f41c2e, /*0x0,*/ 0x33dd40ff},\n\t\t//{\"maps/waste2.bsp\",\t\t/*0x0,*/ 0x91267752, /*0x0,*/ 0x5b53de97},\n\t\t//{\"maps/waste3.bsp\",\t\t/*0x0,*/ 0x7658206d, /*0x0,*/ 0x80101af0},\n\n\t\t//baseq2/pak1.pak\n\t\t{\"maps/q2dm1.bsp\",\t\t/*0x0,*/ 0x6cc8eda7, /*0x0,*/ 0x23393278},\n\t\t{\"maps/q2dm2.bsp\",\t\t/*0x0,*/ 0x60a8b392, /*0x0,*/ 0x2f03a689},\n//\t\t{\"maps/q2dm3.bsp\",\t\t/*0x0,*/ 0xdc3aac9b, /*0x0,*/ 0x378903ee},\t//bmodel snafus\n\t\t{\"maps/q2dm4.bsp\",\t\t/*0x0,*/ 0x87db6388, /*0x0,*/ 0xa6b504da},\n\t\t{\"maps/q2dm5.bsp\",\t\t/*0x0,*/ 0x7da73bb5, /*0x0,*/ 0xc639cbb7},\n\t\t{\"maps/q2dm6.bsp\",\t\t/*0x0,*/ 0xc63f6546, /*0x0,*/ 0xbe784b8a},\n\t\t{\"maps/q2dm7.bsp\",\t\t/*0x0,*/ 0x39f11899, /*0x0,*/ 0xea6ac852},\n//\t\t{\"maps/q2dm8.bsp\",\t\t/*0x0,*/ 0x8659ee44, /*0x0,*/ 0x09a423b2},\t//crates are missing\n\n//\t\t//one-offs\n//\t\t{\"maps/base64.bsp\",\t\t/*0x0,*/ 0x0, /*0x0,*/ 0x7db2883f},\n//\t\t{\"maps/city64.bsp\",\t\t/*0x0,*/ 0x0, /*0x0,*/ 0x635eb291},\n//\t\t{\"maps/sewer64.bsp\",\t/*0x0,*/ 0x0, /*0x0,*/ 0x46da9e37},\n\t\t//mg\n//\t\t{\"maps/mgdm1.bsp\",\t\t/*0x0,*/ 0x0, /*0x0,*/ 0x826e737a},\n//\t\t{\"maps/mgu1m1.bsp\",\t\t/*0x0,*/ 0x0, /*0x0,*/ 0xc27e2cef},\n//\t\t{\"maps/mgu1m2.bsp\",\t\t/*0x0,*/ 0x0, /*0x0,*/ 0x1ec516fa},\n//\t\t{\"maps/mgu1m3.bsp\",\t\t/*0x0,*/ 0x0, /*0x0,*/ 0xb9f13047},\n//\t\t{\"maps/mgu1m4.bsp\",\t\t/*0x0,*/ 0x0, /*0x0,*/ 0x8211337e},\n//\t\t{\"maps/mgu1m5.bsp\",\t\t/*0x0,*/ 0x0, /*0x0,*/ 0x1549c505},\n//\t\t{\"maps/mgu1trial.bsp\",\t/*0x0,*/ 0x0, /*0x0,*/ 0x0f75a608},\n//\t\t{\"maps/mgu2m1.bsp\",\t\t/*0x0,*/ 0x0, /*0x0,*/ 0x4a769bb0},\n//\t\t{\"maps/mgu2m2.bsp\",\t\t/*0x0,*/ 0x0, /*0x0,*/ 0xd307cad8},\n//\t\t{\"maps/mgu2m3.bsp\",\t\t/*0x0,*/ 0x0, /*0x0,*/ 0xd9352a68},\n//\t\t{\"maps/mgu3m1.bsp\",\t\t/*0x0,*/ 0x0, /*0x0,*/ 0x5ab2ea83},\n//\t\t{\"maps/mgu3m2.bsp\",\t\t/*0x0,*/ 0x0, /*0x0,*/ 0x4952301e},\n//\t\t{\"maps/mgu3m3.bsp\",\t\t/*0x0,*/ 0x0, /*0x0,*/ 0x7ed253fa},\n//\t\t{\"maps/mgu3m4.bsp\",\t\t/*0x0,*/ 0x0, /*0x0,*/ 0xc91edb2e},\n//\t\t{\"maps/mgu3secret.bsp\",\t/*0x0,*/ 0x0, /*0x0,*/ 0xdb50aa0d},\n//\t\t{\"maps/mgu4m1.bsp\",\t\t/*0x0,*/ 0x0, /*0x0,*/ 0x5558556a},\n//\t\t{\"maps/mgu4m2.bsp\",\t\t/*0x0,*/ 0x0, /*0x0,*/ 0x32090016},\n//\t\t{\"maps/mgu4m3.bsp\",\t\t/*0x0,*/ 0x0, /*0x0,*/ 0xa60d1e9a},\n//\t\t{\"maps/mgu4trial.bsp\",\t/*0x0,*/ 0x0, /*0x0,*/ 0x5cb612d1},\n//\t\t{\"maps/mgu5m1.bsp\",\t\t/*0x0,*/ 0x0, /*0x0,*/ 0x60d40817},\n//\t\t{\"maps/mgu5m2.bsp\",\t\t/*0x0,*/ 0x0, /*0x0,*/ 0x88083b2f},\n//\t\t{\"maps/mgu5m3.bsp\",\t\t/*0x0,*/ 0x0, /*0x0,*/ 0x4dc5140a},\n//\t\t{\"maps/mgu5trial.bsp\",\t/*0x0,*/ 0x0, /*0x0,*/ 0x4808e583},\n//\t\t{\"maps/mgu6m1.bsp\",\t\t/*0x0,*/ 0x0, /*0x0,*/ 0xbcc16455},\n//\t\t{\"maps/mgu6m2.bsp\",\t\t/*0x0,*/ 0x0, /*0x0,*/ 0x6313d469},\n//\t\t{\"maps/mgu6m3.bsp\",\t\t/*0x0,*/ 0x0, /*0x0,*/ 0x2651b5f7},\n//\t\t{\"maps/mgu6trial.bsp\",\t/*0x0,*/ 0x0, /*0x0,*/ 0xfabf1b9f},\n//\t\t{\"maps/mguboss.bsp\",\t/*0x0,*/ 0x0, /*0x0,*/ 0x12733e70},\n//\t\t{\"maps/mguhub.bsp\",\t\t/*0x0,*/ 0x0, /*0x0,*/ 0x51fbdd73},\n//\n//\t\t//new\n//\t\t{\"maps/ndctf0.bsp\",\t\t/*0x0,*/ 0x0, /*0x0,*/ 0xa8c81b2b},\n//\t\t{\"maps/q2kctf1.bsp\",\t/*0x0,*/ 0x0, /*0x0,*/ 0x8d176d56},\n//\t\t{\"maps/q2kctf2.bsp\",\t/*0x0,*/ 0x0, /*0x0,*/ 0x01a32428},\n//\t\t{\"maps/tutorial.bsp\",\t/*0x0,*/ 0x0, /*0x0,*/ 0x8869d954},\n\n\t\t//q2ctf maps\n\t\t//{\"maps/q2ctf1.bsp\",\t\t/*0x0,*/ 0xe067a203, /*0x0,*/ 0x6143b5e7},\n\t\t//{\"maps/q2ctf2.bsp\",\t\t/*0x0,*/ 0x28030fe8, /*0x0,*/ 0x5c71d212},\n\t\t//{\"maps/q2ctf3.bsp\",\t\t/*0x0,*/ 0xca29c6a3, /*0x0,*/ 0x59bb4823},\n\t\t//{\"maps/q2ctf4.bsp\",\t\t/*0x0,*/ 0x2fa28832, /*0x0,*/ 0x63fef5de},\n\t\t//{\"maps/q2ctf5.bsp\",\t\t/*0x0,*/ 0x033ccd79, /*0x0,*/ 0x98116fdd},\n//\t\t{\"maps/q2ctf6.bsp\",\t\t/*0x0,*/ 0xe0f199b9, /*0x0,*/ 0x0},\t//no idea why these are missing in the rerelease..\n//\t\t{\"maps/q2ctf7.bsp\",\t\t/*0x0,*/ 0x390abd05, /*0x0,*/ 0x0},\n//\t\t{\"maps/q2ctf8.bsp\",\t\t/*0x0,*/ 0x3ba7c491, /*0x0,*/ 0x0},\n\n\t\t//xatrix maps\n\t\t//{\"maps/badlands.bsp\",\t/*0x0,*/ 0xafa0e22e, /*0x0,*/ 0x203ffacb},\n\t\t//{\"maps/industry.bsp\",\t/*0x0,*/ 0x876bd244, /*0x0,*/ 0x3cf0c2bd},\n\t\t//{\"maps/outbase.bsp\",\t/*0x0,*/ 0xe7531d00, /*0x0,*/ 0x1c36a9e0},\n\t\t//{\"maps/refinery.bsp\",\t/*0x0,*/ 0xe4e81e7b, /*0x0,*/ 0xaf61d172},\n\t\t//{\"maps/w_treat.bsp\",\t/*0x0,*/ 0xde9eb38c, /*0x0,*/ 0x4a3685fe},\n\t\t//{\"maps/xcompnd1.bsp\",\t/*0x0,*/ 0xb3540a91, /*0x0,*/ 0x24bc9f6f},\n\t\t//{\"maps/xcompnd2.bsp\",\t/*0x0,*/ 0xe4745192, /*0x0,*/ 0x55df1931},\n\t\t//{\"maps/xdm1.bsp\",\t\t/*0x0,*/ 0x3d48b316, /*0x0,*/ 0x26a71790},\n\t\t//{\"maps/xdm2.bsp\",\t\t/*0x0,*/ 0x1152e7c4, /*0x0,*/ 0x11db3085},\n\t\t//{\"maps/xdm3.bsp\",\t\t/*0x0,*/ 0xdb97aeac, /*0x0,*/ 0xb60a8631},\n\t\t//{\"maps/xdm4.bsp\",\t\t/*0x0,*/ 0xeef68945, /*0x0,*/ 0x6e070a92},\n\t\t//{\"maps/xdm5.bsp\",\t\t/*0x0,*/ 0x47c1f3a3, /*0x0,*/ 0x558d4dd6},\n\t\t//{\"maps/xdm6.bsp\",\t\t/*0x0,*/ 0x852def91, /*0x0,*/ 0x3cb70b50},\n\t\t//{\"maps/xdm7.bsp\",\t\t/*0x0,*/ 0xc4d93896, /*0x0,*/ 0xc0de3235},\n\t\t//{\"maps/xhangar1.bsp\",\t/*0x0,*/ 0x9e18dceb, /*0x0,*/ 0x10de03f6},\n\t\t//{\"maps/xhangar2.bsp\",\t/*0x0,*/ 0x781c8f17, /*0x0,*/ 0xd9f92aa3},\n\t\t//{\"maps/xintell.bsp\",\t/*0x0,*/ 0x5cf61c9b, /*0x0,*/ 0x2c979acf},\n\t\t//{\"maps/xmoon1.bsp\",\t\t/*0x0,*/ 0xc2ec98f6, /*0x0,*/ 0x57f1373c},\n\t\t//{\"maps/xmoon2.bsp\",\t\t/*0x0,*/ 0x79cdedf8, /*0x0,*/ 0xc2236a38},\n\t\t//{\"maps/xreactor.bsp\",\t/*0x0,*/ 0xa32f926d, /*0x0,*/ 0x5dc0db58},\n\t\t//{\"maps/xsewer1.bsp\",\t/*0x0,*/ 0x53f393b3, /*0x0,*/ 0x68c01403},\n\t\t//{\"maps/xsewer2.bsp\",\t/*0x0,*/ 0xdae8d63f, /*0x0,*/ 0xc78c8261},\n\t\t//{\"maps/xship.bsp\",\t\t/*0x0,*/ 0xcaae64c3, /*0x0,*/ 0x16434a51},\n\t\t//{\"maps/xswamp.bsp\",\t\t/*0x0,*/ 0x7a01ffe3, /*0x0,*/ 0x92852525},\n\t\t//{\"maps/xware.bsp\",\t\t/*0x0,*/ 0x769d834c, /*0x0,*/ 0xa21dad68},\n\n\t\t//(q2) rogue maps\n\t\t//{\"maps/rammo1.bsp\",\t\t/*0x0,*/ 0x157c38b5, /*0x0,*/ 0x70778442},\n\t\t//{\"maps/rammo2.bsp\",\t\t/*0x0,*/ 0xe59eb4f3, /*0x0,*/ 0xb9a6bb64},\n\t\t//{\"maps/rbase1.bsp\",\t\t/*0x0,*/ 0xfe907818, /*0x0,*/ 0xcc316310},\n\t\t//{\"maps/rbase2.bsp\",\t\t/*0x0,*/ 0x6a9f5a26, /*0x0,*/ 0xd148fd82},\n\t\t//{\"maps/rboss.bsp\",\t\t/*0x0,*/ 0x5c3167e7, /*0x0,*/ 0x0ba861c9},\n\t\t//{\"maps/rdm1.bsp\",\t\t/*0x0,*/ 0x9d581b00, /*0x0,*/ 0xcc0dc613},\n\t\t//{\"maps/rdm2.bsp\",\t\t/*0x0,*/ 0xcaef085e, /*0x0,*/ 0xcb93d84b},\n\t\t//{\"maps/rdm3.bsp\",\t\t/*0x0,*/ 0xecd65aea, /*0x0,*/ 0x2c334d25},\n\t\t//{\"maps/rdm4.bsp\",\t\t/*0x0,*/ 0xb0e01e67, /*0x0,*/ 0xc9d989ea},\n\t\t//{\"maps/rdm5.bsp\",\t\t/*0x0,*/ 0x998f2929, /*0x0,*/ 0xf4e5d735},\n\t\t//{\"maps/rdm6.bsp\",\t\t/*0x0,*/ 0x06041298, /*0x0,*/ 0x7dc53173},\n\t\t//{\"maps/rdm7.bsp\",\t\t/*0x0,*/ 0x5d1a6d8e, /*0x0,*/ 0x8c618a24},\n\t\t//{\"maps/rdm8.bsp\",\t\t/*0x0,*/ 0x9563932d, /*0x0,*/ 0x07b32d04},\n\t\t//{\"maps/rdm9.bsp\",\t\t/*0x0,*/ 0x6abbd719, /*0x0,*/ 0x726d4859},\n\t\t//{\"maps/rdm10.bsp\",\t\t/*0x0,*/ 0xe5097216, /*0x0,*/ 0x54b0b786},\n\t\t//{\"maps/rdm11.bsp\",\t\t/*0x0,*/ 0x55602288, /*0x0,*/ 0x890dc692},\n\t\t//{\"maps/rdm12.bsp\",\t\t/*0x0,*/ 0xeadab431, /*0x0,*/ 0x762197d8},\n\t\t//{\"maps/rdm13.bsp\",\t\t/*0x0,*/ 0x8b51e2d9, /*0x0,*/ 0x20678587},\n\t\t//{\"maps/rdm14.bsp\",\t\t/*0x0,*/ 0x9f12a7af, /*0x0,*/ 0xe22f5e58},\n\t\t//{\"maps/rhangar1.bsp\",\t/*0x0,*/ 0x29f4a50f, /*0x0,*/ 0xf5b1eeee},\n\t\t//{\"maps/rhangar2.bsp\",\t/*0x0,*/ 0xfa92ab46, /*0x0,*/ 0x6167fa5e},\n\t\t//{\"maps/rlava1.bsp\",\t\t/*0x0,*/ 0x54a8f40b, /*0x0,*/ 0xa16d41ec},\n\t\t//{\"maps/rlava2.bsp\",\t\t/*0x0,*/ 0x994cec5d, /*0x0,*/ 0x92e9c884},\n\t\t//{\"maps/rmine1.bsp\",\t\t/*0x0,*/ 0xf5e042cf, /*0x0,*/ 0x7a9eb9a8},\n\t\t//{\"maps/rmine2.bsp\",\t\t/*0x0,*/ 0xeb8ddc19, /*0x0,*/ 0xa4e07d51},\n\t\t//{\"maps/rsewer1.bsp\",\t/*0x0,*/ 0x934df93c, /*0x0,*/ 0x605443ab},\n\t\t//{\"maps/rsewer2.bsp\",\t/*0x0,*/ 0x07e18c79, /*0x0,*/ 0x62513621},\n//\t\t{\"maps/runit2.bsp\",\t\t/*0x0,*/ 0x074e46de, /*0x0,*/ 0x},\t//pointless content-skip rooms for coop, I guess\n//\t\t{\"maps/runit3.bsp\",\t\t/*0x0,*/ 0x36c066a7, /*0x0,*/ 0x},\n//\t\t{\"maps/runit4.bsp\",\t\t/*0x0,*/ 0xd31d0e32, /*0x0,*/ 0x},\n\t\t//{\"maps/rware1.bsp\",\t\t/*0x0,*/ 0xbba033a9, /*0x0,*/ 0x031eda0a},\n\t\t//{\"maps/rware2.bsp\",\t\t/*0x0,*/ 0xc2993fed, /*0x0,*/ 0xcba25fcf},\n\t};\n\tunsigned int i;\n\tfor (i = 0; i < sizeof(sums)/sizeof(sums[0]); i++)\n\t{\n\t\tif (checksum == sums[i].gpl2)\n\t\t\tif (!Q_strcasecmp(model->name, sums[i].name))\n\t\t\t\treturn sums[i].id12;\n\t}\n#endif\n\treturn checksum;\n}\n#endif\n\nstatic char Base64_Encode(int byt)\n{\n\tif (byt >= 0 && byt < 26)\n\t\treturn 'A' + byt - 0;\n\tif (byt >= 26 && byt < 52)\n\t\treturn 'a' + byt - 26;\n\tif (byt >= 52 && byt < 62)\n\t\treturn '0' + byt - 52;\n\tif (byt == 62)\n\t\treturn '+';\n\tif (byt == 63)\n\t\treturn '/';\n\treturn '!';\n}\nstatic int Base64_Decode(char inp)\n{\n\tif (inp >= 'A' && inp <= 'Z')\n\t\treturn (inp-'A') + 0;\n\tif (inp >= 'a' && inp <= 'z')\n\t\treturn (inp-'a') + 26;\n\tif (inp >= '0' && inp <= '9')\n\t\treturn (inp-'0') + 52;\n\tif (inp == '+' || inp == '-')\n\t\treturn 62;\n\tif (inp == '/' || inp == '_')\n\t\treturn 63;\n\t//if (inp == '=') //padding char\n\treturn 0;\t//invalid\n}\n\nsize_t Base64_EncodeBlock(const qbyte *in, size_t length, char *out, size_t outsize)\n{\n\tchar *start = out;\n\tchar *end = out+outsize-1;\n\tunsigned int v;\n\twhile(length > 0)\n\t{\n\t\tv = 0;\n\t\tif (length > 0)\n\t\t\tv |= in[0]<<16;\n\t\tif (length > 1)\n\t\t\tv |= in[1]<<8;\n\t\tif (length > 2)\n\t\t\tv |= in[2]<<0;\n\n\t\tif (out < end) *out++ = (length>=1)?Base64_Encode((v>>18)&63):'=';\n\t\tif (out < end) *out++ = (length>=1)?Base64_Encode((v>>12)&63):'=';\n\t\tif (out < end) *out++ = (length>=2)?Base64_Encode((v>>6)&63):'=';\n\t\tif (out < end) *out++ = (length>=3)?Base64_Encode((v>>0)&63):'=';\n\n\t\tin+=3;\n\t\tif (length <= 3)\n\t\t\tbreak;\n\t\tlength -= 3;\n\t}\n\tend++;\n\tif (out < end)\n\t\t*out = 0;\n\treturn out-start;\n}\nsize_t Base64_EncodeBlockURI(const qbyte *in, size_t length, char *out, size_t outsize)\n{\t//special uri-safe version (also trims)\n\toutsize = Base64_EncodeBlock(in, length, out, outsize);\n\tfor (length = 0; length < outsize; length++)\n\t{\n\t\tif (out[length] == '+')\n\t\t\tout[length] = '-';\n\t\telse if (out[length] == '/')\n\t\t\tout[length] = '_';\n\t\telse if (out[length] == '=')\n\t\t{\t//truncate it here.\n\t\t\tout[length] = 0;\n\t\t\treturn length;\n\t\t}\n\t}\n\treturn outsize;\n}\nsize_t Base64_DecodeBlock(const char *in, const char *in_end, qbyte *out, size_t outsize)\n{\n\tqbyte *start = out;\n\tunsigned int v;\n\tif (!in_end)\n\t\tin_end = in + strlen(in);\n\tif (!out)\n\t\treturn ((in_end-in+3)/4)*3 + 1;\t//upper estimate, with null terminator for convienience.\n\n\tfor (; outsize > 1;)\n\t{\n\t\twhile(*in > 0 && *in < ' ')\n\t\t\tin++;\n\t\tif (in >= in_end || !*in || outsize < 1)\n\t\t\tbreak;\t//end of message when EOF, otherwise error\n\t\tv  = Base64_Decode(*in++)<<18;\n\t\twhile(*in > 0 && *in < ' ')\n\t\t\tin++;\n\t\tif (in >= in_end || !*in || outsize < 1)\n\t\t\tbreak;\t//some kind of error\n\t\tv |= Base64_Decode(*in++)<<12;\n\t\t*out++ = (v>>16)&0xff;\n\t\tif (in >= in_end || *in == '=' || !*in || outsize < 2)\n\t\t\tbreak;\t//end of message when '=', otherwise error\n\t\tv |= Base64_Decode(*in++)<<6;\n\t\t*out++ = (v>>8)&0xff;\n\t\tif (in >= in_end || *in == '=' || !*in || outsize < 3)\n\t\t\tbreak;\t//end of message when '=', otherwise error\n\t\tv |= Base64_Decode(*in++)<<0;\n\t\t*out++ = (v>>0)&0xff;\n\t\toutsize -= 3;\n\t}\n\treturn out-start;\t//total written (no null, output is considered binary)\n}\nsize_t Base16_DecodeBlock(const char *in, qbyte *out, size_t outsize)\n{\n\tqbyte *start = out;\n\tif (!out)\n\t\treturn ((strlen(in)+1)/2) + 1;\n\n\tfor (; ishexcode(in[0]) && ishexcode(in[1]) && outsize > 0; outsize--, in+=2)\n\t\t*out++ = (dehex(in[0])<<4) | dehex(in[1]);\n\treturn out-start;\n}\nsize_t Base16_EncodeBlock(const char *in, size_t length, qbyte *out, size_t outsize)\n{\n\tconst char tab[16] = \"0123456789abcdef\";\n\tqbyte *start = out;\n\tif (!out)\n\t\treturn (length*2) + 1;\n\n\tif (outsize > length*2)\n\t\t*out = 0;\n\twhile (length --> 0)\n\t{\n\t\t*out++ = tab[(*in>>4)&0xf];\n\t\t*out++ = tab[(*in>>0)&0xf];\n\t\tin++;\n\t}\n\treturn out-start;\n}\n\n/*\n  Info Buffers\n*/\nconst char *basicuserinfos[] =\t//these are used by the client itself, and ignored when the user isn't using csqc.\n{\n\t\"*\",\t//special: all '*' prefixed keys\n\t\"name\",\n\t\"team\",\n\t\"skin\",\n\t\"topcolor\",\n\t\"bottomcolor\",\n\t\"chat\",\t//ezquake's afk indicators\n\tNULL\n};\nconst char *privateuserinfos[] =\t//these can be sent to the server, but must NOT be reported to other clients.\n{\n\t\"_\",\t\t//special prefix: ignore comments\n\t\"password\",\t//many users will forget to clear it after.\n\t\"prx\",\t\t//if someone has this set, don't bother broadcasting it.\n\t\"*ip\",\t\t//this is the ip the client used to connect to the server. this isn't useful as any proxy that would affect it can trivially strip/rewrite it anyway.\n\tNULL\n};\n\nvoid InfoSync_Remove(infosync_t *sync, size_t k)\n{\n\tsync->numkeys--;\n\tZ_Free(sync->keys[k].name);\n\tmemmove(sync->keys + k, sync->keys + k + 1, sizeof(*sync->keys)*(sync->numkeys-k));\n}\nvoid InfoSync_Clear(infosync_t *sync)\n{\n\tsize_t k;\n\tfor (k = 0; k < sync->numkeys; k++)\n\t\tZ_Free(sync->keys[k].name);\n\tZ_Free(sync->keys);\n\tsync->keys = NULL;\n\tsync->numkeys = 0;\n}\nvoid InfoSync_Strip(infosync_t *sync, void *context)\n{\n\tsize_t k;\n\tif (!sync->numkeys)\n\t\treturn;\n\n\tfor (k = 0; k < sync->numkeys; )\n\t{\n\t\tif (sync->keys[k].context == context)\n\t\t{\n\t\t\tsync->numkeys--;\n\t\t\tZ_Free(sync->keys[k].name);\n\t\t\tmemmove(sync->keys + k, sync->keys + k + 1, sizeof(*sync->keys)*(sync->numkeys-k));\n\t\t}\n\t\telse\n\t\t\tk++;\n\t}\n}\nvoid InfoSync_Add(infosync_t *sync, void *context, const char *name)\n{\n\tsize_t k;\n\n\tfor (k = 0; k < sync->numkeys; k++)\n\t{\n\t\tif (sync->keys[k].context == context && !strcmp(sync->keys[k].name, name))\n\t\t{\t//urr, it changed while we were sending it. reset!\n\t\t\tsync->keys[k].syncpos = 0;\n\t\t\treturn;\n\t\t}\n\t}\n\n\tif (!ZF_ReallocElements((void**)&sync->keys, &sync->numkeys, sync->numkeys+1, sizeof(*sync->keys)))\n\t\treturn; //out of memory!\n\tsync->keys[k].context = context;\n\tsync->keys[k].name = Z_StrDup(name);\n\tsync->keys[k].syncpos = 0;\n}\n\nstatic qboolean InfoBuf_NeedsEncoding(const char *str, size_t size)\n{\n\tconst char *c, *e = str+size;\n\tfor (c = str; c < e; c++)\n\t{\n\t\tswitch((unsigned char)*c)\n\t\t{\n\t\tcase 255:\t//invalid for vanilla qw, and also used for special encoding\n\t\tcase '\\\\':\t//abiguity with end-of-token\n\t\tcase '\\\"':\t//parsing often sends these enclosed in quotes\n\t\tcase '\\n':\t//REALLY screws up parsing\n\t\tcase '\\r':\t//generally bad form\n\t\tcase 0:\t\t//are we really doing this?\n\t\tcase '$':\t//a number of engines like expanding things inside quotes. make sure that cannot ever happen.\n\t\tcase ';':\t//in case someone manages to break out of quotes\n\t\t\treturn true;\n\t\t}\n\t}\n\treturn false;\n}\nqboolean InfoBuf_FindKey (infobuf_t *info, const char *key, size_t *idx)\n{\n\tsize_t k;\n\tfor (k = 0; k < info->numkeys; k++)\n\t{\n\t\tif (!strcmp(info->keys[k].name, key))\n\t\t{\n\t\t\t*idx = k;\n\t\t\treturn true;\n\t\t}\n\t}\n\treturn false;\n}\nconst char *InfoBuf_KeyForNumber(infobuf_t *info, int idx)\n{\t//allows itteration, removal can change the names of this/higher keys, but not lower keys.\n\tif (idx >= 0 && idx < info->numkeys)\n\t\treturn info->keys[idx].name;\n\treturn NULL;\n}\nchar *InfoBuf_ReadKey (infobuf_t *info, const char *key, char *outbuf, size_t outsize)\t//not to be used with blobs. writes to a user-supplied buffer\n{\n\tsize_t k;\n\tif (InfoBuf_FindKey(info, key, &k) && !info->keys[k].partial)\n\t{\n\t\tQ_strncpyz(outbuf, info->keys[k].value, outsize);\n\t\treturn outbuf;\n\t}\n\t*outbuf = 0;\n\treturn outbuf;\n}\nchar *InfoBuf_ValueForKey (infobuf_t *info, const char *key)\t//not to be used with blobs. cycles buffer and imposes a length limit.\n{\n\tstatic\tchar value[4][1024];\t// use multiple buffers so compares work without stomping on each other\n\tstatic\tint\tvalueindex;\n\tCOM_AssertMainThread(\"InfoBuf_ValueForKey\");\n\tvalueindex = (valueindex+1)&3;\n\treturn InfoBuf_ReadKey(info, key, value[valueindex], sizeof(value[valueindex]));\n}\nconst char *InfoBuf_BlobForKey (infobuf_t *info, const char *key, size_t *blobsize, qboolean *large)\t//obtains a direct pointer to temp memory\n{\n\tsize_t k;\n\tif (InfoBuf_FindKey(info, key, &k) && !info->keys[k].partial)\n\t{\n\t\tif (large)\n\t\t\t*large = info->keys[k].large;\n\t\t*blobsize = info->keys[k].size;\n\t\treturn info->keys[k].value;\n\t}\n\tif (large)\n\t\t*large = InfoBuf_NeedsEncoding(key, strlen(key));\n\t*blobsize = 0;\n\treturn NULL;\n}\nqboolean InfoBuf_RemoveKey (infobuf_t *info, const char *key)\n{\n\tsize_t k;\n\tif (InfoBuf_FindKey(info, key, &k))\n\t{\n\t\tchar *kn = info->keys[k].name;\t//paranoid\n\t\tZ_Free(info->keys[k].value);\n\t\tinfo->numkeys--;\n\t\tinfo->totalsize -= strlen(info->keys[k].name)+2;\n\t\tinfo->totalsize -= info->keys[k].size;\n\t\tmemmove(info->keys+k+0, info->keys+k+1, sizeof(*info->keys) * (info->numkeys-k));\n\n\t\tif (info->ChangeCB)\n\t\t\tinfo->ChangeCB(info->ChangeCTX, kn);\n\t\tZ_Free(kn);\n\t\treturn true;\t//only one entry per key, so we can give up here\n\t}\n\treturn false;\n}\nchar *InfoBuf_DecodeString(const char *instart, const char *inend, size_t *sz)\n{\n\tchar *ret = Z_Malloc(inend-instart + 1);\t//guarenteed to end up equal or smaller\n\tint i;\n\tunsigned int v;\n\tif (*instart == '\\xff')\n\t{\t//base64-coded\n\t\tinstart++;\n\t\tfor (i = 0; instart+1 < inend;)\n\t\t{\n\t\t\tv  = Base64_Decode(*instart++)<<18;\n\t\t\tv |= Base64_Decode(*instart++)<<12;\n\t\t\tret[i++] = (v>>16)&0xff;\n\t\t\tif (instart >= inend || *instart == '=')\n\t\t\t\tbreak;\n\t\t\tv |= Base64_Decode(*instart++)<<6;\n\t\t\tret[i++] = (v>>8)&0xff;\n\t\t\tif (instart >= inend || *instart == '=')\n\t\t\t\tbreak;\n\t\t\tv |= Base64_Decode(*instart++)<<0;\n\t\t\tret[i++] = (v>>0)&0xff;\n\t\t}\n\t\tret[i] = 0;\n\t\t*sz = i;\n\t}\n\telse\n\t{\t//as-is\n\t\tmemcpy(ret, instart, inend-instart);\n\t\tret[inend-instart] = 0;\n\t\t*sz = inend-instart;\n\t}\n\treturn ret;\n}\n\nstatic qboolean InfoBuf_IsLarge(struct infokey_s *key)\n{\n\tsize_t namesize;\n\tif (key->partial)\n\t\treturn true;\n\n\tif (key->size >= 64)\n\t\treturn true;\t//value length limits is a thing in vanilla qw.\n\t\t\t\t\t\t//note that qw reads values up to 512, but only sets them up to 64 bytes...\n\t\t\t\t\t\t//probably just so that people don't spot buffer overflows so easily.\n\tnamesize = strlen(key->name);\n\tif (namesize >= 64)\n\t\treturn true;\t//key length limits is a thing in vanilla qw.\n\n\tif (InfoBuf_NeedsEncoding(key->name, namesize))\n\t\treturn true;\n\tif (InfoBuf_NeedsEncoding(key->value, key->size))\n\t\treturn true;\n\treturn false;\n}\n//like InfoBuf_SetStarBlobKey, but understands partials.\nqboolean InfoBuf_SyncReceive (infobuf_t *info, const char *key, size_t keysize, const char *val, size_t valsize, size_t offset, qboolean final)\n{\n\tsize_t k;\n\tsize_t newsize;\n\n\tif (!InfoBuf_FindKey(info, key, &k))\n\t{\t//its new\n\t\tif (!valsize)\n\t\t\treturn false;\t//and not set to anything new either\n\n\t\tif (offset)\n\t\t\treturn false;\t//was missing the initial message...\n\n\t\tk = info->numkeys;\n\t\tif (!ZF_ReallocElements((void**)&info->keys, &info->numkeys, info->numkeys+1, sizeof(*info->keys)))\n\t\t\treturn false; //out of memory!\n\t\tinfo->keys[k].name = Z_StrDup(key);\n\t\tinfo->keys[k].size = 0;\n\t\tinfo->keys[k].value = NULL;\n\t\tinfo->totalsize += strlen(info->keys[k].name)+2;\n\t}\n\telse\n\t{\n\t\tif (!valsize)\t//probably an error.\n\t\t\treturn InfoBuf_RemoveKey(info, key);\n\n\t\tif (offset)\n\t\t{\n\t\t\tif (offset != info->keys[k].size)\t//probably an error... should be progressive.\n\t\t\t\treturn InfoBuf_RemoveKey(info, key);\n\t\t}\n//\t\telse silently truncate.\n\t\tinfo->totalsize -= info->keys[k].size;\n\t}\n\n\tnewsize = offset + valsize;\n\tif (final)\n\t{\t//release any excess memory (which could potentially be in the MB)\n\t\tif (!ZF_ReallocElements((void**)&info->keys[k].value, &info->keys[k].buffersize, newsize+1, 1))\n\t\t\treturn false;\n\t\tinfo->keys[k].buffersize = newsize+1;\n\t}\n\telse\n\t{\n\t\tif (info->keys[k].buffersize < newsize+1)\n\t\t{\n\t\t\tif (!ZF_ReallocElements((void**)&info->keys[k].value, &info->keys[k].buffersize, newsize*2+1, 1))\n\t\t\t\treturn false;\n\t\t}\n\t}\n\tmemcpy(info->keys[k].value+offset, val, valsize);\n\tinfo->keys[k].value[newsize] = 0;\n\tinfo->keys[k].size = newsize;\n\tinfo->keys[k].partial = !final;\n\tinfo->keys[k].large = InfoBuf_IsLarge(&info->keys[k]);\n\tinfo->totalsize += info->keys[k].size;\n\n\tif (final)\n\t\tif (info->ChangeCB)\n\t\t\tinfo->ChangeCB(info->ChangeCTX, key);\n\treturn true;\n}\nqboolean InfoBuf_SetStarBlobKey (infobuf_t *info, const char *key, const char *val, size_t valsize)\n{\n\tsize_t k;\n\tif (!val)\n\t{\n\t\tval = \"\";\n\t\tvalsize = 0;\n\t}\n\n\tif (!InfoBuf_FindKey(info, key, &k))\n\t{\t//its new\n\t\tif (!valsize)\n\t\t\treturn false;\t//and not set to anything new either\n\n\t\tk = info->numkeys;\n\t\tif (!ZF_ReallocElements((void**)&info->keys, &info->numkeys, info->numkeys+1, sizeof(*info->keys)))\n\t\t\treturn false; //out of memory!\n\t\tinfo->keys[k].name = Z_StrDup(key);\n\n\t\tinfo->totalsize += strlen(info->keys[k].name)+2;\n\t}\n\telse\n\t{\n\t\tif (!valsize)\n\t\t\treturn InfoBuf_RemoveKey(info, key);\n\n\t\tif (info->keys[k].size == valsize && !memcmp(info->keys[k].value, val, valsize))\n\t\t\treturn false;\t//nothing new\n\t\tZ_Free(info->keys[k].value);\n\t\tinfo->totalsize -= info->keys[k].size;\n\t}\n\n\tinfo->keys[k].buffersize = valsize+1;\n\tinfo->keys[k].size = valsize;\n\tinfo->keys[k].value = Z_Malloc(info->keys[k].buffersize);\n\tmemcpy(info->keys[k].value, val, valsize);\n\tinfo->keys[k].value[valsize] = 0;\n\tinfo->keys[k].partial = false;\n\tinfo->keys[k].large = InfoBuf_IsLarge(&info->keys[k]);\n\tinfo->totalsize += info->keys[k].size;\n\n\tif (info->ChangeCB)\n\t\tinfo->ChangeCB(info->ChangeCTX, key);\n\treturn true;\n}\nqboolean InfoBuf_SetKey (infobuf_t *info, const char *key, const char *val)\n{\n\t// *keys are meant to be secure (or rather unsettable by the user, preventing spoofing of stuff like *ip)\n\t//\t\tbut note that this is pointless as a hacked client can send whatever initial *keys it wants (they are blocked mid-connection at least)\n\t// * userinfos are always sent even to clients that can't support large infokey blobs\n\tif (*key == '*')\n\t\treturn false;\n\treturn InfoBuf_SetStarBlobKey (info, key, val, strlen(val));\n}\nqboolean InfoBuf_SetStarKey (infobuf_t *info, const char *key, const char *val)\n{\n\treturn InfoBuf_SetStarBlobKey (info, key, val, strlen(val));\n}\n\nvoid InfoBuf_Clear(infobuf_t *info, qboolean all)\n{//if all is false, leaves *keys\n\tsize_t k;\n\tfor (k = info->numkeys; k --> 0; )\n\t{\n\t\tif (all || *info->keys[k].name != '*')\n\t\t{\n\t\t\tZ_Free(info->keys[k].name);\n\t\t\tZ_Free(info->keys[k].value);\n\t\t\tinfo->numkeys--;\n\t\t\tmemmove(info->keys+k+0, info->keys+k+1, sizeof(*info->keys) * (info->numkeys-k));\n\t\t}\n\t}\n\tif (!info->numkeys)\n\t{\n\t\tZ_Free(info->keys);\n\t\tinfo->keys = NULL;\n\t}\n\tinfo->totalsize = 0;\n}\n//the callback reports how much data it splurged.\n/*qboolean InfoBuf_SyncSend(infobuf_t *info, size_t(*cb)(void *ctx, const char *key, const char *data, size_t offset, size_t size), void *ctx)\n{\n\tsize_t k;\n\tfor (k = 0; k < info->numkeys; k++)\n\t{\n\t\tif (!info->keys[k].size)\n\t\t{\t//null keys are actually just present to flag removals.\n\t\t\t//the sync is meant to be reliable, so these can be stripped once the update is sent.\n\t\t\tcb(ctx, info->keys[k].name, NULL, 0, 0);\n\t\t\tZ_Free(info->keys[k].name);\n\t\t\tZ_Free(info->keys[k].value);\n\t\t\tinfo->numkeys--;\n\t\t\tmemmove(info->keys+k, info->keys+k+1, (info->numkeys-k)*sizeof(*info->keys));\n\t\t\treturn true;\n\t\t}\n\t\tif (info->keys[k].syncedsize < info->keys[k].size)\n\t\t{\t//regular update, possibly partial.\n\t\t\tinfo->keys[k].syncedsize += cb(ctx, info->keys[k].name, info->keys[k].value, info->keys[k].syncedsize, info->keys[k].size-info->keys[k].syncedsize);\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\t//nothing to change.\n}*/\nvoid InfoBuf_Clone(infobuf_t *dest, infobuf_t *src)\n{\n\tsize_t k;\n\n\tInfoBuf_Clear(dest, true);\n\n\tdest->numkeys = src->numkeys;\n\tdest->keys = BZ_Malloc(sizeof(*dest->keys) * dest->numkeys);\n\tfor (k = 0; k < dest->numkeys; k++)\n\t{\n\t\tdest->keys[k].partial = src->keys[k].partial;\t//this is a problem. should we just not replicate partials?\n\t\tdest->keys[k].large = src->keys[k].large;\n\t\tdest->keys[k].name = Z_StrDup(src->keys[k].name);\n\t\tdest->keys[k].size = src->keys[k].size;\n\t\tdest->keys[k].value = Z_Malloc(src->keys[k].size+1);\n\t\tmemcpy(dest->keys[k].value, src->keys[k].value, src->keys[k].size);\n\t\tdest->keys[k].value[src->keys[k].size] = 0;\n\n\t\tdest->totalsize += strlen(dest->keys[k].name)+2+dest->keys[k].size;\n\t}\n}\nvoid InfoBuf_FromString(infobuf_t *info, const char *infostring, qboolean append)\n{\n\tif (!append)\n\t\tInfoBuf_Clear(info, true);\n\tif (*infostring && *infostring != '\\\\')\n\t\tCon_Printf(\"InfoBuf_FromString: invalid infostring \\\"%s\\\"\\n\", infostring);\n\n\t//all keys must start with a backslash\n\twhile (*infostring++ == '\\\\')\n\t{\n\t\tconst char *keystart = infostring;\n\t\tconst char *keyend;\n\t\tconst char *valstart;\n\t\tconst char *valend;\n\t\tchar *key;\n\t\tchar *val;\n\t\tsize_t keysize, valsize;\n\t\twhile (*infostring)\n\t\t{\n\t\t\tif (*infostring == '\\\\')\n\t\t\t\tbreak;\n\t\t\telse infostring += 1;\n\t\t}\n\t\tkeyend = infostring;\n\t\tif (*infostring++ != '\\\\')\n\t\t\tbreak;\t//missing value...\n\t\tvalstart = infostring;\n\t\twhile (*infostring)\n\t\t{\n\t\t\tif (*infostring == '\\\\')\n\t\t\t\tbreak;\n\t\t\telse infostring += 1;\n\t\t}\n\t\tvalend = infostring;\n\n\t\tkey = InfoBuf_DecodeString(keystart, keyend, &keysize);\n\t\tval = InfoBuf_DecodeString(valstart, valend, &valsize);\n\t\tInfoBuf_SetStarBlobKey(info, key, val, valsize);\n\t\tZ_Free(key);\n\t\tZ_Free(val);\n\t}\n}\n//internal logic\nstatic qboolean InfoBuf_EncodeString_Internal(const char *n, size_t s, char *out, char *end)\n{\n\tsize_t r = 0;\n\tconst char *c;\n\tif (InfoBuf_NeedsEncoding(n, s))\n\t{\n\t\tunsigned int base64_cur = 0;\n\t\tunsigned int base64_bits = 0;\n\t\tr += 1;\n\t\tif (out < end) *out++ = (char)255;\n\n\t\tfor (c = n; c < n+s; c++)\n\t\t{\n\t\t\tbase64_cur |= *(const unsigned char*)c<<(16-\tbase64_bits);//first byte fills highest bits\n\t\t\tbase64_bits += 8;\n\n\t\t\tif (base64_bits == 24)\n\t\t\t{\n\t\t\t\tr += 4;\n\t\t\t\tif (out < end) *out++ = Base64_Encode((base64_cur>>18)&63);\n\t\t\t\tif (out < end) *out++ = Base64_Encode((base64_cur>>12)&63);\n\t\t\t\tif (out < end) *out++ = Base64_Encode((base64_cur>>6)&63);\n\t\t\t\tif (out < end) *out++ = Base64_Encode((base64_cur>>0)&63);\n\t\t\t\tbase64_bits = 0;\n\t\t\t\tbase64_cur = 0;\n\t\t\t}\n\t\t}\n\t\tif (base64_bits != 0)\n\t\t{\n\t\t\tr += 4;\n\t\t\tif (out < end) *out++ = Base64_Encode((base64_cur>>18)&63);\n\t\t\tif (out < end) *out++ = Base64_Encode((base64_cur>>12)&63);\n\t\t\tif (base64_bits == 8)\n\t\t\t{\n\t\t\t\tif (out < end) *out++ = '=';\n\t\t\t\tif (out < end) *out++ = '=';\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (out < end) *out++ = Base64_Encode((base64_cur>>6)&63);\n\t\t\t\tif (base64_bits == 16)\n\t\t\t\t{\n\t\t\t\t\tif (out < end) *out++ = '=';\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tif (out < end) *out++ = Base64_Encode((base64_cur>>0)&63);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\telse\n\t{\n\t\tfor (c = n; c < n+s; c++)\n\t\t{\n\t\t\tr++;\n\t\t\tif (out < end) *out++ = *c;\n\t\t}\n\t}\n\treturn r;\n}\n//public interface to make things easy\nqboolean InfoBuf_EncodeString(const char *n, size_t s, char *out, size_t outsize)\n{\n\tsize_t l = InfoBuf_EncodeString_Internal(n, s, out, out+outsize);\n\tif (l < outsize)\n\t{\n\t\tout[l] = 0;\n\t\treturn true;\n\t}\n\t*out = 0;\n\treturn false;\n}\nstatic void *InfoBuf_EncodeString_Malloc(const char *n, size_t s)\n{\n\tsize_t l = InfoBuf_EncodeString_Internal(n, s, NULL, NULL);\n\tchar *ret = BZ_Malloc(l+1);\n\tif (!ret || l != InfoBuf_EncodeString_Internal(n, s, ret, ret+l))\n\t\tSys_Error(\"InfoBuf_EncodeString_Malloc: error\\n\");\n\tret[l] = 0;\n\treturn ret;\n}\nstatic size_t InfoBuf_EncodeStringSlash(const char *n, size_t s, char *out, char *end)\n{\n\tsize_t l = 1+InfoBuf_EncodeString_Internal(n, s, out+1, end);\n\tif (out < end)\n\t\t*out = '\\\\';\n\treturn l;\n}\nsize_t InfoBuf_ToString(infobuf_t *info, char *infostring, size_t maxsize, const char **priority, const char **ignore, const char **exclusive, infosync_t *sync, void *synccontext)\n{\n\tsize_t k, r = 1, l;\n\tchar *o = infostring;\n\tchar *e = infostring?infostring + maxsize-1:infostring;\n\tint pri, p;\n\n\tif (sync)\t//if we have a sync object then we just wiped whatever infostrings that were set\n\t\tInfoSync_Strip(sync, synccontext);\n\n\tfor (pri = 0; pri < 2; pri++)\n\t{\n\t\tfor (k = 0; k < info->numkeys; k++)\n\t\t{\n\t\t\tif (exclusive)\n\t\t\t{\n\t\t\t\tfor (l = 0; exclusive[l]; l++)\n\t\t\t\t{\n\t\t\t\t\tif (!strcmp(exclusive[l], info->keys[k].name))\n\t\t\t\t\t\tbreak;\n\t\t\t\t\telse if (exclusive[l][0] == '*' && !exclusive[l][1] && *info->keys[k].name == '*')\n\t\t\t\t\t\tbreak;\t//read-only\n\t\t\t\t\telse if (exclusive[l][0] == '_' && !exclusive[l][1] && *info->keys[k].name == '_')\n\t\t\t\t\t\tbreak;\t//comment\n\t\t\t\t}\n\t\t\t\tif (!exclusive[l])\n\t\t\t\t\tcontinue;\t//ignore when not in the list\n\t\t\t}\n\t\t\tif (ignore)\n\t\t\t{\n\t\t\t\tfor (l = 0; ignore[l]; l++)\n\t\t\t\t{\n\t\t\t\t\tif (!strcmp(ignore[l], info->keys[k].name))\n\t\t\t\t\t\tbreak;\n\t\t\t\t\telse if (ignore[l][0] == '*' && !ignore[l][1] && *info->keys[k].name == '*')\n\t\t\t\t\t\tbreak;\t//read-only\n\t\t\t\t\telse if (ignore[l][0] == '_' && !ignore[l][1] && *info->keys[k].name == '_')\n\t\t\t\t\t\tbreak;\t//comment\n\t\t\t\t}\n\t\t\t\tif (ignore[l])\n\t\t\t\t\tcontinue;\t//ignore when in the list\n\t\t\t}\n\t\t\tif (priority)\n\t\t\t{\n\t\t\t\tfor (l = 0; priority[l]; l++)\n\t\t\t\t{\n\t\t\t\t\tif (!strcmp(priority[l], info->keys[k].name))\n\t\t\t\t\t\tbreak;\n\t\t\t\t\telse if (priority[l][0] == '*' && !priority[l][1] && *info->keys[k].name == '*')\n\t\t\t\t\t\tbreak;\t//read-only\n\t\t\t\t\telse if (priority[l][0] == '_' && !priority[l][1] && *info->keys[k].name == '_')\n\t\t\t\t\t\tbreak;\t//comment\n\t\t\t\t}\n\t\t\t\tif (priority[l])\n\t\t\t\t\tp = 0;\t//high priority\n\t\t\t\telse\n\t\t\t\t\tp = 1;\t//low priority\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (*info->keys[k].name == '*')\n\t\t\t\t\tp = 0;\t//keys that cannot be changed always have the highest priority (fixme: useless stuff like version doesn't need to be in here\n\t\t\t\telse\n\t\t\t\t\tp = 1;\n\t\t\t}\n\t\t\tif (pri != p)\n\t\t\t\tcontinue;\n\n\t\t\tif (!info->keys[k].large)\t//lower priorities don't bother with extended blocks. be sure to prioritise them explicitly. they'd just bug stuff out.\n\t\t\t{\n\t\t\t\tl = InfoBuf_EncodeStringSlash(info->keys[k].name, strlen(info->keys[k].name), o, e);\n\t\t\t\tl += InfoBuf_EncodeStringSlash(info->keys[k].value, info->keys[k].size, o+l, e);\n\t\t\t\tr += l;\n\t\t\t\tif (o && o + l < e)\n\t\t\t\t\to += l;\n\t\t\t\telse if (sync)\n\t\t\t\t\tInfoSync_Add(sync, synccontext, info->keys[k].name);\t//not enough space. send this one later\n\t\t\t}\n\t\t\telse if (sync)\n\t\t\t\tInfoSync_Add(sync, synccontext, info->keys[k].name);\t//don't include large/weird keys in the initial string\n\t\t}\n\t}\n\t*o = 0;\n\treturn r;\n}\n\nvoid InfoBuf_Print(infobuf_t *info, const char *lineprefix)\n{\n\tconst char *key;\n\tconst char *val;\n\tsize_t k;\n\n\tfor (k = 0; k < info->numkeys; k++)\n\t{\n\t\tchar *partial = info->keys[k].partial?\"<PARTIAL>\":\"\";\n\t\tkey = info->keys[k].name;\n\t\tval = info->keys[k].value;\n\n\t\tif (info->keys[k].size != strlen(info->keys[k].value))\n\t\t\tCon_Printf (\"%s\"S_COLOR_GREEN\"%-20s\"S_COLOR_RED\"%s<BINARY %u BYTES>\\n\", lineprefix, key, partial, (unsigned int)info->keys[k].size);\n\t\telse if (info->keys[k].size > 64 || strchr(val, '\\n') || strchr(val, '\\r') || strchr(val, '\\t'))\n\t\t\tCon_Printf (\"%s\"S_COLOR_GREEN\"%-20s\"S_COLOR_RED\"%s<%u BYTES>\\n\", lineprefix, key, partial, (unsigned int)info->keys[k].size);\n\t\telse\n\t\t\tCon_Printf (\"%s\"S_COLOR_GREEN\"%-20s\"S_COLOR_WHITE\"%s%s\\n\", lineprefix, key, partial, val);\n\t}\n}\nvoid InfoBuf_Enumerate (infobuf_t *info, void *ctx, void(*cb)(void *ctx, const char *key, const char *value))\n{\n\tconst char *key;\n\tconst char *val;\n\tsize_t k;\n\n\tfor (k = 0; k < info->numkeys; k++)\n\t{\n\t\tkey = info->keys[k].name;\n\t\tval = info->keys[k].value;\n\t\tcb(ctx, key, val);\n\t}\n}\n\nvoid InfoBuf_WriteToFile(vfsfile_t *f, infobuf_t *info, const char *commandname, int cvarflags)\n{\n\tchar *key;\n\tchar *val;\n\tcvar_t *var;\n\tsize_t k;\n\n\tfor (k = 0; k < info->numkeys; k++)\n\t{\n\t\tkey = info->keys[k].name;\n\t\tval = info->keys[k].value;\n\t\tif (*key == '*')\t//unsettable, so don't write it for later setting.\n\t\t\tcontinue;\n\n\t\tif (cvarflags)\n\t\t{\n\t\t\tvar = Cvar_FindVar(key);\n\t\t\tif (var && (var->flags & cvarflags))\n\t\t\t\tcontinue;\t//this is saved via a cvar.\n\t\t}\n\n\t\t//blobs over a certain size cannot safely be parsed (due to Cmd_ExecuteString and com_token having limits)\n\t\t//so just don't write them.\n\t\t//if someone forces a write then the blob will get truncated.\n\t\t//note that blobs are limited im size serverside anyway, so this is probably higher than it needs to be.\n\t\tif (info->keys[k].size > 48000)\n\t\t\tcontinue;\n\n\t\tkey = InfoBuf_EncodeString_Malloc(key, strlen(key));\n\t\tval = InfoBuf_EncodeString_Malloc(val, info->keys[k].size);\n\t\tif (!commandname)\n\t\t{\t//with no command name, just writes a (big) infostring that we can parse later\n\t\t\tVFS_WRITE(f, \"\\\\\", 1);\n\t\t\tVFS_WRITE(f, key, strlen(key));\n\t\t\tVFS_WRITE(f, \"\\\\\", 1);\n\t\t\tVFS_WRITE(f, val, strlen(val));\n\t\t}\n\t\telse\n\t\t{\n\t\t\tVFS_WRITE(f, commandname, strlen(commandname));\n\t\t\tVFS_WRITE(f, \" \\\"\", 2);\n\t\t\tVFS_WRITE(f, key, strlen(key));\n\t\t\tVFS_WRITE(f, \"\\\" \\\"\", 3);\n\t\t\tVFS_WRITE(f, val, strlen(val));\n\t\t\tVFS_WRITE(f, \"\\\"\\n\", 2);\n\t\t}\n\t\tBZ_Free(key);\n\t\tBZ_Free(val);\n\t}\n}\n\n/*\n=====================================================================\n\n  INFO STRINGS\n\n=====================================================================\n*/\n\n/*\n===============\nInfo_ValueForKey\n\nSearches the string for the given\nkey and returns the associated value, or an empty string.\n===============\n*/\nchar *Info_ValueForKey (const char *s, const char *key)\n{\n\tchar\tpkey[1024];\n\tstatic\tchar value[4][1024];\t// use two buffers so compares\n\t\t\t\t\t\t\t\t// work without stomping on each other\n\tstatic\tint\tvalueindex;\n\tchar\t*o;\n\n\tCOM_AssertMainThread(\"Info_ValueForKey\");\n\n\tvalueindex = (valueindex + 1) % 4;\n\tif (*s == '\\\\')\n\t\ts++;\n\twhile (1)\n\t{\n\t\to = pkey;\n\t\twhile (*s != '\\\\')\n\t\t{\n\t\t\tif (!*s)\n\t\t\t{\n\t\t\t\t*value[valueindex]='\\0';\n\t\t\t\treturn value[valueindex];\n\t\t\t}\n\t\t\t*o++ = *s++;\n\t\t\tif (o+2 >= pkey+sizeof(pkey))\t//hrm. hackers at work..\n\t\t\t{\n\t\t\t\t*value[valueindex]='\\0';\n\t\t\t\treturn value[valueindex];\n\t\t\t}\n\t\t}\n\t\t*o = 0;\n\t\ts++;\n\n\t\to = value[valueindex];\n\n\t\twhile (*s != '\\\\' && *s)\n\t\t{\n\t\t\tif (!*s)\n\t\t\t{\n\t\t\t\t*value[valueindex]='\\0';\n\t\t\t\treturn value[valueindex];\n\t\t\t}\n\t\t\t*o++ = *s++;\n\n\t\t\tif (o+2 >= value[valueindex]+sizeof(value[valueindex]))\t//hrm. hackers at work..\n\t\t\t{\n\t\t\t\t*value[valueindex]='\\0';\n\t\t\t\treturn value[valueindex];\n\t\t\t}\n\t\t}\n\t\t*o = 0;\n\n\t\tif (!strcmp (key, pkey) )\n\t\t\treturn value[valueindex];\n\n\t\tif (!*s)\n\t\t{\n\t\t\t*value[valueindex]='\\0';\n\t\t\treturn value[valueindex];\n\t\t}\n\t\ts++;\n\t}\n}\n\nchar *Info_KeyForNumber (const char *s, int num)\n{\n\tstatic char\tpkey[1024];\n\tchar\t*o;\n\n\tif (*s == '\\\\')\n\t\ts++;\n\twhile (1)\n\t{\n\t\to = pkey;\n\t\twhile (*s != '\\\\')\n\t\t{\n\t\t\tif (!*s)\n\t\t\t{\n\t\t\t\t*pkey='\\0';\n\t\t\t\treturn pkey;\n\t\t\t}\n\t\t\t*o++ = *s++;\n\t\t\tif (o+2 >= pkey+sizeof(pkey))\t//hrm. hackers at work..\n\t\t\t{\n\t\t\t\t*pkey='\\0';\n\t\t\t\treturn pkey;\n\t\t\t}\n\t\t}\n\t\t*o = 0;\n\t\ts++;\n\n\t\twhile (*s != '\\\\' && *s)\n\t\t{\n\t\t\tif (!*s)\n\t\t\t{\n\t\t\t\t*pkey='\\0';\n\t\t\t\treturn pkey;\n\t\t\t}\n\t\t\ts++;\n\t\t}\n\n\t\tif (!num--)\n\t\t\treturn pkey;\t//found the right one\n\n\t\tif (!*s)\n\t\t{\n\t\t\t*pkey='\\0';\n\t\t\treturn pkey;\n\t\t}\n\t\ts++;\n\t}\n}\n\nvoid Info_RemoveKey (char *s, const char *key)\n{\n\tchar\t*start;\n\tchar\tpkey[1024];\n\tchar\tvalue[1024];\n\tchar\t*o;\n\n\tif (strstr (key, \"\\\\\"))\n\t{\n\t\tCon_Printf (\"Can't use a key with a \\\\\\n\");\n\t\treturn;\n\t}\n\n\twhile (1)\n\t{\n\t\tstart = s;\n\t\tif (*s == '\\\\')\n\t\t\ts++;\n\t\to = pkey;\n\t\twhile (*s != '\\\\')\n\t\t{\n\t\t\tif (!*s)\n\t\t\t\treturn;\n\t\t\t*o++ = *s++;\n\t\t}\n\t\t*o = 0;\n\t\ts++;\n\n\t\to = value;\n\t\twhile (*s != '\\\\' && *s)\n\t\t{\n\t\t\tif (!*s)\n\t\t\t\treturn;\n\t\t\t*o++ = *s++;\n\t\t}\n\t\t*o = 0;\n\n\t\tif (!strcmp (key, pkey) )\n\t\t{\n\t\t\t//strip out the value by copying the next string over the top of this one\n\t\t\t//(we were using strcpy, but valgrind moaned)\n\t\t\twhile(*s)\n\t\t\t\t*start++ = *s++;\n\t\t\t*start = 0;\n\t\t\treturn;\n\t\t}\n\n\t\tif (!*s)\n\t\t\treturn;\n\t}\n\n}\n\nvoid Info_RemovePrefixedKeys (char *start, char prefix)\n{\n\tchar\t*s;\n\tchar\tpkey[1024];\n\tchar\tvalue[1024];\n\tchar\t*o;\n\n\ts = start;\n\n\twhile (1)\n\t{\n\t\tif (*s == '\\\\')\n\t\t\ts++;\n\t\to = pkey;\n\t\twhile (*s != '\\\\')\n\t\t{\n\t\t\tif (!*s)\n\t\t\t\treturn;\n\t\t\t*o++ = *s++;\n\t\t}\n\t\t*o = 0;\n\t\ts++;\n\n\t\to = value;\n\t\twhile (*s != '\\\\' && *s)\n\t\t{\n\t\t\tif (!*s)\n\t\t\t\treturn;\n\t\t\t*o++ = *s++;\n\t\t}\n\t\t*o = 0;\n\n\t\tif (pkey[0] == prefix)\n\t\t{\n\t\t\tInfo_RemoveKey (start, pkey);\n\t\t\ts = start;\n\t\t}\n\n\t\tif (!*s)\n\t\t\treturn;\n\t}\n}\n\n/*static void Info_RemoveNonStarKeys (char *start)\n{\n\tchar\t*s;\n\tchar\tpkey[1024];\n\tchar\tvalue[1024];\n\tchar\t*o;\n\n\ts = start;\n\n\twhile (1)\n\t{\n\t\tif (*s == '\\\\')\n\t\t\ts++;\n\t\to = pkey;\n\t\twhile (*s != '\\\\')\n\t\t{\n\t\t\tif (!*s)\n\t\t\t\treturn;\n\t\t\t*o++ = *s++;\n\t\t}\n\t\t*o = 0;\n\t\ts++;\n\n\t\to = value;\n\t\twhile (*s != '\\\\' && *s)\n\t\t{\n\t\t\tif (!*s)\n\t\t\t\treturn;\n\t\t\t*o++ = *s++;\n\t\t}\n\t\t*o = 0;\n\n\t\tif (pkey[0] != '*')\n\t\t{\n\t\t\tInfo_RemoveKey (start, pkey);\n\t\t\ts = start;\n\t\t}\n\n\t\tif (!*s)\n\t\t\treturn;\n\t}\n}*/\n\nvoid Info_SetValueForStarKey (char *s, const char *key, const char *value, int maxsize)\n{\n\tchar\tnewv[1024], *v;\n\tint\t\tc;\n#ifdef SERVERONLY\n\textern cvar_t sv_highchars;\n#endif\n\n\tif (strstr (key, \"\\\\\") || strstr (value, \"\\\\\") )\n\t{\n\t\tCon_Printf (\"Can't use a key with a \\\\\\n\");\n\t\treturn;\n\t}\n\n\tif (strstr (key, \"\\\"\") || strstr (value, \"\\\"\") )\n\t{\n\t\tCon_Printf (\"Can't use a key with a \\\"\\n\");\n\t\treturn;\n\t}\n\n\tif (strlen(key) >= MAX_INFO_KEY)// || strlen(value) >= MAX_INFO_KEY)\n\t{\n\t\tCon_Printf (\"Keys and values must be < %i characters.\\n\", MAX_INFO_KEY);\n\t\treturn;\n\t}\n\n\t// this next line is kinda trippy\n\tif (*(v = Info_ValueForKey(s, key)))\n\t{\n\t\t// key exists, make sure we have enough room for new value, if we don't,\n\t\t// don't change it!\n\t\tif (strlen(value) - strlen(v) + strlen(s) + 1 > maxsize)\n\t\t{\n\t\t\tif (*Info_ValueForKey(s, \"*ver\"))\t//quick hack to kill off unneeded info on overflow. We can't simply increase the quantity of this stuff.\n\t\t\t{\n\t\t\t\tInfo_RemoveKey(s, \"*ver\");\n\t\t\t\tInfo_SetValueForStarKey (s, key, value, maxsize);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tCon_Printf (\"Info string length exceeded on addition of %s\\n\", key);\n\t\t\treturn;\n\t\t}\n\t}\n\tInfo_RemoveKey (s, key);\n\tif (!value || !strlen(value))\n\t\treturn;\n\n\tsnprintf (newv, sizeof(newv), \"\\\\%s\\\\%s\", key, value);\n\n\tif ((int)(strlen(newv) + strlen(s) + 1) > maxsize)\n\t{\n\t\tCon_Printf (\"Info string length exceeded on addition of %s\\n\", key);\n\t\treturn;\n\t}\n\n\t// only copy ascii values\n\ts += strlen(s);\n\tv = newv;\n\twhile (*v)\n\t{\n\t\tc = (unsigned char)*v++;\n#ifndef SERVERONLY\n\t\t// client only allows highbits on name\n//\t\tif (stricmp(key, \"name\") != 0) {\n//\t\t\tc &= 127;\n//\t\t\tif (c < 32 || c > 127)\n//\t\t\t\tcontinue;\n//\t\t\t// auto lowercase team\n//\t\t\tif (stricmp(key, \"team\") == 0)\n//\t\t\t\tc = tolower(c);\n//\t\t}\n#else\n\t\tif (!sv_highchars.value) {\n\t\t\tc &= 127;\n\t\t\tif (c < 32 || c > 127)\n\t\t\t\tcontinue;\n\t\t}\n#endif\n//\t\tc &= 127;\t\t// strip high bits\n\t\tif (c > 13) // && c < 127)\n\t\t\t*s++ = c;\n\t}\n\t*s = 0;\n}\n\nvoid Info_SetValueForKey (char *s, const char *key, const char *value, int maxsize)\n{\n\tif (key[0] == '*')\n\t{\n\t\tCon_Printf (\"Can't set * keys\\n\");\n\t\treturn;\n\t}\n\n\tInfo_SetValueForStarKey (s, key, value, maxsize);\n}\n\nvoid Info_Enumerate (const char *s, void *ctx, void(*cb)(void *ctx, const char *key, const char *value))\n{\n\tchar\tkey[1024];\n\tchar\tvalue[1024];\n\tchar\t*o;\n\n\tif (*s == '\\\\')\n\t\ts++;\n\twhile (*s)\n\t{\n\t\to = key;\n\t\twhile (*s && *s != '\\\\' && o < key+countof(key)-1)\n\t\t\t*o++ = *s++;\n\t\t*o = 0;\n\n\t\tif (!*s++)\n\t\t{\n\t\t\t//should never happen.\n\t\t\tcb(ctx, key, \"\");\n\t\t\treturn;\n\t\t}\n\n\t\to = value;\n\t\twhile (*s && *s != '\\\\' && o < value+countof(value)-1)\n\t\t\t*o++ = *s++;\n\t\t*o = 0;\n\n\t\tif (*s)\n\t\t\ts++;\n\t\tcb(ctx, key, value);\n\t}\n}\n\nstatic void Info_PrintCB (void *ctx, const char *key, const char *value)\n{\n\tchar *lineprefix = ctx;\n\tCon_Printf (\"%s%-20s%s\\n\", lineprefix, key, value);\n}\nvoid Info_Print (const char *s, const char *lineprefix)\n{\n\tInfo_Enumerate(s, (void*)lineprefix, Info_PrintCB);\n}\n\n/*static void Info_WriteToFile(vfsfile_t *f, char *info, char *commandname, int cvarflags)\n{\n\tconst char *quotedvalue;\n\tchar buffer[1024];\n\tchar *command;\n\tchar *value, t;\n\tcvar_t *var;\n\n\twhile(*info == '\\\\')\n\t{\n\t\tcommand = info+1;\n\t\tvalue = strchr(command, '\\\\');\n\t\tinfo = strchr(value+1, '\\\\');\n\t\tif (!info)\t//eot..\n\t\t\tinfo = value+strlen(value);\n\n\t\tif (*command == '*')\t//unsettable, so don't write it for later setting.\n\t\t\tcontinue;\n\n\t\tif (cvarflags)\n\t\t{\n\t\t\tvar = Cvar_FindVar(command);\n\t\t\tif (var && var->flags & cvarflags)\n\t\t\t\tcontinue;\t//this is saved via a cvar.\n\t\t}\n\n\t\tVFS_WRITE(f, commandname, strlen(commandname));\n\t\tVFS_WRITE(f, \" \", 1);\n\t\tVFS_WRITE(f, command, value-command);\n\t\tVFS_WRITE(f, \" \", 1);\n\t\tt = *info;\n\t\t*info = 0;\n\t\tquotedvalue = COM_QuotedString(value+1, buffer, sizeof(buffer), false);\n\t\tVFS_WRITE(f, quotedvalue, strlen(quotedvalue));\n\t\t*info = t;\n\t\tVFS_WRITE(f, \"\\n\", 1);\n\t}\n}*/\n\n#if defined(HAVE_CLIENT) || defined(HAVE_SERVER)\nstatic qbyte chktbl[1024 + 4] = {\n0x78,0xd2,0x94,0xe3,0x41,0xec,0xd6,0xd5,0xcb,0xfc,0xdb,0x8a,0x4b,0xcc,0x85,0x01,\n0x23,0xd2,0xe5,0xf2,0x29,0xa7,0x45,0x94,0x4a,0x62,0xe3,0xa5,0x6f,0x3f,0xe1,0x7a,\n0x64,0xed,0x5c,0x99,0x29,0x87,0xa8,0x78,0x59,0x0d,0xaa,0x0f,0x25,0x0a,0x5c,0x58,\n0xfb,0x00,0xa7,0xa8,0x8a,0x1d,0x86,0x80,0xc5,0x1f,0xd2,0x28,0x69,0x71,0x58,0xc3,\n0x51,0x90,0xe1,0xf8,0x6a,0xf3,0x8f,0xb0,0x68,0xdf,0x95,0x40,0x5c,0xe4,0x24,0x6b,\n0x29,0x19,0x71,0x3f,0x42,0x63,0x6c,0x48,0xe7,0xad,0xa8,0x4b,0x91,0x8f,0x42,0x36,\n0x34,0xe7,0x32,0x55,0x59,0x2d,0x36,0x38,0x38,0x59,0x9b,0x08,0x16,0x4d,0x8d,0xf8,\n0x0a,0xa4,0x52,0x01,0xbb,0x52,0xa9,0xfd,0x40,0x18,0x97,0x37,0xff,0xc9,0x82,0x27,\n0xb2,0x64,0x60,0xce,0x00,0xd9,0x04,0xf0,0x9e,0x99,0xbd,0xce,0x8f,0x90,0x4a,0xdd,\n0xe1,0xec,0x19,0x14,0xb1,0xfb,0xca,0x1e,0x98,0x0f,0xd4,0xcb,0x80,0xd6,0x05,0x63,\n0xfd,0xa0,0x74,0xa6,0x86,0xf6,0x19,0x98,0x76,0x27,0x68,0xf7,0xe9,0x09,0x9a,0xf2,\n0x2e,0x42,0xe1,0xbe,0x64,0x48,0x2a,0x74,0x30,0xbb,0x07,0xcc,0x1f,0xd4,0x91,0x9d,\n0xac,0x55,0x53,0x25,0xb9,0x64,0xf7,0x58,0x4c,0x34,0x16,0xbc,0xf6,0x12,0x2b,0x65,\n0x68,0x25,0x2e,0x29,0x1f,0xbb,0xb9,0xee,0x6d,0x0c,0x8e,0xbb,0xd2,0x5f,0x1d,0x8f,\n0xc1,0x39,0xf9,0x8d,0xc0,0x39,0x75,0xcf,0x25,0x17,0xbe,0x96,0xaf,0x98,0x9f,0x5f,\n0x65,0x15,0xc4,0x62,0xf8,0x55,0xfc,0xab,0x54,0xcf,0xdc,0x14,0x06,0xc8,0xfc,0x42,\n0xd3,0xf0,0xad,0x10,0x08,0xcd,0xd4,0x11,0xbb,0xca,0x67,0xc6,0x48,0x5f,0x9d,0x59,\n0xe3,0xe8,0x53,0x67,0x27,0x2d,0x34,0x9e,0x9e,0x24,0x29,0xdb,0x69,0x99,0x86,0xf9,\n0x20,0xb5,0xbb,0x5b,0xb0,0xf9,0xc3,0x67,0xad,0x1c,0x9c,0xf7,0xcc,0xef,0xce,0x69,\n0xe0,0x26,0x8f,0x79,0xbd,0xca,0x10,0x17,0xda,0xa9,0x88,0x57,0x9b,0x15,0x24,0xba,\n0x84,0xd0,0xeb,0x4d,0x14,0xf5,0xfc,0xe6,0x51,0x6c,0x6f,0x64,0x6b,0x73,0xec,0x85,\n0xf1,0x6f,0xe1,0x67,0x25,0x10,0x77,0x32,0x9e,0x85,0x6e,0x69,0xb1,0x83,0x00,0xe4,\n0x13,0xa4,0x45,0x34,0x3b,0x40,0xff,0x41,0x82,0x89,0x79,0x57,0xfd,0xd2,0x8e,0xe8,\n0xfc,0x1d,0x19,0x21,0x12,0x00,0xd7,0x66,0xe5,0xc7,0x10,0x1d,0xcb,0x75,0xe8,0xfa,\n0xb6,0xee,0x7b,0x2f,0x1a,0x25,0x24,0xb9,0x9f,0x1d,0x78,0xfb,0x84,0xd0,0x17,0x05,\n0x71,0xb3,0xc8,0x18,0xff,0x62,0xee,0xed,0x53,0xab,0x78,0xd3,0x65,0x2d,0xbb,0xc7,\n0xc1,0xe7,0x70,0xa2,0x43,0x2c,0x7c,0xc7,0x16,0x04,0xd2,0x45,0xd5,0x6b,0x6c,0x7a,\n0x5e,0xa1,0x50,0x2e,0x31,0x5b,0xcc,0xe8,0x65,0x8b,0x16,0x85,0xbf,0x82,0x83,0xfb,\n0xde,0x9f,0x36,0x48,0x32,0x79,0xd6,0x9b,0xfb,0x52,0x45,0xbf,0x43,0xf7,0x0b,0x0b,\n0x19,0x19,0x31,0xc3,0x85,0xec,0x1d,0x8c,0x20,0xf0,0x3a,0xfa,0x80,0x4d,0x2c,0x7d,\n0xac,0x60,0x09,0xc0,0x40,0xee,0xb9,0xeb,0x13,0x5b,0xe8,0x2b,0xb1,0x20,0xf0,0xce,\n0x4c,0xbd,0xc6,0x04,0x86,0x70,0xc6,0x33,0xc3,0x15,0x0f,0x65,0x19,0xfd,0xc2,0xd3,\n\n// map checksum goes here\n0x00,0x00,0x00,0x00\n};\n\n#if 0\n\nstatic qbyte chkbuf[16 + 60 + 4];\n\nstatic unsigned last_mapchecksum = 0;\n\n\n/*\n====================\nCOM_BlockSequenceCheckByte\n\nFor proxy protecting\n====================\n*/\nqbyte\tCOM_BlockSequenceCheckByte (qbyte *base, int length, int sequence, unsigned mapchecksum)\n{\n\tint\t\tchecksum;\n\tqbyte\t*p;\n\n\tif (last_mapchecksum != mapchecksum) {\n\t\tlast_mapchecksum = mapchecksum;\n\t\tchktbl[1024] = (mapchecksum & 0xff000000) >> 24;\n\t\tchktbl[1025] = (mapchecksum & 0x00ff0000) >> 16;\n\t\tchktbl[1026] = (mapchecksum & 0x0000ff00) >> 8;\n\t\tchktbl[1027] = (mapchecksum & 0x000000ff);\n\n\t\tCom_BlockFullChecksum (chktbl, sizeof(chktbl), chkbuf);\n\t}\n\n\tp = chktbl + (sequence % (sizeof(chktbl) - 8));\n\n\tif (length > 60)\n\t\tlength = 60;\n\tmemcpy (chkbuf + 16, base, length);\n\n\tlength += 16;\n\n\tchkbuf[length] = (sequence & 0xff) ^ p[0];\n\tchkbuf[length+1] = p[1];\n\tchkbuf[length+2] = ((sequence>>8) & 0xff) ^ p[2];\n\tchkbuf[length+3] = p[3];\n\n\tlength += 4;\n\n\tchecksum = LittleLong(Com_BlockChecksum (chkbuf, length));\n\n\tchecksum &= 0xff;\n\n\treturn checksum;\n}\n#endif\n\n/*\n====================\nCOM_BlockSequenceCRCByte\n\nFor proxy protecting\n====================\n*/\nqbyte\tCOM_BlockSequenceCRCByte (qbyte *base, int length, int sequence)\n{\n\tunsigned short crc;\n\tqbyte\t*p;\n\tqbyte chkb[60 + 4];\n\n\tp = chktbl + (sequence % (sizeof(chktbl) - 8));\n\n\tif (length > 60)\n\t\tlength = 60;\n\tmemcpy (chkb, base, length);\n\n\tchkb[length] = (sequence & 0xff) ^ p[0];\n\tchkb[length+1] = p[1];\n\tchkb[length+2] = ((sequence>>8) & 0xff) ^ p[2];\n\tchkb[length+3] = p[3];\n\n\tlength += 4;\n\n\tcrc = CalcHashInt(&hash_crc16, chkb, length);\n\n\tcrc &= 0xff;\n\n\treturn crc;\n}\n\n\n#if defined(Q2CLIENT) || defined(Q2SERVER)\nstatic qbyte q2chktbl[1024] = {\n0x84, 0x47, 0x51, 0xc1, 0x93, 0x22, 0x21, 0x24, 0x2f, 0x66, 0x60, 0x4d, 0xb0, 0x7c, 0xda,\n0x88, 0x54, 0x15, 0x2b, 0xc6, 0x6c, 0x89, 0xc5, 0x9d, 0x48, 0xee, 0xe6, 0x8a, 0xb5, 0xf4,\n0xcb, 0xfb, 0xf1, 0x0c, 0x2e, 0xa0, 0xd7, 0xc9, 0x1f, 0xd6, 0x06, 0x9a, 0x09, 0x41, 0x54,\n0x67, 0x46, 0xc7, 0x74, 0xe3, 0xc8, 0xb6, 0x5d, 0xa6, 0x36, 0xc4, 0xab, 0x2c, 0x7e, 0x85,\n0xa8, 0xa4, 0xa6, 0x4d, 0x96, 0x19, 0x19, 0x9a, 0xcc, 0xd8, 0xac, 0x39, 0x5e, 0x3c, 0xf2,\n0xf5, 0x5a, 0x72, 0xe5, 0xa9, 0xd1, 0xb3, 0x23, 0x82, 0x6f, 0x29, 0xcb, 0xd1, 0xcc, 0x71,\n0xfb, 0xea, 0x92, 0xeb, 0x1c, 0xca, 0x4c, 0x70, 0xfe, 0x4d, 0xc9, 0x67, 0x43, 0x47, 0x94,\n0xb9, 0x47, 0xbc, 0x3f, 0x01, 0xab, 0x7b, 0xa6, 0xe2, 0x76, 0xef, 0x5a, 0x7a, 0x29, 0x0b,\n0x51, 0x54, 0x67, 0xd8, 0x1c, 0x14, 0x3e, 0x29, 0xec, 0xe9, 0x2d, 0x48, 0x67, 0xff, 0xed,\n0x54, 0x4f, 0x48, 0xc0, 0xaa, 0x61, 0xf7, 0x78, 0x12, 0x03, 0x7a, 0x9e, 0x8b, 0xcf, 0x83,\n0x7b, 0xae, 0xca, 0x7b, 0xd9, 0xe9, 0x53, 0x2a, 0xeb, 0xd2, 0xd8, 0xcd, 0xa3, 0x10, 0x25,\n0x78, 0x5a, 0xb5, 0x23, 0x06, 0x93, 0xb7, 0x84, 0xd2, 0xbd, 0x96, 0x75, 0xa5, 0x5e, 0xcf,\n0x4e, 0xe9, 0x50, 0xa1, 0xe6, 0x9d, 0xb1, 0xe3, 0x85, 0x66, 0x28, 0x4e, 0x43, 0xdc, 0x6e,\n0xbb, 0x33, 0x9e, 0xf3, 0x0d, 0x00, 0xc1, 0xcf, 0x67, 0x34, 0x06, 0x7c, 0x71, 0xe3, 0x63,\n0xb7, 0xb7, 0xdf, 0x92, 0xc4, 0xc2, 0x25, 0x5c, 0xff, 0xc3, 0x6e, 0xfc, 0xaa, 0x1e, 0x2a,\n0x48, 0x11, 0x1c, 0x36, 0x68, 0x78, 0x86, 0x79, 0x30, 0xc3, 0xd6, 0xde, 0xbc, 0x3a, 0x2a,\n0x6d, 0x1e, 0x46, 0xdd, 0xe0, 0x80, 0x1e, 0x44, 0x3b, 0x6f, 0xaf, 0x31, 0xda, 0xa2, 0xbd,\n0x77, 0x06, 0x56, 0xc0, 0xb7, 0x92, 0x4b, 0x37, 0xc0, 0xfc, 0xc2, 0xd5, 0xfb, 0xa8, 0xda,\n0xf5, 0x57, 0xa8, 0x18, 0xc0, 0xdf, 0xe7, 0xaa, 0x2a, 0xe0, 0x7c, 0x6f, 0x77, 0xb1, 0x26,\n0xba, 0xf9, 0x2e, 0x1d, 0x16, 0xcb, 0xb8, 0xa2, 0x44, 0xd5, 0x2f, 0x1a, 0x79, 0x74, 0x87,\n0x4b, 0x00, 0xc9, 0x4a, 0x3a, 0x65, 0x8f, 0xe6, 0x5d, 0xe5, 0x0a, 0x77, 0xd8, 0x1a, 0x14,\n0x41, 0x75, 0xb1, 0xe2, 0x50, 0x2c, 0x93, 0x38, 0x2b, 0x6d, 0xf3, 0xf6, 0xdb, 0x1f, 0xcd,\n0xff, 0x14, 0x70, 0xe7, 0x16, 0xe8, 0x3d, 0xf0, 0xe3, 0xbc, 0x5e, 0xb6, 0x3f, 0xcc, 0x81,\n0x24, 0x67, 0xf3, 0x97, 0x3b, 0xfe, 0x3a, 0x96, 0x85, 0xdf, 0xe4, 0x6e, 0x3c, 0x85, 0x05,\n0x0e, 0xa3, 0x2b, 0x07, 0xc8, 0xbf, 0xe5, 0x13, 0x82, 0x62, 0x08, 0x61, 0x69, 0x4b, 0x47,\n0x62, 0x73, 0x44, 0x64, 0x8e, 0xe2, 0x91, 0xa6, 0x9a, 0xb7, 0xe9, 0x04, 0xb6, 0x54, 0x0c,\n0xc5, 0xa9, 0x47, 0xa6, 0xc9, 0x08, 0xfe, 0x4e, 0xa6, 0xcc, 0x8a, 0x5b, 0x90, 0x6f, 0x2b,\n0x3f, 0xb6, 0x0a, 0x96, 0xc0, 0x78, 0x58, 0x3c, 0x76, 0x6d, 0x94, 0x1a, 0xe4, 0x4e, 0xb8,\n0x38, 0xbb, 0xf5, 0xeb, 0x29, 0xd8, 0xb0, 0xf3, 0x15, 0x1e, 0x99, 0x96, 0x3c, 0x5d, 0x63,\n0xd5, 0xb1, 0xad, 0x52, 0xb8, 0x55, 0x70, 0x75, 0x3e, 0x1a, 0xd5, 0xda, 0xf6, 0x7a, 0x48,\n0x7d, 0x44, 0x41, 0xf9, 0x11, 0xce, 0xd7, 0xca, 0xa5, 0x3d, 0x7a, 0x79, 0x7e, 0x7d, 0x25,\n0x1b, 0x77, 0xbc, 0xf7, 0xc7, 0x0f, 0x84, 0x95, 0x10, 0x92, 0x67, 0x15, 0x11, 0x5a, 0x5e,\n0x41, 0x66, 0x0f, 0x38, 0x03, 0xb2, 0xf1, 0x5d, 0xf8, 0xab, 0xc0, 0x02, 0x76, 0x84, 0x28,\n0xf4, 0x9d, 0x56, 0x46, 0x60, 0x20, 0xdb, 0x68, 0xa7, 0xbb, 0xee, 0xac, 0x15, 0x01, 0x2f,\n0x20, 0x09, 0xdb, 0xc0, 0x16, 0xa1, 0x89, 0xf9, 0x94, 0x59, 0x00, 0xc1, 0x76, 0xbf, 0xc1,\n0x4d, 0x5d, 0x2d, 0xa9, 0x85, 0x2c, 0xd6, 0xd3, 0x14, 0xcc, 0x02, 0xc3, 0xc2, 0xfa, 0x6b,\n0xb7, 0xa6, 0xef, 0xdd, 0x12, 0x26, 0xa4, 0x63, 0xe3, 0x62, 0xbd, 0x56, 0x8a, 0x52, 0x2b,\n0xb9, 0xdf, 0x09, 0xbc, 0x0e, 0x97, 0xa9, 0xb0, 0x82, 0x46, 0x08, 0xd5, 0x1a, 0x8e, 0x1b,\n0xa7, 0x90, 0x98, 0xb9, 0xbb, 0x3c, 0x17, 0x9a, 0xf2, 0x82, 0xba, 0x64, 0x0a, 0x7f, 0xca,\n0x5a, 0x8c, 0x7c, 0xd3, 0x79, 0x09, 0x5b, 0x26, 0xbb, 0xbd, 0x25, 0xdf, 0x3d, 0x6f, 0x9a,\n0x8f, 0xee, 0x21, 0x66, 0xb0, 0x8d, 0x84, 0x4c, 0x91, 0x45, 0xd4, 0x77, 0x4f, 0xb3, 0x8c,\n0xbc, 0xa8, 0x99, 0xaa, 0x19, 0x53, 0x7c, 0x02, 0x87, 0xbb, 0x0b, 0x7c, 0x1a, 0x2d, 0xdf,\n0x48, 0x44, 0x06, 0xd6, 0x7d, 0x0c, 0x2d, 0x35, 0x76, 0xae, 0xc4, 0x5f, 0x71, 0x85, 0x97,\n0xc4, 0x3d, 0xef, 0x52, 0xbe, 0x00, 0xe4, 0xcd, 0x49, 0xd1, 0xd1, 0x1c, 0x3c, 0xd0, 0x1c,\n0x42, 0xaf, 0xd4, 0xbd, 0x58, 0x34, 0x07, 0x32, 0xee, 0xb9, 0xb5, 0xea, 0xff, 0xd7, 0x8c,\n0x0d, 0x2e, 0x2f, 0xaf, 0x87, 0xbb, 0xe6, 0x52, 0x71, 0x22, 0xf5, 0x25, 0x17, 0xa1, 0x82,\n0x04, 0xc2, 0x4a, 0xbd, 0x57, 0xc6, 0xab, 0xc8, 0x35, 0x0c, 0x3c, 0xd9, 0xc2, 0x43, 0xdb,\n0x27, 0x92, 0xcf, 0xb8, 0x25, 0x60, 0xfa, 0x21, 0x3b, 0x04, 0x52, 0xc8, 0x96, 0xba, 0x74,\n0xe3, 0x67, 0x3e, 0x8e, 0x8d, 0x61, 0x90, 0x92, 0x59, 0xb6, 0x1a, 0x1c, 0x5e, 0x21, 0xc1,\n0x65, 0xe5, 0xa6, 0x34, 0x05, 0x6f, 0xc5, 0x60, 0xb1, 0x83, 0xc1, 0xd5, 0xd5, 0xed, 0xd9,\n0xc7, 0x11, 0x7b, 0x49, 0x7a, 0xf9, 0xf9, 0x84, 0x47, 0x9b, 0xe2, 0xa5, 0x82, 0xe0, 0xc2,\n0x88, 0xd0, 0xb2, 0x58, 0x88, 0x7f, 0x45, 0x09, 0x67, 0x74, 0x61, 0xbf, 0xe6, 0x40, 0xe2,\n0x9d, 0xc2, 0x47, 0x05, 0x89, 0xed, 0xcb, 0xbb, 0xb7, 0x27, 0xe7, 0xdc, 0x7a, 0xfd, 0xbf,\n0xa8, 0xd0, 0xaa, 0x10, 0x39, 0x3c, 0x20, 0xf0, 0xd3, 0x6e, 0xb1, 0x72, 0xf8, 0xe6, 0x0f,\n0xef, 0x37, 0xe5, 0x09, 0x33, 0x5a, 0x83, 0x43, 0x80, 0x4f, 0x65, 0x2f, 0x7c, 0x8c, 0x6a,\n0xa0, 0x82, 0x0c, 0xd4, 0xd4, 0xfa, 0x81, 0x60, 0x3d, 0xdf, 0x06, 0xf1, 0x5f, 0x08, 0x0d,\n0x6d, 0x43, 0xf2, 0xe3, 0x11, 0x7d, 0x80, 0x32, 0xc5, 0xfb, 0xc5, 0xd9, 0x27, 0xec, 0xc6,\n0x4e, 0x65, 0x27, 0x76, 0x87, 0xa6, 0xee, 0xee, 0xd7, 0x8b, 0xd1, 0xa0, 0x5c, 0xb0, 0x42,\n0x13, 0x0e, 0x95, 0x4a, 0xf2, 0x06, 0xc6, 0x43, 0x33, 0xf4, 0xc7, 0xf8, 0xe7, 0x1f, 0xdd,\n0xe4, 0x46, 0x4a, 0x70, 0x39, 0x6c, 0xd0, 0xed, 0xca, 0xbe, 0x60, 0x3b, 0xd1, 0x7b, 0x57,\n0x48, 0xe5, 0x3a, 0x79, 0xc1, 0x69, 0x33, 0x53, 0x1b, 0x80, 0xb8, 0x91, 0x7d, 0xb4, 0xf6,\n0x17, 0x1a, 0x1d, 0x5a, 0x32, 0xd6, 0xcc, 0x71, 0x29, 0x3f, 0x28, 0xbb, 0xf3, 0x5e, 0x71,\n0xb8, 0x43, 0xaf, 0xf8, 0xb9, 0x64, 0xef, 0xc4, 0xa5, 0x6c, 0x08, 0x53, 0xc7, 0x00, 0x10,\n0x39, 0x4f, 0xdd, 0xe4, 0xb6, 0x19, 0x27, 0xfb, 0xb8, 0xf5, 0x32, 0x73, 0xe5, 0xcb, 0x32\n};\n\n/*\n====================\nCOM_BlockSequenceCRCByte\n\nFor proxy protecting\n====================\n*/\nqbyte\tQ2COM_BlockSequenceCRCByte (qbyte *base, int length, int sequence)\n{\n\tint\t\tn;\n\tqbyte\t*p;\n\tint\t\tx;\n\tqbyte chkb[60 + 4];\n\tunsigned short crc;\n\n\n\tif (sequence < 0)\n\t\tSys_Error(\"sequence < 0, this shouldn't happen\\n\");\n\n\tp = q2chktbl + (sequence % (sizeof(q2chktbl) - 4));\n\n\tif (length > 60)\n\t\tlength = 60;\n\tmemcpy (chkb, base, length);\n\n\tchkb[length] = p[0];\n\tchkb[length+1] = p[1];\n\tchkb[length+2] = p[2];\n\tchkb[length+3] = p[3];\n\n\tlength += 4;\n\n\tcrc = CalcHashInt(&hash_crc16, chkb, length);\n\n\tfor (x=0, n=0; n<length; n++)\n\t\tx += chkb[n];\n\n\tcrc = (crc ^ x) & 0xff;\n\n\treturn crc;\n}\n\n#endif\n#endif\n\n#ifdef _WIN32\n// don't use these functions in MSVC8\n#if (_MSC_VER < 1400)\nint VARGS linuxlike_snprintf(char *buffer, int size, const char *format, ...)\n{\n#undef _vsnprintf\n\tint ret;\n\tva_list\t\targptr;\n\n\tif (size <= 0)\n\t\treturn !buffer?-1:0;\n\tsize--;\n\n\tva_start (argptr, format);\n\tret = _vsnprintf (buffer,size, format,argptr);\n\tva_end (argptr);\n\n\tbuffer[size] = '\\0';\n\n\treturn ret;\n}\n\nint VARGS linuxlike_vsnprintf(char *buffer, int size, const char *format, va_list argptr)\n{\t//_vsnprintf truncates WITHOUT NULL, and returns -1\n#undef _vsnprintf\n\tint ret;\n\n\tif (size <= 0)\n\t\treturn !buffer?-1:0;\n\tsize--;\n\n\tret = _vsnprintf (buffer,size, format,argptr);\n\n\tbuffer[size] = '\\0';\n\n\treturn ret;\n}\n#else\nint VARGS linuxlike_snprintf_vc8(char *buffer, int size, const char *format, ...)\n{\t//vsnprintf_s safely truncates with null, but returns -1 on truncation rather than untruncated full length\n\tint ret;\n\tva_list\t\targptr;\n\n\tva_start (argptr, format);\n\tret = vsnprintf_s (buffer,size, _TRUNCATE, format,argptr);\n\tva_end (argptr);\n\n\treturn ret;\n}\n#endif\n\n#endif\n\n// libSDL.a and libSDLmain.a mingw32 libs use this function for some reason, just here to shut gcc up\n/*#ifdef _MINGW_VFPRINTF\nint __mingw_vfprintf (FILE *__stream, const char *__format, __VALIST __local_argv)\n{\n  return vfprintf( __stream, __format, __local_argv );\n}\n#endif*/\n\nint version_number(void)\n{\n\tint base = FTE_VER_MAJOR * 10000 + FTE_VER_MINOR * 100;\n\n#ifdef OFFICIAL_RELEASE\n\tbase -= 1;\n#endif\n\n\treturn base;\n}\n\nchar *version_string(void)\n{\n\tstatic char s[128];\n\tstatic qboolean done;\n\n\tif (!done)\n\t{\n#ifdef OFFICIAL_RELEASE\n\t\tQ_snprintfz(s, sizeof(s), \"%s v%i.%02i\", DISTRIBUTION, FTE_VER_MAJOR, FTE_VER_MINOR);\n#elif defined(SVNREVISION) && defined(SVNDATE)\n\t#ifdef FTE_BRANCH\n\t\t//something like 'FTE master 6410M-HASH'\n\t\tQ_snprintfz(s, sizeof(s), \"%s %s %s\", DISTRIBUTION, STRINGIFY(FTE_BRANCH), STRINGIFY(SVNREVISION));\n\t#else\n\t\tif (!strncmp(STRINGIFY(SVNREVISION), \"git-\", 4))\n\t\t\tQ_snprintfz(s, sizeof(s), \"%s %s\", DISTRIBUTION, STRINGIFY(SVNREVISION));\t//if both are defined then its a known unmodified svn revision.\n\t\telse\n\t\t\tQ_snprintfz(s, sizeof(s), \"%s SVN %s\", DISTRIBUTION, STRINGIFY(SVNREVISION));\t//if both are defined then its a known unmodified svn revision.\n\t#endif\n#else\n\t#if defined(SVNREVISION)\n\t\tif (!strncmp(STRINGIFY(SVNREVISION), \"git-\", 4))\n\t\t\tQ_snprintfz(s, sizeof(s), \"%s %s %s\", DISTRIBUTION, STRINGIFY(SVNREVISION), __DATE__);\n\t\telse if (strcmp(STRINGIFY(SVNREVISION), \"-\"))\n\t\t\tQ_snprintfz(s, sizeof(s), \"%s SVN %s %s\", DISTRIBUTION, STRINGIFY(SVNREVISION), __DATE__);\n\t\telse\n\t#endif\n\t\tQ_snprintfz(s, sizeof(s), \"%s build %s\", DISTRIBUTION, __DATE__);\n#endif\n\t\tdone = true;\n\t}\n\n\treturn s;\n}\n\n//returns <=0 on error.\n//this function is useful for auto updates.\nint parse_revision_number(const char *s, qboolean strict)\n{\n\tint rev;\n\tchar *e;\n\n\t//no revision info in this build, meaning its custom built and thus cannot check against the available updated versions.\n\tif (!s || !strcmp(s, \"-\") || !*s)\n\t\treturn false; //no version info at all.\n\n\tif (!strncmp(s, \"git-\", 4))\n\t{\t//git gets messy and takes the form of one of the following...\n\t\t//bad: git-XXXXXXXX[-dirty]\n\t\t//git-tag-extracommits-hash[-dirty]\n\t\t//if 'tag' is [R]VVVV then someone's tagging revisions to match svn revisions.\n\t\t//if a fork wants to block updates, then they can either just disable engine updates or they can fiddle with this tagging stuff.\n\t\ts+=4;\n\t\tif (*s == 'r' || *s == 'R')\n\t\t\ts++;\t//R prefix is optional.\n\n\t\tif (strict && strstr(s, \"-dirty\"))\n\t\t\treturn false;\t//boo hiss.\n\n\t\trev = strtoul(s, &e, 10);\n\t\tif (*e == '-')\n\t\t{\t//we used --long so this should be a count of extra commits\n\t\t\tif (strtoul(e+1, &e, 10) && strict)\n\t\t\t\treturn false;\t//doesn't exactly match the tag, and we're strict\n\t\t\tif (*e != '-')\n\t\t\t\treturn false;\t//no hash info? something odd is happening...\n\t\t\t//hash is uninteresting.\n\t\t}\n\t\telse\t//looks like there's no tag info there, just a commit hash. don't consider it a valid revision number.\n\t\t\treturn false;\t//--long didn't\n\t}\n\telse\n\t{\n\t\t//svn: [lower-]upper[M]\n\t\t//git: revision-git-hash[-dirty]\n\t\t//git: branch-revision-git-hash[-dirty]\n\t\trev = strtoul(s, &e, 10);\n\t\tif (!strncmp(e, \"-git\", 4))\n\t\t{\t//if there's a -dirty in there then its bad.\n\t\t\t//we can't validate that the commit id matches the same branch as this build. we'll just have to live with it.\n\t\t\tif (strict && strstr(s, \"-dirty\"))\n\t\t\t\treturn false;\n\t\t}\n\t\telse if (*e && strict)\n\t\t\treturn false;\t//something odd.\n\t}\n\treturn rev;\n}\nint revision_number(qboolean strict)\n{\n#ifdef SVNREVISION\n\treturn parse_revision_number(STRINGIFY(SVNREVISION), strict);\n#else\n\treturn 0;\n#endif\n}\n\n//C90\nvoid COM_TimeOfDay(date_t *date)\n{\n\tstruct tm *newtime;\n\ttime_t long_time;\n\n\ttime(&long_time);\n\tnewtime = localtime(&long_time);\n\n\tdate->day = newtime->tm_mday;\n\tdate->mon = newtime->tm_mon;\n\tdate->year = newtime->tm_year + 1900;\n\tdate->hour = newtime->tm_hour;\n\tdate->min = newtime->tm_min;\n\tdate->sec = newtime->tm_sec;\n\tstrftime( date->str, 128,\n\t\t\"%a %b %d, %H:%M:%S %Y\", newtime);\n}\n\n\n\n\n\n/*\n================\nCon_Printf\n\nHandles cursor positioning, line wrapping, etc\n================\n*/\n#define\tMAXPRINTMSG\t4096\n// FIXME: make a buffer size safe vsprintf?\nvoid SV_FlushRedirect (void);\n#ifndef HAVE_CLIENT\n\nvfsfile_t *con_pipe;\n#ifdef HAVE_SERVER\nvfsfile_t *Con_POpen(const char *conname)\n{\n\tif (!conname || !*conname)\n\t{\n\t\tif (con_pipe)\n\t\t\tVFS_CLOSE(con_pipe);\n\t\tcon_pipe = VFSPIPE_Open(2, false);\n\t\treturn con_pipe;\n\t}\n\treturn NULL;\n}\n#endif\n\nstatic void Con_PrintFromThread (void *ctx, void *data, size_t a, size_t b)\n{\n\tCon_Printf(\"%s\", (char*)data);\n\tBZ_Free(data);\n}\nvoid VARGS Con_Printf (const char *fmt, ...)\n{\n\tva_list\t\targptr;\n\tchar\t\tmsg[MAXPRINTMSG];\n\n\tva_start (argptr,fmt);\n\tvsnprintf (msg,sizeof(msg)-1, fmt,argptr);\n\tva_end (argptr);\n\n\tif (!Sys_IsMainThread())\n\t{\n\t\tCOM_AddWork(WG_MAIN, Con_PrintFromThread, NULL, Z_StrDup(msg), 0, 0);\n\t\treturn;\n\t}\n\n#ifdef HAVE_SERVER\n\t// add to redirected message\n\tif (sv_redirected)\n\t{\n\t\tif (strlen (msg) + strlen(sv_redirected_buf) > sizeof(sv_redirected_buf) - 1)\n\t\t\tSV_FlushRedirect ();\n\t\tstrcat (sv_redirected_buf, msg);\n\t\tif (sv_redirected != -1)\n\t\t\treturn;\n\t}\n#endif\n\n\tSys_Printf (\"%s\", msg);\t// also echo to debugging console\n\tCon_Log(msg); // log to console\n\n\tif (con_pipe)\n\t\tVFS_PUTS(con_pipe, msg);\n}\nvoid Con_TPrintf (translation_t stringnum, ...)\n{\n\tva_list\t\targptr;\n\tchar\t\tmsg[MAXPRINTMSG];\n\tconst char *fmt;\n\n\tif (!Sys_IsMainThread())\n\t{\t//shouldn't be redirected anyway...\n\t\tfmt = localtext(stringnum);\n\t\tva_start (argptr,stringnum);\n\t\tvsnprintf (msg,sizeof(msg)-1, fmt,argptr);\n\t\tva_end (argptr);\n\t\tCOM_AddWork(WG_MAIN, Con_PrintFromThread, NULL, Z_StrDup(msg), 0, 0);\n\t\treturn;\n\t}\n\n#ifdef HAVE_SERVER\n\t// add to redirected message\n\tif (sv_redirected)\n\t{\n\t\tfmt = langtext(stringnum,sv_redirectedlang);\n\t\tva_start (argptr,stringnum);\n\t\tvsnprintf (msg,sizeof(msg)-1, fmt,argptr);\n\t\tva_end (argptr);\n\n\t\tif (strlen (msg) + strlen(sv_redirected_buf) > sizeof(sv_redirected_buf) - 1)\n\t\t\tSV_FlushRedirect ();\n\t\tstrcat (sv_redirected_buf, msg);\n\t\treturn;\n\t}\n#endif\n\n\tfmt = localtext(stringnum);\n\n\tva_start (argptr,stringnum);\n\tvsnprintf (msg,sizeof(msg)-1, fmt,argptr);\n\tva_end (argptr);\n\n\tSys_Printf (\"%s\", msg);\t// also echo to debugging console\n\tCon_Log(msg); // log to console\n\n\tif (con_pipe)\n\t\tVFS_PUTS(con_pipe, msg);\n}\n/*\n================\nCon_DPrintf\n\nA Con_Printf that only shows up if the \"developer\" cvar is set\n================\n*/\nstatic void Con_DPrintFromThread (void *ctx, void *data, size_t a, size_t b)\n{\n\tCon_DLPrintf(a, \"%s\", (char*)data);\n\tBZ_Free(data);\n}\nvoid Con_DPrintf (const char *fmt, ...)\n{\n\tva_list\t\targptr;\n\tchar\t\tmsg[MAXPRINTMSG];\n\textern cvar_t log_developer;\n\n\tif (!developer.value && !log_developer.value)\n\t\treturn;\n\n\tva_start (argptr,fmt);\n\tvsnprintf (msg,sizeof(msg)-1, fmt,argptr);\n\tva_end (argptr);\n\n\tif (!Sys_IsMainThread())\n\t{\n\t\tCOM_AddWork(WG_MAIN, Con_DPrintFromThread, NULL, Z_StrDup(msg), 0, 0);\n\t\treturn;\n\t}\n\n#ifdef HAVE_SERVER\n\t// add to redirected message\n\tif (sv_redirected)\n\t{\n\t\tif (strlen (msg) + strlen(sv_redirected_buf) > sizeof(sv_redirected_buf) - 1)\n\t\t\tSV_FlushRedirect ();\n\t\tstrcat (sv_redirected_buf, msg);\n\t\tif (sv_redirected != -1)\n\t\t\treturn;\n\t}\n#endif\n\n\tif (developer.value)\n\t\tSys_Printf (\"%s\", msg);\t// also echo to debugging console\n\n\tif (log_developer.value)\n\t\tCon_Log(msg); // log to console\n}\nvoid Con_DLPrintf (int level, const char *fmt, ...)\n{\n\tva_list\t\targptr;\n\tchar\t\tmsg[MAXPRINTMSG];\n\textern cvar_t log_developer;\n\n\tif (developer.ival < level && !log_developer.value)\n\t\treturn;\n\n\tva_start (argptr,fmt);\n\tvsnprintf (msg,sizeof(msg)-1, fmt,argptr);\n\tva_end (argptr);\n\n\tif (!Sys_IsMainThread())\n\t{\n\t\tCOM_AddWork(WG_MAIN, Con_DPrintFromThread, NULL, Z_StrDup(msg), level, 0);\n\t\treturn;\n\t}\n\n#ifdef HAVE_SERVER\n\t// add to redirected message\n\tif (sv_redirected)\n\t{\n\t\tif (strlen (msg) + strlen(sv_redirected_buf) > sizeof(sv_redirected_buf) - 1)\n\t\t\tSV_FlushRedirect ();\n\t\tstrcat (sv_redirected_buf, msg);\n\t\tif (sv_redirected != -1)\n\t\t\treturn;\n\t}\n#endif\n\n\tif (developer.ival >= level)\n\t\tSys_Printf (\"%s\", msg);\t// also echo to debugging console\n\n\tif (log_developer.value)\n\t\tCon_Log(msg); // log to console\n}\n\n//for spammed warnings, so they don't spam prints with every single frame/call. the timer arg should be a static local.\nvoid VARGS Con_ThrottlePrintf (float *timer, int developerlevel, const char *fmt, ...)\n{\n\tva_list\t\targptr;\n\tchar\t\tmsg[MAXPRINTMSG];\n\n\tva_start (argptr,fmt);\n\tvsnprintf (msg,sizeof(msg)-1, fmt,argptr);\n\tva_end (argptr);\n\n\tif (developerlevel)\n\t\tCon_DLPrintf (developerlevel, \"%s\", msg);\n\telse\n\t\tCon_Printf(\"%s\", msg);\n}\n#endif\n"
  },
  {
    "path": "engine/common/common.h",
    "content": "/*\nCopyright (C) 1996-1997 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n*/\n// comndef.h  -- general definitions\n\n#include <stdio.h>\n\n//make shared\n#ifndef QDECL\n\t#ifdef _MSC_VER\n\t\t#define QDECL _cdecl\n\t#else\n\t\t#define QDECL\n\t#endif\n#endif\n\n#define VK_NO_STDINT_H //we're handling this. please don't cause conflicts. grr.\n#if __STDC_VERSION__ >= 199901L || defined(__GNUC__) || _MSC_VER >= 1600\n\t//C99 has a stdint header which hopefully contains an intptr_t\n\t//its optional... but if its not in there then its unlikely you'll actually be able to get the engine to a stage where it *can* load anything\n\t#include <stdint.h>\n\ttypedef intptr_t qintptr_t;\n\ttypedef uintptr_t quintptr_t;\n\t#define qint16_t int16_t\n\t#define quint16_t uint16_t\n\t#define qint32_t int32_t\n\t#define quint32_t uint32_t\n\t#define qint64_t int64_t\n\t#define quint64_t uint64_t\n\t#define qintmax_t intmax_t\n\t#define quintmax_t uintmax_t\n#else\n\t#define qint8_t signed char\t//be explicit with this one.\n\t#define quint8_t unsigned char\n\t#define qint16_t short\n\t#define quint16_t unsigned short\n\t#define qint32_t int\n\t#define quint32_t unsigned qint32_t\n\t#if defined(_WIN64)\n\t\t#define qintptr_t __int64\n\t\t#define FTE_WORDSIZE 64\n\t\t#define quintptr_t unsigned qintptr_t\n\t\t#define qint64_t __int64\n\t\t#define quint64_t unsigned __int64\n\t#elif defined(_WIN32)\n\t\t#if !defined(_MSC_VER) || _MSC_VER < 1300\n\t\t\t#define __w64\n\t\t#endif\n\t\ttypedef __int32 __w64 qintptr_t;\t//add __w64 if you need msvc to shut up about unsafe type conversions\n\t\ttypedef unsigned __int32 __w64 quintptr_t;\n//\t\t#define qintptr_t __int32\n//\t\t#define quintptr_t unsigned qintptr_t\n\t\t#define qint64_t __int64\n\t\t#define quint64_t unsigned __int64\n\t\t#define FTE_WORDSIZE 32\n\t#else\n\t\t#ifdef __LP64__\n\t\t\t#define qintptr_t long int\n\t\t\t#define qint64_t long int\n\t\t\t#define FTE_WORDSIZE 64\n\t\t#elif __WORDSIZE == 64\n\t\t\t#define qintptr_t long long\n\t\t\t#define qint64_t long long\n\t\t\t#define FTE_WORDSIZE 64\n\t\t#else\n\t\t\t#define qintptr_t long\n\t\t\t#define qint64_t long long\n\t\t\t#define FTE_WORDSIZE 32\n\t\t#endif\n\t\t#define quintptr_t unsigned qintptr_t\n\t\t#define quint64_t unsigned qint64_t\n\t#endif\n\n\t#define qintmax_t qint64_t\n\t#define quintmax_t quint64_t\n\n\t#ifndef uint32_t\n\t\t#define int8_t\t\tqint8_t\n\t\t#define uint8_t\t\tquint8_t\n\t\t#define int16_t\t\tqint16_t\n\t\t#define uint16_t\tquint16_t\n\t\t#define int32_t\t\tqint32_t\n\t\t#define uint32_t\tquint32_t\n\t\t#define int64_t\t\tqint64_t\n\t\t#define uint64_t\tquint64_t\n\t\t#define intptr_t\tqintptr_t\n\t\t#define uintptr_t\tquintptr_t\n\t\t#define intmax_t\tqintmax_t\n\t\t#define uintmax_t\tquintmax_t\n\t#endif\n#endif\n\n#ifndef FTE_WORDSIZE\n\t#ifdef __WORDSIZE\n\t\t#define FTE_WORDSIZE __WORDSIZE\n\t#elif defined(_WIN64)\n\t\t#define FTE_WORDSIZE 64\n\t#else\n\t\t#define FTE_WORDSIZE 32\n\t#endif\n#endif\n\n#ifdef _MSC_VER\n\t#if _MSC_VER >= 1900\n\t\t// MSVC 14 supports these\n\t#elif _MSC_VER >= 1310\n\t\t#define strtoull _strtoui64\n\t\t#define strtoll _strtoi64\n\t#else\n\t\t#define strtoull strtoul\t//hopefully this won't cause too many issues\n\t\t#define strtoll strtol\t//hopefully this won't cause too many issues\n\t\t#define DWORD_PTR DWORD\t\t//32bit only\n\t\t#define ULONG_PTR ULONG\n\t#endif\n#endif\n\n\ntypedef unsigned char \t\tqbyte;\n\n// KJB Undefined true and false defined in SciTech's DEBUG.H header\n#undef true\n#undef false\n\n#ifdef __cplusplus\ntypedef enum {qfalse, qtrue} qboolean;//false and true are forcivly defined.\n//#define true qtrue\n//#define false qfalse\n#else\ntypedef enum {qfalse, qtrue}\tqboolean;\n#define true qtrue\n#define false qfalse\n#endif\n\n#define STRINGIFY2(s) #s\n#define STRINGIFY(s) STRINGIFY2(s)\n\n#define\tBASIC_INFO_STRING\t\t\t196\t//regular quakeworld. Sickening isn't it.\n#define\tEXTENDED_INFO_STRING\t1024\n#define\tMAX_SERVERINFO_STRING\t1024\t//standard quake has 512 here.\n#define\tMAX_LOCALINFO_STRING\t32768\n\n\n#ifdef HAVE_LEGACY\n#define legacyval(_legval,_newval) _legval\n#else\n#define legacyval(_legval,_newval) _newval\n#endif\n\n#ifdef HAVE_CLIENT\n#define cls_state cls.state\n#else\n#define cls_state 0\n#endif\n\n#ifdef HAVE_SERVER\n#define sv_state sv.state\n#else\n#define sv_state 0\n#endif\n\nstruct netprim_s\n{\n\tqbyte coordtype;\t//low 4 bits are the size, upper 4 are disambiguation...\n\t\t#define COORDTYPE_UNDEFINED\t\t0\t\t\t//invalid\n\t\t#define COORDTYPE_FIXED_13_3\t2\t\t\t//vanilla/etc\n\t\t#define COORDTYPE_FIXED_16_8\t3\t\t\t//rmq\n\t\t#define COORDTYPE_FIXED_28_4\t4\t\t\t//rmq, pointless\n\t\t#define COORDTYPE_FLOAT_32\t\t(4|0x80)\t//fte/dp/rmq\n\t\t#define COORDTYPE_SIZE_MASK\t\t0xf\t\t\t//coordtype&mask == number of bytes.\n\tqbyte anglesize;\n\tqbyte flags;\n\t\t#define NPQ2_ANG16\t\t\t\t(1u<<0)\n\t\t#define NPQ2_SOLID32\t\t\t(1u<<1)\n\t\t#define NPQ2_R1Q2_UCMD\t\t\t(1u<<2)\n\n\tqbyte pad;\n};\n//============================================================================\n\ntypedef enum {\n\tSZ_BAD,\n\tSZ_RAWBYTES,\n\tSZ_RAWBITS,\n\tSZ_HUFFMAN\t//q3 style packets are horrible.\n} sbpacking_t;\ntypedef struct sizebuf_s\n{\n\tqbyte\t\t*data;\n\tint\t\t\tmaxsize;\t//storage size of data\n\tint\t\t\tcursize;\t//assigned size of data\n\tqboolean\tallowoverflow;\t// if false, do a Sys_Error\n\tqboolean\toverflowed;\t\t// set to true if the buffer size failed\n\tsbpacking_t\tpacking;\t//required for q3\n\tint\t\t\tcurrentbit; //ignored for rawbytes\n\n\tstruct netprim_s prim;\t//for unsized write/read coord/angles\n} sizebuf_t;\n\nvoid SZ_Clear (sizebuf_t *buf);\nvoid *SZ_GetSpace (sizebuf_t *buf, int length);\nvoid SZ_Write (sizebuf_t *buf, const void *data, int length);\nvoid SZ_Print (sizebuf_t *buf, const char *data);\t// strcats onto the sizebuf\n\n//============================================================================\n\ntypedef struct link_s\n{\n\tstruct link_s\t*prev, *next;\n} link_t;\n\n#ifdef USEAREAGRID\ntypedef struct\n{\n\tlink_t l;\n\tvoid *ed;\n} areagridlink_t;\n#endif\n\n\nvoid ClearLink (link_t *l);\nvoid RemoveLink (link_t *l);\nvoid InsertLinkBefore (link_t *l, link_t *before);\nvoid InsertLinkAfter (link_t *l, link_t *after);\n\n// (type *)STRUCT_FROM_LINK(link_t *link, type, member)\n// ent = STRUCT_FROM_LINK(link,entity_t,order)\n// FIXME: remove this mess!\n#define\tSTRUCT_FROM_LINK(l,t,m) ((t *)((qbyte *)l - (qbyte*)&(((t *)0)->m)))\n\n#define FOR_EACH_LINK(l,node) for (l = node.next ; l != &node ; l = l->next)\n//============================================================================\n\n#ifndef NULL\n#define NULL ((void *)0)\n#endif\n\n#define Q_MAXCHAR ((char)0x7f)\n#define Q_MAXSHORT ((short)0x7fff)\n#define Q_MAXINT\t((int)0x7fffffff)\n#define Q_MAXLONG ((int)0x7fffffff)\n//#define Q_MAXFLOAT ((int)0x7fffffff)\n\n#define Q_MINCHAR ((char)0x80)\n#define Q_MINSHORT ((short)0x8000)\n#define Q_MININT \t((int)0x80000000)\n#define Q_MINLONG ((int)0x80000000)\n//#define Q_MINFLOAT ((int)0x7fffffff)\n\n//============================================================================\n\n#if defined(FTE_LITTLE_ENDIAN)\n\t#define bigendian\t\tfalse\n\n\t#define LittleShort(x)\t((short)(x))\n\t#define LittleLong(x)\t((int)(x))\n\t#define LittleI64(x)\t((qint64_t)(x))\n\t#define LittleFloat(x)\t((float)(x))\n\n\t#define BigShort(x)\t\t(ShortSwap(x))\n\t#define BigLong(x)\t\t(LongSwap(x))\n\t#define BigI64(x)\t\t(I64Swap(x))\n\t#define BigFloat(x)\t\t(FloatSwap(x))\n#elif defined(FTE_BIG_ENDIAN)\n\t#define bigendian\t\ttrue\n\n\t#define BigShort(x)\t\t((short)(x))\n\t#define BigLong(x)\t\t((int)(x))\n\t#define BigI64(x)\t\t((qint64_t)(x))\n\t#define BigFloat(x)\t\t((float)(x))\n\n\t#define LittleShort(x)\t(ShortSwap(x))\n\t#define LittleLong(x)\t(LongSwap(x))\n\t#define LittleI64(x)\t(I64Swap(x))\n\t#define LittleFloat(x)\t(FloatSwap(x))\n#else\n\textern\tqboolean\t\tbigendian;\n\n\textern\tshort\t(*BigShort) (short l);\n\textern\tshort\t(*LittleShort) (short l);\n\textern\tint\t(*BigLong) (int l);\n\textern\tint\t(*LittleLong) (int l);\n\textern\tqint64_t\t(*BigI64) (qint64_t l);\n\textern\tqint64_t\t(*LittleI64) (qint64_t l);\n\textern\tfloat\t(*BigFloat) (float l);\n\textern\tfloat\t(*LittleFloat) (float l);\n#endif\n\nshort\t\tShortSwap\t(short l);\nint\t\t\tLongSwap\t(int l);\nqint64_t    I64Swap\t\t(qint64_t l);\nfloat\t\tFloatSwap\t(float f);\n\nvoid COM_CharBias (signed char *c, int size);\nvoid COM_SwapLittleShortBlock (short *s, int size);\n\n//============================================================================\n\nstruct usercmd_s;\n\nextern const struct usercmd_s nullcmd;\n\ntypedef union {\t//note: reading from packets can be misaligned\n\tchar b[4];\n\tshort b2;\n\tint b4;\n\tfloat f;\n} coorddata;\nfloat MSG_FromCoord(coorddata c, int bytes);\ncoorddata MSG_ToCoord(float f, int bytes);\ncoorddata MSG_ToAngle(float f, int bytes);\n\nvoid MSG_BeginWriting (sizebuf_t *msg, struct netprim_s prim, void *bufferstorage, size_t buffersize);\nvoid MSG_WriteChar (sizebuf_t *sb, int c);\nvoid MSG_WriteBits (sizebuf_t *msg, int value, int bits);\nvoid MSG_WriteByte (sizebuf_t *sb, int c);\nvoid MSG_WriteShort (sizebuf_t *sb, int c);\nvoid MSG_WriteLong (sizebuf_t *sb, int c);\nvoid MSG_WriteInt64 (sizebuf_t *sb, qint64_t c);\nvoid MSG_WriteUInt64 (sizebuf_t *sb, quint64_t c);\nvoid MSG_WriteULEB128  (sizebuf_t *sb, quint64_t c);\nvoid MSG_WriteSignedQEX (sizebuf_t *sb, qint64_t c);\nvoid MSG_WriteEntity (sizebuf_t *sb, unsigned int e);\nvoid MSG_WriteFloat (sizebuf_t *sb, float f);\nvoid MSG_WriteDouble (sizebuf_t *sb, double f);\nvoid MSG_WriteString (sizebuf_t *sb, const char *s);\nvoid MSG_WriteCoord (sizebuf_t *sb, float f);\nvoid MSG_WriteBigCoord (sizebuf_t *sb, float f);\nvoid MSG_WriteAngle (sizebuf_t *sb, float f);\nvoid MSG_WriteAngle8 (sizebuf_t *sb, float f);\nvoid MSG_WriteAngle16 (sizebuf_t *sb, float f);\nvoid MSGFTE_WriteDeltaUsercmd (sizebuf_t *buf, const short baseanges[3], const struct usercmd_s *from, const struct usercmd_s *cmd);\nvoid MSGQW_WriteDeltaUsercmd (sizebuf_t *sb, const struct usercmd_s *from, const struct usercmd_s *cmd);\nvoid MSGCL_WriteDeltaUsercmd (sizebuf_t *sb, const struct usercmd_s *from, const struct usercmd_s *cmd);\nvoid MSG_WriteDir (sizebuf_t *sb, float dir[3]);\n\nextern\tqboolean\tmsg_badread;\t\t// set if a read goes beyond end of message\nextern struct netprim_s msg_nullnetprim;\n\nint MSG_PeekByte(void);\n\nvoid MSG_BeginReading (sizebuf_t *sb, struct netprim_s prim);\nvoid MSG_ChangePrimitives(struct netprim_s prim);\nint MSG_GetReadCount(void);\nint MSG_ReadChar (void);\nint MSG_ReadBits(int bits);\nint MSG_ReadByte (void);\nint MSG_ReadShort (void);\nint MSG_ReadUInt16 (void);\nint MSG_ReadLong (void);\nqint64_t MSG_ReadInt64 (void);\nquint64_t MSG_ReadUInt64 (void);\nqint64_t MSG_ReadSLEB128 (void);\nquint64_t MSG_ReadULEB128 (void);\nqint64_t MSG_ReadSignedQEX (void);\nstruct client_s;\nunsigned int MSGSV_ReadEntity (struct client_s *fromclient);\nunsigned int MSGCL_ReadEntity (void);\nunsigned int MSG_ReadBigEntity(void);\nfloat MSG_ReadFloat (void);\ndouble MSG_ReadDouble (void);\nchar *MSG_ReadStringBuffer (char *out, size_t outsize);\nchar *MSG_ReadString (void);\nchar *MSG_ReadStringLine (void);\n\nfloat MSG_ReadCoord (void);\nfloat MSG_ReadCoordFloat (void);\nvoid MSG_ReadPos (float *pos);\t//uses md2 normals to approximate a unit vector into a single byte.\nvoid MSG_ReadDir (float *dir);\t//simply 3 coords\nfloat MSG_ReadAngle (void);\nfloat MSG_ReadAngle16 (void);\nvoid MSGQW_ReadDeltaUsercmd (const struct usercmd_s *from, struct usercmd_s *cmd, int qwprotocolver);\nvoid MSGFTE_ReadDeltaUsercmd (const struct usercmd_s *from, struct usercmd_s *move);\nvoid MSGQ2_ReadDeltaUsercmd (struct client_s *cl, const struct usercmd_s *from, struct usercmd_s *move);\nvoid MSG_ReadData (void *data, int len);\nvoid MSG_ReadSkip (int len);\n\nint MSG_ReadSize16 (sizebuf_t *sb);\nvoid MSG_WriteSize16 (sizebuf_t *sb, unsigned int sz);\nvoid COM_DecodeSize(int solid, float *mins, float *maxs);\nint COM_EncodeSize(const float *mins, const float *maxs);\n\n//============================================================================\n\nchar *Q_strcpyline(char *out, const char *in, int maxlen);\t//stops at '\\n' (and '\\r')\n\nvoid Q_ftoa(char *str, float in);\nchar *Q_strlwr(char *str);\nint wildcmp(const char *wild, const char *string);\t//1 if match\n\n#define Q_memset(d, f, c) memset((d), (f), (c))\n#define Q_memcpy(d, s, c) memcpy((d), (s), (c))\n#define Q_memmove(d, s, c) memmove((d), (s), (c))\n#define Q_memcmp(m1, m2, c) memcmp((m1), (m2), (c))\n#define Q_strcpy(d, s) strcpy((d), (s))\n#define Q_strncpy(d, s, n) strncpy((d), (s), (n))\n#define Q_strlen(s) ((int)strlen(s))\n#define Q_strrchr(s, c) strrchr((s), (c))\n#define Q_strcat(d, s) strcat((d), (s))\n#define Q_strcmp(s1, s2) strcmp((s1), (s2))\n#define Q_strncmp(s1, s2, n) strncmp((s1), (s2), (n))\n\nqboolean VARGS Q_snprintfz (char *dest, size_t size, const char *fmt, ...) LIKEPRINTF(3);\t//true means truncated (will also warn in debug builds).\nqboolean VARGS Q_vsnprintfz (char *dest, size_t size, const char *fmt, va_list args);\t\t//true means truncated (will also warn in debug builds).\nvoid VARGS Com_sprintf(char *buffer, int size, const char *format, ...) LIKEPRINTF(3);\n\n#define Q_strncpyS(d, s, n) do{const char *____in=(s);char *____out=(d);int ____i; for (____i=0;*(____in); ____i++){if (____i == (n))break;*____out++ = *____in++;}if (____i < (n))*____out='\\0';}while(0)\t//only use this when it should be used. If undiciided, use N\n#define Q_strncpyN(d, s, n) do{if (n < 0)Sys_Error(\"Bad length in strncpyz\");Q_strncpyS((d), (s), (n));((char *)(d))[n] = '\\0';}while(0)\t//this'll stop me doing buffer overflows. (guarenteed to overflow if you tried the wrong size.)\n//#define Q_strncpyNCHECKSIZE(d, s, n) do{if (n < 1)Sys_Error(\"Bad length in strncpyz\");Q_strncpyS((d), (s), (n));((char *)(d))[n-1] = '\\0';((char *)(d))[n] = '255';}while(0)\t//This forces nothing else to be within the buffer. Should be used for testing and nothing else.\n#if 0\n#define Q_strncpyz(d, s, n) Q_strncpyN(d, s, (n)-1)\n#else\nvoid QDECL Q_strncpyz(char*d, const char*s, int n);\n#define Q_strncatz(dest, src, sizeofdest)\t\\\n\tdo {\t\\\n\t\tstrncat(dest, src, sizeofdest - strlen(dest) - 1);\t\\\n\t\t(dest)[sizeofdest - 1] = 0;\t\\\n\t} while (0)\n#define Q_strncatz2(dest, src)\tQ_strncatz(dest, src, sizeof(dest))\n#endif\n//#define Q_strncpy Please remove all strncpys\n/*#ifndef strncpy\n#define strncpy Q_strncpy\n#endif*/\n\n/*replacement functions which do not care for locale in text formatting ('C' locale), or are non-standard*/\nchar *Q_strcasestr(const char *haystack, const char *needle);\n#if !defined(IQMTOOL) && !defined(WEBSVONLY) && !defined(FTEPLUGIN)\nint Q_strncasecmp (const char *s1, const char *s2, int n);\nint Q_strcasecmp (const char *s1, const char *s2);\n#endif\nint Q_strstopcasecmp(const char *s1start, const char *s1end, const char *s2);\nint\tQ_atoi (const char *str);\nfloat Q_atof (const char *str);\nvoid deleetstring(char *result, const char *leet);\n\n\n//============================================================================\n\nextern\tchar\t\tcom_token[65536];\n\ntypedef enum com_tokentype_e {TTP_UNKNOWN, TTP_STRING, TTP_LINEENDING, TTP_RAWTOKEN, TTP_EOF, TTP_PUNCTUATION} com_tokentype_t;\nextern com_tokentype_t com_tokentype;\n\n//these cast away the const for the return value.\n//char *COM_Parse (const char *data);\n#define COM_Parse(d) COM_ParseOut(d,com_token, sizeof(com_token))\n#define COM_ParseOut(d,o,l) COM_ParseType(d,o,l,NULL)\nchar *COM_ParseType (const char *data, char *out, size_t outlen, com_tokentype_t *toktype);\nchar *COM_ParseStringSet (const char *data, char *out, size_t outlen);\t//whitespace or semi-colon separators\nchar *COM_ParseStringSetSep (const char *data, char sep, char *out, size_t outsize);\t//single-char-separator, no whitespace\nchar *COM_ParseCString (const char *data, char *out, size_t maxoutlen, size_t *writtenlen);\nchar *COM_StringParse (const char *data, char *token, unsigned int tokenlen, qboolean expandmacros, qboolean qctokenize);\t//fancy version used for console etc parsing\n#define COM_ParseToken(data,punct) COM_ParseTokenOut(data, punct, com_token, sizeof(com_token), &com_tokentype)\nchar *COM_ParseTokenOut (const char *data, const char *punctuation, char *token, size_t tokenlen, com_tokentype_t *tokentype);\t//note that line endings are a special type of token.\nqboolean COM_TrimString(char *str, char *buffer, int buffersize);\t//trims leading+trailing whitespace writing to the specified buffer. returns false on truncation.\nconst char *COM_QuotedString(const char *string, char *buf, int buflen, qboolean omitquotes);\t//inverse of COM_StringParse\n\n\nextern\tint\t\tcom_argc;\nextern\tconst char\t**com_argv;\n\nint COM_CheckParm (const char *parm);\t//WARNING: Legacy arguments should be listed in CL_ArgumentOverrides!\nint COM_CheckNextParm (const char *parm, int last);\nvoid COM_AddParm (const char *parm);\n\nvoid COM_Shutdown (void);\nvoid COM_Init (void);\nvoid COM_InitArgv (int argc, const char **argv);\nvoid COM_ParsePlusSets (qboolean docbuf);\n\ntypedef unsigned int conchar_t;\nchar *COM_DeFunString(conchar_t *str, conchar_t *stop, char *out, int outsize, qboolean ignoreflags, qboolean forceutf8);\n#define PFS_KEEPMARKUP\t\t1\t//leave markup in the final string (but do parse it)\n#define PFS_FORCEUTF8\t\t2\t//force utf-8 decoding\n#define PFS_NOMARKUP\t\t4\t//strip markup completely\n#ifdef HAVE_LEGACY\n#define PFS_EZQUAKEMARKUP\t8\t//aim for compat with ezquake instead of q3 compat\n#endif\n#define PFS_CENTERED\t\t16\t//flag used by console prints (text should remain centered)\n#define PFS_NONOTIFY\t\t32\t//flag used by console prints (text won't be visible other than by looking at the console)\nconchar_t *COM_ParseFunString(conchar_t defaultflags, const char *str, conchar_t *out, int outsize_bytes, int keepmarkup);\t//ext is usually CON_WHITEMASK, returns its null terminator\nunsigned int utf8_decode(int *error, const void *in, char const**out);\nunsigned int utf8_encode(void *out, unsigned int unicode, int maxlen);\nunsigned int iso88591_encode(char *out, unsigned int unicode, int maxlen, qboolean markup);\nunsigned int qchar_encode(char *out, unsigned int unicode, int maxlen, qboolean markup);\nunsigned int COM_DeQuake(unsigned int unichar);\n\nvoid COM_BiDi_Shutdown(void);\n\n//small macro to tell COM_ParseFunString (and related functions like con_printf) that the input is a utf-8 string.\n#define U8(s) \"^`u8:\" s \"`=\"\n\n//handles whatever charset is active, including ^U stuff.\nunsigned int unicode_byteofsfromcharofs(const char *str, unsigned int charofs, qboolean markup);\nunsigned int unicode_charofsfrombyteofs(const char *str, unsigned int byteofs, qboolean markup);\nunsigned int unicode_encode(char *out, unsigned int unicode, int maxlen, qboolean markup);\nunsigned int unicode_decode(int *error, const void *in, char const**out, qboolean markup);\nsize_t unicode_strtolower(const char *in, char *out, size_t outsize, qboolean markup);\nsize_t unicode_strtoupper(const char *in, char *out, size_t outsize, qboolean markup);\nunsigned int unicode_charcount(const char *in, size_t buffersize, qboolean markup);\nvoid unicode_strpad(char *out, size_t outsize, const char *in, qboolean leftalign, size_t minwidth, size_t maxwidth, qboolean markup);\n\nchar *COM_SkipPath (const char *pathname);\nvoid QDECL COM_StripExtension (const char *in, char *out, int outlen);\nvoid COM_StripAllExtensions (const char *in, char *out, int outlen);\nvoid COM_FileBase (const char *in, char *out, int outlen);\nint QDECL COM_FileSize(const char *path);\nvoid COM_DefaultExtension (char *path, const char *extension, int maxlen);\nqboolean COM_RequireExtension(char *path, const char *extension, int maxlen);\nchar *COM_FileExtension (const char *in, char *result, size_t sizeofresult);\t//copies the extension, without the dot\nconst char *COM_GetFileExtension (const char *in, const char *term);\t//returns the extension WITH the dot, allowing for scanning backwards.\nvoid COM_CleanUpPath(char *str);\n\nchar\t*VARGS va(const char *format, ...) LIKEPRINTF(1);\n// does a varargs printf into a temp buffer\n\n//============================================================================\n\nstruct cache_user_s;\n\nextern char\tcom_gamepath[MAX_OSPATH];\nextern char\tcom_homepath[MAX_OSPATH];\n//extern char\tcom_configdir[MAX_OSPATH];\t//dir to put cfg_save configs in\n//extern\tchar\t*com_basedir;\n\n//qofs_Make is used to 'construct' a variable of qofs_t type. this is so the code can merge two 32bit ints on old systems and use a long long type internally without generating warnings about bit shifts when qofs_t is only 32bit instead.\n//#if defined(__amd64__) || defined(_AMD64_) || __WORDSIZE == 64\n#if !defined(FTE_TARGET_WEB)\n\t#if !defined(_MSC_VER) || _MSC_VER > 1200\n\t\t#define FS_64BIT\n\t#endif\n#endif\n#ifdef FS_64BIT\n\t//we should probably use off_t here, but then we have fun as to whether its actually 64bit or not, which results in warnings and problems with printf etc.\n\ttypedef quint64_t qofs_t;\t//type to use for a file offset\n\t#define qofs_Make(low,high) (low | (((qofs_t)(high))<<32))\n\t#define qofs_Low(ofs) ((ofs)&0xffffffffu)\n\t#define qofs_High(ofs) ((ofs)>>32)\n\t#define qofs_Error(ofs) ((ofs) == ~(quint64_t)0u)\n\n\t#define PRIxQOFS PRIx64\n\t#define PRIuQOFS PRIu64\n#else\n\ttypedef quint32_t qofs_t;\t//type to use for a file offset\n\t#define qofs_Make(low,high) (low)\n\t#define qofs_Low(ofs) (ofs)\n\t#define qofs_High(ofs) (0)\n\t#define qofs_Error(ofs) ((ofs) == ~0ul)\n\n\t#define PRIxQOFS \"x\"\n\t#define PRIuQOFS \"u\"\n#endif\n#define qofs_ErrorValue() (~(qofs_t)0u)\n\ntypedef struct searchpathfuncs_s searchpathfuncs_t;\ntypedef struct searchpath_s\n{\n\tsearchpathfuncs_t *handle;\n\n\tunsigned int flags; //SPF_*\n\n\tchar logicalpath[MAX_OSPATH];\t//printable hunam-readable location of the package. generally includes a system path, including nested packages.\n\tchar purepath[256];\t//server tracks the path used to load them so it can tell the client\n\tchar prefix[MAX_QPATH];\t//prefix to add to each file within the archive. may also be \"..\" to mean ignore the top-level path.\n\tint crc_seed;\t//can skip some hashes if this is cached.\n\tint crc_check;\t//client sorts packs according to this checksum\n\tint crc_reply;\t//client sends a different crc back to the server, for the paks it's actually loaded.\n\tint orderkey;\t//used to check to see if the paths were actually changed or not.\n\n\tstruct searchpath_s *next;\n\tstruct searchpath_s *nextpure;\n} searchpath_t;\ntypedef struct flocation_s{\n\tstruct searchpath_s\t*search;\t\t\t//used to say which filesystem driver to open the file from\n\tvoid\t\t\t*fhandle;\t\t\t\t//used by the filesystem driver as a simple reference to the file\n\tchar\t\t\trawname[MAX_OSPATH];\t//blank means not readable directly\n\tqofs_t\t\t\toffset;\t\t\t\t\t//only usable if rawname is set.\n\tqofs_t\t\t\tlen;\t\t\t\t\t//uncompressed length\n} flocation_t;\nstruct vfsfile_s;\n\n#define FSLF_IFFOUND\t\t\t0\t\t//\n#define FSLF_DEEPONFAILURE\t\t(1u<<0)\t//upon failure, report that the file is so far into the filesystem as to be irrelevant\n#define FSLF_DEPTH_INEXPLICIT\t(1u<<1)\t//depth is incremented for EVERY package, not just system/explicit paths.\n#define FSLF_IGNOREBASEDEPTH\t(1u<<3)\t//depth is incremented for explicit mod paths, but not id1/qw/fte/paks/pk3s\n#define FSLF_SECUREONLY\t\t\t(1u<<4)\t//ignore files from downloaded packages (ie: configs)\n#define FSLF_DONTREFERENCE\t\t(1u<<5) //don't add any reference flags to packages\n#define FSLF_IGNOREPURE\t\t\t(1u<<6) //use only the client's package list, ignore any lists obtained from the server (including any reordering)\n#define FSLF_IGNORELINKS\t\t(1u<<7) //ignore any pak/pk3 symlinks. system ones may still be followed.\n#define FSLF_QUIET\t\t\t\t(1u<<8)\t//don't spam warnings about any dodgy paths.\n\n//if loc is valid, loc->search is always filled in, the others are filled on success.\n//standard return value is 0 on failure, or depth on success.\nint FS_FLocateFile(const char *filename, unsigned int flags, flocation_t *loc);\nstruct vfsfile_s *FS_OpenReadLocation(const char *fname, flocation_t *location);\t//fname used for extension-based filters\n#define WP_REFERENCE\t1\n#define WP_FULLPATH\t\t2\n#define WP_FORCE\t\t4\nconst char *FS_WhichPackForLocation(flocation_t *loc, unsigned int flags);\nqboolean FS_GetLocationForPackageHandle(flocation_t *loc, searchpathfuncs_t *spath, const char *fname);\nqboolean FS_GetLocMTime(flocation_t *location, time_t *modtime);\nconst char *FS_GetPackageDownloadFilename(flocation_t *loc);\t//returns only packages (or null)\nconst char *FS_GetRootPackagePath(flocation_t *loc);\t\t\t//favours packages, but falls back on gamedirs.\n\nsearchpath_t *FS_GetPackage(const char *package); //for fancy stuff that should probably have its own helper...\nqboolean FS_GetPackageDownloadable(const char *package);\nchar *FS_GetPackHashes(char *buffer, int buffersize, qboolean referencedonly);\nchar *FS_GetPackNames(char *buffer, int buffersize, int referencedonly, qboolean ext);\nqboolean FS_GenCachedPakName(const char *pname, const char *crc, char *local, int llen);\t//returns false if the name is invalid.\nvoid FS_ReferenceControl(unsigned int refflag, unsigned int resetflags);\n\n#define FDEPTH_MISSING 0x7fffffff\n#define COM_FDepthFile(filename,ignorepacks) FS_FLocateFile(filename,FSLF_DONTREFERENCE|FSLF_DEEPONFAILURE|(ignorepacks?0:FSLF_DEPTH_INEXPLICIT), NULL)\n#define COM_FCheckExists(filename) FS_FLocateFile(filename,FSLF_IFFOUND, NULL)\n\ntypedef struct vfsfile_s\n{\n\tint (QDECL *ReadBytes) (struct vfsfile_s *file, void *buffer, int bytestoread);\n\tint (QDECL *WriteBytes) (struct vfsfile_s *file, const void *buffer, int bytestowrite);\n\tqboolean (QDECL *Seek) (struct vfsfile_s *file, qofs_t pos);\t//returns false for error\n\tqofs_t (QDECL *Tell) (struct vfsfile_s *file);\n\tqofs_t (QDECL *GetLen) (struct vfsfile_s *file);\t//could give some lag\n\tqboolean (QDECL *Close) (struct vfsfile_s *file);\t//returns false if there was some error.\n\tvoid (QDECL *Flush) (struct vfsfile_s *file);\n\tenum\n\t{\n\t\tSS_SEEKABLE,\n\t\tSS_SLOW,\t//probably readonly, its fine for an occasional seek, its just really. really. slow.\n\t\tSS_PIPE,\t//read can be seeked, write appends only.\n\t\tSS_UNSEEKABLE\n\t} seekstyle;\n\n#ifdef _DEBUG\n\tchar dbgname[MAX_QPATH];\n#endif\n} vfsfile_t;\n\n#define VFS_ERROR_TRYLATER\t\t0\t//nothing to write/read yet.\n#define VFS_ERROR_UNSPECIFIED\t-1\t//no reason given\n#define VFS_ERROR_NORESPONSE\t-2\t//tcp connection timed out without any response.\n#define VFS_ERROR_REFUSED\t\t-3\t//tcp connection got an error while connecting (woo, icmp packets actually got through for once!).\n#define VFS_ERROR_EOF\t\t\t-4\t//end-of-file/stream (for pipes, the other end closed it)\n#define VFS_ERROR_DNSFAILURE\t-5\t//weird one, but oh well.\n#define VFS_ERROR_WRONGCERT\t\t-6\t//server gave a certificate with the wrong name\n#define VFS_ERROR_UNTRUSTED\t\t-7\t//server gave a certificate with the right name, but we don't have a full chain of trust\n\n#define VFS_CLOSE(vf) ((vf)->Close(vf))\n#define VFS_TELL(vf) ((vf)->Tell(vf))\n#define VFS_GETLEN(vf) ((vf)->GetLen(vf))\n#define VFS_SEEK(vf,pos) ((vf)->Seek(vf,pos))\n#define VFS_READ(vf,buffer,buflen) ((vf)->ReadBytes(vf,buffer,buflen))\n#define VFS_WRITE(vf,buffer,buflen) ((vf)->WriteBytes(vf,buffer,buflen))\n#define VFS_FLUSH(vf) do{if((vf)->Flush)(vf)->Flush(vf);}while(0)\n#define VFS_PUTS(vf,s) do{const char *t=s;(vf)->WriteBytes(vf,t,strlen(t));}while(0)\nchar *VFS_GETS(vfsfile_t *vf, char *buffer, size_t buflen);\nvoid VARGS VFS_PRINTF(vfsfile_t *vf, const char *fmt, ...) LIKEPRINTF(2);\n\nenum fs_relative{\n\t//note that many of theses paths can map to multiple system locations. FS_SystemPath/FS_DisplayPath can vary somewhat in terms of what it returns, generally favouring writable locations rather then the path that actually contains a file.\n\tFS_BINARYPATH,\t//where the 'exe' is located. we'll check here for dlls too.\n\tFS_LIBRARYPATH,\t//for system dlls and stuff\n\tFS_ROOT,\t\t//./ (effectively -homedir if enabled, otherwise effectively -basedir arg)\n\tFS_SYSTEM,\t\t//a system path. absolute paths are explicitly allowed and expected, but not required.\n#define FS_RELATIVE_ISSPECIAL(r) ((r)<FS_GAME)\n\t//after this point, all types must be relative to a gamedir\n\tFS_GAME,\t\t//standard search (not generally valid for writing/save/rename/delete/etc)\n\tFS_GAMEONLY,\t//$gamedir/\n\tFS_BASEGAMEONLY,\t//fte/\n\tFS_PUBGAMEONLY,\t\t//$gamedir/ or qw/ but not fte/\n\tFS_PUBBASEGAMEONLY\t//qw/ (fixme: should be the last non-private basedir)\n};\n\nqboolean COM_WriteFile (const char *filename, enum fs_relative fsroot, const void *data, int len);\n\nchar *FS_AbbreviateSize(char *buf, size_t bufsize, qofs_t fsize);\t//just formats a filesize into the buffer and returns it.\n\nvoid FS_FlushFSHashWritten(const char *fname);\nvoid FS_FlushFSHashRemoved(const char *fname);\nvoid FS_FlushFSHashFull(void);\t//too much/unknown changed...\nvoid FS_CreatePath(const char *pname, enum fs_relative relativeto);\nqboolean FS_Rename(const char *oldf, const char *newf, enum fs_relative relativeto);\t//0 on success, non-0 on error\nqboolean FS_Rename2(const char *oldf, const char *newf, enum fs_relative oldrelativeto, enum fs_relative newrelativeto);\nqboolean FS_Remove(const char *fname, enum fs_relative relativeto);\t//0 on success, non-0 on error\nqboolean FS_RemoveTree(searchpathfuncs_t *pathhandle, const char *fname);\nqboolean FS_Copy(const char *source, const char *dest, enum fs_relative relativesource, enum fs_relative relativedest);\n//qboolean FS_NativePath(const char *fname, enum fs_relative relativeto, char *out, int outlen);\t//if you really need to fopen yourself\nqboolean FS_SystemPath(const char *fname, enum fs_relative relativeto, char *out, int outlen);\t//if you really need to fopen yourself\nqboolean FS_DisplayPath(const char *fname, enum fs_relative relativeto, char *out, int outlen);\t//retrieves a string for user display. prefixes may be masked for privacy.\nqboolean FS_WriteFile (const char *filename, const void *data, int len, enum fs_relative relativeto);\nvoid *FS_MallocFile(const char *filename, enum fs_relative relativeto, qofs_t *filesize);\nvfsfile_t *QDECL FS_OpenVFS(const char *filename, const char *mode, enum fs_relative relativeto);\nvfsfile_t *FS_OpenTemp(void);\nvfsfile_t *FS_OpenTCP(const char *name, int defaultport, qboolean assumetls);\n\nvfsfile_t *FS_OpenWithFriends(const char *fname, char *sysname, size_t sysnamesize, int numfriends, ...);\n\n#define countof(array) (sizeof(array)/sizeof((array)[0]))\n#ifdef _WIN32\n//windows doesn't support utf-8. Which is a shame really, because that's the charset we expect from everything.\nchar *narrowen(char *out, size_t outlen, wchar_t *wide);\nwchar_t *widen(wchar_t *out, size_t outbytes, const char *utf8);\n#define __L(x) L ## x\n#define _L(x) __L(x)\nint MyRegGetIntValue(void *base, const char *keyname, const char *valuename, int defaultval);\nqboolean MyRegGetStringValue(void *base, const char *keyname, const char *valuename, void *data, size_t datalen);\t//data is utf8\nqboolean MyRegGetStringValueMultiSz(void *base, const char *keyname, const char *valuename, void *data, int datalen);\nqboolean MyRegSetValue(void *base, const char *keyname, const char *valuename, int type, const void *data, int datalen);\t//string values are utf8\nvoid MyRegDeleteKeyValue(void *base, const char *keyname, const char *valuename);\n#endif\n\nvoid FS_UnloadPackFiles(void);\nvoid FS_ReloadPackFiles(void);\nchar *FSQ3_GenerateClientPacksList(char *buffer, int maxlen, int basechecksum);\nvoid FS_PureMode(const char *gamedir, int mode, char *purenamelist, char *purecrclist, char *refnamelist, char *refcrclist, int seed);\t//implies an fs_restart. ref package names are optional, for q3 where pure names don't contain usable paths\nint FS_PureOkay(void);\n\n//recursively tries to open files until it can get a zip.\nvfsfile_t *CL_OpenFileInPackage(searchpathfuncs_t *search, char *name);\nqboolean CL_ListFilesInPackage(searchpathfuncs_t *search, char *name, int (QDECL *func)(const char *fname, qofs_t fsize, time_t mtime, void *parm, searchpathfuncs_t *spath), void *parm, void *recursioninfo);\n\nqbyte *QDECL COM_LoadStackFile (const char *path, void *buffer, int bufsize, size_t *fsize);\nqbyte *COM_LoadTempFile (const char *path, unsigned int locateflags, size_t *fsize);\nqbyte *COM_LoadTempMoreFile (const char *path, size_t *fsize);\t//allocates a little bit more without freeing old temp\n//qbyte *COM_LoadHunkFile (const char *path);\n\nsearchpathfuncs_t *COM_IteratePaths (void **iterator, char *pathbuffer, int pathbuffersize, char *dirname, int dirnamesize);\nvoid COM_FlushFSCache(qboolean purge, qboolean domutex);\t//a file was written using fopen\nqboolean FS_Restarted(unsigned int *since);\n\nenum manifestdeptype_e\n{\n\tmdt_invalid,\n\tmdt_singlepackage,\t//regular package, versioned.\n\tmdt_installation\t//allowed to install to the root, only downloaded as part of an initial install.\n};\ntypedef struct\n{\n\tchar *filename;\t\t//filename the manifest was read from. not necessarily writable... NULL when the manifest is synthesised or from http.\n\tenum\n\t{\n\t\tMANIFEST_SECURITY_NOT,\t\t//don't trust it, don't even allow downloadsurl.\n\t\tMANIFEST_SECURITY_DEFAULT,\t//the default.fmf file may suggest packages+force sources\n\t\tMANIFEST_SECURITY_INSTALLER\t//built-in fmf files can 'force' packages+sources\n\t} security;\t\t//manifest was embedded in the engine. don't assume its already installed, but ask to install it (also, enable some extra permissions for writing dlls)\n\n\tenum\n\t{\n\t\tMANIFEST_UNSPECIFIED=0,\n\t\tMANIFEST_CURRENTVER\n\t} parsever;\n\tint minver;\t//if the engine svn revision is lower than this, the manifest will not be used as an 'upgrade'.\n\tint maxver;\t//if not 0, the manifest will not be used\n\tenum\n\t{\n\t\tMANIFEST_HOMEDIRWHENREADONLY=0,\n\t\tMANIFEST_NOHOMEDIR,\n\t\tMANIFEST_FORCEHOMEDIR,\n\t} homedirtype;\n\tchar *mainconfig;\t//eg \"fte.cfg\", reducing conflicts with other engines, but can be other values...\n\tchar *updateurl;\t//url to download an updated manifest file from.\n\tqboolean blockupdate;\t//set to block the updateurl from being used this session. this avoids recursive updates when manifests contain the same update url.\n\tchar *installation;\t//optional hardcoded commercial name, used for scanning the registry to find existing installs.\n\tchar *formalname;\t//the commercial name of the game. you'll get FULLENGINENAME otherwise.\n#ifdef PACKAGEMANAGER\n\tchar *downloadsurl;\t//optional installable files (menu)\n\tchar *installupd;\t//which download/updated package to install.\n\tqboolean installable;\t//(expected) available packages give a playable experience, even if just a basic/demo version.\n#endif\n\tchar *protocolname;\t//the name used for purposes of dpmaster\n\tchar *defaultexec;\t//execed after cvars are reset, to give game-specific engine-defaults.\n\tchar *defaultoverrides;\t//execed after default.cfg, to give usable defaults even when the mod the user is running is shit.\n\tchar *eula;\t\t\t//when running as an installer, the user will be presented with this as a prompt\n\tchar *basedir;\t\t//this is where we expect to find the data.\n\tchar *iconname;\t\t//path we can find the icon (relative to the fmf's location)\n\n\tchar *schemes;\t\t//protocol scheme used to connect to a server running this game, use com_parse.\n\tstruct\n\t{\n\t\tenum\n\t\t{\n\t\t\tGAMEDIR_DEFAULTFLAGS=0,\t\t//forgotten on gamedir switches (and a higher priority)\n\t\t\tGAMEDIR_BASEGAME=1u<<0,\t\t//not forgotten on gamedir switches (and a lower priority)\n\t\t\tGAMEDIR_PRIVATE=1u<<1,\t\t//don't report as the gamedir on networks.\n\t\t\tGAMEDIR_READONLY=1u<<2,\t\t//don't write here...\n\t\t\tGAMEDIR_USEBASEDIR=1u<<3,\t//packages will be read from the basedir (and homedir), but not other files. path is an empty string.\n\t\t\tGAMEDIR_STEAMGAME=1u<<4,\t//finds the game via steam. must also be private+readonly.\n\t\t\tGAMEDIR_QSHACK=1u<<8,\n\n\t\t\tGAMEDIR_SPECIAL=GAMEDIR_USEBASEDIR|GAMEDIR_STEAMGAME,\t//if one of these flags, then the gamedir cannot be simply concatenated to the basedir/homedir.\n\t\t} flags;\n\t\tchar *path;\n\t} gamepath[8];\n\tstruct manpack_s\t//FIXME: this struct should be replaced with packagemanager info instead.\n\t{\n\t\tenum manifestdeptype_e type;\n\t\tchar *packagename;\t//\"package:arch=ver\"\n\t\tchar *path;\t\t\t//the 'pure' name\n\t\tchar *prefix;\n\t\tqboolean crcknown;\t//if the crc was specified\n\t\tunsigned int crc;\t//the public crc\n\t\tchar *mirrors[8];\t//a randomized (prioritized-on-load) list of mirrors to use. (may be 'prompt:game,package', 'unzip:file,url', 'xz:url', 'gz:url'\n\t\tchar *condition;\t//only downloaded if this cvar is set | delimited allows multiple cvars.\n\t\tchar *sha512;\t\t//package must match this hash, if specified\n\t\tchar *signature;\t//signs the hash\n\t\tqofs_t filesize;\n\t\tint mirrornum;\t\t//the index we last tried to download from, so we still work even if mirrors are down.\n\t} package[64];\n} ftemanifest_t;\nextern ftemanifest_t\t*fs_manifest;\t//currently active manifest.\nvoid FS_Manifest_Free(ftemanifest_t *man);\nftemanifest_t *FS_Manifest_ReadMem(const char *fname, const char *basedir, const char *data);\nftemanifest_t *FS_Manifest_ReadSystem(const char *fname, const char *basedir);\nvoid PM_Shutdown(qboolean soft);\nvoid PM_Command_f(void);\nqboolean PM_CanInstall(const char *packagename);\n\nvoid COM_InitFilesystem (void);\t//does not set up any gamedirs.\nqboolean FS_DownloadingPackage(void);\nvoid FS_CreateBasedir(const char *path);\nqboolean FS_DirHasAPackage(char *basedir, ftemanifest_t *man);\nqboolean FS_ChangeGame(ftemanifest_t *newgame, qboolean allowreloadconfigs, qboolean allowbasedirchange);\nqboolean FS_GameIsInitialised(void);\nvoid FS_Shutdown(void);\nstruct gamepacks\n{\n\tchar *package;\n\tchar *path;\n\tchar *url;\n\tchar *subpath;\t//within the package (for zips)\n};\nvoid COM_Gamedir (const char *dir, const struct gamepacks *packagespaths);\nqboolean FS_PathURLCache(const char *url, char *path, size_t pathsize);\t//converts a url to something that can be shoved into a filesystem\nchar *FS_GetGamedir(qboolean publicpathonly);\nchar *FS_GetManifestArgs(void);\nint FS_GetManifestArgv(const char **argv, int maxargs);\n\nstruct zonegroup_s;\nvoid *FS_LoadMallocGroupFile(struct zonegroup_s *ctx, char *path, size_t *fsize, qboolean filters);\nvoid *FS_LoadMallocFile (const char *path, size_t *fsize);\nqbyte *FS_LoadMallocFileFlags (const char *path, unsigned int locateflags, size_t *fsize);\nqofs_t FS_LoadFile(const char *name, void **file);\nvoid FS_FreeFile(void *file);\n\nqbyte *COM_LoadFile (const char *path, unsigned int locateflags, int usehunk, size_t *filesize);\n\nqboolean FS_LoadMapPackFile (const char *filename, searchpathfuncs_t *archive);\nvoid FS_CloseMapPackFile (searchpathfuncs_t *archive);\nvoid COM_FlushTempoaryPacks(void);\n\nvoid COM_EnumerateFiles (const char *match, int (QDECL *func)(const char *fname, qofs_t fsize, time_t mtime, void *parm, searchpathfuncs_t *spath), void *parm);\nvoid COM_EnumerateFilesReverse (const char *match, int (QDECL *func)(const char *fname, qofs_t fsize, time_t mtime, void *parm, searchpathfuncs_t *spath), void *parm);\nsearchpathfuncs_t *FS_OpenPackByExtension(vfsfile_t *f, searchpathfuncs_t *parent, const char *filename, const char *pakname, const char *pakpathprefix);\n\nextern qboolean com_installer;\t//says that the engine is running in an 'installer' mode, and that the correct basedir is not yet known.\nextern\tstruct cvar_s\tregistered;\nextern qboolean standard_quake;\t//fixme: remove\n\n#ifdef NQPROT\nvoid COM_Effectinfo_Enumerate(int (*cb)(const char *pname));\n#endif\n\nstruct model_s;\nunsigned int COM_RemapMapChecksum(struct model_s *model, unsigned int checksum);\n\n#define\tMAX_INFO_KEY\t256\nchar *Info_ValueForKey (const char *s, const char *key);\nvoid Info_SetValueForKey (char *s, const char *key, const char *value, int maxsize);\nvoid Info_SetValueForStarKey (char *s, const char *key, const char *value, int maxsize);\nvoid Info_RemovePrefixedKeys (char *start, char prefix);\nvoid Info_RemoveKey (char *s, const char *key);\nchar *Info_KeyForNumber (const char *s, int num);\nvoid Info_Print (const char *s, const char *lineprefix);\nvoid Info_Enumerate (const char *s, void *ctx, void(*cb)(void *ctx, const char *key, const char *value));\n/*\nvoid Info_RemoveNonStarKeys (char *start);\nvoid Info_WriteToFile(vfsfile_t *f, char *info, char *commandname, int cvarflags);\n*/\n\n/*\n  Info Buffers\n  Keynames are still length limited, and may not contain nulls, but neither restriction applies to values.\n  Using base64 encoding, we're able to encode problematic chars like quotes and newlines (and nulls).\n  This allows mods to store image files inside userinfo.\n*/\ntypedef struct\n{\n\tstruct infokey_s\n\t{\n\t\tqbyte\t\t\tpartial:1;\t\t//partial values read as \"\".\n\t\tqbyte\t\t\tlarge:1;\t\t//requires partial/encoded transmission\n\t\tchar\t\t\t*name;\n\t\tsize_t\t\t\tsize;\n\t\tsize_t\t\t\tbuffersize;\t\t//to avoid excessive reallocs\n\t\tchar\t\t\t*value;\n\t} *keys;\n\tsize_t numkeys;\n\tsize_t totalsize;\t//so we can limit userinfo abuse.\n\n\tvoid (*ChangeCB)(void *context, const char *key);\t//usually calls InfoSync_Add on all the interested parties.\n\tvoid *ChangeCTX;\n} infobuf_t;\ntypedef struct\n{\n\tstruct\n\t{\n\t\tvoid\t\t\t*context;\n\t\tchar\t\t\t*name;\n\t\tsize_t\t\t\tsyncpos;\t\t//reset to 0 when dirty.\n\t} *keys;\n\tsize_t numkeys;\n} infosync_t;\nvoid InfoSync_Remove(infosync_t *sync, size_t k);\nvoid InfoSync_Add(infosync_t *sync, void *context, const char *name);\nvoid InfoSync_Clear(infosync_t *sync);\t//wipes all memory etc.\nvoid InfoSync_Strip(infosync_t *sync, void *context);\t//Clears away all infos from that context.\nextern const char *basicuserinfos[];\t//note: has a leading *\nextern const char *privateuserinfos[];\t//key names that are not broadcast from the server\nqboolean InfoBuf_FindKey (infobuf_t *info, const char *key, size_t *idx);\nconst char *InfoBuf_KeyForNumber (infobuf_t *info, int num);\nconst char *InfoBuf_BlobForKey (infobuf_t *info, const char *key, size_t *blobsize, qboolean *large);\nchar *InfoBuf_ReadKey (infobuf_t *info, const char *key, char *outbuf, size_t outsize);\nchar *InfoBuf_ValueForKey (infobuf_t *info, const char *key);\nqboolean InfoBuf_RemoveKey (infobuf_t *info, const char *key);\nqboolean InfoBuf_SetKey (infobuf_t *info, const char *key, const char *val);\t//refuses to set *keys.\nqboolean InfoBuf_SetStarKey (infobuf_t *info, const char *key, const char *val);\nqboolean InfoBuf_SetStarBlobKey (infobuf_t *info, const char *key, const char *val, size_t valsize);\n#define InfoBuf_SetValueForKey InfoBuf_SetKey\n#define InfoBuf_SetValueForStarKey InfoBuf_SetStarKey\nvoid InfoBuf_Clear(infobuf_t *info, qboolean all);\nvoid InfoBuf_Clone(infobuf_t *dest, infobuf_t *src);\nvoid InfoBuf_FromString(infobuf_t *info, const char *infostring, qboolean append);\nchar *InfoBuf_DecodeString(const char *instart, const char *inend, size_t *sz);\nqboolean InfoBuf_EncodeString(const char *n, size_t s, char *out, size_t outsize);\nsize_t InfoBuf_ToString(infobuf_t *info, char *infostring, size_t maxsize, const char **priority, const char **ignore, const char **exclusive, infosync_t *sync, void *synccontext);\t//_ and * can be used to indicate ALL such keys.\nqboolean InfoBuf_SyncReceive (infobuf_t *info, const char *key, size_t keysize, const char *val, size_t valsize, size_t offset, qboolean final);\nvoid InfoBuf_Print(infobuf_t *info, const char *prefix);\nvoid InfoBuf_WriteToFile(vfsfile_t *f, infobuf_t *info, const char *commandname, int cvarflags);\nvoid InfoBuf_Enumerate (infobuf_t *info, void *ctx, void(*cb)(void *ctx, const char *key, const char *value));\n\n\nqbyte\tCOM_BlockSequenceCheckByte (qbyte *base, int length, int sequence, unsigned mapchecksum);\nqbyte\tCOM_BlockSequenceCRCByte (qbyte *base, int length, int sequence);\nqbyte\tQ2COM_BlockSequenceCRCByte (qbyte *base, int length, int sequence);\n\nsize_t Base64_EncodeBlock(const qbyte *in, size_t length, char *out, size_t outsize);\t//tries to null terminate, but returns length without termination.\nsize_t Base64_EncodeBlockURI(const qbyte *in, size_t length, char *out, size_t outsize); //slightly different chars for uri safety. also trims.\nsize_t Base64_DecodeBlock(const char *in, const char *in_end, qbyte *out, size_t outsize); // +/ and =\nsize_t Base16_EncodeBlock(const char *in, size_t length, qbyte *out, size_t outsize);\nsize_t Base16_DecodeBlock(const char *in, qbyte *out, size_t outsize);\n\n#define DIGEST_MAXSIZE\t(512/8)\t//largest valid digest size, in bytes\ntypedef struct\n{\n\tunsigned int digestsize;\n\tunsigned int contextsize;\t//you need to alloca(te) this much memory...\n\tvoid (*init) (void *context);\n\tvoid (*process) (void *context, const void *data, size_t datasize);\n\tvoid (*terminate) (unsigned char *digest, void *context);\n} hashfunc_t;\nextern hashfunc_t hash_md4;\t\t\t//required for vanilla qw mapchecks\nextern hashfunc_t hash_md5;\t\t\t//required for turn/etc\nextern hashfunc_t hash_sha1;\t\t//required for websockets, and ezquake's crypted rcon\nextern hashfunc_t hash_sha2_224;\nextern hashfunc_t hash_sha2_256;\t//required for webrtc\nextern hashfunc_t hash_sha2_384;\nextern hashfunc_t hash_sha2_512;\nextern hashfunc_t hash_crc16;\t\t//aka ccitt, required for qw's clc_move and various bits of dp compat\nextern hashfunc_t hash_crc16_lower;\n#define hash_certfp hash_sha2_256\t//This is the hash function we're using to compute *fp serverinfo. we can detect 1/2-256/2-512 by sizes, but we need consistency to avoid confusion in clientside things too.\nunsigned int hashfunc_terminate_uint(const hashfunc_t *hash, void *context); //terminate, except returning the digest as a uint instead of a blob. folds the digest if longer than 4 bytes.\nunsigned int CalcHashInt(const hashfunc_t *hash, const void *data, size_t datasize);\nsize_t CalcHash(const hashfunc_t *hash, unsigned char *digest, size_t maxdigestsize, const unsigned char *data, size_t datasize);\nsize_t CalcHMAC(const hashfunc_t *hashfunc, unsigned char *digest, size_t maxdigestsize, const unsigned char *data, size_t datalen, const unsigned char *key, size_t keylen);\n\nint parse_revision_number(const char *revstr, qboolean strict);\t//returns our 'svn' revision numbers\nint revision_number(qboolean strict);\t//returns our 'svn' revision numbers\nint version_number(void);\nchar *version_string(void);\n\n\nvoid TL_InitLanguages(const char *langpath);\t//langpath is where the .po files can be found\nvoid TL_Shutdown(void);\nvoid T_FreeStrings(void);\nchar *T_GetString(int num);\nvoid T_FreeInfoStrings(void);\nchar *T_GetInfoString(int num);\n\nstruct po_s;\nstruct po_s *PO_Create(void);\nvoid PO_Merge(struct po_s *po, vfsfile_t *file);\nconst char *PO_GetText(struct po_s *po, const char *msg);\nvoid PO_Close(struct po_s *po);\nconst char *TL_Translate(int language, const char *src);\t//$foo translations.\nvoid TL_Reformat(int language, char *out, size_t outsize, size_t numargs, const char **arg);\t//\"{0} died\\n\" formatting (with $foo translation, on each arg)\n\n//\n// log.c\n//\ntypedef enum {\n\tLOG_CONSOLE,\n\tLOG_PLAYER,\n\tLOG_RCON,\n\tLOG_TYPES\n} logtype_t;\nvoid Log_String (logtype_t lognum, const char *s);\nvoid Con_Log (const char *s);\nvoid Log_Init(void);\nvoid Log_ShutDown(void);\n#ifdef IPLOG\nvoid IPLog_Add(const char *ip, const char *name);\t//for associating player ip addresses with names.\nqboolean IPLog_Merge_File(const char *fname);\n#endif\nenum certlog_problem_e\n{\n\tCERTLOG_WRONGHOST\t=1<<0,\n\tCERTLOG_EXPIRED\t\t=1<<1,\n\tCERTLOG_MISSINGCA\t=1<<2,\n\n\tCERTLOG_UNKNOWN\t\t=1<<3,\n};\nqboolean CertLog_ConnectOkay(const char *hostname, void *cert, size_t certsize, unsigned int certlogproblems);\n\n#if defined(HAVE_SERVER) && defined(HAVE_CLIENT)\nqboolean Log_CheckMapCompletion(const char *packagename, const char *mapname, float *besttime, float *fulltime, float *bestkills, float *bestsecrets);\nvoid Log_MapNowCompleted(void);\n#endif\n\n\n/*used by and for botlib and q3 gamecode*/\n#define MAX_TOKENLENGTH\t\t1024\ntypedef struct pc_token_s\n{\n\tint type;\n\tint subtype;\n\tint intvalue;\n\tfloat floatvalue;\n\tchar string[MAX_TOKENLENGTH];\n} pc_token_t;\n#define fileHandle_t int\n#define fsMode_t int\n\n\ntypedef struct\n{\n\tint sec;\n\tint min;\n\tint hour;\n\tint day;\n\tint mon;\n\tint year;\n\tchar str[128];\n} date_t;\nvoid COM_TimeOfDay(date_t *date);\n\n//json.c\ntypedef struct json_s\n{\n\tenum\n\t{\n\t\tjson_type_string,\n\t\tjson_type_number,\n\t\tjson_type_object,\n\t\tjson_type_array,\n\t\tjson_type_true,\n\t\tjson_type_false,\n\t\tjson_type_null\n\t} type;\n\tconst char *bodystart;\n\tconst char *bodyend;\n\n\tstruct json_s *parent;\n\tstruct json_s *child;\n\tstruct json_s *sibling;\n\tunion\n\t{\n\t\tstruct json_s **childlink;\n\t\tstruct json_s **array;\n\t};\n\tsize_t arraymax;\t//note that child+siblings are kinda updated with arrays too, just not orphaned cleanly...\n\tqboolean used;\t//set to say when something actually read/walked it, so we can flag unsupported things gracefully\n\tchar name[1];\n} json_t;\n//main functions\njson_t *JSON_Parse(const char *json);\t//simple parsing. returns NULL if there's any kind of parsing error.\nvoid JSON_Destroy(json_t *t);\t\t\t//call this on the root once you're done\njson_t *JSON_FindChild(json_t *t, const char *child);\t//find a named child in an object (or an array, if you're lazy)\njson_t *JSON_GetIndexed(json_t *t, unsigned int idx);\t//find an indexed child in an array (or object, though slower)\ndouble JSON_ReadFloat(json_t *t, double fallback);\t\t//read a numeric value.\nsize_t JSON_ReadBody(json_t *t, char *out, size_t outsize);\t//read a string value.\nsize_t JSON_GetCount(json_t *t);\n//exotic fancy functions\nstruct jsonparsectx_s\n{\n\tchar const *const data;\n\tconst size_t size;\n\tsize_t pos;\n};\njson_t *JSON_ParseNode(json_t *t, const char *namestart, const char *nameend, struct jsonparsectx_s *ctx); //fancy parsing.\n//helpers\njson_t *JSON_FindIndexedChild(json_t *t, const char *child, unsigned int idx);\t\t//just a helper.\nqboolean JSON_Equals(json_t *t, const char *child, const char *expected);\t\t\t//compares a bit faster.\nquintptr_t JSON_GetUInteger(json_t *t, const char *child, unsigned int fallback);\t//grabs a child node's uint value\nqintptr_t JSON_GetInteger(json_t *t, const char *child, int fallback);\t\t\t\t//grabs a child node's int value\nqintptr_t JSON_GetIndexedInteger(json_t *t, unsigned int idx, int fallback);\t\t//grabs an int from an array\ndouble JSON_GetFloat(json_t *t, const char *child, double fallback);\t\t\t\t//grabs a child node's numeric value\ndouble JSON_GetIndexedFloat(json_t *t, unsigned int idx, double fallback);\t\t\t//grabs a numeric value from an array\nconst char *JSON_GetString(json_t *t, const char *child, char *buffer, size_t buffersize, const char *fallback); //grabs a child node's string value. do your own damn indexing for an array.\n//there's no write logic. Its probably easier to just snprintf it or something anyway.\n"
  },
  {
    "path": "engine/common/config_freecs.h",
    "content": "// The Wastes' config.h\r\n// We support both GL and D3D9. If Vulkan matures yeahsurewhynot\r\n// I want to get this mostly running on all systems. \r\n// Possibly Xbox. Yes, the original one. Sue me.\r\n\r\n//general rebranding\r\n#define DISTRIBUTION \"FCS\"\r\n#define DISTRIBUTIONLONG \"eukara\"\r\n#define FULLENGINENAME \"FreeCS\"\r\n#define ENGINEWEBSITE \"https://icculus.org/~marco/freecs/\"\r\n#define BRANDING_ICON \"freecs.ico\"\r\n\r\n//filesystem rebranding\r\n#define GAME_SHORTNAME\t\t\"freecs\"\t//short alphanumeric description\r\n#define GAME_FULLNAME\t\tFULLENGINENAME \t//full name of the game we're playing\r\n#define GAME_BASEGAMES\t\t\"logos\",\"valve\",\"cstrike\",\"freecs\"\t//comma-separate list of basegame strings to use\r\n#define GAME_PROTOCOL\t\t\"FTE-FCS\"\t//so other games won't show up in the server browser\r\n#define GAME_DEFAULTPORT\t23000\t\t//FIXME: change me!\r\n//#define GAME_IDENTIFYINGFILES\tNULL\t//with multiple games, this string-list gives verification that the basedir is actually valid. if null, will just be assumed correct.\r\n//#define GAME_DOWNLOADSURL\tNULL\t\t//url for the package manger to update from\r\n//#define GAME_DEFAULTCMDS\tNULL\t\t//a string containing the things you want to \r\n\r\n// All my fault -eukara\r\n#define ENGINE_ROUTING\r\n\r\n// What do we use\r\n//#define D3D9QUAKE\r\n//#define GLQUAKE\r\n#undef D3D11QUAKE\r\n#if defined(WIN32) && !defined(D3D8QUAKE)\r\n#define D3D8QUAKE\r\n#endif\r\n#undef VKQUAKE\r\n#undef HEADLESSQUAKE\r\n#undef WAYLANDQUAKE\r\n\r\n#define AVAIL_FREETYPE\t//for truetype font rendering\r\n#define HAVE_PACKET\r\n#define QUAKETC\r\n#define AVAIL_OPENAL\r\n#define AVAIL_ZLIB\r\n#define AVAIL_OGGVORBIS\r\n#define CL_MASTER\r\n#define CSQC_DAT\r\n#define MENU_DAT\r\n#define PSET_SCRIPT\r\n#define VOICECHAT\r\n#undef RTLIGHTS\r\n#ifndef MULTITHREAD\r\n#define MULTITHREAD\t//misc basic multithreading - dsound, downloads, basic stuff that's unlikely to have race conditions.\r\n#endif\r\n#define LOADERTHREAD\t//worker threads for loading misc stuff. falls back on main thread if not supported.\r\n//#define USEAREAGRID\t\t//world collision optimisation. REQUIRED for performance with xonotic. hopefully it helps a few other mods too.\r\n\r\n#define NOBUILTINMENUS\r\n#define NOLEGACY\t//just spike trying to kill off crappy crap...\r\n#define AVAIL_DINPUT\r\n#ifndef DEBUG\r\n#define NOQCDESCRIPTIONS 2\t//if 2, disables writing fteextensions.qc completely.\r\n#endif\r\n\r\n\r\n// Various package formats\r\n#define PACKAGE_PK3\r\n#define PACKAGE_Q1PAK\r\n#undef PACKAGE_DOOMWAD\r\n#define PACKAGE_TEXWAD\t// We need this for WAD3 support\r\n\r\n// Map formats\r\n#undef Q3BSPS\r\n#define Q1BSPS // Half-Life Support\r\n#undef Q2BSPS\r\n#undef RFBSPS\r\n#undef TERRAIN\r\n#undef DOOMWADS\r\n#undef MAP_PROC\r\n\r\n// Model formats\r\n#define INTERQUAKEMODELS\r\n#define SPRMODELS\r\n#undef SP2MODELS\r\n#undef DSPMODELS\r\n#undef MD1MODELS\r\n#undef MD2MODELS\r\n#undef MD3MODELS\r\n#undef MD5MODELS\r\n#undef ZYMOTICMODELS\r\n#undef DPMMODELS\r\n#undef PSKMODELS\r\n#define HALFLIFEMODELS\r\n\r\n// What do we NOT want to use\r\n#undef AVAIL_WASAPI\t//windows advanced sound api\r\n#undef AVAIL_DSOUND\r\n#undef BOTLIB_STATIC\t//q3 botlib\r\n#undef AVAIL_XZDEC\t//.xz decompression\r\n#undef AVAIL_GZDEC\t//.gz decompression\r\n#undef PACKAGE_DZIP\t//.dzip special-case archive support\r\n#undef AVAIL_PNGLIB\t//.png image format support (read+screenshots)\r\n#undef AVAIL_JPEGLIB\t//.jpeg image format support (read+screenshots)\r\n#undef AVAIL_MP3_ACM\t//.mp3 support (in windows).\r\n#undef IMAGEFMT_KTX\r\n#undef IMAGEFMT_PKM\r\n#define IMAGEFMT_PCX\r\n#undef IMAGEFMT_DDS\t//.dds files embed mipmaps and texture compression. faster to load.\r\n#undef IMAGEFMT_BLP\t//legacy crap\r\n#define IMAGEFMT_BMP\t//legacy crap\r\n////#undef IMAGEFMT_PCX\t//legacy crap\r\n#undef DECOMPRESS_ETC2\r\n#undef DECOMPRESS_RGTC\r\n#undef DECOMPRESS_S3TC\r\n#undef DECOMPRESS_BPTC\t\t\t//bc6+bc7\r\n#undef NETPREPARSE\t//allows for running both nq+qw on the same server (if not, protocol used must match gamecode).\r\n#undef USE_SQLITE\t//sql-database-as-file support\r\n#undef QUAKESTATS\t//defines STAT_HEALTH etc. if omitted, you'll need to provide that functionality yourself.\r\n#undef QUAKEHUD\t\t//support for drawing the vanilla hud.\r\n#undef QWSKINS\t\t//disabling this means no qw .pcx skins nor enemy/team skin/colour forcing\r\n#undef SVRANKING\t//legacy server-side ranking system.\r\n#undef RAGDOLL\t\t//ragdoll support. requires RBE support.\r\n#undef HUFFNETWORK\t//crappy network compression. probably needs reseeding.\r\n#undef SVCHAT\t\t//ancient lame builtin to support NPC-style chat...\r\n#undef VM_Q1\t\t//q1qvm implementation, to support ktx.\r\n#undef Q2SERVER\t\t//q2 server+gamecode.\r\n#undef Q2CLIENT\t\t//q2 client. file formats enabled separately.\r\n#undef Q3CLIENT\t\t//q3 client stuff.\r\n#undef Q3SERVER\t\t//q3 server stuff.\r\n#undef HEXEN2\t\t//runs hexen2 gamecode, supports hexen2 file formats.\r\n#undef NQPROT\t\t//act as an nq client/server, with nq gamecode.\r\n#undef WEBCLIENT\t//uri_get+any internal downloads etc\r\n#undef RUNTIMELIGHTING\t//automatic generation of .lit files\r\n#undef TEXTEDITOR\t//my funky text editor! its awesome!\r\n#undef TCPCONNECT\t//support for playing over tcp sockets, instead of just udp. compatible with qizmo.\r\n#undef IRCCONNECT\t//lame support for routing game packets via irc server. not a good idea.\r\n#define PLUGINS\t\t//support for external plugins (like huds or fancy menus or whatever)\r\n#undef SUPPORT_ICE\t//Internet Connectivity Establishment, for use by plugins to establish voice or game connections.\r\n#undef PSET_CLASSIC\t//support the 'classic' particle system, for that classic quake feel.\r\n#undef HAVE_CDPLAYER\t//includes cd playback. actual cds. named/numbered tracks are supported regardless (though you need to use the 'music' command to play them without this).\r\n////#undef QTERM\r\n#undef SIDEVIEWS\r\n#undef MAX_SPLITS\r\n#undef SUBSERVERS\r\n////#undef SV_MASTER\r\n#undef HAVE_MIXER\t//openal only\r\n#undef VM_LUA\r\n#undef HLCLIENT\r\n#undef HLSERVER\r\n#undef FTPSERVER\r\n//#undef CLIENTONLY\t//leave this up to the makefiles.\r\n#define HAVE_TCP\r\n#undef HAVE_GNUTLS\t//linux tls/dtls support\r\n#undef HAVE_WINSSPI\t//windows tls/dtls support\r\n#undef HAVE_JUKEBOX\t//includes built-in jukebox crap\r\n#define HAVE_MEDIA_DECODER\t//can play cin/roq, more with plugins\r\n#define HAVE_MEDIA_ENCODER\t//capture/capturedemo work.\r\n#undef HAVE_SPEECHTOTEXT\t//windows speech-to-text thing\r\n\r\n//FIXME: Stuff that Spike has added that Eukara needs to decide whether to keep or not.\r\n#define\tVERTEXINDEXBYTES\t2\t//16bit indexes work everywhere but may break some file types, 32bit indexes are optional in gles<=2 and d3d<=9 and take more memory/copying but allow for bigger batches/models. Plugins need to be compiled the same way so this is no longer set per-renderer.\r\n#define HAVE_OPUS\r\n//#define HAVE_SPEEX\r\n//#define IMAGEFMT_HDR\r\n//#define IMAGEFMT_PBM\r\n//#define IMAGEFMT_PSD\r\n//#define IMAGEFMT_XCF\t\t\t//flattens, most of the time\r\n//#define IPLOG\r\n//#define MVD_RECORDING\r\n//#define PACKAGEMANAGER\r\n//#define SAVEDGAMES\r\n//#define AVAIL_BOTLIB\r\n//#define AVAIL_BZLIB\r\n//#define USE_INTERNAL_ODE\r\n//#define USE_INTERNAL_BULLET\r\n//#define MENU_NATIVECODE\r\n//#define DECOMPRESS_ASTC\r\n//#define HAVE_HTTPSV\r\n//#define IMAGEFMT_ASTC\r\n//#define IMAGEFMT_JPG\r\n//#define IMAGEFMT_GIF\r\n//#define IMAGEFMT_PNG\r\n#define IMAGEFMT_TGA\r\n#define IMAGEFMT_LMP\r\n//#define IMAGEFMT_EXR\t\t\t//openexr, via Industrial Light & Magic's rgba api, giving half-float data.\r\n//#define MODELFMT_MDX\r\n//#define MODELFMT_OBJ\r\n//#define MODELFMT_GLTF\t\t\t//khronos 'transmission format'. .gltf or .glb extension. PBR. Version 2 only, for now.\r\n//#define AVAIL_STBI\t\t\t//make use of Sean T. Barrett's lightweight public domain stb_image[_write] single-file-library, to avoid libpng/libjpeg dependancies.\r\n\r\n\r\n#ifdef COMPILE_OPTS\r\n//things to configure qclib, which annoyingly doesn't include this file itself\r\n-DOMIT_QCC\t//disable the built-in qcc \r\n-DSIMPLE_QCVM\t//disable qc debugging and 32bit opcodes\r\n#ifndef AVAIL_ZLIB\r\n-DNO_ZLIB\t//disable zlib\r\n#endif\r\n#ifdef AVAIL_PNGLIB\r\n-DLINK_PNG\r\n#endif\r\n#ifdef AVAIL_JPEGLIB\r\n-DLINK_JPEG\r\n#endif\r\n\r\n-DNO_SPEEX\t//disable static speex\r\n#ifndef BOTLIB_STATIC\r\n-DNO_BOTLIB\t//disable static botlib\r\n#endif\r\n-DNO_VORBISFILE\t//disable static vorbisfile\r\n\r\n-Os\t\t//optimise for size instead of speed. less cpu cache needed means that its sometimes faster anyway.\r\n#endif\r\n"
  },
  {
    "path": "engine/common/config_fteqw.h",
    "content": "// Build-Config file for FTE's standard builds, the default settings.\r\n// to use: make FTE_CONFIG=fteqw\r\n\r\n// Features should either be commented or not. If you change undefs to defines or vice versa then expect problems.\r\n// Later code will disable any features if they're not supported on the current platform, so don't worry about win/lin/mac/android/web/etc here - any such issues should be fixed elsewhere.\r\n\r\n//general rebranding\r\n//#define DISTRIBUTION\t\t\t\"FTE\"\t\t\t\t\t\t\t//should be kept short. 8 or less letters is good, with no spaces.\r\n//#define DISTRIBUTIONLONG\t\t\"Forethought Entertainment\"\t\t//think of this as your company name. It isn't shown too often, so can be quite long.\r\n//#define FULLENGINENAME\t\t\"FTE Quake\"\t\t\t\t\t\t//nominally user-visible name.\r\n//#define ENGINEWEBSITE\t\t\t\"http://fte.triptohell.info\"\t//for shameless self-promotion purposes.\r\n//#define BRANDING_ICON\t\t\t\"fte_eukara.ico\"\t\t\t\t//The file to use in windows' resource files - for linux your game should include an icon.[png|ico] file in the game's data.\r\n\r\n//filesystem rebranding\r\n//#define GAME_SHORTNAME\t\t\"quake\"\t\t\t//short alphanumeric description\r\n//#define GAME_FULLNAME\t\t\tFULLENGINENAME \t//full name of the game we're playing\r\n//#define GAME_BASEGAMES\t\tGAME_SHORTNAME\t//comma-separate list of basegame strings to use\r\n//#define GAME_PROTOCOL\t\t\t\"FTE-Quake\"\t\t//so other games won't show up in the server browser\r\n//#define GAME_DEFAULTPORT\t\t27500\t\t\t//slightly reduces the chance of people connecting to the wrong type of server\r\n//#define GAME_IDENTIFYINGFILES\tNULL\t\t\t//with multiple games, this string-list gives verification that the basedir is actually valid. if null, will just be assumed correct.\r\n//#define GAME_DOWNLOADSURL\t\tNULL\t\t\t//url for the package manger to update from\r\n//#define GAME_DEFAULTCMDS\t\tNULL\t\t\t//a string containing the things you want to exec in order to override default.cfg \r\n\r\n//#define ENGINE_HAS_ZIP\t//when defined, the engine is effectively a self-extrating zip with the gamedata zipped onto the end (if it in turn contains nested packages then they should probably be STOREd pk3s)\r\n\r\n// Allowed renderers... There should ONLY be undefs here (other C files won't be pulled in automatically)\r\n//#undef GLQUAKE\r\n//#undef D3D8QUAKE\r\n//#undef D3D9QUAKE\r\n//#undef D3D11QUAKE\r\n//#undef VKQUAKE\r\n//#undef HEADLESSQUAKE\t\t\t//no-op renderer...\r\n//#undef WAYLANDQUAKE\t\t\t//linux only\r\n\r\n//Misc Renderer stuff\r\n#define PSET_CLASSIC\t\t\t//support the 'classic' particle system, for that classic quake feel.\r\n#define PSET_SCRIPT\t\t\t\t//scriptable particles (both fte's and importing effectinfo)\r\n#define RTLIGHTS\r\n#define RUNTIMELIGHTING\t\t\t//automatic generation of .lit files\r\n\r\n//Extra misc features.\r\n//#define CLIENTONLY\t\t\t//\r\n#define MULTITHREAD\t\t\t\t//misc basic multithreading - dsound, downloads, basic stuff that's unlikely to have race conditions.\r\n#define LOADERTHREAD\t\t\t//worker threads for loading misc stuff. falls back on main thread if not supported.\r\n#define AVAIL_DINPUT\r\n#define SIDEVIEWS   4   \t\t//enable secondary/reverse views.\r\n#define MAX_SPLITS\t4u\r\n#define\tVERTEXINDEXBYTES\t2\t//16bit indexes work everywhere but may break some file types, 32bit indexes are optional in gles<=2 and d3d<=9 and take more memory/copying but allow for bigger batches/models. Plugins need to be compiled the same way so this is no longer set per-renderer.\r\n#define TEXTEDITOR\t\t\t\t//my funky text editor! its awesome!\r\n#define PLUGINS\t\t\t\t\t//support for external plugins (like huds or fancy menus or whatever)\r\n#define USE_SQLITE\t\t\t\t//sql-database-as-file support\r\n#define IPLOG\t\t\t\t\t//track player's ip addresses (any decent server will hide ip addresses, so this probably isn't that useful, but nq players expect it)\r\n\r\n//Filesystem formats\r\n#define PACKAGE_PK3\t\t\t\t//aka zips. we support utf8,zip64,spans,weakcrypto,(deflate),(bzip2),symlinks. we do not support strongcrypto nor any of the other compression schemes.\r\n#define PACKAGE_Q1PAK\t\t\t//also q2\r\n//#define PACKAGE_DOOMWAD\t\t//doom wad support (generates various file names, and adds support for doom's audio, sprites, etc)\r\n#define AVAIL_XZDEC\t\t\t\t//.xz decompression\r\n#define AVAIL_GZDEC\t\t\t\t//.gz decompression\r\n#define AVAIL_ZLIB\t\t\t\t//whether pk3s can be compressed or not.\r\n//#define AVAIL_BZLIB\t\t\t//whether pk3s can use bz2 compression\r\n#define PACKAGE_DZIP\t\t\t//.dzip support for smaller demos (which are actually more like pak files and can store ANY type of file)\r\n\r\n//Map formats\r\n#define Q1BSPS\t\t\t\t\t//Quake1\r\n#define Q2BSPS\t\t\t\t\t//Quake2\r\n#define Q3BSPS\t\t\t\t\t//Quake3, as well as a load of other games too...\r\n#define RFBSPS\t\t\t\t\t//qfusion's bsp format / jk2o etc.\r\n#define TERRAIN\t\t\t\t\t//FTE's terrain, as well as .map support\r\n//#define DOOMWADS\t\t\t\t//map support, filesystem support is separate.\r\n//#define MAP_PROC\t\t\t\t//doom3...\r\n\r\n//Model formats\r\n#define SPRMODELS\t\t\t\t//Quake's sprites\r\n#define SP2MODELS\t\t\t\t//Quake2's models\r\n#define DSPMODELS\t\t\t\t//Doom sprites!\r\n#define MD1MODELS\t\t\t\t//Quake's models.\r\n#define MD2MODELS\t\t\t\t//Quake2's models\r\n#define MD3MODELS\t\t\t\t//Quake3's models, also often used for q1 etc too.\r\n#define MD5MODELS\t\t\t\t//Doom3 models.\r\n#define ZYMOTICMODELS\t\t\t//nexuiz uses these, for some reason.\r\n#define DPMMODELS\t\t\t\t//these keep popping up, despite being a weak format.\r\n#define PSKMODELS\t\t\t\t//unreal's interchange format. Undesirable in terms of load times.\r\n#define HALFLIFEMODELS\t\t\t//horrible format that doesn't interact well with the rest of FTE's code. Unusable tools (due to license reasons).\r\n#define INTERQUAKEMODELS\t\t//Preferred model format, at least from an idealism perspective.\r\n#define MODELFMT_MDX\t\t\t//kingpin's format (for hitboxes+geomsets).\r\n#define MODELFMT_OBJ\t\t\t//lame mesh-only format that needs far too much processing and even lacks a proper magic identifier too\r\n#define MODELFMT_GLTF\t\t\t//khronos 'transmission format'. .gltf or .glb extension. PBR. Version 2 only, for now.\r\n#define RAGDOLL\t\t\t\t\t//ragdoll support. requires RBE support (via a plugin...).\r\n\r\n//Image formats\r\n#define IMAGEFMT_KTX\t\t\t//Khronos TeXture. common on gles3 devices for etc2 compression\r\n#define IMAGEFMT_PKM\t\t\t//file format generally written by etcpack or android's etc1tool. doesn't support mips.\r\n#define IMAGEFMT_ASTC\t\t\t//lame simple header around a single astc image. not needed for astc in ktx files etc. its better to use ktx files.\r\n#define IMAGEFMT_PBM\t\t\t//pbm/ppm/pgm/pfm family formats.\r\n#define IMAGEFMT_PSD\t\t\t//baselayer only.\r\n#define IMAGEFMT_XCF\t\t\t//flattens, most of the time\r\n#define IMAGEFMT_HDR\t\t\t//an RGBE format.\r\n#define IMAGEFMT_DDS\t\t\t//.dds files embed mipmaps and texture compression. faster to load.\r\n#define IMAGEFMT_TGA\t\t\t//somewhat mandatory\r\n#define IMAGEFMT_LMP\t\t\t//mandatory for quake\r\n#define IMAGEFMT_PNG\t\t\t//common in quakeworld, useful for screenshots.\r\n#define IMAGEFMT_JPG\t\t\t//common in quake3, useful for screenshots.\r\n//#define IMAGEFMT_GIF\t\t\t//for the luls. loads as a texture2DArray\r\n//#define IMAGEFMT_BLP\t\t\t//legacy crap\r\n#define IMAGEFMT_BMP\t\t\t//windows bmp. yuck. also includes .ico for the luls\r\n#define IMAGEFMT_PCX\t\t\t//paletted junk. required for qw player skins, q2 and a few old skyboxes.\r\n#define IMAGEFMT_EXR\t\t\t//openexr, via Industrial Light & Magic's rgba api, giving half-float data.\r\n#define IMAGEFMT_PVR\t\t\t//powervr texture, used by various dreamcast games including HL and Q3\r\n#define AVAIL_PNGLIB\t\t\t//.png image format support (read+screenshots)\r\n#define AVAIL_JPEGLIB\t\t\t//.jpeg image format support (read+screenshots)\r\n//#define AVAIL_STBI\t\t\t//make use of Sean T. Barrett's lightweight public domain stb_image[_write] single-file-library, to avoid libpng/libjpeg dependancies.\r\n#define PACKAGE_TEXWAD\t\t\t//quake's image wad support\r\n#define AVAIL_FREETYPE\t\t\t//for truetype font rendering\r\n#define DECOMPRESS_ETC2\t\t\t//decompress etc2(core in gles3/gl4.3) if the graphics driver doesn't support it (eg d3d or crappy gpus with vulkan).\r\n#define DECOMPRESS_S3TC\t\t\t//allows bc1-3 to work even when drivers don't support it. This is probably only an issue on mobile chips. WARNING: not entirely sure if all patents expired yet...\r\n#define DECOMPRESS_RGTC\t\t\t//bc4+bc5\r\n#define DECOMPRESS_BPTC\t\t\t//bc6+bc7\r\n#define DECOMPRESS_ASTC\t\t\t//ASTC, for drivers that don't support it properly.\r\n\r\n// Game/Gamecode Support\r\n#define CSQC_DAT\r\n#define MENU_DAT\r\n//#define MENU_NATIVECODE\t\t//Use an external dll for menus.\r\n#define VM_Q1\t\t\t\t\t//q1qvm implementation, to support ktx.\r\n//#define VM_LUA\t\t\t\t//optionally supports lua instead of ssqc.\r\n#define Q2SERVER\t\t\t\t//q2 server+gamecode.\r\n#define Q2CLIENT\t\t\t\t//q2 client. file formats enabled separately.\r\n#define Q3CLIENT\t\t\t\t//q3 client stuff.\r\n#define Q3SERVER\t\t\t\t//q3 server stuff.\r\n#define AVAIL_BOTLIB\t\t\t//q3 botlib\r\n//#undef BOTLIB_STATIC\t\t\t//should normally be set only in the makefile, and only if AVAIL_BOTLIB is defined above.\r\n#define HEXEN2\t\t\t\t\t//runs hexen2 gamecode, supports hexen2 file formats.\r\n#define HUFFNETWORK\t\t\t\t//crappy network compression. probably needs reseeding.\r\n#define NETPREPARSE\t\t\t\t//allows for running both nq+qw on the same server (if not, protocol used must match gamecode).\r\n#define SUBSERVERS\t\t\t\t//Allows the server to fork itself, each acting as an MMO-style server instance of a single 'realm'.\r\n//#define HLCLIENT 7\t\t\t//we can run HL gamecode (not protocol compatible, set to 6 or 7)\r\n//#define HLSERVER 140\t\t\t//we can run HL gamecode (not protocol compatible, set to 138 or 140)\r\n#define SAVEDGAMES\t\t\t\t//Can save the game.\r\n#define MVD_RECORDING\t\t\t//server can record MVDs.\r\n#define ENGINE_ROUTING\t\t//Engine-provided routing logic (possibly threaded)\r\n//#define USE_INTERNAL_BULLET\t//Statically link against bullet physics plugin (instead of using an external plugin)\r\n//#define USE_INTERNAL_ODE\t\t//Statically link against ode physics plugin (instead of using an external plugin)\r\n\r\n// Networking options\r\n#define NQPROT\t\t\t\t\t//act as an nq client/server, with nq gamecode.\r\n#define HAVE_PACKET\t\t\t\t//we can send unreliable messages!\r\n#define HAVE_TCP\t\t\t\t//we can create/accept TCP connections.\r\n#define HAVE_GNUTLS\t\t\t\t//on linux\r\n#define HAVE_WINSSPI\t\t\t//on windows\r\n#define FTPSERVER\t\t\t\t//sv_ftp cvar.\r\n#define WEBCLIENT\t\t\t\t//uri_get+any internal downloads etc\r\n#define HAVE_HTTPSV\t\t\t\t//net_enable_http/websocket\r\n#define TCPCONNECT\t\t\t\t//support for playing over tcp sockets, instead of just udp. compatible with qizmo.\r\n//#define IRCCONNECT\t\t\t//lame support for routing game packets via irc server. not a good idea.\r\n#define SUPPORT_ICE\t\t\t\t//Internet Connectivity Establishment, for use by plugins to establish voice or game connections.\r\n#define CL_MASTER\t\t\t\t//Clientside Server Browser functionality.\r\n#define PACKAGEMANAGER\t\t\t//Allows the user to enable/disable/download(with WEBCLIENT) packages and plugins. Also handles map packages.\r\n\r\n// Audio Drivers\r\n#define AVAIL_OPENAL\r\n#define AVAIL_WASAPI\t\t\t//windows advanced sound api\r\n#define AVAIL_DSOUND\r\n#define HAVE_MIXER\t\t\t\t//support non-openal audio drivers\r\n\r\n// Audio Formats\r\n#define AVAIL_OGGVORBIS\t\t\t//.ogg support\r\n#define AVAIL_MP3_ACM\t\t\t//.mp3 support (windows only).\r\n\r\n// Other Audio Options\r\n#define VOICECHAT\r\n#define HAVE_SPEEX\t\t\t\t//Support the speex codec.\r\n#define HAVE_OPUS               //Support the opus codec.\r\n#define HAVE_MEDIA_DECODER\t\t//can play cin/roq, more with plugins\r\n#define HAVE_MEDIA_ENCODER\t\t//capture/capturedemo work.\r\n#define HAVE_CDPLAYER\t\t\t//includes cd playback. actual cds. named/numbered tracks are supported regardless (though you need to use the 'music' command to play them without this).\r\n#define HAVE_JUKEBOX\t\t\t//includes built-in jukebox crap\r\n#define HAVE_SPEECHTOTEXT\t\t//windows speech-to-text thing\r\n\r\n// Features required by vanilla quake/quakeworld...\r\n//#define QUAKETC\r\n#define QUAKESTATS\t\t\t\t//defines STAT_HEALTH etc. if omitted, you'll need to provide that functionality yourself.\r\n#define QUAKEHUD\t\t\t\t//support for drawing the vanilla hud. disable this if you're always going to be using csqc (or equivelent)\r\n#define QWSKINS\t\t\t\t\t//disabling this means no qw .pcx skins nor enemy/team skin/colour forcing\r\n//#define NOBUILTINMENUS\r\n//#define NOLEGACY\t\t\t\t//just spike trying to kill off crappy crap...\r\n#define USEAREAGRID\t\t\t\t//world collision optimisation. REQUIRED for performance with xonotic. hopefully it helps a few other mods too.\r\n//#define NOQCDESCRIPTIONS 2\t//if 2, disables writing fteextensions.qc completely. 1 just omits the text. (ignored in debug builds.)\r\n\r\n// Outdated stuff\r\n#define SVRANKING\t\t\t\t//legacy server-side ranking system.\r\n////#define QTERM\t\t\t\t//qterm... adds a console command that allows running programs from within quake - bit like xterm.\r\n//#define SVCHAT\t\t\t\t\t//ancient lame builtin to support NPC-style chat...\r\n////#define SV_MASTER\t\t\t//Support running the server as a master server. Should probably not be used.\r\n////#define QUAKESPYAPI\t\t\t//define this if you want the engine to be usable via gamespy/quakespy, which has been dead for a long time now. forces the client to use a single port for all outgoing connections, which hurts reconnects.\r\n\r\n\r\n#ifdef COMPILE_OPTS //This is a block of hints for the makefile to decide which deps to link.\r\n//things to configure qclib, which annoyingly doesn't include this file itself\r\n//-DOMIT_QCC\t//disable the built-in qcc \r\n//-DSIMPLE_QCVM\t//disable qc debugging and 32bit opcodes\r\n#ifndef AVAIL_ZLIB\r\n//-DNO_ZLIB\t//disable zlib\r\n#endif\r\n#ifndef FTE_TARGET_WEB\r\n\t#ifdef AVAIL_PNGLIB\r\n\t\t-DLINK_PNG\r\n\t#endif\r\n\t#ifdef AVAIL_JPEGLIB\r\n\t\t-DLINK_JPEG\r\n\t#endif\r\n#endif\r\n#if defined(PLUGINS) && (defined(Q3SERVER) || defined(Q3CLIENT))\r\n-DLINK_QUAKE3\t//ask the makefile to bake the quake3 plugin into the engine itself.\r\n#endif\r\n\r\n//-DNO_OPUS\r\n//-DNO_SPEEX\t//disable static speex\r\n#ifndef AVAIL_BOTLIB\r\n-DNO_BOTLIB\t//disable static botlib\r\n#endif\r\n#ifndef FTE_TARGET_WEB\r\n-DLINK_VORBISFILE\t//disable static vorbisfile\r\n#endif\r\n\r\n\r\n//enable some staticaly linked libraries\r\n#ifndef FTE_TARGET_WEB\r\n-DLINK_FREETYPE\t\t//international text requires international fonts.\r\n#endif\r\n\r\n#if defined(USE_INTERNAL_ODE) && !defined(ODE_DYNAMIC)\r\n-DLINK_ODE\r\n#endif\r\n\r\n#if defined(PLUGINS)\r\n//-DLINK_EZHUD\t\t//uncomment this line to statically link the ezhud plugin (eg for the web-rel target).\r\n#endif\r\n//-DLINK_OPENSSL\t//statically link our openssl plugin into the engine instead of being separate. NOTE: openssl<3 is a license no-go, 3 still requires gpl3 (2 prohits patent).\r\n\r\n//-Os\t\t//optimise for size instead of speed. less cpu cache needed means that its sometimes faster anyway.\r\n#endif\r\n"
  },
  {
    "path": "engine/common/config_fteqw_noweb.h",
    "content": "// Build-Config file for FTE's webless nearly-standard builds.\r\n// these builds have crippled networking options to see if they fare better with malware false positives.\r\n// to use: make FTE_CONFIG=fteqw_noweb\r\n\r\n#include \"config_fteqw.h\"\r\n\r\n#undef WEBCLIENT\r\n#undef HAVE_HTTPSV\r\n#undef TCPCONNECT\r\n#undef FTPSERVER\r\n#undef HAVE_TCP\r\n#undef HAVE_GNUTLS\r\n#undef HAVE_WINSSPI\r\n#undef SUPPORT_ICE\r\n#undef SUBSERVERS\r\n\r\n"
  },
  {
    "path": "engine/common/config_minimal.h",
    "content": "// Build-Config file for FTE's minimal builds.\r\n// to use: make FTE_CONFIG=minimal\r\n// These builds are a compromise between true minimal and something that will actually work.\r\n// As such we allow pk3s, md3s, pngs, and built-in menus, but that's about it.\r\n\r\n// Features should either be commented or not. If you change undefs to defines or vice versa then expect problems.\r\n// Later code will disable any features if they're not supported on the current platform, so don't worry about win/lin/mac/android/web/etc here - any such issues should be fixed elsewhere.\r\n\r\n//general rebranding\r\n//#define DISTRIBUTION\t\t\t\"FTE\"\t\t\t\t\t\t\t//should be kept short. 8 or less letters is good, with no spaces.\r\n//#define DISTRIBUTIONLONG\t\t\"Forethought Entertainment\"\t\t//think of this as your company name. It isn't shown too often, so can be quite long.\r\n//#define FULLENGINENAME\t\t\"FTE Quake\"\t\t\t\t\t\t//nominally user-visible name.\r\n//#define ENGINEWEBSITE\t\t\t\"http://fte.triptohell.info\"\t//for shameless self-promotion purposes.\r\n//#define BRANDING_ICON\t\t\t\"fte_eukara.ico\"\t\t\t\t//The file to use in windows' resource files - for linux your game should include an icon.[png|ico] file in the game's data.\r\n\r\n//filesystem rebranding\r\n//#define GAME_SHORTNAME\t\t\"quake\"\t\t\t//short alphanumeric description\r\n//#define GAME_FULLNAME\t\t\tFULLENGINENAME \t//full name of the game we're playing\r\n//#define GAME_BASEGAMES\t\tGAME_SHORTNAME\t//comma-separate list of basegame strings to use\r\n//#define GAME_PROTOCOL\t\t\t\"FTE-Quake\"\t\t//so other games won't show up in the server browser\r\n//#define GAME_DEFAULTPORT\t\t27500\t\t\t//slightly reduces the chance of people connecting to the wrong type of server\r\n//#define GAME_IDENTIFYINGFILES\tNULL\t\t\t//with multiple games, this string-list gives verification that the basedir is actually valid. if null, will just be assumed correct.\r\n//#define GAME_DOWNLOADSURL\t\tNULL\t\t\t//url for the package manger to update from\r\n//#define GAME_DEFAULTCMDS\t\tNULL\t\t\t//a string containing the things you want to exec in order to override default.cfg \r\n\r\n\r\n// Allowed renderers... There should ONLY be undefs here (other C files won't be pulled in automatically)\r\n//#undef GLQUAKE\r\n//#undef D3D8QUAKE\r\n//#undef D3D9QUAKE\r\n//#undef D3D11QUAKE\r\n//#undef VKQUAKE\r\n#undef HEADLESSQUAKE          //no-op renderer...\r\n//#undef WAYLANDQUAKE           //linux only\r\n\r\n//Misc Renderer stuff\r\n#define PSET_CLASSIC\t\t\t//support the 'classic' particle system, for that classic quake feel.\r\n//#define PSET_SCRIPT\t\t\t//scriptable particles (both fte's and importing effectinfo)\r\n//#define RTLIGHTS\r\n//#define RUNTIMELIGHTING\t\t//automatic generation of .lit files\r\n\r\n//Extra misc features.\r\n//#define CLIENTONLY\t\t\t//\r\n#define MULTITHREAD\t\t\t\t//misc basic multithreading - dsound, downloads, basic stuff that's unlikely to have race conditions.\r\n#define LOADERTHREAD\t\t\t//worker threads for loading misc stuff. falls back on main thread if not supported.\r\n#define AVAIL_DINPUT\r\n#define MAX_CLIENTS\t\t\t32\t//32 for vanilla qw. max 255.\r\n//#define SIDEVIEWS   4\t\t\t//enable secondary/reverse views.\r\n//#define MAX_SPLITS 4u\r\n#define\tVERTEXINDEXBYTES\t2\t//16bit indexes work everywhere but may break some file types, 32bit indexes are optional in gles<=2 and d3d<=9 and take more memory/copying but allow for bigger batches/models. Plugins need to be compiled the same way so this is no longer set per-renderer.\r\n//#define TEXTEDITOR\t\t\t//my funky text editor! its awesome!\r\n//#define PLUGINS\t\t\t\t//support for external plugins (like huds or fancy menus or whatever)\r\n//#define USE_SQLITE\t\t\t//sql-database-as-file support\r\n//#define IPLOG\t\t\t\t\t//track player's ip addresses (any decent server will hide ip addresses, so this probably isn't that useful, but nq players expect it)\r\n\r\n//Filesystem formats\r\n#define PACKAGE_PK3\t\t\t\t//aka zips. we support utf8,zip64,spans,weakcrypto,(deflate),(bzip2),symlinks. we do not support strongcrypto nor any of the other compression schemes.\r\n#define PACKAGE_Q1PAK\t\t\t//also q2\r\n//#define PACKAGE_DOOMWAD\t\t//doom wad support (generates various file names, and adds support for doom's audio, sprites, etc)\r\n//#define AVAIL_XZDEC\t\t\t//.xz decompression\r\n//#define AVAIL_GZDEC\t\t\t//.gz decompression\r\n#define AVAIL_ZLIB\t\t\t\t//whether pk3s can be compressed or not.\r\n//#define AVAIL_BZLIB\t\t\t//whether pk3s can use bz2 compression\r\n//#define PACKAGE_DZIP\t\t\t//.dzip support for smaller demos (which are actually more like pak files and can store ANY type of file)\r\n\r\n//Map formats\r\n#define Q1BSPS\t\t\t\t\t//Quake1\r\n//#define Q2BSPS\t\t\t\t//Quake2\r\n//#define Q3BSPS\t\t\t\t//Quake3, as well as a load of other games too...\r\n//#define RFBSPS\t\t\t\t//qfusion's bsp format / jk2o etc.\r\n//#define TERRAIN\t\t\t\t//FTE's terrain, as well as .map support\r\n//#define DOOMWADS\t\t\t\t//map support, filesystem support is separate.\r\n//#define MAP_PROC\t\t\t\t//doom3...\r\n\r\n//Model formats\r\n#define SPRMODELS\t\t\t\t//Quake's sprites\r\n//#define SP2MODELS\t\t\t\t//Quake2's models\r\n//#define DSPMODELS\t\t\t\t//Doom sprites!\r\n#define MD1MODELS\t\t\t\t//Quake's models.\r\n//#define MD2MODELS\t\t\t\t//Quake2's models\r\n#define MD3MODELS\t\t\t\t//Quake3's models, also often used for q1 etc too.\r\n//#define MD5MODELS\t\t\t\t//Doom3 models.\r\n//#define ZYMOTICMODELS\t\t\t//nexuiz uses these, for some reason.\r\n//#define DPMMODELS\t\t\t\t//these keep popping up, despite being a weak format.\r\n//#define PSKMODELS\t\t\t\t//unreal's interchange format. Undesirable in terms of load times.\r\n//#define HALFLIFEMODELS\t\t//horrible format that doesn't interact well with the rest of FTE's code. Unusable tools (due to license reasons).\r\n//#define INTERQUAKEMODELS\t\t//Preferred model format, at least from an idealism perspective.\r\n//#define MODELFMT_MDX\t\t\t//kingpin's format (for hitboxes+geomsets).\r\n//#define MODELFMT_OBJ\t\t\t//lame mesh-only format that needs far too much processing and even lacks a proper magic identifier too\r\n//#define MODELFMT_GLTF\t\t\t//khronos 'transmission format'. .gltf or .glb extension. PBR. Version 2 only, for now.\r\n//#define RAGDOLL\t\t\t\t//ragdoll support. requires RBE support (via a plugin...).\r\n\r\n//Image formats\r\n//#define IMAGEFMT_KTX\t\t\t//Khronos TeXture. common on gles3 devices for etc2 compression\r\n//#define IMAGEFMT_PKM\t\t\t//file format generally written by etcpack or android's etc1tool. doesn't support mips.\r\n//#define IMAGEFMT_ASTC\t\t\t//lame simple header around a single astc image. not needed for astc in ktx files etc. its better to use ktx files.\r\n//#define IMAGEFMT_PBM\t\t\t//pbm/ppm/pgm/pfm family formats.\r\n//#define IMAGEFMT_PSD\t\t\t//baselayer only.\r\n//#define IMAGEFMT_XCF\t\t\t//flattens, most of the time\r\n//#define IMAGEFMT_HDR\t\t\t//an RGBE format.\r\n//#define IMAGEFMT_DDS\t\t\t//.dds files embed mipmaps and texture compression. faster to load.\r\n#define IMAGEFMT_TGA\t\t\t//somewhat mandatory\r\n#define IMAGEFMT_LMP\t\t\t//mandatory for quake\r\n#define IMAGEFMT_PNG\t\t\t//common in quakeworld, useful for screenshots.\r\n//#define IMAGEFMT_JPG\t\t\t//common in quake3, useful for screenshots.\r\n//#define IMAGEFMT_GIF\t\t\t//for the luls. loads as a texture2DArray\r\n//#define IMAGEFMT_BLP\t\t\t//legacy crap\r\n//#define IMAGEFMT_BMP\t\t\t//windows bmp. yuck. also includes .ico for the luls\r\n//#define IMAGEFMT_PCX\t\t\t//paletted junk. required for qw player skins, q2 and a few old skyboxes.\r\n//#define IMAGEFMT_EXR\t\t\t//openexr, via Industrial Light & Magic's rgba api, giving half-float data.\r\n#define AVAIL_PNGLIB\t\t\t//.png image format support (read+screenshots)\r\n//#define AVAIL_JPEGLIB\t\t\t//.jpeg image format support (read+screenshots)\r\n//#define AVAIL_STBI\t\t\t//make use of Sean T. Barrett's lightweight public domain stb_image[_write] single-file-library, to avoid libpng/libjpeg dependancies.\r\n#define PACKAGE_TEXWAD\t\t\t//quake's image wad support\r\n//#define AVAIL_FREETYPE\t\t//for truetype font rendering\r\n//#define DECOMPRESS_ETC2\t\t//decompress etc2(core in gles3/gl4.3) if the graphics driver doesn't support it (eg d3d or crappy gpus with vulkan).\r\n//#define DECOMPRESS_S3TC\t\t//allows bc1-3 to work even when drivers don't support it. This is probably only an issue on mobile chips. WARNING: not entirely sure if all patents expired yet...\r\n//#define DECOMPRESS_RGTC\t\t//bc4+bc5\r\n//#define DECOMPRESS_BPTC\t\t//bc6+bc7\r\n//#define DECOMPRESS_ASTC\t\t//ASTC, for drivers that don't support it properly.\r\n\r\n// Game/Gamecode Support\r\n//#define CSQC_DAT\r\n//#define MENU_DAT\r\n//#define MENU_NATIVECODE\t\t//Use an external dll for menus.\r\n//#define VM_Q1\t\t\t\t\t//q1qvm implementation, to support ktx.\r\n//#define VM_LUA\t\t\t\t//optionally supports lua instead of ssqc.\r\n//#define Q2SERVER\t\t\t\t//q2 server+gamecode.\r\n//#define Q2CLIENT\t\t\t\t//q2 client. file formats enabled separately.\r\n//#define Q3CLIENT\t\t\t\t//q3 client stuff.\r\n//#define Q3SERVER\t\t\t\t//q3 server stuff.\r\n//#define AVAIL_BOTLIB\t\t\t//q3 botlib\r\n//#undef BOTLIB_STATIC\t\t\t//should normally be set only in the makefile, and only if AVAIL_BOTLIB is defined above.\r\n//#define HEXEN2\t\t\t\t//runs hexen2 gamecode, supports hexen2 file formats.\r\n//#define HUFFNETWORK\t\t\t//crappy network compression. probably needs reseeding.\r\n#define NETPREPARSE\t\t\t\t//allows for running both nq+qw on the same server (if not, protocol used must match gamecode).\r\n//#define SUBSERVERS\t\t\t//Allows the server to fork itself, each acting as an MMO-style server instance of a single 'realm'.\r\n//#define HLCLIENT 7\t\t\t//we can run HL gamecode (not protocol compatible, set to 6 or 7)\r\n//#define HLSERVER 140\t\t\t//we can run HL gamecode (not protocol compatible, set to 138 or 140)\r\n//#define SAVEDGAMES\t\t\t//Can save the game.\r\n//#define MVD_RECORDING\t\t\t//server can record MVDs.\r\n//#define ENGINE_ROUTING\t\t//Engine-provided routing logic (possibly threaded)\r\n//#define USE_INTERNAL_BULLET\t//Statically link against bullet physics plugin (instead of using an external plugin)\r\n//#define USE_INTERNAL_ODE\t\t//Statically link against ode physics plugin (instead of using an external plugin)\r\n\r\n// Networking options\r\n//#define NQPROT\t\t\t\t//act as an nq client/server, with nq gamecode.\r\n#define HAVE_PACKET\t\t\t\t//we can send unreliable messages!\r\n//#define HAVE_TCP\t\t\t\t//we can create/accept TCP connections.\r\n//#define HAVE_GNUTLS\t\t\t//on linux\r\n//#define HAVE_WINSSPI\t\t\t//on windows\r\n//#define FTPSERVER\t\t\t\t//sv_ftp cvar.\r\n//#define WEBCLIENT\t\t\t\t//uri_get+any internal downloads etc\r\n#define HAVE_HTTPSV\t\t\t\t//net_enable_http/websocket\r\n//#define TCPCONNECT\t\t\t//support for playing over tcp sockets, instead of just udp. compatible with qizmo.\r\n//#define IRCCONNECT\t\t\t//lame support for routing game packets via irc server. not a good idea.\r\n//#define SUPPORT_ICE\t\t\t//Internet Connectivity Establishment, for use by plugins to establish voice or game connections.\r\n//#define CL_MASTER\t\t\t\t//Clientside Server Browser functionality.\r\n//#define PACKAGEMANAGER\t\t//Allows the user to enable/disable/download(with WEBCLIENT) packages and plugins.\r\n\r\n// Audio Drivers\r\n//#define AVAIL_OPENAL\r\n//#define AVAIL_WASAPI\t\t\t//windows advanced sound api\r\n#define AVAIL_DSOUND\r\n//#define HAVE_MIXER\t\t\t//support non-openal audio drivers\r\n\r\n// Audio Formats\r\n//#define AVAIL_OGGVORBIS\t\t//.ogg support\r\n//#define AVAIL_MP3_ACM\t\t\t//.mp3 support (windows only).\r\n\r\n// Other Audio Options\r\n//#define VOICECHAT\r\n//#define HAVE_SPEEX\t\t\t//Support the speex codec.\r\n//#define HAVE_OPUS\t\t\t\t//Support the opus codec.\r\n//#define HAVE_MEDIA_DECODER\t//can play cin/roq, more with plugins\r\n//#define HAVE_MEDIA_ENCODER\t//capture/capturedemo work.\r\n//#define HAVE_CDPLAYER\t\t\t//includes cd playback. actual cds. named/numbered tracks are supported regardless (though you need to use the 'music' command to play them without this).\r\n//#define HAVE_JUKEBOX\t\t\t//includes built-in jukebox crap\r\n//#define HAVE_SPEECHTOTEXT\t\t//windows speech-to-text thing\r\n\r\n// Features required by vanilla quake/quakeworld...\r\n//#define QUAKETC\r\n#define QUAKESTATS\t\t\t\t//defines STAT_HEALTH etc. if omitted, you'll need to provide that functionality yourself.\r\n#define QUAKEHUD\t\t\t\t//support for drawing the vanilla hud.\r\n#define QWSKINS\t\t\t\t\t//disabling this means no qw .pcx skins nor enemy/team skin/colour forcing\r\n//#define NOBUILTINMENUS\r\n//#define NOLEGACY\t\t\t\t//just spike trying to kill off crappy crap...\r\n//#define USEAREAGRID\t\t\t//world collision optimisation. REQUIRED for performance with xonotic. hopefully it helps a few other mods too.\r\n#define NOQCDESCRIPTIONS 2\t\t//if 2, disables writing fteextensions.qc completely. 1 just omits the text. (ignored in debug builds.)\r\n\r\n// Outdated stuff\r\n//#define SVRANKING\t\t\t\t//legacy server-side ranking system.\r\n////#define QTERM\t\t\t\t//qterm... adds a console command that allows running programs from within quake - bit like xterm.\r\n//#define SVCHAT\t\t\t\t//ancient lame builtin to support NPC-style chat...\r\n////#define SV_MASTER\t\t\t//Support running the server as a master server. Should probably not be used.\r\n////#define QUAKESPYAPI\t\t\t//define this if you want the engine to be usable via gamespy/quakespy, which has been dead for a long time now. forces the client to use a single port for all outgoing connections, which hurts reconnects.\r\n\r\n\r\n#ifdef COMPILE_OPTS\r\n//things to configure qclib, which annoyingly doesn't include this file itself\r\n-DOMIT_QCC\t//disable the built-in qcc \r\n-DSIMPLE_QCVM\t//disable qc debugging and 32bit opcodes\r\n#ifndef AVAIL_ZLIB\r\n-DNO_ZLIB\t//disable zlib\r\n#endif\r\n#ifdef AVAIL_PNGLIB\r\n-DLINK_PNG\r\n#endif\r\n#ifdef AVAIL_JPEGLIB\r\n-DLINK_JPEG\r\n#endif\r\n\r\n-DNO_OPUS\r\n-DNO_SPEEX\t//disable static speex\r\n#ifndef AVAIL_BOTLIB\r\n-DNO_BOTLIB\t//disable static botlib\r\n#endif\r\n-DNO_VORBISFILE\t//disable static vorbisfile\r\n\r\n\r\n\r\n-Os\t\t//optimise for size instead of speed. less cpu cache needed means that its sometimes faster anyway.\r\n#endif\r\n"
  },
  {
    "path": "engine/common/config_nocompat.h",
    "content": "// Build-Config file for Quake-derived total-conversion mods that have chosen to break compatibility.\r\n// to use: make FTE_CONFIG=nocompat\r\n\r\n// Features should either be commented or not. If you change undefs to defines or vice versa then expect problems.\r\n// Later code will disable any features if they're not supported on the current platform, so don't worry about win/lin/mac/android/web/etc here - any such issues should be fixed elsewhere.\r\n\r\n//general rebranding\r\n//#define DISTRIBUTION\t\t\t\"FTE\"\t\t\t\t\t\t\t//should be kept short. 8 or less letters is good, with no spaces.\r\n//#define DISTRIBUTIONLONG\t\t\"Forethought Entertainment\"\t\t//think of this as your company name. It isn't shown too often, so can be quite long.\r\n//#define FULLENGINENAME\t\t\"FTE Engine\"\t\t\t\t\t\t//nominally user-visible name.\r\n//#define ENGINEWEBSITE\t\t\t\"http://fte.triptohell.info\"\t//for shameless self-promotion purposes.\r\n//#define BRANDING_ICON\t\t\t\"fte_eukara.ico\"\t\t\t\t//The file to use in windows' resource files - for linux your game should include an icon.[png|ico] file in the game's data.\r\n\r\n//filesystem rebranding\r\n//#define GAME_SHORTNAME\t\t\"fte\"\t\t\t//short alphanumeric description\r\n//#define GAME_FULLNAME\t\t\tFULLENGINENAME \t//full name of the game we're playing\r\n//#define GAME_BASEGAMES\t\tGAME_SHORTNAME\t//comma-separate list of basegame strings to use\r\n//#define GAME_PROTOCOL\t\t\t\"FTE-Generic\"\t//so other games won't show up in the server browser\r\n//#define GAME_DEFAULTPORT\t\t27500\t\t\t//slightly reduces the chance of people connecting to the wrong type of server\r\n//#define GAME_IDENTIFYINGFILES\tNULL\t\t\t//with multiple games, this string-list gives verification that the basedir is actually valid. if null, will just be assumed correct.\r\n//#define GAME_DOWNLOADSURL\t\tNULL\t\t\t//url for the package manger to update from\r\n//#define GAME_DEFAULTCMDS\t\tNULL\t\t\t//a string containing the things you want to exec in order to override default.cfg \r\n\r\n\r\n// Allowed renderers... There should ONLY be undefs here (other C files won't be pulled in automatically)\r\n//#undef GLQUAKE\r\n//#undef D3D8QUAKE\r\n//#undef D3D9QUAKE\r\n//#undef D3D11QUAKE\r\n//#undef VKQUAKE\r\n//#undef HEADLESSQUAKE\t\t\t//no-op renderer...\r\n//#undef WAYLANDQUAKE\t\t\t//linux only\r\n\r\n//Misc Renderer stuff\r\n//#define PSET_CLASSIC\t\t\t//support the 'classic' particle system, for that classic quake feel.\r\n#define PSET_SCRIPT\t\t\t\t//scriptable particles (both fte's and importing effectinfo)\r\n#define RTLIGHTS\r\n//#define RUNTIMELIGHTING\t\t//automatic generation of .lit files\r\n\r\n//Extra misc features.\r\n//#define CLIENTONLY\t\t\t//\r\n#define MULTITHREAD\t\t\t\t//misc basic multithreading - dsound, downloads, basic stuff that's unlikely to have race conditions.\r\n#define LOADERTHREAD\t\t\t//worker threads for loading misc stuff. falls back on main thread if not supported.\r\n#define AVAIL_DINPUT\r\n//#define SIDEVIEWS   4   \t\t//enable secondary/reverse views.\r\n#define MAX_SPLITS 4u\r\n#define\tVERTEXINDEXBYTES\t2\t//16bit indexes work everywhere but may break some file types, 32bit indexes are optional in gles<=2 and d3d<=9 and take more memory/copying but allow for bigger batches/models. Plugins need to be compiled the same way so this is no longer set per-renderer.\r\n//#define TEXTEDITOR\t\t\t//my funky text editor! its awesome!\r\n#define PLUGINS\t\t\t\t\t//support for external plugins (like huds or fancy menus or whatever)\r\n//#define USE_SQLITE\t\t\t//sql-database-as-file support\r\n//#define IPLOG\t\t\t\t\t//track player's ip addresses (any decent server will hide ip addresses, so this probably isn't that useful, but nq players expect it)\r\n\r\n//Filesystem formats\r\n#define PACKAGE_PK3\t\t\t\t//aka zips. we support utf8,zip64,spans,weakcrypto,(deflate),(bzip2),symlinks. we do not support strongcrypto nor any of the other compression schemes.\r\n//#define PACKAGE_Q1PAK\t\t\t//also q2\r\n//#define PACKAGE_DOOMWAD\t\t//doom wad support (generates various file names, and adds support for doom's audio, sprites, etc)\r\n//#define AVAIL_XZDEC\t\t\t//.xz decompression\r\n#define AVAIL_GZDEC\t\t\t\t//.gz decompression\r\n#define AVAIL_ZLIB\t\t\t\t//whether pk3s can be compressed or not.\r\n//#define AVAIL_BZLIB\t\t\t//whether pk3s can use bz2 compression\r\n//#define PACKAGE_DZIP\t\t\t//.dzip support for smaller demos (which are actually more like pak files and can store ANY type of file)\r\n\r\n//Map formats\r\n#define Q1BSPS\t\t\t\t\t//Quake1\r\n#define Q2BSPS\t\t\t\t\t//Quake2\r\n#define Q3BSPS\t\t\t\t\t//Quake3, as well as a load of other games too...\r\n#define RFBSPS\t\t\t\t\t//qfusion's bsp format / jk2o etc.\r\n//#define TERRAIN\t\t\t\t//FTE's terrain, as well as .map support\r\n//#define DOOMWADS\t\t\t\t//map support, filesystem support is separate.\r\n//#define MAP_PROC\t\t\t\t//doom3...\r\n\r\n//Model formats\r\n#define SPRMODELS\t\t\t\t//Quake's sprites\r\n//#define SP2MODELS\t\t\t\t//Quake2's models\r\n//#define DSPMODELS\t\t\t\t//Doom sprites!\r\n//#define MD1MODELS\t\t\t\t//Quake's models.\r\n//#define MD2MODELS\t\t\t\t//Quake2's models\r\n//#define MD3MODELS\t\t\t\t//Quake3's models, also often used for q1 etc too.\r\n//#define MD5MODELS\t\t\t\t//Doom3 models.\r\n//#define ZYMOTICMODELS\t\t\t//nexuiz uses these, for some reason.\r\n//#define DPMMODELS\t\t\t\t//these keep popping up, despite being a weak format.\r\n//#define PSKMODELS\t\t\t\t//unreal's interchange format. Undesirable in terms of load times.\r\n//#define HALFLIFEMODELS\t\t//horrible format that doesn't interact well with the rest of FTE's code. Unusable tools (due to license reasons).\r\n#define INTERQUAKEMODELS\t\t//Preferred model format, at least from an idealism perspective.\r\n//#define MODELFMT_MDX\t\t\t//kingpin's format (for hitboxes+geomsets).\r\n//#define MODELFMT_OBJ\t\t\t//lame mesh-only format that needs far too much processing and even lacks a proper magic identifier too\r\n//#define MODELFMT_GLTF\t\t\t//khronos 'transmission format'. .gltf or .glb extension. PBR. Version 2 only, for now.\r\n#define RAGDOLL\t\t\t\t\t//ragdoll support. requires RBE support (via a plugin...).\r\n\r\n//Image formats\r\n#define IMAGEFMT_KTX\t\t\t//Khronos TeXture. common on gles3 devices for etc2 compression\r\n//#define IMAGEFMT_PKM\t\t\t//file format generally written by etcpack or android's etc1tool. doesn't support mips.\r\n//#define IMAGEFMT_ASTC\t\t\t//lame simple header around a single astc image. not needed for astc in ktx files etc. its better to use ktx files.\r\n//#define IMAGEFMT_PBM\t\t\t//pbm/ppm/pgm/pfm family formats.\r\n//#define IMAGEFMT_PSD\t\t\t//baselayer only.\r\n//#define IMAGEFMT_XCF\t\t\t//flattens, most of the time\r\n//#define IMAGEFMT_HDR\t\t\t//an RGBE format.\r\n#define IMAGEFMT_DDS\t\t\t//.dds files embed mipmaps and texture compression. faster to load.\r\n#define IMAGEFMT_TGA\t\t\t//somewhat mandatory\r\n#define IMAGEFMT_LMP\t\t\t//mandatory for quake\r\n#define IMAGEFMT_PNG\t\t\t//common in quakeworld, useful for screenshots.\r\n//#define IMAGEFMT_JPG\t\t\t//common in quake3, useful for screenshots.\r\n//#define IMAGEFMT_GIF\t\t\t//for the luls. loads as a texture2DArray\r\n//#define IMAGEFMT_BLP\t\t\t//legacy crap\r\n//#define IMAGEFMT_BMP\t\t\t//windows bmp. yuck. also includes .ico for the luls\r\n//#define IMAGEFMT_PCX\t\t\t//paletted junk. required for qw player skins, q2 and a few old skyboxes.\r\n//#define IMAGEFMT_EXR\t\t\t//openexr, via Industrial Light & Magic's rgba api, giving half-float data.\r\n#define AVAIL_PNGLIB\t\t\t//.png image format support (read+screenshots)\r\n//#define AVAIL_JPEGLIB\t\t\t//.jpeg image format support (read+screenshots)\r\n//#define AVAIL_STBI\t\t\t//make use of Sean T. Barrett's lightweight public domain stb_image[_write] single-file-library, to avoid libpng/libjpeg dependancies.\r\n//#define PACKAGE_TEXWAD\t\t\t//quake's image wad support\r\n#define AVAIL_FREETYPE\t\t\t//for truetype font rendering\r\n#define DECOMPRESS_ETC2\t\t\t//decompress etc2(core in gles3/gl4.3) if the graphics driver doesn't support it (eg d3d or crappy gpus with vulkan).\r\n//#define DECOMPRESS_S3TC\t\t\t//allows bc1-3 to work even when drivers don't support it. This is probably only an issue on mobile chips. WARNING: not entirely sure if all patents expired yet...\r\n//#define DECOMPRESS_RGTC\t\t\t//bc4+bc5\r\n//#define DECOMPRESS_BPTC\t\t\t//bc6+bc7\r\n//#define DECOMPRESS_ASTC\t\t//ASTC, for drivers that don't support it properly.\r\n\r\n// Game/Gamecode Support\r\n#define CSQC_DAT\r\n#define MENU_DAT\r\n//#define MENU_NATIVECODE\t\t//Use an external dll for menus.\r\n//#define VM_Q1\t\t\t\t\t//q1qvm implementation, to support ktx.\r\n//#define VM_LUA\t\t\t\t//optionally supports lua instead of ssqc.\r\n//#define Q2SERVER\t\t\t\t//q2 server+gamecode.\r\n//#define Q2CLIENT\t\t\t\t//q2 client. file formats enabled separately.\r\n//#define Q3CLIENT\t\t\t\t//q3 client stuff.\r\n//#define Q3SERVER\t\t\t\t//q3 server stuff.\r\n//#define AVAIL_BOTLIB\t\t\t//q3 botlib\r\n//#undef BOTLIB_STATIC\t\t\t//should normally be set only in the makefile, and only if AVAIL_BOTLIB is defined above.\r\n//#define HEXEN2\t\t\t\t//runs hexen2 gamecode, supports hexen2 file formats.\r\n//#define HUFFNETWORK\t\t\t//crappy network compression. probably needs reseeding.\r\n//#define NETPREPARSE\t\t\t//allows for running both nq+qw on the same server (if not, protocol used must match gamecode).\r\n//#define SUBSERVERS\t\t\t//Allows the server to fork itself, each acting as an MMO-style server instance of a single 'realm'.\r\n//#define HLCLIENT 7\t\t\t//we can run HL gamecode (not protocol compatible, set to 6 or 7)\r\n//#define HLSERVER 140\t\t\t//we can run HL gamecode (not protocol compatible, set to 138 or 140)\r\n#define SAVEDGAMES\t\t\t\t//Can save the game.\r\n#define MVD_RECORDING\t\t\t//server can record MVDs.\r\n//#define ENGINE_ROUTING\t\t//Engine-provided routing logic (possibly threaded)\r\n//#define USE_INTERNAL_BULLET\t//Statically link against bullet physics plugin (instead of using an external plugin)\r\n//#define USE_INTERNAL_ODE               //Statically link against ode physics plugin (instead of using an external plugin)\r\n\r\n// Networking options\r\n//#define NQPROT\t\t\t\t//act as an nq client/server, with nq gamecode.\r\n#define HAVE_PACKET\t\t\t\t//we can send unreliable messages!\r\n#define HAVE_TCP\t\t\t\t//we can create/accept TCP connections.\r\n#define HAVE_GNUTLS\t\t\t\t//on linux\r\n#define HAVE_WINSSPI\t\t\t//on windows\r\n//#define FTPSERVER\t\t\t\t//sv_ftp cvar.\r\n#define WEBCLIENT\t\t\t\t//uri_get+any internal downloads etc\r\n#define HAVE_HTTPSV\t\t\t\t//net_enable_http/websocket\r\n//#define TCPCONNECT\t\t\t//support for playing over tcp sockets, instead of just udp. compatible with qizmo.\r\n//#define IRCCONNECT\t\t\t//lame support for routing game packets via irc server. not a good idea.\r\n#define SUPPORT_ICE\t\t\t\t//Internet Connectivity Establishment, for use by plugins to establish voice or game connections.\r\n#define CL_MASTER\t\t\t\t//Clientside Server Browser functionality.\r\n//#define PACKAGEMANAGER\t\t\t//Allows the user to enable/disable/download(with WEBCLIENT) packages and plugins.\r\n\r\n// Audio Drivers\r\n#define AVAIL_OPENAL\r\n#define AVAIL_WASAPI\t\t\t//windows advanced sound api\r\n#define AVAIL_DSOUND\r\n#define HAVE_MIXER\t\t\t\t//support non-openal audio drivers\r\n\r\n// Audio Formats\r\n#define AVAIL_OGGVORBIS\t\t\t//.ogg support\r\n//#define AVAIL_MP3_ACM\t\t\t//.mp3 support (windows only).\r\n\r\n// Other Audio Options\r\n#define VOICECHAT\r\n//#define HAVE_SPEEX\t\t\t//Support the speex codec.\r\n#define HAVE_OPUS\t\t\t\t//Support the opus codec.\r\n#define HAVE_MEDIA_DECODER\t\t//can play cin/roq, more with plugins\r\n#define HAVE_MEDIA_ENCODER\t\t//capture/capturedemo work.\r\n//#define HAVE_CDPLAYER\t\t\t//includes cd playback. actual cds. named/numbered tracks are supported regardless (though you need to use the 'music' command to play them without this).\r\n//#define HAVE_JUKEBOX\t\t\t//includes built-in jukebox crap\r\n//#define HAVE_SPEECHTOTEXT\t\t//windows speech-to-text thing\r\n\r\n// Features required by vanilla quake/quakeworld...\r\n#define QUAKETC\r\n//#define QUAKESTATS\t\t\t//defines STAT_HEALTH etc. if omitted, you'll need to provide that functionality yourself.\r\n//#define QUAKEHUD\t\t\t\t//support for drawing the vanilla hud.\r\n#define QWSKINS\t\t\t\t\t//disabling this means no qw .pcx skins nor enemy/team skin/colour forcing\r\n#define NOBUILTINMENUS\r\n#define NOLEGACY\t\t\t\t//just spike trying to kill off crappy crap...\r\n#define USEAREAGRID\t\t\t\t//world collision optimisation. REQUIRED for performance with xonotic. hopefully it helps a few other mods too.\r\n//#define NOQCDESCRIPTIONS 2\t//if 2, disables writing fteextensions.qc completely. 1 just omits the text. (ignored in debug builds.)\r\n\r\n// Outdated stuff\r\n//#define SVRANKING\t\t\t\t//legacy server-side ranking system.\r\n////#define QTERM\t\t\t\t//qterm... adds a console command that allows running programs from within quake - bit like xterm.\r\n//#define SVCHAT\t\t\t\t//ancient lame builtin to support NPC-style chat...\r\n////#define SV_MASTER\t\t\t//Support running the server as a master server. Should probably not be used.\r\n////#define QUAKESPYAPI\t\t\t//define this if you want the engine to be usable via gamespy/quakespy, which has been dead for a long time now. forces the client to use a single port for all outgoing connections, which hurts reconnects.\r\n\r\n\r\n#ifdef COMPILE_OPTS\r\n//things to configure qclib, which annoyingly doesn't include this file itself\r\n//-DOMIT_QCC\t//disable the built-in qcc \r\n//-DSIMPLE_QCVM\t//disable qc debugging and 32bit opcodes\r\n#ifndef AVAIL_ZLIB\r\n//-DNO_ZLIB\t//disable zlib\r\n#endif\r\n#ifdef AVAIL_PNGLIB\r\n-DLINK_PNG\r\n#endif\r\n#ifdef AVAIL_JPEGLIB\r\n-DLINK_JPEG\r\n#endif\r\n\r\n//-DNO_OPUS\r\n//-DNO_SPEEX\t//disable static speex\r\n#ifndef AVAIL_BOTLIB\r\n-DNO_BOTLIB\t//disable static botlib\r\n#endif\r\n//-DNO_VORBISFILE\t//disable static vorbisfile\r\n\r\n\r\n\r\n//-Os\t\t//optimise for size instead of speed. less cpu cache needed means that its sometimes faster anyway.\r\n#endif\r\n"
  },
  {
    "path": "engine/common/config_wastes.h",
    "content": "/***\r\n*\r\n*   Copyright (c) 2000-2022, Vera Visions. All rights reserved.\r\n*\r\n****/\r\n\r\n#ifndef DEMO\r\n\t#define FULLENGINENAME\t\t\"The Wastes\"\r\n\t#define GAME_SHORTNAME\t\t\"TW\"\r\n\t#define GAME_BASEGAMES\t\t\"platform\",\"wastes\"\r\n\t#define GAME_PROTOCOL\t\t\"The-Wastes\"\r\n#else\r\n\t#define FULLENGINENAME\t\t\"The Wastes Demo\"\r\n\t#define GAME_SHORTNAME\t\t\"TWDemo\"\r\n\t#define GAME_BASEGAMES\t\t\"platform\",\"demotw\"\r\n\t#define GAME_PROTOCOL\t\t\"TW-Demo\"\r\n#endif\r\n\r\n#define BRANDING_ICON \"wastes.ico\"\r\n#define DISTRIBUTION \"VTW\"\r\n#define DISTRIBUTIONLONG \"Vera Visions\"\r\n#define GAME_FULLNAME\t\tFULLENGINENAME\r\n#define GAME_DEFAULTPORT\t23000\r\n#define ENGINEWEBSITE \"https://www.vera-visions.com/\"\r\n\r\n#ifndef GLQUAKE\r\n#define GLQUAKE\r\n#endif\r\n\r\n/*\r\n#ifndef VKQUAKE\r\n#define VKQUAKE\r\n#endif\r\n*/\r\n#undef VKQUAKE /* not yet, needs more testing */\r\n\r\n /* disable quake specific hacks and overrides */\r\n#define QUAKETC\r\n#define NOBUILTINMENUS\r\n#define NOLEGACY\r\n\r\n/* engine behaviour */\r\n#define PLUGINS /* enables fteplug_ files */\r\n#define AVAIL_ZLIB /* we need this for pk3 and ogg vorbis */\r\n#define CL_MASTER /* allows for serverbrowser builtins */\r\n#define CSQC_DAT /* clientside qcvm */\r\n#define MENU_DAT /* persistent qcvm */\r\n#define PSET_SCRIPT /* scripts defining particles */\r\n#define LOADERTHREAD /* multithreading related */\r\n#define USEAREAGRID /* leave it on, improves performance */\r\n#define AVAIL_DINPUT /* input for Windows */\r\n#define AVAIL_FREETYPE\t/* for truetype font rendering */\r\n#define AVAIL_STBI /* avoid libpng/libjpeg dependancies */\r\n#define ENGINE_ROUTING /* engine-side, fast routing */\r\n\r\n#ifndef LEGACY_GPU\r\n\t#define RTLIGHTS\r\n#else\r\n\t#undef RTLIGHTS\r\n#endif\r\n\r\n#undef D3D9QUAKE\t/* MICROS~1 trash */\r\n#undef D3D11QUAKE\t/* MICROS~1 trash */\r\n#undef D3D8QUAKE\t/* MICROS~1 trash */\r\n\r\n/* uncompressed textures */\r\n#define IMAGEFMT_BMP /* sprays */\r\n#define IMAGEFMT_TGA\r\n\r\n/* compressed textures */\r\n#define IMAGEFMT_KTX\r\n#define DECOMPRESS_ETC2\r\n#define DECOMPRESS_RGTC\r\n#define DECOMPRESS_S3TC\r\n\r\n/* To be able to comm with Frag-Net.com */\r\n#define HAVE_PACKET\r\n#define SUPPORT_ICE\r\n#define HAVE_TCP\r\n#define HAVE_GNUTLS /* linux tls/dtls support */\r\n#define HAVE_WINSSPI /* windows tls/dtls support */\r\n#define WEBCLIENT /* uri_get+any internal downloads etc */\r\n\r\n#ifndef MULTITHREAD\r\n#define MULTITHREAD\r\n#endif\r\n\r\n#ifndef DEBUG\r\n/* if 2, disables writing fteextensions.qc completely. */\r\n#define NOQCDESCRIPTIONS 2\r\n#endif\r\n\r\n/* various package formats */\r\n#define PACKAGE_PK3\r\n#define PACKAGE_TEXWAD\r\n#define PACKAGE_Q1PAK\r\n\r\n/* level formats */\r\n#define Q3BSPS\r\n#define Q1BSPS\r\n#define TERRAIN\r\n\r\n/* audio */\r\n#define AVAIL_DSOUND\r\n#undef AVAIL_OPENAL\r\n#define AVAIL_OGGVORBIS\r\n#define HAVE_OPUS\r\n#define VOICECHAT\r\n\r\n/* todo: make OpenAL only */\r\n#define HAVE_MIXER\r\n\r\n/* Model formats, IQM/VVM and HLMDL for legacy maps */\r\n#define INTERQUAKEMODELS\r\n#define HALFLIFEMODELS\r\n\r\n/* physics */\r\n#undef USE_INTERNAL_ODE\r\n#undef USE_INTERNAL_BULLET\r\n#undef USERBE \r\n#undef RAGDOLL\r\n\r\n/* we don't need any of these */\r\n#undef IMAGEFMT_PCX\r\n#undef PACKAGE_DOOMWAD\r\n#undef DOOMWADS\r\n#undef MAP_PROC\r\n#undef Q2BSPS\r\n#define RFBSPS\r\n#define\tVERTEXINDEXBYTES\t2\t//16bit indexes work everywhere but may break some file types, 32bit indexes are optional in gles<=2 and d3d<=9 and take more memory/copying but allow for bigger batches/models. Plugins need to be compiled the same way so this is no longer set per-renderer.\r\n#undef SPRMODELS\r\n#undef SP2MODELS\r\n#undef DSPMODELS\r\n#undef MD1MODELS\r\n#undef MD2MODELS\r\n#undef MD3MODELS\r\n#undef MD5MODELS\r\n#undef ZYMOTICMODELS\r\n#undef DPMMODELS\r\n#undef PSKMODELS\r\n#undef MENU_NATIVECODE\t/* native menu replacing menuQC */\r\n#undef MVD_RECORDING\t/* server can record MVDs. */\r\n#undef AVAIL_WASAPI\t/* windows advanced sound api */\r\n//#undef AVAIL_DSOUND\t/* MICROS~1 trash */\r\n#undef BOTLIB_STATIC\t/* q3 botlib */\r\n#undef AVAIL_XZDEC\t/* .xz decompression */\r\n#undef HAVE_SPEEX\t/* .xz decompression */\r\n#undef AVAIL_GZDEC\t/* .gz decompression */\r\n#undef PACKAGE_DZIP\t/* .dzip special-case archive support */\r\n#undef AVAIL_PNGLIB\t/* .png image format support (read+screenshots) */\r\n#undef AVAIL_JPEGLIB\t/* .jpeg image format support (read+screenshots) */\r\n#undef AVAIL_MP3_ACM\t/* .mp3 support (in windows). */\r\n#undef IMAGEFMT_DDS\r\n#undef IMAGEFMT_PKM\r\n#undef IMAGEFMT_BLP\r\n#undef NETPREPARSE\t/* allows for running both nq+qw on the same server (if not, protocol used must match gamecode) */\r\n#undef USE_SQLITE\t/* sql-database-as-file support */\r\n#undef QUAKESTATS\t/* defines STAT_HEALTH etc. if omitted, you'll need to provide that functionality yourself */\r\n#undef QUAKEHUD\t\t/* support for drawing the vanilla hud */\r\n#undef QWSKINS\t\t/* disabling this means no qw .pcx skins nor enemy/team skin/colour forcing */\r\n#undef SVRANKING\t/* legacy server-side ranking system */\r\n#define HUFFNETWORK\t/* crappy network compression. probably needs reseeding */\r\n#undef SVCHAT\t\t/* ancient lame builtin to support NPC-style chat.. */\r\n#undef VM_Q1\t\t/* q1qvm implementation, to support ktx */\r\n#undef Q2SERVER\t\t/* q2 server+gamecode */\r\n#undef Q2CLIENT\t\t/* q2 client. file formats enabled separately */\r\n#undef Q3CLIENT\t\t/* q3 client stuff */\r\n#undef Q3SERVER\t\t/* q3 server stuff */\r\n#undef HEXEN2\t\t/* runs hexen2 gamecode, supports hexen2 file formats */\r\n#undef NQPROT\t\t/* act as an nq client/server, with nq gamecode */\r\n#undef RUNTIMELIGHTING\t/* automatic generation of .lit files */\r\n#undef TEXTEDITOR\t/* because emacs */\r\n#undef TCPCONNECT\t/* support for playing over tcp sockets, instead of just udp. compatible with qizmo */\r\n#undef IRCCONNECT\t/* lame support for routing game packets via irc server. not a good idea */\r\n#undef PSET_CLASSIC\t/* support the 'classic' particle system, for that classic quake feel */\r\n#undef HAVE_CDPLAYER\t/* Redbook CD Audio */\r\n#undef QTERM\r\n#undef SIDEVIEWS\r\n#undef MAX_SPLITS\r\n#undef SUBSERVERS\t\t/* multi-map */\r\n#undef VM_LUA\t\t\t/* lua game-logic */\r\n#undef HLCLIENT\t\t\t/* regressed, unfinished*/\r\n#undef HLSERVER\t\t\t/* regressed, unfinished */\r\n#undef FTPSERVER\r\n#undef HAVE_JUKEBO\t\t/* includes built-in jukebox */\r\n#define HAVE_MEDIA_DECODER\t/* can play cin/roq, more with plugins */\r\n#undef HAVE_MEDIA_ENCODER\t/* capture/capturedemo work */\r\n#undef HAVE_SPEECHTOTEXT\t/* Windows speech-to-text thing */\r\n#undef SAVEDGAMES\r\n#undef PACKAGEMANAGER\t\t/* enable/disable/download packages and plugins */\r\n#undef HEADLESSQUAKE\r\n#undef WAYLANDQUAKE\r\n#undef SERVER_DEMO_PLAYBACK\t/* deprecated */\r\n#undef DECOMPRESS_BPTC\r\n#undef IMAGEFMT_HDR\r\n#undef IMAGEFMT_PBM\r\n#undef IMAGEFMT_PSD\r\n#undef IMAGEFMT_XCF\r\n#undef IMAGEFMT_LMP\r\n#undef IMAGEFMT_PNG\r\n#undef IMAGEFMT_JPG\r\n#undef IMAGEFMT_GIF\r\n#undef IMAGEFMT_EXR\r\n#undef IPLOG\r\n#undef AVAIL_BOTLIB\r\n#undef AVAIL_BZLIB\r\n#undef DECOMPRESS_ASTC\r\n#undef IMAGEFMT_ASTC\r\n#undef HAVE_HTTPSV\r\n#undef MODELFMT_MDX\r\n#undef MODELFMT_OBJ\r\n#undef MODELFMT_GLTF\r\n\r\n#ifdef COMPILE_OPTS\r\n/* things to configure qclib, which annoyingly doesn't include this\r\n * file itself */\r\n-DOMIT_QCC\t/* disable the built-in qcc */\r\n-DSIMPLE_QCVM\t/* disable qc debugging and 32bit opcodes */\r\n#ifndef AVAIL_ZLIB\r\n-DNO_ZLIB\t/* disable zlib */\r\n#endif\r\n#ifdef AVAIL_PNGLIB\r\n-DLINK_PNG\r\n#endif\r\n#ifdef AVAIL_JPEGLIB\r\n-DLINK_JPEG\r\n#endif\r\n#ifdef AVAIL_FREETYPE\r\n-DLINK_FREETYPE\r\n#endif\r\n\r\n/* makefile will respond to this by trying to link bullet into the\r\n * engine itself, instead of as a plugin. */\r\n#ifdef USE_INTERNAL_BULLET\r\n-DLINK_INTERNAL_BULLET\r\n#endif\r\n\r\n#ifdef USE_INTERNAL_ODE\r\n-DODE_STATIC\r\n#endif\r\n\r\n/* disable static speex */\r\n#ifdef HAVE_SPEEX\r\n-DNO_SPEEX\r\n#endif\r\n\r\n/* disable static botlib */\r\n#ifndef BOTLIB_STATIC\r\n-DNO_BOTLIB\r\n#endif\r\n\r\n-DLIBVORBISFILE_STATIC\r\n\r\n/* optimise for size instead of speed. less cpu cache needed means that\r\n * its sometimes faster.*/\r\n-Os\r\n#endif\r\n"
  },
  {
    "path": "engine/common/console.h",
    "content": "/*\nCopyright (C) 1996-1997 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  \n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n*/\n//\n// console\n//\n\n#define MAXCONCOLOURS 16\ntypedef struct {\n\tfloat fr, fg, fb;\n} consolecolours_t;\n\nextern consolecolours_t consolecolours[MAXCONCOLOURS];\n\n#define MAXQ3COLOURS 10\nextern conchar_t q3codemasks[MAXQ3COLOURS];\n\n#define CON_LONGCHAR_MASK\t(CON_LONGCHAR|CON_RICHFORECOLOUR)\n\n#define CON_NONCLEARBG\t\t0x00800000\t//disabled if CON_RICHFORECOLOUR\n#define CON_HALFALPHA\t\t0x00400000\t//disabled if CON_RICHFORECOLOUR\n#define CON_LINKSPECIAL\t\t0x00200000\t//disabled if CON_RICHFORECOLOUR\n#define CON_LONGCHAR\t\t0x00100000\t//flags (other than hidden) are found in the following conchar. disabled if CON_RICHFORECOLOUR\n#define CON_HIDDEN\t\t\t0x00080000\n#define CON_BLINKTEXT\t\t0x00040000\n#define CON_2NDCHARSETTEXT\t0x00020000\n#define CON_RICHFORECOLOUR\t0x00010000\t//if set, the upper 3 nibbles are r4g4b4. background is clear, halfalpha is ignored.\n//#define CON_HIGHCHARSMASK\t0x00000080 // Quake's alternative mask\n\n#define CON_FLAGSMASK\t\t0xFFFF0000\n#define CON_CHARMASK\t\t0x0000FFFF\n\n#define CON_FGMASK\t\t\t0x0F000000\n#define CON_BGMASK\t\t\t0xF0000000\n#define CON_FGSHIFT 24\t\t//second highest nibble\n#define CON_BGSHIFT 28\t\t//high nibble\n#define CON_RICHFOREMASK\t0xFFF00000\n#define CON_RICHBSHIFT 20\t//\n#define CON_RICHGSHIFT 24\n#define CON_RICHRSHIFT 28\t//high nibble\n\n#define CON_WHITEMASK\t\t0x0F000000 // must be constant. things assume this\n\n#define CON_DEFAULTCHAR\t\t(CON_WHITEMASK | 32)\n\n#define CON_LINKSTART\t\t(CON_LINKSPECIAL | CON_HIDDEN | '[')\n#define CON_LINKEND\t\t\t(CON_LINKSPECIAL | CON_HIDDEN | ']')\n\n// RGBI standard colors\n#define COLOR_BLACK\t\t\t0\n#define COLOR_DARKBLUE\t\t1\n#define COLOR_DARKGREEN\t\t2\n#define COLOR_DARKCYAN\t\t3\n#define COLOR_DARKRED\t\t4\n#define COLOR_DARKMAGENTA\t5\n#define COLOR_BROWN\t\t\t6\n#define COLOR_GREY\t\t\t7\n#define COLOR_DARKGREY\t\t8\n#define COLOR_BLUE\t\t\t9\n#define COLOR_GREEN\t\t\t10\n#define COLOR_CYAN\t\t\t11\n#define COLOR_RED\t\t\t12\n#define COLOR_MAGENTA\t\t13\n#define COLOR_YELLOW\t\t14\n#define COLOR_WHITE\t\t\t15\n\n#define S_COLOR_BLACK\t\"^0\"\n#define S_COLOR_RED\t\t\"^1\"\n#define S_COLOR_GREEN\t\"^2\"\n#define S_COLOR_YELLOW\t\"^3\"\n#define S_COLOR_BLUE\t\"^4\"\n#define S_COLOR_CYAN\t\"^5\"\n#define S_COLOR_MAGENTA\t\"^6\"\n#define S_COLOR_WHITE\t\"^7\"\n#define S_COLOR_TRANS\t\"^8\"\n#define S_COLOR_GRAY\t\"^9\"\n\n#define CON_DEFAULT \"^&--\"\n#define CON_WARNING \"^&E0\"\n#define CON_ERROR   \"^&C0\"\n#define CON_NOTICE  \"^&-1\"\n#if defined(_DEBUG) || defined(FTE_TARGET_WEB)/*urgh...*/\n#define CON_DEBUG\tCON_WARNING __FILE__\":\"STRINGIFY(__LINE__)\" \"\n#endif\n\n#define isextendedcode(x) ((x >= '0' && x <= '9') || (x >= 'A' && x <= 'F') || x == '-')\n#define ishexcode(x) ((x >= '0' && x <= '9') || (x >= 'A' && x <= 'F') || (x >= 'a' && x <= 'f'))\n\n#define CONL_CENTERED\t(1u<<0)\n#define CONL_NONOTIFY\t(1u<<1)\n#define CONL_BREAKPOINT\t(1u<<2)\t//red\n#define CONL_EXECUTION\t(1u<<3)\t//yellow\ntypedef struct conline_s {\n\tstruct conline_s *older;\n\tstruct conline_s *newer;\n\tunsigned short length;\n\tunsigned short maxlength;\n\tunsigned char numlines;\t//updated so we scroll properly\n\tunsigned char flags;\t\n\tunsigned short id;\n\tfloat time;\n} conline_t;\n\n//majority of these are mututally exclusive. the bits allow multiple.\nenum\n{\n\tCB_NONE\t\t\t= 0,\n\tCB_SCROLL\t\t= 1,\n\tCB_SCROLL_R\t\t= 2,\n\tCB_CLOSE\t\t= 3,\n\tCB_MOVE\t\t\t= 4,\n\tCB_ACTIONBAR\t= 5,\n\tCB_SELECT\t\t= 6,\n\tCB_SELECTED\t\t= 7,\t//selection ended (deferred until drawing to ensure selections happen properly)\n\tCB_TAPPED\t\t= 8,\t//quick-tap ended (deferred until drawing to ensure selections happen properly)\n\n\t//the flags part\n\tCB_STALE\t\t= (1u<<28),\t//WAS held last frame - to make sure we still do stuff when released on the same frame.\n\tCB_SIZELEFT\t\t= (1u<<29),\n\tCB_SIZERIGHT\t= (1u<<30),\n\tCB_SIZEBOTTOM\t= (1u<<31),\n};\nenum\n{\n\tCONF_HIDDEN\t\t\t= 1u<<0,\t/*do not show in the console list (unless active)*/\n\tCONF_NOTIFY\t\t\t= 1u<<1,\t/*text printed to console also appears as notify lines*/\n\tCONF_NOTIFY_BOTTOM\t= 1u<<2,\t/*align the bottom*/\n\tCONF_NOTIFY_RIGHT\t= 1u<<3,\n\t//CONF_NOTIMES\t\t= 1u<<4,\n\tCONF_KEYFOCUSED\t\t= 1u<<5,\n\tCONF_ISWINDOW\t\t= 1u<<6,\n\tCONF_NOWRAP\t\t\t= 1u<<7,\n\tCONF_KEEPSELECTION\t= 1u<<8,\t//there's text selected, keep it selected.\n\tCONF_BACKSELECTION\t= 1u<<9,\t//a hint that the text was selected from the end\n};\ntypedef struct console_s\n{\n\tint id;\n\tint nextlineid;\t//the current line being written to. so we can rewrite links etc.\n\tchar name[128];\n\tchar title[128];\n\tchar prompt[128];\n\tchar icon[MAX_QPATH];\t//should really dynamically allocate this stuff.\n\tchar backimage[MAX_QPATH];\n\tstruct shader_s *backshader;\n\tfloat wnd_x;\n\tfloat wnd_y;\n\tfloat wnd_w;\n\tfloat wnd_h;\n\tint linecount;\n\tunsigned int flags;\n\tfloat notif_x;\n\tfloat notif_y;\n\tfloat notif_w;\n\tint notif_l;\n\tfloat notif_fade;\t\t// will be transparent for this long when fading\n\tfloat notif_t;\n\tint maxlines;\n\tconline_t *oldest;\n\tconline_t *current;\t\t// line where next message will be printed\n\tint\t\tx;\t\t\t\t// offset in current line for next print\n\tint\t\tcr;\t\t\t\t// last char printed was a carrage return so the next char printed will wipe the line.\n\tconline_t *display;\t\t// bottom of console displays this line\n\tfloat\tdisplayscroll;\t// to try to handle smoother scrolling.\n\tint\t\tdisplayoffset;\t// horizontal offset\n\tint\t\tvislines;\t\t// pixel lines\n\tint\t\tlinesprinted;\t// for notify times\n\tqboolean unseentext;\n\tunsigned parseflags;\n\tconchar_t defaultcharbits;\n\tint\t\tcommandcompletion;\t//allows tab completion of quake console commands\n\n\t//WARNING: note that links do NOT represent any sort of security. text can be inserted from anywhere. Its fine to use such things for context, but don't treat them as sescure.\n\tint\t\t\t\t(*linebuffered) (struct console_s *con, const char *line);\t//if present, called on enter, causes the standard console input to appear. return 2 to not save the line in history.\n\tqboolean\t\t(*redirect) (struct console_s *con, unsigned int unicode, int key);\t//if present, called every character.\n\tqboolean\t\t(*mouseover)(struct console_s *con, char **out_tiptext, struct shader_s **out_shader);\n\tqboolean\t\t(*close) (struct console_s *con, qboolean force);\n\tvoid\t\t\t*userdata;\t\t//user context\n\tconline_t\t\t*userline;\t//editor cursor line\n\tunsigned int\tuseroffset;\t//editor cursor offset\n\n\tconline_t\t\t*highlightline;\t//used for highlights (this line gets flagged)\n\tconline_t\t*completionline;\t//temp text at the bottom of the console\n\tconline_t\t*footerline;\t//temp text at the bottom of the console\n\tconline_t\t*selstartline, *selendline;\n\tunsigned int\tselstartoffset, selendoffset;\n\tfloat mousedown[2];\t//x,y position that the current buttons were clicked.\n\tunsigned int buttonsdown;\n\tfloat mousecursor[2];\t//x,y\n\tfloat mousedowntime;\t//time mouse1 last went down, to detect double-clicks\n\n\tstruct console_s *next;\n} console_t;\n\nextern\tconsole_t\t*con_head;\nextern\tconsole_t\t*con_curwindow;\t\t// refers to a windowed console\nextern\tconsole_t\t*con_current;\t\t// point to either con_main or con_chat\nextern\tconsole_t\t*con_mouseover;\n\nextern\tconsole_t\t*con_chat;\n\n//shared between console and keys.\n//really the console input should be in console.c instead of keys.c I suppose.\n#define\t\tMAXCMDLINE\t8192\n#define\t\tCON_EDIT_LINES_MASK ((1<<8)-1)\nextern\tunsigned char\t*key_lines[CON_EDIT_LINES_MASK+1];\nextern\tint\t\tedit_line;\nextern\tint\t\tkey_linepos;\nextern\tint\t\thistory_line;\n\n//extern int con_totallines;\nextern qboolean con_initialized;\nextern qbyte *con_chars;\n\nvoid Con_DrawCharacter (int cx, int line, int num);\n\nvoid Con_CheckResize (void);\nvoid Con_ForceActiveNow(void);\nvoid Con_Init (void);\nvoid Con_Shutdown (void);\nvoid Con_History_Save(void);\nvoid Con_History_Load(void);\nstruct font_s;\nvoid Con_DrawOneConsole(console_t *con, qboolean focused, struct font_s *font, float fx, float fy, float fsx, float fsy, float lineagelimit);\nvoid Con_DrawConsole (int lines, qboolean noback);\nvoid Con_ExpandConsoleSelection(console_t *con);\nchar *Con_CopyConsole(console_t *con, qboolean nomarkup, qboolean onlyiflink, qboolean forceutf8);\nvoid Con_Print (const char *txt);\nvoid Con_CenterPrint(const char *txt);\nvoid Con_PrintFlags(const char *text, unsigned int setflags, unsigned int clearflags);\n#ifdef HAVE_CLIENT\nvoid Con_HexDump(qbyte *packet, size_t len, size_t badoffset, size_t stride);\n#endif\nvoid VARGS Con_Printf (const char *fmt, ...) LIKEPRINTF(1);\nvoid VARGS Con_TPrintf (translation_t text, ...);\nvoid VARGS Con_DPrintf (const char *fmt, ...) LIKEPRINTF(1);\t//developer>=1, for stuff that's probably actually slightly useful\nvoid VARGS Con_DLPrintf (int level, const char *fmt, ...) LIKEPRINTF(2);\t//developer>=2, for spammy stuff\nvoid VARGS Con_ThrottlePrintf (float *timer, int developerlevel, const char *fmt, ...) LIKEPRINTF(3); //for spammed warnings, so they don't spam prints with every single frame/call. the timer arg should be a static local.\nvoid VARGS Con_SafePrintf (const char *fmt, ...) LIKEPRINTF(1);\nvoid Con_Footerf(console_t *con, qboolean append, const char *fmt, ...) LIKEPRINTF(3); \nvoid Con_Clear_f (void);\nvoid Con_DrawNotify (void);\nvoid Con_ClearNotify (void);\nvoid Con_ToggleConsole_f (void);//note: allows csqc to intercept the toggleconsole\nvoid Con_ToggleConsole_Force(void);\n\nint Con_ExecuteLine(console_t *con, const char *line);\t//takes normal console commands\nint Con_Navigate(console_t *con, const char *line);\t\t//special webbrowser hacks\n\nvfsfile_t *Con_POpen(const char *conname);\nvoid Con_CycleConsole (void);\nint Con_IsActive (console_t *con);\nvoid Con_Destroy (console_t *con);\nvoid Con_ClearCon(console_t *con);\nvoid Con_SetActive (console_t *con);\nqboolean Con_NameForNum(int num, char *buffer, int buffersize);\nconsole_t *Con_FindConsole(const char *name);\nconsole_t *Con_Create(const char *name, unsigned int flags);\nconsole_t *Con_GetMain(void); //retrieves the main console (creating it if needed)\nvoid Con_PrintCon (console_t *con, const char *txt, unsigned int parseflags);\nqboolean Con_InsertConChars (console_t *con, conline_t *line, int offset, conchar_t *c, int len);\nconline_t *Con_ResizeLineBuffer(console_t *con, conline_t *old, unsigned int length);\nvoid Con_FlushBackgrounds(void);\n\nvoid Con_NotifyBox (char *text);\t// during startup for sound / cd warnings\n\n#ifdef CRAZYDEBUGGING\n#define TRACE(x) Sys_Printf x\n#else\n#define TRACE(x)\n#endif\n\n"
  },
  {
    "path": "engine/common/crc.c",
    "content": "/*\nCopyright (C) 1996-1997 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n*/\n/* crc.c */\n\n#include \"quakedef.h\"\n#include <ctype.h>\n\n// this is a 16 bit, non-reflected CRC using the polynomial 0x1021\n// and the initial and final xor values shown below...  in other words, the\n// CCITT standard CRC used by XMODEM\n\n#define QCRC_INIT_VALUE\t0xffff\n#define QCRC_XOR_VALUE\t0x0000\n\nstatic unsigned short crctable[256] =\n{\n\t0x0000,\t0x1021,\t0x2042,\t0x3063,\t0x4084,\t0x50a5,\t0x60c6,\t0x70e7,\n\t0x8108,\t0x9129,\t0xa14a,\t0xb16b,\t0xc18c,\t0xd1ad,\t0xe1ce,\t0xf1ef,\n\t0x1231,\t0x0210,\t0x3273,\t0x2252,\t0x52b5,\t0x4294,\t0x72f7,\t0x62d6,\n\t0x9339,\t0x8318,\t0xb37b,\t0xa35a,\t0xd3bd,\t0xc39c,\t0xf3ff,\t0xe3de,\n\t0x2462,\t0x3443,\t0x0420,\t0x1401,\t0x64e6,\t0x74c7,\t0x44a4,\t0x5485,\n\t0xa56a,\t0xb54b,\t0x8528,\t0x9509,\t0xe5ee,\t0xf5cf,\t0xc5ac,\t0xd58d,\n\t0x3653,\t0x2672,\t0x1611,\t0x0630,\t0x76d7,\t0x66f6,\t0x5695,\t0x46b4,\n\t0xb75b,\t0xa77a,\t0x9719,\t0x8738,\t0xf7df,\t0xe7fe,\t0xd79d,\t0xc7bc,\n\t0x48c4,\t0x58e5,\t0x6886,\t0x78a7,\t0x0840,\t0x1861,\t0x2802,\t0x3823,\n\t0xc9cc,\t0xd9ed,\t0xe98e,\t0xf9af,\t0x8948,\t0x9969,\t0xa90a,\t0xb92b,\n\t0x5af5,\t0x4ad4,\t0x7ab7,\t0x6a96,\t0x1a71,\t0x0a50,\t0x3a33,\t0x2a12,\n\t0xdbfd,\t0xcbdc,\t0xfbbf,\t0xeb9e,\t0x9b79,\t0x8b58,\t0xbb3b,\t0xab1a,\n\t0x6ca6,\t0x7c87,\t0x4ce4,\t0x5cc5,\t0x2c22,\t0x3c03,\t0x0c60,\t0x1c41,\n\t0xedae,\t0xfd8f,\t0xcdec,\t0xddcd,\t0xad2a,\t0xbd0b,\t0x8d68,\t0x9d49,\n\t0x7e97,\t0x6eb6,\t0x5ed5,\t0x4ef4,\t0x3e13,\t0x2e32,\t0x1e51,\t0x0e70,\n\t0xff9f,\t0xefbe,\t0xdfdd,\t0xcffc,\t0xbf1b,\t0xaf3a,\t0x9f59,\t0x8f78,\n\t0x9188,\t0x81a9,\t0xb1ca,\t0xa1eb,\t0xd10c,\t0xc12d,\t0xf14e,\t0xe16f,\n\t0x1080,\t0x00a1,\t0x30c2,\t0x20e3,\t0x5004,\t0x4025,\t0x7046,\t0x6067,\n\t0x83b9,\t0x9398,\t0xa3fb,\t0xb3da,\t0xc33d,\t0xd31c,\t0xe37f,\t0xf35e,\n\t0x02b1,\t0x1290,\t0x22f3,\t0x32d2,\t0x4235,\t0x5214,\t0x6277,\t0x7256,\n\t0xb5ea,\t0xa5cb,\t0x95a8,\t0x8589,\t0xf56e,\t0xe54f,\t0xd52c,\t0xc50d,\n\t0x34e2,\t0x24c3,\t0x14a0,\t0x0481,\t0x7466,\t0x6447,\t0x5424,\t0x4405,\n\t0xa7db,\t0xb7fa,\t0x8799,\t0x97b8,\t0xe75f,\t0xf77e,\t0xc71d,\t0xd73c,\n\t0x26d3,\t0x36f2,\t0x0691,\t0x16b0,\t0x6657,\t0x7676,\t0x4615,\t0x5634,\n\t0xd94c,\t0xc96d,\t0xf90e,\t0xe92f,\t0x99c8,\t0x89e9,\t0xb98a,\t0xa9ab,\n\t0x5844,\t0x4865,\t0x7806,\t0x6827,\t0x18c0,\t0x08e1,\t0x3882,\t0x28a3,\n\t0xcb7d,\t0xdb5c,\t0xeb3f,\t0xfb1e,\t0x8bf9,\t0x9bd8,\t0xabbb,\t0xbb9a,\n\t0x4a75,\t0x5a54,\t0x6a37,\t0x7a16,\t0x0af1,\t0x1ad0,\t0x2ab3,\t0x3a92,\n\t0xfd2e,\t0xed0f,\t0xdd6c,\t0xcd4d,\t0xbdaa,\t0xad8b,\t0x9de8,\t0x8dc9,\n\t0x7c26,\t0x6c07,\t0x5c64,\t0x4c45,\t0x3ca2,\t0x2c83,\t0x1ce0,\t0x0cc1,\n\t0xef1f,\t0xff3e,\t0xcf5d,\t0xdf7c,\t0xaf9b,\t0xbfba,\t0x8fd9,\t0x9ff8,\n\t0x6e17,\t0x7e36,\t0x4e55,\t0x5e74,\t0x2e93,\t0x3eb2,\t0x0ed1,\t0x1ef0\n};\n\nstatic void CRC16_Init (void *context)\n{\n\tunsigned short *ctx = context;\n\t*ctx = QCRC_INIT_VALUE;\n}\nstatic void CRC16_Update (void *context, const void *data, size_t datasize)\n{\n\tunsigned short *ctx = context;\n\tunsigned short crc = *ctx;\n\tconst qbyte *start = data;\n\twhile (datasize --> 0)\n\t\tcrc = ((crc << 8) & 0xffff) ^ crctable[(crc >> 8) ^ *start++];\n\t*ctx = crc;\n}\nstatic void CRC16_Finish (unsigned char *digest, void *context)\n{\n\tunsigned short *ctx = context;\n\tunsigned short crc = *ctx ^ QCRC_XOR_VALUE;\n\tdigest[0] = (crc>>0)&0xff;\t//digest should be the same regardless of endian. spit it out as little endian.\n\tdigest[1] = (crc>>8)&0xff;\n}\nhashfunc_t hash_crc16 =\n{\n\t2,\n\t2,\n\tCRC16_Init,\n\tCRC16_Update,\n\tCRC16_Finish,\n};\n\nstatic void CRC16_Update_Lower (void *context, const void *data, size_t datasize)\n{\n\tunsigned short *ctx = context;\n\tunsigned short crc = *ctx;\n\tconst qbyte *start = data;\n\twhile (datasize --> 0)\n\t\tcrc = ((crc << 8) & 0xffff) ^ crctable[(crc >> 8) ^ tolower(*start++)];\n\t*ctx = crc;\n}\nhashfunc_t hash_crc16_lower =\t//a case insensitive version...\n{\n\t2,\n\t2,\n\tCRC16_Init,\n\tCRC16_Update_Lower,\n\tCRC16_Finish,\n};\n\n"
  },
  {
    "path": "engine/common/cvar.c",
    "content": "/*\nCopyright (C) 1996-1997 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n*/\n// cvar.c -- dynamic variable tracking\n\n#include \"quakedef.h\"\n#include \"shader.h\"\n\ncvar_group_t *cvar_groups;\n\n//static bucket_t *cvar_buckets[1024];\n//static hashtable_t cvar_hash;\n\ntypedef struct {\n\tsize_t maxentries;\n\tsize_t numentries;\n\tstruct\n\t{\n\t\tconst char *string;\n\t\tvoid *data;\n\t} entry[1];\n} abucket_t;\ntypedef struct {\n\tabucket_t **bucket;\n\tunsigned int numbuckets;\n} ahashtable_t;\t//not thread-safe\n\nstatic abucket_t *cvar_buckets[1024];\nstatic ahashtable_t cvar_hash = {cvar_buckets, countof(cvar_buckets)};\n\nunsigned int Hash_KeyInsensitive(const char *name, unsigned int modulus);\nvoid *AHash_GetInsensitive(ahashtable_t *table, const char *name)\n{\n\tabucket_t *b = table->bucket[Hash_KeyInsensitive(name, table->numbuckets)];\n\tsize_t i;\n\tif (b)\n\t\tfor (i = 0; i < b->numentries; i++)\n\t\t{\n\t\t\tif (!Q_strcasecmp(b->entry[i].string, name))\n\t\t\t\treturn b->entry[i].data;\n\t\t}\n\treturn NULL;\n}\n\nvoid AHash_RemoveDataInsensitive(ahashtable_t *table, const char *name, void *data)\n{\n\tabucket_t *b = table->bucket[Hash_KeyInsensitive(name, table->numbuckets)];\n\tsize_t i;\n\tfor (i = 0; i < b->numentries; i++)\n\t{\n\t\tif (b->entry[i].data == data && !Q_strcasecmp(b->entry[i].string, name))\n\t\t{\n\t\t\t//strip it.\n\t\t\tb->numentries--;\n\t\t\t//shift everything down.\n\t\t\tif (b->numentries > i)\n\t\t\t\tmemmove(&b->entry[i], &b->entry[i+1], sizeof(*b->entry)*(b->numentries-i));\n\t\t\tbreak;\n\t\t}\n\t}\n}\nvoid AHash_AddInsensitive(ahashtable_t *table, const char *name, void *data)\n{\n\tunsigned int idx = Hash_KeyInsensitive(name, table->numbuckets);\n\tabucket_t *b = table->bucket[idx];\n\tif (!b)\n\t{\t//nothing there!...\n\t\tb = table->bucket[idx] = BZ_Malloc(sizeof(*b));\n\t\tb->numentries = 0;\n\t\tb->maxentries = countof(b->entry);\n\t}\n\telse if (b->numentries == b->maxentries)\n\t{\t//can't add anything new\n\t\tsize_t n = b->maxentries*2;\n\t\ttable->bucket[idx] = BZ_Malloc(sizeof(*b)-sizeof(b->entry) + sizeof(b->entry)*n);\n\t\tmemcpy(table->bucket[idx]->entry, b->entry, sizeof(b->entry[0])*b->numentries);\n\t\ttable->bucket[idx]->numentries = b->numentries;\n\t\ttable->bucket[idx]->maxentries = n;\n\t\tBZ_Free(b);\n\t\tb = table->bucket[idx];\n\t}\n\tb->entry[b->numentries].data = data;\n\tb->entry[b->numentries].string = name;\n\tb->numentries++;\n}\nvoid AHash_Cleanup(ahashtable_t *table)\n{\n\tsize_t i;\n\tfor (i = 0; i < table->numbuckets; i++)\n\t{\n\t\tif (table->bucket[i] && !table->bucket[i]->numentries)\n\t\t{\n\t\t\tBZ_Free(table->bucket[i]);\n\t\t\ttable->bucket[i] = NULL;\n\t\t}\n\t}\n}\n\nint cvar_watched;\n\n//cvar_t\t*cvar_vars;\nstatic char\t*cvar_null_string = \"\";\nstatic char *cvar_zero_string = \"0\";\nstatic char *cvar_one_string = \"1\";\n\nstatic char *Cvar_DefaultAlloc(char *str)\n{\n\tchar *c;\n\n\tif (str[0] == '\\0')\n\t\treturn cvar_null_string;\n\tif (str[0] == '0' && str[1] == '\\0')\n\t\treturn cvar_zero_string;\n\tif (str[0] == '1' && str[1] == '\\0')\n\t\treturn cvar_one_string;\n\n\tc = (char *)Z_Malloc(strlen(str)+1);\n\tQ_strcpy(c, str);\n\n\treturn c;\n}\n\nvoid Cvar_DefaultFree(char *str)\n{\n\tif (str == cvar_null_string)\n\t\treturn;\n\telse if (str == cvar_zero_string)\n\t\treturn;\n\telse if (str == cvar_one_string)\n\t\treturn;\n\telse\n\t\tZ_Free(str);\n}\n\n/*\n============\nCvar_FindVar\n============\n*/\ncvar_t *Cvar_FindVar (const char *var_name)\n{\n\treturn AHash_GetInsensitive(&cvar_hash, var_name);\n/*\n\tcvar_group_t\t*grp;\n\tcvar_t\t*var;\n\n\tfor (grp=cvar_groups ; grp ; grp=grp->next)\n\t\tfor (var=grp->cvars ; var ; var=var->next)\n\t\t{\n\t\t\tif (!Q_strcasecmp (var_name, var->name))\n\t\t\t\treturn var;\n\t\t\tif (var->name2 && !Q_strcasecmp (var_name, var->name2))\n\t\t\t\treturn var;\n\t\t}\n*/\n\treturn NULL;\n}\n\nstatic cvar_group_t *Cvar_FindGroup (const char *group_name)\n{\n\tcvar_group_t\t*grp;\n\n\tfor (grp=cvar_groups ; grp ; grp=grp->next)\n\t\tif (!Q_strcasecmp (group_name, grp->name))\n\t\t\treturn grp;\n\n\treturn NULL;\n}\nstatic cvar_group_t *Cvar_GetGroup(const char *gname)\n{\n\tcvar_group_t *g;\n\tif (!gname)\n\t\tgname = \"Miscilaneous vars\";\n\tg = Cvar_FindGroup(gname);\n\tif (g)\n\t\treturn g;\n\n\tg = (cvar_group_t*)Z_Malloc(sizeof(cvar_group_t) + strlen(gname)+1);\n\tg->name = (char*)(g+1);\n\tstrcpy((char*)g->name, gname);\n\tg->next = NULL;\n\tg->next = cvar_groups;\n\tcvar_groups = g;\n\treturn g;\n}\n\n// converts a given single cvar flag into a human readable string\nstatic char *Cvar_FlagToName(int flag)\n{\n\tswitch (flag)\n\t{\n\tcase CVAR_ARCHIVE:\n\t\treturn \"archive\";\n\tcase CVAR_USERINFO:\n\t\treturn \"userinfo\";\n\tcase CVAR_SERVERINFO:\n\t\treturn \"serverinfo\";\n\tcase CVAR_NOSET:\n\t\treturn \"noset\";\n\tcase CVAR_MAPLATCH:\n\t\treturn \"latch\";\n\tcase CVAR_POINTER:\n\t\treturn \"pointer\";\n\tcase CVAR_NOTFROMSERVER:\n\t\treturn \"noserver\";\n\tcase CVAR_USERCREATED:\n\t\treturn \"userset\";\n\tcase CVAR_CHEAT:\n\t\treturn \"cheat\";\n\tcase CVAR_SEMICHEAT:\n\t\treturn \"semicheat\";\n\tcase CVAR_RENDERERLATCH:\n\t\treturn \"renderlatch\";\n\tcase CVAR_VIDEOLATCH:\n\t\treturn \"videolatch\";\n\tcase CVAR_SERVEROVERRIDE:\n\t\treturn \"serverlatch\";\n\tcase CVAR_RENDERERCALLBACK:\n\t\treturn \"rendercallback\";\n\tcase CVAR_NOUNSAFEEXPAND:\n\t\treturn \"nounsafeexpand\";\n\tcase CVAR_RULESETLATCH:\n\t\treturn \"rulesetlatch\";\n\tcase CVAR_SHADERSYSTEM:\n\t\treturn \"shadersystem\";\n\tcase CVAR_NOSAVE:\n\t\treturn \"nosave\";\n\tcase CVAR_TELLGAMECODE:\n\t\treturn \"autocvar\";\n\tcase CVAR_CONFIGDEFAULT:\n\t\treturn \"\";\n\tcase CVAR_NORESET:\n\t\treturn \"noreset\";\n\tcase CVAR_RENDEREROVERRIDE:\n\t\treturn \"rendereroverride\";\n\t}\n\n\treturn NULL;\n}\n\n//lists commands, also prints restriction level\n#define CLF_RAW 0x1\n#define CLF_LEVEL 0x2\n#define CLF_ALTNAME 0x4\n#define CLF_VALUES 0x8\n#define CLF_DEFAULT 0x10\n#define CLF_LATCHES 0x20\n#define CLF_FLAGS 0x40\n#define CLF_FLAGMASK 0x80\n#define CLF_CHANGEDONLY 0x100\nvoid Cvar_List_f (void)\n{\n\tcvar_group_t\t*grp;\n\tcvar_t\t*cmd;\n\tchar *var, *search, *gsearch;\n\tint gnum, i, num = 0;\n\tint listflags = 0, cvarflags = 0;\n\tint total = 0;\n\tchar strtmp[512];\n\tchar *col;\n\tstatic char *cvarlist_help =\n\"cvarlist list all cvars matching given parameters\\n\"\n\"Syntax: cvarlist [-FLdhlrv] [-f flag] [-g group] [cvar]\\n\"\n\"  -c includes only the cvars that have been changed from their defaults\\n\"\n\"  -F shows cvar flags\\n\"\n\"  -L shows latched values\\n\"\n\"  -a shows cvar alternate names\\n\"\n\"  -d shows default cvar values\\n\"\n\"  -f shows only cvars with a matching flag, more than one -f can be used\\n\"\n\"  -g shows only cvar groups using wildcards in group\\n\"\n\"  -h shows this help message\\n\"\n\"  -l shows cvar restriction levels\\n\"\n\"  -r removes group and list headers\\n\"\n\"  -v shows current values\\n\"\n\"  cvar indicates the cvar to show, wildcards (*,?) accepted\\n\"\n\"Cvar flags are:\"\n;\n\n\tif (Cmd_Argc() == 1)\n\t\tgoto showhelp;\n\n\tgsearch = search = NULL;\n\tfor (i = 1; i < Cmd_Argc(); i++)\n\t{\n\t\tvar = Cmd_Argv(i);\n\t\tif (*var == '-')\n\t\t{\n\t\t\t// short options\n\t\t\tfor (var++; *var; var++)\n\t\t\t{\n\t\t\t\tswitch (*var)\n\t\t\t\t{\n\t\t\t\tcase 'g':\n\t\t\t\t\t// fix this so we can search for multiple groups\n\t\t\t\t\ti++;\n\t\t\t\t\tif (i >= Cmd_Argc())\n\t\t\t\t\t{\n\t\t\t\t\t\tCon_Printf(\"Missing parameter for -g\\nUse cvarlist -h for help\\n\");\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tgsearch = Cmd_Argv(i);\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'c':\n\t\t\t\t\tlistflags |= CLF_CHANGEDONLY;\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'a':\n\t\t\t\t\tlistflags |= CLF_ALTNAME;\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'l':\n\t\t\t\t\tlistflags |= CLF_LEVEL;\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'r':\n\t\t\t\t\tlistflags |= CLF_RAW;\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'v':\n\t\t\t\t\tlistflags |= CLF_VALUES;\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'd':\n\t\t\t\t\tlistflags |= CLF_DEFAULT;\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'L':\n\t\t\t\t\tlistflags |= CLF_LATCHES;\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'f':\n\t\t\t\t{\n\t\t\t\t\tchar *tmpv;\n\n\t\t\t\t\tlistflags |= CLF_FLAGMASK;\n\n\t\t\t\t\ti++;\n\t\t\t\t\tif (i >= Cmd_Argc())\n\t\t\t\t\t{\n\t\t\t\t\t\tCon_Printf(\"Missing parameter for -f\\nUse cvarlist -h for help\\n\");\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\ttmpv = Cmd_Argv(i);\n\n\t\t\t\t\tfor (num = 1; num <= CVAR_LASTFLAG; num <<= 1)\n\t\t\t\t\t{\n\t\t\t\t\t\tchar *tmp;\n\n\t\t\t\t\t\ttmp = Cvar_FlagToName(num);\n\n\t\t\t\t\t\tif (tmp && !stricmp(tmp, tmpv))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcvarflags |= num;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif (num > CVAR_LASTFLAG)\n\t\t\t\t\t{\n\t\t\t\t\t\tCon_Printf(\"Invalid cvar flag name\\nUse cvarlist -h for help\\n\");\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'F':\n\t\t\t\t\tlistflags |= CLF_FLAGS;\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'h':\nshowhelp:\n\t\t\t\t\t// list options\n\t\t\t\t\tCon_Printf(\"%s\", cvarlist_help);\n\n\t\t\t\t\tfor (num = 1; num <= CVAR_LASTFLAG; num <<= 1)\n\t\t\t\t\t{\n\t\t\t\t\t\t// no point caring about the content of var at this point\n\t\t\t\t\t\tvar = Cvar_FlagToName(num);\n\n\t\t\t\t\t\tif (var)\n\t\t\t\t\t\t\tCon_Printf(\" %s\", var);\n\t\t\t\t\t}\n\n\t\t\t\t\tCon_Printf(\"\\n\\n\");\n\t\t\t\t\treturn;\n\t\t\t\tcase '-':\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tCon_Printf(\"Invalid option for cvarlist\\nUse cvarlist -h for help\\n\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse\n\t\t\tsearch = var;\n\t}\n\n\t// this is sane.. hopefully\n\tif (gsearch)\n\t\tQ_strlwr(gsearch);\n\n\tif (search)\n\t\tQ_strlwr(search);\n\n\tfor (grp=cvar_groups ; grp ; grp=grp->next)\n\t{\n\t\t// list only cvars with group search substring\n\t\tif (gsearch)\n\t\t{\n\t\t\tQ_strncpyz(strtmp, grp->name, 512);\n\t\t\tQ_strlwr(strtmp);\n\t\t\tif (!wildcmp(gsearch, strtmp))\n\t\t\t\tcontinue;\n\t\t}\n\n\t\tgnum = 0;\n\t\tfor (cmd=grp->cvars ; cmd ; cmd=cmd->next)\n\t\t{\n\t\t\t// list only non-restricted cvars\n\t\t\tif ((cmd->restriction?cmd->restriction:rcon_level.ival) > Cmd_ExecLevel)\n\t\t\t\tcontinue;\n\n\t\t\t// list only cvars with search substring\n\t\t\tif (search)\n\t\t\t{\n\t\t\t\tQ_strncpyz(strtmp, cmd->name, 512);\n\t\t\t\tQ_strlwr(strtmp);\n\n\t\t\t\tif (!wildcmp(search, strtmp))\n\t\t\t\t{\n\t\t\t\t\tif (cmd->name2)\n\t\t\t\t\t{\n\t\t\t\t\t\tQ_strncpyz(strtmp, cmd->name2, 512);\n\t\t\t\t\t\tQ_strlwr(strtmp);\n\t\t\t\t\t\tif (!wildcmp(search, strtmp))\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// list only cvars with matching flags\n\t\t\tif ((listflags & CLF_FLAGMASK) && !(cmd->flags & cvarflags))\n\t\t\t\tcontinue;\n\n\t\t\tif ((listflags & CLF_CHANGEDONLY) && ((cmd->flags & (CVAR_NOSET|CVAR_NOSAVE)) || (cmd->defaultstr && !strcmp(cmd->string, cmd->defaultstr))))\n\t\t\t\tcontinue;\n\n\t\t\t// print cvar list header\n\t\t\tif (!(listflags & CLF_RAW) && !num)\n\t\t\t\tCon_TPrintf(\"^aCVar list:\\n\");\n\n\t\t\t// print group header\n\t\t\tif (!(listflags & CLF_RAW) && !gnum)\n\t\t\t\tCon_Printf(\"^a%s --\\n\", grp->name);\n\n\t\t\t// print restriction level\n\t\t\tif (listflags & CLF_LEVEL)\n\t\t\t\tCon_Printf(\"(%i) \", cmd->restriction);\n\n\t\t\t// print cvar name\n\t\t\tif (!cmd->defaultstr || !strcmp(cmd->string, cmd->defaultstr))\n\t\t\t\tcol = S_COLOR_GREEN;\t//cvar has default value, woo.\n\t\t\telse if (cmd->flags & CVAR_ARCHIVE)\n\t\t\t\tcol = S_COLOR_RED;\t\t//cvar will persist. oh noes.\n\t\t\telse\n\t\t\t\tcol = S_COLOR_YELLOW;\t//cvar is changed, but won't be saved to a config so w/e.\n\t\t\tif (cmd->flags & CVAR_NOUNSAFEEXPAND)\n\t\t\t\tCon_Printf(\"^[%s%s\\\\type\\\\%s\\\\tip\\\\<Current value is hidden>\\n\\n\"S_COLOR_YELLOW\"%s^]\", col, cmd->name, cmd->name, cmd->description?cmd->description:\"\");\t//cvar is changed, but won't be saved to a config so w/e.\n\t\t\telse\n\t\t\t\tCon_Printf(\"^[%s%s\\\\type\\\\%s %s\\\\tip\\\\Default: %s\\nCurrent: %s\\n\\n\"S_COLOR_YELLOW\"%s^]\", col, cmd->name, cmd->name,cmd->string, cmd->defaultstr,cmd->string, cmd->description?cmd->description:\"\");\t//cvar is changed, but won't be saved to a config so w/e.\n\t\t\ttotal++;\n\n\t\t\t// print current value\n\t\t\tif (cmd->flags & CVAR_NOUNSAFEEXPAND)\n\t\t\t\t;\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (listflags & CLF_VALUES)\n\t\t\t\t{\n\t\t\t\t\tif (*cmd->string)\n\t\t\t\t\t\tCon_Printf(\" %s\", cmd->string);\n\t\t\t\t}\n\n\t\t\t\t// print default value\n\t\t\t\tif (cmd->defaultstr && (listflags & CLF_DEFAULT))\n\t\t\t\t\tCon_Printf(\", default \\\"%s\\\"\", cmd->defaultstr);\n\t\t\t}\n\n\t\t\t// print alternate name\n\t\t\tif ((listflags & CLF_ALTNAME) && cmd->name2)\n\t\t\t\tCon_Printf(\", alternate %s\", cmd->name2);\n\n\t\t\t// print cvar flags\n\t\t\tif (listflags & CLF_FLAGS)\n\t\t\t{\n\t\t\t\tfor (i = 1; i <= CVAR_LASTFLAG; i <<= 1)\n\t\t\t\t{\n\t\t\t\t\tif (i & cmd->flags)\n\t\t\t\t\t{\n\t\t\t\t\t\tvar = Cvar_FlagToName(i);\n\t\t\t\t\t\tif (var)\n\t\t\t\t\t\t\tCon_Printf(\" %s\", var);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// print latched value\n\t\t\tif (listflags & CLF_LATCHES)\n\t\t\t{\n\t\t\t\tif (cmd->flags & CVAR_NOUNSAFEEXPAND)\n\t\t\t\t\t;\n\t\t\t\telse if (cmd->latched_string)\n\t\t\t\t\tCon_Printf(\", latched as \\\"%s\\\"\", cmd->latched_string);\n\t\t\t}\n\n\t\t\t// print new line to finish individual cvar\n\t\t\tCon_Printf(\"\\n\");\n\n\t\t\tnum++;\n\t\t\tgnum++;\n\t\t}\n\n\t\t// print new line to seperate groups\n\t\tif (!(listflags & CLF_RAW) && gnum)\n\t\t\tCon_Printf(\"\\n\");\n\t}\n\n\tif (search && !strcmp(search, \"*\"))\n\t\tCon_Printf(\"%i cvars\\n\", total);\n}\n\n//default values are meant to be constants.\n//sometimes that just doesn't make sense.\n//so provide a safe way to change it (MUST be initialised to NULL so that it doesn't get freed)\nvoid Cvar_SetEngineDefault(cvar_t *var, char *val)\n{\n\tqboolean wasdefault = (var->defaultstr == var->enginevalue);\n\tZ_Free(var->enginevalue);\n\tif (val)\n\t\tvar->enginevalue = Z_StrDup(val);\n\telse\n\t\tvar->enginevalue = NULL;\n\n\tif (wasdefault)\n\t\tvar->defaultstr = var->enginevalue;\n}\nvoid Cvar_LockDefaults_f(void)\n{\n\tcvar_group_t *grp;\n\tcvar_t *cmd;\n\tfor (grp=cvar_groups ; grp ; grp=grp->next)\n\t{\n\t\tfor (cmd=grp->cvars ; cmd ; cmd=cmd->next)\n\t\t{\n\t\t\tif (cmd->flags & (CVAR_NOSET | CVAR_CHEAT))\n\t\t\t\tcontinue;\n\n\t\t\tif (strcmp(cmd->string, cmd->defaultstr))\n\t\t\t{\n\t\t\t\tif (cmd->defaultstr != cmd->enginevalue)\n\t\t\t\t\tCvar_DefaultFree(cmd->defaultstr);\n\t\t\t\tcmd->defaultstr = Cvar_DefaultAlloc(cmd->string);\n\t\t\t}\n\t\t}\n\t}\n}\nvoid Cvar_PurgeDefaults_f(void)\n{\n\tcvar_group_t *grp;\n\tcvar_t *cmd;\n\tfor (grp=cvar_groups ; grp ; grp=grp->next)\n\t{\n\t\tfor (cmd=grp->cvars ; cmd ; cmd=cmd->next)\n\t\t{\n\t\t\tif (!cmd->enginevalue)\n\t\t\t\tcontinue;\t//can't reset the cvar's default if its an engine cvar.\n\t\t\tif (cmd->flags & CVAR_NOSET)\n\t\t\t\tcontinue;\n\n\t\t\tif (cmd->defaultstr != cmd->enginevalue)\n\t\t\t{\n\t\t\t\tCvar_DefaultFree(cmd->defaultstr);\n\t\t\t\tcmd->defaultstr = cmd->enginevalue;\n\t\t\t}\n\t\t}\n\t}\n}\n\n//we're changing games. reset everything to engine defaults and kill all user cvars\nstatic void Cvar_Free(cvar_t *tbf);\nvoid Cvar_GamedirChange(void)\n{\n\tcvar_t\t*var, **link;\n\tcvar_group_t *grp;\n\tfor (grp = cvar_groups; grp; grp = grp->next)\n\t{\n\t\tfor (link = &grp->cvars; (var=*link); )\n\t\t{\n\t\t\tif (var->flags & CVAR_NORESET)\n\t\t\t\t;\t//don't reset it (nor kill it).\n\t\t\telse if (var->enginevalue)\n\t\t\t\tCvar_ForceSet(var, var->enginevalue);\n\t\t\telse if (var->flags & CVAR_POINTER)\n\t\t\t{\n\t\t\t\t*link = var->next;\n\t\t\t\tZ_Free(var->string);\n\t\t\t\tif (var->defaultstr != var->enginevalue)\n\t\t\t\t\tCvar_DefaultFree(var->defaultstr);\n\t\t\t\tif (var->latched_string)\n\t\t\t\t\tZ_Free(var->latched_string);\n\t\t\t\tif (var->name)\n\t\t\t\t\tAHash_RemoveDataInsensitive(&cvar_hash, var->name, var);\n\t\t\t\tif (var->name2)\n\t\t\t\t\tAHash_RemoveDataInsensitive(&cvar_hash, var->name2, var);\n\t\t\t\tZ_Free(var);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tlink = &(*link)->next;\n\t\t}\n\t}\n}\n\nvoid Cvar_ResetAll_f(void)\n{\n\tcvar_group_t *grp;\n\tcvar_t *var;\n\tunsigned int bitmask;\t\t//the bits to care about\n\tunsigned int bitmaskvalue;\t//must match this value\n\tchar *cmd = Cmd_Argv(0);\n\tif (!Q_strcasecmp(cmd, \"cvar_resettodefaults_saveonly\"))\n\t\tbitmask = CVAR_NORESET|CVAR_NOSET|CVAR_SAVE, bitmaskvalue = CVAR_SAVE;\n\telse if (!Q_strcasecmp(cmd, \"cvar_resettodefaults_nosaveonly\"))\n\t\tbitmask = CVAR_NORESET|CVAR_NOSET|CVAR_SAVE, bitmaskvalue = 0;\n\telse\t//others...\n\t\tbitmask = CVAR_NORESET|CVAR_NOSET, bitmaskvalue = 0;\n\n\tfor (grp=cvar_groups ; grp ; grp=grp->next)\n\t{\n\t\tfor (var=grp->cvars ; var ; var=var->next)\n\t\t{\n\t\t\tif (!var->enginevalue)\n\t\t\t\tcontinue;\t//can't reset the cvar's default if its an engine cvar.\n\t\t\tif ((var->flags & bitmask) != bitmaskvalue)\n\t\t\t\tcontinue;\n\n\t\t\tif (var->defaultstr != var->enginevalue)\n\t\t\t{\n\t\t\t\tCvar_DefaultFree(var->defaultstr);\n\t\t\t\tvar->defaultstr = var->enginevalue;\n\t\t\t}\n\t\t}\n\t}\n}\n\n#define CRF_ALTNAME 0x1\nvoid Cvar_Reset_f (void)\n{\n\tcvar_group_t *grp;\n\tcvar_t *cmd;\n\tint i, listflags=0, exclflags;\n\tchar *var;\n\tchar *search, *gsearch;\n\tchar strtmp[512];\n\tchar *resetval;\n\tchar *pendingval;\n\n\tsearch = gsearch = NULL;\n\texclflags = 0;\n\n\t// parse command line options\n\tfor (i = 1; i < Cmd_Argc(); i++)\n\t{\n\t\tvar = Cmd_Argv(i);\n\t\tif (*var == '-')\n\t\t{\n\t\t\t// short options\n\t\t\tfor (var++; *var; var++)\n\t\t\t{\n\t\t\t\tswitch (*var)\n\t\t\t\t{\n\t\t\t\tcase 'a':\n\t\t\t\t\tlistflags |= CRF_ALTNAME;\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'g':\n\t\t\t\t\t// fix this so we can search for multiple groups\n\t\t\t\t\ti++;\n\t\t\t\t\tif (i >= Cmd_Argc())\n\t\t\t\t\t{\n\t\t\t\t\t\tCon_Printf(\"Missing parameter for -g\\nUse cvarlist -h for help\\n\");\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tgsearch = Cmd_Argv(i);\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'u':\n\t\t\t\t\texclflags |= CVAR_USERCREATED;\n\t\t\t\tcase 'h':\n\t\t\t\t\tCon_Printf(\"cvarreset resets all cvars to default values matching given parameters\\n\"\n\t\t\t\t\t\t\"Syntax: cvarreset [-ahu] (-g group)/cvar\\n\"\n\t\t\t\t\t\t\"  -a matches cvar against alternate cvar names\\n\"\n\t\t\t\t\t\t\"  -g matches using wildcards in group\\n\"\n\t\t\t\t\t\t\"  -h shows this help message\\n\"\n\t\t\t\t\t\t\"  -u excludes user cvars\\n\"\n\t\t\t\t\t\t\"  cvar indicates the cvars to reset, wildcards (*, ?) accepted\\n\"\n\t\t\t\t\t\t\"A -g or cvar is required\\n\");\n\t\t\t\t\treturn;\n\t\t\t\tdefault:\n\t\t\t\t\tCon_Printf(\"Invalid option for cvarreset\\nUse cvarreset -h for help\\n\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse\n\t\t\tsearch = var;\n\t}\n\n\tif (!search && !gsearch)\n\t{\n\t\tCon_Printf(\"No group or cvars given\\nUse cvarreset -h for help\\n\");\n\t\treturn;\n\t}\n\n\t// this should be sane.. hopefully\n\tif (search)\n\t\tQ_strlwr(search);\n\tif (gsearch)\n\t\tQ_strlwr(gsearch);\n\n\tif (search && !strcmp(search, \"*\"))\n\t\tsearch = NULL;\n\tif (gsearch && !strcmp(gsearch, \"*\"))\n\t\tgsearch = NULL;\n\n\tfor (grp=cvar_groups ; grp ; grp=grp->next)\n\t{\n\t\tif (gsearch)\n\t\t{\n\t\t\tQ_strncpyz(strtmp, grp->name, 512);\n\t\t\tQ_strlwr(strtmp);\n\t\t\tif (!wildcmp(gsearch, strtmp))\n\t\t\t\tcontinue;\n\t\t}\n\n\t\tfor (cmd=grp->cvars ; cmd ; cmd=cmd->next)\n\t\t{\n\t\t\t// reset only non-restricted cvars\n\t\t\tif ((cmd->restriction?cmd->restriction:rcon_level.ival) > Cmd_ExecLevel)\n\t\t\t\tcontinue;\n\n\t\t\t// don't reset cvars with matched flags\n\t\t\tif (exclflags & cmd->flags)\n\t\t\t\tcontinue;\n\n\t\t\t// reset only cvars with search substring\n\t\t\tif (search)\n\t\t\t{\n\t\t\t\tQ_strncpyz(strtmp, cmd->name, 512);\n\t\t\t\tQ_strlwr(strtmp);\n\n\t\t\t\tif (!wildcmp(search, strtmp))\n\t\t\t\t{\n\t\t\t\t\tif ((listflags & CRF_ALTNAME) && cmd->name2)\n\t\t\t\t\t{\n\t\t\t\t\t\tQ_strncpyz(strtmp, cmd->name2, 512);\n\t\t\t\t\t\tQ_strlwr(strtmp);\n\t\t\t\t\t\tif (!wildcmp(search, strtmp))\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif ((cmd->flags & CVAR_NOSET) && !search)\n\t\t\t\tcontinue;\n\t\t\tif (cmd->flags & CVAR_NORESET)\n\t\t\t\tcontinue;\n\n\t\t\t// reset cvar to default only if its okay to do so\n\t\t\tif (cmd->defaultstr)\n\t\t\t\tresetval = cmd->defaultstr;\n\t\t\telse if (cmd->enginevalue)\n\t\t\t\tresetval = cmd->enginevalue;\n\t\t\telse\n\t\t\t\tcontinue;\t//no idea what to reset it to.\n\t\t\tpendingval = cmd->string;\n\t\t\tif (cmd->latched_string)\n\t\t\t\tpendingval = cmd->latched_string;\n\t\t\tif (strcmp(resetval, pendingval))\n\t\t\t\tCvar_Set(cmd, resetval);\n\t\t}\n\t}\n}\n\n/*\n============\nCvar_VariableValue\n============\n*/\nfloat\tCvar_VariableValue (const char *var_name)\n{\n\tcvar_t\t*var;\n\n\tvar = Cvar_FindVar (var_name);\n\tif (!var)\n\t\treturn 0;\n\treturn Q_atof (var->string);\n}\n\n\n/*\n============\nCvar_VariableString\n============\n*/\nchar *Cvar_VariableString (const char *var_name)\n{\n\tcvar_t *var;\n\n\tvar = Cvar_FindVar (var_name);\n\tif (!var)\n\t\treturn cvar_null_string;\n\treturn var->string;\n}\n\nvoid Cvar_SetNamed (const char *var_name, const char *newvalue)\n{\n\tcvar_t *var;\n\n\tvar = Cvar_FindVar (var_name);\n\tif (!var)\n\t\treturn;\n\tCvar_Set(var, newvalue);\n}\n\n/*\n============\nCvar_CompleteVariable\n============\n*/\n/* moved to cmd_compleatevariable\nchar *Cvar_CompleteVariable (char *partial)\n{\n\tcvar_group_t\t*grp;\n\tcvar_t\t\t*cvar;\n\tint\t\t\tlen;\n\n\tlen = Q_strlen(partial);\n\n\tif (!len)\n\t\treturn NULL;\n\n\t// check exact match\n\tfor (grp=cvar_groups ; grp ; grp=grp->next)\n\tfor (cvar=grp->cvars ; cvar ; cvar=cvar->next)\n\t\tif (!strcmp (partial,cvar->name))\n\t\t\treturn cvar->name;\n\n\t// check partial match\n\tfor (grp=cvar_groups ; grp ; grp=grp->next)\n\tfor (cvar=grp->cvars ; cvar ; cvar=cvar->next)\n\t\tif (!Q_strncmp (partial,cvar->name, len))\n\t\t\treturn cvar->name;\n\n\treturn NULL;\n}\n*/\n\nstatic qboolean cvar_archivedvaluechanged;\nqboolean Cvar_UnsavedArchive(void)\n{\n\treturn cvar_archivedvaluechanged;\n}\nvoid Cvar_ConfigChanged(void)\n{\n\tcvar_archivedvaluechanged = true;\n}\nvoid Cvar_Saved(void)\n{\n\tcvar_archivedvaluechanged = false;\n}\n\n/*\n============\nCvar_Set\n============\n*/\nstatic cvar_t *Cvar_SetCore (cvar_t *var, const char *value, qboolean force)\n{\t//fixme: force should probably be a latch bitmask\n\tchar *latch=NULL;\n\tqboolean changed;\n\n\tCOM_AssertMainThread(\"Cvar_SetCore\");\n\n\tif (!var)\n\t\treturn NULL;\n\n\tif ((var->flags & CVAR_WATCHED) || cvar_watched == 2)\n\t\tCon_Printf(\"Cvar Set: %s to %s\\n\", var->name, value);\n\n\tif ((var->flags & CVAR_NOSET) && !force)\n\t{\n\t\tCon_Printf (\"variable %s is readonly\\n\", var->name);\n\t\treturn NULL;\n\t}\n\n\tif (!value)\n\t\tvalue = var->defaultstr;\n\n\tif (force)\n\t\t;\n\telse if (0)//var->flags & CVAR_SERVEROVERRIDE && !force)\n\t\tlatch = \"variable %s is under server control - latched\\n\";\n\telse if (var->flags & CVAR_MAPLATCH && (sv_state || cls_state))\n\t\tlatch = \"variable %s is latched and will be applied for the start of the next map\\n\";\n//\telse if (var->flags & CVAR_LATCHFLUSH)\n//\t\tlatch = \"variable %s is latched (type flush)\\n\";\n#ifdef HAVE_CLIENT\n\telse if (var->flags & CVAR_VIDEOLATCH && qrenderer != QR_NONE)\n\t\tlatch = \"variable %s will be changed after a vid_restart\\n\";\n\telse if (var->flags & CVAR_RENDERERLATCH && qrenderer != QR_NONE)\n\t\tlatch = \"variable %s will be changed after a vid_reload\\n\";\n\telse if (var->flags & CVAR_RENDEREROVERRIDE && qrenderer != QR_NONE)\n\t\tlatch = \"variable %s is not supported by the current renderer/gpu/drivers\\n\";\n#endif\n\telse if (var->flags & CVAR_RULESETLATCH)\n\t\tlatch = \"variable %s is latched due to current ruleset\\n\";\n#ifdef HAVE_CLIENT\n\telse if (var->flags & CVAR_CHEAT && !cls.allow_cheats && cls.state)\n\t\tlatch = \"variable %s is a cheat variable - latched\\n\";\n\telse if (var->flags & CVAR_SEMICHEAT && !cls.allow_semicheats && cls.state)\n\t\tlatch = \"variable %s is a cheat variable - latched\\n\";\n#endif\n\n\tif ((var->flags & CVAR_WARNONCHANGE) && cl_warncmd.ival)\n\t{\n\t\tif (var->defaultstr && strcmp(var->defaultstr, value))\t//warn if its different from the default\n\t\t\tif (!var->enginevalue || strcmp(var->enginevalue, value))\t//unless it matches the ENGINE's default, in which case its probably still okay.\n\t\t\t\tCon_Printf (CON_WARNING\"WARNING: %s has been set to \\\"%s\\\". This is NOT recommended!\\n\", var->name, value);\n\t}\n\n\tif (latch)\n\t{\n\t\tif (cl_warncmd.value)\n\t\t{\t//FIXME: flag that there's a latched cvar instead of spamming prints.\n\t\t\t//FIXME: apply pending rendererlatches vith a vid_reload when leaving the console/menu.\n\t\t\tif (var->latched_string)\n\t\t\t{\t//already latched\n\t\t\t\tif (strcmp(var->latched_string, value))\n\t\t\t\t\tCon_Printf (latch, var->name);\n\t\t\t}\n\t\t\telse\n\t\t\t{\t//new latch\n\t\t\t\tif (strcmp(var->string, value))\n\t\t\t\t\tCon_Printf (latch, var->name);\n\t\t\t}\n\t\t}\n\n\t\tif (var->latched_string && !strcmp(var->latched_string, value))\t//no point, this would force the same\n\t\t\treturn NULL;\n\t\tCvar_ConfigChanged();\n\t\tif (var->latched_string)\n\t\t\tZ_Free(var->latched_string);\n\t\tif (!strcmp(var->string, value))\t//latch to the original value? remove the latch.\n\t\t{\n\t\t\tvar->latched_string = NULL;\n\t\t\treturn NULL;\n\t\t}\n\t\tvar->latched_string = (char*)Z_Malloc(strlen(value)+1);\n\t\tstrcpy(var->latched_string, value);\n\t\treturn NULL;\n\t}\n\n#ifdef HAVE_SERVER\n\tif (var->flags & CVAR_SERVERINFO)\n\t{\n\t\tInfoBuf_SetKey (&svs.info, var->name, value);\n\t}\n#endif\n#ifdef HAVE_CLIENT\n\tif (var->flags & CVAR_SHADERSYSTEM)\n\t{\n\t\tif (var->string && value)\n\t\t\tif (strcmp(var->string, value))\n\t\t\t\tShader_NeedReload(false);\n\t}\n\tif (var->flags & CVAR_USERINFO)\n\t{\n\t\tchar *old = InfoBuf_ValueForKey(&cls.userinfo[0], var->name);\n\t\tconst char *corruptval = TP_ParseFunChars(value);\n\t\tif (strcmp(old, corruptval))\t//only spam the server if it actually changed\n\t\t{\t\t\t\t//this helps with config execs\n\t\t\tInfoBuf_SetKey (&cls.userinfo[0], var->name, corruptval);\n\t\t}\n\t}\n#endif\n\n\tlatch = var->string;//save off the old value (so cvar_set(var, var->string) works)\n\n\tchanged = (!latch) || strcmp(latch, value);\n\n\tvar->flags &= ~CVAR_TEAMPLAYTAINT;\n\tif (changed)\n\t{\n\t\tvar->string = Z_StrDup (value);\n\t\tvar->value = Q_atof (var->string);\n\t\tvar->ival = Q_atoi (var->string);\n\n\t\t{\n\t\t\tchar *str = COM_Parse(var->string);\n\t\t\tvar->vec4[0] = atof(com_token);\n\t\t\tstr = COM_Parse(str);\n\t\t\tvar->vec4[1] = atof(com_token);\n\t\t\tstr = COM_Parse(str);\n\t\t\tvar->vec4[2] = atof(com_token);\n\t\t\tif (!str || !*str)\n\t\t\t\tvar->vec4[3] = 1;\n\t\t\telse\n\t\t\t{\n\t\t\t\tstr = COM_Parse(str);\n\t\t\t\tvar->vec4[3] = atof(com_token);\n\t\t\t}\n\t\t}\n\n\t\tif (latch)\n\t\t{\t//don't do this on registration.\n\t\t\tvar->modified=true;\t//only modified if it changed.\n\t\t\tvar->modifiedcount++;\n\t\t\tif (var->callback)\n\t\t\t\tvar->callback(var, latch);\n\n\t\t\tif (var->flags & CVAR_TELLGAMECODE)\n\t\t\t{\n#ifdef HAVE_SERVER\n\t\t\t\tSVQ1_CvarChanged(var);\n#endif\n#ifdef HAVE_CLIENT\n#ifdef MENU_DAT\n\t\t\t\tMP_CvarChanged(var);\n#endif\n#ifdef CSQC_DAT\n\t\t\t\tCSQC_CvarChanged(var);\n#endif\n#endif\n\t\t\t}\n\t\t}\n\n\t\tif ((var->flags & CVAR_ARCHIVE) && !(var->flags & CVAR_SERVEROVERRIDE) && cl_warncmd.ival)\n\t\t{\n\t\t\tif (var->latched_string)\n\t\t\t{\n\t\t\t\tif (strcmp(var->latched_string, value))\n\t\t\t\t\tCvar_ConfigChanged();\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (!latch || strcmp(latch, value))\n\t\t\t\t\tCvar_ConfigChanged();\n\t\t\t}\n\t\t}\n\n\t\tZ_Free (latch);\t// free the old value string\n\n#ifdef HAVE_SERVER\n\t\tMSV_SendCvarChange(var);\n#endif\n\t}\n\n\tif (var->latched_string)\t//we may as well have this here.\n\t{\n\t\tZ_Free(var->latched_string);\n\t\tvar->latched_string = NULL;\n\t}\n\n\treturn var;\n}\n\nqboolean Cvar_ApplyLatchFlag(cvar_t *var, char *value, unsigned int newflag, unsigned int ignoreflags)\n{\n\tqboolean result = true;\n\n\tchar *latch;\n\tvar->flags &= ~newflag;\n\tlatch = var->latched_string;\n\tvar->latched_string = NULL;\n\tif (!latch)\n\t{\n#ifdef warningmsg\n#pragma warningmsg(\"this means the callback will never be called\")\n#endif\n\t\tlatch = Z_StrDup(var->string);\n//\t\tvar->string = NULL;\n\t}\n\n\tignoreflags = var->flags & ignoreflags;\t//figure out which latching flags we're ignoring\n\tvar->flags -= ignoreflags;\t//and clear them\n\tCvar_ForceSet(var, value);\n\tvar->flags |= ignoreflags;\t//give them back.\n\n\tif (var->latched_string)\n\t{\t//something else latched it\n\t\tZ_Free(var->latched_string);\n\t\tvar->latched_string = NULL;\n\t\tresult = false;\n\t}\n\telse\n\t\tvar->flags |= newflag;\n\n\tif (latch)\n\t{\n\t\tif (!strcmp(var->string, latch))\n\t\t\tZ_Free(latch);\t//don't allow a latch to be the same as the current value\n\t\telse\n\t\t\tvar->latched_string = latch;\n\t}\n\n\treturn result;\n}\n\n#ifdef HAVE_CLIENT\nvoid Cvar_ForceCheatVars(qboolean semicheats, qboolean absolutecheats)\n{\t//this either unlatches if the cheat type is allowed, or enforces a default for full cheats and blank for semicheats.\n\t//this is clientside only.\n\t//if a value is enforced, it is latched to the old value.\n\tcvar_group_t\t*grp;\n\tcvar_t\t*var;\n\n\tchar *latch;\n\n\tfor (grp=cvar_groups ; grp ; grp=grp->next)\n\tfor (var=grp->cvars ; var ; var=var->next)\n\t{\n\t\tif (!(var->flags & (CVAR_CHEAT|CVAR_SEMICHEAT)))\n\t\t\tcontinue;\n\t\tif (var->flags & CVAR_SERVEROVERRIDE)\t//server has control over it. don't force it away.\n\t\t\tcontinue;\n\n\t\tlatch = var->latched_string;\n\t\tvar->latched_string = NULL;\n\t\tif (!latch)\n\t\t\tlatch = Z_StrDup(var->string);\n\n\t\tif (var->flags & CVAR_CHEAT)\n\t\t{\n\t\t\tif (!absolutecheats)\n\t\t\t{\n\t\t\t\tif (var->enginevalue)\n\t\t\t\t\tCvar_ForceSet(var, var->enginevalue);\n\t\t\t\telse\n\t\t\t\t\tCvar_ForceSet(var, var->defaultstr);\n\t\t\t}\n\t\t\telse\n\t\t\t\tCvar_ForceSet(var, latch);\n\t\t}\n\t\telse if (var->flags & CVAR_SEMICHEAT)\n\t\t{\n\t\t\tif (var->flags & CVAR_RULESETLATCH)\n\t\t\t\t;\t//this is too problematic. the ruleset should cover it.\n\t\t\telse if (!semicheats)\n\t\t\t\tCvar_ForceSet(var, \"\");\n\t\t\telse\n\t\t\t\tCvar_ForceSet(var, latch);\n\t\t}\n\n\t\tif (latch)\n\t\t{\n\t\t\tif (!strcmp(var->string, latch))\n\t\t\t\tZ_Free(latch);\n\t\t\telse\n\t\t\t\tvar->latched_string = latch;\n\t\t}\n\t}\n\n\tValidation_Apply_Ruleset();\n}\n#endif\n\nint Cvar_ApplyLatches(int latchflag, qboolean clearflag)\n{\n\tint result = 0;\n\tcvar_group_t\t*grp;\n\tcvar_t\t*var;\n\tint mask = ~0;\n\tint of;\n\n\tif (clearflag)\t//these flags are cleared from cvars.\n\t\tmask = ~latchflag;\n\n\tfor (grp=cvar_groups ; grp ; grp=grp->next)\n\tfor (var=grp->cvars ; var ; var=var->next)\n\t{\n\t\tif (var->flags & latchflag)\n\t\t{\n\t\t\tif (var->latched_string)\n\t\t\t{\n\t\t\t\tof = var->flags;\n\t\t\t\tvar->flags &= ~latchflag;\n\t\t\t\tCvar_Set(var, var->latched_string);\n\t\t\t\tvar->flags = of;\n\n\t\t\t\tresult++;\n\t\t\t}\n\t\t\tvar->flags &= mask;\n\t\t}\n\t}\n\n\treturn result;\n}\n\ncvar_t *Cvar_Set (cvar_t *var, const char *value)\n{\n\treturn Cvar_SetCore(var, value, false);\n}\ncvar_t *Cvar_ForceSet (cvar_t *var, const char *value)\n{\n\treturn Cvar_SetCore(var, value, true);\n}\n\n/*\n============\nCvar_SetValue\n============\n*/\nvoid Cvar_SetValue (cvar_t *var, float value)\n{\n\tchar\tval[32];\n\tchar *e, *p;\n\n//\tif (value == (int)value)\n//\t\tsprintf (val, \"%i\",(int)value);\t//make it look nicer.\n//\telse\n\t\tsprintf (val, \"%f\",value);\n\tp = strchr(val, '.');\n\tif (p)\n\t{\n\t\te = p + strlen(p);\n\t\twhile (e > p && e[-1] == '0')\n\t\t\te--;\n\t\tif (e[-1] == '.')\n\t\t\te--;\n\t\t*e = 0;\n\t}\n\tCvar_Set (var, val);\n}\nvoid Cvar_ForceSetValue (cvar_t *var, float value)\n{\n\tchar\tval[32];\n\n//\tif (value == (int)value)\n//\t\tsprintf (val, \"%i\",(int)value);\t//make it look nicer.\n//\telse\n\t\tsprintf (val, \"%g\",value);\n\tCvar_ForceSet (var, val);\n}\n\nstatic void Cvar_Free(cvar_t *tbf)\n{\n\tcvar_t *var;\n\tcvar_group_t *grp;\n\tif (!(tbf->flags & CVAR_POINTER))\n\t\treturn;\t//only freeable if it was a pointer to begin with.\n\n\tfor (grp=cvar_groups ; grp ; grp=grp->next)\n\t{\n\t\tif (grp->cvars == tbf)\n\t\t{\n\t\t\tgrp->cvars = tbf->next;\n\t\t\tgoto unlinked;\n\t\t}\n\t\tif (grp->cvars)\n\t\tfor (var=grp->cvars ; var->next ; var=var->next)\n\t\t{\n\t\t\tif (var->next == tbf)\n\t\t\t{\n\t\t\t\tvar->next = tbf->next;\n\t\t\t\tgoto unlinked;\n\t\t\t}\n\t\t}\n\t}\nunlinked:\n\tZ_Free(tbf->string);\n\tif (tbf->defaultstr != tbf->enginevalue)\n\t\tCvar_DefaultFree(tbf->defaultstr);\n\tif (tbf->latched_string)\n\t\tZ_Free(tbf->latched_string);\n\tAHash_RemoveDataInsensitive(&cvar_hash, tbf->name, tbf);\n\tif (tbf->name2)\n\t\tAHash_RemoveDataInsensitive(&cvar_hash, tbf->name2, tbf);\n\tZ_Free(tbf);\n}\n\n/*\n============\nCvar_RegisterVariable\n\nAdds a freestanding variable to the variable list.\n============\n*/\n\nqboolean Cvar_Register (cvar_t *variable, const char *groupname)\n{\n\tcvar_t *old;\n\tcvar_group_t *group;\n\tchar *initial;\n\n\tif (variable->defaultstr)\n\t\tinitial = variable->defaultstr;\n\telse if (variable->enginevalue)\n\t\tinitial = variable->enginevalue;\n\telse if (variable->string)\n\t\tinitial = variable->string;\n\telse\n\t\tinitial = \"\";\n\n// check to see if it has already been defined\n\told = Cvar_FindVar (variable->name);\n\tif (!old && variable->name2)\n\t\told = Cvar_FindVar (variable->name2);\n\tif (old)\n\t{\n\t\tif ((old->flags & CVAR_POINTER) && !(variable->flags & CVAR_POINTER))\n\t\t{\n\t\t\tgroup = Cvar_GetGroup(groupname);\n\n\t\t\tvariable->modified = old->modified;\n\t\t\tvariable->modifiedcount = old->modifiedcount;\n\t\t\tvariable->flags |= (old->flags & CVAR_ARCHIVE);\n\n// link the variable in\n\t\t\tvariable->next = group->cvars;\n\t\t\tvariable->restriction = old->restriction;\t//exe registered vars\n\t\t\tgroup->cvars = variable;\n\n// make sure it can be zfreed\n\t\t\tvariable->string = (char*)Z_Malloc (1);\n\n//cheat prevention - engine set default is the one that stays.\n\t\t\tif (initial != variable->enginevalue)\n\t\t\t\tvariable->defaultstr = Cvar_DefaultAlloc(initial);\n\t\t\telse\n\t\t\t\tvariable->defaultstr = initial;\n\n// set it through the function to be consistant\n\t\t\tif (old->latched_string)\n\t\t\t\tCvar_SetCore (variable, old->latched_string, true);\n\t\t\telse\n\t\t\t\tCvar_SetCore (variable, old->string, true);\n\n\t\t\tCvar_Free(old);\n\n\t\t\tAHash_AddInsensitive(&cvar_hash, variable->name, variable);\n\t\t\tif (variable->name2)\n\t\t\t\tAHash_AddInsensitive(&cvar_hash, variable->name2, variable);\n\n\t\t\treturn true;\n\t\t}\n\n\t\tCon_Printf (\"Can't register variable %s, already defined\\n\", variable->name);\n\t\treturn false;\n\t}\n\n// check for overlap with a command\n\tif (Cmd_Exists (variable->name))\n\t{\n\t\tCon_Printf (\"Cvar_RegisterVariable: %s is a command\\n\", variable->name);\n\t\treturn false;\n\t}\n\tif (variable->name2 && (Cmd_Exists (variable->name2) || Cvar_FindVar (variable->name2)))\n\t{\n\t\tCon_Printf (\"Cvar_RegisterVariable: %s is a command/exists\\n\", variable->name2);\n\t\tvariable->name2 = NULL;\n\t}\n\n\tgroup = Cvar_GetGroup(groupname);\n\n// link the variable in\n\tvariable->next = group->cvars;\n\tvariable->restriction = 0;\t//exe registered vars\n\tgroup->cvars = variable;\n\n\tAHash_AddInsensitive(&cvar_hash, variable->name, variable);\n\tif (variable->name2)\n\t\tAHash_AddInsensitive(&cvar_hash, variable->name2, variable);\n\n\tvariable->string = NULL;\n\n\tif (initial != variable->enginevalue)\n\t\tvariable->defaultstr = Cvar_DefaultAlloc(initial);\n\telse\n\t\tvariable->defaultstr = initial;\n\n// set it through the function to be consistant\n\tCvar_SetCore (variable, initial, true);\n\n\treturn true;\n}\n\ncvar_t *Cvar_Get2(const char *name, const char *defaultvalue, int flags, const char *description, const char *group)\n{\n\tcvar_t *var;\n\tvar = Cvar_FindVar(name);\n\n\tif (var)\n\t{\n#ifdef HAVE_CLIENT\n\t\tif ((flags & CVAR_USERINFO) && !(var->flags & CVAR_USERINFO))\n\t\t{\n\t\t\tvar->flags |= CVAR_USERINFO;\n\t\t\tInfoBuf_SetKey(&cls.userinfo[0], var->name, var->string);\n\t\t}\n#endif\n#ifdef HAVE_SERVER\n\t\tif ((flags & CVAR_SERVERINFO) && !(var->flags & CVAR_SERVERINFO))\n\t\t{\n\t\t\tvar->flags |= CVAR_SERVERINFO;\n\t\t\tInfoBuf_SetKey (&svs.info, var->name, var->string);\n\t\t}\n#endif\n\t\treturn var;\n\t}\n\tif (!description || !*description)\n\t\tdescription = NULL;\n\n\t//don't allow cvars with certain funny chars in their name. ever. such things get really messy when saved in configs or whatever.\n\tif (!*name || strchr(name, '\\\"') || strchr(name, '^') || strchr(name, '$') || strchr(name, ' ') || strchr(name, '\\t') || strchr(name, '\\r') || strchr(name, '\\n') || strchr(name, ';'))\n\t\treturn NULL;\n\n\tvar = (cvar_t*)Z_Malloc(sizeof(cvar_t)+strlen(name)+1+(description?(strlen(description)+1):0));\n\tvar->name = (char *)(var+1);\n\tstrcpy(var->name, name);\n\tvar->string = (char*)defaultvalue;\n\tvar->flags = flags|CVAR_POINTER|CVAR_USERCREATED;\n\tvar->modifiedcount = 1;\t//this counter always starts at 1, for q3 compat\n\tif (description)\n\t{\n\t\tchar *desc = var->name+strlen(var->name)+1;\n\t\tstrcpy(desc, description);\n\t\tvar->description = desc;\n\t}\n\n\tif (!Cvar_Register(var, group))\n\t{\n\t\tZ_Free(var);\n\t\treturn NULL;\n\t}\n\n\treturn var;\n}\n\n//prevent the client from altering the cvar until they change map or the server resets the var to the default.\nvoid Cvar_LockFromServer(cvar_t *var, const char *str)\n{\n\tchar *oldlatch;\n\n\tif (!(var->flags & CVAR_SERVEROVERRIDE))\n\t{\n\t\tCon_DPrintf(\"Server taking control of cvar %s (%s)\\n\", var->name, str);\n\t\tvar->flags |= CVAR_SERVEROVERRIDE;\n\t}\n\tvar->flags &= ~CVAR_RENDEREROVERRIDE;\n\n\toldlatch = var->latched_string;\n\tif (oldlatch)\t//maintaining control\n\t\tvar->latched_string = NULL;\n\telse\t//taking control\n\t{\n\t\toldlatch = (char*)Z_Malloc(strlen(var->string)+1);\n\t\tstrcpy(oldlatch, var->string);\n\t}\n\n\tCvar_SetCore (var, str, true);\t//will use all, quote included\n\n\tvar->latched_string = oldlatch;\t//keep track of the original value.\n}\n//not all renderers support all cvars. lets latch some of them if they're unavailable.\nvoid Cvar_LockUnsupportedRendererCvar(cvar_t *var, const char *str)\n{\n\tchar *oldlatch;\n\n\tif (var->latched_string)\n\t\treturn; //err... its not going to do anything anyway so just leave it.\n\tif (!(var->flags & CVAR_RENDEREROVERRIDE))\n\t\tvar->flags |= CVAR_RENDEREROVERRIDE;\n\n\toldlatch = (char*)Z_StrDup(var->string);\n\tCvar_SetCore (var, str, true);\t//will use all, quote included\n\tvar->latched_string = oldlatch;\t//keep track of the original value.\n}\n\n/*\n============\nCvar_Command\n\nHandles variable inspection and changing from the console\n============\n*/\nqboolean\tCvar_Command (cvar_t *v, int level)\n{\n\tchar *str;\n\tchar buffer[65536];\n\tint olev;\n\n// check variables\n\tif (!v)\n\t\treturn false;\n\n\tif (level==RESTRICT_TEAMPLAY || (v->restriction?v->restriction:rcon_level.ival) > level)\n\t{\n\t\tCon_TPrintf (\"You do not have the priveledges for %s\\n\", v->name);\n\t\treturn true;\n\t}\n\n\tif (v->flags & CVAR_NOTFROMSERVER && Cmd_IsInsecure())\n\t{\n\t\tCon_TPrintf (\"Server tried setting %s cvar\\n\", v->name);\n\t\treturn true;\n\t}\n\n// perform a variable print or set\n\tif (Cmd_Argc() == 1)\n\t{\n\t\tif (v->latched_string)\n\t\t{\n\t\t\tif (v->flags & CVAR_MAPLATCH)\n\t\t\t{\n\t\t\t\tCon_TPrintf (\"\\\"%s\\\" is currently \\\"%s\\\"\\n\", v->name, COM_QuotedString(v->string, buffer, sizeof(buffer), true));\n\t\t\t\tCon_TPrintf (\"Will be changed to \\\"%s\\\" on the next map\\n\", COM_QuotedString(v->latched_string, buffer, sizeof(buffer), true));\n\t\t\t}\n\t\t\telse if (v->flags & CVAR_VIDEOLATCH)\n\t\t\t{\n\t\t\t\tCon_TPrintf (\"\\\"%s\\\" is \\\"%s\\\"\\n\", v->name, COM_QuotedString(v->string, buffer, sizeof(buffer), true));\n\t\t\t\tCon_TPrintf (\"Will be changed to \\\"%s\\\" on vid_restart\\n\", COM_QuotedString(v->latched_string, buffer, sizeof(buffer), true));\n\t\t\t}\n\t\t\telse if (v->flags & CVAR_RENDERERLATCH)\n\t\t\t{\n\t\t\t\tCon_TPrintf (\"\\\"%s\\\" is \\\"%s\\\"\\n\", v->name, COM_QuotedString(v->string, buffer, sizeof(buffer), true));\n\t\t\t\tCon_TPrintf (\"Will be changed to \\\"%s\\\" on vid_reload\\n\", COM_QuotedString(v->latched_string, buffer, sizeof(buffer), true));\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tCon_TPrintf (\"\\\"%s\\\" is \\\"%s\\\"\\n\", v->name, COM_QuotedString(v->latched_string, buffer, sizeof(buffer), true));\n\t\t\t\tCon_TPrintf (\"Effective value is \\\"%s\\\"\\n\", COM_QuotedString(v->string, buffer, sizeof(buffer), true));\n\t\t\t}\n\t\t\tif (v->defaultstr)\n\t\t\t\tCon_TPrintf(\"Default: \\\"%s\\\"\\n\", COM_QuotedString(v->defaultstr, buffer, sizeof(buffer), true));\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (v->defaultstr && !strcmp(v->string, v->defaultstr))\n\t\t\t\tCon_TPrintf (\"\\\"%s\\\" is \\\"%s\\\" (default)\\n\", v->name, COM_QuotedString(v->string, buffer, sizeof(buffer), true));\n\t\t\telse\n\t\t\t{\n\t\t\t\tCon_TPrintf (\"\\\"%s\\\" is \\\"%s\\\"\\n\", v->name, COM_QuotedString(v->string, buffer, sizeof(buffer), true));\n\t\t\t\tif (v->defaultstr)\n\t\t\t\t\tCon_TPrintf(\"Default: \\\"%s\\\"\\n\", COM_QuotedString(v->defaultstr, buffer, sizeof(buffer), true));\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\tif (Cmd_Argc() == 2)\n\t\tstr = Cmd_Argv(1);\n\telse\n\t\tstr = Cmd_Args();\n\n\tif (v->flags & CVAR_NOSET)\n\t{\n\t\tif (cl_warncmd.value || developer.value)\n\t\t\tCon_TPrintf (\"Cvar %s may not be set via the console\\n\", v->name);\n\t\treturn true;\n\t}\n\n\tolev = Cmd_ExecLevel;\n\tCmd_ExecLevel = level;\n#ifdef HAVE_CLIENT\n\tif (v->flags & CVAR_USERINFO)\n\t{\n\t\tint seat = CL_TargettedSplit(true);\n\t\tif (Cmd_FromGamecode() && cls.protocol == CP_QUAKEWORLD)\n\t\t{\t//don't bother even changing the cvar locally, just update the server's version.\n\t\t\t//fixme: quake2/quake3 latching.\n\t\t\tCL_SendSeatClientCommand(true, seat, \"setinfo %s %s\", v->name, COM_QuotedString(str, buffer, sizeof(buffer), false));\n\t\t}\n\t\telse\n\t\t\tCL_SetInfo(seat, v->name, str);\n\t\tCmd_ExecLevel = olev;\n\t\treturn true;\n\t}\n\n\tif (v->flags & CVAR_SERVEROVERRIDE)\n\t{\n\t\tif (Cmd_FromGamecode())\n\t\t{\n\t\t\tif (!v->defaultstr || !strcmp(v->defaultstr, str))\t//returning to default\n\t\t\t{\n\t\t\t\tv->flags &= ~CVAR_SERVEROVERRIDE;\n\t\t\t\tif (v->latched_string)\n\t\t\t\t\tstr = v->latched_string;\t//set to the latched\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tCvar_LockFromServer(v, str);\n\t\t\t\tCmd_ExecLevel = olev;\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\t//let cvar_set latch if needed.\n\t}\n\telse if (Cmd_FromGamecode())\n\t{//it's not latched yet\n\t\tif (v->defaultstr && strcmp(v->defaultstr, str))\n\t\t{\t//lock the cvar, unless it's going to it's default value.\n\t\t\tCvar_LockFromServer(v, str);\n\t\t\tCmd_ExecLevel = olev;\n\t\t\treturn true;\n\t\t}\n\t}\n#endif\n\n\tCmd_ExecLevel = 0;//just to try to detect any bugs that could happen from this\n\tCvar_Set (v, str);\t//will use all, quote included\n\tCmd_ExecLevel = olev;\n\treturn true;\n}\n\nstatic char *Cvar_AddDescription(char *buffer, size_t sizeofbuffer, const char *line, const char *cvardesc)\n{\n\tif (!cvardesc || !*cvardesc || strlen(line)+100 > sizeofbuffer)\n\t\tQ_snprintfz(buffer, sizeofbuffer, \"%s\\n\", line);\n\telse\n\t{\n\t\t//fixme: de-funstring.\n\t\tchar *out = buffer, *outend = out+sizeofbuffer-2;\t// 2 for \\n\\0\n\t\tint i,pad;\n\t\tQ_snprintfz(out, outend-out, \"%s\", line);\n\t\tout += strlen(out);\n\n\t\tpad = (strlen(line)+8) & ~7;\n\t\tif (pad < 24)\n\t\t\tpad = 24;\n\t\tfor(i = out-buffer; i < pad; i++)\n\t\t\t*out++ = ' ';\n\t\t*out++ = '/';\n\t\t*out++ = '/';\n\n\t\tfor (;;)\n\t\t{\n\t\t\tif (!*cvardesc || out == outend)\n\t\t\t{\n\t\t\t\t*out++ = '\\n';\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (*cvardesc == '\\n')\n\t\t\t{\n\t\t\t\tcvardesc++;\n\t\t\t\t*out++ = '\\n';\n\t\t\t\tif (out + pad + 2 >= outend)\n\t\t\t\t\tbreak;\n\t\t\t\tfor(i = 0; i < pad; i++)\n\t\t\t\t\t*out++ = ' ';\n\t\t\t\t*out++ = '/';\n\t\t\t\t*out++ = '/';\n\t\t\t}\n\t\t\telse\n\t\t\t\t*out++ = *cvardesc++;\n\t\t}\n\t\t*out = 0;\n\t}\n\treturn buffer;\n}\n\n/*\n============\nCvar_WriteVariables\n\nWrites lines containing \"set variable value\" for all variables\nwith the archive flag set to true.\n============\n*/\nvoid Cvar_WriteVariables (vfsfile_t *f, qboolean all, qboolean nohidden)\n{\n\tqboolean writtengroupheader;\n\tcvar_group_t *grp;\n\tcvar_t\t*var;\n\tchar *val;\n\tchar *s;\n\tchar buffer[65536];\n\n\tfor (grp=cvar_groups ; grp ; grp=grp->next)\n\t{\n\t\twrittengroupheader = false;\n\t\tfor (var = grp->cvars ; var ; var = var->next)\n\t\t\tif (var->flags & CVAR_ARCHIVE || (all && var != &cl_warncmd))\n\t\t\t{\n\t\t\t\t//yeah, don't force-save readonly cvars.\n\t\t\t\tif (var->flags & (CVAR_NOSET|CVAR_NOSAVE))\n\t\t\t\t\tcontinue;\n\t\t\t\tif (nohidden && (var->flags & CVAR_NOUNSAFEEXPAND))\n\t\t\t\t\tcontinue;\n\n\t\t\t\tif (!writtengroupheader)\n\t\t\t\t{\n\t\t\t\t\twrittengroupheader = true;\n\t\t\t\t\ts = va(\"\\n// %s\\n\", grp->name);\n\t\t\t\t\tVFS_WRITE(f, s, strlen(s));\n\t\t\t\t}\n\n\t\t\t\tval = var->string;\t//latched vars should act differently.\n\t\t\t\tif (var->latched_string)\n\t\t\t\t\tval = var->latched_string;\n\n\t\t\t\tif (var->flags & CVAR_USERCREATED)\n\t\t\t\t{\n\t\t\t\t\tif (var->flags & CVAR_ARCHIVE)\n\t\t\t\t\t\ts = va(\"seta %s %s\", var->name, COM_QuotedString(val, buffer, sizeof(buffer), false));\n\t\t\t\t\telse\n\t\t\t\t\t\ts = va(\"set %s %s\", var->name, COM_QuotedString(val, buffer, sizeof(buffer), false));\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\ts = va(\"%s %s\", var->name, COM_QuotedString(val, buffer, sizeof(buffer), false));\n\t\t\t\ts = Cvar_AddDescription(buffer, sizeof(buffer), s, var->description);\n\t\t\t\tVFS_WRITE(f, s, strlen(s));\n\t\t\t}\n\t}\n}\n\nvoid Cvar_Hook(cvar_t *cvar, void (QDECL *callback) (struct cvar_s *var, char *oldvalue))\n{\n\tcvar->callback = callback;\n}\n\nvoid Cvar_Unhook(cvar_t *cvar)\n{\n\tcvar->callback = NULL;\n}\n\nvoid Cvar_ForceCallback(cvar_t *var)\n{\n\tif (var)\n\t\tif (var->callback)\n\t\t\tvar->callback(var, var->string);\n}\n\nvoid Cvar_ApplyCallbacks(int callbackflag)\n{\n\tcvar_group_t\t*grp;\n\tcvar_t\t*var;\n\n\tfor (grp=cvar_groups ; grp ; grp=grp->next)\n\tfor (var=grp->cvars ; var ; var=var->next)\n\t{\n\t\tif (var->flags & callbackflag)\n\t\t{\n\t\t\tif (var->callback)\n\t\t\t\tvar->callback(var, var->string);\n\t\t}\n\t}\n}\n\n// standard callbacks\nvoid QDECL Cvar_Limiter_ZeroToOne_Callback(struct cvar_s *var, char *oldvalue)\n{\n\tif (var->value > 1)\n\t{\n\t\tCvar_ForceSet(var, \"1\");\n\t\treturn;\n\t}\n\telse if (var->value < 0)\n\t{\n\t\tCvar_ForceSet(var, \"0\");\n\t\treturn;\n\t}\n}\n\nvoid Cvar_Init(void)\n{\n\tmemset(cvar_buckets, 0, sizeof(cvar_buckets));\n\t//Hash_InitTable(&cvar_hash, sizeof(cvar_buckets)/Hash_BytesForBuckets(1), cvar_buckets);\n}\n\nvoid Cvar_Shutdown(void)\n{\n\tcvar_t\t*var;\n\tcvar_group_t *grp;\n\twhile(cvar_groups)\n\t{\n\t\twhile(cvar_groups->cvars)\n\t\t{\n\t\t\tvar = cvar_groups->cvars;\n\t\t\tcvar_groups->cvars = var->next;\n\n\t\t\tif (var->defaultstr != var->enginevalue)\n\t\t\t{\n\t\t\t\tCvar_DefaultFree(var->defaultstr);\n\t\t\t\tvar->defaultstr = NULL;\n\t\t\t}\n\t\t\tZ_Free(var->latched_string);\n\t\t\tZ_Free(var->string);\n\n\t\t\tAHash_RemoveDataInsensitive(&cvar_hash, var->name, var);\n\t\t\tif (var->name2)\n\t\t\t\tAHash_RemoveDataInsensitive(&cvar_hash, var->name2, var);\n\n\t\t\tif (var->flags & CVAR_POINTER)\n\t\t\t\tZ_Free(var);\n\t\t\telse\n\t\t\t{\n\t\t\t\tvar->string = NULL;\n\t\t\t\tvar->latched_string = NULL;\n\t\t\t}\n\t\t}\n\n\t\tgrp = cvar_groups;\n\t\tcvar_groups = grp->next;\n\t\tZ_Free(grp);\n\t}\n\tAHash_Cleanup(&cvar_hash);\n}\n"
  },
  {
    "path": "engine/common/cvar.h",
    "content": "/*\nCopyright (C) 1996-1997 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  \n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n*/\n// cvar.h\n\n/*\n\ncvar_t variables are used to hold scalar or string variables that can be changed or displayed at the console or prog code as well as accessed directly\nin C code.\n\nit is sufficient to initialize a cvar_t with just the first two fields, or\nyou can add a ,true flag for variables that you want saved to the configuration\nfile when the game is quit:\n\ncvar_t\tr_draworder = {\"r_draworder\",\"1\"};\ncvar_t\tscr_screensize = {\"screensize\",\"1\",true};\n\nCvars must be registered before use, or they will have a 0 value instead of the float interpretation of the string.  Generally, all cvar_t declarations should be registered in the apropriate init function before any console commands are executed:\nCvar_RegisterVariable (&host_framerate);\n\n\nC code usually just references a cvar in place:\nif ( r_draworder.value )\n\nIt could optionally ask for the value to be looked up for a string name:\nif (Cvar_VariableValue (\"r_draworder\"))\n\nInterpreted prog code can access cvars with the cvar(name) or\ncvar_set (name, value) internal functions:\nteamplay = cvar(\"teamplay\");\ncvar_set (\"registered\", \"1\");\n\nThe user can access cvars from the console in two ways:\nr_draworder\t\t\tprints the current value\nr_draworder 0\t\tsets the current value to 0\nCvars are restricted from having the same names as commands to keep this\ninterface from being ambiguous.\n*/\n\n#include \"../qclib/hash.h\"\n\ntypedef struct cvar_s\n{\n\t//must match q2's definition\n\tchar\t\t\t*name;\n\tchar\t\t\t*string;\n\tchar\t\t\t*latched_string;\t// for CVAR_LATCHMASK vars\n\tunsigned int\tflags;\n\tqboolean\t\tmodified;\n\tfloat\t\t\tvalue;\n\tstruct cvar_s\t*next;\n\n\t//free style :)\n\tchar\t\t\t*name2;\n\n\tvoid\t\t\t(QDECL *callback) (struct cvar_s *var, char *oldvalue);\n\tconst char\t\t*description;\n\tchar\t\t\t*enginevalue;\t\t//when changing manifest dir, the cvar will be reset to this value. never freed.\n\tchar\t\t\t*defaultstr;\t\t//this is the current mod's default value. set on first update.\n\tqbyte\t\t\trestriction;\n\n\tint\t\t\t\tival;\n\tvec4_t\t\t\tvec4;\t//0,0,0,1 if something didn't parse.\n\tint\t\t\t\tmodifiedcount;\n\n#ifdef HLSERVER\n\tstruct hlcvar_s\t*hlcvar;\n#endif\n} cvar_t;\n\n#ifdef MINIMAL\n#define CVARAFCD(ConsoleName,Value,ConsoleName2,Flags,Callback,Description)\t{ConsoleName, NULL, NULL, Flags, 0, 0, 0, ConsoleName2, Callback, NULL,        Value}\n#else\n#define CVARAFCD(ConsoleName,Value,ConsoleName2,Flags,Callback,Description)\t{ConsoleName, NULL, NULL, Flags, 0, 0, 0, ConsoleName2, Callback, Description, Value}\n#endif\n#define CVARAFD(ConsoleName,Value,ConsoleName2,Flags,Description)CVARAFCD(ConsoleName, Value, ConsoleName2, Flags, NULL, Description)\n#define CVARAFC(ConsoleName,Value,ConsoleName2,Flags,Callback)\tCVARAFCD(ConsoleName, Value, ConsoleName2, Flags, Callback, NULL)\n#define CVARAF(ConsoleName,Value,ConsoleName2,Flags)\t\t\tCVARAFCD(ConsoleName, Value, ConsoleName2, Flags, NULL, NULL)\n#define CVARFCD(ConsoleName,Value,Flags,Callback,Description)\tCVARAFCD(ConsoleName, Value, NULL, Flags, Callback, Description)\n#define CVARFC(ConsoleName,Value,Flags,Callback)\t\t\t\tCVARAFCD(ConsoleName, Value, NULL, Flags, Callback, NULL)\n#define CVARAD(ConsoleName,Value,ConsoleName2,Description)\t\tCVARAFCD(ConsoleName, Value, ConsoleName2, 0, NULL, Description)\n#define CVARFD(ConsoleName,Value,Flags,Description)\t\t\t\tCVARAFCD(ConsoleName, Value, NULL, Flags, NULL, Description)\n#define CVARF(ConsoleName,Value,Flags)\t\t\t\t\t\t\tCVARFC(ConsoleName, Value, Flags, NULL)\n#define CVARC(ConsoleName,Value,Callback)\t\t\t\t\t\tCVARFC(ConsoleName, Value, 0, Callback)\n#define CVARCD(ConsoleName,Value,Callback,Description)\t\t\tCVARAFCD(ConsoleName, Value, NULL, 0, Callback, Description)\n#define CVARD(ConsoleName,Value,Description)\t\t\t\t\tCVARAFCD(ConsoleName, Value, NULL, 0, NULL, Description)\n#define CVAR(ConsoleName,Value)\t\t\t\t\t\t\t\t\tCVARD(ConsoleName, Value, NULL)\n\ntypedef struct cvar_group_s\n{\n\tconst char *name;\n\tstruct cvar_group_s *next;\n\n\tcvar_t *cvars;\n} cvar_group_t;\n\n//q2 constants\n#define\tCVAR_ARCHIVE\t\t(1<<0)\t// set to cause it to be saved to vars.rc\n#define\tCVAR_USERINFO\t\t(1<<1)\t// added to userinfo  when changed\n#define\tCVAR_SERVERINFO\t\t(1<<2)\t// added to serverinfo when changed\n#define\tCVAR_NOSET\t\t(1<<3)\t// don't allow change from console at all,\n\t\t\t\t\t\t\t// but can be set from the command line\n#define\tCVAR_MAPLATCH\t\t(1<<4)\t// save changes until server restart, to avoid q2 gamecode bugging out.\n\n//freestyle\n#define CVAR_POINTER\t\t(1<<5)\t// q2 style. May be converted to q1 if needed. These are often specified on the command line and then converted into q1 when registered properly.\n//#define CVAR_UNUSED\t\t\t(1<<6)  //the default string was malloced/needs to be malloced, free on unregister\n#define CVAR_NOTFROMSERVER\t(1<<7)\t//cvar cannot be set by gamecode. the console will ignore changes to cvars if set at from the server or any gamecode. This is to protect against security flaws - like qterm\n#define CVAR_USERCREATED\t(1<<8)\t//write a 'set' or 'seta' in front of the var name.\n#define CVAR_CHEAT\t\t\t(1<<9)\t//latch to the default, unless cheats are enabled.\n#define CVAR_SEMICHEAT\t\t(1<<10)\t//if strict ruleset, force to blank (aka 0).\n#define CVAR_RENDERERLATCH\t(1<<11)\t//requires a vid_reload to reapply.\n#define CVAR_SERVEROVERRIDE\t(1<<12)\t//the server has overridden out local value - should probably be called SERVERLATCH\n#define CVAR_RENDERERCALLBACK\t(1<<13) //force callback for cvars on renderer change\n#define CVAR_NOUNSAFEEXPAND\t(1<<14) //cvar cannot be read by gamecode. do not expand cvar value when command is from gamecode.\n#define CVAR_RULESETLATCH\t(1<<15)\t//latched by the ruleset\n#define CVAR_SHADERSYSTEM\t(1<<16)\t//change flushes shaders.\n#define CVAR_TELLGAMECODE\t(1<<17) //tells the gamecode when it has changed, does not prevent changing, added as an optimisation\n\n#define CVAR_CONFIGDEFAULT\t(1<<18)\t//this cvar's default value has been changed to match a config.\n#define CVAR_NOSAVE\t\t\t(1<<19) //this cvar should never be saved. ever.\n#define CVAR_NORESET\t\t(1<<20) //cvar is not reset by various things.\n#define CVAR_TEAMPLAYTAINT\t(1<<21)\t//current value contains the evaluation of a teamplay macro.\n\n#define CVAR_WATCHED\t\t(1<<22)\t//report any attempts to change this cvar.\n#define CVAR_VIDEOLATCH\t\t(1<<23)\n#define CVAR_WARNONCHANGE\t(1<<24)\t//print a warning when changed to a value other than its default.\n#define CVAR_RENDEREROVERRIDE\t(1<<25)\t//the renderer has forced the cvar to indicate that only that value is supported\n\n#define CVAR_LASTFLAG CVAR_VIDEOLATCH\n\n#define CVAR_LATCHMASK\t\t(CVAR_MAPLATCH|CVAR_RENDERERLATCH|CVAR_VIDEOLATCH|CVAR_SERVEROVERRIDE|CVAR_CHEAT|CVAR_SEMICHEAT)\t//you're only allowed one of these.\n#define CVAR_NEEDDEFAULT\tCVAR_CHEAT\n\n//an alias\n#define CVAR_SAVE CVAR_ARCHIVE\n\ncvar_t *Cvar_Get2 (const char *var_name, const char *value, int flags, const char *description, const char *groupname);\n#define Cvar_Get(n,v,f,g) Cvar_Get2(n,v,f,NULL,g)\n\nvoid Cvar_LockFromServer(cvar_t *var, const char *str);\nvoid Cvar_LockUnsupportedRendererCvar(cvar_t *var, const char *str);\n\nqboolean \tCvar_Register (cvar_t *variable, const char *cvargroup);\n// registers a cvar that already has the name, string, and optionally the\n// archive elements set.\n\ncvar_t\t*Cvar_ForceSet (cvar_t *var, const char *value);\ncvar_t \t*Cvar_Set (cvar_t *var, const char *value);\n// equivelant to \"<name> <variable>\" typed at the console\n\nvoid\tCvar_ForceSetValue (cvar_t *var, float value);\nvoid\tCvar_SetValue (cvar_t *var, float value);\n// expands value to a string and calls Cvar_Set\n\nqboolean Cvar_ApplyLatchFlag(cvar_t *var, char *value, unsigned int newflag, unsigned int ignoreflags);\n\nqboolean Cvar_UnsavedArchive(void);\nvoid Cvar_Saved(void);\nvoid Cvar_ConfigChanged(void);\n\nextern int cvar_watched;\t//so that cmd.c knows that it should add messages when configs are execed\nvoid Cvar_ParseWatches(void);\t//parse -watch args\n\nint Cvar_ApplyLatches(int latchflag, qboolean clearflag);\n//sets vars to their latched values (and optionally forgets the cvarflag in question)\n\nvoid Cvar_Hook(cvar_t *cvar, void (QDECL *callback) (struct cvar_s *var, char *oldvalue));\n//hook a cvar with a given callback function at runtime\n\nvoid Cvar_Unhook(cvar_t *cvar);\n//unhook a cvar\n\nvoid Cvar_ForceCallback(cvar_t *cvar);\n// force a cvar callback\n\nvoid Cvar_ApplyCallbacks(int callbackflag);\n//forces callbacks to be ran for given flags\n\nvoid QDECL Cvar_Limiter_ZeroToOne_Callback(struct cvar_s *var, char *oldvalue);\n//cvar callback to limit cvar value to 0 or 1\n\nfloat\tCvar_VariableValue (const char *var_name);\n// returns 0 if not defined or non numeric\n\nchar\t*Cvar_VariableString (const char *var_name);\n// returns an empty string if not defined\n\nvoid Cvar_SetNamed (const char *var_name, const char *newvalue);\n\nchar \t*Cvar_CompleteVariable (const char *partial);\n// attempts to match a partial variable name for command line completion\n// returns NULL if nothing fits\n\nqboolean Cvar_Command (cvar_t *v, int level);\n// called by Cmd_ExecuteString when Cmd_Argv(0) doesn't match a known\n// command.  Returns true if the command was a variable reference that\n// was handled. (print or change)\n\nvoid Cvar_WriteVariables (vfsfile_t *f, qboolean all, qboolean nohidden);\n// Writes lines containing \"set variable value\" for all variables\n// with the archive flag set to true.\n\ncvar_t *Cvar_FindVar (const char *var_name);\n\nvoid Cvar_SetEngineDefault(cvar_t *var, char *val);\n\nvoid Cvar_Init(void);\nvoid Cvar_Shutdown(void);\n\nvoid Cvar_ForceCheatVars(qboolean semicheats, qboolean absolutecheats);\t//locks/unlocks cheat cvars depending on weather we are allowed them.\n\n//extern cvar_t\t*cvar_vars;\n"
  },
  {
    "path": "engine/common/fs.c",
    "content": "#include \"quakedef.h\"\n#include \"netinc.h\"\n\n//#define com_gamedir com__gamedir\n\n#include <ctype.h>\n#include <limits.h>\n\n#include \"fs.h\"\n#include \"shader.h\"\n#ifdef _WIN32\n#include \"winquake.h\"\n#endif\n\n\n\n#ifdef FTE_TARGET_WEB\t//for stuff that doesn't work right...\n#define FORWEB(a,b) a\n#else\n#define FORWEB(a,b) b\n#endif\n\nstatic char *vidfilenames[] =\t//list of filenames to check to see if graphics stuff needs reloading on filesystem changes\n{\n\t\"gfx.wad\",\n\t\"gfx/conback.lmp\"/*q1*/,\"gfx/menu/conback.lmp\"/*h2*/,\"pics/conback.pcx\"/*q2*/,\t//misc stuff\n\t\"gfx/palette.lmp\", \"pics/colormap.pcx\",\n\t\"gfx/conchars.png\",\t//conchars...\n\t\"fonts/qfont.kfont\", \"gfx/mcharset.lmp\",\t//menu fonts\n};\n\n\n#if !defined(HAVE_LEGACY) || !defined(HAVE_CLIENT)\n\t#define ZFIXHACK\n#elif defined(ANDROID) //on android, these numbers seem to be generating major weirdness, so disable these.\n\t#define ZFIXHACK\n#elif defined(FTE_TARGET_WEB) //on firefox (but not chrome or ie), these numbers seem to be generating major weirdness, so tone them down significantly by default.\n\t#define ZFIXHACK \"set r_polygonoffset_submodel_offset 1\\nset r_polygonoffset_submodel_factor 0.05\\n\"\n#else\t//many quake maps have hideous z-fighting. this provides a way to work around it, although the exact numbers are gpu and bitdepth dependant, and trying to fix it can actually break other things.\n\t#define ZFIXHACK \"set r_polygonoffset_submodel_offset 25\\nset r_polygonoffset_submodel_factor 0.05\\n\"\n#endif\n\n/*ezquake cheats and compat*/\n#define EZQUAKECOMPETITIVE \"set ruleset_allow_fbmodels 1\\nset sv_demoExtensions \\\"\\\"\\n\"\n/*quake requires a few settings for compatibility*/\n#define QRPCOMPAT \"set cl_cursor_scale 0.2\\nset cl_cursor_bias_x 7.5\\nset cl_cursor_bias_y 0.8\\n\"\n#define QUAKESPASMSUCKS \"set mod_h2holey_bugged 1\\n\"\n#define QUAKEOVERRIDES \"set sv_listen_nq 2\\nset v_gammainverted 1\\nset cl_download_mapsrc \\\"https://maps.quakeworld.nu/all/\\\"\\nset con_stayhidden 0\\nset allow_download_pakcontents 2\\nset allow_download_refpackages 0\\nset r_meshpitch -1\\nr_sprite_backfacing 1\\nset sv_bigcoords \\\"\\\"\\nmap_autoopenportals 1\\n\"  \"sv_port \"STRINGIFY(PORT_QWSERVER)\" \"STRINGIFY(PORT_NQSERVER)\"\\n\" ZFIXHACK EZQUAKECOMPETITIVE QUAKESPASMSUCKS\n#define QCFG \"//schemes quake qw\\n\"   QUAKEOVERRIDES \"set com_parseutf8 0\\n\" QRPCOMPAT\n#define EZCFG \"//mainconfig config\\n\" QCFG \"fps_preset fast\\n\"\n#define KEXCFG \"//schemes quake_r2\\n\" QUAKEOVERRIDES \"set com_parseutf8 1\\nset campaign 0\\nset net_enable_dtls 1\\nset sv_mintic 0.016666667\\nset sv_maxtic $sv_mintic\\nset cl_netfps 60\\n\"\n/*NetQuake reconfiguration, to make certain people feel more at home...*/\n#define NQCFG \"//disablehomedir 1\\n//mainconfig ftenq\\n\" QCFG \"cfg_save_auto 1\\nfps_preset nq\\nset cl_loopbackprotocol auto\\ncl_sbar 1\\nset plug_sbar 0\\nset sv_port \"STRINGIFY(PORT_NQSERVER)\"\\ncl_defaultport \"STRINGIFY(PORT_NQSERVER)\"\\nset m_preset_chosen 1\\nset vid_wait 1\\nset cl_demoreel 1\\n\"\n#define SPASMCFG NQCFG \"fps_preset builtin_spasm\\nset cl_demoreel 0\\ncl_sbar 2\\nset gl_load24bit 1\\n\"\n#define FITZCFG NQCFG \"fps_preset builtin_spasm\\ncl_sbar 2\\nset gl_load24bit 1\\n\"\n#define TENEBRAECFG NQCFG \"fps_preset builtin_tenebrae\\n\"\n//nehahra has to be weird with its extra cvars, and buggy fullbrights.\n#define NEHCFG QCFG \"set nospr32 0\\nset cutscene 1\\nalias startmap_sp \\\"map nehstart\\\"\\nr_fb_bmodels 0\\nr_fb_models 0\\n\"\n/*stuff that makes dp-only mods work a bit better*/\n#define DPCOMPAT QCFG \"gl_specular 1\\nset _cl_playermodel \\\"\\\"\\n set dpcompat_set 1\\ndpcompat_console 1\\nset dpcompat_corruptglobals 1\\nset vid_pixelheight 1\\nset dpcompat_set 1\\nset dpcompat_console 1\\nset r_particlesdesc effectinfo\\n\"\n/*nexuiz/xonotic has a few quirks/annoyances...*/\n#define NEXCFG DPCOMPAT \"cl_loopbackprotocol dpp7\\nset sv_listen_dp 1\\nset sv_listen_qw 0\\nset sv_listen_nq 0\\nset dpcompat_nopreparse 1\\nset sv_bigcoords 1\\nset sv_maxairspeed \\\"30\\\"\\nset sv_jumpvelocity 270\\nset sv_mintic \\\"0.01\\\"\\ncl_nolerp 0\\n\"\n#define XONCFG NEXCFG \"set qport $qport_\\ncom_parseutf8 1\\npr_fixbrokenqccarrays 2\\nset pr_csqc_memsize 64m\\nset pr_ssqc_memsize 96m\\n\"\n/*some modern non-compat settings*/\n#define DMFCFG \"set com_parseutf8 1\\npm_airstep 1\\nsv_demoExtensions 1\\n\"\n/*set some stuff so our regular qw client appears more like hexen2. sv_mintic must be 0.015 to 'fix' the ravenstaff so that its projectiles don't impact upon each other, or even 0.05 to exactly match the hardcoded assumptions in obj_push. There's maps that depend on a low framerate via waterjump framerate-dependance too.*/\n#define HEX2CFG \"//schemes hexen2\\n\" \"set v_gammainverted 1\\nset com_parseutf8 -1\\nset gl_font gfx/hexen2\\nset in_builtinkeymap 0\\nset_calc cl_playerclass int (random * 5) + 1\\nset cl_forwardspeed 200\\nset cl_backspeed 200\\ncl_sidespeed 225\\nset sv_maxspeed 640\\ncl_run 0\\nset watervis 1\\nset r_lavaalpha 1\\nset r_lavastyle -2\\nset r_wateralpha 0.5\\nset sv_pupglow 1\\ngl_shaftlight 0.5\\nsv_mintic 0.05\\nset r_meshpitch -1\\nset r_meshroll -1\\nr_sprite_backfacing 1\\nset mod_warnmodels 0\\nset cl_model_bobbing 1\\nsv_sound_watersplash \\\"misc/hith2o.wav\\\"\\nsv_sound_land \\\"fx/thngland.wav\\\"\\nset sv_walkpitch 0\\n\"\n/*yay q2!*/\n#define Q2CFG \"//schemes quake2\\n\" \"set com_protocolversion \"STRINGIFY(PROTOCOL_VERSION_Q2)\"\\nset v_gammainverted 1\\nset com_parseutf8 0\\ncom_gamedirnativecode 1\\nset sv_bigcoords 0\\nsv_port \"STRINGIFY(PORT_Q2SERVER)\" \"STRINGIFY(PORT_Q2EXSERVER)\"\\ncl_defaultport \"STRINGIFY(PORT_Q2SERVER)\"\\n\"\t\\\n\t\"set r_replacemodels \" IFMINIMAL(\"\",\"md3 md5mesh\")\"\\n\"\t\\\n\t\"set r_glsl_emissive 0\\n\" /*work around the _glow textures not being meant to glow*/\n/*Q3's ui doesn't like empty model/headmodel/handicap cvars, even if the gamecode copes*/\n#define Q3CFG \"//schemes quake3\\n\" \"set v_gammainverted 0\\nset snd_ignorecueloops 1\\nsetfl g_gametype 0 s\\nset gl_clear 1\\nset r_clearcolour 0 0 0\\nset com_parseutf8 0\\ngl_overbright \"FORWEB(\"0\",\"2\")\"\\nseta model sarge\\nseta headmodel sarge\\nseta handicap 100\\ncom_gamedirnativecode 1\\nsv_port \"STRINGIFY(PORT_Q3SERVER)\"\\ncl_defaultport \"STRINGIFY(PORT_Q3SERVER)\"\\ncom_protocolversion 68\\n\"\n//#define RMQCFG \"sv_bigcoords 1\\n\"\n\n#define HLCFG \"plug_load ffmpeg\\n\"\n#define HL2CFG \"plug_load ode;plug_load hl2\\n\"\n\n#ifndef UPDATEURL\n\t#ifdef HAVE_SSL\n\t\t#define UPDATEURL(g)\t\"/downloadables.php?game=\" #g\n\t#else\n\t\t#define UPDATEURL(g)\tNULL\n\t#endif\n#endif\n\n#define QUAKEPROT \"FTE-Quake DarkPlaces-Quake\"\n\ntypedef struct {\n\tconst char *argname;\t//used if this was used as a parameter.\n\tconst char *exename;\t//used if the exe name contains this\n\tconst char *protocolname;\t//sent to the master server when this is the current gamemode (Typically set for DP compat).\n\tconst char *auniquefile[4];\t//used if this file is relative from the gamedir. needs just one file\n\n\tconst char *customexec;\n\n\tconst char *dir[4];\n\tconst char *poshname;\t\t//Full name for the game.\n\tconst char *downloadsurl;\t//url to check for updates.\n\tconst char *needpackages;\t//package name(s) that are considered mandatory for this game to work.\n\tconst char *manifestfile;\t//contents of manifest file to use.\n} gamemode_info_t;\nstatic const gamemode_info_t gamemode_info[] = {\n#ifdef GAME_SHORTNAME\n\t#ifndef GAME_PROTOCOL\n\t#define GAME_PROTOCOL\t\t\tDISTRIBUTION\n\t#endif\n\t#ifndef GAME_IDENTIFYINGFILES\n\t#define GAME_IDENTIFYINGFILES\tNULL\t//\n\t#endif\n\t#ifndef GAME_DEFAULTCMDS\n\t#define GAME_DEFAULTCMDS\t\tNULL\t//doesn't need anything\n\t#endif\n\t#ifndef GAME_BASEGAMES\n\t#define GAME_BASEGAMES\t\t\t\"data\"\n\t#endif\n\t#ifndef GAME_FULLNAME\n\t#define GAME_FULLNAME\t\t\tFULLENGINENAME\n\t#endif\n\t#ifndef GAME_MANIFESTUPDATE\n\t#define GAME_MANIFESTUPDATE\t\tNULL\n\t#endif\n\n\t{\"-\"GAME_SHORTNAME,\t\tGAME_SHORTNAME,\t\t\tGAME_PROTOCOL,\t\t\t\t\t{GAME_IDENTIFYINGFILES}, GAME_DEFAULTCMDS, {GAME_BASEGAMES}, GAME_FULLNAME, NULL/*updateurl*/, NULL/*needpackages*/, GAME_MANIFESTUPDATE},\n#endif\n//note that there is no basic 'fte' gamemode, this is because we aim for network compatability. Darkplaces-Quake is the closest we get.\n//this is to avoid having too many gamemodes anyway.\n\n//mission packs should generally come after the main game to avoid prefering the main game. we violate this for hexen2 as the mission pack is mostly a superset.\n//whereas the quake mission packs replace start.bsp making the original episodes unreachable.\n//for quake, we also allow extracting all files from paks. some people think it loads faster that way or something.\n#ifdef HAVE_LEGACY\n\t//cmdline switch exename    protocol name(dpmaster)  identifying file\t\t\t\texec     dir1       dir2    dir3       dir(fte)     full name\n\t//use rerelease behaviours if we seem to be running from that dir.\n\t{\"-quake_rerel\",NULL,\t\t\"FTE-QuakeRerelease\",\t{\"QuakeEX.kpf\"},\t\t\t\tKEXCFG,\t{\"id1\",\t\t\t\t\t\t\t\"*fte\"},\t\"Quake Re-Release\",\t\t\t\t\tUPDATEURL(Q1)},\n\t//standard quake\n\t{\"-quake\",\t\t\"q1\",\t\tQUAKEPROT,\t\t\t\t{\"id1/pak0.pak\",\"id1/quake.rc\"},QCFG,\t{\"id1\",\t\t\"qw\",\t\t\t\t\"*fte\"},\t\"Quake\",\t\t\t\t\t\t\tUPDATEURL(Q1)},\n\t//alternative name, because fmf file install names are messy when a single name is used for registry install path.\n\t{\"-afterquake\",\tNULL,\t\t\"FTE-Quake\",\t\t\t{\"id1/pak0.pak\", \"id1/quake.rc\"},QCFG,\t{\"id1\",\t\t\"qw\",\t\t\t\t\"*fte\"},\t\"AfterQuake\",\t\t\t\t\t\tUPDATEURL(Q1),\tNULL},\n\t//netquake-specific quake that avoids qw/ with its nquake fuckups, and disables nqisms\n\t{\"-netquake\",\tNULL,\t\tQUAKEPROT,\t\t\t\t{\"id1/pak0.pak\",\"id1/quake.rc\"},NQCFG,\t{\"id1\"},\t\t\t\t\t\t\t\t\t\"NetQuake\",\t\t\t\t\t\t\tUPDATEURL(Q1)},\n\t//common variant of fitzquake that includes its own special pak file in the basedir\n\t{\"-spasm\",\t\tNULL,\t\tQUAKEPROT,\t\t\t\t{\"quakespasm.pak\"},\t\t\t\tSPASMCFG,{\"/id1\"},\t\t\t\t\t\t\t\t\t\"FauxSpasm\",\t\t\t\t\t\tUPDATEURL(Q1)},\n\t//because we can. 'fps_preset spasm' is hopefully close enough...\n\t{\"-fitz\",\t\t\"nq\",\t\tQUAKEPROT,\t\t\t\t{\"id1/pak0.pak\",\"id1/quake.rc\"},FITZCFG,{\"id1\"},\t\t\t\t\t\t\t\t\t\"FauxFitz\",\t\t\t\t\t\t\tUPDATEURL(Q1)},\n\t//because we can\n\t{\"-tenebrae\",\tNULL,\t\tQUAKEPROT,\t\t\t\t{\"tenebrae/Pak0.pak\",\"id1/quake.rc\"},TENEBRAECFG,{\"id1\",\t\t\t\t\"tenebrae\"},\"FauxTenebrae\",\t\t\t\t\t\tUPDATEURL(Q1)},\n\t//for the luls\n\t{\"-ezquake\",\t\"ez\",\t\t\"FTE-Quake\",\t\t\t{\"id1/pak0.pak\", \"id1/quake.rc\"},EZCFG,\t{\"id1\",\t\t\"qw\",\t\t\t\t\"*ezquake\"},\"ezFTE\",\t\t\t\t\t\t\tUPDATEURL(Q1),\tNULL},\n\n#if defined(Q2CLIENT) || defined(Q2SERVER)\n\t//list quake2 before q1 missionpacks, to avoid confusion about rogue/pak0.pak\n\t{\"-quake2\",\t\t\"q2\",\t\t\"Quake2\",\t\t\t\t{\"baseq2/pak0.pak\"},\t\t\tQ2CFG,\t{\"baseq2\",\t\t\t\t\t\t\"*fteq2\"},\t\"Quake II\",\t\t\t\t\t\t\tUPDATEURL(Q2)},\n\t//mods of the above that should generally work.\n\t{\"-dday\",\t\t\"dday\",\t\t\"Quake2\",\t\t\t\t{\"dday/pak0.pak\"},\t\t\t\tQ2CFG,\t{\"baseq2\",\t\"dday\",\t\t\t\t\"*fteq2\"},\t\"D-Day: Normandy\"},\n#endif\n\n\t//quake's mission packs technically have their own protocol (thanks to stat_items). copyrights mean its best to keep them separate, too.\n\t{\"-hipnotic\",\t\"hipnotic\",\t\"FTE-Hipnotic\",\t\t\t{\"hipnotic/pak0.pak\",\"hipnotic/gfx.wad\"},QCFG,{\"id1\",\"qw\",\t\"hipnotic\",\t\"*fte\"},\t\"Quake: Scourge of Armagon\",\t\tUPDATEURL(Q1)},\n\t{\"-rogue\",\t\t\"rogue\",\t\"FTE-Rogue\",\t\t\t{\"rogue/pak0.pak\",\"rogue/gfx.wad\"},QCFG,{\"id1\",\t\t\"qw\",\t\"rogue\",\t\"*fte\"},\t\"Quake: Dissolution of Eternity\",\tUPDATEURL(Q1)},\n\t{\"-dopa\",\t\tNULL,\t\tQUAKEPROT,\t\t\t\t{\"dopa/pak0.pak\"},\t\t\t\tKEXCFG,\t{\"id1\",\t\t\"qw\",\t\"dopa\",\t\t\"*fte\"},\t\"Quake: Dimensions of the Past\",\tUPDATEURL(Q1)},\n\t{\"-mg1\",\t\tNULL,\t\tQUAKEPROT,\t\t\t\t{\"mga/pak0.pak\"},\t\t\t\tKEXCFG,\t{\"id1\",\t\t\"qw\",\t\"mg1\",\t\t\"*fte\"},\t\"Quake: Dimension of the Machine\",\tUPDATEURL(Q1)},\n\n\t//various quake-dependant non-standalone mods that require hacks\n\t//quoth needed an extra arg just to enable hipnotic hud drawing, it doesn't actually do anything weird, but most engines have a -quoth arg, so lets have one too.\n\t{\"-quoth\",\t\t\"quoth\",\t\"FTE-Quake\",\t\t\t{\"quoth/pak0.pak\"},\t\t\t\tQCFG,\t{\"id1\",\t\t\"qw\",\t\"quoth\",\t\"*fte\"},\t\"Quake: Quoth\",\t\t\t\t\t\tUPDATEURL(Q1)},\n\t{\"-nehahra\",\t\"nehahra\",\t\"FTE-Quake\",\t\t\t{\"nehahra/pak0.pak\"},\t\t\tNEHCFG,\t{\"id1\",\t\t\"qw\",\t\"nehahra\",\t\"*fte\"},\t\"Quake: Seal Of Nehahra\",\t\t\tUPDATEURL(Q1)},\n\t//various quake-based standalone mods.\n\t{\"-librequake\",\t\"librequake\",\"LibreQuake\",\t\t\t{\"lq1/pak0.pak\",\"lq1/gfx.pk3\",\"lq1/quake.rc\"},QCFG,\t{\"lq1\"},\t\t\t\t\t\t\t\t\t\"LibreQuake\",\t\t\tUPDATEURL(LQ)},\n//\t{\"-nexuiz\",\t\t\"nexuiz\",\t\"Nexuiz\",\t\t\t\t{\"nexuiz.exe\"},\t\t\t\t\tNEXCFG,\t{\"data\",\t\t\t\t\t\t\"*ftedata\"},\"Nexuiz\"},\n//\t{\"-xonotic\",\t\"xonotic\",\t\"Xonotic\",\t\t\t\t{\"data/xonotic-data.pk3dir\",\n//\t\t\t\t\t\t\t\t\t\t\t\t\t\t \"data/xonotic-*data*.pk3\"},\tXONCFG,\t{\"data\",\t\t\t\t\t\t\"*ftedata\"},\"Xonotic\",\t\t\t\t\t\t\tUPDATEURL(Xonotic)},\n//\t{\"-spark\",\t\t\"spark\",\t\"Spark\",\t\t\t\t{\"base/src/progs.src\",\n//\t\t\t\t\t\t\t\t\t\t\t\t\t\t \"base/qwprogs.dat\",\n//\t\t\t\t\t\t\t\t\t\t\t\t\t\t \"base/pak0.pak\"},\t\t\t\tDMFCFG,\t{\"base\",\t\t\t\t\t\t\t\t},\t\"Spark\"},\n//\t{\"-scouts\",\t\t\"scouts\",\t\"FTE-SJ\",\t\t\t\t{\"basesj/src/progs.src\",\n//\t\t\t\t\t\t\t\t\t\t\t\t\t\t \"basesj/progs.dat\",\n//\t\t\t\t\t\t\t\t\t\t\t\t\t\t \"basesj/pak0.pak\"},\t\t\tNULL,\t{\"basesj\",\t\t\t\t\t\t        },\t\"Scouts Journey\"},\n//\t{\"-rmq\",\t\t\"rmq\",\t\t\"RMQ\",\t\t\t\t\t{NULL},\t\t\t\t\t\t\tRMQCFG,\t{\"id1\",\t\t\"qw\",\t\"rmq\",\t\t\"*fte\"\t},\t\"Remake Quake\"},\n\n#ifdef HEXEN2\n\t//hexen2's mission pack generally takes precedence if both are installed.\n\t{\"-portals\",\t\"h2mp\",\t\t\"FTE-H2MP\",\t\t\t\t{\"portals/hexen.rc\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t \"portals/pak3.pak\"},\t\t\tHEX2CFG,{\"data1\",\t\"portals\",\t\t\t\"*fteh2\"},\t\"Hexen II MP\",\t\t\t\t\t\tUPDATEURL(H2)},\n\t{\"-hexen2\",\t\t\"hexen2\",\t\"FTE-Hexen2\",\t\t\t{\"data1/pak0.pak\"},\t\t\t\tHEX2CFG,{\"data1\",\t\t\t\t\t\t\"*fteh2\"},\t\"Hexen II\",\t\t\t\t\t\t\tUPDATEURL(H2)},\n#endif\n\n#if defined(Q3CLIENT) || defined(Q3SERVER)\n\t{\"-quake3\",\t\t\"q3\",\t\t\"Quake3\",\t\t\t\t{\"baseq3/pak0.pk3\"},\t\t\tQ3CFG,\t{\"baseq3\",\t\t\t\t\t\t\"*fteq3\"},\t\"Quake III Arena\",\t\t\t\t\tUPDATEURL(Q3),\t\t\"fteplug_quake3\"},\n\t{\"-quake3demo\",\t\"q3demo\",\t\"Quake3Demo\",\t\t\t{\"demoq3/pak0.pk3\"},\t\t\tQ3CFG,\t{\"demoq3\",\t\t\t\t\t\t\"*fteq3\"},\t\"Quake III Arena Demo\",\t\t\t\tNULL,\t\t\t\t\"fteplug_quake3\"},\n\t//the rest are not supported in any real way. maps-only mostly, if that\n//\t{\"-quake4\",\t\t\"q4\",\t\t\"FTE-Quake4\",\t\t\t{\"q4base/pak00.pk4\"},\t\t\tNULL,\t{\"q4base\",\t\t\t\t\t\t\"*fteq4\"},\t\"Quake 4\"},\n//\t{\"-et\",\t\t\tNULL,\t\t\"FTE-EnemyTerritory\",\t{\"etmain/pak0.pk3\"},\t\t\tNULL,\t{\"etmain\",\t\t\t\t\t\t\"*fteet\"},\t\"Wolfenstein - Enemy Territory\"},\n\n//\t{\"-jk2\",\t\t\"jk2\",\t\t\"FTE-JK2\",\t\t\t\t{\"base/assets0.pk3\"},\t\t\tNULL,\t{\"base\",\t\t\t\t\t\t\"*ftejk2\"},\t\"Jedi Knight II: Jedi Outcast\"},\n//\t{\"-warsow\",\t\t\"warsow\",\t\"FTE-Warsow\",\t\t\t{\"basewsw/pak0.pk3\"},\t\t\tNULL,\t{\"basewsw\",\t\t\t\t\t\t\"*ftewsw\"},\t\"Warsow\"},\n\n\t{\"-cod4\",\t\tNULL,\t\t\"FTE-CoD4\",\t\t\t\t{\"cod4.ico\"},\t\t\t\t\tNULL,\t{\"main\",\t\t\t\t\t\t\"*ftecod\"},\t\"Call of Duty 4\",\t\t\t\tNULL,\t\t\t\t\"fteplug_cod\"},\n\t{\"-cod2\",\t\tNULL,\t\t\"FTE-CoD2\",\t\t\t\t{\"main/iw_00.iwd\"},\t\t\t\tNULL,\t{\"main\",\t\t\t\t\t\t\"*ftecod\"},\t\"Call of Duty 2\",\t\t\t\tNULL,\t\t\t\t\"fteplug_cod\"},\n\t{\"-cod\",\t\tNULL,\t\t\"FTE-CoD\",\t\t\t\t{\"Main/pak0.pk3\"},\t\t\t\tNULL,\t{\"Main\",\t\t\t\t\t\t\"*ftecod\"},\t\"Call of Duty\",\t\t\t\t\tNULL,\t\t\t\t\"fteplug_cod\"},\n#endif\n#if !defined(QUAKETC) && !defined(MINIMAL)\n//\t{\"-doom\",\t\t\"doom\",\t\t\"FTE-Doom\",\t\t\t\t{\"doom.wad\"},\t\t\t\t\tNULL,\t{\"*\",\t\t\t\t\t\t\t\"*ftedoom\"},\"Doom\"},\n//\t{\"-doom2\",\t\t\"doom2\",\t\"FTE-Doom2\",\t\t\t{\"doom2.wad\"},\t\t\t\t\tNULL,\t{\"*\",\t\t\t\t\t\t\t\"*ftedoom\"},\"Doom2\"},\n//\t{\"-doom3\",\t\t\"doom3\",\t\"FTE-Doom3\",\t\t\t{\"doom3.wad\"},\t\t\t\t\tNULL,\t{\"based3\",\t\t\t\t\t\t\"*ftedoom3\"},\"Doom3\"},\n\n\t//for the luls\n//\t{\"-diablo2\",\tNULL,\t\t\"FTE-Diablo2\",\t\t\t{\"d2music.mpq\"},\t\t\t\tNULL,\t{\"*\",\t\t\t\t\t\t\t\"*fted2\"},\t\"Diablo 2\"},\n#endif\n\t/* maintained by frag-net.com ~eukara */\n\t{\"-halflife\",\t\"halflife\",\t\"Rad-Therapy\",\t{\"valve/liblist.gam\"},\tHLCFG,\t{\"valve\"},\t\"Rad-Therapy\",\t\"https://www.frag-net.com/pkgs/halflife.txt\", \"valve-patch-radtherapy;fteplug_ffmpeg\"},\n\t{\"-gunman\",\t\"gunman\",\t\"Rad-Therapy\",\t\t{\"rewolf/liblist.gam\"},\tHLCFG,\t{\"rewolf\"},\t\"Gunman Chronicles\",\t\"https://www.gunmanchronicles.com/packages.txt\", \"rewolf-patch-gunman;fteplug_ffmpeg\"},\n\t{\"-halflife2\",\t\"halflife2\",\t\"Rad-Therapy-II\",\t{\"hl2/gameinfo.txt\"},\tHL2CFG,\t{\"hl2\", \"hl2mp\"},\t\"Rad-Therapy II\",\t\t\t\t\t\t\"https://www.frag-net.com/pkgs/halflife2.txt\", \"hl2-patch-radtherapy2;fteplug_ffmpeg;fteplug_ode;fteplug_hl2\"},\n\t{\"-gmod9\",\t\"halflife2\",\t\"Rad-Therapy-II\",\t{\"gmod9/gameinfo.txt\"},\tHL2CFG,\t{\"css\", \"hl2\", \"hl2mp\", \"gmod9\"},\t\"Free Will\",\t\t\"https://www.frag-net.com/pkgs/halflife2.txt\", \"hl2mp-mod-gmod9;fteplug_ffmpeg;fteplug_ode;fteplug_hl2\"},\n#endif\n\n\t{NULL}\n};\n\n\n\n\nvoid FS_BeginManifestUpdates(void);\nstatic void QDECL fs_game_callback(cvar_t *var, char *oldvalue);\nstatic void COM_InitHomedir(ftemanifest_t *man);\nhashtable_t filesystemhash;\nstatic qboolean com_fschanged = true, com_fsneedreload;\nqboolean com_installer = false;\nqboolean fs_readonly;\nstatic searchpath_t *fs_allowfileuri;\nint waitingformanifest;\nstatic unsigned int fs_restarts;\nvoid *fs_thread_mutex;\nfloat fs_accessed_time;\t//timestamp of read (does not include flocates, which should normally happen via a cache).\n\nstatic cvar_t fs_hidesyspaths\t\t= CVARFD\t(\"fs_hidesyspaths\", IFWEB(\"0\",\"1\"), 0, \"0: Show system paths in console prints that might appear in screenshots or video capture.\\n1: Replace the start of filenames in console prints with generic prefixes that cannot leak private info like your operating system's user name.\");\nstatic cvar_t com_fs_cache\t\t\t= CVARFD\t(\"fs_cache\", IFMINIMAL(\"2\",\"1\"), CVAR_ARCHIVE, \"0: Do individual lookups.\\n1: Scan all files for accelerated lookups. This provides a performance boost on windows and avoids case sensitivity issues on linux.\\n2: like 1, but don't bother checking for external changes (avoiding the cost of rebuild the cache).\");\nstatic cvar_t fs_noreexec\t\t\t= CVARD\t\t(\"fs_noreexec\", \"0\", \"Disables automatic re-execing configs on gamedir switches.\\nThis means your cvar defaults etc may be from the wrong mod, and cfg_save will leave that stuff corrupted!\");\nstatic cvar_t cfg_reload_on_gamedir = CVAR\t\t(\"cfg_reload_on_gamedir\", \"1\");\nstatic cvar_t fs_game\t\t\t\t= CVARAFCD\t(\"fs_game\"/*q3*/, \"\", \"game\"/*q2/qs*/, CVAR_NOSAVE|CVAR_NORESET, fs_game_callback, \"Provided for Q2 compat. Contains the subdir of the current mod.\");\nstatic cvar_t fs_gamepath\t\t\t= CVARAFD\t(\"fs_gamepath\"/*q3ish*/, \"\", \"fs_gamedir\"/*q2*/, CVAR_NOUNSAFEEXPAND|CVAR_NOSET|CVAR_NOSAVE, \"Provided for Q2/Q3 compat. System path of the active gamedir.\");\nstatic cvar_t fs_basepath\t\t\t= CVARAFD\t(\"fs_basepath\"/*q3*/,    \"\", \"fs_basedir\"/*q2*/, CVAR_NOUNSAFEEXPAND|CVAR_NOSET|CVAR_NOSAVE, \"Provided for Q2/Q3 compat. System path of the base directory.\");\nstatic cvar_t fs_homepath\t\t\t= CVARAFD\t(\"fs_homepath\"/*q3ish*/, \"\", \"fs_homedir\"/*q2ish*/, CVAR_NOUNSAFEEXPAND|CVAR_NOSET|CVAR_NOSAVE, \"Provided for Q2/Q3 compat. System path of the base directory.\");\nstatic cvar_t dpcompat_ignoremodificationtimes = CVARAFD(\"fs_packageprioritisation\", \"1\", \"dpcompat_ignoremodificationtimes\", CVAR_NOUNSAFEEXPAND|CVAR_NOSAVE, \"Favours the package that is:\\n0: Most recently modified\\n1: Is alphabetically last (favour z over a, 9 over 0).\");\ncvar_t\tfs_dlURL\t\t\t\t\t= CVARAFD(/*ioq3*/\"sv_dlURL\", \"\", /*dp*/\"sv_curl_defaulturl\", CVAR_SERVERINFO|IFWEB(CVAR_NOSAVE,CVAR_ARCHIVE), \"Provides clients with an external url from which they can obtain pk3s/packages from an external http server instead of having to download over udp.\");\ncvar_t  cl_download_mapsrc\t\t\t= CVARFD(\"cl_download_mapsrc\", \"\", CVAR_ARCHIVE, \"Specifies an http location prefix for map downloads. EG: \\\"http://example.com/path/gamemaps/\\\"\");\nint active_fs_cachetype;\nstatic int fs_referencetype;\nint fs_finds;\nvoid COM_CheckRegistered (void);\nvoid Mods_FlushModList(void);\nstatic void FS_ReloadPackFilesFlags(unsigned int reloadflags);\nstatic qboolean Sys_SteamHasFile(char *steambasedir,size_t steambasedirsize, char *steamdir, char *fname);\n\nstatic void QDECL fs_game_callback(cvar_t *var, char *oldvalue)\n{\n\tstatic qboolean runaway = false;\n\tchar buf[MAX_OSPATH];\n\tif (!strcmp(var->string, oldvalue))\n\t\treturn;\t//no change here.\n\tif (runaway)\n\t\treturn;\t//ignore that\n\trunaway = true;\n\tCmd_ExecuteString(va(\"gamedir %s\\n\", COM_QuotedString(var->string, buf, sizeof(buf), false)), RESTRICT_LOCAL);\n\trunaway = false;\n}\n\nstatic struct\n{\n\tvoid *module;\n\tconst char *extension;\n\tsearchpathfuncs_t *(QDECL *OpenNew)(vfsfile_t *file, searchpathfuncs_t *parent, const char *filename, const char *desc, const char *prefix);\n\tqboolean loadscan;\n} searchpathformats[64];\n\nint FS_RegisterFileSystemType(void *module, const char *extension, searchpathfuncs_t *(QDECL *OpenNew)(vfsfile_t *file, searchpathfuncs_t *parent, const char *filename, const char *desc, const char *prefix), qboolean loadscan)\n{\n\tunsigned int i;\n\tfor (i = 0; i < sizeof(searchpathformats)/sizeof(searchpathformats[0]); i++)\n\t{\n\t\tif (searchpathformats[i].extension && !strcmp(searchpathformats[i].extension, extension))\n\t\t\tbreak;\t//extension match always replaces\n\t\tif (!searchpathformats[i].extension && !searchpathformats[i].OpenNew)\n\t\t\tbreak;\n\t}\n\tif (i == sizeof(searchpathformats)/sizeof(searchpathformats[0]))\n\t\treturn 0;\n\n\tsearchpathformats[i].module = module;\n\tsearchpathformats[i].extension = extension;\n\tsearchpathformats[i].OpenNew = OpenNew;\n\tsearchpathformats[i].loadscan = loadscan;\n\tcom_fschanged = true;\n\tcom_fsneedreload = true;\n\n\treturn i+1;\n}\n\nvoid FS_UnRegisterFileSystemType(int idx)\n{\n\tif ((unsigned int)(idx-1) >= sizeof(searchpathformats)/sizeof(searchpathformats[0]))\n\t\treturn;\n\n\tsearchpathformats[idx-1].OpenNew = NULL;\n\tsearchpathformats[idx-1].module = NULL;\n\tcom_fschanged = true;\n\tcom_fsneedreload = true;\n\n\t//FS_Restart will be needed\n}\nvoid FS_UnRegisterFileSystemModule(void *module)\n{\n\tint i;\n\tqboolean found = false;\n\tif (!fs_thread_mutex || Sys_LockMutex(fs_thread_mutex))\n\t{\n\t\tfor (i = 0; i < sizeof(searchpathformats)/sizeof(searchpathformats[0]); i++)\n\t\t{\n\t\t\tif (searchpathformats[i].module == module)\n\t\t\t{\n\t\t\t\tsearchpathformats[i].extension = NULL;\n\t\t\t\tsearchpathformats[i].OpenNew = NULL;\n\t\t\t\tsearchpathformats[i].module = NULL;\n\t\t\t\tfound = true;\n\t\t\t}\n\t\t}\n\t\tif (fs_thread_mutex)\n\t\t{\n\t\t\tSys_UnlockMutex(fs_thread_mutex);\n\t\t\tif (found)\n\t\t\t{\n\t\t\t\tCmd_ExecuteString(\"fs_restart\", RESTRICT_LOCAL);\n\t\t\t}\n\t\t}\n\t}\n}\n\nchar *VFS_GETS(vfsfile_t *vf, char *buffer, size_t buflen)\n{\n\tchar in;\n\tchar *out = buffer;\n\tsize_t len;\n\tif (buflen <= 1)\n\t\treturn NULL;\n\tlen = buflen-1;\n\twhile (len > 0)\n\t{\n\t\tif (VFS_READ(vf, &in, 1) != 1)\n\t\t{\n\t\t\tif (len == buflen-1)\n\t\t\t\treturn NULL;\n\t\t\t*out = '\\0';\n\t\t\treturn buffer;\n\t\t}\n\t\tif (in == '\\n')\n\t\t\tbreak;\n\t\t*out++ = in;\n\t\tlen--;\n\t}\n\t*out = '\\0';\n\n\t//if there's a trailing \\r, strip it.\n\tif (out > buffer)\n\t\tif (out[-1] == '\\r')\n\t\t\tout[-1] = 0;\n\n\treturn buffer;\n}\n\nvoid VARGS VFS_PRINTF(vfsfile_t *vf, const char *format, ...)\n{\n\tva_list\t\targptr;\n\tchar\t\tstring[2048];\n\n\tva_start (argptr, format);\n\tvsnprintf (string,sizeof(string)-1, format,argptr);\n\tva_end (argptr);\n\n\tVFS_PUTS(vf, string);\n}\n\n\n\n#if defined(_WIN32) && !defined(FTE_SDL) && !defined(WINRT) && !defined(_XBOX)\n//windows has a special helper function to handle legacy URIs.\n#else\nqboolean Sys_ResolveFileURL(const char *inurl, int inlen, char *out, int outlen)\n{\n\tconst unsigned char *i = inurl, *inend = inurl+inlen;\n\tunsigned char *o = out, *outend = out+outlen;\n\tunsigned char hex;\n\n\t//make sure its a file url...\n\tif (inlen < 5 || strncmp(inurl, \"file:\", 5))\n\t\treturn false;\n\ti += 5;\n\n\tif (i+1 < inend && i[0] == '/' && i[1] == '/')\n\t{\t//has an authority field...\n\t\ti+=2;\n\t\t//except we don't support authorities other than ourself...\n\t\tif (i >= inend || *i != '/')\n\t\t\treturn false;\t//must be an absolute path...\n#ifdef _WIN32\n\t\ti++;\t//on windows, (full)absolute paths start with a drive name...\n#endif\n\t}\n\telse if (i < inend && i[0] == '/')\n\t\t;\t// file:/foo (no authority)\n\telse\n\t\treturn false;\n\n\t//everything else must be percent-encoded\n\twhile (i < inend)\n\t{\n\t\tif (!*i || o == outend)\n\t\t\treturn false;\t//don't allow nulls...\n\t\telse if (*i == '/' && i+1<inend && i[1] == '/')\n\t\t\treturn false;\t//two slashes is invalid (can be parent directory on some systems, or just buggy or weird)\n\t\telse if (*i == '\\\\')\n\t\t\treturn false;\t//don't allow backslashes. they're meant to be percent-encoded anyway.\n\t\telse if (*i == '%' && i+2<inend)\n\t\t{\n\t\t\thex = 0;\n\t\t\tif (i[1] >= 'A' && i[1] <= 'F')\n\t\t\t\thex += i[1]-'A'+10;\n\t\t\telse if (i[1] >= 'a' && i[1] <= 'f')\n\t\t\t\thex += i[1]-'a'+10;\n\t\t\telse if (i[1] >= '0' && i[1] <= '9')\n\t\t\t\thex += i[1]-'0';\n\t\t\telse\n\t\t\t{\n\t\t\t\t*o++ = *i++;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\thex <<= 4;\n\t\t\tif (i[2] >= 'A' && i[2] <= 'F')\n\t\t\t\thex += i[2]-'A'+10;\n\t\t\telse if (i[2] >= 'a' && i[2] <= 'f')\n\t\t\t\thex += i[2]-'a'+10;\n\t\t\telse if (i[2] >= '0' && i[2] <= '9')\n\t\t\t\thex += i[2]-'0';\n\t\t\telse\n\t\t\t{\n\t\t\t\t*o++ = *i++;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t*o++ = hex;\n\t\t\ti += 3;\n\t\t}\n\t\telse\n\t\t\t*o++ = *i++;\n\t}\n\n\tif (o == outend)\n\t\treturn false;\n\t*o = 0;\n\n\treturn true;\n}\n#endif\n\n\n\n\nchar\tgamedirfile[MAX_OSPATH];\nstatic char\tpubgamedirfile[MAX_OSPATH];\t//like gamedirfile, but not set to the fte-only paths\n\n\nstatic searchpath_t *gameonly_homedir;\nstatic searchpath_t *gameonly_gamedir;\n\nchar\tcom_gamepath[MAX_OSPATH];\t//c:\\games\\quake\nchar\tcom_homepath[MAX_OSPATH];\t//c:\\users\\foo\\my docs\\fte\\quake\nqboolean\tcom_homepathenabled;\nstatic qboolean\tcom_homepathusable;\t//com_homepath is safe, even if not enabled.\n\n//char\tcom_configdir[MAX_OSPATH];\t//homedir/fte/configs\n\nint fs_hash_dups;\nint fs_hash_files;\n\n\n\n\n\n\n\nstatic const char *FS_GetCleanPath(const char *pattern, qboolean silent, char *outbuf, int outlen);\nstatic void FS_RegisterDefaultFileSystems(void);\nstatic void\tCOM_CreatePath (char *path);\nstatic ftemanifest_t *FS_ReadDefaultManifest(char *newbasedir, size_t newbasedirsize, qboolean fixedbasedir);\n\n#define ENFORCEFOPENMODE(mode) {if (strcmp(mode, \"r\") && strcmp(mode, \"w\")/* && strcmp(mode, \"rw\")*/)Sys_Error(\"fs mode %s is not permitted here\\n\");}\n\n\n\n\n\n\n//forget a manifest entirely.\nvoid FS_Manifest_Free(ftemanifest_t *man)\n{\n\tint i, j;\n\tif (!man)\n\t\treturn;\n\tZ_Free(man->filename);\n\tZ_Free(man->updateurl);\n\tZ_Free(man->installation);\n\tZ_Free(man->formalname);\n#ifdef PACKAGEMANAGER\n\tZ_Free(man->downloadsurl);\n\tZ_Free(man->installupd);\n#endif\n\tZ_Free(man->mainconfig);\n\tZ_Free(man->schemes);\n\tZ_Free(man->protocolname);\n\tZ_Free(man->eula);\n\tZ_Free(man->defaultexec);\n\tZ_Free(man->defaultoverrides);\n\tZ_Free(man->basedir);\n\tZ_Free(man->iconname);\n\tfor (i = 0; i < sizeof(man->gamepath) / sizeof(man->gamepath[0]); i++)\n\t{\n\t\tZ_Free(man->gamepath[i].path);\n\t}\n\tfor (i = 0; i < sizeof(man->package) / sizeof(man->package[0]); i++)\n\t{\n\t\tZ_Free(man->package[i].packagename);\n\t\tZ_Free(man->package[i].path);\n\t\tZ_Free(man->package[i].prefix);\n\t\tZ_Free(man->package[i].condition);\n\t\tZ_Free(man->package[i].sha512);\n\t\tZ_Free(man->package[i].signature);\n\t\tfor (j = 0; j < sizeof(man->package[i].mirrors) / sizeof(man->package[i].mirrors[0]); j++)\n\t\t\tZ_Free(man->package[i].mirrors[j]);\n\t}\n\tZ_Free(man);\n}\n\n//clone a manifest, so we can hack at it.\nstatic ftemanifest_t *FS_Manifest_Clone(ftemanifest_t *oldm)\n{\n\tftemanifest_t *newm;\n\tint i, j;\n\tnewm = Z_Malloc(sizeof(*newm));\n\tif (oldm->updateurl)\n\t\tnewm->updateurl = Z_StrDup(oldm->updateurl);\n\tif (oldm->installation)\n\t\tnewm->installation = Z_StrDup(oldm->installation);\n\tif (oldm->formalname)\n\t\tnewm->formalname = Z_StrDup(oldm->formalname);\n#ifdef PACKAGEMANAGER\n\tif (oldm->downloadsurl)\n\t\tnewm->downloadsurl = Z_StrDup(oldm->downloadsurl);\n\tif (oldm->installupd)\n\t\tnewm->installupd = Z_StrDup(oldm->installupd);\n#endif\n\tif (oldm->schemes)\n\t\tnewm->schemes = Z_StrDup(oldm->schemes);\n\tif (oldm->protocolname)\n\t\tnewm->protocolname = Z_StrDup(oldm->protocolname);\n\tif (oldm->eula)\n\t\tnewm->eula = Z_StrDup(oldm->eula);\n\tif (oldm->defaultexec)\n\t\tnewm->defaultexec = Z_StrDup(oldm->defaultexec);\n\tif (oldm->defaultoverrides)\n\t\tnewm->defaultoverrides = Z_StrDup(oldm->defaultoverrides);\n\tif (oldm->iconname)\n\t\tnewm->iconname = Z_StrDup(oldm->iconname);\n\tif (oldm->basedir)\n\t\tnewm->basedir = Z_StrDup(oldm->basedir);\n\tif (oldm->mainconfig)\n\t\tnewm->mainconfig = Z_StrDup(oldm->mainconfig);\n\tnewm->homedirtype = oldm->homedirtype;\n\n\tfor (i = 0; i < sizeof(newm->gamepath) / sizeof(newm->gamepath[0]); i++)\n\t{\n\t\tif (oldm->gamepath[i].path)\n\t\t\tnewm->gamepath[i].path = Z_StrDup(oldm->gamepath[i].path);\n\t\tnewm->gamepath[i].flags = oldm->gamepath[i].flags;\n\t}\n\tfor (i = 0; i < sizeof(newm->package) / sizeof(newm->package[0]); i++)\n\t{\n\t\tnewm->package[i].type = oldm->package[i].type;\n\t\tnewm->package[i].crc = oldm->package[i].crc;\n\t\tnewm->package[i].crcknown = oldm->package[i].crcknown;\n\t\tif (oldm->package[i].packagename)\n\t\t\tnewm->package[i].packagename = Z_StrDup(oldm->package[i].packagename);\n\t\tif (oldm->package[i].path)\n\t\t\tnewm->package[i].path = Z_StrDup(oldm->package[i].path);\n\t\tif (oldm->package[i].prefix)\n\t\t\tnewm->package[i].prefix = Z_StrDup(oldm->package[i].prefix);\n\t\tif (oldm->package[i].condition)\n\t\t\tnewm->package[i].condition = Z_StrDup(oldm->package[i].condition);\n\t\tif (oldm->package[i].sha512)\n\t\t\tnewm->package[i].sha512 = Z_StrDup(oldm->package[i].sha512);\n\t\tif (oldm->package[i].signature)\n\t\t\tnewm->package[i].signature = Z_StrDup(oldm->package[i].signature);\n\t\tnewm->package[i].filesize = oldm->package[i].filesize;\n\t\tfor (j = 0; j < sizeof(newm->package[i].mirrors) / sizeof(newm->package[i].mirrors[0]); j++)\n\t\t\tif (oldm->package[i].mirrors[j])\n\t\t\t\tnewm->package[i].mirrors[j] = Z_StrDup(oldm->package[i].mirrors[j]);\n\t}\n\n\tnewm->security = oldm->security;\n\n\treturn newm;\n}\n\nstatic void FS_Manifest_Print(ftemanifest_t *man)\n{\n\tchar buffer[65536];\n\tint i, j;\n\tif (man->updateurl)\n\t\tCon_Printf(\"updateurl %s\\n\", COM_QuotedString(man->updateurl, buffer, sizeof(buffer), false));\n\tif (man->eula)\n\t\tCon_Printf(\"eula %s\\n\", COM_QuotedString(man->eula, buffer, sizeof(buffer), false));\n\tif (man->installation)\n\t\tCon_Printf(\"game %s\\n\", COM_QuotedString(man->installation, buffer, sizeof(buffer), false));\n\tif (man->formalname)\n\t\tCon_Printf(\"name %s\\n\", COM_QuotedString(man->formalname, buffer, sizeof(buffer), false));\n\tif (man->mainconfig)\n\t\tCon_Printf(\"mainconfig %s\\n\", COM_QuotedString(man->mainconfig, buffer, sizeof(buffer), false));\n#ifdef PACKAGEMANAGER\n\tif (man->downloadsurl)\n\t\tCon_Printf(\"downloadsurl %s\\n\", COM_QuotedString(man->downloadsurl, buffer, sizeof(buffer), false));\n\tif (man->installupd)\n\t\tCon_Printf(\"install %s\\n\", COM_QuotedString(man->installupd, buffer, sizeof(buffer), false));\n#endif\n\tif (man->schemes)\n\t\tCon_Printf(\"schemes %s\\n\", COM_QuotedString(man->schemes, buffer, sizeof(buffer), false));\n\tif (man->protocolname)\n\t\tCon_Printf(\"protocolname %s\\n\", COM_QuotedString(man->protocolname, buffer, sizeof(buffer), false));\n\tif (man->defaultexec)\n\t{\n\t\tchar *s = buffer, *e;\n\t\tfor (s = man->defaultexec; *s; s = e)\n\t\t{\n\t\t\te = strchr(s, '\\n');\n\t\t\tif (e)\n\t\t\t{\n\t\t\t\t*e = 0;\n\t\t\t\tCon_Printf(\"-%s\\n\", s);\n\t\t\t\t*e++ = '\\n';\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tCon_Printf(\"-%s\\n\", s);\n\t\t\t\te = s+strlen(s);\n\t\t\t}\n\t\t}\n\t\t//Con_Printf(\"defaultexec %s\\n\", COM_QuotedString(man->defaultexec, buffer, sizeof(buffer), false));\n\t}\n\tif (man->defaultoverrides)\n\t{\n\t\tchar *s = buffer, *e;\n\t\tfor (s = man->defaultoverrides; *s; s = e)\n\t\t{\n\t\t\te = strchr(s, '\\n');\n\t\t\tif (e)\n\t\t\t{\n\t\t\t\t*e = 0;\n\t\t\t\tCon_Printf(\"+%s\\n\", s);\n\t\t\t\t*e++ = '\\n';\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tCon_Printf(\"+%s\\n\", s);\n\t\t\t\te = s+strlen(s);\n\t\t\t}\n\t\t}\n\t\t//Con_Printf(\"%s\", man->defaultoverrides);\n\t}\n\tif (man->iconname)\n\t\tCon_Printf(\"icon %s\\n\", COM_QuotedString(man->iconname, buffer, sizeof(buffer), false));\n\tif (man->basedir)\n\t\tCon_Printf(\"basedir %s\\n\", COM_QuotedString(man->basedir, buffer, sizeof(buffer), false));\n\n\tfor (i = 0; i < sizeof(man->gamepath) / sizeof(man->gamepath[0]); i++)\n\t{\n\t\tif (man->gamepath[i].path)\n\t\t{\n\t\t\tchar *str = va(\"%s%s%s\",\n\t\t\t\t(man->gamepath[i].flags & GAMEDIR_QSHACK)?\"/\":\"\",\n\t\t\t\t(man->gamepath[i].flags & GAMEDIR_PRIVATE)?\"*\":\"\",\n\t\t\t\tman->gamepath[i].path);\n\n\t\t\tif (man->gamepath[i].flags & GAMEDIR_BASEGAME)\n\t\t\t\tCon_Printf(\"basegame %s\\n\", COM_QuotedString(str, buffer, sizeof(buffer), false));\n\t\t\telse\n\t\t\t\tCon_Printf(\"gamedir %s\\n\", COM_QuotedString(str, buffer, sizeof(buffer), false));\n\t\t}\n\t}\n\n\tfor (i = 0; i < sizeof(man->package) / sizeof(man->package[0]); i++)\n\t{\n\t\tif (man->package[i].path)\n\t\t{\n\t\t\tif (man->package[i].type == mdt_installation)\n\t\t\t\tCon_Printf(\"library \");\n\t\t\telse\n\t\t\t\tCon_Printf(\"package \");\n\t\t\tCon_Printf(\"%s\", COM_QuotedString(man->package[i].path, buffer, sizeof(buffer), false));\n\t\t\tif (man->package[i].prefix)\n\t\t\t\tCon_Printf(\" name %s\", COM_QuotedString(man->package[i].packagename, buffer, sizeof(buffer), false));\n\t\t\tif (man->package[i].prefix)\n\t\t\t\tCon_Printf(\" prefix %s\", COM_QuotedString(man->package[i].prefix, buffer, sizeof(buffer), false));\n\t\t\tif (man->package[i].condition)\n\t\t\t\tCon_Printf(\" condition %s\", COM_QuotedString(man->package[i].condition, buffer, sizeof(buffer), false));\n\t\t\tif (man->package[i].filesize)\n\t\t\t\tCon_Printf(\" filesize %\"PRIuQOFS, man->package[i].filesize);\n\t\t\tif (man->package[i].sha512)\n\t\t\t\tCon_Printf(\" sha512 %s\", COM_QuotedString(man->package[i].sha512, buffer, sizeof(buffer), false));\n\t\t\tif (man->package[i].signature)\n\t\t\t\tCon_Printf(\" signature %s\", COM_QuotedString(man->package[i].signature, buffer, sizeof(buffer), false));\n\t\t\tif (man->package[i].crcknown)\n\t\t\t\tCon_Printf(\" crc 0x%x\", man->package[i].crc);\n\t\t\tfor (j = 0; j < sizeof(man->package[i].mirrors) / sizeof(man->package[i].mirrors[0]); j++)\n\t\t\t\tif (man->package[i].mirrors[j])\n\t\t\t\t\tCon_Printf(\" %s\", COM_QuotedString(man->package[i].mirrors[j], buffer, sizeof(buffer), false));\n\t\t\tCon_Printf(\"\\n\");\n\t\t}\n\t}\n}\n\n//forget any mod dirs.\nstatic void FS_Manifest_PurgeGamedirs(ftemanifest_t *man)\n{\n\tint i;\n\tif (man->filename)\n\t\tZ_Free(man->filename);\n\tman->filename = NULL;\n\n\tfor (i = 0; i < sizeof(man->gamepath) / sizeof(man->gamepath[0]); i++)\n\t{\n\t\tif (man->gamepath[i].path && !(man->gamepath[i].flags&GAMEDIR_BASEGAME))\n\t\t{\n\t\t\tZ_Free(man->gamepath[i].path);\n\t\t\tman->gamepath[i].path = NULL;\n\n\t\t\t//FIXME: remove packages from the removed paths.\n\t\t}\n\t}\n}\n\n//create a new empty manifest with default values.\nstatic ftemanifest_t *FS_Manifest_Create(const char *syspath, const char *basedir)\n{\n\tftemanifest_t *man = Z_Malloc(sizeof(*man));\n\n\tif (syspath)\n\t{\n\t\tchar base[MAX_QPATH];\n\t\tCOM_FileBase(syspath, base, sizeof(base));\n\t\tif (*base && Q_strcasecmp(base, \"default\"))\n\t\t\tman->formalname = Z_StrDup(base);\n\t}\n\n#ifdef _DEBUG\t//FOR TEMPORARY TESTING ONLY.\n//\tman->doinstall = true;\n#endif\n\n\tif (syspath)\n\t\tman->filename = Z_StrDup(syspath);\t//this should be a system path.\n\tif (basedir)\n\t\tman->basedir = Z_StrDup(basedir);\t//this should be a system path.\n\n#ifdef QUAKETC\n\tman->mainconfig = Z_StrDup(\"config.cfg\");\n#else\n\tman->mainconfig = Z_StrDup(\"fte.cfg\");\n#endif\n\treturn man;\n}\n\nstatic qboolean FS_Manifest_ParsePackage(ftemanifest_t *man, int packagetype)\n{\n\t//CMD [deparch] packagename qhash [archivedfilename] [prefix skip/this/] [mirror url] [[filesize foo] [sha512 hash] [signature \"base64\"]]\n\tchar *path = \"\";\n\tunsigned int crc = 0;\n\tqboolean crcknown = false;\n\tchar *packagename = NULL;\n\tchar *legacyextractname = NULL;\n\tchar *condition = NULL;\n\tchar *prefix = NULL;\n\tchar *arch = NULL;\n\tchar *signature = NULL;\n\tchar *sha512 = NULL;\n\tqofs_t filesize = 0;\n\tunsigned int arg = 1;\n\tunsigned int mirrors = 0;\n\tchar *mirror[countof(man->package[0].mirrors)];\n\tsize_t i, j;\n\tchar *a;\n\n\ta = Cmd_Argv(0);\n\tif (!Q_strcasecmp(a, \"managedpackage\"))\n\t\t;\n\telse\n\t{\n\t\tif (!Q_strcasecmp(a, \"filedependancies\") || !Q_strcasecmp(a, \"archiveddependancies\"))\n\t\t\tarch = Cmd_Argv(arg++);\n\n\t\tpath = Cmd_Argv(arg++);\n\n#ifdef HAVE_LEGACY\n\t\ta = Cmd_Argv(arg);\n\t\tif (!strcmp(a, \"-\"))\n\t\t{\n\t\t\targ++;\n\t\t}\n\t\telse if (*a)\n\t\t{\n\t\t\tcrc = strtoul(a, &a, 0);\n\t\t\tif (!*a)\n\t\t\t{\n\t\t\t\tcrcknown = true;\n\t\t\t\targ++;\n\t\t\t}\n\t\t}\n\n\t\tif (!strncmp(Cmd_Argv(0), \"archived\", 8))\n\t\t\tlegacyextractname = Cmd_Argv(arg++);\n#endif\n\t}\n\n\twhile (arg < Cmd_Argc())\n\t{\n\t\ta = Cmd_Argv(arg++);\n\t\tif (!strcmp(a, \"crc\"))\n\t\t{\n\t\t\tcrcknown = true;\n\t\t\tcrc = strtoul(Cmd_Argv(arg++), NULL, 0);\n\t\t}\n\t\telse if (!strcmp(a, \"condition\"))\n\t\t\tcondition = Cmd_Argv(arg++);\n\t\telse if (!strcmp(a, \"prefix\"))\n\t\t\tprefix = Cmd_Argv(arg++);\n\t\telse if (!strcmp(a, \"arch\"))\n\t\t\tarch = Cmd_Argv(arg++);\n\t\telse if (!strcmp(a, \"signature\"))\n\t\t\tsignature = Cmd_Argv(arg++);\n\t\telse if (!strcmp(a, \"sha512\"))\n\t\t\tsha512 = Cmd_Argv(arg++);\n\t\telse if (!strcmp(a, \"name\"))\n\t\t\tpackagename = Cmd_Argv(arg++);\n\t\telse if (!strcmp(a, \"filesize\")||!strcmp(a, \"size\"))\n\t\t\tfilesize = strtoull(Cmd_Argv(arg++), NULL, 0);\n\t\telse if (!strcmp(a, \"mirror\"))\n\t\t{\n\t\t\ta = Cmd_Argv(arg++);\n\t\t\tgoto mirror;\t//oo evil.\n\t\t}\n\t\telse if (strchr(a, ':') || man->parsever < 1)\n\t\t{\nmirror:\n\t\t\tif (mirrors == countof(mirror))\n\t\t\t\tCon_Printf(CON_WARNING\"too many mirrors for package %s\\n\", path);\n\t\t\telse if (legacyextractname)\n\t\t\t{\n\t\t\t\tif (!strcmp(legacyextractname, \"xz\") || !strcmp(legacyextractname, \"gz\"))\n\t\t\t\t\tmirror[mirrors++] = Z_StrDupf(\"%s:%s\", legacyextractname, a);\n\t\t\t\telse\n\t\t\t\t\tmirror[mirrors++] = Z_StrDupf(\"unzip:%s,%s\", legacyextractname, a);\n\t\t\t}\n\t\t\telse\n\t\t\t\tmirror[mirrors++] = Z_StrDup(a);\n\t\t}\n\t\telse if (man->parsever <= MANIFEST_CURRENTVER)\n\t\t\tCon_Printf(CON_WARNING\"unknown mirror / property %s for package %s\\n\", a, path);\n\t}\n\n\tfor (i = 0; i < countof(man->package); i++)\n\t{\n\t\tif (!man->package[i].path)\n\t\t{\n\t\t\tif (packagetype == mdt_singlepackage && (!strchr(path, '/') || strchr(path, ':') || strchr(path, '\\\\')))\n\t\t\t{\n\t\t\t\tCon_Printf(CON_WARNING\"invalid package path specified in manifest (%s)\\n\", path);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (arch)\n\t\t\t{\n#ifdef PLATFORM\n\t\t\t\tif (Q_strcasecmp(PLATFORM, arch)\n\t\t\t\t\t&& Q_strcasecmp(PLATFORM\"_\"ARCH_CPU_POSTFIX, arch)\n#ifdef ARCH_ALTCPU_POSTFIX\n\t\t\t\t\t&& Q_strcasecmp(PLATFORM\"_\"ARCH_ALTCPU_POSTFIX, arch)\n#endif\n\t\t\t\t\t)\n#endif\n\t\t\t\t{\n\t\t\t\t\tCon_Printf(CON_WARNING\"package archetecture does not match %s\\n\", PLATFORM\"_\"ARCH_CPU_POSTFIX);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tman->package[i].packagename = packagename?Z_StrDup(packagename):NULL;\n\t\t\tman->package[i].type = packagetype;\n\t\t\tman->package[i].path = Z_StrDup(path);\n\t\t\tman->package[i].prefix = prefix?Z_StrDup(prefix):NULL;\n\t\t\tman->package[i].condition = condition?Z_StrDup(condition):NULL;\n\t\t\tman->package[i].sha512 = sha512?Z_StrDup(sha512):NULL;\n\t\t\tman->package[i].signature = signature?Z_StrDup(signature):NULL;\n\t\t\tman->package[i].filesize = filesize;\n\t\t\tman->package[i].crcknown = crcknown;\n\t\t\tman->package[i].crc = crc;\n\t\t\tfor (j = 0; j < mirrors; j++)\n\t\t\t\tman->package[i].mirrors[j] = mirror[j];\n\t\t\treturn true;\n\t\t}\n\t}\n\tif (i == countof(man->package))\n\t\tCon_Printf(CON_WARNING\"Too many packages specified in manifest\\n\");\n\tfor (j = 0; j < mirrors; j++)\n\t\tZ_Free(mirror[j]);\n\treturn false;\n}\n\nstatic qboolean FS_GamedirIsOkay(const char *path, qboolean verbose)\n{\n\tchar tmp[MAX_QPATH];\n\tif (!*path || strchr(path, '\\n') || strchr(path, '\\r') || !strcmp(path, \".\") || !strcmp(path, \"..\") || strchr(path, ':') || strchr(path, '/') || strchr(path, '\\\\') || strchr(path, '$'))\n\t{\n\t\tif (verbose)\n\t\t\tCon_Printf(CON_WARNING\"Illegal path specified: %s\\n\", path);\n\t\treturn false;\n\t}\n\n\t//don't allow leading dots, hidden files are evil.\n\t//don't allow complex paths. those are evil too.\n\tif (!*path || *path == '.' || !strcmp(path, \".\") || strstr(path, \"..\") || strstr(path, \"/\")\n\t\t|| strstr(path, \"\\\\\") || strstr(path, \":\") || strstr(path, \"\\\"\"))\n\t{\n\t\tif (verbose)\n\t\t\tCon_Printf (\"Gamedir should be a single filename, not \\\"%s\\\"\\n\", path);\n\t\treturn false;\n\t}\n\n\t//some gamedirs should never be used for actual games/mods. Reject them.\n\tif (!Q_strncasecmp(path, \"downloads\", 9) ||\t//QI stuff uses this for arbitrary downloads. it doesn't make sense as a gamedir.\n\t\t!Q_strncasecmp(path, \"docs\", 4) ||\t\t//don't pollute this\n\t\t!Q_strncasecmp(path, \"help\", 4) ||\t\t//don't pollute this\n\t\t!Q_strncasecmp(path, \"bin\", 3) ||\t\t//if scripts try executing stuff here then we want to make extra sure that we don't allow writing anything within it.\n\t\t!Q_strncasecmp(path, \"lib\", 3))\t\t\t//same deal\n\t{\n\t\tif (verbose)\n\t\t\tCon_Printf (\"Gamedir should not be \\\"%s\\\"\\n\", path);\n\t\treturn false;\n\t}\n\n\t//this checks for system-specific entries.\n\tif (!FS_GetCleanPath(path, true, tmp, sizeof(tmp)))\n\t{\n\t\tif (verbose)\n\t\t\tCon_Printf (\"Gamedir should not be \\\"%s\\\"\\n\", path);\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\n//parse Cmd_Argv tokens into the manifest.\nstatic qboolean FS_Manifest_ParseTokens(ftemanifest_t *man)\n{\n\tqboolean result = true;\n\tchar *cmd = Cmd_Argv(0);\n\tif (!*cmd)\n\t\treturn result;\n\n\tif (*cmd == '*')\n\t\tcmd++;\n\n\tif (!Q_strcasecmp(cmd, \"ftemanifestver\") || !Q_strcasecmp(cmd, \"ftemanifest\"))\n\t\tman->parsever = atoi(Cmd_Argv(1));\n\telse if (!Q_strcasecmp(cmd, \"minver\"))\n\t{\n\t\t//ignore minimum versions for other engines.\n\t\tif (!strcmp(Cmd_Argv(2), DISTRIBUTION))\n\t\t\tman->minver = atoi(Cmd_Argv(3));\n\t}\n\telse if (!Q_strcasecmp(cmd, \"maxver\"))\n\t{\n\t\t//ignore minimum versions for other engines.\n\t\tif (!strcmp(Cmd_Argv(2), DISTRIBUTION))\n\t\t\tman->maxver = atoi(Cmd_Argv(3));\n\t}\n\telse if (!Q_strcasecmp(cmd, \"game\"))\n\t{\n\t\tZ_Free(man->installation);\n\t\tman->installation = Z_StrDup(Cmd_Argv(1));\n\t}\n\telse if (!Q_strcasecmp(cmd, \"name\"))\n\t{\n\t\tZ_Free(man->formalname);\n\t\tman->formalname = Z_StrDup(Cmd_Argv(1));\n\t}\n\telse if (!Q_strcasecmp(cmd, \"eula\"))\n\t{\n\t\tZ_Free(man->eula);\n\t\tman->eula = Z_StrDup(Cmd_Argv(1));\n\t}\n#ifdef PACKAGEMANAGER\n\telse if (!Q_strcasecmp(cmd, \"downloadsurl\"))\n\t{\n\t\tif (man->downloadsurl)\n\t\t\tZ_StrCat(&man->downloadsurl, \" \");\n\t\tZ_StrCat(&man->downloadsurl, Cmd_Argv(1));\n\t}\n\telse if (!Q_strcasecmp(cmd, \"install\"))\n\t{\n\t\tif (man->installupd)\n\t\t\tZ_StrCat(&man->installupd, va(\";%s\", Cmd_Argv(1)));\n\t\telse\n\t\t\tman->installupd = Z_StrDup(Cmd_Argv(1));\n\t}\n#endif\n\telse if (!Q_strcasecmp(cmd, \"schemes\"))\n\t{\n\t\tint i;\n\t\tZ_Free(man->schemes);\n\t\tman->schemes = Z_StrDup(Cmd_Argv(1));\n\t\tfor (i = 2; i < Cmd_Argc(); i++)\n\t\t\tZ_StrCat(&man->schemes, va(\" %s\", Cmd_Argv(i)));\n\t}\n\telse if (!Q_strcasecmp(cmd, \"protocolname\"))\n\t{\n\t\tZ_Free(man->protocolname);\n\t\tman->protocolname = Z_StrDup(Cmd_Argv(1));\n\t}\n\telse if (!Q_strcasecmp(cmd, \"mainconfig\"))\n\t{\n\t\tZ_Free(man->mainconfig);\n\t\tif (strcmp(\".cfg\", COM_GetFileExtension(Cmd_Argv(1),NULL)))\n\t\t\tman->mainconfig = Z_StrDupf(\"%s.cfg\", Cmd_Argv(1));\n\t\telse\n\t\t\tman->mainconfig = Z_StrDup(Cmd_Argv(1));\n\t}\n\telse if (!Q_strcasecmp(cmd, \"defaultexec\"))\n\t{\n\t\tZ_Free(man->defaultexec);\n\t\tman->defaultexec = Z_StrDup(Cmd_Argv(1));\n\t}\n\telse if (!Q_strcasecmp(cmd, \"-bind\") || !Q_strcasecmp(cmd, \"-set\") || !Q_strcasecmp(cmd, \"-seta\") || !Q_strcasecmp(cmd, \"-alias\") || !Q_strncasecmp(cmd, \"-\", 1))\n\t{\n\t\tZ_StrCat(&man->defaultexec, va(\"%s %s\\n\", Cmd_Argv(0)+1, Cmd_Args()));\n\t}\n\telse if (!Q_strcasecmp(cmd, \"bind\") || !Q_strcasecmp(cmd, \"set\") || !Q_strcasecmp(cmd, \"seta\") || !Q_strcasecmp(cmd, \"alias\") || !Q_strncasecmp(cmd, \"+\", 1))\n\t{\n\t\tZ_StrCat(&man->defaultoverrides, va(\"%s %s\\n\", Cmd_Argv(0), Cmd_Args()));\n\t}\n#ifdef HAVE_LEGACY\n\telse if (!Q_strcasecmp(cmd, \"rtcbroker\"))\n\t{\n\t\tZ_StrCat(&man->defaultexec, va(\"set %s %s\\n\", net_ice_broker.name, Cmd_Args()));\n\t}\n#endif\n\telse if (!Q_strcasecmp(cmd, \"updateurl\"))\n\t{\n\t\tZ_Free(man->updateurl);\n\t\tman->updateurl = Z_StrDup(Cmd_Argv(1));\n\t}\n\telse if (!Q_strcasecmp(cmd, \"icon\"))\t//relative path to an icon image (typically png)\n\t{\n\t\tZ_Free(man->iconname);\n\t\tman->iconname = Z_StrDup(Cmd_Argv(1));\n\t}\n\telse if (!Q_strcasecmp(cmd, \"disablehomedir\") || !Q_strcasecmp(cmd, \"homedirmode\"))\n\t{\n\t\tchar *arg = Cmd_Argv(1);\n\t\tif (!Q_strcasecmp(arg, \"auto\"))\n\t\t\tman->homedirtype = MANIFEST_HOMEDIRWHENREADONLY;\n\t\telse if (!*arg || atoi(arg) || !Q_strcasecmp(arg, \"never\"))\n\t\t\tman->homedirtype = MANIFEST_NOHOMEDIR;\n\t\telse if (!atoi(arg) || !Q_strcasecmp(arg, \"force\") || !Q_strcasecmp(arg, \"always\"))\n\t\t\tman->homedirtype = MANIFEST_FORCEHOMEDIR;\n\t}\n\telse if (!Q_strcasecmp(cmd, \"basegame\") || !Q_strcasecmp(cmd, \"gamedir\"))\n\t{\n\t\tint i;\n\t\tchar *newdir = Cmd_Argv(1);\n\t\tqboolean basegame = !Q_strcasecmp(cmd, \"basegame\");\n\n\t\tfor (i = 0; i < sizeof(man->gamepath) / sizeof(man->gamepath[0]); i++)\n\t\t{\n\t\t\tif (man->gamepath[i].path)\n\t\t\t{\n\t\t\t\tif (!Q_strcasecmp(man->gamepath[i].path, newdir))\n\t\t\t\t{\n\t\t\t\t\tif (basegame && !(man->gamepath[i].flags & GAMEDIR_BASEGAME))\n\t\t\t\t\t{\n\t\t\t\t\t\tZ_Free(man->gamepath[i].path);\n\t\t\t\t\t\tman->gamepath[i].path = NULL;\t//if we're adding a basegame when there's a mod game with the same name then drop the redundant mod name\n\t\t\t\t\t\tman->gamepath[i].flags = 0;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\treturn true;\t//already in there, don't add a conflicting one.\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tfor (i = 0; i < sizeof(man->gamepath) / sizeof(man->gamepath[0]); i++)\n\t\t{\n\t\t\tif (!man->gamepath[i].path)\n\t\t\t{\n\t\t\t\tman->gamepath[i].flags = GAMEDIR_DEFAULTFLAGS;\n\t\t\t\tif (!Q_strcasecmp(cmd, \"basegame\"))\n\t\t\t\t\tman->gamepath[i].flags |= GAMEDIR_BASEGAME;\n\n\t\t\t\tif (*newdir == '/')\n\t\t\t\t{\n\t\t\t\t\tnewdir++;\n\t\t\t\t\tman->gamepath[i].flags |= GAMEDIR_QSHACK;\n\t\t\t\t}\n\t\t\t\tif (*newdir == '*')\n\t\t\t\t{\t//*dir makes the dir 'private' and not networked.\n\t\t\t\t\tnewdir++;\n\t\t\t\t\tman->gamepath[i].flags |= GAMEDIR_PRIVATE;\n\t\t\t\t\tif (!*newdir)\n\t\t\t\t\t{\t//a single asterisk means load packages from the basedir (but not other files). This is for doom compat.\n\t\t\t\t\t\tman->gamepath[i].flags |= GAMEDIR_USEBASEDIR;\n\t\t\t\t\t\tman->gamepath[i].flags |= GAMEDIR_READONLY;\t//must also be readonly, just in case.\n\t\t\t\t\t\tman->gamepath[i].path = Z_StrDup(newdir);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (!strncmp(newdir, \"steam:\", 6))\n\t\t\t\t{\t//\"steam:Subdir/gamedir\"\n\t\t\t\t\tchar *sl = strchr(newdir+6, '/');\n\t\t\t\t\tif (!sl)\n\t\t\t\t\t\tbreak;\t//malformed steam link\n\t\t\t\t\tman->gamepath[i].flags |= GAMEDIR_PRIVATE|GAMEDIR_STEAMGAME;\n\t\t\t\t\t*sl = 0;\n\t\t\t\t\tif (!FS_GamedirIsOkay(sl+1, true))\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t*sl = '/';\n\t\t\t\t\tman->gamepath[i].path = Z_StrDup(newdir);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tif (!FS_GamedirIsOkay(newdir, true))\n\t\t\t\t\tbreak;\n\t\t\t\tman->gamepath[i].path = Z_StrDup(newdir);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (i == sizeof(man->gamepath) / sizeof(man->gamepath[0]))\n\t\t{\n\t\t\tCon_Printf(\"Too many game paths specified in manifest\\n\");\n\t\t}\n\t}\n\t//FIXME: these should generate package-manager entries.\n#ifdef HAVE_LEGACY\n\telse if (!Q_strcasecmp(cmd, \"filedependancies\") || !Q_strcasecmp(cmd, \"archiveddependancies\"))\n\t\tFS_Manifest_ParsePackage(man, mdt_installation);\n\telse if (!Q_strcasecmp(cmd, \"archivedpackage\"))\n\t\tFS_Manifest_ParsePackage(man, mdt_singlepackage);\n#endif\n\telse if (!Q_strcasecmp(cmd, \"library\"))\n\t\tFS_Manifest_ParsePackage(man, mdt_installation);\n\telse if (!Q_strcasecmp(cmd, \"package\") || !Q_strcasecmp(cmd, \"archivedpackage\"))\n\t\tFS_Manifest_ParsePackage(man, mdt_singlepackage);\n\telse if (!Q_strcasecmp(cmd, \"basedir\"))\n\t{\t//allow explicit basedirs when this is an actual file on the user's system, and we don't have an explicit one.\n\t\t//this should only happen from parsing /etc/xdg/games/*.fmf\n\t\tif (!man->basedir && man->filename)\n\t\t\tman->basedir = Z_StrDup(Cmd_Argv(1));\n\t}\n\telse\n\t{\n\t\tCon_Printf(\"Unknown token: %s\\n\", cmd);\n\t\tresult = false;\n\t}\n\treturn result;\n}\n//if the manifest omits some expected stuff, give it some defaults to match known game names (so fmf files can defer to the engine instead of having to be maintained separately).\nstatic void FS_Manifest_SetDefaultSettings(ftemanifest_t *man, const gamemode_info_t *game)\n{\n\tint j;\n\n\tif (game)\n\t{\n\t\t//if there's no base dirs, edit the manifest to give it its default ones.\n\t\tfor (j = 0; j < sizeof(man->gamepath) / sizeof(man->gamepath[0]); j++)\n\t\t{\n\t\t\tif (man->gamepath[j].path && (man->gamepath[j].flags&GAMEDIR_BASEGAME))\n\t\t\t\tbreak;\n\t\t}\n\t\tif (j == sizeof(man->gamepath) / sizeof(man->gamepath[0]))\n\t\t{\n\t\t\tfor (j = 0; j < 4; j++)\n\t\t\t\tif (game->dir[j])\n\t\t\t\t{\n\t\t\t\t\tCmd_TokenizeString(va(\"basegame \\\"%s\\\"\", game->dir[j]), false, false);\n\t\t\t\t\tFS_Manifest_ParseTokens(man);\n\t\t\t\t}\n\t\t}\n\n\t\tif (!man->schemes)\n\t\t{\n\t\t\tCmd_TokenizeString(va(\"schemes \\\"%s\\\"\", game->argname+1), false, false);\n\t\t\tFS_Manifest_ParseTokens(man);\n\t\t}\n\n#ifdef PACKAGEMANAGER\n\t\tif (!man->downloadsurl && game->downloadsurl)\n\t\t{\n#ifndef FTE_TARGET_WEB\n\t\t\tif (*game->downloadsurl == '/')\n\t\t\t{\n\t\t\t\tconchar_t musite[256], *e;\n\t\t\t\tchar site[256];\n\t\t\t\tchar *oldprefix = \"http://fte.\";\n\t\t\t\tchar *newprefix = \"https://updates.\";\n\t\t\t\te = COM_ParseFunString(CON_WHITEMASK, ENGINEWEBSITE, musite, sizeof(musite), false);\n\t\t\t\tCOM_DeFunString(musite, e, site, sizeof(site)-1, true, true);\n\t\t\t\tif (!strncmp(site, oldprefix, strlen(oldprefix)))\n\t\t\t\t{\n\t\t\t\t\tmemmove(site+strlen(newprefix), site+strlen(oldprefix), strlen(site)-strlen(oldprefix)+1);\n\t\t\t\t\tmemcpy(site, newprefix, strlen(newprefix));\n\t\t\t\t}\n\t\t\t\tman->downloadsurl = Z_StrDupf(\"%s%s\", site, game->downloadsurl);\n\t\t\t}\n\t\t\telse\n#endif\n\t\t\t\tman->downloadsurl = Z_StrDup(game->downloadsurl);\n\t\t\tFS_Manifest_ParseTokens(man);\n\t\t}\n\t\tif (!man->installupd && game->needpackages)\n\t\t\tman->installupd = Z_StrDup(game->needpackages);\n#endif\n\n\t\tif (!man->protocolname)\n\t\t\tman->protocolname = Z_StrDup(game->protocolname);\n\n\t\tif (!man->defaultexec && game->customexec)\n\t\t{\n\t\t\tconst char *e = game->customexec;\n\t\t\twhile (e[0] == '/' && e[1] == '/')\n\t\t\t{\n\t\t\t\te+=2;\n\t\t\t\twhile(*e)\n\t\t\t\t{\n\t\t\t\t\tif (*e++ == '\\n')\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tman->defaultexec = Z_StrDup(e);\n\t\t}\n\n\t\tif (!man->formalname)\n\t\t\tman->formalname = Z_StrDup(game->poshname);\n\t}\n\n\n\tif (!man->formalname && man->installation && *man->installation)\n\t\tman->formalname = Z_StrDup(man->installation);\n\telse if (!man->formalname)\n\t\tman->formalname = Z_StrDup(FULLENGINENAME);\n}\n//read a manifest file\nftemanifest_t *FS_Manifest_ReadMem(const char *fname, const char *basedir, const char *data)\n{\n\tint ver;\n\tint i;\n\tftemanifest_t *man;\n\tif (!data)\n\t\treturn NULL;\n\twhile (*data == ' ' || *data == '\\t' || *data == '\\r' || *data == '\\n')\n\t\tdata++;\n\tif (!*data)\n\t\treturn NULL;\n\n\tman = FS_Manifest_Create(fname, basedir);\n\n\twhile (data && *data)\n\t{\n\t\tdata = Cmd_TokenizeString(data, false, false);\n\t\tif (!FS_Manifest_ParseTokens(man) && man->parsever <= 1)\n\t\t{\n\t\t\tFS_Manifest_Free(man);\n\t\t\treturn NULL;\n\t\t}\n\t}\n\tif (!man->installation)\n\t{\t//every manifest should have an internal name specified, so we can guess the correct basedir\n\t\t//if we don't recognise it, then we'll typically prompt (or just use the working directory), but always assuming a default at least ensures things are sane.\n\t\t//fixme: we should probably fill in the basegame here (and share that logic with the legacy manifest generation code)\n#ifdef BRANDING_NAME\n\t\tdata = Cmd_TokenizeString((char*)\"game \"STRINGIFY(BRANDING_NAME), false, false);\n#else\n\t\tdata = Cmd_TokenizeString((char*)\"game quake\", false, false);\n#endif\n\t\tFS_Manifest_ParseTokens(man);\n\t}\n\tif (man->installation)\n\t{\t//if we know about it, fill in some defaults...\n\t\tfor (i = 0; gamemode_info[i].argname; i++)\n\t\t{\n\t\t\tif (!strcmp(man->installation, gamemode_info[i].argname+1))\n\t\t\t{\n\t\t\t\tFS_Manifest_SetDefaultSettings(man, &gamemode_info[i]);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\t//svnrevision is often '-', which means we can't just use it as a constant.\n\tver = revision_number(false);\n\tif (ver && (man->minver > ver || (man->maxver && man->maxver < ver)))\n\t{\n\t\tFS_Manifest_Free(man);\n\t\treturn NULL;\n\t}\n\treturn man;\n}\n\nftemanifest_t *FS_Manifest_ReadSystem(const char *fname, const char *basedir)\n{\n\tftemanifest_t *man = NULL;\n\tvfsfile_t *f;\n\tf = VFSOS_Open(fname, \"rb\");\n\tif (f)\n\t{\n\t\tsize_t len = VFS_GETLEN(f);\n\t\tchar *fdata = BZ_Malloc(len+1);\n\t\tif (fdata)\n\t\t{\n\t\t\tVFS_READ(f, fdata, len);\n\t\t\tfdata[len] = 0;\n\t\t\tman = FS_Manifest_ReadMem(fname, basedir, fdata);\n\t\t\tif (man)\n\t\t\t\tman->security = MANIFEST_SECURITY_DEFAULT;\n\t\t\tBZ_Free(fdata);\n\t\t}\n\t\tVFS_CLOSE(f);\n\t}\n\treturn man;\n}\n//reads eg $homedir/$moddir.fmf or $basedir/$moddir.fmf as appropriate.\nftemanifest_t *FS_Manifest_ReadMod(const char *moddir)\n{\n\tftemanifest_t *man = NULL;\n\tchar path[MAX_OSPATH];\n\tif (*moddir)\n\t{\n\t\t//check the homedir, which is a little messy when manifests might disallow themselves...\n\t\tif (!man && com_homepathusable)\n\t\t{\n\t\t\tQ_snprintfz(path, sizeof(path), \"%s%s\", com_homepath, moddir);\n\t\t\tCOM_RequireExtension(path, \".fmf\", sizeof(path));\n\t\t\tman = FS_Manifest_ReadSystem(path, com_gamepath);\n\t\t\tif (man && man->homedirtype == MANIFEST_NOHOMEDIR)\n\t\t\t{\n\t\t\t\tFS_Manifest_Free(man);\t//manifest doesn't like itself... pretend to not find it.\n\t\t\t\tman = NULL;\n\t\t\t}\n\t\t}\n\n\t\tif (!man)\n\t\t{\n\t\t\tQ_snprintfz(path, sizeof(path), \"%s%s\", com_gamepath, moddir);\n\t\t\tCOM_RequireExtension(path, \".fmf\", sizeof(path));\n\t\t\tman = FS_Manifest_ReadSystem(path, com_gamepath);\n\t\t}\n\t}\n\treturn man;\n}\n\n//======================================================================================================\n\n\nstatic char *fs_loadedcommand;\t\t\t//execed once all packages are (down)loaded\nftemanifest_t\t*fs_manifest;\t//currently active manifest.\nstatic searchpath_t\t*com_searchpaths;\nstatic searchpath_t\t*com_purepaths;\nstatic searchpath_t\t*com_base_searchpaths;\t// without gamedirs\n\nstatic int fs_puremode;\t\t\t\t//0=deprioritise pure, 1=prioritise pure, 2=pure only.\nstatic char *fs_refnames;\t\t\t//list of allowed packages\nstatic char *fs_refcrcs;\t\t\t//list of crcs for those packages. one token per package.\nstatic char *fs_purenames;\t\t\t//list of allowed packages\nstatic char *fs_purecrcs;\t\t\t//list of crcs for those packages. one token per package.\nstatic unsigned int fs_pureseed;\t//used as a key so the server knows we're obeying. completely unreliable/redundant in an open source project, but needed for q3 network compat.\n\nchar *FS_AbbreviateSize(char *buf, size_t bufsize, qofs_t fsize)\n{\n\t//caps mean kibi instead of kilo, and bytes not bits.\n\tif (fsize > 512.0*1024*1024*1024)\n\t\tQ_snprintfz(buf, bufsize, \"%gTB\", fsize/(1024.0*1024*1024*1024));\n\telse if (fsize > 512.0*1024*1024)\n\t\tQ_snprintfz(buf, bufsize, \"%gGB\", fsize/(1024.0*1024*1024));\n\telse if (fsize > 512.0*1024)\n\t\tQ_snprintfz(buf, bufsize, \"%gMB\", fsize/(1024.0*1024));\n\telse if (fsize > 512.0)\n\t\tQ_snprintfz(buf, bufsize, \"%gKB\", fsize/1024.0);\n\telse\n\t\tQ_snprintfz(buf, bufsize, \"%\"PRIuQOFS\"B\", fsize);\n\treturn buf;\n}\n\nint QDECL COM_FileSize(const char *path)\n{\n\tflocation_t loc;\n\tif (FS_FLocateFile(path, FSLF_IFFOUND, &loc))\n\t\treturn loc.len;\n\telse\n\t\treturn -1;\n}\n\n//appends a / on the end of the directory if it does not already have one.\nstatic void FS_CleanDir(char *out, int outlen)\n{\n\tint olen = strlen(out);\n\tif (!olen || olen >= outlen-1)\n\t\treturn;\n\n\tif (out[olen-1] == '\\\\')\n\t\tout[olen-1] = '/';\n\telse if (out[olen-1] != '/')\n\t{\n\t\tout[olen+1] = '\\0';\n\t\tout[olen] = '/';\n\t}\n}\n\n/*\n============\nCOM_Path_f\n\n============\n*/\nstatic void COM_PathLine(searchpath_t *s)\n{\n\tchar displaypath[MAX_OSPATH];\n\tchar *col = \"\";\n\tif (s->flags&SPF_ISDIR)\n\t\tcol = S_COLOR_GREEN;\n\telse if (s->flags&SPF_COPYPROTECTED)\n\t\tcol = S_COLOR_BLUE;\n\telse\n\t\tcol = S_COLOR_CYAN;\n\tif (s->flags&SPF_VIRTUAL)\n\t\tQ_strncpyz(displaypath, s->logicalpath, sizeof(displaypath));\n\telse\n\t\tFS_DisplayPath(s->logicalpath, FS_SYSTEM, displaypath,sizeof(displaypath));\n\tCon_Printf(\" %s\" U8(\"%s\")\"  %s%s%s%s%s%s%s%s\\n\", col, displaypath,\n\t\t(s->flags & SPF_REFERENCED)?\"^[(ref)\\\\tip\\\\Referenced\\\\desc\\\\Package will auto-download to clients^]\":\"\",\n\t\t(s->flags & SPF_TEMPORARY)?\"^[(temp)\\\\tip\\\\Temporary\\\\desc\\\\Flushed on map change^]\":\"\",\n\t\t(s->flags & SPF_SERVER)?\"^[(srv)\\\\tip\\\\Server-Specified\\\\desc\\\\Loaded to match the server, closed on disconnect^]\":\"\",\n\t\t(s->flags & SPF_COPYPROTECTED)?\"^[(c)\\\\tip\\\\Copyrighted\\\\desc\\\\Copy-Protected and is not downloadable^]\":\"\",\n\t\t(s->flags & SPF_EXPLICIT)?\"^[(e)\\\\tip\\\\Explicit\\\\desc\\\\Loaded explicitly by the gamedir^]\":\"\",\n\t\t(s->flags & SPF_UNTRUSTED)?\"^[(u)\\\\tip\\\\Untrusted\\\\desc\\\\Configs and scripts will not be given access to passwords^]\":\"\",\n\t\t(s->flags & SPF_WRITABLE)?\"^[(w)\\\\tip\\\\Writable\\\\desc\\\\We can probably write here^]\":\"\",\n\t\t(s->handle->GeneratePureCRC)?va(\"^[(h)\\\\tip\\\\Hash: %x^]\", s->crc_check):\"\");\n}\nqboolean FS_GameIsInitialised(void)\n{\n\tif (!com_searchpaths && !com_purepaths)\n\t\treturn false;\n\treturn true;\n}\nstatic void COM_Path_f (void)\n{\n\tsearchpath_t\t*s;\n\n\tif (!FS_GameIsInitialised())\n\t{\n\t\tCon_Printf(\"File system not initialised\\n\");\n\t\tCon_Printf(\"gamedirfile: \\\"%s\\\"\\n\", gamedirfile);\n\t\tCon_Printf(\"pubgamedirfile: \\\"%s\\\"\\n\", pubgamedirfile);\n\t\tCon_Printf(\"com_gamepath: \\\"%s\\\"\\n\", com_gamepath);\n\t\tCon_Printf(\"com_homepath: \\\"%s\\\" (enabled: %s, usable: %s)\\n\", com_homepath, com_homepathenabled?\"yes\":\"no\", com_homepathusable?\"yes\":\"no\");\n//\t\tCon_Printf(\"com_configdir: \\\"%s\\\"\\n\", com_configdir);\n\t\tif (fs_manifest)\n\t\t\tFS_Manifest_Print(fs_manifest);\n\t\treturn;\n\t}\n\n\tif (fs_hidesyspaths.ival)\n\t{\n\t\tif (isDedicated)\n\t\t\tCon_Printf(\"External paths are hidden, ^[fs_hidesyspaths 0^7 to unhide\\\\type\\\\set fs_hidesyspaths 0;path^]\\n\");\n\t\telse\n\t\t\tCon_Printf(\"External paths are hidden, ^[click to unhide\\\\type\\\\set fs_hidesyspaths 0;path^]\\n\");\n\t}\n\n\tif (com_purepaths || fs_puremode)\n\t{\n\t\tCon_Printf (\"Pure paths:\\n\");\n\t\tfor (s=com_purepaths ; s ; s=s->nextpure)\n\t\t{\n\t\t\tCOM_PathLine(s);\n\t\t}\n\t\tif (fs_puremode == 2)\n\t\t\tCon_Printf (\"Inactive paths:\\n\");\n\t\telse\n\t\t\tCon_Printf (\"Impure paths:\\n\");\n\t}\n\telse\n\t\tCon_TPrintf (\"Current search path:\\n\");\n\n\n\tfor (s=com_searchpaths ; s ; s=s->next)\n\t{\n\t\tif (s == com_base_searchpaths)\n\t\t\tCon_Printf (\" ----------\\n\");\n\n\t\tCOM_PathLine(s);\n\t}\n\n\n\n\tif (fs_purenames && fs_purecrcs)\n\t{\n\t\tchar crctok[64];\n\t\tchar nametok[MAX_QPATH];\n\n\t\tint crc;\n\t\tchar *pc = fs_purecrcs;\n\t\tchar *pn = fs_purenames;\n\t\tfor (;;)\n\t\t{\n\t\t\tpc = COM_ParseOut(pc, crctok, sizeof(crctok));\n\t\t\tpn = COM_ParseOut(pn, nametok, sizeof(nametok));\n\t\t\tif (!pc || !pn)\n\t\t\t\tbreak;\n\t\t\tcrc = strtoul(crctok, NULL, 0);\n\n\t\t\tfor (s=com_searchpaths ; s ; s=s->next)\n\t\t\t{\n\t\t\t\tif (s && s->crc_check == crc)\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (!s)\n\t\t\t{\n\t\t\t\tCOM_DefaultExtension(nametok, \".pk3\", sizeof(nametok));\n\t\t\t\tif (*nametok == '*')\n\t\t\t\t\tCon_Printf(CON_ERROR \"MISSING: \" U8(\"%s\")\"  (%x)\\n\", nametok+1, crc);\n\t\t\t\telse\n\t\t\t\t\tCon_Printf(CON_WARNING \"MISSING: \" U8(\"%s\")\"  (%x)\\n\", nametok, crc);\n\t\t\t}\n\t\t}\n\t}\n}\n\n\n/*\n============\nCOM_Dir_f\n\n============\n*/\nstatic int QDECL COM_Dir_List(const char *name, qofs_t size, time_t mtime, void *parm, searchpathfuncs_t *spath)\n{\n\tsearchpath_t\t*s;\n\tconst char *ext;\n\tchar link[512];\n\tchar pkgname[MAX_OSPATH];\n\tchar szbuf[16];\n\tchar *colour;\n\tflocation_t loc;\n\tfor (s=com_searchpaths ; s ; s=s->next)\n\t{\n\t\tif (s->handle == spath)\n\t\t\tbreak;\n\t}\n\n\tif (*name && name[strlen(name)-1] == '/')\n\t{\n\t\tcolour = \"^7\";\t//superseeded\n\t\tQ_snprintfz(link, sizeof(link), \"\\\\tip\\\\Scan Sub-Directory\\\\dir\\\\%s*\", name);\n\t}\n\telse if (!FS_FLocateFile(name, FSLF_IFFOUND, &loc))\n\t{\n\t\tcolour = \"^1\";\t//superseeded\n\t\tQ_snprintfz(link, sizeof(link), \"\\\\tip\\\\flocate error\");\n\t}\n\telse if (loc.search->handle == spath || (fs_allowfileuri&&loc.search == fs_allowfileuri))\n\t{\n\t\tcolour = \"^2\";\n\n\t\text = COM_GetFileExtension(name, NULL);\n\t\tif (!Q_strcasecmp(ext, \".gz\") || !Q_strcasecmp(ext, \".xz\"))\n\t\t\text = COM_GetFileExtension(name, ext);\n\t\tif (*ext == '.')\n\t\t{\n\t\t\text++;\n\t\t\tif (strchr(ext, '.'))\n\t\t\t{\n\t\t\t\tCOM_StripAllExtensions(ext, link, sizeof(link));\n\t\t\t\text = link;\n\t\t\t}\n\t\t}\n\n\t\tif ((!Q_strcasecmp(ext, \"bsp\") || !Q_strcasecmp(ext, \"d3dbsp\") || !Q_strcasecmp(ext, \"map\") || !Q_strcasecmp(ext, \"hmp\")) && !strncmp(name, \"maps/\", 5) && strncmp(name, \"maps/b_\", 7))\n\t\t{\n\t\t\tQ_snprintfz(link, sizeof(link), \"\\\\tip\\\\Change Map\\\\map\\\\%s\", name+5);\n\t\t\tcolour = \"^4\";\t//disconnects\n\t\t}\n#if !defined(NOBUILTINMENUS) && !defined(MINIMAL)\n\t\telse if (!Q_strcasecmp(ext, \"bsp\") || !Q_strcasecmp(ext, \"spr\") || !Q_strcasecmp(ext, \"sp2\") || !Q_strcasecmp(ext, \"mdl\") || !Q_strcasecmp(ext, \"md3\") || !Q_strcasecmp(ext, \"iqm\") ||\n\t\t\t\t !Q_strcasecmp(ext, \"vvm\") || !Q_strcasecmp(ext, \"psk\") || !Q_strcasecmp(ext, \"dpm\") || !Q_strcasecmp(ext, \"zym\") || !Q_strcasecmp(ext, \"md5mesh\") ||\n\t\t\t\t !Q_strcasecmp(ext, \"mdx\") || !Q_strcasecmp(ext, \"md2\") || !Q_strcasecmp(ext, \"obj\") || !Q_strcasecmp(ext, \"mds\") || !Q_strcasecmp(ext, \"mdc\") ||\n\t\t\t\t !Q_strcasecmp(ext, \"md5anim\") || !Q_strcasecmp(ext, \"gltf\") || !Q_strcasecmp(ext, \"glb\") || !Q_strcasecmp(ext, \"ase\") || !Q_strcasecmp(ext, \"lwo\") ||\n\t\t\t\t ((!Q_strncasecmp(name, \"xmodel/\", 7)||!Q_strncasecmp(name, \"xanim/\", 6)) && !Q_strcasecmp(ext, \"\"))\t//urgh\n\t\t\t\t )\n\t\t\tQ_snprintfz(link, sizeof(link), \"\\\\tip\\\\Open in Model Viewer\\\\modelviewer\\\\%s\", name);\n#endif\n#ifdef TEXTEDITOR\n\t\telse if (!Q_strcasecmp(ext, \"qc\") || !Q_strcasecmp(ext, \"src\") || !Q_strcasecmp(ext, \"qh\") || !Q_strcasecmp(ext, \"h\") || !Q_strcasecmp(ext, \"c\") ||\n\t\t\t\t!Q_strcasecmp(ext, \"cfg\") || !Q_strcasecmp(ext, \"rc\") ||\n\t\t\t\t!Q_strcasecmp(ext, \"txt\") || !Q_strcasecmp(ext, \"log\") ||\n\t\t\t\t!Q_strcasecmp(ext, \"ent\") || !Q_strcasecmp(ext, \"rtlights\") ||\n\t\t\t\t!Q_strcasecmp(ext, \"glsl\") || !Q_strcasecmp(ext, \"hlsl\") ||\n\t\t\t\t!Q_strcasecmp(ext, \"shader\") || !Q_strcasecmp(ext, \"framegroups\") ||\n\t\t\t\t!Q_strcasecmp(ext, \"vmt\") || !Q_strcasecmp(ext, \"skin\"))\n\t\t\tQ_snprintfz(link, sizeof(link), \"\\\\tip\\\\Open in Text Editor\\\\edit\\\\%s\", name);\n#endif\n\t\telse if (!Q_strcasecmp(ext, \"tga\") || !Q_strcasecmp(ext, \"png\") || !Q_strcasecmp(ext, \"jpg\") || !Q_strcasecmp(ext, \"jpeg\")|| !Q_strcasecmp(ext, \"lmp\") || !Q_strcasecmp(ext, \"ico\") ||\n\t\t\t\t !Q_strcasecmp(ext, \"pcx\") || !Q_strcasecmp(ext, \"bmp\") || !Q_strcasecmp(ext, \"dds\") || !Q_strcasecmp(ext, \"ktx\") || !Q_strcasecmp(ext, \"ktx2\")|| !Q_strcasecmp(ext, \"vtf\") ||\n\t\t\t\t !Q_strcasecmp(ext, \"astc\")|| !Q_strcasecmp(ext, \"htga\")|| !Q_strcasecmp(ext, \"exr\") || !Q_strcasecmp(ext, \"xcf\") || !Q_strcasecmp(ext, \"psd\") || !Q_strcasecmp(ext, \"iwi\") ||\n\t\t\t\t !Q_strcasecmp(ext, \"pbm\") || !Q_strcasecmp(ext, \"ppm\") || !Q_strcasecmp(ext, \"pgm\") || !Q_strcasecmp(ext, \"pam\") || !Q_strcasecmp(ext, \"pfm\") || !Q_strcasecmp(ext, \"hdr\") ||\n\t\t\t\t !Q_strcasecmp(ext, \"wal\") )\n\t\t{\n\t\t\t//FIXME: image replacements are getting in the way here.\n\t\t\tQ_snprintfz(link, sizeof(link), \"\\\\tiprawimg\\\\%s\\\\tip\\\\(note: image replacement rules are context-dependant, including base path, sub path, extension, or complete replacement via a shader)\", name);\n\t\t\tcolour = \"^6\";\t//shown on mouseover\n\t\t}\n\t\telse if (!Q_strcasecmp(ext, \"qwd\") || !Q_strcasecmp(ext, \"dem\") || !Q_strcasecmp(ext, \"mvd\") || !Q_strcasecmp(ext, \"dm2\"))\n\t\t{\n\t\t\tQ_snprintfz(link, sizeof(link), \"\\\\tip\\\\Play Demo\\\\demo\\\\%s\", name);\n\t\t\tcolour = \"^4\";\t//disconnects\n\t\t}\n\t\telse if (!Q_strcasecmp(ext, \"roq\") || !Q_strcasecmp(ext, \"cin\") || !Q_strcasecmp(ext, \"avi\") || !Q_strcasecmp(ext, \"mp4\") || !Q_strcasecmp(ext, \"mkv\") || !Q_strcasecmp(ext, \"ogv\"))\n\t\t\tQ_snprintfz(link, sizeof(link), \"\\\\tip\\\\Play Film\\\\film\\\\%s\", name);\n\t\telse if (!Q_strcasecmp(ext, \"wav\") || !Q_strcasecmp(ext, \"ogg\") || !Q_strcasecmp(ext, \"mp3\") || !Q_strcasecmp(ext, \"opus\") || !Q_strcasecmp(ext, \"flac\"))\n\t\t\tQ_snprintfz(link, sizeof(link), \"\\\\tip\\\\Play Audio\\\\playaudio\\\\%s\", name);\n\t\telse\n\t\t{\n\t\t\tcolour = \"^3\";\t//nothing\n\t\t\t*link = 0;\n\t\t}\n\t}\n\telse\n\t{\n\t\tchar *gah;\n\t\tif (FS_DisplayPath(loc.search->logicalpath, FS_SYSTEM, pkgname,sizeof(pkgname)))\n\t\t\t;\n\t\telse\n\t\t\tQ_strncpyz(pkgname, loc.search->purepath, sizeof(pkgname));\n\n\t\tcolour = \"^1\";\t//superseeded\n\t\tQ_snprintfz(link, sizeof(link), \"\\\\tip\\\\overriden by file from %s\", pkgname);\n\t\tgah = link + 20;\t//whatever\n\t\twhile ((gah = strchr(gah, '\\\\')))\n\t\t\t*gah = '/';\n\t}\n\n\tif (s && FS_DisplayPath(s->logicalpath, FS_SYSTEM, pkgname,sizeof(pkgname)))\n\t\t;\n\telse\n\t\tQ_strncpyz(pkgname, \"??\", sizeof(pkgname));\n\n\tCon_Printf(U8(\"(%s) ^[%s%s%s^] \\t^h(%s)\\n\"), FS_AbbreviateSize(szbuf,sizeof(szbuf), size), colour, name, link, pkgname);\n\treturn 1;\n}\n\nstatic void COM_Dir_f (void)\n{\n\tchar match[MAX_QPATH];\n\n\tif (Cmd_Argc()>1)\n\t\tQ_strncpyz(match, Cmd_Argv(1), sizeof(match));\n\telse\n\t\tQ_strncpyz(match, \"*\", sizeof(match));\n\n\tif (Cmd_Argc()>2)\n\t{\n\t\tstrncat(match, \"/*.\", sizeof(match)-1);\n\t\tmatch[sizeof(match)-1] = '\\0';\n\t\tstrncat(match, Cmd_Argv(2), sizeof(match)-1);\n\t\tmatch[sizeof(match)-1] = '\\0';\n\t}\n//\telse\n//\t\tstrncat(match, \"/*\", sizeof(match)-1);\n\n\tCOM_EnumerateFiles(match, COM_Dir_List, NULL);\n}\n\n/*\n============\nCOM_Locate_f\n\n============\n*/\nstatic void COM_Locate_f (void)\n{\n\tchar pkgname[MAX_OSPATH];\n\tflocation_t loc;\n\tchar *f = Cmd_Argv(1);\n\tif (strchr(f, '^'))\t//fte's filesystem is assumed to be utf-8, but that doesn't mean that console input is. and I'm too lazy to utf-8ify the string (in part because markup can be used to exploit ascii assumptions).\n\t\tCon_Printf(\"Warning: filename contains markup. If this is because of unicode, set com_parseutf8 1\\n\");\n\tif (FS_FLocateFile(f, FSLF_IFFOUND, &loc))\n\t{\n\t\tif (FS_DisplayPath(loc.search->logicalpath, FS_SYSTEM, pkgname,sizeof(pkgname)))\n\t\t\t;\n\t\telse\n\t\t\tQ_strncpyz(pkgname, \"??\", sizeof(pkgname));\n\t\tif (!*loc.rawname)\n\t\t{\n\t\t\tCon_Printf(\"File is %u bytes compressed inside \"U8(\"%s\")\"\\n\", (unsigned)loc.len, pkgname);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tCon_Printf(\"Inside \"U8(\"%s\")\" (%u bytes)\\n  \"U8(\"%s\")\"\\n\", loc.rawname, (unsigned)loc.len, pkgname);\n\t\t}\n\t}\n\telse\n\t\tCon_Printf(\"Not found\\n\");\n}\n\nstatic void COM_CalcHash_Thread(void *ctx, void *fname, size_t a, size_t b)\n{\n\tint h;\n\tstruct\n\t{\n\t\tconst char *name;\n\t\thashfunc_t *hash;\n\t\tvoid *ctx;\n\t} hashes[] =\n\t{\n//\t\t{\"crc16\", &hash_crc16},\n//\t\t{\"md4\", &hash_md4},\n#if defined(HAVE_SERVER) || defined(HAVE_CLIENT)\n\t\t{\"md5\", &hash_md5},\n#endif\n\t\t{\"sha1\", &hash_sha1},\n#if defined(HAVE_SERVER) || defined(HAVE_CLIENT)\n//\t\t{\"sha224\", &hash_sha2_224},\n\t\t{\"sha256\", &hash_sha2_256},\n//\t\t{\"sha384\", &hash_sha2_384},\n//\t\t{\"sha512\", &hash_sha2_512},\n#endif\n\t};\n\tqbyte digest[DIGEST_MAXSIZE];\n\tqbyte digesttext[DIGEST_MAXSIZE*2+1];\n\tqbyte block[65536];\n\tint csize;\n\tquint64_t fsize = 0;\n\tquint64_t tsize = 0;\n\tunsigned int pct, opct=~0;\n\tvfsfile_t *f = FS_OpenVFS(fname, \"rb\", FS_GAME);\n\n\tif (f)\n\t{\n\t\ttsize = VFS_GETLEN(f);\n\t\tCon_Printf(\"%s: Processing...\\r\", (char*)fname);\n\n\t\tfor (h = 0; h < countof(hashes); h++)\n\t\t{\n\t\t\thashes[h].ctx = Z_Malloc(hashes[h].hash->contextsize);\n\t\t\thashes[h].hash->init(hashes[h].ctx);\n\t\t}\n\n\t\tfor(;;)\n\t\t{\n\t\t\tcsize = VFS_READ(f, block, sizeof(block));\n\t\t\tif (csize <= 0)\n\t\t\t\tbreak;\n\t\t\tfsize += csize;\n\t\t\tfor (h = 0; h < countof(hashes); h++)\n\t\t\t{\n\t\t\t\thashes[h].hash->process(hashes[h].ctx, block, csize);\n\t\t\t}\n\t\t\tpct = (100*fsize)/tsize;\n\t\t\tif (pct != opct)\n\t\t\t{\n\t\t\t\tCon_Printf(\"%s: %i%%...\\r\", (char*)fname, pct);\n\t\t\t\topct = pct;\n\t\t\t}\n\t\t}\n\n\t\tVFS_CLOSE(f);\n\n\t\tCon_Printf(\"%s: %s\\n\", (char*)fname, FS_AbbreviateSize(block,sizeof(block), fsize));\n\t\tfor (h = 0; h < countof(hashes); h++)\n\t\t{\n\t\t\thashes[h].hash->terminate(digest, hashes[h].ctx);\n\t\t\tZ_Free(hashes[h].ctx);\n\n\t\t\tdigesttext[Base16_EncodeBlock(digest, hashes[h].hash->digestsize, digesttext, sizeof(digesttext)-1)] = 0;\n\t\t\tCon_Printf(\"  %s: %s\\n\", hashes[h].name, digesttext);\n\t\t}\n\t}\n\tZ_Free(fname);\n}\nstatic void COM_CalcHash_f(void)\n{\n\tif (Cmd_Argc() != 2)\n\t\tCon_Printf(\"%s <FILENAME>: computes various hashes of the specified file\\n\", Cmd_Argv(0));\n\telse\n\t\tCOM_AddWork(WG_LOADER, COM_CalcHash_Thread, NULL, Z_StrDup(Cmd_Argv(1)), 0, 0);\n}\n\n/*\n============\nCOM_WriteFile\n\nThe filename will be prefixed by the current game directory\n============\n*/\nqboolean COM_WriteFile (const char *filename, enum fs_relative fsroot, const void *data, int len)\n{\n\tqboolean success = false;\n\tvfsfile_t *vfs;\n\n\tSys_Printf (\"COM_WriteFile: %s\\n\", filename);\n\n\tFS_CreatePath(filename, fsroot);\n\tvfs = FS_OpenVFS(filename, \"wb\", fsroot);\n\tif (vfs)\n\t{\n\t\tVFS_WRITE(vfs, data, len);\n\t\tsuccess = VFS_CLOSE(vfs);\n\n\t\tif (fsroot >= FS_GAME)\n\t\t\tFS_FlushFSHashWritten(filename);\n\t\telse\n\t\t\tcom_fschanged=true;\n\t}\n\treturn success;\n}\n\n/*\n============\nCOM_CreatePath\n\nOnly used for CopyFile and download\n============\n*/\nstatic void\tCOM_CreatePath (char *path)\n{\n\tchar\t*ofs;\n\n\tif (fs_readonly)\n\t\treturn;\n\n\tfor (ofs = path+1 ; *ofs ; ofs++)\n\t{\n\t\tif (*ofs == '/')\n\t\t{\t// create the directory\n\t\t\t*ofs = 0;\n\t\t\tSys_mkdir (path);\n\t\t\t*ofs = '/';\n\t\t}\n\t}\n}\n\n\n/*\n===========\nCOM_CopyFile\n\nCopies a file over from the net to the local cache, creating any directories\nneeded.  This is for the convenience of developers using ISDN from home.\n===========\n*/\n/*\nstatic void COM_CopyFile (char *netpath, char *cachepath)\n{\n\tFILE\t*in, *out;\n\tint\t\tremaining, count;\n\tchar\tbuf[4096];\n\n\tremaining = COM_FileOpenRead (netpath, &in);\n\tCOM_CreatePath (cachepath);\t// create directories up to the cache file\n\tout = fopen(cachepath, \"wb\");\n\tif (!out)\n\t\tSys_Error (\"Error opening %s\", cachepath);\n\n\twhile (remaining)\n\t{\n\t\tif (remaining < sizeof(buf))\n\t\t\tcount = remaining;\n\t\telse\n\t\t\tcount = sizeof(buf);\n\t\tfread (buf, 1, count, in);\n\t\tfwrite (buf, 1, count, out);\n\t\tremaining -= count;\n\t}\n\n\tfclose (in);\n\tfclose (out);\n}\n//*/\n\nint fs_hash_dups;\nint fs_hash_files;\n\n//normally the filesystem drivers pass a pre-allocated bucket and static strings to us\n//the OS driver can't really be expected to track things that reliably however, so it just gives names via the stack.\n//these files are grouped up to avoid excessive memory allocations.\nstruct fsbucketblock\n{\n\tstruct fsbucketblock *prev;\n\tint used;\n\tint total;\n\tqbyte data[1];\n};\nstatic struct fsbucketblock *fs_hash_filebuckets;\n\nstatic void FS_FlushFSHashReally(qboolean domutexes)\n{\n\tCOM_AssertMainThread(\"FS_FlushFSHashReally\");\n\tif (!domutexes || Sys_LockMutex(fs_thread_mutex))\n\t{\n\t\tcom_fschanged = true;\n\n\t\tif (filesystemhash.numbuckets)\n\t\t{\n\t\t\tint i;\n\t\t\tfor (i = 0; i < filesystemhash.numbuckets; i++)\n\t\t\t\tfilesystemhash.bucket[i] = NULL;\n\t\t}\n\n\t\twhile (fs_hash_filebuckets)\n\t\t{\n\t\t\tstruct fsbucketblock *n = fs_hash_filebuckets->prev;\n\t\t\tZ_Free(fs_hash_filebuckets);\n\t\t\tfs_hash_filebuckets = n;\n\t\t}\n\n\t\tif (domutexes)\n\t\t\tSys_UnlockMutex(fs_thread_mutex);\n\t}\n}\n\nstatic void QDECL FS_AddFileHashUnsafe(int depth, const char *fname, fsbucket_t *filehandle, void *pathhandle)\n{\n\t//threading stuff is fucked.\n\tfsbucket_t *old;\n\n\told = Hash_GetInsensitiveBucket(&filesystemhash, fname);\n\n\tif (old)\n\t{\n\t\tfs_hash_dups++;\n\t\tif (depth >= old->depth)\n\t\t{\n\t\t\treturn;\n\t\t}\n\n\t\t//remove the old version\n\t\t//FIXME: needs to be atomic. just live with multiple in there.\n\t\t//Hash_RemoveBucket(&filesystemhash, fname, &old->buck);\n\t}\n\n\tif (!filehandle)\n\t{\n\t\tint nlen = strlen(fname)+1;\n\t\tint plen = sizeof(*filehandle)+nlen;\n\t\tplen = (plen+fte_alignof(fsbucket_t)-1) & ~(fte_alignof(fsbucket_t)-1);\n\t\tif (!fs_hash_filebuckets || fs_hash_filebuckets->used+plen > fs_hash_filebuckets->total)\n\t\t{\n\t\t\tvoid *o = fs_hash_filebuckets;\n\t\t\tfs_hash_filebuckets = Z_Malloc(65536);\n\t\t\tfs_hash_filebuckets->total = 65536 - sizeof(*fs_hash_filebuckets);\n\t\t\tfs_hash_filebuckets->prev = o;\n\t\t}\n\t\tfilehandle = (fsbucket_t*)(fs_hash_filebuckets->data+fs_hash_filebuckets->used);\n\t\tfs_hash_filebuckets->used += plen;\n\n\t\tif (!filehandle)\n\t\t\treturn;\t//eep!\n\t\tmemcpy((char*)(filehandle+1), fname, nlen);\n\t\tfname = (char*)(filehandle+1);\n\t}\n\tfilehandle->depth = depth;\n\n\tHash_AddInsensitive(&filesystemhash, fname, pathhandle, &filehandle->buck);\n\tfs_hash_files++;\n}\nstatic void QDECL FS_AddFileHash(int depth, const char *fname, fsbucket_t *filehandle, void *pathhandle)\n{\n\tfsbucket_t *old;\n\n\told = Hash_GetInsensitiveBucket(&filesystemhash, fname);\n\n\tif (old)\n\t{\n\t\tfs_hash_dups++;\n\t\tif (depth >= old->depth)\n\t\t{\n\t\t\treturn;\n\t\t}\n\n\t\t//remove the old version\n\t\tHash_RemoveBucket(&filesystemhash, fname, &old->buck);\n\t}\n\n\tif (!filehandle)\n\t{\n\t\tint nlen = strlen(fname)+1;\n\t\tint plen = sizeof(*filehandle)+nlen;\n\t\tplen = (plen+fte_alignof(fsbucket_t)-1) & ~(fte_alignof(fsbucket_t)-1);\n\t\tif (!fs_hash_filebuckets || fs_hash_filebuckets->used+plen > fs_hash_filebuckets->total)\n\t\t{\n\t\t\tvoid *o = fs_hash_filebuckets;\n\t\t\tfs_hash_filebuckets = Z_Malloc(65536);\n\t\t\tfs_hash_filebuckets->total = 65536 - sizeof(*fs_hash_filebuckets);\n\t\t\tfs_hash_filebuckets->prev = o;\n\t\t}\n\t\tfilehandle = (fsbucket_t*)(fs_hash_filebuckets->data+fs_hash_filebuckets->used);\n\t\tfs_hash_filebuckets->used += plen;\n\n\t\tif (!filehandle)\n\t\t\treturn;\t//eep!\n\t\tmemcpy((char*)(filehandle+1), fname, nlen);\n\t\tfname = (char*)(filehandle+1);\n\t}\n\tfilehandle->depth = depth;\n\n\tHash_AddInsensitive(&filesystemhash, fname, pathhandle, &filehandle->buck);\n\tfs_hash_files++;\n}\n\n#ifndef FTE_TARGET_WEB\nstatic void FS_RebuildFSHash(qboolean domutex)\n{\n\tint depth = 1;\n\tsearchpath_t\t*search;\n\tif (!com_fschanged)\n\t\treturn;\n\n\tCOM_AssertMainThread(\"FS_RebuildFSHash\");\n\tif (domutex && !Sys_LockMutex(fs_thread_mutex))\n\t\treturn;\t//amg!\n\n\tif (com_fsneedreload)\n\t\tFS_ReloadPackFilesFlags(~0);\n\n\tif (!filesystemhash.numbuckets)\n\t{\n\t\tfilesystemhash.numbuckets = 1024;\n\t\tfilesystemhash.bucket = (bucket_t**)Z_Malloc(Hash_BytesForBuckets(filesystemhash.numbuckets));\n\t}\n\telse\n\t{\n\t\tFS_FlushFSHashReally(false);\n\t}\n\tHash_InitTable(&filesystemhash, filesystemhash.numbuckets, filesystemhash.bucket);\n\n\tfs_hash_dups = 0;\n\tfs_hash_files = 0;\n\n\tif (com_purepaths)\n\t{\t//go for the pure paths first.\n\t\tfor (search = com_purepaths; search; search = search->nextpure)\n\t\t{\n\t\t\tsearch->handle->BuildHash(search->handle, depth++, FS_AddFileHash);\n\t\t}\n\t}\n\tif (fs_puremode < 2)\n\t{\n\t\tfor (search = com_searchpaths ; search ; search = search->next)\n\t\t{\n\t\t\tsearch->handle->BuildHash(search->handle, depth++, FS_AddFileHash);\n\t\t}\n\t}\n\n\tcom_fschanged = false;\n\tcom_fsneedreload = false;\n\n\tif (domutex)\n\t\tSys_UnlockMutex(fs_thread_mutex);\n\n\tCon_DPrintf(\"%i unique files, %i duplicates\\n\", fs_hash_files, fs_hash_dups);\n}\n#endif\n\nstatic void FS_RebuildFSHash_Update(const char *fname)\n{\n\tflocation_t loc;\n\tsearchpath_t *search;\n\tint depth = 0;\n\tfsbucket_t *old;\n\tvoid *filehandle = NULL;\n\n\tif (com_fschanged)\n\t\treturn;\n\n\tif (!filehandle && com_purepaths)\n\t{\t//go for the pure paths first.\n\t\tfor (search = com_purepaths; search; search = search->nextpure)\n\t\t{\n\t\t\tif (search->handle->FindFile(search->handle, &loc, fname, NULL))\n\t\t\t{\n\t\t\t\tfilehandle = loc.fhandle;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tdepth++;\n\t\t}\n\t}\n\tif (!filehandle && fs_puremode < 2)\n\t{\n\t\tfor (search = com_searchpaths ; search ; search = search->next)\n\t\t{\n\t\t\tif (search->handle->FindFile(search->handle, &loc, fname, NULL))\n\t\t\t{\n\t\t\t\tfilehandle = loc.fhandle;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tdepth++;\n\t\t}\n\t}\n\n\n\tCOM_WorkerLock();\n\tif (!Sys_LockMutex(fs_thread_mutex))\n\t\treturn;\t//amg!\n\n\told = Hash_GetInsensitiveBucket(&filesystemhash, fname);\n\tif (old)\n\t{\n\t\tHash_RemoveBucket(&filesystemhash, fname, &old->buck);\n\t\tfs_hash_files--;\n\t}\n\n\tif (filehandle)\n\t\tFS_AddFileHash(depth, fname, NULL, filehandle);\n\n\tSys_UnlockMutex(fs_thread_mutex);\n\tCOM_WorkerUnlock();\n}\n\nvoid FS_FlushFSHashWritten(const char *fname)\n{\n\tFS_RebuildFSHash_Update(fname);\n}\nvoid FS_FlushFSHashRemoved(const char *fname)\n{\n\tFS_RebuildFSHash_Update(fname);\n}\nvoid FS_FlushFSHashFull(void)\n{\t//any calls to this are typically a bug...\n\t//that said, figuring out if the file was actually within quake's filesystem isn't easy.\n\tcom_fschanged = true;\n\n\t//for safety we would need to sync with all threads, so lets just not bother.\n\t//FS_FlushFSHashReally(true);\n}\n\n\n/*\n===========\nCOM_FindFile\n\nFinds the file in the search path.\nSets com_filesize and one of handle or file\n===========\n*/\n//if loc is valid, loc->search is always filled in, the others are filled on success.\n//returns 0 if couldn't find.\nint FS_FLocateFile(const char *filename, unsigned int lflags, flocation_t *loc)\n{\n\tint depth=0;\n\tsearchpath_t\t*search;\n\tchar cleanpath[MAX_OSPATH];\n\tflocation_t allownoloc;\n\n\tvoid *pf;\n\tunsigned int found = FF_NOTFOUND;\n\n\tif (!loc)\n\t\tloc = &allownoloc;\n\n\tloc->fhandle = NULL;\n\tloc->offset = 0;\n\t*loc->rawname = 0;\n\tloc->search = NULL;\n\tloc->len = -1;\n\n\tif (!strncmp(filename, \"file:\", 5))\n\t{\n\t\tif (fs_allowfileuri && Sys_ResolveFileURL(filename, strlen(filename), cleanpath, sizeof(cleanpath)))\n\t\t{\n\t\t\tfs_finds++;\n\t\t\tfound = fs_allowfileuri->handle->FindFile(fs_allowfileuri->handle, loc, cleanpath, NULL);\n\t\t\tif (found)\n\t\t\t\tloc->search = fs_allowfileuri;\n\t\t}\n\t\tpf = NULL;\n\t\tgoto fail;\n\t}\n\n\tfilename = FS_GetCleanPath(filename, (lflags&FSLF_QUIET), cleanpath, sizeof(cleanpath));\n\tif (!filename)\n\t{\n\t\tpf = NULL;\n\t\tgoto fail;\n\t}\n\n\tif (com_fs_cache.ival && !com_fschanged && !(lflags & FSLF_IGNOREPURE))\n\t{\n\t\tbucket_t *b = Hash_GetInsensitiveBucket(&filesystemhash, filename);\n\t\tif (b)\n\t\t{\n\t\t\tpf = b->data;\n\t\t\tfilename = b->key.string;\t//update the filename to use the correct file case...\n\t\t}\n\t\telse\n\t\t\tgoto fail;\n\t}\n\telse\n\t\tpf = NULL;\n\n\tif (com_purepaths && found == FF_NOTFOUND && !(lflags & FSLF_IGNOREPURE))\n\t{\n\t\t//check if its in one of the 'pure' packages. these override the default ones.\n\t\tfor (search = com_purepaths ; search ; search = search->nextpure)\n\t\t{\n\t\t\tif ((lflags & FSLF_SECUREONLY) && !(search->flags & SPF_UNTRUSTED))\n\t\t\t\tcontinue;\n\t\t\tif (!((lflags & FSLF_IGNOREBASEDEPTH) && (search->flags & SPF_BASEPATH)))\n\t\t\t\tdepth += ((search->flags & SPF_EXPLICIT) || (lflags & FSLF_DEPTH_INEXPLICIT));\n\t\t\tfs_finds++;\n\t\t\tfound = search->handle->FindFile(search->handle, loc, filename, pf);\n\t\t\tif (found)\n\t\t\t{\n\t\t\t\tif (!(lflags & FSLF_DONTREFERENCE))\n\t\t\t\t{\n\t\t\t\t\tif ((search->flags & fs_referencetype) != fs_referencetype)\n\t\t\t\t\t\tCon_DPrintf(\"%s became referenced due to %s\\n\", search->purepath, filename);\n\t\t\t\t\tsearch->flags |= fs_referencetype;\n\t\t\t\t}\n\t\t\t\tloc->search = search;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\tif (((lflags & FSLF_IGNOREPURE) || fs_puremode < 2) && found == FF_NOTFOUND)\n\t{\n\t\t// optionally check the non-pure paths too.\n\t\tfor (search = com_searchpaths ; search ; search = search->next)\n\t\t{\n\t\t\tif ((lflags & FSLF_SECUREONLY) && (search->flags & SPF_UNTRUSTED))\n\t\t\t\tcontinue;\n\t\t\tif (!((lflags & FSLF_IGNOREBASEDEPTH) && (search->flags & SPF_BASEPATH)))\n\t\t\t\tdepth += ((search->flags & SPF_EXPLICIT) || (lflags & FSLF_DEPTH_INEXPLICIT));\n\t\t\tfs_finds++;\n\t\t\tfound = search->handle->FindFile(search->handle, loc, filename, pf);\n\t\t\tif (found)\n\t\t\t{\n\t\t\t\tif (!(lflags & FSLF_DONTREFERENCE))\n\t\t\t\t{\n\t\t\t\t\tif ((search->flags & fs_referencetype) != fs_referencetype)\n\t\t\t\t\t\tCon_DPrintf(\"%s became referenced due to %s\\n\", search->purepath, filename);\n\t\t\t\t\tsearch->flags |= fs_referencetype;\n\t\t\t\t}\n\t\t\t\tloc->search = search;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\nfail:\n\tif (found == FF_SYMLINK && !(lflags & FSLF_IGNORELINKS))\n\t{\n\t\tstatic int blocklink;\n\t\tif (blocklink < 4 && loc->len < MAX_QPATH)\n\t\t{\n\t\t\t//read the link target\n\t\t\tchar *s, *b;\n\t\t\tchar targname[MAX_QPATH];\n\t\t\tchar mergedname[MAX_QPATH];\n\t\t\ttargname[loc->len] = 0;\n\t\t\tloc->search->handle->ReadFile(loc->search->handle, loc, targname);\n\n\t\t\t//properlyish unixify\n\t\t\twhile((s = strchr(targname, '\\\\')))\n\t\t\t\t*s = '/';\n\t\t\tif (*targname == '/')\n\t\t\t\tQ_strncpyz(mergedname, targname+1, sizeof(mergedname));\n\t\t\telse\n\t\t\t{\n\t\t\t\tQ_strncpyz(mergedname, filename, sizeof(mergedname));\n\t\t\t\twhile((s = strchr(mergedname, '\\\\')))\n\t\t\t\t\t*s = '/';\n\t\t\t\tb = COM_SkipPath(mergedname);\n\t\t\t\t*b = 0;\n\t\t\t\tfor (s = targname; !strncmp(s, \"../\", 3) && b > mergedname; )\n\t\t\t\t{\n\t\t\t\t\ts += 3;\n\t\t\t\t\tif (b[-1] == '/')\n\t\t\t\t\t\t*--b = 0;\n\t\t\t\t\t*b = 0;\n\t\t\t\t\tb = strrchr(mergedname, '/');\n\t\t\t\t\tif (b)\n\t\t\t\t\t\t*++b = 0;\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\t//no prefix left.\n\t\t\t\t\t\t*mergedname = 0;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tb = mergedname + strlen(mergedname);\n\t\t\t\tQ_strncpyz(b, s, sizeof(mergedname) - (b - mergedname));\n\t\t\t}\n\n\t\t\t//and locate that instead.\n\t\t\tblocklink++;\n\t\t\tdepth = FS_FLocateFile(mergedname, lflags, loc);\n\t\t\tblocklink--;\n\t\t\tif (!loc->search)\n\t\t\t\tCon_Printf(\"Symlink %s -> %s (%s) is dead\\n\", filename, targname, mergedname);\n\t\t\treturn depth;\n\t\t}\n\t}\n\n/*\tif (len>=0)\n\t{\n\t\tif (loc)\n\t\t\tCon_Printf(\"Found %s:%i\\n\", loc->rawname, loc->len);\n\t\telse\n\t\t\tCon_Printf(\"Found %s\\n\", filename);\n\t}\n\telse\n\t\tCon_Printf(\"Failed\\n\");\n*/\n\tif (found == FF_NOTFOUND || found == FF_DIRECTORY || loc->len == -1)\n\t{\n\t\tif (lflags & FSLF_DEEPONFAILURE)\n\t\t\treturn 0x7fffffff;\t//if we're asking for depth, the file is reported to be so far into the filesystem as to be irrelevant.\n\t\treturn 0;\n\t}\n\treturn depth+1;\n}\n\n//returns the location's root package (or gamedir).\n//(aka: loc->search->purepath, but stripping contained nested packs)\nconst char *FS_GetRootPackagePath(flocation_t *loc)\n{\n\tsearchpath_t *sp, *search;\n\n\tfor (sp = loc->search; ;)\n\t{\n\t\tfor (search = com_searchpaths; search; search = search->next)\n\t\t{\n\t\t\tif (search != sp)\n\t\t\t\tif (search->handle->GeneratePureCRC) //only consider files that have a pure hash. this excludes system paths\n\t\t\t\t\tif (!strncmp(search->purepath, sp->purepath, strlen(search->purepath)))\n\t\t\t\t\t\tif (sp->purepath[strlen(search->purepath)] == '/')\t//also ensures that the path gets shorter, avoiding infinite loops as it fights between base+home dirs.\n\t\t\t\t\t\t\tbreak;\n\t\t}\n\t\tif (search)\n\t\t\tsp = search;\n\t\telse\n\t\t\tbreak;\n\t}\n\n\t//\n\tif (sp)\n\t\treturn sp->purepath;\n\treturn NULL;\n}\n\n//returns the package/'gamedir/foo.pk3' filename to tell the client to download\n//unfortunately foo.pk3 may contain a 'bar.pk3' and downloading dir/foo.pk3/bar.pk3 won't work\n//so if loc->search is dir/foo.pk3/bar.pk3 find dir/foo.pk3 instead\nconst char *FS_GetPackageDownloadFilename(flocation_t *loc)\n{\n\tsearchpath_t *sp, *search;\n\n\tfor (sp = loc->search; ;)\n\t{\n\t\tfor (search = com_searchpaths; search; search = search->next)\n\t\t{\n\t\t\tif (search != sp)\n\t\t\t\tif (search->handle->GeneratePureCRC) //only consider files that have a pure hash. this excludes system paths\n\t\t\t\t\tif (!strncmp(search->purepath, sp->purepath, strlen(search->purepath)))\n\t\t\t\t\t\tif (sp->purepath[strlen(search->purepath)] == '/')\t//also ensures that the path gets shorter, avoiding infinite loops as it fights between base+home dirs.\n\t\t\t\t\t\t\tbreak;\n\t\t}\n\t\tif (search)\n\t\t\tsp = search;\n\t\telse\n\t\t\tbreak;\n\t}\n\n\tif (sp && strchr(sp->purepath, '/') && sp->handle->GeneratePureCRC)\t//never allow any packages that are directly sitting in the basedir.\n\t\treturn sp->purepath;\n\treturn NULL;\n}\nqboolean FS_GetLocationForPackageHandle(flocation_t *loc, searchpathfuncs_t *spath, const char *fname)\n{\n\tsearchpath_t *search;\n\tfor (search = com_searchpaths; search; search = search->next)\n\t{\n\t\tif (search->handle == spath)\n\t\t{\n\t\t\tloc->search = search;\n\t\t\treturn spath->FindFile(spath, loc, fname, NULL) == FF_FOUND;\n\t\t}\n\t}\n\treturn false;\n}\nconst char *FS_WhichPackForLocation(flocation_t *loc, unsigned int flags)\n{\n\tchar *ret;\n\tif (!loc->search)\n\t\treturn NULL;\t//huh? not a valid location.\n\n\tif (flags & WP_FULLPATH)\n\t{\n\t\tif (flags & WP_REFERENCE)\n\t\t\tloc->search->flags |= SPF_REFERENCED;\n\t\treturn loc->search->purepath;\n\t}\n\telse\n\t{\n\t\tret = strchr(loc->search->purepath, '/');\n\t\tif (ret)\n\t\t{\n\t\t\tret++;\n\t\t\tif (!strchr(ret, '/'))\n\t\t\t{\n\t\t\t\tif (flags & WP_REFERENCE)\n\t\t\t\t\tloc->search->flags |= SPF_REFERENCED;\n\t\t\t\treturn ret;\n\t\t\t}\n\t\t}\n\t}\n\treturn NULL;\n}\n\nsearchpath_t *FS_GetPackage(const char *package)\n{\n\tsearchpath_t\t*search;\n\n\tfor (search = com_searchpaths ; search ; search = search->next)\n\t{\n\t\tif (!Q_strcasecmp(package, search->purepath))\n\t\t\treturn search;\n\t}\n\treturn NULL;\n}\n/*requires extension*/\nqboolean FS_GetPackageDownloadable(const char *package)\n{\n\tsearchpath_t\t*search = FS_GetPackage(package);\n\n\tif (search)\n\t\treturn !(search->flags & SPF_COPYPROTECTED);\n\treturn false;\n}\n\nchar *FS_GetPackHashes(char *buffer, int buffersize, qboolean referencedonly)\n{\n\tsearchpath_t\t*search;\n\tbuffersize--;\n\t*buffer = 0;\n\n\tif (com_purepaths)\n\t{\n\t\tfor (search = com_purepaths ; search ; search = search->nextpure)\n\t\t{\n\t\t\tQ_strncatz(buffer, va(\"%i \", search->crc_check), buffersize);\n\t\t}\n\t\treturn buffer;\n\t}\n\telse\n\t{\n\t\tfor (search = com_searchpaths ; search ; search = search->next)\n\t\t{\n\t\t\tif (search->crc_check)\n\t\t\t{\n\t\t\t\tQ_strncatz(buffer, va(\"%i \", search->crc_check), buffersize);\n\t\t\t}\n\t\t}\n\t\treturn buffer;\n\t}\n}\n/*\nreferencedonly=0: show all paks\nreferencedonly=1: show only paks that are referenced (q3-compat)\nreferencedonly=2: show all paks, but paks that are referenced are prefixed with a star\next=0: hide extensions (q3-compat)\next=1: show extensions.\n*/\nchar *FS_GetPackNames(char *buffer, int buffersize, int referencedonly, qboolean ext)\n{\n\tchar temp[MAX_OSPATH];\n\tsearchpath_t\t*search;\n\tbuffersize--;\n\t*buffer = 0;\n\n\tif (com_purepaths)\n\t{\n\t\tfor (search = com_purepaths ; search ; search = search->nextpure)\n\t\t{\n\t\t\tif (referencedonly == 0 && !(search->flags & SPF_REFERENCED))\n\t\t\t\tcontinue;\n\t\t\tif (referencedonly == 2 && (search->flags & SPF_REFERENCED))\n\t\t\t\tQ_strncatz(buffer, \"*\", buffersize);\n\n\t\t\tif (!ext)\n\t\t\t{\n\t\t\t\tCOM_StripExtension(search->purepath, temp, sizeof(temp));\n\t\t\t\tQ_strncatz(buffer, va(\"%s \", temp), buffersize);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tQ_strncatz(buffer, va(\"%s \", search->purepath), buffersize);\n\t\t\t}\n\t\t}\n\t\treturn buffer;\n\t}\n\telse\n\t{\n\t\tfor (search = com_searchpaths ; search ; search = search->next)\n\t\t{\n\t\t\tif (search->crc_check)\n\t\t\t{\n\t\t\t\tif (referencedonly == 0 && !(search->flags & SPF_REFERENCED))\n\t\t\t\t\tcontinue;\n\t\t\t\tif (referencedonly == 2 && (search->flags & SPF_REFERENCED))\n\t\t\t\t{\n\t\t\t\t\t// '*' prefix is meant to mean 'referenced'.\n\t\t\t\t\t//really all that means to the client is that it definitely wants to download it.\n\t\t\t\t\t//if its copyrighted, the client shouldn't try to do so, as it won't be allowed.\n\t\t\t\t\tif (!(search->flags & SPF_COPYPROTECTED))\n\t\t\t\t\t\tQ_strncatz(buffer, \"*\", buffersize);\n\t\t\t\t}\n\n\t\t\t\tif (!ext)\n\t\t\t\t{\n\t\t\t\t\tCOM_StripExtension(search->purepath, temp, sizeof(temp));\n\t\t\t\t\tQ_strncatz(buffer, va(\"%s \", temp), buffersize);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tQ_strncatz(buffer, va(\"%s \", search->purepath), buffersize);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn buffer;\n\t}\n}\n\nvoid FS_ReferenceControl(unsigned int refflag, unsigned int resetflags)\n{\n\tsearchpath_t\t*s;\n\n\trefflag &= SPF_REFERENCED;\n\tresetflags &= SPF_REFERENCED;\n\n\tif (resetflags)\n\t{\n\t\tfor (s=com_searchpaths ; s ; s=s->next)\n\t\t{\n\t\t\ts->flags &= ~resetflags;\n\t\t}\n\t}\n\n\tfs_referencetype = refflag;\n}\n\n//outbuf might not be written into\nstatic const char *FS_GetCleanPath(const char *pattern, qboolean silent, char *outbuf, int outlen)\n{\n\tconst char *s;\n\tchar *o;\n\tchar *seg;\n\tchar *end = outbuf + outlen;\n\tstatic float throttletimer;\n\n\ts = pattern;\n\tseg = o = outbuf;\n\tif (!pattern || !*pattern)\n\t{\n\t\tCon_ThrottlePrintf(&throttletimer, 0, \"Error: Empty filename\\n\");\n\t\treturn NULL;\n\t}\n\tfor(;;)\n\t{\n\t\tif (o == end)\n\t\t{\n\t\t\tCon_ThrottlePrintf(&throttletimer, 0, \"Error: filename too long\\n\");\n\t\t\treturn NULL;\n\t\t}\n\t\tif (*s == ':')\n\t\t{\n\t\t\tif (s == pattern+1 && (s[1] == '/' || s[1] == '\\\\'))\n\t\t\t\tCon_ThrottlePrintf(&throttletimer, 0, \"Error: absolute path in filename %s\\n\", pattern);\n\t\t\telse\n\t\t\t\tCon_ThrottlePrintf(&throttletimer, 0, \"Error: alternative data stream in filename %s\\n\", pattern);\n\t\t\treturn NULL;\n\t\t}\n\t\telse if (*s == '\\\\' || *s == '/' || !*s)\n\t\t{\t//end of segment\n\t\t\tif (o == seg)\n\t\t\t{\n\t\t\t\tif (o == outbuf)\n\t\t\t\t{\n\t\t\t\t\tCon_ThrottlePrintf(&throttletimer, 0, \"Error: absolute path in filename %s\\n\", pattern);\n\t\t\t\t\treturn NULL;\n\t\t\t\t}\n\t\t\t\tif (!*s)\n\t\t\t\t{\n\t\t\t\t\t*o++ = '\\0';\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tCon_ThrottlePrintf(&throttletimer, 0, \"Error: empty directory name (%s)\\n\", pattern);\n\t\t\t\ts++;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t//ignore any leading spaces in the name segment\n\t\t\t//it should just make more stuff invalid\n\t\t\twhile (*seg == ' ')\n\t\t\t\tseg++;\n\t\t\tif (!seg[0])\n\t\t\t{\n\t\t\t\tCon_ThrottlePrintf(&throttletimer, 0, \"Error: No filename (%s)\\n\", pattern);\n\t\t\t\treturn NULL;\n\t\t\t}\n\t\t\tif (seg[0] == '.')\n\t\t\t{\n\t\t\t\tif (o == seg+1)\n\t\t\t\t\tCon_ThrottlePrintf(&throttletimer, 0, \"Error: source directory (%s)\\n\", pattern);\n\t\t\t\telse if (seg[1] == '.')\n\t\t\t\t\tCon_ThrottlePrintf(&throttletimer, 0, \"Error: parent directory (%s)\\n\", pattern);\n\t\t\t\telse\n\t\t\t\t\tCon_ThrottlePrintf(&throttletimer, 0, \"Error: hidden name (%s)\\n\", pattern);\n\t\t\t\treturn NULL;\n\t\t\t}\n#if defined(_WIN32) || defined(__CYGWIN__)\n\t\t\t//in win32, we use the //?/ trick to get around filename length restrictions.\n\t\t\t//4-letter reserved paths: comX, lptX\n\t\t\t//we'll allow this elsewhere to save cycles, just try to avoid running it on a fat32 or ntfs filesystem from linux\n\t\t\tif (((seg[0] == 'c' || seg[0] == 'C') &&\n\t\t\t\t (seg[1] == 'o' || seg[1] == 'O') &&\n\t\t\t\t (seg[2] == 'm' || seg[2] == 'M') &&\n\t\t\t\t (seg[3] >= '0' && seg[3] <= '9')) ||\n\t\t\t\t((seg[0] == 'l' || seg[0] == 'L') &&\n\t\t\t\t (seg[1] == 'p' || seg[1] == 'P') &&\n\t\t\t\t (seg[2] == 't' || seg[2] == 'T') &&\n\t\t\t\t (seg[3] >= '0' && seg[3] <= '9')))\n\t\t\t{\n\t\t\t\tif (o == seg+4 || seg[4] == ' '|| seg[4] == '\\t' || seg[4] == '.')\n\t\t\t\t{\n\t\t\t\t\tCon_ThrottlePrintf(&throttletimer, 0, \"Error: reserved name in path (%c%c%c%c in %s)\\n\", seg[0], seg[1], seg[2], seg[3], pattern);\n\t\t\t\t\treturn NULL;\n\t\t\t\t}\n\t\t\t}\n\t\t\t//3 letter reserved paths: con, nul, prn\n\t\t\tif (((seg[0] == 'c' || seg[0] == 'C') &&\n\t\t\t\t (seg[1] == 'o' || seg[1] == 'O') &&\n\t\t\t\t (seg[2] == 'n' || seg[2] == 'N')) ||\n\t\t\t\t((seg[0] == 'p' || seg[0] == 'P') &&\n\t\t\t\t (seg[1] == 'r' || seg[1] == 'R') &&\n\t\t\t\t (seg[2] == 'n' || seg[2] == 'N')) ||\n\t\t\t\t((seg[0] == 'n' || seg[0] == 'N') &&\n\t\t\t\t (seg[1] == 'u' || seg[1] == 'U') &&\n\t\t\t\t (seg[2] == 'l' || seg[2] == 'L')))\n\t\t\t{\n\t\t\t\tif (o == seg+3 || seg[3] == ' '|| seg[3] == '\\t' || seg[3] == '.')\n\t\t\t\t{\n\t\t\t\t\tCon_ThrottlePrintf(&throttletimer, 0, \"Error: reserved name in path (%c%c%c in %s)\\n\", seg[0], seg[1], seg[2], pattern);\n\t\t\t\t\treturn NULL;\n\t\t\t\t}\n\t\t\t}\n#endif\n\n\t\t\tif (*s++)\n\t\t\t\t*o++ = '/';\n\t\t\telse\n\t\t\t{\n\t\t\t\t*o++ = '\\0';\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tseg = o;\n\t\t}\n\t\telse\n\t\t\t*o++ = *s++;\n\t}\n\n//\tSys_Printf(\"%s changed to %s\\n\", pattern, outbuf);\n\treturn outbuf;\n}\n\nstatic vfsfile_t *VFS_Filter(const char *filename, vfsfile_t *handle)\n{\n//\tchar *ext;\n\tif (!filename)\n\t\treturn handle;\t//block any filtering (so we don't do stupid stuff like having servers pre-decompressing when downloading)\n\tif (!handle || !handle->ReadBytes || handle->seekstyle == SS_UNSEEKABLE)\t//only on readonly files for which we can undo any header read damage\n\t\treturn handle;\n//\tif (handle->seekstyle == SS_SLOW)\n//\t\treturn handle;\t//we only peek at the header, so rewinding shouldn't be too expensive at least...\n//\tconst char *ext = COM_GetFileExtension(filename, NULL);\n#ifdef AVAIL_XZDEC\n//\tif (!Q_strcasecmp(ext, \".xz\"))\n\t{\n\t\tvfsfile_t *nh;\n\t\tnh = FS_XZ_DecompressReadFilter(handle);\n\t\tif (nh!=handle)\n\t\t\treturn nh;\n\t}\n#endif\n#ifdef AVAIL_GZDEC\n//\tif (!Q_strcasecmp(ext, \".gz\"))\n\t{\n\t\treturn FS_DecompressGZip(handle, NULL);\n\t}\n#endif\n\treturn handle;\n}\n\nstatic qboolean FS_NativePath(const char *fname, enum fs_relative relativeto, char *out, int outlen, qboolean fordisplay)\n{\n\tflocation_t loc;\n\tint i;\n\tchar cleanname[MAX_QPATH];\n\tchar *last;\n\tqboolean wasbase;\t//to handle out-of-order base/game dirs.\n\tint nlen;\n\n\tif (!fs_hidesyspaths.ival)\n\t\tfordisplay = false;\t//just show system paths\n\n\tif (relativeto == FS_SYSTEM)\n\t{\n\t\t//system is already the native path. we can just pass it through. perhaps we should clean it up first however, although that's just making sure all \\ are /\t\n\t\tif (fordisplay)\n\t\t{\n\t\t\tif (com_homepathenabled && !strncmp(fname, com_homepath, strlen(com_homepath)))\t//'FS_HOME'\n\t\t\t\tQ_snprintfz(out, outlen, \"$homedir/%s\", fname+strlen(com_homepath));\n\t\t\telse if (!strncmp(fname, com_gamepath, strlen(com_gamepath)))\t\t\t\t\t//FS_ROOT-THAT-ISNT-HOME\n\t\t\t\tQ_snprintfz(out, outlen, \"$basedir/%s\", fname+strlen(com_gamepath));\n\t\t\telse if (host_parms.binarydir && !strncmp(fname, host_parms.binarydir, strlen(host_parms.binarydir)))\t//FS_BINARYDIR\n\t\t\t\tQ_snprintfz(out, outlen, \"$bindir/%s\", fname+strlen(host_parms.binarydir));\n#ifdef FTE_LIBRARY_PATH\n\t\t\telse if (!strncmp(fname, STRINGIFY(FTE_LIBRARY_PATH)\"/\", strlen(STRINGIFY(FTE_LIBRARY_PATH)\"/\")))\t//FS_LIBRARYDIR\n\t\t\t\tQ_snprintfz(out, outlen, \"$libdir/%s\", fname+strlen(STRINGIFY(FTE_LIBRARY_PATH)\"/\"));\n#endif\n\t\t\telse\t//should try bindir\n\t\t\t\tQ_snprintfz(out, outlen, \"$system/%s\", COM_SkipPath(fname));\t\t\t//FS_SYSTEM :(\n\t\t}\n\t\telse\n\t\t\tQ_snprintfz(out, outlen, \"%s\", fname);\n\n#ifdef _WIN32\n\t\tfor (; *out; out++)\n\t\t{\n\t\t\tif (*out == '\\\\')\n\t\t\t\t*out = '/';\n\t\t}\n#endif\n\t\treturn true;\n\t}\n\n\tif (*fname == 0)\n\t{\n\t\t//this is sometimes used to query the actual path.\n\t\t//don't alow it for other stuff though.\n\t\tif (relativeto != FS_ROOT && relativeto != FS_BINARYPATH && relativeto != FS_LIBRARYPATH && relativeto != FS_GAMEONLY)\n\t\t\treturn false;\n\t}\n\telse\n\t{\n\t\tfname = FS_GetCleanPath(fname, false, cleanname, sizeof(cleanname));\n\t\tif (!fname)\n\t\t\treturn false;\n\t}\n\n\tswitch (relativeto)\n\t{\n\tcase FS_GAME: //this is really for diagnostic type stuff...\n\t\tif (FS_FLocateFile(fname, FSLF_IFFOUND, &loc))\n\t\t{\n\t\t\tif (fordisplay)\n\t\t\t{\n\t\t\t\tif (!strncmp(com_homepath, loc.search->logicalpath, strlen(com_homepath)))\n\t\t\t\t\tnlen = Q_snprintfz(out, outlen, \"$homedir/%s/%s\", loc.search->purepath, fname);\n\t\t\t\telse\n\t\t\t\t\tnlen = Q_snprintfz(out, outlen, \"$basedir/%s/%s\", loc.search->purepath, fname);\n\t\t\t}\n\t\t\telse\n\t\t\t\tnlen = Q_snprintfz(out, outlen, \"%s/%s\", loc.search->logicalpath, fname);\n\t\t\tbreak;\n\t\t}\n\t\t//fallthrough\n\tcase FS_GAMEONLY:\n\t\tif (com_homepathenabled)\n\t\t\tnlen = Q_snprintfz(out, outlen, \"%s%s/%s\", fordisplay?\"$homedir/\":com_homepath, gamedirfile, fname);\n\t\telse\n\t\t\tnlen = Q_snprintfz(out, outlen, \"%s%s/%s\", fordisplay?\"$basedir/\":com_gamepath, gamedirfile, fname);\n\t\tbreak;\n\tcase FS_LIBRARYPATH:\n#ifdef FTE_LIBRARY_PATH\n\t\tif (fordisplay)\n\t\t\tnlen = Q_snprintfz(out, outlen, \"$libdir/%s\", fname);\n\t\telse\n\t\t\tnlen = Q_snprintfz(out, outlen, STRINGIFY(FTE_LIBRARY_PATH)\"/%s\", fname);\n\t\tbreak;\n#else\n\t\treturn false;\n#endif\n\tcase FS_BINARYPATH:\n\t\tif (host_parms.binarydir && *host_parms.binarydir)\n\t\t\tnlen = Q_snprintfz(out, outlen, \"%s%s\", fordisplay?\"$bindir/\":host_parms.binarydir, fname);\n\t\telse\n\t\t\tnlen = Q_snprintfz(out, outlen, \"%s%s\", fordisplay?\"$basedir/\":host_parms.basedir, fname);\n\t\tbreak;\n\tcase FS_ROOT:\n\t\tif (com_installer)\n\t\t\treturn false;\n\t\tif (com_homepathenabled)\n\t\t\tnlen = Q_snprintfz(out, outlen, \"%s%s\", fordisplay?\"$homedir/\":com_homepath, fname);\n\t\telse\n\t\t\tnlen = Q_snprintfz(out, outlen, \"%s%s\", fordisplay?\"$basedir/\":com_gamepath, fname);\n\t\tbreak;\n\n\tcase FS_BASEGAMEONLY:\t// fte/\n\t\tlast = NULL;\n\t\tfor (i = 0; i < countof(fs_manifest->gamepath); i++)\n\t\t{\n\t\t\tif (fs_manifest && (fs_manifest->gamepath[i].flags&GAMEDIR_BASEGAME) && fs_manifest->gamepath[i].path)\n\t\t\t{\n\t\t\t\tif (fs_manifest->gamepath[i].flags & GAMEDIR_SPECIAL)\n\t\t\t\t\tcontinue;\n\t\t\t\tlast = fs_manifest->gamepath[i].path;\n\t\t\t}\n\t\t}\n\t\tif (!last)\n\t\t\treturn false;\t//eep?\n\t\tif (com_homepathenabled)\n\t\t\tnlen = Q_snprintfz(out, outlen, \"%s%s/%s\", fordisplay?\"$homedir/\":com_homepath, last, fname);\n\t\telse\n\t\t\tnlen = Q_snprintfz(out, outlen, \"%s%s/%s\", fordisplay?\"$basedir/\":com_gamepath, last, fname);\n\t\tbreak;\n\tcase FS_PUBGAMEONLY:\t// $gamedir/ or qw/ but not fte/\n\t\tlast = NULL;\n\t\twasbase = true;\n\t\tfor (i = 0; i < countof(fs_manifest->gamepath); i++)\n\t\t{\n\t\t\tif (fs_manifest && fs_manifest->gamepath[i].path)\n\t\t\t{\n\t\t\t\tqboolean isbase = fs_manifest->gamepath[i].flags&GAMEDIR_BASEGAME;\n\t\t\t\tif (fs_manifest->gamepath[i].flags&(GAMEDIR_PRIVATE|GAMEDIR_SPECIAL))\n\t\t\t\t\tcontinue;\n\t\t\t\tif (isbase && !wasbase)\n\t\t\t\t\tcontinue;\n\t\t\t\twasbase = isbase;\n\t\t\t\tlast = fs_manifest->gamepath[i].path;\n\t\t\t}\n\t\t}\n\t\tif (!last)\n\t\t\treturn false;\t//eep?\n\t\tif (com_homepathenabled)\n\t\t\tnlen = Q_snprintfz(out, outlen, \"%s%s/%s\", fordisplay?\"$homedir/\":com_homepath, last, fname);\n\t\telse\n\t\t\tnlen = Q_snprintfz(out, outlen, \"%s%s/%s\", fordisplay?\"$basedir/\":com_gamepath, last, fname);\n\t\tbreak;\n\tcase FS_PUBBASEGAMEONLY:\t// qw/ (fixme: should be the last non-private basedir)\n\t\tlast = NULL;\n\t\tfor (i = 0; i < countof(fs_manifest->gamepath); i++)\n\t\t{\n\t\t\tif (fs_manifest && (fs_manifest->gamepath[i].flags&GAMEDIR_BASEGAME) && fs_manifest->gamepath[i].path)\n\t\t\t{\n\t\t\t\tif (fs_manifest->gamepath[i].flags&(GAMEDIR_PRIVATE|GAMEDIR_SPECIAL))\n\t\t\t\t\tcontinue;\n\t\t\t\tlast = fs_manifest->gamepath[i].path;\n\t\t\t}\n\t\t}\n\t\tif (!last)\n\t\t\treturn false;\t//eep?\n\t\tif (com_homepathenabled)\n\t\t\tnlen = Q_snprintfz(out, outlen, \"%s%s/%s\", fordisplay?\"$homedir/\":com_homepath, last, fname);\n\t\telse\n\t\t\tnlen = Q_snprintfz(out, outlen, \"%s%s/%s\", fordisplay?\"$basedir/\":com_gamepath, last, fname);\n\t\tbreak;\n\tdefault:\n\t\tSys_Error(\"FS_NativePath case not handled\\n\");\n\t}\n\treturn nlen < outlen;\n}\n\nqboolean FS_SystemPath(const char *fname, enum fs_relative relativeto, char *out, int outlen)\n{\n\treturn FS_NativePath(fname, relativeto, out, outlen, false);\n}\nqboolean FS_DisplayPath(const char *fname, enum fs_relative relativeto, char *out, int outlen)\n{\n\tif (outlen)\n\t\t*out = 0;\n\treturn FS_NativePath(fname, relativeto, out, outlen, true);\n}\n\n//returns false to stop the enumeration. check the return value of the fs enumerator to see if it was canceled by this return value.\nstatic int QDECL FS_NullFSEnumerator(const char *fname, qofs_t fsize, time_t mtime, void *parm, searchpathfuncs_t *spath)\n{\n\treturn false;\n}\n\n//opens a file in the same (writable) path that contains an existing version of the file or one of the other patterns listed\nvfsfile_t *FS_OpenWithFriends(const char *fname, char *displayname, size_t displaynamesize, int numfriends, ...)\n{\n\tsearchpath_t *search;\n\tsearchpath_t *lastwritable = NULL;\n\tflocation_t loc;\n\tva_list ap;\n\tint i;\n\tchar cleanname[MAX_QPATH];\n\n\tfname = FS_GetCleanPath(fname, false, cleanname, sizeof(cleanname));\n\tif (!fname)\n\t\treturn NULL;\n\n\tfor (search = com_searchpaths; search ; search = search->next)\n\t{\n\t\tif ((search->flags & SPF_EXPLICIT) && (search->flags & SPF_WRITABLE))\n\t\t\tlastwritable = search;\n\t\tif (search->handle->FindFile(search->handle, &loc, fname, NULL))\n\t\t\tbreak;\n\t\t\n\t\tva_start(ap, numfriends);\n\t\tfor (i = 0; i < numfriends; i++)\n\t\t{\n\t\t\tchar *path = va_arg(ap, char*);\n\t\t\tif (!search->handle->EnumerateFiles(search->handle, path, FS_NullFSEnumerator, NULL))\n\t\t\t\tbreak;\n\t\t}\n\t\tva_end(ap);\n\t\tif (i < numfriends)\n\t\t\tbreak;\n\t}\n\tif (lastwritable)\n\t{\n\t\tchar sysname[MAX_OSPATH];\n\t\tvfsfile_t *f;\n\t\t//figure out the system path\n\t\tQ_strncpyz(sysname, lastwritable->logicalpath, sizeof(sysname));\n\t\tFS_CleanDir(sysname, sizeof(sysname));\n\t\tQ_strncatz(sysname, fname, sizeof(sysname));\n\n\t\t//create the dir if needed and open the file.\n\t\tCOM_CreatePath(sysname);\n\t\tf = VFSOS_Open(sysname, \"wbp\");\n\n\t\tif (fs_hidesyspaths.ival)\n\t\t{\n\t\t\tif (*com_homepath && !strncmp(com_homepath, lastwritable->logicalpath, strlen(com_homepath)))\n\t\t\t\tQ_snprintfz(displayname, displaynamesize, \"$homedir/%s/%s\", lastwritable->purepath, fname);\n\t\t\telse\n\t\t\t\tQ_snprintfz(displayname, displaynamesize, \"$basedir/%s/%s\", lastwritable->purepath, fname);\n\t\t}\n\t\telse\n\t\t\tQ_strncpyz(displayname, sysname, displaynamesize);\n\t\treturn f;\n\t}\n\tFS_DisplayPath(fname, FS_GAMEONLY, displayname, displaynamesize);\n\treturn NULL;\n}\n\n//returns false if the string didn't fit. we're not trying to be clever and reallocate the buffer\nstatic qboolean try_snprintf(char *buffer, size_t size, const char *format, ...)\n{\n\tsize_t ret;\n\tva_list\t\targptr;\n\n\tva_start (argptr, format);\n#ifdef _WIN32\n#undef _vsnprintf\n\tret = _vsnprintf(buffer, size, format, argptr);\n#define _vsnprintf unsafe_vsnprintf\n#else\n\tret = vsnprintf (buffer, size, format,argptr);\n#endif\n\tva_end (argptr);\n\tif (ret > size-1)\t//should cope with microsoft's -1s and linuxes total-length return values.\n\t\treturn false;\n\treturn true;\n}\n\n/*locates and opens a file\nmodes:\nr = read\nw = write\na = append\nt = text mode (because windows sucks). binary is otherwise assumed.\np = persist (ie: saved games and configs, but not downloads or large content)\n*/\nvfsfile_t *QDECL FS_OpenVFS(const char *filename, const char *mode, enum fs_relative relativeto)\n{\n\tchar cleanname[MAX_QPATH];\n\tchar fullname[MAX_OSPATH];\n\tflocation_t loc;\n\tvfsfile_t *vfs;\n\n\t//eventually, this function will be the *ONLY* way to get at files\n\n\tfs_accessed_time = realtime;\n\n\tif (fs_readonly && *mode == 'w')\n\t\treturn NULL;\n\n\tif (!strncmp(filename, \"file:\", 5))\n\t{\n\t\tif (fs_allowfileuri || relativeto == FS_SYSTEM)\n\t\t{\n\t\t\tif (Sys_ResolveFileURL(filename, strlen(filename), fullname, sizeof(fullname)))\n\t\t\t\treturn VFSOS_Open(fullname, mode);\n\t\t}\n\t\treturn NULL;\n\t}\n\n\tif (relativeto == FS_SYSTEM)\n\t\treturn VFSOS_Open(filename, mode);\n\n\t//blanket-bans\n\n\tfilename = FS_GetCleanPath(filename, false, cleanname, sizeof(cleanname));\n\tif (!filename)\n\t\treturn NULL;\n\n#ifdef _DEBUG\n\tif (strcmp(mode, \"rb\"))\n\t\tif (strcmp(mode, \"r+b\"))\n\t\t\tif (strcmp(mode, \"wb\"))\n\t\t\t\tif (strcmp(mode, \"w+b\"))\n\t\t\t\t\tif (strcmp(mode, \"ab\"))\n\t\t\t\t\t\tif (strcmp(mode, \"wbp\"))\n\t\t\t\t\t\t\treturn NULL; //urm, unable to write/append\n#endif\n\n\t//if there can only be one file (eg: write access) find out where it is.\n\tswitch (relativeto)\n\t{\n\tcase FS_GAMEONLY:\t//OS access only, no paks. Used for (re)writing files.\n\t\tvfs = NULL;\n\t\t//FIXME: go via a searchpath, because then the fscache can be selectively updated\n\t\tif (com_homepathenabled)\n\t\t{\n\t\t\tif (gameonly_homedir)\n\t\t\t{\n\t\t\t\tif ((*mode == 'w' && gameonly_gamedir->handle->CreateFile)\n\t\t\t\t\t\t? gameonly_homedir->handle->CreateFile(gameonly_homedir->handle, &loc, filename)\n\t\t\t\t\t\t: gameonly_homedir->handle->FindFile  (gameonly_homedir->handle, &loc, filename, NULL))\n\t\t\t\t\tvfs = gameonly_homedir->handle->OpenVFS   (gameonly_homedir->handle, &loc, mode);\n\t\t\t\telse\n\t\t\t\t\tvfs = NULL;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (!try_snprintf(fullname, sizeof(fullname), \"%s%s/%s\", com_homepath, gamedirfile, filename))\n\t\t\t\t\treturn NULL;\n\t\t\t\tif (*mode == 'w')\n\t\t\t\t\tCOM_CreatePath(fullname);\n\t\t\t\tvfs = VFSOS_Open(fullname, mode);\n\t\t\t}\n\t\t}\n\t\tif (!vfs && *gamedirfile)\n\t\t{\n\t\t\tif (gameonly_gamedir)\n\t\t\t{\n\t\t\t\tif ((*mode == 'w' && gameonly_gamedir->handle->CreateFile)\n\t\t\t\t\t\t? gameonly_gamedir->handle->CreateFile(gameonly_gamedir->handle, &loc, filename)\n\t\t\t\t\t\t: gameonly_gamedir->handle->FindFile  (gameonly_gamedir->handle, &loc, filename, NULL))\n\t\t\t\t\tvfs = gameonly_gamedir->handle->OpenVFS   (gameonly_gamedir->handle, &loc, mode);\n\t\t\t\telse\n\t\t\t\t\tvfs = NULL;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (!try_snprintf(fullname, sizeof(fullname), \"%s%s/%s\", com_gamepath, gamedirfile, filename))\n\t\t\t\t\treturn NULL;\n\t\t\t\tif (*mode == 'w')\n\t\t\t\t\tCOM_CreatePath(fullname);\n\t\t\t\tvfs =  VFSOS_Open(fullname, mode);\n\t\t\t}\n\t\t}\n\t\tif (vfs || !(*mode == 'w' || *mode == 'a'))\n\t\t\treturn vfs;\n\t\t//fall through\n\tcase FS_PUBGAMEONLY:\t\t//used for $gamedir/downloads\n\tcase FS_BASEGAMEONLY:\t\t//used for fte/configs/*\n\tcase FS_PUBBASEGAMEONLY:\t//used for qw/skins/*\n\t\tif (!FS_SystemPath(filename, relativeto, fullname, sizeof(fullname)))\n\t\t\treturn NULL;\n\t\tif (*mode == 'w')\n\t\t\tCOM_CreatePath(fullname);\n\t\treturn VFSOS_Open(fullname, mode);\n\tcase FS_GAME:\t//load from paks in preference to system paths. overwriting be damned.\n\t\tif (!FS_SystemPath(filename, relativeto, fullname, sizeof(fullname)))\n\t\t\treturn NULL;\n\t\tbreak;\n\tcase FS_LIBRARYPATH:\n\tcase FS_BINARYPATH:\n\t\tif (!FS_SystemPath(filename, relativeto, fullname, sizeof(fullname)))\n\t\t\treturn NULL;\n\t\tif (*mode == 'w')\n\t\t\tCOM_CreatePath(fullname);\n\t\treturn VFSOS_Open(fullname, mode);\n\tcase FS_ROOT:\t//always bypass packs and gamedirs\n\t\tif (com_installer)\n\t\t\treturn NULL;\n\t\tif (com_homepathenabled)\n\t\t{\n\t\t\tif (!try_snprintf(fullname, sizeof(fullname), \"%s%s\", com_homepath, filename))\n\t\t\t\treturn NULL;\n\t\t\tif (*mode == 'w')\n\t\t\t\tCOM_CreatePath(fullname);\n\t\t\tvfs = VFSOS_Open(fullname, mode);\n\t\t\tif (vfs)\n\t\t\t\treturn vfs;\n\t\t}\n\t\tif (!try_snprintf(fullname, sizeof(fullname), \"%s%s\", com_gamepath, filename))\n\t\t\treturn NULL;\n\t\tif (*mode == 'w')\n\t\t\tCOM_CreatePath(fullname);\n\t\treturn VFSOS_Open(fullname, mode);\n\tdefault:\n\t\tSys_Error(\"FS_OpenVFS: Bad relative path (%i)\", relativeto);\n\t\tbreak;\n\t}\n\n\tFS_FLocateFile(filename, FSLF_IFFOUND, &loc);\n\n\tif (loc.search)\n\t{\n\t\treturn loc.search->handle->OpenVFS(loc.search->handle, &loc, mode);\n\t}\n\n\t//if we're meant to be writing, best write to it.\n\tif (strchr(mode , 'w') || strchr(mode , 'a'))\n\t{\n\t\tCOM_CreatePath(fullname);\n\t\treturn VFSOS_Open(fullname, mode);\n\t}\n\treturn NULL;\n}\n\nqboolean FS_GetLocMTime(flocation_t *location, time_t *modtime)\n{\n\t*modtime = 0;\n\tif (!location->search->handle->FileStat || !location->search->handle->FileStat(location->search->handle, location, modtime))\n\t\treturn false;\n\treturn true;\n}\n\n/*opens a vfsfile from an already discovered location*/\nvfsfile_t *FS_OpenReadLocation(const char *fname, flocation_t *location)\n{\n\tif (location->search)\n\t{\n\t\treturn VFS_Filter(fname, location->search->handle->OpenVFS(location->search->handle, location, \"rb\"));\n\t}\n\treturn NULL;\n}\n\nqboolean FS_Rename2(const char *oldf, const char *newf, enum fs_relative oldrelativeto, enum fs_relative newrelativeto)\n{\n\tchar oldfullname[MAX_OSPATH];\n\tchar newfullname[MAX_OSPATH];\n\n\tif (!FS_SystemPath(oldf, oldrelativeto, oldfullname, sizeof(oldfullname)))\n\t\treturn false;\n\tif (!FS_SystemPath(newf, newrelativeto, newfullname, sizeof(newfullname)))\n\t\treturn false;\n\n\tFS_CreatePath(newf, newrelativeto);\n\tif (Sys_Rename(oldfullname, newfullname))\n\t{\n\t\tif (oldrelativeto >= FS_GAME)\n\t\t\tFS_FlushFSHashRemoved(oldf);\n\t\tif (newrelativeto >= FS_GAME)\n\t\t\tFS_FlushFSHashWritten(newf);\n\t\treturn true;\n\t}\n\treturn false;\n}\nqboolean FS_Rename(const char *oldf, const char *newf, enum fs_relative relativeto)\n{\n\tchar cleanold[MAX_QPATH];\n\tchar cleannew[MAX_QPATH];\n\tif (relativeto != FS_SYSTEM)\n\t{\n\t\toldf = FS_GetCleanPath(oldf, false, cleanold, sizeof(cleanold));\n\t\tnewf = FS_GetCleanPath(newf, false, cleannew, sizeof(cleannew));\n\t}\n\treturn FS_Rename2(oldf, newf, relativeto, relativeto);\n}\nqboolean FS_Remove(const char *fname, enum fs_relative relativeto)\n{\n\tchar fullname[MAX_OSPATH];\n\n\tif (!FS_SystemPath(fname, relativeto, fullname, sizeof(fullname)))\n\t\treturn false;\n\n\tif (Sys_remove (fullname))\n\t{\n\t\tif (relativeto >= FS_GAME)\n\t\t\tFS_FlushFSHashRemoved(fname);\n\t\treturn true;\n\t}\n\treturn false;\n}\nstatic int QDECL FS_RemoveTreeCallback(const char *fname, qofs_t fsize, time_t mtime, void *parm, searchpathfuncs_t *spath)\n{\n\tchar fullname[MAX_OSPATH];\n\tif (*fname && fname[strlen(fname)-1] == '/')\n\t{\n\t\tQ_snprintfz(fullname, sizeof(fullname), \"%s*\", fname);\n\t\tif (!spath->EnumerateFiles(spath, fullname, FS_RemoveTreeCallback, NULL))\n\t\t\treturn false;\n\t}\n\tif (!spath->RemoveFile)\n\t\treturn false;\t//can't remove...\n\n\tif (!spath->RemoveFile(spath, fname))\n\t{\n\t\tCon_Printf(\"Unable to delete %s\\n\", fname);\n\t\treturn false;\t//remove failed\n\t}\n\tFS_RebuildFSHash_Update(fname);\n\treturn true;\n}\nqboolean FS_RemoveTree(searchpathfuncs_t *pathhandle, const char *fname)\n{\t//this requires that the searchpath a) supports remove. b) supports listing directories...\n\t//path is expected to have a trailing /\n\n\t/*char cleaned[MAX_QPATH];\n\tfname = FS_GetCleanPath(fname, false, cleaned, sizeof(cleaned));\n\tif (!fname)\n\t\treturn false;*/\n\n\tif (fs_readonly)\n\t\treturn false;\n\n\t//FIXME: don't cross filesystems.\n\t//FIXME: remove dir symlinks instead of the target's contents.\n\tif (FS_RemoveTreeCallback(fname, 0, 0, NULL, pathhandle))\n\t\treturn true;\n\treturn false;\n}\n\n//create a path for the given filename (dir-only must have trailing slash)\nvoid FS_CreatePath(const char *pname, enum fs_relative relativeto)\n{\n\tchar fullname[MAX_OSPATH];\n\tif (!FS_SystemPath(pname, relativeto, fullname, sizeof(fullname)))\n\t\treturn;\n\n\tCOM_CreatePath(fullname);\n}\n\n//FIXME: why is this qofs_t and not size_t?!?\nvoid *FS_MallocFile(const char *filename, enum fs_relative relativeto, qofs_t *filesize)\n{\n\tvfsfile_t *f;\n\tqbyte *buf;\n\tqofs_t len;\n\n\tf = FS_OpenVFS(filename, \"rb\", relativeto);\n\tif (!f)\n\t\treturn NULL;\n\tlen = VFS_GETLEN(f);\n\tif (filesize)\n\t\t*filesize = len;\n\tif (len >= ~(size_t)0)\n\t{\n\t\tVFS_CLOSE(f);\n\t\tCon_Printf(CON_ERROR\"File %s: too large\\n\", filename);\n\t\treturn NULL;\n\t}\n\n\tbuf = (qbyte*)BZ_Malloc(len+1);\n\tif (!buf)\t//this could be a soft error, but I don't want to have to deal with users reporting misc unrelated issues (and frankly most malloc failures are due to OOB writes)\n\t\tSys_Error (\"FS_MallocFile: out of memory loading %s\", filename);\n\n\t((qbyte *)buf)[len] = 0;\n\n\tVFS_READ(f, buf, len);\n\tVFS_CLOSE(f);\n\treturn buf;\n}\nqboolean FS_WriteFile (const char *filename, const void *data, int len, enum fs_relative relativeto)\n{\n\tvfsfile_t *f;\n\tFS_CreatePath(filename, relativeto);\n\tf = FS_OpenVFS(filename, \"wbp\", relativeto);\n\tif (!f)\n\t\treturn false;\n\tVFS_WRITE(f, data, len);\n\tVFS_CLOSE(f);\n\n\treturn true;\n}\n\nqboolean FS_Copy(const char *source, const char *dest, enum fs_relative relativesource, enum fs_relative relativedest)\n{\n\tvfsfile_t *d, *s;\n\tchar buffer[8192*8];\n\tint read;\n\tqboolean result = false;\n\tFS_CreatePath(dest, relativedest);\n\ts = FS_OpenVFS(source, \"rb\", relativesource);\n\tif (s)\n\t{\n\t\td = FS_OpenVFS(dest, \"wbp\", relativedest);\n\t\tif (d)\n\t\t{\n\t\t\tresult = true;\n\n\t\t\tfor (;;)\n\t\t\t{\n\t\t\t\tread = VFS_READ(s, buffer, sizeof(buffer));\n\t\t\t\tif (read <= 0)\n\t\t\t\t\tbreak;\n\t\t\t\tif (VFS_WRITE(d, buffer, read) != read)\n\t\t\t\t{\n\t\t\t\t\tresult = false;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tVFS_CLOSE(d);\n\n\t\t\tif (!result)\n\t\t\t\tFS_Remove(dest, relativedest);\n\t\t}\n\t\tVFS_CLOSE(s);\n\t}\n\treturn result;\n}\n\nstatic qbyte\t*loadbuf;\nstatic int\t\tloadsize;\n\n/*\n============\nCOM_LoadFile\n\nFilename are reletive to the quake directory.\nAlways appends a 0 qbyte to the loaded data.\n============\n*/\nqbyte *COM_LoadFile (const char *path, unsigned int locateflags, int usehunk, size_t *filesize)\n{\n\tvfsfile_t *f;\n\tqbyte *buf;\n\tqofs_t len;\n\tflocation_t loc;\n\t\n\tlocateflags &= ~FSLF_DEEPONFAILURE;\t//disable any flags that can't be supported here\n\n\tif (!FS_FLocateFile(path, locateflags, &loc) || !loc.search)\n\t\treturn NULL;\t//wasn't found\n\n\tif (loc.len > 0x7fffffff)\t//don't malloc 5000gb sparse files or anything crazy on a 32bit system...\n\t\treturn NULL;\n\n\tfs_accessed_time = realtime;\n\n\tf = loc.search->handle->OpenVFS(loc.search->handle, &loc, \"rb\");\n\tif (!f)\n\t\treturn NULL;\n\n\tlen = VFS_GETLEN(f);\n\tif (filesize)\n\t\t*filesize = len;\n\n\tif (usehunk == 2 || usehunk == 4 || usehunk == 6)\n\t\tCOM_AssertMainThread(\"COM_LoadFile+hunk\");\n\n\tif (usehunk == 0)\n\t\tbuf = (qbyte*)Z_Malloc (len+1);\n\telse if (usehunk == 2)\n\t\tbuf = (qbyte*)Hunk_TempAlloc (len+1);\n\telse if (usehunk == 4)\n\t{\n\t\tif (len+1 > loadsize)\n\t\t\tbuf = (qbyte*)Hunk_TempAlloc (len+1);\n\t\telse\n\t\t\tbuf = loadbuf;\n\t}\n\telse if (usehunk == 5)\n\t\tbuf = (qbyte*)BZ_Malloc(len+1);\n\telse if (usehunk == 6)\n\t\tbuf = (qbyte*)Hunk_TempAllocMore (len+1);\n\telse\n\t{\n\t\tSys_Error (\"COM_LoadFile: bad usehunk\");\n\t\tbuf = NULL;\n\t}\n\n\tif (!buf)\n\t\tSys_Error (\"COM_LoadFile: not enough space for %s\", path);\n\n\t((qbyte *)buf)[len] = 0;\n\n\tVFS_READ(f, buf, len);\n\tVFS_CLOSE(f);\n\n\treturn buf;\n}\n\nvoid *FS_LoadMallocFile (const char *path, size_t *fsize)\n{\n\treturn COM_LoadFile (path, 0, 5, fsize);\n}\nqbyte *FS_LoadMallocFileFlags (const char *path, unsigned int locateflags, size_t *fsize)\n{\n\treturn COM_LoadFile (path, locateflags, 5, fsize);\n}\n\nvoid *FS_LoadMallocGroupFile(zonegroup_t *ctx, char *path, size_t *fsize, qboolean filters)\n{\n\tchar *mem = NULL;\n\tvfsfile_t *f = FS_OpenVFS(path, \"rb\", FS_GAME);\n\tif (f && filters)\n\t\tf = VFS_Filter(path, f);\n\tif (f)\n\t{\n\t\tint len = VFS_GETLEN(f);\n\t\tif (ctx)\n\t\t\tmem = ZG_Malloc(ctx, len+1);\n\t\telse\n\t\t\tmem = BZ_Malloc(len+1);\n\t\tif (mem)\n\t\t{\n\t\t\tmem[len] = 0;\n\t\t\tif (VFS_READ(f, mem, len) == len)\n\t\t\t\t*fsize = len;\n\t\t\telse\n\t\t\t\tmem = NULL;\n\t\t}\n\n\t\tVFS_CLOSE(f);\n\t}\n\treturn mem;\n}\n\nqbyte *COM_LoadTempFile (const char *path, unsigned int locateflags, size_t *fsize)\n{\n\treturn COM_LoadFile (path, locateflags, 2, fsize);\n}\nqbyte *COM_LoadTempMoreFile (const char *path, size_t *fsize)\n{\n\treturn COM_LoadFile (path, 0, 6, fsize);\n}\n\n// uses temp hunk if larger than bufsize\nqbyte *QDECL COM_LoadStackFile (const char *path, void *buffer, int bufsize, size_t *fsize)\n{\n\tqbyte\t*buf;\n\n\tCOM_AssertMainThread(\"COM_LoadStackFile\");\n\n\tloadbuf = (qbyte *)buffer;\n\tloadsize = bufsize;\n\tbuf = COM_LoadFile (path, 0, 4, fsize);\n\n\treturn buf;\n}\n\n\n/*warning: at some point I'll change this function to return only read-only buffers*/\nqofs_t FS_LoadFile(const char *name, void **file)\n{\n\tsize_t fsz;\n\t*file = FS_LoadMallocFile (name, &fsz);\n\tif (!*file)\n\t\treturn (qofs_t)-1;\n\treturn fsz;\n}\nvoid FS_FreeFile(void *file)\n{\n\tBZ_Free(file);\n}\n\n//handle->EnumerateFiles on each a:b:c part of the given matches string.\nstatic qboolean FS_EnumerateFilesEach(searchpathfuncs_t *handle, char *matches, int (QDECL *func)(const char *fname, qofs_t fsize, time_t mtime, void *parm, searchpathfuncs_t *spath), void *parm)\n{\n\tchar cleanpath[MAX_QPATH];\n\tconst char *match;\n\tchar *sep;\n\tfor (; matches; matches = sep)\n\t{\n\t\tif (!strncmp(matches, \"file:\", 5))\n\t\t{\n\t\t\tsep = strchr(matches+5, ':');\n\t\t\tcontinue;\n\t\t}\n\t\tsep = strchr(matches, ':');\n\t\tif (sep)\n\t\t{\n\t\t\t*sep = 0;\n\t\t\tmatch = FS_GetCleanPath(matches, true, cleanpath, sizeof(cleanpath));\n\t\t\t*sep++ = ':';\n\t\t}\n\t\telse\n\t\t\tmatch = FS_GetCleanPath(matches, true, cleanpath, sizeof(cleanpath));\n\n\t\tif (match && *match)\n\t\t\tif (!handle->EnumerateFiles(handle, match, func, parm))\n\t\t\t\treturn false;\n\t}\n\treturn true;\n}\nstatic int FS_EnumerateFilesEachSys (const char *syspath, char *matches, int (*func)(const char *, qofs_t, time_t modtime, void *, searchpathfuncs_t *), void *parm, searchpathfuncs_t *spath)\n{\n\tchar cleanpath[MAX_QPATH];\n\tconst char *match;\n\tchar *sep;\n\tfor (; matches; matches = sep)\n\t{\n\t\tif (!strncmp(matches, \"file:\", 5))\n\t\t{\n\t\t\tsep = strchr(matches+5, ':');\n\t\t\tcontinue;\n\t\t}\n\t\tsep = strchr(matches, ':');\n\t\tif (sep)\n\t\t{\n\t\t\t*sep = 0;\n\t\t\tmatch = FS_GetCleanPath(matches, true, cleanpath, sizeof(cleanpath));\n\t\t\t*sep++ = ':';\n\t\t}\n\t\telse\n\t\t\tmatch = FS_GetCleanPath(matches, true, cleanpath, sizeof(cleanpath));\n\n\t\tif (!Sys_EnumerateFiles(syspath, match, func, parm, spath))\n\t\t\treturn false;\n\t}\n\treturn true;\n}\nsearchpathfuncs_t *COM_EnumerateFilesPackage (char *matches, const char *package, unsigned int flags, int (QDECL *func)(const char *, qofs_t, time_t mtime, void *, searchpathfuncs_t*), void *parm)\n{\t//special version of COM_EnumerateFiles that takes an explicit package name to search inside.\n\t//additionally accepts multiple patterns (separated by : chsrs)\n\tsearchpathfuncs_t *handle;\n\tsearchpath_t    *search;\n\tconst char *sp;\n\tqboolean foundpackage = false;\n\tfor (search = com_searchpaths; search ; search = search->next)\n\t{\n\t\tif (package)\n\t\t{\n\t\t\tif (flags & WP_FULLPATH)\n\t\t\t\tsp = search->purepath;\n\t\t\telse\n\t\t\t{\n\t\t\t\tsp = strchr(search->purepath, '/');\n\t\t\t\tif (sp && !strchr(++sp, '/'))\n\t\t\t\t\t;\n\t\t\t\telse\n\t\t\t\t\tcontinue;\t//ignore packages inside other packages. they're just too weird.\n\t\t\t}\n\t\t\tif (strcmp(package, sp))\n\t\t\t\tcontinue;\t//ignore this package\n\t\t}\n\t\tfoundpackage = true;\n\n\t\tif (!FS_EnumerateFilesEach(search->handle, matches, func, parm))\n\t\t\tbreak;\n\t}\n\n\tif (!foundpackage && package && (flags&WP_FORCE) && (flags & WP_FULLPATH))\n\t{\t//if we're forcing the package search then be prepared to open the gamedir or gamedir/package that was specified.\n\t\tchar cleanname[MAX_OSPATH];\n\t\tchar syspath[MAX_OSPATH];\n\t\tchar *sl;\n\n\t\tpackage = FS_GetCleanPath(package, false, cleanname, sizeof(cleanname));\n\t\tif (!package)\n\t\t\treturn NULL;\n\n\t\tsl = strchr(package, '/');\n\t\tif (sl)\n\t\t{\t//try to open the named package.\n\t\t\t*sl = 0;\n\t\t\tif (strchr(sl+1, '/') || !FS_GamedirIsOkay(package, true))\n\t\t\t\treturn NULL;\n\t\t\t*sl = '/';\n\n\t\t\tif (com_homepathenabled)\n\t\t\t{\t//try the homedir\n\t\t\t\tQ_snprintfz(syspath, sizeof(syspath), \"%s%s\", com_homepath, package);\n\t\t\t\thandle = FS_OpenPackByExtension(VFSOS_Open(syspath, \"rb\"), NULL, package, package, \"\");\n\t\t\t}\n\t\t\telse\n\t\t\t\thandle = NULL;\n\t\t\tif (!handle)\n\t\t\t{\t//now go for the basedir to see if ther.\n\t\t\t\tQ_snprintfz(syspath, sizeof(syspath), \"%s%s\", com_gamepath, package);\n\t\t\t\thandle = FS_OpenPackByExtension(VFSOS_Open(syspath, \"rb\"), NULL, package, package, \"\");\n\t\t\t}\n\n\t\t\tif (handle)\n\t\t\t\tFS_EnumerateFilesEach(handle, matches, func, parm);\n\t\t\treturn handle;\t//caller can use this for context, but is expected to tidy it up too.\n\t\t}\n\t\telse\n\t\t{\t//we use NULLs for spath context here. caller will need to figure out which basedir to read it from.\n\t\t\tif (!FS_GamedirIsOkay(package, true))\n\t\t\t\treturn NULL;\n\n\t\t\tif (com_homepathenabled)\n\t\t\t{\n\t\t\t\tQ_snprintfz(syspath, sizeof(syspath), \"%s%s\", com_homepath, package);\n\t\t\t\tFS_EnumerateFilesEachSys(syspath, matches, func, parm, NULL);\n\t\t\t}\n\n\t\t\tQ_snprintfz(syspath, sizeof(syspath), \"%s%s\", com_gamepath, package);\n\t\t\tFS_EnumerateFilesEachSys(syspath, matches, func, parm, NULL);\n\t\t}\n\t}\n\treturn NULL;\n}\n\nstruct fs_enumerate_fileuri_s\n{\n\tint (QDECL *func)(const char *, qofs_t, time_t mtime, void *, searchpathfuncs_t*);\n\tvoid *parm;\n};\nstatic int QDECL COM_EnumerateFiles_FileURI (const char *name, qofs_t flags, time_t mtime, void *parm, searchpathfuncs_t *spath)\n{\n\tchar syspath[MAX_OSPATH];\n\tstruct fs_enumerate_fileuri_s *e = parm;\n\tsize_t nlen = strlen(name)+1;\n\tif (7+nlen > sizeof(syspath))\n\t\treturn true;\n\tmemcpy(syspath, \"file://\", 7);\n\tmemcpy(syspath+7, name, nlen);\n\treturn e->func(syspath, flags, mtime, e->parm, spath);\n}\n\nvoid COM_EnumerateFiles (const char *match, int (QDECL *func)(const char *, qofs_t, time_t mtime, void *, searchpathfuncs_t*), void *parm)\n{\n\tsearchpath_t    *search;\n\n\tif (!strncmp(match, \"file:\", 5))\n\t{\n\t\tif (fs_allowfileuri)\n\t\t{\n\t\t\tchar syspath[MAX_OSPATH];\n\t\t\tstruct fs_enumerate_fileuri_s e;\n\t\t\te.func = func;\n\t\t\te.parm = parm;\n\t\t\tif (Sys_ResolveFileURL(match, strlen(match), syspath, sizeof(syspath)))\n\t\t\t\tSys_EnumerateFiles(NULL, syspath, COM_EnumerateFiles_FileURI, &e, NULL);\n\t\t}\n\t\treturn;\n\t}\n\n\tfor (search = com_searchpaths; search ; search = search->next)\n\t{\n\t\tif (!search->handle->EnumerateFiles(search->handle, match, func, parm))\n\t\t\tbreak;\n\t}\n}\n\n//scan packages in a reverse order, ie lowest priority first (for less scrolling upwards)\nvoid COM_EnumerateFilesReverse (const char *match, int (QDECL *func)(const char *, qofs_t, time_t mtime, void *, searchpathfuncs_t*), void *parm)\n{\n\tsearchpath_t    *search;\n\tsearchpath_t    **rev;\n\tsize_t count;\n\n\tif (!strncmp(match, \"file:\", 5))\n\t{\n\t\tif (fs_allowfileuri)\n\t\t{\n\t\t\tchar syspath[MAX_OSPATH];\n\t\t\tstruct fs_enumerate_fileuri_s e;\n\t\t\te.func = func;\n\t\t\te.parm = parm;\n\t\t\tif (Sys_ResolveFileURL(match, strlen(match), syspath, sizeof(syspath)))\n\t\t\t\tSys_EnumerateFiles(NULL, syspath, COM_EnumerateFiles_FileURI, &e, NULL);\n\t\t}\n\t\treturn;\n\t}\n\n\tfor (search = com_searchpaths, count=0; search ; search = search->next)\n\t\tcount++;\n\trev = BZ_Malloc(sizeof(*rev)*count);\n\tfor (search = com_searchpaths, count=0; search ; search = search->next)\n\t\trev[count++] = search;\n\twhile(count)\n\t{\n\t\tsearch = rev[--count];\n\t\tif (!search->handle->EnumerateFiles(search->handle, match, func, parm))\n\t\t\tbreak;\n\t}\n\tBZ_Free(rev);\n}\n\nvoid COM_FlushTempoaryPacks(void)\t//flush all temporary packages\n{\n\tsearchpath_t *sp, **link;\n\n\tCOM_AssertMainThread(\"COM_FlushTempoaryPacks\");\n\n\tif (!com_searchpaths || !fs_thread_mutex)\n\t\treturn;\t//we already shut down...\n\n\tCOM_WorkerLock();\t//make sure no workers are poking files...\n\tSys_LockMutex(fs_thread_mutex);\n\n\tlink = &com_searchpaths;\n\twhile (*link)\n\t{\n\t\tsp = *link;\n\t\tif (sp->flags & SPF_TEMPORARY)\n\t\t{\n\t\t\tFS_FlushFSHashFull();\n\n\t\t\t*link = sp->next;\n\t\t\tcom_purepaths = NULL;\n\n\t\t\tsp->handle->ClosePath(sp->handle);\n\t\t\tZ_Free (sp);\n\t\t}\n\t\telse\n\t\t\tlink = &sp->next;\n\t}\n\tSys_UnlockMutex(fs_thread_mutex);\n\tCOM_WorkerUnlock();\t//workers can continue now\n}\n\nstatic searchpath_t *FS_MapPackIsActive(searchpathfuncs_t *archive)\n{\n\tsearchpath_t *sp;\n\tSys_LockMutex(fs_thread_mutex);\n\tfor (sp = com_searchpaths; sp; sp = sp->next)\n\t{\n\t\tif (sp->handle == archive)\n\t\t\tbreak;\n\t}\n\tSys_UnlockMutex(fs_thread_mutex);\n\treturn sp;\n}\n\nstatic searchpath_t *FS_AddPathHandle(searchpath_t **oldpaths, const char *purepath, const char *probablepath, searchpathfuncs_t *handle, const char *prefix, unsigned int flags, unsigned int loadstuff);\nqboolean FS_LoadMapPackFile (const char *filename, searchpathfuncs_t *archive)\n{\n\tif (!archive)\n\t\treturn false;\n\tif (!archive->AddReference)\n\t\treturn false;\t//nope...\n\tif (FS_MapPackIsActive(archive))\n\t\treturn false;\t//don't do it twice.\n\tarchive->AddReference(archive);\n\tif (FS_AddPathHandle(NULL, filename, filename, archive, \"\", SPF_TEMPORARY, 0))\n\t\treturn true;\n\treturn false;\n}\nvoid FS_CloseMapPackFile (searchpathfuncs_t *archive)\n{\n\tsearchpath_t *sp, **link;\n\n\tCOM_AssertMainThread(\"FS_CloseMapPackFile\");\n\n\tCOM_WorkerLock();\t//make sure no workers are poking files...\n\tSys_LockMutex(fs_thread_mutex);\n\n\tlink = &com_searchpaths;\n\twhile (*link)\n\t{\n\t\tsp = *link;\n\t\tif (sp->handle == archive)\n\t\t{\n\t\t\tFS_FlushFSHashFull();\n\n\t\t\t*link = sp->next;\n\t\t\tcom_purepaths = NULL; //FIXME...\n\n\t\t\tsp->handle->ClosePath(sp->handle);\n\t\t\tZ_Free (sp);\n\t\t\tbreak;\n\t\t}\n\t\telse\n\t\t\tlink = &sp->next;\n\t}\n\n\tSys_UnlockMutex(fs_thread_mutex);\n\tCOM_WorkerUnlock();\t//workers can continue now\n\n\tarchive->ClosePath(archive);\n}\n\nstatic searchpathfuncs_t *FS_GetOldPath(searchpath_t **oldpaths, const char *dir, unsigned int *keepflags)\n{\n\tsearchpath_t *p;\n\tsearchpathfuncs_t *r = NULL;\n\t*keepflags = 0;\n\twhile(*oldpaths)\n\t{\n\t\tp = *oldpaths;\n\n\t\tif (!Q_strcasecmp(p->logicalpath, dir))\n\t\t{\n\t\t\t*keepflags |= p->flags & (SPF_REFERENCED | SPF_UNTRUSTED);\n\t\t\t*oldpaths = p->next;\n\t\t\tr = p->handle;\n\t\t\tZ_Free(p);\n\t\t\tbreak;\n\t\t}\n\n\t\toldpaths = &(*oldpaths)->next;\n\t}\n\treturn r;\n}\n\ntypedef struct {\n\tsearchpathfuncs_t *(QDECL *OpenNew)(vfsfile_t *file, searchpathfuncs_t *parent, const char *filename, const char *desc, const char *prefix);\n\tsearchpath_t **oldpaths;\n\tconst char *parentdesc;\n\tconst char *puredesc;\n\tunsigned int inheritflags;\n} wildpaks_t;\nstatic void FS_AddSingleDataFile (const char *descriptor, wildpaks_t *param, searchpathfuncs_t *funcs)\n{\n\tvfsfile_t *vfs;\n\tsearchpath_t\t*search;\n\tsearchpathfuncs_t\t*newpak;\n\tchar\t\t\tpakfile[MAX_OSPATH];\n\tchar\t\t\tpurefile[MAX_OSPATH];\n\tflocation_t loc;\n\tunsigned int keptflags = 0;\n\n\tQ_snprintfz (pakfile, sizeof(pakfile), \"%s%s\", param->parentdesc, descriptor);\n\n\tfor (search = com_searchpaths; search; search = search->next)\n\t{\n\t\tif (!Q_strcasecmp(search->logicalpath, pakfile))\t//assumption: first member of structure is a char array\n\t\t\treturn; //already loaded (base paths?)\n\t}\n\n\tnewpak = FS_GetOldPath(param->oldpaths, pakfile, &keptflags);\n\tif (!newpak)\n\t{\n\t\tif (param->OpenNew == VFSOS_OpenPath)\n\t\t{\n\t\t\tvfs = NULL;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tfs_finds++;\n\t\t\tif (!funcs->FindFile(funcs, &loc, descriptor, NULL))\n\t\t\t\treturn;\t//not found..\n\t\t\tvfs = funcs->OpenVFS(funcs, &loc, \"rb\");\n\t\t\tif (!vfs)\n\t\t\t\treturn;\n\t\t}\n\t\tnewpak = param->OpenNew (vfs, funcs, descriptor, pakfile, \"\");\n\t\tif (!newpak)\n\t\t{\n\t\t\tVFS_CLOSE(vfs);\n\t\t\treturn;\n\t\t}\n\t}\n\n\tQ_snprintfz (pakfile, sizeof(pakfile), \"%s%s\", param->parentdesc, descriptor);\n\tif (*param->puredesc)\n\t\tsnprintf (purefile, sizeof(purefile), \"%s/%s\", param->puredesc, descriptor);\n\telse\n\t\tQ_strncpyz(purefile, descriptor, sizeof(purefile));\n\tFS_AddPathHandle(param->oldpaths, purefile, pakfile, newpak, \"\", ((!Q_strncasecmp(descriptor, \"pak\", 3))?SPF_COPYPROTECTED:0)|keptflags|param->inheritflags, (unsigned int)-1);\n}\ntypedef struct\n{\n\t//name table, to avoid too many reallocs\n\tchar *names;\n\tsize_t numnames;\n\tsize_t maxnames;\n\n\t//file table, again to avoid excess reallocs\n\tstruct wilddatafile_s\n\t{\n\t\tsize_t nameofs;\n\t\tsize_t size;\n\t\ttime_t mtime;\n\t\tsearchpathfuncs_t *source;\n\t} *files;\n\tsize_t numfiles;\n\tsize_t maxfiles;\n} filelist_t;\nstatic int QDECL FS_FindWildDataFiles (const char *descriptor, qofs_t size, time_t mtime, void *vparam, searchpathfuncs_t *funcs)\n{\n\tfilelist_t *list = vparam;\n\tsize_t name = list->numnames;\n\tsize_t file = list->numfiles;\n\n\tsize_t dlen = strlen(descriptor);\n\n\tif (list->numnames + dlen+1 > list->maxnames)\n\t\tZ_ReallocElements((void**)&list->names, &list->maxnames, list->numnames+dlen+1+8192, sizeof(*list->names));\n\tstrcpy(list->names + name, descriptor);\n\tlist->numnames += dlen+1;\n\n\tif (list->numfiles + 1 > list->maxfiles)\n\t\tZ_ReallocElements((void**)&list->files, &list->maxfiles, list->numfiles+1+128, sizeof(*list->files));\n\tlist->files[file].nameofs = name;\n\tlist->files[file].size = size;\n\tlist->files[file].mtime = mtime;\n\tlist->files[file].source = funcs;\n\tlist->numfiles += 1;\n\n\treturn true;\t//keep looking for more\n}\nstatic const char *qsortsucks;\nstatic int QDECL FS_SortWildDataFiles(const void *va, const void *vb)\n{\n\tconst struct wilddatafile_s *a=va, *b=vb;\n\tconst char *na=qsortsucks+a->nameofs, *nb=qsortsucks+b->nameofs;\n\n\t//sort by modification time...\n\tif (a->mtime != b->mtime && a->mtime && b->mtime && !dpcompat_ignoremodificationtimes.ival)\n\t\treturn a->mtime > b->mtime;\n\n\t//then fall back and sort by name\n\treturn Q_strcasecmp(na, nb);\n}\nstatic void FS_LoadWildDataFiles (filelist_t *list, wildpaks_t *wp)\n{\n\tsize_t f;\n\t//sort them\n\tqsortsucks = list->names;\n\tqsort(list->files, list->numfiles, sizeof(*list->files), FS_SortWildDataFiles);\n\tqsortsucks = NULL;\n\n\tfor (f = 0; f < list->numfiles; f++)\n\t\tFS_AddSingleDataFile(list->names+list->files[f].nameofs, wp, list->files[f].source);\n\tlist->numfiles = list->numnames = 0;\n\n\tZ_Free(list->files);\n\tlist->files = NULL;\n\tZ_Free(list->names);\n\tlist->names = NULL;\n\tlist->maxfiles = list->maxnames = 0;\n}\n\nsearchpathfuncs_t *FS_OpenPackByExtension(vfsfile_t *f, searchpathfuncs_t *parent, const char *filename, const char *pakname, const char *pakpathprefix)\n{\n\tsearchpathfuncs_t *pak;\n\tint j;\n\tchar ext[8];\n\tif (!f)\n\t\treturn NULL;\n\tCOM_FileExtension(pakname, ext, sizeof(ext));\n\tfor (j = 0; j < sizeof(searchpathformats)/sizeof(searchpathformats[0]); j++)\n\t{\n\t\tif (!searchpathformats[j].extension || !searchpathformats[j].OpenNew)\n\t\t\tcontinue;\n\t\tif (!Q_strcasecmp(ext, searchpathformats[j].extension))\n\t\t{\n\t\t\tpak = searchpathformats[j].OpenNew(f, parent, filename, pakname, pakpathprefix);\n\t\t\tif (pak)\n\t\t\t\treturn pak;\n\t\t\tCon_Printf(\"Unable to open %s - corrupt?\\n\", pakname);\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tVFS_CLOSE(f);\n\treturn NULL;\n}\n\n//\nvoid FS_AddHashedPackage(searchpath_t **oldpaths, const char *parentpath, const char *logicalpaths, searchpath_t *search, unsigned int loadstuff, const char *pakpath, const char *qhash, const char *pakprefix, unsigned int packflags)\n{\n\tsearchpathfuncs_t\t*handle;\n\tsearchpath_t *oldp;\n\tchar pname[MAX_OSPATH];\n\tchar lname[MAX_OSPATH];\n\tchar lname2[MAX_OSPATH];\n\tunsigned int\tkeptflags;\n\tflocation_t loc;\n\tint fmt;\n\tchar ext[8];\n\tint ptlen = strlen(parentpath);\n\n\tCOM_FileExtension(pakpath, ext, sizeof(ext));\n\n\t//figure out which file format its meant to be.\n\tfor (fmt = 0; fmt < sizeof(searchpathformats)/sizeof(searchpathformats[0]); fmt++)\n\t{\n\t\tif (!searchpathformats[fmt].extension || !searchpathformats[fmt].OpenNew)// || !searchpathformats[i].loadscan)\n\t\t\tcontinue;\n\t\tif ((loadstuff & (1<<fmt)) && !Q_strcasecmp(ext, searchpathformats[fmt].extension))\n\t\t{\n\t\t\t//figure out the logical path names\n\t\t\tif (!FS_GenCachedPakName(pakpath, qhash, pname, sizeof(pname)))\n\t\t\t\treturn;\t//file name was invalid, panic.\n\t\t\tif (!search)\n\t\t\t{\n\t\t\t\tFS_SystemPath(pname, FS_ROOT, lname, sizeof(lname));\n\t\t\t\tFS_SystemPath(pakpath, FS_ROOT, lname2, sizeof(lname2));\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tsnprintf (lname, sizeof(lname), \"%s%s\", logicalpaths, pname+ptlen+1);\n\t\t\t\tsnprintf (lname2, sizeof(lname), \"%s%s\", logicalpaths, pakpath+ptlen+1);\n\t\t\t}\n\n\t\t\t//see if we already added it\n\t\t\tfor (oldp = com_searchpaths; oldp; oldp = oldp->next)\n\t\t\t{\n\t\t\t\tif (strcmp(oldp->prefix, pakprefix?pakprefix:\"\"))\t//probably will only happen from typos, but should be correct.\n\t\t\t\t\tcontinue;\n\t\t\t\tif (!Q_strcasecmp(oldp->purepath, pakpath))\n\t\t\t\t\tbreak;\n\t\t\t\tif (!Q_strcasecmp(oldp->logicalpath, lname))\n\t\t\t\t\tbreak;\n\t\t\t\tif (!Q_strcasecmp(oldp->logicalpath, lname2))\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (!oldp)\n\t\t\t{\n\t\t\t\t//see if we can get an old archive handle from before whatever fs_restart\n\t\t\t\thandle = FS_GetOldPath(oldpaths, lname2, &keptflags);\n\t\t\t\tif (handle)\n\t\t\t\t\tsnprintf (lname, sizeof(lname), \"%s\", lname2);\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\thandle = FS_GetOldPath(oldpaths, lname, &keptflags);\n\n\t\t\t\t\t//seems new, load it.\n\t\t\t\t\tif (!handle)\n\t\t\t\t\t{\n\t\t\t\t\t\tvfsfile_t *vfs = NULL;\n\t\t\t\t\t\tif (search)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (search->handle->FindFile(search->handle, &loc, pakpath+ptlen+1, NULL))\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tvfs = search->handle->OpenVFS(search->handle, &loc, \"rb\");\n\t\t\t\t\t\t\t\tsnprintf (lname, sizeof(lname), \"%s\", lname2);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse if (search->handle->FindFile(search->handle, &loc, pname+ptlen+1, NULL))\n\t\t\t\t\t\t\t\tvfs = search->handle->OpenVFS(search->handle, &loc, \"rb\");\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tvfs = FS_OpenVFS(pakpath, \"rb\", FS_ROOT);\n\t\t\t\t\t\t\tif (vfs)\n\t\t\t\t\t\t\t\tsnprintf (lname, sizeof(lname), \"%s\", lname2);\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\tvfs = FS_OpenVFS(pname, \"rb\", FS_ROOT);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (vfs)\n\t\t\t\t\t\t\thandle = searchpathformats[fmt].OpenNew (vfs, search?search->handle:NULL, pakpath, lname, pakprefix?pakprefix:\"\");\n\t\t\t\t\t\tif (!handle && vfs)\n\t\t\t\t\t\t\tVFS_CLOSE(vfs);\t//erk\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t//insert it into our path lists.\n\t\t\t\tif (handle && qhash)\n\t\t\t\t{\n\t\t\t\t\tint truecrc = handle->GeneratePureCRC(handle, NULL);\n\t\t\t\t\tif (truecrc != (int)strtoul(qhash, NULL, 0))\n\t\t\t\t\t{\n\t\t\t\t\t\tif (pakprefix && *pakprefix)\n\t\t\t\t\t\t\tCon_Printf(CON_ERROR \"File \\\"%s\\\" [prefix %s] has hash %#x (required: %s). Please delete it or move it away\\n\", lname, pakprefix, truecrc, qhash);\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tCon_Printf(CON_ERROR \"File \\\"%s\\\" has hash %#x (required: %s). Please delete it or move it away\\n\", lname, truecrc, qhash);\n\t\t\t\t\t\thandle->ClosePath(handle);\n\t\t\t\t\t\thandle = NULL;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (handle)\n\t\t\t\t\tFS_AddPathHandle(oldpaths, pakpath, lname, handle, pakprefix, packflags|keptflags, (unsigned int)-1);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t}\n}\n\nstatic void FS_AddManifestPackages(searchpath_t **oldpaths, const char *purepath, const char *logicalpaths, searchpath_t *search, unsigned int loadstuff)\n{\n#ifndef PACKAGEMANAGER\n\tint\t\t\t\ti;\n\n\tint ptlen, palen;\n\tptlen = strlen(purepath);\n\tfor (i = 0; i < sizeof(fs_manifest->package) / sizeof(fs_manifest->package[0]); i++)\n\t{\n\t\tchar qhash[16];\n\t\tif (!fs_manifest->package[i].path)\n\t\t\tcontinue;\n\n\t\tpalen = strlen(fs_manifest->package[i].path);\n\t\tif (palen > ptlen && (fs_manifest->package[i].path[ptlen] == '/' || fs_manifest->package[i].path[ptlen] == '\\\\' )&& !strncmp(purepath, fs_manifest->package[i].path, ptlen))\n\t\t{\n\t\t\tQ_snprintfz(qhash, sizeof(qhash), \"%#x\", fs_manifest->package[i].crc);\n\t\t\tFS_AddHashedPackage(oldpaths,purepath,logicalpaths,search,loadstuff, fs_manifest->package[i].path,fs_manifest->package[i].crcknown?qhash:NULL,fs_manifest->package[i].prefix, SPF_COPYPROTECTED|\n#ifdef FTE_TARGET_WEB\n\t\t\t\t\t0\t//web targets consider manifest packages as trusted, because they're about as trusted as the engine/html that goes with it.\n#else\n\t\t\t\t\t(fs_manifest->security==MANIFEST_SECURITY_NOT?SPF_UNTRUSTED:0)\n#endif\n\t\t\t\t\t);\n\t\t}\n\t}\n#endif\n}\n\nstatic void FS_AddDownloadManifestPackages(searchpath_t **oldpaths, unsigned int loadstuff)//, const char *purepath, searchpath_t *search, const char *extension, searchpathfuncs_t *(QDECL *OpenNew)(vfsfile_t *file, const char *desc))\n{\n\tchar logicalroot[MAX_OSPATH];\n\tFS_SystemPath(\"downloads/\", FS_ROOT, logicalroot, sizeof(logicalroot));\n\n\tFS_AddManifestPackages(oldpaths, \"downloads\", logicalroot, NULL, loadstuff);\n}\n\nstatic void FS_AddDataFiles(searchpath_t **oldpaths, const char *purepath, const char *logicalpath, searchpath_t *search, unsigned int pflags, unsigned int loadstuff)\n{\n\t//search is the parent\n\tint\t\t\t\ti, j;\n\tsearchpath_t\t*existing;\n\tsearchpathfuncs_t\t*handle;\n\tchar\t\t\tpakfile[MAX_OSPATH];\n\tchar\t\t\tlogicalpaths[MAX_OSPATH];\t//with a slash\n\tchar\t\t\tpurefile[MAX_OSPATH];\n\tchar\t\t\tlogicalfile[MAX_OSPATH*2];\n\tunsigned int\tkeptflags;\n\tvfsfile_t *vfs;\n\tflocation_t loc;\n\twildpaks_t wp;\n\tfilelist_t list = {0};\n\tqboolean qshack = (pflags&SPF_QSHACK);\n\tpflags &= ~SPF_QSHACK;\n\n\tQ_strncpyz(logicalpaths, logicalpath, sizeof(logicalpaths));\n\tFS_CleanDir(logicalpaths, sizeof(logicalpaths));\n\twp.parentdesc = logicalpaths;\n\twp.puredesc = purepath;\n\twp.oldpaths = oldpaths;\n\twp.inheritflags = pflags;\n\n\t//read pak.lst to get some sort of official ordering of pak files\n\tif (search->handle->FindFile(search->handle, &loc, \"pak.lst\", NULL) == FF_FOUND)\n\t{\n\t\tchar filename[MAX_QPATH];\n\t\tchar *buffer = BZ_Malloc(loc.len+1);\n\t\tchar *names = buffer;\n\t\tsearch->handle->ReadFile(search->handle, &loc, buffer);\n\t\tbuffer[loc.len] = 0;\n\t\t\n\t\twhile (names && *names)\n\t\t{\n\t\t\tnames = COM_ParseOut(names, filename, sizeof(filename));\n\t\t\tif (*filename)\n\t\t\t{\n\t\t\t\tchar extension[MAX_QPATH];\n\t\t\t\tCOM_FileExtension(filename, extension, sizeof(extension));\n\n\t\t\t\t//I dislike that this is tied to extensions, but whatever.\n\t\t\t\tfor (j = 0; j < sizeof(searchpathformats)/sizeof(searchpathformats[0]); j++)\n\t\t\t\t{\n\t\t\t\t\tif (!searchpathformats[j].extension || !searchpathformats[j].OpenNew || !searchpathformats[j].loadscan)\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\tif (!Q_strcasecmp(extension, searchpathformats[j].extension))\n\t\t\t\t\t{\n\t\t\t\t\t\tif (loadstuff & (1<<j))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\twp.OpenNew = searchpathformats[j].OpenNew;\n\t\t\t\t\t\t\tFS_AddSingleDataFile(filename, &wp, search->handle);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tBZ_Free(buffer);\n\t}\n\n#ifdef PACKAGEMANAGER\n\tPM_LoadPackages(oldpaths, purepath, logicalpaths, search, loadstuff, 0x80000000, -1);\n#endif\n\n\tfor (j = 0; j < sizeof(searchpathformats)/sizeof(searchpathformats[0]); j++)\n\t{\n\t\tif (!searchpathformats[j].extension || !searchpathformats[j].OpenNew || !searchpathformats[j].loadscan)\n\t\t\tcontinue;\n\t\tif (loadstuff & (1<<j))\n\t\t{\n\t\t\tqboolean okay = true;\n\t\t\tconst char *extension = searchpathformats[j].extension;\n\n\t\t\t//first load all the numbered pak files\n\t\t\tfor (i=0 ; okay ; i++)\n\t\t\t{\n\t\t\t\tsnprintf (pakfile, sizeof(pakfile), \"pak%i.%s\", i, extension);\n\t\t\t\tfs_finds++;\n\t\t\t\tif (search->handle->FindFile(search->handle, &loc, pakfile, NULL))\n\t\t\t\t{\n\t\t\t\t\tsnprintf (logicalfile, sizeof(logicalfile), \"%spak%i.%s\", logicalpaths, i, extension);\n\t\t\t\t\tsnprintf (purefile, sizeof(purefile), \"%s/pak%i.%s\", purepath, i, extension);\n\n\t\t\t\t\tfor (existing = com_searchpaths; existing; existing = existing->next)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (!Q_strcasecmp(existing->logicalpath, logicalfile))\t//assumption: first member of structure is a char array\n\t\t\t\t\t\t\tbreak; //already loaded (base paths?)\n\t\t\t\t\t}\n\t\t\t\t\tif (!existing)\n\t\t\t\t\t{\n\t\t\t\t\t\thandle = FS_GetOldPath(oldpaths, logicalfile, &keptflags);\n\t\t\t\t\t\tif (!handle)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tvfs = search->handle->OpenVFS(search->handle, &loc, \"rb\");\n\t\t\t\t\t\t\tif (!vfs)\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\thandle = searchpathformats[j].OpenNew (vfs, search->handle, pakfile, logicalfile, \"\");\n\t\t\t\t\t\t\tif (!handle)\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tFS_AddPathHandle(oldpaths, purefile, logicalfile, handle, \"\", SPF_COPYPROTECTED|pflags|keptflags, (unsigned int)-1);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tokay = false;\n\n\t\t\t\tif (i == 0 && qshack)\n\t\t\t\t{\n\t\t\t\t\tsnprintf (pakfile, sizeof(pakfile), \"quakespasm.%s\", extension);\n\t\t\t\t\thandle = FS_GetOldPath(oldpaths, logicalfile, &keptflags);\n\t\t\t\t\tif (!handle)\n\t\t\t\t\t\thandle = FS_OpenPackByExtension(VFSOS_Open(pakfile, \"rb\"), NULL, pakfile, pakfile, \"\");\n\t\t\t\t\tif (handle)\t//logically should have SPF_EXPLICIT set, but that would give it a worse gamedir depth\n\t\t\t\t\t\tFS_AddPathHandle(oldpaths, \"\", pakfile, handle, \"\", SPF_COPYPROTECTED|SPF_PRIVATE, (unsigned int)-1);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t//now load ones from the manifest\n\tFS_AddManifestPackages(oldpaths, purepath, logicalpaths, search, loadstuff);\n\n#ifdef PACKAGEMANAGER\n\tPM_LoadPackages(oldpaths, purepath, logicalpaths, search, loadstuff, 0x0, 1000-1);\n#endif\n\n\t//now load the random ones\n\tfor (j = 0; j < sizeof(searchpathformats)/sizeof(searchpathformats[0]); j++)\n\t{\n\t\tif (!searchpathformats[j].extension || !searchpathformats[j].OpenNew || !searchpathformats[j].loadscan)\n\t\t\tcontinue;\n\t\tif (loadstuff & (1<<j))\n\t\t{\n\t\t\tconst char *extension = searchpathformats[j].extension;\n\t\t\twp.OpenNew = searchpathformats[j].OpenNew;\n\n\t\t\tQ_snprintfz (pakfile, sizeof(pakfile), \"*.%s\", extension);\n\t\t\tsearch->handle->EnumerateFiles(search->handle, pakfile, FS_FindWildDataFiles, &list);\n\t\t\tFS_LoadWildDataFiles(&list, &wp);\n\t\t}\n\t}\n\n#ifdef PACKAGEMANAGER\n\tPM_LoadPackages(oldpaths, purepath, logicalpaths, search, loadstuff, 1000, 0x7ffffffe);\n#endif\n}\n\nstatic searchpath_t *FS_AddPathHandle(searchpath_t **oldpaths, const char *purepath, const char *logicalpath, searchpathfuncs_t *handle, const char *prefix, unsigned int flags, unsigned int loadstuff)\n{\n\tsearchpath_t *search, **link;\n\n\tif (!handle)\n\t{\n\t\tCon_Printf(\"COM_AddPathHandle: not a valid handle (%s)\\n\", logicalpath);\n\t\treturn NULL;\n\t}\n\n\tif (handle->fsver != FSVER)\n\t{\n\t\tCon_Printf(\"%s: file system driver is outdated (%u should be %u)\\n\", logicalpath, handle->fsver, FSVER);\n\t\thandle->ClosePath(handle);\n\t\treturn NULL;\n\t}\n\n\tsearch = (searchpath_t*)Z_Malloc (sizeof(searchpath_t));\n\tsearch->handle = handle;\n\tQ_strncpyz(search->purepath, purepath, sizeof(search->purepath));\n\tQ_strncpyz(search->logicalpath, logicalpath, sizeof(search->logicalpath));\n\tif (prefix && *prefix)\n\t{\n\t\tQ_strncpyz(search->prefix, prefix, sizeof(search->prefix));\n\t\tflags |= SPF_COPYPROTECTED; //don't do downloading weirdness when there's weird prefix shenanegans going on.\n\t}\n\tsearch->flags = flags;\n\n\tsearch->crc_check = 0;\n\tsearch->crc_seed = ~fs_pureseed;\n\tsearch->crc_reply = 0;\n\tif (search->handle->GeneratePureCRC)\n\t{\n\t\tsearch->crc_check = search->handle->GeneratePureCRC(search->handle, NULL);\n\t\tif (flags & SPF_SERVER)\n\t\t{\n\t\t\tsearch->crc_seed = fs_pureseed;\n\t\t\tsearch->crc_reply = search->handle->GeneratePureCRC(search->handle, &search->crc_seed);\n\t\t}\n\t}\n\n\tflags &= ~SPF_WRITABLE;\n\n\t//temp packages also do not nest\n\tif (!(flags & SPF_TEMPORARY))\n\t\tFS_AddDataFiles(oldpaths, purepath, logicalpath, search, flags&(SPF_COPYPROTECTED|SPF_UNTRUSTED|SPF_TEMPORARY|SPF_SERVER|SPF_PRIVATE|SPF_QSHACK|SPF_VIRTUAL), loadstuff);\n\n\tsearch->nextpure = (void*)0x1;\t//mark as not linked\n\n\tif (flags & (SPF_TEMPORARY|SPF_SERVER))\n\t{\n\t\tint depth = 1;\n\t\tsearchpath_t *s;\n\t\t//add at end. pureness will reorder if needed.\n\t\tlink = &com_searchpaths;\n\t\twhile(*link)\n\t\t{\n\t\t\tlink = &(*link)->next;\n\t\t}\n\n\t\tif (com_purepaths)\n\t\t{\t//go for the pure paths first.\n\t\t\tfor (s = com_purepaths; s; s = s->nextpure)\n\t\t\t\tdepth++;\n\t\t}\n\t\tif (fs_puremode < 2)\n\t\t{\n\t\t\tfor (s = com_searchpaths ; s ; s = s->next)\n\t\t\t\tdepth++;\n\t\t}\n\t\t*link = search;\n\n\t\tif (filesystemhash.numbuckets)\n\t\t\tsearch->handle->BuildHash(search->handle, depth, FS_AddFileHashUnsafe);\n\t}\n\telse\n\t{\n\t\tsearch->next = com_searchpaths;\n\t\tcom_searchpaths = search;\n\n\t\tcom_fschanged = true;\t//depth values are screwy\n\t}\n\n\treturn search;\n}\n\nstatic void COM_RefreshFSCache_f(void)\n{\n\tcom_fschanged=true;\n}\n\n//optionally purges the cache and rebuilds it\nvoid COM_FlushFSCache(qboolean purge, qboolean domutex)\n{\n\tsearchpath_t *search;\n\tif (com_fs_cache.ival && com_fs_cache.ival != 2)\n\t{\n\t\tfor (search = com_searchpaths ; search ; search = search->next)\n\t\t{\n\t\t\tif (search->handle->PollChanges)\n\t\t\t\tcom_fschanged |= search->handle->PollChanges(search->handle);\n\t\t}\n\t}\n\n#ifdef FTE_TARGET_WEB\n\t//web target doesn't support filesystem enumeration, so make sure the cache is kept invalid and disabled.\n\tcom_fschanged = true;\n#else\n\tif (com_fs_cache.ival && com_fschanged)\n\t{\n\t\t//rebuild it if needed\n\t\tFS_RebuildFSHash(domutex);\n\t}\n#endif\n}\n\n/*since should start as 0, otherwise this can be used to poll*/\nqboolean FS_Restarted(unsigned int *since)\n{\n\tif (*since < fs_restarts)\n\t{\n\t\t*since = fs_restarts;\n\t\treturn true;\n\t}\n\treturn false;\n}\n\n#ifdef __WIN32\t//already assumed to be case insensitive. let the OS keep fixing up the paths itself.\nstatic qboolean FS_FixupFileCase(char *out, size_t outsize, const char *basedir, const char *entry, qboolean isdir)\n{\n\treturn Q_snprintfz(out, outsize, \"%s%s\", basedir, entry) < outsize;\n}\n#else\nstruct fixupcase_s\n{\n\tchar *out;\n\tsize_t outsize;\n\tconst char *match;\n\tsize_t matchlen;\n\tqboolean isdir;\t//directory results have a trailing /\n};\nstatic int FS_FixupFileCaseResult(const char *name, qofs_t sz, time_t modtime, void *vparm, searchpathfuncs_t *spath)\n{\n\tstruct fixupcase_s *parm = vparm;\n\tif (strlen(name) != parm->matchlen+parm->isdir)\n\t\treturn true;\n\tif (parm->isdir && name[parm->matchlen] != '/')\n\t\treturn true;\n\tif (Q_strncasecmp(name, parm->match, parm->matchlen))\n\t\treturn true;\n\tmemcpy(parm->out, name, parm->matchlen);\n\treturn !!Q_strncmp(name, parm->match, parm->matchlen);\t//stop if we find the exact path case. otherwise keep looking\n}\n//like snprintf(\"%s%s\") but fixes up 'gamedir' case to a real file\nstatic qboolean FS_FixupFileCase(char *out, size_t outsize, const char *basedir, const char *entry, qboolean isdir)\n{\n\tchar *s;\n\tstruct fixupcase_s parm = {out+strlen(basedir), outsize-strlen(basedir), entry, strlen(entry), isdir};\n\tif (Q_snprintfz(out, outsize, \"%s%s\", basedir, entry) >= outsize || outsize < strlen(basedir)+1 || parm.outsize < parm.matchlen+1)\n\t\treturn false;\t//over sized?...\n\tif (strchr(entry, '/')) for(;;)\n\t{\n\t\tparm.match = entry;\n\t\ts = strchr(entry, '/');\n\t\tif (s)\n\t\t{\n\t\t\tparm.isdir = true;\n\t\t\tparm.matchlen = s-entry;\n\t\t\tSys_EnumerateFiles(basedir, \"*\", FS_FixupFileCaseResult, &parm, NULL);\n\t\t\tparm.out += parm.matchlen+1;\n\t\t\tparm.outsize -= parm.matchlen+1;\n\t\t\tentry += (s-entry)+1;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tparm.isdir = isdir;\n\t\t\tparm.matchlen = strlen(entry);\n\t\t\tparm.out[-1] = 0;\n\t\t\tSys_EnumerateFiles(out, \"*\", FS_FixupFileCaseResult, &parm, NULL);\n\t\t\tparm.out[-1] = '/';\n\t\t\tbreak;\n\t\t}\n\t}\n\telse\n\t\tSys_EnumerateFiles(basedir, \"*\", FS_FixupFileCaseResult, &parm, NULL);\n\treturn true;\n}\n#endif\n\n/*\n================\nFS_AddGameDirectory\n\nSets com_gamedir, adds the directory to the head of the path,\nthen loads and adds pak1.pak pak2.pak ...\n================\n*/\nstatic searchpath_t *FS_AddSingleGameDirectory (searchpath_t **oldpaths, const char *puredir, const char *dir, unsigned int loadstuff, unsigned int flags)\n{\n\tunsigned int\tkeptflags;\n\tsearchpath_t\t*search;\n\n\tchar\t\t\t*p;\n\tvoid\t\t\t*handle;\n\n\tfs_restarts++;\n\n\tfor (search = com_searchpaths; search; search = search->next)\n\t{\n\t\tif (!Q_strcasecmp(search->logicalpath, dir))\n\t\t{\n\t\t\tsearch->flags |= flags & SPF_WRITABLE;\n\t\t\treturn search; //already loaded (base paths?)\n\t\t}\n\t}\n\n\tif (!(flags & SPF_PRIVATE))\n\t{\n\t\tif ((p = strrchr(dir, '/')) != NULL)\n\t\t\tQ_strncpyz(pubgamedirfile, ++p, sizeof(pubgamedirfile));\n\t\telse\n\t\t\tQ_strncpyz(pubgamedirfile, dir, sizeof(pubgamedirfile));\n\t}\n\tif ((p = strrchr(dir, '/')) != NULL)\n\t\tQ_strncpyz(gamedirfile, ++p, sizeof(gamedirfile));\n\telse\n\t\tQ_strncpyz(gamedirfile, dir, sizeof(gamedirfile));\n\n//\n// add the directory to the search path\n//\n\thandle = FS_GetOldPath(oldpaths, dir, &keptflags);\n\tif (!handle)\n\t\thandle = VFSOS_OpenPath(NULL, NULL, dir, dir, \"\");\n\n\treturn FS_AddPathHandle(oldpaths, puredir, dir, handle, \"\", flags|keptflags|SPF_ISDIR, loadstuff);\n}\nstatic void FS_AddGameDirectory (searchpath_t **oldpaths, const char *puredir, unsigned int loadstuff, unsigned int flags)\n{\n\tchar syspath[MAX_OSPATH];\n\tif (FS_FixupFileCase(syspath, sizeof(syspath),  com_gamepath, puredir, true))\n\t\tgameonly_gamedir = FS_AddSingleGameDirectory(oldpaths, puredir, syspath, loadstuff, flags&~(com_homepathenabled?SPF_WRITABLE:0u));\n\telse\n\t\tgameonly_gamedir = NULL;\n\tif (com_homepathenabled && FS_FixupFileCase(syspath, sizeof(syspath), com_homepath, puredir, true))\n\t\tgameonly_homedir = FS_AddSingleGameDirectory(oldpaths, puredir, syspath, loadstuff, flags);\n\telse\n\t\tgameonly_homedir = NULL;\n}\n\n//if syspath, something like c:\\quake\\baseq2\n//otherwise just baseq2. beware of dupes.\nsearchpathfuncs_t *COM_IteratePaths (void **iterator, char *pathbuffer, int pathbuffersize, char *dirname, int dirnamesize)\n{\n\tsearchpath_t\t*s;\n\tvoid\t\t\t*prev;\n\n\tprev = NULL;\n\tfor (s=com_searchpaths ; s ; s=s->next)\n\t{\n\t\tif (!(s->flags & SPF_EXPLICIT))\n\t\t\tcontinue;\n\n\t\tif (*iterator == prev)\n\t\t{\n\t\t\t*iterator = s->handle;\n\t\t\tif (!strchr(s->purepath, '/'))\n\t\t\t{\n\t\t\t\tif (pathbuffer)\n\t\t\t\t{\n\t\t\t\t\tQ_strncpyz(pathbuffer, s->logicalpath, pathbuffersize-1);\n\t\t\t\t\tFS_CleanDir(pathbuffer, pathbuffersize);\n\t\t\t\t}\n\t\t\t\tif (dirname)\n\t\t\t\t{\n\t\t\t\t\tQ_strncpyz(dirname, s->purepath, dirnamesize-1);\n\t\t\t\t}\n\t\t\t\treturn s->handle;\n\t\t\t}\n\t\t}\n\t\tprev = s->handle;\n\t}\n\n\t*iterator = NULL;\n\tif (pathbuffer)\n\t\t*pathbuffer = 0;\n\tif (dirname)\n\t\t*dirname = 0;\n\treturn NULL;\n}\n\nchar *FS_GetGamedir(qboolean publicpathonly)\n{\n\tif (publicpathonly)\n\t\treturn pubgamedirfile;\n\telse\n\t\treturn gamedirfile;\n}\n\n//returns the commandline arguments required to duplicate the fs details\nchar *FS_GetManifestArgs(void)\n{\n\tchar *homearg = com_homepathenabled?\"-usehome \":\"-nohome \";\n\tif (fs_manifest->filename)\n\t\treturn va(\"%s-manifest %s -basedir %s\", homearg, fs_manifest->filename, com_gamepath);\n\t\n\treturn va(\"%s-game %s -basedir %s\", homearg, pubgamedirfile, com_gamepath);\n}\n#ifdef SUBSERVERS\nint FS_GetManifestArgv(const char **argv, int maxargs)\n{\n\tint c = 0;\n\tif (maxargs < 5)\n\t\treturn c;\n\targv[c++] = com_homepathenabled?\"-usehome \":\"-nohome \";\n\tif (fs_manifest->filename)\n\t{\n\t\targv[c++] = \"-manifest\";\n\t\targv[c++] = fs_manifest->filename;\n\t}\n\telse\n\t{\n\t\targv[c++] = \"-game\";\n\t\targv[c++] = pubgamedirfile;\n\t}\n\n\targv[c++] = \"-basedir\";\n\targv[c++] = com_gamepath;\n\n\targv[c++] = \"+deathmatch\";\n\targv[c++] = *deathmatch.string?deathmatch.string:\"0\";\n\n\targv[c++] = \"+coop\";\n\targv[c++] = *coop.string?coop.string:\"0\";\n\treturn c;\n}\n#endif\n\n/*\n//given a 'c:/foo/bar/' path, will extract 'bar'.\nstatic void FS_ExtractDir(char *in, char *out, int outlen)\n{\n\tchar *end;\n\tif (!outlen)\n\t\treturn;\n\tend = in + strlen(in);\n\t//skip over any trailing slashes\n\twhile (end > in)\n\t{\n\t\tif (end[-1] == '/' || end[-1] == '\\\\')\n\t\t\tend--;\n\t\telse\n\t\t\tbreak;\n\t}\n\n\t//skip over the path\n\twhile (end > in)\n\t{\n\t\tif (end[-1] != '/' && end[-1] != '\\\\')\n\t\t\tend--;\n\t\telse\n\t\t\tbreak;\n\t}\n\n\t//copy string into the dest\n\twhile (--outlen)\n\t{\n\t\tif (*end == '/' || *end == '\\\\' || !*end)\n\t\t\tbreak;\n\t\t*out++ = *end++;\n\t}\n\t*out = 0;\n}*/\n\nqboolean FS_PathURLCache(const char *url, char *path, size_t pathsize)\n{\n\tchar tmp[MAX_QPATH];\n\tchar *o = tmp;\n\tconst char *i = url;\n\tstrcpy(o, \"downloads/\");\n\to += strlen(o);\n\twhile(*i)\n\t{\n\t\tif (*i == ':' || *i == '?' || *i == '*' || *i == '&')\n\t\t{\n\t\t\tif (i[0] == ':' && i[1] == '/' && i[2] == '/')\n\t\t\t{\n\t\t\t\ti+=2;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t*o++ = '_';\n\t\t\ti++;\n\t\t\tcontinue;\n\t\t}\n\t\tif (*i == '\\\\')\n\t\t{\n\t\t\t*o++ = '/';\n\t\t\ti++;\n\t\t\tcontinue;\n\t\t}\n\t\t*o++ = *i++;\n\t}\n\t*o = 0;\n\n\tif (!FS_GetCleanPath(tmp, false, path, pathsize))\n\t\treturn false;\n\n\treturn true;\n}\n\nstatic ftemanifest_t *FS_Manifest_ChangeGameDir(const char *newgamedir)\n{\n\tftemanifest_t *man;\n\n\tif (*newgamedir && !FS_GamedirIsOkay(newgamedir, true))\n\t\treturn fs_manifest;\n\n\tman = FS_Manifest_ReadMod(newgamedir);\n\n\tif (!man)\n\t{\n\t\t//generate a new manifest based upon the current one.\n\t\tman = FS_ReadDefaultManifest(com_gamepath, sizeof(com_gamepath), true);\n\t\tif (man && strcmp(man->installation, fs_manifest->installation))\n\t\t{\n\t\t\tFS_Manifest_Free(man);\n\t\t\tman = NULL;\n\t\t}\n\t\tif (!man)\n\t\t\tman = FS_Manifest_Clone(fs_manifest);\n\t\tFS_Manifest_PurgeGamedirs(man);\n\t\tif (*newgamedir)\n\t\t{\n\t\t\tchar token[MAX_QPATH], quot[MAX_QPATH];\n\t\t\tchar *dup = Z_StrDup(newgamedir);\t//FIXME: is this really needed?\n\t\t\tnewgamedir = dup;\n\t\t\twhile ((newgamedir = COM_ParseStringSet(newgamedir, token, sizeof(token))))\n\t\t\t{\n\t\t\t\tif (!strcmp(newgamedir, \";\"))\n\t\t\t\t\tcontinue;\n\t\t\t\tif (!*token)\n\t\t\t\t\tcontinue;\n\n\t\t\t\tCmd_TokenizeString(va(\"gamedir %s\", COM_QuotedString(token, quot, sizeof(quot), false)), false, false);\n\t\t\t\tFS_Manifest_ParseTokens(man);\n\t\t\t}\n\t\t\tZ_Free(dup);\n\t\t}\n\t}\n\treturn man;\n}\n\n/*\n================\nCOM_Gamedir\n\nSets the gamedir and path to a different directory.\n================\n*/\nvoid COM_Gamedir (const char *dir, const struct gamepacks *packagespaths)\n{\n\tftemanifest_t *man;\n\tCOM_FlushTempoaryPacks();\n\n\tif (!fs_manifest)\n\t\tFS_ChangeGame(NULL, true, false);\n\n\t//we do allow empty here, for base.\n\tif (*dir && !FS_GamedirIsOkay(dir, true))\n\t{\n\t\tCon_Printf (\"Gamedir should be a single filename, not \\\"%s\\\"\\n\", dir);\n\t\treturn;\n\t}\n\n\tman = FS_Manifest_ChangeGameDir(dir);\n\twhile(packagespaths && (packagespaths->package || packagespaths->path))\n\t{\n\t\tchar quot[MAX_QPATH];\n\t\tchar quot2[MAX_OSPATH];\n\t\tchar quot3[MAX_OSPATH];\n\t\tchar quot4[MAX_OSPATH];\n\t\tCmd_TokenizeString(va(\"package %s %s%s %s%s %s%s\",\n\t\t\t\t\tCOM_QuotedString(packagespaths->path, quot, sizeof(quot), false),\t//name\n\t\t\t\t\tpackagespaths->subpath?\"prefix \":\"\", packagespaths->subpath?COM_QuotedString(packagespaths->subpath, quot2, sizeof(quot2), false):\"\",\t//prefix\n\t\t\t\t\tpackagespaths->url    ?\"mirror \":\"\", packagespaths->url    ?COM_QuotedString(packagespaths->url, quot3, sizeof(quot3), false):\"\",\t//mirror\n\t\t\t\t\tpackagespaths->package?\"name \"  :\"\", packagespaths->package?COM_QuotedString(packagespaths->package, quot4, sizeof(quot4), false):\"\"\t//\n\t\t\t\t), false, false);\n\t\tFS_Manifest_ParseTokens(man);\n\t\tpackagespaths++;\n\t}\n\tFS_ChangeGame(man, cfg_reload_on_gamedir.ival, false);\n\n#ifdef HAVE_SERVER\n\tif (!*dir)\n\t\tdir = FS_GetGamedir(true);\n\tInfoBuf_SetStarKey (&svs.info, \"*gamedir\", dir);\n#endif\n}\n\nstatic void QDECL Q_strnlowercatz(char *d, const char *s, int n)\n{\n\tint c = strlen(d);\n\td += c;\n\tn -= c;\n\tn -= 1;\t//for the null\n\twhile (*s && n-- > 0)\n\t{\n\t\tif (*s >= 'A' && *s <= 'Z')\n\t\t\t*d = (*s-'A') + 'a';\n\t\telse\n\t\t\t*d = *s;\n\t\td++;\n\t\ts++;\n\t}\n\t*d = 0;\n}\n\n//pname must be of the form \"gamedir/foo.pk3\"\n//as a special exception, we allow \"downloads/*.pk3 too\"\nqboolean FS_GenCachedPakName(const char *pname, const char *crc, char *local, int llen)\n{\n\tconst char *fn;\n\tchar hex[16];\n\tif (strstr(pname, \"dlcache\"))\n\t{\n\t\t*local = 0;\n\t\treturn false;\n\t}\n\n\tif (!strncmp(pname, \"downloads/\", 10))\n\t{\n\t\t*local = 0;\n\t\tQ_strnlowercatz(local, pname, llen);\n\t\treturn true;\n\t}\n\n\tfor (fn = pname; *fn; fn++)\n\t{\n\t\tif (*fn == '\\\\' || *fn == '/')\n\t\t{\n\t\t\tfn++;\n\t\t\tbreak;\n\t\t}\n\t}\n//\tfn = COM_SkipPath(pname);\n\tif (fn == pname || !*fn)\n\t{\t//only allow it if it has some game path first.\n\t\t*local = 0;\n\t\treturn false;\n\t}\n\tQ_strncpyz(local, pname, min((fn - pname) + 1, llen));\n\tQ_strncatz(local, \"dlcache/\", llen);\n\tQ_strnlowercatz(local, fn, llen);\n\tif (crc && *crc)\n\t{\n\t\tQ_strncatz(local, \".\", llen);\n\t\tsnprintf(hex, sizeof(hex), \"%0x\", (unsigned int)strtoul(crc, NULL, 0));\n\t\tQ_strncatz(local, hex, llen);\n\t}\n\treturn true;\n}\n\n#ifdef HAVE_CLIENT\n#if 0\nqboolean FS_LoadPackageFromFile(vfsfile_t *vfs, char *pname, char *localname, int *crc, unsigned int flags)\n{\n\tint i;\n\tchar *ext = \"zip\";//(pname);\n\tsearchpathfuncs_t *handle;\n\tsearchpath_t *oldlist = NULL;\n\n\tsearchpath_t *sp;\n\n\tcom_fschanged = true;\n\n\tfor (i = 0; i < sizeof(searchpathformats)/sizeof(searchpathformats[0]); i++)\n\t{\n\t\tif (!searchpathformats[i].extension || !searchpathformats[i].OpenNew)\n\t\t\tcontinue;\n\t\tif (!Q_strcasecmp(ext, searchpathformats[i].extension))\n\t\t{\n\t\t\thandle = searchpathformats[i].OpenNew (vfs, localname);\n\t\t\tif (!handle)\n\t\t\t{\n\t\t\t\tCon_Printf(\"file %s isn't a %s after all\\n\", pname, searchpathformats[i].extension);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (crc)\n\t\t\t{\n\t\t\t\tint truecrc = handle->GeneratePureCRC(handle, 0, false);\n\t\t\t\tif (truecrc != *crc)\n\t\t\t\t{\n\t\t\t\t\t*crc = truecrc;\n\t\t\t\t\thandle->ClosePath(handle);\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t\tsp = FS_AddPathHandle(&oldlist, pname, localname, handle, flags, (unsigned int)-1);\n\n\t\t\tif (sp)\n\t\t\t{\n\t\t\t\tcom_fschanged = true;\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t}\n\n\tVFS_CLOSE(vfs);\n\treturn false;\n}\n#endif\n\n//'small' wrapper to open foo.zip/bar to read files within zips that are not part of the gamedir.\n//name needs to be null terminated. recursive. pass null for search.\n//name is restored to its original state after the call, only technically not const\nvfsfile_t *CL_OpenFileInPackage(searchpathfuncs_t *search, char *name)\n{\n\tint found;\n\tvfsfile_t *f;\n\tflocation_t loc;\n\tchar e, *n;\n\tchar ext[8];\n\tchar *end;\n\tint i;\n\n\t//keep chopping off the last part of the filename until we get an actual package\n\t//once we do, recurse into that package\n\n\tend = name + strlen(name);\n\n\twhile (end > name)\n\t{\n\t\te = *end;\n\t\t*end = 0;\n\n\t\tif (!e)\n\t\t{\n\t\t\t//always open the last file properly.\n\t\t\tloc.search = NULL;\n\t\t\tif (search)\n\t\t\t\tfound = search->FindFile(search, &loc, name, NULL);\n\t\t\telse\n\t\t\t\tfound = FS_FLocateFile(name, FSLF_IFFOUND, &loc); \n\t\t\tif (found)\n\t\t\t{\n\t\t\t\tf = (search?search:loc.search->handle)->OpenVFS(search?search:loc.search->handle, &loc, \"rb\");\n\t\t\t\tif (f)\n\t\t\t\t\treturn f;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tCOM_FileExtension(name, ext, sizeof(ext));\n\t\t\tfor (i = 0; i < sizeof(searchpathformats)/sizeof(searchpathformats[0]); i++)\n\t\t\t{\n\t\t\t\tif (!searchpathformats[i].extension || !searchpathformats[i].OpenNew)\n\t\t\t\t\tcontinue;\n\t\t\t\tif (!Q_strcasecmp(ext, searchpathformats[i].extension))\n\t\t\t\t{\n\t\t\t\t\tloc.search = NULL;\n\t\t\t\t\tif (search)\n\t\t\t\t\t\tfound = search->FindFile(search, &loc, name, NULL);\n\t\t\t\t\telse\n\t\t\t\t\t\tfound = FS_FLocateFile(name, FSLF_IFFOUND, &loc); \n\t\t\t\t\tif (found)\n\t\t\t\t\t{\n\t\t\t\t\t\tf = (search?search:loc.search->handle)->OpenVFS(search?search:loc.search->handle, &loc, \"rb\");\n\t\t\t\t\t\tif (f)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tsearchpathfuncs_t *newsearch = searchpathformats[i].OpenNew(f, search?search:loc.search->handle, name, name, \"\");\n\t\t\t\t\t\t\tif (newsearch)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tf = CL_OpenFileInPackage(newsearch, end+1);\n\t\t\t\t\t\t\t\tnewsearch->ClosePath(newsearch);\n\t\t\t\t\t\t\t\tif (f)\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t*end = e;\n\t\t\t\t\t\t\t\t\treturn f;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\tVFS_CLOSE(f);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tn = COM_SkipPath(name);\n\t\t*end = e;\n\t\tend = n-1;\n\t}\n\treturn NULL;\n}\n\n//some annoying struct+func to prefix the enumerated file name properly.\nstruct CL_ListFilesInPackageCB_s\n{\n\tchar *nameprefix;\n\tsize_t nameprefixlen;\n\n\tint (QDECL *func)(const char *fname, qofs_t fsize, time_t mtime, void *parm, searchpathfuncs_t *spath);\n\tvoid *parm;\n\tsearchpathfuncs_t *spath;\n};\nstatic int QDECL CL_ListFilesInPackageCB(const char *fname, qofs_t fsize, time_t mtime, void *parm, searchpathfuncs_t *spath)\n{\n\tstruct CL_ListFilesInPackageCB_s *cb = parm;\n\tchar name[MAX_OSPATH];\n\tif (cb->nameprefixlen)\n\t{\n\t\tmemcpy(name, cb->nameprefix, cb->nameprefixlen-1);\n\t\tname[cb->nameprefixlen-1] = '/';\n\t\tQ_strncpyz(name+cb->nameprefixlen, fname, sizeof(name)-(cb->nameprefixlen));\n\t\treturn cb->func(name, fsize, mtime, cb->parm, cb->spath);\n\t}\n\telse\n\t\treturn cb->func(fname, fsize, mtime, cb->parm, cb->spath);\n}\n\n//'small' wrapper to list foo.zip/* to list files within zips that are not part of the gamedir.\n//same rules as CL_OpenFileInPackage, except that wildcards should only be in the final part\nqboolean CL_ListFilesInPackage(searchpathfuncs_t *search, char *name, int (QDECL *func)(const char *fname, qofs_t fsize, time_t mtime, void *parm, searchpathfuncs_t *spath), void *parm, void *recursioninfo)\n{\n\tint found;\n\tvfsfile_t *f;\n\tflocation_t loc;\n\tchar e, *n;\n\tchar ext[8];\n\tchar *end;\n\tint i;\n\tqboolean ret = false;\n\tstruct CL_ListFilesInPackageCB_s cb;\n\tcb.nameprefix = recursioninfo?recursioninfo:name;\n\tcb.nameprefixlen = name-cb.nameprefix;\n\tcb.func = func;\n\tcb.parm = parm;\n\n\t//keep chopping off the last part of the filename until we get an actual package\n\t//once we do, recurse into that package\n\n\tend = name + strlen(name);\n\n\twhile (end > name)\n\t{\n\t\te = *end;\n\t\t*end = 0;\n\n\t\tCOM_FileExtension(name, ext, sizeof(ext));\n\t\tfor (i = 0; i < countof(searchpathformats); i++)\n\t\t{\n\t\t\tif (!searchpathformats[i].extension || !searchpathformats[i].OpenNew)\n\t\t\t\tcontinue;\n\t\t\tif (!Q_strcasecmp(ext, searchpathformats[i].extension))\n\t\t\t{\n\t\t\t\tloc.search = NULL;\n\t\t\t\tif (search)\n\t\t\t\t\tfound = search->FindFile(search, &loc, name, NULL);\n\t\t\t\telse\n\t\t\t\t\tfound = FS_FLocateFile(name, FSLF_IFFOUND, &loc); \n\t\t\t\tif (found)\n\t\t\t\t{\n\t\t\t\t\tf = (search?search:loc.search->handle)->OpenVFS(search?search:loc.search->handle, &loc, \"rb\");\n\t\t\t\t\tif (f)\n\t\t\t\t\t{\n\t\t\t\t\t\tsearchpathfuncs_t *newsearch = searchpathformats[i].OpenNew(f, search?search:loc.search->handle, name, name, \"\");\n\t\t\t\t\t\tif (newsearch)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tret = CL_ListFilesInPackage(newsearch, end+1, func, parm, cb.nameprefix);\n\t\t\t\t\t\t\tnewsearch->ClosePath(newsearch);\n\t\t\t\t\t\t\tif (ret)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t*end = e;\n\t\t\t\t\t\t\t\treturn ret;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tVFS_CLOSE(f);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tn = COM_SkipPath(name);\n\t\t*end = e;\n\t\tend = n-1;\n\t}\n\n\t//always open the last file properly.\n\tloc.search = NULL;\n\tif (search)\n\t\tret = search->EnumerateFiles(search, name, CL_ListFilesInPackageCB, &cb);\n\telse\n\t{\n\t\tret = true;\n\t\tif (ret)\n\t\t\tCOM_EnumerateFiles(name, CL_ListFilesInPackageCB, &cb);\n\t}\n\treturn ret;\n}\n\nvoid FS_PureMode(const char *gamedir, int puremode, char *purenamelist, char *purecrclist, char *refnamelist, char *refcrclist, int pureseed)\n{\n\tftemanifest_t *man;\n\tqboolean pureflush;\n\n#ifdef HAVE_SERVER\n\t//if we're the server, we can't be impure.\n\tif (sv.state)\n\t\treturn;\n#endif\n\n\tif (puremode == fs_puremode && fs_pureseed == pureseed)\n\t{\n\t\tif ((!purenamelist && !fs_purenames) || !strcmp(fs_purenames?fs_purenames:\"\", purenamelist?purenamelist:\"\"))\n\t\t\tif ((!purecrclist && !fs_purecrcs) || !strcmp(fs_purecrcs?fs_purecrcs:\"\", purecrclist?purecrclist:\"\"))\n\t\t\t\tif ((!refnamelist && !fs_refnames) || !strcmp(fs_refnames?fs_refnames:\"\", refnamelist?refnamelist:\"\"))\n\t\t\t\t\tif ((!refcrclist && !fs_refcrcs) || !strcmp(fs_refcrcs?fs_refcrcs:\"\", refcrclist?refcrclist:\"\"))\n\t\t\t\t\t\treturn;\n\t}\n\n\tZ_Free(fs_purenames);\n\tZ_Free(fs_purecrcs);\n\tZ_Free(fs_refnames);\n\tZ_Free(fs_refcrcs);\n\n\tpureflush = (fs_puremode != 2 && puremode == 2);\n\tfs_puremode = puremode;\n\tfs_purenames = purenamelist?Z_StrDup(purenamelist):NULL;\n\tfs_purecrcs = purecrclist?Z_StrDup(purecrclist):NULL;\n\tfs_pureseed = pureseed;\n\tfs_refnames = refnamelist?Z_StrDup(refnamelist):NULL;\n\tfs_refcrcs = refcrclist?Z_StrDup(refcrclist):NULL;\n\n\tif (gamedir)\n\t\tman\t= FS_Manifest_ChangeGameDir(gamedir);\n\telse\n\t\tman = fs_manifest;\n\n\tFS_ChangeGame(man, false, false);\n\n\tif (pureflush)\n\t{\n#ifdef HAVE_CLIENT\n\t\tShader_NeedReload(true);\n#endif\n\t\tMod_ClearAll();\n\t\tCache_Flush();\n\t}\n}\n\nint FS_PureOkay(void)\n{\n\tqboolean ret = true;\n\t//returns true if all pure packages that we're meant to need could load.\n\t//if they couldn't then they won't override things, or the game will just be completely screwed due to having absolutely no game data\n\tif (fs_puremode == 1 && fs_purenames && *fs_purenames && fs_purecrcs && *fs_purecrcs)\n\t{\n\t\tchar crctok[64];\n\t\tchar nametok[MAX_QPATH];\n\t\tchar nametok2[MAX_QPATH];\n\t\tsearchpath_t *sp = com_purepaths;\n\t\tchar *names = fs_purenames, *pname;\n\t\tchar *crcs = fs_purecrcs;\n\t\tint crc;\n\t\tqboolean required;\n\n\t\twhile(names && crcs)\n\t\t{\n\t\t\tcrcs = COM_ParseOut(crcs, crctok, sizeof(crctok));\n\t\t\tnames = COM_ParseOut(names, nametok, sizeof(nametok));\n\n\t\t\tcrc = strtoul(crctok, NULL, 0);\n\t\t\tif (!crc)\n\t\t\t\tcontinue;\n\n\t\t\tpname = nametok;\n\n\t\t\tif (fs_refnames && fs_refcrcs)\n\t\t\t{\t//q3 is annoying as hell\n\t\t\t\tint crc2;\n\t\t\t\tchar *rc = fs_refcrcs;\n\t\t\t\tchar *rn = fs_refnames;\n\t\t\t\tpname = \"\";\n\t\t\t\tfor (; rc && rn; )\n\t\t\t\t{\n\t\t\t\t\trc = COM_ParseOut(rc, crctok, sizeof(crctok));\n\t\t\t\t\trn = COM_ParseOut(rn, nametok2, sizeof(nametok2));\n\t\t\t\t\tcrc2 = strtoul(crctok, NULL, 0);\n\t\t\t\t\tif (crc2 == crc)\n\t\t\t\t\t{\n\t\t\t\t\t\tCOM_DefaultExtension(nametok2, \".pk3\", sizeof(nametok2));\n\t\t\t\t\t\tpname = nametok2;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\trequired = *pname == '*';\n\t\t\tif (*pname == '*')\t// * means that its 'referenced' (read: actually useful) thus should be downloaded, which is not relevent here.\n\t\t\t{\n\t\t\t\trequired = true;\n\t\t\t\tpname++;\n\t\t\t}\n\t\t\telse\n\t\t\t\trequired = false;\n\n\t\t\tif (sp && sp->crc_check == crc)\n\t\t\t{\n\t\t\t\tsp = sp->nextpure;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\telse if (!required)\t//if its not referenced, then its not needed, and we probably didn't bother to download it. this might be an issue with sv_pure 1, but that has its own issues.\n\t\t\t\tcontinue;\n\t\t\telse //if (!sp)\n\t\t\t{\n//\t\t\t\tif (!CL_CheckDLFile(va(\"package/%s\", pname)))\n//\t\t\t\t\tif (CL_CheckOrEnqueDownloadFile(pname, va(\"%s.%i\", pname, crc), DLLF_NONGAME))\n//\t\t\t\t\t\treturn -1;\n\t\t\t\tCon_Printf(CON_ERROR\"Pure package %s:%08x missing.\\n\", pname, crc);\n\t\t\t\tret = false;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn ret;\n}\n#endif\n\n#ifdef Q3CLIENT\nchar *FSQ3_GenerateClientPacksList(char *buffer, int maxlen, int basechecksum)\n{\t//this is for q3 compatibility.\n\n\tflocation_t loc;\n\tint numpaks = 0;\n\tsearchpath_t *sp;\n\n\tif (FS_FLocateFile(\"vm/cgame.qvm\", FSLF_IFFOUND, &loc))\n\t{\n\t\tQ_strncatz(buffer, va(\"%i \", loc.search->crc_reply), maxlen);\n\t\tbasechecksum ^= loc.search->crc_reply;\n\t}\n\telse Q_strncatz(buffer, va(\"%i \", 0), maxlen);\n\n\tif (FS_FLocateFile(\"vm/ui.qvm\", FSLF_IFFOUND, &loc))\n\t{\n\t\tQ_strncatz(buffer, va(\"%i \", loc.search->crc_reply), maxlen);\n\t\tbasechecksum ^= loc.search->crc_reply;\n\t}\n\telse Q_strncatz(buffer, va(\"%i \", 0), maxlen);\n\n\tQ_strncatz(buffer, \"@ \", maxlen);\n\n\tfor (sp = com_purepaths; sp; sp = sp->nextpure)\n\t{\n\t\tif (sp->crc_reply)\n\t\t{\n\t\t\tQ_strncatz(buffer, va(\"%i \", sp->crc_reply), maxlen);\n\t\t\tbasechecksum ^= sp->crc_reply;\n\t\t\tnumpaks++;\n\t\t}\n\t}\n\n\tbasechecksum ^= numpaks;\n\tQ_strncatz(buffer, va(\"%i \", basechecksum), maxlen);\n\n\treturn buffer;\n}\n#endif\n\n/*\n================\nFS_ReloadPackFiles\n================\n\nCalled when the client has downloaded a new pak/pk3 file\n*/\nstatic void FS_ReloadPackFilesFlags(unsigned int reloadflags)\n{\n\tsearchpath_t\t*oldpaths;\n\tsearchpath_t\t*next;\n\tint i, j;\n\tint orderkey;\n\tunsigned int fl;\n\n\tCOM_AssertMainThread(\"FS_ReloadPackFilesFlags\");\n\tCOM_WorkerFullSync();\n\n\torderkey = 0;\n\tif (com_purepaths)\n\t\tfor (next = com_purepaths; next; next = next->nextpure)\n\t\t\tnext->orderkey = ++orderkey;\n\tif (fs_puremode < 2)\n\t\tfor (next = com_purepaths; next; next = next->nextpure)\n\t\t\tnext->orderkey = ++orderkey;\n\n\toldpaths = com_searchpaths;\n\tcom_searchpaths = NULL;\n\tcom_purepaths = NULL;\n\tcom_base_searchpaths = NULL;\n\tgameonly_gamedir = gameonly_homedir = NULL;\n\n#if defined(ENGINE_HAS_ZIP) && defined(PACKAGE_PK3)\n\t{\n\t\tsearchpathfuncs_t *pak;\n\t\tvfsfile_t *vfs;\n\t\tvfs = VFSOS_Open(com_argv[0], \"rb\");\n\t\tpak = FSZIP_LoadArchive(vfs, NULL, com_argv[0], com_argv[0], \"\");\n\t\tif (pak)\t//logically should have SPF_EXPLICIT set, but that would give it a worse gamedir depth\n\t\t{\n\t\t\tFS_AddPathHandle(&oldpaths, \"\", com_argv[0], pak, \"\", SPF_COPYPROTECTED, reloadflags);\n\t\t}\n\t}\n#endif\n\n/*#ifdef FTE_SDL3\n\t{\t//FIXME: this is generally just $binarydir/\n\t\tsearchpathfuncs_t *pak = Sys_OpenTitleStore();\n\t\tif (pak)\n\t\t\tFS_AddPathHandle(&oldpaths, \"\", \"$sdldata\", pak, \"\", SPF_PRIVATE|SPF_COPYPROTECTED|SPF_VIRTUAL, reloadflags);\n\t}\n#endif*/\n\n#if defined(HAVE_LEGACY) && defined(PACKAGE_PK3)\n\t{\n\t\tsearchpathfuncs_t *pak;\n\t\tvfsfile_t *vfs;\n\t\tchar pakname[MAX_OSPATH];\n\t\tint i;\n\t\tstatic char *names[] = {\"QuakeEX.kpf\", \"Q2Game.kpf\"};\t//need a better way to handle this rubbish. fucking mod-specific translations being stored in the engine-specific data.\n\t\tfor (i = 0; i < countof(names); i++)\n\t\t{\n\t\t\tQ_snprintfz(pakname, sizeof(pakname), \"%s%s\", com_gamepath, names[i]);\n\t\t\tvfs = VFSOS_Open(pakname, \"rb\");\n\t\t\tpak = FSZIP_LoadArchive(vfs, NULL, pakname, pakname, \"\");\n\t\t\tif (pak)\t//logically should have SPF_EXPLICIT set, but that would give it a worse gamedir depth\n\t\t\t{\n\t\t\t\tFS_AddPathHandle(&oldpaths, \"\", pakname, pak, \"\", SPF_COPYPROTECTED, reloadflags);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n#endif\n\n\ti = COM_CheckParm (\"-basepack\");\n\twhile (i && i < com_argc-1)\n\t{\n\t\tconst char *pakname = com_argv[i+1];\n\t\tsearchpathfuncs_t *pak;\n\t\tconst char *prefix = (i+2 >= com_argc)?NULL:com_argv[i+2];\n\t\tvfsfile_t *vfs = VFSOS_Open(pakname, \"rb\");\n\t\tif (!prefix || *prefix == '-' || *prefix == '+')\n\t\t\tprefix = NULL;\t//some other came here.\n\t\tpak = FS_OpenPackByExtension(vfs, NULL, pakname, pakname, prefix);\n\t\tif (pak)\t//logically should have SPF_EXPLICIT set, but that would give it a worse gamedir depth\n\t\t\tFS_AddPathHandle(&oldpaths, \"\", pakname, pak, prefix, SPF_COPYPROTECTED, reloadflags);\n\t\ti = COM_CheckNextParm (\"-basepack\", i);\n\t}\n\n#ifdef NQPROT\n\tstandard_quake = true;\n#endif\n\tfor (i = 0; i < countof(fs_manifest->gamepath); i++)\n\t{\n\t\tchar *dir = fs_manifest->gamepath[i].path;\n\t\tif (dir && (fs_manifest->gamepath[i].flags&GAMEDIR_BASEGAME))\n\t\t{\n\t\t\t//paths should be validated before here, when parsing the manifest.\n\t\t\t\n#ifdef NQPROT\n\t\t\t//vanilla NQ uses a slightly different protocol when started with -rogue or -hipnotic (and by extension -quoth).\n\t\t\t//QW+FTE protocols don't care so we can get away with being a little loose here\n\t\t\tif (!strcmp(dir, \"rogue\") || !strcmp(dir, \"hipnotic\") || !strcmp(dir, \"quoth\"))\n\t\t\t\tstandard_quake = false;\n#endif\n\n\t\t\tfl = SPF_EXPLICIT;\n\t\t\tif (!(fs_manifest->gamepath[i].flags&GAMEDIR_READONLY))\n\t\t\t\tfl |= SPF_WRITABLE;\n\t\t\tif (fs_manifest->gamepath[i].flags&GAMEDIR_PRIVATE)\n\t\t\t\tfl |= SPF_PRIVATE;\n\t\t\tif (fs_manifest->gamepath[i].flags&GAMEDIR_QSHACK)\n\t\t\t\tfl |= SPF_QSHACK;\n\n\t\t\tif (fs_manifest->gamepath[i].flags&GAMEDIR_USEBASEDIR)\n\t\t\t{\t//for doom - loading packages without an actual gamedir. note that this does not imply that we can write anything.\n\t\t\t\tsearchpathfuncs_t *handle = VFSOS_OpenPath(NULL, NULL, com_gamepath, com_gamepath, \"\");\n\t\t\t\tsearchpath_t *search = (searchpath_t*)Z_Malloc (sizeof(searchpath_t));\n\t\t\t\tsearch->flags = 0;\n\t\t\t\tsearch->handle = handle;\n\t\t\t\tQ_strncpyz(search->purepath, \"\", sizeof(search->purepath));\n\t\t\t\tQ_strncpyz(search->logicalpath, com_gamepath, sizeof(search->logicalpath));\n\n\t\t\t\tFS_AddDataFiles(&oldpaths, search->purepath, search->logicalpath, search, fl, reloadflags);\n\n\t\t\t\thandle->ClosePath(handle);\n\t\t\t\tZ_Free(search);\n\t\t\t}\n\t\t\telse if (fs_manifest->gamepath[i].flags&GAMEDIR_STEAMGAME)\n\t\t\t{\n\t\t\t\tchar steamdir[MAX_OSPATH];\n\t\t\t\tchar *sl;\n\t\t\t\tdir += 6;\n\t\t\t\tsl = strchr(dir, '/');\n\t\t\t\tif (*sl)\n\t\t\t\t{\n\t\t\t\t\tif (Sys_SteamHasFile(steamdir, sizeof(steamdir), dir, \"\"))\n\t\t\t\t\t\tFS_AddSingleGameDirectory(&oldpaths, /*pure*/dir, steamdir, reloadflags, SPF_COPYPROTECTED|(fl&~SPF_WRITABLE));\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (!FS_GamedirIsOkay(dir, true))\n\t\t\t\t\tcontinue;\n\t\t\t\tFS_AddGameDirectory(&oldpaths, dir, reloadflags, fl);\n\t\t\t}\n\t\t}\n\t}\n\n\t//now mark the depth values\n\tif (com_searchpaths)\n\t\tfor (next = com_searchpaths->next; next; next = next->next)\n\t\t\tnext->flags |= SPF_BASEPATH;\n\tcom_base_searchpaths = com_searchpaths;\n\n\tfor (i = 0; i < countof(fs_manifest->gamepath); i++)\n\t{\n\t\tchar *dir = fs_manifest->gamepath[i].path;\n\t\tif (dir && !(fs_manifest->gamepath[i].flags&GAMEDIR_BASEGAME))\n\t\t{\n\t\t\tfor (j = 0; j < countof(fs_manifest->gamepath); j++)\n\t\t\t{\n\t\t\t\tchar *dir2 = fs_manifest->gamepath[j].path;\n\t\t\t\tif (dir2 && (fs_manifest->gamepath[i].flags&GAMEDIR_BASEGAME) && !strcmp(dir, dir2))\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (j < countof(fs_manifest->gamepath))\n\t\t\t\tcontinue;\t//already loaded above. don't mess up gameonly_gamedir.\n\n\t\t\tfl = SPF_EXPLICIT;\n\t\t\tif (!(fs_manifest->gamepath[i].flags&GAMEDIR_READONLY))\n\t\t\t\tfl |= SPF_WRITABLE;\n\t\t\tif (fs_manifest->gamepath[i].flags&GAMEDIR_PRIVATE)\n\t\t\t\tfl |= SPF_PRIVATE;\n\t\t\tif (fs_manifest->gamepath[i].flags&GAMEDIR_QSHACK)\n\t\t\t\tfl |= SPF_QSHACK;\n\n\t\t\tif (*dir == '*')\n\t\t\t{\t//just in case... shouldn't be needed.\n\t\t\t\tdir++;\n\t\t\t\tfl |= GAMEDIR_PRIVATE;\n\t\t\t}\n\n\t\t\tif (fs_manifest->gamepath[i].flags & GAMEDIR_SPECIAL)\n\t\t\t\t; //don't.\n\t\t\telse\n\t\t\t{\n\t\t\t\t//don't use evil gamedir names.\n\t\t\t\tif (!FS_GamedirIsOkay(dir, true))\n\t\t\t\t\tcontinue;\n\t\t\t\tFS_AddGameDirectory(&oldpaths, dir, reloadflags, fl);\n\t\t\t}\n\t\t}\n\t}\n\n\tFS_AddDownloadManifestPackages(&oldpaths, reloadflags);\n\n\t/*sv_pure: Reload pure paths*/\n\tif (fs_purenames && fs_purecrcs)\n\t{\n\t\tchar crctok[64];\n\t\tchar nametok[MAX_QPATH];\n\t\tchar nametok2[MAX_QPATH];\n\t\tsearchpath_t *sp, *lastpure = NULL;\n\t\tchar *names = fs_purenames, *pname;\n\t\tchar *crcs = fs_purecrcs;\n\t\tint crc;\n\n\t\tfor (sp = com_searchpaths; sp; sp = sp->next)\n\t\t{\n\t\t\tif (sp->handle->GeneratePureCRC)\n\t\t\t{\n\t\t\t\tsp->nextpure = (void*)0x1;\n\t\t\t\tif (sp->crc_seed != fs_pureseed)\n\t\t\t\t{\n\t\t\t\t\tsp->crc_seed = fs_pureseed;\n\t\t\t\t\tsp->crc_reply = sp->handle->GeneratePureCRC(sp->handle, &sp->crc_seed);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tsp->nextpure = NULL;\n\t\t\t\tsp->crc_check = 0;\n\t\t\t\tsp->crc_reply = 0;\n\t\t\t}\n\t\t}\n\n\t\twhile(names && crcs)\n\t\t{\n\t\t\tcrcs = COM_ParseOut(crcs, crctok, sizeof(crctok));\n\t\t\tnames = COM_ParseOut(names, nametok, sizeof(nametok));\n\n\t\t\tcrc = strtoul(crctok, NULL, 0);\n\t\t\tif (!*crctok)\n\t\t\t\tcontinue;\n\t\t\tif (!strcmp(crctok, \"-\"))\n\t\t\t\t*crctok = 0;\n\n\t\t\tpname = nametok;\n\n\t\t\tif (fs_refnames && fs_refcrcs)\n\t\t\t{\t//q3 is annoying as hell\n\t\t\t\tint crc2;\n\t\t\t\tchar *rc = fs_refcrcs;\n\t\t\t\tchar *rn = fs_refnames;\n\t\t\t\tpname = \"\";\n\t\t\t\tfor (; rc && rn; )\n\t\t\t\t{\n\t\t\t\t\trc = COM_ParseOut(rc, crctok, sizeof(crctok));\n\t\t\t\t\trn = COM_ParseOut(rn, nametok2, sizeof(nametok2));\n\t\t\t\t\tcrc2 = strtoul(crctok, NULL, 0);\n\t\t\t\t\tif (crc2 == crc)\n\t\t\t\t\t{\n\t\t\t\t\t\tCOM_DefaultExtension(nametok2, \".pk3\", sizeof(nametok2));\n\t\t\t\t\t\tpname = nametok2;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (*pname == '*')\t// * means that its 'referenced' (read: actually useful) thus should be downloaded, which is not relevent here.\n\t\t\t\tpname++;\n\n\t\t\tfor (sp = com_searchpaths; sp; sp = sp->next)\n\t\t\t{\n\t\t\t\tif (sp->nextpure == (void*)0x1)\t//don't add twice.\n\t\t\t\t\tif ((*crctok && sp->crc_check == crc) ||\n\t\t\t\t\t\t(!*crctok && !strcmp(COM_SkipPath(sp->purepath), COM_SkipPath(pname))))\n\t\t\t\t\t{\n\t\t\t\t\t\tif (fs_puremode)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (lastpure)\n\t\t\t\t\t\t\t\tlastpure->nextpure = sp;\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\tcom_purepaths = sp;\n\t\t\t\t\t\t\tsp->nextpure = NULL;\n\t\t\t\t\t\t\tlastpure = sp;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t}\n\t\t\tif (!fs_puremode && !sp)\n\t\t\t{\t//if we're not pure, we don't care if the version differs. don't load the server's version.\n\t\t\t\t//this works around 1.01 vs 1.06 issues.\n\t\t\t\tfor (sp = com_searchpaths; sp; sp = sp->next)\n\t\t\t\t{\n\t\t\t\t\tif (!Q_strcasecmp(pname, sp->purepath))\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\t//if its not already loaded (via wildcards), load it from the download cache, if we can\n\t\t\tif (!sp && *pname)\n\t\t\t{\n\t\t\t\tchar local[MAX_OSPATH];\n\t\t\t\tchar rlocal[MAX_OSPATH];\n\t\t\t\tvfsfile_t *vfs;\n\t\t\t\tchar ext[8];\n\t\t\t\tvoid *handle;\n\t\t\t\tint i;\n\t\t\t\tCOM_FileExtension(pname, ext, sizeof(ext));\n\n\t\t\t\tif (FS_GenCachedPakName(pname, crctok, rlocal, sizeof(rlocal)) && FS_SystemPath(rlocal, FS_ROOT, local, sizeof(local)))\n\t\t\t\t{\n\t\t\t\t\tunsigned int keptflags;\n\n\t\t\t\t\thandle = FS_GetOldPath(&oldpaths, local, &keptflags);\n\t\t\t\t\tif (handle)\n\t\t\t\t\t{\n\t\t\t\t\t\tsp = FS_AddPathHandle(&oldpaths, pname, local, handle, \"\", SPF_COPYPROTECTED|SPF_UNTRUSTED|SPF_SERVER|keptflags, (unsigned int)-1);\n\t\t\t\t\t\tif (!sp)\n\t\t\t\t\t\t\tcontinue;\t//some kind of error...\n\t\t\t\t\t\tif ((*crctok && sp->crc_check == crc) || !*crctok)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (fs_puremode)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tif (lastpure)\n\t\t\t\t\t\t\t\t\tlastpure->nextpure = sp;\n\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t\tcom_purepaths = sp;\n\t\t\t\t\t\t\t\tsp->nextpure = NULL;\n\t\t\t\t\t\t\t\tlastpure = sp;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\t//else crc mismatched...\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tvfs = FS_OpenVFS(rlocal, \"rb\", FS_ROOT);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tvfs = NULL;\n\t\t\t\tif (vfs)\n\t\t\t\t{\n\t\t\t\t\tfor (i = 0; i < sizeof(searchpathformats)/sizeof(searchpathformats[0]); i++)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (!searchpathformats[i].extension || !searchpathformats[i].OpenNew)\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\tif (!Q_strcasecmp(ext, searchpathformats[i].extension))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\thandle = searchpathformats[i].OpenNew (vfs, NULL, local, local, \"\");\n\t\t\t\t\t\t\tif (!handle)\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tsp = FS_AddPathHandle(&oldpaths, pname, local, handle, \"\", SPF_COPYPROTECTED|SPF_UNTRUSTED|SPF_SERVER, (unsigned int)-1);\n\n\t\t\t\t\t\t\tif ((*crctok && sp->crc_check == crc) || !*crctok)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tif (fs_puremode)\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tif (lastpure)\n\t\t\t\t\t\t\t\t\t\tlastpure->nextpure = sp;\n\t\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t\t\tcom_purepaths = sp;\n\t\t\t\t\t\t\t\t\tsp->nextpure = NULL;\n\t\t\t\t\t\t\t\t\tlastpure = sp;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (!sp)\n\t\t\t\t\tCon_DPrintf(CON_ERROR\"Pure package %s:%08x wasn't found\\n\", pname, crc);\n\t\t\t}\n\t\t}\n\t}\n\n\twhile(oldpaths)\n\t{\n\t\tfs_restarts++;\n\n\t\tnext = oldpaths->next;\n\n\t\tCon_DPrintf(\"%s is no longer needed\\n\", oldpaths->logicalpath);\n\t\toldpaths->handle->ClosePath(oldpaths->handle);\n\t\tZ_Free(oldpaths);\n\t\toldpaths = next;\n\t}\n\n\n\ti = orderkey;\n\torderkey = 0;\n\tnext = NULL;\n\tif (com_purepaths)\n\t\tfor (next = com_purepaths; next; next = next->nextpure)\n\t\t\tif (next->orderkey != ++orderkey)\n\t\t\t\tbreak;\n\tif (!next && fs_puremode < 2)\n\t\tfor (next = com_purepaths; next; next = next->nextpure)\n\t\t\tif (next->orderkey != ++orderkey)\n\t\t\t\tbreak;\n\n\tif (next || i != orderkey)//some path changed. make sure the fs cache is flushed.\n\t\tFS_FlushFSHashReally(false);\n\n#ifdef HAVE_CLIENT\n\tShader_NeedReload(true);\n#endif\n//\tMod_ClearAll();\n//\tCache_Flush();\n}\n\nvoid FS_UnloadPackFiles(void)\n{\n\tif (Sys_LockMutex(fs_thread_mutex))\n\t{\n\t\tFS_ReloadPackFilesFlags(0);\n\t\tSys_UnlockMutex(fs_thread_mutex);\n\t}\n}\n\nvoid FS_ReloadPackFiles(void)\n{\n\t//extra junk is to ensure the palette is reloaded if that changed.\n\tflocation_t oldloc[countof(vidfilenames)];\n\tflocation_t newloc = {NULL};\n\tsize_t i;\n\n\tif (!FS_GameIsInitialised())\n\t{\n\t\tftemanifest_t *man = FS_ReadDefaultManifest(com_gamepath, 0, true);\n\t\tif (man)\n\t\t{\n\t\t\tFS_ChangeGame(man, true, false);\n\t\t\treturn;\n\t\t}\n\t}\n\n\tfor (i = 0; i < countof(vidfilenames); i++)\n\t\tFS_FLocateFile(vidfilenames[i], 0, &oldloc[i]);\n\n\tif (Sys_LockMutex(fs_thread_mutex))\n\t{\n\t\tFS_ReloadPackFilesFlags(~0);\n\t\tSys_UnlockMutex(fs_thread_mutex);\n\t}\n\n\tfor (i = 0; i < countof(vidfilenames); i++)\n\t{\n\t\tFS_FLocateFile(vidfilenames[i], 0, &newloc);\n\t\tif (oldloc[i].search != newloc.search)\n\t\t{\t//okay, so this file changed... reload the video stuff\n\t\t\tCbuf_AddText(\"vid_reload\\n\", RESTRICT_LOCAL);\n\t\t\tbreak;\n\t\t}\n\t}\n}\n\nstatic void FS_ReloadPackFiles_f(void)\n{\n\tif (Sys_LockMutex(fs_thread_mutex))\n\t{\n\t\tif (*Cmd_Argv(1))\n\t\t\tFS_ReloadPackFilesFlags(atoi(Cmd_Argv(1)));\n\t\telse\n\t\t\tFS_ReloadPackFilesFlags(~0);\n\t\tSys_UnlockMutex(fs_thread_mutex);\n\t}\n\tif (host_initialized)\n\t\tFS_BeginManifestUpdates();\n}\n\n#ifdef NOSTDIO\nstatic qboolean Sys_DoDirectoryPrompt(char *basepath, size_t basepathsize, const char *poshname, const char *savedname)\n{\n\treturn false;\n}\nstatic void Sys_FindBaseDirs(const char *poshname, const char *gamename, void (*callback) (void *ctx, const char *basepath), void *ctx)\n{\n}\nstatic qboolean Sys_SteamDirsWithFile(char *steamdir, char *fname, void(*callback)(void*ctx,const char*basepath),void*ctx)\n{\n\treturn false;\n}\n#elif defined(_WIN32) && !(defined(FTE_SDL)&&!defined(FTE_SDL3)) && !defined(WINRT) && !defined(_XBOX)\n#include \"winquake.h\"\n#ifdef MINGW\n#define byte BYTE\t//some versions of mingw headers are broken slightly. this lets it compile.\n#endif\nstatic qboolean Sys_SteamDirsWithFile(char *steamdir, char *fname, void(*callback)(void*ctx,const char*basepath),void*ctx)\n{\n\t/*\n\tFind where Valve's Steam distribution platform is installed.\n\tThen take a look at that location for the relevent installed app.\n\t*/\n\tvfsfile_t *f;\n\tDWORD resultlen;\n\tHKEY key = NULL;\n\tchar basepath[MAX_OSPATH];\n\n\tif (RegOpenKeyExW(HKEY_CURRENT_USER, L\"SOFTWARE\\\\Valve\\\\Steam\", 0, STANDARD_RIGHTS_READ|KEY_QUERY_VALUE, &key) == ERROR_SUCCESS)\n\t{\n\t\twchar_t suckysucksuck[MAX_OSPATH];\n\t\tresultlen = sizeof(suckysucksuck);\n\t\tRegQueryValueExW(key, L\"SteamPath\", NULL, NULL, (void*)suckysucksuck, &resultlen);\n\t\tRegCloseKey(key);\n\t\tnarrowen(basepath,sizeof(basepath), suckysucksuck);\n\t\tQ_strncatz(basepath, va(\"/SteamApps/common/%s\", steamdir), sizeof(basepath));\n\t\tif ((f = VFSOS_Open(va(\"%s/%s\", basepath, fname), \"rb\")))\n\t\t{\n\t\t\tVFS_CLOSE(f);\n\t\t\tcallback(ctx, fname);\n\t\t\treturn true;\n\t\t}\n\t}\n\treturn false;\n}\n\n#ifdef HAVE_CLIENT\nstatic INT CALLBACK StupidBrowseCallbackProc(HWND hwnd, UINT uMsg, LPARAM lp, LPARAM pData) \n{\t//'stolen' from microsoft's knowledge base.\n\t//required to work around microsoft being annoying.\n\twchar_t szDir[MAX_PATH];\n\twchar_t *foo;\n\tswitch(uMsg) \n\t{\n\tcase BFFM_INITIALIZED: \n\t\tif (GetCurrentDirectoryW(sizeof(szDir)/sizeof(TCHAR), szDir))\n\t\t{\n//\t\t\tfoo = strrchr(szDir, '\\\\');\n//\t\t\tif (foo)\n//\t\t\t\t*foo = 0;\n//\t\t\tfoo = strrchr(szDir, '\\\\');\n//\t\t\tif (foo)\n//\t\t\t\t*foo = 0;\n\t\t\tSendMessageW(hwnd, BFFM_SETSELECTION, TRUE, (LPARAM)szDir);\n\t\t}\n\t\tbreak;\n\tcase BFFM_VALIDATEFAILEDW:\n\t\tbreak;\t//FIXME: validate that the gamedir contains what its meant to\n\tcase BFFM_SELCHANGED: \n\t\tif (SHGetPathFromIDListW((LPITEMIDLIST) lp, szDir))\n\t\t{\n\t\t\twchar_t statustxt[MAX_OSPATH];\n\t\t\twhile((foo = wcschr(szDir, '\\\\')))\n\t\t\t\t*foo = '/';\n\t\t\tif (pData)\n\t\t\t\t_snwprintf(statustxt, countof(statustxt), L\"%s/%s\", szDir, pData);\n\t\t\telse\n\t\t\t\t_snwprintf(statustxt, countof(statustxt), L\"%s\", szDir);\n\t\t\tstatustxt[countof(statustxt)-1] = 0;\t//ms really suck.\n\t\t\tSendMessageW(hwnd,BFFM_SETSTATUSTEXT,0,(LPARAM)statustxt);\n\t\t}\n\t\tbreak;\n\t}\n\treturn 0;\n}\n#ifdef FTE_SDL3\n#include <SDL3/SDL.h>\n#else\nint MessageBoxU(HWND hWnd, char *lpText, char *lpCaption, UINT uType);\n#endif\n#endif\n\nqboolean Sys_DoDirectoryPrompt(char *basepath, size_t basepathsize, const char *poshname, const char *savedname)\n{\n#ifdef HAVE_CLIENT\n\twchar_t resultpath[MAX_OSPATH];\n\twchar_t title[MAX_OSPATH];\n\tBROWSEINFOW bi;\n\tLPITEMIDLIST il;\n\tmemset(&bi, 0, sizeof(bi));\n\tbi.hwndOwner = mainwindow; //note that this is usually still null\n\tbi.pidlRoot = NULL;\n\tGetCurrentDirectoryW(sizeof(resultpath)-1, resultpath);\n\tbi.pszDisplayName = resultpath;\n\n\twiden(resultpath, sizeof(resultpath), poshname);\n\t_snwprintf(title, countof(title), L\"Please locate your existing %s installation\", resultpath);\n\n#ifndef FTE_SDL\n\t//force mouse to deactivate, so that we can actually see it.\n\tINS_UpdateGrabs(false, false);\n#endif\n\n\tbi.lpszTitle = title;\n\n\tbi.ulFlags = BIF_RETURNONLYFSDIRS|BIF_STATUSTEXT;\n\tbi.lpfn = StupidBrowseCallbackProc;\n\tbi.lParam = 0;//(LPARAM)poshname;\n\tbi.iImage = 0;\n\n\til = SHBrowseForFolderW(&bi);\n\tif (il)\n\t{\n\t\tSHGetPathFromIDListW(il, resultpath);\n\t\tCoTaskMemFree(il);\n\t\tnarrowen(basepath, basepathsize, resultpath);\n\t\tif (savedname)\n\t\t{\n#ifdef FTE_SDL\n\t\t\textern SDL_Window *sdlwindow;\n\t\t\tSDL_MessageBoxButtonData buttons[] = {\n\t\t\t\t{SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT, 1, \"Preserve\"},\n\t\t\t\t{SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT, 2, \"Ask Later\"},\n\t\t\t};\n\t\t\tint button=0;\n\t\t\tstruct SDL_MessageBoxData mbdata;\n\t\t\tmemset(&mbdata, 0, sizeof(mbdata));\n\t\t\tmbdata.window = sdlwindow;\n\t\t\tmbdata.title = \"Save Instaltion path\";\n\t\t\tmbdata.message = va(\"Would you like to save the location of %s as:\\n%s\", poshname, basepath);\n\t\t\tmbdata.numbuttons = countof(buttons);\n\t\t\tmbdata.buttons = buttons;\n\t\t\tif (SDL_ShowMessageBox(&mbdata, &button) && button==1)\n#else\n\t\t\tif (MessageBoxU(mainwindow, va(\"Would you like to save the location of %s as:\\n%s\", poshname, basepath), \"Save Instaltion path\", MB_YESNO|MB_DEFBUTTON2) == IDYES)\n#endif\n\t\t\t\tMyRegSetValue(HKEY_CURRENT_USER, \"SOFTWARE\\\\\" FULLENGINENAME \"\\\\GamePaths\", savedname, REG_SZ, basepath, strlen(basepath));\n\t\t}\n\t\treturn true;\n\t}\n#endif\n\treturn false;\n}\n\nDWORD GetFileAttributesU(const char * lpFileName)\n{\n\twchar_t wide[MAX_OSPATH];\n\twiden(wide, sizeof(wide), lpFileName);\n\treturn GetFileAttributesW(wide);\n}\nstatic void Sys_FindBaseDirs(const char *poshname, const char *gamename, void (*callback) (void *ctx, const char *basepath), void *ctx)\n{\n#ifndef INVALID_FILE_ATTRIBUTES\n\t#define INVALID_FILE_ATTRIBUTES ((DWORD)-1)\n#endif\n\tchar basepath[MAX_OSPATH];\n\n\t//first, try and find it in our game paths location\n\tif (MyRegGetStringValue(HKEY_CURRENT_USER, \"SOFTWARE\\\\\" FULLENGINENAME \"\\\\GamePaths\", gamename, basepath, sizeof(basepath)))\n\t{\n\t\tif (GetFileAttributesU(basepath) != INVALID_FILE_ATTRIBUTES)\n\t\t{\n\t\t\tcallback(ctx, basepath);\n\t\t\treturn;\n\t\t}\n\t}\n\n\tif (!strcmp(gamename, \"quake_rerel\"))\n\t\tSys_SteamDirsWithFile(\"Quake/rerelease\", \"id1/pak0.pak\", callback,ctx);\n\telse if (!strcmp(gamename, \"quake\") || !strcmp(gamename, \"afterquake\") || !strcmp(gamename, \"netquake\") || !strcmp(gamename, \"spasm\") || !strcmp(gamename, \"fitz\") || !strcmp(gamename, \"tenebrae\"))\n\t{\n\t\tchar *prefix[] =\n\t\t{\n\t\t\t\"c:/quake/\",\t\t\t\t\t\t//quite a lot of people have it in c:\\quake, as that's the default install location from the quake cd.\n\t\t\t\"c:/games/quake/\",\t\t\t\t\t//personally I use this\n\n\t\t\t\"c:/nquake/\",\t\t\t\t\t\t//nquake seems to have moved out of programfiles now. woo.\n#ifdef _WIN64\n\t\t\t//quite a few people have nquake installed. FIXME: we need to an api function to read the directory for non-english-windows users.\n\t\t\tva(\"%s/nQuake/\", getenv(\"%ProgramFiles(x86)%\")),\t//64bit builds should look in both places\n\t\t\tva(\"%s/nQuake/\", getenv(\"%ProgramFiles%\")),\t\t\t//\n#else\n\t\t\tva(\"%s/nQuake/\", getenv(\"%ProgramFiles%\")),\t\t\t//32bit builds will get the x86 version anyway.\n#endif\n\t\t\tNULL\n\t\t};\n\t\tint i;\n\n\t\t//try and find it via steam\n\t\t//reads HKEY_LOCAL_MACHINE\\SOFTWARE\\Valve\\Steam\\InstallPath\n\t\t//append SteamApps\\common\\quake\n\t\t//use it if we find winquake.exe there\n\t\tif (Sys_SteamDirsWithFile(\"quake\", \"Winquake.exe\", callback,ctx))\n\t\t\tcallback(ctx, basepath);\t//oh good, they have it.\n\n\t\t//check various 'unadvertised' paths. it might be there too.\n\t\tfor (i = 0; prefix[i]; i++)\n\t\t{\n\t\t\tchar syspath[MAX_OSPATH];\n\t\t\tQ_snprintfz(syspath, sizeof(syspath), \"%sid1/pak0.pak\", prefix[i]);\n\t\t\tif (GetFileAttributesU(syspath) != INVALID_FILE_ATTRIBUTES)\n\t\t\t{\n\t\t\t\tQ_strncpyz(basepath, prefix[i], sizeof(basepath));\n\t\t\t\tcallback(ctx, basepath);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tQ_snprintfz(syspath, sizeof(syspath), \"%squake.exe\", prefix[i]);\n\t\t\tif (GetFileAttributesU(syspath) != INVALID_FILE_ATTRIBUTES)\n\t\t\t{\n\t\t\t\tQ_strncpyz(basepath, prefix[i], sizeof(basepath));\n\t\t\t\tcallback(ctx, basepath);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\t}\n\n\telse if (!strcmp(gamename, \"quake2\"))\n\t{\n\t\t//look for HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\Quake2_exe\\Path\n\t\tif (MyRegGetStringValue(HKEY_LOCAL_MACHINE, \"SOFTWARE\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\App Paths\\\\Quake2_exe\", \"Path\", basepath, sizeof(basepath)))\n\t\t{\n\t\t\tif (GetFileAttributesU(va(\"%s/quake2.exe\", basepath)) != INVALID_FILE_ATTRIBUTES)\n\t\t\t\tcallback(ctx, basepath);\n\t\t}\n\n\t\tif (Sys_SteamDirsWithFile(\"quake 2\", \"quake2.exe\", callback,ctx))\n\t\t\tcallback(ctx, basepath);\n\t}\n\n\telse if (!strcmp(gamename, \"et\"))\n\t{\n\t\t//reads HKEY_LOCAL_MACHINE\\SOFTWARE\\Activision\\Wolfenstein - Enemy Territory\n\t\tif (MyRegGetStringValue(HKEY_LOCAL_MACHINE, \"SOFTWARE\\\\Activision\\\\Wolfenstein - Enemy Territory\", \"InstallPath\", basepath, sizeof(basepath)))\n\t\t{\n//\t\t\tif (GetFileAttributesU(va(\"%s/ET.exe\", basepath) != INVALID_FILE_ATTRIBUTES)\n//\t\t\t\t...\n\t\t\tcallback(ctx, basepath);\n\t\t}\n\t}\n\n\telse if (!strcmp(gamename, \"quake3\"))\n\t{\n\t\t//reads HKEY_LOCAL_MACHINE\\SOFTWARE\\id\\Quake III Arena\\InstallPath\n\t\tif (MyRegGetStringValue(HKEY_LOCAL_MACHINE, \"SOFTWARE\\\\id\\\\Quake III Arena\", \"InstallPath\", basepath, sizeof(basepath)))\n\t\t{\n\t\t\tif (GetFileAttributesU(va(\"%s/quake3.exe\", basepath)) != INVALID_FILE_ATTRIBUTES)\n\t\t\t\tcallback(ctx, basepath);\n\t\t}\n\n\t\tif (Sys_SteamDirsWithFile(\"quake 3 arena\", \"quake3.exe\", callback,ctx))\n\t\t\tcallback(ctx, basepath);\n\t}\n\n\telse if (!strcmp(gamename, \"wop\"))\n\t{\n\t\tif (MyRegGetStringValue(HKEY_LOCAL_MACHINE, \"SOFTWARE\\\\World Of Padman\", \"Path\", basepath, sizeof(basepath)))\n\t\t\tcallback(ctx, basepath);\n\t}\n\n/*\n\telse if (!strcmp(gamename, \"d3\"))\n\t{\n\t\t//reads HKEY_LOCAL_MACHINE\\SOFTWARE\\id\\Doom 3\\InstallPath\n\t\tif (MyRegGetStringValue(HKEY_LOCAL_MACHINE, L\"SOFTWARE\\\\id\\\\Doom 3\", \"InstallPath\", basepath, sizeof(basepath)))\n\t\t\tcallback(ctx, basepath);\n\t}\n*/\n\n\telse if (!strcmp(gamename, \"hexen2\") || !strcmp(gamename, \"h2mp\"))\n\t{\n\t\t//append SteamApps\\common\\hexen 2\n\t\tif (Sys_SteamDirsWithFile(\"hexen 2\", \"glh2.exe\", callback,ctx))\n\t\t\tcallback(ctx, basepath);\n\t}\n}\n#else\n#if (defined(__linux__) || defined(__unix__) || defined(__apple__)) && !defined(ANDROID)\n#include <sys/stat.h>\n\nstatic qboolean Sys_SteamLibraryHasFile(char *basepath, int basepathlen, char *librarypath, char *steamdir, char *fname)\t//returns the base system path\n{\n\tQ_snprintfz(basepath, basepathlen, \"%s/steamapps/common/%s\", librarypath, steamdir);\n\tif (0==access(va(\"%s/%s\", basepath, fname), R_OK))\n\t\treturn true;\n\treturn false;\n}\nstatic qboolean Sys_SteamParseLibraries(void(*callback)(void*ctx,const char*basepath),void*ctx, char *libraryfile, char *steamdir, char *fname)\t//returns the base system path\n{\n\tqboolean success = false;\n\tchar key[1024], *end;\n\tchar value[1024];\n\tchar basepath[1024];\n\tchar *lib = libraryfile;\n\tint depth = 0;\n\tif (!libraryfile)\n\t\treturn false;\n\tlib = COM_ParseCString(lib, key, sizeof(key), NULL);\n\tlib = COM_ParseCString(lib, value, sizeof(value), NULL);\n\tif (!strcmp(key, \"libraryfolders\") && !strcmp(value, \"{\"))\n\t{\n\t\tdepth=1;\n\t\twhile(lib && !success)\n\t\t{\n\t\t\tlib = COM_ParseCString(lib, key, sizeof(key), NULL);\n\t\t\tif (!strcmp(key, \"}\"))\n\t\t\t{\n\t\t\t\tif (!--depth)\n\t\t\t\t\tbreak;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tlib = COM_ParseCString(lib, value, sizeof(value), NULL);\n\n\t\t\tif (!strcmp(value, \"{\"))\n\t\t\t\tdepth++;\n\t\t\telse if (depth == 1 && *key)\n\t\t\t{\t//older format...\n\t\t\t\tstrtoul(key, &end, 10);\n\t\t\t\tif (!*end)\n\t\t\t\t{\n\t\t\t\t\t//okay, its strictly base10\n\t\t\t\t\tif (Sys_SteamLibraryHasFile(basepath,sizeof(basepath), value, steamdir,fname))\n\t\t\t\t\t\tsuccess = true, callback(ctx, basepath);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (depth == 2 && !strcmp(key, \"path\"))\n\t\t\t{\t//newer format...\n\t\t\t\tif (Sys_SteamLibraryHasFile(basepath,sizeof(basepath), value, steamdir,fname))\n\t\t\t\t\tsuccess = true, callback(ctx, basepath);\n\t\t\t}\n\t\t}\n\t}\n\tFS_FreeFile(libraryfile);\n\treturn success;\n}\nstatic qboolean Sys_SteamDirsWithFile(char *steamdir, char *fname, void(*callback)(void*ctx,const char*basepath),void*ctx)\t//returns the base system path\n{\n\t/*\n\tFind where Valve's Steam distribution platform is installed.\n\tThen take a look at that location for the relevent installed app.\n\t*/\n\tchar libdirs[MAX_OSPATH];\n\tchar *userhome = getenv(\"HOME\");\n\tif (userhome && *userhome)\n\t{\n\t\tQ_snprintfz(libdirs,sizeof(libdirs), \"%s/.steam/steam/steamapps/libraryfolders.vdf\", userhome);\n\t\tif (Sys_SteamParseLibraries(callback,ctx, FS_MallocFile(libdirs, FS_SYSTEM, NULL), steamdir, fname))\n\t\t\treturn true;\n\n\t\tQ_snprintfz(libdirs,sizeof(libdirs), \"%s/.local/share/Steam/SteamApps/libraryfolders.vdf\", userhome);\n\t\tif (Sys_SteamParseLibraries(callback,ctx, FS_MallocFile(libdirs, FS_SYSTEM, NULL), steamdir, fname))\n\t\t\treturn true;\n\t}\n\treturn false;\n}\n#else\nstatic qboolean Sys_SteamDirsWithFile(char *steamdir, char *fname, void(*callback)(void*ctx,const char*basepath),void*ctx)\t//returns the base system path\n{\n\treturn false;\n}\n#endif\n\n#if defined(HAVE_CLIENT) //this is *really* unfortunate, but doing this crashes the browser\nstatic qboolean Sys_DoDirectoryPrompt(char *basepath, size_t basepathsize, const char *poshname, const char *savedname)\n{\n\treturn false;\n}\n//#define Sys_DoDirectoryPrompt(bp,bps,game,savename) false\n#endif\n\n#if (defined(__linux__) || defined(__unix__) || defined(__apple__)) && !defined(ANDROID)\nstatic void Sys_XDGHasDirectory(const char *gamename, void (*callback) (void *ctx, const char *basepath), void *ctx)\n{\t//returns true if the gamedir can be found, and fills in the basepath.\n\tstruct stat sb;\n\tconst char *dirs = getenv(\"XDG_DATA_DIRS\");\n\tchar dir[MAX_OSPATH];\n\tchar basedir[MAX_OSPATH];\n\tint m;\n\tstruct\n\t{\n\t\tconst char *pregame;\n\t\tconst char *postgame;\n\t} fixups[] = {\n\t\t{\"games/\",\t\"\"},\t\t//debian likes extra subdirs.\n\t\t{\"\",\t\t\"\"},\n\t\t{\"games/\",\t\"-demo\"},\t//and demos sometimes get extra postfixes. its annoying.\n\t\t{\"\",\t\t\"-demo\"},\n\t};\n\tif (!dirs||!*dirs)\n\t\tdirs = \"/usr/local/share:/usr/share\";\n\n\twhile (dirs && *dirs)\n\t{\n\t\tdirs = COM_ParseStringSetSep(dirs, ':', dir, sizeof(dir));\n\n\t\tfor (m = 0; m < countof(fixups); m++)\n\t\t{\n\t\t\tif (Q_snprintfz(basedir,sizeof(basedir), \"%s/%s%s%s/\", dir, fixups[m].pregame, gamename, fixups[m].postgame))\n\t\t\t\tcontinue;\t//overflow.\n\t\t\tif (stat(basedir, &sb) == 0)\n\t\t\t{\n\t\t\t\tif (S_ISDIR(sb.st_mode))\n\t\t\t\t\tcallback(ctx, basedir);\n\t\t\t}\n\t\t}\n\t}\n}\n#else\nstatic void Sys_XDGHasDirectory(const char *gamename, void (*callback) (void *ctx, const char *basepath), void *ctx)\n{\n}\n#endif\n\nstatic void Sys_FindBaseDirs(const char *poshname, const char *gamename, void (*callback) (void *ctx, const char *basepath), void *ctx)\n{\n\tif (!*gamename)\n\t{\n#ifdef GAME_SHORTNAME\n\t\tgamename = GAME_SHORTNAME;\t//just a paranoia fallback, shouldn't be needed, but we ARE a whatever-this-is engine.\n#else\n\t\tgamename = \"quake\";\t//just a paranoia fallback, shouldn't be needed, but we ARE a quake engine.\n#endif\n\t}\n\n\tif (!strcmp(gamename, \"quake_rerel\"))\n\t{\n\t\tSys_SteamDirsWithFile(\"Quake/rerelease\", \"id1/pak0.pak\", callback,ctx);\n\t}\n\telse if (!strcmp(gamename, \"quake\") || !strcmp(gamename, \"afterquake\") || !strcmp(gamename, \"netquake\") || !strcmp(gamename, \"spasm\") || !strcmp(gamename, \"fitz\") || !strcmp(gamename, \"tenebrae\"))\n\t{\n\t\tif (!Sys_SteamDirsWithFile(\"Quake\", \"Id1/PAK0.PAK\", callback,ctx))\t//dos legacies need to die.\n\t\tif (!Sys_SteamDirsWithFile(\"Quake\", \"id1/PAK0.PAK\", callback,ctx))\t//dos legacies need to die.\n\t\t\tSys_SteamDirsWithFile(\"Quake\", \"id1/pak0.pak\", callback,ctx);\t//people may have tried to sanitise it already.\n\n\t\tif (strcmp(gamename, \"quake\"))\n\t\t{\n\t\t\tSys_XDGHasDirectory(gamename, callback,ctx);\n\t\t\tgamename = \"quake\";\t//clean up aliases.\n\t\t}\n\t}\n\telse if (!strcmp(gamename, \"quake2\"))\n\t{\n\t\tSys_SteamDirsWithFile(\"quake 2\", \"baseq2/pak0.pak\", callback,ctx);\n\t}\n\telse if (!strcmp(gamename, \"hexen2\") || !strcmp(gamename, \"h2mp\") || !strcmp(gamename, \"portals\"))\n\t{\n\t\tSys_SteamDirsWithFile(\"hexen 2\", \"data/pak0.pak\", callback,ctx);\n\n\t\tif (strcmp(gamename, \"hexen2\"))\n\t\t{\n\t\t\tSys_XDGHasDirectory(gamename, callback,ctx);\n\t\t\tgamename = \"hexen2\";\t//clean up aliases.\n\t\t}\n\t}\n\n\tSys_XDGHasDirectory(gamename, callback,ctx);\n}\n#endif\n\nstruct sys_findgamedata_ctx_s\n{\n\tchar *basepath;\n\tint basepathlen;\n\tqboolean found;\n};\nstatic void Sys_FoundGameData (void *vctx, const char *basepath)\n{\n\tstruct sys_findgamedata_ctx_s *ctx = vctx;\n\tif (ctx->found)\t//use the first, not last.\n\t\treturn;\n\tif (strlen(basepath) >= ctx->basepathlen)\n\t\treturn; //too long... just bail.\n\tctx->found = true;\n\tQ_strncpyz(ctx->basepath, basepath, ctx->basepathlen);\n}\nstatic qboolean Sys_FindGameData(const char *poshname, const char *gamename, char *basepath, int basepathlen, qboolean allowprompts)\n{\n\tstruct sys_findgamedata_ctx_s ctx = {basepath, basepathlen, false};\n\tSys_FindBaseDirs(poshname, gamename, Sys_FoundGameData,&ctx);\n\n#if defined(HAVE_CLIENT) //this is *really* unfortunate, but doing this crashes the browser\n\tif (!ctx.found && allowprompts && poshname && *gamename && !COM_CheckParm(\"-manifest\"))\n\t{\n\t\tif (Sys_DoDirectoryPrompt(basepath,basepathlen, poshname, gamename))\n\t\t\treturn true;\n\t}\n#endif\n\treturn ctx.found;\n}\nstatic qboolean Sys_SteamHasFile(char *steambasedir,size_t steambasedirsize, char *steamdir, char *fname)\n{\n\tstruct sys_findgamedata_ctx_s ctx = {steambasedir, steambasedirsize, false};\n\treturn Sys_SteamDirsWithFile(steamdir, fname, Sys_FoundGameData,&ctx);\n}\n\nstatic void FS_FreePaths(void)\n{\n\tsearchpath_t *next;\n\tFS_FlushFSHashReally(true);\n\n\t//\n\t// free up any current game dir info\n\t//\n\twhile (com_searchpaths)\n\t{\n\t\tcom_searchpaths->handle->ClosePath(com_searchpaths->handle);\n\t\tnext = com_searchpaths->next;\n\t\tZ_Free (com_searchpaths);\n\t\tcom_searchpaths = next;\n\t}\n\n\tcom_fschanged = true;\n\n\n\tif (filesystemhash.numbuckets)\n\t{\n\t\tBZ_Free(filesystemhash.bucket);\n\t\tfilesystemhash.bucket = NULL;\n\t\tfilesystemhash.numbuckets = 0;\n\t}\n\n\tFS_Manifest_Free(fs_manifest);\n\tfs_manifest = NULL;\n}\nvoid FS_Shutdown(void)\n{\n\tif (!fs_thread_mutex)\n\t\treturn;\n\n\tMods_FlushModList();\n\n#ifdef PACKAGEMANAGER\n\tPM_ManifestChanged(NULL);\n#endif\n\tFS_FreePaths();\n\tSys_DestroyMutex(fs_thread_mutex);\n\tfs_thread_mutex = NULL;\n\n\tCvar_SetEngineDefault(&fs_gamename, NULL);\n\tCvar_SetEngineDefault(&com_protocolname, NULL);\n}\n\n//returns false if the directory is not suitable.\n//returns true if it contains a known package. if we don't actually know of any packages that it should have, we just have to assume that its okay.\nqboolean FS_DirHasAPackage(char *basedir, ftemanifest_t *man)\n{\n\tqboolean defaultret = true;\n\tint j;\n\tvfsfile_t *f;\n\n\tf = VFSOS_Open(va(\"%sdefault.fmf\", basedir), \"rb\");\n\tif (f)\n\t{\n\t\tVFS_CLOSE(f);\n\t\treturn true;\n\t}\n\n\tfor (j = 0; j < sizeof(fs_manifest->package) / sizeof(fs_manifest->package[0]); j++)\n\t{\n\t\tif (!man->package[j].path)\n\t\t\tcontinue;\n\t\tdefaultret = false;\n\n\t\tf = VFSOS_Open(va(\"%s%s\", basedir, man->package[j].path), \"rb\");\n\t\tif (f)\n\t\t{\n\t\t\tVFS_CLOSE(f);\n\t\t\treturn true;\n\t\t}\n\t}\n\treturn defaultret;\n}\n\n//false stops the search (and returns that value to FS_DirHasGame)\nstatic int QDECL FS_DirDoesHaveGame(const char *fname, qofs_t fsize, time_t modtime, void *ctx, searchpathfuncs_t *subdir)\n{\n\treturn false;\n}\n\n//just check each possible file, see if one is there.\nstatic qboolean FS_DirHasGame(const char *basedir, int gameidx)\n{\n\tint j;\n#if defined(_WIN32) || defined(NOSTDIO) || !defined(_POSIX_C_SOURCE)\n#else\n\tchar realpath[MAX_OSPATH];\n#endif\n\tchar cached[MAX_OSPATH];\n\n\t//none listed, just assume its correct.\n\tif (!gamemode_info[gameidx].auniquefile[0])\n\t\treturn true;\n\n\tfor (j = 0; j < 4; j++)\n\t{\n\t\tif (!gamemode_info[gameidx].auniquefile[j])\n\t\t\tcontinue;\t//no more\n#if defined(_WIN32) || defined(NOSTDIO) || !defined(_POSIX_C_SOURCE)\t//systems that lack a working 'access' function.\n\t\tif (!Sys_EnumerateFiles(basedir, gamemode_info[gameidx].auniquefile[j], FS_DirDoesHaveGame, NULL, NULL))\n\t\t\treturn true;\t//search was cancelled by the callback, so it actually got called.\n#else\n\t\tif (FS_FixupFileCase(realpath, sizeof(realpath), basedir, gamemode_info[gameidx].auniquefile[j], false) && access(realpath, R_OK) == 0)\n\t\t\treturn true;\t//something readable.\n#endif\n\n\t\t//gah, just try a wildcard for hashes.\n\t\tif (FS_GenCachedPakName(gamemode_info[gameidx].auniquefile[j], NULL, cached,sizeof(cached)))\n\t\t{\n\t\t\tQ_strncatz(cached, \".*\", sizeof(cached));\n\t\t\tif (!Sys_EnumerateFiles(basedir, cached, FS_DirDoesHaveGame, NULL, NULL))\n\t\t\t\treturn true;\t//search was cancelled by the callback, so it actually got called.\n\t\t}\n\t}\n\treturn false;\n}\n\n//check em all\nstatic int FS_IdentifyDefaultGameFromDir(const char *basedir)\n{\n\tint i;\n\tfor (i = 0; gamemode_info[i].argname; i++)\n\t{\n\t\tif (FS_DirHasGame(basedir, i))\n\t\t\treturn i;\n\t}\n\treturn -1;\n}\n\n//attempt to work out which game we're meant to be trying to run based upon a few things\n//1: fs_changegame console command override. fixme: needs to cope with manifests too.\n//2: -quake3 (etc) argument implies that the user wants to run quake3.\n//3: otherwise if we are ftequake3.exe then we try to run quake3.\n//4: identify characteristic files within the working directory (like id1/pak0.pak implies we're running quake)\n//5: check where the exe actually is instead of simply where we're being run from.\n//6: try the homedir, just in case.\n//7: fallback to prompting. just returns -1 here.\n//if autobasedir is not set, block gamedir changes/prompts.\nstatic int FS_IdentifyDefaultGame(char *newbase, int sizeof_newbase, qboolean fixedbase)\n{\n\tint i;\n\tint gamenum = -1;\n\n\t//use the game based on an exe name over the filesystem one (could easily have multiple fs path matches).\n\tif (gamenum == -1)\n\t{\n\t\tchar *ev, *v0 = COM_SkipPath(com_argv[0]);\n\t\tfor (i = 0; gamemode_info[i].argname; i++)\n\t\t{\n\t\t\tif (!gamemode_info[i].exename)\n\t\t\t\tcontinue;\n\t\t\tev = strstr(v0, gamemode_info[i].exename);\n\t\t\tif (ev && (!strchr(ev, '\\\\') && !strchr(ev, '/')))\n\t\t\t\tgamenum = i;\n\t\t}\n\t}\n\n\t//identify the game from a telling file in the working directory\n\tif (gamenum == -1)\n\t\tgamenum = FS_IdentifyDefaultGameFromDir(newbase);\n\t//identify the game from a telling file relative to the exe's directory. for when shortcuts don't set the working dir sensibly.\n\tif (gamenum == -1 && host_parms.binarydir && *host_parms.binarydir && !fixedbase)\n\t{\n\t\tgamenum = FS_IdentifyDefaultGameFromDir(host_parms.binarydir);\n\t\tif (gamenum != -1)\n\t\t\tQ_strncpyz(newbase, host_parms.binarydir, sizeof_newbase);\n\t}\n\tif (gamenum == -1 && *com_homepath && com_homepathusable && !fixedbase)\n\t{\n\t\tgamenum = FS_IdentifyDefaultGameFromDir(com_homepath);\n\t\tif (gamenum != -1)\n\t\t\tQ_strncpyz(newbase, com_homepath, sizeof_newbase);\n\t}\n\treturn gamenum;\n}\n\nstatic ftemanifest_t *FS_GenerateLegacyManifest(int game, const char *basedir)\n{\n\tftemanifest_t *man;\n\tconst char *cexec;\n\n\tif (basedir)\n\t{\t//see if the gamedir we're aiming for already has a default.fmf file...\n\t\tman = NULL;\n\t\tif (!man)\n\t\t\tman = FS_Manifest_ReadSystem(va(\"%s%s.fmf\", basedir, gamemode_info[game].exename), basedir);\n#ifdef BRANDING_NAME\n\t\tif (!man)\n\t\t\tman = FS_Manifest_ReadSystem(va(\"%s\"STRINGIFY(BRANDING_NAME)\".fmf\", basedir), basedir);\n#endif\n\t\tif (!man)\n\t\t\tman = FS_Manifest_ReadSystem(va(\"%sdefault.fmf\", basedir), basedir);\n\n\t\tif (man)\n\t\t{\n\t\t\tif (!Q_strcasecmp(man->installation, gamemode_info[game].argname+1))\n\t\t\t\treturn man;\t//this seems to match what we were expecting. use its data instead of making one up.\n\t\t\telse\n\t\t\t\tFS_Manifest_Free(man);\n\t\t}\n\t}\n\n\tif (gamemode_info[game].manifestfile)\n\t\tman = FS_Manifest_ReadMem(NULL, basedir, gamemode_info[game].manifestfile);\n\telse\n\t{\n\t\tman = FS_Manifest_Create(NULL, basedir);\n\n\t\tCmd_TokenizeString(va(\"game \\\"%s\\\"\", gamemode_info[game].argname+1), false, false);\n\t\tFS_Manifest_ParseTokens(man);\n\n\t\tfor (cexec = gamemode_info[game].customexec; cexec && cexec[0] == '/' && cexec[1] == '/'; )\n\t\t{\n\t\t\tchar line[256];\n\t\t\tchar *e = strchr(cexec, '\\n');\n\t\t\tif (!e)\n\t\t\t\tbreak;\n\t\t\tQ_strncpyz(line, cexec+2, min(e-(cexec+2)+1, sizeof(line)));\n\t\t\tcexec = e+1;\n\t\t\tCmd_TokenizeString(line, false, false);\n\t\t\tFS_Manifest_ParseTokens(man);\n\t\t}\n\n\t\tFS_Manifest_SetDefaultSettings(man, &gamemode_info[game]);\n\t}\n\tman->security = MANIFEST_SECURITY_INSTALLER;\n\treturn man;\n}\n\nstatic void FS_AppendManifestGameArguments(ftemanifest_t *man)\n{\n\tint i;\n\n\tif (!man)\n\t\treturn;\n\n\ti = COM_CheckParm (\"-basegame\");\n\tif (i)\n\t{\n\t\tif (man->filename)\n\t\t\tZ_Free(man->filename);\n\t\tman->filename = NULL;\n\t\tdo\n\t\t{\n\t\t\tCmd_TokenizeString(va(\"basegame \\\"%s\\\"\", com_argv[i+1]), false, false);\n\t\t\tFS_Manifest_ParseTokens(man);\n\n\t\t\ti = COM_CheckNextParm (\"-basegame\", i);\n\t\t}\n\t\twhile (i && i < com_argc-1);\n\t}\n\n\ti = COM_CheckParm (\"-game\");\n\tif (i)\n\t{\n\t\tif (man->filename)\n\t\t\tZ_Free(man->filename);\n\t\tman->filename = NULL;\n\t\tdo\n\t\t{\n\t\t\tCmd_TokenizeString(va(\"gamedir \\\"%s\\\"\", com_argv[i+1]), false, false);\n\t\t\tFS_Manifest_ParseTokens(man);\n\n\t\t\ti = COM_CheckNextParm (\"-game\", i);\n\t\t}\n\t\twhile (i && i < com_argc-1);\n\t}\n\n\ti = COM_CheckParm (\"+gamedir\");\n\tif (i)\n\t{\n\t\tif (man->filename)\n\t\t\tZ_Free(man->filename);\n\t\tman->filename = NULL;\n\t\tdo\n\t\t{\n\t\t\tCmd_TokenizeString(va(\"gamedir \\\"%s\\\"\", com_argv[i+1]), false, false);\n\t\t\tFS_Manifest_ParseTokens(man);\n\n\t\t\ti = COM_CheckNextParm (\"+gamedir\", i);\n\t\t}\n\t\twhile (i && i < com_argc-1);\n\t}\n}\n\n#ifdef MANIFESTDOWNLOADS\nstatic struct dl_download *curpackagedownload;\nqboolean FS_DownloadingPackage(void)\n{\n\tif (PM_IsApplying() & 3)\n\t\treturn true;\n\treturn !fs_manifest || !!curpackagedownload;\n}\n\nstatic void FS_ManifestUpdated(struct dl_download *dl)\n{\n\tftemanifest_t *man = fs_manifest;\n\n\tcurpackagedownload = NULL;\n\twaitingformanifest--;\n\n\tif (dl->file)\n\t{\n\t\tif (dl->user_ctx == man)\n\t\t{\n\t\t\tsize_t len = VFS_GETLEN(dl->file), len2;\n\t\t\tchar *fdata = BZ_Malloc(len+1), *fdata2 = NULL;\n\t\t\tif (fdata)\n\t\t\t{\n\t\t\t\tVFS_READ(dl->file, fdata, len);\n\t\t\t\tfdata[len] = 0;\n\t\t\t\tman = FS_Manifest_ReadMem(fs_manifest->filename, fs_manifest->basedir, fdata);\n\t\t\t\tif (man)\n\t\t\t\t{\n\t\t\t\t\t//the updateurl MUST match the current one in order for the local version of the manifest to be saved (to avoid extra updates, and so it appears in the menu_mods)\n\t\t\t\t\t//this is a paranoia measure to avoid too much damage from buggy/malicious proxies that return empty pages or whatever.\n\t\t\t\t\tif (!man->updateurl || !fs_manifest->updateurl || strcmp(man->updateurl, fs_manifest->updateurl))\n\t\t\t\t\t{\n\t\t\t\t\t\tCon_Printf(\"Refusing to update manifest - updateurl changed from \\\"%s\\\" to \\\"%s\\\"\\n\", fs_manifest->updateurl, man->updateurl);\n\t\t\t\t\t\tFS_Manifest_Free(man);\n\t\t\t\t\t}\n\t\t\t\t\telse if (!man->basedir || !fs_manifest->basedir || strcmp(man->basedir, fs_manifest->basedir))\t//basedir must match too... ie: not be overridden.\n\t\t\t\t\t{\n\t\t\t\t\t\tCon_Printf(\"Refusing to update manifest - basedir changed from \\\"%s\\\" to \\\"%s\\\"\\n\", fs_manifest->basedir, man->basedir);\n\t\t\t\t\t\tFS_Manifest_Free(man);\n\t\t\t\t\t}\n\t\t\t\t\telse if (!man->installation || !fs_manifest->installation || strcmp(man->installation, fs_manifest->installation))\n\t\t\t\t\t{\n\t\t\t\t\t\tCon_Printf(\"Refusing to update manifest - game changed from \\\"%s\\\" to \\\"%s\\\"\\n\", fs_manifest->installation, man->installation);\n\t\t\t\t\t\tFS_Manifest_Free(man);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tman->blockupdate = true;\t//don't download it multiple times. that's just crazy.\n\t\t\t\t\t\tif (man->filename)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tvfsfile_t *f2 = FS_OpenVFS(fs_manifest->filename, \"rb\", FS_SYSTEM);\n\t\t\t\t\t\t\tif (f2)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tlen2 = VFS_GETLEN(f2);\n\t\t\t\t\t\t\t\tif (len != len2)\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tfdata2 = NULL;\n\t\t\t\t\t\t\t\t\tlen2 = 0;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tfdata2 = BZ_Malloc(len2);\n\t\t\t\t\t\t\t\t\tVFS_READ(f2, fdata2, len2);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tVFS_CLOSE(f2);\n\t\t\t\t\t\t\t\tif (len == len2 && !memcmp(fdata, fdata2, len))\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t//files match, no need to use this new manifest at all.\n\t\t\t\t\t\t\t\t\tFS_Manifest_Free(man);\n\t\t\t\t\t\t\t\t\tman = NULL;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tBZ_Free(fdata2);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (man)\n\t\t\t\t\t\t\t\tFS_WriteFile(man->filename, fdata, len, FS_SYSTEM);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (man)\n\t\t\t\t\t\t\tFS_ChangeGame(man, true, false);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tBZ_Free(fdata);\n\t\t\t}\n\t\t}\n\n\t\tVFS_CLOSE(dl->file);\n\t\tdl->file = NULL;\n\t}\n\tif (!waitingformanifest)\n\t\tPM_AddManifestPackages(man, true);\n}\nstatic void FS_BeginNextPackageDownload(ftemanifest_t *man)\n{\n\tif (curpackagedownload || !man || com_installer)\n\t\treturn;\n\n\tif (man == fs_manifest && man->updateurl && !man->blockupdate)\n\t{\n\t\tvfsfile_t *f = man->filename?FS_OpenVFS(man->filename, \"ab\", FS_SYSTEM):NULL;\t//this is JUST to make sure its writable. don't bother updating it if it isn't.\n\t\tman->blockupdate = true;\n\t\tif (f)\n\t\t{\n\t\t\tVFS_CLOSE(f);\n\n\t\t\tCon_Printf(\"Updating manifest from %s\\n\", man->updateurl);\n\t\t\twaitingformanifest++;\n\t\t\tcurpackagedownload = HTTP_CL_Get(man->updateurl, NULL, FS_ManifestUpdated);\n\t\t\tif (curpackagedownload)\n\t\t\t{\n\t\t\t\tcurpackagedownload->user_ctx = man;\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n}\nvoid FS_BeginManifestUpdates(void)\n{\n\tftemanifest_t *man = fs_manifest;\n\tif (curpackagedownload || !man)\n\t\treturn;\n\n\tif (!curpackagedownload)\n\t\tFS_BeginNextPackageDownload(man);\n}\n#else\nqboolean FS_DownloadingPackage(void)\n{\n\treturn false;\n}\nvoid FS_BeginManifestUpdates(void)\n{\n}\n#endif\n\nstatic qboolean FS_FoundManifest(void *usr, ftemanifest_t *man, enum modsourcetype_e sourcetype)\n{\n\tif (!*(ftemanifest_t**)usr)\n\t\t*(ftemanifest_t**)usr = man;\n\telse\n\t\tFS_Manifest_Free(man);\n\treturn true;\n}\n\n//reads the default manifest based upon the basedir, the commandline arguments, the name of the exe, etc.\n//may still fail if no game was identified.\n//if fixedbasedir is true, stuff like -quake won't override/change the active basedir (ie: -basedir or gamedir switching without breaking gamedir)\nstatic ftemanifest_t *FS_ReadDefaultManifest(char *newbasedir, size_t newbasedirsize, qboolean fixedbasedir)\n{\n\tint i;\n\tint game = -1;\n\tftemanifest_t *man = NULL;\n\n\tvfsfile_t *f;\n\n\t//commandline generally takes precedence\n\tif (!man && game == -1)\n\t{\n\t\tint i;\n\t\tfor (i = 0; gamemode_info[i].argname; i++)\n\t\t{\n\t\t\tif (COM_CheckParm(gamemode_info[i].argname))\n\t\t\t{\n\t\t\t\tgame = i;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\t//hopefully this will be used for TCs.\n\tif (!man && game == -1)\n\t{\n\t\tchar exename[MAX_QPATH];\n\t\tCOM_StripAllExtensions(COM_SkipPath(com_argv[0]), exename, sizeof(exename));\n\t\t//take away any amd64/x86/x86_64 postfix, so that people can have multiple cpu arch binaries sharing a single fmf\n\t\tif (strlen(exename) > strlen(ARCH_CPU_POSTFIX) && !strcmp(exename+strlen(exename)-strlen(ARCH_CPU_POSTFIX), ARCH_CPU_POSTFIX))\n\t\t\texename[strlen(exename)-strlen(ARCH_CPU_POSTFIX)] = 0;\n\t\t//and then the trailing _ (before said postfix)\n\t\tif (exename[strlen(exename)] == '_')\n\t\t\texename[strlen(exename)] = 0;\n\t\t//and hopefully we now have something consistent that we can try to use.\n\n\t\tif (!man)\n\t\t\tman = FS_Manifest_ReadSystem(va(\"%s%s.fmf\", newbasedir, exename), newbasedir);\n#ifdef BRANDING_NAME\n\t\tif (!man)\n\t\t\tman = FS_Manifest_ReadSystem(va(\"%s\"STRINGIFY(BRANDING_NAME)\".fmf\", newbasedir), newbasedir);\n#endif\n\t\tif (!man)\n\t\t\tman = FS_Manifest_ReadSystem(va(\"%sdefault.fmf\", newbasedir), newbasedir);\n\t\tif (man)\n\t\t\tman->security = MANIFEST_SECURITY_DEFAULT;\n\t}\n\n#if defined(ENGINE_HAS_ZIP) && defined(PACKAGE_PK3)\n\tif (!man && game == -1)\n\t{\n\t\tsearchpathfuncs_t *pak;\n\t\tvfsfile_t *vfs;\n\t\tvfs = VFSOS_Open(com_argv[0], \"rb\");\n\t\tpak = FSZIP_LoadArchive(vfs, NULL, com_argv[0], com_argv[0], \"\");\n\t\tif (pak)\n\t\t{\n\t\t\tflocation_t loc;\n\t\t\tif (pak->FindFile(pak, &loc, \"default.fmf\", NULL))\n\t\t\t{\n\t\t\t\tf = pak->OpenVFS(pak, &loc, \"rb\");\n\t\t\t\tif (f)\n\t\t\t\t{\n\t\t\t\t\tsize_t len = VFS_GETLEN(f);\n\t\t\t\t\tchar *fdata = BZ_Malloc(len+1);\n\t\t\t\t\tif (fdata)\n\t\t\t\t\t{\n\t\t\t\t\t\tVFS_READ(f, fdata, len);\n\t\t\t\t\t\tfdata[len] = 0;\n\t\t\t\t\t\tman = FS_Manifest_ReadMem(NULL, NULL, fdata);\n\t\t\t\t\t\tif (man)\n\t\t\t\t\t\t\tman->security = MANIFEST_SECURITY_DEFAULT;\n\t\t\t\t\t\tBZ_Free(fdata);\n\t\t\t\t\t}\n\t\t\t\t\tVFS_CLOSE(f);\n\t\t\t\t}\n\t\t\t}\n\t\t\tpak->ClosePath(pak);\n\t\t}\n\t}\n#endif\n\n\n\t//-basepack is primarily an android feature\n\ti = COM_CheckParm (\"-basepack\");\n\twhile (!man && game == -1 && i && i < com_argc-1)\n\t{\n\t\tconst char *pakname = com_argv[i+1];\n\t\tsearchpathfuncs_t *pak;\n\t\tvfsfile_t *vfs = VFSOS_Open(pakname, \"rb\");\n\t\tpak = FS_OpenPackByExtension(vfs, NULL, pakname, pakname, \"\");\n\t\tif (pak)\n\t\t{\n\t\t\tflocation_t loc;\n\t\t\tif (pak->FindFile(pak, &loc, \"default.fmf\", NULL))\n\t\t\t{\n\t\t\t\tf = pak->OpenVFS(pak, &loc, \"rb\");\n\t\t\t\tif (f)\n\t\t\t\t{\n\t\t\t\t\tsize_t len = VFS_GETLEN(f);\n\t\t\t\t\tchar *fdata = BZ_Malloc(len+1);\n\t\t\t\t\tif (fdata)\n\t\t\t\t\t{\n\t\t\t\t\t\tVFS_READ(f, fdata, len);\n\t\t\t\t\t\tfdata[len] = 0;\n\t\t\t\t\t\tman = FS_Manifest_ReadMem(NULL, NULL, fdata);\n\t\t\t\t\t\tif (man)\n\t\t\t\t\t\t\tman->security = MANIFEST_SECURITY_DEFAULT;\n\t\t\t\t\t\tBZ_Free(fdata);\n\t\t\t\t\t}\n\t\t\t\t\tVFS_CLOSE(f);\n\t\t\t\t}\n\t\t\t}\n\t\t\tpak->ClosePath(pak);\n\t\t}\n\t\ti = COM_CheckNextParm (\"-basepack\", i);\n\t}\n\n\n\tif (!man && game == -1 && host_parms.manifest)\n\t{\n\t\tman = FS_Manifest_ReadMem(NULL, newbasedir, host_parms.manifest);\n\t\tif (man)\n\t\t\tman->security = MANIFEST_SECURITY_INSTALLER;\n\t}\n\n\tif (!man)\n\t{\n\t\tif (game == -1)\n\t\t\tgame = FS_IdentifyDefaultGame(newbasedir, newbasedirsize, fixedbasedir);\n\t\tif (game != -1)\n\t\t\tman = FS_GenerateLegacyManifest(game, fixedbasedir?newbasedir:NULL);\n\t}\n\n\tFS_AppendManifestGameArguments(man);\n\treturn man;\n}\n\nqboolean FS_FixPath(char *path, size_t pathsize)\n{\n\tsize_t len = strlen(path);\n\tif (len)\n\t{\n\t\tif (path[len-1] == '/')\n\t\t\treturn true;\n#ifdef _WIN32\n\t\tif (path[len-1] == '\\\\')\n\t\t\treturn true;\n#endif\n\t\tif (len >= pathsize-1)\n\t\t\treturn false;\n\t\tpath[len] = '/';\n\t\tpath[len+1] = 0;\n\t}\n\treturn true;\n}\n\n//this is potentially unsafe. needs lots of testing.\nqboolean FS_ChangeGame(ftemanifest_t *man, qboolean allowreloadconfigs, qboolean allowbasedirchange)\n{\n\tint i, j;\n\tchar realpath[MAX_OSPATH-1];\n\tchar newbasedir[MAX_OSPATH];\n#ifdef PACKAGEMANAGER\n\tchar *olddownloadsurl;\n#endif\n\tqboolean fixedbasedir;\n\tqboolean reloadconfigs = false;\n\tqboolean builtingame = false;\n\tqboolean basedirchanged = false;\n\tflocation_t loc;\n#ifdef MANIFESTDOWNLOADS\n\tqboolean allowdownload = man!=fs_manifest && allowreloadconfigs;\t//or at least ask the user.\n#endif\n\n#ifdef HAVE_CLIENT\n\tqboolean allowvidrestart = true;\n\tchar *vidfile[] = {\"gfx.wad\", \"gfx/conback.lmp\"/*q1*/,\"gfx/menu/conback.lmp\"/*h2*/,\"pics/conback.pcx\"/*q2*/,\t//misc stuff\n\t\t\"gfx/palette.lmp\", \"pics/colormap.pcx\", \"gfx/conchars.png\"};\t\t//palettes\n\tsearchpathfuncs_t *vidpath[countof(vidfile)];\n\tchar *menufile[] = {\"menu.dat\"/*mods*/, \"gfx/ttl_main.lmp\"/*q1*/, \"pics/m_main_quit.pcx\"/*q2*/, \"gfx/menu/title0.lmp\"/*h2*/};\n\tsearchpathfuncs_t *menupath[countof(menufile)];\n#endif\n\n\t//if any of these files change location, the configs will be re-execed.\n\t//note that we reuse path handles if they're still valid, so we can just check the pointer to see if it got unloaded/replaced.\n\tchar *conffile[] = {\"quake.rc\", \"hexen.rc\", \"default.cfg\", \"server.cfg\"};\n\tsearchpathfuncs_t *confpath[countof(conffile)];\n\n#ifdef HAVE_CLIENT\n\tfor (i = 0; i < countof(vidfile); i++)\n\t{\n\t\tif (allowvidrestart)\n\t\t{\n\t\t\tFS_FLocateFile(vidfile[i], FSLF_IFFOUND, &loc);\t//q1\n\t\t\tvidpath[i] = loc.search?loc.search->handle:NULL;\n\t\t}\n\t\telse\n\t\t\tvidpath[i] = NULL;\n\t}\n\tfor (i = 0; i < countof(menufile); i++)\n\t{\n\t\tif (allowreloadconfigs)\n\t\t{\n\t\t\tFS_FLocateFile(menufile[i], FSLF_IFFOUND|FSLF_SECUREONLY, &loc);\n\t\t\tmenupath[i] = loc.search?loc.search->handle:NULL;\n\t\t}\n\t\telse\n\t\t\tmenupath[i] = NULL;\n\t}\n#endif\n\n\tif (allowreloadconfigs && fs_noreexec.ival)\n\t\tallowreloadconfigs = false;\n\tfor (i = 0; i < countof(conffile); i++)\n\t{\n\t\tif (allowreloadconfigs)\n\t\t{\n\t\t\tFS_FLocateFile(conffile[i], FSLF_IFFOUND|FSLF_IGNOREPURE, &loc);\t//q1\n\t\t\tconfpath[i] = loc.search?loc.search->handle:NULL;\n\t\t}\n\t\telse\n\t\t\tconfpath[i] = NULL;\n\t}\n\n#if defined(FTE_TARGET_WEB) || defined(ANDROID) || defined(WINRT)\n\t//these targets are considered to be sandboxed already, and have their own app-based base directory which they will always use.\n\tQ_strncpyz (newbasedir, host_parms.basedir, sizeof(newbasedir));\n\tfixedbasedir = true;\n#else\n\ti = COM_CheckParm (\"-basedir\");\n\tfixedbasedir = i && i < com_argc-1;\n\tQ_strncpyz (newbasedir, fixedbasedir?com_argv[i+1]:host_parms.basedir, sizeof(newbasedir));\n#endif\n\n\t//make sure it has a trailing slash, or is empty. woo.\n\tFS_CleanDir(newbasedir, sizeof(newbasedir));\n\n\tif (!allowreloadconfigs || !allowbasedirchange || (man && fs_manifest && !Q_strcasecmp(man->installation, fs_manifest->installation)))\n\t{\n\t\tfixedbasedir = true;\n\t\tQ_strncpyz (newbasedir, com_gamepath, sizeof(newbasedir));\n\t}\n\n\tif (!man)\n\t{\n\t\tint found = 0;\n\t\t//if we're already running a game, don't autodetect.\n\t\tif (fs_manifest)\n\t\t\treturn false;\n\n\t\tman = FS_ReadDefaultManifest(newbasedir, sizeof(newbasedir), fixedbasedir);\n\n\t\tif (!man)\n\t\t{\n\t\t\tfound = FS_EnumerateKnownGames(FS_FoundManifest, &man, fixedbasedir);\n\t\t\tif (found != 1)\n\t\t\t{\n\t\t\t\t//we found more than 1 (or none)\n\t\t\t\t//if we're a client, display a menu to pick between them (or display an error)\n\t\t\t\t//servers can just use the first they find, they'd effectively just crash otherwise, but still give a warning.\n\t\t\t\tif (!isDedicated)\n\t\t\t\t{\n\t\t\t\t\tFS_Manifest_Free(man);\n\t\t\t\t\tman = NULL;\n\t\t\t\t}\n\t\t\t\telse if (found)\n\t\t\t\t\tCon_Printf(CON_WARNING \"Warning: found multiple possible games. Using the first found (%s).\\n\", man->formalname);\n\t\t\t\telse\n\t\t\t\t\tCon_Printf(CON_ERROR \"Error: unable to determine correct game/basedir.\\n\");\n\t\t\t}\n\t\t}\n\t\tif (!man)\n\t\t{\n#ifdef _WIN32\n\t\t\t//quit straight out on windows. this prevents shitty sandboxed malware scanners from seeing bugs in opengl drivers and blaming us for it.\n\t\t\tif (!fixedbasedir && found == 0)\n\t\t\t\tSys_Error(\"No recognised game data found in working directory:\\n%s\", com_gamepath);\n#endif\n\t\t\tman = FS_Manifest_ReadMem(NULL, NULL,\n\t\t\t\t\"FTEManifestVer 1\\n\"\n\t\t\t\t\"game \\\"\\\"\\n\"\n\t\t\t\t\"name \\\"\" FULLENGINENAME \"\\\"\\n\"\n\t\t\t\t\"-set vid_fullscreen 0\\n\"\n\t\t\t\t\"-set gl_font cour\\n\"\n\t\t\t\t\"-set vid_width 640\\n\"\n\t\t\t\t\"-set vid_height 480\\n\"\n\t\t\t\t);\n\t\t}\n\t\tif (!man)\n\t\t\tSys_Error(\"couldn't generate dataless manifest\\n\");\n\t}\n\n#ifdef PACKAGEMANAGER\n\tif (fs_manifest && fs_manifest->downloadsurl)\n\t\tolddownloadsurl = Z_StrDup(fs_manifest->downloadsurl);\n\telse if (!fs_manifest && man->downloadsurl)\n\t\tolddownloadsurl = Z_StrDup(man->downloadsurl);\n\telse\n\t\tolddownloadsurl = NULL;\n#endif\n\n\tif (man->installation && *man->installation)\n\t{\n\t\tfor (i = 0; gamemode_info[i].argname; i++)\n\t\t{\n\t\t\tif (!strcmp(man->installation, gamemode_info[i].argname+1))\n\t\t\t{\n\t\t\t\t//if there's no base dirs, edit the manifest to give it its default ones.\n\t\t\t\tfor (j = 0; j < sizeof(man->gamepath) / sizeof(man->gamepath[0]); j++)\n\t\t\t\t{\n\t\t\t\t\tif (man->gamepath[j].path && (man->gamepath[j].flags&GAMEDIR_BASEGAME))\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tif (j == sizeof(man->gamepath) / sizeof(man->gamepath[0]))\n\t\t\t\t{\n\t\t\t\t\tfor (j = 0; j < 4; j++)\n\t\t\t\t\t\tif (gamemode_info[i].dir[j])\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tCmd_TokenizeString(va(\"basegame \\\"%s\\\"\", gamemode_info[i].dir[j]), false, false);\n\t\t\t\t\t\t\tFS_Manifest_ParseTokens(man);\n\t\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (!man->schemes)\n\t\t\t\t{\n\t\t\t\t\tCmd_TokenizeString(va(\"schemes \\\"%s\\\"\", gamemode_info[i].argname+1), false, false);\n\t\t\t\t\tFS_Manifest_ParseTokens(man);\n\t\t\t\t}\n\n#ifdef PACKAGEMANAGER\n\t\t\t\tif (!man->downloadsurl && gamemode_info[i].downloadsurl)\n\t\t\t\t{\n#ifndef FTE_TARGET_WEB\n\t\t\t\t\tif (*gamemode_info[i].downloadsurl == '/')\n\t\t\t\t\t{\n\t\t\t\t\t\tconchar_t musite[256], *e;\n\t\t\t\t\t\tchar site[256];\n\t\t\t\t\t\tchar *oldprefix = \"http://fte.\";\n\t\t\t\t\t\tchar *newprefix = \"https://updates.\";\n\t\t\t\t\t\te = COM_ParseFunString(CON_WHITEMASK, ENGINEWEBSITE, musite, sizeof(musite), false);\n\t\t\t\t\t\tCOM_DeFunString(musite, e, site, sizeof(site)-1, true, true);\n\t\t\t\t\t\tif (!strncmp(site, oldprefix, strlen(oldprefix)))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tmemmove(site+strlen(newprefix), site+strlen(oldprefix), strlen(site)-strlen(oldprefix)+1);\n\t\t\t\t\t\t\tmemcpy(site, newprefix, strlen(newprefix));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tCmd_TokenizeString(va(\"downloadsurl \\\"%s%s\\\"\", site, gamemode_info[i].downloadsurl), false, false);\n\t\t\t\t\t}\n\t\t\t\t\telse\n#endif\n\t\t\t\t\t\tCmd_TokenizeString(va(\"downloadsurl \\\"%s\\\"\", gamemode_info[i].downloadsurl), false, false);\n\t\t\t\t\tFS_Manifest_ParseTokens(man);\n\t\t\t\t}\n\t\t\t\tif (!man->installupd && gamemode_info[i].needpackages)\n\t\t\t\t{\n\t\t\t\t\tCmd_TokenizeString(va(\"install \\\"%s\\\"\", gamemode_info[i].needpackages), false, false);\n\t\t\t\t\tFS_Manifest_ParseTokens(man);\n\t\t\t\t}\n#endif\n\n\t\t\t\tif (!man->protocolname)\n\t\t\t\t{\n\t\t\t\t\tCmd_TokenizeString(va(\"protocolname \\\"%s\\\"\", gamemode_info[i].protocolname), false, false);\n\t\t\t\t\tFS_Manifest_ParseTokens(man);\n\t\t\t\t}\n\t\t\t\tif (!man->defaultexec && gamemode_info[i].customexec)\n\t\t\t\t{\n\t\t\t\t\tconst char *e = gamemode_info[i].customexec;\n\t\t\t\t\twhile (e[0] == '/' && e[1] == '/')\n\t\t\t\t\t{\n\t\t\t\t\t\te+=2;\n\t\t\t\t\t\twhile(*e)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (*e++ == '\\n')\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tman->defaultexec = Z_StrDup(e);\n\t\t\t\t}\n\n\t\t\t\tbuiltingame = true;\n\t\t\t\tif (!fixedbasedir)\n\t\t\t\t{\n\t\t\t\t\tif (man->basedir)\n\t\t\t\t\t\tQ_strncpyz (newbasedir, man->basedir, sizeof(newbasedir));\n\t\t\t\t\telse if (!FS_DirHasGame(newbasedir, i))\n\t\t\t\t\t\tif (Sys_FindGameData(man->formalname, man->installation, realpath, sizeof(realpath), man->security != MANIFEST_SECURITY_INSTALLER) && FS_FixPath(realpath, sizeof(realpath)) && FS_DirHasGame(realpath, i))\n\t\t\t\t\t\t\tQ_strncpyz (newbasedir, realpath, sizeof(newbasedir));\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\tif (!fixedbasedir)\n\t{\n\t\tif (!builtingame && !fixedbasedir && !FS_DirHasAPackage(newbasedir, man))\n\t\t{\n\t\t\tif (Sys_FindGameData(man->formalname, man->installation, realpath, sizeof(realpath), man->security != MANIFEST_SECURITY_INSTALLER) && FS_FixPath(realpath, sizeof(realpath)) && FS_DirHasAPackage(realpath, man))\n\t\t\t\tQ_strncpyz (newbasedir, realpath, sizeof(newbasedir));\n\t\t\telse if (man->basedir)\n\t\t\t\tQ_strncpyz (newbasedir, man->basedir, sizeof(newbasedir));\n#if !defined(NOBUILTINMENUS) && defined(HAVE_CLIENT)\n\t\t\telse if (man != fs_manifest)\n\t\t\t{\n#ifdef PACKAGEMANAGER\n\t\t\t\tZ_Free(olddownloadsurl);\n#endif\n\t\t\t\tM_Menu_BasedirPrompt(man);\n\t\t\t\treturn false;\n\t\t\t}\n#endif\n#ifdef HAVE_CLIENT\n\t\t\telse\n\t\t\t{\t//no basedir known... switch to installer mode and ask the user where they want it (at least on windows)\n\t\t\t\tZ_Free(man->filename);\n\t\t\t\tman->filename = NULL;\n\t\t\t\tcom_installer = true;\n\t\t\t}\n#endif\n\t\t}\n\t}\n\tif (!fixedbasedir && !com_installer)\n\t{\n\t\tif (strcmp(com_gamepath, newbasedir))\n\t\t{\n\t\t\tQ_strncpyz (com_gamepath, newbasedir, sizeof(com_gamepath));\n\t\t\tbasedirchanged = true;\n\t\t}\n\t}\n\n\t//now that we know what we're running and where we're running it, we can switch to it.\n\tif (man == fs_manifest)\n\t{\n\t\t//don't close anything. theoretically nothing is changing, and we don't want to load new defaults either.\n\t}\n\telse if (!fs_manifest || !strcmp(fs_manifest->installation?fs_manifest->installation:\"\", man->installation?man->installation:\"\"))\n\t{\n\t\tif (!fs_manifest)\n\t\t{\n\t\t\tbasedirchanged = true;\n\t\t\treloadconfigs = true;\n\t\t}\n\t\tFS_Manifest_Free(fs_manifest);\n\t}\n\telse\n\t{\n\t\tFS_FreePaths();\n\n\t\treloadconfigs = true;\n\t}\n\tfs_manifest = man;\n\n\n\n#ifdef PACKAGEMANAGER\n\tif (basedirchanged)\n\t\tPM_ManifestChanged(man);\n\n\tif (man->security == MANIFEST_SECURITY_NOT && strcmp(man->downloadsurl?man->downloadsurl:\"\", olddownloadsurl?olddownloadsurl:\"\"))\n\t{\t//make sure we only fuck over the user if this is a 'secure' manifest, and not hacked in some way.\n\t\tZ_Free(man->downloadsurl);\n\t\tman->downloadsurl = olddownloadsurl;\n\t}\n\telse\n\t\tZ_Free(olddownloadsurl);\n#else\n\t(void)basedirchanged;\n#endif\n\n\t//make sure it has a trailing slash, or is empty. woo.\n\tFS_CleanDir(com_gamepath, sizeof(com_gamepath));\n\n\t{\n\t\tqboolean oldhome = com_homepathenabled;\n\t\tCOM_InitHomedir(man);\n\n\t\tif (com_homepathenabled != oldhome)\n\t\t{\n\t\t\tif (com_homepathenabled)\n\t\t\t\tCon_TPrintf(\"Using home directory \\\"%s\\\"\\n\", com_homepath);\n\t\t\telse\n\t\t\t\tCon_TPrintf(\"Disabled home directory support\\n\");\n\t\t}\n\t}\n\n#ifdef ANDROID\n\t{\n\t\t//write a .nomedia file to avoid people from getting random explosion sounds etc interspersed with their music\n\t\tvfsfile_t *f;\n\t\tchar nomedia[MAX_OSPATH];\n\t\t//figure out the path we're going to end up writing to\n\t\tif (com_homepathenabled)\n\t\t\tsnprintf(nomedia, sizeof(nomedia), \"%s%s\", com_homepath, \".nomedia\");\n\t\telse\n\t\t\tsnprintf(nomedia, sizeof(nomedia), \"%s%s\", com_gamepath, \".nomedia\");\n\n\t\t//make sure it exists.\n\t\tf = VFSOS_Open(nomedia, \"rb\");\n\t\tif (!f)\t//don't truncate\n\t\t{\n\t\t\tCOM_CreatePath(nomedia);\n\t\t\tf = VFSOS_Open(nomedia, \"wb\");\n\t\t}\n\t\tif (f)\n\t\t\tVFS_CLOSE(f);\n\t}\n#endif\n\n\t//our basic filesystem should be okay, but no packages loaded yet.\n#ifdef MANIFESTDOWNLOADS\n\t//make sure the package manager knows what its meant to know...\n\tPM_AddManifestPackages(man, allowdownload);\n#endif\n\n\tif (Sys_LockMutex(fs_thread_mutex))\n\t{\n#ifdef HAVE_CLIENT\n\t\tint vidrestart = FS_GameIsInitialised()?false:2;\n#endif\n\n\t\tFS_ReloadPackFilesFlags(~0);\n\n\t\tSys_UnlockMutex(fs_thread_mutex);\n\n\t\tFS_BeginManifestUpdates();\n\n#ifdef MANIFESTDOWNLOADS\n\t\tif (FS_DownloadingPackage() && fs_loadedcommand)\n\t\t\tallowreloadconfigs = false;\n#endif\n\n\t\tCOM_CheckRegistered();\n\n#ifdef HAVE_CLIENT\n\t\tif (qrenderer != QR_NONE && allowvidrestart && !vidrestart)\n\t\t{\n\t\t\tfor (i = 0; i < countof(vidfile); i++)\n\t\t\t{\n\t\t\t\tFS_FLocateFile(vidfile[i], FSLF_IFFOUND, &loc);\n\t\t\t\tif (vidpath[i] != (loc.search?loc.search->handle:NULL))\n\t\t\t\t{\n\t\t\t\t\tvidrestart = true;\n\t\t\t\t\tCon_DPrintf(\"Restarting video because %s has changed\\n\", vidfile[i]);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n#endif\n\n\t\tif (allowreloadconfigs)\n\t\t{\n\t\t\tif (!reloadconfigs)\n\t\t\t\tfor (i = 0; i < countof(conffile); i++)\n\t\t\t\t{\n\t\t\t\t\tFS_FLocateFile(conffile[i], FSLF_IFFOUND|FSLF_IGNOREPURE, &loc);\n\t\t\t\t\tif (confpath[i] != (loc.search?loc.search->handle:NULL))\n\t\t\t\t\t{\n\t\t\t\t\t\treloadconfigs = true;\n\t\t\t\t\t\tCon_DPrintf(\"Reloading configs because %s has changed\\n\", conffile[i]);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\tif (reloadconfigs)\n\t\t\t{\n\t\t\t\tCvar_SetEngineDefault(&fs_gamename, man->formalname?man->formalname:\"FTE\");\n\t\t\t\tCvar_SetEngineDefault(&com_protocolname, man->protocolname?man->protocolname:\"FTE\");\n\t\t\t\t//FIXME: flag this instead and do it after a delay?\n\t\t\t\tCvar_ForceSet(&fs_gamename, fs_gamename.enginevalue);\n\t\t\t\tCvar_ForceSet(&com_protocolname, com_protocolname.enginevalue);\n\n#ifdef HAVE_SERVER\n\t\t\t\tif (isDedicated)\n\t\t\t\t\tSV_ExecInitialConfigs(man->defaultexec?man->defaultexec:\"\");\n\t\t\t\telse\n#endif\n#ifdef HAVE_CLIENT\n\t\t\t\tif (1)\n\t\t\t\t{\n\t\t\t\t\tCL_ExecInitialConfigs(man->defaultexec?man->defaultexec:\"\", vidrestart==2);\n\t\t\t\t\tvidrestart = false;\n\t\t\t\t}\n\t\t\t\telse\n#endif\n\t\t\t\t{\n\t\t\t\t\tCOM_ParsePlusSets(true);\n\t\t\t\t\tCbuf_Execute ();\n\t\t\t\t}\n\t\t\t}\n#ifdef HAVE_CLIENT\n\n\t\t\tif (Cmd_Exists(\"ui_restart\"))\t//if we're running a q3 ui, restart it now...\n\t\t\t\tCbuf_InsertText(\"ui_restart\\n\", RESTRICT_LOCAL, false);\n#endif\n\n\t\t\tif (fs_loadedcommand)\n\t\t\t{\n\t\t\t\tCbuf_AddText(fs_loadedcommand, RESTRICT_INSECURE);\n\t\t\t\tZ_Free(fs_loadedcommand);\n\t\t\t\tfs_loadedcommand = NULL;\n\t\t\t}\n\t\t}\n#ifdef HAVE_CLIENT\n\t\tif (vidrestart == 2)\n\t\t{\t//done when we picked an initial mod to run (so managed to actually read user settings instead of being forced windowed)\n\t\t\tCbuf_AddText (\"vid_restart\\n\", RESTRICT_LOCAL);\n\t\t\tvidrestart = false;\n\t\t}\n\t\telse if (vidrestart)\n\t\t{\n\t\t\tCbuf_AddText (\"vid_reload\\n\", RESTRICT_LOCAL);\n\t\t\tvidrestart = false;\n\t\t}\n\n\n\t\tif (qrenderer != QR_NONE && allowreloadconfigs)\n\t\t{\n\t\t\tfor (i = 0; i < countof(menufile); i++)\n\t\t\t{\n\t\t\t\tFS_FLocateFile(menufile[i], FSLF_IFFOUND, &loc);\n\t\t\t\tif (menupath[i] != (loc.search?loc.search->handle:NULL))\n\t\t\t\t{\n\t\t\t\t\tCbuf_AddText (\"menu_restart\\n\", RESTRICT_LOCAL);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n#endif\n\n\t\t//rebuild the cache now, should be safe to waste some cycles on it\n\t\tCOM_FlushFSCache(false, true);\n\t}\n\n#ifdef HAVE_CLIENT\n\tValidation_FlushFileList();\t//prevent previous hacks from making a difference.\n#endif\n\n\t{\n\t\tvoid (QDECL *callback)(struct cvar_s *var, char *oldvalue) = fs_game.callback;\n\t\tfs_game.callback = NULL;\n\t\tCvar_ForceSet(&fs_game, FS_GetGamedir(false));\n\t\tfs_game.callback = callback;\n\t}\n\tCvar_ForceSet(&fs_gamepath, va(\"%s%s\", com_gamepath, FS_GetGamedir(false)));\n\tCvar_ForceSet(&fs_basepath, com_gamepath);\n\tCvar_ForceSet(&fs_homepath, com_gamepath);\n\n\tMods_FlushModList();\n\n\treturn true;\n}\n\nvoid FS_CreateBasedir(const char *path)\n{\n\tvfsfile_t *f;\n\tcom_installer = false;\n\tif (path)\n\t{\n\t\tQ_strncpyz (com_gamepath, path, sizeof(com_gamepath));\n\t\tCOM_CreatePath(com_gamepath);\n\t}\n\tfs_manifest->security = MANIFEST_SECURITY_INSTALLER;\n\tFS_ChangeGame(fs_manifest, true, false);\n\n\tif (path && host_parms.manifest)\n\t{\n\t\tf = FS_OpenVFS(\"default.fmf\", \"wb\", FS_ROOT);\n\t\tif (f)\n\t\t{\n\t\t\tVFS_WRITE(f, host_parms.manifest, strlen(host_parms.manifest));\n\t\t\tVFS_CLOSE(f);\n\t\t}\n\t}\n}\n\ntypedef struct\n{\n\tqboolean anygamedir;\n\tconst char *basedir;\n\tint found;\n\tqboolean (*callback)(void *usr, ftemanifest_t *man, enum modsourcetype_e sourcetype);\n\tenum modsourcetype_e sourcetype;\n\tvoid *usr;\n\tint legacyid;\n} fmfenums_t;\nstatic int QDECL FS_EnumeratedFMF(const char *fname, qofs_t fsize, time_t mtime, void *inf, searchpathfuncs_t *spath)\n{\n\tftemanifest_t *man = NULL;\n\tfmfenums_t *e = inf;\n\tvfsfile_t *f = NULL;\t\t\n\tif (spath)\n\t{\n\t\tflocation_t loc;\n\t\tif (spath->FindFile(spath, &loc, fname, NULL))\n\t\t{\n\t\t\tf = spath->OpenVFS(spath, &loc, \"rb\");\n\t\t\tif (f)\n\t\t\t{\n\t\t\t\tsize_t l = VFS_GETLEN(f);\n\t\t\t\tchar *data = Z_Malloc(l+1);\n\t\t\t\tif (data)\n\t\t\t\t{\n\t\t\t\t\tVFS_READ(f, data, l);\n\t\t\t\t\tdata[l] = 0;\t//just in case.\n\n\t\t\t\t\tman = FS_Manifest_ReadMem(NULL, e->basedir, data);\n\t\t\t\t\tZ_Free(data);\n\t\t\t\t}\n\t\t\t\tVFS_CLOSE(f);\n\t\t\t}\n\t\t}\n\t}\n\telse if (e->basedir == com_gamepath)\n\t\tman = FS_Manifest_ReadMod(fname);\n\telse\n\t{\n\t\tman = FS_Manifest_ReadSystem(fname, e->basedir);\n\n\t\tif (man && !man->basedir && man->installation && *man->installation)\n\t\t{\t//try and give it a proper gamedir...\n\t\t\tchar basedir[MAX_OSPATH];\n\n\t\t\t//enables sources etc.\n\t\t\tman->security = MANIFEST_SECURITY_INSTALLER;\n\n\t\t\tif (Sys_FindGameData(NULL, man->installation, basedir, sizeof(basedir), false))\n\t\t\t\tman->basedir = Z_StrDup(basedir);\n\t\t}\n\t}\n\n\tif (man)\n\t{\n\t\tif (!e->anygamedir && man->basedir && strcmp(man->basedir, com_gamepath))\n\t\t\tFS_Manifest_Free(man);\n\t\telse if (e->callback(e->usr, man, e->sourcetype))\n\t\t\te->found++;\n\t\telse\n\t\t\tFS_Manifest_Free(man);\n\t}\n\n\treturn true;\n}\n\nstatic void FS_EnumeratedBasedir(void *vctx, const char *foundbasedir)\n{\n\tfmfenums_t *e = vctx;\n\tftemanifest_t *man;\n\tman = FS_GenerateLegacyManifest(e->legacyid, foundbasedir);\n\tif (e->callback(e->usr, man, MST_INTRINSIC))\n\t\te->found++;\n\telse\n\t\tFS_Manifest_Free(man);\n}\n\n//callback must call FS_Manifest_Free or return false.\nint FS_EnumerateKnownGames(qboolean (*callback)(void *usr, ftemanifest_t *man, enum modsourcetype_e sourcetype), void *usr, qboolean fixedbasedir)\n{\n\tint i;\n\tchar basedir[MAX_OSPATH];\n\tfmfenums_t e;\n\tftemanifest_t *man;\n\te.anygamedir = (!fs_manifest || !*fs_manifest->installation) && !fixedbasedir;\n\te.found = 0;\n\te.callback = callback;\n\te.usr = usr;\n\n\tif (e.anygamedir)\n\t{\n\t\te.basedir = com_gamepath;\n\t\tman = FS_ReadDefaultManifest(com_gamepath, 0, true);\n\t\tif (man)\n\t\t{\n\t\t\tif (e.callback(e.usr, man, MST_DEFAULT))\n\t\t\t\te.found++;\n\t\t\telse\n\t\t\t\tFS_Manifest_Free(man);\n\t\t}\n\t}\n\n\t//okay, no manifests in the basepack, try looking in the basedir.\n\t//this defaults to the working directory. perhaps try the exe's location instead?\n\te.basedir = com_gamepath;\n\te.sourcetype = MST_BASEDIR;\n\tSys_EnumerateFiles(com_gamepath, \"*.fmf\", FS_EnumeratedFMF, &e, NULL);\n\tif (*com_homepath)\n\t{\n\t\te.sourcetype = MST_HOMEDIR;\n\t\tSys_EnumerateFiles(com_homepath, \"*.fmf\", FS_EnumeratedFMF, &e, NULL);\n\t}\n\n//\tif (e.anygamedir)\n\t{\n#ifdef __unix__\n\t\tconst char *dirs = getenv(\"XDG_CONFIG_DIRS\");\n\t\tif (!dirs || !*dirs)\n\t\t\tdirs = \"/etc/xdg\";\n\n\t\te.basedir = NULL;\n\t\te.sourcetype = MST_SYSTEM;\n\t\twhile (dirs && *dirs)\n\t\t{\n\t\t\tdirs = COM_ParseStringSetSep(dirs, ':', basedir, sizeof(basedir));\n\t\t\tif (*basedir)\n\t\t\t\tFS_CleanDir(basedir, sizeof(basedir));\n\t\t\tQ_strncatz(basedir, \"fteqw/*.fmf\", sizeof(basedir));\n\t\t\tSys_EnumerateFiles(NULL, basedir, FS_EnumeratedFMF, &e, NULL);\n\t\t}\n#endif\n\t}\n\n\t//-basepack is primarily an android feature, where the apk file is specified.\n\t//this allows custom mods purely by customising the apk\n\te.basedir = host_parms.basedir;\n\te.sourcetype = MST_SYSTEM;\n\ti = COM_CheckParm (\"-basepack\");\n\twhile (i && i < com_argc-1)\n\t{\n\t\tconst char *pakname = com_argv[i+1];\n\t\tsearchpathfuncs_t *pak;\n\t\tvfsfile_t *vfs = VFSOS_Open(pakname, \"rb\");\n\t\tpak = FS_OpenPackByExtension(vfs, NULL, pakname, pakname, \"\");\n\t\tif (pak)\n\t\t{\n\t\t\tpak->EnumerateFiles(pak, \"*.fmf\", FS_EnumeratedFMF, &e);\n\t\t\tpak->ClosePath(pak);\n\t\t}\n\t\ti = COM_CheckNextParm (\"-basepack\", i);\n\t}\n\n\t//right, no fmf files anywhere.\n\t//just make stuff up from whatever games they may have installed on their system.\n\tfor (i = 0; gamemode_info[i].argname; i++)\n\t{\n\t\tQ_strncpyz(basedir, com_gamepath, sizeof(basedir));\n\t\tif (gamemode_info[i].manifestfile ||\n\t\t\t((gamemode_info[i].exename || (i>0&&gamemode_info[i].customexec&&gamemode_info[i-1].customexec&&strcmp(gamemode_info[i].customexec,gamemode_info[i-1].customexec))) && FS_DirHasGame(basedir, i)))\n\t\t{\n\t\t\tman = FS_GenerateLegacyManifest(i, basedir);\n\t\t\tif (e.callback(e.usr, man, MST_INTRINSIC))\n\t\t\t\te.found++;\n\t\t\telse\n\t\t\t\tFS_Manifest_Free(man);\n\t\t}\n\t\telse if (e.anygamedir)\n\t\t{\n\t\t\te.legacyid = i;\n\t\t\tSys_FindBaseDirs(NULL, gamemode_info[i].argname+1, FS_EnumeratedBasedir,&e);\n\t\t}\n\t}\n\treturn e.found;\n}\n\n//attempts to find a new basedir for 'input', changing to it as appropriate\n//returns fixed up filename relative to the new gamedir.\n//input must be an absolute path.\nqboolean FS_FixupGamedirForExternalFile(char *input, char *filename, size_t fnamelen)\n{\n\tchar syspath[MAX_OSPATH];\n\tchar gamepath[MAX_OSPATH];\n\tvoid *iterator;\n\tchar *sep,*bs;\n\tchar *src = NULL;\n\n\tQ_strncpyz(filename, input, fnamelen);\n\n\titerator = NULL;\n\twhile(COM_IteratePaths(&iterator, syspath, sizeof(syspath), gamepath, sizeof(gamepath)))\n\t{\n\t\tif (!Q_strncasecmp(syspath, filename, strlen(syspath)))\n\t\t{\n\t\t\tsrc = filename+strlen(syspath);\n\t\t\tmemmove(filename, src, strlen(src)+1);\n\t\t\tbreak;\n\t\t}\n\t}\n\tif (!src)\n\t{\n\t\tfor(;;)\n\t\t{\n\t\t\tsep = strchr(filename, '\\\\');\n\t\t\tif (sep)\n\t\t\t\t*sep = '/';\n\t\t\telse\n\t\t\t\tbreak;\n\t\t}\n\t\tfor (sep = NULL;;)\n\t\t{\n\t\t\tbs = sep;\n\t\t\tsep = strrchr(filename, '/');\n\t\t\tif (bs)\n\t\t\t\t*bs = '/';\n\t\t\tif (sep)\n\t\t\t{\n\t\t\t\tint game;\n\t\t\t\t*sep = 0;\n\t\t\t\tif (strchr(filename, '/'))\t//make sure there's always at least one /\n\t\t\t\t{\n\t\t\t\t\tchar temp[MAX_OSPATH];\n\t\t\t\t\tQ_snprintfz(temp, sizeof(temp), \"%s/\", filename);\n\t\t\t\t\tgame = FS_IdentifyDefaultGameFromDir(temp);\n\t\t\t\t\tif (game != -1)\n\t\t\t\t\t{\n\t\t\t\t\t\tstatic char newbase[MAX_OSPATH];\n\t\t\t\t\t\tif (!host_parms.basedir || strcmp(host_parms.basedir, filename))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tCon_Printf(\"switching basedir+game to %s for %s\\n\", filename, input);\n\t\t\t\t\t\t\tQ_strncpyz(newbase, filename, sizeof(newbase));\n\t\t\t\t\t\t\thost_parms.basedir = newbase;\n\t\t\t\t\t\t\tFS_ChangeGame(FS_GenerateLegacyManifest(game, newbase), true, true);\n\t\t\t\t\t\t}\n\t\t\t\t\t\t*sep = '/';\n\t\t\t\t\t\tsep = NULL;\n\t\t\t\t\t\tsrc = filename+strlen(host_parms.basedir);\n\t\t\t\t\t\tmemmove(filename, src, strlen(src)+1);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t\tbreak;\n\t\t}\n\t\tif (sep)\n\t\t\t*sep = '/';\n\t}\n\tif (!src && host_parms.binarydir && !Q_strncasecmp(host_parms.binarydir, filename, strlen(host_parms.binarydir)))\n\t{\n\t\tsrc = filename+strlen(host_parms.binarydir);\n\t\tmemmove(filename, src, strlen(src)+1);\n\t}\n\tif (!src && host_parms.basedir && !Q_strncasecmp(host_parms.basedir, filename, strlen(host_parms.basedir)))\n\t{\n\t\tsrc = filename+strlen(host_parms.basedir);\n\t\tmemmove(filename, src, strlen(src)+1);\n\t}\n\tif (!src)\n\t{\n\t\tQ_snprintfz(filename, fnamelen, \"#%s\", input);\n\t\treturn false;\n\t}\n\tif (*filename == '/' || *filename == '\\\\')\n\t\tmemmove(filename, filename+1, strlen(filename+1)+1);\n\n\tsep = strchr(filename, '/');\n\tbs = strchr(filename, '\\\\');\n\tif (bs && (!sep || bs < sep))\n\t\tsep = bs;\n\tif (sep)\n\t{\n\t\tCon_Printf(\"switching gamedir for %s\\n\", filename);\n\t\t*sep = 0;\n\t\tCOM_Gamedir(filename, NULL);\n\t\tmemmove(filename, sep+1, strlen(sep+1)+1);\n\t\treturn true;\n\t}\n\tQ_snprintfz(filename, fnamelen, \"#%s\", input);\n\treturn false;\n}\n\nvoid Cvar_GamedirChange(void);\nvoid\t\tPlug_Shutdown(qboolean preliminary);\n\n/*mod listing management*/\nstatic struct modlist_s *modlist;\nstatic size_t nummods;\nstatic qboolean modsinited;\nvoid Mods_FlushModList(void)\n{\n\twhile (nummods)\n\t{\n\t\tnummods--;\n\t\tif (modlist[nummods].manifest)\n\t\t\tFS_Manifest_Free(modlist[nummods].manifest);\n\t\tif (modlist[nummods].description)\n\t\t\tZ_Free(modlist[nummods].description);\n\t\tif (modlist[nummods].gamedir)\n\t\t\tZ_Free(modlist[nummods].gamedir);\n\t}\n\tif (modlist)\n\t\tZ_Free(modlist);\n\tmodlist = NULL;\n\tmodsinited = false;\n}\n\nstatic qboolean Mods_AddManifest(void *usr, ftemanifest_t *man, enum modsourcetype_e sourcetype)\n{\n\tint p, best = -1;\n\tint i = nummods;\n\n\tswitch(sourcetype)\n\t{\n\tcase MST_SYSTEM:\t//part of the app's install or via some other system package that should be found upfront.\n\tcase MST_INTRINSIC:\t//embedded into the engine (very little info)\n\t\t//if we seem to already know about this game in the same basedir then assume its a dupe. don't care\n\t\t//note that intrinsics are ignored entirely if someone took the time to make any other kind of fmf for that basedir.\n\t\tfor (p = 0; p < nummods; p++)\n\t\t{\n\t\t\tif (\tmodlist[p].manifest &&\n\t\t\t\t\t!strcmp(modlist[p].manifest->basedir?modlist[p].manifest->basedir:\"\", man->basedir?man->basedir:\"\") &&\n\t\t\t\t\t!strcmp(modlist[p].manifest->mainconfig?modlist[p].manifest->mainconfig:\"\", man->mainconfig?man->mainconfig:\"\") &&\n\t\t\t\t\t((modlist[p].sourcetype!=MST_INTRINSIC&&sourcetype==MST_INTRINSIC) || !Q_strcasecmp(modlist[p].manifest->installation, man->installation)))\n\t\t\t\treturn false;\n\t\t}\n\t\tbreak;\n\tcase MST_DEFAULT:\t//the default.fmf (basically MST_BASEDIR, but posh)\n\tcase MST_BASEDIR:\t//fmf found inside the basedir we're using  (yeah, weird, you'll have to pick the base game to switch basedir first).\n\tcase MST_HOMEDIR:\t//fmf found inside the homedir of the mod we're using (yeah, weird, you'll have to pick the base game first)\n\tcase MST_GAMEDIR:\t//found inside a gamedir...\n\tcase MST_UNKNOWN:\t//shouldn't really be hit.\n\t\tbreak;\n\t}\n\n\tfor (p = 0; p < countof(man->gamepath); p++)\n\t\tif (man->gamepath[p].path)\n\t\t{\n\t\t\tif (man->gamepath[p].flags & (GAMEDIR_PRIVATE|GAMEDIR_STEAMGAME))\n\t\t\t\tcontinue;\t//don't pick paths that don't make sense to others.\n\t\t\tif (!(man->gamepath[p].flags & GAMEDIR_BASEGAME) || (best<0||(man->gamepath[best].flags & GAMEDIR_BASEGAME)))\n\t\t\t\tbest = p;\n\t\t}\n\tif (best < 0)\n\t{\n\t\tmodlist = BZ_Realloc(modlist, (i+1) * sizeof(*modlist));\n\t\tmodlist[i].manifest = man;\n\t\tmodlist[i].sourcetype = sourcetype;\n\t\tmodlist[i].gamedir = Z_StrDup(\"?\");\n\t\tmodlist[i].description = man->formalname?Z_StrDup(man->formalname):NULL;\n\t\tnummods = i+1;\n\t\treturn true; //no gamedirs? wut? some partially-defined game we don't recognise?\n\t}\n\n\tmodlist = BZ_Realloc(modlist, (i+1) * sizeof(*modlist));\n\tmodlist[i].manifest = man;\n\tmodlist[i].sourcetype = sourcetype;\n\tmodlist[i].gamedir = Z_StrDup(man->gamepath[best].path);\n\tmodlist[i].description = man->formalname?Z_StrDup(man->formalname):NULL;\n\tnummods = i+1;\n\treturn true;\n}\nstatic int Mods_WasPackageOrDatFound(const char *fname, qofs_t ofs, time_t modtime, void *usr, searchpathfuncs_t *spath)\n{\t//we check for *.dat too, because we care about [qw]progs.dat/menu.dat/csprogs.dat or possibly addons. hopefully we can get away with such a generic extension.\n\tconst char *ext = COM_GetFileExtension(fname, NULL);\n\tif (!Q_strcasecmp(ext, \".pk3\") || !Q_strcasecmp(ext, \".pak\") || !Q_strcasecmp(ext, \".dat\"))\n\t\treturn false;\t//found one, can stop searching now\n\t//FIXME: pk3dir\n\treturn true;\t//keep looking for one\n}\nstatic int Mods_WasMapFound(const char *fname, qofs_t ofs, time_t modtime, void *usr, searchpathfuncs_t *spath)\n{\n\tconst char *ext = COM_GetFileExtension(fname, NULL);\n\tif (!Q_strcasecmp(ext, \".gz\"))\n\t\text = COM_GetFileExtension(fname, ext);\n\t//don't bother looking for .map\n\tif (!Q_strcasecmp(ext, \".bsp\"   ) || !Q_strcasecmp(ext, \".hmp\"   ) ||\n\t\t!Q_strcasecmp(ext, \".bsp.gz\") || !Q_strcasecmp(ext, \".hmp.gz\"))\n\t\treturn false;\t//found one, can stop searching now\n\treturn true;\t//keep looking for one\n}\nstatic int QDECL Mods_AddGamedir(const char *fname, qofs_t fsize, time_t mtime, void *usr, searchpathfuncs_t *spath)\n{\n\tchar *desc;\n\tsize_t l = strlen(fname);\n\tint i, p;\n\tchar gamedir[MAX_QPATH];\n\tconst char *basedir = usr;\n\tif (l && fname[l-1] == '/' && l < countof(gamedir) && !strchr(fname, '\\\\')/*SDL3 seems buggy on windows - wildcards are not meant to match to '/'.. but windows uses '\\' so far too much nesting.*/)\n\t{\n\t\tl--;\n\t\tmemcpy(gamedir, fname, l);\n\t\tgamedir[l] = 0;\n\n\t\tif (strchr(gamedir, ';') || !FS_GamedirIsOkay(gamedir, false))\t//this is enumeration, so don't spam.\n\t\t\treturn true;\t//don't list it if we can't use it anyway.\n\n\t\tfor (i = 0; i < nummods; i++)\n\t\t{\n\t\t\t//don't add dupes (can happen from basedir+homedir)\n\t\t\t//if the gamedir was already included in one of the manifests, don't bother including it again.\n\t\t\t//this generally removes id1.\n\t\t\tif (modlist[i].manifest)\n\t\t\t{\n\t\t\t\tfor (p = 0; p < countof(fs_manifest->gamepath); p++)\n\t\t\t\t\tif (modlist[i].manifest->gamepath[p].path)\n\t\t\t\t\t\tif (!Q_strcasecmp(modlist[i].manifest->gamepath[p].path, gamedir))\n\t\t\t\t\t\t\treturn true;\n\t\t\t}\n\t\t\telse if (modlist[i].gamedir)\n\t\t\t{\n\t\t\t\tif (!Q_strcasecmp(modlist[i].gamedir, gamedir))\n\t\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\n\t\tif ((desc = FS_MallocFile(va(\"%s%s/modinfo.txt\", basedir, gamedir), FS_SYSTEM, NULL)))\n\t\t\t;\t//dp's modinfo.txt thing (which no mod seems to use anyway)\n\t\telse if ((desc = FS_MallocFile(va(\"%s%s/description.txt\", basedir, gamedir), FS_SYSTEM, NULL)))\n\t\t\t;\t//quake3's description stuff\n\t\telse if ((desc = FS_MallocFile(va(\"%s%s/liblist.gam\", basedir, gamedir), FS_SYSTEM, NULL)))\n\t\t{\t//halflifeisms? o.O mneh why not\n\t\t\tsize_t u;\n\t\t\tCmd_TokenizeString(desc, false, false);\n\t\t\tFS_FreeFile(desc);\n\t\t\tdesc = NULL;\n\t\t\tfor (u = 0; u < Cmd_Argc(); u+=2)\n\t\t\t{\n\t\t\t\tif (!Q_strcasecmp(Cmd_Argv(u), \"game\"))\n\t\t\t\t\tdesc = Cmd_Argv(u+1);\n\t\t\t}\n\t\t\tif (desc)\n\t\t\t\tdesc = Z_StrDup(desc);\n\t\t}\n\t\t//we don't really know what it is. probably some useless subdir. report it only if it looks like there's something actually interesting in there\n\t\telse if (!Sys_EnumerateFiles(va(\"%s%s/\", basedir, gamedir), \"*.*\", Mods_WasPackageOrDatFound, NULL, NULL) ||\n\t\t\t\t!Sys_EnumerateFiles(va(\"%s%s/maps/\", basedir, gamedir), \"*.*\", Mods_WasMapFound, NULL, NULL))\n\t\t\t;\t//stopped early means we found a file.\n\t\telse\n\t\t\treturn true;\t//nothing interesting there... don't bother to list it\n\n\t\tmodlist = BZ_Realloc(modlist, (i+1) * sizeof(*modlist));\n\t\tmodlist[i].manifest = NULL;\n\t\tmodlist[i].gamedir = Z_StrDup(gamedir);\n\t\tmodlist[i].description = desc;\n\t\tnummods = i+1;\n\t}\n\treturn true;\n}\n\nstatic int QDECL Mods_SortMod(const void *first, const void *second)\n{\n\tconst struct modlist_s *a = first;\n\tconst struct modlist_s *b = second;\n\tint d = 0;\n\tif (a->manifest || b->manifest)\n\t{\n\t\tif (a->manifest && b->manifest)\n\t\t{\n\t\t\tif (!d)\n\t\t\t\td = Q_strcasecmp(a->manifest->formalname, b->manifest->formalname);\n\t\t\tif (!d)\n\t\t\t\td = Q_strcasecmp(a->manifest->basedir, b->manifest->basedir);\n\t\t\tif (!d)\n\t\t\t\td = strcmp(a->gamedir, b->gamedir);\n\t\t}\n\t\telse\n\t\t\td = a->manifest?1:-1;\t//put manifest-based ones first.\n\t}\n\tif (!d)\n\t\td = strcmp(a->gamedir, b->gamedir);\n\treturn d;\n}\nstruct modlist_s *Mods_GetMod(size_t diridx)\n{\n\tif (!modsinited)\n\t{\n\t\tint mancount;\n\t\tmodsinited = true;\n\t\tFS_EnumerateKnownGames(Mods_AddManifest, NULL, *fs_manifest->installation);\n\t\tmancount = nummods;\n\t\tif (*fs_manifest->installation)\n\t\t{\n\t\t\tif (com_homepathenabled)\n\t\t\t\tSys_EnumerateFiles(com_homepath, \"*\", Mods_AddGamedir, com_homepath, NULL);\n\t\t\tSys_EnumerateFiles(com_gamepath, \"*\", Mods_AddGamedir, com_gamepath, NULL);\n\t\t}\n\t\tqsort(modlist+mancount, nummods-mancount, sizeof(*modlist), Mods_SortMod);\n\t}\n\tif (diridx < nummods)\n\t\treturn &modlist[diridx];\n\treturn NULL;\n}\n\n\n\n#if defined(HAVE_CLIENT) && defined(WEBCLIENT)\ntypedef struct\n{\n\tchar *manifestname;\t//manifest getting written\n\tchar *url;\t\t\t//url to get the manifest from.\n\tchar *mantext;\t\t//contents of downloaded manifest...\n\tint mansize;\n\tftemanifest_t *man;\n} modinstall_t;\nstatic void FS_ModInstallConfirmed(void *vctx, promptbutton_t button)\n{\n\tmodinstall_t *ctx = vctx;\n\tif (button == PROMPT_YES)\n\t{\n\t\tvfsfile_t *out = FS_OpenVFS(ctx->manifestname, \"wb\", FS_SYSTEM);\n\t\tif (out)\n\t\t{\n\t\t\tVFS_WRITE(out, ctx->mantext, ctx->mansize);\n\t\t\tVFS_CLOSE(out);\n\n\t\t\tFS_ChangeGame(ctx->man, true, true);\n\t\t\tctx->man = NULL;\n\t\t}\n\t}\n\tZ_Free(ctx->mantext);\n\tZ_Free(ctx->url);\n\tZ_Free(ctx->manifestname);\n\tFS_Manifest_Free(ctx->man);\n\tZ_Free(ctx);\n}\nstatic void FS_ModInstallGot(struct dl_download *dl)\n{\n\tmodinstall_t *ctx = dl->user_ctx;\n\tif (dl->file && dl->status == DL_FINISHED)\n\t{\n\t\tctx->mansize = VFS_GETLEN(dl->file);\n\t\tctx->mantext = Z_Malloc(ctx->mansize+1);\n\t\tVFS_READ(dl->file, ctx->mantext, ctx->mansize);\n\t\tctx->mantext[ctx->mansize] = 0;\n\n\t\tctx->man = FS_Manifest_ReadMem(ctx->manifestname, com_gamepath, ctx->mantext);\n\t\tif (ctx->man && !strcmp(ctx->man->basedir, com_gamepath))\n\t\t{\n\t\t\t//should probably show just the hostname for brevity.\n\t\t\tMenu_Prompt(FS_ModInstallConfirmed, ctx, va(localtext(\"Install %s from\\n%s ?\"), ctx->man->formalname, ctx->url), \"Install\", NULL, \"Cancel\", true);\n\t\t\treturn;\n\t\t}\n\t}\n\n\tFS_ModInstallConfirmed(ctx, PROMPT_CANCEL);\n}\nstatic void FS_ModInstall(const char *dest, const char *url)\n{\n\tstruct dl_download *dl;\n\tchar fmfpath[MAX_OSPATH];\n\n\tftemanifest_t *man = NULL;\n\n\t//find out a writable path for the fmf.\n\tif (!FS_SystemPath(va(\"%s.fmf\", dest), FS_ROOT, fmfpath, sizeof(fmfpath)))\n\t\treturn;\n\n\t//read it in if it exists.\n\tman = FS_Manifest_ReadMod(dest);\n\tif (man)\n\t{\n\t\tFS_ChangeGame(man, cfg_reload_on_gamedir.ival, false);\n\t\treturn;\n\t}\n\n\tdl = HTTP_CL_Get(url, NULL, FS_ModInstallGot);\n\tif (dl)\n\t{\n\t\tmodinstall_t *m = Z_Malloc(sizeof(*m));\n\t\tm->manifestname = Z_StrDup(fmfpath);\n\t\tm->url = Z_StrDup(url);\n\t\tdl->user_ctx = m;\n#ifdef MULTITHREAD\n\t\tDL_CreateThread(dl, NULL, NULL);\n#endif\n\t}\n}\n#else\nstatic void FS_ModInstall(const char *dest, const char *url)\n{\n}\n#endif\n\n\n//switches manifests\n//no args: lists known games\n//1 arg:\n//\t~/quake/\t\t\t\ttrailing slash switches basedir (using said basedir's default manifest)\n//  quake3\t\t\t\t\tloads hardcoded mod\n//\t~/foo.fmf\t\t\t\tloads the specified manifest\n//\thttp://foo/bar.fmf\t\tloads the specified manifest. archaic. doesn't save the fmf anywhere (will download its pk3s)\n//2 args:\n//\tfoo http://foo/bar.fmf\tdownloads to $basedir/foo.fmf if it doesn't exist (prompts), and then always loads it (like 'gamedir', no prompt when it already exists).\nstatic void FS_ChangeGame_f(void)\n{\n\tunsigned int i;\n\tconst char *arg = Cmd_Argv(1);\n\tchar *end;\n\tstruct modlist_s *mod;\n\tftemanifest_t *man;\n\n\t//don't execute this if we're executing rcon commands, as this can change game directories and ruin logging.\n\tif (cmd_blockwait)\n\t\treturn;\n\n\tif ((i = strtol(arg, &end, 10)) && !*end)\n\t{\t//for use by qc. loading mods by number...\n\t\tmod = Mods_GetMod(--i);\n\t\tif (mod)\n\t\t{\n#ifdef HAVE_CLIENT\n\t\t\tCL_Disconnect(NULL);\n#endif\n#ifdef HAVE_SERVER\n\t\t\tif (sv.state)\n\t\t\t\tSV_UnspawnServer();\n#endif\n\t\t\tif (mod->manifest)\n\t\t\t{\n\t\t\t\tman = FS_Manifest_Clone(mod->manifest);\t//FS_ChangeGame takes ownership... don't crash if its cached.\n\t\t\t\tFS_ChangeGame(man, true, true);\n\t\t\t}\n\t\t\telse\n\t\t\t\tCOM_Gamedir(mod->gamedir, NULL);\n#ifdef HAVE_CLIENT\n//\t\t\tCbuf_AddText(\"menu_restart\\n\", RESTRICT_LOCAL);\n#endif\n\t\t}\n\t\treturn;\n\t}\n\telse if (Cmd_Argc()==3)\n\t{\t//allowed to bypass insecurity.\n\t\t//acts like gamedir, but prompts if you try anything else.\n\t\tFS_ModInstall(arg, Cmd_Argv(2));\n\t\treturn;\n\t}\n\telse if (Cmd_IsInsecure())\n\t{\n\t\tCon_Printf(\"Blocking insecure command: %s %s\\n\", Cmd_Argv(0), Cmd_Args());\n\t\treturn;\n\t}\n\telse if (!*arg)\n\t{\n\t\tCon_Printf(\"Valid games/mods are:\\n\");\n\t\tfor (i = 0; (mod = Mods_GetMod(i)); i++)\n\t\t{\n\t\t\tman = mod->manifest;\n\t\t\tif (man)\n\t\t\t\tCon_Printf(\"\\t^[%s\\\\tip\\\\%s\\n%s\\\\type\\\\fs_changegame \\\"%u\\\" //%s^] (%s)\\n\", mod->description?mod->description:man->formalname, man->installation, man->filename?man->filename:\"<INTERNAL>\", i+1, man->filename, man->basedir?man->basedir:\"not installed\");\n\t\t\telse\n\t\t\t\tCon_Printf(\"\\t^[%s\\\\type\\\\gamedir \\\"%s\\\"^]\\n\", mod->description?mod->description:mod->gamedir, mod->gamedir);\n\t\t}\n\t}\n\telse\n\t{\n\t\targ = Z_StrDup(arg);\n#ifdef HAVE_SERVER\n\t\tif (sv.state)\n\t\t\tSV_UnspawnServer();\n#endif\n#ifdef HAVE_CLIENT\n\t\tCL_Disconnect (NULL);\n#endif\n#ifdef PLUGINS\n\t\tPlug_Shutdown(true);\n#endif\n\t\tCvar_GamedirChange();\n\n\t\tif (strrchr(arg, '/') && !strrchr(arg, '/')[1])\n\t\t{\t//ends in slash. a new basedir.\n\t\t\tQ_strncpyz(com_gamepath, arg, sizeof(com_gamepath));\n\t\t\thost_parms.basedir = com_gamepath;\n\t\t\tFS_ChangeGame(FS_ReadDefaultManifest(NULL, 0, true), true, true);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tfor (i = 0; gamemode_info[i].argname; i++)\n\t\t\t{\n\t\t\t\tif (!Q_strcasecmp(gamemode_info[i].argname+1, arg))\n\t\t\t\t{\n\t\t\t\t\tCon_Printf(\"Switching to %s\\n\", gamemode_info[i].argname+1);\n\t\t\t\t\tFS_ChangeGame(FS_GenerateLegacyManifest(i, NULL), true, true);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (!gamemode_info[i].argname)\n\t\t\t{\n#ifdef HAVE_CLIENT\n\t\t\t\tif (!Host_RunFile(arg, strlen(arg), NULL))\n\t\t\t\t\tCon_Printf(\"Game unknown\\n\");\n#endif\n\t\t\t}\n\t\t}\n\t\tZ_Free((char*)arg);\n\n#ifdef PLUGINS\n\t\tPlug_Initialise(true);\n#endif\n#if defined(HAVE_CLIENT) && defined(Q3CLIENT)\n\t\tif (q3)\n\t\t\tq3->ui.Start();\n#endif\n\t}\n}\n\n//this function exists for use by the QI plugin and uses hacked up variations of the default manifest.\nstatic void FS_ChangeMod_f(void)\n{\n\tchar cachename[512];\n\tstruct gamepacks packagespaths[16];\n\tint i;\n\tint packages = 0;\n\tconst char *arg = \"?\";\n\tqboolean okay = false;\n\tchar *dir = NULL;\n\n\tif (Cmd_IsInsecure())\n\t\treturn;\n\n\tZ_Free(fs_loadedcommand);\n\tfs_loadedcommand = NULL;\n\n\tmemset(packagespaths, 0, sizeof(packagespaths));\n\n\tfor (i = 1; ; )\n\t{\n\t\tif (i == Cmd_Argc())\n\t\t{\n\t\t\tokay = true;\n\t\t\tbreak;\n\t\t}\n\t\targ = Cmd_Argv(i++);\n\t\tif (!strcmp(arg, \"package\"))\n\t\t{\n\t\t\targ = Cmd_Argv(i++);\n\t\t\tif (packages == countof(packagespaths))\t//must leave space for one, as a terminator.\n\t\t\t\tcontinue;\n\t\t\tif (FS_PathURLCache(arg, cachename, sizeof(cachename)))\n\t\t\t{\n\t\t\t\tpackagespaths[packages].url = Z_StrDup(arg);\n\t\t\t\tpackagespaths[packages].path = Z_StrDup(cachename);\n\t\t\t\tpackages++;\n\t\t\t}\n\t\t}\n\t\telse if (!strcmp(arg, \"hash\"))\n\t\t{\n\t\t\tif (!packages)\n\t\t\t\tbreak;\n\t\t\targ = Cmd_Argv(i++);\n//\t\t\tpackagespaths[packages-1].hash = Z_StrDup(arg);\n\t\t}\n\t\telse if (!strcmp(arg, \"prefix\"))\n\t\t{\n\t\t\tif (!packages)\n\t\t\t\tbreak;\n\t\t\targ = Cmd_Argv(i++);\n\t\t\tpackagespaths[packages-1].subpath = Z_StrDup(arg);\n\t\t}\n\t\telse if (!strcmp(arg, \"dir\"))\n\t\t{\n\t\t\targ = Cmd_Argv(i++);\n\t\t\tZ_StrDupPtr(&dir, arg);\n\t\t}\n\t\telse if (!strcmp(arg, \"map\"))\n\t\t{\n\t\t\tZ_Free(fs_loadedcommand);\n\t\t\targ = va(\"map \\\"%s\\\"\\n\", Cmd_Argv(i++));\n\t\t\tfs_loadedcommand = Z_StrDup(arg);\n\t\t}\n\t\telse if (!strcmp(arg, \"spmap\"))\n\t\t{\n\t\t\tZ_Free(fs_loadedcommand);\n\t\t\targ = va(\"deathmatch 0;coop 0;spmap \\\"%s\\\"\\n\", Cmd_Argv(i++));\n\t\t\tfs_loadedcommand = Z_StrDup(arg);\n\t\t}\n\t\telse if (!strcmp(arg, \"restart\"))\n\t\t{\n\t\t\tZ_Free(fs_loadedcommand);\n\t\t\tfs_loadedcommand = Z_StrDup(\"restart\\n\");\n\t\t}\n\t\telse\n\t\t\tbreak;\n\t}\n\n\tif (okay)\n\t\tCOM_Gamedir(dir?dir:\"\", packagespaths);\n\telse\n\t{\n\t\tCon_Printf(\"unsupported args: %s\\n\", arg);\n\t\tZ_Free(fs_loadedcommand);\n\t\tfs_loadedcommand = NULL;\n\t}\n\tZ_Free(dir);\n\n\tfor (i = 0; i < packages; i++)\n\t{\n\t\tZ_Free(packagespaths[i].url);\n\t\tZ_Free(packagespaths[i].path);\n\t\tZ_Free(packagespaths[i].subpath);\n\t}\n}\n\nstatic void FS_ShowManifest_f(void)\n{\n\tif (Cmd_IsInsecure())\n\t\treturn;\n\n\tif (fs_manifest)\n\t\tFS_Manifest_Print(fs_manifest);\n\telse\n\t\tCon_Printf(\"no manifest loaded...\\n\");\n}\n\nstatic int QDECL FS_ArbitraryFile_cb (const char *name, qofs_t flags, time_t mtime, void *parm, searchpathfuncs_t *spath)\n{\n\tstruct xcommandargcompletioncb_s *ctx = parm;\n\tctx->cb(name, NULL, NULL, ctx);\n\treturn true;\n}\nvoid FS_ArbitraryFile_c(int argn, const char *partial, struct xcommandargcompletioncb_s *ctx)\n{\n\tif (argn == 1)\n\t{\n\t\tCOM_EnumerateFiles(va(\"%s*\", partial), FS_ArbitraryFile_cb, ctx);\n\t}\n}\n\n//FIXME: this should come from the manifest, as fte_GAME or something\n#ifdef _WIN32\n\t//windows gets formal names...\n\t#ifdef GAME_FULLNAME\n\t\t#define HOMESUBDIR GAME_FULLNAME\n\t#else\n\t\t#define HOMESUBDIR FULLENGINENAME\n\t#endif\n#else\n\t//unix gets short names...\n\t#ifdef GAME_SHORTNAME\n\t\t#define HOMESUBDIR GAME_SHORTNAME\n\t#else\n\t\t#define HOMESUBDIR \"fte\"\n\t#endif\n#endif\n\n#if defined(_WIN32) && !defined(FTE_SDL) && !defined(WINRT) && !defined(_XBOX)\n//so this is kinda screwy\n//\"CSIDL_LOCAL_APPDATA/FTE Quake\" is what we switched to, but we only use it if the other home dirs don't exist\n//\"CSIDL_PERSONAL(My Documents)/My Games/FTE Quake\" is what we used to use... but personal now somehow means upload-to-internet in a nicely insecure we-own-all-your-data kind of way...\n//\"%USERPROFILE%/My Documents/My Games/FTE Quake\" is an attempt to fall back to the earlier lame path if everything else fails\n//\"%USERPROFILE%/Saved Games/FTE Quake\" is what we probably should be using. I don't know who comes up with these random paths. We have updates+downloads+etc as well as just saves, so we prioritise localdata instead (stuff that you do NOT want microsoft to upload to the internet all the time).\n#include <shlobj.h>\nstatic qboolean FS_GetBestHomeDir(ftemanifest_t *manifest)\n{\t//win32 sucks.\n\tqboolean usehome = false;\n\tHRESULT (WINAPI *dSHGetFolderPathW) (HWND hwndOwner, int nFolder, HANDLE hToken, DWORD dwFlags, wchar_t *pszPath) = NULL;\n\tHRESULT (WINAPI *dSHGetKnownFolderPath) (const GUID *const rfid, DWORD dwFlags, HANDLE hToken, PWSTR *ppszPath) = NULL;\n\tdllfunction_t funcs[] =\n\t{\n\t\t{(void**)&dSHGetFolderPathW, \"SHGetFolderPathW\"},\n\t\t{NULL,NULL}\n\t};\n\tdllfunction_t funcsvista[] =\n\t{\n\t\t{(void**)&dSHGetKnownFolderPath, \"SHGetKnownFolderPath\"},\n\t\t{NULL,NULL}\n\t};\n\tDWORD winver = (DWORD)LOBYTE(LOWORD(GetVersion()));\n\n\tenum\n\t{\n\t\tWINHOME_LOCALDATA,\n\t\tWINHOME_SAVEDGAMES,\n\t\tWINHOME_DOCUMENTS,\n\t\tWINHOME_COUNT\n\t};\n\n\tstruct\n\t{\n\t\tchar path[MAX_OSPATH];\n\t} homedir[WINHOME_COUNT];\n\tint i;\n\n\t/*HMODULE shfolder =*/ Sys_LoadLibrary(\"shfolder.dll\", funcs);\n\t/*HMODULE shfolder =*/ Sys_LoadLibrary(\"shell32.dll\", funcsvista);\n\n\tif (dSHGetKnownFolderPath)\n\t{\n\t\twchar_t *wide;\n\t\tstatic const GUID qFOLDERID_SavedGames = {0x4c5c32ff, 0xbb9d, 0x43b0, {0xb5, 0xb4, 0x2d, 0x72, 0xe5, 0x4e, 0xaa, 0xa4}};\n\t\t#define qKF_FLAG_CREATE 0x00008000\n\t\tif (SUCCEEDED(dSHGetKnownFolderPath(&qFOLDERID_SavedGames, qKF_FLAG_CREATE, NULL, &wide)))\n\t\t{\n\t\t\tnarrowen(homedir[WINHOME_SAVEDGAMES].path, sizeof(homedir[WINHOME_SAVEDGAMES].path), wide);\n\t\t\tCoTaskMemFree(wide);\n\t\t}\n\t}\n\n\tif (dSHGetFolderPathW)\n\t{\n\t\twchar_t wfolder[MAX_PATH];\n\t\tif (dSHGetFolderPathW(NULL, 0x5/*CSIDL_PERSONAL*/, NULL, 0, wfolder) == S_OK)\n\t\t{\n\t\t\tnarrowen(homedir[WINHOME_DOCUMENTS].path, sizeof(homedir[WINHOME_DOCUMENTS].path), wfolder);\n\t\t\tQ_strncatz(homedir[WINHOME_DOCUMENTS].path, \"/My Games\", sizeof(homedir[WINHOME_DOCUMENTS].path));\n\t\t}\n\t\t//at some point, microsoft (in their infinitesimal wisdom) decided that 'CSIDL_PERSONAL' should mean 'CSIDL_GIVEITALLTOMICROSOFT'\n\t\t//so use the old/CSIDL_NOTACTUALLYPERSONAL path by default for compat, but if there's nothing there then switch to CSIDL_LOCAL_APPDATA instead\n\t\tif (dSHGetFolderPathW(NULL, 0x1c/*CSIDL_LOCAL_APPDATA*/, NULL, 0, wfolder) == S_OK)\n\t\t{\n\t\t\tnarrowen(homedir[WINHOME_LOCALDATA].path, sizeof(homedir[WINHOME_LOCALDATA].path), wfolder);\n\t\t}\n\t}\n\tif (!*homedir[WINHOME_DOCUMENTS].path)\n\t{\t//guess. sucks for non-english people.\n\t\tchar *ev = getenv(\"USERPROFILE\");\n\t\tif (ev)\n\t\t\tQ_snprintfz(homedir[WINHOME_DOCUMENTS].path, sizeof(homedir[WINHOME_DOCUMENTS].path), \"%s/My Documents/My Games/%s/\", ev, HOMESUBDIR);\n\t}\n//\tif (shfolder)\n//\t\tFreeLibrary(shfolder);\n\n\n\tfor(i = 0; i < countof(homedir); i++)\n\t{\n\t\tchar formal[MAX_OSPATH];\n\t\tchar informal[MAX_OSPATH];\n\t\tif (!*homedir[i].path)\n\t\t\tcontinue; //erk, don't know, not valid/known on this system.\n\t\tif (!manifest || !manifest->formalname || (/*legacy compat hack case*/ !strcmp(manifest->formalname, \"Quake\") && strstr(HOMESUBDIR, \"Quake\")))\n\t\t{\n\t\t\tQ_snprintfz(formal, sizeof(formal), \"%s/%s/\", homedir[i].path, HOMESUBDIR); //'FTE Quake' or something hardcoded.\n\t\t\t*informal = 0;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (\t\tstrchr(manifest->formalname, '(') ||\t//ugly\n\t\t\t\t\t\tstrchr(manifest->formalname, '[') ||\t//ugly\n\t\t\t\t\t\tstrchr(manifest->formalname, '.') ||\t//ugly\n\t\t\t\t\t\tstrchr(manifest->formalname, '\\\"') ||\t//ugly (and invalid)\n\t\t\t\t\t\tstrchr(manifest->formalname, '|') ||\t//invalid\n\t\t\t\t\t\tstrchr(manifest->formalname, '<') ||\t//invalid\n\t\t\t\t\t\tstrchr(manifest->formalname, '>') ||\t//invalid\n\t\t\t\t\t\tstrchr(manifest->formalname, '\\\\') ||\t//long paths\n\t\t\t\t\t\tstrchr(manifest->formalname, '/') ||\t//long paths\n\t\t\t\t\t\tstrchr(manifest->formalname, ':') ||\t//alternative data stream separator\n\t\t\t\t\t\tstrchr(manifest->formalname, '*') ||\t//wildcard\n\t\t\t\t\t\tstrchr(manifest->formalname, '?'))\t\t//wildcard\n\t\t\t\t*formal = 0;\t//don't use filenames with awkward chars...\n\t\t\telse\n\t\t\t\tQ_snprintfz(formal, sizeof(formal), \"%s/%s/\", homedir[i].path, manifest->formalname);\t\t//'Quake' / 'The Wastes' / etc\n\t\t\tQ_snprintfz(informal, sizeof(informal), \"%s/%s/\", homedir[i].path, manifest->installation);\t//'quake' / 'wastes' / etc\n\t\t}\n\t\tif (*formal && GetFileAttributesU(formal)!=INVALID_FILE_ATTRIBUTES) //path exists, use it.\n\t\t{\n\t\t\tQ_strncpyz(com_homepath, formal, sizeof(com_homepath));\n\t\t\tbreak;\n\t\t}\n\t\telse if (*informal && GetFileAttributesU(informal)!=INVALID_FILE_ATTRIBUTES) //path exists, use it.\n\t\t{\n\t\t\tQ_strncpyz(com_homepath, informal, sizeof(com_homepath));\n\t\t\tbreak;\n\t\t}\n\t\telse if (!*com_homepath)\n\t\t{\n\t\t\tif (!*informal)\n\t\t\t\tQ_strncpyz(com_homepath, formal, sizeof(com_homepath));\n\t\t\telse\n\t\t\t\tQ_strncpyz(com_homepath, informal, sizeof(com_homepath));\n\t\t\tcontinue;\t//keep looking, we might still find one that actually exists.\n\t\t}\n\t}\n\n\t/*would it not be better to just check to see if we have write permission to the basedir?*/\n\tif (winver >= 0x6) // Windows Vista and above\n\t\tusehome = true; // always use home directory by default, as Vista+ mimics this behavior anyway\n\telse if (winver >= 0x5) // Windows 2000/XP/2003\n\t\tusehome = true;\t//might as well follow this logic. We use .manifest stuff to avoid getting redirected to obscure locations, so access rights is all that is relevant, not whether we're an admin or not.\n\n\tif (usehome && manifest)\n\t{\n\t\tDWORD homeattr = GetFileAttributesU(com_homepath);\n\t\tDWORD baseattr = GetFileAttributesU(com_gamepath);\n\t\tif (homeattr != INVALID_FILE_ATTRIBUTES && (homeattr & FILE_ATTRIBUTE_DIRECTORY))\n\t\t\treturn true; //okay something else already created it. continue using it.\n\t\tif (baseattr != INVALID_FILE_ATTRIBUTES && (baseattr & FILE_ATTRIBUTE_DIRECTORY))\n\t\t{\t//windows has an _access function, but it doesn't actually bother to check if you're allowed to access it, so its utterly pointless.\n\t\t\t//instead try to append nothing to some file that'll probably exist anyway.\n\t\t\t//this MAY fail if another program has it open. windows sucks.\n\t\t\tvfsfile_t *writetest = VFSOS_Open(\"conhistory.txt\", \"a\");\n\t\t\tif (!writetest)\n\t\t\t\treturn true; //basedir isn't writable, we'll need our home! use it by default.\n\t\t\tVFS_CLOSE(writetest);\n\t\t}\n\t\t//else don't use it (unless -usehome, anyway)\n\t}\n\n\treturn false;\n}\n#elif defined(NOSTDIO)\nstatic qboolean FS_GetBestHomeDir(ftemanifest_t *man)\n{\t//no studio? webgl port? no file system access = no homedirs!\n\treturn false;\n}\n#else\n#include <sys/stat.h>\nstatic qboolean FS_GetBestHomeDir(ftemanifest_t *man)\n{\n\tqboolean usehome = false;\n\n\t//on unix, we use environment settings.\n\t//if $HOME/.fte/ exists then we use that because of legacy reasons.\n\t//but if it doesn't exist then we use $XDG_DATA_HOME/.fte instead\n\t//we used to use $HOME/.#HOMESUBDIR/ but this is now only used if it actually exists AND the new path doesn't.\n\t//new installs use $XDG_DATA_HOME/#HOMESUBDIR/ instead\n\tchar *ev = getenv(\"FTEHOME\");\n\tif (ev && *ev)\n\t{\n\t\tif (ev[strlen(ev)-1] == '/')\n\t\t\tQ_strncpyz(com_homepath, ev, sizeof(com_homepath));\n\t\telse\n\t\t\tQ_snprintfz(com_homepath, sizeof(com_homepath), \"%s/\", ev);\n\t\tusehome = true; // always use home on unix unless told not to\n\t\tev = NULL;\n\t}\n\telse\n\t\tev = getenv(\"HOME\");\n\tif (ev && *ev)\n\t{\n\t\tconst char *xdghome;\n\t\tchar oldhome[MAX_OSPATH];\n\t\tchar newhome[MAX_OSPATH];\n\t\tstruct stat s;\n\t\tchar *installation\t= (man && man->installation && *man->installation)?man->installation:HOMESUBDIR;\n\t\tusehome\t\t\t\t= (man && man->installation && *man->installation)?true:false; //use it if we're running a game, otherwise don't bother if we're still loading.\n\n\t\txdghome = getenv(\"XDG_DATA_HOME\");\n\t\tif (!xdghome || !*xdghome)\n\t\t\txdghome = va(\"%s/.local/share\", ev);\n\t\tif (xdghome[strlen(xdghome)-1] == '/')\n\t\t\tQ_snprintfz(newhome, sizeof(newhome), \"%s%s/\", xdghome, installation);\n\t\telse\n\t\t\tQ_snprintfz(newhome, sizeof(newhome), \"%s/%s/\", xdghome, installation);\n\n\t\tif (ev[strlen(ev)-1] == '/')\n\t\t\tQ_snprintfz(oldhome, sizeof(oldhome), \"%s.%s/\", ev, installation);\n\t\telse\n\t\t\tQ_snprintfz(oldhome, sizeof(oldhome), \"%s/.%s/\", ev, installation);\n\n\t\tif (stat(newhome, &s) == -1 && stat(oldhome, &s) != -1)\n\t\t\tQ_strncpyz(com_homepath, oldhome, sizeof(com_homepath));\n\t\telse\n\t\t\tQ_strncpyz(com_homepath, newhome, sizeof(com_homepath));\n\t}\n\n\tif (usehome && man)\n\t{\n\t\tstruct stat statbuf;\n\t\tif (stat(com_homepath, &statbuf) >= 0 && (statbuf.st_mode & S_IFMT)==S_IFDIR)\n\t\t\treturn true; //okay something else already created it. continue using it.\n\t\tif (access(com_gamepath, W_OK) < 0)\n\t\t\treturn true; //baesdir isn't writable, we'll need our home! use it by default.\n\t\t//else don't use it (unless -usehome, anyway)\n\t}\n\treturn false;\n}\n#endif\nstatic void COM_InitHomedir(ftemanifest_t *man)\n{\n\tint i;\n\n\t//assume the home directory is the working directory.\n\t*com_homepath = '\\0';\n\n\tif (man && (strstr(man->installation, \"..\") || strchr(man->installation, '/') || strchr(man->installation, '\\\\')))\n\t\tcom_homepathusable = false; //don't even try to generate a relative homedir.\n\telse\n\t\tcom_homepathusable = FS_GetBestHomeDir(man);\n\tcom_homepathenabled = false;\n\n\tif (man && man->homedirtype == MANIFEST_NOHOMEDIR)\n\t\tcom_homepathusable = false;\n\telse if (man && man->homedirtype == MANIFEST_FORCEHOMEDIR)\n\t\tcom_homepathusable = true;\n\n\ti = COM_CheckParm(\"-homedir\");\n\tif (i && i+1<com_argc)\n\t{\t//explicitly override the homedir.\n\t\tQ_strncpyz(com_homepath, com_argv[i+1], sizeof(com_homepath));\n\t\tif (*com_homepath && com_homepath[strlen(com_homepath)-1] != '/')\n\t\t\tQ_strncatz(com_homepath, \"/\", sizeof(com_homepath));\n\t\tcom_homepathusable = true;\n\t}\n\tif (COM_CheckParm(\"-usehome\"))\n\t\tcom_homepathusable = true;\n\tif (COM_CheckParm(\"-nohome\"))\n\t\tcom_homepathusable = false;\n\tif (!*com_homepath)\n\t\tcom_homepathusable = false;\n\n\tcom_homepathenabled = com_homepathusable;\n}\n\n#if defined(HAVE_DBUS) && !defined(HAVE_CLIENT)\n#undef HAVE_DBUS\n#endif\n#ifdef HAVE_DBUS\nvoid FS_OpenFilePicker_f(void);\n#endif\n\n/*\n================\nCOM_InitFilesystem\n\nnote: does not actually load any packs, just makes sure the basedir+cvars+etc is set up. vfs_fopens will still fail.\n================\n*/\nvoid COM_InitFilesystem (void)\n{\n\tint\t\ti;\n\n\n\tFS_RegisterDefaultFileSystems();\n\n\tCmd_AddCommand(\"fs_restart\", FS_ReloadPackFiles_f);\n\tCmd_AddCommandD(\"fs_changegame\", FS_ChangeGame_f, \"Switch between different manifests (or registered games)\");\n\tCmd_AddCommandD(\"fs_changemod\", FS_ChangeMod_f, \"Provides the backend functionality of a transient online installer. Eg, for quaddicted's map/mod database.\");\n\tCmd_AddCommand(\"fs_showmanifest\", FS_ShowManifest_f);\n\tCmd_AddCommand (\"fs_flush\", COM_RefreshFSCache_f);\n\tCmd_AddCommandAD(\"dir\", COM_Dir_f,\t\t\tFS_ArbitraryFile_c, \"Displays filesystem listings. Accepts wildcards.\"); //q3 like\n\tCmd_AddCommandAD(\"ls\", COM_Dir_f,\t\t\tFS_ArbitraryFile_c, \"Displays filesystem listings. Accepts wildcards.\"); //q3 like\n\tCmd_AddCommandD(\"path\", COM_Path_f,\t\t\t\"prints a list of current search paths.\");\n\tCmd_AddCommandAD(\"flocate\", COM_Locate_f,\tFS_ArbitraryFile_c, \"Searches for a named file, and displays where it can be found in the OS's filesystem\");\t//prints the pak or whatever where this file can be found.\n\tCmd_AddCommandAD(\"which\", COM_Locate_f,\tFS_ArbitraryFile_c, \"Searches for a named file, and displays where it can be found in the OS's filesystem\");\t//prints the pak or whatever where this file can be found.\n\n\tCmd_AddCommandAD(\"fs_hash\", COM_CalcHash_f,\tFS_ArbitraryFile_c, \"Computes a hash of the specified file.\");\n\n#ifdef HAVE_DBUS\n\tCmd_AddCommandD(\"sys_openfile\", FS_OpenFilePicker_f,\t\"Select a file to open/install/etc.\");\n#endif\n\n//\n// -basedir <path>\n// Overrides the system supplied base directory (under id1)\n//\n\ti = COM_CheckParm (\"-basedir\");\n\tif (i && i < com_argc-1)\n\t\tstrcpy (com_gamepath, com_argv[i+1]);\n\telse\n\t\tstrcpy (com_gamepath, host_parms.basedir);\n\n\tFS_CleanDir(com_gamepath, sizeof(com_gamepath));\n\n\n\tCvar_Register(&cfg_reload_on_gamedir, \"Filesystem\");\n\tCvar_Register(&dpcompat_ignoremodificationtimes, \"Filesystem\");\n\tCvar_Register(&com_fs_cache, \"Filesystem\");\n\tCvar_Register(&fs_hidesyspaths, \"Filesystem\");\n\tCvar_Register(&fs_gamename, \"Filesystem\");\n#ifdef PACKAGEMANAGER\n\tCvar_Register(&pkg_autoupdate, \"Filesystem\");\n#endif\n\tCvar_Register(&com_protocolname, \"Server Info\");\n\tCvar_Register(&com_protocolversion, \"Server Info\");\n\tCvar_Register(&fs_game, \"Filesystem\");\n\tCvar_Register(&fs_gamepath, \"Filesystem\");\n\tCvar_Register(&fs_basepath, \"Filesystem\");\n\tCvar_Register(&fs_homepath, \"Filesystem\");\n\tCvar_Register(&fs_dlURL,\t\"Filesystem\");\n\tCvar_Register(&cl_download_mapsrc,\t\"Filesystem\");\n\n\tCOM_InitHomedir(NULL);\n\n\tfs_readonly = COM_CheckParm(\"-readonly\");\n\tif (COM_CheckParm(\"-allowfileuri\") || COM_CheckParm(\"-allowfileurl\"))\n\t{\n\t\tfs_allowfileuri = (searchpath_t*)Z_Malloc (sizeof(searchpath_t));\n\t\tfs_allowfileuri->handle = VFSOS_OpenPath(NULL, NULL, \"\", \"\", \"\");\n\t}\n\n\tfs_thread_mutex = Sys_CreateMutex();\n}\n\n\n\n\n\n//this is at the bottom of the file to ensure these globals are not used elsewhere\n/*extern searchpathfuncs_t *(QDECL VFSOS_OpenPath) (vfsfile_t *file, searchpathfuncs_t *parent, const char *filename, const char *desc, const char *prefix);\n#if 1//def AVAIL_ZLIB\nextern searchpathfuncs_t *(QDECL FSZIP_LoadArchive) (vfsfile_t *packhandle, searchpathfuncs_t *parent, const char *filename, const char *desc, const char *prefix);\n#endif\nextern searchpathfuncs_t *(QDECL FSPAK_LoadArchive) (vfsfile_t *packhandle, searchpathfuncs_t *parent, const char *filename, const char *desc, const char *prefix);\n#ifdef PACKAGE_DOOMWAD\nextern searchpathfuncs_t *(QDECL FSDWD_LoadArchive) (vfsfile_t *packhandle, searchpathfuncs_t *parent, const char *filename, const char *desc, const char *prefix);\n#endif*/\nvoid FS_RegisterDefaultFileSystems(void)\n{\t//packages listed last will be scanned for last (and thus be favoured when searching for game files)\n#ifdef PACKAGE_DOOMWAD\n\tFS_RegisterFileSystemType(NULL, \"wad\", FSDWD_LoadArchive, true);\n#endif\n#ifdef PACKAGE_DZIP\n\tFS_RegisterFileSystemType(NULL, \"dz\", FSDZ_LoadArchive, false);\n#endif\n#ifdef PACKAGE_Q1PAK\n\tFS_RegisterFileSystemType(NULL, \"pak\", FSPAK_LoadArchive, true);\n#if !defined(_WIN32) && !defined(ANDROID)\n\t/*for systems that have case sensitive paths, also include *.PAK */\n\tFS_RegisterFileSystemType(NULL, \"PAK\", FSPAK_LoadArchive, true);\n#endif\n#endif\n#ifdef PACKAGE_PK3\n\tFS_RegisterFileSystemType(NULL, \"pk3\", FSZIP_LoadArchive, true);\t//quake3's extension for zips\n\tFS_RegisterFileSystemType(NULL, \"pk4\", FSZIP_LoadArchive, true);\t//quake4's extension for zips...\n#ifdef Q2CLIENT\n\tFS_RegisterFileSystemType(NULL, \"pkz\", FSZIP_LoadArchive, true);\t//q2pro uses a different extension\n\tFS_RegisterFileSystemType(NULL, \"pkx\", FSZIP_LoadArchive, true);\t//q2xp naturally uses a different extension too... you'll be glad to know that yq2 uses pk3 instead. yay consistency - every engine uses something different!\n#endif\n\tFS_RegisterFileSystemType(NULL, \"iwd\", FSZIP_LoadArchive, true);\t//cod2's variation.\n\tFS_RegisterFileSystemType(NULL, \"apk\", FSZIP_LoadArchive, false);\t//android package\n\tFS_RegisterFileSystemType(NULL, \"zip\", FSZIP_LoadArchive, false);\t//regular zip file (don't automatically read from these, because it gets messy)\n\tFS_RegisterFileSystemType(NULL, \"kpf\", FSZIP_LoadArchive, true);\t//regular zip file (don't automatically read from these, because it gets messy)\n\tFS_RegisterFileSystemType(NULL, \"exe\", FSZIP_LoadArchive, false);\t//for self-extracting zips.\n\tFS_RegisterFileSystemType(NULL, \"dll\", FSZIP_LoadArchive, false);\t//for plugin metas / self-extracting zips.\n\tFS_RegisterFileSystemType(NULL, \"so\", FSZIP_LoadArchive, false);\t//for plugin metas / self-extracting zips.\n#endif\n\tFS_RegisterFileSystemType(NULL, \"pk3dir\", VFSOS_OpenPath, true);\t//used for git repos or whatever, to make packaging easier\n}\n\n#ifdef HAVE_DBUS\n//I'm adding this code primarily for the flatpak builds. this means cmake only, and only when cmake detects the libs. we can also get away with statically linking the dbus lib too, so no dlopen mess, and its only linked in client builds.\n#include <dbus/dbus.h>\nstruct openfile_s\n{\n\tDBusConnection *conn;\n\tchar *replypath;\n};\nstatic void FS_DBus_Poll(int iarg, void *vctx)\n{\t//kept ticking over via timers so we don't stall completely\n\tstruct openfile_s *ctx = vctx;\n\tDBusMessage *msg;\n\tDBusMessageIter args, opts, dict, var, uris;\n\n\tdbus_connection_read_write(ctx->conn, 0);\t//see if there's anything new, with no timeout so we don't lock up\n\tmsg = dbus_connection_pop_message(ctx->conn);\n\n\tif (msg)\n\t{\t//something came back!\n\t\tif (dbus_message_is_signal(msg, \"org.freedesktop.portal.Request\", \"Response\") &&\n\t\t\t!strcmp(dbus_message_get_path(msg), ctx->replypath))\n\t\t{\t//okay, this is the one we're interested in.\n\t\t\tif (dbus_message_iter_init(msg, &args) && dbus_message_iter_get_arg_type(&args) == DBUS_TYPE_UINT32)\n\t\t\t{\n\t\t\t\t//dbus_message_iter_get_basic(&dict, &response); //0 for success, 1 for user cancel, 2 for error.\n\t\t\t\tif (dbus_message_iter_next(&args) && dbus_message_iter_get_arg_type(&args) == DBUS_TYPE_ARRAY)\n\t\t\t\t{\n\t\t\t\t\tdbus_message_iter_recurse(&args, &opts);\n\t\t\t\t\tfor (; dbus_message_iter_get_arg_type(&opts) == DBUS_TYPE_DICT_ENTRY; dbus_message_iter_next(&opts))\n\t\t\t\t\t{\n\t\t\t\t\t\tdbus_message_iter_recurse(&opts, &dict);\n\t\t\t\t\t\tif (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_STRING)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tconst char *optname;\n\t\t\t\t\t\t\tdbus_message_iter_get_basic(&dict, &optname);\n\t\t\t\t\t\t\tif (dbus_message_iter_next(&dict) && dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_VARIANT)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tdbus_message_iter_recurse(&dict, &var);\n\t\t\t\t\t\t\t\t//if (!strcmp(optname, \"choices\")) //array of string pairs\n\t\t\t\t\t\t\t\t//if (!strcmp(optname, \"currentfilter\"))\n\t\t\t\t\t\t\t\tif (!strcmp(optname, \"uris\") && dbus_message_iter_get_arg_type(&var) == DBUS_TYPE_ARRAY)\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tdbus_message_iter_recurse(&var, &uris);\n\t\t\t\t\t\t\t\t\tfor (; dbus_message_iter_get_arg_type(&uris) == DBUS_TYPE_STRING; dbus_message_iter_next(&uris))\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tchar fullname[MAX_OSPATH];\n\t\t\t\t\t\t\t\t\t\tvfsfile_t *f;\n\t\t\t\t\t\t\t\t\t\tconst char *filename;\n\t\t\t\t\t\t\t\t\t\tdbus_message_iter_get_basic(&uris, &filename);\n\t\t\t\t\t\t\t\t\t\tif (Sys_ResolveFileURL(filename, strlen(filename), fullname, sizeof(fullname)))\n\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\tf = VFSOS_Open(fullname, \"rb\");\n\t\t\t\t\t\t\t\t\t\t\tif (f)\n\t\t\t\t\t\t\t\t\t\t\t{\n//\t\t\t\t\t\t\t\t\t\t\t\tfilename = COM_SkipPath(filename);\n\t\t\t\t\t\t\t\t\t\t\t\tHost_RunFile(filename,strlen(filename), f);\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tdbus_message_unref(msg);\n\n\t\t\tdbus_connection_close(ctx->conn);\n\t\t\tZ_Free((char*)ctx->replypath);\n\t\t\treturn; //can stop requeing now\n\t\t}\n\t\tdbus_message_unref(msg);\n\t}\n\n\tCmd_AddTimer(0.2, FS_DBus_Poll, 0, ctx, sizeof(*ctx));\n}\nvoid FS_OpenFilePicker_f(void)\n{\n\tconst char *parentwindow = \"\";\n\tconst char *title = \"All Files\";\n\tDBusMessage *msg;\n\tDBusMessageIter args, opts;\n\tstruct openfile_s ctx;\n\tDBusError err;\n\tDBusPendingCall *pending;\n\n\t// initialiset the errors\n\tdbus_error_init(&err);\n\n\t// connect to the system bus and check for errors\n\tctx.conn = dbus_bus_get_private(DBUS_BUS_SESSION, &err);\n\tif (dbus_error_is_set(&err))\n\t{\n\t\tfprintf(stderr, \"Connection Error (%s)\\n\", err.message);\n\t\tdbus_error_free(&err);\n\t}\n\tif (!ctx.conn)\n\t\treturn;\n\n\t// request our name on the bus\n\tdbus_bus_register(ctx.conn, &err);\n\tif (dbus_error_is_set(&err))\n\t\tdbus_error_free(&err);\n\n\t// create a new method call and check for errors\n\tmsg = dbus_message_new_method_call(\n\t\t\t\t\"org.freedesktop.portal.Desktop\", // target for the method call\n\t\t\t\t\"/org/freedesktop/portal/desktop\", // object to call on\n\t\t\t\t\"org.freedesktop.portal.FileChooser\", // interface to call on\n\t\t\t\t\"OpenFile\"); // method name\n\tif (msg)\n\t{\n\t\tdbus_message_iter_init_append(msg, &args);\n\t\tdbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &parentwindow);\n\t\tdbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &title);\n\t\tdbus_message_iter_open_container(&args, DBUS_TYPE_ARRAY, \"{sv}\", &opts);\n\t\tdbus_message_iter_close_container(&args, &opts);\n\n\t\t//make sure we get the response (without getting any races)\n\t\tdbus_bus_add_match(ctx.conn, \"type='signal'\"\n\t\t\t\t\t\t\t/*\",path='/org/freedesktop/portal/desktop/request/UNIQUE/TOKEN'\"*/\n\t\t\t\t\t\t\t\",interface='org.freedesktop.portal.Request'\"\n\t\t\t\t\t\t\t\",member='Response'\", &err);\n\n\t\t//now send our request\n\t\tif (!dbus_connection_send_with_reply (ctx.conn, msg, &pending, -1) || !pending)\n\t\t{\n\t\t\tdbus_message_unref(msg);\n\t\t\tmsg = NULL;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tdbus_message_unref(msg);\n\t\t\tdbus_connection_flush(ctx.conn);\n\t\t\tdbus_pending_call_block(pending);\n\n\t\t\t//see if we got a reply\n\t\t\tmsg = dbus_pending_call_steal_reply(pending);\n\t\t\tdbus_pending_call_unref(pending);\n\t\t}\n\t}\n\tif (msg)\n\t{\t//read the object path from the response.\n\t\tif (dbus_message_iter_init(msg, &args) && dbus_message_iter_get_arg_type(&args))\n\t\t{\n\t\t\tdbus_message_iter_get_basic(&args, &ctx.replypath);\n\t\t\tctx.replypath = Z_StrDup(ctx.replypath);\n\t\t}\n\n\t\tdbus_message_unref(msg);\n\t}\n\n\tif (dbus_error_is_set(&err))\n\t\tdbus_error_free(&err);\t//oops?\n\tif (ctx.replypath)\n\t{\t//we sent a request, got a valid response... now we need the actual final result... which may take a while...\n\t\tCmd_AddTimer(0.2, FS_DBus_Poll, 0, &ctx, sizeof(ctx));\n\t\treturn;\n\t}\n\n\tZ_Free(ctx.replypath);\n\tdbus_connection_close(ctx.conn);\n}\n#endif\n"
  },
  {
    "path": "engine/common/fs.h",
    "content": "#include \"hash.h\"\n\n/*\nThreading:\nWhen the main thread will harm the filesystem tree/hash, it will first lock fs_thread_mutex (FIXME: make a proper rwlock).\nWorker threads must thus lock that mutex for any opens (to avoid it changing underneath it), but can unlock it as soon as the open call returns.\nFiles may be shared between threads, but not simultaneously.\nThe filesystem driver is responsible for closing the pak/pk3 once all files are closed, and must ensure that opens+reads+closes as well as archive closure are thread safe.\n*/\n\n#define FSVER 3\n\n\n#define FF_NOTFOUND\t\t(0u)\t//file wasn't found\n#define FF_FOUND\t\t(1u<<0u)\t//file was found\n#define FF_SYMLINK\t\t(1u<<1u)\t//file contents are the name of a different file (symlink). do a recursive lookup on the name\n#define FF_DIRECTORY\t(1u<<2u)\n\ntypedef struct\n{\n\tbucket_t buck;\n\tint depth;\t/*shallower files will remove deeper files*/\n} fsbucket_t;\nextern hashtable_t filesystemhash;\t//this table is the one to build your hash references into\nextern int fs_hash_dups;\t//for tracking efficiency. no functional use.\nextern int fs_hash_files;\t//for tracking efficiency. no functional use.\nextern qboolean fs_readonly;\t//if true, fopen(, \"w\") should always fail.\nextern void *fs_thread_mutex;\nextern float fs_accessed_time;\nextern cvar_t\tfs_dlURL;\n\nstruct searchpath_s;\nstruct searchpathfuncs_s\n{\n\tint\t\t\t\tfsver;\n\tvoid\t\t\t(QDECL *ClosePath)(searchpathfuncs_t *handle);\t\t//removes a reference, kills it when it reaches 0. the package can only actually be killed once all contained files are closed.\n\tvoid\t\t\t(QDECL *AddReference)(searchpathfuncs_t *handle);\t//adds an extra reference, so we survive closes better.\n\n\tvoid\t\t\t(QDECL *GetPathDetails)(searchpathfuncs_t *handle, char *outdetails, size_t sizeofdetails);\n\tvoid\t\t\t(QDECL *BuildHash)(searchpathfuncs_t *handle, int depth, void (QDECL *FS_AddFileHash)(int depth, const char *fname, fsbucket_t *filehandle, void *pathhandle));\n\tunsigned int\t(QDECL *FindFile)(searchpathfuncs_t *handle, flocation_t *loc, const char *name, void *hashedresult);\t//true if found (hashedresult can be NULL)\n\t\t//note that if rawfile and offset are set, many Com_FileOpens will read the raw file\n\t\t//otherwise ReadFile will be called instead.\n\tvoid\t\t\t(QDECL *ReadFile)(searchpathfuncs_t *handle, flocation_t *loc, char *buffer);\t//reads the entire file in one go (size comes from loc, so make sure the loc is valid, this is for performance with compressed archives)\n\tint\t\t\t\t(QDECL *EnumerateFiles)(searchpathfuncs_t *handle, const char *match, int (QDECL *func)(const char *fname, qofs_t fsize, time_t mtime, void *parm, searchpathfuncs_t *spath), void *parm);\n\n\tint\t\t\t\t(QDECL *GeneratePureCRC) (searchpathfuncs_t *handle, const int *seed);\n\n\tvfsfile_t *\t\t(QDECL *OpenVFS)(searchpathfuncs_t *handle, flocation_t *loc, const char *mode);\n\n\tqboolean\t\t(QDECL *PollChanges)(searchpathfuncs_t *handle);\t//returns true if there were changes\n\n\tqboolean\t\t(QDECL *FileStat)(searchpathfuncs_t *handle, flocation_t *loc, time_t *mtime);\n\tqboolean\t\t(QDECL *CreateFile)(searchpathfuncs_t *handle, flocation_t *loc, const char *filename);\t\t//like FindFile, but returns a usable loc even if the file does not exist yet (may also create requisite directories too)\n\tqboolean\t\t(QDECL *RenameFile)(searchpathfuncs_t *handle, const char *oldname, const char *newname);\t//returns true on success, false if source doesn't exist, or if dest does (cached locs may refer to either new or old name).\n\tqboolean\t\t(QDECL *RemoveFile)(searchpathfuncs_t *handle, const char *filename);\t//returns true on success, false if it wasn't found or is readonly.\n};\n//searchpathfuncs_t *(QDECL *OpenNew)(vfsfile_t *file, const char *desc);\t//returns a handle to a new pak/path\n\n//the stdio filesystem is special as that's the starting point of the entire filesystem\n//warning: the handle is known to be a string pointer to the dir name\nextern searchpathfuncs_t *(QDECL VFSOS_OpenPath)\t(vfsfile_t *file, searchpathfuncs_t *parent, const char *filename, const char *desc, const char *prefix);\nextern searchpathfuncs_t *(QDECL FSZIP_LoadArchive) (vfsfile_t *file, searchpathfuncs_t *parent, const char *filename, const char *desc, const char *prefix);\nextern searchpathfuncs_t *(QDECL FSPAK_LoadArchive) (vfsfile_t *file, searchpathfuncs_t *parent, const char *filename, const char *desc, const char *prefix);\nextern searchpathfuncs_t *(QDECL FSDWD_LoadArchive) (vfsfile_t *file, searchpathfuncs_t *parent, const char *filename, const char *desc, const char *prefix);\nextern searchpathfuncs_t *(QDECL FSDZ_LoadArchive)\t(vfsfile_t *file, searchpathfuncs_t *parent, const char *filename, const char *desc, const char *prefix);\nvfsfile_t *QDECL VFSOS_Open(const char *osname, const char *mode);\nvfsfile_t *FS_DecompressGZip(vfsfile_t *infile, vfsfile_t *outfile);\n\nint FS_RegisterFileSystemType(void *module, const char *extension, searchpathfuncs_t *(QDECL *OpenNew)(vfsfile_t *file, searchpathfuncs_t *parent, const char *filename, const char *desc, const char *prefix), qboolean loadscan);\nvoid FS_UnRegisterFileSystemType(int idx);\nvoid FS_UnRegisterFileSystemModule(void *module);\n\nvoid FS_AddHashedPackage(searchpath_t **oldpaths, const char *parent_pure, const char *parent_logical, searchpath_t *search, unsigned int loadstuff, const char *pakpath, const char *qhash, const char *pakprefix, unsigned int packageflags);\nvoid PM_LoadPackages(searchpath_t **oldpaths, const char *parent_pure, const char *parent_logical, searchpath_t *search, unsigned int loadstuff, int minpri, int maxpri);\nqboolean PM_HandleRedirect(const char *package, char *url, size_t urlsize);\nvoid PM_ManifestChanged(ftemanifest_t *man);\nvoid *PM_GeneratePackageFromMeta(vfsfile_t *file, char *fname, size_t fnamesize, enum fs_relative *fsroot);\nvoid PM_FileInstalled(const char *filename, enum fs_relative fsroot, void *metainfo, qboolean enable); //we finished installing a file via some other mechanism (drag+drop or from server. insert it into the updates menu.\nvoid PM_EnumeratePlugins(void (*callback)(const char *name, qboolean blocked));\nstruct xcommandargcompletioncb_s;\nvoid PM_EnumerateMaps(const char *partial, struct xcommandargcompletioncb_s *ctx);\nvoid PM_LoadMap(const char *package, const char *map);\nunsigned int PM_IsApplying(void);\nunsigned int PM_MarkUpdates (void);\t//mark new/updated packages as needing install.\nvoid PM_ApplyChanges(void);\t//for -install/-doinstall args\nqboolean PM_AreSourcesNew(qboolean doprompt);\nqboolean PM_FindUpdatedEngine(char *syspath, size_t syspathsize);\t//names the engine we should be running\nvoid PM_AddManifestPackages(ftemanifest_t *man, qboolean mayapply);\nvoid Menu_Download_Update(void);\n\ntypedef struct\n{\n\tchar *description;\n\tvoid (*Update)\t\t\t(const char *url, vfsfile_t *out, qboolean favourcache);\n#define plugupdatesourcefuncs_name \"UpdateSource\"\n} plugupdatesourcefuncs_t;\nqboolean PM_RegisterUpdateSource(void *module, plugupdatesourcefuncs_t *funcs);\n\nenum modsourcetype_e\n{\n\tMST_SYSTEM,\t\t//found via an fmf installed at system level (eg part of a flatpak app)\n\tMST_DEFAULT,\t//the default.fmf in the working directory\n\tMST_BASEDIR,\t//other fmf files in the basedir\n\tMST_HOMEDIR,\t//other fmf files in the homedir\n\tMST_GAMEDIR,\t//mod found from just looking for gamedirs.\n\tMST_INTRINSIC,\t//knowledge of the mod came one of the games we're hardcoded with.\n\n\tMST_UNKNOWN,\t//forgot where it came from...\n};\nint FS_EnumerateKnownGames(qboolean (*callback)(void *usr, ftemanifest_t *man, enum modsourcetype_e sourcetype), void *usr, qboolean fixedbasedir);\n\nstruct modlist_s\n{\n\tftemanifest_t *manifest;\n\tenum modsourcetype_e sourcetype;\n\tchar *gamedir;\n\tchar *description;\n};\nstruct modlist_s *Mods_GetMod(size_t diridx);\n\n#define SPF_REFERENCED\t\t1\t\t//something has been loaded from this path. should filter out client references...\n#define SPF_COPYPROTECTED\t2\t\t//downloads are not allowed fom here.\n#define SPF_TEMPORARY\t\t4\t\t//a map-specific path, purged at map change.\n#define SPF_EXPLICIT\t\t8\t\t//a root gamedir (bumps depth on gamedir depth checks).\n#define SPF_UNTRUSTED\t\t16\t\t//has been downloaded from somewhere. configs inside it should never be execed with local access rights.\n#define SPF_PRIVATE\t\t\t32\t\t//private to the client. ie: the fte dir. name is not networked.\n#define SPF_WRITABLE\t\t64\t\t//safe to write here. lots of weird rules etc.\n#define SPF_BASEPATH\t\t128\t\t//part of the basegames, and not the mod gamedir(s).\n#define SPF_QSHACK\t\t\t256\t\t//a bit of a hack, allows scanning for $rootdir/quakespasm.pak\n#define SPF_SERVER\t\t\t512\t\t//a package that was loaded to match the server's packages\n#define SPF_ISDIR\t\t\t1024\t//is an actual directory (not itself a package).\n#define SPF_VIRTUAL\t\t\t2048\t//path is virtual in some form, and the logicalpath is NOT a path..\nqboolean FS_LoadPackageFromFile(vfsfile_t *vfs, char *pname, char *localname, int *crc, unsigned int flags);\n\n#ifdef AVAIL_XZDEC\nvfsfile_t *FS_XZ_DecompressWriteFilter(vfsfile_t *infile);\nvfsfile_t *FS_XZ_DecompressReadFilter(vfsfile_t *srcfile);\n#endif\n#ifdef AVAIL_GZDEC\nvfsfile_t *FS_GZ_WriteFilter(vfsfile_t *outfile, qboolean autoclosefile, qboolean compress);\n#endif\n"
  },
  {
    "path": "engine/common/fs_dzip.c",
    "content": "/*\r\nCopyright notice from dzip (zlib license):\r\n\r\n (C) 2000-2002 Stefan Schwoon, Nolan Pflug\r\n\r\n  This software is provided 'as-is', without any express or implied\r\n  warranty. In no event will the authors be held liable for any damages\r\n  arising from the use of this software.\r\n\r\n  Permission is granted to anyone to use this software for any purpose,\r\n  including commercial applications, and to alter it and redistribute it\r\n  freely, subject to the following restrictions:\r\n\r\n  1. The origin of this software must not be misrepresented; you must not\r\n     claim that you wrote the original software. If you use this software\r\n     in a product, an acknowledgment in the product documentation would be\r\n     appreciated.\r\n  2. Altered source versions must be plainly marked as such, and must not be\r\n     misrepresented as being the original software.\r\n  3. This notice may not be removed or altered from any source distribution.\r\n\r\n  Stefan Schwoon\tNolan Pflug\r\n  schwoon@in.tum.de\tradix@planetquake.com\r\n*/\r\n/*\r\nAdditionally, portions of this code are copy-righted by id software and provided under the gpl v2 or later.\r\n*/\r\n\r\n/*\r\ndzip has two primary components:\r\nthe first/outer part is that it is some alternative to a .zip, one that stores paks weirdly...\r\nthe second part is some entropy differences that just allows zlib to work more effectively, or something.\r\n\r\nI've rewritten the archive/outer part to plug it in to fte more nicely\r\nthe demo/inner part should mostly be the same as dzip, just with some minor tweaks to make it thread-safe (the 'dc' pointer stuff, in case that's ever an issue).\r\nI have explicitly changed longs to ints, to ensure there's no issues with 64bit builds\r\n*/\r\n\r\n#include \"quakedef.h\"\r\n#include \"fs.h\"\r\n\r\n#ifdef PACKAGE_DZIP\r\n\r\n//handle to a file's metadata\r\ntypedef struct\r\n{\r\n\tfsbucket_t bucket;\r\n\r\n\tchar\tname[MAX_QPATH];\r\n\tqofs_t\tfilepos;\r\n\tsize_t\tfilelen;\t//aka: memory size\r\n\tsize_t\tisize;\r\n\tsize_t\tcsize;\r\n\tunsigned int ztype;\r\n\ttime_t mtime;\r\n\tunsigned int subfiles;\r\n\r\n\t//FIXME: flag files as in paks or whatever.\r\n} mdzfile_t;\r\n\r\n//handle to the archive itself.\r\ntypedef struct\r\n{\r\n\tsearchpathfuncs_t pub;\r\n\tchar\tdescname[MAX_OSPATH];\r\n\tint\t\tnumfiles;\r\n\tmdzfile_t\t*files;\r\n\r\n\tint major_version;\r\n\r\n\tvoid\t\t*mutex;\r\n\tvfsfile_t\t*handle;\r\n\tunsigned int filepos;\t//the pos the subfiles left it at (to optimize calls to vfs_seek)\r\n\tint references;\t//seeing as all vfiles from a pak file use the parent's vfsfile, we need to keep the parent open until all subfiles are closed.\r\n} dzarchive_t;\r\n\r\n//a file that's actively being read\r\ntypedef struct {\r\n\tvfsfile_t funcs;\r\n\tsize_t length;\r\n\tsize_t currentpos;\r\n\r\n\tqbyte data[1];\r\n} vfsdz_t;\r\n\r\n//\r\n// on disk\r\n//\r\ntypedef struct\r\n{\r\n\t//v1\r\n\tunsigned int offset;\r\n\tunsigned int size;\r\n\tunsigned int realsize;\r\n\tunsigned short namelen;\r\n\tunsigned short pak;\r\n\tunsigned int crc;\r\n\tunsigned int type;\r\n\r\n\t//only in v2\r\n\tunsigned int date;\r\n\tunsigned int intersize;\r\n\r\n\t//name follows for namelen bytes\r\n} dpackfile_t;\r\n\r\ntypedef struct\r\n{\r\n\tchar\tid[2];\t//DZ\r\n\tunsigned char major_ver;\t//2\r\n\tunsigned char minor_ver;\t//9\r\n\tint\t\tdirofs;\r\n\tint\t\tdirlen;\r\n} dpackheader_t;\r\n\r\n\r\n//defs copied from dzip.h\r\nenum { TYPE_NORMAL, TYPE_DEMV1, TYPE_TXT, TYPE_PAK, TYPE_DZ, TYPE_DEM, TYPE_NEHAHRA, TYPE_DIR, TYPE_STORE };\r\n\r\n//stuff to decode\r\nenum {\r\n\tDEM_bad, DEM_nop, DEM_disconnect, DEM_updatestat, DEM_version,\r\n\tDEM_setview, DEM_sound, DEM_time, DEM_print, DEM_stufftext,\r\n\tDEM_setangle, DEM_serverinfo, DEM_lightstyle, DEM_updatename,\r\n\tDEM_updatefrags, DEM_clientdata, DEM_stopsound, DEM_updatecolors,\r\n\tDEM_particle, DEM_damage, DEM_spawnstatic, DEM_spawnbinary,\r\n\tDEM_spawnbaseline, DEM_temp_entity, DEM_setpause, DEM_signonnum,\r\n\tDEM_centerprint, DEM_killedmonster, DEM_foundsecret,\r\n\tDEM_spawnstaticsound, DEM_intermission, DEM_finale,\r\n\tDEM_cdtrack, DEM_sellscreen, DEM_cutscene, DZ_longtime,\r\n/* nehahra */\r\n\tDEM_showlmp = 35, DEM_hidelmp, DEM_skybox, DZ_showlmp\r\n};\r\n\r\n//basic types\r\ntypedef unsigned int uInt;\t//gah!\r\ntypedef qbyte uchar;\r\n\r\ntypedef struct {\r\n\tuchar voz, pax;\r\n\tuchar ang0, ang1, ang2;\r\n\tuchar vel0, vel1, vel2;\r\n\tint items;\r\n\tuchar uk10, uk11, invbit;\r\n\tuchar wpf, av, wpm;\r\n\tint health;\r\n\tuchar am, sh, nl, rk, ce, wp;\r\n\tint force;\r\n} cdata_t;\r\ntypedef struct {\r\n\tuchar modelindex, frame;\r\n\tuchar colormap, skin;\r\n\tuchar effects;\r\n\tuchar ang0, ang1, ang2;\r\n\tuchar newbit, present, active;\r\n\tuchar fullbright;\t/* nehahra */\r\n\tint org0, org1, org2;\r\n\tint od0, od1, od2;\r\n\tint force;\r\n\tfloat alpha;\t\t/* nehahra */\r\n} ent_t;\r\n\r\n//the 'globals'\r\ntypedef struct\r\n{\r\n\tint dem_decode_type;\r\n\tqbyte *out;\r\n\tqbyte *outend;\r\n\r\n#define MAX_ENT 1024\r\n#define p_blocksize 32768\r\n\r\n\tuchar dem_updateframe;\r\n\tuchar copybaseline;\r\n\tint maxent, lastent, sble;\r\n\tint entlink[MAX_ENT];\r\n\tint dem_gametime;\r\n\tint outlen;\r\n\tint cam0, cam1, cam2;\r\n\tuchar inblk[p_blocksize], outblk[p_blocksize], *inptr;\r\n\tcdata_t oldcd, newcd;\r\n\tent_t base[MAX_ENT], oldent[MAX_ENT], newent[MAX_ENT];\r\n\r\n} decodectx_t;\r\n\r\nstatic void Outfile_Write(decodectx_t *dc, void *outblk, size_t outlen)\r\n{\t//throw the data at the file\r\n\tif (dc->out + outlen <= dc->outend)\r\n\t{\r\n\t\tmemcpy(dc->out, outblk, outlen);\r\n\t\tdc->out += outlen;\r\n\t}\r\n}\r\n\r\nstatic void copy_msg(decodectx_t *dc, size_t bytes)\r\n{\t//just copy the data over\r\n\tmemcpy(dc->outblk+dc->outlen, dc->inptr, bytes);\r\n\tdc->outlen += bytes;\r\n\tdc->inptr += bytes;\r\n}\r\nstatic void insert_msg (decodectx_t *dc, void *data, size_t bytes)\r\n{\r\n\tmemcpy(dc->outblk+dc->outlen, data, bytes);\r\n\tdc->outlen += bytes;\r\n}\r\nstatic void discard_msg (decodectx_t *dc, size_t bytes)\r\n{\r\n\tdc->inptr += bytes;\r\n}\r\n\r\n#define Outfile_Write(d,b) Outfile_Write(dc,d,b)\r\n#define copy_msg(b)\t\tcopy_msg(dc,b)\r\n#define insert_msg(d,b)\tinsert_msg(dc,d,b)\r\n#define discard_msg(b)\tdiscard_msg(dc,b)\r\n#define dem_decode_type dc->dem_decode_type\r\n#define copybaseline\tdc->copybaseline\r\n#define maxent\t\t\tdc->maxent\r\n#define lastent\t\t\tdc->lastent\r\n#define sble\t\t\tdc->sble\r\n#define entlink\t\t\tdc->entlink\r\n#define dem_gametime\tdc->dem_gametime\r\n#define outlen\t\t\tdc->outlen\r\n#define cam0\t\t\tdc->cam0\r\n#define cam1\t\t\tdc->cam1\r\n#define cam2\t\t\tdc->cam2\r\n#define inblk\t\t\tdc->inblk\r\n#define outblk\t\t\tdc->outblk\r\n#define inptr\t\t\tdc->inptr\r\n#define oldcd\t\t\tdc->oldcd\r\n#define newcd\t\t\tdc->newcd\r\n#define base\t\t\tdc->base\r\n#define oldent\t\t\tdc->oldent\r\n#define newent\t\t\tdc->newent\r\n#define dem_updateframe\tdc->dem_updateframe\r\n\r\n#define GUI\t//because it disables v1\r\n\r\n#define getshort(x) LittleShort(*(short*)(x))\r\n#define getlong(x) LittleLong(*(int*)(x))\r\n#define getfloat(x) LittleFloat(*(float*)(x))\r\n#define cnvlong(x) LittleLong(x)\r\n\r\nvoid dem_copy_ue(decodectx_t *dc)\r\n{\r\n\tuchar mask = inptr[0] & 0x7f;\r\n\tuchar topmask;\r\n\tint len = 1;\r\n\r\n\ttopmask = (mask & 0x01)? inptr[len++] : 0x00;\r\n\tif (topmask & 0x40) len += 2; else len++;\r\n\tif (topmask & 0x04) len++;\r\n\tif (mask & 0x40) len++;\r\n\tif (topmask & 0x08) len++;\r\n\tif (topmask & 0x10) len++;\r\n\tif (topmask & 0x20) len++;\r\n\tif (mask & 0x02) len += 2;\r\n\tif (topmask & 0x01) len++;\r\n\tif (mask & 0x04) len += 2;\r\n\tif (mask & 0x10) len++;\r\n\tif (mask & 0x08) len += 2;\r\n\tif (topmask & 0x02) len++;\r\n//\tif (topmask & 0x80) /* this should be a bailout */\r\n//\t\terror(\"dem_copy_ue(): topmask & 0x80\");\r\n\tcopy_msg(len);\r\n}\r\nconst uchar te_size[] = {8, 8,  8,  8, 8, 16, 16, 8, 8, 16,\r\n\t\t\t\t  8, 8, 10, 16, 8,  8, 14};\r\n\r\n/////////////////////////////////////////////////////////////////////////\r\n//Start decode.c\r\n\r\nvoid demx_nop(decodectx_t *dc)\r\n{\r\n\tcopy_msg(1);\r\n}\r\n\r\nvoid demx_disconnect(decodectx_t *dc)\r\n{\r\n\tcopy_msg(1);\r\n}\r\n\r\nvoid demx_updatestat(decodectx_t *dc)\r\n{\r\n\tcopy_msg(6);\r\n}\r\n\r\nvoid demx_version(decodectx_t *dc)\r\n{\r\n\tcopy_msg(5);\r\n}\r\n\r\nvoid demx_setview(decodectx_t *dc)\r\n{\r\n\tcopy_msg(3);\r\n}\r\n\r\nvoid demx_sound(decodectx_t *dc)\r\n{\r\n\tint c, len;\r\n\tuInt entity;\r\n\tuchar mask = inptr[1];\r\n\tuchar channel;\r\n\r\n\tif (*inptr > DEM_sound)\r\n\t{\r\n\t\tlen = 10;\r\n\t\tmask = *inptr & 3;\r\n\t}\r\n\telse\r\n\t\tlen = 11;\r\n\tif (mask & 0x01) len++;\r\n\tif (mask & 0x02) len++;\r\n\r\n#ifndef GUI\r\n\tif (dem_decode_type == TYPE_DEMV1) { copy_msg(len); return; }\r\n#endif\r\n\t*inptr = DEM_sound;\r\n\tinsert_msg(inptr,1);\r\n\r\n\t*inptr = mask;\r\n\tchannel = inptr[len-9] & 7;\r\n\tinptr[len-9] = (inptr[len-9] & 0xf8) + ((2 - channel) & 7);\r\n\r\n\tif ((entity = getshort(inptr+len-9) >> 3) < MAX_ENT)\r\n\t{\r\n\t\tc = getshort(inptr+len-6); c += newent[entity].org0;\r\n\t\tc = cnvlong(c); memcpy(inptr+len-6,&c,2);\r\n\t\tc = getshort(inptr+len-4); c += newent[entity].org1;\r\n\t\tc = cnvlong(c); memcpy(inptr+len-4,&c,2);\r\n\t\tc = getshort(inptr+len-2); c += newent[entity].org2;\r\n\t\tc = cnvlong(c); memcpy(inptr+len-2,&c,2);\r\n\t}\r\n\r\n\tcopy_msg(len);\r\n}\r\n\r\nvoid demx_longtime(decodectx_t *dc)\r\n{\r\n\tint tmp = getlong(inptr+1);\r\n\tdem_gametime += tmp;\r\n\ttmp = cnvlong(dem_gametime);\r\n\t*inptr = DEM_time;\r\n\tmemcpy(inptr+1,&tmp,4);\r\n\tcopy_msg(5);\r\n}\r\n\r\nvoid demx_time(decodectx_t *dc)\r\n{\r\n\tuchar buf[5];\r\n\tint tmp = getshort(inptr+1) & 0xffff;\r\n\r\n#ifndef GUI\r\n\tif (dem_decode_type == TYPE_DEMV1) { demx_longtime(); return; }\r\n#endif\r\n\tdem_gametime += tmp;\r\n\ttmp = cnvlong(dem_gametime);\r\n\tbuf[0] = DEM_time;\r\n\tmemcpy(buf+1,&tmp,4);\r\n\tinsert_msg(buf,5);\r\n\tdiscard_msg(3);\r\n}\r\n\r\n/* used by lots of msgs */\r\nvoid demx_string(decodectx_t *dc)\r\n{\r\n\tuchar *ptr = inptr + 1;\r\n\twhile (*ptr++);\r\n\tcopy_msg(ptr-inptr);\r\n}\r\n\r\nvoid demx_setangle(decodectx_t *dc)\r\n{\r\n\tcopy_msg(4);\r\n}\r\n\r\nvoid demx_serverinfo(decodectx_t *dc)\r\n{\r\n\tuchar *ptr = inptr + 7;\r\n\tuchar *start_ptr;\r\n\twhile (*ptr++);\r\n\tdo {\r\n\t\tstart_ptr = ptr;\r\n\t\twhile (*ptr++);\r\n\t} while (ptr - start_ptr > 1);\r\n\tdo {\r\n\t\tstart_ptr = ptr;\r\n\t\twhile (*ptr++);\r\n\t} while (ptr - start_ptr > 1);\r\n\tcopy_msg(ptr-inptr);\r\n\tsble = 0;\r\n}\r\n\r\nvoid demx_lightstyle(decodectx_t *dc)\r\n{\r\n\tuchar *ptr = inptr + 2;\r\n\twhile (*ptr++);\r\n\tcopy_msg(ptr-inptr);\r\n}\r\n\r\nvoid demx_updatename(decodectx_t *dc)\r\n{\r\n\tuchar *ptr = inptr + 2;\r\n\twhile (*ptr++);\r\n\tcopy_msg(ptr-inptr);\r\n}\r\n\r\nvoid demx_updatefrags(decodectx_t *dc)\r\n{\r\n\tcopy_msg(4);\r\n}\r\n\r\nstatic int bplus(int x, int y)\r\n{\r\n\tif (x >= 128) x -= 256;\r\n\treturn y + x;\r\n}\r\n\r\nvoid create_clientdata_msg(decodectx_t *dc)\r\n{\r\n\tuchar buf[32];\r\n\tuchar *ptr = buf+3;\r\n\tint mask = newcd.invbit? 0 : 0x0200;\r\n\tint tmp;\r\n\r\n\tbuf[0] = DEM_clientdata;\r\n\r\n\t#define CMADD(x,def,bit,bit2) if (newcd.x != def || newcd.force & bit2)\\\r\n\t\t{ mask |= bit; *ptr++ = newcd.x; }\r\n\r\n\tCMADD(voz,22,0x0001,0x0800);\r\n\tCMADD(pax,0,0x0002,0x1000);\r\n\tCMADD(ang0,0,0x0004,0x0100);\r\n\tCMADD(vel0,0,0x0020,0x0001);\r\n\tCMADD(ang1,0,0x0008,0x0200);\r\n\tCMADD(vel1,0,0x0040,0x0002);\r\n\tCMADD(ang2,0,0x0010,0x0400);\r\n\tCMADD(vel2,0,0x0080,0x0004);\r\n\ttmp = cnvlong(newcd.items); memcpy(ptr,&tmp,4); ptr += 4;\r\n\tif (newcd.uk10) mask |= 0x0400;\r\n\tif (newcd.uk11) mask |= 0x0800;\r\n\tCMADD(wpf,0,0x1000,0x2000);\r\n\tCMADD(av,0,0x2000,0x4000);\r\n\tCMADD(wpm,0,0x4000,0x8000);\r\n\ttmp = cnvlong(newcd.health); memcpy(ptr,&tmp,2); ptr += 2;\r\n\t*ptr++ = newcd.am;\r\n\t*ptr++ = newcd.sh;\r\n\t*ptr++ = newcd.nl;\r\n\t*ptr++ = newcd.rk;\r\n\t*ptr++ = newcd.ce;\r\n\t*ptr++ = newcd.wp;\r\n\tmask = cnvlong(mask);\r\n\tmemcpy(buf+1,&mask,2);\r\n\tinsert_msg(buf,ptr-buf);\r\n\r\n\toldcd = newcd;\r\n}\r\n\r\n#define CPLUS(x,bit) if (mask & bit) { newcd.x = bplus(*ptr++,oldcd.x); }\r\n\r\nvoid demx_clientdata(decodectx_t *dc)\r\n{\r\n\tuchar *ptr = inptr;\r\n\tint mask = *ptr++;\r\n\r\n\tnewcd = oldcd;\r\n\r\n#ifndef GUI\r\n\tif (dem_decode_type == TYPE_DEMV1) { demv1_clientdata(); return; }\r\n#endif\r\n\tif (mask & 0x08) mask += *ptr++ << 8;\r\n\tif (mask & 0x8000) mask += *ptr++ << 16;\r\n\tif (mask & 0x800000) mask += *ptr++ << 24;\r\n\r\n\tCPLUS(vel2,0x00000001);\r\n\tCPLUS(vel0,0x00000002);\r\n\tCPLUS(vel1,0x00000004);\r\n\r\n\tCPLUS(wpf,0x00000100);\r\n\tif (mask & 0x00000200) newcd.uk10 = !oldcd.uk10;\r\n\tCPLUS(ang0,0x00000400);\r\n\tCPLUS(am,0x00000800);\r\n\tif (mask & 0x00001000) { newcd.health += getshort(ptr); ptr += 2; }\r\n\tif (mask & 0x00002000) { newcd.items ^= getlong(ptr); ptr += 4; }\r\n\tCPLUS(av,0x00004000);\r\n\r\n\tCPLUS(pax,0x00010000);\r\n\tCPLUS(sh,0x00020000);\r\n\tCPLUS(nl,0x00040000);\r\n\tCPLUS(rk,0x00080000);\r\n\tCPLUS(wpm,0x00100000);\r\n\tCPLUS(wp,0x00200000);\r\n\tif (mask & 0x00400000) newcd.uk11 = !oldcd.uk11;\r\n\r\n\tCPLUS(voz,0x01000000);\r\n\tCPLUS(ce,0x02000000);\r\n\tCPLUS(ang1,0x04000000);\r\n\tCPLUS(ang2,0x08000000);\r\n\tif (mask & 0x10000000) newcd.invbit = !oldcd.invbit;\r\n\r\n\tdiscard_msg(ptr-inptr);\r\n\r\n\tif ((*ptr & 0xf0) == 0x50)\r\n\t{\r\n\t\tmask = *ptr++;\r\n\t\tif (mask & 0x08) mask |= *ptr++ << 8;\r\n\t\tnewcd.force ^= mask & 0xff07;\r\n\t\tdiscard_msg(ptr-inptr);\r\n\t}\r\n\r\n\tcreate_clientdata_msg(dc);\r\n}\r\n\r\nvoid demx_stopsound(decodectx_t *dc)\r\n{\r\n\tcopy_msg(3);\r\n}\r\n\r\nvoid demx_updatecolors(decodectx_t *dc)\r\n{\r\n\tcopy_msg(3);\r\n}\r\n\r\nvoid demx_particle(decodectx_t *dc)\r\n{\r\n\tcopy_msg(12);\r\n}\r\n\r\nvoid demx_damage(decodectx_t *dc)\r\n{\r\n\tcopy_msg(9);\r\n}\r\n\r\nvoid demx_spawnstatic(decodectx_t *dc)\r\n{\r\n\tcopy_msg(14);\r\n}\r\n\r\nvoid demx_spawnbinary(decodectx_t *dc)\r\n{\r\n\tcopy_msg(1);\r\n}\r\n\r\nvoid demx_spawnbaseline(decodectx_t *dc)\r\n{\r\n\tuchar buf[16], *ptr = inptr+3;\r\n\tent_t ent;\r\n\tint mask = getshort(inptr+1);\r\n\t\r\n\tsble = (sble + (mask & 0x03ff)) % 0x400;\r\n\tmemset(&ent,0,sizeof(ent_t));\r\n\tent.modelindex = *ptr++;\r\n\tif (mask & 0x0400) ent.frame = *ptr++;\r\n\tif (mask & 0x0800) ent.colormap = *ptr++;\r\n\tif (mask & 0x1000) ent.skin = *ptr++;\r\n\tif (mask & 0x2000)\r\n\t{\r\n\t\tent.org0 = getshort(ptr); ptr += 2;\r\n\t\tent.org1 = getshort(ptr); ptr += 2;\r\n\t\tent.org2 = getshort(ptr); ptr += 2;\r\n\t}\r\n\tif (mask & 0x4000) ent.ang1 = *ptr++;\r\n\tif (mask & 0x8000) { ent.ang0 = *ptr++; ent.ang2 = *ptr++; }\r\n\tdiscard_msg(ptr-inptr);\r\n\r\n\tbuf[0] = DEM_spawnbaseline;\r\n\tmask = cnvlong(sble); memcpy(buf+1,&mask,2);\r\n\tbuf[3] = ent.modelindex;\r\n\tbuf[4] = ent.frame;\r\n\tbuf[5] = ent.colormap;\r\n\tbuf[6] = ent.skin;\r\n\tmask = cnvlong(ent.org0); memcpy(buf+7,&mask,2);\r\n\tbuf[9] = ent.ang0;\r\n\tmask = cnvlong(ent.org1); memcpy(buf+10,&mask,2);\r\n\tbuf[12] = ent.ang1;\r\n\tmask = cnvlong(ent.org2); memcpy(buf+13,&mask,2);\r\n\tbuf[15] = ent.ang2;\r\n\tinsert_msg(buf,16);\r\n\r\n\tbase[sble] = ent;\r\n\tcopybaseline = 1;\r\n}\r\n\r\nvoid demx_temp_entity(decodectx_t *dc)\r\n{\r\n\tif (inptr[1] == 17)\r\n\t\tcopy_msg(strlen(inptr + 2) + 17);\r\n\telse\r\n\t\tcopy_msg(te_size[inptr[1]]);\r\n}\r\n\r\nvoid demx_setpause(decodectx_t *dc)\r\n{\r\n\tcopy_msg(2);\r\n}\r\n\r\nvoid demx_signonnum(decodectx_t *dc)\r\n{\r\n\tcopy_msg(2);\r\n}\r\n\r\nvoid demx_killedmonster(decodectx_t *dc)\r\n{\r\n\tcopy_msg(1);\r\n}\r\n\r\nvoid demx_foundsecret(decodectx_t *dc)\r\n{\r\n\tcopy_msg(1);\r\n}\r\n\r\nvoid demx_spawnstaticsound(decodectx_t *dc)\r\n{\r\n\tcopy_msg(10);\r\n}\r\n\r\nvoid demx_intermission(decodectx_t *dc)\r\n{\r\n\tcopy_msg(1);\r\n}\r\n\r\nvoid demx_cdtrack(decodectx_t *dc)\r\n{\r\n\tcopy_msg(3);\r\n}\r\n\r\nvoid demx_sellscreen(decodectx_t *dc)\r\n{\r\n\tcopy_msg(1);\r\n}\r\n\r\n/* nehahra */\r\nvoid demx_showlmp(decodectx_t *dc)\r\n{\r\n\tuchar *ptr = inptr + 1;\r\n\twhile (*ptr++);\r\n\twhile (*ptr++);\r\n\tptr += 2;\r\n\t*inptr = DEM_showlmp;\r\n\tcopy_msg(ptr-inptr);\r\n}\r\n\r\nvoid demx_updateentity(decodectx_t *dc)\r\n{\r\n\tuchar buf[32], *ptr;\r\n\tint mask, i, entity;\r\n\tint baseval = 0, prev;\r\n\tent_t n, o;\r\n\tint tmp;\r\n\r\n#ifndef GUI\r\n\tif (dem_decode_type == TYPE_DEMV1) { demv1_updateentity(); return; }\r\n#endif\r\n\tlastent = 0;\r\n\tfor (ptr = inptr+1; *ptr; ptr++)\r\n\t{\r\n\t\tif (*ptr == 0xff) { baseval += 0xfe; continue; }\r\n\t\tentity = baseval + *ptr;\r\n\t\tnewent[entity].active = 1;\r\n\t\twhile (entlink[lastent] <= entity) lastent = entlink[lastent];\r\n\t\tif (lastent < entity)\r\n\t\t{\r\n\t\t\tentlink[entity] = entlink[lastent];\r\n\t\t\tentlink[lastent] = entity;\r\n\t\t}\r\n\t}\r\n\r\n\tfor (prev = 0, i = entlink[0], ptr++; i < MAX_ENT; i = entlink[i])\r\n\t{\r\n\t\tnewent[i].org0 += newent[i].od0;\r\n\t\tnewent[i].org1 += newent[i].od1;\r\n\t\tnewent[i].org2 += newent[i].od2;\r\n\r\n\t\tif (!newent[i].active) { prev = i; continue; }\r\n\r\n\t\tmask = *ptr++;\r\n\r\n\t\tif (mask == 0x80)\r\n\t\t{\r\n\t\t\toldent[i] = newent[i] = base[i];\r\n\t\t\tentlink[prev] = entlink[i];\r\n\t\t\tcontinue;\r\n\t\t}\r\n\r\n\t\tprev = i;\r\n\r\n\t\tif (mask == 0x00) { newent[i].active = 0; continue; }\r\n\r\n\t\tif (mask & 0x80) mask += (*ptr++) << 8;\r\n\t\tif (mask & 0x8000) mask += (*ptr++) << 16;\r\n\r\n\t\tn = newent[i];\r\n\t\to = oldent[i];\r\n\r\n\t\tif (mask & 0x000001) { n.od2 = bplus(*ptr++,o.od2);\r\n\t\t\t\t       n.org2 = o.org2 + n.od2; }\r\n\t\tif (mask & 0x000800) { n.org2 = getshort(ptr); ptr += 2;\r\n\t\t\t\t       n.od2 = n.org2 - o.org2; }\r\n\t\tif (mask & 0x000002) { n.od1 = bplus(*ptr++,o.od1);\r\n\t\t\t\t       n.org1 = o.org1 + n.od1; }\r\n\t\tif (mask & 0x000400) { n.org1 = getshort(ptr); ptr += 2;\r\n\t\t\t\t       n.od1 = n.org1 - o.org1; }\r\n\t\tif (mask & 0x000004) { n.od0 = bplus(*ptr++,o.od0);\r\n\t\t\t\t       n.org0 = o.org0 + n.od0; }\r\n\t\tif (mask & 0x000200) { n.org0 = getshort(ptr); ptr += 2;\r\n\t\t\t\t       n.od0 = n.org0 - o.org0; }\r\n\r\n\t\tif (mask & 0x000008) n.ang0 = bplus(*ptr++,o.ang0);\r\n\t\tif (mask & 0x000010) n.ang1 = bplus(*ptr++,o.ang1);\r\n\t\tif (mask & 0x000020) n.ang2 = bplus(*ptr++,o.ang2);\r\n\t\tif (mask & 0x000040) n.frame = o.frame+1;\r\n\t\tif (mask & 0x000100) n.frame = bplus(*ptr++,o.frame);\r\n\r\n\t\tif (mask & 0x001000) n.effects = *ptr++;\r\n\t\tif (mask & 0x002000) n.modelindex = *ptr++;\r\n\t\tif (mask & 0x004000) n.newbit = !o.newbit;\r\n\t\tif (mask & 0x010000) n.colormap = *ptr++;\r\n\t\tif (mask & 0x020000) n.skin = *ptr++;\r\n\t/* nehahra */\r\n\t\tif (mask & 0x040000) { n.alpha = getfloat(ptr); ptr += 4; }\r\n\t\tif (mask & 0x080000) n.fullbright = *ptr++;\r\n\r\n\t\tnewent[i] = n;\r\n\t}\r\n\r\n\tif (*ptr == 0x31)\r\n\t{\r\n\t\tptr++;\r\n\t\twhile ((mask = getshort(ptr)))\r\n\t\t{\r\n\t\t\tptr += 2;\r\n\t\t\tmask &= 0xffff;\r\n\t\t\tif (mask & 0x8000) mask |= *ptr++ << 16;\r\n\t\t\tentity = mask & 0x3ff;\r\n\t\t\tnewent[entity].force ^= mask & 0xfffc00;\r\n\t\t}\r\n\t\tptr += 2;\r\n\t}\r\n\r\n\tdiscard_msg(ptr-inptr);\r\n\r\n\tfor (i = entlink[0]; i < MAX_ENT; i = entlink[i])\r\n\t{\r\n\t\tent_t n = newent[i], b = base[i];\r\n\r\n\t\tptr = buf+2;\r\n\t\tmask = 0x80;\r\n\r\n\t\tif (i > 0xff || (n.force & 0x400000))\r\n\t\t{\r\n\t\t\ttmp = cnvlong(i);\r\n\t\t\tmemcpy(ptr,&tmp,2);\r\n\t\t\tptr += 2;\r\n\t\t\tmask |= 0x4000;\r\n\t\t}\r\n\t\telse\r\n\t\t\t*ptr++ = i;\r\n\r\n\t\t#define BDIFF(x,bit,bit2) \\\r\n\t\t\tif (n.x != b.x || n.force & bit2) \\\r\n\t\t\t\t{ *ptr++ = n.x; mask |= bit; }\r\n\r\n\t\tBDIFF(modelindex,0x0400,0x040000);\r\n\t\tBDIFF(frame,0x0040,0x4000);\r\n\t\tBDIFF(colormap,0x0800,0x080000);\r\n\t\tBDIFF(skin,0x1000,0x100000);\r\n\t\tBDIFF(effects,0x2000,0x200000);\r\n\t\tif (n.org0 != b.org0 || n.force & 0x010000)\r\n\t\t    { mask |= 0x0002; tmp = cnvlong(n.org0); \r\n\t\t      memcpy(ptr,&tmp,2); ptr += 2; }\r\n\t\tBDIFF(ang0,0x0100,0x0800);\r\n\t\tif (n.org1 != b.org1 || n.force & 0x0400)\r\n\t\t    { mask |= 0x0004; tmp = cnvlong(n.org1); \r\n\t\t      memcpy(ptr,&tmp,2); ptr += 2; }\r\n\t\tBDIFF(ang1,0x0010,0x1000);\r\n\t\tif (n.org2 != b.org2 || n.force & 0x020000)\r\n\t\t    { mask |= 0x0008; tmp = cnvlong(n.org2); \r\n\t\t      memcpy(ptr,&tmp,2); ptr += 2; }\r\n\t\tBDIFF(ang2,0x0200,0x2000);\r\n/* nehahra */\r\n\t\tif (n.force & 0x800000)\r\n\t\t{\r\n\t\t\tfloat f = 1;\r\n\r\n\t\t\tif (n.fullbright)\r\n\t\t\t\tf = 2;\r\n\t\t\ttmp = cnvlong(*(int *)&f);\r\n\t\t\tmemcpy(ptr, &tmp, 4);\r\n\t\t\ttmp = cnvlong(*(int *)&n.alpha);\r\n\t\t\tmemcpy(ptr + 4, &tmp, 4);\r\n\t\t\tptr += 8;\r\n\t\t\tif (f == 2)\r\n\t\t\t{\r\n\t\t\t\tf = (char)(n.fullbright - 1);\r\n\t\t\t\ttmp = cnvlong(*(int *)&f);\r\n\t\t\t\tmemcpy(ptr, &tmp, 4);\r\n\t\t\t\tptr += 4;\r\n\t\t\t}\r\n\t\t\tmask |= 0x8000;\r\n\t\t}\r\n\t\t\r\n\t\tif (n.newbit) mask |= 0x20;\r\n\t\tif (mask & 0xff00) mask |= 0x01;\r\n\t\tbuf[0] = mask & 0xff;\r\n\t\tbuf[1] = (mask & 0xff00) >> 8;\r\n\t\tif (!(mask & 0x01)) { memcpy(buf+1,buf+2,ptr-buf-2); ptr--; }\r\n\t\tinsert_msg(buf,ptr-buf);\r\n\r\n\t\toldent[i] = newent[i];\r\n\t}\r\n\r\n}\r\n\r\nvoid (* const demx_message[])(decodectx_t *dc) = {\r\n\tdemx_nop, demx_disconnect, demx_updatestat, demx_version,\r\n\tdemx_setview, demx_sound, demx_time, demx_string, demx_string,\r\n\tdemx_setangle, demx_serverinfo, demx_lightstyle, demx_updatename,\r\n\tdemx_updatefrags, demx_clientdata, demx_stopsound, demx_updatecolors,\r\n\tdemx_particle, demx_damage, demx_spawnstatic, demx_spawnbinary,\r\n\tdemx_spawnbaseline, demx_temp_entity, demx_setpause, demx_signonnum,\r\n\tdemx_string, demx_killedmonster, demx_foundsecret,\r\n\tdemx_spawnstaticsound, demx_intermission, demx_string,\r\n\tdemx_cdtrack, demx_sellscreen, demx_string, demx_longtime,\r\n\tdemx_string, demx_string, demx_showlmp\t/* nehahra */\r\n};\r\n\r\nvoid dem_uncompress_init (decodectx_t *dc, int type)\r\n{\r\n\tdem_decode_type = -type;\r\n\tmemset(&base,0,sizeof(ent_t)*MAX_ENT);\r\n\tmemset(&oldent,0,sizeof(ent_t)*MAX_ENT);\r\n\tmemset(&oldcd,0,sizeof(cdata_t));\r\n\toldcd.voz = 22;\r\n\toldcd.items = 0x4001;\r\n\tentlink[0] = MAX_ENT;\r\n\tcam0 = cam1 = cam2 = 0;\r\n\tcopybaseline = 0;\r\n\tdem_gametime = 0;\r\n\tmaxent = 0;\r\n\tsble = 0;\r\n}\r\n\t\r\nuInt dem_uncompress_block(decodectx_t *dc)\r\n{\r\n\tint a1;\r\n\tuchar cfields;\r\n#ifdef GUI\r\n\tint uemask = 0x30, cdmask = 0x40;\r\n#else\r\n\tint uemask = (dem_decode_type == TYPE_DEMV1)? 0x80 : 0x30;\r\n\tint cdmask = (dem_decode_type == TYPE_DEMV1)? 0xf0 : 0x40;\r\n#endif\r\n\tcfields = *inptr++;\r\n\r\n\tif (cfields & 1) { cam0 += getlong(inptr); inptr += 4; }\r\n\tif (cfields & 2) { cam1 += getlong(inptr); inptr += 4; }\r\n\tif (cfields & 4) { cam2 += getlong(inptr); inptr += 4; }\r\n\r\n\toutlen = 0;\r\n\ta1 = 0/*length*/; insert_msg(&a1,4);\r\n\ta1 = cnvlong(cam0); insert_msg(&a1,4);\r\n\ta1 = cnvlong(cam1); insert_msg(&a1,4);\r\n\ta1 = cnvlong(cam2); insert_msg(&a1,4);\r\n\r\n\tdem_updateframe = 0;\r\n\twhile (*inptr)\r\n\t{\t\t\r\n\t\tif ((*inptr & 0xf8) == uemask)\r\n\t\t\tdemx_updateentity(dc);\r\n\t\telse\r\n\t\t{\r\n#ifndef GUI\r\n\t\t\tif (dem_updateframe)\r\n\t\t\t\t{ demv1_dxentities(); dem_updateframe = 0; }\r\n#endif\r\n\t\t\tif (*inptr && *inptr <= DZ_showlmp)\r\n\t\t\t\tdemx_message[*inptr - 1](dc);\r\n\t\t\telse if ((*inptr & 0xf0) == cdmask) \r\n\t\t\t\tdemx_clientdata(dc);\r\n\t\t\telse if ((*inptr & 0xf8) == 0x38)\r\n\t\t\t\tdemx_sound(dc);\r\n\t\t\telse if (*inptr >= 0x80)\r\n\t\t\t\tdem_copy_ue(dc);\r\n\t\t\telse\r\n\t\t\t\treturn 0;\r\n\t\t}\r\n\t}\r\n#ifndef GUI\r\n\tif (dem_updateframe) demv1_dxentities();\r\n#endif\r\n\toutlen -= 16;\r\n\toutlen = cnvlong(outlen);\r\n\tmemcpy(outblk,&outlen,4);\r\n\tOutfile_Write(outblk,cnvlong(outlen)+16);\r\n\r\n\tif (copybaseline)\r\n\t{\r\n\t\tcopybaseline = 0;\r\n\t\tmemcpy(oldent,base,sizeof(ent_t)*MAX_ENT);\r\n\t\tmemcpy(newent,base,sizeof(ent_t)*MAX_ENT);\r\n\t}\r\n\r\n\treturn inptr-inblk+1;\r\n}\r\n\r\nuInt dem_uncompress (decodectx_t *dc, uInt maxsize)\r\n{\r\n\tuInt blocksize = 0;\r\n\tinptr = inblk;\r\n\tif (dem_decode_type < 0) \r\n\t{\r\n\t\tdem_decode_type = -dem_decode_type;\r\n\t\twhile (inptr[blocksize] != '\\n' && blocksize < 12)\r\n\t\t\tblocksize++;\r\n\r\n\t\tif (blocksize == 12)\t/* seriously corrupt! */\r\n\t\t\treturn 0;\r\n\r\n\t\tOutfile_Write(inblk, ++blocksize);\r\n\t\tinptr += blocksize;\r\n\t}\r\n\twhile (blocksize < 16000 && blocksize < maxsize)\r\n\t{\r\n\t\tif (*inptr == 0xff)\r\n\t\t{\r\n\t\t\tuInt len = getlong(inptr+1);\r\n\t\t\tif (p_blocksize - blocksize - 5 < len)\r\n\t\t\t\treturn blocksize;\r\n\t\t\tOutfile_Write(inptr + 5,len);\r\n\t\t\tblocksize = inptr - inblk + len + 5;\r\n\t\t}\r\n\t\telse\r\n\t\t\tblocksize = dem_uncompress_block(dc);\r\n\t\tif (!blocksize)\r\n\t\t\treturn 0;\t/* corrupt encoding */\r\n\t\tinptr++;\r\n\t}\r\n\treturn blocksize;\r\n}\r\n\r\n\r\n//End decode.c\r\n/////////////////////////////////////////////////////////////////////////\r\n#undef outlen\r\n\r\n#undef copy_msg\r\n#undef insert_msg\r\n#undef discard_msg\r\n#undef inptr\r\n#undef dem_decode_type\r\n#undef copybaseline\r\n#undef maxent\r\n#undef lastent\r\n#undef sble\r\n#undef entlink\r\n#undef dem_gametime\r\n#undef outlen\r\n#undef cam0\r\n#undef cam1\r\n#undef cam2\r\n#undef inblk\r\n#undef outblk\r\n#undef inptr\r\n#undef oldcd\r\n#undef newcd\r\n#undef base\r\n#undef oldent\r\n#undef newent\r\n#undef dem_updateframe\r\n\r\n#include <zlib.h>\r\n//pack mutex must be held for this function.\r\nqboolean FSDZ_ExtractFile(qbyte *out, size_t outsize, dzarchive_t *pack, mdzfile_t *src)\r\n{\r\n\tswitch(src->ztype)\r\n\t{\r\n\tcase TYPE_PAK:\r\n\t\t{\r\n\t\t\tunsigned int i;\r\n\t\t\tunsigned int dirsize = src->subfiles * 64;\r\n\t\t\tunsigned int diroffset = src->filelen - dirsize;\r\n\t\t\tsize_t ofs;\r\n\t\t\tqbyte *ftab = out + diroffset;\r\n\t\t\tout[0] = 'P';\r\n\t\t\tout[1] = 'A';\r\n\t\t\tout[2] = 'C';\r\n\t\t\tout[3] = 'K';\r\n\t\t\t((int*)out)[2] = LittleLong(dirsize);//size;\r\n\t\t\t((int*)out)[1] = LittleLong(src->filelen - dirsize);//offset;\r\n\r\n\t\t\tfor (ofs = 12, i = 1; i <= src->subfiles; i++)\r\n\t\t\t{\r\n\t\t\t\tif (ofs + src[i].filelen > diroffset)\r\n\t\t\t\t\treturn false;\t//panic!\r\n\t\t\t\tFSDZ_ExtractFile(out+ofs, src[i].filelen, pack, src+i);\r\n\r\n\t\t\t\tQ_strncpyz(ftab, src[i].name, 56);\r\n\t\t\t\t*(int*)&(ftab[56]) = ofs;\r\n\t\t\t\t*(int*)&(ftab[60]) = src[i].filelen;\r\n\t\t\t\tftab += 64;\r\n\r\n\t\t\t\tofs += src[i].filelen;\r\n\t\t\t}\r\n\t\t}\r\n\t\treturn true;\r\n\r\n\tcase TYPE_STORE:\t//no compression or anything\r\n\t\tVFS_SEEK(pack->handle, src->filepos);\r\n\t\treturn outsize == VFS_READ(pack->handle, out, outsize);\r\n\r\n\t//not actually a file... we shouldn't be here.\r\n\tcase TYPE_DIR:\r\n\t\treturn false;\r\n\tcase TYPE_DEMV1:\t//dz v1 == solid archive. really messy, we don't support them.\r\n\t\treturn false;\r\n\r\n\tcase TYPE_NORMAL:\r\n\tcase TYPE_TXT:\r\n\tcase TYPE_DZ:\t//its defined. its weird, but its defined.\r\n\tcase TYPE_DEM:\r\n\tcase TYPE_NEHAHRA:\r\n\t\t{\r\n\t\t\t//decodectx_t *dc = NULL;\r\n\t\t\tunsigned char inbuffer[p_blocksize];\r\n\t\t\tint ret;\r\n\t\t\tsize_t inremaining = src->csize;\r\n\t\t\tdecodectx_t *dc = NULL;\r\n\r\n\t\t\tz_stream strm = {\r\n\t\t\t\tinbuffer,\r\n\t\t\t\t0,\r\n\t\t\t\t0,\r\n\r\n\t\t\t\tout,\r\n\t\t\t\tsrc->isize,\r\n\t\t\t\t0\r\n\t\t\t};\r\n\t\t\tstrm.data_type = Z_UNKNOWN;\r\n\r\n\t\t\tif (src->ztype == TYPE_DEM||src->ztype == TYPE_NEHAHRA)\r\n\t\t\t{\r\n\t\t\t\tdc = Z_Malloc(sizeof(*dc));\r\n\t\t\t\tdc->out = out;\r\n\t\t\t\tdc->outend = out + outsize;\r\n\t\t\t\tdem_uncompress_init(dc, src->ztype);\r\n\r\n\t\t\t\tstrm.next_out = dc->inblk;\r\n\t\t\t\tstrm.avail_out = sizeof(dc->inblk);\r\n\t\t\t}\r\n\r\n\t\t\tVFS_SEEK(pack->handle, src->filepos);\r\n\r\n\t\t\tstrm.avail_in = 0;\r\n\t\t\tstrm.next_in = inbuffer;\r\n\r\n\t\t\tinflateInit(&strm);\r\n\r\n\t\t\twhile ((ret=inflate(&strm, Z_SYNC_FLUSH)) != Z_STREAM_END)\r\n\t\t\t{\r\n\t\t\t\tif (strm.avail_in == 0 || strm.avail_out == 0)\r\n\t\t\t\t{\t//keep feeding the beast\r\n\t\t\t\t\tif (strm.avail_in == 0)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tsize_t chunk = inremaining;\r\n\t\t\t\t\t\tif (chunk > sizeof(inbuffer))\r\n\t\t\t\t\t\t\tchunk = sizeof(inbuffer);\r\n\t\t\t\t\t\tstrm.avail_in = VFS_READ(pack->handle, inbuffer, chunk);\r\n\t\t\t\t\t\tinremaining -= strm.avail_in;\r\n\t\t\t\t\t\tstrm.next_in = inbuffer;\r\n\t\t\t\t\t\tif (!strm.avail_in)\r\n\t\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\t//and cleaning up its excrement\r\n\t\t\t\t\tif (strm.avail_out == 0)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tif (dc)\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tint chunk = dem_uncompress(dc, strm.next_out - dc->inblk);\r\n\t\t\t\t\t\t\tint remaining = strm.next_out-(dc->inblk+chunk);\r\n\t\t\t\t\t\t\tif (!chunk)\r\n\t\t\t\t\t\t\t\tbreak;\t//made no progress. that's bad\r\n\t\t\t\t\t\t\tmemmove(dc->inblk, dc->inblk+chunk, remaining);\r\n\r\n\t\t\t\t\t\t\tstrm.next_out = dc->inblk+remaining;\r\n\t\t\t\t\t\t\tstrm.avail_out = sizeof(dc->inblk)-remaining;\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\telse\r\n\t\t\t\t\t\t\tbreak;\t//eep\r\n\t\t\t\t\t}\r\n\t\t\t\t\tcontinue;\r\n\t\t\t\t}\r\n\r\n\t\t\t\t//doh, it terminated for no reason\r\n\t\t\t\tif (ret != Z_STREAM_END)\r\n\t\t\t\t\tbreak;\r\n\t\t\t}\r\n\r\n\t\t\tif (dc)\r\n\t\t\t{\r\n\t\t\t\twhile(1)\r\n\t\t\t\t{\r\n\t\t\t\t\tint chunk = dem_uncompress(dc, strm.next_out - dc->inblk);\r\n\t\t\t\t\tint remaining = strm.next_out-(dc->inblk+chunk);\r\n\t\t\t\t\tif (!chunk || !remaining)\r\n\t\t\t\t\t\tbreak;\t//made no progress. that's bad\r\n\t\t\t\t\tmemmove(dc->inblk, dc->inblk+chunk, remaining);\r\n\t\t\t\t\tstrm.next_out = dc->inblk+remaining;\r\n\t\t\t\t}\r\n\t\t\t\tZ_Free(dc);\r\n\t\t\t\tdc = NULL;\r\n\t\t\t}\r\n\r\n\t\t\tinflateEnd(&strm);\r\n\t\t\treturn strm.total_out == src->isize && !inremaining && ret == Z_STREAM_END;\r\n\t\t}\r\n\t\treturn false;\r\n\r\n\tdefault:\t//unknown file types can just fail.\r\n\t\treturn false;\r\n\t}\r\n}\r\n\r\n\r\nstatic void QDECL FSDZ_GetPathDetails(searchpathfuncs_t *handle, char *out, size_t outlen)\r\n{\r\n\tdzarchive_t *pak = (dzarchive_t*)handle;\r\n\r\n\t*out = 0;\r\n\tif (pak->references != 1)\r\n\t\tQ_snprintfz(out, outlen, \"(%i)\", pak->references-1);\r\n}\r\nstatic void QDECL FSDZ_ClosePath(searchpathfuncs_t *handle)\r\n{\r\n\tqboolean stillopen;\r\n\tdzarchive_t *pak = (void*)handle;\r\n\r\n\tif (!Sys_LockMutex(pak->mutex))\r\n\t\treturn;\t//ohnoes\r\n\tstillopen = --pak->references > 0;\r\n\tSys_UnlockMutex(pak->mutex);\r\n\tif (stillopen)\r\n\t\treturn;\t//not free yet\r\n\r\n\r\n\tVFS_CLOSE (pak->handle);\r\n\r\n\tSys_DestroyMutex(pak->mutex);\r\n\tif (pak->files)\r\n\t\tZ_Free(pak->files);\r\n\tZ_Free(pak);\r\n}\r\nstatic void QDECL FSDZ_BuildHash(searchpathfuncs_t *handle, int depth, void (QDECL *AddFileHash)(int depth, const char *fname, fsbucket_t *filehandle, void *pathhandle))\r\n{\r\n\tdzarchive_t *pak = (void*)handle;\r\n\tint i;\r\n\r\n\tfor (i = 0; i < pak->numfiles; i++)\r\n\t{\r\n\t\tAddFileHash(depth, pak->files[i].name, &pak->files[i].bucket, &pak->files[i]);\r\n\t}\r\n}\r\nstatic unsigned int QDECL FSDZ_FLocate(searchpathfuncs_t *handle, flocation_t *loc, const char *filename, void *hashedresult)\r\n{\r\n\tmdzfile_t *pf = hashedresult;\r\n\tint i;\r\n\tdzarchive_t\t\t*pak = (void*)handle;\r\n\r\n// look through all the pak file elements\r\n\r\n\tif (pf)\r\n\t{\t//is this a pointer to a file in this pak?\r\n\t\tif (pf < pak->files || pf > pak->files + pak->numfiles)\r\n\t\t\treturn FF_NOTFOUND;\t//was found in a different path\r\n\t}\r\n\telse\r\n\t{\r\n\t\tfor (i=0 ; i<pak->numfiles ; i++)\t//look for the file\r\n\t\t{\r\n\t\t\tif (!Q_strcasecmp (pak->files[i].name, filename))\r\n\t\t\t{\r\n\t\t\t\tpf = &pak->files[i];\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\tif (pf)\r\n\t{\r\n\t\tif (loc)\r\n\t\t{\r\n\t\t\tloc->fhandle = pf;\r\n\t\t\tsnprintf(loc->rawname, sizeof(loc->rawname), \"%s\", pak->descname);\r\n\t\t\tloc->offset = pf->filepos;\r\n\t\t\tloc->len = pf->filelen;\r\n\t\t}\r\n\t\treturn FF_FOUND;\r\n\t}\r\n\treturn FF_NOTFOUND;\r\n}\r\nstatic int QDECL FSDZ_EnumerateFiles (searchpathfuncs_t *handle, const char *match, int (QDECL *func)(const char *, qofs_t, time_t mtime, void *, searchpathfuncs_t *spath), void *parm)\r\n{\r\n\tdzarchive_t\t*pak = (dzarchive_t*)handle;\r\n\tint\t\tnum;\r\n\r\n\tfor (num = 0; num<(int)pak->numfiles; num++)\r\n\t{\r\n\t\tif (wildcmp(match, pak->files[num].name))\r\n\t\t{\r\n\t\t\tif (!func(pak->files[num].name, pak->files[num].filelen, pak->files[num].mtime, parm, handle))\r\n\t\t\t\treturn false;\r\n\t\t}\r\n\t}\r\n\r\n\treturn true;\r\n}\r\n\r\nstatic int QDECL FSDZ_GeneratePureCRC(searchpathfuncs_t *handle, const int *seed)\r\n{\r\n\tdzarchive_t *pak = (void*)handle;\r\n\r\n\tint result;\r\n\tint *filecrcs;\r\n\tint numcrcs=0;\r\n\tint i;\r\n\r\n\tfilecrcs = BZ_Malloc((pak->numfiles+1)*sizeof(int));\r\n\tif (seed)\r\n\t\tfilecrcs[numcrcs++] = *seed;\r\n\r\n\tfor (i = 0; i < pak->numfiles; i++)\r\n\t{\r\n\t\tif (pak->files[i].filelen > 0)\r\n\t\t{\r\n\t\t\tfilecrcs[numcrcs++] = pak->files[i].filepos ^ pak->files[i].filelen ^ CalcHashInt(&hash_crc16, pak->files[i].name, sizeof(56));\r\n\t\t}\r\n\t}\r\n\r\n\tresult = CalcHashInt(&hash_md4, filecrcs, numcrcs*sizeof(int));\r\n\r\n\tBZ_Free(filecrcs);\r\n\treturn result;\r\n}\r\n\r\nstatic int QDECL VFSDZ_ReadBytes (struct vfsfile_s *vfs, void *buffer, int bytestoread)\r\n{\r\n\tvfsdz_t *vfsp = (vfsdz_t*)vfs;\r\n\r\n\tif (bytestoread == 0)\r\n\t\treturn 0;\r\n\r\n\tif (vfsp->currentpos + bytestoread > vfsp->length)\r\n\t\tbytestoread = vfsp->length - vfsp->currentpos;\r\n\tif (bytestoread <= 0)\r\n\t\treturn -1;\r\n\r\n\tmemcpy(buffer, vfsp->data + vfsp->currentpos, bytestoread);\r\n\tvfsp->currentpos += bytestoread;\r\n\r\n\treturn bytestoread;\r\n}\r\nstatic int QDECL VFSDZ_WriteBytes (struct vfsfile_s *vfs, const void *buffer, int bytestoread)\r\n{\t//not supported.\r\n\tSys_Error(\"Cannot write to dz files\\n\");\r\n\treturn 0;\r\n}\r\nstatic qboolean QDECL VFSDZ_Seek (struct vfsfile_s *vfs, qofs_t pos)\r\n{\r\n\tvfsdz_t *vfsp = (vfsdz_t*)vfs;\r\n\tif (pos > vfsp->length)\r\n\t\treturn false;\r\n\tvfsp->currentpos = pos;\r\n\r\n\treturn true;\r\n}\r\nstatic qofs_t QDECL VFSDZ_Tell (struct vfsfile_s *vfs)\r\n{\r\n\tvfsdz_t *vfsp = (vfsdz_t*)vfs;\r\n\treturn vfsp->currentpos;\r\n}\r\nstatic qofs_t QDECL VFSDZ_GetLen (struct vfsfile_s *vfs)\r\n{\r\n\tvfsdz_t *vfsp = (vfsdz_t*)vfs;\r\n\treturn vfsp->length;\r\n}\r\nstatic qboolean QDECL VFSDZ_Close(vfsfile_t *vfs)\r\n{\r\n\tvfsdz_t *vfsp = (vfsdz_t*)vfs;\r\n\tZ_Free(vfsp);\t//free ourselves.\r\n\treturn true;\r\n}\r\nstatic vfsfile_t *QDECL FSDZ_OpenVFS(searchpathfuncs_t *handle, flocation_t *loc, const char *mode)\r\n{\r\n\tdzarchive_t *pack = (dzarchive_t*)handle;\r\n\tvfsdz_t *vfs;\r\n\tmdzfile_t *pf = loc->fhandle;\r\n\r\n\tif (strcmp(mode, \"rb\") && strcmp(mode, \"r\") && strcmp(mode, \"rt\"))\r\n\t\treturn NULL; //urm, unable to write/append\r\n\r\n\tvfs = Z_Malloc(sizeof(vfsdz_t) + pf->filelen);\r\n\r\n\tif (!Sys_LockMutex(pack->mutex))\r\n\t{\r\n\t\tZ_Free(vfs);\r\n\t\treturn NULL;\r\n\t}\r\n\r\n\tif (!FSDZ_ExtractFile(vfs->data, pf->filelen, pack, pf))\r\n\t{\r\n\t\tSys_UnlockMutex(pack->mutex);\r\n\t\tZ_Free(vfs);\r\n\t\treturn NULL;\r\n\t}\r\n\tSys_UnlockMutex(pack->mutex);\r\n\r\n\tvfs->length = loc->len;\r\n\tvfs->currentpos = 0;\r\n\r\n#ifdef _DEBUG\r\n\tQ_strncpyz(vfs->funcs.dbgname, pf->name, sizeof(vfs->funcs.dbgname));\r\n#endif\r\n\tvfs->funcs.Close = VFSDZ_Close;\r\n\tvfs->funcs.GetLen = VFSDZ_GetLen;\r\n\tvfs->funcs.ReadBytes = VFSDZ_ReadBytes;\r\n\tvfs->funcs.Seek = VFSDZ_Seek;\r\n\tvfs->funcs.Tell = VFSDZ_Tell;\r\n\tvfs->funcs.WriteBytes = VFSDZ_WriteBytes;\t//not supported\r\n\r\n\treturn (vfsfile_t *)vfs;\r\n}\r\n\r\nstatic void QDECL FSDZ_ReadFile(searchpathfuncs_t *handle, flocation_t *loc, char *buffer)\r\n{\r\n\tvfsfile_t *f;\r\n\tf = FSDZ_OpenVFS(handle, loc, \"rb\");\r\n\tif (!f)\t//err...\r\n\t\treturn;\r\n\tVFS_READ(f, buffer, loc->len);\r\n\tVFS_CLOSE(f);\r\n}\r\n\r\n\r\n/*\r\n=================\r\nCOM_LoadPackFile\r\n\r\nTakes an explicit (not game tree related) path to a pak file.\r\n\r\nLoads the header and directory, adding the files at the beginning\r\nof the list so they override previous pack files.\r\n=================\r\n*/\r\nsearchpathfuncs_t *QDECL FSDZ_LoadArchive (vfsfile_t *file, searchpathfuncs_t *parent, const char *filename, const char *desc, const char *prefix)\r\n{\r\n\tdpackheader_t\theader;\r\n\tint\t\t\t\ti;\r\n//\tint\t\t\t\tj;\r\n\tmdzfile_t\t\t*newfiles;\r\n\tint\t\t\t\tnumpackfiles;\r\n\tdzarchive_t\t\t*pack;\r\n\tvfsfile_t\t\t*packhandle;\r\n\tdpackfile_t\t\tinfo;\r\n\tint read;\r\n\tstruct tm t;\r\n//\tunsigned short\t\tcrc;\r\n\r\n\tmemset(&t, 0, sizeof(t));\r\n\r\n\tpackhandle = file;\r\n\tif (packhandle == NULL)\r\n\t\treturn NULL;\r\n\r\n\tif (prefix && *prefix)\r\n\t\treturn NULL;\t//not supported at this time\r\n\r\n\tread = VFS_READ(packhandle, &header, sizeof(header));\r\n\tif (read < sizeof(header) || header.id[0] != 'D' || header.id[1] != 'Z')\r\n\t{\r\n\t\tCon_Printf(\"%s is not a dz - %c%c\\n\", desc, header.id[0], header.id[1]);\r\n\t\treturn NULL;\r\n\t}\r\n\tif (header.major_ver > 2/* || (header.major_ver == 2 && header.minor_ver > 9)*/)\r\n\t{\t//ignore minor versions, assume they've got only additions.\r\n\t\tCon_Printf(\"%s uses too recent a version. %i.%i > 2.9\\n\", desc, header.major_ver, header.minor_ver);\r\n\t\treturn NULL;\r\n\t}\r\n\tif (header.major_ver < 2)\r\n\t{\r\n\t\tCon_Printf(\"%s uses too old a version. %i.%i < 2.0\\n\", desc, header.major_ver, header.minor_ver);\r\n\t\treturn NULL;\r\n\t}\r\n\theader.dirofs = LittleLong (header.dirofs);\r\n\theader.dirlen = LittleLong (header.dirlen);\r\n\r\n\tnumpackfiles = header.dirlen;\r\n\r\n\tnewfiles = (mdzfile_t*)Z_Malloc (numpackfiles * sizeof(mdzfile_t));\r\n\r\n\tVFS_SEEK(packhandle, header.dirofs);\r\n\r\n\tpack = (dzarchive_t*)Z_Malloc (sizeof (dzarchive_t));\r\n// parse the directory\r\n\tfor (i=0 ; i<numpackfiles ; i++)\r\n\t{\r\n\t\tif (header.major_ver == 1)\r\n\t\t\tread = VFS_READ(packhandle, &info, sizeof(info)-8) == sizeof(info)-8;\r\n\t\telse\r\n\t\t\tread = VFS_READ(packhandle, &info, sizeof(info)) == sizeof(info);\r\n\t\tif (!read)\r\n\t\t{\r\n\t\t\tCon_Printf(\"DZIP file table truncated, only found %i files out of %i\\n\", i, numpackfiles);\r\n\t\t\tnumpackfiles = i;\r\n\t\t\tbreak;\r\n\t\t}\r\n/*\r\n\t\tfor (j=0 ; j<sizeof(info) ; j++)\r\n\t\t\tCRC_ProcessByte(&crc, ((qbyte *)&info)[j]);\r\n*/\r\n\t\tinfo.type = LittleLong(info.type);\r\n\t\tinfo.namelen = LittleShort(info.namelen);\r\n\t\tif (info.namelen >= sizeof(newfiles[i].name) || info.type==TYPE_PAK)\r\n\t\t{\r\n\t\t\t//ignore dzip's paks. this allows us to just directly read the demos inside without extra subdirs.\r\n\t\t\tVFS_SEEK(packhandle, VFS_TELL(packhandle)+info.namelen);\r\n\t\t\tnumpackfiles--;\r\n\t\t\ti--;\t//counter the ++\r\n\t\t\tcontinue;\r\n\t\t}\r\n\t\tnewfiles[i].name[info.namelen] = 0; //paranoid\r\n\t\tif (info.namelen != VFS_READ(packhandle, &newfiles[i].name, info.namelen))\r\n\t\t{\r\n\t\t\tCon_Printf(\"DZIP file table truncated, only found %i files out of %i\\n\", i, numpackfiles);\r\n\t\t\tnumpackfiles = i;\r\n\t\t\tbreak;\r\n\t\t}\r\n\t\tCOM_CleanUpPath(newfiles[i].name);\t//this fixes silly people using backslashes in paths.\r\n\t\tnewfiles[i].filepos  = LittleLong(info.offset);\r\n\t\tnewfiles[i].filelen  = LittleLong(info.realsize);\r\n\t\tnewfiles[i].isize    = LittleLong(info.intersize);\r\n\t\tnewfiles[i].csize    = LittleLong(info.size);\r\n\t\tnewfiles[i].ztype    = info.type;\r\n\t\tnewfiles[i].subfiles = (unsigned short)LittleShort(info.pak);\r\n\r\n\t\t//lame, but whatever\r\n\t\t//fixme: make sure they're all correct...\r\n\t\tt.tm_year = ((info.date >> 25) & 0x7f) + 1980 - 1900;\r\n\t\tt.tm_mon = ((info.date >> 21) & 0x0f);\r\n\t\tt.tm_mday = (info.date >> 16) & 0x1f;\r\n\t\tt.tm_hour = ((info.date >> 11) & 0x1f)-1;\r\n\t\tt.tm_min = (info.date >> 5) & 0x3f;\r\n\t\tt.tm_sec = (info.date & 0x1f) << 1;\r\n\t\tnewfiles[i].mtime = mktime(&t);\r\n\t}\r\n\r\n\tstrcpy (pack->descname, desc);\r\n\tpack->handle = packhandle;\r\n\tpack->numfiles = numpackfiles;\r\n\tpack->files = newfiles;\r\n\tpack->filepos = 0;\r\n\tVFS_SEEK(packhandle, pack->filepos);\r\n\r\n\tpack->references++;\r\n\r\n\tpack->mutex = Sys_CreateMutex();\r\n\r\n//\tCon_TPrintf (\"Added packfile %s (%i files)\\n\", desc, numpackfiles);\r\n\r\n\tpack->pub.fsver           = FSVER;\r\n\tpack->pub.GetPathDetails  = FSDZ_GetPathDetails;\r\n\tpack->pub.ClosePath       = FSDZ_ClosePath;\r\n\tpack->pub.BuildHash       = FSDZ_BuildHash;\r\n\tpack->pub.FindFile\t      = FSDZ_FLocate;\r\n\tpack->pub.ReadFile        = FSDZ_ReadFile;\r\n\tpack->pub.EnumerateFiles  = FSDZ_EnumerateFiles;\r\n\tpack->pub.GeneratePureCRC = FSDZ_GeneratePureCRC;\r\n\tpack->pub.OpenVFS         = FSDZ_OpenVFS;\r\n\treturn &pack->pub;\r\n}\r\n\r\n#endif\r\n"
  },
  {
    "path": "engine/common/fs_pak.c",
    "content": "#include \"quakedef.h\"\n#include \"fs.h\"\n\n#ifdef PACKAGE_Q1PAK\n\n//\n// in memory\n//\n\ntypedef struct\n{\n\tfsbucket_t bucket;\n\n\tchar\tname[MAX_QPATH];\n\tunsigned int\t\tfilepos, filelen;\n} mpackfile_t;\n\ntypedef struct pack_s\n{\n\tsearchpathfuncs_t pub;\n\tchar\tdescname[MAX_OSPATH];\n\tint\t\tnumfiles;\n\tmpackfile_t\t*files;\n\n\tvoid\t\t*mutex;\n\tvfsfile_t\t*handle;\n\tqofs_t\t\tfilepos;\t//the pos the subfiles left it at (to optimize calls to vfs_seek)\n\tqatomic32_t references;\t//seeing as all vfiles from a pak file use the parent's vfsfile, we need to keep the parent open until all subfiles are closed.\n} pack_t;\n\n//\n// on disk\n//\ntypedef struct\n{\n\tchar\tname[56];\n\tunsigned int\t\tfilepos, filelen;\n} dpackfile_t;\n\ntypedef struct\n{\n\tunsigned int\t\tfilepos, filelen;\n\tchar\tname[8];\n} dwadfile_t;\n\ntypedef struct\n{\n\tchar\tid[4];\n\tunsigned int\t\tdirofs;\n\tunsigned int\t\tdirlen;\n} dpackheader_t;\n\ntypedef struct\n{\n\tchar\tid[4];\n\tunsigned int\t\tdirlen;\n\tunsigned int\t\tdirofs;\n} dwadheader_t;\n\n#define\tMAX_FILES_IN_PACK\t2048\n\nstatic void QDECL FSPAK_GetPathDetails(searchpathfuncs_t *handle, char *out, size_t outlen)\n{\n\tpack_t *pak = (pack_t*)handle;\n\n\t*out = 0;\n\tif (pak->references != 1)\n\t\tQ_snprintfz(out, outlen, \"(%i)\", pak->references-1);\n}\nstatic void QDECL FSPAK_AddReference(searchpathfuncs_t *handle)\n{\n\tpack_t *pak = (void*)handle;\n\tFTE_Atomic32_Inc(&pak->references);\n}\nstatic void QDECL FSPAK_ClosePath(searchpathfuncs_t *handle)\n{\n\tqboolean stillopen;\n\tpack_t *pak = (void*)handle;\n\n\tstillopen = FTE_Atomic32_Dec(&pak->references) > 0;\n\tif (stillopen)\n\t\treturn;\t//not free yet\n\n\n\tVFS_CLOSE (pak->handle);\n\n\tSys_DestroyMutex(pak->mutex);\n\tif (pak->files)\n\t\tZ_Free(pak->files);\n\tZ_Free(pak);\n}\nstatic void QDECL FSPAK_BuildHash(searchpathfuncs_t *handle, int depth, void (QDECL *AddFileHash)(int depth, const char *fname, fsbucket_t *filehandle, void *pathhandle))\n{\n\tpack_t *pak = (void*)handle;\n\tint i;\n\n\tfor (i = 0; i < pak->numfiles; i++)\n\t{\n\t\tAddFileHash(depth, pak->files[i].name, &pak->files[i].bucket, &pak->files[i]);\n\t}\n}\nstatic unsigned int QDECL FSPAK_FLocate(searchpathfuncs_t *handle, flocation_t *loc, const char *filename, void *hashedresult)\n{\n\tmpackfile_t *pf = hashedresult;\n\tint i;\n\tpack_t\t\t*pak = (void*)handle;\n\n// look through all the pak file elements\n\n\tif (pf)\n\t{\t//is this a pointer to a file in this pak?\n\t\tif (pf < pak->files || pf > pak->files + pak->numfiles)\n\t\t\treturn FF_NOTFOUND;\t//was found in a different path\n\t}\n\telse\n\t{\n\t\tfor (i=0 ; i<pak->numfiles ; i++)\t//look for the file\n\t\t{\n\t\t\tif (!Q_strcasecmp (pak->files[i].name, filename))\n\t\t\t{\n\t\t\t\tpf = &pak->files[i];\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\tif (pf)\n\t{\n\t\tif (loc)\n\t\t{\n\t\t\tloc->fhandle = pf;\n\t\t\tQ_snprintfz(loc->rawname, sizeof(loc->rawname), \"%s\", pak->descname);\n\t\t\tloc->offset = pf->filepos;\n\t\t\tloc->len = pf->filelen;\n\t\t}\n\t\treturn FF_FOUND;\n\t}\n\treturn FF_NOTFOUND;\n}\nstatic int QDECL FSPAK_EnumerateFiles (searchpathfuncs_t *handle, const char *match, int (QDECL *func)(const char *, qofs_t, time_t mtime, void *, searchpathfuncs_t *spath), void *parm)\n{\n\tpack_t\t*pak = (pack_t*)handle;\n\tint\t\tnum;\n\n\tfor (num = 0; num<(int)pak->numfiles; num++)\n\t{\n\t\tif (wildcmp(match, pak->files[num].name))\n\t\t{\n\t\t\t//FIXME: time 0? maybe use the pak's mtime?\n\t\t\tif (!func(pak->files[num].name, pak->files[num].filelen, 0, parm, handle))\n\t\t\t\treturn false;\n\t\t}\n\t}\n\n\treturn true;\n}\n\nstatic int QDECL FSPAK_GeneratePureCRC(searchpathfuncs_t *handle, const int *seed)\n{\t//this is really weak. :(\n\tpack_t *pak = (void*)handle;\n\n\tint result;\n\tint *filecrcs;\n\tint numcrcs=0;\n\tint i;\n\n\tfilecrcs = BZ_Malloc((pak->numfiles+1)*sizeof(int));\n\tif (seed)\t//so exploiters can't cache it in purity replies.\n\t\tfilecrcs[numcrcs++] = *seed;\n\n\tfor (i = 0; i < pak->numfiles; i++)\n\t{\n\t\tif (pak->files[i].filelen > 0)\n\t\t{\n\t\t\tfilecrcs[numcrcs++] = pak->files[i].filepos ^ pak->files[i].filelen ^ CalcHashInt(&hash_crc16, pak->files[i].name, sizeof(56));\n\t\t}\n\t}\n\n\tresult = CalcHashInt(&hash_md4, filecrcs, numcrcs*sizeof(int));\n\n\tBZ_Free(filecrcs);\n\treturn result;\n}\n\ntypedef struct {\n\tvfsfile_t funcs;\n\tpack_t *parentpak;\n\tqofs_t startpos;\n\tqofs_t length;\n\tqofs_t currentpos;\n} vfspack_t;\nstatic int QDECL VFSPAK_ReadBytes (struct vfsfile_s *vfs, void *buffer, int bytestoread)\n{\n\tvfspack_t *vfsp = (vfspack_t*)vfs;\n\tint read;\n\n\tif (bytestoread == 0)\n\t\treturn 0;\n\n\tif (vfsp->currentpos - vfsp->startpos + bytestoread > vfsp->length)\n\t\tbytestoread = vfsp->length - (vfsp->currentpos - vfsp->startpos);\n\tif (bytestoread <= 0)\n\t{\n\t\treturn -1;\n\t}\n\n\tif (Sys_LockMutex(vfsp->parentpak->mutex))\n\t{\n\t\tif (vfsp->parentpak->filepos != vfsp->currentpos)\n\t\t\tVFS_SEEK(vfsp->parentpak->handle, vfsp->currentpos);\n\t\tread = VFS_READ(vfsp->parentpak->handle, buffer, bytestoread);\n\t\tvfsp->currentpos += read;\n\t\tvfsp->parentpak->filepos = vfsp->currentpos;\n\t\tSys_UnlockMutex(vfsp->parentpak->mutex);\n\t}\n\telse\n\t\tread = 0;\n\n\treturn read;\n}\nstatic int QDECL VFSPAK_WriteBytes (struct vfsfile_s *vfs, const void *buffer, int bytestoread)\n{\t//not supported.\n\tSys_Error(\"Cannot write to pak files\\n\");\n\treturn 0;\n}\nstatic qboolean QDECL VFSPAK_Seek (struct vfsfile_s *vfs, qofs_t pos)\n{\n\tvfspack_t *vfsp = (vfspack_t*)vfs;\n\tif (pos > vfsp->length)\n\t\treturn false;\n\tvfsp->currentpos = pos + vfsp->startpos;\n\n\treturn true;\n}\nstatic qofs_t QDECL VFSPAK_Tell (struct vfsfile_s *vfs)\n{\n\tvfspack_t *vfsp = (vfspack_t*)vfs;\n\treturn vfsp->currentpos - vfsp->startpos;\n}\nstatic qofs_t QDECL VFSPAK_GetLen (struct vfsfile_s *vfs)\n{\n\tvfspack_t *vfsp = (vfspack_t*)vfs;\n\treturn vfsp->length;\n}\nstatic qboolean QDECL VFSPAK_Close(vfsfile_t *vfs)\n{\n\tvfspack_t *vfsp = (vfspack_t*)vfs;\n\tFSPAK_ClosePath(&vfsp->parentpak->pub);\t//tell the parent that we don't need it open any more (reference counts)\n\tZ_Free(vfsp);\t//free ourselves.\n\treturn true;\n}\nstatic vfsfile_t *QDECL FSPAK_OpenVFS(searchpathfuncs_t *handle, flocation_t *loc, const char *mode)\n{\n\tpack_t *pack = (pack_t*)handle;\n\tvfspack_t *vfs;\n\n\tif (strcmp(mode, \"rb\"))\n\t\treturn NULL; //urm, unable to write/append\n\n\tvfs = Z_Malloc(sizeof(vfspack_t));\n\n\tvfs->parentpak = pack;\n\tFTE_Atomic32_Inc(&vfs->parentpak->references);\n\n\tvfs->startpos = loc->offset;\n\tvfs->length = loc->len;\n\tvfs->currentpos = vfs->startpos;\n\n#ifdef _DEBUG\n\t{\n\t\tmpackfile_t *pf = loc->fhandle;\n\t\tQ_strncpyz(vfs->funcs.dbgname, pf->name, sizeof(vfs->funcs.dbgname));\n\t}\n#endif\n\tvfs->funcs.Close = VFSPAK_Close;\n\tvfs->funcs.GetLen = VFSPAK_GetLen;\n\tvfs->funcs.ReadBytes = VFSPAK_ReadBytes;\n\tvfs->funcs.Seek = VFSPAK_Seek;\n\tvfs->funcs.Tell = VFSPAK_Tell;\n\tvfs->funcs.WriteBytes = VFSPAK_WriteBytes;\t//not supported\n\n\treturn (vfsfile_t *)vfs;\n}\n\nstatic void QDECL FSPAK_ReadFile(searchpathfuncs_t *handle, flocation_t *loc, char *buffer)\n{\n\tvfsfile_t *f;\n\tf = FSPAK_OpenVFS(handle, loc, \"rb\");\n\tif (!f)\t//err...\n\t\treturn;\n\tVFS_READ(f, buffer, loc->len);\n\tVFS_CLOSE(f);\n}\n\n\n/*\n=================\nCOM_LoadPackFile\n\nTakes an explicit (not game tree related) path to a pak file.\n\nLoads the header and directory, adding the files at the beginning\nof the list so they override previous pack files.\n=================\n*/\nsearchpathfuncs_t *QDECL FSPAK_LoadArchive (vfsfile_t *file, searchpathfuncs_t *parent, const char *filename, const char *desc, const char *prefix)\n{\n\tdpackheader_t\theader = {};\n\tint\t\t\t\ti;\n\tmpackfile_t\t\t*newfiles;\n\tint\t\t\t\tnumpackfiles;\n\tpack_t\t\t\t*pack;\n\tvfsfile_t\t\t*packhandle;\n\tdpackfile_t\t\tinfo;\n\tint read;\n\n\tpackhandle = file;\n\tif (packhandle == NULL)\n\t\treturn NULL;\n\n\tif (prefix && *prefix)\n\t\treturn NULL;\t//not supported at this time\n\n\tread = VFS_READ(packhandle, &header, sizeof(header));\n\tif (read < sizeof(header) || header.id[0] != 'P' || header.id[1] != 'A'\n\t|| header.id[2] != 'C' || header.id[3] != 'K')\n\t{\n\t\theader.dirofs = 0;\n\t\tCon_Printf(\"%s is not a pak - \\\"%s\\\"\\n\", desc, header.id);\n\t\treturn NULL;\n\t}\n\theader.dirofs = LittleLong (header.dirofs);\n\theader.dirlen = LittleLong (header.dirlen);\n\n\tnumpackfiles = header.dirlen / sizeof(dpackfile_t);\n\n\tnewfiles = (mpackfile_t*)Z_Malloc (numpackfiles * sizeof(mpackfile_t));\n\n\tVFS_SEEK(packhandle, header.dirofs);\n\n\tpack = (pack_t*)Z_Malloc (sizeof (pack_t));\n// parse the directory\n\tfor (i=0 ; i<numpackfiles ; i++)\n\t{\n\t\t*info.name = '\\0';\n\t\tread = VFS_READ(packhandle, &info, sizeof(info));\n\t\tif (read != sizeof(info))\n\t\t{\n\t\t\tCon_Printf(\"PAK file table truncated, only found %i files out of %i\\n\", i, numpackfiles);\n\t\t\tnumpackfiles = i;\n\t\t\tbreak;\n\t\t}\n\n\t\tmemcpy(newfiles[i].name, info.name, sizeof(info.name));\n\t\tnewfiles[i].name[min(sizeof(info.name), MAX_QPATH-1)] = 0; //paranoid\n\t\tCOM_CleanUpPath(newfiles[i].name);\t//blooming tanks.\n\t\tnewfiles[i].filepos = LittleLong(info.filepos);\n\t\tnewfiles[i].filelen = LittleLong(info.filelen);\n\t}\n/*\n\tif (crc != PAK0_CRC || numpackfiles != PAK0_COUNT)\n\t\tcom_modified = true;\n*/\n\tstrcpy (pack->descname, desc);\n\tpack->handle = packhandle;\n\tpack->numfiles = numpackfiles;\n\tpack->files = newfiles;\n\tpack->filepos = 0;\n\tVFS_SEEK(packhandle, pack->filepos);\n\n\tpack->references++;\n\n\tpack->mutex = Sys_CreateMutex();\n\n//\tCon_TPrintf (\"Added packfile %s (%i files)\\n\", desc, numpackfiles);\n\n\tpack->pub.fsver\t\t\t= FSVER;\n\tpack->pub.GetPathDetails = FSPAK_GetPathDetails;\n\tpack->pub.AddReference = FSPAK_AddReference;\n\tpack->pub.ClosePath = FSPAK_ClosePath;\n\tpack->pub.BuildHash = FSPAK_BuildHash;\n\tpack->pub.FindFile = FSPAK_FLocate;\n\tpack->pub.ReadFile = FSPAK_ReadFile;\n\tpack->pub.EnumerateFiles = FSPAK_EnumerateFiles;\n\tpack->pub.GeneratePureCRC = FSPAK_GeneratePureCRC;\n\tpack->pub.OpenVFS = FSPAK_OpenVFS;\n\treturn &pack->pub;\n}\n\n#ifdef PACKAGE_DOOMWAD\nsearchpathfuncs_t *QDECL FSDWD_LoadArchive (vfsfile_t *packhandle, searchpathfuncs_t *parent, const char *wadname, const char *desc, const char *prefix)\n{\n\tdwadheader_t\theader;\n\tint\t\t\t\ti;\n\tmpackfile_t\t\t*newfiles;\n\tint\t\t\t\tnumpackfiles;\n\tpack_t\t\t\t*pack;\n\tdwadfile_t\t\tinfo;\n\n\tint section=0;\n\tchar sectionname[MAX_QPATH];\n\tchar filename[52];\n\tchar neatwadname[52];\n\n\tif (packhandle == NULL)\n\t\treturn NULL;\n\n\tif (prefix && *prefix)\n\t\treturn NULL;\t//not supported at this time\n\n\tVFS_READ(packhandle, &header, sizeof(header));\n\tif (header.id[1] != 'W'\t|| header.id[2] != 'A' || header.id[3] != 'D')\n\t\treturn NULL;\t//not a doom wad\n\n\t//doom wads come in two sorts. iwads and pwads.\n\t//iwads are the master wads, pwads are meant to replace parts of the master wad.\n\t//this is awkward, of course.\n\t//we ignore the i/p bit for the most part, but with maps, pwads are given a prefixed name.\n\tif (header.id[0] == 'I')\n\t\t*neatwadname = '\\0';\n\telse if (header.id[0] == 'P')\n\t{\n\t\tCOM_FileBase(desc, neatwadname, sizeof(neatwadname));\n\t\tstrcat(neatwadname, \"#\");\n\t}\n\telse\n\t\treturn NULL;\n\n\theader.dirofs = LittleLong (header.dirofs);\n\theader.dirlen = LittleLong (header.dirlen);\n\n\tnumpackfiles = header.dirlen;\n\tnewfiles = (mpackfile_t*)Z_Malloc (numpackfiles * sizeof(mpackfile_t));\n\tVFS_SEEK(packhandle, header.dirofs);\n\n\t//doom wads are awkward.\n\t//they have no directory structure, except for start/end 'files'.\n\t//they follow along the lines of lumps after the parent name.\n\t//a map is the name of that map, and then a squence of the lumps that form that map (found by next-with-that-name).\n\t//this is a problem for a real virtual filesystem, so we add a hack to recognise special names and expand them specially.\n\tfor (i=0 ; i<numpackfiles ; i++)\n\t{\n\t\tVFS_READ (packhandle, &info, sizeof(info));\n\n\t\tstrcpy (filename, info.name);\n\t\tfilename[8] = '\\0';\n\t\tQ_strlwr(filename);\n\n\t\tnewfiles[i].filepos = LittleLong(info.filepos);\n\t\tnewfiles[i].filelen = LittleLong(info.filelen);\n\n\t\tswitch(section)\t//be prepared to remap filenames.\n\t\t{\nnewsection:\n\t\tcase 0:\n\t\t\tif (info.filelen == 0)\n\t\t\t{\t//marker for something...\n\n\t\t\t\tif (!strcmp(filename, \"s_start\"))\n\t\t\t\t{\n\t\t\t\t\tsection = 2;\n\t\t\t\t\tsprintf (newfiles[i].name, \"sprites/%s\", filename);\t//the model loader has a hack to recognise .dsp\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tif (!strcmp(filename, \"p_start\"))\n\t\t\t\t{\n\t\t\t\t\tsection = 3;\n\t\t\t\t\tsprintf (newfiles[i].name, \"patches/%s.pat\", filename); //the map loader will find these.\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tif (!strcmp(filename, \"f_start\"))\n\t\t\t\t{\n\t\t\t\t\tsection = 4;\n\t\t\t\t\tsprintf (newfiles[i].name, \"flats/%s\", filename);\t//the map loader will find these\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tif ((filename[0] == 'e' && filename[2] == 'm') || !strncmp(filename, \"map\", 3))\n\t\t\t\t{\t//this is the start of a beutiful new map\n\t\t\t\t\tsection = 1;\n\t\t\t\t\tstrcpy(sectionname, filename);\n\t\t\t\t\tsprintf (newfiles[i].name, \"maps/%s%s.bsp\", neatwadname, filename);\t//generate fake bsps to allow the server to find them\n\t\t\t\t\tnewfiles[i].filepos = 0;\n\t\t\t\t\tnewfiles[i].filelen = 4;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tif (!Q_strncmp(filename, \"gl_\", 3) && ((filename[3] == 'e' && filename[5] == 'm') || !strncmp(filename+3, \"map\", 3)))\n\t\t\t\t{\t//this is the start of a beutiful new map\n\t\t\t\t\tsection = 5;\n\t\t\t\t\tstrcpy(sectionname, filename+3);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tsprintf (newfiles[i].name, \"wad/%s\", filename);\t//but there are many files that we don't recognise/know about. archive them off to keep the vfs moderatly clean.\n\t\t\tbreak;\n\t\tcase 1:\t//map section\n\t\t\tif (strcmp(filename, \"things\") &&\n\t\t\t\tstrcmp(filename, \"linedefs\") &&\n\t\t\t\tstrcmp(filename, \"sidedefs\") &&\n\t\t\t\tstrcmp(filename, \"vertexes\") &&\n\t\t\t\tstrcmp(filename, \"segs\") &&\n\t\t\t\tstrcmp(filename, \"ssectors\") &&\n\t\t\t\tstrcmp(filename, \"nodes\") &&\n\t\t\t\tstrcmp(filename, \"sectors\") &&\n\t\t\t\tstrcmp(filename, \"reject\") &&\n\t\t\t\tstrcmp(filename, \"blockmap\"))\n\t\t\t{\n\t\t\t\tsection = 0;\n\t\t\t\tgoto newsection;\n\t\t\t}\n\t\t\tsprintf (newfiles[i].name, \"maps/%s%s.%s\", neatwadname, sectionname, filename);\n\t\t\tbreak;\n\t\tcase 5:\t//glbsp output section\n\t\t\tif (strcmp(filename, \"gl_vert\") &&\n\t\t\t\tstrcmp(filename, \"gl_segs\") &&\n\t\t\t\tstrcmp(filename, \"gl_ssect\") &&\n\t\t\t\tstrcmp(filename, \"gl_pvs\") &&\n\t\t\t\tstrcmp(filename, \"gl_nodes\"))\n\t\t\t{\n\t\t\t\tsection = 0;\n\t\t\t\tgoto newsection;\n\t\t\t}\n\t\t\tsprintf (newfiles[i].name, \"maps/%s%s.%s\", neatwadname, sectionname, filename);\n\t\t\tbreak;\n\t\tcase 2:\t//sprite section. sprites use the first 4 letters to identify the name of the sprite, and the last 4 to define its frame+dir [+ xflipped frame+dir]\n\t\t\t//FIXME: inject a '.dsp' file for filesystem accountability when we see an a0 (or a1) postfix.\n\t\t\tif (!strcmp(filename, \"s_end\"))\n\t\t\t{\n\t\t\t\tsection = 0;\n\t\t\t\tgoto newsection;\n\t\t\t}\n\t\t\tsprintf (newfiles[i].name, \"sprites/%s\", filename);\n\t\t\tbreak;\n\t\tcase 3:\t//patches section. they need the textures1/2 lump in order to position correctly as actual textures.\n\t\t\tif (!strcmp(filename, \"p_end\"))\n\t\t\t{\n\t\t\t\tsection = 0;\n\t\t\t\tgoto newsection;\n\t\t\t}\n\t\t\tsprintf (newfiles[i].name, \"patches/%s.pat\", filename);\n\t\t\tbreak;\n\t\tcase 4:\t//flats section. note that these are raw p8 64*64 images\n\t\t\tif (!strcmp(filename, \"f_end\"))\n\t\t\t{\n\t\t\t\tsection = 0;\n\t\t\t\tgoto newsection;\n\t\t\t}\n\t\t\tsprintf (newfiles[i].name, \"flats/%s.raw\", filename);\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tpack = (pack_t*)Z_Malloc (sizeof (pack_t));\n\tstrcpy (pack->descname, desc);\n\tpack->handle = packhandle;\n\tpack->numfiles = numpackfiles;\n\tpack->files = newfiles;\n\tpack->filepos = 0;\n\tVFS_SEEK(packhandle, pack->filepos);\n\n\tpack->references++;\n\n\tCon_TPrintf (\"Added packfile %s (%i files)\\n\", desc, numpackfiles);\n\n\tpack->mutex = Sys_CreateMutex();\n\n\tpack->pub.fsver\t\t\t= FSVER;\n\tpack->pub.GetPathDetails = FSPAK_GetPathDetails;\n\tpack->pub.ClosePath = FSPAK_ClosePath;\n\tpack->pub.BuildHash = FSPAK_BuildHash;\n\tpack->pub.FindFile = FSPAK_FLocate;\n\tpack->pub.ReadFile = FSPAK_ReadFile;\n\tpack->pub.EnumerateFiles = FSPAK_EnumerateFiles;\n\tpack->pub.GeneratePureCRC = FSPAK_GeneratePureCRC;\n\tpack->pub.OpenVFS = FSPAK_OpenVFS;\n\treturn &pack->pub;\n}\n#endif\n#endif\n"
  },
  {
    "path": "engine/common/fs_stdio.c",
    "content": "#include \"quakedef.h\"\n#include \"fs.h\"\n#include \"errno.h\"\n#if _POSIX_C_SOURCE >= 200112L\n#include <sys/stat.h>\n#endif\n\n#if !defined(FTE_TARGET_WEB) && (!defined(_WIN32) || defined(WEBSVONLY))\n\n#ifdef WEBSVONLY\n\t#define Z_Free free\n\t#define Z_Malloc malloc\n\t#define Con_Printf printf\n\t#define fs_readonly true\n\t#define FS_FlushFSHashFull()\n\tint Sys_EnumerateFiles (const char *gpath, const char *match, int (QDECL *func)(const char *fname, qofs_t fsize, time_t modtime, void *parm, searchpathfuncs_t *spath), void *parm, searchpathfuncs_t *spath) {return 0;}\n#else\n\t#if !defined(_WIN32) || defined(FTE_SDL) || defined(WINRT) || defined(_XBOX)\n\t\t#define FSSTDIO_OpenPath VFSOS_OpenPath\n\t#endif\n\t#define FSSTDIO_OpenTemp FS_OpenTemp\n#endif\n\ntypedef struct {\n\tsearchpathfuncs_t pub;\n\tint depth;\n\tvoid (QDECL *AddFileHash)(int depth, const char *fname, fsbucket_t *filehandle, void *pathhandle);\n\tchar rootpath[1];\n} stdiopath_t;\ntypedef struct {\n\tvfsfile_t funcs;\n\tFILE *handle;\n} vfsstdiofile_t;\nstatic int QDECL VFSSTDIO_ReadBytes (struct vfsfile_s *file, void *buffer, int bytestoread)\n{\n\tvfsstdiofile_t *intfile = (vfsstdiofile_t*)file;\n\treturn fread(buffer, 1, bytestoread, intfile->handle);\n}\nstatic int QDECL VFSSTDIO_WriteBytes (struct vfsfile_s *file, const void *buffer, int bytestoread)\n{\n\tvfsstdiofile_t *intfile = (vfsstdiofile_t*)file;\n\treturn fwrite(buffer, 1, bytestoread, intfile->handle);\n}\nstatic qboolean QDECL VFSSTDIO_Seek (struct vfsfile_s *file, qofs_t pos)\n{\n\tvfsstdiofile_t *intfile = (vfsstdiofile_t*)file;\n#ifdef __USE_LARGEFILE64\n\treturn fseeko64(intfile->handle, (off64_t)pos, SEEK_SET) == 0;\n#elif _POSIX_C_SOURCE >= 200112L\n\treturn fseeko(intfile->handle, (off_t)pos, SEEK_SET) == 0;\n#else\n\treturn fseek(intfile->handle, pos, SEEK_SET) == 0;\n#endif\n}\nstatic qofs_t QDECL VFSSTDIO_Tell (struct vfsfile_s *file)\n{\n\tvfsstdiofile_t *intfile = (vfsstdiofile_t*)file;\n#ifdef __USE_LARGEFILE64\n\treturn (qofs_t)ftello64(intfile->handle);\n#elif _POSIX_C_SOURCE >= 200112L\n\treturn (qofs_t)ftello(intfile->handle);\n#else\n\treturn ftell(intfile->handle);\n#endif\n}\nstatic void QDECL VFSSTDIO_Flush(struct vfsfile_s *file)\n{\n\tvfsstdiofile_t *intfile = (vfsstdiofile_t*)file;\n\tfflush(intfile->handle);\n}\nstatic qofs_t QDECL VFSSTDIO_GetSize (struct vfsfile_s *file)\n{\n\tvfsstdiofile_t *intfile = (vfsstdiofile_t*)file;\n\n#ifdef __USE_LARGEFILE64\n\toff64_t curpos;\n\tqofs_t maxlen;\n\tcurpos = ftello64(intfile->handle);\n\tfseeko64(intfile->handle, 0, SEEK_END);\n\tmaxlen = (qofs_t)ftello64(intfile->handle);\n\tfseeko64(intfile->handle, curpos, SEEK_SET);\n#elif _POSIX_C_SOURCE >= 200112L\n\toff_t curpos;\n\tqofs_t maxlen;\n\tcurpos = ftello(intfile->handle);\n\tfseeko(intfile->handle, 0, SEEK_END);\n\tmaxlen = (qofs_t)ftello(intfile->handle);\n\tfseeko(intfile->handle, curpos, SEEK_SET);\n#else\n\tunsigned int curpos;\n\tunsigned int maxlen;\n\tcurpos = ftell(intfile->handle);\n\tfseek(intfile->handle, 0, SEEK_END);\n\tmaxlen = ftell(intfile->handle);\n\tfseek(intfile->handle, curpos, SEEK_SET);\n#endif\n\n\treturn maxlen;\n}\nstatic qboolean QDECL VFSSTDIO_Close(vfsfile_t *file)\n{\n\tqboolean success;\n\tvfsstdiofile_t *intfile = (vfsstdiofile_t*)file;\n\tsuccess = !ferror(intfile->handle);\n\tfclose(intfile->handle);\n\tZ_Free(file);\n\treturn success;\n}\n\n#ifdef _WIN32\nstatic qboolean QDECL VFSSTDIO_CloseTemp(vfsfile_t *file)\n{\n\tqboolean success;\n\tvfsstdiofile_t *intfile = (vfsstdiofile_t*)file;\n\tchar *fname = (char*)(intfile+1); \n\tsuccess = !ferror(intfile->handle);\n\tfclose(intfile->handle);\n\t_unlink(fname);\n\tZ_Free(file);\n\treturn success;\n}\n#endif\n\nvfsfile_t *FSSTDIO_OpenTemp(void)\n{\n\tFILE *f;\n\tvfsstdiofile_t *file;\n\n#ifdef _WIN32\n\t/*microsoft's tmpfile will nearly always fail, as it insists on writing to the root directory and that requires running everything with full admin rights.\n\twarning: there's a race condition between tempnam and fopen. if the file is not opened exclusively then we can end up with issues\n\ton windows, fopen is typically exclusive anyway, but not on unix. but on unix, tmpfile is actually usable, so special-case the windows code\n\twe also have a special close function to ensure the file is deleted too\n\t*/\n\tchar *fname = _tempnam(NULL, \"ftemp\");\n\tf = fopen(fname, \"w+b\");\n\tif (!f)\n\t\treturn NULL;\n\n\tfile = Z_Malloc(sizeof(vfsstdiofile_t) + strlen(fname)+1);\n\tfile->funcs.Close = VFSSTDIO_CloseTemp;\n\tstrcpy((char*)(file+1), fname);\n\tfree(fname);\n#else\n#ifdef __USE_LARGEFILE64\n\tf = tmpfile64();\n#else\n\tf = tmpfile();\n#endif\n\tif (!f)\n\t\treturn NULL;\n\n\tfile = Z_Malloc(sizeof(vfsstdiofile_t));\n\tfile->funcs.Close = VFSSTDIO_Close;\n#endif\n#ifdef _DEBUG\n\tQ_strncpyz(file->funcs.dbgname, \"FSSTDIO_OpenTemp\", sizeof(file->funcs.dbgname));\n#endif\n\tfile->funcs.ReadBytes = VFSSTDIO_ReadBytes;\n\tfile->funcs.WriteBytes = VFSSTDIO_WriteBytes;\n\tfile->funcs.Seek = VFSSTDIO_Seek;\n\tfile->funcs.Tell = VFSSTDIO_Tell;\n\tfile->funcs.GetLen = VFSSTDIO_GetSize;\n\tfile->funcs.Flush = VFSSTDIO_Flush;\n\tfile->handle = f;\n\n\treturn (vfsfile_t*)file;\n}\n\n#if 0//def ANDROID\nvfsfile_t *Sys_OpenAsset(const char *fname);\n#endif\n\nvfsfile_t *VFSSTDIO_Open(const char *osname, const char *mode, qboolean *needsflush)\n{\n\tFILE *f;\n\tvfsstdiofile_t *file;\n\tqboolean read = !!strchr(mode, 'r');\n\tqboolean write = !!strchr(mode, 'w');\n\tqboolean append = !!strchr(mode, 'a');\n\tqboolean text = !!strchr(mode, 't');\n//\tqboolean dolock = !!strchr(mode, 'l');\n\tchar newmode[5];\n\tint modec = 0;\n\n\tif (needsflush)\n\t\t*needsflush = false;\n\n#if 0//def ANDROID\n//\tif (!strncmp(\"asset/\", osname, 6))\n\t{\n\t\tif (append || write)\n\t\t\treturn NULL;\n\t\treturn Sys_OpenAsset(osname);\n\t}\n#endif\n\n\tif (read)\n\t\tnewmode[modec++] = 'r';\n\tif (write)\n\t\tnewmode[modec++] = 'w';\n\tif (append)\n\t\tnewmode[modec++] = 'a';\n//\tif (append)\n//\t\tnewmode[modec++] = '+';\n\tif (text)\n\t\tnewmode[modec++] = 't';\n\telse\n\t\tnewmode[modec++] = 'b';\n#ifdef __linux__\n\tnewmode[modec++] = 'e';\t//otherwise forks get messy.\n#endif\n\tnewmode[modec++] = '\\0';\n\n#ifdef __USE_LARGEFILE64\n\tf = fopen64(osname, newmode);\n#else\n\tf = fopen(osname, newmode);\n#endif\n\tif (!f)\n\t\treturn NULL;\n\n\tif (write || append)\n\t{\n\t\tif (needsflush)\n\t\t\t*needsflush = true;\n\t}\n\n\tfile = Z_Malloc(sizeof(vfsstdiofile_t));\n#ifdef _DEBUG\n\tQ_strncpyz(file->funcs.dbgname, osname, sizeof(file->funcs.dbgname));\n#endif\n\tfile->funcs.ReadBytes = VFSSTDIO_ReadBytes;\n\tfile->funcs.WriteBytes = VFSSTDIO_WriteBytes;\n\tfile->funcs.Seek = VFSSTDIO_Seek;\n\tfile->funcs.Tell = VFSSTDIO_Tell;\n\tfile->funcs.GetLen = VFSSTDIO_GetSize;\n\tfile->funcs.Close = VFSSTDIO_Close;\n\tfile->funcs.Flush = VFSSTDIO_Flush;\n\tfile->handle = f;\n\n\treturn (vfsfile_t*)file;\n}\n\n#if !defined(_WIN32) || defined(WINRT) || defined(_XBOX)\nvfsfile_t *VFSOS_Open(const char *osname, const char *mode)\n{\n\tvfsfile_t *f;\n\tqboolean needsflush;\n\tf = VFSSTDIO_Open(osname, mode, &needsflush);\n\tif (needsflush)\n\t\tFS_FlushFSHashFull();\n\treturn f;\n}\n#endif\n\nstatic vfsfile_t *QDECL FSSTDIO_OpenVFS(searchpathfuncs_t *handle, flocation_t *loc, const char *mode)\n{\n\tvfsfile_t *f;\n\tstdiopath_t *sp = (void*)handle;\n\tqboolean needsflush;\n\n\tf = VFSSTDIO_Open(loc->rawname, mode, &needsflush);\n\tif (needsflush && sp->AddFileHash)\n\t\tsp->AddFileHash(sp->depth, loc->rawname, NULL, sp);\n\treturn f;\n}\n\nstatic void QDECL FSSTDIO_ClosePath(searchpathfuncs_t *handle)\n{\n\tZ_Free(handle);\n}\nstatic qboolean QDECL FSSTDIO_PollChanges(searchpathfuncs_t *handle)\n{\n//\tstdiopath_t *np = handle;\n\treturn true;\t//can't verify that or not, so we have to assume the worst\n}\nstatic int QDECL FSSTDIO_RebuildFSHash(const char *filename, qofs_t filesize, time_t mtime, void *data, searchpathfuncs_t *spath)\n{\n\tstdiopath_t *sp = (void*)spath;\n\tvoid (QDECL *AddFileHash)(int depth, const char *fname, fsbucket_t *filehandle, void *pathhandle) = data;\n\tif (filename[strlen(filename)-1] == '/')\n\t{\t//this is actually a directory\n\n\t\tchar childpath[256];\n\t\tif (!Q_snprintfz(childpath, sizeof(childpath), \"%s*\", filename))\n\t\t\tSys_EnumerateFiles(sp->rootpath, childpath, FSSTDIO_RebuildFSHash, data, spath);\n\t\treturn true;\n\t}\n\tAddFileHash(sp->depth, filename, NULL, sp);\n\treturn true;\n}\nstatic void QDECL FSSTDIO_BuildHash(searchpathfuncs_t *handle, int depth, void (QDECL *AddFileHash)(int depth, const char *fname, fsbucket_t *filehandle, void *pathhandle))\n{\n\tstdiopath_t *sp = (void*)handle;\n\tsp->depth = depth;\n\tsp->AddFileHash = AddFileHash;\n\tSys_EnumerateFiles(sp->rootpath, \"*\", FSSTDIO_RebuildFSHash, AddFileHash, handle);\n}\n\nstatic unsigned int QDECL FSSTDIO_CreateLoc(searchpathfuncs_t *handle, flocation_t *loc, const char *filename)\n{\n\tstdiopath_t *sp = (void*)handle;\n\tchar\t*ofs;\n\n\tif (fs_readonly)\n\t\treturn FF_NOTFOUND;\n\n\tloc->len = 0;\n\tloc->offset = 0;\n\tloc->fhandle = handle;\n\tif (Q_snprintfz(loc->rawname, sizeof(loc->rawname), \"%s/%s\", sp->rootpath, filename))\n\t\treturn FF_NOTFOUND;\t//too long...\n\n\tfor (ofs = loc->rawname+1 ; *ofs ; ofs++)\n\t{\n\t\tif (*ofs == '/')\n\t\t{\t// create the directory\n\t\t\t*ofs = 0;\n\t\t\tSys_mkdir (loc->rawname);\n\t\t\t*ofs = '/';\n\t\t}\n\t}\n\n\treturn FF_FOUND;\n}\nstatic unsigned int QDECL FSSTDIO_FLocate(searchpathfuncs_t *handle, flocation_t *loc, const char *filename, void *hashedresult)\n{\n\tstdiopath_t *sp = (void*)handle;\n\tqofs_t len;\n\tchar netpath[MAX_OSPATH];\n\n\tif (hashedresult && (void *)hashedresult != handle)\n\t\treturn FF_NOTFOUND;\n\n/*\n\tif (!static_registered)\n\t{\t// if not a registered version, don't ever go beyond base\n\t\tif ( strchr (filename, '/') || strchr (filename,'\\\\'))\n\t\t\tcontinue;\n\t}\n*/\n\n// check a file in the directory tree\n\tif (Q_snprintfz (netpath, sizeof(netpath), \"%s/%s\", sp->rootpath, filename))\n\t\treturn FF_NOTFOUND;\n\n#if _POSIX_C_SOURCE >= 200112L\n\t{\n\t\tstruct stat sb;\n\t\tif (stat(netpath, &sb) == 0)\n\t\t{\n\t\t\tlen = sb.st_size;\n\t\t\tif ((sb.st_mode & S_IFMT) != S_IFREG)\n\t\t\t\treturn FF_NOTFOUND; //no directories nor sockets! boo! (any simlink will already have been resolved)\n\t\t}\n\t\telse\n\t\t\treturn FF_NOTFOUND; //some kind of screwup.\n\t}\n#elif 0//defined(ANDROID)\n\t{\n\t\tvfsfile_t *f = VFSSTDIO_Open(netpath, \"rb\", NULL);\n\t\tif (!f)\n\t\t\treturn false;\n\t\tlen = VFS_GETLEN(f);\n\t\tVFS_CLOSE(f);\n\t}\n#else\n\t{\n#ifdef __USE_LARGEFILE64\n\t\tFILE *f = fopen64(netpath, \"rb\");\n#else\n\t\tFILE *f = fopen(netpath, \"rb\");\n#endif\n\t\tif (!f)\n\t\t\treturn FF_NOTFOUND;\n\n#ifdef __USE_LARGEFILE64\n\t\tfseeko64(f, 0, SEEK_END);\n\t\tlen = ftello64(f);\n#elif _POSIX_C_SOURCE >= 200112L\n\t\tfseeko(f, 0, SEEK_END);\n\t\tlen = ftello(f);\n#else\n\t\tfseek(f, 0, SEEK_END);\n\t\tlen = ftell(f);\n#endif\n\t\tfclose(f);\n\t}\n#endif\n\tif (loc)\n\t{\n\t\tloc->len = len;\n\t\tloc->offset = 0;\n\t\tloc->fhandle = handle;\n\t\tQ_strncpyz(loc->rawname, netpath, sizeof(loc->rawname));\n\t}\n\treturn FF_FOUND;\n}\nstatic void QDECL FSSTDIO_ReadFile(searchpathfuncs_t *handle, flocation_t *loc, char *buffer)\n{\n\tFILE *f;\n\tsize_t result;\n\n#ifdef __USE_LARGEFILE64\n\tf = fopen64(loc->rawname, \"rb\");\n#else\n\tf = fopen(loc->rawname, \"rb\");\n#endif\n\tif (!f)\t//err...\n\t\treturn;\n#ifdef __USE_LARGEFILE64\n\tfseeko64(f, loc->offset, SEEK_SET);\n#elif _POSIX_C_SOURCE >= 200112L\n\tfseeko(f, loc->offset, SEEK_SET);\n#else\n\tfseek(f, loc->offset, SEEK_SET);\n#endif\n\tresult = fread(buffer, 1, loc->len, f); // do soemthing with result\n\n\tif (result != loc->len)\n\t\tCon_Printf(\"FSSTDIO_ReadFile() fread: Filename: %s, expected %u, result was %u (%s)\\n\",loc->rawname,(unsigned int)loc->len,(unsigned int)result,strerror(errno));\n\n\tfclose(f);\n}\nstatic int QDECL FSSTDIO_EnumerateFiles (searchpathfuncs_t *handle, const char *match, int (QDECL *func)(const char *, qofs_t, time_t mtime, void *, searchpathfuncs_t *spath), void *parm)\n{\n\tstdiopath_t *sp = (stdiopath_t*)handle;\n\treturn Sys_EnumerateFiles(sp->rootpath, match, func, parm, handle);\n}\n\n#include <sys/stat.h>\nstatic qboolean QDECL FSSTDIO_FileStat (searchpathfuncs_t *handle, flocation_t *loc, time_t *mtime)\n{\n\tstruct stat s;\n\tif (stat(loc->rawname, &s) != -1)\n\t{\n\t\t*mtime = s.st_mtime;\n\t\treturn true;\n\t}\n\treturn false;\n}\n\nstatic qboolean\t QDECL FSSTDIO_RenameFile(searchpathfuncs_t *handle, const char *oldname, const char *newname)\n{\n\tstdiopath_t *sp = (void*)handle;\n\tchar oldsyspath[MAX_OSPATH];\n\tchar newsyspath[MAX_OSPATH];\n\tif (fs_readonly)\n\t\treturn false;\n\tif (Q_snprintfz (oldsyspath, sizeof(oldsyspath), \"%s/%s\", sp->rootpath, oldname))\n\t\treturn false;\t//too long\n\tif (Q_snprintfz (newsyspath, sizeof(newsyspath), \"%s/%s\", sp->rootpath, newname))\n\t\treturn false;\t//too long\n\treturn Sys_Rename(oldsyspath, newsyspath);\n}\nstatic qboolean QDECL FSSTDIO_RemoveFile(searchpathfuncs_t *handle, const char *filename)\n{\n\tstdiopath_t *sp = (void*)handle;\n\tchar syspath[MAX_OSPATH];\n\tif (fs_readonly)\n\t\treturn false;\n\tif (Q_snprintfz (syspath, sizeof(syspath), \"%s/%s\", sp->rootpath, filename))\n\t\treturn false;\t//too long\n\tif (*filename && filename[strlen(filename)-1] == '/')\n\t\treturn Sys_rmdir(syspath);\n\treturn Sys_remove(syspath);\n}\n\nsearchpathfuncs_t *QDECL FSSTDIO_OpenPath(vfsfile_t *mustbenull, searchpathfuncs_t *parent, const char *filename, const char *desc, const char *prefix)\n{\n\tstdiopath_t *np;\n\tint dlen = strlen(desc);\n\tif (mustbenull)\n\t\treturn NULL;\n\tif (prefix && *prefix)\n\t\treturn NULL;\t//don't try to support this. too risky with absolute paths etc.\n\tnp = Z_Malloc(sizeof(*np) + dlen);\n\tif (np)\n\t{\n\t\tnp->depth = 0;\n\t\tmemcpy(np->rootpath, desc, dlen+1);\n\t}\n\n\tnp->pub.fsver\t\t\t= FSVER;\n\tnp->pub.ClosePath\t\t= FSSTDIO_ClosePath;\n\tnp->pub.BuildHash\t\t= FSSTDIO_BuildHash;\n\tnp->pub.FindFile\t\t= FSSTDIO_FLocate;\n\tnp->pub.ReadFile\t\t= FSSTDIO_ReadFile;\n\tnp->pub.EnumerateFiles\t= FSSTDIO_EnumerateFiles;\n\tnp->pub.OpenVFS\t\t\t= FSSTDIO_OpenVFS;\n\tnp->pub.PollChanges\t\t= FSSTDIO_PollChanges;\n\tnp->pub.FileStat\t\t= FSSTDIO_FileStat;\n\tnp->pub.CreateFile\t\t= FSSTDIO_CreateLoc;\n\tnp->pub.RenameFile\t\t= FSSTDIO_RenameFile;\n\tnp->pub.RemoveFile\t\t= FSSTDIO_RemoveFile;\n\treturn &np->pub;\n}\n\n#endif\n\n"
  },
  {
    "path": "engine/common/fs_win32.c",
    "content": "#include \"quakedef.h\"\n#include \"fs.h\"\n#include \"winquake.h\"\n\n#ifdef FTE_SDL\n#define WinNT true\n#endif\n\n//FIXME: find somewhere better for this win32 utility code.\n//(its here instead of sys_win.c because dedicated servers don't use sys_win.c)\n\n//outlen is the size of out in _BYTES_.\nwchar_t *widen(wchar_t *out, size_t outbytes, const char *utf8)\n{\n\tsize_t outlen;\n\twchar_t *ret = out;\n\t//utf-8 to utf-16, not ucs-2.\n\tunsigned int codepoint;\n\tint error;\n\toutlen = outbytes/sizeof(wchar_t);\n\tif (!outlen)\n\t\treturn L\"\";\n\toutlen--;\n\twhile (*utf8)\n\t{\n\t\tcodepoint = utf8_decode(&error, utf8, (void*)&utf8);\n\t\tif (error || codepoint > 0x10FFFFu)\n\t\t\tcodepoint = 0xFFFDu;\n\t\tif (codepoint > 0xffff)\n\t\t{\n\t\t\tif (outlen < 2)\n\t\t\t\tbreak;\n\t\t\toutlen -= 2;\n\t\t\tcodepoint -= 0x10000u;\n\t\t\t*out++ = 0xD800 | (codepoint>>10);\n\t\t\t*out++ = 0xDC00 | (codepoint&0x3ff);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (outlen < 1)\n\t\t\t\tbreak;\n\t\t\toutlen -= 1;\n\t\t\t*out++ = codepoint;\n\t\t}\n\t}\n\t*out = 0;\n\treturn ret;\n}\n\nchar *narrowen(char *out, size_t outlen, wchar_t *wide)\n{\n\tchar *ret = out;\n\tint bytes;\n\tunsigned int codepoint;\n\tif (!outlen)\n\t\treturn \"\";\n\toutlen--;\n\t//utf-8 to utf-16, not ucs-2.\n\twhile (*wide)\n\t{\n\t\tcodepoint = *wide++;\n\t\tif (codepoint >= 0xD800u && codepoint <= 0xDBFFu)\n\t\t{\t//handle utf-16 surrogates\n\t\t\tif (*wide >= 0xDC00u && *wide <= 0xDFFFu)\n\t\t\t{\n\t\t\t\tcodepoint = (codepoint&0x3ff)<<10;\n\t\t\t\tcodepoint |= *wide++ & 0x3ff;\n\t\t\t}\n\t\t\telse\n\t\t\t\tcodepoint = 0xFFFDu;\n\t\t}\n\t\tbytes = utf8_encode(out, codepoint, outlen);\n\t\tif (bytes <= 0)\n\t\t\tbreak;\n\t\tout += bytes;\n\t\toutlen -= bytes;\n\t}\n\t*out = 0;\n\treturn ret;\n}\n\nint MyRegGetIntValue(void *base, const char *keyname, const char *valuename, int defaultval)\n{\n\tint result = defaultval;\n\tDWORD datalen = sizeof(result);\n\tHKEY subkey;\n\tDWORD type = REG_NONE;\n\twchar_t wide[MAX_PATH];\n\tif (RegOpenKeyExW(base, widen(wide, sizeof(wide), keyname), 0, KEY_READ, &subkey) == ERROR_SUCCESS)\n\t{\n\t\tif (ERROR_SUCCESS != RegQueryValueExW(subkey, widen(wide, sizeof(wide), valuename), NULL, &type, (void*)&result, &datalen) || type != REG_DWORD)\n\t\t\tresult = defaultval;\n\t\tRegCloseKey (subkey);\n\t}\n\treturn result;\n}\n//result is utf-8\nqboolean MyRegGetStringValue(void *base, const char *keyname, const char *valuename, void *data, size_t datalen)\n{\n\tqboolean result = false;\n\tHKEY subkey;\n\tDWORD type = REG_NONE;\n\twchar_t wide[MAX_PATH];\n\twchar_t wdata[2048];\n\tDWORD dwlen = sizeof(wdata) - sizeof(wdata[0]);\n\tif (RegOpenKeyExW(base, widen(wide, sizeof(wide), keyname), 0, KEY_READ, &subkey) == ERROR_SUCCESS)\n\t{\n\t\tresult = ERROR_SUCCESS == RegQueryValueExW(subkey, widen(wide, sizeof(wide), valuename), NULL, &type, (BYTE*)wdata, &dwlen);\n\t\tRegCloseKey (subkey);\n\t}\n\n\tif (result && (type == REG_SZ || type == REG_EXPAND_SZ))\n\t{\n\t\twdata[dwlen/sizeof(wchar_t)] = 0;\n\t\tnarrowen(data, datalen, wdata);\n\t}\n\telse\n\t\t((char*)data)[0] = 0;\n\treturn result;\n}\nqboolean MyRegGetStringValueMultiSz(void *base, const char *keyname, const char *valuename, void *data, int datalen)\n{\n\tqboolean result = false;\n\tHKEY subkey;\n\twchar_t wide[MAX_PATH];\n\tDWORD type = REG_NONE;\n\tif (RegOpenKeyExW(base, widen(wide, sizeof(wide), keyname), 0, KEY_READ, &subkey) == ERROR_SUCCESS)\n\t{\n\t\tDWORD dwlen = datalen;\n\t\tresult = ERROR_SUCCESS == RegQueryValueEx(subkey, valuename, NULL, &type, data, &dwlen);\n\t\tdatalen = dwlen;\n\t\tRegCloseKey (subkey);\n\t}\n\n\tif (type == REG_MULTI_SZ)\n\t\treturn result;\n\treturn false;\n}\n\nqboolean MyRegSetValue(void *base, const char *keyname, const char *valuename, int type, const void *data, int datalen)\n{\n\tqboolean result = false;\n\tHKEY subkey;\n\twchar_t wide[MAX_PATH];\n\twchar_t wided[2048];\n\n\tif (type == REG_SZ)\n\t{\n\t\tdata = widen(wided, sizeof(wided), data);\n\t\tdatalen = wcslen(wided)*2;\n\t}\n\n\t//'trivially' return success if its already set.\n\t//this allows success even when we don't have write access.\n\tif (RegOpenKeyExW(base, widen(wide, sizeof(wide), keyname), 0, KEY_READ, &subkey) == ERROR_SUCCESS)\n\t{\n\t\tDWORD oldtype;\n\t\tchar olddata[2048];\n\t\tDWORD olddatalen = sizeof(olddata);\n\t\tresult = ERROR_SUCCESS == RegQueryValueExW(subkey, widen(wide, sizeof(wide), valuename), NULL, &oldtype, olddata, &olddatalen);\n\t\tRegCloseKey (subkey);\n\n\t\tif (oldtype == REG_SZ || oldtype == REG_EXPAND_SZ)\n\t\t{\t//ignore any null terminators that may have come along for the ride\n\t\t\twhile(olddatalen > 1 && olddata[olddatalen-2] && olddata[olddatalen-1] == 0)\n\t\t\t\tolddatalen-=2;\n\t\t}\n\n\t\tif (result && datalen == olddatalen && type == oldtype && !memcmp(data, olddata, datalen))\n\t\t\treturn result;\n\t\tresult = false;\n\t}\n\n\tif (RegCreateKeyExW(base, widen(wide, sizeof(wide), keyname), 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &subkey, NULL) == ERROR_SUCCESS)\n\t{\n\t\tresult = ERROR_SUCCESS == RegSetValueExW(subkey, widen(wide, sizeof(wide), valuename), 0, type, data, datalen);\n\t\tRegCloseKey (subkey);\n\t}\n\treturn result;\n}\nvoid MyRegDeleteKeyValue(void *base, const char *keyname, const char *valuename)\n{\n\tHKEY subkey;\n\twchar_t wide[MAX_PATH];\n\tif (RegOpenKeyExW(base, widen(wide, sizeof(wide), keyname), 0, KEY_WRITE, &subkey) == ERROR_SUCCESS)\n\t{\n\t\tRegDeleteValueW(subkey, widen(wide, sizeof(wide), valuename));\n\t\tRegCloseKey (subkey);\n\t}\n}\n\n\n\n\n\n#ifndef WINRT\t//winrt is too annoying. lets just use stdio.\n\n#ifndef INVALID_SET_FILE_POINTER\n#define INVALID_SET_FILE_POINTER ~0\n#endif\n\n//read-only memory mapped files.\n//for write access, we use the stdio module as a fallback.\n//do you think anyone will ever notice that utf8 filenames work even in windows? probably not. oh well, worth a try.\n\n#define VFSW32_Open VFSOS_Open\n#define VFSW32_OpenPath VFSOS_OpenPath\n#define VFSW32_OpenTemp FS_OpenTemp\n\ntypedef struct {\n\tsearchpathfuncs_t pub;\n\tHANDLE changenotification;\n\tvoid (QDECL *AddFileHash)(int depth, const char *fname, fsbucket_t *filehandle, void *pathhandle);\n\tint hashdepth;\n\tchar rootpath[1];\n} vfsw32path_t;\ntypedef struct {\n\tvfsfile_t funcs;\n\tHANDLE hand;\n\tHANDLE mmh;\n\tvoid *mmap;\n\tunsigned int length;\n\tunsigned int offset;\n} vfsw32file_t;\n\nstatic int QDECL VFSW32_ReadBytes (struct vfsfile_s *file, void *buffer, int bytestoread)\n{\n\tDWORD read;\n\tvfsw32file_t *intfile = (vfsw32file_t*)file;\n\tif (intfile->mmap)\n\t{\n\t\tif (intfile->offset+bytestoread > intfile->length)\n\t\t\tbytestoread = intfile->length-intfile->offset;\n\t\tif (bytestoread < 0)\n\t\t\tbytestoread = 0;\t//shouldn't happen...\n\n\t\tmemcpy(buffer, (char*)intfile->mmap + intfile->offset, bytestoread);\n\t\tintfile->offset += bytestoread;\n\t\treturn bytestoread;\n\t}\n\tif (!ReadFile(intfile->hand, buffer, bytestoread, &read, NULL))\n\t\treturn 0;\n\treturn read;\n}\nstatic int QDECL VFSW32_WriteBytes (struct vfsfile_s *file, const void *buffer, int bytestoread)\n{\n\tDWORD written;\n\tvfsw32file_t *intfile = (vfsw32file_t*)file;\n\tif (intfile->mmap)\n\t{\n\t\tif (intfile->offset+bytestoread > intfile->length)\n\t\t\tbytestoread = intfile->length-intfile->offset;\n\n\t\tmemcpy((char*)intfile->mmap + intfile->offset, buffer, bytestoread);\n\t\tintfile->offset += bytestoread;\n\t\treturn bytestoread;\n\t}\n\n\tif (!WriteFile(intfile->hand, buffer, bytestoread, &written, NULL))\n\t{\n//\t\tDWORD err = GetLastError();\n\t\t// ERROR_INVALID_USER_BUFFER or ERROR_NOT_ENOUGH_MEMORY \n\t\treturn 0;\n\t}\n\treturn written;\n}\nstatic qboolean QDECL VFSW32_Seek (struct vfsfile_s *file, qofs_t pos)\n{\n\tDWORD hi, lo;\n\tvfsw32file_t *intfile = (vfsw32file_t*)file;\n\tif (intfile->mmap)\n\t{\n\t\tintfile->offset = pos;\n\t\treturn true;\n\t}\n\n\tlo = qofs_Low(pos);\n\thi = qofs_High(pos);\n\treturn SetFilePointer(intfile->hand, lo, &hi, FILE_BEGIN) != INVALID_SET_FILE_POINTER;\n}\nstatic qofs_t QDECL VFSW32_Tell (struct vfsfile_s *file)\n{\n\tDWORD hi = 0, lo;\n\tvfsw32file_t *intfile = (vfsw32file_t*)file;\n\tif (intfile->mmap)\n\t\treturn intfile->offset;\n\tlo = SetFilePointer(intfile->hand, 0, &hi, FILE_CURRENT);\n\treturn qofs_Make(lo,hi);\n}\nstatic void QDECL VFSW32_Flush(struct vfsfile_s *file)\n{\n\tvfsw32file_t *intfile = (vfsw32file_t*)file;\n\tif (intfile->mmap)\n\t\tFlushViewOfFile(intfile->mmap, intfile->length);\n\n\t//we only really flush things to ensure that we don't get a stall later.\n\t//in windows, FlushFileBuffers can have significant costs, so lets see if anyone complains about us not flushing.\n//\tFlushFileBuffers(intfile->hand);\n}\nstatic qofs_t QDECL VFSW32_GetSize (struct vfsfile_s *file)\n{\n\tDWORD lo, hi = 0;\n\tvfsw32file_t *intfile = (vfsw32file_t*)file;\n\n\tif (intfile->mmap)\n\t\treturn intfile->length;\n\tlo = GetFileSize(intfile->hand, &hi);\n\treturn qofs_Make(lo,hi);\n}\nstatic qboolean QDECL VFSW32_Close(vfsfile_t *file)\n{\n\tvfsw32file_t *intfile = (vfsw32file_t*)file;\n\tif (intfile->mmap)\n\t{\n\t\tUnmapViewOfFile(intfile->mmap);\n\t\tCloseHandle(intfile->mmh);\n\t}\n\tCloseHandle(intfile->hand);\n\tZ_Free(file);\n\treturn true;\n}\nstatic qboolean QDECL VFSW32_CloseTemp(vfsfile_t *file)\n{\n\tvfsw32file_t *intfile = (vfsw32file_t*)file;\n\tif (intfile->mmap)\n\t{\n\t\tUnmapViewOfFile(intfile->mmap);\n\t\tCloseHandle(intfile->mmh);\n\t}\n\tCloseHandle(intfile->hand);\n\tDeleteFileA((char*)(intfile+1));\n\tZ_Free(file);\n\treturn true;\n}\n\nvfsfile_t *QDECL VFSW32_OpenTemp(void)\n{\n\tstatic int seq=-1;\n\tHANDLE h = INVALID_HANDLE_VALUE;\n\tvfsw32file_t *file;\n\tif (WinNT)\n\t{\t//gotta use wide stuff.\n\t\t//on the plus side, FILE_SHARE_DELETE works.\n\t\twchar_t osname[MAX_PATH];\n\t\twchar_t tmppath[MAX_PATH];\n\t\tif (GetTempPathW(countof(tmppath), tmppath))\n\t\t\tif ((seq=GetTempFileNameW(tmppath, L\"fte\", ++seq, osname)))\n\t\t\t\th = CreateFileW(osname, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_DELETE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_TEMPORARY|FILE_FLAG_DELETE_ON_CLOSE, NULL);\n\t\tif (!h)\n\t\t\treturn VFSPIPE_Open(1, true);\n\t\tDeleteFileW(osname);\n\n\t\tfile = Z_Malloc(sizeof(vfsw32file_t));\n#ifdef _DEBUG\n\t\tnarrowen(file->funcs.dbgname, sizeof(file->funcs.dbgname), osname);\n#endif\n\t\tfile->funcs.Close = VFSW32_Close;\t//we already deleted it. woo.\n\t}\n\telse\n\t{\t//can't use wide stuff.\n\t\t//FLIE_SHARE_DELETE doesn't work. we have to faff around ourselves.\n\t\tchar osname[MAX_PATH];\n\t\tchar tmppath[MAX_PATH];\n\t\tif (GetTempPathA(countof(tmppath), tmppath))\n\t\t\tif ((seq=GetTempFileNameA(tmppath, \"fte\", ++seq, osname)))\n\t\t\t\th = CreateFileA(osname, GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_TEMPORARY, NULL);\n\t\tif (!h)\n\t\t\treturn VFSPIPE_Open(1, true);\n\n\t\tfile = Z_Malloc(sizeof(vfsw32file_t) + strlen(osname)+1);\n\t\tstrcpy((char*)(file+1), osname);\n#ifdef _DEBUG\n\t\tQ_strncpyz(file->funcs.dbgname, osname, sizeof(file->funcs.dbgname));\n#endif\n\t\tfile->funcs.Close = VFSW32_CloseTemp;\t//gotta delete it after close. hopefully we won't crash too often...\n\t}\n\tfile->funcs.ReadBytes = VFSW32_ReadBytes;\n\tfile->funcs.WriteBytes = VFSW32_WriteBytes;\n\tfile->funcs.Seek = VFSW32_Seek;\n\tfile->funcs.Tell = VFSW32_Tell;\n\tfile->funcs.GetLen = VFSW32_GetSize;\n\tfile->funcs.Flush = VFSW32_Flush;\n\tfile->hand = h;\n\tfile->mmh = INVALID_HANDLE_VALUE;\n\tfile->mmap = NULL;\n\tfile->offset = 0;\n\tfile->length = 0;\n\n\treturn &file->funcs;\n}\n\n//WARNING: handle can be null\nstatic vfsfile_t *QDECL VFSW32_OpenInternal(vfsw32path_t *handle, const char *quakename, const char *osname, const char *mode)\n{\n\tHANDLE h, mh;\n\tunsigned int fsize;\n\tvoid *mmap;\n\tqboolean didexist = true;\n\tqboolean create;\n\n\tvfsw32file_t *file;\n\tqboolean read = !!strchr(mode, 'r');\n\tqboolean write = !!strchr(mode, 'w');\n\tqboolean append = !!strchr(mode, 'a');\n\tqboolean text = !!strchr(mode, 't');\n\t//qboolean persistent = !!strchr(mode, 'p');\t//save to long-term storage\n\twrite |= append;\n\tcreate = write;\n\tif (strchr(mode, '+'))\n\t\tread = write = true;\n\n\tif (fs_readonly && (write || append))\n\t\treturn NULL;\n\n\tif (!WinNT)\n\t{\n\t\t//FILE_SHARE_DELETE is not supported in 9x, sorry.\n\t\tif (!create && write)\n\t\t\th = CreateFileA(osname, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ,\tNULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);\n\t\telse if ((write && read) || append)\n\t\t\th = CreateFileA(osname, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ,\tNULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);\n\t\telse if (write)\n\t\t\th = CreateFileA(osname, GENERIC_READ|GENERIC_WRITE, 0,\t\t\t\t\tNULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);\n\t\telse if (read)\n\t\t\th = CreateFileA(osname, GENERIC_READ,\t\t\t\tFILE_SHARE_READ,\tNULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);\n\t\telse\n\t\t\th = INVALID_HANDLE_VALUE;\n\t}\n\telse\n\t{\n\t\twchar_t wide[MAX_OSPATH];\n\t\twiden(wide, sizeof(wide), osname);\n\t\th = INVALID_HANDLE_VALUE;\n\t\tif (write || append)\n\t\t{\n\t\t\t//this extra block is to avoid flushing fs caches needlessly\n\t\t\th = CreateFileW(wide, GENERIC_READ|GENERIC_WRITE,\tFILE_SHARE_READ|FILE_SHARE_DELETE,\tNULL, (!read&&!append)?CREATE_ALWAYS:OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);\n\t\t\tif (h == INVALID_HANDLE_VALUE)\n\t\t\t\tdidexist = false;\n\t\t}\n\n\t\tif (h != INVALID_HANDLE_VALUE)\n\t\t\t;\n\t\telse if (!create && write)\n\t\t\th = CreateFileW(wide, GENERIC_READ|GENERIC_WRITE,\tFILE_SHARE_READ|FILE_SHARE_DELETE,\tNULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);\n\t\telse if ((write && read) || append)\n\t\t\th = CreateFileW(wide, GENERIC_READ|GENERIC_WRITE,\tFILE_SHARE_READ|FILE_SHARE_DELETE,\tNULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);\n\t\telse if (write)\n\t\t\th = CreateFileW(wide, GENERIC_READ|GENERIC_WRITE,\tFILE_SHARE_READ|FILE_SHARE_DELETE,\tNULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);\n\t\telse if (read)\n\t\t\th = CreateFileW(wide, GENERIC_READ,\t\t\t\t\tFILE_SHARE_READ|FILE_SHARE_DELETE,\tNULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);\n\t\telse\n\t\t\th = INVALID_HANDLE_VALUE;\n\t}\n\tif (h == INVALID_HANDLE_VALUE)\n\t\treturn NULL;\n\n\tif (!didexist)\n\t{\n\t\tif (handle && quakename && handle->AddFileHash)\n\t\t\thandle->AddFileHash(handle->hashdepth, quakename, NULL, handle);\n\t\telse\n\t\t\tFS_FlushFSHashFull();\t//FIXME: no idea where this path is. if its inside a quake path, make sure it gets flushed properly. FIXME: his shouldn't be needed if we have change notifications working properly.\n\t}\n\n\n\tfsize = GetFileSize(h, NULL);\n\tif (write || append || text || fsize > 1024*1024*5)\n\t{\n\t\tfsize = 0;\n\t\tmh = INVALID_HANDLE_VALUE;\n\t\tmmap = NULL;\n\n\t\t/*if appending, set the access position to the end of the file*/\n\t\tif (append)\n\t\t\tSetFilePointer(h, 0, NULL, FILE_END);\n\t}\n\telse\n\t{\n\t\tmh = CreateFileMapping(h, NULL, PAGE_READONLY, 0, 0, NULL);\n\t\tif (mh == INVALID_HANDLE_VALUE)\n\t\t\tmmap = NULL;\n\t\telse\n\t\t{\n\t\t\tmmap = MapViewOfFile(mh, FILE_MAP_READ, 0, 0, fsize);\n\t\t\tif (mmap == NULL)\n\t\t\t{\n\t\t\t\tCloseHandle(mh);\n\t\t\t\tmh = INVALID_HANDLE_VALUE;\n\t\t\t}\n\t\t}\n\t}\n\n\tfile = Z_Malloc(sizeof(vfsw32file_t));\n#ifdef _DEBUG\n\tQ_strncpyz(file->funcs.dbgname, osname, sizeof(file->funcs.dbgname));\n#endif\n\tfile->funcs.ReadBytes = read?VFSW32_ReadBytes:NULL;\n\tfile->funcs.WriteBytes = (write||append)?VFSW32_WriteBytes:NULL;\n\tfile->funcs.Seek = VFSW32_Seek;\n\tfile->funcs.Tell = VFSW32_Tell;\n\tfile->funcs.GetLen = VFSW32_GetSize;\n\tfile->funcs.Close = VFSW32_Close;\n\tfile->funcs.Flush = VFSW32_Flush;\n\tfile->hand = h;\n\tfile->mmh = mh;\n\tfile->mmap = mmap;\n\tfile->offset = 0;\n\tfile->length = fsize;\n\n\treturn &file->funcs;\n}\n\nvfsfile_t *QDECL VFSW32_Open(const char *osname, const char *mode)\n{\n\t//called without regard to a search path\n\treturn VFSW32_OpenInternal(NULL, NULL, osname, mode);\n}\n\n#include <sys/stat.h>\nstatic qboolean QDECL VFSW32_FileStat(searchpathfuncs_t *handle, flocation_t *loc, time_t *mtime)\n{\n\tint r;\n\tstruct _stat s;\n\tif (WinNT)\n\t{\n\t\twchar_t wide[MAX_OSPATH];\n\t\twiden(wide, sizeof(wide), loc->rawname);\n\t\tr = _wstat(wide, &s);\n\t}\n\telse\n\t\tr = _stat(loc->rawname, &s);\n\tif (r)\n\t\treturn false;\n\t*mtime = s.st_mtime;\n\treturn true;\n}\n\nstatic vfsfile_t *QDECL VFSW32_OpenVFS(searchpathfuncs_t *handle, flocation_t *loc, const char *mode)\n{\n\t//path is already cleaned, as anything that gets a valid loc needs cleaning up first.\n\tvfsw32path_t *wp = (void*)handle;\n\treturn VFSW32_OpenInternal(wp, loc->rawname+strlen(wp->rootpath)+1, loc->rawname, mode);\n}\nstatic void QDECL VFSW32_ClosePath(searchpathfuncs_t *handle)\n{\n\tvfsw32path_t *wp = (void*)handle;\n\tif (wp->changenotification != INVALID_HANDLE_VALUE)\n\t\tFindCloseChangeNotification(wp->changenotification);\n\tZ_Free(wp);\n}\nstatic qboolean QDECL VFSW32_PollChanges(searchpathfuncs_t *handle)\n{\n\tqboolean result = false;\n\tvfsw32path_t *wp = (void*)handle;\n\n\tif (wp->changenotification == INVALID_HANDLE_VALUE)\n\t\treturn true;\n\tfor(;;)\n\t{\n\t\tswitch(WaitForSingleObject(wp->changenotification, 0))\n\t\t{\n\t\tcase WAIT_OBJECT_0:\n\t\t\tresult = true;\n\t\t\tbreak;\n\t\tcase WAIT_TIMEOUT:\n\t\t\treturn result;\n\t\tdefault:\n\t\t\tFindCloseChangeNotification(wp->changenotification);\n\t\t\twp->changenotification = INVALID_HANDLE_VALUE;\n\t\t\treturn true;\n\t\t}\n\t\tFindNextChangeNotification(wp->changenotification);\n\t}\n\treturn result;\n}\nstatic int QDECL VFSW32_RebuildFSHash(const char *filename, qofs_t filesize, time_t mtime, void *handle, searchpathfuncs_t *spath)\n{\n\tvfsw32path_t *wp = (void*)spath;\n\tif (filename[strlen(filename)-1] == '/')\n\t{\t//this is actually a directory\n\n\t\tchar childpath[256];\n\t\tQ_snprintfz(childpath, sizeof(childpath), \"%s*\", filename);\n\t\tSys_EnumerateFiles(wp->rootpath, childpath, VFSW32_RebuildFSHash, handle, spath);\n\t\treturn true;\n\t}\n\n\twp->AddFileHash(wp->hashdepth, filename, NULL, wp);\n\treturn true;\n}\nstatic void QDECL VFSW32_BuildHash(searchpathfuncs_t *handle, int hashdepth, void (QDECL *AddFileHash)(int depth, const char *fname, fsbucket_t *filehandle, void *pathhandle))\n{\n\tvfsw32path_t *wp = (void*)handle;\n\twp->AddFileHash = AddFileHash;\n\twp->hashdepth = hashdepth;\n\tSys_EnumerateFiles(wp->rootpath, \"*\", VFSW32_RebuildFSHash, AddFileHash, handle);\n}\nstatic qboolean QDECL VFSW32_CreateLoc(searchpathfuncs_t *handle, flocation_t *loc, const char *filename)\n{\n\tvfsw32path_t *wp = (void*)handle;\n\tchar *ofs;\n\n\tloc->len = 0;\n\tloc->offset = 0;\n\tloc->fhandle = handle;\n\tloc->rawname[sizeof(loc->rawname)-1] = 0;\n\tif (Q_snprintfz (loc->rawname, sizeof(loc->rawname), \"%s%s\", wp->rootpath, filename))\n\t\treturn FF_NOTFOUND;\n\tfor (ofs = loc->rawname+1 ; *ofs ; ofs++)\n\t{\n\t\tif (*ofs == '/')\n\t\t{\t// create the directory\n\t\t\t*ofs = 0;\n\t\t\tSys_mkdir (loc->rawname);\n\t\t\t*ofs = '/';\n\t\t}\n\t}\n\n\treturn FF_FOUND;\n}\n\n#include <errno.h>\nstatic unsigned int QDECL VFSW32_FLocate(searchpathfuncs_t *handle, flocation_t *loc, const char *filename, void *hashedresult)\n{\n\tvfsw32path_t *wp = (void*)handle;\n\tchar netpath[MAX_OSPATH];\n\twchar_t wide[MAX_OSPATH];\n\tqofs_t len;\n\tHANDLE h;\n\tDWORD attr;\n\n\n\tif (hashedresult && (void *)hashedresult != wp)\n\t\treturn FF_NOTFOUND;\n\n/*\n\tif (!static_registered)\n\t{\t// if not a registered version, don't ever go beyond base\n\t\tif ( strchr (filename, '/') || strchr (filename,'\\\\'))\n\t\t\tcontinue;\n\t}\n*/\n\n// check a file in the directory tree\n\tif (Q_snprintfz (netpath, sizeof(netpath), \"%s%s\", wp->rootpath, filename))\n\t\treturn FF_NOTFOUND;\n\n\tif (!WinNT)\n\t{\n\t\tWIN32_FIND_DATAA fda;\n\t\th = FindFirstFileA(netpath, &fda);\n\t\tattr = fda.dwFileAttributes;\n\t\tlen = (h == INVALID_HANDLE_VALUE)?0:qofs_Make(fda.nFileSizeLow, fda.nFileSizeHigh);\n\t}\n\telse\n\t{\n\t\tWIN32_FIND_DATAW fdw;\n\t\th = FindFirstFileW(widen(wide, sizeof(wide), netpath), &fdw);\n\t\tattr = fdw.dwFileAttributes;\n\t\tlen = (h == INVALID_HANDLE_VALUE)?0:qofs_Make(fdw.nFileSizeLow, fdw.nFileSizeHigh);\n\t}\n\tif (h == INVALID_HANDLE_VALUE)\n\t{\n\t//\tint e = GetLastError();\n\t//  if (e == ERROR_PATH_NOT_FOUND)\t//then look inside a zip\n\t\treturn FF_NOTFOUND;\n\t}\n\tFindClose(h);\n\tif (loc)\n\t{\n\t\tif (attr & FILE_ATTRIBUTE_REPARSE_POINT)\n\t\t{\t//when looking for reparse points, FindFirstFile only reports info about the link, not the file. which means the size is wrong.\n\t\t\tHANDLE f = CreateFileW(widen(wide, sizeof(wide), netpath), 0, FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);\n\t\t\tif (f)\n\t\t\t{\n\t\t\t\tLARGE_INTEGER wsize;\n\t\t\t\twsize.LowPart = GetFileSize(f, &wsize.HighPart);\n\t\t\t\tif (wsize.LowPart == INVALID_FILE_SIZE && GetLastError()!=NO_ERROR)\n\t\t\t\t\twsize.LowPart = wsize.HighPart = 0;\n\t\t\t\tCloseHandle(f);\n\t\t\t\tlen = qofs_Make(wsize.LowPart, wsize.HighPart);\n\t\t\t}\n\t\t}\n\t\tloc->len = len;\n\t\tloc->offset = 0;\n\t\tloc->fhandle = handle;\n\t\tQ_strncpyz(loc->rawname, netpath, sizeof(loc->rawname));\n\t}\n\tif (attr & FILE_ATTRIBUTE_DIRECTORY)\n\t\treturn FF_DIRECTORY;\t//not actually openable.\n\treturn FF_FOUND;\n}\nstatic void QDECL VFSW32_ReadFile(searchpathfuncs_t *handle, flocation_t *loc, char *buffer)\n{\n//\tvfsw32path_t *wp = handle;\n\n\tFILE *f;\n\twchar_t wide[MAX_OSPATH];\n\tif (!WinNT)\n\t\tf = fopen(loc->rawname, \"rb\");\n\telse\n\t\tf = _wfopen(widen(wide, sizeof(wide), loc->rawname), L\"rb\");\n\tif (!f)\t//err...\n\t\treturn;\n\tfseek(f, loc->offset, SEEK_SET);\n\tfread(buffer, 1, loc->len, f);\n\tfclose(f);\n}\nstatic int QDECL VFSW32_EnumerateFiles (searchpathfuncs_t *handle, const char *match, int (QDECL *func)(const char *, qofs_t, time_t mtime, void *, searchpathfuncs_t *spath), void *parm)\n{\n\tvfsw32path_t *wp = (vfsw32path_t*)handle;\n\treturn Sys_EnumerateFiles(wp->rootpath, match, func, parm, handle);\n}\n\nstatic qboolean QDECL VFSW32_RenameFile(searchpathfuncs_t *handle, const char *oldfname, const char *newfname)\n{\n\tvfsw32path_t *wp = (vfsw32path_t*)handle;\n\tchar oldsyspath[MAX_OSPATH];\n\tchar newsyspath[MAX_OSPATH];\n\tif (fs_readonly)\n\t\treturn false;\n\tsnprintf (oldsyspath, sizeof(oldsyspath)-1, \"%s%s\", wp->rootpath, oldfname);\n\tsnprintf (newsyspath, sizeof(newsyspath)-1, \"%s%s\", wp->rootpath, newfname);\n\treturn Sys_Rename(oldsyspath, newsyspath);\n}\nstatic qboolean QDECL VFSW32_RemoveFile(searchpathfuncs_t *handle, const char *filename)\n{\n\tvfsw32path_t *wp = (vfsw32path_t*)handle;\n\tchar syspath[MAX_OSPATH];\n\tif (fs_readonly)\n\t\treturn false;\n\tsnprintf (syspath, sizeof(syspath)-1, \"%s%s\", wp->rootpath, filename);\n\tif (*filename && filename[strlen(filename)-1] == '/')\n\t\treturn Sys_rmdir(syspath);\n\treturn Sys_remove(syspath);\n}\n\nsearchpathfuncs_t *QDECL VFSW32_OpenPath(vfsfile_t *mustbenull, searchpathfuncs_t *parent, const char *filename, const char *desc, const char *prefix)\n{\n\tvfsw32path_t *np;\n\tint dlen = strlen(desc);\n\tif (mustbenull)\n\t\treturn NULL;\n\tif (prefix && *prefix)\n\t\treturn NULL;\t//don't try to support this. too risky with absolute paths etc.\n\tnp = Z_Malloc(sizeof(*np) + dlen);\n\tif (np)\n\t{\n\t\twchar_t wide[MAX_OSPATH];\n\t\tmemcpy(np->rootpath, desc, dlen+1);\n\t\tif (*np->rootpath)\n\t\t\tQ_strncpy(np->rootpath+dlen, \"/\", 2);\n\t\tif (!WinNT)\n\t\t\tnp->changenotification = FindFirstChangeNotificationA(np->rootpath, true, FILE_NOTIFY_CHANGE_FILE_NAME|FILE_NOTIFY_CHANGE_CREATION);\n\t\telse\n\t\t\tnp->changenotification = FindFirstChangeNotificationW(widen(wide, sizeof(wide), np->rootpath), true, FILE_NOTIFY_CHANGE_FILE_NAME|FILE_NOTIFY_CHANGE_CREATION);\n\t}\n\n\tnp->pub.fsver\t\t\t= FSVER;\n\tnp->pub.ClosePath\t\t= VFSW32_ClosePath;\n\tnp->pub.BuildHash\t\t= VFSW32_BuildHash;\n\tnp->pub.FindFile\t\t= VFSW32_FLocate;\n\tnp->pub.ReadFile\t\t= VFSW32_ReadFile;\n\tnp->pub.EnumerateFiles\t= VFSW32_EnumerateFiles;\n\tnp->pub.OpenVFS\t\t\t= VFSW32_OpenVFS;\n\tnp->pub.PollChanges\t\t= VFSW32_PollChanges;\n\n\tnp->pub.FileStat\t\t= VFSW32_FileStat;\n\n#undef CreateFile //stoopid windows.h\n\tnp->pub.CreateFile\t\t= VFSW32_CreateLoc;\n\tnp->pub.RenameFile\t\t= VFSW32_RenameFile;\n\tnp->pub.RemoveFile\t\t= VFSW32_RemoveFile;\n\n\treturn &np->pub;\n}\n#endif\n\n\n\nqboolean Sys_GetFreeDiskSpace(const char *path, quint64_t *freespace)\n{\t//symlinks means the path needs to be fairly full. it may also be a file, and relative to the working directory.\n\twchar_t ffs[MAX_OSPATH];\n\tULARGE_INTEGER freebytes;\n\tunsigned int err;\n\tif (GetDiskFreeSpaceExW(widen(ffs, sizeof(ffs), path), &freebytes, NULL, NULL))\n\t{\n\t\t*freespace = freebytes.QuadPart;\n\t\treturn true;\n\t}\n\terr = GetLastError();\n\tCon_Printf(\"GetDiskFreeSpaceExW(%s) failed: %x\\n\", path, err);\n\treturn false;\n}\n"
  },
  {
    "path": "engine/common/fs_xz.c",
    "content": "#include \"quakedef.h\"\r\n#include \"fs.h\"\r\n\r\n#ifdef AVAIL_XZDEC\r\n\r\n#define XZ_EXTERN static\r\n#define XZ_DEC_DYNALLOC\t//data comes in periodically, can't use single mode.\r\n\r\n//http://tukaani.org/xz/embedded.html\r\n//we use an amalgamation, just because.\r\n//note that the original code has no stable api nor public library.\r\n#if 0\r\n#include \"xz/xz_dec_stream.c\"\r\n#include \"xz/xz_dec_lzma2.c\"\r\n#include \"xz/xz_crc32.c\"\r\n#else\r\n\r\n#\tinclude <stddef.h>\r\n\r\n#if __STDC_VERSION__ >= 199901L || defined(__GNUC__)\r\n#\tinclude <stdint.h>\r\n#else\r\n#define uint8_t unsigned char\r\n#define uint16_t unsigned short\r\n#define uint32_t unsigned int\r\n#define uint64_t quint64_t\r\n#endif\r\n\r\n\r\n//BEGIN xz.h\r\n/*\r\n * XZ decompressor\r\n *\r\n * Authors: Lasse Collin <lasse.collin@tukaani.org>\r\n *          Igor Pavlov <http://7-zip.org/>\r\n *\r\n * This file has been put into the public domain.\r\n * You can do whatever you want with this file.\r\n */\r\n\r\n#ifndef XZ_H\r\n#define XZ_H\r\n\r\n#ifdef __KERNEL__\r\n#\tinclude <linux/stddef.h>\r\n#\tinclude <linux/types.h>\r\n#else\r\n#\tinclude <stddef.h>\r\n//#\tinclude <stdint.h>\r\n#endif\r\n\r\n#ifdef __cplusplus\r\nextern \"C\" {\r\n#endif\r\n\r\n/* In Linux, this is used to make extern functions static when needed. */\r\n#ifndef XZ_EXTERN\r\n#\tdefine XZ_EXTERN extern\r\n#endif\r\n\r\n/**\r\n * enum xz_mode - Operation mode\r\n *\r\n * @XZ_SINGLE:              Single-call mode. This uses less RAM than\r\n *                          than multi-call modes, because the LZMA2\r\n *                          dictionary doesn't need to be allocated as\r\n *                          part of the decoder state. All required data\r\n *                          structures are allocated at initialization,\r\n *                          so xz_dec_run() cannot return XZ_MEM_ERROR.\r\n * @XZ_PREALLOC:            Multi-call mode with preallocated LZMA2\r\n *                          dictionary buffer. All data structures are\r\n *                          allocated at initialization, so xz_dec_run()\r\n *                          cannot return XZ_MEM_ERROR.\r\n * @XZ_DYNALLOC:            Multi-call mode. The LZMA2 dictionary is\r\n *                          allocated once the required size has been\r\n *                          parsed from the stream headers. If the\r\n *                          allocation fails, xz_dec_run() will return\r\n *                          XZ_MEM_ERROR.\r\n *\r\n * It is possible to enable support only for a subset of the above\r\n * modes at compile time by defining XZ_DEC_SINGLE, XZ_DEC_PREALLOC,\r\n * or XZ_DEC_DYNALLOC. The xz_dec kernel module is always compiled\r\n * with support for all operation modes, but the preboot code may\r\n * be built with fewer features to minimize code size.\r\n */\r\nenum xz_mode {\r\n\tXZ_SINGLE,\r\n\tXZ_PREALLOC,\r\n\tXZ_DYNALLOC\r\n};\r\n\r\n/**\r\n * enum xz_ret - Return codes\r\n * @XZ_OK:                  Everything is OK so far. More input or more\r\n *                          output space is required to continue. This\r\n *                          return code is possible only in multi-call mode\r\n *                          (XZ_PREALLOC or XZ_DYNALLOC).\r\n * @XZ_STREAM_END:          Operation finished successfully.\r\n * @XZ_UNSUPPORTED_CHECK:   Integrity check type is not supported. Decoding\r\n *                          is still possible in multi-call mode by simply\r\n *                          calling xz_dec_run() again.\r\n *                          Note that this return value is used only if\r\n *                          XZ_DEC_ANY_CHECK was defined at build time,\r\n *                          which is not used in the kernel. Unsupported\r\n *                          check types return XZ_OPTIONS_ERROR if\r\n *                          XZ_DEC_ANY_CHECK was not defined at build time.\r\n * @XZ_MEM_ERROR:           Allocating memory failed. This return code is\r\n *                          possible only if the decoder was initialized\r\n *                          with XZ_DYNALLOC. The amount of memory that was\r\n *                          tried to be allocated was no more than the\r\n *                          dict_max argument given to xz_dec_init().\r\n * @XZ_MEMLIMIT_ERROR:      A bigger LZMA2 dictionary would be needed than\r\n *                          allowed by the dict_max argument given to\r\n *                          xz_dec_init(). This return value is possible\r\n *                          only in multi-call mode (XZ_PREALLOC or\r\n *                          XZ_DYNALLOC); the single-call mode (XZ_SINGLE)\r\n *                          ignores the dict_max argument.\r\n * @XZ_FORMAT_ERROR:        File format was not recognized (wrong magic\r\n *                          bytes).\r\n * @XZ_OPTIONS_ERROR:       This implementation doesn't support the requested\r\n *                          compression options. In the decoder this means\r\n *                          that the header CRC32 matches, but the header\r\n *                          itself specifies something that we don't support.\r\n * @XZ_DATA_ERROR:          Compressed data is corrupt.\r\n * @XZ_BUF_ERROR:           Cannot make any progress. Details are slightly\r\n *                          different between multi-call and single-call\r\n *                          mode; more information below.\r\n *\r\n * In multi-call mode, XZ_BUF_ERROR is returned when two consecutive calls\r\n * to XZ code cannot consume any input and cannot produce any new output.\r\n * This happens when there is no new input available, or the output buffer\r\n * is full while at least one output byte is still pending. Assuming your\r\n * code is not buggy, you can get this error only when decoding a compressed\r\n * stream that is truncated or otherwise corrupt.\r\n *\r\n * In single-call mode, XZ_BUF_ERROR is returned only when the output buffer\r\n * is too small or the compressed input is corrupt in a way that makes the\r\n * decoder produce more output than the caller expected. When it is\r\n * (relatively) clear that the compressed input is truncated, XZ_DATA_ERROR\r\n * is used instead of XZ_BUF_ERROR.\r\n */\r\nenum xz_ret {\r\n\tXZ_OK,\r\n\tXZ_STREAM_END,\r\n\tXZ_UNSUPPORTED_CHECK,\r\n\tXZ_MEM_ERROR,\r\n\tXZ_MEMLIMIT_ERROR,\r\n\tXZ_FORMAT_ERROR,\r\n\tXZ_OPTIONS_ERROR,\r\n\tXZ_DATA_ERROR,\r\n\tXZ_BUF_ERROR\r\n};\r\n\r\n/**\r\n * struct xz_buf - Passing input and output buffers to XZ code\r\n * @in:         Beginning of the input buffer. This may be NULL if and only\r\n *              if in_pos is equal to in_size.\r\n * @in_pos:     Current position in the input buffer. This must not exceed\r\n *              in_size.\r\n * @in_size:    Size of the input buffer\r\n * @out:        Beginning of the output buffer. This may be NULL if and only\r\n *              if out_pos is equal to out_size.\r\n * @out_pos:    Current position in the output buffer. This must not exceed\r\n *              out_size.\r\n * @out_size:   Size of the output buffer\r\n *\r\n * Only the contents of the output buffer from out[out_pos] onward, and\r\n * the variables in_pos and out_pos are modified by the XZ code.\r\n */\r\nstruct xz_buf {\r\n\tconst uint8_t *in;\r\n\tsize_t in_pos;\r\n\tsize_t in_size;\r\n\r\n\tuint8_t *out;\r\n\tsize_t out_pos;\r\n\tsize_t out_size;\r\n};\r\n\r\n/**\r\n * struct xz_dec - Opaque type to hold the XZ decoder state\r\n */\r\nstruct xz_dec;\r\n\r\n/**\r\n * xz_dec_init() - Allocate and initialize a XZ decoder state\r\n * @mode:       Operation mode\r\n * @dict_max:   Maximum size of the LZMA2 dictionary (history buffer) for\r\n *              multi-call decoding. This is ignored in single-call mode\r\n *              (mode == XZ_SINGLE). LZMA2 dictionary is always 2^n bytes\r\n *              or 2^n + 2^(n-1) bytes (the latter sizes are less common\r\n *              in practice), so other values for dict_max don't make sense.\r\n *              In the kernel, dictionary sizes of 64 KiB, 128 KiB, 256 KiB,\r\n *              512 KiB, and 1 MiB are probably the only reasonable values,\r\n *              except for kernel and initramfs images where a bigger\r\n *              dictionary can be fine and useful.\r\n *\r\n * Single-call mode (XZ_SINGLE): xz_dec_run() decodes the whole stream at\r\n * once. The caller must provide enough output space or the decoding will\r\n * fail. The output space is used as the dictionary buffer, which is why\r\n * there is no need to allocate the dictionary as part of the decoder's\r\n * internal state.\r\n *\r\n * Because the output buffer is used as the workspace, streams encoded using\r\n * a big dictionary are not a problem in single-call mode. It is enough that\r\n * the output buffer is big enough to hold the actual uncompressed data; it\r\n * can be smaller than the dictionary size stored in the stream headers.\r\n *\r\n * Multi-call mode with preallocated dictionary (XZ_PREALLOC): dict_max bytes\r\n * of memory is preallocated for the LZMA2 dictionary. This way there is no\r\n * risk that xz_dec_run() could run out of memory, since xz_dec_run() will\r\n * never allocate any memory. Instead, if the preallocated dictionary is too\r\n * small for decoding the given input stream, xz_dec_run() will return\r\n * XZ_MEMLIMIT_ERROR. Thus, it is important to know what kind of data will be\r\n * decoded to avoid allocating excessive amount of memory for the dictionary.\r\n *\r\n * Multi-call mode with dynamically allocated dictionary (XZ_DYNALLOC):\r\n * dict_max specifies the maximum allowed dictionary size that xz_dec_run()\r\n * may allocate once it has parsed the dictionary size from the stream\r\n * headers. This way excessive allocations can be avoided while still\r\n * limiting the maximum memory usage to a sane value to prevent running the\r\n * system out of memory when decompressing streams from untrusted sources.\r\n *\r\n * On success, xz_dec_init() returns a pointer to struct xz_dec, which is\r\n * ready to be used with xz_dec_run(). If memory allocation fails,\r\n * xz_dec_init() returns NULL.\r\n */\r\nXZ_EXTERN struct xz_dec *xz_dec_init(enum xz_mode mode, uint32_t dict_max);\r\n\r\n/**\r\n * xz_dec_run() - Run the XZ decoder\r\n * @s:          Decoder state allocated using xz_dec_init()\r\n * @b:          Input and output buffers\r\n *\r\n * The possible return values depend on build options and operation mode.\r\n * See enum xz_ret for details.\r\n *\r\n * Note that if an error occurs in single-call mode (return value is not\r\n * XZ_STREAM_END), b->in_pos and b->out_pos are not modified and the\r\n * contents of the output buffer from b->out[b->out_pos] onward are\r\n * undefined. This is true even after XZ_BUF_ERROR, because with some filter\r\n * chains, there may be a second pass over the output buffer, and this pass\r\n * cannot be properly done if the output buffer is truncated. Thus, you\r\n * cannot give the single-call decoder a too small buffer and then expect to\r\n * get that amount valid data from the beginning of the stream. You must use\r\n * the multi-call decoder if you don't want to uncompress the whole stream.\r\n */\r\nXZ_EXTERN enum xz_ret xz_dec_run(struct xz_dec *s, struct xz_buf *b);\r\n\r\n/**\r\n * xz_dec_reset() - Reset an already allocated decoder state\r\n * @s:          Decoder state allocated using xz_dec_init()\r\n *\r\n * This function can be used to reset the multi-call decoder state without\r\n * freeing and reallocating memory with xz_dec_end() and xz_dec_init().\r\n *\r\n * In single-call mode, xz_dec_reset() is always called in the beginning of\r\n * xz_dec_run(). Thus, explicit call to xz_dec_reset() is useful only in\r\n * multi-call mode.\r\n */\r\nXZ_EXTERN void xz_dec_reset(struct xz_dec *s);\r\n\r\n/**\r\n * xz_dec_end() - Free the memory allocated for the decoder state\r\n * @s:          Decoder state allocated using xz_dec_init(). If s is NULL,\r\n *              this function does nothing.\r\n */\r\nXZ_EXTERN void xz_dec_end(struct xz_dec *s);\r\n\r\n/*\r\n * Standalone build (userspace build or in-kernel build for boot time use)\r\n * needs a CRC32 implementation. For normal in-kernel use, kernel's own\r\n * CRC32 module is used instead, and users of this module don't need to\r\n * care about the functions below.\r\n */\r\n#ifndef XZ_INTERNAL_CRC32\r\n#\tifdef __KERNEL__\r\n#\t\tdefine XZ_INTERNAL_CRC32 0\r\n#\telse\r\n#\t\tdefine XZ_INTERNAL_CRC32 1\r\n#\tendif\r\n#endif\r\n\r\n/*\r\n * If CRC64 support has been enabled with XZ_USE_CRC64, a CRC64\r\n * implementation is needed too.\r\n */\r\n#ifndef XZ_USE_CRC64\r\n#\tundef XZ_INTERNAL_CRC64\r\n#\tdefine XZ_INTERNAL_CRC64 0\r\n#endif\r\n#ifndef XZ_INTERNAL_CRC64\r\n#\tifdef __KERNEL__\r\n#\t\terror Using CRC64 in the kernel has not been implemented.\r\n#\telse\r\n#\t\tdefine XZ_INTERNAL_CRC64 1\r\n#\tendif\r\n#endif\r\n\r\n#if XZ_INTERNAL_CRC32\r\n/*\r\n * This must be called before any other xz_* function to initialize\r\n * the CRC32 lookup table.\r\n */\r\nXZ_EXTERN void xz_crc32_init(void);\r\n\r\n/*\r\n * Update CRC32 value using the polynomial from IEEE-802.3. To start a new\r\n * calculation, the third argument must be zero. To continue the calculation,\r\n * the previously returned value is passed as the third argument.\r\n */\r\nXZ_EXTERN uint32_t xz_crc32(const uint8_t *buf, size_t size, uint32_t crc);\r\n#endif\r\n\r\n#if XZ_INTERNAL_CRC64\r\n/*\r\n * This must be called before any other xz_* function (except xz_crc32_init())\r\n * to initialize the CRC64 lookup table.\r\n */\r\nXZ_EXTERN void xz_crc64_init(void);\r\n\r\n/*\r\n * Update CRC64 value using the polynomial from ECMA-182. To start a new\r\n * calculation, the third argument must be zero. To continue the calculation,\r\n * the previously returned value is passed as the third argument.\r\n */\r\nXZ_EXTERN uint64_t xz_crc64(const uint8_t *buf, size_t size, uint64_t crc);\r\n#endif\r\n\r\n#ifdef __cplusplus\r\n}\r\n#endif\r\n\r\n#endif\r\n//END xz.h\r\n\r\n//BEGIN xz_config.h\r\n/*\r\n * Private includes and definitions for userspace use of XZ Embedded\r\n *\r\n * Author: Lasse Collin <lasse.collin@tukaani.org>\r\n *\r\n * This file has been put into the public domain.\r\n * You can do whatever you want with this file.\r\n */\r\n\r\n#ifndef XZ_CONFIG_H\r\n#define XZ_CONFIG_H\r\n\r\n/* Uncomment to enable CRC64 support. */\r\n/* #define XZ_USE_CRC64 */\r\n\r\n/* Uncomment as needed to enable BCJ filter decoders. */\r\n/* #define XZ_DEC_X86 */\r\n/* #define XZ_DEC_POWERPC */\r\n/* #define XZ_DEC_IA64 */\r\n/* #define XZ_DEC_ARM */\r\n/* #define XZ_DEC_ARMTHUMB */\r\n/* #define XZ_DEC_SPARC */\r\n\r\n/*\r\n * MSVC doesn't support modern C but XZ Embedded is mostly C89\r\n * so these are enough.\r\n */\r\n#ifdef _MSC_VER\r\ntypedef unsigned char bool;\r\n#\tdefine true 1\r\n#\tdefine false 0\r\n#\tifndef inline\r\n#\t\tdefine inline __inline\r\n#\tendif\r\n#else\r\n#\tinclude <stdbool.h>\r\n#endif\r\n\r\n#include <stdlib.h>\r\n#include <string.h>\r\n\r\n//#include \"xz.h\"\r\n\r\n#define kmalloc(size, flags) malloc(size)\r\n#define kfree(ptr) free(ptr)\r\n#define vmalloc(size) malloc(size)\r\n#define vfree(ptr) free(ptr)\r\n\r\n#define memeq(a, b, size) (memcmp(a, b, size) == 0)\r\n#define memzero(buf, size) memset(buf, 0, size)\r\n\r\n#ifndef min\r\n#\tdefine min(x, y) ((x) < (y) ? (x) : (y))\r\n#endif\r\n#define min_t(type, x, y) min(x, y)\r\n\r\n/*\r\n * Some functions have been marked with __always_inline to keep the\r\n * performance reasonable even when the compiler is optimizing for\r\n * small code size. You may be able to save a few bytes by #defining\r\n * __always_inline to plain inline, but don't complain if the code\r\n * becomes slow.\r\n *\r\n * NOTE: System headers on GNU/Linux may #define this macro already,\r\n * so if you want to change it, you need to #undef it first.\r\n */\r\n#ifndef always_inline\r\n#\tifdef __GNUC__\r\n#\t\tdefine always_inline \\\r\n\t\t\tinline __attribute__((__always_inline__))\r\n#\telse\r\n#\t\tdefine always_inline inline\r\n#\tendif\r\n#endif\r\n\r\n/* Inline functions to access unaligned unsigned 32-bit integers */\r\n#ifndef get_unaligned_le32\r\nstatic inline uint32_t get_unaligned_le32(const uint8_t *buf)\r\n{\r\n\treturn (uint32_t)buf[0]\r\n\t\t\t| ((uint32_t)buf[1] << 8)\r\n\t\t\t| ((uint32_t)buf[2] << 16)\r\n\t\t\t| ((uint32_t)buf[3] << 24);\r\n}\r\n#endif\r\n\r\n/*#ifndef get_unaligned_be32\r\nstatic inline uint32_t get_unaligned_be32(const uint8_t *buf)\r\n{\r\n\treturn (uint32_t)(buf[0] << 24)\r\n\t\t\t| ((uint32_t)buf[1] << 16)\r\n\t\t\t| ((uint32_t)buf[2] << 8)\r\n\t\t\t| (uint32_t)buf[3];\r\n}\r\n#endif\r\n\r\n#ifndef put_unaligned_le32\r\nstatic inline void put_unaligned_le32(uint32_t val, uint8_t *buf)\r\n{\r\n\tbuf[0] = (uint8_t)val;\r\n\tbuf[1] = (uint8_t)(val >> 8);\r\n\tbuf[2] = (uint8_t)(val >> 16);\r\n\tbuf[3] = (uint8_t)(val >> 24);\r\n}\r\n#endif\r\n\r\n#ifndef put_unaligned_be32\r\nstatic inline void put_unaligned_be32(uint32_t val, uint8_t *buf)\r\n{\r\n\tbuf[0] = (uint8_t)(val >> 24);\r\n\tbuf[1] = (uint8_t)(val >> 16);\r\n\tbuf[2] = (uint8_t)(val >> 8);\r\n\tbuf[3] = (uint8_t)val;\r\n}\r\n#endif*/\r\n\r\n/*\r\n * Use get_unaligned_le32() also for aligned access for simplicity. On\r\n * little endian systems, #define get_le32(ptr) (*(const uint32_t *)(ptr))\r\n * could save a few bytes in code size.\r\n */\r\n#ifndef get_le32\r\n#\tdefine get_le32 get_unaligned_le32\r\n#endif\r\n\r\n#endif\r\n//END xz_config.h\r\n\r\n//BEGIN xz_private.h\r\n/*\r\n * Private includes and definitions\r\n *\r\n * Author: Lasse Collin <lasse.collin@tukaani.org>\r\n *\r\n * This file has been put into the public domain.\r\n * You can do whatever you want with this file.\r\n */\r\n\r\n#ifndef XZ_PRIVATE_H\r\n#define XZ_PRIVATE_H\r\n\r\n#ifdef __KERNEL__\r\n#\tinclude <linux/xz.h>\r\n#\tinclude <linux/kernel.h>\r\n#\tinclude <asm/unaligned.h>\r\n\t/* XZ_PREBOOT may be defined only via decompress_unxz.c. */\r\n#\tifndef XZ_PREBOOT\r\n#\t\tinclude <linux/slab.h>\r\n#\t\tinclude <linux/vmalloc.h>\r\n#\t\tinclude <linux/string.h>\r\n#\t\tifdef CONFIG_XZ_DEC_X86\r\n#\t\t\tdefine XZ_DEC_X86\r\n#\t\tendif\r\n#\t\tifdef CONFIG_XZ_DEC_POWERPC\r\n#\t\t\tdefine XZ_DEC_POWERPC\r\n#\t\tendif\r\n#\t\tifdef CONFIG_XZ_DEC_IA64\r\n#\t\t\tdefine XZ_DEC_IA64\r\n#\t\tendif\r\n#\t\tifdef CONFIG_XZ_DEC_ARM\r\n#\t\t\tdefine XZ_DEC_ARM\r\n#\t\tendif\r\n#\t\tifdef CONFIG_XZ_DEC_ARMTHUMB\r\n#\t\t\tdefine XZ_DEC_ARMTHUMB\r\n#\t\tendif\r\n#\t\tifdef CONFIG_XZ_DEC_SPARC\r\n#\t\t\tdefine XZ_DEC_SPARC\r\n#\t\tendif\r\n#\t\tdefine memeq(a, b, size) (memcmp(a, b, size) == 0)\r\n#\t\tdefine memzero(buf, size) memset(buf, 0, size)\r\n#\tendif\r\n#\tdefine get_le32(p) le32_to_cpup((const uint32_t *)(p))\r\n#else\r\n\t/*\r\n\t * For userspace builds, use a separate header to define the required\r\n\t * macros and functions. This makes it easier to adapt the code into\r\n\t * different environments and avoids clutter in the Linux kernel tree.\r\n\t */\r\n//#\tinclude \"xz_config.h\"\r\n#endif\r\n\r\n/* If no specific decoding mode is requested, enable support for all modes. */\r\n#if !defined(XZ_DEC_SINGLE) && !defined(XZ_DEC_PREALLOC) \\\r\n\t\t&& !defined(XZ_DEC_DYNALLOC)\r\n#\tdefine XZ_DEC_SINGLE\r\n#\tdefine XZ_DEC_PREALLOC\r\n#\tdefine XZ_DEC_DYNALLOC\r\n#endif\r\n\r\n/*\r\n * The DEC_IS_foo(mode) macros are used in \"if\" statements. If only some\r\n * of the supported modes are enabled, these macros will evaluate to true or\r\n * false at compile time and thus allow the compiler to omit unneeded code.\r\n */\r\n#ifdef XZ_DEC_SINGLE\r\n#\tdefine DEC_IS_SINGLE(mode) ((mode) == XZ_SINGLE)\r\n#else\r\n#\tdefine DEC_IS_SINGLE(mode) (false)\r\n#endif\r\n\r\n#ifdef XZ_DEC_PREALLOC\r\n#\tdefine DEC_IS_PREALLOC(mode) ((mode) == XZ_PREALLOC)\r\n#else\r\n#\tdefine DEC_IS_PREALLOC(mode) (false)\r\n#endif\r\n\r\n#ifdef XZ_DEC_DYNALLOC\r\n#\tdefine DEC_IS_DYNALLOC(mode) ((mode) == XZ_DYNALLOC)\r\n#else\r\n#\tdefine DEC_IS_DYNALLOC(mode) (false)\r\n#endif\r\n\r\n#if !defined(XZ_DEC_SINGLE)\r\n#\tdefine DEC_IS_MULTI(mode) (true)\r\n#elif defined(XZ_DEC_PREALLOC) || defined(XZ_DEC_DYNALLOC)\r\n#\tdefine DEC_IS_MULTI(mode) ((mode) != XZ_SINGLE)\r\n#else\r\n#\tdefine DEC_IS_MULTI(mode) (false)\r\n#endif\r\n\r\n/*\r\n * If any of the BCJ filter decoders are wanted, define XZ_DEC_BCJ.\r\n * XZ_DEC_BCJ is used to enable generic support for BCJ decoders.\r\n */\r\n#ifndef XZ_DEC_BCJ\r\n#\tif defined(XZ_DEC_X86) || defined(XZ_DEC_POWERPC) \\\r\n\t\t\t|| defined(XZ_DEC_IA64) || defined(XZ_DEC_ARM) \\\r\n\t\t\t|| defined(XZ_DEC_ARM) || defined(XZ_DEC_ARMTHUMB) \\\r\n\t\t\t|| defined(XZ_DEC_SPARC)\r\n#\t\tdefine XZ_DEC_BCJ\r\n#\tendif\r\n#endif\r\n\r\n/*\r\n * Allocate memory for LZMA2 decoder. xz_dec_lzma2_reset() must be used\r\n * before calling xz_dec_lzma2_run().\r\n */\r\nXZ_EXTERN struct xz_dec_lzma2 *xz_dec_lzma2_create(enum xz_mode mode,\r\n\t\t\t\t\t\t   uint32_t dict_max);\r\n\r\n/*\r\n * Decode the LZMA2 properties (one byte) and reset the decoder. Return\r\n * XZ_OK on success, XZ_MEMLIMIT_ERROR if the preallocated dictionary is not\r\n * big enough, and XZ_OPTIONS_ERROR if props indicates something that this\r\n * decoder doesn't support.\r\n */\r\nXZ_EXTERN enum xz_ret xz_dec_lzma2_reset(struct xz_dec_lzma2 *s,\r\n\t\t\t\t\t uint8_t props);\r\n\r\n/* Decode raw LZMA2 stream from b->in to b->out. */\r\nXZ_EXTERN enum xz_ret xz_dec_lzma2_run(struct xz_dec_lzma2 *s,\r\n\t\t\t\t       struct xz_buf *b);\r\n\r\n/* Free the memory allocated for the LZMA2 decoder. */\r\nXZ_EXTERN void xz_dec_lzma2_end(struct xz_dec_lzma2 *s);\r\n\r\n#ifdef XZ_DEC_BCJ\r\n/*\r\n * Allocate memory for BCJ decoders. xz_dec_bcj_reset() must be used before\r\n * calling xz_dec_bcj_run().\r\n */\r\nXZ_EXTERN struct xz_dec_bcj *xz_dec_bcj_create(bool single_call);\r\n\r\n/*\r\n * Decode the Filter ID of a BCJ filter. This implementation doesn't\r\n * support custom start offsets, so no decoding of Filter Properties\r\n * is needed. Returns XZ_OK if the given Filter ID is supported.\r\n * Otherwise XZ_OPTIONS_ERROR is returned.\r\n */\r\nXZ_EXTERN enum xz_ret xz_dec_bcj_reset(struct xz_dec_bcj *s, uint8_t id);\r\n\r\n/*\r\n * Decode raw BCJ + LZMA2 stream. This must be used only if there actually is\r\n * a BCJ filter in the chain. If the chain has only LZMA2, xz_dec_lzma2_run()\r\n * must be called directly.\r\n */\r\nXZ_EXTERN enum xz_ret xz_dec_bcj_run(struct xz_dec_bcj *s,\r\n\t\t\t\t     struct xz_dec_lzma2 *lzma2,\r\n\t\t\t\t     struct xz_buf *b);\r\n\r\n/* Free the memory allocated for the BCJ filters. */\r\n#define xz_dec_bcj_end(s) kfree(s)\r\n#endif\r\n\r\n#endif\r\n//END xz_private.h\r\n//BEGIN xz_stream.h\r\n/*\r\n * Definitions for handling the .xz file format\r\n *\r\n * Author: Lasse Collin <lasse.collin@tukaani.org>\r\n *\r\n * This file has been put into the public domain.\r\n * You can do whatever you want with this file.\r\n */\r\n\r\n#ifndef XZ_STREAM_H\r\n#define XZ_STREAM_H\r\n\r\n#if defined(__KERNEL__) && !XZ_INTERNAL_CRC32\r\n#\tinclude <linux/crc32.h>\r\n#\tundef crc32\r\n#\tdefine xz_crc32(buf, size, crc) \\\r\n\t\t(~crc32_le(~(uint32_t)(crc), buf, size))\r\n#endif\r\n\r\n/*\r\n * See the .xz file format specification at\r\n * http://tukaani.org/xz/xz-file-format.txt\r\n * to understand the container format.\r\n */\r\n\r\n#define STREAM_HEADER_SIZE 12\r\n\r\n#define HEADER_MAGIC \"\\375\"\"7zXZ\"\r\n#define HEADER_MAGIC_SIZE 6\r\n\r\n#define FOOTER_MAGIC \"YZ\"\r\n#define FOOTER_MAGIC_SIZE 2\r\n\r\n/*\r\n * Variable-length integer can hold a 63-bit unsigned integer or a special\r\n * value indicating that the value is unknown.\r\n *\r\n * Experimental: vli_type can be defined to uint32_t to save a few bytes\r\n * in code size (no effect on speed). Doing so limits the uncompressed and\r\n * compressed size of the file to less than 256 MiB and may also weaken\r\n * error detection slightly.\r\n */\r\ntypedef uint64_t vli_type;\r\n\r\n#define VLI_MAX ((vli_type)-1 / 2)\r\n#define VLI_UNKNOWN ((vli_type)-1)\r\n\r\n/* Maximum encoded size of a VLI */\r\n#define VLI_BYTES_MAX (sizeof(vli_type) * 8 / 7)\r\n\r\n/* Integrity Check types */\r\nenum xz_check {\r\n\tXZ_CHECK_NONE = 0,\r\n\tXZ_CHECK_CRC32 = 1,\r\n\tXZ_CHECK_CRC64 = 4,\r\n\tXZ_CHECK_SHA256 = 10\r\n};\r\n\r\n/* Maximum possible Check ID */\r\n#define XZ_CHECK_MAX 15\r\n\r\n#endif\r\n//END xz_stream.h\r\n\r\n//BEGIN \"xz_lzma2.h\"\r\n/*\r\n * LZMA2 definitions\r\n *\r\n * Authors: Lasse Collin <lasse.collin@tukaani.org>\r\n *          Igor Pavlov <http://7-zip.org/>\r\n *\r\n * This file has been put into the public domain.\r\n * You can do whatever you want with this file.\r\n */\r\n\r\n#ifndef XZ_LZMA2_H\r\n#define XZ_LZMA2_H\r\n\r\n/* Range coder constants */\r\n#define RC_SHIFT_BITS 8\r\n#define RC_TOP_BITS 24\r\n#define RC_TOP_VALUE (1 << RC_TOP_BITS)\r\n#define RC_BIT_MODEL_TOTAL_BITS 11\r\n#define RC_BIT_MODEL_TOTAL (1 << RC_BIT_MODEL_TOTAL_BITS)\r\n#define RC_MOVE_BITS 5\r\n\r\n/*\r\n * Maximum number of position states. A position state is the lowest pb\r\n * number of bits of the current uncompressed offset. In some places there\r\n * are different sets of probabilities for different position states.\r\n */\r\n#define POS_STATES_MAX (1 << 4)\r\n\r\n/*\r\n * This enum is used to track which LZMA symbols have occurred most recently\r\n * and in which order. This information is used to predict the next symbol.\r\n *\r\n * Symbols:\r\n *  - Literal: One 8-bit byte\r\n *  - Match: Repeat a chunk of data at some distance\r\n *  - Long repeat: Multi-byte match at a recently seen distance\r\n *  - Short repeat: One-byte repeat at a recently seen distance\r\n *\r\n * The symbol names are in from STATE_oldest_older_previous. REP means\r\n * either short or long repeated match, and NONLIT means any non-literal.\r\n */\r\nenum lzma_state {\r\n\tSTATE_LIT_LIT,\r\n\tSTATE_MATCH_LIT_LIT,\r\n\tSTATE_REP_LIT_LIT,\r\n\tSTATE_SHORTREP_LIT_LIT,\r\n\tSTATE_MATCH_LIT,\r\n\tSTATE_REP_LIT,\r\n\tSTATE_SHORTREP_LIT,\r\n\tSTATE_LIT_MATCH,\r\n\tSTATE_LIT_LONGREP,\r\n\tSTATE_LIT_SHORTREP,\r\n\tSTATE_NONLIT_MATCH,\r\n\tSTATE_NONLIT_REP\r\n};\r\n\r\n/* Total number of states */\r\n#define STATES 12\r\n\r\n/* The lowest 7 states indicate that the previous state was a literal. */\r\n#define LIT_STATES 7\r\n\r\n/* Indicate that the latest symbol was a literal. */\r\nstatic inline void lzma_state_literal(enum lzma_state *state)\r\n{\r\n\tif (*state <= STATE_SHORTREP_LIT_LIT)\r\n\t\t*state = STATE_LIT_LIT;\r\n\telse if (*state <= STATE_LIT_SHORTREP)\r\n\t\t*state -= 3;\r\n\telse\r\n\t\t*state -= 6;\r\n}\r\n\r\n/* Indicate that the latest symbol was a match. */\r\nstatic inline void lzma_state_match(enum lzma_state *state)\r\n{\r\n\t*state = *state < LIT_STATES ? STATE_LIT_MATCH : STATE_NONLIT_MATCH;\r\n}\r\n\r\n/* Indicate that the latest state was a long repeated match. */\r\nstatic inline void lzma_state_long_rep(enum lzma_state *state)\r\n{\r\n\t*state = *state < LIT_STATES ? STATE_LIT_LONGREP : STATE_NONLIT_REP;\r\n}\r\n\r\n/* Indicate that the latest symbol was a short match. */\r\nstatic inline void lzma_state_short_rep(enum lzma_state *state)\r\n{\r\n\t*state = *state < LIT_STATES ? STATE_LIT_SHORTREP : STATE_NONLIT_REP;\r\n}\r\n\r\n/* Test if the previous symbol was a literal. */\r\nstatic inline bool lzma_state_is_literal(enum lzma_state state)\r\n{\r\n\treturn state < LIT_STATES;\r\n}\r\n\r\n/* Each literal coder is divided in three sections:\r\n *   - 0x001-0x0FF: Without match byte\r\n *   - 0x101-0x1FF: With match byte; match bit is 0\r\n *   - 0x201-0x2FF: With match byte; match bit is 1\r\n *\r\n * Match byte is used when the previous LZMA symbol was something else than\r\n * a literal (that is, it was some kind of match).\r\n */\r\n#define LITERAL_CODER_SIZE 0x300\r\n\r\n/* Maximum number of literal coders */\r\n#define LITERAL_CODERS_MAX (1 << 4)\r\n\r\n/* Minimum length of a match is two bytes. */\r\n#define MATCH_LEN_MIN 2\r\n\r\n/* Match length is encoded with 4, 5, or 10 bits.\r\n *\r\n * Length   Bits\r\n *  2-9      4 = Choice=0 + 3 bits\r\n * 10-17     5 = Choice=1 + Choice2=0 + 3 bits\r\n * 18-273   10 = Choice=1 + Choice2=1 + 8 bits\r\n */\r\n#define LEN_LOW_BITS 3\r\n#define LEN_LOW_SYMBOLS (1 << LEN_LOW_BITS)\r\n#define LEN_MID_BITS 3\r\n#define LEN_MID_SYMBOLS (1 << LEN_MID_BITS)\r\n#define LEN_HIGH_BITS 8\r\n#define LEN_HIGH_SYMBOLS (1 << LEN_HIGH_BITS)\r\n#define LEN_SYMBOLS (LEN_LOW_SYMBOLS + LEN_MID_SYMBOLS + LEN_HIGH_SYMBOLS)\r\n\r\n/*\r\n * Maximum length of a match is 273 which is a result of the encoding\r\n * described above.\r\n */\r\n#define MATCH_LEN_MAX (MATCH_LEN_MIN + LEN_SYMBOLS - 1)\r\n\r\n/*\r\n * Different sets of probabilities are used for match distances that have\r\n * very short match length: Lengths of 2, 3, and 4 bytes have a separate\r\n * set of probabilities for each length. The matches with longer length\r\n * use a shared set of probabilities.\r\n */\r\n#define DIST_STATES 4\r\n\r\n/*\r\n * Get the index of the appropriate probability array for decoding\r\n * the distance slot.\r\n */\r\nstatic inline uint32_t lzma_get_dist_state(uint32_t len)\r\n{\r\n\treturn len < DIST_STATES + MATCH_LEN_MIN\r\n\t\t\t? len - MATCH_LEN_MIN : DIST_STATES - 1;\r\n}\r\n\r\n/*\r\n * The highest two bits of a 32-bit match distance are encoded using six bits.\r\n * This six-bit value is called a distance slot. This way encoding a 32-bit\r\n * value takes 6-36 bits, larger values taking more bits.\r\n */\r\n#define DIST_SLOT_BITS 6\r\n#define DIST_SLOTS (1 << DIST_SLOT_BITS)\r\n\r\n/* Match distances up to 127 are fully encoded using probabilities. Since\r\n * the highest two bits (distance slot) are always encoded using six bits,\r\n * the distances 0-3 don't need any additional bits to encode, since the\r\n * distance slot itself is the same as the actual distance. DIST_MODEL_START\r\n * indicates the first distance slot where at least one additional bit is\r\n * needed.\r\n */\r\n#define DIST_MODEL_START 4\r\n\r\n/*\r\n * Match distances greater than 127 are encoded in three pieces:\r\n *   - distance slot: the highest two bits\r\n *   - direct bits: 2-26 bits below the highest two bits\r\n *   - alignment bits: four lowest bits\r\n *\r\n * Direct bits don't use any probabilities.\r\n *\r\n * The distance slot value of 14 is for distances 128-191.\r\n */\r\n#define DIST_MODEL_END 14\r\n\r\n/* Distance slots that indicate a distance <= 127. */\r\n#define FULL_DISTANCES_BITS (DIST_MODEL_END / 2)\r\n#define FULL_DISTANCES (1 << FULL_DISTANCES_BITS)\r\n\r\n/*\r\n * For match distances greater than 127, only the highest two bits and the\r\n * lowest four bits (alignment) is encoded using probabilities.\r\n */\r\n#define ALIGN_BITS 4\r\n#define ALIGN_SIZE (1 << ALIGN_BITS)\r\n#define ALIGN_MASK (ALIGN_SIZE - 1)\r\n\r\n/* Total number of all probability variables */\r\n#define PROBS_TOTAL (1846 + LITERAL_CODERS_MAX * LITERAL_CODER_SIZE)\r\n\r\n/*\r\n * LZMA remembers the four most recent match distances. Reusing these\r\n * distances tends to take less space than re-encoding the actual\r\n * distance value.\r\n */\r\n#define REPS 4\r\n\r\n#endif\r\n\r\n//END xz_lzma2.h\r\n//BEGIN xz_dec_stream.c\r\n\r\n/*\r\n * .xz Stream decoder\r\n *\r\n * Author: Lasse Collin <lasse.collin@tukaani.org>\r\n *\r\n * This file has been put into the public domain.\r\n * You can do whatever you want with this file.\r\n */\r\n\r\n#ifdef XZ_USE_CRC64\r\n#\tdefine IS_CRC64(check_type) ((check_type) == XZ_CHECK_CRC64)\r\n#else\r\n#\tdefine IS_CRC64(check_type) false\r\n#endif\r\n\r\n/* Hash used to validate the Index field */\r\nstruct xz_dec_hash {\r\n\tvli_type unpadded;\r\n\tvli_type uncompressed;\r\n\tuint32_t crc32;\r\n};\r\n\r\nstruct xz_dec {\r\n\t/* Position in dec_main() */\r\n\tenum {\r\n\t\tSEQ_STREAM_HEADER,\r\n\t\tSEQ_BLOCK_START,\r\n\t\tSEQ_BLOCK_HEADER,\r\n\t\tSEQ_BLOCK_UNCOMPRESS,\r\n\t\tSEQ_BLOCK_PADDING,\r\n\t\tSEQ_BLOCK_CHECK,\r\n\t\tSEQ_INDEX,\r\n\t\tSEQ_INDEX_PADDING,\r\n\t\tSEQ_INDEX_CRC32,\r\n\t\tSEQ_STREAM_FOOTER\r\n\t} sequence;\r\n\r\n\t/* Position in variable-length integers and Check fields */\r\n\tuint32_t pos;\r\n\r\n\t/* Variable-length integer decoded by dec_vli() */\r\n\tvli_type vli;\r\n\r\n\t/* Saved in_pos and out_pos */\r\n\tsize_t in_start;\r\n\tsize_t out_start;\r\n\r\n#ifdef XZ_USE_CRC64\r\n\t/* CRC32 or CRC64 value in Block or CRC32 value in Index */\r\n\tuint64_t crc;\r\n#else\r\n\t/* CRC32 value in Block or Index */\r\n\tuint32_t crc;\r\n#endif\r\n\r\n\t/* Type of the integrity check calculated from uncompressed data */\r\n\tenum xz_check check_type;\r\n\r\n\t/* Operation mode */\r\n\tenum xz_mode mode;\r\n\r\n\t/*\r\n\t * True if the next call to xz_dec_run() is allowed to return\r\n\t * XZ_BUF_ERROR.\r\n\t */\r\n\tbool allow_buf_error;\r\n\r\n\t/* Information stored in Block Header */\r\n\tstruct {\r\n\t\t/*\r\n\t\t * Value stored in the Compressed Size field, or\r\n\t\t * VLI_UNKNOWN if Compressed Size is not present.\r\n\t\t */\r\n\t\tvli_type compressed;\r\n\r\n\t\t/*\r\n\t\t * Value stored in the Uncompressed Size field, or\r\n\t\t * VLI_UNKNOWN if Uncompressed Size is not present.\r\n\t\t */\r\n\t\tvli_type uncompressed;\r\n\r\n\t\t/* Size of the Block Header field */\r\n\t\tuint32_t size;\r\n\t} block_header;\r\n\r\n\t/* Information collected when decoding Blocks */\r\n\tstruct {\r\n\t\t/* Observed compressed size of the current Block */\r\n\t\tvli_type compressed;\r\n\r\n\t\t/* Observed uncompressed size of the current Block */\r\n\t\tvli_type uncompressed;\r\n\r\n\t\t/* Number of Blocks decoded so far */\r\n\t\tvli_type count;\r\n\r\n\t\t/*\r\n\t\t * Hash calculated from the Block sizes. This is used to\r\n\t\t * validate the Index field.\r\n\t\t */\r\n\t\tstruct xz_dec_hash hash;\r\n\t} block;\r\n\r\n\t/* Variables needed when verifying the Index field */\r\n\tstruct {\r\n\t\t/* Position in dec_index() */\r\n\t\tenum {\r\n\t\t\tSEQ_INDEX_COUNT,\r\n\t\t\tSEQ_INDEX_UNPADDED,\r\n\t\t\tSEQ_INDEX_UNCOMPRESSED\r\n\t\t} sequence;\r\n\r\n\t\t/* Size of the Index in bytes */\r\n\t\tvli_type size;\r\n\r\n\t\t/* Number of Records (matches block.count in valid files) */\r\n\t\tvli_type count;\r\n\r\n\t\t/*\r\n\t\t * Hash calculated from the Records (matches block.hash in\r\n\t\t * valid files).\r\n\t\t */\r\n\t\tstruct xz_dec_hash hash;\r\n\t} index;\r\n\r\n\t/*\r\n\t * Temporary buffer needed to hold Stream Header, Block Header,\r\n\t * and Stream Footer. The Block Header is the biggest (1 KiB)\r\n\t * so we reserve space according to that. buf[] has to be aligned\r\n\t * to a multiple of four bytes; the size_t variables before it\r\n\t * should guarantee this.\r\n\t */\r\n\tstruct {\r\n\t\tsize_t pos;\r\n\t\tsize_t size;\r\n\t\tuint8_t buf[1024];\r\n\t} temp;\r\n\r\n\tstruct xz_dec_lzma2 *lzma2;\r\n\r\n#ifdef XZ_DEC_BCJ\r\n\tstruct xz_dec_bcj *bcj;\r\n\tbool bcj_active;\r\n#endif\r\n};\r\n\r\n#ifdef XZ_DEC_ANY_CHECK\r\n/* Sizes of the Check field with different Check IDs */\r\nstatic const uint8_t check_sizes[16] = {\r\n\t0,\r\n\t4, 4, 4,\r\n\t8, 8, 8,\r\n\t16, 16, 16,\r\n\t32, 32, 32,\r\n\t64, 64, 64\r\n};\r\n#endif\r\n\r\n/*\r\n * Fill s->temp by copying data starting from b->in[b->in_pos]. Caller\r\n * must have set s->temp.pos to indicate how much data we are supposed\r\n * to copy into s->temp.buf. Return true once s->temp.pos has reached\r\n * s->temp.size.\r\n */\r\nstatic bool fill_temp(struct xz_dec *s, struct xz_buf *b)\r\n{\r\n\tsize_t copy_size = min_t(size_t,\r\n\t\t\tb->in_size - b->in_pos, s->temp.size - s->temp.pos);\r\n\r\n\tmemcpy(s->temp.buf + s->temp.pos, b->in + b->in_pos, copy_size);\r\n\tb->in_pos += copy_size;\r\n\ts->temp.pos += copy_size;\r\n\r\n\tif (s->temp.pos == s->temp.size) {\r\n\t\ts->temp.pos = 0;\r\n\t\treturn true;\r\n\t}\r\n\r\n\treturn false;\r\n}\r\n\r\n/* Decode a variable-length integer (little-endian base-128 encoding) */\r\nstatic enum xz_ret dec_vli(struct xz_dec *s, const uint8_t *in,\r\n\t\t\t   size_t *in_pos, size_t in_size)\r\n{\r\n\tuint8_t byte;\r\n\r\n\tif (s->pos == 0)\r\n\t\ts->vli = 0;\r\n\r\n\twhile (*in_pos < in_size) {\r\n\t\tbyte = in[*in_pos];\r\n\t\t++*in_pos;\r\n\r\n\t\ts->vli |= (vli_type)(byte & 0x7F) << s->pos;\r\n\r\n\t\tif ((byte & 0x80) == 0) {\r\n\t\t\t/* Don't allow non-minimal encodings. */\r\n\t\t\tif (byte == 0 && s->pos != 0)\r\n\t\t\t\treturn XZ_DATA_ERROR;\r\n\r\n\t\t\ts->pos = 0;\r\n\t\t\treturn XZ_STREAM_END;\r\n\t\t}\r\n\r\n\t\ts->pos += 7;\r\n\t\tif (s->pos == 7 * VLI_BYTES_MAX)\r\n\t\t\treturn XZ_DATA_ERROR;\r\n\t}\r\n\r\n\treturn XZ_OK;\r\n}\r\n\r\n/*\r\n * Decode the Compressed Data field from a Block. Update and validate\r\n * the observed compressed and uncompressed sizes of the Block so that\r\n * they don't exceed the values possibly stored in the Block Header\r\n * (validation assumes that no integer overflow occurs, since vli_type\r\n * is normally uint64_t). Update the CRC32 or CRC64 value if presence of\r\n * the CRC32 or CRC64 field was indicated in Stream Header.\r\n *\r\n * Once the decoding is finished, validate that the observed sizes match\r\n * the sizes possibly stored in the Block Header. Update the hash and\r\n * Block count, which are later used to validate the Index field.\r\n */\r\nstatic enum xz_ret dec_block(struct xz_dec *s, struct xz_buf *b)\r\n{\r\n\tenum xz_ret ret;\r\n\r\n\ts->in_start = b->in_pos;\r\n\ts->out_start = b->out_pos;\r\n\r\n#ifdef XZ_DEC_BCJ\r\n\tif (s->bcj_active)\r\n\t\tret = xz_dec_bcj_run(s->bcj, s->lzma2, b);\r\n\telse\r\n#endif\r\n\t\tret = xz_dec_lzma2_run(s->lzma2, b);\r\n\r\n\ts->block.compressed += b->in_pos - s->in_start;\r\n\ts->block.uncompressed += b->out_pos - s->out_start;\r\n\r\n\t/*\r\n\t * There is no need to separately check for VLI_UNKNOWN, since\r\n\t * the observed sizes are always smaller than VLI_UNKNOWN.\r\n\t */\r\n\tif (s->block.compressed > s->block_header.compressed\r\n\t\t\t|| s->block.uncompressed\r\n\t\t\t\t> s->block_header.uncompressed)\r\n\t\treturn XZ_DATA_ERROR;\r\n\r\n\tif (s->check_type == XZ_CHECK_CRC32)\r\n\t\ts->crc = xz_crc32(b->out + s->out_start,\r\n\t\t\t\tb->out_pos - s->out_start, s->crc);\r\n#ifdef XZ_USE_CRC64\r\n\telse if (s->check_type == XZ_CHECK_CRC64)\r\n\t\ts->crc = xz_crc64(b->out + s->out_start,\r\n\t\t\t\tb->out_pos - s->out_start, s->crc);\r\n#endif\r\n\r\n\tif (ret == XZ_STREAM_END) {\r\n\t\tif (s->block_header.compressed != VLI_UNKNOWN\r\n\t\t\t\t&& s->block_header.compressed\r\n\t\t\t\t\t!= s->block.compressed)\r\n\t\t\treturn XZ_DATA_ERROR;\r\n\r\n\t\tif (s->block_header.uncompressed != VLI_UNKNOWN\r\n\t\t\t\t&& s->block_header.uncompressed\r\n\t\t\t\t\t!= s->block.uncompressed)\r\n\t\t\treturn XZ_DATA_ERROR;\r\n\r\n\t\ts->block.hash.unpadded += s->block_header.size\r\n\t\t\t\t+ s->block.compressed;\r\n\r\n#ifdef XZ_DEC_ANY_CHECK\r\n\t\ts->block.hash.unpadded += check_sizes[s->check_type];\r\n#else\r\n\t\tif (s->check_type == XZ_CHECK_CRC32)\r\n\t\t\ts->block.hash.unpadded += 4;\r\n\t\telse if (IS_CRC64(s->check_type))\r\n\t\t\ts->block.hash.unpadded += 8;\r\n#endif\r\n\r\n\t\ts->block.hash.uncompressed += s->block.uncompressed;\r\n\t\ts->block.hash.crc32 = xz_crc32(\r\n\t\t\t\t(const uint8_t *)&s->block.hash,\r\n\t\t\t\tsizeof(s->block.hash), s->block.hash.crc32);\r\n\r\n\t\t++s->block.count;\r\n\t}\r\n\r\n\treturn ret;\r\n}\r\n\r\n/* Update the Index size and the CRC32 value. */\r\nstatic void index_update(struct xz_dec *s, const struct xz_buf *b)\r\n{\r\n\tsize_t in_used = b->in_pos - s->in_start;\r\n\ts->index.size += in_used;\r\n\ts->crc = xz_crc32(b->in + s->in_start, in_used, s->crc);\r\n}\r\n\r\n/*\r\n * Decode the Number of Records, Unpadded Size, and Uncompressed Size\r\n * fields from the Index field. That is, Index Padding and CRC32 are not\r\n * decoded by this function.\r\n *\r\n * This can return XZ_OK (more input needed), XZ_STREAM_END (everything\r\n * successfully decoded), or XZ_DATA_ERROR (input is corrupt).\r\n */\r\nstatic enum xz_ret dec_index(struct xz_dec *s, struct xz_buf *b)\r\n{\r\n\tenum xz_ret ret;\r\n\r\n\tdo {\r\n\t\tret = dec_vli(s, b->in, &b->in_pos, b->in_size);\r\n\t\tif (ret != XZ_STREAM_END) {\r\n\t\t\tindex_update(s, b);\r\n\t\t\treturn ret;\r\n\t\t}\r\n\r\n\t\tswitch (s->index.sequence) {\r\n\t\tcase SEQ_INDEX_COUNT:\r\n\t\t\ts->index.count = s->vli;\r\n\r\n\t\t\t/*\r\n\t\t\t * Validate that the Number of Records field\r\n\t\t\t * indicates the same number of Records as\r\n\t\t\t * there were Blocks in the Stream.\r\n\t\t\t */\r\n\t\t\tif (s->index.count != s->block.count)\r\n\t\t\t\treturn XZ_DATA_ERROR;\r\n\r\n\t\t\ts->index.sequence = SEQ_INDEX_UNPADDED;\r\n\t\t\tbreak;\r\n\r\n\t\tcase SEQ_INDEX_UNPADDED:\r\n\t\t\ts->index.hash.unpadded += s->vli;\r\n\t\t\ts->index.sequence = SEQ_INDEX_UNCOMPRESSED;\r\n\t\t\tbreak;\r\n\r\n\t\tcase SEQ_INDEX_UNCOMPRESSED:\r\n\t\t\ts->index.hash.uncompressed += s->vli;\r\n\t\t\ts->index.hash.crc32 = xz_crc32(\r\n\t\t\t\t\t(const uint8_t *)&s->index.hash,\r\n\t\t\t\t\tsizeof(s->index.hash),\r\n\t\t\t\t\ts->index.hash.crc32);\r\n\t\t\t--s->index.count;\r\n\t\t\ts->index.sequence = SEQ_INDEX_UNPADDED;\r\n\t\t\tbreak;\r\n\t\t}\r\n\t} while (s->index.count > 0);\r\n\r\n\treturn XZ_STREAM_END;\r\n}\r\n\r\n/*\r\n * Validate that the next four or eight input bytes match the value\r\n * of s->crc. s->pos must be zero when starting to validate the first byte.\r\n * The \"bits\" argument allows using the same code for both CRC32 and CRC64.\r\n */\r\nstatic enum xz_ret crc_validate(struct xz_dec *s, struct xz_buf *b,\r\n\t\t\t\tuint32_t bits)\r\n{\r\n\tdo {\r\n\t\tif (b->in_pos == b->in_size)\r\n\t\t\treturn XZ_OK;\r\n\r\n\t\tif (((s->crc >> s->pos) & 0xFF) != b->in[b->in_pos++])\r\n\t\t\treturn XZ_DATA_ERROR;\r\n\r\n\t\ts->pos += 8;\r\n\r\n\t} while (s->pos < bits);\r\n\r\n\ts->crc = 0;\r\n\ts->pos = 0;\r\n\r\n\treturn XZ_STREAM_END;\r\n}\r\n\r\n#ifdef XZ_DEC_ANY_CHECK\r\n/*\r\n * Skip over the Check field when the Check ID is not supported.\r\n * Returns true once the whole Check field has been skipped over.\r\n */\r\nstatic bool check_skip(struct xz_dec *s, struct xz_buf *b)\r\n{\r\n\twhile (s->pos < check_sizes[s->check_type]) {\r\n\t\tif (b->in_pos == b->in_size)\r\n\t\t\treturn false;\r\n\r\n\t\t++b->in_pos;\r\n\t\t++s->pos;\r\n\t}\r\n\r\n\ts->pos = 0;\r\n\r\n\treturn true;\r\n}\r\n#endif\r\n\r\n/* Decode the Stream Header field (the first 12 bytes of the .xz Stream). */\r\nstatic enum xz_ret dec_stream_header(struct xz_dec *s)\r\n{\r\n\tif (!memeq(s->temp.buf, HEADER_MAGIC, HEADER_MAGIC_SIZE))\r\n\t\treturn XZ_FORMAT_ERROR;\r\n\r\n\tif (xz_crc32(s->temp.buf + HEADER_MAGIC_SIZE, 2, 0)\r\n\t\t\t!= get_le32(s->temp.buf + HEADER_MAGIC_SIZE + 2))\r\n\t\treturn XZ_DATA_ERROR;\r\n\r\n\tif (s->temp.buf[HEADER_MAGIC_SIZE] != 0)\r\n\t\treturn XZ_OPTIONS_ERROR;\r\n\r\n\t/*\r\n\t * Of integrity checks, we support none (Check ID = 0),\r\n\t * CRC32 (Check ID = 1), and optionally CRC64 (Check ID = 4).\r\n\t * However, if XZ_DEC_ANY_CHECK is defined, we will accept other\r\n\t * check types too, but then the check won't be verified and\r\n\t * a warning (XZ_UNSUPPORTED_CHECK) will be given.\r\n\t */\r\n\ts->check_type = s->temp.buf[HEADER_MAGIC_SIZE + 1];\r\n\r\n#ifdef XZ_DEC_ANY_CHECK\r\n\tif (s->check_type > XZ_CHECK_MAX)\r\n\t\treturn XZ_OPTIONS_ERROR;\r\n\r\n\tif (s->check_type > XZ_CHECK_CRC32 && !IS_CRC64(s->check_type))\r\n\t\treturn XZ_UNSUPPORTED_CHECK;\r\n#else\r\n\tif (s->check_type > XZ_CHECK_CRC32 && !IS_CRC64(s->check_type))\r\n\t\treturn XZ_OPTIONS_ERROR;\r\n#endif\r\n\r\n\treturn XZ_OK;\r\n}\r\n\r\n/* Decode the Stream Footer field (the last 12 bytes of the .xz Stream) */\r\nstatic enum xz_ret dec_stream_footer(struct xz_dec *s)\r\n{\r\n\tif (!memeq(s->temp.buf + 10, FOOTER_MAGIC, FOOTER_MAGIC_SIZE))\r\n\t\treturn XZ_DATA_ERROR;\r\n\r\n\tif (xz_crc32(s->temp.buf + 4, 6, 0) != get_le32(s->temp.buf))\r\n\t\treturn XZ_DATA_ERROR;\r\n\r\n\t/*\r\n\t * Validate Backward Size. Note that we never added the size of the\r\n\t * Index CRC32 field to s->index.size, thus we use s->index.size / 4\r\n\t * instead of s->index.size / 4 - 1.\r\n\t */\r\n\tif ((s->index.size >> 2) != get_le32(s->temp.buf + 4))\r\n\t\treturn XZ_DATA_ERROR;\r\n\r\n\tif (s->temp.buf[8] != 0 || s->temp.buf[9] != s->check_type)\r\n\t\treturn XZ_DATA_ERROR;\r\n\r\n\t/*\r\n\t * Use XZ_STREAM_END instead of XZ_OK to be more convenient\r\n\t * for the caller.\r\n\t */\r\n\treturn XZ_STREAM_END;\r\n}\r\n\r\n/* Decode the Block Header and initialize the filter chain. */\r\nstatic enum xz_ret dec_block_header(struct xz_dec *s)\r\n{\r\n\tenum xz_ret ret;\r\n\r\n\t/*\r\n\t * Validate the CRC32. We know that the temp buffer is at least\r\n\t * eight bytes so this is safe.\r\n\t */\r\n\ts->temp.size -= 4;\r\n\tif (xz_crc32(s->temp.buf, s->temp.size, 0)\r\n\t\t\t!= get_le32(s->temp.buf + s->temp.size))\r\n\t\treturn XZ_DATA_ERROR;\r\n\r\n\ts->temp.pos = 2;\r\n\r\n\t/*\r\n\t * Catch unsupported Block Flags. We support only one or two filters\r\n\t * in the chain, so we catch that with the same test.\r\n\t */\r\n#ifdef XZ_DEC_BCJ\r\n\tif (s->temp.buf[1] & 0x3E)\r\n#else\r\n\tif (s->temp.buf[1] & 0x3F)\r\n#endif\r\n\t\treturn XZ_OPTIONS_ERROR;\r\n\r\n\t/* Compressed Size */\r\n\tif (s->temp.buf[1] & 0x40) {\r\n\t\tif (dec_vli(s, s->temp.buf, &s->temp.pos, s->temp.size)\r\n\t\t\t\t\t!= XZ_STREAM_END)\r\n\t\t\treturn XZ_DATA_ERROR;\r\n\r\n\t\ts->block_header.compressed = s->vli;\r\n\t} else {\r\n\t\ts->block_header.compressed = VLI_UNKNOWN;\r\n\t}\r\n\r\n\t/* Uncompressed Size */\r\n\tif (s->temp.buf[1] & 0x80) {\r\n\t\tif (dec_vli(s, s->temp.buf, &s->temp.pos, s->temp.size)\r\n\t\t\t\t!= XZ_STREAM_END)\r\n\t\t\treturn XZ_DATA_ERROR;\r\n\r\n\t\ts->block_header.uncompressed = s->vli;\r\n\t} else {\r\n\t\ts->block_header.uncompressed = VLI_UNKNOWN;\r\n\t}\r\n\r\n#ifdef XZ_DEC_BCJ\r\n\t/* If there are two filters, the first one must be a BCJ filter. */\r\n\ts->bcj_active = s->temp.buf[1] & 0x01;\r\n\tif (s->bcj_active) {\r\n\t\tif (s->temp.size - s->temp.pos < 2)\r\n\t\t\treturn XZ_OPTIONS_ERROR;\r\n\r\n\t\tret = xz_dec_bcj_reset(s->bcj, s->temp.buf[s->temp.pos++]);\r\n\t\tif (ret != XZ_OK)\r\n\t\t\treturn ret;\r\n\r\n\t\t/*\r\n\t\t * We don't support custom start offset,\r\n\t\t * so Size of Properties must be zero.\r\n\t\t */\r\n\t\tif (s->temp.buf[s->temp.pos++] != 0x00)\r\n\t\t\treturn XZ_OPTIONS_ERROR;\r\n\t}\r\n#endif\r\n\r\n\t/* Valid Filter Flags always take at least two bytes. */\r\n\tif (s->temp.size - s->temp.pos < 2)\r\n\t\treturn XZ_DATA_ERROR;\r\n\r\n\t/* Filter ID = LZMA2 */\r\n\tif (s->temp.buf[s->temp.pos++] != 0x21)\r\n\t\treturn XZ_OPTIONS_ERROR;\r\n\r\n\t/* Size of Properties = 1-byte Filter Properties */\r\n\tif (s->temp.buf[s->temp.pos++] != 0x01)\r\n\t\treturn XZ_OPTIONS_ERROR;\r\n\r\n\t/* Filter Properties contains LZMA2 dictionary size. */\r\n\tif (s->temp.size - s->temp.pos < 1)\r\n\t\treturn XZ_DATA_ERROR;\r\n\r\n\tret = xz_dec_lzma2_reset(s->lzma2, s->temp.buf[s->temp.pos++]);\r\n\tif (ret != XZ_OK)\r\n\t\treturn ret;\r\n\r\n\t/* The rest must be Header Padding. */\r\n\twhile (s->temp.pos < s->temp.size)\r\n\t\tif (s->temp.buf[s->temp.pos++] != 0x00)\r\n\t\t\treturn XZ_OPTIONS_ERROR;\r\n\r\n\ts->temp.pos = 0;\r\n\ts->block.compressed = 0;\r\n\ts->block.uncompressed = 0;\r\n\r\n\treturn XZ_OK;\r\n}\r\n\r\nstatic enum xz_ret dec_main(struct xz_dec *s, struct xz_buf *b)\r\n{\r\n\tenum xz_ret ret;\r\n\r\n\t/*\r\n\t * Store the start position for the case when we are in the middle\r\n\t * of the Index field.\r\n\t */\r\n\ts->in_start = b->in_pos;\r\n\r\n\twhile (true) {\r\n\t\tswitch (s->sequence) {\r\n\t\tcase SEQ_STREAM_HEADER:\r\n\t\t\t/*\r\n\t\t\t * Stream Header is copied to s->temp, and then\r\n\t\t\t * decoded from there. This way if the caller\r\n\t\t\t * gives us only little input at a time, we can\r\n\t\t\t * still keep the Stream Header decoding code\r\n\t\t\t * simple. Similar approach is used in many places\r\n\t\t\t * in this file.\r\n\t\t\t */\r\n\t\t\tif (!fill_temp(s, b))\r\n\t\t\t\treturn XZ_OK;\r\n\r\n\t\t\t/*\r\n\t\t\t * If dec_stream_header() returns\r\n\t\t\t * XZ_UNSUPPORTED_CHECK, it is still possible\r\n\t\t\t * to continue decoding if working in multi-call\r\n\t\t\t * mode. Thus, update s->sequence before calling\r\n\t\t\t * dec_stream_header().\r\n\t\t\t */\r\n\t\t\ts->sequence = SEQ_BLOCK_START;\r\n\r\n\t\t\tret = dec_stream_header(s);\r\n\t\t\tif (ret != XZ_OK)\r\n\t\t\t\treturn ret;\r\n\r\n\t\tcase SEQ_BLOCK_START:\r\n\t\t\t/* We need one byte of input to continue. */\r\n\t\t\tif (b->in_pos == b->in_size)\r\n\t\t\t\treturn XZ_OK;\r\n\r\n\t\t\t/* See if this is the beginning of the Index field. */\r\n\t\t\tif (b->in[b->in_pos] == 0) {\r\n\t\t\t\ts->in_start = b->in_pos++;\r\n\t\t\t\ts->sequence = SEQ_INDEX;\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\r\n\t\t\t/*\r\n\t\t\t * Calculate the size of the Block Header and\r\n\t\t\t * prepare to decode it.\r\n\t\t\t */\r\n\t\t\ts->block_header.size\r\n\t\t\t\t= ((uint32_t)b->in[b->in_pos] + 1) * 4;\r\n\r\n\t\t\ts->temp.size = s->block_header.size;\r\n\t\t\ts->temp.pos = 0;\r\n\t\t\ts->sequence = SEQ_BLOCK_HEADER;\r\n\r\n\t\tcase SEQ_BLOCK_HEADER:\r\n\t\t\tif (!fill_temp(s, b))\r\n\t\t\t\treturn XZ_OK;\r\n\r\n\t\t\tret = dec_block_header(s);\r\n\t\t\tif (ret != XZ_OK)\r\n\t\t\t\treturn ret;\r\n\r\n\t\t\ts->sequence = SEQ_BLOCK_UNCOMPRESS;\r\n\r\n\t\tcase SEQ_BLOCK_UNCOMPRESS:\r\n\t\t\tret = dec_block(s, b);\r\n\t\t\tif (ret != XZ_STREAM_END)\r\n\t\t\t\treturn ret;\r\n\r\n\t\t\ts->sequence = SEQ_BLOCK_PADDING;\r\n\r\n\t\tcase SEQ_BLOCK_PADDING:\r\n\t\t\t/*\r\n\t\t\t * Size of Compressed Data + Block Padding\r\n\t\t\t * must be a multiple of four. We don't need\r\n\t\t\t * s->block.compressed for anything else\r\n\t\t\t * anymore, so we use it here to test the size\r\n\t\t\t * of the Block Padding field.\r\n\t\t\t */\r\n\t\t\twhile (s->block.compressed & 3) {\r\n\t\t\t\tif (b->in_pos == b->in_size)\r\n\t\t\t\t\treturn XZ_OK;\r\n\r\n\t\t\t\tif (b->in[b->in_pos++] != 0)\r\n\t\t\t\t\treturn XZ_DATA_ERROR;\r\n\r\n\t\t\t\t++s->block.compressed;\r\n\t\t\t}\r\n\r\n\t\t\ts->sequence = SEQ_BLOCK_CHECK;\r\n\r\n\t\tcase SEQ_BLOCK_CHECK:\r\n\t\t\tif (s->check_type == XZ_CHECK_CRC32) {\r\n\t\t\t\tret = crc_validate(s, b, 32);\r\n\t\t\t\tif (ret != XZ_STREAM_END)\r\n\t\t\t\t\treturn ret;\r\n\t\t\t}\r\n\t\t\telse if (IS_CRC64(s->check_type)) {\r\n\t\t\t\tret = crc_validate(s, b, 64);\r\n\t\t\t\tif (ret != XZ_STREAM_END)\r\n\t\t\t\t\treturn ret;\r\n\t\t\t}\r\n#ifdef XZ_DEC_ANY_CHECK\r\n\t\t\telse if (!check_skip(s, b)) {\r\n\t\t\t\treturn XZ_OK;\r\n\t\t\t}\r\n#endif\r\n\r\n\t\t\ts->sequence = SEQ_BLOCK_START;\r\n\t\t\tbreak;\r\n\r\n\t\tcase SEQ_INDEX:\r\n\t\t\tret = dec_index(s, b);\r\n\t\t\tif (ret != XZ_STREAM_END)\r\n\t\t\t\treturn ret;\r\n\r\n\t\t\ts->sequence = SEQ_INDEX_PADDING;\r\n\r\n\t\tcase SEQ_INDEX_PADDING:\r\n\t\t\twhile ((s->index.size + (b->in_pos - s->in_start))\r\n\t\t\t\t\t& 3) {\r\n\t\t\t\tif (b->in_pos == b->in_size) {\r\n\t\t\t\t\tindex_update(s, b);\r\n\t\t\t\t\treturn XZ_OK;\r\n\t\t\t\t}\r\n\r\n\t\t\t\tif (b->in[b->in_pos++] != 0)\r\n\t\t\t\t\treturn XZ_DATA_ERROR;\r\n\t\t\t}\r\n\r\n\t\t\t/* Finish the CRC32 value and Index size. */\r\n\t\t\tindex_update(s, b);\r\n\r\n\t\t\t/* Compare the hashes to validate the Index field. */\r\n\t\t\tif (!memeq(&s->block.hash, &s->index.hash,\r\n\t\t\t\t\tsizeof(s->block.hash)))\r\n\t\t\t\treturn XZ_DATA_ERROR;\r\n\r\n\t\t\ts->sequence = SEQ_INDEX_CRC32;\r\n\r\n\t\tcase SEQ_INDEX_CRC32:\r\n\t\t\tret = crc_validate(s, b, 32);\r\n\t\t\tif (ret != XZ_STREAM_END)\r\n\t\t\t\treturn ret;\r\n\r\n\t\t\ts->temp.size = STREAM_HEADER_SIZE;\r\n\t\t\ts->sequence = SEQ_STREAM_FOOTER;\r\n\r\n\t\tcase SEQ_STREAM_FOOTER:\r\n\t\t\tif (!fill_temp(s, b))\r\n\t\t\t\treturn XZ_OK;\r\n\r\n\t\t\treturn dec_stream_footer(s);\r\n\t\t}\r\n\t}\r\n\r\n\t/* Never reached */\r\n}\r\n\r\n/*\r\n * xz_dec_run() is a wrapper for dec_main() to handle some special cases in\r\n * multi-call and single-call decoding.\r\n *\r\n * In multi-call mode, we must return XZ_BUF_ERROR when it seems clear that we\r\n * are not going to make any progress anymore. This is to prevent the caller\r\n * from calling us infinitely when the input file is truncated or otherwise\r\n * corrupt. Since zlib-style API allows that the caller fills the input buffer\r\n * only when the decoder doesn't produce any new output, we have to be careful\r\n * to avoid returning XZ_BUF_ERROR too easily: XZ_BUF_ERROR is returned only\r\n * after the second consecutive call to xz_dec_run() that makes no progress.\r\n *\r\n * In single-call mode, if we couldn't decode everything and no error\r\n * occurred, either the input is truncated or the output buffer is too small.\r\n * Since we know that the last input byte never produces any output, we know\r\n * that if all the input was consumed and decoding wasn't finished, the file\r\n * must be corrupt. Otherwise the output buffer has to be too small or the\r\n * file is corrupt in a way that decoding it produces too big output.\r\n *\r\n * If single-call decoding fails, we reset b->in_pos and b->out_pos back to\r\n * their original values. This is because with some filter chains there won't\r\n * be any valid uncompressed data in the output buffer unless the decoding\r\n * actually succeeds (that's the price to pay of using the output buffer as\r\n * the workspace).\r\n */\r\nXZ_EXTERN enum xz_ret xz_dec_run(struct xz_dec *s, struct xz_buf *b)\r\n{\r\n\tsize_t in_start;\r\n\tsize_t out_start;\r\n\tenum xz_ret ret;\r\n\r\n\tif (DEC_IS_SINGLE(s->mode))\r\n\t\txz_dec_reset(s);\r\n\r\n\tin_start = b->in_pos;\r\n\tout_start = b->out_pos;\r\n\tret = dec_main(s, b);\r\n\r\n\tif (DEC_IS_SINGLE(s->mode)) {\r\n\t\tif (ret == XZ_OK)\r\n\t\t\tret = b->in_pos == b->in_size\r\n\t\t\t\t\t? XZ_DATA_ERROR : XZ_BUF_ERROR;\r\n\r\n\t\tif (ret != XZ_STREAM_END) {\r\n\t\t\tb->in_pos = in_start;\r\n\t\t\tb->out_pos = out_start;\r\n\t\t}\r\n\r\n\t} else if (ret == XZ_OK && in_start == b->in_pos\r\n\t\t\t&& out_start == b->out_pos) {\r\n\t\tif (s->allow_buf_error)\r\n\t\t\tret = XZ_BUF_ERROR;\r\n\r\n\t\ts->allow_buf_error = true;\r\n\t} else {\r\n\t\ts->allow_buf_error = false;\r\n\t}\r\n\r\n\treturn ret;\r\n}\r\n\r\nXZ_EXTERN struct xz_dec *xz_dec_init(enum xz_mode mode, uint32_t dict_max)\r\n{\r\n\tstruct xz_dec *s = kmalloc(sizeof(*s), GFP_KERNEL);\r\n\tif (s == NULL)\r\n\t\treturn NULL;\r\n\r\n\ts->mode = mode;\r\n\r\n#ifdef XZ_DEC_BCJ\r\n\ts->bcj = xz_dec_bcj_create(DEC_IS_SINGLE(mode));\r\n\tif (s->bcj == NULL)\r\n\t\tgoto error_bcj;\r\n#endif\r\n\r\n\ts->lzma2 = xz_dec_lzma2_create(mode, dict_max);\r\n\tif (s->lzma2 == NULL)\r\n\t\tgoto error_lzma2;\r\n\r\n\txz_dec_reset(s);\r\n\treturn s;\r\n\r\nerror_lzma2:\r\n#ifdef XZ_DEC_BCJ\r\n\txz_dec_bcj_end(s->bcj);\r\nerror_bcj:\r\n#endif\r\n\tkfree(s);\r\n\treturn NULL;\r\n}\r\n\r\nXZ_EXTERN void xz_dec_reset(struct xz_dec *s)\r\n{\r\n\ts->sequence = SEQ_STREAM_HEADER;\r\n\ts->allow_buf_error = false;\r\n\ts->pos = 0;\r\n\ts->crc = 0;\r\n\tmemzero(&s->block, sizeof(s->block));\r\n\tmemzero(&s->index, sizeof(s->index));\r\n\ts->temp.pos = 0;\r\n\ts->temp.size = STREAM_HEADER_SIZE;\r\n}\r\n\r\nXZ_EXTERN void xz_dec_end(struct xz_dec *s)\r\n{\r\n\tif (s != NULL) {\r\n\t\txz_dec_lzma2_end(s->lzma2);\r\n#ifdef XZ_DEC_BCJ\r\n\t\txz_dec_bcj_end(s->bcj);\r\n#endif\r\n\t\tkfree(s);\r\n\t}\r\n}\r\n\r\n//END xz_dec_stream.c\r\n//BEGIN xz_dec_lzma2.c\r\n/*\r\n * LZMA2 decoder\r\n *\r\n * Authors: Lasse Collin <lasse.collin@tukaani.org>\r\n *          Igor Pavlov <http://7-zip.org/>\r\n *\r\n * This file has been put into the public domain.\r\n * You can do whatever you want with this file.\r\n */\r\n\r\n/*\r\n * Range decoder initialization eats the first five bytes of each LZMA chunk.\r\n */\r\n#define RC_INIT_BYTES 5\r\n\r\n/*\r\n * Minimum number of usable input buffer to safely decode one LZMA symbol.\r\n * The worst case is that we decode 22 bits using probabilities and 26\r\n * direct bits. This may decode at maximum of 20 bytes of input. However,\r\n * lzma_main() does an extra normalization before returning, thus we\r\n * need to put 21 here.\r\n */\r\n#define LZMA_IN_REQUIRED 21\r\n\r\n/*\r\n * Dictionary (history buffer)\r\n *\r\n * These are always true:\r\n *    start <= pos <= full <= end\r\n *    pos <= limit <= end\r\n *\r\n * In multi-call mode, also these are true:\r\n *    end == size\r\n *    size <= size_max\r\n *    allocated <= size\r\n *\r\n * Most of these variables are size_t to support single-call mode,\r\n * in which the dictionary variables address the actual output\r\n * buffer directly.\r\n */\r\nstruct dictionary {\r\n\t/* Beginning of the history buffer */\r\n\tuint8_t *buf;\r\n\r\n\t/* Old position in buf (before decoding more data) */\r\n\tsize_t start;\r\n\r\n\t/* Position in buf */\r\n\tsize_t pos;\r\n\r\n\t/*\r\n\t * How full dictionary is. This is used to detect corrupt input that\r\n\t * would read beyond the beginning of the uncompressed stream.\r\n\t */\r\n\tsize_t full;\r\n\r\n\t/* Write limit; we don't write to buf[limit] or later bytes. */\r\n\tsize_t limit;\r\n\r\n\t/*\r\n\t * End of the dictionary buffer. In multi-call mode, this is\r\n\t * the same as the dictionary size. In single-call mode, this\r\n\t * indicates the size of the output buffer.\r\n\t */\r\n\tsize_t end;\r\n\r\n\t/*\r\n\t * Size of the dictionary as specified in Block Header. This is used\r\n\t * together with \"full\" to detect corrupt input that would make us\r\n\t * read beyond the beginning of the uncompressed stream.\r\n\t */\r\n\tuint32_t size;\r\n\r\n\t/*\r\n\t * Maximum allowed dictionary size in multi-call mode.\r\n\t * This is ignored in single-call mode.\r\n\t */\r\n\tuint32_t size_max;\r\n\r\n\t/*\r\n\t * Amount of memory currently allocated for the dictionary.\r\n\t * This is used only with XZ_DYNALLOC. (With XZ_PREALLOC,\r\n\t * size_max is always the same as the allocated size.)\r\n\t */\r\n\tuint32_t allocated;\r\n\r\n\t/* Operation mode */\r\n\tenum xz_mode mode;\r\n};\r\n\r\n/* Range decoder */\r\nstruct rc_dec {\r\n\tuint32_t range;\r\n\tuint32_t code;\r\n\r\n\t/*\r\n\t * Number of initializing bytes remaining to be read\r\n\t * by rc_read_init().\r\n\t */\r\n\tuint32_t init_bytes_left;\r\n\r\n\t/*\r\n\t * Buffer from which we read our input. It can be either\r\n\t * temp.buf or the caller-provided input buffer.\r\n\t */\r\n\tconst uint8_t *in;\r\n\tsize_t in_pos;\r\n\tsize_t in_limit;\r\n};\r\n\r\n/* Probabilities for a length decoder. */\r\nstruct lzma_len_dec {\r\n\t/* Probability of match length being at least 10 */\r\n\tuint16_t choice;\r\n\r\n\t/* Probability of match length being at least 18 */\r\n\tuint16_t choice2;\r\n\r\n\t/* Probabilities for match lengths 2-9 */\r\n\tuint16_t low[POS_STATES_MAX][LEN_LOW_SYMBOLS];\r\n\r\n\t/* Probabilities for match lengths 10-17 */\r\n\tuint16_t mid[POS_STATES_MAX][LEN_MID_SYMBOLS];\r\n\r\n\t/* Probabilities for match lengths 18-273 */\r\n\tuint16_t high[LEN_HIGH_SYMBOLS];\r\n};\r\n\r\nstruct lzma_dec {\r\n\t/* Distances of latest four matches */\r\n\tuint32_t rep0;\r\n\tuint32_t rep1;\r\n\tuint32_t rep2;\r\n\tuint32_t rep3;\r\n\r\n\t/* Types of the most recently seen LZMA symbols */\r\n\tenum lzma_state state;\r\n\r\n\t/*\r\n\t * Length of a match. This is updated so that dict_repeat can\r\n\t * be called again to finish repeating the whole match.\r\n\t */\r\n\tuint32_t len;\r\n\r\n\t/*\r\n\t * LZMA properties or related bit masks (number of literal\r\n\t * context bits, a mask dervied from the number of literal\r\n\t * position bits, and a mask dervied from the number\r\n\t * position bits)\r\n\t */\r\n\tuint32_t lc;\r\n\tuint32_t literal_pos_mask; /* (1 << lp) - 1 */\r\n\tuint32_t pos_mask;         /* (1 << pb) - 1 */\r\n\r\n\t/* If 1, it's a match. Otherwise it's a single 8-bit literal. */\r\n\tuint16_t is_match[STATES][POS_STATES_MAX];\r\n\r\n\t/* If 1, it's a repeated match. The distance is one of rep0 .. rep3. */\r\n\tuint16_t is_rep[STATES];\r\n\r\n\t/*\r\n\t * If 0, distance of a repeated match is rep0.\r\n\t * Otherwise check is_rep1.\r\n\t */\r\n\tuint16_t is_rep0[STATES];\r\n\r\n\t/*\r\n\t * If 0, distance of a repeated match is rep1.\r\n\t * Otherwise check is_rep2.\r\n\t */\r\n\tuint16_t is_rep1[STATES];\r\n\r\n\t/* If 0, distance of a repeated match is rep2. Otherwise it is rep3. */\r\n\tuint16_t is_rep2[STATES];\r\n\r\n\t/*\r\n\t * If 1, the repeated match has length of one byte. Otherwise\r\n\t * the length is decoded from rep_len_decoder.\r\n\t */\r\n\tuint16_t is_rep0_long[STATES][POS_STATES_MAX];\r\n\r\n\t/*\r\n\t * Probability tree for the highest two bits of the match\r\n\t * distance. There is a separate probability tree for match\r\n\t * lengths of 2 (i.e. MATCH_LEN_MIN), 3, 4, and [5, 273].\r\n\t */\r\n\tuint16_t dist_slot[DIST_STATES][DIST_SLOTS];\r\n\r\n\t/*\r\n\t * Probility trees for additional bits for match distance\r\n\t * when the distance is in the range [4, 127].\r\n\t */\r\n\tuint16_t dist_special[FULL_DISTANCES - DIST_MODEL_END];\r\n\r\n\t/*\r\n\t * Probability tree for the lowest four bits of a match\r\n\t * distance that is equal to or greater than 128.\r\n\t */\r\n\tuint16_t dist_align[ALIGN_SIZE];\r\n\r\n\t/* Length of a normal match */\r\n\tstruct lzma_len_dec match_len_dec;\r\n\r\n\t/* Length of a repeated match */\r\n\tstruct lzma_len_dec rep_len_dec;\r\n\r\n\t/* Probabilities of literals */\r\n\tuint16_t literal[LITERAL_CODERS_MAX][LITERAL_CODER_SIZE];\r\n};\r\n\r\nstruct lzma2_dec {\r\n\t/* Position in xz_dec_lzma2_run(). */\r\n\tenum lzma2_seq {\r\n\t\tSEQ_CONTROL,\r\n\t\tSEQ_UNCOMPRESSED_1,\r\n\t\tSEQ_UNCOMPRESSED_2,\r\n\t\tSEQ_COMPRESSED_0,\r\n\t\tSEQ_COMPRESSED_1,\r\n\t\tSEQ_PROPERTIES,\r\n\t\tSEQ_LZMA_PREPARE,\r\n\t\tSEQ_LZMA_RUN,\r\n\t\tSEQ_COPY\r\n\t} sequence;\r\n\r\n\t/* Next position after decoding the compressed size of the chunk. */\r\n\tenum lzma2_seq next_sequence;\r\n\r\n\t/* Uncompressed size of LZMA chunk (2 MiB at maximum) */\r\n\tuint32_t uncompressed;\r\n\r\n\t/*\r\n\t * Compressed size of LZMA chunk or compressed/uncompressed\r\n\t * size of uncompressed chunk (64 KiB at maximum)\r\n\t */\r\n\tuint32_t compressed;\r\n\r\n\t/*\r\n\t * True if dictionary reset is needed. This is false before\r\n\t * the first chunk (LZMA or uncompressed).\r\n\t */\r\n\tbool need_dict_reset;\r\n\r\n\t/*\r\n\t * True if new LZMA properties are needed. This is false\r\n\t * before the first LZMA chunk.\r\n\t */\r\n\tbool need_props;\r\n};\r\n\r\nstruct xz_dec_lzma2 {\r\n\t/*\r\n\t * The order below is important on x86 to reduce code size and\r\n\t * it shouldn't hurt on other platforms. Everything up to and\r\n\t * including lzma.pos_mask are in the first 128 bytes on x86-32,\r\n\t * which allows using smaller instructions to access those\r\n\t * variables. On x86-64, fewer variables fit into the first 128\r\n\t * bytes, but this is still the best order without sacrificing\r\n\t * the readability by splitting the structures.\r\n\t */\r\n\tstruct rc_dec rc;\r\n\tstruct dictionary dict;\r\n\tstruct lzma2_dec lzma2;\r\n\tstruct lzma_dec lzma;\r\n\r\n\t/*\r\n\t * Temporary buffer which holds small number of input bytes between\r\n\t * decoder calls. See lzma2_lzma() for details.\r\n\t */\r\n\tstruct {\r\n\t\tuint32_t size;\r\n\t\tuint8_t buf[3 * LZMA_IN_REQUIRED];\r\n\t} temp;\r\n};\r\n\r\n/**************\r\n * Dictionary *\r\n **************/\r\n\r\n/*\r\n * Reset the dictionary state. When in single-call mode, set up the beginning\r\n * of the dictionary to point to the actual output buffer.\r\n */\r\nstatic void dict_reset(struct dictionary *dict, struct xz_buf *b)\r\n{\r\n\tif (DEC_IS_SINGLE(dict->mode)) {\r\n\t\tdict->buf = b->out + b->out_pos;\r\n\t\tdict->end = b->out_size - b->out_pos;\r\n\t}\r\n\r\n\tdict->start = 0;\r\n\tdict->pos = 0;\r\n\tdict->limit = 0;\r\n\tdict->full = 0;\r\n}\r\n\r\n/* Set dictionary write limit */\r\nstatic void dict_limit(struct dictionary *dict, size_t out_max)\r\n{\r\n\tif (dict->end - dict->pos <= out_max)\r\n\t\tdict->limit = dict->end;\r\n\telse\r\n\t\tdict->limit = dict->pos + out_max;\r\n}\r\n\r\n/* Return true if at least one byte can be written into the dictionary. */\r\nstatic inline bool dict_has_space(const struct dictionary *dict)\r\n{\r\n\treturn dict->pos < dict->limit;\r\n}\r\n\r\n/*\r\n * Get a byte from the dictionary at the given distance. The distance is\r\n * assumed to valid, or as a special case, zero when the dictionary is\r\n * still empty. This special case is needed for single-call decoding to\r\n * avoid writing a '\\0' to the end of the destination buffer.\r\n */\r\nstatic inline uint32_t dict_get(const struct dictionary *dict, uint32_t dist)\r\n{\r\n\tsize_t offset = dict->pos - dist - 1;\r\n\r\n\tif (dist >= dict->pos)\r\n\t\toffset += dict->end;\r\n\r\n\treturn dict->full > 0 ? dict->buf[offset] : 0;\r\n}\r\n\r\n/*\r\n * Put one byte into the dictionary. It is assumed that there is space for it.\r\n */\r\nstatic inline void dict_put(struct dictionary *dict, uint8_t byte)\r\n{\r\n\tdict->buf[dict->pos++] = byte;\r\n\r\n\tif (dict->full < dict->pos)\r\n\t\tdict->full = dict->pos;\r\n}\r\n\r\n/*\r\n * Repeat given number of bytes from the given distance. If the distance is\r\n * invalid, false is returned. On success, true is returned and *len is\r\n * updated to indicate how many bytes were left to be repeated.\r\n */\r\nstatic bool dict_repeat(struct dictionary *dict, uint32_t *len, uint32_t dist)\r\n{\r\n\tsize_t back;\r\n\tuint32_t left;\r\n\r\n\tif (dist >= dict->full || dist >= dict->size)\r\n\t\treturn false;\r\n\r\n\tleft = min_t(size_t, dict->limit - dict->pos, *len);\r\n\t*len -= left;\r\n\r\n\tback = dict->pos - dist - 1;\r\n\tif (dist >= dict->pos)\r\n\t\tback += dict->end;\r\n\r\n\tdo {\r\n\t\tdict->buf[dict->pos++] = dict->buf[back++];\r\n\t\tif (back == dict->end)\r\n\t\t\tback = 0;\r\n\t} while (--left > 0);\r\n\r\n\tif (dict->full < dict->pos)\r\n\t\tdict->full = dict->pos;\r\n\r\n\treturn true;\r\n}\r\n\r\n/* Copy uncompressed data as is from input to dictionary and output buffers. */\r\nstatic void dict_uncompressed(struct dictionary *dict, struct xz_buf *b,\r\n\t\t\t      uint32_t *left)\r\n{\r\n\tsize_t copy_size;\r\n\r\n\twhile (*left > 0 && b->in_pos < b->in_size\r\n\t\t\t&& b->out_pos < b->out_size) {\r\n\t\tcopy_size = min(b->in_size - b->in_pos,\r\n\t\t\t\tb->out_size - b->out_pos);\r\n\t\tif (copy_size > dict->end - dict->pos)\r\n\t\t\tcopy_size = dict->end - dict->pos;\r\n\t\tif (copy_size > *left)\r\n\t\t\tcopy_size = *left;\r\n\r\n\t\t*left -= copy_size;\r\n\r\n\t\tmemcpy(dict->buf + dict->pos, b->in + b->in_pos, copy_size);\r\n\t\tdict->pos += copy_size;\r\n\r\n\t\tif (dict->full < dict->pos)\r\n\t\t\tdict->full = dict->pos;\r\n\r\n\t\tif (DEC_IS_MULTI(dict->mode)) {\r\n\t\t\tif (dict->pos == dict->end)\r\n\t\t\t\tdict->pos = 0;\r\n\r\n\t\t\tmemcpy(b->out + b->out_pos, b->in + b->in_pos,\r\n\t\t\t\t\tcopy_size);\r\n\t\t}\r\n\r\n\t\tdict->start = dict->pos;\r\n\r\n\t\tb->out_pos += copy_size;\r\n\t\tb->in_pos += copy_size;\r\n\t}\r\n}\r\n\r\n/*\r\n * Flush pending data from dictionary to b->out. It is assumed that there is\r\n * enough space in b->out. This is guaranteed because caller uses dict_limit()\r\n * before decoding data into the dictionary.\r\n */\r\nstatic uint32_t dict_flush(struct dictionary *dict, struct xz_buf *b)\r\n{\r\n\tsize_t copy_size = dict->pos - dict->start;\r\n\r\n\tif (DEC_IS_MULTI(dict->mode)) {\r\n\t\tif (dict->pos == dict->end)\r\n\t\t\tdict->pos = 0;\r\n\r\n\t\tmemcpy(b->out + b->out_pos, dict->buf + dict->start,\r\n\t\t\t\tcopy_size);\r\n\t}\r\n\r\n\tdict->start = dict->pos;\r\n\tb->out_pos += copy_size;\r\n\treturn copy_size;\r\n}\r\n\r\n/*****************\r\n * Range decoder *\r\n *****************/\r\n\r\n/* Reset the range decoder. */\r\nstatic void rc_reset(struct rc_dec *rc)\r\n{\r\n\trc->range = (uint32_t)-1;\r\n\trc->code = 0;\r\n\trc->init_bytes_left = RC_INIT_BYTES;\r\n}\r\n\r\n/*\r\n * Read the first five initial bytes into rc->code if they haven't been\r\n * read already. (Yes, the first byte gets completely ignored.)\r\n */\r\nstatic bool rc_read_init(struct rc_dec *rc, struct xz_buf *b)\r\n{\r\n\twhile (rc->init_bytes_left > 0) {\r\n\t\tif (b->in_pos == b->in_size)\r\n\t\t\treturn false;\r\n\r\n\t\trc->code = (rc->code << 8) + b->in[b->in_pos++];\r\n\t\t--rc->init_bytes_left;\r\n\t}\r\n\r\n\treturn true;\r\n}\r\n\r\n/* Return true if there may not be enough input for the next decoding loop. */\r\nstatic inline bool rc_limit_exceeded(const struct rc_dec *rc)\r\n{\r\n\treturn rc->in_pos > rc->in_limit;\r\n}\r\n\r\n/*\r\n * Return true if it is possible (from point of view of range decoder) that\r\n * we have reached the end of the LZMA chunk.\r\n */\r\nstatic inline bool rc_is_finished(const struct rc_dec *rc)\r\n{\r\n\treturn rc->code == 0;\r\n}\r\n\r\n/* Read the next input byte if needed. */\r\nstatic always_inline void rc_normalize(struct rc_dec *rc)\r\n{\r\n\tif (rc->range < RC_TOP_VALUE) {\r\n\t\trc->range <<= RC_SHIFT_BITS;\r\n\t\trc->code = (rc->code << RC_SHIFT_BITS) + rc->in[rc->in_pos++];\r\n\t}\r\n}\r\n\r\n/*\r\n * Decode one bit. In some versions, this function has been splitted in three\r\n * functions so that the compiler is supposed to be able to more easily avoid\r\n * an extra branch. In this particular version of the LZMA decoder, this\r\n * doesn't seem to be a good idea (tested with GCC 3.3.6, 3.4.6, and 4.3.3\r\n * on x86). Using a non-splitted version results in nicer looking code too.\r\n *\r\n * NOTE: This must return an int. Do not make it return a bool or the speed\r\n * of the code generated by GCC 3.x decreases 10-15 %. (GCC 4.3 doesn't care,\r\n * and it generates 10-20 % faster code than GCC 3.x from this file anyway.)\r\n */\r\nstatic always_inline int rc_bit(struct rc_dec *rc, uint16_t *prob)\r\n{\r\n\tuint32_t bound;\r\n\tint bit;\r\n\r\n\trc_normalize(rc);\r\n\tbound = (rc->range >> RC_BIT_MODEL_TOTAL_BITS) * *prob;\r\n\tif (rc->code < bound) {\r\n\t\trc->range = bound;\r\n\t\t*prob += (RC_BIT_MODEL_TOTAL - *prob) >> RC_MOVE_BITS;\r\n\t\tbit = 0;\r\n\t} else {\r\n\t\trc->range -= bound;\r\n\t\trc->code -= bound;\r\n\t\t*prob -= *prob >> RC_MOVE_BITS;\r\n\t\tbit = 1;\r\n\t}\r\n\r\n\treturn bit;\r\n}\r\n\r\n/* Decode a bittree starting from the most significant bit. */\r\nstatic always_inline uint32_t rc_bittree(struct rc_dec *rc,\r\n\t\t\t\t\t   uint16_t *probs, uint32_t limit)\r\n{\r\n\tuint32_t symbol = 1;\r\n\r\n\tdo {\r\n\t\tif (rc_bit(rc, &probs[symbol]))\r\n\t\t\tsymbol = (symbol << 1) + 1;\r\n\t\telse\r\n\t\t\tsymbol <<= 1;\r\n\t} while (symbol < limit);\r\n\r\n\treturn symbol;\r\n}\r\n\r\n/* Decode a bittree starting from the least significant bit. */\r\nstatic always_inline void rc_bittree_reverse(struct rc_dec *rc,\r\n\t\t\t\t\t       uint16_t *probs,\r\n\t\t\t\t\t       uint32_t *dest, uint32_t limit)\r\n{\r\n\tuint32_t symbol = 1;\r\n\tuint32_t i = 0;\r\n\r\n\tdo {\r\n\t\tif (rc_bit(rc, &probs[symbol])) {\r\n\t\t\tsymbol = (symbol << 1) + 1;\r\n\t\t\t*dest += 1 << i;\r\n\t\t} else {\r\n\t\t\tsymbol <<= 1;\r\n\t\t}\r\n\t} while (++i < limit);\r\n}\r\n\r\n/* Decode direct bits (fixed fifty-fifty probability) */\r\nstatic inline void rc_direct(struct rc_dec *rc, uint32_t *dest, uint32_t limit)\r\n{\r\n\tuint32_t mask;\r\n\r\n\tdo {\r\n\t\trc_normalize(rc);\r\n\t\trc->range >>= 1;\r\n\t\trc->code -= rc->range;\r\n\t\tmask = (uint32_t)0 - (rc->code >> 31);\r\n\t\trc->code += rc->range & mask;\r\n\t\t*dest = (*dest << 1) + (mask + 1);\r\n\t} while (--limit > 0);\r\n}\r\n\r\n/********\r\n * LZMA *\r\n ********/\r\n\r\n/* Get pointer to literal coder probability array. */\r\nstatic uint16_t *lzma_literal_probs(struct xz_dec_lzma2 *s)\r\n{\r\n\tuint32_t prev_byte = dict_get(&s->dict, 0);\r\n\tuint32_t low = prev_byte >> (8 - s->lzma.lc);\r\n\tuint32_t high = (s->dict.pos & s->lzma.literal_pos_mask) << s->lzma.lc;\r\n\treturn s->lzma.literal[low + high];\r\n}\r\n\r\n/* Decode a literal (one 8-bit byte) */\r\nstatic void lzma_literal(struct xz_dec_lzma2 *s)\r\n{\r\n\tuint16_t *probs;\r\n\tuint32_t symbol;\r\n\tuint32_t match_byte;\r\n\tuint32_t match_bit;\r\n\tuint32_t offset;\r\n\tuint32_t i;\r\n\r\n\tprobs = lzma_literal_probs(s);\r\n\r\n\tif (lzma_state_is_literal(s->lzma.state)) {\r\n\t\tsymbol = rc_bittree(&s->rc, probs, 0x100);\r\n\t} else {\r\n\t\tsymbol = 1;\r\n\t\tmatch_byte = dict_get(&s->dict, s->lzma.rep0) << 1;\r\n\t\toffset = 0x100;\r\n\r\n\t\tdo {\r\n\t\t\tmatch_bit = match_byte & offset;\r\n\t\t\tmatch_byte <<= 1;\r\n\t\t\ti = offset + match_bit + symbol;\r\n\r\n\t\t\tif (rc_bit(&s->rc, &probs[i])) {\r\n\t\t\t\tsymbol = (symbol << 1) + 1;\r\n\t\t\t\toffset &= match_bit;\r\n\t\t\t} else {\r\n\t\t\t\tsymbol <<= 1;\r\n\t\t\t\toffset &= ~match_bit;\r\n\t\t\t}\r\n\t\t} while (symbol < 0x100);\r\n\t}\r\n\r\n\tdict_put(&s->dict, (uint8_t)(symbol&0xff));\r\n\tlzma_state_literal(&s->lzma.state);\r\n}\r\n\r\n/* Decode the length of the match into s->lzma.len. */\r\nstatic void lzma_len(struct xz_dec_lzma2 *s, struct lzma_len_dec *l,\r\n\t\t     uint32_t pos_state)\r\n{\r\n\tuint16_t *probs;\r\n\tuint32_t limit;\r\n\r\n\tif (!rc_bit(&s->rc, &l->choice)) {\r\n\t\tprobs = l->low[pos_state];\r\n\t\tlimit = LEN_LOW_SYMBOLS;\r\n\t\ts->lzma.len = MATCH_LEN_MIN;\r\n\t} else {\r\n\t\tif (!rc_bit(&s->rc, &l->choice2)) {\r\n\t\t\tprobs = l->mid[pos_state];\r\n\t\t\tlimit = LEN_MID_SYMBOLS;\r\n\t\t\ts->lzma.len = MATCH_LEN_MIN + LEN_LOW_SYMBOLS;\r\n\t\t} else {\r\n\t\t\tprobs = l->high;\r\n\t\t\tlimit = LEN_HIGH_SYMBOLS;\r\n\t\t\ts->lzma.len = MATCH_LEN_MIN + LEN_LOW_SYMBOLS\r\n\t\t\t\t\t+ LEN_MID_SYMBOLS;\r\n\t\t}\r\n\t}\r\n\r\n\ts->lzma.len += rc_bittree(&s->rc, probs, limit) - limit;\r\n}\r\n\r\n/* Decode a match. The distance will be stored in s->lzma.rep0. */\r\nstatic void lzma_match(struct xz_dec_lzma2 *s, uint32_t pos_state)\r\n{\r\n\tuint16_t *probs;\r\n\tuint32_t dist_slot;\r\n\tuint32_t limit;\r\n\r\n\tlzma_state_match(&s->lzma.state);\r\n\r\n\ts->lzma.rep3 = s->lzma.rep2;\r\n\ts->lzma.rep2 = s->lzma.rep1;\r\n\ts->lzma.rep1 = s->lzma.rep0;\r\n\r\n\tlzma_len(s, &s->lzma.match_len_dec, pos_state);\r\n\r\n\tprobs = s->lzma.dist_slot[lzma_get_dist_state(s->lzma.len)];\r\n\tdist_slot = rc_bittree(&s->rc, probs, DIST_SLOTS) - DIST_SLOTS;\r\n\r\n\tif (dist_slot < DIST_MODEL_START) {\r\n\t\ts->lzma.rep0 = dist_slot;\r\n\t} else {\r\n\t\tlimit = (dist_slot >> 1) - 1;\r\n\t\ts->lzma.rep0 = 2 + (dist_slot & 1);\r\n\r\n\t\tif (dist_slot < DIST_MODEL_END) {\r\n\t\t\ts->lzma.rep0 <<= limit;\r\n\t\t\tprobs = s->lzma.dist_special + s->lzma.rep0\r\n\t\t\t\t\t- dist_slot - 1;\r\n\t\t\trc_bittree_reverse(&s->rc, probs,\r\n\t\t\t\t\t&s->lzma.rep0, limit);\r\n\t\t} else {\r\n\t\t\trc_direct(&s->rc, &s->lzma.rep0, limit - ALIGN_BITS);\r\n\t\t\ts->lzma.rep0 <<= ALIGN_BITS;\r\n\t\t\trc_bittree_reverse(&s->rc, s->lzma.dist_align,\r\n\t\t\t\t\t&s->lzma.rep0, ALIGN_BITS);\r\n\t\t}\r\n\t}\r\n}\r\n\r\n/*\r\n * Decode a repeated match. The distance is one of the four most recently\r\n * seen matches. The distance will be stored in s->lzma.rep0.\r\n */\r\nstatic void lzma_rep_match(struct xz_dec_lzma2 *s, uint32_t pos_state)\r\n{\r\n\tuint32_t tmp;\r\n\r\n\tif (!rc_bit(&s->rc, &s->lzma.is_rep0[s->lzma.state])) {\r\n\t\tif (!rc_bit(&s->rc, &s->lzma.is_rep0_long[\r\n\t\t\t\ts->lzma.state][pos_state])) {\r\n\t\t\tlzma_state_short_rep(&s->lzma.state);\r\n\t\t\ts->lzma.len = 1;\r\n\t\t\treturn;\r\n\t\t}\r\n\t} else {\r\n\t\tif (!rc_bit(&s->rc, &s->lzma.is_rep1[s->lzma.state])) {\r\n\t\t\ttmp = s->lzma.rep1;\r\n\t\t} else {\r\n\t\t\tif (!rc_bit(&s->rc, &s->lzma.is_rep2[s->lzma.state])) {\r\n\t\t\t\ttmp = s->lzma.rep2;\r\n\t\t\t} else {\r\n\t\t\t\ttmp = s->lzma.rep3;\r\n\t\t\t\ts->lzma.rep3 = s->lzma.rep2;\r\n\t\t\t}\r\n\r\n\t\t\ts->lzma.rep2 = s->lzma.rep1;\r\n\t\t}\r\n\r\n\t\ts->lzma.rep1 = s->lzma.rep0;\r\n\t\ts->lzma.rep0 = tmp;\r\n\t}\r\n\r\n\tlzma_state_long_rep(&s->lzma.state);\r\n\tlzma_len(s, &s->lzma.rep_len_dec, pos_state);\r\n}\r\n\r\n/* LZMA decoder core */\r\nstatic bool lzma_main(struct xz_dec_lzma2 *s)\r\n{\r\n\tuint32_t pos_state;\r\n\r\n\t/*\r\n\t * If the dictionary was reached during the previous call, try to\r\n\t * finish the possibly pending repeat in the dictionary.\r\n\t */\r\n\tif (dict_has_space(&s->dict) && s->lzma.len > 0)\r\n\t\tdict_repeat(&s->dict, &s->lzma.len, s->lzma.rep0);\r\n\r\n\t/*\r\n\t * Decode more LZMA symbols. One iteration may consume up to\r\n\t * LZMA_IN_REQUIRED - 1 bytes.\r\n\t */\r\n\twhile (dict_has_space(&s->dict) && !rc_limit_exceeded(&s->rc)) {\r\n\t\tpos_state = s->dict.pos & s->lzma.pos_mask;\r\n\r\n\t\tif (!rc_bit(&s->rc, &s->lzma.is_match[\r\n\t\t\t\ts->lzma.state][pos_state])) {\r\n\t\t\tlzma_literal(s);\r\n\t\t} else {\r\n\t\t\tif (rc_bit(&s->rc, &s->lzma.is_rep[s->lzma.state]))\r\n\t\t\t\tlzma_rep_match(s, pos_state);\r\n\t\t\telse\r\n\t\t\t\tlzma_match(s, pos_state);\r\n\r\n\t\t\tif (!dict_repeat(&s->dict, &s->lzma.len, s->lzma.rep0))\r\n\t\t\t\treturn false;\r\n\t\t}\r\n\t}\r\n\r\n\t/*\r\n\t * Having the range decoder always normalized when we are outside\r\n\t * this function makes it easier to correctly handle end of the chunk.\r\n\t */\r\n\trc_normalize(&s->rc);\r\n\r\n\treturn true;\r\n}\r\n\r\n/*\r\n * Reset the LZMA decoder and range decoder state. Dictionary is nore reset\r\n * here, because LZMA state may be reset without resetting the dictionary.\r\n */\r\nstatic void lzma_reset(struct xz_dec_lzma2 *s)\r\n{\r\n\tuint16_t *probs;\r\n\tsize_t i;\r\n\r\n\ts->lzma.state = STATE_LIT_LIT;\r\n\ts->lzma.rep0 = 0;\r\n\ts->lzma.rep1 = 0;\r\n\ts->lzma.rep2 = 0;\r\n\ts->lzma.rep3 = 0;\r\n\r\n\t/*\r\n\t * All probabilities are initialized to the same value. This hack\r\n\t * makes the code smaller by avoiding a separate loop for each\r\n\t * probability array.\r\n\t *\r\n\t * This could be optimized so that only that part of literal\r\n\t * probabilities that are actually required. In the common case\r\n\t * we would write 12 KiB less.\r\n\t */\r\n\tprobs = s->lzma.is_match[0];\r\n\tfor (i = 0; i < PROBS_TOTAL; ++i)\r\n\t\tprobs[i] = RC_BIT_MODEL_TOTAL / 2;\r\n\r\n\trc_reset(&s->rc);\r\n}\r\n\r\n/*\r\n * Decode and validate LZMA properties (lc/lp/pb) and calculate the bit masks\r\n * from the decoded lp and pb values. On success, the LZMA decoder state is\r\n * reset and true is returned.\r\n */\r\nstatic bool lzma_props(struct xz_dec_lzma2 *s, uint8_t props)\r\n{\r\n\tif (props > (4 * 5 + 4) * 9 + 8)\r\n\t\treturn false;\r\n\r\n\ts->lzma.pos_mask = 0;\r\n\twhile (props >= 9 * 5) {\r\n\t\tprops -= 9 * 5;\r\n\t\t++s->lzma.pos_mask;\r\n\t}\r\n\r\n\ts->lzma.pos_mask = (1 << s->lzma.pos_mask) - 1;\r\n\r\n\ts->lzma.literal_pos_mask = 0;\r\n\twhile (props >= 9) {\r\n\t\tprops -= 9;\r\n\t\t++s->lzma.literal_pos_mask;\r\n\t}\r\n\r\n\ts->lzma.lc = props;\r\n\r\n\tif (s->lzma.lc + s->lzma.literal_pos_mask > 4)\r\n\t\treturn false;\r\n\r\n\ts->lzma.literal_pos_mask = (1 << s->lzma.literal_pos_mask) - 1;\r\n\r\n\tlzma_reset(s);\r\n\r\n\treturn true;\r\n}\r\n\r\n/*********\r\n * LZMA2 *\r\n *********/\r\n\r\n/*\r\n * The LZMA decoder assumes that if the input limit (s->rc.in_limit) hasn't\r\n * been exceeded, it is safe to read up to LZMA_IN_REQUIRED bytes. This\r\n * wrapper function takes care of making the LZMA decoder's assumption safe.\r\n *\r\n * As long as there is plenty of input left to be decoded in the current LZMA\r\n * chunk, we decode directly from the caller-supplied input buffer until\r\n * there's LZMA_IN_REQUIRED bytes left. Those remaining bytes are copied into\r\n * s->temp.buf, which (hopefully) gets filled on the next call to this\r\n * function. We decode a few bytes from the temporary buffer so that we can\r\n * continue decoding from the caller-supplied input buffer again.\r\n */\r\nstatic bool lzma2_lzma(struct xz_dec_lzma2 *s, struct xz_buf *b)\r\n{\r\n\tsize_t in_avail;\r\n\tuint32_t tmp;\r\n\r\n\tin_avail = b->in_size - b->in_pos;\r\n\tif (s->temp.size > 0 || s->lzma2.compressed == 0) {\r\n\t\ttmp = 2 * LZMA_IN_REQUIRED - s->temp.size;\r\n\t\tif (tmp > s->lzma2.compressed - s->temp.size)\r\n\t\t\ttmp = s->lzma2.compressed - s->temp.size;\r\n\t\tif (tmp > in_avail)\r\n\t\t\ttmp = in_avail;\r\n\r\n\t\tmemcpy(s->temp.buf + s->temp.size, b->in + b->in_pos, tmp);\r\n\r\n\t\tif (s->temp.size + tmp == s->lzma2.compressed) {\r\n\t\t\tmemzero(s->temp.buf + s->temp.size + tmp,\r\n\t\t\t\t\tsizeof(s->temp.buf)\r\n\t\t\t\t\t\t- s->temp.size - tmp);\r\n\t\t\ts->rc.in_limit = s->temp.size + tmp;\r\n\t\t} else if (s->temp.size + tmp < LZMA_IN_REQUIRED) {\r\n\t\t\ts->temp.size += tmp;\r\n\t\t\tb->in_pos += tmp;\r\n\t\t\treturn true;\r\n\t\t} else {\r\n\t\t\ts->rc.in_limit = s->temp.size + tmp - LZMA_IN_REQUIRED;\r\n\t\t}\r\n\r\n\t\ts->rc.in = s->temp.buf;\r\n\t\ts->rc.in_pos = 0;\r\n\r\n\t\tif (!lzma_main(s) || s->rc.in_pos > s->temp.size + tmp)\r\n\t\t\treturn false;\r\n\r\n\t\ts->lzma2.compressed -= s->rc.in_pos;\r\n\r\n\t\tif (s->rc.in_pos < s->temp.size) {\r\n\t\t\ts->temp.size -= s->rc.in_pos;\r\n\t\t\tmemmove(s->temp.buf, s->temp.buf + s->rc.in_pos,\r\n\t\t\t\t\ts->temp.size);\r\n\t\t\treturn true;\r\n\t\t}\r\n\r\n\t\tb->in_pos += s->rc.in_pos - s->temp.size;\r\n\t\ts->temp.size = 0;\r\n\t}\r\n\r\n\tin_avail = b->in_size - b->in_pos;\r\n\tif (in_avail >= LZMA_IN_REQUIRED) {\r\n\t\ts->rc.in = b->in;\r\n\t\ts->rc.in_pos = b->in_pos;\r\n\r\n\t\tif (in_avail >= s->lzma2.compressed + LZMA_IN_REQUIRED)\r\n\t\t\ts->rc.in_limit = b->in_pos + s->lzma2.compressed;\r\n\t\telse\r\n\t\t\ts->rc.in_limit = b->in_size - LZMA_IN_REQUIRED;\r\n\r\n\t\tif (!lzma_main(s))\r\n\t\t\treturn false;\r\n\r\n\t\tin_avail = s->rc.in_pos - b->in_pos;\r\n\t\tif (in_avail > s->lzma2.compressed)\r\n\t\t\treturn false;\r\n\r\n\t\ts->lzma2.compressed -= in_avail;\r\n\t\tb->in_pos = s->rc.in_pos;\r\n\t}\r\n\r\n\tin_avail = b->in_size - b->in_pos;\r\n\tif (in_avail < LZMA_IN_REQUIRED) {\r\n\t\tif (in_avail > s->lzma2.compressed)\r\n\t\t\tin_avail = s->lzma2.compressed;\r\n\r\n\t\tmemcpy(s->temp.buf, b->in + b->in_pos, in_avail);\r\n\t\ts->temp.size = in_avail;\r\n\t\tb->in_pos += in_avail;\r\n\t}\r\n\r\n\treturn true;\r\n}\r\n\r\n/*\r\n * Take care of the LZMA2 control layer, and forward the job of actual LZMA\r\n * decoding or copying of uncompressed chunks to other functions.\r\n */\r\nXZ_EXTERN enum xz_ret xz_dec_lzma2_run(struct xz_dec_lzma2 *s,\r\n\t\t\t\t       struct xz_buf *b)\r\n{\r\n\tuint32_t tmp;\r\n\r\n\twhile (b->in_pos < b->in_size || s->lzma2.sequence == SEQ_LZMA_RUN) {\r\n\t\tswitch (s->lzma2.sequence) {\r\n\t\tcase SEQ_CONTROL:\r\n\t\t\t/*\r\n\t\t\t * LZMA2 control byte\r\n\t\t\t *\r\n\t\t\t * Exact values:\r\n\t\t\t *   0x00   End marker\r\n\t\t\t *   0x01   Dictionary reset followed by\r\n\t\t\t *          an uncompressed chunk\r\n\t\t\t *   0x02   Uncompressed chunk (no dictionary reset)\r\n\t\t\t *\r\n\t\t\t * Highest three bits (s->control & 0xE0):\r\n\t\t\t *   0xE0   Dictionary reset, new properties and state\r\n\t\t\t *          reset, followed by LZMA compressed chunk\r\n\t\t\t *   0xC0   New properties and state reset, followed\r\n\t\t\t *          by LZMA compressed chunk (no dictionary\r\n\t\t\t *          reset)\r\n\t\t\t *   0xA0   State reset using old properties,\r\n\t\t\t *          followed by LZMA compressed chunk (no\r\n\t\t\t *          dictionary reset)\r\n\t\t\t *   0x80   LZMA chunk (no dictionary or state reset)\r\n\t\t\t *\r\n\t\t\t * For LZMA compressed chunks, the lowest five bits\r\n\t\t\t * (s->control & 1F) are the highest bits of the\r\n\t\t\t * uncompressed size (bits 16-20).\r\n\t\t\t *\r\n\t\t\t * A new LZMA2 stream must begin with a dictionary\r\n\t\t\t * reset. The first LZMA chunk must set new\r\n\t\t\t * properties and reset the LZMA state.\r\n\t\t\t *\r\n\t\t\t * Values that don't match anything described above\r\n\t\t\t * are invalid and we return XZ_DATA_ERROR.\r\n\t\t\t */\r\n\t\t\ttmp = b->in[b->in_pos++];\r\n\r\n\t\t\tif (tmp == 0x00)\r\n\t\t\t\treturn XZ_STREAM_END;\r\n\r\n\t\t\tif (tmp >= 0xE0 || tmp == 0x01) {\r\n\t\t\t\ts->lzma2.need_props = true;\r\n\t\t\t\ts->lzma2.need_dict_reset = false;\r\n\t\t\t\tdict_reset(&s->dict, b);\r\n\t\t\t} else if (s->lzma2.need_dict_reset) {\r\n\t\t\t\treturn XZ_DATA_ERROR;\r\n\t\t\t}\r\n\r\n\t\t\tif (tmp >= 0x80) {\r\n\t\t\t\ts->lzma2.uncompressed = (tmp & 0x1F) << 16;\r\n\t\t\t\ts->lzma2.sequence = SEQ_UNCOMPRESSED_1;\r\n\r\n\t\t\t\tif (tmp >= 0xC0) {\r\n\t\t\t\t\t/*\r\n\t\t\t\t\t * When there are new properties,\r\n\t\t\t\t\t * state reset is done at\r\n\t\t\t\t\t * SEQ_PROPERTIES.\r\n\t\t\t\t\t */\r\n\t\t\t\t\ts->lzma2.need_props = false;\r\n\t\t\t\t\ts->lzma2.next_sequence\r\n\t\t\t\t\t\t\t= SEQ_PROPERTIES;\r\n\r\n\t\t\t\t} else if (s->lzma2.need_props) {\r\n\t\t\t\t\treturn XZ_DATA_ERROR;\r\n\r\n\t\t\t\t} else {\r\n\t\t\t\t\ts->lzma2.next_sequence\r\n\t\t\t\t\t\t\t= SEQ_LZMA_PREPARE;\r\n\t\t\t\t\tif (tmp >= 0xA0)\r\n\t\t\t\t\t\tlzma_reset(s);\r\n\t\t\t\t}\r\n\t\t\t} else {\r\n\t\t\t\tif (tmp > 0x02)\r\n\t\t\t\t\treturn XZ_DATA_ERROR;\r\n\r\n\t\t\t\ts->lzma2.sequence = SEQ_COMPRESSED_0;\r\n\t\t\t\ts->lzma2.next_sequence = SEQ_COPY;\r\n\t\t\t}\r\n\r\n\t\t\tbreak;\r\n\r\n\t\tcase SEQ_UNCOMPRESSED_1:\r\n\t\t\ts->lzma2.uncompressed\r\n\t\t\t\t\t+= (uint32_t)b->in[b->in_pos++] << 8;\r\n\t\t\ts->lzma2.sequence = SEQ_UNCOMPRESSED_2;\r\n\t\t\tbreak;\r\n\r\n\t\tcase SEQ_UNCOMPRESSED_2:\r\n\t\t\ts->lzma2.uncompressed\r\n\t\t\t\t\t+= (uint32_t)b->in[b->in_pos++] + 1;\r\n\t\t\ts->lzma2.sequence = SEQ_COMPRESSED_0;\r\n\t\t\tbreak;\r\n\r\n\t\tcase SEQ_COMPRESSED_0:\r\n\t\t\ts->lzma2.compressed\r\n\t\t\t\t\t= (uint32_t)b->in[b->in_pos++] << 8;\r\n\t\t\ts->lzma2.sequence = SEQ_COMPRESSED_1;\r\n\t\t\tbreak;\r\n\r\n\t\tcase SEQ_COMPRESSED_1:\r\n\t\t\ts->lzma2.compressed\r\n\t\t\t\t\t+= (uint32_t)b->in[b->in_pos++] + 1;\r\n\t\t\ts->lzma2.sequence = s->lzma2.next_sequence;\r\n\t\t\tbreak;\r\n\r\n\t\tcase SEQ_PROPERTIES:\r\n\t\t\tif (!lzma_props(s, b->in[b->in_pos++]))\r\n\t\t\t\treturn XZ_DATA_ERROR;\r\n\r\n\t\t\ts->lzma2.sequence = SEQ_LZMA_PREPARE;\r\n\r\n\t\tcase SEQ_LZMA_PREPARE:\r\n\t\t\tif (s->lzma2.compressed < RC_INIT_BYTES)\r\n\t\t\t\treturn XZ_DATA_ERROR;\r\n\r\n\t\t\tif (!rc_read_init(&s->rc, b))\r\n\t\t\t\treturn XZ_OK;\r\n\r\n\t\t\ts->lzma2.compressed -= RC_INIT_BYTES;\r\n\t\t\ts->lzma2.sequence = SEQ_LZMA_RUN;\r\n\r\n\t\tcase SEQ_LZMA_RUN:\r\n\t\t\t/*\r\n\t\t\t * Set dictionary limit to indicate how much we want\r\n\t\t\t * to be encoded at maximum. Decode new data into the\r\n\t\t\t * dictionary. Flush the new data from dictionary to\r\n\t\t\t * b->out. Check if we finished decoding this chunk.\r\n\t\t\t * In case the dictionary got full but we didn't fill\r\n\t\t\t * the output buffer yet, we may run this loop\r\n\t\t\t * multiple times without changing s->lzma2.sequence.\r\n\t\t\t */\r\n\t\t\tdict_limit(&s->dict, min_t(size_t,\r\n\t\t\t\t\tb->out_size - b->out_pos,\r\n\t\t\t\t\ts->lzma2.uncompressed));\r\n\t\t\tif (!lzma2_lzma(s, b))\r\n\t\t\t\treturn XZ_DATA_ERROR;\r\n\r\n\t\t\ts->lzma2.uncompressed -= dict_flush(&s->dict, b);\r\n\r\n\t\t\tif (s->lzma2.uncompressed == 0) {\r\n\t\t\t\tif (s->lzma2.compressed > 0 || s->lzma.len > 0\r\n\t\t\t\t\t\t|| !rc_is_finished(&s->rc))\r\n\t\t\t\t\treturn XZ_DATA_ERROR;\r\n\r\n\t\t\t\trc_reset(&s->rc);\r\n\t\t\t\ts->lzma2.sequence = SEQ_CONTROL;\r\n\r\n\t\t\t} else if (b->out_pos == b->out_size\r\n\t\t\t\t\t|| (b->in_pos == b->in_size\r\n\t\t\t\t\t\t&& s->temp.size\r\n\t\t\t\t\t\t< s->lzma2.compressed)) {\r\n\t\t\t\treturn XZ_OK;\r\n\t\t\t}\r\n\r\n\t\t\tbreak;\r\n\r\n\t\tcase SEQ_COPY:\r\n\t\t\tdict_uncompressed(&s->dict, b, &s->lzma2.compressed);\r\n\t\t\tif (s->lzma2.compressed > 0)\r\n\t\t\t\treturn XZ_OK;\r\n\r\n\t\t\ts->lzma2.sequence = SEQ_CONTROL;\r\n\t\t\tbreak;\r\n\t\t}\r\n\t}\r\n\r\n\treturn XZ_OK;\r\n}\r\n\r\nXZ_EXTERN struct xz_dec_lzma2 *xz_dec_lzma2_create(enum xz_mode mode,\r\n\t\t\t\t\t\t   uint32_t dict_max)\r\n{\r\n\tstruct xz_dec_lzma2 *s = kmalloc(sizeof(*s), GFP_KERNEL);\r\n\tif (s == NULL)\r\n\t\treturn NULL;\r\n\r\n\ts->dict.mode = mode;\r\n\ts->dict.size_max = dict_max;\r\n\r\n\tif (DEC_IS_PREALLOC(mode)) {\r\n\t\ts->dict.buf = vmalloc(dict_max);\r\n\t\tif (s->dict.buf == NULL) {\r\n\t\t\tkfree(s);\r\n\t\t\treturn NULL;\r\n\t\t}\r\n\t} else if (DEC_IS_DYNALLOC(mode)) {\r\n\t\ts->dict.buf = NULL;\r\n\t\ts->dict.allocated = 0;\r\n\t}\r\n\r\n\treturn s;\r\n}\r\n\r\nXZ_EXTERN enum xz_ret xz_dec_lzma2_reset(struct xz_dec_lzma2 *s, uint8_t props)\r\n{\r\n\t/* This limits dictionary size to 3 GiB to keep parsing simpler. */\r\n\tif (props > 39)\r\n\t\treturn XZ_OPTIONS_ERROR;\r\n\r\n\ts->dict.size = 2 + (props & 1);\r\n\ts->dict.size <<= (props >> 1) + 11;\r\n\r\n\tif (DEC_IS_MULTI(s->dict.mode)) {\r\n\t\tif (s->dict.size > s->dict.size_max)\r\n\t\t\treturn XZ_MEMLIMIT_ERROR;\r\n\r\n\t\ts->dict.end = s->dict.size;\r\n\r\n\t\tif (DEC_IS_DYNALLOC(s->dict.mode)) {\r\n\t\t\tif (s->dict.allocated < s->dict.size) {\r\n\t\t\t\tvfree(s->dict.buf);\r\n\t\t\t\ts->dict.buf = vmalloc(s->dict.size);\r\n\t\t\t\tif (s->dict.buf == NULL) {\r\n\t\t\t\t\ts->dict.allocated = 0;\r\n\t\t\t\t\treturn XZ_MEM_ERROR;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\ts->lzma.len = 0;\r\n\r\n\ts->lzma2.sequence = SEQ_CONTROL;\r\n\ts->lzma2.need_dict_reset = true;\r\n\r\n\ts->temp.size = 0;\r\n\r\n\treturn XZ_OK;\r\n}\r\n\r\nXZ_EXTERN void xz_dec_lzma2_end(struct xz_dec_lzma2 *s)\r\n{\r\n\tif (DEC_IS_MULTI(s->dict.mode))\r\n\t\tvfree(s->dict.buf);\r\n\r\n\tkfree(s);\r\n}\r\n\r\n//END xz_dec_lzma2.c\r\n//BEGIN xz_crc32.c\r\n\r\n/*\r\n * CRC32 using the polynomial from IEEE-802.3\r\n *\r\n * Authors: Lasse Collin <lasse.collin@tukaani.org>\r\n *          Igor Pavlov <http://7-zip.org/>\r\n *\r\n * This file has been put into the public domain.\r\n * You can do whatever you want with this file.\r\n */\r\n\r\n/*\r\n * This is not the fastest implementation, but it is pretty compact.\r\n * The fastest versions of xz_crc32() on modern CPUs without hardware\r\n * accelerated CRC instruction are 3-5 times as fast as this version,\r\n * but they are bigger and use more memory for the lookup table.\r\n */\r\n\r\n/*\r\n * STATIC_RW_DATA is used in the pre-boot environment on some architectures.\r\n * See <linux/decompress/mm.h> for details.\r\n */\r\n#ifndef STATIC_RW_DATA\r\n#\tdefine STATIC_RW_DATA static\r\n#endif\r\n\r\nSTATIC_RW_DATA uint32_t xz_crc32_table[256];\r\n\r\nXZ_EXTERN void xz_crc32_init(void)\r\n{\r\n\tconst uint32_t poly = 0xEDB88320;\r\n\r\n\tuint32_t i;\r\n\tuint32_t j;\r\n\tuint32_t r;\r\n\r\n\tfor (i = 0; i < 256; ++i) {\r\n\t\tr = i;\r\n\t\tfor (j = 0; j < 8; ++j)\r\n\t\t\tr = (r >> 1) ^ (poly & ~((r & 1) - 1));\r\n\r\n\t\txz_crc32_table[i] = r;\r\n\t}\r\n\r\n\treturn;\r\n}\r\n\r\nXZ_EXTERN uint32_t xz_crc32(const uint8_t *buf, size_t size, uint32_t crc)\r\n{\r\n\tcrc = ~crc;\r\n\r\n\twhile (size != 0) {\r\n\t\tcrc = xz_crc32_table[*buf++ ^ (crc & 0xFF)] ^ (crc >> 8);\r\n\t\t--size;\r\n\t}\r\n\r\n\treturn ~crc;\r\n}\r\n//END xz_crc32.c\r\n#endif\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n//returns an unseekable write-only file that will decompress any data that is written to it.\r\n//decompressed data will be written to the output\r\ntypedef struct\r\n{\r\n\tvfsfile_t vf;\r\n\tvfsfile_t *outfile;\r\n\r\n\tqbyte out[65536];\r\n\tstruct xz_buf b;\r\n\tstruct xz_dec *s;\r\n} vf_xz_dec_t;\r\n\r\nstatic vfsfile_t *FS_XZ_Finish(vfsfile_t *f)\r\n{\r\n\tvf_xz_dec_t *n = (vf_xz_dec_t*)f;\r\n\tvfsfile_t *r = n->outfile;\r\n\tif (n->s)\r\n\t\txz_dec_end(n->s);\r\n\tZ_Free(n);\r\n\treturn r;\r\n}\r\nstatic qboolean QDECL FS_XZ_Dec_Close(vfsfile_t *f)\r\n{\r\n\tvfsfile_t *orig = FS_XZ_Finish(f);\r\n\tVFS_CLOSE(orig);\r\n\treturn true;\r\n}\r\nstatic int QDECL FS_XZ_Dec_Write(vfsfile_t *f, const void *buffer, int len)\r\n{\r\n\tenum xz_ret ret;\r\n\tvf_xz_dec_t *n = (vf_xz_dec_t*)f;\r\n\r\n\tn->b.in = buffer;\r\n\tn->b.in_size = len;\r\n\tn->b.in_pos = 0;\r\n\r\n\twhile(n->b.in_pos < n->b.in_size)\r\n\t{\r\n\t\tret = xz_dec_run(n->s, &n->b);\r\n\r\n\t\tif (n->b.out_pos == sizeof(n->out))\r\n\t\t{\r\n\t\t\tif (VFS_WRITE(n->outfile, n->out, n->b.out_pos) != n->b.out_pos)\r\n\t\t\t\treturn -1;\r\n\r\n\t\t\tn->b.out_pos = 0;\r\n\t\t}\r\n\r\n\t\tif (ret == XZ_OK)\r\n\t\t\tcontinue;\r\n\r\n#ifdef XZ_DEC_ANY_CHECK\r\n\t\tif (ret == XZ_UNSUPPORTED_CHECK)\r\n\t\t{\r\n\t\t\tCon_Printf(\"XZ: Unsupported check; not verifying \"\r\n\t\t\t\t\t\"file integrity\\n\");\r\n\t\t\tcontinue;\r\n\t\t}\r\n#endif\r\n\r\n\t\tif (VFS_WRITE(n->outfile, n->out, n->b.out_pos) != n->b.out_pos)\r\n\t\t\treturn -1;\r\n\t\tn->b.out_pos = 0;\r\n\r\n\t\tif (ret == XZ_STREAM_END)\r\n\t\t\tcontinue;\r\n\r\n\r\n\t\tswitch (ret)\r\n\t\t{\r\n\t\tcase XZ_STREAM_END:\r\n\t\t\txz_dec_end(n->s);\r\n\t\t\tn->s = NULL;\r\n\t\t\tbreak;\r\n\r\n\t\tcase XZ_MEM_ERROR:\r\n\t\t\tCon_Printf(\"XZ: Memory allocation failed\\n\");\r\n\t\t\tbreak;\r\n\r\n\t\tcase XZ_MEMLIMIT_ERROR:\r\n\t\t\tCon_Printf(\"XZ: Memory usage limit reached\\n\");\r\n\t\t\tbreak;\r\n\r\n\t\tcase XZ_FORMAT_ERROR:\r\n\t\t\tCon_Printf(\"Not a .xz file\\n\");\r\n\t\t\tbreak;\r\n\r\n\t\tcase XZ_OPTIONS_ERROR:\r\n\t\t\tCon_Printf(\"Unsupported options in the .xz headers\\n\");\r\n\t\t\tbreak;\r\n\r\n\t\tcase XZ_DATA_ERROR:\r\n\t\tcase XZ_BUF_ERROR:\r\n\t\t\tCon_Printf(\"XZ: File is corrupt\\n\");\r\n\t\t\tbreak;\r\n\r\n\t\tdefault:\r\n\t\t\tCon_Printf(\"Bug!\\n\");\r\n\t\t\tbreak;\r\n\t\t}\r\n\t\treturn -1;\r\n\t}\r\n\r\n\treturn n->b.in_pos;\r\n}\r\n\r\n//return a write-only wrapper around another file. .xz data written to the wrapper will be seen as decompressed for the wrapee.\r\nvfsfile_t *FS_XZ_DecompressWriteFilter(vfsfile_t *outfile)\r\n{\r\n\tvf_xz_dec_t *n = Z_Malloc(sizeof(*n));\r\n\txz_crc32_init();\r\n#ifdef XZ_USE_CRC64\r\n\txz_crc64_init();\r\n#endif\r\n\r\n\tn->outfile = outfile;\r\n\r\n\tn->s = xz_dec_init(XZ_DYNALLOC, 1 << 27);\r\n\tif (!n->s)\r\n\t{\r\n\t\tZ_Free(n);\r\n\t\treturn NULL;\r\n\t}\r\n\r\n\tn->b.in\t\t\t= NULL;\r\n\tn->b.in_pos\t\t= 0;\r\n\tn->b.in_size\t= 0;\r\n\tn->b.out\t\t= n->out;\r\n\tn->b.out_pos\t= 0;\r\n\tn->b.out_size\t= sizeof(n->out);\r\n\tn->vf.Flush\t\t\t\t= NULL;\r\n\tn->vf.GetLen\t\t\t= NULL;\r\n\tn->vf.ReadBytes\t\t\t= NULL;\r\n\tn->vf.Seek\t\t\t\t= NULL;\r\n\tn->vf.Tell\t\t\t\t= NULL;\r\n\tn->vf.Close\t\t\t\t= FS_XZ_Dec_Close;\r\n\tn->vf.WriteBytes\t\t= FS_XZ_Dec_Write;\r\n\tn->vf.seekstyle\t\t\t= SS_UNSEEKABLE;\r\n\r\n\treturn &n->vf;\r\n}\r\n\r\n//returns a read-only filter around a compressed .xz file\r\nvfsfile_t *FS_XZ_DecompressReadFilter(vfsfile_t *srcfile)\r\n{\r\n\tchar block[65536];\r\n\tint blocksize = VFS_READ(srcfile, block, HEADER_MAGIC_SIZE);\r\n\r\n\tif (blocksize == HEADER_MAGIC_SIZE && !memcmp(block, HEADER_MAGIC, HEADER_MAGIC_SIZE))\r\n\t{\t//okay, looks like an xz.\r\n\t\tvfsfile_t *xzpipe = FS_OpenTemp();\r\n\t\tif (!xzpipe)\r\n\t\t\txzpipe = VFSPIPE_Open(1, true);\r\n\t\txzpipe = FS_XZ_DecompressWriteFilter(xzpipe);\r\n\t\tfor (;blocksize;)\r\n\t\t{\r\n\t\t\tif (blocksize < 0 || blocksize != VFS_WRITE(xzpipe, block, blocksize))\r\n\t\t\t{\r\n\t\t\t\tVFS_CLOSE(xzpipe);\r\n\t\t\t\txzpipe = NULL;\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\t\t\tblocksize = VFS_READ(srcfile, block, sizeof(block));\r\n\t\t}\r\n\t\tVFS_CLOSE(srcfile);\r\n\r\n\t\tif (xzpipe)\r\n\t\t\txzpipe = FS_XZ_Finish(xzpipe);\r\n\t\tVFS_SEEK(xzpipe, 0);\r\n\t\treturn xzpipe;\r\n\t}\r\n\tVFS_SEEK(srcfile, 0);\r\n\treturn srcfile;\r\n}\r\n#endif\r\n\r\n"
  },
  {
    "path": "engine/common/fs_zip.c",
    "content": "#include \"quakedef.h\"\n#include \"fs.h\"\n\n//#define AVAIL_BZLIB\n//#define DYNAMIC_BZLIB\n\n\n\n//supported ZIP features:\n//zip64 for huge zips\n//utf-8 encoding support (non-utf-8 is always ibm437)\n//symlink flag\n//compression mode: store\n//compression mode: deflate (via zlib)\n//compression mode: deflate64 (via zlib's unofficial extras)\n//compression mode: bzip2 (via libbz2)\n//bigendian cpus. everything misaligned.\n//weak/original file encryption (set fs_zip_password)\n//split archives (see specific notes)\n\n//NOT supported:\n//other compression modes\n//strong/aes encryption\n//if central dir is crypted/compressed, the archive will fail to open\n//crc verification (if present then the local header must match the central dir, but the file data itself will not be verified in part because that'd require decoding the entire file upfront first)\n//infozip utf-8 name override.\n//other 'extra' fields.\n\n//split archives:\n//the central directory must be stored exclusively inside the initial zip/pk3 file.\n//additional volumes will use the first letter of the extension and then at least two base-10 digits denoting the volume index (eg pak0.pk3 + pak0.p00 + pak0.p01).\n//individual files must not be split over files (no rollover on truncation)\n//fteqcc has a feature to generate versioned centraldir-only zips using external volumes for the actual data. This can be used to avoid downloading redundant data when connecting to servers using older revisions, but such revisions must be centrally managed.\n\n\n\n#ifdef AVAIL_BZLIB\n#\tinclude <bzlib.h>\n#\tifdef DYNAMIC_BZLIB\n#\tifndef WINAPI\n#\t\tdefine WINAPI\n#\tendif\n#\tdefine BZ2_bzDecompressInit pBZ2_bzDecompressInit\n#\tdefine BZ2_bzDecompress pBZ2_bzDecompress\n#\tdefine BZ2_bzDecompressEnd pBZ2_bzDecompressEnd\n\tstatic int (WINAPI *BZ2_bzDecompressInit)(bz_stream *strm, int verbosity, int small);\n\tstatic int (WINAPI *BZ2_bzDecompress)(bz_stream* strm);\n\tstatic int (WINAPI *BZ2_bzDecompressEnd)(bz_stream *strm);\n\tstatic qboolean BZLIB_LOADED(void)\n\t{\n\t\tstatic qboolean tried;\n\t\tstatic void *handle;\n\t\tif (!tried)\n\t\t{\n\t\t\tstatic dllfunction_t funcs[] =\n\t\t\t{\n\t\t\t\t{(void*)&BZ2_bzDecompressInit,\t\"BZ2_bzDecompressInit\"},\n\t\t\t\t{(void*)&BZ2_bzDecompress,\t\t\"BZ2_bzDecompress\"},\n\t\t\t\t{(void*)&BZ2_bzDecompressEnd,\t\"BZ2_bzDecompressEnd\"},\n\t\t\t\t{NULL, NULL}\n\t\t\t};\n\t\t\ttried = true;\n\t\t\thandle = Sys_LoadLibrary(\"libbz2\", funcs);\n\t\t}\n\t\treturn handle != NULL;\n\t}\n#\telse\n#\t\tdefine BZLIB_LOADED() 1\n#\tendif\n#endif\n\n#ifdef AVAIL_ZLIB\n# define ZIPCRYPT\n\n# ifndef ZEXPORT\n\t#define ZEXPORT VARGS\n# endif\n# include <zlib.h>\n\n# ifdef DYNAMIC_ZLIB\n#  define ZLIB_LOADED() (zlib_handle!=NULL)\nvoid *zlib_handle;\n#  define ZSTATIC(n)\n# else\n#  define ZLIB_LOADED() 1\n#  define ZSTATIC(n) = &n\n\n# endif\n\n#ifndef Z_U4\n#define z_crc_t uLongf\n#endif\n\n//#pragma comment(lib, MSVCLIBSPATH \"zlib.lib\")\n\n#ifdef DYNAMIC_ZLIB\n\tstatic int (ZEXPORT *qinflateEnd) (z_streamp strm) ZSTATIC(inflateEnd);\n\tstatic int (ZEXPORT *qinflate) (z_streamp strm, int flush) ZSTATIC(inflate);\n\tstatic int (ZEXPORT *qinflateInit2_) (z_streamp strm, int  windowBits,\n\t\t\t\t\t\t\t\t\t\t  const char *version, int stream_size) ZSTATIC(inflateInit2_);\n\t//static z_crc_t (ZEXPORT *qcrc32)   (uLong crc, const Bytef *buf, uInt len) ZSTATIC(crc32);\n\t#ifdef ZIPCRYPT\n\tstatic const z_crc_t *(ZEXPORT *qget_crc_table)   (void) ZSTATIC(get_crc_table);\n\t#endif\n#else\n\t#define qinflateEnd\t\tinflateEnd\n\t#define qinflate\t\tinflate\n\t#define qinflateInit2_\tinflateInit2_\n\t#define qdeflateEnd\t\tdeflateEnd\n\t#define qdeflate\t\tdeflate\n\t#define qdeflateInit2_\tdeflateInit2_\n\t#define qget_crc_table\tget_crc_table\n#endif\n\n#define qinflateInit2(strm, windowBits) \\\n        qinflateInit2_((strm), (windowBits), ZLIB_VERSION, sizeof(z_stream))\n#define qdeflateInit2(strm, level, method, windowBits, memLevel, strategy) \\\n        qdeflateInit2_((strm),(level),(method),(windowBits),(memLevel),\\\n                      (strategy),           ZLIB_VERSION, sizeof(z_stream))\n\nqboolean LibZ_Init(void)\n{\n\t#ifdef DYNAMIC_ZLIB\n\tstatic dllfunction_t funcs[] =\n\t{\n\t\t{(void*)&qinflateEnd,\t\t\"inflateEnd\"},\n\t\t{(void*)&qinflate,\t\t\t\"inflate\"},\n\t\t{(void*)&qinflateInit2_,\t\"inflateInit2_\"},\n\t\t{(void*)&qdeflateEnd,\t\t\"deflateEnd\"},\n\t\t{(void*)&qdeflate,\t\t\t\"deflate\"},\n\t\t{(void*)&qdeflateInit2_,\t\"deflateInit2_\"},\n//\t\t{(void*)&qcrc32,\t\t\t\"crc32\"},\n#ifdef ZIPCRYPT\n\t\t{(void*)&qget_crc_table,\t\"get_crc_table\"},\n#endif\n\t\t{NULL, NULL}\n\t};\n\tif (!ZLIB_LOADED())\n\t\tzlib_handle = Sys_LoadLibrary(\"zlib1\", funcs);\n\t#endif\n\treturn ZLIB_LOADED();\n}\n\n#ifdef AVAIL_GZDEC\ntypedef struct {\n\tunsigned char ident1;\n\tunsigned char ident2;\n\tunsigned char cm;\n\tunsigned char flags;\n\tunsigned int mtime;\n\tunsigned char xflags;\n\tunsigned char os;\n\t//unsigned short xlen;\n\t//unsigned char xdata[xlen];\n\t//unsigned char fname[];\n\t//unsigned char fcomment[];\n\t//unsigned short fhcrc;\n\t//unsigned char compresseddata[];\n\t//unsigned int crc32;\n\t//unsigned int isize;\n} gzheader_t;\n#define sizeofgzheader_t 10\n\n#define\tGZ_FTEXT\t1\n#define\tGZ_FHCRC\t2\n#define GZ_FEXTRA\t4\n#define GZ_FNAME\t8\n#define GZ_FCOMMENT\t16\n#define GZ_RESERVED (32|64|128)\n//outfile may be null\nvfsfile_t *FS_DecompressGZip(vfsfile_t *infile, vfsfile_t *outfile)\n{\n\tchar inchar;\n\tunsigned short inshort;\n\tvfsfile_t *temp;\n\tgzheader_t header;\n\n\tif (VFS_READ(infile, &header, sizeofgzheader_t) == sizeofgzheader_t)\n\t{\n\t\tif (header.ident1 != 0x1f || header.ident2 != 0x8b || header.cm != 8 || header.flags & GZ_RESERVED)\n\t\t{\n\t\t\tVFS_SEEK(infile, 0);\n\t\t\treturn infile;\n\t\t}\n\t}\n\telse\n\t{\n\t\tVFS_SEEK(infile, 0);\n\t\treturn infile;\n\t}\n\tif (header.flags & GZ_FEXTRA)\n\t{\n\t\tVFS_READ(infile, &inshort, sizeof(inshort));\n\t\tinshort = LittleShort(inshort);\n\t\tVFS_SEEK(infile, VFS_TELL(infile) + inshort);\n\t}\n\n\tif (header.flags & GZ_FNAME)\n\t{\n\t\tCon_Printf(\"gzipped file name: \");\n\t\tdo {\n\t\t\tif (VFS_READ(infile, &inchar, sizeof(inchar)) != 1)\n\t\t\t\tbreak;\n\t\t\tCon_Printf(\"%c\", inchar);\n\t\t} while(inchar);\n\t\tCon_Printf(\"\\n\");\n\t}\n\n\tif (header.flags & GZ_FCOMMENT)\n\t{\n\t\tCon_Printf(\"gzipped file comment: \");\n\t\tdo {\n\t\t\tif (VFS_READ(infile, &inchar, sizeof(inchar)) != 1)\n\t\t\t\tbreak;\n\t\t\tCon_Printf(\"%c\", inchar);\n\t\t} while(inchar);\n\t\tCon_Printf(\"\\n\");\n\t}\n\n\tif (header.flags & GZ_FHCRC)\n\t{\n\t\tVFS_READ(infile, &inshort, sizeof(inshort));\n\t}\n\n\n\n\tif (outfile)\n\t\ttemp = outfile;\n\telse\n\t{\n\t\ttemp = FS_OpenTemp();\n\t\tif (!temp)\n\t\t{\n\t\t\tVFS_SEEK(infile, 0);\t//doh\n\t\t\treturn infile;\n\t\t}\n\t}\n\n\n\t{\n\t\tunsigned char inbuffer[16384];\n\t\tunsigned char outbuffer[16384];\n\t\tint ret;\n\n\t\tz_stream strm = {\n\t\t\tinbuffer,\n\t\t\t0,\n\t\t\t0,\n\n\t\t\toutbuffer,\n\t\t\tsizeof(outbuffer),\n\t\t\t0,\n\n\t\t\tNULL,\n\t\t\tNULL,\n\n\t\t\tNULL,\n\t\t\tNULL,\n\t\t\tNULL,\n\n\t\t\tZ_UNKNOWN,\n\t\t\t0,\n\t\t\t0\n\t\t};\n\n\t\tstrm.avail_in = VFS_READ(infile, inbuffer, sizeof(inbuffer));\n\t\tstrm.next_in = inbuffer;\n\n\t\tqinflateInit2(&strm, -MAX_WBITS);\n\n\t\twhile ((ret=qinflate(&strm, Z_SYNC_FLUSH)) != Z_STREAM_END)\n\t\t{\n\t\t\tif (strm.avail_in == 0 || strm.avail_out == 0)\n\t\t\t{\n\t\t\t\tif (strm.avail_in == 0)\n\t\t\t\t{\n\t\t\t\t\tstrm.avail_in = VFS_READ(infile, inbuffer, sizeof(inbuffer));\n\t\t\t\t\tstrm.next_in = inbuffer;\n\t\t\t\t\tif (!strm.avail_in)\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tif (strm.avail_out == 0)\n\t\t\t\t{\n\t\t\t\t\tstrm.next_out = outbuffer;\n\t\t\t\t\tVFS_WRITE(temp, outbuffer, strm.total_out);\n\t\t\t\t\tstrm.total_out = 0;\n\t\t\t\t\tstrm.avail_out = sizeof(outbuffer);\n\t\t\t\t}\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t//doh, it terminated for no reason\n\t\t\tif (ret != Z_STREAM_END)\n\t\t\t{\n\t\t\t\tqinflateEnd(&strm);\n\t\t\t\tCon_Printf(\"Couldn't decompress gz file\\n\");\n\t\t\t\tVFS_CLOSE(temp);\n\t\t\t\tVFS_CLOSE(infile);\n\t\t\t\treturn NULL;\n\t\t\t}\n\t\t}\n\t\t//we got to the end\n\t\tVFS_WRITE(temp, outbuffer, strm.total_out);\n\n\t\tqinflateEnd(&strm);\n\n\t\tif (temp->Seek)\n\t\t\tVFS_SEEK(temp, 0);\n\t}\n\tVFS_CLOSE(infile);\n\n\treturn temp;\n}\n\n\nsize_t ZLib_CompressBuffer(const qbyte *in, size_t insize, qbyte *out, size_t maxoutsize)\n{\t//compresses... returns 0 if the data would have grown.\n\tz_stream strm = {\n\t\t(qbyte*)in,\n\t\tinsize,\n\t\t0,\n\n\t\tout,\n\t\tmaxoutsize,\n\t\t0,\n\n\t\tNULL,\n\t\tNULL,\n\n\t\tNULL,\n\t\tNULL,\n\t\tNULL,\n\n\t\tZ_UNKNOWN,\n\t\t0,\n\t\t0\n\t};\n\n\tqdeflateInit2(&strm, Z_BEST_COMPRESSION, Z_DEFLATED, MAX_WBITS, MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY);\n\tif (qdeflate(&strm, Z_FINISH) != Z_STREAM_END)\n\t\tstrm.total_out = 0; //some sort of failure. probably needs more output buffer\n\tqdeflateEnd(&strm);\n\n\treturn strm.total_out;\n}\nsize_t ZLib_DecompressBuffer(const qbyte *in, size_t insize, qbyte *out, size_t maxoutsize)\n{\n\tint ret;\n\n\tz_stream strm = {\n\t\t(qbyte*)in,\n\t\tinsize,\n\t\t0,\n\n\t\tout,\n\t\tmaxoutsize,\n\t\t0,\n\n\t\tNULL,\n\t\tNULL,\n\n\t\tNULL,\n\t\tNULL,\n\t\tNULL,\n\n\t\tZ_UNKNOWN,\n\t\t0,\n\t\t0\n\t};\n\n\tqinflateInit2(&strm, MAX_WBITS);\n\n\twhile ((ret=qinflate(&strm, Z_SYNC_FLUSH)) != Z_STREAM_END)\n\t{\n\t\tif (strm.avail_in == 0 || strm.avail_out == 0)\n\t\t{\n\t\t\tif (strm.avail_in == 0)\n\t\t\t\tbreak;\t//reached the end of the input\n\t\t\t//output not big enough, but max size is fixed.\n\t\t\tbreak;\n\t\t}\n\n\t\t//doh, it terminated for no reason\n\t\tif (ret != Z_STREAM_END)\n\t\t\tbreak;\n\t}\n\tqinflateEnd(&strm);\n\n\treturn strm.total_out;\n}\n\n\n\n\n\n\n\n\n//returns an unseekable write-only file that will decompress any data that is written to it.\n//decompressed data will be written to the output\ntypedef struct\n{\n\tvfsfile_t vf;\n\tvfsfile_t *outfile;\n\tqboolean autoclosefile;\n\tqboolean compress;\n\n\t//gzip header handling\n\tqboolean headerparsed;\n\tqbyte in[65536];\n\tint inlen;\n\n\tqbyte out[65536];\n\tz_stream strm;\n} vf_gz_dec_t;\n\nstatic qboolean QDECL FS_GZ_Dec_Close(vfsfile_t *f)\n{\n\tvf_gz_dec_t *n = (vf_gz_dec_t*)f;\n\n\tif (n->compress)\n\t\tqdeflate(&n->strm, Z_FINISH);\n\telse\n\t\tqinflate(&n->strm, Z_FINISH);\n\tif (n->strm.next_out != n->out)\n\t\tVFS_WRITE(n->outfile, n->out, n->strm.next_out-n->out);\n\n\tif (n->compress)\n\t\tqdeflateEnd(&n->strm);\n\telse\n\t\tqinflateEnd(&n->strm);\n\n\tif (n->autoclosefile)\n\t\tVFS_CLOSE(n->outfile);\n\tZ_Free(n);\n\treturn true;\n}\nstatic int QDECL FS_GZ_Dec_Write(vfsfile_t *f, const void *buffer, int len)\n{\n\tint ret;\n\tvf_gz_dec_t *n = (vf_gz_dec_t*)f;\n\n\tif (n->headerparsed != 1)\n\t{\n\t\tqofs_t ofs = 0;\n\t\tgzheader_t *header;\n\t\tint chunk;\n\t\tif (len > sizeof(n->in) - n->inlen)\n\t\t\tchunk = sizeof(n->in) - n->inlen;\n\t\telse\n\t\t\tchunk = len;\n\t\tmemmove(n->in, buffer, chunk);\n\t\tn->inlen += chunk;\n\n\t\tif (n->headerparsed == 2)\n\t\t{\n\t\t\tif (n->inlen >= 8)\n\t\t\t{\n\t\t\t\t//unsigned int crc = (n->in[0]<<0) | (n->in[1]<<8) | (n->in[2]<<16) | (n->in[3]<<24);\n\t\t\t\tunsigned int isize = (n->in[4]<<0) | (n->in[5]<<8) | (n->in[6]<<16) | (n->in[7]<<24);\n\t\t\t\tif (n->strm.total_out != isize)\n\t\t\t\t\treturn -1;\t//the file we just received decoded to a different length (yay, concat...).\n\t\t\t\t//FIXME: validate the decoded crc\n\t\t\t\tn->headerparsed = false;\n\t\t\t\tn->strm.total_in = 0;\n\t\t\t\tn->strm.total_out = 0;\n\t\t\t\tlen = n->inlen - 8;\n\t\t\t\tn->inlen = 0;\n\t\t\t\tif (FS_GZ_Dec_Write(f, n->in + 8, len) != len)\n\t\t\t\t\treturn -1;\t//FIXME: make non-fatal somehow\n\t\t\t\treturn chunk;\n\t\t\t}\n\t\t\treturn chunk;\n\t\t}\n\n\t\theader = (gzheader_t*)n->in;\n\t\tofs += sizeofgzheader_t;  if (ofs > n->inlen) goto noheader;\n\n\t\tif (header->ident1 != 0x1f || header->ident2 != 0x8b || header->cm != 8 || header->flags & GZ_RESERVED)\n\t\t\treturn -1;\t//ERROR\n\n\t\tif (header->flags & GZ_FEXTRA)\n\t\t{\n\t\t\tunsigned short ex;\n\t\t\tif (ofs+2 > n->inlen) goto noheader;\n\t\t\tex = (n->in[ofs]<<0) | (n->in[ofs+1]<<8);\t//read little endian\n\t\t\tofs += 2+ex;  if (ofs > n->inlen) goto noheader;\n\t\t}\n\n\t\tif (header->flags & GZ_FNAME)\n\t\t{\n\t\t\tchar ch;\n//\t\t\tCon_Printf(\"gzipped file name: \");\n\t\t\tdo {\n\t\t\t\tif (ofs+1 > n->inlen) goto noheader;\n\t\t\t\tch = n->in[ofs++];\n//\t\t\t\tCon_Printf(\"%c\", ch);\n\t\t\t} while(ch);\n//\t\t\tCon_Printf(\"\\n\");\n\t\t}\n\n\t\tif (header->flags & GZ_FCOMMENT)\n\t\t{\n\t\t\tchar ch;\n//\t\t\tCon_Printf(\"gzipped file comment: \");\n\t\t\tdo {\n\t\t\t\tif (ofs+1 > n->inlen) goto noheader;\n\t\t\t\tch = n->in[ofs++];\n//\t\t\t\tCon_Printf(\"%c\", ch);\n\t\t\t} while(ch);\n//\t\t\tCon_Printf(\"\\n\");\n\t\t}\n\n\t\tif (header->flags & GZ_FHCRC)\n\t\t{\n\t\t\tofs += 2;  if (ofs > n->inlen) goto noheader;\n\t\t}\n\n\t\t//if we got here then the header is now complete.\n\t\t//tail-recurse it.\n\t\tn->headerparsed = true;\n\t\tchunk = n->inlen - ofs;\n\t\tif (FS_GZ_Dec_Write(f, n->in + ofs, chunk) != chunk)\n\t\t\treturn -1;\t//FIXME: make non-fatal somehow\n\t\treturn len;\n\nnoheader:\n\t\tif (n->inlen == sizeof(n->in))\n\t\t\treturn -1;\t//we filled our buffer. if we didn't get past the header yet then someone fucked up.\n\t\treturn len;\n\t}\n\n\n\tn->strm.next_in = (char*)buffer;\n\tn->strm.avail_in = len;\n\n\twhile(n->strm.avail_in)\n\t{\n\t\tif (n->compress)\n\t\t\tret = qdeflate(&n->strm, Z_SYNC_FLUSH);\n\t\telse\n\t\t\tret = qinflate(&n->strm, Z_SYNC_FLUSH);\n\n\t\tif (!n->strm.avail_out)\n\t\t{\n\t\t\tif (VFS_WRITE(n->outfile, n->out, n->strm.next_out-n->out) != n->strm.next_out-n->out)\n\t\t\t\treturn -1;\n\n\t\t\tn->strm.next_out = n->out;\n\t\t\tn->strm.avail_out = sizeof(n->out);\n\t\t}\n\n\t\tif (ret == Z_OK)\n\t\t\tcontinue;\n\n\t\t//flush it\n\t\tif (VFS_WRITE(n->outfile, n->out, n->strm.next_out-n->out) != n->strm.next_out-n->out)\n\t\t\treturn -1;\n\t\tn->strm.next_out = n->out;\n\t\tn->strm.avail_out = sizeof(n->out);\n\n\t\tif (ret == Z_STREAM_END)\t//allow concat\n\t\t{\n\t\t\tint l = n->strm.avail_in;\n\t\t\tn->headerparsed = 2;\n\t\t\tn->inlen = 0;\n\t\t\tif (l != FS_GZ_Dec_Write(f, n->strm.next_in, n->strm.avail_in))\n\t\t\t\treturn -1;\n\t\t\treturn len;\n\t\t}\n\n\t\tswitch (ret)\n\t\t{\n\t\tcase Z_NEED_DICT:\n\t\t\tCon_Printf(\"Z_NEED_DICT\\n\");\n\t\t\tbreak;\n\n\t\tcase Z_ERRNO:\n\t\t\tCon_Printf(\"Z_ERRNO\\n\");\n\t\t\tbreak;\n\n\t\tcase Z_STREAM_ERROR:\n\t\tcase Z_DATA_ERROR:\n\t\tcase Z_MEM_ERROR:\n\t\tcase Z_BUF_ERROR:\n\t\tcase Z_VERSION_ERROR:\n\t\t\tCon_Printf(\"File is corrupt\\n\");\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tCon_Printf(\"Bug!\\n\");\n\t\t\tbreak;\n\t\t}\n\t\treturn -1;\n\t}\n\n\treturn len;\n}\n\nvfsfile_t *FS_GZ_WriteFilter(vfsfile_t *outfile, qboolean autoclosefile, qboolean compress)\n{\n\tvf_gz_dec_t *n = Z_Malloc(sizeof(*n));\n\n\tn->outfile = outfile;\n\tn->autoclosefile = autoclosefile;\n\tn->compress = compress;\n\n\tn->strm.next_in\t\t= NULL;\n\tn->strm.avail_in \t= 0;\n\tn->strm.total_in\t= 0;\n\tn->strm.next_out\t= n->out;\n\tn->strm.avail_out \t= sizeof(n->out);\n\tn->strm.total_out\t= 0;\n\n\tn->vf.Flush\t\t\t\t= NULL;\n\tn->vf.GetLen\t\t\t= NULL;\n\tn->vf.ReadBytes\t\t\t= NULL;\n\tn->vf.Seek\t\t\t\t= NULL;\n\tn->vf.Tell\t\t\t\t= NULL;\n\tn->vf.Close\t\t\t\t= FS_GZ_Dec_Close;\n\tn->vf.WriteBytes\t\t= FS_GZ_Dec_Write;\n\tn->vf.seekstyle\t\t\t= SS_UNSEEKABLE;\n\n\tif (n->compress)\n\t{\n\t\tn->headerparsed = true;\t//deflate will write out a minimal header for us, so we can just splurge everything without rewinding.\n\t\tqdeflateInit2(&n->strm, Z_BEST_COMPRESSION, Z_DEFLATED, MAX_WBITS|16, MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY);\n\t}\n\telse\n\t\tqinflateInit2(&n->strm, -MAX_WBITS);\n\n\treturn &n->vf;\n}\n\n#endif\n#endif\n\n\n\n\n\n#ifdef PACKAGE_PK3\n\ntypedef struct\n{\n\tfsbucket_t bucket;\n\tchar\tname[MAX_QPATH];\n\tunsigned int disknum;\n\tqofs_t\tlocalpos;\t//location of local header\n\tqofs_t\tfilelen;\t//uncompressed size\n\ttime_t\tmtime;\n\tunsigned int\t\tcrc;\n\tunsigned int\t\tflags;\n} zpackfile_t;\n#define ZFL_DEFLATED\t(1u<<0)\t//need to use zlib\n#define ZFL_BZIP2\t\t(1u<<1)\t//bzip2 compression\n#define ZFL_STORED\t\t(1u<<2)\t//direct access is okay\n#define ZFL_SYMLINK\t\t(1u<<3)\t//file is a symlink\n#define ZFL_CORRUPT\t\t(1u<<4)\t//file is corrupt or otherwise unreadable (usually just means we don't support reading it rather than actually corrupt, but hey).\n#define ZFL_WEAKENCRYPT\t(1u<<5)\t//traditional zip encryption\n#define ZFL_DEFLATE64D\t(1u<<6)\t//need to use zlib's 'inflateBack9' stuff.\n\n#define ZFL_COMPRESSIONTYPE (ZFL_STORED|ZFL_CORRUPT|ZFL_DEFLATED|ZFL_DEFLATE64D|ZFL_BZIP2)\n\n\ntypedef struct zipfile_s\n{\n\tsearchpathfuncs_t pub;\n\n\tchar\t\t\tfilename[MAX_OSPATH];\n\tunsigned int\tnumfiles;\n\tzpackfile_t\t\t*files;\n\n\t//spanned zips are weird.\n\t//the starting zip is usually the last one, but should be null to simplify ref tracking.\n\tunsigned int\tthisdisk;\n\tunsigned int\tnumspans;\n\tstruct zipfile_s **spans;\n\n\t//info about the underlying file\n\tvoid\t\t\t*mutex;\n\tqofs_t\t\t\tcurpos;\t//cache position to avoid excess seeks\n\tqofs_t\t\t\trawsize;\n\tvfsfile_t\t\t*raw;\n\n\tqatomic32_t\t\treferences;\t//number of files open inside, so things don't crash if is closed in the wrong order.\n} zipfile_t;\n\n\nstatic void QDECL FSZIP_GetPathDetails(searchpathfuncs_t *handle, char *out, size_t outlen)\n{\n\tzipfile_t *zip = (void*)handle;\n\n\tif (zip->references != 1)\n\t\tQ_snprintfz(out, outlen, \"(%i)\", zip->references-1);\n\telse\n\t\t*out = '\\0';\n}\nstatic void QDECL FSZIP_UnclosePath(searchpathfuncs_t *handle)\n{\n\tzipfile_t *zip = (void*)handle;\n\tFTE_Atomic32_Inc(&zip->references);\n}\nstatic void QDECL FSZIP_ClosePath(searchpathfuncs_t *handle)\n{\n\tsize_t s;\n\tqboolean stillopen;\n\tzipfile_t *zip = (void*)handle;\n\n\tstillopen = FTE_Atomic32_Dec(&zip->references) > 0;\n\tif (stillopen)\n\t\treturn;\t//not free yet\n\n\tVFS_CLOSE(zip->raw);\n\tfor (s = 0; s < zip->numspans; s++)\n\t{\n\t\tif (zip->spans[s])\n\t\t{\n\t\t\tzip->spans[s]->pub.ClosePath(&zip->spans[s]->pub);\n\t\t\tzip->spans[s] = NULL;\n\t\t}\n\t}\n\tZ_Free(zip->spans);\n\tSys_DestroyMutex(zip->mutex);\n\tif (zip->files)\n\t\tZ_Free(zip->files);\n\tZ_Free(zip);\n}\nstatic void QDECL FSZIP_BuildHash(searchpathfuncs_t *handle, int depth, void (QDECL *AddFileHash)(int depth, const char *fname, fsbucket_t *filehandle, void *pathhandle))\n{\n\tzipfile_t *zip = (void*)handle;\n\tint i;\n\n\tfor (i = 0; i < zip->numfiles; i++)\n\t{\n\t\tif (zip->files[i].flags & ZFL_CORRUPT)\n\t\t\tcontinue;\n\t\tAddFileHash(depth, zip->files[i].name, &zip->files[i].bucket, &zip->files[i]);\n\t}\n}\nstatic unsigned int QDECL FSZIP_FLocate(searchpathfuncs_t *handle, flocation_t *loc, const char *filename, void *hashedresult)\n{\n\tzpackfile_t *pf = hashedresult;\n\tint i;\n\tzipfile_t\t*zip = (void*)handle;\n\tint ret = FF_NOTFOUND;\n\n// look through all the pak file elements\n\n\tif (pf)\n\t{\t//is this a pointer to a file in this pak?\n\t\tif (pf < zip->files || pf >= zip->files + zip->numfiles)\n\t\t\treturn FF_NOTFOUND;\t//was found in a different path\n\t}\n\telse\n\t{\n\t\tfor (i=0 ; i<zip->numfiles ; i++)\t//look for the file\n\t\t{\n\t\t\tif (!Q_strcasecmp (zip->files[i].name, filename))\n\t\t\t{\n\t\t\t\tif (zip->files[i].flags & ZFL_CORRUPT)\n\t\t\t\t\tcontinue;\n\t\t\t\tpf = &zip->files[i];\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\tif (pf)\n\t{\n\t\tret = FF_FOUND;\n\t\tif (loc)\n\t\t{\n\t\t\tloc->fhandle = pf;\n\t\t\t*loc->rawname = 0;\n\t\t\tloc->offset = (qofs_t)-1;\n\t\t\tloc->len = pf->filelen;\n\n\t\t\tif (pf->flags & ZFL_SYMLINK)\n\t\t\t\tret = FF_SYMLINK;\n\n\t//\t\tif (unzLocateFileMy (zip->handle, loc->index, zip->files[loc->index].filepos) == 2)\n\t//\t\t\tret = FF_SYMLINK;\n\t//\t\tloc->offset = unzGetCurrentFileUncompressedPos(zip->handle);\n//\t\t\tif (loc->offset<0)\n//\t\t\t{\t//file not found, or is compressed.\n//\t\t\t\t*loc->rawname = '\\0';\n//\t\t\t\tloc->offset=0;\n//\t\t\t}\n\t\t}\n\t\telse\n\t\t\tret = FF_FOUND;\n\t\treturn ret;\n\t}\n\treturn FF_NOTFOUND;\n}\n\nstatic vfsfile_t *QDECL FSZIP_OpenVFS(searchpathfuncs_t *handle, flocation_t *loc, const char *mode);\nstatic void QDECL FSZIP_ReadFile(searchpathfuncs_t *handle, flocation_t *loc, char *buffer)\n{\n\tvfsfile_t *f;\n\tf = FSZIP_OpenVFS(handle, loc, \"rb\");\n\tif (!f)\t//err...\n\t\treturn;\n\tVFS_READ(f, buffer, loc->len);\n\tVFS_CLOSE(f);\n}\n\nstatic int QDECL FSZIP_EnumerateFiles (searchpathfuncs_t *handle, const char *match, int (QDECL *func)(const char *, qofs_t, time_t mtime, void *, searchpathfuncs_t *spath), void *parm)\n{\n\tzipfile_t *zip = (void*)handle;\n\tint\t\tnum;\n\n\tfor (num = 0; num<(int)zip->numfiles; num++)\n\t{\n\t\tif (wildcmp(match, zip->files[num].name))\n\t\t{\n\t\t\tif (!func(zip->files[num].name, zip->files[num].filelen, zip->files[num].mtime, parm, &zip->pub))\n\t\t\t\treturn false;\n\t\t}\n\t}\n\n\treturn true;\n}\n\nstatic int QDECL FSZIP_GeneratePureCRC(searchpathfuncs_t *handle, const int *seed)\n{\n\tzipfile_t *zip = (void*)handle;\n\n\tint result;\n\tint *filecrcs;\n\tint numcrcs=0;\n\tint i;\n\n\tfilecrcs = BZ_Malloc((zip->numfiles+1)*sizeof(int));\n\tif (seed)\n\t\tfilecrcs[numcrcs++] = *seed;\n\n\tfor (i = 0; i < zip->numfiles; i++)\n\t{\n\t\tif (zip->files[i].filelen>0)\n\t\t\tfilecrcs[numcrcs++] = zip->files[i].crc;\n\t}\n\n\tresult = CalcHashInt(&hash_md4, filecrcs, numcrcs*sizeof(int));\n\n\tBZ_Free(filecrcs);\n\treturn result;\n}\n\nstruct decompressstate\n{\n\tstruct decompressstate *(*Reinit)(zipfile_t *source, qofs_t start, qofs_t csize, qofs_t usize, char *filename, char *password, unsigned int crc);\n\tqofs_t (*Read)(struct decompressstate *st, qbyte *buffer, qofs_t bytes);\n\tqboolean (*Seek)(struct decompressstate *st, qofs_t *fileofs, qofs_t newofs);\t//may fail, file will be fully decompressed.\n\tvoid (*Destroy)(struct decompressstate *st);\n\n\tzipfile_t *source;\n\tqofs_t cstart;\t//position of start of data\n\tqofs_t cofs;\t//current read offset\n\tqofs_t cend;\t//compressed data ends at this point.\n\tqofs_t usize;\t//data remaining. refuse to read more bytes than this.\n\tunsigned char inbuffer[16384];\n\tunsigned char outbuffer[16384];\n\tunsigned int readoffset;\n\n#ifdef ZIPCRYPT\n\tqboolean encrypted;\n\tunsigned int cryptkey[3];\n\tunsigned int initialkey[3];\n\tconst z_crc_t * crctable;\n#endif\n\n#ifdef AVAIL_ZLIB\n\tz_stream strm;\n#endif\n#ifdef AVAIL_BZLIB\n\tbz_stream bstrm;\n#endif\n};\n\n#if defined(AVAIL_ZLIB) || defined(AVAIL_BZLIB)\n#define DO_ZIP_DECOMPRESS\n\n#ifdef ZIPCRYPT\n#define CRC32(c, b) ((*(st->crctable+(((int)(c) ^ (b)) & 0xff))) ^ ((c) >> 8))\n\nstatic void FSZIP_UpdateKeys(struct decompressstate *st, unsigned char ch)\n{\n\tst->cryptkey[0] = CRC32(st->cryptkey[0], ch);\n\tst->cryptkey[1] += (st->cryptkey[0] & 0xffu);\n\tst->cryptkey[1] = st->cryptkey[1] * 0x8088405u + 1;\n\tch = st->cryptkey[1] >> 24;\n\tst->cryptkey[2] = CRC32(st->cryptkey[2], ch);\n}\nstatic unsigned char FSZIP_DecryptByte(struct decompressstate *st)\n{\n\tunsigned int temp;\n\ttemp = (st->cryptkey[2]&0xffff) | 2;\n\treturn ((temp * (temp ^ 1)) >> 8) & 0xff;\n}\n\nstatic qboolean FSZIP_SetupCrytoKeys(struct decompressstate *st, const char *password, char *cryptheader, unsigned int crc)\n{\n\tunsigned int u;\n\tst->crctable = qget_crc_table();\n\tst->encrypted = true;\n\tst->cryptkey[0] = 0x12345678;\n\tst->cryptkey[1] = 0x23456789;\n\tst->cryptkey[2] = 0x34567890;\n\twhile (*password)\n\t\tFSZIP_UpdateKeys(st, *password++);\n\n\tfor (u = 0; u < 12; u++)\n\t{\n\t\tunsigned char ch = cryptheader[u] ^ FSZIP_DecryptByte(st);\n\t\tFSZIP_UpdateKeys(st, ch);\n\t\tcryptheader[u] = ch;\n\t}\n\n\tmemcpy(st->initialkey, st->cryptkey, sizeof(st->initialkey));\n\n\t//cryptheader[11] should be the high byte of the file's crc\n\t//[10] might be the second byte, but also might not be.\n\tif (cryptheader[11] != (unsigned char)(crc>>24))\n\t\treturn false;\n//\tif (cryptheader[10] != (unsigned char)(crc>>16))\n//\t\treturn false;\n\treturn true;\n}\nstatic void FSZIP_DecryptBlock(struct decompressstate *st, char *block, size_t blocksize)\n{\n    while (blocksize--)\n\t{\n        unsigned char temp = *block ^ FSZIP_DecryptByte(st);\n        FSZIP_UpdateKeys(st, temp);\n        *block++ = temp;\n\t}\n}\n#endif\n\n#ifdef AVAIL_ZLIB\n//if the offset is still within our decompressed block then we can just rewind a smidge\nstatic qboolean FSZIP_Deflate_Seek(struct decompressstate *st, qofs_t *offset, qofs_t newoffset)\n{\n\tqofs_t dist;\n\tif (newoffset <= *offset)\n\t{\t//rewinding\n\t\tdist = *offset-newoffset;\n\t\tif (st->readoffset >= dist)\n\t\t{\n\t\t\tst->readoffset -= dist;\n\t\t\t*offset -= dist;\n\t\t\treturn true;\n\t\t}\n\t\t//went too far back, we lost that data.\n\t}\n\telse\n\t{\t//seek forward... mneh\n\t\tdist = newoffset - *offset;\n\t\tif (st->readoffset+dist <= st->strm.total_out)\n\t\t{\n\t\t\tst->readoffset += dist;\n\t\t\t*offset += dist;\n\t\t\treturn true;\n\t\t}\n\t\t//fail. we could just decompress more, but we're probably better off copying to a temp file instead.\n\t}\n\treturn false;\n}\nstatic qofs_t FSZIP_Deflate_Read(struct decompressstate *st, qbyte *buffer, qofs_t bytes)\n{\n\tqboolean eof = false;\n\tint err;\n\tqofs_t read = 0;\n\twhile(bytes)\n\t{\n\t\tif (st->readoffset < st->strm.total_out)\n\t\t{\n\t\t\tunsigned int consume = st->strm.total_out-st->readoffset;\n\t\t\tif (consume > bytes)\n\t\t\t\tconsume = bytes;\n\t\t\tmemcpy(buffer, st->outbuffer+st->readoffset, consume);\n\t\t\tbuffer += consume;\n\t\t\tbytes -= consume;\n\t\t\tread += consume;\n\t\t\tst->readoffset += consume;\n\t\t\tcontinue;\n\t\t}\n\t\telse if (eof)\n\t\t\tbreak;\t//no input available, and nothing in the buffers.\n\n\t\tst->strm.total_out = 0;\n\t\tst->strm.avail_out = sizeof(st->outbuffer);\n\t\tst->strm.next_out = st->outbuffer;\n\t\tst->readoffset = 0;\n\t\tif (!st->strm.avail_in)\n\t\t{\n\t\t\tqofs_t sz;\n\t\t\tsz = st->cend - st->cofs;\n\t\t\tif (sz > sizeof(st->inbuffer))\n\t\t\t\tsz = sizeof(st->inbuffer);\n\t\t\tif (sz)\n\t\t\t{\n\t\t\t\t//feed it.\n\t\t\t\tif (Sys_LockMutex(st->source->mutex))\n\t\t\t\t{\n\t\t\t\t\tVFS_SEEK(st->source->raw, st->cofs);\n\t\t\t\t\tst->strm.avail_in = VFS_READ(st->source->raw, st->inbuffer, sz);\n\t\t\t\t\tSys_UnlockMutex(st->source->mutex);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tst->strm.avail_in = 0;\n\t\t\t\tst->strm.next_in = st->inbuffer;\n\t\t\t\tst->cofs += st->strm.avail_in;\n#ifdef ZIPCRYPT\n\t\t\t\tif (st->encrypted)\n\t\t\t\t\tFSZIP_DecryptBlock(st, st->inbuffer, st->strm.avail_in);\n#endif\n\t\t\t}\n\t\t\tif (!st->strm.avail_in)\n\t\t\t\teof = true;\n\t\t}\n\t\terr = qinflate(&st->strm,Z_SYNC_FLUSH);\n\t\tif (err == Z_STREAM_END)\n\t\t\teof = true;\n\t\telse if (err != Z_OK)\n\t\t\tbreak;\n\t}\n\treturn read;\n}\n\nstatic void FSZIP_Deflate_Destroy(struct decompressstate *st)\n{\n\tqinflateEnd(&st->strm);\n\tZ_Free(st);\n}\n\nstatic struct decompressstate *FSZIP_Deflate_Init(zipfile_t *source, qofs_t start, qofs_t csize, qofs_t usize, char *filename, char *password, unsigned int crc)\n{\n\tstruct decompressstate *st;\n\tif (!ZLIB_LOADED())\n\t{\n\t\tCon_Printf(\"zlib not available\\n\");\n\t\treturn NULL;\n\t}\n\tst = Z_Malloc(sizeof(*st));\n\tst->Reinit\t= FSZIP_Deflate_Init;\n\tst->Read\t= FSZIP_Deflate_Read;\n\tst->Seek\t= FSZIP_Deflate_Seek;\n\tst->Destroy\t= FSZIP_Deflate_Destroy;\n\tst->source = source;\n\n#ifdef ZIPCRYPT\n\tif (password && csize >= 12)\n\t{\n\t\tchar entropy[12];\n\t\tif (Sys_LockMutex(source->mutex))\n\t\t{\n\t\t\tVFS_SEEK(source->raw, start);\n\t\t\tVFS_READ(source->raw, entropy, sizeof(entropy));\n\t\t\tSys_UnlockMutex(source->mutex);\n\t\t}\n\t\tif (!FSZIP_SetupCrytoKeys(st, password, entropy, crc))\n\t\t{\n\t\t\tCon_Printf(\"Invalid password, cannot decrypt %s\\n\", filename);\n\t\t\tZ_Free(st);\n\t\t\treturn NULL;\n\t\t}\n\t\tstart += sizeof(entropy);\n\t\tcsize -= sizeof(entropy);\n\t}\n#endif\n\n\tst->cstart = st->cofs = start;\n\tst->cend = start + csize;\n\tst->usize = usize;\n\n\tst->strm.data_type = Z_UNKNOWN;\n\tqinflateInit2(&st->strm, -MAX_WBITS);\n\treturn st;\n}\n#endif\n\n#if defined(ZLIB_DEFLATE64) && !defined(AVAIL_ZLIB)\n\t#undef ZLIB_DEFLATE64\t//don't be silly.\n#endif\n#if defined(ZLIB_DEFLATE64)\n#include \"infback9.h\"\t//an obscure compile-your-own part of zlib.\nstruct def64ctx\n{\n\tvfsfile_t *src;\n\tvfsfile_t *dst;\n\tqofs_t csize;\n\tqofs_t usize;\n\tunsigned int crc;\n\n\tqbyte inbuf[0x8000];\n};\nstatic unsigned int FSZIP_Deflate64_Grab(void *vctx, unsigned char **bufptr)\n{\n\tstruct def64ctx *ctx = vctx;\n\tint avail;\n\tavail = sizeof(ctx->inbuf);\n\tif (avail > ctx->csize)\n\t\tavail = ctx->csize;\t//don't over-read.\n\tif (avail <= 0)\n\t{\n\t\t*bufptr = NULL;\n\t\treturn 0;\n\t}\n\n\tavail = VFS_READ(ctx->src, ctx->inbuf, avail);\n\t*bufptr = ctx->inbuf;\n\n\tif (avail < 0)\n\t\tavail = 0;\t//treated as eof...\n\tctx->csize -= avail;\n\treturn avail;\n}\nstatic int FSZIP_Deflate64_Spew(void *vctx, unsigned char *buf, unsigned int buflen)\n{\n\tstruct def64ctx *ctx = vctx;\n\n\t//update the crc\n\tctx->crc = crc32(ctx->crc, buf, buflen);\n\tctx->usize += buflen;\n\n\tif (VFS_WRITE(ctx->dst, buf, buflen) != buflen)\n\t\treturn 1;\t//failure returns non-zero.\n\treturn 0;\n}\n//inflateBack stuff is apparently not restartable, and must read the entire file\nstatic vfsfile_t *FSZIP_Deflate64(vfsfile_t *src, qofs_t csize, qofs_t usize, unsigned int crc32)\n{\n\tz_stream strm = {NULL};\n\tqbyte window[65536];\n\tstruct def64ctx ctx;\n\tctx.src = src;\n\tctx.dst = VFSPIPE_Open(1, true);\n\tctx.csize = csize;\n\tctx.usize = 0;\n\tctx.crc = 0;\n\n\tstrm.data_type = Z_UNKNOWN;\n\tinflateBack9Init(&strm, window);\n\t//getting inflateBack9 to\n\tif (Z_STREAM_END != inflateBack9(&strm, FSZIP_Deflate64_Grab, &ctx, FSZIP_Deflate64_Spew, &ctx))\n\t{\t//some stream error?\n\t\tCon_Printf(\"Decompression error\\n\");\n\t\tVFS_CLOSE(ctx.dst);\n\t\tctx.dst = NULL;\n\t}\n\telse if (ctx.csize != 0 || ctx.usize != usize)\n\t{\t//corrupt file table?\n\t\tCon_Printf(\"Decompression size error\\n\");\n\t\tCon_Printf(\"read %i of %i bytes\\n\", (unsigned)ctx.csize, (unsigned)csize);\n\t\tCon_Printf(\"wrote %i of %i bytes\\n\", (unsigned)ctx.usize, (unsigned)usize);\n\t\tVFS_CLOSE(ctx.dst);\n\t\tctx.dst = NULL;\n\t}\n\telse if (ctx.crc != crc32)\n\t{\t//corrupt file table?\n\t\tCon_Printf(\"CRC32 error\\n\");\n\t\tVFS_CLOSE(ctx.dst);\n\t\tctx.dst = NULL;\n\t}\n\tinflateBack9End(&strm);\n\n\treturn ctx.dst;\n}\n#endif\n\n#ifdef AVAIL_BZLIB\n//if the offset is still within our decompressed block then we can just rewind a smidge\nstatic qboolean FSZIP_BZip2_Seek(struct decompressstate *st, qofs_t *offset, qofs_t newoffset)\n{\n\tqofs_t dist;\n\tif (newoffset <= *offset)\n\t{\t//rewinding\n\t\tdist = *offset-newoffset;\n\t\tif (st->readoffset >= dist)\n\t\t{\n\t\t\tst->readoffset -= dist;\n\t\t\t*offset -= dist;\n\t\t\treturn true;\n\t\t}\n\t\t//went too far back, we lost that data.\n\t}\n\telse\n\t{\t//seek forward... mneh\n\t\tdist = newoffset - *offset;\n\t\tif (st->readoffset+dist <= st->bstrm.total_out_lo32)\n\t\t{\n\t\t\tst->readoffset += dist;\n\t\t\t*offset += dist;\n\t\t\treturn true;\n\t\t}\n\t\t//fail. we could just decompress more, but we're probably better off copying to a temp file instead.\n\t}\n\treturn false;\n}\n//decompress in chunks.\nstatic qofs_t FSZIP_BZip2_Read(struct decompressstate *st, qbyte *buffer, qofs_t bytes)\n{\n\tqboolean eof = false;\n\tint err;\n\tqofs_t read = 0;\n\twhile(bytes)\n\t{\n\t\tif (st->readoffset < st->bstrm.total_out_lo32)\n\t\t{\n\t\t\tunsigned int consume = st->bstrm.total_out_lo32-st->readoffset;\n\t\t\tif (consume > bytes)\n\t\t\t\tconsume = bytes;\n\t\t\tmemcpy(buffer, st->outbuffer+st->readoffset, consume);\n\t\t\tbuffer += consume;\n\t\t\tbytes -= consume;\n\t\t\tread += consume;\n\t\t\tst->readoffset += consume;\n\t\t\tcontinue;\n\t\t}\n\t\telse if (eof)\n\t\t\tbreak;\t//no input available, and nothing in the buffers.\n\n\t\tst->bstrm.total_out_lo32 = 0;\n\t\tst->bstrm.total_out_hi32 = 0;\n\t\tst->bstrm.avail_out = sizeof(st->outbuffer);\n\t\tst->bstrm.next_out = st->outbuffer;\n\t\tst->readoffset = 0;\n\t\tif (!st->bstrm.avail_in)\n\t\t{\n\t\t\tqofs_t sz;\n\t\t\tsz = st->cend - st->cofs;\n\t\t\tif (sz > sizeof(st->inbuffer))\n\t\t\t\tsz = sizeof(st->inbuffer);\n\t\t\tif (sz)\n\t\t\t{\n\t\t\t\t//feed it.\n\t\t\t\tif (Sys_LockMutex(st->source->mutex))\n\t\t\t\t{\n\t\t\t\t\tVFS_SEEK(st->source->raw, st->cofs);\n\t\t\t\t\tst->bstrm.avail_in = VFS_READ(st->source->raw, st->inbuffer, sz);\n\t\t\t\t\tSys_UnlockMutex(st->source->mutex);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tst->bstrm.avail_in = 0;\n\t\t\t\tst->bstrm.next_in = st->inbuffer;\n\t\t\t\tst->cofs += st->bstrm.avail_in;\n#ifdef ZIPCRYPT\n\t\t\t\tif (st->encrypted)\n\t\t\t\t\tFSZIP_DecryptBlock(st, st->inbuffer, st->bstrm.avail_in);\n#endif\n\t\t\t}\n\t\t\tif (!st->bstrm.avail_in)\n\t\t\t\teof = true;\n\t\t}\n\t\terr = BZ2_bzDecompress(&st->bstrm);\n\t\tif (err == BZ_FINISH_OK)\n\t\t\teof = true;\n\t\telse if (err < 0)\n\t\t\tbreak;\n\t}\n\treturn read;\n}\n\nstatic void FSZIP_BZip2_Destroy(struct decompressstate *st)\n{\n\tBZ2_bzDecompressEnd(&st->bstrm);\n\tZ_Free(st);\n}\n\nstatic struct decompressstate *FSZIP_BZip2_Init(zipfile_t *source, qofs_t start, qofs_t csize, qofs_t usize, char *filename, char *password, unsigned int crc)\n{\n\tstruct decompressstate *st;\n\tif (!BZLIB_LOADED())\n\t{\n\t\tCon_Printf(\"bzlib not available\\n\");\n\t\treturn NULL;\n\t}\n\tst = Z_Malloc(sizeof(*st));\n\tst->Reinit\t= FSZIP_BZip2_Init;\n\tst->Read\t= FSZIP_BZip2_Read;\n\tst->Seek\t= FSZIP_BZip2_Seek;\n\tst->Destroy\t= FSZIP_BZip2_Destroy;\n\tst->source\t= source;\n\n#ifdef ZIPCRYPT\n\tif (password && csize >= 12)\n\t{\n\t\tchar entropy[12];\n\t\tif (Sys_LockMutex(source->mutex))\n\t\t{\n\t\t\tVFS_SEEK(source->raw, start);\n\t\t\tVFS_READ(source->raw, entropy, sizeof(entropy));\n\t\t\tSys_UnlockMutex(source->mutex);\n\t\t}\n\t\tif (!FSZIP_SetupCrytoKeys(st, password, entropy, crc))\n\t\t{\n\t\t\tCon_Printf(\"Invalid password, cannot decrypt %s\\n\", filename);\n\t\t\tZ_Free(st);\n\t\t\treturn NULL;\n\t\t}\n\t\tstart += sizeof(entropy);\n\t\tcsize -= sizeof(entropy);\n\t}\n#endif\n\n\tst->cstart = st->cofs = start;\n\tst->cend = start + csize;\n\tst->usize = usize;\n\n\tBZ2_bzDecompressInit(&st->bstrm, 0, false);\n\treturn st;\n}\n#endif\n\nstruct decompressstate *FSZIP_Decompress_Rewind(struct decompressstate *decompress)\n{\n\tqofs_t cstart = decompress->cstart, csize = decompress->cend - cstart;\n\tqofs_t usize = decompress->usize;\n\tstruct decompressstate *(*Reinit)(zipfile_t *source, qofs_t start, qofs_t csize, qofs_t usize, char *filename, char *password, unsigned int crc) = decompress->Reinit;\n\tzipfile_t *source = decompress->source;\n#ifdef ZIPCRYPT\t//we need to preserve any crypto stuff if we're restarting the stream\n\tqboolean encrypted = decompress->encrypted;\n\tunsigned int cryptkeys[3];\n\tconst z_crc_t *crctab = decompress->crctable;\n\tmemcpy(cryptkeys, decompress->initialkey, sizeof(cryptkeys));\n#endif\n\n\tdecompress->Destroy(decompress);\n\n\tdecompress = Reinit(source, cstart, csize, usize, NULL, NULL, 0);\n#ifdef ZIPCRYPT\n\tdecompress->encrypted = encrypted;\n\tdecompress->crctable = crctab;\n\tmemcpy(decompress->initialkey, cryptkeys, sizeof(decompress->initialkey));\n\tmemcpy(decompress->cryptkey, cryptkeys, sizeof(decompress->cryptkey));\n#endif\n\treturn decompress;\n}\n\nstatic vfsfile_t *FSZIP_Decompress_ToTempFile(struct decompressstate *decompress)\n{\t//if they're going to seek on a file in a zip, let's just copy it out\n\tqofs_t upos = 0, usize = decompress->usize;\n\tqofs_t chunk;\n\tqbyte buffer[16384];\n\tvfsfile_t *defer;\n\n\tdefer = FS_OpenTemp();\n\tif (defer)\n\t{\n\t\tdecompress = FSZIP_Decompress_Rewind(decompress);\n\n\t\twhile (upos < usize)\n\t\t{\n\t\t\tchunk = usize - upos;\n\t\t\tif (chunk > sizeof(buffer))\n\t\t\t\tchunk = sizeof(buffer);\n\t\t\tif (!decompress->Read(decompress, buffer, chunk))\n\t\t\t\tbreak;\n\t\t\tif (VFS_WRITE(defer, buffer, chunk) != chunk)\n\t\t\t\tbreak;\n\t\t\tupos += chunk;\n\t\t}\n\t\tdecompress->Destroy(decompress);\n\n\t\treturn defer;\n\t}\n\treturn NULL;\n}\n#endif\n\n//an open vfsfile_t\ntypedef struct {\n\tvfsfile_t funcs;\n\n\tvfsfile_t *defer;\t//if set, reads+seeks are defered to this file instead.\n\n\t//in case we're forced away.\n\tzipfile_t *parent;\n#ifdef DO_ZIP_DECOMPRESS\n\tstruct decompressstate *decompress;\n#endif\n\tqofs_t pos;\n\tqofs_t length;\t//try and optimise some things\n//\tint index;\n\tqofs_t startpos;\t//file data offset\n} vfszip_t;\n\nstatic int QDECL VFSZIP_ReadBytes (struct vfsfile_s *file, void *buffer, int bytestoread)\n{\n\tint read;\n\tvfszip_t *vfsz = (vfszip_t*)file;\n\n\tif (vfsz->defer)\n\t\treturn VFS_READ(vfsz->defer, buffer, bytestoread);\n\n#ifdef DO_ZIP_DECOMPRESS\n\tif (vfsz->decompress)\n\t{\n\t\tread = vfsz->decompress->Read(vfsz->decompress, buffer, bytestoread);\n\t}\n\telse\n#endif\n\tif (Sys_LockMutex(vfsz->parent->mutex))\n\t{\n\t\tVFS_SEEK(vfsz->parent->raw, vfsz->pos+vfsz->startpos);\n\t\tif (vfsz->pos + bytestoread > vfsz->length)\n\t\t\tbytestoread = max(0, vfsz->length - vfsz->pos);\n\t\tread = VFS_READ(vfsz->parent->raw, buffer, bytestoread);\n\t\tSys_UnlockMutex(vfsz->parent->mutex);\n\t}\n\telse\n\t\tread = 0;\n\n\tif (read < bytestoread)\n\t\t((char*)buffer)[read] = 0;\n\n\tvfsz->pos += read;\n\treturn read;\n}\nstatic qboolean QDECL VFSZIP_Seek (struct vfsfile_s *file, qofs_t pos)\n{\n\tvfszip_t *vfsz = (vfszip_t*)file;\n\n\tif (vfsz->defer)\n\t\treturn VFS_SEEK(vfsz->defer, pos);\n\n#ifdef DO_ZIP_DECOMPRESS\n\t//This is *really* inefficient\n\tif (vfsz->decompress)\n\t{\t//if they're going to seek on a file in a zip, let's just copy it out\n\t\tif (vfsz->decompress->Seek(vfsz->decompress, &vfsz->pos, pos))\n\t\t\treturn true;\n\t\telse if (pos == 0)\n\t\t\tvfsz->decompress = FSZIP_Decompress_Rewind(vfsz->decompress);\n\t\telse\n\t\t{\n\t\t\tvfsz->defer = FSZIP_Decompress_ToTempFile(vfsz->decompress);\n\t\t\tvfsz->decompress = NULL;\n\t\t\tif (vfsz->defer)\n\t\t\t\treturn VFS_SEEK(vfsz->defer, pos);\n\t\t\treturn false;\n\t\t}\n\t}\n#endif\n\n\tif (pos > vfsz->length)\n\t\treturn false;\n\tvfsz->pos = pos;\n\n\treturn true;\n}\nstatic qofs_t QDECL VFSZIP_Tell (struct vfsfile_s *file)\n{\n\tvfszip_t *vfsz = (vfszip_t*)file;\n\n\tif (vfsz->defer)\n\t\treturn VFS_TELL(vfsz->defer);\n\n\treturn vfsz->pos;\n}\nstatic qofs_t QDECL VFSZIP_GetLen (struct vfsfile_s *file)\n{\n\tvfszip_t *vfsz = (vfszip_t*)file;\n\treturn vfsz->length;\n}\nstatic qboolean QDECL VFSZIP_Close (struct vfsfile_s *file)\n{\n\tvfszip_t *vfsz = (vfszip_t*)file;\n\n\tif (vfsz->defer)\n\t\tVFS_CLOSE(vfsz->defer);\n\n#ifdef DO_ZIP_DECOMPRESS\n\tif (vfsz->decompress)\n\t\tvfsz->decompress->Destroy(vfsz->decompress);\n#endif\n\n\tFSZIP_ClosePath(&vfsz->parent->pub);\n\tZ_Free(vfsz);\n\n\treturn true;\t//FIXME: return an error if the crc was wrong.\n}\n\nstatic qboolean FSZIP_ValidateLocalHeader(zipfile_t *zip, zpackfile_t *zfile, qofs_t *datastart, qofs_t *datasize);\n\nstatic qboolean QDECL FSZIP_FileStat (searchpathfuncs_t *handle, flocation_t *loc, time_t *mtime)\n{\n\tzpackfile_t *pf = loc->fhandle;\n\t*mtime = pf->mtime;\n\treturn true;\n}\n\nstatic vfsfile_t *QDECL FSZIP_OpenVFS(searchpathfuncs_t *handle, flocation_t *loc, const char *mode)\n{\n\tzipfile_t *zip = (void*)handle;\n\tvfszip_t *vfsz;\n\tunsigned int flags;\n\tqofs_t datasize = 0;\n\tzpackfile_t *pf = loc->fhandle;\n\n\tif (strcmp(mode, \"rb\"))\n\t\treturn NULL; //urm, unable to write/append\n\n\tif (qofs_Error(loc->len))\n\t\treturn NULL;\n\n\tflags = pf->flags;\n\tif (flags & ZFL_CORRUPT)\n\t\treturn NULL;\n\n\tif (zip->thisdisk != pf->disknum)\n\t{\n\t\tif (pf->disknum >= zip->numspans)\n\t\t{\n\t\t\tCon_Printf(\"file %s has oob disk number\\n\", pf->name);\n\t\t\treturn NULL;\n\t\t}\n\t\tzip = zip->spans[pf->disknum];\n\t\tif (!zip)\n\t\t{\n\t\t\tzip = (void*)handle;\n\t\t\tCon_Printf(\"spanned zip %s lacks segment %u for file %s\\n\", zip->filename, pf->disknum+1, pf->name);\n\t\t\treturn NULL;\n\t\t}\n\t}\n\n\tvfsz = Z_Malloc(sizeof(vfszip_t));\n\n\tvfsz->parent = zip;\n\tvfsz->length = loc->len;\n\n#ifdef _DEBUG\n\tQ_strncpyz(vfsz->funcs.dbgname, pf->name, sizeof(vfsz->funcs.dbgname));\n#endif\n\tvfsz->funcs.Close = VFSZIP_Close;\n\tvfsz->funcs.GetLen = VFSZIP_GetLen;\n\tvfsz->funcs.ReadBytes = VFSZIP_ReadBytes;\n\t//vfsz->funcs.WriteBytes = VFSZIP_WriteBytes;\n\tvfsz->funcs.Seek = VFSZIP_Seek;\n\tvfsz->funcs.Tell = VFSZIP_Tell;\n\tvfsz->funcs.WriteBytes = NULL;\n\tvfsz->funcs.seekstyle = SS_SLOW;\n\n\t//NOTE: pf->name is the quakeified name, and may have an extra prefix/stripped prefix for certain zips - different from what you'd see if you opened the zip yourself. this is only relevant for debugging, whuch might be misleading but is not fatal.\n\tif (!FSZIP_ValidateLocalHeader(zip, pf, &vfsz->startpos, &datasize))\n\t{\n\t\tCon_Printf(CON_WARNING\"file %s:%s is incompatible or inconsistent with zip central directory\\n\", zip->filename, pf->name);\n\t\tZ_Free(vfsz);\n\t\treturn NULL;\n\t}\n\n\tif (flags & ZFL_DEFLATE64D)\n\t{\t//crap\n#if defined(ZLIB_DEFLATE64)\n\t\tvfsfile_t *tmp = NULL;\n\t\tqofs_t startpos = vfsz->startpos;\n\t\tqofs_t usize = vfsz->length;\n\t\tqofs_t csize = datasize;\n\t\tZ_Free(vfsz);\n\n\t\tCon_Printf(CON_WARNING\"file %s:%s was compressed with deflate64\\n\", zip->filename, pf->name);\n\n\t\tif (Sys_LockMutex(zip->mutex))\n\t\t{\n\t\t\tVFS_SEEK(vfsz->parent->raw, startpos);\n\t\t\ttmp = FSZIP_Deflate64(zip->raw, csize, usize, pf->crc);\n\t\t\tSys_UnlockMutex(zip->mutex);\n\t\t}\n\n\t\treturn tmp;\n#else\n\t\tCon_Printf(CON_WARNING\"%s:%s: deflate64 not supported\\n\", COM_SkipPath(zip->filename), pf->name);\n\t\tZ_Free(vfsz);\n\t\treturn NULL;\n#endif\n\t}\n\n\tif (flags & ZFL_DEFLATED)\n\t{\n#ifdef AVAIL_ZLIB\n#ifdef ZIPCRYPT\n\t\t//FIXME: Cvar_Get is not threadsafe, and nor is accessing the cvar...\n\t\tchar *password = (flags & ZFL_WEAKENCRYPT)?Cvar_Get(\"fs_zip_password\", \"thisispublic\", 0, \"Filesystem\")->string:NULL;\n#else\n\t\tchar *password = NULL;\n#endif\n\t\tvfsz->decompress = FSZIP_Deflate_Init(zip, vfsz->startpos, datasize, vfsz->length, pf->name, password, pf->crc);\n\t\tif (!vfsz->decompress)\n#else\n\t\tCon_Printf(\"%s:%s: zlib not supported\\n\", COM_SkipPath(zip->filename), pf->name);\n#endif\n\t\t{\n\t\t\t/*\n\t\t\twindows explorer tends to use deflate64 on large files, which zlib and thus we, do not support, thus this is a 'common' failure path\n\t\t\tthis might also trigger from other errors, of course.\n\t\t\t*/\n\t\t\tZ_Free(vfsz);\n\t\t\treturn NULL;\n\t\t}\n\t}\n\tif (flags & ZFL_BZIP2)\n\t{\n#ifdef AVAIL_BZLIB\n#ifdef ZIPCRYPT\n\t\t//FIXME: Cvar_Get is not threadsafe, and nor is accessing the cvar...\n\t\tchar *password = (flags & ZFL_WEAKENCRYPT)?Cvar_Get(\"fs_zip_password\", \"thisispublic\", 0, \"Filesystem\")->string:NULL;\n#else\n\t\tchar *password = NULL;\n#endif\n\t\tvfsz->decompress = FSZIP_BZip2_Init(zip, vfsz->startpos, datasize, vfsz->length, pf->name, password, pf->crc);\n\t\tif (!vfsz->decompress)\n#else\n\t\tCon_Printf(\"%s:%s: libbz2 not supported\\n\", COM_SkipPath(zip->filename), pf->name);\n#endif\n\t\t{\n\t\t\t/*\n\t\t\twindows explorer tends to use deflate64 on large files, which zlib and thus we, do not support, thus this is a 'common' failure path\n\t\t\tthis might also trigger from other errors, of course.\n\t\t\t*/\n\t\t\tZ_Free(vfsz);\n\t\t\treturn NULL;\n\t\t}\n\t}\n\n\tFTE_Atomic32_Inc(&zip->references);\n\treturn (vfsfile_t*)vfsz;\n}\n\nstruct zipinfo\n{\n\tunsigned int\tthisdisk;\t\t\t\t\t//this disk number\n\tunsigned int\tcentraldir_startdisk;\t\t//central directory starts on this disk\n\tqofs_t\t\t\tcentraldir_numfiles_disk;\t//number of files in the centraldir on this disk\n\tqofs_t\t\t\tcentraldir_numfiles_all;\t//number of files in the centraldir on all disks\n\tqofs_t\t\t\tcentraldir_size;\t\t\t//total size of the centraldir\n\tqofs_t\t\t\tcentraldir_offset;\t\t\t//start offset of the centraldir with respect to the first disk that contains it\n\tunsigned short\tcommentlength;\t\t\t\t//length of the comment at the end of the archive.\n\n\tunsigned int\tzip64_centraldirend_disk;\t//zip64 weirdness\n\tqofs_t\t\t\tzip64_centraldirend_offset;\n\tunsigned int\tdiskcount;\n\tqofs_t\t\t\tzip64_eocdsize;\n\tunsigned short\tzip64_version_madeby;\n\tunsigned short\tzip64_version_needed;\n\n\tunsigned short\tcentraldir_compressionmethod;\n\tunsigned short\tcentraldir_algid;\n\n\tqofs_t\t\t\tcentraldir_end;\n\tqofs_t\t\t\tzipoffset;\n};\nstruct zipcentralentry\n{\n\tunsigned char\t*fname;\n\tqofs_t\t\t\tcesize;\n\tunsigned int\tflags;\n\ttime_t\t\t\tmtime;\n\n\t//PK12\n\tunsigned short\tversion_madeby;\n\tunsigned short\tversion_needed;\n\tunsigned short\tgflags;\n\tunsigned short\tcmethod;\n\tunsigned short\tlastmodfiletime;\n\tunsigned short\tlastmodfiledate;\n\tunsigned int\tcrc32;\n\tqofs_t\t\t\tcsize;\n\tqofs_t\t\t\tusize;\n\tunsigned short\tfname_len;\n\tunsigned short\textra_len;\n\tunsigned short\tcomment_len;\n\tunsigned int\tdisknum;\n\tunsigned short\tiattributes;\n\tunsigned int\teattributes;\n\tqofs_t\tlocalheaderoffset;\t//from start of disk\n};\n\nstruct ziplocalentry\n{\n      //PK34\n\tunsigned short\tversion_needed;\n\tunsigned short\tgpflags;\n\tunsigned short\tcmethod;\n\tunsigned short\tlastmodfiletime;\n\tunsigned short\tlastmodfiledate;\n\tunsigned int\tcrc32;\n\tqofs_t\t\t\tcsize;\n\tqofs_t\t\t\tusize;\n\tunsigned short\tfname_len;\n\tunsigned short\textra_len;\n};\n\n#define LittleU2FromPtr(p) (unsigned int)((p)[0] | ((p)[1]<<8u))\n#define LittleU4FromPtr(p) (unsigned int)((p)[0] | ((p)[1]<<8u) | ((p)[2]<<16u) | ((p)[3]<<24u))\n#define LittleU8FromPtr(p) qofs_Make(LittleU4FromPtr(p), LittleU4FromPtr((p)+4))\n\n#define SIZE_LOCALENTRY 30\n#define SIZE_ENDOFCENTRALDIRECTORY 22\n#define SIZE_ZIP64ENDOFCENTRALDIRECTORY_V1 56\n#define SIZE_ZIP64ENDOFCENTRALDIRECTORY_V2 84\n#define SIZE_ZIP64ENDOFCENTRALDIRECTORY SIZE_ZIP64ENDOFCENTRALDIRECTORY_V2\n#define SIZE_ZIP64ENDOFCENTRALDIRECTORYLOCATOR 20\n\n//check the local header and determine the place the data actually starts.\n//we don't do much checking, just validating a few fields that are copies of fields we tracked elsewhere anyway.\n//don't bother with the crc either\nstatic qboolean FSZIP_ValidateLocalHeader(zipfile_t *zip, zpackfile_t *zfile, qofs_t *datastart, qofs_t *datasize)\n{\n\tstruct ziplocalentry local;\n\tqbyte localdata[SIZE_LOCALENTRY];\n\tqofs_t localstart = zfile->localpos;\n\n\t\n\tif (!Sys_LockMutex(zip->mutex))\n\t\treturn false;\t//ohnoes\n\tVFS_SEEK(zip->raw, localstart);\n\tVFS_READ(zip->raw, localdata, sizeof(localdata));\n\tSys_UnlockMutex(zip->mutex);\n\n\t//make sure we found the right sort of table.\n\tif (localdata[0] != 'P' ||\n\t\tlocaldata[1] != 'K' ||\n\t\tlocaldata[2] != 3 ||\n\t\tlocaldata[3] != 4)\n\t\treturn false;\n\n\tlocal.version_needed = LittleU2FromPtr(localdata+4);\n\tlocal.gpflags = LittleU2FromPtr(localdata+6);\n\tlocal.cmethod = LittleU2FromPtr(localdata+8);\n\tlocal.lastmodfiletime = LittleU2FromPtr(localdata+10);\n\tlocal.lastmodfiledate = LittleU2FromPtr(localdata+12);\n\tlocal.crc32 = LittleU4FromPtr(localdata+14);\n\tlocal.csize = LittleU4FromPtr(localdata+18);\n\tlocal.usize = LittleU4FromPtr(localdata+22);\n\tlocal.fname_len = LittleU2FromPtr(localdata+26);\n\tlocal.extra_len = LittleU2FromPtr(localdata+28);\n\n\tlocalstart += SIZE_LOCALENTRY;\n\tlocalstart += local.fname_len;\n\n\t//parse extra\n\tif (local.usize == 0xffffffffu || local.csize == 0xffffffffu)\t//don't bother otherwise.\n\tif (local.extra_len)\n\t{\n\t\tqbyte extradata[65536];\n\t\tqbyte *extra = extradata;\n\t\tqbyte *extraend = extradata + local.extra_len;\n\t\tunsigned short extrachunk_tag;\n\t\tunsigned short extrachunk_len;\n\n\t\tif (!Sys_LockMutex(zip->mutex))\n\t\t\treturn false;\t//ohnoes\n\t\tVFS_SEEK(zip->raw, localstart);\n\t\tVFS_READ(zip->raw, extradata, sizeof(extradata));\n\t\tSys_UnlockMutex(zip->mutex);\n\n\n\t\twhile(extra+4 < extraend)\n\t\t{\n\t\t\textrachunk_tag = LittleU2FromPtr(extra+0);\n\t\t\textrachunk_len = LittleU2FromPtr(extra+2);\n\t\t\tif (extra + extrachunk_len > extraend)\n\t\t\t\tbreak;\t//error\n\t\t\textra += 4;\n\n\t\t\tswitch(extrachunk_tag)\n\t\t\t{\n\t\t\tcase 1:\t//zip64 extended information extra field. the attributes are only present if the reegular file info is nulled out with a -1\n\t\t\t\tif (local.usize == 0xffffffffu)\n\t\t\t\t{\n\t\t\t\t\tlocal.usize = LittleU8FromPtr(extra);\n\t\t\t\t\textra += 8;\n\t\t\t\t}\n\t\t\t\tif (local.csize == 0xffffffffu)\n\t\t\t\t{\n\t\t\t\t\tlocal.csize = LittleU8FromPtr(extra);\n\t\t\t\t\textra += 8;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tdefault:\n/*\t\t\t\tCon_Printf(\"Unknown chunk %x\\n\", extrachunk_tag);\n\t\t\tcase 0x000a:\t//NTFS (timestamps)\n\t\t\tcase 0x5455:\t//extended timestamp\n\t\t\tcase 0x7875:\t//unix uid/gid\n*/\t\t\t\textra += extrachunk_len;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\tlocalstart += local.extra_len;\n\t*datastart = localstart;\t//this is the end of the local block, and the start of the data block (well, actually, should be encryption, but we don't support that).\n\t*datasize = local.csize;\t//FIXME: this is often masked. use the central directory's value instead.\n\n\tif (local.gpflags & (1u<<3))\n\t{\n\t\t//crc, usize, and csize were not known at the time the file was compressed.\n\t\t//there is a 'data descriptor' after the file data, but to parse that we would need to decompress the file.\n\t\t//instead, just depend upon upon the central directory and don't bother checking.\n\t}\n\telse\n\t{\n\t\tif (local.crc32 != zfile->crc)\n\t\t\treturn false;\n\t\tif (local.usize != zfile->filelen)\n\t\t\treturn false;\n\t}\n\n\t//FIXME: with pure paths, we still don't bother checking the crc (again, would require decompressing the entire file in advance).\n\n\tif (localstart+local.csize > zip->rawsize)\n\t\treturn false;\t//FIXME: proper spanned zips fragment compressed data over multiple spans, but we don't support that\n\n\tif (local.cmethod == 0)\n\t\treturn (zfile->flags & ZFL_COMPRESSIONTYPE) == ZFL_STORED;\n#if defined(AVAIL_ZLIB)\n\tif (local.cmethod == 8)\n\t\treturn (zfile->flags & ZFL_COMPRESSIONTYPE) == ZFL_DEFLATED;\n#endif\n#if defined(ZLIB_DEFLATE64)\n\tif (local.cmethod == 9)\n\t\treturn (zfile->flags & ZFL_COMPRESSIONTYPE) == ZFL_DEFLATE64D;\n#endif\n#ifdef AVAIL_BZLIB\n\tif (local.cmethod == 12)\n\t\treturn (zfile->flags & ZFL_COMPRESSIONTYPE) == ZFL_BZIP2;\n#endif\n\treturn false;\t//some other method that we don't know.\n}\n\nstatic qboolean FSZIP_ReadCentralEntry(zipfile_t *zip, qbyte *data, struct zipcentralentry *entry)\n{\n\tstruct tm t;\n\tentry->flags = 0;\n\tentry->fname = \"\";\n\tentry->fname_len = 0;\n\n\tif (data[0] != 'P' ||\n\t\tdata[1] != 'K' ||\n\t\tdata[2] != 1 ||\n\t\tdata[3] != 2)\n\t\treturn false;\t//verify the signature\n\n\t//if we read too much, we'll catch it after.\n\tentry->version_madeby = LittleU2FromPtr(data+4);\n\tentry->version_needed = LittleU2FromPtr(data+6);\n\tentry->gflags = LittleU2FromPtr(data+8);\n\tentry->cmethod = LittleU2FromPtr(data+10);\n\tentry->lastmodfiletime = LittleU2FromPtr(data+12);\n\tentry->lastmodfiledate = LittleU2FromPtr(data+14);\n\tentry->crc32 = LittleU4FromPtr(data+16);\n\tentry->csize = LittleU4FromPtr(data+20);\n\tentry->usize = LittleU4FromPtr(data+24);\n\tentry->fname_len = LittleU2FromPtr(data+28);\n\tentry->extra_len = LittleU2FromPtr(data+30);\n\tentry->comment_len = LittleU2FromPtr(data+32);\n\tentry->disknum = LittleU2FromPtr(data+34);\n\tentry->iattributes = LittleU2FromPtr(data+36);\n\tentry->eattributes = LittleU4FromPtr(data+38);\n\tentry->localheaderoffset = LittleU4FromPtr(data+42);\n\tentry->cesize = 46;\n\n\t//mark the filename position\n\tentry->fname = data+entry->cesize;\n\tentry->cesize += entry->fname_len;\n\n\tmemset(&t, 0, sizeof(t));\n\tt.tm_mday =  (entry->lastmodfiledate&0x001f)>>0;\n\tt.tm_mon  =  (entry->lastmodfiledate&0x01e0)>>5;\n\tt.tm_year = ((entry->lastmodfiledate&0xfe00)>>9) + (1980 - 1900);\n\tt.tm_sec  = ((entry->lastmodfiletime&0x001f)>>0)*2;\n\tt.tm_min  =  (entry->lastmodfiletime&0x07e0)>>5;\n\tt.tm_hour =  (entry->lastmodfiletime&0xf800)>>11;\n\tentry->mtime = mktime(&t);\n\n\t//parse extra\n\tif (entry->extra_len)\n\t{\n\t\tqbyte *extra = data + entry->cesize;\n\t\tqbyte *extraend = extra + entry->extra_len;\n\t\tunsigned short extrachunk_tag;\n\t\tunsigned short extrachunk_len;\n\t\twhile(extra+4 < extraend)\n\t\t{\n\t\t\textrachunk_tag = LittleU2FromPtr(extra+0);\n\t\t\textrachunk_len = LittleU2FromPtr(extra+2);\n\t\t\tif (extra + extrachunk_len > extraend)\n\t\t\t\tbreak;\t//error\n\t\t\textra += 4;\n\n\t\t\tswitch(extrachunk_tag)\n\t\t\t{\n\t\t\tcase 1:\t//zip64 extended information extra field. the attributes are only present if the reegular file info is nulled out with a -1\n\t\t\t\tif (entry->usize == 0xffffffffu)\n\t\t\t\t{\n\t\t\t\t\tentry->usize = LittleU8FromPtr(extra);\n\t\t\t\t\textra += 8;\n\t\t\t\t}\n\t\t\t\tif (entry->csize == 0xffffffffu)\n\t\t\t\t{\n\t\t\t\t\tentry->csize = LittleU8FromPtr(extra);\n\t\t\t\t\textra += 8;\n\t\t\t\t}\n\t\t\t\tif (entry->localheaderoffset == 0xffffffffu)\n\t\t\t\t{\n\t\t\t\t\tentry->localheaderoffset = LittleU8FromPtr(extra);\n\t\t\t\t\textra += 8;\n\t\t\t\t}\n\t\t\t\tif (entry->disknum == 0xffffu)\n\t\t\t\t{\n\t\t\t\t\tentry->disknum = LittleU4FromPtr(extra);\n\t\t\t\t\textra += 4;\n\t\t\t\t}\n\t\t\t\tbreak;\n#if !defined(_MSC_VER) || _MSC_VER > 1200\n\t\t\tcase 0x000a:\t//NTFS extra field\n\t\t\t\t//0+4: reserved\n\t\t\t\t//4+2: subtag(must be 1, for times)\n\t\t\t\t//6+2: subtagsize(times: must be == 8*3+4)\n\t\t\t\t//8+8: mtime\n\t\t\t\t//16+8: atime\n\t\t\t\t//24+8: ctime\n\t\t\t\tif (extrachunk_len >= 32 && LittleU2FromPtr(extra+4) == 1 && LittleU2FromPtr(extra+6) == 8*3)\t\n\t\t\t\t\tentry->mtime = LittleU8FromPtr(extra+8) / 10000000ULL - 11644473600ULL;\n\t\t\t\telse\n\t\t\t\t\tCon_Printf(\"zip: unsupported ntfs subchunk %x\\n\", extrachunk_tag);\n\t\t\t\textra += extrachunk_len;\n\t\t\t\tbreak;\n#endif\n\t\t\tcase 0x5455:\n\t\t\t\tif (extra[0] & 1)\n\t\t\t\t\tentry->mtime = LittleU4FromPtr(extra+1);\n\t\t\t\t//access and creation do NOT exist in the central header.\n\t\t\t\textra += extrachunk_len;\n\t\t\t\tbreak;\n\t\t\tcase 0x7075:\t//unicode (utf-8) filename replacements.\n\t\t\t\tif (extra[0] == 1)\t//version\n\t\t\t\t{\n\t\t\t\t\t//if (LittleU4FromPtr(extra+1) == qcrc32(?,entry->fname, entry->fnane_len))\n\t\t\t\t\t{\n\t\t\t\t\t\tentry->fname = extra+5;\n\t\t\t\t\t\tentry->fname_len = extrachunk_len-5;\n\t\t\t\t\t\textra += extrachunk_len;\n\n\t\t\t\t\t\tentry->gflags |= (1u<<11);\t//just set that flag. we don't support comments anyway.\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tdefault:\n/*\t\t\t\tCon_Printf(\"Unknown chunk %x\\n\", extrachunk_tag);\n\t\t\tcase 0x5455:\t//extended timestamp\n\t\t\tcase 0x7875:\t//unix uid/gid\n\t\t\tcase 0x9901:\t//aes crypto\n*/\t\t\t\textra += extrachunk_len;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tentry->cesize += entry->extra_len;\n\t}\n\n\t//parse comment\n\tentry->cesize += entry->comment_len;\n\n\t//check symlink flags\n\t{\n\t\tqbyte madeby = entry->version_madeby>>8;\t//system\n\t\t//vms, unix, or beos file attributes includes a symlink attribute.\n\t\t//symlinks mean the file contents is just the name of another file.\n\t\tif (madeby == 2 || madeby == 3 || madeby == 16)\n\t\t{\n\t\t\tunsigned short unixattr = entry->eattributes>>16;\n\t\t\tif ((unixattr & 0xF000) == 0xA000)//fa&S_IFMT==S_IFLNK \n\t\t\t\tentry->flags |= ZFL_SYMLINK;\n\t\t\telse if ((unixattr & 0xA000) == 0xA000)//fa&S_IFMT==S_IFLNK \n\t\t\t\tentry->flags |= ZFL_SYMLINK;\n\t\t}\n\t}\n\n\tif (entry->gflags & (1u<<0))\t//encrypted\n\t{\n#ifdef ZIPCRYPT\n\t\tentry->flags |= ZFL_WEAKENCRYPT;\n#else\n\t\tentry->flags |= ZFL_CORRUPT;\n#endif\n\t}\n\n\n\tif (entry->gflags & (1u<<5))\t//is patch data\n\t\tentry->flags |= ZFL_CORRUPT;\n\telse if (entry->gflags & (1u<<6))\t//strong encryption\n\t\tentry->flags |= ZFL_CORRUPT;\n\telse if (entry->gflags & (1u<<13))\t//strong encryption\n\t\tentry->flags |= ZFL_CORRUPT;\n\telse if (entry->cmethod == 0)\n\t\tentry->flags |= ZFL_STORED;\n\t//1: shrink\n\t//2-5: reduce\n\t//6: implode\n\t//7: tokenize\n\telse if (entry->cmethod == 8)\t//8: deflate\n\t\tentry->flags |= ZFL_DEFLATED;\n\telse if (entry->cmethod == 9)\t//9: deflate64 - patented. sometimes written by microsoft's crap, so this might be problematic. only minor improvements.\n\t\tentry->flags |= ZFL_DEFLATE64D;\n\t//10: implode\n\telse if (entry->cmethod == 12)\t//12: bzip2\n\t\tentry->flags |= ZFL_BZIP2;\n//\telse if (entry->cmethod == 14)\n//\t\tentry->flags |= ZFL_LZMA;\n\t//19: lz77\n\t//97: wavpack\n\t//98: ppmd\n\telse \n\t\tentry->flags |= ZFL_CORRUPT;\t//unsupported compression method.\n\n\tif ((entry->flags & ZFL_WEAKENCRYPT) && !(entry->flags & ZFL_DEFLATED))\n\t\tentry->flags |= ZFL_CORRUPT;\t//only support decryption with deflate.\n\treturn true;\n}\n\n//for compatibility with windows, this is an IBM437 -> unicode translation (ucs-2 is sufficient here)\n//the zip codepage is defined as either IBM437(aka: dos) or UTF-8. Systems that use a different codepage are buggy.\n//this table is for filenames, so lets just fix up windows problems here.\nunsigned short ibmtounicode[256] =\n{\n\t0x0000,0x263A,0x263B,0x2665,0x2666,0x2663,0x2660,0x2022,0x25D8,0x25CB,0x25D9,0x2642,0x2640,0x266A,0x266B,0x263C,\t//0x00(non-ascii, display-only)\n\t0x25BA,0x25C4,0x2195,0x203C,0x00B6,0x00A7,0x25AC,0x21A8,0x2191,0x2193,0x2192,0x2190,0x221F,0x2194,0x25B2,0x25BC,\t//0x10(non-ascii, display-only)\n\t0x0020,0x0021,0x0022,0x0023,0x0024,0x0025,0x0026,0x0027,0x0028,0x0029,0x002A,0x002B,0x002C,0x002D,0x002E,0x002F,\t//0x20(ascii)\n\t0x0030,0x0031,0x0032,0x0033,0x0034,0x0035,0x0036,0x0037,0x0038,0x0039,0x003A,0x003B,0x003C,0x003D,0x003E,0x003F,\t//0x30(ascii)\n\t0x0040,0x0041,0x0042,0x0043,0x0044,0x0045,0x0046,0x0047,0x0048,0x0049,0x004A,0x004B,0x004C,0x004D,0x004E,0x004F,\t//0x40(ascii)\n\t0x0050,0x0051,0x0052,0x0053,0x0054,0x0055,0x0056,0x0057,0x0058,0x0059,0x005A,0x005B,   '/',0x005D,0x005E,0x005F,\t//0x50(ascii)\n\t0x0060,0x0061,0x0062,0x0063,0x0064,0x0065,0x0066,0x0067,0x0068,0x0069,0x006A,0x006B,0x006C,0x006D,0x006E,0x006F,\t//0x60(ascii)\n\t0x0070,0x0071,0x0072,0x0073,0x0074,0x0075,0x0076,0x0077,0x0078,0x0079,0x007A,0x007B,0x007C,0x007D,0x007E,0x2302,\t//0x70(mostly ascii, one display-only)\n\t0x00C7,0x00FC,0x00E9,0x00E2,0x00E4,0x00E0,0x00E5,0x00E7,0x00EA,0x00EB,0x00E8,0x00EF,0x00EE,0x00EC,0x00C4,0x00C5,\t//0x80(non-ascii, printable)\n\t0x00C9,0x00E6,0x00C6,0x00F4,0x00F6,0x00F2,0x00FB,0x00F9,0x00FF,0x00D6,0x00DC,0x00A2,0x00A3,0x00A5,0x20A7,0x0192,\t//0x90(non-ascii, printable)\n\t0x00E1,0x00ED,0x00F3,0x00FA,0x00F1,0x00D1,0x00AA,0x00BA,0x00BF,0x2310,0x00AC,0x00BD,0x00BC,0x00A1,0x00AB,0x00BB,\t//0xa0(non-ascii, printable)\n\t0x2591,0x2592,0x2593,0x2502,0x2524,0x2561,0x2562,0x2556,0x2555,0x2563,0x2551,0x2557,0x255D,0x255C,0x255B,0x2510,\t//0xb0(box-drawing, printable)\n\t0x2514,0x2534,0x252C,0x251C,0x2500,0x253C,0x255E,0x255F,0x255A,0x2554,0x2569,0x2566,0x2560,0x2550,0x256C,0x2567,\t//0xc0(box-drawing, printable)\n\t0x2568,0x2564,0x2565,0x2559,0x2558,0x2552,0x2553,0x256B,0x256A,0x2518,0x250C,0x2588,0x2584,0x258C,0x2590,0x2580,\t//0xd0(box-drawing, printable)\n\t0x03B1,0x00DF,0x0393,0x03C0,0x03A3,0x03C3,0x00B5,0x03C4,0x03A6,0x0398,0x03A9,0x03B4,0x221E,0x03C6,0x03B5,0x2229,\t//0xe0(maths(greek), printable)\n\t0x2261,0x00B1,0x2265,0x2264,0x2320,0x2321,0x00F7,0x2248,0x00B0,0x2219,0x00B7,0x221A,0x207F,0x00B2,0x25A0,0x00A0,\t//0xf0(maths, printable)\n};\n\nstatic qboolean FSZIP_EnumerateCentralDirectory(zipfile_t *zip, struct zipinfo *info, const char *prefix)\n{\n\tqboolean success = false;\n\tzpackfile_t\t\t*f;\n\tstruct zipcentralentry entry;\n\tqofs_t ofs = 0;\n\tunsigned int i;\n\t//lazily read the entire thing.\n\tqbyte *centraldir = BZ_Malloc(info->centraldir_size);\n\tif (centraldir)\n\t{\n\t\tVFS_SEEK(zip->raw, info->centraldir_offset+info->zipoffset);\n\t\tif (VFS_READ(zip->raw, centraldir, info->centraldir_size) == info->centraldir_size)\n\t\t{\n\t\t\tzip->numfiles = info->centraldir_numfiles_disk;\n\t\t\tzip->files = f = Z_Malloc (zip->numfiles * sizeof(zpackfile_t));\n\n\t\t\tfor (i = 0; i < zip->numfiles; i++)\n\t\t\t{\n\t\t\t\tif (!FSZIP_ReadCentralEntry(zip, centraldir+ofs, &entry) || ofs + entry.cesize > info->centraldir_size)\n\t\t\t\t\tbreak;\n\n\t\t\t\tf->disknum = entry.disknum;\n\t\t\t\tf->crc = entry.crc32;\n\n\t\t\t\t//copy out the filename and lowercase it\n\t\t\t\tif (entry.gflags & (1u<<11))\n\t\t\t\t{\t//already utf-8 encoding, still need to work around windows though.\n\t\t\t\t\tif (entry.fname_len > sizeof(f->name)-1)\n\t\t\t\t\t\tentry.fname_len = sizeof(f->name)-1;\n\t\t\t\t\tfor (i = 0; i < entry.fname_len; i++)\n\t\t\t\t\t\tf->name[i] = (entry.fname[i]=='\\\\')?'/':entry.fname[i];\n\t\t\t\t\tmemcpy(f->name, entry.fname, entry.fname_len);\n\t\t\t\t\tf->name[entry.fname_len] = 0;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\t//legacy charset\n\t\t\t\t\tint i;\n\t\t\t\t\tint nlen = 0;\n\t\t\t\t\tint cl;\n\t\t\t\t\tfor (i = 0; i < entry.fname_len; i++)\n\t\t\t\t\t{\n\t\t\t\t\t\tcl = utf8_encode(f->name+nlen, ibmtounicode[entry.fname[i]], sizeof(f->name)-1 - nlen);\n\t\t\t\t\t\tif (!cl)\t//overflowed, truncate cleanly.\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tnlen += cl;\n\t\t\t\t\t}\n\t\t\t\t\tf->name[nlen] = 0;\n\t\t\t\t}\n\t\t\t\tofs += entry.cesize;\n\n\t\t\t\tif (prefix && *prefix)\n\t\t\t\t{\n\t\t\t\t\tif (!strcmp(prefix, \"..\"))\n\t\t\t\t\t{\t//strip leading directories blindly\n\t\t\t\t\t\tchar *c; \n\t\t\t\t\t\tfor (c = f->name; *c; )\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (*c++ == '/')\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tmemmove(f->name, c, strlen(c)+1);\n\t\t\t\t\t}\n\t\t\t\t\telse if (*prefix == '/')\n\t\t\t\t\t{\t//move any files with the specified prefix to the root\n\t\t\t\t\t\tsize_t prelen = strlen(prefix+1);\n\t\t\t\t\t\tif (!strncmp(prefix+1, f->name, prelen))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (f->name[prelen] == '/')\n\t\t\t\t\t\t\t\tprelen++;\n\t\t\t\t\t\t\tmemmove(f->name, f->name+prelen, strlen(f->name+prelen)+1);\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\t//add the specified text to the start of each file name (handy for 'maps')\n\t\t\t\t\t\tsize_t prelen = strlen(prefix);\n\t\t\t\t\t\tsize_t oldlen = strlen(f->name);\n\t\t\t\t\t\tif (prelen+1+oldlen+1 > sizeof(f->name))\n\t\t\t\t\t\t\t*f->name = 0;\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tmemmove(f->name+prelen+1, f->name, oldlen);\n\t\t\t\t\t\t\tf->name[prelen] = '/';\n\t\t\t\t\t\t\tmemmove(f->name, prefix, prelen);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tQ_strlwr(f->name);\n\n\t\t\t\tf->filelen = *f->name?entry.usize:0;\n\t\t\t\tf->localpos = entry.localheaderoffset+info->zipoffset;\n\t\t\t\tf->flags = entry.flags;\n\t\t\t\tf->mtime = entry.mtime;\n\n\t\t\t\tf++;\n\t\t\t}\n\n\t\t\tsuccess = i == zip->numfiles;\n\t\t\tif (!success)\n\t\t\t{\n\t\t\t\tZ_Free(zip->files);\n\t\t\t\tzip->files = NULL;\n\t\t\t\tzip->numfiles = 0;\n\t\t\t}\n\t\t\tzip->numfiles = f - zip->files;\n\t\t}\n\t}\n\n\tBZ_Free(centraldir);\n\treturn success;\n}\n\nstatic qboolean FSZIP_FindEndCentralDirectory(zipfile_t *zip, struct zipinfo *info)\n{\n\tqboolean result = false;\n\t//zip comment is capped to 65k or so, so we can use a single buffer for this\n\tqbyte traildata[0x10000 + SIZE_ENDOFCENTRALDIRECTORY+SIZE_ZIP64ENDOFCENTRALDIRECTORYLOCATOR];\n\tqbyte *magic;\n\tunsigned int trailsize = 0x10000 + SIZE_ENDOFCENTRALDIRECTORY+SIZE_ZIP64ENDOFCENTRALDIRECTORYLOCATOR;\n\tif (trailsize > zip->rawsize)\n\t\ttrailsize = zip->rawsize;\n\t//FIXME: do in a loop to avoid a huge block of stack use\n\tVFS_SEEK(zip->raw, zip->rawsize - trailsize);\n\tVFS_READ(zip->raw, traildata, trailsize);\n\n\tmemset(info, 0, sizeof(*info));\n\n\tfor (magic = traildata+trailsize-SIZE_ENDOFCENTRALDIRECTORY; magic >= traildata; magic--)\n\t{\n\t\tif (magic[0] == 'P' && \n\t\t\tmagic[1] == 'K' && \n\t\t\tmagic[2] == 5 && \n\t\t\tmagic[3] == 6)\n\t\t{\n\t\t\tinfo->centraldir_end = (zip->rawsize-trailsize)+(magic-traildata);\n\n\t\t\tinfo->thisdisk\t\t\t\t\t= LittleU2FromPtr(magic+4);\n\t\t\tinfo->centraldir_startdisk\t\t= LittleU2FromPtr(magic+6);\n\t\t\tinfo->centraldir_numfiles_disk\t= LittleU2FromPtr(magic+8);\n\t\t\tinfo->centraldir_numfiles_all\t= LittleU2FromPtr(magic+10);\n\t\t\tinfo->centraldir_size\t\t\t= LittleU4FromPtr(magic+12);\n\t\t\tinfo->centraldir_offset\t\t\t= LittleU4FromPtr(magic+16);\n\t\t\tinfo->commentlength\t\t\t\t= LittleU2FromPtr(magic+20);\n\n\t\t\tinfo->diskcount = info->thisdisk+1;\t//zips are normally opened via the last file.\n\n\t\t\tresult = true;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tif (!result)\n\t\tCon_Printf(\"%s: unable to find end-of-central-directory (not a zip?)\\n\", zip->filename);\t//usually just not a zip.\n\telse\n\n\t//now look for a zip64 header.\n\t//this gives more larger archives, more files, larger files, more spanned disks.\n\t//note that the central directory itself is the same, it just has a couple of extra attributes on files that need them.\n\tfor (magic -= SIZE_ZIP64ENDOFCENTRALDIRECTORYLOCATOR; magic >= traildata; magic--)\n\t{\n\t\tif (magic[0] == 'P' && \n\t\t\tmagic[1] == 'K' && \n\t\t\tmagic[2] == 6 && \n\t\t\tmagic[3] == 7)\n\t\t{\n\t\t\tqbyte z64eocd[SIZE_ZIP64ENDOFCENTRALDIRECTORY];\n\n\t\t\tinfo->zip64_centraldirend_disk\t\t= LittleU4FromPtr(magic+4);\n\t\t\tinfo->zip64_centraldirend_offset\t= LittleU8FromPtr(magic+8);\n\t\t\tinfo->diskcount\t\t\t\t\t\t= LittleU4FromPtr(magic+16);\n\n\t\t\tVFS_SEEK(zip->raw, info->zip64_centraldirend_offset);\n\t\t\tVFS_READ(zip->raw, z64eocd, sizeof(z64eocd));\n\n\t\t\tif (z64eocd[0] == 'P' &&\n\t\t\t\tz64eocd[1] == 'K' &&\n\t\t\t\tz64eocd[2] == 6 &&\n\t\t\t\tz64eocd[3] == 6)\n\t\t\t{\n\t\t\t\tinfo->zip64_eocdsize\t\t\t\t\t\t= LittleU8FromPtr(z64eocd+4) + 12;\n\t\t\t\tinfo->zip64_version_madeby\t\t\t\t\t= LittleU2FromPtr(z64eocd+12);\n\t\t\t\tinfo->zip64_version_needed       \t\t\t= LittleU2FromPtr(z64eocd+14);\n\t\t\t\tinfo->thisdisk             \t\t\t\t\t= LittleU4FromPtr(z64eocd+16);\n\t\t\t\tinfo->centraldir_startdisk\t\t\t\t\t= LittleU4FromPtr(z64eocd+20);\n\t\t\t\tinfo->centraldir_numfiles_disk  \t\t\t= LittleU8FromPtr(z64eocd+24);\n\t\t\t\tinfo->centraldir_numfiles_all\t\t\t\t= LittleU8FromPtr(z64eocd+32);\n\t\t\t\tinfo->centraldir_size   \t\t\t\t\t= LittleU8FromPtr(z64eocd+40);\n\t\t\t\tinfo->centraldir_offset\t\t\t\t\t\t= LittleU8FromPtr(z64eocd+48);\n\n\t\t\t\tif (info->zip64_eocdsize >= 84)\n\t\t\t\t{\n\t\t\t\t\tinfo->centraldir_compressionmethod\t\t= LittleU2FromPtr(z64eocd+56);\n//\t\t\t\t\tinfo->zip64_2_centraldir_csize\t\t\t= LittleU8FromPtr(z64eocd+58);\n//\t\t\t\t\tinfo->zip64_2_centraldir_usize\t\t\t= LittleU8FromPtr(z64eocd+66);\n\t\t\t\t\tinfo->centraldir_algid\t\t\t\t\t= LittleU2FromPtr(z64eocd+74);\n//\t\t\t\t\tinfo.zip64_2_bitlen\t\t\t\t\t\t= LittleU2FromPtr(z64eocd+76);\n//\t\t\t\t\tinfo->zip64_2_flags\t\t\t\t\t\t= LittleU2FromPtr(z64eocd+78);\n//\t\t\t\t\tinfo->zip64_2_hashid\t\t\t\t\t= LittleU2FromPtr(z64eocd+80);\n//\t\t\t\t\tinfo->zip64_2_hashlength\t\t\t\t= LittleU2FromPtr(z64eocd+82);\n\t\t\t\t\t//info->zip64_2_hashdata\t\t\t\t= LittleUXFromPtr(z64eocd+84, info->zip64_2_hashlength);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tCon_Printf(\"%s: zip64 end-of-central directory at unknown offset.\\n\", zip->filename);\n\t\t\t\tresult = false;\n\t\t\t}\n\n\t\t\tif (info->diskcount < 1 || info->zip64_centraldirend_disk != info->thisdisk)\n\t\t\t{\t//must read the segment with the central directory.\n\t\t\t\tCon_Printf(\"%s: archive is spanned\\n\", zip->filename);\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tif (info->thisdisk != info->centraldir_startdisk || info->centraldir_numfiles_disk != info->centraldir_numfiles_all)\n\t{\t//must read the segment with the central directory.\n\t\tCon_Printf(\"%s: archive is spanned\\n\", zip->filename);\n\t\tresult = false;\n\t}\n\tif (info->centraldir_compressionmethod || info->centraldir_algid)\n\t{\n\t\tCon_Printf(\"%s: encrypted centraldir\\n\", zip->filename);\n\t\tresult = false;\n\t}\n\n\treturn result;\n}\n\n/*\n=================\nCOM_LoadZipFile\n\nTakes an explicit (not game tree related) path to a pak file.\n\nLoads the header and directory, adding the files at the beginning\nof the list so they override previous pack files.\n=================\n*/\nsearchpathfuncs_t *QDECL FSZIP_LoadArchive (vfsfile_t *packhandle, searchpathfuncs_t *parent, const char *filename, const char *desc, const char *prefix)\n{\n\tzipfile_t *zip;\n\tstruct zipinfo info;\n\n\tif (!packhandle)\n\t\treturn NULL;\n\n\tzip = Z_Malloc(sizeof(zipfile_t));\n\tQ_strncpyz(zip->filename, desc, sizeof(zip->filename));\n\tzip->raw = packhandle;\n\tzip->rawsize = VFS_GETLEN(zip->raw);\n\n\t//find the footer\n\tif (!FSZIP_FindEndCentralDirectory(zip, &info))\n\t{\n\t\tZ_Free(zip);\n\t\tCon_TPrintf (\"Failed opening zipfile \\\"%s\\\" corrupt?\\n\", desc);\n\t\treturn NULL;\n\t}\n\n\t//find the central directory.\n\tif (!FSZIP_FindEndCentralDirectory(zip, &info))\n\t{\n\t\tZ_Free(zip);\n\t\tCon_TPrintf (\"Failed opening zipfile \\\"%s\\\" corrupt?\\n\", desc);\n\t\treturn NULL;\n\t}\n\n\t//now read it.\n\tif (!FSZIP_EnumerateCentralDirectory(zip, &info, prefix))\n\t{\n\t\t//uh oh... the central directory wasn't where it was meant to be!\n\t\t//assuming that the endofcentraldir is packed at the true end of the centraldir (and that we're not zip64 and thus don't have an extra block), then we can guess based upon the offset difference\n\t\tinfo.zipoffset = info.centraldir_end - (info.centraldir_offset+info.centraldir_size);\n\t\tif (!FSZIP_EnumerateCentralDirectory(zip, &info, prefix))\n\t\t{\n\t\t\tZ_Free(zip);\n\t\t\tCon_TPrintf (\"zipfile \\\"%s\\\" appears to be missing its central directory\\n\", desc);\n\t\t\treturn NULL;\n\t\t}\n\t}\n\n\tzip->thisdisk = info.thisdisk;\n\tif (info.diskcount > 1)\n\t{\n\t\tif (parent && filename)\n\t\t{\n\t\t\tzipfile_t *szip;\n\t\t\tunsigned int s;\n\t\t\tflocation_t loc;\n\t\t\tchar splitname[MAX_OSPATH];\n\t\t\tchar *ext;\n\t\t\tzip->numspans = info.diskcount;\n\t\t\tzip->spans = Z_Malloc(info.diskcount*sizeof(*zip->spans));\n\t\t\tfor(s = 0; s < zip->numspans; s++)\n\t\t\t{\n\t\t\t\tif (info.thisdisk == s)\n\t\t\t\t\tcontinue;\t//would be weird.\n\n\t\t\t\tQ_strncpyz(splitname, filename, sizeof(splitname));\n\t\t\t\text = strrchr(splitname, '.');\n\t\t\t\tif (ext)\n\t\t\t\t{\n\t\t\t\t\text++;\n\t\t\t\t\tif (*ext)\n\t\t\t\t\t\text++;\t//skip the first letter of the extension\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\text = splitname + strlen(splitname);\n\t\t\t\tQ_snprintfz(ext, sizeof(splitname)-(ext-splitname), *ext?\"%02u\":\"%03u\", s+1);\n\t\t\t\tif (parent->FindFile(parent, &loc, splitname, NULL) != FF_FOUND)\n\t\t\t\t\tcontinue;\n\t\t\t\tpackhandle = parent->OpenVFS(parent, &loc, \"rb\");\n\t\t\t\tif (!packhandle)\n\t\t\t\t\tcontinue;\n\n\t\t\t\tzip->spans[s] = szip = Z_Malloc(sizeof(zipfile_t));\n\t\t\t\tszip->thisdisk = s;\n\t\t\t\tQ_strncpyz(szip->filename, splitname, sizeof(szip->filename));\n\t\t\t\tszip->raw = packhandle;\n\t\t\t\tszip->rawsize = VFS_GETLEN(szip->raw);\n\t\t\t\tszip->references = 1;\n\t\t\t\tszip->mutex = Sys_CreateMutex();\n\t\t\t\tszip->pub.ClosePath\t\t\t= FSZIP_ClosePath;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t\tCon_TPrintf (\"spanned zip \\\"%s\\\" with no path info\\n\", desc);\n\t}\n\n\tzip->references = 1;\n\tzip->mutex = Sys_CreateMutex();\n\n//\tCon_TPrintf (\"Added zipfile %s (%i files)\\n\", desc, zip->numfiles);\n\n\tzip->pub.fsver\t\t\t\t= FSVER;\n\tzip->pub.GetPathDetails\t\t= FSZIP_GetPathDetails;\n\tzip->pub.ClosePath\t\t\t= FSZIP_ClosePath;\n\tzip->pub.AddReference\t\t= FSZIP_UnclosePath;\n\tzip->pub.BuildHash\t\t\t= FSZIP_BuildHash;\n\tzip->pub.FindFile\t\t\t= FSZIP_FLocate;\n\tzip->pub.ReadFile\t\t\t= FSZIP_ReadFile;\n\tzip->pub.EnumerateFiles\t\t= FSZIP_EnumerateFiles;\n\tzip->pub.FileStat\t\t\t= FSZIP_FileStat;\n\tzip->pub.GeneratePureCRC\t= FSZIP_GeneratePureCRC;\n\tzip->pub.OpenVFS\t\t\t= FSZIP_OpenVFS;\n\treturn &zip->pub;\n}\n\n\n\n\n\n#if 0\n//our download protocol permits requesting various different parts of the file.\n//this means that its theoretically possible to download sections pk3s such that we can skip blocks that contain files that we already have.\n\ntypedef struct\n{\n\tstruct archivedeltafuncs_s\n\t{\n\t\t//called when the downloader needs to know a new target region. each call should return a new region, eventually returning the entire file as data is injected from elsewhere\n\t\t//return false on error.\n\t\tqboolean (*GetNextRange) (struct archivedeltafuncs_s *archive, qofs_t *start, qofs_t *end);\n\n\t\t//called when the download completes/aborts. frees memory.\n\t\tvoid (*Finish) (struct archivedeltafuncs_s);\n\t} pub;\n\n\tqdownload_t *dl;\n\n\tenum\n\t{\n\t\tstep_eocd,\n\t\tstep_cd,\n\t\tstep_file\n\t} step;\n\n\tstruct\n\t{\n\t\tqofs_t start;\n\t\tqofs_t end;\n\t} eocd;\n\n\tzipfile_t *old;\n\n\tzipfile_t zip;\n\tstruct zipinfo info;\n} ziparchivedelta_t;\n\nvoid FSZIPDL_Finish(struct archivedeltafuncs_s *archive)\n{\n\tziparchivedelta_t *za = (ziparchivedelta_t*)archive;\n\n\tza->old->pub.ClosePath(&za->old->pub);\n\n\tif (za->zip.files)\n\t\tZ_Free(za->zip.files);\n\n\tZ_Free(za);\n}\nqboolean FSZIPDL_GetNextRange(struct archivedeltafuncs_s *archive, qofs_t *start, qofs_t *end)\n{\n\tflocation_t loc;\n\tint i;\n\tziparchivedelta_t *za = (ziparchivedelta_t*)archive;\n\tswitch(za->step)\n\t{\n\tcase step_eocd:\n\t\t//find the header (actually, the trailer)\n\t\t*start = za->eocd.start;\n\t\t*end = za->eocd.end;\n\t\tza->step++;\n\t\tbreak;\n\tcase step_cd:\n\t\tif (!FSZIP_FindEndCentralDirectory(&za->zip, &za->info))\n\t\t\treturn false;\t//corrupt/unusable.\n\n\t\t//central directory should be located at this position\n\t\t*start = za->info.centraldir_offset+za->info.zipoffset;\n\t\t*end = za->info.centraldir_end+za->info.zipoffset;\n\t\tza->step++;\n\t\tbreak;\n\tcase step_file:\n\t\tif (FSZIP_EnumerateCentralDirectory(&za->zip, &za->info))\n\t\t{\n\t\t\treturn false;\t//couldn't find the central directory properly (fixme: this can happen with zip32 self-extractors)\n\t\t}\n\n\t\t//walk through the central directory looking for matching files in the existing versions of the zip\n\t\tfor (i = 0; i < za->zip.numfiles; i++)\n\t\t\tif (FSZIP_FLocate(&za->old->pub, &loc, za->zip.files[i].name, NULL) != FF_NOTFOUND)\n\t\t\t{\n\t\t\t\tif (za->old->files[loc.index].crc == za->zip.files[i].crc)\n\t\t\t\t{\n\t\t\t\t\tqofs_t datastart, datasize;\n\t\t\t\t\tif (FSZIP_ValidateLocalHeader(za->old, &za->old->files[loc.index], &datastart, &datasize))\n\t\t\t\t\t{\n\t\t\t\t\t\tsize_t chunk;\n\t\t\t\t\t\tdatastart = za->old->files[loc.index].localpos;\n\n\t\t\t\t\t\t//tell the download context that we already know this data region\n\t\t\t\t\t\tDL_Completed(za->dl, datastart, datastart + datasize);\n\t\t\t\t\t\t//and inject the data into the downloaded file.\n\t\t\t\t\t\tVFS_SEEK(za->old->raw, datastart);\n\t\t\t\t\t\tVFS_SEEK(za->dl->file, za->zip.files[i].localpos);\n\t\t\t\t\t\tfor(;datasize;)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tchar copybuffer[0x10000];\n\t\t\t\t\t\t\tchunk = datasize;\n\t\t\t\t\t\t\tif (chunk > sizeof(copybuffer))\n\t\t\t\t\t\t\t\tchunk = sizeof(copybuffer);\n\n\t\t\t\t\t\t\tVFS_READ(za->old->raw, copybuffer, chunk);\n\t\t\t\t\t\t\tdatasize -= chunk;\n\t\t\t\t\t\t}\n\t\t\t\t\t\t//FIXME: graphics/progress updates.\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t//anything we didn't receive yet needs to be completed. the download code is meant to track the holes.\n\t\t*start = 0;\n\t\t*end = za->eocd.end;\n\t\tza->step++;\n\t\tbreak;\n\tdefault:\n\t\treturn false;\n\t}\n\treturn true;\n}\nstruct archivedeltafuncs_s *FSZIP_OpenDeltaZip(qdownload_t *dl)\n{\n\tziparchivedelta_t *za;\n\tunsigned int trailsize = 0x10000 + SIZE_ENDOFCENTRALDIRECTORY+SIZE_ZIP64ENDOFCENTRALDIRECTORYLOCATOR;\n\tza->eocd.end = dl->size;\n\tif (dl->size < trailsize)\n\t\tza->eocd.start = 0;\n\telse\n\t\tza->eocd.start = dl->size - trailsize;\n\n\tza->dl = dl;\n\tza->zip.rawsize = dl->size;\n\tza->zip.raw = dl->file;\n\tza->pub.GetNextRange = FSZIPDL_GetNextRange;\n\treturn &za->pub;\n}\n#endif\n#endif\n\n"
  },
  {
    "path": "engine/common/gl_q2bsp.c",
    "content": "#include \"quakedef.h\"\n#ifndef SERVERONLY\n#include \"glquake.h\"\n#endif\n#include \"com_mesh.h\"\n#include \"com_bih.h\"\n\n#define MAX_Q3MAP_INDICES 0x8000000\t//just a sanity limit\n#define\tMAX_Q3MAP_VERTEXES\t0x800000\t//just a sanity limit\n//#define MAX_CM_PATCH_VERTS\t\t(4096)\n//#define MAX_CM_FACES\t\t\t(MAX_Q2MAP_FACES)\n#ifdef FTE_TARGET_WEB\n#define MAX_CM_PATCHES\t\t\t(0x1000)\t\t//fixme\n#else\n#define MAX_CM_PATCHES\t\t\t(0x10000)\t\t//fixme\n#endif\n//#define MAX_CM_LEAFFACES\t\t(MAX_Q2MAP_LEAFFACES)\n#define\tMAX_CM_AREAS\t\t\tMAX_Q2MAP_AREAS\n\n//#define Q3SURF_NODAMAGE\t\t0x00000001\n//#define Q3SURF_SLICK\t\t\t0x00000002\n//#define Q3SURF_SKY\t\t\t0x00000004\n//#define Q3SURF_LADDER\t\t\t0x00000008\n//#define Q3SURF_NOIMPACT\t\t0x00000010\n//#define Q3SURF_NOMARKS\t\t0x00000020\n//#define Q3SURF_FLESH\t\t\t0x00000040\n#define\tQ3SURF_NODRAW\t\t\t0x00000080\t// don't generate a drawsurface at all\n//#define Q3SURF_HINT\t\t\t0x00000100\n#define\tQ3SURF_SKIP\t\t\t\t0x00000200\t// completely ignore, allowing non-closed brushes\n//#define Q3SURF_NOLIGHTMAP\t\t0x00000400\n//#define Q3SURF_POINTLIGHT\t\t0x00000800\n//#define Q3SURF_METALSTEPS\t\t0x00001000\n//#define Q3SURF_NOSTEPS\t\t0x00002000\n#define\tQ3SURF_NONSOLID\t\t\t0x00004000\t// don't collide against curves with this set\n//#define Q3SURF_LIGHTFILTER\t0x00008000\n//#define Q3SURF_ALPHASHADOW\t0x00010000\n//#define Q3SURF_NODLIGHT\t\t0x00020000\n//#define Q3SURF_DUST\t\t\t0x00040000\ncvar_t q3bsp_surf_meshcollision_flag = CVARD(\"q3bsp_surf_meshcollision_flag\", \"0x80000000\", \"The surfaceparm flag(s) that enables q3bsp trisoup collision\");\ncvar_t q3bsp_surf_meshcollision_force = CVARD(\"q3bsp_surf_meshcollision_force\", \"0\", \"Force mesh-based collisions on all q3bsp trisoup surfaces.\");\ncvar_t q3bsp_mergeq3lightmaps = CVARD(\"q3bsp_mergelightmaps\", \"1\", \"Specifies whether to merge lightmaps into atlases in order to boost performance. Unfortunately this breaks tcgen on lightmap passes - if you care, set this to 0.\");\ncvar_t q3bsp_ignorestyles = CVARD(\"q3bsp_ignorestyles\", \"0\", \"Ignores multiple lightstyles in Raven's q3bsp variant(and derivatives) for better batch/rendering performance.\");\ncvar_t q3bsp_bihtraces = CVARFD(\"_q3bsp_bihtraces\", /*FIXME: generate BIH leafs more carefully*/\"0\", CVAR_RENDERERLATCH, \"Uses runtime-generated bih collision culling for faster traces.\");\n\n#if Q3SURF_NODRAW != TI_NODRAW\n#error \"nodraw isn't constant\"\n#endif\n\nextern cvar_t r_shadow_bumpscale_basetexture;\n\n//these are in model.c (or gl_model.c)\nqboolean Mod_LoadVertexes (model_t *loadmodel, qbyte *mod_base, lump_t *l);\nqboolean Mod_LoadVertexNormals (model_t *loadmodel, bspx_header_t *bspx, qbyte *mod_base, lump_t *l);\nqboolean Mod_LoadEdges (model_t *loadmodel, qbyte *mod_base, lump_t *l, subbsp_t lm);\nqboolean Mod_LoadMarksurfaces (model_t *loadmodel, qbyte *mod_base, lump_t *l, subbsp_t lm);\nqboolean Mod_LoadSurfedges (model_t *loadmodel, qbyte *mod_base, lump_t *l);\nvoid Mod_LoadEntities (model_t *loadmodel, qbyte *mod_base, lump_t *l);\n\nextern void BuildLightMapGammaTable (float g, float c);\n\n#if defined(Q2BSPS) || defined(Q3BSPS)\nstatic qboolean CM_NativeTrace(model_t *model, int forcehullnum, const framestate_t *framestate, const vec3_t axis[3], const vec3_t start, const vec3_t end, const vec3_t mins, const vec3_t maxs, qboolean capsule, unsigned int contents, trace_t *trace);\nstatic unsigned int CM_NativeContents(struct model_s *model, int hulloverride, const framestate_t *framestate, const vec3_t axis[3], const vec3_t point, const vec3_t mins, const vec3_t maxs);\nstatic unsigned int Q2BSP_PointContents(model_t *mod, const vec3_t axis[3], const vec3_t p);\nstatic int CM_PointCluster (model_t *mod, const vec3_t p, int *area);\nstatic void CM_InfoForPoint (struct model_s *mod, vec3_t pos, int *area, int *cluster, unsigned int *contentbits);\nstruct cminfo_s;\n\n\nvoid CM_Init(void);\n\nstatic qboolean\tVARGS CM_AreasConnected (struct model_s *mod, unsigned int area1, unsigned int area2);\nstatic size_t\tCM_WriteAreaBits (struct model_s *mod, qbyte *buffer, size_t buffersize, int area, qboolean merge);\nstatic qbyte\t*CM_ClusterPVS (struct model_s *mod, int cluster, pvsbuffer_t *buffer, pvsmerge_t merge);\nstatic qbyte\t*CM_ClusterPHS (struct model_s *mod, int cluster, pvsbuffer_t *buffer);\n\n//for gamecode to control portals/areas\nstatic void\tCM_SetAreaPortalState (model_t *mod, unsigned int portalnum, unsigned int area1, unsigned int area2, qboolean open);\n\n//for saved games to write the raw state.\nstatic size_t\tCM_SaveAreaPortalBlob (model_t *mod, void **data);\nstatic size_t\tCM_LoadAreaPortalBlob (model_t *mod, void *ptr, size_t ptrsize);\n\n#ifdef HAVE_SERVER\nstatic unsigned int Q23BSP_FatPVS(model_t *mod, const vec3_t org, pvsbuffer_t *buffer, qboolean merge);\nstatic qboolean Q23BSP_EdictInFatPVS(model_t *mod, const struct pvscache_s *ent, const qbyte *pvs, const int *areas);\nstatic void Q23BSP_FindTouchedLeafs(model_t *mod, struct pvscache_s *ent, const float *mins, const float *maxs);\nstatic qboolean\tCM_HeadnodeVisible (struct model_s *mod, int nodenum, const qbyte *visbits);\n#endif\n\n#ifdef HAVE_CLIENT\nstatic void CM_PrepareFrame(model_t *mod, refdef_t *refdef, int area, int viewclusters[2], pvsbuffer_t *vis, qbyte **entvis_out, qbyte **surfvis_out);\nextern void Q2BSP_GenerateShadowMesh(model_t *mod, dlight_t *dl, const qbyte *lightvis, qbyte *litvis, void(*callback)(msurface_t*));\nextern void Q3BSP_GenerateShadowMesh(model_t *mod, dlight_t *dl, const qbyte *lightvis, qbyte *litvis, void(*callback)(msurface_t*));\n#endif\n\n#endif\n\nfloat RadiusFromBounds (const vec3_t mins, const vec3_t maxs)\n{\n\tint\t\ti;\n\tvec3_t\tcorner;\n\n\tfor (i=0 ; i<3 ; i++)\n\t{\n\t\tcorner[i] = fabs(mins[i]) > fabs(maxs[i]) ? fabs(mins[i]) : fabs(maxs[i]);\n\t}\n\n\treturn Length (corner);\n}\n\nvoid CalcSurfaceExtents (model_t *mod, msurface_t *s)\n{\n\tfloat\tmins[2], maxs[2], val;\n\tint\t\ti,j, e;\n\tmvertex_t\t*v;\n\tmtexinfo_t\t*tex;\n\tint\t\tbmins[2], bmaxs[2];\n\tint idx;\n\n\tmins[0] = mins[1] = 999999;\n\tmaxs[0] = maxs[1] = -999999;\n\n\ttex = s->texinfo;\n\n\tfor (i=0 ; i<s->numedges ; i++)\n\t{\n\t\te = mod->surfedges[s->firstedge+i];\n\t\tidx = e < 0;\n\t\tif (idx)\n\t\t\te = -e;\n\t\tif (e < 0 || e >= mod->numedges)\n\t\t\tv = &mod->vertexes[0];\n\t\telse\n\t\t\tv = &mod->vertexes[mod->edges[e].v[idx]];\n\n\t\tfor (j=0 ; j<2 ; j++)\n\t\t{\n\t\t\t//doubles should replicate win32/x87 compiler 80-bit precision better. We have to hope that win64 compilers use the same precision.\n\t\t\tval = DotProduct_Double(v->position, tex->vecs[j]) + tex->vecs[j][3];\n\t\t\tif (val < mins[j])\n\t\t\t\tmins[j] = val;\n\t\t\tif (val > maxs[j])\n\t\t\t\tmaxs[j] = val;\n\t\t}\n\t}\n\n\tfor (i=0 ; i<2 ; i++)\n\t{\n\t\tbmins[i] = floor(mins[i]/(1<<s->lmshift));\n\t\tbmaxs[i] = ceil(maxs[i]/(1<<s->lmshift));\n\n\t\ts->texturemins[i] = bmins[i] << s->lmshift;\n\t\ts->extents[i] = (bmaxs[i] - bmins[i]);\n\n//\t\tif ( !(tex->flags & TEX_SPECIAL) && s->extents[i] > 17 )\t//vanilla used 16(+1), glquake used 17(+1). FTE uses 255(+1), but we omit lightmapping instead of crashing if its larger than our limit, so we omit the check here. different engines use different limits here, many of them make no sense.\n//\t\t\tCon_Printf (\"Bad surface extents (texture %s, more than %i lightmap samples)\\n\", s->texinfo->texture->name, s->extents[i]);\n\n\t\ts->extents[i] <<= s->lmshift;\n\t}\n}\n\nvoid AddPointToBounds (const vec3_t v, vec3_t mins, vec3_t maxs)\n{\n\tint\t\ti;\n\tvec_t\tval;\n\n\tfor (i=0 ; i<3 ; i++)\n\t{\n\t\tval = v[i];\n\t\tif (val < mins[i])\n\t\t\tmins[i] = val;\n\t\tif (val > maxs[i])\n\t\t\tmaxs[i] = val;\n\t}\n}\n\nvoid ClearBounds (vec3_t mins, vec3_t maxs)\n{\n\tmins[0] = mins[1] = mins[2] = FLT_MAX;\n\tmaxs[0] = maxs[1] = maxs[2] = -FLT_MAX;\n}\n\n\nvoid Mod_SortShaders(model_t *mod)\n{\n\t//surely this isn't still needed?\n\ttexture_t *textemp;\n\tint i, j;\n\n\t//sort loadmodel->textures\n\tfor (i = 0; i < mod->numtextures; i++)\n\t{\n\t\tfor (j = i+1; j < mod->numtextures; j++)\n\t\t{\n\t\t\tif ((mod->textures[i]->shader && mod->textures[j]->shader) && (mod->textures[j]->shader->sort < mod->textures[i]->shader->sort))\n\t\t\t{\n\t\t\t\ttextemp = mod->textures[j];\n\t\t\t\tmod->textures[j] = mod->textures[i];\n\t\t\t\tmod->textures[i] = textemp;\n\t\t\t}\n\t\t}\n\t}\n}\n\n\n\n#if defined(Q2BSPS) || defined(Q3BSPS)\n\n#ifdef IMAGEFMT_PCX\nqbyte *ReadPCXPalette(qbyte *buf, int len, qbyte *out);\n#endif\n\n#ifdef SERVERONLY\n#define Host_Error SV_Error\n#endif\n\nextern qbyte *mod_base;\n\n#define capsuledist(dist,plane,mins,maxs)\t\t\t\t\t\\\n\t\tcase shape_iscapsule:\t\t\t\t\t\t\t\t\\\n\t\t\tdist = DotProduct(trace_up, plane->normal);\t\t\\\n\t\t\tdist = dist*(trace_capsulesize[(dist<0)?1:2]) - trace_capsulesize[0];\t\\\n\t\t\tdist = plane->dist - dist;\t\t\t\t\t\t\\\n\t\t\tbreak;\n\n#ifdef Q2BSPS\n#ifdef HAVE_CLIENT\nstatic unsigned char q2_palette[256*3];\n#endif\n#endif\n\n\n\n\ntypedef struct {\n\tchar\t\tshader[64];\n\tint\t\t\tbrushNum;\n\tint\t\t\tvisibleSide;\t// the brush side that ray tests need to clip against (-1 == none)\n} dfog_t;\n\n#ifdef Q2BSPS\ntypedef struct\n{\n\tint\t\tnumareaportals;\n\tint\t\tfirstareaportal;\n} q2carea_t;\n#endif\n#ifdef Q3BSPS\ntypedef struct\n{\n\tint\t\t\tnumareaportals[MAX_CM_AREAS];\n} q3carea_t;\n#endif\ntypedef struct\n{\n\tint\t\tfloodnum;\t\t\t// if two areas have equal floodnums, they are connected\n\tint\t\tfloodvalid;\t\t\t// flags the area as having been visited (sequence numbers matching prv->floodvalid)\n} careaflood_t;\n\ntypedef struct\n{\n\tint\t\tfacetype;\n\n\tint\t\tnumverts;\n\tint\t\tfirstvert;\n\n\tint\t\tshadernum;\n\tunion\n\t{\n\t\tstruct\n\t\t{\n\t\t\tunsigned short cp[2];\n\t\t\tunsigned short fixedres[2];\n\t\t} patch;\n\t\tstruct\n\t\t{\n\t\t\tint firstindex;\n\t\t\tint numindicies;\n\t\t} soup;\n\t};\n} q3cface_t;\n\ntypedef struct cmodel_s\n{\n\tvec3_t\t\tmins, maxs;\n\tvec3_t\t\torigin;\t\t// for sounds or lights\n\tmnode_t\t\t*headnode;\n\tmleaf_t\t\t*headleaf;\n\tint\t\tnumsurfaces;\n\tint\t\tfirstsurface;\n\n\tint firstbrush;\t//q3 submodels are considered small enough that you will never need to walk any sort of tree.\n\tint num_brushes;//the brushes are checked instead.\n\n\t//these things are generated at load time.\n\tint firstpatch;\n\tint num_patches;\n\tint firstcmesh;\n\tint num_cmeshes;\n} cmodel_t;\n\n/*used to trace*/\nstatic int\t\t\tcheckcount;\n\ntypedef struct cminfo_s\n{\n\tint\t\t\t\tnumbrushsides;\n\tq2cbrushside_t *brushsides;\n\n\tq2mapsurface_t\t*surfaces;\n\n\tint\t\t\t\tnumleafbrushes;\n\tq2cbrush_t\t\t**leafbrushes;\n\n\tint\t\t\t\tnumcmodels;\n\tcmodel_t\t\t*cmodels;\n\n\tint\t\t\t\tnumbrushes;\n\tq2cbrush_t\t\t*brushes;\n\n\tint\t\t\t\tnumvisibility;\n\tq2dvis_t\t\t*q2vis;\n\tq3dvis_t\t\t*q3pvs;\n\tq3dvis_t\t\t*q3phs;\n\tqbyte\t\t\t*phscalced;\n\n\tint\t\t\t\tnumareas;\n\tint\t\t\t\tfloodvalid;\n\tcareaflood_t\tareaflood[MAX_CM_AREAS];\n#ifdef Q3BSPS\n\t//q3's areas are simple bidirectional area1/area2 pairs. refcounted (so two areas can have two doors/openings)\n\tq3carea_t\t\tq3areas[MAX_CM_AREAS];\n#endif\n#ifdef Q2BSPS\n\t//q2's areas have a list of portals that open into other areas.\n\tq2carea_t\t*q2areas;\t//indexes into q2areaportals for flooding\n\tsize_t\t\t\tnumq2areaportals;\n\tq2dareaportal_t\t*q2areaportals;\n\n\t//and this is the state that is actually changed. booleans.\n\tqbyte\t\t\tq2portalopen[MAX_Q2MAP_AREAPORTALS];\t//memset will work if it's a qbyte, really it should be a qboolean\n#endif\n\n\n\t//list of mesh surfaces within the leaf\n\tq3cmesh_t\tcmeshes[MAX_CM_PATCHES];\n\tint\t\t\tnumcmeshes;\n\tint\t\t\t*leafcmeshes;\n\tint\t\t\tnumleafcmeshes;\n\tint\t\t\tmaxleafcmeshes;\n\n\t//FIXME: remove the below\n\t//(deprecated) patch collisions\n\tq3cpatch_t\tpatches[MAX_CM_PATCHES];\n\tint\t\t\tnumpatches;\n\tint\t\t\t*leafpatches;\n\tint\t\t\tnumleafpatches;\n\tint\t\t\tmaxleafpatches;\n\t//FIXME: remove the above\n\n\tqboolean\t\t\tmapisq3;\n\n\n\n#ifdef Q3BSPS\n\t//this is for loading stuff. it used to be globals, but we have threads now. and multiple q3bsps at the same time is a problem.\n\tint\t\t\tnumvertexes;\n\tvecV_t\t\t*verts;\t//3points\n\tvec2_t\t\t*vertstmexcoords;\n\tvec2_t\t\t*vertlstmexcoords[MAXRLIGHTMAPS];\n\tvec4_t\t\t*colors4f_array[MAXRLIGHTMAPS];\n\tvec3_t\t\t*normals_array;\n\t//vec3_t\t\t*map_svector_array;\n\t//vec3_t\t\t*map_tvector_array;\n\n\tindex_t *surfindexes;\n\t//int\tmap_numsurfindexes;\n\n\tq3cface_t\t*faces;\n\tint\t\t\tnumfaces;\n#endif\n\n#ifdef HAVE_CLIENT\n\tint\t\t\toldclusters[2];\n\tqbyte\t\t*oldvis;\n#endif\n\n//\tstruct bihnode_s *bihnodes;\n} cminfo_t;\n\nstatic q2mapsurface_t\tnullsurface;\n\ncvar_t\t\tmap_noareas\t\t\t= CVAR(\"map_noareas\", \"0\");\t//1 for lack of mod support.\ncvar_t\t\tmap_noCurves\t\t= CVARF(\"map_noCurves\", \"0\", CVAR_CHEAT);\ncvar_t\t\tmap_autoopenportals\t= CVARD(\"map_autoopenportals\", \"0\", \"When set to 1, force-opens all area portals. Normally these start closed and are opened by doors when they move, but this requires the gamecode to signal this.\");\t//1 for lack of mod support.\ncvar_t\t\tr_subdivisions\t\t= CVAR(\"r_subdivisions\", \"2\");\n\nstatic int\t\tCM_NumInlineModels (model_t *model);\nstatic cmodel_t\t*CM_InlineModel (model_t *model, char *name);\nstatic void\tCM_InitBoxHull (void);\nstatic void CM_FinalizeBrush(q2cbrush_t *brush);\nstatic void\tFloodAreaConnections (cminfo_t\t*prv);\n\nqboolean BoundsIntersect (const vec3_t mins1, const vec3_t maxs1, const vec3_t mins2, const vec3_t maxs2)\n{\n\treturn (mins1[0] <= maxs2[0] && mins1[1] <= maxs2[1] && mins1[2] <= maxs2[2] &&\n\t\t maxs1[0] >= mins2[0] && maxs1[1] >= mins2[1] && maxs1[2] >= mins2[2]);\n}\n\n#ifdef Q3BSPS\n\nstatic int\tPlaneTypeForNormal ( vec3_t normal )\n{\n\tvec_t\tax, ay, az;\n\n// NOTE: should these have an epsilon around 1.0?\n\tif ( normal[0] >= 1.0)\n\t\treturn PLANE_X;\n\tif ( normal[1] >= 1.0 )\n\t\treturn PLANE_Y;\n\tif ( normal[2] >= 1.0 )\n\t\treturn PLANE_Z;\n\n\tax = fabs( normal[0] );\n\tay = fabs( normal[1] );\n\taz = fabs( normal[2] );\n\n\tif ( ax >= ay && ax >= az )\n\t\treturn PLANE_ANYX;\n\tif ( ay >= ax && ay >= az )\n\t\treturn PLANE_ANYY;\n\treturn PLANE_ANYZ;\n}\n\nvoid CategorizePlane ( mplane_t *plane )\n{\n\tint i;\n\n\tplane->signbits = 0;\n\tplane->type = PLANE_ANYZ;\n\tfor (i = 0; i < 3; i++)\n\t{\n\t\tif (plane->normal[i] < 0)\n\t\t\tplane->signbits |= 1<<i;\n\t\tif (plane->normal[i] == 1.0f)\n\t\t\tplane->type = i;\n\t}\n\tplane->type = PlaneTypeForNormal(plane->normal);\n}\n\nstatic void PlaneFromPoints ( vec3_t verts[3], mplane_t *plane )\n{\n\tvec3_t\tv1, v2;\n\n\tVectorSubtract( verts[1], verts[0], v1 );\n\tVectorSubtract( verts[2], verts[0], v2 );\n\tCrossProduct( v2, v1, plane->normal );\n\tVectorNormalize( plane->normal );\n\tplane->dist = DotProduct( verts[0], plane->normal );\n}\n\n/*\n===============\nPatch_FlatnessTest\n===============\n*/\nstatic int Patch_FlatnessTest( float maxflat2, const float *point0, const float *point1, const float *point2 )\n{\n\tfloat d;\n\tint ft0, ft1;\n\tvec3_t t, n;\n\tvec3_t v1, v2, v3;\n\n\tVectorSubtract( point2, point0, n );\n\tif( !VectorNormalize( n ) )\n\t\treturn 0;\n\n\tVectorSubtract( point1, point0, t );\n\td = -DotProduct( t, n );\n\tVectorMA( t, d, n, t );\n\tif( DotProduct( t, t ) < maxflat2 )\n\t\treturn 0;\n\n\tVectorAvg( point1, point0, v1 );\n\tVectorAvg( point2, point1, v2 );\n\tVectorAvg( v1, v2, v3 );\n\n\tft0 = Patch_FlatnessTest( maxflat2, point0, v1, v3 );\n\tft1 = Patch_FlatnessTest( maxflat2, v3, v2, point2 );\n\n\treturn 1 + (int)( floor( max( ft0, ft1 ) ) + 0.5f );\n}\n\n/*\n===============\nPatch_GetFlatness\n===============\n*/\nstatic void Patch_GetFlatness( float maxflat, const float *points, int comp, const unsigned short *patch_cp, int *flat )\n{\n\tint i, p, u, v;\n\tfloat maxflat2 = maxflat * maxflat;\n\n\tflat[0] = flat[1] = 0;\n\tfor( v = 0; v < patch_cp[1] - 1; v += 2 )\n\t{\n\t\tfor( u = 0; u < patch_cp[0] - 1; u += 2 )\n\t\t{\n\t\t\tp = v * patch_cp[0] + u;\n\n\t\t\ti = Patch_FlatnessTest( maxflat2, &points[p*comp], &points[( p+1 )*comp], &points[( p+2 )*comp] );\n\t\t\tflat[0] = max( flat[0], i );\n\t\t\ti = Patch_FlatnessTest( maxflat2, &points[( p+patch_cp[0] )*comp], &points[( p+patch_cp[0]+1 )*comp], &points[( p+patch_cp[0]+2 )*comp] );\n\t\t\tflat[0] = max( flat[0], i );\n\t\t\ti = Patch_FlatnessTest( maxflat2, &points[( p+2*patch_cp[0] )*comp], &points[( p+2*patch_cp[0]+1 )*comp], &points[( p+2*patch_cp[0]+2 )*comp] );\n\t\t\tflat[0] = max( flat[0], i );\n\n\t\t\ti = Patch_FlatnessTest( maxflat2, &points[p*comp], &points[( p+patch_cp[0] )*comp], &points[( p+2*patch_cp[0] )*comp] );\n\t\t\tflat[1] = max( flat[1], i );\n\t\t\ti = Patch_FlatnessTest( maxflat2, &points[( p+1 )*comp], &points[( p+patch_cp[0]+1 )*comp], &points[( p+2*patch_cp[0]+1 )*comp] );\n\t\t\tflat[1] = max( flat[1], i );\n\t\t\ti = Patch_FlatnessTest( maxflat2, &points[( p+2 )*comp], &points[( p+patch_cp[0]+2 )*comp], &points[( p+2*patch_cp[0]+2 )*comp] );\n\t\t\tflat[1] = max( flat[1], i );\n\t\t}\n\t}\n}\n\n/*\n===============\nPatch_Evaluate_QuadricBezier\n===============\n*/\nstatic void Patch_Evaluate_QuadricBezier( float t, const vec_t *point0, const vec_t *point1, const vec_t *point2, vec_t *out, int comp )\n{\n\tint i;\n\tvec_t qt = t * t;\n\tvec_t dt = 2.0f * t, tt, tt2;\n\n\ttt = 1.0f - dt + qt;\n\ttt2 = dt - 2.0f * qt;\n\n\tfor( i = 0; i < comp; i++ )\n\t\tout[i] = point0[i] * tt + point1[i] * tt2 + point2[i] * qt;\n}\n\n/*\n===============\nPatch_Evaluate\n===============\n*/\n#define Patch_Evaluate(p,numcp,tess,dest, comp) Patch_EvaluateStride(p,comp,numcp,tess,dest,comp,comp)\nstatic void Patch_EvaluateStride(const vec_t *p, int pstride, const unsigned short *numcp, const int *tess, vec_t *dest, int deststride, int comp)\n{\n\tint num_patches[2], num_tess[2];\n\tint index[3], dstpitch, i, u, v, x, y;\n\tfloat s, t, step[2];\n\tvec_t *tvec, *tvec2;\n\tconst vec_t *pv[3][3];\n\tvec4_t v1, v2, v3;\n\n\tif (!tess[0] || !tess[1])\n\t{\t//not really a patch\n\t\tfor( u = 0; u < numcp[1]*numcp[0]; u++, dest += deststride, p += pstride)\n\t\t\tfor( i = 0; i < comp; i++ )\n\t\t\t\tdest[i] = p[i];\n\t\treturn;\n\t}\n\n\tnum_patches[0] = numcp[0] / 2;\n\tnum_patches[1] = numcp[1] / 2;\n\tdstpitch = ( num_patches[0] * tess[0] + 1 ) * deststride;\n\n\tstep[0] = 1.0f / (float)tess[0];\n\tstep[1] = 1.0f / (float)tess[1];\n\n\tfor( v = 0; v < num_patches[1]; v++ )\n\t{\n\t\t// last patch has one more row\n\t\tif( v < num_patches[1] - 1 )\n\t\t\tnum_tess[1] = tess[1];\n\t\telse\n\t\t\tnum_tess[1] = tess[1] + 1;\n\n\t\tfor( u = 0; u < num_patches[0]; u++ )\n\t\t{\n\t\t\t// last patch has one more column\n\t\t\tif( u < num_patches[0] - 1 )\n\t\t\t\tnum_tess[0] = tess[0];\n\t\t\telse\n\t\t\t\tnum_tess[0] = tess[0] + 1;\n\n\t\t\tindex[0] = ( v * numcp[0] + u ) * 2;\n\t\t\tindex[1] = index[0] + numcp[0];\n\t\t\tindex[2] = index[1] + numcp[0];\n\n\t\t\t// current 3x3 patch control points\n\t\t\tfor( i = 0; i < 3; i++ )\n\t\t\t{\n\t\t\t\tpv[i][0] = &p[( index[0]+i ) * pstride];\n\t\t\t\tpv[i][1] = &p[( index[1]+i ) * pstride];\n\t\t\t\tpv[i][2] = &p[( index[2]+i ) * pstride];\n\t\t\t}\n\n\t\t\ttvec = dest + v * tess[1] * dstpitch + u * tess[0] * deststride;\n\t\t\tfor( y = 0, t = 0.0f; y < num_tess[1]; y++, t += step[1], tvec += dstpitch )\n\t\t\t{\n\t\t\t\tPatch_Evaluate_QuadricBezier( t, pv[0][0], pv[0][1], pv[0][2], v1, comp );\n\t\t\t\tPatch_Evaluate_QuadricBezier( t, pv[1][0], pv[1][1], pv[1][2], v2, comp );\n\t\t\t\tPatch_Evaluate_QuadricBezier( t, pv[2][0], pv[2][1], pv[2][2], v3, comp );\n\n\t\t\t\tfor( x = 0, tvec2 = tvec, s = 0.0f; x < num_tess[0]; x++, s += step[0], tvec2 += deststride )\n\t\t\t\t\tPatch_Evaluate_QuadricBezier( s, v1, v2, v3, tvec2, comp );\n\t\t\t}\n\t\t}\n\t}\n}\n#ifdef TERRAIN\n#include \"gl_terrain.h\"\npatchtessvert_t *PatchInfo_Evaluate(const qcpatchvert_t *cp, const unsigned short patch_cp[2], const short subdiv[2], unsigned short *size)\n{\n\tint step[2], flat[2];\n\tfloat subdivlevel;\n\tunsigned int numverts;\n\tpatchtessvert_t *out;\n\tint i;\n\n\tif (subdiv[0]>=0 && subdiv[1]>=0)\n\t{\t//fixed\n\t\tstep[0] = subdiv[0];\n\t\tstep[1] = subdiv[1];\n\t}\n\telse\n\t{\n\t\t// find the degree of subdivision in the u and v directions\n\t\tsubdivlevel = bound(1, r_subdivisions.ival, 15);\n\t\tPatch_GetFlatness ( subdivlevel, cp->v, sizeof(*cp)/sizeof(vec_t), patch_cp, flat );\n\n\t\tstep[0] = 1 << flat[0];\n\t\tstep[1] = 1 << flat[1];\n\t}\n\tif (!step[0] || !step[1])\n\t{\n\t\tsize[0] = patch_cp[0];\n\t\tsize[1] = patch_cp[1];\n\t}\n\telse\n\t{\n\t\tsize[0] = ( patch_cp[0] >> 1 ) * step[0] + 1;\n\t\tsize[1] = ( patch_cp[1] >> 1 ) * step[1] + 1;\n\t}\n\tif( size[0] <= 0 || size[1] <= 0 )\n\t\treturn NULL;\n\n\tnumverts = (unsigned int)size[0] * size[1];\n\n// fill in\n\n\tout = BZ_Malloc(sizeof(*out) * numverts);\n\tfor (i = 0; i < numverts*sizeof(*out)/sizeof(vec_t); i++)\n\t\t((vec_t *)out)[i] = -1;\n\tPatch_EvaluateStride ( cp->v, sizeof(*cp)/sizeof(vec_t), patch_cp, step, out->v, sizeof(*out)/sizeof(vec_t), countof(cp->v));\n\tPatch_EvaluateStride ( cp->rgba, sizeof(*cp)/sizeof(vec_t), patch_cp, step, out->rgba, sizeof(*out)/sizeof(vec_t), countof(cp->rgba));\n\tPatch_EvaluateStride ( cp->tc, sizeof(*cp)/sizeof(vec_t), patch_cp, step, out->tc, sizeof(*out)/sizeof(vec_t), countof(cp->tc));\n\n\treturn out;\n}\nunsigned int PatchInfo_EvaluateIndexes(const unsigned short *size, index_t *out_indexes)\n{\n\tint i, u, v, p;\n// compute new indexes avoiding adding invalid triangles\n\tunsigned int numindexes = 0;\n\tindex_t\t*indexes = out_indexes;\n\tfor (v = 0, i = 0; v < size[1]-1; v++)\n\t{\n\t\tfor (u = 0; u < size[0]-1; u++, i += 6)\n\t\t{\n\t\t\tindexes[0] = p = v * size[0] + u;\n\t\t\tindexes[1] = p + size[0];\n\t\t\tindexes[2] = p + 1;\n\n//\t\t\tif ( !VectorEquals(mesh->xyz_array[indexes[0]], mesh->xyz_array[indexes[1]]) &&\n//\t\t\t\t!VectorEquals(mesh->xyz_array[indexes[0]], mesh->xyz_array[indexes[2]]) &&\n//\t\t\t\t!VectorEquals(mesh->xyz_array[indexes[1]], mesh->xyz_array[indexes[2]]) )\n\t\t\t{\n\t\t\t\tindexes += 3;\n\t\t\t\tnumindexes += 3;\n\t\t\t}\n\n\t\t\tindexes[0] = p + 1;\n\t\t\tindexes[1] = p + size[0];\n\t\t\tindexes[2] = p + size[0] + 1;\n\n//\t\t\tif ( !VectorEquals(mesh->xyz_array[indexes[0]], mesh->xyz_array[indexes[1]]) &&\n//\t\t\t\t!VectorEquals(mesh->xyz_array[indexes[0]], mesh->xyz_array[indexes[2]]) &&\n//\t\t\t\t!VectorEquals(mesh->xyz_array[indexes[1]], mesh->xyz_array[indexes[2]]) )\n\t\t\t{\n\t\t\t\tindexes += 3;\n\t\t\t\tnumindexes += 3;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn numindexes;\n}\n#endif\n\n\n#define\tPLANE_NORMAL_EPSILON\t0.00001\n#define\tPLANE_DIST_EPSILON\t0.01\nstatic qboolean ComparePlanes( const vec3_t p1normal, vec_t p1dist, const vec3_t p2normal, vec_t p2dist )\n{\n\tif( fabs( p1normal[0] - p2normal[0] ) < PLANE_NORMAL_EPSILON\n\t    && fabs( p1normal[1] - p2normal[1] ) < PLANE_NORMAL_EPSILON\n\t    && fabs( p1normal[2] - p2normal[2] ) < PLANE_NORMAL_EPSILON\n\t    && fabs( p1dist - p2dist ) < PLANE_DIST_EPSILON )\n\t\treturn true;\n\n\treturn false;\n}\n\nstatic void SnapVector( vec3_t normal )\n{\n\tint i;\n\n\tfor( i = 0; i < 3; i++ )\n\t{\n\t\tif( fabs( normal[i] - 1 ) < PLANE_NORMAL_EPSILON )\n\t\t{\n\t\t\tVectorClear( normal );\n\t\t\tnormal[i] = 1;\n\t\t\tbreak;\n\t\t}\n\t\tif( fabs( normal[i] + 1 ) < PLANE_NORMAL_EPSILON )\n\t\t{\n\t\t\tVectorClear( normal );\n\t\t\tnormal[i] = -1;\n\t\t\tbreak;\n\t\t}\n\t}\n}\n\n#define Q_rint( x )   ( ( x ) < 0 ? ( (int)( ( x )-0.5f ) ) : ( (int)( ( x )+0.5f ) ) )\nstatic void SnapPlane( vec3_t normal, vec_t *dist )\n{\n\tSnapVector( normal );\n\n\tif( fabs( *dist - Q_rint( *dist ) ) < PLANE_DIST_EPSILON )\n\t{\n\t\t*dist = Q_rint( *dist );\n\t}\n}\n\n/*\n===============================================================================\n\n\t\t\t\t\tPATCH LOADING\n\n===============================================================================\n*/\n\n#define MAX_FACET_PLANES 32\n#define cm_subdivlevel\t15\n\n/*\n* CM_CreateFacetFromPoints\n*/\nstatic int CM_CreateFacetFromPoints(q2cbrush_t *facet, vec3_t *verts, int numverts, q2mapsurface_t *shaderref, mplane_t *brushplanes )\n{\n\tint i, j;\n\tint axis, dir;\n\tvec3_t normal;\n\tfloat d, dist;\n\tmplane_t mainplane;\n\tvec3_t vec, vec2;\n\tint numbrushplanes;\n\n\t// set default values for brush\n\tfacet->numsides = 0;\n\tfacet->brushside = NULL;\n\tfacet->contents = shaderref->c.value;\n\n\t// calculate plane for this triangle\n\tPlaneFromPoints( verts, &mainplane );\n\tif( ComparePlanes( mainplane.normal, mainplane.dist, vec3_origin, 0 ) )\n\t\treturn 0;\n\n\t// test a quad case\n\tif( numverts > 3 )\n\t{\n\t\td = DotProduct( verts[3], mainplane.normal ) - mainplane.dist;\n\t\tif( d < -0.1 || d > 0.1 )\n\t\t\treturn 0;\n\n\t\tif( 0 )\n\t\t{\n\t\t\tvec3_t v[3];\n\t\t\tmplane_t plane;\n\n\t\t\t// try different combinations of planes\n\t\t\tfor( i = 1; i < 4; i++ )\n\t\t\t{\n\t\t\t\tVectorCopy( verts[i], v[0] );\n\t\t\t\tVectorCopy( verts[( i+1 )%4], v[1] );\n\t\t\t\tVectorCopy( verts[( i+2 )%4], v[2] );\n\t\t\t\tPlaneFromPoints( v, &plane );\n\n\t\t\t\tif( fabs( DotProduct( mainplane.normal, plane.normal ) ) < 0.9 )\n\t\t\t\t\treturn 0;\n\t\t\t}\n\t\t}\n\t}\n\n\tnumbrushplanes = 0;\n\n\t// add front plane\n\tSnapPlane( mainplane.normal, &mainplane.dist );\n\tVectorCopy( mainplane.normal, brushplanes[numbrushplanes].normal );\n\tbrushplanes[numbrushplanes].dist = mainplane.dist; numbrushplanes++;\n\n\t// calculate mins & maxs\n\tClearBounds( facet->absmins, facet->absmaxs );\n\tfor( i = 0; i < numverts; i++ )\n\t\tAddPointToBounds( verts[i], facet->absmins, facet->absmaxs );\n\n\t// add the axial planes\n\tfor( axis = 0; axis < 3; axis++ )\n\t{\n\t\tfor( dir = -1; dir <= 1; dir += 2 )\n\t\t{\n\t\t\tfor( i = 0; i < numbrushplanes; i++ )\n\t\t\t{\n\t\t\t\tif( brushplanes[i].normal[axis] == dir )\n\t\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tif( i == numbrushplanes )\n\t\t\t{\n\t\t\t\tVectorClear( normal );\n\t\t\t\tnormal[axis] = dir;\n\t\t\t\tif( dir == 1 )\n\t\t\t\t\tdist = facet->absmaxs[axis];\n\t\t\t\telse\n\t\t\t\t\tdist = -facet->absmins[axis];\n\n\t\t\t\tVectorCopy( normal, brushplanes[numbrushplanes].normal );\n\t\t\t\tbrushplanes[numbrushplanes].dist = dist; numbrushplanes++;\n\t\t\t}\n\t\t}\n\t}\n\n\t// add the edge bevels\n\tfor( i = 0; i < numverts; i++ )\n\t{\n\t\tj = ( i + 1 ) % numverts;\n//\t\tk = ( i + 2 ) % numverts;\n\n\t\tVectorSubtract( verts[i], verts[j], vec );\n\t\tif( VectorNormalize( vec ) < 0.5 )\n\t\t\tcontinue;\n\n\t\tSnapVector( vec );\n\t\tfor( j = 0; j < 3; j++ )\n\t\t{\n\t\t\tif( vec[j] == 1 || vec[j] == -1 )\n\t\t\t\tbreak; // axial\n\t\t}\n\t\tif( j != 3 )\n\t\t\tcontinue; // only test non-axial edges\n\n\t\t// try the six possible slanted axials from this edge\n\t\tfor( axis = 0; axis < 3; axis++ )\n\t\t{\n\t\t\tfor( dir = -1; dir <= 1; dir += 2 )\n\t\t\t{\n\t\t\t\t// construct a plane\n\t\t\t\tVectorClear( vec2 );\n\t\t\t\tvec2[axis] = dir;\n\t\t\t\tCrossProduct( vec, vec2, normal );\n\t\t\t\tif( VectorNormalize( normal ) < 0.5 )\n\t\t\t\t\tcontinue;\n\t\t\t\tdist = DotProduct( verts[i], normal );\n\n\t\t\t\tfor( j = 0; j < numbrushplanes; j++ )\n\t\t\t\t{\n\t\t\t\t\t// if this plane has already been used, skip it\n\t\t\t\t\tif( ComparePlanes( brushplanes[j].normal, brushplanes[j].dist, normal, dist ) )\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tif( j != numbrushplanes )\n\t\t\t\t\tcontinue;\n\n\t\t\t\t// if all other points are behind this plane, it is a proper edge bevel\n\t\t\t\tfor( j = 0; j < numverts; j++ )\n\t\t\t\t{\n\t\t\t\t\tif( j != i )\n\t\t\t\t\t{\n\t\t\t\t\t\td = DotProduct( verts[j], normal ) - dist;\n\t\t\t\t\t\tif( d > 0.1 )\n\t\t\t\t\t\t\tbreak; // point in front: this plane isn't part of the outer hull\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif( j != numverts )\n\t\t\t\t\tcontinue;\n\n\t\t\t\t// add this plane\n\t\t\t\tVectorCopy( normal, brushplanes[numbrushplanes].normal );\n\t\t\t\tbrushplanes[numbrushplanes].dist = dist; numbrushplanes++;\n\t\t\t\tif( numbrushplanes == MAX_FACET_PLANES )\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn ( facet->numsides = numbrushplanes );\n}\n\n/*\n* CM_CreatePatch\n*/\nstatic void CM_CreatePatch(model_t *loadmodel, q3cpatch_t *patch, q2mapsurface_t *shaderref, const vec_t *verts, const unsigned short *patch_cp, const unsigned short *patch_subdiv)\n{\n\tint step[2], size[2], flat[2];\n\tint i, j, k ,u, v;\n\tint numsides, totalsides;\n\tq2cbrush_t *facets, *facet;\n\tvecV_t *points;\n\tvec3_t tverts[4];\n\tqbyte *data;\n\tmplane_t *brushplanes;\n\tfloat subdivlevel;\n\n\tpatch->surface = shaderref;\n\n\tif (patch_subdiv)\n\t{\t//fixed\n\t\tstep[0] = patch_subdiv[0];\n\t\tstep[1] = patch_subdiv[1];\n\t}\n\telse\n\t{\n\t\t// find the degree of subdivision in the u and v directions\n\t\tsubdivlevel = cm_subdivlevel;//r_subdivisions.value;\n\t\tif ( subdivlevel < 1 )\n\t\t\tsubdivlevel = 1;\n\t\tPatch_GetFlatness( subdivlevel, verts, sizeof(vecV_t)/sizeof(vec_t), patch_cp, flat );\n\n\t\tstep[0] = 1 << flat[0];\n\t\tstep[1] = 1 << flat[1];\n\t}\n\tif (!step[0] || !step[1])\n\t{\n\t\tsize[0] = patch_cp[0];\n\t\tsize[1] = patch_cp[1];\n\t}\n\telse\n\t{\n\t\tsize[0] = ( patch_cp[0] >> 1 ) * step[0] + 1;\n\t\tsize[1] = ( patch_cp[1] >> 1 ) * step[1] + 1;\n\t}\n\tif( size[0] <= 0 || size[1] <= 0 )\n\t\treturn;\n\n\tdata = BZ_Malloc( size[0] * size[1] * sizeof( vecV_t ) +\n\t\t( size[0]-1 ) * ( size[1]-1 ) * 2 * ( sizeof( q2cbrush_t ) + 32 * sizeof( mplane_t ) ) );\n\n\tpoints = ( vecV_t * )data; data += size[0] * size[1] * sizeof( vecV_t );\n\tfacets = ( q2cbrush_t * )data; data += ( size[0]-1 ) * ( size[1]-1 ) * 2 * sizeof( q2cbrush_t );\n\tbrushplanes = ( mplane_t * )data; data += ( size[0]-1 ) * ( size[1]-1 ) * 2 * MAX_FACET_PLANES * sizeof( mplane_t );\n\n\t// fill in\n\tPatch_Evaluate(verts, patch_cp, step, points[0], sizeof(vecV_t)/sizeof(vec_t));\n\n\ttotalsides = 0;\n\tpatch->numfacets = 0;\n\tpatch->facets = NULL;\n\tClearBounds( patch->absmins, patch->absmaxs );\n\n\t// create a set of facets\n\tfor( v = 0; v < size[1]-1; v++ )\n\t{\n\t\tfor( u = 0; u < size[0]-1; u++ )\n\t\t{\n\t\t\ti = v * size[0] + u;\n\t\t\tVectorCopy( points[i], tverts[0] );\n\t\t\tVectorCopy( points[i + size[0]], tverts[1] );\n\t\t\tVectorCopy( points[i + size[0] + 1], tverts[2] );\n\t\t\tVectorCopy( points[i + 1], tverts[3] );\n\n\t\t\tfor( i = 0; i < 4; i++ )\n\t\t\t\tAddPointToBounds( tverts[i], patch->absmins, patch->absmaxs );\n\n\t\t\t// try to create one facet from a quad\n\t\t\tnumsides = CM_CreateFacetFromPoints( &facets[patch->numfacets], tverts, 4, shaderref, brushplanes + totalsides );\n\t\t\tif( !numsides )\n\t\t\t{\t// create two facets from triangles\n\t\t\t\tVectorCopy( tverts[3], tverts[2] );\n\t\t\t\tnumsides = CM_CreateFacetFromPoints( &facets[patch->numfacets], tverts, 3, shaderref, brushplanes + totalsides );\n\t\t\t\tif( numsides )\n\t\t\t\t{\n\t\t\t\t\ttotalsides += numsides;\n\t\t\t\t\tpatch->numfacets++;\n\t\t\t\t}\n\n\t\t\t\tVectorCopy( tverts[2], tverts[0] );\n\t\t\t\tVectorCopy( points[v *size[0] + u + size[0] + 1], tverts[2] );\n\t\t\t\tnumsides = CM_CreateFacetFromPoints( &facets[patch->numfacets], tverts, 3, shaderref, brushplanes + totalsides );\n\t\t\t}\n\n\t\t\tif( numsides )\n\t\t\t{\n\t\t\t\ttotalsides += numsides;\n\t\t\t\tpatch->numfacets++;\n\t\t\t}\n\t\t}\n\t}\n\n\tif (patch->numfacets)\n\t{\n\t\tqbyte *data;\n\n\t\tdata = ZG_Malloc(&loadmodel->memgroup, patch->numfacets * sizeof( q2cbrush_t ) + totalsides * ( sizeof( q2cbrushside_t ) + sizeof( mplane_t ) ));\n\n\t\tpatch->facets = ( q2cbrush_t * )data; data += patch->numfacets * sizeof( q2cbrush_t );\n\t\tmemcpy( patch->facets, facets, patch->numfacets * sizeof( q2cbrush_t ) );\n\t\tfor( i = 0, k = 0, facet = patch->facets; i < patch->numfacets; i++, facet++ )\n\t\t{\n\t\t\tmplane_t *planes;\n\t\t\tq2cbrushside_t *s;\n\n\t\t\tfacet->brushside = ( q2cbrushside_t * )data; data += facet->numsides * sizeof( q2cbrushside_t );\n\t\t\tplanes = ( mplane_t * )data; data += facet->numsides * sizeof( mplane_t );\n\n\t\t\tfor( j = 0, s = facet->brushside; j < facet->numsides; j++, s++ )\n\t\t\t{\n\t\t\t\tplanes[j] = brushplanes[k++];\n\n\t\t\t\ts->plane = &planes[j];\n\t\t\t\tSnapPlane( s->plane->normal, &s->plane->dist );\n\t\t\t\tCategorizePlane( s->plane );\n\t\t\t\ts->surface = shaderref;\n\t\t\t}\n\t\t}\n\n\t\tfor( i = 0; i < 3; i++ )\n\t\t{\n\t\t\t// spread the mins / maxs by a pixel\n\t\t\tpatch->absmins[i] -= 1;\n\t\t\tpatch->absmaxs[i] += 1;\n\t\t}\n\t}\n\n\tBZ_Free( points );\n}\n\n//======================================================\n\nstatic qboolean CM_CreatePatchForFace (model_t *loadmodel, cminfo_t *prv, mleaf_t *leaf, int facenum, int *checkout)\n{\n\tsize_t u;\n\tq3cface_t *face;\n\tq2mapsurface_t *surf;\n\tq3cpatch_t *patch;\n\tq3cmesh_t *cmesh;\n\n\tface = &prv->faces[facenum];\n\n\tif (face->numverts <= 0)\n\t\treturn true;\n\tif (face->shadernum < 0 || face->shadernum >= loadmodel->numtextures)\n\t\treturn true;\n\tsurf = &prv->surfaces[face->shadernum];\n\tif (!surf->c.value)\t//surface has no contents value, so can't ever block anything.\n\t\treturn true;\n\n\tswitch(face->facetype)\n\t{\n\tcase MST_TRIANGLE_SOUP:\n\t\tif (!face->soup.numindicies)\n\t\t\treturn true;\n\t\t//only enable mesh collisions if its meant to be enabled.\n\t\t//we haven't parsed any shaders, so we depend upon the stuff that the bsp compiler left lying around.\n\t\tif (!(surf->c.flags & q3bsp_surf_meshcollision_flag.ival) && !q3bsp_surf_meshcollision_force.ival)\n\t\t\treturn true;\n\n\t\tif (prv->numleafcmeshes >= prv->maxleafcmeshes)\n\t\t{\n\t\t\tprv->maxleafcmeshes *= 2;\n\t\t\tprv->maxleafcmeshes += 16;\n\t\t\tif (prv->numleafcmeshes > prv->maxleafcmeshes)\n\t\t\t{\t//detect overflow\n\t\t\t\tCon_Printf (CON_ERROR \"CM_CreateCMeshesForLeafs: map is insanely huge!\\n\");\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tprv->leafcmeshes = realloc(prv->leafcmeshes, sizeof(*prv->leafcmeshes) * prv->maxleafcmeshes);\n\t\t}\n\n\t\t// the patch was already built\n\t\tif (checkout[facenum] != -1)\n\t\t{\n\t\t\tprv->leafcmeshes[prv->numleafcmeshes] = checkout[facenum];\n\t\t\tcmesh = &prv->cmeshes[checkout[facenum]];\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (prv->numcmeshes >= MAX_CM_PATCHES)\n\t\t\t{\n\t\t\t\tCon_Printf (CON_ERROR \"CM_CreatePatchesForLeafs: map has too many patches\\n\");\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tcmesh = &prv->cmeshes[prv->numcmeshes];\n\t\t\tprv->leafcmeshes[prv->numleafcmeshes] = prv->numcmeshes;\n\t\t\tcheckout[facenum] = prv->numcmeshes++;\n\n//gcc warns without this cast\n\n\t\t\tcmesh->surface = surf;\n\t\t\tcmesh->numverts = face->numverts;\n\t\t\tcmesh->numincidies = face->soup.numindicies;\n\t\t\tcmesh->xyz_array = ZG_Malloc(&loadmodel->memgroup, cmesh->numverts * sizeof(*cmesh->xyz_array) + cmesh->numincidies * sizeof(*cmesh->indicies));\n\t\t\tcmesh->indicies = (index_t*)(cmesh->xyz_array + cmesh->numverts);\n\n\t\t\tVectorCopy(prv->verts[face->firstvert+0], cmesh->xyz_array[0]);\n\t\t\tVectorCopy(cmesh->xyz_array[0], cmesh->absmaxs);\n\t\t\tVectorCopy(cmesh->xyz_array[0], cmesh->absmins);\n\t\t\tfor (u = 1; u < cmesh->numverts; u++)\n\t\t\t{\n\t\t\t\tVectorCopy(prv->verts[face->firstvert+u], cmesh->xyz_array[u]);\n\t\t\t\tAddPointToBounds(cmesh->xyz_array[u], cmesh->absmins, cmesh->absmaxs);\n\t\t\t}\n\t\t\tfor (u = 0; u < cmesh->numincidies; u++)\n\t\t\t\tcmesh->indicies[u] = prv->surfindexes[face->soup.firstindex+u];\n\t\t}\n\t\tleaf->contents |= surf->c.value;\n\t\tleaf->numleafcmeshes++;\n\n\t\tprv->numleafcmeshes++;\n\n\t\tbreak;\n\tcase MST_PATCH:\n\tcase MST_PATCH_FIXED:\n\t\tif (face->patch.cp[0] <= 0 || face->patch.cp[1] <= 0)\n\t\t\treturn true;\n\n\t\tif ( !surf->c.value || (surf->c.flags & Q3SURF_NONSOLID) )\n\t\t\treturn true;\n\n\t\tif (prv->numleafpatches >= prv->maxleafpatches)\n\t\t{\n\t\t\tprv->maxleafpatches *= 2;\n\t\t\tprv->maxleafpatches += 16;\n\t\t\tif (prv->numleafpatches > prv->maxleafpatches)\n\t\t\t{\t//detect overflow\n\t\t\t\tCon_Printf (CON_ERROR \"CM_CreatePatchesForLeafs: map is insanely huge!\\n\");\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tprv->leafpatches = realloc(prv->leafpatches, sizeof(*prv->leafpatches) * prv->maxleafpatches);\n\t\t}\n\n\t\t// the patch was already built\n\t\tif (checkout[facenum] != -1)\n\t\t{\n\t\t\tprv->leafpatches[prv->numleafpatches] = checkout[facenum];\n\t\t\tpatch = &prv->patches[checkout[facenum]];\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (prv->numpatches >= MAX_CM_PATCHES)\n\t\t\t{\n\t\t\t\tCon_Printf (CON_ERROR \"CM_CreatePatchesForLeafs: map has too many patches\\n\");\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tpatch = &prv->patches[prv->numpatches];\n\t\t\tprv->leafpatches[prv->numleafpatches] = prv->numpatches;\n\t\t\tcheckout[facenum] = prv->numpatches++;\n\n//gcc warns without this cast\n\t\t\tCM_CreatePatch (loadmodel, patch, surf, (const vec_t *)(prv->verts + face->firstvert), face->patch.cp, (face->facetype==MST_PATCH_FIXED)?face->patch.fixedres:NULL );\n\t\t}\n\t\tleaf->contents |= patch->surface->c.value;\n\t\tleaf->numleafpatches++;\n\n\t\tprv->numleafpatches++;\n\t\tbreak;\n\t}\n\treturn true;\n}\nstatic qboolean CM_CreatePatchesForLeaf (model_t *loadmodel, cminfo_t *prv, mleaf_t *leaf, int *checkout)\n{\n\tint j, k;\n\n\tleaf->numleafpatches = 0;\n\tleaf->firstleafpatch = prv->numleafpatches;\n\tleaf->numleafcmeshes = 0;\n\tleaf->firstleafcmesh = prv->numleafcmeshes;\n\n\tif (leaf->cluster == -1)\n\t\treturn true;\n\n\tfor (j=0 ; j<leaf->nummarksurfaces ; j++)\n\t{\n\t\tk = leaf->firstmarksurface[j] - loadmodel->surfaces;\n\t\tif (k >= prv->numfaces)\n\t\t{\n\t\t\tCon_Printf (CON_ERROR \"CM_CreatePatchesForLeafs: corrupt map\\n\");\n\t\t\tbreak;\n\t\t}\n\t\tif (!CM_CreatePatchForFace (loadmodel, prv, leaf, k, checkout))\n\t\t\treturn false;\n\t}\n\treturn true;\n}\n\n/*\n=================\nCM_CreatePatchesForLeafs\n=================\n*/\nstatic qboolean CM_CreatePatchesForLeafs (model_t *loadmodel, cminfo_t *prv)\n{\n\tint i, k;\n\tmleaf_t *leaf;\n\tint *checkout = alloca(sizeof(int)*prv->numfaces);\n\n\tif (map_noCurves.ival)\n\t\treturn true;\n\n\tmemset (checkout, -1, sizeof(int)*prv->numfaces);\n\n\tfor (i = prv->numcmodels; i-- > 0; )\n\t{\n\t\tprv->cmodels[i].firstpatch = prv->numpatches;\n\t\tprv->cmodels[i].firstcmesh = prv->numcmeshes;\n\t\tif (i == 0)\n\t\t{\t//worldmodel's leafs\n\t\t\tfor (k = 0, leaf = loadmodel->leafs; k < loadmodel->numleafs; k++, leaf++)\n\t\t\t\tif (!CM_CreatePatchesForLeaf(loadmodel, prv, leaf, checkout))\n\t\t\t\t\treturn false;\n\t\t}\n\t\telse\n\t\t{\t//submodel uni-leaf thing.\n\t\t\tleaf = prv->cmodels[i].headleaf;\n\t\t\tif (leaf)\n\t\t\t{\n\t\t\t\tif (!CM_CreatePatchesForLeaf(loadmodel, prv, leaf, checkout))\n\t\t\t\t\treturn false;\n\t\t\t\tfor (k = 0; k < prv->cmodels[i].numsurfaces; k++)\n\t\t\t\t\tCM_CreatePatchForFace(loadmodel, prv, leaf, prv->cmodels[i].firstsurface+k, checkout);\n\t\t\t}\n\t\t}\n\t\tprv->cmodels[i].num_patches = prv->numpatches-prv->cmodels[i].firstpatch;\n\t\tprv->cmodels[i].num_cmeshes = prv->numcmeshes-prv->cmodels[i].firstcmesh;\n\t}\n\treturn true;\n}\n#endif\n\n\n/*\n===============================================================================\n\n\t\t\t\t\tMAP LOADING\n\n===============================================================================\n*/\n\nstatic void CMod_SetParent (mnode_t *node, mnode_t *parent)\n{\n\tnode->parent = parent;\n\tif (node->contents != -1)\n\t\treturn;\n\tCMod_SetParent (node->children[0], node);\n\tCMod_SetParent (node->children[1], node);\n}\n\n#ifdef Q2BSPS\n/*\n=================\nCMod_LoadSubmodels\n=================\n*/\nstatic qboolean CModQ2_LoadSubmodels (model_t *loadmodel, qbyte *mod_base, lump_t *l)\n{\n\tcminfo_t\t*prv = (cminfo_t*)loadmodel->meshinfo;\n\tq2dmodel_t\t*in;\n\tcmodel_t\t*out;\n\tint\t\t\ti, j, count;\n\n\tin = (void *)(mod_base + l->fileofs);\n\tif (l->filelen % sizeof(*in))\n\t{\n\t\tCon_Printf (CON_ERROR \"MOD_LoadBmodel: funny lump size\\n\");\n\t\treturn false;\n\t}\n\tcount = l->filelen / sizeof(*in);\n\n\tif (count < 1)\n\t{\n\t\tCon_Printf (CON_ERROR \"Map with no models\\n\");\n\t\treturn false;\n\t}\n\tif (count > SANITY_MAX_Q2MAP_MODELS)\n\t{\n\t\tCon_Printf (CON_ERROR \"Map has too many models\\n\");\n\t\treturn false;\n\t}\n\n\tout = prv->cmodels = ZG_Malloc(&loadmodel->memgroup, count * sizeof(*prv->cmodels));\n\tprv->numcmodels = count;\n\n\tfor (i=0 ; i<count ; i++, in++, out++)\n\t{\n\t\tfor (j=0 ; j<3 ; j++)\n\t\t{\t// spread the mins / maxs by a pixel\n\t\t\tout->mins[j] = LittleFloat (in->mins[j]) - 1;\n\t\t\tout->maxs[j] = LittleFloat (in->maxs[j]) + 1;\n\t\t\tout->origin[j] = LittleFloat (in->origin[j]);\n\t\t}\n\t\tout->headnode = loadmodel->nodes + LittleLong (in->headnode);\n\t\tout->firstsurface = LittleLong (in->firstface);\n\t\tout->numsurfaces = LittleLong (in->numfaces);\n\t}\n\n\tAddPointToBounds(prv->cmodels[0].mins, loadmodel->mins, loadmodel->maxs);\n\tAddPointToBounds(prv->cmodels[0].maxs, loadmodel->mins, loadmodel->maxs);\n\n\treturn true;\n}\n\n\n/*\n=================\nCMod_LoadSurfaces\n=================\n*/\nstatic qboolean CModQ2_LoadSurfaces (model_t *mod, qbyte *mod_base, lump_t *l)\n{\n\tcminfo_t\t*prv = (cminfo_t*)mod->meshinfo;\n\tq2texinfo_t\t*in;\n\tq2mapsurface_t\t*out;\n\tint\t\t\ti, count;\n\n\tin = (void *)(mod_base + l->fileofs);\n\tif (l->filelen % sizeof(*in))\n\t{\n\t\tCon_Printf (CON_ERROR \"MOD_LoadBmodel: funny lump size\\n\");\n\t\treturn false;\n\t}\n\tcount = l->filelen / sizeof(*in);\n\tif (count < 1)\n\t{\n\t\tCon_Printf (CON_ERROR \"Map with no surfaces\\n\");\n\t\treturn false;\n\t}\n//\tif (count > MAX_Q2MAP_TEXINFO)\n//\t\tHost_Error (\"Map has too many surfaces\");\n\n\tmod->numtexinfo = count;\n\tout = prv->surfaces = ZG_Malloc(&mod->memgroup, count * sizeof(*prv->surfaces));\n\n\tfor ( i=0 ; i<count ; i++, in++, out++)\n\t{\n\t\tQ_strncpyz (out->c.name, in->texture, sizeof(out->c.name));\n\t\tQ_strncpyz (out->rname, in->texture, sizeof(out->rname));\n\t\tout->c.flags = LittleLong (in->flags);\n\t\tout->c.value = LittleLong (in->value);\n\t}\n\n\treturn true;\n}\n#ifdef HAVE_CLIENT\nstatic texture_t *Mod_LoadWall(model_t *loadmodel, char *mapname, char *texname, char *shadername, unsigned int imageflags)\n{\n\tchar name[MAX_QPATH];\n\tq2miptex_t replacementwal;\n\ttexture_t *tex;\n\tq2miptex_t *wal;\n\timage_t *base;\n\n\tQ_snprintfz (name, sizeof(name), \"textures/%s.wal\", texname);\n\twal = (void *)FS_LoadMallocFile (name, NULL);\n\tif (!wal)\n\t{\n\t\twal = &replacementwal;\n\t\tmemset(wal, 0, sizeof(*wal));\n\t\tQ_strncpyz(wal->name, texname, sizeof(wal->name));\n\t\twal->width = 64;\n\t\twal->height = 64;\n\t}\n\telse\n\t{\n\t\twal->width = LittleLong(wal->width);\n\t\twal->height = LittleLong(wal->height);\n\t}\n\t{\n\t\tint i;\n\n\t\tfor (i = 0; i < MIPLEVELS; i++)\n\t\t\twal->offsets[i] = LittleLong(wal->offsets[i]);\n\t}\n\n\twal->flags = LittleLong(wal->flags);\n\twal->contents = LittleLong(wal->contents);\n\twal->value = LittleLong(wal->value);\n\n\ttex = ZG_Malloc(&loadmodel->memgroup, sizeof(texture_t));\n\n\ttex->vwidth = tex->srcwidth = wal->width;\n\ttex->vheight = tex->srcheight = wal->height;\n\n\tif (!tex->vwidth || !tex->vheight || wal == &replacementwal)\n\t{\n\t\timageflags |= IF_LOADNOW;\t//make sure the size is known BEFORE it returns.\n\t\tif (wal->offsets[0])\n\t\t\tbase = R_LoadReplacementTexture(wal->name, \"bmodels\", imageflags, (qbyte *)wal+wal->offsets[0], wal->width, wal->height, TF_SOLID8);\n\t\telse\n\t\t\tbase = R_LoadHiResTexture(wal->name, \"bmodels\", imageflags);\n\t}\n\telse\n\t\tbase = NULL;\n\n\tif (wal == &replacementwal)\n\t{\n\t\tif (base)\n\t\t{\n\t\t\tif (base->status == TEX_LOADED||base->status==TEX_LOADING)\n\t\t\t{\n\t\t\t\ttex->vwidth = base->width;\n\t\t\t\ttex->vheight = base->height;\n\t\t\t}\n\t\t\telse\n\t\t\t\tCon_Printf(\"Unable to load textures/%s.wal\\n\", wal->name);\n\t\t}\n\n\t}\n\telse\n\t{\n\t\tqbyte *out;\n\t\tunsigned int size = \n\t\t(wal->width>>0)*(wal->height>>0) +\n\t\t(wal->width>>1)*(wal->height>>1) +\n\t\t(wal->width>>2)*(wal->height>>2) +\n\t\t(wal->width>>3)*(wal->height>>3);\n\n\t\ttex->srcdata = out = BZ_Malloc(size);\n\t\ttex->srcfmt = TF_MIP4_8PAL24_T255;\n\t\ttex->palette = q2_palette;\n\t\tmemcpy(out, (qbyte *)wal + wal->offsets[0], (wal->width>>0)*(wal->height>>0));\n\t\tout += (wal->width>>0)*(wal->height>>0);\n\t\tmemcpy(out, (qbyte *)wal + wal->offsets[1], (wal->width>>1)*(wal->height>>1));\n\t\tout += (wal->width>>1)*(wal->height>>1);\n\t\tmemcpy(out, (qbyte *)wal + wal->offsets[2], (wal->width>>2)*(wal->height>>2));\n\t\tout += (wal->width>>2)*(wal->height>>2);\n\t\tmemcpy(out, (qbyte *)wal + wal->offsets[3], (wal->width>>3)*(wal->height>>3));\n\t\tout += (wal->width>>3)*(wal->height>>3);\n\n\t\tBZ_Free(wal);\n\t}\n\n\treturn tex;\n}\n\nstatic qboolean CModQ2_LoadTexInfo (model_t *mod, qbyte *mod_base, lump_t *l, char *mapname)\t//yes I know these load from the same place\n{\n\tq2texinfo_t *in;\n\tmtexinfo_t *out;\n\tint \ti, j, count;\n\tchar\t*lwr;\n\tchar\tsname[MAX_QPATH];\n\tint texcount;\n\n\tin = (void *)(mod_base + l->fileofs);\n\tif (l->filelen % sizeof(*in))\n\t{\n\t\tCon_Printf (\"MOD_LoadBmodel: funny lump size in %s\\n\", mod->name);\n\t\treturn false;\n\t}\n\tcount = l->filelen / sizeof(*in);\n\tout = ZG_Malloc(&mod->memgroup, count*sizeof(*out));\n\n\tmod->textures = ZG_Malloc(&mod->memgroup, sizeof(texture_t *)*count);\n\ttexcount = 0;\n\n\tmod->texinfo = out;\n\tmod->numtexinfo = count;\n\n\tif (in[0].nexttexinfo != -1)\n\t{\n\t\tfor (i = 1; i < count && in[i].nexttexinfo == in[0].nexttexinfo; i++)\n\t\t\t;\n\t\tif (i == count)\n\t\t{\n\t\t\tCon_Printf(\"WARNING: invalid texture animations in \\\"%s\\\"\\n\", mod->name);\n\t\t\tfor (i = 0; i < count; i++) \n\t\t\t\tin[i].nexttexinfo = -1;\n\t\t}\n\t}\n\n\tfor ( i=0 ; i<count ; i++, in++, out++)\n\t{\n\t\tout->flags = LittleLong (in->flags);\n\n\t\tfor (j=0 ; j<4 ; j++)\n\t\t\tout->vecs[0][j] = LittleFloat (in->vecs[0][j]);\n\t\tfor (j=0 ; j<4 ; j++)\n\t\t\tout->vecs[1][j] = LittleFloat (in->vecs[1][j]);\n\t\tout->vecscale[0] = 1.0/Length (out->vecs[0]);\n\t\tout->vecscale[1] = 1.0/Length (out->vecs[1]);\n\n\t\tif (out->flags & TI_SKY)\n\t\t\tQ_snprintfz(sname, sizeof(sname), \"sky/%s\", in->texture);\n\t\telse\n\t\t\tQ_snprintfz(sname, sizeof(sname), \"%s\", in->texture);\n\t\tif (out->flags & (TI_WARP))\n\t\t\tQ_strncatz(sname, \"#WARP\", sizeof(sname));\n\t\tif (out->flags & TI_FLOWING)\n\t\t\tQ_strncatz(sname, \"#FLOW\", sizeof(sname));\n\t\tif (out->flags & (TI_N64_SCROLL_X | TI_N64_SCROLL_Y | TI_N64_SCROLL_FLIP))\n\t\t{\n\t\t\tQ_snprintfz(sname+strlen(sname), sizeof(sname)-strlen(sname), \"#FLOWV=%s%s,%s%s\",\n\t\t\t\t\t\t(out->flags&TI_N64_SCROLL_FLIP)?\"\":\"-\",\n\t\t\t\t\t\t(out->flags&TI_N64_SCROLL_X)?\"1.0\":\"0.0\",\n\t\t\t\t\t\t(out->flags&TI_N64_SCROLL_FLIP)?\"-\":\"\",\n\t\t\t\t\t\t(out->flags&TI_N64_SCROLL_Y)?\"1.0\":\"0.0\"\n\t\t\t\t\t\t);\n\t\t}\n\t\tif (out->flags & TI_TRANS66)\n\t\t\tQ_strncatz(sname, \"#ALPHA=0.66\", sizeof(sname));\n\t\telse if (out->flags & TI_TRANS33)\n\t\t\tQ_strncatz(sname, \"#ALPHA=0.33\", sizeof(sname));\n\t\telse if (out->flags & (TI_KINGPIN_ALPHATEST|TI_Q2EX_ALPHATEST)) //kingpin...\n\t\t\tQ_strncatz(sname, \"#MASK=0.666#MASKLT\", sizeof(sname));\n\t\telse if (out->flags & (TI_WARP))\n\t\t\tQ_strncatz(sname, \"#ALPHA=1\", sizeof(sname));\n\t\tif (in->nexttexinfo != -1)\t//used to ensure non-looping and looping don't conflict and get confused.\n\t\t\tQ_strncatz(sname, \"#ANIMLOOP\", sizeof(sname));\n\n\t\t//in q2, 'TEX_SPECIAL' is TI_LIGHT, and that conflicts.\n\t\tout->flags &= ~TI_LIGHT;\t//TI_LIGHT makes the surface emissive. its for the rad tool, not useful to us, so its safe to just strip it to avoid confusion.\n\t\tif (out->flags & (TI_SKY))\n\t\t\tout->flags |= TEX_SPECIAL;\n\n\t\t//compact the textures.\n\t\tfor (j=0; j < texcount; j++)\n\t\t{\n\t\t\tif (!strcmp(sname, mod->textures[j]->name))\n\t\t\t{\n\t\t\t\tout->texture = mod->textures[j];\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (j == texcount)\t//load a new one\n\t\t{\n\t\t\tfor (lwr = in->texture; *lwr; lwr++)\n\t\t\t{\n\t\t\t\tif (*lwr >= 'A' && *lwr <= 'Z')\n\t\t\t\t\t*lwr = *lwr - 'A' + 'a';\n\t\t\t}\n\t\t\tout->texture = Mod_LoadWall (mod, mapname, in->texture, sname, (out->flags&TEX_SPECIAL)?0:IF_NOALPHA);\n\t\t\tif (!out->texture || !out->texture->srcwidth || !out->texture->srcheight)\n\t\t\t{\n\t\t\t\tout->texture = ZG_Malloc(&mod->memgroup, sizeof(texture_t) + 16*16+8*8+4*4+2*2);\n\n\t\t\t\tCon_Printf (CON_WARNING \"Couldn't load \\\"%s.wal\\\"\\n\", in->texture);\n\t\t\t\tmemcpy(out->texture, r_notexture_mip, sizeof(texture_t) + 16*16+8*8+4*4+2*2);\n\t\t\t}\n\n\t\t\tQ_strncpyz(out->texture->name, sname, sizeof(out->texture->name));\n\n\t\t\tmod->textures[texcount++] = out->texture;\n\t\t}\n\n//\t\tif (in->nexttexinfo != -1)\n//\t\t{\n//\t\t\tCon_DPrintf(\"FIXME: %s should animate to %s\\n\", in->texture, (in->nexttexinfo+(q2texinfo_t *)(mod_base + l->fileofs))->texture);\n//\t\t}\n\t}\n\n\tin = (void *)(mod_base + l->fileofs);\n\tout = mod->texinfo;\n\tfor (i=0 ; i<count ; i++)\n\t{\n\t\tif (in[i].nexttexinfo >= 0 && in[i].nexttexinfo < count)\n\t\t\tout[i].texture->anim_next = out[in[i].nexttexinfo].texture;\n\t}\n\tfor (i=0 ; i<count ; i++)\n\t{\n\t\ttexture_t *tex;\n\t\tif (!out[i].texture->anim_next)\n\t\t\tcontinue;\n\n\t\tout[i].texture->anim_total = 1;\n\t\tfor (tex = out[i].texture->anim_next ; tex && tex != out[i].texture && out[i].texture->anim_total < 100; tex=tex->anim_next)\n\t\t\tout[i].texture->anim_total++;\n\t}\n\n\tmod->numtextures = texcount;\n\n\tMod_SortShaders(mod);\n\treturn true;\n}\n#endif\n/*\nstatic void CalcSurfaceExtents (msurface_t *s)\n{\n\tfloat\tmins[2], maxs[2], val;\n\tint\t\ti,j, e;\n\tmvertex_t\t*v;\n\tmtexinfo_t\t*tex;\n\tint\t\tbmins[2], bmaxs[2];\n\n\tmins[0] = mins[1] = 999999;\n\tmaxs[0] = maxs[1] = -99999;\n\n\ttex = s->texinfo;\n\n\tfor (i=0 ; i<s->numedges ; i++)\n\t{\n\t\te = loadmodel->surfedges[s->firstedge+i];\n\t\tif (e >= 0)\n\t\t\tv = &loadmodel->vertexes[loadmodel->edges[e].v[0]];\n\t\telse\n\t\t\tv = &loadmodel->vertexes[loadmodel->edges[-e].v[1]];\n\n\t\tfor (j=0 ; j<2 ; j++)\n\t\t{\n\t\t\tval = v->position[0] * tex->vecs[j][0] +\n\t\t\t\tv->position[1] * tex->vecs[j][1] +\n\t\t\t\tv->position[2] * tex->vecs[j][2] +\n\t\t\t\ttex->vecs[j][3];\n\t\t\tif (val < mins[j])\n\t\t\t\tmins[j] = val;\n\t\t\tif (val > maxs[j])\n\t\t\t\tmaxs[j] = val;\n\t\t}\n\t}\n\n\tfor (i=0 ; i<2 ; i++)\n\t{\n\t\tbmins[i] = floor(mins[i]/16);\n\t\tbmaxs[i] = ceil(maxs[i]/16);\n\n\t\ts->texturemins[i] = bmins[i] * 16;\n\t\ts->extents[i] = (bmaxs[i] - bmins[i]) * 16;\n\n//\t\tif ( !(tex->flags & TEX_SPECIAL) && s->extents[i] > 512 )// 256 )\n//\t\t\tSys_Error (\"Bad surface extents\");\n\t}\n}*/\n\n/*\n=================\nMod_LoadFaces\n=================\n*/\n#ifdef HAVE_CLIENT\nstatic qboolean CModQ2_LoadFaces (model_t *mod, qbyte *mod_base, lump_t *l, lump_t *lightlump, qboolean lightofsisdouble, bspx_header_t *bspx, qboolean isbig)\n{\n\tdsface_t\t\t*ins = NULL;\n\tdlface_t\t\t*inl = NULL;\n\tmsurface_t \t*out;\n\tint\t\t\ti, count, surfnum;\n\tint\t\t\tplanenum, side;\n\tint\t\t\tti;\n\tint\t\t\tstyle;\n\n\tstruct decoupled_lm_info_s *decoupledlm;\n\tsize_t dcsize;\n\tunsigned int lofs;\n\n\tunsigned short lmshift, lmscale;\n\tchar buf[64];\n\tlightmapoverrides_t overrides = {0};\n\toverrides.defaultshift = LMSHIFT_DEFAULT;\n\n\tif (!isbig && !(l->filelen % sizeof(*ins)))\n\t\tins = (void *)(mod_base + l->fileofs);\n\telse if (isbig && !(l->filelen % sizeof(*inl)))\n\t\tinl = (void *)(mod_base + l->fileofs);\n\telse\n\t{\n\t\tCon_Printf (\"MOD_LoadBmodel: funny lump size in %s\\n\",mod->name);\n\t\treturn false;\n\t}\n\tcount = l->filelen / (isbig?sizeof(*inl):sizeof(*ins));\n\tout = ZG_Malloc(&mod->memgroup, (count+6)*sizeof(*out));\t//spare for skybox\n\n\tmod->surfaces = out;\n\tmod->numsurfaces = count;\n\n\tMod_LoadLighting(mod, bspx, mod_base, lightlump, lightofsisdouble, &overrides, sb_none);\n\tif (overrides.offsets)\n\t\tlmshift = overrides.defaultshift;\n\telse\n\t{\n\t\tlmscale = atoi(Mod_ParseWorldspawnKey(mod, \"lightmap_scale\", buf, sizeof(buf)));\n\t\tif (!lmscale)\n\t\t\tlmshift = LMSHIFT_DEFAULT;\n\t\telse\n\t\t{\n\t\t\tfor(lmshift = 0; lmscale > 1; lmshift++)\n\t\t\t\tlmscale >>= 1;\n\t\t}\n\t}\n\n\tdecoupledlm = BSPX_FindLump(bspx, mod_base, \"DECOUPLED_LM\", &dcsize); //RGB packed data\n\tif (dcsize == count*sizeof(*decoupledlm))\n\t\tmod->facelmvecs = ZG_Malloc(&mod->memgroup, count * sizeof(*mod->facelmvecs));\t//seems good.\n\telse\n\t\tdecoupledlm\t= NULL;\t//wrong size somehow... discard it.\n\n\tfor ( surfnum=0 ; surfnum<count ; surfnum++, out++)\n\t{\n\t\tif (isbig)\n\t\t{\n\t\t\tout->firstedge = LittleLong(inl->firstedge);\n\t\t\tout->numedges = (unsigned int)LittleLong(inl->numedges);\n\t\t\tplanenum = (unsigned int)LittleLong(inl->planenum);\n\t\t\tside = (unsigned int)LittleLong(inl->side);\n\t\t\tti = (unsigned int)LittleLong (inl->texinfo);\n\t\t\tlofs = LittleLong(inl->lightofs);\n\t\t\tfor (i=0 ; i<4 ; i++)\n\t\t\t{\n\t\t\t\tstyle = inl->styles[i];\n\t\t\t\tif (style == 0xff)\n\t\t\t\t\tstyle = INVALID_LIGHTSTYLE;\n\t\t\t\telse if (mod->lightmaps.maxstyle < style)\n\t\t\t\t\tmod->lightmaps.maxstyle = style;\n\t\t\t\tout->styles[i] = style;\n\t\t\t}\n\t\t\tinl++;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tout->firstedge = LittleLong(ins->firstedge);\n\t\t\tout->numedges = (unsigned short)LittleShort(ins->numedges);\n\t\t\tplanenum = (unsigned short)LittleShort(ins->planenum);\n\t\t\tside = (unsigned short)LittleShort(ins->side);\n\t\t\tti = (unsigned short)LittleShort (ins->texinfo);\n\t\t\tlofs = LittleLong(ins->lightofs);\n\t\t\tfor (i=0 ; i<4 ; i++)\n\t\t\t{\n\t\t\t\tstyle = ins->styles[i];\n\t\t\t\tif (style == 0xff)\n\t\t\t\t\tstyle = INVALID_LIGHTSTYLE;\n\t\t\t\telse if (mod->lightmaps.maxstyle < style)\n\t\t\t\t\tmod->lightmaps.maxstyle = style;\n\t\t\t\tout->styles[i] = style;\n\t\t\t}\n\t\t\tins++;\n\t\t}\n\t\tout->plane = mod->planes + planenum;\n\t\tout->flags = 0;\n\t\tif (side)\n\t\t\tout->flags |= SURF_PLANEBACK;\n\t\tif (ti < 0 || ti >= mod->numtexinfo)\n\t\t{\n\t\t\tCon_Printf (CON_ERROR \"MOD_LoadBmodel: bad texinfo number\\n\");\n\t\t\treturn false;\n\t\t}\n\t\tout->texinfo = mod->texinfo + ti;\n\n\t\tif (decoupledlm)\n\t\t{\n\t\t\tlofs = LittleLong(decoupledlm->lmoffset);\n\t\t\tout->texturemins[0] = out->texturemins[1] = 0; // should be handled by the now-per-surface vecs[][3] value.\n\t\t\tout->lmshift = 0;\t//redundant.\n\t\t\tout->extents[0] = (unsigned short)LittleShort(decoupledlm->lmsize[0]) - 1;\n\t\t\tout->extents[1] = (unsigned short)LittleShort(decoupledlm->lmsize[1]) - 1;\n\t\t\tmod->facelmvecs[surfnum].lmvecs[0][0] = LittleFloat(decoupledlm->lmvecs[0][0]);\n\t\t\tmod->facelmvecs[surfnum].lmvecs[0][1] = LittleFloat(decoupledlm->lmvecs[0][1]);\n\t\t\tmod->facelmvecs[surfnum].lmvecs[0][2] = LittleFloat(decoupledlm->lmvecs[0][2]);\n\t\t\tmod->facelmvecs[surfnum].lmvecs[0][3] = LittleFloat(decoupledlm->lmvecs[0][3]) + 0.5f; //sigh\n\t\t\tmod->facelmvecs[surfnum].lmvecs[1][0] = LittleFloat(decoupledlm->lmvecs[1][0]);\n\t\t\tmod->facelmvecs[surfnum].lmvecs[1][1] = LittleFloat(decoupledlm->lmvecs[1][1]);\n\t\t\tmod->facelmvecs[surfnum].lmvecs[1][2] = LittleFloat(decoupledlm->lmvecs[1][2]);\n\t\t\tmod->facelmvecs[surfnum].lmvecs[1][3] = LittleFloat(decoupledlm->lmvecs[1][3]) + 0.5f; //sigh\n\t\t\tmod->facelmvecs[surfnum].lmvecscale[0] = 1.0f/Length(mod->facelmvecs[surfnum].lmvecs[0]);\t//luxels->qu\n\t\t\tmod->facelmvecs[surfnum].lmvecscale[1] = 1.0f/Length(mod->facelmvecs[surfnum].lmvecs[1]);\n\t\t\tdecoupledlm++;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (overrides.shifts)\n\t\t\t\tout->lmshift = overrides.shifts[surfnum];\n\t\t\telse\n\t\t\t\tout->lmshift = lmshift;\n\n\t\t\tif (overrides.offsets)\n\t\t\t\tlofs = overrides.offsets[surfnum];\n\n\t\t\tCalcSurfaceExtents (mod, out);\n\t\t\tif (overrides.extents)\n\t\t\t{\n\t\t\t\tout->extents[0] = overrides.extents[surfnum*2+0];\n\t\t\t\tout->extents[1] = overrides.extents[surfnum*2+1];\n\t\t\t}\n\t\t}\n\n\t// lighting info\n\t\tif (overrides.styles16)\n\t\t{\n\t\t\tfor (i=0 ; i<overrides.stylesperface ; i++)\n\t\t\t{\n\t\t\t\tstyle = overrides.styles16[surfnum*overrides.stylesperface+i];\n\t\t\t\tif (style == 0xffff)\n\t\t\t\t\tstyle = INVALID_LIGHTSTYLE;\n\t\t\t\telse if (mod->lightmaps.maxstyle < style)\n\t\t\t\t\tmod->lightmaps.maxstyle = style;\n\t\t\t\tout->styles[i] = style;\n\t\t\t}\n\t\t}\n\t\telse if (overrides.styles8)\n\t\t{\n\t\t\tfor (i=0 ; i<overrides.stylesperface ; i++)\n\t\t\t{\n\t\t\t\tstyle = overrides.styles8[surfnum*overrides.stylesperface+i];\n\t\t\t\tif (style == 0xff)\n\t\t\t\t\tstyle = INVALID_LIGHTSTYLE;\n\t\t\t\telse if (mod->lightmaps.maxstyle < style)\n\t\t\t\t\tmod->lightmaps.maxstyle = style;\n\t\t\t\tout->styles[i] = style;\n\t\t\t}\n\t\t}\n\t\tfor ( ; i<MAXCPULIGHTMAPS ; i++)\n\t\t\tout->styles[i] = INVALID_LIGHTSTYLE;\n\t\tif (lofs == ~0u)\n\t\t\tout->samples = NULL;\n\t\telse\n\t\t{\n\t\t\tif (lightofsisdouble)\n\t\t\t\tlofs /= 2;\n\t\t\tif (mod->lightmaps.fmt == LM_E5BGR9)\n\t\t\t\tlofs = lofs / 3 * 4;\n\t\t\tout->samples = mod->lightdata + lofs;\n\t\t}\n\n\t// set the drawing flags\n\n\n\t\tif (out->texinfo->flags & TI_SKY)\n\t\t\tout->flags |= SURF_DRAWSKY|SURF_DRAWTILED;\n\t\tif (out->texinfo->flags & TI_WARP)\n\t\t{\n\t\t\tout->flags |= SURF_DRAWTURB;\n\t\t\tif (out->styles[0]==0&&out->styles[1]==0&&out->styles[2]==0&&out->styles[3]==0)\n\t\t\t\tout->flags |= SURF_DRAWTILED;\t//normally you won't get the same lightmap 4 times over... assume uninitialised, and therefore unlit.\n\t\t\telse if (!strstr(out->texinfo->texture->name, \"#LIT\"))\n\t\t\t\tQ_strncatz(out->texinfo->texture->name, \"#LIT\", sizeof(out->texinfo->texture->name));\n\t\t}\n\t\tif (out->flags & SURF_DRAWTILED)\n\t\t{\t//shouldn't have been lit...\n\t\t\tfor (i=0 ; i<2 ; i++)\n\t\t\t{\n\t\t\t\tout->extents[i] = 16384;\n\t\t\t\tout->texturemins[i] = -8192;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn true;\n}\n#endif\n\n/*\n=================\nCMod_LoadNodes\n\n=================\n*/\nstatic qboolean CModQ2_LoadNodes (model_t *mod, qbyte *mod_base, lump_t *l, qboolean isbig)\n{\n\tq2dsnode_t\t\t*ins;\n\tdl2node_t\t\t*inl;\n\tint\t\t\tchild;\n\tmnode_t\t\t*out;\n\tint\t\t\ti, j, count;\n\n\tif (l->filelen % (isbig?sizeof(*inl):sizeof(*ins)))\n\t{\n\t\tCon_Printf (CON_ERROR \"MOD_LoadBmodel: funny lump size\\n\");\n\t\treturn false;\n\t}\n\tcount = l->filelen / (isbig?sizeof(*inl):sizeof(*ins));\n\n\tif (count < 1)\n\t{\n\t\tCon_Printf (CON_ERROR \"Map has no nodes\\n\");\n\t\treturn false;\n\t}\n\tif (count > SANITY_LIMIT(*out))\n\t{\n\t\tCon_Printf (CON_ERROR \"Map has too many nodes\\n\");\n\t\treturn false;\n\t}\n\n\tout = ZG_Malloc(&mod->memgroup, sizeof(mnode_t)*count);\n\n\tmod->nodes = out;\n\tmod->numnodes = count;\n\n\tif (isbig)\n\t{\n\t\tinl = (void *)(mod_base + l->fileofs);\n\t\tfor (i=0 ; i<count ; i++, out++, inl++)\n\t\t{\n\t\t\tfor (j=0 ; j<3 ; j++)\n\t\t\t{\n\t\t\t\tout->minmaxs[j] = LittleFloat (inl->mins[j]);\n\t\t\t\tout->minmaxs[3+j] = LittleFloat (inl->maxs[j]);\n\t\t\t}\n\n\t\t\tout->plane = mod->planes + LittleLong(inl->planenum);\n\n\t\t\tout->firstsurface = (unsigned int)LittleLong (inl->firstface);\n\t\t\tout->numsurfaces = (unsigned int)LittleLong (inl->numfaces);\n\t\t\tout->contents = -1;\t// differentiate from leafs\n\n\t\t\tfor (j=0 ; j<2 ; j++)\n\t\t\t{\n\t\t\t\tchild = LittleLong (inl->children[j]);\n\t\t\t\tout->childnum[j] = child;\n\t\t\t\tif (child < 0)\n\t\t\t\t\tout->children[j] = (mnode_t *)(mod->leafs + -1-child);\n\t\t\t\telse\n\t\t\t\t\tout->children[j] = mod->nodes + child;\n\t\t\t}\n\t\t}\n\t}\n\telse\n\t{\n\t\tins = (void *)(mod_base + l->fileofs);\n\t\tfor (i=0 ; i<count ; i++, out++, ins++)\n\t\t{\n\t\t\tfor (j=0 ; j<3 ; j++)\n\t\t\t{\n\t\t\t\tout->minmaxs[j] = LittleShort (ins->mins[j]);\n\t\t\t\tout->minmaxs[3+j] = LittleShort (ins->maxs[j]);\n\t\t\t}\n\n\t\t\tout->plane = mod->planes + LittleLong(ins->planenum);\n\n\t\t\tout->firstsurface = (unsigned short)LittleShort (ins->firstface);\n\t\t\tout->numsurfaces = (unsigned short)LittleShort (ins->numfaces);\n\t\t\tout->contents = -1;\t// differentiate from leafs\n\n\t\t\tfor (j=0 ; j<2 ; j++)\n\t\t\t{\n\t\t\t\tchild = LittleLong (ins->children[j]);\n\t\t\t\tout->childnum[j] = child;\n\t\t\t\tif (child < 0)\n\t\t\t\t\tout->children[j] = (mnode_t *)(mod->leafs + -1-child);\n\t\t\t\telse\n\t\t\t\t\tout->children[j] = mod->nodes + child;\n\t\t\t}\n\t\t}\n\t}\n\n\tCMod_SetParent (mod->nodes, NULL);\t// sets nodes and leafs\n\n\treturn true;\n}\n\n/*\n=================\nCMod_LoadBrushes\n\n=================\n*/\nstatic qboolean CModQ2_LoadBrushes (model_t *mod, qbyte *mod_base, lump_t *l)\n{\n\tcminfo_t\t*prv = (cminfo_t*)mod->meshinfo;\n\tq2dbrush_t\t*in;\n\tq2cbrush_t\t*out;\n\tint\t\t\ti, count;\n\n\tin = (void *)(mod_base + l->fileofs);\n\tif (l->filelen % sizeof(*in))\n\t{\n\t\tCon_Printf (CON_ERROR \"MOD_LoadBmodel: funny lump size\\n\");\n\t\treturn false;\n\t}\n\tcount = l->filelen / sizeof(*in);\n\n\tif (count > SANITY_MAX_MAP_BRUSHES)\n\t{\n\t\tCon_Printf (CON_ERROR \"Map has too many brushes\");\n\t\treturn false;\n\t}\n\n\tprv->brushes = ZG_Malloc(&mod->memgroup, sizeof(*out) * (count+1));\n\n\tout = prv->brushes;\n\n\tprv->numbrushes = count;\n\n\tfor (i=0 ; i<count ; i++, out++, in++)\n\t{\n\t\t//FIXME: missing bounds checks\n\t\tout->brushside = &prv->brushsides[LittleLong(in->firstside)];\n\t\tout->numsides = LittleLong(in->numsides);\n\t\tout->contents = LittleLong(in->contents);\n\t\tCM_FinalizeBrush(out);\n\t}\n\n\treturn true;\n}\n\n/*\n=================\nCMod_LoadLeafs\n=================\n*/\nstatic qboolean CModQ2_LoadLeafs (model_t *mod, qbyte *mod_base, lump_t *l, qboolean isbig)\n{\n\tint\t\t\ti, j;\n\tmleaf_t\t\t*out;\n\tq2dsleaf_t \t*ins;\n\tq2dlleaf_t \t*inl;\n\tint\t\t\tcount;\n\n\tif (l->filelen % (isbig?sizeof(*inl):sizeof(*ins)))\n\t{\n\t\tCon_Printf (CON_ERROR \"MOD_LoadBmodel: funny lump size\\n\");\n\t\treturn false;\n\t}\n\tcount = l->filelen / (isbig?sizeof(*inl):sizeof(*ins));\n\n\tif (count < 1)\n\t{\n\t\tCon_Printf (CON_ERROR \"Map with no leafs\\n\");\n\t\treturn false;\n\t}\n\t// need to save space for box planes\n\tif (count > SANITY_LIMIT(*out))\n\t{\n\t\tCon_Printf (CON_ERROR \"Map has too many leafs\\n\");\n\t\treturn false;\n\t}\n\n\tout = ZG_Malloc(&mod->memgroup, sizeof(*out) * (count+1));\n\tmod->numclusters = 0;\n\n\tmod->leafs = out;\n\tmod->numleafs = count;\n\n\tmemset(out, 0, sizeof(*out)*count);\n\tif (isbig)\n\t{\n\t\tinl = (void *)(mod_base + l->fileofs);\n\t\tfor ( i=0 ; i<count ; i++, inl++, out++)\n\t\t{\n\t\t\tfor (j=0 ; j<3 ; j++)\n\t\t\t{\n\t\t\t\tout->minmaxs[j] = LittleFloat (inl->mins[j]);\n\t\t\t\tout->minmaxs[3+j] = LittleFloat (inl->maxs[j]);\n\t\t\t}\n\n\t\t\tout->contents = LittleLong (inl->contents);\n\t\t\tout->cluster = (unsigned int)LittleLong (inl->cluster);\n\t\t\tif (out->cluster == 0xffffffff)\n\t\t\t\tout->cluster = -1;\n\n\t\t\tout->area = (unsigned int)LittleLong (inl->area);\n\t\t\tout->firstleafbrush = (unsigned int)LittleLong (inl->firstleafbrush);\n\t\t\tout->numleafbrushes = (unsigned int)LittleLong (inl->numleafbrushes);\n\n\t\t\tout->firstmarksurface = mod->marksurfaces +\n\t\t\t\t(unsigned int)LittleLong(inl->firstleafface);\n\t\t\tout->nummarksurfaces = (unsigned int)LittleLong(inl->numleaffaces);\n\n\t\t\tif (out->cluster >= mod->numclusters)\n\t\t\t\tmod->numclusters = out->cluster + 1;\n\t\t}\n\t}\n\telse\n\t{\n\t\tins = (void *)(mod_base + l->fileofs);\n\t\tfor ( i=0 ; i<count ; i++, ins++, out++)\n\t\t{\n\t\t\tfor (j=0 ; j<3 ; j++)\n\t\t\t{\n\t\t\t\tout->minmaxs[j] = LittleShort (ins->mins[j]);\n\t\t\t\tout->minmaxs[3+j] = LittleShort (ins->maxs[j]);\n\t\t\t}\n\n\t\t\tout->contents = LittleLong (ins->contents);\n\t\t\tout->cluster = (unsigned short)LittleShort (ins->cluster);\n\t\t\tif (out->cluster == 0xffff)\n\t\t\t\tout->cluster = -1;\n\n\t\t\tout->area = (unsigned short)LittleShort (ins->area);\n\t\t\tout->firstleafbrush = (unsigned short)LittleShort (ins->firstleafbrush);\n\t\t\tout->numleafbrushes = (unsigned short)LittleShort (ins->numleafbrushes);\n\n\t\t\tout->firstmarksurface = mod->marksurfaces +\n\t\t\t\t(unsigned short)LittleShort(ins->firstleafface);\n\t\t\tout->nummarksurfaces = (unsigned short)LittleShort(ins->numleaffaces);\n\n\t\t\tif (out->cluster >= mod->numclusters)\n\t\t\t\tmod->numclusters = out->cluster + 1;\n\t\t}\n\t}\n\tout = mod->leafs;\n\tmod->pvsbytes = ((mod->numclusters + 31)>>3)&~3;\n\n\tif (out[0].contents != Q2CONTENTS_SOLID)\n\t{\n\t\tCon_Printf (CON_ERROR \"Map leaf 0 is not CONTENTS_SOLID\\n\");\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\n/*\n=================\nCMod_LoadPlanes\n=================\n*/\nstatic qboolean CModQ2_LoadPlanes (model_t *mod, qbyte *mod_base, lump_t *l)\n{\n\tint\t\t\ti, j;\n\tmplane_t\t*out;\n\tdplane_t \t*in;\n\tint\t\t\tcount;\n\tint\t\t\tbits;\n\n\tin = (void *)(mod_base + l->fileofs);\n\tif (l->filelen % sizeof(*in))\n\t{\n\t\tCon_Printf (CON_ERROR \"MOD_LoadBmodel: funny lump size\\n\");\n\t\treturn false;\n\t}\n\tcount = l->filelen / sizeof(*in);\n\n\tif (count < 1)\n\t{\n\t\tCon_Printf (CON_ERROR \"Map with no planes\\n\");\n\t\treturn false;\n\t}\n\t// need to save space for box planes\n\tif (count > SANITY_LIMIT(*out))\n\t{\n\t\tCon_Printf (CON_ERROR \"Map has too many planes (%i)\\n\", count);\n\t\treturn false;\n\t}\n\n\tmod->planes = out = ZG_Malloc(&mod->memgroup, sizeof(*out) * count);\n\tmod->numplanes = count;\n\n\tfor ( i=0 ; i<count ; i++, in++, out++)\n\t{\n\t\tbits = 0;\n\t\tfor (j=0 ; j<3 ; j++)\n\t\t{\n\t\t\tout->normal[j] = LittleFloat (in->normal[j]);\n\t\t\tif (out->normal[j] < 0)\n\t\t\t\tbits |= 1<<j;\n\t\t}\n\n\t\tout->dist = LittleFloat (in->dist);\n\t\tout->type = LittleLong (in->type);\n\t\tout->signbits = bits;\n\t}\n\n\treturn true;\n}\n\n/*\n=================\nCMod_LoadLeafBrushes\n=================\n*/\nstatic qboolean CModQ2_LoadLeafBrushes (model_t *mod, qbyte *mod_base, lump_t *l, qboolean isbig)\n{\n\tcminfo_t\t*prv = (cminfo_t*)mod->meshinfo;\n\tint\t\t\ti;\n\tq2cbrush_t\t**out;\n\tunsigned short \t*ins;\n\tunsigned int \t*inl;\n\tint\t\t\tcount;\n\n\tif (l->filelen % (isbig?sizeof(*inl):sizeof(*ins)))\n\t{\n\t\tCon_Printf (CON_ERROR \"MOD_LoadBmodel: funny lump size\\n\");\n\t\treturn false;\n\t}\n\tcount = l->filelen / (isbig?sizeof(*inl):sizeof(*ins));\n\n\tif (count < 1)\n\t{\n\t\tCon_Printf (CON_ERROR \"Map with no planes\\n\");\n\t\treturn false;\n\t}\n\t// need to save space for box planes\n\tif (count > SANITY_MAX_MAP_LEAFBRUSHES)\n\t{\n\t\tCon_Printf (CON_ERROR \"Map has too many leafbrushes\\n\");\n\t\treturn false;\n\t}\n\n\t//prv->numbrushes is because of submodels being weird.\n\tout = prv->leafbrushes = ZG_Malloc(&mod->memgroup, sizeof(*out) * (count+prv->numbrushes));\n\tprv->numleafbrushes = count;\n\n\tif (isbig)\n\t{\n\t\tinl = (void *)(mod_base + l->fileofs);\n\t\tfor ( i=0 ; i<count ; i++, inl++, out++)\n\t\t\t*out = prv->brushes + (unsigned int)LittleLong (*inl);\n\t}\n\telse\n\t{\n\t\tins = (void *)(mod_base + l->fileofs);\n\t\tfor ( i=0 ; i<count ; i++, ins++, out++)\n\t\t\t*out = prv->brushes + (unsigned short)(short)LittleShort (*ins);\n\t}\n\n\treturn true;\n}\n\n/*\n=================\nCMod_LoadBrushSides\n=================\n*/\nstatic qboolean CModQ2_LoadBrushSides (model_t *mod, qbyte *mod_base, lump_t *l, qboolean isbig)\n{\n\tcminfo_t\t*prv = (cminfo_t*)mod->meshinfo;\n\tunsigned int\t\t\ti, j;\n\tq2cbrushside_t\t*out;\n\tq2dsbrushside_t \t*ins;\n\tq2dlbrushside_t \t*inl;\n\tint\t\t\tcount;\n\tint\t\t\tnum;\n\n\tif (l->filelen % (isbig?sizeof(*inl):sizeof(*ins)))\n\t{\n\t\tCon_Printf (CON_ERROR \"MOD_LoadBmodel: funny lump size\\n\");\n\t\treturn false;\n\t}\n\tcount = l->filelen / (isbig?sizeof(*inl):sizeof(*ins));\n\n\t// need to save space for box planes\n\tif (count > SANITY_MAX_MAP_BRUSHSIDES)\n\t{\n\t\tCon_Printf (CON_ERROR \"Map has too many brushsides (%i)\\n\", count);\n\t\treturn false;\n\t}\n\n\tout = prv->brushsides = ZG_Malloc(&mod->memgroup, sizeof(*out) * count);\n\tprv->numbrushsides = count;\n\n\tif (isbig)\n\t{\n\t\tinl = (void *)(mod_base + l->fileofs);\n\t\tfor ( i=0 ; i<count ; i++, inl++, out++)\n\t\t{\n\t\t\tnum = (unsigned int)LittleLong (inl->planenum);\n\t\t\tout->plane = &mod->planes[num];\n\t\t\tj = (unsigned int)LittleLong (inl->texinfo);\n\t\t\tif (j >= mod->numtexinfo)\n\t\t\t\tout->surface = &nullsurface;\n\t\t\telse\n\t\t\t\tout->surface = &prv->surfaces[j];\n\t\t}\n\t}\n\telse\n\t{\n\t\tins = (void *)(mod_base + l->fileofs);\n\t\tfor ( i=0 ; i<count ; i++, ins++, out++)\n\t\t{\n\t\t\tnum = (unsigned short)LittleShort (ins->planenum);\n\t\t\tout->plane = &mod->planes[num];\n\t\t\tj = (unsigned short)LittleShort (ins->texinfo);\n\t\t\tif (j >= mod->numtexinfo)\n\t\t\t\tout->surface = &nullsurface;\n\t\t\telse\n\t\t\t\tout->surface = &prv->surfaces[j];\n\t\t}\n\t}\n\n\treturn true;\n}\n\n/*\n=================\nCMod_LoadAreas\n=================\n*/\nstatic qboolean CModQ2_LoadAreas (model_t *mod, qbyte *mod_base, lump_t *l)\n{\n\tcminfo_t\t*prv = (cminfo_t*)mod->meshinfo;\n\tint\t\t\ti;\n\tq2carea_t\t*out;\n\tq2darea_t \t*in;\n\tint\t\t\tcount;\n\n\tin = (void *)(mod_base + l->fileofs);\n\tif (l->filelen % sizeof(*in))\n\t{\n\t\tCon_Printf (CON_ERROR \"MOD_LoadBmodel: funny lump size\\n\");\n\t\treturn false;\n\t}\n\tcount = l->filelen / sizeof(*in);\n\n\tif (count > MAX_Q2MAP_AREAS)\n\t{\n\t\tCon_Printf (CON_ERROR \"Map has too many areas\\n\");\n\t\treturn false;\n\t}\n\n\tout = prv->q2areas = ZG_Malloc(&mod->memgroup, sizeof(*out) * count);\n\tprv->numareas = count;\n\n\tfor ( i=0 ; i<count ; i++, in++, out++)\n\t{\n\t\tout->numareaportals = LittleLong (in->numareaportals);\n\t\tout->firstareaportal = LittleLong (in->firstareaportal);\n\t}\n\n\treturn true;\n}\n\n/*\n=================\nCMod_LoadAreaPortals\n=================\n*/\nstatic qboolean CModQ2_LoadAreaPortals (model_t *mod, qbyte *mod_base, lump_t *l)\n{\n\tcminfo_t\t*prv = (cminfo_t*)mod->meshinfo;\n\tint\t\t\ti;\n\tq2dareaportal_t\t\t*out;\n\tq2dareaportal_t \t*in;\n\tint\t\t\tcount;\n\n\tin = (void *)(mod_base + l->fileofs);\n\tif (l->filelen % sizeof(*in))\n\t{\n\t\tCon_Printf (CON_ERROR \"MOD_LoadBmodel: funny lump size\\n\");\n\t\treturn false;\n\t}\n\tcount = l->filelen / sizeof(*in);\n\n\tif (count > MAX_Q2MAP_AREAS)\n\t{\n\t\tCon_Printf (CON_ERROR \"Map has too many areas\\n\");\n\t\treturn false;\n\t}\n\n\tout = prv->q2areaportals = ZG_Malloc(&mod->memgroup, sizeof(*out) * count);\n\tprv->numq2areaportals = count;\n\n\tfor ( i=0 ; i<count ; i++, in++, out++)\n\t{\n\t\tout->portalnum = LittleLong (in->portalnum);\n\t\tout->otherarea = LittleLong (in->otherarea);\n\t}\n\n\treturn true;\n}\n\n/*\n=================\nCMod_LoadVisibility\n=================\n*/\nstatic qboolean CModQ2_LoadVisibility (model_t *mod, qbyte *mod_base, lump_t *l)\n{\n\tcminfo_t\t*prv = (cminfo_t*)mod->meshinfo;\n\tint\t\ti;\n\n\tprv->numvisibility = l->filelen;\n//\tif (l->filelen > MAX_Q2MAP_VISIBILITY)\n//\t{\n//\t\tCon_Printf (CON_ERROR \"Map has too large visibility lump\\n\");\n//\t\treturn false;\n//\t}\n\n\tprv->q2vis = ZG_Malloc(&mod->memgroup, l->filelen);\n\tmemcpy (prv->q2vis, mod_base + l->fileofs, l->filelen);\n\n\tmod->vis = prv->q2vis;\n\n\tprv->q2vis->numclusters = LittleLong (prv->q2vis->numclusters);\n\tfor (i=0 ; i<prv->q2vis->numclusters ; i++)\n\t{\n\t\tprv->q2vis->bitofs[i][0] = LittleLong (prv->q2vis->bitofs[i][0]);\n\t\tprv->q2vis->bitofs[i][1] = LittleLong (prv->q2vis->bitofs[i][1]);\n\t}\n\tmod->numclusters = prv->q2vis->numclusters;\n\tmod->pvsbytes = ((mod->numclusters + 31)>>3)&~3;\n\n\treturn true;\n}\n#endif\t//q2bsps\n\n#ifdef Q3BSPS\nstatic qboolean CModQ3_LoadMarksurfaces (model_t *loadmodel, qbyte *mod_base, lump_t *l)\n{\n\tint\t\ti, j, count;\n\tint\t\t*in;\n\tmsurface_t **out;\n\n\tin = (void *)(mod_base + l->fileofs);\n\tif (l->filelen % sizeof(*in))\n\t{\n\t\tCon_Printf (CON_ERROR \"CModQ3_LoadMarksurfaces: funny lump size in %s\\n\",loadmodel->name);\n\t\treturn false;\n\t}\n\tcount = l->filelen / sizeof(*in);\n\tout = ZG_Malloc(&loadmodel->memgroup, count*sizeof(*out));\n\n\tloadmodel->marksurfaces = out;\n\tloadmodel->nummarksurfaces = count;\n\n\tfor ( i=0 ; i<count ; i++)\n\t{\n\t\tj = LittleLong(in[i]);\n\t\tif (j < 0 || j >= loadmodel->numsurfaces)\n\t\t{\n\t\t\tCon_Printf (CON_ERROR \"Mod_ParseMarksurfaces: bad surface number\\n\");\n\t\t\treturn false;\n\t\t}\n\t\tout[i] = loadmodel->surfaces + j;\n\t}\n\n\treturn true;\n}\n\nstatic qboolean CModQ3_LoadSubmodels (model_t *mod, qbyte *mod_base, lump_t *l)\n{\n\tcminfo_t\t*prv = (cminfo_t*)mod->meshinfo;\n\tq3dmodel_t\t*in;\n\tcmodel_t\t*out;\n\tint\t\t\ti, j, count;\n\tq2cbrush_t **leafbrush;\n\tmleaf_t\t\t*bleaf;\n\n\tin = (void *)(mod_base + l->fileofs);\n\tif (l->filelen % sizeof(*in))\n\t{\n\t\tCon_Printf (CON_ERROR \"MOD_LoadBmodel: funny lump size\\n\");\n\t\treturn false;\n\t}\n\tcount = l->filelen / sizeof(*in);\n\n\tif (count < 1)\n\t{\n\t\tCon_Printf (CON_ERROR \"Map with no models\\n\");\n\t\treturn false;\n\t}\n\tif (count > SANITY_MAX_Q2MAP_MODELS)\n\t{\n\t\tCon_Printf (CON_ERROR \"Map has too many models\\n\");\n\t\treturn false;\n\t}\n\n\tout = prv->cmodels = ZG_Malloc(&mod->memgroup, count * sizeof(*prv->cmodels));\n\tprv->numcmodels = count;\n\n\tif (count > 1)\n\t\tbleaf = ZG_Malloc(&mod->memgroup, (count-1) * sizeof(*bleaf));\n\telse\n\t\tbleaf = NULL;\n\n\tprv->mapisq3 = true;\n\n\tfor (i=0 ; i<count ; i++, in++, out++)\n\t{\n\t\tfor (j=0 ; j<3 ; j++)\n\t\t{\t// spread the mins / maxs by a pixel\n\t\t\tout->mins[j] = LittleFloat (in->mins[j]) - 1;\n\t\t\tout->maxs[j] = LittleFloat (in->maxs[j]) + 1;\n\t\t\tout->origin[j] = (out->maxs[j] + out->mins[j])/2;\n\t\t}\n\t\tout->firstsurface = LittleLong (in->firstsurface);\n\t\tout->numsurfaces = LittleLong (in->num_surfaces);\n\n\t\tout->firstbrush = LittleLong(in->firstbrush);\n\t\tout->num_brushes = LittleLong(in->num_brushes);\n\t\tif (!i)\n\t\t{\n\t\t\tout->headnode = mod->nodes;\n\t\t\tout->headleaf = NULL;\n\t\t}\n\t\telse\n\t\t{\n//create a new leaf to hold the brushes and be directly clipped\n\t\t\tout->headleaf = bleaf;\n\t\t\tout->headnode = NULL;\n\n\t\t\tbleaf->numleafbrushes = LittleLong ( in->num_brushes );\n\t\t\tbleaf->firstleafbrush = prv->numleafbrushes;\n\t\t\tbleaf->contents = 0;\n\n\t\t\tleafbrush = &prv->leafbrushes[prv->numleafbrushes];\n\t\t\tfor ( j = 0; j < bleaf->numleafbrushes; j++, leafbrush++ )\n\t\t\t{\n\t\t\t\t*leafbrush = prv->brushes + LittleLong ( in->firstbrush ) + j;\n\t\t\t\tbleaf->contents |= (*leafbrush)->contents;\n\t\t\t}\n\t\t\tprv->numleafbrushes += bleaf->numleafbrushes;\n\t\t\tbleaf++;\n\t\t}\n\t\t//submodels\n\t}\n\n\tAddPointToBounds(prv->cmodels[0].mins, mod->mins, mod->maxs);\n\tAddPointToBounds(prv->cmodels[0].maxs, mod->mins, mod->maxs);\n\n\treturn true;\n}\n\nstatic qboolean CModQ3_LoadShaders (model_t *mod, qbyte *mod_base, lump_t *l)\n{\n\tcminfo_t\t*prv = (cminfo_t*)mod->meshinfo;\n\tdq3shader_t\t*in;\n\tq2mapsurface_t\t*out;\n\tint\t\t\t\ti, count;\n\ttexture_t *tex;\n\n\tin = (void *)(mod_base + l->fileofs);\n\tif (l->filelen % sizeof(*in))\n\t{\n\t\tCon_Printf (CON_ERROR \"MOD_LoadBmodel: funny lump size\\n\");\n\t\treturn false;\n\t}\n\tcount = l->filelen / sizeof(*in);\n\n\tif (count < 1)\n\t{\n\t\tCon_Printf (CON_ERROR \"Map with no shaders\\n\");\n\t\treturn false;\n\t}\n//\telse if (count > MAX_Q2MAP_TEXINFO)\n//\t\tHost_Error (\"Map has too many shaders\");\n\n\tmod->numtexinfo = count;\n\tout = prv->surfaces = ZG_Malloc(&mod->memgroup, count*sizeof(*out));\n\n\tmod->textures = ZG_Malloc(&mod->memgroup, (sizeof(texture_t*)+sizeof(mtexinfo_t)+sizeof(texture_t))*(count*2+1));\t//+1 is 'noshader' for flares.\n\tmod->texinfo = (mtexinfo_t*)(mod->textures+(count*2+1));\n\ttex = (texture_t*)(mod->texinfo+(count*2+1));\n\tmod->numtextures = count*2+1;\n\n\tfor ( i=0 ; i<count ; i++, in++, out++ )\n\t{\n\t\tout->c.flags = LittleLong ( in->surfflags );\n\t\tout->c.value = LittleLong ( in->contents );\n\n\t\tmod->texinfo[i].texture = tex+i;\n\t\tmod->texinfo[i].flags = prv->surfaces[i].c.flags;\n\t\tQ_strncpyz(mod->texinfo[i].texture->name, in->shadername, sizeof(mod->texinfo[i].texture->name));\n\t\tmod->textures[i] = mod->texinfo[i].texture;\n\t}\n\tfor ( i=0, in-=count ; i<count ; i++, in++ )\n\t{\n\t\tmod->texinfo[i+count].texture = tex+i+count;\n\t\tmod->texinfo[i+count].flags = prv->surfaces[i].c.flags;\n\t\tQ_strncpyz(mod->texinfo[i+count].texture->name, in->shadername, sizeof(mod->texinfo[i+count].texture->name));\n\t\tmod->textures[i+count] = mod->texinfo[i+count].texture;\n\t}\n\n\t//and for flares, which are not supported at this time.\n\tmod->texinfo[count*2].texture = tex+count*2;\n\tmod->texinfo[i+count].flags = 0;\n\tQ_strncpyz(mod->texinfo[count*2].texture->name, \"noshader\", sizeof(mod->texinfo[count*2].texture->name));\n\tmod->textures[count*2] = mod->texinfo[count*2].texture;\n\n\treturn true;\n}\n\nstatic qboolean CModQ3_LoadVertexes (model_t *mod, qbyte *mod_base, lump_t *l)\n{\n\tcminfo_t\t*prv = (cminfo_t*)mod->meshinfo;\n\tq3dvertex_t\t*in;\n\tvecV_t\t\t*out;\n\tvec3_t\t\t*nout;\n\t//, *sout, *tout;\n\tint\t\t\ti, count, j;\n\tvec2_t\t\t*lmout, *stout;\n\tvec4_t *cout;\n\textern cvar_t gl_overbright;\n\textern qbyte lmgamma[256];\n\n\tin = (void *)(mod_base + l->fileofs);\n\tif (l->filelen % sizeof(*in))\n\t{\n\t\tCon_Printf (CON_ERROR \"CMOD_LoadVertexes: funny lump size\\n\");\n\t\treturn false;\n\t}\n\tcount = l->filelen / sizeof(*in);\n\n\tif (count > MAX_Q3MAP_VERTEXES)\n\t{\n\t\tCon_Printf (CON_ERROR \"Map has too many vertexes\\n\");\n\t\treturn false;\n\t}\n\n\tBuildLightMapGammaTable(1, 1<<(2-gl_overbright.ival));\n\n\tout = ZG_Malloc(&mod->memgroup, count*sizeof(*out));\n\tstout = ZG_Malloc(&mod->memgroup, count*sizeof(*stout));\n\tlmout = ZG_Malloc(&mod->memgroup, count*sizeof(*lmout));\n\tcout = ZG_Malloc(&mod->memgroup, count*sizeof(*cout));\n\tnout = ZG_Malloc(&mod->memgroup, count*sizeof(*nout));\n//\tsout = ZG_Malloc(&mod->memgroup, count*sizeof(*nout));\n//\ttout = ZG_Malloc(&mod->memgroup, count*sizeof(*nout));\n\tprv->verts = out;\n\tprv->vertstmexcoords = stout;\n\tfor (i = 0; i < MAXRLIGHTMAPS; i++)\n\t{\n\t\tprv->vertlstmexcoords[i] = lmout;\n\t\tprv->colors4f_array[i] = cout;\n\t}\n\tprv->normals_array = nout;\n//\tprv->svector_array = sout;\n//\tprv->tvector_array = tout;\n\tprv->numvertexes = count;\n\n\tfor ( i=0 ; i<count ; i++, in++)\n\t{\n\t\tfor ( j=0 ; j < 3 ; j++)\n\t\t{\n\t\t\tout[i][j] = LittleFloat ( in->point[j] );\n\t\t\tnout[i][j] = LittleFloat (in->normal[j]);\n\t\t}\n\t\tfor ( j=0 ; j < 2 ; j++)\n\t\t{\n\t\t\tstout[i][j] = LittleFloat ( ((float *)in->texcoords)[j] );\n\t\t\tlmout[i][j] = LittleFloat ( ((float *)in->texcoords)[j+2] );\n\t\t}\n\t\tcout[i][0] = (lmgamma[in->color[0]]<<gl_overbright.ival)/255.0f;\n\t\tcout[i][1] = (lmgamma[in->color[1]]<<gl_overbright.ival)/255.0f;\n\t\tcout[i][2] = (lmgamma[in->color[2]]<<gl_overbright.ival)/255.0f;\n\t\tcout[i][3] = in->color[3]/255.0f;\n\t}\n\n//\tif (r_lightmap_saturation.value != 1.0f)\n//\t\tSaturateR8G8B8(cout, count*4, r_lightmap_saturation.value);\n\n\treturn true;\n}\n\n#ifdef RFBSPS\nstatic qboolean CModRBSP_LoadVertexes (model_t *mod, qbyte *mod_base, lump_t *l)\n{\n\tcminfo_t\t*prv = (cminfo_t*)mod->meshinfo;\n\trbspvertex_t\t*in;\n\tvecV_t\t\t*out;\n\tvec3_t\t\t*nout;\n\t//, *sout, *tout;\n\tint\t\t\ti, count, j;\n\tvec2_t\t\t*lmout, *stout;\n\tvec4_t *cout;\n\tint sty;\n\textern qbyte lmgamma[256];\n\n\tin = (void *)(mod_base + l->fileofs);\n\tif (l->filelen % sizeof(*in))\n\t{\n\t\tCon_Printf (CON_ERROR \"CMOD_LoadVertexes: funny lump size\\n\");\n\t\treturn false;\n\t}\n\tcount = l->filelen / sizeof(*in);\n\n\tif (count > MAX_Q3MAP_VERTEXES)\n\t{\n\t\tCon_Printf (CON_ERROR \"Map has too many vertexes\\n\");\n\t\treturn false;\n\t}\n\n\tout = ZG_Malloc(&mod->memgroup, count*sizeof(*out));\n\tstout = ZG_Malloc(&mod->memgroup, count*sizeof(*stout));\n\tlmout = ZG_Malloc(&mod->memgroup, MAXRLIGHTMAPS*count*sizeof(*lmout));\n\tcout = ZG_Malloc(&mod->memgroup, MAXRLIGHTMAPS*count*sizeof(*cout));\n\tnout = ZG_Malloc(&mod->memgroup, count*sizeof(*nout));\n//\tsout = ZG_Malloc(&mod->memgroup, count*sizeof(*sout));\n//\ttout = ZG_Malloc(&mod->memgroup, count*sizeof(*tout));\n\tprv->verts = out;\n\tprv->vertstmexcoords = stout;\n\tfor (sty = 0; sty < MAXRLIGHTMAPS; sty++)\n\t{\n\t\tprv->vertlstmexcoords[sty] = lmout + sty*count;\n\t\tprv->colors4f_array[sty] = cout + sty*count;\n\t}\n\tprv->normals_array = nout;\n//\tprv->svector_array = sout;\n//\tprv->tvector_array = tout;\n\tprv->numvertexes = count;\n\n\tfor ( i=0 ; i<count ; i++, in++)\n\t{\n\t\tfor ( j=0 ; j < 3 ; j++)\n\t\t{\n\t\t\tout[i][j] = LittleFloat ( in->point[j] );\n\t\t\tnout[i][j] = LittleFloat (in->normal[j]);\n\t\t}\n\t\tfor ( j=0 ; j < 2 ; j++)\n\t\t{\n\t\t\tstout[i][j] = LittleFloat (in->stcoords[j]);\n\t\t\tfor (sty = 0; sty < min(MAXRLIGHTMAPS, RBSP_STYLESPERSURF); sty++)\n\t\t\t\tprv->vertlstmexcoords[sty][i][j] = LittleFloat(in->lmtexcoords[sty][j]);\n\t\t}\n\t\tfor (sty = 0; sty < min(MAXRLIGHTMAPS, RBSP_STYLESPERSURF); sty++)\n\t\t{\n\t\t\tprv->colors4f_array[sty][i][0] = lmgamma[in->color[sty][0]]/255.0f;\n\t\t\tprv->colors4f_array[sty][i][1] = lmgamma[in->color[sty][1]]/255.0f;\n\t\t\tprv->colors4f_array[sty][i][2] = lmgamma[in->color[sty][2]]/255.0f;\n\t\t\tprv->colors4f_array[sty][i][3] = in->color[sty][3]/255.0f;\n\t\t}\n\t}\n\n\treturn true;\n}\n#endif\n\n#ifndef SERVERONLY\nstatic qboolean CModQ3_LoadIndexes (model_t *loadmodel, qbyte *mod_base, lump_t *l)\n{\n\tcminfo_t\t*prv = (cminfo_t*)loadmodel->meshinfo;\n\tint\t\ti, count;\n\tint\t\t*in;\n\tindex_t\t*out;\n\n\tin = (void *)(mod_base + l->fileofs);\n\tif (l->filelen % sizeof(*in))\n\t{\n\t\tCon_Printf (CON_ERROR \"MOD_LoadBmodel: funny lump size in %s\\n\", loadmodel->name);\n\t\treturn false;\n\t}\n\tcount = l->filelen / sizeof(*in);\n\tif (count < 1 || count >= MAX_Q3MAP_INDICES)\n\t{\n\t\tCon_Printf (CON_ERROR \"MOD_LoadBmodel: too many indicies in %s: %i\\n\",\n\t\t\t\t\tloadmodel->name, count);\n\t\treturn false;\n\t}\n\n\tout = ZG_Malloc(&loadmodel->memgroup, count*sizeof(*out));\n\n\tprv->surfindexes = out;\n//\tprv->numsurfindexes = count;\n\n\tfor ( i=0 ; i<count ; i++)\n\t\tout[i] = LittleLong (in[i]);\n\n\treturn true;\n}\n#endif\n\n/*\n=================\nCMod_LoadFaces\n=================\n*/\nstatic qboolean CModQ3_LoadFaces (model_t *mod, qbyte *mod_base, lump_t *l)\n{\n\tcminfo_t\t*prv = (cminfo_t*)mod->meshinfo;\n\tq3dface_t\t\t*in;\n\tq3cface_t\t\t*out;\n\tint\t\t\ti, count;\n\n\tin = (void *)(mod_base + l->fileofs);\n\tif (l->filelen % sizeof(*in))\n\t{\n\t\tCon_Printf (CON_ERROR \"MOD_LoadBmodel: funny lump size\\n\");\n\t\treturn false;\n\t}\n\tcount = l->filelen / sizeof(*in);\n\n\tif (count > SANITY_LIMIT(*out))\n\t{\n\t\tCon_Printf (CON_ERROR \"Map has too many faces\\n\");\n\t\treturn false;\n\t}\n\n\tout = BZ_Malloc ( count*sizeof(*out) );\n\tprv->faces = out;\n\tprv->numfaces = count;\n\n\tfor ( i=0 ; i<count ; i++, in++, out++)\n\t{\n\t\tout->facetype = LittleLong ( in->facetype );\n\t\tout->shadernum = LittleLong ( in->shadernum );\n\n\t\tout->numverts = LittleLong ( in->num_vertices );\n\t\tout->firstvert = LittleLong ( in->firstvertex );\n\n\t\tif (out->facetype == MST_PATCH || out->facetype == MST_PATCH_FIXED)\n\t\t{\n\t\t\tunsigned int pw = LittleLong ( in->patchwidth );\n\t\t\tunsigned int ph = LittleLong ( in->patchheight );\n\t\t\tout->patch.cp[0] = pw&0xffff;\n\t\t\tout->patch.cp[1] = ph&0xffff;\n\t\t\tout->patch.fixedres[0] = pw>>16;\n\t\t\tout->patch.fixedres[1] = ph>>16;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tout->soup.firstindex = LittleLong(in->firstindex);\n\t\t\tout->soup.numindicies = LittleLong(in->num_indexes);\n\t\t}\n\t}\n\n\tmod->numsurfaces = i;\n\n\treturn true;\n}\n\n#ifdef RFBSPS\nstatic qboolean CModRBSP_LoadFaces (model_t *mod, qbyte *mod_base, lump_t *l)\n{\n\tcminfo_t\t*prv = (cminfo_t*)mod->meshinfo;\n\trbspface_t\t\t*in;\n\tq3cface_t\t\t*out;\n\tint\t\t\ti, count;\n\n\tin = (void *)(mod_base + l->fileofs);\n\tif (l->filelen % sizeof(*in))\n\t{\n\t\tCon_Printf (CON_ERROR \"MOD_LoadBmodel: funny lump size\\n\");\n\t\treturn false;\n\t}\n\tcount = l->filelen / sizeof(*in);\n\n\tif (count > SANITY_LIMIT(*out))\n\t{\n\t\tCon_Printf (CON_ERROR \"Map has too many faces\\n\");\n\t\treturn false;\n\t}\n\n\tout = BZ_Malloc ( count*sizeof(*out) );\n\tprv->faces = out;\n\tprv->numfaces = count;\n\n\tfor ( i=0 ; i<count ; i++, in++, out++)\n\t{\n\t\tout->facetype = LittleLong ( in->facetype );\n\t\tout->shadernum = LittleLong ( in->shadernum );\n\n\t\tout->numverts = LittleLong ( in->num_vertices );\n\t\tout->firstvert = LittleLong ( in->firstvertex );\n\n\t\tif (out->facetype == MST_PATCH || out->facetype == MST_PATCH_FIXED)\n\t\t{\n\t\t\tunsigned int pw = LittleLong ( in->patchwidth );\n\t\t\tunsigned int ph = LittleLong ( in->patchheight );\n\t\t\tout->patch.cp[0] = pw&0xffff;\n\t\t\tout->patch.cp[1] = ph&0xffff;\n\t\t\tout->patch.fixedres[0] = pw>>16;\n\t\t\tout->patch.fixedres[1] = ph>>16;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tout->soup.firstindex = LittleLong(in->firstindex);\n\t\t\tout->soup.numindicies = LittleLong(in->num_indexes);\n\t\t}\n\t}\n\n\tmod->numsurfaces = i;\n\treturn true;\n}\n#endif\n\n#ifndef SERVERONLY\n\n/*\n=================\nMod_LoadFogs\n=================\n*/\nstatic qboolean CModQ3_LoadFogs (model_t *mod, qbyte *mod_base, lump_t *l)\n{\n\tcminfo_t\t*prv = (cminfo_t*)mod->meshinfo;\n\tdfog_t \t*in;\n\tmfog_t \t*out;\n\tq2cbrush_t *brush;\n\tq2cbrushside_t *visibleside, *brushsides;\n\tint\t\ti, j, count;\n\n\tin = (void *)(mod_base + l->fileofs);\n\tif (l->filelen % sizeof(*in))\n\t{\n\t\tCon_Printf (CON_ERROR \"MOD_LoadBmodel: funny lump size in %s\\n\", mod->name);\n\t\treturn false;\n\t}\n\tcount = l->filelen / sizeof(*in);\n\tout = ZG_Malloc(&mod->memgroup, count*sizeof(*out));\n\n\tmod->fogs = out;\n\tmod->numfogs = count;\n\n\tfor ( i=0 ; i<count ; i++, in++, out++)\n\t{\n\t\tif ( LittleLong ( in->visibleSide ) == -1 )\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tbrush = prv->brushes + LittleLong ( in->brushNum );\n\t\tbrushsides = brush->brushside;\n\t\tvisibleside = brushsides + LittleLong ( in->visibleSide );\n\n\t\tout->visibleplane = visibleside->plane;\n\t\tQ_strncpyz(out->shadername, in->shader, sizeof(out->shadername));\n\t\tout->numplanes = brush->numsides;\n\t\tout->planes = ZG_Malloc(&mod->memgroup, out->numplanes*sizeof(cplane_t *));\n\n\t\tfor ( j = 0; j < out->numplanes; j++ )\n\t\t{\n\t\t\tout->planes[j] = brushsides[j].plane;\n\t\t}\n\t}\n\n\treturn true;\n}\n\nmfog_t *Mod_FogForOrigin(model_t *wmodel, vec3_t org)\n{\n\tint i, j;\n\tmfog_t \t*ret;\n\tfloat dot;\n\n\tif (!wmodel || wmodel->loadstate != MLS_LOADED)\n\t\treturn NULL;\n\n\tfor ( i=0 , ret=wmodel->fogs ; i<wmodel->numfogs ; i++, ret++)\n\t{\n\t\tif (!ret->shader)\n\t\t\tcontinue;\n\t\tfor (j = 0; j < ret->numplanes; j++)\n\t\t{\n\t\t\tdot = DotProduct(ret->planes[j]->normal, org);\n\t\t\tif (dot - ret->planes[j]->dist > 0)\n\t\t\t\tbreak;\n\t\t}\n\t\tif (j == ret->numplanes)\n\t\t{\n\t\t\treturn ret;\n\t\t}\n\t}\n\n\treturn NULL;\n}\n\n//Convert a patch in to a list of glpolys\n\n#define MAX_ARRAY_VERTS 65535\n\nstatic index_t tempIndexesArray[MAX_ARRAY_VERTS*6];\n\nstatic void GL_SizePatchFixed(mesh_t *mesh, int patchwidth, int patchheight, int numverts, int firstvert, cminfo_t *prv)\n{\n\tunsigned short patch_cp[2];\n\tint step[2], size[2];\n\n\tpatch_cp[0] = patchwidth&0xffff;\n\tpatch_cp[1] = patchheight&0xffff;\n\n\tif (patch_cp[0] <= 0 || patch_cp[1] <= 0 )\n\t{\n\t\tmesh->numindexes = 0;\n\t\tmesh->numvertexes = 0;\n\t\treturn;\n\t}\n\n\t// allocate space for mesh\n\tstep[0] = patchwidth>>16;\n\tstep[1] = patchheight>>16;\n\tif (!step[0] || !step[1])\n\t{\n\t\tsize[0] = patch_cp[0];\n\t\tsize[1] = patch_cp[1];\n\t}\n\telse\n\t{\n\t\tsize[0] = (patch_cp[0] / 2) * step[0] + 1;\n\t\tsize[1] = (patch_cp[1] / 2) * step[1] + 1;\n\t}\n\n\tmesh->numvertexes = size[0] * size[1];\n\tmesh->numindexes = (size[0]-1) * (size[1]-1) * 6;\n}\nstatic void GL_SizePatch(mesh_t *mesh, int patchwidth, int patchheight, int numverts, int firstvert, cminfo_t *prv)\n{\n\tunsigned short patch_cp[2];\n\tint step[2], size[2], flat[2];\n\tfloat subdivlevel;\n\n\tpatch_cp[0] = patchwidth;\n\tpatch_cp[1] = patchheight;\n\n\tif (patch_cp[0] <= 0 || patch_cp[1] <= 0 )\n\t{\n\t\tmesh->numindexes = 0;\n\t\tmesh->numvertexes = 0;\n\t\treturn;\n\t}\n\n\tsubdivlevel = r_subdivisions.value;\n\tif ( subdivlevel < 1 )\n\t\tsubdivlevel = 1;\n\n// find the degree of subdivision in the u and v directions\n\tPatch_GetFlatness ( subdivlevel, prv->verts[firstvert], sizeof(vecV_t)/sizeof(vec_t), patch_cp, flat );\n\n// allocate space for mesh\n\tstep[0] = (1 << flat[0]);\n\tstep[1] = (1 << flat[1]);\n\tsize[0] = (patch_cp[0] / 2) * step[0] + 1;\n\tsize[1] = (patch_cp[1] / 2) * step[1] + 1;\n\n\tmesh->numvertexes = size[0] * size[1];\n\tmesh->numindexes = (size[0]-1) * (size[1]-1) * 6;\n}\n\n//mesh_t *GL_CreateMeshForPatch ( model_t *mod, q3dface_t *surf )\nstatic void GL_CreateMeshForPatch (model_t *mod, mesh_t *mesh, int patchwidth, int patchheight, int numverts, int firstvert)\n{\n\tcminfo_t\t*prv = (cminfo_t*)mod->meshinfo;\n\tint numindexes, step[2], size[2], flat[2], i, u, v, p;\n\tunsigned short patch_cp[2];\n\tindex_t\t*indexes;\n\tfloat subdivlevel;\n\tint sty;\n\n\tpatch_cp[0] = patchwidth;\n\tpatch_cp[1] = patchheight;\n\n\tif (patch_cp[0] <= 0 || patch_cp[1] <= 0 )\n\t{\n\t\tmesh->numindexes = 0;\n\t\tmesh->numvertexes = 0;\n\t\treturn;\n\t}\n\n\tsubdivlevel = r_subdivisions.value;\n\tif ( subdivlevel < 1 )\n\t\tsubdivlevel = 1;\n\n// find the degree of subdivision in the u and v directions\n\tPatch_GetFlatness ( subdivlevel, prv->verts[firstvert], sizeof(vecV_t)/sizeof(vec_t), patch_cp, flat );\n\n// allocate space for mesh\n\tstep[0] = (1 << flat[0]);\n\tstep[1] = (1 << flat[1]);\n\tsize[0] = (patch_cp[0] / 2) * step[0] + 1;\n\tsize[1] = (patch_cp[1] / 2) * step[1] + 1;\n\tnumverts = size[0] * size[1];\n\n\tif ( numverts < 0 || numverts > MAX_ARRAY_VERTS )\n\t{\n\t\tmesh->numindexes = 0;\n\t\tmesh->numvertexes = 0;\n\t\treturn;\n\t}\n\n\n\tif (mesh->numvertexes != numverts)\n\t{\n\t\tmesh->numindexes = 0;\n\t\tmesh->numvertexes = 0;\n\t\treturn;\n\t}\n\n// fill in\n\n\tPatch_Evaluate ( prv->verts[firstvert], patch_cp, step, mesh->xyz_array[0], sizeof(vecV_t)/sizeof(vec_t));\n\tfor (sty = 0; sty < MAXRLIGHTMAPS; sty++)\n\t{\n\t\tif (mesh->colors4f_array[sty])\n\t\t\tPatch_Evaluate ( prv->colors4f_array[sty][firstvert], patch_cp, step, mesh->colors4f_array[sty][0], 4 );\n\t}\n\tPatch_Evaluate ( prv->normals_array[firstvert], patch_cp, step, mesh->normals_array[0], 3 );\n\tPatch_Evaluate ( prv->vertstmexcoords[firstvert], patch_cp, step, mesh->st_array[0], 2 );\n\tfor (sty = 0; sty < MAXRLIGHTMAPS; sty++)\n\t{\n\t\tif (mesh->lmst_array[sty])\n\t\t\tPatch_Evaluate ( prv->vertlstmexcoords[sty][firstvert], patch_cp, step, mesh->lmst_array[sty][0], 2 );\n\t}\n\n// compute new indexes avoiding adding invalid triangles\n\tnumindexes = 0;\n\tindexes = tempIndexesArray;\n\tfor (v = 0, i = 0; v < size[1]-1; v++)\n\t{\n\t\tfor (u = 0; u < size[0]-1; u++, i += 6)\n\t\t{\n\t\t\tindexes[0] = p = v * size[0] + u;\n\t\t\tindexes[1] = p + size[0];\n\t\t\tindexes[2] = p + 1;\n\n//\t\t\tif ( !VectorEquals(mesh->xyz_array[indexes[0]], mesh->xyz_array[indexes[1]]) &&\n//\t\t\t\t!VectorEquals(mesh->xyz_array[indexes[0]], mesh->xyz_array[indexes[2]]) &&\n//\t\t\t\t!VectorEquals(mesh->xyz_array[indexes[1]], mesh->xyz_array[indexes[2]]) )\n\t\t\t{\n\t\t\t\tindexes += 3;\n\t\t\t\tnumindexes += 3;\n\t\t\t}\n\n\t\t\tindexes[0] = p + 1;\n\t\t\tindexes[1] = p + size[0];\n\t\t\tindexes[2] = p + size[0] + 1;\n\n//\t\t\tif ( !VectorEquals(mesh->xyz_array[indexes[0]], mesh->xyz_array[indexes[1]]) &&\n//\t\t\t\t!VectorEquals(mesh->xyz_array[indexes[0]], mesh->xyz_array[indexes[2]]) &&\n//\t\t\t\t!VectorEquals(mesh->xyz_array[indexes[1]], mesh->xyz_array[indexes[2]]) )\n\t\t\t{\n\t\t\t\tindexes += 3;\n\t\t\t\tnumindexes += 3;\n\t\t\t}\n\t\t}\n\t}\n\n// allocate and fill index table\n\n\tmesh->numindexes = numindexes;\n\n\tmemcpy (mesh->indexes, tempIndexesArray, numindexes * sizeof(index_t) );\n}\n\nstatic void GL_CreateMeshForPatchFixed (model_t *mod, mesh_t *mesh, int patchwidth, int patchheight, int numverts, int firstvert)\n{\n\tcminfo_t\t*prv = (cminfo_t*)mod->meshinfo;\n\tint numindexes, step[2], size[2], i, u, v, p;\n\tunsigned short patch_cp[2];\n\tindex_t\t*indexes;\n\tfloat subdivlevel;\n\tint sty;\n\n\tpatch_cp[0] = patchwidth&0xffff;\n\tpatch_cp[1] = patchheight&0xffff;\n\tif (patch_cp[0] <= 0 || patch_cp[1] <= 0 )\n\t{\n\t\tmesh->numindexes = 0;\n\t\tmesh->numvertexes = 0;\n\t\treturn;\n\t}\n\n\tsubdivlevel = r_subdivisions.value;\n\tif ( subdivlevel < 1 )\n\t\tsubdivlevel = 1;\n\n// allocate space for mesh\n\tstep[0] = patchwidth>>16;\n\tstep[1] = patchheight>>16;\n\tif (!step[0] || !step[1])\n\t{\t//explicit CPs only.\n\t\tsize[0] = patch_cp[0];\n\t\tsize[1] = patch_cp[1];\n\t}\n\telse\n\t{\n\t\tsize[0] = (patch_cp[0] / 2) * step[0] + 1;\n\t\tsize[1] = (patch_cp[1] / 2) * step[1] + 1;\n\t}\n\tnumverts = size[0] * size[1];\n\n\tif ( numverts < 0 || numverts > MAX_ARRAY_VERTS )\n\t{\n\t\tmesh->numindexes = 0;\n\t\tmesh->numvertexes = 0;\n\t\treturn;\n\t}\n\n\n\tif (mesh->numvertexes != numverts)\n\t{\n\t\tmesh->numindexes = 0;\n\t\tmesh->numvertexes = 0;\n\t\treturn;\n\t}\n\n// fill in\n\n\tPatch_Evaluate ( prv->verts[firstvert], patch_cp, step, mesh->xyz_array[0], sizeof(vecV_t)/sizeof(vec_t));\n\tfor (sty = 0; sty < MAXRLIGHTMAPS; sty++)\n\t{\n\t\tif (mesh->colors4f_array[sty])\n\t\t\tPatch_Evaluate ( prv->colors4f_array[sty][firstvert], patch_cp, step, mesh->colors4f_array[sty][0], 4 );\n\t}\n\tPatch_Evaluate ( prv->normals_array[firstvert], patch_cp, step, mesh->normals_array[0], 3 );\n\tPatch_Evaluate ( prv->vertstmexcoords[firstvert], patch_cp, step, mesh->st_array[0], 2 );\n\tfor (sty = 0; sty < MAXRLIGHTMAPS; sty++)\n\t{\n\t\tif (mesh->lmst_array[sty])\n\t\t\tPatch_Evaluate ( prv->vertlstmexcoords[sty][firstvert], patch_cp, step, mesh->lmst_array[sty][0], 2 );\n\t}\n\n// compute new indexes avoiding adding invalid triangles\n\tnumindexes = 0;\n\tindexes = tempIndexesArray;\n\tfor (v = 0, i = 0; v < size[1]-1; v++)\n\t{\n\t\tfor (u = 0; u < size[0]-1; u++, i += 6)\n\t\t{\n\t\t\tindexes[0] = p = v * size[0] + u;\n\t\t\tindexes[1] = p + size[0];\n\t\t\tindexes[2] = p + 1;\n\n//\t\t\tif ( !VectorEquals(mesh->xyz_array[indexes[0]], mesh->xyz_array[indexes[1]]) &&\n//\t\t\t\t!VectorEquals(mesh->xyz_array[indexes[0]], mesh->xyz_array[indexes[2]]) &&\n//\t\t\t\t!VectorEquals(mesh->xyz_array[indexes[1]], mesh->xyz_array[indexes[2]]) )\n\t\t\t{\n\t\t\t\tindexes += 3;\n\t\t\t\tnumindexes += 3;\n\t\t\t}\n\n\t\t\tindexes[0] = p + 1;\n\t\t\tindexes[1] = p + size[0];\n\t\t\tindexes[2] = p + size[0] + 1;\n\n//\t\t\tif ( !VectorEquals(mesh->xyz_array[indexes[0]], mesh->xyz_array[indexes[1]]) &&\n//\t\t\t\t!VectorEquals(mesh->xyz_array[indexes[0]], mesh->xyz_array[indexes[2]]) &&\n//\t\t\t\t!VectorEquals(mesh->xyz_array[indexes[1]], mesh->xyz_array[indexes[2]]) )\n\t\t\t{\n\t\t\t\tindexes += 3;\n\t\t\t\tnumindexes += 3;\n\t\t\t}\n\t\t}\n\t}\n\n// allocate and fill index table\n\n\tmesh->numindexes = numindexes;\n\n\tmemcpy (mesh->indexes, tempIndexesArray, numindexes * sizeof(index_t) );\n}\n\n#ifdef RFBSPS\nstatic void CModRBSP_BuildSurfMesh(model_t *mod, msurface_t *out, builddata_t *bd)\n{\n\tcminfo_t\t*prv = (cminfo_t*)mod->meshinfo;\n\trbspface_t *in = (rbspface_t*)(bd+1);\n\tint idx = (out - mod->surfaces) - mod->firstmodelsurface;\n\tint sty;\n\tin += idx;\n\n\tif (LittleLong(in->facetype) == MST_PATCH)\n\t{\n\t\tGL_CreateMeshForPatch(mod, out->mesh, LittleLong(in->patchwidth), LittleLong(in->patchheight), LittleLong(in->num_vertices), LittleLong(in->firstvertex));\n\t}\n\telse if (LittleLong(in->facetype) == MST_PATCH_FIXED)\n\t{\n\t\tGL_CreateMeshForPatchFixed(mod, out->mesh, LittleLong(in->patchwidth), LittleLong(in->patchheight), LittleLong(in->num_vertices), LittleLong(in->firstvertex));\n\t}\n\telse if (LittleLong(in->facetype) == MST_PLANAR || LittleLong(in->facetype) == MST_TRIANGLE_SOUP)\n\t{\n\t\tunsigned int fv = LittleLong(in->firstvertex), i;\n\t\tfor (i = 0; i < out->mesh->numvertexes; i++)\n\t\t{\n\t\t\tVectorCopy(prv->verts[fv + i], out->mesh->xyz_array[i]);\n\t\t\tVector2Copy(prv->vertstmexcoords[fv + i], out->mesh->st_array[i]);\n\t\t\tfor (sty = 0; sty < MAXRLIGHTMAPS; sty++)\n\t\t\t{\n\t\t\t\tVector2Copy(prv->vertlstmexcoords[sty][fv + i], out->mesh->lmst_array[sty][i]);\n\t\t\t\tVector4Copy(prv->colors4f_array[sty][fv + i], out->mesh->colors4f_array[sty][i]);\n\t\t\t}\n\n\t\t\tVectorCopy(prv->normals_array[fv + i], out->mesh->normals_array[i]);\n\t\t}\n\t\tfv = LittleLong(in->firstindex);\n\t\tfor (i = 0; i < out->mesh->numindexes; i++)\n\t\t{\n\t\t\tout->mesh->indexes[i] = prv->surfindexes[fv + i];\n\t\t}\n\t}\n\telse\n\t{\n/*\t\t//flare\n\t\tint r, g, b;\n\t\textern index_t r_quad_indexes[6];\n\t\tstatic vec2_t\tst[4] = {{0,0},{0,1},{1,1},{1,0}};\n\n\t\tmesh = out->mesh = (mesh_t *)Hunk_Alloc(sizeof(mesh_t));\n\t\tmesh->xyz_array = (vecV_t *)Hunk_Alloc(sizeof(vecV_t)*4);\n\t\tmesh->colors4b_array = (byte_vec4_t *)Hunk_Alloc(sizeof(byte_vec4_t)*4);\n\t\tmesh->numvertexes = 4;\n\t\tmesh->indexes = r_quad_indexes;\n\t\tmesh->st_array = st;\n\t\tmesh->numindexes = 6;\n\n\t\tVectorCopy (in->lightmap_origin, mesh->xyz_array[0]);\n\t\tVectorCopy (in->lightmap_origin, mesh->xyz_array[1]);\n\t\tVectorCopy (in->lightmap_origin, mesh->xyz_array[2]);\n\t\tVectorCopy (in->lightmap_origin, mesh->xyz_array[3]);\n\n\t\tr = LittleFloat(in->lightmap_vecs[0][0]) * 255.0f;\n\t\tr = bound (0, r, 255);\n\t\tg = LittleFloat(in->lightmap_vecs[0][1]) * 255.0f;\n\t\tg = bound (0, g, 255);\n\t\tb = LittleFloat(in->lightmap_vecs[0][2]) * 255.0f;\n\t\tb = bound (0, b, 255);\n\n\t\tmesh->colors4b_array[0][0] = r;\n\t\tmesh->colors4b_array[0][1] = g;\n\t\tmesh->colors4b_array[0][2] = b;\n\t\tmesh->colors4b_array[0][3] = 255;\n\t\tVector4Copy(mesh->colors4b_array[0], mesh->colors4b_array[1]);\n\t\tVector4Copy(mesh->colors4b_array[0], mesh->colors4b_array[2]);\n\t\tVector4Copy(mesh->colors4b_array[0], mesh->colors4b_array[3]);\n*/\n\t}\n\n\tMod_AccumulateMeshTextureVectors(out->mesh);\n\tMod_NormaliseTextureVectors(out->mesh->normals_array, out->mesh->snormals_array, out->mesh->tnormals_array, out->mesh->numvertexes, false);\n}\n#endif\n\nstatic void CModQ3_BuildSurfMesh(model_t *mod, msurface_t *out, builddata_t *bd)\n{\n\tcminfo_t\t*prv = (cminfo_t*)mod->meshinfo;\n\tint idx = (out - mod->surfaces) - mod->firstmodelsurface;\n\tq3dface_t *in = (q3dface_t*)(bd+1) + idx;\n\tint facetype = LittleLong(in->facetype);\n\n\tif (facetype == MST_PATCH)\n\t{\n\t\tGL_CreateMeshForPatch(mod, out->mesh, LittleLong(in->patchwidth), LittleLong(in->patchheight), LittleLong(in->num_vertices), LittleLong(in->firstvertex));\n\t}\n\telse if (facetype == MST_PATCH_FIXED)\n\t{\n\t\tGL_CreateMeshForPatchFixed(mod, out->mesh, LittleLong(in->patchwidth), LittleLong(in->patchheight), LittleLong(in->num_vertices), LittleLong(in->firstvertex));\n\t}\n\telse if (facetype == MST_PLANAR || facetype == MST_TRIANGLE_SOUP)\n\t{\n\t\tunsigned int fv = LittleLong(in->firstvertex), fi = LittleLong(in->firstindex), i;\n\t\tfor (i = 0; i < out->mesh->numvertexes; i++)\n\t\t{\n\t\t\tVectorCopy(prv->verts[fv + i], out->mesh->xyz_array[i]);\n\t\t\tVector2Copy(prv->vertstmexcoords[fv + i], out->mesh->st_array[i]);\n\t\t\tVector2Copy(prv->vertlstmexcoords[0][fv + i], out->mesh->lmst_array[0][i]);\n\t\t\tVector4Copy(prv->colors4f_array[0][fv + i], out->mesh->colors4f_array[0][i]);\n\n\t\t\tVectorCopy(prv->normals_array[fv + i], out->mesh->normals_array[i]);\n\t\t}\n\t\tfor (i = 0; i < out->mesh->numindexes; i++)\n\t\t{\n\t\t\tout->mesh->indexes[i] = prv->surfindexes[fi + i];\n\t\t}\n\t}\n\telse\n\t{\n/*\t\t//flare\n\t\tint r, g, b;\n\t\textern index_t r_quad_indexes[6];\n\t\tstatic vec2_t\tst[4] = {{0,0},{0,1},{1,1},{1,0}};\n\n\t\tmesh = out->mesh = (mesh_t *)Hunk_Alloc(sizeof(mesh_t));\n\t\tmesh->xyz_array = (vecV_t *)Hunk_Alloc(sizeof(vecV_t)*4);\n\t\tmesh->colors4b_array = (byte_vec4_t *)Hunk_Alloc(sizeof(byte_vec4_t)*4);\n\t\tmesh->numvertexes = 4;\n\t\tmesh->indexes = r_quad_indexes;\n\t\tmesh->st_array = st;\n\t\tmesh->numindexes = 6;\n\n\t\tVectorCopy (in->lightmap_origin, mesh->xyz_array[0]);\n\t\tVectorCopy (in->lightmap_origin, mesh->xyz_array[1]);\n\t\tVectorCopy (in->lightmap_origin, mesh->xyz_array[2]);\n\t\tVectorCopy (in->lightmap_origin, mesh->xyz_array[3]);\n\n\t\tr = LittleFloat(in->lightmap_vecs[0][0]) * 255.0f;\n\t\tr = bound (0, r, 255);\n\t\tg = LittleFloat(in->lightmap_vecs[0][1]) * 255.0f;\n\t\tg = bound (0, g, 255);\n\t\tb = LittleFloat(in->lightmap_vecs[0][2]) * 255.0f;\n\t\tb = bound (0, b, 255);\n\n\t\tmesh->colors4b_array[0][0] = r;\n\t\tmesh->colors4b_array[0][1] = g;\n\t\tmesh->colors4b_array[0][2] = b;\n\t\tmesh->colors4b_array[0][3] = 255;\n\t\tVector4Copy(mesh->colors4b_array[0], mesh->colors4b_array[1]);\n\t\tVector4Copy(mesh->colors4b_array[0], mesh->colors4b_array[2]);\n\t\tVector4Copy(mesh->colors4b_array[0], mesh->colors4b_array[3]);\n*/\n\t}\n\n\tMod_AccumulateMeshTextureVectors(out->mesh);\n\tMod_NormaliseTextureVectors(out->mesh->normals_array, out->mesh->snormals_array, out->mesh->tnormals_array, out->mesh->numvertexes, false);\n}\n\nstatic qboolean CModQ3_LoadRFaces (model_t *mod, qbyte *mod_base, lump_t *l)\n{\n\tcminfo_t\t*prv = (cminfo_t*)mod->meshinfo;\n\textern cvar_t r_vertexlight;\n\tq3dface_t *in;\n\tmsurface_t *out;\n\tmplane_t *pl;\n\n\tint facetype;\n\tint count;\n\tint surfnum;\n\tint shadernum;\n\n\tint fv;\n\tint sty; \n\n\tmesh_t *mesh;\n\n\tin = (void *)(mod_base + l->fileofs);\n\tif (l->filelen % sizeof(*in))\n\t{\n\t\tCon_Printf (CON_ERROR \"MOD_LoadBmodel: funny lump size in %s\\n\",mod->name);\n\t\treturn false;\n\t}\n\tcount = l->filelen / sizeof(*in);\n\tout = ZG_Malloc(&mod->memgroup, count*sizeof(*out));\n\tpl = ZG_Malloc(&mod->memgroup, count*sizeof(*pl));//create a new array of planes for speed.\n\tmesh = ZG_Malloc(&mod->memgroup, count*sizeof(*mesh));\n\n\tmod->surfaces = out;\n\tmod->numsurfaces = count;\n\tmod->lightmaps.first = 0;\n\n\tfor (surfnum = 0; surfnum < count; surfnum++, out++, in++, pl++)\n\t{\n\t\tout->plane = pl;\n\t\t\n\t\tshadernum = LittleLong(in->shadernum);\n\t\tif (shadernum < 0 || shadernum >= mod->numtexinfo)\n\t\t{\n\t\t\tCon_Printf (CON_ERROR \"CMod_LoadRFaces: bad shader index %s\\n\",mod->name);\n\t\t\treturn false;\n\t\t}\n\t\tfacetype = LittleLong(in->facetype);\n\t\tout->texinfo = mod->texinfo + shadernum;\n\t\tout->lightmaptexturenums[0] = LittleLong(in->lightmapnum);\n\t\tif (facetype == MST_FLARE)\n\t\t\tout->texinfo = mod->texinfo + mod->numtexinfo*2;\n\t\telse if (out->lightmaptexturenums[0] < 0 /*|| facetype == MST_TRIANGLE_SOUP*/ || r_vertexlight.value)\n\t\t\tout->texinfo += mod->numtexinfo;\t//various surfaces use a different version of the same shader (with all the lightmaps collapsed)\n\n\t\tout->light_s[0] = LittleLong(in->lightmap_offs[0]);\n\t\tout->light_t[0] = LittleLong(in->lightmap_offs[1]);\n\t\tout->styles[0] = INVALID_LIGHTSTYLE;\n\t\tout->vlstyles[0] = INVALID_VLIGHTSTYLE;\n\t\tfor (sty = 1; sty < MAXRLIGHTMAPS; sty++)\n\t\t{\n\t\t\tout->styles[sty] = INVALID_LIGHTSTYLE;\n\t\t\tout->vlstyles[sty] = INVALID_VLIGHTSTYLE;\n\t\t\tout->lightmaptexturenums[sty] = -1;\n\t\t}\n\t\tfor (;  sty < MAXCPULIGHTMAPS; sty++)\n\t\t\tout->styles[sty] = INVALID_LIGHTSTYLE;\n\t\tout->lmshift = LMSHIFT_DEFAULT;\n\t\t//fixme: determine texturemins from lightmap_origin\n\t\tout->extents[0] = (LittleLong(in->lightmap_width)-1)<<out->lmshift;\n\t\tout->extents[1] = (LittleLong(in->lightmap_height)-1)<<out->lmshift;\n\t\tout->samples=NULL;\n\n\t\tif (mod->lightmaps.count < out->lightmaptexturenums[0]+1)\n\t\t\tmod->lightmaps.count = out->lightmaptexturenums[0]+1;\n\n\t\tfv = LittleLong(in->firstvertex);\n\t\t{\n\t\t\tvec3_t v[3];\n\t\t\tVectorCopy(prv->verts[fv+0], v[0]);\n\t\t\tVectorCopy(prv->verts[fv+1], v[1]);\n\t\t\tVectorCopy(prv->verts[fv+2], v[2]);\n\t\t\tPlaneFromPoints(v, pl);\n\t\t\tCategorizePlane(pl);\n\t\t}\n\n\t\tif (prv->surfaces[shadernum].c.value == 0 || prv->surfaces[shadernum].c.value & Q3CONTENTS_TRANSLUCENT)\n\t\t\t\t//q3dm10's thingie is 0\n\t\t\tout->flags |= SURF_DRAWALPHA;\n\n\t\tif (mod->texinfo[shadernum].flags & TI_SKY)\n\t\t\tout->flags |= SURF_DRAWSKY|SURF_DRAWTILED;\n\n\t\tif (LittleLong(in->fognum) == -1 || !mod->numfogs)\n\t\t\tout->fog = NULL;\n\t\telse\n\t\t\tout->fog = mod->fogs + LittleLong(in->fognum);\n\t\tif (prv->surfaces[shadernum].c.flags & (Q3SURF_NODRAW | Q3SURF_SKIP))\n\t\t{\n\t\t\tout->mesh = &mesh[surfnum];\n\t\t\tout->mesh->numindexes = 0;\n\t\t\tout->mesh->numvertexes = 0;\n\t\t}\n\t\telse if (facetype == MST_PATCH)\n\t\t{\n\t\t\tout->mesh = &mesh[surfnum];\n\t\t\tGL_SizePatch(out->mesh, LittleLong(in->patchwidth), LittleLong(in->patchheight), LittleLong(in->num_vertices), LittleLong(in->firstvertex), prv);\n\t\t}\n\t\telse if (facetype == MST_PATCH_FIXED)\n\t\t{\n\t\t\tout->mesh = &mesh[surfnum];\n\t\t\tGL_SizePatchFixed(out->mesh, LittleLong(in->patchwidth), LittleLong(in->patchheight), LittleLong(in->num_vertices), LittleLong(in->firstvertex), prv);\n\t\t}\n\t\telse if (facetype == MST_PLANAR || facetype == MST_TRIANGLE_SOUP)\n\t\t{\n\t\t\tout->mesh = &mesh[surfnum];\n\t\t\tout->mesh->numindexes = LittleLong(in->num_indexes);\n\t\t\tout->mesh->numvertexes = LittleLong(in->num_vertices);\n/*\n\t\t\tMod_AccumulateMeshTextureVectors(out->mesh);\n*/\n\t\t}\n\t\telse\n\t\t{\n\t\t\tout->mesh = &mesh[surfnum];\n\t\t\tout->mesh->numindexes = 6;\n\t\t\tout->mesh->numvertexes = 4;\n\t\t}\n\t}\n\n\tMod_SortShaders(mod);\n\treturn true;\n}\n\n#ifdef RFBSPS\nstatic qboolean CModRBSP_LoadRFaces (model_t *mod, qbyte *mod_base, lump_t *l)\n{\n\tcminfo_t\t*prv = (cminfo_t*)mod->meshinfo;\n\textern cvar_t r_vertexlight;\n\trbspface_t *in;\n\tmsurface_t *out;\n\tmplane_t *pl;\n\tint facetype;\n\n\tint count;\n\tint surfnum;\n\tint shadernum;\n\n\tint fv;\n\tint j;\n\n\tmesh_t *mesh;\n\n\tint maxstyle = q3bsp_ignorestyles.ival?1:min(MAXRLIGHTMAPS, RBSP_STYLESPERSURF);\n\n\n\tin = (void *)(mod_base + l->fileofs);\n\tif (l->filelen % sizeof(*in))\n\t{\n\t\tCon_Printf (CON_ERROR \"MOD_LoadBmodel: funny lump size in %s\\n\",mod->name);\n\t\treturn false;\n\t}\n\tcount = l->filelen / sizeof(*in);\n\tout = ZG_Malloc(&mod->memgroup, count*sizeof(*out));\n\tpl = ZG_Malloc(&mod->memgroup, count*sizeof(*pl));//create a new array of planes for speed.\n\tmesh = ZG_Malloc(&mod->memgroup, count*sizeof(*mesh));\n\n\tmod->surfaces = out;\n\tmod->numsurfaces = count;\n\n\tfor (surfnum = 0; surfnum < count; surfnum++, out++, in++, pl++)\n\t{\n\t\tout->plane = pl;\n\t\tshadernum = LittleLong(in->shadernum);\n\t\tif (shadernum < 0 || shadernum >= mod->numtexinfo)\n\t\t{\n\t\t\tCon_Printf (CON_ERROR \"CMod_LoadRFaces: bad shader index %s\\n\",mod->name);\n\t\t\treturn false;\n\t\t}\n\t\tfacetype = LittleLong(in->facetype);\n\t\tout->texinfo = mod->texinfo + shadernum;\n\t\tfor (j = 0; j < maxstyle; j++)\n\t\t{\n\t\t\tout->lightmaptexturenums[j] = LittleLong(in->lightmapnum[j]);\n\t\t\tout->light_s[j] = LittleLong(in->lightmap_offs[0][j]);\n\t\t\tout->light_t[j] = LittleLong(in->lightmap_offs[1][j]);\n\t\t\tout->styles[j] = (in->lm_styles[j]!=255)?in->lm_styles[j]:INVALID_LIGHTSTYLE;\n\t\t\tout->vlstyles[j] = (in->vt_styles[j]!=255)?in->vt_styles[j]:INVALID_VLIGHTSTYLE;\n\n\t\t\tif (mod->lightmaps.count < out->lightmaptexturenums[j]+1)\n\t\t\t\tmod->lightmaps.count = out->lightmaptexturenums[j]+1;\n\t\t}\n\t\tfor (; j < MAXRLIGHTMAPS; j++)\n\t\t{\n\t\t\tout->lightmaptexturenums[j] = -1;\n\t\t\tout->light_s[j] = 0;\n\t\t\tout->light_t[j] = 0;\n\t\t\tout->styles[j] = INVALID_LIGHTSTYLE;\n\t\t\tout->vlstyles[j] = INVALID_VLIGHTSTYLE;\n\t\t}\n\t\tfor (;  j < MAXCPULIGHTMAPS; j++)\n\t\t\tout->styles[j] = INVALID_LIGHTSTYLE;\n\t\tif (facetype == MST_FLARE)\n\t\t\tout->texinfo = mod->texinfo + mod->numtexinfo*2;\n\t\telse if (out->lightmaptexturenums[0]<0 || r_vertexlight.value)\n\t\t\tout->texinfo += mod->numtexinfo;\t//soup/vertex light uses a different version of the same shader (with all the lightmaps collapsed)\n\n\t\tout->lmshift = LMSHIFT_DEFAULT;\n\t\tout->extents[0] = (LittleLong(in->lightmap_width)-1)<<out->lmshift;\n\t\tout->extents[1] = (LittleLong(in->lightmap_height)-1)<<out->lmshift;\n\t\tout->samples=NULL;\n\n\t\tfv = LittleLong(in->firstvertex);\n\t\t{\n\t\t\tvec3_t v[3];\n\t\t\tVectorCopy(prv->verts[fv+0], v[0]);\n\t\t\tVectorCopy(prv->verts[fv+1], v[1]);\n\t\t\tVectorCopy(prv->verts[fv+2], v[2]);\n\t\t\tPlaneFromPoints(v, pl);\n\t\t\tCategorizePlane(pl);\n\t\t}\n\n\t\tif (prv->surfaces[shadernum].c.value == 0 || prv->surfaces[shadernum].c.value & Q3CONTENTS_TRANSLUCENT)\n\t\t\t\t//q3dm10's thingie is 0\n\t\t\tout->flags |= SURF_DRAWALPHA;\n\n\t\tif (mod->texinfo[shadernum].flags & TI_SKY)\n\t\t\tout->flags |= SURF_DRAWSKY|SURF_DRAWTILED;\n\n\t\tif (in->fognum < 0 || in->fognum >= mod->numfogs)\n\t\t\tout->fog = NULL;\n\t\telse\n\t\t\tout->fog = mod->fogs + in->fognum;\n\n\t\tif (prv->surfaces[shadernum].c.flags & (Q3SURF_NODRAW | Q3SURF_SKIP))\n\t\t{\n\t\t\tout->mesh = &mesh[surfnum];\n\t\t\tout->mesh->numindexes = 0;\n\t\t\tout->mesh->numvertexes = 0;\n\t\t}\n\t\telse if (facetype == MST_PATCH)\n\t\t{\n\t\t\tout->mesh = &mesh[surfnum];\n\t\t\tGL_SizePatch(out->mesh, LittleLong(in->patchwidth), LittleLong(in->patchheight), LittleLong(in->num_vertices), LittleLong(in->firstvertex), prv);\n\t\t}\n\t\telse if (facetype == MST_PATCH_FIXED)\n\t\t{\n\t\t\tout->mesh = &mesh[surfnum];\n\t\t\tGL_SizePatchFixed(out->mesh, LittleLong(in->patchwidth), LittleLong(in->patchheight), LittleLong(in->num_vertices), LittleLong(in->firstvertex), prv);\n\t\t}\n\t\telse if (facetype == MST_PLANAR || facetype == MST_TRIANGLE_SOUP)\n\t\t{\n\t\t\tout->mesh = &mesh[surfnum];\n\t\t\tout->mesh->numindexes = LittleLong(in->num_indexes);\n\t\t\tout->mesh->numvertexes = LittleLong(in->num_vertices);\n/*\n\t\t\tMod_AccumulateMeshTextureVectors(out->mesh);\n*/\n\t\t}\n\t\telse\n\t\t{\n\t\t\tout->mesh = &mesh[surfnum];\n\t\t\tout->mesh->numindexes = 6;\n\t\t\tout->mesh->numvertexes = 4;\n\t\t}\n\t}\n\t\n\tMod_SortShaders(mod);\n\treturn true;\n}\n#endif\n#endif\n\nstatic qboolean CModQ3_LoadNodes (model_t *loadmodel, qbyte *mod_base, lump_t *l)\n{\n\tint\t\t\ti, j, count, p;\n\tq3dnode_t\t*in;\n\tmnode_t \t*out;\n\t//dnode_t\n\n\tin = (void *)(mod_base + l->fileofs);\n\tif (l->filelen % sizeof(*in))\n\t{\n\t\tCon_Printf (CON_ERROR \"MOD_LoadBmodel: funny lump size in %s\\n\",loadmodel->name);\n\t\treturn false;\n\t}\n\tcount = l->filelen / sizeof(*in);\n\tout = ZG_Malloc(&loadmodel->memgroup, count*sizeof(*out));\n\n\tif (count > SANITY_LIMIT(*out))\n\t{\n\t\tCon_Printf (CON_ERROR \"Too many nodes on map\\n\");\n\t\treturn false;\n\t}\n\n\tloadmodel->nodes = out;\n\tloadmodel->numnodes = count;\n\n\tfor ( i=0 ; i<count ; i++, in++, out++)\n\t{\n\t\tfor (j=0 ; j<3 ; j++)\n\t\t{\n\t\t\tout->minmaxs[j] = LittleLong (in->mins[j]);\n\t\t\tout->minmaxs[3+j] = LittleLong (in->maxs[j]);\n\t\t}\n\t\tAddPointToBounds(out->minmaxs, loadmodel->mins, loadmodel->maxs);\n\t\tAddPointToBounds(out->minmaxs+3, loadmodel->mins, loadmodel->maxs);\n\n\t\tp = LittleLong(in->plane);\n\t\tout->plane = loadmodel->planes + p;\n\n\t\tout->firstsurface = 0;//LittleShort (in->firstface);\n\t\tout->numsurfaces = 0;//LittleShort (in->numfaces);\n\n\t\tout->contents = -1;\n\n\t\tfor (j=0 ; j<2 ; j++)\n\t\t{\n\t\t\tp = LittleLong (in->children[j]);\n\t\t\tout->childnum[j] = p;\n\t\t\tif (p >= 0)\n\t\t\t{\n\t\t\t\tout->children[j] = loadmodel->nodes + p;\n\t\t\t}\n\t\t\telse\n\t\t\t\tout->children[j] = (mnode_t *)(loadmodel->leafs + (-1 - p));\n\t\t}\n\t}\n\n\tCMod_SetParent (loadmodel->nodes, NULL);\t// sets nodes and leafs\n\n\treturn true;\n}\n\nstatic qboolean CModQ3_LoadBrushes (model_t *mod, qbyte *mod_base, lump_t *l)\n{\n\tcminfo_t\t*prv = (cminfo_t*)mod->meshinfo;\n\tq3dbrush_t\t*in;\n\tq2cbrush_t\t*out;\n\tint\t\t\ti, count;\n\tint\t\t\tshaderref;\n\n\tin = (void *)(mod_base + l->fileofs);\n\tif (l->filelen % sizeof(*in))\n\t{\n\t\tCon_Printf (CON_ERROR \"MOD_LoadBmodel: funny lump size\\n\");\n\t\treturn false;\n\t}\n\tcount = l->filelen / sizeof(*in);\n\n\tif (count > SANITY_MAX_MAP_BRUSHES)\n\t{\n\t\tCon_Printf (CON_ERROR \"Map has too many brushes\");\n\t\treturn false;\n\t}\n\n\tprv->brushes = ZG_Malloc(&mod->memgroup, sizeof(*out) * (count+1));\n\n\tout = prv->brushes;\n\n\tprv->numbrushes = count;\n\n\tfor (i=0 ; i<count ; i++, out++, in++)\n\t{\n\t\tshaderref = LittleLong ( in->shadernum );\n\t\tout->contents = prv->surfaces[shaderref].c.value;\n\t\tout->brushside = &prv->brushsides[LittleLong ( in->firstside )];\n\t\tout->numsides = LittleLong ( in->num_sides );\n\t\tCM_FinalizeBrush(out);\n\t}\n\n\treturn true;\n}\n\nstatic qboolean CModQ3_LoadLeafs (model_t *mod, qbyte *mod_base, lump_t *l)\n{\n\tcminfo_t\t*prv = (cminfo_t*)mod->meshinfo;\n\tint\t\t\ti, j;\n\tmleaf_t\t\t*out;\n\tq3dleaf_t \t*in;\n\tint\t\t\tcount;\n\tq2cbrush_t\t**brush;\n\n\tin = (void *)(mod_base + l->fileofs);\n\tif (l->filelen % sizeof(*in))\n\t{\n\t\tCon_Printf (CON_ERROR \"MOD_LoadBmodel: funny lump size\\n\");\n\t\treturn false;\n\t}\n\tcount = l->filelen / sizeof(*in);\n\n\tif (count < 1)\n\t{\n\t\tCon_Printf (CON_ERROR \"Map with no leafs\\n\");\n\t\treturn false;\n\t}\n\t// need to save space for box planes\n\n\tif (count > SANITY_LIMIT(*out))\n\t{\n\t\tCon_Printf (CON_ERROR \"Too many leaves on map\");\n\t\treturn false;\n\t}\n\n\tout = ZG_Malloc(&mod->memgroup, sizeof(*out) * (count+1));\n\n\tmod->leafs = out;\n\tmod->numleafs = count;\n\n\tfor ( i=0 ; i<count ; i++, in++, out++)\n\t{\n\t\tfor (j = 0; j < 3; j++)\n\t\t{\n\t\t\tout->minmaxs[0+j] = LittleLong(in->mins[j]);\n\t\t\tout->minmaxs[3+j] = LittleLong(in->maxs[j]);\n\t\t}\n\t\tout->cluster = LittleLong(in->cluster);\n\t\tout->area = LittleLong(in->area);\n//\t\tout->firstleafface = LittleLong(in->firstleafsurface);\n//\t\tout->numleaffaces = LittleLong(in->num_leafsurfaces);\n\t\tout->contents = 0;\n\t\tout->firstleafbrush = LittleLong(in->firstleafbrush);\n\t\tout->numleafbrushes = LittleLong(in->num_leafbrushes);\n\n\t\tout->firstmarksurface = mod->marksurfaces + LittleLong(in->firstleafsurface);\n\t\tout->nummarksurfaces = LittleLong(in->num_leafsurfaces);\n\n\t\tif (out->minmaxs[0] > out->minmaxs[3+0] || out->minmaxs[1] > out->minmaxs[3+1] ||\n\t\t\tout->minmaxs[2] > out->minmaxs[3+2])// || VectorEquals (out->minmaxs, out->minmaxs+3))\n\t\t{\n\t\t\tout->nummarksurfaces = 0;\n\t\t}\n\n\t\tbrush = &prv->leafbrushes[out->firstleafbrush];\n\t\tfor (j=0 ; j<out->numleafbrushes ; j++)\n\t\t{\n\t\t\tout->contents |= brush[j]->contents;\n\t\t}\n\n\t\tif (out->area >= prv->numareas)\n\t\t{\n\t\t\tprv->numareas = out->area + 1;\n\t\t}\n\t}\n\n\treturn true;\n}\n\nstatic qboolean CModQ3_LoadPlanes (model_t *loadmodel, qbyte *mod_base, lump_t *l)\n{\n\tint\t\t\ti, j;\n\tmplane_t\t*out;\n\tQ3PLANE_t \t*in;\n\tint\t\t\tcount;\n\n\tin = (void *)(mod_base + l->fileofs);\n\tif (l->filelen % sizeof(*in))\n\t{\n\t\tCon_Printf (CON_ERROR \"MOD_LoadBmodel: funny lump size in %s\\n\",loadmodel->name);\n\t\treturn false;\n\t}\n\tcount = l->filelen / sizeof(*in);\n\n\tif (count > SANITY_LIMIT(*out))\n\t{\n\t\tCon_Printf (CON_ERROR \"Too many planes on map (%i)\\n\", count);\n\t\treturn false;\n\t}\n\n\tloadmodel->planes = out = ZG_Malloc(&loadmodel->memgroup, sizeof(*out) * count);\n\tloadmodel->numplanes = count;\n\n\tfor ( i=0 ; i<count ; i++, in++, out++)\n\t{\n\t\tfor (j=0 ; j<3 ; j++)\n\t\t{\n\t\t\tout->normal[j] = LittleFloat (in->n[j]);\n\t\t}\n\t\tout->dist = LittleFloat (in->d);\n\n\t\tCategorizePlane(out);\n\t}\n\n\treturn true;\n}\n\nstatic qboolean CModQ3_LoadLeafBrushes (model_t *mod, qbyte *mod_base, lump_t *l)\n{\n\tcminfo_t\t*prv = (cminfo_t*)mod->meshinfo;\n\tint\t\t\ti;\n\tq2cbrush_t  **out;\n\tint \t*in;\n\tint\t\t\tcount;\n\n\tin = (void *)(mod_base + l->fileofs);\n\tif (l->filelen % sizeof(*in))\n\t{\n\t\tCon_Printf (CON_ERROR \"MOD_LoadBmodel: funny lump size\\n\");\n\t\treturn false;\n\t}\n\tcount = l->filelen / sizeof(*in);\n\n\tif (count < 1)\n\t{\n\t\tCon_Printf (CON_ERROR \"Map with no leafbrushes\\n\");\n\t\treturn false;\n\t}\n\t// need to save space for box planes\n\tif (count > SANITY_MAX_MAP_LEAFBRUSHES)\n\t{\n\t\tCon_Printf (CON_ERROR \"Map has too many leafbrushes\\n\");\n\t\treturn false;\n\t}\n\n\t//prv->numbrushes is because of submodels being weird.\n\tout = prv->leafbrushes = ZG_Malloc(&mod->memgroup, sizeof(*out) * (count+prv->numbrushes));\n\tprv->numleafbrushes = count;\n\n\tfor ( i=0 ; i<count ; i++, in++, out++)\n\t\t*out = prv->brushes + (unsigned int)LittleLong (*in);\n\n\treturn true;\n}\n\nstatic qboolean CModQ3_LoadBrushSides (model_t *mod, qbyte *mod_base, lump_t *l)\n{\n\tcminfo_t\t*prv = (cminfo_t*)mod->meshinfo;\n\tint\t\t\ti, j;\n\tq2cbrushside_t\t*out;\n\tq3dbrushside_t \t*in;\n\tint\t\t\tcount;\n\tint\t\t\tnum;\n\n\tin = (void *)(mod_base + l->fileofs);\n\tif (l->filelen % sizeof(*in))\n\t{\n\t\tCon_Printf (CON_ERROR \"MOD_LoadBmodel: funny lump size\\n\");\n\t\treturn false;\n\t}\n\tcount = l->filelen / sizeof(*in);\n\n\t// need to save space for box planes\n\tif (count > SANITY_MAX_MAP_BRUSHSIDES)\n\t{\n\t\tCon_Printf (CON_ERROR \"Map has too many brushsides (%i)\\n\", count);\n\t\treturn false;\n\t}\n\n\tout = prv->brushsides = ZG_Malloc(&mod->memgroup, sizeof(*out) * count);\n\tprv->numbrushsides = count;\n\n\tfor ( i=0 ; i<count ; i++, in++, out++)\n\t{\n\t\tnum = LittleLong (in->planenum);\n\t\tout->plane = &mod->planes[num];\n\t\tj = LittleLong (in->texinfo);\n\t\tif (j >= mod->numtexinfo)\n\t\t{\n\t\t\tCon_Printf (CON_ERROR \"Bad brushside texinfo\\n\");\n\t\t\treturn false;\n\t\t}\n\t\tout->surface = &prv->surfaces[j];\n\t}\n\n\treturn true;\n}\n\n#ifdef RFBSPS\nstatic qboolean CModRBSP_LoadBrushSides (model_t *mod, qbyte *mod_base, lump_t *l)\n{\n\tcminfo_t\t*prv = (cminfo_t*)mod->meshinfo;\n\tint\t\t\ti, j;\n\tq2cbrushside_t\t*out;\n\trbspbrushside_t \t*in;\n\tint\t\t\tcount;\n\tint\t\t\tnum;\n\n\tin = (void *)(mod_base + l->fileofs);\n\tif (l->filelen % sizeof(*in))\n\t{\n\t\tCon_Printf (CON_ERROR \"MOD_LoadBmodel: funny lump size\\n\");\n\t\treturn false;\n\t}\n\tcount = l->filelen / sizeof(*in);\n\n\t// need to save space for box planes\n\tif (count > SANITY_MAX_MAP_BRUSHSIDES)\n\t{\n\t\tCon_Printf (CON_ERROR \"Map has too many brushsides (%i)\\n\", count);\n\t\treturn false;\n\t}\n\n\tout = prv->brushsides = ZG_Malloc(&mod->memgroup, sizeof(*out) * count);\n\tprv->numbrushsides = count;\n\n\tfor ( i=0 ; i<count ; i++, in++, out++)\n\t{\n\t\tnum = LittleLong (in->planenum);\n\t\tout->plane = &mod->planes[num];\n\t\tj = LittleLong (in->texinfo);\n\t\tif (j >= mod->numtexinfo)\n\t\t{\n\t\t\tCon_Printf (CON_ERROR \"Bad brushside texinfo\\n\");\n\t\t\treturn false;\n\t\t}\n\t\tout->surface = &prv->surfaces[j];\n\t}\n\n\treturn true;\n}\n#endif\n\nstatic qboolean CModQ3_LoadVisibility (model_t *mod, qbyte *mod_base, lump_t *l)\n{\n\tcminfo_t\t*prv = (cminfo_t*)mod->meshinfo;\n\tunsigned int numclusters;\n\tif (l->filelen == 0)\n\t{\n\t\tint i;\n#if 0\n\t\t//the 'correct' code\n\t\tnumclusters = 0;\n\t\tfor (i = 0; i < mod->numleafs; i++)\n\t\t\tif (numclusters < mod->leafs[i].cluster+1)\n\t\t\t\tnumclusters = mod->leafs[i].cluster+1;\n\n\t\tnumclusters++;\n#else\n\t\t//but its much faster to merge all leafs into a single pvs cluster. no vis is no vis.\n\t\tnumclusters = 8*sizeof(int);\n\t\tfor (i = 0; i < mod->numleafs; i++)\n\t\t\tmod->leafs[i].cluster = !!mod->leafs[i].cluster;\n#endif\n\t\tprv->q3pvs = ZG_Malloc(&mod->memgroup, sizeof(*prv->q3pvs) + (numclusters+7)/8 * numclusters);\n\t\tmemset (prv->q3pvs, 0xff, sizeof(*prv->q3pvs) + (numclusters+7)/8 * numclusters);\n\t\tprv->q3pvs->numclusters = numclusters;\n\t\tprv->numvisibility = 0;\n\t\tprv->q3pvs->rowsize = (prv->q3pvs->numclusters+7)/8;\n\t}\n\telse\n\t{\n\t\tprv->numvisibility = l->filelen;\n\n\t\tprv->q3pvs = ZG_Malloc(&mod->memgroup, l->filelen);\n\t\tmod->vis = (q2dvis_t *)prv->q3pvs;\n\t\tmemcpy (prv->q3pvs, mod_base + l->fileofs, l->filelen);\n\n\t\tnumclusters = prv->q3pvs->numclusters = LittleLong (prv->q3pvs->numclusters);\n\t\tprv->q3pvs->rowsize = LittleLong (prv->q3pvs->rowsize);\n\t}\n\tmod->numclusters = numclusters;\n\tmod->pvsbytes = ((mod->numclusters + 31)>>3)&~3;\n\n\treturn true;\n}\n\n#ifndef SERVERONLY\nstatic void CModQ3_LoadLighting (model_t *loadmodel, qbyte *mod_base, lump_t *l)\n{\n\tqbyte *in = mod_base + l->fileofs;\n\tqbyte *out;\n\tunsigned int samples = l->filelen;\n\tint m, s, t;\n\tint mapstride = loadmodel->lightmaps.width*3;\n\tint mapsize = mapstride*loadmodel->lightmaps.height;\n\tint maps;\n\tint merge;\n\tint mergestride;\n\n\textern cvar_t gl_overbright;\n\tfloat scale = (1<<(2-gl_overbright.ival));\n\n\tloadmodel->lightmaps.fmt = LM_RGB8;\n\tloadmodel->lightmaps.prebaked = PTI_RGB8;\n\n\t//round up the samples, in case the last one is partial.\n\tmaps = ((samples+mapsize-1)&~(mapsize-1)) / mapsize;\n\n\t//q3 maps have built in 4-fold overbright.\n\t//if we're not rendering with that, we need to brighten the lightmaps in order to keep the darker parts the same brightness. we loose the 2 upper bits. those bright areas become uniform and indistinct.\n\tgl_overbright.flags |= CVAR_RENDERERLATCH;\n\tBuildLightMapGammaTable(1, (1<<(2-gl_overbright.ival)));\n\n\tloadmodel->lightmaps.mergew = 0;\n\tloadmodel->lightmaps.mergeh = 0;\n\n\tloadmodel->engineflags |= MDLF_NEEDOVERBRIGHT;\n\n\tif (!samples)\n\t\treturn;\n\n\tif (loadmodel->lightmaps.deluxemapping)\n\t\tmaps /= 2;\n\n\t{\n\t\tint limitw = sh_config.texture2d_maxsize / loadmodel->lightmaps.width;\n\t\tint limith = sh_config.texture2d_maxsize / loadmodel->lightmaps.height;\n\t\tif (!q3bsp_mergeq3lightmaps.ival)\n\t\t{\n\t\t\tlimitw = 1;\n\t\t\tlimith = 1;\n\t\t}\n\t\tloadmodel->lightmaps.mergeh = loadmodel->lightmaps.mergew = 1;\n\t\twhile (loadmodel->lightmaps.mergew*loadmodel->lightmaps.mergeh < maps)\n\t\t{\t//this could probably be smarter.\n\t\t\tif (loadmodel->lightmaps.mergew*2 <= limitw && loadmodel->lightmaps.mergew < loadmodel->lightmaps.mergeh)\n\t\t\t\tloadmodel->lightmaps.mergew *= 2;\n\t\t\telse if (loadmodel->lightmaps.mergeh*2 <= limith)\n\t\t\t\tloadmodel->lightmaps.mergeh *= 2;\n\t\t\telse if (loadmodel->lightmaps.mergew*2 <= limitw)\n\t\t\t\tloadmodel->lightmaps.mergew *= 2;\n\t\t\telse\n\t\t\t\tbreak;\t//can't expand in either direction.\n\t\t}\n\t}\n\tmerge = loadmodel->lightmaps.mergew*loadmodel->lightmaps.mergeh;\n\tmergestride = loadmodel->lightmaps.mergew*mapstride;\n\n\t//q3bsp itself does not support deluxemapping.\n\t//the way it works is by interleaving the data in lightmap+deluxemap pairs.\n\t//the surface data makes no references to the deluxemap maps, they're implied by lightmap+1\n\t//if no surface references an odd lightmap index then we know we have deluxemaps... assuming there are at least two lightmaps.\n\t//q3map2 likes generating null lightmaps, so beware false positives.\n\n\t//note that external lighting makes this even more fun.\n\n\t//if we have deluxemapping data then we split it here. beware externals.\n\tif (loadmodel->lightmaps.deluxemapping)\n\t{\n\t\tm = merge;\n\t\twhile (m < maps)\n\t\t\tm += merge;\n\t\tloadmodel->lightdata = ZG_Malloc(&loadmodel->memgroup, mapsize*m*2);\n\t\tloadmodel->lightdatasize = mapsize*m*2;\n\t}\n\telse\n\t{\n\t\tm = merge;\n\t\twhile (m < maps)\n\t\t\tm += merge;\n\t\tloadmodel->lightdata = ZG_Malloc(&loadmodel->memgroup, mapsize*m);\n\t\tloadmodel->lightdatasize = mapsize*m;\n\t}\n\n\tif (!loadmodel->lightdata)\n\t\treturn;\n\n\n\t//be careful here, q3bsp deluxemapping is done using interleaving. we want to unoverbright ONLY lightmaps and not deluxemaps.\n\tfor (m = 0; m < maps; m++)\n\t{\n\t\tout = loadmodel->lightdata;\n\t\t//figure out which merged lightmap we're putting it into\n\t\tout += (m/merge)*merge*mapsize * (loadmodel->lightmaps.deluxemapping?2:1);\n\t\t//and the submap\n\t\ts = m%merge;\n\t\tt = s/loadmodel->lightmaps.mergew;\n\t\ts = s%loadmodel->lightmaps.mergew;\n\t\tout += s*mapstride;\n\t\tout += t*mergestride*loadmodel->lightmaps.height;\n\n\t\t//q3bsp has 4-fold overbrights, so if we're not using overbrights then we basically need to scale the values up by 4\n\t\t//this will require clamping, which can result in oversaturation of channels, meaning discolouration\n\t\tfor (t = 0; t < loadmodel->lightmaps.height; t++)\n\t\t{\n\t\t\tfor (s = 0; s < loadmodel->lightmaps.width; s++)\n\t\t\t{\n\t\t\t\tfloat i;\n\t\t\t\tvec3_t l;\n\t\t\t\tl[0] = *in++;\n\t\t\t\tl[1] = *in++;\n\t\t\t\tl[2] = *in++;\n\t\t\t\tVectorScale(l, scale, l);\t\t//it should be noted that this maths is wrong if you're trying to use srgb lightmaps.\n\t\t\t\ti = max(l[0], max(l[1], l[2]));\n\t\t\t\tif (i > 255)\n\t\t\t\t\tVectorScale(l, 255/i, l);\t//clamp the brightest channel, scaling the others down to retain chromiance.\n\t\t\t\t*out++ = l[0];\n\t\t\t\t*out++ = l[1];\n\t\t\t\t*out++ = l[2];\n\t\t\t}\n\t\t\tout += mergestride-mapstride;\n\t\t}\n\n\t\tif (r_lightmap_saturation.value != 1.0f)\n\t\t\tSaturateR8G8B8(out, mapsize, r_lightmap_saturation.value);\n\t\t\n\t\tif (loadmodel->lightmaps.deluxemapping)\n\t\t{\n\t\t\tout -= mergestride*loadmodel->lightmaps.height;\n\t\t\tout += merge*mapsize;\n\n\t\t\t//no gamma for deluxemap\n\t\t\tfor (t = 0; t < loadmodel->lightmaps.height; t++)\n\t\t\t{\n\t\t\t\tfor (s = 0; s < loadmodel->lightmaps.width; s++)\n\t\t\t\t{\n\t\t\t\t\t*out++ = in[0];\n\t\t\t\t\t*out++ = in[1];\n\t\t\t\t\t*out++ = in[2];\n\t\t\t\t\tin += 3;\n\t\t\t\t}\n\t\t\t\tout += mergestride-mapstride;\n\t\t\t}\n\t\t}\n\t}\n\t/*for (; m%merge; m++)\n\t{\n\t\tout = loadmodel->lightdata;\n\t\t//figure out which merged lightmap we're putting it into\n\t\tout += (m/merge)*merge*mapsize * (loadmodel->lightmaps.deluxemapping?2:1);\n\t\t//and the submap\n\t\tout += (m%merge)*mapsize;\n\n\t\tfor(s = 0; s < mapsize; s+=3)\n\t\t{\n\t\t\tout[s+0] = 0;\n\t\t\tout[s+1] = 255;\n\t\t\tout[s+2] = 0;\n\t\t}\n\t}*/\n}\n\nstatic qboolean CModQ3_LoadLightgrid (model_t *loadmodel, qbyte *mod_base, lump_t *l)\n{\n\tdq3gridlight_t \t*in;\n\tdq3gridlight_t \t*out;\n\tq3lightgridinfo_t *grid;\n\tint\tcount;\n\n\tin = (void *)(mod_base + l->fileofs);\n\tif (l->filelen % sizeof(*in))\n\t{\n\t\tCon_Printf (CON_ERROR \"MOD_LoadBmodel: funny lump size in %s\\n\",loadmodel->name);\n\t\treturn false;\n\t}\n\tcount = l->filelen / sizeof(*in);\n\tgrid = ZG_Malloc(&loadmodel->memgroup, sizeof(q3lightgridinfo_t) + count*sizeof(*out));\n\tgrid->lightgrid = (dq3gridlight_t*)(grid+1);\n\tout = grid->lightgrid;\n\n\tloadmodel->lightgrid = grid;\n\tgrid->numlightgridelems = count;\n\n\t// lightgrid is all 8 bit\n\tmemcpy ( out, in, count*sizeof(*out) );\n\n\treturn true;\n}\n\n#ifdef RFBSPS\nstatic qboolean CModRBSP_LoadLightgrid (model_t *loadmodel, qbyte *mod_base, lump_t *elements, lump_t *indexes)\n{\n\tunsigned short\t*iin;\n\trbspgridlight_t\t*ein;\n\tunsigned short\t*iout;\n\trbspgridlight_t\t*eout;\n\tq3lightgridinfo_t *grid;\n\tint\tecount;\n\tint icount;\n\n\tint i;\n\n\tein = (void *)(mod_base + elements->fileofs);\n\tiin = (void *)(mod_base + indexes->fileofs);\n\tif (indexes->filelen % sizeof(*iin) || elements->filelen % sizeof(*ein))\n\t{\n\t\tCon_Printf (CON_ERROR \"MOD_LoadBmodel: funny lump size in %s\\n\",loadmodel->name);\n\t\treturn false;\n\t}\n\ticount = indexes->filelen / sizeof(*iin);\n\tecount = elements->filelen / sizeof(*ein);\n\n\tgrid = ZG_Malloc(&loadmodel->memgroup, sizeof(q3lightgridinfo_t) + ecount*sizeof(*eout) + icount*sizeof(*iout));\n\tgrid->rbspelements = (rbspgridlight_t*)((char *)grid + sizeof(q3lightgridinfo_t));\n\tgrid->rbspindexes = (unsigned short*)((char *)grid + sizeof(q3lightgridinfo_t) + ecount*sizeof(*eout));\n\teout = grid->rbspelements;\n\tiout = grid->rbspindexes;\n\n\tloadmodel->lightgrid = grid;\n\n\tgrid->numlightgridelems = icount;\n\n\t// elements are all 8 bit\n\tmemcpy ( eout, ein, ecount*sizeof(*eout) );\n\n\tfor (i = 0; i < icount; i++)\n\t\tiout[i] = LittleShort(iin[i]);\n\n\treturn true;\n}\n#endif\n#endif\n#endif\n\n#if !defined(SERVERONLY) && (defined(Q2BSPS) || defined(Q3BSPS))\n#ifdef IMAGEFMT_PCX\nqbyte *ReadPCXPalette(qbyte *buf, int len, qbyte *out);\nstatic int CM_GetQ2Palette (void)\n{\n\tchar *f;\n\tsize_t sz;\n\tsz = FS_LoadFile(\"pics/colormap.pcx\", (void**)&f);\n\tif (!f)\n\t{\n\t\tCon_Printf (CON_WARNING \"Couldn't find pics/colormap.pcx\\n\");\n\t\treturn -1;\n\t}\n\tif (!ReadPCXPalette(f, sz, q2_palette))\n\t{\n\t\tCon_Printf (CON_WARNING \"Couldn't read pics/colormap.pcx\\n\");\n\t\tFS_FreeFile(f);\n\t\treturn -1;\n\t}\n\n\tFS_FreeFile(f);\n\n\n#if 0\n\t{\n\t\tfloat\tinf;\n\t\tqbyte\tpalette[768];\n\t\tqbyte *pal;\n\t\tint\t\ti;\n\n\t\tpal = q2_palette;\n\n\t\tfor (i=0 ; i<768 ; i++)\n\t\t{\n\t\t\tinf = ((pal[i]+1)/256.0)*255 + 0.5;\n\t\t\tif (inf < 0)\n\t\t\t\tinf = 0;\n\t\t\tif (inf > 255)\n\t\t\t\tinf = 255;\n\t\t\tpalette[i] = inf;\n\t\t}\n\n\t\tmemcpy (q2_palette, palette, sizeof(palette));\n\t}\n#endif\n\treturn 0;\n}\n#endif\n#endif\n\n#if 0\nstatic void CM_OpenAllPortals(model_t *mod, char *ents)\t//this is a compleate hack. About as compleate as possible.\n{\t//q2 levels contain a thingie called area portals. Basically, doors can seperate two areas and\n\t//the engine knows when this portal is open, and weather to send ents from both sides of the door\n\t//or not. It's not just ents, but also walls. We want to just open them by default and hope the\n\t//progs knows how to close them.\n\n\tchar style[8];\n\tchar name[64];\n\n\tif (!map_autoopenportals.value)\n\t\treturn;\n\n\twhile(*ents)\n\t{\n\t\tif (*ents == '{')\t//an entity\n\t\t{\n\t\t\tents++;\n\t\t\t*style = '\\0';\n\t\t\t*name = '\\0';\n\t\t\twhile (*ents)\n\t\t\t{\n\t\t\t\tents = COM_Parse(ents);\n\n\t\t\t\tif (!strcmp(com_token, \"classname\"))\n\t\t\t\t{\n\t\t\t\t\tents = COM_ParseOut(ents, name, sizeof(name));\n\t\t\t\t}\n\t\t\t\telse if (!strcmp(com_token, \"style\"))\n\t\t\t\t{\n\t\t\t\t\tents = COM_ParseOut(ents, style, sizeof(style));\n\t\t\t\t}\n\t\t\t\telse if (*com_token == '}')\n\t\t\t\t\tbreak;\n\t\t\t\telse\n\t\t\t\t\tents = COM_Parse(ents);\t//other field\n\t\t\t\tents++;\n\t\t\t}\n\n\t\t\tif (!strcmp(name, \"func_areaportal\"))\n\t\t\t{\n\t\t\t\tCMQ2_SetAreaPortalState(mod, atoi(style), true);\n\t\t\t}\n\t\t}\n\n\t\tents++;\n\t}\n}\n#endif\n\n\n#if defined(Q3BSPS)\nstatic void CalcClusterPHS(cminfo_t\t*prv, int cluster)\n{\n\tint j, k, l, index;\n\tint bitbyte;\n\tunsigned int *dest, *src;\n\tqbyte *scan;\n\n\tint numclusters = prv->q3pvs->numclusters;\n\tint rowbytes = prv->q3pvs->rowsize;\n\tint rowwords = rowbytes / sizeof(int);\n\n\tscan = (qbyte *)prv->q3pvs->data;\n\tdest = (unsigned int *)(prv->q3phs->data);\n\n\tdest += rowwords*cluster;\n\tscan += rowbytes*cluster;\n\tfor (j=0 ; j<rowbytes ; j++)\n\t{\n\t\tbitbyte = scan[j];\n\t\tif (!bitbyte)\n\t\t\tcontinue;\n\t\tfor (k=0 ; k<8 ; k++)\n\t\t{\n\t\t\tif (! (bitbyte & (1<<k)) )\n\t\t\t\tcontinue;\n\t\t\t// OR this pvs row into the phs\n\t\t\tindex = (j<<3) + k;\n\t\t\tif (index >= numclusters)\n\t\t\t{\n//\t\t\t\tif (!buggytools)\n//\t\t\t\t\tCon_Printf (\"CM_CalcPHS: Bad bit(s) in PVS (%i >= %i)\\n\", index, numclusters);\t// pad bits should be 0\n//\t\t\t\tbuggytools = true;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tsrc = (unsigned int *)(prv->q3pvs->data) + index*rowwords;\n\t\t\t\tfor (l=0 ; l<rowwords ; l++)\n\t\t\t\t\tdest[l] |= src[l];\n\t\t\t}\n\t\t}\n\t}\n\tprv->phscalced[cluster>>3] |= 1<<(cluster&7);\n}\n#endif\n#if defined(HAVE_SERVER) && defined(Q3BSPS)\nstatic void CMQ3_CalcPHS (model_t *mod)\n{\n\tcminfo_t\t*prv = (cminfo_t*)mod->meshinfo;\n\tint\t\trowbytes, rowwords;\n\tint\t\ti, j, k, l, index;\n\tint\t\tbitbyte;\n\tunsigned int\t*dest, *src;\n\tqbyte\t*scan;\n\tint\t\tcount, vcount;\n\tint\t\tnumclusters;\n\tqboolean buggytools = false;\n\textern cvar_t sv_calcphs;\n\n\tCon_DPrintf (\"Building PHS...\\n\");\n\n\tprv->q3phs = ZG_Malloc(&mod->memgroup, sizeof(*prv->q3phs) + prv->q3pvs->rowsize * prv->q3pvs->numclusters);\n\n\trowwords = prv->q3pvs->rowsize / sizeof(int);\n\trowbytes = prv->q3pvs->rowsize;\n\n\tmemset ( prv->q3phs, 0, sizeof(*prv->q3phs) + prv->q3pvs->rowsize * prv->q3pvs->numclusters );\n\n\tprv->q3phs->rowsize = prv->q3pvs->rowsize;\n\tprv->q3phs->numclusters = numclusters = prv->q3pvs->numclusters;\n\tif (!numclusters)\n\t\treturn;\n\n\tvcount = 0;\n\tfor (i=0 ; i<numclusters ; i++)\n\t{\n\t\tscan = CM_ClusterPVS (mod, i, NULL, PVM_FAST);\n\t\tfor (j=0 ; j<numclusters ; j++)\n\t\t{\n\t\t\tif ( scan[j>>3] & (1<<(j&7)) )\n\t\t\t{\n\t\t\t\tvcount++;\n\t\t\t}\n\t\t}\n\t}\n\n\tcount = 0;\n\tscan = (qbyte *)prv->q3pvs->data;\n\tdest = (unsigned int *)(prv->q3phs->data);\n\n\tif (sv_calcphs.ival >= 2)\n\t{\t//delay-calculate it.\n\t\tprv->phscalced = ZG_Malloc(&mod->memgroup, (prv->q3pvs->numclusters+7)/8);\n\t\tmemcpy(dest, scan, rowbytes*numclusters);\n\t\tCon_DPrintf (\"Average clusters visible / total: %i / %i\\n\"\n\t\t\t, vcount/numclusters, numclusters);\n\t}\n\telse if (!sv_calcphs.ival)\n\t{\t//disable calcs (behaves like broadcast so just fill with 1s)\n\t\tmemset(dest, 0xff, rowbytes*numclusters);\n\t}\n\telse\n\t{\t//the original slow logic like q3.\n\t\tfor (i=0 ; i<numclusters ; i++, dest += rowwords, scan += rowbytes)\n\t\t{\n\t\t\tmemcpy (dest, scan, rowbytes);\n\t\t\tfor (j=0 ; j<rowbytes ; j++)\n\t\t\t{\n\t\t\t\tbitbyte = scan[j];\n\t\t\t\tif (!bitbyte)\n\t\t\t\t\tcontinue;\n\t\t\t\tfor (k=0 ; k<8 ; k++)\n\t\t\t\t{\n\t\t\t\t\tif (! (bitbyte & (1<<k)) )\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t// OR this pvs row into the phs\n\t\t\t\t\tindex = (j<<3) + k;\n\t\t\t\t\tif (index >= numclusters)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (!buggytools)\n\t\t\t\t\t\t\tCon_Printf (\"CM_CalcPHS: Bad bit(s) in PVS (%i >= %i)\\n\", index, numclusters);\t// pad bits should be 0\n\t\t\t\t\t\tbuggytools = true;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tsrc = (unsigned int *)(prv->q3pvs->data) + index*rowwords;\n\t\t\t\t\t\tfor (l=0 ; l<rowwords ; l++)\n\t\t\t\t\t\t\tdest[l] |= src[l];\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tfor (j=0 ; j<numclusters ; j++)\n\t\t\t\tif ( ((qbyte *)dest)[j>>3] & (1<<(j&7)) )\n\t\t\t\t\tcount++;\n\t\t}\n\n\t\tCon_DPrintf (\"Average clusters visible / hearable / total: %i / %i / %i\\n\"\n\t\t\t, vcount/numclusters, count/numclusters, numclusters);\n\t}\n}\n#endif\n\n/*\nstatic qbyte *CM_LeafnumPVS (model_t *model, int leafnum, qbyte *buffer, unsigned int buffersize)\n{\n\treturn CM_ClusterPVS(model, CM_LeafCluster(model, leafnum), buffer, buffersize);\n}\n*/\n\n#ifndef SERVERONLY\n#define GLQ2BSP_LightPointValues GLQ1BSP_LightPointValues\n\nextern int\tr_dlightframecount;\nstatic void Q2BSP_MarkLights (dlight_t *light, dlightbitmask_t bit, mnode_t *node)\n{\n\tmplane_t\t*splitplane;\n\tfloat\t\tdist;\n\tmsurface_t\t*surf;\n\tint\t\t\ti;\n\n\tif (node->contents != -1)\n\t{\n\t\tmleaf_t *leaf = (mleaf_t *)node;\n\t\tmsurface_t **mark;\n\n\t\ti = leaf->nummarksurfaces;\n\t\tmark = leaf->firstmarksurface;\n\t\twhile(i--!=0)\n\t\t{\n\t\t\tsurf = *mark++;\n\t\t\tif (surf->dlightframe != r_dlightframecount)\n\t\t\t{\n\t\t\t\tsurf->dlightbits = 0;\n\t\t\t\tsurf->dlightframe = r_dlightframecount;\n\t\t\t}\n\t\t\tsurf->dlightbits |= bit;\n\t\t}\n\t\treturn;\n\t}\n\n\tsplitplane = node->plane;\n\tdist = DotProduct (light->origin, splitplane->normal) - splitplane->dist;\n\n\tif (dist > light->radius)\n\t{\n\t\tQ2BSP_MarkLights (light, bit, node->children[0]);\n\t\treturn;\n\t}\n\tif (dist < -light->radius)\n\t{\n\t\tQ2BSP_MarkLights (light, bit, node->children[1]);\n\t\treturn;\n\t}\n\n// mark the polygons\n\tsurf = cl.worldmodel->surfaces + node->firstsurface;\n\tfor (i=0 ; i<node->numsurfaces ; i++, surf++)\n\t{\n\t\tif (surf->dlightframe != r_dlightframecount)\n\t\t{\n\t\t\tsurf->dlightbits = 0u;\n\t\t\tsurf->dlightframe = r_dlightframecount;\n\t\t}\n\t\tsurf->dlightbits |= bit;\n\t}\n\n\tQ2BSP_MarkLights (light, bit, node->children[0]);\n\tQ2BSP_MarkLights (light, bit, node->children[1]);\n}\n\n#ifndef SERVERONLY\nstatic void GLR_Q2BSP_StainNode_r (model_t *model, mnode_t *node, float *parms)\n{\n\tmplane_t\t*splitplane;\n\tfloat\t\tdist;\n\tmsurface_t\t*surf;\n\tint\t\t\ti;\n\n\tif (node->contents != -1)\n\t\treturn;\n\n\tsplitplane = node->plane;\n\tdist = DotProduct ((parms+1), splitplane->normal) - splitplane->dist;\n\n\tif (dist > (*parms))\n\t{\n\t\tGLR_Q2BSP_StainNode_r (model, node->children[0], parms);\n\t\treturn;\n\t}\n\tif (dist < (-*parms))\n\t{\n\t\tGLR_Q2BSP_StainNode_r (model, node->children[1], parms);\n\t\treturn;\n\t}\n\n// mark the polygons\n\tsurf = model->surfaces + node->firstsurface;\n\tfor (i=0 ; i<node->numsurfaces ; i++, surf++)\n\t{\n\t\tif (surf->flags&~(SURF_DONTWARP|SURF_PLANEBACK))\n\t\t\tcontinue;\n\t\tSurf_StainSurf(model, surf, parms);\n\t}\n\n\tGLR_Q2BSP_StainNode_r (model, node->children[0], parms);\n\tGLR_Q2BSP_StainNode_r (model, node->children[1], parms);\n}\nstatic void GLR_Q2BSP_StainNode (model_t *model, float *parms)\n{\n\tGLR_Q2BSP_StainNode_r(model, model->rootnode, parms);\n}\n#endif\n\n#endif\n\nstatic void CM_BuildBIH(model_t *mod, int submodel)\n{\n\tcminfo_t\t*prv = (cminfo_t*)mod->meshinfo;\n\tcmodel_t\t*sub = &prv->cmodels[submodel];\n\n\tstruct bihleaf_s *bihleaf, *l;\n\tsize_t bihleafs, i;\n\n\t//undo any bih damage on the copy\n\tmod->cnodes = NULL;\n\tmod->funcs.NativeTrace\t\t\t= CM_NativeTrace;\n\tmod->funcs.NativeContents\t\t= CM_NativeContents;\n\tmod->funcs.PointContents\t\t= Q2BSP_PointContents;\n\tif (!q3bsp_bihtraces.ival)\n\t\treturn;\t//skip this. fall back on other stuff.\n\n\tif (mod->fromgame != fg_quake3)\n\t\treturn;\n\n\tbihleafs = sub->num_brushes;\n\tfor (i = 0; i < sub->num_patches; i++)\n\t\tbihleafs += prv->patches[sub->firstpatch + i].numfacets;\n\tfor (i = 0; i < sub->num_cmeshes; i++)\n\t\tbihleafs += prv->cmeshes[sub->firstcmesh + i].numincidies/3;\n\tbihleaf = l = BZ_Malloc(sizeof(*bihleaf)*bihleafs);\n\n\t//now we have enough storage, spit them out providing bounds info.\n\tfor (i = 0; i < sub->num_brushes; i++)\n\t{\n\t\tq2cbrush_t *b = &prv->brushes[sub->firstbrush+i];\n\t\tl->type = BIH_BRUSH;\n\t\tl->data.brush = b;\n\n\t\tl->data.contents = b->contents;\n\t\tVectorCopy(b->absmins, l->mins);\n\t\tVectorCopy(b->absmaxs, l->maxs);\n\t\tl++;\n\t}\n#ifdef Q3BSPS\n\tfor (i = 0; i < sub->num_patches; i++)\n\t{\n\t\tq3cpatch_t *p = &prv->patches[sub->firstpatch+i];\n\t\tsize_t j;\n\t\tfor (j = 0; j < p->numfacets; j++)\n\t\t{\n\t\t\tq2cbrush_t *b = &p->facets[j];\n\t\t\tl->type = BIH_PATCHBRUSH;\n\t\t\tl->data.patchbrush = b;\n\t\t\tl->data.contents = b->contents;\n\t\t\tVectorCopy(b->absmins, l->mins);\n\t\t\tVectorCopy(b->absmaxs, l->maxs);\n\t\t\tl++;\n\t\t}\n\t}\n#endif\n#ifdef Q3BSPS\n\tfor (i = 0; i < sub->num_cmeshes; i++)\n\t{\n\t\tq3cmesh_t *m = &prv->cmeshes[sub->firstcmesh+i];\n\t\tsize_t j;\n\t\tfor (j = 0; j+2 < m->numincidies; j+=3)\n\t\t{\n\t\t\tindex_t *v = m->indicies+j;\n\t\t\tvec_t *v1 = m->xyz_array[v[0]], *v2 = m->xyz_array[v[1]], *v3 = m->xyz_array[v[2]];\n\n\t\t\tl->type = BIH_TRIANGLE;\n\t\t\tl->data.tri.xyz = m->xyz_array;\n\t\t\tl->data.tri.indexes = v;\n\n\t\t\tl->data.contents = m->surface->c.value;\n\t\t\tVectorCopy(v1, l->mins);\n\t\t\tVectorCopy(v1, l->maxs);\n\t\t\tAddPointToBounds(v2, l->mins, l->maxs);\n\t\t\tAddPointToBounds(v3, l->mins, l->maxs);\n\t\t\tl++;\n\t\t}\n\t}\n#endif\n\n\tBIH_Build(mod, bihleaf, l-bihleaf);\n\tBZ_Free(bihleaf);\n}\n\n#ifdef AVAIL_ZLIB\n#include <zlib.h>\t//for crc32.\n#endif\n/*\n==================\nCM_LoadMap\n\nLoads in the map and all submodels\n==================\n*/\nstatic cmodel_t *CM_LoadMap (model_t *mod, qbyte *filein, size_t filelen, qboolean clientload)\n{\n\tunsigned\t\t*buf;\n\tint\t\t\t\ti;\n\tq2dheader_t\t\theader;\n\tint\t\t\t\tlength;\n\tqboolean noerrors = true;\n\tmodel_t\t\t\t*wmod = mod;\n\tchar\t\t\tloadname[32];\n\tqbyte\t\t\t*mod_base = (qbyte *)filein;\n\tbspx_header_t\t*bspx = NULL;\n\tunsigned int\tchecksum1, checksum2;\n#ifdef Q3BSPS\n\textern cvar_t\tgl_overbright;\n#endif\n\n#ifndef SERVERONLY\n\tvoid (*buildmeshes)(model_t *mod, msurface_t *surf, builddata_t *cookie) = NULL;\n\tqbyte *facedata = NULL;\n\tunsigned int facesize = 0;\n#endif\n\tcminfo_t\t*prv;\n\tqboolean isbig;\n\n\tCOM_FileBase (mod->name, loadname, sizeof(loadname));\n\n\t// free old stuff\n\tmod->meshinfo = prv = ZG_Malloc(&mod->memgroup, sizeof(*prv));\n\tprv->numcmodels = 0;\n\tprv->numvisibility = 0;\n\n\tmod->type = mod_brush;\n\n\tif (!mod->name[0])\n\t{\n\t\tprv->cmodels = ZG_Malloc(&mod->memgroup, 1 * sizeof(*prv->cmodels));\n\t\tmod->leafs = ZG_Malloc(&mod->memgroup, 1 * sizeof(*mod->leafs));\n\t\tmod->funcs.AreasConnected\t\t= CM_AreasConnected;\n\t\tprv->numcmodels = 1;\n\t\tprv->numareas = 1;\n\t\tmod->checksum = mod->checksum2 = 0;\n\t\tprv->cmodels[0].headnode = (mnode_t*)mod->leafs;\t//directly start with the empty leaf\n\t\treturn &prv->cmodels[0];\t\t\t// cinematic servers won't have anything at all\n\t}\n\n\t//\n\t// load the file\n\t//\n\tbuf = (unsigned\t*)filein;\n\tlength = filelen;\n\tif (!buf)\n\t{\n\t\tCon_Printf (CON_ERROR \"Couldn't load %s\\n\", mod->name);\n\t\treturn NULL;\n\t}\n\n\tchecksum1 = LittleLong (CalcHashInt(&hash_md4, buf, length));\n#ifdef AVAIL_ZLIB\n\tchecksum2 = crc32(0, (void*)buf, length);\t//q2rerelease uses crc32 instead... *sigh*\n#else\n\tchecksum2 = checksum1;\t//we accept either, so wimp out.\n#endif\n\n\theader = *(q2dheader_t *)(buf);\n\theader.ident = LittleLong(header.ident);\n\theader.version = LittleLong(header.version);\n\n\tClearBounds(mod->mins, mod->maxs);\n\n\tswitch(header.version)\n\t{\n\tdefault:\n\t\tCon_Printf (CON_ERROR \"Quake 2 or Quake 3 based BSP with unknown header (%s: %i should be %i or %i)\\n\"\n\t\t\t, mod->name, header.version, BSPVERSION_Q2, BSPVERSION_Q3);\n\t\treturn NULL;\n\t\tbreak;\n#ifdef Q3BSPS\n#ifdef RFBSPS\n\tcase BSPVERSION_RBSP: //rbsp/fbsp\n#endif\n\tcase BSPVERSION_RTCW:\t//rtcw\n\tcase BSPVERSION_Q3:\n#ifdef RFBSPS\n\t\tif (header.ident == (('F'<<0)+('B'<<8)+('S'<<16)+('P'<<24)))\n\t\t{\n\t\t\tmod->lightmaps.width = 512;\n\t\t\tmod->lightmaps.height = 512;\n\t\t}\n\t\telse\n#endif\n\t\t{\n\t\t\tmod->lightmaps.width = 128;\n\t\t\tmod->lightmaps.height = 128;\n\t\t}\n\n\t\tprv->mapisq3 = true;\n\t\tmod->fromgame = fg_quake3;\n\t\tfor (i=0 ; i<Q3LUMPS_TOTAL ; i++)\n\t\t{\n#ifdef RFBSPS\n\t\t\tif (i == RBSPLUMP_LIGHTINDEXES && header.version != BSPVERSION_RBSP)\n\t\t\t{\n\t\t\t\theader.lumps[i].filelen = 0;\n\t\t\t\theader.lumps[i].fileofs = 0;\n\t\t\t}\n\t\t\telse\n#endif\n\t\t\t{\n\t\t\t\theader.lumps[i].filelen = LittleLong (header.lumps[i].filelen);\n\t\t\t\theader.lumps[i].fileofs = LittleLong (header.lumps[i].fileofs);\n\n\t\t\t\tif (header.lumps[i].filelen && header.lumps[i].fileofs + header.lumps[i].filelen > filelen)\n\t\t\t\t{\n\t\t\t\t\tCon_Printf (CON_ERROR \"WARNING: q3bsp %s truncated (lump %i, %i+%i > %u)\\n\", mod->name, i, header.lumps[i].fileofs, header.lumps[i].filelen, (unsigned int)filelen);\n\t\t\t\t\theader.lumps[i].filelen = filelen - header.lumps[i].fileofs;\n\t\t\t\t\tif (header.lumps[i].filelen < 0)\n\t\t\t\t\t\theader.lumps[i].filelen = 0;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t/*\n\t\t#ifndef SERVERONLY\n\t\t\tGLMod_LoadVertexes\t\t(mod, cmod_base, &header.lumps[Q3LUMP_DRAWVERTS]);\n//\t\t\tGLMod_LoadEdges\t\t\t(mod, cmod_base, &header.lumps[Q3LUMP_EDGES]);\n//\t\t\tGLMod_LoadSurfedges\t\t(mod, cmod_base, &header.lumps[Q3LUMP_SURFEDGES]);\n\t\t\tGLMod_LoadLighting\t\t(mod, cmod_base, &header.lumps[Q3LUMP_LIGHTMAPS]);\n\t\t#endif\n\t\t\tCModQ3_LoadShaders\t\t(mod, cmod_base, &header.lumps[Q3LUMP_SHADERS]);\n\t\t\tCModQ3_LoadPlanes\t\t(mod, cmod_base, &header.lumps[Q3LUMP_PLANES]);\n\t\t\tCModQ3_LoadLeafBrushes\t(mod, cmod_base, &header.lumps[Q3LUMP_LEAFBRUSHES]);\n\t\t\tCModQ3_LoadBrushes\t\t(mod, cmod_base, &header.lumps[Q3LUMP_BRUSHES]);\n\t\t\tCModQ3_LoadBrushSides\t(mod, cmod_base, &header.lumps[Q3LUMP_BRUSHSIDES]);\n\t\t#ifndef SERVERONLY\n\t\t\tCMod_LoadTexInfo\t\t(mod, cmod_base, &header.lumps[Q3LUMP_SHADERS]);\n\t\t\tCMod_LoadFaces\t\t\t(mod, cmod_base, &header.lumps[Q3LUMP_SURFACES]);\n//\t\t\tGLMod_LoadMarksurfaces\t(mod, cmod_base, &header.lumps[Q3LUMP_LEAFFACES]);\n\t\t#endif\n\t\t\tCMod_LoadVisibility\t\t(mod, cmod_base, &header.lumps[Q3LUMP_VISIBILITY]);\n\t\t\tCModQ3_LoadSubmodels\t(mod, cmod_base, &header.lumps[Q3LUMP_MODELS]);\n\t\t\tCModQ3_LoadLeafs\t\t(mod, cmod_base, &header.lumps[Q3LUMP_LEAFS]);\n\t\t\tCModQ3_LoadNodes\t\t(mod, cmod_base, &header.lumps[Q3LUMP_NODES]);\n//\t\t\tCMod_LoadAreas\t\t\t(mod, cmod_base, &header.lumps[Q3LUMP_AREAS]);\n//\t\t\tCMod_LoadAreaPortals\t(mod, cmod_base, &header.lumps[Q3LUMP_AREAPORTALS]);\n\t\t\tCMod_LoadEntityString\t(mod, cmod_base, &header.lumps[Q3LUMP_ENTITIES]);\n*/\n\n\t\tprv->faces = NULL;\n\n\t\tbspx = BSPX_Setup(mod, mod_base, filelen, header.lumps, Q3LUMPS_TOTAL);\n\n\t\t//q3 maps have built in 4-fold overbright.\n\t\t//if we're not rendering with that, we need to brighten the lightmaps in order to keep the darker parts the same brightness. we loose the 2 upper bits. those bright areas become uniform and indistinct.\n\t\t//this is used for both the lightmap AND vertex lighting\n\t\t//FIXME: when not using overbrights, we suffer a loss of precision.\n\t\tgl_overbright.flags |= CVAR_RENDERERLATCH;\n\t\tBuildLightMapGammaTable(1, (1<<(2-gl_overbright.ival)));\n\n\t\tprv->mapisq3 = true;\n\t\tnoerrors = noerrors && CModQ3_LoadShaders\t\t\t\t(mod, mod_base, &header.lumps[Q3LUMP_SHADERS]);\n\t\tnoerrors = noerrors && CModQ3_LoadPlanes\t\t\t\t(mod, mod_base, &header.lumps[Q3LUMP_PLANES]);\n#ifdef RFBSPS\n\t\tif (header.version == BSPVERSION_RBSP)\n\t\t{\n\t\t\tnoerrors = noerrors && CModRBSP_LoadBrushSides\t\t(mod, mod_base, &header.lumps[Q3LUMP_BRUSHSIDES]);\n\t\t\tnoerrors = noerrors && CModRBSP_LoadVertexes\t\t(mod, mod_base, &header.lumps[Q3LUMP_DRAWVERTS]);\n\t\t}\n\t\telse\n#endif\n\t\t{\n\t\t\tnoerrors = noerrors && CModQ3_LoadBrushSides\t\t(mod, mod_base, &header.lumps[Q3LUMP_BRUSHSIDES]);\n\t\t\tnoerrors = noerrors && CModQ3_LoadVertexes\t\t\t(mod, mod_base, &header.lumps[Q3LUMP_DRAWVERTS]);\n\t\t}\n\t\tnoerrors = noerrors && CModQ3_LoadBrushes\t\t\t\t(mod, mod_base, &header.lumps[Q3LUMP_BRUSHES]);\n\t\tnoerrors = noerrors && CModQ3_LoadLeafBrushes\t\t\t(mod, mod_base, &header.lumps[Q3LUMP_LEAFBRUSHES]);\n#ifdef RFBSPS\n\t\tif (header.version == BSPVERSION_RBSP)\n\t\t\tnoerrors = noerrors && CModRBSP_LoadFaces\t\t\t(mod, mod_base, &header.lumps[Q3LUMP_SURFACES]);\n\t\telse\n#endif\n\t\t\tnoerrors = noerrors && CModQ3_LoadFaces\t\t\t\t(mod, mod_base, &header.lumps[Q3LUMP_SURFACES]);\n\n\t\tif (noerrors)\n\t\t\tMod_LoadEntities\t\t\t\t\t\t\t\t(mod, mod_base, &header.lumps[Q3LUMP_ENTITIES]);\n#ifndef SERVERONLY\n\t\tif (qrenderer != QR_NONE)\n\t\t{\n#ifdef RFBSPS\n\t\t\tif (header.version == BSPVERSION_RBSP)\n\t\t\t\tnoerrors = noerrors && CModRBSP_LoadLightgrid\t(mod, mod_base, &header.lumps[Q3LUMP_LIGHTGRID], &header.lumps[RBSPLUMP_LIGHTINDEXES]);\n\t\t\telse\n#endif\n\t\t\t\tnoerrors = noerrors && CModQ3_LoadLightgrid\t\t(mod, mod_base, &header.lumps[Q3LUMP_LIGHTGRID]);\n\t\t\tnoerrors = noerrors && CModQ3_LoadIndexes\t\t\t(mod, mod_base, &header.lumps[Q3LUMP_DRAWINDEXES]);\n\n\t\t\tif (header.version != BSPVERSION_RTCW)\n\t\t\t\tnoerrors = noerrors && CModQ3_LoadFogs\t\t\t(mod, mod_base, &header.lumps[Q3LUMP_FOGS]);\n\t\t\telse\n\t\t\t\tmod->numfogs = 0;\n\n\t\t\tfacedata = (void *)(mod_base + header.lumps[Q3LUMP_SURFACES].fileofs);\n#ifdef RFBSPS\n\t\t\tif (header.version == BSPVERSION_RBSP)\n\t\t\t{\n\t\t\t\tnoerrors = noerrors && CModRBSP_LoadRFaces\t\t(mod, mod_base, &header.lumps[Q3LUMP_SURFACES]);\n\t\t\t\tbuildmeshes = CModRBSP_BuildSurfMesh;\n\t\t\t\tfacesize = sizeof(rbspface_t);\n\t\t\t\tmod->lightmaps.surfstyles = 4;\n\t\t\t}\n\t\t\telse\n#endif\n\t\t\t{\n\t\t\t\tnoerrors = noerrors && CModQ3_LoadRFaces\t\t(mod, mod_base, &header.lumps[Q3LUMP_SURFACES]);\n\t\t\t\tbuildmeshes = CModQ3_BuildSurfMesh;\n\t\t\t\tfacesize = sizeof(q3dface_t);\n\t\t\t\tmod->lightmaps.surfstyles = 1;\n\t\t\t}\n\t\t\tif (noerrors)\n\t\t\t{\n\t\t\t\ti = header.lumps[Q3LUMP_LIGHTMAPS].filelen / (mod->lightmaps.width*mod->lightmaps.height*3);\n\t\t\t\tmod->lightmaps.deluxemapping = !(i&1);\n\t\t\t\tmod->lightmaps.count = max(mod->lightmaps.count, i);\n\t\t\t\tmod->lightmaps.deluxemapping_modelspace = true;\t//we assume true for q3bsp.\n\n\t\t\t\tfor (i = 0; i < mod->numsurfaces && mod->lightmaps.deluxemapping; i++)\n\t\t\t\t{\n\t\t\t\t\tif (mod->surfaces[i].lightmaptexturenums[0] >= 0 && (mod->surfaces[i].lightmaptexturenums[0] & 1))\n\t\t\t\t\t\tmod->lightmaps.deluxemapping = false;\n\t\t\t\t}\n\n\t\t\t\t{\n\t\t\t\t\tchar deluxeMaps[64], *key;\n\t\t\t\t\tkey = (char*)Mod_ParseWorldspawnKey(mod, \"deluxeMaps\", deluxeMaps, sizeof(deluxeMaps));\n\t\t\t\t\tif (*key)\n\t\t\t\t\t{\n\t\t\t\t\t\tswitch(atoi(key))\n\t\t\t\t\t\t{\n\t\t\t\t\t\tcase 0:\n\t\t\t\t\t\t\tmod->lightmaps.deluxemapping = false;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 1:\n\t\t//\t\t\t\t\tmod->lightmaps.deluxemapping = true;\n\t\t\t\t\t\t\tmod->lightmaps.deluxemapping_modelspace = true;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 2:\n\t\t//\t\t\t\t\tmod->lightmaps.deluxemapping = true;\n\t\t\t\t\t\t\tmod->lightmaps.deluxemapping_modelspace = false;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (noerrors)\n\t\t\t\tCModQ3_LoadLighting\t\t\t\t\t\t\t\t(mod, mod_base, &header.lumps[Q3LUMP_LIGHTMAPS]);\t//fixme: duplicated loading.\n\t\t}\n#endif\n\t\tnoerrors = noerrors && CModQ3_LoadMarksurfaces\t\t\t(mod, mod_base, &header.lumps[Q3LUMP_LEAFSURFACES]);\n\t\tnoerrors = noerrors && CModQ3_LoadLeafs\t\t\t\t\t(mod, mod_base, &header.lumps[Q3LUMP_LEAFS]);\n\t\tnoerrors = noerrors && CModQ3_LoadNodes\t\t\t\t\t(mod, mod_base, &header.lumps[Q3LUMP_NODES]);\n\t\tnoerrors = noerrors && CModQ3_LoadSubmodels\t\t\t\t(mod, mod_base, &header.lumps[Q3LUMP_MODELS]);\n\t\tnoerrors = noerrors && CModQ3_LoadVisibility\t\t\t(mod, mod_base, &header.lumps[Q3LUMP_VISIBILITY]);\n\n\t\tif (!noerrors)\n\t\t{\n\t\t\tif (prv->faces)\n\t\t\t\tBZ_Free(prv->faces);\n\t\t\treturn NULL;\n\t\t}\n\n#ifdef HAVE_SERVER\n\t\tmod->funcs.FatPVS\t\t\t\t= Q23BSP_FatPVS;\n\t\tmod->funcs.EdictInFatPVS\t\t= Q23BSP_EdictInFatPVS;\n\t\tmod->funcs.FindTouchedLeafs\t\t= Q23BSP_FindTouchedLeafs;\n#endif\n\t\tmod->funcs.ClusterPVS\t\t\t= CM_ClusterPVS;\n\t\tmod->funcs.ClusterPHS\t\t\t= CM_ClusterPHS;\n\t\tmod->funcs.ClusterForPoint\t\t= CM_PointCluster;\n\n#ifdef HAVE_CLIENT\n\t\tmod->funcs.LightPointValues\t\t= GLQ3_LightGrid;\n\t\tmod->funcs.StainNode\t\t\t= GLR_Q2BSP_StainNode;\n\t\tmod->funcs.MarkLights\t\t\t= Q2BSP_MarkLights;\n\t\tmod->funcs.PrepareFrame\t\t\t= CM_PrepareFrame;\n#ifdef RTLIGHTS\n\t\tmod->funcs.GenerateShadowMesh\t= Q3BSP_GenerateShadowMesh;\n#endif\n#endif\n\t\tmod->funcs.PointContents\t\t= Q2BSP_PointContents;\n\t\tmod->funcs.NativeTrace\t\t\t= CM_NativeTrace;\n\t\tmod->funcs.NativeContents\t\t= CM_NativeContents;\n\n\t\tmod->funcs.InfoForPoint\t\t\t= CM_InfoForPoint;\n\t\tmod->funcs.AreasConnected\t\t= CM_AreasConnected;\n\t\tmod->funcs.SetAreaPortalState\t= CM_SetAreaPortalState;\n\t\tmod->funcs.WriteAreaBits\t\t= CM_WriteAreaBits;\n\t\tmod->funcs.LoadAreaPortalBlob\t= CM_LoadAreaPortalBlob;\n\t\tmod->funcs.SaveAreaPortalBlob\t= CM_SaveAreaPortalBlob;\n\n#ifdef HAVE_CLIENT\n\t\t//light grid info\n\t\tif (mod->lightgrid)\n\t\t{\n\t\t\tchar gridsize[256], *key;\n\t\t\tchar val[64];\n\t\t\tfloat maxs;\n\t\t\tq3lightgridinfo_t *lg = mod->lightgrid;\n\t\t\tkey = (char*)Mod_ParseWorldspawnKey(mod, \"gridsize\", gridsize, sizeof(gridsize));\n\n\t\t\tkey = COM_ParseOut(key, val, sizeof(val));\n\t\t\tlg->gridSize[0] = atof(val);\n\t\t\tkey = COM_ParseOut(key, val, sizeof(val));\n\t\t\tlg->gridSize[1] = atof(val);\n\t\t\tkey = COM_ParseOut(key, val, sizeof(val));\n\t\t\tlg->gridSize[2] = atof(val);\n\n\t\t\tif ( lg->gridSize[0] < 1 || lg->gridSize[1] < 1 || lg->gridSize[2] < 1 )\n\t\t\t{\n\t\t\t\tlg->gridSize[0] = 64;\n\t\t\t\tlg->gridSize[1] = 64;\n\t\t\t\tlg->gridSize[2] = 128;\n\t\t\t}\n\n\t\t\tfor ( i = 0; i < 3; i++ )\n\t\t\t{\n\t\t\t\tlg->gridMins[i] = lg->gridSize[i] * ceil( (prv->cmodels->mins[i] + 1) / lg->gridSize[i] );\n\t\t\t\tmaxs = lg->gridSize[i] * floor( (prv->cmodels->maxs[i] - 1) / lg->gridSize[i] );\n\t\t\t\tlg->gridBounds[i] = (maxs - lg->gridMins[i])/lg->gridSize[i] + 1;\n\t\t\t}\n\n\t\t\tlg->gridBounds[3] = lg->gridBounds[1] * lg->gridBounds[0];\n\t\t}\n#endif\n\n\t\tif (!CM_CreatePatchesForLeafs (mod, prv))\t//for clipping\n\t\t{\n\t\t\tBZ_Free(prv->faces);\n\t\t\treturn NULL;\n\t\t}\n#ifdef HAVE_SERVER\n\t\tCMQ3_CalcPHS(mod);\n#endif\n//\t\t\tBZ_Free(map_verts);\n\t\tBZ_Free(prv->faces);\n\t\tbreak;\n#endif\n#ifdef Q2BSPS\n\tcase BSPVERSION_Q2:\n\tcase BSPVERSION_Q2W:\n\t\tisbig = *mod_base == 'Q';\t//'qbism'\n\n\t\tmod->lightmaps.width = LMBLOCK_SIZE_MAX;\n\t\tmod->lightmaps.height = LMBLOCK_SIZE_MAX;\n\n\t\tprv->mapisq3 = false;\n\t\tmod->engineflags |= MDLF_NEEDOVERBRIGHT;\n\t\tfor (i=0 ; i<Q2HEADER_LUMPS ; i++)\n\t\t{\n\t\t\theader.lumps[i].filelen = LittleLong (header.lumps[i].filelen);\n\t\t\theader.lumps[i].fileofs = LittleLong (header.lumps[i].fileofs);\n\t\t}\n\t\tif (header.version == BSPVERSION_Q2W)\n\t\t{\n\t\t\theader.lumps[i].filelen = LittleLong (header.lumps[i].filelen);\n\t\t\theader.lumps[i].fileofs = LittleLong (header.lumps[i].fileofs);\n\t\t\ti++;\n\t\t}\n\t\tbspx = BSPX_Setup(mod, mod_base, filelen, header.lumps, i);\n\n#if defined(HAVE_CLIENT) && defined(IMAGEFMT_PCX)\n\t\tif (CM_GetQ2Palette())\n\t\t\tmemcpy(q2_palette, host_basepal, 768);\n#endif\n\n\n#ifdef HAVE_SERVER\n\t\tmod->funcs.FatPVS\t\t\t\t= Q23BSP_FatPVS;\n\t\tmod->funcs.EdictInFatPVS\t\t= Q23BSP_EdictInFatPVS;\n\t\tmod->funcs.FindTouchedLeafs\t\t= Q23BSP_FindTouchedLeafs;\n#endif\n\t\tmod->funcs.LightPointValues\t\t= NULL;\n\t\tmod->funcs.StainNode\t\t\t= NULL;\n\t\tmod->funcs.MarkLights\t\t\t= NULL;\n\t\tmod->funcs.ClusterPVS\t\t\t= CM_ClusterPVS;\n\t\tmod->funcs.ClusterPHS\t\t\t= CM_ClusterPHS;\n\t\tmod->funcs.ClusterForPoint\t\t= CM_PointCluster;\n\t\tmod->funcs.PointContents\t\t= Q2BSP_PointContents;\n\t\tmod->funcs.NativeTrace\t\t\t= CM_NativeTrace;\n\t\tmod->funcs.NativeContents\t\t= CM_NativeContents;\n\n\t\tmod->funcs.InfoForPoint\t\t\t= CM_InfoForPoint;\n\t\tmod->funcs.AreasConnected\t\t= CM_AreasConnected;\n\t\tmod->funcs.SetAreaPortalState\t= CM_SetAreaPortalState;\n\t\tmod->funcs.WriteAreaBits\t\t= CM_WriteAreaBits;\n\t\tmod->funcs.LoadAreaPortalBlob\t= CM_LoadAreaPortalBlob;\n\t\tmod->funcs.SaveAreaPortalBlob\t= CM_SaveAreaPortalBlob;\n\t\tmod->funcs.PrepareFrame\t\t\t= NULL;\n\n\t\tswitch(qrenderer)\n\t\t{\n\t\tcase QR_NONE:\t//dedicated only\n\t\t\tnoerrors = noerrors && CModQ2_LoadSurfaces\t\t(mod, mod_base, &header.lumps[Q2LUMP_TEXINFO]);\n\t\t\tnoerrors = noerrors && CModQ2_LoadPlanes\t\t(mod, mod_base, &header.lumps[Q2LUMP_PLANES]);\n\t\t\tnoerrors = noerrors && CModQ2_LoadVisibility\t(mod, mod_base, &header.lumps[Q2LUMP_VISIBILITY]);\n\t\t\tnoerrors = noerrors && CModQ2_LoadBrushSides\t(mod, mod_base, &header.lumps[Q2LUMP_BRUSHSIDES], isbig);\n\t\t\tnoerrors = noerrors && CModQ2_LoadBrushes\t\t(mod, mod_base, &header.lumps[Q2LUMP_BRUSHES]);\n\t\t\tnoerrors = noerrors && CModQ2_LoadLeafBrushes\t(mod, mod_base, &header.lumps[Q2LUMP_LEAFBRUSHES], isbig);\n\t\t\tnoerrors = noerrors && CModQ2_LoadLeafs\t\t\t(mod, mod_base, &header.lumps[Q2LUMP_LEAFS], isbig);\n\t\t\tnoerrors = noerrors && CModQ2_LoadNodes\t\t\t(mod, mod_base, &header.lumps[Q2LUMP_NODES], isbig);\n\t\t\tnoerrors = noerrors && CModQ2_LoadSubmodels\t\t(mod, mod_base, &header.lumps[Q2LUMP_MODELS]);\n\t\t\tnoerrors = noerrors && CModQ2_LoadAreas\t\t\t(mod, mod_base, &header.lumps[Q2LUMP_AREAS]);\n\t\t\tnoerrors = noerrors && CModQ2_LoadAreaPortals\t(mod, mod_base, &header.lumps[Q2LUMP_AREAPORTALS]);\n\t\t\tif (noerrors)\n\t\t\t\tMod_LoadEntities\t\t\t\t\t\t\t(mod, mod_base, &header.lumps[Q2LUMP_ENTITIES]);\n\t\t\tbreak;\n#ifdef HAVE_CLIENT\n\t\tdefault:\n\t\t\t// load into heap\n\t\t\tnoerrors = noerrors && Mod_LoadVertexes\t\t\t(mod, mod_base, &header.lumps[Q2LUMP_VERTEXES]);\n\t\t\tnoerrors = noerrors && Mod_LoadEdges\t\t\t(mod, mod_base, &header.lumps[Q2LUMP_EDGES], isbig?sb_long2:sb_none);\n\t\t\tnoerrors = noerrors && Mod_LoadSurfedges\t\t(mod, mod_base, &header.lumps[Q2LUMP_SURFEDGES]);\n\t\t\tnoerrors = noerrors && CModQ2_LoadSurfaces\t\t(mod, mod_base, &header.lumps[Q2LUMP_TEXINFO]);\n\t\t\tnoerrors = noerrors && CModQ2_LoadPlanes\t\t(mod, mod_base, &header.lumps[Q2LUMP_PLANES]);\n\t\t\tnoerrors = noerrors && CModQ2_LoadTexInfo\t\t(mod, mod_base, &header.lumps[Q2LUMP_TEXINFO], loadname);\n\t\t\tif (noerrors)\n\t\t\t\tMod_LoadEntities\t\t\t\t\t\t\t(mod, mod_base, &header.lumps[Q2LUMP_ENTITIES]);\n\t\t\tnoerrors = noerrors && CModQ2_LoadFaces\t\t\t(mod, mod_base, &header.lumps[Q2LUMP_FACES], &header.lumps[Q2LUMP_LIGHTING], header.version == BSPVERSION_Q2W, bspx, isbig);\n\t\t\t\t\t\t\t\t   Mod_LoadVertexNormals(mod, bspx, mod_base, (header.version == BSPVERSION_Q2W)?&header.lumps[19]:NULL);\n\t\t\tnoerrors = noerrors && Mod_LoadMarksurfaces\t\t(mod, mod_base, &header.lumps[Q2LUMP_LEAFFACES], isbig?sb_long2:sb_none);\n\t\t\tnoerrors = noerrors && CModQ2_LoadVisibility\t(mod, mod_base, &header.lumps[Q2LUMP_VISIBILITY]);\n\t\t\tnoerrors = noerrors && CModQ2_LoadBrushSides\t(mod, mod_base, &header.lumps[Q2LUMP_BRUSHSIDES], isbig);\n\t\t\tnoerrors = noerrors && CModQ2_LoadBrushes\t\t(mod, mod_base, &header.lumps[Q2LUMP_BRUSHES]);\n\t\t\tnoerrors = noerrors && CModQ2_LoadLeafBrushes\t(mod, mod_base, &header.lumps[Q2LUMP_LEAFBRUSHES], isbig);\n\t\t\tnoerrors = noerrors && CModQ2_LoadLeafs\t\t\t(mod, mod_base, &header.lumps[Q2LUMP_LEAFS], isbig);\n\t\t\tnoerrors = noerrors && CModQ2_LoadNodes\t\t\t(mod, mod_base, &header.lumps[Q2LUMP_NODES], isbig);\n\t\t\tnoerrors = noerrors && CModQ2_LoadSubmodels\t\t(mod, mod_base, &header.lumps[Q2LUMP_MODELS]);\n\t\t\tnoerrors = noerrors && CModQ2_LoadAreas\t\t\t(mod, mod_base, &header.lumps[Q2LUMP_AREAS]);\n\t\t\tnoerrors = noerrors && CModQ2_LoadAreaPortals\t(mod, mod_base, &header.lumps[Q2LUMP_AREAPORTALS]);\n\n\t\t\tif (!noerrors)\n\t\t\t{\n\t\t\t\treturn NULL;\n\t\t\t}\n\t\t\tmod->funcs.LightPointValues\t\t= GLQ2BSP_LightPointValues;\n\t\t\tmod->funcs.StainNode\t\t\t= GLR_Q2BSP_StainNode;\n\t\t\tmod->funcs.MarkLights\t\t\t= Q2BSP_MarkLights;\n\t\t\tmod->funcs.PrepareFrame\t\t\t= CM_PrepareFrame;\n#ifdef RTLIGHTS\n\t\t\tmod->funcs.GenerateShadowMesh\t= Q2BSP_GenerateShadowMesh;\n#endif\n\t\t\tbreak;\n#endif\n\t\t}\n#endif\n\t}\n\n\tBSPX_LoadEnvmaps(mod, bspx, mod_base);\n\n#ifdef Q3BSPS\n\t{\n\t\tint x, y;\n\t\tfor (x = 0; x < prv->numareas; x++)\n\t\t\tfor (y = 0; y < prv->numareas; y++)\n\t\t\t\tprv->q3areas[x].numareaportals[y] = map_autoopenportals.ival;\n\t}\n#endif\n#ifdef Q2BSPS\n\tif (map_autoopenportals.value)\n\t\tmemset (prv->q2portalopen, 1, sizeof(prv->q2portalopen));\t//open them all. Used for progs that havn't got a clue.\n\telse\n\t\tmemset (prv->q2portalopen, 0, sizeof(prv->q2portalopen));\t//make them start closed.\n#endif\n\tFloodAreaConnections (prv);\n\n\tmod->checksum = checksum1;\n\tmod->checksum2 = checksum2;\n\n\tmod->nummodelsurfaces = mod->numsurfaces;\n\tmemset(&mod->batches, 0, sizeof(mod->batches));\n\tmod->vbos = NULL;\n\n\tmod->numsubmodels = CM_NumInlineModels(mod);\n\n\tmod->hulls[0].firstclipnode = prv->cmodels[0].headnode-mod->nodes;\n\tmod->rootnode = prv->cmodels[0].headnode;\n\tmod->nummodelsurfaces = prv->cmodels[0].numsurfaces;\n\n#ifdef HAVE_CLIENT\n\tprv->oldclusters[0] = prv->oldclusters[1] = -1;\n\tif (qrenderer != QR_NONE)\n\t{\n\t\tbuilddata_t *bd = NULL;\n\t\tif (buildmeshes)\n\t\t{\n\t\t\tbd = Z_Malloc(sizeof(*bd) + facesize*mod->nummodelsurfaces);\n\t\t\tbd->buildfunc = buildmeshes;\n\t\t\tmemcpy(bd+1, facedata + mod->firstmodelsurface*facesize, facesize*mod->nummodelsurfaces);\n\t\t}\n\t\tCOM_AddWork(WG_MAIN, ModBrush_LoadGLStuff, mod, bd, 0, 0);\n\t}\n#endif\n\n\t//FIXME: q2bsp apparently doesn't report which brushes are part of which submodels.\n\t//FIXME: ALL patches? not just worldmodel?\n\tCM_BuildBIH(mod, 0);\n\n\tfor (i=1 ; i< mod->numsubmodels ; i++)\n\t{\n\t\tcmodel_t\t*bm;\n\n\t\tchar\tname[MAX_QPATH];\n\n\t\tQ_snprintfz (name, sizeof(name), \"*%i:%s\", i, wmod->publicname);\n\t\tmod = Mod_FindName (name);\n\t\t*mod = *wmod;\n\t\tmod->archive = NULL;\n\t\tmod->entities_raw = NULL;\n\t\tmod->submodelof = wmod;\n\t\tQ_strncpyz(mod->publicname, name, sizeof(mod->publicname));\n\t\tQ_snprintfz (mod->name, sizeof(mod->name), \"*%i:%s\", i, wmod->name);\n\t\tmemset(&mod->memgroup, 0, sizeof(mod->memgroup));\n\n\t\tbm = CM_InlineModel (wmod, name);\n\t\t\n\t\tmod->hulls[0].firstclipnode = -1;\t//no nodes, \n\t\tif (bm->headleaf)\n\t\t{\n\t\t\tmod->leafs = bm->headleaf;\n\t\t\tmod->nodes = NULL;\n\t\t\tmod->hulls[0].firstclipnode = -1;\t//make it refer directly to the first leaf, for things that still use numbers. \n\t\t\tmod->rootnode = (mnode_t*)bm->headleaf;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tmod->leafs = wmod->leafs;\n\t\t\tmod->nodes = wmod->nodes;\n\t\t\tmod->hulls[0].firstclipnode = bm->headnode - mod->nodes;\t//determine the correct node index\n\t\t\tmod->rootnode = bm->headnode;\n\t\t}\n\t\tmod->nummodelsurfaces = bm->numsurfaces;\n\t\tmod->firstmodelsurface = bm->firstsurface;\n\n\t\tCM_BuildBIH(mod, i);\n\n\t\tmemset(&mod->batches, 0, sizeof(mod->batches));\n\t\tmod->vbos = NULL;\n\n\t\tVectorCopy (bm->maxs, mod->maxs);\n\t\tVectorCopy (bm->mins, mod->mins);\n#ifndef SERVERONLY\n\t\tmod->radius = RadiusFromBounds (mod->mins, mod->maxs);\n\n\t\tif (qrenderer != QR_NONE)\n\t\t{\n\t\t\tbuilddata_t *bd = NULL;\n\t\t\tif (buildmeshes)\n\t\t\t{\n\t\t\t\tbd = Z_Malloc(sizeof(*bd) + facesize*mod->nummodelsurfaces);\n\t\t\t\tbd->buildfunc = buildmeshes;\n\t\t\t\tmemcpy(bd+1, facedata + mod->firstmodelsurface*facesize, facesize*mod->nummodelsurfaces);\n\t\t\t}\n\t\t\tCOM_AddWork(WG_MAIN, ModBrush_LoadGLStuff, mod, bd, i, 0);\n\t\t}\n#endif\n\t\tCOM_AddWork(WG_MAIN, Mod_ModelLoaded, mod, NULL, MLS_LOADED, 0);\n\t}\n\n#ifdef TERRAIN\n\twmod->terrain = Mod_LoadTerrainInfo(wmod, loadname, false);\n#endif\n\n\treturn &prv->cmodels[0];\n}\n\n/*\n==================\nCM_InlineModel\n==================\n*/\nstatic cmodel_t\t*CM_InlineModel (model_t *model, char *name)\n{\n\tcminfo_t\t*prv = (cminfo_t*)model->meshinfo;\n\tint\t\tnum;\n\n\tif (!name)\n\t\tHost_Error(\"Bad model\\n\");\n\telse if (name[0] != '*')\n\t\tHost_Error(\"Bad model\\n\");\n\n\tnum = atoi (name+1);\n\n\tif (num < 1 || num >= prv->numcmodels)\n\t\tHost_Error (\"CM_InlineModel: bad number\");\n\n\treturn &prv->cmodels[num];\n}\n\nstatic int\t\tCM_NumInlineModels (model_t *model)\n{\n\tcminfo_t\t*prv = (cminfo_t*)model->meshinfo;\n\treturn prv->numcmodels;\n}\n\nstatic int\t\tCM_LeafContents (model_t *model, int leafnum)\n{\n\tif (leafnum < 0 || leafnum >= model->numleafs)\n\t\tHost_Error (\"CM_LeafContents: bad number\");\n\treturn model->leafs[leafnum].contents;\n}\n\nstatic int\t\tCM_LeafCluster (model_t *model, int leafnum)\n{\n\tif (leafnum < 0 || leafnum >= model->numleafs)\n\t\tHost_Error (\"CM_LeafCluster: bad number\");\n\treturn model->leafs[leafnum].cluster;\n}\n\nstatic int\t\tCM_LeafArea (model_t *model, int leafnum)\n{\n\tif (leafnum < 0 || leafnum >= model->numleafs)\n\t\tHost_Error (\"CM_LeafArea: bad number\");\n\treturn model->leafs[leafnum].area;\n}\n\n//=======================================================================\n\n#define PlaneDiff(point,plane) (((plane)->type < 3 ? (point)[(plane)->type] : DotProduct((point), (plane)->normal)) - (plane)->dist)\n#define PlaneDiffPush(point,plane,pushdist) (((plane)->type < 3 ? (point)[(plane)->type] : DotProduct((point), (plane)->normal)) - (plane)->dist - pushdist)\n\nstatic mplane_t\t\t\tbox_planes[6];\nstatic model_t\t\t\tbox_model;\nstatic q2cbrush_t\t\tbox_brush;\nstatic q2cbrushside_t\tbox_sides[6];\nstatic qboolean BM_NativeTrace(model_t *model, int forcehullnum, const framestate_t *framestate, const vec3_t axis[3], const vec3_t start, const vec3_t end, const vec3_t mins, const vec3_t maxs, qboolean capsule, unsigned int contents, trace_t *trace);\nstatic unsigned int BM_NativeContents(struct model_s *model, int hulloverride, const framestate_t *framestate, const vec3_t axis[3], const vec3_t p, const vec3_t mins, const vec3_t maxs)\n{\n\tunsigned int j;\n\tq2cbrushside_t *brushside = box_sides;\n\tfor ( j = 0; j < 6; j++, brushside++ )\n\t{\n\t\tif ( PlaneDiff (p, brushside->plane) > 0 )\n\t\t\treturn 0;\n\t}\n\treturn FTECONTENTS_BODY;\n}\n\n/*\n===================\nCM_InitBoxHull\n\nSet up the planes and nodes so that the six floats of a bounding box\ncan just be stored out and get a proper clipping hull structure.\n===================\n*/\nstatic void CM_InitBoxHull (void)\n{\n\tint\t\t\ti;\n\tmplane_t\t*p;\n\tq2cbrushside_t\t*s;\n\n\tbox_model.funcs.NativeContents\t\t= BM_NativeContents;\n\tbox_model.funcs.NativeTrace\t\t\t= BM_NativeTrace;\n\n\n\tbox_model.loadstate = MLS_LOADED;\n\n\tbox_brush.contents = FTECONTENTS_BODY;\n\tbox_brush.numsides = 6;\n\tbox_brush.brushside = box_sides;\n\n\tfor (i=0 ; i<6 ; i++)\n\t{\n\t\t//the pointers\n\t\ts = &box_sides[i];\n\t\tp = &box_planes[i];\n\n\t\t// brush sides\n\t\ts->plane = \tp;\n\t\ts->surface = &nullsurface;\n\n\t\t// planes\n\t\tp->type = ((i>=3)?i-3:i);\n\t\tp->signbits = 0;\n\t\tVectorClear (p->normal);\n\t\tp->normal[p->type] = ((i>=3)?-1:1);\n\t}\n}\n\n\n/*\n===================\nCM_HeadnodeForBox\n\nTo keep everything totally uniform, bounding boxes are turned into small\nBSP trees instead of being compared directly.\n===================\n*/\nstatic void CM_SetTempboxSize (const vec3_t mins, const vec3_t maxs)\n{\n\tbox_planes[0].dist = maxs[0];\n\tbox_planes[1].dist = maxs[1];\n\tbox_planes[2].dist = maxs[2];\n\tbox_planes[3].dist = -mins[0];\n\tbox_planes[4].dist = -mins[1];\n\tbox_planes[5].dist = -mins[2];\n}\n\nmodel_t *CM_TempBoxModel(const vec3_t mins, const vec3_t maxs)\n{\n\tCM_SetTempboxSize(mins, maxs);\n\treturn &box_model;\n}\n\n/*\n==================\nCM_PointLeafnum_r\n\n==================\n*/\nstatic int CM_PointLeafnum_r (model_t *mod, const vec3_t p, int num)\n{\n\tfloat\t\td;\n\tmnode_t\t\t*node;\n\tmplane_t\t*plane;\n\n\twhile (num >= 0)\n\t{\n\t\tnode = mod->nodes + num;\n\t\tplane = node->plane;\n\n\t\tif (plane->type < 3)\n\t\t\td = p[plane->type] - plane->dist;\n\t\telse\n\t\t\td = DotProduct (plane->normal, p) - plane->dist;\n\t\tif (d < 0)\n\t\t\tnum = node->childnum[1];\n\t\telse\n\t\t\tnum = node->childnum[0];\n\t}\n\n\treturn -1 - num;\n}\n\n#ifdef HAVE_SERVER\nstatic int CM_PointLeafnum (model_t *mod, const vec3_t p)\n{\n\tif (!mod || mod->loadstate != MLS_LOADED)\n\t\treturn 0;\t\t// sound may call this without map loaded\n\treturn CM_PointLeafnum_r (mod, p, 0);\n}\n#endif\n\nstatic int CM_PointCluster (model_t *mod, const vec3_t p, int *area)\n{\n\tint leaf;\n\tif (!mod || mod->loadstate != MLS_LOADED)\n\t\treturn 0;\t\t// sound may call this without map loaded\n\n\tleaf = CM_PointLeafnum_r (mod, p, 0);\n\tif (area)\n\t\t*area = CM_LeafArea(mod, leaf);\n\treturn CM_LeafCluster(mod, leaf);\n}\n\nstatic int CM_PointContents (model_t *mod, const vec3_t p);\nstatic void CM_InfoForPoint (struct model_s *mod, vec3_t pos, int *area, int *cluster, unsigned int *contentbits)\n{\n\tint leaf = CM_PointLeafnum_r (mod, pos, 0);\n\n\t*area = CM_LeafArea(mod, leaf);\n\t*cluster = CM_LeafCluster(mod, leaf);\n\t*contentbits = CM_LeafContents(mod, leaf);\n\n\t//q3 needs to use brush contents (its leafs no longer need to strictly follow brushes)\n\tif (mod->fromgame != fg_quake2)\n\t\t*contentbits = CM_PointContents (mod, pos);\n}\n\n/*\n=============\nCM_BoxLeafnums\n\nFills in a list of all the leafs touched\n=============\n*/\nstatic int\t\tleaf_count, leaf_maxcount;\nstatic int\t\t*leaf_list;\nstatic const float\t*leaf_mins, *leaf_maxs;\nstatic int\t\tleaf_topnode;\n\nstatic void CM_BoxLeafnums_r (model_t *mod, int nodenum)\n{\n\tmplane_t\t*plane;\n\tmnode_t\t\t*node;\n\tint\t\ts;\n\n\twhile (1)\n\t{\n\t\tif (nodenum < 0)\n\t\t{\n\t\t\tif (leaf_count >= leaf_maxcount)\n\t\t\t{\n//\t\t\t\tCom_Printf (\"CM_BoxLeafnums_r: overflow\\n\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tleaf_list[leaf_count++] = -1 - nodenum;\n\t\t\treturn;\n\t\t}\n\n\t\tnode = &mod->nodes[nodenum];\n\t\tplane = node->plane;\n//\t\ts = BoxOnPlaneSide (leaf_mins, leaf_maxs, plane);\n\t\ts = BOX_ON_PLANE_SIDE(leaf_mins, leaf_maxs, plane);\n\t\tif (s == 1)\n\t\t\tnodenum = node->childnum[0];\n\t\telse if (s == 2)\n\t\t\tnodenum = node->childnum[1];\n\t\telse\n\t\t{\t// go down both\n\t\t\tif (leaf_topnode == -1)\n\t\t\t\tleaf_topnode = nodenum;\n\t\t\tCM_BoxLeafnums_r (mod, node->childnum[0]);\n\t\t\tnodenum = node->childnum[1];\n\t\t}\n\n\t}\n}\n\nstatic int\tCM_BoxLeafnums_headnode (model_t *mod, const vec3_t mins, const vec3_t maxs, int *list, int listsize, int headnode, int *topnode)\n{\n\tleaf_list = list;\n\tleaf_count = 0;\n\tleaf_maxcount = listsize;\n\tleaf_mins = mins;\n\tleaf_maxs = maxs;\n\n\tleaf_topnode = -1;\n\n\tCM_BoxLeafnums_r (mod, headnode);\n\n\tif (topnode)\n\t\t*topnode = leaf_topnode;\n\n\treturn leaf_count;\n}\n\nstatic int\tCM_BoxLeafnums (model_t *mod, const vec3_t mins, const vec3_t maxs, int *list, int listsize, int *topnode)\n{\n\treturn CM_BoxLeafnums_headnode (mod, mins, maxs, list,\n\t\tlistsize, mod->hulls[0].firstclipnode, topnode);\n}\n\n\n\n/*\n==================\nCM_PointContents\n\n==================\n*/\nstatic int CM_PointContents (model_t *mod, const vec3_t p)\n{\n\tcminfo_t\t\t*prv = (cminfo_t*)mod->meshinfo;\n\tint\t\t\t\ti, j, contents;\n\tmleaf_t\t\t\t*leaf;\n\tq2cbrush_t\t\t*brush;\n\tq2cbrushside_t\t*brushside;\n\n\tif (!mod)\t// map not loaded\n\t\treturn 0;\n\n\ti = CM_PointLeafnum_r (mod, p, mod->hulls[0].firstclipnode);\n\n\tif (mod->fromgame == fg_quake2)\n\t\tcontents = mod->leafs[i].contents;\t//q2 is simple.\n\telse\n\t{\n\t\tleaf = &mod->leafs[i];\n\n\t//\tif ( leaf->contents & CONTENTS_NODROP ) {\n\t//\t\tcontents = CONTENTS_NODROP;\n\t//\t} else {\n\t\t\tcontents = 0;\n\t//\t}\n\n\t\tfor (i = 0; i < leaf->numleafbrushes; i++)\n\t\t{\n\t\t\tbrush = prv->leafbrushes[leaf->firstleafbrush + i];\n\n\t\t\t// check if brush actually adds something to contents\n\t\t\tif ( (contents & brush->contents) == brush->contents ) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tbrushside = brush->brushside;\n\t\t\tfor ( j = 0; j < brush->numsides; j++, brushside++ )\n\t\t\t{\n\t\t\t\tif ( PlaneDiff (p, brushside->plane) > 0 )\n\t\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tif (j == brush->numsides)\n\t\t\t\tcontents |= brush->contents;\n\t\t}\n\t}\n\n#ifdef TERRAIN\n\tif (mod->terrain)\n\t\tcontents |= Heightmap_PointContents(mod, NULL, p);\n#endif\n\treturn contents;\n}\n\nstatic unsigned int CM_NativeContents(struct model_s *model, int hulloverride, const framestate_t *framestate, const vec3_t axis[3], const vec3_t point, const vec3_t mins, const vec3_t maxs)\n{\n\tcminfo_t\t*prv = (cminfo_t*)model->meshinfo;\n\tint\tcontents;\n\tif (!DotProduct(mins, mins) && !DotProduct(maxs, maxs))\n\t\treturn CM_PointContents(model, point);\n\n\tif (!model)\t// map not loaded\n\t\treturn 0;\n\n\n\t{\n\t\tint i, j, k;\n\t\tmleaf_t\t\t\t*leaf;\n\t\tq2cbrush_t\t\t*brush;\n\t\tq2cbrushside_t\t*brushside;\n\t\tvec3_t absmin, absmax, ofs;\n\n\t\tint leaflist[64];\n\n\t\tVectorAdd(point, mins, absmin);\n\t\tVectorAdd(point, maxs, absmax);\n\n\t\tk = CM_BoxLeafnums (model, absmin, absmax, leaflist, 64, NULL);\n\n\t\tcontents = 0;\n\t\tfor (k--; k >= 0; k--)\n\t\t{\n\t\t\tleaf = &model->leafs[leaflist[k]];\n\t\t\tif (model->fromgame != fg_quake2)\n\t\t\t{\t//q3 is more complex\n\t\t\t\tfor (i = 0; i < leaf->numleafbrushes; i++)\n\t\t\t\t{\n\t\t\t\t\tbrush = prv->leafbrushes[leaf->firstleafbrush + i];\n\n\t\t\t\t\t// check if brush actually adds something to contents\n\t\t\t\t\tif ( (contents & brush->contents) == brush->contents ) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\n\t\t\t\t\tbrushside = brush->brushside;\n\t\t\t\t\tfor ( j = 0; j < brush->numsides; j++, brushside++ )\n\t\t\t\t\t{\n\t\t\t\t\t\tfor (j=0 ; j<3 ; j++)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (brushside->plane->normal[j] < 0)\n\t\t\t\t\t\t\t\tofs[j] = maxs[j];\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\tofs[j] = mins[j];\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (PlaneDiffPush (point, brushside->plane, DotProduct (ofs, brushside->plane->normal)) > 0)\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (j == brush->numsides)\n\t\t\t\t\t\tcontents |= brush->contents;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\t//q2 is simple\n\t\t\t\tcontents |= leaf->contents;\n\t\t}\n\t}\n\n\treturn contents;\n}\n\n/*\n===============================================================================\n\nBOX TRACING\n\n===============================================================================\n*/\n\n// 1/32 epsilon to keep floating point happy\n#define\tDIST_EPSILON\t(0.03125)\n\nstatic vec3_t\ttrace_start, trace_end;\nstatic vec3_t\ttrace_mins, trace_maxs;\nstatic vec3_t\ttrace_extents;\nstatic vec3_t\ttrace_absmins, trace_absmaxs;\nstatic vec3_t\ttrace_up;\t//capsule points upwards in this direction\nstatic vec3_t\ttrace_capsulesize;\t//radius, up, down\nstatic float\ttrace_truefraction;\nstatic float\ttrace_nearfraction;\n\nstatic trace_t\ttrace_trace;\nstatic int\t\ttrace_contents;\nstatic enum\n{\n\tshape_isbox,\n\tshape_iscapsule,\n\tshape_ispoint\n} trace_shape;\t\t// optimized case\n\n\nstatic void CM_FinalizeBrush(q2cbrush_t *brush)\n{\n\tvecV_t verts[256];\n\tvec4_t planes[256];\n\tint i, j;\n\tClearBounds(brush->absmins, brush->absmaxs);\n\tfor (i = 0; i < brush->numsides; i++)\n\t{\n\t\tVectorCopy(brush->brushside[i].plane->normal, planes[i]);\n\t\tplanes[i][3] = brush->brushside[i].plane->dist;\n\t}\n\tfor (i = 0; i < brush->numsides; i++)\n\t{\n\t\t//most brushes are axial, which can save some a little loadtime\n\t\tif (planes[i][0] == 1)\n\t\t\tbrush->absmaxs[0] = planes[i][3];\n\t\telse if (planes[i][1] == 1)\n\t\t\tbrush->absmaxs[1] = planes[i][3];\n\t\telse if (planes[i][2] == 1)\n\t\t\tbrush->absmaxs[2] = planes[i][3];\n\t\telse if (planes[i][0] == -1)\n\t\t\tbrush->absmins[0] = -planes[i][3];\n\t\telse if (planes[i][1] == -1)\n\t\t\tbrush->absmins[1] = -planes[i][3];\n\t\telse if (planes[i][2] == -1)\n\t\t\tbrush->absmins[2] = -planes[i][3];\n\t\telse\n\t\t{\n\t\t\tj = Fragment_ClipPlaneToBrush(verts, countof(verts), planes, sizeof(planes[0]), brush->numsides, planes[i]);\n\t\t\twhile (j-- > 0)\n\t\t\t\tAddPointToBounds(verts[j], brush->absmins, brush->absmaxs);\n\t\t}\n\t}\n}\n\n/*\n================\nCM_ClipBoxToBrush\n================\n*/\nstatic void CM_ClipBoxToBrush (vec3_t mins, vec3_t maxs, vec3_t p1, vec3_t p2,\n\t\t\t\t\t  trace_t *trace, q2cbrush_t *brush)\n{\n\tint\t\t\ti, j;\n\tmplane_t\t*plane, *clipplane;\n\tfloat\t\tdist;\n\tfloat\t\tenterfrac, leavefrac;\n\tvec3_t\t\tofs;\n\tfloat\t\td1, d2;\n\tqboolean\tgetout, startout;\n\tfloat\t\tf;\n\tq2cbrushside_t\t*side, *leadside;\n\n\tfloat nearfrac=0;\n\tenterfrac = -1;\n\tleavefrac = 2;\n\tclipplane = NULL;\n\n\tif (!brush->numsides)\n\t\treturn;\n\n\tgetout = false;\n\tstartout = false;\n\tleadside = NULL;\n\n\tfor (i=0 ; i<brush->numsides ; i++)\n\t{\n\t\tside = brush->brushside+i;\n\t\tplane = side->plane;\n\n\t\tswitch(trace_shape)\n\t\t{\n\t\tdefault:\n\t\tcase shape_isbox: // general box case\n\t\t\t// push the plane out apropriately for mins/maxs\n\n\t\t\t// FIXME: use signbits into 8 way lookup for each mins/maxs\n\t\t\tfor (j=0 ; j<3 ; j++)\n\t\t\t{\n\t\t\t\tif (plane->normal[j] < 0)\n\t\t\t\t\tofs[j] = maxs[j];\n\t\t\t\telse\n\t\t\t\t\tofs[j] = mins[j];\n\t\t\t}\n\t\t\tdist = DotProduct (ofs, plane->normal);\n\t\t\tdist = plane->dist - dist;\n\t\t\tbreak;\n\t\tcapsuledist(dist,plane,mins,maxs)\n\t\tcase shape_ispoint: // special point case\n\t\t\tdist = plane->dist;\n\t\t\tbreak;\n\t\t}\n\n\t\td1 = DotProduct (p1, plane->normal) - dist;\n\t\td2 = DotProduct (p2, plane->normal) - dist;\n\n\t\tif (d2 > 0)\n\t\t\tgetout = true;\t// endpoint is not in solid\n\t\tif (d1 > 0)\n\t\t\tstartout = true;\n\n\t\t// if completely in front of face, no intersection\n\t\tif (d1 > 0 && d2 >= d1)\n\t\t\treturn;\n\n\t\tif (d1 <= 0 && d2 <= 0)\n\t\t\tcontinue;\n\n\t\t// crosses face\n\t\tif (d1 > d2)\n\t\t{\t// enter\n\t\t\tf = (d1) / (d1-d2);\n\t\t\tif (f > enterfrac)\n\t\t\t{\n\t\t\t\tenterfrac = f;\n\t\t\t\tnearfrac = (d1-DIST_EPSILON) / (d1-d2);\n\t\t\t\tclipplane = plane;\n\t\t\t\tleadside = side;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\t// leave\n\t\t\tf = (d1) / (d1-d2);\n\t\t\tif (f < leavefrac)\n\t\t\t\tleavefrac = f;\n\t\t}\n\t}\n\n\tif (!startout)\n\t{\t// original point was inside brush\n\t\ttrace->startsolid = true;\n\t\tif (!getout)\n\t\t\ttrace->allsolid = true;\n\t\treturn;\n\t}\n\tif (enterfrac <= leavefrac)\n\t{\n\t\tif (enterfrac > -1 && enterfrac <= trace_truefraction)\n\t\t{\n\t\t\tif (enterfrac < 0)\n\t\t\t\tenterfrac = 0;\n\n\t\t\ttrace_nearfraction = nearfrac;\n\t\t\ttrace_truefraction = enterfrac;\n\n\t\t\ttrace->plane.dist = clipplane->dist;\n\t\t\tVectorCopy(clipplane->normal, trace->plane.normal);\n\t\t\ttrace->surface = &(leadside->surface->c);\n\t\t\ttrace->contents = brush->contents;\n\t\t}\n\t}\n}\n\n#ifdef Q3BSPS\nstatic void CM_ClipBoxToPlanes (vec3_t trmins, vec3_t trmaxs, vec3_t p1, vec3_t p2, trace_t *trace, vec3_t plmins, vec3_t plmaxs, mplane_t *plane, int numplanes, q2csurface_t *surf)\n{\n\tint\t\t\ti, j;\n\tmplane_t\t*clipplane;\n\tfloat\t\tdist;\n\tfloat\t\tenterfrac, leavefrac;\n\tvec3_t\t\tofs;\n\tfloat\t\td1, d2;\n\tqboolean\tgetout, startout;\n\tfloat\t\tf;\n//\tq2cbrushside_t\t*side, *leadside;\n\tstatic mplane_t\tbboxplanes[6] = //we change the dist, but nothing else\n\t{\n\t\t{{1, 0, 0}},\n\t\t{{0, 1, 0}},\n\t\t{{0, 0, 1}},\n\t\t{{-1, 0, 0}},\n\t\t{{0, -1, 0}},\n\t\t{{0, 0, -1}},\n\t};\n\n\tfloat nearfrac=0;\n\tenterfrac = -1;\n\tleavefrac = 2;\n\tclipplane = NULL;\n\n\tgetout = false;\n\tstartout = false;\n//\tleadside = NULL;\n\n\tfor (i=0 ; i<numplanes ; i++, plane++)\n\t{\n\t\tswitch(trace_shape)\n\t\t{\n\t\tdefault:\n\t\tcase shape_isbox:\t// general box case\n\t\t\t// push the plane out apropriately for mins/maxs\n\n\t\t\t// FIXME: special case for axial\n\t\t\t// FIXME: use signbits into 8 way lookup for each mins/maxs\n\t\t\tfor (j=0 ; j<3 ; j++)\n\t\t\t{\n\t\t\t\tif (plane->normal[j] < 0)\n\t\t\t\t\tofs[j] = trmaxs[j];\n\t\t\t\telse\n\t\t\t\t\tofs[j] = trmins[j];\n\t\t\t}\n\t\t\tdist = DotProduct (ofs, plane->normal);\n\t\t\tdist = plane->dist - dist;\n\t\t\tbreak;\n\t\tcapsuledist(dist,plane,trmins,trmaxs)\n\t\tcase shape_ispoint:\t// special point case\n\t\t\tdist = plane->dist;\n\t\t\tbreak;\n\t\t}\n\n\t\td1 = DotProduct (p1, plane->normal) - dist;\n\t\td2 = DotProduct (p2, plane->normal) - dist;\n\n\t\tif (d2 > 0)\n\t\t\tgetout = true;\t// endpoint is not in solid\n\t\tif (d1 > 0)\n\t\t\tstartout = true;\n\n\t\t// if completely in front of face, no intersection\n\t\tif (d1 > 0 && d2 >= d1)\n\t\t\treturn;\n\n\t\tif (d1 <= 0 && d2 <= 0)\n\t\t\tcontinue;\n\n\t\t// crosses face\n\t\tif (d1 > d2)\n\t\t{\t// enter\n\t\t\tf = (d1) / (d1-d2);\n\t\t\tif (f > enterfrac)\n\t\t\t{\n\t\t\t\tenterfrac = f;\n\t\t\t\tnearfrac = (d1-DIST_EPSILON) / (d1-d2);\n\t\t\t\tclipplane = plane;\n//\t\t\t\tleadside = side;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\t// leave\n\t\t\tf = (d1) / (d1-d2);\n\t\t\tif (f < leavefrac)\n\t\t\t\tleavefrac = f;\n\t\t}\n\t}\n\n\t//bevel the brush axially (to match the player's bbox), in case that wasn't already done\n\tfor (i=0, plane = bboxplanes; i<6 ; i++, plane++)\n\t{\n\t\tif (i < 3)\n\t\t{\t//positive normal\n\t\t\tdist = trmins[i];\n\t\t\tplane->dist = plmaxs[i];\n\t\t\tdist = plane->dist - dist;\n\t\t\td1 = p1[i] - dist;\n\t\t\td2 = p2[i] - dist;\n\t\t}\n\t\telse\n\t\t{\t//negative normal\n\t\t\tj = i-3;\n\t\t\tdist = -trmaxs[j];\n\t\t\tplane->dist = -plmins[j];\n\t\t\tdist = plane->dist - dist;\n\t\t\td1 = -p1[j] - dist;\n\t\t\td2 = -p2[j] - dist;\n\t\t}\n\n\t\tif (d2 > 0)\n\t\t\tgetout = true;\t// endpoint is not in solid\n\t\tif (d1 > 0)\n\t\t\tstartout = true;\n\n\t\t// if completely in front of face, no intersection\n\t\tif (d1 > 0 && d2 >= d1)\n\t\t\treturn;\n\n\t\tif (d1 <= 0 && d2 <= 0)\n\t\t\tcontinue;\n\n\t\t// crosses face\n\t\tif (d1 > d2)\n\t\t{\t// enter\n\t\t\tf = (d1) / (d1-d2);\n\t\t\tif (f > enterfrac)\n\t\t\t{\n\t\t\t\tenterfrac = f;\n\t\t\t\tnearfrac = (d1-DIST_EPSILON) / (d1-d2);\n\t\t\t\tclipplane = plane;\n//\t\t\t\tleadside = side;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\t// leave\n\t\t\tf = (d1) / (d1-d2);\n\t\t\tif (f < leavefrac)\n\t\t\t\tleavefrac = f;\n\t\t}\n\t}\n\n\tif (!startout)\n\t{\t// original point was inside brush\n\t\ttrace->startsolid = true;\n\t\tif (!getout)\n\t\t\ttrace->allsolid = true;\n\t\treturn;\n\t}\n\tif (enterfrac <= leavefrac)\n\t{\n\t\tif (enterfrac > -1 && enterfrac <= trace_truefraction)\n\t\t{\n\t\t\tif (enterfrac < 0)\n\t\t\t\tenterfrac = 0;\n\n\t\t\ttrace_nearfraction = nearfrac;\n\t\t\ttrace_truefraction = enterfrac;\n\n\t\t\ttrace->plane.dist = clipplane->dist;\n\t\t\tVectorCopy(clipplane->normal, trace->plane.normal);\n\t\t\ttrace->surface = surf;\n\t\t\ttrace->contents = surf->value;\n\t\t}\n\t}\n}\n\nstatic void Mod_Trace_Trisoup_(vecV_t *posedata, index_t *indexes, size_t numindexes, vec3_t start, vec3_t end, vec3_t mins, vec3_t maxs, trace_t *trace, q2csurface_t *surf)\n{\n\tsize_t i;\n\tint j;\n\tfloat *p1, *p2, *p3;\n\tvec3_t edge1, edge2, edge3;\n\tmplane_t planes[5];\n\tvec3_t tmins, tmaxs;\n\n\tfor (i = 0; i < numindexes; i+=3)\n\t{\n\t\tp1 = posedata[indexes[i+0]];\n\t\tp2 = posedata[indexes[i+1]];\n\t\tp3 = posedata[indexes[i+2]];\n\n\t\t//determine the triangle extents, and skip the triangle if we're completely out of bounds\n\t\tfor (j = 0; j < 3; j++)\n\t\t{\n\t\t\ttmins[j] = p1[j];\n\t\t\tif (tmins[j] > p2[j])\n\t\t\t\ttmins[j] = p2[j];\n\t\t\tif (tmins[j] > p3[j])\n\t\t\t\ttmins[j] = p3[j];\n\t\t\tif (trace_absmaxs[j]+(1/8.f) < tmins[j])\n\t\t\t\tbreak;\n\t\t\ttmaxs[j] = p1[j];\n\t\t\tif (tmaxs[j] < p2[j])\n\t\t\t\ttmaxs[j] = p2[j];\n\t\t\tif (tmaxs[j] < p3[j])\n\t\t\t\ttmaxs[j] = p3[j];\n\t\t\tif (trace_absmins[j]-(1/8.f) > tmaxs[j])\n\t\t\t\tbreak;\n\t\t}\n\t\t//skip any triangles which are completely outside the trace bounds\n\t\tif (j < 3)\n\t\t\tcontinue;\n\n\t\tVectorSubtract(p1, p2, edge1);\n\t\tVectorSubtract(p3, p2, edge2);\n\t\tVectorSubtract(p1, p3, edge3);\n\t\tCrossProduct(edge1, edge2, planes[0].normal);\n\t\tVectorNormalize(planes[0].normal);\n\t\tplanes[0].dist = DotProduct(p1, planes[0].normal);\n\t\tVectorNegate(planes[0].normal, planes[1].normal);\n\t\tplanes[1].dist = -planes[0].dist + 4;\n\n\t\t//determine edges\n\t\t//FIXME: use adjacency info\n\t\tCrossProduct(edge1, planes[0].normal, planes[2].normal);\n\t\tVectorNormalize(planes[2].normal);\n\t\tplanes[2].dist = DotProduct(p2, planes[2].normal);\n\n\t\tCrossProduct(planes[0].normal, edge2, planes[3].normal);\n\t\tVectorNormalize(planes[3].normal);\n\t\tplanes[3].dist = DotProduct(p3, planes[3].normal);\n\n\t\tCrossProduct(planes[0].normal, edge3, planes[4].normal);\n\t\tVectorNormalize(planes[4].normal);\n\t\tplanes[4].dist = DotProduct(p1, planes[4].normal);\n\n\t\tCM_ClipBoxToPlanes(mins, maxs, start, end, trace, tmins, tmaxs, planes, 5, surf); \n\t}\n}\n\n/*\nstatic void CM_ClipBoxToMesh (vec3_t mins, vec3_t maxs, vec3_t p1, vec3_t p2, trace_t *trace, mesh_t *mesh)\n{\n\ttrace_truefraction = trace->truefraction;\n\ttrace_nearfraction = trace->fraction;\n\tMod_Trace_Trisoup_(mesh->xyz_array, mesh->indexes, mesh->numindexes, p1, p2, mins, maxs, trace, &nullsurface.c);\n\ttrace->truefraction = trace_truefraction;\n\ttrace->fraction = trace_nearfraction;\n}\n*/\n\nstatic void CM_ClipBoxToPatch (vec3_t mins, vec3_t maxs, vec3_t p1, vec3_t p2,\n\t\t\t\t\t  trace_t *trace, q2cbrush_t *brush)\n{\n\tint\t\t\ti, j;\n\tmplane_t\t*plane, *clipplane;\n\tfloat\t\tenterfrac, leavefrac, nearfrac = 0;\n\tvec3_t\t\tofs;\n\tfloat\t\td1, d2;\n\tfloat dist;\n\tqboolean\tstartout;\n\tfloat\t\tf;\n\tq2cbrushside_t\t*side, *leadside;\n\n\tif (!brush->numsides)\n\t\treturn;\n\n\tenterfrac = -1;\n\tleavefrac = 2;\n\tclipplane = NULL;\n\tstartout = false;\n\tleadside = NULL;\n\n\tfor (i=0 ; i<brush->numsides ; i++)\n\t{\n\t\tside = brush->brushside+i;\n\t\tplane = side->plane;\n\n\t\t// push the plane out apropriately for mins/maxs\n\t\tswitch(trace_shape)\n\t\t{\n\t\tdefault:\n\t\tcase shape_isbox:\t// general box case\n\t\t\t// FIXME: use signbits into 8 way lookup for each mins/maxs\n\t\t\tfor (j=0 ; j<3 ; j++)\n\t\t\t{\n\t\t\t\tif (plane->normal[j] < 0)\n\t\t\t\t\tofs[j] = maxs[j];\n\t\t\t\telse\n\t\t\t\t\tofs[j] = mins[j];\n\t\t\t}\n\t\t\tdist = DotProduct (ofs, plane->normal);\n\t\t\tdist = plane->dist - dist;\n\t\t\tbreak;\n\t\tcapsuledist(dist,plane,mins,maxs)\n\t\tcase shape_ispoint:\t// special point case\n\t\t\tdist = plane->dist;\n\t\t\tbreak;\n\t\t}\n\n\t\td1 = DotProduct (p1, plane->normal) - dist;\n\t\td2 = DotProduct (p2, plane->normal) - dist;\n\n\t\t// if completely in front of face, no intersection\n\t\tif (d1 > 0 && d2 >= d1)\n\t\t\treturn;\n\n\t\tif (d1 > 0)\n\t\t\tstartout = true;\n\n\t\tif (d1 <= 0 && d2 <= 0)\n\t\t\tcontinue;\n\n\t\t// crosses face\n\t\tif (d1 > d2)\n\t\t{\t// enter\n\t\t\tf = (d1) / (d1-d2);\n\t\t\tif (f > enterfrac)\n\t\t\t{\n\t\t\t\tenterfrac = f;\n\t\t\t\tnearfrac = (d1-DIST_EPSILON) / (d1-d2);\n\t\t\t\tclipplane = plane;\n\t\t\t\tleadside = side;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\t// leave\n\t\t\tf = (d1) / (d1-d2);\n\t\t\tif (f < leavefrac)\n\t\t\t\tleavefrac = f;\n\t\t}\n\t}\n\n\tif (!startout)\n\t{\n\t\ttrace->startsolid = true;\n\t\treturn;\t\t// original point is inside the patch\n\t}\n\n\tif (nearfrac <= leavefrac)\n\t{\n\t\tif (leadside && leadside->surface\n\t\t\t&& enterfrac <= trace_truefraction)\n\t\t{\n\t\t\tif (enterfrac < 0)\n\t\t\t\tenterfrac = 0;\n\t\t\ttrace_truefraction = enterfrac;\n\t\t\ttrace_nearfraction = nearfrac;\n\t\t\ttrace->plane.dist = clipplane->dist;\n\t\t\tVectorCopy(clipplane->normal, trace->plane.normal);\n\t\t\ttrace->surface = &leadside->surface->c;\n\t\t\ttrace->contents = brush->contents;\n\t\t}\n\t\telse if (enterfrac < trace_truefraction)\n\t\t\tleavefrac=0;\n\t}\n}\n#endif\n\n/*\n================\nCM_TestBoxInBrush\n================\n*/\nstatic void CM_TestBoxInBrush (vec3_t mins, vec3_t maxs, vec3_t p1,\n\t\t\t\t\t  trace_t *trace, q2cbrush_t *brush)\n{\n\tint\t\t\ti, j;\n\tmplane_t\t*plane;\n\tfloat\t\tdist;\n\tvec3_t\t\tofs;\n\tfloat\t\td1;\n\tq2cbrushside_t\t*side;\n\n\tif (!brush->numsides)\n\t\treturn;\n\n\tfor (i=0 ; i<brush->numsides ; i++)\n\t{\n\t\tside = brush->brushside+i;\n\t\tplane = side->plane;\n\n\t\tswitch(trace_shape)\n\t\t{\n\t\tdefault:\n\t\tcase shape_isbox:\t// general box case\n\n\t\t\t// push the plane out apropriately for mins/maxs\n\n\t\t\t// FIXME: use signbits into 8 way lookup for each mins/maxs\n\t\t\tfor (j=0 ; j<3 ; j++)\n\t\t\t{\n\t\t\t\tif (plane->normal[j] < 0)\n\t\t\t\t\tofs[j] = maxs[j];\n\t\t\t\telse\n\t\t\t\t\tofs[j] = mins[j];\n\t\t\t}\n\t\t\tdist = DotProduct (ofs, plane->normal);\n\t\t\tdist = plane->dist - dist;\n\t\t\tbreak;\n\t\tcapsuledist(dist,plane,mins,maxs)\n\t\tcase shape_ispoint:\n\t\t\tdist = plane->dist;\n\t\t\tbreak;\n\t\t}\n\n\t\td1 = DotProduct (p1, plane->normal) - dist;\n\n\t\t// if completely in front of face, no intersection\n\t\tif (d1 > 0)\n\t\t\treturn;\n\t}\n\n\t// inside this brush\n\ttrace->startsolid = trace->allsolid = true;\n\ttrace->contents |= brush->contents;\n}\n\n#ifdef Q3BSPS\nstatic void CM_TestBoxInPatch (vec3_t mins, vec3_t maxs, vec3_t p1,\n\t\t\t\t\t  trace_t *trace, q2cbrush_t *brush)\n{\n\tint\t\t\ti, j;\n\tmplane_t\t*plane;\n\tvec3_t\t\tofs, ofs2;\n\tfloat dist, thickness;\n\tfloat\t\td1;\n\tq2cbrushside_t\t*side;\n\n\tif (!brush->numsides)\n\t\treturn;\n\n\ti = 0;\t//front plane\n\t{\n\t\tside = brush->brushside+i;\n\t\tplane = side->plane;\n\n\t\tswitch(trace_shape)\n\t\t{\n\t\tdefault:\n\t\tcase shape_isbox:\n\t\t\tfor (j=0 ; j<3 ; j++)\n\t\t\t{\n\t\t\t\tif (plane->normal[j] < 0)\n\t\t\t\t\tofs[j] = maxs[j], ofs2[j] = mins[j];\n\t\t\t\telse\n\t\t\t\t\tofs[j] = mins[j], ofs2[j] = maxs[j];\n\t\t\t}\n\n\t\t\tdist = DotProduct (ofs, plane->normal);\n\t\t\tthickness = DotProduct (ofs2, plane->normal)-dist;\n\t\t\tdist = plane->dist - dist;\n\t\t\tbreak;\n\t\tcase shape_iscapsule:\n\t\t\tdist = DotProduct(trace_up, plane->normal);\n\t\t\tthickness = dist*(trace_capsulesize[(dist<0)?2:1]) + trace_capsulesize[0]*2;\n\t\t\tdist = dist*(trace_capsulesize[(dist<0)?1:2]) - trace_capsulesize[0];\n\t\t\tdist = plane->dist - dist;\n\t\t\tbreak;\n\t\tcase shape_ispoint:\n\t\t\tdist = plane->dist;\n\t\t\tthickness = 0;\n\t\t\tbreak;\n\t\t}\n\n\t\td1 = DotProduct (p1, plane->normal) - dist;\n\n\t\t// if completely in front of face, no intersection\n\t\tif (d1 > 0)\n\t\t\treturn;\n\n\t\t//point is behind the front plane, so no real intersection.\n\t\tif (thickness < 0.25)\n\t\t\tthickness = 0.25; //FIXME: patches should probably be infinitely thin, but that makes stuff messy.\n\t\tif (d1 < -thickness)\n\t\t\treturn;\n\t}\n\n\tfor (i=1 ; i<brush->numsides ; i++)\n\t{\n\t\tside = brush->brushside+i;\n\t\tplane = side->plane;\n\n\t\tswitch(trace_shape)\n\t\t{\n\t\tdefault:\n\t\tcase shape_isbox:\n\t\t\t// general box case\n\n\t\t\t// push the plane out apropriately for mins/maxs\n\n\t\t\t// FIXME: use signbits into 8 way lookup for each mins/maxs\n\t\t\tfor (j=0 ; j<3 ; j++)\n\t\t\t{\n\t\t\t\tif (plane->normal[j] < 0)\n\t\t\t\t\tofs[j] = maxs[j];\n\t\t\t\telse\n\t\t\t\t\tofs[j] = mins[j];\n\t\t\t}\n\n\t\t\tdist = DotProduct (ofs, plane->normal);\n\t\t\tdist = plane->dist - dist;\n\t\t\tbreak;\n\t\tcapsuledist(dist,plane,mins,maxs)\n\t\tcase shape_ispoint:\n\t\t\tdist = plane->dist;\n\t\t\tbreak;\n\t\t}\n\n\t\td1 = DotProduct (p1, plane->normal) - dist;\n\n\t\t// if completely in front of face, no intersection\n\t\tif (d1 > 0)\n\t\t\treturn;\n\t}\n\n\t// inside this patch\n\ttrace->startsolid = trace->allsolid = true;\n\ttrace->contents = brush->contents;\n}\n#endif\n\n/*\n================\nCM_TraceToLeaf\n================\n*/\nstatic void CM_TraceToLeaf (cminfo_t\t*prv, mleaf_t\t\t*leaf)\n{\n\tint\t\t\tk;\n\tq2cbrush_t\t*b;\n\n#ifdef Q3BSPS\n\tint patchnum, j;\n\tq3cpatch_t *patch;\n\tq3cmesh_t *cmesh;\n#endif\n\n\tif ( !(leaf->contents & trace_contents))\n\t\treturn;\n\t// trace line against all brushes in the leaf\n\tfor (k=0 ; k<leaf->numleafbrushes ; k++)\n\t{\n\t\tb = prv->leafbrushes[leaf->firstleafbrush+k];\n\t\tif (b->checkcount == checkcount)\n\t\t\tcontinue;\t// already checked this brush in another leaf\n\t\tb->checkcount = checkcount;\n\n\t\tif ( !(b->contents & trace_contents))\n\t\t\tcontinue;\n\t\tif (!BoundsIntersect(b->absmins, b->absmaxs, trace_absmins, trace_absmaxs))\n\t\t\tcontinue;\n\t\tCM_ClipBoxToBrush (trace_mins, trace_maxs, trace_start, trace_end, &trace_trace, b);\n\t\tif (trace_nearfraction <= 0)\n\t\t\treturn;\n\t}\n\n#ifdef Q3BSPS\n\tif (!prv->mapisq3 || map_noCurves.value)\n\t\treturn;\n\n\t// trace line against all patches in the leaf\n\tfor (k = 0; k < leaf->numleafpatches; k++)\n\t{\n\t\tpatchnum = prv->leafpatches[leaf->firstleafpatch+k];\n\n\t\tpatch = &prv->patches[patchnum];\n\t\tif (patch->checkcount == checkcount)\n\t\t\tcontinue;\t// already checked this patch in another leaf\n\t\tpatch->checkcount = checkcount;\n\t\tif ( !(patch->surface->c.value & trace_contents) )\n\t\t\tcontinue;\n\t\tif ( !BoundsIntersect(patch->absmins, patch->absmaxs, trace_absmins, trace_absmaxs) )\n\t\t\tcontinue;\n\t\tfor (j = 0; j < patch->numfacets; j++)\n\t\t{\n\t\t\tCM_ClipBoxToPatch (trace_mins, trace_maxs, trace_start, trace_end, &trace_trace, &patch->facets[j]);\n\t\t\tif (trace_nearfraction<=0)\n\t\t\t\treturn;\n\t\t}\n\t}\n\n\tfor (k = 0; k < leaf->numleafcmeshes; k++)\n\t{\n\t\tpatchnum = prv->leafcmeshes[leaf->firstleafcmesh+k];\n\t\tcmesh = &prv->cmeshes[patchnum];\n\t\tif (cmesh->checkcount == checkcount)\n\t\t\tcontinue;\t// already checked this patch in another leaf\n\t\tcmesh->checkcount = checkcount;\n\t\tif ( !(cmesh->surface->c.value & trace_contents) )\n\t\t\tcontinue;\n\t\tif ( !BoundsIntersect(cmesh->absmins, cmesh->absmaxs, trace_absmins, trace_absmaxs) )\n\t\t\tcontinue;\n\n\t\tMod_Trace_Trisoup_(cmesh->xyz_array, cmesh->indicies, cmesh->numincidies, trace_start, trace_end, trace_mins, trace_maxs, &trace_trace, &cmesh->surface->c);\n\t\tif (trace_nearfraction<=0)\n\t\t\treturn;\n\t}\n#endif\n}\n\n\n/*\n================\nCM_TestInLeaf\n================\n*/\nstatic void CM_TestInLeaf (cminfo_t *prv, mleaf_t *leaf)\n{\n\tint\t\t\tk;\n\tq2cbrush_t\t*b;\n#ifdef Q3BSPS\n\tint patchnum, j;\n\tq3cmesh_t\t*cmesh;\n\tq3cpatch_t *patch;\n#endif\n\n\tif ( !(leaf->contents & trace_contents))\n\t\treturn;\n\t// trace line against all brushes in the leaf\n\tfor (k=0 ; k<leaf->numleafbrushes ; k++)\n\t{\n\t\tb = prv->leafbrushes[leaf->firstleafbrush+k];\n\t\tif (b->checkcount == checkcount)\n\t\t\tcontinue;\t// already checked this brush in another leaf\n\t\tb->checkcount = checkcount;\n\n\t\tif (!(b->contents & trace_contents))\n\t\t\tcontinue;\n\t\tif (!BoundsIntersect(b->absmins, b->absmaxs, trace_absmins, trace_absmaxs))\n\t\t\tcontinue;\n\t\tCM_TestBoxInBrush (trace_mins, trace_maxs, trace_start, &trace_trace, b);\n\t\tif (!trace_trace.fraction)\n\t\t\treturn;\n\t}\n\n#ifdef Q3BSPS\n\tif (!prv->mapisq3 || map_noCurves.value)\n\t\treturn;\n\n\t// trace line against all patches in the leaf\n\tfor (k = 0; k < leaf->numleafpatches; k++)\n\t{\n\t\tpatchnum = prv->leafpatches[leaf->firstleafpatch+k];\n\n\t\tpatch = &prv->patches[patchnum];\n\t\tif (patch->checkcount == checkcount)\n\t\t\tcontinue;\t// already checked this patch in another leaf\n\t\tpatch->checkcount = checkcount;\n\t\tif ( !(patch->surface->c.value & trace_contents) )\n\t\t\tcontinue;\n\t\tif ( !BoundsIntersect(patch->absmins, patch->absmaxs, trace_absmins, trace_absmaxs) )\n\t\t\tcontinue;\n\t\tfor (j = 0; j < patch->numfacets; j++)\n\t\t{\n\t\t\tCM_TestBoxInPatch (trace_mins, trace_maxs, trace_start, &trace_trace, &patch->facets[j]);\n\t\t\tif (!trace_trace.fraction)\n\t\t\t\treturn;\n\t\t}\n\t}\n\n\tfor (k = 0; k < leaf->numleafcmeshes; k++)\n\t{\n\t\tpatchnum = prv->leafcmeshes[leaf->firstleafcmesh+k];\n\t\tcmesh = &prv->cmeshes[patchnum];\n\t\tif (cmesh->checkcount == checkcount)\n\t\t\tcontinue;\t// already checked this patch in another leaf\n\t\tcmesh->checkcount = checkcount;\n\t\tif ( !(cmesh->surface->c.value & trace_contents) )\n\t\t\tcontinue;\n\t\tif ( !BoundsIntersect(cmesh->absmins, cmesh->absmaxs, trace_absmins, trace_absmaxs) )\n\t\t\tcontinue;\n\n\t\tMod_Trace_Trisoup_(cmesh->xyz_array, cmesh->indicies, cmesh->numincidies, trace_start, trace_end, trace_mins, trace_maxs, &trace_trace, &cmesh->surface->c);\n\t\tif (trace_nearfraction<=0)\n\t\t\treturn;\n\t}\n#endif\n}\n\n\n/*\n==================\nCM_RecursiveHullCheck\n\n==================\n*/\nstatic void CM_RecursiveHullCheck (model_t *mod, int num, float p1f, float p2f, vec3_t p1, vec3_t p2)\n{\n\tmnode_t\t\t*node;\n\tmplane_t\t*plane;\n\tfloat\t\tt1, t2, offset;\n\tfloat\t\tfrac, frac2;\n\tfloat\t\tidist;\n\tint\t\t\ti;\n\tvec3_t\t\tmid;\n\tint\t\t\tside;\n\tfloat\t\tmidf;\n\n\tif (trace_truefraction <= p1f)\n\t\treturn;\t\t// already hit something nearer\n\n\t// if < 0, we are in a leaf node\n\tif (num < 0)\n\t{\n\t\tCM_TraceToLeaf (mod->meshinfo, &mod->leafs[-1-num]);\n\t\treturn;\n\t}\n\n\t//\n\t// find the point distances to the seperating plane\n\t// and the offset for the size of the box\n\t//\n\tnode = mod->nodes + num;\n\tplane = node->plane;\n\n\tif (plane->type < 3)\n\t{\n\t\tt1 = p1[plane->type] - plane->dist;\n\t\tt2 = p2[plane->type] - plane->dist;\n\t\toffset = trace_extents[plane->type];\n\t}\n\telse\n\t{\n\t\tt1 = DotProduct (plane->normal, p1) - plane->dist;\n\t\tt2 = DotProduct (plane->normal, p2) - plane->dist;\n\t\tif (trace_shape == shape_ispoint)\n\t\t\toffset = 0;\n\t\telse\n\t\t\toffset = fabs(trace_extents[0]*plane->normal[0]) +\n\t\t\t\tfabs(trace_extents[1]*plane->normal[1]) +\n\t\t\t\tfabs(trace_extents[2]*plane->normal[2]);\n\t}\n\n\n#if 0\nCM_RecursiveHullCheck (node->childnum[0], p1f, p2f, p1, p2);\nCM_RecursiveHullCheck (node->childnum[1], p1f, p2f, p1, p2);\nreturn;\n#endif\n\n\t// see which sides we need to consider\n\tif (t1 >= offset && t2 >= offset)\n\t{\n\t\tCM_RecursiveHullCheck (mod, node->childnum[0], p1f, p2f, p1, p2);\n\t\treturn;\n\t}\n\tif (t1 < -offset && t2 < -offset)\n\t{\n\t\tCM_RecursiveHullCheck (mod, node->childnum[1], p1f, p2f, p1, p2);\n\t\treturn;\n\t}\n\n\t// put the crosspoint DIST_EPSILON pixels on the near side\n\tif (t1 < t2)\n\t{\n\t\tidist = 1.0/(t1-t2);\n\t\tside = 1;\n\t\tfrac2 = (t1 + offset + DIST_EPSILON)*idist;\n\t\tfrac = (t1 - offset + DIST_EPSILON)*idist;\n\t}\n\telse if (t1 > t2)\n\t{\n\t\tidist = 1.0/(t1-t2);\n\t\tside = 0;\n\t\tfrac2 = (t1 - offset - DIST_EPSILON)*idist;\n\t\tfrac = (t1 + offset + DIST_EPSILON)*idist;\n\t}\n\telse\n\t{\n\t\tside = 0;\n\t\tfrac = 1;\n\t\tfrac2 = 0;\n\t}\n\n\t// move up to the node\n\tif (frac < 0)\n\t\tfrac = 0;\n\tif (frac > 1)\n\t\tfrac = 1;\n\n\tmidf = p1f + (p2f - p1f)*frac;\n\tfor (i=0 ; i<3 ; i++)\n\t\tmid[i] = p1[i] + frac*(p2[i] - p1[i]);\n\n\tCM_RecursiveHullCheck (mod, node->childnum[side], p1f, midf, p1, mid);\n\n\n\t// go past the node\n\tif (frac2 < 0)\n\t\tfrac2 = 0;\n\tif (frac2 > 1)\n\t\tfrac2 = 1;\n\n\tmidf = p1f + (p2f - p1f)*frac2;\n\tfor (i=0 ; i<3 ; i++)\n\t\tmid[i] = p1[i] + frac2*(p2[i] - p1[i]);\n\n\tCM_RecursiveHullCheck (mod, node->childnum[side^1], midf, p2f, mid, p2);\n}\n\n//======================================================================\n\n/*\n==================\nCM_BoxTrace\n==================\n*/\nstatic trace_t\t\tCM_BoxTrace (model_t *mod, const vec3_t start, const vec3_t end,\n\t\t\t\t\t\t  const vec3_t mins, const vec3_t maxs, qboolean capsule,\n\t\t\t\t\t\t  int brushmask)\n{\n\tint\t\ti;\n\tvec3_t point;\n\n\n\tcheckcount++;\t\t// for multi-check avoidance\n\n\t// fill in a default trace\n\tmemset (&trace_trace, 0, sizeof(trace_trace));\n\ttrace_truefraction = 1;\n\ttrace_nearfraction = 1;\n\ttrace_trace.fraction = 1;\n\ttrace_trace.truefraction = 1;\n\ttrace_trace.surface = &(nullsurface.c);\n\n\tif (!mod)\t// map not loaded\n\t\treturn trace_trace;\n\n\ttrace_contents = brushmask;\n\tVectorCopy (start, trace_start);\n\tVectorCopy (end, trace_end);\n\tVectorCopy (mins, trace_mins);\n\tVectorCopy (maxs, trace_maxs);\n\n\tif (1)\n\t{\n\t\tVectorAdd(trace_maxs, trace_mins, point);\n\t\tVectorScale(point, 0.5, point);\n\n\t\tVectorAdd(trace_start, point, trace_start);\n\t\tVectorAdd(trace_end, point, trace_end);\n\t\tVectorSubtract(trace_mins, point, trace_mins);\n\t\tVectorSubtract(trace_maxs, point, trace_maxs);\n\t}\n\n\n\n\t// build a bounding box of the entire move (for patches)\n\tClearBounds (trace_absmins, trace_absmaxs);\n\n\t//determine the type of trace that we're going to use, and the max extents\n\tif (trace_mins[0] == 0 && trace_mins[1] == 0 && trace_mins[2] == 0 && trace_maxs[0] == 0 && trace_maxs[1] == 0 && trace_maxs[2] == 0)\n\t{\n\t\ttrace_shape = shape_ispoint;\n\t\tVectorSet (trace_extents, 1/32.0, 1/32.0, 1/32.0);\n\t\t//acedemic\n\t\tAddPointToBounds (trace_start, trace_absmins, trace_absmaxs);\n\t\tAddPointToBounds (trace_end, trace_absmins, trace_absmaxs);\n\t}\n\telse if (capsule)\n\t{\n\t\tfloat ext;\n\t\ttrace_shape = shape_iscapsule;\n\t\t//determine the capsule sizes\n\t\ttrace_capsulesize[0] = ((trace_maxs[0]-trace_mins[0]) + (trace_maxs[1]-trace_mins[1]))/4.0;\n\t\ttrace_capsulesize[1] = trace_maxs[2];\n\t\ttrace_capsulesize[2] = trace_mins[2];\n\t\t//make sure the mins_z/maxs_z isn't screwed.\n//\t\tif (trace_capsulesize[1]-trace_capsulesize[2] < trace_capsulesize[0])\n//\t\t\ttrace_capsulesize[1] = trace_capsulesize[0]+trace_capsulesize[2];\n\t\text = (trace_capsulesize[1] > -trace_capsulesize[2])?trace_capsulesize[1]:-trace_capsulesize[2];\n\t\ttrace_capsulesize[1] -= trace_capsulesize[0];\n\t\ttrace_capsulesize[2] += trace_capsulesize[0];\n\t\ttrace_extents[0] = ext+1;\n\t\ttrace_extents[1] = ext+1;\n\t\ttrace_extents[2] = ext+1;\n\n\t\t//determine the total range\n\t\tVectorSubtract (trace_start, trace_extents, point);\n\t\tAddPointToBounds (point, trace_absmins, trace_absmaxs);\n\t\tVectorAdd (trace_start, trace_extents, point);\n\t\tAddPointToBounds (point, trace_absmins, trace_absmaxs);\n\t\tVectorSubtract (trace_end, trace_extents, point);\n\t\tAddPointToBounds (point, trace_absmins, trace_absmaxs);\n\t\tVectorAdd (trace_end, trace_extents, point);\n\t\tAddPointToBounds (point, trace_absmins, trace_absmaxs);\n\t}\n\telse\n\t{\n\t\tVectorAdd (trace_start, trace_mins, point);\n\t\tAddPointToBounds (point, trace_absmins, trace_absmaxs);\n\t\tVectorAdd (trace_start, trace_maxs, point);\n\t\tAddPointToBounds (point, trace_absmins, trace_absmaxs);\n\t\tVectorAdd (trace_end, trace_mins, point);\n\t\tAddPointToBounds (point, trace_absmins, trace_absmaxs);\n\t\tVectorAdd (trace_end, trace_maxs, point);\n\t\tAddPointToBounds (point, trace_absmins, trace_absmaxs);\n\n\t\ttrace_shape = shape_isbox;\n\t\ttrace_extents[0] = ((-trace_mins[0] > trace_maxs[0]) ? -trace_mins[0] : trace_maxs[0])+1;\n\t\ttrace_extents[1] = ((-trace_mins[1] > trace_maxs[1]) ? -trace_mins[1] : trace_maxs[1])+1;\n\t\ttrace_extents[2] = ((-trace_mins[2] > trace_maxs[2]) ? -trace_mins[2] : trace_maxs[2])+1;\n\t}\n\n\ttrace_absmins[0] -= 1.0;\n\ttrace_absmins[1] -= 1.0;\n\ttrace_absmins[2] -= 1.0;\n\ttrace_absmaxs[0] += 1.0;\n\ttrace_absmaxs[1] += 1.0;\n\ttrace_absmaxs[2] += 1.0;\n\n#if 0\n\tif (0)\n\t{\t//treat *ALL* tests against the actual geometry instead of using any brushes.\n\t\t//also ignores the bsp etc. not fast. testing only.\n\n\t\ttrace_ispoint = trace_mins[0] == 0 && trace_mins[1] == 0 && trace_mins[2] == 0\n\t\t\t\t&& trace_maxs[0] == 0 && trace_maxs[1] == 0 && trace_maxs[2] == 0;\n\t\n\t\tfor (i = 0; i < mod->numsurfaces; i++)\n\t\t{\n\t\t\tCM_ClipBoxToMesh(trace_mins, trace_maxs, trace_start, trace_end, &trace_trace, mod->surfaces[i].mesh);\n\t\t}\n\t}\n\telse\n\tif (0)\n\t{\n\t\ttrace_ispoint = trace_mins[0] == 0 && trace_mins[1] == 0 && trace_mins[2] == 0\n\t\t\t\t&& trace_maxs[0] == 0 && trace_maxs[1] == 0 && trace_maxs[2] == 0;\n\t\n\t\tfor (i = 0; i < mod->numleafs; i++)\n\t\t\tCM_TraceToLeaf(&mod->leafs[i]);\n\t}\n\telse\n#endif\n\t//\n\t// check for position test special case\n\t//\n\tif (start[0] == end[0] && start[1] == end[1] && start[2] == end[2])\n\t{\n\t\tint\t\tleafs[1024];\n\t\tint\t\ti, numleafs;\n\t\tint\t\ttopnode;\n\n\t\tnumleafs = CM_BoxLeafnums_headnode (mod, trace_absmins, trace_absmaxs, leafs, sizeof(leafs)/sizeof(leafs[0]), mod->hulls[0].firstclipnode, &topnode);\n\t\tfor (i=0 ; i<numleafs ; i++)\n\t\t{\n\t\t\tCM_TestInLeaf (mod->meshinfo, &mod->leafs[leafs[i]]);\n\t\t\tif (trace_trace.allsolid)\n\t\t\t\tbreak;\n\t\t}\n\t\tVectorCopy (start, trace_trace.endpos);\n\t\treturn trace_trace;\n\t}\n\t//\n\t// general aabb trace\n\t//\n\telse\n\t{\n\t\tCM_RecursiveHullCheck (mod, mod->hulls[0].firstclipnode, 0, 1, trace_start, trace_end);\n\t}\n\n\tif (trace_nearfraction == 1)\n\t{\n\t\ttrace_trace.fraction = 1;\n\t\tVectorCopy (end, trace_trace.endpos);\n\t}\n\telse\n\t{\n\t\tif (trace_nearfraction<0)\n\t\t\ttrace_nearfraction=0;\n\t\ttrace_trace.fraction = trace_nearfraction;\n\t\tfor (i=0 ; i<3 ; i++)\n\t\t\ttrace_trace.endpos[i] = start[i] + trace_trace.fraction * (end[i] - start[i]);\n\t}\n\treturn trace_trace;\n}\n\nstatic qboolean BM_NativeTrace(model_t *model, int forcehullnum, const framestate_t *framestate, const vec3_t axis[3], const vec3_t start, const vec3_t end, const vec3_t mins, const vec3_t maxs, qboolean capsule, unsigned int contents, trace_t *trace)\n{\n\tint i;\n\tmemset (trace, 0, sizeof(*trace));\n\ttrace_truefraction = 1;\n\ttrace_nearfraction = 1;\n\ttrace->fraction = 1;\n\ttrace->truefraction = 1;\n\ttrace->surface = &(nullsurface.c);\n\n\tif (contents & FTECONTENTS_BODY)\n\t{\n\t\ttrace_contents = contents;\n\t\tVectorCopy (start, trace_start);\n\t\tVectorCopy (end, trace_end);\n\t\tVectorCopy (mins, trace_mins);\n\t\tVectorCopy (maxs, trace_maxs);\n\n\t\tif (trace_mins[0] == 0 && trace_mins[1] == 0 && trace_mins[2] == 0 && trace_maxs[0] == 0 && trace_maxs[1] == 0 && trace_maxs[2] == 0)\n\t\t\ttrace_shape = shape_ispoint;\n\t\telse if (capsule)\n\t\t\ttrace_shape = shape_iscapsule;\n\t\telse\n\t\t\ttrace_shape = shape_isbox;\n\n\t\tCM_ClipBoxToBrush (trace_mins, trace_maxs, trace_start, trace_end, trace, &box_brush);\n\t}\n\n\tif (trace_nearfraction == 1)\n\t{\n\t\ttrace->fraction = 1;\n\t\tVectorCopy (trace_end, trace->endpos);\n\t}\n\telse\n\t{\n\t\tif (trace_nearfraction<0)\n\t\t\ttrace_nearfraction=0;\n\t\ttrace->fraction = trace_nearfraction;\n\t\ttrace->truefraction = trace_truefraction;\n\t\tfor (i=0 ; i<3 ; i++)\n\t\t\ttrace->endpos[i] = trace_start[i] + trace->fraction * (trace_end[i] - trace_start[i]);\n\t}\n\treturn trace->fraction != 1;\n}\nstatic qboolean CM_NativeTrace(model_t *model, int forcehullnum, const framestate_t *framestate, const vec3_t axis[3], const vec3_t start, const vec3_t end, const vec3_t mins, const vec3_t maxs, qboolean capsule, unsigned int contents, trace_t *trace)\n{\n\tif (axis)\n\t{\n\t\tvec3_t start_l;\n\t\tvec3_t end_l;\n\t\tstart_l[0] = DotProduct(start, axis[0]);\n\t\tstart_l[1] = DotProduct(start, axis[1]);\n\t\tstart_l[2] = DotProduct(start, axis[2]);\n\t\tend_l[0] = DotProduct(end, axis[0]);\n\t\tend_l[1] = DotProduct(end, axis[1]);\n\t\tend_l[2] = DotProduct(end, axis[2]);\n\t\tVectorSet(trace_up, axis[0][2], -axis[1][2], axis[2][2]);\n\t\t*trace = CM_BoxTrace(model, start_l, end_l, mins, maxs, capsule, contents);\n#ifdef TERRAIN\n\t\tif (model->terrain)\n\t\t{\n\t\t\ttrace_t hmt;\n\t\t\tHeightmap_Trace(model, forcehullnum, framestate, NULL, start, end, mins, maxs, capsule, contents, &hmt);\n\t\t\tif (hmt.fraction < trace->fraction)\n\t\t\t\t*trace = hmt;\n\t\t}\n#endif\n\n\t\tif (trace->fraction == 1)\n\t\t{\n\t\t\tVectorCopy (end, trace->endpos);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tvec3_t iaxis[3];\n\t\t\tvec3_t norm;\n\t\t\tMatrix3x3_RM_Invert_Simple((void *)axis, iaxis);\n\t\t\tVectorCopy(trace->plane.normal, norm);\n\t\t\ttrace->plane.normal[0] = DotProduct(norm, iaxis[0]);\n\t\t\ttrace->plane.normal[1] = DotProduct(norm, iaxis[1]);\n\t\t\ttrace->plane.normal[2] = DotProduct(norm, iaxis[2]);\n\n\t\t\t/*just interpolate it, its easier than inverse matrix rotations*/\n\t\t\tVectorInterpolate(start, trace->fraction, end, trace->endpos);\n\t\t}\n\t}\n\telse\n\t{\n\t\tVectorSet(trace_up, 0, 0, 1);\n\t\t*trace = CM_BoxTrace(model, start, end, mins, maxs, capsule, contents);\n#ifdef TERRAIN\n\t\tif (model->terrain)\n\t\t{\n\t\t\ttrace_t hmt;\n\t\t\tHeightmap_Trace(model, forcehullnum, framestate, NULL, start, end, mins, maxs, capsule, contents, &hmt);\n\t\t\tif (hmt.fraction < trace->fraction)\n\t\t\t\t*trace = hmt;\n\t\t}\n#endif\n\t}\n\treturn trace->fraction != 1;\n}\n\n/*\n===============================================================================\n\nPVS / PHS\n\n===============================================================================\n*/\n\n/*\n===================\nCM_DecompressVis\n===================\n*/\n\n/*\nqbyte *Mod_Q2DecompressVis (qbyte *in, model_t *model)\n{\n\tstatic qbyte\tdecompressed[MAX_MAP_LEAFS/8];\n\tint\t\tc;\n\tqbyte\t*out;\n\tint\t\trow;\n\n\trow = (model->vis->numclusters+7)>>3;\n\tout = decompressed;\n\n\tif (!in)\n\t{\t// no vis info, so make all visible\n\t\twhile (row)\n\t\t{\n\t\t\t*out++ = 0xff;\n\t\t\trow--;\n\t\t}\n\t\treturn decompressed;\n\t}\n\n\tdo\n\t{\n\t\tif (*in)\n\t\t{\n\t\t\t*out++ = *in++;\n\t\t\tcontinue;\n\t\t}\n\n\t\tc = in[1];\n\t\tin += 2;\n\t\twhile (c)\n\t\t{\n\t\t\t*out++ = 0;\n\t\t\tc--;\n\t\t}\n\t} while (out - decompressed < row);\n\n\treturn decompressed;\n}\n\n#define\tDVIS_PVS\t0\n#define\tDVIS_PHS\t1\nqbyte *Mod_ClusterPVS (int cluster, model_t *model)\n{\n\tif (cluster == -1 || !model->vis)\n\t\treturn mod_novis;\n\treturn Mod_Q2DecompressVis ( (qbyte *)model->vis + model->vis->bitofs[cluster][DVIS_PVS],\n\t\tmodel);\n}\n*/\nstatic void CM_DecompressVis (model_t *mod, qbyte *in, qbyte *out, qboolean merge)\n{\n\tcminfo_t\t*prv = (cminfo_t*)mod->meshinfo;\n\tint\t\tc;\n\tqbyte\t*out_p;\n\tint\t\trow;\n\n\trow = (mod->numclusters+7)>>3;\n\tout_p = out;\n\n\tif (!in || !prv->numvisibility)\n\t{\t// no vis info, so make all visible\n\t\twhile (row)\n\t\t{\n\t\t\t*out_p++ = 0xff;\n\t\t\trow--;\n\t\t}\n\t\treturn;\n\t}\n\n\tif (merge)\n\t{\n\t\tdo\n\t\t{\n\t\t\tif (*in)\n\t\t\t{\n\t\t\t\t*out_p++ |= *in++;\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tout_p += in[1];\n\t\t\tin += 2;\n\t\t} while (out_p - out < row);\n\t}\n\telse\n\t{\n\t\tdo\n\t\t{\n\t\t\tif (*in)\n\t\t\t{\n\t\t\t\t*out_p++ = *in++;\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tc = in[1];\n\t\t\tin += 2;\n\t\t\tif ((out_p - out) + c > row)\n\t\t\t{\n\t\t\t\tc = row - (out_p - out);\n\t\t\t\tCon_DPrintf (\"warning: Vis decompression overrun\\n\");\n\t\t\t}\n\t\t\twhile (c)\n\t\t\t{\n\t\t\t\t*out_p++ = 0;\n\t\t\t\tc--;\n\t\t\t}\n\t\t} while (out_p - out < row);\n\t}\n}\n\nstatic pvsbuffer_t\tpvsrow;\nstatic pvsbuffer_t\tphsrow;\n\n\n\nstatic qbyte\t*CM_ClusterPVS (model_t *mod, int cluster, pvsbuffer_t *buffer, pvsmerge_t merge)\n{\n\tcminfo_t\t*prv = (cminfo_t*)mod->meshinfo;\n\tif (!buffer)\n\t\tbuffer = &pvsrow;\n\tif (buffer->buffersize < mod->pvsbytes)\n\t\tbuffer->buffer = BZ_Realloc(buffer->buffer, buffer->buffersize=mod->pvsbytes);\n\n\tif (mod->fromgame == fg_quake2)\n\t{\n\t\tif (cluster == -1)\n\t\t\tmemset (buffer->buffer, 0, (mod->numclusters+7)>>3);\n\t\telse\n\t\t\tCM_DecompressVis (mod, ((qbyte*)prv->q2vis) + prv->q2vis->bitofs[cluster][DVIS_PVS], buffer->buffer, merge==PVM_MERGE);\n\t\treturn buffer->buffer;\n\t}\n\telse\n\t{\n\t\tif (cluster != -1 && prv->q3pvs->numclusters)\n\t\t{\n\t\t\tif (merge == PVM_FAST)\n\t\t\t\treturn (qbyte *)prv->q3pvs->data + cluster * prv->q3pvs->rowsize;\n\t\t\telse if (merge == PVM_REPLACE)\n\t\t\t\tmemcpy(buffer->buffer, prv->q3pvs->data + cluster * prv->q3pvs->rowsize, mod->pvsbytes);\n\t\t\telse\n\t\t\t{\n\t\t\t\tint c;\n\t\t\t\tchar *in = prv->q3pvs->data + cluster * prv->q3pvs->rowsize;\n\t\t\t\tfor (c = 0; c < mod->pvsbytes; c+=4)\n\t\t\t\t\t*(int*)&buffer->buffer[c] |= *(int*)&in[c];\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (merge != PVM_MERGE)\n\t\t\t\tmemset (buffer->buffer, 0, (mod->numclusters+7)>>3);\n\t\t}\n\t\treturn buffer->buffer;\n\t}\n}\n\nstatic qbyte\t*CM_ClusterPHS (model_t *mod, int cluster, pvsbuffer_t *buffer)\n{\n\tcminfo_t\t*prv = (cminfo_t*)mod->meshinfo;\n\n\tif (!buffer)\n\t\tbuffer = &phsrow;\n\tif (buffer->buffersize < mod->pvsbytes)\n\t\tbuffer->buffer = BZ_Realloc(buffer->buffer, buffer->buffersize=mod->pvsbytes);\n\n#ifdef Q3BSPS\n\tif (mod->fromgame != fg_quake2)\n\t{\n\t\tif (cluster != -1 && prv->q3phs->numclusters)\n\t\t{\n\t\t\tif (prv->phscalced && !(prv->phscalced[cluster>>3] & (1<<(cluster&7))))\n\t\t\t\tCalcClusterPHS(prv, cluster);\n\t\t\treturn (qbyte *)prv->q3phs->data + cluster * prv->q3phs->rowsize;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tmemset (buffer->buffer, 0, (mod->numclusters+7)>>3);\n\t\t\treturn buffer->buffer;\n\t\t}\n\t}\n#endif\n\n\tif (cluster == -1)\n\t\tmemset (buffer->buffer, 0, (mod->numclusters+7)>>3);\n\telse\n\t\tCM_DecompressVis (mod, ((qbyte*)prv->q2vis) + prv->q2vis->bitofs[cluster][DVIS_PHS], buffer->buffer, false);\n\treturn buffer->buffer;\n}\n\n#ifdef HAVE_SERVER\nstatic unsigned int  SV_Q2BSP_FatPVS (model_t *mod, const vec3_t org, pvsbuffer_t *result, qboolean merge)\n{\n\tint\tleafs[64];\n\tint\t\ti, j, count;\n\tvec3_t\tmins, maxs;\n\n\tfor (i=0 ; i<3 ; i++)\n\t{\n\t\tmins[i] = org[i] - 8;\n\t\tmaxs[i] = org[i] + 8;\n\t}\n\n\tcount = CM_BoxLeafnums (mod, mins, maxs, leafs, countof(leafs), NULL);\n\tif (count < 1)\n\t\tSys_Error (\"SV_Q2FatPVS: count < 1\");\n\n\t// convert leafs to clusters\n\tfor (i=0 ; i<count ; i++)\n\t\tleafs[i] = CM_LeafCluster(mod, leafs[i]);\n\n\t//grow the buffer if needed\n\tif (result->buffersize < mod->pvsbytes)\n\t\tresult->buffer = BZ_Realloc(result->buffer, result->buffersize=mod->pvsbytes);\n\n\tif (count == 1 && leafs[0] == -1)\n\t{\t//if the only leaf is the outside then broadcast it.\n\t\tmemset(result->buffer, 0xff, mod->pvsbytes);\n\t\ti = count;\n\t}\n\telse\n\t{\n\t\ti = 0;\n\t\tif (!merge)\n\t\t\tCM_ClusterPVS(mod, leafs[i++], result, PVM_REPLACE);\n\t\t// or in all the other leaf bits\n\t\tfor ( ; i<count ; i++)\n\t\t{\n\t\t\tfor (j=0 ; j<i ; j++)\n\t\t\t\tif (leafs[i] == leafs[j])\n\t\t\t\t\tbreak;\n\t\t\tif (j != i)\n\t\t\t\tcontinue;\t\t// already have the cluster we want\n\t\t\tCM_ClusterPVS(mod, leafs[i], result, PVM_MERGE);\n\t\t}\n\t}\n\treturn mod->pvsbytes;\n}\n\nstatic int\t\tclientarea;\nstatic unsigned int Q23BSP_FatPVS(model_t *mod, const vec3_t org, pvsbuffer_t *fte_restrict buffer, qboolean merge)\n{//fixme: this doesn't add areas\n\tint\t\tleafnum;\n\tleafnum = CM_PointLeafnum (mod, org);\n\tclientarea = CM_LeafArea (mod, leafnum);\n\n\treturn SV_Q2BSP_FatPVS (mod, org, buffer, merge);\n}\n\nstatic qboolean Q23BSP_EdictInFatPVS(model_t *mod, const pvscache_t *ent, const qbyte *pvs, const int *areas)\n{\n\tint i,l;\n\tint nullarea = (mod->fromgame == fg_quake2)?0:-1;\n\tif (areas)\n\t{\n\t\tfor (i = 1; ; i++)\n\t\t{\n\t\t\tif (i > areas[0])\n\t\t\t\treturn false;\t//none of the camera's areas could see the entity\n\t\t\tif (areas[i] == ent->areanum)\n\t\t\t{\n\t\t\t\tif (areas[i] != nullarea)\n\t\t\t\t\tbreak;\n\t\t\t\t//else entity is fully outside the world, invisible to all...\n\t\t\t}\n\t\t\telse if (CM_AreasConnected (mod, areas[i], ent->areanum))\n\t\t\t\tbreak;\n\t\t\t// doors can legally straddle two areas, so\n\t\t\t// we may need to check another one\n\t\t\telse if (ent->areanum2 != nullarea && CM_AreasConnected (mod, areas[i], ent->areanum2))\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\tif (ent->num_leafs == -1)\n\t{\t// too many leafs for individual check, go by headnode\n\t\tif (!CM_HeadnodeVisible (mod, ent->headnode, pvs))\n\t\t\treturn false;\n\t}\n\telse\n\t{\t// check individual leafs\n\t\tfor (i=0 ; i < ent->num_leafs ; i++)\n\t\t{\n\t\t\tl = ent->leafnums[i];\n\t\t\tif (pvs[l >> 3] & (1 << (l&7) ))\n\t\t\t\tbreak;\n\t\t}\n\t\tif (i == ent->num_leafs)\n\t\t\treturn false;\t\t// not visible\n\t}\n\treturn true;\n}\n\nstatic void Q23BSP_FindTouchedLeafs(model_t *model, struct pvscache_s *ent, const float *mins, const float *maxs)\n{\n#define MAX_TOTAL_ENT_LEAFS\t\t128\n\tint\t\t\tleafs[MAX_TOTAL_ENT_LEAFS];\n\tint\t\t\tclusters[MAX_TOTAL_ENT_LEAFS];\n\tint num_leafs;\n\tint\t\t\ttopnode;\n\tint i, j;\n\tint\t\t\tarea;\n\tint nullarea = (model->fromgame == fg_quake2)?0:-1;\n\n\t//ent->num_leafs == q2's ent->num_clusters\n\tent->num_leafs = 0;\n\tent->areanum = nullarea;\n\tent->areanum2 = nullarea;\n\n\tif (!mins || !maxs)\n\t\treturn;\n\n\t//get all leafs, including solids\n\tnum_leafs = CM_BoxLeafnums (model, mins, maxs,\n\t\tleafs, MAX_TOTAL_ENT_LEAFS, &topnode);\n\n\t// set areas\n\tfor (i=0 ; i<num_leafs ; i++)\n\t{\n\t\tclusters[i] = CM_LeafCluster (model, leafs[i]);\n\t\tarea = CM_LeafArea (model, leafs[i]);\n\t\tif (area != nullarea)\n\t\t{\t// doors may legally straggle two areas,\n\t\t\t// but nothing should ever need more than that\n\t\t\tif (ent->areanum != nullarea && ent->areanum != area)\n\t\t\t\tent->areanum2 = area;\n\t\t\telse\n\t\t\t\tent->areanum = area;\n\t\t}\n\t}\n\n\tif (num_leafs >= MAX_TOTAL_ENT_LEAFS)\n\t{\t// assume we missed some leafs, and mark by headnode\n\t\tent->num_leafs = -1;\n\t\tent->headnode = topnode;\n\t}\n\telse\n\t{\n\t\tent->num_leafs = 0;\n\t\tfor (i=0 ; i<num_leafs ; i++)\n\t\t{\n\t\t\tif (clusters[i] == -1)\n\t\t\t\tcontinue;\t\t// not a visible leaf\n\t\t\tfor (j=0 ; j<i ; j++)\n\t\t\t\tif (clusters[j] == clusters[i])\n\t\t\t\t\tbreak;\n\t\t\tif (j == i)\n\t\t\t{\n\t\t\t\tif (ent->num_leafs == MAX_ENT_LEAFS)\n\t\t\t\t{\t// assume we missed some leafs, and mark by headnode\n\t\t\t\t\tent->num_leafs = -1;\n\t\t\t\t\tent->headnode = topnode;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tent->leafnums[ent->num_leafs++] = clusters[i];\n\t\t\t}\n\t\t}\n\t}\n}\n#endif\n\n/*\n===============================================================================\n\nAREAPORTALS\n\n===============================================================================\n*/\n\nstatic void FloodArea_r (cminfo_t\t*prv, size_t areaidx, int floodnum)\n{\n\tsize_t\t\ti;\n\n\tcareaflood_t *flood = &prv->areaflood[areaidx];\n\tif (flood->floodvalid == prv->floodvalid)\n\t{\n\t\tif (flood->floodnum == floodnum)\n\t\t\treturn;\n\t\tCon_Printf (\"FloodArea_r: reflooded\\n\");\n\t\treturn;\n\t}\n\n\tflood->floodnum = floodnum;\n\tflood->floodvalid = prv->floodvalid;\n\tswitch(prv->mapisq3)\n\t{\n\tcase true:\n#ifdef Q3BSPS\n\t\tfor (i=0 ; i<prv->numareas ; i++)\n\t\t{\n\t\t\tif (prv->q3areas[areaidx].numareaportals[i]>0)\n\t\t\t\tFloodArea_r (prv, i, floodnum);\n\t\t}\n#endif\n\t\tbreak;\n\tcase false:\n#ifdef Q2BSPS\n\t\t{\n\t\t\tq2carea_t *area = &prv->q2areas[areaidx];\n\t\t\tq2dareaportal_t\t*p = &prv->q2areaportals[area->firstareaportal];\n\t\t\tfor (i=0 ; i<area->numareaportals ; i++, p++)\n\t\t\t{\n\t\t\t\tif (prv->q2portalopen[p->portalnum])\n\t\t\t\t\tFloodArea_r (prv, p->otherarea, floodnum);\n\t\t\t}\n\t\t}\n#endif\n\t\tbreak;\n\t}\n}\n\n/*\n====================\nFloodAreaConnections\n\n\n====================\n*/\nstatic void\tFloodAreaConnections (cminfo_t\t*prv)\n{\n\tsize_t\t\ti;\n\tint\t\tfloodnum;\n\n\t// all current floods are now invalid\n\tprv->floodvalid++;\n\tfloodnum = 0;\n\n\t// area 0 is not used\n\tfor (i=0 ; i<prv->numareas ; i++)\n\t{\n\t\tif (prv->areaflood[i].floodvalid == prv->floodvalid)\n\t\t\tcontinue;\t\t// already flooded into\n\t\tfloodnum++;\n\t\tFloodArea_r (prv, i, floodnum);\n\t}\n}\n\nstatic void\tCM_SetAreaPortalState (model_t *mod, unsigned int portalnum, unsigned int area1, unsigned int area2, qboolean open)\n{\n\tcminfo_t\t*prv = (cminfo_t*)mod->meshinfo;\n\tswitch(prv->mapisq3)\n\t{\n#ifdef Q3BSPS\n\tcase true:\n\t\tif (area1 >= prv->numareas || area2 >= prv->numareas || area1==area2)\n\t\t\treturn;\n\n\t\tif (open)\n\t\t{\n\t\t\tprv->q3areas[area1].numareaportals[area2]++;\n\t\t\tprv->q3areas[area2].numareaportals[area1]++;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (!prv->q3areas[area1].numareaportals[area2])\n\t\t\t{\n\t\t\t\tCon_Printf(CON_WARNING\"CM_SetAreaPortalState: Areaportal closed more than opened...\\n\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tprv->q3areas[area1].numareaportals[area2]--;\n\t\t\tprv->q3areas[area2].numareaportals[area1]--;\n\t\t}\n\t\tbreak;\n#endif\n#ifdef Q2BSPS\n\tcase false:\n\t\tif (portalnum > prv->numq2areaportals)\n\t\t\treturn;\n\n\t\tif (prv->q2portalopen[portalnum] == open)\n\t\t\treturn;\n\t\tprv->q2portalopen[portalnum] = open;\n\t\tbreak;\n#endif\n\tdefault: break;\n\t}\n\tFloodAreaConnections (prv);\n}\n\nstatic qboolean\tVARGS CM_AreasConnected (model_t *mod, unsigned int area1, unsigned int area2)\n{\n\tcminfo_t\t*prv = (cminfo_t*)mod->meshinfo;\n\n\tif (map_noareas.value)\n\t\treturn true;\n\n\tif (area1 == ~0 || area2 == ~0)\n\t\treturn area1 == area2;\n\tif (area1 > prv->numareas || area2 > prv->numareas)\n\t\tHost_Error (\"area > numareas\");\n\n\tif (prv->areaflood[area1].floodnum == prv->areaflood[area2].floodnum)\n\t\treturn true;\n\treturn false;\n}\n\n\n/*\n=================\nCM_WriteAreaBits\n\nWrites a length qbyte followed by a bit vector of all the areas\nthat area in the same flood as the area parameter\n\nThis is used by the client refreshes to cull visibility\n=================\n*/\nstatic size_t CM_WriteAreaBits (model_t *mod, qbyte *buffer, size_t buffersize, int area, qboolean merge)\n{\n\tcminfo_t\t*prv = (cminfo_t*)mod->meshinfo;\n\tint\t\ti;\n\tint\t\tfloodnum;\n\tint\t\tbytes;\n\n\tbytes = (prv->numareas+7)>>3;\n\tif (bytes > buffersize)\n\t\tbytes = buffersize;\n\n\tif (map_noareas.value || (area < 0 && !merge))\n\t{\t// for debugging, send everything\n\t\tif (!merge)\n\t\t\tmemset (buffer, 255, bytes);\n\t}\n\telse\n\t{\n\t\tif (!merge)\n\t\t\tmemset (buffer, 0, bytes);\n\n\t\tfloodnum = prv->areaflood[area].floodnum;\n\t\tfor (i=0 ; i<prv->numareas ; i++)\n\t\t{\n\t\t\tif (prv->areaflood[i].floodnum == floodnum || !area)\n\t\t\t\tbuffer[i>>3] |= 1<<(i&7);\n\t\t}\n\t}\n\n\treturn bytes;\n}\n\n/*\n===================\nCM_WritePortalState\n\nReturns a size+pointer to the data that needs to be written into a saved game. \n===================\n*/\nstatic size_t CM_SaveAreaPortalBlob (model_t *mod, void **data)\n{\n\tcminfo_t\t*prv = (cminfo_t*)mod->meshinfo;\n\n\tif (mod->type == mod_brush && (mod->fromgame == fg_quake2 || mod->fromgame == fg_quake3))\n\t{\n\t\tswitch(prv->mapisq3)\n\t\t{\n#ifdef Q3BSPS\n\t\tcase true:\n\t\t\t//endian issues. oh well.\n\t\t\t*data = prv->q3areas;\n\t\t\treturn sizeof(prv->q3areas);\n#endif\n#ifdef Q2BSPS\n\t\tcase false:\n\t\t\t*data = prv->q2portalopen;\n\t\t\treturn sizeof(prv->q2portalopen);\n#endif\n\t\tdefault: break;\n\t\t}\n\t}\n\t*data = NULL;\n\treturn 0;\n}\n\n/*\n===================\nCM_ReadPortalState\n\nReads the portal state from a savegame file\nand recalculates the area connections\n===================\n*/\nstatic size_t\tCM_LoadAreaPortalBlob (model_t *mod, void *ptr, size_t ptrsize)\n{\n\tcminfo_t\t*prv = (cminfo_t*)mod->meshinfo;\n\n\tif (mod->type == mod_brush && (mod->fromgame == fg_quake2 || mod->fromgame == fg_quake3))\n\t{\n\t\tswitch(prv->mapisq3)\n\t\t{\n#ifdef Q3BSPS\n\t\tcase 1:\t//area*area refcounts. byte sizes don't tell us how many areas there were - would need sqrt(ptrsize/4) and I cba.\n\t\t\tif (ptrsize != sizeof(prv->q3areas))\n\t\t\t{\t//don't bother trying to handle graceful expansion/truncation, just reset the entire thing.\n\t\t\t\tsize_t x,y;\n\t\t\t\tif (ptrsize)\n\t\t\t\t\tCon_Printf(\"CM_ReadPortalState() expected %u, but only %u available\\n\",(unsigned int)sizeof(prv->q3areas),(unsigned int)ptrsize);\n\t\t\t\tfor (x = 0; x < countof(prv->q3areas); x++)\n\t\t\t\t\tfor (y = 0; y < countof(prv->q3areas[x].numareaportals); y++)\n\t\t\t\t\t\tprv->q3areas[x].numareaportals[y] = map_autoopenportals.ival;\n\t\t\t}\n\t\t\telse\n\t\t\t\tmemcpy(prv->q3areas, ptr, ptrsize);\n\t\t\tFloodAreaConnections (prv);\n\t\t\treturn sizeof(prv->q3areas);\n#endif\n#ifdef Q2BSPS\n\t\tcase 0:\t//per-portal booleans. we can just pad any missing portals.\n\t\t\tif (ptrsize && ptrsize != sizeof(prv->q2portalopen))\n\t\t\t\tCon_Printf(\"CM_ReadPortalState() expected %u, but only %u available\\n\",(unsigned int)sizeof(prv->q2portalopen),(unsigned int)ptrsize);\n\t\t\tif (ptrsize > sizeof(prv->q2portalopen))\n\t\t\t\tptrsize = sizeof(prv->q2portalopen);\n\t\t\tmemcpy(prv->q2portalopen, ptr, ptrsize);\n\t\t\tmemset(prv->q2portalopen+ptrsize, map_autoopenportals.ival, sizeof(prv->q2portalopen)-ptrsize);\n\t\t\tFloodAreaConnections (prv);\n\t\t\treturn sizeof(prv->q2portalopen);\n#endif\n\t\tdefault: break;\n\t\t}\n\t}\n\treturn 0;\n}\n\n#ifdef HAVE_SERVER\n/*\n=============\nCM_HeadnodeVisible\n\nReturns true if any leaf under headnode has a cluster that\nis potentially visible\n=============\n*/\nstatic qboolean CM_HeadnodeVisible (model_t *mod, int nodenum, const qbyte *visbits)\n{\n\tint\t\tleafnum;\n\tint\t\tcluster;\n\tmnode_t\t*node;\n\n\tif (nodenum < 0)\n\t{\n\t\tleafnum = -1-nodenum;\n\t\tcluster = mod->leafs[leafnum].cluster;\n\t\tif (cluster == -1)\n\t\t\treturn false;\n\t\tif (visbits[cluster>>3] & (1<<(cluster&7)))\n\t\t\treturn true;\n\t\treturn false;\n\t}\n\n\tnode = &mod->nodes[nodenum];\n\tif (CM_HeadnodeVisible(mod, node->childnum[0], visbits))\n\t\treturn true;\n\treturn CM_HeadnodeVisible(mod, node->childnum[1], visbits);\n}\n#endif\n\nstatic unsigned int Q2BSP_PointContents(model_t *mod, const vec3_t axis[3], const vec3_t p)\n{\n\tint pc;\n\tpc = CM_PointContents (mod, p);\n\treturn pc;\n}\n\n\n\n#ifdef HAVE_CLIENT\nstatic qbyte *frustumvis;\nstatic vec3_t modelorg;\nstatic unsigned int scenesequence;\nstatic unsigned int vissequence;\n/*\n===============\nR_MarkLeaves\n===============\n*/\n#ifdef Q3BSPS\nqbyte *R_MarkLeaves_Q3 (model_t *mod, int clusters[2])\n{\n\tstatic pvsbuffer_t\tcurframevis[R_MAX_RECURSE];\n\tqbyte *vis;\n\tint\t\ti;\n\n\tint cluster;\n\tmleaf_t\t*leaf;\n\tmnode_t *node;\n\tint portal = r_refdef.recurse;\n\tcminfo_t\t*prv = mod->meshinfo;\n\n\tif (!portal)\n\t{\n\t\tif (prv->oldclusters[0] == clusters[0] && !r_novis.value && clusters[0] != -1)\n\t\t\treturn prv->oldvis;\n\t}\n\n\t// development aid to let you run around and see exactly where\n\t// the pvs ends\n//\t\tif (r_lockpvs->value)\n//\t\t\treturn;\n\n\tvissequence++;\n\tprv->oldclusters[0] = clusters[0];\n\n\tif (r_novis.ival || clusters[0] == -1 || !mod->vis )\n\t{\n\t\tvis = NULL;\n\t\t// mark everything\n\t\tfor (i=0,leaf=mod->leafs ; i<mod->numleafs ; i++, leaf++)\n\t\t{\n//\t\t\tif (!leaf->nummarksurfaces)\n//\t\t\t{\n//\t\t\t\tcontinue;\n//\t\t\t}\n\n#if 1\n\t\t\tfor (node = (mnode_t*)leaf; node; node = node->parent)\n\t\t\t{\n\t\t\t\tif (node->visframe == vissequence)\n\t\t\t\t\tbreak;\n\t\t\t\tnode->visframe = vissequence;\n\t\t\t}\n#else\n\t\t\tleaf->visframe = vissequence;\n\t\t\tleaf->vischain = r_vischain;\n\t\t\tr_vischain = leaf;\n#endif\n\t\t}\n\t}\n\telse\n\t{\n\t\tvis = CM_ClusterPVS (mod, clusters[0], &curframevis[portal], PVM_FAST);\n\t\tfor (i=0,leaf=mod->leafs ; i<mod->numleafs ; i++, leaf++)\n\t\t{\n\t\t\tcluster = leaf->cluster;\n\t\t\tif (cluster == -1)// || !leaf->nummarksurfaces)\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (vis[cluster>>3] & (1<<(cluster&7)))\n\t\t\t{\n#if 1\n\t\t\t\tfor (node = (mnode_t*)leaf; node; node = node->parent)\n\t\t\t\t{\n\t\t\t\t\tif (node->visframe == vissequence)\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tnode->visframe = vissequence;\n\t\t\t\t}\n#else\n\t\t\t\tleaf->visframe = vissequence;\n\t\t\t\tleaf->vischain = r_vischain;\n\t\t\t\tr_vischain = leaf;\n#endif\n\t\t\t}\n\t\t}\n\t}\n\tprv->oldvis = vis;\n\treturn vis;\n}\n\nstatic void Surf_RecursiveQ3WorldNode (mnode_t *node, unsigned int clipflags)\n{\n\tint\t\t\tc, side, clipped;\n\tmplane_t\t*plane, *clipplane;\n\tmsurface_t\t*surf, **mark;\n\tmleaf_t\t\t*pleaf;\n\tdouble\t\tdot;\n\nstart:\n\n\tif (node->visframe != vissequence)\n\t\treturn;\n\n\tfor (c = 0, clipplane = r_refdef.frustum; c < r_refdef.frustum_numworldplanes; c++, clipplane++)\n\t{\n\t\tif (!(clipflags & (1 << c)))\n\t\t\tcontinue;\t// don't need to clip against it\n\n\t\tclipped = BOX_ON_PLANE_SIDE (node->minmaxs, node->minmaxs + 3, clipplane);\n\t\tif (clipped == 2)\n\t\t\treturn;\n\t\telse if (clipped == 1)\n\t\t\tclipflags -= (1<<c);\t// node is entirely on screen\n\t}\n\n// if a leaf node, draw stuff\n\tif (node->contents != -1)\n\t{\n\t\tpleaf = (mleaf_t *)node;\n\n\t\tif (! (r_refdef.areabits[pleaf->area>>3] & (1<<(pleaf->area&7)) ) )\n\t\t\treturn;\t\t// not visible\n\n\t\tc = pleaf->cluster;\n\t\tif (c >= 0)\n\t\t\tfrustumvis[c>>3] |= 1<<(c&7);\n\n\t\tmark = pleaf->firstmarksurface;\n\t\tfor (c = pleaf->nummarksurfaces; c; c--)\n\t\t{\n\t\t\tsurf = *mark++;\n\t\t\tif (surf->visframe == scenesequence)\n\t\t\t\tcontinue;\n\t\t\tsurf->visframe = scenesequence;\n\n//\t\t\tif (((dot < 0) ^ !!(surf->flags & SURF_PLANEBACK)))\n//\t\t\t\tcontinue;\t\t// wrong side\n\n\t\t\tsurf->sbatch->mesh[surf->sbatch->meshes++] = surf->mesh;\n\t\t}\n\t\treturn;\n\t}\n\n// node is just a decision point, so go down the apropriate sides\n\n// find which side of the node we are on\n\tplane = node->plane;\n\n\tswitch (plane->type)\n\t{\n\tcase PLANE_X:\n\t\tdot = modelorg[0] - plane->dist;\n\t\tbreak;\n\tcase PLANE_Y:\n\t\tdot = modelorg[1] - plane->dist;\n\t\tbreak;\n\tcase PLANE_Z:\n\t\tdot = modelorg[2] - plane->dist;\n\t\tbreak;\n\tdefault:\n\t\tdot = DotProduct (modelorg, plane->normal) - plane->dist;\n\t\tbreak;\n\t}\n\n\tif (dot >= 0)\n\t\tside = 0;\n\telse\n\t\tside = 1;\n\n// recurse down the children, front side first\n\tSurf_RecursiveQ3WorldNode (node->children[side], clipflags);\n\n// q3 nodes contain no drawables\n\n// recurse down the back side\n\t//GLR_RecursiveWorldNode (node->children[!side], clipflags);\n\tnode = node->children[!side];\n\tgoto start;\n}\n#endif\n\n#ifdef Q2BSPS\nqbyte *R_MarkLeaves_Q2 (model_t *mod, int viewclusters[2])\n{\n\tstatic pvsbuffer_t\tcurframevis[R_MAX_RECURSE];\n\tstatic qbyte\t*cvis[R_MAX_RECURSE];\n\tmnode_t\t*node;\n\tint\t\ti;\n\n\tint cluster;\n\tmleaf_t\t*leaf;\n\tqbyte *vis;\n\n\tint portal = r_refdef.recurse;\n\tcminfo_t\t*prv = mod->meshinfo;\n\n\tif (r_refdef.forcevis)\n\t{\n\t\tvis = cvis[portal] = r_refdef.forcedvis;\n\n\t\tprv->oldclusters[0] = -1;\n\t\tprv->oldclusters[1] = -1;\n\t}\n\telse\n\t{\n\t\tvis = cvis[portal];\n\t\tif (!portal)\n\t\t{\n\t\t\tif (prv->oldclusters[0] == viewclusters[0] && prv->oldclusters[1] == viewclusters[1])\n\t\t\t\treturn vis;\n\n\t\t\tprv->oldclusters[0] = viewclusters[0];\n\t\t\tprv->oldclusters[1] = viewclusters[1];\n\t\t}\n\t\telse\n\t\t{\n\t\t\tprv->oldclusters[0] = -1;\n\t\t\tprv->oldclusters[1] = -1;\n\t\t}\n\n\t\tif (r_novis.ival == 2)\n\t\t\treturn vis;\n\n\t\tif (r_novis.ival || r_viewcluster == -1 || !mod->vis)\n\t\t{\n\t\t\t// mark everything\n\t\t\tfor (i=0 ; i<mod->numleafs ; i++)\n\t\t\t\tmod->leafs[i].visframe = vissequence;\n\t\t\tfor (i=0 ; i<mod->numnodes ; i++)\n\t\t\t\tmod->nodes[i].visframe = vissequence;\n\t\t\treturn vis;\n\t\t}\n\n\t\tif (viewclusters[1] != viewclusters[0])\t// may have to combine two clusters because of solid water boundaries\n\t\t{\n\t\t\tvis = CM_ClusterPVS (mod, viewclusters[0], &curframevis[portal], PVM_REPLACE);\n\t\t\tvis = CM_ClusterPVS (mod, viewclusters[1], &curframevis[portal], PVM_MERGE);\n\t\t}\n\t\telse\n\t\t\tvis = CM_ClusterPVS (mod, viewclusters[0], &curframevis[portal], PVM_FAST);\n\t\tcvis[portal] = vis;\n\t}\n\n\tvissequence++;\n\n\tfor (i=0,leaf=mod->leafs ; i<mod->numleafs ; i++, leaf++)\n\t{\n\t\tcluster = leaf->cluster;\n\t\tif (cluster == -1)\n\t\t\tcontinue;\n\t\tif (vis[cluster>>3] & (1<<(cluster&7)))\n\t\t{\n\t\t\tnode = (mnode_t *)leaf;\n\t\t\tdo\n\t\t\t{\n\t\t\t\tif (node->visframe == vissequence)\n\t\t\t\t\tbreak;\n\t\t\t\tnode->visframe = vissequence;\n\t\t\t\tnode = node->parent;\n\t\t\t} while (node);\n\t\t}\n\t}\n\treturn vis;\n}\nstatic void Surf_RecursiveQ2WorldNode (mnode_t *node)\n{\n\tint\t\t\tc, side;\n\tmplane_t\t*plane;\n\tmsurface_t\t*surf, **mark;\n\tmleaf_t\t\t*pleaf;\n\tdouble\t\tdot;\n\n\tint sidebit;\n\n\tif (node->contents == Q2CONTENTS_SOLID)\n\t\treturn;\t\t// solid\n\n\tif (node->visframe != vissequence)\n\t\treturn;\n\tif (R_CullBox (node->minmaxs, node->minmaxs+3))\n\t\treturn;\n\n// if a leaf node, draw stuff\n\tif (node->contents != -1)\n\t{\n\t\tpleaf = (mleaf_t *)node;\n\n\t\t// check for door connected areas\n\t\tif (! (r_refdef.areabits[pleaf->area>>3] & (1<<(pleaf->area&7)) ) )\n\t\t\treturn;\t\t// not visible\n\n\t\tc = pleaf->cluster;\n\t\tif (c >= 0)\n\t\t\tfrustumvis[c>>3] |= 1<<(c&7);\n\n\t\tmark = pleaf->firstmarksurface;\n\t\tc = pleaf->nummarksurfaces;\n\n\t\tif (c)\n\t\t{\n\t\t\tdo\n\t\t\t{\n\t\t\t\t(*mark)->visframe = scenesequence;\n\t\t\t\tmark++;\n\t\t\t} while (--c);\n\t\t}\n\t\treturn;\n\t}\n\n// node is just a decision point, so go down the apropriate sides\n\n// find which side of the node we are on\n\tplane = node->plane;\n\n\tswitch (plane->type)\n\t{\n\tcase PLANE_X:\n\t\tdot = modelorg[0] - plane->dist;\n\t\tbreak;\n\tcase PLANE_Y:\n\t\tdot = modelorg[1] - plane->dist;\n\t\tbreak;\n\tcase PLANE_Z:\n\t\tdot = modelorg[2] - plane->dist;\n\t\tbreak;\n\tdefault:\n\t\tdot = DotProduct (modelorg, plane->normal) - plane->dist;\n\t\tbreak;\n\t}\n\n\tif (dot >= 0)\n\t{\n\t\tside = 0;\n\t\tsidebit = 0;\n\t}\n\telse\n\t{\n\t\tside = 1;\n\t\tsidebit = SURF_PLANEBACK;\n\t}\n\n// recurse down the children, front side first\n\tSurf_RecursiveQ2WorldNode (node->children[side]);\n\n\t// draw stuff\n\tfor ( c = node->numsurfaces, surf = currentmodel->surfaces + node->firstsurface; c ; c--, surf++)\n\t{\n\t\tif (surf->visframe != scenesequence)\n\t\t\tcontinue;\n\n\t\tif ( (surf->flags & SURF_PLANEBACK) != sidebit )\n\t\t\tcontinue;\t\t// wrong side\n\n\t\tsurf->visframe = 0;//scenesequence+1;//-1;\n\n\t\tSurf_RenderDynamicLightmaps (surf);\n\n\t\tsurf->sbatch->mesh[surf->sbatch->meshes++] = surf->mesh;\n\t}\n\n\n// recurse down the back side\n\tSurf_RecursiveQ2WorldNode (node->children[!side]);\n}\n#endif\n\n\nstatic void CM_PrepareFrame(model_t *mod, refdef_t *refdef, int area, int viewclusters[2], pvsbuffer_t *vis, qbyte **entvis_out, qbyte **surfvis_out)\n{\n\tqbyte *surfvis, *entvis;\n//\tqbyte *frustumvis;\n\n\tif (vis->buffersize < mod->pvsbytes)\n\t\tvis->buffer = BZ_Realloc(vis->buffer, vis->buffersize=mod->pvsbytes);\n\tfrustumvis = vis->buffer;\n\tmemset(frustumvis, 0, mod->pvsbytes);\n\n\tVectorCopy (r_refdef.vieworg, modelorg);\n\n\tscenesequence++;\n#ifdef Q3BSPS\n\tif (mod->fromgame == fg_quake3)\n\t{\n\t\tentvis = surfvis = R_MarkLeaves_Q3 (mod, viewclusters);\n\t\tSurf_RecursiveQ3WorldNode (mod->nodes, (1<<r_refdef.frustum_numworldplanes)-1);\n\t}\n\telse\n#endif\n#ifdef Q2BSPS\n\tif (mod->fromgame == fg_quake2)\n\t{\n\t\tentvis = surfvis = R_MarkLeaves_Q2 (mod, viewclusters);\n\t\tSurf_RecursiveQ2WorldNode (mod->nodes);\n\t}\n\telse\n#endif\n\t{\n\t\tentvis = surfvis = NULL;\n\t}\n\n\t*surfvis_out = frustumvis;\n\t*entvis_out = entvis;\n}\n#endif\n\n\n\n\nqboolean QDECL Mod_LoadQ2BrushModel (model_t *mod, void *buffer, size_t fsize)\n{\n\tmod->fromgame = fg_quake2;\n\treturn CM_LoadMap(mod, buffer, fsize, true) != NULL;\n}\n\nvoid CM_Init(void)\t//register cvars.\n{\n#define MAPOPTIONS \"Map Cvar Options\"\n\tCvar_Register(&map_noareas, MAPOPTIONS);\n\tCvar_Register(&map_noCurves, MAPOPTIONS);\n\tCvar_Register(&map_autoopenportals, MAPOPTIONS);\n\tCvar_Register(&q3bsp_surf_meshcollision_flag, MAPOPTIONS);\n\tCvar_Register(&q3bsp_surf_meshcollision_force, MAPOPTIONS);\n\tCvar_Register(&q3bsp_mergeq3lightmaps, MAPOPTIONS);\n\tCvar_Register(&q3bsp_ignorestyles, MAPOPTIONS);\n\tCvar_Register(&q3bsp_bihtraces, MAPOPTIONS);\n\tCvar_Register(&r_subdivisions, MAPOPTIONS);\n\n\tCM_InitBoxHull ();\n}\n#endif\n"
  },
  {
    "path": "engine/common/huff.c",
    "content": "/*\n===========================================================================\nCopyright (C) 1999-2005 Id Software, Inc.\n\nThis file is part of Quake III Arena source code.\n\nQuake III Arena source code is free software; you can redistribute it\nand/or modify it under the terms of the GNU General Public License as\npublished by the Free Software Foundation; either version 2 of the License,\nor (at your option) any later version.\n\nQuake III Arena source code is distributed in the hope that it will be\nuseful, but WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Quake III Arena source code; if not, write to the Free Software\nFoundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n===========================================================================\n*/\n\n/* This is based on the Adaptive Huffman algorithm described in Sayood's Data\n * Compression book.  The ranks are not actually stored, but implicitly defined\n * by the location of a node within a doubly-linked list */\n#include \"quakedef.h\"\n\n#ifdef HUFFNETWORK\n#define NYT HMAX\t\t\t\t\t/* NYT = Not Yet Transmitted */\n#define INTERNAL_NODE (HMAX+1)\n\ntypedef struct nodetype {\n\tstruct\tnodetype *left, *right, *parent; /* tree structure */ \n\tstruct\tnodetype *next, *prev; /* doubly-linked list */\n\tstruct\tnodetype **head; /* highest ranked node in block */\n\tint\t\tweight;\n\tint\t\tsymbol;\n} node_t;\n\n#define HMAX 256 /* Maximum symbol */\n\ntypedef struct {\n\tint\t\t\tblocNode;\n\tint\t\t\tblocPtrs;\n\n\tnode_t*\t\ttree;\n\tnode_t*\t\tlhead;\n\tnode_t*\t\tltail;\n\tnode_t*\t\tloc[HMAX+1];\n\tnode_t**\tfreelist;\n\n\tnode_t\t\tnodeList[768];\n\tnode_t*\t\tnodePtrs[768];\n} huff_t;\n\nstruct huffman_s{\n\tint counts[256];\n\tunsigned int crc;\n\tqboolean built;\n\n\thuff_t\t\tcompressor;\n\thuff_t\t\tdecompressor;\n};\nextern cvar_t net_compress;\n\nstatic int\t\t\tbloc = 0;\n/*\nstatic void\tHuff_putBit( int bit, qbyte *fout, int *offset) {\n\tbloc = *offset;\n\tif ((bloc&7) == 0) {\n\t\tfout[(bloc>>3)] = 0;\n\t}\n\tfout[(bloc>>3)] |= bit << (bloc&7);\n\tbloc++;\n\t*offset = bloc;\n}\n\nstatic int\t\tHuff_getBloc(void)\n{\n\treturn bloc;\n}\n\nstatic void\tHuff_setBloc(int _bloc)\n{\n\tbloc = _bloc;\n}\n\nstatic int\t\tHuff_getBit( qbyte *fin, int *offset) {\n\tint t;\n\tbloc = *offset;\n\tt = (fin[(bloc>>3)] >> (bloc&7)) & 0x1;\n\tbloc++;\n\t*offset = bloc;\n\treturn t;\n}*/\n\n/* Add a bit to the output file (buffered) */\nstatic void huff_add_bit (char bit, qbyte *fout) {\n\tif ((bloc&7) == 0) {\n\t\tfout[(bloc>>3)] = 0;\n\t}\n\tfout[(bloc>>3)] |= bit << (bloc&7);\n\tbloc++;\n}\n\n/* Receive one bit from the input file (buffered) */\nstatic int huff_get_bit (qbyte *fin) {\n\tint t;\n\tt = (fin[(bloc>>3)] >> (bloc&7)) & 0x1;\n\tbloc++;\n\treturn t;\n}\n\nstatic node_t **huff_get_ppnode(huff_t* huff) {\n\tnode_t **tppnode;\n\tif (!huff->freelist) {\n\t\treturn &(huff->nodePtrs[huff->blocPtrs++]);\n\t} else {\n\t\ttppnode = huff->freelist;\n\t\thuff->freelist = (node_t **)*tppnode;\n\t\treturn tppnode;\n\t}\n}\n\nstatic void huff_free_ppnode(huff_t* huff, node_t **ppnode) {\n\t*ppnode = (node_t *)huff->freelist;\n\thuff->freelist = ppnode;\n}\n\n/* Swap the location of these two nodes in the tree */\nstatic void huff_swap (huff_t* huff, node_t *node1, node_t *node2) { \n\tnode_t *par1, *par2;\n\n\tpar1 = node1->parent;\n\tpar2 = node2->parent;\n\n\tif (par1) {\n\t\tif (par1->left == node1) {\n\t\t\tpar1->left = node2;\n\t\t} else {\n\t      par1->right = node2;\n\t\t}\n\t} else {\n\t\thuff->tree = node2;\n\t}\n\n\tif (par2) {\n\t\tif (par2->left == node2) {\n\t\t\tpar2->left = node1;\n\t\t} else {\n\t\t\tpar2->right = node1;\n\t\t}\n\t} else {\n\t\thuff->tree = node1;\n\t}\n\n\tnode1->parent = par2;\n\tnode2->parent = par1;\n}\n\n/* Swap these two nodes in the linked list (update ranks) */\nstatic void huff_swaplist(node_t *node1, node_t *node2) {\n\tnode_t *par1;\n\n\tpar1 = node1->next;\n\tnode1->next = node2->next;\n\tnode2->next = par1;\n\n\tpar1 = node1->prev;\n\tnode1->prev = node2->prev;\n\tnode2->prev = par1;\n\n\tif (node1->next == node1) {\n\t\tnode1->next = node2;\n\t}\n\tif (node2->next == node2) {\n\t\tnode2->next = node1;\n\t}\n\tif (node1->next) {\n\t\tnode1->next->prev = node1;\n\t}\n\tif (node2->next) {\n\t\tnode2->next->prev = node2;\n\t}\n\tif (node1->prev) {\n\t\tnode1->prev->next = node1;\n\t}\n\tif (node2->prev) {\n\t\tnode2->prev->next = node2;\n\t}\n}\n\n/* Do the increments */\nstatic void huff_increment(huff_t* huff, node_t *node) {\n\tnode_t *lnode;\n\n\tif (!node) {\n\t\treturn;\n\t}\n\n\tif (node->next != NULL && node->next->weight == node->weight) {\n\t\tlnode = *node->head;\n\t\tif (lnode != node->parent) {\n\t\t\thuff_swap(huff, lnode, node);\n\t\t}\n\t\thuff_swaplist(lnode, node);\n\t}\n\tif (node->prev && node->prev->weight == node->weight) {\n\t\t*node->head = node->prev;\n\t} else {\n\t\t*node->head = NULL;\n\t\thuff_free_ppnode(huff, node->head);\n\t}\n\tnode->weight++;\n\tif (node->next && node->next->weight == node->weight) {\n\t\tnode->head = node->next->head;\n\t} else { \n\t\tnode->head = huff_get_ppnode(huff);\n\t\t*node->head = node;\n\t}\n\tif (node->parent) {\n\t\thuff_increment(huff, node->parent);\n\t\tif (node->prev == node->parent) {\n\t\t\thuff_swaplist(node, node->parent);\n\t\t\tif (*node->head == node) {\n\t\t\t\t*node->head = node->parent;\n\t\t\t}\n\t\t}\n\t}\n}\n\nstatic void Huff_addRef(huff_t* huff, qbyte ch) {\n\tnode_t *tnode, *tnode2;\n\tif (huff->loc[ch] == NULL) { /* if this is the first transmission of this node */\n\t\ttnode = &(huff->nodeList[huff->blocNode++]);\n\t\ttnode2 = &(huff->nodeList[huff->blocNode++]);\n\n\t\ttnode2->symbol = INTERNAL_NODE;\n\t\ttnode2->weight = 1;\n\t\ttnode2->next = huff->lhead->next;\n\t\tif (huff->lhead->next) {\n\t\t\thuff->lhead->next->prev = tnode2;\n\t\t\tif (huff->lhead->next->weight == 1) {\n\t\t\t\ttnode2->head = huff->lhead->next->head;\n\t\t\t} else {\n\t\t\t\ttnode2->head = huff_get_ppnode(huff);\n\t\t\t\t*tnode2->head = tnode2;\n\t\t\t}\n\t\t} else {\n\t\t\ttnode2->head = huff_get_ppnode(huff);\n\t\t\t*tnode2->head = tnode2;\n\t\t}\n\t\thuff->lhead->next = tnode2;\n\t\ttnode2->prev = huff->lhead;\n\n\t\ttnode->symbol = ch;\n\t\ttnode->weight = 1;\n\t\ttnode->next = huff->lhead->next;\n\t\tif (huff->lhead->next) {\n\t\t\thuff->lhead->next->prev = tnode;\n\t\t\tif (huff->lhead->next->weight == 1) {\n\t\t\t\ttnode->head = huff->lhead->next->head;\n\t\t\t} else {\n\t\t\t\t/* this should never happen */\n\t\t\t\ttnode->head = huff_get_ppnode(huff);\n\t\t\t\t*tnode->head = tnode2;\n\t\t\t}\n\t\t} else {\n\t\t\t/* this should never happen */\n\t\t\ttnode->head = huff_get_ppnode(huff);\n\t\t\t*tnode->head = tnode;\n\t\t}\n\t\thuff->lhead->next = tnode;\n\t\ttnode->prev = huff->lhead;\n\t\ttnode->left = tnode->right = NULL;\n\n\t\tif (huff->lhead->parent) {\n\t\t\tif (huff->lhead->parent->left == huff->lhead) { /* lhead is guaranteed to by the NYT */\n\t\t\t\thuff->lhead->parent->left = tnode2;\n\t\t\t} else {\n\t\t\t\thuff->lhead->parent->right = tnode2;\n\t\t\t}\n\t\t} else {\n\t\t\thuff->tree = tnode2; \n\t\t}\n\n\t\ttnode2->right = tnode;\n\t\ttnode2->left = huff->lhead;\n\n\t\ttnode2->parent = huff->lhead->parent;\n\t\thuff->lhead->parent = tnode->parent = tnode2;\n\n\t\thuff->loc[ch] = tnode;\n\n\t\thuff_increment(huff, tnode2->parent);\n\t} else {\n\t\thuff_increment(huff, huff->loc[ch]);\n\t}\n}\n\n/* Get a symbol */\nstatic int Huff_Receive (node_t *node, int *ch, qbyte *fin) {\n\twhile (node && node->symbol == INTERNAL_NODE) {\n\t\tif (huff_get_bit(fin)) {\n\t\t\tnode = node->right;\n\t\t} else {\n\t\t\tnode = node->left;\n\t\t}\n\t}\n\tif (!node) {\n\t\treturn 0;\n//\t\tCom_Error(ERR_DROP, \"Illegal tree!\");\n\t}\n\treturn (*ch = node->symbol);\n}\n\n/* Get a symbol */\nstatic void Huff_offsetReceive (node_t *node, int *ch, qbyte *fin, int *offset) {\n\tbloc = *offset;\n\twhile (node && node->symbol == INTERNAL_NODE) {\n\t\tif (huff_get_bit(fin)) {\n\t\t\tnode = node->right;\n\t\t} else {\n\t\t\tnode = node->left;\n\t\t}\n\t}\n\tif (!node) {\n\t\t*ch = 0;\n\t\treturn;\n//\t\tCom_Error(ERR_DROP, \"Illegal tree!\");\n\t}\n\t*ch = node->symbol;\n\t*offset = bloc;\n}\n\n/* Send the prefix code for this node */\nstatic void huff_send(node_t *node, node_t *child, qbyte *fout) {\n\tif (node->parent) {\n\t\thuff_send(node->parent, node, fout);\n\t}\n\tif (child) {\n\t\tif (node->right == child) {\n\t\t\thuff_add_bit(1, fout);\n\t\t} else {\n\t\t\thuff_add_bit(0, fout);\n\t\t}\n\t}\n}\n\n/* Send a symbol */\nstatic void Huff_transmit (huff_t *huff, int ch, qbyte *fout) {\n\tint i;\n\tif (huff->loc[ch] == NULL) { \n\t\t/* node_t hasn't been transmitted, send a NYT, then the symbol */\n\t\tHuff_transmit(huff, NYT, fout);\n\t\tfor (i = 7; i >= 0; i--) {\n\t\t\thuff_add_bit((char)((ch >> i) & 0x1), fout);\n\t\t}\n\t} else {\n\t\thuff_send(huff->loc[ch], NULL, fout);\n\t}\n}\n\nstatic void Huff_offsetTransmit (huff_t *huff, int ch, qbyte *fout, int *offset) {\n\tbloc = *offset;\n\thuff_send(huff->loc[ch], NULL, fout);\n\t*offset = bloc;\n}\n\nstatic void Huff_Decompress(sizebuf_t *mbuf, int offset) {\n\tint\t\t\tch, cch, i, j, size;\n\tqbyte\t\tseq[65536];\n\tqbyte*\t\tbuffer;\n\thuff_t\t\thuff;\n\n\tsize = mbuf->cursize - offset;\n\tbuffer = mbuf->data + offset;\n\n\tif ( size <= 0 ) {\n\t\treturn;\n\t}\n\n\tmemset(&huff, 0, sizeof(huff_t));\n\t// Initialize the tree & list with the NYT node \n\thuff.tree = huff.lhead = huff.ltail = huff.loc[NYT] = &(huff.nodeList[huff.blocNode++]);\n\thuff.tree->symbol = NYT;\n\thuff.tree->weight = 0;\n\thuff.lhead->next = huff.lhead->prev = NULL;\n\thuff.tree->parent = huff.tree->left = huff.tree->right = NULL;\n\n\tcch = buffer[0]*256 + buffer[1];\n\t// don't overflow with bad messages\n\tif ( cch > mbuf->maxsize - offset ) {\n\t\tcch = mbuf->maxsize - offset;\n\t}\n\tbloc = 16;\n\n\tfor ( j = 0; j < cch; j++ ) {\n\t\tch = 0;\n\t\t// don't overflow reading from the messages\n\t\t// FIXME: would it be better to have an overflow check in get_bit ?\n\t\tif ( (bloc >> 3) > size ) {\n\t\t\tseq[j] = 0;\n\t\t\tbreak;\n\t\t}\n\t\tHuff_Receive(huff.tree, &ch, buffer);\t\t\t\t/* Get a character */\n\t\tif ( ch == NYT ) {\t\t\t\t\t\t\t\t/* We got a NYT, get the symbol associated with it */\n\t\t\tch = 0;\n\t\t\tfor ( i = 0; i < 8; i++ ) {\n\t\t\t\tch = (ch<<1) + huff_get_bit(buffer);\n\t\t\t}\n\t\t}\n\n\t\tseq[j] = ch;\t\t\t\t\t\t\t\t\t/* Write symbol */\n\n\t\tHuff_addRef(&huff, (qbyte)ch);\t\t\t\t\t\t\t\t/* Increment node */\n\t}\n\tmbuf->cursize = cch + offset;\n\tmemcpy(mbuf->data + offset, seq, cch);\n}\n\nstatic void Huff_Compress(sizebuf_t *mbuf, int offset) {\n\tint\t\t\ti, ch, size;\n\tqbyte\t\tseq[65536];\n\tqbyte*\t\tbuffer;\n\thuff_t\t\thuff;\n\n\tsize = mbuf->cursize - offset;\n\tbuffer = mbuf->data+ + offset;\n\n\tif (size<=0) {\n\t\treturn;\n\t}\n\n\tmemset(&huff, 0, sizeof(huff_t));\n\t// Add the NYT (not yet transmitted) node into the tree/list */\n\thuff.tree = huff.lhead = huff.loc[NYT] =  &(huff.nodeList[huff.blocNode++]);\n\thuff.tree->symbol = NYT;\n\thuff.tree->weight = 0;\n\thuff.lhead->next = huff.lhead->prev = NULL;\n\thuff.tree->parent = huff.tree->left = huff.tree->right = NULL;\n\n\tseq[0] = (size>>8);\n\tseq[1] = size&0xff;\n\n\tbloc = 16;\n\n\tfor (i=0; i<size; i++ ) {\n\t\tch = buffer[i];\n\t\tHuff_transmit(&huff, ch, seq);\t\t\t\t\t\t/* Transmit symbol */\n\t\tHuff_addRef(&huff, (qbyte)ch);\t\t\t\t\t\t\t\t/* Do update */\n\t}\n\n\tbloc += 8;\t\t\t\t\t\t\t\t\t\t\t\t// next byte\n\n\tmbuf->cursize = (bloc>>3) + offset;\n\tmemcpy(mbuf->data+offset, seq, (bloc>>3));\n}\n\nstatic void Huff_Init(huffman_t *huff)\n{\n\tint i, j;\n\n\tmemset(&huff->compressor, 0, sizeof(huff_t));\n\tmemset(&huff->decompressor, 0, sizeof(huff_t));\n\n\t// Initialize the tree & list with the NYT node \n\thuff->decompressor.tree = huff->decompressor.lhead = huff->decompressor.ltail = huff->decompressor.loc[NYT] = &(huff->decompressor.nodeList[huff->decompressor.blocNode++]);\n\thuff->decompressor.tree->symbol = NYT;\n\thuff->decompressor.tree->weight = 0;\n\thuff->decompressor.lhead->next = huff->decompressor.lhead->prev = NULL;\n\thuff->decompressor.tree->parent = huff->decompressor.tree->left = huff->decompressor.tree->right = NULL;\n\n\t// Add the NYT (not yet transmitted) node into the tree/list */\n\thuff->compressor.tree = huff->compressor.lhead = huff->compressor.loc[NYT] =  &(huff->compressor.nodeList[huff->compressor.blocNode++]);\n\thuff->compressor.tree->symbol = NYT;\n\thuff->compressor.tree->weight = 0;\n\thuff->compressor.lhead->next = huff->compressor.lhead->prev = NULL;\n\thuff->compressor.tree->parent = huff->compressor.tree->left = huff->compressor.tree->right = NULL;\n\n\tfor(i=0;i<256;i++)\n\t{\n\t\tfor (j=0;j<huff->counts[i];j++)\n\t\t{\n\t\t\tHuff_addRef(&huff->compressor,\t(qbyte)i);\n\t\t\tHuff_addRef(&huff->decompressor,\t(qbyte)i);\n\t\t}\n\t}\n\thuff->built = true;\n}\n\n\n\n\nstatic huffman_t q3huff = \n{\n\t{\n\t0x3D1CB, 0x0A0E9, 0x01894, 0x01BC2, 0x00E92, 0x00EA6, 0x017DE, 0x05AF3,\n\t0x08225, 0x01B26, 0x01E9E, 0x025F2, 0x02429, 0x0436B, 0x00F6D, 0x006F2,\n\t0x02060, 0x00644, 0x00636, 0x0067F, 0x0044C, 0x004BD, 0x004D6, 0x0046E,\n\t0x006D5, 0x00423, 0x004DE, 0x0047D, 0x004F9, 0x01186, 0x00AF5, 0x00D90,\n\t0x0553B, 0x00487, 0x00686, 0x0042A, 0x00413, 0x003F4, 0x0041D, 0x0042E,\n\t0x006BE, 0x00378, 0x0049C, 0x00352, 0x003C0, 0x0030C, 0x006D8, 0x00CE0,\n\t0x02986, 0x011A2, 0x016F9, 0x00A7D, 0x0122A, 0x00EFD, 0x0082D, 0x0074B,\n\t0x00A18, 0x0079D, 0x007B4, 0x003AC, 0x0046E, 0x006FC, 0x00686, 0x004B6,\n\t0x01657, 0x017F0, 0x01C36, 0x019FE, 0x00E7E, 0x00ED3, 0x005D4, 0x005F4,\n\t0x008A7, 0x00474, 0x0054B, 0x003CB, 0x00884, 0x004E0, 0x00530, 0x004AB,\n\t0x006EA, 0x00436, 0x004F0, 0x004F2, 0x00490, 0x003C5, 0x00483, 0x004A2,\n\t0x00543, 0x004CC, 0x005F9, 0x00640, 0x00A39, 0x00800, 0x009F2, 0x00CCB,\n\t0x0096A, 0x00E01, 0x009C8, 0x00AF0, 0x00A73, 0x01802, 0x00E4F, 0x00B18,\n\t0x037AD, 0x00C5C, 0x008AD, 0x00697, 0x00C88, 0x00AB3, 0x00DB8, 0x012BC,\n\t0x00FFB, 0x00DBB, 0x014A8, 0x00FB0, 0x01F01, 0x0178F, 0x014F0, 0x00F54,\n\t0x0131C, 0x00E9F, 0x011D6, 0x012C7, 0x016DC, 0x01900, 0x01851, 0x02063,\n\t0x05ACB, 0x01E9E, 0x01BA1, 0x022E7, 0x0153D, 0x01183, 0x00E39, 0x01488,\n\t0x014C0, 0x014D0, 0x014FA, 0x00DA4, 0x0099A, 0x0069E, 0x0071D, 0x00849,\n\t0x0077C, 0x0047D, 0x005EC, 0x00557, 0x004D4, 0x00405, 0x004EA, 0x00450,\n\t0x004DD, 0x003EE, 0x0047D, 0x00401, 0x004D9, 0x003B8, 0x00507, 0x003E5,\n\t0x006B1, 0x003F1, 0x004A3, 0x0036F, 0x0044B, 0x003A1, 0x00436, 0x003B7,\n\t0x00678, 0x003A2, 0x00481, 0x00406, 0x004EE, 0x00426, 0x004BE, 0x00424,\n\t0x00655, 0x003A2, 0x00452, 0x00390, 0x0040A, 0x0037C, 0x00486, 0x003DE,\n\t0x00497, 0x00352, 0x00461, 0x00387, 0x0043F, 0x00398, 0x00478, 0x00420,\n\t0x00D86, 0x008C0, 0x0112D, 0x02F68, 0x01E4E, 0x00541, 0x0051B, 0x00CCE,\n\t0x0079E, 0x00376, 0x003FF, 0x00458, 0x00435, 0x00412, 0x00425, 0x0042F,\n\t0x005CC, 0x003E9, 0x00448, 0x00393, 0x0041C, 0x003E3, 0x0042E, 0x0036C,\n\t0x00457, 0x00353, 0x00423, 0x00325, 0x00458, 0x0039B, 0x0044F, 0x00331,\n\t0x0076B, 0x00750, 0x003D0, 0x00349, 0x00467, 0x003BC, 0x00487, 0x003B6,\n\t0x01E6F, 0x003BA, 0x00509, 0x003A5, 0x00467, 0x00C87, 0x003FC, 0x0039F,\n\t0x0054B, 0x00300, 0x00410, 0x002E9, 0x003B8, 0x00325, 0x00431, 0x002E4,\n\t0x003F5, 0x00325, 0x003F0, 0x0031C, 0x003E4, 0x00421, 0x02CC1, 0x034C0\n\t},\n\t0x286f2e8d\n};\n\nint Huff_PreferedCompressionCRC (void)\n{\n\tif (!net_compress.ival)\n\t\treturn 0;\n\treturn q3huff.crc;\n}\nhuffman_t *Huff_CompressionCRC(int crc)\n{\n\thuffman_t *huff = NULL;\n\n\tif (crc == q3huff.crc)\n\t\thuff = &q3huff;\n\telse\n\t\thuff = NULL;\n\n\tif (huff && !huff->built)\n\t\tHuff_Init(huff);\n\treturn huff;\n}\nvoid Huff_DecryptPacket(sizebuf_t *msg, int offset)\n{\n\t//decompress using a dynamic from-nil tree\n\tHuff_Decompress(msg, offset);\n}\nvoid Huff_EncryptPacket(sizebuf_t *msg, int offset)\n{\n\t//decompress using a dynamic from-nil tree\n\tHuff_Compress(msg, offset);\n}\nint Huff_GetByte(qbyte *buffer, int *count)\n{\n\tint ch;\n\tHuff_offsetReceive (q3huff.decompressor.tree, &ch, buffer, count);\n\treturn ch;\n}\nvoid Huff_EmitByte(int ch, qbyte *buffer, int *count)\n{\n\tHuff_offsetTransmit(&q3huff.compressor, ch, buffer, count);\n}\n\nvoid Huff_CompressPacket(huffman_t *huff, sizebuf_t *msg, int offset)\n{\n\tqbyte\tbuffer[MAX_OVERALLMSGLEN];\n\tqbyte\t*data;\n\tint\t\toutLen;\n\tint\t\tinLen;\n\tint\t\ti;\n\n\tdata = msg->data + offset;\n\tinLen = msg->cursize - offset;\t\n\tif (inLen <= 0 || inLen >= MAX_OVERALLMSGLEN)\n\t{\n\t\t//panic!\n\t\treturn;\n\t}\n\n\toutLen = 0;\n\tfor (i=0; i < inLen; i++)\n\t{\n\t\tHuff_offsetTransmit(&huff->compressor, data[i], buffer, &outLen);\n\n\t\tif (outLen > inLen)\n\t\t\tbreak;\n\t\tif (outLen > MAX_OVERALLMSGLEN-64)\n\t\t{\n\t\t\tCon_Printf(\"Huffman overflow\\n\");\n\t\t\t//panic\n\t\t\tdata[0] = 0x80;\n\t\t\tmsg->cursize = offset+1;\n\t\t\treturn;\n\t\t}\n\t}\n\n\tif (outLen > inLen)\n\t{\n\t\tmemmove(data+1, data, inLen);\n\t\tdata[0] = 0x80;\t//this would have grown the packet.\n\t\tmsg->cursize+=1;\n\t\treturn;\t//cap it at only 1 qbyte growth.\n\t}\n\n\tmsg->cursize = offset + outLen;\n\t{\t//add the bitcount\n\t\tdata[0] = (outLen<<3) - outLen;\n\t\tdata+=1;\n\t\tmsg->cursize+=1;\n\t}\n\tif (msg->cursize > msg->maxsize)\n\t\tSys_Error(\"Compression became too large\\n\");\n\tmemcpy(data, buffer, outLen);\n}\nvoid Huff_DecompressPacket(huffman_t *huff, sizebuf_t *msg, int offset)\n{\n\tqbyte\tbuffer[MAX_OVERALLMSGLEN];\n\tqbyte\t*data;\n\tint\t\toutLen;\n\tint\t\tinLen;\n\tint\t\ti, ch;\n\n\tdata = msg->data + offset;\n\tinLen = msg->cursize - offset;\t\n\tif (inLen <= 0 || inLen >= MAX_OVERALLMSGLEN)\n\t{\n\t\treturn;\n\t}\n\n\tinLen<<=3;\n\t{\t//add the bitcount\n\t\tinLen = inLen-8-data[0];\n\t\tif (data[0]&0x80)\n\t\t{\t//packet would have grown.\n\t\t\tmsg->cursize -= 1;\n\t\t\tmemmove(data, data+1, msg->cursize);\n\t\t\treturn;\t//this never happened, okay?\n\t\t}\n\t\tdata+=1;\n\t}\n\n\toutLen = 0;\n\tfor(i=0; outLen < inLen; i++)\n\t{\n\t\tif (i == MAX_OVERALLMSGLEN)\n\t\t\tSys_Error(\"Decompression became too large\\n\");\n\t\tHuff_offsetReceive (huff->decompressor.tree, &ch, data, &outLen);\n\t\tbuffer[i] = ch;\n\t}\n\t\n\tmsg->cursize = offset + i;\n\tif (msg->cursize > msg->maxsize)\n\t\tSys_Error(\"Decompression became too large\\n\");\n\tmemcpy(msg->data + offset, buffer, i);\n}\n#endif\n\n"
  },
  {
    "path": "engine/common/json.c",
    "content": "#include \"quakedef.h\"\n\n/*struct jsonparsectx_s\n{\n\tchar const *const data;\n\tconst size_t size;\n\tsize_t pos;\n};*/\n\n//node destruction\nstatic void JSON_Orphan(json_t *t)\n{\n\tif (t->parent)\n\t{\n\t\tjson_t *p = t->parent, **l = &p->child;\n\t\tif (p->arraymax)\n\t\t{\n\t\t\tsize_t idx = atoi(t->name);\n\t\t\tif (idx <= p->arraymax)\n\t\t\t\tp->array[idx] = NULL;\n\t\t\t//FIXME: sibling links are screwed. be careful iterrating after a removal.\n\t\t}\n\t\telse\n\t\t{\n\t\t\twhile (*l)\n\t\t\t{\n\t\t\t\tif (*l == t)\n\t\t\t\t{\n\t\t\t\t\t*l = t->sibling;\n\t\t\t\t\tif (*l)\n\t\t\t\t\t\tp->childlink = l;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tl = &(*l)->sibling;\n\t\t\t}\n\t\t}\n\t\tt->parent = NULL;\n\t\tt->sibling = NULL;\n\t}\n}\nvoid JSON_Destroy(json_t *t)\n{\n\tif (t)\n\t{\n\t\tif (t->arraymax)\n\t\t{\n\t\t\tsize_t idx;\n\t\t\tfor (idx = 0; idx < t->arraymax; idx++)\n\t\t\t\tif (t->array[idx])\n\t\t\t\t\tJSON_Destroy(t->array[idx]);\n\t\t\tfree(t->array);\n\t\t}\n\t\telse\n\t\t{\n\t\t\twhile(t->child)\n\t\t\t\tJSON_Destroy(t->child);\n\t\t}\n\t\tJSON_Orphan(t);\n\t\tfree(t);\n\t}\n}\n\n//node creation\nstatic json_t *JSON_CreateNode(json_t *parent, const char *namestart, const char *nameend, const char *bodystart, const char *bodyend, int type)\n{\n\tjson_t *j;\n\tqboolean dupbody = false;\n\tif (namestart && !nameend)\n\t\tnameend = namestart+strlen(namestart);\n\tif (bodystart && !bodyend)\n\t{\n\t\tdupbody = true;\n\t\tbodyend = bodystart+strlen(bodystart);\n\t}\n\t//FIXME: escapes within names are a thing. a stupid thing, but still a thing.\n\tj = malloc(sizeof(*j) + nameend-namestart + (dupbody?1+bodyend-bodystart:0));\n\tmemcpy(j->name, namestart, nameend-namestart);\n\tj->name[nameend-namestart] = 0;\n\tj->bodystart = bodystart;\n\tj->bodyend = bodyend;\n\n\tj->child = NULL;\n\tj->sibling = NULL;\n\tj->arraymax = 0;\n\tj->type = type;\n\tif (type == json_type_array)\n\t{\t//pre-initialise the array a bit.\n\t\tj->arraymax = 32;\n\t\tj->array = calloc(j->arraymax, sizeof(*j->array));\n\t}\n\telse\n\t\tj->childlink = &j->child;\n\tj->parent = parent;\n\tif (parent)\n\t{\n\t\tif (parent->arraymax)\n\t\t{\n\t\t\tsize_t idx = atoi(j->name);\n\t\t\tif (idx >= parent->arraymax)\n\t\t\t{\n\t\t\t\tsize_t oldmax = parent->arraymax;\n\t\t\t\tparent->arraymax = max(idx+1, parent->arraymax*2);\n\t\t\t\tparent->array = realloc(parent->array, sizeof(*parent->array)*parent->arraymax);\n\t\t\t\twhile (oldmax < parent->arraymax)\n\t\t\t\t\tparent->array[oldmax++] = NULL;\t//make sure there's no gaps.\n\t\t\t}\n\t\t\tparent->array[idx] = j;\n\t\t\tif (!idx)\n\t\t\t\tparent->child = j;\n\t\t\telse if (parent->array[idx-1])\n\t\t\t\tparent->array[idx-1]->sibling = j;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t*parent->childlink = j;\n\t\t\tparent->childlink = &j->sibling;\n\t\t}\n\t\tj->used = false;\n\t}\n\telse\n\t\tj->used = true;\n\n\tif (dupbody)\n\t{\n\t\tchar *bod = j->name + (nameend-namestart)+1;\n\t\tj->bodystart = bod;\n\t\tj->bodyend = j->bodystart + (bodyend-bodystart);\n\t\tmemcpy(bod, bodystart, bodyend-bodystart);\n\t\tbod[bodyend-bodystart] = 0;\n\t}\n\treturn j;\n}\n\n//node parsing\nstatic void JSON_SkipWhite(struct jsonparsectx_s *ctx)\n{\n\twhile (ctx->pos < ctx->size)\n\t{\n\t\t//if its simple whitespace then keep skipping over it\n\t\tif (ctx->data[ctx->pos] == ' ' ||\n\t\t\tctx->data[ctx->pos] == '\\t' ||\n\t\t\tctx->data[ctx->pos] == '\\r' ||\n\t\t\tctx->data[ctx->pos] == '\\n' )\n\t\t{\n\t\t\tctx->pos+=1;\n\t\t\tcontinue;\n\t\t}\n\n\t\t//BEGIN NON-STANDARD - Note that comments are NOT part of json, but people insist on using them anyway (c-style, like javascript).\n\t\telse if (ctx->data[ctx->pos] == '/' && ctx->pos+1 < ctx->size)\n\t\t{\n\t\t\tif (ctx->data[ctx->pos+1] == '/')\n\t\t\t{\t//C++ style single-line comments that continue till the next line break\n\t\t\t\tctx->pos+=2;\n\t\t\t\twhile (ctx->pos < ctx->size)\n\t\t\t\t{\n\t\t\t\t\tif (ctx->data[ctx->pos] == '\\r' || ctx->data[ctx->pos] == '\\n')\n\t\t\t\t\t\tbreak;\t//ends on first line break (the break is then whitespace will will be skipped naturally)\n\t\t\t\t\tctx->pos+=1;\t//not yet\n\t\t\t\t}\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\telse if (ctx->data[ctx->pos+1] == '*')\n\t\t\t{\t/*C style multi-line comment*/\n\t\t\t\tctx->pos+=2;\n\t\t\t\twhile (ctx->pos+1 < ctx->size)\n\t\t\t\t{\n\t\t\t\t\tif (ctx->data[ctx->pos] == '*' && ctx->data[ctx->pos+1] == '/')\n\t\t\t\t\t{\n\t\t\t\t\t\tctx->pos+=2;\t//skip past the terminator ready for whitespace or trailing comments directly after\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tctx->pos+=1;\t//not yet\n\t\t\t\t}\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\t\t//END NON-STANDARD\n\t\tbreak;\t//not whitespace/comment/etc.\n\t}\n}\n//handles the not-null-terminated nature of our bodies.\ndouble JSON_ReadFloat(json_t *t, double fallback)\n{\n\tif (t)\n\t{\n\t\tchar tmp[MAX_QPATH];\n\t\tsize_t l = t->bodyend-t->bodystart;\n\t\tif (l > MAX_QPATH-1)\n\t\t\tl = MAX_QPATH-1;\n\t\tmemcpy(tmp, t->bodystart, l);\n\t\ttmp[l] = 0;\n\t\treturn atof(tmp);\n\t}\n\treturn fallback;\n}\n\n#if defined(FTEPLUGIN) || defined(IQMTOOL)\t//grr, stupid copypasta.\nunsigned int utf8_encode(void *out, unsigned int unicode, int maxlen)\n{\n\tunsigned int bcount = 1;\n\tunsigned int lim = 0x80;\n\tunsigned int shift;\n\tif (!unicode)\n\t{\t//modified utf-8 encodes encapsulated nulls as over-long.\n\t\tbcount = 2;\n\t}\n\telse\n\t{\n\t\twhile (unicode >= lim)\n\t\t{\n\t\t\tif (bcount == 1)\n\t\t\t\tlim <<= 4;\n\t\t\telse if (bcount < 7)\n\t\t\t\tlim <<= 5;\n\t\t\telse\n\t\t\t\tlim <<= 6;\n\t\t\tbcount++;\n\t\t}\n\t}\n\n\t//error if needed\n\tif (maxlen < bcount)\n\t\treturn 0;\n\n\t//output it.\n\tif (bcount == 1)\n\t{\n\t\t*((unsigned char *)out) = (unsigned char)(unicode&0x7f);\n\t\tout = (char*)out + 1;\n\t}\n\telse\n\t{\n\t\tshift = bcount*6;\n\t\tshift = shift-6;\n\t\t*((unsigned char *)out) = (unsigned char)((unicode>>shift)&(0x0000007f>>bcount)) | ((0xffffff00 >> bcount) & 0xff);\n\t\tout = (char*)out + 1;\n\t\tdo\n\t\t{\n\t\t\tshift = shift-6;\n\t\t\t*((unsigned char *)out) = (unsigned char)((unicode>>shift)&0x3f) | 0x80;\n\t\t\tout = (char*)out + 1;\n\t\t}\n\t\twhile(shift);\n\t}\n\treturn bcount;\n}\n#endif\nstatic int dehex(int chr, unsigned int *ret, int shift)\n{\n\tif      (chr >= '0' && chr <= '9')\n\t\t*ret |= (chr-'0') << shift;\n\telse if (chr >= 'A' && chr <= 'F')\n\t\t*ret |= (chr-'A'+10) << shift;\n\telse if (chr >= 'a' && chr <= 'f')\n\t\t*ret |= (chr-'a'+10) << shift;\n\telse\n\t\treturn 0;\n\treturn 1;\n}\n\n//writes the body to a null-terminated string, handling escapes as needed.\n//returns required body length (without terminator) (NOTE: return value is not escape-aware, so this is an over-estimate).\nsize_t JSON_ReadBody(json_t *t, char *out, size_t outsize)\n{\n//\tsize_t bodysize;\n\tif (!t)\n\t{\n\t\tif (out)\n\t\t\t*out = 0;\n\t\treturn 0;\n\t}\n\tif (out && outsize)\n\t{\n\t\tchar *outend = out+outsize-1;\t//compensate for null terminator\n\t\tconst char *in = t->bodystart;\n\t\twhile (in < t->bodyend && out < outend)\n\t\t{\n\t\t\tif (*in == '\\\\')\n\t\t\t{\n\t\t\t\tif (++in < t->bodyend)\n\t\t\t\t{\n\t\t\t\t\tswitch(*in++)\n\t\t\t\t\t{\n\t\t\t\t\tcase '\\\"':\t*out++ = '\\\"'; break;\n\t\t\t\t\tcase '\\\\':\t*out++ = '\\\\'; break;\n\t\t\t\t\tcase '/':\t*out++ =  '/'; break;\t//json is not C...\n\t\t\t\t\tcase 'b':\t*out++ = '\\b'; break;\n\t\t\t\t\tcase 'f':\t*out++ = '\\f'; break;\n\t\t\t\t\tcase 'n':\t*out++ = '\\n'; break;\n\t\t\t\t\tcase 'r':\t*out++ = '\\r'; break;\n\t\t\t\t\tcase 't':\t*out++ = '\\t'; break;\n\t\t\t\t\tcase 'u':\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tunsigned int code = 0, low = 0;\n\t\t\t\t\t\t\tif (dehex(out[0], &code, 12) &&\t//javscript escapes are strictly 16bit...\n\t\t\t\t\t\t\t\tdehex(out[1], &code, 8) &&\n\t\t\t\t\t\t\t\tdehex(out[2], &code, 4) &&\n\t\t\t\t\t\t\t\tdehex(out[3], &code, 0) )\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tin += 4;\n\t\t\t\t\t\t\t\t//and as its actually UTF-16 we need to waste more cpu cycles on this insanity when its a high-surrogate.\n\t\t\t\t\t\t\t\tif (code >= 0xd800u && code < 0xdc00u && out[4] == '\\\\' && out[5] == 'u' &&\n\t\t\t\t\t\t\t\t\tdehex(out[6], &low, 12) &&\n\t\t\t\t\t\t\t\t\tdehex(out[7], &low, 8) &&\n\t\t\t\t\t\t\t\t\tdehex(out[8], &low, 4) &&\n\t\t\t\t\t\t\t\t\tdehex(out[9], &low, 0) && low >= 0xdc00 && low < 0xde00)\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tin += 6;\n\t\t\t\t\t\t\t\t\tcode = 0x10000 + (code-0xd800)*0x400 + (low-0xdc00);\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tout += utf8_encode(out, code, outend-out);\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t//fall through.\n\t\t\t\t\tdefault:\n\t\t\t\t\t\t//unknown escape. will warn when actually reading it.\n\t\t\t\t\t\t*out++ = '\\\\';\n\t\t\t\t\t\tif (out < outend)\n\t\t\t\t\t\t\t*out++ = in[-1];\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\t*out++ = '\\\\';\t//error...\n\t\t\t}\n\t\t\telse\n\t\t\t\t*out++ = *in++;\n\t\t}\n\t\t*out = 0;\n\t}\n\treturn t->bodyend-t->bodystart;\n}\n\nstatic qboolean JSON_ParseString(struct jsonparsectx_s *ctx, char const**start, char const** end)\n{\n\tif (ctx->pos < ctx->size && ctx->data[ctx->pos] == '\\\"')\n\t{\t//quoted string\n\t\t//FIXME: no handling of backslash followed by one of \"\\/bfnrtu\n\t\tctx->pos+=1;\n\t\t*start = ctx->data+ctx->pos;\n\t\twhile (ctx->pos < ctx->size)\n\t\t{\n\t\t\tif (ctx->data[ctx->pos] == '\\\"')\n\t\t\t\tbreak;\n\t\t\tif (ctx->data[ctx->pos] == '\\\\')\n\t\t\t{\t//escapes are expanded elsewhere, we're just skipping over them here.\n\t\t\t\tswitch(ctx->data[ctx->pos+1])\n\t\t\t\t{\n\t\t\t\tcase '\\\"':\n\t\t\t\tcase '\\\\':\n\t\t\t\tcase '/':\n\t\t\t\tcase 'b':\n\t\t\t\tcase 'f':\n\t\t\t\tcase 'n':\n\t\t\t\tcase 'r':\n\t\t\t\tcase 't':\n\t\t\t\t\tctx->pos+=2;\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'u':\n\t\t\t\t\tctx->pos+=2;\n\t\t\t\t\t//*pos+=4; //4 hex digits, not escapes so just wait till later before parsing them properly.\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\t//unknown escape. will warn when actually reading it.\n\t\t\t\t\tctx->pos+=1;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t\tctx->pos+=1;\n\t\t}\n\t\tif (ctx->pos < ctx->size && ctx->data[ctx->pos] == '\\\"')\n\t\t{\n\t\t\t*end = ctx->data+ctx->pos;\n\t\t\tctx->pos+=1;\n\t\t\treturn true;\n\t\t}\n\t}\n\telse\n\t{\t//name\n\t\t*start = ctx->data+ctx->pos;\n\t\twhile (ctx->pos < ctx->size\n\t\t\t&& ctx->data[ctx->pos] != ' '\n\t\t\t&& ctx->data[ctx->pos] != '\\t'\n\t\t\t&& ctx->data[ctx->pos] != '\\r'\n\t\t\t&& ctx->data[ctx->pos] != '\\n'\n\t\t\t&& ctx->data[ctx->pos] != ':'\n\t\t\t&& ctx->data[ctx->pos] != ','\n\t\t\t&& ctx->data[ctx->pos] != '}'\n\t\t\t&& ctx->data[ctx->pos] != '{'\n\t\t\t&& ctx->data[ctx->pos] != '['\n\t\t\t&& ctx->data[ctx->pos] != ']')\n\t\t{\n\t\t\tctx->pos+=1;\n\t\t}\n\t\t*end = ctx->data+ctx->pos;\n\t\tif (*start != *end)\n\t\t\treturn true;\n\t}\n\t*end = *start;\n\treturn false;\n}\njson_t *JSON_ParseNode(json_t *t, const char *namestart, const char *nameend, struct jsonparsectx_s *ctx)\n{\n\tconst char *childstart, *childend;\n\tJSON_SkipWhite(ctx);\n\n\tif (ctx->pos < ctx->size)\n\t{\n\t\tif (ctx->data[ctx->pos] == '{')\n\t\t{\n\t\t\tctx->pos+=1;\n\t\t\tJSON_SkipWhite(ctx);\n\n\t\t\tt = JSON_CreateNode(t, namestart, nameend, NULL, NULL, json_type_object);\n\n\t\t\twhile (ctx->pos < ctx->size && ctx->data[ctx->pos] == '\\\"')\n\t\t\t{\n\t\t\t\tif (!JSON_ParseString(ctx, &childstart, &childend))\n\t\t\t\t\tbreak;\n\t\t\t\tJSON_SkipWhite(ctx);\n\t\t\t\tif (ctx->pos < ctx->size && ctx->data[ctx->pos] == ':')\n\t\t\t\t{\n\t\t\t\t\tctx->pos+=1;\n\t\t\t\t\tif (!JSON_ParseNode(t, childstart, childend, ctx))\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tJSON_SkipWhite(ctx);\n\n\t\t\t\tif (ctx->pos < ctx->size && ctx->data[ctx->pos] == ',')\n\t\t\t\t{\n\t\t\t\t\tctx->pos+=1;\n\t\t\t\t\tJSON_SkipWhite(ctx);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tif (ctx->pos < ctx->size && ctx->data[ctx->pos] == '}')\n\t\t\t{\n\t\t\t\tctx->pos+=1;\n\t\t\t\treturn t;\n\t\t\t}\n\t\t\tJSON_Destroy(t);\n\t\t}\n\t\telse if (ctx->data[ctx->pos] == '[')\n\t\t{\n\t\t\tchar idxname[MAX_QPATH];\n\t\t\tunsigned int idx = 0;\n\t\t\tctx->pos+=1;\n\t\t\tJSON_SkipWhite(ctx);\n\n\t\t\tt = JSON_CreateNode(t, namestart, nameend, NULL, NULL, json_type_array);\n\n\t\t\tfor(;;)\n\t\t\t{\n\t\t\t\tQ_snprintfz(idxname, sizeof(idxname), \"%u\", idx++);\n\t\t\t\tif (!JSON_ParseNode(t, idxname, NULL, ctx))\n\t\t\t\t\tbreak;\n\n\t\t\t\tif (ctx->pos < ctx->size && ctx->data[ctx->pos] == ',')\n\t\t\t\t{\n\t\t\t\t\tctx->pos+=1;\n\t\t\t\t\tJSON_SkipWhite(ctx);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tJSON_SkipWhite(ctx);\n\t\t\tif (ctx->pos < ctx->size && ctx->data[ctx->pos] == ']')\n\t\t\t{\n\t\t\t\tctx->pos+=1;\n\t\t\t\treturn t;\n\t\t\t}\n\t\t\tJSON_Destroy(t);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (ctx->data[ctx->pos] == '\\\"')\n\t\t\t{\n\t\t\t\tif (JSON_ParseString(ctx, &childstart, &childend))\n\t\t\t\t\treturn JSON_CreateNode(t, namestart, nameend, childstart, childend, json_type_string);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (JSON_ParseString(ctx, &childstart, &childend))\n\t\t\t\t{\n\t\t\t\t\tif (childend-childstart == 4 && !Q_strncasecmp(childstart, \"true\", 4))\n\t\t\t\t\t\treturn JSON_CreateNode(t, namestart, nameend, childstart, childend, json_type_true);\n\t\t\t\t\telse if (childend-childstart == 5 && !Q_strncasecmp(childstart, \"false\", 5))\n\t\t\t\t\t\treturn JSON_CreateNode(t, namestart, nameend, childstart, childend, json_type_false);\n\t\t\t\t\telse if (childend-childstart == 4 && !Q_strncasecmp(childstart, \"null\", 4))\n\t\t\t\t\t\treturn JSON_CreateNode(t, namestart, nameend, childstart, childend, json_type_null);\n\t\t\t\t\telse\n\t\t\t\t\t\treturn JSON_CreateNode(t, namestart, nameend, childstart, childend, json_type_number);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn NULL;\n}\njson_t *JSON_Parse(const char *json)\n{\n\tstruct jsonparsectx_s ctx =\n\t{\n\t\tjson,\n\t\tstrlen(json),\n\t\t(json[0] == '\\xef' && json[1] == '\\xbb' && json[2] == '\\xbf')?3:0,\t//skip a utf-8 bom, if present, to be a bit more permissive.\n\t};\n\tjson_t *n = JSON_ParseNode(NULL, NULL, NULL, &ctx);\n\tJSON_SkipWhite(&ctx);\n\tif (ctx.pos == ctx.size)\n\t\treturn n;\n\tJSON_Destroy(n);\t//trailing junk?... fail it.\n\treturn NULL;\n}\n\n\n\n//we don't really understand arrays here (we just treat them as tables) so eg \"foo.0.bar\" to find t->foo[0]->bar\njson_t *JSON_FindChild(json_t *t, const char *child)\n{\n\tif (t)\n\t{\n\t\tsize_t nl;\n\t\tconst char *dot = strchr(child, '.');\n\t\tif (dot)\n\t\t\tnl = dot-child;\n\t\telse\n\t\t\tnl = strlen(child);\n\t\tif (t->arraymax)\n\t\t{\n\t\t\tsize_t idx = atoi(child);\n\t\t\tif (idx < t->arraymax)\n\t\t\t{\n\t\t\t\tt = t->array[idx];\n\t\t\t\tif (t)\n\t\t\t\t\tgoto found;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tfor (t = t->child; t; t = t->sibling)\n\t\t\t{\n\t\t\t\tif (!strncmp(t->name, child, nl) && (t->name[nl] == '.' || !t->name[nl]))\n\t\t\t\t{\nfound:\n\t\t\t\t\tchild+=nl;\n\t\t\t\t\tt->used = true;\n\t\t\t\t\tif (*child == '.')\n\t\t\t\t\t\treturn JSON_FindChild(t, child+1);\n\t\t\t\t\treturn t;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn NULL;\n}\njson_t *JSON_GetIndexed(json_t *t, unsigned int idx)\n{\n\tif (t)\n\t{\n\t\tif (t->arraymax)\n\t\t{\n\t\t\tif (idx < t->arraymax)\n\t\t\t{\n\t\t\t\tt = t->array[idx];\n\t\t\t\tif (t)\n\t\t\t\t{\n\t\t\t\t\tt->used = true;\n\t\t\t\t\treturn t;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tfor (t = t->child; t; t = t->sibling, idx--)\n\t\t\t{\n\t\t\t\tif (!idx)\n\t\t\t\t{\n\t\t\t\t\tt->used = true;\n\t\t\t\t\treturn t;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn NULL;\n}\n\nsize_t JSON_GetCount(json_t *t)\n{\t//can't cope with deletions.\n\tsize_t count = 0;\n\twhile (JSON_GetIndexed(t, count))\n\t\tcount++;\n\treturn count;\n}\n\n\n//helpers...\njson_t *JSON_FindIndexedChild(json_t *t, const char *child, unsigned int idx)\n{\n\tif (child)\n\t\tt = JSON_FindChild(t, child);\n\treturn JSON_GetIndexed(t, idx);\n}\nqboolean JSON_Equals(json_t *t, const char *child, const char *expected)\n{\n\tif (child)\n\t\tt = JSON_FindChild(t, child);\n\tif (t && t->bodyend-t->bodystart == strlen(expected))\n\t\treturn !strncmp(t->bodystart, expected, t->bodyend-t->bodystart);\n\treturn false;\n}\nquintptr_t JSON_GetUInteger(json_t *t, const char *child, unsigned int fallback)\n{\n\tif (child)\n\t\tt = JSON_FindChild(t, child);\n\tif (t)\n\t{\t//copy it to another buffer. can probably skip that tbh.\n\t\tchar tmp[MAX_QPATH];\n\t\tchar *trail;\n\t\tsize_t l = t->bodyend-t->bodystart;\n\t\tquintptr_t r;\n\t\tif (l > MAX_QPATH-1)\n\t\t\tl = MAX_QPATH-1;\n\t\tmemcpy(tmp, t->bodystart, l);\n\t\ttmp[l] = 0;\n\t\tif (!strcmp(tmp, \"false\"))\t//special cases, for booleans\n\t\t\treturn 0u;\n\t\tif (!strcmp(tmp, \"true\"))\t//special cases, for booleans\n\t\t\treturn 1u;\n\t\tr = (quintptr_t)strtoull(tmp, &trail, 0);\n\t\tif (!*trail)\n\t\t\treturn r;\n\t}\n\treturn fallback;\n}\nqintptr_t JSON_GetInteger(json_t *t, const char *child, int fallback)\n{\n\tif (child)\n\t\tt = JSON_FindChild(t, child);\n\tif (t)\n\t{\t//copy it to another buffer. can probably skip that tbh.\n\t\tchar tmp[MAX_QPATH];\n\t\tchar *trail;\n\t\tsize_t l = t->bodyend-t->bodystart;\n\t\tqintptr_t r;\n\t\tif (l > MAX_QPATH-1)\n\t\t\tl = MAX_QPATH-1;\n\t\tmemcpy(tmp, t->bodystart, l);\n\t\ttmp[l] = 0;\n\t\tif (!strcmp(tmp, \"false\"))\t//special cases, for booleans\n\t\t\treturn 0;\n\t\tif (!strcmp(tmp, \"true\"))\t//special cases, for booleans\n\t\t\treturn 1;\n\t\tr = (qintptr_t)strtoll(tmp, &trail, 0);\n\t\tif (!*trail)\n\t\t\treturn r;\n\t}\n\treturn fallback;\n}\nqintptr_t JSON_GetIndexedInteger(json_t *t, unsigned int idx, int fallback)\n{\n\tchar idxname[MAX_QPATH];\n\tQ_snprintfz(idxname, sizeof(idxname), \"%u\", idx);\n\treturn JSON_GetInteger(t, idxname, fallback);\n}\ndouble JSON_GetFloat(json_t *t, const char *child, double fallback)\n{\n\tif (child)\n\t\tt = JSON_FindChild(t, child);\n\treturn JSON_ReadFloat(t, fallback);\n}\ndouble JSON_GetIndexedFloat(json_t *t, unsigned int idx, double fallback)\n{\n\tchar idxname[MAX_QPATH];\n\tQ_snprintfz(idxname, sizeof(idxname), \"%u\", idx);\n\treturn JSON_GetFloat(t, idxname, fallback);\n}\nconst char *JSON_GetString(json_t *t, const char *child, char *buffer, size_t buffersize, const char *fallback)\n{\n\tif (child)\n\t\tt = JSON_FindChild(t, child);\n\tif (t)\n\t{\t//copy it to another buffer. can probably skip that tbh.\n\t\tJSON_ReadBody(t, buffer, buffersize);\n\t\treturn buffer;\n\t}\n\treturn fallback;\n}\n"
  },
  {
    "path": "engine/common/log.c",
    "content": "// log.c: handles console logging functions and cvars\n\n#include \"quakedef.h\"\n\n// cvar callbacks\nstatic void QDECL Log_Dir_Callback (struct cvar_s *var, char *oldvalue);\nstatic void QDECL Log_Name_Callback (struct cvar_s *var, char *oldvalue);\n\n// cvars\n#define CONLOGGROUP \"Console logging\"\ncvar_t\t\tlog_enable[LOG_TYPES]\t= {\tCVARF(\"log_enable\", \"0\", CVAR_NOTFROMSERVER),\n\t\t\t\t\t\t\t\t\t\tCVARF(\"log_enable_players\", \"0\", CVAR_NOTFROMSERVER),\n\t\t\t\t\t\t\t\t\t\tCVARF(\"log_enable_rcon\", \"1\", CVAR_NOTFROMSERVER)\n\t\t\t\t\t\t\t\t};\ncvar_t\t\tlog_name[LOG_TYPES] = { CVARFC(\"log_name\", \"qconsole\", CVAR_NOTFROMSERVER, Log_Name_Callback),\n\t\t\t\t\t\t\t\t\tCVARFC(\"log_name_players\", \"players\", CVAR_NOTFROMSERVER, Log_Name_Callback),\n\t\t\t\t\t\t\t\t\tCVARFC(\"log_name_rcon\", \"rcon\", CVAR_NOTFROMSERVER, Log_Name_Callback)};\ncvar_t\t\tlog_dir_var = CVARAFC(\"log_dir\", \"\", \"sv_logdir\", CVAR_NOTFROMSERVER, Log_Dir_Callback);\ncvar_t\t\tlog_readable = CVARFD(\"log_readable\", \"7\", CVAR_NOTFROMSERVER, \"Bitfield describing what to convert/strip. If 0, exact byte representation will be used.\\n&1: Dequakify text.\\n&2: Strip special markup.\\n&4: Strip ansi control codes.\");\ncvar_t\t\tlog_developer = CVARFD(\"log_developer\", \"0\", CVAR_NOTFROMSERVER, \"Enables logging of console prints when set to 1. Otherwise unimportant messages will not fill up your log files.\");\ncvar_t\t\tlog_rotate_files = CVARF(\"log_rotate_files\", \"0\", CVAR_NOTFROMSERVER);\ncvar_t\t\tlog_rotate_size = CVARF(\"log_rotate_size\", \"131072\", CVAR_NOTFROMSERVER);\ncvar_t\t\tlog_timestamps = CVARF(\"log_timestamps\", \"1\", CVAR_NOTFROMSERVER);\n#ifdef _WIN32\ncvar_t\t\tlog_dosformat = CVARF(\"log_dosformat\", \"1\", CVAR_NOTFROMSERVER);\n#else\ncvar_t\t\tlog_dosformat = CVARF(\"log_dosformat\", \"0\", CVAR_NOTFROMSERVER);\n#endif\nqboolean\tlog_newline[LOG_TYPES];\n\n#ifdef IPLOG\ncvar_t\t\tiplog_autodump = CVARFD(\"ipautodump\", \"0\", CVAR_ARCHIVE|CVAR_NOTFROMSERVER, \"Enables dumping the 'iplog.txt' file, which contains a log of usernames seen for a given IP, which is useful for detecting fake-nicks.\");\n#endif\n\nstatic char log_dir[MAX_OSPATH];\nstatic enum fs_relative log_root = FS_GAMEONLY;\n\n\n// Log_Dir_Callback: called when a log_dir is changed\nstatic void QDECL Log_Dir_Callback (struct cvar_s *var, char *oldvalue)\n{\n\tchar *t = var->string;\n\tchar *e = t + (*t?strlen(t):0);\n\n\t// sanity check for directory. // is equivelent to /../ on some systems, so make sure that can't be used either. : is for drives on windows or amiga, or alternative thingies on windows, so block thoses completely.\n\tif (strstr(t, \"..\") || strstr(t, \":\") || *t == '/' || *t == '\\\\' || *e == '/' || *e == '\\\\' || strstr(t, \"//\") || strstr(t, \"\\\\\\\\\"))\n\t{\n\t\tCon_Printf(CON_NOTICE \"%s forced to default due to invalid characters.\\n\", var->name);\n\t\t// recursion is avoided by assuming the default value is sane\n\t\tCvar_ForceSet(var, var->enginevalue);\n\t}\n\n\tif (!strncmp(var->string, \"./\", 2)||!strncmp(var->string, \".\\\\\", 2))\n\t{\n\t\tstrcpy(log_dir, var->string+2);\n\t\tlog_root = FS_ROOT;\n\t}\n\telse\n\t{\n\t\tstrcpy(log_dir, var->string);\n\t\tlog_root = FS_GAMEONLY;\n\t}\n}\n\n// Log_Name_Callback: called when a log_name is changed\nstatic void QDECL Log_Name_Callback (struct cvar_s *var, char *oldvalue)\n{\n\tchar *t = var->string;\n\n\t// sanity check for directory\n\tif (strstr(t, \"..\") || strstr(t, \":\") || strstr(t, \"/\") || strstr(t, \"\\\\\"))\n\t{\n\t\tCon_Printf(CON_NOTICE \"%s forced to default due to invalid characters.\\n\", var->name);\n\t\t// recursion is avoided by assuming the default value is sane\n\t\tCvar_ForceSet(var, var->enginevalue);\n\t}\n}\n\n// Con_Log: log string to console log\nvoid Log_String (logtype_t lognum, const char *s)\n{\n\tvfsfile_t *fi;\n\tchar *f; // filename\n\tchar *t;\n\tchar utf8[2048];\n\tint i;\n\tchar fbase[MAX_QPATH];\n\tchar fname[MAX_QPATH];\n\tconchar_t cline[2048], *c;\n\tunsigned int u, flags;\n\n\tif (!log_enable[lognum].value)\n\t\treturn;\n\n\tif (log_name[lognum].string[0])\n\t\tf = log_name[lognum].string;\n\telse\n\t\tf = log_name[lognum].enginevalue;\n\n\tif (!f)\n\t\treturn;\n\n\tCOM_ParseFunString(CON_WHITEMASK, s, cline, sizeof(cline), !(log_readable.ival & 2));\n\tt = utf8;\n\tfor (c = cline; *c; )\n\t{\n\t\tc = Font_Decode(c, &flags, &u);\n\t\tif ((flags & CON_HIDDEN) && (log_readable.ival & 2))\n\t\t\tcontinue;\n\t\tif (log_readable.ival&1)\n\t\t\tu = COM_DeQuake(u);\n\n\t\t//at the start of a new line, we might want a timestamp (so timestamps are correct for the first char of the line, instead of the preceeding \\n)\n\t\tif (log_newline[lognum])\n\t\t{\n\t\t\tif (log_timestamps.ival)\n\t\t\t{\n\t\t\t\ttime_t unixtime = time(NULL);\n\t\t\t\tint bufferspace = utf8+sizeof(utf8)-1-t;\n\t\t\t\tif (bufferspace > 0)\n\t\t\t\t{\n\t\t\t\t\tstrftime(t, bufferspace, \"%Y-%m-%d %H:%M:%S \", localtime(&unixtime));\n\t\t\t\t\tt += strlen(t);\n\t\t\t\t}\n\t\t\t}\n\t\t\tlog_newline[lognum] = false;\n\t\t}\n\n\t\t//make sure control codes are stripped. no exploiting xterm bugs please.\n\t\tif ((log_readable.ival & 4) && ((u < 32 && u != '\\t' && u != '\\n') || u == 127 || (u >= 128 && u < 128+32))) //\\r is stripped too\n\t\t\tu = '?';\n\t\t//if dos format logs, we insert a \\r before every \\n (also flag next char as the start of a new line)\n\t\tif (u == '\\n')\n\t\t{\n\t\t\tlog_newline[lognum] = true;\n\t\t\tif (log_dosformat.ival)\n\t\t\t\tt += utf8_encode(t, '\\r', utf8+sizeof(utf8)-1-t);\n\t\t}\n\t\tt += utf8_encode(t, u, utf8+sizeof(utf8)-1-t);\n\t}\n\t*t = 0;\n\n\tif (*log_dir)\n\t\tQ_snprintfz(fbase, sizeof(fname)-4, \"%s/%s\", log_dir, f);\n\telse\n\t\tQ_snprintfz(fbase, sizeof(fname)-4, \"%s\", f);\n\tQ_snprintfz(fname, sizeof(fname), \"%s.log\", fbase);\n\n\t// file rotation\n\tif (log_rotate_size.value >= 4096 && log_rotate_files.value >= 1)\n\t{\n\t\tint x;\n\t\tvfsfile_t *fi;\n\n\t\t// check file size, use x as temp\n\t\tif ((fi = FS_OpenVFS(fname, \"rb\", log_root)))\n\t\t{\n\t\t\tx = VFS_GETLEN(fi);\n\t\t\tVFS_CLOSE(fi);\n\t\t\tx += strlen(utf8); // add string size to file size to never go over\n\t\t}\n\t\telse\n\t\t\tx = 0;\n\n\t\tif (x > (int)log_rotate_size.value)\n\t\t{\n\t\t\tchar newf[MAX_QPATH];\n\t\t\tchar oldf[MAX_QPATH];\n\n\t\t\ti = log_rotate_files.value;\n\n\t\t\t// unlink file at the top of the chain\n\t\t\tQ_snprintfz(oldf, sizeof(oldf), \"%s.%i.log\", fbase, i);\n\t\t\tFS_Remove(oldf, log_root);\n\n\t\t\t// rename files through chain\n\t\t\tfor (x = i-1; x > 0; x--)\n\t\t\t{\n\t\t\t\tstrcpy(newf, oldf);\n\t\t\t\tQ_snprintfz(oldf, sizeof(oldf), \"%s.%i.log\", fbase, x);\n\n\t\t\t\t// check if file exists, otherwise skip\n\t\t\t\tif ((fi = FS_OpenVFS(oldf, \"rb\", log_root)))\n\t\t\t\t\tVFS_CLOSE(fi);\n\t\t\t\telse\n\t\t\t\t\tcontinue; // skip nonexistant files\n\n\t\t\t\tif (!FS_Rename(oldf, newf, log_root))\n\t\t\t\t{\n\t\t\t\t\t// rename failed, disable log and bug out\n\t\t\t\t\tCvar_ForceSet(&log_enable[lognum], \"0\");\n\t\t\t\t\tCon_Printf(\"Unable to rotate log files. Logging disabled.\\n\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// TODO: option to compress file somewhere in here?\n\t\t\t// rename our base file, which had better exist...\n\t\t\tif (!FS_Rename(fname, oldf, log_root))\n\t\t\t{\n\t\t\t\t// rename failed, disable log and bug out\n\t\t\t\tCvar_ForceSet(&log_enable[lognum], \"0\");\n\t\t\t\tCon_Printf(\"Unable to rename base log file. Logging disabled.\\n\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n\n\tFS_CreatePath(fname, log_root);\n\tif ((fi = FS_OpenVFS(fname, \"ab\", log_root)))\n\t{\n\t\tVFS_WRITE(fi, utf8, strlen(utf8));\n\t\tVFS_CLOSE(fi);\n\t}\n\telse\n\t{\n\t\t// write failed, bug out\n\t\tCvar_ForceSet(&log_enable[lognum], \"0\");\n\t\tCon_Printf(\"Unable to write to log file. Logging disabled.\\n\");\n\t\treturn;\n\t}\n}\n\nvoid Con_Log (const char *s)\n{\n\tLog_String(LOG_CONSOLE, s);\n}\n\n\n#ifdef HAVE_SERVER\n//still to add stuff at:\n//connects\n//disconnects\n//kicked\nvoid SV_LogPlayer(client_t *cl, char *msg)\n{\n\tchar line[2048];\n\tchar remote_adr[MAX_ADR_SIZE];\n\tchar realip_adr[MAX_ADR_SIZE];\n\n\tif (cl->protocol == SCP_BAD)\n\t\treturn;\t//don't log botclients\n\n\tQ_snprintfz(line, sizeof(line)-1,\n\t\t\t\"%s\\\\%s\\\\%i\\\\%s\\\\%s\\\\%i\\\\guid\\\\%s\",\n\t\t\tmsg, cl->name, cl->userid,\n\t\t\tNET_BaseAdrToString(remote_adr, sizeof(remote_adr), &cl->netchan.remote_address), (cl->realip_status > 0 ? NET_BaseAdrToString(realip_adr, sizeof(realip_adr), &cl->realip) : \"??\"),\n\t\t\tcl->netchan.remote_address.port, cl->guid);\n\tInfoBuf_ToString(&cl->userinfo, line+strlen(line), sizeof(line)-1-strlen(line), NULL, NULL, NULL, NULL, NULL);\n\tQ_strncatz(line, \"\\n\", sizeof(line));\n\n\tLog_String(LOG_PLAYER, line);\n}\n#endif\n\n\n#ifdef HAVE_LEGACY\nstatic struct {\n\tconst char *commandname;\n\tconst char *desc;\n} legacylog[] =\n{\n\t{\"logfile\",\t\t\"\"},\n\t{\"logplayers\",\t\" players\"},\n\t{\"logrcon\",\t\t\" frags\"},\n};\n\nstatic void Log_Logfile_f (void)\n{\t//these legacy commands are just toggles. not args (other than the commandname to know which log type to toggle)\n\tsize_t logtype;\n\tconst char *cmd = Cmd_Argv(0);\n\tfor (logtype = 0; logtype < countof(legacylog); logtype++)\n\t\tif (!Q_strcasecmp(legacylog[logtype].commandname, cmd))\n\t\t\tbreak;\n\n\tif (log_enable[logtype].value)\n\t{\n\t\tCvar_SetValue(&log_enable[logtype], 0);\n\t\tCon_Printf(\"Logging%s disabled.\\n\", legacylog[logtype].desc);\n\t}\n\telse\n\t{\n\t\tconst char *f;\n\t\tchar syspath[MAX_OSPATH];\n\n\t\tif (log_name[logtype].string[0])\n\t\t\tf = log_name[logtype].string;\n\t\telse\n\t\t\tf = log_name[logtype].enginevalue;\n\n\t\tif (*log_dir)\n\t\t\tf = va(\"%s/%s.log\", log_dir, f);\n\t\telse\n\t\t\tf = va(\"%s.log\", f);\n\n\t\tif (FS_DisplayPath(f, log_root, syspath, sizeof(syspath)))\n\t\t\tCon_Printf(\"%s\", va(\"Logging%s to %s\\n\", legacylog[logtype].desc, syspath));\n\t\telse\n\t\t\tCon_Printf(\"%s\", va(\"Logging%s to %s\\n\", legacylog[logtype].desc, f));\n\t\tCvar_SetValue(&log_enable[logtype], 1);\n\t}\n\n}\n#endif\n\n#ifdef IPLOG\n/*for fuck sake, why can people still not write simple files. proquake is writing binary files as text ones. this function is to try to deal with that fuckup*/\nstatic size_t IPLog_Read_Fucked(qbyte *file, size_t *offset, size_t totalsize, qbyte *out, size_t outsize)\n{\n\tsize_t read = 0;\n\twhile (outsize-- > 0 && *offset < totalsize)\n\t{\n\t\tif (file[*offset] == '\\r' && *offset+1 < totalsize && file[*offset+1] == '\\n')\n\t\t{\n\t\t\tout[read] = '\\n';\n\t\t\t*offset += 2;\n\t\t\tread += 1;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tout[read] = file[*offset];\n\t\t\t*offset += 1;\n\t\t\tread += 1;\n\t\t}\n\t}\n\treturn read;\n}\n/*need to make sure any 13 bytes are followed by 10s so that we don't bug out when read back in *sigh* */\nstatic size_t IPLog_Write_Fucked(vfsfile_t *file, qbyte *out, size_t outsize)\n{\n\tqbyte tmp[64];\n\tsize_t write = 0;\n\tsize_t block = 0;\n\twhile (outsize-- > 0)\n\t{\n\t\tif (block >= sizeof(tmp)-4)\n\t\t{\n\t\t\tVFS_WRITE(file, tmp, block);\n\t\t\twrite += block;\n\t\t\tblock = 0;\n\t\t}\n\t\tif (*out == '\\n')\n\t\t\ttmp[block++] = '\\r';\n\t\ttmp[block++] = *out++;\n\t}\n\tif (block)\n\t{\n\t\tVFS_WRITE(file, tmp, block);\n\t\twrite += block;\n\t}\n\treturn write;\n}\nqboolean IPLog_Merge_File(const char *fname)\n{\n\tchar ip[MAX_ADR_SIZE];\n\tchar name[256];\n\tchar line[1024];\n\tvfsfile_t *f;\n\tif (!*fname)\n\t\tfname = \"iplog.txt\";\n\tf = FS_OpenVFS(fname, \"rb\", FS_PUBBASEGAMEONLY);\n\tif (!f)\n\t\tf = FS_OpenVFS(fname, \"rb\", FS_GAME);\n\tif (!f)\n\t\treturn false;\n\tif (!Q_strcasecmp(COM_FileExtension(fname, name, sizeof(name)), \"dat\"))\n\t{\t//we don't write this format because of it being limited to ipv4, as well as player name lengths\n\t\tsize_t l = VFS_GETLEN(f), offset = 0;\n\t\tqbyte *ffs = malloc(l+1);\n\t\tVFS_READ(f, ffs, l);\n\t\tffs[l] = 0;\n\t\twhile (IPLog_Read_Fucked(ffs, &offset, l, line, 20) == 20)\n\t\t{\t//yes, these addresses are weird.\n\t\t\tQ_snprintfz(ip, sizeof(ip), \"%i.%i.%i.xxx\", (qbyte)line[2], (qbyte)line[1], (qbyte)line[0]);\n\t\t\tmemcpy(name, line+4, 20-4);\n\t\t\tname[20-4] = 0;\n\t\t\tIPLog_Add(ip, name);\n\t\t}\n\t\tfree(ffs);\n\t}\n\telse\n\t{\n\t\twhile (VFS_GETS(f, line, sizeof(line)-1))\n\t\t{\n\t\t\t//whether the name contains quotes or what is an awkward one.\n\t\t\t//we always write quotes (including string markup to avoid issues)\n\t\t\t//dp doesn't, and our parser is lazy, so its possible we'll get gibberish that way\n\t\t\tif (COM_ParseOut(COM_ParseOut(line, ip, sizeof(ip)), name, sizeof(name)))\n\t\t\t\tIPLog_Add(ip, name);\n\t\t}\n\t}\n\tVFS_CLOSE(f);\n\treturn true;\n}\nstruct iplog_entry\n{\n\tnetadr_t adr;\n\tnetadr_t mask;\n\tchar name[1];\n} **iplog_entries;\nsize_t iplog_num, iplog_max;\nvoid IPLog_Add(const char *ipstr, const char *name)\n{\n\tsize_t i;\n\tnetadr_t a, m;\n\twhile (*ipstr == ' ' || *ipstr == '\\t')\n\t\tipstr++;\n\tif (*ipstr != '[' && *ipstr < '0' && *ipstr > '9')\n\t\treturn;\n\tif (*ipstr == '[')\n\t\tipstr++;\n\t//some names are dodgy.\n\tif (!*name \n\t\t//|| !Q_strcasecmp(name, /*nq default*/\"player\") || !Q_strcasecmp(name, /*qw default*/\"unnamed\")\n\t\t|| !strcmp(name, /*nq fallback*/\"unconnected\") || !strncmp(name, \"BOT:\", 4))\n\t\treturn;\n\tmemset(&a, 0, sizeof(a));\n\tmemset(&m, 0, sizeof(m));\n\tif (!NET_StringToAdrMasked(ipstr, false, &a, &m))\n\t\treturn;\n\t//might be x.y.z.w:port\n\t//might be x.y.z.FUCKED\n\t//might be x.y.z.0/24\n\t//might be [::]:port\n\t//might be [::]/bits\n\t//or other ways to express an ip address\n\n\t//FIXME: ignore private addresses?\n\n\t//check for dupes\n\tfor (i = 0; i < iplog_num; i++)\n\t{\n\t\tif (!memcmp(&a, &iplog_entries[i]->adr, sizeof(netadr_t)) && !memcmp(&m, &iplog_entries[i]->mask, sizeof(netadr_t)) && !Q_strcasecmp(name, iplog_entries[i]->name))\n\t\t\treturn;\n\t}\n\n\t//looks like its new...\n\tif (iplog_num == iplog_max)\n\t\tZ_ReallocElements((void**)&iplog_entries, &iplog_max, iplog_max+64, sizeof(*iplog_entries));\n\tiplog_entries[iplog_num] = BZ_Malloc(sizeof(struct iplog_entry) + strlen(name));\n\tiplog_entries[iplog_num]->adr = a;\n\tiplog_entries[iplog_num]->mask = m;\n\tstrcpy(iplog_entries[iplog_num]->name, name);\n\tiplog_num++;\n}\nstatic void IPLog_Identify(netadr_t *adr, netadr_t *mask, char *fmt, ...)\n{\n\tva_list\t\targptr;\n\n\tqboolean found = false;\n\tchar line[256];\n\tsize_t i;\n\t\t\n\tva_start(argptr, fmt);\n\tvsnprintf(line, sizeof(line), fmt, argptr);\n\tva_end(argptr);\n\tCon_Printf(\"%s: \", line);\n\n\tfor (i = 0; i < iplog_num; i++)\n\t{\n\t\tif (NET_CompareAdrMasked(adr, &iplog_entries[i]->adr, mask?mask:&iplog_entries[i]->mask))\n\t\t{\n\t\t\tif (found)\n\t\t\t\tCon_Printf(\", \");\n\t\t\tfound=true;\n\t\t\tCon_Printf(\"%s\", iplog_entries[i]->name);\n\t\t}\n\t}\n\tif (!found)\n\t\tCon_Printf(\"<no matches>\");\n\tCon_Printf(\"\\n\");\n}\n#include \"cl_ignore.h\"\nstatic void IPLog_Identify_f(void)\n{\n\tconst char *nameorip = Cmd_Argv(1);\n\tnetadr_t adr, mask;\n\tchar clean[256];\n\tchar *endofnum;\n\tstrtoul(nameorip, &endofnum, 10);\n\n\tif (*endofnum && NET_StringToAdrMasked (nameorip, false, &adr, &mask))\n\t{\t//if not a single number, try to parse as an ip\n\t\t//treading carefully here, to avoid dns name lookups weirding everything out.\n\t\tIPLog_Identify(&adr, &mask, \"Identity of %s\", NET_AdrToStringMasked(clean, sizeof(clean), &adr, &mask));\n\t}\n#ifdef HAVE_SERVER\n\telse if (sv.active)\n\t{\t//if server is active, walk players to see if there's a name match to get their address and guess an address mask\n\t\tclient_t *cl;\n\t\tint clnum = -1;\n\t\twhile((cl = SV_GetClientForString(nameorip, &clnum)))\n\t\t{\n\t\t\tif (cl->realip_status)\n\t\t\t{\n\t\t\t\tIPLog_Identify(&cl->realip, NULL, \"Identity of %s (real) [%s]\", cl->name, NET_AdrToString(clean, sizeof(clean), &cl->realip));\n\t\t\t\tIPLog_Identify(&cl->netchan.remote_address, NULL, \"Identity of %s (proxy) [%s]\", cl->name, NET_AdrToString(clean, sizeof(clean), &cl->realip));\n\t\t\t}\n\t\t\telse\n\t\t\t\tIPLog_Identify(&cl->netchan.remote_address, NULL, \"Identity of %s [%s]\", cl->name, NET_AdrToString(clean, sizeof(clean), &cl->realip));\n\t\t}\n\t}\n#endif\n#ifdef HAVE_CLIENT\n\telse if (cls.state >= ca_connected)\n\t{\t//else if client is active, walk players to see if there's a name match, to get their address+mask if known via nq hacks\n\t\tint slot;\n\t\tnetadr_t adr;\n\t\tif ((slot = Player_StringtoSlot(nameorip)) < 0)\n\t\t\tCon_Printf(\"%s: no player with userid %s\\n\", Cmd_Argv(0), nameorip);\n\t\telse if (!*cl.players[slot].ip)\n\t\t\tCon_Printf(\"%s: ip address of %s is not known\\n\", Cmd_Argv(0), cl.players[slot].name);\n\t\telse\n\t\t{\n\t\t\tif (NET_StringToAdrMasked(cl.players[slot].ip, false, &adr, &mask))\n\t\t\t\tIPLog_Identify(&adr, &mask, \"Identity of %s [%s]\", cl.players[slot].name, cl.players[slot].ip);\n\t\t\telse\n\t\t\t\tCon_Printf(\"ip address of %s not known, cannot identify\\n\", cl.players[slot].name);\n\t\t}\n\t}\n#endif\n\telse\n\t\tCon_Printf(\"%s: not connected, nor raw address\\n\", Cmd_Argv(0));\n}\nstatic int IPLog_Dump(const char *fname)\n{\n\tsize_t i;\n\tvfsfile_t *f;\n\tqbyte line[20];\n\tif (!*fname)\n\t\tfname = \"iplog.txt\";\n\n\tif (!iplog_num && !COM_FCheckExists(fname))\n\t\treturn 2;\t//no entries, nothing to overwrite\n\n\tf = FS_OpenVFS(fname, \"wb\", FS_PUBBASEGAMEONLY);\n\tif (!f)\n\t\treturn false;\n\tif (!Q_strcasecmp(COM_FileExtension(fname, line, sizeof(line)), \"dat\"))\n\t{\n\t\tfor (i = 0; i < iplog_num; i++)\n\t\t{\n\t\t\t//this shitty format supports only ipv4.\n\t\t\tif (iplog_entries[i]->adr.type != NA_IP)\n\t\t\t\tcontinue;\n\t\t\tline[0] = iplog_entries[i]->adr.address.ip[2];\n\t\t\tline[1] = iplog_entries[i]->adr.address.ip[1];\n\t\t\tline[2] = iplog_entries[i]->adr.address.ip[0];\n\t\t\tline[3] = 0;\n\t\t\tstrncpy(line+4, iplog_entries[i]->name, sizeof(line)-4);\n\t\t\tIPLog_Write_Fucked(f, line, sizeof(line));\t//convert \\n to \\r\\n, to avoid fucking up any formatting with binary data (inside the address part, so *.13.10.* won't corrupt the file)\n\t\t}\n\t}\n\telse\n\t{\n\t\tVFS_PRINTF(f, \"//generated by \"FULLENGINENAME\"\\n\");\n\t\tfor (i = 0; i < iplog_num; i++)\n\t\t{\n\t\t\tchar ip[512];\n\t\t\tchar buf[1024];\n\t\t\tchar buf2[1024];\n\t\t\tVFS_PRINTF(f, log_dosformat.value?\"%s %s\\r\\n\":\"%s %s\\n\", COM_QuotedString(NET_AdrToStringMasked(ip, sizeof(ip), &iplog_entries[i]->adr, &iplog_entries[i]->mask), buf2, sizeof(buf2), false), COM_QuotedString(iplog_entries[i]->name, buf, sizeof(buf), false));\n\t\t}\n\t}\n\tVFS_CLOSE(f);\n\treturn true;\n}\nstatic void IPLog_Dump_f(void)\n{\n\tchar displaypath[MAX_OSPATH];\n\tconst char *fname = Cmd_Argv(1);\n\tif (FS_DisplayPath(fname, FS_GAMEONLY, displaypath, sizeof(displaypath)))\n\t\tQ_strncpyz(displaypath, fname, sizeof(displaypath));\n\tIPLog_Merge_File(fname);\t//merge from the existing file, so that we're hopefully more robust if multiple processes are poking the same file.\n\tswitch (IPLog_Dump(fname))\n\t{\n\tcase 0:\n\t\tCon_Printf(\"unable to write %s\\n\", fname);\n\t\tbreak;\n\tdefault:\n\tcase 1:\n\t\tCon_Printf(\"wrote %s\\n\", displaypath);\n\t\tbreak;\n\tcase 2:\n\t\tCon_Printf(\"nothing to write\\n\");\n\t\tbreak;\n\t}\n}\nstatic void IPLog_Merge_f(void)\n{\n\tconst char *fname = Cmd_Argv(1);\n\tif (!IPLog_Merge_File(fname))\n\t\tCon_Printf(\"unable to read iplog \\\"%s\\\" for merging\\n\", fname);\n}\n#endif\n\n#if defined(HAVE_DTLS) && defined(HAVE_CLIENT)\t//requires UI prompts\nstruct certlog_s\n{\n\tlink_t l;\n\tchar *hostname;\n\tqboolean trusted;\t//when true, the user has given explicit trust\n\t\t\t\t\t\t//when false we buldozed straight through and will only complain when it changes (legacy mode).\n\tsize_t certsize;\n\tqbyte cert[1];\n};\n#define CERTLOG_FILENAME \"knowncerts.txt\"\nstatic link_t certlog;\nstatic qboolean certlog_inited = false;\nstatic void CertLog_Import(const char *filename);\nstatic struct certlog_s *CertLog_Find(const char *hostname)\n{\n\tstruct certlog_s *l;\n\tfor (l = (struct certlog_s*)certlog.next ; l != (struct certlog_s*)&certlog ; l = (struct certlog_s*)l->l.next)\n\t{\n\t\tif (!strcmp(l->hostname, hostname))\n\t\t\treturn l;\n\t}\n\treturn NULL;\n}\nstatic void CertLog_Update(const char *hostname, const void *cert, size_t certsize, qboolean trusted)\n{\n\tstruct certlog_s *l = CertLog_Find(hostname);\n\tif (l)\n\t{\n\t\tRemoveLink(&l->l);\n\t\tZ_Free(l);\n\t}\n\tl = Z_Malloc(sizeof(*l) + certsize + strlen(hostname));\n\tl->trusted = trusted;\n\tl->certsize = certsize;\n\tl->hostname = l->cert + l->certsize;\n\tmemcpy(l->cert, cert, certsize);\n\tstrcpy(l->hostname, hostname);\n\tInsertLinkAfter(&l->l, &certlog);\n}\nstatic void CertLog_Write(void)\n{\n\tstruct certlog_s *l;\n\tvfsfile_t *f = FS_OpenVFS(CERTLOG_FILENAME, \"wb\", FS_ROOT);\n\tif (f)\n\t{\n\t\tVFS_PRINTF(f, \"version 1.1\\n\");\n\n\t\tfor (l=(struct certlog_s*)certlog.next ; l != (struct certlog_s*)&certlog ; l = (struct certlog_s*)l->l.next)\n\t\t{\n\t\t\tchar certhex[32768];\n\t\t\tsize_t i;\n\t\t\tconst char *hex = \"0123456789abcdef\";\n\t\t\tfor (i = 0; i < l->certsize; i++)\n\t\t\t{\n\t\t\t\tcerthex[i*2+0] = hex[l->cert[i]>>4];\n\t\t\t\tcerthex[i*2+1] = hex[l->cert[i]&0xf];\n\t\t\t}\n\t\t\tcerthex[i*2] = 0;\n\t\t\tVFS_PRINTF(f, \"%s \\\"\", l->hostname);\n\t\t\tVFS_PUTS(f, certhex);\n\t\t\tVFS_PRINTF(f, \"\\\" %i\\n\", l->trusted?true:false);\n\t\t}\n\t\tVFS_CLOSE(f);\n\t}\n\telse\n\t\tCon_Printf(CON_ERROR\"Unable to write %s\\n\", CERTLOG_FILENAME);\n}\nstatic void CertLog_Purge(void)\n{\n\twhile (certlog.next != &certlog)\n\t{\n\t\tstruct certlog_s *l = (struct certlog_s*)certlog.next;\n\t\tRemoveLink(&l->l);\n\t\tZ_Free(l);\n\t}\n\n\tcertlog_inited = false;\n}\nstatic int hexdecode(char c)\n{\n\tif (c >= '0' && c <= '9')\n\t\treturn c - '0';\n\telse if (c >= 'a' && c <= 'f')\n\t\treturn c - 'a' + 10;\n\telse if (c >= 'A' && c <= 'F')\n\t\treturn c - 'A' + 10;\n\treturn 0;\n}\nstatic void CertLog_Import(const char *filename)\n{\n\tchar addressstring[512];\n\tchar certhex[32768];\n\tchar certdata[16384];\n\tchar trusted[16];\n\tchar line[65536], *l;\n\tsize_t i, certsize;\n\tvfsfile_t *f;\n\tif (!certlog_inited && filename)\n\t\tCertLog_Import(NULL);\n\tcertlog_inited |= !filename;\n\tf = FS_OpenVFS(filename?filename:CERTLOG_FILENAME, \"rb\", FS_ROOT);\n\tif (!f)\n\t\treturn;\n\t//CertLog_Purge();\n\tVFS_GETS(f, line, sizeof(line));\n\tif (strncmp(line, \"version 1.\", 10))\n\t\treturn;\t//unsupported...\n\twhile (VFS_GETS(f, line, sizeof(line)))\n\t{\n\t\tl = line;\n\t\tl = COM_ParseOut(l, addressstring, sizeof(addressstring));\n\t\tl = COM_ParseOut(l, certhex, sizeof(certhex));\n\t\tl = COM_ParseOut(l, trusted, sizeof(trusted));\n\n\t\tcertsize = 0;\n\t\tfor (i = 0; certsize < sizeof(certdata); i++)\n\t\t{\n\t\t\tif (!certhex[(i<<1)+0] || !certhex[(i<<1)+1])\n\t\t\t\tbreak;\n\t\t\tcertdata[certsize++] = (hexdecode(certhex[(i<<1)+0])<<4)|hexdecode(certhex[(i<<1)+1]);\n\t\t}\n\t\tCertLog_Update(addressstring, certdata, certsize, atoi(trusted));\n\t}\n\tVFS_CLOSE(f);\n}\nstatic void CertLog_UntrustAll_f(void)\n{\n\tCertLog_Purge();\n}\nstatic void CertLog_Import_f(void)\n{\n\tconst char *fname = Cmd_Argv(1);\n\tif (Cmd_IsInsecure())\n\t\treturn;\n\tif (!*fname)\n\t\tfname = NULL;\n\tCertLog_Import(fname);\n}\nstruct certprompt_s\n{\n\tchar *hostname;\n\n\tsize_t certsize;\n\tqbyte cert[1];\n};\nstatic struct certprompt_s *certlog_curprompt;\nstatic void CertLog_Add_Prompted(void *vctx, promptbutton_t button)\n{\n\tstruct certprompt_s *ctx = vctx;\n\tif (button == PROMPT_YES)\t//button_yes / button_left\n\t{\n\t\tCertLog_Update(ctx->hostname, ctx->cert, ctx->certsize, true);\n\t\tCertLog_Write();\n\n\t\tCL_BeginServerReconnect();\n\t}\n\telse\n\t\tCL_Disconnect(\"Server certificate rejected\");\n\n\tcertlog_curprompt = NULL;\n}\nqboolean CertLog_ConnectOkay(const char *hostname, void *cert, size_t certsize, unsigned int certlogproblems)\n{\t//this is specifically for dtls certs.\n\tstruct certlog_s *l;\n\tqboolean trusted = (net_enable_dtls.ival >= 2);\n\tchar digest[DIGEST_MAXSIZE];\n\tchar fp[DIGEST_MAXSIZE*2+1];\n\n\tif (certlog_curprompt)\n\t\treturn false;\n\tCOM_AssertMainThread(\"CertLog_ConnectOkay\");\n\n\tif (!certlog_inited)\n\t\tCertLog_Import(NULL);\n\tl = CertLog_Find(hostname);\n\n\tif (!l && !trusted)\n\t{\t//cert is new, but we don't care about full trust. don't bother to prompt when the user doesn't much care.\n\t\t//(but do pin so we at least know when its MITMed after the fact)\n\t\tCon_Printf(CON_WARNING\"Auto-Pinning certificate for %s.\"CON_DEFAULT\" ^[/seta %s 2^]+ for actual security.\\n\", hostname, net_enable_dtls.name);\n\t\tif (certsize)\n\t\t\tBase64_EncodeBlockURI(digest, CalcHash(&hash_certfp, digest, sizeof(digest), cert, certsize), fp, sizeof(fp));\n\t\telse\n\t\t\tstrcpy(fp, \"<No Certificate>\");\n\t\tCon_Printf(S_COLOR_GRAY\"  fp: %s\\n\", fp);\n\t\tCertLog_Update(hostname, cert, certsize, false);\n\t\tCertLog_Write();\n\t}\n\telse if (!l || l->certsize != certsize || memcmp(l->cert, cert, certsize) || (trusted && !l->trusted))\n\t{\t//new or different\n\t\tif (certsize)\n\t\t\tBase64_EncodeBlockURI(digest, CalcHash(&hash_certfp, digest, sizeof(digest), cert, certsize), fp, sizeof(fp));\n\t\telse\n\t\t\tstrcpy(fp, \"<No Certificate>\");\n\t\tif (qrenderer)\n\t\t{\n\t\t\tunsigned int i;\n\t\t\tsize_t len;\n\t\t\tchar *text;\n\t\t\tconst char *accepttext;\n\t\t\tconst char *lines[] = {\n\t\t\t\t\t\t\t\t\tva(localtext(\"Certificate for %s\\n(fp:\"S_COLOR_GRAY\"%s\"S_COLOR_WHITE\")\\n\"), hostname, fp),\n\t\t\t\t\t\t\t\t\t(certlogproblems&CERTLOG_WRONGHOST)?localtext(\"^1Certificate does not match host\\n\"):\"\",\n\t\t\t\t\t\t\t\t\t((certlogproblems&(CERTLOG_MISSINGCA|CERTLOG_WRONGHOST))==CERTLOG_MISSINGCA)?localtext(\"^1Certificate authority is untrusted.\\n\"):\"\",\n\t\t\t\t\t\t\t\t\t(certlogproblems&CERTLOG_EXPIRED)?localtext(\"^1Expired Certificate\\n\"):\"\",\n\t\t\t\t\t\t\t\t\tl?localtext(\"\\n^1WARNING: Certificate has changed since previously trusted.\"):\"\"};\n\t\t\tstruct certprompt_s *ctx = certlog_curprompt = Z_Malloc(sizeof(*ctx)+certsize + strlen(hostname));\n\t\t\tctx->hostname = ctx->cert + certsize;\n\t\t\tctx->certsize = certsize;\n\t\t\tmemcpy(ctx->cert, cert, certsize);\n\t\t\tstrcpy(ctx->hostname, hostname);\n\n\t\t\tif (l)\t//FIXME: show expiry info for the old cert, warn if more than a month?\n\t\t\t\taccepttext = localtext(\"Replace\");\n\t\t\telse if (!certlogproblems)\n\t\t\t\taccepttext = localtext(\"Pin\");\n\t\t\telse\n\t\t\t\taccepttext = localtext(\"Trust\");\n\n\t\t\tfor (i = 0, len = 0; i < countof(lines); i++)\n\t\t\t\tlen += strlen(lines[i]);\n\t\t\ttext = alloca(len+1);\n\t\t\tfor (i = 0, len = 0; i < countof(lines); i++)\n\t\t\t{\n\t\t\t\tstrcpy(text+len, lines[i]);\n\t\t\t\tlen += strlen(lines[i]);\n\t\t\t}\n\t\t\ttext[len] = 0;\n\n\t\t\t//FIXME: display some sort of fingerprint\n\t\t\tMenu_Prompt(CertLog_Add_Prompted, ctx, text, accepttext, NULL, localtext(\"Disconnect\"), true);\n\t\t}\n\t\treturn false;\t//can't connect yet...\n\t}\n\telse if (!l->trusted)\n\t\tCon_Printf(CON_WARNING\"Server certificate for %s was previously auto-pinned.\"CON_DEFAULT\" ^[/seta %s 2^]+ for actual security.\\n\", hostname, net_enable_dtls.name);\n\treturn true;\n}\n#endif\n\n\n\n#if defined(HAVE_SERVER) && defined(HAVE_CLIENT)\nstatic struct maplog_entry\n{\n\tstruct maplog_entry *next;\n\tfloat bestkills;\n\tfloat bestsecrets;\n\tfloat besttime;\t //updated when besttime<newtime (note: doesn't respond to user changelevels from the console...)\n\tfloat fulltime; //updated when bestkills>=newkills\n\tchar name[1];\n} *maplog_enties;\nstatic void Log_MapsRead(void)\n{\n\tstruct maplog_entry *m, **link = &maplog_enties;\n\tvfsfile_t *f;\n\tstatic qboolean maplog_loaded;\n\tchar line[8192], *s;\n\tif (maplog_loaded)\n\t\treturn;\n\tmaplog_loaded = true;\n\tf = FS_OpenVFS(\"maptimes.txt\", \"rb\", FS_ROOT);\n\tif (!f)\n\t\treturn; //no info yet.\n\twhile (VFS_GETS(f, line, sizeof(line)))\n\t{\n\t\ts = line;\n\t\ts = COM_Parse(s);\n\t\tm = Z_Malloc(sizeof(*m) + strlen(com_token));\n\t\tstrcpy(m->name, com_token);\n\n\t\ts = COM_Parse(s);\n\t\tm->besttime = atof(com_token);\n\t\ts = COM_Parse(s);\n\t\tm->fulltime = atof(com_token);\n\t\ts = COM_Parse(s);\n\t\tm->bestkills = atof(com_token);\n\t\ts = COM_Parse(s);\n\t\tm->bestsecrets = atof(com_token);\n\n\t\t*link = m;\n\t\tlink = &m->next;\n\t}\n\tVFS_CLOSE(f);\n}\nstruct maplog_entry *Log_FindMap(const char *purepackage, const char *mapname)\n{\n\tchar name[MAX_OSPATH];\n\tstruct maplog_entry *m;\n\tif (Q_snprintfz(name, sizeof(name), \"%s/%s\", purepackage, mapname))\n\t\treturn NULL;\n\tLog_MapsRead();\n\tfor (m = maplog_enties; m; m = m->next)\n\t{\n\t\tif (!strcmp(m->name, name))\n\t\t\tbreak;\n\t}\n\treturn m;\n}\nstatic void Log_MapsDump(void)\n{\n\tif (maplog_enties)\n\t{\n\t\tstruct maplog_entry *m;\n\t\tvfsfile_t *f = FS_OpenVFS(\"maptimes.txt\", \"wbp\", FS_ROOT);\n\t\tif (f)\n\t\t{\n\t\t\tfor(m = maplog_enties; m; m = m->next)\n\t\t\t{\n\t\t\t\tVFS_PRINTF(f, \"\\\"%s\\\" %.9g %.9g %.9g %.9g\\n\", m->name, m->besttime, m->fulltime, m->bestkills, m->bestsecrets);\n\t\t\t}\n\t\t\tVFS_CLOSE(f);\n\t\t}\n\t}\n}\nqboolean Log_CheckMapCompletion(const char *packagename, const char *mapname, float *besttime, float *fulltime, float *bestkills, float *bestsecrets)\n{\n\tstruct maplog_entry *m;\n\tif (!packagename)\n\t{\n\t\tflocation_t loc;\n\t\tif (!FS_FLocateFile(mapname, FSLF_DONTREFERENCE|FSLF_IGNORELINKS, &loc))\n\t\t\treturn false;\t//no idea which package, don't guess.\n\t\tpackagename = FS_GetRootPackagePath(&loc);\n\t\tif (!packagename)\n\t\t\treturn false;\n\t}\n\tm = Log_FindMap(packagename, mapname);\n\tif (m)\n\t{\n\t\t*besttime = m->besttime;\n\t\t*fulltime = m->fulltime;\n\t\t*bestkills = m->bestkills;\n\t\t*bestsecrets = m->bestsecrets;\n\t\treturn true;\n\t}\n\treturn false;\n}\nvoid Log_MapNowCompleted(void)\n{\n\tstruct maplog_entry *m;\n\tflocation_t loc;\n\tfloat kills, secrets, oldprogress, newprogress, maptime;\n\tconst char *packagename;\n\n\t//don't log it if its deathmatch/coop/cheating.\n\textern int sv_allow_cheats;\n\tif (deathmatch.ival || coop.ival || sv_allow_cheats == 1)\n\t\treturn;\n\n\tif (!FS_FLocateFile(sv.world.worldmodel->name, FSLF_DONTREFERENCE|FSLF_IGNORELINKS, &loc))\n\t{\n\t\tCon_Printf(\"completion log: unable to determine logical path for map\\n\");\n\t\treturn;\t//don't know\n\t}\n\tpackagename = FS_GetRootPackagePath(&loc);\n\tif (!packagename)\n\t{\n\t\tCon_Printf(\"completion log: unable to determine logical path for map\\n\");\n\t\treturn;\n\t}\n\n\tm = Log_FindMap(packagename, sv.world.worldmodel->name);\n\tif (!m)\n\t{\n\t\tm = Z_Malloc(sizeof(*m)+strlen(packagename)+strlen(sv.world.worldmodel->name)+2);\n\t\tsprintf(m->name, \"%s/%s\", packagename, sv.world.worldmodel->name);\n\n\t\tm->fulltime = m->besttime = FLT_MAX;\n\t\tm->bestkills = m->bestsecrets = 0;\n\t\tm->next = maplog_enties;\n\t\tmaplog_enties = m;\n\t}\n\n\tkills = pr_global_struct->killed_monsters;\n\tsecrets = pr_global_struct->found_secrets;\n\tmaptime = sv.world.physicstime;\n\n\tnewprogress = secrets*10+kills;\n\toldprogress = m->bestsecrets*10+m->bestkills;\n\n\t//if they got a new time record, update.\n\tif (maptime<m->besttime)\n\t\tm->besttime = maptime;\n\t//if they got a new kills record, update\n\tif (newprogress > oldprogress || (newprogress==oldprogress && maptime<m->fulltime))\n\t{\n\t\tm->bestkills = kills;\n\t\tm->bestsecrets = secrets;\n\t\tm->fulltime = maptime;\n\t}\n}\n#endif\n\nvoid Log_ShutDown(void)\n{\n#if defined(HAVE_SERVER) && defined(HAVE_CLIENT)\n\tLog_MapsDump();\n#endif\n\n#ifdef IPLOG\n\tif (iplog_autodump.ival)\n\t\tIPLog_Dump(\"iplog.txt\");\n//\tIPLog_Dump(\"iplog.dat\");\n\n\twhile(iplog_num > 0)\n\t{\n\t\tiplog_num--;\n\t\tBZ_Free(iplog_entries[iplog_num]);\n\t}\n\tBZ_Free(iplog_entries);\n\tiplog_entries = NULL;\n\tiplog_max = iplog_num = 0;\n#endif\n}\n\nvoid Log_Init(void)\n{\n\tint i;\n\t// register cvars\n\tfor (i = 0; i < LOG_TYPES; i++)\n\t{\n#ifdef CLIENTONLY\n\t\tif (i != LOG_CONSOLE)\n\t\t\tcontinue;\n#endif\n\t\tCvar_Register (&log_enable[i], CONLOGGROUP);\n\t\tCvar_Register (&log_name[i], CONLOGGROUP);\n\t\tlog_newline[i] = true;\n#ifdef HAVE_LEGACY\n\t\tCmd_AddCommand(legacylog[i].commandname, Log_Logfile_f);\n#endif\n\t}\n\tCvar_Register (&log_dir_var, CONLOGGROUP);\n\tCvar_Register (&log_readable, CONLOGGROUP);\n\tCvar_Register (&log_developer, CONLOGGROUP);\n\tCvar_Register (&log_rotate_size, CONLOGGROUP);\n\tCvar_Register (&log_rotate_files, CONLOGGROUP);\n\tCvar_Register (&log_dosformat, CONLOGGROUP);\n\tCvar_Register (&log_timestamps, CONLOGGROUP);\n\n#ifdef IPLOG\n\tCmd_AddCommandD(\"identify\", IPLog_Identify_f, \"Looks up a player's ip to see if they're using a different name\");\n\tCmd_AddCommand(\"ipmerge\", IPLog_Merge_f);\n\tCmd_AddCommand(\"ipdump\", IPLog_Dump_f);\n\tCvar_Register (&iplog_autodump, CONLOGGROUP);\n#endif\n\n\t// cmd line options, debug options\n#ifdef CRAZYDEBUGGING\n\tCvar_ForceSet(&log_enable[LOG_CONSOLE], \"1\");\n\tTRACE((\"dbg: Con_Init: log_enable forced\\n\"));\n#endif\n\n\tif (COM_CheckParm(\"-condebug\"))\n\t\tCvar_ForceSet(&log_enable[LOG_CONSOLE], \"1\");\n\n#if defined(HAVE_DTLS) && defined(HAVE_CLIENT)\n\tClearLink(&certlog);\n\tCmd_AddCommand(\"dtls_untrustall\", CertLog_UntrustAll_f);\n\tCmd_AddCommand(\"dtls_importtrust\", CertLog_Import_f);\n#endif\n}\n"
  },
  {
    "path": "engine/common/mathlib.c",
    "content": "/*\nCopyright (C) 1996-1997 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  \n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n*/\n// mathlib.c -- math primitives\n\n#include \"quakedef.h\"\n#include <math.h>\n\nvec3_t vec3_origin = {0,0,0};\n\n/*-----------------------------------------------------------------*/\n\n#define DEG2RAD( a ) ( a * M_PI ) / 180.0F\n\nstatic void ProjectPointOnPlane( vec3_t dst, const vec3_t p, const vec3_t normal )\n{\n\tfloat d;\n\tvec3_t n;\n\tfloat inv_denom;\n\n\tinv_denom = 1.0F / DotProduct( normal, normal );\n\n\td = DotProduct( normal, p ) * inv_denom;\n\n\tn[0] = normal[0] * inv_denom;\n\tn[1] = normal[1] * inv_denom;\n\tn[2] = normal[2] * inv_denom;\n\n\tdst[0] = p[0] - d * n[0];\n\tdst[1] = p[1] - d * n[1];\n\tdst[2] = p[2] - d * n[2];\n}\n\n/*\n** assumes \"src\" is normalized\n*/\nvoid PerpendicularVector( vec3_t dst, const vec3_t src )\n{\n\tint\tpos;\n\tint i;\n\tfloat minelem = 1.0F;\n\tvec3_t tempvec;\n\n\t/*\n\t** find the smallest magnitude axially aligned vector\n\t*/\n\tfor ( pos = 0, i = 0; i < 3; i++ )\n\t{\n\t\tif ( fabs( src[i] ) < minelem )\n\t\t{\n\t\t\tpos = i;\n\t\t\tminelem = fabs( src[i] );\n\t\t}\n\t}\n\ttempvec[0] = tempvec[1] = tempvec[2] = 0.0F;\n\ttempvec[pos] = 1.0F;\n\n\t/*\n\t** project the point onto the plane defined by src\n\t*/\n\tProjectPointOnPlane( dst, tempvec, src );\n\n\t/*\n\t** normalize the result\n\t*/\n\tVectorNormalize( dst );\n}\n\n#ifdef _MSC_VER\n#pragma optimize( \"\", off )\n#endif\n\n\nvoid RotatePointAroundVector( vec3_t dst, const vec3_t dir, const vec3_t point, float degrees )\n{\n\tfloat\tm[3][3];\n\tfloat\tim[3][3];\n\tfloat\tzrot[3][3];\n\tfloat\ttmpmat[3][3];\n\tfloat\trot[3][3];\n\tint\ti;\n\tvec3_t vr, vup, vf;\n\n\tvf[0] = dir[0];\n\tvf[1] = dir[1];\n\tvf[2] = dir[2];\n\n\tPerpendicularVector( vr, dir );\n\tCrossProduct( vr, vf, vup );\n\n\tm[0][0] = vr[0];\n\tm[1][0] = vr[1];\n\tm[2][0] = vr[2];\n\n\tm[0][1] = vup[0];\n\tm[1][1] = vup[1];\n\tm[2][1] = vup[2];\n\n\tm[0][2] = vf[0];\n\tm[1][2] = vf[1];\n\tm[2][2] = vf[2];\n\n\tmemcpy( im, m, sizeof( im ) );\n\n\tim[0][1] = m[1][0];\n\tim[0][2] = m[2][0];\n\tim[1][0] = m[0][1];\n\tim[1][2] = m[2][1];\n\tim[2][0] = m[0][2];\n\tim[2][1] = m[1][2];\n\n\tmemset( zrot, 0, sizeof( zrot ) );\n\tzrot[0][0] = zrot[1][1] = zrot[2][2] = 1.0F;\n\n\tzrot[0][0] = cos( DEG2RAD( degrees ) );\n\tzrot[0][1] = sin( DEG2RAD( degrees ) );\n\tzrot[1][0] = -sin( DEG2RAD( degrees ) );\n\tzrot[1][1] = cos( DEG2RAD( degrees ) );\n\n\tR_ConcatRotations( m, zrot, tmpmat );\n\tR_ConcatRotations( tmpmat, im, rot );\n\n\tfor ( i = 0; i < 3; i++ )\n\t{\n\t\tdst[i] = rot[i][0] * point[0] + rot[i][1] * point[1] + rot[i][2] * point[2];\n\t}\n}\n\n#ifdef _MSC_VER\n#pragma optimize( \"\", on )\n#endif\n\n/*-----------------------------------------------------------------*/\n\nfloat\tanglemod(float a)\n{\n#if 0\n\tif (a >= 0)\n\t\ta -= 360*(int)(a/360);\n\telse\n\t\ta += 360*( 1 + (int)(-a/360) );\n#endif\n\ta = (360.0/65536) * ((int)(a*(65536/360.0)) & 65535);\n\treturn a;\n}\n\n/*\n==================\nBoxOnPlaneSide\n\nReturns 1, 2, or 1 + 2\n==================\n*/\nint VARGS BoxOnPlaneSide (const vec3_t emins, const vec3_t emaxs, const mplane_t *p)\n{\n\tfloat\tdist1, dist2;\n\tint\t\tsides;\n\n#if 0\t// this is done by the BOX_ON_PLANE_SIDE macro before calling this\n\t\t// function\n// fast axial cases\n\tif (p->type < 3)\n\t{\n\t\tif (p->dist <= emins[p->type])\n\t\t\treturn 1;\n\t\tif (p->dist >= emaxs[p->type])\n\t\t\treturn 2;\n\t\treturn 3;\n\t}\n#endif\n\t\n// general case\n\tswitch (p->signbits)\n\t{\n\tdefault:\n\tcase 0:\ndist1 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2];\ndist2 = p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2];\n\t\tbreak;\n\tcase 1:\ndist1 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2];\ndist2 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2];\n\t\tbreak;\n\tcase 2:\ndist1 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2];\ndist2 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2];\n\t\tbreak;\n\tcase 3:\ndist1 = p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2];\ndist2 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2];\n\t\tbreak;\n\tcase 4:\ndist1 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2];\ndist2 = p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2];\n\t\tbreak;\n\tcase 5:\ndist1 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2];\ndist2 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2];\n\t\tbreak;\n\tcase 6:\ndist1 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2];\ndist2 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2];\n\t\tbreak;\n\tcase 7:\ndist1 = p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2];\ndist2 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2];\n\t\tbreak;\n\t}\n\n#if 0\n\tint\t\ti;\n\tvec3_t\tcorners[2];\n\n\tfor (i=0 ; i<3 ; i++)\n\t{\n\t\tif (plane->normal[i] < 0)\n\t\t{\n\t\t\tcorners[0][i] = emins[i];\n\t\t\tcorners[1][i] = emaxs[i];\n\t\t}\n\t\telse\n\t\t{\n\t\t\tcorners[1][i] = emins[i];\n\t\t\tcorners[0][i] = emaxs[i];\n\t\t}\n\t}\n\tdist = DotProduct (plane->normal, corners[0]) - plane->dist;\n\tdist2 = DotProduct (plane->normal, corners[1]) - plane->dist;\n\tsides = 0;\n\tif (dist1 >= 0)\n\t\tsides = 1;\n\tif (dist2 < 0)\n\t\tsides |= 2;\n\n#endif\n\n\tsides = 0;\n\tif (dist1 >= p->dist)\n\t\tsides = 1;\n\tif (dist2 < p->dist)\n\t\tsides |= 2;\n\n#ifdef PARANOID\nif (sides == 0)\n\tSys_Error (\"BoxOnPlaneSide: sides==0\");\n#endif\n\n\treturn sides;\n}\n\n\n\n\nstatic void VVPerpendicularVector(vec3_t dst, const vec3_t src)\n{\n\tif (!src[0] && !src[1])\n\t{\n\t\tif (src[2])\n\t\t\tdst[1] = -1;\n\t\telse\n\t\t\tdst[1] = 0;\n\t\tdst[0] = dst[2] = 0;\n\t}\n\telse\n\t{\n\t\tdst[0] = src[1];\n\t\tdst[1] = -src[0];\n\t\tdst[2] = 0;\n\t\tVectorNormalize(dst);\n\t}\n}\nvoid VectorVectors(const vec3_t forward, vec3_t right, vec3_t up)\n{\n\tVVPerpendicularVector(right, forward);\n\tCrossProduct(right, forward, up);\n}\n\nvoid QDECL VectorAngles(const float *forward, const float *up, float *result, qboolean meshpitch)\t//up may be NULL\n{\n\tfloat\tyaw, pitch, roll;\t\n\n\tif (forward[1] == 0 && forward[0] == 0)\n\t{\n\t\tif (forward[2] > 0)\n\t\t{\n\t\t\tpitch = -M_PI * 0.5;\n\t\t\tyaw = up ? atan2(-up[1], -up[0]) : 0;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tpitch = M_PI * 0.5;\n\t\t\tyaw = up ? atan2(up[1], up[0]) : 0;\n\t\t}\n\t\troll = 0;\n\t}\n\telse\n\t{\n\t\tyaw = atan2(forward[1], forward[0]);\n\t\tpitch = -atan2(forward[2], sqrt (forward[0]*forward[0] + forward[1]*forward[1]));\n\n\t\tif (up)\n\t\t{\n\t\t\tvec_t cp = cos(pitch), sp = sin(pitch);\n\t\t\tvec_t cy = cos(yaw), sy = sin(yaw);\n\t\t\tvec3_t tleft, tup;\n\t\t\ttleft[0] = -sy;\n\t\t\ttleft[1] = cy;\n\t\t\ttleft[2] = 0;\n\t\t\ttup[0] = sp*cy;\n\t\t\ttup[1] = sp*sy;\n\t\t\ttup[2] = cp;\n\t\t\troll = -atan2(DotProduct(up, tleft), DotProduct(up, tup));\n\t\t}\n\t\telse\n\t\t\troll = 0;\n\t}\n\n\tpitch *= 180 / M_PI;\n\tyaw *= 180 / M_PI;\n\troll *= 180 / M_PI;\n\tif (meshpitch)\n\t{\n\t\tpitch *= r_meshpitch.value;\n\t\troll *= r_meshroll.value;\n\t}\n\tif (pitch < 0)\n\t\tpitch += 360;\n\tif (yaw < 0)\n\t\tyaw += 360;\n\tif (roll < 0)\n\t\troll += 360;\n\n\tresult[0] = pitch;\n\tresult[1] = yaw;\n\tresult[2] = roll;\n}\n\nvoid QDECL AngleVectors (const vec3_t angles, vec3_t forward, vec3_t right, vec3_t up)\n{\n\tfloat\t\tangle;\n\tfloat\t\tsr, sp, sy, cr, cp, cy;\n\t\n\tangle = angles[YAW] * (M_PI*2 / 360);\n\tsy = sin(angle);\n\tcy = cos(angle);\n\tangle = angles[PITCH] * (M_PI*2 / 360);\n\tsp = sin(angle);\n\tcp = cos(angle);\n\tangle = angles[ROLL] * (M_PI*2 / 360);\n\tsr = sin(angle);\n\tcr = cos(angle);\n\n\tif (forward)\n\t{\n\t\tforward[0] = cp*cy;\n\t\tforward[1] = cp*sy;\n\t\tforward[2] = -sp;\n\t}\n\tif (right)\n\t{\n\t\tright[0] = (-1*sr*sp*cy+-1*cr*-sy);\n\t\tright[1] = (-1*sr*sp*sy+-1*cr*cy);\n\t\tright[2] = -1*sr*cp;\n\t}\n\tif (up)\n\t{\n\t\tup[0] = (cr*sp*cy+-sr*-sy);\n\t\tup[1] = (cr*sp*sy+-sr*cy);\n\t\tup[2] = cr*cp;\n\t}\n}\nvoid AngleVectorsMesh (const vec3_t angles, vec3_t forward, vec3_t right, vec3_t up)\n{\n\tvec3_t ang;\n\tang[0] = angles[0] * r_meshpitch.value;\n\tang[1] = angles[1];\n\tang[2] = angles[2] * r_meshroll.value;\n\tAngleVectors (ang, forward, right, up);\n}\n\nint VectorCompare (const vec3_t v1, const vec3_t v2)\n{\n\tint\t\ti;\n\t\n\tfor (i=0 ; i<3 ; i++)\n\t\tif (v1[i] != v2[i])\n\t\t\treturn 0;\n\t\t\t\n\treturn 1;\n}\nint Vector4Compare (const vec4_t v1, const vec4_t v2)\n{\n\tint\t\ti;\n\t\n\tfor (i=0 ; i<4 ; i++)\n\t\tif (v1[i] != v2[i])\n\t\t\treturn 0;\n\t\t\t\n\treturn 1;\n}\n/*\nvoid _VectorMA (const vec3_t veca, const float scale, const vec3_t vecb, vec3_t vecc)\n{\n\tvecc[0] = veca[0] + scale*vecb[0];\n\tvecc[1] = veca[1] + scale*vecb[1];\n\tvecc[2] = veca[2] + scale*vecb[2];\n}\n\n\nvec_t _DotProduct (vec3_t v1, vec3_t v2)\n{\n\treturn v1[0]*v2[0] + v1[1]*v2[1] + v1[2]*v2[2];\n}\n\nvoid _VectorSubtract (vec3_t veca, vec3_t vecb, vec3_t out)\n{\n\tout[0] = veca[0]-vecb[0];\n\tout[1] = veca[1]-vecb[1];\n\tout[2] = veca[2]-vecb[2];\n}\n\nvoid _VectorAdd (vec3_t veca, vec3_t vecb, vec3_t out)\n{\n\tout[0] = veca[0]+vecb[0];\n\tout[1] = veca[1]+vecb[1];\n\tout[2] = veca[2]+vecb[2];\n}\n\nvoid _VectorCopy (vec3_t in, vec3_t out)\n{\n\tout[0] = in[0];\n\tout[1] = in[1];\n\tout[2] = in[2];\n}\n*/\nvoid CrossProduct (const vec3_t v1, const vec3_t v2, vec3_t cross)\n{\n\tcross[0] = v1[1]*v2[2] - v1[2]*v2[1];\n\tcross[1] = v1[2]*v2[0] - v1[0]*v2[2];\n\tcross[2] = v1[0]*v2[1] - v1[1]*v2[0];\n}\n\nvec_t Length(const vec3_t v)\n{\n\tint\t\ti;\n\tfloat\tlength;\n\t\n\tlength = 0;\n\tfor (i=0 ; i< 3 ; i++)\n\t\tlength += v[i]*v[i];\n\tlength = sqrt (length);\t\t// FIXME\n\n\treturn length;\n}\n\nfloat Q_rsqrt(float number)\n{\n\tint i;\n\tfloat x2, y;\n\tconst float threehalfs = 1.5F;\n\n\tx2 = number * 0.5F;\n\ty  = number;\n\ti  = * (int *) &y;\t\t\t\t\t\t// evil floating point bit level hacking\n\ti  = 0x5f3759df - (i >> 1);               // what the fuck?\n\ty  = * (float *) &i;\n\ty  = y * (threehalfs - (x2 * y * y));   // 1st iteration\n//\ty  = y * (threehalfs - (x2 * y * y));   // 2nd iteration, this can be removed\n\n\treturn y;\n}\n\nfloat QDECL VectorNormalize (vec3_t v)\n{\n\tfloat\tlength;\n\tfloat\tilength;\n\n\tlength = v[0]*v[0] + v[1]*v[1] + v[2]*v[2];\n\tlength = sqrt (length);\t\t// FIXME\n\n\tif (length)\n\t{\n\t\tilength = 1.0/length;\n\t\tv[0] *= ilength;\n\t\tv[1] *= ilength;\n\t\tv[2] *= ilength;\n\t}\n\t\t\n\treturn length;\n}\n\nvoid VectorNormalizeFast(vec3_t v)\n{\n\tfloat ilength;\n\n\tilength = Q_rsqrt(DotProduct(v, v));\n\n\tv[0] *= ilength;\n\tv[1] *= ilength;\n\tv[2] *= ilength;\n}\n\nvoid VectorInverse (vec3_t v)\n{\n\tv[0] = -v[0];\n\tv[1] = -v[1];\n\tv[2] = -v[2];\n}\n\n\nint Q_log2(int val)\n{\n\tint answer=0;\n\twhile ((val>>=1) != 0)\n\t\tanswer++;\n\treturn answer;\n}\n\n\n/*\n================\nR_ConcatRotations\n================\n*/\nvoid R_ConcatRotations (float in1[3][3], float in2[3][3], float out[3][3])\n{\n\tout[0][0] = in1[0][0] * in2[0][0] + in1[0][1] * in2[1][0] + in1[0][2] * in2[2][0];\n\tout[0][1] = in1[0][0] * in2[0][1] + in1[0][1] * in2[1][1] + in1[0][2] * in2[2][1];\n\tout[0][2] = in1[0][0] * in2[0][2] + in1[0][1] * in2[1][2] + in1[0][2] * in2[2][2];\n\tout[1][0] = in1[1][0] * in2[0][0] + in1[1][1] * in2[1][0] + in1[1][2] * in2[2][0];\n\tout[1][1] = in1[1][0] * in2[0][1] + in1[1][1] * in2[1][1] + in1[1][2] * in2[2][1];\n\tout[1][2] = in1[1][0] * in2[0][2] + in1[1][1] * in2[1][2] + in1[1][2] * in2[2][2];\n\tout[2][0] = in1[2][0] * in2[0][0] + in1[2][1] * in2[1][0] + in1[2][2] * in2[2][0];\n\tout[2][1] = in1[2][0] * in2[0][1] + in1[2][1] * in2[1][1] + in1[2][2] * in2[2][1];\n\tout[2][2] = in1[2][0] * in2[0][2] + in1[2][1] * in2[1][2] + in1[2][2] * in2[2][2];\n}\n\n/*\n================\nR_ConcatTransforms\n================\n*/\nvoid QDECL R_ConcatTransforms (const float in1[3][4], const float in2[3][4], float out[3][4])\n{\n\tout[0][0] = in1[0][0] * in2[0][0] + in1[0][1] * in2[1][0] +\n\t\t\t\tin1[0][2] * in2[2][0];\n\tout[0][1] = in1[0][0] * in2[0][1] + in1[0][1] * in2[1][1] +\n\t\t\t\tin1[0][2] * in2[2][1];\n\tout[0][2] = in1[0][0] * in2[0][2] + in1[0][1] * in2[1][2] +\n\t\t\t\tin1[0][2] * in2[2][2];\n\tout[0][3] = in1[0][0] * in2[0][3] + in1[0][1] * in2[1][3] +\n\t\t\t\tin1[0][2] * in2[2][3] + in1[0][3];\n\tout[1][0] = in1[1][0] * in2[0][0] + in1[1][1] * in2[1][0] +\n\t\t\t\tin1[1][2] * in2[2][0];\n\tout[1][1] = in1[1][0] * in2[0][1] + in1[1][1] * in2[1][1] +\n\t\t\t\tin1[1][2] * in2[2][1];\n\tout[1][2] = in1[1][0] * in2[0][2] + in1[1][1] * in2[1][2] +\n\t\t\t\tin1[1][2] * in2[2][2];\n\tout[1][3] = in1[1][0] * in2[0][3] + in1[1][1] * in2[1][3] +\n\t\t\t\tin1[1][2] * in2[2][3] + in1[1][3];\n\tout[2][0] = in1[2][0] * in2[0][0] + in1[2][1] * in2[1][0] +\n\t\t\t\tin1[2][2] * in2[2][0];\n\tout[2][1] = in1[2][0] * in2[0][1] + in1[2][1] * in2[1][1] +\n\t\t\t\tin1[2][2] * in2[2][1];\n\tout[2][2] = in1[2][0] * in2[0][2] + in1[2][1] * in2[1][2] +\n\t\t\t\tin1[2][2] * in2[2][2];\n\tout[2][3] = in1[2][0] * in2[0][3] + in1[2][1] * in2[1][3] +\n\t\t\t\tin1[2][2] * in2[2][3] + in1[2][3];\n}\n\n//R_ConcatTransforms where there's no offset values, and a transposed axis\nvoid R_ConcatTransformsAxis (const float in1[3][3], const float in2[3][4], float out[3][4])\n{\n\tout[0][0] = in1[0][0] * in2[0][0] + in1[1][0] * in2[1][0] +\n\t\t\t\tin1[2][0] * in2[2][0];\n\tout[0][1] = in1[0][0] * in2[0][1] + in1[1][0] * in2[1][1] +\n\t\t\t\tin1[2][0] * in2[2][1];\n\tout[0][2] = in1[0][0] * in2[0][2] + in1[1][1] * in2[1][2] +\n\t\t\t\tin1[2][0] * in2[2][2];\n\tout[0][3] = in1[0][0] * in2[0][3] + in1[1][1] * in2[1][3] +\n\t\t\t\tin1[2][0] * in2[2][3];\n\tout[1][0] = in1[0][1] * in2[0][0] + in1[1][1] * in2[1][0] +\n\t\t\t\tin1[2][1] * in2[2][0];\n\tout[1][1] = in1[0][1] * in2[0][1] + in1[1][1] * in2[1][1] +\n\t\t\t\tin1[2][1] * in2[2][1];\n\tout[1][2] = in1[0][1] * in2[0][2] + in1[1][1] * in2[1][2] +\n\t\t\t\tin1[2][1] * in2[2][2];\n\tout[1][3] = in1[0][1] * in2[0][3] + in1[1][1] * in2[1][3] +\n\t\t\t\tin1[2][1] * in2[2][3];\n\tout[2][0] = in1[0][2] * in2[0][0] + in1[1][2] * in2[1][0] +\n\t\t\t\tin1[2][2] * in2[2][0];\n\tout[2][1] = in1[0][2] * in2[0][1] + in1[1][2] * in2[1][1] +\n\t\t\t\tin1[2][2] * in2[2][1];\n\tout[2][2] = in1[0][2] * in2[0][2] + in1[1][2] * in2[1][2] +\n\t\t\t\tin1[2][2] * in2[2][2];\n\tout[2][3] = in1[0][2] * in2[0][3] + in1[1][2] * in2[1][3] +\n\t\t\t\tin1[2][2] * in2[2][3];\n}\n\n//R_ConcatTransforms where we don't care about the resulting offsets.\nvoid R_ConcatRotationsPad (float in1[3][4], float in2[3][4], float out[3][4])\n{\n\tout[0][0] = in1[0][0] * in2[0][0] + in1[0][1] * in2[1][0] +\n\t\t\t\tin1[0][2] * in2[2][0];\n\tout[0][1] = in1[0][0] * in2[0][1] + in1[0][1] * in2[1][1] +\n\t\t\t\tin1[0][2] * in2[2][1];\n\tout[0][2] = in1[0][0] * in2[0][2] + in1[0][1] * in2[1][2] +\n\t\t\t\tin1[0][2] * in2[2][2];\n\n\tout[1][0] = in1[1][0] * in2[0][0] + in1[1][1] * in2[1][0] +\n\t\t\t\tin1[1][2] * in2[2][0];\n\tout[1][1] = in1[1][0] * in2[0][1] + in1[1][1] * in2[1][1] +\n\t\t\t\tin1[1][2] * in2[2][1];\n\tout[1][2] = in1[1][0] * in2[0][2] + in1[1][1] * in2[1][2] +\n\t\t\t\tin1[1][2] * in2[2][2];\n\n\tout[2][0] = in1[2][0] * in2[0][0] + in1[2][1] * in2[1][0] +\n\t\t\t\tin1[2][2] * in2[2][0];\n\tout[2][1] = in1[2][0] * in2[0][1] + in1[2][1] * in2[1][1] +\n\t\t\t\tin1[2][2] * in2[2][1];\n\tout[2][2] = in1[2][0] * in2[0][2] + in1[2][1] * in2[1][2] +\n\t\t\t\tin1[2][2] * in2[2][2];\n}\n\nvoid Matrix3x4_Multiply(const float *a, const float *b, float *out)\n{\n\tout[0]  = a[0] * b[0] + a[4] * b[1] + a[8] * b[2];\n\tout[1]  = a[1] * b[0] + a[5] * b[1] + a[9] * b[2];\n\tout[2]  = a[2] * b[0] + a[6] * b[1] + a[10] * b[2];\n\tout[3]  = a[3] * b[0] + a[7] * b[1] + a[11] * b[2] + b[3];\n\n\tout[4]  = a[0] * b[4] + a[4] * b[5] + a[8] * b[6];\n\tout[5]  = a[1] * b[4] + a[5] * b[5] + a[9] * b[6];\n\tout[6]  = a[2] * b[4] + a[6] * b[5] + a[10] * b[6];\n\tout[7]  = a[3] * b[4] + a[7] * b[5] + a[11] * b[6] + b[7];\n\n\tout[8]  = a[0] * b[8] + a[4] * b[9] + a[8] * b[10];\n\tout[9]  = a[1] * b[8] + a[5] * b[9] + a[9] * b[10];\n\tout[10] = a[2] * b[8] + a[6] * b[9] + a[10] * b[10];\n\tout[11] = a[3] * b[8] + a[7] * b[9] + a[11] * b[10] + b[11];\n}\n\n/*\n===================\nFloorDivMod\n\nReturns mathematically correct (floor-based) quotient and remainder for\nnumer and denom, both of which should contain no fractional part. The\nquotient must fit in 32 bits.\n====================\n*/\n\nvoid FloorDivMod (double numer, double denom, int *quotient,\n\t\tint *rem)\n{\n\tint\t\tq, r;\n\tdouble\tx;\n\n#ifdef PARANOID\n\tif (denom <= 0.0)\n\t\tSys_Error (\"FloorDivMod: bad denominator %f\\n\", denom);\n\n//\tif ((floor(numer) != numer) || (floor(denom) != denom))\n//\t\tSys_Error (\"FloorDivMod: non-integer numer or denom %f %f\\n\",\n//\t\t\t\tnumer, denom);\n#endif\n\n\tif (numer >= 0.0)\n\t{\n\n\t\tx = floor(numer / denom);\n\t\tq = (int)x;\n\t\tr = (int)floor(numer - (x * denom));\n\t}\n\telse\n\t{\n\t//\n\t// perform operations with positive values, and fix mod to make floor-based\n\t//\n\t\tx = floor(-numer / denom);\n\t\tq = -(int)x;\n\t\tr = (int)floor(-numer - (x * denom));\n\t\tif (r != 0)\n\t\t{\n\t\t\tq--;\n\t\t\tr = (int)denom - r;\n\t\t}\n\t}\n\n\t*quotient = q;\n\t*rem = r;\n}\n\n\n/*\n===================\nGreatestCommonDivisor\n====================\n*/\nint GreatestCommonDivisor (int i1, int i2)\n{\n\tif (i1 > i2)\n\t{\n\t\tif (i2 == 0)\n\t\t\treturn (i1);\n\t\treturn GreatestCommonDivisor (i2, i1 % i2);\n\t}\n\telse\n\t{\n\t\tif (i1 == 0)\n\t\t\treturn (i2);\n\t\treturn GreatestCommonDivisor (i1, i2 % i1);\n\t}\n}\n\n\n// TODO: move to nonintel.c\n\n/*\n===================\nInvert24To16\n\nInverts an 8.24 value to a 16.16 value\n====================\n*/\n\nfixed16_t Invert24To16(fixed16_t val)\n{\n\tif (val < 256)\n\t\treturn (0xFFFFFFFF);\n\n\treturn (fixed16_t)\n\t\t\t(((double)0x10000 * (double)0x1000000 / (double)val) + 0.5);\n}\n\n\n\n\n\n\n\n\n\nvoid VectorTransform (const vec3_t in1, const matrix3x4 in2, vec3_t out)\n{\n\tout[0] = DotProduct(in1, in2[0]) + in2[0][3];\n\tout[1] = DotProduct(in1, in2[1]) + in2[1][3];\n\tout[2] = DotProduct(in1, in2[2]) + in2[2][3];\n}\n\nvoid Bones_To_PosQuat4(int numbones, const float *matrix, short *result)\n{\t//I ripped this function out of DP. tweaked slightly.\n\t//http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm\n\tfloat origininvscale = 64;\n\tfloat origin[3];\n\tfloat quat[4];\n\tfloat quatscale;\n\twhile (numbones --> 0)\n\t{\n\t\tfloat trace = matrix[0*4+0] + matrix[1*4+1] + matrix[2*4+2];\n\t\torigin[0] = matrix[0*4+3];\n\t\torigin[1] = matrix[1*4+3];\n\t\torigin[2] = matrix[2*4+3];\n\t\tif(trace > 0)\n\t\t{\n\t\t\tfloat r = sqrt(1.0f + trace), inv = 0.5f / r;\n\t\t\tquat[0] = (matrix[2*4+1] - matrix[1*4+2]) * inv;\n\t\t\tquat[1] = (matrix[0*4+2] - matrix[2*4+0]) * inv;\n\t\t\tquat[2] = (matrix[1*4+0] - matrix[0*4+1]) * inv;\n\t\t\tquat[3] = 0.5f * r;\n\t\t}\n\t\telse if(matrix[0*4+0] > matrix[1*4+1] && matrix[0*4+0] > matrix[2*4+2])\n\t\t{\n\t\t\tfloat r = sqrt(1.0f + matrix[0*4+0] - matrix[1*4+1] - matrix[2*4+2]), inv = 0.5f / r;\n\t\t\tquat[0] = 0.5f * r;\n\t\t\tquat[1] = (matrix[1*4+0] + matrix[0*4+1]) * inv;\n\t\t\tquat[2] = (matrix[0*4+2] + matrix[2*4+0]) * inv;\n\t\t\tquat[3] = (matrix[2*4+1] - matrix[1*4+2]) * inv;\n\t\t}\n\t\telse if(matrix[1*4+1] > matrix[2*4+2])\n\t\t{\n\t\t\tfloat r = sqrt(1.0f + matrix[1*4+1] - matrix[0*4+0] - matrix[2*4+2]), inv = 0.5f / r;\n\t\t\tquat[0] = (matrix[1*4+0] + matrix[0*4+1]) * inv;\n\t\t\tquat[1] = 0.5f * r;\n\t\t\tquat[2] = (matrix[2*4+1] + matrix[1*4+2]) * inv;\n\t\t\tquat[3] = (matrix[0*4+2] - matrix[2*4+0]) * inv;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tfloat r = sqrt(1.0f + matrix[2*4+2] - matrix[0*4+0] - matrix[1*4+1]), inv = 0.5f / r;\n\t\t\tquat[0] = (matrix[0*4+2] + matrix[2*4+0]) * inv;\n\t\t\tquat[1] = (matrix[2*4+1] + matrix[1*4+2]) * inv;\n\t\t\tquat[2] = 0.5f * r;\n\t\t\tquat[3] = (matrix[1*4+0] - matrix[0*4+1]) * inv;\n\t\t}\n\t\t// normalize quaternion so that it is unit length\n\t\tquatscale = quat[0]*quat[0]+quat[1]*quat[1]+quat[2]*quat[2]+quat[3]*quat[3];\n\t\tif (quatscale)\n\t\t\tquatscale = (quat[3] >= 0 ? -32767.0f : 32767.0f) / sqrt(quatscale);\n\t\t// use a negative scale on the quat because the above function produces a\n\t\t// positive quat[3] and canonical quaternions have negative quat[3]\n\t\tresult[0] = origin[0] * origininvscale;\n\t\tresult[1] = origin[1] * origininvscale;\n\t\tresult[2] = origin[2] * origininvscale;\n\t\tresult[3] = quat[0] * quatscale;\n\t\tresult[4] = quat[1] * quatscale;\n\t\tresult[5] = quat[2] * quatscale;\n\t\tresult[6] = quat[3] * quatscale;\n\n\t\tmatrix += 12;\n\t\tresult += 7;\n\t}\n}\n\nvoid QDECL GenMatrixPosQuat4Scale(const vec3_t pos, const vec4_t quat, const vec3_t scale, float result[12])\n{\n\tfloat xx, xy, xz, xw, yy, yz, yw, zz, zw;\n\tfloat x2, y2, z2;\n\tfloat s;\n\tx2 = quat[0] + quat[0];\n\ty2 = quat[1] + quat[1];\n\tz2 = quat[2] + quat[2];\n\n\txx = quat[0] * x2;   xy = quat[0] * y2;   xz = quat[0] * z2;\n\tyy = quat[1] * y2;   yz = quat[1] * z2;   zz = quat[2] * z2;\n\txw = quat[3] * x2;   yw = quat[3] * y2;   zw = quat[3] * z2;\n\n\ts = scale[0];\n\tresult[0*4+0] = s*(1.0f - (yy + zz));\n\tresult[1*4+0] = s*(xy + zw);\n\tresult[2*4+0] = s*(xz - yw);\n\n\ts = scale[1];\n\tresult[0*4+1] = s*(xy - zw);\n\tresult[1*4+1] = s*(1.0f - (xx + zz));\n\tresult[2*4+1] = s*(yz + xw);\n\n\ts = scale[2];\n\tresult[0*4+2] = s*(xz + yw);\n\tresult[1*4+2] = s*(yz - xw);\n\tresult[2*4+2] = s*(1.0f - (xx + yy));\n\n\tresult[0*4+3]  =     pos[0];\n\tresult[1*4+3]  =     pos[1];\n\tresult[2*4+3]  =     pos[2];\n}\n#if 0//def HALFLIFEMODELS\n\nstatic void AngleQuaternion( const vec3_t angles, vec4_t quaternion )\n{\n\tfloat\t\tangle;\n\tfloat\t\tsr, sp, sy, cr, cp, cy;\n\n\t// FIXME: rescale the inputs to 1/2 angle\n\tangle = angles[2] * 0.5;\n\tsy = sin(angle);\n\tcy = cos(angle);\n\tangle = angles[1] * 0.5;\n\tsp = sin(angle);\n\tcp = cos(angle);\n\tangle = angles[0] * 0.5;\n\tsr = sin(angle);\n\tcr = cos(angle);\n\n\tquaternion[0] = sr*cp*cy-cr*sp*sy; // X\n\tquaternion[1] = cr*sp*cy+sr*cp*sy; // Y\n\tquaternion[2] = cr*cp*sy-sr*sp*cy; // Z\n\tquaternion[3] = cr*cp*cy+sr*sp*sy; // W\n}\n\nstatic void QuaternionMatrix( const vec4_t quaternion, float (*matrix)[4] )\n{\n\n\tmatrix[0][0] = 1.0 - 2.0 * quaternion[1] * quaternion[1] - 2.0 * quaternion[2] * quaternion[2];\n\tmatrix[1][0] = 2.0 * quaternion[0] * quaternion[1] + 2.0 * quaternion[3] * quaternion[2];\n\tmatrix[2][0] = 2.0 * quaternion[0] * quaternion[2] - 2.0 * quaternion[3] * quaternion[1];\n\n\tmatrix[0][1] = 2.0 * quaternion[0] * quaternion[1] - 2.0 * quaternion[3] * quaternion[2];\n\tmatrix[1][1] = 1.0 - 2.0 * quaternion[0] * quaternion[0] - 2.0 * quaternion[2] * quaternion[2];\n\tmatrix[2][1] = 2.0 * quaternion[1] * quaternion[2] + 2.0 * quaternion[3] * quaternion[0];\n\n\tmatrix[0][2] = 2.0 * quaternion[0] * quaternion[2] + 2.0 * quaternion[3] * quaternion[1];\n\tmatrix[1][2] = 2.0 * quaternion[1] * quaternion[2] - 2.0 * quaternion[3] * quaternion[0];\n\tmatrix[2][2] = 1.0 - 2.0 * quaternion[0] * quaternion[0] - 2.0 * quaternion[1] * quaternion[1];\n}\n#endif\nvoid QuaternionSlerp( const vec4_t p, vec4_t q, float t, vec4_t qt )\n{\n\tint i;\n\tfloat omega, cosom, sinom, sclp, sclq;\n\n\t// decide if one of the quaternions is backwards\n\tfloat a = 0;\n\tfloat b = 0;\n\tfor (i = 0; i < 4; i++) {\n\t\ta += (p[i]-q[i])*(p[i]-q[i]);\n\t\tb += (p[i]+q[i])*(p[i]+q[i]);\n\t}\n\tif (a > b) {\n\t\tfor (i = 0; i < 4; i++) {\n\t\t\tq[i] = -q[i];\n\t\t}\n\t}\n\n\tcosom = p[0]*q[0] + p[1]*q[1] + p[2]*q[2] + p[3]*q[3];\n\n\tif ((1.0 + cosom) > 0.00000001) {\n\t\tif ((1.0 - cosom) > 0.00000001) {\n\t\t\tomega = acos( cosom );\n\t\t\tsinom = sin( omega );\n\t\t\tsclp = sin( (1.0 - t)*omega) / sinom;\n\t\t\tsclq = sin( t*omega ) / sinom;\n\t\t}\n\t\telse {\n\t\t\tsclp = 1.0 - t;\n\t\t\tsclq = t;\n\t\t}\n\t\tfor (i = 0; i < 4; i++) {\n\t\t\tqt[i] = sclp * p[i] + sclq * q[i];\n\t\t}\n\t}\n\telse {\n\t\tqt[0] = -p[1];\n\t\tqt[1] = p[0];\n\t\tqt[2] = -p[3];\n\t\tqt[3] = p[2];\n\t\tsclp = sin( (1.0 - t) * 0.5 * M_PI);\n\t\tsclq = sin( t * 0.5 * M_PI);\n\t\tfor (i = 0; i < 4; i++) {\n\t\t\tqt[i] = sclp * p[i] + sclq * qt[i];\n\t\t}\n\t}\n}\n\n//This function is GL stylie (use as 2nd arg to ML_MultMatrix4).\nfloat *Matrix4x4_CM_NewRotation(float a, float x, float y, float z)\n{\n\tstatic float ret[16];\n\tfloat c = cos(a* M_PI / 180.0);\n\tfloat s = sin(a* M_PI / 180.0);\n\n\tret[0] = x*x*(1-c)+c;\n\tret[4] = x*y*(1-c)-z*s;\n\tret[8] = x*z*(1-c)+y*s;\n\tret[12] = 0;\n\n\tret[1] = y*x*(1-c)+z*s;\n    ret[5] = y*y*(1-c)+c;\n\tret[9] = y*z*(1-c)-x*s;\n\tret[13] = 0;\n\n\tret[2] = x*z*(1-c)-y*s;\n\tret[6] = y*z*(1-c)+x*s;\n\tret[10] = z*z*(1-c)+c;\n\tret[14] = 0;\n\n\tret[3] = 0;\n\tret[7] = 0;\n\tret[11] = 0;\n\tret[15] = 1;\n\treturn ret;\n}\n\n//This function is GL stylie (use as 2nd arg to ML_MultMatrix4).\nfloat *Matrix4x4_CM_NewTranslation(float x, float y, float z)\n{\n\tstatic float ret[16];\n\tret[0] = 1;\n\tret[4] = 0;\n\tret[8] = 0;\n\tret[12] = x;\n\n\tret[1] = 0;\n    ret[5] = 1;\n\tret[9] = 0;\n\tret[13] = y;\n\n\tret[2] = 0;\n\tret[6] = 0;\n\tret[10] = 1;\n\tret[14] = z;\n\n\tret[3] = 0;\n\tret[7] = 0;\n\tret[11] = 0;\n\tret[15] = 1;\n\treturn ret;\n}\n\n//be aware that this generates two sorts of matricies depending on order of a+b\nvoid Matrix4_Multiply(const float *a, const float *b, float *out)\n{\n\tout[0]  = a[0] * b[0] + a[4] * b[1] + a[8] * b[2] + a[12] * b[3];\n\tout[1]  = a[1] * b[0] + a[5] * b[1] + a[9] * b[2] + a[13] * b[3];\n\tout[2]  = a[2] * b[0] + a[6] * b[1] + a[10] * b[2] + a[14] * b[3];\n\tout[3]  = a[3] * b[0] + a[7] * b[1] + a[11] * b[2] + a[15] * b[3];\n\n\tout[4]  = a[0] * b[4] + a[4] * b[5] + a[8] * b[6] + a[12] * b[7];\n\tout[5]  = a[1] * b[4] + a[5] * b[5] + a[9] * b[6] + a[13] * b[7];\n\tout[6]  = a[2] * b[4] + a[6] * b[5] + a[10] * b[6] + a[14] * b[7];\n\tout[7]  = a[3] * b[4] + a[7] * b[5] + a[11] * b[6] + a[15] * b[7];\n\n\tout[8]  = a[0] * b[8] + a[4] * b[9] + a[8] * b[10] + a[12] * b[11];\n\tout[9]  = a[1] * b[8] + a[5] * b[9] + a[9] * b[10] + a[13] * b[11];\n\tout[10] = a[2] * b[8] + a[6] * b[9] + a[10] * b[10] + a[14] * b[11];\n\tout[11] = a[3] * b[8] + a[7] * b[9] + a[11] * b[10] + a[15] * b[11];\n\n\tout[12] = a[0] * b[12] + a[4] * b[13] + a[8] * b[14] + a[12] * b[15];\n\tout[13] = a[1] * b[12] + a[5] * b[13] + a[9] * b[14] + a[13] * b[15];\n\tout[14] = a[2] * b[12] + a[6] * b[13] + a[10] * b[14] + a[14] * b[15];\n\tout[15] = a[3] * b[12] + a[7] * b[13] + a[11] * b[14] + a[15] * b[15];\n}\n\nvoid Matrix3x4_RM_Transform3(const float *matrix, const float *vector, float *product)\n{\n\tproduct[0] = matrix[0]*vector[0] + matrix[1]*vector[1] + matrix[2]*vector[2] + matrix[3];\n\tproduct[1] = matrix[4]*vector[0] + matrix[5]*vector[1] + matrix[6]*vector[2] + matrix[7];\n\tproduct[2] = matrix[8]*vector[0] + matrix[9]*vector[1] + matrix[10]*vector[2] + matrix[11];\n}\nvoid Matrix3x4_RM_Transform3x3(const float *matrix, const float *vector, float *product)\n{\n\tproduct[0] = matrix[0]*vector[0] + matrix[1]*vector[1] + matrix[2]*vector[2];\n\tproduct[1] = matrix[4]*vector[0] + matrix[5]*vector[1] + matrix[6]*vector[2];\n\tproduct[2] = matrix[8]*vector[0] + matrix[9]*vector[1] + matrix[10]*vector[2];\n}\n\n//transform 4d vector by a 4d matrix.\nvoid Matrix4x4_CM_Transform4(const float *matrix, const float *vector, float *product)\n{\n\tproduct[0] = matrix[0]*vector[0] + matrix[4]*vector[1] + matrix[8]*vector[2] + matrix[12]*vector[3];\n\tproduct[1] = matrix[1]*vector[0] + matrix[5]*vector[1] + matrix[9]*vector[2] + matrix[13]*vector[3];\n\tproduct[2] = matrix[2]*vector[0] + matrix[6]*vector[1] + matrix[10]*vector[2] + matrix[14]*vector[3];\n\tproduct[3] = matrix[3]*vector[0] + matrix[7]*vector[1] + matrix[11]*vector[2] + matrix[15]*vector[3];\n}\n\n//ignore the entire right+bottom row/column of the 4*4 matrix\nvoid Matrix4x4_CM_Transform3x3(const float *matrix, const float *vector, float *product)\n{\n\tproduct[0] = matrix[0]*vector[0] + matrix[4]*vector[1] + matrix[8]*vector[2];\n\tproduct[1] = matrix[1]*vector[0] + matrix[5]*vector[1] + matrix[9]*vector[2];\n\tproduct[2] = matrix[2]*vector[0] + matrix[6]*vector[1] + matrix[10]*vector[2];\n}\n\n//disregard the extra bit of the matrix\nvoid Matrix4x4_CM_Transform3(const float *matrix, const float *vector, float *product)\n{\n\tproduct[0] = matrix[0]*vector[0] + matrix[4]*vector[1] + matrix[8]*vector[2] + matrix[12];\n\tproduct[1] = matrix[1]*vector[0] + matrix[5]*vector[1] + matrix[9]*vector[2] + matrix[13];\n\tproduct[2] = matrix[2]*vector[0] + matrix[6]*vector[1] + matrix[10]*vector[2] + matrix[14];\n}\nvoid Matrix4x4_CM_Transform34(const float *matrix, const vec3_t vector, vec4_t product)\n{\n\t//transform as though vector[3] == 1\n\tproduct[0] = matrix[0]*vector[0] + matrix[4]*vector[1] + matrix[8]*vector[2] + matrix[12];\n\tproduct[1] = matrix[1]*vector[0] + matrix[5]*vector[1] + matrix[9]*vector[2] + matrix[13];\n\tproduct[2] = matrix[2]*vector[0] + matrix[6]*vector[1] + matrix[10]*vector[2] + matrix[14];\n\tproduct[3] = matrix[3]*vector[0] + matrix[7]*vector[1] + matrix[11]*vector[2] + matrix[15];\n}\n\nvoid Matrix4x4_CM_ModelViewMatrix(float *modelview, const vec3_t viewangles, const vec3_t vieworg)\n{\n#if 1\n\tfloat *out = modelview;\n\tfloat cp = cos(-viewangles[0] * M_PI / 180.0);\n\tfloat sp = sin(-viewangles[0] * M_PI / 180.0);\n\tfloat cy = cos(-viewangles[1] * M_PI / 180.0);\n\tfloat sy = sin(-viewangles[1] * M_PI / 180.0);\n\tfloat cr = cos(-viewangles[2] * M_PI / 180.0);\n\tfloat sr = sin(-viewangles[2] * M_PI / 180.0);\n\n\tout[0]  = -sr*sp*cy - cr*sy;\n\tout[1]  = -cr*sp*cy + sr*sy;\n\tout[2]  = -cp*cy;\n\tout[3]  = 0;\n\tout[4]  = sr*sp*sy - cr*cy;\n\tout[5]  = cr*sp*sy + sr*cy;\n\tout[6]  = cp*sy;\n\tout[7]  = 0;\n\tout[8]  = sr*cp;\n\tout[9]  = cr*cp;\n\tout[10] = -sp;\n\tout[11] = 0;\n\tout[12] =   - out[0]*vieworg[0] - out[4]*vieworg[1] - out[ 8]*vieworg[2];\n\tout[13] =   - out[1]*vieworg[0] - out[5]*vieworg[1] - out[ 9]*vieworg[2];\n\tout[14] =   - out[2]*vieworg[0] - out[6]*vieworg[1] - out[10]*vieworg[2];\n\tout[15] = 1 - out[3]*vieworg[0] - out[7]*vieworg[1] - out[11]*vieworg[2];\n#else\n\tfloat tempmat[16];\n\t//load identity.\n\tmemset(modelview, 0, sizeof(*modelview)*16);\n#if FULLYGL\n\tmodelview[0] = 1;\n\tmodelview[5] = 1;\n\tmodelview[10] = 1;\n\tmodelview[15] = 1;\n\n\tMatrix4_Multiply(modelview, Matrix4_CM_NewRotation(-90,  1, 0, 0), tempmat);\t    // put Z going up\n\tMatrix4_Multiply(tempmat, Matrix4_CM_NewRotation(90,  0, 0, 1), modelview);\t    // put Z going up\n#else\n\t//use this lame wierd and crazy identity matrix..\n\tmodelview[2] = -1;\n\tmodelview[4] = -1;\n\tmodelview[9] = 1;\n\tmodelview[15] = 1;\n#endif\n\t//figure out the current modelview matrix\n\n\t//I would if some of these, but then I'd still need a couple of copys\n\tMatrix4_Multiply(modelview, Matrix4x4_CM_NewRotation(-viewangles[2],  1, 0, 0), tempmat);\n\tMatrix4_Multiply(tempmat, Matrix4x4_CM_NewRotation(-viewangles[0],  0, 1, 0), modelview);\n\tMatrix4_Multiply(modelview, Matrix4x4_CM_NewRotation(-viewangles[1],  0, 0, 1), tempmat);\n\n\tMatrix4_Multiply(tempmat, Matrix4x4_CM_NewTranslation(-vieworg[0],  -vieworg[1],  -vieworg[2]), modelview);\t    // put Z going up\n#endif\n}\n\nvoid Matrix4x4_CM_CreateTranslate (float *out, float x, float y, float z)\n{\n\tout[0] = 1;\n\tout[1] = 0;\n\tout[2] = 0;\n\tout[3] = 0;\n\n\tout[4] = 0;\n    out[5] = 1;\n\tout[6] = 0;\n\tout[7] = 0;\n\n\tout[8] = 0;\n\tout[9] = 0;\n\tout[10] = 1;\n\tout[11] = 0;\n\n\tout[12] = x;\n\tout[13] = y;\n\tout[14] = z;\n\tout[15] = 1;\n}\n\nvoid Matrix4x4_RM_CreateTranslate (float *out, float x, float y, float z)\n{\n\tout[0] = 1;\n\tout[4] = 0;\n\tout[8] = 0;\n\tout[12] = 0;\n\n\tout[1] = 0;\n    out[5] = 1;\n\tout[9] = 0;\n\tout[13] = 0;\n\n\tout[2] = 0;\n\tout[6] = 0;\n\tout[10] = 1;\n\tout[14] = 0;\n\n\tout[3] = x;\n\tout[7] = y;\n\tout[11] = z;\n\tout[15] = 1;\n}\n\nvoid Matrix4x4_CM_LightMatrixFromAxis(float *modelview, const vec3_t px, const vec3_t py, const vec3_t pz, const vec3_t org)\n{\n\tmodelview[ 0] = px[0];\n\tmodelview[ 1] = py[0];\n\tmodelview[ 2] = pz[0];\n\tmodelview[ 3] = 0;\n\tmodelview[ 4] = px[1];\n\tmodelview[ 5] = py[1];\n\tmodelview[ 6] = pz[1];\n\tmodelview[ 7] = 0;\n\tmodelview[ 8] = px[2];\n\tmodelview[ 9] = py[2];\n\tmodelview[10] = pz[2];\n\tmodelview[11] = 0;\n\tmodelview[12] = -(px[0]*org[0] + px[1]*org[1] + px[2]*org[2]);\n\tmodelview[13] = -(py[0]*org[0] + py[1]*org[1] + py[2]*org[2]);\n\tmodelview[14] = -(pz[0]*org[0] + pz[1]*org[1] + pz[2]*org[2]);\n\tmodelview[15] = 1;\n}\n\nvoid Matrix4x4_CM_ModelViewMatrixFromAxis(float *modelview, const vec3_t pn, const vec3_t right, const vec3_t up, const vec3_t vieworg)\n{\n\tfloat tempmat[16];\n\n\ttempmat[ 0] = right[0];\n\ttempmat[ 1] = up[0];\n\ttempmat[ 2] = -pn[0];\n\ttempmat[ 3] = 0;\n\ttempmat[ 4] = right[1];\n\ttempmat[ 5] = up[1];\n\ttempmat[ 6] = -pn[1];\n\ttempmat[ 7] = 0;\n\ttempmat[ 8] = right[2];\n\ttempmat[ 9] = up[2];\n\ttempmat[10] = -pn[2];\n\ttempmat[11] = 0;\n\ttempmat[12] = 0;\n\ttempmat[13] = 0;\n\ttempmat[14] = 0;\n\ttempmat[15] = 1;\n\n\tMatrix4_Multiply(tempmat, Matrix4x4_CM_NewTranslation(-vieworg[0],  -vieworg[1],  -vieworg[2]), modelview);\t    // put Z going up\n}\n\n\nvoid Matrix3x4_RM_FromAngles(const vec3_t angles, const vec3_t origin, float *out)\n{\n\tfloat\t\tangle;\n\tfloat\t\tsr, sp, sy, cr, cp, cy;\n\n\tangle = angles[YAW] * (M_PI*2 / 360);\n\tsy = sin(angle);\n\tcy = cos(angle);\n\tangle = angles[PITCH] * (M_PI*2 / 360);\n\tsp = sin(angle);\n\tcp = cos(angle);\n\tangle = angles[ROLL] * (M_PI*2 / 360);\n\tsr = sin(angle);\n\tcr = cos(angle);\n\n\tout[0] = cp*cy;\n\tout[1] = (sr*sp*cy+cr*-sy);\n\tout[2] = (cr*sp*cy+-sr*-sy);\n\tout[3] = origin[0];\n\n\tout[4] = cp*sy;\n\tout[5] = (sr*sp*sy+cr*cy);\n\tout[6] = (cr*sp*sy+-sr*cy);\n\tout[7] = origin[1];\n\n\tout[8] = -sp;\n\tout[9] = sr*cp;\n\tout[10] = cr*cp;\n\tout[11] = origin[2];\n}\nvoid Matrix3x4_RM_ToVectors(const float *in, float vx[3], float vy[3], float vz[3], float t[3])\n{\n\tvx[0] = in[0];\n\tvx[1] = in[4];\n\tvx[2] = in[8];\n\n\tvy[0] = in[1];\n\tvy[1] = in[5];\n\tvy[2] = in[9];\n\n\tvz[0] = in[2];\n\tvz[1] = in[6];\n\tvz[2] = in[10];\n\n\tt [0] = in[3];\n\tt [1] = in[7];\n\tt [2] = in[11];\n}\n\nvoid Matrix4x4_RM_FromVectors(float *out, const float vx[3], const float vy[3], const float vz[3], const float t[3])\n{\n\tout[0] = vx[0];\n\tout[1] = vy[0];\n\tout[2] = vz[0];\n\tout[3] = t[0];\n\tout[4] = vx[1];\n\tout[5] = vy[1];\n\tout[6] = vz[1];\n\tout[7] = t[1];\n\tout[8] = vx[2];\n\tout[9] = vy[2];\n\tout[10] = vz[2];\n\tout[11] = t[2];\n\tout[12] = 0.0f;\n\tout[13] = 0.0f;\n\tout[14] = 0.0f;\n\tout[15] = 1.0f;\n}\n\nvoid Matrix3x4_RM_FromVectors(float *out, const float vx[3], const float vy[3], const float vz[3], const float t[3])\n{\n\tout[0] = vx[0];\n\tout[1] = vy[0];\n\tout[2] = vz[0];\n\tout[3] = t[0];\n\tout[4] = vx[1];\n\tout[5] = vy[1];\n\tout[6] = vz[1];\n\tout[7] = t[1];\n\tout[8] = vx[2];\n\tout[9] = vy[2];\n\tout[10] = vz[2];\n\tout[11] = t[2];\n}\n\nvoid Matrix4x4_CM_ModelMatrixFromAxis(float *modelview, const vec3_t pn, const vec3_t right, const vec3_t up, const vec3_t vieworg)\n{\n\tfloat tempmat[16];\n\n\ttempmat[ 0] = pn[0];\n\ttempmat[ 1] = pn[1];\n\ttempmat[ 2] = pn[2];\n\ttempmat[ 3] = 0;\n\ttempmat[ 4] = right[0];\n\ttempmat[ 5] = right[1];\n\ttempmat[ 6] = right[2];\n\ttempmat[ 7] = 0;\n\ttempmat[ 8] = up[0];\n\ttempmat[ 9] = up[1];\n\ttempmat[10] = up[2];\n\ttempmat[11] = 0;\n\ttempmat[12] = 0;\n\ttempmat[13] = 0;\n\ttempmat[14] = 0;\n\ttempmat[15] = 1;\n\n\tMatrix4_Multiply(Matrix4x4_CM_NewTranslation(vieworg[0],  vieworg[1],  vieworg[2]), tempmat, modelview);\t    // put Z going up\n}\n\nvoid Matrix4x4_CM_ModelMatrix(float *modelview, vec_t x, vec_t y, vec_t z, vec_t pitch, vec_t yaw, vec_t roll, vec_t scale)\n{\n\tfloat tempmat[16];\n\t//load identity.\n\tmemset(modelview, 0, sizeof(*modelview)*16);\n#if FULLYGL\n\tmodelview[0] = 1;\n\tmodelview[5] = 1;\n\tmodelview[10] = 1;\n\tmodelview[15] = 1;\n\n\tMatrix4_Multiply(modelview, Matrix4x4_CM_NewRotation(-90,  1, 0, 0), tempmat);\t    // put Z going up\n\tMatrix4_Multiply(tempmat, Matrix4x4_CM_NewRotation(90,  0, 0, 1), modelview);\t    // put Z going up\n#else\n\t//use this lame wierd and crazy identity matrix..\n\tmodelview[2] = -1;\n\tmodelview[4] = -1;\n\tmodelview[9] = 1;\n\tmodelview[15] = 1;\n#endif\n\t//figure out the current modelview matrix\n\n\t//I would if some of these, but then I'd still need a couple of copys\n\tMatrix4_Multiply(modelview, Matrix4x4_CM_NewRotation(-roll,  1, 0, 0), tempmat);\n\tMatrix4_Multiply(tempmat, Matrix4x4_CM_NewRotation(-pitch,  0, 1, 0), modelview);\n\tMatrix4_Multiply(modelview, Matrix4x4_CM_NewRotation(-yaw,  0, 0, 1), tempmat);\n\n\tMatrix4_Multiply(tempmat, Matrix4x4_CM_NewTranslation(x,  y,  z), modelview);\n}\n\nvoid Matrix4x4_Identity(float *outm)\n{\n\toutm[ 0] = 1;\n\toutm[ 1] = 0;\n\toutm[ 2] = 0;\n\toutm[ 3] = 0;\n\toutm[ 4] = 0;\n\toutm[ 5] = 1;\n\toutm[ 6] = 0;\n\toutm[ 7] = 0;\n\toutm[ 8] = 0;\n\toutm[ 9] = 0;\n\toutm[10] = 1;\n\toutm[11] = 0;\n\toutm[12] = 0;\n\toutm[13] = 0;\n\toutm[14] = 0;\n\toutm[15] = 1;\n}\n\nvoid Matrix4x4_CM_Projection_Offset(float *proj, float fovl, float fovr, float fovd, float fovu, float neard, float fard, qboolean d3d)\n{\n\tdouble dn = (d3d?0:-1), df = 1;\t//d3d outputs near as 0, opengl has near as -1. that's the only difference.\n\tdouble ymax = tan( fovu * M_PI / 180.0 );\n\tdouble ymin = tan( fovd * M_PI / 180.0 );\n\tdouble xmin = tan( fovl * M_PI / 180.0 );\n\tdouble xmax = tan( fovr * M_PI / 180.0 );\n\n\tif (fard <= neard)\n\t{\t//switch to an infinite projection\n\t\tconst double epsilon = 1.0/(1<<22);\n\n\t\tproj[0] = (2) / (xmax - xmin);\n\t\tproj[4] = 0;\n\t\tproj[8] = (xmax + xmin) / (xmax - xmin);\n\t\tproj[12] = 0;\n\n\t\tproj[1] = 0;\n\t\tproj[5] = (2) / (ymax - ymin);\n\t\tproj[9] = (ymax + ymin) / (ymax - ymin);\n\t\tproj[13] = 0;\n\n\t\tproj[2] = 0;\n\t\tproj[6] = 0;\n\t\tproj[10] = epsilon-1;\n\t\tproj[14] = (epsilon-(df-dn))*neard;\n\n\t\tproj[3] = 0;\n\t\tproj[7] = 0;\n\t\tproj[11] = -1;\n\t\tproj[15] = 0;\n\t}\n\telse\n\t{\n\t\tproj[0] = (2) / (xmax - xmin);\n\t\tproj[4] = 0;\n\t\tproj[8] = (xmax + xmin) / (xmax - xmin);\n\t\tproj[12] = 0;\n\n\t\tproj[1] = 0;\n\t\tproj[5] = (2) / (ymax - ymin);\n\t\tproj[9] = (ymax + ymin) / (ymax - ymin);\n\t\tproj[13] = 0;\n\n\t\tproj[2] = 0;\n\t\tproj[6] = 0;\n\t\tproj[10] = (fard*df-neard*dn)/(neard-fard);\n\t\tproj[14] = ((df-dn)*fard*neard)/(neard-fard);\n\n\t\tproj[3] = 0;\n\t\tproj[7] = 0;\n\t\tproj[11] = -1;\n\t\tproj[15] = 0;\n\t}\n}\n\nvoid Matrix4x4_CM_Projection_Far(float *proj, float fovx, float fovy, float neard, float fard, qboolean d3d)\n{\n\tdouble xmin, xmax, ymin, ymax;\n\tdouble dn = (d3d?0:-1), df = 1;\n\n\t//proj\n\tymax = neard * tan( fovy * M_PI / 360.0 );\n\tymin = -ymax;\n\n\tif (fovx == fovy)\n\t{\n\t\txmax = ymax;\n\t\txmin = ymin;\n\t}\n\telse\n\t{\n\t\txmax = neard * tan( fovx * M_PI / 360.0 );\n\t\txmin = -xmax;\n\t}\n\n\tproj[0] = (2*neard) / (xmax - xmin);\n\tproj[4] = 0;\n\tproj[8] = (xmax + xmin) / (xmax - xmin);\n\tproj[12] = 0;\n\n\tproj[1] = 0;\n\tproj[5] = (2*neard) / (ymax - ymin);\n\tproj[9] = (ymax + ymin) / (ymax - ymin);\n\tproj[13] = 0;\n\n\tproj[2] = 0;\n\tproj[6] = 0;\n\tproj[10] = (fard*df-neard*dn)/(neard-fard);\n\tproj[14] = ((df-dn)*fard*neard)/(neard-fard);\n\t\n\tproj[3] = 0;\n\tproj[7] = 0;\n\tproj[11] = -1;\n\tproj[15] = 0;\n}\n\nvoid Matrix4x4_CM_Projection_Inf(float *proj, float fovx, float fovy, float neard, qboolean d3d)\n{\n//FIXME: glDepthRange(1,0) for reverse-z (with cull flipped). combine with arb_clip_control for 0-1. this should give much better depth precision with floating point depth buffers.\n\tfloat xmin, xmax, ymin, ymax;\n\tdouble dn = (d3d?0:-1), df = 1;\n\n\t//proj\n\tymax = neard * tan( fovy * M_PI / 360.0 );\n\tymin = -ymax;\n\n\tif (fovx == fovy)\n\t{\n\t\txmax = ymax;\n\t\txmin = ymin;\n\t}\n\telse\n\t{\n\t\txmax = neard * tan( fovx * M_PI / 360.0 );\n\t\txmin = -xmax;\n\t}\n\n\tproj[0] = (2*neard) / (xmax - xmin);\n\tproj[4] = 0;\n\tproj[8] = (xmax + xmin) / (xmax - xmin);\n\tproj[12] = 0;\n\n\tproj[1] = 0;\n\tproj[5] = (2*neard) / (ymax - ymin);\n\tproj[9] = (ymax + ymin) / (ymax - ymin);\n\tproj[13] = 0;\n\n#if 1\n\t{\n\t\tconst double epsilon = 1.0/(1<<22);\n\t\tproj[2] = 0;\n\t\tproj[6] = 0;\n\t\tproj[10] = epsilon-1;\n\t\tproj[14] = (epsilon-(df-dn))*neard;\n\t}\n#elif 1\n\t{\t//mathematical target\n\t\tconst float fard = (1<<22);\n\t\tproj[2] = 0;\n\t\tproj[6] = 0;\n\t\tproj[10] = (fard*df-neard*dn)/(neard-fard);\n\t\tproj[14] = ((df-dn)*fard*neard)/(neard-fard);\n\t}\n#else\n\t//old logic\n\tproj[2] = 0;\n\tproj[6] = 0;\n\tproj[10] = -1  * ((float)(1<<21)/(1<<22));\n\tproj[14] = -2*neard;\n#endif\n\t\n\tproj[3] = 0;\n\tproj[7] = 0;\n\tproj[11] = -1;\n\tproj[15] = 0;\n}\nvoid Matrix4x4_CM_Projection2(float *proj, float fovx, float fovy, float neard)\n{\n\tfloat xmin, xmax, ymin, ymax;\n\tfloat nudge = 1;\n\n\t//proj\n\tymax = neard * tan( fovy * M_PI / 360.0 );\n\tymin = -ymax;\n\n\txmax = neard * tan( fovx * M_PI / 360.0 );\n\txmin = -xmax;\n\n\tproj[0] = (2*neard) / (xmax - xmin);\n\tproj[4] = 0;\n\tproj[8] = (xmax + xmin) / (xmax - xmin);\n\tproj[12] = 0;\n\n\tproj[1] = 0;\n\tproj[5] = (2*neard) / (ymax - ymin);\n\tproj[9] = (ymax + ymin) / (ymax - ymin);\n\tproj[13] = 0;\n\n\tproj[2] = 0;\n\tproj[6] = 0;\n\tproj[10] = -1  * nudge;\n\tproj[14] = -2*neard * nudge;\n\t\n\tproj[3] = 0;\n\tproj[7] = 0;\n\tproj[11] = -1;\n\tproj[15] = 0;\n}\n\nvoid Matrix4x4_CM_Orthographic(float *proj, float xmin, float xmax, float ymin, float ymax,\n\t\t     float znear, float zfar)\n{\n\tproj[0] = 2/(xmax-xmin);\n\tproj[4] = 0;\n\tproj[8] = 0;\n\tproj[12] = -(xmax+xmin)/(xmax-xmin);\n\n\tproj[1] = 0;\n\tproj[5] = 2/(ymax-ymin);\n\tproj[9] = 0;\n\tproj[13] = -(ymax+ymin)/(ymax-ymin);\n\n\tproj[2] = 0;\n\tproj[6] = 0;\n\tproj[10] = -2/(zfar-znear);\n\tproj[14] = -(zfar+znear)/(zfar-znear);\n\t\n\tproj[3] = 0;\n\tproj[7] = 0;\n\tproj[11] = 0;\n\tproj[15] = 1;\n}\nvoid Matrix4x4_CM_OrthographicD3D(float *proj, float xmin, float xmax, float ymax, float ymin,\n\t\t     float znear, float zfar)\n{\n\tproj[0] = 2/(xmax-xmin);\n\tproj[4] = 0;\n\tproj[8] = 0;\n\tproj[12] = (xmax+xmin)/(xmin-xmax);\n\n\tproj[1] = 0;\n\tproj[5] = 2/(ymax-ymin);\n\tproj[9] = 0;\n\tproj[13] = (ymax+ymin)/(ymin-ymax);\n\n\tproj[2] = 0;\n\tproj[6] = 0;\n\tproj[10] = 1/(znear-zfar);\n\tproj[14] = znear/(znear-zfar);\n\t\n\tproj[3] = 0;\n\tproj[7] = 0;\n\tproj[11] = 0;\n\tproj[15] = 1;\n}\n/*\n * Compute inverse of 4x4 transformation matrix.\n * Code contributed by Jacques Leroy jle@star.be\n * Return true for success, false for failure (singular matrix)\n * This came to FTE via mesa's GLU.\n */\nqboolean Matrix4_Invert(const float *m, float *out)\n{\n/* NB. OpenGL Matrices are COLUMN major. */\n#define SWAP_ROWS(a, b) { float *_tmp = a; (a)=(b); (b)=_tmp; }\n#define MAT(m,r,c) (m)[(c)*4+(r)]\n\n   float wtmp[4][8];\n   float m0, m1, m2, m3, s;\n   float *r0, *r1, *r2, *r3;\n\n   r0 = wtmp[0], r1 = wtmp[1], r2 = wtmp[2], r3 = wtmp[3];\n\n   r0[0] = MAT(m, 0, 0), r0[1] = MAT(m, 0, 1),\n      r0[2] = MAT(m, 0, 2), r0[3] = MAT(m, 0, 3),\n      r0[4] = 1.0, r0[5] = r0[6] = r0[7] = 0.0,\n      r1[0] = MAT(m, 1, 0), r1[1] = MAT(m, 1, 1),\n      r1[2] = MAT(m, 1, 2), r1[3] = MAT(m, 1, 3),\n      r1[5] = 1.0, r1[4] = r1[6] = r1[7] = 0.0,\n      r2[0] = MAT(m, 2, 0), r2[1] = MAT(m, 2, 1),\n      r2[2] = MAT(m, 2, 2), r2[3] = MAT(m, 2, 3),\n      r2[6] = 1.0, r2[4] = r2[5] = r2[7] = 0.0,\n      r3[0] = MAT(m, 3, 0), r3[1] = MAT(m, 3, 1),\n      r3[2] = MAT(m, 3, 2), r3[3] = MAT(m, 3, 3),\n      r3[7] = 1.0, r3[4] = r3[5] = r3[6] = 0.0;\n\n   /* choose pivot - or die */\n   if (fabs(r3[0]) > fabs(r2[0]))\n      SWAP_ROWS(r3, r2);\n   if (fabs(r2[0]) > fabs(r1[0]))\n      SWAP_ROWS(r2, r1);\n   if (fabs(r1[0]) > fabs(r0[0]))\n      SWAP_ROWS(r1, r0);\n   if (0.0 == r0[0])\n      return false;\n\n   /* eliminate first variable     */\n   m1 = r1[0] / r0[0];\n   m2 = r2[0] / r0[0];\n   m3 = r3[0] / r0[0];\n   s = r0[1];\n   r1[1] -= m1 * s;\n   r2[1] -= m2 * s;\n   r3[1] -= m3 * s;\n   s = r0[2];\n   r1[2] -= m1 * s;\n   r2[2] -= m2 * s;\n   r3[2] -= m3 * s;\n   s = r0[3];\n   r1[3] -= m1 * s;\n   r2[3] -= m2 * s;\n   r3[3] -= m3 * s;\n   s = r0[4];\n   if (s != 0.0) {\n      r1[4] -= m1 * s;\n      r2[4] -= m2 * s;\n      r3[4] -= m3 * s;\n   }\n   s = r0[5];\n   if (s != 0.0) {\n      r1[5] -= m1 * s;\n      r2[5] -= m2 * s;\n      r3[5] -= m3 * s;\n   }\n   s = r0[6];\n   if (s != 0.0) {\n      r1[6] -= m1 * s;\n      r2[6] -= m2 * s;\n      r3[6] -= m3 * s;\n   }\n   s = r0[7];\n   if (s != 0.0) {\n      r1[7] -= m1 * s;\n      r2[7] -= m2 * s;\n      r3[7] -= m3 * s;\n   }\n\n   /* choose pivot - or die */\n   if (fabs(r3[1]) > fabs(r2[1]))\n      SWAP_ROWS(r3, r2);\n   if (fabs(r2[1]) > fabs(r1[1]))\n      SWAP_ROWS(r2, r1);\n   if (0.0 == r1[1])\n      return false;\n\n   /* eliminate second variable */\n   m2 = r2[1] / r1[1];\n   m3 = r3[1] / r1[1];\n   r2[2] -= m2 * r1[2];\n   r3[2] -= m3 * r1[2];\n   r2[3] -= m2 * r1[3];\n   r3[3] -= m3 * r1[3];\n   s = r1[4];\n   if (0.0 != s) {\n      r2[4] -= m2 * s;\n      r3[4] -= m3 * s;\n   }\n   s = r1[5];\n   if (0.0 != s) {\n      r2[5] -= m2 * s;\n      r3[5] -= m3 * s;\n   }\n   s = r1[6];\n   if (0.0 != s) {\n      r2[6] -= m2 * s;\n      r3[6] -= m3 * s;\n   }\n   s = r1[7];\n   if (0.0 != s) {\n      r2[7] -= m2 * s;\n      r3[7] -= m3 * s;\n   }\n\n   /* choose pivot - or die */\n   if (fabs(r3[2]) > fabs(r2[2]))\n      SWAP_ROWS(r3, r2);\n   if (0.0 == r2[2])\n      return false;\n\n   /* eliminate third variable */\n   m3 = r3[2] / r2[2];\n   r3[3] -= m3 * r2[3], r3[4] -= m3 * r2[4],\n      r3[5] -= m3 * r2[5], r3[6] -= m3 * r2[6], r3[7] -= m3 * r2[7];\n\n   /* last check */\n   if (0.0 == r3[3])\n      return false;\n\n   s = 1.0 / r3[3];             /* now back substitute row 3 */\n   r3[4] *= s;\n   r3[5] *= s;\n   r3[6] *= s;\n   r3[7] *= s;\n\n   m2 = r2[3];                  /* now back substitute row 2 */\n   s = 1.0 / r2[2];\n   r2[4] = s * (r2[4] - r3[4] * m2), r2[5] = s * (r2[5] - r3[5] * m2),\n      r2[6] = s * (r2[6] - r3[6] * m2), r2[7] = s * (r2[7] - r3[7] * m2);\n   m1 = r1[3];\n   r1[4] -= r3[4] * m1, r1[5] -= r3[5] * m1,\n      r1[6] -= r3[6] * m1, r1[7] -= r3[7] * m1;\n   m0 = r0[3];\n   r0[4] -= r3[4] * m0, r0[5] -= r3[5] * m0,\n      r0[6] -= r3[6] * m0, r0[7] -= r3[7] * m0;\n\n   m1 = r1[2];                  /* now back substitute row 1 */\n   s = 1.0 / r1[1];\n   r1[4] = s * (r1[4] - r2[4] * m1), r1[5] = s * (r1[5] - r2[5] * m1),\n      r1[6] = s * (r1[6] - r2[6] * m1), r1[7] = s * (r1[7] - r2[7] * m1);\n   m0 = r0[2];\n   r0[4] -= r2[4] * m0, r0[5] -= r2[5] * m0,\n      r0[6] -= r2[6] * m0, r0[7] -= r2[7] * m0;\n\n   m0 = r0[1];                  /* now back substitute row 0 */\n   s = 1.0 / r0[0];\n   r0[4] = s * (r0[4] - r1[4] * m0), r0[5] = s * (r0[5] - r1[5] * m0),\n      r0[6] = s * (r0[6] - r1[6] * m0), r0[7] = s * (r0[7] - r1[7] * m0);\n\n   MAT(out, 0, 0) = r0[4];\n   MAT(out, 0, 1) = r0[5], MAT(out, 0, 2) = r0[6];\n   MAT(out, 0, 3) = r0[7], MAT(out, 1, 0) = r1[4];\n   MAT(out, 1, 1) = r1[5], MAT(out, 1, 2) = r1[6];\n   MAT(out, 1, 3) = r1[7], MAT(out, 2, 0) = r2[4];\n   MAT(out, 2, 1) = r2[5], MAT(out, 2, 2) = r2[6];\n   MAT(out, 2, 3) = r2[7], MAT(out, 3, 0) = r3[4];\n   MAT(out, 3, 1) = r3[5], MAT(out, 3, 2) = r3[6];\n   MAT(out, 3, 3) = r3[7];\n\n   return true;\n\n#undef MAT\n#undef SWAP_ROWS\n}\n\nvoid Matrix3x3_RM_Invert_Simple (const vec3_t in1[3], vec3_t out[3])\n{\n\t// we only support uniform scaling, so assume the first row is enough\n\t// (note the lack of sqrt here, because we're trying to undo the scaling,\n\t// this means multiplying by the inverse scale twice - squaring it, which\n\t// makes the sqrt a waste of time)\n#if 1\n\tdouble scale = 1.0 / (in1[0][0] * in1[0][0] + in1[0][1] * in1[0][1] + in1[0][2] * in1[0][2]);\n#else\n\tdouble scale = 3.0 / sqrt\n\t\t (in1->m[0][0] * in1->m[0][0] + in1->m[0][1] * in1->m[0][1] + in1->m[0][2] * in1->m[0][2]\n\t\t+ in1->m[1][0] * in1->m[1][0] + in1->m[1][1] * in1->m[1][1] + in1->m[1][2] * in1->m[1][2]\n\t\t+ in1->m[2][0] * in1->m[2][0] + in1->m[2][1] * in1->m[2][1] + in1->m[2][2] * in1->m[2][2]);\n\tscale *= scale;\n#endif\n\n\t// invert the rotation by transposing and multiplying by the squared\n\t// recipricol of the input matrix scale as described above\n\tout[0][0] = in1[0][0] * scale;\n\tout[0][1] = in1[1][0] * scale;\n\tout[0][2] = in1[2][0] * scale;\n\n\tout[1][0] = in1[0][1] * scale;\n\tout[1][1] = in1[1][1] * scale;\n\tout[1][2] = in1[2][1] * scale;\n\n\tout[2][0] = in1[0][2] * scale;\n\tout[2][1] = in1[1][2] * scale;\n\tout[2][2] = in1[2][2] * scale;\n}\n\nvoid Matrix3x4_Invert (const float *in1, float *out)\n{\n\tvec3_t a, b, c, trans;\n\n\tVectorSet (a, in1[0], in1[4], in1[8]);\n\tVectorSet (b, in1[1], in1[5], in1[9]);\n\tVectorSet (c, in1[2], in1[6], in1[10]);\n\n\tVectorScale (a, 1 / DotProduct (a, a), a);\n\tVectorScale (b, 1 / DotProduct (b, b), b);\n\tVectorScale (c, 1 / DotProduct (c, c), c);\n\n\tVectorSet (trans, in1[3], in1[7], in1[11]);\n\n\tVector4Set (out+0, a[0], a[1], a[2], -DotProduct (a, trans));\n\tVector4Set (out+4, b[0], b[1], b[2], -DotProduct (b, trans));\n\tVector4Set (out+8, c[0], c[1], c[2], -DotProduct (c, trans));\n}\n\nvoid QDECL Matrix3x4_Invert_Simple (const float *in1, float *out)\n{\n\t// we only support uniform scaling, so assume the first row is enough\n\t// (note the lack of sqrt here, because we're trying to undo the scaling,\n\t// this means multiplying by the inverse scale twice - squaring it, which\n\t// makes the sqrt a waste of time)\n#if 1\n\tdouble scale = 1.0 / (in1[0] * in1[0] + in1[1] * in1[1] + in1[2] * in1[2]);\n#else\n\tdouble scale = 3.0 / sqrt\n\t\t (in1->m[0][0] * in1->m[0][0] + in1->m[0][1] * in1->m[0][1] + in1->m[0][2] * in1->m[0][2]\n\t\t+ in1->m[1][0] * in1->m[1][0] + in1->m[1][1] * in1->m[1][1] + in1->m[1][2] * in1->m[1][2]\n\t\t+ in1->m[2][0] * in1->m[2][0] + in1->m[2][1] * in1->m[2][1] + in1->m[2][2] * in1->m[2][2]);\n\tscale *= scale;\n#endif\n\n\t// invert the rotation by transposing and multiplying by the squared\n\t// recipricol of the input matrix scale as described above\n\tout[0] = in1[0] * scale;\n\tout[1] = in1[4] * scale;\n\tout[2] = in1[8] * scale;\n\tout[4] = in1[1] * scale;\n\tout[5] = in1[5] * scale;\n\tout[6] = in1[9] * scale;\n\tout[8] = in1[2] * scale;\n\tout[9] = in1[6] * scale;\n\tout[10] = in1[10] * scale;\n\n\t// invert the translate\n\tout[3] = -(in1[3] * out[0] + in1[7] * out[1] + in1[11] * out[2]);\n\tout[7] = -(in1[3] * out[4] + in1[7] * out[5] + in1[11] * out[6]);\n\tout[11] = -(in1[3] * out[8] + in1[7] * out[9] + in1[11] * out[10]);\n}\n\nvoid Matrix3x4_InvertTo4x4_Simple (const float *in1, float *out)\n{\n\tMatrix3x4_Invert_Simple(in1, out);\n\tout[12] = 0;\n\tout[13] = 0;\n\tout[14] = 0;\n\tout[15] = 1;\n}\n\nvoid Matrix3x4_InvertTo3x3(const float *in, float *result)\n{\n\tfloat t1[16], tr[16];\n\tmemcpy(t1, in, sizeof(float)*12);\n\tt1[12] = 0;\n\tt1[13] = 0;\n\tt1[14] = 0;\n\tt1[15] = 1;\n\tMatrix4_Invert(t1, tr);\n\tVectorCopy(tr+0, result+0);\n\tVectorCopy(tr+4, result+3);\n\tVectorCopy(tr+8, result+6);\n\treturn;\n/*\n#define A(x,y) in[x+y*4]\n#define result(x,y) result[x+y*3]\n\tdouble determinant =    +A(0,0)*(A(1,1)*A(2,2)-A(2,1)*A(1,2))\n\t\t\t\t\t\t\t-A(0,1)*(A(1,0)*A(2,2)-A(1,2)*A(2,0))\n\t\t\t\t\t\t\t+A(0,2)*(A(1,0)*A(2,1)-A(1,1)*A(2,0));\n\tdouble invdet = 1/determinant;\n\tresult(0,0) =  (A(1,1)*A(2,2)-A(2,1)*A(1,2))*invdet;\n\tresult(1,0) = -(A(0,1)*A(2,2)-A(0,2)*A(2,1))*invdet;\n\tresult(2,0) =  (A(0,1)*A(1,2)-A(0,2)*A(1,1))*invdet;\n\tresult(0,1) = -(A(1,0)*A(2,2)-A(1,2)*A(2,0))*invdet;\n\tresult(1,1) =  (A(0,0)*A(2,2)-A(0,2)*A(2,0))*invdet;\n\tresult(2,1) = -(A(0,0)*A(1,2)-A(1,0)*A(0,2))*invdet;\n\tresult(0,2) =  (A(1,0)*A(2,1)-A(2,0)*A(1,1))*invdet;\n\tresult(1,2) = -(A(0,0)*A(2,1)-A(2,0)*A(0,1))*invdet;\n\tresult(2,2) =  (A(0,0)*A(1,1)-A(1,0)*A(0,1))*invdet;\n\t*/\n}\n\n//screen->3d\n\nvoid Matrix4x4_CM_UnProject(const vec3_t in, vec3_t out, const vec3_t viewangles, const vec3_t vieworg, float fovx, float fovy)\n{\n\tfloat modelview[16];\n\tfloat proj[16];\n\tfloat tempm[16];\n\n\tMatrix4x4_CM_ModelViewMatrix(modelview, viewangles, vieworg);\n\tMatrix4x4_CM_Projection_Inf(proj, fovx, fovy, 4, true);\n\tMatrix4_Multiply(proj, modelview, tempm);\n\n\tMatrix4_Invert(tempm, proj);\n\n\t{\n\t\tfloat v[4], tempv[4];\n\t\tv[0] = in[0]*2-1;\n\t\tv[1] = in[1]*2-1;\n\t\tv[2] = in[2];\n\t\tv[3] = 1;\n\n\t\t//don't use 1, because the far clip plane really is an infinite distance away\n\t\tif (v[2] >= 1)\n\t\t\tv[2] = 0.999999;\n\n\t\tMatrix4x4_CM_Transform4(proj, v, tempv); \n\n\t\tout[0] = tempv[0]/tempv[3];\n\t\tout[1] = tempv[1]/tempv[3];\n\t\tout[2] = tempv[2]/tempv[3];\n\t}\n}\n\n//returns fractions of screen.\n//uses GL style rotations and translations and stuff.\n//3d -> screen (fixme: offscreen return values needed)\n//returns false if the 2d point is offscreen.\nqboolean Matrix4x4_CM_Project (const vec3_t in, vec3_t out, const vec3_t viewangles, const vec3_t vieworg, float fovx, float fovy)\n{\n\tqboolean result = true;\n\tfloat modelview[16];\n\tfloat proj[16];\n\n\tMatrix4x4_CM_ModelViewMatrix(modelview, viewangles, vieworg);\n\tMatrix4x4_CM_Projection_Inf(proj, fovx, fovy, 4, true);\n\n\t{\n\t\tfloat v[4], tempv[4];\n\t\tv[0] = in[0];\n\t\tv[1] = in[1];\n\t\tv[2] = in[2];\n\t\tv[3] = 1;\n\n\t\tMatrix4x4_CM_Transform4(modelview, v, tempv); \n\t\tMatrix4x4_CM_Transform4(proj, tempv, v);\n\n\t\tv[0] /= v[3];\n\t\tv[1] /= v[3];\n\t\tif (v[2] < 0)\n\t\t\tresult = false;\t//too close to the view\n\t\tv[2] /= v[3];\n\n\t\tout[0] = (1+v[0])/2;\n\t\tout[1] = (1+v[1])/2;\n\t\tout[2] = (1+v[2])/2;\n\t\tif (out[2] > 1)\n\t\t\tresult = false;\t//beyond far clip plane\n\t}\n\treturn result;\n}\n\n\n//I much prefer it to take float*...\nvoid Matrix3_Multiply (vec3_t *in1, vec3_t *in2, vec3_t *out)\n{\n\tout[0][0] = in1[0][0]*in2[0][0] + in1[0][1]*in2[1][0] + in1[0][2]*in2[2][0];\n\tout[0][1] = in1[0][0]*in2[0][1] + in1[0][1]*in2[1][1] + in1[0][2]*in2[2][1];\n\tout[0][2] = in1[0][0]*in2[0][2] + in1[0][1]*in2[1][2] + in1[0][2]*in2[2][2];\n\tout[1][0] = in1[1][0]*in2[0][0] + in1[1][1]*in2[1][0] +\tin1[1][2]*in2[2][0];\n\tout[1][1] = in1[1][0]*in2[0][1] + in1[1][1]*in2[1][1] + in1[1][2]*in2[2][1];\n\tout[1][2] = in1[1][0]*in2[0][2] + in1[1][1]*in2[1][2] +\tin1[1][2]*in2[2][2];\n\tout[2][0] = in1[2][0]*in2[0][0] + in1[2][1]*in2[1][0] +\tin1[2][2]*in2[2][0];\n\tout[2][1] = in1[2][0]*in2[0][1] + in1[2][1]*in2[1][1] +\tin1[2][2]*in2[2][1];\n\tout[2][2] = in1[2][0]*in2[0][2] + in1[2][1]*in2[1][2] +\tin1[2][2]*in2[2][2];\n}\n\nvec_t QDECL VectorNormalize2 (const vec3_t v, vec3_t out)\n{\n\tfloat\tlength, ilength;\n\n\tlength = v[0]*v[0] + v[1]*v[1] + v[2]*v[2];\n\tlength = sqrt (length);\n\tif (length)\n\t{\n\t\tilength = 1/length;\n\t\tout[0] = v[0]*ilength;\n\t\tout[1] = v[1]*ilength;\n\t\tout[2] = v[2]*ilength;\n\t}\n\telse\n\t{\n\t\tVectorClear (out);\n\t}\n\t\t\n\treturn length;\n}\nfloat ColorNormalize (const vec3_t in, vec3_t out)\n{\n\tfloat f = max (max (in[0], in[1]), in[2]);\n\n\tif ( f > 1.0 ) {\n\t\tf = 1.0 / f;\n\t\tout[0] = in[0] * f;\n\t\tout[1] = in[1] * f;\n\t\tout[2] = in[2] * f;\n\t} else {\n\t\tout[0] = in[0];\n\t\tout[1] = in[1];\n\t\tout[2] = in[2];\n\t}\n\n\treturn f;\n}\n\nvoid MakeNormalVectors (const vec3_t forward, vec3_t right, vec3_t up)\n{\n\tfloat\t\td;\n\n\t// this rotate and negat guarantees a vector\n\t// not colinear with the original\n\tright[1] = -forward[0];\n\tright[2] = forward[1];\n\tright[0] = forward[2];\n\n\td = DotProduct (right, forward);\n\tVectorMA (right, -d, forward, right);\n\tVectorNormalize (right);\n\tCrossProduct (right, forward, up);\n}\n\n"
  },
  {
    "path": "engine/common/mathlib.h",
    "content": "/*\nCopyright (C) 1996-1997 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  \n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n*/\n// mathlib.h\n\ntypedef float vec_t;\ntypedef vec_t vec2_t[2];\ntypedef vec_t vec3_t[3];\ntypedef vec_t vec4_t[4];\ntypedef vec_t vec5_t[5];\n\ntypedef int ivec_t;\ntypedef ivec_t ivec2_t[2];\ntypedef ivec_t ivec3_t[3];\ntypedef ivec_t ivec4_t[4];\ntypedef ivec_t ivec5_t[5];\n\n/*16-byte aligned vectors, for auto-vectorising, should propogate to structs\nsse and altivec can unroll loops using aligned reads, which should be faster... 4 at once.\n*/\ntypedef FTE_ALIGN(16) vec3_t avec3_t;\ntypedef FTE_ALIGN(16) vec4_t avec4_t;\ntypedef FTE_ALIGN(4) qbyte byte_vec4_t[4];\n\n//VECV_STRIDE is used only as an argument for opengl.\n#ifdef FTE_TARGET_WEB\n\t//emscripten is alergic to explicit strides without packed attributes, at least in emulated code.\n\t//so we need to keep everything packed. screw sse-friendly packing.\n\t#define vecV_t vec3_t\n\t#define VECV_STRIDE 0\n#else\n\t#define vecV_t avec4_t\n\t#define VECV_STRIDE sizeof(vecV_t)\n#endif\n\ntypedef\tint\tfixed4_t;\ntypedef\tint\tfixed8_t;\ntypedef\tint\tfixed16_t;\n\n#ifndef M_PI\n#define M_PI\t\t3.14159265358979323846\t// matches value in gcc v2 math.h\n#endif\n\nstruct mplane_s;\n\nextern vec3_t vec3_origin;\n\n#define bound(min,num,max) ((num) >= (min) ? ((num) < (max) ? (num) : (max)) : (min))\n\n#define nanmask (255<<23)\n#define\tIS_NAN(x) (((*(int *)&x)&nanmask)==nanmask)\n\n#define FloatInterpolate(a, bness, b, c) ((c) = (a) + (b - a)*bness)\n\n#define DotProduct_Double(x,y) ((double)(x)[0]*(double)(y)[0]+(double)(x)[1]*(double)(y)[1]+(double)(x)[2]*(double)(y)[2])\t//cast to doubles, to try to replicate x87 precision in 64bit sse builds etc. there'll still be a precision difference though.\n#define DotProduct(x,y) ((x)[0]*(y)[0]+(x)[1]*(y)[1]+(x)[2]*(y)[2])\n#define DotProduct2(x,y) ((x)[0]*(y)[0]+(x)[1]*(y)[1])\n#define DotProduct4(x,y) ((x)[0]*(y)[0]+(x)[1]*(y)[1]+(x)[2]*(y)[2]+(x)[3]*(y)[3])\n#define VectorSubtract(a,b,c) do{(c)[0]=(a)[0]-(b)[0];(c)[1]=(a)[1]-(b)[1];(c)[2]=(a)[2]-(b)[2];}while(0)\n#define VectorAdd(a,b,c) do{(c)[0]=(a)[0]+(b)[0];(c)[1]=(a)[1]+(b)[1];(c)[2]=(a)[2]+(b)[2];}while(0)\n#define VectorCopy(a,b) do{(b)[0]=(a)[0];(b)[1]=(a)[1];(b)[2]=(a)[2];}while(0)\n#define VectorScale(a,s,b) do{(b)[0]=(s)*(a)[0];(b)[1]=(s)*(a)[1];(b)[2]=(s)*(a)[2];}while(0)\n#define VectorMul(a,s,b) do{(b)[0]=(s)[0]*(a)[0];(b)[1]=(s)[1]*(a)[1];(b)[2]=(s)[2]*(a)[2];}while(0)\n#define VectorClear(a)\t\t\t((a)[0]=(a)[1]=(a)[2]=0)\n#define VectorSet(r,x,y,z) do{(r)[0] = x; (r)[1] = y;(r)[2] = z;}while(0)\n#define VectorNegate(a,b)\t\t((b)[0]=-(a)[0],(b)[1]=-(a)[1],(b)[2]=-(a)[2])\n#define VectorLength(a)\t\tLength(a)\n#define VectorMA(a,s,b,c) do{(c)[0] = (a)[0] + (s)*(b)[0];(c)[1] = (a)[1] + (s)*(b)[1];(c)[2] = (a)[2] + (s)*(b)[2];}while(0)\n#define VectorEquals(a,b) ((a)[0] == (b)[0] && (a)[1] == (b)[1] && (a)[2] == (b)[2])\n#define VectorAvg(a,b,c)\t\t((c)[0]=((a)[0]+(b)[0])*0.5f,(c)[1]=((a)[1]+(b)[1])*0.5f, (c)[2]=((a)[2]+(b)[2])*0.5f)\n#define VectorInterpolate(a, bness, b, c) FloatInterpolate((a)[0], bness, (b)[0], (c)[0]),FloatInterpolate((a)[1], bness, (b)[1], (c)[1]),FloatInterpolate((a)[2], bness, (b)[2], (c)[2])\n#define Vector2Clear(a)\t\t\t((a)[0]=(a)[1]=0)\n#define Vector2Copy(a,b) do{(b)[0]=(a)[0];(b)[1]=(a)[1];}while(0)\n#define Vector2Set(r,x,y) do{(r)[0] = x; (r)[1] = y;}while(0)\n#define Vector2MA(a,s,b,c) do{(c)[0] = (a)[0] + (s)*(b)[0];(c)[1] = (a)[1] + (s)*(b)[1];}while(0)\n#define Vector2Interpolate(a, bness, b, c) FloatInterpolate((a)[0], bness, (b)[0], (c)[0]),FloatInterpolate((a)[1], bness, (b)[1], (c)[1])\n\n#define Vector4Copy(a,b) do{(b)[0]=(a)[0];(b)[1]=(a)[1];(b)[2]=(a)[2];(b)[3]=(a)[3];}while(0)\n#define Vector4Scale(in,scale,out)\t\t((out)[0]=(in)[0]*scale,(out)[1]=(in)[1]*scale,(out)[2]=(in)[2]*scale,(out)[3]=(in)[3]*scale)\n#define Vector4Add(a,b,c)\t\t((c)[0]=(((a[0])+(b[0]))),(c)[1]=(((a[1])+(b[1]))),(c)[2]=(((a[2])+(b[2]))),(c)[3]=(((a[3])+(b[3]))))\n#define Vector4Set(r,x,y,z,w) (r)[0] = x, (r)[1] = y, (r)[2] = z, (r)[3]=w\n#define Vector4Interpolate(a, bness, b, c) FloatInterpolate((a)[0], bness, (b)[0], (c)[0]),FloatInterpolate((a)[1], bness, (b)[1], (c)[1]),FloatInterpolate((a)[2], bness, (b)[2], (c)[2]),FloatInterpolate((a)[3], bness, (b)[3], (c)[3])\n#define Vector4MA(a,s,b,c) do{(c)[0] = (a)[0] + (s)*(b)[0];(c)[1] = (a)[1] + (s)*(b)[1];(c)[2] = (a)[2] + (s)*(b)[2];(c)[3] = (a)[3] + (s)*(b)[3];}while(0)\n\ntypedef float matrix3x4[3][4];\ntypedef float matrix3x3[3][3];\n\n\n#define BOX_ON_PLANE_SIDE(emins, emaxs, p)\t\\\n\t(((p)->type < 3)?\t\t\t\t\t\t\\\n\t(\t\t\t\t\t\t\t\t\t\t\\\n\t\t((p)->dist <= (emins)[(p)->type])?\t\\\n\t\t\t1\t\t\t\t\t\t\t\t\\\n\t\t:\t\t\t\t\t\t\t\t\t\\\n\t\t(\t\t\t\t\t\t\t\t\t\\\n\t\t\t((p)->dist >= (emaxs)[(p)->type])?\\\n\t\t\t\t2\t\t\t\t\t\t\t\\\n\t\t\t:\t\t\t\t\t\t\t\t\\\n\t\t\t\t3\t\t\t\t\t\t\t\\\n\t\t)\t\t\t\t\t\t\t\t\t\\\n\t)\t\t\t\t\t\t\t\t\t\t\\\n\t:\t\t\t\t\t\t\t\t\t\t\\\n\t\tBoxOnPlaneSide( (emins), (emaxs), (p)))\n\ntypedef struct {\n\tfloat m[4][4];\n} matrix4x4_t;\n\n//vec_t\t\t_DotProduct (vec3_t v1, vec3_t v2);\n//void\t\t_VectorAdd (vec3_t veca, vec3_t vecb, vec3_t out);\n//void\t\t_VectorCopy (vec3_t in, vec3_t out);\n//void\t\t_VectorSubtract (vec3_t veca, vec3_t vecb, vec3_t out);\nvoid\t\tAddPointToBounds (const vec3_t v, vec3_t mins, vec3_t maxs);\nfloat\t\tanglemod (float a);\nvoid\t\tQDECL AngleVectors (const vec3_t angles, vec3_t forward, vec3_t right, vec3_t up);\nvoid\t\tQDECL AngleVectorsMesh (const vec3_t angles, vec3_t forward, vec3_t right, vec3_t up);\nvoid\t\tQDECL VectorAngles (const float *forward, const float *up, float *angles, qboolean meshpitch);\t//up may be NULL\nvoid VARGS\tBOPS_Error (void);\nint VARGS\tBoxOnPlaneSide (const vec3_t emins, const vec3_t emaxs, const struct mplane_s *plane);\nvoid\t\tClearBounds (vec3_t mins, vec3_t maxs);\nfloat\t\tColorNormalize (const vec3_t in, vec3_t out);\nvoid\t\tCrossProduct (const vec3_t v1, const vec3_t v2, vec3_t cross);\nvoid\t\tFloorDivMod (double numer, double denom, int *quotient, int *rem);\nint\t\t\tGreatestCommonDivisor (int i1, int i2);\nfixed16_t\tInvert24To16 (fixed16_t val);\nvec_t\t\tLength (const vec3_t v);\nvoid\t\tMakeNormalVectors (const vec3_t forward, vec3_t right, vec3_t up);\nfloat\t\tQ_rsqrt(float number);\n\n/*\n_CM means column major.\n_RM means row major\nNote that openGL is column-major.\nLogical C code uses row-major.\nmat3x4 is always row-major (and functions can accept many RM mat4x4)\n*/\n\nvoid\t\tMatrix3_Multiply (vec3_t *in1, vec3_t *in2, vec3_t *out);\nvoid\t\tMatrix4x4_Identity(float *outm);\nqboolean\tMatrix4_Invert(const float *m, float *out);\nvoid\t\tMatrix3x4_Invert (const float *in1, float *out);\nvoid\t\tQDECL Matrix3x4_Invert_Simple (const float *in1, float *out);\nvoid\t\tMatrix3x4_InvertTo4x4_Simple (const float *in1, float *out);\nvoid\t\tMatrix3x3_RM_Invert_Simple(const vec3_t in[3], vec3_t out[3]);\nvoid\t\tMatrix4x4_RM_CreateTranslate (float *out, float x, float y, float z);\nvoid\t\tMatrix4x4_CM_CreateTranslate (float *out, float x, float y, float z);\nvoid\t\tMatrix4x4_CM_ModelMatrixFromAxis (float *modelview, const vec3_t pn, const vec3_t right, const vec3_t up, const vec3_t vieworg);\nvoid\t\tMatrix4x4_CM_ModelMatrix(float *modelview, vec_t x, vec_t y, vec_t z, vec_t pitch, vec_t yaw, vec_t roll, vec_t scale);\nvoid\t\tMatrix4x4_CM_ModelViewMatrix (float *modelview, const vec3_t viewangles, const vec3_t vieworg);\nvoid\t\tMatrix4x4_CM_ModelViewMatrixFromAxis (float *modelview, const vec3_t pn, const vec3_t right, const vec3_t up, const vec3_t vieworg);\nvoid\t\tMatrix4x4_CM_LightMatrixFromAxis(float *modelview, const vec3_t px, const vec3_t py, const vec3_t pz, const vec3_t vieworg);\t//\nvoid\t\tMatrix4_CreateFromQuakeEntity (float *matrix, float x, float y, float z, float pitch, float yaw, float roll, float scale);\nvoid\t\tMatrix4_Multiply (const float *a, const float *b, float *out);\nvoid\t\tMatrix3x4_Multiply(const float *a, const float *b, float *out);\nqboolean\tMatrix4x4_CM_Project (const vec3_t in, vec3_t out, const vec3_t viewangles, const vec3_t vieworg, float fovx, float fovy);\nvoid\t\tMatrix4x4_CM_Transform3x3(const float *matrix, const float *vector, float *product);\nvoid\t\tMatrix4x4_CM_Transform3 (const float *matrix, const float *vector, float *product);\nvoid\t\tMatrix4x4_CM_Transform4 (const float *matrix, const float *vector, float *product);\nvoid\t\tMatrix4x4_CM_Transform34(const float *matrix, const vec3_t vector, vec4_t product);\nvoid\t\tMatrix4x4_CM_UnProject (const vec3_t in, vec3_t out, const vec3_t viewangles, const vec3_t vieworg, float fovx, float fovy);\nvoid\t\tMatrix3x4_RM_FromAngles(const vec3_t angles, const vec3_t origin, float *out);\nvoid\t\tMatrix3x4_RM_FromVectors(float *out, const float vx[3], const float vy[3], const float vz[3], const float t[3]);\nvoid\t\tMatrix4x4_RM_FromVectors(float *out, const float vx[3], const float vy[3], const float vz[3], const float t[3]);\nvoid\t\tMatrix3x4_RM_ToVectors(const float *in, float vx[3], float vy[3], float vz[3], float t[3]);\nvoid\t\tMatrix3x4_RM_Transform3(const float *matrix, const float *vector, float *product);\nvoid\t\tMatrix3x4_RM_Transform3x3(const float *matrix, const float *vector, float *product);\n\nfloat\t\t*Matrix4x4_CM_NewRotation(float a, float x, float y, float z);\nfloat\t\t*Matrix4x4_CM_NewTranslation(float x, float y, float z);\n\nvoid Bones_To_PosQuat4(int numbones, const float *matrix, short *result);\nvoid QDECL GenMatrixPosQuat4Scale(const vec3_t pos, const vec4_t quat, const vec3_t scale, float result[12]);\nvoid QuaternionSlerp(const vec4_t p, vec4_t q, float t, vec4_t qt);\n\n#define AngleVectorsFLU(a,f,l,u) do{AngleVectors(a,f,l,u);VectorNegate(l,l);}while(0)\n\n//projection matricies of different types... gesh\nvoid\t\tMatrix4x4_CM_Orthographic (float *proj, float xmin, float xmax, float ymax, float ymin, float znear, float zfar);\nvoid Matrix4x4_CM_OrthographicD3D(float *proj, float xmin, float xmax, float ymax, float ymin, float znear, float zfar);\nvoid\t\tMatrix4x4_CM_Projection_Offset(float *proj, float fovl, float fovr, float fovu, float fovd, float neard, float fard, qboolean d3d);\nvoid\t\tMatrix4x4_CM_Projection_Far(float *proj, float fovx, float fovy, float neard, float fard, qboolean d3d);\nvoid\t\tMatrix4x4_CM_Projection2 (float *proj, float fovx, float fovy, float neard);\nvoid\t\tMatrix4x4_CM_Projection_Inf(float *proj, float fovx, float fovy, float neard, qboolean d3d);\n\nfixed16_t\tMul16_30 (fixed16_t multiplier, fixed16_t multiplicand);\nint\t\t\tQ_log2 (int val);\n\nvoid\t\tMatrix3x4_InvertTo3x3(const float *in, float *result);\n\nfixed16_t\tMul16_30 (fixed16_t multiplier, fixed16_t multiplicand);\nint\t\t\tQ_log2 (int val);\nvoid\t\tR_ConcatRotations (float in1[3][3], float in2[3][3], float out[3][3]);\nvoid\t\tR_ConcatRotationsPad (float in1[3][4], float in2[3][4], float out[3][4]);\nvoid\t\tQDECL R_ConcatTransforms (const matrix3x4 in1, const matrix3x4 in2, matrix3x4 out);\nvoid\t\tR_ConcatTransformsAxis (const float in1[3][3], const float in2[3][4], float out[3][4]);\nvoid\t\tPerpendicularVector(vec3_t dst, const vec3_t src);\nvoid\t\tRotatePointAroundVector (vec3_t dst, const vec3_t dir, const vec3_t point, float degrees);\nvoid\t\tRotateLightVector(const vec3_t *axis, const vec3_t origin, const vec3_t lightpoint, vec3_t result);\nint\t\t\tVectorCompare (const vec3_t v1, const vec3_t v2);\nint\t\t\tVector4Compare (const vec4_t v1, const vec4_t v2);\nvoid\t\tVectorInverse (vec3_t v);\nvoid\t\t_VectorMA (const vec3_t veca, const float scale, const vec3_t vecb, vec3_t vecc);\nfloat\t\tQDECL VectorNormalize (vec3_t v);\t\t// returns vector length\nvec_t\t\tQDECL VectorNormalize2 (const vec3_t v, vec3_t out);\nvoid\t\tVectorNormalizeFast(vec3_t v);\nvoid\t\tVectorTransform (const vec3_t in1, const matrix3x4 in2, vec3_t out);\nvoid\t\tVectorVectors (const vec3_t forward, vec3_t right, vec3_t up);\n"
  },
  {
    "path": "engine/common/md4.c",
    "content": "/*\nCopyright (C) 1996-1997 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n*/\n/* GLOBAL.H - RSAREF types and constants */\n\n#include <string.h>\n\n/* POINTER defines a generic pointer type */\ntypedef unsigned char *POINTER;\n\n/* UINT2 defines a two byte word */\ntypedef unsigned short int UINT2;\n\n/* UINT4 defines a four byte word */\ntypedef unsigned int UINT4;\n\n\n/* MD4.H - header file for MD4C.C */\n\n/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991.\n\nAll rights reserved.\n\nLicense to copy and use this software is granted provided that it is identified as the RSA Data Security, Inc. MD4 Message-Digest Algorithm in all material mentioning or referencing this software or this function.\nLicense is also granted to make and use derivative works provided that such works are identified as derived from the RSA Data Security, Inc. MD4 Message-Digest Algorithm in all material mentioning or referencing the derived work.\nRSA Data Security, Inc. makes no representations concerning either the merchantability of this software or the suitability of this software for any particular purpose. It is provided as is without express or implied warranty of any kind.\n\nThese notices must be retained in any copies of any part of this documentation and/or software. */\n\n/* MD4 context. */\ntypedef struct {\n\tUINT4 state[4];\t\t\t\t/* state (ABCD) */\n\tUINT4 count[2];\t\t\t\t/* number of bits, modulo 2^64 (lsb first) */\n\tunsigned char buffer[64]; \t\t\t/* input buffer */\n} MD4_CTX;\n\nvoid MD4Init (MD4_CTX *);\nvoid MD4Update (MD4_CTX *, const unsigned char *, size_t);\nvoid MD4Final (unsigned char [16], MD4_CTX *);\n\n\n\n/* MD4C.C - RSA Data Security, Inc., MD4 message-digest algorithm */\n/* Copyright (C) 1990-2, RSA Data Security, Inc. All rights reserved.\n\nLicense to copy and use this software is granted provided that it is identified as the\nRSA Data Security, Inc. MD4 Message-Digest Algorithm\n in all material mentioning or referencing this software or this function.\nLicense is also granted to make and use derivative works provided that such works are identified as\nderived from the RSA Data Security, Inc. MD4 Message-Digest Algorithm\nin all material mentioning or referencing the derived work.\nRSA Data Security, Inc. makes no representations concerning either the merchantability of this software or the suitability of this software for any particular purpose. It is provided\nas is without express or implied warranty of any kind.\n\nThese notices must be retained in any copies of any part of this documentation and/or software. */\n\n/* Constants for MD4Transform routine.  */\n#define S11 3\n#define S12 7\n#define S13 11\n#define S14 19\n#define S21 3\n#define S22 5\n#define S23 9\n#define S24 13\n#define S31 3\n#define S32 9\n#define S33 11\n#define S34 15\n\nstatic void MD4Transform (UINT4 [4], const unsigned char [64]);\nstatic void Encode (unsigned char *, UINT4 *, unsigned int);\nstatic void Decode (UINT4 *, const unsigned char *, unsigned int);\n\nstatic unsigned char PADDING[64] = {\n0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\n};\n\n/* F, G and H are basic MD4 functions. */\n#define F(x, y, z) (((x) & (y)) | ((~x) & (z)))\n#define G(x, y, z) (((x) & (y)) | ((x) & (z)) | ((y) & (z)))\n#define H(x, y, z) ((x) ^ (y) ^ (z))\n\n/* ROTATE_LEFT rotates x left n bits. */\n#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n))))\n\n/* FF, GG and HH are transformations for rounds 1, 2 and 3 */\n/* Rotation is separate from addition to prevent recomputation */\n#define FF(a, b, c, d, x, s) {(a) += F ((b), (c), (d)) + (x); (a) = ROTATE_LEFT ((a), (s));}\n\n#define GG(a, b, c, d, x, s) {(a) += G ((b), (c), (d)) + (x) + (UINT4)0x5a827999; (a) = ROTATE_LEFT ((a), (s));}\n\n#define HH(a, b, c, d, x, s) {(a) += H ((b), (c), (d)) + (x) + (UINT4)0x6ed9eba1; (a) = ROTATE_LEFT ((a), (s));}\n\n\n/* MD4 initialization. Begins an MD4 operation, writing a new context. */\nvoid MD4Init (MD4_CTX *context)\n{\n\tcontext->count[0] = context->count[1] = 0;\n\n/* Load magic initialization constants.*/\ncontext->state[0] = 0x67452301;\ncontext->state[1] = 0xefcdab89;\ncontext->state[2] = 0x98badcfe;\ncontext->state[3] = 0x10325476;\n}\n\n/* MD4 block update operation. Continues an MD4 message-digest operation, processing another message block, and updating the context. */\nvoid MD4Update (MD4_CTX *context, const unsigned char *input, size_t inputLen)\n{\n\tunsigned int i, index, partLen;\n\n\t/* Compute number of bytes mod 64 */\n\tindex = (unsigned int)((context->count[0] >> 3) & 0x3F);\n\n\t/* Update number of bits */\n\tif ((context->count[0] += ((UINT4)inputLen << 3))< ((UINT4)inputLen << 3))\n\t\tcontext->count[1]++;\n\n\tcontext->count[1] += ((UINT4)inputLen >> 29);\n\n\tpartLen = 64 - index;\n\n\t/* Transform as many times as possible.*/\n\tif (inputLen >= partLen)\n\t{\n \t\tmemcpy((POINTER)&context->buffer[index], (POINTER)input, partLen);\n \t\tMD4Transform (context->state, context->buffer);\n\n \t\tfor (i = partLen; i + 63 < inputLen; i += 64)\n \t\t\tMD4Transform (context->state, &input[i]);\n\n \t\tindex = 0;\n\t}\n\telse\n \t\ti = 0;\n\n\t/* Buffer remaining input */\n\tmemcpy ((POINTER)&context->buffer[index], (POINTER)&input[i], inputLen-i);\n}\n\n\n/* MD4 finalization. Ends an MD4 message-digest operation, writing the the message digest and zeroizing the context. */\nvoid MD4Final (unsigned char digest[16], MD4_CTX *context)\n{\n\tunsigned char bits[8];\n\tunsigned int index, padLen;\n\n\t/* Save number of bits */\n\tEncode (bits, context->count, 8);\n\n\t/* Pad out to 56 mod 64.*/\n\tindex = (unsigned int)((context->count[0] >> 3) & 0x3f);\n\tpadLen = (index < 56) ? (56 - index) : (120 - index);\n\tMD4Update (context, PADDING, padLen);\n\n\t/* Append length (before padding) */\n\tMD4Update (context, bits, 8);\n\n\t/* Store state in digest */\n\tEncode (digest, context->state, 16);\n\n\t/* Zeroize sensitive information.*/\n\tmemset ((POINTER)context, 0, sizeof (*context));\n}\n\n\n/* MD4 basic transformation. Transforms state based on block. */\nstatic void MD4Transform (UINT4 state[4], const unsigned char block[64])\n{\n\tUINT4 a = state[0], b = state[1], c = state[2], d = state[3], x[16];\n\n\tDecode (x, block, 64);\n\n/* Round 1 */\nFF (a, b, c, d, x[ 0], S11); \t\t\t\t/* 1 */\nFF (d, a, b, c, x[ 1], S12); \t\t\t\t/* 2 */\nFF (c, d, a, b, x[ 2], S13); \t\t\t\t/* 3 */\nFF (b, c, d, a, x[ 3], S14); \t\t\t\t/* 4 */\nFF (a, b, c, d, x[ 4], S11); \t\t\t\t/* 5 */\nFF (d, a, b, c, x[ 5], S12); \t\t\t\t/* 6 */\nFF (c, d, a, b, x[ 6], S13); \t\t\t\t/* 7 */\nFF (b, c, d, a, x[ 7], S14); \t\t\t\t/* 8 */\nFF (a, b, c, d, x[ 8], S11); \t\t\t\t/* 9 */\nFF (d, a, b, c, x[ 9], S12); \t\t\t\t/* 10 */\nFF (c, d, a, b, x[10], S13); \t\t\t/* 11 */\nFF (b, c, d, a, x[11], S14); \t\t\t/* 12 */\nFF (a, b, c, d, x[12], S11); \t\t\t/* 13 */\nFF (d, a, b, c, x[13], S12); \t\t\t/* 14 */\nFF (c, d, a, b, x[14], S13); \t\t\t/* 15 */\nFF (b, c, d, a, x[15], S14); \t\t\t/* 16 */\n\n/* Round 2 */\nGG (a, b, c, d, x[ 0], S21); \t\t\t/* 17 */\nGG (d, a, b, c, x[ 4], S22); \t\t\t/* 18 */\nGG (c, d, a, b, x[ 8], S23); \t\t\t/* 19 */\nGG (b, c, d, a, x[12], S24); \t\t\t/* 20 */\nGG (a, b, c, d, x[ 1], S21); \t\t\t/* 21 */\nGG (d, a, b, c, x[ 5], S22); \t\t\t/* 22 */\nGG (c, d, a, b, x[ 9], S23); \t\t\t/* 23 */\nGG (b, c, d, a, x[13], S24); \t\t\t/* 24 */\nGG (a, b, c, d, x[ 2], S21); \t\t\t/* 25 */\nGG (d, a, b, c, x[ 6], S22); \t\t\t/* 26 */\nGG (c, d, a, b, x[10], S23); \t\t\t/* 27 */\nGG (b, c, d, a, x[14], S24); \t\t\t/* 28 */\nGG (a, b, c, d, x[ 3], S21); \t\t\t/* 29 */\nGG (d, a, b, c, x[ 7], S22); \t\t\t/* 30 */\nGG (c, d, a, b, x[11], S23); \t\t\t/* 31 */\nGG (b, c, d, a, x[15], S24); \t\t\t/* 32 */\n\n/* Round 3 */\nHH (a, b, c, d, x[ 0], S31);\t\t\t\t/* 33 */\nHH (d, a, b, c, x[ 8], S32); \t\t\t/* 34 */\nHH (c, d, a, b, x[ 4], S33); \t\t\t/* 35 */\nHH (b, c, d, a, x[12], S34); \t\t\t/* 36 */\nHH (a, b, c, d, x[ 2], S31); \t\t\t/* 37 */\nHH (d, a, b, c, x[10], S32); \t\t\t/* 38 */\nHH (c, d, a, b, x[ 6], S33); \t\t\t/* 39 */\nHH (b, c, d, a, x[14], S34); \t\t\t/* 40 */\nHH (a, b, c, d, x[ 1], S31); \t\t\t/* 41 */\nHH (d, a, b, c, x[ 9], S32); \t\t\t/* 42 */\nHH (c, d, a, b, x[ 5], S33); \t\t\t/* 43 */\nHH (b, c, d, a, x[13], S34); \t\t\t/* 44 */\nHH (a, b, c, d, x[ 3], S31); \t\t\t/* 45 */\nHH (d, a, b, c, x[11], S32); \t\t\t/* 46 */\nHH (c, d, a, b, x[ 7], S33); \t\t\t/* 47 */\nHH (b, c, d, a, x[15], S34);\t\t\t/* 48 */\n\nstate[0] += a;\nstate[1] += b;\nstate[2] += c;\nstate[3] += d;\n\n\t/* Zeroize sensitive information.*/\n\tmemset ((POINTER)x, 0, sizeof (x));\n}\n\n\n/* Encodes input (UINT4) into output (unsigned char). Assumes len is a multiple of 4. */\nstatic void Encode (unsigned char *output, UINT4 *input, unsigned int len)\n{\n\tunsigned int i, j;\n\n\tfor (i = 0, j = 0; j < len; i++, j += 4) {\n \t\toutput[j] = (unsigned char)(input[i] & 0xff);\n \t\toutput[j+1] = (unsigned char)((input[i] >> 8) & 0xff);\n \t\toutput[j+2] = (unsigned char)((input[i] >> 16) & 0xff);\n \t\toutput[j+3] = (unsigned char)((input[i] >> 24) & 0xff);\n\t}\n}\n\n\n/* Decodes input (unsigned char) into output (UINT4). Assumes len is a multiple of 4. */\nstatic void Decode (UINT4 *output, const unsigned char *input, unsigned int len)\n{\nunsigned int i, j;\n\nfor (i = 0, j = 0; j < len; i++, j += 4)\n \toutput[i] = ((UINT4)input[j]) | (((UINT4)input[j+1]) << 8) | (((UINT4)input[j+2]) << 16) | (((UINT4)input[j+3]) << 24);\n}\n\n//===================================================================\n#include \"quakedef.h\"\nhashfunc_t hash_md4 =\n{\n\t16,\t//digest size\n\tsizeof(MD4_CTX),\n\t(void(*)(void*ctx))MD4Init,\n\t(void(*)(void*ctx,const void*in,size_t))MD4Update,\n\t(void(*)(qbyte*out,void*ctx))MD4Final,\n};\n\n"
  },
  {
    "path": "engine/common/md5.c",
    "content": "/* md5.c - MD5 Message-Digest Algorithm\n * Copyright (C) 1995,1996,1998,1999,2001,2002,\n *               2003  Free Software Foundation, Inc.\n *\n * This file is part of Libgcrypt.\n *\n * Libgcrypt is free software; you can redistribute it and/or modify\n * it under the terms of the GNU Lesser General Public License as\n * published by the Free Software Foundation; either version 2.1 of\n * the License, or (at your option) any later version.\n *\n * Libgcrypt is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Lesser General Public License for more details.\n *\n * You should have received a copy of the GNU Lesser General Public\n * License along with this program; if not, see <https://www.gnu.org/licenses/>.\n * SPDX-License-Identifier: LGPL-2.1-or-later\n *\n * According to the definition of MD5 in RFC 1321 from April 1992.\n * NOTE: This is *not* the same file as the one from glibc.\n * Written by Ulrich Drepper <drepper@gnu.ai.mit.edu>, 1995.\n * heavily modified for GnuPG by Werner Koch <wk@gnupg.org>\n   further de-libgcrypted to make it a bit more standalone.\n */\n\n/* Test values:\n * \"\"                  D4 1D 8C D9 8F 00 B2 04  E9 80 09 98 EC F8 42 7E\n * \"a\"                 0C C1 75 B9 C0 F1 B6 A8  31 C3 99 E2 69 77 26 61\n * \"abc                90 01 50 98 3C D2 4F B0  D6 96 3F 7D 28 E1 7F 72\n * \"message digest\"    F9 6B 69 7D 7C B7 93 8D  52 5A 2F 31 AA F1 61 D0\n */\n\n#include \"quakedef.h\"\n\n\ntypedef struct {\n\tunsigned char buf[64];\n\tqint32_t count;\t//count in the buffer\n\tquint64_t totalbytes;\n    quint32_t A,B,C,D;\t  /* chaining variables */\n} MD5_CONTEXT;\n\nstatic void\nmd5_init( void *context)\n{\n\tMD5_CONTEXT *ctx = context;\n\n\tctx->A = 0x67452301;\n\tctx->B = 0xefcdab89;\n\tctx->C = 0x98badcfe;\n\tctx->D = 0x10325476;\n\n\tctx->totalbytes = 0;\n\tctx->count = 0;\n}\n\n\n/* These are the four functions used in the four steps of the MD5 algorithm\n   and defined in the RFC 1321.  The first function is a little bit optimized\n   (as found in Colin Plumbs public domain implementation).  */\n/* #define FF(b, c, d) ((b & c) | (~b & d)) */\n#define FF(b, c, d) (d ^ (b & (c ^ d)))\n#define FG(b, c, d) FF (d, b, c)\n#define FH(b, c, d) (b ^ c ^ d)\n#define FI(b, c, d) (c ^ (b | ~d))\n\n#define buf_put_le32(b,d) ((b)[0]=(d)&0xff, (b)[1]=((d)>>8)&0xff, (b)[2]=((d)>>16)&0xff, (b)[3]=((d)>>24)&0xff)\n#define buf_get_le32(b) ((b)[0]|((b)[1]<<8)|((b)[2]<<16)|((b)[3]<<24))\n#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))\n\n\n/****************\n * transform 64 bytes\n */\nstatic void\nmd5_transform_blk ( void *c, const unsigned char *data )\n{\n\tMD5_CONTEXT *ctx = c;\n\tquint32_t correct_words[16];\n\tregister quint32_t A = ctx->A;\n\tregister quint32_t B = ctx->B;\n\tregister quint32_t C = ctx->C;\n\tregister quint32_t D = ctx->D;\n\tquint32_t *cwp = correct_words;\n\tint i;\n\n\tfor ( i = 0; i < 16; i++ )\n\t\tcorrect_words[i] = buf_get_le32(data + i * 4);\n\n\t#define OP(a, b, c, d, s, T)\t\\\n\t\tdo\t\t\t\t\t\t\t\\\n\t\t{\t\t\t\t\t\t\t\\\n\t\t\ta += FF (b, c, d) + (*cwp++) + T;\t\\\n\t\t\ta = rol(a, s);\t\t\t\\\n\t\t\ta += b;\t\t\t\t\t\\\n\t\t} while (0)\n\n\t/* Before we start, one word about the strange constants.\n\tThey are defined in RFC 1321 as\n\n\tT[i] = (int) (4294967296.0 * fabs (sin (i))), i=1..64\n\t*/\n\n\t/* Round 1.  */\n\tOP (A, B, C, D,  7, 0xd76aa478);\n\tOP (D, A, B, C, 12, 0xe8c7b756);\n\tOP (C, D, A, B, 17, 0x242070db);\n\tOP (B, C, D, A, 22, 0xc1bdceee);\n\tOP (A, B, C, D,  7, 0xf57c0faf);\n\tOP (D, A, B, C, 12, 0x4787c62a);\n\tOP (C, D, A, B, 17, 0xa8304613);\n\tOP (B, C, D, A, 22, 0xfd469501);\n\tOP (A, B, C, D,  7, 0x698098d8);\n\tOP (D, A, B, C, 12, 0x8b44f7af);\n\tOP (C, D, A, B, 17, 0xffff5bb1);\n\tOP (B, C, D, A, 22, 0x895cd7be);\n\tOP (A, B, C, D,  7, 0x6b901122);\n\tOP (D, A, B, C, 12, 0xfd987193);\n\tOP (C, D, A, B, 17, 0xa679438e);\n\tOP (B, C, D, A, 22, 0x49b40821);\n\n\t#undef OP\n\t#define OP(f, a, b, c, d, k, s, T)\t\\\n\t\tdo\t\t\t\t\t\t\t\t\\\n\t\t{\t\t\t\t\t\t\t\t\\\n\t\t\ta += f (b, c, d) + correct_words[k] + T;\t\\\n\t\t\ta = rol(a, s);\t\t\t\t\\\n\t\t\ta += b;\t\t\t\t\t\t\\\n\t\t} while (0)\n\n\t/* Round 2.  */\n\tOP (FG, A, B, C, D,  1,  5, 0xf61e2562);\n\tOP (FG, D, A, B, C,  6,  9, 0xc040b340);\n\tOP (FG, C, D, A, B, 11, 14, 0x265e5a51);\n\tOP (FG, B, C, D, A,  0, 20, 0xe9b6c7aa);\n\tOP (FG, A, B, C, D,  5,  5, 0xd62f105d);\n\tOP (FG, D, A, B, C, 10,  9, 0x02441453);\n\tOP (FG, C, D, A, B, 15, 14, 0xd8a1e681);\n\tOP (FG, B, C, D, A,  4, 20, 0xe7d3fbc8);\n\tOP (FG, A, B, C, D,  9,  5, 0x21e1cde6);\n\tOP (FG, D, A, B, C, 14,  9, 0xc33707d6);\n\tOP (FG, C, D, A, B,  3, 14, 0xf4d50d87);\n\tOP (FG, B, C, D, A,  8, 20, 0x455a14ed);\n\tOP (FG, A, B, C, D, 13,  5, 0xa9e3e905);\n\tOP (FG, D, A, B, C,  2,  9, 0xfcefa3f8);\n\tOP (FG, C, D, A, B,  7, 14, 0x676f02d9);\n\tOP (FG, B, C, D, A, 12, 20, 0x8d2a4c8a);\n\n\t/* Round 3.  */\n\tOP (FH, A, B, C, D,  5,  4, 0xfffa3942);\n\tOP (FH, D, A, B, C,  8, 11, 0x8771f681);\n\tOP (FH, C, D, A, B, 11, 16, 0x6d9d6122);\n\tOP (FH, B, C, D, A, 14, 23, 0xfde5380c);\n\tOP (FH, A, B, C, D,  1,  4, 0xa4beea44);\n\tOP (FH, D, A, B, C,  4, 11, 0x4bdecfa9);\n\tOP (FH, C, D, A, B,  7, 16, 0xf6bb4b60);\n\tOP (FH, B, C, D, A, 10, 23, 0xbebfbc70);\n\tOP (FH, A, B, C, D, 13,  4, 0x289b7ec6);\n\tOP (FH, D, A, B, C,  0, 11, 0xeaa127fa);\n\tOP (FH, C, D, A, B,  3, 16, 0xd4ef3085);\n\tOP (FH, B, C, D, A,  6, 23, 0x04881d05);\n\tOP (FH, A, B, C, D,  9,  4, 0xd9d4d039);\n\tOP (FH, D, A, B, C, 12, 11, 0xe6db99e5);\n\tOP (FH, C, D, A, B, 15, 16, 0x1fa27cf8);\n\tOP (FH, B, C, D, A,  2, 23, 0xc4ac5665);\n\n\t/* Round 4.  */\n\tOP (FI, A, B, C, D,  0,  6, 0xf4292244);\n\tOP (FI, D, A, B, C,  7, 10, 0x432aff97);\n\tOP (FI, C, D, A, B, 14, 15, 0xab9423a7);\n\tOP (FI, B, C, D, A,  5, 21, 0xfc93a039);\n\tOP (FI, A, B, C, D, 12,  6, 0x655b59c3);\n\tOP (FI, D, A, B, C,  3, 10, 0x8f0ccc92);\n\tOP (FI, C, D, A, B, 10, 15, 0xffeff47d);\n\tOP (FI, B, C, D, A,  1, 21, 0x85845dd1);\n\tOP (FI, A, B, C, D,  8,  6, 0x6fa87e4f);\n\tOP (FI, D, A, B, C, 15, 10, 0xfe2ce6e0);\n\tOP (FI, C, D, A, B,  6, 15, 0xa3014314);\n\tOP (FI, B, C, D, A, 13, 21, 0x4e0811a1);\n\tOP (FI, A, B, C, D,  4,  6, 0xf7537e82);\n\tOP (FI, D, A, B, C, 11, 10, 0xbd3af235);\n\tOP (FI, C, D, A, B,  2, 15, 0x2ad7d2bb);\n\tOP (FI, B, C, D, A,  9, 21, 0xeb86d391);\n\n\t/* Put checksum in context given as argument.  */\n\tctx->A += A;\n\tctx->B += B;\n\tctx->C += C;\n\tctx->D += D;\n}\n\nstatic void md5_update(void *ctx, const void *in, size_t sz)\n{\n\tMD5_CONTEXT *hd = ctx;\n\thd->totalbytes += sz;\t//need this for later.\n\tif (hd->count)\n\t{\t//pack any extra data into our temp buffer, if we've already got data there.\n\t\tsize_t pre = sizeof(hd->buf) - hd->count;\n\t\tif (pre > sz)\n\t\t\tpre = sz;\n\t\tmemcpy(hd->buf + hd->count, in, sz);\n\t\tsz -= pre;\n\t\tin = (const qbyte*)in + pre;\n\t}\n\tif (hd->count == sizeof(hd->buf))\n\t{\t//if we filled it then process it.\n\t\tmd5_transform_blk(ctx, hd->buf);\n\t\thd->count = 0;\n\t}\n\twhile (sz >= sizeof(hd->buf))\n\t{\t//and process the bulk of it.\n\t\tmd5_transform_blk(ctx, in);\n\t\tsz -= sizeof(hd->buf);\n\t\tin = (const qbyte*)in + sizeof(hd->buf);\n\t}\n\t//save off any extra data.\n\tmemcpy(hd->buf, in, sz);\n\thd->count = sz;\n}\n\n/* The routine final terminates the message-digest computation and\n * ends with the desired message digest in mdContext->digest[0...15].\n * The handle is prepared for a new MD5 cycle.\n * Returns 16 bytes representing the digest.\n */\n\nstatic void\nmd5_final(qbyte *digest, void *context)\n{\n\tMD5_CONTEXT *hd = context;\n\tqbyte *p;\n\tquint64_t origbigcount = hd->totalbytes<<3;\n\n\thd->buf[hd->count++] = 0x80;\t//pad the data with a 1 (md5_update keeps at least one byte of the buffer free).\n\tif (hd->count > 56)\n\t{\t//not enough space to fix the total count. fill with 0s.\n\t\tif (hd->count < sizeof(hd->buf))\n\t\t\thd->buf[hd->count++] = 0;\t\t\t//pad any remaining\n\t\tmd5_transform_blk(context, hd->buf);\n\t\thd->count = 0;\n\t}\n\twhile(hd->count < 56)\t//we need to fill the entire thing, pretty much\n\t\thd->buf[hd->count++] = 0;\t\t\t//pad any remaining\n\n\tbuf_put_le32(hd->buf + 56, (origbigcount));\n\tbuf_put_le32(hd->buf + 60, (origbigcount>>32));\n\tmd5_transform_blk(context, hd->buf);\n\n\tp = digest;\n\t#define X(a) do { buf_put_le32(p, hd->a); p += 4; } while(0)\n\tX(A);\n\tX(B);\n\tX(C);\n\tX(D);\n\t#undef X\n\n\tmemset(hd->buf, 0, sizeof(hd->buf));\n\thd->count = 0;\n}\n\nhashfunc_t hash_md5 =\n{\n\t16,\n\tsizeof(MD5_CONTEXT),\n\tmd5_init,\n\tmd5_update,\n\tmd5_final,\n};\n\n/*\n__attribute__((constructor)) void md5_unit_test(void)\n{\n\tqbyte digest[16];\n\n\tCalcHash(&hash_md5, digest, sizeof(digest), \"\", 0);\n\tif (memcmp(digest, \"\\xD4\\x1D\\x8C\\xD9\\x8F\\x00\\xB2\\x04\" \"\\xE9\\x80\\x09\\x98\\xEC\\xF8\\x42\\x7E\", 16))\n\t\tprintf(\"fail\\n\");\n\n\tCalcHash(&hash_md5, digest, sizeof(digest), \"a\", 1);\n\tif (memcmp(digest, \"\\x0C\\xC1\\x75\\xB9\\xC0\\xF1\\xB6\\xA8\" \"\\x31\\xC3\\x99\\xE2\\x69\\x77\\x26\\x61\", 16))\n\t\tprintf(\"fail\\n\");\n\n\tCalcHash(&hash_md5, digest, sizeof(digest), \"abc\", 3);\n\tif (memcmp(digest, \"\\x90\\x01\\x50\\x98\\x3C\\xD2\\x4F\\xB0\" \"\\xD6\\x96\\x3F\\x7D\\x28\\xE1\\x7F\\x72\", 16))\n\t\tprintf(\"fail\\n\");\n\n\tCalcHash(&hash_md5, digest, sizeof(digest), \"message digest\", 14);\n\tif (memcmp(digest, \"\\xF9\\x6B\\x69\\x7D\\x7C\\xB7\\x93\\x8D\" \"\\x52\\x5A\\x2F\\x31\\xAA\\xF1\\x61\\xD0\", 16))\n\t\tprintf(\"fail\\n\");\n }*/\n"
  },
  {
    "path": "engine/common/net.h",
    "content": "/*\nCopyright (C) 1996-1997 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  \n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n*/\n// net.h -- quake's interface to the networking layer\n\n#define\tPORT_ANY\t-1\n\n#if defined(FTE_TARGET_WEB)\n#define HAVE_WEBSOCKCL\n#endif\n\n#ifdef __linux__\n//#define UNIXSOCKETS\n#endif\n\n//FIXME: should split this into loopback/dgram/stream/dtls/tls/irc\n//with the ipv4/v6/x as a separate parameter\ntypedef enum {\n\tNA_INVALID,\n\tNA_LOOPBACK,\n\t/*NA_HYBRID,*/\t//ipv6 hybrid socket that might accept ipv4 packets too.\n\tNA_IP,\n\tNA_IPV6,\n\tNA_IPX,\n#ifdef UNIXSOCKETS\n\tNA_UNIX,\n#endif\n#ifdef IRCCONNECT\n\tNA_IRC/*remove!*/,\n#endif\n#ifdef HAVE_WEBSOCKCL\n\tNA_WEBSOCKET,\n#endif\n#ifdef SUPPORT_ICE\n\tNA_ICE\n#endif\n} netadrtype_t;\ntypedef enum {\n\tNP_DGRAM,\n\tNP_DTLS,\t//connected via ICE/WebRTC\n\tNP_KEXLAN,\t//layered over some silly lobby mess\n#define NP_ISLAYERED(np) (np>=NP_DTLS && np<=NP_KEXLAN)\n\n\tNP_STREAM,\n\tNP_TLS,\n\tNP_WS,\n\tNP_WSS,\n\tNP_NATPMP,\t//server-only scheme for registering public ports.\n\tNP_RTC_TCP,\n\tNP_RTC_TLS,\t//really need a better way to do this than two copies of every protocol...\n\n\tNP_INVALID\n} netproto_t;\n\ntypedef struct netadr_s\n{\n\tnetadrtype_t\ttype;\n\tnetproto_t\t\tprot;\n\n\tunsigned short\tport;\t//stored as network-endian.\n\tunsigned short\tconnum;\t//which quake connection/socket the address is talking about. 1-based. 0 is unspecified. this is NOT used for address equivelency.\n\tunsigned int scopeid;\t//ipv6 interface id thing.\n\n\tunion {\n\t\tqbyte\tip[4];\n\t\tqbyte\tip6[16];\n\t\tqbyte\tipx[10];\n#ifdef IRCCONNECT\n\t\tstruct {\n\t\t\tchar host[32];\n\t\t\tchar user[32];\n\t\t\tchar channel[12];\n\t\t} irc;\n#endif\n#ifdef SUPPORT_ICE\n\t\tchar icename[64];\n#endif\n#ifdef HAVE_WEBSOCKCL\n\t\tchar websocketurl[64];\n#endif\n#ifdef UNIXSOCKETS\n\t\tstruct\n\t\t{\n\t\t\tint len;\t//abstract addresses contain nulls, so this is needed.\n\t\t\tchar path[108];\n\t\t} un;\n#endif\n\t} address;\n} netadr_t;\n\nstruct sockaddr_qstorage\n{\n\tshort dontusesa_family;\n\tunsigned char dontusesa_pad[6];\n\tqint64_t sa_align;\n\tunsigned char sa_pad2[112];\n};\n\n\nextern\tnetadr_t\tnet_local_cl_ipadr;\nextern\tnetadr_t\tnet_from;\t\t// address of who sent the packet\nextern\tstruct ftenet_generic_connection_s *net_from_connection;\nextern\tsizebuf_t\tnet_message;\n//#define\tMAX_UDP_PACKET\t(MAX_MSGLEN*2)\t// one more than msg + header\n#define\tMAX_UDP_PACKET\t8192\t// one more than msg + header\nextern\tFTE_ALIGN(4) qbyte\t\tnet_message_buffer[MAX_OVERALLMSGLEN];\n\ntypedef enum\n{\n\tNETERR_SENT\t\t\t= 0,\t//all is well\n\tNETERR_NOROUTE\t\t= 1,\t//destination isn't valid for this socket/etc. try a different one if possible\n\tNETERR_DISCONNECTED = 2,\t//socket can no longer send anything\n\tNETERR_MTU\t\t\t= 3,\t//packet wasn't sent due to MTU\n\tNETERR_CLOGGED\t\t= 4\t\t//socket is suffering from conjestion\n} neterr_t;\n\nextern\tcvar_t\thostname;\n\nint TCP_OpenStream (netadr_t *remoteaddr, const char *remotename);\t//makes things easier. remotename is printable-only\n\nstruct ftenet_connections_s;\nvoid\t\tNET_Init (void);\nvoid\t\tNET_Tick (void);\nvoid\t\tSVNET_RegisterCvars(void);\nvoid\t\tNET_InitClient (qboolean loopbackonly);\nvoid\t\tNET_CloseClient(void);\nvoid\t\tNET_InitServer (void);\nqboolean\tNET_WasSpecialPacket(struct ftenet_connections_s *col);\nvoid\t\tNET_CloseServer (void);\nvoid\t\tUDP_CloseSocket (int socket);\nvoid\t\tNET_Shutdown (void);\nqboolean\tNET_GetRates(struct ftenet_connections_s *collection, float *pi, float *po, float *bi, float *bo);\nqboolean\tNET_UpdateRates(struct ftenet_connections_s *collection, qboolean inbound, size_t size);\t//for demos to not be weird\nvoid\t\tNET_ReadPackets (struct ftenet_connections_s *collection);\nneterr_t\tNET_SendPacket (struct ftenet_connections_s *col, int length, const void *data, netadr_t *to);\nint\t\t\tNET_LocalAddressForRemote(struct ftenet_connections_s *collection, netadr_t *remote, netadr_t *local, int idx);\nvoid\t\tNET_PrintAddresses(struct ftenet_connections_s *collection);\nqboolean\tNET_AddressSmellsFunny(netadr_t *a);\nstruct dtlspeercred_s;\nqboolean\tNET_EnsureRoute(struct ftenet_connections_s *collection, char *routename, const struct dtlspeercred_s *peerinfo, const char *adrstring, netadr_t *adr, qboolean outgoing);\nvoid\t\tNET_TerminateRoute(struct ftenet_connections_s *collection, netadr_t *adr);\nvoid\t\tNET_PrintConnectionsStatus(struct ftenet_connections_s *collection);\n\nenum addressscope_e\n{\n\tASCOPE_PROCESS=0,\t//unusable\n\tASCOPE_HOST=1,\t\t//unroutable\n\tASCOPE_LINK=2,\t\t//unpredictable\n\tASCOPE_LAN=3,\t\t//private\n\tASCOPE_NET=4\t\t//aka hopefully globally routable\n};\nenum addressscope_e NET_ClassifyAddress(netadr_t *adr, const char **outdesc);\n\nstruct urischeme_s\n{\n\tconst char *name;\n\tnetproto_t prot;\t\t//NP_INVALID means its a game-specific one that should really be handled elsewhere but is silently ignored by the netcode.\n\tnetadrtype_t family;\t//usually NA_INVALID, unless ipv4/ipv6-specific\n\tenum\n\t{\n\t\tURISCHEME_NEEDSRESOURCE = (1<<0),\t//forwards it on to the server\n\t} flags;\n};\nconst struct urischeme_s *NET_IsURIScheme(const char *possible);\n\nqboolean NET_AddrIsReliable(netadr_t *adr);\t//hints that the protocol is reliable. if so, we don't need to wait for acks\nqboolean\tNET_IsEncrypted(netadr_t *adr);\nqboolean\tNET_CompareAdr (netadr_t *a, netadr_t *b);\nqboolean\tNET_CompareBaseAdr (netadr_t *a, netadr_t *b);\nvoid\t\tNET_AdrToStringResolve (netadr_t *adr, void (*resolved)(void *ctx, void *data, size_t a, size_t b), void *ctx, size_t a, size_t b);\nchar\t\t*NET_AdrToString (char *s, int len, netadr_t *a);\nchar\t\t*NET_SockadrToString (char *s, int len, struct sockaddr_qstorage *a, size_t sizeofa);\nchar\t\t*NET_BaseAdrToString (char *s, int len, netadr_t *a);\nsize_t\t\tNET_StringToSockaddr2 (const char *s, int defaultport, netadrtype_t afhint, struct sockaddr_qstorage *sadr, int *addrfamily, int *addrsize, size_t addrcount);\nqboolean NET_StringToAdr_NoDNS(const char *address, int port, netadr_t *out);\n#define NET_StringToSockaddr(s,p,a,f,z) (NET_StringToSockaddr2(s,p,NA_INVALID,a,f,z,1)>0)\nsize_t\t\tNET_StringToAdr2 (const char *s, int defaultport, netadr_t *a, size_t addrcount, const char **pathstart);\n#define NET_StringToAdr(s,p,a) NET_StringToAdr2(s,p,a,1,NULL)\nqboolean\tNET_PortToAdr (netadrtype_t adrfamily, netproto_t adrprot, const char *s, netadr_t *a);\nqboolean NET_IsClientLegal(netadr_t *adr);\n\nqboolean\tNET_IsLoopBackAddress (netadr_t *adr);\n\nqboolean NET_StringToAdrMasked (const char *s, qboolean allowdns, netadr_t *a, netadr_t *amask);\nchar\t*NET_AdrToStringMasked (char *s, int len, netadr_t *a, netadr_t *amask);\nvoid NET_IntegerToMask (netadr_t *a, netadr_t *amask, int bits);\nqboolean NET_CompareAdrMasked(netadr_t *a, netadr_t *b, netadr_t *mask);\n\nqboolean FTENET_AddToCollection(struct ftenet_connections_s *col, const char *name, const char *address, netadrtype_t addrtype, netproto_t addrprot);\n\nenum certprops_e\n{\n\tQCERT_ISENCRYPTED,\t\t//0 or error\n\tQCERT_PEERSUBJECT,\t\t//null terminated. should be a hash of the primary cert, ignoring chain.\n\tQCERT_PEERCERTIFICATE,\t//should be the primary cert, ignoring chain. no fixed maximum size required, mostly 2k but probably best to allow at leasy 5k.. or 8k.\n\n\tQCERT_LOCALCERTIFICATE,\t//the cert we're using/advertising. may have no context. to tell people what fp to expect.\n\n\tQCERT_LOBBYSTATUS,\t\t//for special-case lobby wrappers.\n\tQCERT_LOBBYSENDCHAT,\t//to send chat via the stupid lobby instead of the game itself.\n};\nint NET_GetConnectionCertificate(struct ftenet_connections_s *col, netadr_t *a, enum certprops_e prop, char *out, size_t outsize);\n\n#ifdef HAVE_DTLS\nstruct dtlscred_s;\nstruct dtlsfuncs_s;\nqboolean NET_DTLS_Create(struct ftenet_connections_s *col, netadr_t *to, const struct dtlscred_s *cred, qboolean outgoing);\nqboolean NET_DTLS_Decode(struct ftenet_connections_s *col);\nqboolean NET_DTLS_Disconnect(struct ftenet_connections_s *col, netadr_t *to);\nextern cvar_t dtls_psk_hint, dtls_psk_user, dtls_psk_key;\nextern cvar_t net_enable_dtls;\n#endif\n#ifdef SUPPORT_ICE\nneterr_t ICE_SendPacket(size_t length, const void *data, netadr_t *to);\nvoid ICE_Terminate(netadr_t *to); //if we kicked the client/etc, kill their ICE too.\nint ICE_GetPeerCertificate(netadr_t *to, enum certprops_e prop, char *out, size_t outsize);\nvoid ICE_Init(void);\n#endif\nextern cvar_t timeout;\nextern cvar_t tls_ignorecertificateerrors;\t//evil evil evil.\nstruct ftecrypto_s;\nqboolean NET_RegisterCrypto(void *module, struct ftecrypto_s *driver);\n\n//============================================================================\n\n#define\tOLD_AVG\t\t0.99\t\t// total = oldtotal*OLD_AVG + new*(1-OLD_AVG)\n\n//#define\tMAX_LATENT\t32\n#define MAX_ADR_SIZE\t64\n\ntypedef struct\n{\n\tqboolean\tfatal_error;\n\n#ifdef NQPROT\n\tint\tisnqprotocol;\n\tqboolean\tnqreliable_allowed;\t//says the peer has acked the last reliable (or timed out and needs resending).\n\tfloat\t\tnqreliable_resendtime;//force nqreliable_allowed, thereby forcing a resend of anything n\n\tqbyte\t\tnqunreliableonly;\t//nq can't cope with certain reliables some times. if 2, we have a reliable that result in a block (that should be sent). if 1, we are blocking. if 0, we can send reliables freely. if 3, then we just want to ignore clc_moves\n#endif\n\tunsigned int flags;\t//NCF_ bitmask\n\t\t\t#define NCF_CLIENT\t\t(1u<<0)\t//clientside sends the qport.\n\t\t\t#define NCF_SERVER\t\t(0u<<0)\t//serverside reads the qport.\n\t\t\t#define NCF_FRAGABLE\t(1u<<1)\t//fte's packet fragmentation extension, to avoid issues with low mtus.\n\t\t\t#define NCF_STUNAWARE\t(1u<<2)\t//prevent the two lead-bits of packets from being either 0(stun), so stray stun packets cannot mess things up for us.\n\tstruct netprim_s netprim;\n\tint\t\t\tdupe;\t\t\t\t//how many times to dupe packets\n\n\t//Packetisation Layer Path MTU Discovery (PLPMTUD / RFC4821)\n\tint\t\t\tmtu_cur;\t\t\t\t//the path mtu, if known\n\tint\t\t\tmtu_min;\t\t\t\t//minimum mtu to drop to. lower values being abusive.\n\tint\t\t\tmtu_max;\t\t\t\t//the size the user specified.\n\tint\t\t\tmtu_resends;\t\t\t//number of times we had to resend a reliable.\n\tint\t\t\tmtu_overhead;\t\t\t//extra slop vs a\n\tint\t\t\toutgoing_mtu_probe;\t\t//sequence number that was a probe. if its acked we can send a new one straight away to grow fast. if its not acked, we back off.\n\tint\t\t\tmtu_probes;\t\t\t\t//number of probes sent without success. give up if we get no growth.\n\tdouble\t\tmtu_reprobetime;\t\t//send an extra mtu probe every 30ish secs to see if it grew\n\tunsigned short sentsizes[64];\t\t//to bump known mtu if an oversized packet got received.\n\tint\t\t\toutgoing_sequence_last;\t//to detect gaps in outgoing sequences (servers get forced to match client's)\n\n\tfloat\t\tlast_received;\t\t// for timeouts\n\n// the statistics are cleared at each client begin, because\n// the server connecting process gives a bogus picture of the data\n\tfloat\t\tframe_latency;\t\t// rolling average\n\tfloat\t\tframe_rate;\n\n\tint\t\t\tdrop_count;\t\t\t// dropped packets, cleared each level\n\n\tint\t\t\tbytesin;\n\tint\t\t\tbytesout;\n\n\tnetadr_t\tremote_address;\n\tint\t\t\tqport;\n\tint\t\t\tqportsize;\n\n// bandwidth estimator\n\tdouble\t\tcleartime;\t\t\t// if realtime > nc->cleartime, free to go\n//\tdouble\t\trate;\t\t\t\t// seconds / qbyte\n\n// sequencing variables\n\tint\t\t\tincoming_unreliable;\t//dictated by the other end.\n\tint\t\t\tincoming_sequence;\n\tint\t\t\tincoming_acknowledged;\n\tint\t\t\tincoming_reliable_acknowledged;\t// single bit\n\n\tint\t\t\tincoming_reliable_sequence;\t\t// single bit, maintained local\n\n\tint\t\t\toutgoing_unreliable;\n\tint\t\t\toutgoing_sequence;\n\tint\t\t\treliable_sequence;\t\t\t// single bit\n\tint\t\t\tlast_reliable_sequence;\t\t// sequence number of last send\n\n// reliable staging and holding areas\n\tsizebuf_t\tmessage;\t\t// writing buffer to send to server\n\tqbyte\t\tmessage_buf[MAX_OVERALLMSGLEN];\n\n\t//nq has message truncation.\n\tint\t\t\treliable_length;\n\tint\t\t\treliable_start;\n\tqbyte\t\treliable_buf[MAX_OVERALLMSGLEN];\t// unacked reliable message\n\n// time and size data to calculate bandwidth\n//\tint\t\t\toutgoing_size[MAX_LATENT];\n//\tdouble\t\toutgoing_time[MAX_LATENT];\n\tstruct huffman_s\t*compresstable;\n\n\t//nq servers must recieve truncated packets.\n\tint in_fragment_length;\n\tchar in_fragment_buf[MAX_OVERALLMSGLEN];\n\tint in_fragment_start;\n} netchan_t;\n\nextern\tint\tnet_drop;\t\t// packets dropped before this one\n\nvoid Net_Master_Init(void);\n\nvoid Netchan_Init (void);\nsize_t Netchan_GetMaxUnreliable(netchan_t *chan);\nint Netchan_Transmit (netchan_t *chan, int length, qbyte *data, int rate);\nvoid Netchan_OutOfBand (unsigned int ncflags, netadr_t *adr, int length, const qbyte *data);\nvoid VARGS Netchan_OutOfBandPrint (unsigned int ncflags, netadr_t *adr, char *format, ...) LIKEPRINTF(3);\nvoid VARGS Netchan_OutOfBandTPrintf (unsigned int ncflags, netadr_t *adr, int language, translation_t text, ...);\nqboolean Netchan_Process (netchan_t *chan);\nvoid Netchan_Setup (unsigned int ncflags, netchan_t *chan, netadr_t *adr, int qport, unsigned int mtu);\nunsigned int Net_PextMask(unsigned int protover, qboolean fornq);\nextern cvar_t net_mtu;\n\nqboolean Netchan_CanPacket (netchan_t *chan, int rate);\nvoid Netchan_Block (netchan_t *chan, int bytes, int rate);\nqboolean Netchan_CanReliable (netchan_t *chan, int rate);\n#ifdef NQPROT\nenum nqnc_packettype_e\n{\n\tNQNC_IGNORED,\n\tNQNC_ACK,\n\tNQNC_RELIABLE,\n\tNQNC_UNRELIABLE,\n};\nenum nqnc_packettype_e NQNetChan_Process(netchan_t *chan);\n#endif\n\n#ifdef HUFFNETWORK\n#define HUFFCRC_QUAKE3 0x286f2e8d\n\ntypedef struct huffman_s huffman_t;\nint Huff_PreferedCompressionCRC (void);\nvoid Huff_EncryptPacket(sizebuf_t *msg, int offset);\nvoid Huff_DecryptPacket(sizebuf_t *msg, int offset);\nhuffman_t *Huff_CompressionCRC(int crc);\nvoid Huff_CompressPacket(huffman_t *huff, sizebuf_t *msg, int offset);\nvoid Huff_DecompressPacket(huffman_t *huff, sizebuf_t *msg, int offset);\nint Huff_GetByte(qbyte *buffer, int *count);\nvoid Huff_EmitByte(int ch, qbyte *buffer, int *count);\n#endif\n\n#ifdef NQPROT\n//taken from nq's net.h\n//refer to that for usage info. :)\n\n#define NETFLAG_LENGTH_MASK\t0x0000ffff\n#define NETFLAG_DATA\t\t0x00010000\n#define NETFLAG_ACK\t\t\t0x00020000\n#define NETFLAG_NAK\t\t\t0x00040000\n#define NETFLAG_EOM\t\t\t0x00080000\n#define NETFLAG_UNRELIABLE\t0x00100000\n#define NETFLAG_ZLIB\t\t0x00200000\t//QEx - payload contains the real (full) packet\n#define NETFLAG_CTL\t\t\t0x80000000\n\n#define NQ_NETCHAN_GAMENAME\t\"QUAKE\"\n#define NQ_NETCHAN_VERSION\t3\n#define NQ_NETCHAN_VERSION_QEX\t4\t//the rerelease's id, used for nqish-over dtls.\n\n\n#define CCREQ_CONNECT\t\t0x01\n#define CCREQ_SERVER_INFO\t0x02\n#define CCREQ_PLAYER_INFO\t0x03\n#define CCREQ_RULE_INFO\t\t0x04\n#define CCREQ_PROQUAKE_RCON\t0x05\n\n#define CCREP_ACCEPT\t\t0x81\n#define CCREP_REJECT\t\t0x82\n#define CCREP_SERVER_INFO\t0x83\n#define CCREP_PLAYER_INFO\t0x84\n#define CCREP_RULE_INFO\t\t0x85\n\n//server->client protocol info\n#define PROTOCOL_VERSION_NQ\t\t15\n#define PROTOCOL_VERSION_H2\t\t19\n#define PROTOCOL_VERSION_NEHD\t250\n#define PROTOCOL_VERSION_FITZ\t666\n#define PROTOCOL_VERSION_RMQ\t999\n#define PROTOCOL_VERSION_DP5\t3502\n#define PROTOCOL_VERSION_DP6\t3503\n#define PROTOCOL_VERSION_DP7\t3504\n#define PROTOCOL_VERSION_BJP1\t10000\n#define PROTOCOL_VERSION_BJP2\t10001\n#define PROTOCOL_VERSION_BJP3\t10002\n\n#define MOD_PROQUAKE 1\n//#define MOD_PROQUAKE_VERSION (10*3.1)\t\t//password feature added\n//#define MOD_PROQUAKE_VERSION (10*3.2)\t\t//first 'cheatfree'\n#define MOD_PROQUAKE_VERSION (10*3.3)\t\t//no real changes, but w/e, this is the highest we can claim without having serverside issues.\n//#define MOD_PROQUAKE_VERSION (10*3.4)\t\t//added nat wait weirdness that's redundant and breaks the whole single-port thing by using two ports on the client too. *sigh*.\n//#define MOD_PROQUAKE_VERSION (10*3.5)\t\t//optional cheatfree encryption\n//#define MOD_PROQUAKE_VERSION (10*4.51)\t//current version\n\n/*RMQ protocol flags*/\n#define RMQFL_SHORTANGLE\t(1 << 1)\n#define RMQFL_FLOATANGLE\t(1 << 2)\n#define RMQFL_24BITCOORD\t(1 << 3)\n#define RMQFL_FLOATCOORD\t(1 << 4)\n#define RMQFL_EDICTSCALE\t(1 << 5)\n#define RMQFL_ALPHASANITY\t(1 << 6)\n#define RMQFL_INT32COORD\t(1 << 7)\n#define RMQFL_MOREFLAGS\t\t(1 << 31)\n\n#endif\n\nint UDP_OpenSocket (int port);\nint UDP6_OpenSocket (int port);\nint IPX_OpenSocket (int port);\nint NetadrToSockadr (netadr_t *a, struct sockaddr_qstorage *s);\nvoid SockadrToNetadr (struct sockaddr_qstorage *s, int sizeofsockaddr, netadr_t *a);\nqboolean NET_Sleep(float seconds, qboolean stdinissocket);\n"
  },
  {
    "path": "engine/common/net_chan.c",
    "content": "/*\nCopyright (C) 1996-1997 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  \n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n*/\n\n#include \"quakedef.h\"\n\n#ifdef _WIN32\n#include \"winquake.h\"\n#else\n#include \"unistd.h\"\n#endif\n\n#define\tPACKET_HEADER\t8\n\n#define ANTISTUNBIAS 0x40000000\t//adding this to sequences in the header ensures that we our packets will not get confused for stun or rtp packets.\n\n/*\n\npacket header\n-------------\n31\tsequence\n1\tdoes this message contain a reliable payload\n31\tacknowledge sequence\n1\tacknowledge receipt of even/odd message\n16  qport (only from client)\n15  fragoffset (extension)\n1\tlastfrag (extension)\n\nThe remote connection never knows if it missed a reliable message, the\nlocal side detects that it has been dropped by seeing a sequence acknowledge\nhigher thatn the last reliable sequence, but without the correct even/odd\nbit for the reliable set.\n\nIf the sender notices that a reliable message has been dropped, it will be\nretransmitted.  It will not be retransmitted again until a message after\nthe retransmit has been acknowledged and the reliable still failed to get there.\n\nif the sequence number is -1, the packet should be handled without a netcon\n\nThe reliable message can be added to at any time by doing\nMSG_Write* (&netchan->message, <data>).\n\nIf the message buffer is overflowed, either by a single message, or by\nmultiple frames worth piling up while the last reliable transmit goes\nunacknowledged, the netchan signals a fatal error.\n\nReliable messages are always placed first in a packet, then the unreliable\nmessage is included if there is sufficient room.\n\nTo the receiver, there is no distinction between the reliable and unreliable\nparts of the message, they are just processed out as a single larger message.\n\nIllogical packet sequence numbers cause the packet to be dropped, but do\nnot kill the connection.  This, combined with the tight window of valid\nreliable acknowledgement numbers provides protection against malicious\naddress spoofing.\n\nThe qport field is a workaround for bad address translating routers that\nsometimes remap the client's source port on a packet during gameplay.\n\nIf the base part of the net address matches and the qport matches, then the\nchannel matches even if the IP port differs.  The IP port should be updated\nto the new value before sending out any replies.\n\nfragmentation works like IP, offset and morefrags. offset is *8 (decode: (offset&~1)<<2 to avoid stomping on the morefrags flag, this allows really jumbo packets with 18 bits of length)\n\n*/\n\nint\t\tnet_drop;\ncvar_t\tshowpackets = CVAR(\"showpackets\", \"0\");\ncvar_t\tshowdrop = CVAR(\"showdrop\", \"0\");\ncvar_t\tqport = CVARF(\"qport_\", \"0\", CVAR_NOSAVE);\n#ifdef FTE_TARGET_WEB //with webrtc our packets will be layered over sctp(header=28,extras=20ish) over dtls(13),\ncvar_t\tnet_mtu = CVARD(\"net_mtu\", \"1384\", \"Specifies a maximum udp payload size, above which packets will be fragmented. If routers all worked properly this could be some massive value, and some massive value may work really nicely for lans. Use smaller values than the default if you're connecting through nested tunnels through routers that fail with IP fragmentation.\");\n#else\ncvar_t\tnet_mtu = CVARD(\"net_mtu\", \"1440\", \"Specifies a maximum udp payload size, above which packets will be fragmented. If routers all worked properly this could be some massive value, and some massive value may work really nicely for lans. Use smaller values than the default if you're connecting through nested tunnels through routers that fail with IP fragmentation.\");\n#endif\ncvar_t\tnet_compress = CVARD(\"net_compress\", \"0\", \"Enables huffman compression of network packets.\");\n\ncvar_t\tpext_vrinputs = CVARD(\"_pext_vrinputs\", \"0\", \"RENAME ME WHEN STABLE. Networks player inputs slightly differently, allowing for greater capabilities, particuarly vr controller info.\");\ncvar_t\tpext_lerptime = CVARD(\"_pext_lerptime\", \"0\", \"RENAME ME WHEN STABLE. Sends timing hints for interpolation.\");\ncvar_t\tpext_infoblobs = CVARD(\"_pext_infoblobs\", \"0\", \"RENAME ME WHEN STABLE. Enables the use of very large infokeys containing potentially invalid chars. Note that the userinfo is still limited by sv_userinfo_bytelimit and sv_userinfo_keylimit.\");\ncvar_t\tpext_replacementdeltas = CVARD(\"pext_replacementdeltas\", \"1\", \"Enables the use of alternative nack-based entity deltas\");\ncvar_t\tpext_predinfo = CVARD(\"pext_predinfo\", \"1\", \"Enables some extra things to support prediction over NQ protocols.\");\nextern cvar_t net_fakemtu;\n\n#if defined(HAVE_CLIENT) && defined(HAVE_SERVER)\n#define NET_SendPacket(c,s,d,t) NET_SendPacket((c&NCF_CLIENT)?cls.sockets:svs.sockets,s,d,t)\n#elif defined(HAVE_SERVER)\n#define NET_SendPacket(c,s,d,t) NET_SendPacket(svs.sockets,s,d,t)\n#else\n#define NET_SendPacket(c,s,d,t) NET_SendPacket(cls.sockets,s,d,t)\n#endif\n\n/*returns the entire bitmask of supported+enabled extensions*/\nunsigned int Net_PextMask(unsigned int protover, qboolean fornq)\n{\n\tunsigned int mask = 0;\n\tif (protover == PROTOCOL_VERSION_FTE1)\n\t{\n\t#ifdef PEXT_SCALE\n\t\tmask |= PEXT_SCALE;\n\t#endif\n\t#ifdef PEXT_LIGHTSTYLECOL\n\t\tmask |= PEXT_LIGHTSTYLECOL;\n\t#endif\n\t#ifdef PEXT_TRANS\n\t\tmask |= PEXT_TRANS;\n\t#endif\n\t#ifdef PEXT_VIEW2\n\t\tmask |= PEXT_VIEW2;\n\t#endif\n\t#ifdef PEXT_ACCURATETIMINGS\n\t\t#ifdef QUAKESTATS\t//needs stat_time\n\t\t\tmask |= PEXT_ACCURATETIMINGS;\n\t\t#endif\n\t#endif\n\t#ifdef PEXT_ZLIBDL\n\t\tmask |= PEXT_ZLIBDL;\n\t#endif\n\t#ifdef PEXT_FATNESS\n\t\tmask |= PEXT_FATNESS;\n\t#endif\n\t#ifdef PEXT_HLBSP\n\t\tmask |= PEXT_HLBSP;\n\t#endif\n\n\t#ifdef PEXT_Q2BSP\n\t\tmask |= PEXT_Q2BSP;\n\t#endif\n\t#ifdef PEXT_Q3BSP\n\t\tmask |= PEXT_Q3BSP;\n\t#endif\n\n\t#ifdef PEXT_TE_BULLET\n\t\tmask |= PEXT_TE_BULLET;\n\t#endif\n\t#ifdef PEXT_HULLSIZE\n\t\tmask |= PEXT_HULLSIZE;\n\t#endif\n\t#ifdef PEXT_SETVIEW\n\t\tmask |= PEXT_SETVIEW;\n\t#endif\n\t#ifdef PEXT_MODELDBL\n\t\tmask |= PEXT_MODELDBL;\n\t#endif\n\t#ifdef PEXT_SOUNDDBL\n\t\tmask |= PEXT_SOUNDDBL;\n\t#endif\n\t#ifdef PEXT_VWEAP\n\t\tmask |= PEXT_VWEAP;\n\t#endif\n\t#ifdef PEXT_FLOATCOORDS\n\t\tmask |= PEXT_FLOATCOORDS;\n\t#endif\n\t\tmask |= PEXT_SPAWNSTATIC2;\n\t\tmask |= PEXT_COLOURMOD;\n\t#if MAX_SPLITS > 1\n\t\tmask |= PEXT_SPLITSCREEN;\n\t#endif\n\t\tmask |= PEXT_HEXEN2;\n\t\tmask |= PEXT_CUSTOMTEMPEFFECTS;\n\t\tmask |= PEXT_256PACKETENTITIES;\n\t\tmask |= PEXT_ENTITYDBL;\n\t\tmask |= PEXT_ENTITYDBL2;\n\t\tmask |= PEXT_SHOWPIC;\n\t\tmask |= PEXT_SETATTACHMENT;\n\t#ifdef PEXT_CHUNKEDDOWNLOADS\n\t\tmask |= PEXT_CHUNKEDDOWNLOADS;\n\t#endif\n\t#ifdef CSQC_DAT\n\t\tmask |= PEXT_CSQC;\n\t#endif\n\t#ifdef PEXT_DPFLAGS\n\t\tmask |= PEXT_DPFLAGS;\n\t#endif\n\n\t\tif (fornq)\n\t\t{\n\t\t\t//only ones that are tested\n\t\t\tmask &= \n#ifdef CSQC_DAT\n\t\t\t\t\tPEXT_CSQC |\n#endif\n#ifdef PEXT_Q2BSP\n\t\t\t\t\tPEXT_Q2BSP |\n#endif\n#ifdef PEXT_Q3BSP\n\t\t\t\t\tPEXT_Q3BSP |\n#endif\n\t\t\t\t\tPEXT_TE_BULLET |\t//qw's gunshot+explosions etc.\n\t\t\t\t\tPEXT_FLOATCOORDS | PEXT_HLBSP;\n\n\t\t\t//these all depend fully upon the player/entity deltas, and don't make sense for NQ. Implement PEXT2_REPLACEMENTDELTAS instead.\n\t\t\tmask &= ~(PEXT_SCALE|PEXT_TRANS|PEXT_ACCURATETIMINGS|PEXT_FATNESS|PEXT_HULLSIZE|PEXT_MODELDBL|PEXT_ENTITYDBL|PEXT_ENTITYDBL2|PEXT_COLOURMOD|PEXT_SPAWNSTATIC2|PEXT_256PACKETENTITIES|PEXT_SETATTACHMENT|PEXT_DPFLAGS); \n\t\t}\n\t}\n\telse if (protover == PROTOCOL_VERSION_FTE2)\n\t{\n\t\tmask |= PEXT2_PRYDONCURSOR;\n\t#ifdef PEXT2_VOICECHAT\n\t\tmask |= PEXT2_VOICECHAT;\n\t#endif\n\t\tmask |= PEXT2_SETANGLEDELTA;\n\n\t\tif (pext_replacementdeltas.ival)\n\t\t{\n\t\t\tmask |= PEXT2_REPLACEMENTDELTAS;\n\t\t\tif (/*fornq &&*/ pext_predinfo.ival)\n\t\t\t\tmask |= PEXT2_PREDINFO;\n\n\t\t\tif (pext_vrinputs.ival)\n\t\t\t\tmask |= PEXT2_VRINPUTS;\n\n\t\t\tif (pext_lerptime.ival)\n\t\t\t\tmask |= PEXT2_LERPTIME;\n\n\t\t\tmask |= PEXT2_NEWSIZEENCODING;\t//use if we can\n\t\t}\n\n\t\tif (pext_infoblobs.ival)\n\t\t\tmask |= PEXT2_INFOBLOBS;\n\n\t\tif (MAX_CLIENTS != QWMAX_CLIENTS)\n\t\t\tmask |= PEXT2_MAXPLAYERS;\n\n\t\tmask |= PEXT2_STUNAWARE;\n\n\t\tif (fornq)\n\t\t{\n\t\t\t//only ones that are tested\n\t\t\tmask &= PEXT2_PRYDONCURSOR | PEXT2_VOICECHAT | PEXT2_SETANGLEDELTA | PEXT2_REPLACEMENTDELTAS | PEXT2_MAXPLAYERS | PEXT2_PREDINFO | PEXT2_NEWSIZEENCODING | PEXT2_VRINPUTS | PEXT2_LERPTIME;\n\t\t}\n//\t\telse\n//\t\t\tmask &= ~PEXT2_PREDINFO;\n\t}\n\telse if (protover == PROTOCOL_VERSION_EZQUAKE1)\n\t{\n\t\tmask = EZPEXT1_FLOATENTCOORDS;//|EZPEXT1_SETANGLEREASON;\n\n\t\tif (fornq)\n\t\t{\n\t\t\tmask &= ~EZPEXT1_FLOATENTCOORDS;\t//keep things simple. interactions are not defined.\n\t\t\tmask &= ~EZPEXT1_SETANGLEREASON;\t//potentially breaks too many nq mods. don't encourage it.\n\t\t}\n\t}\n\n\treturn mask;\n}\n\n/*\n===============\nNetchan_Init\n\n===============\n*/\nvoid Netchan_Init (void)\n{\n\tstatic char qportstr[16];\n\tint\t\tport;\n\n\t// pick a port value that should be nice and random\n#ifdef _WIN32\n\tport = (time(NULL)) & 0xffff;\n#else\n\tport = ((int)(getpid()+getuid()*1000) * time(NULL)) & 0xffff;\n#endif\n\tQ_snprintfz(qportstr, sizeof(qportstr), \"%i\", port);\n\tqport.enginevalue = qportstr;\n\n\tCvar_Register (&pext_predinfo, \"Protocol Extensions\");\n\tCvar_Register (&pext_replacementdeltas, \"Protocol Extensions\");\n\tCvar_Register (&pext_infoblobs, \"Protocol Extensions\");\n\tCvar_Register (&pext_vrinputs, \"Protocol Extensions\");\n\tCvar_Register (&pext_lerptime, \"Protocol Extensions\");\n\tCvar_Register (&showpackets, \"Networking\");\n\tCvar_Register (&showdrop, \"Networking\");\n\tCvar_Register (&qport, \"Networking\");\n\tCvar_Register (&net_mtu, \"Networking\");\n\tCvar_Register (&net_compress, \"Networking\");\n}\n\n/*\n===============\nNetchan_OutOfBand\n\nSends an out-of-band datagram\n================\n*/\nvoid Netchan_OutOfBand (unsigned int ncflags, netadr_t *adr, int length, const qbyte *data)\n{\n\tsizebuf_t\tsend;\n\tqbyte\t\tsend_buf[MAX_QWMSGLEN + PACKET_HEADER];\n\n// write the packet header\n\tmemset(&send, 0, sizeof(send));\n\tsend.data = send_buf;\n\tsend.maxsize = sizeof(send_buf);\n\tsend.cursize = 0;\n\t\n\tMSG_WriteLong (&send, -1);\t// -1 sequence means out of band\n\tSZ_Write (&send, data, length);\n\n// send the datagram\n\t//zoid, no input in demo playback mode\n#ifndef SERVERONLY\n\tif (!cls.demoplayback)\n#endif\n\t\tNET_SendPacket (ncflags, send.cursize, send.data, adr);\n}\n\n/*\n===============\nNetchan_OutOfBandPrint\n\nSends a text message in an out-of-band datagram\n================\n*/\nvoid VARGS Netchan_OutOfBandPrint (unsigned int ncflags, netadr_t *adr, char *format, ...)\n{\n\tva_list\t\targptr;\n\tstatic char\t\tstring[8192];\t\t// ??? why static?\n\t\n\tva_start (argptr, format);\n\tvsnprintf (string,sizeof(string)-1, format,argptr);\n\tva_end (argptr);\n\n\n\tNetchan_OutOfBand (ncflags, adr, strlen(string), (qbyte *)string);\n}\n#ifndef CLIENTONLY\nvoid VARGS Netchan_OutOfBandTPrintf (unsigned int ncflags, netadr_t *adr, int language, translation_t text, ...)\n{\n\tva_list\t\targptr;\n\tstatic char\t\tstring[8192];\t\t// ??? why static?\n\n\tconst char *format = langtext(text, language);\n\n\tstring[0] = A2C_PRINT;\n\t\n\tva_start (argptr, text);\n\tvsnprintf (string+1,sizeof(string)-1, format,argptr);\n\tva_end (argptr);\n\n\n\tNetchan_OutOfBand (ncflags, adr, strlen(string), (qbyte *)string);\n}\n#endif\n\nsize_t Netchan_GetMaxUnreliable(netchan_t *chan)\n{\t//returns the maximum unreliable size we should be aiming for\n\n\n#ifdef HAVE_SERVER\n\t//debug prints\n\tif (!(chan->flags&NCF_CLIENT) && chan == &svs.clients[0].netchan)\n\t{\n\t\tstatic int oldmtu;\n\t\tif (oldmtu != chan->mtu_cur)\n\t\t{\n\t\t\tCon_DPrintf(\"Player0 MTU changed %i->%i\\n\", oldmtu, chan->mtu_cur);\n\t\t\toldmtu = chan->mtu_cur;\n\t\t}\n\t}\n#endif\n\n//\tif (chan->remote_address.type == NA_LOOPBACK)\n//\t\treturn ~0u;\t//our client supports big stuff. demos don't really care either so its fine.\n\n\n\tif (chan->incoming_acknowledged > chan->last_reliable_sequence\n\t\t&& chan->incoming_reliable_acknowledged != chan->reliable_sequence)\n\t{\t//we want to send a reliable...\n\t\tif (chan->reliable_length)\t//one will be resent\n\t\t\treturn chan->mtu_cur - chan->reliable_length;\n\t\tif (chan->message.cursize)\t//a new one will go out\n\t\t\treturn chan->mtu_cur - chan->message.cursize;\n\t}\n\n\n\treturn chan->mtu_cur;\n}\n\n/*\n==============\nNetchan_Setup\n\ncalled to open a channel to a remote system\n==============\n*/\nvoid Netchan_Setup (unsigned int flags, netchan_t *chan, netadr_t *adr, int qport, unsigned int mtu)\n{\n\tmemset (chan, 0, sizeof(*chan));\n\n\tchan->flags = flags;\n\tchan->remote_address = *adr;\n\tchan->last_received = realtime;\n#ifdef NQPROT\n\tchan->nqreliable_allowed = true;\n#endif\n\tchan->incoming_unreliable = -1;\n\n\tchan->outgoing_sequence = 1;\t//so the first one doesn't get dropped.\n\n\tif (adr->prot == NP_KEXLAN)\n\t\tchan->qportsize = 0;\n\telse\n\t\tchan->qportsize = 2;\n\tchan->qport = qport;\n\n\n\n\t//input mtu is the expected 'udp data size'\n\tif (mtu<64 || mtu > 0xffff)\n\t\tmtu = MAX_QWMSGLEN;\n\tmtu = max(mtu, 508);\n\n\t//compute the minimum mtu for the address type\n\tif (adr->type == NA_IP)\n\t\tchan->mtu_min = 508;\t//576 - 20..60(IP header) - 8(udp header)\n\telse if (adr->type == NA_IPV6)\n\t\tchan->mtu_min = 1200;\n\telse if (adr->type == NA_IPX)\n\t\tchan->mtu_min = 1450;\n#ifdef SUPPORT_ICE\n\telse if (adr->type == NA_ICE)\n\t\tchan->mtu_min = 508;\t//match ipv4 here...\n#endif\n\telse if (adr->type == NA_LOOPBACK)\n\t\tchan->mtu_min = 8192;\n\telse\n\t\tchan->mtu_min = 1450;\n\tif (chan->mtu_min < 1024)\n\t\tchan->mtu_min = 1200;\t//the internet is ethernet. if you can't deal with ipv6's limit then your connection is seriously dodgy. plus this lets us be a bit more agressive.\n\n\tmtu -= PACKET_HEADER;\t//Note: This is not considered in vanilla - meaning net_mtu should be 1458 to match it (which will probably cause PTB issues).\n\tif (flags&NCF_CLIENT)\n\t\tmtu -= chan->qportsize;\n\tif (flags&NCF_FRAGABLE)\n\t\tmtu -= 2;\n#ifdef SUPPORT_ICE\n\tif (adr->type == NA_ICE)\n\t\tmtu -= 48+12;\t//fixme: check if we're actually using the dtls and sctp layers or not.\n\telse\n#endif\n\t{\n\t\tif (adr->prot == NP_DTLS || adr->prot == NP_TLS)\n\t\t\tmtu -= 48;\n\t}\n\n\tchan->mtu_min = min(mtu, chan->mtu_min);\n\tchan->mtu_cur = mtu;\t//try to use the requested size to begin with. if it fails (probably on the realibles) then we'll allow it to drop\n\tchan->mtu_max = mtu;\t//don't grow beyond what the user set. we're not aware of multiple paths nor are we tracking packetloss rates so if its partly wrong they'll get a load more loss, so don't be agressive here. let the user do that themselves!\n\tchan->mtu_reprobetime = realtime;\t//try and grow the effective mtu after a bit (route may have changed)\n\n\tchan->message.data = chan->message_buf;\n\tchan->message.allowoverflow = true;\n\n\tif ((flags&NCF_FRAGABLE) && NET_AddrIsReliable(adr))\n\t\tchan->message.maxsize = sizeof(chan->message_buf);\t//something big. might as well if its all tcp anyway.\n\telse\n\t\tchan->message.maxsize = min(chan->mtu_cur, sizeof(chan->message_buf));\n}\n\n\n/*\n===============\nNetchan_CanPacket\n\nReturns true if the bandwidth choke isn't active\n================\n*/\n#define\tMAX_BACKUP\t200\nqboolean Netchan_CanPacket (netchan_t *chan, int rate)\n{\n\tif (chan->remote_address.type == NA_LOOPBACK)\n\t\treturn true;\t//don't ever drop packets due to possible routing problems when there is no routing.\n\tif (!rate)\n\t\treturn true;\n\tif (chan->cleartime < realtime + 0.25)//(MAX_BACKUP/(float)rate))\n\t\treturn true;\n\treturn false;\n}\n\nint Netchan_CanBytes (netchan_t *chan, int rate)\n{\n\tconst double slop = 0.25;\n\tif (chan->remote_address.type == NA_LOOPBACK)\n\t\treturn 0x7fffffff;\t//don't ever drop packets due to possible routing problems when there is no routing.\n\tif (!rate)\n\t\treturn 0x7fffffff;\n\treturn ((realtime+slop)-chan->cleartime)*rate;\n}\n\nvoid Netchan_Block (netchan_t *chan, int bytes, int rate)\n{\n\tif (rate)\n\t{\n\t\tif (chan->cleartime < realtime-0.25)\t//0.25 allows it to be a little bursty.\n\t\t\tchan->cleartime = realtime + (bytes/(float)rate);\n\t\telse\n\t\t\tchan->cleartime += bytes/(float)rate;\n\t}\n}\n\n\n/*\n===============\nNetchan_CanReliable\n\nReturns true if the bandwidth choke isn't \n================\n*/\nqboolean Netchan_CanReliable (netchan_t *chan, int rate)\n{\n\tif (chan->reliable_length)\n\t\treturn false;\t\t\t// waiting for ack\n\treturn Netchan_CanPacket (chan, rate);\n}\n\n#ifdef SERVERONLY\nqboolean ServerPaused(void);\n#endif\n\n#ifdef NQPROT\nsize_t ZLib_DecompressBuffer(qbyte *in, size_t insize, qbyte *out, size_t maxoutsize);\nenum nqnc_packettype_e NQNetChan_Process(netchan_t *chan)\n{\n\tint header;\n\tint sequence;\n\tint drop;\n\n\tchan->bytesin += net_message.cursize;\n\tMSG_BeginReading (&net_message, chan->netprim);\n\n\theader = LongSwap(MSG_ReadLong());\n\n\tif (header & NETFLAG_CTL)\n\t\treturn NQNC_IGNORED;\t//huh?\n\n#ifdef HAVE_CLIENT\n\tif (header & NETFLAG_ZLIB)\n\t{\t//note: qex gets the size header wrong here.\n\t\tqbyte *tmp;\n\t\tif (net_message.cursize <= PACKET_HEADER || net_message.cursize != PACKET_HEADER+(header & NETFLAG_LENGTH_MASK))\n\t\t\treturn NQNC_IGNORED;\t//huh?\n\t\t/*redundantsequence =*/ MSG_ReadLong();\t//wasting 4 bytes...\n#ifdef AVAIL_ZLIB\n\t\ttmp = alloca(0xffff);\n\t\t//note: its zlib rather than raw deflate (wasting a further 6 bytes...).\n\t\tnet_message.cursize = ZLib_DecompressBuffer(net_message.data+8, net_message.cursize-8, tmp, 0xffff);\n\t\tif (net_message.cursize < PACKET_HEADER)\n\t\t{\n\t\t\tif (chan->flags&NCF_CLIENT)\n\t\t\t{\t//clients can just throw an error. the server will appear dead if we try to just ignore it.\n\t\t\t\tHost_EndGame(\"QuakeEx netchan decompression error\");\n\t\t\t\treturn NQNC_IGNORED;\n\t\t\t}\n\t\t\telse\n\t\t\t{\t//inject a disconnect request. clients shouldn't be sending this anyway.\n\t\t\t\tnet_message.data[8] = clc_disconnect;\n\t\t\t\tnet_message.cursize = 9;\n\t\t\t\treturn NQNC_RELIABLE;\n\t\t\t}\n\t\t}\n\t\tmemcpy(net_message.data, tmp, net_message.cursize);\n\n\t\tMSG_BeginReading (&net_message, chan->netprim);\n\t\theader = LongSwap(MSG_ReadLong());\t//re-read the now-decompressed copy of the header for the real flags\n#else\n\t\tif (chan->flags&NCF_CLIENT)\n\t\t\tHost_EndGame(\"NQNetChan_Process: zlib not enabled at compile time\");\n\t\telse\n\t\t\tCon_Printf(\"QuakeEx netchan decompression error\");\n\t\tnet_message.data[8] = (chan->flags&NCF_CLIENT)?svc_disconnect:clc_disconnect;\n\t\tnet_message.cursize = 9;\n\t\treturn NQNC_RELIABLE;\n#endif\n\t}\n#endif\n\tif (net_message.cursize != (header & NETFLAG_LENGTH_MASK))\n\t\treturn NQNC_IGNORED;\t//size was wrong, couldn't have been ours.\n\tsequence = LongSwap(MSG_ReadLong());\n\n\tif (header & NETFLAG_ACK)\n\t{\n\t\tif (sequence == chan->reliable_sequence)\n\t\t{\n\t\t\tchan->reliable_start += MAX_NQDATAGRAM;\n\t\t\tif (chan->reliable_start >= chan->reliable_length)\n\t\t\t{\n\t\t\t\tchan->reliable_length = 0;\t//they got the entire message\n\t\t\t\tchan->reliable_start = 0;\n\t\t\t}\n\t\t\tchan->incoming_reliable_acknowledged = chan->reliable_sequence;\n\t\t\tchan->reliable_sequence++;\n\t\t\tchan->nqreliable_allowed = true;\n\n\t\t\tchan->last_received = realtime;\n\t\t}\n\t\telse if (sequence < chan->reliable_sequence)\n\t\t{\n\t\t\tif (showdrop.ival)\n\t\t\t\tCon_Printf(\"Stale ack recieved\\n\");\n\t\t}\n\t\telse if (sequence > chan->reliable_sequence)\n\t\t{\n\t\t\tif (showdrop.ival)\n\t\t\t\tCon_Printf(\"Future ack recieved\\n\");\n\t\t}\n\n\t\tif (showpackets.value)\n\t\t\tCon_Printf (\"in  %s a=%i %i\\n\"\n\t\t\t\t\t\t, (chan->flags&NCF_CLIENT)?\"s2c\":\"c2s\"\n\t\t\t\t\t\t, sequence\n\t\t\t\t\t\t, 0);\n\n\t\treturn NQNC_ACK;\t//don't try execing the 'payload'. I hate ack packets.\n\t}\n\n\tif (header & NETFLAG_UNRELIABLE)\n\t{\n\t\tif (sequence <= chan->incoming_unreliable)\n\t\t{\n\t\t\tif (showdrop.ival)\n\t\t\t\tCon_Printf(\"Stale datagram recieved (%i<=%i)\\n\", sequence, chan->incoming_unreliable);\n\t\t\treturn NQNC_IGNORED;\n\t\t}\n\t\tdrop = sequence - chan->incoming_unreliable - 1;\n\t\tif (drop > 0)\n\t\t{\n\t\t\tif (showdrop.ival)\n\t\t\t\tCon_Printf(\"Dropped %i datagrams (%i - %i)\\n\", drop, chan->incoming_unreliable+1, sequence-1);\n\t\t\tchan->drop_count += drop;\n\t\t}\n\t\tchan->incoming_unreliable = sequence;\n\n\n\n//\t\tchan->frame_latency = chan->frame_latency*OLD_AVG\n//\t\t\t+ (chan->outgoing_sequence-sequence_ack)*(1.0-OLD_AVG);\n\t\tchan->frame_rate = chan->frame_rate*OLD_AVG\n\t\t\t+ (realtime-chan->last_received)*(1.0-OLD_AVG);\t\t\n\n\t\tchan->last_received = realtime;\n\n\t\tchan->incoming_acknowledged++;\n\n\t\tif (showpackets.value)\n\t\t\tCon_Printf (\"in  %s u=%i %i\\n\"\n\t\t\t\t\t\t, (chan->flags&NCF_CLIENT)?\"c2s\":\"s2c\"\n\t\t\t\t\t\t, chan->incoming_unreliable\n\t\t\t\t\t\t, net_message.cursize);\n\t\treturn NQNC_UNRELIABLE;\n\t}\n\tif (header & NETFLAG_DATA)\n\t{\n\t\tint runt[2];\n\t\t//always reply. a stale sequence probably means our ack got lost.\n\t\trunt[0] = BigLong(NETFLAG_ACK | 8);\n\t\trunt[1] = BigLong(sequence);\n\t\tNET_SendPacket (chan->flags, 8, runt, &net_from);\n\t\tif (showpackets.value)\n\t\t\tCon_Printf (\"out %s a=%i %i\\n\"\n\t\t\t\t\t\t, (chan->flags&NCF_CLIENT)?\"c2s\":\"s2c\"\n\t\t\t\t\t\t, sequence\n\t\t\t\t\t\t, 0);\n\n\t\tchan->last_received = realtime;\n\t\tif (sequence == chan->incoming_reliable_sequence)\n\t\t{\n\t\t\tchan->incoming_reliable_sequence++;\n\n\t\t\tif (chan->in_fragment_length + net_message.cursize-8 >= sizeof(chan->in_fragment_buf))\n\t\t\t{\n\t\t\t\tchan->fatal_error = true;\n\t\t\t\treturn NQNC_IGNORED;\n\t\t\t}\n\n\t\t\tmemcpy(chan->in_fragment_buf + chan->in_fragment_length, net_message.data+8, net_message.cursize-8);\n\t\t\tchan->in_fragment_length += net_message.cursize-8;\n\n\t\t\tif (header & NETFLAG_EOM)\n\t\t\t{\n\t\t\t\tSZ_Clear(&net_message);\n\t\t\t\tSZ_Write(&net_message, chan->in_fragment_buf, chan->in_fragment_length);\n\t\t\t\tchan->in_fragment_length = 0;\n\t\t\t\tMSG_BeginReading(&net_message, chan->netprim);\n\n\t\t\t\tif (showpackets.value)\n\t\t\t\t\tCon_Printf (\"in  %s r=%i %i\\n\"\n\t\t\t\t\t\t\t\t, (chan->flags&NCF_CLIENT)?\"s2c\":\"c2s\"\n\t\t\t\t\t\t\t\t, sequence\n\t\t\t\t\t\t\t\t, net_message.cursize);\n\t\t\t\treturn NQNC_RELIABLE;\t//we can read it now\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (showdrop.ival)\n\t\t\t\tCon_Printf(\"Stale reliable (%i)\\n\", sequence);\n\t\t}\n\n\t\treturn NQNC_IGNORED;\n\t}\n\n\treturn NQNC_IGNORED;\t//not supported.\n}\n#endif\n\n/*\n===============\nNetchan_Transmit\n\ntries to send an unreliable message to a connection, and handles the\ntransmition / retransmition of the reliable messages.\n\nA 0 length will still generate a packet and deal with the reliable messages.\n================\n*/\nint Netchan_Transmit (netchan_t *chan, int length, qbyte *data, int rate)\n{\n\tsizebuf_t\tsend = {NULL};\n\tqbyte\t\tsend_buf[MAX_OVERALLMSGLEN + PACKET_HEADER];\n\tqboolean\tsend_reliable;\n\tchar\t\tremote_adr[MAX_ADR_SIZE];\n\tunsigned\tw1, w2, mtuseq;\n\tint\t\t\ti;\n\tneterr_t e;\n\n\tqboolean ismtuprobe;\n\tint dupes = chan->dupe;\n\tint availbytes = Netchan_CanBytes(chan, rate);\n\tint hsz;\n\tavailbytes = max(0, availbytes); //make sure it can't go negative (clientside doesn't check rate limits much)\n\n#ifdef NQPROT\n\tif (chan->isnqprotocol)\n\t{\n\t\tint sentsize = 0;\n\n\t\tsend.data = send_buf;\n\t\tsend.maxsize = MAX_NQMSGLEN + PACKET_HEADER;\n\t\tsend.cursize = 0;\n\n\t\t/*unreliables flood out, but reliables are tied to server sequences*/\n\t\tif (chan->nqreliable_resendtime < realtime)\n\t\t\tchan->nqreliable_allowed = true;\n\t\tif (chan->nqreliable_allowed)\n\t\t{\n\t\t\t//consume the new reliable when we can.\n\t\t\tif (!chan->reliable_length && chan->message.cursize && chan->nqunreliableonly != 1)\n\t\t\t{\n\t\t\t\tif (chan->nqunreliableonly == 2)\n\t\t\t\t\tchan->nqunreliableonly = 1;\n\t\t\t\tmemcpy (chan->reliable_buf, chan->message_buf, chan->message.cursize);\n\t\t\t\tchan->reliable_length = chan->message.cursize;\n\t\t\t\tchan->reliable_start = 0;\n\t\t\t\tchan->message.cursize = 0;\n\t\t\t}\n\n\t\t\ti = chan->reliable_length - chan->reliable_start;\n\t\t\tif (i>0)\n\t\t\t{\n\t\t\t\tMSG_WriteLong(&send, 0);\n\t\t\t\tMSG_WriteLong(&send, LongSwap(chan->reliable_sequence));\n\n\t\t\t\t//limit the payload length to nq's datagram max size.\n\t\t\t\t//relax the limitation if its reliable (ie: over tcp) where its assumed to have no real limit (beware tunnels)\n\t\t\t\tif (i > MAX_NQDATAGRAM && !NET_AddrIsReliable(&chan->remote_address))\n\t\t\t\t\ti = MAX_NQDATAGRAM;\n\n\t\t\t\tSZ_Write (&send, chan->reliable_buf+chan->reliable_start, i);\n\n\t\t\t\tif (chan->reliable_start+i == chan->reliable_length)\n\t\t\t\t{\n\t\t\t\t\tif (send.cursize + length < send.maxsize)\n\t\t\t\t\t{\t//throw the unreliable packet into the same one as the reliable (but not sent reliably)\n//\t\t\t\t\t\tSZ_Write (&send, data, length);\n//\t\t\t\t\t\tlength = 0;\n\t\t\t\t\t}\n\n\t\t\t\t\t*(int*)send_buf = BigLong(NETFLAG_DATA | NETFLAG_EOM | send.cursize);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\t*(int*)send_buf = BigLong(NETFLAG_DATA | send.cursize);\n\n\t\t\t\tchan->bytesout += send.cursize;\n\t\t\t\tsentsize += send.cursize;\n\t\t\t\tif (showpackets.value)\n\t\t\t\t\tCon_Printf (\"out %s r s=%i %i\\n\"\n\t\t\t\t\t\t, (chan->flags&NCF_CLIENT)?\"c2s\":\"s2c\"\n\t\t\t\t\t\t, chan->reliable_sequence\n\t\t\t\t\t\t, send.cursize);\n\t\t\t\tchan->nqreliable_allowed = false;\n\t\t\t\tchan->nqreliable_resendtime = realtime + 0.3;\t//resend reliables after 0.3 seconds. nq transports suck. FIXME: reduce to pingtime\n\n\t\t\t\tif (NET_SendPacket (chan->flags, send.cursize, send.data, &chan->remote_address) == NETERR_SENT && (\n\t\t\t\t\tNET_AddrIsReliable(&chan->remote_address) || chan->nqunreliableonly==3\t))\n\t\t\t\t{\t//if over tcp (or we're dropping the connection), everything is assumed to be reliable. pretend it got acked now.\n\t\t\t\t\t//if we get an ack later, then who cares.\n\t\t\t\t\tchan->reliable_start += i;\n\t\t\t\t\tif (chan->reliable_start >= chan->reliable_length)\n\t\t\t\t\t{\n\t\t\t\t\t\tchan->reliable_length = 0;\t//they got the entire message\n\t\t\t\t\t\tchan->reliable_start = 0;\n\t\t\t\t\t}\n\t\t\t\t\tchan->incoming_reliable_acknowledged = chan->reliable_sequence;\n\t\t\t\t\tchan->reliable_sequence++;\n\t\t\t\t\tchan->nqreliable_allowed = true;\n\t\t\t\t}\n\t\t\t\tsend.cursize = 0;\n\t\t\t}\n\t\t}\n\n\t\t//send out the unreliable (if still unsent)\n\t\tif (length)\n\t\t{\n\t\t\tMSG_WriteLong(&send, 0);\n\t\t\tMSG_WriteLong(&send, LongSwap(chan->outgoing_unreliable));\n\t\t\tchan->outgoing_unreliable++;\n\n\t\t\tSZ_Write (&send, data, length);\n\n\t\t\t*(int*)send_buf = BigLong(NETFLAG_UNRELIABLE | send.cursize);\n\t\t\tfor (i = -1, e = NETERR_SENT; i < dupes && e == NETERR_SENT; i++)\n\t\t\t\te = NET_SendPacket (chan->flags, send.cursize, send.data, &chan->remote_address);\n\t\t\tsentsize += send.cursize*i;\n\t\t\tif (e == NETERR_MTU && chan->mtu_cur > chan->mtu_min)\n\t\t\t{\t//yay, router works properly. unfortunately we don't know the exact size so keep retrying with a slightly smaller value until it goes through...\n\t\t\t\tchan->mtu_cur = max(chan->mtu_min, chan->mtu_cur-10);\n\t\t\t\tCon_Printf(\"Reducing MSS to %i\\n\", chan->mtu_cur);\n\t\t\t}\n\n\t\t\tif (showpackets.value)\n\t\t\t\tCon_Printf (\"out %s u=%i %i\\n\"\n\t\t\t\t\t\t, (chan->flags&NCF_CLIENT)?\"c2s\":\"s2c\"\n\t\t\t\t\t\t, chan->outgoing_unreliable-1\n\t\t\t\t\t\t, send.cursize);\n\t\t\tsend.cursize = 0;\n\t\t}\n\t\tchan->bytesout += sentsize;\n\t\tNetchan_Block(chan, sentsize, rate);\n\t\treturn sentsize;\n\t}\n#endif\n\n// check for message overflow\n\tif (chan->message.overflowed)\n\t{\n\t\tchan->fatal_error = true;\n\t\tCon_TPrintf (\"%s: Outgoing message overflow\\n\"\n\t\t\t, NET_AdrToString (remote_adr, sizeof(remote_adr), &chan->remote_address));\n\t\treturn 0;\n\t}\n\n// if the remote side dropped the last reliable message, resend it\n\tsend_reliable = false;\n\n\tif (chan->incoming_acknowledged > chan->last_reliable_sequence\n\t&& chan->incoming_reliable_acknowledged != chan->reliable_sequence)\n\t\tsend_reliable = true;\t//they acked a later packet without acking the reliable...\n\n// if the reliable transmit buffer is empty, copy the current message out\n\tif (!chan->reliable_length && chan->message.cursize)\n\t{\n\t\tmemcpy (chan->reliable_buf, chan->message_buf, chan->message.cursize);\n\t\tchan->reliable_length = chan->message.cursize;\n\t\tchan->message.cursize = 0;\n\t\tchan->reliable_sequence ^= 1;\n\t\tsend_reliable = true;\n\n\t\tchan->mtu_resends=0;\n\t}\n\n\tif (send_reliable && chan->remote_address.prot == NP_KEXLAN)\t//FIXME: use with webrtc too - sctp can avoid the round-trip delay.\n#ifndef SERVERONLY\n\tif (!cls.demoplayback)\n#endif\n\t{\n\t\tif (chan->reliable_length)\n\t\t{\n\t\t\tsend.data = send_buf;\n\t\t\tsend.maxsize = sizeof(send_buf);\n\t\t\tsend.cursize = 0;\n\n\t\t\tMSG_WriteLong (&send, 1u<<31);\n\t\t\tMSG_WriteLong (&send, 1u<<31);\n\t\t\tSZ_Write (&send, chan->reliable_buf, chan->reliable_length);\n\n\t\t\tif (NETERR_SENT == NET_SendPacket (chan->flags, send.cursize, send.data, &chan->remote_address))\n\t\t\t\tchan->reliable_length = 0;\t//the lower layer will handle any retransmission for us.\n\t\t}\n\t\tsend_reliable = 0;\n\t\tchan->incoming_reliable_sequence = 0;\n\t}\n\n// write the packet header\n\tsend.data = send_buf;\n\tsend.maxsize = PACKET_HEADER + ((chan->flags&NCF_CLIENT)?chan->qportsize:0) + ((chan->flags&NCF_FRAGABLE)?2:0);\n\tsend.maxsize += chan->mtu_cur;\n\tsend.cursize = 0;\n\n\tmtuseq = chan->outgoing_sequence&(countof(chan->sentsizes)-1);\n\tfor (; (chan->outgoing_sequence_last&(countof(chan->sentsizes)-1)) != mtuseq; chan->outgoing_sequence_last++)\n\t\tchan->sentsizes[chan->outgoing_sequence_last&(countof(chan->sentsizes)-1)] = 0;\t//lost c2s or something, gaps now.\n\n\tw1 = chan->outgoing_sequence | (send_reliable<<31);\n\tw2 = chan->incoming_sequence | (chan->incoming_reliable_sequence<<31);\n\n\tif (chan->flags&NCF_STUNAWARE)\n\t{\n\t\tw1 = BigLong(w1+ANTISTUNBIAS);\n\t\tw2 = BigLong(w2);\n\t}\n\n\tchan->outgoing_sequence++;\n\n\tMSG_WriteLong (&send, w1);\n\tMSG_WriteLong (&send, w2);\n\thsz = 8;\n\n\t// send the qport if we are a client\n#ifndef SERVERONLY\n\tif (chan->flags&NCF_CLIENT)\n\t{\n\t\tif (chan->qportsize == 2)\n\t\t\tMSG_WriteShort (&send, chan->qport);\n\t\telse if (chan->qportsize == 1)\n\t\t\tMSG_WriteByte (&send, chan->qport&0xff);\n\t\thsz += chan->qportsize;\n\t}\n#endif\n\n\tif (chan->flags&NCF_FRAGABLE)\n\t{\n\t\t//allow the max size to be bigger, sending everything available\n\t\tsend.maxsize = MAX_OVERALLMSGLEN-100;\n\t\tMSG_WriteShort(&send, 0);\n\t\thsz += 2;\n\t}\n\n// copy the reliable message to the packet first\n\tif (send_reliable)\n\t{\n\t\tif (send.maxsize - send.cursize < chan->reliable_length)\n\t\t{\n\t\t\tif (!chan->fatal_error)\n\t\t\t{\n\t\t\t\tchan->fatal_error = true;\n\t\t\t\tCon_TPrintf (\"%s: Path MTU is lower than %u\\n\"\n\t\t\t\t\t, NET_AdrToString (remote_adr, sizeof(remote_adr), &chan->remote_address), chan->reliable_length);\n\t\t\t}\n\t\t\tchan->outgoing_sequence--;\n\t\t\treturn 0;\n\t\t}\n\t\tSZ_Write (&send, chan->reliable_buf, chan->reliable_length);\n\t\tchan->last_reliable_sequence = chan->outgoing_sequence;\n\n\t\tif (chan->mtu_resends > 5)\n\t\t{\t//getting blackholed?\n\t\t\tif (chan->mtu_cur > chan->mtu_min)\n\t\t\t{\t//reset the mtu, and re-enable probes to get it back up to something usable.\n\t\t\t\tchan->mtu_cur = chan->mtu_min;\n//\t\t\t\tchan->mtu_cur = max(chan->mtu_min, min(send.cursize, chan->mtu_cur-16));\n\t\t\t\tCon_DPrintf(\"Reliables Blackholed? Reducing MSS to %i\\n\", chan->mtu_cur);\n\t\t\t\tchan->mtu_probes = 0;\t//and try and grow it again.\n\t\t\t}\n\t\t\t//chan->mtu_resends = 0;\n\t\t}\n\t\tchan->mtu_resends++;\n\t}\n\n\tif (chan->outgoing_sequence - chan->incoming_acknowledged > 128 && chan->mtu_cur > chan->mtu_min)\n\t{\n\t\tchan->mtu_cur = chan->mtu_min;\n//\t\tchan->mtu_cur = max(chan->mtu_min, min(send.cursize, chan->mtu_cur-16));\n\t\tCon_DPrintf(\"MTU blackhole? Reducing MSS to %i\\n\", chan->mtu_cur);\n\t\tchan->mtu_probes = 0;\t//and try and grow it again.\n\t}\n\n// add the unreliable part if space is available\n\tif (send.maxsize - send.cursize >= length)\n\t\tSZ_Write (&send, data, length);\n\n\tismtuprobe = false;\n\tif (chan->mtu_reprobetime < realtime)\n\t{\n\t\tchan->mtu_probes = min(chan->mtu_probes,4);\n\t\tchan->mtu_reprobetime = realtime + 30;\n\t}\n\tif (!send_reliable/*reliables depend on round trip times, don't risk losing them*/ &&\n\t\tchan->incoming_acknowledged >= chan->outgoing_mtu_probe+chan->mtu_probes/*not still waiting for one, slow down a bit if they're dropping.*/ &&\n\t\tchan->mtu_cur < chan->mtu_max && chan->mtu_probes < 5/*give up if its just not growing*/)\n\t{\n\t\tint targsize = min(chan->mtu_max, chan->mtu_cur+16);\n\t\tint padsize = (hsz+targsize)-send.cursize;\n\t\tif (padsize > 0 && targsize <= send.maxsize)\n\t\t{\n\t\t\tif (chan->flags&NCF_CLIENT)\n\t\t\t\tQ_memset (SZ_GetSpace(&send,padsize),clc_nop,padsize);\n\t\t\telse\n\t\t\t\tQ_memset (SZ_GetSpace(&send,padsize),svc_nop,padsize);\n\t\t\tismtuprobe = true;\t//don't do our fragmentation stuff.\n\t\t\tchan->mtu_probes++;\n\t\t\tchan->outgoing_mtu_probe = chan->outgoing_sequence;\n\t\t\tchan->mtu_reprobetime = realtime + 30;\n\t\t\tCon_DPrintf(\"Sending mtu probe\\n\");\n\t\t}\n\t}\n\n\n// send the datagram\n//\ti = chan->outgoing_sequence & (MAX_LATENT-1);\n//\tchan->outgoing_size[i] = send.cursize;\n//\tchan->outgoing_time[i] = realtime;\n\n#ifdef HUFFNETWORK\n\tif (chan->compresstable)\n\t{\n\t\t//int oldsize = send.cursize;\n\t\tHuff_CompressPacket(chan->compresstable, &send, 8 + ((chan->flags&NCF_CLIENT)?2:0) + (chan->flags&NCF_FRAGABLE?2:0));\n//\t\tCon_Printf(\"%i becomes %i\\n\", oldsize, send.cursize);\n//\t\tHuff_DecompressPacket(&send, (chan->sock == NS_CLIENT)?10:8);\n\t}\n#endif\n\n\te = NETERR_SENT;\n\t//zoid, no input in demo playback mode\n#ifndef SERVERONLY\n\tif (!cls.demoplayback)\n#endif\n\t{\n\t\tdupes = min(chan->dupe, availbytes / send.cursize);\n\n\t\tif (ismtuprobe || !(chan->flags&NCF_FRAGABLE))// || send.cursize < ((chan->mtu - hsz)&~7))\n\t\t{\t//vanilla sends\n\t\t\tfor (i = -1; i < dupes && e == NETERR_SENT; i++)\n\t\t\t\te = NET_SendPacket (chan->flags, send.cursize, send.data, &chan->remote_address);\n\n\t\t\t//ipv4 'guarentees' mtu sizes of at least 560ish.\n\t\t\t//our reliable/backbuf messages are limited to 1024 bytes.\n\t\t\t//this means that large reliables may be unsendable.\n\t\t\tif (e == NETERR_MTU && send.cursize-hsz > chan->mtu_min)\n\t\t\t{\n\t\t\t\tchan->mtu_cur = max(chan->mtu_min, send.cursize-hsz-10);\n\t\t\t\tchan->mtu_max = min(chan->mtu_max, chan->mtu_cur);\t//don't try growing past it\n\t\t\t\tCon_Printf(\"Reducing MSS to %i\\n\", chan->mtu_cur);\n\t\t\t}\n\t\t\tchan->sentsizes[mtuseq] = send.cursize-hsz;\n\n\t\t\tsend.cursize += send.cursize * i;\n\t\t}\n\t\telse\n\t\t{\t//fte's fragmentaton protocol\n\t\t\tint offset = 0, no;\n\t\t\tqboolean more;\n\t\t\tint outbytes = 0;\n\t\t\tint fragbytes;\n\n\t\t\t/*FIXME: splurge over a number of frames, if we have an outgoing reliable*/\n\n\t\t\t/*send the additional parts, adding new headers within the previous packet*/\n\t\t\tdo\n\t\t\t{\n\t\t\t\tno = offset + chan->mtu_cur - hsz;\n\n\t\t\t\tif (no < send.cursize-hsz)\n\t\t\t\t{\n\t\t\t\t\tno &= ~7;\n\t\t\t\t\tmore = true;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\t//this is the last...\n\t\t\t\t\tno = send.cursize-hsz;\n\t\t\t\t\tmore = false;\n\t\t\t\t}\n\n\t\t\t\t*(int*)&send.data[(offset) + 0] = LittleLong(w1);\n\t\t\t\t*(int*)&send.data[(offset) + 4] = LittleLong(w2);\n#ifndef SERVERONLY\n\t\t\t\tif (chan->flags&NCF_CLIENT)\n\t\t\t\t{\n\t\t\t\t\tif (chan->qportsize == 2)\n\t\t\t\t\t\t*(short*)&send.data[offset + hsz-4] = LittleShort(chan->qport);\n\t\t\t\t\telse if (chan->qportsize == 1)\n\t\t\t\t\t\t*(qbyte*)&send.data[offset + hsz-3] = chan->qport&0xff;\n\t\t\t\t}\n#endif\n\t\t\t\t*(short*)&send.data[offset + hsz-2] = LittleShort((offset>>2) | (more?1:0));\n\n\t\t\t\tif (e == NETERR_SENT)\n\t\t\t\t{\n\t\t\t\t\tfor (i = -1; i < dupes && e == NETERR_SENT; i++)\n\t\t\t\t\t{\n\t\t\t\t\t\tfragbytes = (no - offset) + hsz;\n\t\t\t\t\t\te = NET_SendPacket (chan->flags, fragbytes, send.data + offset, &chan->remote_address);\n\t\t\t\t\t\tif (e == NETERR_MTU && !offset && chan->mtu_cur > chan->mtu_min)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tchan->mtu_cur = max(chan->mtu_min, chan->mtu_cur-16);\n\t\t\t\t\t\t\tchan->mtu_max = min(chan->mtu_max, chan->mtu_cur);\t//don't try growing past it\n\t\t\t\t\t\t\tCon_Printf(\"Reducing MSS to %i\\n\", chan->mtu_cur);\n\t\t\t\t\t\t\tno = offset;\n\t\t\t\t\t\t\tmore = true;\n\t\t\t\t\t\t\te = NETERR_SENT; //... keep trying...\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (!offset)\n\t\t\t\t\t\t\tchan->sentsizes[mtuseq] = fragbytes-hsz;\n\t\t\t\t\t\toutbytes += fragbytes;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\toffset = no;\n\t\t\t} while(more);\n\t\t\tsend.cursize = outbytes;\n\t\t}\n\t}\n\n\tif (e == NETERR_SENT)\n\t{\n\t\tif (send_reliable && NET_AddrIsReliable(&chan->remote_address))\n\t\t\tchan->reliable_length = 0;\t//we know the peer will receive it. don't worry about waiting for their acks.\n\n\t\tchan->bytesout += send.cursize;\n\t\tNetchan_Block(chan, send.cursize, rate);\n\t}\n#ifdef SERVERONLY\n\tif (ServerPaused())\n\t\tchan->cleartime = realtime;\n#endif\n\n\tif (showpackets.value)\n\t{\n\t\tchar *errtext;\n\t\tswitch(e)\n\t\t{\n\t\tcase NETERR_SENT: errtext = \"\"; break;\n\t\tcase NETERR_NOROUTE: errtext = \" unroutable\"; break;\n\t\tcase NETERR_DISCONNECTED: errtext = \" disconnected\"; break;\n\t\tcase NETERR_MTU: errtext = \" mss exceeded\"; break;\n\t\tcase NETERR_CLOGGED: errtext = \" conjestion\"; break;\n\t\tdefault: errtext = \" unk error\"; break;\n\t\t}\n\t\tCon_Printf (\"%f %s --> s=%i(%i) a=%i(%i) %i%s\\n\"\n\t\t\t, Sys_DoubleTime()\n\t\t\t, (chan->flags&NCF_CLIENT)?\"c2s\":\"s2c\"\n\t\t\t, chan->outgoing_sequence\n\t\t\t, send_reliable\n\t\t\t, chan->incoming_sequence\n\t\t\t, chan->incoming_reliable_sequence\n\t\t\t, send.cursize,\n\t\t\terrtext);\n\t}\n\treturn send.cursize;\n\n}\n\n/*\n=================\nNetchan_Process\n\ncalled when the current net_message is from remote_address\nmodifies net_message so that it points to the packet payload\n=================\n*/\nqboolean Netchan_Process (netchan_t *chan)\n{\n\tunsigned\t\tsequence, sequence_ack;\n\tunsigned\t\treliable_ack, reliable_message;\n\tchar\t\t\tadr[MAX_ADR_SIZE];\n\tint offset;\n\tint oob_reliable;\n\n\tif (\n#ifndef SERVERONLY\n\t\t\t!cls.demoplayback && \n#endif\n\t\t\t!NET_CompareAdr (&net_from, &chan->remote_address))\n\t\treturn false;\n\n\tchan->bytesin += net_message.cursize;\n\n// get sequence numbers\t\t\n\tMSG_BeginReading (&net_message, chan->netprim);\n\tsequence = MSG_ReadLong ();\n\tsequence_ack = MSG_ReadLong ();\n\n\tif (chan->flags&NCF_STUNAWARE)\n\t{\n\t\tsequence = BigLong(sequence);\n\t\tif (!(sequence&ANTISTUNBIAS))\n\t\t\treturn false;\n\t\tsequence -= ANTISTUNBIAS;\n\t\tsequence_ack = BigLong(sequence_ack);\n\t}\n\n\toob_reliable = (sequence == (1u<<31) && sequence_ack == (1u<<31));\n\n\t// skip over the qport if we are a server (its handled elsewhere)\n#ifndef CLIENTONLY\n\tif (!(chan->flags&NCF_CLIENT))\n\t\tMSG_ReadSkip (chan->qportsize);\n#endif\n\n\tif (chan->flags&NCF_FRAGABLE)\n\t\toffset = (unsigned short)MSG_ReadShort();\n\telse\n\t\toffset = 0;\n\n\treliable_message = sequence >> 31;\n\treliable_ack = sequence_ack >> 31;\n\n\tsequence &= ~(1u<<31);\n\tsequence_ack &= ~(1u<<31);\n\n\tif (showpackets.value)\n\t\tCon_Printf (\"%f %s <-- s=%i(%i) a=%i(%i) %i%s\\n\"\n\t\t\t, Sys_DoubleTime()\n\t\t\t, (chan->flags&NCF_CLIENT)?\"s2c\":\"c2s\"\n\t\t\t, sequence\n\t\t\t, reliable_message\n\t\t\t, sequence_ack\n\t\t\t, reliable_ack\n\t\t\t, net_message.cursize\n\t\t\t, offset?\" frag\":\"\");\n\n// get a rate estimation\n#if 0\n\tif (chan->outgoing_sequence - sequence_ack < MAX_LATENT)\n\t{\n\t\tint\t\t\t\ti;\n\t\tdouble\t\t\ttime, rate;\n\t\n\t\ti = sequence_ack & (MAX_LATENT - 1);\n\t\ttime = realtime - chan->outgoing_time[i];\n\t\ttime -= 0.1;\t// subtract 100 ms\n\t\tif (time <= 0)\n\t\t{\t// gotta be a digital link for <100 ms ping\n\t\t\tif (chan->rate > 1.0/5000)\n\t\t\t\tchan->rate = 1.0/5000;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (chan->outgoing_size[i] < 512)\n\t\t\t{\t// only deal with small messages\n\t\t\t\trate = chan->outgoing_size[i]/time;\n\t\t\t\tif (rate > 5000)\n\t\t\t\t\trate = 5000;\n\t\t\t\trate = 1.0/rate;\n\t\t\t\tif (chan->rate > rate)\n\t\t\t\t\tchan->rate = rate;\n\t\t\t}\n\t\t}\n\t}\n#endif\n\n//\n// discard stale or duplicated packets\n//\n\tif (oob_reliable)\n\t{\t//if its an oob reliable then its sequence numbers are screwy and bypass the dupe/etc check.\n\t\tif (NET_AddrIsReliable(&chan->remote_address))\n\t\t\t;\t//mostly for NP_KEXLAN.\n\t\telse\n\t\t{\n\t\t\tCon_TPrintf (\"%s:Unexpected out-of-band reliable at %i\\n\"\n\t\t\t\t, NET_AdrToString (adr, sizeof(adr), &chan->remote_address)\n\t\t\t\t, chan->incoming_sequence);\n\t\t\treturn false;\n\t\t}\n\t}\n\telse\n\t{\n\t\tint ssize = 0;\n\t\tif (sequence <= (unsigned)chan->incoming_sequence &&\n\t\t\t!(reliable_message && chan->remote_address.prot == NP_KEXLAN))\t//*sigh* reliables don't work properly here.\n\t\t{\n\t\t\tif (showdrop.value)\n\t\t\t\tCon_TPrintf (\"%s:Out of order packet %i at %i\\n\"\n\t\t\t\t\t, NET_AdrToString (adr, sizeof(adr), &chan->remote_address)\n\t\t\t\t\t,  sequence\n\t\t\t\t\t, chan->incoming_sequence);\n\t\t\treturn false;\n\t\t}\n\n\t\tif (chan->outgoing_sequence-sequence_ack < countof(chan->sentsizes)-2)\n\t\t\tssize = chan->sentsizes[sequence_ack&(countof(chan->sentsizes)-1)];\n\t\tif (ssize && ssize > chan->mtu_cur)\n\t\t{\n\t\t\tchan->mtu_cur = ssize;\n\t\t\tCon_DPrintf(\"MTU confirmed to %i\\n\", chan->mtu_cur);\n\t\t\tchan->mtu_probes = 0;\t//start growing again.\n\t\t}\n\t}\n\n\n\tif (offset)\n\t{\n\t\tint len = net_message.cursize - MSG_GetReadCount();\n\t\tqboolean more = false;\n\t\tif (offset & 1)\n\t\t{\n\t\t\tmore = true;\n\t\t\toffset &= ~1;\n\t\t}\n\t\toffset = offset << 2;\n\n\t\tif (offset + len > sizeof(chan->in_fragment_buf)) /*stop the overflow*/\n\t\t{\n\t\t\tif (showdrop.value)\n\t\t\t\tCon_Printf(\"Dropping packet - too many fragments\\n\");\n\t\t\treturn false;\n\t\t}\n\t\tif (chan->incoming_unreliable != sequence)\n\t\t{\n\t\t\tif (chan->in_fragment_length && showdrop.ival)\n\t\t\t\tCon_Printf(\"final fragment lost (%i). dropping entire packet\\n\", offset);\n\t\t\t/*sequence doesn't match, forget the old*/\n\t\t\tchan->in_fragment_length = 0;\n\t\t\tchan->incoming_unreliable = sequence;\n\t\t}\n\t\tif (offset != chan->in_fragment_length)\n\t\t{\n\t\t\tif (showdrop.ival)\n\t\t\t\tCon_Printf(\"prior fragment lost (%i-%i). dropping entire packet\\n\", offset, chan->in_fragment_length);\n\t\t\treturn false; /*dropped one*/\n\t\t}\n\n\t\tmemcpy(chan->in_fragment_buf + offset, net_message.data + MSG_GetReadCount(), len);\n\t\tchan->in_fragment_length += len;\n\n\t\tif (more)\n\t\t{\n\t\t\t/*nothing to process yet*/\n\t\t\treturn false;\n\t\t}\n\t\tmemcpy(net_message.data, chan->in_fragment_buf, chan->in_fragment_length);\n\t\tnet_message.currentbit = 0;\n\t\tnet_message.cursize = chan->in_fragment_length;\n\n\t\tif (showpackets.value)\n\t\t\tCon_Printf (\"<-- s=%i(%i) a=%i(%i) %i Recombined\\n\"\n\t\t\t\t, sequence\n\t\t\t\t, reliable_message\n\t\t\t\t, sequence_ack\n\t\t\t\t, reliable_ack\n\t\t\t\t, net_message.cursize);\n\n\t\tchan->incoming_unreliable = 0;\n\t\tchan->in_fragment_length = 0;\n\t}\n\telse\n\t{\n\t\t/*kill any pending reliable*/\n\t\tchan->incoming_unreliable = 0;\n\t\tchan->in_fragment_length = 0;\n\t}\n\n//\n// dropped packets don't keep the message from being used\n//\n\tnet_drop = sequence - (chan->incoming_sequence+1);\n\tif (net_drop > 0)\n\t{\n\t\tchan->drop_count += 1;\n\n\t\tif (showdrop.value)\n\t\t\tCon_TPrintf (\"%s:Dropped %i packets at %i\\n\"\n\t\t\t, NET_AdrToString (adr, sizeof(adr), &chan->remote_address)\n\t\t\t, sequence-(chan->incoming_sequence+1)\n\t\t\t, sequence);\n\t}\n\n//\n// if the current outgoing reliable message has been acknowledged\n// clear the buffer to make way for the next\n//\n\tif (reliable_ack == (unsigned)chan->reliable_sequence)\n\t\tchan->reliable_length = 0;\t// it has been received\n\n//\n// if this message contains a reliable message, bump incoming_reliable_sequence \n//\n\tif (oob_reliable)\t//*sigh* reliables don't work properly here.\n\t\t;\t//don't corrupt sequences/acks/etc.\n\telse\n\t{\n\t\tchan->incoming_sequence = sequence;\n\t\tchan->incoming_acknowledged = sequence_ack;\n\t\tchan->incoming_reliable_acknowledged = reliable_ack;\n\t\tif (reliable_message)\n\t\t\tchan->incoming_reliable_sequence ^= 1;\n\t}\n\n//\n// the message can now be read from the current message pointer\n// update statistics counters\n//\n\tchan->frame_latency = chan->frame_latency*OLD_AVG\n\t\t+ (chan->outgoing_sequence-sequence_ack)*(1.0-OLD_AVG);\n\tchan->frame_rate = chan->frame_rate*OLD_AVG\n\t\t+ (realtime-chan->last_received)*(1.0-OLD_AVG);\t\t\n\n\tchan->last_received = realtime;\n\n#ifdef HUFFNETWORK\n\tif (chan->compresstable)\n\t{\n//\t\tHuff_CompressPacket(&net_message, (chan->sock == NS_SERVER)?10:8);\n\t\tHuff_DecompressPacket(chan->compresstable, &net_message, MSG_GetReadCount());\n\t}\n#endif\n\n\treturn true;\n}\n\n"
  },
  {
    "path": "engine/common/net_ice.c",
    "content": "/*\nInteractive Connectivity Establishment (rfc 5245)\nfind out your peer's potential ports.\nspam your peer with stun packets.\nsee what sticks.\nthe 'controller' assigns some final candidate pair to ensure that both peers send+receive from a single connection.\nif no candidates are available, try using stun to find public nat addresses.\n\nin fte, a 'pair' is actually in terms of each local socket and remote address. hopefully that won't cause too much weirdness.\n\tthis does limit which interfaces we can send packets from, which may cause issues with local TURN relays(does not result in extra prflx candidates) and VPNs(may need to be set up as the default route), and prevents us from being able to report reladdr in candidate offers (again mostly only of use with TURN)\n\tlan connections should resolve to a host interface anyway\n\nstun test packets must contain all sorts of info. username+integrity+fingerprint for validation. priority+usecandidate+icecontrol(ing) to decree the priority of any new remote candidates, whether its finished, and just who decides whether its finished.\npeers don't like it when those are missing.\n\nhost candidates - addresses that are directly known (but are probably unroutable private things)\nserver reflexive candidates - addresses that we found from some public stun server (useful for NATs that use a single public port for each unique private port)\npeer reflexive candidates - addresses that our peer finds out about as we spam them\nrelayed candidates - some sort of socks5 or something proxy.\n\n\nNote: Even after the ICE connection becomes active, you should continue to collect local candidates and transmit them to the peer out of band.\nthis allows the connection to pick a new route if some router dies (like a relay kicking us).\nFIXME: the client currently disconnects from the broker. the server tracks players via ip rather than ICE.\n\ntcp rtp framing should generally be done with a 16-bit network-endian length prefix followed by the data.\n\nNOTE: we do NOT distinguish between media-level and session-level attributes, as such we can only handle ONE media stream per session. we also don't support rtcp.\n*/\n/*\nwebrtc\nbasically just sctp-over-dtls-over-ice or srtp(negotiated via dtls)-over-ice.\nthe sctp part is pure bloat+pain for us, but as its required for browser compat we have to support it anyway - but we only use it where we must.\nwe don't do any srtp stuff at all.\n*/\n/*\nbroker\nftemaster provides a broker service\n*/\n\n#include \"quakedef.h\"\n#include \"netinc.h\"\n\ntypedef struct\n{\n\tunsigned short msgtype;\n\tunsigned short msglen;\n\tunsigned int magiccookie;\n\tunsigned int transactid[3];\n} stunhdr_t;\n//class\n#define STUN_REQUEST\t0x0000\n#define STUN_REPLY\t\t0x0100\n#define STUN_ERROR\t\t0x0110\n#define STUN_INDICATION\t0x0010\n//request\n#define STUN_BINDING\t0x0001\n#define STUN_ALLOCATE\t0x0003 //TURN\n#define STUN_REFRESH\t0x0004 //TURN\n#define STUN_SEND\t\t0x0006 //TURN\n#define STUN_DATA\t\t0x0007 //TURN\n#define STUN_CREATEPERM\t0x0008 //TURN\n#define STUN_CHANBIND\t0x0009 //TURN\n\n//misc stuff...\n#define STUN_MAGIC_COOKIE\t0x2112a442\n\n//attributes\n#define STUNATTR_MAPPED_ADDRESS\t\t\t0x0001\n#define STUNATTR_USERNAME\t\t\t\t0x0006\n#define STUNATTR_MSGINTEGRITIY_SHA1\t\t0x0008\n#define STUNATTR_ERROR_CODE\t\t\t\t0x0009\n//#define STUNATTR_CHANNELNUMBER\t\t\t0x000c\t//TURN\n#define STUNATTR_LIFETIME\t\t\t\t0x000d\t//TURN\n#define STUNATTR_XOR_PEER_ADDRESS\t\t0x0012\t//TURN\n#define STUNATTR_DATA\t\t\t\t\t0x0013\t//TURN\n#define STUNATTR_REALM\t\t\t\t\t0x0014\t//TURN\n#define STUNATTR_NONCE\t\t\t\t\t0x0015\t//TURN\n#define STUNATTR_XOR_RELAYED_ADDRESS\t0x0016\t//TURN\n#define STUNATTR_REQUESTED_ADDRFAM\t\t0x0017\t//TURN\n//#define STUNATTR_EVEN_PORT\t\t\t0x0018\t//TURN\n#define STUNATTR_REQUESTED_TRANSPORT\t0x0019\t//TURN\n#define STUNATTR_DONT_FRAGMENT\t\t\t0x001a\t//TURN\n#define STUNATTR_MSGINTEGRITIY_SHA2_256\t0x001C\n#define STUNATTR_PASSWORD_ALGORITHM\t\t0x001D\t//yay, screw md5\n#define\tSTUNATTR_XOR_MAPPED_ADDRESS\t\t0x0020\n#define\tSTUNATTR_ICE_PRIORITY\t\t\t0x0024\t//ICE\n#define\tSTUNATTR_ICE_USE_CANDIDATE\t\t0x0025\t//ICE\n\n//0x8000 attributes are optional, and may be silently ignored without issue.\n#define STUNATTR_ADDITIONAL_ADDRFAM\t\t0x8000\t//TURN -- listen for ipv6 in addition to ipv4\n#define STUNATTR_SOFTWARE\t\t\t\t0x8022\t//TURN\n#define STUNATTR_FINGERPRINT\t\t\t0x8028\n#define\tSTUNATTR_ICE_CONTROLLED\t\t\t0x8029\t//ICE\n#define STUNATTR_ICE_CONTROLLING\t\t0x802A\t//ICE\n\n\ntypedef struct\n{\n\tunsigned short attrtype;\n\tunsigned short attrlen;\n} stunattr_t;\n#if defined(SUPPORT_ICE) || (defined(MASTERONLY) && defined(AVAIL_ZLIB))\n#include \"zlib.h\"\n#endif\n#ifdef SUPPORT_ICE\nstatic cvar_t net_ice_exchangeprivateips = CVARFD(\"net_ice_exchangeprivateips\", \"0\", CVAR_NOTFROMSERVER, \"Boolean. When set to 0, hides private IP addresses from your peers - only addresses determined from the other side of your router will be shared. You should only need to set this to 1 if mdns is unavailable.\");\nstatic cvar_t net_ice_allowstun = CVARFD(\"net_ice_allowstun\", \"1\", CVAR_NOTFROMSERVER, \"Boolean. When set to 0, prevents the use of stun to determine our public address (does not prevent connecting to our peer's server-reflexive candidates).\");\nstatic cvar_t net_ice_allowturn = CVARFD(\"net_ice_allowturn\", \"1\", CVAR_NOTFROMSERVER, \"Boolean. When set to 0, prevents registration of turn connections (does not prevent connecting to our peer's relay candidates).\");\nstatic cvar_t net_ice_allowmdns = CVARFD(\"net_ice_allowmdns\", \"1\", CVAR_NOTFROMSERVER, \"Boolean. When set to 0, prevents the use of multicast-dns to obtain candidates using random numbers instead of revealing private network info.\");\nstatic cvar_t net_ice_relayonly = CVARFD(\"net_ice_relayonly\", \"0\", CVAR_NOTFROMSERVER, \"Boolean. When set to 1, blocks reporting non-relay local candidates, does not attempt to connect to remote candidates other than via a relay.\");\nstatic cvar_t net_ice_usewebrtc = CVARFD(\"net_ice_usewebrtc\", \"\", CVAR_NOTFROMSERVER, \"Use webrtc's extra overheads rather than simple ICE. This makes packets larger and is slower to connect, but is compatible with the web port.\");\nstatic cvar_t net_ice_servers = CVARFD(\"net_ice_servers\", \"\", CVAR_NOTFROMSERVER, \"A space-separated list of ICE servers, eg stun:host.example:3478 or turn:host.example:3478?user=foo?auth=blah\");\nstatic cvar_t net_ice_debug = CVARFD(\"net_ice_debug\", \"0\", CVAR_NOTFROMSERVER, \"0: Hide messy details.\\n1: Show new candidates.\\n2: Show each connectivity test.\");\n\n#if 0\t//enable this for testing only. will generate excessive error messages with non-hacked turn servers...\n\t#define ASCOPE_TURN_REQUIRESCOPE\tASCOPE_HOST //try sending it any address.\n#else\n\t#define ASCOPE_TURN_REQUIRESCOPE\tASCOPE_LAN\t//don't report loopback/link-local addresses to turn relays.\n#endif\n\nstruct icecandidate_s\n{\n\tstruct icecandinfo_s info;\n\n\tstruct icecandidate_s *next;\n\n\tnetadr_t peer;\n\t//peer needs telling or something.\n\tqboolean dirty;\n\tqboolean ismdns;\t\t//indicates that the candidate is a .local domain and thus can be shared without leaking private info.\n\n\t//these are bitmasks. one bit for each local socket.\n\tunsigned int reachable;\t//looked like it was up a while ago...\n\tunsigned int reached;\t//timestamp of when it was last reachable. pick a new one if too old.\n\tunsigned int tried;\t\t//don't try again this round\n};\nstruct icestate_s\n{\n\tstruct icestate_s *next;\n\tvoid *module;\n\n\tnetadr_t qadr;\t\t\t//address reported to the rest of the engine (packets from our peer get remapped to this)\n\tnetadr_t chosenpeer;\t//address we're sending our data to.\n\n\tstruct iceserver_s\n\t{\t//stun/turn servers\n\t\tnetadr_t addr;\n\t\tqboolean isstun;\n\t\tunsigned int stunretry;\t//once a second, extended to once a minite on reply\n\t\tunsigned int stunrnd[3];\n\n\t\t//turn stuff.\n\t\tftenet_generic_connection_t *con;\t//TURN needs unique client addresses otherwise it gets confused and starts reporting errors about client ids. make sure these connections are unique.\n\t\t\t\t\t\t\t\t\t\t\t//FIXME: should be able to get away with only one if the addr field is unique... move to icestate_s\n\t\tnetadrtype_t family;\t//ipv4 or ipv6. can't send to other peers.\n\t\tchar *user, *auth;\n\t\tenum {\n\t\t\tTURN_UNINITED,\t\t//never tried to poke server, need to send a quicky allocate request.\n\t\t\tTURN_HAVE_NONCE,\t//nonce should be valid, need to send a full allocate request.\n\t\t\tTURN_ALLOCATED,\t\t//we have an allocation that we need to refresh every now and then.\n\t\t\tTURN_TERMINATING,\t\t//we have an allocation that we need to refresh every now and then.\n\t\t} state;\n\t\tunsigned int expires;\t//allocation expiry time.\n\t\tchar *nonce, *realm;\t//gathered from the server\n//\t\tnetadr_t relay, srflx;\t//our relayed port, and our local address for the sake of it.\n\n\t\tunsigned int peers;\n\t\tstruct\n\t\t{\n\t\t\tunsigned int expires;\t\t//once a second, extended to once a minite on reply\n\t\t\tunsigned int retry;\t\t\t//gotta keep retrying\n\t\t\tunsigned int stunrnd[3];\t//so we know when to stop retrying.\n\t\t\tstruct icecandidate_s *rc;\t//if its not rc then we need to reauth now.\n\t\t} peer[32];\n\t} server[8];\n\tunsigned int servers;\n\n\tqboolean brokerless;\t//we lost connection to our broker... clean up on failure status.\n\tunsigned int icetimeout;\t//time when we consider the connection dead\n\tunsigned int keepalive;\t//sent periodically...\n\tunsigned int retries;\t//bumped after each round of connectivity checks. affects future intervals.\n\tenum iceproto_e proto;\n\tenum icemode_e mode;\n\tqboolean initiator;\t\t//sends the initial sdpoffer.\n\tqboolean controlled;\t//controller chooses final ports.\n\tenum icestate_e state;\n\tchar *conname;\t\t//internal id.\n\tchar *friendlyname;\t//who you're talking to.\n\n\tunsigned int originid;\t//should be randomish\n\tunsigned int originversion;//bumped each time something in the sdp changes.\n\tchar originaddress[16];\n\n\tstruct icecandidate_s *lc;\n\tchar *lpwd;\n\tchar *lufrag;\n\n\tstruct icecandidate_s *rc;\n\tchar *rpwd;\n\tchar *rufrag;\n\n\tunsigned int tiehigh;\n\tunsigned int tielow;\n\tint foundation;\n\n\tqboolean blockcandidates;\t\t//don't send candidates yet. FIXME: replace with gathering.\n#ifdef HAVE_DTLS\n\tvoid *dtlsstate;\n\tstruct sctp_s *sctp;\t//ffs! extra processing needed.\n\n\tconst dtlsfuncs_t *dtlsfuncs;\n\tqboolean dtlspassive;\t//true=server, false=client (separate from ice controller and whether we're hosting. yay...)\n\tdtlscred_t cred;\t//credentials info for dtls (both peer and local info)\n\tquint16_t mysctpport;\n\tquint16_t peersctpport;\n\tqboolean sctpoptional;\n\tqboolean peersctpoptional; //still uses dtls at least.\n#endif\n\n\tftenet_connections_t *connections;\t//used only for PRIVATE sockets.\n\n\tstruct icecodecslot_s\n\t{\n\t\t//FIXME: we should probably include decode state in here somehow so multiple connections don't clobber each other.\n\t\tint id;\n\t\tchar *name;\n\t} codecslot[34];\t\t//96-127. don't really need to care about other ones.\n\n\tstruct\n\t{\t//this block is for our inbound udp broker reliability, ensuring we get candidate info to where its needed...\n\t\tchar *text;\n\t\tunsigned int inseq;\n\t\tunsigned int outseq;\n\t} u;\n};\n\ntypedef struct sctp_s\n{\n\tchar *friendlyname;\t//for printing/debugging.\n\tstruct icestate_s *icestate;\t//for forwarding over ice connections...\n\tconst struct dtlsfuncs_s *dtlsfuncs;\t//for forwarding over dtls connects (ice-lite)\n\tvoid *dtlsstate;\n\n\tquint16_t myport, peerport;\n\tqboolean peerhasfwdtsn;\n\tdouble nextreinit;\n\tvoid *cookie;\t//sent part of the handshake (might need resending).\n\tsize_t cookiesize;\n\tstruct\n\t{\n\t\tquint32_t verifycode;\n\t\tqboolean writable;\n\t\tquint32_t tsn;\t//Transmit Sequence Number\n\n\t\tquint32_t ctsn;\t//acked tsn\n\t\tquint32_t losttsn; //total gap size...\n\t} o;\n\tstruct\n\t{\n\t\tquint32_t verifycode;\n\t\tint ackneeded;\n\t\tquint32_t ctsn;\n\t\tquint32_t htsn;\t//so we don't have to walk so many packets to count gaps.\n#define SCTP_RCVSIZE 2048\t//cannot be bigger than 65536\n\t\tqbyte received[SCTP_RCVSIZE/8];\n\n\t\tstruct\n\t\t{\n\t\t\tquint32_t\tfirsttns; //so we only ack fragments that were complete.\n\t\t\tquint32_t\ttsn; //if a continuation doesn't match, we drop it.\n\t\t\tquint32_t\tppid;\n\t\t\tquint16_t\tsid;\n\t\t\tquint16_t\tseq;\n\t\t\tsize_t\t\tsize;\n\t\t\tqboolean\ttoobig;\n\t\t\tqbyte\t\tbuf[65536];\n\t\t} r;\n\t} i;\n\tunsigned short qstreamid;\t//in network endian.\n} sctp_t;\n#ifdef HAVE_DTLS\n\nstatic const struct\n{\n\tconst char *name;\n\thashfunc_t *hash;\n} webrtc_hashes[] =\n{\t//RFC8211 specifies this list of hashes\n//\t{\"md2\",\t&hash_md2},\t//deprecated, hopefully we won't see it\n//\t{\"md5\",\t&hash_md5},\t//deprecated, hopefully we won't see it\n\t{\"sha-1\",\t&hash_sha1},\n\t{\"sha-224\",\t&hash_sha2_224},\n\t{\"sha-256\",\t&hash_sha2_256},\n\t{\"sha-384\",\t&hash_sha2_384},\n\t{\"sha-512\",\t&hash_sha2_512},\n};\n#endif\nstatic neterr_t ICE_Transmit(void *cbctx, const qbyte *data, size_t datasize);\nstatic neterr_t TURN_Encapsulate(struct icestate_s *ice, netadr_t *to, const qbyte *data, size_t datasize);\nstatic void TURN_AuthorisePeer(struct icestate_s *con, struct iceserver_s *srv, int peer);\n#ifdef HAVE_DTLS\nstatic neterr_t SCTP_Transmit(sctp_t *sctp, const void *data, size_t length);\n#endif\nstatic qboolean ICE_SetFailed(struct icestate_s *con, const char *reasonfmt, ...) LIKEPRINTF(2);\nstatic struct icestate_s *icelist;\n\n\nconst char *ICE_GetCandidateType(struct icecandinfo_s *info)\n{\n\tswitch(info->type)\n\t{\n\tcase ICE_HOST:\treturn \"host\";\n\tcase ICE_SRFLX:\treturn \"srflx\";\n\tcase ICE_PRFLX:\treturn \"prflx\";\n\tcase ICE_RELAY:\treturn \"relay\";\n\t}\n\treturn \"?\";\n}\n\n\nstatic struct icecodecslot_s *ICE_GetCodecSlot(struct icestate_s *ice, int slot)\n{\n\tif (slot >= 96 && slot < 96+32)\n\t\treturn &ice->codecslot[slot-96];\n\telse if (slot == 0)\n\t\treturn &ice->codecslot[32];\n\telse if (slot == 8)\n\t\treturn &ice->codecslot[33];\n\treturn NULL;\n}\n\n\n#if !defined(SERVERONLY) && defined(VOICECHAT)\nextern cvar_t snd_voip_send;\nstruct rtpheader_s\n{\n\tunsigned char v2_p1_x1_cc4;\n\tunsigned char m1_pt7;\n\tunsigned short seq;\n\tunsigned int timestamp;\n\tunsigned int ssrc;\n\tunsigned int csrc[1];\t//sized according to cc\n};\nvoid S_Voip_RTP_Parse(unsigned short sequence, const char *codec, const unsigned char *data, unsigned int datalen);\nqboolean S_Voip_RTP_CodecOkay(const char *codec);\nstatic qboolean NET_RTP_Parse(void)\n{\n\tstruct rtpheader_s *rtpheader = (void*)net_message.data;\n\tif (net_message.cursize >= sizeof(*rtpheader) && (rtpheader->v2_p1_x1_cc4 & 0xc0) == 0x80)\n\t{\n\t\tint hlen;\n\t\tint padding = 0;\n\t\tstruct icestate_s *con;\n\t\t//make sure this really came from an accepted rtp stream\n\t\t//note that an rtp connection equal to the game connection will likely mess up when sequences start to get big\n\t\t//(especially problematic in sane clients that start with a random sequence)\n\t\tfor (con = icelist; con; con = con->next)\n\t\t{\n\t\t\tif (con->state != ICE_INACTIVE && (con->proto == ICEP_VIDEO || con->proto == ICEP_VOICE) && NET_CompareAdr(&net_from, &con->chosenpeer))\n\t\t\t{\n\t\t\t\tstruct icecodecslot_s *codec = ICE_GetCodecSlot(con, rtpheader->m1_pt7 & 0x7f);\n\t\t\t\tif (codec)\t//untracked slot\n\t\t\t\t{\n\t\t\t\t\tchar *codecname = codec->name;\n\t\t\t\t\tif (!codecname)\t//inactive slot\n\t\t\t\t\t\tcontinue;\n\n\t\t\t\t\tif (rtpheader->v2_p1_x1_cc4 & 0x20)\n\t\t\t\t\t\tpadding = net_message.data[net_message.cursize-1];\n\t\t\t\t\thlen = sizeof(*rtpheader);\n\t\t\t\t\thlen += ((rtpheader->v2_p1_x1_cc4 & 0xf)-1) * sizeof(int);\n\t\t\t\t\tif (con->proto == ICEP_VOICE)\n\t\t\t\t\t\tS_Voip_RTP_Parse((unsigned short)BigShort(rtpheader->seq), codecname, hlen+(char*)(rtpheader), net_message.cursize - padding - hlen);\n//\t\t\t\t\tif (con->proto == ICEP_VIDEO)\n//\t\t\t\t\t\tS_Video_RTP_Parse((unsigned short)BigShort(rtpheader->seq), codecname, hlen+(char*)(rtpheader), net_message.cursize - padding - hlen);\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn false;\n}\nqboolean NET_RTP_Active(void)\n{\n\tstruct icestate_s *con;\n\tfor (con = icelist; con; con = con->next)\n\t{\n\t\tif (con->state == ICE_CONNECTED && con->proto == ICEP_VOICE)\n\t\t\treturn true;\n\t}\n\treturn false;\n}\nqboolean NET_RTP_Transmit(unsigned int sequence, unsigned int timestamp, const char *codec, char *cdata, int clength)\n{\n\tsizebuf_t buf;\n\tchar pdata[512];\n\tint i;\n\tstruct icestate_s *con;\n\tqboolean built = false;\n\n\tmemset(&buf, 0, sizeof(buf));\n\tbuf.maxsize = sizeof(pdata);\n\tbuf.cursize = 0;\n\tbuf.allowoverflow = true;\n\tbuf.data = pdata;\n\n\tfor (con = icelist; con; con = con->next)\n\t{\n\t\tif (con->state == ICE_CONNECTED && con->proto == ICEP_VOICE)\n\t\t{\n\t\t\tfor (i = 0; i < countof(con->codecslot); i++)\n\t\t\t{\n\t\t\t\tif (con->codecslot[i].name && !strcmp(con->codecslot[i].name, codec))\n\t\t\t\t{\n\t\t\t\t\tif (!built)\n\t\t\t\t\t{\n\t\t\t\t\t\tbuilt = true;\n\t\t\t\t\t\tMSG_WriteByte(&buf, (2u<<6) | (0u<<5) | (0u<<4) | (0<<0));\t//v2_p1_x1_cc4\n\t\t\t\t\t\tMSG_WriteByte(&buf, (0u<<7) | (con->codecslot[i].id<<0));\t//m1_pt7\n\t\t\t\t\t\tMSG_WriteShort(&buf, BigShort(sequence&0xffff));\t//seq\n\t\t\t\t\t\tMSG_WriteLong(&buf, BigLong(timestamp));\t//timestamp\n\t\t\t\t\t\tMSG_WriteLong(&buf, BigLong(0));\t\t\t//ssrc\n\t\t\t\t\t\tSZ_Write(&buf, cdata, clength);\n\t\t\t\t\t\tif (buf.overflowed)\n\t\t\t\t\t\t\treturn built;\n\t\t\t\t\t}\n\t\t\t\t\tICE_Transmit(con, buf.data, buf.cursize);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn built;\n}\n#endif\n\n\n\nstatic struct icestate_s *QDECL ICE_Find(void *module, const char *conname)\n{\n\tstruct icestate_s *con;\n\n\tfor (con = icelist; con; con = con->next)\n\t{\n\t\tif (con->module == module && !strcmp(con->conname, conname))\n\t\t\treturn con;\n\t}\n\treturn NULL;\n}\nstatic ftenet_connections_t *ICE_PickConnection(struct icestate_s *con)\n{\n\tif (con->connections)\n\t\treturn con->connections;\n\tswitch(con->proto)\n\t{\n\tdefault:\n\t\tbreak;\n#ifndef SERVERONLY\n\tcase ICEP_VOICE:\n\tcase ICEP_QWCLIENT:\n\t\treturn cls.sockets;\n#endif\n#ifndef CLIENTONLY\n\tcase ICEP_QWSERVER:\n\t\treturn svs.sockets;\n#endif\n\t}\n\treturn NULL;\n}\nstatic qboolean TURN_AddXorAddressAttrib(sizebuf_t *buf, unsigned int attr, netadr_t *to)\n{\t//12 or 24 bytes.\n\tint alen, atype, aofs, i;\n\tif (to->type == NA_IP)\n\t{\n\t\talen = 4;\n\t\tatype = 1;\n\t\taofs = 0;\n\t}\n\telse if (to->type == NA_IPV6 &&\n\t\t\t\t!*(int*)&to->address.ip6[0] &&\n\t\t\t\t!*(int*)&to->address.ip6[4] &&\n\t\t\t\t!*(short*)&to->address.ip6[8] &&\n\t\t\t\t*(short*)&to->address.ip6[10] == (short)0xffff)\n\t{\t//just because we use an ipv6 address for ipv4 internally doesn't mean we should tell the peer that they're on ipv6...\n\t\talen = 4;\n\t\tatype = 1;\n\t\taofs = sizeof(to->address.ip6) - sizeof(to->address.ip);\n\t}\n\telse if (to->type == NA_IPV6)\n\t{\n\t\talen = 16;\n\t\tatype = 2;\n\t\taofs = 0;\n\t}\n\telse\n\t\treturn false;\n\n\tMSG_WriteShort(buf, BigShort(attr));\n\tMSG_WriteShort(buf, BigShort(4+alen));\n\tMSG_WriteShort(buf, BigShort(atype));\n\tMSG_WriteShort(buf, to->port ^ *(short*)(buf->data+4));\n\tfor (i = 0; i < alen; i++)\n\t\tMSG_WriteByte(buf, ((qbyte*)&to->address)[aofs+i] ^ (buf->data+4)[i]);\n\treturn true;\n}\nstatic qboolean TURN_AddAuth(sizebuf_t *buf, struct iceserver_s *srv)\n{\t//adds auth info to a stun packet\n\tunsigned short len;\n\tchar integrity[DIGEST_MAXSIZE];\n\thashfunc_t *hash = &hash_sha1;\n\thashfunc_t *pwdhash = &hash_md5;\n\n\tif (!srv->user || !srv->nonce || !srv->realm)\n\t\treturn false;\n\tMSG_WriteShort(buf, BigShort(STUNATTR_USERNAME));\n\tlen = strlen(srv->user);\n\tMSG_WriteShort(buf, BigShort(len));\n\tSZ_Write (buf, srv->user, len);\n\tif (len&3)\n\t\tSZ_Write (buf, \"\\0\\0\\0\\0\", 4-(len&3));\n\n\tMSG_WriteShort(buf, BigShort(STUNATTR_REALM));\n\tlen = strlen(srv->realm);\n\tMSG_WriteShort(buf, BigShort(len));\n\tSZ_Write (buf, srv->realm, len);\n\tif (len&3)\n\t\tSZ_Write (buf, \"\\0\\0\\0\\0\", 4-(len&3));\n\n\tMSG_WriteShort(buf, BigShort(STUNATTR_NONCE));\n\tlen = strlen(srv->nonce);\n\tMSG_WriteShort(buf, BigShort(len));\n\tSZ_Write (buf, srv->nonce, len);\n\tif (len&3)\n\t\tSZ_Write (buf, \"\\0\\0\\0\\0\", 4-(len&3));\n\n\tif (pwdhash != &hash_md5)\n\t{\n\t\tMSG_WriteShort(buf, BigShort(STUNATTR_PASSWORD_ALGORITHM));\n\t\tlen = strlen(srv->nonce);\n\t\tMSG_WriteShort(buf, 4);\n\t\tif (pwdhash == &hash_md5)\n\t\t\tMSG_WriteShort(buf, 1);\n\t\telse if (pwdhash == &hash_sha2_256)\n\t\t\tMSG_WriteShort(buf, 2);\n\t\telse\n\t\t\treturn false;\t//not defined... panic.\n\t\tMSG_WriteShort(buf, 0);\t//paramlength\n\t\t//no params.\n\t}\n\n\t//message integrity is a bit annoying\n\tbuf->data[2] = ((buf->cursize+4+hash->digestsize-20)>>8)&0xff;\t//hashed header length is up to the end of the hmac attribute\n\tbuf->data[3] = ((buf->cursize+4+hash->digestsize-20)>>0)&0xff;\n\t//but the hash is to the start of the attribute's header\n\t{\t//long-term credentials do stuff weird.\n\t\tchar *tmpkey = va(\"%s:%s:%s\", srv->user, srv->realm, srv->auth);\n\t\tlen = CalcHash(pwdhash, integrity,sizeof(integrity), tmpkey, strlen(tmpkey));\n\t}\n\tlen = CalcHMAC(hash, integrity, sizeof(integrity), buf->data, buf->cursize, integrity,len);\n\tif (hash == &hash_sha2_256)\n\t\tMSG_WriteShort(buf, BigShort(STUNATTR_MSGINTEGRITIY_SHA2_256));\n\telse if (hash == &hash_sha1)\n\t\tMSG_WriteShort(buf, BigShort(STUNATTR_MSGINTEGRITIY_SHA1));\n\telse\n\t\treturn false;\t//not defined!\n\tMSG_WriteShort(buf, BigShort(len));\t//integrity length\n\tSZ_Write(buf, integrity, len);\t//integrity data\n\treturn true;\n}\n\nstatic const char *ICE_NetworkToName(struct icestate_s *ice, int network)\n{\n\tnetwork -= 1;\n\tif (network >= 0 && network < MAX_CONNECTIONS)\n\t{\t//return the cvar name\n\t\tftenet_connections_t *col = ICE_PickConnection(ice);\n\t\tif (col && col->conn[network])\n\t\t\treturn col->conn[network]->name;\n\t}\n\telse if (network >= MAX_CONNECTIONS)\n\t{\n\t\tnetwork -= MAX_CONNECTIONS;\n\t\tif (network >= countof(ice->server))\n\t\t{\t//a peer-reflexive address from poking a TURN server...\n\t\t\tnetwork -= countof(ice->server);\n\t\t\tif (network < ice->servers)\n\t\t\t\treturn \"turn-reflexive\";\n\t\t}\n\t\telse\n\t\t\treturn va(\"turn:%s\", ice->server[network].realm);\n\t}\n\n\treturn \"<UNKNOWN>\";\n}\nstatic neterr_t TURN_Encapsulate(struct icestate_s *ice, netadr_t *to, const qbyte *data, size_t datasize)\n{\n\tsizebuf_t buf;\n\tunsigned int network = to->connum-1;\n\tstruct iceserver_s *srv;\n\tif (to->type == NA_INVALID)\n\t\treturn NETERR_NOROUTE;\n\tif (to->connum && network >= MAX_CONNECTIONS)\n\t{\t//fancy turn-related gubbins\n\t\tnetwork -= MAX_CONNECTIONS;\n\t\tif (network >= countof(ice->server))\n\t\t{\t//really high, its from the raw socket, unstunned.\n\t\t\tnetwork -= countof(ice->server);\n\t\t\tif (network >= countof(ice->server))\n\t\t\t\treturn NETERR_NOROUTE;\n\t\t\tsrv = &ice->server[network];\n\n\t\t\tif (!srv->con || net_ice_relayonly.ival)\n\t\t\t\treturn NETERR_CLOGGED;\n\t\t\treturn srv->con->SendPacket(srv->con, datasize, data, &srv->addr);\n\t\t}\n\t\tsrv = &ice->server[network];\n\n\t\tmemset(&buf, 0, sizeof(buf));\n\t\tbuf.maxsize =\t20+//stun header\n\t\t\t\t\t\t8+16+//(max)peeraddr\n\t\t\t\t\t\t4+((datasize+3)&~3);//data\n\t\tbuf.cursize = 0;\n\t\tbuf.data = alloca(buf.maxsize);\n\n\t\tMSG_WriteShort(&buf, BigShort(STUN_INDICATION|STUN_SEND));\n\t\tMSG_WriteShort(&buf, 0);\t//fill in later\n\t\tMSG_WriteLong(&buf, BigLong(STUN_MAGIC_COOKIE));\n\t\tMSG_WriteLong(&buf, 0);\t//randomid\n\t\tMSG_WriteLong(&buf, 0);\t//randomid\n\t\tMSG_WriteLong(&buf, 0);\t//randomid\n\n\t\tif (!TURN_AddXorAddressAttrib(&buf, STUNATTR_XOR_PEER_ADDRESS, to))\n\t\t\treturn NETERR_NOROUTE;\n\n\t\tMSG_WriteShort(&buf, BigShort(STUNATTR_DATA));\n\t\tMSG_WriteShort(&buf, BigShort(datasize));\n\t\tSZ_Write(&buf, data, datasize);\n\t\tif (datasize&3)\n\t\t\tSZ_Write(&buf, \"\\0\\0\\0\\0\", 4-(datasize&3));\n\n\t\t//fill in the length (for the final time, after filling in the integrity and fingerprint)\n\t\tbuf.data[2] = ((buf.cursize-20)>>8)&0xff;\n\t\tbuf.data[3] = ((buf.cursize-20)>>0)&0xff;\n\n\t\tif (!srv->con)\n\t\t\treturn NETERR_CLOGGED;\n\t\treturn srv->con->SendPacket(srv->con, buf.cursize, buf.data, &srv->addr);\n\t}\n\tif (net_ice_relayonly.ival)\n\t\treturn NETERR_CLOGGED;\n\treturn NET_SendPacket(ICE_PickConnection(ice), datasize, data, to);\n}\nstatic neterr_t ICE_Transmit(void *cbctx, const qbyte *data, size_t datasize)\n{\n\tstruct icestate_s *ice = cbctx;\n\tif (ice->chosenpeer.type == NA_INVALID)\n\t{\n\t\tif (ice->state == ICE_FAILED)\n\t\t\treturn NETERR_DISCONNECTED;\n\t\telse\n\t\t\treturn NETERR_CLOGGED;\n\t}\n\n\treturn TURN_Encapsulate(ice, &ice->chosenpeer, data, datasize);\n}\n\nstatic struct icestate_s *QDECL ICE_Create(void *module, const char *conname, const char *peername, enum icemode_e mode, enum iceproto_e proto, qboolean initiator)\n{\n\tftenet_connections_t *collection;\n\tstruct icestate_s *con;\n\tstatic unsigned int icenum;\n\n\t//only allow modes that we actually support.\n\tif (mode != ICEM_RAW && mode != ICEM_ICE && mode != ICEM_WEBRTC)\n\t\treturn NULL;\n#ifndef HAVE_DTLS\n\tif (mode == ICEM_WEBRTC)\n\t\treturn NULL;\n#endif\n\n\t//only allow protocols that we actually support.\n\tswitch(proto)\n\t{\n\tdefault:\n\t\treturn NULL;\n//\tcase ICEP_VIDEO:\n//\t\tcollection = NULL;\n//\t\tbreak;\n#if !defined(SERVERONLY) && defined(VOICECHAT)\n\tcase ICEP_VOICE:\n\tcase ICEP_VIDEO:\n\t\tcollection = cls.sockets;\n\t\tif (!collection)\n\t\t{\n\t\t\tNET_InitClient(false);\n\t\t\tcollection = cls.sockets;\n\t\t}\n\t\tbreak;\n#endif\n#ifndef SERVERONLY\n\tcase ICEP_QWCLIENT:\n\t\tcollection = cls.sockets;\n\t\tif (!collection)\n\t\t{\n\t\t\tNET_InitClient(false);\n\t\t\tcollection = cls.sockets;\n\t\t}\n\t\tbreak;\n#endif\n#ifndef CLIENTONLY\n\tcase ICEP_QWSERVER:\n\t\tcollection = svs.sockets;\n\t\tbreak;\n#endif\n\t}\n\tif (!collection)\n\t\treturn NULL;\t//not initable or something\n\n\tif (!conname)\n\t{\n\t\tint rnd[2];\n\t\tSys_RandomBytes((void*)rnd, sizeof(rnd));\n\t\tconname = va(\"fte%08x%08x\", rnd[0], rnd[1]);\n\t}\n\n\tif (ICE_Find(module, conname))\n\t\treturn NULL;\t//don't allow dupes.\n\t\n\tcon = Z_Malloc(sizeof(*con));\n\tcon->conname = Z_StrDup(conname);\n\ticenum++;\n\tcon->friendlyname = peername?Z_StrDup(peername):Z_StrDupf(\"%i\", icenum);\n\tcon->proto = proto;\n\tcon->rpwd = Z_StrDup(\"\");\n\tcon->rufrag = Z_StrDup(\"\");\n\tSys_RandomBytes((void*)&con->originid, sizeof(con->originid));\n\tcon->originversion = 1;\n\tQ_strncpyz(con->originaddress, \"127.0.0.1\", sizeof(con->originaddress));\n\n\tcon->initiator = initiator;\n\tcon->controlled = !initiator;\n\tcon->blockcandidates = true;\t//until offers/answers are sent.\n\n#ifdef HAVE_DTLS\n\tcon->dtlspassive = (proto == ICEP_QWSERVER);\t//note: may change later.\n\n\tif (mode == ICEM_WEBRTC)\n\t{\t//dtls+sctp is a mandatory part of our connection, sadly.\n\t\tif (!con->dtlsfuncs)\n\t\t{\n\t\t\tif (con->dtlspassive)\n\t\t\t\tcon->dtlsfuncs = DTLS_InitServer();\n\t\t\telse\n\t\t\t\tcon->dtlsfuncs = DTLS_InitClient();\t//credentials are a bit different, though fingerprints make it somewhat irrelevant.\n\t\t}\n\t\tif (con->dtlsfuncs && con->dtlsfuncs->GenTempCertificate && !con->cred.local.certsize)\n\t\t{\n\t\t\tCon_DPrintf(\"Generating dtls certificate...\\n\");\n\t\t\tif (!con->dtlsfuncs->GenTempCertificate(NULL, &con->cred.local))\n\t\t\t{\n\t\t\t\tcon->dtlsfuncs = NULL;\n\t\t\t\tmode = ICEM_ICE;\n\t\t\t\tCon_DPrintf(\"Failed\\n\");\n\t\t\t}\n\t\t\telse\n\t\t\t\tCon_DPrintf(\"Done\\n\");\n\t\t}\n\t\telse\n\t\t{\t//failure if we can't do the whole dtls thing.\n\t\t\tcon->dtlsfuncs = NULL;\n\t\t\tCon_Printf(CON_WARNING\"DTLS %s support unavailable, disabling encryption (and webrtc compat).\\n\", con->dtlspassive?\"server\":\"client\");\n\t\t\tmode = ICEM_ICE;\t//fall back on unencrypted (this doesn't depend on the peer, so while shitty it hopefully shouldn't be exploitable with a downgrade-attack)\n\t\t}\n\n\t\tif (mode == ICEM_WEBRTC && !net_ice_usewebrtc.ival)\n\t\t\tcon->sctpoptional = true;\n\n\t\tif (mode == ICEM_WEBRTC)\n\t\t\tcon->mysctpport = 27500;\n\t}\n\n\tcon->qadr.port = htons(con->mysctpport);\n#endif\n\tcon->qadr.type = NA_ICE;\n\tcon->qadr.prot = NP_DGRAM;\n\tQ_strncpyz(con->qadr.address.icename, con->friendlyname, sizeof(con->qadr.address.icename));\n\tcon->qadr.port = icenum;\n\n\tcon->mode = mode;\n\n\tcon->next = icelist;\n\ticelist = con;\n\n\t{\n\t\tint rnd[1];\t//'must have at least 24 bits randomness'\n\t\tSys_RandomBytes((void*)rnd, sizeof(rnd));\n\t\tcon->lufrag = Z_StrDupf(\"%08x\", rnd[0]);\n\t}\n\t{\n\t\tint rnd[4];\t//'must have at least 128 bits randomness'\n\t\tSys_RandomBytes((void*)rnd, sizeof(rnd));\n\t\tcon->lpwd = Z_StrDupf(\"%08x%08x%08x%08x\", rnd[0], rnd[1], rnd[2], rnd[3]);\n\t}\n\n\tSys_RandomBytes((void*)&con->tiehigh, sizeof(con->tiehigh));\n\tSys_RandomBytes((void*)&con->tielow, sizeof(con->tielow));\n\n\tif (collection)\n\t{\n\t\tint i, m;\n\n\t\tnetadr_t\taddr[64];\n\t\tstruct ftenet_generic_connection_s\t\t\t*gcon[sizeof(addr)/sizeof(addr[0])];\n\t\tunsigned int\t\t\tflags[sizeof(addr)/sizeof(addr[0])];\n\t\tconst char *params[sizeof(addr)/sizeof(addr[0])];\n\n\t\tm = NET_EnumerateAddresses(collection, gcon, flags, addr, params, sizeof(addr)/sizeof(addr[0]));\n\n\t\tfor (i = 0; i < m; i++)\n\t\t{\n\t\t\tif (addr[i].type == NA_IP || addr[i].type == NA_IPV6)\n\t\t\t{\n\t\t\t\tif (flags[i] & ADDR_REFLEX)\n\t\t\t\t\tICE_AddLCandidateInfo(con, &addr[i], i, ICE_SRFLX); //FIXME: needs reladdr relport info\n\t\t\t\telse\n\t\t\t\t\tICE_AddLCandidateInfo(con, &addr[i], i, ICE_HOST);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn con;\n}\n//if either remotecand is null, new packets will be sent to all.\nstatic qboolean ICE_SendSpam(struct icestate_s *con)\n{\n\tstruct icecandidate_s *rc;\n\tint i;\n\tint bestlocal = -1;\n\tstruct icecandidate_s *bestpeer = NULL;\n\tftenet_connections_t *collection = ICE_PickConnection(con);\n\tif (!collection)\n\t\treturn false;\n\n\t//only send one ping to each.\n\tfor(rc = con->rc; rc; rc = rc->next)\n\t{\n\t\tfor (i = 0; i < MAX_CONNECTIONS; i++)\n\t\t{\n\t\t\tif (collection->conn[i] && collection->conn[i]->prot == NP_DGRAM && (collection->conn[i]->addrtype[0]==NA_IP||collection->conn[i]->addrtype[0]==NA_IPV6))\n\t\t\t{\n\t\t\t\tif (!(rc->tried & (1u<<i)))\n\t\t\t\t{\t\t\t\t\t\n\t\t\t\t\tif (!bestpeer || bestpeer->info.priority < rc->info.priority)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (NET_ClassifyAddress(&rc->peer, NULL) < ASCOPE_TURN_REQUIRESCOPE)\n\t\t\t\t\t\t{\t//don't waste time asking the relay to poke its loopback. if only because it'll report lots of errors.\n\t\t\t\t\t\t\trc->tried |= (1u<<i);\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tbestpeer = rc;\n\t\t\t\t\t\tbestlocal = i;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t//send via appropriate turn servers\n\t\tif (rc->info.type == ICE_RELAY || rc->info.type == ICE_SRFLX)\n\t\t{\n\t\t\tfor (i = 0; i < con->servers; i++)\n\t\t\t{\n\t\t\t\tif (con->server[i].state!=TURN_ALLOCATED)\n\t\t\t\t\tcontinue;\t//not ready yet...\n\t\t\t\tif (!con->server[i].con)\n\t\t\t\t\tcontinue;\t//can't...\n\t\t\t\tif (!(rc->tried & (1u<<(MAX_CONNECTIONS+i))))\n\t\t\t\t{\n\t\t\t\t\t//fixme: no local priority. a multihomed machine will try the same ip from different ports.\n\t\t\t\t\tif (!bestpeer || bestpeer->info.priority < rc->info.priority)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (con->server[i].family && rc->peer.type != con->server[i].family)\n\t\t\t\t\t\t\tcontinue;\t//if its ipv4-only then don't send it ipv6 packets or whatever.\n\t\t\t\t\t\tbestpeer = rc;\n\t\t\t\t\t\tbestlocal = MAX_CONNECTIONS+i;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\n\tif (bestpeer && bestlocal >= 0)\n\t{\n\t\tneterr_t err;\n\t\tnetadr_t to;\n\t\tsizebuf_t buf;\n\t\tchar data[512];\n\t\tchar integ[20];\n\t\tint crc;\n\t\tqboolean usecandidate = false;\n\t\tconst char *candtype;\n\t\tunsigned int priority;\n\t\tmemset(&buf, 0, sizeof(buf));\n\t\tbuf.maxsize = sizeof(data);\n\t\tbuf.cursize = 0;\n\t\tbuf.data = data;\n\n\t\tbestpeer->tried |= (1u<<bestlocal);\n\n\t\tto = bestpeer->peer;\n\t\tto.connum = 1+bestlocal;\n\n\t\tif (to.type == NA_INVALID)\n\t\t\treturn true; //erk?\n\n\t\tsafeswitch(bestpeer->info.type)\n\t\t{\n\t\tcase ICE_HOST:\tcandtype=\"host\";\tbreak;\n\t\tcase ICE_SRFLX:\tcandtype=\"srflx\";\tbreak;\n\t\tcase ICE_PRFLX:\tcandtype=\"prflx\";\tbreak;\n\t\tcase ICE_RELAY:\tcandtype=\"relay\";\tbreak;\n\t\tsafedefault: candtype=\"?\";\n\t\t}\n\n\t\tif (bestlocal >= MAX_CONNECTIONS)\n\t\t{\n\t\t\tstruct iceserver_s *srv = &con->server[bestlocal-MAX_CONNECTIONS];\n\t\t\tunsigned int i;\n\t\t\tunsigned int now = Sys_Milliseconds();\n\n\t\t\tfor (i = 0; ; i++)\n\t\t\t{\n\t\t\t\tif (i == srv->peers)\n\t\t\t\t{\n\t\t\t\t\tif (i == countof(srv->peer))\n\t\t\t\t\t\treturn true;\n\t\t\t\t\tsrv->peer[i].expires = now;\n\t\t\t\t\tsrv->peer[i].rc = bestpeer;\n\t\t\t\t\tSys_RandomBytes((char*)srv->peer[i].stunrnd, sizeof(srv->peer[i].stunrnd));\n\t\t\t\t\tsrv->peer[i].retry = now;\n\t\t\t\t\tsrv->peers++;\n\t\t\t\t}\n\t\t\t\tif (srv->peer[i].rc == bestpeer)\n\t\t\t\t{\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif ((int)(srv->peer[i].retry-now) <= 0)\n\t\t\t{\n\t\t\t\tsrv->peer[i].retry = now + (con->state==ICE_CONNECTED?2000:50);\t//keep retrying till we get an ack. be less agressive once it no longer matters so much\n\t\t\t\tTURN_AuthorisePeer(con, srv, i);\n\t\t\t}\n\t\t}\n\n\t\tif (!con->controlled && NET_CompareAdr(&to, &con->chosenpeer) && to.connum == con->chosenpeer.connum)\n\t\t\tusecandidate = true;\n\n\t\tMSG_WriteShort(&buf, BigShort(STUN_BINDING));\n\t\tMSG_WriteShort(&buf, 0);\t//fill in later\n\t\tMSG_WriteLong(&buf, BigLong(STUN_MAGIC_COOKIE));\t//magic\n\t\tMSG_WriteLong(&buf, BigLong(0));\t\t\t\t\t//randomid\n\t\tMSG_WriteLong(&buf, BigLong(0));\t\t\t\t\t//randomid\n\t\tMSG_WriteLong(&buf, BigLong(0x80000000|bestlocal));\t//randomid\n\n\t\tif (usecandidate)\n\t\t{\n\t\t\tMSG_WriteShort(&buf, BigShort(STUNATTR_ICE_USE_CANDIDATE));//ICE-USE-CANDIDATE\n\t\t\tMSG_WriteShort(&buf, BigShort(0));\t//just a flag, so no payload to this attribute\n\t\t}\n\n\t\t//username\n\t\tMSG_WriteShort(&buf, BigShort(STUNATTR_USERNAME));\t//USERNAME\n\t\tMSG_WriteShort(&buf, BigShort(strlen(con->rufrag) + 1 + strlen(con->lufrag)));\n\t\tSZ_Write(&buf, con->rufrag, strlen(con->rufrag));\n\t\tMSG_WriteChar(&buf, ':');\n\t\tSZ_Write(&buf, con->lufrag, strlen(con->lufrag));\n\t\twhile(buf.cursize&3)\n\t\t\tMSG_WriteChar(&buf, 0);\t//pad\n\n\t\t//priority\n\t\tpriority =\n\t\t\t//FIXME. should be set to:\n\t\t\t//\t\t\tpriority =\t(2^24)*(type preference) +\n\t\t\t//\t\t\t\t\t\t(2^8)*(local preference) +\n\t\t\t//\t\t\t\t\t\t(2^0)*(256 - component ID)\n\t\t\t//type preference should be 126 and is a function of the candidate type (direct sending should be highest priority at 126)\n\t\t\t//local preference should reflect multi-homed preferences. ipv4+ipv6 count as multihomed.\n\t\t\t//component ID should be 1 (rtcp would be 2 if we supported it)\n\t\t\t(1<<24)*(bestlocal>=MAX_CONNECTIONS?0:126) +\n\t\t\t(1<<8)*((bestpeer->peer.type == NA_IP?32768:0)+bestlocal*256+(255/*-adrno*/)) +\n\t\t\t(1<<0)*(256 - bestpeer->info.component);\n\t\tMSG_WriteShort(&buf, BigShort(STUNATTR_ICE_PRIORITY));//ICE-PRIORITY\n\t\tMSG_WriteShort(&buf, BigShort(4));\n\t\tMSG_WriteLong(&buf, BigLong(priority));\n\n\t\t//these two attributes carry a random 64bit tie-breaker.\n\t\t//the controller is the one with the highest number.\n\t\tif (con->controlled)\n\t\t{\n\t\t\tMSG_WriteShort(&buf, BigShort(STUNATTR_ICE_CONTROLLED));//ICE-CONTROLLED\n\t\t\tMSG_WriteShort(&buf, BigShort(8));\n\t\t\tMSG_WriteLong(&buf, BigLong(con->tiehigh));\n\t\t\tMSG_WriteLong(&buf, BigLong(con->tielow));\n\t\t}\n\t\telse\n\t\t{\n\t\t\tMSG_WriteShort(&buf, BigShort(STUNATTR_ICE_CONTROLLING));//ICE-CONTROLLING\n\t\t\tMSG_WriteShort(&buf, BigShort(8));\n\t\t\tMSG_WriteLong(&buf, BigLong(con->tiehigh));\n\t\t\tMSG_WriteLong(&buf, BigLong(con->tielow));\n\t\t}\n\n\t\t//message integrity is a bit annoying\n\t\tdata[2] = ((buf.cursize+4+sizeof(integ)-20)>>8)&0xff;\t//hashed header length is up to the end of the hmac attribute\n\t\tdata[3] = ((buf.cursize+4+sizeof(integ)-20)>>0)&0xff;\n\t\t//but the hash is to the start of the attribute's header\n\t\tCalcHMAC(&hash_sha1, integ, sizeof(integ), data, buf.cursize, con->rpwd, strlen(con->rpwd));\n\t\tMSG_WriteShort(&buf, BigShort(STUNATTR_MSGINTEGRITIY_SHA1));\t//MESSAGE-INTEGRITY\n\t\tMSG_WriteShort(&buf, BigShort(20));\t//sha1 key length\n\t\tSZ_Write(&buf, integ, sizeof(integ));\t//integrity data\n\n\t\tdata[2] = ((buf.cursize+8-20)>>8)&0xff;\t//dummy length\n\t\tdata[3] = ((buf.cursize+8-20)>>0)&0xff;\n\t\tcrc = crc32(0, data, buf.cursize)^0x5354554e;\n\t\tMSG_WriteShort(&buf, BigShort(STUNATTR_FINGERPRINT));\t//FINGERPRINT\n\t\tMSG_WriteShort(&buf, BigShort(sizeof(crc)));\n\t\tMSG_WriteLong(&buf, BigLong(crc));\n\n\t\t//fill in the length (for the fourth time, after filling in the integrity and fingerprint)\n\t\tdata[2] = ((buf.cursize-20)>>8)&0xff;\n\t\tdata[3] = ((buf.cursize-20)>>0)&0xff;\n\n\t\terr = TURN_Encapsulate(con, &to, buf.data, buf.cursize);\n\n\t\tswitch(err)\n\t\t{\n\t\tcase NETERR_SENT:\n\t\t\tif (net_ice_debug.ival >= 2)\n\t\t\t\tCon_Printf(S_COLOR_GRAY\"[%s]: checking %s -> %s:%i (%s)\\n\", con->friendlyname, ICE_NetworkToName(con, to.connum), bestpeer->info.addr, bestpeer->info.port, candtype);\n\t\t\tbreak;\n\t\tcase NETERR_CLOGGED:\t//oh well... we retry anyway.\n\t\t\tbreak;\n\n\t\tcase NETERR_NOROUTE:\n\t\t\tif (net_ice_debug.ival >= 2)\n\t\t\t\tCon_Printf(S_COLOR_GRAY\"ICE NETERR_NOROUTE to %s:%i(%s)\\n\", bestpeer->info.addr, bestpeer->info.port, candtype);\n\t\t\tbreak;\n\t\tcase NETERR_DISCONNECTED:\n\t\t\tif (net_ice_debug.ival >= 2)\n\t\t\t\tCon_Printf(S_COLOR_GRAY\"ICE NETERR_DISCONNECTED to %s:%i(%s)\\n\", bestpeer->info.addr, bestpeer->info.port, candtype);\n\t\t\tbreak;\n\t\tcase NETERR_MTU:\n\t\t\tif (net_ice_debug.ival >= 2)\n\t\t\t\tCon_Printf(S_COLOR_GRAY\"ICE NETERR_MTU to %s:%i(%s) (%i bytes)\\n\", bestpeer->info.addr, bestpeer->info.port, candtype, buf.cursize);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tif (net_ice_debug.ival >= 2)\n\t\t\t\tCon_Printf(S_COLOR_GRAY\"ICE send error(%i) to %s:%i(%s)\\n\", err, bestpeer->info.addr, bestpeer->info.port, candtype);\n\t\t\tbreak;\n\t\t}\n\t\treturn true;\n\t}\n\treturn false;\n}\n\n#ifdef HAVE_TCP\nstruct turntcp_connection_s\n{\t//this sends packets only to the relay, and accepts them only from there too. all packets must be stun packets (for byte-count framing)\n\tftenet_generic_connection_t pub;\n\tvfsfile_t *f;\n\tstruct\n\t{\n\t\tqbyte buf[20+65536];\n\t\tunsigned int offset;\n\t\tunsigned int avail;\n\t} recv, send;\n\tnetadr_t adr;\n};\nstatic qboolean TURN_TCP_GetPacket(struct ftenet_generic_connection_s *con)\n{\n\tstruct turntcp_connection_s *n = (struct turntcp_connection_s*)con;\n\tqboolean tried = false;\n\tint err;\n\n\tif (n->send.avail)\n\t{\n\t\terr = VFS_WRITE(n->f, n->send.buf+n->send.offset, n->send.avail);\n\t\tif (err >= 0)\n\t\t{\n\t\t\tn->send.avail -= err;\n\t\t\tn->send.offset += err;\n\t\t}\n\t}\n\n\tfor (;;)\n\t{\n\t\tif (n->recv.avail >= 20) //must be a stun packet...\n\t\t{\n\t\t\tunsigned int psize = 20 + (n->recv.buf[n->recv.offset+2]<<8)+n->recv.buf[n->recv.offset+3];\n\t\t\tif (psize <= n->recv.avail)\n\t\t\t{\n\t\t\t\tmemcpy(net_message.data, n->recv.buf+n->recv.offset, psize);\n\t\t\t\tn->recv.avail -= psize;\n\t\t\t\tn->recv.offset += psize;\n\t\t\t\tnet_message.cursize = psize;\n\t\t\t\tnet_from = n->adr;\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\tif (n->recv.offset && n->recv.offset+n->recv.avail == sizeof(n->recv.buf))\n\t\t{\n\t\t\tmemmove(n->recv.buf, n->recv.buf+n->recv.offset, n->recv.avail);\n\t\t\tn->recv.offset = 0;\n\t\t}\n\t\telse if (tried)\n\t\t\treturn false;\t//don't infinitely loop.\n\t\terr = VFS_READ(n->f, n->recv.buf+n->recv.offset+n->recv.avail, sizeof(n->recv.buf) - (n->recv.offset+n->recv.avail));\n\t\tif (!err)\t//no data...\n\t\t\treturn false;\n\t\telse if (err < 0)\n\t\t\treturn false;\n\t\telse\n\t\t{\n\t\t\ttried = true;\n\t\t\tn->recv.avail += err;\n\t\t}\n\t}\n}\nstatic neterr_t TURN_TCP_SendPacket(struct ftenet_generic_connection_s *con, int length, const void *data, netadr_t *to)\n{\n\tint err;\n\tstruct turntcp_connection_s *n = (struct turntcp_connection_s*)con;\n\tif (!NET_CompareAdr(to, &n->adr))\n\t\treturn NETERR_NOROUTE;\n\n\t//validate the packet - make sure its a TURN one. only checking size here cos we're lazy and that's enough.\n\tif (length < 20 || length != 20 + (((const qbyte*)data)[2]<<8)+((const qbyte*)data)[3])\n\t\treturn NETERR_NOROUTE;\n\n\tif (!n->send.avail && length < sizeof(n->send.buf))\n\t{\t//avoid copying if we have to\n\t\terr = VFS_WRITE(n->f, data, length);\n\t\tif (err >= 0 && err < length)\n\t\t{\t//but sometimes its partial.\n\t\t\tdata = (const char*)data + err;\n\t\t\tlength -= err;\n\t\t\tn->send.offset = 0;\n\t\t\tmemcpy(n->send.buf, data, length);\n\t\t\tn->send.avail = length;\n\t\t}\n\t\tif (!err)\n\t\t\treturn NETERR_CLOGGED;\t//mostly so we don't spam while still doing tcp/tls handshakes.\n\t}\n\telse\n\t{\n\t\tif (               n->send.avail+length > sizeof(n->send.buf))\n\t\t\treturn NETERR_CLOGGED; //can't possibly fit.\n\t\tif (n->send.offset+n->send.avail+length > sizeof(n->send.buf))\n\t\t{\t//move it down if we need.\n\t\t\tmemmove(n->send.buf, n->send.buf+n->send.offset, n->send.avail);\n\t\t\tn->send.offset = 0;\n\t\t}\n\t\tmemcpy(n->send.buf+n->send.offset, data, length);\n\t\tn->send.avail += length;\n\t\terr = VFS_WRITE(n->f, n->send.buf+n->send.offset, n->send.avail);\n\t\tif (err >= 0)\n\t\t{\n\t\t\tn->send.offset += err;\n\t\t\tn->send.avail -= err;\n\t\t}\n\t}\n\tif (err >= 0)\n\t{\n\t\treturn NETERR_SENT;\t//sent something at least.\n\t}\n\telse\n\t{\n\t\t//one of the VFS_ERROR_* codes\n\t\treturn NETERR_DISCONNECTED;\n\t}\n}\nstatic void TURN_TCP_Close(struct ftenet_generic_connection_s *con)\n{\n\tstruct turntcp_connection_s *n = (struct turntcp_connection_s*)con;\n\tif (n->f)\n\t\tVFS_CLOSE(n->f);\n}\nstatic ftenet_generic_connection_t *TURN_TCP_EstablishConnection(ftenet_connections_t *col, const char *address, netadr_t adr)\n{\n\tstruct turntcp_connection_s *n = Z_Malloc(sizeof(*n));\n\tn->pub.thesocket = TCP_OpenStream(&adr, address);\n\tn->f = FS_WrapTCPSocket(n->pub.thesocket, true, address);\n\n#ifdef HAVE_SSL\n\t//convert to tls...\n\tif (adr.prot == NP_TLS || adr.prot == NP_RTC_TLS)\n\t\tn->f = FS_OpenSSL(address, n->f, false);\n#endif\n\n\tif (!n->f)\n\t{\n\t\tZ_Free(n);\n\t\treturn NULL;\n\t}\n\tn->pub.owner = col;\n\tn->pub.SendPacket = TURN_TCP_SendPacket;\n\tn->pub.GetPacket = TURN_TCP_GetPacket;\n\tn->pub.Close = TURN_TCP_Close;\n\tn->adr = adr;\n\n\treturn &n->pub;\n}\n#endif\n\nstatic void ICE_ToStunServer(struct icestate_s *con, struct iceserver_s *srv)\n{\n\tsizebuf_t buf;\n\tchar data[512];\n\tint crc;\n\tftenet_connections_t *collection = ICE_PickConnection(con);\n\tneterr_t err;\n\tif (!collection)\n\t\treturn;\n\n\tmemset(&buf, 0, sizeof(buf));\n\tbuf.maxsize = sizeof(data);\n\tbuf.cursize = 0;\n\tbuf.data = data;\n\n\tif (srv->isstun)\n\t{\n\t\tif (!net_ice_allowstun.ival || net_ice_relayonly.ival)\n\t\t\treturn;\n\t\tif (net_ice_debug.ival >= 2)\n\t\t\tCon_Printf(S_COLOR_GRAY\"[%s]: STUN: Checking public IP via %s\\n\", con->friendlyname, NET_AdrToString(data, sizeof(data), &srv->addr));\n\t\tMSG_WriteShort(&buf, BigShort(STUN_BINDING));\n\t}\n\telse\n\t{\n\t\tif (!net_ice_allowturn.ival)\n\t\t{\n\t\t\tif (net_ice_relayonly.ival)\n\t\t\t{\n\t\t\t\tCon_Printf(\"%s: forcing %s on\\n\", net_ice_relayonly.name, net_ice_allowturn.name);\n\t\t\t\tCvar_Set(&net_ice_allowturn, \"1\");\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tif (!srv->con)\n\t\t{\n\t\t\tif (srv->addr.type == NA_INVALID)\n\t\t\t\treturn; //nope...\n\t\t\tif (srv->addr.prot != NP_DGRAM)\n\t\t\t{\n#ifdef HAVE_TCP\n\t\t\t\tsrv->con = TURN_TCP_EstablishConnection(collection, srv->realm, srv->addr);\n#else\n\t\t\t\tsrv->con = NULL;\n#endif\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tnetadr_t localadr;\n\t\t\t\tmemset(&localadr, 0, sizeof(localadr));\n\t\t\t\tlocaladr.type = srv->addr.type;\n\t\t\t\tlocaladr.prot = srv->addr.prot;\n\t\t\t\tsrv->con = FTENET_Datagram_EstablishConnection(collection, srv->realm, localadr, NULL);\n\t\t\t}\n\t\t\tif (!srv->con)\n\t\t\t{\n\t\t\t\tsrv->addr.type = NA_INVALID; //fail it.\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tsrv->con->connum = 1+MAX_CONNECTIONS+countof(con->server)+(srv-con->server);\t//*sigh*\n\t\t}\n\n\t\tif (srv->state==TURN_TERMINATING)\n\t\t{\n\t\t\tif (net_ice_debug.ival >= 2)\n\t\t\t\tCon_Printf(S_COLOR_GRAY\"[%s]: TURN: Terminating %s\\n\", con->friendlyname, NET_AdrToString(data, sizeof(data), &srv->addr));\n\t\t\tMSG_WriteShort(&buf, BigShort(STUN_REFRESH));\n\t\t}\n\t\telse if (srv->state==TURN_ALLOCATED)\n\t\t{\n\t\t\tif (net_ice_debug.ival >= 2)\n\t\t\t\tCon_Printf(S_COLOR_GRAY\"[%s]: TURN: Refreshing %s\\n\", con->friendlyname, NET_AdrToString(data, sizeof(data), &srv->addr));\n\t\t\tMSG_WriteShort(&buf, BigShort(STUN_REFRESH));\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (net_ice_debug.ival >= 2)\n\t\t\t\tCon_Printf(S_COLOR_GRAY \"[%s]: TURN: Allocating %s\\n\", con->friendlyname, NET_AdrToString(data, sizeof(data), &srv->addr));\n\t\t\tMSG_WriteShort(&buf, BigShort(STUN_ALLOCATE));\n\t\t}\n\t}\n\tSys_RandomBytes((char*)srv->stunrnd, sizeof(srv->stunrnd));\n\n\tMSG_WriteShort(&buf, 0);\t//fill in later\n\tMSG_WriteLong(&buf, BigLong(STUN_MAGIC_COOKIE));\n\tMSG_WriteLong(&buf, srv->stunrnd[0]);\t//randomid\n\tMSG_WriteLong(&buf, srv->stunrnd[1]);\t//randomid\n\tMSG_WriteLong(&buf, srv->stunrnd[2]);\t//randomid\n\n\tif (!srv->isstun)\n\t{\n\t\tif (srv->state<TURN_ALLOCATED)\n\t\t{\n\t\t\tMSG_WriteShort(&buf, BigShort(STUNATTR_REQUESTED_TRANSPORT));\n\t\t\tMSG_WriteShort(&buf, BigShort(4));\n\t\t\tMSG_WriteLong(&buf, 17/*udp*/);\n\n\t\t\tswitch (srv->family)\n\t\t\t{\n\t\t\tcase NA_IP:\n\t\t\t\t//MSG_WriteShort(&buf, BigShort(STUNATTR_REQUESTED_ADDRFAM));\n\t\t\t\t//MSG_WriteShort(&buf, BigShort(4));\n\t\t\t\t//MSG_WriteLong(&buf, 1/*ipv4*/);\n\t\t\t\tbreak;\n\t\t\tcase NA_IPV6:\n\t\t\t\tMSG_WriteShort(&buf, BigShort(STUNATTR_REQUESTED_ADDRFAM));\n\t\t\t\tMSG_WriteShort(&buf, BigShort(4));\n\t\t\t\tMSG_WriteLong(&buf, 2/*ipv6*/);\n\t\t\t\tbreak;\n\t\t\tcase NA_INVALID:\t//ask for both ipv4+ipv6.\n\t\t\t\tMSG_WriteShort(&buf, BigShort(STUNATTR_ADDITIONAL_ADDRFAM));\n\t\t\t\tMSG_WriteShort(&buf, BigShort(4));\n\t\t\t\tMSG_WriteLong(&buf, 2/*ipv6*/);\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\treturn;\t//nope... not valid.\n\t\t\t}\n\t\t}\n\n//\t\tMSG_WriteShort(&buf, BigShort(STUNATTR_DONT_FRAGMENT));\n//\t\tMSG_WriteShort(&buf, BigShort(0));\n\n/*\t\tMSG_WriteShort(&buf, BigShort(STUNATTR_SOFTWARE));\n\t\tcrc = strlen(FULLENGINENAME);\n\t\tMSG_WriteShort(&buf, BigShort(crc));\n\t\tSZ_Write (&buf, FULLENGINENAME, crc);\n\t\tif (crc&3)\n\t\t\tSZ_Write (&buf, \"\\0\\0\\0\\0\", 4-(crc&3));\n*/\n\n\t\tif (srv->state==TURN_TERMINATING)\n\t\t{\n\t\t\tMSG_WriteShort(&buf, BigShort(STUNATTR_LIFETIME));\n\t\t\tMSG_WriteShort(&buf, BigShort(4));\n\t\t\tMSG_WriteLong(&buf, 0);\n\t\t}\n\t\telse\n\t\t{\n//\t\t\tMSG_WriteShort(&buf, BigShort(STUNATTR_LIFETIME));\n//\t\t\tMSG_WriteShort(&buf, BigShort(4));\n//\t\t\tMSG_WriteLong(&buf, BigLong(300));\n\n\t\t\tif (srv->state != TURN_UNINITED)\n\t\t\t{\n\t\t\t\tif (!TURN_AddAuth(&buf, srv))\n\t\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n\telse\n\t{\n\t\tdata[2] = ((buf.cursize+8-20)>>8)&0xff;\t//dummy length\n\t\tdata[3] = ((buf.cursize+8-20)>>0)&0xff;\n\t\tcrc = crc32(0, data, buf.cursize)^0x5354554e;\n\t\tMSG_WriteShort(&buf, BigShort(STUNATTR_FINGERPRINT));\n\t\tMSG_WriteShort(&buf, BigShort(sizeof(crc)));\n\t\tMSG_WriteLong(&buf, BigLong(crc));\n\t}\n\n\t//fill in the length (for the final time, after filling in the integrity and fingerprint)\n\tdata[2] = ((buf.cursize-20)>>8)&0xff;\n\tdata[3] = ((buf.cursize-20)>>0)&0xff;\n\n\tif (srv->isstun)\n\t\terr = NET_SendPacket(collection, buf.cursize, data, &srv->addr);\n\telse if (srv->con)\n\t\terr = srv->con->SendPacket(srv->con, buf.cursize, data, &srv->addr);\n\telse\n\t\treturn;\n\tif (err == NETERR_CLOGGED)\n\t\tsrv->stunretry = Sys_Milliseconds();\t//just keep retrying until it actually goes through.\n}\n\nstatic void TURN_AuthorisePeer(struct icestate_s *con, struct iceserver_s *srv, int peer)\n{\n\tstruct icecandidate_s *rc = srv->peer[peer].rc;\n\tsizebuf_t buf;\n\tnetadr_t to2;\n\tchar data[512];\n\tif (srv->state != TURN_ALLOCATED)\n\t\treturn;\n\tmemset(&buf, 0, sizeof(buf));\n\tbuf.maxsize = sizeof(data);\n\tbuf.cursize = 0;\n\tbuf.data = data;\n\n\tMSG_WriteShort(&buf, BigShort(STUN_CREATEPERM));\n\tMSG_WriteShort(&buf, 0);\t//fill in later\n\tMSG_WriteLong(&buf, BigLong(STUN_MAGIC_COOKIE));\t\t//magic\n\tMSG_WriteLong(&buf, srv->peer[peer].stunrnd[0]);\t//randomid\n\tMSG_WriteLong(&buf, srv->peer[peer].stunrnd[1]);\t//randomid\n\tMSG_WriteLong(&buf, srv->peer[peer].stunrnd[2]);\t//randomid\n\n\tif (!TURN_AddXorAddressAttrib(&buf, STUNATTR_XOR_PEER_ADDRESS, &rc->peer))\n\t\treturn;\n\tif (*rc->info.reladdr && strcmp(rc->info.addr, rc->info.reladdr))\t//allow the relay to bypass the peer's relay if its different (TURN doesn't care about port permissions).\n\t\tif (NET_StringToAdr(rc->info.reladdr, rc->info.relport, &to2))\n\t\t\tTURN_AddXorAddressAttrib(&buf, STUNATTR_XOR_PEER_ADDRESS, &to2);\n\tif (!TURN_AddAuth(&buf, srv))\n\t\treturn;\n\n\tbuf.data[2] = ((buf.cursize-20)>>8)&0xff;\n\tbuf.data[3] = ((buf.cursize-20)>>0)&0xff;\n\tsrv->con->SendPacket(srv->con, buf.cursize, buf.data, &srv->addr);\n\n\tif (net_ice_debug.ival >= 1)\n\t\tCon_Printf(S_COLOR_GRAY\"[%s]: (re)registering %s -> %s:%i (%s)\\n\", con->friendlyname, srv->realm, rc->info.addr, rc->info.port, ICE_GetCandidateType(&rc->info));\n}\n\nstatic void QDECL ICE_AddRCandidateInfo(struct icestate_s *con, struct icecandinfo_s *n);\n\nstatic struct mdns_peer_s\n{\n\tdouble nextretry;\n\tint tries;\t//stop sending after 4, forget a couple seconds after that.\n\n\tstruct icestate_s *con;\n\tstruct icecandinfo_s can;\n\n\tstruct mdns_peer_s *next;\n} *mdns_peers;\nstatic SOCKET mdns_socket = INVALID_SOCKET;\nstatic char mdns_name[2][43];\t//client, server (so we reply with the right set of sockets... make per-ice?)\n\nstruct dnshdr_s\n{\n\tunsigned short tid, flags, questions, answerrr, authrr, addrr;\n};\nstatic qbyte *MDNS_ReadCName(qbyte *in, qbyte *end, char *out, char *outend)\n{\n\tchar *cname = out;\n\twhile (*in && in < end)\n\t{\n\t\tif (cname != out)\n\t\t\t*cname++ = '.';\n\t\tif (cname+1+*in > outend)\n\t\t\treturn end;\t//if it overflows then its an error.\n\t\tmemcpy(cname, in+1, *in);\n\t\tcname += *in;\n\t\tin += 1+*in;\n\t}\n\t*cname = 0;\n\treturn ++in;\n}\nstatic void MDNS_ProcessPacket(qbyte *inmsg, size_t inmsgsize, netadr_t *source)\n{\n\tstruct dnshdr_s *rh = (struct dnshdr_s *)inmsg;\n\tunsigned short flags;\n\tqbyte *in, *end;\n\tstruct {\n\t\tunsigned short idx, count;\n\t\tchar name[256];\n\t\tunsigned short type;\n\t\tunsigned short class;\n\t\tunsigned int ttl;\n\t\tqbyte *data;\n\t\tunsigned short datasize;\n\t} a;\n\tstruct mdns_peer_s *peer;\n\n\tend = inmsg + inmsgsize;\n\n\t//ignore packets from outside the lan...\n\tif (NET_ClassifyAddress(source, NULL) > ASCOPE_LAN)\n\t\treturn;\n\tif (source->port != htons(5353))\n\t\treturn;\t//don't answer/read anything unless its actually mdns. there's supposed to be legacy stuff, but browsers don't seem to respond to that either so lets just play safe.\n\n\tif (inmsgsize < sizeof(*rh))\n\t\treturn;\t//some kind of truncation...\n\n\tflags = ntohs(rh->flags);\n\tif (flags & 0x780f)\n\t\treturn;\t//opcode must be 0, response must be 0\n\n\tin = (qbyte*)(rh+1);\n\n\tif (rh->questions)\n\t{\n\t\ta.count = ntohs(rh->questions);\n\t\tfor (a.idx = 0; a.idx < a.count; a.idx++)\n\t\t{\n\t\t\tftenet_connections_t *collection = NULL;\n\n\t\t\tqbyte *questionstart = in;\n\t\t\tin = MDNS_ReadCName(in, end, a.name, a.name+sizeof(a.name));\n\t\t\tif (in+4 > end)\n\t\t\t\treturn;\t//error...\n\t\t\ta.type = *in++<<8;\n\t\t\ta.type |= *in++<<0;\n\t\t\ta.class = *in++<<8;\n\t\t\ta.class |= *in++<<0;\n\n#ifdef HAVE_CLIENT\n\t\t\tif (*mdns_name[0] && !strcmp(a.name, mdns_name[0]))\n\t\t\t\tcollection = cls.sockets;\n#endif\n#ifdef HAVE_SERVER\n\t\t\tif (*mdns_name[1] && !strcmp(a.name, mdns_name[1]))\n\t\t\t\tcollection = svs.sockets;\n#endif\n\t\t\tif (collection && (a.type == 1/*A*/ || a.type == 28/*AAAA*/) && a.class == 1/*IN*/)\n\t\t\t{\n\t\t\t\tqbyte resp[512], *o = resp;\n\t\t\t\tint n,m, found=0, sz,ty;\n\t\t\t\tnetadr_t\taddr[16];\n\t\t\t\tstruct ftenet_generic_connection_s\t\t\t*gcon[sizeof(addr)/sizeof(addr[0])];\n\t\t\t\tunsigned int\t\t\tflags[sizeof(addr)/sizeof(addr[0])];\n\t\t\t\tconst char *params[sizeof(addr)/sizeof(addr[0])];\n\t\t\t\tstruct sockaddr_qstorage dest;\n\t\t\t\tconst unsigned int ttl = 120;\n\n\t\t\t\tm = NET_EnumerateAddresses(collection, gcon, flags, addr, params, sizeof(addr)/sizeof(addr[0]));\n\t\t\t\t*o++ = 0;*o++ = 0;\t//tid - must be 0 for mdns responses.\n\t\t\t\t*o++=0x84;*o++= 0;\t//flags\n\t\t\t\t*o++ = 0;*o++ = 0;\t//questions\n\t\t\t\t*o++ = 0;*o++ = 0;\t//answers\n\t\t\t\t*o++ = 0;*o++ = 0;\t//auths\n\t\t\t\t*o++ = 0;*o++ = 0;\t//additionals\n\t\t\t\tfor (n = 0; n < m; n++)\n\t\t\t\t{\n\t\t\t\t\tif (NET_ClassifyAddress(&addr[n], NULL) == ASCOPE_LAN)\n\t\t\t\t\t{\t//just copy a load of crap over\n\t\t\t\t\t\tif (addr[n].type == NA_IP)\n\t\t\t\t\t\t\tsz = 4, ty=1;/*A*/\n\t\t\t\t\t\telse if (addr[n].type == NA_IPV6)\n\t\t\t\t\t\t\tsz = 16, ty=28;/*AAAA*/\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tcontinue;\t//nope.\n\t\t\t\t\t\tif (ty != a.type)\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\ta.class |= 0x8000;\n\n\t\t\t\t\t\tif (o+(in-questionstart)+6+sz > resp+sizeof(resp))\n\t\t\t\t\t\t\tbreak;\t//won't fit.\n\n\t\t\t\t\t\tmemcpy(o, questionstart, in-questionstart-4);\n\t\t\t\t\t\to += in-questionstart-4;\n\t\t\t\t\t\t*o++ = ty>>8; *o++ = ty;\n\t\t\t\t\t\t*o++ = a.class>>8; *o++ = a.class;\n\t\t\t\t\t\t*o++ = ttl>>24; *o++ = ttl>>16; *o++ = ttl>>8; *o++ = ttl>>0;\n\t\t\t\t\t\t*o++ = sz>>8; *o++ = sz;\n\t\t\t\t\t\tmemcpy(o, &addr[n].address, sz);\n\t\t\t\t\t\to+=sz;\n\n\t\t\t\t\t\tfound++;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tresp[6] = found>>8; resp[7] = found&0xff;\t//replace the answer count now that we actually know\n\n\t\t\t\tif (!found)\t//don't bother if we can't actually answer it.\n\t\t\t\t\tcontinue;\n\n\t\t\t\t//send a multicast response... (browsers don't seem to respond to unicasts).\n\t\t\t\tif (a.type & 0x8000)\n\t\t\t\t{\t//they asked for a unicast response.\n\t\t\t\t\tresp[0] = inmsg[0]; resp[1] = inmsg[1];\t//redo the tid.\n\t\t\t\t\tsz = NetadrToSockadr(source, &dest);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tsz = sizeof(struct sockaddr_in);\n\t\t\t\t\tmemset(&dest, 0, sz);\n\t\t\t\t\t((struct sockaddr_in*)&dest)->sin_family = AF_INET;\n\t\t\t\t\t((struct sockaddr_in*)&dest)->sin_port = htons(5353);\n\t\t\t\t\t((struct sockaddr_in*)&dest)->sin_addr.s_addr = inet_addr(\"224.0.0.251\");\n\t\t\t\t}\n\t\t\t\tsendto(mdns_socket, resp, o-resp, 0, (struct sockaddr*)&dest, sz);\n\t\t\t\tif (net_ice_debug.ival)\n\t\t\t\t\tCon_Printf(S_COLOR_GRAY\"%s: Answering mdns (%s)\\n\", NET_AdrToString(resp, sizeof(resp), source), a.name);\n\t\t\t}\n\t\t}\n\t}\n\n\ta.count = ntohs(rh->answerrr);\n\tfor (a.idx = 0; a.idx < a.count; a.idx++)\n\t{\n\t\tin = MDNS_ReadCName(in, end, a.name, a.name+sizeof(a.name));\n\t\tif (in+10 > end)\n\t\t\treturn;\t//error...\n\t\ta.type = *in++<<8;\n\t\ta.type |= *in++<<0;\n\t\ta.class = *in++<<8;\n\t\ta.class |= *in++<<0;\n\t\ta.ttl = *in++<<24;\n\t\ta.ttl |= *in++<<16;\n\t\ta.ttl |= *in++<<8;\n\t\ta.ttl |= *in++<<0;\n\t\ta.datasize = *in++<<8;\n\t\ta.datasize |= *in++<<0;\n\t\ta.data = in;\n\t\tin += a.datasize;\n\t\tif (in > end)\n\t\t\treturn;\n\n\t\tfor (peer = mdns_peers; peer; peer = peer->next)\n\t\t{\n\t\t\tif (!strcmp(a.name, peer->can.addr))\n\t\t\t{\t//this is the record we were looking for. yay.\n\n\t\t\t\tstruct icestate_s *ice;\n\t\t\t\tfor\t(ice = icelist; ice; ice = ice->next)\n\t\t\t\t\tif (ice == peer->con)\n\t\t\t\t\t\tbreak;\n\t\t\t\tif (!ice)\n\t\t\t\t\tbreak;\t//no longer valid...\n\n\t\t\t\tif ((a.type&0x7fff) == 1/*A*/ && (a.class&0x7fff) == 1/*IN*/ && a.datasize == 4)\n\t\t\t\t{\t//we got a proper ipv4 address. yay.\n\t\t\t\t\tQ_snprintfz(peer->can.addr, sizeof(peer->can.addr), \"%i.%i.%i.%i\", a.data[0], a.data[1], a.data[2], a.data[3]);\n\t\t\t\t}\n\t\t\t\telse if ((a.type&0x7fff) == 28/*AAAA*/ && (a.class&0x7fff) == 1/*IN*/ && a.datasize == 16)\n\t\t\t\t{\t//we got a proper ipv4 address. yay.\n\t\t\t\t\tQ_snprintfz(peer->can.addr, sizeof(peer->can.addr), \"%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\",\n\t\t\t\t\t\t\t(a.data[ 0]<<8)|a.data[ 1],\n\t\t\t\t\t\t\t(a.data[ 2]<<8)|a.data[ 3],\n\t\t\t\t\t\t\t(a.data[ 4]<<8)|a.data[ 5],\n\t\t\t\t\t\t\t(a.data[ 6]<<8)|a.data[ 7],\n\t\t\t\t\t\t\t(a.data[ 8]<<8)|a.data[ 9],\n\t\t\t\t\t\t\t(a.data[10]<<8)|a.data[11],\n\t\t\t\t\t\t\t(a.data[12]<<8)|a.data[13],\n\t\t\t\t\t\t\t(a.data[14]<<8)|a.data[15]);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tCon_Printf(\"Useless answer\\n\");\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tif (net_ice_debug.ival)\n\t\t\t\t\tCon_Printf(S_COLOR_GRAY\"[%s]: Resolved %s to %s\\n\", peer->con->friendlyname, a.name, peer->can.addr);\n\t\t\t\tif (peer->tries != 4)\n\t\t\t\t{\t//first response?...\n\t\t\t\t\tpeer->tries = 4;\n\t\t\t\t\tpeer->nextretry = Sys_DoubleTime()+0.5;\n\t\t\t\t}\n\t\t\t\tICE_AddRCandidateInfo(peer->con, &peer->can);\t//restore it, so we can handle alternatives properly.\n\t\t\t\tQ_strncpyz(peer->can.addr, a.name, sizeof(peer->can.addr));\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n}\nstatic void MDNS_ReadPackets(void)\n{\n\tqbyte inmsg[9000];\n\tint inmsgsize;\n\tnetadr_t adr;\n\tstruct sockaddr_qstorage source;\n\n\tfor(;;)\n\t{\n\t\tint slen = sizeof(source);\n\t\tinmsgsize = recvfrom(mdns_socket, inmsg, sizeof(inmsg), 0, (struct sockaddr*)&source, &slen);\n\t\tif (inmsgsize <= 0)\n\t\t\tbreak;\n\t\tSockadrToNetadr(&source, slen, &adr);\n\t\tMDNS_ProcessPacket(inmsg, inmsgsize, &adr);\n\t}\n}\n\nstatic void MDNS_Shutdown(void)\n{\n\tif (mdns_socket == INVALID_SOCKET)\n\t\treturn;\n\tclosesocket(mdns_socket);\n\tmdns_socket = INVALID_SOCKET;\n}\n\nstatic void MDNS_GenChars(char *d, size_t len, qbyte *s)\n{\t//big endian hex, big endian data, can just do it by bytes.\n\tstatic char tohex[16] = \"0123456789abcdef\";\n\tfor (; len--; s++)\n\t{\n\t\t*d++ = tohex[*s>>4];\n\t\t*d++ = tohex[*s&15];\n\t}\n}\nstatic void MDNS_Generate(char name[43])\n{\t//generate a suitable mdns name.\n\tunsigned char uuid[16];\n\tSys_RandomBytes((char*)uuid, sizeof(uuid));\n\n\tuuid[8]&=~(1<<6);\t//clear clk_seq_hi_res bit 6\n\tuuid[8]|=(1<<7);\t//set clk_seq_hi_res bit 7\n\n\tuuid[6] &= ~0xf0;\t//clear time_hi_and_version's high 4 bits\n\tuuid[6] |= 0x40;\t//replace with version\n\n\tMDNS_GenChars(name+0, 4, uuid+0);\n\tname[8] = '-';\n\tMDNS_GenChars(name+9, 2, uuid+4);\n\tname[13] = '-';\n\tMDNS_GenChars(name+14, 2, uuid+6);\n\tname[18] = '-';\n\tMDNS_GenChars(name+19, 2, uuid+8);\n\tname[23] = '-';\n\tMDNS_GenChars(name+24, 6, uuid+10);\n\tstrcpy(name+36, \".local\");\n}\n\nstatic qboolean MDNS_Setup(void)\n{\n\tstruct sockaddr_in adr;\n\tint _true = true;\n//\tint _false = false;\n\tstruct ip_mreq mbrship;\n\tqboolean success = true;\n\n\tif (mdns_socket != INVALID_SOCKET)\n\t\treturn true;\t//already got one\n\n\tmemset(&adr, 0, sizeof(adr));\n\tadr.sin_family = AF_INET;\n\tadr.sin_port = htons(5353);\n\tadr.sin_addr.s_addr = INADDR_ANY;\n\n\tmemset(&mbrship, 0, sizeof(mbrship));\n\tmbrship.imr_multiaddr.s_addr = inet_addr(\"224.0.0.251\");\n\n\t//browsers don't seem to let us take the easy route, so we can't just use our existing helpers.\n\tmdns_socket = socket (PF_INET, SOCK_CLOEXEC|SOCK_DGRAM, IPPROTO_UDP);\n\tif (mdns_socket == INVALID_SOCKET) success = false;\n\tif (!success || 0 > ioctlsocket(mdns_socket, FIONBIO, (void*)&_true)) success = false;\n\t//other processes on the same host may be listening for mdns packets to insert their own responses (eg two browsers), so we need to ensure that other processes can use the same port. there's no real security here for us (that comes from stun's user/pass stuff).\n\tif (!success || 0 > setsockopt (mdns_socket, SOL_SOCKET, SO_REUSEADDR, (const void*)&_true, sizeof(_true))) success = false;\n#ifdef SO_REUSEPORT\t//not on windows.\n\tif (!success || 0 > setsockopt (mdns_socket, SOL_SOCKET, SO_REUSEPORT, (const void*)&_true, sizeof(_true))) success = false;\n#endif\n#if IP_MULTICAST_LOOP\t//ideally we'd prefer to not receive our own requests, but this is host-level, not socket-level, so unusable for communication with browsers on the same machine\n//\tif (success)\t\tsetsockopt (mdns_socket, IPPROTO_IP, IP_MULTICAST_LOOP, &_false, sizeof(_false));\n#endif\n\tif (!success || 0 > setsockopt (mdns_socket, IPPROTO_IP, IP_ADD_MEMBERSHIP, (const void*)&mbrship, sizeof(mbrship))) success = false;\n\n\tadr.sin_addr.s_addr = INADDR_ANY;\n\tif (!success || bind (mdns_socket, (void *)&adr, sizeof(adr)) < 0)\n\t\tsuccess = false;\n\tif (!success)\n\t{\n\t\tMDNS_Shutdown();\n\t\tCon_Printf(\"mdns setup failed\\n\");\n\t}\n\telse\n\t\tMDNS_Generate(mdns_name[0]), MDNS_Generate(mdns_name[1]);\n\treturn success;\n}\n\nstatic void MDNS_SendQuery(struct mdns_peer_s *peer)\n{\n\tchar *n = peer->can.addr, *dot;\n\tstruct sockaddr_in dest;\n\tqbyte outmsg[1024], *o = outmsg;\n\n\tmemset(&dest, 0, sizeof(dest));\n\tdest.sin_family = AF_INET;\n\tdest.sin_port = htons(5353);\n\tdest.sin_addr.s_addr = inet_addr(\"224.0.0.251\");\n\n\t*o++ = 0;*o++ = 0;\t//tid\n\t*o++ = 0;*o++ = 0;\t//flags\n\t*o++ = 0;*o++ = 1;\t//questions\n\t*o++ = 0;*o++ = 0;\t//answers\n\t*o++ = 0;*o++ = 0;\t//auths\n\t*o++ = 0;*o++ = 0;\t//additionals\n\n\t//mdns is strictly utf-8. no punycode needed.\n\tfor(;;)\n\t{\n\t\tdot = strchr(n, '.');\n\t\tif (!dot)\n\t\t\tdot = n + strlen(n);\n\t\tif (dot == n)\n\t\t\treturn; //err... can't write a 0-length label.\n\t\t*o++ = dot-n;\n\t\tmemcpy(o, n, dot-n); o += dot-n; n += dot-n;\n\t\tif (!*n)\n\t\t\tbreak;\n\t\tn++;\n\t}\n\t*o++ = 0;\n\n\t*o++ = 0; *o++ = 1; //type: 'A' record\n\t*o++ = 0; *o++ = 1; //class: 'IN'\n\n\tsendto(mdns_socket, outmsg, o-outmsg, 0, (struct sockaddr*)&dest, sizeof(dest));\n\tpeer->tries++;\n\tpeer->nextretry = Sys_DoubleTime() + (50/1000.0);\n}\nstatic void MDNS_SendQueries(void)\n{\n\tdouble time;\n\tstruct mdns_peer_s *peer, **link;\n\tif (mdns_socket == INVALID_SOCKET)\n\t\treturn;\n\tMDNS_ReadPackets();\n\tif (!mdns_peers)\n\t\treturn;\n\ttime = Sys_DoubleTime();\n\n\tfor (link = &mdns_peers; (peer=*link); )\n\t{\n\t\tif (peer->nextretry < time)\n\t\t{\n\t\t\tif (peer->tries == 4)\n\t\t\t{\t//bye bye.\n\t\t\t\t*link = peer->next;\n\t\t\t\tBZ_Free(peer);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tMDNS_SendQuery(peer);\n\n\t\t\tif (peer->tries == 4)\n\t\t\t\tpeer->nextretry = Sys_DoubleTime() + 2.0;\n\t\t\tbreak;\t//don't spam multiple each frame.\n\t\t}\n\t\tlink = &peer->next;\n\t}\n}\nstatic void MDNS_AddQuery(struct icestate_s *con, struct icecandinfo_s *can)\n{\n\tstruct mdns_peer_s *peer;\n\tif (!MDNS_Setup())\n\t\treturn;\n\tpeer = Z_Malloc(sizeof(*peer));\n\tpeer->con = con;\n\tpeer->can = *can;\n\tpeer->next = mdns_peers;\n\tpeer->tries = 0;\n\tpeer->nextretry = Sys_DoubleTime();\n\tmdns_peers = peer;\n\tMDNS_SendQuery(peer);\n}\n\nstatic qboolean MDNS_CharsAreHex(char *s, size_t len)\n{\n\tfor (; len--; s++)\n\t{\n\t\tif (*s >= '0' && *s <= '9')\n\t\t\t;\n\t\telse if (*s >= 'a' && *s <= 'f')\n\t\t\t;\n\t\telse\n\t\t\treturn false;\n\t}\n\treturn true;\n}\n\nint ParsePartialIP(const char *s, netadr_t *a);\nstatic void QDECL ICE_AddRCandidateInfo(struct icestate_s *con, struct icecandinfo_s *n)\n{\n\tstruct icecandidate_s *o;\n\tqboolean isnew;\n\tnetadr_t peer;\n\tint peerbits;\n\t//I don't give a damn about rtcp.\n\tif (n->component != 1)\n\t\treturn;\n\tif (n->transport != 0)\n\t\treturn;\t//only UDP is supported.\n\n\t//check if its an mDNS name - must be a UUID, with a .local on the end.\n\tif (net_ice_allowmdns.ival &&\n\t\tMDNS_CharsAreHex(n->addr, 8) && n->addr[8]=='-' &&\n\t\tMDNS_CharsAreHex(n->addr+9, 4) && n->addr[13]=='-' &&\n\t\tMDNS_CharsAreHex(n->addr+14, 4) && n->addr[18]=='-' &&\n\t\tMDNS_CharsAreHex(n->addr+19, 4) && n->addr[23]=='-' &&\n\t\tMDNS_CharsAreHex(n->addr+24, 12) && !strcmp(&n->addr[36], \".local\"))\n\t{\n\t\tMDNS_AddQuery(con, n);\n\t\treturn;\n\t}\n\n\t//don't use the regular string->addr, they can fail and stall and make us unresponsive etc. hostnames don't really make sense here anyway.\n\tpeerbits = ParsePartialIP(n->addr, &peer);\n\tpeer.prot = NP_DGRAM;\n\tpeer.port = htons(n->port);\n\tif (peer.type == NA_IP && peerbits == 32)\n\t{\n\t\t//ignore invalid addresses\n\t\tif (!peer.address.ip[0] && !peer.address.ip[1] && !peer.address.ip[2] && !peer.address.ip[3])\n\t\t\treturn;\n\t}\n\telse if (peer.type == NA_IPV6 && peerbits == 128)\n\t{\n\t\t//ignore invalid addresses\n\t\tint i;\n\t\tfor (i = 0; i < countof(peer.address.ip6); i++)\n\t\t\tif (peer.address.ip6[i])\n\t\t\t\tbreak;\n\t\tif (i == countof(peer.address.ip6))\n\t\t\treturn; //all clear. in6_addr_any\n\t}\n\telse\n\t\treturn;\t//bad address type, or partial.\n\n\tif (*n->candidateid)\n\t{\n\t\tfor (o = con->rc; o; o = o->next)\n\t\t{\n\t\t\t//not sure that updating candidates is particuarly useful tbh, but hey.\n\t\t\tif (!strcmp(o->info.candidateid, n->candidateid))\n\t\t\t\tbreak;\n\t\t}\n\t}\n\telse\n\t{\n\t\tfor (o = con->rc; o; o = o->next)\n\t\t{\n\t\t\t//avoid dupes.\n\t\t\tif (!strcmp(o->info.addr, n->addr) && o->info.port == n->port)\n\t\t\t\tbreak;\n\t\t}\n\t}\n\tif (!o)\n\t{\n\t\to = Z_Malloc(sizeof(*o));\n\t\to->next = con->rc;\n\t\tcon->rc = o;\n\t\tQ_strncpyz(o->info.candidateid, n->candidateid, sizeof(o->info.candidateid));\n\n\t\tisnew = true;\n\t}\n\telse\n\t{\n\t\tisnew = false;\n\t}\n\tQ_strncpyz(o->info.addr, n->addr, sizeof(o->info.addr));\n\to->info.port = n->port;\n\to->info.type = n->type;\n\to->info.priority = n->priority;\n\to->info.network = n->network;\n\to->info.generation = n->generation;\n\to->info.foundation = n->foundation;\n\to->info.component = n->component;\n\to->info.transport = n->transport;\n\to->dirty = true;\n\to->peer = peer;\n\to->tried = 0;\n\to->reachable = 0;\n\n\tif (net_ice_debug.ival >= 1)\n\t\tCon_Printf(S_COLOR_GRAY\"[%s]: %s remote candidate %s: [%s]:%i\\n\", con->friendlyname, isnew?\"Added\":\"Updated\", o->info.candidateid, o->info.addr, o->info.port);\n\n\tif (n->type == ICE_RELAY && *n->reladdr && (strcmp(n->addr, n->reladdr) || n->port != n->relport))\n\t{\t//for relay candidates, add an srflx candidate too.\n\t\tstruct icecandinfo_s t = o->info;\n\t\tt.type = ICE_SRFLX;\n\t\tstrcpy(t.addr, n->reladdr);\n\t\tt.port = n->relport;\n\t\t*t.reladdr = 0;\n\t\tt.relport = 0;\n\t\tt.priority |= 1<<24;\t//nudge its priority up slightly to favour more direct links when we can.\n\t\t*t.candidateid = 0;\t\t//anonymous...\n\t\tICE_AddRCandidateInfo(con, &t);\n\t}\n}\n\nstatic qboolean QDECL ICE_Set(struct icestate_s *con, const char *prop, const char *value);\nstatic void ICE_ParseSDPLine(struct icestate_s *con, const char *value)\n{\n\tif      (!strncmp(value, \"a=ice-pwd:\", 10))\n\t\tICE_Set(con, \"rpwd\", value+10);\n\telse if (!strncmp(value, \"a=ice-ufrag:\", 12))\n\t\tICE_Set(con, \"rufrag\", value+12);\n#ifdef HAVE_DTLS\n\telse if (!strncmp(value, \"a=setup:\", 8))\n\t{\t//this is their state, so we want the opposite.\n\t\tif (!strncmp(value+8, \"passive\", 7))\n\t\t\tcon->dtlspassive = false;\n\t\telse if (!strncmp(value+8, \"active\", 6))\n\t\t\tcon->dtlspassive = true;\n\t}\n\telse if (!strncmp(value, \"a=fingerprint:\", 14))\n\t{\n\t\thashfunc_t *hash = NULL;\n\t\tint i;\n\t\tchar name[64];\n\t\tvalue = COM_ParseOut(value+14, name, sizeof(name));\n\t\tfor (i = 0; i < countof(webrtc_hashes); i++)\n\t\t{\n\t\t\tif (!strcasecmp(name, webrtc_hashes[i].name))\n\t\t\t{\n\t\t\t\thash = webrtc_hashes[i].hash;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (hash && (!con->cred.peer.hash || hash->digestsize>con->cred.peer.hash->digestsize))\t//FIXME: digest size is not a good indicator of whether its exploitable or not, but should work for sha1/sha2 options. the sender here is expected to be trustworthy anyway.\n\t\t{\n\t\t\tint b, o, v;\n\t\t\twhile (*value == ' ')\n\t\t\t\tvalue++;\n\t\t\tfor (b = 0; b < hash->digestsize; )\n\t\t\t{\n\t\t\t\tv = *value;\n\t\t\t\tif      (v >= '0' && v <= '9')\n\t\t\t\t\to = (v-'0');\n\t\t\t\telse if (v >= 'A' && v <= 'F')\n\t\t\t\t\to = (v-'A'+10);\n\t\t\t\telse if (v >= 'a' && v <= 'f')\n\t\t\t\t\to = (v-'a'+10);\n\t\t\t\telse\n\t\t\t\t\tbreak;\n\t\t\t\to <<= 4;\n\t\t\t\tv = *++value;\n\t\t\t\tif      (v >= '0' && v <= '9')\n\t\t\t\t\to |= (v-'0');\n\t\t\t\telse if (v >= 'A' && v <= 'F')\n\t\t\t\t\to |= (v-'A'+10);\n\t\t\t\telse if (v >= 'a' && v <= 'f')\n\t\t\t\t\to |= (v-'a'+10);\n\t\t\t\telse\n\t\t\t\t\tbreak;\n\t\t\t\tcon->cred.peer.digest[b++] = o;\n\t\t\t\tv = *++value;\n\t\t\t\tif (v != ':')\n\t\t\t\t\tbreak;\n\t\t\t\tvalue++;\n\t\t\t}\n\t\t\tif (b == hash->digestsize)\n\t\t\t\tcon->cred.peer.hash = hash;\t//it was the right size, woo.\n\t\t\telse\n\t\t\t\tcon->cred.peer.hash = NULL; //bad! (should we 0-pad?)\n\t\t}\n\t}\n\telse if (!strncmp(value, \"a=sctp-port:\", 12))\n\t\tcon->peersctpport = atoi(value+12);\n\telse if (!strncmp(value, \"a=sctp-optional:\", 16))\n\t\tcon->peersctpoptional = atoi(value+16);\n#endif\n\telse if (!strncmp(value, \"a=rtpmap:\", 9))\n\t{\n\t\tchar name[64];\n\t\tint codec;\n\t\tchar *sl;\n\t\tvalue += 9;\n\t\tcodec = strtoul(value, (char**)&value, 0);\n\t\tif (*value == ' ') value++;\n\n\t\tCOM_ParseOut(value, name, sizeof(name));\n\t\tsl = strchr(name, '/');\n\t\tif (sl)\n\t\t\t*sl = '@';\n\t\tICE_Set(con, va(\"codec%i\", codec), name);\n\t}\n\telse if (!strncmp(value, \"a=candidate:\", 12))\n\t{\n\t\tstruct icecandinfo_s n;\n\t\tmemset(&n, 0, sizeof(n));\n\n\t\tvalue += 12;\n\t\tn.foundation = strtoul(value, (char**)&value, 0);\n\n\t\tif(*value == ' ')value++;\n\t\tn.component = strtoul(value, (char**)&value, 0);\n\n\t\tif(*value == ' ')value++;\n\t\tif (!strncasecmp(value, \"UDP \", 4))\n\t\t{\n\t\t\tn.transport = 0;\n\t\t\tvalue += 3;\n\t\t}\n\t\telse\n\t\t\treturn;\n\n\t\tif(*value == ' ')value++;\n\t\tn.priority = strtoul(value, (char**)&value, 0);\n\n\t\tif(*value == ' ')value++;\n\t\tvalue = COM_ParseOut(value, n.addr, sizeof(n.addr));\n\t\tif (!value) return;\n\n\t\tif(*value == ' ')value++;\n\t\tn.port = strtoul(value, (char**)&value, 0);\n\n\t\tif(*value == ' ')value++;\n\t\tif (strncmp(value, \"typ \", 4)) return;\n\t\tvalue += 3;\n\n\t\tif(*value == ' ')value++;\n\t\tif (!strncmp(value, \"host\", 4))\n\t\t\tn.type = ICE_HOST;\n\t\telse if (!strncmp(value, \"srflx\", 4))\n\t\t\tn.type = ICE_SRFLX;\n\t\telse if (!strncmp(value, \"prflx\", 4))\n\t\t\tn.type = ICE_PRFLX;\n\t\telse if (!strncmp(value, \"relay\", 4))\n\t\t\tn.type = ICE_RELAY;\n\t\telse\n\t\t\treturn;\n\n\t\twhile (*value)\n\t\t{\n\t\t\tif(*value == ' ')value++;\n\t\t\tif (!strncmp(value, \"raddr \", 6))\n\t\t\t{\n\t\t\t\tvalue += 6;\n\t\t\t\tvalue = COM_ParseOut(value, n.reladdr, sizeof(n.reladdr));\n\t\t\t\tif (!value)\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\telse if (!strncmp(value, \"rport \", 6))\n\t\t\t{\n\t\t\t\tvalue += 6;\n\t\t\t\tn.relport = strtoul(value, (char**)&value, 0);\n\t\t\t}\n\t\t\t/*else if (!strncmp(value, \"network-cost \", 13))\n\t\t\t{\n\t\t\t\tvalue += 13;\n\t\t\t\tn.netcost = strtoul(value, (char**)&value, 0);\n\t\t\t}*/\n\t\t\t/*else if (!strncmp(value, \"ufrag \", 6))\n\t\t\t{\n\t\t\t\tvalue += 6;\n\t\t\t\twhile (*value && *value != ' ')\n\t\t\t\t\tvalue++;\n\t\t\t}*/\n\t\t\telse\n\t\t\t{\n\t\t\t\t//this is meant to be extensible.\n\t\t\t\twhile (*value && *value != ' ')\n\t\t\t\t\tvalue++;\n\t\t\t\tif(*value == ' ')value++;\n\t\t\t\twhile (*value && *value != ' ')\n\t\t\t\t\tvalue++;\n\t\t\t}\n\t\t}\n\t\tICE_AddRCandidateInfo(con, &n);\n\t}\n}\n\nstatic qboolean QDECL ICE_Set(struct icestate_s *con, const char *prop, const char *value)\n{\n\tif (!strcmp(prop, \"state\"))\n\t{\n\t\tint oldstate = con->state;\n\t\tif (!strcmp(value, STRINGIFY(ICE_CONNECTING)))\n\t\t{\n\t\t\tcon->state = ICE_CONNECTING;\n\t\t\tif (net_ice_debug.ival >= 1)\n\t\t\t\tCon_Printf(S_COLOR_GRAY\"[%s]: ice state connecting\\n\", con->friendlyname);\n\t\t}\n\t\telse if (!strcmp(value, STRINGIFY(ICE_INACTIVE)))\n\t\t{\n\t\t\tcon->state = ICE_INACTIVE;\n\t\t\tif (net_ice_debug.ival >= 1)\n\t\t\t\tCon_Printf(S_COLOR_GRAY\"[%s]: ice state inactive\\n\", con->friendlyname);\n\t\t}\n\t\telse if (!strcmp(value, STRINGIFY(ICE_FAILED)))\n\t\t{\n\t\t\tif (con->state != ICE_FAILED)\n\t\t\t{\n\t\t\t\tcon->state = ICE_FAILED;\n\t\t\t\tif (net_ice_debug.ival >= 1)\n\t\t\t\t\tCon_Printf(S_COLOR_GRAY\"[%s]: ice state failed\\n\", con->friendlyname);\n\t\t\t}\n\t\t}\n\t\telse if (!strcmp(value, STRINGIFY(ICE_CONNECTED)))\n\t\t{\n\t\t\tcon->state = ICE_CONNECTED;\n\t\t\tif (net_ice_debug.ival >= 1)\n\t\t\t\tCon_Printf(S_COLOR_GRAY\"[%s]: ice state connected\\n\", con->friendlyname);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tCon_Printf(\"ICE_Set invalid state %s\\n\", value);\n\t\t\tcon->state = ICE_INACTIVE;\n\t\t}\n\t\tcon->icetimeout = Sys_Milliseconds() + 30*1000;\n\n\t\tcon->retries = 0;\n\n#ifndef SERVERONLY\n\t\tif (con->state == ICE_CONNECTING && (con->proto == ICEP_QWCLIENT || con->proto == ICEP_VOICE))\n\t\t\tif (!cls.sockets)\n\t\t\t\tNET_InitClient(false);\n#endif\n\n\t\tif (con->state >= ICE_CONNECTING)\n\t\t{\n#ifndef SERVERONLY\n\t\t\tif (con->proto == ICEP_QWCLIENT)\n\t\t\t\tCL_Transfer(&con->qadr);\t//okay, the client should be using this ice connection now. FIXME: this should only switch them over if they're still trying to use the aerlier broker.\n#endif\n#ifdef HAVE_DTLS\n\t\t\tif (con->mode == ICEM_WEBRTC)\n\t\t\t{\n\t\t\t\tif (!con->dtlsstate && con->dtlsfuncs)\n\t\t\t\t{\n\t\t\t\t\tif (con->cred.peer.hash)\n\t\t\t\t\t\tcon->dtlsstate = con->dtlsfuncs->CreateContext(&con->cred, con, ICE_Transmit, con->dtlspassive);\n\t\t\t\t\telse if (net_enable_dtls.ival >= 3)\n\t\t\t\t\t{\t//peer doesn't seem to support dtls.\n\t\t\t\t\t\tICE_SetFailed(con, \"peer does not support dtls. Set net_enable_dtls to 1 to make optional.\\n\");\n\t\t\t\t\t}\n\t\t\t\t\telse if (con->state == ICE_CONNECTING && net_enable_dtls.ival>=2)\n\t\t\t\t\t\tCon_Printf(CON_WARNING\"WARNING: [%s]: peer does not support dtls.\\n\", con->friendlyname);\n\t\t\t\t}\n\t\t\t\tif (!con->sctp && (!con->sctpoptional || !con->peersctpoptional) && con->mysctpport && con->peersctpport)\n\t\t\t\t{\n\t\t\t\t\tcon->sctp = Z_Malloc(sizeof(*con->sctp));\n\t\t\t\t\tcon->sctp->icestate = con;\n\t\t\t\t\tcon->sctp->friendlyname = con->friendlyname;\n\t\t\t\t\tcon->sctp->myport = htons(con->mysctpport);\n\t\t\t\t\tcon->sctp->peerport = htons(con->peersctpport);\n\t\t\t\t\tcon->sctp->o.tsn = rand() ^ (rand()<<16);\n\t\t\t\t\tSys_RandomBytes((void*)&con->sctp->o.verifycode, sizeof(con->sctp->o.verifycode));\n\t\t\t\t\tSys_RandomBytes((void*)&con->sctp->i.verifycode, sizeof(con->sctp->i.verifycode));\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (!con->dtlsstate && con->cred.peer.hash)\n\t\t\t{\n\t\t\t\tif (!con->peersctpoptional)\n\t\t\t\t\tICE_SetFailed(con, \"peer is trying to use dtls.%s\\n\", net_enable_dtls.ival?\"\":\" Set ^[/net_enable_dtls 1^].\");\n\t\t\t}\n#endif\n\t\t}\n\n\t\tif (oldstate != con->state && con->state == ICE_INACTIVE)\n\t\t{\t//forget our peer\n\t\t\tstruct icecandidate_s *c;\n\t\t\tint i;\n\t\t\tmemset(&con->chosenpeer, 0, sizeof(con->chosenpeer));\n\n#ifdef HAVE_DTLS\n\t\t\tif (con->sctp)\n\t\t\t{\n\t\t\t\tZ_Free(con->sctp->cookie);\n\t\t\t\tZ_Free(con->sctp);\n\t\t\t\tcon->sctp = NULL;\n\t\t\t}\n\t\t\tif (con->dtlsstate)\n\t\t\t{\n\t\t\t\tcon->dtlsfuncs->DestroyContext(con->dtlsstate);\n\t\t\t\tcon->dtlsstate = NULL;\n\t\t\t}\n#endif\n\t\t\twhile(con->rc)\n\t\t\t{\n\t\t\t\tc = con->rc;\n\t\t\t\tcon->rc = c->next;\n\t\t\t\tZ_Free(c);\n\t\t\t}\n\t\t\twhile(con->lc)\n\t\t\t{\n\t\t\t\tc = con->lc;\n\t\t\t\tcon->lc = c->next;\n\t\t\t\tZ_Free(c);\n\t\t\t}\n\t\t\tfor (i = 0; i < con->servers; i++)\n\t\t\t{\n\t\t\t\tstruct iceserver_s *s = &con->server[i];\n\t\t\t\tif (s->con)\n\t\t\t\t{\t//make sure we tell the TURN server to release our allocation.\n\t\t\t\t\ts->state = TURN_TERMINATING;\n\t\t\t\t\tICE_ToStunServer(con, s);\n\n\t\t\t\t\ts->con->Close(s->con);\n\t\t\t\t\ts->con = NULL;\n\t\t\t\t}\n\t\t\t\tZ_Free(s->nonce);\n\t\t\t\ts->nonce = NULL;\n\t\t\t\ts->peers = 0;\n\t\t\t}\n\t\t}\n\n\t\tif (oldstate != con->state && con->state == ICE_CONNECTED)\n\t\t{\n\t\t\tif (con->chosenpeer.type == NA_INVALID)\n\t\t\t\tICE_SetFailed(con, \"ICE failed. peer not valid.\\n\");\n#ifndef CLIENTONLY\n\t\t\telse if (con->proto == ICEP_QWSERVER && con->mode != ICEM_WEBRTC)\n\t\t\t{\n\t\t\t\tnet_from = con->qadr;\n\t\t\t\tSVC_GetChallenge(false);\n\t\t\t}\n#endif\n\t\t\tif (con->state == ICE_CONNECTED)\n\t\t\t{\n\t\t\t\tif (con->proto >= ICEP_VOICE || net_ice_debug.ival)\n\t\t\t\t{\n\t\t\t\t\tchar msg[256];\n\t\t\t\t\tCon_Printf(S_COLOR_GRAY \"[%s]: %s connection established (peer %s, via %s).\\n\", con->friendlyname, con->proto == ICEP_VOICE?\"voice\":\"data\", NET_AdrToString(msg, sizeof(msg), &con->chosenpeer), ICE_NetworkToName(con, con->chosenpeer.connum));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n#if !defined(SERVERONLY) && defined(VOICECHAT)\n\t\tsnd_voip_send.ival = (snd_voip_send.ival & ~4) | (NET_RTP_Active()?4:0);\n#endif\n\t}\n\telse if (!strcmp(prop, \"controlled\"))\n\t\tcon->controlled = !!atoi(value);\n\telse if (!strcmp(prop, \"controller\"))\n\t\tcon->controlled = !atoi(value);\n\telse if (!strncmp(prop, \"codec\", 5))\n\t{\n\t\tstruct icecodecslot_s *codec = ICE_GetCodecSlot(con, atoi(prop+5));\n\t\tif (!codec)\n\t\t\treturn false;\n\t\tcodec->id = atoi(prop+5);\n#if !defined(SERVERONLY) && defined(VOICECHAT)\n\t\tif (!S_Voip_RTP_CodecOkay(value))\n#endif\n\t\t{\n\t\t\tZ_Free(codec->name);\n\t\t\tcodec->name = NULL;\n\t\t\treturn false;\n\t\t}\n\t\tZ_Free(codec->name);\n\t\tcodec->name = Z_StrDup(value);\n\t}\n\telse if (!strcmp(prop, \"rufrag\"))\n\t{\n\t\tZ_Free(con->rufrag);\n\t\tcon->rufrag = Z_StrDup(value);\n\t}\n\telse if (!strcmp(prop, \"rpwd\"))\n\t{\n\t\tZ_Free(con->rpwd);\n\t\tcon->rpwd = Z_StrDup(value);\n\t}\n\telse if (!strcmp(prop, \"server\"))\n\t{\n\t\tnetadr_t hostadr[1];\n\t\tqboolean okay;\n\t\tqboolean tcp = false;\n\t\tqboolean tls;\n\t\tqboolean stun;\n\t\tchar *s, *next;\n\t\tconst char *user=NULL, *auth=NULL;\n\t\tconst char *host;\n\t\tnetadrtype_t family = NA_INVALID;\n\t\tif (!strncmp(value, \"stun:\", 5))\n\t\t\tstun=true, tls=false, value+=5;\n\t\telse if (!strncmp(value, \"turn:\", 5))\n\t\t\tstun=false, tls=false, value+=5;\n\t\telse if (!strncmp(value, \"turns:\", 6))\n\t\t\tstun=false, tls=true, value+=6;\n\t\telse\n\t\t\treturn false;\t//nope, uri not known.\n\n\t\thost = Z_StrDup(value);\n\n\t\ts = strchr(host, '?');\n\t\tfor (;s;s=next)\n\t\t{\n\t\t\t*s++ = 0;\n\t\t\tnext = strchr(s, '?');\n\t\t\tif (next)\n\t\t\t\t*next = 0;\n\n\t\t\tif (!strncmp(s, \"transport=\", 10))\n\t\t\t{\n\t\t\t\tif (!strcmp(s+10, \"udp\"))\n\t\t\t\t\ttcp=false;\n\t\t\t\telse if (!strcmp(s+10, \"tcp\"))\n\t\t\t\t\ttcp=true;\n\t\t\t}\n\t\t\telse if (!strncmp(s, \"user=\", 5))\n\t\t\t\tuser = s+5;\n\t\t\telse if (!strncmp(s, \"auth=\", 5))\n\t\t\t\tauth = s+5;\n\t\t\telse if (!strncmp(s, \"fam=\", 4))\n\t\t\t{\n\t\t\t\tif (!strcmp(s+4, \"ipv4\") || !strcmp(s+4, \"ip4\") || !strcmp(s+4, \"ip\") || !strcmp(s+4, \"4\"))\n\t\t\t\t\tfamily=NA_IP;\n\t\t\t\telse if (!strcmp(s+4, \"ipv6\") || !strcmp(s+4, \"ip6\") || !strcmp(s+4, \"6\"))\n\t\t\t\t\tfamily=NA_IPV6;\n\t\t\t}\n\t\t}\n\n\t\tokay = !strchr(host, '/');\n\t\tif (con->servers == countof(con->server))\n\t\t\tokay = false;\n\t\telse if (okay)\n\t\t{\n\t\t\tstruct iceserver_s *srv = &con->server[con->servers];\n\n\t\t\t//handily both stun and turn default to the same port numbers.\n\t\t\t//FIXME: worker thread...\n\t\t\tokay = NET_StringToAdr2(host, tls?5349:3478, hostadr, countof(hostadr), NULL);\n\t\t\tif (okay)\n\t\t\t{\n\t\t\t\tif (tls)\n\t\t\t\t\thostadr->prot = tcp?NP_TLS:NP_DTLS;\n\t\t\t\telse\n\t\t\t\t\thostadr->prot = tcp?NP_STREAM:NP_DGRAM;\n\n\t\t\t\tcon->servers++;\n\t\t\t\tsrv->isstun = stun;\n\t\t\t\tsrv->family = family;\n\t\t\t\tsrv->realm = Z_StrDup(host);\n\t\t\t\tSys_RandomBytes((char*)srv->stunrnd, sizeof(srv->stunrnd));\n\t\t\t\tsrv->stunretry = Sys_Milliseconds();\t//'now'...\n\t\t\t\tsrv->addr = *hostadr;\n\t\t\t\tsrv->user = user?Z_StrDup(user):NULL;\n\t\t\t\tsrv->auth = auth?Z_StrDup(auth):NULL;\n\t\t\t}\n\t\t}\n\n\t\tZ_Free(s);\n\t\treturn !!okay;\n\t}\n\telse if (!strcmp(prop, \"sdp\") || !strcmp(prop, \"sdpoffer\") || !strcmp(prop, \"sdpanswer\"))\n\t{\n\t\tchar line[8192];\n\t\tconst char *eol;\n\t\tfor (; *value; value = eol)\n\t\t{\n\t\t\teol = strchr(value, '\\n');\n\t\t\tif (!eol)\n\t\t\t\teol = value+strlen(value);\n\n\t\t\tif (eol-value < sizeof(line))\n\t\t\t{\n\t\t\t\tmemcpy(line, value, eol-value);\n\t\t\t\tline[eol-value] = 0;\n\t\t\t\tif (eol>value && line[eol-value-1] == '\\r')\n\t\t\t\t\tline[eol-value-1] = 0;\n\t\t\t\tICE_ParseSDPLine(con, line);\n\t\t\t}\n\n\t\t\tif (eol && *eol)\n\t\t\t\teol++;\n\t\t}\n\t}\n\telse\n\t\treturn false;\n\treturn true;\n}\nstatic qboolean ICE_SetFailed(struct icestate_s *con, const char *reasonfmt, ...)\n{\n\tva_list\t\targptr;\n\tchar\t\tstring[256];\n\n\tva_start (argptr, reasonfmt);\n\tQ_vsnprintfz (string,sizeof(string)-1, reasonfmt,argptr);\n\tva_end (argptr);\n\n\tif (con->state == ICE_FAILED)\n\t\tCon_Printf(S_COLOR_GRAY\"[%s]: %s\\n\", con->friendlyname, string);\t//we were probably already expecting this. don't show it as a warning.\n\telse\n\t\tCon_Printf(CON_WARNING\"[%s]: %s\\n\", con->friendlyname, string);\n\treturn ICE_Set(con, \"state\", STRINGIFY(ICE_FAILED));\t//does the disconnection stuff.\n}\nstatic char *ICE_CandidateToSDP(struct icecandidate_s *can, char *value, size_t valuelen)\n{\n\tQ_snprintfz(value, valuelen, \"a=candidate:%i %i %s %i %s %i typ %s\",\n\t\t\tcan->info.foundation,\n\t\t\tcan->info.component,\n\t\t\tcan->info.transport==0?\"UDP\":\"ERROR\",\n\t\t\tcan->info.priority,\n\t\t\tcan->info.addr,\n\t\t\tcan->info.port,\n\t\t\tICE_GetCandidateType(&can->info)\n\t\t\t);\n\tif (can->info.generation)\n\t\tQ_strncatz(value, va(\" generation %i\", can->info.generation), valuelen);\t//firefox doesn't like this.\n\tif (can->info.type != ICE_HOST)\n\t{\n\t\tif (net_ice_relayonly.ival)\n\t\t{\t//don't leak srflx info (technically this info is mandatory)\n\t\t\tQ_strncatz(value, va(\" raddr %s\", can->info.addr), valuelen);\n\t\t\tQ_strncatz(value, va(\" rport %i\", can->info.port), valuelen);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (*can->info.reladdr)\n\t\t\t\tQ_strncatz(value, va(\" raddr %s\", can->info.reladdr), valuelen);\n\t\t\telse\n\t\t\t\tQ_strncatz(value, \" raddr 0.0.0.0\", valuelen);\n\t\t\tQ_strncatz(value, va(\" rport %i\", can->info.relport), valuelen);\n\t\t}\n\t}\n\n\treturn value;\n}\nstatic qboolean ICE_LCandidateIsPrivate(struct icecandidate_s *caninfo)\n{\t//return true for the local candidates that we're actually allowed to report. they'll stay flagged as 'dirty' otherwise.\n\tif (!net_ice_exchangeprivateips.ival && caninfo->info.type == ICE_HOST && !caninfo->ismdns)\n\t\treturn true;\n\tif (net_ice_relayonly.ival && caninfo->info.type != ICE_RELAY)\n\t\treturn true;\n\treturn false;\n}\nstatic qboolean QDECL ICE_Get(struct icestate_s *con, const char *prop, char *value, size_t valuelen)\n{\n\tif (!strcmp(prop, \"sid\"))\n\t\tQ_strncpyz(value, con->conname, valuelen);\n\telse if (!strcmp(prop, \"state\"))\n\t{\n\t\tswitch(con->state)\n\t\t{\n\t\tcase ICE_INACTIVE:\n\t\t\tQ_strncpyz(value, STRINGIFY(ICE_INACTIVE), valuelen);\n\t\t\tbreak;\n\t\tcase ICE_FAILED:\n\t\t\tQ_strncpyz(value, STRINGIFY(ICE_FAILED), valuelen);\n\t\t\tbreak;\n\t\tcase ICE_GATHERING:\n\t\t\tQ_strncpyz(value, STRINGIFY(ICE_GATHERING), valuelen);\n\t\t\tbreak;\n\t\tcase ICE_CONNECTING:\n\t\t\tQ_strncpyz(value, STRINGIFY(ICE_CONNECTING), valuelen);\n\t\t\tbreak;\n\t\tcase ICE_CONNECTED:\n\t\t\tQ_strncpyz(value, STRINGIFY(ICE_CONNECTED), valuelen);\n\t\t\tbreak;\n\t\t}\n\t}\n\telse if (!strcmp(prop, \"lufrag\"))\n\t\tQ_strncpyz(value, con->lufrag, valuelen);\n\telse if (!strcmp(prop, \"lpwd\"))\n\t\tQ_strncpyz(value, con->lpwd, valuelen);\n\telse if (!strncmp(prop, \"codec\", 5))\n\t{\n\t\tint codecid = atoi(prop+5);\n\t\tstruct icecodecslot_s *codec = ICE_GetCodecSlot(con, atoi(prop+5));\n\t\tif (!codec || codec->id != codecid)\n\t\t\treturn false;\n\t\tif (codec->name)\n\t\t\tQ_strncpyz(value, codec->name, valuelen);\n\t\telse\n\t\t\tQ_strncpyz(value, \"\", valuelen);\n\t}\n\telse if (!strcmp(prop, \"newlc\"))\n\t{\n\t\tstruct icecandidate_s *can;\n\t\tQ_strncpyz(value, \"0\", valuelen);\n\t\tfor (can = con->lc; can; can = can->next)\n\t\t{\n\t\t\tif (can->dirty && !ICE_LCandidateIsPrivate(can))\n\t\t\t{\n\t\t\t\tQ_strncpyz(value, \"1\", valuelen);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\telse if (!strcmp(prop, \"peersdp\"))\n\t{\t//for debugging.\n\t\tQ_strncpyz(value, \"\", valuelen);\n\t\tif ((con->proto == ICEP_QWSERVER || con->proto == ICEP_QWCLIENT) && con->mode == ICEM_WEBRTC)\n\t\t{\n#ifdef HAVE_DTLS\n\t\t\tif (con->cred.peer.hash)\n\t\t\t{\n\t\t\t\tint b;\n\t\t\t\tQ_strncatz(value, \"a=fingerprint:\", valuelen);\n\t\t\t\tfor (b = 0; b < countof(webrtc_hashes); b++)\n\t\t\t\t{\n\t\t\t\t\tif (con->cred.peer.hash == webrtc_hashes[b].hash)\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tQ_strncatz(value, (b==countof(webrtc_hashes))?\"UNKNOWN\":webrtc_hashes[b].name, valuelen);\n\t\t\t\tfor (b = 0; b < con->cred.peer.hash->digestsize; b++)\n\t\t\t\t\tQ_strncatz(value, va(b?\":%02X\":\" %02X\", con->cred.peer.digest[b]), valuelen);\n\t\t\t\tQ_strncatz(value, \"\\n\", valuelen);\n\t\t\t}\n#endif\n\t\t}\n\t\tQ_strncatz(value, va(\"a=ice-pwd:%s\\n\", con->rpwd), valuelen);\n\t\tQ_strncatz(value, va(\"a=ice-ufrag:%s\\n\", con->rufrag), valuelen);\n\n#ifdef HAVE_DTLS\n\t\tif (con->peersctpport)\n\t\t\tQ_strncatz(value, va(\"a=sctp-port:%i\\n\", con->peersctpport), valuelen);\t//stupid hardcoded thing.\n\t\tif (con->peersctpoptional)\n\t\t\tQ_strncatz(value, \"a=sctp-optional:1\\n\", valuelen);\n#endif\n\n\n\n\t}\n\telse if (!strcmp(prop, \"sdp\") || !strcmp(prop, \"sdpoffer\") || !strcmp(prop, \"sdpanswer\"))\n\t{\n\t\tstruct icecandidate_s *can;\n\t\tnetadr_t sender;\n\t\tchar tmpstr[MAX_QPATH], *at;\n\t\tint i;\n\n\t\t{\n\t\t\tnetadr_t\taddr[1];\n\t\t\tstruct ftenet_generic_connection_s *gcon[countof(addr)];\n\t\t\tint\t\t\tflags[countof(addr)];\n\t\t\tconst char *params[countof(addr)];\n\n\t\t\tif (!NET_EnumerateAddresses(ICE_PickConnection(con), gcon, flags, addr, params, countof(addr)))\n\t\t\t{\n\t\t\t\tsender.type = NA_INVALID;\n\t\t\t\tsender.port = 0;\n\t\t\t}\n\t\t\telse\n\t\t\t\tsender = *addr;\n\t\t}\n\n\t\tQ_strncpyz(value, \"v=0\\n\", valuelen);\t//version...\n\t\tQ_strncatz(value, va(\"o=%s %u %u IN IP4 %s\\n\", \"-\", con->originid, con->originversion, con->originaddress), valuelen);\t//originator. usually just dummy info.\n\t\tQ_strncatz(value, va(\"s=%s\\n\", con->conname), valuelen);\t//session name.\n\t\tQ_strncatz(value, \"t=0 0\\n\", valuelen);\t//start+end times...\n\t\tQ_strncatz(value, va(\"a=ice-options:trickle\\n\"), valuelen);\n\n\t\tif (con->proto == ICEP_QWSERVER || con->proto == ICEP_QWCLIENT)\n\t\t{\n#ifdef HAVE_DTLS\n\t\t\tif (net_enable_dtls.ival >= 3)\n\t\t\t{\t//this is a preliminary check to avoid wasting time\n\t\t\t\tif (!con->cred.local.certsize)\n\t\t\t\t\treturn false;\t//fail if we cannot do dtls when its required.\n\t\t\t\tif (!strcmp(prop, \"sdpanswer\") && !con->cred.peer.hash)\n\t\t\t\t\treturn false;\t//don't answer if they failed to provide a cert\n\t\t\t}\n\t\t\tif (con->cred.local.certsize)\n\t\t\t{\n\t\t\t\tqbyte fingerprint[DIGEST_MAXSIZE];\n\t\t\t\tint b;\n\t\t\t\thashfunc_t *hash = &hash_sha2_256;\t//browsers use sha-256, lets match them.\n\t\t\t\tCalcHash(hash, fingerprint, sizeof(fingerprint), con->cred.local.cert, con->cred.local.certsize);\n\t\t\t\tQ_strncatz(value, \"a=fingerprint:sha-256\", valuelen);\n\t\t\t\tfor (b = 0; b < hash->digestsize; b++)\n\t\t\t\t\tQ_strncatz(value, va(b?\":%02X\":\" %02X\", fingerprint[b]), valuelen);\n\t\t\t\tQ_strncatz(value, \"\\n\", valuelen);\n\n\t\t\t\tif (con->mode == ICEM_WEBRTC)\n\t\t\t\t{\n\t\t\t\t\tQ_strncatz(value, \"m=application 9 UDP/DTLS/SCTP webrtc-datachannel\\n\", valuelen);\n\t\t\t\t\tif (con->mysctpport)\n\t\t\t\t\t\tQ_strncatz(value, va(\"a=sctp-port:%i\\n\", con->mysctpport), valuelen);\t//stupid hardcoded thing.\n\t\t\t\t\tif (con->sctpoptional)\n\t\t\t\t\t\tQ_strncatz(value, \"a=sctp-optional:1\\n\", valuelen);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tQ_strncatz(value, \"m=application 9 UDP/DTLS\\n\", valuelen);\n\t\t\t}\n\t\t\telse\n\t\t\t\tQ_strncatz(value, \"m=application 9 UDP\\n\", valuelen);\n#endif\n\t\t}\n//\t\tQ_strncatz(value, va(\"c=IN %s %s\\n\", sender.type==NA_IPV6?\"IP6\":\"IP4\", NET_BaseAdrToString(tmpstr, sizeof(tmpstr), &sender)), valuelen);\n\t\tQ_strncatz(value, \"c=IN IP4 0.0.0.0\\n\", valuelen);\n\n\t\tfor (can = con->lc; can; can = can->next)\n\t\t{\n\t\t\tchar canline[256];\n\t\t\tif (ICE_LCandidateIsPrivate(can))\n\t\t\t\tcontinue;\t//ignore it.\n\t\t\tcan->dirty = false;\t//doesn't matter now.\n\t\t\tICE_CandidateToSDP(can, canline, sizeof(canline));\n\t\t\tQ_strncatz(value, canline, valuelen);\n\t\t\tQ_strncatz(value, \"\\n\", valuelen);\n\t\t}\n\n\t\tQ_strncatz(value, va(\"a=ice-pwd:%s\\n\", con->lpwd), valuelen);\n\t\tQ_strncatz(value, va(\"a=ice-ufrag:%s\\n\", con->lufrag), valuelen);\n\n#ifdef HAVE_DTLS\n\t\tif (con->dtlsfuncs)\n\t\t{\n\t\t\tif (!strcmp(prop, \"sdpanswer\"))\n\t\t\t{\t//answerer decides.\n\t\t\t\tif (con->dtlspassive)\n\t\t\t\t\tQ_strncatz(value, va(\"a=setup:passive\\n\"), valuelen);\n\t\t\t\telse\n\t\t\t\t\tQ_strncatz(value, va(\"a=setup:active\\n\"), valuelen);\n\t\t\t}\n\t\t\telse if (!strcmp(prop, \"sdpoffer\"))\n\t\t\t\tQ_strncatz(value, va(\"a=setup:actpass\\n\"), valuelen);\t//don't care if we're active or passive\n\t\t}\n#endif\n\n\t\t/*fixme: merge the codecs into a single media line*/\n\t\tfor (i = 0; i < countof(con->codecslot); i++)\n\t\t{\n\t\t\tint id = con->codecslot[i].id;\n\t\t\tif (!con->codecslot[i].name)\n\t\t\t\tcontinue;\n\n\t\t\tQ_strncatz(value, va(\"m=audio %i RTP/AVP %i\\n\", sender.port, id), valuelen);\n\t\t\tQ_strncatz(value, va(\"b=RS:0\\n\"), valuelen);\n\t\t\tQ_strncatz(value, va(\"b=RR:0\\n\"), valuelen);\n\t\t\tQ_strncpyz(tmpstr, con->codecslot[i].name, sizeof(tmpstr));\n\t\t\tat = strchr(tmpstr, '@');\n\t\t\tif (at)\n\t\t\t{\n\t\t\t\t*at = '/';\n\t\t\t\tQ_strncatz(value, va(\"a=rtpmap:%i %s\\n\", id, tmpstr), valuelen);\n\t\t\t}\n\t\t\telse\n\t\t\t\tQ_strncatz(value, va(\"a=rtpmap:%i %s/%i\\n\", id, tmpstr, 8000), valuelen);\n\n\t\t\tfor (can = con->lc; can; can = can->next)\n\t\t\t{\n\t\t\t\tchar canline[256];\n\t\t\t\tcan->dirty = false;\t//doesn't matter now.\n\t\t\t\tICE_CandidateToSDP(can, canline, sizeof(canline));\n\t\t\t\tQ_strncatz(value, canline, valuelen);\n\t\t\t\tQ_strncatz(value, \"\\n\", valuelen);\n\t\t\t}\n\t\t}\n\t}\n\telse\n\t\treturn false;\n\treturn true;\n}\n\nstatic void ICE_PrintSummary(struct icestate_s *con, qboolean islisten)\n{\n\tchar msg[64];\n\n\tCon_Printf(S_COLOR_GRAY\" ^[[%s]\\\\ice\\\\%s^]: \", con->friendlyname, con->friendlyname);\n\tswitch(con->proto)\n\t{\n\tcase ICEP_VOICE:\tCon_Printf(S_COLOR_GRAY\"(voice) \"); break;\n\tcase ICEP_VIDEO:\tCon_Printf(S_COLOR_GRAY\"(video) \"); break;\n\tdefault:\t\t\tbreak;\n\t}\n\tswitch(con->state)\n\t{\n\tcase ICE_INACTIVE:\t\tCon_Printf(S_COLOR_RED \"inactive\"); break;\n\tcase ICE_FAILED:\t\tCon_Printf(S_COLOR_RED \"failed\"); break;\n\tcase ICE_GATHERING:\t\tCon_Printf(S_COLOR_YELLOW \"gathering\"); break;\n\tcase ICE_CONNECTING:\tCon_Printf(S_COLOR_YELLOW \"connecting\"); break;\n\tcase ICE_CONNECTED:\t\tCon_Printf(S_COLOR_GRAY\"%s via %s\", NET_AdrToString(msg,sizeof(msg), &con->chosenpeer), ICE_NetworkToName(con, con->chosenpeer.connum)); break;\n\t}\n#ifdef HAVE_DTLS\n\tif (con->dtlsstate)\n\t\tCon_Printf(S_COLOR_GREEN \" (encrypted%s)\", con->sctp?\", sctp\":\"\");\n\telse if (con->sctp)\n\t\tCon_Printf(S_COLOR_RED \" (plain-text, sctp)\");\t//weeeeeeird and pointless...\n\telse\n#endif\n\t\tCon_Printf(S_COLOR_RED \" (plain-text)\");\n\tCon_Printf(\"\\n\");\n}\nstatic void ICE_Debug(struct icestate_s *con)\n{\n\tconst char *addrclass;\n\tstruct icecandidate_s *can;\n\tchar buf[65536];\n\tint i;\n\tICE_Get(con, \"state\", buf, sizeof(buf));\n\tCon_Printf(\"ICE [%s] (%s):\\n\", con->friendlyname, buf);\n\tif (con->brokerless)\n\t\tCon_Printf(\" timeout: %g\\n\", (int)(con->icetimeout-Sys_Milliseconds())/1000.0);\n\telse\n\t{\n\t\tunsigned int idle = (Sys_Milliseconds()+30*1000 - con->icetimeout);\n\t\tif (idle > 500)\n\t\t\tCon_Printf(\" idle: %g\\n\", idle/1000.0);\n\t}\n\tif (net_ice_debug.ival >= 2)\n\t{\t//rather uninteresting really...\n\t\tif (con->initiator)\n\t\t\tICE_Get(con, \"sdpoffer\", buf, sizeof(buf));\n\t\telse\n\t\t\tICE_Get(con, \"sdpanswer\", buf, sizeof(buf));\n\t\tCon_Printf(\"sdp:\\n\"S_COLOR_YELLOW \"%s\\n\", buf);\n\n\t\t//incomplete anyway\n\t\tICE_Get(con, \"peersdp\", buf, sizeof(buf));\n\t\tCon_Printf(\"peer:\\n\"S_COLOR_YELLOW\"%s\\n\", buf);\n\t}\n\n\tCon_Printf(\" servers:\\n\");\n\tfor (i = 0; i < con->servers; i++)\n\t{\n\t\tconst char *status = \"?\";\n\t\tswitch(con->server[i].state)\n\t\t{\n\t\tcase TURN_UNINITED:\t\tstatus = \"uninited\";\tbreak;\n\t\tcase TURN_HAVE_NONCE:\tstatus = \"registering\";\tbreak;\n\t\tcase TURN_ALLOCATED:\tstatus = \"allocated\";\tbreak;\n\t\tcase TURN_TERMINATING:\tstatus = \"terminating\";\tbreak;\n\t\t}\n\t\tNET_AdrToString(buf,sizeof(buf), &con->server[i].addr);\n\t\tCon_Printf(\"  %s:%s %s realm=%s user=%s auth=%s\\n\", con->server[i].isstun?\"stun\":\"turn\", buf, status, con->server[i].realm, con->server[i].user?con->server[i].user:\"<unspecified>\", con->server[i].auth?\"<hidden>\":\"<none>\");\n\t}\n\n\tCon_Printf(\" local:\\n\");\n\tfor (can = con->lc; can; can = can->next)\n\t{\n\t\tICE_CandidateToSDP(can, buf, sizeof(buf));\n\t\tif (con->chosenpeer.type!=NA_INVALID && con->chosenpeer.connum == can->info.network)\n\t\t\tCon_Printf(S_COLOR_GREEN \"  %s\"S_COLOR_GRAY\" <chosen>\\n\", buf);\n\t\telse if (can->dirty)\n\t\t\tCon_Printf(S_COLOR_RED   \"  %s\"S_COLOR_GRAY\" <not sent>\\n\", buf);\n\t\telse\n\t\t\tCon_Printf(S_COLOR_YELLOW\"  %s\\n\", buf);\n\t}\n\n\tCon_Printf(\" remote:\\n\");\n\tfor (can = con->rc; can; can = can->next)\n\t{\n\t\tICE_CandidateToSDP(can, buf, sizeof(buf));\n\t\tif (can->reachable)\n\t\t{\n\t\t\tif (con->chosenpeer.type!=NA_INVALID && NET_CompareAdr(&can->peer,&con->chosenpeer))\n\t\t\t\tCon_Printf(S_COLOR_GREEN \"  %s\"S_COLOR_GRAY\" <chosen>\\n\", buf);\n\t\t\telse\n\t\t\t\tCon_Printf(S_COLOR_YELLOW\"  %s\"S_COLOR_GRAY\" <reachable>\\n\", buf);\n\t\t}\n\t\telse if (NET_ClassifyAddress(&can->peer, &addrclass) < ASCOPE_TURN_REQUIRESCOPE)\n\t\t\tCon_Printf(S_COLOR_RED\"  %s\"S_COLOR_GRAY\" <ignored: %s>\\n\", buf, addrclass);\n\t\telse\n\t\t\tCon_Printf(S_COLOR_RED\"  %s\"S_COLOR_GRAY\" <unreachable>\\n\", buf);\n\t}\n}\nstatic void ICE_Show_f(void)\n{\n\tconst char *findname = Cmd_Argv(1);\n\tstruct icestate_s *ice;\n\tfor (ice = icelist; ice; ice = ice->next)\n\t{\n\t\tif (!*findname || !strcmp(findname, ice->friendlyname))\n\t\t\tICE_Debug(ice);\n\t}\n}\nstatic struct icecandinfo_s *QDECL ICE_GetLCandidateInfo(struct icestate_s *con)\n{\n\tstruct icecandidate_s *can;\n\tfor (can = con->lc; can; can = can->next)\n\t{\n\t\tif (can->dirty)\n\t\t{\n\t\t\tif (ICE_LCandidateIsPrivate(can))\n\t\t\t\tcontinue;\n\n\t\t\tcan->dirty = false;\n\t\t\treturn &can->info;\n\t\t}\n\t}\n\treturn NULL;\n}\n\nstatic qboolean QDECL ICE_GetLCandidateSDP(struct icestate_s *con, char *out, size_t outsize)\n{\n\tstruct icecandinfo_s *info = ICE_GetLCandidateInfo(con);\n\tif (info)\n\t{\n\t\tstruct icecandidate_s *can = (struct icecandidate_s*)info;\n\t\tICE_CandidateToSDP(can, out, outsize);\n\t\treturn true;\n\t}\n\treturn false;\n}\n\nstatic unsigned int ICE_ComputePriority(netadr_t *adr, struct icecandinfo_s *info)\n{\n\tint tpref, lpref;\n\tswitch(info->type)\n\t{\n\tcase ICE_HOST:\ttpref = 126;\tbreak;\t//ideal\n\tcase ICE_PRFLX: tpref = 110;\tbreak;\n\tcase ICE_SRFLX: tpref = 100;\tbreak;\n\tdefault:\n\tcase ICE_RELAY:\ttpref = 0;\tbreak;\t//relays suck\n\t}\n\tlpref = 0;\n\tif (info->transport == 0)\n\t\tlpref += 0x8000;\t//favour udp the most (tcp sucks for stalls)\n\tlpref += (255-info->network)<<16;\t//favour the first network/socket specified\n\tlpref += (255-NET_ClassifyAddress(adr, NULL))<<8;\n\tif (adr->type == NA_IP)\n\t\tlpref += 0x0001;\t//favour ipv4 over ipv6 (less header/mtu overhead...). this is only slight,\n\n\treturn (tpref<<24) + (lpref<<lpref) + ((256 - info->component)<<0);\n}\n\n//adrno is 0 if the type is anything but host.\nvoid QDECL ICE_AddLCandidateInfo(struct icestate_s *con, netadr_t *adr, int adrno, int type)\n{\n\tint rnd[2];\n\tstruct icecandidate_s *cand;\n\tif (!con)\n\t\treturn;\n\n\tswitch(adr->type)\n\t{\n\tcase NA_IP:\n\tcase NA_IPV6:\n\t\tswitch(NET_ClassifyAddress(adr, NULL))\n\t\t{\n\t\tcase ASCOPE_PROCESS://doesn't make sense.\n\t\tcase ASCOPE_HOST:\t//don't waste time asking the relay to poke its loopback. if only because it'll report lots of errors.\n\t\t\treturn;\n\t\tcase ASCOPE_NET:\t//public addresses, just add local candidates as normal\n\t\t\tbreak;\n\t\tcase ASCOPE_LINK:\t//random screwy addresses... hopefully don't need em if we're talking to a broker... no dhcp server is weird.\n\t\t\treturn;\n\t\tcase ASCOPE_LAN:\t//private addresses. give them random info instead...\n\t\t\tif (net_ice_allowmdns.ival && MDNS_Setup())\n\t\t\t{\n\t\t\t\tfor (cand = con->lc; cand; cand = cand->next)\n\t\t\t\t{\n\t\t\t\t\tif (cand->ismdns)\n\t\t\t\t\t\treturn;\t//DUPE\n\t\t\t\t}\n\n\t\t\t\tcand = Z_Malloc(sizeof(*cand));\n\t\t\t\tcand->next = con->lc;\n\t\t\t\tcon->lc = cand;\n\t\t\t\tQ_strncpyz(cand->info.addr, mdns_name[con->proto == ICEP_QWSERVER], sizeof(cand->info.addr));\n\t\t\t\tcand->info.port = ntohs(adr->port);\n\t\t\t\tcand->info.type = type;\n\t\t\t\tcand->info.generation = 0;\n\t\t\t\tcand->info.foundation = 1;\n\t\t\t\tcand->info.component = 1;\n\t\t\t\tcand->info.network = adr->connum;\n\t\t\t\tcand->dirty = true;\n\t\t\t\tcand->ismdns = true;\n\n\t\t\t\tSys_RandomBytes((void*)rnd, sizeof(rnd));\n\t\t\t\tQ_strncpyz(cand->info.candidateid, va(\"x%08x%08x\", rnd[0], rnd[1]), sizeof(cand->info.candidateid));\n\n\t\t\t\tcand->info.priority = ICE_ComputePriority(adr, &cand->info);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t\tbreak;\n\tdefault:\t//bad protocols\n\t\treturn;\t//no, just no.\n\t}\n\tswitch(adr->prot)\n\t{\n\tcase NP_DTLS:\n\tcase NP_DGRAM:\n\t\tbreak;\n\tdefault:\n\t\treturn;\t//don't report any tcp/etc connections...\n\t}\n\n\tfor (cand = con->lc; cand; cand = cand->next)\n\t{\n\t\tif (NET_CompareAdr(adr, &cand->peer))\n\t\t\treturn; //DUPE\n\t}\n\n\tcand = Z_Malloc(sizeof(*cand));\n\tcand->next = con->lc;\n\tcon->lc = cand;\n\tNET_BaseAdrToString(cand->info.addr, sizeof(cand->info.addr), adr);\n\tcand->info.port = ntohs(adr->port);\n\tcand->info.type = type;\n\tcand->info.generation = 0;\n\tcand->info.foundation = 1;\n\tcand->info.component = 1;\n\tcand->info.network = adr->connum;\n\tcand->dirty = true;\n\n\tSys_RandomBytes((void*)rnd, sizeof(rnd));\n\tQ_strncpyz(cand->info.candidateid, va(\"x%08x%08x\", rnd[0], rnd[1]), sizeof(cand->info.candidateid));\n\n\tcand->info.priority = ICE_ComputePriority(adr, &cand->info);\n}\nvoid QDECL ICE_AddLCandidateConn(ftenet_connections_t *col, netadr_t *addr, int type)\n{\n\tstruct icestate_s *ice;\n\tfor (ice = icelist; ice; ice = ice->next)\n\t{\n\t\tif (ICE_PickConnection(ice) == col)\n\t\t\tICE_AddLCandidateInfo(ice, addr, 0, type);\n\t}\n}\n\nstatic void ICE_Destroy(struct icestate_s *con)\n{\n\tstruct icecandidate_s *c;\n\n\tICE_Set(con, \"state\", STRINGIFY(ICE_INACTIVE));\n\n#ifdef HAVE_DTLS\n\tif (con->sctp)\n\t{\n\t\tZ_Free(con->sctp->cookie);\n\t\tZ_Free(con->sctp);\n\t}\n\tif (con->dtlsstate)\n\t\tcon->dtlsfuncs->DestroyContext(con->dtlsstate);\n\tif (con->cred.local.cert)\n\t\tZ_Free(con->cred.local.cert);\n\tif (con->cred.local.key)\n\t\tZ_Free(con->cred.local.key);\n#endif\n\twhile(con->rc)\n\t{\n\t\tc = con->rc;\n\t\tcon->rc = c->next;\n\t\tZ_Free(c);\n\t}\n\twhile(con->lc)\n\t{\n\t\tc = con->lc;\n\t\tcon->lc = c->next;\n\t\tZ_Free(c);\n\t}\n\twhile (con->servers)\n\t{\n\t\tstruct iceserver_s *s = &con->server[--con->servers];\n\t\tif (s->con)\n\t\t{\t//make sure we tell the TURN server to release our allocation.\n\t\t\ts->state = TURN_TERMINATING;\n\t\t\tICE_ToStunServer(con, s);\n\n\t\t\ts->con->Close(s->con);\n\t\t}\n\t\tZ_Free(s->user);\n\t\tZ_Free(s->auth);\n\t\tZ_Free(s->realm);\n\t\tZ_Free(s->nonce);\n\t}\n\tif (con->connections)\n\t\tFTENET_CloseCollection(con->connections);\n\tZ_Free(con->lufrag);\n\tZ_Free(con->lpwd);\n\tZ_Free(con->rufrag);\n\tZ_Free(con->rpwd);\n\tZ_Free(con->friendlyname);\n\tZ_Free(con->conname);\n\t//has already been unlinked\n\tZ_Free(con);\n}\n\n//send pings to establish/keep the connection alive\nvoid ICE_Tick(void)\n{\n\tstruct icestate_s **link, *con;\n\tunsigned int curtime;\n\n\tif (!icelist)\n\t\treturn;\n\tcurtime = Sys_Milliseconds();\n\n\tMDNS_SendQueries();\n\n\tfor (link = &icelist; (con=*link);)\n\t{\n\t\tif (con->brokerless)\n\t\t{\n\t\t\tif (con->state <= ICE_GATHERING)\n\t\t\t{\n\t\t\t\t*link = con->next;\n\t\t\t\tICE_Destroy(con);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\telse if ((signed int)(curtime-con->icetimeout) > 0)\n\t\t\t\tICE_SetFailed(con, S_COLOR_GRAY\"[%s]: ice timeout\\n\", con->friendlyname);\n\t\t}\n\n\t\tswitch(con->mode)\n\t\t{\n\t\tcase ICEM_RAW:\n\t\t\t//raw doesn't do handshakes or keepalives. it should just directly connect.\n\t\t\t//raw just uses the first (assumed only) option\n\t\t\tif (con->state == ICE_CONNECTING)\n\t\t\t{\n\t\t\t\tstruct icecandidate_s *rc;\n\t\t\t\trc = con->rc;\n\t\t\t\tif (!rc || !NET_StringToAdr(rc->info.addr, rc->info.port, &con->chosenpeer))\n\t\t\t\t\tcon->chosenpeer.type = NA_INVALID;\n\t\t\t\tICE_Set(con, \"state\", STRINGIFY(ICE_CONNECTED));\n\t\t\t}\n\t\t\tbreak;\n\t\tcase ICEM_WEBRTC:\n\t\tcase ICEM_ICE:\n\t\t\tif (con->state == ICE_CONNECTING || con->state == ICE_CONNECTED)\n\t\t\t{\n\t\t\t\tsize_t i, j;\n\t\t\t\tstruct iceserver_s *srv;\n\t\t\t\tfor (i = 0; i < con->servers; i++)\n\t\t\t\t{\n\t\t\t\t\tsrv = &con->server[i];\n\t\t\t\t\tif ((signed int)(srv->stunretry-curtime) < 0)\n\t\t\t\t\t{\n\t\t\t\t\t\tsrv->stunretry = curtime + 2*1000;\n\t\t\t\t\t\tICE_ToStunServer(con, srv);\n\t\t\t\t\t}\n\t\t\t\t\tfor (j = 0; j < srv->peers; j++)\n\t\t\t\t\t{\n\t\t\t\t\t\tif ((signed int)(srv->peer[j].retry-curtime) < 0)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tTURN_AuthorisePeer(con, srv, j);\n\t\t\t\t\t\t\tsrv->peer[j].retry = curtime + 2*1000;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif (srv->con)\n\t\t\t\t\t{\n\t\t\t\t\t\twhile (srv->con->GetPacket(srv->con))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tnet_from.connum = srv->con->connum;\n\t\t\t\t\t\t\tnet_from_connection = srv->con;\n\t\t\t\t\t\t\tsrv->con->owner->ReadGamePacket();\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (con->keepalive < curtime)\n\t\t\t\t{\n\t\t\t\t\tif (!ICE_SendSpam(con))\n\t\t\t\t\t{\n\t\t\t\t\t\tstruct icecandidate_s *rc;\n\t\t\t\t\t\tstruct icecandidate_s *best = NULL;\n\n\t\t\t\t\t\tfor (rc = con->rc; rc; rc = rc->next)\n\t\t\t\t\t\t{\t//FIXME:\n\t\t\t\t\t\t\tif (rc->reachable && (!best || rc->info.priority > best->info.priority))\n\t\t\t\t\t\t\t\tbest = rc;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (best)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tnetadr_t nb = best->peer;\n\t\t\t\t\t\t\tfor (i = 0; ; i++)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tif (best->reachable&(1<<i))\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tbest->tried &= ~(1<<i);\t//keep poking it...\n\t\t\t\t\t\t\t\t\tnb.connum = i+1;\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (memcmp(&con->chosenpeer, &nb, sizeof(nb)) && (con->chosenpeer.type==NA_INVALID || !con->controlled))\n\t\t\t\t\t\t\t{\t//it actually changed... let them know NOW.\n\t\t\t\t\t\t\t\tbest->tried &= ~(1<<(con->chosenpeer.connum-1));\t//make sure we resend this one.\n\t\t\t\t\t\t\t\tcon->chosenpeer = nb;\n\t\t\t\t\t\t\t\tICE_SendSpam(con);\n\n\t\t\t\t\t\t\t\tif (net_ice_debug.ival >= 1)\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tchar msg[64];\n\t\t\t\t\t\t\t\t\tCon_Printf(S_COLOR_GRAY\"[%s]: New peer chosen %s (%s), via %s.\\n\", con->friendlyname, NET_AdrToString(msg, sizeof(msg), &con->chosenpeer), ICE_GetCandidateType(&best->info), ICE_NetworkToName(con, con->chosenpeer.connum));\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\t/*if (con->state == ICE_CONNECTED && best)\n\t\t\t\t\t\t{\t//once established, back off somewhat\n\t\t\t\t\t\t\tfor (rc = con->rc; rc; rc = rc->next)\n\t\t\t\t\t\t\t\trc->tried &= ~rc->reachable;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse*/\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tfor (rc = con->rc; rc; rc = rc->next)\n\t\t\t\t\t\t\t\trc->tried = 0;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tcon->retries++;\n\t\t\t\t\t\tif (con->retries > 32)\n\t\t\t\t\t\t\tcon->retries = 32;\n\t\t\t\t\t\tcon->keepalive = curtime + 200*(con->retries);\t//RTO... ish.\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\tcon->keepalive = curtime + 50*(con->retries+1);\t//Ta... absmin of 5ms\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (con->state == ICE_CONNECTED)\n\t\t\t{\n#ifdef HAVE_DTLS\n\t\t\t\tif (con->sctp)\n\t\t\t\t\tSCTP_Transmit(con->sctp, NULL,0);\t//try to keep it ticking...\n\t\t\t\tif (con->dtlsstate)\n\t\t\t\t\tcon->dtlsfuncs->Timeouts(con->dtlsstate);\n#endif\n\n\t\t\t\t//FIXME: We should be sending a stun binding indication every 15 secs with a fingerprint attribute\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\n\t\tlink = &con->next;\n\t}\n}\nstatic void QDECL ICE_Close(struct icestate_s *con, qboolean force)\n{\n\tstruct icestate_s **link;\n\n\tfor (link = &icelist; *link; )\n\t{\n\t\tif (con == *link)\n\t\t{\n\t\t\tif (!force)\n\t\t\t\tcon->brokerless = true;\n\t\t\telse\n\t\t\t{\n\t\t\t\t*link = con->next;\n\t\t\t\tICE_Destroy(con);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\telse\n\t\t\tlink = &(*link)->next;\n\t}\n}\nstatic void QDECL ICE_CloseModule(void *module)\n{\n\tstruct icestate_s **link, *con;\n\n\tfor (link = &icelist; *link; )\n\t{\n\t\tcon = *link;\n\t\tif (con->module == module)\n\t\t{\n\t\t\t*link = con->next;\n\t\t\tICE_Destroy(con);\n\t\t}\n\t\telse\n\t\t\tlink = &(*link)->next;\n\t}\n}\n\nicefuncs_t iceapi =\n{\n\tICE_Create,\n\tICE_Set,\n\tICE_Get,\n\tICE_GetLCandidateInfo,\n\tICE_AddRCandidateInfo,\n\tICE_Close,\n\tICE_CloseModule,\n\tICE_GetLCandidateSDP,\n\tICE_Find\n};\n#endif\n\n\n\n#if defined(SUPPORT_ICE) && defined(HAVE_DTLS)\n//========================================\n//WebRTC's interpretation of SCTP. its annoying, but hey its only 28 wasted bytes... along with the dtls overhead too. most of this is redundant.\n//we only send unreliably.\n//there's no point in this code without full webrtc code.\n\nstruct sctp_header_s\n{\n\tquint16_t srcport;\n\tquint16_t dstport;\n\tquint32_t verifycode;\n\tquint32_t crc;\n};\nstruct sctp_chunk_s\n{\n\tqbyte type;\n#define SCTP_TYPE_DATA 0\n#define SCTP_TYPE_INIT 1\n#define SCTP_TYPE_INITACK 2\n#define SCTP_TYPE_SACK 3\n#define SCTP_TYPE_PING 4\n#define SCTP_TYPE_PONG 5\n#define SCTP_TYPE_ABORT 6\n#define SCTP_TYPE_SHUTDOWN 7\n#define SCTP_TYPE_SHUTDOWNACK 8\n#define SCTP_TYPE_ERROR 9\n#define SCTP_TYPE_COOKIEECHO 10\n#define SCTP_TYPE_COOKIEACK 11\n#define SCTP_TYPE_SHUTDOWNDONE 14\n#define SCTP_TYPE_PAD 132\n#define SCTP_TYPE_FORWARDTSN 192\n\tqbyte flags;\n\tquint16_t length;\n\t//value...\n};\nstruct sctp_chunk_data_s\n{\n\tstruct sctp_chunk_s chunk;\n\tquint32_t tsn;\n\tquint16_t sid;\n\tquint16_t seq;\n\tquint32_t ppid;\n#define SCTP_PPID_DCEP 50 //datachannel establishment protocol\n#define SCTP_PPID_DATA 53 //our binary quake data.\n};\nstruct sctp_chunk_init_s\n{\n\tstruct sctp_chunk_s chunk;\n\tquint32_t verifycode;\n\tquint32_t arwc;\n\tquint16_t numoutstreams;\n\tquint16_t numinstreams;\n\tquint32_t tsn;\n};\nstruct sctp_chunk_sack_s\n{\n\tstruct sctp_chunk_s chunk;\n\tquint32_t tsn;\n\tquint32_t arwc;\n\tquint16_t gaps;\n\tquint16_t dupes;\n\t/*struct {\n\t\tquint16_t first;\n\t\tquint16_t last;\n\t} gapblocks[];\t//actually received rather than gaps, but same meaning.\n\tquint32_t dupe_tsns[];*/\n};\nstruct sctp_chunk_fwdtsn_s\n{\n\tstruct sctp_chunk_s chunk;\n\tquint32_t tsn;\n\t/*struct\n\t{\n\t\tquint16_t sid;\n\t\tquint16_t seq;\n\t} streams[];*/\n};\n\nstatic neterr_t SCTP_PeerSendPacket(sctp_t *sctp, int length, const void *data)\n{\t//sends to the dtls layer (which will send to the generic ice dispatcher that'll send to the dgram stuff... layers on layers.\n\tstruct icestate_s *peer = sctp->icestate;\n\tif (sctp->dtlsstate)\n\t\treturn sctp->dtlsfuncs->Transmit(sctp->dtlsstate, data, length);\n\telse if (peer)\n\t{\n\t\tif (peer->dtlsstate)\n\t\t\treturn peer->dtlsfuncs->Transmit(peer->dtlsstate, data, length);\n\t\telse if (peer->chosenpeer.type != NA_INVALID)\n\t\t\treturn ICE_Transmit(peer, data, length);\n\t\telse if (peer->state < ICE_CONNECTING)\n\t\t\treturn NETERR_DISCONNECTED;\n\t\telse\n\t\t\treturn NETERR_CLOGGED;\n\t}\n\telse\n\t\treturn NETERR_NOROUTE;\n}\n\nstatic quint32_t SCTP_Checksum(const struct sctp_header_s *h, size_t size)\n{\n    int k;\n    const qbyte *buf = (const qbyte*)h;\n    size_t ofs;\n    uint32_t crc = 0xFFFFFFFF;\n\n\tfor (ofs = 0; ofs < size; ofs++)\n    {\n\t\tif (ofs >= 8 && ofs < 8+4)\n\t\t\t;\t//the header's crc should be read as 0.\n\t\telse\n\t\t\tcrc ^= buf[ofs];\n        for (k = 0; k < 8; k++)\t            //CRC-32C polynomial 0x1EDC6F41 in reversed bit order.\n            crc = crc & 1 ? (crc >> 1) ^ 0x82f63b78 : crc >> 1;\n    }\n    return ~crc;\n}\n\nneterr_t SCTP_Transmit(sctp_t *sctp, const void *data, size_t length)\n{\n\tqbyte pkt[65536];\n\tsize_t pktlen = 0;\n\tstruct sctp_header_s *h = (void*)pkt;\n\tstruct sctp_chunk_data_s *d;\n\tstruct sctp_chunk_fwdtsn_s *fwd;\n\tif (length > sizeof(pkt))\n\t\treturn NETERR_MTU;\n\n\th->dstport = sctp->peerport;\n\th->srcport = sctp->myport;\n\th->verifycode = sctp->o.verifycode;\n\tpktlen += sizeof(*h);\n\n\t//advance our ctsn if we're received the relevant packets\n\twhile(sctp->i.htsn)\n\t{\n\t\tquint32_t tsn = sctp->i.ctsn+1;\n\t\tif (!(sctp->i.received[(tsn>>3)%sizeof(sctp->i.received)] & 1<<(tsn&7)))\n\t\t\tbreak;\n\t\t//advance our cumulative ack.\n\t\tsctp->i.received[(tsn>>3)%sizeof(sctp->i.received)] &= ~(1<<(tsn&7));\n\t\tsctp->i.ctsn = tsn;\n\t\tsctp->i.htsn--;\n\t}\n\n\tif (!sctp->o.writable)\n\t{\n\t\tdouble time = Sys_DoubleTime();\n\t\tif (time > sctp->nextreinit)\n\t\t{\n\t\t\tsctp->nextreinit = time + 0.5;\n\t\t\tif (!sctp->cookie)\n\t\t\t{\n\t\t\t\tstruct sctp_chunk_init_s *init = (struct sctp_chunk_init_s*)&pkt[pktlen];\n\t\t\t\tstruct {\n\t\t\t\t\tquint16_t ptype;\n\t\t\t\t\tquint16_t plen;\n\t\t\t\t} *ftsn = (void*)(init+1);\n\t\t\t\th->verifycode = 0;\n\t\t\t\tinit->chunk.type = SCTP_TYPE_INIT;\n\t\t\t\tinit->chunk.flags = 0;\n\t\t\t\tinit->chunk.length = BigShort(sizeof(*init)+sizeof(*ftsn));\n\t\t\t\tinit->verifycode = sctp->i.verifycode;\n\t\t\t\tinit->arwc = BigLong(65535);\n\t\t\t\tinit->numoutstreams = BigShort(2);\n\t\t\t\tinit->numinstreams = BigShort(2);\n\t\t\t\tinit->tsn = BigLong(sctp->o.tsn);\n\t\t\t\tftsn->ptype = BigShort(49152);\n\t\t\t\tftsn->plen = BigShort(sizeof(*ftsn));\n\t\t\t\tpktlen += sizeof(*init) + sizeof(*ftsn);\n\n\t\t\t\th->crc = SCTP_Checksum(h, pktlen);\n\t\t\t\treturn SCTP_PeerSendPacket(sctp, pktlen, h);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tstruct sctp_chunk_s *cookie = (struct sctp_chunk_s*)&pkt[pktlen];\n\n\t\t\t\tif (pktlen + sizeof(*cookie) + sctp->cookiesize > sizeof(pkt))\n\t\t\t\t\treturn NETERR_DISCONNECTED;\n\t\t\t\tcookie->type = SCTP_TYPE_COOKIEECHO;\n\t\t\t\tcookie->flags = 0;\n\t\t\t\tcookie->length = BigShort(sizeof(*cookie)+sctp->cookiesize);\n\t\t\t\tmemcpy(cookie+1, sctp->cookie, sctp->cookiesize);\n\t\t\t\tpktlen += sizeof(*cookie) + sctp->cookiesize;\n\n\t\t\t\th->crc = SCTP_Checksum(h, pktlen);\n\t\t\t\treturn SCTP_PeerSendPacket(sctp, pktlen, h);\n\t\t\t}\n\t\t}\n\n\t\treturn NETERR_CLOGGED;\t//nope, not ready yet\n\t}\n\n\tif (sctp->peerhasfwdtsn && (int)(sctp->o.ctsn-sctp->o.tsn) < -5 && sctp->o.losttsn)\n\t{\t\n\t\tfwd = (struct sctp_chunk_fwdtsn_s*)&pkt[pktlen];\n\t\tfwd->chunk.type = SCTP_TYPE_FORWARDTSN;\n\t\tfwd->chunk.flags = 0;\n\t\tfwd->chunk.length = BigShort(sizeof(*fwd));\n\t\tfwd->tsn = BigLong(sctp->o.tsn-1);\n\n\t\t//we only send unordered unreliables, so this stream stuff here is irrelevant.\n//\t\tfwd->streams[0].sid = sctp->qstreamid;\n//\t\tfwd->streams[0].seq = BigShort(0);\n\t\tpktlen += sizeof(*fwd);\n\t}\n\n\tif (sctp->i.ackneeded >= 2)\n\t{\n\t\tstruct sctp_chunk_sack_s *rsack;\n\t\tstruct sctp_chunk_sack_gap_s {\n\t\t\tuint16_t first;\n\t\t\tuint16_t last;\n\t\t} *rgap;\n\t\tquint32_t otsn;\n\n\t\trsack = (struct sctp_chunk_sack_s*)&pkt[pktlen];\n\t\trsack->chunk.type = SCTP_TYPE_SACK;\n\t\trsack->chunk.flags = 0;\n\t\trsack->chunk.length = BigShort(sizeof(*rsack));\n\t\trsack->tsn = BigLong(sctp->i.ctsn);\n\t\trsack->arwc = BigLong(65535);\n\t\trsack->gaps = 0;\n\t\trsack->dupes = BigShort(0);\n\t\tpktlen += sizeof(*rsack);\n\n\t\trgap = (struct sctp_chunk_sack_gap_s*)&pkt[pktlen];\n\t\tfor (otsn = 0; otsn < sctp->i.htsn; otsn++)\n\t\t{\n\t\t\tquint32_t tsn = sctp->i.ctsn+otsn;\n\t\t\tif (!(sctp->i.received[(tsn>>3)%sizeof(sctp->i.received)] & 1<<(tsn&7)))\n\t\t\t\tcontinue;\t//missing, don't report it in the 'gaps'... yeah, backwards naming.\n\t\t\tif (rsack->gaps && rgap[-1].last == otsn-1)\n\t\t\t\trgap[-1].last = otsn;\t//merge into the last one.\n\t\t\telse\n\t\t\t{\n\t\t\t\trgap->first = otsn;\t//these values are Offset from the Cumulative TSN, to save storage.\n\t\t\t\trgap->last = otsn;\n\t\t\t\trgap++;\n\t\t\t\trsack->gaps++;\n\t\t\t\tpktlen += sizeof(*rgap);\n\t\t\t\tif (pktlen >= 500)\n\t\t\t\t\tbreak;\t//might need fragmentation... just stop here.\n\t\t\t}\n\t\t}\n\t\tfor (otsn = 0, rgap = (struct sctp_chunk_sack_gap_s*)&pkt[pktlen]; otsn < rsack->gaps; otsn++)\n\t\t{\n\t\t\trgap[otsn].first = BigShort(rgap[otsn].first);\n\t\t\trgap[otsn].last = BigShort(rgap[otsn].last);\n\t\t}\n\t\trsack->gaps = BigShort(rsack->gaps);\n\n\t\tsctp->i.ackneeded = 0;\n\t}\n\n\tif (pktlen + sizeof(*d) + length >= 500 && length && pktlen != sizeof(*h))\n\t{\t//probably going to result in fragmentation issues. send separate packets.\n\t\th->crc = SCTP_Checksum(h, pktlen);\n\t\tSCTP_PeerSendPacket(sctp, pktlen, h);\n\n\t\t//reset to the header\n\t\tpktlen = sizeof(*h);\n\t}\n\n\tif (length)\n\t{\n\t\td = (void*)&pkt[pktlen];\n\t\td->chunk.type = SCTP_TYPE_DATA;\n\t\td->chunk.flags = 3|4;\n\t\td->chunk.length = BigShort(sizeof(*d) + length);\n\t\td->tsn = BigLong(sctp->o.tsn++);\n\t\td->sid = sctp->qstreamid;\n\t\td->seq = BigShort(0); //not needed for unordered\n\t\td->ppid = BigLong(SCTP_PPID_DATA);\n\t\tmemcpy(d+1, data, length);\n\t\tpktlen += sizeof(*d) + length;\n\n\t\t//chrome insists on pointless padding at the end. its annoying.\n\t\twhile(pktlen&3)\n\t\t\tpkt[pktlen++]=0;\n\t}\n\tif (pktlen == sizeof(*h))\n\t\treturn NETERR_SENT; //nothing to send...\n\n\th->crc = SCTP_Checksum(h, pktlen);\n\treturn SCTP_PeerSendPacket(sctp, pktlen, h);\n}\n\nstatic void SCTP_DecodeDCEP(sctp_t *sctp, qbyte *resp)\n{\t//send an ack...\n\tsize_t pktlen = 0;\n\tstruct sctp_header_s *h = (void*)resp;\n\tstruct sctp_chunk_data_s *d;\n\tchar *data = \"\\02\";\n\tsize_t length = 1; //*sigh*...\n\n\tstruct\n\t{\n\t\tqbyte type;\n\t\tqbyte chantype;\n\t\tquint16_t priority;\n\t\tquint32_t relparam;\n\t\tquint16_t labellen;\n\t\tquint16_t protocollen;\n\t} *dcep = (void*)sctp->i.r.buf;\n\n\tif (dcep->type == 3)\n\t{\n\t\tchar *label = (qbyte*)(dcep+1);\n\t\tchar *prot = label + strlen(label)+1;\n\n\t\tsctp->qstreamid = sctp->i.r.sid;\n\t\tif (net_ice_debug.ival >= 1)\n\t\t\tCon_Printf(S_COLOR_GRAY\"[%s]: New SCTP Channel: \\\"%s\\\" (%s)\\n\", sctp->friendlyname, label, prot);\n\n\t\th->dstport = sctp->peerport;\n\t\th->srcport = sctp->myport;\n\t\th->verifycode = sctp->o.verifycode;\n\t\tpktlen += sizeof(*h);\n\n\t\tpktlen = (pktlen+3)&~3;\t//pad.\n\t\td = (void*)&resp[pktlen];\n\t\td->chunk.type = SCTP_TYPE_DATA;\n\t\td->chunk.flags = 3;\n\t\td->chunk.length = BigShort(sizeof(*d) + length);\n\t\td->tsn = BigLong(sctp->o.tsn++);\n\t\td->sid = sctp->qstreamid;\n\t\td->seq = BigShort(0); //not needed for unordered\n\t\td->ppid = BigLong(SCTP_PPID_DCEP);\n\t\tmemcpy(d+1, data, length);\n\t\tpktlen += sizeof(*d) + length;\n\n\t\th->crc = SCTP_Checksum(h, pktlen);\n\t\tSCTP_PeerSendPacket(sctp, pktlen, h);\n\t}\n}\n\nstruct sctp_errorcause_s\n{\n\tquint16_t cause;\n\tquint16_t length;\n};\nstatic void SCTP_ErrorChunk(sctp_t *sctp, const char *errortype, struct sctp_errorcause_s *s, size_t totallen)\n{\n\tquint16_t cc, cl;\n\twhile(totallen > 0)\n\t{\n\t\tif (totallen < sizeof(*s))\n\t\t\treturn;\t//that's an error in its own right\n\t\tcc = BigShort(s->cause);\n\t\tcl = BigShort(s->length);\n\t\tif (totallen < cl)\n\t\t\treturn;\t//err..\n\n\t\tif (net_ice_debug.ival >= 1) switch(cc)\n\t\t{\n\t\tcase 1:\t\tCon_Printf(S_COLOR_GRAY\"[%s]: SCTP %s: Invalid Stream Identifier\\n\",\tsctp->friendlyname, errortype);\tbreak;\n        case 2:\t\tCon_Printf(S_COLOR_GRAY\"[%s]: SCTP %s: Missing Mandatory Parameter\\n\",\tsctp->friendlyname, errortype);\tbreak;\n        case 3:\t\tCon_Printf(S_COLOR_GRAY\"[%s]: SCTP %s: Stale Cookie Error\\n\",\t\t\tsctp->friendlyname, errortype);\tbreak;\n        case 4:\t\tCon_Printf(S_COLOR_GRAY\"[%s]: SCTP %s: Out of Resource\\n\",\t\t\t\tsctp->friendlyname, errortype);\tbreak;\n        case 5:\t\tCon_Printf(S_COLOR_GRAY\"[%s]: SCTP %s: Unresolvable Address\\n\",\t\t\tsctp->friendlyname, errortype);\tbreak;\n        case 6:\t\tCon_Printf(S_COLOR_GRAY\"[%s]: SCTP %s: Unrecognized Chunk Type\\n\",\t\tsctp->friendlyname, errortype);\tbreak;\n        case 7:\t\tCon_Printf(S_COLOR_GRAY\"[%s]: SCTP %s: Invalid Mandatory Parameter\\n\",\tsctp->friendlyname, errortype);\tbreak;\n        case 8:\t\tCon_Printf(S_COLOR_GRAY\"[%s]: SCTP %s: Unrecognized Parameters\\n\",\t\tsctp->friendlyname, errortype);\tbreak;\n        case 9:\t\tCon_Printf(S_COLOR_GRAY\"[%s]: SCTP %s: No User Data\\n\",\t\t\t\t\tsctp->friendlyname, errortype);\tbreak;\n        case 10:\tCon_Printf(S_COLOR_GRAY\"[%s]: SCTP %s: Cookie Received While Shutting Down\\n\",\t\t\tsctp->friendlyname, errortype);\tbreak;\n        case 11:\tCon_Printf(S_COLOR_GRAY\"[%s]: SCTP %s: Restart of an Association with New Addresses\\n\",\tsctp->friendlyname, errortype);\tbreak;\n        case 12:\tCon_Printf(S_COLOR_GRAY\"[%s]: SCTP %s: User Initiated Abort\\n\",\t\t\tsctp->friendlyname, errortype);\tbreak;\n        case 13:\tCon_Printf(S_COLOR_GRAY\"[%s]: SCTP %s: Protocol Violation [%s]\\n\",\t\tsctp->friendlyname, errortype, (char*)(s+1));\tbreak;\n        default:\tCon_Printf(S_COLOR_GRAY\"[%s]: SCTP %s: Unknown Reason\\n\",\t\t\t\tsctp->friendlyname, errortype);\tbreak;\n\t\t}\n\n\t\ttotallen -= cl;\n\t\ttotallen &= ~3;\n\t\ts = (struct sctp_errorcause_s*)((qbyte*)s + ((cl+3)&~3));\n\t}\n}\n\nvoid SCTP_Decode(sctp_t *sctp, ftenet_connections_t *col)\n{\n\tqbyte resp[4096];\n\n\tqbyte *msg = net_message.data;\n\tqbyte *msgend = msg+net_message.cursize;\n\tstruct sctp_header_s *h = (struct sctp_header_s*)msg;\n\tstruct sctp_chunk_s *c = (struct sctp_chunk_s*)(h+1);\n\tquint16_t clen;\n\tif ((qbyte*)c+1 > msgend)\n\t\treturn;\t//runt\n\tif (h->dstport != sctp->myport)\n\t\treturn;\t//not for us...\n\tif (h->srcport != sctp->peerport)\n\t\treturn; //not from them... we could check for a INIT but its over dtls anyway so why give a damn.\n\tif (h->verifycode != ((c->type == SCTP_TYPE_INIT)?0:sctp->i.verifycode))\n\t\treturn;\t//wrong cookie... (be prepared to parse dupe inits if our ack got lost...\n\tif (h->crc != SCTP_Checksum(h, net_message.cursize))\n\t\treturn;\t//crc wrong. assume corruption.\n\tif (net_message.cursize&3)\n\t{\n\t\tif (net_ice_debug.ival >= 2)\n\t\t\tCon_Printf(S_COLOR_GRAY\"[%s]: SCTP: packet not padded\\n\", sctp->friendlyname);\n\t\treturn;\t//mimic chrome, despite it being pointless.\n\t}\n\n\t//passed the simple header checks, spend a memcpy...\n\tmsg = alloca(net_message.cursize);\n\tmemcpy(msg, net_message.data, net_message.cursize);\n\tmsgend = msg+net_message.cursize;\n\th = (struct sctp_header_s*)msg;\n\tc = (struct sctp_chunk_s*)(h+1);\n\n\twhile ((qbyte*)(c+1) <= msgend)\n\t{\n\t\tclen = BigShort(c->length);\n\t\tif ((qbyte*)c + clen > msgend || clen < sizeof(*c))\n\t\t{\n\t\t\tCon_Printf(CON_ERROR\"Corrupt SCTP message\\n\");\n\t\t\tbreak;\n\t\t}\n\t\tsafeswitch(c->type)\n\t\t{\n\t\tcase SCTP_TYPE_DATA:\n\t\t\tif (clen >= sizeof(struct sctp_chunk_data_s))\n\t\t\t{\n\t\t\t\tstruct sctp_chunk_data_s *dc = (void*)c;\n\t\t\t\tquint32_t tsn = BigLong(dc->tsn), u;\n\t\t\t\tqint32_t adv = tsn - sctp->i.ctsn;\n\t\t\t\tsctp->i.ackneeded++;\n\t\t\t\tif (adv >= SCTP_RCVSIZE)\n\t\t\t\t{\n\t\t\t\t\tif (net_ice_debug.ival >= 1)\n\t\t\t\t\t\tCon_Printf(S_COLOR_GRAY\"[%s]: SCTP: Future Packet\\n\", sctp->friendlyname);/*too far in the future. we can't track such things*/\n\t\t\t\t}\n\t\t\t\telse if (adv <= 0)\n\t\t\t\t{\n\t\t\t\t\tif (net_ice_debug.ival >= 2)\n\t\t\t\t\t\tCon_Printf(S_COLOR_GRAY\"[%s]: SCTP: PreCumulative\\n\", sctp->friendlyname);/*already acked this*/\n\t\t\t\t}\n\t\t\t\telse if (sctp->i.received[(tsn>>3)%sizeof(sctp->i.received)] & 1<<(tsn&7))\n\t\t\t\t{\n\t\t\t\t\tif (net_ice_debug.ival >= 2)\n\t\t\t\t\t\tCon_DPrintf(S_COLOR_GRAY\"[%s]: SCTP: Dupe\\n\", sctp->friendlyname);/*already processed it. FIXME: Make a list for the next SACK*/\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tqboolean err = false;\n\n\t\t\t\t\tif (c->flags & 2)\n\t\t\t\t\t{\t//beginning...\n\t\t\t\t\t\tsctp->i.r.firsttns = tsn;\n\t\t\t\t\t\tsctp->i.r.tsn = tsn;\n\t\t\t\t\t\tsctp->i.r.size = 0;\n\t\t\t\t\t\tsctp->i.r.ppid = dc->ppid;\n\t\t\t\t\t\tsctp->i.r.sid = dc->sid;\n\t\t\t\t\t\tsctp->i.r.seq = dc->seq;\n\t\t\t\t\t\tsctp->i.r.toobig = false;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tif (sctp->i.r.tsn != tsn || sctp->i.r.ppid != dc->ppid)\n\t\t\t\t\t\t\terr = true;\n\t\t\t\t\t}\n\t\t\t\t\tif (err)\n\t\t\t\t\t\t;\t//don't corrupt anything in case we get a quick resend that fixes it.\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tsize_t dlen = clen-sizeof(*dc);\n\t\t\t\t\t\tif (adv > sctp->i.htsn)\t//weird maths in case it wraps.\n\t\t\t\t\t\t\tsctp->i.htsn = adv;\n\t\t\t\t\t\tsctp->i.r.tsn++;\n\t\t\t\t\t\tif (sctp->i.r.size + dlen > sizeof(sctp->i.r.buf))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (net_ice_debug.ival >= 2)\n\t\t\t\t\t\t\t\tCon_Printf(S_COLOR_GRAY\"[%s]: SCTP: Oversized\\n\", sctp->friendlyname);\n\t\t\t\t\t\t\tsctp->i.r.toobig = true;\t//reassembled packet was too large, just corrupt it.\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tmemcpy(sctp->i.r.buf+sctp->i.r.size, dc+1, dlen);\t//include the dc header\n\t\t\t\t\t\t\tsctp->i.r.size += dlen;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (c->flags & 1)\t//an ending. we have the complete packet now.\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tfor (u = sctp->i.r.tsn - sctp->i.r.firsttns; u --> 0; )\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\ttsn = sctp->i.r.firsttns + u;\n\t\t\t\t\t\t\t\tsctp->i.received[(tsn>>3)%sizeof(sctp->i.received)] |= 1<<(tsn&7);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (sctp->i.r.toobig)\n\t\t\t\t\t\t\t\t;/*ignore it when it cannot be handled*/\n\t\t\t\t\t\t\telse if (sctp->i.r.ppid == BigLong(SCTP_PPID_DATA))\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tmemmove(net_message.data, sctp->i.r.buf, sctp->i.r.size);\n\t\t\t\t\t\t\t\tnet_message.cursize = sctp->i.r.size;\n\t\t\t\t\t\t\t\tcol->ReadGamePacket();\n\t\t\t\t\t\t\t\tif (net_message.cursize != sctp->i.r.size)\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tnet_message.cursize = 0;\n\t\t\t\t\t\t\t\t\treturn;\t//something weird happened...\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse if (sctp->i.r.ppid == BigLong(SCTP_PPID_DCEP))\n\t\t\t\t\t\t\t\tSCTP_DecodeDCEP(sctp, resp);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t//FIXME: we don't handle reordering properly at all.\n\n//\t\t\t\t\tif (c->flags & 4)\n//\t\t\t\t\t\tCon_Printf(\"\\tUnordered\\n\");\n//\t\t\t\t\tCon_Printf(\"\\tStream Id %i\\n\", BigShort(dc->sid));\n//\t\t\t\t\tCon_Printf(\"\\tStream Seq %i\\n\", BigShort(dc->seq));\n//\t\t\t\t\tCon_Printf(\"\\tPPID %i\\n\", BigLong(dc->ppid));\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\t\tcase SCTP_TYPE_INIT:\n\t\tcase SCTP_TYPE_INITACK:\n\t\t\tif (clen >= sizeof(struct sctp_chunk_init_s))\n\t\t\t{\n\t\t\t\tqboolean isack = c->type==SCTP_TYPE_INITACK;\n\t\t\t\tstruct sctp_chunk_init_s *init = (void*)c;\n\t\t\t\tstruct {\n\t\t\t\t\t\tquint16_t ptype;\n\t\t\t\t\t\tquint16_t plen;\n\t\t\t\t} *p = (void*)(init+1);\n\n\t\t\t\tsctp->i.ctsn = BigLong(init->tsn)-1;\n\t\t\t\tsctp->i.htsn = 0;\n\t\t\t\tsctp->o.verifycode = init->verifycode;\n\t\t\t\t(void)BigLong(init->arwc);\n\t\t\t\t(void)BigShort(init->numoutstreams);\n\t\t\t\t(void)BigShort(init->numinstreams);\n\n\t\t\t\twhile ((qbyte*)p+sizeof(*p) <= (qbyte*)c+clen)\n\t\t\t\t{\n\t\t\t\t\tunsigned short ptype = BigShort(p->ptype);\n\t\t\t\t\tunsigned short plen = BigShort(p->plen);\n\t\t\t\t\tswitch(ptype)\n\t\t\t\t\t{\n\t\t\t\t\tcase 7:\t//init cookie\n\t\t\t\t\t\tif (sctp->cookie)\n\t\t\t\t\t\t\tZ_Free(sctp->cookie);\n\t\t\t\t\t\tsctp->cookiesize = plen - sizeof(*p);\n\t\t\t\t\t\tsctp->cookie = Z_Malloc(sctp->cookiesize);\n\t\t\t\t\t\tmemcpy(sctp->cookie, p+1, sctp->cookiesize);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 32773:\t//Padding\n\t\t\t\t\tcase 32776:\t//ASCONF\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 49152:\n\t\t\t\t\t\tsctp->peerhasfwdtsn = true;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tif (net_ice_debug.ival >= 2)\n\t\t\t\t\t\t\tCon_Printf(S_COLOR_GRAY\"[%s]: SCTP: Found unknown init parameter %i||%#x\\n\", sctp->friendlyname, ptype, ptype);\n\n\t\t\t\t\t\tif (ptype&0x4000)\n\t\t\t\t\t\t\t; //FIXME: SCTP_TYPE_ERROR(6,\"Unrecognized Chunk Type\")\n\t\t\t\t\t\tif (!(ptype&0x8000))\n\t\t\t\t\t\t\treturn;\t//'do not process nay further chunks'\n\t\t\t\t\t\t//otherwise parse the next as normal.\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tp = (void*)((qbyte*)p + ((plen+3)&~3));\n\t\t\t\t}\n\n\t\t\t\tif (isack)\n\t\t\t\t{\n\t\t\t\t\tsctp->nextreinit = 0;\n\t\t\t\t\tif (sctp->cookie)\n\t\t\t\t\t\tSCTP_Transmit(sctp, NULL, 0);\t//make sure we send acks occasionally even if we have nothing else to say.\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tstruct sctp_header_s *rh = (void*)resp;\n\t\t\t\t\tstruct sctp_chunk_init_s *rinit = (void*)(rh+1);\n\t\t\t\t\tstruct {\n\t\t\t\t\t\tquint16_t ptype;\n\t\t\t\t\t\tquint16_t plen;\n\t\t\t\t\t\tstruct {\n\t\t\t\t\t\t\tqbyte data[16];\n\t\t\t\t\t\t} cookie;\n\t\t\t\t\t} *rinitcookie = (void*)(rinit+1);\n\t\t\t\t\tstruct {\n\t\t\t\t\t\tquint16_t ptype;\n\t\t\t\t\t\tquint16_t plen;\n\t\t\t\t\t} *rftsn = (void*)(rinitcookie+1);\n\t\t\t\t\tqbyte *end = sctp->peerhasfwdtsn?(void*)(rftsn+1):(void*)(rinitcookie+1);\n\n\t\t\t\t\trh->srcport = sctp->myport;\n\t\t\t\t\trh->dstport = sctp->peerport;\n\t\t\t\t\trh->verifycode = sctp->o.verifycode;\n\t\t\t\t\trh->crc = 0;\n\t\t\t\t\t*rinit = *init;\n\t\t\t\t\trinit->chunk.type = SCTP_TYPE_INITACK;\n\t\t\t\t\trinit->chunk.flags = 0;\n\t\t\t\t\trinit->chunk.length = BigShort(end-(qbyte*)rinit);\n\t\t\t\t\trinit->verifycode = sctp->i.verifycode;\n\t\t\t\t\trinit->arwc = BigLong(65536);\n\t\t\t\t\trinit->numoutstreams = init->numoutstreams;\n\t\t\t\t\trinit->numinstreams = init->numinstreams;\n\t\t\t\t\trinit->tsn = BigLong(sctp->o.tsn);\n\t\t\t\t\trinitcookie->ptype = BigShort(7);\n\t\t\t\t\trinitcookie->plen = BigShort(sizeof(*rinitcookie));\n\t\t\t\t\tmemcpy(&rinitcookie->cookie, \"deadbeefdeadbeef\", sizeof(rinitcookie->cookie));\t//frankly the contents of the cookie are irrelevant to anything. we've already verified the peer's ice pwd/ufrag stuff as well as their dtls certs etc.\n\t\t\t\t\trftsn->ptype = BigShort(49152);\n\t\t\t\t\trftsn->plen = BigShort(sizeof(*rftsn));\n\n\t\t\t\t\t//complete. calc the proper crc and send it off.\n\t\t\t\t\trh->crc = SCTP_Checksum(rh, end-resp);\n\t\t\t\t\tSCTP_PeerSendPacket(sctp, end-resp, rh);\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\t\tcase SCTP_TYPE_SACK:\n\t\t\tif (clen >= sizeof(struct sctp_chunk_sack_s))\n\t\t\t{\n\t\t\t\tstruct sctp_chunk_sack_s *sack = (void*)c;\n\t\t\t\tquint32_t tsn = BigLong(sack->tsn);\n\t\t\t\tsctp->o.ctsn = tsn;\n\n\t\t\t\tsctp->o.losttsn = BigShort(sack->gaps);\t//if there's a gap then they're telling us they got a later one.\n\n\t\t\t\t//Con_Printf(CON_ERROR\"Sack %#x (%i in flight)\\n\"\n\t\t\t\t//\t\t\t\"\\tgaps: %i, dupes %i\\n\",\n\t\t\t\t//\t\t\ttsn, sctp->o.tsn-tsn,\n\t\t\t\t//\t\t\tBigShort(sack->gaps), BigShort(sack->dupes));\n\t\t\t}\n\t\t\tbreak;\n\t\tcase SCTP_TYPE_PING:\n\t\t\tif (clen >= sizeof(struct sctp_chunk_s))\n\t\t\t{\n\t\t\t\tstruct sctp_chunk_s *ping = (void*)c;\n\t\t\t\tstruct sctp_header_s *pongh = Z_Malloc(sizeof(*pongh) + clen);\n\n\t\t\t\tpongh->srcport = sctp->myport;\n\t\t\t\tpongh->dstport = sctp->peerport;\n\t\t\t\tpongh->verifycode = sctp->o.verifycode;\n\t\t\t\tpongh->crc = 0;\n\t\t\t\tmemcpy(pongh+1, ping, clen);\n\t\t\t\t((struct sctp_chunk_s*)(pongh+1))->type = SCTP_TYPE_PONG;\n\n\t\t\t\t//complete. calc the proper crc and send it off.\n\t\t\t\tpongh->crc = SCTP_Checksum(pongh, sizeof(*pongh) + clen);\n\t\t\t\tSCTP_PeerSendPacket(sctp, sizeof(*pongh) + clen, pongh);\n\t\t\t\tZ_Free(pongh);\n\t\t\t}\n\t\t\tbreak;\n//\t\tcase SCTP_TYPE_PONG:\t//we don't send pings\n\t\tcase SCTP_TYPE_ABORT:\n\t\t\tSCTP_ErrorChunk(sctp, \"Abort\", (struct sctp_errorcause_s*)(c+1), clen-sizeof(*c));\n\t\t\tif (sctp->icestate)\n\t\t\t\tICE_SetFailed(sctp->icestate, \"SCTP Abort\");\n\t\t\tbreak;\n\t\tcase SCTP_TYPE_SHUTDOWN:\t//FIXME. we should send an ack...\n\t\t\tif (sctp->icestate)\n\t\t\t\tICE_SetFailed(sctp->icestate, \"SCTP Shutdown\");\n\t\t\tif (net_ice_debug.ival >= 1)\n\t\t\t\tCon_Printf(S_COLOR_GRAY\"[%s]: SCTP: Shutdown\\n\", sctp->friendlyname);\n\t\t\tbreak;\n//\t\tcase SCTP_TYPE_SHUTDOWNACK:\t//we don't send shutdowns, cos we're lame like that...\n\t\tcase SCTP_TYPE_ERROR:\n\t\t\t//not fatal...\n\t\t\tSCTP_ErrorChunk(sctp, \"Error\", (struct sctp_errorcause_s*)(c+1), clen-sizeof(*c));\n\t\t\tbreak;\n\t\tcase SCTP_TYPE_COOKIEECHO:\n\t\t\tif (clen >= sizeof(struct sctp_chunk_s))\n\t\t\t{\n\t\t\t\tstruct sctp_header_s *rh = (void*)resp;\n\t\t\t\tstruct sctp_chunk_s *rack = (void*)(rh+1);\n\t\t\t\tqbyte *end = (void*)(rack+1);\n\n\t\t\t\trh->srcport = sctp->myport;\n\t\t\t\trh->dstport = sctp->peerport;\n\t\t\t\trh->verifycode = sctp->o.verifycode;\n\t\t\t\trh->crc = 0;\n\t\t\t\track->type = SCTP_TYPE_COOKIEACK;\n\t\t\t\track->flags = 0;\n\t\t\t\track->length = BigShort(sizeof(*rack));\n\n\t\t\t\t//complete. calc the proper crc and send it off.\n\t\t\t\trh->crc = SCTP_Checksum(rh, end-resp);\n\t\t\t\tSCTP_PeerSendPacket(sctp, end-resp, rh);\n\n\t\t\t\tsctp->o.writable = true;\t//channel SHOULD now be open for data.\n\t\t\t}\n\t\t\tbreak;\n\t\tcase SCTP_TYPE_COOKIEACK:\n\t\t\tsctp->o.writable = true;\t//we know the other end is now open.\n\t\t\tbreak;\n\t\tcase SCTP_TYPE_PAD:\n\t\t\t//don't care.\n\t\t\tbreak;\n\t\tcase SCTP_TYPE_FORWARDTSN:\n\t\t\tif (clen >= sizeof(struct sctp_chunk_fwdtsn_s))\n\t\t\t{\n\t\t\t\tstruct sctp_chunk_fwdtsn_s *fwd = (void*)c;\n\t\t\t\tquint32_t tsn = BigLong(fwd->tsn), count;\n\t\t\t\tcount = tsn - sctp->i.ctsn;\n\t\t\t\tif ((int)count < 0)\n\t\t\t\t\tbreak;\t//overflow? don't go backwards.\n\t\t\t\tif (count > 1024)\n\t\t\t\t\tcount = 1024; //don't advance too much in one go. we'd block and its probably an error anyway.\n\t\t\t\twhile(count --> 0)\n\t\t\t\t{\n\t\t\t\t\ttsn = ++sctp->i.ctsn;\n\t\t\t\t\tsctp->i.received[(tsn>>3)%sizeof(sctp->i.received)] &= ~(1<<(tsn&7));\n\t\t\t\t\tif (sctp->i.htsn)\n\t\t\t\t\t\tsctp->i.htsn--;\n\t\t\t\t\tsctp->i.ackneeded++;\t//flag for a sack if we actually completed something here.\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n//\t\tcase SCTP_TYPE_SHUTDOWNDONE:\n\t\tsafedefault:\n\t\t\t//no idea what this chunk is, just ignore it...\n\t\t\tif (net_ice_debug.ival >= 1)\n\t\t\t\tCon_Printf(S_COLOR_GRAY\"[%s]: SCTP: Unsupported chunk %i\\n\", sctp->friendlyname, c->type);\n\n\t\t\tswitch (c->type>>6)\n\t\t\t{\n\t\t\tcase 0:\n\t\t\t\tclen = (qbyte*)msgend - (qbyte*)c;\t//'do not process any further chunks'\n\t\t\t\tbreak;\n\t\t\tcase 1:\n\t\t\t\tclen = (qbyte*)msgend - (qbyte*)c;\t//'do not process any further chunks'\n\t\t\t\t/*FIXME: SCTP_TYPE_ERROR(6,\"Unrecognized Chunk Type\")*/\n\t\t\t\tbreak;\n\t\t\tcase 2:\n\t\t\t\t//silently ignore it\n\t\t\t\tbreak;\n\t\t\tcase 3:\n\t\t\t\t//ignore-with-error\n\t\t\t\t/*FIXME: SCTP_TYPE_ERROR(6,\"Unrecognized Chunk Type\")*/\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t\tc = (struct sctp_chunk_s*)((qbyte*)c + ((clen+3)&~3));\t//next chunk is 4-byte aligned.\n\t}\n\n\tif (sctp->i.ackneeded >= 5)\n\t\tSCTP_Transmit(sctp, NULL, 0);\t//make sure we send acks occasionally even if we have nothing else to say.\n\n\t//we already made sense of it all.\n\tnet_message.cursize = 0;\n}\n\n#if 0\nqboolean SCTP_Handshake(const dtlsfuncs_t *dtlsfuncs, void *dtlsstate, sctp_t **out)\n{\n\tconst int myport = ICELITE_SCTP_PORT;\n\tstruct cookiedata_s\n\t{\n\t\tqbyte peerhasfwdtsn;\n\t\tint overifycode;\n\t\tint iverifycode;\n\t\tint ictsn;\n\t\tint otsn;\n//\t\tint checkcode;\n\t};\n\t//if this is an sctp init packet, send a cookie.\n\t//if its an initack then create the new state.\n\tqbyte resp[4096];\n\n\tqbyte *msg = net_message.data;\n\tqbyte *msgend = msg+net_message.cursize;\n\tstruct sctp_header_s *h = (struct sctp_header_s*)msg;\n\tstruct sctp_chunk_s *c = (struct sctp_chunk_s*)(h+1);\n\tquint16_t clen;\n\tif (net_message.cursize&3)\n\t\treturn false;\n\tif ((qbyte*)c+1 > msgend)\n\t\treturn false;\t//runt\n\tif (h->dstport != htons(myport))\n\t\treturn false;\t//not for us...\n\tclen = BigShort(c->length);\n\tif ((qbyte*)c + ((clen+3)&~3) == msgend)\t//don't allow multiple chucks\n\tswitch(c->type)\n\t{\n\tdefault:\n\t\treturn false;\t//not the right kind of packet.\n\tcase SCTP_TYPE_COOKIEECHO:\n\t\tif (clen == sizeof(struct sctp_chunk_s)+sizeof(struct cookiedata_s))\n\t\t{\n\t\t\tstruct cookiedata_s *cookie = (struct cookiedata_s *)(c+1);\n\t\t\tsctp_t *sctp;\n\t\t\tstruct sctp_header_s *rh = (void*)resp;\n\t\t\tstruct sctp_chunk_s *rack = (void*)(rh+1);\n\t\t\tqbyte *end = (void*)(rack+1);\n\t\t\tif (h->verifycode == cookie->iverifycode)\n\t\t\tif (h->crc == SCTP_Checksum(h, net_message.cursize))\t//make sure the crc matches.\n\t\t\t{\t//looks okay.\n\t\t\t\tNET_AdrToString(resp, sizeof(resp), &net_from);\n\n\t\t\t\t*out = sctp = Z_Malloc(sizeof(*sctp) + strlen(resp));\n\t\t\t\tsctp->friendlyname = strcpy((char*)(sctp+1), resp);\n\t\t\t\tsctp->icestate = NULL;\n\t\t\t\tsctp->dtlsfuncs = dtlsfuncs;\n\t\t\t\tsctp->dtlsstate = dtlsstate;\n\n\t\t\t\tsctp->myport = h->dstport;\n\t\t\t\tsctp->peerport = h->srcport;\n\t\t\t\tsctp->o.tsn = cookie->otsn;\n\t\t\t\tsctp->i.ctsn = cookie->ictsn;\n\t\t\t\tsctp->o.verifycode = cookie->overifycode;\n\t\t\t\tsctp->i.verifycode = cookie->iverifycode;\n\t\t\t\tsctp->peerhasfwdtsn = cookie->peerhasfwdtsn;\n\t\t\t\tsctp->o.writable = true;\t//channel SHOULD now be open for data (once it gets the ack anyway).\n\n\t\t\t\t//let our peer know too.\n\t\t\t\trh->srcport = sctp->myport;\n\t\t\t\trh->dstport = sctp->peerport;\n\t\t\t\trh->verifycode = sctp->o.verifycode;\n\t\t\t\trh->crc = 0;\n\t\t\t\track->type = SCTP_TYPE_COOKIEACK;\n\t\t\t\track->flags = 0;\n\t\t\t\track->length = BigShort(sizeof(*rack));\n\n\t\t\t\t//complete. calc the proper crc and send it off.\n\t\t\t\trh->crc = SCTP_Checksum(rh, end-resp);\n\t\t\t\tSCTP_PeerSendPacket(sctp, end-resp, rh);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\tbreak;\n\tcase SCTP_TYPE_INIT:\n\t\tif (h->verifycode == 0)\t//this must be 0 for inits.\n\t\tif (clen >= sizeof(struct sctp_chunk_init_s))\n\t\tif (h->crc == SCTP_Checksum(h, net_message.cursize))\t//make sure the crc matches.\n\t\t{\n\t\t\tstruct sctp_chunk_init_s *init = (void*)c;\n\t\t\tstruct {\n\t\t\t\t\tquint16_t ptype;\n\t\t\t\t\tquint16_t plen;\n\t\t\t} *p = (void*)(init+1);\n\n\t\t\tstruct cookiedata_s cookie = {0};\n\n\t\t\tcookie.otsn = rand() ^ (rand()<<16);\n\t\t\tSys_RandomBytes((void*)&cookie.iverifycode, sizeof(cookie.iverifycode));\n\t\t\tcookie.ictsn = BigLong(init->tsn)-1;\n\t\t\tcookie.overifycode = init->verifycode;\n\t\t\t(void)BigLong(init->arwc);\n\t\t\t(void)BigShort(init->numoutstreams);\n\t\t\t(void)BigShort(init->numinstreams);\n\n\t\t\twhile ((qbyte*)p+sizeof(*p) <= (qbyte*)c+clen)\n\t\t\t{\n\t\t\t\tunsigned short ptype = BigShort(p->ptype);\n\t\t\t\tunsigned short plen = BigShort(p->plen);\n\t\t\t\tswitch(ptype)\n\t\t\t\t{\n\t\t\t\tcase 32776:\t//ASCONF\n\t\t\t\t\tbreak;\n\t\t\t\tcase 49152:\n\t\t\t\t\tcookie.peerhasfwdtsn = true;\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tif (net_ice_debug.ival >= 2)\n\t\t\t\t\t\tCon_Printf(S_COLOR_GRAY\"[%s]: SCTP: Found unknown init parameter %i||%#x\\n\", NET_AdrToString(resp,sizeof(resp), &net_from), ptype, ptype);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tp = (void*)((qbyte*)p + ((plen+3)&~3));\n\t\t\t}\n\n\t\t\t{\n\t\t\t\tstruct sctp_header_s *rh = (void*)resp;\n\t\t\t\tstruct sctp_chunk_init_s *rinit = (void*)(rh+1);\n\t\t\t\tstruct {\n\t\t\t\t\tquint16_t ptype;\n\t\t\t\t\tquint16_t plen;\n\t\t\t\t\tstruct cookiedata_s cookie;\n\t\t\t\t} *rinitcookie = (void*)(rinit+1);\n\t\t\t\tstruct {\n\t\t\t\t\tquint16_t ptype;\n\t\t\t\t\tquint16_t plen;\n\t\t\t\t} *rftsn = (void*)(rinitcookie+1);\n\t\t\t\tqbyte *end = cookie.peerhasfwdtsn?(void*)(rftsn+1):(void*)(rinitcookie+1);\n\n\t\t\t\trh->srcport = h->dstport;\n\t\t\t\trh->dstport = h->srcport;\n\t\t\t\trh->verifycode = init->verifycode;\n\t\t\t\trh->crc = 0;\n\t\t\t\t*rinit = *init;\n\t\t\t\trinit->chunk.type = SCTP_TYPE_INITACK;\n\t\t\t\trinit->chunk.flags = 0;\n\t\t\t\trinit->chunk.length = BigShort(end-(qbyte*)rinit);\n\t\t\t\trinit->verifycode = cookie.iverifycode;\n\t\t\t\trinit->arwc = BigLong(65536);\n\t\t\t\trinit->numoutstreams = init->numoutstreams;\n\t\t\t\trinit->numinstreams = init->numinstreams;\n\t\t\t\trinit->tsn = BigLong(cookie.otsn);\n\t\t\t\trinitcookie->ptype = BigShort(7);\n\t\t\t\trinitcookie->plen = BigShort(sizeof(*rinitcookie));\n\t\t\t\tmemcpy(&rinitcookie->cookie, &cookie, sizeof(rinitcookie->cookie));\t//frankly the contents of the cookie are irrelevant to anything. we've already verified the peer's ice pwd/ufrag stuff as well as their dtls certs etc.\n\t\t\t\trftsn->ptype = BigShort(49152);\n\t\t\t\trftsn->plen = BigShort(sizeof(*rftsn));\n\n\t\t\t\t//complete. calc the proper crc and send it off.\n\t\t\t\trh->crc = SCTP_Checksum(rh, end-resp);\n\t\t\t\tdtlsfuncs->Transmit(dtlsstate, resp, end-resp);\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\t\tbreak;\n\t}\n\n\treturn false;\n}\n#endif\n\n//========================================\n#endif\n\n#if defined(SUPPORT_ICE) || (defined(MASTERONLY) && defined(AVAIL_ZLIB))\nqboolean ICE_WasStun(ftenet_connections_t *col)\n{\n#ifdef SUPPORT_ICE\n\tif (net_from.type == NA_ICE)\n\t\treturn false;\t//this stuff over an ICE connection doesn't make sense.\n#endif\n//\tif (net_from.prot != NP_DGRAM)\n//\t\treturn false;\n\n#if defined(HAVE_CLIENT) && defined(VOICECHAT)\n\tif (col == cls.sockets)\n\t{\n\t\tif (NET_RTP_Parse())\n\t\t\treturn true;\n\t}\n#endif\n\n\tif ((net_from.type == NA_IP || net_from.type == NA_IPV6) && net_message.cursize >= 20 && *net_message.data<2)\n\t{\n\t\tstunhdr_t *stun = (stunhdr_t*)net_message.data;\n\t\tint stunlen = BigShort(stun->msglen);\n#ifdef SUPPORT_ICE\n\t\tif ((stun->msgtype == BigShort(STUN_BINDING|STUN_REPLY) || stun->msgtype == BigShort(STUN_BINDING|STUN_ERROR)) && net_message.cursize == stunlen + sizeof(*stun))\n\t\t{\n\t\t\t//binding reply (or error)\n\t\t\tnetadr_t adr = net_from;\n\t\t\tchar xor[16];\n\t\t\tshort portxor;\n\t\t\tstunattr_t *attr = (stunattr_t*)(stun+1);\n\t\t\tint alen;\n\t\t\tunsigned short attrval;\n\t\t\tint err = 0;\n\t\t\tchar errmsg[64];\n\t\t\t*errmsg = 0;\n\n\t\t\tadr.type = NA_INVALID;\n\t\t\twhile(stunlen)\n\t\t\t{\n\t\t\t\tstunlen -= sizeof(*attr);\n\t\t\t\talen = (unsigned short)BigShort(attr->attrlen);\n\t\t\t\tif (alen > stunlen)\n\t\t\t\t\treturn false;\n\t\t\t\tstunlen -= (alen+3)&~3;\n\t\t\t\tattrval = BigShort(attr->attrtype);\n\t\t\t\tswitch(attrval)\n\t\t\t\t{\n\t\t\t\tcase STUNATTR_USERNAME:\n\t\t\t\tcase STUNATTR_MSGINTEGRITIY_SHA1:\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tif (attrval & 0x8000)\n\t\t\t\t\t\tbreak;\t//okay to ignore\n\t\t\t\t\treturn true;\n\t\t\t\tcase STUNATTR_MAPPED_ADDRESS:\n\t\t\t\t\tif (adr.type != NA_INVALID)\n\t\t\t\t\t\tbreak;\t//ignore it if we already got an address...\n\t\t\t\t//fallthrough\n\t\t\t\tcase STUNATTR_XOR_MAPPED_ADDRESS:\n\t\t\t\t\tif (attrval == STUNATTR_XOR_MAPPED_ADDRESS)\n\t\t\t\t\t{\n\t\t\t\t\t\tportxor = *(short*)&stun->magiccookie;\n\t\t\t\t\t\tmemcpy(xor, &stun->magiccookie, sizeof(xor));\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tportxor = 0;\n\t\t\t\t\t\tmemset(xor, 0, sizeof(xor));\n\t\t\t\t\t}\n\t\t\t\t\tif (alen == 8 && ((qbyte*)attr)[5] == 1)\t\t//ipv4\n\t\t\t\t\t{\n\t\t\t\t\t\tadr.type = NA_IP;\n\t\t\t\t\t\tadr.port = (((short*)attr)[3]) ^ portxor;\n\t\t\t\t\t\t*(int*)adr.address.ip = *(int*)(&((qbyte*)attr)[8]) ^ *(int*)xor;\n\t\t\t\t\t}\n\t\t\t\t\telse if (alen == 20 && ((qbyte*)attr)[5] == 2)\t//ipv6\n\t\t\t\t\t{\n\t\t\t\t\t\tadr.type = NA_IPV6;\n\t\t\t\t\t\tadr.port = (((short*)attr)[3]) ^ portxor;\n\t\t\t\t\t\t((int*)adr.address.ip6)[0] = ((int*)&((qbyte*)attr)[8])[0] ^ ((int*)xor)[0];\n\t\t\t\t\t\t((int*)adr.address.ip6)[1] = ((int*)&((qbyte*)attr)[8])[1] ^ ((int*)xor)[1];\n\t\t\t\t\t\t((int*)adr.address.ip6)[2] = ((int*)&((qbyte*)attr)[8])[2] ^ ((int*)xor)[2];\n\t\t\t\t\t\t((int*)adr.address.ip6)[3] = ((int*)&((qbyte*)attr)[8])[3] ^ ((int*)xor)[3];\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase STUNATTR_ERROR_CODE:\n\t\t\t\t\t{\n\t\t\t\t\t\tunsigned short len = BigShort(attr->attrlen)-4;\n\t\t\t\t\t\tif (len > sizeof(errmsg)-1)\n\t\t\t\t\t\t\tlen = sizeof(errmsg)-1;\n\t\t\t\t\t\tmemcpy(errmsg, &((qbyte*)attr)[8], len);\n\t\t\t\t\t\terrmsg[len] = 0;\n\t\t\t\t\t\tif (err==0)\n\t\t\t\t\t\t\terr = (((qbyte*)attr)[6]*100) + (((qbyte*)attr)[7]%100);\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\talen = (alen+3)&~3;\n\t\t\t\tattr = (stunattr_t*)((char*)(attr+1) + alen);\n\t\t\t}\n\n\t\t\tif (err)\n\t\t\t{\n\t\t\t\tchar sender[256];\n\t\t\t\tif (net_ice_debug.ival >= 1)\n\t\t\t\t\tCon_Printf(\"%s: Stun error code %u : %s\\n\", NET_AdrToString(sender, sizeof(sender), &net_from), err, errmsg);\n\t\t\t}\n\t\t\telse if (adr.type!=NA_INVALID && !err)\n\t\t\t{\n\t\t\t\tstruct icestate_s *con;\n\t\t\t\tfor (con = icelist; con; con = con->next)\n\t\t\t\t{\n\t\t\t\t\tstruct icecandidate_s *rc;\n\t\t\t\t\tsize_t i;\n\t\t\t\t\tstruct iceserver_s *s;\n\t\t\t\t\tif (con->mode == ICEM_RAW)\n\t\t\t\t\t\tcontinue;\n\n\t\t\t\t\tfor (i = 0; i < con->servers; i++)\n\t\t\t\t\t{\n\t\t\t\t\t\ts = &con->server[i];\n\t\t\t\t\t\tif (NET_CompareAdr(&net_from, &s->addr) &&\ts->stunrnd[0] == stun->transactid[0] &&\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ts->stunrnd[1] == stun->transactid[1] &&\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ts->stunrnd[2] == stun->transactid[2])\n\t\t\t\t\t\t{\t//check to see if this is a new server-reflexive address, which happens when the peer is behind a nat.\n\t\t\t\t\t\t\tfor (rc = con->lc; rc; rc = rc->next)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tif (NET_CompareAdr(&adr, &rc->peer))\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (!rc)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t//netadr_t reladdr;\n\t\t\t\t\t\t\t\t//int relflags;\n\t\t\t\t\t\t\t\t//const char *relpath;\n\t\t\t\t\t\t\t\tint rnd[2];\n\t\t\t\t\t\t\t\tstruct icecandidate_s *src;\t//server Reflexive Candidate\n\t\t\t\t\t\t\t\tchar str[256];\n\t\t\t\t\t\t\t\tsrc = Z_Malloc(sizeof(*src));\n\t\t\t\t\t\t\t\tsrc->next = con->lc;\n\t\t\t\t\t\t\t\tcon->lc = src;\n\t\t\t\t\t\t\t\tsrc->peer = adr;\n\t\t\t\t\t\t\t\tNET_BaseAdrToString(src->info.addr, sizeof(src->info.addr), &adr);\n\t\t\t\t\t\t\t\tsrc->info.port = ntohs(adr.port);\n\t\t\t\t\t\t\t\t//if (net_from.connum >= 1 && net_from.connum < 1+MAX_CONNECTIONS && col->conn[net_from.connum-1])\n\t\t\t\t\t\t\t\t//\tcol->conn[net_from.connum-1]->GetLocalAddresses(col->conn[net_from.connum-1], &relflags, &reladdr, &relpath, 1);\n\t\t\t\t\t\t\t\t//FIXME: we don't really know which one... NET_BaseAdrToString(src->info.reladdr, sizeof(src->info.reladdr), &reladdr);\n\t\t\t\t\t\t\t\t//src->info.relport = ntohs(reladdr.port);\n\t\t\t\t\t\t\t\tsrc->info.type = ICE_SRFLX;\n\t\t\t\t\t\t\t\tsrc->info.component = 1;\n\t\t\t\t\t\t\t\tsrc->info.network = net_from.connum;\n\t\t\t\t\t\t\t\tsrc->dirty = true;\n\t\t\t\t\t\t\t\tsrc->info.priority = ICE_ComputePriority(&src->peer, &src->info);\t//FIXME\n\n\t\t\t\t\t\t\t\tSys_RandomBytes((void*)rnd, sizeof(rnd));\n\t\t\t\t\t\t\t\tQ_strncpyz(src->info.candidateid, va(\"x%08x%08x\", rnd[0], rnd[1]), sizeof(src->info.candidateid));\n\n\t\t\t\t\t\t\t\tif (net_ice_debug.ival >= 1)\n\t\t\t\t\t\t\t\t\tCon_Printf(S_COLOR_GRAY\"[%s]: Public address: %s\\n\", con->friendlyname, NET_AdrToString(str, sizeof(str), &adr));\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\ts->stunretry = Sys_Milliseconds() + 60*1000;\n\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t//check to see if this is a new peer-reflexive address, which happens when the peer is behind a nat.\n\t\t\t\t\tfor (rc = con->rc; rc; rc = rc->next)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (NET_CompareAdr(&net_from, &rc->peer))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (!(rc->reachable & (1u<<(net_from.connum-1))))\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tchar str[256];\n\t\t\t\t\t\t\t\tif (net_ice_debug.ival >= 1)\n\t\t\t\t\t\t\t\t\tCon_Printf(S_COLOR_GRAY\"[%s]: We can reach %s (%s) via %s\\n\", con->friendlyname, NET_AdrToString(str, sizeof(str), &net_from), ICE_GetCandidateType(&rc->info), ICE_NetworkToName(con, net_from.connum));\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\trc->reachable |= 1u<<(net_from.connum-1);\n\t\t\t\t\t\t\trc->reached = Sys_Milliseconds();\n\n\t\t\t\t\t\t\tif (NET_CompareAdr(&net_from, &con->chosenpeer) && (stun->transactid[2] & BigLong(0x80000000)))\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tif (con->state == ICE_CONNECTING)\n\t\t\t\t\t\t\t\t\tICE_Set(con, \"state\", STRINGIFY(ICE_CONNECTED));\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t//only accept actual responses, not spoofed stuff.\n\t\t\t\tif (stun->magiccookie == BigLong(0x2112a442)\n\t\t\t\t\t&& stun->transactid[0]==col->srflx_tid[0]\n\t\t\t\t\t&& stun->transactid[1]==col->srflx_tid[1]\n\t\t\t\t\t&& stun->transactid[2]==col->srflx_tid[2]\n\t\t\t\t\t&& !NET_CompareAdr(&col->srflx[adr.type!=NA_IP], &adr))\n\t\t\t\t{\n\t\t\t\t\tif (col->srflx[adr.type!=NA_IP].type==NA_INVALID)\n\t\t\t\t\t\tCon_Printf(S_COLOR_GRAY\"Public address reported as %s\\n\", NET_AdrToString(errmsg, sizeof(errmsg), &adr));\n\t\t\t\t\telse\n\t\t\t\t\t\tCon_Printf(CON_ERROR\"Server reflexive address changed to %s\\n\", NET_AdrToString(errmsg, sizeof(errmsg), &adr));\n\t\t\t\t\tcol->srflx[adr.type!=NA_IP] = adr;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\t\telse if (stun->msgtype == BigShort(STUN_BINDING|STUN_INDICATION))// && net_message.cursize == stunlen + sizeof(*stun) && stun->magiccookie == BigLong(0x2112a442))\n\t\t{\n\t\t\t//binding indication. used as an rtp keepalive. should have a fingerprint\n\t\t\treturn true;\n\t\t}\n\t\telse if (stun->msgtype == BigShort(STUN_DATA|STUN_INDICATION)\n\t\t\t\t && net_message.cursize == stunlen + sizeof(*stun) && stun->magiccookie == BigLong(STUN_MAGIC_COOKIE))\n\t\t{\n\t\t\t//TURN relayed data\n\t\t\t//these MUST come from a _known_ turn server.\n\t\t\tnetadr_t adr;\n\t\t\tchar xor[16];\n\t\t\tshort portxor;\n\t\t\tvoid *data = NULL;\n\t\t\tunsigned short datasize = 0;\n\t\t\tunsigned short attrval;\n\t\t\tstunattr_t *attr = (stunattr_t*)(stun+1);\n\t\t\tint alen;\n\t\t\tunsigned int network = net_from.connum-1;\t//also net_from_connection->connum\n\t\t\tstruct icestate_s *con;\n\n\t\t\tif (network < MAX_CONNECTIONS)\n\t\t\t\treturn true;\t//don't handle this if its on the non-turn sockets.\n\t\t\tnetwork -= MAX_CONNECTIONS;\n\t\t\tif (network < countof(con->server))\n\t\t\t\treturn true; //don't double-decapsulate...\n\t\t\tnetwork -= countof(con->server);\n\n\t\t\tfor (con = icelist; con; con = con->next)\n\t\t\t{\n\t\t\t\tif (network < con->servers && net_from_connection == con->server[network].con)\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (!con)\n\t\t\t\treturn true;\t//don't know what it was. just ignore it.\n\t\t\tif (network >= con->servers || !NET_CompareAdr(&net_from, &con->server[network].addr))\n\t\t\t\treturn true;\t//right socket, but not from the server that we expected...\n\n\t\t\tadr.type = NA_INVALID;\n\t\t\twhile(stunlen>0)\n\t\t\t{\n\t\t\t\tstunlen -= sizeof(*attr);\n\t\t\t\talen = (unsigned short)BigShort(attr->attrlen);\n\t\t\t\tif (alen > stunlen)\n\t\t\t\t\treturn false;\n\t\t\t\tstunlen -= (alen+3)&~3;\n\t\t\t\tattrval = BigShort(attr->attrtype);\n\t\t\t\tswitch(attrval)\n\t\t\t\t{\n\t\t\t\tdefault:\n\t\t\t\t\tif (attrval & 0x8000)\n\t\t\t\t\t\tbreak;\t//okay to ignore\n\t\t\t\t\treturn true;\n\t\t\t\tcase STUNATTR_DATA:\n\t\t\t\t\tdata = attr+1;\n\t\t\t\t\tdatasize = alen;\n\t\t\t\t\tbreak;\n\t\t\t\tcase STUNATTR_XOR_PEER_ADDRESS:\n\t\t\t\t\t//always xor\n\t\t\t\t\tportxor = *(short*)&stun->magiccookie;\n\t\t\t\t\tmemcpy(xor, &stun->magiccookie, sizeof(xor));\n\n\t\t\t\t\tadr.prot = NP_DGRAM;\n\t\t\t\t\tadr.connum = net_from.connum;\n\t\t\t\t\tadr.scopeid = net_from.scopeid;\n\t\t\t\t\tif (alen == 8 && ((qbyte*)attr)[5] == 1)\t\t//ipv4\n\t\t\t\t\t{\n\t\t\t\t\t\tadr.type = NA_IP;\n\t\t\t\t\t\tadr.port = (((short*)attr)[3]) ^ portxor;\n\t\t\t\t\t\t*(int*)adr.address.ip = *(int*)(&((qbyte*)attr)[8]) ^ *(int*)xor;\n\t\t\t\t\t}\n\t\t\t\t\telse if (alen == 20 && ((qbyte*)attr)[5] == 2)\t//ipv6\n\t\t\t\t\t{\n\t\t\t\t\t\tadr.type = NA_IPV6;\n\t\t\t\t\t\tadr.port = (((short*)attr)[3]) ^ portxor;\n\t\t\t\t\t\t((int*)adr.address.ip6)[0] = ((int*)&((qbyte*)attr)[8])[0] ^ ((int*)xor)[0];\n\t\t\t\t\t\t((int*)adr.address.ip6)[1] = ((int*)&((qbyte*)attr)[8])[1] ^ ((int*)xor)[1];\n\t\t\t\t\t\t((int*)adr.address.ip6)[2] = ((int*)&((qbyte*)attr)[8])[2] ^ ((int*)xor)[2];\n\t\t\t\t\t\t((int*)adr.address.ip6)[3] = ((int*)&((qbyte*)attr)[8])[3] ^ ((int*)xor)[3];\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\talen = (alen+3)&~3;\n\t\t\t\tattr = (stunattr_t*)((char*)(attr+1) + alen);\n\t\t\t}\n\t\t\tif (data)\n\t\t\t{\n\t\t\t\tmemmove(net_message.data, data, net_message.cursize = datasize);\n\t\t\t\tadr.connum = net_from.connum-countof(con->server);\t//came via the relay.\n\t\t\t\tnet_from = adr;\n\t\t\t\tcol->ReadGamePacket();\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\n\t\telse if ((stun->msgtype == BigShort(STUN_CREATEPERM|STUN_REPLY) || stun->msgtype == BigShort(STUN_CREATEPERM|STUN_ERROR))\n\t\t\t\t && net_message.cursize == stunlen + sizeof(*stun) && stun->magiccookie == BigLong(STUN_MAGIC_COOKIE))\n\t\t{\n\t\t\t//TURN CreatePermissions reply (or error)\n\t\t\tunsigned short attrval;\n\t\t\tstunattr_t *attr = (stunattr_t*)(stun+1), *nonce=NULL, *realm=NULL;\n\t\t\tint alen;\n\t\t\tstruct iceserver_s *s = NULL;\n\t\t\tint i, j;\n\t\t\tstruct icestate_s *con;\n\t\t\tchar errmsg[128];\n\t\t\tint err = 0;\n\t\t\t*errmsg = 0;\n\n\t\t\t//make sure it makes sense.\n\t\t\twhile(stunlen>0)\n\t\t\t{\n\t\t\t\tstunlen -= sizeof(*attr);\n\t\t\t\talen = (unsigned short)BigShort(attr->attrlen);\n\t\t\t\tif (alen > stunlen)\n\t\t\t\t\treturn false;\n\t\t\t\tstunlen -= (alen+3)&~3;\n\t\t\t\tattrval = BigShort(attr->attrtype);\n\t\t\t\tswitch(attrval)\n\t\t\t\t{\n\t\t\t\tdefault:\n\t\t\t\t\tif (attrval & 0x8000)\n\t\t\t\t\t\tbreak;\t//okay to ignore\n\t\t\t\t\treturn true;\n\t\t\t\tcase STUNATTR_NONCE:\n\t\t\t\t\tnonce = attr;\n\t\t\t\t\tbreak;\n\t\t\t\tcase STUNATTR_REALM:\n\t\t\t\t\trealm = attr;\n\t\t\t\t\tbreak;\n\t\t\t\tcase STUNATTR_ERROR_CODE:\n\t\t\t\t\t{\n\t\t\t\t\t\tunsigned short len = BigShort(attr->attrlen)-4;\n\t\t\t\t\t\tif (len > sizeof(errmsg)-1)\n\t\t\t\t\t\t\tlen = sizeof(errmsg)-1;\n\t\t\t\t\t\tmemcpy(errmsg, &((qbyte*)attr)[8], len);\n\t\t\t\t\t\terrmsg[len] = 0;\n\t\t\t\t\t\tif (err==0)\n\t\t\t\t\t\t\terr = (((qbyte*)attr)[6]*100) + (((qbyte*)attr)[7]%100);\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase STUNATTR_MSGINTEGRITIY_SHA1:\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\talen = (alen+3)&~3;\n\t\t\t\tattr = (stunattr_t*)((char*)(attr+1) + alen);\n\t\t\t}\n\n\t\t\t//now figure out what it acked.\n\t\t\tfor (con = icelist; con; con = con->next)\n\t\t\t{\n\t\t\t\tfor (i = 0; i < con->servers; i++)\n\t\t\t\t{\n\t\t\t\t\ts = &con->server[i];\n\t\t\t\t\tif (NET_CompareAdr(&net_from, &s->addr))\n\t\t\t\t\t\tfor (j = 0; j < s->peers; j++)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (s->peer[j].stunrnd[0] == stun->transactid[0] && s->peer[j].stunrnd[1] == stun->transactid[1] && s->peer[j].stunrnd[2] == stun->transactid[2])\n\t\t\t\t\t\t\t{\t//the lifetime of a permission is a fixed 5 mins (this is separately from the port allocation)\n\t\t\t\t\t\t\t\tunsigned int now = Sys_Milliseconds();\n\n\t\t\t\t\t\t\t\tif (err)\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tif (err == 438 && realm && nonce)\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\talen = BigShort(nonce->attrlen);\n\t\t\t\t\t\t\t\t\t\tZ_Free(s->nonce);\n\t\t\t\t\t\t\t\t\t\ts->nonce = Z_Malloc(alen+1);\n\t\t\t\t\t\t\t\t\t\tmemcpy(s->nonce, nonce+1, alen);\n\t\t\t\t\t\t\t\t\t\ts->nonce[alen] = 0;\n\n\t\t\t\t\t\t\t\t\t\talen = BigShort(realm->attrlen);\n\t\t\t\t\t\t\t\t\t\tZ_Free(s->realm);\n\t\t\t\t\t\t\t\t\t\ts->realm = Z_Malloc(alen+1);\n\t\t\t\t\t\t\t\t\t\tmemcpy(s->realm, realm+1, alen);\n\t\t\t\t\t\t\t\t\t\ts->realm[alen] = 0;\n\n\t\t\t\t\t\t\t\t\t\ts->peer[j].retry = now;\t//retry fast.\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tnow -= 25;\t//we don't know when it acked, so lets pretend we're a few MS ago.\n\t\t\t\t\t\t\t\t\ts->peer[j].expires = now + 5*60*1000;\n\t\t\t\t\t\t\t\t\ts->peer[j].retry = now + 4*60*1000;\t//start trying to refresh it a min early (which will do resends).\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t//next attempt will use a different id.\n\t\t\t\t\t\t\t\tSys_RandomBytes((char*)s->peer[i].stunrnd, sizeof(s->peer[i].stunrnd));\n\t\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (i < con->servers)\n\t\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\treturn true;\n\t\t}\n\t\telse if ((stun->msgtype == BigShort(STUN_ALLOCATE|STUN_REPLY) || stun->msgtype == BigShort(STUN_ALLOCATE|STUN_ERROR)||\n\t\t\t\t (stun->msgtype == BigShort(STUN_REFRESH|STUN_REPLY) || stun->msgtype == BigShort(STUN_REFRESH|STUN_ERROR)))\n\t\t\t\t && net_message.cursize == stunlen + sizeof(*stun) && stun->magiccookie == BigLong(STUN_MAGIC_COOKIE))\n\t\t{\n\t\t\t//TURN allocate reply (or error)\n\t\t\tnetadr_t adrs[2], ladr, *adr;\t//the last should be our own ip.\n\t\t\tchar xor[16];\n\t\t\tshort portxor;\n\t\t\tunsigned short attrval;\n\t\t\tstunattr_t *attr = (stunattr_t*)(stun+1);\n\t\t\tint alen;\n\t\t\tint err = 0;\n\t\t\tchar errmsg[64];\n\t\t\tstruct iceserver_s *s = NULL;\n\t\t\tint i;\n\t\t\tstruct icestate_s *con;\n\t\t\tqboolean noncechanged = false;\n\t\t\tunsigned int lifetime = 0;\n\n\t\t\t//gotta have come from our private socket.\n\t\t\tunsigned int network = net_from.connum-1;\t//also net_from_connection->connum\n\t\t\tif (network < MAX_CONNECTIONS)\n\t\t\t\treturn true;\t//don't handle this if its on the non-turn sockets.\n\t\t\tnetwork -= MAX_CONNECTIONS;\n\t\t\tif (network < countof(con->server))\n\t\t\t\treturn true; //TURN-over-TURN is bad...\n\t\t\tnetwork -= countof(con->server);\n\n\t\t\tfor (con = icelist; con; con = con->next)\n\t\t\t{\n\t\t\t\tif (network < con->servers && net_from_connection == con->server[network].con)\n\t\t\t\t{\n\t\t\t\t\ts = &con->server[network];\n\t\t\t\t\tif (s->stunrnd[0] == stun->transactid[0] && s->stunrnd[1] == stun->transactid[1] && s->stunrnd[2] == stun->transactid[2] && NET_CompareAdr(&net_from, &s->addr))\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tif (net_ice_debug.ival)\n\t\t\t\t\t\tCon_Printf(S_COLOR_GRAY\"Stale transaction id (got %x, expected %x)\\n\", stun->transactid[0], s->stunrnd[0]);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!con)\n\t\t\t\treturn true;\t//don't know what it was. just ignore it.\n\n\t\t\tnetwork += 1 + MAX_CONNECTIONS;\t//fix it up to refer to the relay rather than the private socket.\n\n\t\t\tadrs[0].type = NA_INVALID;\n\t\t\tadrs[1].type = NA_INVALID;\n\t\t\tladr.type = NA_INVALID;\n\n\t\t\twhile(stunlen>0)\n\t\t\t{\n\t\t\t\tstunlen -= sizeof(*attr);\n\t\t\t\talen = (unsigned short)BigShort(attr->attrlen);\n\t\t\t\tif (alen > stunlen)\n\t\t\t\t\treturn false;\n\t\t\t\tstunlen -= (alen+3)&~3;\n\t\t\t\tattrval = BigShort(attr->attrtype);\n\t\t\t\tswitch(attrval)\n\t\t\t\t{\n\t\t\t\tcase STUNATTR_LIFETIME:\n\t\t\t\t\tif (alen >= 4)\n\t\t\t\t\t\tlifetime = BigLong(*(int*)(attr+1));\n\t\t\t\t\tbreak;\n//\t\t\t\tcase STUNATTR_SOFTWARE:\n\t\t\t\tcase STUNATTR_MSGINTEGRITIY_SHA1:\n//\t\t\t\tcase STUNATTR_FINGERPRINT:\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tif (attrval & 0x8000)\n\t\t\t\t\t\tbreak;\t//okay to ignore\n\t\t\t\t\terr = -1;\t//got an attribute we 'must' handle...\n\t\t\t\t\treturn true;\n\t\t\t\tcase STUNATTR_NONCE:\n\t\t\t\t\tZ_Free(s->nonce);\n\t\t\t\t\ts->nonce = Z_Malloc(alen+1);\n\t\t\t\t\tmemcpy(s->nonce, attr+1, alen);\n\t\t\t\t\ts->nonce[alen] = 0;\n\t\t\t\t\tnoncechanged = true;\n\t\t\t\t\tbreak;\n\t\t\t\tcase STUNATTR_REALM:\n\t\t\t\t\tZ_Free(s->realm);\n\t\t\t\t\ts->realm = Z_Malloc(alen+1);\n\t\t\t\t\tmemcpy(s->realm, attr+1, alen);\n\t\t\t\t\ts->realm[alen] = 0;\n\t\t\t\t\tbreak;\n\t\t\t\tcase STUNATTR_XOR_RELAYED_ADDRESS:\n\t\t\t\tcase STUNATTR_XOR_MAPPED_ADDRESS:\n\t\t\t\t\tif (BigShort(attr->attrtype) == STUNATTR_XOR_MAPPED_ADDRESS)\n\t\t\t\t\t\tadr = &ladr;\n\t\t\t\t\telse\n\t\t\t\t\t\tadr = adrs[0].type?&adrs[1]:&adrs[0];\n\t\t\t\t\t//always xor\n\t\t\t\t\tportxor = *(short*)&stun->magiccookie;\n\t\t\t\t\tmemcpy(xor, &stun->magiccookie, sizeof(xor));\n\n\t\t\t\t\tadr->prot = NP_DGRAM;\n\t\t\t\t\tadr->connum = net_from.connum;\n\t\t\t\t\tadr->scopeid = net_from.scopeid;\n\t\t\t\t\tif (alen == 8 && ((qbyte*)attr)[5] == 1)\t\t//ipv4\n\t\t\t\t\t{\n\t\t\t\t\t\tadr->type = NA_IP;\n\t\t\t\t\t\tadr->port = (((short*)attr)[3]) ^ portxor;\n\t\t\t\t\t\t*(int*)adr->address.ip = *(int*)(&((qbyte*)attr)[8]) ^ *(int*)xor;\n\t\t\t\t\t}\n\t\t\t\t\telse if (alen == 20 && ((qbyte*)attr)[5] == 2)\t//ipv6\n\t\t\t\t\t{\n\t\t\t\t\t\tadr->type = NA_IPV6;\n\t\t\t\t\t\tadr->port = (((short*)attr)[3]) ^ portxor;\n\t\t\t\t\t\t((int*)adr->address.ip6)[0] = ((int*)&((qbyte*)attr)[8])[0] ^ ((int*)xor)[0];\n\t\t\t\t\t\t((int*)adr->address.ip6)[1] = ((int*)&((qbyte*)attr)[8])[1] ^ ((int*)xor)[1];\n\t\t\t\t\t\t((int*)adr->address.ip6)[2] = ((int*)&((qbyte*)attr)[8])[2] ^ ((int*)xor)[2];\n\t\t\t\t\t\t((int*)adr->address.ip6)[3] = ((int*)&((qbyte*)attr)[8])[3] ^ ((int*)xor)[3];\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase STUNATTR_ERROR_CODE:\n\t\t\t\t\t{\n\t\t\t\t\t\tunsigned short len = BigShort(attr->attrlen)-4;\n\t\t\t\t\t\tif (len > sizeof(errmsg)-1)\n\t\t\t\t\t\t\tlen = sizeof(errmsg)-1;\n\t\t\t\t\t\tmemcpy(errmsg, &((qbyte*)attr)[8], len);\n\t\t\t\t\t\terrmsg[len] = 0;\n\t\t\t\t\t\tif (!len)\n\t\t\t\t\t\t\tQ_strncpyz(errmsg, \"<no description>\", len);\n\t\t\t\t\t\tif (err==0)\n\t\t\t\t\t\t\terr = (((qbyte*)attr)[6]*100) + (((qbyte*)attr)[7]%100);\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\talen = (alen+3)&~3;\n\t\t\t\tattr = (stunattr_t*)((char*)(attr+1) + alen);\n\t\t\t}\n\n\t\t\tif (err)\n\t\t\t{\n\t\t\t\tchar sender[256];\n\n\t\t\t\tif (err == 438/*stale nonce*/)\n\t\t\t\t{\t//reset everything.\n\t\t\t\t\ts->state = noncechanged?TURN_HAVE_NONCE:TURN_UNINITED;\n\t\t\t\t\ts->stunretry = Sys_Milliseconds();\n\n\t\t\t\t\tif (net_ice_debug.ival >= 1)\n\t\t\t\t\t\tCon_Printf(S_COLOR_GRAY\"[%s]: %s: TURN error code %u : %s\\n\", con->friendlyname, NET_AdrToString(sender, sizeof(sender), &net_from), err, errmsg);\n\t\t\t\t}\n\t\t\t\telse if (err == 403/*forbidden*/)\t//something bad...\n\t\t\t\t{\n\t\t\t\t\ts->state = TURN_UNINITED, s->stunretry = Sys_Milliseconds() + 60*1000;\n\t\t\t\t\tif (net_ice_debug.ival >= 1)\n\t\t\t\t\t\tCon_Printf(CON_ERROR\"[%s]: %s: TURN error code %u : %s\\n\", con->friendlyname, NET_AdrToString(sender, sizeof(sender), &net_from), err, errmsg);\n\t\t\t\t}\n\t\t\t\telse if (err == 401 && s->state == TURN_UNINITED && s->nonce)\t//failure when sending auth... give up for a min\n\t\t\t\t{\t//this happens from initial auth. we need to reply with the real auth request now.\n\t\t\t\t\ts->state = TURN_HAVE_NONCE, s->stunretry = Sys_Milliseconds();\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\ts->stunretry = Sys_Milliseconds() + 60*1000;\n//\t\t\t\t\tif (net_ice_debug.ival >= 1)\n\t\t\t\t\t\tCon_Printf(CON_ERROR\"[%s]: %s: TURN error code %u : %s\\n\", con->friendlyname, NET_AdrToString(sender, sizeof(sender), &net_from), err, errmsg);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tstruct icecandidate_s *lc;\n\t\t\t\tfor (i = 0; i < countof(adrs); i++)\n\t\t\t\t{\n\t\t\t\t\tif (adrs[i].type != NA_INVALID && stun->msgtype == BigShort(STUN_ALLOCATE|STUN_REPLY))\n\t\t\t\t\t{\n\t\t\t\t\t\ts->state = TURN_ALLOCATED;\n\n\t\t\t\t\t\tif (!i)\n\t\t\t\t\t\t\ts->family = adrs[i].type;\n\t\t\t\t\t\tif (s->family != adrs[i].type)\n\t\t\t\t\t\t\ts->family = NA_INVALID;\t//send it both types.\n\n\t\t\t\t\t\tif (ladr.type != NA_INVALID)\n\t\t\t\t\t\t\tadr = &ladr;\t//can give a proper reflexive address\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tadr = &adrs[i];\t//no info... give something.\n\n\t\t\t\t\t\t//check to see if this is a new server-reflexive address, which happens when the peer is behind a nat.\n\t\t\t\t\t\tfor (lc = con->lc; lc; lc = lc->next)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (NET_CompareAdr(&adrs[i], &lc->peer))\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (!lc)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tint rnd[2];\n\t\t\t\t\t\t\tstruct icecandidate_s *src;\t//server Reflexive Candidate\n\t\t\t\t\t\t\tchar str[256];\n\t\t\t\t\t\t\tsrc = Z_Malloc(sizeof(*src));\n\t\t\t\t\t\t\tsrc->next = con->lc;\n\t\t\t\t\t\t\tcon->lc = src;\n\t\t\t\t\t\t\tsrc->peer = adrs[i];\n\t\t\t\t\t\t\tNET_BaseAdrToString(src->info.addr, sizeof(src->info.addr), &adrs[i]);\n\t\t\t\t\t\t\tsrc->info.port = ntohs(adrs[i].port);\n\t\t\t\t\t\t\tNET_BaseAdrToString(src->info.reladdr, sizeof(src->info.reladdr), adr);\n\t\t\t\t\t\t\tsrc->info.relport = ntohs(adr->port);\n\t\t\t\t\t\t\tsrc->info.type = ICE_RELAY;\n\t\t\t\t\t\t\tsrc->info.component = 1;\n\t\t\t\t\t\t\tsrc->info.network = network;\n\t\t\t\t\t\t\tsrc->dirty = true;\n\t\t\t\t\t\t\tsrc->info.priority = ICE_ComputePriority(&adrs[i], &src->info);\n\n\t\t\t\t\t\t\tSys_RandomBytes((void*)rnd, sizeof(rnd));\n\t\t\t\t\t\t\tQ_strncpyz(src->info.candidateid, va(\"x%08x%08x\", rnd[0], rnd[1]), sizeof(src->info.candidateid));\n\n\t\t\t\t\t\t\tif (net_ice_debug.ival >= 1)\n\t\t\t\t\t\t\t\tCon_Printf(S_COLOR_GRAY\"[%s]: Relayed local candidate: %s\\n\", con->friendlyname, NET_AdrToString(str, sizeof(str), &adrs[i]));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (lifetime < 60)\t//don't spam reauth requests too often...\n\t\t\t\t\tlifetime = 60;\n\t\t\t\ts->stunretry = Sys_Milliseconds() + (lifetime-50)*1000;\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\n#endif\n\t\t\tif (stun->msgtype == BigShort(STUN_BINDING|STUN_REQUEST) && net_message.cursize == stunlen + sizeof(*stun) && stun->magiccookie == BigLong(STUN_MAGIC_COOKIE))\n\t\t{\n\t\t\tchar username[256];\n\t\t\tchar integrity[20];\n#ifdef SUPPORT_ICE\n\t\t\tstruct icestate_s *con;\n\t\t\tint role = 0;\n\t\t\tunsigned int tiehigh = 0;\n\t\t\tunsigned int tielow = 0;\n\t\t\tqboolean usecandidate = false;\n\t\t\tunsigned int priority = 0;\n\t\t\tchar *lpwd = NULL;\n#endif\n\t\t\tchar *integritypos = NULL;\n\t\t\tint error = 0;\n\n\t\t\tsizebuf_t buf;\n\t\t\tchar data[512];\n\t\t\tint alen = 0, atype = 0, aofs = 0;\n\t\t\tint crc;\n\n\t\t\t//binding request\n\t\t\tstunattr_t *attr = (stunattr_t*)(stun+1);\n\t\t\t*username = 0;\n\t\t\twhile(stunlen)\n\t\t\t{\n\t\t\t\talen = (unsigned short)BigShort(attr->attrlen);\n\t\t\t\tif (alen+sizeof(*attr) > stunlen)\n\t\t\t\t\treturn false;\n\t\t\t\tswitch((unsigned short)BigShort(attr->attrtype))\n\t\t\t\t{\t\t\t\tcase 0xc057: /*'network cost'*/ break;\n\t\t\t\tdefault:\n\t\t\t\t\t//unknown attributes < 0x8000 are 'mandatory to parse', and such packets must be dropped in their entirety.\n\t\t\t\t\t//other ones are okay.\n\t\t\t\t\tif (!((unsigned short)BigShort(attr->attrtype) & 0x8000))\n\t\t\t\t\t\treturn false;\n\t\t\t\t\tbreak;\n\t\t\t\tcase STUNATTR_USERNAME:\n\t\t\t\t\tif (alen < sizeof(username))\n\t\t\t\t\t{\n\t\t\t\t\t\tmemcpy(username, attr+1, alen);\n\t\t\t\t\t\tusername[alen] = 0;\n//\t\t\t\t\t\tCon_Printf(\"Stun username = \\\"%s\\\"\\n\", username);\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase STUNATTR_MSGINTEGRITIY_SHA1:\n\t\t\t\t\tmemcpy(integrity, attr+1, sizeof(integrity));\n\t\t\t\t\tintegritypos = (char*)(attr+1);\n\t\t\t\t\tbreak;\n#ifdef SUPPORT_ICE\n\t\t\t\tcase STUNATTR_ICE_PRIORITY:\n//\t\t\t\t\tCon_Printf(\"priority = \\\"%i\\\"\\n\", priority);\n\t\t\t\t\tpriority = BigLong(*(int*)(attr+1));\n\t\t\t\t\tbreak;\n\t\t\t\tcase STUNATTR_ICE_USE_CANDIDATE:\n\t\t\t\t\tusecandidate = true;\n\t\t\t\t\tbreak;\n#endif\n\t\t\t\tcase STUNATTR_FINGERPRINT:\n//\t\t\t\t\tCon_Printf(\"fingerprint = \\\"%08x\\\"\\n\", BigLong(*(int*)(attr+1)));\n\t\t\t\t\tbreak;\n#ifdef SUPPORT_ICE\n\t\t\t\tcase STUNATTR_ICE_CONTROLLED:\n\t\t\t\tcase STUNATTR_ICE_CONTROLLING:\n\t\t\t\t\trole = (unsigned short)BigShort(attr->attrtype);\n\t\t\t\t\ttiehigh = BigLong(((int*)(attr+1))[0]);\n\t\t\t\t\ttielow = BigLong(((int*)(attr+1))[1]);\n\t\t\t\t\tbreak;\n#endif\n\t\t\t\t}\n\t\t\t\talen = (alen+3)&~3;\n\t\t\t\tattr = (stunattr_t*)((char*)(attr+1) + alen);\n\t\t\t\tstunlen -= alen+sizeof(*attr);\n\t\t\t}\n\n#ifdef SUPPORT_ICE\n\t\t\tif (*username || integritypos)\n\t\t\t{\n\t\t\t\t//we need to know which connection its from in order to validate the integrity\n\t\t\t\tfor (con = icelist; con; con = con->next)\n\t\t\t\t{\n\t\t\t\t\tif (!strcmp(va(\"%s:%s\", con->lufrag, con->rufrag), username))\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tif (!con)\n\t\t\t\t{\n\t\t\t\t\tif (net_ice_debug.ival >= 2)\n\t\t\t\t\t\tCon_Printf(\"Received STUN request from unknown user \\\"%s\\\"\\n\", username);\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t\t/*else if (con->chosenpeer.type != NA_INVALID)\n\t\t\t\t{\t//got one.\n\t\t\t\t\tif (!NET_CompareAdr(&net_from, &con->chosenpeer))\n\t\t\t\t\t\treturn true;\t//FIXME: we're too stupid to handle switching. pretend to be dead.\n\t\t\t\t}*/\n\t\t\t\telse if (con->state == ICE_INACTIVE)\n\t\t\t\t\treturn true;\t//bad timing\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tstruct icecandidate_s *rc;\n\n\t\t\t\t\tif (net_ice_debug.ival >= 2)\n\t\t\t\t\t\tCon_Printf(S_COLOR_GRAY\"[%s]: got binding request on %s from %s\\n\", con->friendlyname, ICE_NetworkToName(con, net_from.connum), NET_AdrToString(username,sizeof(username), &net_from));\n\n\t\t\t\t\tif (integritypos)\n\t\t\t\t\t{\n\t\t\t\t\t\tchar key[20];\n\t\t\t\t\t\t//the hmac is a bit weird. the header length includes the integrity attribute's length, but the checksum doesn't even consider the attribute header.\n\t\t\t\t\t\tstun->msglen = BigShort(integritypos+sizeof(integrity) - (char*)stun - sizeof(*stun));\n\t\t\t\t\t\tCalcHMAC(&hash_sha1, key, sizeof(key), (qbyte*)stun, integritypos-4 - (char*)stun, con->lpwd, strlen(con->lpwd));\n\t\t\t\t\t\tif (memcmp(key, integrity, sizeof(integrity)))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tCon_DPrintf(CON_WARNING\"Integrity is bad! needed %x got %x\\n\", *(int*)key, *(int*)integrity);\n\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t//check to see if this is a new peer-reflexive address, which happens when the peer is behind a nat.\n\t\t\t\t\tfor (rc = con->rc; rc; rc = rc->next)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (NET_CompareAdr(&net_from, &rc->peer))\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tif (!rc)\n\t\t\t\t\t{\n\t\t\t\t\t\t//netadr_t reladdr;\n\t\t\t\t\t\t//int relflags;\n\t\t\t\t\t\t//const char *relpath;\n\t\t\t\t\t\tstruct icecandidate_s *rc;\n\t\t\t\t\t\trc = Z_Malloc(sizeof(*rc));\n\t\t\t\t\t\trc->next = con->rc;\n\t\t\t\t\t\tcon->rc = rc;\n\n\t\t\t\t\t\trc->peer = net_from;\n\t\t\t\t\t\tNET_BaseAdrToString(rc->info.addr, sizeof(rc->info.addr), &net_from);\n\t\t\t\t\t\trc->info.port = ntohs(net_from.port);\n\t\t\t\t\t\t//if (net_from.connum >= 1 && net_from.connum < 1+MAX_CONNECTIONS && col->conn[net_from.connum-1])\n\t\t\t\t\t\t//\tcol->conn[net_from.connum-1]->GetLocalAddresses(col->conn[net_from.connum-1], &relflags, &reladdr, &relpath, 1);\n\t\t\t\t\t\t//FIXME: we don't really know which one... NET_BaseAdrToString(rc->info.reladdr, sizeof(rc->info.reladdr), &reladdr);\n\t\t\t\t\t\t//rc->info.relport = ntohs(reladdr.port);\n\t\t\t\t\t\trc->info.type = ICE_PRFLX;\n\t\t\t\t\t\trc->dirty = true;\n\t\t\t\t\t\trc->info.priority = priority;\n\t\t\t\t\t}\n\n\t\t\t\t\t//flip ice control role, if we're wrong.\n\t\t\t\t\tif (role && role != (con->controlled?STUNATTR_ICE_CONTROLLING:STUNATTR_ICE_CONTROLLED))\n\t\t\t\t\t{\n\t\t\t\t\t\tif (tiehigh == con->tiehigh && tielow == con->tielow)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tCon_Printf(\"ICE: Evil loopback hack enabled\\n\");\n\t\t\t\t\t\t\tif (usecandidate)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tif ((con->chosenpeer.connum != net_from.connum || !NET_CompareAdr(&con->chosenpeer, &net_from)) && net_ice_debug.ival >= 1)\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tchar msg[64];\n\t\t\t\t\t\t\t\t\tif (con->chosenpeer.connum-1 >= MAX_CONNECTIONS)\n\t\t\t\t\t\t\t\t\t\tCon_Printf(S_COLOR_GRAY\"[%s]: New peer imposed %s, via %s.\\n\", con->friendlyname, NET_AdrToString(msg, sizeof(msg), &net_from), ICE_NetworkToName(con, con->chosenpeer.connum));\n\t\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t\t\tCon_Printf(S_COLOR_GRAY\"[%s]: New peer imposed %s.\\n\", con->friendlyname, NET_AdrToString(msg, sizeof(msg), &net_from));\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tcon->chosenpeer = net_from;\n\n\t\t\t\t\t\t\t\tif (con->state == ICE_CONNECTING)\n\t\t\t\t\t\t\t\t\tICE_Set(con, \"state\", STRINGIFY(ICE_CONNECTED));\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcon->controlled = (tiehigh > con->tiehigh) || (tiehigh == con->tiehigh && tielow > con->tielow);\n\t\t\t\t\t\t\tif (net_ice_debug.ival >= 1)\n\t\t\t\t\t\t\t\tCon_Printf(S_COLOR_GRAY\"[%s]: role conflict detected. We should be %s\\n\", con->friendlyname, con->controlled?\"controlled\":\"controlling\");\n\t\t\t\t\t\t\terror = 87;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\telse if (usecandidate && con->controlled)\n\t\t\t\t\t{\n\t\t\t\t\t\t//in the controlled role, we're connected once we're told the pair to use (by the usecandidate flag).\n\t\t\t\t\t\t//note that this 'nominates' candidate pairs, from which the highest priority is chosen.\n\t\t\t\t\t\t//so we just pick select the highest.\n\t\t\t\t\t\t//this is problematic, however, as we don't actually know the real priority that the peer thinks we'll nominate it with.\n\n\t\t\t\t\t\tif ((con->chosenpeer.connum != net_from.connum || !NET_CompareAdr(&con->chosenpeer, &net_from)) && net_ice_debug.ival >= 1)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tchar msg[64];\n\t\t\t\t\t\t\tCon_Printf(S_COLOR_GRAY\"[%s]: New peer imposed %s, via %s.\\n\", con->friendlyname, NET_AdrToString(msg, sizeof(msg), &net_from), ICE_NetworkToName(con, net_from.connum));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcon->chosenpeer = net_from;\n\n\t\t\t\t\t\tif (con->state == ICE_CONNECTING)\n\t\t\t\t\t\t\tICE_Set(con, \"state\", STRINGIFY(ICE_CONNECTED));\n\t\t\t\t\t}\n\t\t\t\t\tlpwd = con->lpwd;\n\t\t\t\t}\n\t\t\t}//otherwise its just an ip check\n\t\t\telse\n\t\t\t\tcon = NULL;\n#else\n\t\t\t(void)integritypos;\n#endif\n\n\t\t\tmemset(&buf, 0, sizeof(buf));\n\t\t\tbuf.maxsize = sizeof(data);\n\t\t\tbuf.cursize = 0;\n\t\t\tbuf.data = data;\n\n\t\t\tif (net_from.type == NA_IP)\n\t\t\t{\n\t\t\t\talen = 4;\n\t\t\t\tatype = 1;\n\t\t\t\taofs = 0;\n\t\t\t}\n\t\t\telse if (net_from.type == NA_IPV6 &&\n\t\t\t\t\t\t!*(int*)&net_from.address.ip6[0] &&\n\t\t\t\t\t\t!*(int*)&net_from.address.ip6[4] &&\n\t\t\t\t\t\t!*(short*)&net_from.address.ip6[8] &&\n\t\t\t\t\t\t*(short*)&net_from.address.ip6[10] == (short)0xffff)\n\t\t\t{\t//just because we use an ipv6 address for ipv4 internally doesn't mean we should tell the peer that they're on ipv6...\n\t\t\t\talen = 4;\n\t\t\t\tatype = 1;\n\t\t\t\taofs = sizeof(net_from.address.ip6) - sizeof(net_from.address.ip);\n\t\t\t}\n\t\t\telse if (net_from.type == NA_IPV6)\n\t\t\t{\n\t\t\t\talen = 16;\n\t\t\t\tatype = 2;\n\t\t\t\taofs = 0;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\talen = 0;\n\t\t\t\tatype = 0;\n\t\t\t}\n\n//Con_DPrintf(\"STUN from %s\\n\", NET_AdrToString(data, sizeof(data), &net_from));\n\n\t\t\tMSG_WriteShort(&buf, BigShort(error?(STUN_BINDING|STUN_ERROR):(STUN_BINDING|STUN_REPLY)));\n\t\t\tMSG_WriteShort(&buf, BigShort(0));\t//fill in later\n\t\t\tMSG_WriteLong(&buf, stun->magiccookie);\n\t\t\tMSG_WriteLong(&buf, stun->transactid[0]);\n\t\t\tMSG_WriteLong(&buf, stun->transactid[1]);\n\t\t\tMSG_WriteLong(&buf, stun->transactid[2]);\n\n\t\t\tif (error == 87)\n\t\t\t{\n\t\t\t\tchar *txt = \"Role Conflict\";\n\t\t\t\tMSG_WriteShort(&buf, BigShort(STUNATTR_ERROR_CODE));\n\t\t\t\tMSG_WriteShort(&buf, BigShort(4 + strlen(txt)));\n\t\t\t\tMSG_WriteShort(&buf, 0);\t//reserved\n\t\t\t\tMSG_WriteByte(&buf, 0);\t\t//class\n\t\t\t\tMSG_WriteByte(&buf, error);\t//code\n\t\t\t\tSZ_Write(&buf, txt, strlen(txt));\t//readable\n\t\t\t\twhile(buf.cursize&3)\t\t//padding\n\t\t\t\t\tMSG_WriteChar(&buf, 0);\n\t\t\t}\n\t\t\telse if (1)\n\t\t\t{\t//xor mapped\n\t\t\t\tnetadr_t xored = net_from;\n\t\t\t\tint i;\n\t\t\t\txored.port ^= *(short*)(data+4);\n\t\t\t\tfor (i = 0; i < alen; i++)\n\t\t\t\t\t((qbyte*)&xored.address)[aofs+i] ^= ((qbyte*)data+4)[i];\n\t\t\t\tMSG_WriteShort(&buf, BigShort(STUNATTR_XOR_MAPPED_ADDRESS));\n\t\t\t\tMSG_WriteShort(&buf, BigShort(4+alen));\n\t\t\t\tMSG_WriteShort(&buf, BigShort(atype));\n\t\t\t\tMSG_WriteShort(&buf, xored.port);\n\t\t\t\tSZ_Write(&buf, (char*)&xored.address + aofs, alen);\n\t\t\t}\n\t\t\telse\n\t\t\t{\t//non-xor mapped\n\t\t\t\tMSG_WriteShort(&buf, BigShort(STUNATTR_MAPPED_ADDRESS));\n\t\t\t\tMSG_WriteShort(&buf, BigShort(4+alen));\n\t\t\t\tMSG_WriteShort(&buf, BigShort(atype));\n\t\t\t\tMSG_WriteShort(&buf, net_from.port);\n\t\t\t\tSZ_Write(&buf, (char*)&net_from.address + aofs, alen);\n\t\t\t}\n\n\t\t\tMSG_WriteShort(&buf, BigShort(STUNATTR_USERNAME));\t//USERNAME\n\t\t\tMSG_WriteShort(&buf, BigShort(strlen(username)));\n\t\t\tSZ_Write(&buf, username, strlen(username));\n\t\t\twhile(buf.cursize&3)\n\t\t\t\tMSG_WriteChar(&buf, 0);\n\n#ifdef SUPPORT_ICE\n\t\t\tif (lpwd)\n\t\t\t{\n\t\t\t\t//message integrity is a bit annoying\n\t\t\t\tdata[2] = ((buf.cursize+4+hash_sha1.digestsize-20)>>8)&0xff;\t//hashed header length is up to the end of the hmac attribute\n\t\t\t\tdata[3] = ((buf.cursize+4+hash_sha1.digestsize-20)>>0)&0xff;\n\t\t\t\t//but the hash is to the start of the attribute's header\n\t\t\t\tCalcHMAC(&hash_sha1, integrity, sizeof(integrity), data, buf.cursize, lpwd, strlen(lpwd));\n\t\t\t\tMSG_WriteShort(&buf, BigShort(STUNATTR_MSGINTEGRITIY_SHA1));\n\t\t\t\tMSG_WriteShort(&buf, BigShort(hash_sha1.digestsize));\t//sha1 key length\n\t\t\t\tSZ_Write(&buf, integrity, hash_sha1.digestsize);\t//integrity data\n\t\t\t}\n#endif\n\n\t\t\tdata[2] = ((buf.cursize+8-20)>>8)&0xff;\t//dummy length\n\t\t\tdata[3] = ((buf.cursize+8-20)>>0)&0xff;\n\t\t\tcrc = crc32(0, data, buf.cursize)^0x5354554e;\n\t\t\tMSG_WriteShort(&buf, BigShort(STUNATTR_FINGERPRINT));\t//FINGERPRINT\n\t\t\tMSG_WriteShort(&buf, BigShort(sizeof(crc)));\n\t\t\tMSG_WriteLong(&buf, BigLong(crc));\n\n\t\t\tdata[2] = ((buf.cursize-20)>>8)&0xff;\n\t\t\tdata[3] = ((buf.cursize-20)>>0)&0xff;\n\n#ifdef SUPPORT_ICE\n\t\t\tif (con)\n\t\t\t\tTURN_Encapsulate(con, &net_from, data, buf.cursize);\n\t\t\telse\n#endif\n\t\t\t\tNET_SendPacket(col, buf.cursize, data, &net_from);\n\t\t\treturn true;\n\t\t}\n\t}\n\n\n#ifdef SUPPORT_ICE\n\t{\n\t\tstruct icestate_s *con;\n\t\tstruct icecandidate_s *rc;\n\t\tfor (con = icelist; con; con = con->next)\n\t\t{\n\t\t\tfor (rc = con->rc; rc; rc = rc->next)\n\t\t\t{\n\t\t\t\tif (NET_CompareAdr(&net_from, &rc->peer))\n\t\t\t\t{\n\t\t\t\t//\tif (rc->reachable)\n\t\t\t\t\t{\t//found it. fix up its source address to our ICE connection (so we don't have path-switching issues) and keep chugging along.\n\t\t\t\t\t\tcon->icetimeout = Sys_Milliseconds() + 1000*30;\t//not dead yet...\n\n#ifdef HAVE_DTLS\n\t\t\t\t\t\tif (con->dtlsstate)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tswitch(con->dtlsfuncs->Received(con->dtlsstate, &net_message))\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase NETERR_SENT:\n\t\t\t\t\t\t\t\tbreak;\t//\n\t\t\t\t\t\t\tcase NETERR_NOROUTE:\n\t\t\t\t\t\t\t\treturn false;\t//not a dtls packet at all. don't de-ICE it when we're meant to be using ICE.\n\t\t\t\t\t\t\tcase NETERR_DISCONNECTED:\t//dtls failure. ICE failed.\n\t\t\t\t\t\t\t\tICE_SetFailed(con, \"DTLS Terminated\");\n\t\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t\tdefault: //some kind of failure decoding the dtls packet. drop it.\n\t\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t\tnet_from = con->qadr;\n#ifdef HAVE_DTLS\n\t\t\t\t\t\tif (con->sctp)\n\t\t\t\t\t\t\tSCTP_Decode(con->sctp, col);\n\t\t\t\t\t\telse\n#endif\n\t\t\t\t\t\tif (net_message.cursize)\n\t\t\t\t\t\t\tcol->ReadGamePacket();\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n#endif\n\treturn false;\n}\n#ifdef SUPPORT_ICE\nint ICE_GetPeerCertificate(netadr_t *to, enum certprops_e prop, char *out, size_t outsize)\n{\n#ifdef HAVE_DTLS\n\tstruct icestate_s *con;\n\tint i, c;\n\tfor (con = icelist; con; con = con->next)\n\t{\n\t\tif (NET_CompareAdr(to, &con->qadr))\n\t\t{\n\t\t\tif (prop==QCERT_LOBBYSTATUS)\n\t\t\t{\n\t\t\t\t*out = 0;\n\t\t\t\tswitch(con->state)\n\t\t\t\t{\n\t\t\t\tcase ICE_INACTIVE:\n\t\t\t\t\tQ_strncpyz(out, \"idle\", outsize);\n\t\t\t\t\tbreak;\n\t\t\t\tcase ICE_FAILED:\n\t\t\t\t\tQ_strncpyz(out, \"Failed\", outsize);\n\t\t\t\t\tbreak;\n\t\t\t\tcase ICE_GATHERING:\n\t\t\t\t\tQ_strncpyz(out, \"Gathering\", outsize);\n\t\t\t\t\tbreak;\n\t\t\t\tcase ICE_CONNECTING:\n\t\t\t\t\tfor (i = 0, c = false; i < con->servers; i++)\n\t\t\t\t\t\tif (!con->server[i].isstun)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (con->server[i].state == TURN_ALLOCATED)\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tc = true;\n\t\t\t\t\t\t}\n\t\t\t\t\tif (i == con->servers)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (net_ice_relayonly.ival)\n\t\t\t\t\t\t\tQ_strncpyz(out, \"Probing (\"CON_ERROR\"NO TURN SERVER\"CON_DEFAULT\")\", outsize);\t//can't work, might still get an allocation though.\n\t\t\t\t\t\telse if (c)\n\t\t\t\t\t\t\tQ_strncpyz(out, \"Probing (\"CON_WARNING\"waiting for TURN allocation\"CON_DEFAULT\")\", outsize);\t//still good for latency. not for privacy though.\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tQ_strncpyz(out, \"Probing (\"CON_WARNING\"no relay configured\"CON_DEFAULT\")\", outsize);\t//still good for latency. not for privacy though.\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\tQ_strncpyz(out, \"Probing (\"S_COLOR_GREEN\"with fallback\"CON_DEFAULT\")\", outsize);\t//we have a relay for a fallback, all is good, hopefully. except we're still at this stage...\n\t\t\t\t\tbreak;\n\t\t\t\tcase ICE_CONNECTED:\t//past the ICE stage (but maybe not the dtls+sctp layers, these should be less likely to fail, but dtls versions may become an issue)\n\t\t\t\t\t//if (con->dtlsstate && notokay)\n\t\t\t\t\tif (con->sctp && !con->sctp->o.writable)\n\t\t\t\t\t\tQ_strncpyz(out, \"Establishing\", outsize);\t//will also block for the dtls channel of course. its not as easy check the dtls layer.\n\t\t\t\t\telse\n\t\t\t\t\t\tQ_strncpyz(out, \"Established\", outsize);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\treturn strlen(out);\n\t\t\t}\n\t\t\telse if (con->dtlsstate && con->dtlsfuncs->GetPeerCertificate)\n\t\t\t\treturn con->dtlsfuncs->GetPeerCertificate(con->dtlsstate, prop, out, outsize);\n\t\t\telse if (prop==QCERT_ISENCRYPTED && con->dtlsstate)\n\t\t\t\treturn 0;\n\t\t}\n\t}\n#endif\n\treturn -1;\n}\nvoid ICE_Terminate(netadr_t *to)\n{\n\tstruct icestate_s *con;\n\tfor (con = icelist; con; con = con->next)\n\t{\n\t\tif (NET_CompareAdr(to, &con->qadr))\n\t\t{\n\t\t\tICE_Set(con, \"state\", STRINGIFY(ICE_INACTIVE));\n\t\t\treturn;\n\t\t}\n\t}\n}\nneterr_t ICE_SendPacket(size_t length, const void *data, netadr_t *to)\n{\n\tstruct icestate_s *con;\n\tfor (con = icelist; con; con = con->next)\n\t{\n\t\tif (NET_CompareAdr(to, &con->qadr))\n\t\t{\n\t\t\tcon->icetimeout = Sys_Milliseconds()+30*1000;\t//keep it alive a little longer.\n\n\t\t\tif (con->state == ICE_CONNECTING)\n\t\t\t\treturn NETERR_CLOGGED;\n\t\t\telse if (con->state != ICE_CONNECTED)\n\t\t\t\treturn NETERR_DISCONNECTED;\n#ifdef HAVE_DTLS\n\t\t\tif (con->sctp)\n\t\t\t\treturn SCTP_Transmit(con->sctp, data, length);\n\t\t\tif (con->dtlsstate)\n\t\t\t\treturn con->dtlsfuncs->Transmit(con->dtlsstate, data, length);\n#endif\n\t\t\tif (con->chosenpeer.type != NA_INVALID)\n\t\t\t\treturn ICE_Transmit(con, data, length);\n\t\t\treturn NETERR_CLOGGED;\t//still pending\n\t\t}\n\t}\n\treturn NETERR_DISCONNECTED;\n}\n#endif\n#endif\n\n\n#ifdef SUPPORT_ICE\n//this is the clientside part of our custom accountless broker protocol\n//basically just keeps the broker processing, but doesn't send/receive actual game packets.\n//inbound messages can change ice connection states.\n//clients only handle one connection. servers need to handle multiple\ntypedef struct {\n\tftenet_generic_connection_t generic;\n\n\t//config state\n\tchar brokername[64];\t//dns name\n\tnetadr_t brokeradr;\t\t//actual ip\n\tchar gamename[64];\t\t//what we're trying to register as/for with the broker\n\n\t//broker connection state\n\tvfsfile_t *broker;\n\tqboolean handshaking;\n\tdouble nextping;\t\t//send heartbeats every now and then\n\tdouble heartbeat;\n\tdouble timeout;\t\t\t//detect if the broker goes dead, so we can reconnect reliably (instead of living for two hours without anyone able to connect).\n\tqbyte in[8192];\n\tsize_t insize;\n\tqbyte out[8192];\n\tsize_t outsize;\n\tint error;\t//outgoing data is corrupt. kill it.\n\n\n\n\t//client state...\n\tstruct icestate_s *ice;\n\tint serverid;\n\n\t//server state...\n\tstruct\n\t{\n\t\tstruct icestate_s *ice;\n\t} *clients;\n\tsize_t numclients;\n} ftenet_ice_connection_t;\nstatic void FTENET_ICE_Close(ftenet_generic_connection_t *gcon)\n{\n\tftenet_ice_connection_t *b = (void*)gcon;\n\tint cl;\n\tif (b->broker)\n\t\tVFS_CLOSE(b->broker);\n\n\tfor (cl = 0; cl < b->numclients; cl++)\n\t\tif (b->clients[cl].ice)\n\t\t\ticeapi.Close(b->clients[cl].ice, true);\n\tZ_Free(b->clients);\n\tif (b->ice)\n\t\ticeapi.Close(b->ice, true);\n\n\tZ_Free(b);\n}\n\nstatic void FTENET_ICE_Flush(ftenet_ice_connection_t *b)\n{\n\tint r;\n\tif (!b->outsize || b->error || !b->broker)\n\t\treturn;\n\tr = VFS_WRITE(b->broker, b->out, b->outsize);\n\tif (r > 0)\n\t{\n\t\tb->outsize -= r;\n\t\tmemmove(b->out, b->out+r, b->outsize);\n\t}\n\tif (r < 0)\n\t\tb->error = true;\n}\nstatic neterr_t FTENET_ICE_SendPacket(ftenet_generic_connection_t *gcon, int length, const void *data, netadr_t *to)\n{\n\tftenet_ice_connection_t *b = (void*)gcon;\n\tif (to->prot != NP_RTC_TCP && to->prot != NP_RTC_TLS)\n\t\treturn NETERR_NOROUTE;\n\tif (!NET_CompareAdr(to, &b->brokeradr))\n\t\treturn NETERR_NOROUTE;\t//its using some other broker, don't bother trying to handle it here.\n\tif (b->error)\n\t\treturn NETERR_DISCONNECTED;\n\treturn NETERR_CLOGGED;\t//we'll switch to a connect localcmd when the connection completes, so we don't really need to send any packets when they're using ICE. Just make sure the client doesn't give up.\n}\n\nstatic void FTENET_ICE_SplurgeRaw(ftenet_ice_connection_t *b, const qbyte *data, size_t len)\n{\n//0: dropclient (cl=-1 drops entire connection)\n\tif (b->outsize+len > sizeof(b->out))\n\t\tb->error = true;\n\telse\n\t{\n\t\tmemcpy(b->out+b->outsize, data, len);\n\t\tb->outsize += len;\n\t}\n}\nstatic void FTENET_ICE_SplurgeWS(ftenet_ice_connection_t *b, enum websocketpackettype_e pkttype, const qbyte *data1, size_t len1, const qbyte *data2, size_t len2)\n{\n\tsize_t tlen = len1+len2;\n\tqbyte header[8];\n\theader[0] = 0x80|pkttype;\n\tif (tlen >= 126)\n\t{\n\t\theader[1] = 126;\n\t\theader[2] = tlen>>8;\t//bigendian\n\t\theader[3] = tlen&0xff;\n\t\tFTENET_ICE_SplurgeRaw(b, header, 4);\n\t}\n\telse\n\t{\t//small data\n\t\theader[1] = tlen;\n\t\tFTENET_ICE_SplurgeRaw(b, header, 2);\n\t}\n\tFTENET_ICE_SplurgeRaw(b, data1, len1);\n\tFTENET_ICE_SplurgeRaw(b, data2, len2);\n}\nstatic void FTENET_ICE_SplurgeCmd(ftenet_ice_connection_t *b, int icemsg, int cl, const char *data)\n{\n\tqbyte msg[3] = {icemsg, cl&0xff, (cl>>8)&0xff};\t//little endian...\n\tFTENET_ICE_SplurgeWS(b, WS_PACKETTYPE_BINARYFRAME, msg, sizeof(msg), data, strlen(data));\n}\nstatic void FTENET_ICE_Heartbeat(ftenet_ice_connection_t *b)\n{\n\tb->heartbeat = realtime+30;\n#ifdef HAVE_SERVER\n\tif (b->generic.islisten)\n\t{\n\t\tchar info[2048];\n\t\tSV_GeneratePublicServerinfo(info, info+sizeof(info));\n\t\tFTENET_ICE_SplurgeCmd(b, ICEMSG_SERVERINFO, -1, info);\n\t}\n#endif\n}\nstatic void FTENET_ICE_SendOffer(ftenet_ice_connection_t *b, int cl, struct icestate_s *ice, const char *type)\n{\n\tchar buf[8192];\n\t//okay, now send the sdp to our peer.\n\tif (iceapi.Get(ice, type, buf, sizeof(buf)))\n\t{\n\t\tchar json[8192+256];\n\t\tif (ice->state == ICE_GATHERING)\n\t\t\tice->state = ICE_CONNECTING;\n\t\tif (ice->mode == ICEM_WEBRTC)\n\t\t{\n\t\t\tQ_strncpyz(json, va(\"{\\\"type\\\":\\\"%s\\\",\\\"sdp\\\":\\\"\", type+3), sizeof(json));\n\t\t\tCOM_QuotedString(buf, json+strlen(json), sizeof(json)-strlen(json)-2, true);\n\t\t\tQ_strncatz(json, \"\\\"}\", sizeof(json));\n\t\t\tFTENET_ICE_SplurgeCmd(b, ICEMSG_OFFER, cl, json);\n\t\t}\n\t\telse\n\t\t\tFTENET_ICE_SplurgeCmd(b, ICEMSG_OFFER, cl, buf);\n\n\t\tice->blockcandidates = false;\n\t}\n}\nstatic void FTENET_ICE_Establish(ftenet_ice_connection_t *b, const char *peeraddr, int cl, struct icestate_s **ret)\n{\t//sends offer\n\tstruct icestate_s *ice;\n\tqboolean usewebrtc;\n\tchar *s;\n\tif (*ret)\n\t\ticeapi.Close(*ret, false);\n#ifndef HAVE_DTLS\n\tusewebrtc = false;\n#else\n\tif (!*net_ice_usewebrtc.string && net_enable_dtls.ival)\n\t\tusewebrtc = true;\t//let the peer decide. this means we can use dtls, but not sctp.\n\telse\n\t\tusewebrtc = net_ice_usewebrtc.ival;\n#endif\n\tice = *ret = iceapi.Create(b, NULL, b->generic.islisten?((peeraddr&&*peeraddr)?va(\"%s:%i\", peeraddr,cl):NULL):va(\"/%s\", b->gamename), usewebrtc?ICEM_WEBRTC:ICEM_ICE, b->generic.islisten?ICEP_QWSERVER:ICEP_QWCLIENT, !b->generic.islisten);\n\tif (!*ret)\n\t\treturn;\t//some kind of error?!?\n\n\ticeapi.Set(ice, \"server\", va(\"stun:%s:%i\", b->brokername, BigShort(b->brokeradr.port)));\n\n\ts = net_ice_servers.string;\n\twhile((s=COM_Parse(s)))\n\t\ticeapi.Set(ice, \"server\", com_token);\n\n\tif (!b->generic.islisten)\n\t\tFTENET_ICE_SendOffer(b, cl, ice, \"sdpoffer\");\n}\nstatic void FTENET_ICE_Refresh(ftenet_ice_connection_t *b, int cl, struct icestate_s *ice)\n{\t//sends offer\n\tchar buf[8192];\n\tif (ice->blockcandidates)\n\t\treturn;\t//don't send candidates before the offers...\n\twhile (ice && iceapi.GetLCandidateSDP(ice, buf, sizeof(buf)))\n\t{\n\t\tchar json[8192+256];\n\t\tif (ice->mode == ICEM_WEBRTC)\n\t\t{\n\t\t\tQ_strncpyz(json, \"{\\\"candidate\\\":\\\"\", sizeof(json));\n\t\t\tCOM_QuotedString(buf+2, json+strlen(json), sizeof(json)-strlen(json)-2, true);\n\t\t\tQ_strncatz(json, \"\\\",\\\"sdpMid\\\":\\\"0\\\",\\\"sdpMLineIndex\\\":0}\", sizeof(json));\n\t\t\tFTENET_ICE_SplurgeCmd(b, ICEMSG_CANDIDATE, cl, json);\n\t\t}\n\t\telse\n\t\t\tFTENET_ICE_SplurgeCmd(b, ICEMSG_CANDIDATE, cl, buf);\n\t}\n}\nstatic void Buf_ReadString(const char **data, const char *end, char *out, size_t outsize)\n{\n\tconst char *in = *data;\n\tchar c;\n\toutsize--;\t//count the null early.\n\twhile (in < end)\n\t{\n\t\tc = *in++;\n\t\tif (!c)\n\t\t\tbreak;\n\t\tif (outsize)\n\t\t{\n\t\t\toutsize--;\n\t\t\t*out++ = c;\n\t\t}\n\t}\n\t*out = 0;\n\t*data = in;\n}\nstatic qboolean FTENET_ICE_GetPacket(ftenet_generic_connection_t *gcon)\n{\n\tjson_t *json;\n\tftenet_ice_connection_t *b = (void*)gcon;\n\tint ctrl, len, cmd, cl, ofs;\n\tconst char *data;\n\tchar n;\n\n\tif (!b->broker)\n\t{\n\t\tconst char *s;\n\t\tif (b->timeout > realtime)\n\t\t\treturn false;\n\t\tb->generic.thesocket = TCP_OpenStream(&b->brokeradr, b->brokername);\t//save this for select.\n\t\tb->broker = FS_WrapTCPSocket(b->generic.thesocket, true, b->brokername);\n\n#ifdef HAVE_SSL\n\t\t//convert to tls...\n\t\tif (b->brokeradr.prot == NP_TLS || b->brokeradr.prot == NP_RTC_TLS)\n\t\t\tb->broker = FS_OpenSSL(b->brokername, b->broker, false);\n#endif\n\n\t\tif (!b->broker)\n\t\t{\n\t\t\tb->timeout = realtime + 30;\n\t\t\tCon_Printf(\"rtc broker connection to %s failed (retry: 30 secs)\\n\", b->brokername);\n\t\t\treturn false;\n\t\t}\n\n\t\tb->insize = b->outsize = 0;\n\n\t\tCOM_Parse(com_protocolname.string);\n\n\t\tb->handshaking = true;\n\t\ts = va(\"GET /%s/%s HTTP/1.1\\r\\n\"\n\t\t\t\"Host: %s\\r\\n\"\n\t\t\t\"Connection: Upgrade\\r\\n\"\n\t\t\t\"Upgrade: websocket\\r\\n\"\n\t\t\t\"Sec-WebSocket-Version: 13\\r\\n\"\n\t\t\t\"Sec-WebSocket-Protocol: %s\\r\\n\"\n\t\t\t\"\\r\\n\", com_token, b->gamename, b->brokername, b->generic.islisten?\"rtc_host\":\"rtc_client\");\n\t\tFTENET_ICE_SplurgeRaw(b, s, strlen(s));\n\t\tb->heartbeat = realtime;\n\t\tb->nextping = realtime + 100;\n\t\tb->timeout = realtime + 270;\n\t}\n\tif (b->error)\n\t{\nhandleerror:\n\t\tb->generic.thesocket = INVALID_SOCKET;\n\t\tif (b->broker)\n\t\t\tVFS_CLOSE(b->broker);\n\t\tb->broker = NULL;\n\n\t\tfor (cl = 0; cl < b->numclients; cl++)\n\t\t{\n\t\t\tif (b->clients[cl].ice)\n\t\t\t\ticeapi.Close(b->clients[cl].ice, false);\n\t\t\tb->clients[cl].ice = NULL;\n\t\t}\n\t\tif (b->ice)\n\t\t\ticeapi.Close(b->ice, false);\n\t\tb->ice = NULL;\n\t\tif (b->error != 1 || !b->generic.islisten)\n\t\t\treturn false;\t//permanant error...\n\t\tb->error = false;\n\t\tb->insize = b->outsize = 0;\n\t\tb->timeout = realtime + 30;\n\t\treturn false;\n\t}\n\n\t//keep checking for new candidate info.\n\tif (b->ice)\n\t\tFTENET_ICE_Refresh(b, b->serverid, b->ice);\n\tfor (cl = 0; cl < b->numclients; cl++)\n\t\tif (b->clients[cl].ice)\n\t\t\tFTENET_ICE_Refresh(b, cl, b->clients[cl].ice);\n\tif (realtime >= b->heartbeat)\n\t\tFTENET_ICE_Heartbeat(b);\n\n\tlen = VFS_READ(b->broker, b->in+b->insize, sizeof(b->in)-1-b->insize);\n\tif (!len)\n\t{\n\t\tFTENET_ICE_Flush(b);\n\n\t\tif (realtime > b->nextping)\n\t\t{\t//nothing happening... make sure the connection isn't dead...\n\t\t\tFTENET_ICE_SplurgeWS(b, WS_PACKETTYPE_PING, NULL, 0, NULL, 0);\n\t\t\tb->nextping = realtime + 100;\n\t\t}\n\t\treturn false;\t//nothing new\n\t}\n\tif (len < 0)\n\t{\n\t\tif (!b->error)\n\t\t\tCon_Printf(\"rtc broker connection to %s failed (retry: 30 secs)\\n\", b->brokername);\n\t\tb->error = true;\n\t\tgoto handleerror;\n\t}\n\tb->insize += len;\n\tb->in[b->insize] = 0;\n\tofs = 0;\n\n\tb->nextping = realtime + 100;\n\tb->timeout = max(b->timeout, realtime + 270);\n\n\tif (b->handshaking)\n\t{\t//we're still waiting for an http 101 code. websocket data starts straight after.\n\t\tchar *end = strstr(b->in, \"\\r\\n\\r\\n\");\n\t\tif (!end)\n\t\t\treturn false;\t//not available yet...\n\t\tif (strncmp(b->in, \"HTTP/1.1 101 \", 13))\n\t\t{\n\t\t\tb->error = ~0;\n\t\t\treturn false;\n\t\t}\n\t\tend+=4;\n\t\tb->handshaking = false; //done...\n\n\t\tofs = (qbyte*)end-b->in;\n\t}\n\n\twhile (b->insize >= ofs+2)\n\t{\n\t\tctrl = b->in[ofs+0];\n\t\tlen = b->in[ofs+1];\n\t\tofs+=2;\n\t\tif (len > 126)\n\t\t{//unsupported\n\t\t\tb->error = 1;\n\t\t\tbreak;\n\t\t}\n\t\telse if (len == 126)\n\t\t{\n\t\t\tif (b->insize <= 4)\n\t\t\t\tbreak;\n\t\t\tlen = (b->in[ofs+0]<<8)|(b->in[ofs+1]);\n\t\t\tofs+=2;\n\t\t}\n\t\tif (b->insize < ofs+len)\n\t\t\tbreak;\n\t\tn = b->in[ofs+len];\n\t\tb->in[ofs+len] = 0;\n\n\t\tswitch(ctrl & 0xf)\n\t\t{\n\t\tcase WS_PACKETTYPE_PING:\n\t\t\tFTENET_ICE_SplurgeWS(b, WS_PACKETTYPE_PONG, NULL, 0, b->in+ofs, len);\n\t\t\tFTENET_ICE_Flush(b);\n\t\t\tbreak;\n\t\tcase WS_PACKETTYPE_CLOSE:\n\t\t\tb->error = true;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tbreak;\n\t\tcase WS_PACKETTYPE_BINARYFRAME:\n\t\t\tcmd = b->in[ofs];\n\t\t\tcl = (short)(b->in[ofs+1] | (b->in[ofs+2]<<8));\n\t\t\tdata = b->in+ofs+3;\n\n\t\t\tswitch(cmd)\n\t\t\t{\n\t\t\tcase ICEMSG_PEERLOST:\t//the broker lost its connection to our peer...\n\t\t\t\tif (cl == -1)\n\t\t\t\t{\n\t\t\t\t\tb->error = true;\n\t\t\t\t\tif (net_ice_debug.ival)\n\t\t\t\t\t\tCon_Printf(S_COLOR_GRAY\"[%s]: Broker host lost connection: %s\\n\", b->ice?b->ice->friendlyname:\"?\", *data?data:\"<NO REASON>\");\n\t\t\t\t}\n\t\t\t\telse if (cl >= 0 && cl < b->numclients)\n\t\t\t\t{\n\t\t\t\t\tif (net_ice_debug.ival)\n\t\t\t\t\t\tCon_Printf(S_COLOR_GRAY\"[%s]: Broker client lost connection: %s\\n\", b->clients[cl].ice?b->clients[cl].ice->friendlyname:\"?\", *data?data:\"<NO REASON>\");\n\t\t\t\t\tif (b->clients[cl].ice)\n\t\t\t\t\t\ticeapi.Close(b->clients[cl].ice, false);\n\t\t\t\t\tb->clients[cl].ice = NULL;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase ICEMSG_NAMEINUSE:\n\t\t\t\tCon_Printf(\"Unable to listen on /%s - name already taken\\n\", b->gamename);\n\t\t\t\tb->error = true;\t//try again later.\n\t\t\t\tbreak;\n\t\t\tcase ICEMSG_GREETING:\t//reports the trailing url we're 'listening' on. anyone else using that url will connect to us.\n\t\t\t\tdata = strchr(data, '/');\n\t\t\t\tif (data++)\n\t\t\t\t\tQ_strncpyz(b->gamename, data, sizeof(b->gamename));\n\t\t\t\tCon_Printf(\"Publicly listening on /%s\\n\", b->gamename);\n\t\t\t\tbreak;\n\t\t\tcase ICEMSG_NEWPEER:\t//relay connection established with a new peer\n\t\t\t\t//note that the server ought to wait for an offer from the client before replying with any ice state, but it doesn't really matter for our use-case.\n\t\t\t\t{\n\t\t\t\t\tchar peer[MAX_QPATH];\n\t\t\t\t\tchar relay[MAX_QPATH];\n\t\t\t\t\tchar *s;\n\t\t\t\t\tBuf_ReadString(&data, b->in+ofs+len, peer, sizeof(peer));\n\t\t\t\t\tBuf_ReadString(&data, b->in+ofs+len, relay, sizeof(relay));\n\n\t\t\t\t\tif (b->generic.islisten)\n\t\t\t\t\t{\n\t//\t\t\t\t\tCon_DPrintf(\"Client connecting: %s\\n\", data);\n\t\t\t\t\t\tif (cl < 1024 && cl >= b->numclients)\n\t\t\t\t\t\t{\t//looks like a new one... but don't waste memory\n\t\t\t\t\t\t\tZ_ReallocElements((void**)&b->clients, &b->numclients, cl+1, sizeof(b->clients[0]));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (cl >= 0 && cl < b->numclients)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tFTENET_ICE_Establish(b, *peer?peer:NULL, cl, &b->clients[cl].ice);\n\t\t\t\t\t\t\tfor (s = relay; (s=COM_Parse(s)); )\n\t\t\t\t\t\t\t\ticeapi.Set(b->clients[cl].ice, \"server\", com_token);\n\t\t\t\t\t\t\tif (net_ice_debug.ival)\n\t\t\t\t\t\t\t\tCon_Printf(S_COLOR_GRAY\"[%s]: New client spotted...\\n\", b->clients[cl].ice?b->clients[cl].ice->friendlyname:\"?\");\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (net_ice_debug.ival)\n\t\t\t\t\t\t\tCon_Printf(S_COLOR_GRAY\"[%s]: New client spotted, but index is unusable\\n\", \"?\");\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\t//Con_DPrintf(\"Server found: %s\\n\", data);\n\t\t\t\t\t\tFTENET_ICE_Establish(b, *peer?peer:NULL, cl, &b->ice);\n\t\t\t\t\t\tb->serverid = cl;\n\t\t\t\t\t\tfor (s = relay; (s=COM_Parse(s)); )\n\t\t\t\t\t\t\ticeapi.Set(b->ice, \"server\", com_token);\n\t\t\t\t\t\tif (net_ice_debug.ival)\n\t\t\t\t\t\t\tCon_Printf(S_COLOR_GRAY\"[%s]: Meta channel to game server now open\\n\", b->ice?b->ice->friendlyname:\"?\");\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase ICEMSG_OFFER:\t//we received an offer from a client\n\t\t\t\tjson = JSON_Parse(data);\n\t\t\t\tif (json)\n\t\t\t\t\t//should probably also verify the type.\n\t\t\t\t\tdata = JSON_GetString(json, \"sdp\", com_token,sizeof(com_token), NULL);\n\t\t\t\tif (b->generic.islisten)\n\t\t\t\t{\n\t\t\t\t\tif (cl >= 0 && cl < b->numclients && b->clients[cl].ice)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (net_ice_debug.ival)\n\t\t\t\t\t\t\tCon_Printf(S_COLOR_GRAY\"[%s]: Got offer:\\n%s\\n\", b->clients[cl].ice?b->clients[cl].ice->friendlyname:\"?\", data);\n\t\t\t\t\t\ticeapi.Set(b->clients[cl].ice, \"sdpoffer\", data);\n\t\t\t\t\t\ticeapi.Set(b->clients[cl].ice, \"state\", STRINGIFY(ICE_CONNECTING));\n\n\t\t\t\t\t\tFTENET_ICE_SendOffer(b, cl, b->clients[cl].ice, \"sdpanswer\");\n\t\t\t\t\t}\n\t\t\t\t\telse if (net_ice_debug.ival)\n\t\t\t\t\t\tCon_Printf(S_COLOR_GRAY\"[%s]: Got bad offer/answer:\\n%s\\n\", b->clients[cl].ice?b->clients[cl].ice->friendlyname:\"?\", data);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tif (b->ice)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (net_ice_debug.ival)\n\t\t\t\t\t\t\tCon_Printf(S_COLOR_GRAY\"[%s]: Got answer:\\n%s\\n\", b->ice?b->ice->friendlyname:\"?\", data);\n\t\t\t\t\t\ticeapi.Set(b->ice, \"sdpanswer\", data);\n\t\t\t\t\t\ticeapi.Set(b->ice, \"state\", STRINGIFY(ICE_CONNECTING));\n\t\t\t\t\t}\n\t\t\t\t\telse if (net_ice_debug.ival)\n\t\t\t\t\t\tCon_Printf(S_COLOR_GRAY\"[%s]: Got bad offer/answer:\\n%s\\n\", b->ice?b->ice->friendlyname:\"?\", data);\n\t\t\t\t}\n\t\t\t\tJSON_Destroy(json);\n\t\t\t\tbreak;\n\t\t\tcase ICEMSG_CANDIDATE:\n\t\t\t\tjson = JSON_Parse(data);\n\t\t\t\tif (json)\n\t\t\t\t{\n\t\t\t\t\tdata = com_token;\n\t\t\t\t\tcom_token[0]='a';\n\t\t\t\t\tcom_token[1]='=';\n\t\t\t\t\tcom_token[2]=0;\n\t\t\t\t\tJSON_GetString(json, \"candidate\", com_token+2,sizeof(com_token)-2, NULL);\n\t\t\t\t}\n//\t\t\t\tCon_Printf(\"Candidate update: %s\\n\", data);\n\t\t\t\tif (b->generic.islisten)\n\t\t\t\t{\n\t\t\t\t\tif (cl >= 0 && cl < b->numclients && b->clients[cl].ice)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (net_ice_debug.ival)\n\t\t\t\t\t\t\tCon_Printf(S_COLOR_GRAY\"[%s]: Got candidate:\\n%s\\n\", b->clients[cl].ice->friendlyname, data);\n\t\t\t\t\t\ticeapi.Set(b->clients[cl].ice, \"sdp\", data);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tif (b->ice)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (net_ice_debug.ival)\n\t\t\t\t\t\t\tCon_Printf(S_COLOR_GRAY\"[%s]: Got candidate:\\n%s\\n\", b->ice->friendlyname, data);\n\t\t\t\t\t\ticeapi.Set(b->ice, \"sdp\", data);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tJSON_Destroy(json);\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tif (net_ice_debug.ival)\n\t\t\t\t\tCon_Printf(S_COLOR_GRAY\"[%s]: Broker send unknown packet: %i\\n\", b->ice?b->ice->friendlyname:\"?\", cmd);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\n\t\tofs+=len;\n\t\tb->in[ofs] = n;\n\n\t}\n\n\tif (ofs)\n\t{\t//and eat the newly parsed data...\n\t\tb->insize -= ofs;\n\t\tmemmove(b->in, b->in+ofs, b->insize);\n\t}\n\n\tFTENET_ICE_Flush(b);\n\treturn false;\n}\nstatic void FTENET_ICE_PrintStatus(ftenet_generic_connection_t *gcon)\n{\n\tftenet_ice_connection_t *b = (void*)gcon;\n\tsize_t c;\n\n\tif (b->ice)\n\t\tICE_Debug(b->ice);\n\tif (b->numclients)\n\t{\n\t\tsize_t activeice = 0;\n\t\tfor (c = 0; c < b->numclients; c++)\n\t\t\tif (b->clients[c].ice)\n\t\t\t{\n\t\t\t\tactiveice++;\n\t\t\t\tICE_PrintSummary(b->clients[c].ice, b->generic.islisten);\n\t\t\t}\n\t\tCon_Printf(\"%u ICE connections\\n\", (unsigned)activeice);\n\t}\n}\nstatic int FTENET_ICE_GetLocalAddresses(struct ftenet_generic_connection_s *gcon, unsigned int *adrflags, netadr_t *addresses, const char **adrparms, int maxaddresses)\n{\n\tftenet_ice_connection_t *b = (void*)gcon;\n\tif (maxaddresses < 1)\n\t\treturn 0;\n\t*addresses = b->brokeradr;\n\t*adrflags = 0;\n\t*adrparms = b->gamename;\n\treturn 1;\n}\n\nstatic qboolean FTENET_ICE_ChangeLocalAddress(struct ftenet_generic_connection_s *gcon, const char *address, netadr_t *newadr)\n{\n\tftenet_ice_connection_t *b = (void*)gcon;\n\tnetadr_t adr;\n\tconst char *path;\n\n\tif (!NET_StringToAdr2(address, PORT_ICEBROKER, &adr, 1, &path))\n\t\treturn true;\t//err... something failed? don't break what works!\n\n\tif (!NET_CompareAdr(&adr, &b->brokeradr))\n\t\treturn false;\t//the broker changed! zomg! just kill it all!\n\n\tif (path && *path++=='/')\n\t{\n\t\tif (*path && strcmp(path, b->gamename))\n\t\t\treturn false;\t//it changed! and we care! break everything!\n\t}\n\treturn true;\n}\n\nftenet_generic_connection_t *FTENET_ICE_EstablishConnection(ftenet_connections_t *col, const char *address, netadr_t adr, const struct dtlspeercred_s *peerinfo)\n{\n\tftenet_ice_connection_t *newcon;\n\tconst char *path;\n\tchar *c;\n\n\tif (!NET_StringToAdr2(address, PORT_ICEBROKER, &adr, 1, &path))\n\t\treturn NULL;\n/*\tif (adr.prot == NP_ICES)\n\t\tadr.prot = NP_TLS;\n\telse if (adr.prot == NP_ICE)\n\t\tadr.prot = NP_STREAM;\n*/\n\tnewcon = Z_Malloc(sizeof(*newcon));\n\n\tif (!strncmp(address, \"ice://\", 6)||!strncmp(address, \"rtc://\", 6))\n\t\taddress+=6;\n\telse if (!strncmp(address, \"ices://\", 7)||!strncmp(address, \"rtcs://\", 7))\n\t\taddress+=7;\n\tif (address == path && *path=='/')\n\t{\n\t\tif (!strncmp(net_ice_broker.string, \"tls://\", 6) || !strncmp(net_ice_broker.string, \"tcp://\", 6))\n\t\t\tQ_strncpyz(newcon->brokername, net_ice_broker.string+6, sizeof(newcon->brokername));\t//name is for prints only.\n\t\telse\n\t\t\tQ_strncpyz(newcon->brokername, net_ice_broker.string, sizeof(newcon->brokername));\t//name is for prints only.\n\t\tQ_strncpyz(newcon->gamename, path+1, sizeof(newcon->gamename));\t//so we know what to tell the broker.\n\t}\n\telse\n\t{\n\t\tQ_strncpyz(newcon->brokername, address, sizeof(newcon->brokername));\t//name is for prints only.\n\t\tif (path && *path == '/' && path-address < sizeof(newcon->brokername))\n\t\t{\n\t\t\tnewcon->brokername[path-address] = 0;\n\t\t\tQ_strncpyz(newcon->gamename, path+1, sizeof(newcon->gamename));\t//so we know what to tell the broker.\n\t\t}\n\t\telse\n\t\t\t*newcon->gamename = 0;\n\t}\n\tc = strchr(newcon->brokername, ':');\n\tif (c) *c = 0;\n\n\tnewcon->brokeradr = adr;\n\tnewcon->broker = NULL;\n\tnewcon->timeout = realtime;\n\tnewcon->heartbeat = realtime;\n\tnewcon->nextping = realtime;\n\tnewcon->generic.owner = col;\n\tnewcon->generic.thesocket = INVALID_SOCKET;\n\n\tnewcon->generic.addrtype[0] = NA_INVALID;\n\tnewcon->generic.addrtype[1] = NA_INVALID;\n\n\tnewcon->generic.GetPacket = FTENET_ICE_GetPacket;\n\tnewcon->generic.SendPacket = FTENET_ICE_SendPacket;\n\tnewcon->generic.Close = FTENET_ICE_Close;\n\tnewcon->generic.PrintStatus = FTENET_ICE_PrintStatus;\n\tnewcon->generic.GetLocalAddresses = FTENET_ICE_GetLocalAddresses;\n\tnewcon->generic.ChangeLocalAddress = FTENET_ICE_ChangeLocalAddress;\n\n\tnewcon->generic.islisten = col->islisten;\n\n\treturn &newcon->generic;\n}\n\nvoid ICE_Init(void)\n{\n\tCvar_Register(&net_ice_exchangeprivateips, \"networking\");\n\tCvar_Register(&net_ice_allowstun, \"networking\");\n\tCvar_Register(&net_ice_allowturn, \"networking\");\n\tCvar_Register(&net_ice_allowmdns, \"networking\");\n\tCvar_Register(&net_ice_relayonly, \"networking\");\n\tCvar_Register(&net_ice_usewebrtc, \"networking\");\n\tCvar_Register(&net_ice_servers, \"networking\");\n\tCvar_Register(&net_ice_debug,\t\"networking\");\n\tCmd_AddCommand(\"net_ice_show\", ICE_Show_f);\n}\n\n\n#ifdef HAVE_SERVER\nvoid SVC_ICE_Offer(void)\n{\t//handles an 'ice_offer' udp message from a broker\n\textern cvar_t net_ice_servers;\n\tstruct icestate_s *ice;\n\tstatic float throttletimer;\n\tconst char *sdp, *s;\n\tchar buf[1400];\n\tint sz;\n\tchar *clientaddr = Cmd_Argv(1);\t//so we can do ip bans on the client's srflx address\n\tchar *brokerid = Cmd_Argv(2);\t//specific id to identify the pairing on the broker.\n\tnetadr_t adr;\n\tjson_t *json;\n\tif (!sv.state)\n\t\treturn;\t//err..?\n\tif (net_from.prot != NP_DTLS && net_from.prot != NP_WSS && net_from.prot != NP_TLS)\n\t{\t//a) dtls provides a challenge (ensuring we can at least ban them).\n\t\t//b) this contains the caller's ips. We'll be pinging them anyway, but hey. also it'll be too late at this point but it keeps the other side honest.\n\t\tCon_ThrottlePrintf(&throttletimer, 0, CON_WARNING\"%s: ice handshake from %s was unencrypted\\n\", NET_AdrToString (buf, sizeof(buf), &net_from), clientaddr);\n\t\treturn;\n\t}\n\n\tif (!NET_StringToAdr_NoDNS(clientaddr, 0, &adr))\t//no dns-resolution denial-of-service attacks please.\n\t{\n\t\tCon_ThrottlePrintf(&throttletimer, 0, CON_WARNING\"%s: ice handshake specifies bad client address: %s\\n\", NET_AdrToString (buf, sizeof(buf), &net_from), clientaddr);\n\t\treturn;\n\t}\n\tif (SV_BannedReason(&adr)!=NULL)\n\t{\n\t\tCon_ThrottlePrintf(&throttletimer, 0, CON_WARNING\"%s: ice handshake for %s - banned\\n\", NET_AdrToString (buf, sizeof(buf), &net_from), clientaddr);\n\t\treturn;\n\t}\n\n\tice = iceapi.Create(NULL, brokerid, clientaddr, ICEM_WEBRTC, ICEP_QWSERVER, false);\n\tif (!ice)\n\t\treturn;\t//some kind of error?!?\n\t//use the sender as a stun server. FIXME: put server's address in the request instead.\n\ticeapi.Set(ice, \"server\", va(\"stun:%s\", NET_AdrToString (buf, sizeof(buf), &net_from)));\t//the sender should be able to act as a stun server for use. should probably just pass who its sending to and call it a srflx anyway, tbh.\n\n\ts = net_ice_servers.string;\n\twhile((s=COM_Parse(s)))\n\t\ticeapi.Set(ice, \"server\", com_token);\n\n\tsdp = MSG_ReadString();\n\tjson = JSON_Parse(sdp);\t//browsers are poo\n\tif (json)\n\t\tsdp = JSON_GetString(json, \"sdp\", buf,sizeof(buf), \"\");\n\n\tif (iceapi.Set(ice, \"sdpoffer\", sdp))\n\t{\n\t\ticeapi.Set(ice, \"state\", STRINGIFY(ICE_CONNECTING));\t//skip gathering, just trickle.\n\n\t\tQ_snprintfz(buf, sizeof(buf), \"\\xff\\xff\\xff\\xff\"\"ice_answer %s\", brokerid);\n\t\tsz = strlen(buf)+1;\n\t\tif (iceapi.Get(ice, \"sdpanswer\", buf+sz, sizeof(buf)-sz))\n\t\t{\n\t\t\tsz += strlen(buf+sz);\n\n\t\t\tNET_SendPacket(svs.sockets, sz, buf, &net_from);\n\t\t}\n\t}\n\tJSON_Destroy(json);\n\n\t//and because we won't have access to its broker, disconnect it from any persistent state to let it time out.\n\ticeapi.Close(ice, false);\n}\nvoid SVC_ICE_Candidate(void)\n{\t//handles an 'ice_ccand' udp message from a broker\n\tstruct icestate_s *ice;\n\tjson_t *json;\n\tconst char *sdp;\n\tchar buf[1400];\n\tchar *brokerid = Cmd_Argv(1);\t//specific id to identify the pairing on the broker.\n\tunsigned int seq = atoi(Cmd_Argv(2));\t//their seq, to ack and prevent dupes\n\tunsigned int ack = atoi(Cmd_Argv(3));\t//so we don't resend endlessly... *cough*\n\tif (net_from.prot != NP_DTLS && net_from.prot != NP_WSS && net_from.prot != NP_TLS)\n\t{\n\t\treturn;\n\t}\n\tice = iceapi.Find(NULL, brokerid);\n\tif (!ice)\n\t\treturn;\t//bad state. lost packet?\n\n\t//parse the inbound candidates\n\tfor(;;)\n\t{\n\t\tsdp = MSG_ReadStringLine();\n\t\tif (msg_badread || !*sdp)\n\t\t\tbreak;\n\t\tif (seq++ < ice->u.inseq)\n\t\t\tcontinue;\n\t\tice->u.inseq++;\n\t\tjson = JSON_Parse(sdp);\n\t\tif (json)\n\t\t{\n\t\t\tsdp = buf;\n\t\t\tbuf[0]='a';\n\t\t\tbuf[1]='=';\n\t\t\tbuf[2]=0;\n\t\t\tJSON_GetString(json, \"candidate\", buf+2,sizeof(buf)-2, NULL);\n\t\t}\n\t\ticeapi.Set(ice, \"sdp\", sdp);\n\t\tJSON_Destroy(json);\n\t}\n\n\twhile (ack > ice->u.outseq)\n\t{\t//drop an outgoing candidate line\n\t\tchar *nl = strchr(ice->u.text, '\\n');\n\t\tif (nl)\n\t\t{\n\t\t\tnl++;\n\t\t\tmemmove(ice->u.text, nl, strlen(nl)+1);\t//chop it away.\n\t\t\tice->u.outseq++;\n\t\t\tcontinue;\n\t\t}\n\t\t//wut?\n\t\tif (ack > ice->u.outseq)\n\t\t\tice->u.outseq = ack;\t//a gap? oh noes!\n\t\tbreak;\n\t}\n\n\t//check for new candidates to include\n\twhile (iceapi.GetLCandidateSDP(ice, buf, sizeof(buf)))\n\t{\n\t\tZ_StrCat(&ice->u.text, buf);\n\t\tZ_StrCat(&ice->u.text, \"\\n\");\n\t}\n\n\tQ_snprintfz(buf, sizeof(buf), \"\\xff\\xff\\xff\\xff\"\"ice_scand %s %u %u\\n%s\", brokerid, ice->u.outseq, ice->u.inseq, ice->u.text?ice->u.text:\"\");\n\tNET_SendPacket(svs.sockets, strlen(buf), buf, &net_from);\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "engine/common/net_ssl_gnutls.c",
    "content": "//This file should be easily portable.\n//The biggest strength of this plugin system is that ALL interactions are performed via\n//named functions, this makes it *really* easy to port plugins from one engine to another.\n\n#include \"quakedef.h\"\n#include \"netinc.h\"\n\n#ifndef GNUTLS_STATIC\n\t#define GNUTLS_DYNAMIC \t//statically linking is bad, because that just dynamically links to a .so that probably won't exist.\n\t\t\t\t\t\t\t//on the other hand, it does validate that the function types are correct.\n#endif\n\n#ifdef HAVE_GNUTLS\n\n\t#include <gnutls/gnutls.h>\n\t#if GNUTLS_VERSION_MAJOR >= 3\n\t\t#include <gnutls/abstract.h>\n\t#endif\n\t#include <gnutls/x509.h>\n\t#if GNUTLS_VERSION_MAJOR >= 3 && defined(HAVE_DTLS)\n\t\t#include <gnutls/dtls.h>\n\t#else\n\t\t#undef HAVE_DTLS\n\t#endif\n\t#define gnutls_connection_end_t unsigned int\n\n\t\t#if GNUTLS_VERSION_MAJOR < 3 || (GNUTLS_VERSION_MAJOR == 3 && GNUTLS_VERSION_MINOR < 3)\n\t\t\t#define GNUTLS_SONUM 26\t//cygwin or something.\n\t\t#endif\n\t\t#if GNUTLS_VERSION_MAJOR == 3 && GNUTLS_VERSION_MINOR == 3\n\t\t\t#define GNUTLS_SOPREFIX \"-deb0\"\t//not sure what this is about.\n\t\t\t#define GNUTLS_SONUM 28\t//debian jessie\n\t\t#endif\n\t\t#if GNUTLS_VERSION_MAJOR == 3 && GNUTLS_VERSION_MINOR == 4\n\t\t\t#define GNUTLS_SONUM 30\t//ubuntu 16.04\n\t\t#endif\n\t\t#if GNUTLS_VERSION_MAJOR == 3 && GNUTLS_VERSION_MINOR == 5\n\t\t\t#define GNUTLS_SONUM 30\t//debian stretch\n\t\t#endif\n\t\t#if GNUTLS_VERSION_MAJOR == 3 && GNUTLS_VERSION_MINOR > 5\n\t\t\t#define GNUTLS_SONUM 30\t//no idea what the future holds. maybe we'll be lucky...\n\t\t#endif\n\t\t#ifndef GNUTLS_SONUM\n\t\t\t#pragma message \"GNUTLS version not recognised. Will probably not be loadable.\"\n\t\t#endif\n\t\t#ifndef GNUTLS_SOPREFIX\n\t\t\t#define GNUTLS_SOPREFIX\n\t\t#endif\n\n#if GNUTLS_VERSION_MAJOR >= 3\n#if GNUTLS_VERSION_MAJOR >= 3\n\t#define GNUTLS_HAVE_SYSTEMTRUST\n#endif\n#if GNUTLS_VERSION_MAJOR >= 4 || (GNUTLS_VERSION_MAJOR == 3 && (GNUTLS_VERSION_MINOR > 1 || (GNUTLS_VERSION_MINOR == 1 && GNUTLS_VERSION_PATCH >= 1)))\n\t#define GNUTLS_HAVE_VERIFY3\n#endif\n\n\n\n\n#ifdef GNUTLS_HAVE_SYSTEMTRUST\n\t#define GNUTLS_TRUSTFUNCS GNUTLS_FUNC(gnutls_certificate_set_x509_system_trust,int,(gnutls_certificate_credentials_t cred))\n#else\n\t#define GNUTLS_TRUSTFUNCS GNUTLS_FUNC(gnutls_certificate_set_x509_trust_file,void,(void))\n#endif\n#ifdef GNUTLS_HAVE_VERIFY3\n\t#define GNUTLS_VERIFYFUNCS \\\n\t\tGNUTLS_FUNC(gnutls_certificate_verify_peers3,int,(gnutls_session_t session,const char *hostname,unsigned int *status)) \\\n\t\tGNUTLS_FUNC(gnutls_certificate_verification_status_print,int,(unsigned int status, gnutls_certificate_type_t type, gnutls_datum_t * out, unsigned int flags))\t\\\n\t\tGNUTLS_FUNC(gnutls_certificate_type_get,gnutls_certificate_type_t,(gnutls_session_t session))\t\\\n\t\tGNUTLS_FUNC(gnutls_certificate_get_peers,const gnutls_datum_t *,(gnutls_session_t session, unsigned int *list_size))\n#else\n\t#define GNUTLS_VERIFYFUNCS \\\n\t\tGNUTLS_FUNC(gnutls_certificate_verify_peers2,int,(gnutls_session_t session, unsigned int *status)) \\\n\t\tGNUTLS_FUNC(gnutls_x509_crt_check_hostname,unsigned,(gnutls_x509_crt_t cert, const char *hostname)) \\\n\t\tGNUTLS_FUNC(gnutls_x509_crt_import,int,(gnutls_x509_crt_t cert, const gnutls_datum_t * data, gnutls_x509_crt_fmt_t format)) \\\n\t\tGNUTLS_FUNC(gnutls_certificate_get_peers,const gnutls_datum_t *,(gnutls_session_t session, unsigned int *list_size))\n#endif\n\n#ifdef HAVE_DTLS\n#define GNUTLS_DTLS_STUFF \\\n\t\tGNUTLS_FUNC(gnutls_key_generate,int,(gnutls_datum_t *key, unsigned int key_size)) \\\n\t\tGNUTLS_FUNC(gnutls_privkey_sign_hash,int,(gnutls_privkey_t signer, gnutls_digest_algorithm_t hash_algo, unsigned int flags, const gnutls_datum_t * hash_data, gnutls_datum_t * signature)) \\\n\t\tGNUTLS_FUNC(gnutls_certificate_get_x509_key,int,(gnutls_certificate_credentials_t res, unsigned index, gnutls_x509_privkey_t *key)) \\\n\t\tGNUTLS_FUNC(gnutls_transport_set_pull_timeout_function,void,(gnutls_session_t session, gnutls_pull_timeout_func func)) \\\n\t\tGNUTLS_FUNC(gnutls_dtls_cookie_verify,int,(gnutls_datum_t *key, void *client_data, size_t client_data_size, void *_msg, size_t msg_size, gnutls_dtls_prestate_st *prestate)) \\\n\t\tGNUTLS_FUNC(gnutls_dtls_cookie_send,int,(gnutls_datum_t *key, void *client_data, size_t client_data_size, gnutls_dtls_prestate_st *prestate, gnutls_transport_ptr_t ptr, gnutls_push_func push_func)) \\\n\t\tGNUTLS_FUNC(gnutls_dtls_prestate_set,void,(gnutls_session_t session, gnutls_dtls_prestate_st *prestate)) \\\n\t\tGNUTLS_FUNC(gnutls_dtls_set_mtu,void,(gnutls_session_t session, unsigned int mtu)) \\\n\t\tGNUTLS_FUNC(gnutls_psk_allocate_server_credentials,int,(gnutls_psk_server_credentials_t *sc)) \\\n\t\tGNUTLS_FUNC(gnutls_psk_set_server_credentials_function,void,(gnutls_psk_server_credentials_t cred, gnutls_psk_server_credentials_function *func)) \\\n\t\tGNUTLS_FUNC(gnutls_psk_set_server_credentials_hint,int,(gnutls_psk_server_credentials_t res, const char *hint)) \\\n\t\tGNUTLS_FUNC(gnutls_psk_allocate_client_credentials,int,(gnutls_psk_client_credentials_t *sc)) \\\n\t\tGNUTLS_FUNC(gnutls_psk_set_client_credentials_function,void,(gnutls_psk_client_credentials_t cred, gnutls_psk_client_credentials_function *func)) \\\n\t\tGNUTLS_FUNC(gnutls_psk_client_get_hint,const char *,(gnutls_session_t session))\n#else\n\t#define GNUTLS_DTLS_STUFF\n#endif\n\n\n#define GNUTLS_X509_STUFF \\\n\tGNUTLS_FUNC(gnutls_certificate_server_set_request,void,(gnutls_session_t session, gnutls_certificate_request_t req)) \\\n\tGNUTLS_FUNC(gnutls_sec_param_to_pk_bits,unsigned int,(gnutls_pk_algorithm_t algo, gnutls_sec_param_t param))\t\\\n\tGNUTLS_FUNC(gnutls_x509_crt_init,int,(gnutls_x509_crt_t * cert))\t\\\n\tGNUTLS_FUNC(gnutls_x509_crt_deinit,void,(gnutls_x509_crt_t cert))\t\\\n\tGNUTLS_FUNC(gnutls_x509_crt_import,int,(gnutls_x509_crt_t cert, const gnutls_datum_t * data, gnutls_x509_crt_fmt_t format))\t\\\n\tGNUTLS_FUNC(gnutls_x509_crt_set_version,int,(gnutls_x509_crt_t crt, unsigned int version))\t\\\n\tGNUTLS_FUNC(gnutls_x509_crt_set_activation_time,int,(gnutls_x509_crt_t cert, time_t act_time))\t\\\n\tGNUTLS_FUNC(gnutls_x509_crt_set_expiration_time,int,(gnutls_x509_crt_t cert, time_t exp_time))\t\\\n\tGNUTLS_FUNC(gnutls_x509_crt_set_serial,int,(gnutls_x509_crt_t cert, const void *serial, size_t serial_size))\t\\\n\tGNUTLS_FUNC(gnutls_x509_crt_set_dn,int,(gnutls_x509_crt_t crt, const char *dn, const char **err))\t\\\n\tGNUTLS_FUNC(gnutls_x509_crt_get_dn3,int,(gnutls_x509_crt_t crt, gnutls_datum_t * dn, unsigned flags))\t\\\n\tGNUTLS_FUNC(gnutls_x509_crt_set_issuer_dn,int,(gnutls_x509_crt_t crt,const char *dn, const char **err))\t\\\n\tGNUTLS_FUNC(gnutls_x509_crt_set_key,int,(gnutls_x509_crt_t crt, gnutls_x509_privkey_t key))\t\\\n\tGNUTLS_FUNC(gnutls_x509_crt_export2,int,(gnutls_x509_crt_t cert, gnutls_x509_crt_fmt_t format, gnutls_datum_t * out))\t\\\n\tGNUTLS_FUNC(gnutls_x509_privkey_init,int,(gnutls_x509_privkey_t * key))\t\\\n\tGNUTLS_FUNC(gnutls_x509_privkey_deinit,void,(gnutls_x509_privkey_t key))\t\\\n\tGNUTLS_FUNC(gnutls_x509_privkey_generate,int,(gnutls_x509_privkey_t key, gnutls_pk_algorithm_t algo, unsigned int bits, unsigned int flags))\t\\\n\tGNUTLS_FUNC(gnutls_x509_privkey_export2,int,(gnutls_x509_privkey_t key, gnutls_x509_crt_fmt_t format, gnutls_datum_t * out))\t\\\n\tGNUTLS_FUNC(gnutls_x509_crt_privkey_sign,int,(gnutls_x509_crt_t crt, gnutls_x509_crt_t issuer, gnutls_privkey_t issuer_key, gnutls_digest_algorithm_t dig, unsigned int flags))\t\\\n\tGNUTLS_FUNC(gnutls_privkey_init,int,(gnutls_privkey_t * key))\t\\\n\tGNUTLS_FUNC(gnutls_privkey_deinit,void,(gnutls_privkey_t key))\t\\\n\tGNUTLS_FUNC(gnutls_privkey_import_x509,int,(gnutls_privkey_t pkey, gnutls_x509_privkey_t key, unsigned int flags))\t\\\n\tGNUTLS_FUNC(gnutls_certificate_set_x509_key_mem,int,(gnutls_certificate_credentials_t res, const gnutls_datum_t * cert, const gnutls_datum_t * key, gnutls_x509_crt_fmt_t type))\t\\\n\tGNUTLS_FUNC(gnutls_pubkey_init,int,(gnutls_pubkey_t * key))\t\\\n\tGNUTLS_FUNC(gnutls_pubkey_deinit,void,(gnutls_pubkey_t key))\t\\\n\tGNUTLS_FUNC(gnutls_pubkey_import_x509,int,(gnutls_pubkey_t key, gnutls_x509_crt_t crt, unsigned int flags))\t\\\n\tGNUTLS_FUNC(gnutls_pubkey_verify_hash2,int,(gnutls_pubkey_t key, gnutls_sign_algorithm_t algo, unsigned int flags, const gnutls_datum_t * hash, const gnutls_datum_t * signature))\t\\\n\tGNUTLS_FUNC(gnutls_certificate_get_ours,const gnutls_datum_t*,(gnutls_session_t session))\t\\\n\tGNUTLS_FUNC(gnutls_certificate_get_crt_raw,int,(gnutls_certificate_credentials_t sc, unsigned idx1, unsigned idx2, gnutls_datum_t * cert))\n\n\n#define GNUTLS_FUNCS \\\n\tGNUTLS_FUNC(gnutls_bye,int,(gnutls_session_t session, gnutls_close_request_t how))\t\\\n\tGNUTLS_FUNC(gnutls_alert_get,gnutls_alert_description_t,(gnutls_session_t session)) \\\n\tGNUTLS_FUNC(gnutls_alert_get_name,const char *,(gnutls_alert_description_t alert)) \\\n\tGNUTLS_FUNC(gnutls_strerror,const char *,(int error))\t\\\n\tGNUTLS_FUNC(gnutls_handshake,int,(gnutls_session_t session))\t\\\n\tGNUTLS_FUNC(gnutls_transport_set_ptr,void,(gnutls_session_t session, gnutls_transport_ptr_t ptr))\t\\\n\tGNUTLS_FUNC(gnutls_transport_set_push_function,void,(gnutls_session_t session, gnutls_push_func push_func))\t\\\n\tGNUTLS_FUNC(gnutls_transport_set_pull_function,void,(gnutls_session_t session, gnutls_pull_func pull_func))\t\\\n\tGNUTLS_FUNC(gnutls_transport_set_errno,void,(gnutls_session_t session, int err))\t\\\n\tGNUTLS_FUNC(gnutls_error_is_fatal,int,(int error))\t\\\n\tGNUTLS_FUNC(gnutls_credentials_set,int,(gnutls_session_t, gnutls_credentials_type_t type, void* cred))\t\\\n\tGNUTLS_FUNC(gnutls_init,int,(gnutls_session_t * session, gnutls_connection_end_t con_end))\t\\\n\tGNUTLS_FUNC(gnutls_deinit,void,(gnutls_session_t session))\t\\\n\tGNUTLS_FUNC(gnutls_set_default_priority,int,(gnutls_session_t session))\t\\\n\tGNUTLS_FUNC(gnutls_certificate_allocate_credentials,int,(gnutls_certificate_credentials_t *sc))\t\\\n\tGNUTLS_FUNC(gnutls_certificate_free_credentials,void,(gnutls_certificate_credentials_t sc))\t\\\n\tGNUTLS_FUNC(gnutls_session_channel_binding,int,(gnutls_session_t session, gnutls_channel_binding_t cbtype, gnutls_datum_t * cb))\t\\\n\tGNUTLS_FUNC(gnutls_global_init,int,(void))\t\\\n\tGNUTLS_FUNC(gnutls_global_deinit,void,(void))\t\\\n\tGNUTLS_FUNC(gnutls_record_send,ssize_t,(gnutls_session_t session, const void *data, size_t sizeofdata))\t\\\n\tGNUTLS_FUNC(gnutls_record_recv,ssize_t,(gnutls_session_t session, void *data, size_t sizeofdata))\t\\\n\tGNUTLS_FUNC(gnutls_certificate_set_verify_function,void,(gnutls_certificate_credentials_t cred, gnutls_certificate_verify_function *func))\t\\\n\tGNUTLS_FUNC(gnutls_session_get_ptr,void*,(gnutls_session_t session))\t\\\n\tGNUTLS_FUNC(gnutls_session_set_ptr,void,(gnutls_session_t session, void *ptr))\t\\\n\tGNUTLS_FUNCPTR(gnutls_malloc,void*,(size_t sz),(sz))\t\\\n\tGNUTLS_FUNCPTR(gnutls_free,void,(void *ptr),(ptr))\t\\\n\tGNUTLS_FUNC(gnutls_server_name_set,int,(gnutls_session_t session, gnutls_server_name_type_t type, const void * name, size_t name_length))\t\\\n\tGNUTLS_TRUSTFUNCS\t\\\n\tGNUTLS_VERIFYFUNCS\t\\\n\tGNUTLS_DTLS_STUFF\t\\\n\tGNUTLS_X509_STUFF\n\n\n#ifdef GNUTLS_DYNAMIC\n\t#define GNUTLS_FUNC(n,ret,args) static ret (VARGS *q##n)args;\n\t#define GNUTLS_FUNCPTR(n,ret,arglist,callargs) static ret (VARGS **q##n)arglist;\n#else\n\t#define GNUTLS_FUNC(n,ret,args) static ret (VARGS *q##n)args = n;\n\t#define GNUTLS_FUNCPTR(n,ret,arglist,callargs) static ret VARGS q##n arglist {return n(callargs);};\n#endif\n\n#ifdef HAVE_DTLS\n\tGNUTLS_FUNC(gnutls_set_default_priority_append,int,(gnutls_session_t session, const char *add_prio, const char **err_pos, unsigned flags))\n#endif\nGNUTLS_FUNCS\n\n#undef GNUTLS_FUNC\n#undef GNUTLS_FUNCPTR\n\n#if defined(GNUTLS_DYNAMIC) && defined(HAVE_DTLS)\nstatic int VARGS fallback_gnutls_set_default_priority_append(gnutls_session_t session, const char *add_prio, const char **err_pos, unsigned flags)\n{\n\treturn qgnutls_set_default_priority(session);\n}\n#endif\n\nstatic struct\n{\n#ifdef GNUTLS_DYNAMIC\n\tdllhandle_t *hmod;\n#endif\n\tint initstatus[2];\n} gnutls;\n\nstatic qboolean Init_GNUTLS(void)\n{\n#ifdef GNUTLS_DYNAMIC\n\tdllfunction_t functable[] =\n\t{\n#define GNUTLS_FUNC(nam,ret,args) {(void**)&q##nam, #nam},\n#define GNUTLS_FUNCPTR(nam,ret,arglist,calllist) {(void**)&q##nam, #nam},\n\t\tGNUTLS_FUNCS\n#undef GNUTLS_FUNC\n#undef GNUTLS_FUNCPTR\n\t\t{NULL, NULL}\n\t};\n\t\n#ifdef GNUTLS_SONUM\n\t#ifdef __CYGWIN__\n\t\tgnutls.hmod = Sys_LoadLibrary(\"cyggnutls\"GNUTLS_SOPREFIX\"-\"STRINGIFY(GNUTLS_SONUM)\".dll\", functable);\n\t#else\n\t\tgnutls.hmod = Sys_LoadLibrary(\"libgnutls\"GNUTLS_SOPREFIX\".so.\"STRINGIFY(GNUTLS_SONUM), functable);\n\t#endif\n#else\n\tgnutls.hmod = Sys_LoadLibrary(\"libgnutls\"GNUTLS_SOPREFIX\".so\", functable);\t//hope and pray\n#endif\n\tif (!gnutls.hmod)\n\t\treturn false;\n\n#ifdef HAVE_DTLS\n\tqgnutls_set_default_priority_append = Sys_GetAddressForName(gnutls.hmod, \"gnutls_set_default_priority_append\");\n\tif (!qgnutls_set_default_priority_append)\n\t\tqgnutls_set_default_priority_append = fallback_gnutls_set_default_priority_append;\n#endif\n#endif\n\treturn true;\n}\n\ntypedef struct \n{\n\tvfsfile_t funcs;\n\tvfsfile_t *stream;\n\n\tchar certname[512];\n\tgnutls_session_t session;\n\n\tqboolean handshaking;\n\tqboolean datagram;\n\n\tint pullerror;\t//adding these two because actual networking errors are not getting represented properly, at least with regard to timeouts.\n\tint pusherror;\n\n\tgnutls_certificate_credentials_t certcred;\n\thashfunc_t *peerhashfunc;\n\tqbyte peerdigest[DIGEST_MAXSIZE];\n\n\tqboolean challenging;\t//not sure this is actually needed, but hey.\n\tvoid *cbctx;\n\tneterr_t(*cbpush)(void *cbctx, const qbyte *data, size_t datasize);\n\tqbyte *readdata;\n\tsize_t readsize;\n#ifdef HAVE_DTLS\n\tgnutls_dtls_prestate_st prestate;\n#endif\n//\tint mtu;\n} gnutlsfile_t;\n\nstatic void SSL_SetCertificateName(gnutlsfile_t *f, const char *hostname)\n{\n\tint i;\n\tif (hostname)\n\t{\n\t\tconst char *host = strstr(hostname, \"://\");\n\t\tif (host)\n\t\t\thostname = host+3;\n\t\t//any dtls:// prefix will have been stripped now.\n\t\tif (*hostname == '[')\n\t\t{\t//eg: [::1]:foo - skip the lead [ and strip the ] and any trailing data (hopefully just a :port or nothing)\n\t\t\thostname++;\n\t\t\thost = strchr(hostname, ']');\n\t\t\tif (host && host-hostname < sizeof(f->certname))\n\t\t\t{\n\t\t\t\tmemcpy(f->certname, hostname, host-hostname);\n\t\t\t\tf->certname[host-hostname] = 0;\n\t\t\t\thostname = f->certname;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\t//eg: 127.0.0.1:port - strip the port number if specified.\n\t\t\thost = strchr(hostname, ':');\n\t\t\tif (host && host-hostname < sizeof(f->certname))\n\t\t\t{\n\t\t\t\tmemcpy(f->certname, hostname, host-hostname);\n\t\t\t\tf->certname[host-hostname] = 0;\n\t\t\t\thostname = f->certname;\n\t\t\t}\n\t\t}\n\t\tfor (i = 0; hostname[i]; i++)\n\t\t{\n\t\t\tif (hostname[i] >= 'a' && hostname[i] <= 'z')\n\t\t\t\t;\n\t\t\telse if (hostname[i] >= 'A' && hostname[i] <= 'Z')\n\t\t\t\t;\n\t\t\telse if (hostname[i] >= '0' && hostname[i] <= '9')\n\t\t\t\t;\n\t\t\telse if (hostname[i] == '-' || hostname[i] == '.')\n\t\t\t\t;\n\t\t\telse\n\t\t\t{\n\t\t\t\thostname = NULL;\t//something invalid. bum.\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\t//we should have a cleaned up host name now, ready for (ab)use in certificates.\n\t}\n\n\tif (!hostname)\n\t\t*f->certname = 0;\n\telse if (hostname == f->certname)\n\t\t;\n\telse if (strlen(hostname) >= sizeof(f->certname))\n\t\t*f->certname = 0;\n\telse\n\t\tmemcpy(f->certname, hostname, strlen(hostname)+1);\n}\n\n#define CAFILE \"/etc/ssl/certs/ca-certificates.crt\"\n\nstatic void SSL_Close(vfsfile_t *vfs)\n{\n\tgnutlsfile_t *file = (void*)vfs;\n\n\tfile->handshaking = true;\t//so further attempts to use it will fail.\n\n\tif (file->session)\n\t{\n\t\tqgnutls_bye (file->session, file->datagram?GNUTLS_SHUT_WR:GNUTLS_SHUT_RDWR);\n\t\tqgnutls_deinit(file->session);\n\t\tfile->session = NULL;\n\t}\n\n\tif (file->certcred)\n\t{\n\t\tqgnutls_certificate_free_credentials(file->certcred);\n\t\tfile->certcred = NULL;\n\t}\n}\nstatic qboolean QDECL SSL_CloseFile(vfsfile_t *vfs)\n{\n\tgnutlsfile_t *file = (void*)vfs;\n\tSSL_Close(vfs);\n\tif (file->stream)\n\t{\n\t\tVFS_CLOSE(file->stream);\n\t\tfile->stream = NULL;\n\t}\n\tif (file->certcred)\n\t\tqgnutls_certificate_free_credentials(file->certcred);\n\tZ_Free(vfs);\n\treturn true;\n}\n\nstatic int SSL_CheckUserTrust(gnutls_session_t session, gnutlsfile_t *file, int gcertcode)\n{\n\tint ret = gcertcode?GNUTLS_E_CERTIFICATE_ERROR:GNUTLS_E_SUCCESS;\n#if defined(HAVE_CLIENT) && defined(HAVE_DTLS)\n\tunsigned int ferrcode;\n\t//when using dtls, we expect self-signed certs and persistent trust.\n\tif (file->datagram)\n\t{\n\t\tqbyte *certdata;\n\t\tsize_t certsize;\n\t\tunsigned int certcount, j;\n\t\tconst gnutls_datum_t *const certlist = qgnutls_certificate_get_peers(session, &certcount);\n\t\tfor (certsize = 0, j = 0; j < certcount; j++)\n\t\t\tcertsize += certlist[j].size;\n\t\tcertdata = malloc(certsize);\n\t\tfor (certsize = 0, j = 0; j < certcount; j++)\n\t\t{\n\t\t\tmemcpy(certdata+certsize, certlist[j].data, certlist[j].size);\n\t\t\tcertsize += certlist[j].size;\n\t\t}\n\n\t\t//if gcertcode is 0 then we can still pin it.\n\t\tferrcode = 0;\n\t\tif (gcertcode & GNUTLS_CERT_SIGNER_NOT_FOUND)\n\t\t\tferrcode |= CERTLOG_MISSINGCA;\n\t\tif (gcertcode & GNUTLS_CERT_UNEXPECTED_OWNER)\n\t\t\tferrcode |= CERTLOG_WRONGHOST;\n\t\tif (gcertcode & GNUTLS_CERT_EXPIRED)\n\t\t\tferrcode |= CERTLOG_EXPIRED;\n\t\tif (gcertcode & ~(GNUTLS_CERT_INVALID|GNUTLS_CERT_SIGNER_NOT_FOUND|GNUTLS_CERT_UNEXPECTED_OWNER|GNUTLS_CERT_EXPIRED))\n\t\t\tferrcode |= CERTLOG_UNKNOWN;\n\n\t\tif (CertLog_ConnectOkay(file->certname, certdata, certsize, ferrcode))\n\t\t\tret = GNUTLS_E_SUCCESS;\t//user has previously authorised it.\n\t\telse\n\t\t\tret = GNUTLS_E_CERTIFICATE_ERROR;\t//user didn't trust it yet\n\t\tfree(certdata);\n\t}\n#endif\n\n\treturn ret;\n}\n\nstatic int QDECL SSL_CheckCert(gnutls_session_t session)\n{\n\tgnutlsfile_t *file = qgnutls_session_get_ptr (session);\n\tunsigned int certstatus;\n\tqboolean preverified = false;\n\n\tsize_t knownsize;\n\tqbyte *knowndata = TLS_GetKnownCertificate(file->certname, &knownsize);\n\n\tif (knowndata)\n\t{\n\t\tunsigned int certcount, j;\n\t\tconst gnutls_datum_t *const certlist = qgnutls_certificate_get_peers(session, &certcount);\n\t\tif (!certlist || !certcount)\n\t\t{\n\t\t\tBZ_Free(knowndata);\n\t\t\treturn GNUTLS_E_CERTIFICATE_ERROR;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tsize_t offset = 0;\n\n\t\t\tfor (j = 0; j < certcount; offset += certlist[j++].size)\n\t\t\t{\n\t\t\t\tif (certlist[j].size+offset > knownsize)\n\t\t\t\t\tbreak;\t//overflow...\n\t\t\t\tif (memcmp(certlist[j].data, knowndata+offset, certlist[j].size))\n\t\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tif (j && j == certcount && offset == knownsize)\n\t\t\t\tpreverified = true;\n\t\t\telse\n\t\t\t{\n#ifdef _DEBUG\n\t\t\t\tfor (j = 0, offset = 0; j < certcount; j++)\n\t\t\t\t\toffset += certlist[j].size;\n\t\t\t\tCon_Printf(\"%s cert %\"PRIuSIZE\" bytes (chain %u)\\n\", file->certname, offset, certcount);\n\t\t\t\tCon_Printf(\"/*%s*/\\\"\", file->certname);\n\t\t\t\tfor (j = 0; file->certname[j]; j++)\n\t\t\t\t\tCon_Printf(\"\\\\x%02x\", file->certname[j]^0xff);\n\t\t\t\tCon_Printf(\"\\\\xff\");\n\t\t\t\tCon_Printf(\"\\\\x%02x\\\\x%02x\", (unsigned)offset&0xff, ((unsigned)offset>>8)&0xff);\n\t\t\t\tfor (j = 0; j < certcount; j++)\n\t\t\t\t{\n\t\t\t\t\tunsigned char *data = certlist[j].data;\n\t\t\t\t\tunsigned int datasize = certlist[j].size, k;\n\t\t\t\t\tfor (k = 0; k < datasize; k++)\n\t\t\t\t\t\tCon_Printf(\"\\\\x%02x\", data[k]^0xff);\n\t\t\t\t}\n\t\t\t\tCon_Printf(\"\\\",\\n\\n\");\n#endif\n\t\t\t\tCon_Printf(CON_ERROR \"%s: Reported certificate does not match known certificate. Possible MITM attack, alternatively just an outdated client.\\n\", file->certname);\n\t\t\t\tBZ_Free(knowndata);\n\t\t\t\treturn GNUTLS_E_CERTIFICATE_ERROR;\n\t\t\t}\n\t\t}\n\t\tBZ_Free(knowndata);\n\t}\n\n#ifdef GNUTLS_HAVE_VERIFY3\n\tif (qgnutls_certificate_verify_peers3(session, file->certname, &certstatus) >= 0)\n\t{\n\t\tgnutls_datum_t out = {NULL,0};\n\t\tgnutls_certificate_type_t type;\n\t\tint ret;\n\n\t\tif (preverified && (certstatus&~GNUTLS_CERT_EXPIRED) == (GNUTLS_CERT_INVALID|GNUTLS_CERT_SIGNER_NOT_FOUND))\n\t\t\treturn 0;\n\t\tret = SSL_CheckUserTrust(session, file, certstatus);\n\t\tif (!ret)\n\t\t\treturn ret;\n\n\t\ttype = qgnutls_certificate_type_get (session);\n\t\tif (qgnutls_certificate_verification_status_print(certstatus, type, &out, 0) >= 0)\n\t\t{\n\t\t\tCon_Printf(CON_ERROR \"%s: %s (%x)\\n\", file->certname, out.data, certstatus);\n\t\t\t(*qgnutls_free)(out.data);\n\t\t}\n\t\telse\n\t\t\tCon_Printf(CON_ERROR \"%s: UNKNOWN STATUS (%x)\\n\", file->certname, certstatus);\n\n\t\tif (tls_ignorecertificateerrors.ival)\n\t\t{\n\t\t\tCon_Printf(CON_ERROR \"%s: Ignoring certificate errors (tls_ignorecertificateerrors is %i)\\n\", file->certname, tls_ignorecertificateerrors.ival);\n\t\t\treturn 0;\n\t\t}\n\t}\n#else\n\tif (qgnutls_certificate_verify_peers2(session, &certstatus) >= 0)\n\t{\n\t\tint certslen;\n\t\t//grab the certificate\n\t\tconst gnutls_datum_t *const certlist = qgnutls_certificate_get_peers(session, &certslen);\n\t\tif (certlist && certslen)\n\t\t{\n\t\t\t//and make sure the hostname on it actually makes sense.\n\t\t\tint ret;\n\t\t\tgnutls_x509_crt_t cert;\n\t\t\tqgnutls_x509_crt_init(&cert);\n\t\t\tqgnutls_x509_crt_import(cert, certlist, GNUTLS_X509_FMT_DER);\n\t\t\tif (qgnutls_x509_crt_check_hostname(cert, file->certname))\n\t\t\t{\n\t\t\t\tif (preverified && (certstatus&~GNUTLS_CERT_EXPIRED) == (GNUTLS_CERT_INVALID|GNUTLS_CERT_SIGNER_NOT_FOUND))\n\t\t\t\t\treturn 0;\n\n\t\t\t\tret = SSL_CheckUserTrust(session, file, certstatus);\t//looks okay... pin it by default...\n\t\t\t\tif (!ret)\n\t\t\t\t\treturn ret;\n\n\t\t\t\tif (certstatus & GNUTLS_CERT_SIGNER_NOT_FOUND)\n\t\t\t\t\tCon_Printf(CON_ERROR \"%s: Certificate authority is not recognised\\n\", file->certname);\n\t\t\t\telse if (certstatus & GNUTLS_CERT_INSECURE_ALGORITHM)\n\t\t\t\t\tCon_Printf(CON_ERROR \"%s: Certificate uses insecure algorithm\\n\", file->certname);\n\t\t\t\telse if (certstatus & (GNUTLS_CERT_REVOCATION_DATA_ISSUED_IN_FUTURE|GNUTLS_CERT_REVOCATION_DATA_SUPERSEDED|GNUTLS_CERT_EXPIRED|GNUTLS_CERT_REVOKED|GNUTLS_CERT_NOT_ACTIVATED))\n\t\t\t\t\tCon_Printf(CON_ERROR \"%s: Certificate has expired or was revoked or not yet valid\\n\", file->certname);\n\t\t\t\telse if (certstatus & GNUTLS_CERT_SIGNATURE_FAILURE)\n\t\t\t\t\tCon_Printf(CON_ERROR \"%s: Certificate signature failure\\n\", file->certname);\n\t\t\t\telse\n\t\t\t\t\tCon_Printf(CON_ERROR \"%s: Certificate error\\n\", file->certname);\n\t\t\t\tif (tls_ignorecertificateerrors.ival)\n\t\t\t\t{\n\t\t\t\t\tCon_Printf(CON_ERROR \"%s: Ignoring certificate errors (tls_ignorecertificateerrors is %i)\\n\", file->certname, tls_ignorecertificateerrors.ival);\n\t\t\t\t\treturn 0;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t\tCon_DPrintf(CON_ERROR \"%s: certificate is for a different domain\\n\", file->certname);\n\t\t}\n\t}\n#endif\n\n\tCon_DPrintf(CON_ERROR \"%s: rejecting certificate\\n\", file->certname);\n\treturn GNUTLS_E_CERTIFICATE_ERROR;\n}\n\n#ifdef HAVE_DTLS\nstatic int QDECL SSL_CheckFingerprint(gnutls_session_t session)\n{\t//actual certificate doesn't matter so long as it matches the hash we expect.\n\tgnutlsfile_t *file = qgnutls_session_get_ptr (session);\n\tunsigned int certcount, j;\n\tconst gnutls_datum_t *const certlist = qgnutls_certificate_get_peers(session, &certcount);\n\tif (certlist && certcount)\n\t{\n\t\tqbyte digest[DIGEST_MAXSIZE];\n\t\tvoid *ctx = alloca(file->peerhashfunc->contextsize);\n\n\t\tfile->peerhashfunc->init(ctx);\n\t\tfor (j = 0; j < certcount; j++)\n\t\t\tfile->peerhashfunc->process(ctx, certlist[j].data, certlist[j].size);\n\t\tfile->peerhashfunc->terminate(digest, ctx);\n\n\t\tif (!memcmp(digest, file->peerdigest, file->peerhashfunc->digestsize))\n\t\t\treturn 0;\n\t\tCon_Printf(CON_ERROR \"%s: certificate chain (%i) does not match fingerprint\\n\", *file->certname?file->certname:\"<anon>\", certcount);\n\t}\n\telse\n\t\tCon_Printf(CON_ERROR \"%s: peer did not provide any certificate\\n\", *file->certname?file->certname:\"<anon>\");\n\treturn GNUTLS_E_CERTIFICATE_ERROR;\n}\n#endif\n\n//return 1 to read data.\n//-1 for error\n//0 for not ready\nstatic int SSL_DoHandshake(gnutlsfile_t *file)\n{\n\tint err;\n\t//session was previously closed = error\n\tif (!file->session)\n\t{\n\t\t//Sys_Printf(\"null session\\n\");\n\t\treturn VFS_ERROR_UNSPECIFIED;\n\t}\n\n\terr = qgnutls_handshake (file->session);\n\tif (err < 0)\n\t{\t//non-fatal errors can just handshake again the next time the caller checks to see if there's any data yet\n\t\t//(e_again or e_intr)\n\t\tif (!qgnutls_error_is_fatal(err))\n\t\t\treturn 0;\n\t\tif (developer.ival)\n\t\t{\n\t\t\tif (err == GNUTLS_E_FATAL_ALERT_RECEIVED)\n\t\t\t{\t//peer doesn't like us.\n\t\t\t\tgnutls_alert_description_t desc = qgnutls_alert_get(file->session);\n\t\t\t\tCon_Printf(CON_ERROR\"GNU%sTLS: %s: %s(%i)\\n\", file->datagram?\"D\":\"\", file->certname, qgnutls_alert_get_name(desc), desc);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t//we didn't like the peer.\n\t\t\t\tCon_Printf(CON_ERROR\"GNU%sTLS: %s: %s(%i)\\n\", file->datagram?\"D\":\"\", file->certname, qgnutls_strerror(err), err);\n\t\t\t}\n\t\t}\n\n//\t\tCon_Printf(\"%s: abort\\n\", file->certname);\n\n\t\tswitch(err)\n\t\t{\n\t\tcase GNUTLS_E_INSUFFICIENT_CREDENTIALS:\n\t\tcase GNUTLS_E_CERTIFICATE_ERROR:\t\terr = VFS_ERROR_UNTRUSTED;\t\tbreak;\n\t\tcase GNUTLS_E_SESSION_EOF:\n\t\tcase GNUTLS_E_PREMATURE_TERMINATION:\terr = VFS_ERROR_EOF;\t\t\tbreak;\n\t\tcase GNUTLS_E_PUSH_ERROR:\t\t\t\terr = file->pusherror;\t\t\tbreak;\n\t\tcase GNUTLS_E_PULL_ERROR:\t\t\t\terr = file->pullerror;\t\t\tbreak;\n\t\tdefault:\t\t\t\t\t\t\t\terr = VFS_ERROR_UNSPECIFIED;\tbreak;\n\t\t}\n\t\tSSL_Close(&file->funcs);\n\t\treturn err;\n\t}\n\tfile->handshaking = false;\n\treturn 1;\n}\n\nstatic int QDECL SSL_Read(struct vfsfile_s *f, void *buffer, int bytestoread)\n{\n\tgnutlsfile_t *file = (void*)f;\n\tint read;\n\n\tif (file->handshaking)\n\t{\n\t\tread = SSL_DoHandshake(file);\n\t\tif (read <= 0)\n\t\t\treturn read;\n\t}\n\n\tif (!bytestoread)\t//gnutls doesn't like this.\n\t\treturn VFS_ERROR_UNSPECIFIED;\t//caller is expecting data that we can never return, or something.\n\n\tread = qgnutls_record_recv(file->session, buffer, bytestoread);\n\tif (read < 0)\n\t{\n\t\tif (read == GNUTLS_E_PREMATURE_TERMINATION)\n\t\t{\n\t\t\tCon_Printf(\"TLS Premature Termination from %s\\n\", file->certname);\n\t\t\treturn VFS_ERROR_EOF;\n\t\t}\n\t\telse if (read == GNUTLS_E_REHANDSHAKE)\n\t\t{\n\t\t\tfile->handshaking = false;//gnutls_safe_renegotiation_status();\n\t\t\t//if false, 'recommended' to send an GNUTLS_A_NO_RENEGOTIATION alert, no idea how.\n\t\t}\n\t\telse if (!qgnutls_error_is_fatal(read))\n\t\t\treturn 0;\t//caller is expected to try again later, no real need to loop here, just in case it repeats (eg E_AGAIN)\n\t\telse\n\t\t{\n\t\t\tif (read == GNUTLS_E_PULL_ERROR)\n\t\t\t\tCon_Printf(\"GNUTLS_E_PULL_ERROR (%s)\\n\", file->certname);\n\t\t\telse\n\t\t\t\tCon_Printf(\"GNUTLS Read Warning %i (bufsize %i)\\n\", read, bytestoread);\n\t\t\treturn -1;\n\t\t}\n\t}\n\telse if (read == 0)\n\t\treturn VFS_ERROR_EOF;\t//closed by remote connection.\n\treturn read;\n}\nstatic int QDECL SSL_Write(struct vfsfile_s *f, const void *buffer, int bytestowrite)\n{\n\tgnutlsfile_t *file = (void*)f;\n\tint written;\n\n\tif (file->handshaking)\n\t{\n\t\twritten = SSL_DoHandshake(file);\n\t\tif (written <= 0)\n\t\t\treturn written;\n\t}\n\n\twritten = qgnutls_record_send(file->session, buffer, bytestowrite);\n\tif (written < 0)\n\t{\n\t\tif (!qgnutls_error_is_fatal(written))\n\t\t\treturn 0;\n\t\telse\n\t\t{\n\t\t\tCon_DPrintf(\"GNUTLS Send Error %i (%i bytes)\\n\", written, bytestowrite);\n\t\t\treturn VFS_ERROR_UNSPECIFIED;\n\t\t}\n\t}\n\telse if (written == 0)\n\t\treturn VFS_ERROR_EOF;\t//closed by remote connection.\n\treturn written;\n}\nstatic qboolean QDECL SSL_Seek (struct vfsfile_s *file, qofs_t pos)\n{\n\treturn false;\n}\nstatic qofs_t QDECL SSL_Tell (struct vfsfile_s *file)\n{\n\treturn 0;\n}\nstatic qofs_t QDECL SSL_GetLen (struct vfsfile_s *file)\n{\n\treturn 0;\n}\n\n\n#include <errno.h>\n\n/*functions for gnutls to call when it wants to send data*/\nstatic ssize_t SSL_Push(gnutls_transport_ptr_t p, const void *data, size_t size)\n{\n\tgnutlsfile_t *file = p;\n//\tSys_Printf(\"SSL_Push: %u\\n\", size);\n\tint done = VFS_WRITE(file->stream, data, size);\n\tif (done <= 0)\n\t{\n\t\tint eno;\n\t\tfile->pusherror = done;\n\t\tswitch(done)\n\t\t{\n\t\tcase VFS_ERROR_EOF:\t\t\treturn 0;\n\t\tcase VFS_ERROR_DNSFAILURE:\n\t\tcase VFS_ERROR_NORESPONSE:\teno = ECONNRESET;\tbreak;\n\t\tcase VFS_ERROR_TRYLATER:\teno = EAGAIN;\t\tbreak;\n\t\tcase VFS_ERROR_REFUSED:\t\teno = ECONNREFUSED;\tbreak;\n//\t\tcase VFS_ERROR_UNSPECIFIED:\n//\t\tcase VFS_ERROR_DNSFAILURE:\n//\t\tcase VFS_ERROR_WRONGCERT:\n//\t\tcase VFS_ERROR_UNTRUSTED:\n\t\tdefault:\t\t\t\t\teno = ECONNRESET;\tbreak;\n\t\t}\n\t\tqgnutls_transport_set_errno(file->session, eno);\n\t\treturn -1;\n\t}\n\treturn done;\n}\nstatic ssize_t SSL_Pull(gnutls_transport_ptr_t p, void *data, size_t size)\n{\n\tgnutlsfile_t *file = p;\n//\tSys_Printf(\"SSL_Pull: %u\\n\", size);\n\tint done = VFS_READ(file->stream, data, size);\n\tif (done <= 0)\n\t{\n\t\tint eno;\n\t\tfile->pullerror = done;\n\t\tswitch(done)\n\t\t{\n\t\tcase VFS_ERROR_EOF:\t\t\treturn 0;\n\t\tcase VFS_ERROR_DNSFAILURE:\n\t\tcase VFS_ERROR_NORESPONSE:\teno = ECONNRESET;\tbreak;\n\t\tcase VFS_ERROR_TRYLATER:\teno = EAGAIN;\t\tbreak;\n\t\tcase VFS_ERROR_REFUSED:\t\teno = ECONNREFUSED;\tbreak;\n\t\tdefault:\t\t\t\t\teno = ECONNRESET;\tbreak;\n\t\t}\n\t\tqgnutls_transport_set_errno(file->session, eno);\n\t\treturn -1;\n\t}\n\treturn done;\n}\n\nstatic ssize_t DTLS_Push(gnutls_transport_ptr_t p, const void *data, size_t size)\n{\n\tgnutlsfile_t *file = p;\n\n\tneterr_t ne = file->cbpush(file->cbctx, data, size);\n\n//\tSys_Printf(\"DTLS_Push: %u, err=%i\\n\", (unsigned)size, (int)ne);\n\n\tif (!file->session)\n\t\treturn ne?-1:size;\n\n\tswitch(ne)\n\t{\n\tcase NETERR_CLOGGED:\n\tcase NETERR_NOROUTE:\n\t\tqgnutls_transport_set_errno(file->session, EAGAIN);\n\t\treturn -1;\n\tcase NETERR_MTU:\n\t\tqgnutls_transport_set_errno(file->session, EMSGSIZE);\n\t\treturn -1;\n\tcase NETERR_DISCONNECTED:\n\t\tqgnutls_transport_set_errno(file->session, EPERM);\n\t\treturn -1;\n\tdefault:\n\t\tqgnutls_transport_set_errno(file->session, 0);\n\t\treturn size;\n\t}\n}\nstatic ssize_t DTLS_Pull(gnutls_transport_ptr_t p, void *data, size_t size)\n{\n\tgnutlsfile_t *file = p;\n\n//\tSys_Printf(\"DTLS_Pull: %u of %u\\n\", size, file->readsize);\n\n\tif (!file->readsize)\n\t{\t//no data left\n//\t\tSys_Printf(\"DTLS_Pull: EAGAIN\\n\");\n\t\tqgnutls_transport_set_errno(file->session, EAGAIN);\n\t\treturn -1;\n\t}\n\telse if (file->readsize > size)\n\t{\t//buffer passed is smaller than available data\n//\t\tSys_Printf(\"DTLS_Pull: EMSGSIZE\\n\");\n\t\tmemcpy(data, file->readdata, size);\n\t\tfile->readsize = 0;\n\t\tqgnutls_transport_set_errno(file->session, EMSGSIZE);\n\t\treturn -1;\n\t}\n\telse\n\t{\t//buffer is big enough to read it all\n\t\tsize = file->readsize;\n\t\tfile->readsize = 0;\n//\t\tSys_Printf(\"DTLS_Pull: reading %i\\n\", size);\n\t\tmemcpy(data, file->readdata, size);\n\t\tqgnutls_transport_set_errno(file->session, 0);\n\t\treturn size;\n\t}\n}\n#ifdef HAVE_DTLS\nstatic int DTLS_Pull_Timeout(gnutls_transport_ptr_t p, unsigned int timeout)\n{\t//gnutls (pointlessly) requires this function for dtls.\n\tgnutlsfile_t *f = p;\n//\tSys_Printf(\"DTLS_Pull_Timeout %i, %i\\n\", timeout, f->readsize);\n\treturn f->readsize>0?1:0;\n}\n#endif\n\n#ifdef USE_ANON\nstatic gnutls_anon_client_credentials_t anoncred[2];\n#else\nstatic gnutls_certificate_credentials_t xcred[2];\nstatic qboolean\tservercertfail;\n#endif\n#ifdef HAVE_DTLS\nstatic gnutls_datum_t cookie_key;\n#endif\n\nstatic vfsfile_t *SSL_OpenPrivKey(char *displayname, size_t displaysize)\n{\n#define privname \"privkey.pem\"\n\tvfsfile_t *privf;\n\tconst char *mode = displayname?\"wb\":\"rb\";\n\tint i = COM_CheckParm(\"-privkey\");\n\tif (i++)\n\t{\n\t\tif (displayname)\n\t\t\tif (!FS_DisplayPath(com_argv[i], FS_SYSTEM, displayname, displaysize))\n\t\t\t\tQ_strncpyz(displayname, com_argv[i], displaysize);\n\t\tprivf = FS_OpenVFS(com_argv[i], mode, FS_SYSTEM);\n\t}\n\telse\n\t{\n\t\tif (displayname)\n\t\t\tif (!FS_DisplayPath(privname, FS_ROOT, displayname, displaysize))\n\t\t\t\treturn NULL;\n\n\t\tprivf = FS_OpenVFS(privname, mode, FS_ROOT);\n\t}\n\treturn privf;\n#undef privname\n}\nstatic vfsfile_t *SSL_OpenPubKey(char *displayname, size_t displaysize)\n{\n#define fullchainname \"fullchain.pem\"\n#define pubname \"cert.pem\"\n\tvfsfile_t *pubf = NULL;\n\tconst char *mode = displayname?\"wb\":\"rb\";\n\tint i = COM_CheckParm(\"-pubkey\");\n\tif (i++)\n\t{\n\t\tif (displayname)\n\t\t\tQ_strncpyz(displayname, com_argv[i], displaysize);\n\t\tpubf = FS_OpenVFS(com_argv[i], mode, FS_SYSTEM);\n\t}\n\telse\n\t{\n\t\tif (!pubf && (!displayname || FS_DisplayPath(fullchainname, FS_ROOT, displayname, displaysize)))\n\t\t\tpubf = FS_OpenVFS(fullchainname, mode, FS_ROOT);\n\t\tif (!pubf && (!displayname || FS_DisplayPath(pubname, FS_ROOT, displayname, displaysize)))\n\t\t\tpubf = FS_OpenVFS(pubname, mode, FS_ROOT);\n\t}\n\treturn pubf;\n#undef pubname\n}\n\nstatic qboolean SSL_LoadPrivateCert(gnutls_certificate_credentials_t cred)\n{\n\tint ret = -1;\n\tgnutls_datum_t priv, pub;\n\tvfsfile_t *privf = SSL_OpenPrivKey(NULL, 0);\n\tvfsfile_t *pubf = SSL_OpenPubKey(NULL, 0);\n\tconst char *hostname = NULL;\n\n\tint i = COM_CheckParm(\"-certhost\");\n\tif (i)\n\t\thostname = com_argv[i+1];\n\n\tmemset(&priv, 0, sizeof(priv));\n\tmemset(&pub, 0, sizeof(pub));\n\n\tif ((!privf || !pubf))// && hostname)\n\t{\t//not found? generate a new one.\n\t\t//FIXME: how to deal with race conditions with multiple servers on the same host?\n\t\t//delay till the first connection? we at least write both files at the sameish time.\n\t\t//even so they might get different certs the first time the server(s) run.\n\t\t//TODO: implement a lockfile\n\t\tgnutls_x509_privkey_t key;\n\t\tgnutls_x509_crt_t cert;\n\t\tchar serial[64];\n\t\tconst char *errstr;\n\t\tgnutls_pk_algorithm_t privalgo = GNUTLS_PK_RSA;\n\n\t\tif (privf)\n\t\t{\n\t\t\tVFS_CLOSE(privf);\n\t\t\tprivf = NULL;\n\t\t}\n\t\tif (pubf)\n\t\t{\n\t\t\tVFS_CLOSE(pubf);\n\t\t\tpubf = NULL;\n\t\t}\n\n\t\tCon_Printf(\"Generating new GNUTLS key+cert...\\n\");\n\n\t\tqgnutls_x509_privkey_init(&key);\n\t\tret = qgnutls_x509_privkey_generate(key, privalgo, qgnutls_sec_param_to_pk_bits(privalgo, GNUTLS_SEC_PARAM_HIGH), 0);\n\t\tif (ret < 0)\n\t\t\tCon_Printf(CON_ERROR\"gnutls_x509_privkey_generate failed: %i\\n\", ret);\n\t\tret = qgnutls_x509_privkey_export2(key, GNUTLS_X509_FMT_PEM, &priv);\n\t\tif (ret < 0)\n\t\t\tCon_Printf(CON_ERROR\"gnutls_x509_privkey_export2 failed: %i\\n\", ret);\n\n\t\t//stoopid browsers insisting that serial numbers are different even on throw-away self-signed certs.\n\t\t//we should probably just go and make our own root ca/master. post it a cert and get a signed one (with sequential serial) back or something.\n\t\t//we'll probably want something like that for client certs anyway, for stat tracking.\n\t\tQ_snprintfz(serial, sizeof(serial), \"%u\", (unsigned)time(NULL));\n\n\t\tqgnutls_x509_crt_init(&cert);\n\t\tqgnutls_x509_crt_set_version(cert, 1);\n\t\tqgnutls_x509_crt_set_activation_time(cert, time(NULL)-1);\n\t\tqgnutls_x509_crt_set_expiration_time(cert, time(NULL)+(time_t)10*365*24*60*60);\n\t\tqgnutls_x509_crt_set_serial(cert, serial, strlen(serial));\n\t\tif (!hostname)\n\t\t\t/*qgnutls_x509_crt_set_key_usage(cert, GNUTLS_KEY_DIGITAL_SIGNATURE)*/;\n\t\telse\n\t\t{\n\t\t\tif (qgnutls_x509_crt_set_dn(cert, va(\"CN=%s\", hostname), &errstr) < 0)\n\t\t\t\tCon_Printf(CON_ERROR\"gnutls_x509_crt_set_dn failed: %s\\n\", errstr);\n\t\t\tif (qgnutls_x509_crt_set_issuer_dn(cert, va(\"CN=%s\", hostname), &errstr) < 0)\n\t\t\t\tCon_Printf(CON_ERROR\"gnutls_x509_crt_set_issuer_dn failed: %s\\n\", errstr);\n//\t\t\tqgnutls_x509_crt_set_key_usage(cert, GNUTLS_KEY_KEY_ENCIPHERMENT|GNUTLS_KEY_DATA_ENCIPHERMENT|);\n\t\t}\n\t\tqgnutls_x509_crt_set_key(cert, key);\n\n\t\t/*sign it with our private key*/\n\t\t{\n\t\t\tgnutls_privkey_t akey;\n\t\t\tqgnutls_privkey_init(&akey);\n\t\t\tqgnutls_privkey_import_x509(akey, key, GNUTLS_PRIVKEY_IMPORT_COPY);\n\t\t\tret = qgnutls_x509_crt_privkey_sign(cert, cert, akey, GNUTLS_DIG_SHA256, 0);\n\t\t\tif (ret < 0)\n\t\t\t\tCon_Printf(CON_ERROR\"gnutls_x509_crt_privkey_sign failed: %i\\n\", ret);\n\t\t\tqgnutls_privkey_deinit(akey);\n\t\t}\n\t\tret = qgnutls_x509_crt_export2(cert, GNUTLS_X509_FMT_PEM, &pub);\n\t\tqgnutls_x509_crt_deinit(cert);\n\t\tqgnutls_x509_privkey_deinit(key);\n\t\tif (ret < 0)\n\t\t\tCon_Printf(CON_ERROR\"gnutls_x509_crt_export2 failed: %i\\n\", ret);\n\n\t\tif (priv.size && pub.size)\n\t\t{\n\t\t\tchar displayname[MAX_OSPATH];\n\t\t\tprivf = SSL_OpenPrivKey(displayname, sizeof(displayname));\n\t\t\tif (privf)\n\t\t\t{\n\t\t\t\tVFS_WRITE(privf, priv.data, priv.size);\n\t\t\t\tVFS_CLOSE(privf);\n\t\t\t\tCon_Printf(\"Wrote %s\\n\", displayname);\n\t\t\t}\n//\t\t\tmemset(priv.data, 0, priv.size);\n\t\t\t(*qgnutls_free)(priv.data);\n\t\t\tmemset(&priv, 0, sizeof(priv));\n\n\t\t\tpubf = SSL_OpenPubKey(displayname, sizeof(displayname));\n\t\t\tif (pubf)\n\t\t\t{\n\t\t\t\tVFS_WRITE(pubf, pub.data, pub.size);\n\t\t\t\tVFS_CLOSE(pubf);\n\t\t\t\tCon_Printf(\"Wrote %s\\n\", displayname);\n\t\t\t}\n\t\t\t(*qgnutls_free)(pub.data);\n\t\t\tmemset(&pub, 0, sizeof(pub));\n\n\t\t\tprivf = SSL_OpenPrivKey(NULL, 0);\n\t\t\tpubf = SSL_OpenPubKey(NULL, 0);\n\n\t\t\tCon_Printf(\"Certificate generated\\n\");\n\t\t}\n\t}\n\n\tif (privf && pubf)\n\t{\n\t\t//read the two files now\n\t\tpriv.size = VFS_GETLEN(privf);\n\t\tpriv.data = (*qgnutls_malloc)(priv.size+1);\n\t\tif (priv.size != VFS_READ(privf, priv.data, priv.size))\n\t\t\tpriv.size = 0;\n\t\tpriv.data[priv.size] = 0;\n\n\t\tpub.size = VFS_GETLEN(pubf);\n\t\tpub.data = (*qgnutls_malloc)(pub.size+1);\n\t\tif (pub.size != VFS_READ(pubf, pub.data, pub.size))\n\t\t\tpub.size = 0;\n\t\tpub.data[pub.size] = 0;\n\n\t\tVFS_CLOSE(privf);\n\t\tVFS_CLOSE(pubf);\n\t}\n\n\t//FIXME: extend the expiration time if its old?\n\n\tif (priv.size && pub.size)\n\t{\t//submit them to gnutls\n\t\tret = qgnutls_certificate_set_x509_key_mem(cred, &pub, &priv, GNUTLS_X509_FMT_PEM);\n\t\tif (ret == GNUTLS_E_CERTIFICATE_KEY_MISMATCH)\n\t\t\tCon_Printf(CON_ERROR\"gnutls_certificate_set_x509_key_mem failed: GNUTLS_E_CERTIFICATE_KEY_MISMATCH\\n\");\n\t\telse if (ret < 0)\n\t\t\tCon_Printf(CON_ERROR\"gnutls_certificate_set_x509_key_mem failed: %i\\n\", ret);\n\t}\n\telse\n\t\tCon_Printf(CON_ERROR\"Unable to read/generate cert ('-certhost HOSTNAME' commandline arguments to autogenerate one)\\n\");\n\n\tmemset(priv.data, 0, priv.size);//just in case. FIXME: we didn't scrub the filesystem code. libc has its own caches etc. lets hope that noone comes up with some way to scrape memory remotely (although if they can inject code then we've lost either way so w/e)\n\tif (priv.data)\n\t\t(*qgnutls_free)(priv.data);\n\tif (pub.data)\n\t\t(*qgnutls_free)(pub.data);\n\n\treturn ret>=0;\n}\n\nqboolean SSL_InitGlobal(qboolean isserver)\n{\n\tint err;\n\tisserver = !!isserver;\n\tif (COM_CheckParm(\"-notls\"))\n\t\treturn false;\n#ifdef LOADERTHREAD\n\tif (com_resourcemutex)\n\t\tSys_LockMutex(com_resourcemutex);\n#endif\n\tif (!gnutls.initstatus[isserver])\n\t{\n\t\tif (!Init_GNUTLS())\n\t\t{\n#ifdef LOADERTHREAD\n\t\t\tif (com_resourcemutex)\n\t\t\t\tSys_UnlockMutex(com_resourcemutex);\n#endif\n\t\t\tCon_Printf(\"GnuTLS \"GNUTLS_VERSION\" library not available.\\n\");\n\t\t\treturn false;\n\t\t}\n\t\tgnutls.initstatus[isserver] = true;\n\t\tqgnutls_global_init ();\n\n#ifdef HAVE_DTLS\n\t\tif (isserver)\n\t\t\tqgnutls_key_generate(&cookie_key, GNUTLS_COOKIE_KEY_SIZE);\n#endif\n\n\n#ifdef USE_ANON\n\t\tqgnutls_anon_allocate_client_credentials (&anoncred[isserver]);\n#else\n\n\t\tqgnutls_certificate_allocate_credentials (&xcred[isserver]);\n\n#ifdef GNUTLS_HAVE_SYSTEMTRUST\n\t\terr = qgnutls_certificate_set_x509_system_trust (xcred[isserver]);\n\t\tif (err <= 0)\n\t\t\tCon_Printf(CON_ERROR\"gnutls_certificate_set_x509_system_trust: error %i.\\n\", err);\n#else\n\t\tqgnutls_certificate_set_x509_trust_file (xcred[isserver], CAFILE, GNUTLS_X509_FMT_PEM);\n#endif\n\n#ifdef LOADERTHREAD\n\t\tif (com_resourcemutex)\n\t\t\tSys_UnlockMutex(com_resourcemutex);\n#endif\n\t\tif (isserver)\n\t\t{\n#if 1\n\t\t\tif (!SSL_LoadPrivateCert(xcred[isserver]))\n\t\t\t\tservercertfail = true;\n#else\n\t\t\tint ret = -1;\n\t\t\tchar keyfile[MAX_OSPATH];\n\t\t\tchar certfile[MAX_OSPATH];\n\t\t\t*keyfile = *certfile = 0;\n\t\t\tif (FS_SystemPath(\"key.pem\", FS_ROOT, keyfile, sizeof(keyfile)))\n\t\t\t\tif (FS_SystemPath(\"cert.pem\", FS_ROOT, certfile, sizeof(certfile)))\n\t\t\t\t\tret = qgnutls_certificate_set_x509_key_file(xcred[isserver], certfile, keyfile, GNUTLS_X509_FMT_PEM);\n\t\t\tif (ret < 0)\n\t\t\t{\n\t\t\t\tCon_Printf(CON_ERROR\"No certificate or key was found in %s and %s\\n\", certfile, keyfile);\n\t\t\t\tgnutls.initstatus[isserver] = -1;\n\t\t\t}\n#endif\n\t\t}\n\t\telse\n\t\t{\n\t\t\tqgnutls_certificate_set_verify_function (xcred[isserver], SSL_CheckCert);\n//\t\t\tqgnutls_certificate_set_retrieve_function (xcred[isserver], SSL_FindClientCert);\n\t\t}\n#endif\n\t}\n\telse\n\t{\n#ifdef LOADERTHREAD\n\t\tif (com_resourcemutex)\n\t\t\tSys_UnlockMutex(com_resourcemutex);\n#endif\n\t}\n\n\tif (gnutls.initstatus[isserver] < 0)\n\t\treturn false;\n\treturn true;\n}\n\nvoid GnuTLS_Shutdown(void)\n{\n\tint isserver;\n\tfor (isserver = 0; isserver < 2; isserver++)\n\t\tif (gnutls.initstatus[isserver])\n\t\t{\n\t\t\tqgnutls_certificate_free_credentials(xcred[isserver]);\n\t\t\txcred[isserver] = NULL;\n\t\t\tgnutls.initstatus[isserver] = false;\n\n\t\t\tqgnutls_global_deinit();\t//refcounted.\n\t\t}\n#ifdef HAVE_DTLS\n\tif (cookie_key.data)\n\t{\n\t\t(*qgnutls_free)(cookie_key.data);\n\t\tmemset(&cookie_key, 0, sizeof(cookie_key));\n\t}\n#endif\n#ifdef GNUTLS_DYNAMIC\n\tif (gnutls.hmod)\n\t\tSys_CloseLibrary(gnutls.hmod);\n\tgnutls.hmod = NULL;\n#endif\n}\n\n\n#ifdef HAVE_DTLS\nstatic int GetPSKForUser(gnutls_session_t sess, const char *username, gnutls_datum_t * key)\n{\t//serverside. name must match what we expect (this isn't very secure), and we return the key we require for that user name.\n\tif (!strcmp(username, dtls_psk_user.string))\n\t{\n\t\tkey->size = (strlen(dtls_psk_key.string)+1)/2;\n\t\tkey->data = (*qgnutls_malloc)(key->size);\n\t\tkey->size = Base16_DecodeBlock(dtls_psk_key.string, key->data, key->size);\n\t\treturn 0;\n\t}\n\treturn -1;\n}\nstatic int GetPSKForServer(gnutls_session_t sess, char **username, gnutls_datum_t *key)\n{\t//clientside. return the appropriate username for the hint, along with the matching key.\n\t//this could be made more fancy with a database, but we'll keep it simple with cvars.\n\tconst char *svhint = qgnutls_psk_client_get_hint(sess);\n\n\tif (!svhint)\n\t\tsvhint = \"\";\n\n\tif ((!*dtls_psk_hint.string&&*dtls_psk_user.string) || (*dtls_psk_hint.string&&!strcmp(svhint, dtls_psk_hint.string)))\n\t{\t//okay, hints match (or ours is unset), report our user as appropriate.\n#ifndef NOLEGACY\n\t\tif (*svhint)\n\t\t{\n\t\t\t//Try to avoid crashing QE servers by recognising its hint and blocking it when the hashes of the user+key are wrong.\n\t\t\tif (CalcHashInt(&hash_sha1, svhint, strlen(svhint)) == 0xb6c27b61)\n\t\t\t{\n\t\t\t\tif (strcmp(svhint, dtls_psk_user.string) || CalcHashInt(&hash_sha1, dtls_psk_key.string, strlen(dtls_psk_key.string)) != 0x3dd348e4)\n\t\t\t\t{\n\t\t\t\t\tCon_Printf(CON_WARNING\t\"Possible QEx Server, please set your ^[%s\\\\type\\\\%s^] and ^[%s\\\\type\\\\%s^] cvars correctly, their current values are likely to crash the server.\\n\", dtls_psk_user.name,dtls_psk_user.name, dtls_psk_key.name,dtls_psk_key.name);\n\t\t\t\t\treturn -1; //don't report anything.\n\t\t\t\t}\n\t\t\t}\n\t\t}\n#endif\n\n\t\t*username = strcpy((*qgnutls_malloc)(strlen(dtls_psk_user.string)+1), dtls_psk_user.string);\n\n\t\tkey->size = (strlen(dtls_psk_key.string)+1)/2;\n\t\tkey->data = (*qgnutls_malloc)(key->size);\n\t\tkey->size = Base16_DecodeBlock(dtls_psk_key.string, key->data, key->size);\n\t\treturn 0;\n\t}\n\telse if (!*dtls_psk_user.string && !*dtls_psk_hint.string)\n\t\tCon_Printf(CON_ERROR\"Server requires a Pre-Shared Key (hint: \\\"%s\\\"). Please set %s, %s, and %s accordingly.\\n\", svhint, dtls_psk_hint.name, dtls_psk_user.name, dtls_psk_key.name);\n\telse\n\t\tCon_Printf(CON_ERROR\"Server requires different Pre-Shared Key credentials (hint: \\\"%s\\\", expected \\\"%s\\\"). Please set %s, %s, and %s accordingly.\\n\", svhint, dtls_psk_hint.string, dtls_psk_hint.name, dtls_psk_user.name, dtls_psk_key.name);\n\treturn -1;\n}\n#endif\nstatic qboolean SSL_InitConnection(gnutlsfile_t *newf, qboolean isserver, qboolean datagram)\n{\n\t// Initialize TLS session\n\tqgnutls_init (&newf->session, ((newf->certcred)?GNUTLS_FORCE_CLIENT_CERT:0)\n\t\t\t\t\t\t\t\t\t|GNUTLS_NONBLOCK\n\t\t\t\t\t\t\t\t\t|(isserver?GNUTLS_SERVER:GNUTLS_CLIENT)\n\t\t\t\t\t\t\t\t\t|(datagram?GNUTLS_DATAGRAM:0));\n\n\tif (!isserver)\n\t\tqgnutls_server_name_set(newf->session, GNUTLS_NAME_DNS, newf->certname, strlen(newf->certname));\n\tqgnutls_session_set_ptr(newf->session, newf);\n\n\tif (newf->certcred)\n\t{\n\t\tqgnutls_certificate_server_set_request(newf->session, GNUTLS_CERT_REQUIRE);\t//we will need to validate their fingerprint.\n\t\tqgnutls_credentials_set (newf->session, GNUTLS_CRD_CERTIFICATE, newf->certcred);\n\t\tqgnutls_set_default_priority (newf->session);\n\t}\n\telse\n\t{\n#ifdef USE_ANON\n\t\t//qgnutls_kx_set_priority (newf->session, kx_prio);\n\t\tqgnutls_credentials_set (newf->session, GNUTLS_CRD_ANON, anoncred[isserver]);\n#else\n#ifdef HAVE_DTLS\n\n#if defined(MASTERONLY)\n\t\tqgnutls_certificate_server_set_request(newf->session, GNUTLS_CERT_IGNORE);\t//don't request a cert. masters don't really need it and chrome bugs out if you connect to a websocket server that offers for the client to provide one. chrome users will just have to stick to webrtc.\n#else\n\t\tqgnutls_certificate_server_set_request(newf->session, GNUTLS_CERT_REQUEST);\t//request a cert, we'll use it for fingerprints.\n#endif\n\n\t\tif (datagram && !isserver)\n\t\t{\t//do psk as needed. we can still do the cert stuff if the server isn't doing psk.\n\t\t\tgnutls_psk_client_credentials_t pskcred;\n\t\t\tqgnutls_psk_allocate_client_credentials(&pskcred);\n\t\t\tqgnutls_psk_set_client_credentials_function(pskcred, GetPSKForServer);\n\n\t\t\tqgnutls_set_default_priority_append (newf->session, \"+ECDHE-PSK:+DHE-PSK:+PSK\", NULL, 0);\n\t\t\tqgnutls_credentials_set(newf->session, GNUTLS_CRD_PSK, pskcred);\n\t\t}\n\t\telse if (datagram && isserver && (*dtls_psk_user.string || servercertfail))\n\t\t{\t//offer some arbitrary PSK for dtls clients.\n\t\t\tgnutls_psk_server_credentials_t pskcred;\n\t\t\tqgnutls_psk_allocate_server_credentials(&pskcred);\n\t\t\tqgnutls_psk_set_server_credentials_function(pskcred, GetPSKForUser);\n\t\t\tif (*dtls_psk_hint.string)\n\t\t\t\tqgnutls_psk_set_server_credentials_hint(pskcred, dtls_psk_hint.string);\n\n\t\t\tqgnutls_set_default_priority_append (newf->session, (\"-KX-ALL:+ECDHE-PSK:+DHE-PSK:+PSK\")+(servercertfail?0:8), NULL, 0);\n\t\t\tqgnutls_credentials_set(newf->session, GNUTLS_CRD_PSK, pskcred);\n\t\t}\n\t\telse\n#endif\n\t\t{\n\t\t\t// Use default priorities for regular tls sessions\n\t\t\tqgnutls_set_default_priority (newf->session);\n\t\t}\n#endif\n\t\tif (xcred[isserver])\n\t\t\tqgnutls_credentials_set (newf->session, GNUTLS_CRD_CERTIFICATE, xcred[isserver]);\n\t}\n\n\t// tell gnutls how to send/receive data\n\tqgnutls_transport_set_ptr (newf->session, newf);\n\tqgnutls_transport_set_push_function(newf->session, datagram?DTLS_Push:SSL_Push);\n\t//qgnutls_transport_set_vec_push_function(newf->session, SSL_PushV);\n\tqgnutls_transport_set_pull_function(newf->session, datagram?DTLS_Pull:SSL_Pull);\n#ifdef HAVE_DTLS\n\tif (datagram)\n\t\tqgnutls_transport_set_pull_timeout_function(newf->session, DTLS_Pull_Timeout);\n#endif\n\n\tnewf->handshaking = true;\n\n\treturn true;\n}\n\nstatic vfsfile_t *GNUTLS_OpenVFS(const char *hostname, vfsfile_t *source, qboolean isserver)\n{\n\tgnutlsfile_t *newf;\n\n\tif (!source)\n\t\treturn NULL;\n\n\tif (!SSL_InitGlobal(isserver))\n\t\tnewf = NULL;\n\telse\n\t\tnewf = Z_Malloc(sizeof(*newf));\n\tif (!newf)\n\t{\n\t\treturn NULL;\n\t}\n\tnewf->funcs.Close = SSL_CloseFile;\n\tnewf->funcs.Flush = NULL;\n\tnewf->funcs.GetLen = SSL_GetLen;\n\tnewf->funcs.ReadBytes = SSL_Read;\n\tnewf->funcs.WriteBytes = SSL_Write;\n\tnewf->funcs.Seek = SSL_Seek;\n\tnewf->funcs.Tell = SSL_Tell;\n\tnewf->funcs.seekstyle = SS_UNSEEKABLE;\n\n\tSSL_SetCertificateName(newf, hostname);\n\n\tif (!SSL_InitConnection(newf, isserver, false))\n\t{\n\t\tVFS_CLOSE(&newf->funcs);\n\t\treturn NULL;\n\t}\n\tnewf->stream = source;\n\n\treturn &newf->funcs;\n}\n\nstatic int GNUTLS_GetChannelBinding(vfsfile_t *vf, qbyte *binddata, size_t *bindsize)\n{\n\tgnutls_datum_t cb;\n\tgnutlsfile_t *f = (gnutlsfile_t*)vf;\n\tif (vf->Close != SSL_CloseFile)\n\t\treturn -1;\t//err, not a gnutls connection.\n\n\tif (qgnutls_session_channel_binding(f->session, GNUTLS_CB_TLS_UNIQUE, &cb))\n\t{\t//error of some kind\n\t\t//if the error is because of the other side not supporting it, then we should return 0 here.\n\t\treturn -1;\n\t}\n\telse\n\t{\n\t\tif (cb.size > *bindsize)\n\t\t\treturn 0;\t//overflow\n\t\t*bindsize = cb.size;\n\t\tmemcpy(binddata, cb.data, cb.size);\n\t\treturn 1;\n\t}\n}\n\n//crypto: generates a signed blob\n#ifdef HAVE_DTLS\nstatic int GNUTLS_GenerateSignature(const qbyte *hashdata, size_t hashsize, qbyte *signdata, size_t signsizemax)\n{\n\tgnutls_datum_t hash = {(qbyte*)hashdata, hashsize};\n\tgnutls_datum_t sign = {NULL, 0};\n\n\tgnutls_certificate_credentials_t cred;\n\tif (Init_GNUTLS())\n\t{\n\t\tqgnutls_certificate_allocate_credentials (&cred);\n\t\tif (SSL_LoadPrivateCert(cred))\n\t\t{\n\t\t\tgnutls_x509_privkey_t xkey;\n\t\t\tgnutls_privkey_t privkey;\n\t\t\tqgnutls_privkey_init(&privkey);\n\t\t\tqgnutls_certificate_get_x509_key(cred, 0, &xkey);\n\t\t\tqgnutls_privkey_import_x509(privkey, xkey, 0);\n\n\t\t\tqgnutls_privkey_sign_hash(privkey, GNUTLS_DIG_SHA512, 0, &hash, &sign);\n\t\t\tqgnutls_privkey_deinit(privkey);\n\t\t}\n\t\telse\n\t\t\tsign.size = 0;\n\t\tqgnutls_certificate_free_credentials(cred);\n\t}\n\telse\n\t\tCon_Printf(\"Unable to init gnutls\\n\");\n\tmemcpy(signdata, sign.data, sign.size);\n\treturn sign.size;\n}\n#else\n#define GNUTLS_GenerateSignature NULL\n#endif\n\n//crypto: verifies a signed blob matches an authority's public cert. windows equivelent https://docs.microsoft.com/en-us/windows/win32/seccrypto/example-c-program-signing-a-hash-and-verifying-the-hash-signature\nstatic enum hashvalidation_e GNUTLS_VerifyHash(const qbyte *hashdata, size_t hashsize, const qbyte *pubkeydata, size_t pubkeysize, const qbyte *signdata, size_t signsize)\n{\n\tgnutls_datum_t hash = {(qbyte*)hashdata, hashsize};\n\tgnutls_datum_t sign = {(qbyte*)signdata, signsize};\n\tint r;\n\n\tgnutls_datum_t rawcert = {(qbyte*)pubkeydata, pubkeysize};\n#if 1\n\tgnutls_pubkey_t pubkey;\n\tgnutls_x509_crt_t cert;\n\n\tif (!rawcert.data)\n\t\treturn VH_AUTHORITY_UNKNOWN;\n\tif (!Init_GNUTLS())\n\t\treturn VH_UNSUPPORTED;\n\n\tqgnutls_pubkey_init(&pubkey);\n\tqgnutls_x509_crt_init(&cert);\n\tqgnutls_x509_crt_import(cert, &rawcert, GNUTLS_X509_FMT_PEM);\n\n\tqgnutls_pubkey_import_x509(pubkey, cert, 0);\n#else\n\tqgnutls_pubkey_import(pubkey, rawcert, GNUTLS_X509_FMT_PEM);\n#endif\n\n\tr = qgnutls_pubkey_verify_hash2(pubkey, GNUTLS_SIGN_RSA_SHA512, 0, &hash, &sign);\n\tqgnutls_x509_crt_deinit(cert);\n\tqgnutls_pubkey_deinit(pubkey);\n\tif (r < 0)\n\t{\n\t\tif (r == GNUTLS_E_PK_SIG_VERIFY_FAILED)\n\t\t{\n\t\t\tCon_Printf(CON_ERROR\"GNUTLS_VerifyHash: GNUTLS_E_PK_SIG_VERIFY_FAILED!\\n\");\n\t\t\treturn VH_INCORRECT;\n\t\t}\n\t\telse if (r == GNUTLS_E_INSUFFICIENT_SECURITY)\n\t\t{\n\t\t\tCon_Printf(CON_ERROR\"GNUTLS_VerifyHash: GNUTLS_E_INSUFFICIENT_SECURITY\\n\");\n\t\t\treturn VH_AUTHORITY_UNKNOWN;\t//should probably be incorrect or something, but oh well\n\t\t}\n\t\treturn VH_INCORRECT;\n\t}\n\telse\n\t\treturn VH_CORRECT;\n}\n\n#ifdef HAVE_DTLS\n\nstatic void GNUDTLS_DestroyContext(void *ctx)\n{\n\tSSL_Close(ctx);\n\tZ_Free(ctx);\n}\nstatic void *GNUDTLS_CreateContext(const dtlscred_t *credinfo, void *cbctx, neterr_t(*push)(void *cbctx, const qbyte *data, size_t datasize), qboolean isserver)\n{\n\tgnutlsfile_t *newf;\n\n\tif (!SSL_InitGlobal(isserver))\n\t\tnewf = NULL;\n\telse\n\t\tnewf = Z_Malloc(sizeof(*newf));\n\tif (!newf)\n\t\treturn NULL;\n\tnewf->datagram = true;\n\tnewf->cbctx = cbctx;\n\tnewf->cbpush = push;\n\tnewf->challenging = isserver;\n\n//\tSys_Printf(\"DTLS_CreateContext: server=%i\\n\", isserver);\n\n\tif (credinfo && ((credinfo->local.cert && credinfo->local.key) || credinfo->peer.hash))\n\t{\n\t\tqgnutls_certificate_allocate_credentials (&newf->certcred);\n\t\tif (credinfo->local.cert && credinfo->local.key)\n\t\t{\n\t\t\tgnutls_datum_t\tpub = {credinfo->local.cert, credinfo->local.certsize},\n\t\t\t\t\t\t\tpriv = {credinfo->local.key, credinfo->local.keysize};\n\t\t\tqgnutls_certificate_set_x509_key_mem(newf->certcred, &pub, &priv, GNUTLS_X509_FMT_DER);\n\t\t}\n\n\t\tif (credinfo->peer.hash)\n\t\t{\n\t\t\tnewf->peerhashfunc = credinfo->peer.hash;\n\t\t\tmemcpy(newf->peerdigest, credinfo->peer.digest, newf->peerhashfunc->digestsize);\n\t\t\tqgnutls_certificate_set_verify_function (newf->certcred, SSL_CheckFingerprint);\n\t\t}\n\t}\n\n\tSSL_SetCertificateName(newf, credinfo?credinfo->peer.name:NULL);\n\n\tif (!SSL_InitConnection(newf, isserver, true))\n\t{\n\t\tSSL_Close(&newf->funcs);\n\t\tZ_Free(newf);\n\t\treturn NULL;\n\t}\n\n\treturn newf;\n}\n\nstatic neterr_t GNUDTLS_Transmit(void *ctx, const qbyte *data, size_t datasize)\n{\n\tint ret;\n\tgnutlsfile_t *f = (gnutlsfile_t *)ctx;\n\n\tif (f->challenging)\n\t\treturn NETERR_CLOGGED;\n\tif (f->handshaking)\n\t{\n\t\tret = SSL_DoHandshake(f);\n\t\tif (!ret)\n\t\t\treturn NETERR_CLOGGED;\n\t\tif (ret < 0)\n\t\t\treturn NETERR_DISCONNECTED;\n\t}\n\n\tif (!datasize)\n\t\treturn NETERR_SENT;\n\n\tret = qgnutls_record_send(f->session, data, datasize);\n\tif (ret < 0)\n\t{\n\t\tif (ret == GNUTLS_E_LARGE_PACKET)\n\t\t\treturn NETERR_MTU;\n\n\t\tif (qgnutls_error_is_fatal(ret))\n\t\t\treturn NETERR_DISCONNECTED;\n\t\treturn NETERR_CLOGGED;\n\t}\n\treturn NETERR_SENT;\n}\n\nstatic neterr_t GNUDTLS_Received(void *ctx, sizebuf_t *message)\n{\n\tint ret;\n\tgnutlsfile_t *f = (gnutlsfile_t *)ctx;\n\n\tif (f->challenging)\n\t{\n\t\tsize_t asize;\n\t\tsafeswitch (net_from.type)\n\t\t{\n\t\tcase NA_LOOPBACK:\tasize = 0; break;\n\t\tcase NA_IP:\t\t\tasize = sizeof(net_from.address.ip);\tbreak;\n\t\tcase NA_IPV6:\t\tasize = sizeof(net_from.address.ip6);\tbreak;\n\t\tcase NA_IPX:\t\tasize = sizeof(net_from.address.ipx);\tbreak;\n#ifdef UNIXSOCKETS\n\t\tcase NA_UNIX:\t\tasize = (qbyte*)&net_from.address.un.path[net_from.address.un.len]-(qbyte*)&net_from.address; break;\t//unlikely to be spoofed...\n#endif\n#ifdef IRCCONNECT\n\t\t//case NA_IRC:\n#endif\n#ifdef HAVE_WEBSOCKCL\n\t\t//case NA_WEBSOCKET:\t//basically web browser.\n#endif\n#ifdef SUPPORT_ICE\n\t\tcase NA_ICE:\t\tasize = strlen(net_from.address.icename);\tbreak;\n#endif\n\t\tcase NA_INVALID:\n\t\tsafedefault: return NETERR_NOROUTE;\n\t\t}\n\n\t\tmemset(&f->prestate, 0, sizeof(f->prestate));\n\t\tret = qgnutls_dtls_cookie_verify(&cookie_key,\n\t\t\t\t&net_from.address, asize,\n\t\t\t\tmessage->data, message->cursize,\n\t\t\t\t&f->prestate);\n\n\t\tif (ret == GNUTLS_E_BAD_COOKIE)\n\t\t{\n\t\t\tqgnutls_dtls_cookie_send(&cookie_key,\n\t\t\t\t\t&net_from.address, asize,\n\t\t\t\t\t&f->prestate,\n\t\t\t\t\t(gnutls_transport_ptr_t)f, DTLS_Push);\n\t\t\treturn NETERR_CLOGGED;\n\t\t}\n\t\telse if (ret < 0)\n\t\t\treturn NETERR_NOROUTE;\n\t\tf->challenging = false;\n\n\t\tqgnutls_dtls_prestate_set(f->session, &f->prestate);\n\t\tqgnutls_dtls_set_mtu(f->session, 1440);\n\n\t\tf->handshaking = true;\n\t}\n\n\tf->readdata = message->data;\n\tf->readsize = message->cursize;\n\n\tif (f->handshaking)\n\t{\n\t\tret = SSL_DoHandshake(f);\n\t\tif (ret <= 0)\n\t\t\tf->readsize = 0;\n\t\tif (!ret)\n\t\t\treturn NETERR_CLOGGED;\n\t\tif (ret < 0)\n\t\t\treturn NETERR_DISCONNECTED;\n\t}\n\n\tret = qgnutls_record_recv(f->session, message->data, message->maxsize);\n//Sys_Printf(\"DTLS_Received returned %i of %i\\n\", ret, f->readsize);\n\tf->readsize = 0;\n\tif (ret <= 0)\n\t{\n\t\tif (!ret)\n\t\t{\n//\t\t\tSys_Printf(\"DTLS_Received peer terminated connection\\n\");\n\t\t\treturn NETERR_DISCONNECTED;\n\t\t}\n\t\tif (qgnutls_error_is_fatal(ret))\n\t\t{\n\t\t\tif (ret == GNUTLS_E_FATAL_ALERT_RECEIVED)\n\t\t\t\tCon_DPrintf(CON_ERROR\"GNUDTLS_Received: fatal alert %s\\n\", qgnutls_alert_get_name(qgnutls_alert_get(f->session)));\n\t\t\telse\n\t\t\t\tCon_DPrintf(CON_ERROR\"GNUDTLS_Received fatal error %s\\n\", qgnutls_strerror(ret));\n\t\t\treturn NETERR_DISCONNECTED;\n\t\t}\n\t\tif (ret == GNUTLS_E_WARNING_ALERT_RECEIVED)\n\t\t\tCon_DPrintf(CON_WARNING\"GNUDTLS_Received: alert %s\\n\", qgnutls_alert_get_name(qgnutls_alert_get(f->session)));\n//\t\tSys_Printf(\"DTLS_Received temp error\\n\");\n\t\treturn NETERR_CLOGGED;\n\t}\n\tmessage->cursize = ret;\n\tmessage->data[ret] = 0;\n//\tSys_Printf(\"DTLS_Received returned %s\\n\", data);\n\treturn NETERR_SENT;\n}\n\nstatic qboolean GNUDTLS_CheckConnection(void *cbctx, void *peeraddr, size_t peeraddrsize, void *indata, size_t insize, neterr_t(*push)(void *cbctx, const qbyte *data, size_t datasize), void (*EstablishTrueContext)(void **cbctx, void *state))\n{\t//called when we got a possibly-dtls packet out of the blue.\n\tgnutlsfile_t *f;\n\tint ret;\n\tgnutls_dtls_prestate_st prestate;\n\n\tmemset(&prestate, 0, sizeof(prestate));\n\tret = qgnutls_dtls_cookie_verify(&cookie_key,\n\t\t\tpeeraddr, peeraddrsize,\n\t\t\tindata, insize,\n\t\t\t&prestate);\n\n\tif (ret == GNUTLS_E_BAD_COOKIE)\n\t{\t//some sort of handshake with a bad/unknown cookie. send them a real one.\n\t\tgnutlsfile_t f;\n\t\tf.cbctx = cbctx;\n\t\tf.cbpush = push;\n\t\tf.session = NULL;\n\n\t\tqgnutls_dtls_cookie_send(&cookie_key,\n\t\t\t\tpeeraddr, peeraddrsize,\n\t\t\t\t&prestate,\n\t\t\t\t(gnutls_transport_ptr_t)&f, DTLS_Push);\n\t\treturn true;\n\t}\n\telse if (ret < 0)\n\t\treturn false;\t//dunno... might still be dtls but doesn't seem to be needed... oh well...\n\n\t//allocate our context\n\tf = GNUDTLS_CreateContext(NULL, cbctx, push, true);\n\tif (!f)\n\t{\n\t\tCon_Printf(\"GNUDTLS_CreateContext: failed\\n\");\n\t\treturn false;\n\t}\n\n\t//tell caller that this is an actual valid connection\n\tEstablishTrueContext(&f->cbctx, f);\n\tif (!f->cbctx)\n\t\treturn true;\n\n\t//we're done with the challenge stuff\n\tf->challenging = false;\n\n\t//and this is the result...\n\tqgnutls_dtls_prestate_set(f->session, &prestate);\n\tqgnutls_dtls_set_mtu(f->session, 1400);\n\n\t//still need to do the whole certificate thing though.\n\tf->handshaking = true;\n\treturn true;\n}\n\nstatic neterr_t GNUDTLS_Timeouts(void *ctx)\n{\n\tgnutlsfile_t *f = (gnutlsfile_t *)ctx;\n\tint ret;\n\tif (f->challenging)\n\t\treturn NETERR_CLOGGED;\n\tif (f->handshaking)\n\t{\n\t\tf->readsize = 0;\n\t\tret = SSL_DoHandshake(f);\n\t\tf->readsize = 0;\n\t\tif (!ret)\n\t\t\treturn NETERR_CLOGGED;\n\t\tif (ret < 0)\n\t\t\treturn NETERR_DISCONNECTED;\n\n//\t\tSys_Printf(\"handshaking over?\\n\");\n\t}\n\treturn NETERR_SENT;\n}\n\nstatic int GNUDTLS_GetPeerCertificate(void *ctx, enum certprops_e prop, char *out, size_t outsize)\n{\n\tgnutlsfile_t *f = (gnutlsfile_t *)ctx;\n\tif (f && (f->challenging || f->handshaking))\n\t\treturn -1;\t//no cert locked down yet...\n\tsafeswitch(prop)\n\t{\n\tcase QCERT_ISENCRYPTED:\n\t\treturn 0;\t//well, should be...\n\tcase QCERT_PEERSUBJECT:\n\t\t{\n\t\t\tunsigned int certcount;\n\t\t\tconst gnutls_datum_t *const certlist = qgnutls_certificate_get_peers(f->session, &certcount);\n\t\t\tif (certlist)\n\t\t\t{\n\t\t\t\tgnutls_x509_crt_t cert = NULL;\n\t\t\t\tgnutls_datum_t dn={NULL};\n\t\t\t\tqgnutls_x509_crt_init(&cert);\n\t\t\t\tqgnutls_x509_crt_import(cert, certlist, GNUTLS_X509_FMT_DER);\n\t\t\t\tqgnutls_x509_crt_get_dn3(cert, &dn, 0);\n\t\t\t\tif (dn.size >= outsize)\n\t\t\t\t\tdn.size = -1;\t//too big...\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tmemcpy(out, dn.data, dn.size);\n\t\t\t\t\tout[dn.size] = 0;\n\t\t\t\t}\n\t\t\t\t(*qgnutls_free)(dn.data);\n\t\t\t\tqgnutls_x509_crt_deinit(cert);\n\t\t\t\treturn (int)dn.size;\n\t\t\t}\n\t\t}\n\t\treturn -1;\n\tcase QCERT_PEERCERTIFICATE:\n\t\t{\n\t\t\tunsigned int certcount;\n\t\t\tconst gnutls_datum_t *const certlist = qgnutls_certificate_get_peers(f->session, &certcount);\n\t\t\tif (certlist && certlist->size <= outsize)\n\t\t\t{\n\t\t\t\tmemcpy(out, certlist->data, certlist->size);\n\t\t\t\treturn certlist->size;\n\t\t\t}\n\t\t}\n\t\treturn -1;\n\tcase QCERT_LOCALCERTIFICATE:\n\t\t{\n\t\t\tconst gnutls_datum_t *cert;\n\t\t\tgnutls_datum_t d;\n\n\t\t\tif (f)\n\t\t\t\tcert = qgnutls_certificate_get_ours(f->session);\n\t\t\telse\t//no actual context? get our default dtls server cert.\n\t\t\t{\n\t\t\t\tqgnutls_certificate_get_crt_raw (xcred[true], 0/*first chain*/, 0/*primary one*/, &d);\n\t\t\t\tcert = &d;\n\t\t\t}\n\t\t\tif (cert->size <= outsize)\n\t\t\t{\n\t\t\t\tmemcpy(out, cert->data, cert->size);\n\t\t\t\treturn cert->size;\n\t\t\t}\n\t\t}\n\t\treturn -1;\n\tcase QCERT_LOBBYSTATUS:\n\tcase QCERT_LOBBYSENDCHAT:\n\t\treturn -1;\n\tsafedefault:\n\t\treturn -1; //dunno what you want from me.\n\t}\n}\n\nstatic qboolean GNUDTLS_GenTempCertificate(const char *subject, struct dtlslocalcred_s *qcred)\n{\n\tgnutls_datum_t priv = {NULL}, pub = {NULL};\n\tgnutls_x509_privkey_t key;\n\tgnutls_x509_crt_t cert;\n\tchar serial[64];\n\tchar randomsub[32+1];\n\tconst char *errstr;\n\tgnutls_pk_algorithm_t privalgo = GNUTLS_PK_RSA;\n\tint ret;\n\n\tqgnutls_x509_privkey_init(&key);\n\tret = qgnutls_x509_privkey_generate(key, privalgo, qgnutls_sec_param_to_pk_bits(privalgo, GNUTLS_SEC_PARAM_HIGH), 0);\n\tif (ret < 0)\n\t\tCon_Printf(CON_ERROR\"gnutls_x509_privkey_generate failed: %i\\n\", ret);\n\tret = qgnutls_x509_privkey_export2(key, GNUTLS_X509_FMT_DER, &priv);\n\tif (ret < 0)\n\t\tCon_Printf(CON_ERROR\"gnutls_x509_privkey_export2 failed: %i\\n\", ret);\n\n\t//stoopid browsers insisting that serial numbers are different even on throw-away self-signed certs.\n\t//we should probably just go and make our own root ca/master. post it a cert and get a signed one (with sequential serial) back or something.\n\t//we'll probably want something like that for client certs anyway, for stat tracking.\n\tQ_snprintfz(serial, sizeof(serial), \"%u\", (unsigned)time(NULL));\n\n\tqgnutls_x509_crt_init(&cert);\n\tqgnutls_x509_crt_set_version(cert, 1);\n\tqgnutls_x509_crt_set_activation_time(cert, time(NULL)-1);\n\tqgnutls_x509_crt_set_expiration_time(cert, time(NULL)+(time_t)10*365*24*60*60);\n\tqgnutls_x509_crt_set_serial(cert, serial, strlen(serial));\n//\tqgnutls_x509_crt_set_key_usage(cert, GNUTLS_KEY_DIGITAL_SIGNATURE);\n\tif (!subject)\n\t{\n\t\tqbyte tmp[16];\n\t\tSys_RandomBytes(tmp, sizeof(tmp));\n\t\trandomsub[Base16_EncodeBlock(tmp, sizeof(tmp), randomsub, sizeof(randomsub)-1)] = 0;\n\t\tsubject = randomsub;\n\t}\n\n\tif (qgnutls_x509_crt_set_dn(cert, va(\"CN=%s\", subject), &errstr) < 0)\n\t\tCon_Printf(CON_ERROR\"gnutls_x509_crt_set_dn failed: %s\\n\", errstr);\n\tif (qgnutls_x509_crt_set_issuer_dn(cert, va(\"CN=%s\", subject), &errstr) < 0)\n\t\tCon_Printf(CON_ERROR\"gnutls_x509_crt_set_issuer_dn failed: %s\\n\", errstr);\n//\tqgnutls_x509_crt_set_key_usage(cert, GNUTLS_KEY_KEY_ENCIPHERMENT|GNUTLS_KEY_DATA_ENCIPHERMENT|);\n\n\tqgnutls_x509_crt_set_key(cert, key);\n\n\t/*sign it with our private key*/\n\t{\n\t\tgnutls_privkey_t akey;\n\t\tqgnutls_privkey_init(&akey);\n\t\tqgnutls_privkey_import_x509(akey, key, GNUTLS_PRIVKEY_IMPORT_COPY);\n\t\tret = qgnutls_x509_crt_privkey_sign(cert, cert, akey, GNUTLS_DIG_SHA256, 0);\n\t\tif (ret < 0)\n\t\t\tCon_Printf(CON_ERROR\"gnutls_x509_crt_privkey_sign failed: %i\\n\", ret);\n\t\tqgnutls_privkey_deinit(akey);\n\t}\n\tret = qgnutls_x509_crt_export2(cert, GNUTLS_X509_FMT_DER, &pub);\n\tqgnutls_x509_crt_deinit(cert);\n\tqgnutls_x509_privkey_deinit(key);\n\tif (ret < 0)\n\t\tCon_Printf(CON_ERROR\"gnutls_x509_crt_export2 failed: %i\\n\", ret);\n\n\t//okay, we have them in memory, make sure the rest of the engine can play with it.\n\tqcred->certsize = pub.size;\n\tmemcpy(qcred->cert = Z_Malloc(pub.size), pub.data, pub.size);\n\n\tqcred->keysize = priv.size;\n\tmemcpy(qcred->key = Z_Malloc(priv.size), priv.data, priv.size);\n\n\t(*qgnutls_free)(priv.data);\n\t(*qgnutls_free)(pub.data);\n\n\treturn true;\n}\nstatic const dtlsfuncs_t dtlsfuncs_gnutls =\n{\n\tGNUDTLS_CreateContext,\n\tGNUDTLS_CheckConnection,\n\tGNUDTLS_DestroyContext,\n\tGNUDTLS_Transmit,\n\tGNUDTLS_Received,\n\tGNUDTLS_Timeouts,\n\tGNUDTLS_GetPeerCertificate,\n\tGNUDTLS_GenTempCertificate\n};\nstatic const dtlsfuncs_t *GNUDTLS_InitServer(void)\n{\n\tif (!SSL_InitGlobal(true))\n\t\treturn NULL;\t//unable to init a server certificate. don't allow dtls to init.\n\tif (servercertfail && !*dtls_psk_user.string)\t//FIXME: with ICE connections we'll be using temporary certs anyway.\n\t\treturn NULL;\n\treturn &dtlsfuncs_gnutls;\n}\nstatic const dtlsfuncs_t *GNUDTLS_InitClient(void)\n{\n\tif (!SSL_InitGlobal(false))\n\t\treturn NULL;\n\treturn &dtlsfuncs_gnutls;\n}\n#else\n#define GNUDTLS_InitServer NULL\n#define GNUDTLS_InitClient NULL\n#endif\n\nftecrypto_t crypto_gnutls =\n{\n\t\"GNUTLS\",\n\tGNUTLS_OpenVFS,\n\tGNUTLS_GetChannelBinding,\n\tGNUDTLS_InitClient,\n\tGNUDTLS_InitServer,\n\tGNUTLS_VerifyHash,\n\tGNUTLS_GenerateSignature,\n};\n\n#else\n#warning \"GNUTLS version is too old (3.0+ required). Please clean and then recompile with CFLAGS=-DNO_GNUTLS\"\n\nftecrypto_t crypto_gnutls;\nqboolean SSL_InitGlobal(qboolean isserver) {return false;}\n#endif\n#endif\n\n"
  },
  {
    "path": "engine/common/net_ssl_winsspi.c",
    "content": "#include \"quakedef.h\"\r\n#if defined(HAVE_WINSSPI)\r\n\r\n/*regarding HAVE_DTLS\r\nDTLS1.0 is supported from win8 onwards, or patched-win7\r\nDTLS1.2 is supported from win10-1607 onwards.\r\n\r\nunlike the other tls providers we use pfx files (to work around microsoft making it so fucking hard to actually import/export the damn private key any other way)\r\npfx files are encrypted with your username as a password\r\nconvert from pem to pfx:\r\n\t\topenssl pkcs12 -inkey private.pem -in fullchain.pem -export -nodes -out identity.pfx -passout pass:$USER\r\n\t(in the above formula, swap out $USER for %USERNAME% if you're using a windows cmd prompt)\r\nyou can start fte with an arg like the following for a fully CA-signed cert:\r\n\t\tfteqwsv -pfx c:/foo/bar.pfx\r\n\t(otherwise it'll use identity.pfx from your basedir, autogenerated with some dodgy info)\r\n\r\nTODO: RTC connections do not validate the client peer.\r\nTODO: get someone else to figure out the PSK stuff. no way can I test/debug/write that without documentation nor working drivers.\r\nNote: testing this stuff is a pain when eg browsers do NOT support DTLS1.0 any more.\r\n*/\r\n\r\n#include \"winquake.h\"\r\n#include \"netinc.h\"\r\n#define SECURITY_WIN32\r\n#include <security.h>\r\n#include <sspi.h>\r\n#include <wincrypt.h>\r\n#include <schannel.h>\r\n\r\n#define SP_PROT_TLS1_1_SERVER\t\t0x00000100\r\n#define SP_PROT_TLS1_1_CLIENT\t\t0x00000200\r\n\r\n#define SP_PROT_TLS1_2_SERVER\t\t0x00000400\r\n#define SP_PROT_TLS1_2_CLIENT\t\t0x00000800\r\n\r\n#define SP_PROT_DTLS_SERVER\t\t0x00010000\r\n#define SP_PROT_DTLS_CLIENT\t\t0x00020000\r\n#define SP_PROT_DTLS1_0_SERVER SP_PROT_DTLS_SERVER\r\n#define SP_PROT_DTLS1_0_CLIENT SP_PROT_DTLS_CLIENT\r\n#define SP_PROT_DTLS1_2_SERVER 0x00040000\r\n#define SP_PROT_DTLS1_2_CLIENT 0x00080000\r\n#define SP_PROT_DTLS1_X_SERVER (SP_PROT_DTLS1_0_SERVER | SP_PROT_DTLS1_2_SERVER)\r\n#define SP_PROT_DTLS1_X_CLIENT (SP_PROT_DTLS1_0_CLIENT | SP_PROT_DTLS1_2_CLIENT)\r\n\r\n\r\n//avoid the use of outdated/insecure protocols\r\n//so no ssl2/ssl3\r\n#define USE_PROT_SERVER (SP_PROT_TLS1_SERVER | SP_PROT_TLS1_1_SERVER | SP_PROT_TLS1_2_SERVER)\r\n#define USE_PROT_CLIENT (SP_PROT_TLS1_CLIENT | SP_PROT_TLS1_1_CLIENT | SP_PROT_TLS1_2_CLIENT)\r\n\r\n#define USE_PROT_DGRAM_SERVER (SP_PROT_DTLS1_X_SERVER)\r\n#define USE_PROT_DGRAM_CLIENT (SP_PROT_DTLS1_X_CLIENT)\r\n\r\n#ifndef szOID_RSA_SHA512RSA\r\n#define szOID_RSA_SHA512RSA \"1.2.840.113549.1.1.13\"\r\n#endif\r\n#ifndef SCH_CRED_SNI_CREDENTIAL\r\n#define SCH_CRED_SNI_CREDENTIAL 0x00080000\r\n#endif\r\n\r\n#ifndef SEC_I_MESSAGE_FRAGMENT\r\n#define SEC_I_MESSAGE_FRAGMENT\t0x00090364L\r\n#endif\r\n#ifndef SEC_E_INVALID_PARAMETER\r\n#define SEC_E_INVALID_PARAMETER\t0x8009035DL\r\n#endif\r\n\r\n#ifndef SECBUFFER_ALERT\r\n#define SECBUFFER_ALERT 17\r\n#endif\r\n#ifndef SECPKG_ATTR_DTLS_MTU\r\n#define SECPKG_ATTR_DTLS_MTU 34\r\n#endif\r\n\r\n#ifndef CRYPT_ARCHIVABLE\r\n#define CRYPT_ARCHIVABLE 0x00004000\r\n#endif\r\n\r\n\r\n//hungarian ensures we hit no macros.\r\nstatic struct\r\n{\r\n\tdllhandle_t *lib;\r\n\tSECURITY_STATUS (WINAPI *pDecryptMessage)\t\t\t\t(PCtxtHandle,PSecBufferDesc,ULONG,PULONG);\r\n\tSECURITY_STATUS (WINAPI *pEncryptMessage)\t\t\t\t(PCtxtHandle,ULONG,PSecBufferDesc,ULONG);\r\n\tSECURITY_STATUS (WINAPI *pAcquireCredentialsHandleA)\t(SEC_CHAR*,SEC_CHAR*,ULONG,PLUID,PVOID,SEC_GET_KEY_FN,PVOID,PCredHandle,PTimeStamp);\r\n//\tSECURITY_STATUS (WINAPI *pInitializeSecurityContextA)\t(PCredHandle,PCtxtHandle,SEC_CHAR*,ULONG,ULONG,ULONG,PSecBufferDesc,ULONG,PCtxtHandle,PSecBufferDesc,PULONG,PTimeStamp);\r\n\tSECURITY_STATUS (WINAPI *pInitializeSecurityContextW)\t(PCredHandle,PCtxtHandle,SEC_WCHAR*,ULONG,ULONG,ULONG,PSecBufferDesc,ULONG,PCtxtHandle,PSecBufferDesc,PULONG,PTimeStamp);\r\n\tSECURITY_STATUS (WINAPI *pAcceptSecurityContext)\t\t(PCredHandle,PCtxtHandle,PSecBufferDesc,unsigned long,unsigned long,PCtxtHandle,PSecBufferDesc,unsigned long SEC_FAR *,PTimeStamp);\r\n\tSECURITY_STATUS (WINAPI *pCompleteAuthToken)\t\t\t(PCtxtHandle,PSecBufferDesc);\r\n\tSECURITY_STATUS (WINAPI *pSetContextAttributesA)\t\t(PCtxtHandle,unsigned long,void*,unsigned long);\r\n\tSECURITY_STATUS (WINAPI *pQueryContextAttributesA)\t\t(PCtxtHandle,ULONG,PVOID);\r\n\tSECURITY_STATUS (WINAPI *pFreeCredentialsHandle)\t\t(PCredHandle);\r\n\tSECURITY_STATUS (WINAPI *pDeleteSecurityContext)\t\t(PCtxtHandle);\r\n} secur;\r\nstatic struct\r\n{\r\n\tdllhandle_t *lib;\r\n\tBOOL (WINAPI *pCertGetCertificateChain)\t\t\t\t\t(HCERTCHAINENGINE,PCCERT_CONTEXT,LPFILETIME,HCERTSTORE,PCERT_CHAIN_PARA,DWORD,LPVOID,PCCERT_CHAIN_CONTEXT*);\r\n\tBOOL (WINAPI *pCertVerifyCertificateChainPolicy)\t\t(LPCSTR,PCCERT_CHAIN_CONTEXT,PCERT_CHAIN_POLICY_PARA,PCERT_CHAIN_POLICY_STATUS);\r\n\tvoid (WINAPI *pCertFreeCertificateChain)\t\t\t\t(PCCERT_CHAIN_CONTEXT);\r\n\tPCCERT_CONTEXT (WINAPI *pCertCreateCertificateContext)\t(DWORD dwCertEncodingType, const BYTE *pbCertEncoded, DWORD cbCertEncoded);\r\n\tDWORD (WINAPI *pCertNameToStrA)\t\t\t\t\t\t\t(DWORD dwCertEncodingType, PCERT_NAME_BLOB pName, DWORD dwStrType, LPCSTR psz, DWORD csz);\r\n\tBOOL (WINAPI *pCertFreeCertificateContext)\t\t\t\t(PCCERT_CONTEXT pCertContext);\r\n\tPCCERT_CONTEXT (WINAPI *pCertCreateSelfSignCertificate)\t(HCRYPTPROV,PCERT_NAME_BLOB,DWORD,PCRYPT_KEY_PROV_INFO,PCRYPT_ALGORITHM_IDENTIFIER,PSYSTEMTIME,PSYSTEMTIME,PCERT_EXTENSIONS);\r\n\tBOOL (WINAPI *pCertStrToNameA)\t\t\t\t\t\t\t(DWORD,LPCSTR,DWORD,void *,BYTE *,DWORD *,LPCSTR *);\r\n\tHCERTSTORE (WINAPI *pCertOpenStore)\t\t\t\t\t\t(LPCSTR lpszStoreProvider, DWORD dwEncodingType, HCRYPTPROV hCryptProv, DWORD dwFlags, const void *pvPara);\r\n\tBOOL (WINAPI *pCertAddCertificateContextToStore)\t\t(HCERTSTORE hCertStore, PCCERT_CONTEXT pCertContext, DWORD dwAddDisposition, PCCERT_CONTEXT *ppStoreContext);\r\n\tBOOL (WINAPI *pPFXExportCertStoreEx)\t\t\t\t\t(HCERTSTORE hStore, CRYPT_DATA_BLOB *pPFX, LPCWSTR szPassword, void *pvPara, DWORD dwFlags);\r\n\tBOOL (WINAPI *pCertCloseStore)\t\t\t\t\t\t\t(HCERTSTORE hCertStore, DWORD dwFlags);\r\n\tHCERTSTORE (WINAPI *pPFXImportCertStore)\t\t\t\t(CRYPT_DATA_BLOB *pPFX, LPCWSTR szPassword, DWORD dwFlags);\r\n\tPCCERT_CONTEXT (WINAPI *pCertFindCertificateInStore)\t(HCERTSTORE hCertStore, DWORD dwCertEncodingType, DWORD dwFindFlags, DWORD dwFindType, const void *pvFindPara, PCCERT_CONTEXT pPrevCertContext);\r\n\tBOOL (WINAPI *pCryptAcquireCertificatePrivateKey)\t\t(PCCERT_CONTEXT pCert, DWORD dwFlags, void *pvParameters, HCRYPTPROV *phCryptProvOrNCryptKey, DWORD *pdwKeySpec, BOOL *pfCallerFreeProvOrNCryptKey);\r\n\tBOOL (WINAPI *pCertSetCertificateContextProperty)\t\t(PCCERT_CONTEXT pCertContext, DWORD dwPropId, DWORD dwFlags, const void *pvData);\r\n} crypt;\r\nstatic struct\r\n{\r\n\tdllhandle_t *lib;\r\n\tBOOL (WINAPI *pCryptAcquireContextW)\t\t\t\t\t(HCRYPTPROV *phProv, LPCWSTR szContainer, LPCWSTR szProvider, DWORD dwProvType, DWORD dwFlags);\r\n\tBOOL (WINAPI *pCryptGenKey)\t\t\t\t\t\t\t\t(HCRYPTPROV hProv, ALG_ID Algid, DWORD dwFlags, HCRYPTKEY *phKey);\r\n} advapi;\r\nvoid SSL_Init(void)\r\n{\r\n\tdllfunction_t secur_functable[] =\r\n\t{\r\n\t\t{(void**)&secur.pDecryptMessage,\t\t\t\t\"DecryptMessage\"},\r\n\t\t{(void**)&secur.pEncryptMessage,\t\t\t\t\"EncryptMessage\"},\r\n\t\t{(void**)&secur.pAcquireCredentialsHandleA,\t\t\"AcquireCredentialsHandleA\"},\r\n//\t\t{(void**)&secur.pInitializeSecurityContextA,\t\"InitializeSecurityContextA\"},\r\n\t\t{(void**)&secur.pInitializeSecurityContextW,\t\"InitializeSecurityContextW\"},\r\n\t\t{(void**)&secur.pAcceptSecurityContext,\t\t\t\"AcceptSecurityContext\"},\r\n\t\t{(void**)&secur.pCompleteAuthToken,\t\t\t\t\"CompleteAuthToken\"},\r\n\t\t{(void**)&secur.pSetContextAttributesA,\t\t\t\"SetContextAttributesA\"},\r\n\t\t{(void**)&secur.pQueryContextAttributesA,\t\t\"QueryContextAttributesA\"},\r\n\t\t{(void**)&secur.pFreeCredentialsHandle,\t\t\t\"FreeCredentialsHandle\"},\r\n\t\t{(void**)&secur.pDeleteSecurityContext,\t\t\t\"DeleteSecurityContext\"},\r\n\t\t{NULL, NULL}\r\n\t};\r\n\r\n\tdllfunction_t crypt_functable[] =\r\n\t{\r\n\t\t{(void**)&crypt.pCertGetCertificateChain,\t\t\t\"CertGetCertificateChain\"},\r\n\t\t{(void**)&crypt.pCertVerifyCertificateChainPolicy,\t\"CertVerifyCertificateChainPolicy\"},\r\n\t\t{(void**)&crypt.pCertFreeCertificateChain,\t\t\t\"CertFreeCertificateChain\"},\r\n\t\t{(void**)&crypt.pCertCreateCertificateContext,\t\t\"CertCreateCertificateContext\"},\r\n\t\t{(void**)&crypt.pCertNameToStrA,\t\t\t\t\t\"CertNameToStrA\"},\r\n\t\t{(void**)&crypt.pCertFreeCertificateContext,\t\t\"CertFreeCertificateContext\"},\r\n\t\t{(void**)&crypt.pCertCreateSelfSignCertificate,\t\t\"CertCreateSelfSignCertificate\"},\r\n\t\t{(void**)&crypt.pCertStrToNameA,\t\t\t\t\t\"CertStrToNameA\"},\r\n\t\t{(void**)&crypt.pCertOpenStore,\t\t\t\t\t\t\"CertOpenStore\"},\r\n\t\t{(void**)&crypt.pCertAddCertificateContextToStore,\t\"CertAddCertificateContextToStore\"},\r\n\t\t{(void**)&crypt.pPFXExportCertStoreEx,\t\t\t\t\"PFXExportCertStoreEx\"},\r\n\t\t{(void**)&crypt.pCertCloseStore,\t\t\t\t\t\"CertCloseStore\"},\r\n\t\t{(void**)&crypt.pPFXImportCertStore,\t\t\t\t\"PFXImportCertStore\"},\r\n\t\t{(void**)&crypt.pCertFindCertificateInStore,\t\t\"CertFindCertificateInStore\"},\r\n\t\t{(void**)&crypt.pCryptAcquireCertificatePrivateKey,\t\"CryptAcquireCertificatePrivateKey\"},\r\n\t\t{(void**)&crypt.pCertSetCertificateContextProperty,\t\"CertSetCertificateContextProperty\"},\r\n\t\t{NULL, NULL}\r\n\t};\r\n\r\n\tdllfunction_t advapi_functable[] =\r\n\t{\r\n\t\t{(void**)&advapi.pCryptAcquireContextW,\t\t\t\t\"CryptAcquireContextW\"},\r\n\t\t{(void**)&advapi.pCryptGenKey,\t\t\t\t\t\t\"CryptGenKey\"},\r\n\t\t{NULL, NULL}\r\n\t};\r\n\r\n\tif (!secur.lib)\r\n\t\tsecur.lib = Sys_LoadLibrary(\"secur32.dll\", secur_functable);\r\n\tif (!crypt.lib)\r\n\t\tcrypt.lib = Sys_LoadLibrary(\"crypt32.dll\", crypt_functable);\r\n\tif (!advapi.lib)\r\n\t\tadvapi.lib = Sys_LoadLibrary(\"advapi32.dll\", advapi_functable);\r\n}\r\nqboolean SSL_Inited(void)\r\n{\r\n\treturn !!secur.lib && !!crypt.lib && !!advapi.lib;\r\n}\r\n\r\n#define MessageAttribute (ISC_REQ_SEQUENCE_DETECT | ISC_REQ_REPLAY_DETECT | ISC_REQ_CONFIDENTIALITY | /*ISC_REQ_EXTENDED_ERROR |*/ ISC_REQ_ALLOCATE_MEMORY | ISC_REQ_MANUAL_CRED_VALIDATION | ISC_REQ_USE_SUPPLIED_CREDS)\r\n\r\nstruct sslbuf\r\n{\r\n\tsize_t datasize;\r\n\tchar *data;\r\n\tsize_t avail;\r\n};\r\n\r\ntypedef struct {\r\n\tvfsfile_t funcs;\r\n\tvfsfile_t *stream;\r\n\r\n\twchar_t wpeername[256];\r\n\tqboolean datagram;\r\n\tenum\r\n\t{\r\n\t\tHS_ESTABLISHED,\r\n\r\n\t\tHS_ERROR,\r\n\r\n\t\tHS_STARTCLIENT,\r\n\t\tHS_CLIENT,\r\n\r\n\t\tHS_STARTSERVER,\r\n\t\tHS_SERVER\r\n\t} handshaking;\r\n\r\n\tstruct sslbuf outraw;\r\n\tstruct sslbuf outcrypt;\r\n\tstruct sslbuf inraw;\r\n\tstruct sslbuf incrypt;\r\n\r\n\r\n\tCredHandle cred;\r\n\tSecHandle sechnd;\r\n\tint headersize, mtu, footersize;\t//schannel is strict about its mtus.\r\n\tchar headerdata[1024], footerdata[1024];\r\n\r\n\tdouble resendtimer;\t//for the client so it can keep retrying the handshake.\r\n\r\n#ifdef HAVE_DTLS\r\n\tvoid *cbctx;\r\n\tneterr_t (*transmit)(void *cbctx, const qbyte *data, size_t datasize);\r\n#endif\r\n} sslfile_t;\r\n\r\nstatic int SSPI_ExpandBuffer(struct sslbuf *buf, size_t bytes)\r\n{\r\n\tif (bytes < buf->datasize)\r\n\t\treturn buf->datasize;\r\n\tZ_ReallocElements((void**)&buf->data, &buf->datasize, bytes, 1);\r\n\treturn bytes;\r\n}\r\n\r\nstatic int SSPI_CopyIntoBuffer(struct sslbuf *buf, const void *data, unsigned int bytes, qboolean expand)\r\n{\r\n\tif (bytes > buf->datasize - buf->avail)\r\n\t{\r\n\t\tif (!expand || SSPI_ExpandBuffer(buf, buf->avail + bytes + 1024) < buf->avail + bytes)\r\n\t\t\tbytes = buf->datasize - buf->avail;\r\n\t}\r\n\tmemcpy(buf->data + buf->avail, data, bytes);\r\n\tbuf->avail += bytes;\r\n\r\n\treturn bytes;\r\n}\r\n\r\nstatic void SSPI_Error(sslfile_t *f, const char *error, ...)\r\n{\r\n\tva_list         argptr;\r\n\tchar             string[1024];\r\n\tva_start (argptr, error);\r\n\tvsnprintf (string,sizeof(string)-1, error,argptr);\r\n\tva_end (argptr);\r\n\r\n\tf->handshaking = HS_ERROR;\r\n\tif (*string)\r\n\t{\r\n\t\tif (f->datagram)\r\n\t\t\tCon_Printf(CON_ERROR \"%s\", string);\r\n\t\telse\r\n\t\t\tSys_Printf(CON_ERROR \"%s\", string);\r\n\t}\r\n\tif (f->stream)\r\n\t\tVFS_CLOSE(f->stream);\r\n\r\n\tsecur.pDeleteSecurityContext(&f->sechnd);\r\n\tsecur.pFreeCredentialsHandle(&f->cred);\r\n\tf->stream = NULL;\r\n}\r\n\r\nstatic neterr_t SSPI_TryFlushCryptOut(sslfile_t *f)\r\n{\r\n\tint sent;\r\n\tif (f->outcrypt.avail)\r\n\t{\r\n#ifdef HAVE_DTLS\r\n\t\tif (f->transmit)\r\n\t\t{\r\n\t\t\tneterr_t e = f->transmit(f->cbctx, f->outcrypt.data, f->outcrypt.avail);\r\n\t\t\tf->outcrypt.avail = 0;\r\n\t\t\treturn e;\r\n\t\t}\r\n#endif\r\n\t\tsent = VFS_WRITE(f->stream, f->outcrypt.data, f->outcrypt.avail);\r\n\t}\r\n\telse\r\n\t\treturn NETERR_SENT;\r\n\r\n\tif (f->datagram)\r\n\t\tf->outcrypt.avail = 0;\t//anything unsent is a dropped packet...\r\n\telse if (sent > 0)\r\n\t{\r\n\t\tmemmove(f->outcrypt.data, f->outcrypt.data + sent, f->outcrypt.avail - sent);\r\n\t\tf->outcrypt.avail -= sent;\r\n\t}\r\n\treturn NETERR_SENT;\r\n}\r\n\r\nstatic int SSPI_CheckNewInCrypt(sslfile_t *f)\r\n{\r\n\tint newd;\r\n\tif (!f->stream)\r\n\t\treturn VFS_ERROR_EOF;\r\n\tnewd = VFS_READ(f->stream, f->incrypt.data+f->incrypt.avail, f->incrypt.datasize - f->incrypt.avail);\r\n\tif (newd < 0)\r\n\t\treturn newd;\r\n\telse\r\n\t\tf->incrypt.avail += newd;\r\n\r\n\treturn 0;\r\n}\r\n\r\n//convert inbound crypt->data\r\nstatic void SSPI_Decode(sslfile_t *f)\r\n{\r\n\tSECURITY_STATUS\t\tss;\r\n\tSecBufferDesc\t\tBuffDesc;\r\n\tSecBuffer\t\t\tSecBuff[4];\r\n\tULONG\t\t\t\tulQop = 0;\r\n\tSecBuffer\t\t\t*extra = NULL;\r\n\tint i;\r\n\r\n\tif (!f->incrypt.avail)\r\n\t\treturn;\r\n\r\n\tBuffDesc.ulVersion    = SECBUFFER_VERSION;\r\n\tBuffDesc.cBuffers     = countof(SecBuff);\r\n\tBuffDesc.pBuffers     = SecBuff;\r\n\r\n\tSecBuff[0].BufferType = SECBUFFER_DATA;\r\n\tSecBuff[0].cbBuffer   = f->incrypt.avail;\r\n\tSecBuff[0].pvBuffer   = f->incrypt.data;\r\n\t\r\n\tSecBuff[1].BufferType = SECBUFFER_EMPTY;\t//space for header\r\n\tSecBuff[2].BufferType = SECBUFFER_EMPTY;\t//space for footer\r\n\tSecBuff[3].BufferType = SECBUFFER_EMPTY;\t//space for extra marker\r\n\r\n\tss = secur.pDecryptMessage(&f->sechnd, &BuffDesc, 0, &ulQop);\r\n\tif (FAILED(ss))\r\n\t{\r\n\t\tif (f->datagram)\r\n\t\t\treturn; //some sort of corruption? ignore that packet.\r\n\t\tif (ss == SEC_E_INCOMPLETE_MESSAGE)\r\n\t\t{\r\n\t\t\tif (f->incrypt.avail == f->incrypt.datasize)\r\n\t\t\t\tSSPI_ExpandBuffer(&f->incrypt, f->incrypt.datasize+1024);\r\n\t\t\treturn;\t//no error if its incomplete, we can just get more data later on.\r\n\t\t}\r\n\t\tswitch(ss)\r\n\t\t{\r\n\t\tcase SEC_E_DECRYPT_FAILURE:\tSSPI_Error(f, \"DecryptMessage failed: SEC_E_DECRYPT_FAILURE\\n\", ss); break;\r\n\t\tcase SEC_E_INVALID_HANDLE:\tSSPI_Error(f, \"DecryptMessage failed: SEC_E_INVALID_HANDLE\\n\"); break;\r\n\t\tcase SEC_E_UNFINISHED_CONTEXT_DELETED: SSPI_Error(f, \"DecryptMessage failed: SEC_E_UNFINISHED_CONTEXT_DELETED\\n\"); break;\t//peer aborted?\r\n\t\tdefault:\t\t\t\t\tSSPI_Error(f, \"DecryptMessage failed: %0#lx\\n\", ss); break;\r\n\t\t}\r\n\t\treturn;\r\n\t}\r\n\r\n\tfor (i = 0; i < BuffDesc.cBuffers; i++)\r\n\t{\r\n\t\tswitch(SecBuff[i].BufferType)\r\n\t\t{\r\n\t\tcase SECBUFFER_DATA:\r\n\t\t\tif (SSPI_CopyIntoBuffer(&f->inraw, SecBuff[i].pvBuffer, SecBuff[i].cbBuffer, true) != SecBuff[i].cbBuffer)\r\n\t\t\t\tSSPI_Error(f, \"outraw buffer overflowed\\n\");\r\n\t\t\tbreak;\r\n\t\tcase SECBUFFER_EXTRA:\r\n\t\t\tif (extra)\r\n\t\t\t\tSSPI_Error(f, \"multiple extra buffers\\n\");\r\n\t\t\textra = &SecBuff[i];\r\n\t\t\tbreak;\r\n\t\tcase SECBUFFER_EMPTY:\r\n\t\tcase SECBUFFER_MISSING:\r\n\t\tcase SECBUFFER_STREAM_TRAILER:\r\n\t\tcase SECBUFFER_STREAM_HEADER:\r\n\t\t\tbreak;\r\n\t\tdefault:\r\n\t\t\tSSPI_Error(f, \"got unexpected buffer type\\n\");\r\n\t\t\tbreak;\r\n\t\t}\r\n\t}\r\n\r\n\t//retain the extra. if there's no extra then mark it so.\r\n\tif (extra)\r\n\t{\r\n\t\tmemmove(f->incrypt.data, f->incrypt.data + (f->incrypt.avail - extra->cbBuffer), extra->cbBuffer);\r\n\t\tf->incrypt.avail = extra->cbBuffer;\r\n\t}\r\n\telse\r\n\t\tf->incrypt.avail = 0;\r\n}\r\n\r\n//convert outgoing data->crypt\r\nstatic neterr_t SSPI_Encode(sslfile_t *f)\r\n{\r\n\tSECURITY_STATUS\t\tss;\r\n\tSecBufferDesc\t\tBuffDesc;\r\n\tSecBuffer\t\t\tSecBuff[4];\r\n\tULONG\t\t\t\tulQop = 0;\r\n\r\n\tif (f->outcrypt.avail)\r\n\t{\r\n\t\tSSPI_TryFlushCryptOut(f);\r\n\t\tif (f->outcrypt.avail)\r\n\t\t\treturn NETERR_CLOGGED;\t//don't flood too much\r\n\t}\r\n\r\n\r\n\t//don't corrupt the handshake data.\r\n\tif (f->handshaking)\r\n\t\treturn NETERR_CLOGGED;\r\n\r\n\tif (!f->outraw.avail)\r\n\t\treturn NETERR_SENT;\r\n\r\n\tBuffDesc.ulVersion    = SECBUFFER_VERSION;\r\n\tBuffDesc.cBuffers     = 4;\r\n\tBuffDesc.pBuffers     = SecBuff;\r\n\r\n\tSecBuff[0].BufferType = SECBUFFER_STREAM_HEADER;\r\n\tSecBuff[0].cbBuffer   = f->headersize;\r\n\tSecBuff[0].pvBuffer   = f->headerdata;\r\n\t\r\n\tSecBuff[1].BufferType = SECBUFFER_DATA;\r\n\tSecBuff[1].cbBuffer   = f->outraw.avail;\r\n\tSecBuff[1].pvBuffer   = f->outraw.data;\r\n\r\n\tSecBuff[2].BufferType = SECBUFFER_STREAM_TRAILER;\r\n\tSecBuff[2].cbBuffer   = f->footersize;\r\n\tSecBuff[2].pvBuffer   = f->footerdata;\r\n\r\n\tSecBuff[3].BufferType = SECBUFFER_EMPTY;\r\n\tSecBuff[3].cbBuffer   = 0;\r\n\tSecBuff[3].pvBuffer   = NULL;\r\n\r\n\tss = secur.pEncryptMessage(&f->sechnd, ulQop, &BuffDesc, 0);\r\n\r\n\tif (ss < 0)\r\n\t{\r\n\t\tswitch (ss)\r\n\t\t{\r\n\t\tcase SEC_E_ENCRYPT_FAILURE:\tSSPI_Error(f, \"EncryptMessage failed SEC_E_ENCRYPT_FAILURE (in: %i, max out %i)\\n\", f->outraw.avail, f->outcrypt.avail);\r\n\t\tdefault:\t\t\tSSPI_Error(f, \"EncryptMessage failed %x\\n\", ss);\r\n\t\t}\r\n\t\treturn NETERR_DISCONNECTED;\r\n\t}\r\n\r\n\tf->outraw.avail = 0;\r\n\r\n\t//fixme: these should be made non-fatal.\r\n\tif (SSPI_CopyIntoBuffer(&f->outcrypt, SecBuff[0].pvBuffer, SecBuff[0].cbBuffer, true) < SecBuff[0].cbBuffer)\r\n\t{\r\n\t\tSSPI_Error(f, \"crypt buffer overflowed\\n\");\r\n\t\treturn NETERR_DISCONNECTED;\r\n\t}\r\n\tif (SSPI_CopyIntoBuffer(&f->outcrypt, SecBuff[1].pvBuffer, SecBuff[1].cbBuffer, true) < SecBuff[1].cbBuffer)\r\n\t{\r\n\t\tSSPI_Error(f, \"crypt buffer overflowed\\n\");\r\n\t\treturn NETERR_DISCONNECTED;\r\n\t}\r\n\tif (SSPI_CopyIntoBuffer(&f->outcrypt, SecBuff[2].pvBuffer, SecBuff[2].cbBuffer, true) < SecBuff[2].cbBuffer)\r\n\t{\r\n\t\tSSPI_Error(f, \"crypt buffer overflowed\\n\");\r\n\t\treturn NETERR_DISCONNECTED;\r\n\t}\r\n\r\n\treturn SSPI_TryFlushCryptOut(f);\r\n}\r\n\r\nchar *narrowen(char *out, size_t outlen, wchar_t *wide);\r\nstatic DWORD VerifyKnownCertificates(DWORD status, wchar_t *domain, qbyte *data, size_t datasize, qboolean datagram)\r\n{\r\n\tsize_t knownsize;\r\n\tvoid *knowncert;\r\n\tchar realdomain[256];\r\n\tunsigned int probs = 0;\r\n\tif (datagram)\r\n\t{\r\n\t\tif (status == CERT_E_UNTRUSTEDROOT || status == CERT_E_UNTRUSTEDTESTROOT)\r\n\t\t\tprobs |= CERTLOG_MISSINGCA;\r\n\t\tif (status == CERT_E_EXPIRED)\r\n\t\t\tprobs |= CERTLOG_EXPIRED;\r\n\t\tif (status == SEC_E_WRONG_PRINCIPAL)\r\n\t\t\tprobs |= CERTLOG_WRONGHOST;\r\n\t\tif (status == CERT_E_UNTRUSTEDROOT || SUCCEEDED(status))\r\n\t\t{\r\n#ifdef HAVE_CLIENT\r\n\t\t\tif (CertLog_ConnectOkay(narrowen(realdomain, sizeof(realdomain), domain), data, datasize, probs))\r\n\t\t\t\tstatus = SEC_E_OK;\r\n\t\t\telse\r\n#endif\r\n\t\t\tif (SUCCEEDED(status))\r\n\t\t\t\tstatus = TRUST_E_EXPLICIT_DISTRUST;\r\n\t\t}\r\n\t\treturn status;\r\n\t}\r\n\r\n\tnarrowen(realdomain, sizeof(realdomain), domain);\r\n\tknowncert = TLS_GetKnownCertificate(realdomain, &knownsize);\r\n\tif (knowncert)\r\n\t{\r\n\t\tif (knownsize == datasize && !memcmp(data, knowncert, datasize))\r\n\t\t{\t//what we know about matched\r\n\t\t\tif (status == CERT_E_UNTRUSTEDROOT || status == CERT_E_EXPIRED)\r\n\t\t\t\tstatus = SEC_E_OK;\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tif (status != CERT_E_EXPIRED)\r\n\t\t\t\tCon_Printf(\"%ls has an unexpected certificate\\n\", domain);\r\n\t\t\tif (status == SEC_E_OK)\t//we (think) we know better.\r\n\t\t\t\tstatus = TRUST_E_EXPLICIT_DISTRUST;\r\n\t\t}\r\n\t\tBZ_Free(knowncert);\r\n\t}\r\n\r\n#ifdef HAVE_CLIENT\r\n\t//self-signed and expired certs are understandable in many situations.\r\n\t//prompt and cache (although this connection attempt will fail).\r\n\tif (status == CERT_E_UNTRUSTEDROOT || status == CERT_E_UNTRUSTEDTESTROOT)\r\n\t\tprobs |= CERTLOG_MISSINGCA;\r\n\telse if (status == CERT_E_EXPIRED)\r\n\t\tprobs |= CERTLOG_EXPIRED;\r\n\telse if (status == SEC_E_WRONG_PRINCIPAL)\r\n\t\tprobs |= CERTLOG_WRONGHOST;\r\n\telse if (status != SEC_E_OK)\r\n\t\tprobs |= CERTLOG_UNKNOWN;\r\n\tif (status == CERT_E_UNTRUSTEDROOT || status == CERT_E_UNTRUSTEDTESTROOT || status == CERT_E_EXPIRED)\r\n\t\tif (CertLog_ConnectOkay(realdomain, data, datasize, probs))\r\n\t\t\treturn SEC_E_OK;\r\n#endif\r\n\r\n\treturn status;\r\n}\r\n\r\nstatic DWORD VerifyServerCertificate(PCCERT_CONTEXT pServerCert, PWSTR pwszServerName, DWORD dwCertFlags, qboolean datagram)\r\n{\r\n\tHTTPSPolicyCallbackData polHttps;\r\n\tCERT_CHAIN_POLICY_PARA PolicyPara;\r\n\tCERT_CHAIN_POLICY_STATUS PolicyStatus;\r\n\tCERT_CHAIN_PARA ChainPara;\r\n\tPCCERT_CHAIN_CONTEXT pChainContext;\r\n\tDWORD Status;\r\n\tLPSTR rgszUsages[]\t\t=\r\n\t{\r\n\t\tszOID_PKIX_KP_SERVER_AUTH,\r\n\t\tszOID_SERVER_GATED_CRYPTO,\r\n\t\tszOID_SGC_NETSCAPE\r\n\t};\r\n\tDWORD cUsages\t\t\t=\tsizeof(rgszUsages) / sizeof(LPSTR);\r\n\r\n\tif(pServerCert == NULL)\r\n\t\treturn SEC_E_WRONG_PRINCIPAL;\r\n\tif(!*pwszServerName)\r\n\t\treturn SEC_E_WRONG_PRINCIPAL;\r\n\r\n\t// Build certificate chain.\r\n\tmemset(&ChainPara, 0, sizeof(ChainPara));\r\n\tChainPara.cbSize = sizeof(ChainPara);\r\n\tChainPara.RequestedUsage.dwType = USAGE_MATCH_TYPE_OR;\r\n\tChainPara.RequestedUsage.Usage.cUsageIdentifier = cUsages;\r\n\tChainPara.RequestedUsage.Usage.rgpszUsageIdentifier = rgszUsages;\r\n\r\n\tif (!crypt.pCertGetCertificateChain(NULL, pServerCert, NULL, pServerCert->hCertStore, &ChainPara, 0, NULL, &pChainContext))\r\n\t{\r\n\t\tStatus = GetLastError();\r\n\t\tSys_Printf(\"Error %#lx returned by CertGetCertificateChain!\\n\", Status);\r\n\t}\r\n\telse\r\n\t{\r\n\t\t// Validate certificate chain.\r\n\t\tmemset(&polHttps, 0, sizeof(HTTPSPolicyCallbackData));\r\n\t\tpolHttps.cbStruct = sizeof(HTTPSPolicyCallbackData);\r\n\t\tpolHttps.dwAuthType = AUTHTYPE_SERVER;\r\n\t\tpolHttps.fdwChecks = dwCertFlags;\r\n\t\tpolHttps.pwszServerName = pwszServerName;\r\n\r\n\t\tmemset(&PolicyPara, 0, sizeof(PolicyPara));\r\n\t\tPolicyPara.cbSize = sizeof(PolicyPara);\r\n\t\tPolicyPara.pvExtraPolicyPara = &polHttps;\r\n\r\n\t\tmemset(&PolicyStatus, 0, sizeof(PolicyStatus));\r\n\t\tPolicyStatus.cbSize = sizeof(PolicyStatus);\r\n\r\n\t\tif (!crypt.pCertVerifyCertificateChainPolicy(CERT_CHAIN_POLICY_SSL, pChainContext, &PolicyPara, &PolicyStatus))\r\n\t\t{\r\n\t\t\tStatus = GetLastError();\r\n\t\t\tSys_Printf(\"Error %#lx returned by CertVerifyCertificateChainPolicy!\\n\", Status);\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tStatus = VerifyKnownCertificates(PolicyStatus.dwError, pwszServerName, pServerCert->pbCertEncoded, pServerCert->cbCertEncoded, datagram);\r\n\t\t\tif (Status)\r\n\t\t\t{\r\n\t\t\t\tchar fmsg[512];\r\n\t\t\t\tchar *err;\r\n\t\t\t\tswitch (Status)\r\n\t\t\t\t{\r\n\t\t\t\t\tcase CERT_E_EXPIRED:\t\t\t\terr = \"CERT_E_EXPIRED\";\t\t\t\t\tbreak;\r\n\t\t\t\t\tcase CERT_E_VALIDITYPERIODNESTING:\terr = \"CERT_E_VALIDITYPERIODNESTING\";\tbreak;\r\n\t\t\t\t\tcase CERT_E_ROLE:\t\t\t\t\terr = \"CERT_E_ROLE\";\t\t\t\t\tbreak;\r\n\t\t\t\t\tcase CERT_E_PATHLENCONST:\t\t\terr = \"CERT_E_PATHLENCONST\";\t\t\tbreak;\r\n\t\t\t\t\tcase CERT_E_CRITICAL:\t\t\t\terr = \"CERT_E_CRITICAL\";\t\t\t\tbreak;\r\n\t\t\t\t\tcase CERT_E_PURPOSE:\t\t\t\terr = \"CERT_E_PURPOSE\";\t\t\t\t\tbreak;\r\n\t\t\t\t\tcase CERT_E_ISSUERCHAINING:\t\t\terr = \"CERT_E_ISSUERCHAINING\";\t\t\tbreak;\r\n\t\t\t\t\tcase CERT_E_MALFORMED:\t\t\t\terr = \"CERT_E_MALFORMED\";\t\t\t\tbreak;\r\n\t\t\t\t\tcase CERT_E_UNTRUSTEDROOT:\t\t\terr = \"CERT_E_UNTRUSTEDROOT\";\t\t\tbreak;\r\n\t\t\t\t\tcase CERT_E_CHAINING:\t\t\t\terr = \"CERT_E_CHAINING\";\t\t\t\tbreak;\r\n\t\t\t\t\tcase TRUST_E_FAIL:\t\t\t\t\terr = \"TRUST_E_FAIL\";\t\t\t\t\tbreak;\r\n\t\t\t\t\tcase TRUST_E_EXPLICIT_DISTRUST:\t\terr = \"blocked\";\t\t\t\t\t\tbreak;\r\n\t\t\t\t\tcase CERT_E_REVOKED:\t\t\t\terr = \"CERT_E_REVOKED\";\t\t\t\t\tbreak;\r\n\t\t\t\t\tcase CERT_E_UNTRUSTEDTESTROOT:\t\terr = \"CERT_E_UNTRUSTEDTESTROOT\";\t\tbreak;\r\n\t\t\t\t\tcase CERT_E_REVOCATION_FAILURE:\t\terr = \"CERT_E_REVOCATION_FAILURE\";\t\tbreak;\r\n\t\t\t\t\tcase CERT_E_CN_NO_MATCH:            \r\n\t\t\t\t\t\terr = fmsg;\r\n\t\t\t\t\t\tQ_strncpyz(fmsg, \"Certificate is for \", sizeof(fmsg));\r\n\t\t\t\t\t\tcrypt.pCertNameToStrA(X509_ASN_ENCODING, &pServerCert->pCertInfo->Subject, 0, fmsg+strlen(fmsg), sizeof(fmsg)-strlen(fmsg));\r\n\t\t\t\t\t\tbreak;\r\n\t\t\t\t\tcase CERT_E_WRONG_USAGE:\t\t\terr = \"CERT_E_WRONG_USAGE\";\t\t\t\tbreak;\r\n\t\t\t\t\tdefault:\t\t\t\t\t\t\terr = va(\"%#x\", (int)Status);\t\t\t\t\tbreak;\r\n\t\t\t\t}\r\n\t\t\t\tCon_Printf(CON_ERROR \"Error verifying certificate for '%ls': %s\\n\", pwszServerName, err);\r\n\r\n\t\t\t\tif (tls_ignorecertificateerrors.ival)\r\n\t\t\t\t{\r\n\t\t\t\t\tCon_Printf(CON_WARNING \"pretending it didn't happen... (tls_ignorecertificateerrors is set)\\n\");\r\n\t\t\t\t\tStatus = SEC_E_OK;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t\tStatus = SEC_E_OK;\r\n\t\t}\r\n\t\tcrypt.pCertFreeCertificateChain(pChainContext);\r\n\t}\r\n\r\n\treturn Status;\r\n}\r\nstatic PCCERT_CONTEXT SSPI_GetServerCertificate(void)\r\n{\r\n\t//frankly this is all kinda fucked\r\n\tstatic PCCERT_CONTEXT ret;\r\n\tstatic const char *issuertext = \"CN=127.0.0.1, O=\\\"FTE QuakeWorld\\\", OU=Fallback, C=QW\";\r\n\twchar_t *const container = L\"diediedie\";\r\n\tstatic const int provtype = PROV_RSA_SCHANNEL;\r\n\tstatic wchar_t *const provname = MS_DEF_RSA_SCHANNEL_PROV_W;\r\n\tCRYPT_KEY_PROV_INFO kpi;\r\n\tCERT_NAME_BLOB issuerblob;\r\n\tHCRYPTPROV prov = 0;\r\n\tHCRYPTKEY hkey = 0; //nope, not registry related.\r\n\r\n\tCRYPT_ALGORITHM_IDENTIFIER sigalg;\r\n\tSYSTEMTIME expiredate;\r\n\tint i;\r\n\tconst char *pfxname = NULL;\r\n\r\n\tqofs_t fsz = 0;\r\n\tCRYPT_DATA_BLOB pfxblob = {0, NULL};\r\n\tHCERTSTORE store;\r\n\twchar_t password[512];\r\n\tDWORD fucksake = countof(password);\r\n\tstatic qboolean tried;\r\n\r\n\tif (ret||tried)\r\n\t\treturn ret;\r\n\ttried = true;\r\n\r\n\ti = COM_CheckParm(\"-pfx\");\r\n\tif (i && i < com_argc-1)\r\n\t\tpfxname = com_argv[i+1];\r\n\r\n\tif (pfxname)\r\n\t\tpfxblob.pbData = FS_MallocFile(pfxname, FS_SYSTEM, &fsz);\r\n\tif (!pfxblob.pbData)\r\n\t\tpfxblob.pbData = FS_MallocFile(\"identity.pfx\", FS_ROOT, &fsz);\r\n\tif (pfxblob.pbData)\r\n\t{\r\n\t\tpfxblob.cbData = fsz;\r\n\t\tif (!GetUserNameW(password, &fucksake))\t//use their username as a password. at least it'll block most accidental redistriction. should prolly mix in their computer name, w/e\r\n\t\t\t*password = 0;\r\n\t\tstore = crypt.pPFXImportCertStore(&pfxblob, password, 0);\r\n\t\tif (!store)\t//try a couple of other passwords, for people creating their own manually.\r\n\t\t\tstore = crypt.pPFXImportCertStore(&pfxblob, L\"\", 0);\r\n\t\tif (!store)\r\n\t\t\tstore = crypt.pPFXImportCertStore(&pfxblob, NULL, 0);\r\n\r\n\t\tif (store)\r\n\t\t{\r\n\t\t\tHCRYPTPROV hprov = 0;\r\n\t\t\tDWORD keyspec = 0;\r\n\t\t\tBOOL willbefalse = false;\r\n\r\n\t\t\tret = crypt.pCertFindCertificateInStore(store, X509_ASN_ENCODING|PKCS_7_ASN_ENCODING, 0, CERT_FIND_ANY, NULL, NULL);\r\n\t\t\tif (ret && crypt.pCryptAcquireCertificatePrivateKey(ret, 0, NULL, &hprov, &keyspec, &willbefalse))\r\n\t\t\t{\r\n\t\t\t\tchar fdn[1024];\r\n\t\t\t\tchar digest[DIGEST_MAXSIZE];\r\n\t\t\t\tchar b64[DIGEST_MAXSIZE*2+1];\r\n\t\t\t\tsize_t dgsz = CalcHash(&hash_sha1, digest, sizeof(digest), ret->pbCertEncoded, ret->cbCertEncoded);\r\n\t\t\t\tBase64_EncodeBlock(digest, dgsz, b64, sizeof(b64));\r\n\t\t\t\tCon_Printf(\"Loaded Certificate fingerprint is %s\\n\", b64);\r\n\t\t\t\tcrypt.pCertNameToStrA(ret->dwCertEncodingType, &ret->pCertInfo->Subject, CERT_X500_NAME_STR, fdn, sizeof(fdn));\r\n\t\t\t\tCon_Printf(\"Loaded Certificate DN: %s\\n\", fdn);\r\n\t\t\t\treturn ret;\r\n\t\t\t}\r\n\t\t}\r\n\t\tCon_Printf(CON_ERROR\"pfx certificate failed to load.\\n\");\r\n\t\tpfxname = NULL;\t//don't overwrite an overridden name. it \r\n\t}\r\n\telse if (pfxname)\r\n\t\tCon_Printf(CON_WARNING\"Generating new pfx file: %s\\n\", pfxname);\r\n\r\n\tmemset(&sigalg, 0, sizeof(sigalg));\r\n\tsigalg.pszObjId = szOID_RSA_SHA512RSA;\r\n\r\n\tGetSystemTime(&expiredate);\r\n\texpiredate.wYear += 5;\t//5 years hence. woo\r\n\texpiredate.wDay = 1;\t//work around feb nightmares... not to be confused with Week-Day...\r\n\r\n\r\n\tmemset(&issuerblob, 0, sizeof(issuerblob));\r\n\tcrypt.pCertStrToNameA(X509_ASN_ENCODING, issuertext, CERT_X500_NAME_STR, NULL, issuerblob.pbData, &issuerblob.cbData, NULL);\r\n\tissuerblob.pbData = Z_Malloc(issuerblob.cbData);\r\n\tcrypt.pCertStrToNameA(X509_ASN_ENCODING, issuertext, CERT_X500_NAME_STR, NULL, issuerblob.pbData, &issuerblob.cbData, NULL);\r\n\r\n\tadvapi.pCryptAcquireContextW(&prov, container, provname, provtype, CRYPT_NEWKEYSET|CRYPT_MACHINE_KEYSET);\r\n\tif (!prov)\r\n\t{\t//try again. fucking retarded api.\r\n\t\tif (!advapi.pCryptAcquireContextW(&prov, container, provname, provtype, CRYPT_MACHINE_KEYSET))\r\n\t\t{\r\n\t\t\tCon_Printf(CON_ERROR\"CryptAcquireContext failed.\\n\");\r\n\t\t\treturn NULL;\r\n\t\t}\r\n\t}\r\n\tadvapi.pCryptGenKey(prov, AT_KEYEXCHANGE, CRYPT_EXPORTABLE|CRYPT_ARCHIVABLE, &hkey);\r\n\tkpi.pwszContainerName = container;\r\n\tkpi.pwszProvName = provname;\r\n\tkpi.dwProvType = provtype;\r\n\tkpi.dwFlags = CERT_SET_KEY_CONTEXT_PROP_ID;\r\n\tkpi.cProvParam = 0;\r\n\tkpi.dwKeySpec = AT_KEYEXCHANGE;\r\n\r\n\tret = crypt.pCertCreateSelfSignCertificate(\r\n\t\t\tprov,\r\n\t\t\t&issuerblob,\r\n\t\t\t0,\r\n\t\t\t&kpi,\r\n\t\t\t&sigalg,\r\n\t\t\tNULL,\r\n\t\t\t&expiredate,\r\n\t\t\tNULL\r\n\t\t);\r\n\tif (!ret)\r\n\t{\t//try and downgrade the signature algo if it failed.\r\n\t\tsigalg.pszObjId = szOID_RSA_SHA1RSA;\r\n\t\tret = crypt.pCertCreateSelfSignCertificate(\r\n\t\t\tprov,\r\n\t\t\t&issuerblob,\r\n\t\t\t0,\r\n\t\t\t&kpi,\r\n\t\t\t&sigalg,\r\n\t\t\tNULL,\r\n\t\t\t&expiredate,\r\n\t\t\tNULL\r\n\t\t);\r\n\t}\r\n\tif (!ret)\r\n\t\tCon_Printf(CON_ERROR\"SChannel: Certificate generation failed...\\n\");\t//happens in wine.\r\n\telse\r\n\t{\r\n\t\t//this is stupid and redundant, yet apparently still needed.\r\n\t\tkpi.pwszContainerName = container;\r\n\t\tkpi.pwszProvName = provname;\r\n\t\tkpi.dwProvType = provtype;\r\n\t\tkpi.dwFlags = CRYPT_MACHINE_KEYSET;\r\n\t\tkpi.dwKeySpec = AT_KEYEXCHANGE;\r\n\t\tcrypt.pCertSetCertificateContextProperty(ret, CERT_KEY_PROV_INFO_PROP_ID, 0, &kpi);\r\n\r\n\t\t{\r\n\t\t\tHCRYPTPROV hprov = 0;\r\n\t\t\tDWORD keyspec = 0;\r\n\t\t\tBOOL willbefalse = false;\r\n\t\t\tif (!crypt.pCryptAcquireCertificatePrivateKey(ret, 0, NULL, &hprov, &keyspec, &willbefalse))\r\n\t\t\t{\r\n\t\t\t\tCon_Printf(CON_ERROR\"Private key is defective.\\n\");\r\n\t\t\t\treturn NULL;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t//write it to disk\r\n\t\t{\r\n\t\t\twchar_t password[512];\r\n\t\t\tCRYPT_DATA_BLOB blob = {0};\r\n\t\t\tHCERTSTORE store = crypt.pCertOpenStore(CERT_STORE_PROV_MEMORY, 0, 0, CERT_STORE_CREATE_NEW_FLAG, NULL);\r\n\t\t\tif (store)\r\n\t\t\t{\r\n\t\t\t\tif (crypt.pCertAddCertificateContextToStore(store, ret, CERT_STORE_ADD_ALWAYS, NULL))\r\n\t\t\t\t{\r\n\t\t\t\t\tDWORD fucksake = countof(password);\r\n\t\t\t\t\tif (!GetUserNameW(password, &fucksake))\t//use their username as a password. at least it'll block most accidental redistriction. should prolly mix in their computer name, w/e\r\n\t\t\t\t\t\t*password = 0;\r\n\r\n\t\t\t\t\tif (crypt.pPFXExportCertStoreEx(store, &blob, password, NULL, EXPORT_PRIVATE_KEYS|REPORT_NO_PRIVATE_KEY|REPORT_NOT_ABLE_TO_EXPORT_PRIVATE_KEY))\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tblob.pbData = alloca(blob.cbData);\r\n\t\t\t\t\t\tif (crypt.pPFXExportCertStoreEx(store, &blob, password, NULL, EXPORT_PRIVATE_KEYS|REPORT_NO_PRIVATE_KEY|REPORT_NOT_ABLE_TO_EXPORT_PRIVATE_KEY))\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tif (blob.cbData)\r\n\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\tif (!FS_WriteFile(pfxname?pfxname:\"identity.pfx\", blob.pbData, blob.cbData, pfxname?FS_SYSTEM:FS_ROOT))\r\n\t\t\t\t\t\t\t\t\tCon_Printf(CON_ERROR\"FS_WriteFile(%s) failed\\n\", pfxname?pfxname:\"identity.pfx\");\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\telse\r\n\t\t\t\t\t\t\t\tCon_Printf(CON_ERROR\"PFXExportCertStoreEx no data\\n\");\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\telse\r\n\t\t\t\t\t\t\tCon_Printf(CON_ERROR\"PFXExportCertStoreEx failed\\n\");\r\n\t\t\t\t\t}\r\n\t\t\t\t\telse\r\n\t\t\t\t\t\tCon_Printf(CON_ERROR\"PFXExportCertStoreEx failed\\n\");\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t\tCon_Printf(CON_ERROR\"CertAddCertificateContextToStore failed\\n\");\r\n\t\t\t\tcrypt.pCertCloseStore(store, 0);\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t\tCon_Printf(CON_ERROR\"CertOpenStore failed\\n\");\r\n\t\t}\r\n\r\n\t\t{\r\n\t\t\tchar fdn[1024];\r\n\t\t\tchar digest[DIGEST_MAXSIZE];\r\n\t\t\tchar b64[DIGEST_MAXSIZE*2+1];\r\n\t\t\tsize_t dgsz = CalcHash(&hash_sha1, digest, sizeof(digest), ret->pbCertEncoded, ret->cbCertEncoded);\r\n\t\t\tBase64_EncodeBlock(digest, dgsz, b64, sizeof(b64));\r\n\t\t\tCon_Printf(\"Generated Certificate fingerprint is %s\\n\", b64);\r\n\t\t\tcrypt.pCertNameToStrA(ret->dwCertEncodingType, &ret->pCertInfo->Subject, CERT_X500_NAME_STR, fdn, sizeof(fdn));\r\n\t\t\tCon_Printf(\"Generated Certificate DN: %s\\n\", fdn);\r\n\t\t}\r\n\t}\r\n\tZ_Free(issuerblob.pbData);\r\n\r\n\treturn ret;\r\n}\r\n\r\nstatic void SSPI_GenServerCredentials(sslfile_t *f)\r\n{\r\n\tSECURITY_STATUS   ss;\r\n\tTimeStamp         Lifetime;\r\n\tSCHANNEL_CRED SchannelCred;\r\n\tPCCERT_CONTEXT cred;\r\n\r\n\tmemset(&SchannelCred, 0, sizeof(SchannelCred));\r\n\tSchannelCred.dwVersion = SCHANNEL_CRED_VERSION;\r\n\tSchannelCred.grbitEnabledProtocols = f->datagram?USE_PROT_DGRAM_SERVER:USE_PROT_SERVER;\r\n\tSchannelCred.dwFlags |= SCH_CRED_NO_SYSTEM_MAPPER|SCH_CRED_DISABLE_RECONNECTS;\t/*don't use windows login info or anything*/\r\n\r\n\tcred = SSPI_GetServerCertificate();\r\n\tSchannelCred.cCreds = 1;\r\n\tSchannelCred.paCred = &cred;\r\n\r\n\tif (!cred)\r\n\t{\r\n\t\tSSPI_Error(f, localtext(\"Unable to load/generate certificate\\n\"));\r\n\t\treturn;\r\n\t}\r\n\r\n\tss = secur.pAcquireCredentialsHandleA (NULL, UNISP_NAME_A, SECPKG_CRED_INBOUND, NULL, &SchannelCred, NULL, NULL, &f->cred, &Lifetime);\r\n\tif (FAILED(ss) && f->datagram)\r\n\t{\t//try again with just dtls1, for <win10\r\n\t\tSchannelCred.grbitEnabledProtocols = SP_PROT_DTLS1_0_SERVER;\r\n\t\tss = secur.pAcquireCredentialsHandleA (NULL, UNISP_NAME_A, SECPKG_CRED_INBOUND, NULL, &SchannelCred, NULL, NULL, &f->cred, &Lifetime);\r\n\t}\r\n\tif (FAILED(ss))\r\n\t{\r\n\t\tSSPI_Error(f, localtext(\"WinSSPI: AcquireCredentialsHandle failed %#x\\n\"), ss);\r\n\t\treturn;\r\n\t}\r\n}\r\n\r\nstatic void SSPI_Handshake (sslfile_t *f)\r\n{\r\n\tSECURITY_STATUS\t\tss;\r\n\tTimeStamp\t\t\tLifetime;\r\n\tSecBufferDesc\t\tOutBuffDesc;\r\n\tSecBuffer\t\t\tOutSecBuff[8];\r\n\tSecBufferDesc\t\tInBuffDesc;\r\n\tSecBuffer\t\t\tInSecBuff[8];\r\n\tULONG\t\t\t\tContextAttributes;\r\n\tSCHANNEL_CRED SchannelCred;\r\n\tint i;\r\n\tqboolean retries = 5;\r\n\r\nretry:\r\n\r\n\tif (f->outcrypt.avail)\r\n\t{\r\n\t\t//don't let things build up too much\r\n\t\tSSPI_TryFlushCryptOut(f);\r\n\t\tif (f->outcrypt.avail)\r\n\t\t\treturn;\r\n\t}\r\n\r\n\t//FIXME: skip this if we've had no new data since last time\r\n\r\n\tOutBuffDesc.ulVersion = SECBUFFER_VERSION;\r\n\tOutBuffDesc.cBuffers  = countof(OutSecBuff);\r\n\tOutBuffDesc.pBuffers  = OutSecBuff;\r\n\r\n\tOutSecBuff[0].BufferType = SECBUFFER_TOKEN;\r\n\tOutSecBuff[0].cbBuffer   = f->outcrypt.datasize - f->outcrypt.avail;\r\n\tOutSecBuff[0].pvBuffer   = f->outcrypt.data + f->outcrypt.avail;\r\n\r\n\tfor (i = 0; i < OutBuffDesc.cBuffers; i++)\r\n\t{\r\n\t\tOutSecBuff[i].BufferType = SECBUFFER_EMPTY;\r\n\t\tOutSecBuff[i].pvBuffer   = NULL;\r\n\t\tOutSecBuff[i].cbBuffer   = 0;\r\n\t}\r\n\r\n\tif (f->handshaking == HS_ERROR)\r\n\t\treturn;\t//gave up.\r\n\telse if (f->handshaking == HS_STARTCLIENT)\r\n\t{\r\n\t\tPCCERT_CONTEXT cert;\r\n\r\n\t\t//no input data yet.\r\n\t\tf->handshaking = HS_CLIENT;\r\n\r\n\t\tmemset(&SchannelCred, 0, sizeof(SchannelCred));\r\n\t\tSchannelCred.dwVersion = SCHANNEL_CRED_VERSION;\r\n\t\tSchannelCred.grbitEnabledProtocols = f->datagram?USE_PROT_DGRAM_CLIENT:USE_PROT_CLIENT;\r\n\t\tSchannelCred.dwFlags |= SCH_CRED_SNI_CREDENTIAL | SCH_CRED_NO_DEFAULT_CREDS;\t/*don't use windows login info or anything*/\r\n\r\n\t\t//just always use the same credentials, cos we're lazy and lame, and windows users don't give a shit about privacy anyway.\r\n\t\tcert = SSPI_GetServerCertificate();\r\n\t\tif (cert)\r\n\t\t{\r\n\t\t\tSchannelCred.cCreds = 1;\r\n\t\t\tSchannelCred.paCred = &cert;\r\n\t\t}\r\n\r\n\t\tss = secur.pAcquireCredentialsHandleA (NULL, UNISP_NAME_A, SECPKG_CRED_OUTBOUND, NULL, &SchannelCred, NULL, NULL, &f->cred, &Lifetime);\r\n\t\tif (FAILED(ss) && f->datagram)\r\n\t\t{\r\n\t\t\tSchannelCred.grbitEnabledProtocols = SP_PROT_DTLS1_0_CLIENT;\r\n\t\t\tss = secur.pAcquireCredentialsHandleA (NULL, UNISP_NAME_A, SECPKG_CRED_OUTBOUND, NULL, &SchannelCred, NULL, NULL, &f->cred, &Lifetime);\r\n\t\t}\r\n\t\tif (ss < 0)\r\n\t\t{\r\n\t\t\tSSPI_Error(f, localtext(\"WINSSPI: AcquireCredentialsHandle failed (%x)\\n\"), (int)ss);\r\n\t\t\treturn;\r\n\t\t}\r\n\t\tss = secur.pInitializeSecurityContextW (&f->cred, NULL, f->wpeername, MessageAttribute|(f->datagram?ISC_REQ_DATAGRAM:ISC_REQ_STREAM), 0, SECURITY_NATIVE_DREP, NULL, 0, &f->sechnd, &OutBuffDesc, &ContextAttributes, &Lifetime);\r\n\t}\r\n\telse if (f->handshaking == HS_CLIENT)\r\n\t{\r\n\t\t//only if we actually have data.\r\n\t\tif (!f->incrypt.avail && !f->datagram)\r\n\t\t\treturn;\r\n\r\n\t\tInBuffDesc.ulVersion = SECBUFFER_VERSION;\r\n\t\tInBuffDesc.cBuffers  = 4;\r\n\t\tInBuffDesc.pBuffers  = InSecBuff;\r\n\r\n\t\ti = 0;\r\n\r\n\t\tif (f->incrypt.avail)\r\n\t\t{\r\n\t\t\tInSecBuff[i].BufferType = SECBUFFER_TOKEN;\r\n\t\t\tInSecBuff[i].cbBuffer   = f->incrypt.avail;\r\n\t\t\tInSecBuff[i].pvBuffer   = f->incrypt.data;\r\n\t\t\ti++;\r\n\t\t}\r\n\r\n\t\tfor (; i < InBuffDesc.cBuffers; i++)\r\n\t\t{\r\n\t\t\tInSecBuff[i].BufferType = SECBUFFER_EMPTY;\r\n\t\t\tInSecBuff[i].pvBuffer   = NULL;\r\n\t\t\tInSecBuff[i].cbBuffer   = 0;\r\n\t\t}\r\n\r\n\t\tss = secur.pInitializeSecurityContextW (&f->cred, &f->sechnd, NULL, MessageAttribute|(f->datagram?ISC_REQ_DATAGRAM:ISC_REQ_STREAM), 0, SECURITY_NETWORK_DREP, &InBuffDesc, 0, NULL, &OutBuffDesc, &ContextAttributes, &Lifetime);\r\n\r\n\t\tif (ss == SEC_E_INCOMPLETE_MESSAGE)\r\n\t\t{\t//TLS splits the data randomly, so this should not be considered fatal\r\n//\t\t\tCon_Printf(\"SEC_E_INCOMPLETE_MESSAGE (available %i)\\n\", (int)f->incrypt.avail);\r\n\t\t\tif (!f->datagram && f->incrypt.avail == f->incrypt.datasize)\r\n\t\t\t\tSSPI_ExpandBuffer(&f->incrypt, f->incrypt.datasize+1024);\r\n\t\t\treturn;\r\n\t\t}\r\n\t\telse if (ss == SEC_E_INVALID_TOKEN)\r\n\t\t{\r\n//\t\t\tCon_Printf(\"SEC_E_INVALID_TOKEN\\n\");\r\n\t\t\tif (f->datagram)\r\n\t\t\t\treturn;\t//our udp protocol may have non-dtls packets mixed in. besides, we don't want to die from spoofed packets.\r\n\t\t}\r\n//\t\telse if (ss == SEC_I_MESSAGE_FRAGMENT)\r\n//\t\t\tCon_Printf(\"SEC_I_MESSAGE_FRAGMENT\\n\");\r\n//\t\telse if (ss == SEC_I_CONTINUE_NEEDED)\r\n//\t\t\tCon_Printf(\"SEC_I_CONTINUE_NEEDED\\n\");\r\n//\t\telse\r\n//\t\t\tCon_Printf(\"InitializeSecurityContextA %x\\n\", ss);\r\n\r\n\r\n\t\t//any extra data should still remain for the next time around. this might be more handshake data or payload data.\r\n\t\tif (InSecBuff[1].BufferType == SECBUFFER_EXTRA)\r\n\t\t{\r\n\t\t\tmemmove(f->incrypt.data, f->incrypt.data + (f->incrypt.avail - InSecBuff[1].cbBuffer), InSecBuff[1].cbBuffer);\r\n\t\t\tf->incrypt.avail = InSecBuff[1].cbBuffer;\r\n\t\t}\r\n\t\telse f->incrypt.avail = 0;\r\n\t}\r\n\telse if (f->handshaking == HS_STARTSERVER || f->handshaking == HS_SERVER)\r\n\t{\r\n\t\t//only if we actually have data.\r\n\t\tif (!f->incrypt.avail)\r\n\t\t\treturn;\r\n\r\n\t\tInBuffDesc.ulVersion = SECBUFFER_VERSION;\r\n\t\tInBuffDesc.cBuffers  = countof(InSecBuff);\r\n\t\tInBuffDesc.pBuffers  = InSecBuff;\r\n\t\ti = 0;\r\n\r\n\t\tif (f->incrypt.avail)\r\n\t\t{\r\n\t\t\tInSecBuff[i].BufferType = SECBUFFER_TOKEN;\r\n\t\t\tInSecBuff[i].cbBuffer   = f->incrypt.avail;\r\n\t\t\tInSecBuff[i].pvBuffer   = f->incrypt.data;\r\n\t\t\ti++;\r\n\t\t}\r\n\r\n\t\tif (f->datagram)\r\n\t\t{\t//for dtls's cookie\r\n\t\t\tInSecBuff[i].BufferType = SECBUFFER_EXTRA;\r\n\t\t\tInSecBuff[i].cbBuffer   = 11;\r\n\t\t\tInSecBuff[i].pvBuffer   = \"Hello World\";\r\n\t\t\ti++;\r\n\t\t}\r\n\r\n\t\tfor (; i < InBuffDesc.cBuffers; i++)\r\n\t\t{\r\n\t\t\tInSecBuff[i].BufferType = SECBUFFER_EMPTY;\r\n\t\t\tInSecBuff[i].pvBuffer   = NULL;\r\n\t\t\tInSecBuff[i].cbBuffer   = 0;\r\n\t\t}\r\n\r\n\t\ti = 1;\r\n\t\tOutSecBuff[i++].BufferType = SECBUFFER_EXTRA;\r\n\t\tOutSecBuff[i++].BufferType = SECBUFFER_ALERT;\r\n\r\n#define ServerMessageAttribute (ASC_REQ_SEQUENCE_DETECT | ASC_REQ_REPLAY_DETECT | ASC_REQ_CONFIDENTIALITY | /*ASC_REQ_EXTENDED_ERROR |*/ ASC_REQ_ALLOCATE_MEMORY)\r\n\t\tContextAttributes = ServerMessageAttribute|(f->datagram?ASC_REQ_DATAGRAM:ASC_REQ_STREAM);\r\n//\t\tif (f->peerhash)\t//for ICE\r\n//\t\t\tContextAttributes |= ASC_REQ_MUTUAL_AUTH;\r\n\r\n\t\tss = secur.pAcceptSecurityContext(&f->cred, (f->handshaking==HS_SERVER)?&f->sechnd:NULL, &InBuffDesc,\r\n\t\t\t\t\t\t\t\tContextAttributes, SECURITY_NETWORK_DREP, &f->sechnd,\r\n\t\t\t\t\t\t\t\t&OutBuffDesc, &ContextAttributes, NULL); \r\n\t\tif (f->datagram && ss == SEC_I_CONTINUE_NEEDED && f->handshaking != HS_SERVER)\r\n\t\t{\t//this seems wrong, but schannel complains if we don't continue to pass null above.\r\n\t\t\tsecur.pDeleteSecurityContext(&f->sechnd); //avoid leaks\r\n\t\t\tmemset(&f->sechnd, 0, sizeof(f->sechnd));\r\n\t\t}\r\n\t\telse\r\n\t\t\tf->handshaking = HS_SERVER;\r\n\r\n\t\tif (ss == SEC_E_INVALID_TOKEN)\r\n\t\t{\t//sender sent us something that wasn't (d)tls.\r\n//\t\t\tCon_Printf(\"SEC_E_INVALID_TOKEN\\n\");\r\n\t\t\tif (f->datagram)\r\n\t\t\t\treturn;\r\n\t\t}\r\n\t\telse if (ss == SEC_E_INCOMPLETE_MESSAGE)\r\n\t\t{\r\n//\t\t\tCon_Printf(\"SEC_E_INCOMPLETE_MESSAGE\\n\");\r\n\t\t\tif (!f->datagram && f->incrypt.avail == f->incrypt.datasize)\r\n\t\t\t\tSSPI_ExpandBuffer(&f->incrypt, f->incrypt.datasize+1024);\r\n\t\t\treturn;\r\n\t\t}\r\n//\t\telse if (FAILED(ss))\r\n//\t\t\tCon_Printf(\"AcceptSecurityContext %x\\n\", ss);\r\n\r\n\t\t//any extra data should still remain for the next time around. this might be more handshake data or payload data.\r\n\t\tif (InSecBuff[1].BufferType == SECBUFFER_EXTRA && !f->datagram)\r\n\t\t{\r\n\t\t\tmemmove(f->incrypt.data, f->incrypt.data + (f->incrypt.avail - InSecBuff[1].cbBuffer), InSecBuff[1].cbBuffer);\r\n\t\t\tf->incrypt.avail = InSecBuff[1].cbBuffer;\r\n\t\t}\r\n\t\telse f->incrypt.avail = 0;\r\n\t}\r\n\telse\r\n\t\treturn;\r\n\r\n\r\n\tif (ss == SEC_I_INCOMPLETE_CREDENTIALS)\r\n\t{\r\n\t\t//FIXME: load/generate our identity, redo the pAcquireCredentialsHandleA\r\n\t\tCon_TPrintf(CON_WARNING\"server requires credentials, attempting to ignore\\n\");\r\n\t\tgoto retry;\r\n\t}\r\n\r\n\tif (FAILED(ss))\r\n\t{\r\n\t\tconst char *fname = (f->handshaking>=HS_STARTSERVER)?\"AcceptSecurityContext\":\"InitializeSecurityContext\";\r\n\t\tswitch(ss)\r\n\t\t{\r\n\t\tcase SEC_E_ALGORITHM_MISMATCH:\tSSPI_Error(f, \"%s failed: SEC_E_ALGORITHM_MISMATCH\\n\",\tfname);\tbreak;\r\n\t\tcase SEC_E_INVALID_HANDLE:\t\tSSPI_Error(f, \"%s failed: SEC_E_INVALID_HANDLE\\n\",\t\tfname);\tbreak;\r\n\t\tcase SEC_E_ILLEGAL_MESSAGE:\t\tSSPI_Error(f, \"%s failed: SEC_E_ILLEGAL_MESSAGE\\n\",\t\tfname);\tbreak;\r\n\t\tcase SEC_E_INVALID_TOKEN:\t\tSSPI_Error(f, \"%s failed: SEC_E_INVALID_TOKEN\\n\",\t\tfname);\tbreak;\r\n\t\tcase SEC_E_INVALID_PARAMETER:\tSSPI_Error(f, \"%s failed: SEC_E_INVALID_PARAMETER\\n\",\tfname);\tbreak;\r\n\t\tcase SEC_E_INTERNAL_ERROR:\t\tSSPI_Error(f, \"%s failed: SEC_E_INTERNAL_ERROR\\n\",\t\tfname);\tbreak;\r\n\t\tdefault:\t\t\t\t\t\tSSPI_Error(f, \"%s failed: %lx\\n\",\t\t\tfname, (long)ss);\tbreak;\r\n\t\t}\r\n\t\treturn;\r\n\t}\r\n\r\n\tif ((SEC_I_COMPLETE_NEEDED == ss) || (SEC_I_COMPLETE_AND_CONTINUE == ss))  \r\n\t{\r\n\t\tss = secur.pCompleteAuthToken (&f->sechnd, &OutBuffDesc);\r\n\t\tif (ss < 0)  \r\n\t\t{\r\n\t\t\tSSPI_Error(f, \"CompleteAuthToken failed\\n\");\r\n\t\t\treturn;\r\n\t\t}\r\n\t}\r\n\r\n\t//its all okay and established if we get this far.\r\n\tif (ss == SEC_E_OK)\r\n\t{\r\n\t\tSecPkgContext_StreamSizes strsizes;\r\n\t\tCERT_CONTEXT *remotecert;\r\n\r\n//\t\tCon_Printf(\"ContextAttributes: %x (%s, %s)\\n\", ContextAttributes, (f->handshaking == HS_SERVER)?\"sv\":\"cl\", f->datagram?\"dtls\":\"tls\");\r\n\r\n\t\tif (f->datagram)\r\n\t\t{\t//ask for a bit bigger. we use sendto for mtu guessing, schannel spitting out errors for arbitrary lower values is just annoying - it knows nothing about the actual connection.\r\n\t\t\tULONG mtu = 8192;\r\n\t\t\tsecur.pSetContextAttributesA(&f->sechnd, SECPKG_ATTR_DTLS_MTU, &mtu, sizeof(mtu));\r\n\t\t}\r\n\r\n\t\tsecur.pQueryContextAttributesA(&f->sechnd, SECPKG_ATTR_STREAM_SIZES, &strsizes);\r\n\t\tf->headersize = strsizes.cbHeader;\r\n\t\tf->footersize = strsizes.cbTrailer;\r\n\r\n\t\tf->mtu = strsizes.cbMaximumMessage - (f->headersize+f->footersize);\t//compute the maximum payload we can actually get with that.\r\n\r\n\t\tif (f->handshaking != HS_SERVER)\r\n\t\t{\t//server takes an annonymous client. client expects a proper certificate.\r\n\t\t\tif (*f->wpeername)\r\n\t\t\t{\r\n\t\t\t\tss = secur.pQueryContextAttributesA(&f->sechnd, SECPKG_ATTR_REMOTE_CERT_CONTEXT, &remotecert);\r\n\t\t\t\tif (ss != SEC_E_OK)\r\n\t\t\t\t{\r\n\t\t\t\t\tSSPI_Error(f, localtext(\"unable to read server's certificate\\n\"));\r\n\t\t\t\t\treturn;\r\n\t\t\t\t}\r\n\t\t\t\tif (VerifyServerCertificate(remotecert, f->wpeername, 0, f->datagram))\r\n\t\t\t\t{\r\n\t\t\t\t\tSSPI_Error(f, localtext(\"Error validating certificate\\n\"));\r\n\t\t\t\t\treturn;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t\tSys_Printf(\"SSL/TLS Server name not specified, skipping verification\\n\");\r\n\t\t}\r\n\r\n\t\tf->handshaking = HS_ESTABLISHED;\r\n\t}\r\n\r\n\t//send early, send often.\r\n#ifdef HAVE_DTLS\r\n\tif (f->transmit)\r\n\t{\r\n\t\tfor (i = 0; i < OutBuffDesc.cBuffers; i++)\r\n\t\t\tif (OutSecBuff[i].BufferType == SECBUFFER_TOKEN && OutSecBuff[i].cbBuffer)\r\n\t\t\t{\r\n\t\t\t\tf->resendtimer = realtime+0.2;\r\n\t\t\t\tf->transmit(f->cbctx, OutSecBuff[i].pvBuffer, OutSecBuff[i].cbBuffer);\r\n\t\t\t}\r\n\t}\r\n\telse\r\n#endif\r\n\t{\r\n\t\ti = 0;\r\n\t\tif (SSPI_CopyIntoBuffer(&f->outcrypt, OutSecBuff[i].pvBuffer, OutSecBuff[i].cbBuffer, true) < OutSecBuff[i].cbBuffer)\r\n\t\t{\r\n\t\t\tSSPI_Error(f, \"crypt overflow\\n\");\r\n\t\t\treturn;\r\n\t\t}\r\n\t\tSSPI_TryFlushCryptOut(f);\r\n\t}\r\n\r\n\tif (f->handshaking == HS_ESTABLISHED)\r\n\t\tSSPI_Encode(f);\r\n\telse if (ss == SEC_I_MESSAGE_FRAGMENT)\t//looks like we can connect faster if we loop when we get this result.\r\n\t\tif (retries --> 0)\r\n\t\t\tgoto retry;\r\n}\r\n\r\nstatic int QDECL SSPI_ReadBytes (struct vfsfile_s *file, void *buffer, int bytestoread)\r\n{\r\n\tsslfile_t *f = (sslfile_t *)file;\r\n\tint err = SSPI_CheckNewInCrypt(f);\r\n\r\n\tif (f->handshaking)\r\n\t{\r\n\t\tSSPI_Handshake(f);\r\n\t\treturn err;\r\n\t}\r\n\r\n\tSSPI_Encode(f);\r\n\r\n\tSSPI_Decode(f);\r\n\r\n\tbytestoread = min(bytestoread, f->inraw.avail);\r\n\tif (bytestoread)\r\n\t{\r\n\t\tmemcpy(buffer, f->inraw.data, bytestoread);\r\n\t\tf->inraw.avail -= bytestoread;\r\n\t\tmemmove(f->inraw.data, f->inraw.data + bytestoread, f->inraw.avail);\r\n\t}\r\n\telse\r\n\t{\r\n\t\tif (err)\r\n\t\t\treturn err;\r\n\t}\r\n\treturn bytestoread;\r\n}\r\nstatic int QDECL SSPI_WriteBytes (struct vfsfile_s *file, const void *buffer, int bytestowrite)\r\n{\r\n\tsslfile_t *f = (sslfile_t *)file;\r\n\r\n\t//don't endlessly accept data faster than we can push it out.\r\n\t//we'll buffer a little, but don't go overboard\r\n\tif (f->outcrypt.avail > 8192)\r\n\t\treturn 0;\r\n\r\n\tbytestowrite = SSPI_CopyIntoBuffer(&f->outraw, buffer, bytestowrite, false);\r\n\r\n\tif (f->handshaking)\r\n\t{\r\n\t\tSSPI_CheckNewInCrypt(f);\t//make sure its ticking over\r\n\t\tSSPI_Handshake(f);\r\n\t}\r\n\telse\r\n\t{\r\n\t\tSSPI_Encode(f);\r\n\t}\r\n\r\n\treturn bytestowrite;\r\n}\r\nstatic qboolean QDECL SSPI_Seek (struct vfsfile_s *file, qofs_t pos)\r\n{\r\n\tSSPI_Error((sslfile_t*)file, \"unable to seek on streams\\n\");\r\n\treturn false;\r\n}\r\nstatic qofs_t QDECL SSPI_Tell (struct vfsfile_s *file)\r\n{\r\n\tSSPI_Error((sslfile_t*)file, \"unable to seek on streams\\n\");\r\n\treturn 0;\r\n}\r\nstatic qofs_t QDECL SSPI_GetLen (struct vfsfile_s *file)\r\n{\r\n\treturn 0;\r\n}\r\nstatic qboolean QDECL SSPI_Close (struct vfsfile_s *file)\r\n{\r\n\tsslfile_t *f = (sslfile_t *)file;\r\n\tqboolean success = f->stream != NULL;\r\n\tSSPI_Error(f, \"\");\r\n\r\n\tZ_Free(f->outraw.data);\r\n\tZ_Free(f->outcrypt.data);\r\n\tZ_Free(f->inraw.data);\r\n\tZ_Free(f->incrypt.data);\r\n\r\n\tZ_Free(f);\r\n\treturn success;\r\n}\r\n\r\n#include <wchar.h>\r\nstatic qboolean CleanUpHostname(const char *hostname, wchar_t *out, size_t outcount)\r\n{\t//strip any fte-specific junk, like dtls:// or [] or :\r\n\tconst char *hostnameend;\r\n\tconst char *host = strstr(hostname, \"://\");\r\n\tint i = 0;\r\n\tif (host)\r\n\t\thostname = host+3;\r\n\thostnameend = hostname+strlen(hostname);\r\n\t//any dtls:// prefix will have been stripped now.\r\n\tif (*hostname == '[')\r\n\t{\t//eg: [::1]:foo - skip the lead [ and strip the ] and any trailing data (hopefully just a :port or nothing)\r\n\t\thostname++;\r\n\t\thost = strchr(hostname, ']');\r\n\t\tif (host)\r\n\t\t\thostnameend = host;\r\n\t}\r\n\telse\r\n\t{\t//eg: 127.0.0.1:port - strip the port number if specified.\r\n\t\thost = strchr(hostname, ':');\r\n\t\tif (host)\r\n\t\t\thostnameend = host;\r\n\t}\r\n\r\n\twhile(hostname < hostnameend)\r\n\t{\r\n\t\tint err;\r\n\t\tint c = utf8_decode(&err, hostname, (void*)&hostname);\r\n\t\tif (c > WCHAR_MAX)\r\n\t\t\terr = true;\t//no 16bit surrogates. they're evil.\r\n\t\telse if (i == outcount - 1)\r\n\t\t\terr = true; //no space to store it\r\n\t\telse\r\n\t\t\tout[i++] = c;\r\n\t\tif (err)\r\n\t\t{\r\n\t\t\tout[i] = 0;\r\n\t\t\treturn false;\r\n\t\t}\r\n\t}\r\n\tout[i] = 0;\r\n\treturn true;\r\n}\r\n\r\nstatic vfsfile_t *SSPI_OpenVFS(const char *servername, vfsfile_t *source, qboolean server)\r\n{\r\n\tsslfile_t *newf;\r\n\tif (!source || !SSL_Inited())\r\n\t\treturn NULL;\r\n\r\n/*\r\n\tif (server)\t//unsupported\r\n\t\treturn NULL;\r\n*/\r\n\r\n\tnewf = Z_Malloc(sizeof(*newf));\r\n\tif (!CleanUpHostname(server?\"\":servername, newf->wpeername, countof(newf->wpeername)))\r\n\t{\r\n\t\tZ_Free(newf);\r\n\t\treturn NULL;\r\n\t}\r\n\r\n\tnewf->handshaking = server?HS_STARTSERVER:HS_STARTCLIENT;\r\n\tnewf->stream = source;\r\n\tnewf->funcs.Close = SSPI_Close;\r\n\tnewf->funcs.Flush = NULL;\r\n\tnewf->funcs.GetLen = SSPI_GetLen;\r\n\tnewf->funcs.ReadBytes = SSPI_ReadBytes;\r\n\tnewf->funcs.Seek = SSPI_Seek;\r\n\tnewf->funcs.Tell = SSPI_Tell;\r\n\tnewf->funcs.WriteBytes = SSPI_WriteBytes;\r\n\tnewf->funcs.seekstyle = SS_UNSEEKABLE;\r\n\r\n\tSSPI_ExpandBuffer(&newf->outraw,\t8192);\r\n\tSSPI_ExpandBuffer(&newf->outcrypt,\t8192);\r\n\tSSPI_ExpandBuffer(&newf->inraw,\t\t8192);\r\n\tSSPI_ExpandBuffer(&newf->incrypt,\t8192);\r\n\r\n\tif (server)\r\n\t\tSSPI_GenServerCredentials(newf);\r\n\r\n\treturn &newf->funcs;\r\n}\r\n\r\n#ifndef SECPKG_ATTR_UNIQUE_BINDINGS\r\n#define SECPKG_ATTR_UNIQUE_BINDINGS 25\r\ntypedef struct _SecPkgContext_Bindings\r\n{\r\n\tunsigned long        BindingsLength;\r\n\tSEC_CHANNEL_BINDINGS *Bindings;\r\n} SecPkgContext_Bindings, *PSecPkgContext_Bindings;\r\n#endif\r\nstatic int SSPI_GetChannelBinding(vfsfile_t *vf, qbyte *binddata, size_t *bindsize)\r\n{\r\n\tint ret;\r\n\tsslfile_t *f = (sslfile_t*)vf;\r\n\tSecPkgContext_Bindings bindings;\r\n\tif (vf->Close != SSPI_Close)\r\n\t\treturn -2;\t//not one of ours.\r\n\r\n\tbindings.BindingsLength = 0;\r\n\tbindings.Bindings = NULL;\r\n\tret = 0;\r\n\tswitch(secur.pQueryContextAttributesA(&f->sechnd, SECPKG_ATTR_UNIQUE_BINDINGS, &bindings))\r\n\t{\r\n\tcase SEC_E_OK:\r\n\t\tif (bindings.Bindings->cbApplicationDataLength <= *bindsize && !Q_strncasecmp(((char*)bindings.Bindings)+bindings.Bindings->dwApplicationDataOffset, \"tls-unique:\", 11))\r\n\t\t{\r\n\t\t\t//will contain 'tls-unique:BINARYDATA'\r\n\t\t\t*bindsize = bindings.Bindings->cbApplicationDataLength-11;\r\n\t\t\tmemcpy(binddata, ((unsigned char*) bindings.Bindings) + bindings.Bindings->dwApplicationDataOffset+11, bindings.Bindings->cbApplicationDataLength-11);\r\n\t\t\tret = 1;\r\n\t\t}\r\n\t\t//FIXME: leak\r\n\t\t//secur.pFreeContextBuffer(bindings.Bindings);\r\n\t\tbreak;\r\n\tcase SEC_E_UNSUPPORTED_FUNCTION:\r\n\t\tret = -1;\t//schannel doesn't support it. too old an OS, I guess.\r\n\t\tbreak;\r\n\tdefault:\r\n\t\tbreak;\r\n\t}\r\n\treturn ret;\r\n}\r\n\r\n#include \"netinc.h\"\r\n#if defined(HAVE_DTLS)\r\nstatic void *SSPI_DTLS_CreateContext(const dtlscred_t *credinfo, void *cbctx, neterr_t(*push)(void *cbctx, const qbyte *data, size_t datasize), qboolean isserver)\r\n{\r\n\tsslfile_t *ctx;\r\n\tif (!SSL_Inited())\r\n\t\treturn NULL;\r\n\r\n\tctx = Z_Malloc(sizeof(*ctx));\r\n\tctx->datagram = true;\r\n\tctx->handshaking = isserver?HS_STARTSERVER:HS_STARTCLIENT;\r\n\tctx->cbctx = cbctx;\r\n\tctx->transmit = push;\r\n\r\n\tif (!CleanUpHostname((credinfo&&credinfo->peer.name)?credinfo->peer.name:\"\", ctx->wpeername, countof(ctx->wpeername)))\r\n\t{\r\n\t\tZ_Free(ctx);\r\n\t\treturn NULL;\r\n\t}\r\n\r\n\tSSPI_ExpandBuffer(&ctx->outraw,\t\t8192);\r\n\tSSPI_ExpandBuffer(&ctx->outcrypt,\t65536);\r\n\tSSPI_ExpandBuffer(&ctx->inraw,\t\t8192);\r\n\tSSPI_ExpandBuffer(&ctx->incrypt,\t65536);\r\n\r\n\tif (isserver)\r\n\t\tSSPI_GenServerCredentials(ctx);\r\n\telse\r\n\t\tSSPI_Handshake(ctx);\t//begin the initial handshake now\r\n\treturn ctx;\r\n}\r\n\r\nstatic void SSPI_DTLS_DestroyContext(void *vctx)\r\n{\r\n\tSSPI_Close(vctx);\r\n}\r\n\r\n\r\nstatic neterr_t SSPI_DTLS_Transmit(void *ctx, const qbyte *data, size_t datasize)\r\n{\r\n\tsslfile_t *f = (sslfile_t *)ctx;\r\n\r\n\tif (!datasize)\r\n\t{\t//we use these as a way to probe whether its sendable or not yet.\r\n\t\tif (f->handshaking)\r\n\t\t{\r\n\t\t\tif (f->resendtimer < realtime)\r\n\t\t\t\tSSPI_Handshake(f);\t//keep trying...\r\n\t\t\treturn NETERR_CLOGGED;\r\n\t\t}\r\n\t\treturn NETERR_SENT;\r\n\t}\r\n\r\n\tif (f->handshaking)\r\n\t\tSSPI_Handshake(f);\t//keep trying...\r\n\tif (f->handshaking == HS_ERROR)\r\n\t\treturn NETERR_DISCONNECTED;\r\n\tif (f->handshaking)\r\n\t\treturn NETERR_CLOGGED;\t//not ready yet\r\n\r\n\tif (datasize >= f->mtu)\r\n\t\treturn NETERR_MTU;\t//we're not allowed.\r\n\r\n\t//sspi likes writing over the source data. make sure nothing is hurt by copying it out first.\r\n\tf->outraw.avail = 0;\r\n\tSSPI_CopyIntoBuffer(&f->outraw, data, datasize, true);\r\n\treturn SSPI_Encode(f);\r\n}\r\n\r\nstatic neterr_t SSPI_DTLS_Received(void *ctx, sizebuf_t *msg)\r\n{\r\n\tint ret;\r\n\tsslfile_t *f = (sslfile_t *)ctx;\r\n\r\n\tf->incrypt.data = msg->data;\r\n\tf->incrypt.avail = f->incrypt.datasize = msg->cursize;\r\n\r\n\tif (f->handshaking)\r\n\t{\r\n\t\tSSPI_Handshake(f);\r\n\t\tret = NETERR_CLOGGED;\t//not ready yet\r\n\r\n\t\tif (f->handshaking == HS_ERROR)\r\n\t\t\tret = NETERR_DISCONNECTED;\r\n\t}\r\n\telse\r\n\t{\r\n\t\tSSPI_Decode(f);\r\n\t\tret = NETERR_SENT;\r\n\r\n\t\tif (f->inraw.avail < msg->maxsize)\r\n\t\t\tmsg->cursize = f->inraw.avail;\r\n\t\telse\r\n\t\t\tmsg->cursize = msg->maxsize;\r\n\t\tmemcpy(msg->data, f->inraw.data, msg->cursize);\r\n\t\tf->inraw.avail = 0;\r\n\t}\r\n\tf->incrypt.data = NULL;\r\n\treturn ret;\r\n}\r\nstatic neterr_t SSPI_DTLS_Timeouts(void *ctx)\r\n{\r\n\tsslfile_t *f = (sslfile_t *)ctx;\r\n\tif (f->handshaking)\r\n\t{\r\n\t\tSSPI_DTLS_Transmit(ctx, NULL, 0);\r\n\t\treturn NETERR_CLOGGED;\r\n\t}\r\n\treturn NETERR_SENT;\r\n}\r\n\r\nstatic qboolean SSPI_DTLS_CheckConnection(void *cbctx, void *peeraddr, size_t peeraddrsize, void *indata, size_t insize, neterr_t(*push)(void *cbctx, const qbyte *data, size_t datasize), void (*EstablishTrueContext)(void **cbctx, void *state))\r\n{\t//we got a packet that might be a 'dtls hello' packet. try figuring out what's going on.\r\n\r\n\tSECURITY_STATUS\t\tss;\r\n\tSecBufferDesc\t\tOutBuffDesc;\r\n\tSecBuffer\t\t\tOutSecBuff[8];\r\n\tSecBufferDesc\t\tInBuffDesc;\r\n\tSecBuffer\t\t\tInSecBuff[8];\r\n\tULONG\t\t\t\tContextAttributes;\r\n\tint i;\r\n\tchar replymessage[4094];\r\n\r\n\tstatic sslfile_t *pending;\r\n\tif (!pending)\r\n\t{\r\n\t\tpending = SSPI_DTLS_CreateContext(NULL, NULL, NULL, true);\r\n\t\tif (!pending)\r\n\t\t\treturn false;\r\n\t}\r\n\r\n\tInBuffDesc.ulVersion = SECBUFFER_VERSION;\r\n\tInBuffDesc.cBuffers  = countof(InSecBuff);\r\n\tInBuffDesc.pBuffers  = InSecBuff;\r\n\ti = 0;\r\n\r\n\tInSecBuff[i].BufferType = SECBUFFER_TOKEN;\r\n\tInSecBuff[i].cbBuffer   = insize;\r\n\tInSecBuff[i].pvBuffer   = indata;\r\n\ti++;\r\n\r\n\t//for dtls's cookie\r\n\tInSecBuff[i].BufferType = SECBUFFER_EXTRA;\r\n\tInSecBuff[i].cbBuffer   = peeraddrsize;\r\n\tInSecBuff[i].pvBuffer   = peeraddr;\r\n\ti++;\r\n\r\n\tfor (; i < InBuffDesc.cBuffers; i++)\r\n\t{\r\n\t\tInSecBuff[i].BufferType = SECBUFFER_EMPTY;\r\n\t\tInSecBuff[i].pvBuffer   = NULL;\r\n\t\tInSecBuff[i].cbBuffer   = 0;\r\n\t}\r\n\r\n\tOutBuffDesc.ulVersion = SECBUFFER_VERSION;\r\n\tOutBuffDesc.cBuffers  = countof(OutSecBuff);\r\n\tOutBuffDesc.pBuffers  = OutSecBuff;\r\n\r\n\tOutSecBuff[0].BufferType = SECBUFFER_TOKEN;\r\n\tOutSecBuff[0].cbBuffer   = sizeof(replymessage);\r\n\tOutSecBuff[0].pvBuffer   = replymessage;\r\n\r\n\tfor (i = 1; i < OutBuffDesc.cBuffers; i++)\r\n\t{\r\n\t\tOutSecBuff[i].BufferType = SECBUFFER_EMPTY;\r\n\t\tOutSecBuff[i].pvBuffer   = NULL;\r\n\t\tOutSecBuff[i].cbBuffer   = 0;\r\n\t}\r\n\r\n\ti = 1;\r\n\tOutSecBuff[i++].BufferType = SECBUFFER_EXTRA;\r\n\tOutSecBuff[i++].BufferType = SECBUFFER_ALERT;\r\n\r\n\tContextAttributes = ServerMessageAttribute|ASC_REQ_DATAGRAM;\r\n\tss = secur.pAcceptSecurityContext(&pending->cred, NULL, &InBuffDesc,\r\n\t\t\t\t\t\t\tContextAttributes, SECURITY_NETWORK_DREP, &pending->sechnd,\r\n\t\t\t\t\t\t\t&OutBuffDesc, &ContextAttributes, NULL); \r\n\r\n\t//expect SEC_I_CONTINUE_NEEDED for a outgoing challenge (cookie request)\r\n\t//expect SEC_I_MESSAGE_FRAGMENT for anything more.\r\n\r\n\tfor (i = 0; i < OutBuffDesc.cBuffers; i++)\r\n\t\tif (OutSecBuff[i].BufferType == SECBUFFER_TOKEN && OutSecBuff[i].cbBuffer)\r\n\t\t\tpush(cbctx, OutSecBuff[i].pvBuffer, OutSecBuff[i].cbBuffer);\r\n\tif (ss == SEC_I_MESSAGE_FRAGMENT)\r\n\t{\t//looks like we got a reply to the dtls handshake. lock down its ip.\r\n\t\tpending->cbctx = cbctx;\r\n\t\tpending->transmit = push;\r\n\t\tpending->handshaking = HS_SERVER;\r\n\t\tEstablishTrueContext(&pending->cbctx, pending);\r\n\t\tpending = NULL;\r\n\t\treturn true;\r\n\t}\r\n\t//try to avoid memory leaks. this stuff isn't documented nearly well enough.\r\n\tsecur.pDeleteSecurityContext(&pending->sechnd);\r\n\tmemset(&pending->sechnd, 0, sizeof(pending->sechnd));\r\n\r\n\treturn false;\r\n}\r\n\r\nqboolean SSPI_DTLS_GenTempCertificate(const char *subject, struct dtlslocalcred_s *cred)\r\n{\t//exporting+importing certs here is just too damn painful. use a single cert and a dummy value for the key. for webrtc this SHOULD be okay, even if its expired...\r\n\tPCCERT_CONTEXT cert = SSPI_GetServerCertificate();\r\n\tif (!cert)\r\n\t\treturn false;\r\n\r\n\tcred->cert = memcpy(BZ_Malloc(cert->cbCertEncoded), cert->pbCertEncoded, cert->cbCertEncoded);\r\n\tcred->certsize = cert->cbCertEncoded;\r\n\tcred->key = NULL;\r\n\tcred->keysize = 0;\r\n\r\n\treturn true;\r\n}\r\n\r\n\r\nstatic int SSPI_DTLS_GetPeerCertificate(void *ctx, enum certprops_e prop, char *out, size_t outsize)\r\n{\r\n\tsslfile_t *f = (sslfile_t *)ctx;\r\n\tCERT_CONTEXT *cert = NULL;\r\n\r\n\tsafeswitch(prop)\r\n\t{\r\n\tcase QCERT_ISENCRYPTED:\r\n\t\tif (f->handshaking == HS_ESTABLISHED)\r\n\t\t\treturn 0;\t//handshake is done, its all encrypted.\r\n\t\treturn -1;\t\t//still pending, doesn't count as encrypted yet.\r\n\r\n\tcase QCERT_PEERCERTIFICATE:\r\n\t\tsecur.pQueryContextAttributesA(&f->sechnd, SECPKG_ATTR_REMOTE_CERT_CONTEXT, &cert);\r\n\t\tif (cert && cert->cbCertEncoded <= outsize)\r\n\t\t{\r\n\t\t\tmemcpy(out, cert->pbCertEncoded, cert->cbCertEncoded);\r\n\t\t\treturn cert->cbCertEncoded;\r\n\t\t}\r\n\t\treturn -1;\r\n\r\n\tcase QCERT_PEERSUBJECT:\r\n\t\tsecur.pQueryContextAttributesA(&f->sechnd, SECPKG_ATTR_REMOTE_CERT_CONTEXT, &cert);\r\n\t\tif (cert && cert->cbCertEncoded <= outsize)\r\n\t\t\treturn crypt.pCertNameToStrA(cert->dwCertEncodingType, &cert->pCertInfo->Subject, CERT_X500_NAME_STR, out, outsize);\r\n\t\treturn -1;\r\n\tcase QCERT_LOCALCERTIFICATE:\r\n\tcase QCERT_LOBBYSTATUS:\r\n\tcase QCERT_LOBBYSENDCHAT:\r\n\tsafedefault:\r\n\t\treturn -1;\r\n\t}\r\n}\r\n\r\nstatic const dtlsfuncs_t dtlsfuncs_schannel =\r\n{\r\n\tSSPI_DTLS_CreateContext,\r\n\tSSPI_DTLS_CheckConnection,\r\n\tSSPI_DTLS_DestroyContext,\r\n\tSSPI_DTLS_Transmit,\r\n\tSSPI_DTLS_Received,\r\n\tSSPI_DTLS_Timeouts,\r\n\tSSPI_DTLS_GetPeerCertificate,\r\n\tSSPI_DTLS_GenTempCertificate\r\n};\r\nstatic const dtlsfuncs_t *SSPI_DTLS_InitServer(void)\r\n{\r\n\t//make sure we can load a cert...\r\n\tif (!SSL_Inited() || !SSPI_GetServerCertificate())\r\n\t\treturn NULL;\t//otherwise refuse to run as a server instead of causing cert issues with every restart for users.\r\n\r\n\treturn &dtlsfuncs_schannel;\r\n}\r\nstatic const dtlsfuncs_t *SSPI_DTLS_InitClient(void)\r\n{\r\n\treturn &dtlsfuncs_schannel;\r\n}\r\n#endif\r\n\r\n#if defined(_MSC_VER) && (_MSC_VER < 1900)\r\n#define SSPI_VerifyHash NULL\t//old versions of msvc are crippled...\r\n#else\r\n#define STATUS_SUCCESS ((NTSTATUS)0x00000000)\r\n#define STATUS_INVALID_SIGNATURE ((NTSTATUS)0xC000A000)\r\n#include <bcrypt.h>\r\nstatic enum hashvalidation_e SSPI_VerifyHash(const qbyte *hashdata, size_t hashsize, const qbyte *pemcert, size_t pemcertsize, const qbyte *signdata, size_t signsize)\r\n{\r\n\tNTSTATUS status;\r\n\tBCRYPT_KEY_HANDLE pubkey;\r\n\tconst char *pem = pemcert;\r\n\tconst char *pemend;\r\n\tqbyte *der;\r\n\tsize_t dersize;\r\n\r\n\tstatic const void *(WINAPI *pCertCreateContext) (DWORD dwContextType, DWORD dwEncodingType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags, PCERT_CREATE_CONTEXT_PARA pCreatePara);\r\n\tstatic BOOL (WINAPI *pCryptImportPublicKeyInfoEx2) (DWORD dwCertEncodingType, PCERT_PUBLIC_KEY_INFO pInfo, DWORD dwFlags, void *pvAuxInfo, BCRYPT_KEY_HANDLE *phKey);\r\n\tstatic BOOL (WINAPI *pCertFreeCertificateContext) (PCCERT_CONTEXT pCertContext);\r\n\tstatic dllhandle_t *crypt32;\r\n\tstatic dllfunction_t crypt32funcs[] = {\r\n\t\t{(void**)&pCertCreateContext,\t\t\t\"CertCreateContext\"},\r\n\t\t{(void**)&pCryptImportPublicKeyInfoEx2,\t\"CryptImportPublicKeyInfoEx2\"},\t//WARNING: fails on wine.\r\n\t\t{(void**)&pCertFreeCertificateContext,\t\"CertFreeCertificateContext\"},\r\n\t\t{NULL,NULL}\r\n\t};\r\n\r\n\tstatic NTSTATUS (WINAPI *pBCryptVerifySignature) (BCRYPT_KEY_HANDLE hKey, VOID *pPaddingInfo, PUCHAR pbHash, ULONG cbHash, PUCHAR pbSignature, ULONG cbSignature, ULONG dwFlags);\r\n\tstatic NTSTATUS (WINAPI *pBCryptDestroyKey) (BCRYPT_KEY_HANDLE hKey);\r\n\tstatic dllhandle_t *bcrypt;\r\n\tstatic dllfunction_t bcryptfuncs[] = {\r\n\t\t{(void**)&pBCryptVerifySignature,\t\t\"BCryptVerifySignature\"},\r\n\t\t{(void**)&pBCryptDestroyKey,\t\t\t\"BCryptDestroyKey\"},\r\n\t\t{NULL,NULL}\r\n\t};\r\n\r\n\tif (!crypt32)\r\n\t\tcrypt32 = Sys_LoadLibrary(\"crypt32.dll\", crypt32funcs);\r\n\tif (!bcrypt)\r\n\t\tbcrypt = Sys_LoadLibrary(\"bcrypt.dll\", bcryptfuncs);\r\n\tif (!crypt32 || !bcrypt)\r\n\t{\r\n\t\tCon_Printf(\"Unable to obtain required crypto functions\\n\");\r\n\t\treturn VH_UNSUPPORTED;\r\n\t}\r\n\r\n\tif (!pem)\r\n\t\treturn VH_AUTHORITY_UNKNOWN;\t//no public cert/key for authority.\r\n\tpem = strstr(pem, \"-----BEGIN CERTIFICATE-----\");\r\n\tif (!pem)\r\n\t\treturn VH_UNSUPPORTED;\t//not a pem\r\n\tpem += strlen(\"-----BEGIN CERTIFICATE-----\");\r\n\tpemend = strstr(pem, \"-----END CERTIFICATE-----\");\r\n\tif (!pemend)\r\n\t\treturn VH_UNSUPPORTED;\r\n\tdersize = Base64_DecodeBlock(pem, pemend, NULL, 0);\t//guess\r\n\tder = alloca(dersize);\r\n\tdersize = Base64_DecodeBlock(pem, pemend, der, dersize);\r\n\t//okay, now its in binary der format.\r\n\r\n\t//make sense of the cert and pull out its public key...\r\n\t{\r\n\t\tconst CERT_CONTEXT* cert = pCertCreateContext(CERT_STORE_CERTIFICATE_CONTEXT, X509_ASN_ENCODING, der, dersize, 0, NULL);\r\n\t\tif (!pCryptImportPublicKeyInfoEx2(X509_ASN_ENCODING, &cert->pCertInfo->SubjectPublicKeyInfo, 0, NULL, &pubkey))\r\n            return VH_UNSUPPORTED;\r\n        pCertFreeCertificateContext(cert);\r\n\t}\r\n\r\n\t//yay, now we can do what we actually wanted in the first place.\r\n\tstatus = pBCryptVerifySignature(pubkey, NULL, (qbyte*)hashdata, hashsize, (qbyte*)signdata, signsize, 0);\r\n\tpBCryptDestroyKey(pubkey);\r\n\tif (status == STATUS_SUCCESS)\r\n\t\treturn VH_CORRECT;\t\t//its okay\r\n\telse if (status == STATUS_INVALID_SIGNATURE)\r\n\t\treturn VH_INCORRECT;\t//its bad\r\n\treturn VH_UNSUPPORTED;\t\t//some weird transient error...?\r\n}\r\n#endif\r\n\r\nftecrypto_t crypto_sspi =\r\n{\r\n\t\"SChannel\",\r\n\tSSPI_OpenVFS,\r\n\tSSPI_GetChannelBinding,\r\n\tSSPI_DTLS_InitClient,\r\n\tSSPI_DTLS_InitServer,\r\n\tSSPI_VerifyHash,\r\n\tNULL,//SSPI_GenerateHash,\r\n};\r\n#endif\r\n"
  },
  {
    "path": "engine/common/net_wins.c",
    "content": "/*\nCopyright (C) 1996-1997 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n*/\n// net_wins.c\n#include \"quakedef.h\"\n#include \"netinc.h\"\n#include <stddef.h>\n\n#ifdef UNIXSOCKETS\n#include <sys/stat.h>\t//to delete the file/socket.\n#endif\n\nextern ftemanifest_t\t*fs_manifest;\n\n// Eww, eww. This is hacky but so is netinc.h, so bite me\n#ifdef _XBOX\n\tstruct sockaddr\n\t{\n\t\tshort  sa_family;\n\t};\n\n\t#define ntohs BigShort\n\t#define htons BigShort\n\t#define htonl BigLong\n\t#define ntohl BigLong\n#endif\n\n#if defined(_WIN32) || defined(__unix__) && !defined(ANDROID)\n\t#define USE_GETHOSTNAME_LOCALLISTING\n#endif\n\nnetadr_t\tnet_local_cl_ipadr;\t//still used to match local ui requests (quake/gamespy), and to generate ip reports for q3 servers (which is probably pointless).\n\nstruct ftenet_generic_connection_s *net_from_connection;\nnetadr_t\tnet_from;\nsizebuf_t\tnet_message;\n\n//#define\tMAX_UDP_PACKET\t(MAX_MSGLEN*2)\t// one more than msg + header\n#define\tMAX_UDP_PACKET\t8192\t// one more than msg + header\n//emscripten can misalign stuff, which is a problem when the leading int is checked directly in a few places. gah.\nFTE_ALIGN(4) qbyte\t\tnet_message_buffer[MAX_OVERALLMSGLEN];\n#if defined(_WIN32) && defined(HAVE_PACKET)\n\tWSADATA\t\twinsockdata;\n#endif\n\n#if defined(_WIN32)\n\t#define getaddrinfo pgetaddrinfo\n\t#define freeaddrinfo pfreeaddrinfo\n\t#define getnameinfo pgetnameinfo\n\n\tstatic int (WSAAPI *getaddrinfo) (\n\t  const char* nodename,\n\t  const char* servname,\n\t  const struct addrinfo* hints,\n\t  struct addrinfo** res\n\t);\n\tstatic void (WSAAPI *freeaddrinfo) (struct addrinfo*);\n\tstatic int (WSAAPI *getnameinfo) (const struct sockaddr *addr, socklen_t addrlen, char *host, socklen_t hostlen, char *serv, socklen_t servlen, int flags);\n#endif\n\n#if defined(HAVE_IPV4) && defined(HAVE_SERVER)\n\t#define HAVE_NATPMP\n#endif\n#if (defined(Q2CLIENT) || defined(Q2SERVER)) && (defined(HAVE_CLIENT) || defined(HAVE_SERVER)) && defined(HAVE_DTLS) && defined(HAVE_PACKET)\t//q2e's lobby/tunnel crap\n\t#define KEXLOBBY \"q2e\"\t//enables the feature, and defines the name of the sceme we use for it.\n#ifdef HAVE_SERVER\nstatic cvar_t\tnet_enable_kexlobby\t\t\t= CVARD(\"net_enable_\"KEXLOBBY,\t\t\t\"0\", \"If enabled, accept connection requests from the quake2-remaster engine on our listening udp ports.\\nNote that this defaults to disabled due to it being highly vulnerable to applification attacks.\");\n#endif\n#endif\n\n//#if !defined(HAVE_SERVER) && !defined(MASTERONLY)\n//\t#undef HAVE_HTTPSV\n//#endif\n\n#ifdef HAVE_EPOLL\nint epoll_fd = -1;\n#endif\n\nvoid NET_GetLocalAddress (int socket, netadr_t *out);\n//int TCP_OpenListenSocket (const char *localip, int port);\n#ifdef HAVE_IPV6\nint UDP6_OpenSocket (int port);\n#endif\n#ifdef HAVE_IPX\nvoid IPX_CloseSocket (int socket);\n#endif\ncvar_t\tnet_ice_broker\t\t\t= CVARFD(\"net_ice_broker\", \"tls://master.frag-net.com:27950\", CVAR_NOTFROMSERVER, \"This is the default broker we attempt to connect through when using 'sv_public /foo' or 'connect /foo'.\");\ncvar_t\ttimeout\t\t\t\t\t= CVARD(\"timeout\",\"65\", \"Connections will time out if no packets are received for this duration of time.\");\t\t// seconds without any message\ncvar_t\tnet_hybriddualstack\t\t= CVARD(\"net_hybriddualstack\",\t\t\"1\", \"Uses hybrid ipv4+ipv6 sockets where possible. Not supported on xp or below.\");\ncvar_t\tnet_fakeloss\t\t\t= CVARFD(\"net_fakeloss\",\t\t\t\"0\", CVAR_CHEAT, \"Simulates packetloss in both receiving and sending, on a scale from 0 to 1.\");\ncvar_t\tnet_fakemtu\t\t\t\t= CVARFD(\"net_fakemtu\",\t\t\t\t\"0\", CVAR_CHEAT, \"Cripples packet reception sizes.\");\nstatic cvar_t net_dns_ipv4\t\t= CVARD(\"net_dns_ipv4\",\t\t\t\t\"1\", \"If 0, disables dns resolution of names to ipv4 addresses (removing any associated error messages). Also hides ipv4 addresses in address:port listings.\");\nstatic cvar_t net_dns_ipv6\t\t= CVARD(\"net_dns_ipv6\",\t\t\t\t\"1\", \"If 0, disables dns resolution of names to ipv6 addresses (removing any associated error messages). Also hides ipv6 addresses in address:port listings.\");\ncvar_t\tnet_enabled\t\t\t\t= CVARD(\"net_enabled\",\t\t\t\t\"1\", \"If 0, disables all network access, including name resolution and socket creation. Does not affect loopback/internal connections.\");\n#if defined(HAVE_SSL)\ncvar_t\ttls_ignorecertificateerrors\t= CVARFD(\"tls_ignorecertificateerrors\", \"0\", CVAR_NOTFROMSERVER|CVAR_NOSAVE|CVAR_NOUNSAFEEXPAND|CVAR_NOSET, \"This should NEVER be set to 1!\");\nstatic void QDECL NET_TLS_Provider_Changed(struct cvar_s *var, char *oldvalue);\nstatic cvar_t\ttls_provider\t= CVARFCD(\"tls_provider\", \"\", CVAR_NOTFROMSERVER, NET_TLS_Provider_Changed, \"Controls which TLS provider to use.\");\n#endif\n#if defined(TCPCONNECT) && (defined(HAVE_SERVER) || defined(HAVE_HTTPSV))\n#ifdef HAVE_SERVER\ncvar_t\tnet_enable_qizmo\t\t= CVARD(\"net_enable_qizmo\",\t\t\t\"1\", \"Enables serverside support for 'connect tcp://foo' or 'connect tls://foo' (with net_enable_tls), as well as qizmo's tcp connections and compatibles.\");\n#endif\n#ifdef MVD_RECORDING\ncvar_t\tnet_enable_qtv\t\t\t= CVARD(\"net_enable_qtv\",\t\t\t\"2\", \"Controls whether inbound qtv requests will be honoured (both proxies and clients using qtvplay).\\n0: Don't accept qtv connections.\\n1: Accept connections.\\n2: Accept qtv connections only from host-scopped addresses (read: 127.*.*.*, ::1, or unix sockets).\");\n#endif\n#if defined(HAVE_SSL)\ncvar_t\tnet_enable_tls\t\t\t= CVARD(\"net_enable_tls\",\t\t\t\"1\", \"If enabled, binary data sent to a non-tls tcp port will be interpretted as a tls handshake (enabling https or wss over the same tcp port.\");\n#endif\n#ifdef HAVE_HTTPSV\n#ifdef SV_MASTER\nextern ftenet_connections_t *svm_sockets;\ncvar_t\tnet_enable_http\t\t\t= CVARD(\"net_enable_http\",\t\t\t\"1\", \"If enabled, tcp ports will accept inbound http clients, potentially serving large files which could distrupt gameplay (This does not affect outgoing http(s) requests).\");\ncvar_t\tnet_enable_rtcbroker\t= CVARD(\"net_enable_rtcbroker\",\t\t\"1\", \"If 1, tcp ports will accept websocket connections from clients trying to broker direct webrtc connections. This should be low traffic, but might involve a lot of mostly-idle connections.\");\ncvar_t\tnet_enable_websockets\t= CVARD(\"net_enable_websockets\",\t\"0\", \"If enabled, tcp ports will accept websocket game clients.\");\n#else\ncvar_t\tnet_enable_http\t\t\t= CVARD(\"net_enable_http\",\t\t\t\"0\", \"If enabled, tcp ports will accept inbound http clients, potentially serving large files which could distrupt gameplay (This does not affect outgoing http(s) requests).\");\ncvar_t\tnet_enable_rtcbroker\t= CVARD(\"net_enable_rtcbroker\",\t\t\"0\", \"If 1, tcp ports will accept websocket connections from clients trying to broker direct webrtc connections. This should be low traffic, but might involve a lot of mostly-idle connections.\");\ncvar_t\tnet_enable_websockets\t= CVARD(\"net_enable_websockets\",\t\"1\", \"If enabled, tcp ports will accept websocket game clients.\");\n#endif\n#endif\n#endif\n#if defined(HAVE_DTLS)\nstatic void QDECL NET_Enable_DTLS_Changed(struct cvar_s *var, char *oldvalue)\n{\n\tvar->ival = var->value;\n\t//set up the default value\n\tif (!*var->string)\n\t\tvar->ival = 1;\t//FIXME: change to 1 then 2 when better tested.\n\n#if defined(HAVE_SERVER)\n\tif (svs.sockets)\n\t{\n\t\tsvs.sockets->dtlsfuncs = (var->ival)?DTLS_InitServer():NULL;\n\t\tif (!svs.sockets->dtlsfuncs && var->ival >= 2)\n\t\t\tCon_Printf(\"%sUnable to set %s to \\\"%s\\\", no DTLS provider available.\\n\", (var->ival >= 2)?CON_ERROR:CON_WARNING, var->name, var->string);\n\t}\n\n\t{\n\t\tchar cert[8192];\n\t\tint certsize;\n\t\tchar digest[DIGEST_MAXSIZE];\n\t\tcertsize = (svs.sockets&&svs.sockets->dtlsfuncs)?svs.sockets->dtlsfuncs->GetPeerCertificate(NULL, QCERT_LOCALCERTIFICATE, cert, sizeof(cert)):-1;\n\t\tif (certsize > 0)\n\t\t\tInfoBuf_SetStarBlobKey(&svs.info, \"*fp\", cert, Base64_EncodeBlockURI(digest, CalcHash(&hash_certfp, digest, sizeof(digest), cert, certsize), cert, sizeof(cert)));\n\t\telse\n\t\t\tInfoBuf_SetStarKey(&svs.info, \"*fp\", \"\");\n\t}\n#endif\n}\ncvar_t net_enable_dtls\t\t= CVARAFCD(\"net_enable_dtls\", \"\", \"sv_listen_dtls\", 0, NET_Enable_DTLS_Changed, \"Controls serverside dtls support.\\n0: dtls blocked, not advertised.\\n1: clientside choice.\\n2: used where possible (recommended setting).\\n3: disallow non-dtls clients (sv_port_tcp should be eg tls://[::]:27500 to also disallow unencrypted tcp connections).\");\ncvar_t dtls_psk_hint\t\t= CVARFD(\"dtls_psk_hint\", \"\", CVAR_NOUNSAFEEXPAND, \"For DTLS-PSK handshakes. This specifies the public server identity.\");\ncvar_t dtls_psk_user\t\t= CVARFD(\"dtls_psk_user\", \"\", CVAR_NOUNSAFEEXPAND, \"For DTLS-PSK handshakes. This specifies the username to use when the client+server's hints match.\");\ncvar_t dtls_psk_key\t\t\t= CVARFD(\"dtls_psk_key\",  \"\", CVAR_NOUNSAFEEXPAND, \"For DTLS-PSK handshakes. This specifies the hexadecimal key which must match between client+server. Will only be used when client+server's hint settings match.\");\n#endif\n\n#ifdef HAVE_CLIENT\nstatic void QDECL cl_delay_packets_Announce(cvar_t *var, char *oldval)\n{\n\tif (cls.state >= ca_connected && cl.fpd & FPD_ANOUNCE_FAKE_LAG)\n\t\tCbuf_AddText(va(\"say Fake lag now %ims\\n\", var->ival), RESTRICT_LOCAL);\n}\nstatic cvar_t\tcl_delay_packets\t\t= CVARCD(\"cl_delay_packets\",\t\t\t\"0\", cl_delay_packets_Announce, \"Extra latency, in milliseconds.\");\n#endif\n\nextern cvar_t sv_public, sv_listen_qw, sv_listen_nq, sv_listen_dp;\n#ifdef QWOVERQ3\nextern cvar_t sv_listen_q3;\n#endif\n\n\n#ifdef HAVE_DTLS\nstatic neterr_t FTENET_DTLS_SendPacket(ftenet_connections_t *col, int length, const void *data, netadr_t *to);\n#endif\nstatic neterr_t NET_SendPacketCol (ftenet_connections_t *collection, int length, const void *data, netadr_t *to);\n\n\nstatic void\t\t*cryptolibmodule[cryptolib_count];\nftecrypto_t *cryptolib[cryptolib_count] =\n{\n\tNULL,\n\tNULL,\n\tNULL,\n\tNULL,\n#ifdef HAVE_GNUTLS\n\t&crypto_gnutls,\t//generally works best, but statically linked\n#endif\n#ifdef HAVE_WINSSPI\n\t&crypto_sspi,\t//generally unreliable\n#endif\n};\n#if defined(HAVE_SSL)\nstatic void NET_TLS_Provider_Changed(struct cvar_s *var, char *oldvalue)\n{\n\tint i;\n\tvar->ival = 0;\n\tif (!*var->string || !strcmp(var->string, \"0\"))\n\t\treturn;\n\tfor (i = 0; i < cryptolib_count; i++)\n\t{\n\t\tif (cryptolib[i] && !Q_strcasecmp(var->string, cryptolib[i]->drivername))\n\t\t\tvar->ival = i+1;\n\t}\n\tif (host_initialized && !var->ival)\n\t{\n\t\tint found = 0;\n\t\tfor (i = 0; i < cryptolib_count; i++)\n\t\t\tif (cryptolib[i])\n\t\t\t{\n\t\t\t\tif (!found++)\n\t\t\t\t\tCon_Printf(\"%s: \\\"%s\\\" not loaded, valid values are:\", var->name, var->string);\n\t\t\t\tCon_Printf(\" ^[%s\\\\type\\\\%s %s^]\", cryptolib[i]->drivername, var->name, cryptolib[i]->drivername);\n\t\t\t}\n\t\tif (!found)\n\t\t\tCon_Printf(\"%s: no tls plugins loaded\", var->name);\n\t\tCon_Printf(\"\\n\");\n\t}\n\n#if defined(HAVE_DTLS) && defined(HAVE_SERVER)\n\tif (net_enable_dtls.string)\t//might not be registered yet...\n\t\tCvar_ForceCallback(&net_enable_dtls);\t//note: any active connections will continue using their old contexts, but this will change the *fp serverinfo to the new provider's public key.\n#endif\n}\n#endif\nqboolean NET_RegisterCrypto(void *module, ftecrypto_t *driver)\n{\n\tint i;\n\tif (!driver)\n\t{\n\t\tfor (i = 0; i < cryptolib_count; i++)\n\t\t\tif (cryptolibmodule[i] == module)\n\t\t\t\tcryptolibmodule[i] = NULL, cryptolib[i] = NULL;\n#if defined(HAVE_SSL)\n\t\tCvar_ForceCallback(&tls_provider);\n#endif\n\t\treturn true;\n\t}\n\telse\n\t{\n\t\tfor (i = 0; i < cryptolib_count; i++)\n\t\t\tif (!cryptolib[i])\n\t\t\t{\n\t\t\t\tcryptolibmodule[i] = module, cryptolib[i] = driver;\n#if defined(HAVE_SSL)\n\t\t\t\tCvar_ForceCallback(&tls_provider);\n#endif\n\t\t\t\treturn true;\n\t\t\t}\n\t\treturn false;\n\t}\n}\n\n#ifdef _DEBUG\nstatic void TLS_Provider_Test_f(void)\n{\n\tint i;\n\tftecrypto_t *crypt;\n\n\tsize_t certsize, hashsize, sigsize;\n\tvoid *cert = Auth_GetKnownCertificate(\"Spike\", &certsize);\n\n\tchar rnd[512/8+64];\n\tchar sig[384+64];\n\n\thashsize = Base16_DecodeBlock(\"23edbbe92c4f54d1f0f2adf34de9e1bd9b9b1906c28b6d0e4a8a77205820add5bc8a36bd31c786be924a8f62f74dce736d439a311c6d74741af169e94405e8c3\", rnd,sizeof(rnd));\n\tsigsize = Base64_DecodeBlock(\"HkV0N+sfHvGywhqwl7SCu0xTSin0pnp00XOw98rH5lXpGfFab2Gg/TFZKW/DwLoqdS7xIfl7RRxoSIxUmVk4MaxWCn4SuauoB+hf7dA5QAJuuJhxciwHUviqAHfZPBWm65Za1DKYRxOLd4i6/XWAtVZLnv+DhVOSf68cAwCjnCGZf1lFCQhPx96jRqK57U0Oux+DVdCfQYSJAUv/TQrFRIUmrW0wBnt1xaG+AfXsnEY7vdFme+AFtUsPTy+I5HWFKgu/8UsRcLs3Bvd1mFYgmC76hp3oSl+u152CBSFBO7mc6UPYbCVI8QC5BxE76pT3MXLX1nZXIbzKviAT5UwKAUk2fI1ZHJo2gYvjf0OkZ1CSaPJElKAublMHMwrT9Pqk8nqYWfWtfbu2/hXPWq5Pm2szgB4nO32B1sqfWhwkdFTL6U0cyzzUaw1v5DviupGsDaNu/yad0Zq7xq6YlIpOcN+R9QXhu4uijhdUaHmEecO6su5TW9go7bCufwohScX4\", NULL, sig,sizeof(sig));\n\n\tfor (i = 0; i < cryptolib_count; i++)\n\t{\n\t\tcrypt = cryptolib[i];\n\t\tif (!crypt)\n\t\t\tcontinue;\n\n\t\tCon_Printf(\"Testing Provider %s%s:\\n\", crypt->drivername, (i==tls_provider.ival-1)?\"(forced)\":\"\");\n\n\t\tif (crypt->GenerateSignature)\n\t\t\tCon_Printf(S_COLOR_GREEN\"\tAble to sign\\n\");\n\t\telse\n\t\t\tCon_Printf(S_COLOR_YELLOW\"\tUnable to sign\\n\");\n\n//doesn't actually return the public certificate - the authority is kinda hardcoded right now. so don't bother testing it.\n//(only really needed for signing releases so it only working with gnutls isn't actually an issue)\n//\t\tif (crypt->GenerateSignature)\n//\t\t\tcrypt->GenerateSignature(rnd,sizeof(rnd), sig,sizeof(sig));\n\n\t\tswitch(crypt->VerifyHash?crypt->VerifyHash(rnd,hashsize, cert,certsize, sig,sigsize):VH_UNSUPPORTED)\n\t\t{\n\t\tcase VH_UNSUPPORTED:\t\tCon_Printf(S_COLOR_RED\t\"\tVH_UNSUPPORTED\\n\");\t\t\tbreak;\n\t\tcase VH_AUTHORITY_UNKNOWN:\tCon_Printf(S_COLOR_RED\t\"\tVH_AUTHORITY_UNKNOWN\\n\");\tbreak;\n\t\tcase VH_INCORRECT:\t\t\tCon_Printf(S_COLOR_RED\t\"\tVH_INCORRECT\\n\");\t\t\tbreak;\n\t\tcase VH_CORRECT:\t\t\tCon_Printf(S_COLOR_GREEN\"\tVH_CORRECT\\n\");\t\t\t\tbreak;\n\t\tdefault:\t\t\t\t\tCon_Printf(S_COLOR_RED\t\"\tVH_???\\n\");\t\t\t\t\tbreak;\n\t\t}\n\t}\n}\n#endif\n\n//=============================================================================\n\nint NetadrToSockadr (netadr_t *a, struct sockaddr_qstorage *s)\n{\n\tswitch(a->type)\n\t{\n#ifdef HAVE_WEBSOCKCL\n\tcase NA_WEBSOCKET:\n\t\tmemset (s, 0, sizeof(struct sockaddr_websocket));\n\t\t((struct sockaddr_websocket*)s)->sws_family = AF_WEBSOCK;\n\t\tmemcpy(((struct sockaddr_websocket*)s)->url, a->address.websocketurl, sizeof(((struct sockaddr_websocket*)s)->url));\n\t\treturn sizeof(struct sockaddr_websocket);\n#endif\n#ifdef HAVE_IPV4\n\tcase NA_IP:\n\t\tmemset (s, 0, sizeof(struct sockaddr_in));\n\t\t((struct sockaddr_in*)s)->sin_family = AF_INET;\n\n\t\t*(int *)&((struct sockaddr_in*)s)->sin_addr = *(int *)&a->address.ip;\n\t\t((struct sockaddr_in*)s)->sin_port = a->port;\n\t\treturn sizeof(struct sockaddr_in);\n#endif\n#ifdef HAVE_IPV6\n\tcase NA_IPV6:\n\t\tmemset (s, 0, sizeof(struct sockaddr_in6));\n\t\t((struct sockaddr_in6*)s)->sin6_family = AF_INET6;\n\n\t\tmemcpy(&((struct sockaddr_in6*)s)->sin6_addr, a->address.ip6, sizeof(struct in6_addr));\n\t\t((struct sockaddr_in6*)s)->sin6_port = a->port;\n\t\t((struct sockaddr_in6 *)s)->sin6_scope_id = a->scopeid;\n\t\treturn sizeof(struct sockaddr_in6);\n#endif\n#ifdef HAVE_IPX\n\tcase NA_IPX:\n#ifdef _WIN32\n\t\t((struct sockaddr_ipx *)s)->sa_family = AF_IPX;\n\t\tmemcpy(((struct sockaddr_ipx *)s)->sa_netnum, &a->address.ipx[0], 4);\n\t\tmemcpy(((struct sockaddr_ipx *)s)->sa_nodenum, &a->address.ipx[4], 6);\n\t\t((struct sockaddr_ipx *)s)->sa_socket = a->port;\n#else\n\t\t((struct sockaddr_ipx *)s)->sipx_family = AF_IPX;\n\t\tmemcpy(&((struct sockaddr_ipx *)s)->sipx_network, &a->address.ipx[0], 4);\n\t\tmemcpy(((struct sockaddr_ipx *)s)->sipx_node, &a->address.ipx[4], 6);\n\t\t((struct sockaddr_ipx *)s)->sipx_port = a->port;\n#endif\n\t\treturn sizeof(struct sockaddr_ipx);\n#endif\n#ifdef UNIXSOCKETS\n\tcase NA_UNIX:\n\t\t{\n\t\t\tstruct sockaddr_un *un = (struct sockaddr_un*)s;\n\t\t\tun->sun_family = AF_UNIX;\n\t\t\tmemcpy(un->sun_path, a->address.un.path, a->address.un.len);\n\t\t\treturn offsetof(struct sockaddr_un, sun_path) + a->address.un.len;\n\t\t}\n#endif\n\tdefault:\n\t\tSys_Error(\"NetadrToSockadr: Bad type %i\", a->type);\n\t\treturn 0;\n\t}\n}\n\nvoid SockadrToNetadr (struct sockaddr_qstorage *s, int sizeofsockaddr, netadr_t *a)\n{\n#ifndef HAVE_PACKET\n\tmemset(a, 0, sizeof(*a));\n\ta->type = NA_INVALID;\n#else\n\ta->scopeid = 0;\n\ta->connum = 0;\n\ta->prot = NP_DGRAM;\n\n\tif (sizeofsockaddr < offsetof(struct sockaddr, sa_family)+sizeof(((struct sockaddr*)s)->sa_family))\n\t{\t//truncated far too much...\n\t\tmemset(a, 0, sizeof(*a));\n\t\ta->type = NA_INVALID;\n\t\treturn;\n\t}\n\n\tswitch (((struct sockaddr*)s)->sa_family)\n\t{\n#ifdef HAVE_WEBSOCKCL\n\tcase AF_WEBSOCK:\n\t\ta->type = NA_WEBSOCKET;\n\t\tmemcpy(a->address.websocketurl, ((struct sockaddr_websocket*)s)->url, sizeof(a->address.websocketurl));\n\t\ta->port = 0;\n\t\tbreak;\n#endif\n#ifdef HAVE_IPV4\n\tcase AF_INET:\n\t\ta->type = NA_IP;\n\t\t*(int *)&a->address.ip = ((struct sockaddr_in *)s)->sin_addr.s_addr;\n\t\ta->port = ((struct sockaddr_in *)s)->sin_port;\n\t\tbreak;\n#endif\n#ifdef HAVE_IPV6\n\tcase AF_INET6:\n\t\ta->type = NA_IPV6;\n\t\tmemcpy(&a->address.ip6, &((struct sockaddr_in6 *)s)->sin6_addr, sizeof(a->address.ip6));\n\t\ta->port = ((struct sockaddr_in6 *)s)->sin6_port;\n\t\ta->scopeid = ((struct sockaddr_in6 *)s)->sin6_scope_id;\n\t\tbreak;\n#endif\n#ifdef HAVE_IPX\n\tcase AF_IPX:\n\t\ta->type = NA_IPX;\n\t\t*(int *)a->address.ip = 0xffffffff;\n#ifdef _WIN32\n\t\tmemcpy(&a->address.ipx[0], ((struct sockaddr_ipx *)s)->sa_netnum, 4);\n\t\tmemcpy(&a->address.ipx[4], ((struct sockaddr_ipx *)s)->sa_nodenum, 6);\n\t\ta->port = ((struct sockaddr_ipx *)s)->sa_socket;\n#else\n\t\tmemcpy(&a->address.ipx[0], &((struct sockaddr_ipx *)s)->sipx_network, 4);\n\t\tmemcpy(&a->address.ipx[4], ((struct sockaddr_ipx *)s)->sipx_node, 6);\n\t\ta->port = ((struct sockaddr_ipx *)s)->sipx_port;\n#endif\n\t\tbreak;\n#endif\n#ifdef UNIXSOCKETS\n\tcase AF_UNIX:\n\t\t{\n\t\t\tstruct sockaddr_un *un = (struct sockaddr_un*)s;\n\t\t\ta->type = NA_UNIX;\n\t\t\ta->address.un.len = sizeofsockaddr - offsetof(struct sockaddr_un, sun_path);\n\t\t\tmemcpy(a->address.un.path, un->sun_path, a->address.un.len);\n\t\t\tif (a->address.un.len && a->address.un.path)\n\t\t\t\ta->address.un.len = strnlen(a->address.un.path, a->address.un.len);\n\t\t\ta->port = 0;\n\t\t}\n\t\tbreak;\n#endif\n\tdefault:\n\t\tCon_Printf(\"SockadrToNetadr: bad socket family - %i\", ((struct sockaddr*)s)->sa_family);\n\tcase AF_UNSPEC:\n\t\tmemset(a, 0, sizeof(*a));\n\t\ta->type = NA_INVALID;\n\t\tbreak;\n\t}\n#endif\n}\nchar\t*NET_SockadrToString (char *s, int len, struct sockaddr_qstorage *a, size_t sizeofa)\n{\n\tnetadr_t na;\n\tSockadrToNetadr(a, sizeofa, &na);\n\treturn NET_AdrToString(s, len, &na);\n}\n\nqboolean NET_AddrIsReliable(netadr_t *adr)\t//hints that the protocol is reliable. if so, we don't need to wait for acks\n{\n\tswitch(adr->prot)\n\t{\n\tcase NP_DGRAM:\n\tcase NP_DTLS:\n\tcase NP_NATPMP:\n\tcase NP_RTC_TCP:\t//dtls makes it capable of reliability\n\tcase NP_RTC_TLS:\t//dtls makes it capable of reliability\n\tdefault:\n\t\treturn false;\n\tcase NP_KEXLAN: //provides its own reliability (at least for packets with the 'reliable' netchan flag set)\n\tcase NP_STREAM:\n\tcase NP_TLS:\n\tcase NP_WS:\n\tcase NP_WSS:\n\t\treturn true;\n\t}\n}\n\nqboolean\tNET_CompareAdr (netadr_t *a, netadr_t *b)\n{\n\tif (a->prot != b->prot)\n\t\treturn false;\n\n\tif (a->type != b->type)\n\t{\n\t\tif ((a->type == NA_INVALID || b->type == NA_INVALID) && (a->prot==NP_RTC_TCP||a->prot==NP_RTC_TLS)&&(b->prot==NP_RTC_TCP||b->prot==NP_RTC_TLS))\n\t\t\treturn true;\t//broker stuff can be written as /foo which doesn't necessarily have all the info.\n\t\tif (a->port != b->port)\n\t\t\treturn false;\n#if defined(HAVE_IPV4) && defined(HAVE_IPV6)\n\t\tif (a->type == NA_IP && b->type == NA_IPV6)\n\t\t{\n\t\t\tint i;\n\t\t\tfor (i = 0; i < 10; i++)\n\t\t\t\tif (b->address.ip6[i] != 0)\n\t\t\t\t\treturn false;\t//only matches if they're 0s, otherwise its not an ipv4 address there\n\t\t\tfor (; i < 12; i++)\n\t\t\t\tif (b->address.ip6[i] != 0xff)// && b->address.ip6[i] != 0x00)\t//0x00 is depricated\n\t\t\t\t\treturn false;\t//only matches if they're 0s or ffs, otherwise its not an ipv4 address there\n\t\t\tfor (i = 0; i < 4; i++)\n\t\t\t{\n\t\t\t\tif (a->address.ip[i] != b->address.ip6[12+i])\n\t\t\t\t\treturn false;\t//mask doesn't match\n\t\t\t}\n\t\t\treturn true;\t//its an ipv4 address in there, the mask matched the whole way through\n\t\t}\n\t\tif (a->type == NA_IPV6 && b->type == NA_IP)\n\t\t{\n\t\t\tint i;\n\t\t\tfor (i = 0; i < 10; i++)\n\t\t\t\tif (a->address.ip6[i] != 0)\n\t\t\t\t\treturn false;\t//only matches if they're 0s, otherwise its not an ipv4 address there\n\n\t\t\tfor (; i < 12; i++)\n\t\t\t\tif (a->address.ip6[i] != 0xff)// && a->address.ip6[i] != 0x00)\t//0x00 is depricated\n\t\t\t\t\treturn false;\t//only matches if they're 0s or ffs, otherwise its not an ipv4 address there\n\n\t\t\tfor (i = 0; i < 4; i++)\n\t\t\t{\n\t\t\t\tif (a->address.ip6[12+i] != b->address.ip[i])\n\t\t\t\t\treturn false;\t//mask doesn't match\n\t\t\t}\n\t\t\treturn true;\t//its an ipv4 address in there, the mask matched the whole way through\n\t\t}\n#endif\n\t\treturn false;\n\t}\n\n\tif (a->type == NA_LOOPBACK)\n\t\treturn true;\n\n#ifdef HAVE_WEBSOCKCL\n\tif (a->type == NA_WEBSOCKET)\n\t{\n\t\tif (!strcmp(a->address.websocketurl, b->address.websocketurl) && a->port == b->port)\n\t\t\treturn true;\n\t\treturn false;\n\t}\n#endif\n\n#ifdef HAVE_IPV4\n\tif (a->type == NA_IP)\n\t{\n\t\tif ((memcmp(a->address.ip, b->address.ip, sizeof(a->address.ip)) == 0) && a->port == b->port)\n\t\t\treturn true;\n\t\treturn false;\n\t}\n#endif\n\n#ifdef HAVE_IPV6\n\tif (a->type == NA_IPV6)\n\t{\n\t\tif ((memcmp(a->address.ip6, b->address.ip6, sizeof(a->address.ip6)) == 0) && a->port == b->port)\n\t\t\treturn true;\n\t\treturn false;\n\t}\n#endif\n\n#ifdef HAVE_IPX\n\tif (a->type == NA_IPX)\n\t{\n\t\tif ((memcmp(a->address.ipx, b->address.ipx, sizeof(a->address.ipx)) == 0) && a->port == b->port)\n\t\t\treturn true;\n\t\treturn false;\n\t}\n#endif\n\n#ifdef IRCCONNECT\n\tif (a->type == NA_IRC)\n\t{\n\t\tif (!strcmp(a->address.irc.user, b->address.irc.user))\n\t\t\treturn true;\n\t\treturn false;\n\t}\n#endif\n\n#ifdef UNIXSOCKETS\n\tif (a->type == NA_UNIX)\n\t{\n\t\tif (a->address.un.len == b->address.un.len && !memcmp(a->address.un.path, b->address.un.path, a->address.un.len))\n\t\t\treturn true;\n\t\treturn false;\n\t}\n#endif\n\n#ifdef SUPPORT_ICE\n\tif (a->type == NA_ICE)\n\t{\n\t\tif (a->port != b->port)\n\t\t\treturn false;\n\t\tif (!strcmp(a->address.icename, b->address.icename))\n\t\t\treturn true;\n\t\treturn false;\n\t}\n#endif\n\n\tif (a->type == NA_INVALID && a->prot)\n\t\treturn true;\t//mneh...\n\n\tCon_Printf(\"NET_CompareAdr: Bad address type\\n\");\n\treturn false;\n}\n\n/*\n===================\nNET_CompareBaseAdr\n\nCompares without the port\n(udp/tcp/etc must still match)\n===================\n*/\nqboolean\tNET_CompareBaseAdr (netadr_t *a, netadr_t *b)\n{\n\tif (a->prot != b->prot)\n\t\treturn false;\n\n\tif (a->type != b->type)\n\t{\n\t\tif ((a->type == NA_INVALID || b->type == NA_INVALID) && (a->prot==NP_RTC_TCP||a->prot==NP_RTC_TLS)&&(b->prot==NP_RTC_TCP||b->prot==NP_RTC_TLS))\n\t\t\treturn true;\t//broker stuff can be written as /foo which doesn't necessarily have all the info.\n#if defined(HAVE_IPV4) && defined(HAVE_IPV6)\n\t\tif (a->type == NA_IP && b->type == NA_IPV6)\n\t\t{\n\t\t\tint i;\n\t\t\tfor (i = 0; i < 10; i++)\n\t\t\t\tif (b->address.ip6[i] != 0)\n\t\t\t\t\treturn false;\t//only matches if they're 0s, otherwise its not an ipv4 address there\n\t\t\tfor (; i < 12; i++)\n\t\t\t\tif (b->address.ip6[i] != 0xff)// && b->address.ip6[i] != 0x00)\t//0x00 is depricated\n\t\t\t\t\treturn false;\t//only matches if they're 0s or ffs, otherwise its not an ipv4 address there\n\t\t\tfor (i = 0; i < 4; i++)\n\t\t\t{\n\t\t\t\tif (a->address.ip[i] != b->address.ip6[12+i])\n\t\t\t\t\treturn false;\t//mask doesn't match\n\t\t\t}\n\t\t\treturn true;\t//its an ipv4 address in there, the mask matched the whole way through\n\t\t}\n\t\tif (a->type == NA_IPV6 && b->type == NA_IP)\n\t\t{\n\t\t\tint i;\n\t\t\tfor (i = 0; i < 10; i++)\n\t\t\t\tif (a->address.ip6[i] != 0)\n\t\t\t\t\treturn false;\t//only matches if they're 0s, otherwise its not an ipv4 address there\n\n\t\t\tfor (; i < 12; i++)\n\t\t\t\tif (a->address.ip6[i] != 0xff)// && a->address.ip6[i] != 0x00)\t//0x00 is depricated\n\t\t\t\t\treturn false;\t//only matches if they're 0s or ffs, otherwise its not an ipv4 address there\n\n\t\t\tfor (i = 0; i < 4; i++)\n\t\t\t{\n\t\t\t\tif (a->address.ip6[12+i] != b->address.ip[i])\n\t\t\t\t\treturn false;\t//mask doesn't match\n\t\t\t}\n\t\t\treturn true;\t//its an ipv4 address in there, the mask matched the whole way through\n\t\t}\n#endif\n\t\treturn false;\n\t}\n\n\tif (a->type == NA_LOOPBACK)\n\t\treturn true;\n\n#ifdef HAVE_IPV4\n\tif (a->type == NA_IP)\n\t{\n\t\tif ((memcmp(a->address.ip, b->address.ip, sizeof(a->address.ip)) == 0))\n\t\t\treturn true;\n\t\treturn false;\n\t}\n#endif\n#ifdef HAVE_IPV6\n\tif (a->type == NA_IPV6)\n\t{\n\t\tif ((memcmp(a->address.ip6, b->address.ip6, 16) == 0))\n\t\t\treturn true;\n\t\treturn false;\n\t}\n#endif\n#ifdef HAVE_IPX\n\tif (a->type == NA_IPX)\n\t{\n\t\tif ((memcmp(a->address.ipx, b->address.ipx, 10) == 0))\n\t\t\treturn true;\n\t\treturn false;\n\t}\n#endif\n#ifdef IRCCONNECT\n\tif (a->type == NA_IRC)\n\t{\n\t\tif (!strcmp(a->address.irc.user, b->address.irc.user))\n\t\t\treturn true;\n\t\treturn false;\n\t}\n#endif\n\n#ifdef HAVE_WEBSOCKCL\n\tif (a->type == NA_WEBSOCKET)\n\t{\n\t\tif (!strcmp(a->address.websocketurl, b->address.websocketurl))\n\t\t\treturn true;\n\t\treturn false;\n\t}\n#endif\n\n#ifdef UNIXSOCKETS\n\tif (a->type == NA_UNIX)\n\t{\n\t\tif (a->address.un.len == b->address.un.len && !memcmp(a->address.un.path, b->address.un.path, a->address.un.len))\n\t\t\treturn true;\n\t\treturn false;\n\t}\n#endif\n\n#ifdef SUPPORT_ICE\n\tif (a->type == NA_ICE)\n\t{\n\t\tif (!strcmp(a->address.icename, b->address.icename))\n\t\t\treturn true;\n\t\treturn false;\n\t}\n#endif\n\n\tif (a->type == NA_INVALID && a->prot)\n\t\treturn true;\t//mneh...\n\n\tSys_Error(\"NET_CompareBaseAdr: Bad address type\");\n\treturn false;\n}\n\nqboolean NET_AddressSmellsFunny(netadr_t *a)\n{\n#ifdef HAVE_IPV6\n\tint i;\n#endif\n\n\t//rejects certain blacklisted addresses\n\tswitch(a->type)\n\t{\n#ifdef HAVE_IPV4\n\tcase NA_IP:\n\t\t//reject localhost\n\t\tif (a->address.ip[0] == 127)// && a->address.ip[1] == 0   && a->address.ip[2] == 0   && a->address.ip[3] == 1  )\n\t\t\treturn true;\n\t\t//'this' network (not an issue, but lets reject it anyway)\n\t\tif (a->address.ip[0] == 0   && a->address.ip[1] == 0   && a->address.ip[2] == 0   && a->address.ip[3] == 0  )\n\t\t\treturn true;\n\t\t//reject any broadcasts\n\t\tif (a->address.ip[0] == 255 && a->address.ip[1] == 255 && a->address.ip[2] == 255 && a->address.ip[3] == 0  )\n\t\t\treturn true;\n\t\t//not much else I can reject\n\t\treturn false;\n#endif\n\n#ifdef HAVE_IPV6\n\tcase NA_IPV6:\n\t\t//reject [::XXXX] (this includes obsolete ipv4-compatible (not ipv4 mapped), and localhost)\n\t\tfor (i = 0; i < 12; i++)\n\t\t\tif (a->address.ip6[i])\n\t\t\t\tbreak;\n\t\tif (i == 12)\n\t\t\treturn true;\n\t\treturn false;\n#endif\n\n#ifdef HAVE_IPX\n\t//no idea how this protocol's addresses work\n\tcase NA_IPX:\n\t\treturn false;\n#endif\n\n\tcase NA_LOOPBACK:\n\t\treturn false;\n\n\tdefault:\n\t\treturn true;\n\t}\n}\n\n#if (_POSIX_C_SOURCE >= 200112L || defined(getnameinfo)) && defined(HAVE_PACKET)\nstatic void NET_AdrToStringDoResolve(void *ctx, void *data, size_t a, size_t b)\n{\n\tnetadr_t *n = data;\n\tstruct sockaddr_qstorage s;\n\tint ssz;\n\tchar *adrstring = Z_Malloc(NI_MAXHOST);\n\tvoid (*resolved)(void *ctx, void *data, size_t a, size_t b) = *(void**)(n+1);\n\tif (n->type == NA_LOOPBACK)\n\t\tNET_BaseAdrToString(adrstring, NI_MAXHOST, n);\n\telse\n\t{\n\t\tssz = NetadrToSockadr(n, &s);\n\t\tif (\n\t\t#ifdef getnameinfo\n\t\t\t!getnameinfo ||\n\t\t#endif\n\t\t\tgetnameinfo((struct sockaddr *)&s, ssz, adrstring, NI_MAXHOST, NULL, 0, NI_NUMERICSERV|NI_DGRAM))\n\t\t{\n\t\t\tNET_BaseAdrToString(adrstring, NI_MAXHOST, n);\n\t\t}\n\t}\n\tCOM_AddWork(WG_MAIN, resolved, ctx, adrstring, a, b);\n\tZ_Free(n);\n}\n\nvoid NET_AdrToStringResolve (netadr_t *adr, void (*resolved)(void *ctx, void *data, size_t a, size_t b), void *ctx, size_t a, size_t b)\n{\n\tnetadr_t *n = Z_Malloc(sizeof(*n) + sizeof(void*));\n\t*n = *adr;\n\t*(void**)(n+1) = resolved;\n\tCOM_AddWork(WG_LOADER, NET_AdrToStringDoResolve, ctx, n, a, b);\n}\n#else\nvoid NET_AdrToStringResolve (netadr_t *adr, void (*resolved)(void *ctx, void *data, size_t a, size_t b), void *ctx, size_t a, size_t b)\n{\n\tchar adrstring[512];\n\tNET_BaseAdrToString(adrstring, countof(adrstring), adr);\n\tresolved(ctx, Z_StrDup(adrstring), a, b);\n}\n#endif\n\nchar\t*NET_AdrToString (char *s, int len, netadr_t *a)\n{\n\tchar *rs = s;\n\tchar *prot = \"\";\n#ifdef HAVE_IPV6\n\tint doneblank;\n#endif\n\n\tswitch(a->prot)\n\t{\n\tcase NP_INVALID:prot = \"invalid://\";break;\n\tcase NP_KEXLAN:\n#ifdef KEXLOBBY\n\t\tprot = KEXLOBBY\"://\";\n#endif\n\t\tbreak;\n\tcase NP_DGRAM:\tprot = \"\"/*qw://*/;\tbreak;\n\tcase NP_DTLS:\tprot = \"dtls://\";\tbreak;\n\tcase NP_STREAM:\tprot = \"tcp://\";\tbreak;\t//not strictly true for ipx, but whatever.\n\tcase NP_TLS:\tprot = \"tls://\";\tbreak;\n\tcase NP_WS:\t\tprot = \"ws://\";\t\tbreak;\n\tcase NP_WSS:\tprot = \"wss://\";\tbreak;\n\tcase NP_NATPMP:\tprot = \"natpmp://\";\tbreak;\n\tcase NP_RTC_TCP:prot = \"rtc://\";\tbreak;\n\tcase NP_RTC_TLS:prot = \"rtcs://\";\tbreak;\n\t}\n\n\tswitch(a->type)\n\t{\n#ifdef HAVE_WEBSOCKCL\n\tcase NA_WEBSOCKET:\t//ws / wss is part of the url\n\t\t{\n\t\t\tchar *url = a->address.websocketurl;\n\t\t\tprot = \"\";\n\t\t\tif (a->port)\n\t\t\t\tQ_snprintfz(s, len, \"%s%s#%i\", prot, url, ntohs(a->port));\n\t\t\telse\n\t\t\t\tQ_snprintfz(s, len, \"%s%s\", prot, url);\n\t\t}\n\t\tbreak;\n#endif\n#ifdef HAVE_IPV4\n\tcase NA_IP:\n\t\tif (a->port)\n\t\t{\n\t\t\tQ_snprintfz(s, len, \"%s%i.%i.%i.%i:%i\",\n\t\t\t\tprot,\n\t\t\t\ta->address.ip[0],\n\t\t\t\ta->address.ip[1],\n\t\t\t\ta->address.ip[2],\n\t\t\t\ta->address.ip[3],\n\t\t\t\tntohs(a->port));\n\t\t}\n\t\telse\n\t\t{\n\t\t\tsnprintf (s, len, \"%s%i.%i.%i.%i\",\n\t\t\t\tprot,\n\t\t\t\ta->address.ip[0],\n\t\t\t\ta->address.ip[1],\n\t\t\t\ta->address.ip[2],\n\t\t\t\ta->address.ip[3]);\n\t\t}\n\t\tbreak;\n#endif\n#ifdef HAVE_IPV6\n\tcase NA_IPV6:\n\t\t{\n\t\t\tchar *p;\n\t\t\tint i;\n\t\t\tif (!*(int*)&a->address.ip6[0] &&\n\t\t\t\t!*(int*)&a->address.ip6[4] &&\n\t\t\t\t!*(short*)&a->address.ip6[8] &&\n\t\t\t\t*(short*)&a->address.ip6[10] == (short)0xffff)\n\t\t\t{\n\t\t\t\tif (a->port)\n\t\t\t\t\tsnprintf (s, len, \"%s%i.%i.%i.%i:%i\",\n\t\t\t\t\t\tprot,\n\t\t\t\t\t\ta->address.ip6[12],\n\t\t\t\t\t\ta->address.ip6[13],\n\t\t\t\t\t\ta->address.ip6[14],\n\t\t\t\t\t\ta->address.ip6[15],\n\t\t\t\t\t\tntohs(a->port));\n\t\t\t\telse\n\t\t\t\t\tsnprintf (s, len, \"%s%i.%i.%i.%i\",\n\t\t\t\t\t\tprot,\n\t\t\t\t\t\ta->address.ip6[12],\n\t\t\t\t\t\ta->address.ip6[13],\n\t\t\t\t\t\ta->address.ip6[14],\n\t\t\t\t\t\ta->address.ip6[15]);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\t*s = 0;\n\t\t\tdoneblank = false;\n\t\t\tp = s;\n\t\t\tif (a->port)\n\t\t\t\tsnprintf (s, len-strlen(s), \"%s[\", prot);\n\t\t\telse\n\t\t\t\tsnprintf (s, len-strlen(s), \"%s\", prot);\n\t\t\tp += strlen(p);\n\n\t\t\tfor (i = 0; i < 16; i+=2)\n\t\t\t{\n\t\t\t\tif (doneblank!=true && a->address.ip6[i] == 0 && a->address.ip6[i+1] == 0)\n\t\t\t\t{\n\t\t\t\t\tif (!doneblank)\n\t\t\t\t\t{\n\t\t\t\t\t\tsnprintf (p, len-strlen(s), \"::\");\n\t\t\t\t\t\tp += strlen(p);\n\t\t\t\t\t\tdoneblank = 2;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tif (doneblank==2)\n\t\t\t\t\t\tdoneblank = true;\n\t\t\t\t\telse if (i != 0)\n\t\t\t\t\t{\n\t\t\t\t\t\tsnprintf (p, len-strlen(s), \":\");\n\t\t\t\t\t\tp += strlen(p);\n\t\t\t\t\t}\n\t\t\t\t\tif (a->address.ip6[i+0])\n\t\t\t\t\t{\n\t\t\t\t\t\tsnprintf (p, len-strlen(s), \"%x%02x\",\n\t\t\t\t\t\t\ta->address.ip6[i+0],\n\t\t\t\t\t\t\ta->address.ip6[i+1]);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tsnprintf (p, len-strlen(s), \"%x\",\n\t\t\t\t\t\t\ta->address.ip6[i+1]);\n\t\t\t\t\t}\n\t\t\t\t\tp += strlen(p);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (a->scopeid)\n\t\t\t{\n\t\t\t\tsnprintf (p, len-strlen(s), \"%%%u\",\n\t\t\t\t\ta->scopeid);\n\t\t\t\tp += strlen(p);\n\t\t\t}\n\n\t\t\tif (a->port)\n\t\t\t\tsnprintf (p, len-strlen(s), \"]:%i\",\n\t\t\t\t\tntohs(a->port));\n\t\t}\n\t\tbreak;\n#endif\n#ifdef HAVE_IPX\n\tcase NA_IPX:\n\t\tsnprintf (s, len, \"%s%02x%02x%02x%02x:%02x%02x%02x%02x%02x%02x:%i\",\n\t\t\tprot,\n\t\t\ta->address.ipx[0],\n\t\t\ta->address.ipx[1],\n\t\t\ta->address.ipx[2],\n\t\t\ta->address.ipx[3],\n\t\t\ta->address.ipx[4],\n\t\t\ta->address.ipx[5],\n\t\t\ta->address.ipx[6],\n\t\t\ta->address.ipx[7],\n\t\t\ta->address.ipx[8],\n\t\t\ta->address.ipx[9],\n\t\t\tntohs(a->port));\n\t\tbreak;\n#endif\n\tcase NA_LOOPBACK:\n\t\tsnprintf (s, len, \"%sQLoopBack:%i\", prot, a->port);\n\t\tbreak;\n\n#ifdef IRCCONNECT\n\tcase NA_IRC:\n\t\tif (*a->address.irc.channel)\n\t\t\tsnprintf (s, len, \"irc://%s@%s\", a->address.irc.user, a->address.irc.channel);\n\t\telse\n\t\t\tsnprintf (s, len, \"irc://%s\", a->address.irc.user);\n\t\tbreak;\n#endif\n\n#ifdef UNIXSOCKETS\n\tcase NA_UNIX:\n\t\tswitch(a->prot)\n\t\t{\n\t\tcase NP_DGRAM:\tprot = \"udg://\";\tbreak;\n\t\tcase NP_STREAM:\tprot = \"unix://\";\tbreak;\n\t\tdefault:\n\t\t\tsnprintf (s, len, \"unix+\");\n\t\t\tlen-=strlen(s);\n\t\t\ts+=strlen(s);\n\t\t\tbreak;\n\t\t}\n\t\tsnprintf (s, len, prot);\n\t\tlen-=strlen(s);\n\t\ts+=strlen(s);\n\n\t\tif (len)\t//hopefully this will always be true...\n\t\t{\n\t\t\tchar *end = a->address.un.path+a->address.un.len, *in;\n\t\t\tchar c;\n\t\t\tfor (in = a->address.un.path; in < end; in++)\n\t\t\t{\n\t\t\t\tif (--len == 0)\n\t\t\t\t\tbreak;\n\t\t\t\tif (*in == '\\\\')\t\t//ugly encoding\n\t\t\t\t\tc = '\\\\';\n\t\t\t\telse if (*in == '\\0')\t//null chars are always a problem. abstract sockets generally get them displayed using @ chars.\n\t\t\t\t{\n\t\t\t\t\t*s++ = '@';\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\telse if (*in == '@')\t//which means actual @ chars need to be escaped\n\t\t\t\t\tc = '@';\n\t\t\t\t//don't screw up from these, either.\n\t\t\t\telse if (*in == '\\n')\n\t\t\t\t\tc = 'n';\n\t\t\t\telse if (*in == '\\r')\n\t\t\t\t\tc = 'r';\n\t\t\t\telse if (*in == '\\t')\n\t\t\t\t\tc = 't';\n\t\t\t\t//special quake chars can screw up display too\n\t\t\t\telse if (*in == '\\1')\n\t\t\t\t\tc = '1';\n\t\t\t\telse if (*in == '\\2')\n\t\t\t\t\tc = '2';\n\t\t\t\telse if (*in == '\\3')\n\t\t\t\t\tc = '3';\n\t\t\t\telse\n\t\t\t\t{\t//as-is.\n\t\t\t\t\t*s++ = *in;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\t//marked up chars need extra storage.\n\t\t\t\tif (--len == 0)\n\t\t\t\t\tbreak;\n\t\t\t\t*s++ = '\\\\';\n\t\t\t\t*s++ = c;\n\t\t\t}\n\t\t\t*s = 0;\t//and always null terminate the string.\n\t\t}\n\t\tbreak;\n#endif\n\n#ifdef SUPPORT_ICE\n\tcase NA_ICE:\n\t\tsnprintf (s, len, \"%s[%s]\", prot, a->address.icename);\n\t\tbreak;\n#endif\n\n\tdefault:\n\t\tif (a->prot == NP_RTC_TCP || a->prot == NP_RTC_TLS)\n\t\t\tQ_strncpyz(s, prot, len);\n\t\telse\n\t\t\tsnprintf (s, len, \"invalid netadr_t type\");\n//\t\tSys_Error(\"NET_AdrToString: Bad netadr_t type\");\n\t}\n\n\treturn rs;\n}\n\nchar\t*NET_BaseAdrToString (char *s, int len, netadr_t *a)\n{\n\tchar *prot = \"\";\n\tswitch(a->prot)\n\t{\n\tcase NP_INVALID:prot = \"invalid://\";break;\n\tcase NP_DGRAM:\tprot = \"\"/*qw://*/;\tbreak;\n\tcase NP_DTLS:\tprot = \"dtls://\";\tbreak;\n\tcase NP_KEXLAN:\n#ifdef KEXLOBBY\n\t\t\t\t\tprot = KEXLOBBY\"://\";\n#endif\n\t\tbreak;\n\tcase NP_STREAM:\tprot = \"tcp://\";\tbreak;\t//not strictly true for ipx, but whatever.\n\tcase NP_TLS:\tprot = \"tls://\";\tbreak;\n\tcase NP_WS:\t\tprot = \"ws://\";\t\tbreak;\n\tcase NP_WSS:\tprot = \"wss://\";\tbreak;\n\tcase NP_NATPMP:\tprot = \"natpmp://\";\tbreak;\n\tcase NP_RTC_TCP:prot = \"rtc://\";\tbreak;\n\tcase NP_RTC_TLS:prot = \"rtcs://\";\tbreak;\n\t}\n\n\tswitch(a->type)\n\t{\n#ifdef HAVE_WEBSOCKCL\n\tcase NA_WEBSOCKET:\t//ws / wss is part of the url\n\t\t{\n\t\t\tchar *url = a->address.websocketurl;\n\t\t\tQ_snprintfz(s, len, \"%s%s\", prot, url);\n\t\t}\n\t\tbreak;\n#endif\n\n\tcase NA_IP:\n\t\tsnprintf (s, len, \"%s%i.%i.%i.%i\",\n\t\t\tprot,\n\t\t\ta->address.ip[0],\n\t\t\ta->address.ip[1],\n\t\t\ta->address.ip[2],\n\t\t\ta->address.ip[3]);\n\t\tbreak;\n#ifdef HAVE_IPV6\n\tcase NA_IPV6:\n\t\t{\n\t\t\tchar *p;\n\t\t\tint i, doneblank;\n\t\t\tif (!*(int*)&a->address.ip6[0] &&\n\t\t\t\t!*(int*)&a->address.ip6[4] &&\n\t\t\t\t!*(short*)&a->address.ip6[8] &&\n\t\t\t\t*(short*)&a->address.ip6[10] == (short)0xffff)\n\t\t\t{\n\t\t\t\tsnprintf (s, len, \"%s%i.%i.%i.%i\",\n\t\t\t\t\tprot,\n\t\t\t\t\ta->address.ip6[12],\n\t\t\t\t\ta->address.ip6[13],\n\t\t\t\t\ta->address.ip6[14],\n\t\t\t\t\ta->address.ip6[15]);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\t*s = 0;\n\t\t\tdoneblank = false;\n\t\t\tp = s;\n\t\t\tfor (i = 0; i < 16; i+=2)\n\t\t\t{\n\t\t\t\tif (doneblank!=true && a->address.ip6[i] == 0 && a->address.ip6[i+1] == 0)\n\t\t\t\t{\n\t\t\t\t\tif (!doneblank)\n\t\t\t\t\t{\n\t\t\t\t\t\tsnprintf (p, len-strlen(s), \"::\");\n\t\t\t\t\t\tp += strlen(p);\n\t\t\t\t\t\tdoneblank = 2;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tif (doneblank==2)\n\t\t\t\t\t\tdoneblank = true;\n\t\t\t\t\telse if (i != 0)\n\t\t\t\t\t{\n\t\t\t\t\t\tsnprintf (p, len-strlen(s), \":\");\n\t\t\t\t\t\tp += strlen(p);\n\t\t\t\t\t}\n\t\t\t\t\tif (a->address.ip6[i+0])\n\t\t\t\t\t{\n\t\t\t\t\t\tsnprintf (p, len-strlen(s), \"%x%02x\",\n\t\t\t\t\t\t\ta->address.ip6[i+0],\n\t\t\t\t\t\t\ta->address.ip6[i+1]);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tsnprintf (p, len-strlen(s), \"%x\",\n\t\t\t\t\t\t\ta->address.ip6[i+1]);\n\t\t\t\t\t}\n\t\t\t\t\tp += strlen(p);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tbreak;\n#endif\n#ifdef HAVE_IPX\n\tcase NA_IPX:\n\t\tsnprintf (s, len, \"%s%02x%02x%02x%02x:%02x%02x%02x%02x%02x%02x\",\n\t\t\tprot,\n\t\t\ta->address.ipx[0],\n\t\t\ta->address.ipx[1],\n\t\t\ta->address.ipx[2],\n\t\t\ta->address.ipx[3],\n\t\t\ta->address.ipx[4],\n\t\t\ta->address.ipx[5],\n\t\t\ta->address.ipx[6],\n\t\t\ta->address.ipx[7],\n\t\t\ta->address.ipx[8],\n\t\t\ta->address.ipx[9]);\n\t\tbreak;\n#endif\n\tcase NA_LOOPBACK:\n\t\tsnprintf (s, len, \"%sQLoopBack\", prot);\n\t\tbreak;\n\n#ifdef IRCCONNECT\n\tcase NA_IRC:\n\t\tNET_AdrToString(s, len, a);\n\t\tbreak;\n#endif\n\n#ifdef UNIXSOCKETS\n\tcase NA_UNIX:\n\t\t//no ports, so no base paths.\n\t\treturn NET_AdrToString(s, len, a);\n#endif\n\n#ifdef SUPPORT_ICE\n\tcase NA_ICE:\n\t\tsnprintf (s, len, \"%s[%s]\", prot, a->address.icename);\n\t\tbreak;\n#endif\n\n\tdefault:\n\t\tif (a->prot == NP_RTC_TCP || a->prot == NP_RTC_TLS)\n\t\t\tQ_strncpyz(s, prot, len);\n\t\telse\n\t\t\tSys_Error(\"NET_BaseAdrToString: Bad netadr_t type\");\n\t}\n\n\treturn s;\n}\n\n/*\n=============\nNET_StringToAdr\n\nidnewt\nidnewt:28000\n192.246.40.70\n192.246.40.70:28000\nany form of ipv6, including port number.\n=============\n*/\nsize_t NET_StringToSockaddr2 (const char *s, int defaultport, netadrtype_t afhint, struct sockaddr_qstorage *sadr, int *addrfamily, int *addrsize, size_t addresses)\n{\n\tsize_t\tresult = 0;\n\n\tif (!(*s) || !addresses)\n\t\treturn result;\n\n#ifdef WEBCLIENT\n\t//EVIL HACK!\n\t//updates.tth uses a known self-signed certificate (to protect against dns hijacks like fteqw.com suffered).\n\t//its not meant to be used for browsers etc, and I cba to register dns stuff for it.\n\t//besides, browsers/etc would just bitch about its cert, so w/e.\n\t//redirect the dns to the base host without affecting http(s) hosts/certificates.\n\tif (!strncmp(s, \"updates.\", 8))\n\t{\n\t\tconchar_t musite[256], *e;\n\t\tchar site[256];\n\t\tchar *oldprefix = \"http://fte.\";\n\t\te = COM_ParseFunString(CON_WHITEMASK, ENGINEWEBSITE, musite, sizeof(musite), false);\n\t\tCOM_DeFunString(musite, e, site, sizeof(site), true, true);\n\t\tif (!strncmp(site, oldprefix, strlen(oldprefix)))\n\t\t{\n\t\t\tif (!strcmp(s+8,site+strlen(oldprefix)))\n\t\t\t{\n#ifdef HAVE_IPV4\n\t\t\t\tstruct sockaddr_in *a = (struct sockaddr_in*)sadr;\n\t\t\t\tqbyte *ip = (qbyte*)&a->sin_addr;\n\t\t\t\tmemset (a, 0, sizeof(*sadr));\n\t\t\t\ta->sin_family = AF_INET;\n\t\t\t\tip[0] = 213;\n\t\t\t\tip[1] = 219;\n\t\t\t\tip[2] = 36;\n\t\t\t\tip[3] = 248;\n\t\t\t\ta->sin_port = htons(defaultport);\n\n\t\t\t\tif (addrsize)\n\t\t\t\t\t*addrsize = sizeof(*a);\n\t\t\t\tif (addrfamily)\n\t\t\t\t\t*addrfamily = AF_INET;\n\t\t\t\treturn 1;\n#else\n\t\t\t\ts += 8;\n#endif\n\t\t\t}\n\t\t}\n\t}\n#endif\n\n\tmemset (sadr, 0, sizeof(*sadr));\n\n#ifdef UNIXSOCKETS\n\tif (afhint == NA_UNIX)\n\t{\n\t\tstruct sockaddr_un *sa = (struct sockaddr_un *)sadr;\n\t\tint i;\n\n\t\t//limit to known prefixes. this allows for sandboxing.\n\t\tconst char *allowedprefixes[] = {\"@\"DISTRIBUTION, \"/tmp/\"DISTRIBUTION\".\", \"/tmp/qsock.\", \"@FTE\", \"@qtv\", \"@qsock\"};\n\t\tfor (i = 0; i < countof(allowedprefixes); i++)\n\t\t{\n\t\t\tif (!Q_strncasecmp(s, allowedprefixes[i], strlen(allowedprefixes[i])))\n\t\t\t\tbreak;\n\t\t}\n\t\tif (i == countof(allowedprefixes))\n\t\t{\n\t\t\tCon_DPrintf(CON_WARNING \"\\\"%s\\\" is not an accepted prefix for a unix socket. Forcing prefix.\\n\", s);\n\t\t\ti = 0;\n\t\t\tsa->sun_path[i++] = 0;\n\t\t\tsa->sun_path[i++] = 'q';\n\t\t\tsa->sun_path[i++] = 's';\n\t\t\tsa->sun_path[i++] = 'o';\n\t\t\tsa->sun_path[i++] = 'c';\n\t\t\tsa->sun_path[i++] = 'k';\n\t\t}\n\t\telse i = 0;\n\n\t\tsa->sun_family = AF_UNIX;\n\n\t\t//this parsing is so annoying because I want to support abstract sockets too, which have nulls.\n\t\t//we're using @ charsto represent nulls, to match 'lsof -U'\n\t\tfor ( ; *s && i < countof(sa->sun_path); )\n\t\t{\n\t\t\tif (*s == '@')\n\t\t\t{\n\t\t\t\tsa->sun_path[i++] = 0;\n\t\t\t\ts++;\n\t\t\t}\n\t\t\telse if (*s == '\\\\')\n\t\t\t{\n\t\t\t\tif (s[1] == 0)\n\t\t\t\t{\n\t\t\t\t\tsa->sun_path[i++] = '\\\\';\n\t\t\t\t\tbreak;\t//error.\n\t\t\t\t}\n\t\t\t\telse if (s[1] == '\\\\')\n\t\t\t\t\tsa->sun_path[i++] = '\\\\';\n\t\t\t\telse if (s[1] == '@')\n\t\t\t\t\tsa->sun_path[i++] = '@';\n\t\t\t\telse if (s[1] == 'n')\n\t\t\t\t\tsa->sun_path[i++] = '\\n';\n\t\t\t\telse if (s[1] == 'r')\n\t\t\t\t\tsa->sun_path[i++] = '\\r';\n\t\t\t\telse if (s[1] == 't')\n\t\t\t\t\tsa->sun_path[i++] = '\\t';\n\t\t\t\telse if (s[1] == '1')\n\t\t\t\t\tsa->sun_path[i++] = '\\1';\n\t\t\t\telse if (s[1] == '2')\n\t\t\t\t\tsa->sun_path[i++] = '\\2';\n\t\t\t\telse if (s[1] == '3')\n\t\t\t\t\tsa->sun_path[i++] = '\\3';\n\t\t\t\telse\n\t\t\t\t\tsa->sun_path[i++] = '?';\n\t\t\t\ts+=2;\n\t\t\t}\n\t\t\telse\n\t\t\t\t sa->sun_path[i++] = *s++;\n\t\t}\n\t\tif (sa->sun_path[0])\t//'pathname sockets should be null terminated'\n\t\t\tsa->sun_path[i++] = 0;\n\t\tif (i < countof(sa->sun_path))\n\t\t\tsa->sun_path[i] = 'X';\n\t\tif (addrsize)\n\t\t\t*addrsize = offsetof(struct sockaddr_un, sun_path) + i;\n\t\tif (addrfamily)\n\t\t\t*addrfamily = AF_UNIX;\n\t\tresult++;\n\t}\n\telse\n#endif\n#ifdef HAVE_IPX\n\tif ((strlen(s) >= 23) && (s[8] == ':') && (s[21] == ':'))\t// check for an IPX address\n\t{\n\t\tunsigned int val;\n\t\tchar\tcopy[128];\n\n\n\t\t((struct sockaddr_ipx *)sadr)->sa_family = AF_IPX;\n\n#define DO(src,dest)\t\\\n\tcopy[0] = s[src];\t\\\n\tcopy[1] = s[src + 1];\t\\\n\tsscanf (copy, \"%x\", &val);\t\\\n\t((struct sockaddr_ipx *)sadr)->dest = val\n\n\t\tcopy[2] = 0;\n\t\tDO(0, sa_netnum[0]);\n\t\tDO(2, sa_netnum[1]);\n\t\tDO(4, sa_netnum[2]);\n\t\tDO(6, sa_netnum[3]);\n\t\tDO(9, sa_nodenum[0]);\n\t\tDO(11, sa_nodenum[1]);\n\t\tDO(13, sa_nodenum[2]);\n\t\tDO(15, sa_nodenum[3]);\n\t\tDO(17, sa_nodenum[4]);\n\t\tDO(19, sa_nodenum[5]);\n\t\tsscanf (&s[22], \"%u\", &val);\n\n#undef DO\n\n\t\t((struct sockaddr_ipx *)sadr)->sa_socket = htons((unsigned short)val);\n\t\tif (addrfamily)\n\t\t\t*addrfamily = AF_IPX;\n\t\tif (addrsize)\n\t\t\t*addrsize = sizeof(struct sockaddr_ipx);\n\t\tresult++;\n\t}\n\telse\n#endif\n#ifdef HAVE_IPV6\n#ifdef getaddrinfo\n\tif (getaddrinfo)\n#else\n\tif (1)\n#endif\n\t{\n\t\tstruct addrinfo *addrinfo = NULL;\n\t\tstruct addrinfo *pos;\n\t\tstruct addrinfo udp6hint;\n\t\tint error;\n\t\tchar *port;\n\t\tchar dupbase[256];\n\t\tint len;\n\t\tsize_t i;\n\t\tdouble restime = Sys_DoubleTime();\n\n\t\tmemset(&udp6hint, 0, sizeof(udp6hint));\n\t\tswitch(afhint)\n\t\t{\n#ifdef HAVE_IPV4\n\t\tcase NA_IP:\n\t\t\tudp6hint.ai_family = AF_INET;\n\t\t\tbreak;\n#endif\n#ifdef HAVE_IPV6\n\t\tcase NA_IPV6:\n\t\t\tudp6hint.ai_family = AF_INET6;\n\t\t\tbreak;\n#endif\n#ifdef HAVE_IPX\n\t\tcase NA_IPX:\n\t\t\tudp6hint.ai_family = AF_IPX;\n\t\t\tbreak;\n#endif\n\t\tdefault:\n\t\t\tudp6hint.ai_family = 0;//Any... we check for AF_INET6 or 4\n\t\t\tbreak;\n\t\t}\n\t\tudp6hint.ai_socktype = SOCK_DGRAM;\n\t\tudp6hint.ai_protocol = 0;\n\n\t\tif (*s == '[')\n\t\t{\n\t\t\tport = strstr(s, \"]\");\n\t\t\tif (!port)\n\t\t\t\terror = EAI_NONAME;\n\t\t\telse\n\t\t\t{\n\t\t\t\tlen = port - (s+1);\n\t\t\t\tif (len >= sizeof(dupbase))\n\t\t\t\t\tlen = sizeof(dupbase)-1;\n\t\t\t\tstrncpy(dupbase, s+1, len);\n\t\t\t\tdupbase[len] = '\\0';\n\t\t\t\terror = getaddrinfo(dupbase, (port[1] == ':')?port+2:NULL, &udp6hint, &addrinfo);\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n#if defined(AI_ADDRCONFIG) && !defined(_WIN32)\n\t\t\tudp6hint.ai_flags |= AI_ADDRCONFIG;\t//don't return ipv6 if we can't send to ipv6 hosts\n#endif\n\n\t\t\tport = strrchr(s, ':');\n\n\t\t\tif (port)\n\t\t\t{\n\t\t\t\tlen = port - s;\n\t\t\t\tif (len >= sizeof(dupbase))\n\t\t\t\t\tlen = sizeof(dupbase)-1;\n\t\t\t\tstrncpy(dupbase, s, len);\n\t\t\t\tdupbase[len] = '\\0';\n\t\t\t\terror = getaddrinfo(dupbase, port+1, &udp6hint, &addrinfo);\n\t\t\t}\n\t\t\telse\n\t\t\t\terror = EAI_NONAME;\n\t\t\tif (error)\t//failed, try string with no port.\n\t\t\t\terror = getaddrinfo(s, NULL, &udp6hint, &addrinfo);\t//remember, this func will return any address family that could be using the udp protocol... (ip4 or ip6)\n\t\t}\n\n\t\trestime = Sys_DoubleTime()-restime;\n\t\tif (restime > 0.5)\n\t\t{\t//adding this in an attempt to debug somewhat periodic stalls that I'm being told about.\n\t\t\tCon_DPrintf(\"DNS resolution of %s %s %f seconds (on %s thread)\\n\", s, error?\"failed after\":\"took\", restime, Sys_IsMainThread()?\"main\":\"worker\");\n\t\t}\n\n\t\tif (error)\n\t\t{\n\t\t\treturn false;\n\t\t}\n\t\t((struct sockaddr*)sadr)->sa_family = 0;\n\t\tfor (pos = addrinfo; pos; pos = pos->ai_next)\n\t\t{\n\t\t\tswitch(pos->ai_family)\n\t\t\t{\n\t\t\tcase AF_INET6:\n\t\t\t\tif (!net_dns_ipv6.ival)\n\t\t\t\t\tcontinue;\n\t\t\t\tif (result < addresses)\n\t\t\t\t\tmemcpy(&sadr[result++], pos->ai_addr, pos->ai_addrlen);\n\t\t\t\tbreak;\n#ifdef HAVE_IPV4\n\t\t\tcase AF_INET:\n\t\t\t\tif (!net_dns_ipv4.ival)\n\t\t\t\t\tcontinue;\n\t\t\t\t//ipv4 addresses have a higher priority than ipv6 ones (too few other quake engines support ipv6).\n\t\t\t\tif (result && ((struct sockaddr_in *)&sadr[0])->sin_family == AF_INET6)\n\t\t\t\t{\n\t\t\t\t\tif (result < addresses)\n\t\t\t\t\t\tmemcpy(&sadr[result++], &sadr[0], sizeof(sadr[0]));\n\t\t\t\t\tmemcpy(&sadr[0], pos->ai_addr, pos->ai_addrlen);\n\t\t\t\t}\n\t\t\t\telse if (result < addresses)\n\t\t\t\t\tmemcpy(&sadr[result++], pos->ai_addr, pos->ai_addrlen);\n\t\t\t\tbreak;\n#endif\n\t\t\t}\n\t\t}\n\t\tfreeaddrinfo (addrinfo);\n\n\t\tfor (i = 0; i < result; i++)\n\t\t{\n\t\t\tif (addrfamily)\n\t\t\t\taddrfamily[i] = ((struct sockaddr*)sadr)->sa_family;\n\n\t\t\tif (((struct sockaddr*)&sadr[i])->sa_family == AF_INET)\n\t\t\t{\n\t\t\t\tif (!((struct sockaddr_in *)&sadr[i])->sin_port)\n\t\t\t\t\t((struct sockaddr_in *)&sadr[i])->sin_port = htons(defaultport);\n\t\t\t\tif (addrsize)\n\t\t\t\t\taddrsize[i] = sizeof(struct sockaddr_in);\n\t\t\t}\n\t\t\telse if (((struct sockaddr*)&sadr[i])->sa_family == AF_INET6)\n\t\t\t{\n\t\t\t\tif (!((struct sockaddr_in6 *)&sadr[i])->sin6_port)\n\t\t\t\t\t((struct sockaddr_in6 *)&sadr[i])->sin6_port = htons(defaultport);\n\t\t\t\tif (addrsize)\n\t\t\t\t\taddrsize[i] = sizeof(struct sockaddr_in6);\n\t\t\t}\n\t\t}\n\t}\n\telse\n#endif\n\t{\n#if defined(HAVE_IPV4) && defined(getaddrinfo) && !defined(HAVE_IPV6)\n\t\tchar\tcopy[128];\n\t\tchar\t*colon;\n\n\t\t((struct sockaddr_in *)sadr)->sin_family = AF_INET;\n\n\t\t((struct sockaddr_in *)sadr)->sin_port = 0;\n\n\t\tif (strlen(s) >= sizeof(copy)-1)\n\t\t\treturn false;\n\t\tif (!net_dns_ipv4.ival)\n\t\t\treturn false;\n\n\t\t((struct sockaddr_in *)sadr)->sin_port = htons(defaultport);\n\n\t\tstrcpy (copy, s);\n\t\t// strip off a trailing :port if present\n\t\tfor (colon = copy ; *colon ; colon++)\n\t\t\tif (*colon == ':')\n\t\t\t{\n\t\t\t\t*colon = 0;\n\t\t\t\t((struct sockaddr_in *)sadr)->sin_port = htons((short)atoi(colon+1));\n\t\t\t}\n\n\t\tif (copy[0] >= '0' && copy[0] <= '9')\t//this is the wrong way to test. a server name may start with a number.\n\t\t{\n\t\t\t*(int *)&((struct sockaddr_in *)sadr)->sin_addr = inet_addr(copy);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tstruct hostent\t*h;\n\t\t\tif (! (h = gethostbyname(copy)) )\n\t\t\t\treturn false;\n\t\t\tif (h->h_addrtype != AF_INET)\n\t\t\t\treturn false;\n\t\t\t*(int *)&((struct sockaddr_in *)sadr)->sin_addr = *(int *)h->h_addr_list[0];\n\t\t}\n\t\tif (addrfamily)\n\t\t\t*addrfamily = AF_INET;\n\t\tif (addrsize)\n\t\t\t*addrsize = sizeof(struct sockaddr_in);\n\t\tresult++;\n#endif\n\t}\n\n\treturn result;\n}\n\nstatic const struct urischeme_s urischemes[] =\n{\n#ifdef HAVE_PACKET\n\t{\"udp://\",\tNP_DGRAM,\tNA_INVALID},\t//placeholder for dgram rather than an actual family.\n\t{\"udp4://\",\tNP_DGRAM,\tNA_IP},\n\t{\"udp6://\",\tNP_DGRAM,\tNA_IPV6},\n\t{\"ipx://\",\tNP_DGRAM,\tNA_IPX},\n\n\t//compat with qtv. we don't have any way to exclude specific protocols though.\n\t{\"qw://\",\tNP_DGRAM,\tNA_INVALID},\n\t{\"nq://\",\tNP_DGRAM,\tNA_INVALID},\n\t{\"dp://\",\tNP_DGRAM,\tNA_INVALID},\n\t{\"q2://\",\tNP_DGRAM,\tNA_INVALID},\n\t{\"q3://\",\tNP_DGRAM,\tNA_INVALID},\n\n\t#ifdef KEXLOBBY\n\t{KEXLOBBY\"://\",\tNP_KEXLAN,\tNA_INVALID},\n\t#endif\n#endif\n\n#ifdef TCPCONNECT\n\t{\"tcp://\",\tNP_STREAM,\tNA_INVALID},\t//placeholder for dgram rather than an actual family.\n\t{\"tcp4://\",\tNP_STREAM,\tNA_IP},\n\t{\"tcp6://\",\tNP_STREAM,\tNA_IPV6},\n\t{\"spx://\",\tNP_STREAM,\tNA_IPX},\n\n\t{\"ws://\",\tNP_WS,\t\tNA_INVALID,\tURISCHEME_NEEDSRESOURCE},\n\t#ifdef HAVE_SSL\n\t{\"wss://\",\tNP_WSS,\t\tNA_INVALID,\tURISCHEME_NEEDSRESOURCE},\n\t{\"tls://\",\tNP_TLS,\t\tNA_INVALID},\n\t#endif\n#elif defined(HAVE_WEBSOCKCL)\n\t{\"ws://\",\tNP_WS,\t\tNA_WEBSOCKET, URISCHEME_NEEDSRESOURCE},\n\t{\"wss://\",\tNP_WSS,\t\tNA_WEBSOCKET, URISCHEME_NEEDSRESOURCE},\n\t{\"tcp://\",\tNP_WS,\t\tNA_WEBSOCKET, URISCHEME_NEEDSRESOURCE},\t//fake it\n\t{\"tls://\",\tNP_WSS,\t\tNA_WEBSOCKET, URISCHEME_NEEDSRESOURCE},\t//fake it\n#endif\n#ifdef HAVE_DTLS\n\t{\"dtls://\",\tNP_DTLS,\tNA_INVALID},\n#endif\n\n#if defined(SUPPORT_ICE) || defined(HAVE_WEBSOCKCL)\n\t{\"ice://\",\tNP_RTC_TCP,\tNA_INVALID, URISCHEME_NEEDSRESOURCE},\n\t{\"rtc://\",\tNP_RTC_TCP,\tNA_INVALID, URISCHEME_NEEDSRESOURCE},\n\t{\"ices://\",\tNP_RTC_TLS,\tNA_INVALID,\tURISCHEME_NEEDSRESOURCE},\n\t{\"rtcs://\",\tNP_RTC_TLS,\tNA_INVALID,\tURISCHEME_NEEDSRESOURCE},\n#endif\n\n#ifdef IRCCONNECT\n\t{\"irc://\",\tNP_IRC,\t\tNA_INVALID,\tURISCHEME_NEEDSRESOURCE},\t//should have been handled explicitly, if supported.\n#endif\n\n#ifdef UNIXSOCKETS\n\t{\"udg://\",\tNP_DGRAM,\tNA_UNIX, URISCHEME_NEEDSRESOURCE},\n\t#ifdef TCPCONNECT\n\t{\"unix://\",\tNP_STREAM,\tNA_UNIX, URISCHEME_NEEDSRESOURCE},\n\t#endif\n#endif\n};\n\nconst struct urischeme_s *NET_IsURIScheme(const char *possible)\n{\n\tsize_t i;\n\tfor (i = 0; i < countof(urischemes); i++)\n\t{\n\t\tif (!strncmp(possible, urischemes[i].name, strlen(urischemes[i].name)))\n\t\t\treturn &urischemes[i];\n\t}\n\treturn NULL;\n}\n\n/*\naccepts anything that NET_StringToSockaddr accepts plus certain url schemes\nincluding: tcp, irc\n\nFIXME: should move schemes out of here (so caller can handle paths+etc), using an address family hint for args.\n*/\nsize_t\tNET_StringToAdr2 (const char *s, int defaultport, netadr_t *a, size_t numaddresses, const char **pathstart)\n{\n\tsize_t result = 0, i;\n\tstruct sockaddr_qstorage sadr[8];\n\tint asize[countof(sadr)];\n\tnetproto_t prot;\n\tnetadrtype_t afhint;\n\tchar *path;\n\tchar *args;\n\n\n\n\tmemset(a, 0, sizeof(*a)*numaddresses);\n\tif (pathstart)\n\t\t*pathstart = NULL;\n\n\tif (!numaddresses)\n\t\treturn false;\n\n\tif (!strcmp (s, \"internalserver\"))\n\t{\n\t\ta->type = NA_LOOPBACK;\n\t\treturn true;\n\t}\n\n\tif (!strncmp(s, \"QLoopBack\", 9))\n\t{\n\t\ta->type = NA_LOOPBACK;\n\t\tif (s[9] == ':')\n\t\t\ta->port = atoi(s+10);\n\t\telse\n\t\t\ta->port = defaultport;\n\t\treturn true;\n\t}\n\n\tif (!net_enabled.ival)\n\t\treturn false;\n\tCon_DPrintf(\"Resolving address: %s\\n\", s);\n\n#ifdef HAVE_WEBSOCKCL\n\t//with websockets we can't really resolve anything. failure happens only when trying to connect.\n\t//`connect /GAMENAME` is equivelent to `connect rtc://broker/GAMENAME`\n\tif (*s == '/' || !strncmp (s, \"rtc://\", 6) || !strncmp (s, \"rtcs://\", 7) || !strncmp (s, \"ice://\", 6) || !strncmp (s, \"ices://\", 7))\n\t{\t//basically ICE using sdp-via-websockets to a named relay server.\n\t\tconst char *path;\n\t\ta->prot = NP_RTC_TLS;\n\t\ta->type = NA_WEBSOCKET;\n\t\tif (*s == '/')\n\t\t\t;\n\t\telse if (s[4] == ':')\n\t\t{\n\t\t\ta->prot = NP_RTC_TLS;\n\t\t\ts += 7;\n\t\t}\n\t\telse\n\t\t{\n\t\t\ta->prot = NP_RTC_TCP;\n\t\t\ts += 6;\n\t\t}\n\n\t\tif (*s == '/')\n\t\t{\t//if the broker was omitted then we're using the net_ice_broker cvar... track its security instead...\n\t\t\tif (!strncmp(net_ice_broker.string, \"tls://\", 6) || !strncmp(net_ice_broker.string, \"wss://\", 6))\n\t\t\t\ta->prot=NP_RTC_TLS;\n\t\t\telse if (!strncmp(net_ice_broker.string, \"tcp://\", 6) || !strncmp(net_ice_broker.string, \"ws://\", 5))\n\t\t\t\ta->prot=NP_RTC_TCP;\n\t\t\telse\n\t\t\t\treturn 0;\t//no default broker, don't allow it.\n\t\t}\n\n\t\tpath = s+strlen(s);\n\t\tif (path)\n\t\t{\n\t\t\tif (path-s < sizeof(a->address.websocketurl))\n\t\t\t{\n\t\t\t\tmemcpy(a->address.websocketurl, s, path-s);\n\t\t\t\ta->address.websocketurl[path-s] = 0;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tCon_DPrintf(\"Path too long\\n\");\n\t\t\t\treturn 0;\t//too long\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tCon_DPrintf(\"No path\\n\");\n\t\t\treturn 0;\t//reject it when there's no path\n\t\t}\n\t\treturn 1;\n\t}\n\telse if (!strncmp (s, \"ws://\", 5) || !strncmp (s, \"wss://\", 6))\n\t{\n\t\ta->type = NA_WEBSOCKET;\n\t\tif (!strncmp (s, \"wss://\", 6))\n\t\t\ta->prot = NP_WSS;\n\t\telse\n\t\t\ta->prot = NP_WS;\n\t\tQ_strncpyz(a->address.websocketurl, s, sizeof(a->address.websocketurl));\n\t\treturn 1;\n\t}\n\telse if (*net_ice_broker.string)\n\t{\n\t\ta->type = NA_WEBSOCKET;\t//figure it out later, to keep it more readable.\n\t\ta->prot = NP_RTC_TLS;\n\t\tQ_snprintfz(a->address.websocketurl, sizeof(a->address.websocketurl), \"/udp/%s\", s);\n//\t\tif (numaddresses < 2)\n\t\t\treturn 1;\n\n\t\ta++;\n\t\ta->type = NA_WEBSOCKET;\n\t\ta->prot = NP_WSS;\n\t\tQ_snprintfz(a->address.websocketurl, sizeof(a->address.websocketurl), \"wss://%s\", s);\n\t\treturn 2;\n\t}\n\telse\n\t{\n\t\t/*code for convienience - no other protocols work anyway*/\n\t\tstatic float warned;\n\t\tint i;\n\t\tfor (i = 0; s[i] == ':' || s[i] == '[' || s[i] == ']' || s[i] == '.' || (s[i] >= '0' && s[i] <= '9'); i++)\n\t\t\t;\n\t\ta->type = NA_WEBSOCKET;\n\t\tif (s[i])\n\t\t{\t//assume there's part of some domain name in there. FIXME: this may be a false positive in the case of hex ipv6 addresses.\n\t\t\tif (warned < realtime)\n\t\t\t{\n\t\t\t\tCon_DPrintf(\"Note: Assuming wss:// prefix\\n\");\n\t\t\t\twarned = realtime + 1;\n\t\t\t}\n\t\t\ta->prot = NP_WSS;\n\t\t\tmemcpy(a->address.websocketurl, \"wss://\", 6);\n\t\t\tQ_strncpyz(a->address.websocketurl+6, s, sizeof(a->address.websocketurl)-6);\n\t\t}\n\t\telse\n\t\t{\t//looks like a straight ip address.\n\t\t\t//assume most server-by-ip addresses will not have proper certificates set up with that specific ip address as an actual name, and fall back on unsecure rubbish instead.\n\t\t\tif (warned < realtime)\n\t\t\t{\n\t\t\t\tCon_Printf(\"Note: Assuming ws:// prefix\\n\");\n\t\t\t\twarned = realtime + 1;\n\t\t\t}\n\t\t\ta->prot = NP_WS;\n\t\t\tmemcpy(a->address.websocketurl, \"ws://\", 5);\n\t\t\tQ_strncpyz(a->address.websocketurl+5, s, sizeof(a->address.websocketurl)-5);\n\t\t}\n\t\treturn 1;\n\t}\n#endif\n\n#ifdef IRCCONNECT\n\tif (!strncmp (s, \"irc://\", 6))\n\t{\n\t\tchar *at;\n\t\tchar *slash;\n\t\tmemset (a, 0, sizeof(*a));\n\t\ta->type = NA_IRC;\n\n\t\ts+=6;\n\t\tslash = strchr(s, '/');\n\t\tif (!slash)\n\t\t\treturn false;\n\t\tif (slash - s+1 >= sizeof(a->address.irc.host))\n\t\t\treturn false;\n\t\tmemcpy(a->address.irc.host, s, slash - s);\n\t\ta->address.irc.host[slash - s] = 0;\n\t\ts = slash+1;\n\t\tat = strchr(s, '@');\n\t\tif (at)\n\t\t{\n\t\t\tif (at-s+1 >= sizeof(a->address.irc.user))\n\t\t\t\treturn false;\n\t\t\tQ_strncpyz(a->address.irc.user, s, at-s+1);\n\t\t\tQ_strncpyz(a->address.irc.channel, at+1, sizeof(a->address.irc.channel));\n\t\t}\n\t\telse\n\t\t{\n\t\t\t//just a user.\n\t\t\tQ_strncpyz(a->address.irc.user, s, sizeof(a->address.irc.user));\n\t\t}\n\t\treturn 1;\n\t}\n#endif\n\n#ifdef HAVE_NATPMP\n\tif (!strncmp (s, \"natpmp://\", 9))\n\t{\t//our natpmp thing omits the host part. FIXME: host should be the NAT that we're sending to\n\t\tNET_PortToAdr(NA_IP, NP_NATPMP, s+9, a);\n\t\tif (a->prot != NP_NATPMP)\n\t\t\treturn false;\n\t\treturn true;\n\t}\n#endif\n\n\tfor (prot = NP_DGRAM, afhint = NA_INVALID/*any*/, i = 0; i < countof(urischemes); i++)\n\t{\n\t\tif (!strncmp(s, urischemes[i].name, strlen(urischemes[i].name)))\n\t\t{\n\t\t\ts += strlen(urischemes[i].name);\n\t\t\tprot = urischemes[i].prot;\n\t\t\tafhint = urischemes[i].family;\n\n\t\t\tif (prot == NP_RTC_TCP || prot == NP_RTC_TLS)\n\t\t\t\tdefaultport = PORT_ICEBROKER;\n\t\t\telse if (prot == NP_WSS)\n\t\t\t\tdefaultport = 443;\n\t\t\telse if (prot == NP_WS)\n\t\t\t\tdefaultport = 80;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\targs = strchr(s, '?');\n\tif (args)\n\t\t*args=0;\n\n\tpath = strchr(s, '/');\n#if !defined(HAVE_WEBSOCKCL) && defined(SUPPORT_ICE)\n\tif (path == s)\n\t{\n\t\tif (!*net_ice_broker.string)\n\t\t\treturn result;\n\t\ts = net_ice_broker.string;\n\t\tif (!strncmp(s, \"tls://\", 6) || !strncmp(s, \"wss://\", 6))\n\t\t\ts+=6, prot=NP_RTC_TLS;\n\t\telse if (!strncmp(s, \"tcp://\", 6))\n\t\t\ts+=6, prot=NP_RTC_TCP;\n\t\telse if (!strncmp(s, \"ws://\", 5))\n\t\t\ts+=5, prot=NP_RTC_TCP;\n\t\telse\n\t\t\tprot = NP_RTC_TLS;\t//best-practise by default.\n\t\tif (pathstart)\n\t\t\t*pathstart = path;\n\t\tresult = NET_StringToSockaddr2 (s, PORT_ICEBROKER, afhint, sadr, NULL, asize, min(numaddresses, countof(sadr)));\n\t}\n\telse\n#endif\n\tif (path && (path-s)<MAX_OSPATH)\n\t{\n\t\tchar host[MAX_OSPATH];\n\t\tif (pathstart)\n\t\t\t*pathstart = path;\n\t\tmemcpy(host, s, path-s);\n\t\thost[path-s] = 0;\n\t\tresult = NET_StringToSockaddr2 (host, defaultport, afhint, sadr, NULL, asize, min(numaddresses, countof(sadr)));\n\t}\n\telse\n\t\tresult = NET_StringToSockaddr2 (s, defaultport, afhint, sadr, NULL, asize, min(numaddresses, countof(sadr)));\n\tfor (i = 0; i < result; i++)\n\t{\n\t\tSockadrToNetadr (&sadr[i], asize[i], &a[i]);\n\t\ta[i].prot = prot;\n\t}\n\n\tif (args)\n\t\t*args='?';\n\n\treturn result;\n}\n\n// NET_IntegerToMask: given a source address pointer, a mask address pointer, and\n// desired number of bits, fills the mask pointer with given bits\n// (bits < 0 will always fill all bits)\nvoid NET_IntegerToMask (netadr_t *a, netadr_t *amask, int bits)\n{\n\tunsigned int i;\n\tqbyte *n;\n\n\tmemset (amask, 0, sizeof(*amask));\n\tamask->type = a->type;\n\n\tif (bits < 0)\n\t\ti = 8000; // fill all bits\n\telse\n\t\ti = bits;\n\n\tswitch (amask->type)\n\t{\n\tcase NA_INVALID:\n\t\tbreak;\n\tcase NA_IP:\n\t\tn = amask->address.ip;\n\t\tif (i > 32)\n\t\t\ti = 32;\n\t\tfor (; i >= 8; i -= 8)\n\t\t{\n\t\t\t*n = 0xFF;\n\t\t\tn++;\n\t\t}\n\n\t\t// fill last bit\n\t\tif (i)\n\t\t{\n\t\t\ti = 8 - i;\n\t\t\ti = 255 - ((1 << i) - 1);\n\t\t\t*n = i;\n\t\t}\n\t\tbreak;\n\tcase NA_IPV6:\n#ifdef HAVE_IPV6\n\t\tn = amask->address.ip6;\n\t\tif (i > 128)\n\t\t\ti = 128;\n\t\tfor (; i >= 8; i -= 8)\n\t\t{\n\t\t\t*n = 0xFF;\n\t\t\tn++;\n\t\t}\n\n\t\t// fill last bit\n\t\tif (i)\n\t\t{\n\t\t\ti = 8 - i;\n\t\t\ti = 255 - ((1 << i) - 1);\n\t\t\t*n = i;\n\t\t}\n#endif\n\t\tbreak;\n\tcase NA_IPX:\n#ifdef HAVE_IPX\n\t\tn = amask->address.ipx;\n\t\tif (i > 80)\n\t\t\ti = 80;\n\t\tfor (; i >= 8; i -= 8)\n\t\t{\n\t\t\t*n = 0xFF;\n\t\t\tn++;\n\t\t}\n\n\t\t// fill last bit\n\t\tif (i)\n\t\t{\n\t\t\ti = 8 - i;\n\t\t\ti = 255 - ((1 << i) - 1);\n\t\t\t*n = i;\n\t\t}\n#endif\n\t\tbreak;\n\tcase NA_LOOPBACK:\n\t\tbreak;\n#ifdef UNIXSOCKETS\n\tcase NA_UNIX:\t//address masks/filtering don't make sense.\n#endif\n#ifdef HAVE_WEBSOCKCL\n\tcase NA_WEBSOCKET:\n#endif\n#ifdef IRCCONNECT\n\tcase NA_IRC:\n#endif\n#ifdef SUPPORT_ICE\n\tcase NA_ICE:\n#endif\n\t\tbreak;\n\n\t}\n}\n\n// ParsePartialIP: check string to see if it is a partial IP address and\n// return bits to mask and set netadr_t or 0 if not an address\nint ParsePartialIP(const char *s, netadr_t *a)\n{\n\tchar *colon;\n\tint bits;\n\n\tif (!*s)\n\t\treturn 0;\n\n\tmemset (a, 0, sizeof(*a));\n\n\t//if its ::ffff:a.b.c.d then parse it as ipv4 by just skipping the prefix.\n\t//we ought to leave it as ipv6, but any printing will show it as ipv4 anyway.\n\tif (!strncasecmp(s, \"::ffff:\", 7) && strchr(s+7, '.') && !strchr(s+7, ':'))\n\t\ts += 7;\n\n\t//multiple colons == ipv6\n\t//single colon = ipv4:port\n\tcolon = strchr(s, ':');\n\tif (colon && strchr(colon+1, ':'))\n\t{\n\t\tqbyte *address = a->address.ip6;\n\t\tunsigned long tmp;\n\t\tint gapstart = -1;\t//in bytes...\n\t\tbits = 0;\n\n\t\twhile(*s)\n\t\t{\n\t\t\ttmp = strtoul(s, &colon, 16);\n\t\t\tif (colon == s)\n\t\t\t{\n\t\t\t\tif (bits)\n\t\t\t\t\treturn 0;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (tmp > 0xffff)\n\t\t\t\t\treturn 0;\t//invalid\n\t\t\t\t*address++ = (tmp>>8)&0xff;\n\t\t\t\t*address++ = (tmp>>0)&0xff;\n\t\t\t\tbits += 16;\n\t\t\t}\n\n\t\t\tif (bits == 128)\n\t\t\t{\n\t\t\t\tif (!*colon)\n\t\t\t\t\tbreak;\n\t\t\t\treturn 0;\t//must have ended here\n\t\t\t}\n\n\n\t\t\t//double-colon is a gap (or partial end).\n\t\t\t//hopefully the last 64 bits or whatever will be irrelevant anyway, so such addresses won't be common\n\t\t\tif (colon[0] == ':' && colon[1] == ':')\n\t\t\t{\n\t\t\t\tif (gapstart >= 0)\n\t\t\t\t\treturn 0; //only one gap...\n\t\t\t\tif (!colon[2])\n\t\t\t\t\tbreak;\t//nothing after. its partial.\n\t\t\t\tgapstart = bits/8;\n\t\t\t\tcolon+=2;\n\t\t\t}\n\t\t\telse if (*colon == ':' && bits)\n\t\t\t\tcolon++;\n\t\t\telse if (*colon)\n\t\t\t\treturn 0; //gibberish here...\n\t\t\telse\n\t\t\t\tbreak;\t//end of address... anything more is a partial.\n\t\t\ts = colon;\n\t\t}\n\t\tif (gapstart >= 0)\n\t\t{\n\t\t\tint tailsize = (bits/8)-gapstart;\t//bits to move to the end\n\t\t\tint gapsize = 16 - gapstart - tailsize;\n\t\t\tmemmove(a->address.ip6+gapstart+gapsize, a->address.ip6+gapstart, tailsize);\t//move the bits we found to the end\n\t\t\tmemset(a->address.ip6+gapstart, 0, gapsize);\t//and make sure the gap is cleared\n\t\t\tbits = 128;\t//found it all, or something.\n\t\t}\n\t\tif (!bits)\n\t\t\tbits = 1;\t//FIXME: return of 0 is an error, but :: is 0-length... lie.\n\t\ta->type = NA_IPV6;\n\t\ta->port = 0;\n\t}\n\telse\n\t{\n\t\tchar *address = a->address.ip;\n\t\tint port = 0;\n\t\tbits = 8;\n\t\twhile (*s)\n\t\t{\n\t\t\tif (*s == ':')\n\t\t\t{\n\t\t\t\tport = htons(strtoul(s+1, &address, 10));\n\t\t\t\tif (*address)\t//if there was something other than a number there, give up now\n\t\t\t\t\treturn 0;\n\t\t\t\tbreak;\t//end-of-string\n\t\t\t}\n\t\t\telse if (*s == '.')\n\t\t\t{\n\t\t\t\tif (bits >= 32) // only 32 bits in ipv4\n\t\t\t\t\treturn 0;\n\t\t\t\telse if (*(s+1) == '.')\n\t\t\t\t\treturn 0;\n\t\t\t\telse if (*(s+1) == '\\0')\n\t\t\t\t\tbreak; // don't add more bits to the mask for x.x., etc\n\t\t\t\taddress++;\n\n\t\t\t\t//many nq servers mask addresses with Xs.\n\t\t\t\tif (s[1] == 'x' || s[1] == 'X')\n\t\t\t\t{\n\t\t\t\t\ts++;\n\t\t\t\t\twhile (*s == 'x' || *s == 'X' || *s == '.')\n\t\t\t\t\t\ts++;\n\t\t\t\t\tif (*s)\n\t\t\t\t\t\treturn 0;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tbits += 8;\n\t\t\t}\n\t\t\telse if (*s >= '0' && *s <= '9')\n\t\t\t\t*address = ((*address)*10) + (*s-'0');\n\t\t\telse\n\t\t\t\treturn 0; // invalid character\n\n\t\t\ts++;\n\t\t}\n\t\ta->type = NA_IP;\n\t\ta->port = port;\n\t}\n\n\treturn bits;\n}\n\n// NET_StringToAdrMasked: extension to NET_StringToAdr to handle IP addresses\n// with masks or integers representing the bit masks\nqboolean NET_StringToAdrMasked (const char *s, qboolean allowdns, netadr_t *a, netadr_t *amask)\n{\n\tchar t[64];\n\tchar *spoint;\n\tint i;\n\n\tspoint = strchr(s, '/');\n\n\tif (spoint)\n\t{\n\t\t// we have a slash in the address so split and resolve separately\n\t\tchar *c;\n\n\t\ti = (int)(spoint - s) + 1;\n\t\tif (i > sizeof(t))\n\t\t\ti = sizeof(t);\n\n\t\tQ_strncpyz(t, s, i);\n\t\tif (!ParsePartialIP(t, a) && (!allowdns || !NET_StringToAdr(t, 0, a)))\n\t\t\treturn false;\n\t\tspoint++;\n\n\t\tc = spoint;\n\t\tif (!*c)\n\t\t\treturn false;\n\n\t\twhile (*c) // check for non-numeric characters\n\t\t{\n\t\t\tif (*c < '0' || *c > '9')\n\t\t\t{\n\t\t\t\tc = NULL;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tc++;\n\t\t}\n\n\t\tif (c == NULL) // we have an address so resolve it and return\n\t\t\treturn ParsePartialIP(spoint, amask) || (allowdns && NET_StringToAdr(spoint, 0, amask));\n\n\t\t// otherwise generate mask for given bits\n\t\ti = atoi(spoint);\n\t\tNET_IntegerToMask(a, amask, i);\n\t}\n\telse\n\t{\n\t\t// we don't have a slash, resolve and fill with a full mask\n\t\ti = ParsePartialIP(s, a);\n\t\tif (!i && (!allowdns || !NET_StringToAdr(s, 0, a)))\n\t\t\treturn false;\n\n\t\tmemset (amask, 0, sizeof(*amask));\n\t\tamask->type = a->type;\n\n\t\tif (i)\n\t\t\tNET_IntegerToMask(a, amask, i);\n\t\telse\n\t\t\tNET_IntegerToMask(a, amask, -1);\n\t}\n\n\treturn true;\n}\n\nqboolean NET_StringToAdr_NoDNS(const char *address, int port, netadr_t *out)\n{\n\tint peerbits;\n\tif (*address == '[')\n\t{\n\t\tchar *close = strchr(address+1, ']');\n\t\tif (close)\n\t\t\t*close = 0;\n\t\tpeerbits = NET_StringToAdr_NoDNS(address+1, 0, out);\n\t\tif (close)\n\t\t{\n\t\t\t*close = ']';\n\t\t\tif (close[1] == ':')\n\t\t\t\tout->port = htons(atoi(close+2));\n\t\t}\n\t\treturn peerbits;\n\t}\n\telse\n\t{\n\t\tpeerbits = ParsePartialIP(address, out);\n\t\tif (out->type == NA_IP && peerbits == 32)\n\t\t{\n\t\t\t//ignore invalid addresses\n\t\t\tif (!out->address.ip[0] && !out->address.ip[1] && !out->address.ip[2] && !out->address.ip[3])\n\t\t\t\tout->type = NA_INVALID;\n\t\t}\n\t\telse if (out->type == NA_IPV6 && peerbits == 128)\n\t\t{\n\t\t\t//ignore invalid addresses\n\t\t\tint i;\n\t\t\tfor (i = 0; i < countof(out->address.ip6); i++)\n\t\t\t\tif (out->address.ip6[i])\n\t\t\t\t\tbreak;\n\t\t\tif (i == countof(out->address.ip6))\n\t\t\t\tout->type = NA_INVALID;\n\t\t}\n\t\telse\n\t\t\tout->type = NA_INVALID;\n\n\t\treturn out->type != NA_INVALID;\n\t}\n}\n\nqboolean NET_IsEncrypted(netadr_t *adr)\n{\n\tif (adr->type == NA_LOOPBACK)\n\t\treturn true;\t//might as well claim it, others can't snoop on it so...\n#ifdef SUPPORT_ICE\n\tif (adr->type == NA_ICE && ICE_GetPeerCertificate(adr, QCERT_ISENCRYPTED, NULL, 0)==0)\n\t\treturn true;\n#endif\n#if defined(FTE_TARGET_WEB)\n\tif (adr->prot == NP_RTC_TLS)\t//web port works a bit differently... webrtc is ALWAYS encrypted, but only report it as secure when the broker connection is encrypted too.\n\t\treturn true;\n#endif\n\tif (adr->prot == NP_DTLS || adr->prot == NP_TLS || adr->prot == NP_WSS)\n\t\treturn true;\n\treturn false;\n}\n\n// NET_CompareAdrMasked: given 3 addresses, 2 to compare with a complimentary mask,\n// returns true or false if they match\n//WARNING: a is typically an ipv6 address, even if its an ipv4-mapped address.\n//so ipv4ify first.\n//this is not intended to identify any specific connection, so we can ignore udp/tcp distinctions (especially as this is usually used for bans).\nqboolean NET_CompareAdrMasked(netadr_t *a, netadr_t *b, netadr_t *mask)\n{\n\tint i;\n\n\t//make sure the address being checked against matches the mask\n\tif (b->type != mask->type)\n\t\treturn false;\n\n\t// check port if both are non-zero\n\tif (a->port && b->port && a->port != b->port)\n\t\treturn false;\n\n\t// check to make sure all types match\n\tif (a->type != b->type)\n\t{\n\t\tif (a->type == NA_IP && b->type == NA_IPV6 && mask->type == NA_IP)\n\t\t{\n\t\t\tfor (i = 0; i < 10; i++)\n\t\t\t\tif (b->address.ip6[i] != 0)\n\t\t\t\t\treturn false;\t//only matches if they're 0s, otherwise its not an ipv4 address there\n\t\t\tfor (; i < 12; i++)\n\t\t\t\tif (b->address.ip6[i] != 0xff)// && b->address.ip6[i] != 0x00)\t//0x00 is depricated\n\t\t\t\t\treturn false;\t//only matches if they're 0s or ffs, otherwise its not an ipv4 address there\n\t\t\tfor (i = 0; i < 4; i++)\n\t\t\t{\n\t\t\t\tif ((a->address.ip[i] & mask->address.ip[i]) != (b->address.ip6[12+i] & mask->address.ip[i]))\n\t\t\t\t\treturn false;\t//mask doesn't match\n\t\t\t}\n\t\t\treturn true;\t//its an ipv4 address in there, the mask matched the whole way through\n\t\t}\n\t\tif (a->type == NA_IPV6 && b->type == NA_IP && mask->type == NA_IP)\n\t\t{\n\t\t\tfor (i = 0; i < 10; i++)\n\t\t\t\tif (a->address.ip6[i] != 0)\n\t\t\t\t\treturn false;\t//only matches if they're 0s, otherwise its not an ipv4 address there\n\n\t\t\tfor (; i < 12; i++)\n\t\t\t\tif (a->address.ip6[i] != 0xff)// && a->address.ip6[i] != 0x00)\t//0x00 is depricated\n\t\t\t\t\treturn false;\t//only matches if they're 0s or ffs, otherwise its not an ipv4 address there\n\n\t\t\tfor (i = 0; i < 4; i++)\n\t\t\t{\n\t\t\t\tif ((a->address.ip6[12+i] & mask->address.ip[i]) != (b->address.ip[i] & mask->address.ip[i]))\n\t\t\t\t\treturn false;\t//mask doesn't match\n\t\t\t}\n\t\t\treturn true;\t//its an ipv4 address in there, the mask matched the whole way through\n\t\t}\n\t\treturn false;\n\t}\n\n\t// match on protocol type and compare address\n\tswitch (a->type)\n\t{\n\tcase NA_LOOPBACK:\n\t\treturn true;\n\tcase NA_IP:\n\t\tfor (i = 0; i < 4; i++)\n\t\t{\n\t\t\tif ((a->address.ip[i] & mask->address.ip[i]) != (b->address.ip[i] & mask->address.ip[i]))\n\t\t\t\treturn false;\n\t\t}\n\t\tbreak;\n#ifdef HAVE_IPV6\n\tcase NA_IPV6:\n\t\tfor (i = 0; i < 16; i++)\n\t\t{\n\t\t\tif ((a->address.ip6[i] & mask->address.ip6[i]) != (b->address.ip6[i] & mask->address.ip6[i]))\n\t\t\t\treturn false;\n\t\t}\n\t\tbreak;\n#endif\n#ifdef HAVE_IPX\n\tcase NA_IPX:\n\t\tfor (i = 0; i < 10; i++)\n\t\t{\n\t\t\tif ((a->address.ipx[i] & mask->address.ipx[i]) != (b->address.ipx[i] & mask->address.ipx[i]))\n\t\t\t\treturn false;\n\t\t}\n\t\tbreak;\n#endif\n\n#ifdef IRCCONNECT\n\tcase NA_IRC:\n\t\t//masks are not supported, match explicitly\n\t\tif (strcmp(a->address.irc.user, b->address.irc.user))\n\t\t\treturn false;\n\t\tbreak;\n#endif\n\tdefault:\n\t\treturn false; // invalid protocol\n\t}\n\n\treturn true; // all checks passed\n}\n\n// UniformMaskedBits: counts number of bits in an assumed uniform mask, returns\n// -1 if not uniform\nint UniformMaskedBits(netadr_t *mask)\n{\n\tint bits;\n\tint b;\n\tunsigned int bs;\n\tqboolean bitenc = false;\n\n\tswitch (mask->type)\n\t{\n\tcase NA_IP:\n\t\tbits = 32;\n\t\tfor (b = 3; b >= 0; b--)\n\t\t{\n\t\t\tif (mask->address.ip[b] == 0xFF)\n\t\t\t\tbitenc = true;\n\t\t\telse if (mask->address.ip[b])\n\t\t\t{\n\t\t\t\tbs = (~mask->address.ip[b]) & 0xFF;\n\t\t\t\twhile (bs)\n\t\t\t\t{\n\t\t\t\t\tif (bs & 1)\n\t\t\t\t\t{\n\t\t\t\t\t\tbits -= 1;\n\t\t\t\t\t\tif (bitenc)\n\t\t\t\t\t\t\treturn -1;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\tbitenc = true;\n\t\t\t\t\tbs >>= 1;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (bitenc)\n\t\t\t\treturn -1;\n\t\t\telse\n\t\t\t\tbits -= 8;\n\t\t}\n\t\tbreak;\n#ifdef HAVE_IPV6\n\tcase NA_IPV6:\n\t\tbits = 128;\n\t\tfor (b = 15; b >= 0; b--)\n\t\t{\n\t\t\tif (mask->address.ip6[b] == 0xFF)\n\t\t\t\tbitenc = true;\n\t\t\telse if (mask->address.ip6[b])\n\t\t\t{\n\t\t\t\tbs = (~mask->address.ip6[b]) & 0xFF;\n\t\t\t\twhile (bs)\n\t\t\t\t{\n\t\t\t\t\tif (bs & 1)\n\t\t\t\t\t{\n\t\t\t\t\t\tbits -= 1;\n\t\t\t\t\t\tif (bitenc)\n\t\t\t\t\t\t\treturn -1;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\tbitenc = true;\n\t\t\t\t\tbs >>= 1;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (bitenc)\n\t\t\t\treturn -1;\n\t\t\telse\n\t\t\t\tbits -= 8;\n\t\t}\n\t\tbreak;\n#endif\n#ifdef HAVE_IPX\n\tcase NA_IPX:\n\t\tbits = 80;\n\t\tfor (b = 9; b >= 0; b--)\n\t\t{\n\t\t\tif (mask->address.ipx[b] == 0xFF)\n\t\t\t\tbitenc = true;\n\t\t\telse if (mask->address.ipx[b])\n\t\t\t{\n\t\t\t\tbs = (~mask->address.ipx[b]) & 0xFF;\n\t\t\t\twhile (bs)\n\t\t\t\t{\n\t\t\t\t\tif (bs & 1)\n\t\t\t\t\t{\n\t\t\t\t\t\tbits -= 1;\n\t\t\t\t\t\tif (bitenc)\n\t\t\t\t\t\t\treturn -1;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\tbitenc = true;\n\t\t\t\t\tbs >>= 1;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (bitenc)\n\t\t\t\treturn -1;\n\t\t\telse\n\t\t\t\tbits -= 8;\n\t\t}\n\t\tbreak;\n#endif\n\tdefault:\n\t\treturn -1; // invalid protocol\n\t}\n\n\treturn bits; // all checks passed\n}\n\nchar *NET_AdrToStringMasked (char *s, int len, netadr_t *a, netadr_t *amask)\n{\n\tint i;\n\tchar adr[MAX_ADR_SIZE], mask[MAX_ADR_SIZE];\n\n\ti = UniformMaskedBits(amask);\n\n\tif (i >= 0)\n\t\tsnprintf(s, len, \"%s/%i\", NET_AdrToString(adr, sizeof(adr), a), i);\n\telse\n\t\tsnprintf(s, len, \"%s/%s\", NET_AdrToString(adr, sizeof(adr), a), NET_AdrToString(mask, sizeof(mask), amask));\n\n\treturn s;\n}\n\n// Returns true if we can't bind the address locally--in other words,\n// the IP is NOT one of our interfaces.\nqboolean NET_IsClientLegal(netadr_t *adr)\n{\n#if 0\n\tstruct sockaddr_in sadr;\n\tint newsocket;\n\n\tif (adr->ip[0] == 127)\n\t\treturn false; // no local connections period\n\n\tNetadrToSockadr (adr, &sadr);\n\n\tif ((newsocket = socket (PF_INET, SOCK_CLOEXEC|SOCK_DGRAM, IPPROTO_UDP)) == INVALID_SOCKET)\n\t\tSys_Error (\"NET_IsClientLegal: socket:\", strerror(qerrno));\n\n\tsadr.sin_port = 0;\n\n\tif( bind (newsocket, (void *)&sadr, sizeof(sadr)) == -1)\n\t{\n\t\t// It is not a local address\n\t\tclose(newsocket);\n\t\treturn true;\n\t}\n\tclose(newsocket);\n\treturn false;\n#else\n\treturn true;\n#endif\n}\n\nqboolean\tNET_IsLoopBackAddress (netadr_t *adr)\n{\n//\treturn (!strcmp(cls.servername, NET_AdrToString(net_local_adr)) || !strcmp(cls.servername, \"local\");\n\treturn adr->type == NA_LOOPBACK;\n}\n\n\nvoid *Auth_GetKnownCertificate(const char *certname, size_t *size)\n{\t//our 'code signing' certs\n\t//we only allow packages to be installed into the root dir (or with dll/so/exe extensions) when their signature is signed by one of these certificates\n#ifdef HAVE_SSL\n\tstatic struct\n\t{\n\t\tconst char *name;\n\t\tqbyte *cert;\n\t} certs[] =\n\t{\t//the contents of a -pubcert FILE\n\t\t//note: not enforced for pk3 files (which we will otherwise happily randomly download of random servers anyway).\n\t\t{\"Spike\",\t\"-----BEGIN CERTIFICATE-----\\n\"\n\t\t\t\t\t\"MIIDnTCCAgUCCjE1ODQ4ODg2OTEwDQYJKoZIhvcNAQELBQAwEDEOMAwGA1UEAxMF\\n\"\n\t\t\t\t\t\"U3Bpa2UwHhcNMjAwMzIyMTQ1MTMwWhcNMzAwMzIwMTQ1MTMxWjAQMQ4wDAYDVQQD\\n\"\n\t\t\t\t\t\"EwVTcGlrZTCCAaIwDQYJKoZIhvcNAQEBBQADggGPADCCAYoCggGBAN07KHTPc0Pn\\n\"\n\t\t\t\t\t\"lC8MlQNiI+OEUEJBakTjfNq+IJzJ6oTWXxfQbHrN+UpKXwxploDbeyxTE1Fniisi\\n\"\n\t\t\t\t\t\"nLWhOkW/XQqyXLXAv/3lxiSwe4QcVMOhQlw5U05VrdB7xHvFMShLsyc12sNvBiIa\\n\"\n\t\t\t\t\t\"Vw05wFJgIXYTd9nJfm9x3kpxRoTJBvdDbYl8OagT6SxzJfHkdmfI2TYVYxGUH9nX\\n\"\n\t\t\t\t\t\"R9zvwXTIXvV47cko2ON8scH9QQ6KgwMfcwyIBL74Btvl4ye+TrL2srj38FBxyyPG\\n\"\n\t\t\t\t\t\"SSdKPk4LN2zsfNsJm31hzWrdLEkl3CTOX5gHHSweOKpuPwmX/GPd1xo0nIMwDou4\\n\"\n\t\t\t\t\t\"BsMMBAhK/JSyLpUUzk5gbRmy4PwFccktHdFW6LF8ZvPY7e7LEiD5KOWZ7a7c1WR/\\n\"\n\t\t\t\t\t\"4oJrjo0t+7OugVADolxzLXFrq9ACBGrD8r6QlsGC8O7WqpKGQCT+4q3tUup9tPkh\\n\"\n\t\t\t\t\t\"3dhjC0jEkKljS+39uukbisV702bHwoEZPzjMpz4O9bHf6JbIJLlQzQIDAQABMA0G\\n\"\n\t\t\t\t\t\"CSqGSIb3DQEBCwUAA4IBgQC5rj7R7a9LLnqgiXMUITGnygK1lp0EV2BdnIrg/MHr\\n\"\n\t\t\t\t\t\"y+Gk9BA+XgFSI4W9odiG/hJnA7aQ0S2kk1GNYQ+NNzU2bQIMkaobaZApV9ojD4lL\\n\"\n\t\t\t\t\t\"s33Qbgt/Ocpadtpj8EiMInjLkn1B+wnqcX3S76Zcrf8RT4WP2A4klxcN3zBNBiBL\\n\"\n\t\t\t\t\t\"DAJ3SrH8hZ9wmruwAY5tMZhQzDHkeK8uaDb7nE0HA5GXeT4QYA/L7Ys2nGYgxj1O\\n\"\n\t\t\t\t\t\"L5YlGddBcX3O6XyJpSeCO2Z2kwl4qg8oiM+Y546lILotuL5qD/+FTDeX3dGd8nyD\\n\"\n\t\t\t\t\t\"e1g/7xd0V4IyKUjii8Vu2V1F7t0xVTPWEe13TqU/JTfKX4zvQnMF7zxgGFIwabHX\\n\"\n\t\t\t\t\t\"lzk2olte4rPp+iQzPmnynLiUrdkxGXLnE0V545VO+iGO8+bwclbJ+7SG6N5l8xox\\n\"\n\t\t\t\t\t\"WjGunhXXkEjitAk+ssBjbEh8kIfpFdVA09v60rMdm7BdfO3//QOsjwiwKkBOXcYW\\n\"\n\t\t\t\t\t\"QGE0Ue4J7anLVAKiQq4n1aU=\\n\"\n\t\t\t\t\t\"-----END CERTIFICATE-----\\n\"},\n\t\t{\"eukara\",\t\"-----BEGIN CERTIFICATE-----\\n\"\n\t\t\t\t\t\"MIIDnzCCAgcCCjE2ODI1NDc0MDEwDQYJKoZIhvcNAQELBQAwETEPMA0GA1UEAxMG\\n\"\n\t\t\t\t\t\"ZXVrYXJhMB4XDTIzMDQyNjIyMTY0MFoXDTMzMDQyMzIyMTY0MVowETEPMA0GA1UE\\n\"\n\t\t\t\t\t\"AxMGZXVrYXJhMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEAqyI2LKEb\\n\"\n\t\t\t\t\t\"kBN7JhbCUD1VJFDMPwcnj3JXXswiIVl0j56W7PBP0k8nT8Ar/Wy/NhplDqmJz78p\\n\"\n\t\t\t\t\t\"DPimU1ajifZLvRSW7sqln/KqZdUBvfHbO3drLj/NPAIymLvNB+NJ+TG6ljzwiu+P\\n\"\n\t\t\t\t\t\"RFtxphPXqxHkjt5PzJXfmgXO5id3iXrC0N66rQwJHOeTYWuOHPooKpXqBNkrDn1p\\n\"\n\t\t\t\t\t\"IKtfwVbyjWgvyy19wplXmYeOVr4zW/iH4VMhHWkfD0Gzvm5oofa93ZXrurQf6lMs\\n\"\n\t\t\t\t\t\"0RvTCB8+fS9t+tQL9Rf1zuBJ2HtcV46OPEM/+RtD1l6Hq9yFyoC1vjdFd69NefQR\\n\"\n\t\t\t\t\t\"+ox9EwztSXZ+omP2BWe3cl8UyPVNA22vdPEVz70Cc/d3JYk+ewAJ9L4FR0P2/czv\\n\"\n\t\t\t\t\t\"V3rB8EzRCYRqrYAv7eKVbatmogRQqx558sO0OVSapPPjqQlowczkfZ+eddrXjVK4\\n\"\n\t\t\t\t\t\"efRfI158kkSX/+nG3UeBtPpf6Za26EjXwbRmhushnPE9B+VreIogMzanAgMBAAEw\\n\"\n\t\t\t\t\t\"DQYJKoZIhvcNAQELBQADggGBAERSnFNwFxxvwksgwdZjFO7HUtupuAGmB+FU5TpP\\n\"\n\t\t\t\t\t\"R94zE2rm7yhT2EfmoqlSWL1yBEab3Qp39pQwygoVbgwlqEhytNo08QcqODRXtmBv\\n\"\n\t\t\t\t\t\"GsoEf6OVbr3NrFtuiqByxgX7tNTf3KdHEnJMBdT5PuB6f72ue3cvpOHOezrgY/t4\\n\"\n\t\t\t\t\t\"paLgLKXTCaZCOwSBFmMpZJWKH0stX1EANWktTHUoGsii3ai3PY2jG2NCP/fazwcK\\n\"\n\t\t\t\t\t\"SJbKHFwiOzfHHz9dHibnOMb7IDEmSE3P3newBPrMuxjcVcZFFcR8vz/vwVyffOB5\\n\"\n\t\t\t\t\t\"qbuD7YACB446v4m3CZ3wgXUdxqYqraaqksWZTanDm6WDKVpIdXlTOZi7XI3mbQpn\\n\"\n\t\t\t\t\t\"cS3KDJ93nfPnsfxSdDCThHaYwUDZJy8Q70sGSiewJOxeg8wCa5VnJN7r2wHZ1sQy\\n\"\n\t\t\t\t\t\"TtVkEgKRGCsCwchgJQIuNU3A5mBidYM1CP0O4idzmYfRSum1nHkuKuuwFWmCGZPl\\n\"\n\t\t\t\t\t\"B/MXftuzxxv65defj4e05UntzQ==\"\n\t\t\t\t\t\"-----END CERTIFICATE-----\\n\"},\n\t};\n\tsize_t i;\n\tfor (i = 0; i < countof(certs); i++)\n\t{\n\t\tif (!strcmp(certname, certs[i].name))\n\t\t{\n\t\t\t*size = strlen(certs[i].cert);\n\t\t\treturn certs[i].cert;\n\t\t}\n\t}\n#endif\n\treturn NULL;\n}\n#ifdef HAVE_SSL\nvoid *TLS_GetKnownCertificate(const char *certname, size_t *size)\n{\n\t//Note: This is XORed because of shitty scanners flagging binaries through false positive, flagging the sites that they were downloaded from, flagging binaries that contain references to those sites, and flagging any site that contains binaries.\n\t//the xor helps break that shitty recursive loop of mistrust from defects in other people's code.\n\t//at least until there's a sandbox that checks the dns resolutions for our update requests anyway.\n\t//I should probably just copy the downloadables file to sourceforge.\n\t//FIXME: we should be signing the content, not the sender. this SHOULD become redundant.\n\tstatic struct\n\t{\n\t\tqbyte *data;\n\t} knowncerts[] = {\n\t\t{\n\t\t/*updates.triptohell.info*/\"\\x8a\\x8f\\x9b\\x9e\\x8b\\x9a\\x8c\\xd1\\x8b\\x8d\\x96\\x8f\\x8b\\x90\\x97\\x9a\\x93\\x93\\xd1\\x96\\x91\\x99\\x90\\xff\\xc2\\x03\\xcf\\x7d\\xfc\\x41\\xcf\\x7d\\xfd\\x59\\x5f\\xfc\\xfd\\xfe\\xfd\\xfd\\xf6\\xff\\x0b\\xb1\\xec\\xf7\\xcd\\x7a\\x4a\\xe6\\xcf\\xf2\\xf9\\xf6\\xd5\\x79\\xb7\\x79\\x08\\xf2\\xfe\\xfe\\xf4\\xfa\\xff\\xcf\\x8b\\xce\\xf4\\xcf\\xf6\\xf9\\xfc\\xaa\\xfb\\xf9\\xec\\xfd\\xaa\\xb4\\xce\\xef\\xcf\\xf1\\xf9\\xfc\\xaa\\xfb\\xf7\\xf3\\xf8\\xba\\x91\\x98\\x93\\x9e\\x91\\x9b\\xce\\xf0\\xcf\\xf2\\xf9\\xfc\\xaa\\xfb\\xf8\\xf3\\xf9\\xb3\\x90\\x91\\x9b\\x90\\x91\\xce\\xf1\\xcf\\xf3\\xf9\\xfc\\xaa\\xfb\\xf5\\xf3\\xfa\\xb9\\xab\\xba\\xae\\xa8\\xce\\xef\\xcf\\xf1\\xf9\\xfc\\xaa\\xfb\\xf4\\xf3\\xf8\\xaa\\x8f\\x9b\\x9e\\x8b\\x9a\\x8c\\xce\\xdf\\xcf\\xe1\\xf9\\xfc\\xaa\\xfb\\xfc\\xf3\\xe8\\x8a\\x8f\\x9b\\x9e\\x8b\\x9a\\x8c\\xd1\\x8b\\x8d\\x96\\x8f\\x8b\\x90\\x97\\x9a\\x93\\x93\\xd1\\x96\\x91\\x99\\x90\\xcf\\xe1\\xe8\\xf2\\xce\\xc6\\xcf\\xca\\xcc\\xce\\xce\\xcf\\xcf\\xc6\\xce\\xc6\\xa5\\xe8\\xf2\\xcd\\xc6\\xcf\\xca\\xcd\\xc7\\xce\\xcf\\xcf\\xc6\\xce\\xc6\\xa5\\xcf\\x8b\\xce\\xf4\\xcf\\xf6\\xf9\\xfc\\xaa\\xfb\\xf9\\xec\\xfd\\xaa\\xb4\\xce\\xef\\xcf\\xf1\\xf9\\xfc\\xaa\\xfb\\xf7\\xf3\\xf8\\xba\\x91\\x98\\x93\\x9e\\x91\\x9b\\xce\\xf0\\xcf\\xf2\\xf9\\xfc\\xaa\\xfb\\xf8\\xf3\\xf9\\xb3\\x90\\x91\\x9b\\x90\\x91\\xce\\xf1\\xcf\\xf3\\xf9\\xfc\\xaa\\xfb\\xf5\\xf3\\xfa\\xb9\\xab\\xba\\xae\\xa8\\xce\\xef\\xcf\\xf1\\xf9\\xfc\\xaa\\xfb\\xf4\\xf3\\xf8\\xaa\\x8f\\x9b\\x9e\\x8b\\x9a\\x8c\\xce\\xdf\\xcf\\xe1\\xf9\\xfc\\xaa\\xfb\\xfc\\xf3\\xe8\\x8a\\x8f\\x9b\\x9e\\x8b\\x9a\\x8c\\xd1\\x8b\\x8d\\x96\\x8f\\x8b\\x90\\x97\\x9a\\x93\\x93\\xd1\\x96\\x91\\x99\\x90\\xcf\\x7d\\xfe\\xdd\\xcf\\xf2\\xf9\\xf6\\xd5\\x79\\xb7\\x79\\x08\\xf2\\xfe\\xfe\\xfe\\xfa\\xff\\xfc\\x7d\\xfe\\xf0\\xff\\xcf\\x7d\\xfe\\xf5\\xfd\\x7d\\xfe\\xfe\\xff\\x55\\x4a\\x63\\x33\\x17\\x42\\x52\\xe3\\x80\\x94\\xe3\\x36\\xfb\\x19\\xd3\\xef\\x53\\x66\\x14\\x98\\xf4\\x63\\xdb\\x47\\x6f\\x88\\x21\\x55\\xbb\\x5d\\xea\\xce\\x9e\\xad\\xb2\\x14\\x70\\xa9\\x47\\x50\\x3e\\xd0\\x99\\x22\\xaa\\x72\\x29\\x13\\x3c\\x5b\\x6c\\x73\\x79\\x14\\x50\\x76\\xe8\\xe6\\xd1\\x93\\x3d\\x2b\\x06\\x6d\\x53\\xd1\\x8c\\x66\\xa9\\x0d\\x3c\\x3b\\xeb\\xa9\\xb5\\xf2\\x41\\xae\\x36\\x70\\xb1\\x6d\\xdf\\xd4\\x51\\xfa\\xf3\\x81\\x78\\x5a\\xfd\\x1e\\x3f\\x82\\x8e\\x58\\xc7\\x8d\\xb8\\xc0\\xce\\xf8\\x6f\\x4f\\x92\\x30\\x51\\x49\\x24\\x14\\xc6\\x55\\xa0\\x4b\\x90\\xf3\\x9c\\xd5\\xde\\x9a\\xc9\\x55\\x94\\x53\\x68\\x49\\x41\\xdf\\x5b\\x78\\xc9\\x40\\xca\\x3a\\x59\\xce\\x1b\\x62\\x7a\\x0c\\x51\\x70\\x94\\x07\\xa6\\x8a\\xf0\\x4a\\xa2\\xce\\xbf\\xc6\\xd1\\x15\\xb7\\x9a\\x20\\x6e\\x1c\\xf9\\x04\\x4d\\x13\\x23\\x2f\\x6f\\x6b\\x29\\x97\\xb2\\x9d\\xda\\x65\\xc2\\x3c\\x8b\\xe8\\x82\\xf1\\x1d\\xe1\\xcb\\x40\\xfd\\x7a\\x3b\\xbf\\x77\\x6e\\x14\\x1f\\x0a\\x6d\\xa9\\xbd\\xb0\\x59\\xb3\\xe8\\x77\\x4d\\x76\\x2d\\x13\\x9f\\xab\\x68\\xdf\\xf5\\x35\\x0f\\x2e\\xcc\\xc0\\xa4\\x99\\x48\\x75\\xbd\\x8d\\x98\\x36\\xb8\\x7c\\x4c\\x2b\\xe1\\x57\\xbb\\x40\\xa5\\xe5\\x7a\\x86\\x11\\x07\\x7f\\x21\\xe6\\xe2\\x3a\\x22\\xaf\\xbd\\xef\\xe8\\x48\\x3c\\x2b\\x0e\\x34\\x75\\x47\\x8e\\xaa\\xce\\xfd\\xfc\\xfe\\xff\\xfe\\x5c\\xac\\xcf\\xae\\xcf\\xe2\\xf9\\xfc\\xaa\\xe2\\xf1\\xfb\\xe9\\xfb\\xeb\\xd3\\x97\\x7e\\x70\\xbf\\x73\\xbf\\xbd\\x60\\x42\\x3a\\xf4\\xc9\\x04\\x1d\\x89\\x14\\x72\\x4b\\x0c\\xcf\\xe0\\xf9\\xfc\\xaa\\xe2\\xdc\\xfb\\xe7\\xcf\\xe9\\x7f\\xeb\\xd3\\x97\\x7e\\x70\\xbf\\x73\\xbf\\xbd\\x60\\x42\\x3a\\xf4\\xc9\\x04\\x1d\\x89\\x14\\x72\\x4b\\x0c\\xcf\\xf0\\xf9\\xfc\\xaa\\xe2\\xec\\xfe\\xfe\\x00\\xfb\\xfa\\xcf\\xfc\\xfe\\xfe\\x00\\xcf\\xf2\\xf9\\xf6\\xd5\\x79\\xb7\\x79\\x08\\xf2\\xfe\\xfe\\xf4\\xfa\\xff\\xfc\\x7d\\xfe\\xfe\\xff\\xf3\\xfe\\xca\\xcd\\x47\\x18\\x69\\x45\\xc1\\xac\\x73\\x87\\xbe\\x54\\x64\\x80\\x1d\\x85\\x7f\\xa5\\x36\\x77\\xf9\\xd6\\xd7\\x06\\xaf\\x80\\x33\\x49\\x33\\xcb\\xfc\\xba\\xcd\\x86\\x9c\\x18\\x21\\x63\\xb9\\xd6\\x0b\\x50\\xcd\\x8d\\xd9\\xee\\x5f\\x84\\xad\\xdc\\xf5\\x2a\\xae\\x6e\\x86\\x0d\\xaf\\x9e\\x7f\\x8d\\xbf\\x18\\x7a\\x4f\\xec\\xe2\\x67\\x24\\xeb\\xdc\\xa6\\x5b\\x43\\x16\\x1f\\xe4\\x3f\\xc7\\xcc\\x69\\x43\\x44\\xa9\\xb8\\x33\\x42\\x17\\xbf\\xb6\\x20\\x55\\x9b\\x81\\xd6\\x1a\\x62\\xbf\\x5a\\xe5\\xa3\\xba\\xe0\\xa5\\x88\\xa6\\x01\\x85\\x47\\x07\\xb2\\x3b\\x64\\xce\\x19\\xf7\\x3b\\x6a\\x05\\x6e\\x70\\x6e\\x60\\xc3\\x3b\\x7d\\x46\\x0e\\x92\\x57\\x59\\x3b\\xf6\\x4e\\x16\\x57\\x9f\\x64\\x55\\xb3\\x86\\x0f\\x66\\x47\\x52\\x9c\\x4e\\x1b\\x3f\\x50\\x0f\\x20\\x36\\xcc\\xac\\xb2\\xf6\\x1b\\xc0\\x72\\x61\\xc7\\x39\\x6c\\x00\\x33\\x6e\\xb9\\x81\\x98\\xd7\\x9e\\x55\\x38\\xf4\\x1d\\x27\\x73\\x1b\\x13\\x72\\xbb\\x18\\x95\\xeb\\x87\\x6e\\x82\\x13\\x38\\xf8\\x12\\x36\\xa7\\x24\\xca\\x2b\\x8f\\xf9\\xf9\\xc6\\x72\\xb4\\x7f\\xd3\\x49\\x57\\x86\\xa3\\x6b\\xea\\x93\\xcb\\xf9\\xa3\\x28\\x3a\\xbd\\x3f\\x8d\\xfe\\x8e\\xf8\\x0a\\xda\\x95\\x2f\\xdb\\x79\\x32\\xe4\\xde\\xf8\\x51\\xbf\\x07\\x3e\\x1b\\xdc\\xf2\\x5f\\x3f\\xdc\\x0f\\xf8\\x45\\x23\\xcb\\xa2\\xb8\\x30\\xb4\\x84\\x2a\\xa2\",\n\t\t},\n\t};\n\n\tqbyte *r, *t;\n\tsize_t i, j, sz;\n\n\tfor (i = 0; i < countof(knowncerts); i++)\n\t{\n\t\tt = knowncerts[i].data;\n\t\tfor (j = 0; ; j++)\n\t\t{\n\t\t\tif (certname[j] != (t[j]^0xff))\n\t\t\t\tbreak;\n\t\t\tif (!certname[j])\n\t\t\t{\n\t\t\t\tj++;\n\t\t\t\tt+=j;\n\t\t\t\tsz = t[0] | (t[1]<<8);\n\t\t\t\tt += 2;\n\n\t\t\t\tr = BZ_Malloc(sz);\n\t\t\t\t*size = sz;\n\t\t\t\twhile(sz --> 0)\n\t\t\t\t\tr[sz] = t[sz]^0xff;\n\t\t\t\treturn r;\n\t\t\t}\n\t\t}\n\t}\n\n\t*size = 0;\n\t//return Z_StrDup(\"\"); //to force failure... gnutls debug code will dump out a cert that can be inserted above.\n\treturn NULL;\n}\n\nvfsfile_t *FS_OpenSSL(const char *peername, vfsfile_t *source, qboolean isserver)\n{\n\tint i;\n\tvfsfile_t *f = NULL;\n\tchar hostname[MAX_OSPATH];\n\n\tif (!source)\n\t\treturn NULL;\t//can happen if socket() fails.\n\n\tif (peername)\n\t{\n\t\t//clean up the name, stripping any port or other weirdness.\n\t\tif (!strncmp(peername, \"tls://\", 6))\n\t\t\tpeername+=6;\n\t\tif (*peername == '[')\n\t\t{\t//an ipv6 address, strip the brackets (and trailing port)\n\t\t\tQ_strncpyz(hostname, peername+1, sizeof(hostname));\n\t\t\tif (strchr(hostname, ']'))\n\t\t\t\t*strchr(hostname, ']') = 0;\n\t\t}\n\t\telse\n\t\t{\t//a hostname or ipv4 address, strip the port.\n\t\t\tQ_strncpyz(hostname, peername, sizeof(hostname));\n\t\t\tif (strchr(hostname, ':'))\n\t\t\t\t*strchr(hostname, ':') = 0;\n\t\t}\n\t}\n\telse\n\t\t*hostname = 0;\n\n\ti = tls_provider.ival-1;\n\tif (i>=0 && i < cryptolib_count && cryptolib[i])\n\t\tf = !cryptolib[i]->OpenStream?NULL:cryptolib[i]->OpenStream(hostname, source, isserver);\n\telse for (i = 0; !f && i < cryptolib_count; i++)\n\t{\n\t\tif (cryptolib[i] && cryptolib[i]->OpenStream)\n\t\t\tf = cryptolib[i]->OpenStream(hostname, source, isserver);\n\t}\n\tif (!f)\t//it all failed.\n\t{\n\t\tif (isserver && i < cryptolib_count && cryptolib[i] && cryptolib[i]->OpenStream)\n\t\t{\n\t\t\tCon_Printf(\"%s: no tls provider available. You may need to create a public certificate\\n\", peername?peername:\"<HOST>\");\n\t\t}\n\t\telse\n\t\t\tCon_Printf(\"%s: no tls provider available\\n\", peername);\n\t\tVFS_CLOSE(source);\n\t}\n\treturn f;\n}\nint TLS_GetChannelBinding(vfsfile_t *stream, qbyte *data, size_t *datasize)\n{\n\tint r = -1;\n\tint i;\n\tfor (i = 0; r==-1 && i < cryptolib_count; i++)\n\t{\n\t\tif (cryptolib[i] && cryptolib[i]->GetChannelBinding)\n\t\t\tr = cryptolib[i]->GetChannelBinding(stream, data, datasize);\n\t}\n\treturn r;\n}\n#endif\n\n/////////////////////////////////////////////\n//loopback stuff\n\n#if defined(HAVE_SERVER) && defined(HAVE_CLIENT)\n#define\tMAX_LOOPBACK\t64\ntypedef struct\n{\n\tqbyte\t*data;\n\tint\t\tdatalen;\n\tint\t\tdatamax;\n} loopmsg_t;\n\ntypedef struct\n{\n\tqboolean\tinited;\n\tloopmsg_t\tmsgs[MAX_LOOPBACK];\n\tint\t\t\tget, send;\n} loopback_t;\n\nstatic loopback_t\tloopbacks[2];\n\nstatic qboolean\tNET_GetLoopPacket (int sock, netadr_t *from, sizebuf_t *message)\n{\n\tint\t\ti;\n\tloopback_t\t*loop;\n\n\tsock &= 1;\n\n\tloop = &loopbacks[sock];\n\n\tif (loop->send - loop->get > MAX_LOOPBACK)\n\t{\n\t\textern cvar_t showdrop;\n\t\tif (showdrop.ival)\n\t\t\tCon_Printf(\"loopback dropping %i packets\\n\", (loop->send - MAX_LOOPBACK) - loop->get);\n\t\tloop->get = loop->send - MAX_LOOPBACK;\n\t}\n\n\tif (loop->get >= loop->send)\n\t\treturn false;\n\n\ti = loop->get & (MAX_LOOPBACK-1);\n\tloop->get++;\n\n\tif (message->maxsize < loop->msgs[i].datalen)\n\t\tSys_Error(\"NET_SendLoopPacket: Loopback buffer was too big\");\n\n\tmemcpy (message->data, loop->msgs[i].data, loop->msgs[i].datalen);\n\tmessage->cursize = loop->msgs[i].datalen;\n\tmemset (from, 0, sizeof(*from));\n\tfrom->type = NA_LOOPBACK;\n\tmessage->packing = SZ_RAWBYTES;\n\tmessage->currentbit = 0;\n\tloop->msgs[i].datalen = 0;\n\treturn true;\n\n}\n\n\nstatic neterr_t NET_SendLoopPacket (int sock, int length, const void *data, netadr_t *to)\n{\n\tint\t\ti;\n\tloopback_t\t*loop;\n\tif (!length && !data)\t//NET_EnsureRoute tests.\n\t\treturn NETERR_SENT;\n\tif (length > sizeof(net_message_buffer))\n\t\treturn NETERR_MTU;\t//reader probably won't be able to make sense of it if its too big...\n\n\tif (net_fakemtu.ival)\n\t\tif (length > abs(net_fakemtu.ival))\n\t\t\treturn (net_fakemtu.ival < 0)?NETERR_MTU:NETERR_SENT;\n\n\tsock &= 1;\n\n\tloop = &loopbacks[sock^1];\n\tif (!loop->inited)\n\t\treturn NETERR_NOROUTE;\n\n\ti = loop->send & (MAX_LOOPBACK-1);\n\tif (length > loop->msgs[i].datamax)\n\t{\n\t\tloop->msgs[i].datamax = length + 1024;\n\t\tBZ_Free(loop->msgs[i].data);\n\t\tloop->msgs[i].data = BZ_Malloc(loop->msgs[i].datamax);\n\t}\n\tif (loop->msgs[i].datalen)\n\t{\n\t\tstatic float throttle;\n\t\tCon_ThrottlePrintf(&throttle, 0, \"Warning: loopback queue overflow\\n\");\n\t}\n\n\tloop->send++;\n\n\tmemcpy (loop->msgs[i].data, data, length);\n\tloop->msgs[i].datalen = length;\n\treturn NETERR_SENT;\n}\n\nstatic int FTENET_Loop_GetLocalAddresses(struct ftenet_generic_connection_s *con, unsigned int *adrflags, netadr_t *addresses, const char **adrparams, int maxaddresses)\n{\n\tif (maxaddresses)\n\t{\n\t\taddresses->type = NA_LOOPBACK;\n\t\taddresses->port = con->thesocket+1;\n\t\t*adrflags = 0;\n\t\t*adrparams = NULL;\n\t\treturn 1;\n\t}\n\treturn 0;\n}\n\nstatic qboolean FTENET_Loop_GetPacket(ftenet_generic_connection_t *con)\n{\n\treturn NET_GetLoopPacket(con->thesocket, &net_from, &net_message);\n}\n\n#if defined(HAVE_PACKET) && !defined(HAVE_EPOLL)\n//just a null function so we don't pass bad things to select.\nstatic int FTENET_Loop_SetFDSets(ftenet_generic_connection_t *gcon, fd_set *readfdset, fd_set *writefdset)\n{\n\treturn 0;\n}\n#endif\n\nstatic neterr_t FTENET_Loop_SendPacket(ftenet_generic_connection_t *con, int length, const void *data, netadr_t *to)\n{\n\tif (to->type == NA_LOOPBACK)\n\t{\n\t\treturn NET_SendLoopPacket(con->thesocket, length, data, to);\n\t}\n\n\treturn NETERR_NOROUTE;\n}\n\nstatic void FTENET_Loop_Close(ftenet_generic_connection_t *con)\n{\n\tint i;\n\tint sock = con->thesocket;\n\tsock &= 1;\n\tloopbacks[sock].inited = false;\n\tloopbacks[sock].get = loopbacks[sock].send = 0;\n\tfor (i = 0; i < MAX_LOOPBACK; i++)\n\t{\n\t\tBZ_Free(loopbacks[sock].msgs[i].data);\n\t\tloopbacks[sock].msgs[i].data = NULL;\n\t\tloopbacks[sock].msgs[i].datalen = 0;\n\t\tloopbacks[sock].msgs[i].datamax = 0;\n\t}\n\tZ_Free(con);\n}\n\nstatic ftenet_generic_connection_t *FTENET_Loop_EstablishConnection(ftenet_connections_t *col, const char *address, netadr_t adr, const struct dtlspeercred_s *peerinfo)\n{\n\tftenet_generic_connection_t *newcon;\n\tint sock;\n\tfor (sock = 0; sock < countof(loopbacks); sock++)\n\t\tif (!loopbacks[sock].inited)\n\t\t\tbreak;\n\tif (sock == countof(loopbacks))\n\t\treturn NULL;\n\tnewcon = Z_Malloc(sizeof(*newcon));\n\tif (newcon)\n\t{\n\t\tloopbacks[sock].inited = true;\n\t\tloopbacks[sock].get = loopbacks[sock].send = 0;\n\n\t\tnewcon->GetLocalAddresses = FTENET_Loop_GetLocalAddresses;\n\t\tnewcon->GetPacket = FTENET_Loop_GetPacket;\n\t\tnewcon->SendPacket = FTENET_Loop_SendPacket;\n\t\tnewcon->Close = FTENET_Loop_Close;\n#if defined(HAVE_PACKET) && !defined(HAVE_EPOLL)\n\t\tnewcon->SetFDSets = FTENET_Loop_SetFDSets;\n#endif\n\n\t\tnewcon->islisten = col->islisten;\n\t\tnewcon->prot = adr.prot;\n\t\tnewcon->addrtype[0] = NA_LOOPBACK;\n\t\tnewcon->addrtype[1] = NA_INVALID;\n\n\t\tnewcon->thesocket = sock;\n\t}\n\treturn newcon;\n}\n#endif\n//=============================================================================\n\nftenet_connections_t *FTENET_CreateCollection(qboolean listen, void(*ReadPacket)(void))\n{\n\tftenet_connections_t *col;\n\tcol = Z_Malloc(sizeof(*col));\n\tcol->islisten = listen;\n\tcol->ReadGamePacket = ReadPacket;\n\treturn col;\n}\n#if defined(HAVE_CLIENT) && defined(HAVE_SERVER)\nstatic ftenet_generic_connection_t *FTENET_Loop_EstablishConnection(ftenet_connections_t *col, const char *address, netadr_t adr, const struct dtlspeercred_s *peerinfo);\n#endif\n#ifdef HAVE_PACKET\nftenet_generic_connection_t *FTENET_Datagram_EstablishConnection(ftenet_connections_t *col, const char *address, netadr_t adr, const struct dtlspeercred_s *peerinfo);\n#endif\n#ifdef TCPCONNECT\nstatic ftenet_generic_connection_t *FTENET_TCP_EstablishConnection(ftenet_connections_t *col, const char *address, netadr_t adr, const struct dtlspeercred_s *peerinfo);\n#endif\n#ifdef HAVE_WEBSOCKCL\nstatic ftenet_generic_connection_t *FTENET_WebSocket_EstablishConnection(ftenet_connections_t *col, const char *address, netadr_t adr, const struct dtlspeercred_s *peerinfo);\nstatic ftenet_generic_connection_t *FTENET_WebRTC_EstablishConnection(ftenet_connections_t *col, const char *address, netadr_t adr, const struct dtlspeercred_s *peerinfo);\n#endif\n#ifdef IRCCONNECT\nstatic ftenet_generic_connection_t *FTENET_IRCConnect_EstablishConnection(ftenet_connections_t *col, const char *address, netadr_t adr, const struct dtlspeercred_s *peerinfo);\n#endif\n#ifdef HAVE_NATPMP\nstatic ftenet_generic_connection_t *FTENET_NATPMP_EstablishConnection(ftenet_connections_t *col, const char *address, netadr_t adr, const struct dtlspeercred_s *peerinfo);\n#endif\n\n#ifdef HAVE_NATPMP\ntypedef struct\n{\n\tftenet_generic_connection_t pub;\n\tftenet_connections_t *col;\n\tnetadr_t reqpmpaddr;\n\tnetadr_t pmpaddr;\n\tnetadr_t natadr;\n\tunsigned int refreshtime;\n} pmpcon_t;\n\nint FTENET_NATPMP_GetLocalAddresses(struct ftenet_generic_connection_s *con, unsigned int *adrflags, netadr_t *addresses, const char **adrparams, int maxaddresses);\nstatic qboolean NET_Was_NATPMP(ftenet_connections_t *collection)\n{\n\tpmpcon_t *pmp;\n\tstruct\n\t{\n\t\tqbyte ver; qbyte op; short resultcode;\n\t\tint age;\n\t\tunion\n\t\t{\n\t\t\tstruct\n\t\t\t{\n\t\t\t\tshort privport; short pubport;\n\t\t\t\tint mapping_expectancy;\n\t\t\t};\n\t\t\tqbyte ipv4[4];\n\t\t};\n\t} *pmpreqrep;\n\tint i;\n\n\tfor (i = 0; i < MAX_CONNECTIONS; i++)\n\t{\n\t\tif (!collection->conn[i])\n\t\t\tcontinue;\n\t\tif (collection->conn[i]->GetLocalAddresses == FTENET_NATPMP_GetLocalAddresses)\n\t\t{\n\t\t\tpmp = (pmpcon_t*)collection->conn[i];\n\t\t\tif (NET_CompareAdr(&pmp->pmpaddr, &net_from))\n\t\t\t{\n\t\t\t\tpmpreqrep = (void*)net_message.data;\n\n\t\t\t\tif (pmpreqrep->ver != 0)\n\t\t\t\t\treturn false;\n\t\t\t\tif (net_message.cursize == 12 && pmpreqrep->op == 128)\n\t\t\t\t{\n\t\t\t\t\tchar adrbuf[256];\n\t\t\t\t\tpmp->natadr.type = NA_IP;\n\t\t\t\t\tpmp->natadr.port = 0;\n\t\t\t\t\tmemcpy(pmp->natadr.address.ip, pmpreqrep->ipv4, sizeof(pmp->natadr.address.ip));\n\t\t\t\t\tNET_AdrToString(adrbuf, sizeof(adrbuf), &pmp->natadr);\n\t\t\t\t\tpmp->natadr.connum = i+1;\n\t\t\t\t\tCon_DPrintf(\"NAT-PMP: Public ip is %s\\n\", adrbuf);\n\n#ifdef SUPPORT_ICE\n\t\t\t\t\tif (pmp->natadr.type && pmp->natadr.port)\n\t\t\t\t\t\tICE_AddLCandidateConn(collection, &pmp->natadr, ICE_SRFLX);\t//let ICE connections know about it\n#endif\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t\tif (net_message.cursize == 16 && pmpreqrep->op == 129)\n\t\t\t\t{\n\t\t\t\t\tswitch(BigShort(pmpreqrep->resultcode))\n\t\t\t\t\t{\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\tCon_Printf(\"NAT-PMP: unsupported version\\n\");\n\t\t\t\t\t\treturn true;\n\t\t\t\t\tcase 2:\n\t\t\t\t\t\tCon_Printf(\"NAT-PMP: refused - please reconfigure router\\n\");\n\t\t\t\t\t\treturn true;\n\t\t\t\t\tcase 3:\n\t\t\t\t\t\tCon_Printf(\"NAT-PMP: network failure\\n\");\n\t\t\t\t\t\treturn true;\n\t\t\t\t\tcase 4:\n\t\t\t\t\t\tCon_Printf(\"NAT-PMP: out of resources\\n\");\n\t\t\t\t\t\treturn true;\n\t\t\t\t\tcase 5:\n\t\t\t\t\t\tCon_Printf(\"NAT-PMP: unsupported opcode\\n\");\n\t\t\t\t\t\treturn true;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\n\t\t\t\t\tCon_DPrintf(\"NAT-PMP: Local port %u publicly available on port %u\\n\", (unsigned short)BigShort(pmpreqrep->privport), (unsigned short)BigShort(pmpreqrep->pubport));\n\t\t\t\t\tpmp->natadr.port = pmpreqrep->pubport;\n\n#ifdef SUPPORT_ICE\n\t\t\t\t\tif (pmp->natadr.type && pmp->natadr.port)\n\t\t\t\t\t\tICE_AddLCandidateConn(collection, &pmp->natadr, ICE_SRFLX);\n#endif\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t}\n\treturn false;\n}\n\nstatic void FTENET_NATPMP_Refresh(pmpcon_t *pmp, short oldport, ftenet_connections_t *collection)\n{\n\tint i, m;\n\tnetadr_t adr;\n\n\tnetadr_t\taddr[64];\n\tstruct ftenet_generic_connection_s\t\t\t*con[sizeof(addr)/sizeof(addr[0])];\n\tint\t\t\tflags[sizeof(addr)/sizeof(addr[0])];\n\tconst char *params[sizeof(addr)/sizeof(addr[0])];\n\n\tstruct\n\t{\n\t\tqbyte ver; qbyte op; short reserved1;\n\t\tshort privport; short pubport;\n\t\tint mapping_expectancy;\n\t} pmpreqmsg;\n\n\tpmpreqmsg.ver = 0;\n\tpmpreqmsg.op = 1;\n\tpmpreqmsg.reserved1 = BigShort(0);\n\tpmpreqmsg.privport = BigShort(0);\n\tpmpreqmsg.pubport = BigShort(0);\n\tpmpreqmsg.mapping_expectancy = BigLong(60*5);\n\n\tif (!collection)\n\t\treturn;\n\n\tm = NET_EnumerateAddresses(collection, con, flags, addr, params, sizeof(addr)/sizeof(addr[0]));\n\n\tfor (i = 0; i < m; i++)\n\t{\n\t\t//ignore any ips which are proxied by other people. that would be too weird.\n\t\tif (flags[i] & (ADDR_NATPMP|ADDR_UPNPIGP))\n\t\t\tcontinue;\n\n\t\tadr = addr[i];\n\n\t\t//unipv6ify it if its a hybrid socket.\n\t\tif (adr.type == NA_IPV6 &&\n\t\t\t!*(int*)&adr.address.ip6[0] &&\n\t\t\t!*(int*)&adr.address.ip6[4] &&\n\t\t\t!*(short*)&adr.address.ip6[8] &&\n\t\t\t*(short*)&adr.address.ip6[10]==(short)0xffff &&\n\t\t\t!*(int*)&adr.address.ip6[12])\n\t\t{\n\t\t\t*(int*)adr.address.ip = *(int*)&adr.address.ip6[12];\n\t\t\tadr.type = NA_IP;\n\t\t}\n\n\t\tif (adr.type == NA_IP)\n\t\t{\n\t\t\tif (adr.address.ip[0] == 127)\t//yes. loopback has a lot of ip addresses. wasteful but whatever.\n\t\t\t\tcontinue;\n\n\t\t\t//assume a netmask of 255.255.255.0\n\t\t\tadr.address.ip[3] = 1;\n\t\t}\n//\t\telse if (adr.type == NA_IPV6)\n//\t\t{\n//\t\t}\n\t\telse\n\t\t\tcontinue;\n\n\t\tpmpreqmsg.privport = adr.port;\n\t\tpmpreqmsg.pubport = oldport?oldport:adr.port;\n\n\t\tif (*(int*)pmp->reqpmpaddr.address.ip == INADDR_ANY)\n\t\t{\n\t\t\tpmp->pmpaddr = adr;\n\t\t\tpmp->pmpaddr.port = pmp->reqpmpaddr.port;\n\t\t}\n\t\telse\n\t\t\tpmp->pmpaddr = pmp->reqpmpaddr;\n\n\t\tif (*(int*)pmp->pmpaddr.address.ip == INADDR_ANY)\n\t\t\tcontinue;\n\n\t\t//get the public ip.\n\t\tpmpreqmsg.op = 0;\n\t\tNET_SendPacket(collection, 2, &pmpreqmsg, &pmp->pmpaddr);\n\n\t\t//open the firewall/nat.\n\t\tpmpreqmsg.op = 1;\n\t\tNET_SendPacket(collection, sizeof(pmpreqmsg), &pmpreqmsg, &pmp->pmpaddr);\n\n\t\tbreak;\n\t}\n}\n#define PMP_POLL_TIME (1000*30)//every 30 seconds\nqboolean Net_OpenUDPPort(char *privateip, int privateport, char *publicip, size_t publiciplen, int *publicport);\nint FTENET_NATPMP_GetLocalAddresses(struct ftenet_generic_connection_s *con, unsigned int *adrflags, netadr_t *addresses, const char **adrparams, int maxaddresses)\n{\n\tpmpcon_t *pmp = (pmpcon_t*)con;\n/*\n\tchar pubip[256];\n\tint pubport;\n\n\tif (Net_OpenUDPPort(\"192.168.1.4\", 27500, pubip, sizeof(pubip), &pubport))\n\t{\n\t\t*adrflags = ADDR_UPNPIGP;\n\t\tNET_StringToAdr(pubip, pubport, addresses);\n\t\treturn 1;\n\t}\n*/\n\tif (maxaddresses)\n\t{\n\t\t*adrflags = ADDR_NATPMP;\n\t\t*adrparams = NULL;\n\t\t*addresses = pmp->natadr;\n\t\treturn (pmp->natadr.type != NA_INVALID) && (pmp->natadr.port != 0);\n\t}\n\treturn 0;\n}\nqboolean FTENET_NATPMP_GetPacket(struct ftenet_generic_connection_s *con)\n{\n\tpmpcon_t *pmp = (pmpcon_t*)con;\n\tunsigned int now = Sys_Milliseconds();\n\tif (now - pmp->refreshtime > PMP_POLL_TIME)\t//weird logic to cope with wrapping\n\t{\n\t\tpmp->refreshtime = now;\n\t\tFTENET_NATPMP_Refresh(pmp, pmp->natadr.port, pmp->col);\n\t}\n\treturn false;\n}\nneterr_t FTENET_NATPMP_SendPacket(struct ftenet_generic_connection_s *con, int length, const void *data, netadr_t *to)\n{\n\treturn NETERR_NOROUTE;\n}\nvoid FTENET_NATPMP_Close(struct ftenet_generic_connection_s *con)\n{\n\t//FIXME: we should send a packet to close the port\n\tZ_Free(con);\n}\n//qboolean Net_OpenUDPPort(char *privateip, int privateport, char *publicip, size_t publiciplen, int *publicport);\nftenet_generic_connection_t *FTENET_NATPMP_EstablishConnection(ftenet_connections_t *col, const char *address, netadr_t pmpadr, const struct dtlspeercred_s *peerinfo)\n{\n\tpmpcon_t *pmp;\n\n\tif (pmpadr.prot == NP_NATPMP)\n\t\tpmpadr.prot = NP_DGRAM;\n\tif (pmpadr.type != NA_IP)\n\t\treturn NULL;\n\n\tpmp = Z_Malloc(sizeof(*pmp));\n\tpmp->col = svs.sockets;\n\tQ_strncpyz(pmp->pub.name, \"natpmp\", sizeof(pmp->pub.name));\n\tpmp->reqpmpaddr = pmpadr;\n\tpmp->pub.GetLocalAddresses = FTENET_NATPMP_GetLocalAddresses;\n\tpmp->pub.GetPacket = FTENET_NATPMP_GetPacket;\n\t//qboolean (*ChangeLocalAddress)(struct ftenet_generic_connection_s *con, const char *newaddress);\n\tpmp->pub.SendPacket = FTENET_NATPMP_SendPacket;\n\tpmp->pub.Close = FTENET_NATPMP_Close;\n\tpmp->pub.thesocket = INVALID_SOCKET;\n\n\tpmp->refreshtime = Sys_Milliseconds() + PMP_POLL_TIME*64;\n\n//\tNet_OpenUDPPort();\n\n\treturn &pmp->pub;\n}\n#endif\n\n#ifdef HAVE_DTLS\nstruct dtlspeer_s\n{\n\tconst dtlsfuncs_t *funcs;\n\n\tftenet_connections_t *col;\n\tvoid *dtlsstate;\n\tnetadr_t\taddr;\t//underlying address\n\tnetproto_t\tprot;\t//layered protocol\n\n\tfloat timeout;\n\n\tstruct dtlspeer_s *next;\n\tstruct dtlspeer_s **link;\n};\n\nstatic void NET_DTLS_Timeouts(ftenet_connections_t *col)\n{\n\tstruct dtlspeer_s *peer, **link;\n\tif (!col)\n\t\treturn;\n\tfor (link = &col->dtls; (peer=*link); )\n\t{\n\t\tif (peer->timeout < realtime)\n\t\t{\n\t\t\tpeer->funcs->DestroyContext(peer->dtlsstate);\n\t\t\t*link = peer->next;\n\t\t\tcontinue;\n\t\t}\n\n\t\tpeer->funcs->Timeouts(peer->dtlsstate);\n\t\tlink = &peer->next;\n\t}\n}\n\nconst dtlsfuncs_t *DTLS_InitServer(void)\n{\n\tconst dtlsfuncs_t *f = NULL;\n\tint i;\n\tconst char *provname;\n\tif (tls_provider.ival>0 && tls_provider.ival <= cryptolib_count && cryptolib[tls_provider.ival-1])\n\t{\n\t\tf = !cryptolib[tls_provider.ival-1]->DTLS_InitServer?NULL:cryptolib[tls_provider.ival-1]->DTLS_InitServer();\n\t\tprovname = cryptolib[tls_provider.ival-1]->drivername;\n\n\t\tif (!f)\n\t\t\tCon_Printf(\"DTLS provider %s failed\\n\", provname);\n\t}\n\telse for (i = 0; !f && i < cryptolib_count; i++)\n\t{\n\t\tif (cryptolib[i] && cryptolib[i]->DTLS_InitServer)\n\t\t{\n\t\t\tf = cryptolib[i]->DTLS_InitServer();\n\t\t\tprovname = cryptolib[i]->drivername;\n\t\t\tif (!f)\n\t\t\t\tCon_Printf(\"DTLS provider %s failed\\n\", provname);\n\t\t}\n\t}\n\tif (f)\n\t\tCon_DPrintf(\"Using DTLS provider %s\\n\", provname);\n\treturn f;\n}\nconst dtlsfuncs_t *DTLS_InitClient(void)\n{\n\tconst dtlsfuncs_t *f = NULL;\n\tint i;\n\tif (tls_provider.ival>0 && tls_provider.ival <= cryptolib_count && cryptolib[tls_provider.ival-1])\n\t{\n\t\tf = !cryptolib[tls_provider.ival-1]->DTLS_InitClient?NULL:cryptolib[tls_provider.ival-1]->DTLS_InitClient();\n\t\tif (!f)\n\t\t\tCon_Printf(\"DTLS provider %s failed\\n\", cryptolib[tls_provider.ival-1]->drivername);\n\t}\n\telse for (i = 0; !f && i < cryptolib_count; i++)\n\t{\n\t\tif (cryptolib[i] && cryptolib[i]->DTLS_InitClient)\n\t\t{\n\t\t\tf = cryptolib[i]->DTLS_InitClient();\n\t\t\tif (!f)\n\t\t\t\tCon_DPrintf(\"DTLS provider %s failed\\n\", cryptolib[i]->drivername);\n\t\t}\n\t}\n\treturn f;\n}\n\nstatic neterr_t NET_SendPacketCol (ftenet_connections_t *collection, int length, const void *data, netadr_t *to);\nstatic neterr_t FTENET_DTLS_DoSendPacket(void *cbctx, const qbyte *data, size_t length)\n{\t//callback that does the actual sending\n\tstruct dtlspeer_s *peer = cbctx;\n\treturn NET_SendPacketCol(peer->col, length, data, &peer->addr);\n}\nqboolean NET_DTLS_Create(ftenet_connections_t *col, netadr_t *to, const dtlscred_t *cred, qboolean outgoing)\n{\n\textern cvar_t timeout;\n\tstruct dtlspeer_s *peer;\n\tif (to->prot != NP_DGRAM)\n\t\treturn false;\n\tfor (peer = col->dtls; peer; peer = peer->next)\n\t{\n\t\tif (NET_CompareAdr(&peer->addr, to))\n\t\t\tbreak;\n\t}\n\tif (!peer)\n\t{\n\t\tpeer = Z_Malloc(sizeof(*peer));\n\t\tpeer->addr = *to;\n\t\tpeer->prot = NP_DTLS;\n\t\tpeer->col = col;\n\n\t\tif (outgoing)\n\t\t\tpeer->funcs = DTLS_InitClient();\n\t\telse\n\t\t\tpeer->funcs = DTLS_InitServer();\n\t\tif (peer->funcs)\n\t\t\tpeer->dtlsstate = peer->funcs->CreateContext(cred, peer, FTENET_DTLS_DoSendPacket, !outgoing);\n\n\t\tpeer->timeout = realtime+timeout.value;\n\t\tif (peer->dtlsstate)\n\t\t{\n\t\t\tpeer->link = &col->dtls;\n\t\t\tpeer->next = col->dtls;\n\t\t\tif (peer->next)\n\t\t\t\tpeer->next->link = &peer->next;\n\t\t\tcol->dtls = peer;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tZ_Free(peer);\n\t\t\tpeer = NULL;\n\t\t}\n\t}\n\telse\n\t\tpeer->timeout = realtime+timeout.value;\n\treturn peer!=NULL;\n}\n#ifdef HAVE_SERVER\nstatic void FTENET_DTLS_Established(void **ctx, void *state)\n{\n\tftenet_connections_t *col;\n\tstruct dtlspeer_s *peer = Z_Malloc(sizeof(*peer));\n\tmemcpy(peer, *ctx, sizeof(*peer));\n\t*ctx = peer;\n\tcol = peer->col;\n\n\tpeer->dtlsstate = state;\n\n\tpeer->timeout = realtime+timeout.value;\n\tpeer->link = &col->dtls;\n\tpeer->next = col->dtls;\n\tif (peer->next)\n\t\tpeer->next->link = &peer->next;\n\tcol->dtls = peer;\n}\nqboolean NET_DTLS_CheckInbound(ftenet_connections_t *col)\n{\n\textern cvar_t timeout;\n\tstruct dtlspeer_s *peer;\n\tnetadr_t *from = &net_from;\n\tif (from->prot != NP_DGRAM || !net_enable_dtls.ival || !col->dtlsfuncs)\n\t\treturn false;\n\tif (!net_message.cursize || !(20 <= net_message.data[0] && net_message.data[0] <= 63))\n\t\treturn false;\t//lead byte must be between 20 and 63 to be valid dtls.\n\tfor (peer = col->dtls; peer; peer = peer->next)\n\t{\n\t\tif (NET_CompareAdr(&peer->addr, from))\n\t\t\tbreak;\n\t}\n\tif (!peer)\n\t{\n\t\tif (col->dtlsfuncs->CheckConnection)\n\t\t{\n\t\t\tstruct dtlspeer_s peer = {col->dtlsfuncs};\n\t\t\t//fill it with preliminary info\n\t\t\tpeer.addr = *from;\n\t\t\tpeer.col = col;\n\t\t\tpeer.prot = NP_DTLS;\n\t\t\treturn col->dtlsfuncs->CheckConnection(&peer, from, sizeof(*from), net_message.data, net_message.cursize, FTENET_DTLS_DoSendPacket, FTENET_DTLS_Established);\n\t\t}\n\t}\n\treturn false;\n}\n#endif\nstatic void NET_DTLS_DisconnectPeer(ftenet_connections_t *col, struct dtlspeer_s *peer)\n{\n//\tSys_Printf(\"Destroy %p\\n\", peer->dtlsstate);\n\tif (peer->next)\n\t\tpeer->next->link = peer->link;\n\t*peer->link = peer->next;\n\n\tpeer->funcs->DestroyContext(peer->dtlsstate);\n\tZ_Free(peer);\n}\nstatic struct dtlspeer_s *FTENET_DTLS_FindPeer(ftenet_connections_t *col, netadr_t *to)\n{\n\tstruct dtlspeer_s *peer;\n\tfor (peer = col->dtls; peer; peer = peer->next)\n\t{\n\t\tif (NET_CompareAdr(&peer->addr, to))\n\t\t\tbreak;\n\t}\n\treturn peer;\n}\nqboolean NET_DTLS_Disconnect(ftenet_connections_t *col, netadr_t *to)\n{\n\tstruct dtlspeer_s *peer;\n\tnetadr_t n = *to;\n\tif (!col || (to->prot != NP_DGRAM && !NP_ISLAYERED(to->prot)))\n\t\treturn false;\n\tn.prot = NP_DGRAM;\n\tpeer = FTENET_DTLS_FindPeer(col, &n);\n\tif (!peer)\n\t\treturn false;\n\tNET_DTLS_DisconnectPeer(col, peer);\n\treturn true;\n}\nstatic neterr_t FTENET_DTLS_SendPacket(ftenet_connections_t *col, int length, const void *data, netadr_t *to)\n{\n\tnetproto_t np = to->prot;\n\tstruct dtlspeer_s *peer;\n\tto->prot = NP_DGRAM;\n\tpeer = FTENET_DTLS_FindPeer(col, to);\n\tto->prot = np;\n\tif (peer)\n\t\treturn peer->funcs->Transmit(peer->dtlsstate, data, length);\n\telse\n\t\treturn NETERR_NOROUTE;\n}\n\nqboolean NET_DTLS_Decode(ftenet_connections_t *col)\n{\n\textern cvar_t timeout;\n\tstruct dtlspeer_s *peer = FTENET_DTLS_FindPeer(col, &net_from);\n\tif (peer)\n\t{\n\t\tpeer->timeout = realtime+timeout.value;\t//refresh the timeout if our peer is still alive.\n\t\tswitch(peer->funcs->Received(peer->dtlsstate, &net_message))\n\t\t{\n\t\tcase NETERR_DISCONNECTED:\n\t\t\tif (col->islisten)\n\t\t\t\tNET_DTLS_DisconnectPeer(col, peer);\n\t\t\tnet_message.cursize = 0;\n\t\t\tbreak;\n\t\tcase NETERR_NOROUTE:\n\t\t\treturn false;\t//not a valid dtls packet.\n\t\tdefault:\n\t\tcase NETERR_CLOGGED:\n\t\t\t//ate it\n\t\t\tnet_message.cursize = 0;\n\t\t\tbreak;\n\t\tcase NETERR_SENT:\n\t\t\t//we decoded it properly\n\t\t\tnet_from.prot = peer->prot;\n\t\t\tbreak;\n\t\t}\n\t\tnet_from.prot = peer->prot;\n\t\treturn true;\n\t}\n\treturn false;\n}\n#endif\n\n\nint NET_GetConnectionCertificate(struct ftenet_connections_s *col, netadr_t *a, enum certprops_e prop, char *out, size_t outsize)\n{\n\tif (!col)\n\t\treturn -1;\n\n#ifdef SUPPORT_ICE\n\tif (a->type == NA_ICE)\n\t\treturn ICE_GetPeerCertificate(a, prop, out, outsize);\n#endif\n#ifdef HAVE_DTLS\n\tif (NP_ISLAYERED(a->prot))\n\t{\n\t\tnetproto_t np = a->prot;\n\t\tstruct dtlspeer_s *peer;\n\t\ta->prot = NP_DGRAM;\n\t\tpeer = FTENET_DTLS_FindPeer(col, a);\n\t\ta->prot = np;\n\t\tif (peer && peer->funcs->GetPeerCertificate)\n\t\t\treturn peer->funcs->GetPeerCertificate(peer->dtlsstate, prop, out, outsize);\n\t}\n#endif\n\treturn -1;\n}\n\n\n\n\nstatic qboolean FTENET_AddToCollection_Ptr(ftenet_connections_t *col, const char *name, const char *address, netadr_t *adr, const struct dtlspeercred_s *peerinfo)\n{\n\tint count = 0;\n\tint i;\n\tif (!col)\n\t\treturn false;\n\n\tif (name)\n\t{\n\t\tfor (i = 0; i < MAX_CONNECTIONS; i++)\n\t\t{\n\t\t\tif (col->conn[i])\n\t\t\tif (*col->conn[i]->name && !strcmp(col->conn[i]->name, name))\n\t\t\t{\n\t\t\t\tif (adr && (adr->type != NA_INVALID||adr->prot != NP_INVALID) && col->islisten)\n\t\t\t\tif (col->conn[i]->ChangeLocalAddress)\n\t\t\t\t{\n\t\t\t\t\tif (col->conn[i]->ChangeLocalAddress(col->conn[i], address, adr))\n\t\t\t\t\t\treturn true;\n\t\t\t\t}\n\n\t\t\t\tcol->conn[i]->Close(col->conn[i]);\n\t\t\t\tcol->conn[i] = NULL;\n\t\t\t}\n\t\t}\n\t}\n\n\tif (adr)\n\t{\n\t\t//FIXME: combine with urischemes somehow\n\t\tftenet_generic_connection_t *(*establish)(ftenet_connections_t *col, const char *address, netadr_t adr, const struct dtlspeercred_s *peerinfo);\n#ifdef HAVE_WEBSOCKCL\n\t\tif (adr->prot == NP_WS && adr->type == NA_WEBSOCKET)\testablish = FTENET_WebSocket_EstablishConnection; else\n\t\tif (adr->prot == NP_WSS && adr->type == NA_WEBSOCKET)\testablish = FTENET_WebSocket_EstablishConnection; else\n\t\tif (adr->prot == NP_RTC_TCP)\t\t\t\t\t\t\testablish = FTENET_WebRTC_EstablishConnection; else\n\t\tif (adr->prot == NP_RTC_TLS)\t\t\t\t\t\t\testablish = FTENET_WebRTC_EstablishConnection; else\n#endif\n#ifdef HAVE_NATPMP\n\t\tif (adr->prot == NP_NATPMP && adr->type == NA_IP)\t\testablish = FTENET_NATPMP_EstablishConnection; else\n#endif\n#if defined(HAVE_CLIENT) && defined(HAVE_SERVER)\n\t\tif (adr->prot == NP_DGRAM && adr->type == NA_LOOPBACK)\testablish = FTENET_Loop_EstablishConnection; else\n#endif\n#ifdef HAVE_IPV4\n\t\tif ((adr->prot == NP_DGRAM) && adr->type == NA_IP)\t\testablish = FTENET_Datagram_EstablishConnection;\telse\n#endif\n#ifdef HAVE_IPV6\n\t\tif ((adr->prot == NP_DGRAM) && adr->type == NA_IPV6)\testablish = FTENET_Datagram_EstablishConnection;\telse\n#endif\n#ifdef HAVE_IPX\n\t\tif (adr->prot == NP_DGRAM && adr->type == NA_IPX)\t\testablish = FTENET_Datagram_EstablishConnection;\telse\n#endif\n#ifdef UNIXSOCKETS\n\t\tif (adr->prot == NP_DGRAM && adr->type == NA_UNIX)\t\testablish = FTENET_Datagram_EstablishConnection;\telse\n\t#if defined(TCPCONNECT)\n\t\tif (adr->prot == NP_STREAM&& adr->type == NA_UNIX)\t\testablish = FTENET_TCP_EstablishConnection;\telse\n\t\tif (adr->prot == NP_WS    && adr->type == NA_UNIX)\t\testablish = FTENET_TCP_EstablishConnection;\telse\n\t\tif (adr->prot == NP_TLS    && adr->type == NA_UNIX)\t\testablish = FTENET_TCP_EstablishConnection;\telse\n\t#endif\n#endif\n#if defined(TCPCONNECT) && defined(HAVE_IPV4)\n\t\tif (adr->prot == NP_WS\t&& adr->type == NA_IP)\t\t\testablish = FTENET_TCP_EstablishConnection;\telse\n\t\tif (adr->prot == NP_WSS\t&& adr->type == NA_IP)\t\t\testablish = FTENET_TCP_EstablishConnection;\telse\n\t\tif (adr->prot == NP_STREAM&& adr->type == NA_IP)\t\testablish = FTENET_TCP_EstablishConnection;\telse\n\t\tif (adr->prot == NP_TLS\t&& adr->type == NA_IP)\t\t\testablish = FTENET_TCP_EstablishConnection;\telse\n#endif\n#if defined(TCPCONNECT) && defined(HAVE_IPV6)\n\t\tif (adr->prot == NP_WS\t&& adr->type == NA_IPV6)\t\testablish = FTENET_TCP_EstablishConnection;\telse\n\t\tif (adr->prot == NP_WSS\t&& adr->type == NA_IPV6)\t\testablish = FTENET_TCP_EstablishConnection;\telse\n\t\tif (adr->prot == NP_STREAM&& adr->type == NA_IPV6)\t\testablish = FTENET_TCP_EstablishConnection;\telse\n\t\tif (adr->prot == NP_TLS\t&& adr->type == NA_IPV6)\t\testablish = FTENET_TCP_EstablishConnection;\telse\n#endif\n#ifdef SUPPORT_ICE\n\t\tif (adr->prot == NP_RTC_TCP)\t\t\t\t\t\t\testablish = FTENET_ICE_EstablishConnection;\telse\n\t\tif (adr->prot == NP_RTC_TLS)\t\t\t\t\t\t\testablish = FTENET_ICE_EstablishConnection;\telse\n#endif\n#ifdef IRCCONNECT\n\t\tif (adr->prot == NP_TLS && adr->type == NA_IRC)\t\t\testablish = FTENET_IRCConnect_EstablishConnection;\telse\n#endif\n\t\t\testablish = NULL;\n\n\t\tif (establish)\n\t\t{\n\t\t\tfor (i = 0; i < MAX_CONNECTIONS; i++)\n\t\t\t{\n\t\t\t\tif (!col->conn[i])\n\t\t\t\t{\n\t\t\t\t\tcol->conn[i] = establish(col, address, *adr, peerinfo);\n\t\t\t\t\tif (!col->conn[i])\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcol->conn[i]->connum = i+1;\n\t\t\t\t\tif (name)\n\t\t\t\t\t\tQ_strncpyz(col->conn[i]->name, name, sizeof(col->conn[i]->name));\n\t\t\t\t\tcount++;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn count > 0;\n}\nqboolean FTENET_AddToCollection(ftenet_connections_t *col, const char *name, const char *addresslist, netadrtype_t addrtype, netproto_t addrprot)\n{\n\tnetadr_t adr[8];\n\tchar address[countof(adr)][256];\n\tunsigned int i, j;\n\tqboolean success = false;\n\tif (!col)\n\t\treturn false;\n\tif (name && strchr(name, ':'))\n\t\treturn false;\n\tfor (i = 0; addresslist && *addresslist && i < countof(adr); i++)\n\t{\n\t\taddresslist = COM_ParseStringSet(addresslist, address[i], sizeof(address[i]));\n\t\t//resolve the address to something sane so we can determine the address type and thus the connection type to use\n\t\tif (!*address[i])\n\t\t\tadr[i].type = NA_INVALID, adr[i].prot = NP_INVALID;\n\t\telse //if (islisten)\n\t\t{\n\t\t\tif (!NET_PortToAdr(addrtype, addrprot, address[i], &adr[i]))\n\t\t\t\treturn false;\n\t\t}\n\t}\n\tif (i == 1)\n\t{\n\t\tsuccess |= FTENET_AddToCollection_Ptr(col, name, address[0], &adr[0], NULL);\n\t\ti = 0;\n\t}\n\telse\n\t\tsuccess |= FTENET_AddToCollection_Ptr(col, name, NULL, NULL, NULL);\n\n\tfor (j = 0; j < i; j++)\n\t\tsuccess |= FTENET_AddToCollection_Ptr(col, va(\"%s:%i\", name, j), address[j], &adr[j], NULL);\n\tfor (; j < countof(adr); j++)\n\t\tsuccess |= FTENET_AddToCollection_Ptr(col, va(\"%s:%i\", name, j), NULL, NULL, NULL);\n\treturn success;\n}\n\nvoid FTENET_CloseCollection(ftenet_connections_t *col)\n{\n\tint i;\n\tif (!col)\n\t\treturn;\n\tfor (i = 0; i < MAX_CONNECTIONS; i++)\n\t{\n\t\tif (col->conn[i])\n\t\t{\n\t\t\tcol->conn[i]->Close(col->conn[i]);\n\t\t\tcol->conn[i] = NULL;\n\t\t}\n\t}\n\tZ_Free(col);\n}\n\n#if defined(_WIN32) && defined(HAVE_PACKET)\nint FTENET_GetLocalAddress(int port, qboolean ipx, qboolean ipv4, qboolean ipv6,   unsigned int *adrflags, netadr_t *addresses, const char **adrparams, int maxaddresses)\n{\n\t//in win32, we can look up our own hostname to retrieve a list of local interface addresses.\n\tchar\t\tadrs[MAX_ADR_SIZE];\n\tint found = 0;\n\n\tgethostname(adrs, sizeof(adrs));\n#ifndef pgetaddrinfo\n\tif (!pgetaddrinfo)\n\t{\n\t\tstruct hostent *h = gethostbyname(adrs);\n\t\tint b = 0;\n#ifdef HAVE_IPV4\n\t\tif(h && h->h_addrtype == AF_INET)\n\t\t{\n\t\t\tfor (b = 0; h->h_addr_list[b] && maxaddresses; b++)\n\t\t\t{\n\t\t\t\tstruct sockaddr_in from;\n\t\t\t\tfrom.sin_family = AF_INET;\n\t\t\t\tfrom.sin_port = port;\n\t\t\t\tmemcpy(&from.sin_addr, h->h_addr_list[b], sizeof(from.sin_addr));\n\t\t\t\tSockadrToNetadr((struct sockaddr_qstorage*)&from, sizeof(from), addresses);\n\n\t\t\t\t*adrflags++ = 0;\n\t\t\t\t*adrparams++ = NULL;\n\t\t\t\taddresses++;\n\t\t\t\tmaxaddresses--;\n\t\t\t\tfound++;\n\t\t\t}\n\t\t}\n#endif\n#ifdef HAVE_IPV6\n\t\tif(h && h->h_addrtype == AF_INET6)\n\t\t{\n\t\t\tfor (b = 0; h->h_addr_list[b] && maxaddresses; b++)\n\t\t\t{\n\t\t\t\tstruct sockaddr_in6 from;\n\t\t\t\tfrom.sin6_family = AF_INET6;\n\t\t\t\tfrom.sin6_port = port;\n\t\t\t\tfrom.sin6_scope_id = 0;\n\t\t\t\tmemcpy(&from.sin6_addr, h->h_addr_list[b], sizeof(((struct sockaddr_in6*)&from)->sin6_addr));\n\t\t\t\tSockadrToNetadr((struct sockaddr_qstorage*)&from, sizeof(from), addresses);\n\t\t\t\t*adrflags++ = 0;\n\t\t\t\t*adrparams++ = NULL;\n\t\t\t\taddresses++;\n\t\t\t\tmaxaddresses--;\n\t\t\t\tfound++;\n\t\t\t}\n\t\t}\n#endif\n\t}\n\telse\n#endif\n\t{\n\t\tstruct addrinfo hints, *result, *itr;\n\t\tmemset(&hints, 0, sizeof(struct addrinfo));\n\t\thints.ai_family = 0;    /* Allow IPv4 or IPv6 */\n\t\thints.ai_socktype = SOCK_DGRAM; /* Datagram socket */\n\t\thints.ai_flags = 0;\n\t\thints.ai_protocol = 0;          /* Any protocol */\n\n\t\tif (pgetaddrinfo(adrs, NULL, &hints, &result) == 0)\n\t\t{\n\t\t\tfor (itr = result; itr; itr = itr->ai_next)\n\t\t\t{\n\t\t\t\tif (0\n#ifdef HAVE_IPV4\n\t\t\t\t\t|| (itr->ai_addr->sa_family == AF_INET && ipv4)\n#endif\n#ifdef HAVE_IPV6\n\t\t\t\t\t|| (itr->ai_addr->sa_family == AF_INET6 && ipv6)\n#endif\n#ifdef HAVE_IPX\n\t\t\t\t\t|| (itr->ai_addr->sa_family == AF_IPX && ipx)\n#endif\n\t\t\t\t\t)\n\t\t\t\tif (maxaddresses)\n\t\t\t\t{\n\t\t\t\t\tSockadrToNetadr((struct sockaddr_qstorage*)itr->ai_addr, sizeof(struct sockaddr_qstorage), addresses);\n\t\t\t\t\taddresses->port = port;\n\t\t\t\t\t*adrflags++ = 0;\n\t\t\t\t\t*adrparams++ = NULL;\n\t\t\t\t\taddresses++;\n\t\t\t\t\tmaxaddresses--;\n\t\t\t\t\tfound++;\n\t\t\t\t}\n\t\t\t}\n\t\t\tfreeaddrinfo(result);\n\n\t\t\t/*if none found, fill in the 0.0.0.0 or whatever*/\n\t\t\tif (!found && maxaddresses)\n\t\t\t{\n\t\t\t\tmemset(addresses, 0, sizeof(*addresses));\n\t\t\t\taddresses->port = port;\n\t\t\t\tif (ipv6)\n\t\t\t\t\taddresses->type = NA_IPV6;\n\t\t\t\telse if (ipv4)\n\t\t\t\t\taddresses->type = NA_IP;\n\t\t\t\telse if (ipx)\n\t\t\t\t\taddresses->type = NA_IPX;\n\t\t\t\telse\n\t\t\t\t\taddresses->type = NA_INVALID;\n\t\t\t\t*adrflags++ = 0;\n\t\t\t\t*adrparams++ = NULL;\n\t\t\t\taddresses++;\n\t\t\t\tmaxaddresses--;\n\t\t\t\tfound++;\n\t\t\t}\n\t\t}\n\t}\n\treturn found;\n}\n\n#elif defined(__linux__) && !defined(ANDROID)\n//in linux, looking up our own hostname to retrieve a list of local interface addresses will give no indication that other systems are able to do the same thing and is thus not supported.\n//there's some special api instead\n//glibc 2.3.\n//also available with certain bsds, I'm but unsure which preprocessor we can use.\n#include <ifaddrs.h>\n\nstatic struct ifaddrs *iflist;\nunsigned int iftime;\t//requery sometimes.\nint FTENET_GetLocalAddress(int port, qboolean ipx, qboolean ipv4, qboolean ipv6,   unsigned int *adrflags, netadr_t *addresses, const char **adrparams, int maxaddresses)\n{\n\tstruct ifaddrs *ifa;\n\tint fam;\n\tint idx = 0;\n\tunsigned int time = Sys_Milliseconds();\n\n\tipv4 = ipv4 && net_dns_ipv4.ival;\n\tipv6 = ipv6 && net_dns_ipv6.ival;\n\n\tif (time - iftime > 1000 && iflist)\n\t{\n\t\tfreeifaddrs(iflist);\n\t\tiflist = NULL;\n\t}\n\tif (!iflist)\n\t{\n\t\tiftime = time;\n\t\tgetifaddrs(&iflist);\n\t}\n\n\tfor (ifa = iflist; ifa && idx < maxaddresses; ifa = ifa->ifa_next)\n\t{\n\t\t//can happen if the interface is not bound.\n\t\tif (ifa->ifa_addr == NULL)\n\t\t\tcontinue;\n\n\t\t//filter out families that we're not interested in.\n\t\tfam = ifa->ifa_addr->sa_family;\n\t\tif (\n#ifdef HAVE_IPV4\n\t\t\t(fam == AF_INET && ipv4) ||\n#endif\n#ifdef HAVE_IPV6\n\t\t\t(fam == AF_INET6 && ipv6) ||\n#endif\n#ifdef HAVE_IPX\n\t\t\t(fam == AF_IPX && ipx) ||\n#endif\n\t\t\t0)\n\t\t{\n\t\t\tSockadrToNetadr((struct sockaddr_qstorage*)ifa->ifa_addr, sizeof(struct sockaddr_qstorage), &addresses[idx]);\n\t\t\taddresses[idx].port = port;\n\t\t\tadrflags[idx] = 0;\n\t\t\tadrparams[idx] = NULL;\n\t\t\tidx++;\n\t\t}\n\t}\n\treturn idx;\n}\n#else\nint FTENET_GetLocalAddress(int port, qboolean ipx, qboolean ipv4, qboolean ipv6,   unsigned int *adrflags, netadr_t *addresses, const char **adrparams, int maxaddresses)\n{\n\treturn 0;\n}\n#endif\n\nint FTENET_Generic_GetLocalAddresses(struct ftenet_generic_connection_s *con, unsigned int *adrflags, netadr_t *addresses, const char **adrparams, int maxaddresses)\n{\n#ifndef HAVE_PACKET\n\treturn 0;\n#else\n\tstruct sockaddr_qstorage\tfrom;\n\tint fromsize = sizeof(from);\n\tnetadr_t adr;\n\tint found = 0;\n\n\tif (getsockname (con->thesocket, (struct sockaddr*)&from, &fromsize) != -1)\n\t{\n\t\tmemset(&adr, 0, sizeof(adr));\n\t\tSockadrToNetadr(&from, fromsize, &adr);\n\n#ifdef USE_GETHOSTNAME_LOCALLISTING\n\t\t//if its bound to 'any' address, ask the system what addresses it actually accepts.\n\t\tif ((adr.type == NA_IPV6) &&\n\t\t\t!*(int*)&adr.address.ip6[0] &&\n\t\t\t!*(int*)&adr.address.ip6[4] &&\n\t\t\t!*(short*)&adr.address.ip6[8] &&\n\t\t\t*(short*)&adr.address.ip6[10]==(short)0xffff &&\n\t\t\t!*(int*)&adr.address.ip6[12])\n\t\t{\n\t\t\t//ipv6 socket bound to the ipv4-any address is a bit weird, but oh well.\n#ifdef _WIN32\n\t\t\t//win32 is buggy and treats binding to [::] as [::ffff:0.0.0.0] (even with pure ipv6 sockets)\n\t\t\t//explicitly binding to [::ffff:0.0.0.0] appears to fail in windows, thus any such socket will definitely support ipv6.\n\t\t\tqboolean canipv4 = (con->addrtype[0] == NA_IP) || (con->addrtype[1] == NA_IP);\n\t\t\tfound = FTENET_GetLocalAddress(adr.port, false, canipv4, true, adrflags, addresses, adrparams, maxaddresses);\n#else\n\t\t\t//FIXME: we should validate that we support hybrid sockets?\n\t\t\tfound = FTENET_GetLocalAddress(adr.port, false, true, false, adrflags, addresses, adrparams, maxaddresses);\n#endif\n\t\t}\n\t\telse\n\t\t{\n\t\t\tint b;\n\t\t\tfor (b = 0; b < sizeof(adr.address); b++)\n\t\t\t\tif (((unsigned char*)&adr.address)[b] != 0)\n\t\t\t\t\tbreak;\n\n\t\t\tif (b == sizeof(adr.address))\n\t\t\t{\n\t\t\t\tqboolean ipx=false, ipv4=false, ipv6=false;\n\t\t\t\tif (adr.type == NA_IP)\n\t\t\t\t\tipv4 = true;\n\t\t\t\telse if (adr.type == NA_IPX)\n\t\t\t\t\tipx = true;\n\t\t\t\telse if (adr.type == NA_IPV6)\n\t\t\t\t{\n\t\t\t\t\tipv4 = (con->addrtype[0] == NA_IP) || (con->addrtype[1] == NA_IP);\n\t\t\t\t\tipv6 = true;\n\t\t\t\t}\n\n\t\t\t\tfound = FTENET_GetLocalAddress(adr.port, ipx, ipv4, ipv6, adrflags, addresses, adrparams, maxaddresses);\n\t\t\t}\n\t\t}\n#endif\n\n\t\t//and use the bound address (even if its 0.0.0.0) if we didn't grab a list from the system.\n\t\tif (!found)\n\t\t{\n\t\t\tif (maxaddresses && adr.type == NA_IPV6 &&\n\t\t\t\t!*(int*)&adr.address.ip6[0] &&\n\t\t\t\t!*(int*)&adr.address.ip6[4] &&\n\t\t\t\t!*(int*)&adr.address.ip6[8] &&\n\t\t\t\t!*(int*)&adr.address.ip6[12])\n\t\t\t{\n\t\t\t\t*addresses = adr;\n\t\t\t\taddresses->type = NA_IP;\n\n\t\t\t\t*adrflags++ = 0;\n\t\t\t\t*adrparams++ = NULL;\n\t\t\t\taddresses++;\n\t\t\t\tmaxaddresses--;\n\t\t\t\tfound++;\n\t\t\t}\n\n\t\t\tif (maxaddresses)\n\t\t\t{\n\t\t\t\t*addresses = adr;\n\n\t\t\t\t*adrflags++ = 0;\n\t\t\t\t*adrparams++ = NULL;\n\t\t\t\taddresses++;\n\t\t\t\tmaxaddresses--;\n\t\t\t\tfound++;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn found;\n#endif\n}\n\nqboolean FTENET_Datagram_GetPacket(ftenet_generic_connection_t *con)\n{\n#ifndef HAVE_PACKET\n\treturn false;\n#else\n\tstruct sockaddr_qstorage\tfrom;\n\tint fromlen;\n\tint ret;\n\tint err;\n\tchar\t\tadr[MAX_ADR_SIZE];\n\n\tif (con->thesocket == INVALID_SOCKET)\n\t\treturn false;\n\n\tfromlen = sizeof(from);\n\t((struct sockaddr*)&from)->sa_family = AF_UNSPEC;\n\tret = recvfrom (con->thesocket, (char *)net_message_buffer, sizeof(net_message_buffer), 0, (struct sockaddr*)&from, &fromlen);\n\n\tif (ret == -1)\n\t{\n\t\terr = neterrno();\n\n\t\tif (err == NET_EWOULDBLOCK)\n\t\t\treturn false;\n\t\tif (err == NET_EMSGSIZE)\n\t\t{\n\t\t\tstatic unsigned int resettime;\n\t\t\tunsigned int curtime = Sys_Milliseconds();\n\t\t\tif (curtime-resettime >= 5000)\t//throttle prints to once per 5 secs (even if they're about different clients, yay ddos)\n\t\t\t{\n\t\t\t\tSockadrToNetadr (&from, fromlen, &net_from);\n\t\t\t\tCon_TPrintf (\"Warning:  Oversize packet from %s\\n\",\n\t\t\t\tNET_AdrToString (adr, sizeof(adr), &net_from));\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\t\tif (err == NET_ECONNABORTED || err == NET_ECONNRESET)\n\t\t{\n\t\t\tstatic unsigned int resettime;\n\t\t\tunsigned int curtime = Sys_Milliseconds();\n\t\t\tif (curtime-resettime >= 5000 || err == NET_ECONNRESET)\t//throttle prints to once per 5 secs (even if they're about different clients, yay ddos)\n\t\t\t{\n\t\t\t\tif (((struct sockaddr*)&from)->sa_family != AF_UNSPEC)\n\t\t\t\t{\n\t\t\t\t\tSockadrToNetadr (&from, fromlen, &net_from);\n\t\t\t\t\tCon_TPrintf (\"Connection lost or aborted (%s)\\n\", NET_AdrToString (adr, sizeof(adr), &net_from));\t//server died/connection lost.\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tCon_TPrintf (\"Connection lost or aborted\\n\");\t//server died/connection lost.\n\t\t\t\tresettime = curtime;\n#ifdef HAVE_CLIENT\n\t\t\t\t//fixme: synthesise a reset packet for the caller to handle? \"\\xff\\xff\\xff\\xffreset\" ?\n\t\t\t\tif (cls.state != ca_disconnected && !con->islisten)\n\t\t\t\t{\n\t\t\t\t\tif (cls.lastarbiatarypackettime+5 < Sys_DoubleTime())\t//too many mvdsv\n\t\t\t\t\t\tCbuf_AddText(\"disconnect\\nreconnect\\n\", RESTRICT_LOCAL);\t//retry connecting.\n\t\t\t\t\telse\n\t\t\t\t\t\tCon_Printf(\"Packet was not delivered - server might be badly configured\\n\");\n\t\t\t\t\treturn false;\n\t\t\t\t}\n#endif\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\t\tif (((struct sockaddr*)&from)->sa_family != AF_UNSPEC)\n\t\t\tCon_Printf (\"NET_GetPacket: Error (%i): %s (%s)\\n\", err, strerror(err), NET_AdrToString (adr, sizeof(adr), &net_from));\n\t\telse\n\t\t\tCon_Printf (\"NET_GetPacket: Error (%i): %s\\n\", err, strerror(err));\n\t\treturn false;\n\t}\n\n\tSockadrToNetadr (&from, fromlen, &net_from);\n\n\tif (net_from.type == NA_INVALID)\n\t{\t//this really shouldn't happen. Blame the OS.\n\t\tCon_TPrintf (\"Warning: sender's address type not known (%i)\\n\", (int)((struct sockaddr*)&from)->sa_family);\n\t\treturn false;\t//packet from an unsupported protocol? no way can we respond, so what's the point\n\t}\n\n\tnet_message.packing = SZ_RAWBYTES;\n\tnet_message.currentbit = 0;\n\tnet_message.cursize = ret;\n\tif (net_message.cursize >= sizeof(net_message_buffer) )\n\t{\n\t\tCon_TPrintf (\"Warning:  Oversize packet from %s\\n\", NET_AdrToString (adr, sizeof(adr), &net_from));\n\t\treturn false;\n\t}\n\n\treturn true;\n#endif\n}\n\nneterr_t FTENET_Datagram_SendPacket(ftenet_generic_connection_t *con, int length, const void *data, netadr_t *to)\n{\n#ifndef HAVE_PACKET\n\treturn NETERR_DISCONNECTED;\n#else\n\tstruct sockaddr_qstorage\taddr;\n\tint size;\n\tint ret;\n\n\tif (to->prot != NP_DGRAM)\n\t\treturn NETERR_NOROUTE;\n\n\tfor (size = 0; size < FTENET_ADDRTYPES; size++)\n\t\tif (to->type == con->addrtype[size])\n\t\t\tbreak;\n\tif (size == FTENET_ADDRTYPES)\n\t\treturn NETERR_NOROUTE;\n\n#ifdef HAVE_IPV6\n\t/*special code to handle sending to hybrid sockets*/\n\tif (con->addrtype[1] == NA_IPV6 && to->type == NA_IP)\n\t{\n\t\tmemset(&addr, 0, sizeof(struct sockaddr_in6));\n\t\t((struct sockaddr_in6*)&addr)->sin6_family = AF_INET6;\n\t\t*(short*)&((struct sockaddr_in6*)&addr)->sin6_addr.s6_addr[10] = 0xffff;\n\t\t*(int*)&((struct sockaddr_in6*)&addr)->sin6_addr.s6_addr[12] = *(int*)&to->address.ip;\n\t\t((struct sockaddr_in6*)&addr)->sin6_port = to->port;\n\t\tsize = sizeof(struct sockaddr_in6);\n\t}\n\telse\n#endif\n\t{\n\t\tsize = NetadrToSockadr (to, &addr);\n\t}\n\n\tif (!data)\n\t\tret = 0;\t//don't send a runt, but pretend we did... yes, this'll confuse EnsureRoute, but at least it'll ensure there's a udp socket open, somewhere.\n\telse\n\t\tret = sendto (con->thesocket, data, length, 0, (struct sockaddr*)&addr, size );\n\tif (ret == -1)\n\t{\n\t\tint ecode = neterrno();\n// wouldblock is silent\n\t\tif (ecode == NET_EWOULDBLOCK)\n\t\t\treturn NETERR_CLOGGED;\n\n\t\tif (ecode == NET_ECONNREFUSED)\n\t\t\treturn NETERR_DISCONNECTED;\n\n\t\tif (ecode == NET_EMSGSIZE)\n\t\t\treturn NETERR_MTU;\n\n\t\tif (ecode == NET_EADDRNOTAVAIL)\n\t\t\treturn NETERR_NOROUTE;\t//this interface doesn't actually support that (eg: happens when ipv6 is disabled on a specific interface).\n\n\t\tif (ecode == NET_EACCES)\n\t\t{\n\t\t\tCon_Printf(\"Access denied: check firewall\\n\");\n\t\t\treturn NETERR_DISCONNECTED;\n\t\t}\n\n\t\t{\n\t\t\tchar adr[256];\n\t\t\tif (ecode==NET_ENETUNREACH)\t//ipv6 support STILL sucks too much. don't spam non-developers, its just annoying.\n\t\t\t\treturn NETERR_NOROUTE;\n#ifdef HAVE_CLIENT\n\t\t\telse if (ecode == NET_EADDRNOTAVAIL || (ecode==NET_ENETUNREACH&&to->type==NA_IPV6))\n\t\t\t\tCon_DPrintf(S_COLOR_GRAY\"%s - Warning: %i\\n\", NET_AdrToString (adr, sizeof(adr), to), ecode);\n#endif\n\t\t\telse\n\t\t\t{\n#ifdef _WIN32\n\t\t\t\tCon_Printf (S_COLOR_GRAY\"%s - ERROR: %i\\n\", NET_AdrToString (adr, sizeof(adr), to), ecode);\n#else\n\t\t\t\tCon_Printf (S_COLOR_GRAY\"%s - ERROR: %s\\n\", NET_AdrToString (adr, sizeof(adr), to), strerror(ecode));\n#endif\n\t\t\t}\n\t\t}\n\t}\n\telse if (ret < length)\n\t\treturn NETERR_MTU;\n\treturn NETERR_SENT;\n#endif\n}\n\nqboolean\tNET_PortToAdr (netadrtype_t adrfamily, netproto_t adrprot, const char *s, netadr_t *a)\n{\n\tchar *e;\n\tif (net_enabled.ival || adrfamily == NA_LOOPBACK)\n\t{\n\t\tint port = strtoul(s, &e, 10);\n\t\tif (*e)\t//if *e then its not just a single number in there, so treat it as a proper address.\n\t\t\treturn NET_StringToAdr(s, 0, a);\n\t\telse if (e != s)\t//if we actually read something (even a 0)\n\t\t{\n\t\t\tmemset(a, 0, sizeof(*a));\n\t\t\ta->port = htons((unsigned short)port);\n\t\t\ta->type = adrfamily;\n\t\t\ta->prot = adrprot;\n\n\t\t\treturn a->type != NA_INVALID;\n\t\t}\n\t}\n\ta->type = NA_INVALID;\n\treturn false;\n}\n\n#ifdef HAVE_PACKET\n/*just here to prevent the client from spamming new sockets, which can be a problem with certain q2 servers*/\nstatic qboolean FTENET_Datagram_ChangeLocalAddress(struct ftenet_generic_connection_s *con, const char *addressstring, netadr_t *adr)\n{\n\tstruct sockaddr_qstorage address;\n\tnetadr_t current;\n\tint namelen = sizeof(address);\n\tif (getsockname (con->thesocket, (struct sockaddr *)&address, &namelen) == 0)\n\t{\n\t\tSockadrToNetadr(&address, namelen, &current);\n\n\t\t//make sure the types match (special check for ipv6 hybrid sockets that accept ipv4 too)\n\t\tif (adr->type == current.type\n#if defined(HAVE_IPV4) && defined(HAVE_IPV6)\n\t\t\t|| (net_hybriddualstack.ival && adr->type == NA_IP && current.type == NA_IPV6)\n#endif\n\t\t\t)\n\t\t{\t//make sure the port is currect (or they don't care which port)\n\t\t\tif (adr->port == current.port || !adr->port)\n\t\t\t\treturn true;\t//then pretend we changed it, because needed to change in the first place.\n\t\t}\n\t}\n\n\t//doesn't match how its currently bound, so I guess we need to rebind then.\n\treturn false;\n}\n\nstatic void FTENET_Datagram_Close(ftenet_generic_connection_t *con)\n{\n\tif (con->thesocket != INVALID_SOCKET)\n\t{\n#ifdef HAVE_EPOLL\n\t\tepoll_ctl(epoll_fd, EPOLL_CTL_DEL, con->thesocket, NULL);\n#endif\n\t\tclosesocket(con->thesocket);\n\t}\n\tZ_Free(con);\n}\n#endif\n\n#ifdef HAVE_EPOLL\nstatic void FTENET_Datagram_Polled(epollctx_t *ctx, unsigned int events)\n{\n\tftenet_generic_connection_t *con = NULL;\n\tcon = (ftenet_generic_connection_t *)((qbyte*)ctx - ((qbyte*)&con->epoll-(qbyte*)con));\n\twhile (con->GetPacket(con))\n\t{\n\t\tnet_from.connum = con->connum;\n\t\tnet_from_connection = con;\n\t\tcon->owner->ReadGamePacket();\n\t}\n}\n#endif\n\nftenet_generic_connection_t *FTENET_Datagram_EstablishConnection(ftenet_connections_t *col, const char *address, netadr_t adr, const struct dtlspeercred_s *peerinfo)\n{\n#ifndef HAVE_PACKET\n\treturn NULL;\n#else\n\t//this is written to support either ipv4 or ipv6, depending on the remote addr.\n\tftenet_generic_connection_t *newcon;\n\tqboolean isserver = col->islisten;\n\n\tunsigned long _true = true;\n\tSOCKET newsocket = INVALID_SOCKET;\n\tint temp;\n\tstruct sockaddr_qstorage qs;\n\tint family;\n\tint port;\n\tint bindtries;\n\tconst int bindmaxtries = 100;\n\tint bufsz;\n\tqboolean hybrid = false;\n\tint protocol;\n\tchar addrstr[128];\n\n\tswitch(adr.type)\n\t{\n#if defined(HAVE_IPV4) || defined(HAVE_IPV6)\n\tcase NA_IP:\n\tcase NA_IPV6:\n\t\tprotocol = IPPROTO_UDP;\n\t\tbreak;\n#endif\n#ifdef HAVE_IPX\n\tcase NA_IPX:\n\t\tprotocol = NSPROTO_IPX;\n\t\tbreak;\n#endif\n\tdefault:\n\t\tprotocol = 0;\n\t\tbreak;\n\t}\n\n\n\tif (adr.type == NA_INVALID)\n\t{\n\t\tCon_Printf(CON_ERROR \"unable to resolve local address %s\\n\", address);\n\t\treturn NULL;\t//couldn't resolve the name\n\t}\n\ttemp = NetadrToSockadr(&adr, &qs);\n\tfamily = ((struct sockaddr*)&qs)->sa_family;\n\n#if defined(HAVE_IPV6) && defined(IPV6_V6ONLY)\n\tif (/*isserver &&*/ family == AF_INET && net_hybriddualstack.ival && !((struct sockaddr_in*)&qs)->sin_addr.s_addr)\n\t{\n\t\tunsigned long _false = false;\n\t\tif ((newsocket = socket (AF_INET6, SOCK_CLOEXEC|SOCK_DGRAM, protocol)) != INVALID_SOCKET)\n\t\t{\n\t\t\tif (0 == setsockopt(newsocket, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&_false, sizeof(_false)))\n\t\t\t{\n//\t\t\t\tint ip = ((struct sockaddr_in*)&qs)->sin_addr.s_addr;\n\t\t\t\tint port = ((struct sockaddr_in*)&qs)->sin_port;\n//\t\t\t\tip = ((struct sockaddr_in*)&qs)->sin_addr.s_addr;\n\t\t\t\tmemset(&qs, 0, sizeof(struct sockaddr_in6));\n\t\t\t\t((struct sockaddr_in6*)&qs)->sin6_family = AF_INET6;\n/*\n\t\t\t\tif (((struct sockaddr_in*)&qs)->sin_addr.s_addr)\n\t\t\t\t{\n\t\t\t\t\t((struct sockaddr_in6*)&qs)->sin6_addr.s6_addr[10] = 0xff;\n\t\t\t\t\t((struct sockaddr_in6*)&qs)->sin6_addr.s6_addr[11] = 0xff;\n\t\t\t\t\t((struct sockaddr_in6*)&qs)->sin6_addr.s6_addr[12] = ((qbyte*)&ip)[0];\n\t\t\t\t\t((struct sockaddr_in6*)&qs)->sin6_addr.s6_addr[13] = ((qbyte*)&ip)[1];\n\t\t\t\t\t((struct sockaddr_in6*)&qs)->sin6_addr.s6_addr[14] = ((qbyte*)&ip)[2];\n\t\t\t\t\t((struct sockaddr_in6*)&qs)->sin6_addr.s6_addr[15] = ((qbyte*)&ip)[3];\n\t\t\t\t}\n*/\n\t\t\t\t((struct sockaddr_in6*)&qs)->sin6_port = port;\n\t\t\t\ttemp = sizeof(struct sockaddr_in6);\n\t\t\t\tadr.type = NA_IPV6;\n\t\t\t\thybrid = true;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t/*v6only failed... if the option doesn't exist, chances are this is a hybrid system which doesn't support both simultaneously anyway*/\n\t\t\t\tclosesocket(newsocket);\n\t\t\t\tnewsocket = INVALID_SOCKET;\n\t\t\t}\n\t\t}\n\t}\n#endif\n\n\tif (newsocket == INVALID_SOCKET)\n\t\tif ((newsocket = socket (family, SOCK_CLOEXEC|SOCK_DGRAM, protocol)) == INVALID_SOCKET)\n\t\t{\n\t\t\treturn NULL;\n\t\t}\n\n#if defined(HAVE_IPV6) && defined(IPV6_V6ONLY)\n\tif (family == AF_INET6)\n\t\tsetsockopt(newsocket, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&_true, sizeof(_true));\n#endif\n\n#if defined(_WIN32) && defined(SO_EXCLUSIVEADDRUSE)\n\t//win32 is so fucked up\n\tsetsockopt(newsocket, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (char *)&_true, sizeof(_true));\n#endif\n\n\tbufsz = 1<<18;\n\tsetsockopt(newsocket, SOL_SOCKET, SO_RCVBUF, (void*)&bufsz, sizeof(bufsz));\n\n\tswitch(family)\n\t{\n#ifdef UNIXSOCKETS\n\tcase AF_UNIX:\n\t\t{\n\t\t\tstruct sockaddr_un *sa = (struct sockaddr_un *)&qs;\n\t\t\tif (!isserver)\n\t\t\t\ttemp = (char*)&sa->sun_path[0] - (char*)sa;\t//linux-specific: bind to an automatic abstract address.\n\t\t\tif (*sa->sun_path && isserver)\n\t\t\t{\t//non-abstract sockets don't clean up the filesystem when the socket is closed\n\t\t\t\t//and we can't re-bind to it while it still exists.\n\t\t\t\t//so standard practise is to delete it before the bind.\n\t\t\t\t//we do want to make sure the file is actually a socket before we remove it (so people can't abuse stuffcmds)\n\t\t\t\t//FIXME: use lock-files\n\t\t\t\tstruct stat s;\n\t\t\t\tif (stat(sa->sun_path, &s)!=-1)\n\t\t\t\t{\n\t\t\t\t\tif ((s.st_mode & S_IFMT) == S_IFSOCK)\n\t\t\t\t\t\tunlink(sa->sun_path);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (bind(newsocket, (struct sockaddr *)sa, temp) == INVALID_SOCKET)\n\t\t\t{\n//\t\t\t\tperror(\"gah\");\n\t\t\t\tSockadrToNetadr(&qs, temp, &adr);\n\t\t\t\tNET_AdrToString(addrstr, sizeof(addrstr), &adr);\n\t\t\t\tCon_Printf(CON_ERROR \"Unable to bind to %s\\n\", addrstr);\n\t\t\t\tclosesocket(newsocket);\n\t\t\t\treturn NULL;\n\t\t\t}\n\t\t}\n\t\tbreak;\n#endif\n\n//\tcase AF_INET:\n//\tcase AF_INET6:\n//\tcase AF_IPX:\n\tdefault:\n\t\t//try and find an unused port.\n\t\tport = ntohs(((struct sockaddr_in*)&qs)->sin_port);\n\t\tfor (bindtries = 0; bindtries < bindmaxtries; bindtries++)\n\t\t{\n\t\t\t((struct sockaddr_in*)&qs)->sin_port = htons((unsigned short)(port+bindtries));\n\t\t\tif ((bind(newsocket, (struct sockaddr *)&qs, temp) == INVALID_SOCKET))\n\t\t\t{\n\t\t\t\tif (port == 0)\n\t\t\t\t{\t//if binding to an ephemerial port failed, binding to the admin-only ports won't work any better...\n\t\t\t\t\tbindtries = bindmaxtries;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t\tif (bindtries == bindmaxtries)\n\t\t{\n\t\t\tSockadrToNetadr(&qs, temp, &adr);\n\t\t\tNET_AdrToString(addrstr, sizeof(addrstr), &adr);\n\t\t\tCon_Printf(CON_ERROR \"Unable to listen at %s\\n\", addrstr);\n\t\t\tclosesocket(newsocket);\n\t\t\treturn NULL;\n\t\t}\n\t\telse if (bindtries && isserver)\n\t\t{\n\t\t\tSockadrToNetadr(&qs, temp, &adr);\n\t\t\tNET_AdrToString(addrstr, sizeof(addrstr), &adr);\n\t\t\tif (SSV_IsSubServer())\n\t\t\t\tCon_DLPrintf(2, CON_ERROR \"Unable to bind to port %i, bound to %s instead\\n\", port, addrstr);\n\t\t\telse\n\t\t\t\tCon_Printf(CON_ERROR \"Unable to bind to port %i, bound to %s instead\\n\", port, addrstr);\n\t\t}\n\t\tbreak;\n\t}\n\n\tif (ioctlsocket (newsocket, FIONBIO, &_true) == -1)\n\t\tSys_Error (\"FTENET_Datagram_EstablishConnection: ioctl FIONBIO: %s\", strerror(neterrno()));\n\n\t//ipv6 sockets need to add themselves to a multicast group, so that we can receive broadcasts on a lan\n#if defined(HAVE_IPV6)\n\tif (family == AF_INET6 || hybrid || isserver)\n\t{\n\t\tstruct ipv6_mreq req;\n\t\tmemset(&req, 0, sizeof(req));\n\t\treq.ipv6mr_multiaddr.s6_addr[0] = 0xff;\n\t\treq.ipv6mr_multiaddr.s6_addr[1] = 0x02;\n\t\treq.ipv6mr_multiaddr.s6_addr[15]= 0x01;\n\t\treq.ipv6mr_interface = 0;\n\t\tsetsockopt(newsocket, IPPROTO_IPV6, IPV6_JOIN_GROUP, (char *)&req, sizeof(req));\n\t}\n#endif\n\n\t//\n\t// determine my name & address if we don't already know it\n\t//\n\tif (!isserver && net_local_cl_ipadr.type == NA_INVALID)\n\t\tNET_GetLocalAddress (newsocket, &net_local_cl_ipadr);\n\n\tnewcon = Z_Malloc(sizeof(*newcon));\n\tif (newcon)\n\t{\n\t\tnewcon->GetLocalAddresses = FTENET_Generic_GetLocalAddresses;\n\t\tnewcon->GetPacket = FTENET_Datagram_GetPacket;\n\t\tnewcon->SendPacket = FTENET_Datagram_SendPacket;\n\t\tnewcon->Close = FTENET_Datagram_Close;\n\t\tnewcon->ChangeLocalAddress = FTENET_Datagram_ChangeLocalAddress;\n\n\t\tnewcon->owner = col;\n\t\tnewcon->islisten = isserver;\n\t\tnewcon->prot = adr.prot;\n\t\tif (hybrid)\n\t\t{\n\t\t\tnewcon->addrtype[0] = NA_IP;\n\t\t\tnewcon->addrtype[1] = NA_IPV6;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tnewcon->addrtype[0] = adr.type;\n\t\t\tnewcon->addrtype[1] = NA_INVALID;\n\t\t}\n\n\t\tnewcon->thesocket = newsocket;\n\n#ifdef HAVE_EPOLL\n\t\t{\n\t\t\tstruct epoll_event event = {EPOLLIN|EPOLLET, {&newcon->epoll}};\n\t\t\tnewcon->epoll.Polled = FTENET_Datagram_Polled;\n\t\t\tif (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, newsocket, &event) < 0)\n\t\t\t{\n\t\t\t\tint err = errno;\n\t\t\t\tCon_Printf(\"epoll_ctl failed - errno %i\\n\", err);\n\t\t\t}\n\t\t}\n#endif\n\n\t\treturn newcon;\n\t}\n\telse\n\t{\n\t\tclosesocket(newsocket);\n\t\treturn NULL;\n\t}\n#endif\n}\n\n\n///////////////////////////////////////////////////////////////////////////\n#ifdef KEXLOBBY\n\n//Note: Kex stuff has a lobby. but we do not really have any lobby state of our own.\n//FIXME: provide feedback to the client about whether it connected and who's in the lobby.\n//FIXME: chat needs to go via this code too (both c2s and eg yq2->q2e).\n\nstruct kexcon_s\n{\n\tqboolean isserver;\n\tvoid *cbctx;\n\tneterr_t(*push)(void *cbctx, const qbyte *data, size_t datasize);\n\n\tstruct kexcon_s *next;\n\n\tunsigned short inseq;\n\tunsigned short inrseq;\n\tqbyte infrag[65536];\n\tunsigned int infragsize;\n\tunsigned int infragseq;\n\n\tunsigned short outseq;\n\tunsigned short outrseq;\n\tqbyte outqueue[1400];\t//temp buffer for naggle-type stuff.\n\tunsigned int outqueuesize;\n\tstruct kexlan_resendqueue_s\n\t{\t//in seq order. popped on ack.\n\t\tstruct kexlan_resendqueue_s *next;\n\t\tunsigned short sz;\n\t\tunsigned short seq;\n\t\tqbyte frag[1];\n\t} *resendqueue;\n\n\tchar name[64];\n\tenum\n\t{\n\t\tKEXLOBBY_PENDING,\n\t\tKEXLOBBY_ESTABLISHED,\n\t\tKEXLOBBY_INGAME,\n\t\tKEXLOBBY_DROPPED,\n\t} state;\n\tint plidx[MAX_SPLITS];\n\tint seats;\n\tint entnum;\n\n\tinfobuf_t slot[256];\n\tinfobuf_t rules;\n\n\tdouble resend;\n\tdouble nextping;\t//sigh... even if we're spamming valid packets we still need a ping every now and then.\n\tdouble timeout;\n};\n\n#define KEXLAN_SHAMELESSSELFPROMOMAGIC \"\\x08\"\"CRANTIME\"\t//hey, if you can't shove your own nick in your network protocols then you're doing it wrong.\n#define KEXLAN_SUBPROTOCOL \"\\x08\"\"Quake II\"\t//this should be cvar-ised at some point, if its to ever be useful for anything but q2.\n#define KEXLAN_RELIABLE\t\t0x100\t//part(ish) of the protocol, don't change\n#define KEXLAN_SEQUENCED\t0x200\t//part(ish) of the protocol, don't change\n#define KEXLAN_NAGGLE\t\t0x400\t//delay sending in order to merge it with other stuff. we don't have timeouts though.\nenum kexlobby_type_e\n{\n\tKEXLAN_GAMEPACKET=0x00,\t\t//both reliables+unreliables\n\tKEXLAN_MATCHDETAILS=0x1,\t//sent at end of game\n\tKEXLAN_ZGAMEPACKET=0x7f,\t//typically only reliables, probably fragmented.\n\tKEXLAN_NEWCONNECTION=0x80,\t//both request+response.\n\tKEXLAN_KEEPALIVE=0x81,\t\t//I *THINK* this is a keepalive.\n\tKEXLAN_ACK=0x82,\n\tKEXLAN_LOBBYCHAT=0xfc,\n\tKEXLAN_LOBBYPLAYER=0xfd,\n\tKEXLAN_LOBBYCOMMAND=0xfe,\n\tKEXLAN_LOBBYRULE=0xff\n};\nstatic neterr_t  NET_KexLobby_SendMessage(struct kexcon_s *peer, int type, const void *data, size_t length)\n{\n\tneterr_t e;\n\tsize_t offset = 0;\n\tint minmtu;\n\tif (length > 1400-7 && !(type & KEXLAN_RELIABLE))\n\t\treturn NETERR_MTU;\t//drop unreliables bigger than we can cope with\n\tif (type & KEXLAN_RELIABLE)\n\t{\n\t\tminmtu = 1280-8-20;\t//we can't reduce the size of reliables while they're still pending as we wouldn't know what was acked,\n\t\t\t\t\t\t\t//so keep reliables under the mtu size so nothing bad happens if we find the mtu is smaller later.\n\t\ttype |= KEXLAN_SEQUENCED;\n\t}\n\telse\n\t\tminmtu = sizeof(peer->outqueue);\t//don't bother fragmenting.\n\n\tif (peer->resendqueue && peer->resend < realtime && peer->state != KEXLOBBY_PENDING)\n\t{\t//try to fill a single segment.\n\t\tstruct kexlan_resendqueue_s *q = peer->resendqueue;\n\t\tfor (q = peer->resendqueue; q && peer->outqueuesize+q->sz < sizeof(peer->outqueue); q = q->next)\n\t\t{\n//Con_Printf(S_COLOR_GRAY\"Retransmit %x\\n\", q->seq);\n\t\t\tmemcpy(peer->outqueue+peer->outqueuesize, q->frag, q->sz);\n\t\t\tpeer->outqueuesize += q->sz;\n\t\t\tpeer->resend = realtime + 0.050;\t//too aggressive?\n\t\t}\n\t}\n\n\tif (data)\n\twhile (offset < length)\n\t{\n\t\tqbyte flags = (type&(KEXLAN_RELIABLE|KEXLAN_SEQUENCED))>>8;\n\t\tunsigned short hdrsize = 2 + ((type & KEXLAN_SEQUENCED)?4:0) + (offset?0:1);\n\t\tunsigned short pktsize = hdrsize+length-offset;\n\t\tqboolean morefrags = false;\n\t\tqbyte *pkt;\n\t\tif (pktsize > minmtu)\n\t\t\tpktsize = minmtu, morefrags = true;\n\n\t\tif (peer->outqueuesize+pktsize > sizeof(peer->outqueue))\n\t\t{\t//can't fit it, flush the queue.\n\t\t\te = peer->push(peer->cbctx, peer->outqueue, peer->outqueuesize);\n\t\t\tif (e == NETERR_MTU && peer->outqueuesize == 0)\n\t\t\t\treturn NETERR_DISCONNECTED;\t//we need to handle fragmentation here\n\t\t\tif (e != NETERR_SENT)\n\t\t\t\treturn e;\n\t\t\tpeer->nextping = realtime + 15;\n\t\t\tpeer->outqueuesize = 0;\n\t\t}\n\t\tpkt = peer->outqueue + peer->outqueuesize;\n\n\t\tif (morefrags)\n\t\t{\t//these bits are kinda inconsistent\n\t\t\tif (!offset)\n\t\t\t\tflags |= 0x4;\t//frag start\n\t\t\telse\n\t\t\t\tflags |= 0x8;\t//frag mid\n\t\t}\n\t\telse if (offset)\n\t\t\tflags |= 0xc;\t//frag tail\n\n\t\tpkt[0] = (pktsize)>>4;\n\t\tpkt[1] = ((pktsize)<<4) | flags;\n\t\thdrsize = 2;\n\t\tif (type & KEXLAN_SEQUENCED)\n\t\t{\n\t\t\tpeer->outseq++;\n\t\t\tpkt[hdrsize++] = peer->outseq>>8;\n\t\t\tpkt[hdrsize++] = peer->outseq;\n\t\t\tif (flags & 1)\n\t\t\t\tpeer->outrseq++;\n\t\t\tpkt[hdrsize++] = peer->outrseq>>8;\n\t\t\tpkt[hdrsize++] = peer->outrseq;\n\t\t}\n\t\tif (!offset)\n\t\t\tpkt[hdrsize++] = type&0xff;\n\t\tmemcpy(pkt+hdrsize, (const char*)data+offset, pktsize-hdrsize);\n\t\toffset += pktsize-hdrsize;\n\n\t\tif (flags & 1)\n\t\t{\n\t\t\tstruct kexlan_resendqueue_s **l, *q;\n\t\t\tif (!peer->resendqueue)\n\t\t\t\tpeer->resend = realtime + 0.050;\n\t\t\tfor (l = &peer->resendqueue; *l; l = &(*l)->next)\n\t\t\t\t;\n\t\t\t*l = q = BZ_Malloc(sizeof(*q)-sizeof(q->frag) + pktsize);\n\t\t\tq->next = NULL;\n\t\t\tq->sz = pktsize;\n\t\t\tq->seq = peer->outrseq;\t//so we know when to stop trying to resent it!\n\t\t\tmemcpy(q->frag, pkt, pktsize);\n\t\t}\n\n\t\tif (peer->state != KEXLOBBY_PENDING)\n\t\t\tpeer->outqueuesize += pktsize;\n\t\t//else just drop it here. if its reliable then its queued until we actually connect so that's fine.\n\t}\n\n\tif (peer->nextping < realtime && peer->state != KEXLOBBY_PENDING)\n\t{\n\t\tpeer->nextping = realtime + 15;\n\t\tif (peer->outqueuesize)\n\t\t\ttype&=~KEXLAN_NAGGLE;\n\t\telse\n\t\t{\t//nothing to send... make sure we send SOMETHING to keep it open.\n\t\t\tstatic qbyte pkt[1] = {0};\n//Con_Printf(S_COLOR_GRAY\"sendkeepalive\\n\");\n\n\t\t\t//might as well just recurse.\n\t\t\treturn NET_KexLobby_SendMessage(peer, KEXLAN_SEQUENCED|KEXLAN_KEEPALIVE, pkt, sizeof(pkt));\n\t\t}\n\t}\n\n\tif (peer->outqueuesize > ((type&KEXLAN_NAGGLE)?500:0))\n\t{\n\t\te = peer->push(peer->cbctx, peer->outqueue, peer->outqueuesize);\n\t\tif (e == NETERR_SENT)\n\t\t\tpeer->nextping = realtime + 15;\n\t\tpeer->outqueuesize = 0;\n\t}\n\telse if (peer->nextping > realtime + 0.3 && peer->outqueuesize)\n\t\tpeer->nextping = realtime + 0.3;\t//set a timeout for it to be flushed.\n\treturn NETERR_SENT;\n}\n#ifdef HAVE_SERVER\nstatic neterr_t  NET_KexLobby_SendMessageZ(struct kexcon_s *peer, int type, const char *data)\n{\n\treturn NET_KexLobby_SendMessage(peer, type, data, strlen(data));\n}\n#endif\nstatic neterr_t NET_KexLobby_SendChat(struct kexcon_s *peer, int seat, const char *text)\n{\n\tchar pkt[2+256];\n\tpkt[0] = peer->plidx[seat];\n\tpkt[1] = strlen(text);\n\tmemcpy(pkt+2, text, pkt[1]);\n\treturn NET_KexLobby_SendMessage(peer, KEXLAN_NAGGLE|KEXLAN_RELIABLE|KEXLAN_LOBBYCHAT, pkt, 2 + pkt[1]);\n}\n#ifdef HAVE_CLIENT\nstatic neterr_t NET_KexLobby_CLSetPlayerInfo(struct kexcon_s *peer, int seat, const char *info)\n{\t//info sould be eg 'playername\\YourName'\n\tchar pkt[2+256];\n\tpkt[0] = seat;\n\tpkt[1] = strlen(info);\n\tmemcpy(pkt+2, info, pkt[1]);\n\treturn NET_KexLobby_SendMessage(peer, KEXLAN_NAGGLE|KEXLAN_RELIABLE|KEXLAN_LOBBYPLAYER, pkt, 2 + pkt[1]);\n}\n#endif\n#ifdef HAVE_SERVER\nstatic neterr_t NET_KexLobby_SVSetPlayerInfo(struct kexcon_s *peer, int plid, const char *info)\n{\t//info sould be eg 'playername\\YourName'\n\tchar pkt[2+256];\n\tpkt[0] = info?0:2;//update type\n\tpkt[1] = plid;\n\tif (!info) info = \"\";\n\tpkt[2] = strlen(info);\n\tmemcpy(pkt+3, info, pkt[2]);\n\treturn NET_KexLobby_SendMessage(peer, KEXLAN_NAGGLE|KEXLAN_RELIABLE|KEXLAN_LOBBYPLAYER, pkt, 3 + pkt[2]);\n}\n#endif\nstatic void NET_KexLobby_Ack(struct kexcon_s *peer, int rseq)\n{\n\tchar pkt[3] = {0x00,rseq>>8, rseq};\n\tNET_KexLobby_SendMessage(peer, KEXLAN_NAGGLE|KEXLAN_ACK, pkt, sizeof(pkt));\n}\n\nstatic void NET_KexLobby_DestroyContext(void *ctx)\n{\n\tstruct kexcon_s *peer = ctx;\n\tstruct kexlan_resendqueue_s *queue;\n\tint i;\n\n\t//let the server know to kill the client too (serverside will notice soon enough). should probably send a couple\n\tNET_KexLobby_SendMessage(peer, KEXLAN_LOBBYCOMMAND, \"\\x0a\"\"disconnect\", 11);\n\n\twhile ((queue=peer->resendqueue))\n\t{\t//would could have been is never to be.\n\t\tpeer->resendqueue = queue->next;\n\t\tZ_Free(queue);\n\t}\n\tInfoBuf_Clear(&peer->rules, true);\n\tfor (i = 0; i < countof(peer->slot); i++)\n\t\tInfoBuf_Clear(&peer->slot[i], true);\n\tZ_Free(peer);\n}\n\nstatic void * NET_KexLobby_CreateContext(const dtlscred_t *credinfo, void *cbctx, neterr_t(*push)(void *cbctx, const qbyte *data, size_t datasize), qboolean isserver)\n{\n\tstruct kexcon_s *peer;\n\tpeer = Z_Malloc(sizeof(*peer));\n\tpeer->cbctx = cbctx;\n\tpeer->push = push;\n\tpeer->isserver = isserver;\n\n\tpeer->inseq = 0;\n\tpeer->inrseq = 0;\n\tpeer->outseq = 0;\n\tpeer->outrseq = 0;\n\tpeer->nextping = 0;\n\tpeer->timeout = realtime + timeout.value;\n\tpeer->state = KEXLOBBY_PENDING;\n\n\tif (isserver)\n\t{\n#ifdef HAVE_SERVER\n\t\tpeer->seats = 1;\n\t\tpeer->plidx[0] = 1;\t//Q2E does _NOT_ like it when its id 0...\n\t\tpeer->plidx[1] = 2;\n\t\tpeer->plidx[2] = 3;\n\t\tpeer->plidx[3] = 4;\n\n\t\tNET_KexLobby_SendMessageZ(peer, KEXLAN_NAGGLE|KEXLAN_RELIABLE|KEXLAN_LOBBYRULE, va(\"map\\\\%s\", sv.mapname));\n\t\t// NET_KexLobby_SendMessageZ(peer, KEXLAN_NAGGLE|KEXLAN_RELIABLE|KEXLAN_LOBBDYRULE, \"_savedgamename\\\\\");\n\t\t// NET_KexLobby_SendMessageZ(peer, KEXLAN_NAGGLE|KEXLAN_RELIABLE|KEXLAN_LOBBDYRULE, \"_savedgame\\\\0\");\n\t\tif (deathmatch.value)\n\t\t\tNET_KexLobby_SendMessageZ(peer, KEXLAN_NAGGLE|KEXLAN_RELIABLE|KEXLAN_LOBBYRULE, \"gamemode\\\\$m_deathmatch\");\n\t\telse\n\t\t\tNET_KexLobby_SendMessageZ(peer, KEXLAN_NAGGLE|KEXLAN_RELIABLE|KEXLAN_LOBBYRULE, \"gamemode\\\\$m_coop\");\n\t\t// NET_KexLobby_SendMessageZ(peer, KEXLAN_NAGGLE|KEXLAN_RELIABLE|KEXLAN_LOBBYRULE, \"ingame\\\\0\");\n\t\t// NET_KexLobby_SendMessageZ(peer, KEXLAN_NAGGLE|KEXLAN_RELIABLE|KEXLAN_LOBBYRULE, \"_maplist\\\\0\");\n\t\t// NET_KexLobby_SendMessageZ(peer, KEXLAN_NAGGLE|KEXLAN_RELIABLE|KEXLAN_LOBBYRULE, \"q2game\\\\baseq2\");\n\n\t\tNET_KexLobby_SVSetPlayerInfo(peer, 0, va(\"playername\\\\\"DISTRIBUTION\" - %s\", hostname.string));\t//always give a name for the server, so Q2E doesn't bug out and make up its own thing.\n//\t\tNET_KexLobby_SVSetPlayerInfo(peer, 0, \"q2ent\\\\1\");\n\t\tNET_KexLobby_SVSetPlayerInfo(peer, peer->plidx[0], \"playername\\\\You...\");\n//\t\tNET_KexLobby_SVSetPlayerInfo(peer, peer->plidx, \"q2ent\\\\2\");\n\n//\t\tNET_KexLobby_SVSetPlayerInfo(peer, peer->plidx[1], \"playername\\\\Also You...\");\n//\t\tNET_KexLobby_SVSetPlayerInfo(peer, peer->plidx[2], \"playername\\\\Kinda You...\");\n//\t\tNET_KexLobby_SVSetPlayerInfo(peer, peer->plidx[3], \"playername\\\\Partly you...\");\n\n\t\tNET_KexLobby_SendMessageZ(peer, KEXLAN_NAGGLE|KEXLAN_RELIABLE|KEXLAN_LOBBYRULE, \"ingame\\\\1\");\n\t\tpeer->state = KEXLOBBY_ESTABLISHED;\n#endif\n\t}\n\telse\n\t{\n#ifdef HAVE_CLIENT\n\t\tint i;\n\t\tfor (i = 0; i < MAX_SPLITS; i++)\n\t\t\tNET_KexLobby_CLSetPlayerInfo(peer, i, va(\"playername\\\\%s\", InfoBuf_ValueForKey(&cls.userinfo[i], \"name\")));\n#endif\n\t}\n\n\treturn peer;\n}\n#ifdef AVAIL_ZLIB\nsize_t ZLib_DecompressBuffer(const qbyte *in, size_t insize, qbyte *out, size_t maxoutsize);\nsize_t ZLib_CompressBuffer(const qbyte *in, size_t insize, qbyte *out, size_t maxoutsize);\n#endif\nstatic neterr_t NET_KexLobby_Received(void *ctx, sizebuf_t *message)\n{\n\tstatic float throttle;\n\tstruct kexcon_s *peer = ctx;\n\n\tconst qbyte *buf = memcpy(alloca(message->cursize), message->data, message->cursize);\n\tconst qbyte *bufend = buf + message->cursize, *nextseq;\n\tstruct kexlan_resendqueue_s **ql;\n\tnet_from.prot = NP_KEXLAN;\n\n\tfor (; buf < bufend; buf = nextseq)\n\t{\n\t\tchar s[256], *sl;\n\t\tqbyte l, p;\n\t\tqbyte fl = buf[1]&0xf;\n\t\tqbyte ptype;\n\t\tint csz = (buf[0]<<4) | (buf[1]>>4);\n\t\tconst qbyte *seqend = buf + csz;\n\t\tunsigned short useq = peer->inseq;\n\t\tif (buf+csz > bufend)\n\t\t{\n\t\t\tCon_DPrintf(\"%s: Trunctaed\\n\", \"NET_KexLobby_Received\");\n\t\t\tbreak;\n\t\t}\n\t\tnextseq = seqend;\n\t\tbuf += 2;\n\n\t\tif (fl&0x2)\n\t\t{\n\t\t\tunsigned short useq = (buf[0]<<8) | (buf[1]);\n\t\t\tunsigned short rseq = (buf[2]<<8) | (buf[3]);\n\n\t\t\tif (rseq != peer->inrseq + (unsigned short)(fl&1))\n\t\t\t{\t//is this right? it'll stop us from reading unreliables until the reliable is actually received.\n//Con_Printf(S_COLOR_GRAY\"missed a reliable %x(%x) vs %x(%x)\\n\", useq,rseq, peer->inseq,peer->inrseq);\n\t\t\t\tif ((short)(rseq-peer->inrseq) <= 0)\n\t\t\t\t{\n\t\t\t\t\tif (fl & 1)\n\t\t\t\t\t\tNET_KexLobby_Ack(peer, rseq);\n\t\t\t\t\tcontinue;\t//a dupe\n\t\t\t\t}\n\t\t\t\t//FIXME: queue these (may be either reliable or unreliables). sort by useq, process once rseq is met...\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\telse if (fl & 1)\n\t\t\t\tNET_KexLobby_Ack(peer, rseq);\n\n\t\t\tif ((short)(useq-peer->inseq) <= 0)\n\t\t\t{\t//dupe?\n//Con_Printf(S_COLOR_GRAY\"Dupe useq %x at %x\\n\", useq, peer->inseq);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tpeer->inseq = useq;\n\t\t\tpeer->inrseq = rseq;\n\t\t\tbuf += 4;\n\t\t}\n\t\telse if (fl & 1)\n\t\t\tCon_DPrintf(S_COLOR_GRAY\"%s: unsequenced reliable\\n\", \"NET_KexLobby_Received\");\n\n\t\tif (fl&0xc)\n\t\t{\t//doesn't make sense without sequence info, but w/e\n\t\t\tcsz = seqend-buf;\n\t\t\tif (fl&0x8)\n\t\t\t{\n\t\t\t\tif (!peer->infragsize)\n\t\t\t\t{\n//Con_Printf(S_COLOR_GRAY\"Missed first fragment %x (%x)\\n\", peer->inseq, peer->inrseq);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tmemcpy(peer->infrag+peer->infragsize, buf, csz);\n\t\t\t\tpeer->infragsize += csz;\n\t\t\t\tif (fl&0x4)\n\t\t\t\t{\n//Con_Printf(S_COLOR_GRAY\"Got tail fragment %x (%x)\\n\", peer->inseq, peer->inrseq);\n\n\t\t\t\t\t//completed fragment. switch it over to read that instead.\n\t\t\t\t\tbuf = peer->infrag;\n\t\t\t\t\tseqend = buf + peer->infragsize;\n\t\t\t\t\tpeer->infragsize = 0;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n//Con_Printf(S_COLOR_GRAY\"Got mid fragment %x (%x)\\n\", peer->inseq, peer->inrseq);\n\t\t\t\t\tcontinue;\t//incomplete, can't process it yet.\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n//Con_Printf(S_COLOR_GRAY\"Got first fragment %x (%x)\\n\", peer->inseq, peer->inrseq);\n\t\t\t\tmemcpy(peer->infrag, buf, csz);\n\t\t\t\tpeer->infragsize = csz;\n\t\t\t\tcontinue;\t//\n\t\t\t}\n\t\t}\n\t\tptype = *buf++;\n\n\t\tcsz = seqend-buf;\n\n\t\tsafeswitch((enum kexlobby_type_e)ptype)\n\t\t{\n\t\tcase KEXLAN_GAMEPACKET:\t//regular game packet\n\t\t\tif (!csz)\n\t\t\t\tbreak;\n\t\t\tmemcpy(net_message_buffer, buf, csz);\n\t\t\tnet_message.cursize = csz;\n\t\t\t{\n\t\t\t\tstruct dtlspeer_s *dp = peer->cbctx;\n\t\t\t\tdp->col->ReadGamePacket();\n\t\t\t}\n\t\t\tbreak;\n\t\tcase KEXLAN_MATCHDETAILS:\n//\t\t\tCon_ThrottlePrintf(&throttle, 0, \"NET_KexLobby_Received: KEXLAN_MATCHDETAILS\\n\", ptype);\n\t\t\treturn NETERR_CLOGGED;\n\t\tcase KEXLAN_ZGAMEPACKET:\n#ifdef AVAIL_ZLIB\n\t\t\tif (*buf++ != 0)\n\t\t\t\tCon_ThrottlePrintf(&throttle, 0, \"zgame: always-0 is not 0\\n\");\n\t\t\tcsz--;\n\t\t\tnet_message.cursize = ZLib_DecompressBuffer(buf, csz, net_message.data, net_message.maxsize);\n\n\t\t\t{\n\t\t\t\tstruct dtlspeer_s *dp = peer->cbctx;\n\t\t\t\tdp->col->ReadGamePacket();\n\t\t\t}\n\t\t\tbreak;\n#else\n\t\t\tbreak;\n#endif\n\t\tcase KEXLAN_NEWCONNECTION:\t//connection ack.\n\t\t\tif (strncmp(buf, KEXLAN_SHAMELESSSELFPROMOMAGIC, strlen(KEXLAN_SHAMELESSSELFPROMOMAGIC)))\n\t\t\t\treturn NETERR_CLOGGED;\t//wtf\n\t\t\tbuf += strlen(KEXLAN_SHAMELESSSELFPROMOMAGIC);\n\t\t\tif (peer->state == KEXLOBBY_PENDING)\n\t\t\t{\n\t\t\t\tif (!peer->isserver)\n\t\t\t\t\tCon_Printf(S_COLOR_GRAY KEXLOBBY\" channel established\\n\");\n\t\t\t\tpeer->state = KEXLOBBY_ESTABLISHED;\n\t\t\t}\n\t\t\tif (peer->isserver)\n\t\t\t{\n\t\t\t\tpeer->outqueuesize = 0;\n\t\t\t\tpeer->resend = realtime;\n\t\t\t\tNET_KexLobby_SendMessage(peer, KEXLAN_NEWCONNECTION, va(KEXLAN_SHAMELESSSELFPROMOMAGIC\"%c\", peer->plidx[0]), strlen(KEXLAN_SHAMELESSSELFPROMOMAGIC)+1);\t//flush-naggle or resend any other queued reliables or w/e\n\t\t\t}\n\t\t\telse\n\t\t\t\tpeer->plidx[0] = *buf++;\t//which slot(s)? we got rammed in to.\n\t\t\tNET_KexLobby_SendMessage(peer, 0, NULL, 0);\t//flush-naggle or resend any other queued reliables or w/e\n\t\t\tbreak;\n\t\tcase KEXLAN_KEEPALIVE:\n//Con_Printf(S_COLOR_GRAY\"?ping? %i: got %i\\n\", csz, *buf++);\n\t\t\tbreak;\n\t\tcase KEXLAN_ACK:\t//ack\n\t\t\tp = *buf++;\n\t\t\tuseq = (buf[0]<<8)|buf[1];\n\t\t\tbuf+=2;\n\t\t\t//acks may be out of order, independant of earlier acks, and may be dupes.\n\t\t\tfor (ql = &peer->resendqueue; *ql; )\n\t\t\t{\t//these are packets the peer has received.\n\t\t\t\tstruct kexlan_resendqueue_s *f = *ql;\n\t\t\t\tif (f->seq == useq || (p && f->seq-useq<0))\n\t\t\t\t{\n//Con_Printf(S_COLOR_GRAY\"Acked %x\\n\", f->seq);\n\t\t\t\t\t*ql = f->next;\n\t\t\t\t\tBZ_Free(f);\n\t\t\t\t\tif (p)\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tql = &(*ql)->next;\n\t\t\t}\n//if (*ql)Con_Printf(S_COLOR_GRAY\"Dupe Acked %x\\n\", useq);\n\t\t\tbreak;\n\t\tcase KEXLAN_LOBBYCHAT:\t//player chat\n\t\t\tp = *buf++;\n\t\t\tif (peer->isserver)\n\t\t\t\tp = peer->plidx[p];\n\t\t\tl = *buf++;\t//skip the length info\n\t\t\tcsz = seqend-buf;\n\t\t\tif (l == csz && l < sizeof(s))\n\t\t\t{\n\t\t\t\tmemcpy(s, buf, l);\n\t\t\t\ts[l] = 0;\n\t\t\t\tif (peer->isserver)\n\t\t\t\t{\t//inject a reliable clientcommand to fake it.\n\t\t\t\t\t((int*)net_message_buffer)[0] = LittleLong(0x80000000);\n\t\t\t\t\t((int*)net_message_buffer)[1] = LittleLong(0x80000000);\n\t\t\t\t\tnet_message_buffer[8] = clcq2_stringcmd;\n\t\t\t\t\tnet_message_buffer[9] = 0;\n\t\t\t\t\tmemcpy(&net_message_buffer[10], \"say \", 4);\n\t\t\t\t\tmemcpy(net_message_buffer+14, buf, l);\n\t\t\t\t\tnet_message_buffer[14+l] = 0;\n\t\t\t\t\tnet_message.cursize = 14+l+1;\n\t\t\t\t\t{\n\t\t\t\t\t\tstruct dtlspeer_s *dp = peer->cbctx;\n\t\t\t\t\t\tdp->col->ReadGamePacket();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tCon_Printf(\"\\x01%s: %s\\n\", InfoBuf_ValueForKey(&peer->slot[p], \"playername\"), s);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase KEXLAN_LOBBYPLAYER:\n\t\t\tif (peer->isserver)\n\t\t\t{\n\t\t\t\tptype = 0;\n\t\t\t\tp = *buf++;\n\t\t\t\tp = bound(0, p, countof(peer->plidx)-1);\n\t\t\t\tp = peer->plidx[p];\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tptype = *buf++;\n\t\t\t\tp = *buf++;\n\t\t\t}\n\t\t\tif (ptype == 2)\n\t\t\t\tInfoBuf_Clear(&peer->slot[p], true);\n\t\t\telse if (ptype == 0)\n\t\t\t{\n\t\t\t\tl = *buf++;\n\t\t\t\tcsz = seqend-buf;\n\t\t\t\tif (l == csz && l < sizeof(s))\n\t\t\t\t{\n\t\t\t\t\tmemcpy(s, buf, l);\n\t\t\t\t\ts[l] = 0;\n\t\t\t\t\tsl = strchr(s, '\\\\');\n\t\t\t\t\tif (sl)\n\t\t\t\t\t{\t//no slash? no idea.\n\t\t\t\t\t\t*sl++ = 0;\n\t\t\t\t\t\tCon_DPrintf(S_COLOR_GRAY\"p%02x: %s = %s\\n\", p, s, sl);\n\t\t\t\t\t\tInfoBuf_SetValueForStarKey(&peer->slot[p], s, sl);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t\tCon_ThrottlePrintf(&throttle, 0, \"Unknown player update type\\n\");\n\t\t\tbreak;\n\t\tcase KEXLAN_LOBBYCOMMAND:\n\t\t\tl = *buf++;\n\t\t\tcsz = seqend-buf;\n\t\t\tif (csz != l || csz >= sizeof(s))\n\t\t\t\tcsz = 0;\n\t\t\tmemcpy(s, buf, csz);\n\t\t\ts[csz] = 0;\n\t\t\tif (!strcmp(s, \"disconnect\"))\n\t\t\t{\n\t\t\t\tpeer->state = KEXLOBBY_DROPPED;\n\t\t\t\treturn NETERR_DISCONNECTED;\n\t\t\t}\n#ifdef HAVE_CLIENT\n\t\t\telse if (!strcmp(s, \"kicked\"))\n\t\t\t{\n\t\t\t\textern cvar_t cl_disconnectreason;\n\t\t\t\tCvar_Set(&cl_disconnectreason, \"You were kicked\");\n\t\t\t\tpeer->state = KEXLOBBY_DROPPED;\n\t\t\t\treturn NETERR_DISCONNECTED;\n\t\t\t}\n#endif\n\t\t\telse\n\t\t\t\tCon_ThrottlePrintf(&throttle, 0, KEXLOBBY\": unrecognised command %s\\n\", s);\n\t\t\tbreak;\n\t\tcase KEXLAN_LOBBYRULE:\n#ifdef HAVE_CLIENT\n\t\t\tif (csz < sizeof(s) && !peer->isserver)\n\t\t\t{\n\t\t\t\tmemcpy(s, buf, csz);\n\t\t\t\ts[csz] = 0;\n\t\t\t\tsl = strchr(s, '\\\\');\n\t\t\t\tif (sl)\n\t\t\t\t{\t//no slash? no idea.\n\t\t\t\t\t*sl++ = 0;\n\t\t\t\t\tCon_DPrintf(S_COLOR_GRAY\"srv: %s = %s\\n\", s, sl);\n\t\t\t\t\tInfoBuf_SetValueForStarKey(&peer->rules, s, sl);\n\t\t\t\t\tif (!strcmp(s, \"ingame\"))\n\t\t\t\t\t{\n\t\t\t\t\t\tif (atoi(sl))\n\t\t\t\t\t\t\tpeer->state = KEXLOBBY_INGAME;\n\t\t\t\t\t\telse if (peer->state == KEXLOBBY_INGAME)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tpeer->state = KEXLOBBY_ESTABLISHED;\n\t\t\t\t\t\t\tCbuf_AddText(\"disconnect;reconnect\\n\", RESTRICT_LOCAL);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n#endif\n\t\t\tbreak;\n\t\tsafedefault:\n\t\t\tCon_ThrottlePrintf(&throttle, 0, \"NET_KexLobby_Received: Unknown packet type %i\\n\", ptype);\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tif (peer->resendqueue)\n\t\tNET_KexLobby_SendMessage(peer, 0, NULL, 0);\n\n\treturn NETERR_CLOGGED;\n}\nstatic neterr_t NET_KexLobby_Transmit(void *ctx, const qbyte *data, size_t length)\n{\n\tstruct kexcon_s *peer = ctx;\n\n\tif (peer->state == KEXLOBBY_DROPPED)\n\t\treturn NETERR_DISCONNECTED;\n\n#ifdef HAVE_CLIENT\n\tif (peer->state == KEXLOBBY_PENDING)\n\t{\t//send a peer request until we get an ack that its all okay.\n\t\tif (peer->nextping < realtime)\n\t\t{\n\t\t\tstatic char pkt[] = \"\\x01\\x60\\x80\"KEXLAN_SHAMELESSSELFPROMOMAGIC KEXLAN_SUBPROTOCOL\"\\x01\";\n\t\t\t//pkt[strlen(pkt)-1] = bound(1, cl_splitscreen.ival+1, MAX_SPLITS);\t//fix the last byte to the seat count.\n\t\t\tpeer->push(peer->cbctx, pkt, strlen(pkt));\n\t\t\tpeer->nextping = realtime + 15;\n\t\t}\n\t\treturn NETERR_CLOGGED;\n\t}\n\tif (!peer->isserver)\n\t{\n\t\tif (peer->state != KEXLOBBY_INGAME)\n\t\t{\n\t\t\tneterr_t e = NET_KexLobby_SendMessage(peer, KEXLAN_RELIABLE|KEXLAN_GAMEPACKET, NULL, 0);\t//send this packet reliably!\n\t\t\tif (e == NETERR_SENT)\n\t\t\t\te = NETERR_CLOGGED;\t//we didn't actually send the requested packet yet.\n\t\t\treturn e;\n\t\t}\n\t\tif (peer->entnum != cl.playerview[0].playernum+1)\n\t\t{\n\t\t\tpeer->entnum = cl.playerview[0].playernum+1;\n\t\t\tNET_KexLobby_CLSetPlayerInfo(peer, 0, va(\"q2ent\\\\%i\", peer->entnum));\n\t\t}\n\t}\n#endif\n\tif (length >= 4 && ((const qbyte*)data)[3] & 0x80)\n\t{\t//reliables...\n#ifdef AVAIL_ZLIB\n\t\tif (((const qbyte*)data)[0] == 0xff && ((const qbyte*)data)[1] == 0xff && ((const qbyte*)data)[2] == 0xff && ((const qbyte*)data)[3] == 0xff)\n\t\t{\t//connect packets should be compressed... 4/8 sets of userinfo can get big and bloated, but mostly it also helps obfuscate.\n\t\t\tqbyte *zdata = alloca(length+1);\n\t\t\tsize_t zlength = ZLib_CompressBuffer(data, length, zdata+1, length);\n\t\t\tif (zlength)\n\t\t\t{\n\t\t\t\tzdata[0] = 0;\n\t\t\t\treturn NET_KexLobby_SendMessage(peer, KEXLAN_RELIABLE|KEXLAN_ZGAMEPACKET, zdata, zlength+1);\t//send this packet reliably!\n\t\t\t}\n\t\t}\n#endif\n\t\treturn NET_KexLobby_SendMessage(peer, KEXLAN_RELIABLE|KEXLAN_GAMEPACKET, data, length);\t//send this packet reliably!\n\t}\n\telse\n\t\treturn NET_KexLobby_SendMessage(peer, KEXLAN_SEQUENCED|KEXLAN_GAMEPACKET, data, length);\t//can be unreliable.\n}\n\nstatic neterr_t NET_KexLobby_Timeouts(void *ctx)\n{\n\tstruct kexcon_s *peer = ctx;\n\tNET_KexLobby_SendMessage(peer, 0, NULL, 0);\n\treturn NETERR_SENT;\n}\n\nstatic int NET_KexLobby_GetPeerCertificate(void *ctx, enum certprops_e prop, char *out, size_t outsize)\n{\n\tstruct kexcon_s *peer = ctx;\n\tconst char *map, *mode;\n\tchar *s = out;\n\tint i;\n\tsize_t n;\n\tswitch(prop)\n\t{\n\tcase QCERT_LOBBYSENDCHAT:\n\t\tif (outsize > 255)\n\t\t\treturn -1;\t//dumb limis.\n\t\tNET_KexLobby_SendChat(peer, 0, out);\n\t\treturn outsize;\n\tcase QCERT_LOBBYSTATUS:\n\t\tswitch(peer->state)\n\t\t{\n\t\tcase KEXLOBBY_PENDING:\n\t\t\treturn 0;\n//\t\t\tQ_snprintfz(out, outsize, \"\\nWaiting for response...\\n\");\n//\t\t\tbreak;\n\t\tcase KEXLOBBY_ESTABLISHED:\n\t\t\tQ_snprintfz(out, outsize, \"\\nWaiting for host to start...\\n\");\n\t\t\tbreak;\n\t\tcase KEXLOBBY_INGAME:\n\t\t\tQ_snprintfz(out, outsize, \"\\nStarting...\\n\");\n\t\t\tbreak;\n\t\tcase KEXLOBBY_DROPPED:\n\t\t\tQ_snprintfz(out, outsize, \"\\n\"CON_ERROR\"Dropped!\\n\");\n\t\t\tbreak;\n\t\t}\n\t\tn = strlen(out); out += n; outsize -= n;\n\n\t\tmap = InfoBuf_ValueForKey(&peer->rules, \"map\");\n\t\tmap = TL_Translate(com_language, map);\n\t\tmode = InfoBuf_ValueForKey(&peer->rules, \"gamemode\");\n\t\tmode = TL_Translate(com_language, mode);\n\t\tQ_snprintfz(out, outsize, \"%s - %s\\n\\n\", mode, map);\n\t\tn = strlen(out); out += n; outsize -= n;\n\n\t\tfor (i = 0; i < countof(peer->slot); i++)\n\t\t\tif (peer->slot[i].numkeys)\n\t\t\t{\n\t\t\t\tif (peer->plidx[0] == i)\n\t\t\t\t\tQ_snprintfz(out, outsize, S_COLOR_GREEN\"%s\\n\", InfoBuf_ValueForKey(&peer->slot[i], \"playername\"));\n\t\t\t\telse\n\t\t\t\t\tQ_snprintfz(out, outsize, \"%s\\n\", InfoBuf_ValueForKey(&peer->slot[i], \"playername\"));\n\t\t\t\tn = strlen(out); out += n; outsize -= n;\n\t\t\t}\n\t\treturn out-s;\n\tdefault:\n\t\treturn -1;\n\t}\n}\n\nstatic dtlsfuncs_t kexpeerfuncs =\n{\n\tNET_KexLobby_CreateContext,//void *(*CreateContext)(const dtlscred_t *credinfo, void *cbctx, neterr_t(*push)(void *cbctx, const qbyte *data, size_t datasize), qboolean isserver);\t//the certificate to advertise.\n\tNULL,//qboolean (*CheckConnection)(void *cbctx, void *peeraddr, size_t peeraddrsize, void *indata, size_t insize, neterr_t(*push)(void *cbctx, const qbyte *data, size_t datasize), void (*EstablishTrueContext)(void **cbctx, void *state));\n\tNET_KexLobby_DestroyContext,//void (*DestroyContext)(void *ctx);\n\tNET_KexLobby_Transmit,//neterr_t (*Transmit)(void *ctx, const qbyte *data, size_t datasize);\n\tNET_KexLobby_Received,//neterr_t (*Received)(void *ctx, sizebuf_t *message);\t//operates in-place...\n\tNET_KexLobby_Timeouts,//neterr_t (*Timeouts)(void *ctx);\n\tNET_KexLobby_GetPeerCertificate,//int (*GetPeerCertificate)(void *ctx, enum certprops_e prop, char *out, size_t outsize);\n\tNULL,//qboolean (*GenTempCertificate)(const char *subject, struct dtlslocalcred_s *cred);\n};\nstatic struct dtlspeer_s *NET_KexLobby_Create(ftenet_connections_t *col, netadr_t *to, qboolean outgoing)\n{\n\textern cvar_t timeout;\n\tstruct dtlspeer_s *peer;\n\tif (to->prot != NP_DGRAM || to->type==NA_LOOPBACK)\t//FIXME: allow this to be layered over dtls...\n\t\treturn NULL;\n\tfor (peer = col->dtls; peer; peer = peer->next)\n\t{\n\t\tif (NET_CompareAdr(&peer->addr, to))\n\t\t\tbreak;\n\t}\n\tif (!peer)\n\t{\n\t\tpeer = Z_Malloc(sizeof(*peer));\n\t\tpeer->addr = *to;\n\t\tpeer->prot = NP_KEXLAN;\n\t\tpeer->col = col;\n\n\t\tpeer->funcs = &kexpeerfuncs;\n\t\tif (peer->funcs)\n\t\t\tpeer->dtlsstate = peer->funcs->CreateContext(NULL, peer, FTENET_DTLS_DoSendPacket, !outgoing);\n\n\t\tpeer->timeout = realtime+timeout.value;\n\t\tif (peer->dtlsstate)\n\t\t{\n\t\t\tpeer->link = &col->dtls;\n\t\t\tpeer->next = col->dtls;\n\t\t\tif (peer->next)\n\t\t\t\tpeer->next->link = &peer->next;\n\t\t\tcol->dtls = peer;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tZ_Free(peer);\n\t\t\tpeer = NULL;\n\t\t}\n\t}\n\telse\n\t\tpeer->timeout = realtime+timeout.value;\n\treturn peer;\n}\n\nqboolean SV_ChallengeRecent(void);\nqboolean NET_KexLobby_CheckInbound(ftenet_connections_t *col)\n{\n\tqbyte *buf = net_message.data;\n\tif (net_from.prot != NP_DGRAM)\n\t\treturn false;\t//could allow dtls here perhaps.\n#ifdef HAVE_SERVER\n\tif (col->islisten)\n\tif (svs.gametype == GT_QUAKE2 && sv.state!=ss_dead)\n\t{\n\t\tif (((buf[0]<<4)|(buf[1]>>4)) == net_message.cursize && (buf[1]&15)==0 && buf[2] == KEXLAN_NEWCONNECTION && net_message.cursize == 3+strlen(KEXLAN_SHAMELESSSELFPROMOMAGIC KEXLAN_SUBPROTOCOL)+1)\n\t\tif (!memcmp(buf+3, KEXLAN_SHAMELESSSELFPROMOMAGIC KEXLAN_SUBPROTOCOL, strlen(KEXLAN_SHAMELESSSELFPROMOMAGIC KEXLAN_SUBPROTOCOL)))\n\t\tif (buf[3+strlen(KEXLAN_SHAMELESSSELFPROMOMAGIC KEXLAN_SUBPROTOCOL)] == 0x1)\t//'player count' byte must be at least 1... we don't accept higher because I dunno how player ids are meant to work for those extra seats.\n\t\tif (!SV_ChallengeRecent())\t//don't respond if we got a getchallenge packet recently. it'd waste resources.\n\t\t{\n\t\t\tstatic float throttle;\n\t\t\tif (!net_enable_kexlobby.ival)\n\t\t\t\tCon_ThrottlePrintf(&throttle, 0, CON_WARNING\"Inbound connection blocked - %s is disabled.\\n\", net_enable_kexlobby.name);\n\t\t\telse\n\t\t\t{\n\t\t\t\tstruct dtlspeer_s *peer = NET_KexLobby_Create(col, &net_from, !col->islisten);\n\t\t\t\tif (peer)\n\t\t\t\t\tpeer->funcs->Received(peer->dtlsstate, &net_message);\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\t}\n#endif\n#ifdef HAVE_CLIENT\n\tif (!col->islisten)\n\t{\n\t\tif (((buf[0]<<4)|(buf[1]>>4)) == net_message.cursize && (buf[1]&15)==0 && buf[2] == KEXLAN_NEWCONNECTION && net_message.cursize == 3+strlen(KEXLAN_SHAMELESSSELFPROMOMAGIC)+1)\n\t\tif (!memcmp(buf+3, KEXLAN_SHAMELESSSELFPROMOMAGIC, strlen(KEXLAN_SHAMELESSSELFPROMOMAGIC)))\n\t\tif (CL_IsPendingServerAddress(&net_from))\n\t\t{\n\t\t\tstruct dtlspeer_s *peer = NET_KexLobby_Create(col, &net_from, !col->islisten);\n\t\t\tif (peer)\n\t\t\t{\n\t\t\t\tnet_from.prot = peer->prot;\n\t\t\t\tCL_Transfer(&net_from);\n\t\t\t\tpeer->funcs->Received(peer->dtlsstate, &net_message);\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\t}\n#endif\n\treturn false;\n}\n#endif\n\n///////////////////////////////////////////////////////////////////////////\n\n#ifdef TCPCONNECT\ntypedef struct ftenet_tcp_stream_s {\n\tvfsfile_t *clientstream;\n\tint inlen;\n\tint outlen;\n\n#ifdef HAVE_EPOLL\n\tepollctx_t epoll;\t//so our epoll dispatcher knows which connection/stream\n\tstruct ftenet_tcp_connection_s *con;\n#endif\n\n\tenum\n\t{\n\t\tTCPC_UNKNOWN,\t\t//waiting to see what they send us.\n\t\t//TCPC_QTV,\t\t\t//included for completeness. qtv handles the sockets itself, we just parse initial handshake and then pass it over (as either a tcp or tls vfsfile_t)\n\t\tTCPC_QIZMO,\t\t\t//'qizmo\\n' handshake, followed by packets prefixed with a 16bit packet length.\n#ifdef HAVE_HTTPSV\n\t\tTCPC_WEBSOCKETU,\t//utf-8 encoded data. //TODO(fhomolka): Check if any targets use this, remove if not\n\t\tTCPC_WEBSOCKETB,\t//binary encoded data (subprotocol = 'binary')\n\t\tTCPC_WEBSOCKETNQ,\t//raw nq msg buffers with no encapsulation or handshake\n\t\tTCPC_HTTPCLIENT,\t//we're sending a file to this victim.\n\t\tTCPC_WEBRTC_HOST,\t//for brokering webrtc connections, doesn't carry any actual game data itself.\n\t\tTCPC_WEBRTC_CLIENT,\t//for brokering webrtc connections, doesn't carry any actual game data itself.\n#endif\n\t} clienttype;\n\tqbyte inbuffer[MAX_OVERALLMSGLEN];\n\tqbyte outbuffer[MAX_OVERALLMSGLEN];\n\tvfsfile_t *dlfile;\t\t//if the client looked like an http client, this is the file that they're downloading.\n\tfloat timeouttime;\n\tqboolean pinging;\n\tnetadr_t remoteaddr;\n\tstruct ftenet_tcp_stream_s *next;\n\n\tSOCKET socketnum;\t//for select. not otherwise used.\n\n\tint fakesequence;\t//TCPC_WEBSOCKETNQ\n\n#ifdef HAVE_HTTPSV\n\tstruct\n\t{\n\t\tqboolean connection_close;\n\t} httpstate;\n#ifdef MVD_RECORDING\n\tqtvpendingstate_t qtvstate;\n#endif\n\tstruct\n\t{\n\t\tchar resource[64];\n\t\tint clientnum;\t//low number slots\n\t\tunsigned int clientseq;\t//something less reuse-y\n#ifdef SUPPORT_RTC_ICE\n\t\tstruct icestate_s\t*ice;\n#endif\n\t\t//for brokering with a udp-based server.\n\t\tnetadr_t target;\n\t\tint sends;\n\t\tchar *offer;\n\t\tchar *candidates;\n\t\tfloat resendtime;\n\t\tint candack;\t//number of received candidates (to avoid dupes)\n\t\tint outcand;\t//number of sent candidates (to avoid redundant resends)\n\t} webrtc;\n#endif\n} ftenet_tcp_stream_t;\n\ntypedef struct ftenet_tcp_connection_s {\n\tftenet_generic_connection_t generic;\n\tqboolean tls;\n\n\tint active;\n\tftenet_tcp_stream_t *tcpstreams;\n} ftenet_tcp_connection_t;\n\nvoid tobase64(unsigned char *out, int outlen, const unsigned char *in, int inlen)\n{\n\tstatic unsigned char tab[64] =\n\t{\n\t\t'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P',\n\t\t'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f',\n\t\t'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v',\n\t\t'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/'\n\t};\n\tunsigned int usedbits = 0;\n\tunsigned int val = 0;\n\toutlen--;\n\twhile(inlen)\n\t{\n\t\twhile(usedbits < 24 && inlen)\n\t\t{\n\t\t\tval <<= 8;\n\t\t\tval |= (*in++);\n\t\t\tinlen--;\n\t\t\tusedbits += 8;\n\t\t}\n\t\tif (outlen < 4)\n\t\t\treturn;\n\t\tval <<= 24 - usedbits;\n\n\t\t*out++ = (usedbits > 0)?tab[(val>>18)&0x3f]:'=';\n\t\t*out++ = (usedbits > 6)?tab[(val>>12)&0x3f]:'=';\n\t\t*out++ = (usedbits > 12)?tab[(val>>6)&0x3f]:'=';\n\t\t*out++ = (usedbits > 18)?tab[(val>>0)&0x3f]:'=';\n\t\tval=0;\n\t\tusedbits = 0;\n\t}\n\t*out = 0;\n}\n\nneterr_t FTENET_TCP_WebSocket_Splurge(ftenet_tcp_stream_t *st, enum websocketpackettype_e packettype, const qbyte *data, unsigned int length)\n{\n\t/*as a server, we don't need the mask stuff*/\n\tunsigned short ctrl = 0x8000 | (packettype<<8);\n\tunsigned int paylen = 0;\n\tunsigned int payoffs = st->outlen;\n\tint i;\n\tswitch((ctrl>>8) & 0xf)\n\t{\n\tcase WS_PACKETTYPE_TEXTFRAME:\n\t\tfor (i = 0; i < length; i++)\n\t\t{\n\t\t\tpaylen += (data[i] == 0 || data[i] >= 0x80)?2:1;\n\t\t}\n\t\tbreak;\n\tdefault:\n\t\tpaylen = length;\n\t\tbreak;\n\t}\n\tif (paylen >= 126)\n\t\tctrl |= 126;\n\telse\n\t\tctrl |= paylen;\n\n\tif (6 + paylen > sizeof(st->outbuffer))\n\t\treturn NETERR_MTU;\n\tif (payoffs + 6 + paylen > sizeof(st->outbuffer))\n\t\treturn NETERR_CLOGGED;\n\n\tst->outbuffer[payoffs++] = ctrl>>8;\n\tst->outbuffer[payoffs++] = ctrl&0xff;\n\tif ((ctrl&0x7f) == 126)\n\t{\n\t\tst->outbuffer[payoffs++] = paylen>>8;\n\t\tst->outbuffer[payoffs++] = paylen&0xff;\n\t}\n\tswitch((ctrl>>8) & 0xf)\n\t{\n\tcase WS_PACKETTYPE_TEXTFRAME:/*utf8ify the data*/\n\t\tfor (i = 0; i < length; i++)\n\t\t{\n\t\t\tif (!data[i])\n\t\t\t{\t/*0 is encoded as 0x100 to avoid safety checks*/\n\t\t\t\tst->outbuffer[payoffs++] = 0xc0 | (0x100>>6);\n\t\t\t\tst->outbuffer[payoffs++] = 0x80 | (0x100&0x3f);\n\t\t\t}\n\t\t\telse if (data[i] >= 0x80)\n\t\t\t{\t/*larger bytes require markup*/\n\t\t\t\tst->outbuffer[payoffs++] = 0xc0 | (data[i]>>6);\n\t\t\t\tst->outbuffer[payoffs++] = 0x80 | (data[i]&0x3f);\n\t\t\t}\n\t\t\telse\n\t\t\t{\t/*lower 7 bits are as-is*/\n\t\t\t\tst->outbuffer[payoffs++] = data[i];\n\t\t\t}\n\t\t}\n\t\tbreak;\n\tdefault: //raw data\n\t\tmemcpy(st->outbuffer+payoffs, data, length);\n\t\tpayoffs += length;\n\t\tbreak;\n\t}\n\tst->outlen = payoffs;\n\n\n\tif (st->outlen && st->clientstream)\n\t{\t/*try and flush the old data*/\n\t\tint done;\n\t\tdone = VFS_WRITE(st->clientstream, st->outbuffer, st->outlen);\n\t\tif (done > 0)\n\t\t{\n\t\t\tmemmove(st->outbuffer, st->outbuffer + done, st->outlen - done);\n\t\t\tst->outlen -= done;\n\nmemset(st->outbuffer+st->outlen, 0, sizeof(st->outbuffer)-st->outlen);\n\t\t}\n\t}\n\treturn NETERR_SENT;\t//it might not have actually gone, but it'll go eventually...\n}\n\n#ifdef HAVE_HTTPSV\nenum\n{\n\tWCATTR_METHOD,\n\tWCATTR_URL,\n\tWCATTR_HTTP,\n\tWCATTR_HOST,\n\tWCATTR_CONNECTION,\n\n\tWCATTR_UPGRADE,\n\tWCATTR_WSKEY,\n\t//WCATTR_ORIGIN,\n\tWCATTR_WSPROTO,\n\t//WCATTR_WSEXT,\n\n\tWCATTR_IFNONEMATCH,\n\n\tWCATTR_COUNT,\n\n\tWCATTR_WSVER,\n\tWCATTR_CONTENT_LENGTH,\n\tWCATTR_ACCEPT_ENCODING,\n\tWCATTR_TRANSFER_ENCODING\n};\ntypedef char httparg_t[256];\n#include \"fs.h\"\n#ifdef _WIN32\n#include \"resource.h\"\n#endif\nvoid SV_UserCmdMVDList_HTML (vfsfile_t *pipe);\nqboolean FTENET_TCP_HTTPResponse(ftenet_tcp_connection_t *con, ftenet_tcp_stream_t *st, httparg_t arg[WCATTR_COUNT], qboolean allowgzip)\n{\n\tchar adr[256];\n\tint i;\n\tconst char *filetype = NULL;\n\tconst char *resp = NULL;\t//response headers (no length/gap)\n\tconst char *body = NULL;\t//response body\n\tint method;\n\tnet_from = st->remoteaddr;\n\tif (!strcmp(arg[WCATTR_METHOD], \"GET\"))\n\t\tmethod = 0;\n\telse if (!strcmp(arg[WCATTR_METHOD], \"HEAD\"))\n\t\tmethod = 1;\n\telse //if (!strcmp(arg[WCATTR_METHOD], \"POST\") || !strcmp(arg[WCATTR_METHOD], \"PUT\") || !strcmp(arg[WCATTR_METHOD], \"OPTIONS\"))\n\t{\n\t\tmethod = 404;\n\t\tresp =\t\"HTTP/1.1 405 Method Not Allowed\\r\\n\";\n\t\tst->httpstate.connection_close = true;\n\t\tbody = NULL;\n\t}\n\n\tif (!stricmp(arg[WCATTR_CONNECTION], \"Close\"))\n\t\tst->httpstate.connection_close = true;\t//peer wants us to kill the connection on completion.\n\n\tst->dlfile = NULL;\n\tif (!resp && *arg[WCATTR_URL] == '/')\n\t{\t//'can't use SV_LocateDownload, as that assumes an active client.\n\t\tconst char *name = arg[WCATTR_URL]+1;\n\t\tchar *extraheaders = \"\";\n\t\ttime_t modificationtime = 0;\n\t\tchar *query = strchr(arg[WCATTR_URL]+1, '?');\n#ifdef HAVE_SERVER\n\t\tfunc_t func = 0;\n#endif\n\t\tif (query)\n\t\t\t*query++ = 0;\n\n\t\t//FIXME: remove ?\n\t\t//FIXME: any path that ends with / should be sent to index.html or something\n\t\tif (!*name)\n\t\t\tname = \"index.html\";\n\n#ifdef HAVE_SERVER\n\t\tif (sv.state && svs.gametype == GT_PROGS && svprogfuncs)\n\t\t\tfunc = svprogfuncs->FindFunction(svprogfuncs, \"HTTP_GeneratePage\", PR_ANY);\n\n\t\tif (func)\n\t\t{\n\t\t\tvoid *pr_globals = PR_globals(svprogfuncs, PR_CURRENT);\n\t\t\t((string_t *)pr_globals)[OFS_PARM0] = svprogfuncs->TempString(svprogfuncs, query?va(\"%s?%s\", name, query):name);\n\t\t\t((string_t *)pr_globals)[OFS_PARM1] = svprogfuncs->TempString(svprogfuncs, arg[WCATTR_METHOD]);\n\t\t\t((string_t *)pr_globals)[OFS_PARM2] = 0;\t//we don't support any postdata at this time.\n\t\t\t((string_t *)pr_globals)[OFS_PARM3] = 0;\t//we don't support any request headers at this time.\n\t\t\t((string_t *)pr_globals)[OFS_PARM4] = 0;\t//we don't have any default response headers yet.\n\t\t\t((string_t *)pr_globals)[OFS_PARM5] = 0;\n\t\t\t((string_t *)pr_globals)[OFS_PARM6] = 0;\n\t\t\t((string_t *)pr_globals)[OFS_PARM7] = 0;\n\t\t\tsvprogfuncs->ExecuteProgram(svprogfuncs, func);\n\n\t\t\tif (((string_t *)pr_globals)[OFS_RETURN])\n\t\t\t{\t//note that \"\" is not null\n\t\t\t\tbody = svprogfuncs->StringToNative(svprogfuncs, ((string_t *)pr_globals)[OFS_RETURN]);\n\t\t\t\tresp = svprogfuncs->StringToNative(svprogfuncs, ((string_t *)pr_globals)[OFS_PARM4]);\n\t\t\t\tresp = va(\"%s%s\", *body?\"HTTP/1.1 200 Ok\\r\\n\":\"HTTP/1.1 404 File Not Found\\r\\n\", resp);\n\t\t\t}\n\t\t}\n#endif\n\n\t\t//FIXME: provide some resource->filename mapping that allows various misc files.\n\n\t\tif (body)\n\t\t\t;\n#ifdef _WIN32\n\t\telse if (!strcmp(name, \"favicon.ico\"))\n\t\t{\t//we can serve up the icon from the exe. we just have to reformat it a little.\n\t\t\tst->dlfile = VFSPIPE_Open(1, false);\n\t\t\tif (st->dlfile)\n\t\t\t{\n\t\t\t\tstruct\n\t\t\t\t{\n\t\t\t\t\tshort reserved;\n\t\t\t\t\tshort type;\n\t\t\t\t\tshort count;\n\t\t\t\t\tstruct\n\t\t\t\t\t{\n\t\t\t\t\t\tqbyte width;\n\t\t\t\t\t\tqbyte height;\n\t\t\t\t\t\tqbyte colours;\n\t\t\t\t\t\tqbyte reserved;\n\t\t\t\t\t\tshort planes;\n\t\t\t\t\t\tshort bitcount;\n\t\t\t\t\t\tunsigned short bytes[2];\n\t\t\t\t\t\tunsigned short fileoffset[2];\n\t\t\t\t\t} entry[1];\n\t\t\t\t} icohdr;\n\t\t\t\tstruct\n\t\t\t\t{\n\t\t\t\t\tshort reserved;\n\t\t\t\t\tshort type;\n\t\t\t\t\tshort count;\n\t\t\t\t\tstruct\n\t\t\t\t\t{\n\t\t\t\t\t\tqbyte width;\n\t\t\t\t\t\tqbyte height;\n\t\t\t\t\t\tqbyte colours;\n\t\t\t\t\t\tqbyte reserved;\n\t\t\t\t\t\tshort planes;\n\t\t\t\t\t\tshort bitcount;\n\t\t\t\t\t\tunsigned short bytes[2];\n\t\t\t\t\t\tunsigned short id;\n\t\t\t\t\t} entry[1];\n\t\t\t\t} *grphdr = LockResource(LoadResource(NULL, FindResource(NULL, MAKEINTRESOURCE(IDI_ICON1), RT_GROUP_ICON)));\n\t\t\t\tvoid *blob;\n\t\t\t\t//fixme: scan for the best icon size to use.\n\n\t\t\t\ticohdr.reserved = 0;\n\t\t\t\ticohdr.type = 1;//type\n\t\t\t\ticohdr.count = countof(icohdr.entry);//count\n\t\t\t\ticohdr.entry[0].width = grphdr->entry[0].width;\n\t\t\t\ticohdr.entry[0].height = grphdr->entry[0].height;\n\t\t\t\ticohdr.entry[0].colours = grphdr->entry[0].colours;\n\t\t\t\ticohdr.entry[0].reserved = grphdr->entry[0].reserved;\n\t\t\t\ticohdr.entry[0].planes = grphdr->entry[0].planes;\n\t\t\t\ticohdr.entry[0].bitcount = grphdr->entry[0].bitcount;\n\t\t\t\ticohdr.entry[0].bytes[0] = grphdr->entry[0].bytes[0];\n\t\t\t\ticohdr.entry[0].bytes[1] = grphdr->entry[0].bytes[1];\n\t\t\t\ticohdr.entry[0].fileoffset[0] = sizeof(icohdr);\n\t\t\t\ticohdr.entry[0].fileoffset[1] = sizeof(icohdr)>>16;\n\t\t\t\tVFS_WRITE(st->dlfile, &icohdr, sizeof(icohdr));\n\n\t\t\t\tblob = LockResource(LoadResource(NULL, FindResource(NULL, MAKEINTRESOURCE(grphdr->entry[0].id), RT_ICON)));\n\n\t\t\t\tVFS_WRITE(st->dlfile, blob, grphdr->entry[0].bytes[0] | (grphdr->entry[0].bytes[1]<<16));\n\n\t\t\t\tresp = NULL;\n\t\t\t\tbody = NULL;\n\t\t\t}\n\t\t}\n#else\n\t\telse if (!strcmp(name, \"favicon.ico\"))\n\t\t{\n//\t\t\tCon_Printf(\"Redirect %s to %s (copyrighted)\\n\", arg[WCATTR_URL], NET_AdrToString (adr, sizeof(adr), &st->remoteaddr));\n\t\t\tresp = va(\t\"HTTP/1.1 302 Found\\r\\n\"\n\t\t\t\t\t\"Location: https://fteqw.org/%s\\r\\n\"\n\t\t\t\t\t\"Content-Type: text/html\\r\\n\"\n\t\t\t\t\t, \"favicon.png\");\n\t\t\tbody = NULL;\n\t\t}\n#endif\n#if defined(SV_MASTER)\n\t\telse if (svm_sockets && con->generic.owner==svm_sockets && (st->dlfile=SVM_GenerateIndex(arg[WCATTR_HOST], name, &filetype, query)))\n\t\t\t;\n#endif\n#ifdef HAVE_SERVER\n\t\telse if (!strcmp(name, \"index.html\"))\n\t\t{\t\t\t\n\t\t\tresp = \"HTTP/1.1 200 Ok\\r\\n\"\n\t\t\t\t\"Content-Type: text/html\\r\\n\";\n\n\t\t\tbody = va(\n\t\t\t\t\"<!DOCTYPE HTML>\\n\"\n\t\t\t\t\"<HTML><HEAD>\"\n\t\t\t\t\t\"<meta charset='utf-8' />\"\n\t\t\t\t\t\"<title>%s - %s</title>\"\n\t\t\t\t\t//\"<link rel=\\\"StyleSheet\\\" href=\\\"/style.css\\\" type=\\\"text/css\\\" />\\n\"\n\t\t\t\t\t\"<style>\"\n\t\t\t\t\t\t\"html,body { background-color:#000000; color:#808080; height:100%%;width:100%%;margin:0;padding:0;}\"\n\t\t\t\t\t\t\".emscripten { padding-right: 0; margin-left: auto; margin-right: auto; display: block; }\"\n\t\t\t\t\t\t\"div.emscripten { text-align: center; padding:0; margin: 0;}\"\n\t\t\t\t\t\t\"div.emscripten_border { padding:0; margin: 0; }\"\n\t\t\t\t\t\t/* the canvas *must not* have any border or padding, or mouse coords will be wrong */\n\t\t\t\t\t\t\"canvas.emscripten { border: 0px none; width:100%%; height:100%%; padding:0; margin: 0;}\"\n\t\t\t\t\t\"</style>\"\n\t\t\t\t\"</HEAD><body>\"\n\t\t\t\t\t\"<div class='emscripten' id='status'>Please allow/unblock our javascript to play.</div>\"\n\t\t\t\t\t\"<div class='emscripten'><progress value='0' max='100' id='progress' hidden=1></progress></div>\"\n\t\t\t\t\t\"<canvas class='emscripten' id='canvas' oncontextmenu='event.preventDefault()' hidden=1>Canvas not supported<br/></canvas>\"\n\t\t\t\t\t\"<script type='text/javascript'>\"\n\t\t\t\t\t\t\"var Module = {\"\n\t\t\t\t\t\t\t\"canvas: document.getElementById('canvas'),\"\n\t\t\t\t\t\t\t\"print: function(msg)\"\n\t\t\t\t\t\t\t\"{\"\n\t\t\t\t\t\t\t\t\"console.log(msg);\"\n\t\t\t\t\t\t\t\"},\"\n\t\t\t\t\t\t\t\"printErr: function(text)\"\n\t\t\t\t\t\t\t\"{\"\n\t\t\t\t\t\t\t\t\"if (text.substr(0, 28) == 'Cannot enlarge memory arrays')\"\n\t\t\t\t\t\t\t\t\t\"alert('Memory full/fragmented. Please reload the page.');\"\n\t\t\t\t\t\t\t\t\"else \"\n\t\t\t\t\t\t\t\t\t\"console.log(text);\"\n\t\t\t\t\t\t\t\"},\"\n\t\t\t\t\t\t\t\"setStatus: function(text)\"\n\t\t\t\t\t\t\t\"{\"   //gets spammed some prints during startup. blame emscripten.\n\t\t\t\t\t\t\t\t\"if (Module.setStatus.interval)\"\n\t\t\t\t\t\t\t\t\t\"clearInterval(Module.setStatus.interval);\"\n\t\t\t\t\t\t\t\t\"var m = text.match(/([^(]+)\\\\((\\\\d+(\\\\.\\\\d+)?)\\\\/(\\\\d+)\\\\)/);\"\n\t\t\t\t\t\t\t\t\"var statusElement = document.getElementById('status');\"\n\t\t\t\t\t\t\t\t\"var progressElement = document.getElementById('progress');\"\n\t\t\t\t\t\t\t\t\"if (m) {\"\n\t\t\t\t\t\t\t\t\t\"text = m[1];\"\n\t\t\t\t\t\t\t\t\t\"progressElement.value = parseInt(m[2])*100;\"\n\t\t\t\t\t\t\t\t\t\"progressElement.max = parseInt(m[4])*100;\"\n\t\t\t\t\t\t\t\t\t\"progressElement.hidden = false;\"\n\t\t\t\t\t\t\t\t\"} else {\"\n\t\t\t\t\t\t\t\t\t\"progressElement.value = null;\"\n\t\t\t\t\t\t\t\t\t\"progressElement.max = null;\"\n\t\t\t\t\t\t\t\t\t\"progressElement.hidden = true;\"\n\t\t\t\t\t\t\t\t\"}\"\n\t\t\t\t\t\t\t\t\"statusElement.innerHTML = text;\"\n\t\t\t\t\t\t\t\t\"statusElement.hidden = text.length==0;\"\n\t\t\t\t\t\t\t\"},\"\n\t\t\t\t\t\t\t\"totalDependencies: 0,\"\n\t\t\t\t\t\t\t\"monitorRunDependencies: function(left)\"\n\t\t\t\t\t\t\t\"{\"   //progress is progress...\n\t\t\t\t\t\t\t\t\"this.totalDependencies = Math.max(this.totalDependencies, left);\"\n\t\t\t\t\t\t\t\t\"Module.setStatus(left ? 'Preparing... (' + (this.totalDependencies-left) + '/' + this.totalDependencies + ')' : 'All downloads complete.');\"\n\t\t\t\t\t\t\t\"},\"\n\t\t\t\t\t\t\t\"arguments: [\"\n\t\t\t\t\t\t\t\t\"'-nohome',\"\n\t\t\t\t\t\t\t\t\"'-manifest',\"\n\t\t\t\t\t\t\t\t\"'default.fmf',\"\n\t\t\t\t\t\t\t\t\"'+connect',\"\n\t\t\t\t\t\t\t\t\"(window.location.protocol=='https:'?'wss://':'ws://')+window.location.host\"\n\t\t\t\t\t\t\t\"],\"\n\t\t\t\t\t\t\t\"postRun:\"\n\t\t\t\t\t\t\t\"[\"\t//each of these are called after main was run. we should have our mainloop set up now\n\t\t\t\t\t\t\t\t\"function()\"\n\t\t\t\t\t\t\t\t\"{\"\n\t\t\t\t\t\t\t\t\t\"if (Module['sched'] === undefined)\"\n\t\t\t\t\t\t\t\t\t\"{\"\t//our main function failed to set up the main loop. ie: main didn't get called. panic.\n\t\t\t\t\t\t\t\t\t\t\"alert('Unable to initialise. You may need to restart your browser.');\"\n\t\t\t\t\t\t\t\t\t\t\"Module.setStatus('Initialisation Failure');\"\n\t\t\t\t\t\t\t\t\t\"}\"\n\t\t\t\t\t\t\t\t\"}\"\n\t\t\t\t\t\t\t\"],\"\n\t\t\t\t\t\t\"};\"\n\n\t\t\t\t\t\t//create the script object\n\t\t\t\t\t\t\"var s = document.createElement('script');\"\n\t\t\t\t\t\t// set it up\n\t\t\t\t\t\t\"s.setAttribute('src','/ftewebgl.js');\"\n\t\t\t\t\t\t\"s.setAttribute('type','text/javascript');\"\n\t\t\t\t\t\t\"s.setAttribute('charset','utf-8');\"\n\t\t\t\t\t\t\"s.addEventListener('error', function() {Module.setStatus('Unable to download engine javascript');alert('Error loading game script.');}, false);\"\n\t\t\t\t\t\t// add to DOM\n\t\t\t\t\t\t\"document.head.appendChild(s);\"\n\n\t\t\t\t\t\t//set up some functions that the embedded stuff can use\n\t\t\t\t\t\t\"parent.joinserver = function(d)\"\n\t\t\t\t\t\t\"{\"\n\t\t\t\t\t\t\t\"d = '\\\\nconnect '+d+'\\\\n';\"\n\t\t\t\t\t\t\t\"FTEC.cbufadd(d);\"\n\t\t\t\t\t\t\"};\"\n\t\t\t\t\t\t\"parent.joinstream = function(d)\"\n\t\t\t\t\t\t\"{\"\n\t\t\t\t\t\t\t\"joinserver((window.location.protocol=='https:'?'wss://':'ws://')+window.location.host+'/'+d);\"\n\t\t\t\t\t\t\"};\"\n\t\t\t\t\t\t\"parent.playdemo = function(d)\"\n\t\t\t\t\t\t\"{\"\n\t\t\t\t\t\t\t\"alert('playdemo'+d);\"\n\t\t\t\t\t\t\"};\"\n\t\t\t\t\t\"</script>\"\n\t\t\t\t\t\"<noscript>\"\n\t\t\t\t\t\t\"It looks like you have javascript disabled.<br/>\"\n\t\t\t\t\t\t\"If you want to run a javascript port of a game engine, it helps to have javascript enabled. Just saying.<br/>\"\n\t\t\t\t\t\t\"<br/>\"\n\t\t\t\t\t\"</noscript>\"\n\t\t\t\t\"</body></HTML>\"\n\t\t\t,fs_manifest->formalname, hostname.string);\n\t\t}\n\t\telse if (!strcmp(name, \"ftewebgl.js\") || !strcmp(name, \"ftewebgl.wasm\") || !strcmp(name, \"default.fmf\"))\n\t\t{\n\t\t\tconchar_t musite[256], *e;\n\t\t\tchar site[256];\n\t\t\te = COM_ParseFunString(CON_WHITEMASK, ENGINEWEBSITE, musite, sizeof(musite), false);\n\t\t\tCOM_DeFunString(musite, e, site, sizeof(site)-1, true, true);\n\t\t\tQ_strncatz(site, \"/\", sizeof(site));\n\n\t\t\tst->dlfile = FS_OpenVFS(va(\"%s.gz\", name), \"rb\", FS_ROOT);\n\t\t\tif (st->dlfile)\n\t\t\t\textraheaders = \"Content-Encoding: gzip\\r\\n\";\n\t\t\telse\n\t\t\t{\n\t\t\t\textraheaders = \"\";\n\t\t\t\tst->dlfile = FS_OpenVFS(name, \"rb\", FS_ROOT);\n\t\t\t}\n\t\t\tif (st->dlfile)\n\t\t\t{\t//if we have it locally then use it.\n\t\t\t\tresp = NULL;\n\t\t\t\tbody = NULL;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tresp = va(\t\"HTTP/1.1 302 Found\\r\\n\"\n\t\t\t\t\t\"Location: %s%s\\r\\n\"\n\t\t\t\t\t\"Content-Type: text/html\\r\\n\"\n\t\t\t\t\t, site, name);\n\t\t\t\tbody = \"Please follow the redirect!\";\n\t\t\t}\n\t\t}\n\t\t/*else if (!strcmp(name, \"default.fmf\") && (st->dlfile = FS_OpenVFS(\"default.fmf\", \"rb\", FS_ROOT)))\n\t\t{\n\t\t\tresp =\t\"HTTP/1.1 200 Ok\\r\\n\"\n\t\t\t\t\t\"Content-Type: application/x-ftemanifest\\r\\n\";\n\t\t\tbody = NULL;\n\t\t}*/\n#ifdef MVD_RECORDING\n\t\telse if (!Q_strncasecmp(name, \"demolist\", 8))\n\t\t{\n\t\t\tfiletype = \"text/html\";\n\t\t\tst->dlfile = VFSPIPE_Open(1, false);\n\t\t\tif (st->dlfile)\n\t\t\t\tSV_UserCmdMVDList_HTML(st->dlfile);\n\t\t}\n\t\telse if (!Q_strncasecmp(name, \"demonum/\", 8))\n\t\t{\n\t\t\tchar *mvdname = SV_MVDNum(arg[WCATTR_METHOD], sizeof(arg[WCATTR_METHOD]), atoi(name+8));\n\t\t\tif (mvdname)\n\t\t\t{\n\t\t\t\tCon_Printf(\"Redirect %s to %s (copyrighted)\\n\", arg[WCATTR_URL], NET_AdrToString (adr, sizeof(adr), &st->remoteaddr));\n\t\t\t\tresp = va(\t\"HTTP/1.1 302 Found\\r\\n\"\n\t\t\t\t\t\t\"Location: /demos/%s\\r\\n\"\n\t\t\t\t\t\t\"Content-Type: text/html\\r\\n\"\n\t\t\t\t\t\t, mvdname);\n\t\t\t\tbody = NULL;\n\t\t\t}\n\t\t}\n#endif\n\t\telse if (!SV_AllowDownload(name))\n\t\t{\n\t\t\tCon_Printf(\"Denied download of %s to %s\\n\", arg[WCATTR_URL], NET_AdrToString (adr, sizeof(adr), &st->remoteaddr));\n\t\t\tresp =\t\"HTTP/1.1 403 Forbidden\\r\\n\"\n\t\t\t\t\t\"Content-Type: text/html\\r\\n\";\n\t\t\tbody = va(\"File \\\"%s\\\" may not be downloaded\"\n\t\t\t\t\t, name);\n\t\t}\n\t\telse if (!Q_strncasecmp(name, \"package/\", 8))\n\t\t{\n\t\t\tif (FS_GetPackageDownloadable(name+8))\n\t\t\t\tst->dlfile = FS_OpenVFS(name+8, \"rb\", FS_ROOT);\n\t\t\telse\n\t\t\t{\n\t\t\t\tCon_Printf(\"Unable to download %s to %s (copyrighted)\\n\", arg[WCATTR_URL], NET_AdrToString (adr, sizeof(adr), &st->remoteaddr));\n\t\t\t\tresp = \"HTTP/1.1 403 Forbidden\\r\\n\"\n\t\t\t\t\t\t\"Content-Type: text/html\\r\\n\";\n\t\t\t\tbody = \"File is flagged as copyrighted\";\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tflocation_t gzloc;\n\t\t\tflocation_t rawloc;\n#ifdef MVD_RECORDING\n\t\t\tif (!Q_strncasecmp(name, \"demos/\", 6))\n\t\t\t\tname = va(\"%s/%s\", sv_demoDir.string, name+6);\n#endif\n\n\t\t\tif (FS_FLocateFile(name, FSLF_IFFOUND, &rawloc))\n\t\t\t{\n\t\t\t\tchar gzname[MAX_QPATH];\n\t\t\t\ttime_t rt;\n\t\t\t\tFS_GetLocMTime(&rawloc, &rt);\n\t\t\t\tQ_snprintfz(gzname, sizeof(gzname), \"%s.gz\", name);\n\t\t\t\tif (allowgzip && FS_FLocateFile(gzname, FSLF_IFFOUND, &gzloc))\n\t\t\t\t{\n\t\t\t\t\ttime_t gt;\n\t\t\t\t\tif (rawloc.search == gzloc.search && FS_GetLocMTime(&gzloc, &gt) && gt >= rt)\n\t\t\t\t\t{\t//must be in the same gamedir, and not older\n\t\t\t\t\t\textraheaders = \"Content-Encoding: gzip\\r\\n\";\n\t\t\t\t\t\trawloc = gzloc;\n\t\t\t\t\t\trt = gt;\n\t\t\t\t\t\tCon_DPrintf(\"HTTP: Serving %s instead\\n\", gzname);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\tCon_DPrintf(\"HTTP: Ignoring %s, outdated\\n\", gzname);\n\t\t\t\t}\n\n\t\t\t\tmodificationtime = rt;\n\n\t\t\t\tif (rawloc.search->flags & SPF_COPYPROTECTED)\n\t\t\t\t{\n\t\t\t\t\tCon_Printf(\"Unable to download %s to %s (copyrighted)\\n\", arg[WCATTR_URL], NET_AdrToString (adr, sizeof(adr), &st->remoteaddr));\n\t\t\t\t\tresp =\t\"HTTP/1.1 403 Forbidden\\r\\n\"\n\t\t\t\t\t\t\t\"Content-Type: text/html\\r\\n\";\n\n\t\t\t\t\tbody = va(\"File %s inside a package<br/><a href=\\\"/package/%s\\\">Download</a>\"\n\t\t\t\t\t\t\t, name, FS_GetPackageDownloadFilename(&rawloc));\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tst->dlfile = rawloc.search->handle->OpenVFS(rawloc.search->handle, &rawloc, \"rb\");\n\t\t\t}\n\t\t\telse\n\t\t\t\tst->dlfile = NULL;\n\t\t}\n#endif\n\t\tif (st->dlfile)\n\t\t{\n\t\t\tchar etag[64];\n\t\t\tif (!filetype)\n\t\t\t{\n\t\t\t\tconst char *ext;\n\t\t\t\tint i;\n\t\t\t\tstatic const char *mimes[] =\n\t\t\t\t{\n\t\t\t\t\t\".html\",\t\"text/html\",\n\t\t\t\t\t\".htm\",\t\t\"text/html\",\n\t\t\t\t\t\".png\",\t\t\"image/png\",\n\t\t\t\t\t\".jpeg\",\t\"image/jpeg\",\n\t\t\t\t\t\".jpg\",\t\t\"image/jpeg\",\n\t\t\t\t\t\".ico\",\t\t\"image/vnd.microsoft.icon\",\n\t\t\t\t\t\".pk3\",\t\t\"application/zip\",\n\t\t\t\t\t\".fmf\",\t\t\"application/x-ftemanifest\",\n\t\t\t\t\t\".qtv\",\t\t\"application/x-qtv\",\n\t\t\t\t\t\".wasm\",\t\"application/wasm\",\n\t\t\t\t\t\".js\",\t\t\"text/javascript\",\n\n\t\t\t\t\t\".mvd\",\t\t\"application/x-multiviewdemo\",\n\t\t\t\t\t\".mvd.gz\",\t\"application/x-multiviewdemo\",\n\t\t\t\t\t\".qwd\",\t\t\"application/x-quakeworlddemo\",\n\t\t\t\t\t\".qwd.gz\",\t\"application/x-quakeworlddemo\",\n\t\t\t\t\t\".dem\",\t\t\"application/x-quakedemo\",\n\t\t\t\t\t\".dem.gz\",\t\"application/x-quakedemo\",\n\t\t\t\t};\n\t\t\t\text = COM_GetFileExtension(name, NULL);\n\t\t\t\tif (!strcmp(ext, \".gz\")||!strcmp(ext, \".xz\"))\n\t\t\t\t\text = COM_GetFileExtension(name, ext);\n\t\t\t\tfor (i = 0; i < countof(mimes); i+=2)\n\t\t\t\t{\n\t\t\t\t\tif (!Q_strcasecmp(ext, mimes[i]))\n\t\t\t\t\t{\n\t\t\t\t\t\tfiletype = mimes[i+1];\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (modificationtime)\n\t\t\t{\n\t\t\t\tQ_snprintfz(etag, sizeof(etag), \"W/\\\"%0\"PRIxQOFS\"\\\"\", (qofs_t)modificationtime);\n\t\t\t\tif (!strcmp(arg[WCATTR_IFNONEMATCH], etag))\n\t\t\t\t{\n\t\t\t\t\tresp =\t\"HTTP/1.1 304 Not Modified \\r\\n\";\n\t\t\t\t\tbody = NULL;\n\t\t\t\t}\n\n\t\t\t\tQ_snprintfz(etag, sizeof(etag), \"ETag: W/\\\"%0\"PRIxQOFS\"\\\"\\r\\n\", (qofs_t)modificationtime);\n\t\t\t}\n\t\t\telse *etag = 0;\n\n\t\t\tif (resp)\n\t\t\t{\n\t\t\t\tVFS_CLOSE(st->dlfile);\n\t\t\t\tst->dlfile = NULL;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tCon_Printf(\"Downloading %s to %s\\n\", arg[WCATTR_URL], NET_AdrToString (adr, sizeof(adr), &st->remoteaddr));\n\t\t\t\tif (filetype)\n\t\t\t\t{\n\t\t\t\t\tresp =\tva(\"HTTP/1.1 200 Ok\\r\\n\"\n\t\t\t\t\t\t\t\"Content-Type: %s\\r\\n\"\n\t\t\t\t\t\t\t\"%s%s\",\n\t\t\t\t\t\t\tfiletype,\n\t\t\t\t\t\t\tetag,extraheaders);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tresp =\tva(\"HTTP/1.1 200 Ok\\r\\n\"\n\t\t\t\t\t\t\t\"%s%s\",\n\t\t\t\t\t\t\tetag,extraheaders);\n\t\t\t\tbody = NULL;\n\t\t\t}\n\t\t}\n\t\telse if (!resp)\n\t\t{\n\t\t\tCon_Printf(\"Unable to download %s to %s\\n\", arg[WCATTR_URL], NET_AdrToString (adr, sizeof(adr), &st->remoteaddr));\n\t\t\tresp =\t\t\"HTTP/1.1 404 File Not Found\\r\\n\"\n\t\t\t\t\t\t\"Content-Type: text/html\\r\\n\";\n\t\t\tbody = \t\t\"File not found\";\n\t\t}\n\t}\n\tif (!resp)\n\t{\n\t\tCon_Printf(\"Invalid download request %s to %s\\n\", arg[WCATTR_URL], NET_AdrToString (adr, sizeof(adr), &st->remoteaddr));\n\t\tresp =\t\t\"HTTP/1.1 404 File Not Found\\r\\n\"\n\t\t\t\t\t\"Content-Type: text/html\\r\\n\";\n\t\tbody =\t\t\"This is a Quake WebSocket server, not an http server.<br/>\\r\\n\"\n\t\t\t\t\t//\"<a href='\"ENGINEWEBSITE\"'>\"FULLENGINENAME\"</a>\"\n\t\t\t\t\t;\n\t}\n\n\tst->clienttype = TCPC_HTTPCLIENT;\n\n\ti = strlen(resp);\n\tif (st->outlen + i > sizeof(st->outbuffer))\n\t\treturn false;\n\tmemcpy(st->outbuffer+st->outlen, resp, i);\n\tst->outlen+= i;\n\n\tresp = \"Access-Control-Allow-Origin: *\\r\\n\";\n\ti = strlen(resp);\n\tif (st->outlen + i > sizeof(st->outbuffer))\n\t\treturn false;\n\tmemcpy(st->outbuffer+st->outlen, resp, i);\n\tst->outlen+= i;\n\n\tif (st->httpstate.connection_close)\n\t{\n\t\tresp = \"Connection: Close\\r\\n\";\n\t\ti = strlen(resp);\n\t\tif (st->outlen + i > sizeof(st->outbuffer))\n\t\t\treturn false;\n\t\tmemcpy(st->outbuffer+st->outlen, resp, i);\n\t\tst->outlen+= i;\n\t}\n\n\tif (st->dlfile || body)\n\t{\n\t\tqofs_t size;\n\t\tif (body)\n\t\t\tsize = strlen(body);\n\t\telse\n\t\t\tsize = VFS_GETLEN(st->dlfile);\n\t\tresp = adr;\n\t\tQ_snprintfz(adr, sizeof(adr), \"Content-Length: %\"PRIuQOFS\"\\r\\n\", size);\n\t}\n\telse\n\t\tresp = \"Content-Length: 0\\r\\n\";\n\ti = strlen(resp);\n\tif (st->outlen + i > sizeof(st->outbuffer))\n\t\treturn false;\n\tmemcpy(st->outbuffer+st->outlen, resp, i);\n\tst->outlen+= i;\n\n\tif (st->outlen + 2 > sizeof(st->outbuffer))\n\t\treturn false;\n\tmemcpy(st->outbuffer+st->outlen, \"\\r\\n\", 2);\n\tst->outlen+= 2;\n\n\tif (method == 1)\n\t{\t//body is not included in HEAD responses\n\t\tbody = NULL;\n\t\tif (st->dlfile)\n\t\t\tVFS_CLOSE(st->dlfile);\n\t\tst->dlfile = NULL;\n\t}\n\telse if (body)\n\t{\n\t\ti = strlen(body);\n\t\tif (st->outlen + i > sizeof(st->outbuffer))\n\t\t\treturn false;\n\t\tmemcpy(st->outbuffer+st->outlen, body, i);\n\t\tst->outlen+= i;\n\t}\n\treturn true;\n}\n\nstatic int FTENET_TCP_WebRTCIncludeRelay(char *buffer, size_t bufsize,  ftenet_tcp_stream_t *list, ftenet_tcp_stream_t *receipient)\n{\n\tint len;\n\t*buffer = 0;\n#ifdef SV_MASTER\n\tSVM_SelectRelay(&receipient->remoteaddr, receipient->webrtc.resource, buffer,bufsize);\n#endif\n\tlen = strlen(buffer);\n\tbuffer[len++] = 0;\t//always add a null to end the list.\n\treturn len;\n}\n\nvoid FTENET_TCP_WebRTCServerAssigned(ftenet_tcp_stream_t *list, ftenet_tcp_stream_t *client, ftenet_tcp_stream_t *server)\n{\n\tqbyte buffer[256];\n\tint trynext = 0;\n\tint len = 0;\n\tftenet_tcp_stream_t *o;\n\tif (client->webrtc.clientnum < 0)\n\t\tclient->webrtc.clientnum = 0;\n\tfor(;;)\n\t{\n\t\tfor (o = list; o; o = o->next)\n\t\t{\n\t\t\tif (o != client && o->clienttype == TCPC_WEBRTC_CLIENT && !strcmp(o->webrtc.resource, client->webrtc.resource) && client->webrtc.clientnum == o->webrtc.clientnum)\n\t\t\t\tbreak;\n\t\t}\n\t\tif (!o)\n\t\t\tbreak;\n\t\tclient->webrtc.clientnum = trynext++;\n\t}\n\n\tif (server)\n\t{\t//and tell them both, if the server is actually up\n\t\tbuffer[len++] = ICEMSG_NEWPEER;\n\t\tbuffer[len++] = (client->webrtc.clientnum>>0)&0xff;\n\t\tbuffer[len++] = (client->webrtc.clientnum>>8)&0xff;\n//\t\tbuffer[len++] = (client->webrtc.clientnum>>16)&0xff;\n//\t\tbuffer[len++] = (client->webrtc.clientnum>>24)&0xff;\n\n\t\t//write the client's address, kinda\n\t\tif (client->remoteaddr.type == NA_IP)\t//anonymise it. hopefully still enough of an address to ban.\n\t\t\tQ_snprintfz(buffer+len, sizeof(buffer)-len, \"%i.%i\", client->remoteaddr.address.ip[0], client->remoteaddr.address.ip[1]);\n\t\telse if (client->remoteaddr.type == NA_IPV6)\t//anonymise it. we don't really know how big an allocation their router got... so include the first 4 bytes and hash the rest to compensate somewhat. most of it'll probably random though. this is messy. the server will be identifying connections more by index.\n\t\t\tQ_snprintfz(buffer+len, sizeof(buffer)-len, \"%04x:%04x-%04x\", client->remoteaddr.address.ip6[0]|client->remoteaddr.address.ip6[1], client->remoteaddr.address.ip6[2]|client->remoteaddr.address.ip6[3], 0xffffu&CalcHashInt(&hash_sha1, client->remoteaddr.address.ip6+4, sizeof(client->remoteaddr.address.ip6)-4));\n\t\telse\n\t\t{\t//generically shove the client's address into the broker->server packet\n\t\t\tint o = client->remoteaddr.prot;\n\t\t\tclient->remoteaddr.prot = 0;\n\t\t\tNET_BaseAdrToString(buffer+len, sizeof(buffer)-len, &client->remoteaddr);\t//let the server know who's trying to connect to them. for ip bans.\n\t\t\tclient->remoteaddr.prot = o;\n\t\t}\n\t\tlen += strlen(buffer+len)+1;\n\t\tlen += FTENET_TCP_WebRTCIncludeRelay(buffer+len,sizeof(buffer)-len, list, server);\n\t\tFTENET_TCP_WebSocket_Splurge(server, WS_PACKETTYPE_BINARYFRAME, buffer, len);\n\n\t\tlen = 0;\n\t\tbuffer[len++] = ICEMSG_NEWPEER;\n\t\tbuffer[len++] = 0xff;\n\t\tbuffer[len++] = 0xff;\n//\t\tbuffer[len++] = 0xff;\n//\t\tbuffer[len++] = 0xff;\n\t\tbuffer[len++] = 0;\t//no remote peer name info...\n\t\tlen += FTENET_TCP_WebRTCIncludeRelay(buffer+len,sizeof(buffer)-len, list, server);\n\t\tFTENET_TCP_WebSocket_Splurge(client, WS_PACKETTYPE_BINARYFRAME, buffer, len);\n\t}\n}\n\nstatic const char *FTENET_TCP_ParseHTTPRequest(ftenet_tcp_connection_t *con, ftenet_tcp_stream_t *st)\n{\n\tchar *resp;\n\tchar adr[256];\n\tint i, j;\n\tint attr = 0;\n\tint alen = 0;\n\tqboolean headerscomplete = false;\n\tint contentlen = 0;\n\tint websocketver = 0;\n\tqboolean acceptsgzip = false;\n\tqboolean sendingweirdness = false;\n\thttparg_t arg[WCATTR_COUNT];\n\n\n\tif (!net_enable_http.ival && !net_enable_websockets.ival && !net_enable_rtcbroker.ival)\n\t{\n\t\t//we need to respond, firefox will create 10 different connections if we just close it\n\t\tresp = va(\t\"HTTP/1.1 403 Forbidden\\r\\n\"\n\t\t\t\t\t\"Connection: close\\r\\n\"\t//let the client know that any pipelining it was doing will have been ignored\n\t\t\t\t\t\"\\r\\n\");\n\t\tVFS_WRITE(st->clientstream, resp, strlen(resp));\n\t\treturn \"websockets disabled\";\n\t}\n\n\tfor (i = 0; i < WCATTR_COUNT; i++)\n\t\targ[i][0] = 0;\n\tfor (i = 0; i < st->inlen; i++)\n\t{\n\t\tif (alen >= sizeof(arg[attr])-1)\n\t\t{\n\t\t\tCon_Printf(\"http request overflow from %s\\n\", NET_AdrToString (adr, sizeof(adr), &st->remoteaddr));\n\t\t\t//we need to respond, firefox will create 10 different connections if we just close it\n\t\t\tresp = va(\t\"HTTP/1.1 414 URI Too Long\\r\\n\"\n\t\t\t\t\t\t\"Connection: close\\r\\n\"\t//let the client know that any pipelining it was doing will have been ignored\n\t\t\t\t\t\t\"\\r\\n\");\n\t\t\tVFS_WRITE(st->clientstream, resp, strlen(resp));\n\t\t\treturn \"overflow\";\t//overflow...\n\t\t}\n\t\tif (st->inbuffer[i] == ' ' || st->inbuffer[i] == '\\t' || st->inbuffer[i] == '\\r')\n\t\t{\n\t\t\targ[attr][alen++] = 0;\n\t\t\talen=0;\n\t\t\tif (attr++ == WCATTR_HTTP)\n\t\t\t\tbreak;\n\n\t\t\tfor (; i < st->inlen && (st->inbuffer[i] == ' ' || st->inbuffer[i] == '\\t' || st->inbuffer[i] == '\\r'); i++)\n\t\t\t\t;\n\t\t\tif (i == st->inlen)\n\t\t\t\tbreak;\n\t\t}\n\t\tif (st->inbuffer[i] == '\\n')\n\t\t{\n\t\t\targ[attr][alen++] = 0;\n\t\t\talen=0;\n\t\t\tbreak;\n\t\t}\n\t\tif (st->inbuffer[i] < ' ' && st->inbuffer[i] != '\\t')\n\t\t{\n\t\t\tCon_Printf(\"http request contained control codes\\n\");\n\t\t\treturn \"bad char\";\n\t\t}\n\t\targ[attr][alen++] = st->inbuffer[i];\n\t}\n\tif (!*arg[WCATTR_URL])\t//don't bug out if it was truncated.\n\t\tstrcpy(arg[WCATTR_URL], \"/\");\n\n\tif (st->inbuffer[i] == '\\r')\n\t\ti++;\n\tif (st->inbuffer[i] == '\\n')\n\t{\t//okay, we have at least a line... try scanning the rest of the header for known key:value pairs, and see if we can reach the end\n\t\ti++;\n\n\t\tattr = 0;\n\t\tj = i;\n\t\tfor (; i < st->inlen; i++)\n\t\t{\n\t\t\tif ((i+1 < st->inlen && st->inbuffer[i] == '\\r' && st->inbuffer[i+1] == '\\n') ||\n\t\t\t\t(i < st->inlen && st->inbuffer[i] == '\\n'))\n\t\t\t{\n\t\t\t\tif (st->inbuffer[i] == '\\n')\n\t\t\t\t\ti++;\n\t\t\t\telse\n\t\t\t\t\ti+=2;\n\t\t\t\theaderscomplete = true;\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tfor (; i < st->inlen && (st->inbuffer[i] == ' ' || st->inbuffer[i] == '\\t'); i++)\n\t\t\t\t;\n\t\t\tif (i == st->inlen)\n\t\t\t\tbreak;\n\n\t\t\tfor (j = i; j < st->inlen; j++)\n\t\t\t{\n\t\t\t\tif (st->inbuffer[j] == ':' || st->inbuffer[j] == '\\n')\n\t\t\t\t{\n\t\t\t\t\t/*set j to the end of the word, going back past whitespace*/\n\t\t\t\t\twhile (j > i && (st->inbuffer[j-1] == ' ' || st->inbuffer[i-1] == '\\t'))\n\t\t\t\t\t\tj--;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (j-i == 4 && !strnicmp(&st->inbuffer[i], \"Host\", 4))\n\t\t\t\tattr = WCATTR_HOST;\n\t\t\telse if (j-i == 7 && !strnicmp(&st->inbuffer[i], \"Upgrade\", 7))\n\t\t\t\tattr = WCATTR_UPGRADE;\n\t\t\telse if (j-i == 10 && !strnicmp(&st->inbuffer[i], \"Connection\", 10))\n\t\t\t\tattr = WCATTR_CONNECTION;\n\t\t\t//websocket stuff\n\t\t\telse if (j-i == 17 && !strnicmp(&st->inbuffer[i], \"Sec-WebSocket-Key\", 17))\n\t\t\t\tattr = WCATTR_WSKEY;\n\t\t\telse if (j-i == 21 && !strnicmp(&st->inbuffer[i], \"Sec-WebSocket-Version\", 21))\n\t\t\t\tattr = WCATTR_WSVER;\n//\t\t\telse if (j-i == 6 && !strnicmp(&st->inbuffer[i], \"Origin\", j-i))\n//\t\t\t\tattr = WCATTR_ORIGIN;\n\t\t\telse if (j-i == 22 && !strnicmp(&st->inbuffer[i], \"Sec-WebSocket-Protocol\", 22))\n\t\t\t\tattr = WCATTR_WSPROTO;\n//\t\t\telse if (j-i == 24 && !strnicmp(&st->inbuffer[i], \"Sec-WebSocket-Extensions\", 24))\n//\t\t\t\tattr = WCATTR_WSEXT;\n\t\t\t//http stuff\n\t\t\telse if (j-i == 14 && !strnicmp(&st->inbuffer[i], \"Content-Length\", 14))\n\t\t\t\tattr = WCATTR_CONTENT_LENGTH;\t//in case they're trying to post/put stuff\n\t\t\telse if (j-i == 15 && !strnicmp(&st->inbuffer[i], \"Accept-Encoding\", 15))\n\t\t\t\tattr = WCATTR_ACCEPT_ENCODING;\t//for gzip\n\t\t\telse if (j-i == 17 && !strnicmp(&st->inbuffer[i], \"Transfer-Encoding\", 17))\n\t\t\t\tattr = WCATTR_TRANSFER_ENCODING;//in case they're trying to post/put complex stuff\n\t\t\telse if (j-i == 13 && !strnicmp(&st->inbuffer[i], \"If-None-Match\", 13))\n\t\t\t\tattr = WCATTR_IFNONEMATCH;//for clientside caches\n\t\t\t//FIXME: X-Forwarded-For for _trusted_ wss proxies. use last-listed only, and only if sender is [127.0.0.1]/[::1]\n\t\t\telse\n\t\t\t\tattr = 0;\n\n\t\t\ti = j;\n\t\t\t/*skip over the whitespace at the end*/\n\t\t\tfor (; i < st->inlen && (st->inbuffer[i] == ' ' || st->inbuffer[i] == '\\t'); i++)\n\t\t\t\t;\n\t\t\tif (i < st->inlen && st->inbuffer[i] == ':')\n\t\t\t{\n\t\t\t\ti++;\n\t\t\t\tfor (; i < st->inlen && (st->inbuffer[i] == ' ' || st->inbuffer[i] == '\\t'); i++)\n\t\t\t\t\t;\n\t\t\t\tj = i;\n\n\t\t\t\t//FIXME: check for control codes. although probably not a problem in this part\n\t\t\t\tfor (; i < st->inlen && st->inbuffer[i] != '\\n'; i++)\n\t\t\t\t\t;\n\t\t\t\tif (i > j && st->inbuffer[i-1] == '\\r')\n\t\t\t\t\ti--;\n\t\t\t\tif (attr)\n\t\t\t\t{\n\t\t\t\t\tswitch(attr)\n\t\t\t\t\t{\n\t\t\t\t\tcase WCATTR_CONTENT_LENGTH:\n\t\t\t\t\t\tcontentlen = atoi(&st->inbuffer[j]);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase WCATTR_ACCEPT_ENCODING:\n\t\t\t\t\t\twhile (j < i)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (st->inbuffer[j] == ' ' || st->inbuffer[j] == '\\t')\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tj++;\n\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse if (j+4 <= i && !strncmp(&st->inbuffer[j], \"gzip\", 4) && (j+4==i || st->inbuffer[j+4] == ';' || st->inbuffer[j+4] == ','))\n\t\t\t\t\t\t\t\tacceptsgzip = true;\n\n\t\t\t\t\t\t\twhile (j < i && st->inbuffer[j] != ',')\n\t\t\t\t\t\t\t\tj++;\n\t\t\t\t\t\t\tif (j < i && st->inbuffer[j] == ',')\n\t\t\t\t\t\t\t\tj++;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase WCATTR_TRANSFER_ENCODING:\n\t\t\t\t\t\tsendingweirdness = true;\t//doesn't matter what it is, we can't handle it.\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase WCATTR_WSVER:\n\t\t\t\t\t\twebsocketver = atoi(&st->inbuffer[j]);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tQ_strncpyz(arg[attr], &st->inbuffer[j], (i-j >= sizeof(arg[attr]))?sizeof(arg[attr]):(i - j + 1));\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (i < st->inlen && st->inbuffer[i] == '\\r')\n\t\t\t\t\ti++;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t/*just a word on the line on its own. that would be invalid in http*/\n\t\t\t\treturn \"bad header\";\n\t\t\t}\n\t\t}\n\t}\n\n\tif (!headerscomplete)\n\t{\n\t\tCon_DPrintf(\"http header parsing failed\\n\");\n\t\treturn \"bad header\";\t//the caller said it was complete! something's fucked if we're here\n\t}\n\n\t//okay, the above code parsed all the headers that we care about.\n\n\tif (contentlen && i+contentlen > st->inlen)\n\t{\t//request isn't complete yet\n\t\tif (i+contentlen > sizeof(st->inbuffer)-1)\n\t\t{\n\t\t\tresp = va(\t\"HTTP/1.1 413 Payload Too Large \\r\\n\"\n\t\t\t\t\t\t\"Connection: close\\r\\n\"\t//let the client know that any pipelining it was doing will have been ignored\n\t\t\t\t\t\t\"\\r\\n\");\n\t\t\tVFS_WRITE(st->clientstream, resp, strlen(resp));\n\t\t\tCon_DPrintf(\"http oversize request\\n\");\n\t\t\treturn \"bad header\";\t//can never be completed.\n\t\t}\n\t\treturn NULL;\n\t}\n\n\t//clients uploading chunked stuff is bad/unsupported.\n\tif (sendingweirdness)\n\t{\n\t\tresp = va(\t\"HTTP/1.1 413 Payload Too Large \\r\\n\"\n\t\t\t\t\t\"Connection: close\\r\\n\"\t//let the client know that any pipelining it was doing will have been ignored\n\t\t\t\t\t\"\\r\\n\");\n\t\tVFS_WRITE(st->clientstream, resp, strlen(resp));\n\t\tCon_DPrintf(\"unsupported http encoded request\\n\");\n\t\treturn \"unsupported\";\t//can't handle the request, so discard the connection\n\t}\n\n\tmemmove(st->inbuffer, st->inbuffer+i, st->inlen - (i));\n\tst->inlen -= i;\n\n\t//for websocket connections:\n\t//must be a Host, Upgrade=websocket, Connection=Upgrade, Sec-WebSocket-Key=base64(randbytes(16)), Sec-WebSocket-Version=13\n\t//optionally will be Origin=url, Sec-WebSocket-Protocol=FTEWebSocket, Sec-WebSocket-Extensions\n\t//other fields will be ignored.\n\tif (!stricmp(arg[WCATTR_UPGRADE], \"websocket\") && (!stricmp(arg[WCATTR_CONNECTION], \"Upgrade\") || !stricmp(arg[WCATTR_CONNECTION], \"keep-alive, Upgrade\")))\n\t{\n\t\tint cltype;\n\t\tchar prot[64];\n\t\tconst char *p = arg[WCATTR_WSPROTO];\n\t\tcom_tokentype_t toktype;\n\t\tconst char *protoname = \"\";\n\t\tstatic const struct\n\t\t{\n\t\t\tconst char *name;\n\t\t\tint tcpc;\n\t\t} protonames[] = {\t{\"fteqw\",TCPC_WEBSOCKETB},\t\t\t//our standard-data-over-ws protocol. even challenges (they're used for extension handshakes)\n\t\t\t\t\t\t//\t{\"binary\",TCPC_WEBSOCKETB},\t\t\t//older name for 'fteqw', from back when emscripten hardcoded stuff.\n\t\t\t\t\t\t\t{\"quake\", TCPC_WEBSOCKETNQ},\t\t//quirky mangled headers, no OOB, included purely for compat with netquake.io's servers.\n\t\t\t\t\t\t\t{\"rtc_client\", TCPC_WEBRTC_CLIENT},\t//an fte client trying to connect to a server.\n\t\t\t\t\t\t\t{\"rtc_host\", TCPC_WEBRTC_HOST}};\t//an fte server advertising itself.\n\n\t\tcltype = TCPC_UNKNOWN;\n\t\twhile(*p)\n\t\t{\n\t\t\tint pidx;\n\t\t\tp = COM_ParseTokenOut(p, \",\", prot,sizeof(prot), &toktype);\n\t\t\tif (toktype == TTP_LINEENDING || toktype == TTP_EOF)\n\t\t\t\tbreak;\n\t\t\tif (toktype == TTP_PUNCTUATION)\n\t\t\t\tcontinue;\n\t\t\tif (toktype == TTP_RAWTOKEN)\n\t\t\t{\n\t\t\t\tfor (pidx = 0; pidx < countof(protonames); pidx++)\n\t\t\t\t{\n\t\t\t\t\tif (!strcmp(prot, protonames[pidx].name))\n\t\t\t\t\t{\n\t\t\t\t\t\tprotoname = protonames[pidx].name;\n\t\t\t\t\t\tcltype = protonames[pidx].tcpc;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (pidx < countof(protonames))\n\t\t\t\t{\t//found a match use it. favour the first listed like the docs say, unless its going to be misordered due to netquake.io's servers being shite.\n\t\t\t\t\tif (cltype == TCPC_WEBSOCKETNQ)\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (cltype == TCPC_UNKNOWN)\n\t\t\treturn \"websocket protocol not specified/known\";\n\n\t\tif (cltype == TCPC_WEBRTC_CLIENT||cltype==TCPC_WEBRTC_HOST)\n\t\t{\n\t\t\tif (!net_enable_rtcbroker.ival)\n\t\t\t\treturn \"broker disabled\";\n\t\t}\n\t\telse\t//TCPC_WEBSOCKETNQ, TCPC_WEBSOCKETB, TCPC_WEBSOCKETU\n\t\t{\n\t\t\tif (!net_enable_websockets.ival)\n\t\t\t\treturn \"websocket clients disabled\";\n\t\t}\n\n\t\tif (websocketver != 13)\n\t\t{\n\t\t\tCon_DPrintf(\"Outdated websocket request for \\\"%s\\\" from \\\"%s\\\". got version %i, expected version 13\\n\", arg[WCATTR_URL], NET_AdrToString (adr, sizeof(adr), &st->remoteaddr), websocketver);\n\n\t\t\tresp = va(\t\"HTTP/1.1 426 Upgrade Required\\r\\n\"\n\t\t\t\t\t\t\"Sec-WebSocket-Version: 13\\r\\n\"\n\t\t\t\t\t\t\"Connection: close\\r\\n\"\t//let the client know that any pipelining it was doing will have been ignored\n\t\t\t\t\t\t\"\\r\\n\");\n\t\t\tVFS_WRITE(st->clientstream, resp, strlen(resp));\n\t\t\treturn \"bad header\";\n\t\t}\n\t\telse\n\t\t{\n\t\t\tconst char *failreason = NULL;\n\t\t\tchar acceptkey[20*2];\n\t\t\tunsigned char sha1digest[20];\n\t\t\tchar *blurgh;\n\t\t\tstatic int clientseq;\n\n\t\t\tblurgh = va(\"%s258EAFA5-E914-47DA-95CA-C5AB0DC85B11\", arg[WCATTR_WSKEY]);\n\t\t\ttobase64(acceptkey, sizeof(acceptkey), sha1digest, CalcHash(&hash_sha1, sha1digest, sizeof(sha1digest), blurgh, strlen(blurgh)));\n\n\t\t\tif (st->remoteaddr.prot == NP_TLS)\n\t\t\t\tst->remoteaddr.prot = NP_WSS;\n\t\t\telse\n\t\t\t\tst->remoteaddr.prot = NP_WS;\n\n\t\t\tif (*protoname)\n\t\t\t\tprotoname = va(\"Sec-WebSocket-Protocol: %s\\r\\n\", protoname);\n\t\t\tst->clienttype = cltype;\n\n\t\t\tswitch(st->clienttype)\n\t\t\t{\n\t\t\tcase TCPC_WEBSOCKETNQ:\n\t\t\tcase TCPC_WEBSOCKETU:\n\t\t\tcase TCPC_WEBSOCKETB:\n\t\t\t\tif (!net_enable_websockets.ival)\n\t\t\t\t\tfailreason = \"blocked by net_enable_websockets\";\n\t\t\t\tbreak;\n\t\t\tcase TCPC_WEBRTC_HOST:\n\t\t\tcase TCPC_WEBRTC_CLIENT:\n\t\t\t\tif (!net_enable_rtcbroker.ival)\n\t\t\t\t\tfailreason = \"blocked by net_enable_rtcbroker\";\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tfailreason = \"unsupported protocol type\";\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tif (*arg[WCATTR_URL] == '/')\n\t\t\t\tQ_strncpyz(st->webrtc.resource, arg[WCATTR_URL]+1, sizeof(st->webrtc.resource));\n\t\t\telse\n\t\t\t\tQ_strncpyz(st->webrtc.resource, arg[WCATTR_URL], sizeof(st->webrtc.resource));\n\t\t\tst->webrtc.clientnum = -1;\n\t\t\tst->webrtc.clientseq = clientseq++;\n#ifndef SUPPORT_RTC_ICE\n\t\t\tif (st->clienttype == TCPC_WEBRTC_CLIENT && !*st->webrtc.resource)\n\t\t\t\tfailreason = \"client did not specify resource type\";\n#endif\n\n\t\t\tif (failreason)\n\t\t\t{\n\t\t\t\tCon_DPrintf(\"Websocket(%s) request for %s from %s - %s\\n\", arg[WCATTR_WSPROTO], arg[WCATTR_URL], NET_AdrToString (adr, sizeof(adr), &st->remoteaddr), failreason);\n\t\t\t\treturn failreason;\n\t\t\t}\n\t\t\tCon_DPrintf(\"Websocket request for %s from %s (%s)\\n\", arg[WCATTR_URL], NET_AdrToString (adr, sizeof(adr), &st->remoteaddr), arg[WCATTR_WSPROTO]);\n\n\t\t\tresp = va(\t\"HTTP/1.1 101 WebSocket Protocol Handshake\\r\\n\"\n\t\t\t\t\t\t\"Upgrade: websocket\\r\\n\"\n\t\t\t\t\t\t\"Connection: Upgrade\\r\\n\"\n\t\t\t\t\t\t\"Access-Control-Allow-Origin: *\\r\\n\"\t//allow cross-origin requests. this means you can use any domain to play on any public server.\n\t\t\t\t\t\t\"Sec-WebSocket-Accept: %s\\r\\n\"\n\t\t\t\t\t\t\"%s\"\n\t\t\t\t\t\t\"\\r\\n\", acceptkey, protoname);\n\t\t\t//send the websocket handshake response.\n\t\t\tVFS_WRITE(st->clientstream, resp, strlen(resp));\n\n\t\t\tif (st->clienttype == TCPC_WEBRTC_HOST || st->clienttype == TCPC_WEBRTC_CLIENT)\n\t\t\t{\n\t\t\t\tftenet_tcp_stream_t *o;\n\n\t\t\t\t//split the requested resource by protocol/room\n\t\t\t\tchar *idstart = strchr(st->webrtc.resource, '/');\n\t\t\t\tif (!idstart++)\n\t\t\t\t{\t//MUST have a protocol name\n\t\t\t\t\treturn \"no game protocol specified\";\n\t\t\t\t}\n\n\t\t\t\tif (st->clienttype == TCPC_WEBRTC_HOST)\n\t\t\t\t{\n\t\t\t\t\tif (!*idstart)\n\t\t\t\t\t{\t//webrtc servers need some unique resource address. lets use their ip+port for now. we should probably be randomising this\n\t\t\t\t\t\tstatic unsigned int g;\n\t\t\t\t\t\tint ofs = strlen(st->webrtc.resource);\n\t\t\t\t\t\tQ_snprintfz(st->webrtc.resource+ofs, sizeof(st->webrtc.resource)-ofs, \"%u\", ++g);\n\t\t\t\t\t}\n\n\t\t\t\t\tfor (o = con->tcpstreams; o; o = o->next)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (o != st && o->clienttype == TCPC_WEBRTC_HOST && !strcmp(st->webrtc.resource, o->webrtc.resource))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tnet_message_buffer[0] = ICEMSG_NAMEINUSE;\n\t\t\t\t\t\t\tstrcpy(net_message_buffer+1, st->webrtc.resource);\n\t\t\t\t\t\t\tFTENET_TCP_WebSocket_Splurge(st, WS_PACKETTYPE_BINARYFRAME, net_message_buffer, strlen(net_message_buffer));\n\n\t\t\t\t\t\t\t*st->webrtc.resource = 0; //don't trigger shutdown broadcasts to valid clients.\n\t\t\t\t\t\t\treturn \"room conflict\";\t//conflict! can't have two servers listening on the same url\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tnet_message_buffer[0] = ICEMSG_GREETING;\n\t\t\t\t\tnet_message_buffer[1] = 0xff;\n\t\t\t\t\tnet_message_buffer[2] = 0xff;\n\t\t\t\t\tstrcpy(net_message_buffer+3, st->webrtc.resource);\n\t\t\t\t\tFTENET_TCP_WebSocket_Splurge(st, WS_PACKETTYPE_BINARYFRAME, net_message_buffer, strlen(net_message_buffer));\n\n\t\t\t\t\t//if we have (inactive) clients connected, assign them (and let them know that they need to start handshaking)\n\t\t\t\t\tfor (o = con->tcpstreams; o; o = o->next)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (o->clienttype == TCPC_WEBRTC_CLIENT && !strcmp(st->webrtc.resource, o->webrtc.resource))\n\t\t\t\t\t\t\tFTENET_TCP_WebRTCServerAssigned(con->tcpstreams, o, st);\n\t\t\t\t\t}\n\n#ifdef SV_MASTER\n\t\t\t\t\tSVM_AddBrokerGame(st->webrtc.resource, \"\");\n#endif\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\t//a client looking for a server...\n\t\t\t\t\tif (!*idstart)\n\t\t\t\t\t{\t//that's trying to connect to us...\n#ifdef HAVE_SERVER\n\t\t\t\t\t\tif (sv.state != ss_dead)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tnet_message_buffer[0] = ICEMSG_NEWPEER;\n\t\t\t\t\t\t\tnet_message_buffer[1] = 0xff;\n\t\t\t\t\t\t\tnet_message_buffer[2] = 0xff;\n\t\t\t\t\t\t\tFTENET_TCP_WebSocket_Splurge(st, WS_PACKETTYPE_BINARYFRAME, net_message_buffer, 3);\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n#endif\n\t\t\t\t\t\t\treturn \"no local server\"; //not running a server. can't honour it.\n\t\t\t\t\t}\n\t\t\t\t\telse if (!strncmp(idstart, \"udp/\", 4))\n\t\t\t\t\t{\t//we don't use StringToAdr to avoid dns lookup stalls (denial of service attacks). should at least work for server browsers.\n#if defined(HAVE_DTLS) && defined(SV_MASTER)\n\t\t\t\t\t\tstruct dtlspeercred_s cred={NULL};\n\t\t\t\t\t\tchar adr2[256];\n\t\t\t\t\t\tif (net_enable_dtls.ival)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (!NET_StringToAdr_NoDNS(idstart+4, PORT_QWCLIENT, &st->webrtc.target))\n\t\t\t\t\t\t\t\treturn \"bad target address (names are disallowed)\";\n//\t\t\t\t\t\t\tif (NET_ClassifyAddress(&st->webrtc.target, NULL) <= ASCOPE_LAN)\n//\t\t\t\t\t\t\t\treturn false;\t//block addresses on the broker's lan. we use non-standard protocols so this should not cause problems while being useful for custom deployments.\n\n#ifdef SV_MASTER\n\t\t\t\t\t\t\tif (con->generic.owner == svm_sockets)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tif (!SVM_FixupServerAddress(&st->webrtc.target, &cred))\n\t\t\t\t\t\t\t\t\treturn \"target not registered\";\t//we don't know about this server...\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\treturn \"request sent to wrong socket\";\t//wut?\n\t\t\t\t\t\t\t//if the specified credentials, reject the connection if different.\n//#else\n\t\t\t\t\t\t\t//if we can't filter it by known servers then we can at least spam.\n\t\t\t\t\t\t\tCon_DPrintf(S_COLOR_GRAY\"%s: brokering with %s\\n\", NET_AdrToString(adr, sizeof(adr), &st->remoteaddr), NET_AdrToString(adr2, sizeof(adr2), &st->webrtc.target));\n#endif\n\n\t\t\t\t\t\t\t//use dtls to contact the server.\n\t\t\t\t\t\t\tif (st->webrtc.target.prot == NP_DGRAM)\n\t\t\t\t\t\t\t\tst->webrtc.target.prot = NP_DTLS;\n\t\t\t\t\t\t\tif (st->webrtc.target.prot == NP_DTLS)\t//don't make expensive tcp connections!\n\t\t\t\t\t\t\t\tNET_EnsureRoute(con->generic.owner, NULL, &cred, idstart+4, &st->webrtc.target, true);\n\n\t\t\t\t\t\t\t//we'll sythesise some rdp when we get an offer.\n\t\t\t\t\t\t\tnet_message_buffer[0] = ICEMSG_NEWPEER;\n\t\t\t\t\t\t\tnet_message_buffer[1] = 0xff;\n\t\t\t\t\t\t\tnet_message_buffer[2] = 0xff;\n\t\t\t\t\t\t\tFTENET_TCP_WebSocket_Splurge(st, WS_PACKETTYPE_BINARYFRAME, net_message_buffer, 3);\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n#endif\n\t\t\t\t\t\t\treturn \"sdp-via-udp is not enabled on this broker\";\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\t//find its server, if we can\n\t\t\t\t\t\tfor (o = con->tcpstreams; o; o = o->next)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (o->clienttype == TCPC_WEBRTC_HOST && !strcmp(st->webrtc.resource, o->webrtc.resource))\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t\t//and assign it to this client\n\t\t\t\t\t\tFTENET_TCP_WebRTCServerAssigned(con->tcpstreams, st, o);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t//and the connection is okay\n\n\t\t\tif (st->clienttype == TCPC_WEBSOCKETNQ)\n\t\t\t{\n\t\t\t\t//inject a connection request so that our server actually accepts them...\n\t\t\t\tnet_message.cursize = 0;\n\t\t\t\tnet_message.packing = SZ_RAWBYTES;\n\t\t\t\tnet_message.currentbit = 0;\n\t\t\t\tnet_from = st->remoteaddr;\n\t\t\t\tnet_from.connum = con->generic.connum;\n\t\t\t\tnet_from_connection = &con->generic;\n\t\t\t\tMSG_WriteLong(&net_message, LongSwap(NETFLAG_CTL | (strlen(NQ_NETCHAN_GAMENAME)+7+7)));\n\t\t\t\tMSG_WriteByte(&net_message, CCREQ_CONNECT);\n\t\t\t\tMSG_WriteString(&net_message, NQ_NETCHAN_GAMENAME);\n\t\t\t\tMSG_WriteByte(&net_message, NQ_NETCHAN_VERSION);\n\n\t\t\t\tMSG_WriteByte(&net_message, MOD_PROQUAKE); /*'mod'*/\n\t\t\t\tMSG_WriteByte(&net_message, 34); /*'mod' version*/\n\t\t\t\tMSG_WriteByte(&net_message, 0); /*flags*/\n\t\t\t\tMSG_WriteLong(&net_message, 0); /*password*/\n\n\t\t\t\tcon->generic.owner->ReadGamePacket();\n\t\t\t}\n\t\t\treturn NULL;\n\t\t}\n\t}\n\telse\n\t{\n\t\tif (!net_enable_http.ival)\n\t\t\treturn \"http disabled\";\n\t\tif (FTENET_TCP_HTTPResponse(con, st, arg, acceptsgzip))\n\t\t\treturn NULL;\n\t\telse\n\t\t\treturn \"http error\";\n\t}\n}\n#endif\n#if defined(HAVE_SSL) && (defined(HAVE_SERVER) || defined(HAVE_HTTPSV))\nstatic int QDECL TLSPromoteRead (struct vfsfile_s *file, void *buffer, int bytestoread)\n{\n\tif (bytestoread > net_message.cursize)\n\t\tbytestoread = net_message.cursize;\n\tmemcpy(buffer, net_message_buffer, bytestoread);\n\tnet_message.cursize -= bytestoread;\n\tmemmove(net_message_buffer, net_message_buffer+bytestoread, net_message.cursize);\n\treturn bytestoread;\n}\n#endif\nvoid FTENET_TCP_PrintStatus(ftenet_generic_connection_t *gcon)\n{\n\tftenet_tcp_connection_t *con = (ftenet_tcp_connection_t*)gcon;\n\tftenet_tcp_stream_t *st;\n\tchar adr[MAX_QPATH];\n\tif (!con->tcpstreams)\n\t\treturn;\n\tfor (st = con->tcpstreams; st; st = st->next)\n\t{\n\t\tNET_AdrToString(adr, sizeof(adr), &st->remoteaddr);\n\t\tswitch(st->clienttype)\n\t\t{\n\t\tcase TCPC_UNKNOWN:\t//note: this is often a pending http client that's waiting on the off-chance of having more requests to send\n\t\t\tCon_Printf(\"handshaking %s\\n\", adr);\n\t\t\tbreak;\n\t\tcase TCPC_QIZMO:\n\t\t\tCon_Printf(\"qizmo %s\\n\", adr);\n\t\t\tbreak;\n#ifdef HAVE_HTTPSV\n\t\tcase TCPC_WEBSOCKETU:\n\t\tcase TCPC_WEBSOCKETB:\n\t\tcase TCPC_WEBSOCKETNQ:\n\t\t\tCon_Printf(\"websocket %s\\n\", adr);\n\t\t\tbreak;\n\t\tcase TCPC_HTTPCLIENT:\n\t\t\tCon_Printf(\"http %s\\n\", adr);\n\t\t\tbreak;\n\t\tcase TCPC_WEBRTC_CLIENT:\n\t\t\tCon_Printf(\"webrtc client %s/%s\\n\", adr, st->webrtc.resource);\n\t\t\tbreak;\n\t\tcase TCPC_WEBRTC_HOST:\n\t\t\tCon_Printf(\"webrtc host %s/%s\\n\", adr, st->webrtc.resource);\n\t\t\tbreak;\n#endif\n\t\t}\n\t}\n}\n\nstatic qboolean FTENET_TCP_KillStream(ftenet_tcp_connection_t *con, ftenet_tcp_stream_t *st, const char *reason)\n{\t//some sort of error. kill the connection info (will be cleaned up later)\n\n#ifdef HAVE_HTTPSV\n\tif (st->clienttype == TCPC_WEBRTC_CLIENT && st->clientstream && !strcmp(st->webrtc.resource, st->webrtc.resource))\n\t{\n\t\tqbyte msg[256];\n\t\tmsg[0] = ICEMSG_PEERLOST;\n\t\tmsg[1] = 0xff;\n\t\tmsg[2] = 0xff;\n\t\tQ_strncpyz(msg+3, reason, sizeof(msg)-3);\n\n\t\tFTENET_TCP_WebSocket_Splurge(st, WS_PACKETTYPE_BINARYFRAME, msg, 3+strlen(msg+3));\n\t}\n#endif\n\n#ifdef HAVE_EPOLL\n\tif (st->socketnum != INVALID_SOCKET)\n\t\tepoll_ctl(epoll_fd, EPOLL_CTL_DEL, st->socketnum, NULL);\n#endif\n\tif (st->clientstream)\n\t\tVFS_CLOSE(st->clientstream);\n\tst->clientstream = NULL;\n\tst->socketnum = INVALID_SOCKET;\n\n\tif (st->dlfile)\n\t\tVFS_CLOSE(st->dlfile);\n\n#ifdef HAVE_SERVER\n\tif (con->generic.islisten)\n\t\tSV_DropClient_ByAddress(&st->remoteaddr);\n#endif\n\n#ifdef HAVE_HTTPSV\n\tif (st->clienttype == TCPC_WEBRTC_CLIENT)\n\t{\t//notify its server\n\t\tftenet_tcp_stream_t *o;\n\t\tfor (o = con->tcpstreams; o; o = o->next)\n\t\t{\n\t\t\tif (o->clienttype == TCPC_WEBRTC_HOST && !strcmp(o->webrtc.resource, st->webrtc.resource))\n\t\t\t{\n\t\t\t\tqbyte msg[256];\n\t\t\t\tmsg[0] = ICEMSG_PEERLOST;\n\t\t\t\tmsg[1] = (st->webrtc.clientnum>>0)&0xff;\n\t\t\t\tmsg[2] = (st->webrtc.clientnum>>8)&0xff;\n\t\t\t\tQ_strncpyz(msg+3, reason, sizeof(msg)-3);\n\n\t\t\t\tFTENET_TCP_WebSocket_Splurge(o, WS_PACKETTYPE_BINARYFRAME, msg, 3+strlen(msg+3));\n\t\t\t\tbreak;\t//should only be one.\n\t\t\t}\n\t\t}\n\t}\n\telse if (st->clienttype == TCPC_WEBRTC_HOST)\n\t{\t//we're brokering a client+server. all messages should be unicasts between a client and its host, matched by resource.\n\t\tftenet_tcp_stream_t *o;\n\t\tfor (o = con->tcpstreams; o; o = o->next)\n\t\t{\n\t\t\tif (o->clienttype == TCPC_WEBRTC_CLIENT && !strcmp(o->webrtc.resource, st->webrtc.resource))\n\t\t\t{\n\t\t\t\tqbyte msg[256];\n\t\t\t\tmsg[0] = ICEMSG_PEERLOST;\n\t\t\t\tmsg[1] = (st->webrtc.clientnum>>0)&0xff;\n\t\t\t\tmsg[2] = (st->webrtc.clientnum>>8)&0xff;\n\t\t\t\tQ_strncpyz(msg+3, reason, sizeof(msg)-3);\n\n\t\t\t\tFTENET_TCP_WebSocket_Splurge(o, WS_PACKETTYPE_BINARYFRAME, msg, 3+strlen(msg+3));\n\t\t\t}\n\t\t}\n#ifdef SV_MASTER\n\t\tSVM_RemoveBrokerGame(st->webrtc.resource);\n#endif\n\t}\n#endif\n\n\treturn false;\n}\nstatic void FTENET_TCP_Flush(ftenet_tcp_connection_t *con, ftenet_tcp_stream_t *st)\n{\n\t//write after the reads, for slightly faster pings\n\tif (st->outlen && st->clientstream)\n\t{\t/*try and flush any old outgoing data*/\n\t\tint done;\n\t\tdone = VFS_WRITE(st->clientstream, st->outbuffer, st->outlen);\n\t\tif (done > 0)\n\t\t{\n\t\t\tmemmove(st->outbuffer, st->outbuffer + done, st->outlen - done);\n\t\t\tst->outlen -= done;\n\t\t\tst->timeouttime = Sys_DoubleTime() + 30;\n\t\t}\n\t\t/*else if (done == 0)\n\t\t{\n\t\t\tCon_DPrintf (\"tcp peer %s closed connection\\n\", NET_AdrToString (adr, sizeof(adr), &st->remoteaddr));\n\t\t\tst->outlen = 0;\n\t\t}*/\n\t}\n}\n//returns a string for why it was killed. or NULL for nothing more to do.\nstatic const char *FTENET_TCP_ReadStream(ftenet_tcp_connection_t *con, ftenet_tcp_stream_t *st)\n{\n\tchar\t\tadr[MAX_ADR_SIZE];\nrestart:\t//gotos are evil. I am evil. live with it.\n\tif (!st->clientstream)\n\t\treturn NULL;\n\tif (st->inlen < sizeof(st->inbuffer)-1)\n\t{\n\t\tint ret = VFS_READ(st->clientstream, st->inbuffer+st->inlen, sizeof(st->inbuffer)-1-st->inlen);\n\t\tif (ret < 0)\n\t\t{\n\t\t\tst->outlen = 0;\t//don't flush, no point.\n\t\t\tCon_DPrintf (\"tcp peer %s closed connection\\n\", NET_AdrToString (adr, sizeof(adr), &st->remoteaddr));\n\t\t\treturn \"connection lost\";\n\t\t}\n\t\tst->inlen += ret;\n\t}\n\n\tswitch(st->clienttype)\n\t{\n\tcase TCPC_UNKNOWN:\n\t\tif (st->inlen < 6)\n\t\t\treturn NULL;\n\n\t\t//so TLS apparently uses a first byte that is always < 64. which is handy to know.\n\t\tif (con->generic.islisten && st->remoteaddr.prot == NP_STREAM && st->clientstream && !((st->inbuffer[0] >= 'a' && st->inbuffer[0] <= 'z') || (st->inbuffer[0] >= 'A' && st->inbuffer[0] <= 'Z')))\n\t\t{\n#if defined(HAVE_SSL) && (defined(HAVE_SERVER) || defined(HAVE_HTTPSV))\t//if its non-ascii, then try and upgrade the connection to tls\n\t\t\tif (net_enable_tls.ival)\n\t\t\t{\n\t\t\t\t//copy off our buffer so we can read it into the tls stream's buffer instead.\n\t\t\t\tchar tmpbuf[256];\n\t\t\t\tvfsfile_t *stream = st->clientstream;\n\t\t\t\tint (QDECL *realread) (struct vfsfile_s *file, void *buffer, int bytestoread);\n\t\t\t\tif (st->inlen > sizeof(net_message_buffer))\n\t\t\t\t\treturn \"oversize\";\t//would cause data loss...\n\t\t\t\trealread = stream->ReadBytes;\n\t\t\t\tstream->ReadBytes = TLSPromoteRead;\n\t\t\t\tmemcpy(net_message_buffer, st->inbuffer, st->inlen);\n\t\t\t\tnet_message.cursize = st->inlen;\n\t\t\t\t//wrap the stream now\n\t\t\t\tst->clientstream = FS_OpenSSL(NULL, st->clientstream, true);\n\t\t\t\tst->remoteaddr.prot = NP_TLS;\n\t\t\t\tif (st->clientstream)\n\t\t\t\t{\n\t\t\t\t\t//try and reclaim it all\n\t\t\t\t\tst->inlen = VFS_READ(st->clientstream, st->inbuffer, sizeof(st->inbuffer)-1);\n\t\t\t\t\tif (st->inlen < 0)\n\t\t\t\t\t{\t//okay, something failed...\n\t\t\t\t\t\tst->inlen = 0;\n\t\t\t\t\t\treturn \"error\";\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\t//make sure we actually read from the proper stream again\n\t\t\t\t\t\tstream->ReadBytes = realread;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (!st->clientstream || net_message.cursize)\n\t\t\t\t\treturn \"tls error\";\t//failure, or it didn't read all the data that we buffered for it (error instead of forgetting it).\n\t\t\t\tif (developer.ival)\n\t\t\t\t\tCon_Printf(\"promoted peer to tls: %s\\n\", NET_AdrToString(tmpbuf, sizeof(tmpbuf), &st->remoteaddr));\n\t\t\t\tgoto restart;\t//might be a usable packet in there that we now need to make sense of.\n\t\t\t}\n#endif\n\t\t\treturn \"no tls\";\n\t\t}\n\n\t\t//check if its a qizmo connection (or rather a general qw-over-tcp connection)\n\t\tif (st->inlen >= 6 && !strncmp(st->inbuffer, \"qizmo\\n\", 6))\n\t\t{\n\t\t\tif (\n#ifdef HAVE_SERVER\n\t\t\t\t\tnet_enable_qizmo.ival ||\n#endif\n\t\t\t\t\t!con->generic.islisten)\n\t\t\t{\n\t\t\t\tmemmove(st->inbuffer, st->inbuffer+6, st->inlen - (6));\n\t\t\t\tst->inlen -= 6;\n\t\t\t\tst->clienttype = TCPC_QIZMO;\n\t\t\t\tif (con->generic.islisten)\n\t\t\t\t{\n\t\t\t\t\t//send the qizmo handshake response.\n\t\t\t\t\tif (VFS_WRITE(st->clientstream, \"qizmo\\n\", 6) != 6)\n\t\t\t\t\t\treturn \"write error\";\t//unable to write for some reason.\n\t\t\t\t}\n\t\t\t\tgoto restart;\t//connected, next read will read the actual qizmo data\n\t\t\t}\n\t\t\treturn \"net_enable_qizmo\";\t//not enabled.\n\t\t}\n\n\t\t//check if we have some http-like protocol with a header that ends with two trailing new lines (carrage returns optional, at least here)\n\t\t//(must have a full request header, meaning double-lineendings somewhere)\n//\t\tif (con->generic.islisten)// && !strncmp(st->inbuffer, \"GET \", 4))\n\t\t{\n\t\t\t//qtv or http request header. these terminate with a blank line.\n\t\t\tint i = 0;\n\t\t\tqboolean headerscomplete = false;\n\n\t\t\tfor (; i < st->inlen; i++)\n\t\t\t{\n\t\t\t\t//we're at the start of a line, so if its a \\r\\n or a \\n then its a blank line, and the headers are complete\n\t\t\t\tif ((i+1 < st->inlen && st->inbuffer[i] == '\\r' && st->inbuffer[i+1] == '\\n') ||\n\t\t\t\t\t(i < st->inlen && st->inbuffer[i] == '\\n'))\n\t\t\t\t{\n\t\t\t\t\tif (st->inbuffer[i] == '\\n')\n\t\t\t\t\t\ti++;\n\t\t\t\t\telse\n\t\t\t\t\t\ti+=2;\n\t\t\t\t\theaderscomplete = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tfor (; i < st->inlen && st->inbuffer[i] != '\\n'; i++)\n\t\t\t\t\t;\n\t\t\t}\n\n\t\t\tif (!headerscomplete)\n\t\t\t{\n\t\t\t\t//they splurged too much data and we don't even know what they were\n\t\t\t\t//either way we're expecting a request header in our buffer that can never be completed\n\t\t\t\tif (st->inlen >= sizeof(st->inbuffer)-1)\n\t\t\t\t\treturn \"unknown\";\n\t\t\t\treturn NULL;\t//keep waiting for the rest of the headers.\n\t\t\t}\n\t\t\telse if (con->generic.islisten)\n\t\t\t{\n#if defined(SUBSERVERS) && defined(HAVE_SERVER)\n\t\t\t\t//this is a new subserver node...\n\t\t\t\tif (!Q_strncasecmp(st->inbuffer, \"NODE\", 4))\n\t\t\t\t{\n\t\t\t\t\tchar tmpbuf[256];\n#ifdef HAVE_EPOLL\n\t\t\t\t\t//the tcp connection will be handled elsewhere.\n\t\t\t\t\t//make sure we don't get tcp-handler wakeups from this connection.\n\t\t\t\t\tepoll_ctl(epoll_fd, EPOLL_CTL_DEL, st->socketnum, NULL);\n\t\t\t\t\tst->socketnum = INVALID_SOCKET;\n\t\t\t\t\tst->epoll.Polled = NULL;\n#endif\n\t\t\t\t\t//now try to pass it over\n\t\t\t\t\tMSV_NewNetworkedNode(st->clientstream, st->inbuffer, st->inbuffer+i, st->inlen-i, NET_AdrToString(tmpbuf, sizeof(tmpbuf), &st->remoteaddr));\n\t\t\t\t\tst->clientstream = NULL;\t//qtv code took it.\n\t\t\t\t\treturn \"node linked\";\n\t\t\t\t}\n\t\t\t\telse\n#endif\n#ifdef MVD_RECORDING\n\t\t\t\t//for QTV connections, we just need the method and a blank line. our qtv parser will parse the actual headers.\n\t\t\t\tif (!Q_strncasecmp(st->inbuffer, \"QTV\", 3))\n\t\t\t\t{\t//FIXME: make sure its removed from epoll and not killed prematurely\n\t\t\t\t\tint r = -2;\n\t\t\t\t\tconst char *desc;\n\t\t\t\t\tif (net_enable_qtv.ival == 2 && NET_ClassifyAddress(&st->remoteaddr, &desc) > ASCOPE_HOST)\n\t\t\t\t\t\t;\n\t\t\t\t\telse if (net_enable_qtv.ival)\n\t\t\t\t\t\tr = SV_MVD_GotQTVRequest(st->clientstream, st->inbuffer, st->inbuffer+st->inlen, &st->qtvstate);\n\t\t\t\t\ti = st->inlen;\n\t\t\t\t\tmemmove(st->inbuffer, st->inbuffer+i, st->inlen - (i));\n\t\t\t\t\tst->inlen -= i;\n\t\t\t\t\tswitch(r)\n\t\t\t\t\t{\n\t\t\t\t\tcase -2:\n\t\t\t\t\t\tVFS_PUTS(st->clientstream, \"QTVSV 1\\n\" \"PERROR: net_enable_qtv is disabled on this server\\n\\n\");\n\t\t\t\t\t\treturn \"net_enable_qtv disabled\";\n\t\t\t\t\tcase -1:\t//error\n\t\t\t\t\t\treturn \"error\";\n\t\t\t\t\tcase 0:\t\t//retry\n\t\t\t\t\t\treturn NULL;\n\t\t\t\t\tcase 1:\t\t//accepted\n#ifdef HAVE_EPOLL\n\t\t\t\t\t\t//the tcp connection will now be handled by the dedicated qtv code rather than us.\n\t\t\t\t\t\t//make sure we don't get tcp-handler wakeups from this connection.\n\t\t\t\t\t\tepoll_ctl(epoll_fd, EPOLL_CTL_DEL, st->socketnum, NULL);\n\t\t\t\t\t\tst->socketnum = INVALID_SOCKET;\n\t\t\t\t\t\tst->epoll.Polled = NULL;\n#endif\n\t\t\t\t\t\tst->clientstream = NULL;\t//qtv code took it.\n\t\t\t\t\t\treturn \"qtv client\";\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n#endif\n\t\t\t\t{\n#ifdef HAVE_HTTPSV\n\t\t\t\t\tconst char *err = FTENET_TCP_ParseHTTPRequest(con, st);\n\t\t\t\t\tif (!err)\n\t\t\t\t\t\tgoto restart;\n\t\t\t\t\treturn err;\n#else\n\t\t\t\t\tCon_DPrintf (\"Unknown TCP handshake from %s\\n\", NET_AdrToString (adr, sizeof(adr), &st->remoteaddr));\n\t\t\t\t\treturn \"unsupported handshake\";\n#endif\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\t//We're the _client_ in this block.\n#ifdef HAVE_HTTPSV\t//wut? wtf?\n\t\t\t\tif (!strncmp(st->inbuffer, \"HTTP/1.1 101\", 12))\n\t\t\t\t{\n\t\t\t\t\tchar tok[64];\n\t\t\t\t\tconst char *prot;\n\t\t\t\t\tst->inbuffer[i-1] = 0;\n\t\t\t\t\tprot = strstr(st->inbuffer, \"Sec-Websocket-Protocol: \");\n\t\t\t\t\tif (prot)\n\t\t\t\t\t\tCOM_ParseOut(prot+24, tok, sizeof(tok));\n\t\t\t\t\telse\n\t\t\t\t\t\tCOM_ParseOut(\"fteqw\", tok, sizeof(tok));\t//give it a sane default.\n\t\t\t\t\tmemmove(st->inbuffer, st->inbuffer+i, st->inlen - (i));\n\t\t\t\t\tst->inlen -= i;\n\n\t\t\t\t\tif (!strcmp(tok, \"fteqw\"))\n\t\t\t\t\t{\t//same as we send over udp, just over wss.\n\t\t\t\t\t\tst->clienttype = TCPC_WEBSOCKETB;\n\t\t\t\t\t}\n#ifdef NQPROT\n\t\t\t\t\telse if (!strcmp(tok, \"quake\"))\n\t\t\t\t\t{\t//webquake.io's servers, but it lacks the OOB stuff and the full packet headers too.\n\t\t\t\t\t\tst->clienttype = TCPC_WEBSOCKETNQ;\n\n\t\t\t\t\t\t//set up where its from...\n\t\t\t\t\t\tnet_from = st->remoteaddr;\n\t\t\t\t\t\tnet_from.connum = con->generic.connum;\n\t\t\t\t\t\tnet_from_connection = &con->generic;\n\n\t\t\t\t\t\t//inject an accept response - our client is not otherwise expecting to read anything.\n\t\t\t\t\t\tMSG_BeginWriting(&net_message, net_message.prim, NULL,0);\n\t\t\t\t\t\tMSG_WriteLong(&net_message, 0);\n\t\t\t\t\t\tMSG_WriteByte(&net_message, CCREP_ACCEPT);\n\t\t\t\t\t\tMSG_WriteLong(&net_message, 0);\t//port number. don't mess it up.\n\t\t\t\t\t\tMSG_WriteByte(&net_message, MOD_PROQUAKE);\n\t\t\t\t\t\tMSG_WriteByte(&net_message, MOD_PROQUAKE_VERSION);\n\t\t\t\t\t\tMSG_WriteByte(&net_message, 0);\t//flags\n\t\t\t\t\t\t*(int*)net_message.data = BigLong(NETFLAG_CTL|net_message.cursize);\n\n\t\t\t\t\t\t//and parse it.\n\t\t\t\t\t\tcon->generic.owner->ReadGamePacket();\n\t\t\t\t\t}\n#endif\n\t\t\t\t\telse\n\t\t\t\t\t\treturn \"server is using an unsupported protocol\";\n\n\t\t\t\t\tgoto restart;\t//connected, next read will read the actual qizmo data\n\t\t\t\t}\n#endif\n\t\t\t\treturn \"unsupported response\";\n\t\t\t}\n\t\t}\n\n\t\tFTE_UNREACHABLE;\n\t\treturn NULL;\n#ifdef HAVE_HTTPSV\n\tcase TCPC_HTTPCLIENT:\n\t\t/*try and keep it flushed*/\n\t\tFTENET_TCP_Flush(con, st);\n\t\tif (!st->outlen)\n\t\t{\n\t\t\tif (st->dlfile)\n\t\t\t\tst->outlen = VFS_READ(st->dlfile, st->outbuffer, sizeof(st->outbuffer));\n\t\t\telse\n\t\t\t\tst->outlen = 0;\n\t\t\tif (st->outlen <= 0)\n\t\t\t{\n\t\t\t\tst->outlen = 0;\n\t\t\t\tif (st->dlfile)\n\t\t\t\t\tVFS_CLOSE(st->dlfile);\n\t\t\t\tst->dlfile = NULL;\n\t\t\t\tst->clienttype = TCPC_UNKNOWN;\t//wait for the next request (could potentially be a websocket connection)\n\t\t\t\tCon_DPrintf (\"Outgoing file transfer complete\\n\");\n\t\t\t\tif (st->httpstate.connection_close)\n\t\t\t\t\treturn \"complete\";\n\t\t\t}\n\t\t\tFTENET_TCP_Flush(con, st);\n\t\t}\n\t\treturn NULL;\n#endif\n\tcase TCPC_QIZMO:\n\t\tif (st->inlen < 2)\n\t\t\treturn NULL;\n\n\t\tnet_message.cursize = BigShort(*(short*)st->inbuffer);\n\t\tif (net_message.cursize >= sizeof(net_message_buffer) )\n\t\t{\n\t\t\tCon_TPrintf (\"Warning:  Oversize packet from %s\\n\", NET_AdrToString (adr, sizeof(adr), &st->remoteaddr));\n\t\t\treturn \"oversize\";\n\t\t}\n\t\tif (net_message.cursize+2 > st->inlen)\n\t\t{\t//not enough buffered to read a packet out of it.\n\t\t\treturn NULL;\n\t\t}\n\n\t\tmemcpy(net_message_buffer, st->inbuffer+2, net_message.cursize);\n\t\tmemmove(st->inbuffer, st->inbuffer+net_message.cursize+2, st->inlen - (net_message.cursize+2));\n\t\tst->inlen -= net_message.cursize+2;\n\n\t\tnet_message.packing = SZ_RAWBYTES;\n\t\tnet_message.currentbit = 0;\n\t\tnet_from = st->remoteaddr;\n\t\tnet_from.connum = con->generic.connum;\n\t\tnet_from_connection = &con->generic;\n\n\t\tcon->generic.owner->ReadGamePacket();\n\t\tgoto restart;\n#ifdef HAVE_HTTPSV\n\tcase TCPC_WEBSOCKETU:\n\tcase TCPC_WEBSOCKETB:\n\tcase TCPC_WEBSOCKETNQ:\n\tcase TCPC_WEBRTC_HOST:\n\tcase TCPC_WEBRTC_CLIENT:\n\t\twhile (st->inlen >= 2)\n\t\t{\n\t\t\tunsigned short ctrl = ((unsigned char*)st->inbuffer)[0]<<8 | ((unsigned char*)st->inbuffer)[1];\n\t\t\tunsigned long paylen;\n\t\t\tunsigned int payoffs = 2;\n\t\t\tunsigned int mask = 0;\n//\t\t\tst->inbuffer[st->inlen]=0;\n\t\t\tif (ctrl & 0x7000)\n\t\t\t{\n\t\t\t\tCon_Printf (\"%s: reserved bits set\\n\", NET_AdrToString (adr, sizeof(adr), &st->remoteaddr));\n\t\t\t\treturn \"reserved\";\n\t\t\t}\n\t\t\tif ((ctrl & 0x7f) == 127)\n\t\t\t{\n\t\t\t\tquint64_t ullpaylen;\n\t\t\t\t//as a payload is not allowed to be encoded as too large a type, and quakeworld never used packets larger than 1450 bytes anyway, this code isn't needed (65k is the max even without this)\n\t\t\t\tif (sizeof(ullpaylen) < 8)\n\t\t\t\t{\n\t\t\t\t\tCon_Printf (\"%s: payload frame too large\\n\", NET_AdrToString (adr, sizeof(adr), &st->remoteaddr));\n\t\t\t\t\treturn \"oversize\";\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tif (payoffs + 8 > st->inlen)\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tullpaylen =\n\t\t\t\t\t\t(quint64_t)((unsigned char*)st->inbuffer)[payoffs+0]<<56u |\n\t\t\t\t\t\t(quint64_t)((unsigned char*)st->inbuffer)[payoffs+1]<<48u |\n\t\t\t\t\t\t(quint64_t)((unsigned char*)st->inbuffer)[payoffs+2]<<40u |\n\t\t\t\t\t\t(quint64_t)((unsigned char*)st->inbuffer)[payoffs+3]<<32u |\n\t\t\t\t\t\t(quint64_t)((unsigned char*)st->inbuffer)[payoffs+4]<<24u |\n\t\t\t\t\t\t(quint64_t)((unsigned char*)st->inbuffer)[payoffs+5]<<16u |\n\t\t\t\t\t\t(quint64_t)((unsigned char*)st->inbuffer)[payoffs+6]<< 8u |\n\t\t\t\t\t\t(quint64_t)((unsigned char*)st->inbuffer)[payoffs+7]<< 0u;\n\t\t\t\t\tif (ullpaylen < 0x10000)\n\t\t\t\t\t{\n\t\t\t\t\t\tCon_Printf (\"%s: payload size (%\"PRIu64\") encoded badly\\n\", NET_AdrToString (adr, sizeof(adr), &st->remoteaddr), ullpaylen);\n\t\t\t\t\t\treturn \"corrupt\";\n\t\t\t\t\t}\n\t\t\t\t\tif (ullpaylen > 0x40000)\n\t\t\t\t\t{\n\t\t\t\t\t\tCon_Printf (\"%s: payload size (%\"PRIu64\") is abusive\\n\", NET_AdrToString (adr, sizeof(adr), &st->remoteaddr), ullpaylen);\n\t\t\t\t\t\treturn \"oversize\";\n\t\t\t\t\t}\n\t\t\t\t\tpaylen = ullpaylen;\n\t\t\t\t\tpayoffs += 8;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if ((ctrl & 0x7f) == 126)\n\t\t\t{\n\t\t\t\tif (payoffs + 2 > st->inlen)\n\t\t\t\t\tbreak;\n\t\t\t\tpaylen =\n\t\t\t\t\t((unsigned char*)st->inbuffer)[payoffs+0]<<8 |\n\t\t\t\t\t((unsigned char*)st->inbuffer)[payoffs+1]<<0;\n\t\t\t\tif (paylen < 126)\n\t\t\t\t{\n\t\t\t\t\tCon_Printf (\"%s: payload size encoded badly\\n\", NET_AdrToString (adr, sizeof(adr), &st->remoteaddr));\n\t\t\t\t\treturn \"corrupt\";\n\t\t\t\t}\n\t\t\t\tpayoffs += 2;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tpaylen = ctrl & 0x7f;\n\t\t\t}\n\t\t\tif (ctrl & 0x80)\n\t\t\t{\n\t\t\t\tif (payoffs + 4 > st->inlen)\n\t\t\t\t\tbreak;\n\t\t\t\t/*this might read data that isn't set yet, but should be safe*/\n\t\t\t\t((unsigned char*)&mask)[0] = ((unsigned char*)st->inbuffer)[payoffs+0];\n\t\t\t\t((unsigned char*)&mask)[1] = ((unsigned char*)st->inbuffer)[payoffs+1];\n\t\t\t\t((unsigned char*)&mask)[2] = ((unsigned char*)st->inbuffer)[payoffs+2];\n\t\t\t\t((unsigned char*)&mask)[3] = ((unsigned char*)st->inbuffer)[payoffs+3];\n\t\t\t\tpayoffs += 4;\n\t\t\t}\n\t\t\t/*if there isn't space, try again next time around*/\n\t\t\tif (payoffs + paylen > st->inlen)\n\t\t\t{\n\t\t\t\tif (payoffs + paylen >= sizeof(st->inbuffer)-1)\n\t\t\t\t{\n\t\t\t\t\tCon_TPrintf (\"Warning:  Oversize packet from %s\\n\", NET_AdrToString (adr, sizeof(adr), &st->remoteaddr));\n\t\t\t\t\treturn \"oversize\";\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tif (mask)\n\t\t\t{\n\t\t\t\tint i;\n\t\t\t\tfor (i = 0; i < paylen; i++)\n\t\t\t\t{\n\t\t\t\t\t((unsigned char*)st->inbuffer)[i + payoffs] ^= ((unsigned char*)&mask)[i&3];\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tnet_message.cursize = 0;\n\n\t\t\tswitch((ctrl>>8) & 0xf)\n\t\t\t{\n\t\t\tcase WS_PACKETTYPE_CONTINUATION:\t/*continuation*/\n\t\t\t\tCon_Printf (\"websocket continuation frame from %s\\n\", NET_AdrToString (adr, sizeof(adr), &st->remoteaddr));\n\t\t\t\treturn \"unsupported\";\t//can't handle these.\n\t\t\tcase WS_PACKETTYPE_TEXTFRAME:\t/*text frame*/\n//\t\t\t\tCon_Printf (\"websocket text frame from %s\\n\", NET_AdrToString (adr, sizeof(adr), st->remoteaddr));\n\t\t\t\t{\n\t\t\t\t\t/*text frames are pure utf-8 chars, no dodgy encodings or anything, all pre-checked...\n\t\t\t\t\t  except we're trying to send binary data.\n\t\t\t\t\t  so we need to unmask things (char 0 is encoded as 0x100 - truncate it)\n\t\t\t\t\t*/\n\t\t\t\t\tunsigned char *in = st->inbuffer+payoffs, *out = net_message_buffer;\n\t\t\t\t\tint len = paylen;\n\t\t\t\t\twhile(len && out < net_message_buffer + sizeof(net_message_buffer))\n\t\t\t\t\t{\n\t\t\t\t\t\tif ((*in & 0xe0)==0xc0 && len > 1)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t*out = ((in[0] & 0x1f)<<6) | ((in[1] & 0x3f)<<0);\n\t\t\t\t\t\t\tin+=2;\n\t\t\t\t\t\t\tlen -= 2;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (*in & 0x80)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t*out = '?';\n\t\t\t\t\t\t\tin++;\n\t\t\t\t\t\t\tlen -= 1;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t*out = in[0];\n\t\t\t\t\t\t\tin++;\n\t\t\t\t\t\t\tlen -= 1;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tout++;\n\t\t\t\t\t}\n\t\t\t\t\tnet_message.cursize = out - net_message_buffer;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase WS_PACKETTYPE_BINARYFRAME: /*binary frame*/\n//\t\t\t\tCon_Printf (\"websocket binary frame from %s\\n\", NET_AdrToString (adr, sizeof(adr), &st->remoteaddr));\n\t\t\t\tnet_message.cursize = paylen;\n\t\t\t\tif (net_message.cursize+8 >= sizeof(net_message_buffer) )\n\t\t\t\t{\n\t\t\t\t\tCon_TPrintf (\"Warning:  Oversize packet from %s\\n\", NET_AdrToString (adr, sizeof(adr), &st->remoteaddr));\n\t\t\t\t\treturn \"oversize\";\n\t\t\t\t}\n#ifdef SUPPORT_RTC_ICE\n\t\t\t\tif (st->clienttype == TCPC_WEBRTC_CLIENT && !*st->webrtc.resource)\n\t\t\t\t{\t//this is a client that's connected directly to us via webrtc.\n\t\t\t\t\t//FIXME: we don't support dtls, so browers will bitch about our sdp.\n\t\t\t\t\tif (paylen+1 < sizeof(net_message_buffer))\n\t\t\t\t\t{\n\t\t\t\t\t\tnet_message_buffer[paylen] = 0;\n\t\t\t\t\t\tmemcpy(net_message_buffer, st->inbuffer+payoffs, paylen);\n\n\t\t\t\t\t\tif (!st->webrtc.ice)\t//if the ice state isn't established yet, do that now.\n\t\t\t\t\t\t\tst->webrtc.ice = iceapi.ICE_Create(NULL, \"test\", \"rtc://foo\", ICEM_ICE, ICEP_QWSERVER);\n\t\t\t\t\t\ticeapi.ICE_Set(st->webrtc.ice, \"sdp\", net_message_buffer);\n\n\t\t\t\t\t\tif (iceapi.ICE_Get(st->webrtc.ice, \"sdp\", net_message_buffer, sizeof(net_message_buffer)))\n\t\t\t\t\t\t\tFTENET_TCP_WebSocket_Splurge(st, WS_PACKETTYPE_BINARYFRAME, net_message_buffer, strlen(net_message_buffer));\n\t\t\t\t\t}\n\t\t\t\t\tnet_message.cursize = 0;\n\t\t\t\t}\n\t\t\t\telse\n#endif\n\t\t\t\t\t if (st->clienttype == TCPC_WEBRTC_HOST && st->inbuffer[payoffs+0] == ICEMSG_SERVERINFO)\n\t\t\t\t{\n#ifdef SV_MASTER\n\t\t\t\t\tqbyte old = st->inbuffer[payoffs+paylen];\n\t\t\t\t\tst->inbuffer[payoffs+paylen] = 0;\t//make sure its null terminated...\n\t\t\t\t\tSVM_AddBrokerGame(st->webrtc.resource, st->inbuffer+payoffs+3);\n\t\t\t\t\tst->inbuffer[payoffs+paylen] = old;\n#endif\n\t\t\t\t\tnet_message.cursize = 0;\n\t\t\t\t}\n\t\t\t\telse if ((st->clienttype == TCPC_WEBRTC_CLIENT || st->clienttype == TCPC_WEBRTC_HOST) && paylen >= 3)\n\t\t\t\t{\t//we're brokering a client+server. all messages should be unicasts between a client and its host, matched by resource.\n\t\t\t\t\tif (st->webrtc.target.type != NA_INVALID && st->clienttype==TCPC_WEBRTC_CLIENT)\n\t\t\t\t\t{\t//if the server is a udp one, we need to buffer some stuff to handle resends over a dtls connection that still has to be established.\n\t\t\t\t\t\tif (st->inbuffer[payoffs] == ICEMSG_OFFER)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tBZ_Free(st->webrtc.offer);\n\t\t\t\t\t\t\tst->webrtc.offer = BZF_Malloc(paylen-3+1);\n\t\t\t\t\t\t\tmemcpy(st->webrtc.offer, st->inbuffer+payoffs+3, paylen-3);\n\t\t\t\t\t\t\tst->webrtc.offer[paylen-3] = 0;\n\t\t\t\t\t\t\tst->webrtc.resendtime = FLT_MIN;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (st->inbuffer[payoffs] == ICEMSG_CANDIDATE && paylen > 3)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tZ_StrCatLen(&st->webrtc.candidates, st->inbuffer+payoffs+3, paylen-3);\n\t\t\t\t\t\t\tif ((st->inbuffer+payoffs+3)[paylen-3-1]!= '\\n')\n\t\t\t\t\t\t\t\tZ_StrCat(&st->webrtc.candidates, \"\\n\");\n\t\t\t\t\t\t\tst->webrtc.resendtime = FLT_MIN;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\t//forward it to the other side. much easier with tcp.\n\t\t\t\t\t\tftenet_tcp_stream_t *o;\n\t\t\t\t\t\tint clnum = (st->inbuffer[payoffs+1]<<0)|(st->inbuffer[payoffs+2]<<8);\n\t\t\t\t\t\tint type = (st->clienttype != TCPC_WEBRTC_CLIENT)?TCPC_WEBRTC_CLIENT:TCPC_WEBRTC_HOST;\n\t\t\t\t\t\tif (clnum == 0xffff)\n\t\t\t\t\t\t\tclnum = -1;\n\t\t\t\t\t\tfor (o = con->tcpstreams; o; o = o->next)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (o->clienttype == type && clnum == o->webrtc.clientnum && !strcmp(o->webrtc.resource, st->webrtc.resource))\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tst->inbuffer[payoffs+1] = (st->webrtc.clientnum>>0)&0xff;\n\t\t\t\t\t\t\t\tst->inbuffer[payoffs+2] = (st->webrtc.clientnum>>8)&0xff;\n\t\t\t\t\t\t\t\tFTENET_TCP_WebSocket_Splurge(o, WS_PACKETTYPE_BINARYFRAME, st->inbuffer+payoffs, paylen);\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (!o)\n\t\t\t\t\t\t\tCon_DPrintf(\"Unable to relay p%i to %s\\n\", st->inbuffer[payoffs+0], (st->clienttype == TCPC_WEBRTC_CLIENT)?\"server\":\"client\");\n\t\t\t\t\t}\n\t\t\t\t\tnet_message.cursize = 0;\n\t\t\t\t}\n\t\t\t\telse\n#ifdef NQPROT\n\t\t\t\t\tif (st->clienttype == TCPC_WEBSOCKETNQ && paylen>=1)\n\t\t\t\t{\t//hack in an 8-byte header\n\t\t\t\t\tpaylen-=1;\n\t\t\t\t\tswitch (st->inbuffer[payoffs++])\n\t\t\t\t\t{\t//unreliable...\n\t\t\t\t\tcase 0:\t//unreliable...\n\t\t\t\t\t\tmemcpy(net_message_buffer+4, st->inbuffer+payoffs, paylen);\n\t\t\t\t\t\tnet_message.cursize=paylen+4;\n\t\t\t\t\t\t((int*)net_message_buffer)[0] = BigLong(NETFLAG_UNRELIABLE | net_message.cursize);\n\t\t\t\t\t\t((int*)net_message_buffer)[1] = BigLong(++st->fakesequence);\t//webquake is buggy and writes 0s. but this is reliable anyway so fake it all here.\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 1:\t//reliable\n\t\t\t\t\t\tmemcpy(net_message_buffer+4, st->inbuffer+payoffs, paylen);\n\t\t\t\t\t\tnet_message.cursize=paylen+4;\n\t\t\t\t\t\t((int*)net_message_buffer)[0] = BigLong(NETFLAG_DATA|NETFLAG_EOM | net_message.cursize);\n\t\t\t\t\t\t//the packet included its own sequence.\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 2:\t//ack - we don't send 'reliables' because websockets are unconditionally reliable anyway, so we won't receive an ack.\n\t\t\t\t\t\tmemcpy(net_message_buffer+4, st->inbuffer+payoffs, paylen);\n\t\t\t\t\t\tnet_message.cursize=paylen+4;\n\t\t\t\t\t\t((int*)net_message_buffer)[0] = BigLong(NETFLAG_ACK | net_message.cursize);\n\t\t\t\t\t\t//the packet included its own sequence.\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\treturn \"TCPC_WEBSOCKETNQ: unknown packet type\";\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n#endif\n\t\t\t\t\tmemcpy(net_message_buffer, st->inbuffer+payoffs, paylen);\n\t\t\t\tbreak;\n\t\t\tcase WS_PACKETTYPE_CLOSE:\t/*connection close*/\n\t\t\t\tCon_Printf (\"websocket closure %s\\n\", NET_AdrToString (adr, sizeof(adr), &st->remoteaddr));\n\t\t\t\treturn \"drop\";\t//they're about to drop anyway.\n\t\t\tcase WS_PACKETTYPE_PING:\t/*ping*/\n//\t\t\t\tCon_Printf (\"websocket ping from %s\\n\", NET_AdrToString (adr, sizeof(adr), &st->remoteaddr));\n\t\t\t\tif (FTENET_TCP_WebSocket_Splurge(st, WS_PACKETTYPE_PONG, st->inbuffer+payoffs, paylen) != NETERR_SENT)\n\t\t\t\t\treturn \"write error\";\n\t\t\t\tbreak;\n\t\t\tcase WS_PACKETTYPE_PONG: /*pong*/\n\t\t\t\tst->timeouttime = Sys_DoubleTime() + 30;\n\t\t\t\tst->pinging = false;\n//\t\t\t\tCon_Printf (\"websocket pong from %s\\n\", NET_AdrToString (adr, sizeof(adr), &st->remoteaddr));\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tCon_Printf (\"Unsupported websocket opcode (%i) from %s\\n\", (ctrl>>8) & 0xf, NET_AdrToString (adr, sizeof(adr), &st->remoteaddr));\n\t\t\t\treturn \"unsupported\";\n\t\t\t}\n\n\t\t\tmemmove(st->inbuffer, st->inbuffer+payoffs + paylen, st->inlen - (payoffs + paylen));\n\t\t\tst->inlen -= payoffs + paylen;\n\n\t\t\tif (net_message.cursize)\n\t\t\t{\n\t\t\t\tnet_message.packing = SZ_RAWBYTES;\n\t\t\t\tnet_message.currentbit = 0;\n\t\t\t\tnet_from = st->remoteaddr;\n\t\t\t\tnet_from.connum = con->generic.connum;\n\t\t\t\tnet_from_connection = &con->generic;\n\t\t\t\tcon->generic.owner->ReadGamePacket();\n\t\t\t\tgoto restart;\n\t\t\t}\n\t\t}\n\t\treturn NULL;\n#endif\n\t}\n\treturn NULL;\n}\n\n#ifdef SV_MASTER\nstatic qboolean FTENET_TCP_GetPacket(ftenet_generic_connection_t *gcon);\nvoid FTENET_TCP_ICEResponse(ftenet_connections_t *col, int type, const char *cid, const char *sdp)\n{\t//Handle a ice_answer/ice_scand response from a brokered udp-only server (forwards the response to the original ws(s) sender).\n\tunsigned int id, connum;\n\tftenet_tcp_connection_t *con;\n\tftenet_tcp_stream_t *o;\n\n\tcid = COM_Parse(cid);\n\tid = strtoul(com_token, NULL, 16);\n\tconnum = ((id>>16)&0xffff)-1;\n\tid=(short)(id&0xffff);\n\tif (connum >= countof(col->conn) || !col->conn[connum] || col->conn[connum]->GetPacket != FTENET_TCP_GetPacket)\n\t\treturn;\n\tcon = (ftenet_tcp_connection_t*)col->conn[connum];\n\n\tfor (o = con->tcpstreams; o; o = o->next)\n\t{\n\t\tif (o->clienttype != TCPC_WEBRTC_CLIENT || o->webrtc.target.type==NA_INVALID || o->webrtc.clientnum != id)\n\t\t\tcontinue;\n\t\tif (NET_CompareAdr(&net_from, &o->webrtc.target))\n\t\t{\n\t\t\tchar msg[1400];\n\t\t\tZ_Free(o->webrtc.offer);\t//we got the answer. can stop trying to spam it now.\n\t\t\to->webrtc.offer = NULL;\n\n\t\t\tif (type == ICEMSG_CANDIDATE)\n\t\t\t{\n\t\t\t\tconst char *nc = o->webrtc.candidates;\n\t\t\t\tchar seq, ack;\n\t\t\t\tcid = COM_Parse(cid);\n\t\t\t\tseq = atoi(com_token);\n\t\t\t\tcid = COM_Parse(cid);\n\t\t\t\tack = atoi(com_token);\n\n\t\t\t\twhile (nc && o->webrtc.outcand < ack)\n\t\t\t\t{\t//server saw one of our candidate lines. stop respamming that one.\n\t\t\t\t\to->webrtc.outcand++;\n\n\t\t\t\t\tfor (; *nc && *nc != '\\n'; nc++)\n\t\t\t\t\t\t;\n\t\t\t\t\tif (*nc != '\\n')\n\t\t\t\t\t\tbreak;\t//getting exploited...\n\t\t\t\t\tnc++;\t//skip the nl\n\t\t\t\t}\n\t\t\t\tif (!nc || !*nc)\n\t\t\t\t{\n\t\t\t\t\tZ_Free(o->webrtc.candidates);\t//all acked...\n\t\t\t\t\to->webrtc.candidates = NULL;\n\t\t\t\t}\n\t\t\t\telse if (nc != o->webrtc.candidates)\n\t\t\t\t\tmemmove(o->webrtc.candidates, nc, strlen(nc)+1);\n\t\t\t\to->webrtc.outcand = ack; //in case the server just wanted to drop some.\n\n\t\t\t\twhile(*sdp)\n\t\t\t\t{\n\t\t\t\t\tfor (nc = sdp; *nc && *nc != '\\n'; nc++)\n\t\t\t\t\t\t;\n\t\t\t\t\tif (*nc != '\\n')\n\t\t\t\t\t\tbreak;\t//getting exploited...\n\t\t\t\t\tnc++;\t//skip the nl.\n\t\t\t\t\tif (seq++ < o->webrtc.candack)\n\t\t\t\t\t\t;\t//already saw this line\n\t\t\t\t\telse\n\t\t\t\t\t{\t//new. yay reliables...\n\t\t\t\t\t\tmsg[0] = type;\n\t\t\t\t\t\tmsg[1] = 0xff;\n\t\t\t\t\t\tmsg[2] = 0xff;\n\t\t\t\t\t\tmemcpy(msg+3, sdp, nc-sdp);\n\t\t\t\t\t\tFTENET_TCP_WebSocket_Splurge(o, WS_PACKETTYPE_BINARYFRAME, msg, 3+nc-sdp);\n\t\t\t\t\t\to->webrtc.candack=seq;\n\t\t\t\t\t}\n\n\t\t\t\t\tsdp = nc;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tmsg[0] = type;\n\t\t\t\tmsg[1] = 0xff;\n\t\t\t\tmsg[2] = 0xff;\n\t\t\t\tQ_strncpyz(msg+3, sdp, sizeof(msg));\n\t\t\t\tFTENET_TCP_WebSocket_Splurge(o, WS_PACKETTYPE_BINARYFRAME, msg, 3+strlen(msg+3));\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t}\n}\n#endif\n\n#ifdef HAVE_EPOLL\nstatic void FTENET_TCP_Polled(epollctx_t *ctx, unsigned int events)\n{\n\tftenet_tcp_stream_t *st = NULL;\n\tconst char *err;\n\tst = (ftenet_tcp_stream_t *)((qbyte*)ctx - ((qbyte*)&st->epoll-(qbyte*)st));\n\n\terr = FTENET_TCP_ReadStream(st->con, st);\n\tif (err)\n\t\tFTENET_TCP_KillStream(st->con, st, err);\n\telse\n\t\tFTENET_TCP_Flush(st->con, st);\n}\n#endif\n\nstatic qboolean FTENET_TCP_GetPacket(ftenet_generic_connection_t *gcon)\n{\n\tftenet_tcp_connection_t *con = (ftenet_tcp_connection_t*)gcon;\n//\tint ret;\n\tchar\t\tadr[MAX_ADR_SIZE];\n\tstruct sockaddr_qstorage\tfrom;\n\tint fromlen;\n\n\tfloat timeval = Sys_DoubleTime();\n\tftenet_tcp_stream_t *st;\n\tst = con->tcpstreams;\n\n\twhile (con->tcpstreams && con->tcpstreams->clientstream == NULL)\n\t{\t//remove initial stale ones\n\t\tst = con->tcpstreams;\n\t\tcon->tcpstreams = con->tcpstreams->next;\n#ifdef HAVE_EPOLL\n\t\tst->epoll.Polled = NULL;\t//to cause segfaults if we failed somehow.\n#endif\n\t\tBZ_Free(st);\n\t\tcon->active--;\n\t}\n\n\tfor (st = con->tcpstreams; st; st = st->next)\n\t{//client receiving only via tcp\n\n\t\twhile (st->next && st->next->clientstream == NULL)\n\t\t{\t//remove following stale ones\n\t\t\tftenet_tcp_stream_t *temp;\n\t\t\ttemp = st->next;\n\t\t\tst->next = st->next->next;\n#ifdef HAVE_EPOLL\n\t\t\ttemp->epoll.Polled = NULL;\t//to cause segfaults if we failed somehow.\n#endif\n\t\t\tBZ_Free(temp);\n\t\t\tcon->active--;\n\t\t}\n\n//due to the above checks about invalid sockets, the socket is always open for st below.\n\t\tif (st->timeouttime < timeval)\n\t\t{\n#ifdef HAVE_HTTPSV\n\t\t\tif (!st->pinging && (st->clienttype==TCPC_WEBRTC_CLIENT||st->clienttype==TCPC_WEBRTC_HOST) && *st->webrtc.resource)\n\t\t\t{\t//ping broker clients. there usually shouldn't be any data flow to keep it active otherwise.\n\t\t\t\tst->timeouttime = timeval + 30;\n\t\t\t\tst->pinging = true;\t//cleared on ack.\n\n\t\t\t\tFTENET_TCP_WebSocket_Splurge(st, WS_PACKETTYPE_PING, \"ping\", 4);\n\t\t\t}\n\t\t\telse\n#endif\n\t\t\t{\n\t\t\t\tCon_DPrintf (\"tcp peer %s timed out\\n\", NET_AdrToString (adr, sizeof(adr), &st->remoteaddr));\n\t\t\t\tFTENET_TCP_KillStream(con, st, \"timeout\");\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\n\t\tfor(;st->clientstream;)\n\t\t{\n\t\t\tconst char *err = FTENET_TCP_ReadStream(con, st);\n\t\t\tif (err)\n\t\t\t\tFTENET_TCP_KillStream(con, st, err);\n\t\t\tbreak;\n\t\t}\n\t\tFTENET_TCP_Flush(con, st);\n\n#ifdef HAVE_HTTPSV\n\t\tif (st->clienttype==TCPC_WEBRTC_CLIENT && st->webrtc.target.type!=NA_INVALID && st->webrtc.resendtime < timeval && st->clientstream)\n\t\t{\n\t\t\tif (st->webrtc.offer)\n\t\t\t{\n\t\t\t\tstatic struct netprim_s prim={0};\n\t\t\t\tsizebuf_t msg;\n\t\t\t\tchar adr[256];\n\t\t\t\tnetproto_t o = st->remoteaddr.prot;\n\n\t\t\t\tif (st->webrtc.sends > 5)\n\t\t\t\t{\n\t\t\t\t\tif (st->webrtc.sends > 6)\n\t\t\t\t\t{\n\t\t\t\t\t\tFTENET_TCP_KillStream(con, st, \"Too many resends\");\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tst->webrtc.resendtime = timeval + 3;\n\t\t\t\t\tst->webrtc.sends++;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tst->remoteaddr.prot = 0;\t//no prefixes!\n\t\t\t\tNET_BaseAdrToString(adr, sizeof(adr), &st->remoteaddr);\t//let the server know who's trying to connect to them. for ip bans.\n\t\t\t\tst->remoteaddr.prot = o;\n\n\t\t\t\tMSG_BeginWriting(&msg, prim, net_message_buffer, sizeof(net_message_buffer));\n\t\t\t\tMSG_WriteLong(&msg, ~0);\n\t\t\t\tMSG_WriteString(&msg, va(\"ice_offer %s %x:%x\", adr, (con->generic.connum<<16)|(quint16_t)st->webrtc.clientnum, st->webrtc.clientseq));\n\t\t\t\tMSG_WriteString(&msg, st->webrtc.offer);\n\t\t\t\tsafeswitch (NET_SendPacket(con->generic.owner, msg.cursize, msg.data, &st->webrtc.target))\n\t\t\t\t{\n\t\t\t\tcase NETERR_CLOGGED:\t//dtls still connecting, or just unable to send...\n\t\t\t\t\tbreak;\t//don't update resend timer..\n\t\t\t\tcase NETERR_SENT:\n\t\t\t\t\tst->webrtc.sends++;\n\t\t\t\t\tst->webrtc.resendtime = timeval + 1;\n\t\t\t\t\tbreak;\n\t\t\t\tcase NETERR_NOROUTE:\n\t\t\t\tcase NETERR_DISCONNECTED:\n\t\t\t\tcase NETERR_MTU:\n\t\t\t\tsafedefault:\n\t\t\t\t\tFTENET_TCP_KillStream(con, st, \"target not reachable\");\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (st->webrtc.candidates)\n\t\t\t{\t//hopefully the server will have a proper public address and so won't need this... but just in case...\n\t\t\t\tstatic struct netprim_s prim={0};\n\t\t\t\tsizebuf_t msg;\n\n\t\t\t\tMSG_BeginWriting(&msg, prim, net_message_buffer, sizeof(net_message_buffer));\n\t\t\t\tMSG_WriteLong(&msg, ~0);\n\t\t\t\tMSG_WriteString(&msg, va(\"ice_ccand %x:%x %i %i\", (con->generic.connum<<16)|(quint16_t)st->webrtc.clientnum, st->webrtc.clientseq, st->webrtc.outcand, st->webrtc.candack));\n\t\t\t\tMSG_WriteString(&msg, st->webrtc.candidates);\n\t\t\t\tsafeswitch (NET_SendPacket(con->generic.owner, msg.cursize, msg.data, &st->webrtc.target))\n\t\t\t\t{\n\t\t\t\tcase NETERR_CLOGGED:\t//unable to send...\n\t\t\t\t\tbreak;\t//don't update resend timer so we don't lose too much time\n\t\t\t\tcase NETERR_SENT:\n\t\t\t\t\tst->webrtc.resendtime = timeval + 1;\n\t\t\t\t\tst->webrtc.sends++;\n\t\t\t\t\tbreak;\n\t\t\t\tcase NETERR_NOROUTE:\n\t\t\t\tcase NETERR_DISCONNECTED:\n\t\t\t\tcase NETERR_MTU:\n\t\t\t\tsafedefault:\n\t\t\t\t\tFTENET_TCP_KillStream(con, st, \"target not reachable\");\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t\tst->webrtc.resendtime = timeval + 30;\n\t\t}\n#endif\n\t}\n\n\tif (con->generic.thesocket != INVALID_SOCKET && con->active < 256)\n\t{\n\t\tint newsock;\n\t\tfromlen = sizeof(from);\n\t\tnewsock = accept(con->generic.thesocket, (struct sockaddr*)&from, &fromlen);\n\t\tif (newsock != INVALID_SOCKET)\n\t\t{\n\t\t\tchar tmpbuf[256];\n\t\t\tint _true = true;\n\t\t\tioctlsocket(newsock, FIONBIO, (u_long *)&_true);\n\t\t\tsetsockopt(newsock, IPPROTO_TCP, TCP_NODELAY, (char *)&_true, sizeof(_true));\n\n\t\t\tcon->active++;\n\t\t\tst = Z_Malloc(sizeof(*con->tcpstreams));\n\t\t\t/*grab the net address*/\n\t\t\tSockadrToNetadr(&from, fromlen, &st->remoteaddr);\n\t\t\tif (developer.ival)\n\t\t\t\tCon_Printf(\"new TCP connection from %s\\n\", NET_AdrToString(tmpbuf, sizeof(tmpbuf), &st->remoteaddr));\n\t\t\tst->clienttype = TCPC_UNKNOWN;\n\t\t\tst->next = con->tcpstreams;\n\t\t\tcon->tcpstreams = st;\n\t\t\tst->socketnum = newsock;\n\t\t\tst->clientstream = FS_WrapTCPSocket(newsock, false, NET_AdrToString(tmpbuf, sizeof(tmpbuf), &st->remoteaddr));\n\t\t\tst->inlen = 0;\n\n#ifdef HAVE_EPOLL\n\t\t\t{\n\t\t\t\tstruct epoll_event event = {EPOLLIN|EPOLLOUT|EPOLLET, {&st->epoll}};\n\t\t\t\tst->con = con;\n\t\t\t\tst->epoll.Polled = FTENET_TCP_Polled;\n\t\t\t\tepoll_ctl(epoll_fd, EPOLL_CTL_ADD, newsock, &event);\n\t\t\t}\n#endif\n\n#ifdef HAVE_SSL\n\t\t\tif (con->tls && st->clientstream)\t//if we're meant to be using tls, wrap the stream in a tls connection\n\t\t\t{\n\t\t\t\tst->clientstream = FS_OpenSSL(NULL, st->clientstream, true);\n\t\t\t\t/*sockadr doesn't contain transport info, so fix that up here*/\n\t\t\t\tst->remoteaddr.prot = NP_TLS;\n\t\t\t}\n\t\t\telse\n#endif\n\t\t\t{\n\t\t\t\t/*sockadr doesn't contain transport info, so fix that up here*/\n\t\t\t\tst->remoteaddr.prot = NP_STREAM;\n\t\t\t}\n\n\t\t\tst->timeouttime = timeval + 30;\n\t\t}\n\t}\n\treturn false;\n}\n\nneterr_t FTENET_TCP_SendPacket(ftenet_generic_connection_t *gcon, int length, const void *data, netadr_t *to)\n{\n\tftenet_tcp_connection_t *con = (ftenet_tcp_connection_t*)gcon;\n\tftenet_tcp_stream_t *st;\n\n\tfor (st = con->tcpstreams; st; st = st->next)\n\t{\n\t\tif (st->clientstream == NULL)\n\t\t\tcontinue;\n\n\t\tif (NET_CompareAdr(to, &st->remoteaddr))\n\t\t{\n\t\t\tif (!st->outlen)\n\t\t\t{\n\t\t\t\tswitch(st->clienttype)\n\t\t\t\t{\n\t\t\t\tcase TCPC_QIZMO:\n\t\t\t\t\t{\n\t\t\t\t\t\tunsigned short slen = BigShort((unsigned short)length);\n\t\t\t\t\t\tif (length > 0xffff)\n\t\t\t\t\t\t\treturn NETERR_MTU;\n\t\t\t\t\t\tif (st->outlen + sizeof(slen) + length > sizeof(st->outbuffer))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (length+sizeof(slen) > sizeof(st->outbuffer))\n\t\t\t\t\t\t\t\treturn NETERR_MTU;\n\t\t\t\t\t\t\tCon_DPrintf(\"FTENET_TCP_SendPacket: outgoing overflow\\n\");\n\t\t\t\t\t\t\treturn NETERR_CLOGGED;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tmemcpy(st->outbuffer + st->outlen, &slen, sizeof(slen));\n\t\t\t\t\t\t\tmemcpy(st->outbuffer + st->outlen + sizeof(slen), data, length);\n\t\t\t\t\t\t\tst->outlen += sizeof(slen) + length;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n#ifdef HAVE_HTTPSV\n\t\t\t\tcase TCPC_WEBSOCKETNQ:\n\t\t\t\t\tif (length < 8 || ((char*)data)[0] & 0x80)\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t{\n\t\t\t\t\t\tunsigned int flags = (((const qbyte*)data)[0]<<24)|(((const qbyte*)data)[1]<<16)|(((const qbyte*)data)[2]<<8)|(((const qbyte*)data)[3]<<0);\n\t\t\t\t\t\tif (flags & NETFLAG_ACK)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tlength -= 3;\n\t\t\t\t\t\t\tdata=(const char*)data + 3;\n\t\t\t\t\t\t\t*(char*)data = 2;\t//for compat with webquake, we add an extra byte at the start. 1 for reliable, 2 for unreliable.\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (flags & NETFLAG_UNRELIABLE)\n\t\t\t\t\t\t{\t//unreliable, etc...\n\t\t\t\t\t\t\tif (length==9)\n\t\t\t\t\t\t\t\treturn NETERR_SENT;\t//don't bother with the nop.\n\t\t\t\t\t\t\tlength-=3;\n\t\t\t\t\t\t\tdata=(const char*)data + 3;\n\t\t\t\t\t\t\t*(char*)data = 0;\t//for compat with webquake, we add an extra byte at the start. 1 for reliable, 2 for unreliable.\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (flags & NETFLAG_DATA)\n\t\t\t\t\t\t{\t//reliable\n\t\t\t\t\t\t\tif (!(flags & NETFLAG_EOM))\n\t\t\t\t\t\t\t\treturn NETERR_DISCONNECTED;\t//can't handle fragmentation.\n\t\t\t\t\t\t\tlength-=3;\n\t\t\t\t\t\t\tdata=(const char*)data + 3;\n\t\t\t\t\t\t\t*(char*)data = 1;\t//for compat with webquake, we add an extra byte at the start. 1 for reliable, 2 for unreliable.\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\treturn NETERR_SENT;\t//just drop it, without causing errors (eg if they try rconing)\n\t\t\t\t\t}\n\t\t\t\t\t//fallthrough\n\t\t\t\tcase TCPC_WEBSOCKETU:\n\t\t\t\tcase TCPC_WEBSOCKETB:\n\t\t\t\t\t{\n\t\t\t\t\t\tneterr_t e = FTENET_TCP_WebSocket_Splurge(st, (st->clienttype==TCPC_WEBSOCKETU)?WS_PACKETTYPE_TEXTFRAME:WS_PACKETTYPE_BINARYFRAME, data, length);\n\t\t\t\t\t\tif (e != NETERR_SENT)\n\t\t\t\t\t\t\treturn e;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n#endif\n\t\t\t\tcase TCPC_UNKNOWN:\n\t\t\t\t\treturn NETERR_CLOGGED;\t//still waiting for the other side to ack out connection.\n\t\t\t\tdefault:\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (st->outlen)\n\t\t\t{\t/*try and flush the old data*/\n\t\t\t\tint done;\n\t\t\t\tdone = VFS_WRITE(st->clientstream, st->outbuffer, st->outlen);\n\t\t\t\tif (done > 0)\n\t\t\t\t{\n\t\t\t\t\tmemmove(st->outbuffer, st->outbuffer + done, st->outlen - done);\n\t\t\t\t\tst->outlen -= done;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tst->timeouttime = Sys_DoubleTime() + 20;\n\n\t\t\treturn NETERR_SENT;\n\t\t}\n\t}\n\treturn NETERR_NOROUTE;\n}\n\nstatic int FTENET_TCP_GetLocalAddresses(struct ftenet_generic_connection_s *gcon, unsigned int *adrflags, netadr_t *addresses, const char **adrparams, int maxaddresses)\n{\n\tftenet_tcp_connection_t *con = (ftenet_tcp_connection_t*)gcon;\n\tnetproto_t prot = con->tls?NP_TLS:NP_STREAM;\n\tint i, r = FTENET_Generic_GetLocalAddresses(gcon, adrflags, addresses, adrparams, maxaddresses);\n\tfor (i = 0; i < r; i++)\n\t{\n\t\taddresses[i].prot = prot;\n\t}\n\treturn r;\n}\n\nstatic qboolean FTENET_TCP_ChangeLocalAddress(struct ftenet_generic_connection_s *con, const char *addressstring, netadr_t *adr)\n{\n\t//if we're a server, we want to try switching listening tcp port without shutting down all other connections.\n\t//yes, this might mean we leave a connection active on the old port, but oh well.\n\tint addrsize, addrsize2;\n\tint family;\n\tstruct sockaddr_qstorage qs;\n\tstruct sockaddr_qstorage cur;\n\tnetadr_t n;\n\tSOCKET newsocket = INVALID_SOCKET;\n\tunsigned long _true = true;\n\tint sysprot;\n\n\taddrsize = NetadrToSockadr(adr, &qs);\n\tfamily = ((struct sockaddr*)&qs)->sa_family;\n\n\tswitch(adr->type)\n\t{\n#if defined(HAVE_IPV4) || defined(HAVE_IPV6)\n\tcase NA_IP:\n\tcase NA_IPV6:\n\t\tsysprot = IPPROTO_TCP;\n\t\tbreak;\n#endif\n#ifdef HAVE_IPX\n\tcase NA_IPX:\n\t\tsysprot = NSPROTO_IPX;\n\t\tbreak;\n#endif\n\tdefault:\n\t\tsysprot = 0;\n\t\tbreak;\n\t}\n\n\tif (con->thesocket != INVALID_SOCKET)\n\t{\n\t\taddrsize2 = sizeof(cur);\n\t\tgetsockname(con->thesocket, (struct sockaddr *)&cur, &addrsize2);\n\n\t\tif (addrsize == addrsize2)\n\t\t{\n\t\t\tSockadrToNetadr(&cur, addrsize2, &n);\n\t\t\tif (NET_CompareAdr(adr, &n))\t//the address+port we're trying is already current, apparently.\n\t\t\t\treturn true;\n\t\t}\n\n\t\tclosesocket(con->thesocket);\n\t\tcon->thesocket = INVALID_SOCKET;\n\t}\n\n#if defined(HAVE_IPV6) && defined(IPV6_V6ONLY)\n\tif (newsocket == INVALID_SOCKET)\n\tif (family == AF_INET && net_hybriddualstack.ival && !((struct sockaddr_in*)&qs)->sin_addr.s_addr)\n\t{\t//hybrid sockets pathway takes over when INADDR_ANY\n\t\tunsigned long _false = false;\n\t\tif ((newsocket = socket (AF_INET6, SOCK_STREAM, sysprot)) != INVALID_SOCKET)\n\t\t{\n#ifdef __linux__\t//note: windows blindly allows dupes, whereas linux prevents exact matches\n\t\t\tsetsockopt(newsocket, SOL_SOCKET, SO_REUSEADDR, (const char *)&_true, sizeof(_true));\t//try to avoid 'address in use' problems when killing+restarting.\n#elif defined(_WIN32)\n\t\t\tsetsockopt(newsocket, SOL_SOCKET, SO_REUSEADDR, (const char *)&_true, sizeof(_true));\t//try to avoid 'address in use' problems when killing+restarting.\n#endif\n\n\t\t\tif (0 == setsockopt(newsocket, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&_false, sizeof(_false)))\n\t\t\t{\n\t\t\t\tmemset(&n, 0, sizeof(n));\n\t\t\t\tn.type = NA_IPV6;\n\t\t\t\tn.port = adr->port;\n\t\t\t\tn.scopeid = adr->scopeid;\n\t\t\t\taddrsize2 = NetadrToSockadr(&n, &cur);\n\n\t\t\t\tcon->prot = NP_STREAM;\n\t\t\t\tif ((bind(newsocket, (struct sockaddr *)&cur, addrsize2) != INVALID_SOCKET) &&\n\t\t\t\t\t(listen(newsocket, 2) != INVALID_SOCKET) &&\n\t\t\t\t\tioctlsocket (newsocket, FIONBIO, &_true) != -1)\n\t\t\t\t{\n\t\t\t\t\tcon->addrtype[0] = NA_IP;\n\t\t\t\t\tcon->addrtype[1] = NA_IPV6;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tclosesocket(newsocket);\n\t\t\t\t\tnewsocket = INVALID_SOCKET;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tclosesocket(newsocket);\n\t\t\t\tnewsocket = INVALID_SOCKET;\n\t\t\t}\n\t\t}\n\t}\n#endif\n\n\tif (newsocket == INVALID_SOCKET)\n\t{\n\t\tif ((newsocket = socket (family, SOCK_STREAM, sysprot)) != INVALID_SOCKET)\n\t\t{\n#ifdef UNIXSOCKETS\n\t\t\tif (family == AF_UNIX)\n\t\t\t{\n\t\t\t\tstruct sockaddr_un *un = (struct sockaddr_un *)&qs;\n\t\t\t\tstruct stat s;\n\t\t\t\tif (*un->sun_path)\n\t\t\t\t{\t//non-abstract sockets don't clean up the filesystem when the socket is closed\n\t\t\t\t\t//and we can't re-bind to it while it still exists.\n\t\t\t\t\t//so standard practise is to delete it before the bind.\n\t\t\t\t\t//we do want to make sure the file is actually a socket before we remove it (so people can't abuse stuffcmds)\n\t\t\t\t\tif (stat(un->sun_path, &s)!=-1)\n\t\t\t\t\t{\n\t\t\t\t\t\tif ((s.st_mode & S_IFMT) == S_IFSOCK)\n\t\t\t\t\t\t\tunlink(un->sun_path);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n#endif\n\n\t\t\tif ((bind(newsocket, (struct sockaddr *)&qs, addrsize) != INVALID_SOCKET) &&\n\t\t\t\t(listen(newsocket, 2) != INVALID_SOCKET) &&\n\t\t\t\tioctlsocket (newsocket, FIONBIO, &_true) != -1)\n\t\t\t\t;\n\t\t\telse\n\t\t\t{\n\t\t\t\tclosesocket(newsocket);\n\t\t\t\tnewsocket = INVALID_SOCKET;\n\t\t\t}\n\t\t}\n\t}\n\n\n\tif (newsocket != INVALID_SOCKET)\n\t{\n#ifdef UNIXSOCKETS\n\t\tif (family != NA_UNIX)\n#endif\n\t\t\tsetsockopt(newsocket, IPPROTO_TCP, TCP_NODELAY, (char *)&_true, sizeof(_true));\n\n\t\tcon->thesocket = newsocket;\n\n\n#ifdef HAVE_EPOLL\n\t\t{\n\t\t\tstruct epoll_event event = {EPOLLIN|EPOLLET, {NULL}};//&newcon->generic.epoll}};\n\t\t\t//newcon->generic.epoll.Polled = FTENET_TCP_AcceptPolled;\n\t\t\tepoll_ctl(epoll_fd, EPOLL_CTL_ADD, newsocket, &event);\n\t\t}\n#endif\n\t\treturn true;\n\t}\n\treturn false;\n}\nstatic void FTENET_TCP_Close(ftenet_generic_connection_t *gcon)\n{\n\tftenet_tcp_connection_t *con = (ftenet_tcp_connection_t*)gcon;\n\tftenet_tcp_stream_t *st;\n\n\tst = con->tcpstreams;\n\twhile (con->tcpstreams)\n\t{\n\t\tst = con->tcpstreams;\n\t\tcon->tcpstreams = st->next;\n\n\t\tif (st->clientstream != NULL)\n\t\t\tVFS_CLOSE(st->clientstream);\n\n\t\tBZ_Free(st);\n\t}\n\n\tFTENET_Datagram_Close(gcon);\n}\n\n#if defined(HAVE_PACKET) && !defined(HAVE_EPOLL)\nstatic int FTENET_TCP_SetFDSets(ftenet_generic_connection_t *gcon, fd_set *readfdset, fd_set *writefdset)\n{\n\tint maxfd = -1;\n\tftenet_tcp_connection_t *con = (ftenet_tcp_connection_t*)gcon;\n\tftenet_tcp_stream_t *st;\n\n\tfor (st = con->tcpstreams; st; st = st->next)\n\t{\n#ifdef SUPPORT_RTC_ICE\n\t\tif (st->webrtc.ice)\n\t\t{\n\t\t\twhile(iceapi.ICE_GetLCandidateSDP(st->webrtc.ice, net_message_buffer, sizeof(net_message_buffer)))\n\t\t\t{\n\t\t\t\tFTENET_TCP_WebSocket_Splurge(st, WS_PACKETTYPE_BINARYFRAME, net_message_buffer, strlen(net_message_buffer));\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n#endif\n\t\tif (st->clientstream == NULL || st->socketnum == INVALID_SOCKET)\n\t\t\tcontinue;\n#ifdef HAVE_HTTPSV\n\t\tif (st->clienttype == TCPC_HTTPCLIENT && st->outlen)\n\t\t\tFD_SET(st->socketnum, writefdset); // network socket\n#endif\n\t\tFD_SET(st->socketnum, readfdset); // network socket\n\t\tif (maxfd < st->socketnum)\n\t\t\tmaxfd = st->socketnum;\n\t}\n\tif (con->generic.thesocket != INVALID_SOCKET)\n\t{\n\t\tFD_SET(con->generic.thesocket, readfdset); // network socket\n\t\tif (maxfd < con->generic.thesocket)\n\t\t\tmaxfd = con->generic.thesocket;\n\t}\n\treturn maxfd;\n}\n#endif\n\nftenet_generic_connection_t *FTENET_TCP_EstablishConnection(ftenet_connections_t *col, const char *address, netadr_t adr, const struct dtlspeercred_s *peerinfo)\n{\n\t//this is written to support either ipv4 or ipv6, depending on the remote addr.\n\tftenet_tcp_connection_t *newcon;\n\tqboolean isserver = col->islisten;\n\n\tunsigned long _true = true;\n\tSOCKET newsocket;\n\tqboolean tls = (adr.prot == NP_TLS || adr.prot == NP_WSS);\n\n#ifndef HAVE_SSL\n\tif (tls)\n\t{\n\t\tCon_Printf(\"tls not supported in this build\\n\");\n\t\treturn NULL;\n\t}\n#endif\n\n\tnewcon = Z_Malloc(sizeof(*newcon));\n\tnewcon->generic.thesocket = newsocket = INVALID_SOCKET;\n\n\tnewcon->generic.prot = adr.prot;\n\tnewcon->generic.addrtype[0] = adr.type;\n\tnewcon->generic.addrtype[1] = NA_INVALID;\n\n\tif (isserver)\n\t{\n#ifdef HAVE_PACKET\t//unable to listen on tcp if we have no packet interface\n\t\tif (!FTENET_TCP_ChangeLocalAddress(&newcon->generic, address, &adr))\n\t\t{\n\t\t\tZ_Free(newcon);\n\t\t\treturn NULL;\n\t\t}\n#endif\n\t}\n\telse\n\t{\n\t\tnewsocket = TCP_OpenStream(&adr, address);\n\t\tif (newsocket == INVALID_SOCKET)\n\t\t{\n\t\t\tZ_Free(newcon);\n\t\t\treturn NULL;\n\t\t}\n\n#ifdef UNIXSOCKETS\n\t\tif (adr.type != NA_UNIX)\n#endif\n\t\t{\n\t\t\t//this isn't fatal\n\t\t\tsetsockopt(newsocket, IPPROTO_TCP, TCP_NODELAY, (char *)&_true, sizeof(_true));\n\t\t}\n\t}\n\n\n\tif (newcon)\n\t{\n\t\tnewcon->tls = tls;\n\t\tif (isserver)\n\t\t{\n\t\t\tnewcon->generic.GetLocalAddresses = FTENET_TCP_GetLocalAddresses;\n\t\t\tnewcon->generic.ChangeLocalAddress = FTENET_TCP_ChangeLocalAddress;\n\t\t}\n\t\tnewcon->generic.GetPacket = FTENET_TCP_GetPacket;\n\t\tnewcon->generic.SendPacket = FTENET_TCP_SendPacket;\n\t\tnewcon->generic.Close = FTENET_TCP_Close;\n#if defined(HAVE_PACKET) && !defined(HAVE_EPOLL)\n\t\tnewcon->generic.SetFDSets = FTENET_TCP_SetFDSets;\n#endif\n\t\tnewcon->generic.PrintStatus = FTENET_TCP_PrintStatus;\n\n\t\tnewcon->generic.owner = col;\n\t\tnewcon->generic.islisten = isserver;\n\n\t\tnewcon->active = 0;\n\n\t\tif (!isserver)\n\t\t{\n\t\t\tchar hostonly[MAX_QPATH];\n\t\t\tchar *resource;\n\n\t\t\tnewcon->active++;\n\t\t\tnewcon->tcpstreams = Z_Malloc(sizeof(*newcon->tcpstreams));\n\t\t\tnewcon->tcpstreams->next = NULL;\n\t\t\tnewcon->tcpstreams->socketnum = newsocket;\n\t\t\tnewcon->tcpstreams->clientstream = FS_WrapTCPSocket(newsocket, true, address);\n\t\t\tnewcon->tcpstreams->inlen = 0;\n\n\t\t\tnewcon->tcpstreams->remoteaddr = adr;\n\n\t\t\t{\n\t\t\t\tconst char *host = strstr(address, \"://\");\n\t\t\t\tconst char *port;\n\t\t\t\thost = host?host+3:address;\n\t\t\t\tport = strchr(host, ':');\n\t\t\t\tresource = strchr(host, '/');\n\t\t\t\tif (!port)\n\t\t\t\t\tport = resource;\n\t\t\t\tif (!port)\n\t\t\t\t\tport = host+strlen(host);\n\t\t\t\tif (!resource)\n\t\t\t\t\tresource = \"/\";\n\n\t\t\t\tif (port-host >= sizeof(hostonly))\n\t\t\t\t{\n\t\t\t\t\tVFS_CLOSE(&newcon->generic);\n\t\t\t\t\treturn NULL;\n\t\t\t\t}\n\t\t\t\tmemcpy(hostonly, host, port-host);\n\t\t\t\thostonly[port-host] = 0;\n\t\t\t}\n#ifdef HAVE_SSL\n\t\t\tif (newcon->tls)\t//if we're meant to be using tls, wrap the stream in a tls connection\n\t\t\t{\t//remove any markup junk, get just the hostname out of it.\n\t\t\t\tnewcon->tcpstreams->clientstream = FS_OpenSSL((peerinfo && peerinfo->name)?peerinfo->name:hostonly, newcon->tcpstreams->clientstream, false);\n\t\t\t\tif (!newcon->tcpstreams->clientstream)\n\t\t\t\t\treturn NULL;\n\t\t\t}\n#endif\n\n#ifdef HAVE_HTTPSV\n\t\t\tif (adr.prot == NP_WS || adr.prot == NP_WSS)\n\t\t\t{\n\t\t\t\tconst char *s;\n\t\t\t\tchar key[16];\n\t\t\t\tchar b64key[(16*4)/3+3];\n\t\t\t\tSys_RandomBytes(key, sizeof(key));\n\t\t\t\tBase64_EncodeBlock(key, sizeof(key), b64key, sizeof(b64key));\n\t\t\t\ts = va(\"GET %s HTTP/1.1\\r\\n\"\n\t\t\t\t\t\t\"Host: %s\\r\\n\"\n\t\t\t\t\t\t\"Connection: Upgrade\\r\\n\"\n\t\t\t\t\t\t\"Upgrade: websocket\\r\\n\"\n\t\t\t\t\t\t\"Sec-WebSocket-Key: %s\\r\\n\"\n\t\t\t\t\t\t\"Sec-WebSocket-Version: 13\\r\\n\"\n\t\t\t\t\t\t\"Sec-WebSocket-Protocol: quake,fteqw\\r\\n\"\t//fteqw for proper protocols, falling back to webquake's alternative which breaks OOB/handshakes/etc (which is also buggy enough to require it to be the first protocol passed).\n\t\t\t\t\t\t\"\\r\\n\", resource, hostonly, b64key);\n\t\t\t\tnewcon->tcpstreams->clienttype = TCPC_UNKNOWN;\n\t\t\t\tmemcpy(newcon->tcpstreams->outbuffer, s, newcon->tcpstreams->outlen = strlen(s));\n\t\t\t}\n\t\t\telse\n#endif\n\t\t\t//if (adr.prot == NP_STREAM || adr.prot == NP_TLS)\n\t\t\t{\t//send the qizmo greeting. any actual data is just <ushort len><byte data[len]> otherwise consistent with qw/udp, including challenges.\n\t\t\t\tnewcon->tcpstreams->clienttype = TCPC_UNKNOWN;\n\t\t\t\tmemcpy(newcon->tcpstreams->outbuffer, \"qizmo\\n\", newcon->tcpstreams->outlen = 6);\n\t\t\t}\n\n\t\t\tnewcon->tcpstreams->timeouttime = Sys_DoubleTime() + 30;\n\t\t\tFTENET_TCP_Flush(newcon, newcon->tcpstreams);\t//try and send it now, if we can.\n\t\t}\n\t\telse\n\t\t{\n\t\t\tnewcon->tcpstreams = NULL;\n\t\t}\n\n\t\treturn &newcon->generic;\n\t}\n\telse\n\t{\n\t\tclosesocket(newsocket);\n\t\treturn NULL;\n\t}\n}\n\n#endif\n\n#ifdef IRCCONNECT\n\ntypedef struct ftenet_ircconnect_stream_s {\n\tchar theiruser[16];\n\n\tint inlen;\n\tchar inbuffer[1500];\n\tfloat timeouttime;\n\tnetadr_t remoteaddr;\n\tstruct ftenet_ircconnect_stream_s *next;\n} ftenet_ircconnect_stream_t;\n\ntypedef struct {\n\tftenet_generic_connection_t generic;\n\n\tnetadr_t ircserver;\n\n\tchar incoming[512+1];\n\tint income;\n\n\tchar ourusername[16];\n\tchar usechannel[16];\n\n\tchar outbuf[8192];\n\tunsigned int outbufcount;\n\n\tftenet_ircconnect_stream_t *streams;\n} ftenet_ircconnect_connection_t;\n\nqboolean FTENET_IRCConnect_GetPacket(ftenet_generic_connection_t *gcon)\n{\n\tunsigned char *s, *start, *end, *endl;\n\tint read;\n\tunsigned char *from;\n\tint fromlen;\n\tint code;\n\tchar adr[128];\n\n\tftenet_ircconnect_connection_t *con = (ftenet_ircconnect_connection_t*)gcon;\n\n\tif (con->generic.thesocket == INVALID_SOCKET)\n\t{\n\t\tif (con->income == 0)\n\t\t{\n\t\t\tnetadr_t ip;\n\t\t\tcvar_t *ircuser = Cvar_Get(\"ircuser\", \"none\", 0, \"IRC Connect\");\n\t\t\tcvar_t *ircnick = Cvar_Get(\"ircnick\", \"\", 0, \"IRC Connect\");\n\t\t\tcvar_t *ircsomething = Cvar_Get(\"ircsomething\", \"moo\", 0, \"IRC Connect\");\n\t\t\tcvar_t *ircclientaddr = Cvar_Get(\"ircclientaddr\", \"127.0.0.1\", 0, \"IRC Connect\");\n\n\t\t\tif (!NET_StringToAdr(con->ircserver.address.irc.host, 6667, &ip))\n\t\t\t\treturn false;\n\t\t\tcon->generic.thesocket = TCP_OpenStream(&ip);\n\n\t\t\t//when hosting, the specified nick is the name we're using.\n\t\t\t//when connecting, the specified nick is the name we're trying to send to, and our own name is inconsequential.\n\t\t\tif (con->generic.islisten && *con->ircserver.address.irc.user)\n\t\t\t\tQ_strncpyz(con->ourusername, con->ircserver.address.irc.user, sizeof(con->ourusername));\n\t\t\telse\n\t\t\t\tQ_strncpyz(con->ourusername, ircnick->string, sizeof(con->ourusername));\n\n\t\t\tif (!*con->ourusername)\n\t\t\t{\n\t\t\t\tQ_snprintfz(con->ourusername, sizeof(con->ourusername), \"fte%x\\n\", rand());\n\t\t\t}\n\n\t\t\tsend(con->generic.thesocket, \"USER \", 5, 0);\n\t\t\tsend(con->generic.thesocket, ircuser->string, strlen(ircuser->string), 0);\n\t\t\tsend(con->generic.thesocket, \" \", 1, 0);\n\t\t\tsend(con->generic.thesocket, con->ircserver.address.irc.host, strlen(con->ircserver.address.irc.host), 0);\n\t\t\tsend(con->generic.thesocket, \" \", 1, 0);\n\t\t\tsend(con->generic.thesocket, ircclientaddr->string, strlen(ircclientaddr->string), 0);\n\t\t\tsend(con->generic.thesocket, \" :\", 2, 0);\n\t\t\tsend(con->generic.thesocket, ircsomething->string, strlen(ircsomething->string), 0);\n\t\t\tsend(con->generic.thesocket, \"\\r\\n\", 2, 0);\n\t\t\tsend(con->generic.thesocket, \"NICK \", 5, 0);\n\t\t\tsend(con->generic.thesocket, con->ourusername, strlen(con->ourusername), 0);\n\t\t\tsend(con->generic.thesocket, \"\\r\\n\", 2, 0);\n\t\t}\n\t}\n\telse\n\t{\n\t\tread = recv(con->generic.thesocket, con->incoming+con->income, sizeof(con->incoming)-1 - con->income, 0);\n\t\tif (read < 0)\n\t\t{\n\t\t\tread = neterrno();\n\t\t\tswitch(read)\n\t\t\t{\n\t\t\tcase NET_ECONNABORTED:\n\t\t\tcase NET_ECONNRESET:\n\t\t\t\tclosesocket(con->generic.thesocket);\n\t\t\t\tcon->generic.thesocket = INVALID_SOCKET;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tread = 0;//return false;\n\t\t}\n\t\telse if (read == 0)\t//they disconnected.\n\t\t{\n\t\t\tclosesocket(con->generic.thesocket);\n\t\t\tcon->generic.thesocket = INVALID_SOCKET;\n\t\t}\n\n\t\tcon->income += read;\n\t\tcon->incoming[con->income] = 0;\n\t}\n\n\tstart = con->incoming;\n\tend = start+con->income;\n\n\twhile (start < end)\n\t{\n\t\tendl = NULL;\n\t\tfor (s = start; s < end; s++)\n\t\t{\n\t\t\tif (*s == '\\n')\n\t\t\t{\n\t\t\t\tendl = s;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (endl == NULL)\n\t\t\t//not got a complete command.\n\t\t\tbreak;\n\n\t\ts = start;\n\t\twhile(*s == ' ')\n\t\t\ts++;\n\t\tif (*s == ':')\n\t\t{\n\t\t\ts++;\n\t\t\tfrom = s;\n\t\t\twhile(s<endl && *s != ' ' && *s != '\\n')\n\t\t\t{\n\t\t\t\ts++;\n\t\t\t}\n\t\t\tfromlen = s - from;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tfrom = NULL;\n\t\t\tfromlen = 0;\n\t\t}\n\n\t\twhile(*s == ' ')\n\t\t\ts++;\n\t\tif (!strncmp(s, \"PRIVMSG \", 8))\n\t\t{\n\t\t\tunsigned char *dest;\n\n\t\t\ts+=8;\n\t\t\twhile(*s == ' ')\n\t\t\t\ts++;\n\n\t\t\t//cap the length\n\t\t\tif (fromlen > sizeof(net_from.address.irc.user)-1)\n\t\t\t\tfromlen = sizeof(net_from.address.irc.user)-1;\n\t\t\tfor (code = 0; code < fromlen; code++)\n\t\t\t\tif (from[code] == '!')\n\t\t\t\t{\n\t\t\t\t\tfromlen = code;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\tnet_from.type = NA_IRC;\n\t\t\tmemcpy(net_from.address.irc.user, from, fromlen);\n\t\t\tnet_from.address.irc.user[fromlen] = 0;\n\n\t\t\tdest = s;\n\t\t\t//discard the destination name\n\t\t\twhile(s<endl && *s != ' ' && *s != '\\n')\n\t\t\t{\n\t\t\t\ts++;\n\t\t\t}\n\t\t\tif (s-dest >= sizeof(net_from.address.irc.channel))\n\t\t\t{\t//no space, just pretend it was direct.\n\t\t\t\tnet_from.address.irc.channel[0] = 0;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tmemcpy(net_from.address.irc.channel, dest, s-dest);\n\t\t\t\tnet_from.address.irc.channel[s-dest] = 0;\n\n\t\t\t\tif (!strcmp(net_from.address.irc.channel, con->ourusername))\n\t\t\t\t{\t//this was aimed at us. clear the channel.\n\t\t\t\t\tnet_from.address.irc.channel[0] = 0;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\twhile(*s == ' ')\n\t\t\t\ts++;\n\n\t\t\tif (*s == ':')\n\t\t\t{\n\t\t\t\ts++;\n\n\t\t\t\tif (*s == '!')\n\t\t\t\t{\n\t\t\t\t\ts++;\n\n\t\t\t\t\t/*interpret as a connectionless packet*/\n\t\t\t\t\tnet_message.cursize = 4 + endl - s;\n\t\t\t\t\tif (net_message.cursize >= sizeof(net_message_buffer) )\n\t\t\t\t\t{\n\t\t\t\t\t\tCon_TPrintf (\"Warning:  Oversize packet from %s\\n\", NET_AdrToString (adr, sizeof(adr), &net_from));\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\t*(unsigned int*)net_message_buffer = ~0;\n\t\t\t\t\tmemcpy(net_message_buffer+4, s, net_message.cursize);\n\n\t\t\t\t\tnet_message.packing = SZ_RAWBYTES;\n\t\t\t\t\tnet_message.currentbit = 0;\n\n\t\t\t\t\t//clean up the incoming data\n\t\t\t\t\tmemmove(con->incoming, start, end - (endl+1));\n\t\t\t\t\tcon->income = end - (endl+1);\n\t\t\t\t\tcon->incoming[con->income] = 0;\n\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t\tif (*s == '$')\n\t\t\t\t{\n\t\t\t\t\tunsigned char *nstart = s;\n\t\t\t\t\twhile (*s != '\\r' && *s != '\\n' && *s != '#' && *s != ' ' && *s != ':')\n\t\t\t\t\t\ts++;\n\t\t\t\t\tif (*s == '#')\n\t\t\t\t\t{\n\t\t\t\t\t\tif (strncmp(nstart, con->ourusername, strlen(con->ourusername)) || strlen(con->ourusername) != s - nstart)\n\t\t\t\t\t\t\twhile(*s == '#')\n\t\t\t\t\t\t\t\ts++;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (*s == '#')\n\t\t\t\t{\n\t\t\t\t\tftenet_ircconnect_stream_t *st;\n\t\t\t\t\tint psize;\n\n\t\t\t\t\tfor (st = con->streams; st; st = st->next)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (!strncmp(st->remoteaddr.address.irc.user, from, fromlen)\t&& st->remoteaddr.address.irc.user[fromlen] == 0)\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tif (!st)\n\t\t\t\t\t{\n\t\t\t\t\t\tst = Z_Malloc(sizeof(*st));\n\n\t\t\t\t\t\tst->remoteaddr = net_from;\n\t\t\t\t\t\tst->next = con->streams;\n\t\t\t\t\t\tcon->streams = st;\n\t\t\t\t\t}\n\n\t\t\t\t\t//skip over the hash\n\t\t\t\t\ts++;\n\n\t\t\t\t\tpsize = 0;\n\t\t\t\t\tif (*s >= 'a' && *s <= 'f')\n\t\t\t\t\t\tpsize += *s - 'a' + 10;\n\t\t\t\t\telse if (*s >= '0' && *s <= '9')\n\t\t\t\t\t\tpsize += *s - '0';\n\t\t\t\t\ts++;\n\n\t\t\t\t\tpsize*=16;\n\t\t\t\t\tif (*s >= 'a' && *s <= 'f')\n\t\t\t\t\t\tpsize += *s - 'a' + 10;\n\t\t\t\t\telse if (*s >= '0' && *s <= '9')\n\t\t\t\t\t\tpsize += *s - '0';\n\t\t\t\t\ts++;\n\n\t\t\t\t\tpsize*=16;\n\t\t\t\t\tif (*s >= 'a' && *s <= 'f')\n\t\t\t\t\t\tpsize += *s - 'a' + 10;\n\t\t\t\t\telse if (*s >= '0' && *s <= '9')\n\t\t\t\t\t\tpsize += *s - '0';\n\t\t\t\t\ts++;\n\n\t\t\t\t\twhile (s < endl && st->inlen < sizeof(st->inbuffer))\n\t\t\t\t\t{\n\t\t\t\t\t\tswitch (*s)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t//handle markup\n\t\t\t\t\t\tcase '\\\\':\n\t\t\t\t\t\t\ts++;\n\t\t\t\t\t\t\tif (s < endl)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tswitch(*s)\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tcase '\\\\':\n\t\t\t\t\t\t\t\t\tst->inbuffer[st->inlen++] = *s;\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\tcase 'n':\n\t\t\t\t\t\t\t\t\tst->inbuffer[st->inlen++] = '\\n';\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\tcase 'r':\n\t\t\t\t\t\t\t\t\tst->inbuffer[st->inlen++] = '\\r';\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\tcase '0':\n\t\t\t\t\t\t\t\t\tst->inbuffer[st->inlen++] = 0;\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\t\t\tst->inbuffer[st->inlen++] = '?';\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\t//ignore these\n\t\t\t\t\t\tcase '\\n':\n\t\t\t\t\t\tcase '\\r':\n\t\t\t\t\t\tcase '\\0':\t//this one doesn't have to be ignored.\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\t//handle normal char\n\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\tst->inbuffer[st->inlen++] = *s;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t\ts++;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (st->inlen > psize || psize >= sizeof(net_message_buffer) )\n\t\t\t\t\t{\n\t\t\t\t\t\tst->inlen = 0;\n\t\t\t\t\t\tCon_Printf (\"Corrupt packet from %s\\n\", NET_AdrToString (adr, sizeof(adr), &net_from));\n\t\t\t\t\t}\n\t\t\t\t\telse if (st->inlen == psize)\n\t\t\t\t\t{\n\t\t\t\t\t\t/*interpret as a connectionless packet*/\n\t\t\t\t\t\tnet_message.cursize = st->inlen;\n\t\t\t\t\t\tif (net_message.cursize >= sizeof(net_message_buffer) )\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tCon_TPrintf (\"Warning:  Oversize packet from %s\\n\", NET_AdrToString (adr, sizeof(adr), &net_from));\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tmemcpy(net_message_buffer, st->inbuffer, net_message.cursize);\n\n\t\t\t\t\t\tnet_message.packing = SZ_RAWBYTES;\n\t\t\t\t\t\tnet_message.currentbit = 0;\n\n\t\t\t\t\t\tst->inlen = 0;\n\n\t\t\t\t\t\t//clean up the incoming data\n\t\t\t\t\t\tmemmove(con->incoming, start, end - (endl+1));\n\t\t\t\t\t\tcon->income = end - (endl+1);\n\t\t\t\t\t\tcon->incoming[con->income] = 0;\n\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse if (!strncmp(s, \"PING \", 5))\n\t\t{\n\t\t\tsend(con->generic.thesocket, \"PONG \", 5, 0);\n\t\t\tsend(con->generic.thesocket, s+5, endl - s - 5, 0);\n\t\t\tsend(con->generic.thesocket, \"\\r\\n\", 2, 0);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tcode = strtoul(s, (char **)&s, 10);\n\t\t\tswitch (code)\n\t\t\t{\n\t\t\tcase   1:\n\t\t\t\t{\n\t\t\t\t\tif (con->ircserver.address.irc.channel)\n\t\t\t\t\t{\n\t\t\t\t\t\tsend(con->generic.thesocket, \"JOIN \", 5, 0);\n\t\t\t\t\t\tsend(con->generic.thesocket, con->ircserver.address.irc.channel, strlen(con->ircserver.address.irc.channel), 0);\n\t\t\t\t\t\tsend(con->generic.thesocket, \"\\r\\n\", 2, 0);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase 433:\n\t\t\t\t//nick already in use\n\t\t\t\tsend(con->generic.thesocket, \"NICK \", 5, 0);\n\t\t\t\t{\n\t\t\t\t\tcvar_t *ircnick2 = Cvar_Get(\"ircnick2\", \"YIBBLE\", 0, \"IRC Connect\");\n\t\t\t\t\tQ_strncpyz(con->ourusername, ircnick2->string, sizeof(con->ourusername));\n\t\t\t\t\tsend(con->generic.thesocket, con->ourusername, strlen(con->ourusername), 0);\n\t\t\t\t}\n\t\t\t\tsend(con->generic.thesocket, \"\\r\\n\", 2, 0);\n\t\t\t\tbreak;\n\t\t\tcase 0:\n\t\t\t\t//non-numerical event.\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\twhile(*s == ' ')\n\t\t\ts++;\n\n\t\tstart = s = endl+1;\n\t}\n\n\tmemmove(con->incoming, start, end - start);\n\tcon->income = end - start;\n\tcon->incoming[con->income] = 0;\n\n\tif (con->generic.thesocket == INVALID_SOCKET)\n\t\tcon->income = 0;\n\n\treturn false;\n}\nneterr_t FTENET_IRCConnect_SendPacket(ftenet_generic_connection_t *gcon, int length, const void *data, netadr_t *to)\n{\n\tftenet_ircconnect_connection_t *con = (ftenet_ircconnect_connection_t*)gcon;\n\n\tunsigned char *buffer;\n\tunsigned char *lenofs;\n\tint packed;\n\tint fulllen = length;\n\tint newoutcount;\n\n\tfor (packed = 0; packed < FTENET_ADDRTYPES; packed++)\n\t\tif (to->type == con->generic.addrtype[packed])\n\t\t\tbreak;\n\tif (packed == FTENET_ADDRTYPES)\n\t\treturn NETERR_NOROUTE;\n\n\tpacked = 0;\n\n\tif (con->generic.thesocket == INVALID_SOCKET)\n\t\treturn NETERR_DISCONNECTED;\n/*\n\tif (*(unsigned int *)data == ~0 && !strchr(data, '\\n') && !strchr(data, '\\r') && strlen(data) == length)\n\t{\n\t\tif (send(con->generic.thesocket, va(\"PRIVMSG %s :!\", to.address.irc.user), 15, 0) != 15)\n\t\t\tCon_Printf(\"bad send\\n\");\n\t\telse if (send(con->generic.thesocket, (char*)data+4, length - 4, 0) != length-4)\n\t\t\tCon_Printf(\"bad send\\n\");\n\t\telse if (send(con->generic.thesocket, \"\\r\\n\", 2, 0) != 2)\n\t\t\tCon_Printf(\"bad send\\n\");\n\t\treturn true;\n\t}\n*/\n\tnewoutcount = con->outbufcount;\n\tif (!con->outbufcount)\n\twhile(length)\n\t{\n\t\tbuffer = con->outbuf + newoutcount;\n\n\t\tif (*to->address.irc.channel)\n\t\t{\n\t\t\tint unamelen;\n\t\t\tint chanlen;\n\t\t\tunamelen = strlen(to->address.irc.user);\n\t\t\tchanlen = strlen(to->address.irc.channel);\n\t\t\tpacked = 8+chanlen+3+unamelen+1 + 3;\n\n\t\t\tif (packed+1 + newoutcount > sizeof(con->outbuf))\n\t\t\t\tbreak;\n\n\t\t\tmemcpy(buffer, \"PRIVMSG \", 8);\n\t\t\tmemcpy(buffer+8, to->address.irc.channel, chanlen);\n\t\t\tmemcpy(buffer+8+chanlen, \" :$\", 3);\n\t\t\tmemcpy(buffer+8+chanlen+3, to->address.irc.user, unamelen);\n\t\t\tmemcpy(buffer+8+chanlen+3+unamelen, \"#\", 1);\n\t\t\tlenofs = buffer+8+chanlen+3+unamelen+1;\n\t\t\tsprintf(lenofs, \"%03x\", fulllen);\n\n\t\t}\n\t\telse\n\t\t{\n\t\t\tint unamelen;\n\t\t\tunamelen = strlen(to->address.irc.user);\n\t\t\tpacked = 8 + unamelen + 3 + 3;\n\n\t\t\tif (packed+1 + newoutcount > sizeof(con->outbuf))\n\t\t\t\tbreak;\n\n\t\t\tmemcpy(buffer, \"PRIVMSG \", 8);\n\t\t\tmemcpy(buffer+8, to->address.irc.user, unamelen);\n\t\t\tmemcpy(buffer+8+unamelen, \" :#\", 3);\n\t\t\tlenofs = buffer+8+unamelen+3;\n\t\t\tsprintf(lenofs, \"%03x\", fulllen);\n\t\t}\n\n\n\t\twhile(length && packed < 400 && packed+newoutcount < sizeof(con->outbuf)-2)\t//make sure there's always space\n\t\t{\n\t\t\tswitch(*(unsigned char*)data)\n\t\t\t{\n\t\t\tcase '\\\\':\n\t\t\t\tbuffer[packed++] = '\\\\';\n\t\t\t\tbuffer[packed++] = '\\\\';\n\t\t\t\tbreak;\n\t\t\tcase '\\n':\n\t\t\t\tbuffer[packed++] = '\\\\';\n\t\t\t\tbuffer[packed++] = 'n';\n\t\t\t\tbreak;\n\t\t\tcase '\\r':\n\t\t\t\tbuffer[packed++] = '\\\\';\n\t\t\t\tbuffer[packed++] = 'r';\n\t\t\t\tbreak;\n\t\t\tcase '\\0':\n\t\t\t\tbuffer[packed++] = '\\\\';\n\t\t\t\tbuffer[packed++] = '0';\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tbuffer[packed++] = *(unsigned char*)data;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tlength--;\n\t\t\tdata = (char*)data + 1;\n\t\t}\n\n\t\tbuffer[packed++] = '\\r';\n\t\tbuffer[packed++] = '\\n';\n\n\t\tnewoutcount += packed;\n\t\tpacked = 0;\n\t}\n\tif (!length)\n\t{\n\t\t//only if we flushed all\n\t\tcon->outbufcount = newoutcount;\n\t}\n\n\t//try and flush it\n\tlength = send(con->generic.thesocket, con->outbuf, con->outbufcount, 0);\n\tif (length > 0)\n\t{\n\t\tmemmove(con->outbuf, con->outbuf+length, con->outbufcount-length);\n\t\tcon->outbufcount -= length;\n\t}\n\treturn NETERR_SENT;\n}\nvoid FTENET_IRCConnect_Close(ftenet_generic_connection_t *gcon)\n{\n\tftenet_ircconnect_connection_t *con = (ftenet_ircconnect_connection_t *)gcon;\n\tftenet_ircconnect_stream_t *st;\n\n\twhile(con->streams)\n\t{\n\t\tst = con->streams;\n\t\tcon->streams = st->next;\n\t\tZ_Free(st);\n\t}\n\n\tFTENET_Generic_Close(gcon);\n}\n\nstruct ftenet_generic_connection_s *FTENET_IRCConnect_EstablishConnection(qboolean isserver, const char *address, netadr_t adr)\n{\n\t//this is written to support either ipv4 or ipv6, depending on the remote addr.\n\tftenet_ircconnect_connection_t *newcon;\n\n\tif (!NET_StringToAdr(address, 6667, &adr))\n\t\treturn NULL;\t//couldn't resolve the name\n\n\n\n\tnewcon = Z_Malloc(sizeof(*newcon));\n\tif (newcon)\n\t{\n\t\tnewcon->generic.GetPacket = FTENET_IRCConnect_GetPacket;\n\t\tnewcon->generic.SendPacket = FTENET_IRCConnect_SendPacket;\n\t\tnewcon->generic.Close = FTENET_IRCConnect_Close;\n\n\t\tnewcon->generic.islisten = isserver;\n\t\tnewcon->generic.prot = adr.prot;\n\t\tnewcon->generic.addrtype[0] = NA_IRC;\n\t\tnewcon->generic.addrtype[1] = NA_INVALID;\n\n\t\tnewcon->generic.thesocket = INVALID_SOCKET;\n\n\t\tnewcon->ircserver = adr;\n\n\n\t\treturn &newcon->generic;\n\t}\n\telse\n\t{\n\t\treturn NULL;\n\t}\n}\n\n\n#endif\n\n#ifdef FTE_TARGET_WEB\ncvar_t  net_ice_servers = CVAR(\"net_ice_servers\", \"\");\ncvar_t  net_ice_relayonly = CVAR(\"net_ice_relayonly\", \"0\");\n\ntypedef struct\n{\n\tftenet_generic_connection_t generic;\n\tint brokersock;\t//only if rtc\n\tnetadr_t remoteadr;\n\tqboolean failed;\n\n\tint datasock;\t//only if we're a client\n\tdouble heartbeat;\t//timestamp of next heartbeat.\n\n\tsize_t numclients;\n\tstruct\n\t{\n\t\tnetadr_t remoteadr;\n\t\tint datasock;\n\t} *clients;\n} ftenet_websocket_connection_t;\n\nstatic void FTENET_WebSocket_Close(ftenet_generic_connection_t *gcon)\n{\n\tftenet_websocket_connection_t *wsc = (void*)gcon;\n\tsize_t i;\n\tif (wsc->brokersock != INVALID_SOCKET)\n\t\temscriptenfte_ws_close(wsc->brokersock);\n\tif (wsc->datasock != INVALID_SOCKET)\n\t\temscriptenfte_ws_close(wsc->datasock);\n\tfor (i = 0; i < wsc->numclients; i++)\n\t{\n\t\tif (wsc->clients[i].datasock != INVALID_SOCKET)\n\t\t\temscriptenfte_ws_close(wsc->clients[i].datasock);\n\t}\n\tfree(wsc->clients);\n}\nstatic qboolean FTENET_WebSocket_GetPacket(ftenet_generic_connection_t *gcon)\n{\n\tftenet_websocket_connection_t *wsc = (void*)gcon;\n\tnet_message.cursize = emscriptenfte_ws_recv(wsc->datasock, net_message_buffer, sizeof(net_message_buffer));\n\tif (net_message.cursize > 0)\n\t{\n\t\tnet_from = wsc->remoteadr;\n\t\treturn true;\n\t}\n\tif ((int)net_message.cursize < 0)\n\t\twsc->failed = true;\n\tnet_message.cursize = 0;//just incase\n\treturn false;\n}\nstatic neterr_t FTENET_WebSocket_SendPacket(ftenet_generic_connection_t *gcon, int length, const void *data, netadr_t *to)\n{\n\tftenet_websocket_connection_t *wsc = (void*)gcon;\n\tif (wsc->failed)\n\t\treturn NETERR_DISCONNECTED;\n\tif (NET_CompareAdr(to, &wsc->remoteadr))\n\t{\n\t\tint r = emscriptenfte_ws_send(wsc->datasock, data, length);\n\t\tif (r < 0)\n\t\t\treturn NETERR_DISCONNECTED;\n\t\tif (r == 0 && length)\n\t\t\treturn NETERR_CLOGGED;\n\t\treturn NETERR_SENT;\n\t}\n\treturn NETERR_NOROUTE;\n}\n\nstatic void FTENET_WebRTC_Heartbeat(ftenet_websocket_connection_t *b)\n{\n#ifdef HAVE_SERVER\n\tif (b->generic.islisten)\n\t{\n\t\tchar info[2048];\n\t\tinfo[0] = ICEMSG_SERVERINFO;\n\t\tinfo[1] =\n\t\tinfo[2] = 0xff;\t//to the broker rather than any actual client\n\t\tSV_GeneratePublicServerinfo(info+3, info+sizeof(info));\n\t\tif (emscriptenfte_ws_send(b->brokersock, info, 3+strlen(info+3)) <= 0)\n\t\t\treturn;\n\t}\n#endif\n\tb->heartbeat = realtime+30;\n}\n\n//called from the javascript when there was some ice event. just forwards over the broker connection.\nstatic void FTENET_WebRTC_Callback(void *ctxp, int ctxi, int/*enum icemsgtype_s*/ evtype, const char *data)\n{\n\tftenet_websocket_connection_t *wsc = ctxp;\n\tsize_t dl = strlen(data);\n\tqbyte *o = net_message_buffer;\n\t*o++ = evtype;\n\t*o++ = (ctxi>>0)&0xff;\n\t*o++ = (ctxi>>8)&0xff;\n\tmemcpy(o, data, dl);\n\to+=dl;\n\t//Con_Printf(\"To Broker: %i %i %s\\n\", evtype, ctxi, data);\n\temscriptenfte_ws_send(wsc->brokersock, net_message_buffer, o-net_message_buffer);\n}\nstatic void FTENET_WebRTC_AddICEServer(char *config, size_t sizeofconfig, qboolean *first, const char *uri)\n{\n\t//we don't do the ?foo stuff properly (RFCs say only ?transport= and only for stun)\n\tchar *s = strchr(uri, '?'), *next;\n\tconst char *transport = NULL;\n\tconst char *user = NULL;\n\tconst char *auth = NULL;\n\tchar tmp[256];\n\tfor (;s;s=next)\n\t{\n\t\t*s++ = 0;\n\t\tnext = strchr(s, '?');\n\t\tif (next)\n\t\t\t*next = 0;\n\n\t\tif (!strncmp(s, \"transport=\", 10))\n\t\t\ttransport = s+10;\n\t\telse if (!strncmp(s, \"user=\", 5))\n\t\t\tuser = s+5;\n\t\telse if (!strncmp(s, \"auth=\", 5))\n\t\t\tauth = s+5;\n\t\telse if (!strncmp(s, \"fam=\", 4))\n\t\t\t;\n\t}\n\n\tif (!strncmp(uri, \"turn:\", 5) || !strncmp(uri, \"turns:\", 6))\n\t\tif (!user || !auth)\n\t\t\treturn;\n\n\tif (*first)\n\t\t*first = false;\n\telse\n\t\tQ_strncatz(config, \",\", sizeofconfig);\n\tif (transport)\n\t\tQ_strncatz(config, va(\"\\n{\\\"urls\\\":[\\\"%s?transport=%s\\\"]\", COM_QuotedString(uri, tmp,sizeof(tmp), true), transport), sizeofconfig);\n\telse\n\t\tQ_strncatz(config, va(\"\\n{\\\"urls\\\":[\\\"%s\\\"]\", COM_QuotedString(uri, tmp,sizeof(tmp), true)), sizeofconfig);\n\tif (user)\n\t\tQ_strncatz(config, va(\",\\\"username\\\":\\\"%s\\\"\", COM_QuotedString(user, tmp,sizeof(tmp), true)), sizeofconfig);\n\tif (auth)\n\t\tQ_strncatz(config, va(\",\\\"credential\\\":\\\"%s\\\"\", COM_QuotedString(auth, tmp,sizeof(tmp), true)), sizeofconfig);\n\tQ_strncatz(config, \"}\", sizeofconfig);\n}\nstatic int FTENET_WebRTC_Create(qboolean initiator, ftenet_websocket_connection_t *wsc, int clid, const char *relays)\n{\n\tint fd;\n\tchar config[4096], tmp[256];\n\tqboolean first = true;\n\tconst char *servers;\n\n\t*config = 0;\n\tQ_strncatz(config, \"{\\\"iceServers\\\":[\",\tsizeof(config));\n\t{\n\t\t/*\n\t\t\trtc://broker/id\n\t\t\trtc:///id\n\t\t\t/id\n\t\t*/\n\t\tchar *c;\n\t\tint i;\n\t\tconst char *brokeraddress = wsc->remoteadr.address.websocketurl;\n\n\t\tchar *pre[] = {\t\"wss://\",\t\"ices://\",\t\"rtcs://\",\t\"tls://\",\n\t\t\t\t\t\t\"ws://\",\t\"ice://\",\t\"rtc://\",\t\"tcp://\"};\n\n\t\t//try and clean up the prefix, if specified\n\t\tfor (i = countof(pre); i --> 0; )\n\t\t{\n\t\t\tif (!strncmp(brokeraddress, pre[i], strlen(pre[i])))\n\t\t\t{\n\t\t\t\tbrokeraddress += strlen(pre[i]);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (*brokeraddress == '/' || !*brokeraddress)\n\t\t{\n\t\t\tbrokeraddress = net_ice_broker.string;\n\t\t\tfor (i = countof(pre); i --> 0; )\n\t\t\t{\n\t\t\t\tif (!strncmp(brokeraddress, pre[i], strlen(pre[i])))\n\t\t\t\t{\n\t\t\t\t\tbrokeraddress += strlen(pre[i]);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tQ_strncpyz(com_token, brokeraddress, sizeof(com_token));\n\t\tc = strchr(com_token, '/');\n\t\tif (c) *c = 0;\n\t\tfirst = false;\n\t\tif (*com_token)\n\t\t\tQ_strncatz(config, va(\"{\\\"urls\\\":[\\\"stun:%s\\\"]}\", COM_QuotedString(com_token, tmp,sizeof(tmp), true)), sizeof(config));\n\t}\n\n\t//add any user-specified ice servers\n\tfor(servers = net_ice_servers.string; (servers=COM_Parse(servers)); )\n\t\tFTENET_WebRTC_AddICEServer(config, sizeof(config), &first, com_token);\n\n\t//add any auto-config ones.\n\tfor(servers = relays; (servers=COM_Parse(servers)); )\n\t\tFTENET_WebRTC_AddICEServer(config, sizeof(config), &first, com_token);\n\n\tQ_strncatz(config, va(\"]\"\n//\t\t\",\\\"bundlePolicy\\\":\\\"max-bundle\\\"\"\n\t\t\",\\\"iceTransportPolicy\\\":\\\"%s\\\"\"\n\t\t\"}\",net_ice_relayonly.ival?\"relay\":\"all\"),\tsizeof(config));\n\n\tfd = emscriptenfte_rtc_create(initiator, wsc, clid, FTENET_WebRTC_Callback, config);\n\tif (fd < 0)\n\t\tCon_Printf(\"emscriptenfte_rtc_create failed: %s\\n\", config);\n\treturn fd;\n}\n\nstatic qboolean FTENET_WebRTC_GetPacket(ftenet_generic_connection_t *gcon)\n{\n\tftenet_websocket_connection_t *wsc = (void*)gcon;\n\tsize_t i;\n\tchar id[256];\n\n\tif (wsc->heartbeat < realtime)\n\t\tFTENET_WebRTC_Heartbeat(wsc);\n\n\tif (!wsc->generic.islisten)\n\t{\n\t\tif (wsc->datasock != INVALID_SOCKET && FTENET_WebSocket_GetPacket(gcon))\n\t\t\treturn true;\n\t}\n\telse\n\t{\n\t\tfor (i = 0; i < wsc->numclients; i++)\n\t\t{\n\t\t\tnet_message.cursize = emscriptenfte_ws_recv(wsc->clients[i].datasock, net_message_buffer, sizeof(net_message_buffer));\n\t\t\tif (net_message.cursize > 0)\n\t\t\t{\n\t\t\t\tnet_from = wsc->clients[i].remoteadr;\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t}\n\n\tif (wsc->brokersock == INVALID_SOCKET)\n\t\tnet_message.cursize = 0;\n\telse\n\t{\n\t\tnet_message.cursize = emscriptenfte_ws_recv(wsc->brokersock, net_message_buffer, sizeof(net_message_buffer));\n\t\tif (net_message.cursize < 0)\n\t\t{\n\t\t\temscriptenfte_ws_close(wsc->brokersock);\n\t\t\twsc->brokersock = INVALID_SOCKET; //error!\n\t\t}\n\t}\n\tif (net_message.cursize > 0)\n\t{\n\t\tint cmd;\n\t\tshort cl;\n\t\tconst char *s, *relays;\n\t\tchar *p;\n\n\t\tMSG_BeginReading(&net_message, msg_nullnetprim);\n\t\tcmd = MSG_ReadByte();\n\t\tcl = MSG_ReadShort();\n\n\t\t//Con_Printf(\"From Broker: %i %i\\n\", cmd, cl);\n\n\t\tswitch(cmd)\n\t\t{\n\t\tcase ICEMSG_PEERLOST:\t//connection closing...\n\t\t\ts = MSG_ReadString();\t//reason\n\t\t\tif (cl == -1)\n\t\t\t{\n\t\t\t\twsc->failed = true;\n\t\t\t\tCon_Printf(\"Broker closing connection: %s\\n\", s);\n\t\t\t}\n\t\t\telse if (cl >= 0 && cl < wsc->numclients)\n\t\t\t{\n\t\t\t\twsc->clients[cl].remoteadr.type = NA_INVALID;\n\t\t\t\tif (wsc->clients[cl].datasock != INVALID_SOCKET)\n\t\t\t\t\temscriptenfte_ws_close(wsc->clients[cl].datasock);\n\t\t\t\twsc->clients[cl].datasock = INVALID_SOCKET;\n//\t\t\t\tCon_Printf(\"Broker closing connection: %s\\n\", s);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase ICEMSG_GREETING:\t//reports the trailing url we're 'listening' on. anyone else using that url will connect to us.\n\t\t\ts = MSG_ReadString();\t//the id we're visible as\n\t\t\tif (*s == '/')\n\t\t\t\ts++;\n\t\t\tp = wsc->remoteadr.address.websocketurl;\n\t\t\twhile (*p)\n\t\t\t{\n\t\t\t\tif (p[0] == ':' && p[1] == '/' && p[2] == '/')\n\t\t\t\t\tp+=3;\n\t\t\t\telse if (p[0] == '/')\n\t\t\t\t{\n\t\t\t\t\t*p = 0;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tp++;\n\t\t\t}\n\t\t\tQ_strncatz(wsc->remoteadr.address.websocketurl, \"/\", sizeof(wsc->remoteadr.address.websocketurl));\n\t\t\tQ_strncatz(wsc->remoteadr.address.websocketurl, s, sizeof(wsc->remoteadr.address.websocketurl));\n\t\t\tCon_Printf(\"Listening on %s\\n\", wsc->remoteadr.address.websocketurl);\n\t\t\tbreak;\n\t\tcase ICEMSG_NEWPEER:\t//connection established with a new peer\n\t\t\t/*peer*/ MSG_ReadString();\n\t\t\trelays = MSG_ReadString();\n\n\t\t\tif (wsc->generic.islisten)\n\t\t\t{\n\t\t\t\tif (cl < 1024 && cl >= wsc->numclients)\n\t\t\t\t{\t//looks like a new one... but don't waste memory\n\t\t\t\t\tsize_t nm;\n\t\t\t\t\tnm = cl+1;\n\t\t\t\t\twsc->clients = realloc(wsc->clients, sizeof(*wsc->clients)*nm);\n\t\t\t\t\twhile(wsc->numclients < nm)\n\t\t\t\t\t{\n\t\t\t\t\t\tmemset(&wsc->clients[i].remoteadr, 0, sizeof(wsc->clients[i].remoteadr));\n\t\t\t\t\t\twsc->clients[wsc->numclients++].datasock = INVALID_SOCKET;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (cl < wsc->numclients)\n\t\t\t\t{\n\t\t\t\t\tQ_snprintfz(id, sizeof(id), \"/%i_%x\", cl+1, rand());\n\t\t\t\t\tif (wsc->clients[cl].datasock != INVALID_SOCKET)\n\t\t\t\t\t\temscriptenfte_ws_close(wsc->clients[cl].datasock);\n\t\t\t\t\tmemcpy(&wsc->clients[cl].remoteadr, &wsc->remoteadr, sizeof(netadr_t));\n\t\t\t\t\tQ_strncatz(wsc->clients[cl].remoteadr.address.websocketurl, id, sizeof(wsc->clients[cl].remoteadr.address.websocketurl));\n\t\t\t\t\twsc->clients[cl].remoteadr.port = htons(cl+1);\n\t\t\t\t\twsc->clients[cl].datasock = FTENET_WebRTC_Create(false, wsc, cl, relays);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (wsc->datasock != INVALID_SOCKET)\n\t\t\t\t\temscriptenfte_ws_close(wsc->datasock);\n\t\t\t\twsc->datasock = FTENET_WebRTC_Create(true, wsc, cl, relays);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase ICEMSG_OFFER:\t//we received an offer from a client\n\t\t\ts = MSG_ReadString();\t//either json or raw sdp\n\t\t\tif (wsc->generic.islisten)\n\t\t\t{\n\t\t\t\tif (cl < wsc->numclients && wsc->clients[cl].datasock != INVALID_SOCKET)\n\t\t\t\t\temscriptenfte_rtc_offer(wsc->clients[cl].datasock, s, \"offer\");\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (wsc->datasock != INVALID_SOCKET)\n\t\t\t\t\temscriptenfte_rtc_offer(wsc->datasock, s, \"answer\");\n\t\t\t}\n\t\t\tbreak;\n\t\tcase ICEMSG_CANDIDATE:\n\t\t\ts = MSG_ReadString();\t//either json or raw sdp\n\t\t\tif (s[0] == 'a' && s[1] == '=')\n\t\t\t\ts+=2;\n\t\t\tif (wsc->generic.islisten)\n\t\t\t{\n\t\t\t\tif (cl < wsc->numclients && wsc->clients[cl].datasock != INVALID_SOCKET)\n\t\t\t\t\tif (emscriptenfte_rtc_candidate(wsc->clients[cl].datasock, s) < 0)\n\t\t\t\t\t\tCon_Printf(\"ICEMSG_CANDIDATE(%s): error\\n\", s);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (wsc->datasock != INVALID_SOCKET)\n\t\t\t\t\tif (emscriptenfte_rtc_candidate(wsc->datasock, s) < 0)\n\t\t\t\t\t\tCon_Printf(\"ICEMSG_CANDIDATE(%s): error\\n\", s);\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tnet_message.cursize = 0;//just incase\n\treturn false;\n}\nstatic neterr_t FTENET_WebRTC_SendPacket(ftenet_generic_connection_t *gcon, int length, const void *data, netadr_t *to)\n{\n\tftenet_websocket_connection_t *wsc = (void*)gcon;\n\tsize_t i;\n\tif (!wsc->generic.islisten)\n\t{\n//\t\tif (wsc->failed)\n//\t\t\treturn NETERR_DISCONNECTED;\n\t\tif (NET_CompareAdr(to, &wsc->remoteadr))\n\t\t{\n\t\t\tif (wsc->datasock == INVALID_SOCKET)\n\t\t\t{\n\t\t\t\tif (wsc->brokersock == INVALID_SOCKET)\n\t\t\t\t\treturn NETERR_DISCONNECTED;\t//no broker nor active data channel. its dead jim.\n\t\t\t\treturn NETERR_CLOGGED;\t//we're still waiting for the broker to give us a server... or for a server to become available.\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (emscriptenfte_ws_send(wsc->datasock, data, length) <= 0)\n\t\t\t\t\treturn NETERR_CLOGGED;\n\t\t\t\treturn NETERR_SENT;\n\t\t\t}\n\t\t}\n\t}\n\telse\n\t{\n\t\tfor (i = 0; i < wsc->numclients; i++)\n\t\t{\n\t\t\tif (NET_CompareAdr(to, &wsc->clients[i].remoteadr))\n\t\t\t{\n\t\t\t\tif (emscriptenfte_ws_send(wsc->clients[i].datasock, data, length) <= 0)\n\t\t\t\t\treturn NETERR_CLOGGED;\n\t\t\t\treturn NETERR_SENT;\n\t\t\t}\n\t\t}\n\t}\n\treturn NETERR_NOROUTE;\n}\n\nstatic int FTENET_WebRTC_GetAddresses(struct ftenet_generic_connection_s *con, unsigned int *adrflags, netadr_t *addresses, const char **adrparams, int maxaddresses)\n{\n\tftenet_websocket_connection_t *wsc = (void*)con;\n\tif (maxaddresses)\n\t{\n\t\t*addresses = wsc->remoteadr;\n\t\t*adrflags = 0;\n\t\t*adrparams = NULL;\n\t\treturn 1;\n\t}\n\treturn 0;\n}\n\nstatic int FTENET_WebRTC_Establish(const char *address, const char *type)\n{\n\t/*\n\t\trtc://broker/id\n\t\trtc:///id\n\t\t/id\n\t*/\n\tconst char *path, *host;\n\tchar *c;\n\tint i;\n\tchar url[512];\n\tchar cleanaddress[512];\n\tchar *udp=\"\";\n\n\tchar *pre[] = {\t\"wss://\",\t\"ices://\",\t\"rtcs://\",\t\"tls://\",\n\t\t\t\t\t\"ws://\",\t\"ice://\",\t\"rtc://\",\t\"tcp://\"};\n\n\t//try and clean up the prefix, if specified\n\tfor (i = countof(pre); i --> 0; )\n\t{\n\t\tif (!strncmp(address, pre[i], strlen(pre[i])))\n\t\t{\n\t\t\taddress += strlen(pre[i]);\n\t\t\ti -= i%(countof(pre)/2);\n\t\t\tbreak;\n\t\t}\n\t}\n\n\thost = address;\n\tif (*address == '/')\n\t{\n\t\tpath = address+1;\n\t\taddress = NULL;\n\t}\n\telse\n\t{\n\t\tpath = strchr(address, '/');\n\t\tif (!path)\n\t\t{\n\t\t\tif (i<0)\n\t\t\t{\n\t\t\t\tudp = \"udp/\";\n\t\t\t\tpath = address;\n\t\t\t\taddress = NULL;\n\t\t\t}\n\t\t\telse\n\t\t\t\tpath = \"\";\n\t\t}\n\t}\n\tif (!address)\n\t{\n\t\taddress = net_ice_broker.string;\n\t\tfor (i = countof(pre); i --> 0; )\n\t\t{\n\t\t\tif (!strncmp(address, pre[i], strlen(pre[i])))\n\t\t\t{\n\t\t\t\taddress += strlen(pre[i]);\n\t\t\t\ti -= i%(countof(pre)/2);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (i<0)\n\t\t\ti = 0; //default to the first one... wss...\n\t\tif (!*address)\n\t\t\treturn INVALID_SOCKET;\n\t}\n\n\tQ_strncpyz(cleanaddress, address, sizeof(cleanaddress));\n\tc = strchr(cleanaddress, '/');\n\tif (c) *c = 0;\n\tCOM_Parse(com_protocolname.string);\n\tQ_snprintfz(url, sizeof(url), \"%s%s/%s/%s%s\", pre[i], cleanaddress, com_token, udp, path);\n\treturn emscriptenfte_ws_connect(url, type);\n}\n\nstatic qboolean FTENET_WebRTC_ChangeLocalAddress(struct ftenet_generic_connection_s *con, const char *addressstring, netadr_t *adr)\n{\n\t//ftenet_websocket_connection_t *wsc = (void*)con;\n\treturn true;\t//pretend we changed it, because needed to change in the first place.\n\t//doesn't match how its currently bound, so I guess we need to rebind then.\n//\treturn false;\n}\n\nstatic ftenet_generic_connection_t *FTENET_WebSocket_EstablishConnection(ftenet_connections_t *col, const char *address, netadr_t adr, const struct dtlspeercred_s *peerinfo)\n{\n\tqboolean isserver = col->islisten;\n\tftenet_websocket_connection_t *newcon;\n\n\tint brokersocket = INVALID_SOCKET;\n\tint datasocket = INVALID_SOCKET;\n\n\tnewcon = Z_Malloc(sizeof(*newcon));\n\n\tif (isserver)\n\t{\n\t\tCon_Printf(\"Browsers are unable to host regular servers. Please use an rtc://broker:port/serverid scheme instead.\\n\");\n\t\tdatasocket = INVALID_SOCKET;\n\t}\n\telse\n\t\tdatasocket = emscriptenfte_ws_connect(adr.address.websocketurl, \"fteqw\");\n\n\tnewcon->generic.GetPacket = FTENET_WebSocket_GetPacket;\n\tnewcon->generic.SendPacket = FTENET_WebSocket_SendPacket;\n\tif (brokersocket == INVALID_SOCKET && datasocket == INVALID_SOCKET)\n\t{\n\t\tCon_Printf(\"Unable to create rtc/ws connection\\n\");\n\t\tZ_Free(newcon);\n\t}\n\telse\n\t{\n\t\tQ_strncpyz(newcon->generic.name, \"WebSocket\", sizeof(newcon->generic.name));\n\t\tnewcon->generic.Close = FTENET_WebSocket_Close;\n\n\t\tnewcon->generic.islisten = isserver;\n\t\tnewcon->generic.prot = adr.prot;\n\t\tnewcon->generic.addrtype[0] = NA_WEBSOCKET;\n\t\tnewcon->generic.addrtype[1] = NA_INVALID;\n\n\t\tnewcon->generic.thesocket = INVALID_SOCKET;\n\t\tnewcon->brokersock = brokersocket;\n\t\tnewcon->datasock = datasocket;\n\t\tnewcon->heartbeat = realtime-1;\n\n\t\tadr.port = 0;\n\t\tnewcon->remoteadr = adr;\n\n\t\treturn &newcon->generic;\n\t}\n\treturn NULL;\n}\n\nstatic ftenet_generic_connection_t *FTENET_WebRTC_EstablishConnection(ftenet_connections_t *col, const char *address, netadr_t adr, const struct dtlspeercred_s *peerinfo)\n{\n\tqboolean isserver = col->islisten;\n\tftenet_websocket_connection_t *newcon;\n\n\tint brokersocket = INVALID_SOCKET;\n\tint datasocket = INVALID_SOCKET;\n\n\tnewcon = Z_Malloc(sizeof(*newcon));\n\n\tif (adr.type == NA_INVALID)\n\t{\t//if its using our broker, flip it over to a real address type, if we can.\n\t\tadr.type = NA_WEBSOCKET;\n\t\tQ_strncpyz(adr.address.websocketurl, address, sizeof(adr.address.websocketurl));\n\t}\n\n\tbrokersocket = FTENET_WebRTC_Establish(address, isserver?\"rtc_host\":\"rtc_client\");\n\n\tnewcon->generic.GetPacket = FTENET_WebRTC_GetPacket;\n\tnewcon->generic.SendPacket = FTENET_WebRTC_SendPacket;\n\tnewcon->generic.GetLocalAddresses = FTENET_WebRTC_GetAddresses;\n\n\tnewcon->generic.ChangeLocalAddress = FTENET_WebRTC_ChangeLocalAddress;\n\n\tif (brokersocket == INVALID_SOCKET && datasocket == INVALID_SOCKET)\n\t{\n\t\tCon_Printf(\"Unable to create rtc/ws connection\\n\");\n\t\tZ_Free(newcon);\n\t}\n\telse\n\t{\n\t\tQ_strncpyz(newcon->generic.name, \"WebSocket\", sizeof(newcon->generic.name));\n\t\tnewcon->generic.Close = FTENET_WebSocket_Close;\n\n\t\tnewcon->generic.islisten = isserver;\n\t\tnewcon->generic.prot = adr.prot;\n\t\tnewcon->generic.addrtype[0] = NA_WEBSOCKET;\n\t\tnewcon->generic.addrtype[1] = NA_INVALID;\n\n\t\tnewcon->generic.thesocket = INVALID_SOCKET;\n\t\tnewcon->brokersock = brokersocket;\n\t\tnewcon->datasock = datasocket;\n\t\tnewcon->heartbeat = realtime-1;\n\n\t\tadr.port = 0;\n\t\tnewcon->remoteadr = adr;\n\n\t\treturn &newcon->generic;\n\t}\n\treturn NULL;\n}\n#endif\n\nqboolean NET_GetRates(ftenet_connections_t *collection, float *pi, float *po, float *bi, float *bo)\n{\n\tint ctime;\n\tif (!collection)\n\t\treturn false;\n\n\tctime = Sys_Milliseconds();\n\tif ((ctime - collection->timemark) > 1000)\n\t{\n\t\tfloat secs = (ctime - collection->timemark) / 1000.0f;\n\t\tcollection->packetsinrate = collection->packetsin * secs;\n\t\tcollection->packetsoutrate = collection->packetsout * secs;\n\t\tcollection->bytesinrate = collection->bytesin * secs;\n\t\tcollection->bytesoutrate = collection->bytesout * secs;\n\t\tcollection->packetsin = 0;\n\t\tcollection->packetsout = 0;\n\t\tcollection->bytesin = 0;\n\t\tcollection->bytesout = 0;\n\t\tcollection->timemark = ctime;\n\t}\n\n\t*pi = collection->packetsinrate;\n\t*po = collection->packetsoutrate;\n\t*bi = collection->bytesinrate;\n\t*bo = collection->bytesoutrate;\n\treturn true;\n}\n\n#ifdef HAVE_CLIENT\n//for demo playback\nqboolean NET_UpdateRates(ftenet_connections_t *collection, qboolean inbound, size_t size)\n{\n\tint ctime;\n\tif (!collection)\n\t\treturn false;\n\n\tif (inbound)\n\t{\n\t\tcls.sockets->bytesin += size;\n\t\tcls.sockets->packetsin += 1;\n\t}\n\telse\n\t{\n\t\tcls.sockets->bytesout += size;\n\t\tcls.sockets->packetsout += 1;\n\t}\n\n\tctime = Sys_Milliseconds();\n\tif ((ctime - collection->timemark) > 1000)\n\t{\n\t\tfloat secs = (ctime - collection->timemark) / 1000.0f;\n\t\tcollection->packetsinrate = collection->packetsin * secs;\n\t\tcollection->packetsoutrate = collection->packetsout * secs;\n\t\tcollection->bytesinrate = collection->bytesin * secs;\n\t\tcollection->bytesoutrate = collection->bytesout * secs;\n\t\tcollection->packetsin = 0;\n\t\tcollection->packetsout = 0;\n\t\tcollection->bytesin = 0;\n\t\tcollection->bytesout = 0;\n\t\tcollection->timemark = ctime;\n\t}\n\treturn true;\n}\n#endif\n\nvoid NET_ReadPackets (ftenet_connections_t *collection)\n{\n\tstruct ftenet_delayed_packet_s *p;\n\tunsigned int ctime;\n\tsize_t c = 0;\n\n\tif (!collection)\n\t\treturn;\n\n\twhile ((p = collection->delayed_packets) && (int)(Sys_Milliseconds()-p->sendtime) > 0)\n\t{\n\t\tcollection->delayed_packets = p->next;\n#ifdef SUPPORT_ICE\n\t\tif (p->dest.type == NA_ICE)\n\t\t\tNET_SendPacketCol (collection, p->cursize, p->data, &p->dest);\n\t\telse\n#endif\n#ifdef HAVE_DTLS\n\t\tif (p->dest.prot == NP_DTLS)\n\t\t\tFTENET_DTLS_SendPacket(collection, p->cursize, p->data, &p->dest);\n\t\telse\n#endif\n\t\t\tNET_SendPacketCol (collection, p->cursize, p->data, &p->dest);\n\t\tZ_Free(p);\n\t}\n\n\tfor (c = 0; c < MAX_CONNECTIONS; c++)\n\t{\n\t\tif (collection->conn[c])\n\t\t{\n\t\t\twhile (collection->conn[c] && collection->conn[c]->GetPacket(collection->conn[c]))\n\t\t\t{\n\t\t\t\tif (net_fakeloss.value)\n\t\t\t\t{\n\t\t\t\t\tif (frandom () < net_fakeloss.value)\n\t\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tif (net_fakemtu.ival)\n\t\t\t\t\tif (net_message.cursize > abs(net_fakemtu.ival))\n\t\t\t\t\t\tcontinue;\n\n\t\t\t\tcollection->bytesin += net_message.cursize;\n\t\t\t\tcollection->packetsin += 1;\n\t\t\t\tnet_from.connum = c+1;\n\t\t\t\tnet_from_connection = collection->conn[c];\n\t\t\t\tcollection->ReadGamePacket();\n\t\t\t}\n\t\t}\n\t}\n\n\tctime = Sys_Milliseconds();\n\tif ((ctime - collection->timemark) > 1000)\n\t{\n\t\tfloat secs = (ctime - collection->timemark) / 1000.0f;\n\t\tcollection->packetsinrate = collection->packetsin * secs;\n\t\tcollection->packetsoutrate = collection->packetsout * secs;\n\t\tcollection->bytesinrate = collection->bytesin * secs;\n\t\tcollection->bytesoutrate = collection->bytesout * secs;\n\t\tcollection->packetsin = 0;\n\t\tcollection->packetsout = 0;\n\t\tcollection->bytesin = 0;\n\t\tcollection->bytesout = 0;\n\t\tcollection->timemark = ctime;\n\t}\n\n#ifdef HAVE_DTLS\n\tNET_DTLS_Timeouts(collection);\n#endif\n}\n\nint NET_LocalAddressForRemote(ftenet_connections_t *collection, netadr_t *remote, netadr_t *local, int idx)\n{\n\tint adrflags;\n\tconst char *adrparams;\n\tif (!remote->connum)\n\t\treturn 0;\n\n\tif (!collection->conn[remote->connum-1])\n\t\treturn 0;\n\n\tif (!collection->conn[remote->connum-1]->GetLocalAddresses)\n\t\treturn 0;\n\n\treturn collection->conn[remote->connum-1]->GetLocalAddresses(collection->conn[remote->connum-1], &adrflags, local, &adrparams, 1);\n}\n\nstatic neterr_t NET_SendPacketCol (ftenet_connections_t *collection, int length, const void *data, netadr_t *to)\n{\n\tneterr_t err;\n\tint i;\n\n\tif (!collection)\n\t\treturn NETERR_NOROUTE;\n\n\tif (net_fakeloss.value && data)\n\t{\n\t\tif (frandom () < net_fakeloss.value)\n\t\t{\n\t\t\tcollection->bytesout += length;\n\t\t\tcollection->packetsout += 1;\n\t\t\treturn NETERR_SENT;\n\t\t}\n\t}\n\n\tif (to->connum)\n\t{\n\t\ti = to->connum-1;\n\t\tif (i < MAX_CONNECTIONS && collection->conn[i])\n\t\t{\n\t\t\terr = collection->conn[i]->SendPacket(collection->conn[i], length, data, to);\n\t\t\tif (err != NETERR_NOROUTE)\n\t\t\t{\n\t\t\t\t/*if (err == NETERR_DISCONNECTED)\n\t\t\t\t{\n\t\t\t\t\tcollection->conn[i]->Close(collection->conn[i]);\n\t\t\t\t\tcollection->conn[i] = NULL;\n\t\t\t\t\tcontinue;\n\t\t\t\t}*/\n\n\t\t\t\tcollection->bytesout += length;\n\t\t\t\tcollection->packetsout += 1;\n\t\t\t\treturn err;\n\t\t\t}\n\t\t}\n\t\treturn NETERR_NOROUTE;\n\t}\n\n\tfor (i = 0; i < MAX_CONNECTIONS; i++)\n\t{\n\t\tif (!collection->conn[i])\n\t\t\tcontinue;\n\t\terr = collection->conn[i]->SendPacket(collection->conn[i], length, data, to);\n\t\tif (err != NETERR_NOROUTE)\n\t\t{\n\t\t\t/*if (err == NETERR_DISCONNECTED)\n\t\t\t{\n\t\t\t\tcollection->conn[i]->Close(collection->conn[i]);\n\t\t\t\tcollection->conn[i] = NULL;\n\t\t\t\tcontinue;\n\t\t\t}*/\n\n\t\t\tcollection->bytesout += length;\n\t\t\tcollection->packetsout += 1;\n\t\t\treturn err;\n\t\t}\n\t}\n\n//\tCon_Printf(\"No route to %s - try reconnecting\\n\", NET_AdrToString(buffer, sizeof(buffer), to));\n\treturn NETERR_NOROUTE;\n}\n\nneterr_t NET_SendPacket (ftenet_connections_t *collection, int length, const void *data, netadr_t *to)\n{\n\tif (!collection)\n\t\treturn NETERR_NOROUTE;\n\n#ifdef HAVE_CLIENT\n\tif (collection == cls.sockets && cl_delay_packets.ival >= 1 && !(cl.fpd & FPD_NO_FAKE_LAG))\n\t{\n\t\tstruct ftenet_delayed_packet_s *p, **l;\n\t\tif (!collection)\n\t\t\treturn NETERR_NOROUTE;\t//erk...\n\t\tp = BZ_Malloc(sizeof(*p) - sizeof(p->data) + length);\n\t\tp->sendtime = Sys_Milliseconds() + cl_delay_packets.ival;\n\t\tp->next = NULL;\n\t\tp->cursize = length;\n\t\tp->dest = *to;\n\t\tmemcpy(p->data, data, length);\n\t\tfor (l = &collection->delayed_packets; *l; l = &((*l)->next))\n\t\t\t;\n\t\t*l = p;\n\t\treturn NETERR_SENT; //fixme: mtu, noroute, etc... panic? only allow if udp dest?\n\t}\n#endif\n\n#ifdef SUPPORT_ICE\n\tif (to->type == NA_ICE)\n\t\treturn ICE_SendPacket(length, data, to);\n#endif\n#ifdef HAVE_DTLS\n\tif (NP_ISLAYERED(to->prot))\n\t\treturn FTENET_DTLS_SendPacket(collection, length, data, to);\n#endif\n\treturn NET_SendPacketCol (collection, length, data, to);\n}\n\nqboolean NET_EnsureRoute(ftenet_connections_t *collection, char *routename, const struct dtlspeercred_s *peerinfo, const char *adrstring, netadr_t *adr, qboolean outgoing)\n{\n\tchar temp[MAX_QPATH];\n\tswitch(adr->prot)\n\t{\n\tcase NP_DGRAM:\n#ifdef KEXLAN\n\tcase NP_KEXLAN:\n#endif\n\t\tif (NET_SendPacketCol(collection, 0, NULL, adr) != NETERR_NOROUTE)\n\t\t\treturn true;\n\t\tif (!FTENET_AddToCollection(collection, routename, \"0\", adr->type, adr->prot))\n\t\t\treturn false;\n\t\tbreak;\n\tcase NP_DTLS:\n#ifdef HAVE_DTLS\n\t\tadr->prot = NP_DGRAM;\n\t\tif (NET_EnsureRoute(collection, routename, peerinfo, adrstring, adr, outgoing))\n\t\t{\n\t\t\tdtlscred_t cred;\n\t\t\tmemset(&cred, 0, sizeof(cred));\n\t\t\tcred.peer = *peerinfo;\n\t\t\tif (NET_DTLS_Create(collection, adr, &cred, outgoing))\n\t\t\t{\n\t\t\t\tadr->prot = NP_DTLS;\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\tadr->prot = NP_DTLS;\n#endif\n\t\treturn false;\n#ifdef KEXLOBBY\n\tcase NP_KEXLAN:\n\t\tadr->prot = NP_DGRAM;\n\t\tif (NET_EnsureRoute(collection, routename, peerinfo, adrstring, adr, outgoing))\n\t\t{\n\t\t\tif (NET_KexLobby_Create(collection, adr, outgoing))\n\t\t\t{\n\t\t\t\tadr->prot = NP_KEXLAN;\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\tadr->prot = NP_KEXLAN;\n\t\treturn false;\n#endif\n\n\tcase NP_WS:\n\tcase NP_WSS:\n\tcase NP_TLS:\n\tcase NP_STREAM:\n\t\tif (!adrstring || !*adrstring)\n\t\t\tadrstring = NET_AdrToString(temp, sizeof(temp), adr);\t//urgh\n\t\tif (!FTENET_AddToCollection_Ptr(collection, routename, adrstring, adr, peerinfo))\n\t\t\treturn false;\n\t\tCon_Printf(\"Establishing connection to \\\"%s\\\"\\n\", adrstring);\n\t\tbreak;\n#if defined(SUPPORT_ICE) || defined(FTE_TARGET_WEB)\n\tcase NP_RTC_TCP:\n\tcase NP_RTC_TLS:\n\t\tif (!adrstring)\n\t\t\tadrstring = NET_AdrToString(temp, sizeof(temp), adr);\t//urgh\n\t\tif (!FTENET_AddToCollection_Ptr(collection, routename, adrstring, adr, peerinfo))\n\t\t\treturn false;\n\t\tbreak;\n#endif\n\tdefault:\n\t\t//not recognised, or not needed\n\t\tbreak;\n\t}\n\treturn true;\n}\nvoid NET_TerminateRoute(ftenet_connections_t *collection, netadr_t *adr)\n{\n#ifdef HAVE_DTLS\n\tif (NP_ISLAYERED(adr->prot))\n\t\tNET_DTLS_Disconnect(collection, adr);\n#endif\n\n#ifdef SUPPORT_ICE\n\tif (adr->type == NA_ICE)\n\t\tICE_Terminate(adr);\n#endif\n}\n\nint NET_EnumerateAddresses(ftenet_connections_t *collection, struct ftenet_generic_connection_s **con, unsigned int *adrflags, netadr_t *addresses, const char **adrparams, int maxaddresses)\n{\n\tunsigned int found = 0, c, i, j;\n\tfor (i = 0; i < MAX_CONNECTIONS; i++)\n\t{\n\t\tif (!collection->conn[i])\n\t\t\tcontinue;\n\n\t\tif (collection->conn[i]->GetLocalAddresses)\n\t\t\tc = collection->conn[i]->GetLocalAddresses(collection->conn[i], adrflags+found, addresses+found, adrparams+found, maxaddresses-found);\n\t\telse\n\t\t\tc = 0;\n\n\t\tif (maxaddresses>found && !c)\n\t\t{\n\t\t\tadrflags[found] = 0;\n\t\t\tadrparams[found] = NULL;\n\t\t\taddresses[found].type = NA_INVALID;\n\t\t\taddresses[found].prot = NP_INVALID;\n\t\t\tc = 1;\n\t\t}\n\n\t\t//fill in connection info\n\t\tfor (j = 0; j < c; j++)\n\t\t{\n\t\t\tcon[found+j] = collection->conn[i];\n\t\t\taddresses[found+j].connum = i+1;\n\t\t}\n\n\t\tfound += c;\n\t}\n\treturn found;\n}\n\nstatic enum addressscope_e NET_ClassifyAddressipv4(int ip, const char **outdesc)\n{\n\tint scope = ASCOPE_NET;\n\tconst char *desc = NULL;\n\tif ((ip&BigLong(0xffff0000)) == BigLong(0xA9FE0000))\t//169.254.x.x/16\n\t\tscope = ASCOPE_LINK, desc = localtext(\"link-local\");\n\telse if ((ip&BigLong(0xff000000)) == BigLong(0x0a000000))\t//10.x.x.x/8\n\t\tscope = ASCOPE_LAN, desc = localtext(\"private\");\n\telse if ((ip&BigLong(0xff000000)) == BigLong(0x7f000000))\t//127.x.x.x/8\n\t\tscope = ASCOPE_HOST, desc = \"localhost\";\n\telse if ((ip&BigLong(0xfff00000)) == BigLong(0xac100000))\t//172.16.x.x/12\n\t\tscope = ASCOPE_LAN, desc = localtext(\"private\");\n\telse if ((ip&BigLong(0xffff0000)) == BigLong(0xc0a80000))\t//192.168.x.x/16\n\t\tscope = ASCOPE_LAN, desc = localtext(\"private\");\n\telse if ((ip&BigLong(0xffc00000)) == BigLong(0x64400000))\t//100.64.x.x/10\n\t\tscope = ASCOPE_LAN, desc = localtext(\"CGNAT\");\n\telse if (ip == BigLong(0x00000000))\t//0.0.0.0/32\n\t\tscope = ASCOPE_HOST, desc = \"any\";\n\n\t*outdesc = desc;\n\treturn scope;\n}\nenum addressscope_e NET_ClassifyAddress(netadr_t *adr, const char **outdesc)\n{\n\tint scope = ASCOPE_NET;\n\tconst char *desc = NULL;\n\n\tif (adr->type == NA_LOOPBACK)\n\t{\n\t\t//we don't list 127.0.0.1 or ::1, so don't bother with this either. its not interesting.\n\t\tscope = ASCOPE_PROCESS, desc = localtext(\"internal\");\n\t}\n\telse if (adr->type == NA_IPV6)\n\t{\n\t\tif ((*(int*)adr->address.ip6&BigLong(0xffc00000)) == BigLong(0xfe800000))\t//fe80::/10\n\t\t\tscope = ASCOPE_LINK, desc = localtext(\"link-local\");\n\t\telse if ((*(int*)adr->address.ip6&BigLong(0xfe000000)) == BigLong(0xfc00000))\t//fc::/7\n\t\t\tscope = ASCOPE_LAN, desc = localtext(\"ULA/private\");\n\t\telse if (*(int*)adr->address.ip6 == BigLong(0x20010000)) //2001::/32\n\t\t\tscope = ASCOPE_NET, desc = \"toredo\";\n\t\telse if ((*(int*)adr->address.ip6&BigLong(0xffff0000)) == BigLong(0x20020000)) //2002::/16\n\t\t\tscope = ASCOPE_NET, desc = \"6to4\";\n\t\telse if (memcmp(adr->address.ip6, \"\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\1\", 16) == 0)\t//::1\n\t\t\tscope = ASCOPE_HOST, desc = \"localhost\";\n\t\telse if (memcmp(adr->address.ip6, \"\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\", 16) == 0)\t//::\n\t\t\tscope = ASCOPE_HOST, desc = \"any\";\n\t\telse if (memcmp(adr->address.ip6, \"\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\xff\\xff\", 12) == 0)\t//::ffff:x.y.z.w\n\t\t{\n\t\t\tscope = NET_ClassifyAddressipv4(*(int*)(adr->address.ip6+12), &desc);\n\t\t\tif (!desc)\n\t\t\t\tdesc = localtext(\"v4-mapped\");\n\t\t}\n\t}\n#ifdef UNIXSOCKETS\n\telse if (adr->type == NA_UNIX)\n\t{\n\t\tscope = ASCOPE_HOST, desc = \"unix\";\n\t}\n#endif\n\telse if (adr->type == NA_IP)\n\t\tscope = NET_ClassifyAddressipv4(*(int*)adr->address.ip, &desc);\n\tif (outdesc)\n\t\t*outdesc = desc;\n\treturn scope;\n}\n\n#define MAXADDRESSES 64\nvoid NET_PrintAddresses(ftenet_connections_t *collection)\n{\n\tint i, j;\n\tchar adrbuf[MAX_ADR_SIZE];\n\tint m;\n\tqboolean shown = false;\n\tnetadr_t\taddr[64];\n\tstruct ftenet_generic_connection_s\t\t\t*con[sizeof(addr)/sizeof(addr[0])], *nc;\n\tint\t\t\tflags[sizeof(addr)/sizeof(addr[0])];\n\tconst char *params[sizeof(addr)/sizeof(addr[0])];\n\tqboolean\twarn = true;\n\tstatic const char *scopes[] = {S_COLOR_GRAY\"process\", S_COLOR_GRAY\"local\", S_COLOR_GRAY\"link\", S_COLOR_GRAY\"lan\", \"net\"};\n\tconst char *desc;\n\tchar *fp, *scheme;\n\n\tif (!collection)\n\t\treturn;\n\n\tm = NET_EnumerateAddresses(collection, con, flags, addr, params, sizeof(addr)/sizeof(addr[0]));\n\n\tfor (i = 0; i < m; i++)\n\t{\n\t\tif (addr[i].type != NA_INVALID)\n\t\t{\n\t\t\tenum addressscope_e scope = NET_ClassifyAddress(&addr[i], &desc);\n\t\t\tif (i+1 < m)\n\t\t\t\tnc = con[i+1];\n\t\t\telse\n\t\t\t\tnc = NULL;\n\t\t\tif (nc != con[i])\n\t\t\t{\t//next is a different family.\n\t\t\t\tif (!shown && (scope == ASCOPE_LINK || scope == ASCOPE_HOST))\n\t\t\t\t\tscope = ASCOPE_LAN;\t//force it visible.\n\t\t\t\tshown = false;\n\t\t\t}\n\t\t\tif (developer.ival || scope >= ASCOPE_LAN)\n\t\t\t{\n\t\t\t\tshown = true;\n\t\t\t\twarn = false;\n\t\t\t\tfor (j = 0; j < countof(collection->srflx); j++)\n\t\t\t\t{\n\t\t\t\t\tif (collection->srflx[j].type!=NA_INVALID && NET_CompareAdr(&collection->srflx[j], &addr[i]))\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tif (m < countof(collection->srflx))\n\t\t\t\t\t;\t//gonna print it later anyway.\n\t\t\t\telse if ((addr[i].prot == NP_RTC_TCP || addr[i].prot == NP_RTC_TLS) && params[i])\n\t\t\t\t{\n\t\t\t\t\tif (addr[i].type == NA_INVALID)\n\t\t\t\t\t\tCon_TPrintf(\"%s address (%s): /%s\\n\", scopes[scope], con[i]->name, params[i]);\n\t\t\t\t\telse\n\t\t\t\t\t\tCon_TPrintf(\"%s address (%s): %s/%s\\n\", scopes[scope], con[i]->name, NET_AdrToString(adrbuf, sizeof(adrbuf), &addr[i]), params[i]);\n\t\t\t\t}\n\t\t\t\telse if (desc)\n\t\t\t\t\tCon_TPrintf(\"%s address (%s): %s (%s)\\n\", scopes[scope], con[i]->name, NET_AdrToString(adrbuf, sizeof(adrbuf), &addr[i]), desc);\n\t\t\t\telse\n\t\t\t\t\tCon_TPrintf(\"%s address (%s): %s\\n\", scopes[scope], con[i]->name, NET_AdrToString(adrbuf, sizeof(adrbuf), &addr[i]));\n\t\t\t}\n\t\t}\n\t}\n\n\tfp = \"\";\n\tscheme = \"\";\n#if defined(HAVE_SERVER) && defined(HAVE_DTLS)\n\tif (collection == svs.sockets)\n\t{\n\t\tfp = InfoBuf_ValueForKey(&svs.info, \"*fp\");\n\t\tif (*fp)\n\t\t\tfp = va(S_COLOR_GRAY\"?fp=%s\", fp);\n\t\tif (fs_manifest->schemes && *COM_Parse(fs_manifest->schemes))\n\t\t\tscheme = va(S_COLOR_GRAY\"%s://\", com_token);\n\t}\n#endif\n\t//show any master-reflexive addresses that were not above.\n\tfor (m = 0; m < countof(collection->srflx); m++)\n\t{\n\t\tif (collection->srflx[m].type == NA_INVALID)\n\t\t\tcontinue;\n\t\tif (collection->srflx[m].connum && collection->srflx[m].connum-1u < countof(collection->conn) && collection->conn[collection->srflx[m].connum-1])\n\t\t\tCon_TPrintf(\"reflexive address (%s): %s\"S_COLOR_WHITE\"%s%s\\n\", collection->conn[collection->srflx[m].connum-1]->name, scheme, NET_AdrToString(adrbuf, sizeof(adrbuf), &collection->srflx[m]), fp);\n\t\telse\n\t\t\tCon_TPrintf(\"reflexive address: %s\"S_COLOR_WHITE\"%s%s\\n\", scheme, NET_AdrToString(adrbuf, sizeof(adrbuf), &collection->srflx[m]), fp);\n\t\twarn = false;\n\t}\n\n\tif (warn)\n\t\tCon_TPrintf(\"net address: no public addresses\\n\");\n}\n\nvoid NET_PrintConnectionsStatus(ftenet_connections_t *collection)\n{\n\tunsigned int i;\n\tif (!collection)\n\t\treturn;\n\tfor (i = 0; i < MAX_CONNECTIONS; i++)\n\t{\n\t\tif (!collection->conn[i])\n\t\t\tcontinue;\n\t\tif (collection->conn[i]->PrintStatus)\n\t\t\tcollection->conn[i]->PrintStatus(collection->conn[i]);\n\t}\n\n#ifdef HAVE_DTLS\n\tif (developer.ival)\n\t{\n\t\tstruct dtlspeer_s *dtls;\n\t\tchar adr[64];\n\t\tfor (dtls = collection->dtls; dtls; dtls = dtls->next)\n\t\t\tCon_Printf(\"dtls: %s\\n\", NET_AdrToString(adr, sizeof(adr), &dtls->addr));\n\t}\n\telse\n\t{\n\t\tstruct dtlspeer_s *dtls;\n\t\tint c = 0;\n\t\tfor (dtls = collection->dtls; dtls; dtls = dtls->next)\n\t\t\tc++;\n\t\tif (c)\n\t\t\tCon_Printf(\"dtls connections : %i\\n\", c);\n\t}\n#endif\n}\n\n//=============================================================================\n\nint TCP_OpenStream (netadr_t *remoteaddr, const char *remotename)\n{\n#ifndef HAVE_TCP\n\treturn (int)INVALID_SOCKET;\n#else\n\tunsigned long _true = true;\n\tint newsocket;\n\tint temp;\n\tstruct sockaddr_qstorage qs;\n//\tstruct sockaddr_qstorage loc;\n\tint recvbufsize = (1<<19);//512kb\n\tint sysprot;\n\n\tswitch(remoteaddr->type)\n\t{\n#if defined(HAVE_IPV4) || defined(HAVE_IPV6)\n\tcase NA_IP:\n\tcase NA_IPV6:\n\t\tsysprot = IPPROTO_TCP;\n\t\tbreak;\n#endif\n#ifdef HAVE_IPX\n\tcase NA_IPX:\n\t\tsysprot = NSPROTO_IPX;\n\t\tbreak;\n#endif\n\t//case NA_UNIX:\n\tdefault:\n\t\tsysprot = 0;\t//'auto'\n\t\tbreak;\n\t}\n\ttemp = NetadrToSockadr(remoteaddr, &qs);\n\n\tif ((newsocket = socket (((struct sockaddr_in*)&qs)->sin_family, SOCK_CLOEXEC|SOCK_STREAM, sysprot)) == INVALID_SOCKET)\n\t\treturn (int)INVALID_SOCKET;\n\n\tsetsockopt(newsocket, SOL_SOCKET, SO_RCVBUF, (void*)&recvbufsize, sizeof(recvbufsize));\n\n\tif (ioctlsocket (newsocket, FIONBIO, &_true) == -1)\n\t\tSys_Error (\"TCP_OpenStream: ioctl FIONBIO: %s\", strerror(neterrno()));\n\n#ifdef UNIXSOCKETS\n\tif (remoteaddr->type == AF_UNIX)\n\t{\t//if its a unix socket, attempt to bind it to an unnamed address. linux should generate an ephemerial abstract address (otherwise the server will see an empty address).\n\t\tstruct sockaddr_un un;\n\t\tmemset(&un, 0, offsetof(struct sockaddr_un, sun_path));\n\t\tbind(newsocket, (struct sockaddr*)&un, offsetof(struct sockaddr_un, sun_path));\n\t}\n\telse\n#endif\n\t{\n//\t\tmemset(&loc, 0, sizeof(loc));\n//\t\t((struct sockaddr*)&loc)->sa_family = ((struct sockaddr*)&loc)->sa_family;\n//\t\tbind(newsocket, (struct sockaddr *)&loc, ((struct sockaddr_in*)&qs)->sin_family == AF_INET?sizeof(struct sockaddr_in):sizeof(struct sockaddr_in6));\n\t}\n\n\tif (connect(newsocket, (struct sockaddr *)&qs, temp) == INVALID_SOCKET)\n\t{\n\t\tint err = neterrno();\n\t\tif (err != NET_EWOULDBLOCK && err != NET_EINPROGRESS)\n\t\t{\n\t\t\tif (err == NET_EADDRNOTAVAIL)\n\t\t\t{\n\t\t\t\tif (remoteaddr->port == 0 && (remoteaddr->type == NA_IP || remoteaddr->type == NA_IPV6))\n\t\t\t\t\tCon_Printf (\"TCP_OpenStream: no port specified (%s)\\n\", remotename);\n\t\t\t\telse\n\t\t\t\t\tCon_Printf (\"TCP_OpenStream: invalid address trying to connect to %s\\n\", remotename);\n\t\t\t}\n\t\t\telse if (err == NET_ECONNREFUSED)\n\t\t\t\tCon_Printf (\"TCP_OpenStream: connection refused (%s)\\n\", remotename);\n\t\t\telse if (err == NET_EACCES)\n\t\t\t\tCon_Printf (\"TCP_OpenStream: access denied: check firewall (%s)\\n\", remotename);\n\t\t\telse if (err == NET_ENETUNREACH)\n\t\t\t\tCon_Printf (\"TCP_OpenStream: unreachable (%s)\\n\", remotename);\n\t\t\telse\n\t\t\t\tCon_Printf (\"TCP_OpenStream: connect: error %i (%s)\\n\", err, remotename);\n\t\t\tclosesocket(newsocket);\n\t\t\treturn (int)INVALID_SOCKET;\n\t\t}\n\t}\n\n\treturn newsocket;\n#endif\n}\n\n#if defined(SV_MASTER) || defined(CL_MASTER)\n#ifdef HAVE_IPV4\nint UDP_OpenSocket (int port)\n{\n\tSOCKET newsocket;\n\tstruct sockaddr_in address;\n\tunsigned long _true = true;\n\tint i;\n\tint maxport = port + 100;\n\n\tif ((newsocket = socket (PF_INET, SOCK_CLOEXEC|SOCK_DGRAM, IPPROTO_UDP)) == INVALID_SOCKET)\n\t\treturn (int)INVALID_SOCKET;\n\n\tif (ioctlsocket (newsocket, FIONBIO, &_true) == -1)\n\t\tSys_Error (\"UDP_OpenSocket: ioctl FIONBIO: %s\", strerror(neterrno()));\n\n\taddress.sin_family = AF_INET;\n//ZOID -- check for interface binding option\n\tif ((i = COM_CheckParm(\"-ip\")) != 0 && i < com_argc) {\n\t\taddress.sin_addr.s_addr = inet_addr(com_argv[i+1]);\n\t\tCon_TPrintf(\"Binding to IP Interface Address of %s\\n\",\n\t\t\t\tinet_ntoa(address.sin_addr));\n\t} else\n\t\taddress.sin_addr.s_addr = INADDR_ANY;\n\n\tfor(;;)\n\t{\n\t\tif (port == PORT_ANY)\n\t\t\taddress.sin_port = 0;\n\t\telse\n\t\t\taddress.sin_port = htons((short)port);\n\n\t\tif( bind (newsocket, (void *)&address, sizeof(address)) == -1)\n\t\t{\n\t\t\tif (!port)\n\t\t\t\tSys_Error (\"UDP_OpenSocket: bind: %s\", strerror(neterrno()));\n\t\t\tport++;\n\t\t\tif (port > maxport)\n\t\t\t\tSys_Error (\"UDP_OpenSocket: bind: %s\", strerror(neterrno()));\n\t\t}\n\t\telse\n\t\t\tbreak;\n\t}\n\n\treturn newsocket;\n}\nvoid UDP_CloseSocket (int socket)\n{\n\tclosesocket(socket);\n}\n#endif\n\n#ifdef HAVE_IPV6\nint UDP6_OpenSocket (int port)\n{\n\tint err;\n\tSOCKET newsocket;\n\tstruct sockaddr_in6 address;\n\tunsigned long _true = true;\n//\tint i;\nint maxport = port + 100;\n\n\tmemset(&address, 0, sizeof(address));\n\n\tif ((newsocket = socket (PF_INET6, SOCK_CLOEXEC|SOCK_DGRAM, 0)) == INVALID_SOCKET)\n\t{\n\t\tCon_Printf(\"IPV6 is not supported: %s\\n\", strerror(neterrno()));\n\t\treturn (int)INVALID_SOCKET;\n\t}\n\n\tif (ioctlsocket (newsocket, FIONBIO, &_true) == -1)\n\t\tSys_Error (\"UDP_OpenSocket: ioctl FIONBIO: %s\", strerror(neterrno()));\n\n#ifdef IPV6_V6ONLY\n\tsetsockopt(newsocket, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&_true, sizeof(_true));\n#endif\n\n\taddress.sin6_family = AF_INET6;\n//ZOID -- check for interface binding option\n//\tif ((i = COM_CheckParm(\"-ip6\")) != 0 && i < com_argc) {\n//\t\taddress.sin6_addr = inet_addr(com_argv[i+1]);\n///\t\tCon_TPrintf(\"Binding to IP Interface Address of %s\\n\",\n//\t\t\t\tinet_ntoa(address.sin6_addr));\n//\t} else\n\t\tmemset(&address.sin6_addr, 0, sizeof(struct in6_addr));\n\n\tfor(;;)\n\t{\n\t\tif (port == PORT_ANY)\n\t\t\taddress.sin6_port = 0;\n\t\telse\n\t\t\taddress.sin6_port = htons((short)port);\n\n\t\tif( bind (newsocket, (void *)&address, sizeof(address)) == -1)\n\t\t{\n\t\t\tif (!port)\n\t\t\t{\n\t\t\t\terr = neterrno();\n\t\t\t\tCon_Printf (\"UDP6_OpenSocket: bind: (%i) %s\", err, strerror(err));\n\t\t\t\tclosesocket(newsocket);\n\t\t\t\treturn (int)INVALID_SOCKET;\n\t\t\t}\n\t\t\tport++;\n\t\t\tif (port > maxport)\n\t\t\t{\n\t\t\t\terr = neterrno();\n\t\t\t\tCon_Printf (\"UDP6_OpenSocket: bind: (%i) %s\", err, strerror(err));\n\t\t\t\tclosesocket(newsocket);\n\t\t\t\treturn (int)INVALID_SOCKET;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t\tbreak;\n\t}\n\n\treturn newsocket;\n}\nvoid UDP6_CloseSocket (int socket)\n{\n\tclosesocket(socket);\n}\n#endif\n\n#ifdef HAVE_IPX\nint IPX_OpenSocket (int port)\n{\n\tSOCKET\t\t\t\t\tnewsocket;\n\tstruct sockaddr_ipx\taddress;\n\tu_long\t\t\t\t\t_true = 1;\n\n\tif ((newsocket = socket (PF_IPX, SOCK_CLOEXEC|SOCK_DGRAM, NSPROTO_IPX)) == INVALID_SOCKET)\n\t{\n\t\tint e = neterrno();\n\t\tif (e != NET_EAFNOSUPPORT)\n\t\t\tCon_Printf (\"WARNING: IPX_Socket: socket: %i\\n\", e);\n\t\treturn INVALID_SOCKET;\n\t}\n\n\t// make it non-blocking\n\tif (ioctlsocket (newsocket, FIONBIO, &_true) == -1)\n\t{\n\t\tCon_Printf (\"WARNING: IPX_Socket: ioctl FIONBIO: %i\\n\", neterrno());\n\t\treturn INVALID_SOCKET;\n\t}\n\n\taddress.sa_family = AF_IPX;\n\tmemset (address.sa_netnum, 0, 4);\n\tmemset (address.sa_nodenum, 0, 6);\n\tif (port == PORT_ANY)\n\t\taddress.sa_socket = 0;\n\telse\n\t\taddress.sa_socket = htons((short)port);\n\n\tif( bind (newsocket, (void *)&address, sizeof(address)) == -1)\n\t{\n\t\tCon_Printf (\"WARNING: IPX_Socket: bind: %i\\n\", neterrno());\n\t\tclosesocket (newsocket);\n\t\treturn INVALID_SOCKET;\n\t}\n\n\treturn newsocket;\n}\n\nvoid IPX_CloseSocket (int socket)\n{\n\tclosesocket(socket);\n}\n#endif\n#endif\n\n#ifdef HAVE_EPOLL\nstatic qboolean stdin_ready;\nstatic qboolean stdin_epolling;\nstatic void StdIn_Now_Ready (struct epollctx_s *ctx, unsigned int events)\n{\n\tstdin_ready = true;\n}\nqboolean NET_Sleep(float seconds, qboolean stdinissocket)\n{\n\tint waitms;\n\tstruct epoll_event waitevents[256];\n\tint n, i;\n\tif (epoll_fd < 0)\n\t\treturn false; // o.O\n\tif (stdin_epolling != stdinissocket)\n\t{\n\t\tstatic epollctx_t stdinctx = {StdIn_Now_Ready};\n\t\tstruct epoll_event event = {EPOLLIN, {&stdinctx}};\n\t\tstdin_epolling = stdinissocket;\n\t\tif (stdinissocket)\n\t\t\tepoll_ctl(epoll_fd, EPOLL_CTL_ADD, STDIN_FILENO, &event);\n\t\telse\n\t\t\tepoll_ctl(epoll_fd, EPOLL_CTL_DEL, STDIN_FILENO, &event);\n\t}\n\n\twaitms = bound(0, (int)(seconds*1000), 4*1000);\n\tn = epoll_wait(epoll_fd, waitevents, countof(waitevents), waitms);\n\tif (n < 0)\n\t{\n\t\tint err = errno;\n\t\tswitch(err)\n\t\t{\n\t\tcase EINTR:\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tCon_Printf(\"EPoll error: %s\\n\", strerror(err));\n\t\t\tbreak;\n\t\t}\n\t}\n\tfor (i = 0; i < n; i++)\n\t{\n\t\tstruct epoll_event *ev = &waitevents[i];\n\t\tstruct epollctx_s *ctx = ev->data.ptr;\n\t\tif (ctx)\n\t\t\tctx->Polled(ctx, ev->events);\n\t\t//else edge-triggered events can be processed as part of the main loop\n\t}\n\treturn stdin_ready;\n}\n#else\n// sleeps msec or until net socket is ready\n//stdin can sometimes be a socket. As a result,\n//we give the option to select it for nice console imput with timeouts.\nqboolean NET_Sleep(float seconds, qboolean stdinissocket)\n{\n#ifdef HAVE_PACKET\n\tstruct timeval timeout;\n\tfd_set\treadfdset;\n\tfd_set\twritefdset;\n\tqintptr_t maxfd = -1;\n\tint con, sock;\n\tunsigned int usec;\n\n\tFD_ZERO(&readfdset);\n\tFD_ZERO(&writefdset);\n\n#ifndef _WIN32\n\tif (stdinissocket)\n\t{\n\t\tsock = STDIN_FILENO;\t//stdin tends to be socket/filehandle 0 in unix\n\t\tFD_SET(sock, &readfdset);\n\t\tmaxfd = sock;\n\t}\n#endif\n\n#ifdef SV_MASTER\n\t{\n\t\textern ftenet_connections_t *svm_sockets;\n\t\tif (svm_sockets)\n\t\tfor (con = 0; con < MAX_CONNECTIONS; con++)\n\t\t{\n\t\t\tif (!svm_sockets->conn[con])\n\t\t\t\tcontinue;\n\t\t\tif (svm_sockets->conn[con]->SetFDSets)\n\t\t\t{\n\t\t\t\tsock = svm_sockets->conn[con]->SetFDSets(svm_sockets->conn[con], &readfdset, &writefdset);\n\t\t\t\tif (sock > maxfd)\n\t\t\t\t\tmaxfd = sock;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tsock = svm_sockets->conn[con]->thesocket;\n\t\t\t\tif (sock != INVALID_SOCKET)\n\t\t\t\t{\n\t\t\t\t\tFD_SET(sock, &readfdset); // network socket\n\t\t\t\t\tif (sock > maxfd)\n\t\t\t\t\t\tmaxfd = sock;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n#endif\n#ifdef HAVE_SERVER\n\tif (svs.sockets)\n\tfor (con = 0; con < MAX_CONNECTIONS; con++)\n\t{\n\t\tif (!svs.sockets->conn[con])\n\t\t\tcontinue;\n\t\tif (svs.sockets->conn[con]->SetFDSets)\n\t\t{\n\t\t\tsock = svs.sockets->conn[con]->SetFDSets(svs.sockets->conn[con], &readfdset, &writefdset);\n\t\t\tif (sock > maxfd)\n\t\t\t\tmaxfd = sock;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tsock = svs.sockets->conn[con]->thesocket;\n\t\t\tif (sock != INVALID_SOCKET)\n\t\t\t{\n\t\t\t\tFD_SET(sock, &readfdset); // network socket\n\t\t\t\tif (sock > maxfd)\n\t\t\t\t\tmaxfd = sock;\n\t\t\t}\n\t\t}\n\t}\n#endif\n\n\tseconds = bound(0.0, seconds, 4.0);\t//realy? oh well.\n\tif (maxfd == -1)\n\t\tSys_Sleep(seconds);\n\telse\n\t{\n\t\tusec = seconds*1000*1000;\n\t\tusec += 1000;\t//slight extra delay, to ensure we don't wake up with nothing to do.\n\t\ttimeout.tv_sec = usec/(1000*1000);\n\t\ttimeout.tv_usec = usec;\n\t\tselect(maxfd+1, &readfdset, &writefdset, NULL, &timeout);\n\t}\n\n#ifndef _WIN32\n\tif (stdinissocket)\n\t\treturn FD_ISSET(STDIN_FILENO, &readfdset);\n#endif\n#endif\n\treturn true;\n}\n#endif\n\n//this function is used to determine the 'default' local address.\n//this is used for compat with gamespy which insists on sending us a packet via that interface and not something more sensible like 127.0.0.1\n//thus its only needed on windows and with ipv4.\nvoid NET_GetLocalAddress (int socket, netadr_t *out)\n{\n#if defined(_WIN32) && defined(HAVE_PACKET)\n\tchar\tbuff[512];\n\tstruct sockaddr_qstorage\taddress;\n\tint\t\tnamelen;\n\tnetadr_t adr = {0};\n\tqboolean notvalid = false;\n\n\tstrcpy(buff, \"localhost\");\n\tgethostname(buff, 512);\n\tbuff[512-1] = 0;\n\n\tif (!NET_StringToAdr (buff, 0, &adr))\t//urm\n\t\tif (!NET_StringToAdr (\"127.0.0.1\", 0, &adr))\n\t\t\treturn;\n\n\n\tnamelen = sizeof(address);\n\tif (getsockname (socket, (struct sockaddr *)&address, &namelen) == -1)\n\t{\n\t\tnotvalid = true;\n\t\tNET_StringToSockaddr2(\"0.0.0.0\", 0, NA_INVALID, (struct sockaddr_qstorage *)&address, NULL, NULL, 1);\n//\t\tSys_Error (\"NET_Init: getsockname:\", strerror(qerrno));\n\t}\n\n\tSockadrToNetadr(&address, namelen, out);\n\tif (out->type == NA_IP)\n\t{\n\t\tif (!*(int*)out->address.ip)\t//socket was set to auto\n\t\t{\n\t\t\tif (adr.type == NA_IP)\n\t\t\t\t*(int *)out->address.ip = *(int *)adr.address.ip;\t//change it to what the machine says it is, rather than the socket.\n\t\t}\n\t}\n\tif (out->type == NA_IPV6)\n\t{\n\t\tif (!((int*)out->address.ip6)[0] &&\n\t\t\t!((int*)out->address.ip6)[1] &&\n\t\t\t!((short*)out->address.ip6)[4] &&\n\t\t\t(!((short*)out->address.ip6)[5] || ((unsigned short*)out->address.ip6)[5]==0xffffu)\n\t\t\t&& !((int*)out->address.ip6)[3])\t//ipv6 any or ipv4-mapped any.\n\t\t{\n\t\t\tif (adr.type == NA_IP)\n\t\t\t{\n\t\t\t\tmemset(out->address.ip6, 0, sizeof(out->address.ip6));\n\t\t\t\t((short *)out->address.ip6)[5] = 0xffff;\n\t\t\t\t((int *)out->address.ip6)[3] = *(int *)adr.address.ip;\n\t\t\t}\n\t\t\telse if (adr.type == NA_IPV6)\n\t\t\t\tmemcpy(out->address.ip6, adr.address.ip6, sizeof(out->address.ip6));\n\t\t}\n\t}\n\n\tif (!notvalid)\n\t{\n//\t\tchar\tadrbuf[MAX_ADR_SIZE];\n//\t\tCon_TPrintf(\"Client IP address %s\\n\", NET_AdrToString (adrbuf, sizeof(adrbuf), out) );\n\t\treturn;\n\t}\n//\tCon_Printf(\"Couldn't detect local ip\\n\");\n#endif\n\n\tout->type = NA_INVALID;\n}\n\n#ifdef HAVE_SERVER\nvoid SVNET_AddPort_f(void)\n{\n\tchar *s = Cmd_Argv(1);\n\tchar *conname = Cmd_Argv(2);\n\n\tif (!*s && !*conname)\n\t{\n\t\tCon_Printf(\"Active Server ports:\\n\");\n\t\tNET_PrintAddresses(svs.sockets);\n\t\tCon_Printf(\"end of list\\n\");\n\t\treturn;\n\t}\n\tif (!*conname)\n\t\tconname = NULL;\n\n\t//just in case\n\tif (!svs.sockets)\n\t{\n\t\tsvs.sockets = FTENET_CreateCollection(true, SV_ReadPacket);\n#ifdef HAVE_CLIENT\n\t\tFTENET_AddToCollection(svs.sockets, \"SVLoopback\", STRINGIFY(PORT_DEFAULTSERVER), NA_LOOPBACK, NP_DGRAM);\n#endif\n\t}\n\n\tFTENET_AddToCollection(svs.sockets, conname, *s?s:NULL, *s?NA_IP:NA_INVALID, NP_DGRAM);\n}\n#endif\n\n#ifdef HAVE_CLIENT\nvoid NET_ClientPort_f(void)\n{\n\tCon_Printf(\"Active Client ports:\\n\");\n\tNET_PrintAddresses(cls.sockets);\n\tCon_Printf(\"end of list\\n\");\n}\n#endif\n\nqboolean NET_WasSpecialPacket(ftenet_connections_t *collection)\n{\n#ifdef HAVE_NATPMP\n\tif (NET_Was_NATPMP(collection))\n\t\treturn true;\n#endif\n\n#if defined(SUPPORT_ICE) || (defined(MASTERONLY) && defined(AVAIL_ZLIB))\n\tif (ICE_WasStun(collection))\n\t\treturn true;\n#endif\n\n#if defined(HAVE_DTLS) && defined(HAVE_SERVER)\n\tif (collection->islisten && NET_DTLS_CheckInbound(collection))\n\t\treturn true;\n#endif\n#ifdef KEXLOBBY\n\tif (NET_KexLobby_CheckInbound(collection))\n\t\treturn true;\n#endif\n\n\treturn false;\n}\n\n//static void QDECL NET_UPNPIGP_Callback(cvar_t *var, char *oldval)\n//{\n//}\n//cvar_t net_upnpigp = CVARCD(\"net_upnpigp\", \"0\", NET_UPNPIGP_Callback, \"If set, enables the use of the upnp-igd protocol to punch holes in your local NAT box.\");\n\nvoid SSL_Init(void);\n/*\n====================\nNET_Init\n====================\n*/\nvoid NET_Init (void)\n{\n#ifdef HAVE_EPOLL\n\tif (epoll_fd < 0)\n\t\tepoll_fd = epoll_create1(EPOLL_CLOEXEC);\n#endif\n\n\tCvar_Register(&net_enabled, \"networking\");\n\tCvar_Register(&net_dns_ipv4, \"networking\");\n\tCvar_Register(&net_dns_ipv6, \"networking\");\n\tif (net_enabled.ival)\n\t{\n#if defined(_WIN32) && defined(HAVE_PACKET)\n\t\tint\t\tr;\n#ifdef HAVE_IPV6\n\t\tdllfunction_t fncs[] =\n\t\t{\n\t\t\t{(void**)&pgetaddrinfo, \"getaddrinfo\"},\n\t\t\t{(void**)&pfreeaddrinfo, \"freeaddrinfo\"},\n\t\t\t{(void**)&pgetnameinfo, \"getnameinfo\"},\n\t\t\t{NULL, NULL}\n\t\t};\n\t\tSys_LoadLibrary(\"ws2_32.dll\", fncs);\n#endif\n\n\t\tr = WSAStartup (MAKEWORD(2, 2), &winsockdata);\n\n\t\tif (r)\n\t\t\tSys_Error (\"Winsock initialization failed.\");\n#endif\n\t}\n\n\tCvar_Register(&net_ice_broker, \"networking\");\n\tCvar_Register(&timeout, \"networking\");\n\tCvar_Register(&net_hybriddualstack, \"networking\");\n\tCvar_Register(&net_fakeloss, \"networking\");\n\tCvar_Register(&net_fakemtu, \"networking\");\n\n#if defined(HAVE_SSL)\n#ifdef _DEBUG\n\tCmd_AddCommand(\"tls_provider_test\", TLS_Provider_Test_f);\n#endif\n\tCvar_Register(&tls_provider, \"networking\");\n\tCvar_Register(&tls_ignorecertificateerrors, \"networking\");\n#endif\n#if defined(TCPCONNECT) && (defined(HAVE_SERVER) || defined(HAVE_HTTPSV))\n#ifdef HAVE_SERVER\n\tCvar_Register(&net_enable_qizmo, \"networking\");\n#endif\n#ifdef MVD_RECORDING\n\tCvar_Register(&net_enable_qtv, \"networking\");\n#endif\n#if defined(HAVE_SSL)\n\tCvar_Register(&net_enable_tls, \"networking\");\n#endif\n#ifdef HAVE_HTTPSV\n\tCvar_Register(&net_enable_http, \"networking\");\n\tCvar_Register(&net_enable_websockets, \"networking\");\n\tCvar_Register(&net_enable_rtcbroker, \"networking\");\n#endif\n#endif\n#ifdef HAVE_DTLS\n\tCvar_Register(&net_enable_dtls, \"networking\");\n\tCvar_ForceCallback(&net_enable_dtls);\n#endif\n#if defined(HAVE_SERVER) && defined(KEXLOBBY)\n\tCvar_Register(&net_enable_kexlobby, \"networking\");\n#endif\n\n\n#ifdef HAVE_SERVER\n\tCmd_AddCommand(\"sv_addport\", SVNET_AddPort_f);\n#endif\n#ifdef HAVE_CLIENT\n\tCvar_Register(&cl_delay_packets, \"networking\");\n\tCmd_AddCommand(\"cl_addport\", NET_ClientPort_f);\n#endif\n\n//\tCvar_Register (&net_upnpigp, \"networking\");\n//\tnet_upnpigp.restriction = RESTRICT_MAX;\n\n\t//\n\t// init the message buffer\n\t//\n\tnet_message.maxsize = sizeof(net_message_buffer);\n\tnet_message.data = net_message_buffer;\n\n#if defined(HAVE_WINSSPI)\n\tSSL_Init();\n#endif\n\n#ifdef SUPPORT_ICE\n\tICE_Init();\n#endif\n\n#if defined(HAVE_CLIENT)||defined(HAVE_SERVER)\n\tNet_Master_Init();\n#endif\n\n#if defined(SUBSERVERS) && defined(HAVE_SERVER)\n\tif (isDedicated && !SSV_IsSubServer())\n\t{\t//-clusterhost address:port password\n\t\t//connects this server to a remote control/gateway server.\n\t\tint i = COM_CheckParm(\"-clusterhost\");\n\t\tif (i && i+2 < com_argc)\n\t\t{\n\t\t\tvfsfile_t *f = FS_OpenTCP(com_argv[i+1], PORT_DEFAULTSERVER, true);\n\t\t\tif (!f)\n\t\t\t\tSys_Error(\"Unable to resolve/connect to cluster host address \\\"%s\\\"\\n\", com_argv[i+1]);\n\t\t\tVFS_PRINTF(f, \"NODE\\r\\nPassword: \\\"%s\\\"\\r\\n\\r\\n\", com_argv[i+2]);\n\t\t\tSSV_SetupControlPipe(f, true);\n\t\t\tCon_Printf(\"Connecting to remote node...\\n\");\n\t\t\tCon_Printf(CON_WARNING\"This gives rcon access to the remote node!\\n\");\n\t\t\treturn;\n\t\t}\n\t}\n#endif\n}\n#ifdef HAVE_CLIENT\nvoid NET_CloseClient(void)\n{\t//called by disconnect console command\n\tFTENET_CloseCollection(cls.sockets);\n\tcls.sockets = NULL;\n}\nvoid NET_InitClient(qboolean loopbackonly)\n{\n\tconst char *port;\n\tint p;\n\n#ifdef QUAKESPYAPI\n\tport = STRINGIFY(PORT_QWCLIENT);\n#else\n\tport = \"0\";\n#endif\n\n\tp = COM_CheckParm (\"-clport\");\n\tif (p && p < com_argc)\n\t{\n\t\tport = com_argv[p+1];\n\t}\n\n\tif (!cls.sockets)\n\t\tcls.sockets = FTENET_CreateCollection(false, CL_ReadPacket);\n#ifdef HAVE_SERVER\n\tFTENET_AddToCollection(cls.sockets, \"CLLoopback\", \"1\", NA_LOOPBACK, NP_DGRAM);\n#endif\n\tif (loopbackonly)\n\t\tport = \"\";\n#if defined(HAVE_IPV4) && defined(HAVE_IPV6)\n\tif (net_hybriddualstack.ival)\n\t{\n\t\tFTENET_AddToCollection(cls.sockets, \"CLUDP\", port, NA_IP, NP_DGRAM);\n\t}\n\telse\n#endif\n\t{\n\t\t#ifdef HAVE_IPV4\n\t\t\tFTENET_AddToCollection(cls.sockets, \"CLUDP4\", port, NA_IP, NP_DGRAM);\n\t\t#endif\n\t\t#ifdef HAVE_IPV6\n\t\t\tFTENET_AddToCollection(cls.sockets, \"CLUDP6\", port, NA_IPV6, NP_DGRAM);\n\t\t#endif\n\t}\n#ifdef HAVE_IPX\n\tFTENET_AddToCollection(cls.sockets, \"CLIPX\", port, NA_IPX, NP_DGRAM);\n#endif\n\n\t//\tCon_TPrintf(\"Client port Initialized\\n\");\n}\n#endif\n\n#ifdef HAVE_SERVER\n#ifdef HAVE_IPV4\nstatic void QDECL SV_Tcpport_Callback(struct cvar_s *var, char *oldvalue)\n{\n\tif (!strcmp(var->string, \"0\"))\t//qtv_streamport had an old default value of 0. make sure we don't end up listening on random ports.\n\t\tFTENET_AddToCollection(svs.sockets, var->name, \"\", NA_IP, NP_STREAM);\n\telse\n\t\tFTENET_AddToCollection(svs.sockets, var->name, var->string, NA_IP, NP_STREAM);\n}\ncvar_t\tsv_port_tcp = CVARFCD(\"sv_port_tcp\", \"\", CVAR_SERVERINFO, SV_Tcpport_Callback, \"Port number to list on for inbound tcp-based connections (including tls variants). Can be a list for multiple ports. If ips are included then binds to that specific interface. Whether any specific protocol is accepted depends upon other settings.\");\n#ifdef HAVE_LEGACY\ncvar_t\tqtv_streamport\t= CVARAFCD(\t\"qtv_streamport\", \"\",\n\t\t\t\t\t\t\t\t\t\"mvd_streamport\", 0, SV_Tcpport_Callback, \"Legacy cvar. Use sv_port_tcp instead.\");\n#endif\n#endif\n#ifdef HAVE_IPV6\nstatic void QDECL SV_Tcpport6_Callback(struct cvar_s *var, char *oldvalue)\n{\n\tFTENET_AddToCollection(svs.sockets, var->name, var->string, NA_IPV6, NP_STREAM);\n}\ncvar_t\tsv_port_tcp6 = CVARCD(\"sv_port_tcp6\", \"\", SV_Tcpport6_Callback, \"Equivelent to sv_port_tcp, but port numbers are assumed to be ipv6 only. relevant only when not using hybrid sockets.\");\n#endif\n#ifdef HAVE_IPV4\nstatic void QDECL SV_Port_Callback(struct cvar_s *var, char *oldvalue)\n{\n\tFTENET_AddToCollection(svs.sockets, var->name, var->string, NA_IP, NP_DGRAM);\n}\ncvar_t  sv_port_ipv4 = CVARCD(\"sv_port\", STRINGIFY(PORT_DEFAULTSERVER), SV_Port_Callback, \"Port number to list on for inbound udp-based connections (including dtls variants). Can be a list for multiple ports. If ips are included then binds to that specific interface. Whether any specific protocol is accepted depends upon other settings.\");\n#endif\n#ifdef HAVE_IPV6\nstatic void QDECL SV_PortIPv6_Callback(struct cvar_s *var, char *oldvalue)\n{\n\tFTENET_AddToCollection(svs.sockets, var->name, var->string, NA_IPV6, NP_DGRAM);\n}\ncvar_t  sv_port_ipv6 = CVARCD(\"sv_port_ipv6\", \"\", SV_PortIPv6_Callback, \"Port to use for incoming ipv6 udp connections. Due to hybrid sockets this might not be needed. You can specify an ipv4 address:port for a second ipv4 port if you want.\");\n#endif\n#ifdef HAVE_IPX\nvoid QDECL SV_PortIPX_Callback(struct cvar_s *var, char *oldvalue)\n{\n\tFTENET_AddToCollection(svs.sockets, var->name, var->string, NA_IPX, NP_DGRAM);\n}\ncvar_t  sv_port_ipx = CVARCD(\"sv_port_ipx\", \"\", SV_PortIPX_Callback, \"If you need this cvar then you're doing something weird/ancient.\");\n#endif\n#ifdef UNIXSOCKETS\nvoid QDECL SV_PortUNIX_Callback(struct cvar_s *var, char *oldvalue)\n{\n\tFTENET_AddToCollection(svs.sockets, var->name, var->string, NA_UNIX, NP_DGRAM);\n}\n#ifdef __linux\t//linux adds abstract sockets, which require no filesystem cleanup.\ncvar_t  sv_port_unix = CVARC(\"sv_port_unix\", \"@qsock.fte\", SV_PortUNIX_Callback);\n#else\ncvar_t  sv_port_unix = CVARC(\"sv_port_unix\", \"/tmp/qsock.fte\", SV_PortUNIX_Callback);\t//should probably use $XDG_RUNTIME_DIR\n#endif\n#endif\n#ifdef HAVE_NATPMP\nstatic void QDECL SV_Port_NatPMP_Callback(struct cvar_s *var, char *oldvalue)\n{\n\tFTENET_AddToCollection(svs.sockets, var->name, va(\"natpmp://%s\", var->string), NA_IP, NP_NATPMP);\n}\n#if 1//def SERVERONLY\n#define NATPMP_DEFAULT_PORT \"\"\t\t//don't fuck with dedicated servers\n#else\n#define NATPMP_DEFAULT_PORT \"5351\"\t//home users, yay, lucky people.\n#endif\ncvar_t sv_port_natpmp = CVARCD(\"sv_port_natpmp\", NATPMP_DEFAULT_PORT, SV_Port_NatPMP_Callback, \"If set (typically to 5351), automatically configures your router's port forwarding. You can instead specify the full ip address of your router (192.168.1.1:5351 for example). Your router must have NAT-PMP supported and enabled.\");\n#endif\n\n#ifdef FTE_TARGET_WEB\nvoid QDECL SV_PortRTC_Callback(struct cvar_s *var, char *oldvalue)\n{\n\tFTENET_AddToCollection(svs.sockets, var->name, var->string, NA_WEBSOCKET, NP_DTLS);\n}\nstatic cvar_t  sv_port_rtc = CVARCD(\"sv_port_rtc\", \"/\", SV_PortRTC_Callback, \"This specifies the broker url to use to obtain clients from. If the hostname is ommitted, it'll come from the manifest. If omitted, the broker service will randomize the resource part, so be sure to tell your friends the path reported by eg status rather than just this cvar value. Or just set it to 'rtc:///example' and tell clients to connect to the same sservevalue.\");\n#elif defined(SUPPORT_ICE)\nvoid QDECL SV_PortRTC_Callback(struct cvar_s *var, char *oldvalue)\n{\n\tconst char *s = var->string;\n\tif (*s == '/')\n\t\t;\n\telse if (*var->string)\n\t\ts = va(\"/%s\", var->string);\n\tFTENET_AddToCollection(svs.sockets, var->name, s, NA_INVALID, NP_RTC_TLS);\n}\nstatic cvar_t  sv_port_rtc = CVARCD(\"sv_port_rtc\", \"\", SV_PortRTC_Callback, \"This specifies the broker url to use to obtain clients from. If the hostname is ommitted, it'll come from the manifest. If omitted, the broker service will randomize the resource part, so be sure to tell your friends the path reported by eg status rather than just this cvar value. Or just set it to 'rtc:///example' and tell clients to connect to the same sservevalue.\");\n#endif\n\nvoid SVNET_RegisterCvars(void)\n{\n#ifdef FTE_TARGET_WEB\n\tCvar_Register (&net_ice_relayonly,\t\"networking\");\n\tCvar_Register (&net_ice_servers,\t\"networking\");\n\tCvar_Register (&sv_port_rtc,\t\"networking\");\n//\tsv_port_rtc.restriction = RESTRICT_MAX;\n#elif defined(SUPPORT_ICE)\n\tCvar_Register (&sv_port_rtc,\t\"networking\");\n#endif\n#if defined(TCPCONNECT) && defined(HAVE_IPV4)\n\tCvar_Register (&sv_port_tcp,\t\"networking\");\n\tsv_port_tcp.restriction = RESTRICT_MAX;\n#ifdef HAVE_LEGACY\n\tCvar_Register (&qtv_streamport,\t\"networking\");\n\tqtv_streamport.restriction = RESTRICT_MAX;\n#endif\n#endif\n#if defined(TCPCONNECT) && defined(HAVE_IPV6)\n\tCvar_Register (&sv_port_tcp6,\t\"networking\");\n\tsv_port_tcp6.restriction = RESTRICT_MAX;\n#endif\n#ifdef HAVE_IPV6\n\tCvar_Register (&sv_port_ipv6,\t\"networking\");\n\tsv_port_ipv6.restriction = RESTRICT_MAX;\n#endif\n#ifdef HAVE_IPX\n\tCvar_Register (&sv_port_ipx,\t\"networking\");\n\tsv_port_ipx.restriction = RESTRICT_MAX;\n#endif\n#ifdef HAVE_IPV4\n\tCvar_Register (&sv_port_ipv4,\t\"networking\");\n\tsv_port_ipv4.restriction = RESTRICT_MAX;\n#endif\n#ifdef HAVE_NATPMP\n\tCvar_Register (&sv_port_natpmp,\t\"networking\");\n\tsv_port_natpmp.restriction = RESTRICT_MAX;\n#endif\n#ifdef UNIXSOCKETS\n//\tCvar_Register (&sv_port_unix,\t\"networking\");\n#endif\n\n#ifdef HAVE_DTLS\n\tCvar_Register (&dtls_psk_hint,\t\t\t\"networking\");\n\tCvar_Register (&dtls_psk_user,\t\t\t\"networking\");\n\tCvar_Register (&dtls_psk_key,\t\t\t\"networking\");\n#endif\n}\n\nvoid NET_CloseServer(void)\n{\n\tFTENET_CloseCollection(svs.sockets);\n\tsvs.sockets = NULL;\n}\n\nvoid NET_InitServer(void)\n{\n\tqboolean singleplayer = (sv.allocated_client_slots == 1) && !isDedicated;\n\n#ifdef HAVE_EPOLL\n\tif (epoll_fd < 0)\n\t\tepoll_fd = epoll_create1(EPOLL_CLOEXEC);\n#endif\n\n\tif ((sv_listen_nq.value || sv_listen_dp.value || sv_listen_qw.value\n#ifdef QWOVERQ3\n\t\t|| sv_listen_q3.ival\n#endif\n\t\t) && !singleplayer)\n\t{\n\t\tif (!svs.sockets)\n\t\t{\n\t\t\tsvs.sockets = FTENET_CreateCollection(true, SV_ReadPacket);\n#ifdef HAVE_CLIENT\n\t\t\tFTENET_AddToCollection(svs.sockets, \"SVLoopback\", STRINGIFY(PORT_DEFAULTSERVER), NA_LOOPBACK, NP_DGRAM);\n#endif\n\t\t}\n\n#if defined(SUPPORT_ICE) || defined(FTE_TARGET_WEB)\n\t\tCvar_ForceCallback(&sv_public);\n\t\tCvar_ForceCallback(&sv_port_rtc);\n#endif\n#ifdef HAVE_IPV4\n\t\tCvar_ForceCallback(&sv_port_ipv4);\n#endif\n#ifdef HAVE_IPV6\n\t\tCvar_ForceCallback(&sv_port_ipv6);\n#endif\n#ifdef HAVE_IPX\n\t\tCvar_ForceCallback(&sv_port_ipx);\n#endif\n#if defined(TCPCONNECT) && defined(HAVE_TCP)\n\t\tCvar_ForceCallback(&sv_port_tcp);\n#ifdef HAVE_LEGACY\n\t\tCvar_ForceCallback(&qtv_streamport);\n#endif\n#ifdef HAVE_IPV6\n\t\tCvar_ForceCallback(&sv_port_tcp6);\n#endif\n#endif\n#ifdef HAVE_NATPMP\n\t\tCvar_ForceCallback(&sv_port_natpmp);\n#endif\n#ifdef UNIXSOCKETS\n//\t\tCvar_ForceCallback(&sv_port_unix);\n#endif\n#ifdef HAVE_DTLS\n\t\tCvar_ForceCallback(&net_enable_dtls);\n#endif\n\t}\n\telse\n\t{\n\t\tNET_CloseServer();\n\n#ifdef HAVE_CLIENT\n\t\tsvs.sockets = FTENET_CreateCollection(true, SV_ReadPacket);\n\t\tFTENET_AddToCollection(svs.sockets, \"SVLoopback\", STRINGIFY(PORT_DEFAULTSERVER), NA_LOOPBACK, NP_DGRAM);\n#endif\n\t}\n}\n#endif\n\nvoid NET_Tick(void)\n{\n#ifdef SUPPORT_ICE\n\tICE_Tick();\n#endif\n}\n/*\n====================\nNET_Shutdown\n====================\n*/\nvoid\tNET_Shutdown (void)\n{\n#ifdef HAVE_SERVER\n\tNET_CloseServer();\n#endif\n#ifdef HAVE_CLIENT\n\tFTENET_CloseCollection(cls.sockets);\n\tcls.sockets = NULL;\n#endif\n\n\n#ifdef HAVE_EPOLL\n\tif (epoll_fd >= 0)\n\t\tepoll_close(epoll_fd);\n\tepoll_fd = -1;\n\tstdin_epolling = false;\n#endif\n\n\n#if defined(_WIN32) && defined(HAVE_PACKET)\n#ifdef SERVERTONLY\n\tif (!serverthreadID)\t//running as subsystem of client. Don't close all of it's sockets too.\n#endif\n\t\tWSACleanup ();\n#endif\n}\n\n\n\n\n\n\n#ifdef HAVE_TCP\n#ifdef HAVE_EPOLL\n#include <poll.h>\n#endif\nstatic int VFSTCP_IsStillConnecting(SOCKET sock)\n{\n#ifdef HAVE_EPOLL\n\t//poll has no arbitrary fd limit. use it instead of select where possible.\n\tstruct pollfd ourfd[1];\n\tourfd[0].fd = sock;\n\tourfd[0].events = POLLOUT;\n\tourfd[0].revents = 0;\n\tif (!poll(ourfd, countof(ourfd), 0))\n\t{\n\t\tif (ourfd[0].revents & POLLERR)\n\t\t\treturn VFS_ERROR_UNSPECIFIED;\n\t\tif (ourfd[0].revents & POLLHUP)\n\t\t\treturn VFS_ERROR_REFUSED;\n\t\treturn true;\t//no events yet.\n\t}\n#else\n\t//okay on windows where sock+1 is ignored, has issues when lots of other fds are already open (for any reason).\n\tfd_set fdw, fdx;\n\tstruct timeval timeout;\n\ttimeout.tv_sec = 0;\n\ttimeout.tv_usec = 0;\n\tFD_ZERO(&fdw);\n\tFD_SET(sock, &fdw);\n\tFD_ZERO(&fdx);\n\tFD_SET(sock, &fdx);\n\t//check if we can actually write to it yet, without generating weird errors...\n\tif (!select((int)sock+1, NULL, &fdw, &fdx, &timeout))\n\t\treturn true;\n#endif\n\n\t//if we get here then its writable(read: connected) or failed.\n\n//\tint error = NET_ENOTCONN;\n//\tsocklen_t sz = sizeof(error);\n//\tif (getsockopt(sock, SOL_SOCKET, SO_ERROR, &error, &sz))\n//\t\terror = NET_ENOTCONN;\n\treturn false;\n}\ntypedef struct {\n\tvfsfile_t funcs;\n\n\tSOCKET sock;\n\tqboolean conpending;\n\tint readaborted;\t//some kind of error. don't spam\n\tint writeaborted;\t//some kind of error. don't spam\n\n\tchar readbuffer[65536];\n\tint readbuffered;\n\tchar peer[1];\n} tcpfile_t;\nint QDECL VFSTCP_ReadBytes (struct vfsfile_s *file, void *buffer, int bytestoread)\n{\n\ttcpfile_t *tf = (tcpfile_t*)file;\n\tint len;\n\tint trying;\n\n\tif (tf->conpending)\n\t{\n\t\ttrying = VFSTCP_IsStillConnecting(tf->sock);\n\t\tif (trying < 0)\n\t\t{\n\t\t\ttf->readaborted = trying;\n\t\t\ttf->writeaborted = true;\n\t\t}\n\t\telse if (trying)\n\t\t\treturn 0;\n\t\ttf->conpending = false;\n\t}\n\n\tif (!tf->readaborted)\n\t{\n\t\ttrying = sizeof(tf->readbuffer) - tf->readbuffered;\n\t\tif (bytestoread > 1500)\n\t\t{\n\t\t\tif (trying > bytestoread)\n\t\t\t\ttrying = bytestoread;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (trying > 1500)\n\t\t\t\ttrying = 1500;\n\t\t}\n\t\tlen = recv(tf->sock, tf->readbuffer + tf->readbuffered, trying, 0);\n\t\tif (len == -1)\n\t\t{\n\t\t\tint e = neterrno();\n\t\t\tif (e != NET_EWOULDBLOCK && e != NET_EINTR)\n\t\t\t{\n\t\t\t\ttf->readaborted = VFS_ERROR_UNSPECIFIED;\n\t\t\t\tswitch(e)\n\t\t\t\t{\n\t\t\t\tcase NET_ENOTCONN:\n\t\t\t\t\tCon_Printf(\"connection to \\\"%s\\\" failed\\n\", tf->peer);\n\t\t\t\t\ttf->readaborted = VFS_ERROR_NORESPONSE;\n\t\t\t\t\ttf->writeaborted = true;\n\t\t\t\t\tbreak;\n\t\t\t\tcase NET_ECONNABORTED:\n\t\t\t\t\tCon_DPrintf(\"connection to \\\"%s\\\" aborted\\n\", tf->peer);\n\t\t\t\t\ttf->readaborted = VFS_ERROR_NORESPONSE;\n\t\t\t\t\ttf->writeaborted = true;\n\t\t\t\t\tbreak;\n\t\t\t\tcase NET_ETIMEDOUT:\n\t\t\t\t\tCon_Printf(\"connection to \\\"%s\\\" timed out\\n\", tf->peer);\n\t\t\t\t\ttf->readaborted = VFS_ERROR_NORESPONSE;\n\t\t\t\t\ttf->writeaborted = true;\n\t\t\t\t\tbreak;\n\t\t\t\tcase NET_ECONNREFUSED:\n\t\t\t\t\tCon_DPrintf(\"connection to \\\"%s\\\" refused\\n\", tf->peer);\n\t\t\t\t\ttf->readaborted = VFS_ERROR_REFUSED;\n\t\t\t\t\ttf->writeaborted = true;\n\t\t\t\t\tbreak;\n\t\t\t\tcase NET_ECONNRESET:\n\t\t\t\t\tCon_DPrintf(\"connection to \\\"%s\\\" reset\\n\", tf->peer);\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tCon_Printf(\"tcp socket error %i (%s)\\n\", e, tf->peer);\n\t\t\t\t}\n\t\t\t}\n\t\t\t//fixme: figure out wouldblock or error\n\t\t}\n\t\telse if (len == 0 && trying != 0)\n\t\t{\n\t\t\t//peer disconnected\n\t\t\ttf->readaborted = VFS_ERROR_EOF;\n\t\t}\n\t\telse\n\t\t{\n\t\t\ttf->readbuffered += len;\n\t\t}\n\t}\n\n\t//return a partially filled buffer.\n\tif (bytestoread > tf->readbuffered)\n\t\tbytestoread = tf->readbuffered;\n\tif (bytestoread < 0)\n\t\treturn VFS_ERROR_UNSPECIFIED;\t//caller error...\n\n\tif (bytestoread > 0)\n\t{\n\t\tmemcpy(buffer, tf->readbuffer, bytestoread);\n\t\ttf->readbuffered -= bytestoread;\n\t\tmemmove(tf->readbuffer, tf->readbuffer+bytestoread, tf->readbuffered);\n\t\treturn bytestoread;\n\t}\n\telse return tf->readaborted;\n}\nint QDECL VFSTCP_WriteBytes (struct vfsfile_s *file, const void *buffer, int bytestoread)\n{\n\ttcpfile_t *tf = (tcpfile_t*)file;\n\tint len;\n\n\tif (tf->writeaborted)\n\t\treturn VFS_ERROR_UNSPECIFIED;\t//a previous write failed.\n\n\tif (tf->conpending)\n\t{\n\t\tlen = VFSTCP_IsStillConnecting(tf->sock);\n\t\tif (len < 0)\n\t\t{\n\t\t\ttf->writeaborted = true;\n\t\t\ttf->conpending = false;\n\t\t\treturn len;\n\t\t}\n\t\tif (len)\n\t\t\treturn 0;\n\t\ttf->conpending = false;\n\t}\n\n\tlen = send(tf->sock, buffer, bytestoread, MSG_NOSIGNAL);\n\tif (len == -1 || len == 0)\n\t{\n\t\tint reason = VFS_ERROR_UNSPECIFIED;\n\t\tint e = (len==0)?NET_ECONNABORTED:neterrno();\n\t\tswitch(e)\n\t\t{\n\t\tcase NET_EINTR:\n\t\tcase NET_EWOULDBLOCK:\n\t\t\treturn 0;\t//nothing available yet.\n\t\tcase NET_ETIMEDOUT:\n\t\t\tCon_Printf(\"connection to \\\"%s\\\" timed out\\n\", tf->peer);\n\t\t\ttf->writeaborted = true;\n\t\t\ttf->conpending = false;\n\t\t\treturn VFS_ERROR_NORESPONSE;\t//don't bother trying to read if we never connected.\n\t\tcase NET_ECONNREFUSED:\t//peer sent a reset instead of accepting a new connection\n\t\t\tCon_DPrintf(\"connection to \\\"%s\\\" refused\\n\", tf->peer);\n\t\t\ttf->writeaborted = true;\n\t\t\ttf->conpending = false;\n\t\t\treturn VFS_ERROR_REFUSED;\t//don't bother trying to read if we never connected.\n\t\tcase NET_ECONNABORTED:\t//peer closed its socket\n\t\t\tCon_Printf(\"connection to \\\"%s\\\" aborted\\n\", tf->peer);\n\t\t\treason = len?VFS_ERROR_NORESPONSE:VFS_ERROR_EOF;\n\t\t\tbreak;\n\t\tcase NET_ECONNRESET:\t//'peer' claims no knowledge (rebooted?) or forcefully closed\n\t\t\tCon_DPrintf(\"connection to \\\"%s\\\" reset\\n\", tf->peer);\n\t\t\treason = VFS_ERROR_EOF;\n\t\t\tbreak;\n\t\tcase NET_ENOTCONN:\n#ifdef __unix__\n\t\tcase EPIPE:\n#endif\n\t\t\tCon_Printf(\"connection to \\\"%s\\\" failed\\n\", tf->peer);\n\t\t\ttf->writeaborted = true;\n\t\t\ttf->conpending = false;\n\t\t\treturn VFS_ERROR_NORESPONSE;\t//don't bother trying to read if we never connected.\n\t\tdefault:\n\t\t\tSys_Printf(\"tcp socket error %i (%s)\\n\", e, tf->peer);\n\t\t\tbreak;\n\t\t}\n//\t\tdon't destroy it on write errors, because that prevents us from reading anything that was sent to us afterwards.\n//\t\tinstead let the read handling kill it if there's nothing new to be read\n\t\tVFSTCP_ReadBytes(file, NULL, 0);\n\t\ttf->writeaborted = true;\n\t\treturn reason;\n\t}\n\treturn len;\n}\nqboolean QDECL VFSTCP_Seek (struct vfsfile_s *file, qofs_t pos)\n{\n\treturn false;\n}\nstatic qofs_t QDECL VFSTCP_Tell (struct vfsfile_s *file)\n{\n\treturn 0;\n}\nstatic qofs_t QDECL VFSTCP_GetLen (struct vfsfile_s *file)\n{\n\treturn 0;\n}\nstatic qboolean QDECL VFSTCP_Close (struct vfsfile_s *file)\n{\n\ttcpfile_t *f = (tcpfile_t *)file;\n\tqboolean success = f->sock != INVALID_SOCKET;\n\tif (f->sock != INVALID_SOCKET)\n\t{\n\t\tclosesocket(f->sock);\n\t\tf->sock = INVALID_SOCKET;\n\t}\n\tZ_Free(f);\n\treturn success;\n}\n\nvfsfile_t *FS_WrapTCPSocket(SOCKET sock, qboolean conpending, const char *peername)\n{\n\ttcpfile_t *newf;\n\tif (sock == INVALID_SOCKET)\n\t\treturn NULL;\n\n\tnewf = Z_Malloc(sizeof(*newf) + strlen(peername));\n\tstrcpy(newf->peer, peername);\n\tnewf->conpending = conpending;\n\tnewf->sock = sock;\n\tnewf->funcs.Close = VFSTCP_Close;\n\tnewf->funcs.Flush = NULL;\n\tnewf->funcs.GetLen = VFSTCP_GetLen;\n\tnewf->funcs.ReadBytes = VFSTCP_ReadBytes;\n\tnewf->funcs.Seek = VFSTCP_Seek;\n\tnewf->funcs.Tell = VFSTCP_Tell;\n\tnewf->funcs.WriteBytes = VFSTCP_WriteBytes;\n\tnewf->funcs.seekstyle = SS_UNSEEKABLE;\n\n\treturn &newf->funcs;\n}\n\ntypedef struct {\n\tvfsfile_t funcs;\n\n\tvfsfile_t *stream;\n\tint conpending;\t\t\t//waiting for the proper handshake response, don't write past this.\n\tunsigned int mask;\t\t//xor masking, to make it harder to exploit buggy shit that's parsing streams (like magic packets or w/e).\n\n\tchar readbuffer[65536];\n\tint readbufferofs;\n\tint readbuffered;\n\n\tchar *pending;\n\tint pendingofs;\n\tint pendingsize;\n\tint pendingmax;\n\n\tint err;\n} websocketfile_t;\nstatic void VFSWS_Flush (websocketfile_t *f)\n{\n//try flushing it now. note: tls packet sizes can leak.\n\tint i = f->conpending?f->conpending:f->pendingsize;\n\tif (i == f->pendingofs)\n\t\treturn;\t//nothing to flush.\n\ti = VFS_WRITE(f->stream, f->pending+f->pendingofs, i-f->pendingofs);\n\tif (i > 0)\n\t\tf->pendingofs += i;\n\telse if (i < 0)\n\t{\n\t\tf->err = i;\n\t\tVFS_CLOSE(f->stream);\t//close it.\n\t\tf->stream = NULL;\n\t}\n}\nstatic void VFSWS_Append (websocketfile_t *f, unsigned packettype, const unsigned char *data, size_t length)\n{\n\tunion\n\t{\n\t\tunsigned char b[4];\n\t\tint i;\n\t} mask;\n\tunsigned short ctrl = 0x8000 | (packettype<<8);\n\tquint64_t paylen = 0;\n\tunsigned int payoffs = f->pendingsize;\n//\tint i;\n\tif (!f->stream)\n\t\treturn;\t//can't do anything anyway...\n\tswitch((ctrl>>8) & 0xf)\n\t{\n\t/*case WS_PACKETTYPE_TEXTFRAME:\n\t\tfor (i = 0; i < length; i++)\n\t\t{\n\t\t\tpaylen += (data[i] == 0 || data[i] >= 0x80)?2:1;\n\t\t}\n\t\tbreak;*/\n\tdefault:\n\t\tpaylen = length;\n\t\tbreak;\n\t}\n\tpayoffs = 2;\t//ctrl header\n\tif (paylen >= (1<<16))\n\t\tctrl |= 127, payoffs+=8;\t//64bit len... overkill\n\telse if (paylen >= 126)\n\t\tctrl |= 126, payoffs+=2;\t//16bit len.\n\telse\n\t\tctrl |= paylen;\t//smol\n\tif (ctrl&0x80)\n\t\tpayoffs += 4;\t//mask\n\tpayoffs += paylen;\n\n\tif (f->pendingmax < f->pendingsize+payoffs)\n\t{\t//oh noes. wouldn't be space\n\t\tif (f->pendingofs && !f->conpending/*don't get confused*/)\n\t\t{\t//move it down, we already sent that bit.\n\t\t\tf->pendingsize -= f->pendingofs;\n\t\t\tmemmove(f->pending, f->pending + f->pendingofs, f->pendingsize);\n\t\t\tf->pendingofs = 0;\n\t\t}\n\t\tif (f->pendingmax < f->pendingsize + payoffs)\n\t\t{\t//still too big. make the buffer bigger.\n\t\t\tf->pendingmax = f->pendingsize + payoffs;\n\t\t\tf->pending = realloc(f->pending, f->pendingmax);\n\t\t}\n\t}\n\n\tpayoffs = f->pendingsize;\n\tf->pending[payoffs++] = ctrl>>8;\n\tf->pending[payoffs++] = ctrl&0xff;\n\tif ((ctrl&0x7f) == 127)\n\t{\n\t\tf->pending[payoffs++] = (paylen>>56)&0xff;\n\t\tf->pending[payoffs++] = (paylen>>48)&0xff;\n\t\tf->pending[payoffs++] = (paylen>>40)&0xff;\n\t\tf->pending[payoffs++] = (paylen>>32)&0xff;\n\t\tf->pending[payoffs++] = (paylen>>24)&0xff;\n\t\tf->pending[payoffs++] = (paylen>>16)&0xff;\n\t\tf->pending[payoffs++] = (paylen>> 8)&0xff;\n\t\tf->pending[payoffs++] = (paylen>> 0)&0xff;\n\t}\n\telse if ((ctrl&0x7f) == 126)\n\t{\n\t\tf->pending[payoffs++] = (paylen>>8)&0xff;\n\t\tf->pending[payoffs++] = (paylen>>0)&0xff;\n\t}\n\tif (ctrl&0x80)\n\t{\n\t\tmask.i = f->mask;\n\t\t//'re-randomise' it a bit\n\t\tf->mask = (f->mask<<4) | (f->mask>>(32-4));\n\t\tf->mask += (payoffs<<16) + paylen;\n\n\t\tf->pending[payoffs++] = mask.b[0];\n\t\tf->pending[payoffs++] = mask.b[1];\n\t\tf->pending[payoffs++] = mask.b[2];\n\t\tf->pending[payoffs++] = mask.b[3];\n\t}\n\tswitch((ctrl>>8) & 0xf)\n\t{\n#if 0\n\tcase WS_PACKETTYPE_TEXTFRAME:/*utf8ify the data*/\n\t\tfor (i = 0; i < length; i++)\n\t\t{\n\t\t\tif (!data[i])\n\t\t\t{\t/*0 is encoded as 0x100 to avoid safety checks*/\n\t\t\t\tf->pending[payoffs++] = 0xc0 | (0x100>>6);\n\t\t\t\tf->pending[payoffs++] = 0x80 | (0x100&0x3f);\n\t\t\t}\n\t\t\telse if (data[i] >= 0x80)\n\t\t\t{\t/*larger bytes require markup*/\n\t\t\t\tf->pending[payoffs++] = 0xc0 | (data[i]>>6);\n\t\t\t\tf->pending[payoffs++] = 0x80 | (data[i]&0x3f);\n\t\t\t}\n\t\t\telse\n\t\t\t{\t/*lower 7 bits are as-is*/\n\t\t\t\tf->pending[payoffs++] = data[i];\n\t\t\t}\n\t\t}\n\t\tbreak;\n#endif\n\tdefault: //raw data\n\t\tmemcpy(f->pending+payoffs, data, length);\n\t\tpayoffs += length;\n\t\tbreak;\n\t}\n\tif (ctrl&0x80)\n\t{\n\t\tunsigned char *buf = f->pending+payoffs-paylen;\n\t\tint i;\n\t\tfor (i = 0; i < paylen; i++)\n\t\t\tbuf[i] ^= mask.b[i&3];\n\t}\n\tf->pendingsize = payoffs;\n\n\t//try flushing it now. note: tls packet sizes can leak.\n\tVFSWS_Flush(f);\n}\nstatic qboolean QDECL VFSWS_Close (struct vfsfile_s *file)\n{\n\twebsocketfile_t *f = (websocketfile_t *)file;\n\tqboolean success = f->stream != NULL;\n\tif (f->stream != NULL)\n\t{\t//still open? o.O\n\t\tVFSWS_Append(f, WS_PACKETTYPE_CLOSE, NULL, 0);\t//let the other side know it was intended\n\t\tVFS_WRITE(f->stream, f->pending+f->pendingofs, f->pendingsize-f->pendingofs);\t//final flush\n\t\tsuccess = VFS_CLOSE(f->stream);\t//close it.\n\t\tf->stream = NULL;\n\t}\n\tfree(f->pending);\n\tf->pending = NULL;\n\tZ_Free(f);\n\treturn success;\n}\nstatic int QDECL VFSWS_ReadBytes (struct vfsfile_s *file, void *buffer, int bytestoread)\n{\n\twebsocketfile_t *f = (websocketfile_t *)file;\n\tint r;\n\tint t;\n\n\tVFSWS_Flush(f);\t//flush any pending writes.\n\n\tfor(t = 0; t < 2; t++)\n\t{\n\t\tif (t==1 && !f->err)\n\t\t{\n\t\t\tif (f->readbufferofs >= 1024)\n\t\t\t{\n\t\t\t\tf->readbuffered -= f->readbufferofs;\n\t\t\t\tmemmove(f->readbuffer, f->readbuffer+f->readbufferofs, f->readbuffered);\n\t\t\t\tf->readbufferofs = 0;\n\t\t\t}\n\t\t\tr = f->stream?VFS_READ(f->stream, f->readbuffer+f->readbuffered, sizeof(f->readbuffer)-f->readbuffered):-1;\n\t\t\tif (r > 0)\n\t\t\t\tf->readbuffered += r;\n\t\t\tif (r < 0 && f->stream)\n\t\t\t\tf->err = r;\n\t\t\tif (r <= 0)\n\t\t\t\treturn f->err;\t//needed more, couldn't get it.\n\t\t}\n\n\t\tif (f->conpending)\n\t\t{\t//look for \\r\\n\\r\\n\n\t\t\tchar *l, *e, *le;\n\t\t\tchar *upg=NULL, *con=NULL, *accept=NULL, *prot=NULL;\n\t\t\tchar tok[128];\n\t\t\tif (!t)\n\t\t\t\tcontinue;\t//read the size\n\n\t\t\tif (f->readbuffered < 13)\n\t\t\t\tcontinue; //not nuff data for the basic header\n\t\t\tif (strncmp(f->readbuffer, \"HTTP/1.1 101 \", 13))\n\t\t\t{\n\t\t\t\tf->err = VFS_ERROR_UNSPECIFIED;\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tl = f->readbuffer;\n\t\t\te = f->readbuffer+f->readbuffered;\n\t\t\tfor(;;)\n\t\t\t{\n\t\t\t\tfor (le = l; le < e && *le != '\\n'; le++)\n\t\t\t\t\t;\n\t\t\t\tif (le == e)\n\t\t\t\t\tbreak;\t//failed.\n\t\t\t\t//track interesting lines as we parse.\n\t\t\t\tif (!strncmp(l, \"Upgrade:\", 8))\n\t\t\t\t\tupg = l+8;\n\t\t\t\telse if (!strncmp(l, \"Connection:\", 11))\n\t\t\t\t\tcon = l+11;\n\t\t\t\telse if (!strncmp(l, \"Sec-WebSocket-Accept:\", 21))\n\t\t\t\t\taccept = l+21;\n\t\t\t\telse if (!strncmp(l, \"Sec-WebSocket-Protocol:\", 23))\n\t\t\t\t\tprot = l+23;\n\t\t\t\tif (le[0] == '\\n' && le[1] == '\\r' && le[2] == '\\n')\n\t\t\t\t{\n\t\t\t\t\tle += 3;\n\n\t\t\t\t\tif (!con || !COM_ParseTokenOut(con, NULL, tok,sizeof(tok),NULL) || Q_strcasecmp(tok, \"Upgrade\"))\n\t\t\t\t\t\tf->err = VFS_ERROR_UNSPECIFIED;\t//wrong connection state...\n\t\t\t\t\telse if (!upg || !COM_ParseTokenOut(upg, NULL, tok,sizeof(tok),NULL) || Q_strcasecmp(tok, \"websocket\"))\n\t\t\t\t\t\tf->err = VFS_ERROR_UNSPECIFIED;\t//wrong type of upgrade...\n\t\t\t\t\telse if (!accept)\n\t\t\t\t\t\tf->err = VFS_ERROR_UNSPECIFIED;\t//wrong hash\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tCOM_ParseTokenOut(prot, NULL, tok,sizeof(tok),NULL);\n\t\t\t\t\t\tCon_Printf(\"websocket connection using protocol %s\\n\", prot);\n\t\t\t\t\t}\n\n\t\t\t\t\tf->conpending = false;\n\t\t\t\t\tf->readbufferofs = le-f->readbuffer;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tl = le+1;\n\t\t\t}\n\t\t\tif (f->conpending)\n\t\t\t\tcontinue;\n\t\t\tif (f->err)\n\t\t\t\tbreak;\n\t\t\t//try and read the next thing.\n\t\t\tt = 0;\n\t\t\tcontinue;\n\t\t}\n\t\telse if (f->readbuffered-f->readbufferofs >= 2)\n\t\t{\t//try to make sense of the packet..\n\t\t\tunsigned char *inbuffer = f->readbuffer + f->readbufferofs;\n\t\t\tsize_t inlen = f->readbuffered-f->readbufferofs;\n\n\t\t\tunsigned short ctrl = inbuffer[0]<<8 | inbuffer[1];\n\t\t\tunsigned long paylen;\n\t\t\tunsigned int payoffs = 2;\n\t\t\tunsigned int mask = 0;\n\n\t\t\tif (ctrl & 0x7000)\n\t\t\t{\n\t\t\t\tf->err = VFS_ERROR_UNSPECIFIED;\t//reserved bits set\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\telse if ((ctrl & 0x7f) == 127)\n\t\t\t{\n\t\t\t\tquint64_t ullpaylen;\n\t\t\t\t//as a payload is not allowed to be encoded as too large a type, and quakeworld never used packets larger than 1450 bytes anyway, this code isn't needed (65k is the max even without this)\n\t\t\t\tif (sizeof(ullpaylen) < 8)\n\t\t\t\t{\n\t\t\t\t\tf->err = VFS_ERROR_UNSPECIFIED;\t//wut...\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tif (payoffs + 8 > inlen)\n\t\t\t\t\tcontinue;\t//not enough buffered\n\t\t\t\tullpaylen =\n\t\t\t\t\t(quint64_t)inbuffer[payoffs+0]<<56u |\n\t\t\t\t\t(quint64_t)inbuffer[payoffs+1]<<48u |\n\t\t\t\t\t(quint64_t)inbuffer[payoffs+2]<<40u |\n\t\t\t\t\t(quint64_t)inbuffer[payoffs+3]<<32u |\n\t\t\t\t\t(quint64_t)inbuffer[payoffs+4]<<24u |\n\t\t\t\t\t(quint64_t)inbuffer[payoffs+5]<<16u |\n\t\t\t\t\t(quint64_t)inbuffer[payoffs+6]<< 8u |\n\t\t\t\t\t(quint64_t)inbuffer[payoffs+7]<< 0u;\n\t\t\t\tif (ullpaylen < 0x10000)\n\t\t\t\t{\n\t\t\t\t\tf->err = VFS_ERROR_UNSPECIFIED;\t//should have used a smaller encoding...\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tif (ullpaylen > 0x40000)\n\t\t\t\t{\n\t\t\t\t\tf->err = VFS_ERROR_UNSPECIFIED;\t//abusively large...\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tpaylen = ullpaylen;\n\t\t\t\tpayoffs += 8;\n\t\t\t}\n\t\t\telse if ((ctrl & 0x7f) == 126)\n\t\t\t{\n\t\t\t\tif (payoffs + 2 > inlen)\n\t\t\t\t\tcontinue;\t//not enough buffered\n\t\t\t\tpaylen =\n\t\t\t\t\tinbuffer[payoffs+0]<<8 |\n\t\t\t\t\tinbuffer[payoffs+1]<<0;\n\t\t\t\tif (paylen < 126)\n\t\t\t\t{\n\t\t\t\t\tf->err = VFS_ERROR_UNSPECIFIED;\t//should have used a smaller encoding...\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tpayoffs += 2;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tpaylen = ctrl & 0x7f;\n\t\t\t}\n\t\t\tif (ctrl & 0x80)\n\t\t\t{\n\t\t\t\tif (payoffs + 4 > inlen)\n\t\t\t\t\tcontinue;\n\t\t\t\t/*this might read data that isn't set yet, but should be safe*/\n\t\t\t\t((unsigned char*)&mask)[0] = inbuffer[payoffs+0];\n\t\t\t\t((unsigned char*)&mask)[1] = inbuffer[payoffs+1];\n\t\t\t\t((unsigned char*)&mask)[2] = inbuffer[payoffs+2];\n\t\t\t\t((unsigned char*)&mask)[3] = inbuffer[payoffs+3];\n\t\t\t\tpayoffs += 4;\n\t\t\t}\n\t\t\t/*if there isn't space, try again next time around*/\n\t\t\tif (payoffs + paylen > inlen)\n\t\t\t{\n\t\t\t\tif (payoffs + paylen >= sizeof(inbuffer)-1)\n\t\t\t\t{\n\t\t\t\t\tf->err = VFS_ERROR_UNSPECIFIED;\t//payload is too big for out in buffer\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcontinue;\t//need more data\n\t\t\t}\n\n\t\t\tif (mask)\n\t\t\t{\n\t\t\t\tint i;\n\t\t\t\tfor (i = 0; i < paylen; i++)\n\t\t\t\t{\n\t\t\t\t\tinbuffer[i + payoffs] ^= ((unsigned char*)&mask)[i&3];\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tt = 0; //allow checking for new data again.\n\t\t\tf->readbufferofs += payoffs + paylen;\t//skip to end...\n\t\t\tswitch((ctrl>>8) & 0xf)\n\t\t\t{\n\t\t\tcase WS_PACKETTYPE_CLOSE:\n\t\t\t\tif (!f->err)\n\t\t\t\t{\n\t\t\t\t\tVFSWS_Flush(f);\n\t\t\t\t\tf->err = VFS_ERROR_EOF;\n\t\t\t\t\tif (f->pendingofs < f->pendingsize)\n\t\t\t\t\t\treturn VFS_ERROR_EOF;\t//nothing more to read (might still have some to flush).\n\t\t\t\t}\n\t\t\t\tbreak;\t//will kill it.\n\t\t\tcase WS_PACKETTYPE_CONTINUATION:\n\t\t\t\tf->err = VFS_ERROR_UNSPECIFIED;\t//a prior packet lacked the 'fin' flag. we don't support fragmentation though.\n\t\t\t\tbreak;\n\t\t\tcase WS_PACKETTYPE_TEXTFRAME:\t//we don't distinguish. use utf-8 data if you wanted that.\n\t\t\tcase WS_PACKETTYPE_BINARYFRAME:\t//actual data\n\t\t\t\tif (bytestoread >= paylen)\n\t\t\t\t{\t//caller passed a big enough buffer\n\t\t\t\t\tmemcpy(buffer, f->readbuffer+f->readbufferofs-paylen, paylen);\n\t\t\t\t\treturn paylen;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tCon_Printf(\"websocket connection received %u-byte package. only %i requested\\n\", (unsigned)paylen, bytestoread);\n\t\t\t\tcontinue;\t//buffer too small... sorry\n\t\t\tcase WS_PACKETTYPE_PING:\n\t\t\t\tVFSWS_Append(f, WS_PACKETTYPE_PONG, f->readbuffer+f->readbufferofs-paylen, paylen);\t//send it back\n\t\t\t\tcontinue;\t//and look for more.\n\t\t\tcase WS_PACKETTYPE_PONG:\t//wut? we didn't ask for this\n\t\t\tdefault:\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t\tcontinue;\t//need more data\n\n\t\tbreak;\t//oops?\n\t}\n\n\tif (f->err)\n\t{\t//something bad happened\n\t\tif (f->stream)\n\t\t\tVFS_CLOSE(f->stream);\n\t\tf->stream = NULL;\n\t\treturn f->err;\n\t}\n\treturn VFS_ERROR_TRYLATER;\n}\nstatic int QDECL VFSWS_WriteBytes (struct vfsfile_s *file, const void *buffer, int bytestowrite)\n{\t//websockets are a pseudo-packet protocol, so queue one packet at a time. there may still be extra data queued at a lower level.\n\twebsocketfile_t *f = (websocketfile_t *)file;\n\tif (!f->stream)\n\t\treturn f->err;\n\tif (f->pendingsize > 8192)\n\t\treturn VFS_ERROR_TRYLATER;\t//something pending... don't queue excessively.\n\n\t//okay, we're taking this packet. all or nothing.\n\tVFSWS_Append(f, WS_PACKETTYPE_BINARYFRAME, buffer, bytestowrite);\n\treturn bytestowrite;\n}\nstatic vfsfile_t *Websocket_WrapStream(vfsfile_t *stream, const char *host, const char *resource, const char *proto)\n{\t//this is kinda messy. Websocket_WrapStream(FS_OpenSSL(FS_WrapTCPSocket(TCP_OpenStream())))... *sigh*. wss uris kinda require all the extra layers.\n\n\twebsocketfile_t *newf;\n\tchar *hello;\n\tif (!stream)\n\t\treturn NULL;\n\n\thello = va(\"GET %s HTTP/1.1\\r\\n\"\n\t\t\t\"Host: %s\\r\\n\"\n\t\t\t\"Connection: Upgrade\\r\\n\"\n\t\t\t\"Upgrade: websocket\\r\\n\"\n\t\t\t\"Sec-WebSocket-Version: 13\\r\\n\"\n\t\t\t\"Sec-WebSocket-Protocol: %s\\r\\n\"\n\t\t\t\"\\r\\n\", resource, host, proto);\n\n\tnewf = Z_Malloc(sizeof(*newf) + strlen(host));\n\tSys_RandomBytes((void*)&newf->mask, sizeof(newf->mask));\n\tnewf->stream = stream;\n\tnewf->funcs.Close = VFSWS_Close;\n\tnewf->funcs.ReadBytes = VFSWS_ReadBytes;\n\tnewf->funcs.WriteBytes = VFSWS_WriteBytes;\n\tnewf->funcs.Flush = NULL;\n\tnewf->funcs.GetLen = VFSTCP_GetLen;\n\tnewf->funcs.Seek = VFSTCP_Seek;\n\tnewf->funcs.Tell = VFSTCP_Tell;\n\tnewf->funcs.seekstyle = SS_UNSEEKABLE;\n\n\t//send the hello, the weird way.\n\tnewf->pending = strdup(hello);\n\tnewf->conpending = newf->pendingsize = newf->pendingmax = strlen(newf->pending);\n\tVFSWS_Flush(newf);\n\n\treturn &newf->funcs;\n}\n\nvfsfile_t *FS_OpenTCP(const char *name, int defaultport, qboolean assumetls/*used when no scheme specified*/)\n{\n\tnetadr_t adr = {0};\n\n\tconst char *resource = \"/\";\n\tconst char *host = name;\n\tconst char *proto = NULL;\n\tif (!strncmp(name, \"ws:\", 3))\n\t\tassumetls = false, host += 3, defaultport=defaultport?defaultport:80;\n\telse if (!strncmp(name, \"wss:\", 4))\n\t\tassumetls = true, host += 4, defaultport=defaultport?defaultport:443;\n\telse\n\t\thost = name;\n\tif (name==host) proto = NULL; else\n\tif (host != name && host[0] == '/' && host[1] == '/')\n\t{\n\t\thost += 2;\n\t\tproto = \"\";\t//not specified\n\t}\n\telse\n\t{\n\t\tproto = host;\n\t\thost = strstr(host, \"://\");\n\t\tif (host)\n\t\t{\n\t\t\tchar *t = alloca(1+host-proto);\n\t\t\tt[host-proto] = 0;\n\t\t\tproto = memcpy(t, proto, host-proto);\n\t\t\thost+=3;\n\t\t}\n\t\telse\n\t\t{\n\t\t\thost = name;\n\t\t\tproto = \"\";\n\t\t}\n\t}\n\n\tif (proto)\n\t{\n\t\tresource = strchr(host, '/');\n\t\tif (!resource)\n\t\t\tresource = \"/\";\n\t\telse\n\t\t{\n\t\t\tchar *t = alloca(1+resource-host);\n\t\t\tt[resource-host] = 0;\n\t\t\thost = memcpy(t, host, resource-host);\n\t\t}\n\t}\n\n\tif (NET_StringToAdr(host, defaultport, &adr))\n\t{\n\t\tqboolean wanttls = (adr.prot == NP_WSS || adr.prot == NP_TLS || (adr.prot != NP_STREAM && assumetls));\n\t\tvfsfile_t *f;\n#ifndef HAVE_SSL\n\t\tif (wanttls)\n\t\t\treturn NULL;\t//don't even make the connection if we can't satisfy it.\n#endif\n\t\tf = FS_WrapTCPSocket(TCP_OpenStream(&adr, name), true, name);\n#ifdef HAVE_SSL\n\t\tif (f && wanttls)\n\t\t\tf = FS_OpenSSL(host, f, false);\n#endif\n\n\t\tif (proto)\n\t\t\tf = Websocket_WrapStream(f, host, resource, proto);\n\t\treturn f;\n\t}\n\telse\n\t\treturn NULL;\n}\n#elif defined(FTE_TARGET_WEB)\ntypedef struct {\n\tvfsfile_t funcs;\n\n\tqboolean packetmode;\n\tint id;\n\tint readbuffered;\n\tchar readbuffer[65536];\n} wsfile_t;\nint QDECL VFSWS_ReadBytes (struct vfsfile_s *file, void *buffer, int bytestoread)\n{\n\twsfile_t *f = (wsfile_t*)file;\n\tint len;\n\tint trying;\n\n\tif (f->packetmode)\n\t{\t//just grab the next packet.\n\t\tlen = emscriptenfte_ws_recv(f->id, buffer, bytestoread);\n\t\tif (len < 0)\n\t\t\tlen = VFS_ERROR_EOF;\n\t\treturn len;\n\t}\n\n\t//websockets are pseudo-packetised. tcp isn't.\n\twhile (f->readbuffered < bytestoread)\n\t{\n\t\ttrying = sizeof(f->readbuffer) - f->readbuffered;\n\t\tif (trying < sizeof(f->readbuffer)/2)\n\t\t\tbreak;\t//don't try to fill the entire buffer, if we grab too much we'll truncate a message.\n\t\tlen = emscriptenfte_ws_recv(f->id, f->readbuffer + f->readbuffered, trying);\n\t\tif (len == 0)\n\t\t\tbreak;\t//no more left to read\n\t\telse if (len < 0)\n\t\t{\n\t\t\tif (!f->readbuffered)\n\t\t\t\treturn VFS_ERROR_EOF;\t//not gonna give any more.\n\t\t\tbreak;\t//will find it next time.\n\t\t}\n\t\telse f->readbuffered += len;\n\t}\n\n\tlen = min(bytestoread, f->readbuffered);\n\tmemcpy(buffer, f->readbuffer, len);\n\tf->readbuffered -= len;\n\tmemmove(f->readbuffer, f->readbuffer+len, f->readbuffered);\n\treturn len;\n}\nint QDECL VFSWS_WriteBytes (struct vfsfile_s *file, const void *buffer, int bytestowrite)\n{\n\twsfile_t *f = (wsfile_t*)file;\n\tint len;\n\n\tlen = emscriptenfte_ws_send(f->id, buffer, bytestowrite);\n\tif (len < 0)\n\t\treturn VFS_ERROR_EOF;\t//its dead jim.\n\t//if len == 0 //couldn't write yet\n\treturn len;\t//would have been the full packet.\n}\nqboolean QDECL VFSWS_Seek (struct vfsfile_s *file, qofs_t pos)\n{\n\treturn false;\n}\nstatic qofs_t QDECL VFSWS_Tell (struct vfsfile_s *file)\n{\n\treturn 0;\n}\nstatic qofs_t QDECL VFSWS_GetLen (struct vfsfile_s *file)\n{\n\treturn 0;\n}\nstatic qboolean QDECL VFSWS_Close (struct vfsfile_s *file)\n{\n\twsfile_t *f = (wsfile_t *)file;\n\temscriptenfte_ws_close(f->id);\n\tf->id = -1;\n\tZ_Free(f);\n\treturn true;\n}\n\nvfsfile_t *FS_OpenTCP(const char *name, int defaultport, qboolean assumetls)\n{\n\twsfile_t *newf;\n\tint id;\n\tconst char *proto = \"faketcp\";\n\tif (!strncmp(name, \"./\", 2) ||\t//relative-to-page\n\t\t!strncmp(name, \"/\", 1) ||\t//relative-to-host\n\t\t!strncmp(name, \"wss://\", 6) ||\t//what we'd rather be using...\n\t\t!strncmp(name, \"ws://\", 5))\t\t//what we're probably going to be able to use\n\t\t;\n\telse\n\t{\n\t\tconst char *host = name;\n\t\tif (!strncmp(name, \"ws:\", 3))\n\t\t\tassumetls = false, host += 3, defaultport=defaultport?defaultport:80;\n\t\telse if (!strncmp(name, \"wss:\", 4))\n\t\t\tassumetls = true, host += 4, defaultport=defaultport?defaultport:443;\n\t\telse\n\t\t\thost = name;\n\t\tif (host == name)\n\t\t\t;\t//no scheme\n\t\telse if (host != name && host[0] == '/' && host[1] == '/')\n\t\t\tname = host+2;\t//just ws:// without protocol name\n\t\telse\n\t\t{\n\t\t\tproto = host;\n\t\t\thost = strstr(host, \"://\");\n\t\t\tif (host)\n\t\t\t{\n\t\t\t\tchar *t = alloca(1+host-proto);\n\t\t\t\tt[host-proto] = 0;\n\t\t\t\tproto = memcpy(t, proto, host-proto);\n\t\t\t\tname = host+3;\n\t\t\t}\n\t\t\telse\n\t\t\t\treturn NULL; //something screwy.\n\t\t}\n\n\t\t//bad prefix... probably just a real hostname. don't get confused with relative-to-page uris.\n\t\t//FIXME: we should probably be trying to handle the defaultport. oh well.\n\t\tif (assumetls)\n\t\t\tname = va(\"wss://%s\", name);\n\t\telse\n\t\t{\n\t\t\tif (host == name)\n\t\t\t\tCon_Printf(CON_WARNING\"FS_OpenTCP(%s): Assuming insecure\\n\", name);\n\t\t\tname = va(\"ws://%s\", name);\t//urgh. will probably fail when browsers block it on https pages.\n\t\t}\n\t}\n\tid = emscriptenfte_ws_connect(name, proto);\n\tif (id < 0)\n\t\treturn NULL;\n\n\tnewf = Z_Malloc(sizeof(*newf));\n\tnewf->id = id;\n\tnewf->packetmode = strcmp(proto, \"faketcp\");\n\tnewf->funcs.Close = VFSWS_Close;\n\tnewf->funcs.Flush = NULL;\n\tnewf->funcs.GetLen = VFSWS_GetLen;\n\tnewf->funcs.ReadBytes = VFSWS_ReadBytes;\n\tnewf->funcs.Seek = VFSWS_Seek;\n\tnewf->funcs.Tell = VFSWS_Tell;\n\tnewf->funcs.WriteBytes = VFSWS_WriteBytes;\n\tnewf->funcs.seekstyle = SS_UNSEEKABLE;\n\n\treturn &newf->funcs;\n}\n#else\nvfsfile_t *FS_OpenTCP(const char *name, int defaultport, qboolean assumetls)\n{\n\treturn NULL;\n}\n#endif\n"
  },
  {
    "path": "engine/common/netinc.h",
    "content": "#ifndef NETINC_INCLUDED\n#define NETINC_INCLUDED\n\n\n#ifndef HAVE_PACKET\n\n#ifndef _XBOX\n//\tstruct sockaddr\n//\t{\n//\t\tshort  sa_family;\n//\t};\n\n\t#define ntohs BigShort\n\t#define htons BigShort\n\t#define htonl BigLong\n\t#define ntohl BigLong\n#endif\n/*\tstruct sockaddr_in\n\t{\n\t\tshort  sin_family;\n\t\tunsigned short\tsin_port;\n\t\tin_addr sin_addr;\n\t};*/\n\t#define AF_UNSPEC 0\n//\t#define AF_INET 1\n\n\t#define AF_WEBSOCK 342\n\n\tstruct sockaddr_websocket\n\t{\n\t\tshort  sws_family;\n\t\tchar url[64];\n\t};\n\n\n\n#elif defined(_WIN32)\n\t#ifdef _XBOX\n\t\t#include <xtl.h>\n\t\t#include <WinSockX.h>\n\t#else\n\t\t#if defined(_MSC_VER) && defined(HAVE_LEGACY)\n\t\t\t#define HAVE_IPX\n\t\t#endif\n\t\t#define WIN32_LEAN_AND_MEAN\n\t\t#include <windows.h>\n\t\t#include <winsock2.h>\n\t\t#ifdef HAVE_IPX\n\t\t\t#include \"wsipx.h\"\n\t\t#endif\n\t\t#include <ws2tcpip.h>\n\t#endif\n\t#include <errno.h>\n\t#if !defined(IPPROTO_IPV6) && !defined(_XBOX)\n\t\t/*for msvc6*/\n\t\t#define\tIPPROTO_IPV6 41\n\t\t\n\t\t#ifndef EAI_NONAME\t\t\n\t\t\t#define EAI_NONAME 8\n\t\t#endif\n\n\t\tstruct ip6_scope_id\n\t\t{\n\t\t\tunion\n\t\t\t{\n\t\t\t\tstruct\n\t\t\t\t{\n\t\t\t\t\tu_long  Zone : 28;\n\t\t\t\t\tu_long  Level : 4;\n\t\t\t\t};\n\t\t\t\tu_long  Value;\n\t\t\t};\n\t\t};\n\n\t\t#if !defined(in_addr6)\n\t\t\tstruct in6_addr\n\t\t\t{\n\t\t\t\tu_char\ts6_addr[16];\t/* IPv6 address */\n\t\t\t};\n\t\t\t#define sockaddr_in6 sockaddr_in6_fixed /*earlier versions of msvc have a sockaddr_in6 which does _not_ match windows, so this *must* be redefined for any non-final msvc releases or it won't work at all*/\n\t\t\ttypedef struct sockaddr_in6\n\t\t\t{\n\t\t\t\tshort  sin6_family;\n\t\t\t\tu_short  sin6_port;\n\t\t\t\tu_long  sin6_flowinfo;\n\t\t\t\tstruct in6_addr  sin6_addr;\n\t\t\t\tunion\n\t\t\t\t{\n\t\t\t\t\tu_long  sin6_scope_id;\n\t\t\t\t\tstruct ip6_scope_id  sin6_scope_struct; \n\t\t\t\t};\n\t\t\t};\n\t\t\tstruct addrinfo\n\t\t\t{\n\t\t\t  int ai_flags;\n\t\t\t  int ai_family;\n\t\t\t  int ai_socktype;\n\t\t\t  int ai_protocol;\n\t\t\t  size_t ai_addrlen;\n\t\t\t  char* ai_canonname;\n\t\t\t  struct sockaddr * ai_addr;\n\t\t\t  struct addrinfo * ai_next;\n\t\t\t};\n\t\t#endif\n\t#endif\n\t#ifndef IPV6_V6ONLY\n\t\t#define IPV6_V6ONLY 27\n\t#endif\n\n\t#define HAVE_IPV4\n\t#ifdef IPPROTO_IPV6\n\t\t\t#define HAVE_IPV6\n\t#endif\n\n\t#ifndef SOCK_CLOEXEC\n\t\t#define SOCK_CLOEXEC 0\n\t#endif\n#else\n\t#include <sys/time.h>\n\t#include <sys/types.h>\n\t#include <sys/socket.h>\n\t#include <netinet/in.h>\n\t#include <netinet/tcp.h>\n\t#include <netdb.h>\n\t#include <sys/ioctl.h>\n\t#include <sys/uio.h>\n\t#include <arpa/inet.h>\n\t#include <errno.h>\n\n\t#include <unistd.h>\n\n\t#ifdef sun\n\t\t#include <sys/filio.h>\n\t#endif\n\t#ifdef UNIXSOCKETS\n\t\t#include <sys/un.h>\n\t#endif\n\n\t#ifdef NeXT\n\t\t#include <libc.h>\n\t#endif\n\n\t#if defined(__linux__) || defined(HAVE_EPOLL)\n\t\t//requires linux 2.6.27 up (and equivelent libc)\n\t\t//note that BSD does tend to support the api, but emulated.\n\t\t//this works around the select FD limit, and supposedly has better performance.\n\t\t#include <sys/epoll.h>\n\t\t#ifdef EPOLL_CLOEXEC\n\t\t\t#define HAVE_EPOLL\n\t\t//#else too old, probably android...\n\t\t#endif\n\t\t#if defined(close)\t//epoll-shim? stop it from breaking shit\n\t\t\t#undef close\n\t\t\t#undef fcntl\n\t\t\t#define epoll_close epoll_shim_close\n\t\t#else\n\t\t\t#define epoll_close close\n\t\t#endif\n\t#endif\n\n\t#if defined(__MORPHOS__) && !defined(ixemul)\n\t\t#define closesocket CloseSocket\n\t\t#define ioctlsocket IoctlSocket\n\t#else\n\t\t#define closesocket close\n\t\t#define ioctlsocket ioctl\n\t#endif\n\n\t#if defined(AF_INET)\n\t\t#define HAVE_IPV4\n\t#endif\n\t#if defined(AF_INET6)\n\t\t#define HAVE_IPV6\n\t#endif\n\n//\t#if defined(AF_IPX) && defined(HAVE_LEGACY)\n//\t\t#include <netipx/ipx.h>\n//\t\t#define HAVE_IPX\n//\t#endif\n\n\t#define SOCKET int\n\n\t#ifndef SOCK_CLOEXEC\n\t\t#define SOCK_CLOEXEC 0\n\t#endif\n#endif\n\n#if defined(_WIN32)\n\t#define neterrno() WSAGetLastError()\n\t//this madness is because winsock defines its own errors instead of using system error codes.\n\t//*AND* microsoft then went and defined names for all the unix ones too... with different values! oh the insanity of it all!\n\t#define NET_EINTR\t\t\tWSAEINTR\n\t#define NET_EWOULDBLOCK\t\tWSAEWOULDBLOCK\n\t#define NET_EINPROGRESS\t\tWSAEINPROGRESS\n\t#define NET_EMSGSIZE\t\tWSAEMSGSIZE\n\t#define NET_ECONNRESET\t\tWSAECONNRESET\n\t#define NET_ECONNABORTED\tWSAECONNABORTED\n\t#define NET_ECONNREFUSED\tWSAECONNREFUSED\n\t#define NET_ETIMEDOUT\t\tWSAETIMEDOUT\n\t#define NET_ENOTCONN\t\tWSAENOTCONN\n\t#define NET_EACCES\t\t\tWSAEACCES\n\t#define NET_EADDRNOTAVAIL\tWSAEADDRNOTAVAIL\n\t#define NET_ENETUNREACH\t\tWSAENETUNREACH\n\t#define NET_EAFNOSUPPORT\tWSAEAFNOSUPPORT\n#elif defined(__MORPHOS__) && !defined(ixemul)\n\t#define neterrno() Errno()\n#else\n\t#define neterrno() errno\n#endif\n\n#ifndef NET_EWOULDBLOCK\n\t//assume unix codes instead, so our prefix still works.\n\t#define NET_EINTR\t\t\tEINTR\n\t#define NET_EWOULDBLOCK\t\tEWOULDBLOCK\n\t#define NET_EINPROGRESS\t\tEINPROGRESS\n\t#define NET_EMSGSIZE\t\tEMSGSIZE\n\t#define NET_ECONNRESET\t\tECONNRESET\n\t#define NET_ECONNABORTED\tECONNABORTED\n\t#define NET_ECONNREFUSED\tECONNREFUSED\n\t#define NET_ETIMEDOUT\t\tETIMEDOUT\n\t#define NET_ENOTCONN\t\tENOTCONN\n\t#define NET_EACCES\t\t\tEACCES\n\t#define NET_EADDRNOTAVAIL\tEADDRNOTAVAIL\n\t#define NET_ENETUNREACH\t\tENETUNREACH\n\t#define NET_EAFNOSUPPORT\tEAFNOSUPPORT\n#endif\n\n#ifndef INVALID_SOCKET\n\t#define INVALID_SOCKET -1\n#endif\n#ifndef MSG_NOSIGNAL\n\t#define MSG_NOSIGNAL\t0\t//available on linux, no idea about other unixes. don't bug out too much... (d)tls needs this to not get constant SIGPIPE errors\n#endif\n\n#ifndef INADDR_LOOPBACK\n\t#define INADDR_LOOPBACK 0x7f000001\n#endif\n\n#if defined(FTE_TARGET_WEB)\n\t#undef HAVE_IPV4\n\t#undef HAVE_IPV6\n\t#undef HAVE_IPX\n#endif\n\n#if 1//def SUPPORT_ICE\nstruct icecandinfo_s\n{\n\tchar candidateid[64];\n\tchar addr[64];\t\t//v4/v6/fqdn. fqdn should prefer ipv6\n\tint port;\t\t\t//native endian...\n\tint transport;\t\t//0=udp. other values not supported\n\tint foundation;\t\t//to figure out...\n\tint component;\t\t//1-based. allows rtp+rtcp in a single ICE... we only support one.\n\tint priority;\t\t//some random value...\n\tenum\n\t{\n\t\tICE_HOST=0,\n\t\tICE_SRFLX=1,\t//Server Reflexive (from stun, etc)\n\t\tICE_PRFLX=2,\t//Peer Reflexive \n\t\tICE_RELAY=3,\n\t} type;\t\t\t\t//says what sort of proxy is used.\n\tchar reladdr[64];\t//when proxied, this is our local info\n\tint relport;\n\tint generation;\t\t//for ice restarts. starts at 0.\n\tint network;\t\t//which network device this comes from.\n};\nenum iceproto_e\n{\n\tICEP_INVALID,\t//not allowed..\n\tICEP_QWSERVER,\t//we're server side\n\tICEP_QWCLIENT,\t//we're client side\n\tICEP_VOICE,\t\t//speex. requires client.\n\tICEP_VIDEO\t\t//err... REALLY?!?!?\n};\nenum icemode_e\n{\n\tICEM_RAW,\t\t//not actually interactive beyond a simple handshake.\n\tICEM_ICE,\t\t//rfc5245. meant to be able to holepunch, but not implemented properly yet.\n\tICEM_WEBRTC,\t//IP+UDP+((ICE/STUN/SDP)+(DTLS+SCTP))... no more layers? :o\n};\nenum icestate_e\n{\n\tICE_INACTIVE,\t//idle.\n\tICE_FAILED,\n\tICE_GATHERING,\n\tICE_CONNECTING,\t//exchanging pings.\n\tICE_CONNECTED\t//media is flowing, supposedly. sending keepalives.\n};\nstruct icestate_s;\n#define ICE_API_CURRENT \"Internet Connectivity Establishment 0.0\"\ntypedef struct\n{\n\tstruct icestate_s *(QDECL *Create)(void *module, const char *conname, const char *peername, enum icemode_e mode, enum iceproto_e proto, qboolean initiator);\t//doesn't start pinging anything.\n\tqboolean (QDECL *Set)(struct icestate_s *con, const char *prop, const char *value);\n\tqboolean (QDECL *Get)(struct icestate_s *con, const char *prop, char *value, size_t valuesize);\n\tstruct icecandinfo_s *(QDECL *GetLCandidateInfo)(struct icestate_s *con);\t\t//retrieves candidates that need reporting to the peer.\n\tvoid (QDECL *AddRCandidateInfo)(struct icestate_s *con, struct icecandinfo_s *cand);\t\t//stuff that came from the peer.\n\tvoid (QDECL *Close)(struct icestate_s *con, qboolean force);\t//bye then.\n\tvoid (QDECL *CloseModule)(void *module);\t//closes all unclosed connections, with warning.\n\tqboolean (QDECL *GetLCandidateSDP)(struct icestate_s *con, char *out, size_t valuesize);\t\t//retrieves candidates that need reporting to the peer.\n\tstruct icestate_s *(QDECL *Find)(void *module, const char *conname);\n} icefuncs_t;\nextern icefuncs_t iceapi;\nextern cvar_t net_ice_broker;\n#endif\n\n#ifdef HAVE_EPOLL\ntypedef struct epollctx_s\n{\n\tvoid (*Polled) (struct epollctx_s *ctx, unsigned int events);\n} epollctx_t;\n#endif\n\n//address flags\n#define ADDR_NATPMP\t\t(1u<<0)\n#define ADDR_UPNPIGP\t(1u<<1)\n#define ADDR_REFLEX\t\t(1u<<2)\t//as reported by some external server.\n\n#define FTENET_ADDRTYPES 2\ntypedef struct ftenet_generic_connection_s {\n\tchar name[MAX_QPATH];\n\n\tint (*GetLocalAddresses)(struct ftenet_generic_connection_s *con, unsigned int *adrflags, netadr_t *addresses, const char **adrparams, int maxaddresses);\n\tqboolean (*ChangeLocalAddress)(struct ftenet_generic_connection_s *con, const char *addressstring, netadr_t *newadr);\n\tqboolean (*GetPacket)(struct ftenet_generic_connection_s *con);\n\tneterr_t (*SendPacket)(struct ftenet_generic_connection_s *con, int length, const void *data, netadr_t *to);\n\tvoid (*Close)(struct ftenet_generic_connection_s *con);\n#if defined(HAVE_PACKET) && !defined(HAVE_EPOLL)\n\tint (*SetFDSets) (struct ftenet_generic_connection_s *con, fd_set *readfdset, fd_set *writefdset);\t/*set for connections which have multiple sockets (ie: listening tcp connections)*/\n#endif\n\tvoid (*PrintStatus)(struct ftenet_generic_connection_s *con);\n\n\tnetproto_t\t\tprot;\t//if there's some special weirdness\n\tnetadrtype_t\taddrtype[FTENET_ADDRTYPES];\t//which address families it accepts\n\tqboolean islisten;\n\n\tint connum;\n\tstruct ftenet_connections_s *owner;\n\n#ifdef HAVE_EPOLL\n\tepollctx_t epoll;\n#endif\n\n#ifdef HAVE_PACKET\n\tSOCKET thesocket;\n#else\n\tint thesocket;\n#endif\n} ftenet_generic_connection_t;\n\nenum hashvalidation_e\n{\n\tVH_UNSUPPORTED,\t\t\t//library not loaded\t(bad but not malicious)\n\tVH_AUTHORITY_UNKNOWN,\t//don't know who signed it / untrusted (bad but probably not malicious)\n\tVH_INCORRECT,\t\t\t//signature is wrong for that authority (bad, probably maliciously so)\n\tVH_CORRECT\t\t\t\t//all is well.\n};\nstruct dtlsfuncs_s;\ntypedef struct dtlscred_s\n{\n\tstruct dtlslocalcred_s\n\t{\n\t\tvoid *cert;\n\t\tsize_t certsize;\n\t\tvoid *key;\n\t\tsize_t keysize;\n\t} local;\n\tstruct dtlspeercred_s\n\t{\n\t\tconst char *name;\t//cert must match this if specified\n\n\t\thashfunc_t *hash;\t//if set peer's cert MUST match the specified digest (with this hash function)\n\t\tqbyte digest[DIGEST_MAXSIZE];\n\t} peer;\n} dtlscred_t;\ntypedef struct dtlsfuncs_s\n{\n\tvoid *(*CreateContext)(const dtlscred_t *credinfo, void *cbctx, neterr_t(*push)(void *cbctx, const qbyte *data, size_t datasize), qboolean isserver);\t//the certificate to advertise.\n\tqboolean (*CheckConnection)(void *cbctx, void *peeraddr, size_t peeraddrsize, void *indata, size_t insize, neterr_t(*push)(void *cbctx, const qbyte *data, size_t datasize), void (*EstablishTrueContext)(void **cbctx, void *state));\n\tvoid (*DestroyContext)(void *ctx);\n\tneterr_t (*Transmit)(void *ctx, const qbyte *data, size_t datasize);\n\tneterr_t (*Received)(void *ctx, sizebuf_t *message);\t//operates in-place...\n\tneterr_t (*Timeouts)(void *ctx);\n\tint (*GetPeerCertificate)(void *ctx, enum certprops_e prop, char *out, size_t outsize);\n\tqboolean (*GenTempCertificate)(const char *subject, struct dtlslocalcred_s *cred);\n} dtlsfuncs_t;\n#ifdef HAVE_DTLS\nconst dtlsfuncs_t *DTLS_InitServer(void);\nconst dtlsfuncs_t *DTLS_InitClient(void);\n#endif\ntypedef struct ftecrypto_s\n{\n\tconst char *drivername;\n\n\t//tlsey things\n\tvfsfile_t *(*OpenStream)(const char *hostname, vfsfile_t *source, qboolean isserver);\t//establish a tls connection around a tcp stream\n\tint (*GetChannelBinding)(vfsfile_t *vf, qbyte *binddata, size_t *bindsize);\t//returns -1 if functions don't match those from OpenStream\n\n\t//dtls entry points\n\tconst struct dtlsfuncs_s *(*DTLS_InitClient)(void);\t//should always return something, if implemented.\n\tconst struct dtlsfuncs_s *(*DTLS_InitServer)(void);\t//returns NULL if there's no cert available.\n\n\t//digital signature stuff. note: uses sha2_512\n\tenum hashvalidation_e (*VerifyHash)(const qbyte *hashdata, size_t hashsize, const qbyte *certdata, size_t certsize, const qbyte *signdata, size_t signsize); //expect the cert in PEM format.\n\tint (*GenerateSignature)(const qbyte *hashdata, size_t hashsize, qbyte *signdata, size_t signsizemax);\n} ftecrypto_t;\n#define cryptolib_count 6\nextern ftecrypto_t\tcrypto_sspi, crypto_gnutls;\nextern ftecrypto_t *cryptolib[cryptolib_count];\n\n\n\n#define MAX_CONNECTIONS 8\ntypedef struct ftenet_connections_s\n{\n\tqboolean islisten;\n\tunsigned int packetsin;\n\tunsigned int packetsout;\n\tunsigned int bytesin;\n\tunsigned int bytesout;\n\tunsigned int timemark;\n\tfloat packetsinrate;\n\tfloat packetsoutrate;\n\tfloat bytesinrate;\n\tfloat bytesoutrate;\n\tftenet_generic_connection_t *conn[MAX_CONNECTIONS];\n\n\tvoid (*ReadGamePacket) (void);\n\n#ifdef HAVE_DTLS\n\tstruct dtlspeer_s *dtls;\t//linked list. linked lists are shit, but at least it keeps pointers valid when things are resized.\n\tconst dtlsfuncs_t *dtlsfuncs;\n#endif\n\n\tstruct ftenet_delayed_packet_s\n\t{\n\t\tunsigned int sendtime;\t//in terms of Sys_Milliseconds()\n\t\tstruct ftenet_delayed_packet_s *next;\n\t\tnetadr_t dest;\n\t\tsize_t cursize;\n\t\tqbyte data[1];\n\t} *delayed_packets;\n\n\tnetadr_t srflx[2];\t//ipv4, ipv6\n\tunsigned int srflx_tid[3]; //to verify the above.\n} ftenet_connections_t;\n\nvoid ICE_Tick(void);\nqboolean ICE_WasStun(ftenet_connections_t *col);\nvoid QDECL ICE_AddLCandidateConn(ftenet_connections_t *col, netadr_t *addr, int type);\nvoid QDECL ICE_AddLCandidateInfo(struct icestate_s *con, netadr_t *adr, int adrno, int type);\nftenet_generic_connection_t *FTENET_ICE_EstablishConnection(ftenet_connections_t *col, const char *address, netadr_t adr, const struct dtlspeercred_s *peerinfo);\nextern ftenet_generic_connection_t *FTENET_Datagram_EstablishConnection(ftenet_connections_t *col, const char *address, netadr_t adr, const struct dtlspeercred_s *peerinfo);\nenum icemsgtype_s\n{\t//shared by rtcpeers+broker\n\tICEMSG_PEERLOST=0,\t//other side dropped connection\n\tICEMSG_GREETING=1,\t//master telling us our unique game name\n\tICEMSG_NEWPEER=2,\t//relay established, send an offer now.\n\tICEMSG_OFFER=3,\t\t//peer->peer - peer's offer or answer details\n\tICEMSG_CANDIDATE=4,\t//peer->peer - candidate updates. may arrive late as new ones are discovered.\n\tICEMSG_ACCEPT=5,\t//go go go (response from offer)\n\tICEMSG_SERVERINFO=6,//server->broker (for advertising the server properly)\n\tICEMSG_SERVERUPDATE=7,//broker->browser (for querying available server lists)\n\tICEMSG_NAMEINUSE=8,\t//requested resource is unavailable.\n};\n\nenum websocketpackettype_e\n{\t//websocket packet types, used by both our tcp/http/broker/etc server and our ice client.\n\tWS_PACKETTYPE_CONTINUATION=0,\n\tWS_PACKETTYPE_TEXTFRAME=1,\n\tWS_PACKETTYPE_BINARYFRAME=2,\n\tWS_PACKETTYPE_CLOSE=8,\n\tWS_PACKETTYPE_PING=9,\n\tWS_PACKETTYPE_PONG=10,\n};\n\nftenet_connections_t *FTENET_CreateCollection(qboolean listen, void (*ReadPacket) (void));\nvoid FTENET_CloseCollection(ftenet_connections_t *col);\nqboolean FTENET_AddToCollection(struct ftenet_connections_s *col, const char *name, const char *address, netadrtype_t addrtype, netproto_t addrprot);\nint NET_EnumerateAddresses(ftenet_connections_t *collection, struct ftenet_generic_connection_s **con, unsigned int *adrflags, netadr_t *addresses, const char **adrparams, int maxaddresses);\n\nvoid *TLS_GetKnownCertificate(const char *certname, size_t *size);\nvoid *Auth_GetKnownCertificate(const char *certname, size_t *size);\nvfsfile_t *FS_OpenSSL(const char *hostname, vfsfile_t *source, qboolean server);\nint TLS_GetChannelBinding(vfsfile_t *stream, qbyte *data, size_t *datasize);\t//datasize should be preinitialised to the max length allowed. -1 for not implemented. 0 for peer problems. 1 for success\n#ifdef HAVE_PACKET\nvfsfile_t *FS_WrapTCPSocket(SOCKET socket, qboolean conpending, const char *peername);\t//conpending allows us to reject any writes until the connection has succeeded. considers the socket owned (so be sure to stop using the direct socket at least before the VFS_CLOSE call).\n#endif\nvfsfile_t *FS_OpenTCP(const char *name, int defaultport, qboolean assumetls);\n\n#endif //NETINC_INCLUDED\n"
  },
  {
    "path": "engine/common/particles.h",
    "content": "#ifndef _PARTICLES_H_\n#define _PARTICLES_H_\n\nextern int \n\tpt_gunshot,\n\tptdp_gunshotquad,\n\tpt_spike,\n\tptdp_spikequad,\n\tpt_superspike,\n\tptdp_superspikequad,\n\tpt_wizspike,\n\tpt_knightspike,\n\tpt_explosion,\n\tptdp_explosionquad,\n\tpt_tarexplosion,\n\tpt_teleportsplash,\n\tpt_lavasplash,\n\tptdp_smallflash,\n\tptdp_flamejet,\n\tptdp_flame,\n\tptdp_blood,\n\tptdp_spark,\n\tptdp_plasmaburn,\n\tptdp_tei_g3,\n\tptdp_tei_smoke,\n\tptdp_tei_bigexplosion,\n\tptdp_tei_plasmahit,\n\tptdp_stardust,\n\trt_rocket,\n\trt_grenade,\n\trt_blood,\n\trt_wizspike,\n\trt_slightblood,\n\trt_knightspike,\n\trt_vorespike,\n\trtdp_nexuizplasma,\n\trtdp_glowtrail,\n\n\tptqw_blood,\n\tptqw_lightningblood,\n\n\trtqw_railtrail,\t//common to zquake/fuhquake/fte\n\tptfte_bullet,\n\tptfte_superbullet;\n\n\n#ifdef Q2CLIENT\n/*WARNING: must match cl_tent.c*/\ntypedef enum\n{\n\t/*MUST start with standard q2 te effects*/\n\tQ2TE_GUNSHOT = 0,\t//0\n\tQ2TE_BLOOD,\n\tQ2TE_BLASTER,\n\tQ2TE_RAILTRAIL,\n\tQ2TE_SHOTGUN,\n\tQ2TE_EXPLOSION1,\n\tQ2TE_EXPLOSION2,\n\tQ2TE_ROCKET_EXPLOSION,\n\tQ2TE_GRENADE_EXPLOSION,\n\tQ2TE_SPARKS,\n\tQ2TE_SPLASH,\t//10\n\tQ2TE_BUBBLETRAIL,\n\tQ2TE_SCREEN_SPARKS,\n\tQ2TE_SHIELD_SPARKS,\n\tQ2TE_BULLET_SPARKS,\n\tQ2TE_LASER_SPARKS,\n\tQ2TE_PARASITE_ATTACK,\n\tQ2TE_ROCKET_EXPLOSION_WATER,\n\tQ2TE_GRENADE_EXPLOSION_WATER,\n\tQ2TE_MEDIC_CABLE_ATTACK,\n\tQ2TE_BFG_EXPLOSION,\t//20\n\tQ2TE_BFG_BIGEXPLOSION,\n\tQ2TE_BOSSTPORT,\t\t\t// used as '22' in a map, so DON'T RENUMBER!!!\n\tQ2TE_BFG_LASER,\n\tQ2TE_GRAPPLE_CABLE,\n\tQ2TE_WELDING_SPARKS,\n\tQ2TE_GREENBLOOD,\n\tQ2TE_BLUEHYPERBLASTER,\n\tQ2TE_PLASMA_EXPLOSION,\n\tQ2TE_TUNNEL_SPARKS,\n//ROGUE\n\tQ2TE_BLASTER2,\t//30\n\tQ2TEEX_RAILTRAIL2,\n\tQ2TE_FLAME,\n\tQ2TE_LIGHTNING,\n\tQ2TE_DEBUGTRAIL,\n\tQ2TE_PLAIN_EXPLOSION,\n\tQ2TE_FLASHLIGHT,\n\tQ2TE_FORCEWALL,\n\tQ2TE_HEATBEAM,\n\tQ2TE_MONSTER_HEATBEAM,\n\tQ2TE_STEAM,\t\t//40\n\tQ2TE_BUBBLETRAIL2,\n\tQ2TE_MOREBLOOD,\n\tQ2TE_HEATBEAM_SPARKS,\n\tQ2TE_HEATBEAM_STEAM,\n\tQ2TE_CHAINFIST_SMOKE,\n\tQ2TE_ELECTRIC_SPARKS,\n\tQ2TE_TRACKER_EXPLOSION,\n\tQ2TE_TELEPORT_EFFECT,\n\tQ2TE_DBALL_GOAL,\n\tQ2TE_WIDOWBEAMOUT,\t//50\n\tQ2TE_NUKEBLAST,\n\tQ2TE_WIDOWSPLASH,\n\tQ2TE_EXPLOSION1_BIG,\n\tQ2TE_EXPLOSION1_NP,\n\tQ2TE_FLECHETTE,\n//ROGUE\n\n//RERELEASE\n\tQ2TEEX_BLUEHYPERBLASTER=56,\n\tQ2TEEX_BFGZAP,\n\tQ2TEEX_BERSERK_SLAM,\n\tQ2TEEX_GRAPPLE_CABLE_2,\n\tQ2TEEX_POWER_SPLASH,\n\tQ2TEEX_LIGHTNING_BEAM,\n\tQ2TEEX_EXPLOSION1_NL,\n\tQ2TEEX_EXPLOSION2_NL,\n//RERELEASE\n\n//CODERED\n\tCRTE_LEADERBLASTER=56,\t//56\n\tCRTE_BLASTER_MUZZLEFLASH,\n\tCRTE_BLUE_MUZZLEFLASH,\n\tCRTE_SMART_MUZZLEFLASH,\n\tCRTE_LEADERFIELD,\t//60\n\tCRTE_DEATHFIELD,\n\tCRTE_BLASTERBEAM,\n\tCRTE_STAIN,\n\tCRTE_FIRE,\n\tCRTE_CABLEGUT,\n\tCRTE_SMOKE,\n//CODERED\n\n#define Q2TE_MAX CRTE_SMOKE\n\n\t/*splashes are somewhat special, but are dynamically indexed*/\n\tQ2SPLASH_UNKNOWN,\t\t//0\n\tQ2SPLASH_SPARKS,\t\t//1\n\tQ2SPLASH_BLUE_WATER,\t//2\n\tQ2SPLASH_BROWN_WATER,\t//3\n\tQ2SPLASH_SLIME,\t\t\t//4\n\tQ2SPLASH_LAVA,\t\t\t//5\n\tQ2SPLASH_BLOOD,\t\t\t//6\n\tQ2EXSPLASH_ELECTRIC,\t//7\n#define Q2SPLASH_MAX Q2EXSPLASH_ELECTRIC\n\n\t/*free form*/\n\t/*WARNING: must match cl_tent.c*/\n\tQ2RT_BLASTERTRAIL,\n\tQ2RT_BLASTERTRAIL2,\n\tQ2RT_GIB,\n\tQ2RT_GREENGIB,\n\tQ2RT_ROCKET,\n\tQ2RT_GRENADE,\n\n\tQ2RT_TRAP,\n\tQ2RT_FLAG1,\n\tQ2RT_FLAG2,\n\tQ2RT_TAGTRAIL,\n\tQ2RT_TRACKER,\n\tQ2RT_IONRIPPER,\n\tQ2RT_PLASMA,\n\n\tQ2PT_BFGPARTICLES,\n\tQ2PT_FLIES,\n\tQ2PT_TRAP,\n\tQ2PT_TRACKERSHELL,\n\n\tQ2PT_RESPAWN,\n\tQ2PT_PLAYER_TELEPORT,\n\tQ2PT_FOOTSTEP,\n\tQ2PT_OTHER_FOOTSTEP,\n\tQ2PT_LADDER_STEP,\n\n\tQ2PT_MAX\n} q2particleeffects_t;\nextern int pt_q2[];\n#endif\n\ntypedef quint32_t trailkey_t;\n#define trailkey_null 0\n\n#define PARTICLE_Z_CLIP\t8.0\n\ntypedef enum {\n\tBM_BLEND/*SRC_ALPHA ONE_MINUS_SRC_ALPHA*/,\n\tBM_BLENDCOLOUR/*SRC_COLOR ONE_MINUS_SRC_COLOR*/,\n\tBM_ADDA/*SRC_ALPHA ONE*/,\n\tBM_ADDC/*GL_SRC_COLOR GL_ONE*/,\n\tBM_SUBTRACT/*SRC_ALPHA ONE_MINUS_SRC_COLOR*/,\n\tBM_INVMODA/*ZERO ONE_MINUS_SRC_ALPHA*/,\n\tBM_INVMODC/*ZERO ONE_MINUS_SRC_COLOR*/,\n\tBM_PREMUL/*ONE ONE_MINUS_SRC_ALPHA*/,\n\tBM_RTSMOKE\t/*special shader generation that causes these particles to be lit up by nearby rtlights, instead of just being fullbright junk*/\n} blendmode_t;\n#define frandom() (rand()*(1.0f/(float)RAND_MAX))\n#define crandom() (rand()*(2.0f/(float)RAND_MAX)-1.0f)\n#define hrandom() (rand()*(1.0f/(float)RAND_MAX)-0.5f)\n\n#define P_INVALID -1\n\n#define P_RunParticleEffectType(a,b,c,d) P_RunParticleEffectState(a,b,c,d,NULL)\n\n// used for callback\nextern cvar_t r_particlesdesc;\nextern cvar_t r_particlesystem;\n\nstruct model_s;\nstruct msurface_s;\n\nvoid P_InitParticleSystem(void);\nvoid P_ShutdownParticleSystem(void);\nvoid P_Shutdown(void);\nvoid P_LoadedModel(struct model_s *mod);\t/*checks a model's various effects*/\nvoid P_DefaultTrail (unsigned int entityeffects, unsigned int modelflags, int *trailid, int *trailpalidx);\nvoid P_EmitEffect (vec3_t pos, vec3_t orientation[3], unsigned int modeleflags, int type, trailkey_t *tsk);//this is just a wrapper\nint P_FindParticleType(const char *efname);\n#ifdef PSET_SCRIPT\nvoid PScript_ClearSurfaceParticles(struct model_s *mod);\n#endif\n\n#define P_RunParticleEffectTypeString pe->RunParticleEffectTypeString\n#define P_ParticleTrail pe->ParticleTrail\n#define P_RunParticleEffectState pe->RunParticleEffectState\n#define P_RunParticleWeather pe->RunParticleWeather\n#define P_RunParticleCube pe->RunParticleCube\n#define P_RunParticleEffect pe->RunParticleEffect\n#define P_RunParticleEffect2 pe->RunParticleEffect2\n#define P_RunParticleEffect3 pe->RunParticleEffect3\n#define P_RunParticleEffect4 pe->RunParticleEffect4\n#define P_RunParticleEffectPalette pe->RunParticleEffectPalette\n\n#define P_ParticleTrailIndex pe->ParticleTrailIndex\n#define P_InitParticles pe->InitParticles\n#define P_DelinkTrailstate pe->DelinkTrailstate\n#define P_ClearParticles pe->ClearParticles\n#define P_DrawParticles pe->DrawParticles\n\ntypedef struct {\n\tchar *name1;\n\tchar *name2;\n\n\tint (*FindParticleType) (const char *name);\n\tqboolean (*ParticleQuery) (int type, int body, char *outstr, int outstrlen);\n\n\tint (*RunParticleEffectTypeString) (vec3_t org, vec3_t dir, float count, char *name);\n\tint (*ParticleTrail) (vec3_t startpos, vec3_t end, int type, float timeinterval, int dlkey, vec3_t dlaxis[3], trailkey_t *tk);\n\tint (*RunParticleEffectState) (vec3_t org, vec3_t dir, float count, int typenum, trailkey_t *tk);\n\tvoid (*RunParticleWeather) (vec3_t minb, vec3_t maxb, vec3_t dir, float count, int colour, char *efname);\n\tvoid (*RunParticleCube) (int typenum, vec3_t minb, vec3_t maxb, vec3_t dir_min, vec3_t dir_max, float count, int colour, qboolean gravity, float jitter); //typenum may be P_INVALID\n\tvoid (*RunParticleEffect) (vec3_t org, vec3_t dir, int color, int count);\n\tvoid (*RunParticleEffect2) (vec3_t org, vec3_t dmin, vec3_t dmax, int color, int effect, int count);\n\tvoid (*RunParticleEffect3) (vec3_t org, vec3_t box, int color, int effect, int count);\n\tvoid (*RunParticleEffect4) (vec3_t org, float radius, int color, int effect, int count);\n\tvoid (*RunParticleEffectPalette) (const char *nameprefix, vec3_t org, vec3_t dir, int color, int count);\n\n\tvoid (*ParticleTrailIndex) (vec3_t start, vec3_t end, int type, float timeinterval, int color, int crnd, trailkey_t *tk);\t//P_INVALID is fine for the type here, you'll get a default trail.\n\tqboolean (*InitParticles) (void);\n\tvoid (*ShutdownParticles) (void);\n\tvoid (*DelinkTrailstate) (trailkey_t *tk);\n\tvoid (*ClearParticles) (void);\n\tvoid (*DrawParticles) (void);\n} particleengine_t;\nextern particleengine_t *pe;\n\n#endif\n"
  },
  {
    "path": "engine/common/plugin.c",
    "content": "//This file should be easily portable.\n//The biggest strength of this plugin system is that ALL interactions are performed via\n//named functions, this makes it *really* easy to port plugins from one engine to another.\n\n#include \"quakedef.h\"\n#include \"fs.h\"\n#include \"vr.h\"\n#include \"com_bih.h\"\n#define FTEENGINE\n#include \"../plugins/plugin.h\"\n\n#ifdef PLUGINS\n\n#if defined(Q3SERVER)||defined(Q3CLIENT)\nstruct q3gamecode_s *q3;\nstatic struct plugin_s *q3plug;\n#endif\n\n#define Q_strlcpy Q_strncpyz\n#define Q_strlcat Q_strncatz\n#define Sys_Errorf Sys_Error\n\n#ifdef MODELFMT_GLTF\n\t#include \"../plugins/models/gltf.c\"\n#endif\n#ifdef USE_INTERNAL_ODE\n\t#include \"../engine/common/com_phys_ode.c\"\n#endif\n\n#if defined(HAVE_CLIENT) && defined(STATIC_EZHUD)\t//if its statically linked and loading by default then block it by default and let configs reenable it. The defaults must be maintained for deltaing configs to work, yet they're defective and should never be used in that default configuration\ncvar_t plug_sbar = CVARD(\"plug_sbar\", \"0\", \"Controls whether plugins are allowed to draw the hud, rather than the engine (when allowed by csqc). This is typically used to permit the ezhud plugin without needing to bother unloading it.\\n=0: never use hud plugins.\\n&1: Use hud plugins in deathmatch.\\n&2: Use hud plugins in singleplayer/coop.\\n=3: Always use hud plugins (when loaded).\");\n#else\ncvar_t plug_sbar = CVARD(\"plug_sbar\", \"3\", \"Controls whether plugins are allowed to draw the hud, rather than the engine (when allowed by csqc). This is typically used to permit the ezhud plugin without needing to bother unloading it.\\n=0: never use hud plugins.\\n&1: Use hud plugins in deathmatch.\\n&2: Use hud plugins in singleplayer/coop.\\n=3: Always use hud plugins (when loaded).\");\n#endif\ncvar_t plug_loaddefault = CVARD(\"plug_loaddefault\", \"1\", \"0: Load plugins only via explicit plug_load commands\\n1: Load built-in plugins and those selected via the package manager\\n2: Scan for misc plugins, loading all that can be found, but not built-ins.\\n3: Scan for plugins, and then load any built-ins\");\n\nextern qboolean Plug_Q3_Init(void);\nextern qboolean Plug_Bullet_Init(void);\nextern qboolean Plug_ODE_Init(void);\n#if defined(HAVE_CLIENT) && defined(STATIC_EZHUD)\nextern qboolean Plug_EZHud_Init(void);\n#endif\nextern qboolean Plug_OpenSSL_Init(void);\nstatic struct\n{\n\tconst char *name;\n\tqboolean (QDECL *initfunction)(void);\n} staticplugins[] = \n{\n#if defined(USE_INTERNAL_BULLET)\n\t{\"bullet_internal\", Plug_Bullet_Init},\n#endif\n#if defined(USE_INTERNAL_ODE)\n\t{\"ODE_internal\", Plug_ODE_Init},\n#endif\n#if defined(MODELFMT_GLTF)\n\t{\"GLTF\", Plug_GLTF_Init},\n#endif\n\n#ifdef STATIC_OPENSSL\n\t{\"openssl_internal\", Plug_OpenSSL_Init},\n#endif\n#if defined(HAVE_CLIENT) && defined(STATIC_EZHUD)\n\t{\"EZHud_internal\", Plug_EZHud_Init},\n#endif\n#ifdef STATIC_Q3\n\t{\"quake3\", Plug_Q3_Init},\n#endif\n\t{NULL}\n};\n//for internal plugins to link against\nplugcorefuncs_t *plugfuncs;\nplugcvarfuncs_t *cvarfuncs;\nplugcmdfuncs_t *cmdfuncs;\n\n#ifdef GLQUAKE\n#include \"glquake.h\"\n#endif\n#include \"com_mesh.h\"\n#include \"shader.h\"\n\nstatic void *QDECL PlugBI_GetEngineInterface(const char *interfacename, size_t structsize);\n\n#ifdef SKELETALMODELS\nstatic int QDECL Plug_RegisterModelFormatText(const char *formatname, char *magictext, qboolean (QDECL *load) (struct model_s *mod, void *buffer, size_t fsize))\n{\n\tvoid *module = currentplug;\n\treturn Mod_RegisterModelFormatText(module, formatname, magictext, load);\n}\nstatic int QDECL Plug_RegisterModelFormatMagic(const char *formatname, qbyte *magic, size_t magicsize, qboolean (QDECL *load) (struct model_s *mod, void *buffer, size_t fsize))\n{\n\tvoid *module = currentplug;\n\treturn Mod_RegisterModelFormatMagic(module, formatname, magic, magicsize, load);\n}\nstatic void QDECL Plug_UnRegisterModelFormat(int idx)\n{\n\tvoid *module = currentplug;\n\tMod_UnRegisterModelFormat(module, idx);\n}\nstatic void QDECL Plug_UnRegisterAllModelFormats(void)\n{\n\tvoid *module = currentplug;\n\tMod_UnRegisterAllModelFormats(module);\n}\n#endif\n\n//custom plugin builtins.\nvoid Plug_RegisterBuiltin_(char *name, funcptr_t bi, int flags);\n#define Plug_RegisterBuiltin(n,bi,fl) Plug_RegisterBuiltin_(n,(funcptr_t)bi,fl)\n#define PLUG_BIF_NEEDSRENDERER 4\n\n#include \"netinc.h\"\n\ntypedef struct plugin_s {\n\tchar *name;\n\tchar filename[MAX_OSPATH];\n\tdllhandle_t *lib;\n\n\tvoid (QDECL *tick)(double realtime, double gametime);\n#ifndef SERVERONLY\n\tqboolean (QDECL *consolelink)(void);\n\tqboolean (QDECL *consolelinkmouseover)(float x, float y);\n\tint (QDECL *conexecutecommand)(qboolean isinsecure);\n\tqboolean (QDECL *menufunction)(int eventtype, int keyparam, int unicodeparm, float mousecursor_x, float mousecursor_y, float vidwidth, float vidheight);\n\tint (QDECL *sbarlevel[3])(int seat, float x, float y, float w, float h, unsigned int showscores);\t//0 - main sbar, 1 - supplementry sbar sections (make sure these can be switched off), 2 - overlays (scoreboard). menus kill all.\n\tvoid (QDECL *reschange)(int width, int height, qboolean restarted);\n\n\t//protocol-in-a-plugin\n\tint (QDECL *connectionlessclientpacket)(const char *buffer, size_t size, netadr_t *from);\n#endif\n\tqboolean (QDECL *svmsgfunction)(int messagelevel);\n\tqboolean (QDECL *chatmsgfunction)(int talkernum, int tpflags);\n\tqboolean (QDECL *centerprintfunction)(int clientnum);\n\tqboolean (QDECL *mayshutdown)(void);\t//lets the plugin report when its safe to close it.\n\tvoid (QDECL *shutdown)(void);\n\n\tstruct plugin_s *next;\n} plugin_t;\n\nint Plug_SubConsoleCommand(console_t *con, const char *line);\n\nplugin_t *currentplug;\n\n\n\n#ifndef SERVERONLY\n#include \"cl_plugin.inc\"\n#include \"cl_master.h\"\n#else\nvoid Plug_Client_Init(void){}\nvoid Plug_Client_Close(plugin_t *plug) {}\n#endif\n\nvoid Plug_Close(plugin_t *plug);\n\n\nstatic plugin_t *plugs;\n\n#ifdef USERBE\n#include \"pr_common.h\"\n#endif\n\nstatic char *Plug_CleanName(const char *file, char *out, size_t sizeof_out)\n{\n\tsize_t len;\n\t//\"fteplug_ezhud_x86.REV.dll\" gets converted into \"ezhud\"\n\n\t//skip fteplug_\n\tif (!Q_strncasecmp(file, PLUGINPREFIX, strlen(PLUGINPREFIX)))\n\t\tfile += strlen(PLUGINPREFIX);\n\n\t//strip .REV.dll\n\tCOM_StripAllExtensions(file, out, sizeof_out);\n\n\t//strip _x86\n\tlen = strlen(out);\n\tif (len > strlen(\"_\"ARCH_CPU_POSTFIX) && !Q_strncasecmp(out+len-strlen(\"_\"ARCH_CPU_POSTFIX), \"_\"ARCH_CPU_POSTFIX, strlen(\"_\"ARCH_CPU_POSTFIX)))\n\t{\n\t\tlen -= strlen(\"_\"ARCH_CPU_POSTFIX);\n\t\tout[len] = 0;\n\t}\n\treturn out;\n}\nstatic plugin_t *Plug_Load(const char *file)\n{\n\tstatic enum fs_relative prefixes[] =\n\t{\n\t\tFS_BINARYPATH,\n\t\tFS_LIBRARYPATH,\n#ifndef ANDROID\n\t\tFS_ROOT,\n#endif\n\t};\n\tstatic char *postfixes[] =\n\t{\n\t\t\"_\" ARCH_CPU_POSTFIX ARCH_DL_POSTFIX,\n#ifndef ANDROID\n\t\tARCH_DL_POSTFIX,\n#endif\n\t};\n\tsize_t i, j;\n\tchar temp[MAX_OSPATH];\n\tplugin_t *newplug;\n\tint nlen = strlen(file);\n\tqboolean success = false;\n\tqboolean (QDECL *initfunction)(plugcorefuncs_t*);\n\tdllfunction_t funcs[] =\n\t{\n\t\t{(void**)&initfunction, \"FTEPlug_Init\"},\n\t\t{NULL,NULL},\n\t};\n\n\t//reject obviously invalid names\n\tif (!*file || strchr(file, '/') || strchr(file, '\\\\'))\n\t\treturn NULL;\n\n\tPlug_CleanName(file, temp, sizeof(temp));\n\tfor (newplug = plugs; newplug; newplug = newplug->next)\n\t{\n\t\tif (!Q_strcasecmp(newplug->name, temp))\n\t\t\treturn newplug;\n\t}\n\n\tif (COM_CheckParm(\"-noplugins\"))\n\t\treturn NULL;\n\n\tnewplug = Z_Malloc(sizeof(plugin_t)+strlen(temp)+1);\n\tnewplug->name = (char*)(newplug+1);\n\tstrcpy(newplug->name, temp);\n\n\t//[basedir|binroot]fteplug_%s[_cpu][.so]\n\tfor (i = 0; i < countof(prefixes) && !newplug->lib; i++)\n\t{\n\t\t//if the name matches a postfix then just go with that\n\t\tfor (j = 0; j < countof(postfixes); j++)\n\t\t{\n\t\t\tint pfl = strlen(postfixes[j]);\n\t\t\tif (nlen > pfl && !Q_strcasecmp(file+nlen-pfl, postfixes[j]))\n\t\t\t\tbreak;\n\t\t}\n\t\tif (j < countof(postfixes))\n\t\t{\t//already postfixed, don't mess with the name given\n\t\t\t//mandate the fteplug_ prefix (don't let them load random dlls)\n\t\t\tif (!Q_strncasecmp(file, PLUGINPREFIX, strlen(PLUGINPREFIX)))\n\t\t\t\tif (FS_SystemPath(file, prefixes[i], newplug->filename, sizeof(newplug->filename)))\n\t\t\t\t\tnewplug->lib = Sys_LoadLibrary(newplug->filename, funcs);\n\t\t}\n\t\telse\n\t\t{\t//otherwise scan for it\n\t\t\tfor (j = 0; j < countof(postfixes) && !newplug->lib; j++)\n\t\t\t{\n\t\t\t\tif (FS_SystemPath(va(PLUGINPREFIX\"%s%s\", file, postfixes[j]), prefixes[i], newplug->filename, sizeof(newplug->filename)))\n\t\t\t\t\tnewplug->lib = Sys_LoadLibrary(newplug->filename, funcs);\n\t\t\t}\n\t\t}\n\t}\n\n#ifdef _WIN32\n\t{\n\t\tchar *mssuck;\n\t\twhile ((mssuck=strchr(newplug->filename, '\\\\')))\n\t\t\t*mssuck = '/';\n\t}\n#endif\n\n\tnewplug->next = plugs;\n\tplugs = newplug;\n\tif (newplug->lib)\n\t{\n\t\tCon_DPrintf(\"Created plugin %s\\n\", file);\n\n\t\tcurrentplug = newplug;\n\t\tsuccess = initfunction(PlugBI_GetEngineInterface(plugcorefuncs_name, sizeof(plugcorefuncs_t)));\n\t}\n\telse\n\t{\n\t\tunsigned int u;\n\t\tfor (u = 0; staticplugins[u].name; u++)\n\t\t{\n\t\t\tif (!Q_strcasecmp(file, staticplugins[u].name))\n\t\t\t{\n\t\t\t\tCon_DPrintf(\"Activated module %s\\n\", file);\n\t\t\t\tnewplug->lib = NULL;\n\n\t\t\t\tQ_strncpyz(newplug->filename, staticplugins[u].name, sizeof(newplug->filename));\n\t\t\t\tcurrentplug = newplug;\n\t\t\t\tsuccess = staticplugins[u].initfunction();\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\tif (!success)\n\t{\n\t\tPlug_Close(newplug);\n\t\treturn NULL;\n\t}\n\n\n#ifndef SERVERONLY\n\tif (newplug->reschange)\n\t\tnewplug->reschange(vid.width, vid.height, false);\n#endif\n\n\tcurrentplug = NULL;\n\n\treturn newplug;\n}\nstatic void Plug_Load_Update(const char *name, qboolean blocked)\n{\n\tif (blocked)\n\t{\t//plugins can be blocked by gametypes (to prevents conflicts)\n\t\tplugin_t *plug;\n\t\tfor (plug = plugs; plug; plug = plug->next)\n\t\t{\n\t\t\tif (!strcmp(plug->name, name))\n\t\t\t{\n\t\t\t\tPlug_Close(plug);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n\telse\n\t\tPlug_Load(name);\n}\n\nstatic int QDECL Plug_EnumeratedRoot (const char *name, qofs_t size, time_t mtime, void *param, searchpathfuncs_t *spath)\n{\n\tchar vmname[MAX_QPATH];\n\tint len;\n\tchar *dot;\n\tif (!strncmp(name, PLUGINPREFIX, strlen(PLUGINPREFIX)))\n\t\tname += 8;\n\tQ_strncpyz(vmname, name, sizeof(vmname));\n\tlen = strlen(vmname);\n\tlen -= strlen(ARCH_CPU_POSTFIX ARCH_DL_POSTFIX);\n\tif (!strcmp(vmname+len, ARCH_CPU_POSTFIX ARCH_DL_POSTFIX))\n\t\tvmname[len] = 0;\n\telse\n\t{\n\t\tdot = strchr(vmname, '.');\n\t\tif (dot)\n\t\t\t*dot = 0;\n\t}\n\tlen = strlen(vmname);\n\tif (len > 0 && vmname[len-1] == '_')\n\t\tvmname[len-1] = 0;\n\tif (!Plug_Load(vmname))\n\t\tCon_Printf(\"Couldn't load plugin %s\\n\", vmname);\n\n\treturn true;\n}\n\nstatic void QDECL Plug_Con_Print(const char *text)\n{\n\tCon_Printf(\"%s\", text);\n}\nstatic quintptr_t QDECL Plug_Sys_Milliseconds(void)\n{\n\treturn Sys_DoubleTime()*1000u;\n}\n\nqboolean VARGS PlugBI_ExportFunction(const char *name, funcptr_t function)\n{\n\tif (!currentplug)\n\t\treturn false;\n\tif (!strcmp(name, \"Tick\"))\t\t\t\t\t//void(int realtime)\n\t\tcurrentplug->tick = function;\n\telse if (!strcmp(name, \"Shutdown\"))\t\t\t//void()\n\t\tcurrentplug->shutdown = function;\n\telse if (!strcmp(name, \"MayShutdown\")||!strcmp(name, \"MayUnload\"))\n\t\tcurrentplug->mayshutdown = function;\n#ifdef HAVE_CLIENT\n\telse if (!strcmp(name, \"ConsoleLink\"))\n\t\tcurrentplug->consolelink = function;\n\telse if (!strcmp(name, \"ConsoleLinkMouseOver\"))\n\t\tcurrentplug->consolelinkmouseover = function;\n\telse if (!strcmp(name, \"ConExecuteCommand\"))\n\t\tcurrentplug->conexecutecommand = function;\n\telse if (!strcmp(name, \"MenuEvent\"))\n\t\tcurrentplug->menufunction = function;\n\telse if (!strcmp(name, \"UpdateVideo\"))\n\t\tcurrentplug->reschange = function;\n\telse if (!strcmp(name, \"SbarBase\"))\t\t\t//basic SBAR.\n\t\tcurrentplug->sbarlevel[0] = function;\n\telse if (!strcmp(name, \"SbarSupplement\"))\t//supplementry stuff - teamplay\n\t\tcurrentplug->sbarlevel[1] = function;\n\telse if (!strcmp(name, \"SbarOverlay\"))\t\t//overlay - scoreboard type stuff.\n\t\tcurrentplug->sbarlevel[2] = function;\n\telse if (!strcmp(name, \"ConnectionlessClientPacket\"))\n\t\tcurrentplug->connectionlessclientpacket = function;\n\telse if (!strcmp(name, \"ServerMessageEvent\"))\n\t\tcurrentplug->svmsgfunction = function;\n\telse if (!strcmp(name, \"ChatMessageEvent\"))\n\t\tcurrentplug->chatmsgfunction = function;\n\telse if (!strcmp(name, \"CenterPrintMessage\"))\n\t\tcurrentplug->centerprintfunction = function;\n\telse if (!strcmp(name, \"S_LoadSound\"))\t//a hook for loading extra types of sound (wav, mp3, ogg, midi, whatever you choose to support)\n\t\tS_RegisterSoundInputPlugin(currentplug, function);\n#endif\n\telse if (!strncmp(name, \"FS_RegisterArchiveType_\", 23))\t//module as in pak/pk3\n\t\tFS_RegisterFileSystemType(currentplug, name+23, function, true);\n\telse\n\t\treturn 0;\n\treturn 1;\n}\n\n//retrieve a plugin's name\nstatic qboolean QDECL PlugBI_GetPluginName(int plugnum, char *outname, size_t namesize)\n{\n\tplugin_t *plug;\n\t//int plugnum (0 for current), char *buffer, int bufferlen\n\n\tif (plugnum <= 0)\n\t{\n\t\tif (!currentplug)\n\t\t\treturn false;\n\t\telse if (plugnum == -1 && currentplug->lib)\t//plugin file name\n\t\t\tQ_strncpyz(outname, currentplug->filename, namesize);\n\t\telse\t//plugin name\n\t\t\tQ_strncpyz(outname, currentplug->name, namesize);\n\t\treturn true;\n\t}\n\n\tfor (plug = plugs; plug; plug = plug->next)\n\t{\n\t\tif (--plugnum == 0)\n\t\t{\n\t\t\tQ_strncpyz(outname, plug->name, namesize);\n\t\t\treturn true;\n\t\t}\n\t}\n\treturn false;\n}\n\nstatic qboolean QDECL PlugBI_ExportInterface(const char *name, void *interfaceptr, size_t structsize)\n{\n#if defined(PLUGINS) && !defined(SERVERONLY)\n#ifdef HAVE_MEDIA_DECODER\n\tif (!strcmp(name, \"Media_VideoDecoder\"))\n\t\treturn Media_RegisterDecoder(currentplug, interfaceptr);\n#endif\n#ifdef HAVE_MEDIA_ENCODER\n\tif (!strcmp(name, \"Media_VideoEncoder\"))\n\t\treturn Media_RegisterEncoder(currentplug, interfaceptr);\n#endif\n#endif\n\tif (!strcmp(name, \"Crypto\"))\n\t\treturn NET_RegisterCrypto(currentplug, interfaceptr);\n#if defined(Q3SERVER)||defined(Q3CLIENT)\n\tif (!strcmp(name, \"Quake3Plugin\") && sizeof(*q3) == structsize)\n\t{\n\t\tif (q3plug)\n\t\t{\n\t\t\tstruct plugin_s *p = currentplug;\n\t\t\tPlug_Close(q3plug);\n\t\t\tcurrentplug = p;\n\t\t}\n\t\tq3 = interfaceptr;\n\t\tq3plug = currentplug;\n\t\treturn true;\n\t}\n#endif\n#ifdef HAVE_CLIENT\n\tif (!strcmp(name, plugvrfuncs_name))\n\t\treturn R_RegisterVRDriver(currentplug, interfaceptr);\n\tif (!strcmp(name, plugimageloaderfuncs_name))\n\t\treturn Image_RegisterLoader(currentplug, interfaceptr);\n\tif (!strcmp(name, plugmaterialloaderfuncs_name))\n\t\treturn Material_RegisterLoader(currentplug, interfaceptr);\n#endif\n#ifdef PACKAGEMANAGER\n\tif (!strcmp(name, plugupdatesourcefuncs_name))\n\t\treturn PM_RegisterUpdateSource(currentplug, interfaceptr);\n#endif\n\treturn false;\n}\n\nstatic cvar_t *QDECL Plug_Cvar_GetNVFDG(const char *name, const char *defaultvalue, unsigned int flags, const char *description, const char *groupname)\n{\n\tif (!defaultvalue)\n\t\treturn Cvar_FindVar(name);\n\treturn Cvar_Get2(name, defaultvalue, flags&1, description, groupname);\n}\n\nstatic void QDECL Plug_Cmd_TokenizeString(const char *text)\n{\n\tCmd_TokenizeString(text, false, false);\n}\nstatic void QDECL Plug_Cmd_ShiftArgs(int args)\n{\n\tCmd_ShiftArgs(args, false);\n}\n//void Cmd_Args(char *buffer, int buffersize)\nstatic void QDECL Plug_Cmd_Args(char *buffer, int maxsize)\n{\n\tchar *args;\n\targs = Cmd_Args();\n\tif (strlen(args)+1>maxsize)\n\t{\n\t\tif (maxsize)\n\t\t\t*buffer = 0;\n\t}\n\telse\n\t\tstrcpy(buffer, args);\n}\n//void Cmd_Argv(int num, char *buffer, int buffersize)\nstatic char *QDECL Plug_Cmd_Argv(int argn, char *outbuffer, size_t buffersize)\n{\n\tchar *args;\n\targs = Cmd_Argv(argn);\n\t\n\tif (strlen(args)+1>buffersize)\n\t{\n\t\tif (buffersize)\n\t\t\t*outbuffer = 0;\n\t}\n\telse\n\t\tstrcpy(outbuffer, args);\n\treturn args;\n}\n//int Cmd_Argc(void)\nstatic int QDECL Plug_Cmd_Argc(void)\n{\n\treturn Cmd_Argc();\n}\n\n//void Cvar_SetString (char *name, char *value);\nstatic void QDECL Plug_Cvar_SetString(const char *name, const char *value)\n{\n\tcvar_t *var;\n\tif (!value)\n\t\tvar = Cvar_FindVar(name);\n\telse\n\t\tvar = Cvar_Get(name, value, 0, \"Plugin vars\");\n\tif (var)\n\t\tCvar_Set(var, value);\n}\n//void Cvar_SetString (char *name, char *value);\nstatic void QDECL Plug_Cvar_ForceSetString(const char *name, const char *value)\n{\n\tcvar_t *var = Cvar_Get(name, value, 0, \"Plugin vars\");\n\tif (var)\n\t\tCvar_ForceSet(var, value);\n}\n\n//void Cvar_SetFloat (char *name, float value);\nstatic void QDECL Plug_Cvar_SetFloat(const char *cvarname, float newvalue)\n{\n\tcvar_t *var = Cvar_Get(cvarname, \"\", 0, \"Plugin vars\");\t//\"\" because I'm lazy\n\tif (var)\n\t\tCvar_SetValue(var, newvalue);\n}\n\n//void Cvar_GetFloat (char *name);\nstatic float QDECL Plug_Cvar_GetFloat(const char *cvarname)\n{\n\tint ret;\n\tcvar_t *var;\n#ifndef CLIENTONLY\n\tif (!strcmp(cvarname, \"sv.state\"))\t//ugly hack\n\t\treturn sv.state;\n\telse\n#endif\n\t{\n\t\tvar = Cvar_Get(cvarname, \"\", 0, \"Plugin vars\");\n\t\tif (var)\n\t\t\treturn var->value;\n\t\telse\n\t\t\treturn 0;\n\t}\n\treturn ret;\n}\n\n//qboolean Cvar_GetString (char *name, char *retstring, int sizeofretstring);\nstatic qboolean QDECL Plug_Cvar_GetString(const char *name, char *outbuffer, quintptr_t sizeofbuffer)\n{\n\tcvar_t *var;\n\n\tif (!strcmp(name, \"sv.mapname\"))\n\t{\n#ifdef CLIENTONLY\n\t\tQ_strncpyz(outbuffer, \"\", sizeofbuffer);\n#else\n\t\tQ_strncpyz(outbuffer, svs.name, sizeofbuffer);\n#endif\n\t}\n\telse\n\t{\n\t\tvar = Cvar_Get(name, \"\", 0, \"Plugin vars\");\n\t\tif (!var)\n\t\t\treturn false;\n\t\tif (strlen(var->name)+1 > sizeofbuffer)\n\t\t\treturn false;\n\n\t\tstrcpy(outbuffer, var->string);\n\t}\n\n\treturn true;\n}\n\n//void Cmd_AddText (char *text, qboolean insert);\t//abort the entire engine.\nstatic void QDECL Plug_Cmd_AddText(const char *text, qboolean insert)\n{\n\tint level = RESTRICT_LOCAL;\n\tif (insert)\n\t\tCbuf_InsertText(text, level, false);\n\telse\n\t\tCbuf_AddText(text, level);\n}\n\nstatic qboolean QDECL Plug_Cmd_IsInsecure(void)\n{\n\treturn Cmd_IsInsecure();\n}\n\nstatic int plugincommandarraylen;\ntypedef struct {\n\tplugin_t *plugin;\n\tchar command[64];\n\txcommand_t func;\n} plugincommand_t;\nstatic plugincommand_t *plugincommandarray;\nvoid Plug_Command_f(void)\n{\n\tint i;\n\tchar *cmd = Cmd_Argv(0);\n\tplugin_t *oldplug = currentplug;\n\tfor (i = 0; i < plugincommandarraylen; i++)\n\t{\n\t\tif (!plugincommandarray[i].func)\n\t\t\tcontinue;\t//don't check commands who's owners died.\n\n\t\tif (Q_strcasecmp(plugincommandarray[i].command, cmd))\t//not the right command\n\t\t\tcontinue;\n\n\t\tcurrentplug = plugincommandarray[i].plugin;\n\n\t\tplugincommandarray[i].func();\n\t\tbreak;\n\t}\n\n\tcurrentplug = oldplug;\n}\n\nstatic qboolean QDECL Plug_Cmd_AddCommand(const char *name, xcommand_t func, const char *desc)\n{\n\tint i;\n\tif (!currentplug)\n\t\treturn false;\n\tfor (i = 0; i < plugincommandarraylen; i++)\n\t{\n\t\tif (!plugincommandarray[i].plugin)\n\t\t\tbreak;\n\t\tif (plugincommandarray[i].plugin == currentplug)\n\t\t{\n\t\t\tif (!strcmp(name, plugincommandarray[i].command))\n\t\t\t\treturn true;\t//already registered\n\t\t}\n\t}\n\tif (i == plugincommandarraylen)\n\t{\n\t\tplugincommandarraylen++;\n\t\tplugincommandarray = BZ_Realloc(plugincommandarray, plugincommandarraylen*sizeof(plugincommand_t));\n\t}\n\n\tQ_strncpyz(plugincommandarray[i].command, name, sizeof(plugincommandarray[i].command));\n\tif (!Cmd_AddCommandD(plugincommandarray[i].command, Plug_Command_f, desc))\n\t\treturn false;\n\tplugincommandarray[i].plugin = currentplug;\t//worked\n\tplugincommandarray[i].func = func;\t//worked\n\treturn true;\n}\nstatic qboolean QDECL Plug_Cmd_AddCommandOld(const char *name)\n{\n\tint i;\n\tif (!currentplug)\n\t\treturn false;\n\tfor (i = 0; i < plugincommandarraylen; i++)\n\t{\n\t\tif (!plugincommandarray[i].plugin)\n\t\t\tbreak;\n\t\tif (plugincommandarray[i].plugin == currentplug)\n\t\t{\n\t\t\tif (!strcmp(name, plugincommandarray[i].command))\n\t\t\t\treturn true;\t//already registered\n\t\t}\n\t}\n\tif (i == plugincommandarraylen)\n\t{\n\t\tplugincommandarraylen++;\n\t\tplugincommandarray = BZ_Realloc(plugincommandarray, plugincommandarraylen*sizeof(plugincommand_t));\n\t}\n\n\tQ_strncpyz(plugincommandarray[i].command, name, sizeof(plugincommandarray[i].command));\n\tif (!Cmd_AddCommand(plugincommandarray[i].command, Plug_Command_f))\n\t\treturn false;\n\tplugincommandarray[i].plugin = currentplug;\t//worked\n\tplugincommandarray[i].func = NULL;\t//worked\n\treturn true;\n}\nstatic void Plug_FreeConCommands(plugin_t *plug)\n{\n\tint i;\n\tfor (i = 0; i < plugincommandarraylen; i++)\n\t{\n\t\tif (plugincommandarray[i].plugin == plug)\n\t\t{\n\t\t\tplugincommandarray[i].plugin = NULL;\n\t\t\tplugincommandarray[i].func = NULL;\n\t\t\tCmd_RemoveCommand(plugincommandarray[i].command);\n\t\t\t*plugincommandarray[i].command = 0;\n\t\t}\n\t}\n}\n\ntypedef enum{\n\tSTREAM_NONE,\n\tSTREAM_SOCKET,\n\tSTREAM_VFS,\n\tSTREAM_WEB,\n} plugstream_e;\n\ntypedef struct {\n\tplugin_t *plugin;\n\tplugstream_e type;\n\tint socket;\n\tvfsfile_t *vfs;\n\tstruct dl_download *dl;\n\tstruct {\n\t\tchar filename[MAX_QPATH];\n//\t\tqbyte *buffer;\n//\t\tint buflen;\n//\t\tint curlen;\n//\t\tint curpos;\n\t} file;\n} pluginstream_t;\nstatic pluginstream_t *pluginstreamarray;\nstatic unsigned int pluginstreamarraylen;\n\nstatic int Plug_NewStreamHandle(plugstream_e type)\n{\n\tint i;\n\tfor (i = 0; i < pluginstreamarraylen; i++)\n\t{\n\t\tif (!pluginstreamarray[i].plugin)\n\t\t\tbreak;\n\t}\n\tif (i >= pluginstreamarraylen)\n\t{\n\t\tpluginstreamarraylen=i+16;\n\t\tpluginstreamarray = BZ_Realloc(pluginstreamarray, pluginstreamarraylen*sizeof(pluginstream_t));\n\t}\n\n\tmemset(&pluginstreamarray[i], 0, sizeof(pluginstream_t));\n\tpluginstreamarray[i].plugin = currentplug;\n\tpluginstreamarray[i].type = type;\n\tpluginstreamarray[i].socket = -1;\n\t*pluginstreamarray[i].file.filename = '\\0';\n\n\treturn i;\n}\n\n#ifdef HAVE_CLIENT\nstatic qhandle_t QDECL Plug_Con_POpen(const char *consolename)\n{\n\tint handle;\n\tif (!currentplug)\n\t\treturn -3;\t//streams depend upon current plugin context. which isn't valid in a thread.\n\thandle = Plug_NewStreamHandle(STREAM_VFS);\n\tpluginstreamarray[handle].vfs = Con_POpen(consolename);\n\treturn handle;\n}\n#endif\n\n#ifdef HAVE_PACKET\n//EBUILTIN(int, NET_TCPListen, (char *ip, int port, int maxcount));\n//returns a new socket with listen enabled.\nstatic qhandle_t QDECL Plug_Net_TCPListen(const char *localip, int localport, int maxcount)\n{\n\tint handle;\n\tint sock;\n\tstruct sockaddr_qstorage address;\n\tint _true = 1;\n\tint alen;\n\n\tnetadr_t a;\n\tif (!currentplug)\n\t\treturn -3;\t//streams depend upon current plugin context. which isn't valid in a thread.\n\tif (!localip)\n\t\tlocalip = \"tcp://0.0.0.0\";\t//pass \"[::]\" for ipv6\n\n\tif (!NET_StringToAdr(localip, localport, &a))\n\t\treturn -1;\n\tif (a.prot != NP_STREAM && a.prot != NP_DGRAM)\n\t\treturn -1;\n\talen = NetadrToSockadr(&a, &address);\n\n\tif ((sock = socket(((struct sockaddr*)&address)->sa_family, SOCK_STREAM, 0)) == -1)\n\t{\n\t\tCon_Printf(\"Failed to create socket\\n\");\n\t\treturn -2;\n\t}\n\tif (ioctlsocket (sock, FIONBIO, (u_long *)&_true) == -1)\n\t{\n\t\tclosesocket(sock);\n\t\treturn -2;\n\t}\n\tsetsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char *)&_true, sizeof(_true));\n\n\tif (bind (sock, (void *)&address, alen) == -1)\n\t{\n\t\tclosesocket(sock);\n\t\treturn -2;\n\t}\n\tif (listen (sock, maxcount) == -1)\n\t{\n\t\tclosesocket(sock);\n\t\treturn -2;\n\t}\n\n\thandle = Plug_NewStreamHandle(STREAM_SOCKET);\n\tpluginstreamarray[handle].socket = sock;\n\n\treturn handle;\n\n}\n\nstatic qhandle_t QDECL Plug_Net_Accept(qhandle_t handle, char *outaddress, int outaddresssize)\n{\n\tstruct sockaddr_qstorage address;\n\tint addrlen;\n\tint sock;\n\tint _true = 1;\n\tchar adr[MAX_ADR_SIZE];\n\n\tif (!currentplug || handle < 0 || handle >= pluginstreamarraylen || pluginstreamarray[handle].plugin != currentplug || pluginstreamarray[handle].type != STREAM_SOCKET)\n\t\treturn -2;\n\tsock = pluginstreamarray[handle].socket;\n\n\tif (sock < 0)\n\t\treturn -1;\n\n\taddrlen = sizeof(address);\n\tsock = accept(sock, (struct sockaddr *)&address, &addrlen);\n\tif (sock < 0)\n\t\treturn -1;\n\n\tif (ioctlsocket (sock, FIONBIO, (u_long *)&_true) == -1)\t//now make it non blocking.\n\t{\n\t\tclosesocket(sock);\n\t\treturn -1;\n\t}\n\n\tif (outaddresssize)\n\t{\n\t\tnetadr_t a;\n\t\tchar *s;\n\t\tSockadrToNetadr(&address, addrlen, &a);\n\t\ts = NET_AdrToString(adr, sizeof(adr), &a);\n\t\tQ_strncpyz(outaddress, s, addrlen);\n\t}\n\n\thandle = Plug_NewStreamHandle(STREAM_SOCKET);\n\tpluginstreamarray[handle].socket = sock;\n\n\treturn handle;\n}\n\nstatic qhandle_t QDECL Plug_Net_TCPConnect(const char *remoteip, int remoteport)\n{\n\tint handle;\n\tvfsfile_t *stream = FS_OpenTCP(remoteip, remoteport, false);\n\tif (!currentplug || !stream)\n\t\treturn -1;\n\thandle = Plug_NewStreamHandle(STREAM_VFS);\n\tpluginstreamarray[handle].vfs = stream;\n\treturn handle;\n\n}\n\n\nvoid Plug_Net_Close_Internal(qhandle_t handle);\nstatic int QDECL Plug_Net_SetTLSClient(qhandle_t handle, const char *certhostname)\n{\n#ifndef HAVE_SSL\n\treturn -1;\n#else\n\tpluginstream_t *stream;\n\tif (handle >= pluginstreamarraylen || pluginstreamarray[handle].plugin != currentplug)\n\t{\n\t\tCon_Printf(\"Plug_Net_SetTLSClient: socket does not belong to you (or is invalid)\\n\");\n\t\treturn -2;\n\t}\n\tstream = &pluginstreamarray[handle];\n\tif (stream->type != STREAM_VFS)\n\t{\t//not a socket - invalid\n\t\tCon_Printf(\"Plug_Net_SetTLSClient: Not a socket handle\\n\");\n\t\treturn -2;\n\t}\n\n\tstream->vfs = FS_OpenSSL(certhostname, stream->vfs, false);\n\tif (!stream->vfs)\n\t{\n\t\tPlug_Net_Close_Internal(handle);\n\t\treturn -1;\n\t}\n\treturn 0;\n#endif\n}\n\nstatic int QDECL Plug_Net_GetTLSBinding(qhandle_t handle, char *outbinddata, int *outbinddatalen)\n{\n#ifndef HAVE_SSL\n\treturn -1;\n#else\n\tpluginstream_t *stream;\n\tsize_t sz;\n\tint r;\n\tif ((size_t)handle >= pluginstreamarraylen || pluginstreamarray[handle].plugin != currentplug)\n\t{\n\t\tCon_Printf(\"Plug_Net_GetTLSBinding: socket does not belong to you (or is invalid)\\n\");\n\t\treturn -2;\n\t}\n\tstream = &pluginstreamarray[handle];\n\tif (stream->type != STREAM_VFS)\n\t{\t//not a socket - invalid\n\t\tCon_Printf(\"Plug_Net_GetTLSBinding: Not a socket handle\\n\");\n\t\treturn -2;\n\t}\n\n\tsz = *outbinddatalen;\n\tr = TLS_GetChannelBinding(stream->vfs, outbinddata, &sz);\n\t*outbinddatalen = sz;\n\treturn r;\n#endif\n}\n#endif\n\n#ifdef WEBCLIENT\nstatic void Plug_DownloadComplete(struct dl_download *dl)\n{\n\tint handle = dl->user_num;\n\tdl->file = NULL;\n\tpluginstreamarray[handle].dl = NULL;\t\t\t//download no longer needs to be canceled.\n\tpluginstreamarray[handle].type = STREAM_VFS;\t//getlen should start working now.\n}\n#endif\n\n\nstatic qhandle_t QDECL Plug_FS_Open(const char *fname, qhandle_t *outhandle, int modenum)\n{\n\t//modes:\n\t//1: read\n\t//2: write\n\n\t//char *name, int *handle, int mode\n\n\t//return value is length of the file.\n\n\tint handle;\n\t//char *data;\n\tchar *mode;\n\tvfsfile_t *f;\n\n\t*outhandle = -1;\n\tif (!currentplug)\n\t\treturn -3;\t//streams depend upon current plugin context. which isn't valid in a thread.\n\n\tswitch(modenum)\n\t{\n\tcase 1:\n\t\tmode = \"rb\";\n\t\tbreak;\n\tcase 2:\n\t\tmode = \"wb\";\n\t\tbreak;\n\tdefault:\n\t\treturn -2;\n\t}\n\tif (!strcmp(fname, \"**plugconfig\"))\n\t\tf = FS_OpenVFS(va(\"%s.cfg\", currentplug->name), mode, FS_ROOT);\n\telse if (!strncmp(fname, \"http://\", 7) || !strncmp(fname, \"https://\", 8))\n\t{\n#ifndef WEBCLIENT\n\t\tf = NULL;\n#else\n\t\tCon_Printf(\"Plugin %s requesting %s\\n\", currentplug->name, fname);\n\t\thandle = Plug_NewStreamHandle(STREAM_WEB);\n\t\tpluginstreamarray[handle].dl = HTTP_CL_Get(fname, NULL, Plug_DownloadComplete);\n\t\tpluginstreamarray[handle].dl->user_num = handle;\n\t\tpluginstreamarray[handle].dl->file = pluginstreamarray[handle].vfs = VFSPIPE_Open(2, true);\n\t\tpluginstreamarray[handle].dl->isquery = true;\n#ifdef MULTITHREAD\n\t\tDL_CreateThread(pluginstreamarray[handle].dl, NULL, NULL);\n#endif\n\t\t*outhandle = handle;\n\t\treturn VFS_GETLEN(pluginstreamarray[handle].vfs);\n#endif\n\t}\n\telse if (modenum == 2)\n\t\tf = FS_OpenVFS(fname, mode, FS_GAMEONLY);\n\telse\n\t\tf = FS_OpenVFS(fname, mode, FS_GAME);\n\tif (!f)\n\t\treturn -1;\n\thandle = Plug_NewStreamHandle(STREAM_VFS);\n\tpluginstreamarray[handle].vfs = f;\n\tQ_strncpyz(pluginstreamarray[handle].file.filename, fname, sizeof(pluginstreamarray[handle].file.filename));\n\t*outhandle = handle;\n\treturn VFS_GETLEN(pluginstreamarray[handle].vfs);\n}\nstatic int QDECL Plug_FS_Seek(qhandle_t handle, qofs_t offset)\n{\n\tpluginstream_t *stream;\n\n\tif (handle >= pluginstreamarraylen)\n\t\treturn -1;\n\tstream = &pluginstreamarray[handle];\n\tif (stream->type != STREAM_VFS)\n\t\treturn -1;\n\tVFS_SEEK(stream->vfs, offset);\n\treturn VFS_TELL(stream->vfs);\n}\n\nstatic qboolean QDECL Plug_FS_GetLength(qhandle_t handle, qofs_t *outsize)\n{\n\tpluginstream_t *stream;\n\n\tif (handle >= pluginstreamarraylen)\n\t\treturn false;\n\tstream = &pluginstreamarray[handle];\n\tif (stream->type == STREAM_VFS)\n\tif (stream->vfs->GetLen)\n\t{\n\t\t*outsize = VFS_GETLEN(stream->vfs);\n\t\treturn true;\n\t}\n\t*outsize = 0;\n\treturn false;\n}\n\nvoid Plug_Net_Close_Internal(qhandle_t handle)\n{\n\tswitch(pluginstreamarray[handle].type)\n\t{\n\tcase STREAM_NONE:\n\t\tbreak;\n\tcase STREAM_WEB:\n#ifdef WEBCLIENT\n\t\tif (pluginstreamarray[handle].dl)\n\t\t{\n\t\t\tpluginstreamarray[handle].dl->file = NULL;\n\t\t\tDL_Close(pluginstreamarray[handle].dl);\n\t\t}\n#endif\n\t\t//fall through\n\tcase STREAM_VFS:\n\t\tif (pluginstreamarray[handle].vfs)\n\t\t{\n\t\t\tif (*pluginstreamarray[handle].file.filename && pluginstreamarray[handle].vfs->WriteBytes)\n\t\t\t{\n\t\t\t\tVFS_CLOSE(pluginstreamarray[handle].vfs);\n\t\t\t\tFS_FlushFSHashWritten(pluginstreamarray[handle].file.filename);\n\t\t\t}\n\t\t\telse\n\t\t\t\tVFS_CLOSE(pluginstreamarray[handle].vfs);\n\t\t}\n\t\tpluginstreamarray[handle].vfs = NULL;\n\t\tbreak;\n\tcase STREAM_SOCKET:\n#ifdef HAVE_PACKET\n\t\tclosesocket(pluginstreamarray[handle].socket);\n#endif\n\t\tbreak;\n\t}\n\n\tpluginstreamarray[handle].type = STREAM_NONE;\n\tpluginstreamarray[handle].plugin = NULL;\n}\n\nstatic int QDECL Plug_Net_Recv(qhandle_t handle, void *dest, int destlen)\n{\n#ifdef HAVE_PACKET\n\tint read;\n#endif\n\n\tif (handle < 0 || handle >= pluginstreamarraylen || pluginstreamarray[handle].plugin != currentplug)\n\t\treturn -2;\n\tswitch(pluginstreamarray[handle].type)\n\t{\n#ifdef HAVE_PACKET\n\tcase STREAM_SOCKET:\n\t\tread = recv(pluginstreamarray[handle].socket, dest, destlen, 0);\n\t\tif (read < 0)\n\t\t{\n\t\t\tif (neterrno() == NET_EWOULDBLOCK)\n\t\t\t\treturn -1;\n\t\t\telse\n\t\t\t\treturn -2;\n\t\t}\n\t\telse if (read == 0)\n\t\t\treturn -2;\t//closed by remote connection.\n\t\treturn read;\n#endif\n\n\tcase STREAM_WEB:\n\tcase STREAM_VFS:\n\t\treturn VFS_READ(pluginstreamarray[handle].vfs, dest, destlen);\n\tdefault:\n\t\treturn -2;\n\t}\n}\nstatic int QDECL Plug_Net_Send(qhandle_t handle, void *src, int srclen)\n{\n#ifdef HAVE_PACKET\n\tint written;\n#endif\n\tif (handle < 0 || handle >= pluginstreamarraylen || pluginstreamarray[handle].plugin != currentplug)\n\t\treturn -2;\n\tswitch(pluginstreamarray[handle].type)\n\t{\n#ifdef HAVE_PACKET\n\tcase STREAM_SOCKET:\n\t\twritten = send(pluginstreamarray[handle].socket, src, srclen, 0);\n\t\tif (written < 0)\n\t\t{\n\t\t\tif (neterrno() == NET_EWOULDBLOCK)\n\t\t\t\treturn -1;\n\t\t\telse\n\t\t\t\treturn -2;\n\t\t}\n\t\telse if (written == 0)\n\t\t\treturn -2;\t//closed by remote connection.\n\t\treturn written;\n#endif\n\n\tcase STREAM_VFS:\n\t\treturn VFS_WRITE(pluginstreamarray[handle].vfs, src, srclen);\n\n\tdefault:\n\t\treturn -2;\n\t}\n}\n#ifdef HAVE_PACKET\nstatic int QDECL Plug_Net_SendTo(qhandle_t handle, void *src, int srclen, netadr_t *address)\n{\n\tint written;\n\n\tstruct sockaddr_qstorage sockaddr;\n\tif (handle == -1)\n\t{\n#ifdef HAVE_CLIENT\n\t\tNET_SendPacket(cls.sockets, srclen, src, address);\n\t\treturn srclen;\n#else\n\t\treturn -2;\n#endif\n\t}\n\n\tNetadrToSockadr(address, &sockaddr);\n\n\tif (handle < 0 || handle >= pluginstreamarraylen || pluginstreamarray[handle].plugin != currentplug)\n\t\treturn -2;\n\tswitch(pluginstreamarray[handle].type)\n\t{\n\tcase STREAM_SOCKET:\n\t\twritten = sendto(pluginstreamarray[handle].socket, src, srclen, 0, (struct sockaddr*)&sockaddr, sizeof(sockaddr));\n\t\tif (written < 0)\n\t\t{\n\t\t\tif (neterrno() == NET_EWOULDBLOCK)\n\t\t\t\treturn -1;\n\t\t\telse\n\t\t\t\treturn -2;\n\t\t}\n\t\telse if (written == 0)\n\t\t\treturn -2;\t//closed by remote connection.\n\t\treturn written;\n\tdefault:\n\t\treturn -2;\n\t}\n}\n#endif\nstatic void QDECL Plug_Net_Close(qhandle_t handle)\n{\n\tif (handle < 0 || handle >= pluginstreamarraylen || pluginstreamarray[handle].plugin != currentplug)\n\t\treturn;\n\tPlug_Net_Close_Internal(handle);\n}\n\nvoid QDECL Plug_FS_EnumerateFiles(enum fs_relative fsroot, const char *match, int (QDECL *callback)(const char *fname, qofs_t fsize, time_t mtime, void *ctx, struct searchpathfuncs_s *package), void *ctx)\n{\n\tif (fsroot == FS_GAME)\n\t\tCOM_EnumerateFiles(match, callback, ctx);\n\telse\n\t{\n\t\tchar base[MAX_OSPATH];\n\t\tif (fsroot == FS_ROOT)\n\t\t{\n\t\t\textern qboolean com_homepathenabled;\n\t\t\tif (com_homepathenabled)\n\t\t\t\tSys_EnumerateFiles(com_homepath, match, callback, ctx, NULL);\n\t\t\tSys_EnumerateFiles(com_gamepath, match, callback, ctx, NULL);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tFS_SystemPath(\"\", fsroot, base, sizeof(base));\n\t\t\tSys_EnumerateFiles(base, match, callback, ctx, NULL);\n\t\t}\n\t}\n}\n\nunsigned int Plug_BlockChecksum(const void *data, size_t datasize)\n{\t//convienience function. we use md4 for legacy reasons (every qw engine must have an implementation)\n\treturn CalcHashInt(&hash_md4, data, datasize);\n}\n\n#if defined(HAVE_SERVER) && defined(HAVE_CLIENT)\nstatic qboolean QDECL Plug_MapLog_Query(const char *packagename, const char *mapname, float *vals)\n{\n\tif (!strncmp(packagename, \"http://\", 7) || !strncmp(packagename, \"https://\", 8))\n\t{\n\t\tchar temp[MAX_OSPATH];\n\t\tif (!FS_PathURLCache(packagename, temp, sizeof(temp)))\n\t\t\treturn false;\n\t\tif (Log_CheckMapCompletion(temp, mapname, &vals[0], &vals[1], &vals[2], &vals[3]))\n\t\t\treturn true;\n\t\treturn false;\n\t}\n\treturn Log_CheckMapCompletion(packagename, mapname, &vals[0], &vals[1], &vals[2], &vals[3]);\n}\n#endif\n\nvoid Plug_CloseAll_f(void);\nvoid Plug_List_f(void);\nvoid Plug_Close_f(void);\nstatic void Plug_Load_f(void)\n{\n\tchar *plugin;\n\tplugin = Cmd_Argv(1);\n\tif (!*plugin)\n\t{\n\t\tCon_Printf(\"Loads a plugin\\n\");\n\t\tCon_Printf(\"plug_load [pluginpath]\\n\");\n\t\tCon_Printf(\"example pluginpath: blah\\n\");\n\t\tCon_Printf(\"will load \"PLUGINPREFIX\"blah\"ARCH_CPU_POSTFIX ARCH_DL_POSTFIX\"\\n\");\n\t\treturn;\n\t}\n\tif (!Plug_Load(plugin))\n\t\tCon_Printf(\"Couldn't load plugin %s\\n\", Cmd_Argv(1));\n}\n\nvoid Plug_Initialise(qboolean fromgamedir)\n{\n\tchar sys[MAX_OSPATH], disp[MAX_OSPATH];\n\n\tif (!plugfuncs)\n\t{\n\t\tCvar_Register(&plug_sbar, \"plugins\");\n\t\tCvar_Register(&plug_loaddefault, \"plugins\");\n\n\t\tCmd_AddCommand(\"plug_closeall\", Plug_CloseAll_f);\n\t\tCmd_AddCommand(\"plug_close\", Plug_Close_f);\n\t\tCmd_AddCommand(\"plug_load\", Plug_Load_f);\n\t\tCmd_AddCommand(\"plug_list\", Plug_List_f);\n\n\t\t//set up internal plugins\n\t\tplugfuncs = PlugBI_GetEngineInterface(plugcorefuncs_name, sizeof(*plugfuncs));\n\t\tcvarfuncs = plugfuncs->GetEngineInterface(plugcvarfuncs_name, sizeof(*cvarfuncs));\n\t\tcmdfuncs = plugfuncs->GetEngineInterface(plugcmdfuncs_name, sizeof(*cmdfuncs));\n\t}\n\n#ifdef SUBSERVERS\n\tif (!SSV_IsSubServer())\t//subservers will need plug_load I guess\n#endif\n\tif (plug_loaddefault.ival & 2)\n\t{\n\t\tif (!fromgamedir)\n\t\t{\n\t\t\tif (FS_DisplayPath(\"\", FS_BINARYPATH, disp, sizeof(disp)) && FS_SystemPath(\"\", FS_BINARYPATH, sys, sizeof(sys)))\n\t\t\t{\n\t\t\t\tCon_DPrintf(\"Loading plugins from \\\"%s\\\"\\n\", disp);\n\t\t\t\tSys_EnumerateFiles(sys, PLUGINPREFIX\"*\" ARCH_CPU_POSTFIX ARCH_DL_POSTFIX, Plug_EnumeratedRoot, NULL, NULL);\n\t\t\t}\n\t\t\tif (FS_DisplayPath(\"\", FS_LIBRARYPATH, disp, sizeof(disp)) && FS_SystemPath(\"\", FS_BINARYPATH, sys, sizeof(sys)))\n\t\t\t{\n\t\t\t\tCon_DPrintf(\"Loading plugins from \\\"%s\\\"\\n\", disp);\n\t\t\t\tSys_EnumerateFiles(sys, PLUGINPREFIX\"*\" ARCH_CPU_POSTFIX ARCH_DL_POSTFIX, Plug_EnumeratedRoot, NULL, NULL);\n\t\t\t}\n\t\t}\n\t}\n\tif (plug_loaddefault.ival & 1)\n\t{\n\t\tunsigned int u;\n\t\tfor (u = 0; staticplugins[u].name; u++)\n\t\t{\n\t\t\tPlug_Load(staticplugins[u].name);\n\t\t}\n\t\tPM_EnumeratePlugins(Plug_Load_Update);\n\t}\n}\n\nvoid Plug_Tick(void)\n{\n\tplugin_t *oldplug = currentplug;\n\tfor (currentplug = plugs; currentplug; currentplug = currentplug->next)\n\t{\n\t\tif (currentplug->tick)\n\t\t{\n\t\t\tdouble st = 0;\n#ifdef SERVERONLY \n\t\t\tst = sv.time;\n#elif defined(CLIENTONLY)\n\t\t\tst = cl.time;\n#else\n\t\t\tst = sv.state?sv.time:cl.time;\n#endif\n\t\t\tcurrentplug->tick(realtime, st);\n\t\t}\n\t}\n\tcurrentplug = oldplug;\n}\n\n#ifndef SERVERONLY\nvoid Plug_ResChanged(qboolean restarted)\n{\n\tplugin_t *oldplug = currentplug;\n\tfor (currentplug = plugs; currentplug; currentplug = currentplug->next)\n\t{\n\t\tif (currentplug->reschange)\n\t\t\tcurrentplug->reschange(vid.width, vid.height, restarted);\n\t}\n\tcurrentplug = oldplug;\n}\n#endif\n\n#ifndef SERVERONLY\nqboolean Plug_ConsoleLinkMouseOver(float x, float y, char *text, char *info)\n{\n\tqboolean result = false;\n\tplugin_t *oldplug = currentplug;\n\tfor (currentplug = plugs; currentplug; currentplug = currentplug->next)\n\t{\n\t\tif (currentplug->consolelinkmouseover)\n\t\t{\n\t\t\tchar buffer[8192];\n\t\t\tchar *ptr;\n\t\t\tptr = (char*)COM_QuotedString(text, buffer, sizeof(buffer)-10, false);\n\t\t\tptr += strlen(ptr);\n\t\t\t*ptr++ = ' ';\n\t\t\tCOM_QuotedString(info, ptr, sizeof(buffer)-(ptr-buffer), false);\n\n\t\t\tCmd_TokenizeString(buffer, false, false);\n\t\t\tresult = currentplug->consolelinkmouseover(x, y);\n\t\t\tif (result)\n\t\t\t\tbreak;\n\t\t}\n\t}\n\tcurrentplug = oldplug;\n\treturn result;\n}\nqboolean Plug_ConsoleLink(char *text, char *info, const char *consolename)\n{\n\tqboolean result = false;\n\tplugin_t *oldplug = currentplug;\n\tfor (currentplug = plugs; currentplug; currentplug = currentplug->next)\n\t{\n\t\tif (currentplug->consolelink)\n\t\t{\n\t\t\tchar buffer[8192];\n\t\t\tchar *ptr, oinfo = *info;\n\t\t\t*info = 0;\n\t\t\tptr = (char*)COM_QuotedString(text, buffer, sizeof(buffer)-10, false);\n\t\t\tptr += strlen(ptr);\n\t\t\t*ptr++ = ' ';\n\t\t\t*info = oinfo;\n\t\t\tptr = (char*)COM_QuotedString(info, ptr, sizeof(buffer)-(ptr-buffer)-5, false);\n\t\t\tptr += strlen(ptr);\n\t\t\t*ptr++ = ' ';\n\t\t\tCOM_QuotedString(consolename, ptr, sizeof(buffer)-(ptr-buffer), false);\n\n\t\t\tCmd_TokenizeString(buffer, false, false);\n\t\t\tresult = currentplug->consolelink();\n\t\t\tif (result)\n\t\t\t\tbreak;\n\t\t}\n\t}\n\tcurrentplug = oldplug;\n\treturn result;\n}\n\nint Plug_SubConsoleCommand(console_t *con, const char *line)\n{\n\tint ret;\n\tchar buffer[2048];\n\tplugin_t *oldplug = currentplug;\t//shouldn't really be needed, but oh well\n\tcurrentplug = con->userdata;\n\n\tQ_strncpyz(buffer, va(\"\\\"%s\\\" %s\", con->name, line), sizeof(buffer));\n\tCmd_TokenizeString(buffer, false, false);\n\tret = currentplug->conexecutecommand(0);\n\tcurrentplug = oldplug;\n\treturn ret;\n}\n#endif\n\n#ifndef SERVERONLY\nint Plug_ConnectionlessClientPacket(char *buffer, int size)\n{\n\tfor (currentplug = plugs; currentplug; currentplug = currentplug->next)\n\t{\n\t\tif (currentplug->connectionlessclientpacket)\n\t\t{\n\t\t\tswitch (currentplug->connectionlessclientpacket(buffer, size, &net_from))\n\t\t\t{\n\t\t\tcase 0:\n\t\t\t\tcontinue;\t//wasn't handled\n\t\t\tcase 1:\n\t\t\t\tcurrentplug = NULL;\t//was handled with no apparent result\n\t\t\t\treturn true;\n\t\t\tcase 2:\n#ifndef SERVERONLY\n\t\t\t\tcls.protocol = CP_PLUGIN;\t//woo, the plugin wants to connect to them!\n\t\t\t\tprotocolclientplugin = currentplug;\n#endif\n\t\t\t\tcurrentplug = NULL;\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t}\n\treturn false;\n}\n#endif\n#ifndef SERVERONLY\nvoid Plug_SBar(playerview_t *pv)\n{\n#ifdef QUAKEHUD\n\t#define sb_showscores pv->sb_showscores\n\t#define sb_showteamscores pv->sb_showteamscores\n#else\n\t#define sb_showscores 0\n\t#define sb_showteamscores 0\n#endif\n\n\tplugin_t *oc=currentplug;\n\tint ret;\n\tint cleared = false;\n\tint hudmode;\n\n\tif (!Sbar_ShouldDraw(pv))\n\t{\n\t\tSCR_TileClear (0);\n\t\treturn;\n\t}\n\n\tret = 0;\n\tif (cl.deathmatch)\n\t\thudmode = 1;\n\telse\n\t\thudmode = 2;\n\tif (!(plug_sbar.ival&4) && ((cls.protocol != CP_QUAKEWORLD && cls.protocol != CP_NETQUAKE) || M_GameType()!=MGT_QUAKE1))\n\t\tcurrentplug = NULL;\t//disable the hud if we're not running quake. q2/q3/h2 must not display the hud, allowing for a simpler install-anywhere installer that can include it.\n\telse if (!(plug_sbar.ival & hudmode))\n\t\tcurrentplug = NULL;\n\telse\n\t{\n\t\tfor (currentplug = plugs; currentplug; currentplug = currentplug->next)\n\t\t{\n\t\t\tif (currentplug->sbarlevel[0])\n\t\t\t{\n\t\t\t\t//if you don't use splitscreen, use a full videosize rect.\n\t\t\t\tR2D_ImageColours(1, 1, 1, 1); // ensure menu colors are reset\n\t\t\t\tif (!cleared)\n\t\t\t\t{\n\t\t\t\t\tcleared = true;\n\t\t\t\t\tSCR_TileClear (0);\n\t\t\t\t}\n\t\t\t\tret |= currentplug->sbarlevel[0](pv-cl.playerview, (int)r_refdef.vrect.x, (int)r_refdef.vrect.y, (int)r_refdef.vrect.width, (int)r_refdef.vrect.height, sb_showscores+sb_showteamscores*2);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\tif (!(ret & 1))\n\t{\n\t\tif (!cleared)\n\t\t\tSCR_TileClear (sb_lines);\n\t\tSbar_Draw(pv);\n\t}\n\n\tfor (currentplug = plugs; currentplug; currentplug = currentplug->next)\n\t{\n\t\tif (currentplug->sbarlevel[1])\n\t\t{\n\t\t\tR2D_ImageColours(1, 1, 1, 1); // ensure menu colors are reset\n\t\t\tret |= currentplug->sbarlevel[1](pv-cl.playerview, r_refdef.vrect.x, r_refdef.vrect.y, r_refdef.vrect.width, r_refdef.vrect.height, sb_showscores+sb_showteamscores*2);\n\t\t}\n\t}\n\n\tfor (currentplug = plugs; currentplug; currentplug = currentplug->next)\n\t{\n\t\tif (currentplug->sbarlevel[2])\n\t\t{\n\t\t\tR2D_ImageColours(1, 1, 1, 1); // ensure menu colors are reset\n\t\t\tret |= currentplug->sbarlevel[2](pv-cl.playerview, r_refdef.vrect.x, r_refdef.vrect.y, r_refdef.vrect.width, r_refdef.vrect.height, sb_showscores+sb_showteamscores*2);\n\t\t}\n\t}\n\n\tif (!(ret & 2))\n\t{\n\t\tSbar_DrawScoreboard(pv);\n\t}\n\n\n\tcurrentplug = oc;\n}\n#endif\n\nqboolean Plug_ServerMessage(char *buffer, int messagelevel)\n{\n\tqboolean ret = true;\n\n\tCmd_TokenizeString(buffer, false, false);\n\tCmd_Args_Set(buffer, strlen(buffer));\n\n\tfor (currentplug = plugs; currentplug; currentplug = currentplug->next)\n\t{\n\t\tif (currentplug->svmsgfunction)\n\t\t{\n\t\t\tret &= currentplug->svmsgfunction(messagelevel);\n\t\t}\n\t}\n\n\tCmd_Args_Set(NULL, 0);\n\n\treturn ret; // true to display message, false to supress\n}\n\nqboolean Plug_ChatMessage(char *buffer, int talkernum, int tpflags)\n{\n\tqboolean ret = true;\n\n\tCmd_TokenizeString(buffer, false, false);\n\tCmd_Args_Set(buffer, strlen(buffer));\n\n\tfor (currentplug = plugs; currentplug; currentplug = currentplug->next)\n\t{\n\t\tif (currentplug->chatmsgfunction)\n\t\t{\n\t\t\tret &= currentplug->chatmsgfunction(talkernum, tpflags);\n\t\t}\n\t}\n\n\tCmd_Args_Set(NULL, 0);\n\n\treturn ret; // true to display message, false to supress\n}\n\nqboolean Plug_CenterPrintMessage(const char *buffer, int clientnum)\n{\n\tqboolean ret = true;\n\n\tCmd_TokenizeString(buffer, false, false);\n\tCmd_Args_Set(buffer, strlen(buffer));\n\n\tfor (currentplug = plugs; currentplug; currentplug = currentplug->next)\n\t{\n\t\tif (currentplug->centerprintfunction)\n\t\t{\n\t\t\tret &= currentplug->centerprintfunction(clientnum);\n\t\t}\n\t}\n\n\tCmd_Args_Set(NULL, 0);\n\n\treturn ret; // true to display message, false to supress\n}\n\nvoid Plug_Close(plugin_t *plug)\n{\n\tint i;\n\tcurrentplug = plug;\n\tif (plug->mayshutdown && !plug->mayshutdown())\n\t{\n\t\tcurrentplug = NULL;\n\t\tCon_Printf(\"Plugin %s provides driver features, and cannot safely be unloaded at this time\\n\", plug->name);\n\t\treturn;\n\t}\n\tif (plugs == plug)\n\t\tplugs = plug->next;\n\telse\n\t{\n\t\tplugin_t *prev;\n\t\tfor (prev = plugs; prev; prev = prev->next)\n\t\t{\n\t\t\tif (prev->next == plug)\n\t\t\t\tbreak;\n\t\t}\n\t\tif (!prev)\n\t\t\tSys_Error(\"Plug_Close: not linked\\n\");\n\t\tprev->next = plug->next;\n\t}\n\n\tif (!com_workererror && plug->lib)\n\t\tCon_DPrintf(\"Closing plugin %s\\n\", plug->name);\n\n\t//ensure any active contexts provided by the plugin are closed (stuff with destroy callbacks)\n#if defined(PLUGINS) && !defined(SERVERONLY)\n#ifdef HAVE_MEDIA_DECODER\n\tMedia_UnregisterDecoder(plug, NULL);\n#endif\n#ifdef HAVE_MEDIA_ENCODER\n\tMedia_UnregisterEncoder(plug, NULL);\n#endif\n#endif\n#ifdef HAVE_CLIENT\n\tS_UnregisterSoundInputModule(plug);\n\tR_RegisterVRDriver(plug, NULL);\n\tImage_RegisterLoader(plug, NULL);\n\tMaterial_RegisterLoader(plug, NULL);\n#endif\n\tNET_RegisterCrypto(plug, NULL);\n#ifdef PACKAGEMANAGER\n\tPM_RegisterUpdateSource(currentplug, NULL);\n#endif\n\tFS_UnRegisterFileSystemModule(plug);\n\tMod_UnRegisterAllModelFormats(plug);\n\n#if defined(Q3SERVER)||defined(Q3CLIENT)\n\tif (q3plug == plug)\n\t{\n\t\tq3 = NULL;\n\t\tq3plug = NULL;\n\t}\n#endif\n\n\t//tell the plugin that everything is closed and that it should free up any lingering memory/stuff\n\t//it is still allowed to create/have open files.\n\tif (plug->shutdown)\n\t{\n\t\tplugin_t *cp = currentplug;\n\t\tcurrentplug = plug;\n\t\tplug->shutdown();\n\t\tcurrentplug = cp;\n\t}\n\n\tif (plug->lib)\n\t\tSys_CloseLibrary(plug->lib);\n\n\t//make sure anything else that was left is unlinked (stuff without destroy callbacks).\n\tfor (i = 0; i < pluginstreamarraylen; i++)\n\t{\n\t\tif (pluginstreamarray[i].plugin == plug)\n\t\t{\n\t\t\tPlug_Net_Close_Internal(i);\n\t\t}\n\t}\n\n\tPlug_FreeConCommands(plug);\n\n\tPlug_Client_Close(plug);\n\tZ_Free(plug);\n\n\tcurrentplug = NULL;\n}\n\nvoid Plug_Close_f(void)\n{\n\tplugin_t *plug;\n\tchar *name = Cmd_Argv(1);\n\tchar cleaned[MAX_OSPATH];\n\tif (Cmd_Argc()<2)\n\t{\n\t\tCon_Printf(\"Close which plugin?\\n\");\n\t\treturn;\n\t}\n\n\tname = Plug_CleanName(name, cleaned, sizeof(cleaned));\n\n\tif (currentplug)\n\t\tSys_Error(\"Plug_CloseAll_f called inside a plugin!\\n\");\n\n\tfor (plug = plugs; plug; plug = plug->next)\n\t{\n\t\tif (!Q_strcasecmp(plug->name, name))\n\t\t{\n\t\t\tPlug_Close(plug);\n\t\t\treturn;\n\t\t}\n\t}\n\n\tname = va(\"plugins/%s\", name);\n\tfor (plug = plugs; plug; plug = plug->next)\n\t{\n\t\tif (!Q_strcasecmp(plug->name, name))\n\t\t{\n\t\t\tPlug_Close(plug);\n\t\t\treturn;\n\t\t}\n\t}\n\tCon_Printf(\"Plugin %s does not appear to be loaded\\n\", Cmd_Argv(1));\n}\n\nvoid Plug_CloseAll_f(void)\n{\n\tplugin_t *p;\n\tif (currentplug)\n\t\tSys_Error(\"Plug_CloseAll_f called inside a plugin!\\n\");\n\twhile(plugs)\n\t{\n\t\tp = plugs;\n\t\twhile (p->mayshutdown && !p->mayshutdown())\n\t\t{\n\t\t\tp = p->next;\n\t\t\tif (!p)\n\t\t\t\treturn;\n\t\t}\n\t\tPlug_Close(p);\n\t}\n}\n\nint QDECL Plug_List_Print(const char *fname, qofs_t fsize, time_t modtime, void *parm, searchpathfuncs_t *spath)\n{\n\tplugin_t *plug;\n\tchar plugname[MAX_QPATH];\n\t//lots of awkward logic so we hide modules for other cpus.\n\tsize_t nl = strlen(fname);\n\tsize_t u;\n\tchar *arch_ext = ARCH_DL_POSTFIX;\n\tstatic const char *knownarch[] =\n\t{\n\t\t\"x32\", \"x64\", \"amd64\", \"x86\",\t//various x86 ABIs\n\t\t\"arm\", \"arm64\", \"armhf\",\t\t//various arm ABIs\n\t\t\"ppc\", \"unk\",\t\t\t\t\t//various misc ABIs\n\t};\n#ifdef _WIN32\n\tchar *mssuck;\n\twhile ((mssuck=strchr(fname, '\\\\')))\n\t\t*mssuck = '/';\n#endif\n\tif (!parm)\n\t{\n\t\tparm = \"\";\n\t\tarch_ext = \"\";\t//static plugins have no extension.\n\t}\n\tif (nl >= strlen(arch_ext) && !Q_strcasecmp(fname+nl-strlen(arch_ext), arch_ext))\n\t{\n\t\tnl -= strlen(arch_ext);\n\t\tfor (u = 0; u < countof(knownarch); u++)\n\t\t{\n\t\t\tsize_t al = strlen(knownarch[u]);\n\t\t\tif (!Q_strncasecmp(fname+nl-al, knownarch[u], al))\n\t\t\t{\n\t\t\t\tnl -= al;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (u == countof(knownarch) || !Q_strcasecmp(knownarch[u], ARCH_CPU_POSTFIX))\n\t\t{\n\t\t\tif (nl > sizeof(plugname)-1)\n\t\t\t\tnl = sizeof(plugname)-1;\n\t\t\tif (nl>0&&fname[nl] == '_')\n\t\t\t\tnl--;\t//ignore the _ before the ABI name.\n\t\t\tmemcpy(plugname, fname, nl);\n\t\t\tplugname[nl] = 0;\n\n\t\t\t//don't bother printing it if its already loaded.\n\t\t\tfor (plug = plugs; plug; plug = plug->next)\n\t\t\t{\n\t\t\t\tif (!Q_strncasecmp(plug->filename, parm, strlen(parm)) && !Q_strcasecmp(plug->filename+strlen(parm), fname))\n\t\t\t\t\treturn true;\n\t\t\t}\n\t\t\tCon_Printf(\"^[^1%s%s\\\\type\\\\plug_load %s\\\\^]: not loaded\\n\", (const char*)parm, fname, plugname+((!Q_strncasecmp(plugname,PLUGINPREFIX, strlen(PLUGINPREFIX)))?strlen(PLUGINPREFIX):0));\n\t\t}\n\t}\n\treturn true;\n}\n\nvoid Plug_List_f(void)\n{\n\tchar displaypath[MAX_OSPATH];\n\tchar binarypath[MAX_OSPATH];\n\tchar librarypath[MAX_OSPATH];\n\tchar rootpath[MAX_OSPATH];\n\tunsigned int u;\n\tplugin_t *plug;\n\n\tCon_Printf(\"Loaded plugins:\\n\");\n\tfor (plug = plugs; plug; plug = plug->next)\n\t\tif (plug->lib && FS_DisplayPath(plug->filename, FS_SYSTEM, displaypath, sizeof(displaypath)))\n\t\t\tCon_Printf(\"^[^2%s\\\\type\\\\plug_close %s\\\\^]: loaded\\n\", displaypath, plug->name);\n\t\telse\n\t\t\tCon_Printf(\"^[^2%s\\\\type\\\\plug_close %s\\\\^]: loaded\\n\", plug->filename, plug->name);\n\n\tif (staticplugins[0].name)\n\t{\n\t\tCon_DPrintf(\"Internal plugins:\\n\");\n\t\tfor (u = 0; staticplugins[u].name; u++)\n\t\t\tPlug_List_Print(staticplugins[u].name, 0, 0, NULL, NULL);\n\t}\n\n\tif (FS_SystemPath(\"\", FS_BINARYPATH, binarypath, sizeof(binarypath)))\n\t{\n#ifdef _WIN32\n\t\tchar *mssuck;\n\t\twhile ((mssuck=strchr(binarypath, '\\\\')))\n\t\t\t*mssuck = '/';\n#endif\n\t\tif (FS_DisplayPath(binarypath, FS_SYSTEM, displaypath, sizeof(displaypath)))\n\t\t\tCon_Printf(\"Scanning for plugins at %s:\\n\", displaypath);\n\t\tSys_EnumerateFiles(binarypath, PLUGINPREFIX\"*\" ARCH_DL_POSTFIX, Plug_List_Print, displaypath, NULL);\n\t}\n\tif (FS_SystemPath(\"\", FS_LIBRARYPATH, librarypath, sizeof(librarypath)))\n\t{\n#ifdef _WIN32\n\t\tchar *mssuck;\n\t\twhile ((mssuck=strchr(librarypath, '\\\\')))\n\t\t\t*mssuck = '/';\n#endif\n\t\tif (strcmp(librarypath, rootpath))\n\t\t{\n\t\t\tif (FS_DisplayPath(librarypath, FS_SYSTEM, displaypath, sizeof(displaypath)))\n\t\t\t\tCon_Printf(\"Scanning for plugins at %s:\\n\", displaypath);\n\t\t\tSys_EnumerateFiles(librarypath, PLUGINPREFIX\"*\" ARCH_DL_POSTFIX, Plug_List_Print, displaypath, NULL);\n\t\t}\n\t}\n\tif (FS_SystemPath(\"\", FS_ROOT, rootpath, sizeof(rootpath)))\n\t{\n#ifdef _WIN32\n\t\tchar *mssuck;\n\t\twhile ((mssuck=strchr(rootpath, '\\\\')))\n\t\t\t*mssuck = '/';\n#endif\n\t\tif (strcmp(binarypath, rootpath))\n\t\t{\n\t\t\tif (FS_DisplayPath(rootpath, FS_SYSTEM, displaypath, sizeof(displaypath)))\n\t\t\t\tCon_Printf(\"Scanning for plugins at %s:\\n\", displaypath);\n\t\t\tSys_EnumerateFiles(rootpath, PLUGINPREFIX\"*\" ARCH_DL_POSTFIX, Plug_List_Print, displaypath, NULL);\n\t\t}\n\t}\n\n\t//should probably check downloadables too.\n}\n\nvoid Plug_Shutdown(qboolean preliminary)\n{\n\tplugin_t **p;\n\tif (preliminary)\n\t{\n\t\t//close the non-block-closes plugins first, before most of the rest of the subsystems are down\n\t\tfor (p = &plugs; *p; )\n\t\t{\n\t\t\tif ((*p)->mayshutdown && !(*p)->mayshutdown())\n\t\t\t\tp = &(*p)->next;\n\t\t\telse\n\t\t\t\tPlug_Close(*p);\n\t\t}\n\t}\n\telse\n\t{\n\t\t//now that our various handles etc are closed, its safe to terminate the various driver plugins.\n\t\twhile(plugs)\n\t\t{\n\t\t\tplugs->mayshutdown = NULL;\n\t\t\tPlug_Close(plugs);\n\t\t}\n\n\t\tBZ_Free(pluginstreamarray);\n\t\tpluginstreamarray = NULL;\n\t\tpluginstreamarraylen = 0;\n\n\t\tplugincommandarraylen = 0;\n\t\tBZ_Free(plugincommandarray);\n\t\tplugincommandarray = NULL;\n\t}\n}\n\n\n\nplugcorefuncs_t plugcorefuncs =\n{\n\tPlugBI_GetEngineInterface,\n\tPlugBI_ExportFunction,\n\tPlugBI_ExportInterface,\n\tPlugBI_GetPluginName,\n\tPlug_Con_Print,\n\tSys_Error,\n\tHost_EndGame,\n\tPlug_Sys_Milliseconds,\n\tSys_DoubleTime,\n\tSys_LoadLibrary,\n\tSys_GetAddressForName,\n\tSys_CloseLibrary,\n\n\tZ_Malloc,\n\tBZ_Realloc,\n\tZ_Free,\n\tZG_Malloc,\n\tZG_Free,\n\tZG_FreeGroup,\n};\n\nstatic void *QDECL PlugBI_GetEngineInterface(const char *interfacename, size_t structsize)\n{\n\tif (!strcmp(interfacename, plugcorefuncs_name))\n\t{\n\t\tif (structsize == sizeof(plugcorefuncs))\n\t\t\treturn &plugcorefuncs;\n\t}\n\tif (!strcmp(interfacename, plugcmdfuncs_name))\n\t{\n\t\tstatic plugcmdfuncs_t funcs =\n\t\t{\n\t\t\tCOM_QuotedString,\n\t\t\tCOM_ParseType,\n\t\t\tCOM_ParseTokenOut,\n\t\t\tPlug_Cmd_TokenizeString,\n\t\t\tPlug_Cmd_ShiftArgs,\n\t\t\tPlug_Cmd_Args,\n\t\t\tPlug_Cmd_Argv,\n\t\t\tPlug_Cmd_Argc,\n\t\t\tPlug_Cmd_IsInsecure,\n\t\t\tPlug_Cmd_AddCommand,\n\t\t\tPlug_Cmd_AddText,\n\t\t};\n\n\t\tstatic struct\n\t\t{\n\t\t\tqboolean\t(QDECL*AddCommand)\t\t\t(const char *cmdname);\n\t\t\tvoid\t\t(QDECL*TokenizeString)\t\t(const char *msg);\n\t\t\tvoid\t\t(QDECL*Args)\t\t\t\t(char *buffer, int bufsize);\n\t\t\tchar *\t\t(QDECL*Argv)\t\t\t\t(int argnum, char *buffer, size_t bufsize);\n\t\t\tint\t\t\t(QDECL*Argc)\t\t\t\t(void);\n\t\t\tvoid\t\t(QDECL*AddText)\t\t\t\t(const char *text, qboolean insert);\n\t\t} oldfuncs =\n\t\t{\n\t\t\tPlug_Cmd_AddCommandOld,\n\t\t\tPlug_Cmd_TokenizeString,\n\t\t\tPlug_Cmd_Args,\n\t\t\tPlug_Cmd_Argv,\n\t\t\tPlug_Cmd_Argc,\n\t\t\tPlug_Cmd_AddText,\n\t\t};\n\t\tif (structsize == sizeof(funcs))\n\t\t\treturn &funcs;\n\t\tif (structsize == sizeof(oldfuncs))\n\t\t\treturn &oldfuncs;\n\t}\n\tif (!strcmp(interfacename, plugcvarfuncs_name))\n\t{\n\t\tstatic plugcvarfuncs_t funcs =\n\t\t{\n\t\t\tPlug_Cvar_SetString,\n\t\t\tPlug_Cvar_SetFloat,\n\t\t\tPlug_Cvar_GetString,\n\t\t\tPlug_Cvar_GetFloat,\n\t\t\tPlug_Cvar_GetNVFDG,\n\t\t\tPlug_Cvar_ForceSetString,\n\t\t};\n\t\tif (structsize == sizeof(funcs))\n\t\t\treturn &funcs;\n\t}\n\tif (!strcmp(interfacename, plugfsfuncs_name))\n\t{\n\t\tstatic plugfsfuncs_t funcs =\n\t\t{\n\t\t\tPlug_FS_Open,\n\t\t\tPlug_Net_Close,\n\t\t\tPlug_Net_Send,\n\t\t\tPlug_Net_Recv,\n\t\t\tPlug_FS_Seek,\n\t\t\tPlug_FS_GetLength,\n\n\t\t\tFS_FLocateFile,\n\t\t\tFS_OpenVFS,\n\t\t\tFS_SystemPath,\n\n\t\t\tFS_Rename,\n\t\t\tFS_Remove,\n\t\t\tPlug_FS_EnumerateFiles,\n\n\t\t\twildcmp,\n\t\t\tCOM_GetFileExtension,\n\t\t\tCOM_FileBase,\n\t\t\tCOM_CleanUpPath,\n\t\t\tPlug_BlockChecksum,\n\t\t\tFS_LoadMallocFile,\n\n\t\t\tFS_GetPackHashes,\n\t\t\tFS_GetPackNames,\n\t\t\tFS_GenCachedPakName,\n#ifdef HAVE_CLIENT\n\t\t\tFS_PureMode,\n#endif\n#ifdef Q3CLIENT\n\t\t\tFSQ3_GenerateClientPacksList,\n#endif\n\t\t};\n\t\tif (structsize == sizeof(funcs))\n\t\t\treturn &funcs;\n\t}\n\tif (!strcmp(interfacename, plugmsgfuncs_name))\n\t{\n\t\tstatic plugmsgfuncs_t funcs =\n\t\t{\n\t\t\tMSG_BeginReading,\n\t\t\tMSG_GetReadCount,\n\t\t\tMSG_ReadBits,\n\t\t\tMSG_ReadByte,\n\t\t\tMSG_ReadShort,\n\t\t\tMSG_ReadLong,\n\t\t\tMSG_ReadData,\n\t\t\tMSG_ReadString,\n\n\t\t\tMSG_BeginWriting,\n\t\t\tMSG_WriteBits,\n\t\t\tMSG_WriteByte,\n\t\t\tMSG_WriteShort,\n\t\t\tMSG_WriteLong,\n\t\t\tSZ_Write,\n\t\t\tMSG_WriteString,\n\n\t\t\tNET_CompareAdr,\n\t\t\tNET_CompareBaseAdr,\n\t\t\tNET_AdrToString,\n\t\t\tNET_StringToAdr2,\n\t\t\tNET_SendPacket,\n#ifdef HUFFNETWORK\n\t\t\tHuff_CompressionCRC,\n\t\t\tHuff_EncryptPacket,\n\t\t\tHuff_DecryptPacket,\n#endif\n\t\t};\n\t\tif (structsize == sizeof(funcs))\n\t\t\treturn &funcs;\n\t}\n#ifdef HAVE_PACKET\n\tif (!strcmp(interfacename, plugnetfuncs_name))\n\t{\n\t\tstatic plugnetfuncs_t funcs =\n\t\t{\n\t\t\tPlug_Net_TCPConnect,\n\t\t\tPlug_Net_TCPListen,\n\t\t\tPlug_Net_Accept,\n\t\t\tPlug_Net_Recv,\n\t\t\tPlug_Net_Send,\n\t\t\tPlug_Net_SendTo,\n\t\t\tPlug_Net_Close,\n\t\t\tPlug_Net_SetTLSClient,\n\t\t\tPlug_Net_GetTLSBinding,\n\n\t\t\tSys_RandomBytes,\n#ifdef HAVE_DTLS\n\t\t\tTLS_GetKnownCertificate,\n#else\n\t\t\tNULL,\n#endif\n#if defined(HAVE_DTLS) && defined(HAVE_CLIENT)\n\t\t\tCertLog_ConnectOkay,\n#else\n\t\t\tNULL,\n#endif\n\t\t};\n\t\tif (structsize == sizeof(funcs))\n\t\t\treturn &funcs;\n\t}\n#endif\n\tif (!strcmp(interfacename, plugworldfuncs_name))\n\t{\n\t\tstatic plugworldfuncs_t funcs =\n\t\t{\n\t\t\tMod_ForName,\n\t\t\tMod_FixName,\n\t\t\tMod_GetEntitiesString,\n\n\t\t\tWorld_TransformedTrace,\n\t\t\tCM_TempBoxModel,\n\n\t\t\tInfoBuf_ToString,\n\t\t\tInfoBuf_FromString,\n\t\t\tInfoBuf_SetKey,\n\t\t\tInfoBuf_ValueForKey,\n\t\t\tInfo_ValueForKey,\n\t\t\tInfo_SetValueForKey,\n#ifdef HAVE_SERVER\n\t\t\tSV_DropClient,\n\t\t\tSV_ExtractFromUserinfo,\n\t\t\tSV_ChallengePasses,\n#else\n\t\t\tNULL,\n\t\t\tNULL,\n\t\t\tNULL,\n#endif\n\t\t};\n\t\tif (structsize == sizeof(funcs))\n\t\t\treturn &funcs;\n\t}\n#ifdef HAVE_CLIENT\n\tif (!strcmp(interfacename, plug2dfuncs_name))\n\t{\n\t\tstatic plug2dfuncs_t funcs =\n\t\t{\n\t\t\tPlug_Draw_GetScreenSize,\n\t\t\tPlug_Draw_LoadImageData,\n\t\t\tPlug_Draw_LoadImageShader,\n\t\t\tPlug_Draw_LoadImagePic,\n\t\t\tPlug_Draw_ShaderFromId,\n\t\t\tPlug_Draw_UnloadImage,\n\t\t\tPlug_Draw_Image,\n\t\t\tPlug_Draw_Image2dQuad,\n\t\t\tPlug_Draw_ImageSize,\n\t\t\tPlug_Draw_Fill,\n\t\t\tPlug_Draw_Line,\n\t\t\tPlug_Draw_Character,\n\t\t\tPlug_Draw_String,\n\t\t\tPlug_Draw_CharacterH,\n\t\t\tPlug_Draw_StringH,\n\t\t\tPlug_Draw_StringWidth,\n\t\t\tPlug_Draw_ColourP,\n\t\t\tPlug_Draw_Colour4f,\n\n\t\t\tPlug_Draw_RedrawScreen,\n\n\t\t\tPlug_LocalSound,\n\n\t\t\t{\n\t\t\t\tR_ShaderGetCinematic,\n#ifdef HAVE_MEDIA_DECODER\n\t\t\t\tPlug_Media_SetState,\n\t\t\t\tPlug_Media_GetState,\n\t\t\t\tMedia_Send_Reset,\n\t\t\t\tMedia_Send_Command,\n\t\t\t\tMedia_Send_GetProperty,\n\t\t\t\tMedia_Send_MouseMove,\n\t\t\t\tMedia_Send_Resize,\n\t\t\t\tMedia_Send_GetSize,\n\t\t\t\tMedia_Send_KeyEvent,\n#endif\n\t\t\t},\n\t\t};\n\t\tif (structsize == sizeof(funcs))\n\t\t\treturn &funcs;\n\t}\n\tif (!strcmp(interfacename, plug3dfuncs_name))\n\t{\n\t\tstatic plug3dfuncs_t funcs =\n\t\t{\n\t\t\tMod_ForName,\n\t\t\tPlug_Scene_ModelToId,\n\t\t\tPlug_Scene_ModelFromId,\n\t\t\tR_RemapShader,\n\t\t\tPlug_Scene_ShaderForSkin,\n\t\t\tMod_RegisterSkinFile,\n\t\t\tMod_LookupSkin,\n\t\t\tMod_TagNumForName,\n\t\t\tMod_GetTag,\n\t\t\tMod_ClipDecal,\n\n\t\t\tSurf_NewMap,\n\t\t\tPlug_Scene_Clear,\n\t\t\tV_AddAxisEntity,\n\t\t\tPlug_Scene_AddPolydata,\n\n\t\t\tCL_NewDlight,\n\t\t\tCL_AllocDlightOrg,\n\t\t\tR_CalcModelLighting,\n\t\t\tPlug_Scene_RenderScene,\n\t\t};\n\t\tif (structsize == sizeof(funcs))\n\t\t\treturn &funcs;\n\t}\n\tif (!strcmp(interfacename, plugclientfuncs_name))\n\t{\n\t\tstatic plugclientfuncs_t funcs =\n\t\t{\n\t\t\tPlug_CL_GetStats,\n\t\t\tPlug_GetPlayerInfo,\n\t\t\tPlug_GetNetworkInfo,\n\t\t\tPlug_GetLocalPlayerNumbers,\n\t\t\tPlug_GetLocationName,\n\t\t\tPlug_GetLastInputFrame,\n\t\t\tPlug_GetServerInfoRaw,\n\t\t\tPlug_GetServerInfoBlob,\n\t\t\tPlug_SetUserInfo,\n\t\t\tPlug_SetUserInfoBlob,\n\t\t\tPlug_GetUserInfoBlob,\n#if defined(HAVE_SERVER) && defined(HAVE_CLIENT)\n\t\t\tPlug_MapLog_Query,\n#else\n\t\t\tNULL,\n#endif\n#ifdef QUAKEHUD\n\t\t\tPlug_GetTeamInfo,\n\t\t\tPlug_GetWeaponStats,\n\t\t\tPlug_GetTrackerOwnFrags,\n\t\t\tPlug_GetPredInfo,\n#else\n\t\t\tNULL,\n\t\t\tNULL,\n\t\t\tNULL,\n\t\t\tNULL,\n#endif\n\n\t\t\tCon_ClearNotify,\n\t\t\tPlug_CL_ClearState,\n\t\t\tPlug_CL_SetLoadscreenState,\n\t\t\tPlug_CL_UpdateGameTime,\n\n\t\t\tCvar_ForceCheatVars,\n\t\t\tDL_Begun,\n\t\t\tCL_DownloadFinished,\n\t\t\tCL_DownloadFailed,\n\t\t};\n\t\tif (structsize == sizeof(funcs))\n\t\t\treturn &funcs;\n\t}\n\tif (!strcmp(interfacename, pluginputfuncs_name))\n\t{\n\t\tstatic pluginputfuncs_t funcs =\n\t\t{\n\t\t\tPlug_SetMenuFocus,\n\t\t\tPlug_HasMenuFocus,\n\n\t\t\tMenu_Push,\n\t\t\tMenu_Unlink,\n\n\t\t\t//for menu input\n\t\t\tPlug_Key_GetKeyCode,\n\t\t\tPlug_Key_GetKeyName,\n\t\t\tM_FindKeysForBind,\n\t\t\tPlug_Key_GetKeyBind,\n\t\t\tPlug_Key_SetKeyBind,\n\n\t\t\tPlug_Input_IsKeyDown,\n\t\t\tPlug_Input_ClearKeyStates,\n\t\t\tPlug_Input_SetSensitivityScale,\n\t\t\tPlug_Input_GetMoveCount,\n\t\t\tPlug_Input_GetMoveEntry,\n\n\t\t\tSys_Clipboard_PasteText,\n\t\t\tSys_SaveClipboard,\n\t\t\tutf8_decode,\n\t\t\tutf8_encode,\n\n\t\t\tIN_GetKeyDest,\n\t\t\tIN_KeyEvent,\n\t\t\tIN_MouseMove,\n\t\t\tIN_JoystickAxisEvent,\n\t\t\tIN_Accelerometer,\n\t\t\tIN_Gyroscope,\n\n\t\t\tIN_SetHandPosition,\n\t\t};\n\t\tif (structsize == sizeof(funcs))\n\t\t\treturn &funcs;\n\t}\n\tif (!strcmp(interfacename, plugsubconsolefuncs_name))\n\t{\n\t\tstatic plugsubconsolefuncs_t funcs =\n\t\t{\n\t\t\tPlug_Con_POpen,\n\t\t\tPlug_Con_SubPrint,\n\t\t\tPlug_Con_RenameSub,\n\t\t\tPlug_Con_IsActive,\n\t\t\tPlug_Con_SetActive,\n\t\t\tPlug_Con_Destroy,\n\t\t\tPlug_Con_NameForNum,\n\t\t\tPlug_Con_GetConsoleFloat,\n\t\t\tPlug_Con_SetConsoleFloat,\n\t\t\tPlug_Con_GetConsoleString,\n\t\t\tPlug_Con_SetConsoleString,\n\t\t};\n\t\tif (structsize == sizeof(funcs))\n\t\t\treturn &funcs;\n\t}\n\tif (!strcmp(interfacename, plugaudiofuncs_name))\n\t{\n\t\tstatic plugaudiofuncs_t funcs =\n\t\t{\n\t\t\tPlug_LocalSound,\n\t\t\tPlug_S_RawAudio,\n\n\t\t\tS_Spacialize,\n\t\t\tS_UpdateReverb,\n\t\t\tPlug_S_PrecacheSound,\n\t\t\tS_StartSound,\n\t\t\tS_GetChannelLevel,\n\t\t\tS_Voip_ClientLoudness,\n\t\t\tMedia_NamedTrack\n\t\t};\n\t\tif (structsize == sizeof(funcs))\n\t\t\treturn &funcs;\n\t}\n\n#ifdef CL_MASTER\n\tif (!strcmp(interfacename, plugmasterfuncs_name))\n\t{\n\t\tstatic plugmasterfuncs_t funcs =\n\t\t{\n\t\t\tNET_StringToAdr2,\n\t\t\tNET_AdrToString,\n\t\t\tMaster_InfoForServer,\n\t\t\tCL_QueryServers,\n\t\t\tMaster_QueryServer,\n\t\t\tMaster_CheckPollSockets,\n\t\t\tMaster_TotalCount,\n\t\t\tMaster_InfoForNum,\n\t\t\tMaster_ReadKeyString,\n\t\t\tMaster_ReadKeyFloat,\n\t\t\tMasterInfo_WriteServers,\n\t\t\tMaster_ServerToString,\n\t\t};\n\t\tif (structsize == sizeof(funcs))\n\t\t\treturn &funcs;\n\t}\n#endif\n\tif (!strcmp(interfacename, plugimagefuncs_name))\n\t{\n\t\tstatic plugimagefuncs_t funcs =\n\t\t{\n\t\t\tImage_BlockSizeForEncoding,\n\t\t\tImage_FormatName,\n\t\t};\n\t\tif (structsize == sizeof(funcs))\n\t\t\treturn &funcs;\n\t}\n#endif\n\n#ifdef SUPPORT_ICE\n\tif (!strcmp(interfacename, ICE_API_CURRENT) && structsize == sizeof(iceapi))\n\t\treturn &iceapi;\n#endif\n#ifdef USERBE\n\tif (!strcmp(interfacename, plugrbefuncs_name))\n\t{\n\t\tstatic rbeplugfuncs_t funcs =\n\t\t{\n\t\t\tRBEPLUGFUNCS_VERSION,\n\t\t\tsizeof(wedict_t),\n\n\t\t\tWorld_RegisterPhysicsEngine,\n\t\t\tWorld_UnregisterPhysicsEngine,\n\t\t\tWorld_GenerateCollisionMesh,\n\t\t\tWorld_ReleaseCollisionMesh,\n\t\t\tWorld_LinkEdict,\n\n\t\t\tVectorAngles,\n\t\t\tAngleVectors\n\t\t};\n\t\tif (structsize == sizeof(funcs))\n\t\t\treturn &funcs;\n\t}\n#endif\n#ifdef MULTITHREAD\n\tif (!strcmp(interfacename, plugthreadfuncs_name))\n\t{\n\t\tstatic plugthreadfuncs_t funcs =\n\t\t{\n\t\t\tSys_CreateMutex,\n\t\t\tSys_LockMutex,\n\t\t\tSys_UnlockMutex,\n\t\t\tSys_DestroyMutex,\n\n\t\t\tCOM_AddWork,\n\t\t\tCOM_WorkerPartialSync,\n\t\t};\n\n\t\tif (structsize == sizeof(funcs))\n\t\t\treturn &funcs;\n\t}\n#endif\n#ifdef VM_ANY\n\tif (!strcmp(interfacename, plugq3vmfuncs_name))\n\t{\n\t\tstatic plugq3vmfuncs_t funcs =\n\t\t{\n\t\t\tVM_Create,\n\t\t\tVM_NonNative,\n\t\t\tVM_MemoryBase,\n\t\t\tVM_Call,\n\t\t\tVM_Destroy,\n\t\t};\n\t\tif (structsize == sizeof(funcs))\n\t\t\treturn &funcs;\n\t}\n#endif\n#ifdef SKELETALMODELS\n\tif (!strcmp(interfacename, plugmodfuncs_name))\n\t{\n\t\tstatic plugmodfuncs_t funcs =\n\t\t{\n\t\t\tMODPLUGFUNCS_VERSION,\n\n\t\t\tPlug_RegisterModelFormatText,\n\t\t\tPlug_RegisterModelFormatMagic,\n\t\t\tPlug_UnRegisterModelFormat,\n\t\t\tPlug_UnRegisterAllModelFormats,\n\n\t\t\tCOM_StripExtension,\n\n\t\t\tR_ConcatTransforms,\n\t\t\tMatrix3x4_Invert_Simple,\n\t\t\tVectorAngles,\n\t\t\tAngleVectors,\n\t\t\tGenMatrixPosQuat4Scale,\n\t\t\tQuaternionSlerp,\n\n\t\t\tAlias_ForceConvertBoneData,\n\n#ifdef HAVE_CLIENT\n\t\t\tImage_GetTexture,\n#else\n\t\t\tNULL,\n#endif\n\t\t\tMod_AccumulateTextureVectors,\n\t\t\tMod_NormaliseTextureVectors,\n\t\t\tMod_ForName,\n\n\t\t\tMod_FindName,\n\t\t\tMod_LoadEntitiesBlob,\n\t\t\tMod_LoadMapArchive,\n\t\t\tBIH_Build,\n\t\t\tBIH_BuildAlias,\n\t\t\tFragment_ClipPlaneToBrush,\n#ifdef HAVE_CLIENT\n\t\t\tMod_RegisterBasicShader,\n\t\t\tMod_Batches_Build,\n\t\t\tSurf_RenderDynamicLightmaps,\n\t\t\tV_AddNewEntity,\n#else\n\t\t\tNULL,\n\t\t\tNULL,\n\t\t\tNULL,\n\t\t\tNULL,\n#endif\n\t\t\tMod_SubmodelLoaded,\n\t\t};\n\t\tif (structsize == sizeof(funcs))\n\t\t\treturn &funcs;\n\t}\n#endif\n\n#ifdef TERRAIN\n\tif (!strcmp(interfacename, /*plugterrainfuncs_name*/\"Terrain\"))\n\t\treturn Terr_GetTerrainFuncs(structsize);\n#endif\n\n#ifdef HAVE_SERVER\n\tif (!strcmp(interfacename, \"SSQCVM\"))\n\t\treturn sv.world.progs;\n#endif\n#if defined(CSQC_DAT) && defined(HAVE_CLIENT)\n\tif (!strcmp(interfacename, \"CSQCVM\"))\n\t{\n\t\textern world_t csqc_world;\n\t\treturn csqc_world.progs;\n\t}\n#endif\n#if defined(MENU_DAT) && defined(HAVE_CLIENT)\n\tif (!strcmp(interfacename, \"MenuQCVM\"))\n\t{\n\t\textern world_t menu_world;\n\t\treturn menu_world.progs;\n\t}\n#endif\n\tCon_DPrintf(\"Plugin %s requested interface %s#%x, but its unavailable.\\n\", currentplug?currentplug->filename:\"UNKNOWN\", interfacename, (unsigned int)structsize);\n\treturn NULL;\n}\n\n#endif\n"
  },
  {
    "path": "engine/common/pmove.c",
    "content": "/*\nCopyright (C) 1996-1997 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n*/\n\n#include \"quakedef.h\"\n\nmovevars_t\t\tmovevars;\nplayermove_t\tpmove;\nextern cvar_t\tpm_noround;\t//evile.\n\n#define movevars_dpflags\t\tMOVEFLAG_QWCOMPAT\n#define movevars_maxairspeed\t30\n#define movevars_jumpspeed\t\t270\n\nfloat\t\tframetime;\n\nvec3_t\t\tforward, right, up;\n\nvoid PM_Init (void)\n{\n\tPM_InitBoxHull();\n}\n\n#define\tMIN_STEP_NORMAL\t0.7\t\t// roughly 45 degrees\n\n#define\tSTOP_EPSILON\t0.1\n#define BLOCKED_FLOOR\t1\n#define BLOCKED_STEP\t2\n#define BLOCKED_OTHER\t4\n#define BLOCKED_ANY\t\t7\n\n/*\n** Add an entity to touch list, discarding duplicates\n*/\nstatic void PM_AddTouchedEnt (int num)\n{\n\tif (pmove.numtouch == MAX_PHYSENTS)\n\t\treturn;\n\n\tif (pmove.numtouch)\n\t\tif (pmove.touchindex[pmove.numtouch - 1] == num)\n\t\t\treturn; // already added\n\n\tpmove.touchindex[pmove.numtouch] = num;\n\tVectorCopy(pmove.velocity, pmove.touchvel[pmove.numtouch]);\n\tpmove.numtouch++;\n}\n\n\n/*\n==================\nPM_ClipVelocity\n\nSlide off of the impacting object\n==================\n*/\n\nvoid PM_ClipVelocity (vec3_t in, vec3_t normal, vec3_t out, float overbounce)\n{\n\tfloat\tbackoff;\n\tfloat\tchange;\n\tint\t\ti;\n\t\n\tbackoff = DotProduct (in, normal) * overbounce;\n\n\tfor (i=0 ; i<3 ; i++)\n\t{\n\t\tchange = normal[i]*backoff;\n\t\tout[i] = in[i] - change;\n\t\tif (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON)\n\t\t\tout[i] = 0;\n\t}\n}\n\n#include \"pr_common.h\"\nstatic qboolean PM_PortalTransform(world_t *w, int portalnum, vec3_t org, vec3_t move, vec3_t newang, vec3_t newvel)\n{\n\tvec3_t rounded;\n\tqboolean okay = true;\n\twedict_t *portal = WEDICT_NUM_UB(w->progs, portalnum);\n\tint oself = *w->g.self;\n\tvoid *pr_globals = PR_globals(w->progs, PR_CURRENT);\n\tint i;\n\tint tmp;\n\tfloat f;\n\n\t*w->g.self = EDICT_TO_PROG(w->progs, portal);\n\t//transform origin+velocity etc\n\tVectorCopy(org, G_VECTOR(OFS_PARM0));\n\tVectorCopy(pmove.angles, G_VECTOR(OFS_PARM1));\n\tVectorCopy(pmove.velocity, w->g.v_forward);\n\tVectorCopy(move, w->g.v_right);\n\tVectorCopy(pmove.gravitydir, w->g.v_up);\n\tif (!DotProduct(w->g.v_up, w->g.v_up))\n\t\tw->g.v_up[2] = -1;\n\n\tPR_ExecuteProgram (w->progs, portal->xv->camera_transform);\n\n\tfor (i = 0; i < 3; i++)\n\t{\n\t\ttmp = floor(G_VECTOR(OFS_RETURN)[i]*8 + 0.5);\n\t\trounded[i] = tmp/8.0;\n\t}\n\t//make sure the new origin is okay for the player. back out if its invalid.\n\tif (!PM_TestPlayerPosition(rounded, true))\n\t\tokay = false;\n\telse\n\t{\n\t\tVectorCopy(rounded, org);\n\t\tVectorCopy(w->g.v_forward, newvel);\n\t\tVectorCopy(w->g.v_right, move);\n//\t\tVectorCopy(w->g.v_up, pmove.gravitydir);\n\n\t\t//floor+floor, ish\n\t\tif (DotProduct(w->g.v_up, pmove.gravitydir) < 0.7)\n\t\t{\n\t\t\tf = DotProduct(newvel, newvel);\n\t\t\tif (f < 200*200)\n\t\t\t{\n\t\t\t\tVectorScale(newvel, 200 / sqrt(f), newvel);\n\t\t\t}\n\t\t}\n\n\n\t\t//transform the angles too\n\t\tVectorCopy(org, G_VECTOR(OFS_PARM0));\n\t\tVectorCopy(pmove.angles, G_VECTOR(OFS_PARM1));\n\t\tAngleVectors(pmove.angles, w->g.v_forward, w->g.v_right, w->g.v_up);\n\t\tPR_ExecuteProgram (w->progs, portal->xv->camera_transform);\n\t\tVectorAngles(w->g.v_forward, w->g.v_up, newang, false);\n\t}\n\n\t*w->g.self = oself;\n\treturn okay;\n}\n\nstatic trace_t\tPM_PlayerTracePortals(vec3_t start, vec3_t end, unsigned int solidmask, float *tookportal)\n{\n\ttrace_t trace = PM_PlayerTrace (start, end, MASK_PLAYERSOLID);\n\tif (tookportal)\n\t\t*tookportal = 0;\n\tif (trace.entnum >= 0 && pmove.world)\n\t{\n\t\tphysent_t *impact = &pmove.physents[trace.entnum];\n\t\tif (impact->isportal)\n\t\t{\n\t\t\tvec3_t move;\n\t\t\tvec3_t from;\n\t\t\tvec3_t newang, newvel = {0,0,0};\n\n\t\t\tVectorCopy(trace.endpos, from);\t//just in case\n\t\t\tVectorSubtract(end, trace.endpos, move);\n\t\t\tif (PM_PortalTransform(pmove.world, impact->info, from, move, newang, newvel))\n\t\t\t{\n\t\t\t\ttrace_t exit;\n\t\t\t\tint i, tmp;\n\t\t\t\tVectorAdd(from, move, end);\n\t\t\t\t\n\t\t\t\t//if we follow the portal, then we basically need to restart from the other side.\n\t\t\t\texit = PM_PlayerTrace (from, end, MASK_PLAYERSOLID);\n\n\t\t\t\tfor (i = 0; i < 3; i++)\n\t\t\t\t{\n\t\t\t\t\ttmp = floor(exit.endpos[i]*8 + 0.5);\n\t\t\t\t\texit.endpos[i] = tmp/8.0;\n\t\t\t\t}\n\t\t\t\tif (PM_TestPlayerPosition(exit.endpos, false))\n\t\t\t\t{\n\t\t\t\t\tif (tookportal)\n\t\t\t\t\t\t*tookportal = trace.fraction;\n\t\t\t\t\tVectorCopy(newang, pmove.angles);\n\t\t\t\t\tVectorCopy(newvel, pmove.velocity);\n\t\t\t\t\treturn exit;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn trace;\n}\n\n/*\n============\nPM_SlideMove\n\nThe basic solid body movement clip that slides along multiple planes\n============\n*/\n#define\tMAX_CLIP_PLANES\t5\n\nint PM_SlideMove (void)\n{\n\tint\t\t\tbumpcount, numbumps;\n\tvec3_t\t\tdir;\n\tfloat\t\td;\n\tint\t\t\tnumplanes;\n\tvec3_t\t\tplanes[MAX_CLIP_PLANES];\n\tvec3_t\t\tprimal_velocity, original_velocity;\n\tint\t\t\ti, j;\n\ttrace_t\t\ttrace;\n\tvec3_t\t\tend;\n\tfloat\t\ttime_left;\n\tint\t\t\tblocked;\n\tfloat\t\ttookportal;\n\tvec3_t\t\tstart;\n\n\tnumbumps = 4;\n\n\tblocked = 0;\n\tVectorCopy (pmove.velocity, original_velocity);\n\tVectorCopy (pmove.velocity, primal_velocity);\n\tnumplanes = 0;\n\n\ttime_left = frametime;\n\n//\tVectorAdd(pmove.velocity, pmove.basevelocity, pmove.velocity);\n\n\tfor (bumpcount=0 ; bumpcount<numbumps ; bumpcount++)\n\t{\n\t\tfor (i=0 ; i<3 ; i++)\n\t\t\tend[i] = pmove.origin[i] + time_left * pmove.velocity[i];\n\n\t\tVectorCopy(pmove.origin, start);\n\t\ttrace = PM_PlayerTracePortals (start, end, MASK_PLAYERSOLID, &tookportal);\n\t\tif (tookportal)\n\t\t{\n\t\t\t//made progress, but hit a portal\n\t\t\ttime_left -= time_left * tookportal;\n\t\t\tVectorCopy (pmove.velocity, primal_velocity);\n\t\t\tVectorCopy (pmove.velocity, original_velocity);\n\t\t\tnumplanes = 0;\n\t\t}\n\n\n\t\tif (trace.startsolid || trace.allsolid)\n\t\t{\t// entity is trapped in another solid\n\t\t\tVectorClear (pmove.velocity);\n\t\t\treturn 3;\n\t\t}\n\n\t\tif (trace.fraction > 0)\n\t\t{\t// actually covered some distance\n\t\t\tVectorCopy (trace.endpos, pmove.origin);\n\t\t\tnumplanes = 0;\n\t\t}\n\n\t\tif (trace.fraction == 1)\n\t\t\t break;\t\t// moved the entire distance\n\n\t\t// save entity for contact\n\t\tPM_AddTouchedEnt (trace.entnum);\n\n\t\tif (trace.plane.normal[2] >= MIN_STEP_NORMAL)\n\t\t\tblocked |= BLOCKED_FLOOR;\n\t\telse if (!trace.plane.normal[2])\n\t\t\tblocked |= BLOCKED_STEP;\n\t\telse\n\t\t\tblocked |= BLOCKED_OTHER;\n\n\t\ttime_left -= time_left * trace.fraction;\n\n\t// cliped to another plane\n\t\tif (numplanes >= MAX_CLIP_PLANES)\n\t\t{\t// this shouldn't really happen\n\t\t\tVectorClear (pmove.velocity);\n\t\t\tbreak;\n\t\t}\n\n\t\tVectorCopy (trace.plane.normal, planes[numplanes]);\n\t\tnumplanes++;\n\n//\n// modify original_velocity so it parallels all of the clip planes\n//\n\t\tfor (i=0 ; i<numplanes ; i++)\n\t\t{\n\t\t\tif (movevars.walljump == 2)\t//just bounce off!\n\t\t\t{\t//pinball\n\t\t\t\tPM_ClipVelocity (original_velocity, planes[i], pmove.velocity, 2);\n\t\t\t\treturn blocked;\n\t\t\t}\n\t\t\t//regular run at a wall and jump off\n\t\t\tif (movevars.walljump && planes[i][2] != 1\t//not on floors\n\t\t\t\t&& Length(pmove.velocity)>200 && pmove.cmd.buttons & 2 && !pmove.jump_held && !pmove.waterjumptime)\n\t\t\t{\n\t\t\t\tPM_ClipVelocity (original_velocity, planes[i], pmove.velocity, 2);\n\t\t\t\tif (pmove.velocity[2] < movevars_jumpspeed)\n\t\t\t\t\tpmove.velocity[2] = movevars_jumpspeed;\n\t\t\t\tpmove.jump_msec = pmove.cmd.msec;\n\t\t\t\tpmove.jump_held = true;\n\t\t\t\tpmove.waterjumptime = 0;\n\t\t\t\treturn blocked;\n\t\t\t}\n\t\t\tPM_ClipVelocity (original_velocity, planes[i], pmove.velocity, 1);\n\t\t\tfor (j=0 ; j<numplanes ; j++)\n\t\t\t\tif (j != i)\n\t\t\t\t{\n\t\t\t\t\tif (DotProduct (pmove.velocity, planes[j]) < 0)\n\t\t\t\t\t\tbreak;\t// not ok\n\t\t\t\t}\n\t\t\tif (j == numplanes)\n\t\t\t\tbreak;\n\t\t}\n\n\t\tif (i != numplanes)\n\t\t{\t// go along this plane\n\t\t}\n\t\telse\n\t\t{\t// go along the crease\n\t\t\tif (numplanes != 2)\n\t\t\t{\n\t\t\t\tVectorClear (pmove.velocity);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tCrossProduct (planes[0], planes[1], dir);\n\t\t\td = DotProduct (dir, pmove.velocity);\n\t\t\tVectorScale (dir, d, pmove.velocity);\n\t\t}\n\n//\n// if velocity is against the original velocity, stop dead\n// to avoid tiny occilations in sloping corners\n//\n\t\tif (DotProduct (pmove.velocity, primal_velocity) <= 0)\n\t\t{\n\t\t\tVectorClear (pmove.velocity);\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tif (pmove.waterjumptime)\n\t{\n\t\tVectorCopy (primal_velocity, pmove.velocity);\n\t}\n\treturn blocked;\n}\n\n/*\n=============\nPM_StepSlideMove\n\nEach intersection will try to step over the obstruction instead of\nsliding along it.\n=============\n*/\nint PM_StepSlideMove (qboolean in_air)\n{\n\tvec3_t\tdest;\n\ttrace_t\ttrace;\n\tvec3_t\toriginal, originalvel, down, up, downvel;\n\tfloat\tdowndist, updist;\n\tint\t\tblocked;\n\tfloat\tstepsize;\n\n\t// try sliding forward both on ground and up 16 pixels\n\t// take the move that goes farthest\n\tVectorCopy (pmove.origin, original);\n\tVectorCopy (pmove.velocity, originalvel);\n\n\tblocked = PM_SlideMove ();\n\n\tif (!blocked)\n\t{\n\t\tif (!in_air && movevars.stepdown)\n\t\t{\t//if we were onground, try stepping down after the move to try to stay on said ground.\n\t\t\tVectorMA (pmove.origin, movevars.stepheight, pmove.gravitydir, dest);\n\t\t\ttrace = PM_PlayerTracePortals (pmove.origin, dest, MASK_PLAYERSOLID, NULL);\n\t\t\tif (trace.fraction != 1 && -DotProduct(pmove.gravitydir, trace.plane.normal) > MIN_STEP_NORMAL)\n\t\t\t{\n\t\t\t\tif (!trace.startsolid && !trace.allsolid)\n\t\t\t\t\tVectorCopy (trace.endpos, pmove.origin);\n\t\t\t}\n\t\t}\n\n\t\treturn blocked;\t\t// moved the entire distance\n\t}\n\n\tif (in_air)\n\t{\n\t\t// don't let us step up unless it's indeed a step we bumped in\n\t\t// (that is, there's solid ground below)\n\t\tfloat *org;\n\n\t\tif (!(blocked & BLOCKED_STEP))\n\t\t\treturn blocked;\n\n\t\torg = (-DotProduct(pmove.gravitydir, originalvel) < 0) ? pmove.origin : original;\n\t\tVectorMA (org, movevars.stepheight, pmove.gravitydir, dest);\n\t\ttrace = PM_PlayerTrace (org, dest, MASK_PLAYERSOLID);\n\t\tif (trace.fraction == 1 || -DotProduct(pmove.gravitydir, trace.plane.normal) < MIN_STEP_NORMAL)\n\t\t\treturn blocked;\n\n\t\t// adjust stepsize, otherwise it would be possible to walk up a\n\t\t// a step higher than STEPSIZE\n\t\t//FIXME gravitydir, portals\n\t\tstepsize = movevars.stepheight - (org[2] - trace.endpos[2]);\n\t}\n\telse\n\t\tstepsize = movevars.stepheight;\n\n\tVectorCopy (pmove.origin, down);\n\tVectorCopy (pmove.velocity, downvel);\n\n\tVectorCopy (original, pmove.origin);\n\tVectorCopy (originalvel, pmove.velocity);\n\n// move up a stair height\n\tVectorMA (pmove.origin, -stepsize, pmove.gravitydir, dest);\n\ttrace = PM_PlayerTracePortals (pmove.origin, dest, MASK_PLAYERSOLID, NULL);\n\tif (!trace.startsolid && !trace.allsolid)\n\t{\n\t\tVectorCopy (trace.endpos, pmove.origin);\n\t}\n\n\tif (in_air && -DotProduct(pmove.gravitydir, original) < 0)\n\t\tVectorMA(pmove.velocity, -DotProduct(pmove.velocity, pmove.gravitydir), pmove.gravitydir, pmove.velocity); //z=0\n\n\tPM_SlideMove ();\n\n// press down the stepheight\n\tVectorMA (pmove.origin, stepsize, pmove.gravitydir, dest);\n\ttrace = PM_PlayerTracePortals (pmove.origin, dest, MASK_PLAYERSOLID, NULL);\n\tif (trace.fraction != 1 && -DotProduct(pmove.gravitydir, trace.plane.normal) < MIN_STEP_NORMAL)\n\t\tgoto usedown;\n\tif (!trace.startsolid && !trace.allsolid)\n\t{\n\t\tVectorCopy (trace.endpos, pmove.origin);\n\t}\n\n\tif (-DotProduct(pmove.gravitydir, pmove.origin) < -DotProduct(pmove.gravitydir, original))\n\t\tgoto usedown;\n\n\tVectorCopy (pmove.origin, up);\n\n\t// decide which one went farther (in the forwards direction regardless of step values)\n\tVectorSubtract(down, original, dest);\n\tVectorMA(dest, -DotProduct(dest, pmove.gravitydir), pmove.gravitydir, dest); //z=0\n\tdowndist = DotProduct(dest, dest);\n\tVectorSubtract(up, original, dest);\n\tVectorMA(dest, -DotProduct(dest, pmove.gravitydir), pmove.gravitydir, dest); //z=0\n\tupdist = DotProduct(dest, dest);\n\n\tif (downdist >= updist)\n\t{\nusedown:\n\t\tVectorCopy (down, pmove.origin);\n\t\tVectorCopy (downvel, pmove.velocity);\n\t\treturn blocked;\n\t}\n\n\t// copy z value from slide move\n\tVectorMA(pmove.velocity, DotProduct(downvel, pmove.gravitydir)-DotProduct(pmove.velocity, pmove.gravitydir), pmove.gravitydir, pmove.velocity); //z=downvel\n\n\tif (!pmove.onground && pmove.waterlevel < 2 && (blocked & BLOCKED_STEP)) {\n\t\tfloat scale;\n\t\t// in pm_airstep mode, walking up a 16 unit high step\n\t\t// will kill 16% of horizontal velocity\n\t\tscale = 1 - 0.01*(pmove.origin[2] - original[2]);\n\t\t//FIXME gravitydir\n\t\tpmove.velocity[0] *= scale;\n\t\tpmove.velocity[1] *= scale;\n\t}\n\n\treturn blocked;\n}\n\n\n\n/*\n==================\nPM_Friction\n\nHandles both ground friction and water friction\n==================\n*/\nvoid PM_Friction (void)\n{\n\tfloat\tspeed, newspeed, control;\n\tfloat\tfriction;\n\tfloat\tdrop;\n\tvec3_t\tstart, stop;\n\ttrace_t\ttrace;\n\n\tif (pmove.waterjumptime)\n\t\treturn;\n\n\tspeed = Length(pmove.velocity);\n\tif (speed < 1)\n\t{\n//fixme: gravitydir fix needed\n\t\tpmove.velocity[0] = 0;\n\t\tpmove.velocity[1] = 0;\n\t\tif (pmove.pm_type == PM_FLY || pmove.pm_type == PM_6DOF)\n\t\t\tpmove.velocity[2] = 0;\n\t\treturn;\n\t}\n\n\tif (pmove.waterlevel >= 2)\n\t\t// apply water friction, even if in fly mode\n\t\tdrop = speed*movevars.waterfriction*pmove.waterlevel*frametime;\n\telse if (pmove.pm_type == PM_FLY || pmove.pm_type == PM_6DOF) {\n\t\t// apply flymode friction\n\t\tdrop = speed * movevars.flyfriction * frametime;\n\t}\n\telse if (pmove.onground) {\n\t\t// apply ground friction\n\t\tfriction = movevars.friction;\n\t\tif (movevars.edgefriction != 1.0)\n\t\t{\n\t\t\t// if the leading edge is over a dropoff, increase friction\n\t\t\tstart[0] = stop[0] = pmove.origin[0] + pmove.velocity[0]/speed*16;\n\t\t\tstart[1] = stop[1] = pmove.origin[1] + pmove.velocity[1]/speed*16;\n\t\t\t//FIXME: gravitydir.\n\t\t\t//id quirk: this is a tracebox, NOT a traceline, yet still starts BELOW the player.\n\t\t\tstart[2] = pmove.origin[2] + pmove.player_mins[2];\n\t\t\tstop[2] = start[2] - 34;\n\t\t\tif (movevars.flags & MOVEFLAG_QWEDGEBOX)\t//vanilla qw behaviour is to use a tracebox, which makes edge friction almost unnoticable.\n\t\t\t\ttrace = PM_PlayerTrace (start, stop, MASK_PLAYERSOLID);\n\t\t\telse\n\t\t\t{\t//traceline instead.\n\t\t\t\tvec3_t min, max;\n\t\t\t\tVectorCopy(pmove.player_mins, min);\n\t\t\t\tVectorCopy(pmove.player_maxs, max);\n\t\t\t\tVectorClear(pmove.player_mins);\n\t\t\t\tVectorClear(pmove.player_maxs);\n\t\t\t\ttrace = PM_PlayerTrace (start, stop, MASK_PLAYERSOLID);\n\t\t\t\tVectorCopy(min, pmove.player_mins);\n\t\t\t\tVectorCopy(max, pmove.player_maxs);\n\t\t\t}\n\t\t\tif (trace.fraction == 1 && !trace.startsolid)\n\t\t\t\tfriction *= movevars.edgefriction;\n\t\t}\n\t\tcontrol = speed < movevars.stopspeed ? movevars.stopspeed : speed;\n\t\tdrop = control*friction*frametime;\n\t}\n\telse if (pmove.onladder)\n\t{\n\t\tcontrol = speed < movevars.stopspeed ? movevars.stopspeed : speed;\n\t\tdrop = control*movevars.friction*frametime*6;\n\t}\n\telse\n\t\treturn;\t\t// in air, no friction\n\n// scale the velocity\n\tnewspeed = speed - drop;\n\tif (newspeed < 0)\n\t\tnewspeed = 0;\n\n\tVectorScale (pmove.velocity, newspeed / speed, pmove.velocity);\n}\n\n\n/*\n==============\nPM_Accelerate\n==============\n*/\nvoid PM_Accelerate (vec3_t wishdir, float wishspeed, float accel)\n{\n\tint\t\t\ti;\n\tfloat\t\taddspeed, accelspeed, currentspeed;\n\n\tif (pmove.pm_type == PM_DEAD)\n\t\treturn;\n\tif (pmove.waterjumptime)\n\t\treturn;\n\n\tcurrentspeed = DotProduct (pmove.velocity, wishdir);\n\taddspeed = wishspeed - currentspeed;\n\tif (addspeed <= 0)\n\t\treturn;\n\taccelspeed = accel*frametime*wishspeed;\n\tif (accelspeed > addspeed)\n\t\taccelspeed = addspeed;\n\n\tfor (i=0 ; i<3 ; i++)\n\t\tpmove.velocity[i] += accelspeed*wishdir[i];\n}\n\nvoid PM_AirAccelerate (vec3_t wishdir, float wishspeed, float accel)\n{\n\tint\t\t\ti;\n\tfloat\t\taddspeed, accelspeed, currentspeed, wishspd = wishspeed;\n\tfloat\t\toriginalspeed, newspeed, speedcap;\n\n\tif (pmove.pm_type == PM_DEAD)\n\t\treturn;\n\tif (pmove.waterjumptime)\n\t\treturn;\n\n\tif (movevars.bunnyspeedcap > 0)\n\t{\n\t\toriginalspeed = sqrt(pmove.velocity[0]*pmove.velocity[0] +\n\t\t\t\t\t\tpmove.velocity[1]*pmove.velocity[1]);\n\t}\n\telse\n\t\toriginalspeed = 0;\t//shh compiler.\n\n\tif (wishspd > movevars_maxairspeed)\n\t\twishspd = movevars_maxairspeed;\n\tcurrentspeed = DotProduct (pmove.velocity, wishdir);\n\taddspeed = wishspd - currentspeed;\n\tif (addspeed <= 0)\n\t\treturn;\n\taccelspeed = accel * wishspeed * frametime;\n\tif (accelspeed > addspeed)\n\t\taccelspeed = addspeed;\n\n\tfor (i=0 ; i<3 ; i++)\n\t\tpmove.velocity[i] += accelspeed*wishdir[i];\n\n\tif (movevars.bunnyspeedcap > 0)\n\t{\n\t\tnewspeed = sqrt(pmove.velocity[0]*pmove.velocity[0] +\n\t\t\t\t\tpmove.velocity[1]*pmove.velocity[1]);\n\t\tif (newspeed > originalspeed)\n\t\t{\n\t\t\tspeedcap = movevars.maxspeed * movevars.bunnyspeedcap;\n\t\t\tif (newspeed > speedcap)\n\t\t\t{\n\t\t\t\tif (originalspeed < speedcap)\n\t\t\t\t\toriginalspeed = speedcap;\n\t\t\t\tpmove.velocity[0] *= originalspeed / newspeed;\n\t\t\t\tpmove.velocity[1] *= originalspeed / newspeed;\n\t\t\t}\n\t\t}\n\t}\n}\n\n\n\n/*\n===================\nPM_WaterMove\n===================\n*/\nvoid PM_WaterMove (void)\n{\n\tint\t\ti;\n\tvec3_t\twishvel;\n\tfloat\twishspeed;\n\tvec3_t\twishdir;\n\n//\n// user intentions\n//\n\tfor (i=0 ; i<3 ; i++)\n\t\twishvel[i] = forward[i]*pmove.cmd.forwardmove + right[i]*pmove.cmd.sidemove;\n\n\tif (pmove.pm_type != PM_FLY && !pmove.cmd.forwardmove && !pmove.cmd.sidemove && !pmove.cmd.upmove && !pmove.onladder)\n\t{\n\t\tVectorMA(wishvel, movevars.watersinkspeed, pmove.gravitydir, wishvel);\n\t}\n\telse\n\t{\n\t\tVectorMA(wishvel, -pmove.cmd.upmove, pmove.gravitydir, wishvel);\n\t}\n\n\tVectorCopy (wishvel, wishdir);\n\twishspeed = VectorNormalize(wishdir);\n\n\tif (wishspeed > movevars.maxspeed) {\n\t\tVectorScale (wishvel, movevars.maxspeed/wishspeed, wishvel);\n\t\twishspeed = movevars.maxspeed;\n\t}\n\twishspeed *= 0.7;\n\n//\n// water acceleration\n//\n\tPM_Accelerate (wishdir, wishspeed, movevars.wateraccelerate);\n\n\tPM_StepSlideMove (false);\n}\n\n\n/*\n*/\nvoid PM_FlyMove (void)\n{\n\tint\t\ti;\n\tvec3_t\twishvel;\n\tfloat\twishspeed;\n\tvec3_t\twishdir;\n\n\tif (pmove.pm_type == PM_6DOF)\n\t{\n\t\tfor (i=0 ; i<3 ; i++)\n\t\t\twishvel[i] = forward[i]*pmove.cmd.forwardmove + right[i]*pmove.cmd.sidemove + up[i]*pmove.cmd.upmove;\n\t}\n\telse\n\t{\n\t\tfor (i=0 ; i<3 ; i++)\n\t\t\twishvel[i] = forward[i]*pmove.cmd.forwardmove + right[i]*pmove.cmd.sidemove;\n\n\t\tVectorMA(wishvel, -pmove.cmd.upmove, pmove.gravitydir, wishvel);\n\t}\n\n\tVectorCopy (wishvel, wishdir);\n\twishspeed = VectorNormalize(wishdir);\n\n\tif (wishspeed > movevars.maxspeed) {\n\t\tVectorScale (wishvel, movevars.maxspeed/wishspeed, wishvel);\n\t\twishspeed = movevars.maxspeed;\n\t}\n\n\tPM_Accelerate (wishdir, wishspeed, movevars.accelerate);\n\n\tPM_StepSlideMove (false);\n}\n\nvoid PM_LadderMove (void)\n{\n\tint\t\ti;\n\tvec3_t\twishvel;\n\tfloat\twishspeed;\n\tvec3_t\twishdir;\n\tvec3_t\tstart, dest;\n\ttrace_t\ttrace;\n\n//\n// user intentions\n//\n\tfor (i=0 ; i<3 ; i++)\n\t\twishvel[i] = forward[i]*pmove.cmd.forwardmove + right[i]*pmove.cmd.sidemove + up[i]*pmove.cmd.upmove;\n\n\tif (wishvel[2] >= 100 || wishvel[2] <= -100)\t//large up/down move\n\t\twishvel[2]*=10;\n\n\tif (pmove.cmd.buttons & 2)\n\t{\n\t\tVectorMA(wishvel, -movevars.maxspeed, pmove.gravitydir, wishvel);\n\t}\n\n\tVectorCopy (wishvel, wishdir);\n\twishspeed = VectorNormalize(wishdir);\n\n\tif (wishspeed > movevars.maxspeed)\n\t{\n\t\tVectorScale (wishvel, movevars.maxspeed/wishspeed, wishvel);\n\t\twishspeed = movevars.maxspeed;\n\t}\n\n\tPM_Accelerate (wishdir, wishspeed, movevars.wateraccelerate);\n\n// assume it is a stair or a slope, so press down from stepheight above\n\tVectorMA (pmove.origin, frametime, pmove.velocity, dest);\n\tVectorMA(dest, -(movevars.stepheight + 1), pmove.gravitydir, start);\n\ttrace = PM_PlayerTrace (start, dest, MASK_PLAYERSOLID);\n\tif (!trace.startsolid && !trace.allsolid)\t// FIXME: check steep slope?\n\t{\t// walked up the step\n\t\tVectorCopy (trace.endpos, pmove.origin);\n\t\treturn;\n\t}\n\n\tPM_FlyMove ();\n\n}\n\n/*\n===================\nPM_AirMove\n\n===================\n*/\nvoid PM_AirMove (void)\n{\n\tint\t\t\ti;\n\tfloat\t\tfmove, smove;\n\tvec3_t\t\twishdir;\n\tfloat\t\twishspeed;\n\n\tif (pmove.gravitydir[2] == -1 && (pmove.angles[0] == 90 || pmove.angles[0] == -90))\n\t{\t//HACK: attempt to avoid a stupid numerical precision issue.\n\t\t//You know its a hack because I'm comparing exact angles.\n\t\tvec3_t tmp;\n\t\tVectorSet(tmp, pmove.angles[0]*0.99, pmove.angles[1], pmove.angles[2]);\n\t\tAngleVectors (tmp, forward, right, up);\n\t}\n\n\tfmove = pmove.cmd.forwardmove;\n\tsmove = pmove.cmd.sidemove;\n\tVectorMA(forward, -DotProduct(forward, pmove.gravitydir), pmove.gravitydir, forward); //z=0\n\tVectorMA(right, -DotProduct(right, pmove.gravitydir), pmove.gravitydir, right); //z=0\n\tVectorNormalize (forward);\n\tVectorNormalize (right);\n\n\tfor (i=0 ; i<3 ; i++)\n\t\twishdir[i] = forward[i]*fmove + right[i]*smove;\n\tVectorMA(wishdir, -DotProduct(wishdir, pmove.gravitydir), pmove.gravitydir, wishdir); //z=0\n\n\twishspeed = VectorNormalize(wishdir);\n\n//\n// clamp to server defined max speed\n//\n\tif (wishspeed > movevars.maxspeed)\n\t{\n\t\twishspeed = movevars.maxspeed;\n\t}\n\n\tif (pmove.onground)\n\t{\n\t\tif (movevars.slidefix)\n\t\t{\n\t\t\tif (DotProduct(pmove.velocity, pmove.gravitydir) < 0)\n\t\t\t{\n\t\t\t\tVectorMA(pmove.velocity, -DotProduct(pmove.velocity, pmove.gravitydir), pmove.gravitydir, pmove.velocity); //z=0\n\t\t\t\t//pmove.velocity[2] = min(pmove.velocity[2], 0);\t// bound above by 0\n\t\t\t}\n\t\t\tPM_Accelerate (wishdir, wishspeed, movevars.accelerate);\n\t\t\t// add gravity\n\t\t\tVectorMA(pmove.velocity, movevars.entgravity * movevars.gravity * frametime, pmove.gravitydir, pmove.velocity);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tVectorMA(pmove.velocity, -DotProduct(pmove.velocity, pmove.gravitydir), pmove.gravitydir, pmove.velocity); //z=0\n\t\t\tPM_Accelerate (wishdir, wishspeed, movevars.accelerate);\n\t\t}\n\n\t\t//clear the z out, so we can test if we're moving horizontally relative to gravity\n\t\tVectorMA(pmove.velocity, -DotProduct(pmove.velocity, pmove.gravitydir), pmove.gravitydir, wishdir);\n\t\tif (!DotProduct(wishdir, wishdir) && !movevars.slidyslopes)\n\t\t{\n\t\t\t//clear z if we're not moving\n\t\t\tVectorClear(pmove.velocity);\n\t\t\treturn;\n\t\t}\n\t\telse if (!movevars.slidefix && !movevars.slidyslopes)\n\t\t\tVectorMA(pmove.velocity, -DotProduct(pmove.velocity, pmove.gravitydir), pmove.gravitydir, pmove.velocity); //z=0\n\n\t\tPM_StepSlideMove(false);\n\t}\n\telse\n\t{\n\t\tint blocked;\n\n\t\t// not on ground, so little effect on velocity\n\t\tPM_AirAccelerate (wishdir, wishspeed, movevars.accelerate);\n\n\t\t// add gravity\n\t\tVectorMA(pmove.velocity, movevars.entgravity * movevars.gravity * frametime, pmove.gravitydir, pmove.velocity);\n\n\t\tif (DotProduct(pmove.velocity,pmove.velocity) > 1000*1000)\n\t\t{\n\t\t\t//when in a windtunnel, step up from where we are rather than the actual ground in order to more closely match nq.\n\t\t\t//this is needed for r1m5 (770 800 192), just beyond the silver key door.\n\t\t\tblocked = PM_StepSlideMove (false);\n\t\t}\n\t\telse if (movevars.airstep)\n\t\t\tblocked = PM_StepSlideMove (true);\n\t\telse\n\t\t\tblocked = PM_SlideMove ();\n\n\t\tif (movevars.pground && (blocked & BLOCKED_FLOOR))\n\t\t\tpmove.onground = true;\n\t}\n}\n\n\ncplane_t\tgroundplane;\n\n/*\n=============\nPM_CategorizePosition\n=============\n*/\ntrace_t PM_TraceLine (vec3_t start, vec3_t end);\nvoid PM_CategorizePosition (void)\n{\n\tvec3_t\t\tpoint;\n\tint\t\t\tcont;\n\ttrace_t\t\ttrace;\n\n\tif (pmove.gravitydir[0] == 0 && pmove.gravitydir[1] == 0 && pmove.gravitydir[2] == 0)\n\t{\n\t\tpmove.gravitydir[0] = 0;\n\t\tpmove.gravitydir[1] = 0;\n\t\tpmove.gravitydir[2] = -1;\n\t}\n\tif (pmove.pm_type == PM_WALLWALK)\n\t{\n\t\tvec3_t tmin,tmax;\n\t\tVectorCopy(pmove.player_mins, tmin);\n\t\tVectorCopy(pmove.player_maxs, tmax);\n\n//\t\t//try tracing forwards+down\n//\t\tVectorMA(pmove.origin, -48, up, point);\n//\t\tVectorMA(point, 48, forward, point);\n//\t\ttrace = PM_TraceLine(pmove.origin, point);\n//\t\ttrace.fraction = 1;\n//\t\tif (1)//trace.fraction == 1)\n\t\t{\t//getting desparate\n\t\t\tVectorMA(pmove.origin, -48, up, point);\n\t\t\tVectorMA(point, 48, forward, point);\n\t\t\ttrace = PM_TraceLine(pmove.origin, point);\n\t\t}\n\t\tif (trace.fraction == 1)\n\t\t{\n\t\t\t//try tracing directly down only (we may be stepping off a cliff)\n\t\t\tVectorMA(pmove.origin, -48, up, point);\n\t\t\ttrace = PM_TraceLine(pmove.origin, point);\n\t\t}\n\t\tif (trace.fraction == 1)\n\t\t{\n\t\t\tvec3_t point2;\n\t\t\t//try tracing back from the cliff to see if we can find the ground beyond\n\t\t\tVectorMA(point, 48, forward, point2);\n\t\t\tVectorMA(point2, 48, forward, point);\n\t\t\ttrace = PM_TraceLine(point2, point);\n\t\t}\n\t\tif (trace.fraction == 1)\n\t\t{\t//getting desparate\n\t\t\tVectorMA(pmove.origin, -48, up, point);\n\t\t\tVectorMA(point, -48, forward, point);\n\t\t\ttrace = PM_TraceLine(pmove.origin, point);\n\t\t}\n\n\t\tVectorCopy(tmin, pmove.player_mins);\n\t\tVectorCopy(tmax, pmove.player_maxs);\n\n\t\tif (trace.fraction < 1)\n\t\t\tVectorNegate(trace.plane.normal, pmove.gravitydir);\n\t}\n\n// if the player hull point one unit down is solid, the player\n// is on ground\n\n// see if standing on something solid\n\tVectorAdd(pmove.origin, pmove.gravitydir, point);\n\ttrace.startsolid = trace.allsolid = true;\n\tVectorClear(trace.endpos);\n\tif (-DotProduct(pmove.gravitydir, pmove.velocity) > 180)\n\t{\n\t\tpmove.onground = false;\n\t}\n\telse if (!movevars.pground || pmove.onground)\n\t{\n\t\ttrace = PM_PlayerTracePortals (pmove.origin, point, MASK_PLAYERSOLID, NULL);\n\t\tif (!trace.startsolid && trace.fraction < 1 && -DotProduct(pmove.gravitydir, trace.plane.normal) < MIN_STEP_NORMAL)\n\t\t{\t//if the trace hit a slope, slide down the slope to see if we can find ground below. this should fix the 'base-of-slope-is-slide' bug.\n\t\t\tvec3_t bounce;\n\t\t\tPM_ClipVelocity (pmove.gravitydir, trace.plane.normal, bounce, 2);\n\t\t\tVectorMA(trace.endpos, 1-trace.fraction, bounce, point);\n\t\t\ttrace = PM_PlayerTracePortals (trace.endpos, point, MASK_PLAYERSOLID, NULL);\n\t\t}\n\n\t\tif (!trace.startsolid && (trace.fraction == 1 || -DotProduct(pmove.gravitydir, trace.plane.normal) < MIN_STEP_NORMAL))\n\t\t\tpmove.onground = false;\n\t\telse\n\t\t{\n\t\t\tpmove.onground = !trace.startsolid;\n\t\t\tpmove.groundent = trace.entnum;\n\t\t\tgroundplane = trace.plane;\n\t\t\tpmove.waterjumptime = 0;\n\t\t}\n\n\t\t// standing on an entity other than the world\n\t\tif (trace.entnum > 0)\n\t\t\tPM_AddTouchedEnt (trace.entnum);\n\t}\n\n//\n// get waterlevel\n//\n\tpmove.waterlevel = 0;\n\tpmove.watertype = FTECONTENTS_EMPTY;\n\n\t//FIXME: gravitydir\n\tVectorCopy(pmove.origin, point);\n\tpoint[2] = pmove.origin[2] + pmove.player_mins[2] + 1;\n\tcont = PM_PointContents (point);\n\n\tif (cont & FTECONTENTS_FLUID)\n\t{\n\t\tpmove.watertype = cont;\n\t\tpmove.waterlevel = 1;\n\t\tpoint[2] = pmove.origin[2] + (pmove.player_mins[2] + pmove.player_maxs[2])*0.5;\n\t\tcont = PM_PointContents (point);\n\t\tif (cont & FTECONTENTS_FLUID)\n\t\t{\n\t\t\tpmove.waterlevel = 2;\n\t\t\tpoint[2] = pmove.origin[2] + pmove.player_mins[2]+24+DEFAULT_VIEWHEIGHT;\n\t\t\tcont = PM_PointContents (point);\n\t\t\tif (cont & FTECONTENTS_FLUID)\n\t\t\t\tpmove.waterlevel = 3;\n\t\t}\n\t}\n\n\t//bsp objects marked as ladders mark regions to stand in to be classed as on a ladder.\n\tcont = PM_ExtraBoxContents(pmove.origin);\n\n\tif (pmove.physents[0].model)\n\t{\n#ifdef Q3BSPS\n\t\t//q3 has surfaceflag-based ladders\n\t\tif (pmove.physents[0].model->fromgame == fg_quake3)\n\t\t{\n\t\t\ttrace_t t;\n\t\t\tvec3_t flatforward, fwd1;\n\n\t\t\tflatforward[0] = forward[0];\n\t\t\tflatforward[1] = forward[1];\n\t\t\tflatforward[2] = 0;\n\t\t\tVectorNormalize (flatforward);\n\n\t\t\tVectorMA (pmove.origin, 24, flatforward, fwd1);\n\n\t\t\tpmove.physents[0].model->funcs.NativeTrace(pmove.physents[0].model, 0, PE_FRAMESTATE, NULL, pmove.origin, fwd1, pmove.player_mins, pmove.player_maxs, pmove.capsule, MASK_PLAYERSOLID, &t);\n\t\t\tif (t.surface && t.surface->flags & Q3SURFACEFLAG_LADDER)\n\t\t\t{\n\t\t\t\tpmove.onladder = true;\n\t\t\t\tpmove.onground = false;\t// too steep\n\t\t\t}\n\t\t}\n#endif\n\t\t//q2 has contents-based ladders\n\t\tif ((cont & FTECONTENTS_LADDER) || ((cont & Q2CONTENTS_LADDER) && pmove.physents[0].model->fromgame == fg_quake2))\n\t\t{\n\t\t\ttrace_t t;\n\t\t\tvec3_t flatforward, fwd1;\n\n\t\t\tflatforward[0] = forward[0];\n\t\t\tflatforward[1] = forward[1];\n\t\t\tflatforward[2] = 0;\n\t\t\tVectorNormalize (flatforward);\n\n\t\t\tVectorMA (pmove.origin, 24, flatforward, fwd1);\n\n\t\t\t//if we hit a wall when going forwards and we are in a ladder region, then we are on a ladder.\n\t\t\tt = PM_PlayerTrace(pmove.origin, fwd1, MASK_PLAYERSOLID);\n\t\t\tif (t.fraction < 1)\n\t\t\t{\n\t\t\t\tpmove.onladder = true;\n\t\t\t\tpmove.onground = false;\t// too steep\n\t\t\t}\n\t\t}\n\t}\n\n\tif (!movevars.pground && pmove.onground && pmove.pm_type != PM_FLY && pmove.waterlevel < 2)\n\t{\n\t\t// snap to ground so that we can't jump higher than we're supposed to\n\t\tif (!trace.startsolid && !trace.allsolid)\n\t\t\tVectorCopy (trace.endpos, pmove.origin);\n\t}\n}\n\n\n/*\n=============\nPM_CheckJump\n=============\n*/\nstatic void PM_CheckJump (void)\n{\n\tif (pmove.pm_type == PM_FLY)\n\t\treturn;\n\n\tif (pmove.pm_type == PM_DEAD)\n\t{\n\t\tpmove.jump_held = true;\t// don't jump on respawn\n\t\treturn;\n\t}\n\n\tif (!(pmove.cmd.buttons & BUTTON_JUMP))\n\t{\n\t\tpmove.jump_held = false;\n\t\treturn;\n\t}\n\n\tif (pmove.waterjumptime)\n\t\treturn;\n\n\tif (pmove.waterlevel >= 2)\n\t{\t// swimming, not jumping\n\t\tfloat speed;\n\t\tpmove.onground = false;\n\n\t\tif (pmove.watertype == FTECONTENTS_WATER)\n\t\t\tspeed = 100;\n\t\telse if (pmove.watertype == FTECONTENTS_SLIME)\n\t\t\tspeed = 80;\n\t\telse\n\t\t\tspeed = 50;\n\n\t\tVectorMA(pmove.velocity, -speed-DotProduct(pmove.velocity, pmove.gravitydir), pmove.gravitydir, pmove.velocity);\n\t\treturn;\n\t}\n\n\tif (!pmove.onground)\n\t\treturn;\t\t// in air, so no effect\n\n\tif (pmove.jump_held && !pmove.jump_msec)\n\t\treturn;\t\t// don't pogo stick\n\n\t// check for jump bug\n\t// groundplane normal was set in the call to PM_CategorizePosition\n\tif (!movevars.pground && -DotProduct(pmove.gravitydir, pmove.velocity) < 0 && DotProduct(pmove.velocity, groundplane.normal) < -0.1)\n\t{\n\t\t// pmove.velocity is pointing into the ground, clip it\n\t\tPM_ClipVelocity (pmove.velocity, groundplane.normal, pmove.velocity, 1);\n\t}\n\n\tpmove.onground = false;\n\tVectorMA(pmove.velocity, -movevars_jumpspeed, pmove.gravitydir, pmove.velocity);\n\n\tif (movevars.ktjump > 0 && pmove.pm_type != PM_WALLWALK)\n\t{\n\t\tif (movevars.ktjump > 1)\n\t\t\tmovevars.ktjump = 1;\n\t\tif (pmove.velocity[2] < movevars_jumpspeed)\n\t\t\tpmove.velocity[2] = pmove.velocity[2] * (1 - movevars.ktjump)\n\t\t\t\t+ movevars_jumpspeed * movevars.ktjump;\n\t}\n\n\tpmove.jump_held = true;\t\t// don't jump again until released\n\tpmove.jump_msec = pmove.cmd.msec;\n}\n\n/*\n=============\nPM_CheckWaterJump\n=============\n*/\nstatic void PM_CheckWaterJump (void)\n{\n\tvec3_t\tspot, spot2;\n//\tint\t\tcont;\n\tvec3_t\tflatforward;\n\ttrace_t tr;\n\tvec3_t oldmin, oldmax;\n\n\tif (pmove.waterjumptime>0)\n\t\treturn;\n\tif (pmove.pm_type == PM_DEAD)\n\t\treturn;\n\n\t// don't hop out if we just jumped in\n\tif (pmove.velocity[2] < -180)\n\t\treturn;\n\n\t// see if near an edge\n\tflatforward[0] = forward[0];\n\tflatforward[1] = forward[1];\n\tflatforward[2] = 0;\n\tVectorNormalize (flatforward);\n\n#if 1\t//NQ does a traceline, which gives more permissive results. This is required for various maps that have awkward water jumps.\n\tVectorCopy(pmove.player_mins, oldmin);\n\tVectorCopy(pmove.player_maxs, oldmax);\n\tVectorCopy(pmove.origin, spot);\n\tspot[2] += 8 + 24+pmove.player_mins[2];\t//hexen2 fix. calculated from the normal bottom of bbox\n\tVectorMA (spot, 24, flatforward, spot2);\n\ttr = PM_TraceLine(spot, spot2);\n\tVectorCopy(oldmin, pmove.player_mins);\n\tVectorCopy(oldmax, pmove.player_maxs);\n\tif (tr.fraction == 1)\t//(possibly) give up if open at waist\n\t{\t//NQ bug workaround: NQ does waterjump checks inside prethink, and THEN sets waterlevel after.\n\t\t//The player then moves to where waterlevel SHOULD be 3, except you're still allowed to waterjump because of last frame.\n\t\t//Which is horrible buggy framerate-dependant behaviour...\n\t\t//so lets just try again 2qu up.\n\t\t//This'll cause slight prediction issues with other qw engines, and maybe some newly bugged maps, but those maps were probably already buggy with a low enough nq framerate.\n\t\tspot[2] += 2;\n\t\tspot2[2] += 2;\n\t\ttr = PM_TraceLine(spot, spot2);\n\t\tVectorCopy(oldmin, pmove.player_mins);\n\t\tVectorCopy(oldmax, pmove.player_maxs);\n\t\tif (tr.fraction == 1)\t//give up if open at waist\n\t\t\treturn;\n\t}\n\tspot[2] += 24;\n\tspot2[2] += 24;\n\ttr = PM_TraceLine(spot, spot2);\n\tVectorCopy(oldmin, pmove.player_mins);\n\tVectorCopy(oldmax, pmove.player_maxs);\n\tif (tr.fraction < 1)\t//give up if blocked at eye\n\t\treturn;\n#else\n\tVectorMA (pmove.origin, 24, flatforward, spot);\n\tspot[2] += 8 + 24+pmove.player_mins[2];\t//hexen2 fix. calculated from the normal bottom of bbox\n\tcont = PM_PointContents (spot);\n\tif (!(cont & FTECONTENTS_SOLID))\n\t\treturn;\n\tspot[2] += 24;\n\tcont = PM_PointContents (spot);\n\tif (cont != FTECONTENTS_EMPTY)\n\t\treturn;\n#endif\n\t// jump out of water\n\tVectorScale (flatforward, 50, pmove.velocity);\n\tpmove.velocity[2] = 310;\n\tpmove.waterjumptime = 2;\t// safety net\n\tpmove.jump_held = true;\t\t// don't jump again until released\n}\n\n/*\n=================\nPM_NudgePosition\n\nIf pmove.origin is in a solid position,\ntry nudging slightly on all axis to\nallow for the cut precision of the net coordinates\n=================\n*/\nstatic void PM_NudgePosition (void)\n{\n\tvec3_t\tbase, nudged;\n\tint\t\tx, y, z;\n\tint\t\ti;\n\tstatic float\tsign[] = {0, -1/8.0, 1/8.0};\n\n\tVectorCopy (pmove.origin, base);\n\n\t//really we want to just use this here\n\t//base[i] = MSG_FromCoord(MSG_ToCoord(pmove.origin[i], movevars.coordsize), movevars.coordsize);\n\t//but it has overflow issues, so do things the painful way instead.\n\t//this stuff is so annoying because we're trying to avoid biasing the position towards 0. you'll see the effects of that if you use a low forwardspeed or low sv_gamespeed etc, but its also noticable with default settings too.\n\tif (\n#ifdef HAVE_LEGACY\n\t\t\tpm_noround.ival ||\n#endif\n\t\t\tmovevars.coordtype == COORDTYPE_FLOAT_32)\t//float precision on the network. no need to truncate.\n\t{\n\t\tVectorCopy (base, nudged);\n\t}\n\telse if (movevars.coordtype == COORDTYPE_FIXED_13_3)\t//1/8th precision, but don't truncate because that screws everything up.\n\t{\n\t\tfor (i=0 ; i<3 ; i++)\n\t\t{\n\t\t\t/*if (pmove.velocity[i])\n\t\t\t{\t//round in the direction of velocity, which means we're less likely to get stuck.\n\t\t\t\tif (pmove.velocity[i] >= 0)\n\t\t\t\t\tnudged[i] = (qintptr_t)(base[i]*8+0.5f) / 8.0;\n\t\t\t\telse\n\t\t\t\t\tnudged[i] = (qintptr_t)(base[i]*8-0.5f) / 8.0;\n\t\t\t}\n\t\t\telse*/\n\t\t\t{\n\t\t\t\tif (base[i] >= 0)\n\t\t\t\t\tnudged[i] = (qintptr_t)(base[i]*8+0.5f) / 8.0;\n\t\t\t\telse\n\t\t\t\t\tnudged[i] = (qintptr_t)(base[i]*8-0.5f) / 8.0;\n\t\t\t}\n\t\t}\n\t}\n\telse for (i=0 ; i<3 ; i++)\n\t\tnudged[i] = ((qintptr_t) (pmove.origin[i] * 8)) * 0.125;\t//legacy compat, which biases towards the origin.\n\n//\tVectorCopy (base, pmove.origin);\n\n\t//if we're moving, allow that spot without snapping to any grid\n//\tif (pmove.velocity[0] || pmove.velocity[1] || pmove.velocity[2])\n//\t\tif (PM_TestPlayerPosition (pmove.origin, false))\n//\t\t\treturn;\n\n\t//this is potentially 27 tests, and required for qw compat...\n\t//with unquantized floors it often succeeds only after 19 checks. which sucks.\n\tfor (z=0 ; z<countof(sign) ; z++)\n\t{\n\t\tfor (x=0 ; x<countof(sign) ; x++)\n\t\t{\n\t\t\tfor (y=0 ; y<countof(sign) ; y++)\n\t\t\t{\n\t\t\t\tpmove.origin[0] = nudged[0] + sign[x];\n\t\t\t\tpmove.origin[1] = nudged[1] + sign[y];\n\t\t\t\tpmove.origin[2] = nudged[2] + sign[z];\n\t\t\t\tif (PM_TestPlayerPosition (pmove.origin, false))\n\t\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n\n\t//still not managed it... be more agressive axially.\n\tfor (z=0 ; z<3; z++)\n\t{\n\t\tVectorCopy(base, pmove.origin);\n\t\tpmove.origin[z] = nudged[z] + (2/8.0);\n\t\tif (PM_TestPlayerPosition (pmove.origin, false))\n\t\t\treturn;\n\n\t\tVectorCopy(base, pmove.origin);\n\t\tpmove.origin[z] = nudged[z] - (2/8.0);\n\t\tif (PM_TestPlayerPosition (pmove.origin, false))\n\t\t\treturn;\n\t}\n\n\t//be more aggresssive at moving up, to match NQ\n\tfor (z=1 ; z<movevars.stepheight ; z++)\n\t{\n\t\tfor (x=0 ; x<3 ; x++)\n\t\t{\n\t\t\tfor (y=0 ; y<3 ; y++)\n\t\t\t{\n\t\t\t\tpmove.origin[0] = nudged[0] + sign[x];\n\t\t\t\tpmove.origin[1] = nudged[1] + sign[y];\n\t\t\t\tpmove.origin[2] = nudged[2] + z;\n\t\t\t\tif (PM_TestPlayerPosition (pmove.origin, false))\n\t\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n\n\tif (pmove.safeorigin_known && PM_TestPlayerPosition(pmove.safeorigin, false))\n\t\tVectorCopy (pmove.safeorigin, pmove.origin);\n\telse\n\t\tVectorCopy (base, pmove.origin);\n//\tCon_DPrintf (\"NudgePosition: stuck\\n\");\n}\n\n/*\n===============\nPM_SpectatorMove\n===============\n*/\nvoid PM_SpectatorMove (void)\n{\n\tfloat\tspeed, drop, friction, control, newspeed;\n\tfloat\tcurrentspeed, addspeed, accelspeed;\n\tint\t\t\ti;\n\tvec3_t\t\twishvel;\n\tfloat\t\tfmove, smove;\n\tvec3_t\t\twishdir;\n\tfloat\t\twishspeed;\n\n\t// friction\n\n\tspeed = Length (pmove.velocity);\n\tif (speed < 1)\n\t{\n\t\tVectorClear (pmove.velocity);\n\t}\n\telse\n\t{\n\t\tdrop = 0;\n\n\t\tfriction = movevars.friction*1.5;\t// extra friction\n\t\tcontrol = speed < movevars.stopspeed ? movevars.stopspeed : speed;\n\t\tdrop += control*friction*frametime;\n\n\t\t// scale the velocity\n\t\tnewspeed = speed - drop;\n\t\tif (newspeed < 0)\n\t\t\tnewspeed = 0;\n\t\tnewspeed /= speed;\n\n\t\tVectorScale (pmove.velocity, newspeed, pmove.velocity);\n\t}\n\n\t// accelerate\n\tfmove = pmove.cmd.forwardmove;\n\tsmove = pmove.cmd.sidemove;\n\n\tVectorNormalize (forward);\n\tVectorNormalize (right);\n\n\tfor (i=0 ; i<3 ; i++)\n\t\twishvel[i] = forward[i]*fmove + right[i]*smove;\n\twishvel[2] += pmove.cmd.upmove;\n\n\tVectorCopy (wishvel, wishdir);\n\twishspeed = VectorNormalize(wishdir);\n\n\t//\n\t// clamp to server defined max speed\n\t//\n\tif (wishspeed > movevars.spectatormaxspeed)\n\t{\n\t\tVectorScale (wishvel, movevars.spectatormaxspeed/wishspeed, wishvel);\n\t\twishspeed = movevars.spectatormaxspeed;\n\t}\n\n\tcurrentspeed = DotProduct(pmove.velocity, wishdir);\n\taddspeed = wishspeed - currentspeed;\n\n\t// Buggy QW spectator mode, kept for compatibility\n\tif (pmove.pm_type == PM_OLD_SPECTATOR)\n\t{\n\t\tif (addspeed <= 0)\n\t\t\treturn;\n\t}\n\n\tif (addspeed > 0) {\n\t\taccelspeed = movevars.accelerate*frametime*wishspeed;\n\t\tif (accelspeed > addspeed)\n\t\t\taccelspeed = addspeed;\n\n\t\tfor (i=0 ; i<3 ; i++)\n\t\t\tpmove.velocity[i] += accelspeed*wishdir[i];\n\t}\n\n\t// move\n\tVectorMA (pmove.origin, frametime, pmove.velocity, pmove.origin);\n}\n\n/*\n=============\nPM_PlayerMove\n\nReturns with origin, angles, and velocity modified in place.\n\nNumtouch and touchindex[] will be set if any of the physents\nwere contacted during the move.\n=============\n*/\nvoid PM_PlayerMove (float gamespeed)\n{\n//\tint i;\n//\tint tmp;\t//for rounding\n\n\tframetime = pmove.cmd.msec * 0.001*gamespeed;\n\tpmove.numtouch = 0;\n\n\tif (pmove.pm_type == PM_NONE || pmove.pm_type == PM_FREEZE)\n\t{\n\t\tPM_CategorizePosition ();\n\t\treturn;\n\t}\n\n\t// take angles directly from command\n\tpmove.angles[0] = SHORT2ANGLE(pmove.cmd.angles[0]);\n\tpmove.angles[1] = SHORT2ANGLE(pmove.cmd.angles[1]);\n\tpmove.angles[2] = SHORT2ANGLE(pmove.cmd.angles[2]);\n\n\tAngleVectors (pmove.angles, forward, right, up);\n\n\tif (pmove.pm_type == PM_SPECTATOR || pmove.pm_type == PM_OLD_SPECTATOR)\n\t{\n\t\tPM_SpectatorMove ();\n\t\tpmove.onground = false;\n\t\treturn;\n\t}\n\n\tPM_NudgePosition ();\n\n\t// set onground, watertype, and waterlevel\n\tPM_CategorizePosition ();\n\n\tif (movevars.autobunny && !pmove.onground)\n\t\tpmove.jump_held = false;\n\n\tif (pmove.waterlevel == 2 && pmove.pm_type != PM_FLY)\n\t\tPM_CheckWaterJump ();\n\n\tif (-DotProduct(pmove.gravitydir, pmove.velocity) < 0 || pmove.pm_type == PM_DEAD)\n\t\tpmove.waterjumptime = 0;\n\n\tif (pmove.waterjumptime)\n\t{\n\t\tpmove.waterjumptime -= frametime;\n\t\tif (pmove.waterjumptime < 0)\n\t\t\tpmove.waterjumptime = 0;\n\t}\n\n\tif (pmove.jump_msec)\n\t{\n\t\tpmove.jump_msec += pmove.cmd.msec;\n\t\tif (pmove.jump_msec > 50)\n\t\t\tpmove.jump_msec = 0;\n\t}\n\n\n\tif (!movevars.bunnyfriction)\n\t\tPM_CheckJump ();\t//qw-style bunny\n\tPM_Friction ();\n\n\tif (pmove.waterlevel >= 2)\n\t\tPM_WaterMove ();\n\telse if (pmove.pm_type == PM_FLY || pmove.pm_type == PM_6DOF)\n\t\tPM_FlyMove ();\n\telse if (pmove.onladder)\n\t\tPM_LadderMove ();\n\telse\n\t\tPM_AirMove ();\n\n\tif (movevars.bunnyfriction)\n\t\tPM_CheckJump ();\t//nq-style bunny. note tick rate differences too.\n\n/*\t//round to network precision\n\tfor (i = 0; i < 3; i++)\n\t{\n\t\ttmp = floor(pmove.velocity[i]*8 + 0.5);\n\t\tpmove.velocity[i] = tmp/8.0;\n\t\ttmp = floor(pmove.origin[i]*8 + 0.5);\n\t\tpmove.origin[i] = tmp/8.0;\n\t}\n\tPM_NudgePosition ();\n*/\n\t// set onground, watertype, and waterlevel for final spot\n\tPM_CategorizePosition ();\n\n\t// this is to make sure landing sound is not played twice\n\t// and falling damage is calculated correctly\n\tif (!movevars.pground && pmove.onground && -DotProduct(pmove.gravitydir, pmove.velocity) < -300\n\t\t&& DotProduct(pmove.velocity, groundplane.normal) < -0.1)\n\t{\n\t\tPM_ClipVelocity (pmove.velocity, groundplane.normal, pmove.velocity, 1);\n\t}\n}\n"
  },
  {
    "path": "engine/common/pmove.h",
    "content": "/*\nCopyright (C) 1996-1997 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  \n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n*/\n\n#define BUTTON_ATTACK 1\n#define BUTTON_JUMP 2\n\ntypedef enum {\n\tPM_NORMAL,\t\t\t// normal ground movement\n\tPM_OLD_SPECTATOR,\t// fly, no clip to world (QW bug)\n\tPM_SPECTATOR,\t\t// fly, no clip to world\n\tPM_DEAD,\t\t\t// no acceleration\n\tPM_FLY,\t\t\t\t// fly, bump into walls\n\tPM_NONE,\t\t\t// can't move\n\tPM_FREEZE,\t\t\t// can't move or look around (TODO)\n\tPM_WALLWALK,\t\t// sticks to walls. on ground while near one\n\tPM_6DOF\t\t\t\t// spaceship mode\n} pmtype_t;\n\n#define PMF_JUMP_HELD\t\t\t1\n#define PMF_LADDER\t\t\t\t2\t//pmove flags. seperate from flags\n\n#define\tMAX_PHYSENTS\t2048\ntypedef struct\n{\n\tvec3_t\torigin;\n\tvec3_t\tangles;\n\tmodel_t\t*model;\t\t// only for bsp models\n\tvec3_t\tmins, maxs;\t// only for non-bsp models\n\tunsigned int\tinfo;\t\t// for client or server to identify\n\tqbyte\t\tnonsolid;\t\t//contributes to contents, but does not block. FIXME: why not just use the contentsmask directly?\n\tqbyte\t\tnotouch;\t\t//don't trigger touch events. FIXME: why are these entities even in the list?\n\tqbyte\t\tisportal;\t\t//special portal traversion required\n\tunsigned int forcecontentsmask;\n//\tframestate_t framestate;\n#define PE_FRAMESTATE NULLFRAMESTATE\t//remove this once we start wanting players to interact with ents in different frames.\n} physent_t;\n\ntypedef struct\n{\n\t// player state\n\tvec3_t\t\torigin;\n\tvec3_t\t\tsafeorigin;\t//valid when safeorigin_known. needed for extrasr4's ladders otherwise they bug out.\n\tvec3_t\t\tangles;\n\tvec3_t\t\tvelocity;\n\tvec3_t\t\tbasevelocity;\n\tvec3_t\t\tgravitydir;\n\tqboolean\t\tjump_held;\n\tint\t\t\tjump_msec;\t// msec since last jump\n\tfloat\t\twaterjumptime;\n\tint\t\t\tpm_type;\n\tvec3_t\t\tplayer_mins;\n\tvec3_t\t\tplayer_maxs;\n\tqboolean\tcapsule;\n\n\t// world state\n\tint\t\t\tnumphysent;\n\tphysent_t\tphysents[MAX_PHYSENTS];\t// 0 should be the world\n\n\t// input\n\tusercmd_t\tcmd;\n\n\tqboolean onladder;\n\tqboolean safeorigin_known;\n\n\t// results\n\tint\t\t\tskipent;\n\tint\t\t\tnumtouch;\n\tint\t\t\ttouchindex[MAX_PHYSENTS];\n\tvec3_t\t\ttouchvel[MAX_PHYSENTS];\n\tqboolean\t\tonground;\n\tint\t\t\tgroundent;\t\t// index in physents array, only valid\n\t\t\t\t\t\t\t\t// when onground is true\n\tint\t\t\twaterlevel;\n\tint\t\t\twatertype;\n\n\tstruct world_s\t\t*world;\n} playermove_t;\n\ntypedef struct {\n\t//standard quakeworld\n\tfloat gravity;\n\tfloat stopspeed;\n\tfloat maxspeed;\n\tfloat spectatormaxspeed;\n\tfloat accelerate;\n\tfloat airaccelerate;\n\tfloat wateraccelerate;\n\tfloat friction;\n\tfloat waterfriction;\n\tfloat flyfriction;\n\tfloat entgravity;\n\n\t//extended stuff, sent via serverinfo\n\tfloat bunnyspeedcap;\n\tfloat watersinkspeed;\n\tfloat ktjump;\n\tfloat edgefriction; //default 2\n\tint\twalljump;\n\tqboolean slidefix;\n\tqboolean airstep;\n\tqboolean pground;\n\tqboolean stepdown;\n\tqboolean slidyslopes;\n\tqboolean autobunny;\n\tqboolean bunnyfriction;\t//force at least one frame of friction when bunnying.\n\tint stepheight;\n\n\tqbyte coordtype;\t//FIXME: EZPEXT1_FLOATENTCOORDS should mean 4, but the result does not match ezquake/mvdsv's round-towards-origin which would result in inconsistencies. so player coords are rounded inconsistently.\n\n\tunsigned int\tflags;\n} movevars_t;\n\n#define MOVEFLAG_VALID\t\t\t\t\t\t\t0x80000000\t//to signal that these are actually known. otherwise reserved.\n//#define MOVEFLAG_Q2AIRACCELERATE\t\t\t\t0x00000001\n#define MOVEFLAG_NOGRAVITYONGROUND\t\t\t\t0x00000002\t//no slope sliding\n//#define MOVEFLAG_GRAVITYUNAFFECTEDBYTICRATE\t0x00000004\t//apply half-gravity both before AND after the move, which better matches the curve\n#define MOVEFLAG_QWEDGEBOX\t\t\t\t\t\t0x00010000\t//calculate edgefriction using tracebox and a buggy start pos\n#define MOVEFLAG_QWCOMPAT\t\t\t\t\t\t(MOVEFLAG_NOGRAVITYONGROUND|MOVEFLAG_QWEDGEBOX)\n\nextern\tmovevars_t\t\tmovevars;\nextern\tplayermove_t\tpmove;\n\nvoid PM_PlayerMove (float gamespeed);\nvoid PM_Init (void);\nvoid PM_InitBoxHull (void);\n\nvoid PM_CategorizePosition (void);\nint PM_HullPointContents (hull_t *hull, int num, vec3_t p);\n\nint PM_ExtraBoxContents (vec3_t p);\t//Peeks for HL-style water.\nint PM_PointContents (vec3_t point);\nqboolean PM_TestPlayerPosition (vec3_t point, qboolean ignoreportals);\n#ifndef __cplusplus\nstruct trace_s PM_PlayerTrace (vec3_t start, vec3_t stop, unsigned int solidmask);\n#endif\n\n"
  },
  {
    "path": "engine/common/pmovetst.c",
    "content": "/*\nCopyright (C) 1996-1997 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  \n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n*/\n#include \"quakedef.h\"\n\nstatic qboolean PM_TransformedHullCheck (model_t *model, framestate_t *framestate, vec3_t start, vec3_t end, vec3_t mins, vec3_t maxs, trace_t *trace, vec3_t origin, vec3_t angles);\nint Q1BSP_HullPointContents(hull_t *hull, vec3_t p);\nstatic\thull_t\t\tbox_hull;\nstatic\tmclipnode_t\tbox_clipnodes[6];\nstatic\tmplane_t\tbox_planes[6];\n\n/*\n===================\nPM_InitBoxHull\n\nSet up the planes and clipnodes so that the six floats of a bounding box\ncan just be stored out and get a proper hull_t structure.\n===================\n*/\nvoid PM_InitBoxHull (void)\n{\n\tint\t\ti;\n\tint\t\tside;\n\n\tbox_hull.clipnodes = box_clipnodes;\n\tbox_hull.planes = box_planes;\n\tbox_hull.firstclipnode = 0;\n\tbox_hull.lastclipnode = 5;\n\n\tfor (i=0 ; i<6 ; i++)\n\t{\n\t\tbox_clipnodes[i].planenum = i;\n\t\t\n\t\tside = i&1;\n\t\t\n\t\tbox_clipnodes[i].children[side] = Q1CONTENTS_EMPTY;\n\t\tif (i != 5)\n\t\t\tbox_clipnodes[i].children[side^1] = i + 1;\n\t\telse\n\t\t\tbox_clipnodes[i].children[side^1] = Q1CONTENTS_SOLID;\n\t\t\n\t\tbox_planes[i].type = i>>1;\n\t\tbox_planes[i].normal[i>>1] = 1;\n\t}\n\t\n}\n\n\n/*\n===================\nPM_HullForBox\n\nTo keep everything totally uniform, bounding boxes are turned into small\nBSP trees instead of being compared directly.\n===================\n*/\nstatic hull_t\t*PM_HullForBox (vec3_t mins, vec3_t maxs)\n{\n\tbox_planes[0].dist = maxs[0];\n\tbox_planes[1].dist = mins[0];\n\tbox_planes[2].dist = maxs[1];\n\tbox_planes[3].dist = mins[1];\n\tbox_planes[4].dist = maxs[2];\n\tbox_planes[5].dist = mins[2];\n\n\treturn &box_hull;\n}\n\n\nstatic int PM_TransformedModelPointContents (model_t *mod, vec3_t p, vec3_t origin, vec3_t angles)\n{\n\tvec3_t p_l, axis[3];\n\tVectorSubtract (p, origin, p_l);\n\n\tif (!mod->funcs.PointContents)\n\t\treturn FTECONTENTS_EMPTY;\n\n\t// rotate start and end into the models frame of reference\n\tif (angles[0] || angles[1] || angles[2])\n\t{\n\t\tAngleVectors (angles, axis[0], axis[1], axis[2]);\n\t\tVectorNegate(axis[1], axis[1]);\n\t\treturn mod->funcs.PointContents(mod, axis, p_l);\n\t}\n\n\treturn mod->funcs.PointContents(mod, NULL, p_l);\n}\n\n\n/*\n==================\nPM_PointContents\n\n==================\n*/\nint PM_PointContents (vec3_t p)\n{\n\tint\t\t\tnum;\n\n\tint pc;\n\tphysent_t *pe;\n\tmodel_t *pm;\n\n\t//check world.\n\tpm = pmove.physents[0].model;\n\tif (!pm || pm->loadstate != MLS_LOADED)\n\t\treturn FTECONTENTS_EMPTY;\n\tpc = pm->funcs.PointContents(pm, NULL, p);\n\n\t//we need this for e2m2 - waterjumping on to plats wouldn't work otherwise.\n\tfor (num = 1; num < pmove.numphysent; num++)\n\t{\n\t\tpe = &pmove.physents[num];\n\n\t\tif (pe->info == pmove.skipent)\n\t\t\tcontinue;\n\n\t\tpm = pe->model;\n\t\tif (pm)\n\t\t{\n\t\t\tif (p[0] >= pe->origin[0]+pm->mins[0] && p[0] <= pe->origin[0]+pm->maxs[0] && \n\t\t\t\tp[1] >= pe->origin[1]+pm->mins[1] && p[1] <= pe->origin[1]+pm->maxs[1] &&\n\t\t\t\tp[2] >= pe->origin[2]+pm->mins[2] && p[2] <= pe->origin[2]+pm->maxs[2])\n\t\t\t{\n\t\t\t\tif (pe->forcecontentsmask)\n\t\t\t\t{\n\t\t\t\t\tif (PM_TransformedModelPointContents(pm, p, pe->origin, pe->angles))\n\t\t\t\t\t\tpc |= pe->forcecontentsmask;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tif (pe->nonsolid)\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\tpc |= PM_TransformedModelPointContents(pm, p, pe->origin, pe->angles);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse if (pe->forcecontentsmask)\n\t\t{\n\t\t\tif (p[0] >= pe->origin[0]+pe->mins[0] && p[0] <= pe->origin[0]+pe->maxs[0] && \n\t\t\t\tp[1] >= pe->origin[1]+pe->mins[1] && p[1] <= pe->origin[1]+pe->maxs[1] &&\n\t\t\t\tp[2] >= pe->origin[2]+pe->mins[2] && p[2] <= pe->origin[2]+pe->maxs[2])\n\t\t\t\tpc |= pe->forcecontentsmask;\n\t\t}\n\t}\n\n\treturn pc;\n}\n\nint PM_ExtraBoxContents (vec3_t p)\n{\n\tint\t\t\tnum;\n\n\tint pc = 0;\n\tphysent_t *pe;\n\tmodel_t *pm;\n\ttrace_t tr;\n\n\tfor (num = 1; num < pmove.numphysent; num++)\n\t{\n\t\tpe = &pmove.physents[num];\n\t\tif (!pe->nonsolid)\n\t\t\tcontinue;\n\t\tpm = pe->model;\n\t\tif (pm)\n\t\t{\n\t\t\tif (pe->forcecontentsmask)\n\t\t\t{\n\t\t\t\tif (!PM_TransformedHullCheck(pm, PE_FRAMESTATE, p, p, pmove.player_mins, pmove.player_maxs, &tr, pe->origin, pe->angles))\n\t\t\t\t\tcontinue;\n\t\t\t\tif (tr.startsolid || tr.inwater)\n\t\t\t\t\tpc |= pe->forcecontentsmask;\n\t\t\t}\n\t\t}\n\t\telse if (pe->forcecontentsmask)\n\t\t{\n\t\t\tif (p[0]+pmove.player_maxs[0] >= pe->origin[0]+pe->mins[0] && p[0]+pmove.player_mins[0] <= pe->origin[0]+pe->maxs[0] && \n\t\t\t\tp[1]+pmove.player_maxs[1] >= pe->origin[1]+pe->mins[1] && p[1]+pmove.player_mins[1] <= pe->origin[1]+pe->maxs[1] &&\n\t\t\t\tp[2]+pmove.player_maxs[2] >= pe->origin[2]+pe->mins[2] && p[2]+pmove.player_mins[2] <= pe->origin[2]+pe->maxs[2])\n\t\t\t\tpc |= pe->forcecontentsmask;\n\t\t}\n\t}\n\n\treturn pc;\n}\n\n/*\n===============================================================================\n\nLINE TESTING IN HULLS\n\n===============================================================================\n*/\n\n/*returns if it actually did a trace*/\nstatic qboolean PM_TransformedHullCheck (model_t *model, framestate_t *framestate, vec3_t start, vec3_t end, vec3_t player_mins, vec3_t player_maxs, trace_t *trace, vec3_t origin, vec3_t angles)\n{\n\tvec3_t\t\tstart_l, end_l;\n\tint i;\n\tvec3_t\t\taxis[3];\n\n\t// subtract origin offset\n\tVectorSubtract (start, origin, start_l);\n\tVectorSubtract (end, origin, end_l);\n\n\t// sweep the box through the model\n\tif (model && model->funcs.NativeTrace)\n\t{\n\t\tif (angles[0] || angles[1] || angles[2])\n\t\t{\n\t\t\tAngleVectors (angles, axis[0], axis[1], axis[2]);\n\t\t\tVectorNegate(axis[1], axis[1]);\n\t\t\tmodel->funcs.NativeTrace(model, 0, framestate, axis, start_l, end_l, player_mins, player_maxs, pmove.capsule, MASK_PLAYERSOLID, trace);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tfor (i = 0; i < 3; i++)\n\t\t\t{\n\t\t\t\tif (start_l[i]+player_mins[i] > model->maxs[i] && end_l[i] + player_mins[i] > model->maxs[i])\n\t\t\t\t\treturn false;\n\t\t\t\tif (start_l[i]+player_maxs[i] < model->mins[i] && end_l[i] + player_maxs[i] < model->mins[i])\n\t\t\t\t\treturn false;\n\t\t\t}\n\t\t\tmodel->funcs.NativeTrace(model, 0, framestate, NULL, start_l, end_l, player_mins, player_maxs, pmove.capsule, MASK_PLAYERSOLID, trace);\n\t\t}\n\t}\n\telse\n\t{\n\t\tfor (i = 0; i < 3; i++)\n\t\t{\n\t\t\tif (start_l[i]+player_mins[i] > box_planes[0+i*2].dist && end_l[i] + player_mins[i] > box_planes[0+i*2].dist)\n\t\t\t\treturn false;\n\t\t\tif (start_l[i]+player_maxs[i] < box_planes[1+i*2].dist && end_l[i] + player_maxs[i] < box_planes[1+i*2].dist)\n\t\t\t\treturn false;\n\t\t}\n\n\t\tmemset (trace, 0, sizeof(trace_t));\n\t\ttrace->fraction = 1;\n\t\ttrace->allsolid = true;\n\t\tQ1BSP_RecursiveHullCheck (&box_hull, box_hull.firstclipnode, start_l, end_l, MASK_PLAYERSOLID, trace);\n\t}\n\n\ttrace->endpos[0] += origin[0];\n\ttrace->endpos[1] += origin[1];\n\ttrace->endpos[2] += origin[2];\n\treturn true;\n}\n\n\n//a portal is flush with a world surface behind it.\n//this causes problems. namely that we can't pass through the portal plane if the bsp behind it prevents out origin from getting through.\n//so if the trace was clipped and ended infront of the portal, continue the trace to the edges of the portal cutout instead.\nstatic void PM_PortalCSG(physent_t *portal, int entnum, float *trmin, float *trmax, vec3_t start, vec3_t end, trace_t *trace)\n{\n\tvec4_t planes[6];\t//far, near, right, left, up, down\n\tint plane;\n\tvec3_t worldpos;\n\tfloat portalradius = 128;\n\tint hitplane = -1;\n\tfloat bestfrac;\n\t//only run this code if we impacted on the portal's parent.\n\tif (trace->fraction == 1 && !trace->startsolid)\n\t\treturn;\n\tif (!portalradius)\n\t\treturn;\n\t\n\tif (trace->startsolid)\n\t\tVectorCopy(start, worldpos);\t//make sure we use a sane valid position.\n\telse\n\t\tVectorCopy(trace->endpos, worldpos);\n\n\t//determine the csg area. normals should be facing in\n\tAngleVectors(portal->angles, planes[1], planes[3], planes[5]);\n\tVectorNegate(planes[1], planes[0]);\n\tVectorNegate(planes[3], planes[2]);\n\tVectorNegate(planes[5], planes[4]);\n\n\tportalradius/=2;\n\tplanes[0][3] = DotProduct(portal->origin, planes[0]) - (4.0/32);\n\tplanes[1][3] = DotProduct(portal->origin, planes[1]) - (4.0/32);\t//an epsilon beyond the portal. this needs to cover funny angle differences\n\tplanes[2][3] = DotProduct(portal->origin, planes[2]) - portalradius;\n\tplanes[3][3] = DotProduct(portal->origin, planes[3]) - portalradius;\n\tplanes[4][3] = DotProduct(portal->origin, planes[4]) - portalradius;\n\tplanes[5][3] = DotProduct(portal->origin, planes[5]) - portalradius;\n\n\t//if we're actually inside the csg region\n\tfor (plane = 0; plane < 6; plane++)\n\t{\n\t\tvec3_t nearest;\n\t\tfloat d = DotProduct(worldpos, planes[plane]);\n\t\tint k;\n\t\tfor (k = 0; k < 3; k++)\n\t\t\tnearest[k] = (planes[plane][k]>=0)?trmax[k]:trmin[k];\n\t\tif (!plane)\t//front plane gets further away with side\n\t\t\tplanes[plane][3] -= DotProduct(nearest, planes[plane]);\n\t\telse if (plane>1)\t//side planes get nearer with size\n\t\t\tplanes[plane][3] += 24;//+= DotProduct(nearest, planes[plane]);\n\t\tif (d - planes[plane][3] >= 0)\n\t\t\tcontinue;\t//endpos is inside\n\t\telse\n\t\t\treturn;\t\t//end is already outside\n\t}\n\t//yup, we're inside, the trace shouldn't end where it actually did\n\tbestfrac = 1;\n\thitplane = -1;\n\tfor (plane = 0; plane < 6; plane++)\n\t{\n\t\tfloat ds = DotProduct(start, planes[plane]) - planes[plane][3];\n\t\tfloat de = DotProduct(end, planes[plane]) - planes[plane][3];\n\t\tfloat frac;\n\t\tif (ds >= 0 && de < 0)\n\t\t{\n\t\t\tfrac = (ds - (1/32.0)) / (ds - de);\n\t\t\tif (frac < bestfrac)\n\t\t\t{\n\t\t\t\tif (frac < 0)\n\t\t\t\t\tfrac = 0;\n\t\t\t\thitplane = plane;\n\t\t\t\tbestfrac = frac;\n\t\t\t\tVectorInterpolate(start, frac, end, trace->endpos);\n\t\t\t}\n\t\t}\n\t}\n\ttrace->startsolid = trace->allsolid = false;\n\t//if we cross the front of the portal, don't shorten the trace, that will artificially clip us\n\tif (hitplane == 0 && trace->fraction > bestfrac)\n\t\treturn;\n\t//okay, elongate to clip to the portal hole properly.\n\ttrace->fraction = bestfrac;\n\tVectorInterpolate(start, bestfrac, end, trace->endpos);\n\n\tif (hitplane >= 0)\n\t{\n\t\tVectorCopy(planes[hitplane], trace->plane.normal);\n\t\ttrace->plane.dist = planes[hitplane][3];\n\t\tif (hitplane == 1)\n\t\t\ttrace->entnum = entnum;\n\t}\n}\n\n/*\n================\nPM_TestPlayerPosition\n\nReturns false if the given player position is not valid (in solid)\n================\n*/\nqboolean PM_TestPlayerPosition (vec3_t pos, qboolean ignoreportals)\n{\n\tint\t\t\ti, j;\n\tphysent_t\t*pe;\n\tvec3_t\t\tmins, maxs;\n\thull_t\t\t*hull;\n\ttrace_t\t\ttrace;\n\tint\t\t\tcsged = false;\n\n\tfor (i=0 ; i< pmove.numphysent ; i++)\n\t{\n\t\tpe = &pmove.physents[i];\n\n\t\tif (pe->info == pmove.skipent)\n\t\t\tcontinue;\n\n\t\tif (pe->nonsolid)\n\t\t\tcontinue;\n\n\t\tif (pe->forcecontentsmask && !(pe->forcecontentsmask & MASK_PLAYERSOLID))\n\t\t\tcontinue;\n\n\t// get the clipping hull\n\t\tif (pe->isportal)\n\t\t{\n\t\t\tif (ignoreportals)\n\t\t\t\tcontinue;\n\t\t\t//if the trace ended up inside a portal region, then its not valid.\n\t\t\tif (pe->model)\n\t\t\t{\n\t\t\t\tif (!PM_TransformedHullCheck (pe->model, PE_FRAMESTATE, pos, pos, vec3_origin, vec3_origin, &trace, pe->origin, pe->angles))\n\t\t\t\t\tcontinue;\n\t\t\t\tif (trace.allsolid)\n\t\t\t\t\treturn false;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\thull = PM_HullForBox (pe->mins, pe->maxs);\n\t\t\t\tVectorSubtract(pos, pe->origin, mins);\n\t\t\t\tif (Q1BSP_HullPointContents(hull, mins) & MASK_PLAYERSOLID)\n\t\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (pe->model)\n\t\t\t{\n\t\t\t\tif (!PM_TransformedHullCheck (pe->model, PE_FRAMESTATE, pos, pos, pmove.player_mins, pmove.player_maxs, &trace, pe->origin, pe->angles))\n\t\t\t\t\tcontinue;\n\t\t\t\tif (trace.allsolid)\n\t\t\t\t{\n\t\t\t\t\tfor (j = i+1; j < pmove.numphysent && trace.allsolid; j++)\n\t\t\t\t\t{\n\t\t\t\t\t\tpe = &pmove.physents[j];\n\t\t\t\t\t\tif (pe->isportal)\n\t\t\t\t\t\t\tPM_PortalCSG(pe, j, pmove.player_mins, pmove.player_maxs, pos, pos, &trace);\n\t\t\t\t\t}\n\t\t\t\t\tif (trace.allsolid)\n\t\t\t\t\t\treturn false;\n\t\t\t\t\tcsged = true;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tVectorSubtract (pe->mins, pmove.player_maxs, mins);\n\t\t\t\tVectorSubtract (pe->maxs, pmove.player_mins, maxs);\n\t\t\t\thull = PM_HullForBox (mins, maxs);\n\t\t\t\tVectorSubtract(pos, pe->origin, mins);\n\n\t\t\t\tif (Q1BSP_HullPointContents(hull, mins) & MASK_PLAYERSOLID)\n\t\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t}\n\n\tif (!csged && !ignoreportals)\n\t{\n\t\t//the point the player is returned to if the portal dissipates\n\t\tpmove.safeorigin_known = true;\n\t\tVectorCopy (pmove.origin, pmove.safeorigin);\n\t}\n\n\treturn true;\n}\n\n/*\n================\nPM_PlayerTrace\n================\n*/\ntrace_t PM_PlayerTrace (vec3_t start, vec3_t end, unsigned int solidmask)\n{\n\ttrace_t\t\ttrace, total;\n\tint\t\t\ti, j;\n\tphysent_t\t*pe;\n\n// fill in a default trace\n\tmemset (&total, 0, sizeof(trace_t));\n\ttotal.fraction = 1;\n\ttotal.entnum = -1;\n\tVectorCopy (end, total.endpos);\n\n\tfor (i=0 ; i< pmove.numphysent ; i++)\n\t{\n\t\tpe = &pmove.physents[i];\n\n\t\tif (pe->nonsolid)\n\t\t\tcontinue;\n\t\tif (pe->info == pmove.skipent)\n\t\t\tcontinue;\n\t\tif (pe->forcecontentsmask && !(pe->forcecontentsmask & solidmask))\n\t\t\tcontinue;\n\n\t\tif (!pe->model || pe->model->loadstate != MLS_LOADED)\n\t\t{\n\t\t\tvec3_t mins, maxs;\n\n\t\t\tVectorSubtract (pe->mins, pmove.player_maxs, mins);\n\t\t\tVectorSubtract (pe->maxs, pmove.player_mins, maxs);\n\t\t\tPM_HullForBox (mins, maxs);\n\n\t\t\t// trace a line through the apropriate clipping hull\n\t\t\tif (!PM_TransformedHullCheck (NULL, NULL, start, end, pmove.player_mins, pmove.player_maxs, &trace, pe->origin, pe->angles))\n\t\t\t\tcontinue;\n\t\t}\n\t\telse if (pe->isportal)\n\t\t{\n\t\t\t//make sure we don't hit the world if we're inside the portal\n\t\t\tPM_PortalCSG(pe, i, pmove.player_mins, pmove.player_maxs, start, end, &total);\n\n\t\t\t// trace a line through the apropriate clipping hull\n\t\t\tif (!PM_TransformedHullCheck (pe->model, PE_FRAMESTATE, start, end, vec3_origin, vec3_origin, &trace, pe->origin, pe->angles))\n\t\t\t\tcontinue;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// trace a line through the apropriate clipping hull\n\t\t\tif (!PM_TransformedHullCheck (pe->model, PE_FRAMESTATE, start, end, pmove.player_mins, pmove.player_maxs, &trace, pe->origin, pe->angles))\n\t\t\t\tcontinue;\n\n\t\t\tif (trace.allsolid)\n\t\t\t{\n\t\t\t\tfor (j = i+1; j < pmove.numphysent && trace.allsolid; j++)\n\t\t\t\t{\n\t\t\t\t\tpe = &pmove.physents[j];\n\t\t\t\t\tif (pe->isportal)\n\t\t\t\t\t\tPM_PortalCSG(pe, j, pmove.player_mins, pmove.player_maxs, start, end, &trace);\n\t\t\t\t}\n\t\t\t\tpe = &pmove.physents[i];\n\t\t\t}\n\t\t}\n\n\t\tif (trace.allsolid)\n\t\t\ttrace.startsolid = true;\n\t\tif (trace.startsolid && pe->isportal)\n\t\t\ttrace.startsolid = false;\n//\t\tif (trace.startsolid)\n//\t\t\ttrace.fraction = 0;\n\n\t// did we clip the move?\n\t\tif (trace.fraction < total.fraction || (trace.startsolid && !total.startsolid))\n\t\t{\n\t\t\t// fix trace up by the offset\n\t\t\ttotal = trace;\n\t\t\ttotal.entnum = i;\n\t\t}\n\t}\n\n//\t//this is needed to avoid *2 friction. some id bug.\n\tif (total.startsolid)\n\t\ttotal.fraction = 0;\n\treturn total;\n}\n\n//for use outside the pmove code. lame, but works.\ntrace_t PM_TraceLine (vec3_t start, vec3_t end)\n{\n\tVectorClear(pmove.player_mins);\n\tVectorClear(pmove.player_maxs);\n\treturn PM_PlayerTrace(start, end, MASK_PLAYERSOLID);\n}\n"
  },
  {
    "path": "engine/common/pr_bgcmd.c",
    "content": "//file for builtin implementations relevent to all VMs.\n\n#include \"quakedef.h\"\n\n#if !defined(CLIENTONLY) || defined(CSQC_DAT) || defined(MENU_DAT)\n\n#include \"pr_common.h\"\n#ifdef SQL\n#include \"sv_sql.h\"\n#endif\n#include \"fs.h\"\n\n#include <ctype.h>\n\n#define VMUTF8 utf8_enable.ival\n#define VMUTF8MARKUP false\n\nstatic char *cvargroup_progs = \"Progs variables\";\n\ncvar_t utf8_enable = CVARD(\"utf8_enable\", \"0\", \"When 1, changes the qc builtins to act upon codepoints instead of bytes. Do not use unless com_parseutf8 is also set.\");\ncvar_t sv_gameplayfix_linknonsolid = CVARD(\"sv_gameplayfix_nolinknonsolid\", \"1\", \"When 0, setorigin et al will not link the entity into the collision nodes (which is faster, especially if you have a lot of non-solid entities. When 1, allows entities to freely switch between .solid values (except for SOLID_BSP) without relinking. A lot of DP mods assume a value of 1 and will bug out otherwise, while 0 will restore a bugs present in various mods.\");\ncvar_t sv_gameplayfix_blowupfallenzombies = CVARD(\"sv_gameplayfix_blowupfallenzombies\", \"0\", \"Allow findradius to find non-solid entities. This may break certain mods. It is better for mods to use FL_FINDABLE_NONSOLID instead.\");\ncvar_t sv_gameplayfix_findradiusdistancetobox = CVARD(\"sv_gameplayfix_findradiusdistancetobox\", \"0\", \"When 1, findradius checks to the nearest part of the entity instead of only its origin, making it find slightly more entities.\");\ncvar_t sv_gameplayfix_droptofloorstartsolid = CVARD(\"sv_gameplayfix_droptofloorstartsolid\", \"0\", \"When droptofloor fails, this causes a second attemp, but with traceline instead.\");\ncvar_t dpcompat_findradiusarealinks = CVARD(\"dpcompat_findradiusarealinks\", \"0\", \"Use the world collision info to accelerate findradius instead of looping through every single entity. May actually be slower for large radiuses, or fail to find entities which have not been linked properly with setorigin.\");\n#ifdef HAVE_LEGACY\ncvar_t dpcompat_strcat_limit = CVARD(\"dpcompat_strcat_limit\", \"\", \"When set, cripples strcat (and related function) string lengths to the value specified.\\nSet to 16383 to replicate DP's limit, otherwise leave as 0 to avoid limits.\");\n#endif\ncvar_t pr_autocreatecvars = CVARD(\"pr_autocreatecvars\", \"1\", \"Implicitly create any cvars that don't exist when read.\");\ncvar_t pr_droptofloorunits = CVARD(\"pr_droptofloorunits\", \"256\", \"Distance that droptofloor is allowed to drop to be considered successul.\");\ncvar_t pr_brokenfloatconvert = CVAR(\"pr_brokenfloatconvert\", \"0\");\ncvar_t\tpr_fixbrokenqccarrays = CVARFD(\"pr_fixbrokenqccarrays\", \"0\", CVAR_MAPLATCH, \"As part of its nq/qw/h2/csqc support, FTE remaps QC fields to match an internal order. This is a faster way to handle extended fields. However, some QCCs are buggy and don't report all field defs.\\n0: do nothing. QCC must be well behaved.\\n1: Duplicate engine fields, remap the ones we can to known offsets. This is sufficient for QCCX/FrikQCC mods that use hardcoded or even occasional calculated offsets (fixes ktpro).\\n2: Scan the mod for field accessing instructions, and assume those are the fields (and that they don't alias non-fields). This can be used to work around gmqcc's WTFs (fixes xonotic).\");\ncvar_t pr_tempstringcount = CVARD(\"pr_tempstringcount\", \"\", \"Obsolete. Set to 16 if you want to recycle+reuse the same 16 tempstring references and break lots of mods.\");\ncvar_t pr_tempstringsize = CVARD(\"pr_tempstringsize\", \"4096\", \"Obsolete\");\n#ifdef MULTITHREAD\ncvar_t pr_gc_threaded = CVARD(\"pr_gc_threaded\", \"1\", \"Says whether to use a separate thread for tempstring garbage collections. This avoids main-thread stalls but at the expense of more memory usage.\");\n#else\ncvar_t pr_gc_threaded = CVARFD(\"pr_gc_threaded\", \"0\", CVAR_NOSET|CVAR_NOSAVE, \"Says whether to use a separate thread for tempstring garbage collections. This avoids main-thread stalls but at the expense of more memory usage.\");\n#endif\ncvar_t\tpr_sourcedir = CVARD(\"pr_sourcedir\", \"src\", \"Subdirectory where your qc source is located. Used by the internal compiler and qc debugging functionality.\");\ncvar_t pr_enable_uriget = CVARD(\"pr_enable_uriget\", \"1\", \"Allows gamecode to make direct http requests\");\ncvar_t pr_enable_profiling = CVARD(\"pr_enable_profiling\", \"0\", \"Enables profiling support. Will run more slowly. Change the map and then use the profile_ssqc/profile_csqc commands to see the results.\");\n#ifdef HAVE_CLIENT\ncvar_t pr_precachepic_slow = CVARD(\"pr_precachepic_slow\", \"0\", \"Legacy setting. Should be set to 0 where supported.\");\n#endif\nint tokenizeqc(const char *str, qboolean dpfuckage);\n\nvoid PF_buf_shutdown(pubprogfuncs_t *prinst);\n\nvoid skel_info_f(void);\nvoid skel_generateragdoll_f(void);\nvoid *PR_PointerToNative_Resize(pubprogfuncs_t *inst, pint_t ptr, size_t offset, size_t datasize);\t\t\t//dangerous version\nvoid *PR_PointerToNative_MoInvalidate(pubprogfuncs_t *inst, pint_t ptr, size_t offset, size_t datasize);\t//safer faily version.\n\n#ifdef __SSE2__\n#include \"xmmintrin.h\"\n#endif\n\nvoid PF_Common_RegisterCvars(void)\n{\n#ifndef QUAKETC\n#ifdef __SSE2__\n\t//disable FTZ and DAZ, in case some compiler left them on...\n\tunsigned int mxcsr = _mm_getcsr();\n\tif (mxcsr & 0x8040)\n\t{\n\t\tif (COM_CheckParm(\"-nodaz\"))\n\t\t{\n\t\t\tCon_DPrintf(\"Disabling DAZ. This may have performance implications.\\n\");\n\t\t\t_mm_setcsr(mxcsr & ~(0x8040));\n\t\t}\n\t\telse\n\t\t\tCon_DPrintf(CON_WARNING \"denormalised floats are disabled. Use -nodaz to re-enable if mods malfunction\\n\");\n\t}\n#else\n\tvolatile union\n\t{\n\t\tint i;\n\t\tfloat f;\n\t} a, b;\n\ta.i = 1;\n\tb.i = 1;\n\tif (!(a.f && b.f))\n\t\tCon_Printf(CON_WARNING \"denormalised floats are disabled. Some mods might may malfunction\\n\");\n#endif\n#endif\n\n\n\tCvar_Register (&sv_gameplayfix_blowupfallenzombies, cvargroup_progs);\n\tCvar_Register (&sv_gameplayfix_findradiusdistancetobox, cvargroup_progs);\n\tCvar_Register (&sv_gameplayfix_linknonsolid, cvargroup_progs);\n\tCvar_Register (&sv_gameplayfix_droptofloorstartsolid, cvargroup_progs);\n\tCvar_Register (&dpcompat_findradiusarealinks, cvargroup_progs);\n#ifdef HAVE_LEGACY\n\tCvar_Register (&dpcompat_strcat_limit, cvargroup_progs);\n#endif\n\tCvar_Register (&pr_droptofloorunits, cvargroup_progs);\n\tCvar_Register (&pr_brokenfloatconvert, cvargroup_progs);\n\tCvar_Register (&pr_tempstringcount, cvargroup_progs);\n\tCvar_Register (&pr_tempstringsize, cvargroup_progs);\n\tCvar_Register (&pr_gc_threaded, cvargroup_progs);\n#ifdef WEBCLIENT\n\tCvar_Register (&pr_enable_uriget, cvargroup_progs);\n#endif\n\tCvar_Register (&pr_enable_profiling, cvargroup_progs);\n\tCvar_Register (&pr_sourcedir, cvargroup_progs);\n\tCvar_Register (&pr_fixbrokenqccarrays, cvargroup_progs);\n\tCvar_Register (&utf8_enable, cvargroup_progs);\n\tCvar_Register (&pr_autocreatecvars, cvargroup_progs);\n#ifdef HAVE_CLIENT\n\tCvar_Register (&pr_precachepic_slow, cvargroup_progs);\n#endif\n\n#ifdef RAGDOLL\n\tCmd_AddCommand(\"skel_info\", skel_info_f);\n\tCmd_AddCommand(\"skel_generateragdoll\", skel_generateragdoll_f);\n#endif\n\n\tWPhys_Init();\n\n#ifdef ENGINE_ROUTING\n\tPR_Route_Init();\n#endif\n}\n\nqofs_t PR_ReadBytesString(char *str)\n{\n\t//use doubles, so we can cope with eg \"5.3mb\" or much larger values\n\tdouble d = strtod(str, &str);\n\tif (d < 0)\n\t{\n#if (defined(_WIN64) && !defined(WINRT)) || (defined(__linux__)&&defined(__LP64__))\n\t\treturn 0x80000000;\t//use of virtual address space rather than physical memory means we can just go crazy and use the max of 2gb.\n#elif defined(FTE_TARGET_WEB)\n\t\treturn 8*1024*1024;\n#else\n\t\treturn 32*1024*1024;\n#endif\n\t}\n\tif (*str == 'g')\n\t\td *= 1024*1024*1024;\n\tif (*str == 'm')\n\t\td *= 1024*1024;\n\tif (*str == 'k')\n\t\td *= 1024;\n\treturn d;\n}\n\n//just prints out a warning with stack trace. so I can throttle spammy stack traces.\nstatic void PF_Warningf(pubprogfuncs_t *prinst, const char *fmt, ...)\n{\n\tva_list\t\targptr;\n\tchar\t\tstring[1024];\n\n\tva_start (argptr, fmt);\n\tvsnprintf (string, sizeof(string)-1, fmt, argptr);\n\tva_end (argptr);\n\n\tif (developer.ival)\n\t\tPR_StackTrace(prinst, false);\n\tCon_Printf(\"%s\", string);\n}\n\nconst char *PF_VarString (pubprogfuncs_t *prinst, int\tfirst, struct globalvars_s *pr_globals)\n{\n#define VARSTRINGLEN 65536+8\n\tint\t\ti;\n\tstatic char buffer[2][VARSTRINGLEN];\n\tstatic int bufnum;\n\tconst char *s;\n\tchar *out;\n\n\tif (prinst->callargc - first == 1)\n\t\treturn PR_GetStringOfs(prinst, OFS_PARM0+first*3);\t//no need to copy/etc.\n\n\tout = buffer[(bufnum++)&1];\n\n\tout[0] = 0;\n\tfor (i=first ; i<prinst->callargc ; i++)\n\t{\n//\t\tif (G_INT(OFS_PARM0+i*3) < 0 || G_INT(OFS_PARM0+i*3) >= 1024*1024);\n//\t\t\tbreak;\n\n\t\ts = PR_GetStringOfs(prinst, OFS_PARM0+i*3);\n\t\tif (s)\n\t\t{\n\t\t\tif (strlen(out)+strlen(s)+1 >= VARSTRINGLEN)\n\t\t\t\tCon_DPrintf(\"VarString (builtin call ending with strings) exceeded maximum string length of %i chars\", VARSTRINGLEN);\n\n\t\t\tQ_strncatz (out, s, VARSTRINGLEN);\n\t\t}\n\t}\n\treturn out;\n}\n\nstatic int debuggerresume;\nstatic int debuggerresumeline;\nextern int isPlugin;\t//if 2, we were invoked by a debugger, and we need to give it debug locations (and it'll feed us continue/steps/breakpoints)\n#ifndef SERVERONLY\nstatic int debuggerstacky;\n#endif\n\n#if defined(_WIN32) && !defined(FTE_SDL) && !defined(_XBOX)\n\t#include <windows.h>\n\tvoid INS_UpdateGrabs(int fullscreen, int activeapp);\n#else\n\t#include <unistd.h>\n#endif\n\nint QCLibEditor(pubprogfuncs_t *prinst, const char *filename, int *line, int *statement, int firststatement, char *error, pbool fatal);\nvoid QCLoadBreakpoints(const char *vmname, const char *progsname)\n{\t//this asks the gui to reapply any active breakpoints and waits for them so that any spawn functions can be breakpointed properly.\n\textern int\t\t\t\tisPlugin;\n\tif (isPlugin >= 2)\n\t{\n#ifdef SERVERONLY\n\t\tSV_GetConsoleCommands();\n#else\n\t\tSys_SendKeyEvents();\n#endif\n\t\tdebuggerresume = -1;\n\t\tfprintf(stdout, \"qcreloaded \\\"%s\\\" \\\"%s\\\"\\n\", vmname, progsname);\n\t\tfflush(stdout);\n#if defined(_WIN32) && !defined(FTE_SDL)\n#ifndef SERVERONLY\n\t\tINS_UpdateGrabs(false, false);\n#endif\n#endif\n\t\twhile(debuggerresume == -1 && !wantquit)\n\t\t{\n#if defined(_WIN32) && !defined(FTE_SDL)\n\t\t\tSleep(10);\n#else\n\t\t\tusleep(10*1000);\n#endif\n#ifdef SERVERONLY\n\t\t\tSV_GetConsoleCommands();\n#else\n\t\t\tSys_SendKeyEvents();\n#endif\n\t\t}\n\t}\n}\nextern cvar_t pr_sourcedir;\npubprogfuncs_t *debuggerinstance;\nconst char *debuggerfile;\nsize_t debuggerwnd;\n\nqboolean QCExternalDebuggerCommand(char *text)\n{\n#if defined(CSQC_DAT) && !defined(SERVERONLY)\n\textern world_t csqc_world;\n#endif\n#if defined(MENU_DAT) && !defined(SERVERONLY)\n\textern world_t menu_world;\n#endif\n\n\tif (!isPlugin)\n\t\treturn false;\n\tif ((!strncmp(text, \"qcstep\", 6) && (text[6] == 0 || text[6] == ' ')) || (!strncmp(text, \"qcresume\", 8) && (text[8] == 0 || text[8] == ' ')))\n\t{\n//\t\tint l;\n\t\tif (text[2] == 's')\n\t\t{\n\t\t\ttext += 6;\n\t\t\twhile(*text==' ' || *text=='\\t')\n\t\t\t\ttext++;\n\t\t\tif (!strncmp(text, \"out\", 3))\n\t\t\t\tdebuggerresume = DEBUG_TRACE_OUT;\n\t\t\telse if (!strncmp(text, \"over\", 3))\n\t\t\t\tdebuggerresume = DEBUG_TRACE_OVER;\n\t\t\telse\n\t\t\t\tdebuggerresume = DEBUG_TRACE_INTO;\n//\t\t\tl = atoi(text+7);\n\t\t}\n\t\telse\n\t\t{\n//\t\t\tl = atoi(text+9);\n\t\t\tdebuggerresume = DEBUG_TRACE_OFF;\n\t\t}\n//\t\tif (l)\n//\t\t\tdebuggerresumeline = l;\n\t}\n\telse if (!strncmp(text, \"qcjump \", 7))\n\t{\n\t\tchar file[MAX_QPATH];\n\t\tchar linebuf[32];\n\t\ttext += 7;\n\t\ttext = COM_ParseOut(text, file, sizeof(file));\n\t\ttext = COM_ParseOut(text, linebuf, sizeof(linebuf));\n\n\t\tif (debuggerinstance && debuggerfile && !Q_strcasecmp(file, debuggerfile))\n\t\t{\n\t\t\tdebuggerresumeline = atoi(linebuf);\n\t\t\tdebuggerresume = DEBUG_TRACE_NORESUME;\t//'resume' from the debugger only to break again, so we know the new line number (if they tried setting the line to a blank one)\n\t\t}\n\t}\n\telse if (!strncmp(text, \"debuggerwnd \", 11))\n\t{\n\t\t//send focus to this window when debugging\n\t\tdebuggerwnd = strtoul(text+12, NULL, 0);\n\t}\n\telse if (!strncmp(text, \"qcinspect \", 10))\n\t{\n\t\t//called on mouse-over events in the gui\n\t\tchar *variable;\n//\t\tchar *function;\n\t\tchar resultbuffer[8192], tmpbuffer[8192];\n\t\tchar *vmnames[4] = {\"cur: \", \"ssqc: \", \"csqc: \", \"menu: \"};\n\t\tchar *values[4] = {NULL, NULL, NULL, NULL};\n\t\tint i;\n\t\tCmd_TokenizeString(text, false, false);\n\t\tvariable = Cmd_Argv(1);\n//\t\tfunction = Cmd_Argv(2);\n\n\t\t\n\t\t//togglebreakpoint just finds the first statement (via the function table for file names) with the specified line number, and sets some unused high bit that causes it to be an invalid opcode.\n\t\tif (debuggerinstance)\n\t\t{\n\t\t\tif (debuggerinstance->EvaluateDebugString)\n\t\t\t\tvalues[0] = debuggerinstance->EvaluateDebugString(debuggerinstance, variable);\n\t\t}\n\t\telse\n\t\t{\n#ifndef CLIENTONLY\n\t\t\tif (sv.world.progs && sv.world.progs->EvaluateDebugString)\n\t\t\t\tvalues[1] = sv.world.progs->EvaluateDebugString(sv.world.progs, variable);\n#endif\n#ifndef SERVERONLY\n#ifdef CSQC_DAT\n\t\t\tif (csqc_world.progs && csqc_world.progs->EvaluateDebugString)\n\t\t\t\tvalues[2] = csqc_world.progs->EvaluateDebugString(csqc_world.progs, variable);\n#endif\n#ifdef MENU_DAT\n\t\t\tif (menu_world.progs && menu_world.progs->EvaluateDebugString)\n\t\t\t\tvalues[3] = menu_world.progs->EvaluateDebugString(menu_world.progs, variable);\n#endif\n#endif\n\t\t}\n\n\t\tfor (i = 0, *resultbuffer = 0; i < 4; i++)\n\t\t{\n\t\t\tif (!values[i])\n\t\t\t\tcontinue;\n\t\t\tif (!strcmp(values[i], \"(unable to evaluate)\"))\n\t\t\t\tcontinue;\n\t\t\tif (*resultbuffer || !debuggerinstance)\n\t\t\t\tQ_strncatz(resultbuffer, \"\\n\", sizeof(resultbuffer));\n\t\t\tif (!debuggerinstance)\n\t\t\t{\n\t\t\t\tQ_strncatz(resultbuffer, vmnames[i], sizeof(resultbuffer));\n\t\t\t}\n\t\t\tQ_strncatz(resultbuffer, COM_QuotedString(values[i], tmpbuffer, sizeof(tmpbuffer), true), sizeof(resultbuffer));\n\t\t}\n\n\t\tprintf(\"qcvalue \\\"%s\\\" %s\\n\", variable, COM_QuotedString(resultbuffer, tmpbuffer, sizeof(tmpbuffer), false));\n\t\tfflush(stdout);\n\t}\n\telse if (!strncmp(text, \"qcreload\", 8))\n\t{\n#if defined(MENU_DAT) && !defined(SERVERONLY)\n\t\tCbuf_AddText(\"menu_restart\\n\", RESTRICT_LOCAL);\n#endif\n#ifndef CLIENTONLY\n\t\tif (sv.state)\n\t\t\tCbuf_AddText(\"restart\\n\", RESTRICT_LOCAL);\n#endif\n//\t\tHost_EndGame(\"Reloading QC\");\n\t\tdebuggerresume = DEBUG_TRACE_ABORTERROR;\n\t}\n\telse if (!strncmp(text, \"qcbreakpoint \", 13))\n\t{\n\t\tint mode;\n\t\tchar *filename;\n\t\tint line;\n\t\tCmd_TokenizeString(text, false, false);\n\t\tmode = strtoul(Cmd_Argv(1), NULL, 0);\n\t\tfilename = Cmd_Argv(2);\n\t\tline = strtoul(Cmd_Argv(3), NULL, 0);\n\t\t//togglebreakpoint just finds the first statement (via the function table for file names) with the specified line number, and sets some unused high bit that causes it to be an invalid opcode.\n#ifndef SERVERONLY\n#ifdef CSQC_DAT\n\t\tif (csqc_world.progs && csqc_world.progs->ToggleBreak)\n\t\t\tcsqc_world.progs->ToggleBreak(csqc_world.progs, filename, line, mode);\n#endif\n#ifdef MENU_DAT\n\t\tif (menu_world.progs && menu_world.progs->ToggleBreak)\n\t\t\tmenu_world.progs->ToggleBreak(menu_world.progs, filename, line, mode);\n#endif\n#endif\n#ifndef CLIENTONLY\n\t\tif (sv.world.progs && sv.world.progs->ToggleBreak)\n\t\t\tsv.world.progs->ToggleBreak(sv.world.progs, filename, line, mode);\n#endif\n\t}\n\telse\n\t\treturn false;\n\treturn true;\n}\n\nint QDECL QCEditor (pubprogfuncs_t *prinst, const char *filename, int *line, int *statement, int firststatement, char *reason, pbool fatal)\n{\n//#if defined(_WIN32) && !defined(FTE_SDL) && !defined(_XBOX)\n\tif (isPlugin >= 2)\n\t{\n\t\tif (wantquit)\n\t\t\treturn DEBUG_TRACE_ABORTERROR;\n\t\tif (!*filename || !line || !*line)\t//don't try editing an empty line, it won't work\n\t\t{\n\t\t\tif (!*filename)\n\t\t\t\tCon_Printf(\"Unable to debug, please disable optimisations\\n\");\n\t\t\telse\n\t\t\t\tCon_Printf(\"Unable to debug, please provide line number info\\n\");\n\t\t\tif (fatal)\n\t\t\t\treturn DEBUG_TRACE_ABORTERROR;\n\t\t\treturn DEBUG_TRACE_OFF;\n\t\t}\n#ifdef SERVERONLY\n\t\tSV_GetConsoleCommands();\n#else\n\t\tSys_SendKeyEvents();\n#endif\n\t\tdebuggerresume = -1;\n\t\tdebuggerresumeline = *line;\n#if defined(_WIN32) && !defined(FTE_SDL)\n\t\tif (debuggerwnd)\n\t\t\tSetForegroundWindow((HWND)debuggerwnd);\n#endif\n\t\tif (reason)\n\t\t{\n\t\t\tchar tmpbuffer[8192];\n\t\t\tprintf(\"qcfault \\\"%s\\\":%i %s\\n\", filename, *line, COM_QuotedString(reason, tmpbuffer, sizeof(tmpbuffer), false));\n\t\t}\n\t\telse\n\t\t\tprintf(\"qcstep \\\"%s\\\":%i\\n\", filename, *line);\n\t\tfflush(stdout);\n\t\tdebuggerinstance = prinst;\n\t\tdebuggerfile = filename;\n#ifdef SERVERONLY\n\t\tif (reason)\n\t\t{\n\t\t\tprintf(\"Debugger triggered at \\\"%s\\\":%i, %s\\n\", filename, *line, reason);\n\t\t\tPR_StackTrace(prinst, 1);\n\t\t}\n\t\twhile(debuggerresume == -1 && !wantquit)\n\t\t{\n\t\t\tSys_Sleep(1.0/100);\n\t\t\tSV_GetConsoleCommands();\n\t\t}\n#else\n#if defined(_WIN32) && !defined(FTE_SDL)\n\t\tINS_UpdateGrabs(false, false);\n#endif\n\t\tif (reason)\n\t\t\tCon_Footerf(NULL, false, \"^bDebugging: %s\", reason);\n\t\telse\n\t\t\tCon_Footerf(NULL, false, \"^bDebugging\");\n\t\twhile(debuggerresume == -1 && !wantquit)\n\t\t{\n\t\t\tSys_Sleep(1.0/100);\n\t\t\tSys_SendKeyEvents();\n\n\t\t\tif (qrenderer)\n\t\t\t{\n\t\t\t\t//FIXME: display a stack trace and locals instead\n\t\t\t\tR2D_ImageColours(0.1, 0, 0, 1);\n\t\t\t\tR2D_FillBlock(0, 0, vid.width, vid.height);\n\t\t\t\tCon_DrawConsole(vid.height/2, true);\t//draw console at half-height\n\t\t\t\tdebuggerstacky = vid.height/2;\n\t\t\t\tif (debuggerstacky)\n\t\t\t\t\tPR_StackTrace(prinst, 2);\n\t\t\t\tdebuggerstacky = 0;\n\t\t\t\tif (R2D_Flush)\n\t\t\t\t\tR2D_Flush();\n\t\t\t\tVID_SwapBuffers();\n\t\t\t}\n\t\t}\n\t\tCon_Footerf(NULL, false, \"\");\n#endif\n\t\t*line = debuggerresumeline;\n\t\tdebuggerinstance = NULL;\n\t\tdebuggerfile = NULL;\n\t\tif (wantquit)\n\t\t\treturn DEBUG_TRACE_ABORTERROR;\n\t\treturn debuggerresume;\n\t}\n//#endif\n\n#ifdef TEXTEDITOR\n\treturn QCLibEditor(prinst, filename, line, statement, firststatement, reason, fatal);\n#else\n\tif (fatal)\n\t\treturn DEBUG_TRACE_ABORTERROR;\n\treturn DEBUG_TRACE_OFF;\t//get lost\n#endif\n}\n\n//tag warnings/errors for easier debugging.\nint PR_Print (qboolean dev, const char *msg)\n{\n\tchar\t\tfile[MAX_OSPATH];\n\tint\t\t\tline = -1;\n\tchar\t\t*ls, *ms, *nl;\n\n\twhile (*msg)\n\t{\n\t\tnl = strchr(msg, '\\n');\n\t\tif (nl)\n\t\t\t*nl = 0;\n\t\t*file = 0;\n\n\t\t/*when we're debugging, stack dumps should appear directly on-screen instead of being shoved on the console*/\n#ifndef SERVERONLY\n\t\tif (debuggerstacky)\n\t\t{\n\t\t\tDraw_FunString(0, debuggerstacky, msg);\n\t\t\tdebuggerstacky += 8;\n\t\t\tif (nl)\n\t\t\t\tmsg = nl+1;\n\t\t\telse\n\t\t\t\tbreak;\n\t\t\tcontinue;\n\t\t}\n#endif\n\n\t\tls = strchr(msg, ':');\n\t\tif (ls)\n\t\t{\n\t\t\tms = strchr(ls+1, ':');\n\t\t\tif (ms)\n\t\t\t{\n\t\t\t\t*ms = '\\0';\n\t\t\t\tif (!strchr(msg, ' ') && !strchr(msg, '\\t') && !strchr(msg, '\\r') && (ls - msg) < sizeof(file)-1)\n\t\t\t\t{\n\t\t\t\t\tmemcpy(file, msg, ls - msg);\n\t\t\t\t\tfile[ls-msg] = 0;\n\t\t\t\t\tline = strtoul(ls+1, NULL, 0);\n\t\t\t\t}\n\t\t\t\t*ms = ':';\n\t\t\t}\n\t\t}\n\n\t\tif (dev)\n\t\t{\n\t\t\tif (*file)\n\t\t\t\tCon_DPrintf (\"^[%s\\\\edit\\\\%s:%i^]\", msg, file, line);\n\t\t\telse\n\t\t\t\tCon_DPrintf (\"%s\", msg);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (*file)\n\t\t\t\tCon_Printf (\"^[%s\\\\edit\\\\%s:%i^]\", msg, file, line);\n\t\t\telse\n\t\t\t\tCon_Printf (\"%s\", msg);\n\t\t}\n\n\t\tif (nl)\n\t\t{\n\t\t\tif (dev)\n\t\t\t\tCon_DPrintf (\"\\n\");\n\t\t\telse\n\t\t\t\tCon_Printf (\"\\n\");\n\t\t\tmsg = nl+1;\n\t\t}\n\t\telse\n\t\t\tbreak;\n\t}\n\treturn 0;\n}\n\nint PR_Printf (const char *fmt, ...)\n{\n\tva_list\t\targptr;\n\tchar\t\tmsg[1024];\n\n\tva_start (argptr,fmt);\n\tvsnprintf (msg,sizeof(msg), fmt,argptr);\n\tva_end (argptr);\n\n\treturn PR_Print(false, msg);\n}\nint PR_DPrintf (const char *fmt, ...)\n{\n\tva_list\t\targptr;\n\tchar\t\tmsg[1024];\n\n\tva_start (argptr,fmt);\n\tvsnprintf (msg,sizeof(msg), fmt,argptr);\n\tva_end (argptr);\n\n\treturn PR_Print(true, msg);\n}\n\n#define MAX_TEMPSTRS\t((int)pr_tempstringcount.value)\n#define MAXTEMPBUFFERLEN\t((int)pr_tempstringsize.value)\nstring_t PR_TempString(pubprogfuncs_t *prinst, const char *str)\n{\n\tchar *tmp;\n\tif (!prinst->user.tempstringbase)\n\t\treturn prinst->TempString(prinst, str);\n\n\tif (!str || !*str)\n\t\treturn 0;\n\n\tif (prinst->user.tempstringnum == MAX_TEMPSTRS)\n\t\tprinst->user.tempstringnum = 0;\n\ttmp = prinst->user.tempstringbase + (prinst->user.tempstringnum++)*MAXTEMPBUFFERLEN;\n\n\tQ_strncpyz(tmp, str, MAXTEMPBUFFERLEN);\n\treturn tmp - prinst->stringtable;\n}\n\nvoid PF_InitTempStrings(pubprogfuncs_t *prinst)\n{\n\tif (pr_tempstringcount.value > 0 && pr_tempstringcount.value < 2)\n\t\tpr_tempstringcount.value = 2;\n\tif (pr_tempstringsize.value < 256)\n\t\tpr_tempstringsize.value = 256;\n\tpr_tempstringcount.flags |= CVAR_NOSET;\n\tpr_tempstringsize.flags |= CVAR_NOSET;\n\n\tif (pr_tempstringcount.value >= 2)\n\t\tprinst->user.tempstringbase = prinst->AddString(prinst, \"\", MAXTEMPBUFFERLEN*MAX_TEMPSTRS, false);\n\telse\n\t\tprinst->user.tempstringbase = 0;\n\tprinst->user.tempstringnum = 0;\n}\n\n//#define\tRETURN_EDICT(pf, e) (((int *)pr_globals)[OFS_RETURN] = EDICT_TO_PROG(pf, e))\n#define\tRETURN_SSTRING(s) (((int *)pr_globals)[OFS_RETURN] = PR_SetString(prinst, s))\t//static - exe will not change it.\n#define\tRETURN_TSTRING(s) (((int *)pr_globals)[OFS_RETURN] = PR_TempString(prinst, s))\t//temp (static but cycle buffers)\n#define\tRETURN_CSTRING(s) (((int *)pr_globals)[OFS_RETURN] = PR_SetString(prinst, s))\t//semi-permanant. (hash tables?)\n#define\tRETURN_PSTRING(s) (((int *)pr_globals)[OFS_RETURN] = PR_NewString(prinst, s, 0))\t//permanant\n\n\n\nvoid VARGS PR_BIError(pubprogfuncs_t *progfuncs, char *format, ...)\n{\n\tva_list\t\targptr;\n\tstatic char\t\tstring[2048];\n\n\tva_start (argptr, format);\n\tvsnprintf (string,sizeof(string)-1, format,argptr);\n\tva_end (argptr);\n\n\tif (developer.value || !progfuncs)\n\t{\n\t\tstruct globalvars_s *pr_globals = PR_globals(progfuncs, PR_CURRENT);\n\t\tPR_RunWarning(progfuncs, CON_ERROR\"%s\\n\", string);\n\t\tG_INT(OFS_RETURN)=0;\t//just in case it was a float and should be an ent...\n\t\tG_INT(OFS_RETURN+1)=0;\n\t\tG_INT(OFS_RETURN+2)=0;\n\t}\n\telse\n\t{\n\t\tPR_StackTrace(progfuncs, false);\n//\t\tPR_AbortStack(progfuncs);\n\t\tprogfuncs->parms->Abort (CON_ERROR\"%s\", string);\n\t}\n}\n\n\npbool QDECL QC_WriteFile(const char *name, void *data, int len)\n{\n\tchar buffer[256];\n\tQ_snprintfz(buffer, sizeof(buffer), \"%s\", name);\n\tCOM_WriteFile(buffer, FS_GAMEONLY, data, len);\n\treturn true;\n}\n\n//a little loop so we can keep track of used mem\nvoid *VARGS PR_CB_Malloc(int size)\n{\n\treturn BZ_Malloc(size);//Z_TagMalloc (size, 100);\n}\nvoid VARGS PR_CB_Free(void *mem)\n{\n\tBZ_Free(mem);\n}\n\n////////////////////////////////////////////////////\n//JSON stuff.\ntypedef struct qcjson_s\n{\n\tint type;\n\tstring_t name;\n\tunion\n\t{\n\t\tstruct\n\t\t{\n\t\t\tint childptr;\n\t\t\tunsigned int count;\t//for arrays and objects.\n\t\t};\n\t\tdouble num;\t\t\t//for number types.\n\t\tstring_t strofs;\t//for strings.\n\t} u;\n} qcjson_t;\nstatic qcjson_t json_null = {json_type_null};\t//dummy safe node that we poke on bad inputs.\n#define JSONFromQC(qcptr) ((unsigned int)qcptr >= prinst->stringtablesize-sizeof(qcjson_t))?PR_BIError (prinst, \"PR_JSONFromQC: bad pointer\"),&json_null:qcptr?(qcjson_t*)((char*)prinst->stringtable + qcptr):&json_null\n#define RETURN_JSON(r) G_INT(OFS_RETURN) = (const char*)r - prinst->stringtable\n\n#define JSON_PERSIST_STRINGDATA\t//slower but safer\nstatic void PR_JSON_Count(json_t *r, size_t *nodes, size_t *strings)\t//counts the nodes and bytes for string data.\n{\n\t*nodes+=1;\n\tif (*r->name)\n\t\t*strings += strlen(r->name)+1;\n\tsafeswitch(r->type)\n\t{\n\tcase json_type_object:\n\tcase json_type_array:\n\t\tfor(r = r->child; r; r = r->sibling)\n\t\t\tPR_JSON_Count(r, nodes, strings);\n\t\tbreak;\n\tcase json_type_string:\n#ifndef JSON_PERSIST_STRINGDATA\n\t\t*strings += JSON_ReadBody(r, NULL, 0)+1;\n\t\tbreak;\n#endif\n\tcase json_type_true:\n\tcase json_type_false:\n\tcase json_type_null:\n\tcase json_type_number:\n\tsafedefault:\n\t\tbreak;\n\t}\n}\nstatic void PR_JSON_Linearise(pubprogfuncs_t *prinst, json_t *r, qcjson_t *out, qcjson_t **nodes, char **strings)\n{\n\tjson_t *c;\n\tsize_t children = 0;\n\tout->type = r->type;\n\n\t//give it a name\n\tif (*r->name)\n\t{\n\t\tout->name = *strings - prinst->stringtable;\n\t\tmemcpy(*strings, r->name, strlen(r->name)+1);\n\t\t*strings += strlen(r->name)+1;\n\t}\n\n\t//make sure its values are valid.\n\tsafeswitch(out->type)\n\t{\n\tcase json_type_string:\n#ifdef JSON_PERSIST_STRINGDATA\n\t\t{\t//allocate a tempstring for each, so that they last beyond node destruction.\n\t\t\tsize_t sz = JSON_ReadBody(r, NULL, 0);\n\t\t\tchar *tmp = alloca(sz+1);\n\t\t\tJSON_ReadBody(r, tmp, sz+1);\n\t\t\tout->u.strofs = PR_TempString(prinst, tmp);\n\t\t}\n#else\n\t\tout->u.strofs = *strings - prinst->stringtable;\n\t\tJSON_ReadBody(r, *strings, prinst->stringtablesize - out->u.strofs-1);\n\t\t*strings += strlen(*strings)+1;\n#endif\n\t\tbreak;\n\tcase json_type_object:\n\tcase json_type_array:\n\t\tout->u.childptr = (char*)*nodes - prinst->stringtable;\n\t\tfor(c = r->child; c; c = c->sibling)\n\t\t\tchildren++;\n\t\tout->u.count = children;\n\t\tout = *nodes;\n\t\t*nodes+=children;\n\t\tfor(c = r->child; c; c = c->sibling, out++)\n\t\t\tPR_JSON_Linearise(prinst, c, out, nodes, strings);\n\t\tbreak;\n\tcase json_type_false:\n\tcase json_type_null:\n\t\tout->u.num = false;\n\t\tbreak;\n\tcase json_type_true:\n\t\tout->u.num = true;\n\t\tbreak;\n\tcase json_type_number:\n\tsafedefault:\n\t\tout->u.num = JSON_ReadFloat(r, 0);\n\t\tbreak;\n\t}\n}\nvoid QCBUILTIN PF_json_parse(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tjson_t *inroot = JSON_Parse(PR_GetStringOfs(prinst, OFS_PARM0));\n\tqcjson_t *outroot, *outnodes;\n\tchar *outstrings;\n\tsize_t n = 0, s = 0;\n\tif (inroot)\n\t{\n\t\t//linearise the json nodes for easy access (just use qc pointers instead of needing lots of separate handles)\n\t\tPR_JSON_Count(inroot, &n, &s);\n\t\toutnodes = prinst->AddressableAlloc(prinst, sizeof(*outroot)*n + s);\n\t\toutstrings = (char*)(outnodes + n);\n\n\t\toutroot = outnodes++;\n\t\tPR_JSON_Linearise(prinst, inroot, outroot, &outnodes, &outstrings);\n\n\t\tJSON_Destroy(inroot);\t//our input string becomes irrelevant at this point\n\n\t\tRETURN_JSON(outroot);\n\t}\n\telse\n\t\tG_INT(OFS_RETURN) = 0;\n}\n\nvoid QCBUILTIN PF_json_get_value_type(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tqcjson_t *handle = JSONFromQC(G_INT(OFS_PARM0));\n\tG_INT(OFS_RETURN) = handle->type;\n}\nvoid QCBUILTIN PF_json_get_name(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tqcjson_t *handle = JSONFromQC(G_INT(OFS_PARM0));\n\tG_INT(OFS_RETURN) = handle->name;\n}\nvoid QCBUILTIN PF_json_get_integer(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tqcjson_t *handle = JSONFromQC(G_INT(OFS_PARM0));\n\tsafeswitch (handle->type)\n\t{\n\tcase json_type_number:\n\tcase json_type_true:\n\tcase json_type_false:\n\t\tG_INT(OFS_RETURN) = handle->u.num;\n\t\tbreak;\n\tcase json_type_string:\n\t\tG_INT(OFS_RETURN) = atoi(PR_GetString(prinst, handle->u.strofs));\n\t\tbreak;\n\tcase json_type_object:\n\tcase json_type_array:\n\tcase json_type_null:\n\tsafedefault:\n\t\tG_INT(OFS_RETURN) = 0;\n\t\tbreak;\n\t}\n}\nvoid QCBUILTIN PF_json_get_float(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tqcjson_t *handle = JSONFromQC(G_INT(OFS_PARM0));\n\tsafeswitch (handle->type)\n\t{\n\tcase json_type_number:\n\tcase json_type_true:\n\tcase json_type_false:\n\t\tG_FLOAT(OFS_RETURN) = handle->u.num;\n\t\tbreak;\n\tcase json_type_string:\n\t\tG_FLOAT(OFS_RETURN) = atof(PR_GetString(prinst, handle->u.strofs));\n\t\tbreak;\n\tcase json_type_object:\n\tcase json_type_array:\n\tcase json_type_null:\n\tsafedefault:\n\t\tG_FLOAT(OFS_RETURN) = 0;\n\t\tbreak;\n\t}\n}\nvoid QCBUILTIN PF_json_get_string(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tqcjson_t *handle = JSONFromQC(G_INT(OFS_PARM0));\n\tif (handle->type != json_type_string)\n\t\tG_INT(OFS_RETURN) = 0;\n\telse\n\t\tG_INT(OFS_RETURN) = handle->u.strofs;\n}\nvoid QCBUILTIN PF_json_get_child_at_index(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tqcjson_t *handle = JSONFromQC(G_INT(OFS_PARM0));\n\tsize_t idx = G_INT(OFS_PARM1);\n\tG_INT(OFS_RETURN) = 0;\t//assume the worst\n\tswitch(handle->type)\n\t{\n\tcase json_type_array:\n\tcase json_type_object:\n\t\tif (idx < handle->u.count)\n\t\t\tG_INT(OFS_RETURN) = handle->u.childptr + idx*sizeof(*handle);\t//don't really need to validate this here.\n\t\tbreak;\n\tdefault:\n\t\tbreak;\n\t}\n}\nvoid QCBUILTIN PF_json_get_length(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tqcjson_t *handle = JSONFromQC(G_INT(OFS_PARM0));\n\tswitch (handle->type)\n\t{\n\tcase json_type_object:\n\tcase json_type_array:\n\t\tG_INT(OFS_RETURN) = handle->u.count;\n\t\tbreak;\n\tdefault:\n\t\tG_INT(OFS_RETURN) = 0;\n\t\tbreak;\n\t}\n}\nvoid QCBUILTIN PF_json_find_object_child(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tqcjson_t *parent = JSONFromQC(G_INT(OFS_PARM0));\n\tconst char *childname = PR_GetStringOfs(prinst, OFS_PARM1);\n\tunsigned int idx;\n\tG_INT(OFS_RETURN) = 0;\t//assume the worst\n\tswitch (parent->type)\n\t{\n\tcase json_type_object:\n\tcase json_type_array:\n\t\tfor (idx = 0; idx < parent->u.count; idx++)\n\t\t{\n\t\t\tqcjson_t *childnode = JSONFromQC(parent->u.childptr + idx*sizeof(*childnode));\n\t\t\tif (!strcmp(childname, PR_GetString(prinst, childnode->name)))\n\t\t\t{\n\t\t\t\tRETURN_JSON(childnode);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tbreak;\n\tdefault:\n\t\tbreak;\n\t}\n}\n\n#ifdef FTE_TARGET_WEB\n#include <emscripten.h>\n//FIXME: make sure the module is signed/'local'/trusted\nvoid QCBUILTIN PF_js_run_script(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char *jscript = PR_GetStringOfs(prinst, OFS_PARM0);\n\tconst char *ret;\n\tret = emscripten_run_script_string(jscript);\n\tif (ret)\n\t\tG_INT(OFS_RETURN) = PR_TempString(prinst, ret);\n\telse\n\t\tG_INT(OFS_RETURN) = 0;\n}\n#endif\n\n\n////////////////////////////////////////////////////\n//model functions\n//DP_QC_GETSURFACE\nstatic void PF_BuildSurfaceMesh(model_t *model, unsigned int surfnum)\n{\n\t//this function might be called on dedicated servers.\n#ifdef Q1BSPS\n\tvoid ModQ1_Batches_BuildQ1Q2Poly(model_t *mod, msurface_t *surf, builddata_t *cookie);\n\tif (model->fromgame == fg_quake || model->fromgame == fg_halflife)\n\t\tModQ1_Batches_BuildQ1Q2Poly(model, &model->surfaces[surfnum], NULL);\n#endif\n\t//fixme: q3...\n}\n// #434 float(entity e, float s) getsurfacenumpoints (DP_QC_GETSURFACE)\nvoid QCBUILTIN PF_getsurfacenumpoints(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tunsigned int surfnum;\n\tmodel_t *model;\n\twedict_t *ent;\n\tworld_t *w = prinst->parms->user;\n\n\tent = G_WEDICT(prinst, OFS_PARM0);\n\tsurfnum = G_FLOAT(OFS_PARM1);\n\n\tmodel = w->Get_CModel(w, ent->v->modelindex);\n\n\tif (!model || model->type != mod_brush || surfnum >= model->nummodelsurfaces)\n\t\tG_FLOAT(OFS_RETURN) = 0;\n\telse\n\t{\n\t\tsurfnum += model->firstmodelsurface;\n\t\tif (!model->surfaces[surfnum].mesh)\n\t\t\tPF_BuildSurfaceMesh(model, surfnum);\n\t\tif (model->surfaces[surfnum].mesh)\n\t\t\tG_FLOAT(OFS_RETURN) = model->surfaces[surfnum].mesh->numvertexes;\n\t\telse\n\t\t\tG_FLOAT(OFS_RETURN) = 0;\t//not loaded properly.\n\t}\n}\n// #435 vector(entity e, float s, float n) getsurfacepoint (DP_QC_GETSURFACE)\nvoid QCBUILTIN PF_getsurfacepoint(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tunsigned int surfnum, pointnum;\n\tmodel_t *model;\n\twedict_t *ent;\n\tworld_t *w = prinst->parms->user;\n\n\tent = G_WEDICT(prinst, OFS_PARM0);\n\tsurfnum = G_FLOAT(OFS_PARM1);\n\tpointnum = G_FLOAT(OFS_PARM2);\n\n\tmodel = w->Get_CModel(w, ent->v->modelindex);\n\n\tif (!model || model->type != mod_brush || surfnum >= model->nummodelsurfaces)\n\t{\n\t\tG_FLOAT(OFS_RETURN+0) = 0;\n\t\tG_FLOAT(OFS_RETURN+1) = 0;\n\t\tG_FLOAT(OFS_RETURN+2) = 0;\n\t}\n\telse\n\t{\n\t\tsurfnum += model->firstmodelsurface;\n\n\t\tG_FLOAT(OFS_RETURN+0) = model->surfaces[surfnum].mesh->xyz_array[pointnum][0];\n\t\tG_FLOAT(OFS_RETURN+1) = model->surfaces[surfnum].mesh->xyz_array[pointnum][1];\n\t\tG_FLOAT(OFS_RETURN+2) = model->surfaces[surfnum].mesh->xyz_array[pointnum][2];\n\t}\n}\n// #436 vector(entity e, float s) getsurfacenormal (DP_QC_GETSURFACE)\nvoid QCBUILTIN PF_getsurfacenormal(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tunsigned int surfnum;\n\tmodel_t *model;\n\twedict_t *ent;\n\tworld_t *w = prinst->parms->user;\n\n\tent = G_WEDICT(prinst, OFS_PARM0);\n\tsurfnum = G_FLOAT(OFS_PARM1);\n\n\tmodel = w->Get_CModel(w, ent->v->modelindex);\n\n\tif (!model || model->type != mod_brush || surfnum >= model->nummodelsurfaces || !model->surfaces[model->firstmodelsurface+surfnum].plane)\n\t{\t//non-planar surfs don't always have a single plane... return nothing instead of breaking.\n\t\tG_FLOAT(OFS_RETURN+0) = 0;\n\t\tG_FLOAT(OFS_RETURN+1) = 0;\n\t\tG_FLOAT(OFS_RETURN+2) = 0;\n\t}\n\telse\n\t{\n\t\tsurfnum += model->firstmodelsurface;\n\n\t\tG_FLOAT(OFS_RETURN+0) = model->surfaces[surfnum].plane->normal[0];\n\t\tG_FLOAT(OFS_RETURN+1) = model->surfaces[surfnum].plane->normal[1];\n\t\tG_FLOAT(OFS_RETURN+2) = model->surfaces[surfnum].plane->normal[2];\n\t\tif (model->surfaces[surfnum].flags & SURF_PLANEBACK)\n\t\t\tVectorInverse(G_VECTOR(OFS_RETURN));\n\t}\n}\n// #437 string(entity e, float s) getsurfacetexture (DP_QC_GETSURFACE)\nvoid QCBUILTIN PF_getsurfacetexture(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tmodel_t *model;\n\twedict_t *ent;\n\tmsurface_t *surf;\n\tint surfnum;\n\tworld_t *w = prinst->parms->user;\n\n\tent = G_WEDICT(prinst, OFS_PARM0);\n\tsurfnum = G_FLOAT(OFS_PARM1);\n\tmodel = w->Get_CModel(w, ent->v->modelindex);\n\n\tG_INT(OFS_RETURN) = 0;\n\tif (!model || model->type != mod_brush)\n\t\treturn;\n\n\tif (surfnum >= model->nummodelsurfaces)\n\t\treturn;\n\tif (surfnum < 0)\n\t{\n\t\tsurfnum = -1-surfnum;\n\t\tif ((unsigned)surfnum >= (unsigned)model->numtextures)\n\t\t\treturn;\t//nope, outta range.\n\t\tif (!model->textures[surfnum])\n\t\t\treturn;\t//some maps are just broken.\n\t\tG_INT(OFS_RETURN) = PR_TempString(prinst, model->textures[surfnum]->name);\n\t}\n\telse\n\t{\n\t\tsurfnum += model->firstmodelsurface;\n\t\tsurf = &model->surfaces[surfnum];\n\t\tG_INT(OFS_RETURN) = PR_TempString(prinst, surf->texinfo->texture->name);\n\t}\n}\n#define TriangleNormal(a,b,c,n) ( \\\n\t(n)[0] = ((a)[1] - (b)[1]) * ((c)[2] - (b)[2]) - ((a)[2] - (b)[2]) * ((c)[1] - (b)[1]), \\\n\t(n)[1] = ((a)[2] - (b)[2]) * ((c)[0] - (b)[0]) - ((a)[0] - (b)[0]) * ((c)[2] - (b)[2]), \\\n\t(n)[2] = ((a)[0] - (b)[0]) * ((c)[1] - (b)[1]) - ((a)[1] - (b)[1]) * ((c)[0] - (b)[0]) \\\n\t)\nstatic float getsurface_clippointpoly(model_t *model, msurface_t *surf, pvec3_t point, pvec3_t bestcpoint, float bestdist)\n{\n\tint e, edge;\n\tvec3_t edgedir, edgenormal, cpoint, temp;\n\tmvertex_t *v1, *v2;\n\tfloat dist = DotProduct(point, surf->plane->normal) - surf->plane->dist;\n\t//don't care about SURF_PLANEBACK, the maths works out the same.\n\n\tif (dist*dist < bestdist)\n\t{\t//within a specific range\n\t\t//make sure it's within the poly\n\t\tVectorMA(point, dist, surf->plane->normal, cpoint);\n\t\tfor (e = surf->firstedge+surf->numedges; e > surf->firstedge; edge++)\n\t\t{\n\t\t\tedge = model->surfedges[--e];\n\t\t\tif (edge < 0)\n\t\t\t{\n\t\t\t\tv1 = &model->vertexes[model->edges[-edge].v[0]];\n\t\t\t\tv2 = &model->vertexes[model->edges[-edge].v[1]];\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tv2 = &model->vertexes[model->edges[edge].v[0]];\n\t\t\t\tv1 = &model->vertexes[model->edges[edge].v[1]];\n\t\t\t}\n\t\t\t\n\t\t\tVectorSubtract(v1->position, v2->position, edgedir);\n\t\t\tCrossProduct(edgedir, surf->plane->normal, edgenormal);\n\t\t\tif (!(surf->flags & SURF_PLANEBACK))\n\t\t\t{\n\t\t\t\tVectorNegate(edgenormal, edgenormal);\n\t\t\t}\n\t\t\tVectorNormalize(edgenormal);\n\n\t\t\tdist = DotProduct(v1->position, edgenormal) - DotProduct(cpoint, edgenormal);\n\t\t\tif (dist < 0)\n\t\t\t\tVectorMA(cpoint, dist, edgenormal, cpoint);\n\t\t}\n\n\t\tVectorSubtract(cpoint, point, temp);\n\t\tdist = DotProduct(temp, temp);\n\t\tif (dist < bestdist)\n\t\t{\n\t\t\tbestdist = dist;\n\t\t\tVectorCopy(cpoint, bestcpoint);\n\t\t}\n\t}\n\treturn bestdist;\n}\nstatic float getsurface_clippointtri(model_t *model, msurface_t *surf, pvec3_t point, pvec3_t bestcpoint, float bestdist)\n{\n\tint j;\n\tmesh_t *mesh = surf->mesh;\n\tvec3_t trinorm, edgedir, edgenormal, temp, cpoint;\n\tfloat dist;\n\tint e;\n\tfloat *v1, *v2;\n\tif (!mesh)\n\t{\n\t\tPF_BuildSurfaceMesh(model, surf - model->surfaces);\n\t\tmesh = surf->mesh;\n\t\tif (!mesh)\n\t\t\treturn 0;\n\t}\n\tfor (j = 0; j < mesh->numindexes; j+=3)\n\t{\n\t\t//calculate the distance from the plane\n\t\tTriangleNormal(mesh->xyz_array[mesh->indexes[j+2]], mesh->xyz_array[mesh->indexes[j+1]], mesh->xyz_array[mesh->indexes[j+0]], trinorm);\n\t\tif (!trinorm[0] && !trinorm[1] && !trinorm[2])\n\t\t\tcontinue;\n\t\tVectorNormalize(trinorm);\n\t\tdist = DotProduct(point, trinorm) - DotProduct(mesh->xyz_array[mesh->indexes[j+0]], trinorm);\n\t\tif (dist*dist < bestdist)\n\t\t{\n\t\t\t//set cpoint to be the point on the plane\n\t\t\tVectorMA(point, -dist, trinorm, cpoint);\n\n\t\t\t//clip to each edge of the triangle\n\t\t\tfor (e = 0; e < 3; e++)\n\t\t\t{\n\t\t\t\tv1 = mesh->xyz_array[mesh->indexes[j+e]];\n\t\t\t\tv2 = mesh->xyz_array[mesh->indexes[j+((e+1)%3)]];\n\n\t\t\t\tVectorSubtract(v1, v2, edgedir);\n\t\t\t\tCrossProduct(edgedir, trinorm, edgenormal);\n\t\t\t\tVectorNormalize(edgenormal);\n\n\t\t\t\tdist = DotProduct(cpoint, edgenormal) - DotProduct(v1, edgenormal);\n\t\t\t\tif (dist < 0)\n\t\t\t\t\tVectorMA(cpoint, -dist, edgenormal, cpoint);\n\t\t\t}\n\n\t\t\t//if the point is closer, we win.\n\t\t\tVectorSubtract(cpoint, point, temp);\n\t\t\tdist = DotProduct(temp, temp);\n\t\t\tif (dist < bestdist)\n\t\t\t{\n\t\t\t\tbestdist = dist;\n\t\t\t\tVectorCopy(cpoint, bestcpoint);\n\t\t\t}\n\t\t}\n\t}\n\treturn bestdist;\n}\nmsurface_t *Mod_GetSurfaceNearPoint(model_t *model, pvec3_t point)\n{\n\tmsurface_t *surf;\n\tint i;\n\n\tpvec3_t cpoint = {0,0,0};\n\tfloat bestdist = FLT_MAX, dist;\n\tmsurface_t *bestsurf = NULL;\n\n\tif (model->fromgame == fg_quake || model->fromgame == fg_quake2)\n\t{\n\t\t//all polies, we can skip parts. special case.\n\t\tsurf = model->surfaces + model->firstmodelsurface;\n\t\tfor (i = 0; i < model->nummodelsurfaces; i++, surf++)\n\t\t{\n\t\t\tdist = getsurface_clippointpoly(model, surf, point, cpoint, bestdist);\n\t\t\tif (dist < bestdist)\n\t\t\t{\n\t\t\t\tbestdist = dist;\n\t\t\t\tbestsurf = surf;\n\t\t\t}\n\t\t}\n\t}\n\telse\n\t{\n\t\t//if performance is needed, I suppose we could try walking bsp nodes a bit\n\t\tsurf = model->surfaces + model->firstmodelsurface;\n\t\tfor (i = 0; i < model->nummodelsurfaces; i++, surf++)\n\t\t{\n\t\t\tdist = getsurface_clippointtri(model, surf, point, cpoint, bestdist);\n\t\t\tif (dist < bestdist)\n\t\t\t{\n\t\t\t\tbestdist = dist;\n\t\t\t\tbestsurf = surf;\n\t\t\t}\n\t\t}\n\t}\n\treturn bestsurf;\n}\n// #438 float(entity e, vector p) getsurfacenearpoint (DP_QC_GETSURFACE)\nvoid QCBUILTIN PF_getsurfacenearpoint(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\twedict_t *ent = G_WEDICT(prinst, OFS_PARM0);\n\tpvec_t *point = G_VECTOR(OFS_PARM1);\n\n\tworld_t *w = prinst->parms->user;\n\tmodel_t *model = w->Get_CModel(w, ent->v->modelindex);\n\tmsurface_t *surf;\n\n\tG_FLOAT(OFS_RETURN) = -1;\n\tif (model && model->type == mod_brush)\n\t{\n\t\tsurf = Mod_GetSurfaceNearPoint(model, point);\n\t\tif (surf)\n\t\t\tG_FLOAT(OFS_RETURN) = surf - (model->surfaces + model->firstmodelsurface);\n\t}\n}\n\n// #439 vector(entity e, float s, vector p) getsurfaceclippedpoint (DP_QC_GETSURFACE)\nvoid QCBUILTIN PF_getsurfaceclippedpoint(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tmodel_t *model;\n\twedict_t *ent;\n\tmsurface_t *surf;\n\tfloat *point;\n\tunsigned int surfnum;\n\n\tworld_t *w = prinst->parms->user;\n\tpvec_t *result = G_VECTOR(OFS_RETURN);\n\n\tent = G_WEDICT(prinst, OFS_PARM0);\n\tsurfnum = G_FLOAT(OFS_PARM1);\n\tpoint = G_VECTOR(OFS_PARM2);\n\n\tVectorCopy(point, result);\n\n\tmodel = w->Get_CModel(w, ent->v->modelindex);\n\n\tif (!model || model->type != mod_brush)\n\t\treturn;\n\tif (surfnum >= model->nummodelsurfaces)\n\t\treturn;\n\n\tif (model->fromgame == fg_quake)\n\t{\n\t\t//all polies, we can skip parts. special case.\n\t\tsurf = model->surfaces + model->firstmodelsurface + surfnum;\n\t\tgetsurface_clippointpoly(model, surf, point, result, FLT_MAX);\n\t}\n\telse\n\t{\n\t\t//if performance is needed, I suppose we could try walking bsp nodes a bit\n\t\tsurf = model->surfaces + model->firstmodelsurface + surfnum;\n\t\tgetsurface_clippointtri(model, surf, point, result, FLT_MAX);\n\t}\n}\n\n// #628 float(entity e, float s) getsurfacenumtriangles\nvoid QCBUILTIN PF_getsurfacenumtriangles(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tunsigned int surfnum;\n\tmodel_t *model;\n\twedict_t *ent;\n\tworld_t *w = prinst->parms->user;\n\n\tent = G_WEDICT(prinst, OFS_PARM0);\n\tsurfnum = G_FLOAT(OFS_PARM1);\n\n\tmodel = w->Get_CModel(w, ent->v->modelindex);\n\n\tif (!model || model->type != mod_brush || surfnum >= model->nummodelsurfaces)\n\t{\n\t\tG_FLOAT(OFS_RETURN) = 0;\n\t}\n\telse\n\t{\n\t\tsurfnum += model->firstmodelsurface;\n\t\tif (!model->surfaces[surfnum].mesh)\n\t\t\tPF_BuildSurfaceMesh(model, surfnum);\n\t\tif (model->surfaces[surfnum].mesh)\n\t\t\tG_FLOAT(OFS_RETURN) = model->surfaces[surfnum].mesh->numindexes/3;\n\t\telse\n\t\t\tG_FLOAT(OFS_RETURN) = 0;\n\t}\n}\n// #629 float(entity e, float s) getsurfacetriangle\nvoid QCBUILTIN PF_getsurfacetriangle(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tunsigned int surfnum, firstidx;\n\tmodel_t *model;\n\twedict_t *ent;\n\tworld_t *w = prinst->parms->user;\n\n\tent = G_WEDICT(prinst, OFS_PARM0);\n\tsurfnum = G_FLOAT(OFS_PARM1);\n\tfirstidx = G_FLOAT(OFS_PARM2)*3;\n\n\tmodel = w->Get_CModel(w, ent->v->modelindex);\n\n\tif (model && model->type == mod_brush && surfnum < model->nummodelsurfaces)\n\t{\n\t\tsurfnum += model->firstmodelsurface;\n\n\t\tif (!model->surfaces[surfnum].mesh)\n\t\t\tPF_BuildSurfaceMesh(model, surfnum);\n\t\tif (model->surfaces[surfnum].mesh)\n\t\tif (firstidx+2 < model->surfaces[surfnum].mesh->numindexes)\n\t\t{\n\t\t\tG_FLOAT(OFS_RETURN+0) = model->surfaces[surfnum].mesh->indexes[firstidx+0];\n\t\t\tG_FLOAT(OFS_RETURN+1) = model->surfaces[surfnum].mesh->indexes[firstidx+1];\n\t\t\tG_FLOAT(OFS_RETURN+2) = model->surfaces[surfnum].mesh->indexes[firstidx+2];\n\t\t\treturn;\n\t\t}\n\t}\n\n\tG_FLOAT(OFS_RETURN+0) = 0;\n\tG_FLOAT(OFS_RETURN+1) = 0;\n\tG_FLOAT(OFS_RETURN+2) = 0;\n}\n\n//vector(entity e, float s, float n, float a) getsurfacepointattribute\nvoid QCBUILTIN PF_getsurfacepointattribute(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\twedict_t *ent = G_WEDICT(prinst, OFS_PARM0);\n\tunsigned int surfnum = G_FLOAT(OFS_PARM1);\n\tunsigned int pointnum = G_FLOAT(OFS_PARM2);\n\tunsigned int attribute = G_FLOAT(OFS_PARM3);\n\tworld_t *w = prinst->parms->user;\n\tmodel_t *model = w->Get_CModel(w, ent->v->modelindex);\n\tG_FLOAT(OFS_RETURN+0) = 0;\n\tG_FLOAT(OFS_RETURN+1) = 0;\n\tG_FLOAT(OFS_RETURN+2) = 0;\n\tif (model && model->type == mod_brush && surfnum < model->nummodelsurfaces)\n\t{\n\t\tsurfnum += model->firstmodelsurface;\n\t\tif (!model->surfaces[surfnum].mesh)\n\t\t\tPF_BuildSurfaceMesh(model, surfnum);\n\t\tif (model->surfaces[surfnum].mesh)\n\t\tif (pointnum < model->surfaces[surfnum].mesh->numvertexes)\n\t\t{\n\t\t\tswitch(attribute)\n\t\t\t{\n\t\t\tcase 0:\n\t\t\t\tG_FLOAT(OFS_RETURN+0) = model->surfaces[surfnum].mesh->xyz_array[pointnum][0];\n\t\t\t\tG_FLOAT(OFS_RETURN+1) = model->surfaces[surfnum].mesh->xyz_array[pointnum][1];\n\t\t\t\tG_FLOAT(OFS_RETURN+2) = model->surfaces[surfnum].mesh->xyz_array[pointnum][2];\n\t\t\t\tbreak;\n\t\t\tcase 1:\n\t\t\t\tG_FLOAT(OFS_RETURN+0) = model->surfaces[surfnum].mesh->snormals_array[pointnum][0];\n\t\t\t\tG_FLOAT(OFS_RETURN+1) = model->surfaces[surfnum].mesh->snormals_array[pointnum][1];\n\t\t\t\tG_FLOAT(OFS_RETURN+2) = model->surfaces[surfnum].mesh->snormals_array[pointnum][2];\n\t\t\t\tbreak;\n\t\t\tcase 2:\n\t\t\t\tG_FLOAT(OFS_RETURN+0) = model->surfaces[surfnum].mesh->tnormals_array[pointnum][0];\n\t\t\t\tG_FLOAT(OFS_RETURN+1) = model->surfaces[surfnum].mesh->tnormals_array[pointnum][1];\n\t\t\t\tG_FLOAT(OFS_RETURN+2) = model->surfaces[surfnum].mesh->tnormals_array[pointnum][2];\n\t\t\t\tbreak;\n\t\t\tcase 3:\n\t\t\t\tG_FLOAT(OFS_RETURN+0) = model->surfaces[surfnum].mesh->normals_array[pointnum][0];\n\t\t\t\tG_FLOAT(OFS_RETURN+1) = model->surfaces[surfnum].mesh->normals_array[pointnum][1];\n\t\t\t\tG_FLOAT(OFS_RETURN+2) = model->surfaces[surfnum].mesh->normals_array[pointnum][2];\n\t\t\t\tbreak;\n\t\t\tcase 4:\n\t\t\t\tG_FLOAT(OFS_RETURN+0) = model->surfaces[surfnum].mesh->st_array[pointnum][0];\n\t\t\t\tG_FLOAT(OFS_RETURN+1) = model->surfaces[surfnum].mesh->st_array[pointnum][1];\n\t\t\t\tG_FLOAT(OFS_RETURN+2) = 0;\n\t\t\t\tbreak;\n\t\t\tcase 5:\n\t\t\t\tG_FLOAT(OFS_RETURN+0) = model->surfaces[surfnum].mesh->lmst_array[0][pointnum][0];\n\t\t\t\tG_FLOAT(OFS_RETURN+1) = model->surfaces[surfnum].mesh->lmst_array[0][pointnum][1];\n\t\t\t\tG_FLOAT(OFS_RETURN+2) = 0;\n\t\t\t\tbreak;\n\t\t\tcase 6:\n\t\t\t\tG_FLOAT(OFS_RETURN+0) = model->surfaces[surfnum].mesh->colors4f_array[0][pointnum][0];\n\t\t\t\tG_FLOAT(OFS_RETURN+1) = model->surfaces[surfnum].mesh->colors4f_array[0][pointnum][1];\n\t\t\t\tG_FLOAT(OFS_RETURN+2) = model->surfaces[surfnum].mesh->colors4f_array[0][pointnum][2];\n\t\t\t\t//no way to return alpha here.\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n}\n\n//#240 float(vector viewpos, entity viewee) checkpvs (FTE_QC_CHECKPVS)\n//note: this requires a correctly setorigined entity.\nvoid QCBUILTIN PF_checkpvs(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tworld_t *world = prinst->parms->user;\n\tmodel_t *worldmodel = world->worldmodel;\n\tVM_VECTORARG(viewpos, OFS_PARM0);\n\twedict_t *ent = G_WEDICT(prinst, OFS_PARM1);\n\tint cluster;\n\tint qcpvsarea[2];\n\tqbyte *pvs;\n\n\tif (!worldmodel || worldmodel->loadstate != MLS_LOADED)\n\t\tG_FLOAT(OFS_RETURN) = false;\n\telse if (!worldmodel->funcs.FatPVS)\n\t\tG_FLOAT(OFS_RETURN) = true;\n\telse\n\t{\n\t\tqcpvsarea[0] = 1;\n\t\tcluster = worldmodel->funcs.ClusterForPoint(worldmodel, viewpos, &qcpvsarea[1]);\n\t\tpvs = worldmodel->funcs.ClusterPVS(worldmodel, cluster, NULL, PVM_FAST);\n\t\tG_FLOAT(OFS_RETURN) = worldmodel->funcs.EdictInFatPVS(worldmodel, &ent->pvsinfo, pvs, qcpvsarea);\n\t}\n}\n\nvoid QCBUILTIN PF_setattachment(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\twedict_t *e = G_WEDICT(prinst, OFS_PARM0);\n\twedict_t *tagentity = G_WEDICT(prinst, OFS_PARM1);\n\tconst char *tagname = PR_GetStringOfs(prinst, OFS_PARM2);\n\tworld_t *world = prinst->parms->user;\n\n\tmodel_t *model;\n\n\tint tagidx;\n\n\ttagidx = 0;\n\n\tif (tagentity != world->edicts && tagname && tagname[0])\n\t{\n\t\tmodel = world->Get_CModel(world, tagentity->v->modelindex);\n\t\tif (model)\n\t\t{\n\t\t\tif (model && model->loadstate == MLS_LOADING)\n\t\t\t\tCOM_WorkerPartialSync(model, &model->loadstate, MLS_LOADING);\n\t\t\ttagidx = Mod_TagNumForName(model, tagname, 0);\n\t\t\tif (tagidx == 0)\n\t\t\t\tCon_DPrintf(\"setattachment(edict %i, edict %i, string \\\"%s\\\"): tried to find tag named \\\"%s\\\" on entity %i (model \\\"%s\\\") but could not find it\\n\", NUM_FOR_EDICT(prinst, e), NUM_FOR_EDICT(prinst, tagentity), tagname, tagname, NUM_FOR_EDICT(prinst, tagentity), model->name);\n\t\t}\n\t\telse\n\t\t\tCon_DPrintf(\"setattachment(edict %i, edict %i, string \\\"%s\\\"): Couldn't load model\\n\", NUM_FOR_EDICT(prinst, e), NUM_FOR_EDICT(prinst, tagentity), tagname);\n\t}\n\n\te->xv->tag_entity = EDICT_TO_PROG(prinst, tagentity);\n\te->xv->tag_index = tagidx;\n}\n\n#ifndef TERRAIN\nvoid QCBUILTIN PF_terrain_edit(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tG_FLOAT(OFS_RETURN) = false;\n}\n#endif\n\n//end model functions\n////////////////////////////////////////////////////\n\nvoid QCBUILTIN PF_touchtriggers(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tworld_t *w = prinst->parms->user;\n\twedict_t *ent = ((prinst->callargc>0)?G_WEDICT(prinst, OFS_PARM0):PROG_TO_WEDICT(prinst, *w->g.self));\n\tif (prinst->callargc > 1)\n\t{\n\t\tif (ent->readonly)\n\t\t{\n\t\t\tCon_Printf(\"setorigin on readonly entity %i\\n\", ent->entnum);\n\t\t\treturn;\n\t\t}\n\t\tVectorCopy(G_VECTOR(OFS_PARM1), ent->v->origin);\n\t}\n\tWorld_LinkEdict (w, ent, true);\n}\n\n\n////////////////////////////////////////////////////\n//Finding\n\n//entity(string field, float match) findchainflags = #450\n//chained search for float reference fields\nvoid QCBUILTIN PF_findchainflags (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint i;\n\tunsigned int ff, cf;\n\tint s;\n\twedict_t\t*ent, *chain;\n\n\tchain = (wedict_t *) *prinst->parms->edicts;\n\n\tff = G_INT(OFS_PARM0)+prinst->fieldadjust;\n\ts = G_FLOAT(OFS_PARM1);\n\tif (prinst->callargc > 2)\n\t\tcf = G_INT(OFS_PARM2)+prinst->fieldadjust;\n\telse\n\t\tcf = &((comentvars_t*)NULL)->chain - (pint_t*)NULL;\n\tif (ff >= prinst->activefieldslots || cf >= prinst->activefieldslots)\n\t{\n\t\tPR_BIError (prinst, \"PF_FindChain: bad field reference\");\n\t\treturn;\n\t}\n\n\tfor (i = 1; i < *prinst->parms->num_edicts; i++)\n\t{\n\t\tent = WEDICT_NUM_PB(prinst, i);\n\t\tif (ED_ISFREE(ent))\n\t\t\tcontinue;\n\t\tif (!((int)((pvec_t *)ent->v)[ff] & s))\n\t\t\tcontinue;\n\n\t\t((pint_t*)ent->v)[cf] = EDICT_TO_PROG(prinst, chain);\n\t\tchain = ent;\n\t}\n\n\tRETURN_EDICT(prinst, chain);\n}\n\n//entity(string field, float match) findchainfloat = #403\n//chained search for float, int, and entity reference fields\nvoid QCBUILTIN PF_findchainfloat (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint i;\n\tunsigned int ff, cf;\n\tfloat s;\n\twedict_t\t*ent, *chain;\n\n\tchain = (wedict_t *) *prinst->parms->edicts;\n\n\tff = G_INT(OFS_PARM0)+prinst->fieldadjust;\n\ts = G_FLOAT(OFS_PARM1);\n\tif (prinst->callargc > 2)\n\t\tcf = G_INT(OFS_PARM2)+prinst->fieldadjust;\n\telse\n\t\tcf = &((comentvars_t*)NULL)->chain - (pint_t*)NULL;\n\tif (ff >= prinst->activefieldslots || cf >= prinst->activefieldslots)\n\t{\n\t\tPR_BIError (prinst, \"PF_FindChain: bad field reference\");\n\t\treturn;\n\t}\n\n\tfor (i = 1; i < *prinst->parms->num_edicts; i++)\n\t{\n\t\tent = WEDICT_NUM_PB(prinst, i);\n\t\tif (ED_ISFREE(ent))\n\t\t\tcontinue;\n\t\tif (((pvec_t *)ent->v)[ff] != s)\n\t\t\tcontinue;\n\n\t\t((pint_t*)ent->v)[cf] = EDICT_TO_PROG(prinst, chain);\n\t\tchain = ent;\n\t}\n\n\tRETURN_EDICT(prinst, chain);\n}\n\n//entity(string field, string match) findchain = #402\n//chained search for strings in entity fields\nvoid QCBUILTIN PF_findchain (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint i;\n\tunsigned int ff, cf;\n\tconst char *s;\n\tstring_t t;\n\twedict_t *ent, *chain;\n\t\n\tchain = (wedict_t *) *prinst->parms->edicts;\n\n\tff = G_INT(OFS_PARM0)+prinst->fieldadjust;\n\ts = PR_GetStringOfs(prinst, OFS_PARM1);\n\tif (prinst->callargc > 2)\n\t\tcf = G_INT(OFS_PARM2)+prinst->fieldadjust;\n\telse\n\t\tcf = &((comentvars_t*)NULL)->chain - (int*)NULL;\n\tif (ff >= prinst->activefieldslots || cf >= prinst->activefieldslots)\n\t{\n\t\tPR_BIError (prinst, \"PF_FindChain: bad field reference\");\n\t\treturn;\n\t}\n\n\tfor (i = 1; i < *prinst->parms->num_edicts; i++)\n\t{\n\t\tent = WEDICT_NUM_PB(prinst, i);\n\t\tif (ED_ISFREE(ent))\n\t\t\tcontinue;\n\t\tt = *(string_t *)&((float*)ent->v)[ff];\n\t\tif (!t)\n\t\t\tcontinue;\n\t\tif (strcmp(PR_GetString(prinst, t), s))\n\t\t\tcontinue;\n\n\t\t((int*)ent->v)[cf] = EDICT_TO_PROG(prinst, chain);\n\t\tchain = ent;\n\t}\n\n\tRETURN_EDICT(prinst, chain);\n}\n\n//EXTENSION: DP_QC_FINDFLAGS\n//entity(entity start, float fld, float match) findflags = #449\nvoid QCBUILTIN PF_FindFlags (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint e;\n\tunsigned int f;\n\tint s;\n\twedict_t *ed;\n\n\te = G_EDICTNUM(prinst, OFS_PARM0);\n\tf = G_INT(OFS_PARM1)+prinst->fieldadjust;\n\tif (f >= prinst->activefieldslots)\n\t{\n\t\tPR_BIError (prinst, \"PF_FindFlags: bad field reference\");\n\t\treturn;\n\t}\n\ts = G_FLOAT(OFS_PARM2);\n\n\tfor (e++; e < *prinst->parms->num_edicts; e++)\n\t{\n\t\ted = WEDICT_NUM_PB(prinst, e);\n\t\tif (ED_ISFREE(ed))\n\t\t\tcontinue;\n\t\tif ((int)((float *)ed->v)[f] & s)\n\t\t{\n\t\t\tRETURN_EDICT(prinst, ed);\n\t\t\treturn;\n\t\t}\n\t}\n\n\tRETURN_EDICT(prinst, *prinst->parms->edicts);\n}\n\n//entity(entity start, float fld, float match) findfloat = #98\nvoid QCBUILTIN PF_FindFloat (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint e;\n\tunsigned int f;\n\tint s;\n\twedict_t *ed;\n\n#ifdef HAVE_LEGACY\n\tif (prinst->callargc != 3)\t//I can hate mvdsv if I want to.\n\t{\n\t\tPR_BIError(prinst, \"PF_FindFloat (#98): callargc != 3\\nDid you mean to set pr_imitatemvdsv to 1?\");\n\t\treturn;\n\t}\n#endif\n\n\te = G_EDICTNUM(prinst, OFS_PARM0);\n\tf = G_INT(OFS_PARM1)+prinst->fieldadjust;\n\tif (f >= prinst->activefieldslots)\n\t{\n\t\tPR_BIError (prinst, \"PF_FindFloat: bad field reference\");\n\t\treturn;\n\t}\n\ts = G_INT(OFS_PARM2);\n\n\tfor (e++; e < *prinst->parms->num_edicts; e++)\n\t{\n\t\ted = WEDICT_NUM_PB(prinst, e);\n\t\tif (ED_ISFREE(ed))\n\t\t\tcontinue;\n\t\tif (((int *)ed->v)[f] == s)\n\t\t{\n\t\t\tRETURN_EDICT(prinst, ed);\n\t\t\treturn;\n\t\t}\n\t}\n\n\tRETURN_EDICT(prinst, *prinst->parms->edicts);\n}\n\n// entity (entity start, .string field, string match) find = #5;\nvoid QCBUILTIN PF_FindString (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint\t\te;\n\tunsigned int\t\tf;\n\tconst char\t*s;\n\tstring_t t;\n\twedict_t\t*ed;\n\n\te = G_EDICTNUM(prinst, OFS_PARM0);\n\tf = G_INT(OFS_PARM1)+prinst->fieldadjust;\n\tif (f >= prinst->activefieldslots)\n\t{\n\t\tPR_BIError (prinst, \"PF_FindString: bad field reference\");\n\t\treturn;\n\t}\n\ts = PR_GetStringOfs(prinst, OFS_PARM2);\n\n\tif (!s || !*s)\n\t{\t//checking for empty (and by extension null)\n\t\t//looking for an empty string is a bloody stupid thing to do, and basically always a bug, but existing code exists. either way it warrents a warning.\n\t\tif (developer.ival)\n\t\t\tPR_RunWarning(prinst, \"PF_FindString: empty string\\n\");\n\t\tfor (e++ ; e < *prinst->parms->num_edicts ; e++)\n\t\t{\n\t\t\ted = WEDICT_NUM_PB(prinst, e);\n\t\t\tif (ED_ISFREE(ed))\n\t\t\t\tcontinue;\n\t\t\tt = ((string_t *)ed->v)[f];\n\t\t\tif (!t || !*PR_GetString(prinst, t))\n\t\t\t{\n\t\t\t\tRETURN_EDICT(prinst, ed);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n\telse\n\t{\t//should be safe to assume that null is empty and thus never a match. speed\n\t\tfor (e++ ; e < *prinst->parms->num_edicts ; e++)\n\t\t{\n\t\t\ted = WEDICT_NUM_PB(prinst, e);\n\t\t\tif (ED_ISFREE(ed))\n\t\t\t\tcontinue;\n\t\t\tt = ((string_t *)ed->v)[f];\n\t\t\tif (!t)\n\t\t\t\tcontinue;\n\t\t\tif (!strcmp(PR_GetString(prinst, t),s))\n\t\t\t{\n\t\t\t\tRETURN_EDICT(prinst, ed);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n\n\tRETURN_EDICT(prinst, *prinst->parms->edicts);\n}\n\n#ifdef QCGC\nvoid QCBUILTIN PF_FindList (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tworld_t *w = prinst->parms->user;\n\tint\t\te;\n\tunsigned int f = G_INT(OFS_PARM0)+prinst->fieldadjust;\n\twedict_t\t*ed;\n\tetype_t type = G_INT(OFS_PARM2);\n\n\tint *list = alloca(sizeof(*list)*w->num_edicts);\t//guess at a max\n\tint *retlist;\n\tunsigned found = 0;\n\n\tif (type <= ev_double)\n\t{\n\t\textern const unsigned int type_size[];\n\t\tif (f < 0 || f+type_size[type] > prinst->activefieldslots)\n\t\t{\t//invalid field.\n\t\t\tG_INT(OFS_PARM3) = G_INT(OFS_RETURN) = 0;\n\t\t\treturn;\n\t\t}\n\t}\n\telse\n\t{\t//unsupported field type.\n\t\tG_INT(OFS_PARM3) = G_INT(OFS_RETURN) = 0;\n\t\treturn;\n\t}\n\n\tif (type == ev_string)\n\t{\n\t\tconst char\t*s = PR_GetStringOfs(prinst, OFS_PARM1);\n\t\tstring_t t;\n\t\tif (!s)\n\t\t\ts = \"\"; /* o.O */\n\t\tfor (e=1 ; e < *prinst->parms->num_edicts ; e++)\n\t\t{\n\t\t\ted = WEDICT_NUM_PB(prinst, e);\n\t\t\tif (ED_ISFREE(ed) || ed->readonly)\n\t\t\t\tcontinue;\n\t\t\tt = ((string_t *)ed->v)[f];\n\t\t\tif (!t)\n\t\t\t\tcontinue;\n\t\t\tif (!strcmp(PR_GetString(prinst, t),s))\n\t\t\t\tlist[found++] = EDICT_TO_PROG(prinst, ed);\n\t\t}\n\t}\n\telse if (type == ev_float)\n\t{\t//handling -0 properly requires care\n\t\tpvec_t s = G_FLOAT(OFS_PARM1);\n\t\tfor (e=1 ; e < *prinst->parms->num_edicts ; e++)\n\t\t{\n\t\t\ted = WEDICT_NUM_PB(prinst, e);\n\t\t\tif (ED_ISFREE(ed))\n\t\t\t\tcontinue;\n\t\t\tif (((pvec_t*)ed->v)[f] == s)\n\t\t\t\tlist[found++] = EDICT_TO_PROG(prinst, ed);\n\t\t}\n\t}\n\telse if (type == ev_double)\n\t{\t//handling 64bit -0 properly requires care\n\t\tdouble s = G_DOUBLE(OFS_PARM1);\n\t\tfor (e=1 ; e < *prinst->parms->num_edicts ; e++)\n\t\t{\n\t\t\ted = WEDICT_NUM_PB(prinst, e);\n\t\t\tif (ED_ISFREE(ed))\n\t\t\t\tcontinue;\n\t\t\tif (*(pdouble_t*)((pvec_t*)ed->v+f) == s)\n\t\t\t\tlist[found++] = EDICT_TO_PROG(prinst, ed);\n\t\t}\n\t}\n\telse if (type == ev_int64 || type == ev_uint64)\n\t{\t//handling -0 properly requires care\n\t\tpint64_t s = G_INT64(OFS_PARM1);\n\t\tfor (e=1 ; e < *prinst->parms->num_edicts ; e++)\n\t\t{\n\t\t\ted = WEDICT_NUM_PB(prinst, e);\n\t\t\tif (ED_ISFREE(ed))\n\t\t\t\tcontinue;\n\t\t\tif (*(pint64_t*)((pint_t*)ed->v+f) == s)\n\t\t\t\tlist[found++] = EDICT_TO_PROG(prinst, ed);\n\t\t}\n\t}\n\telse if (type == ev_vector)\n\t{\t//big types...\n\t\tpvec_t *s = G_VECTOR(OFS_PARM1);\n\t\tfor (e=1 ; e < *prinst->parms->num_edicts ; e++)\n\t\t{\n\t\t\ted = WEDICT_NUM_PB(prinst, e);\n\t\t\tif (ED_ISFREE(ed))\n\t\t\t\tcontinue;\n\t\t\tif (((pvec_t*)ed->v)[f+0] == s[0]&&\n\t\t\t\t((pvec_t*)ed->v)[f+1] == s[1]&&\n\t\t\t\t((pvec_t*)ed->v)[f+2] == s[2])\n\t\t\t\tlist[found++] = EDICT_TO_PROG(prinst, ed);\n\t\t}\n\t}\n\telse\n\t{\t//generic references and other stuff that can just be treated as ints\n\t\tpint_t s = G_INT(OFS_PARM1);\n\t\tfor (e=1 ; e < *prinst->parms->num_edicts ; e++)\n\t\t{\n\t\t\ted = WEDICT_NUM_PB(prinst, e);\n\t\t\tif (ED_ISFREE(ed))\n\t\t\t\tcontinue;\n\t\t\tif (((pint_t*)ed->v)[f] == s)\n\t\t\t\tlist[found++] = EDICT_TO_PROG(prinst, ed);\n\t\t}\n\t}\n\n\tG_INT(OFS_PARM3) = found;\n\tG_INT(OFS_RETURN) = prinst->AllocTempString(prinst, (char**)&retlist, (found+1)*sizeof(*retlist));\n\tmemcpy(retlist, list, found*sizeof(*retlist));\n\tretlist[found] = 0;\n}\n#endif\n\n//Finding\n////////////////////////////////////////////////////\n//Cvars\n\ncvar_t *PF_Cvar_FindOrGet(const char *var_name)\n{\n\tconst char *def;\n\tcvar_t *var;\n\n\tvar = Cvar_FindVar(var_name);\n\tif (!var && pr_autocreatecvars.ival)\n\t{\n\t\t//this little chunk is so cvars dp creates are created with meaningful values\n\t\tdef = \"\";\n\t\tif (!strcmp(var_name, \"sv_maxairspeed\"))\n\t\t\tdef = \"30\";\n\t\telse if (!strcmp(var_name, \"sv_jumpvelocity\"))\n\t\t\tdef = \"270\";\n\t\telse\n\t\t\tdef = \"\";\n\n\t\tvar = Cvar_Get(var_name, def, 0, \"Implicit QC variables\");\n\t\tif (var)\n\t\t\tCon_DPrintf(\"^3Created QC Cvar %s\\n\", var_name);\n\t\telse\n\t\t\tCon_Printf(CON_ERROR\"Unable to create QC Cvar %s\\n\", var_name);\n\t}\n\treturn var;\n}\n\n//string(string cvarname) cvar_string\nvoid QCBUILTIN PF_cvar_string (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char\t*str = PR_GetStringOfs(prinst, OFS_PARM0);\n\tcvar_t *cv = PF_Cvar_FindOrGet(str);\n\tif (cv && !(cv->flags & CVAR_NOUNSAFEEXPAND))\n\t{\n\t\tif(cv->latched_string)\n\t\t\tRETURN_TSTRING(cv->latched_string);\n\t\telse\n\t\t\tRETURN_TSTRING(cv->string);\n\t}\n\telse\n\t\tG_INT(OFS_RETURN) = 0;\n}\n\nvoid QCBUILTIN PF_cvars_haveunsaved (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tG_FLOAT(OFS_RETURN) = Cvar_UnsavedArchive();\n}\n\n//string(string cvarname) cvar_defstring\nvoid QCBUILTIN PF_cvar_defstring (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char\t*str = PR_GetStringOfs(prinst, OFS_PARM0);\n\tcvar_t *cv = PF_Cvar_FindOrGet(str);\n\tif (cv && !(cv->flags & CVAR_NOUNSAFEEXPAND))\n\t\tRETURN_TSTRING(cv->defaultstr);\n\telse\n\t\tG_INT(OFS_RETURN) = 0;\n}\n\n//string(string cvarname) cvar_description\nvoid QCBUILTIN PF_cvar_description (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char\t*str = PR_GetStringOfs(prinst, OFS_PARM0);\n\tcvar_t *cv = PF_Cvar_FindOrGet(str);\n\tif (cv && !(cv->flags & CVAR_NOUNSAFEEXPAND))\n\t{\n\t\tif (cv->description)\n\t\t\tRETURN_TSTRING(localtext(cv->description));\n\t\telse\n\t\t\tG_INT(OFS_RETURN) = 0;\n\t}\n\telse\n\t\tG_INT(OFS_RETURN) = 0;\n}\n\n//float(string name) cvar_type\nvoid QCBUILTIN PF_cvar_type (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char\t*str = PR_GetStringOfs(prinst, OFS_PARM0);\n\tint ret = 0;\n\tcvar_t *v = Cvar_FindVar(str);\t//this builtin MUST NOT create cvars implicitly, otherwise there would be no way to test if it exists.\n\tif (v)\n\t{\n\t\tret |= CVAR_TYPEFLAG_EXISTS;\n\t\tif(v->flags & CVAR_ARCHIVE)\n\t\t\tret |= CVAR_TYPEFLAG_SAVED;\n\t\tif(v->flags & (CVAR_NOTFROMSERVER|CVAR_NOUNSAFEEXPAND))\n\t\t\tret |= CVAR_TYPEFLAG_PRIVATE;\n\t\tif(!(v->flags & CVAR_USERCREATED))\n\t\t\tret |= CVAR_TYPEFLAG_ENGINE;\n\t\tif (v->description)\n\t\t\tret |= CVAR_TYPEFLAG_HASDESCRIPTION;\n\t\tif (v->flags & (CVAR_NOSET|CVAR_RENDEREROVERRIDE))\n\t\t\tret |= CVAR_TYPEFLAG_READONLY;\n\t}\n\tG_FLOAT(OFS_RETURN) = ret;\n}\n\n//void(string cvarname, string newvalue) cvar\nvoid QCBUILTIN PF_cvar_set (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char\t*var_name, *val;\n\tcvar_t *var;\n\n\tvar_name = PR_GetStringOfs(prinst, OFS_PARM0);\n\tval = PR_GetStringOfs(prinst, OFS_PARM1);\n\n\tvar = PF_Cvar_FindOrGet(var_name);\n\tif (!var || (var->flags & CVAR_NOTFROMSERVER))\n\t\treturn;\n\tCvar_Set (var, val);\n}\n\nvoid QCBUILTIN PF_cvar_setlatch (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char\t*var_name, *val;\n\tcvar_t *var;\n\n\tvar_name = PR_GetStringOfs(prinst, OFS_PARM0);\n\tval = PR_GetStringOfs(prinst, OFS_PARM1);\n\n\tvar = PF_Cvar_FindOrGet(var_name);\n\tif (!var || (var->flags & CVAR_NOTFROMSERVER))\n\t\treturn;\n\tCvar_LockFromServer(var, val);\n}\n\nvoid QCBUILTIN PF_cvar_setf (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char\t*var_name;\n\tfloat\tval;\n\tcvar_t *var;\n\n\tvar_name = PR_GetStringOfs(prinst, OFS_PARM0);\n\tval = G_FLOAT(OFS_PARM1);\n\n\tvar = PF_Cvar_FindOrGet(var_name);\n\tif (!var || (var->flags & CVAR_NOTFROMSERVER))\n\t\treturn;\n\tCvar_SetValue (var, val);\n}\n\n//float(string name, string value) registercvar\nvoid QCBUILTIN PF_registercvar (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char *name = PR_GetStringOfs(prinst, OFS_PARM0);\n\tconst char *value = (prinst->callargc>2)?PR_GetStringOfs(prinst, OFS_PARM1):\"\";\n\tint dpflags = (prinst->callargc>2)?G_FLOAT(OFS_PARM2):0;\n\tint realflags = 0;\n\tname = PR_GetStringOfs(prinst, OFS_PARM0);\n\n\tif (dpflags)\n\t{\t//this is a DP extension, so uses DP's internal cvar flags.\n\t\t//which is stupid when cvar_type reports a different set of flags.\n\t\t//if (dpflags & (1<<0)) dpflags &= ~(1<<0), blocked from realflags |= avialable only to ;\n\t\tif (dpflags & (1<<4)) dpflags &= ~(1<<4), realflags |= CVAR_CHEAT;\n\t\tif (dpflags & (1<<5)) dpflags &= ~(1<<5), realflags |= CVAR_ARCHIVE;\n\t\tif (dpflags & (1<<8)) dpflags &= ~(1<<8), realflags |= CVAR_SERVERINFO;\n\t\tif (dpflags & (1<<9)) dpflags &= ~(1<<9), realflags |= CVAR_USERINFO;\n\t\tif (dpflags & (1<<11)) dpflags &= ~(1<<11), realflags |= CVAR_NOUNSAFEEXPAND;\n\t\tif (dpflags)\n\t\t\tCon_Printf(CON_WARNING\"WARNING: Unknown flags passed to registercvar(\\\"%s\\\", \\\"%s\\\", %x)\\n\", name, value, dpflags);\n\t}\n\n\tif (Cvar_FindVar(name))\n\t\tG_FLOAT(OFS_RETURN) = 0;\n\telse\n\t{\n\t// archive?\n\t\tif (Cvar_Get(name, value, CVAR_USERCREATED|realflags, \"QC created vars\"))\n\t\t\tG_FLOAT(OFS_RETURN) = 1;\n\t\telse\n\t\t\tG_FLOAT(OFS_RETURN) = 0;\n\t}\n}\n\n//Cvars\n////////////////////////////////////////////////////\n//memory stuff\nvoid QCBUILTIN PF_memalloc (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint size = G_INT(OFS_PARM0);\n\tvoid *ptr;\n\tif (!size)\n\t\tsize = 1; //return something free can free.\n\tif (size <= 0 || size > 0x01000000)\n\t\tptr = NULL;\t//don't let them abuse things too much. values that are too large might overflow.\n\telse\n\t\tptr = prinst->AddressableAlloc(prinst, size);\n\tif (ptr)\n\t{\n\t\tmemset(ptr, 0, size);\n\t\tG_INT(OFS_RETURN) = (char*)ptr - prinst->stringtable;\n\t}\n\telse\n\t{\n\t\tG_INT(OFS_RETURN) = 0;\n\t\tPR_BIError(prinst, \"PF_memalloc: failure (size %i)\\n\", size);\n\t}\n}\nvoid QCBUILTIN PF_memrealloc (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tvoid *oldcptr = prinst->stringtable + G_INT(OFS_PARM0);\n\tint size = G_INT(OFS_PARM1);\n\tvoid *ptr;\n\tif (!size)\n\t\tsize = 1; //return something free can free.\n\tif (size <= 0 || size > 0x01000000)\n\t\tptr = NULL;\t//don't let them abuse things too much. values that are too large might overflow.\n\telse\n\t\tptr = prinst->AddressableRealloc(prinst, oldcptr, size);\n\tif (ptr)\n\t\tG_INT(OFS_RETURN) = (char*)ptr - prinst->stringtable;\n\telse\n\t{\n\t\tG_INT(OFS_RETURN) = 0;\n\t\tPR_BIError(prinst, \"PF_memrealloc: failure (size %i)\\n\", size);\n\t}\n}\nvoid QCBUILTIN PF_memfree (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint qcptr = G_INT(OFS_PARM0);\n\tvoid *cptr = prinst->stringtable + G_INT(OFS_PARM0);\n\tif (qcptr)\n\t\tprinst->AddressableFree(prinst, cptr);\n}\nvoid QCBUILTIN PF_memcmp (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint qcdst = G_INT(OFS_PARM0);\n\tint qcsrc = G_INT(OFS_PARM1);\n\tint size = G_INT(OFS_PARM2);\n\tint srcoffset = (prinst->callargc>3)?G_INT(OFS_PARM3):0;\n\tint dstoffset = (prinst->callargc>4)?G_INT(OFS_PARM4):0;\n\tG_INT(OFS_RETURN) = 0;\n\tif (size < 0)\n\t\tPR_BIError(prinst, \"PF_memcmp: invalid size\\n\");\n\telse if (size)\n\t{\n\t\tvoid *dst = PR_PointerToNative_MoInvalidate(prinst, qcdst, dstoffset, size);\n\t\tvoid *src = PR_PointerToNative_MoInvalidate(prinst, qcsrc, srcoffset, size);\n\t\tif (!dst)\n\t\t\tPR_BIError(prinst, \"PF_memcmp: invalid dest\\n\");\n\t\telse if (!src)\n\t\t\tPR_BIError(prinst, \"PF_memcmp: invalid source\\n\");\n\t\telse\n\t\t\tG_INT(OFS_RETURN) = memcmp(dst, src, size);\n\t}\n}\nvoid QCBUILTIN PF_memcpy (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint qcdst = G_INT(OFS_PARM0);\n\tint qcsrc = G_INT(OFS_PARM1);\n\tint size = G_INT(OFS_PARM2);\n\tint srcoffset = (prinst->callargc>3)?G_INT(OFS_PARM3):0;\n\tint dstoffset = (prinst->callargc>4)?G_INT(OFS_PARM4):0;\n\tif (size < 0)\n\t\tPR_BIError(prinst, \"PF_memcpy: invalid size %#x\\n\", size);\n\telse if (size)\n\t{\n\t\tvoid *dst = PR_PointerToNative_Resize(prinst, qcdst, dstoffset, size);\n\t\tvoid *src = PR_PointerToNative_MoInvalidate(prinst, qcsrc, srcoffset, size);\n\t\tif (!dst)\n\t\t\tPR_BIError(prinst, \"PF_memcpy: invalid dest (%#x[%#x...%#x]\\n\", qcdst, dstoffset, dstoffset+size-1);\n\t\telse if (!src)\n\t\t\tPR_BIError(prinst, \"PF_memcpy: invalid source (%#x[%#x...%#x]\\n\", qcsrc, srcoffset, srcoffset+size-1);\n\t\telse\n\t\t\tmemmove(dst, src, size);\n\t}\n}\n\nvoid QCBUILTIN PF_memfill8 (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint dst = G_INT(OFS_PARM0);\n\tint val = G_INT(OFS_PARM1);\n\tint size = G_INT(OFS_PARM2);\n\tint dstoffset = (prinst->callargc>3)?G_INT(OFS_PARM3):0;\n\n\tvoid *ptr = PR_PointerToNative_Resize(prinst, dst, dstoffset, size);\n\tif (ptr)\n\t\tmemset(ptr, val, size);\n\telse\n\t\tPR_BIError(prinst, \"PF_memfill8: invalid dest\\n\");\n}\n\nvoid QCBUILTIN PF_memptradd (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\t//convienience function. needed for: ptr = &ptr[5]; or ptr += 5;\n\tint dst = G_INT(OFS_PARM0);\n\tint ofs = G_FLOAT(OFS_PARM1);\n\tif (ofs != G_FLOAT(OFS_PARM1))\n\t\tPR_BIError(prinst, \"PF_memptradd: non-integer offset\\n\");\n\tif (ofs & 3)\n\t\tPR_BIError(prinst, \"PF_memptradd: offset is not 32-bit aligned.\\n\");\t//allows for other implementations to provide this with eg pointers expressed as 16.16 with 18-bit segments/allocations.\n\t//cannot work with tempstrings/createbuffer\n\tif (((unsigned int)ofs & 0x80000000) && ofs!=0)\n\t\tPR_BIError(prinst, \"PF_memptradd: special pointers cannot be offset.\\n\");\n\n\tG_INT(OFS_RETURN) = dst + ofs;\n}\n\nvoid QCBUILTIN PF_memstrsize(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\t//explicitly returns bytes, not chars.\n\tG_FLOAT(OFS_RETURN) = strlen(PR_GetStringOfs(prinst, OFS_PARM0));\n}\n\n//memory stuff\n////////////////////////////////////////////////////\n//hash table stuff\ntypedef struct\n{\n\tpubprogfuncs_t *prinst;\n\tetype_t defaulttype;\n\thashtable_t tab;\n\tvoid *bucketmem;\n} pf_hashtab_t;\ntypedef struct \n{\n\tbucket_t\tbuck;\n\tchar\t\t*name;\n\tetype_t\t\ttype;\n\tunion\n\t{\n\t\tvec3_t\tdata;\n\t\tchar\t*stringdata;\n\t};\n} pf_hashentry_t;\n#define HASH_REPLACE\t256\n#define HASH_ADD\t\t512\n#define FIRSTTABLE 1\nstatic pf_hashtab_t *pf_hashtab;\nstatic size_t pf_hash_maxtables;\nstatic pf_hashtab_t pf_peristanthashtab;\t//persists over map changes.\n//static pf_hashtab_t pf_reverthashtab;\t\t//pf_peristanthashtab as it was at map start, for map restarts.\nstatic pf_hashtab_t *PF_hash_findtab(pubprogfuncs_t *prinst, int idx)\n{\n\tidx -= FIRSTTABLE;\n\tif (idx >= 0 && (unsigned)idx < pf_hash_maxtables && pf_hashtab[idx].prinst)\n\t\treturn &pf_hashtab[idx];\n\telse if (idx == 0-FIRSTTABLE)\n\t{\n\t\tif (!pf_peristanthashtab.tab.numbuckets)\n\t\t{\n\t\t\tint numbuckets = 256;\n\t\t\tpf_peristanthashtab.defaulttype = ev_string;\n\t\t\tpf_peristanthashtab.prinst = NULL;\n\t\t\tpf_peristanthashtab.bucketmem = Z_Malloc(Hash_BytesForBuckets(numbuckets));\n\t\t\tHash_InitTable(&pf_peristanthashtab.tab, numbuckets, pf_peristanthashtab.bucketmem);\n\t\t}\n\t\treturn &pf_peristanthashtab;\n\t}\n\telse\n\t\tPR_BIError(prinst, \"PF_hash_findtab: invalid hash table\\n\");\n\treturn NULL;\n}\n\nvoid QCBUILTIN PF_hash_getkey (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tpf_hashtab_t *tab = PF_hash_findtab(prinst, G_FLOAT(OFS_PARM0));\n\tint idx = G_FLOAT(OFS_PARM1);\n\tpf_hashentry_t *ent = NULL;\n\tG_INT(OFS_RETURN) = 0;\n\tif (tab)\n\t{\n\t\tent = Hash_GetIdx(&tab->tab, idx);\n\t\tif (ent)\n\t\t\tRETURN_TSTRING(ent->name);\n\t}\n}\nvoid QCBUILTIN PF_hash_delete (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tpf_hashtab_t *tab = PF_hash_findtab(prinst, G_FLOAT(OFS_PARM0));\n\tconst char *name = PR_GetStringOfs(prinst, OFS_PARM1);\n\tpf_hashentry_t *ent = NULL;\n\tmemset(G_VECTOR(OFS_RETURN), 0, sizeof(vec3_t));\n\tif (tab)\n\t{\n\t\tent = Hash_Get(&tab->tab, name);\n\t\tif (ent)\n\t\t{\n\t\t\tif (ent->type == ev_string)\n\t\t\t{\n\t\t\t\tG_INT(OFS_RETURN+2) = 0;\n\t\t\t\tG_INT(OFS_RETURN+1) = 0;\n\t\t\t\tRETURN_TSTRING(ent->stringdata);\n\t\t\t}\n\t\t\telse\n\t\t\t\tmemcpy(G_VECTOR(OFS_RETURN), ent->data, sizeof(vec3_t));\n\t\t\tHash_RemoveData(&tab->tab, name, ent);\n\t\t\tBZ_Free(ent);\n\t\t}\n\t}\n}\nvoid QCBUILTIN PF_hash_get (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tpf_hashtab_t *tab = PF_hash_findtab(prinst, G_FLOAT(OFS_PARM0));\n\tconst char *name = PR_GetStringOfs(prinst, OFS_PARM1);\n\tvoid *dflt = (prinst->callargc>2)?G_VECTOR(OFS_PARM2):vec3_origin;\n\tint type = (prinst->callargc>3)?G_FLOAT(OFS_PARM3):0;\n\tint index = (prinst->callargc>4)?G_FLOAT(OFS_PARM4):0;\n\tpf_hashentry_t *ent = NULL;\n\tif (tab)\n\t{\n\t\tent = Hash_Get(&tab->tab, name);\n\t\t//skip ones that are the wrong type.\n\t\twhile (type != 0 && ent && ent->type != type)\n\t\t\tent = Hash_GetNext(&tab->tab, name, ent);\n\t\t//and ones that are not the match that we're after.\n\t\twhile(index-->0 && ent)\n\t\t{\n\t\t\tent = Hash_GetNext(&tab->tab, name, ent);\n\t\t\twhile (type != 0 && ent && ent->type != type)\n\t\t\t\tent = Hash_GetNext(&tab->tab, name, ent);\n\t\t}\n\t\tif (ent)\n\t\t{\n\t\t\tif (ent->type == ev_string)\n\t\t\t{\n\t\t\t\tG_INT(OFS_RETURN+2) = 0;\n\t\t\t\tG_INT(OFS_RETURN+1) = 0;\n\t\t\t\tRETURN_TSTRING(ent->stringdata);\n\t\t\t}\n\t\t\telse\n\t\t\t\tmemcpy(G_VECTOR(OFS_RETURN), ent->data, sizeof(vec3_t));\n\t\t\treturn;\n\t\t}\n\t}\n\tmemcpy(G_VECTOR(OFS_RETURN), dflt, sizeof(vec3_t));\n}\nvoid QCBUILTIN PF_hash_getcb (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n/*\n\tpf_hashtab_t *tab = PF_hash_findtab(prinst, G_FLOAT(OFS_PARM0));\n\tfunc_t callback = G_FUNCTION(OFS_PARM1);\n\tchar *name = PR_GetStringOfs(prinst, OFS_PARM2);\n\tpf_hashentry_t *ent = NULL;\n\tif (tab >= 0 && tab < MAX_QC_HASHTABLES && pf_hashtab[tab].valid)\n\t\tent = Hash_Get(&pf_hashtab[tab].tab, name);\n\telse\n\t\tPR_BIError(prinst, \"PF_hash_getcb: invalid hash table\\n\");\n\tif (ent)\n\t\tmemcpy(G_VECTOR(OFS_RETURN), ent->data, sizeof(vec3_t));\n\telse\n\t\tmemcpy(G_VECTOR(OFS_RETURN), G_VECTOR(OFS_PARM2), sizeof(vec3_t));\n*/\n}\n\nvoid QCBUILTIN PF_hash_add (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tpf_hashtab_t *tab = PF_hash_findtab(prinst, G_FLOAT(OFS_PARM0));\n\tconst char *name = PR_GetStringOfs(prinst, OFS_PARM1);\n\tvoid *data = G_VECTOR(OFS_PARM2);\n\tint flags = (prinst->callargc>3)?G_FLOAT(OFS_PARM3):0;\n\tint type = flags & 0xff;\n\tpf_hashentry_t *ent = NULL;\n\tif (tab && *name)\t//our hash tables can't cope with empty keys.\n\t{\n\t\tif (!type)\n\t\t\ttype = tab->defaulttype;\n\t\tif (!(flags & HASH_ADD) || (flags & HASH_REPLACE))\n\t\t{\n\t\t\tent = Hash_Get(&tab->tab, name);\n\t\t\tif (ent)\n\t\t\t{\n\t\t\t\tHash_RemoveData(&tab->tab, name, ent);\n\t\t\t\tBZ_Free(ent);\n\t\t\t}\n\t\t}\n\n\t\tif (type == ev_string)\n\t\t{\t//strings copy their value out.\n\t\t\tconst char *value = PR_GetStringOfs(prinst, OFS_PARM2);\n\t\t\tint nlen = strlen(name);\n\t\t\tint vlen = strlen(value);\n\t\t\tent = BZ_Malloc(sizeof(*ent) + nlen+1 + vlen+1);\n\t\t\tent->name = (char*)(ent+1);\n\t\t\tent->type = ev_string;\n\t\t\tent->stringdata = ent->name+(nlen+1);\n\t\t\tmemcpy(ent->name, name, nlen);\n\t\t\tent->name[nlen] = 0;\n\t\t\tmemcpy(ent->stringdata, value, vlen+1);\n\t\t\tHash_Add(&tab->tab, ent->name, ent, &ent->buck);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tint nlen = strlen(name);\n\t\t\tent = BZ_Malloc(sizeof(*ent) + nlen + 1);\n\t\t\tent->name = (char*)(ent+1);\n\t\t\tent->type = type;\n\t\t\tmemcpy(ent->name, name, nlen);\n\t\t\tent->name[nlen] = 0;\n\t\t\tmemcpy(ent->data, data, sizeof(vec3_t));\n\t\t\tHash_Add(&tab->tab, ent->name, ent, &ent->buck);\n\t\t}\n\t}\n}\nstatic void PF_hash_destroytab_enum(void *ctx, void *ent)\n{\n\tBZ_Free(ent);\n}\nvoid QCBUILTIN PF_hash_destroytab (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tpf_hashtab_t *tab = PF_hash_findtab(prinst, G_FLOAT(OFS_PARM0));\n\tif (tab && tab->prinst == prinst)\n\t{\n\t\ttab->prinst = NULL;\n\t\tHash_Enumerate(&tab->tab, PF_hash_destroytab_enum, NULL);\n\t\tZ_Free(tab->bucketmem);\n\t}\n}\nvoid PF_Hash_DestroyAll(pubprogfuncs_t *prinst)\n{\n\tqboolean freed = true;\n\tsize_t idx;\n\tfor (idx = 0; idx < pf_hash_maxtables; idx++)\n\t{\n\t\tif (pf_hashtab[idx].prinst == prinst)\n\t\t{\n\t\t\tpf_hashtab[idx].prinst = NULL;\n\t\t\tHash_Enumerate(&pf_hashtab[idx].tab, PF_hash_destroytab_enum, NULL);\n\t\t\tZ_Free(pf_hashtab[idx].bucketmem);\n\t\t}\n\t\telse if (pf_hashtab[idx].prinst)\n\t\t\tfreed = false;\n\t}\n\n\tif (freed && pf_hashtab)\n\t{\n\t\tpf_hash_maxtables = 0;\n\t\tZ_Free(pf_hashtab);\n\t\tpf_hashtab\t= NULL;\n\t}\n}\nvoid QCBUILTIN PF_hash_createtab (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\t//FIXME: these need to be managed by the qcvm for garbage collection\n\tint i;\n\tint numbuckets = G_FLOAT(OFS_PARM0);\n//\tqboolean dupestrings = (prinst->callargc>1)?G_FLOAT(OFS_PARM1):false;\n\tetype_t type = (prinst->callargc>1)?G_FLOAT(OFS_PARM1):ev_vector;\n\tif (!type)\n\t\ttype = ev_vector;\n\tif (numbuckets < 4)\n\t\tnumbuckets = 64;\n\n\tfor (i = 0; i < pf_hash_maxtables; i++)\n\t{\t//look for an empty slot\n\t\tif (!pf_hashtab[i].prinst)\n\t\t\tbreak;\n\t}\n\tif (i == pf_hash_maxtables)\n\t{\t//all slots taken, expand list\n\t\tif (!ZF_ReallocElements((void**)&pf_hashtab, &pf_hash_maxtables, pf_hash_maxtables+64, sizeof(*pf_hashtab)))\n\t\t{\n\t\t\tG_FLOAT(OFS_RETURN) = 0;\n\t\t\treturn;\n\t\t}\n\t}\n\n\t//fill it in\n\tpf_hashtab[i].defaulttype = type;\n\tpf_hashtab[i].prinst = prinst;\n\tpf_hashtab[i].bucketmem = Z_Malloc(Hash_BytesForBuckets(numbuckets));\n\tHash_InitTable(&pf_hashtab[i].tab, numbuckets, pf_hashtab[i].bucketmem);\n\tG_FLOAT(OFS_RETURN) = i + FIRSTTABLE;\n}\n\nstatic void PF_hash_savetab(void *ctx, void *data)\n{\n\tchar tmp[8192];\n\tpf_hashentry_t *ent = data;\n\tif (ent->type == ev_string)\n\t\tVFS_PRINTF (ctx, \"\\t%i \\\"%s\\\" %s\\n\", ent->type, ent->name, COM_QuotedString(ent->stringdata, tmp, sizeof(tmp), false));\n\telse if (ent->type == ev_vector)\n\t\tVFS_PRINTF (ctx, \"\\t%i \\\"%s\\\" %f %f %f\\n\", ent->type, ent->name, ent->data[0], ent->data[1], ent->data[2]);\n\telse if (ent->type == ev_float)\n\t\tVFS_PRINTF (ctx, \"\\t%i \\\"%s\\\" %f\\n\", ent->type, ent->name, *(float*)ent->data);\n\telse\n\t\tVFS_PRINTF (ctx, \"\\t%i \\\"%s\\\" %#x\\n\", ent->type, ent->name, *(int*)ent->data);\n}\nstatic void PR_hash_savegame(vfsfile_t *f, pubprogfuncs_t *prinst, qboolean binary)\t//write the persistant table to a saved game.\n{\n\tunsigned int tab;\n\tchar *tmp = NULL;\n\n\tfor (tab = 0; tab < pf_hash_maxtables; tab++)\n\t{\n\t\tif (pf_hashtab[tab].prinst == prinst)// && (pf_hashtab[tab].flags & BUFFLAG_SAVED))\n\t\t{\n\t\t\tVFS_PRINTF (f, \"hashtable %u %i %u\\n\", tab+FIRSTTABLE, pf_hashtab[tab].defaulttype, (unsigned int)pf_hashtab[tab].tab.numbuckets);\n\t\t\tVFS_PRINTF (f, \"{\\n\");\n\t\t\tHash_Enumerate(&pf_hashtab[tab].tab, PF_hash_savetab, f);\n\t\t\tVFS_PRINTF (f, \"}\\n\");\n\t\t}\n\t}\n\tfree(tmp);\n}\nstatic const char *PR_hash_loadgame(pubprogfuncs_t *prinst, const char *l)\n{\n\tchar name[8192];\n\tchar token[65536];\n\tint tabno;\n\tint nlen, vlen;\n\tetype_t hashtype;\n\tsize_t buffersize;\n\tcom_tokentype_t tt;\n\tpf_hashtab_t *tab;\n\tpf_hashentry_t *ent;\n\tl = COM_ParseTokenOut(l, NULL, token, sizeof(token), &tt);if (tt != TTP_RAWTOKEN)return NULL;\n\ttabno = atoi(token)-FIRSTTABLE;\n\tl = COM_ParseTokenOut(l, NULL, token, sizeof(token), &tt);if (tt != TTP_RAWTOKEN)return NULL;\n\thashtype = atoi(token);\n\tl = COM_ParseTokenOut(l, NULL, token, sizeof(token), &tt);if (tt != TTP_RAWTOKEN)return NULL;\n\tbuffersize = atoi(token);\n\n\tl = COM_ParseTokenOut(l, NULL, token, sizeof(token), &tt);if (tt != TTP_LINEENDING)return NULL;\n\tl = COM_ParseTokenOut(l, NULL, token, sizeof(token), &tt);if (tt != TTP_PUNCTUATION)return NULL;\n\tif (strcmp(token, \"{\"))\treturn NULL;\n\n\tif (tabno < 0 || tabno >= 1<<16)\n\t\treturn NULL;\n\tif (tabno >= pf_hash_maxtables)\n\t\tZ_ReallocElements((void**)&pf_hashtab, &pf_hash_maxtables, tabno+1, sizeof(*pf_hashtab));\n\n\ttab = &pf_hashtab[tabno];\n\tif (tab->prinst)\n\t{\n\t\ttab->prinst = NULL;\n\t\tHash_Enumerate(&tab->tab, PF_hash_destroytab_enum, NULL);\n\t\tZ_Free(tab->bucketmem);\n\t\ttab->bucketmem = NULL;\n\t}\n\ttab->prinst = prinst;\n\ttab->defaulttype = hashtype;\n\ttab->bucketmem = Z_Malloc(Hash_BytesForBuckets(buffersize));\n\tHash_InitTable(&tab->tab, buffersize, tab->bucketmem);\n\n\tfor(;;)\n\t{\n\t\tl = COM_ParseTokenOut(l, NULL, token, sizeof(token), &tt);\n\t\tif (tt == TTP_LINEENDING)\n\t\t\tcontinue;\n\t\tif (tt == TTP_PUNCTUATION && !strcmp(token, \"}\"))\n\t\t\tbreak;\n\t\tif (tt != TTP_RAWTOKEN)\n\t\t\tbreak;\n\t\thashtype = atoi(token);\n\t\tl = COM_ParseTokenOut(l, NULL, name, sizeof(name), &tt);if (tt != TTP_STRING)return NULL;\n\n\t\tnlen = strlen(name);\n\t\tif (hashtype == ev_string)\n\t\t{\n\t\t\tl = COM_ParseTokenOut(l, NULL, token, sizeof(token), &tt);if (tt != TTP_STRING)return NULL;\n\t\t\tvlen = strlen(token);\n\t\t\tl = COM_ParseTokenOut(l, NULL, token, sizeof(token), &tt);if (tt != TTP_LINEENDING)return NULL;\n\n\t\t\tent = BZ_Malloc(sizeof(*ent) + nlen+1 + vlen+1);\n\t\t\tent->name = (char*)(ent+1);\n\t\t\tent->type = hashtype;\n\t\t\tent->stringdata = ent->name+(nlen+1);\n\t\t\tmemcpy(ent->name, name, nlen);\n\t\t\tent->name[nlen] = 0;\n\t\t\tmemcpy(ent->stringdata, token, vlen+1);\n\t\t\tHash_Add(&tab->tab, ent->name, ent, &ent->buck);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tvec3_t data = {0,0,0};\n\t\t\tif (hashtype == ev_vector)\n\t\t\t{\n\t\t\t\tl = COM_ParseTokenOut(l, NULL, token, sizeof(token), &tt);if (tt != TTP_RAWTOKEN)return NULL;\n\t\t\t\tdata[0] = atof(token);\n\t\t\t\tl = COM_ParseTokenOut(l, NULL, token, sizeof(token), &tt);if (tt != TTP_RAWTOKEN)return NULL;\n\t\t\t\tdata[1] = atof(token);\n\t\t\t\tl = COM_ParseTokenOut(l, NULL, token, sizeof(token), &tt);if (tt != TTP_RAWTOKEN)return NULL;\n\t\t\t\tdata[2] = atof(token);\n\t\t\t}\n\t\t\telse if (hashtype == ev_float)\n\t\t\t{\n\t\t\t\tl = COM_ParseTokenOut(l, NULL, token, sizeof(token), &tt);if (tt != TTP_RAWTOKEN)return NULL;\n\t\t\t\t*data = atof(token);\n\t\t\t}\n\t\t\telse\t//treat it as an ev_int\n\t\t\t{\n\t\t\t\tl = COM_ParseTokenOut(l, NULL, token, sizeof(token), &tt);if (tt != TTP_RAWTOKEN)return NULL;\n\t\t\t\t*(int*)data = atoi(token);\n\t\t\t}\n\t\t\tl = COM_ParseTokenOut(l, NULL, token, sizeof(token), &tt);if (tt != TTP_LINEENDING)return NULL;\n\n\t\t\tent = BZ_Malloc(sizeof(*ent) + nlen + 1);\n\t\t\tent->name = (char*)(ent+1);\n\t\t\tent->type = hashtype;\n\t\t\tmemcpy(ent->name, name, nlen);\n\t\t\tent->name[nlen] = 0;\n\t\t\tmemcpy(ent->data, data, sizeof(vec3_t));\n\t\t\tHash_Add(&tab->tab, ent->name, ent, &ent->buck);\n\t\t}\n\t}\n\treturn l;\n}\nvoid pf_hash_preserve(void)\t//map changed, make sure it can be reset properly.\n{\n}\nvoid pf_hash_purge(void)\t//restart command was used. revert to the state at the start of the map.\n{\n}\n\n//hash table stuff\n////////////////////////////////////////////////////\n//File access\n\n#define MAX_QC_FILES 256\n\n#define FIRST_QC_FILE_INDEX 1000\n\ntypedef struct {\n\tchar name[256];\n\tvfsfile_t *file;\n\tchar *data;\n\tsize_t bufferlen;\n\tsize_t len;\n\tsize_t ofs;\n\tpubprogfuncs_t *prinst;\n\tlong accessmode;\n} pf_fopen_files_t;\nstatic pf_fopen_files_t pf_fopen_files[MAX_QC_FILES];\n\nstatic qboolean QC_PathRequiresSandbox(const char *name)\n{\n\t//if its a user config, ban any fallback locations so that csqc can't read passwords or whatever.\n\t/* (sorry about the windows paths, it avoids compiler warnings... -Werror sucks)\n\tbad:\n\t\t*.cfg\n\t\tconfigs\\*.cfg\n\tallowed:\n\t\tparticles\\*.cfg (required for particle editor type stuff)\n\t\thuds\\*.cfg  (shouldn't have any passwords. yay editing)\n\t\tmodels\\*.*  (xonotic is evil)\n\t\tsound\\*.*  (xonotic is evil)\n\t*/\n\tif ((!strchr(name, '/') || !strnicmp(name, \"configs/\", 8))\t//\n\t\t&& !stricmp(COM_GetFileExtension(name, NULL), \".cfg\"))\n\t\treturn true;\n\treturn false;\n}\n\n//returns false if the file is denied.\n//fallbackread can be NULL, if the qc is not allowed to read that (original) file at all.\nqboolean QC_FixFileName(const char *name, const char **result, const char **fallbackread)\n{\n\tif (!strncmp(name, \"file:\", 5))\n\t{\n\t\t*result = name;\n\t\t*fallbackread = NULL;\n\t\treturn true;\n\t}\n\n\tif (!*name ||\t//blank names are bad\n\t\tstrchr(name, ':') ||\t//dos/win absolute path, ntfs ADS, amiga drives. reject them all.\n\t\tstrchr(name, '\\\\') ||\t//windows-only paths.\n\t\t*name == '/' ||\t//absolute path was given - reject\n\t\tstrstr(name, \"..\"))\t//someone tried to be clever.\n\t{\n\t\treturn false;\n\t}\n\n\tif (!strncmp(name, \"data/\", 5))\n\t{\n\t\t*fallbackread = NULL;\t//don't be weird.\n\t\t*result = name;\t//already has a data/ prefix.\n\t}\n\telse if (COM_CheckParm(\"-unsafefopen\") && !QC_PathRequiresSandbox(name))\n\t{\n\t\t*fallbackread = va(\"data/%s\", name);\t//in case the mod was distributed with a data/ subdir.\n\t\t*result = name;\n\t}\n\telse\n\t{\n\t\t*fallbackread = QC_PathRequiresSandbox(name)?NULL:name;\n\t\t*result = va(\"data/%s\", name);\n\n\t}\n\treturn true;\n}\n\nvoid QCBUILTIN PF_fopen (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char *name = PR_GetStringOfs(prinst, OFS_PARM0);\n\tint fmode = (prinst->callargc>1)?G_FLOAT(OFS_PARM1):-1;\n\tint fsize = (prinst->callargc>2)?G_FLOAT(OFS_PARM2):0;\n\tconst char *fallbackread;\n\tint i;\n\tsize_t insize;\n\n\tCon_DPrintf(\"qcfopen(\\\"%s\\\", %i) called\\n\", name, fmode);\n\n\tfor (i = 0; i < MAX_QC_FILES; i++)\n\t\tif (!pf_fopen_files[i].prinst)\n\t\t\tbreak;\n\n\tG_FLOAT(OFS_RETURN) = -1; //assume an error\n\tif (i == MAX_QC_FILES)\t//too many already open\n\t{\n\t\tCon_Printf(\"qcfopen(\\\"%s\\\"): too many files open\\n\", name);\n\t\treturn;\n\t}\n\n\tif (fmode < 0 && (!strncmp(name, \"tcp://\", 6) || !strncmp(name, \"tls://\", 6) || !strncmp(name, \"ws:\", 3) || !strncmp(name, \"wss:\", 4)))\n\t{\n\t\textern cvar_t pr_enable_uriget;\n#if defined(CSQC_DAT) && !defined(SERVERONLY)\n\t\textern world_t csqc_world;\n\t\tif (prinst == csqc_world.progs)\t//menuqc will refuse to load from untrusted sources. ssqc is the admin's choice. csqc is fundamentally untrusted though.\n\t\t\treturn;\n#endif\n\t\tif (!pr_enable_uriget.ival)\t//same cvar to block http requests.\n\t\t\treturn;\n\n\t\tQ_strncpyz(pf_fopen_files[i].name, name, sizeof(pf_fopen_files[i].name));\n\t\tpf_fopen_files[i].accessmode = FRIK_FILE_STREAM;\n\t\tpf_fopen_files[i].bufferlen = 0;\n\t\tpf_fopen_files[i].data = NULL;\n\t\tpf_fopen_files[i].len = 0;\n\t\tpf_fopen_files[i].ofs = 0;\n\t\tpf_fopen_files[i].prinst = prinst;\n\n\t\tpf_fopen_files[i].file = FS_OpenTCP(name, 0, true);\n\t\tif (pf_fopen_files[i].file)\n\t\t\tG_FLOAT(OFS_RETURN) = i + FIRST_QC_FILE_INDEX;\n\t\telse\n\t\t{\n\t\t\tG_FLOAT(OFS_RETURN) = -1;\n\t\t\tmemset(&pf_fopen_files[i], 0, sizeof(pf_fopen_files[i]));\n\t\t}\n\t\treturn;\n\t}\n\n\tif (!QC_FixFileName(name, &name, &fallbackread))\n\t{\n\t\tCon_Printf(\"qcfopen(\\\"%s\\\"): Access denied\\n\", name);\n\t\treturn;\n\t}\n\n\tQ_strncpyz(pf_fopen_files[i].name, name, sizeof(pf_fopen_files[i].name));\n\n\tpf_fopen_files[i].accessmode = fmode;\n\tswitch (fmode)\n\t{\n\tcase FRIK_FILE_MMAP_READ:\n\tcase FRIK_FILE_MMAP_RW:\n\t\t{\n\t\t\tvfsfile_t *f = FS_OpenVFS(pf_fopen_files[i].name, \"rb\", FS_GAME);\n\t\t\tif (!f && fallbackread)\n\t\t\t\tf = FS_OpenVFS(fallbackread, \"rb\", FS_GAME);\n\t\t\tif (f)\n\t\t\t{\n\t\t\t\tpf_fopen_files[i].bufferlen = pf_fopen_files[i].len = VFS_GETLEN(f);\n\t\t\t\tif (pf_fopen_files[i].bufferlen < fsize)\n\t\t\t\t\tpf_fopen_files[i].bufferlen = fsize;\n\t\t\t\tpf_fopen_files[i].data = PR_AddressableAlloc(prinst, pf_fopen_files[i].bufferlen);\n\t\t\t\tVFS_READ(f, pf_fopen_files[i].data, pf_fopen_files[i].len);\n\t\t\t\tVFS_CLOSE(f);\n\t\t\t}\n\t\t\telse if (fmode == FRIK_FILE_MMAP_RW)\n\t\t\t{\n\t\t\t\tpf_fopen_files[i].bufferlen = fsize;\n\t\t\t\tpf_fopen_files[i].data = PR_AddressableAlloc(prinst, pf_fopen_files[i].bufferlen);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tpf_fopen_files[i].bufferlen = 0;\n\t\t\t\tpf_fopen_files[i].data = NULL;\n\t\t\t}\n\n\t\t\tif (!pf_fopen_files[i].data)\n\t\t\t\tbreak;\n\n\t\t\tpf_fopen_files[i].len = pf_fopen_files[i].bufferlen;\n\t\t\tpf_fopen_files[i].ofs = 0;\n\t\t\tG_FLOAT(OFS_RETURN) = i + FIRST_QC_FILE_INDEX;\n\t\t\tpf_fopen_files[i].prinst = prinst;\n\t\t}\n\t\tbreak;\n\tcase FRIK_FILE_READ:\n\tcase FRIK_FILE_READ_DELAY:\n\t\t{\n\t\t\tpf_fopen_files[i].accessmode = FRIK_FILE_READ_DELAY;\n\t\t\tpf_fopen_files[i].file = FS_OpenVFS(pf_fopen_files[i].name, \"rb\", FS_GAME);\n\t\t\tif (!pf_fopen_files[i].file && fallbackread)\n\t\t\t{\n\t\t\t\tQ_strncpyz(pf_fopen_files[i].name, fallbackread, sizeof(pf_fopen_files[i].name));\n\t\t\t\tpf_fopen_files[i].file = FS_OpenVFS(pf_fopen_files[i].name, \"rb\", FS_GAME);\n\t\t\t}\n\t\t\tpf_fopen_files[i].ofs = 0;\n\t\t\tif (pf_fopen_files[i].file)\n\t\t\t{\n\t\t\t\tpf_fopen_files[i].len = VFS_GETLEN(pf_fopen_files[i].file);\n\n\t\t\t\tG_FLOAT(OFS_RETURN) = i + FIRST_QC_FILE_INDEX;\n\t\t\t\tpf_fopen_files[i].prinst = prinst;\n\t\t\t}\n\t\t}\n\t\tbreak;\n\n//\tcase FRIK_FILE_READ:\t//read\n\tcase FRIK_FILE_READNL:\t//read whole file\n\t\tfsize = FS_LoadFile(pf_fopen_files[i].name, (void**)&pf_fopen_files[i].data);\n\t\tif (!pf_fopen_files[i].data && fallbackread)\n\t\t{\n\t\t\tQ_strncpyz(pf_fopen_files[i].name, fallbackread, sizeof(pf_fopen_files[i].name));\n\t\t\tfsize = FS_LoadFile(pf_fopen_files[i].name, (void**)&pf_fopen_files[i].data);\n\t\t}\n\n\t\tif (pf_fopen_files[i].data)\n\t\t{\n\t\t\tG_FLOAT(OFS_RETURN) = i + FIRST_QC_FILE_INDEX;\n\t\t\tpf_fopen_files[i].prinst = prinst;\n\t\t}\n\n\t\tpf_fopen_files[i].bufferlen = pf_fopen_files[i].len = fsize;\n\t\tpf_fopen_files[i].ofs = 0;\n\t\tbreak;\n\tcase FRIK_FILE_APPEND:\t//append\n\t\tpf_fopen_files[i].data = FS_LoadMallocFile(pf_fopen_files[i].name, &insize);\n\t\tpf_fopen_files[i].ofs = pf_fopen_files[i].bufferlen = pf_fopen_files[i].len = insize;\n\t\tif (pf_fopen_files[i].data)\n\t\t{\n\t\t\tG_FLOAT(OFS_RETURN) = i + FIRST_QC_FILE_INDEX;\n\t\t\tpf_fopen_files[i].prinst = prinst;\n\t\t\tbreak;\n\t\t}\n\t\t//file didn't exist - fall through\n\tcase FRIK_FILE_WRITE:\t//write\n\t\tpf_fopen_files[i].bufferlen = 8192;\n\t\tpf_fopen_files[i].data = BZ_Malloc(pf_fopen_files[i].bufferlen);\n\t\tpf_fopen_files[i].len = 0;\n\t\tpf_fopen_files[i].ofs = 0;\n\t\tG_FLOAT(OFS_RETURN) = i + FIRST_QC_FILE_INDEX;\n\t\tpf_fopen_files[i].prinst = prinst;\n\t\tbreak;\n\tcase FRIK_FILE_INVALID:\n\t\tpf_fopen_files[i].bufferlen = 0;\n\t\tpf_fopen_files[i].data = \"\";\n\t\tpf_fopen_files[i].len = 0;\n\t\tpf_fopen_files[i].ofs = 0;\n\t\tG_FLOAT(OFS_RETURN) = i + FIRST_QC_FILE_INDEX;\n\t\tpf_fopen_files[i].prinst = prinst;\n\t\tbreak;\n\tdefault: //bad\n\t\tG_FLOAT(OFS_RETURN) = -1;\n\t\tbreak;\n\t}\n}\n\n//non-builtin function used by saved game code.\nint PR_QCFile_From_VFS (pubprogfuncs_t *prinst, const char *name, vfsfile_t *f, qboolean write)\n{\n\tint i;\n\n\tfor (i = 0; i < MAX_QC_FILES; i++)\n\t\tif (!pf_fopen_files[i].prinst)\n\t\t\tbreak;\n\tif (i == MAX_QC_FILES)\t//too many already open\n\t\treturn -1;\n\n\tpf_fopen_files[i].accessmode = write?FRIK_FILE_STREAM:FRIK_FILE_READ_DELAY;\n\n\tQ_strncpyz(pf_fopen_files[i].name, name, sizeof(pf_fopen_files[i].name));\n\tpf_fopen_files[i].file = f;\n\n\tpf_fopen_files[i].ofs = VFS_TELL(pf_fopen_files[i].file);\n\tif (pf_fopen_files[i].file)\n\t{\n\t\tpf_fopen_files[i].len = VFS_GETLEN(pf_fopen_files[i].file);\n\n\t\tpf_fopen_files[i].prinst = prinst;\n\t\treturn i + FIRST_QC_FILE_INDEX;\n\t}\n\telse\n\t\treturn -1;\n}\nint PR_QCFile_From_Buffer (pubprogfuncs_t *prinst, const char *name, void *buffer, size_t ofs, size_t len)\n{\n\tint i;\n\n\tfor (i = 0; i < MAX_QC_FILES; i++)\n\t\tif (!pf_fopen_files[i].prinst)\n\t\t\tbreak;\n\tif (i == MAX_QC_FILES)\t//too many already open\n\t\treturn -1;\n\n\tpf_fopen_files[i].accessmode = FRIK_FILE_READ;\n\n\tQ_strncpyz(pf_fopen_files[i].name, name, sizeof(pf_fopen_files[i].name));\n\tpf_fopen_files[i].file = NULL;\n\tpf_fopen_files[i].data = (void*)buffer;\n\tpf_fopen_files[i].ofs = ofs;\n\tpf_fopen_files[i].len = len;\n\n\tpf_fopen_files[i].prinst = prinst;\n\treturn i + FIRST_QC_FILE_INDEX;\n}\n\n//internal function used by search_begin\nstatic int PF_fopen_search (pubprogfuncs_t *prinst, const char *name, flocation_t *loc)\n{\n\tint i;\n\n\tCon_DPrintf(\"qcfopen(\\\"%s\\\") called\\n\", name);\n\n\tfor (i = 0; i < MAX_QC_FILES; i++)\n\t\tif (!pf_fopen_files[i].prinst)\n\t\t\tbreak;\n\n\tif (i == MAX_QC_FILES)\t//too many already open\n\t{\n\t\tCon_Printf(\"qcfopen(\\\"%s\\\"): too many files open\\n\", name);\n\t\treturn -1;\n\t}\n\n\tif (QC_PathRequiresSandbox(name))\n\t{\t//we're ignoring the data/ dir so using only the fallback, but still blocking it if its a nasty path.\n\t\tCon_Printf(\"qcfopen(\\\"%s\\\"): Access denied\\n\", name);\n\t\treturn -1;\n\t}\n\n\tpf_fopen_files[i].accessmode = FRIK_FILE_READ_DELAY;\n\n\tQ_strncpyz(pf_fopen_files[i].name, name, sizeof(pf_fopen_files[i].name));\n\tif (loc->search->handle)\n\t\tpf_fopen_files[i].file = FS_OpenReadLocation(name, loc);\n\telse\n\t\tpf_fopen_files[i].file = FS_OpenVFS(loc->rawname, \"rb\", FS_ROOT);\n\n\tpf_fopen_files[i].ofs = 0;\n\tif (pf_fopen_files[i].file)\n\t{\n\t\tpf_fopen_files[i].len = VFS_GETLEN(pf_fopen_files[i].file);\n\n\t\tpf_fopen_files[i].prinst = prinst;\n\t\treturn i + FIRST_QC_FILE_INDEX;\n\t}\n\telse\n\t\treturn -1;\n}\n\nvoid PF_fclose_i (int fnum)\n{\n\tif (fnum < 0 || fnum >= MAX_QC_FILES)\n\t{\n\t\tCon_Printf(\"PF_fclose: File out of range\\n\");\n\t\treturn;\t//out of range\n\t}\n\n\tif (!pf_fopen_files[fnum].prinst)\n\t{\n\t\tCon_Printf(\"PF_fclose: File is not open\\n\");\n\t\treturn;\t//not open\n\t}\n\n\tswitch(pf_fopen_files[fnum].accessmode)\n\t{\n\tcase FRIK_FILE_MMAP_RW:\n\t\tCOM_WriteFile(pf_fopen_files[fnum].name, FS_GAMEONLY, pf_fopen_files[fnum].data, pf_fopen_files[fnum].len);\n\t\t/*fall through*/\n\tcase FRIK_FILE_MMAP_READ:\n\t\tpf_fopen_files[fnum].prinst->AddressableFree(pf_fopen_files[fnum].prinst, pf_fopen_files[fnum].data);\n\t\tbreak;\n\n\tcase FRIK_FILE_STREAM:\n\tcase FRIK_FILE_READ_DELAY:\n\t\tVFS_CLOSE(pf_fopen_files[fnum].file);\n\t\tbreak;\n\n\tcase FRIK_FILE_READ:\n\tcase FRIK_FILE_READNL:\n\t\tBZ_Free(pf_fopen_files[fnum].data);\n\t\tbreak;\n\tcase FRIK_FILE_APPEND:\n\tcase FRIK_FILE_WRITE:\n\t\tCOM_WriteFile(pf_fopen_files[fnum].name, FS_GAMEONLY, pf_fopen_files[fnum].data, pf_fopen_files[fnum].len);\n\t\tBZ_Free(pf_fopen_files[fnum].data);\n\t\tbreak;\n\tcase FRIK_FILE_INVALID:\n\t\tbreak;\n\t}\n\tpf_fopen_files[fnum].file = NULL;\n\tpf_fopen_files[fnum].data = NULL;\n\tpf_fopen_files[fnum].prinst = NULL;\n}\n\nvoid QCBUILTIN PF_fclose (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint fnum = G_FLOAT(OFS_PARM0)-FIRST_QC_FILE_INDEX;\n\n\tif (fnum < 0 || fnum >= MAX_QC_FILES)\n\t{\n\t\tPF_Warningf(prinst, \"PF_fclose: File out of range (%g)\\n\", G_FLOAT(OFS_PARM0));\n\t\treturn;\t//out of range\n\t}\n\n\tif (!pf_fopen_files[fnum].prinst)\n\t{\n\t\tCon_Printf(\"PF_fclose: File is not open\\n\");\n\t\treturn;\t//not open\n\t}\n\n\tif (pf_fopen_files[fnum].prinst != prinst)\n\t{\n\t\tPF_Warningf(prinst, \"PF_fclose: File is from wrong instance\\n\");\n\t\treturn;\t//this just isn't ours.\n\t}\n\n\tPF_fclose_i(fnum);\n}\n\nvoid QCBUILTIN PF_fgets (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tchar c, *s, *o, *max, *eof;\n\tint fnum = G_FLOAT(OFS_PARM0) - FIRST_QC_FILE_INDEX;\n\tchar pr_string_temp[4096];\n\n\t*pr_string_temp = '\\0';\n\tG_INT(OFS_RETURN) = 0;\t//EOF\n\tif (fnum < 0 || fnum >= MAX_QC_FILES)\n\t{\n\t\tPF_Warningf(prinst, \"PF_fgets: File out of range (%g)\\n\", G_FLOAT(OFS_PARM0));\n\t\treturn;\t//out of range\n\t}\n\n\tif (!pf_fopen_files[fnum].prinst)\n\t{\n\t\tPF_Warningf(prinst, \"PF_fgets: File is not open\\n\");\n\t\treturn;\t//not open\n\t}\n\n\tif (pf_fopen_files[fnum].prinst != prinst)\n\t{\n\t\tPF_Warningf(prinst, \"PF_fgets: File is from wrong instance\\n\");\n\t\treturn;\t//this just isn't ours.\n\t}\n\n\tif (pf_fopen_files[fnum].accessmode == FRIK_FILE_STREAM)\n\t{\n\t\tif (VFS_GETS(pf_fopen_files[fnum].file, pr_string_temp, sizeof(pr_string_temp)))\n\t\t\tRETURN_TSTRING(pr_string_temp);\n\t\telse\n\t\t\tG_INT(OFS_RETURN) = 0;\t//EOF\n\t\treturn;\n\t}\n\tif (pf_fopen_files[fnum].accessmode == FRIK_FILE_READ_DELAY)\n\t{\t//on first read, convert into a regular file.\n\t\tpf_fopen_files[fnum].accessmode = FRIK_FILE_READ;\n\t\tpf_fopen_files[fnum].data = BZ_Malloc(pf_fopen_files[fnum].len+1);\n\t\tpf_fopen_files[fnum].data[pf_fopen_files[fnum].len] = 0;\n\t\tpf_fopen_files[fnum].len = pf_fopen_files[fnum].bufferlen = VFS_READ(pf_fopen_files[fnum].file, pf_fopen_files[fnum].data, pf_fopen_files[fnum].len);\n\t\tVFS_CLOSE(pf_fopen_files[fnum].file);\n\t\tpf_fopen_files[fnum].file = NULL;\n\t}\n\n\tif (pf_fopen_files[fnum].accessmode == FRIK_FILE_MMAP_READ || pf_fopen_files[fnum].accessmode == FRIK_FILE_MMAP_RW)\n\t{\n\t\tG_INT(OFS_RETURN) = PR_SetString(prinst, pf_fopen_files[fnum].data);\n\t\treturn;\n\t}\n\t\n\tif (pf_fopen_files[fnum].accessmode == FRIK_FILE_READNL)\n\t{\n\t\tif (pf_fopen_files[fnum].ofs >= pf_fopen_files[fnum].len)\n\t\t\tG_INT(OFS_RETURN) = 0;\t//EOF\n\t\telse\n\t\t\tRETURN_TSTRING(pf_fopen_files[fnum].data);\n\t}\n\telse\n\t{\n\t\t//read up to the next \\n, ignoring any \\rs.\n\t\to = pr_string_temp;\n\t\tmax = o + sizeof(pr_string_temp)-1;\n\t\ts = pf_fopen_files[fnum].data+pf_fopen_files[fnum].ofs;\n\t\teof = pf_fopen_files[fnum].data+pf_fopen_files[fnum].len;\n\t\t\n\t\tif (s >= eof)\n\t\t{\n\t\t\tG_INT(OFS_RETURN) = 0;\t//EOF\n\t\t\treturn;\n\t\t}\n\n\t\twhile(s < eof)\n\t\t{\n\t\t\tc = *s++;\n\t\t\tif (c == '\\n' && pf_fopen_files[fnum].accessmode != FRIK_FILE_READNL)\n\t\t\t\tbreak;\n\t\t\tif (c == '\\r' && pf_fopen_files[fnum].accessmode != FRIK_FILE_READNL)\n\t\t\t\tcontinue;\n\n\t\t\tif (c == 0)\n\t\t\t{\t//modified utf-8, woo. but don't double-encode other chars.\n\t\t\t\tif (o+1 >= max)\n\t\t\t\t\tbreak;\n\t\t\t\t*o++ = 0xc0;\n\t\t\t\t*o++ = 0x80;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (o == max)\n\t\t\t\t\tbreak;\n\t\t\t\t*o++ = c;\n\t\t\t}\n\t\t}\n\t\t*o = '\\0';\n\n\t\tpf_fopen_files[fnum].ofs = s - pf_fopen_files[fnum].data;\n\n\t\tRETURN_TSTRING(pr_string_temp);\n\t}\n}\n\n//ensures that the buffer is at least big enough.\nstatic qboolean PF_fresizebuffer_internal (pf_fopen_files_t *f, size_t newlen)\n{\n\tswitch(f->accessmode)\n\t{\n\tdefault:\n\t\treturn false;\n\tcase FRIK_FILE_MMAP_RW:\n\t\t//mmap cannot change the buffer size/allocation.\n\t\tif (newlen > f->bufferlen)\n\t\t\treturn false;\n\t\tbreak;\n\tcase FRIK_FILE_APPEND:\n\tcase FRIK_FILE_WRITE:\n\t\tif (f->bufferlen < newlen)\n\t\t{\n\t\t\tchar *newbuf;\n\t\t\tsize_t newbuflen = max(newlen, newlen*2+1024);\n\t\t\tnewbuf = BZF_Malloc(newbuflen);\n\t\t\tif (!newbuf)\n\t\t\t\treturn false;\n\t\t\tmemcpy(newbuf, f->data, f->bufferlen);\n\t\t\tmemset(newbuf+f->bufferlen, 0, newbuflen - f->bufferlen);\n\t\t\tBZ_Free(f->data);\n\t\t\tf->data = newbuf;\n\t\t\tf->bufferlen = newbuflen;\n\t\t}\n\t\tbreak;\n\t}\n\treturn true;\n}\nstatic int PF_fwrite_internal (pubprogfuncs_t *prinst, int fnum, const char *msg, size_t len)\n{\n\tif (fnum < 0 || fnum >= MAX_QC_FILES)\n\t{\n\t\tPF_Warningf(prinst, \"PF_fwrite: File out of range\\n\");\n\t\treturn 0;\t//out of range\n\t}\n\n\tif (!pf_fopen_files[fnum].prinst)\n\t{\n\t\tPF_Warningf(prinst, \"PF_fwrite: File is not open\\n\");\n\t\treturn 0;\t//not open\n\t}\n\n\tif (pf_fopen_files[fnum].prinst != prinst)\n\t{\n\t\tPF_Warningf(prinst, \"PF_fwrite: File is from wrong instance\\n\");\n\t\treturn 0;\t//this just isn't ours.\n\t}\n\n\tif (pf_fopen_files[fnum].accessmode == FRIK_FILE_STREAM)\n\t\treturn VFS_WRITE(pf_fopen_files[fnum].file, msg, len);\n\n\tif (pf_fopen_files[fnum].ofs + len < pf_fopen_files[fnum].ofs)\n\t{\n\t\tPF_Warningf(prinst, \"PF_fwrite: size overflow\\n\");\n\t\treturn 0;\n\t}\n\n\tswitch(pf_fopen_files[fnum].accessmode)\n\t{\n\tdefault:\n\t\treturn 0;\n\tcase FRIK_FILE_APPEND:\n\tcase FRIK_FILE_WRITE:\n\tcase FRIK_FILE_MMAP_RW:\n\t\tPF_fresizebuffer_internal(&pf_fopen_files[fnum], pf_fopen_files[fnum].ofs+len);\n\t\tlen = min(len, pf_fopen_files[fnum].bufferlen-pf_fopen_files[fnum].ofs);\n\n\t\tmemcpy(pf_fopen_files[fnum].data + pf_fopen_files[fnum].ofs, msg, len);\n\t\tif (pf_fopen_files[fnum].len < pf_fopen_files[fnum].ofs + len)\n\t\t\tpf_fopen_files[fnum].len = pf_fopen_files[fnum].ofs + len;\n\t\tpf_fopen_files[fnum].ofs+=len;\n\t\treturn len;\n\t}\n}\n\nstatic int PF_fread_internal (pubprogfuncs_t *prinst, int fnum, char *buf, size_t len)\n{\n\tif (fnum < 0 || fnum >= MAX_QC_FILES)\n\t{\n\t\tPF_Warningf(prinst, \"PF_fread: File out of range\\n\");\n\t\treturn 0;\t//out of range\n\t}\n\n\tif (!pf_fopen_files[fnum].prinst)\n\t{\n\t\tPF_Warningf(prinst, \"PF_fread: File is not open\\n\");\n\t\treturn 0;\t//not open\n\t}\n\n\tif (pf_fopen_files[fnum].prinst != prinst)\n\t{\n\t\tPF_Warningf(prinst, \"PF_fread: File is from wrong instance\\n\");\n\t\treturn 0;\t//this just isn't ours.\n\t}\n\n\tif (pf_fopen_files[fnum].accessmode == FRIK_FILE_STREAM)\n\t\treturn VFS_READ(pf_fopen_files[fnum].file, buf, len);\n\tif (pf_fopen_files[fnum].accessmode == FRIK_FILE_READ_DELAY)\n\t{\t//on first read, convert into a regular file.\n\t\tpf_fopen_files[fnum].accessmode = FRIK_FILE_READ;\n\t\tpf_fopen_files[fnum].data = BZ_Malloc(pf_fopen_files[fnum].len+1);\n\t\tpf_fopen_files[fnum].data[pf_fopen_files[fnum].len] = 0;\n\t\tpf_fopen_files[fnum].len = pf_fopen_files[fnum].bufferlen = VFS_READ(pf_fopen_files[fnum].file, pf_fopen_files[fnum].data, pf_fopen_files[fnum].len);\n\t\tVFS_CLOSE(pf_fopen_files[fnum].file);\n\t\tpf_fopen_files[fnum].file = NULL;\n\t}\n\n\tswitch(pf_fopen_files[fnum].accessmode)\n\t{\n\tdefault:\n\t\tPF_Warningf(prinst, \"PF_fread: File not opened for reading\\n\");\n\t\treturn 0;\n\tcase FRIK_FILE_READ:\n\t\tif (pf_fopen_files[fnum].ofs + len > pf_fopen_files[fnum].len)\n\t\t\tlen = pf_fopen_files[fnum].len - pf_fopen_files[fnum].ofs;\n\n\t\tmemcpy(buf, pf_fopen_files[fnum].data + pf_fopen_files[fnum].ofs, len);\n\t\tpf_fopen_files[fnum].ofs+=len;\n\t\treturn len;\n\t}\n}\n\nvoid QCBUILTIN PF_fputs (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint fnum = G_FLOAT(OFS_PARM0) - FIRST_QC_FILE_INDEX;\n\tconst char *msg = PF_VarString(prinst, 1, pr_globals);\n\tint len = strlen(msg);\n\n\tPF_fwrite_internal (prinst, fnum, msg, len);\n}\n\nvoid QCBUILTIN PF_fwrite (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint fnum = G_FLOAT(OFS_PARM0) - FIRST_QC_FILE_INDEX;\n\tint qcptr = G_INT(OFS_PARM1);\n\tint size = G_INT(OFS_PARM2);\n\tint srcoffset = (prinst->callargc>3)?G_INT(OFS_PARM3):0;\n\tconst void *ptr = PR_PointerToNative_MoInvalidate(prinst, qcptr, srcoffset, size);\n\tif (!ptr)\n\t{\n\t\tPR_BIError(prinst, \"PF_fwrite: invalid ptr / size\\n\");\n\t\treturn;\n\t}\n\n\tG_INT(OFS_RETURN) = PF_fwrite_internal (prinst, fnum, ptr, size);\n}\nvoid QCBUILTIN PF_fread (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint fnum = G_FLOAT(OFS_PARM0) - FIRST_QC_FILE_INDEX;\n\tint qcptr = G_INT(OFS_PARM1);\n\tint size = G_INT(OFS_PARM2);\n\tint dstoffset = (prinst->callargc>3)?G_INT(OFS_PARM3):0;\n\tvoid *ptr = PR_PointerToNative_Resize(prinst, qcptr, dstoffset, size);\n\tif (!ptr)\n\t{\n\t\tPR_BIError(prinst, \"PF_fread: invalid ptr / size\\n\");\n\t\treturn;\n\t}\n\n\tG_INT(OFS_RETURN) = PF_fread_internal (prinst, fnum, ptr, size);\n}\nvoid QCBUILTIN PF_fseek64 (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint fnum = G_FLOAT(OFS_PARM0) - FIRST_QC_FILE_INDEX;\n\tG_INT64(OFS_RETURN) = -1;\n\tif (fnum < 0 || fnum >= MAX_QC_FILES)\n\t{\n\t\tPF_Warningf(prinst, \"PF_fseek: File out of range\\n\");\n\t\treturn;\t//out of range\n\t}\n\tif (!pf_fopen_files[fnum].prinst)\n\t{\n\t\tPF_Warningf(prinst, \"PF_fseek: File is not open\\n\");\n\t\treturn;\t//not open\n\t}\n\tif (pf_fopen_files[fnum].prinst != prinst)\n\t{\n\t\tPF_Warningf(prinst, \"PF_fseek: File is from wrong instance\\n\");\n\t\treturn;\t//this just isn't ours.\n\t}\n\n\tif (pf_fopen_files[fnum].accessmode == FRIK_FILE_READ_DELAY)\n\t{\t//on first read, convert into a regular file.\n\t\tpf_fopen_files[fnum].accessmode = FRIK_FILE_READ;\n\t\tpf_fopen_files[fnum].data = BZ_Malloc(pf_fopen_files[fnum].len+1);\n\t\tpf_fopen_files[fnum].data[pf_fopen_files[fnum].len] = 0;\n\t\tpf_fopen_files[fnum].len = pf_fopen_files[fnum].bufferlen = VFS_READ(pf_fopen_files[fnum].file, pf_fopen_files[fnum].data, pf_fopen_files[fnum].len);\n\t\tVFS_CLOSE(pf_fopen_files[fnum].file);\n\t\tpf_fopen_files[fnum].file = NULL;\n\t}\n\n\tif (pf_fopen_files[fnum].file)\n\t{\n\t\tG_INT64(OFS_RETURN) = VFS_TELL(pf_fopen_files[fnum].file);\n\t\tif (prinst->callargc>1 && G_INT64(OFS_PARM1) >= 0)\n\t\t\tVFS_SEEK(pf_fopen_files[fnum].file, G_INT64(OFS_PARM1));\n\t}\n\telse\n\t{\n\t\tG_INT64(OFS_RETURN) = pf_fopen_files[fnum].ofs;\n\t\tif (prinst->callargc>1 && G_INT64(OFS_PARM1) >= 0)\n\t\t{\n\t\t\tpf_fopen_files[fnum].ofs = G_INT64(OFS_PARM1);\n\t\t}\n\t}\n}\nvoid QCBUILTIN PF_fseek32 (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tG_INT64(OFS_PARM1) = G_INT(OFS_PARM1);\n\tPF_fseek64(prinst, pr_globals);\n\tG_INT(OFS_RETURN) = G_INT64(OFS_RETURN);\n}\nvoid QCBUILTIN PF_fsize64 (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint fnum = G_FLOAT(OFS_PARM0) - FIRST_QC_FILE_INDEX;\n\tG_INT64(OFS_RETURN) = -1;\n\tif (fnum < 0 || fnum >= MAX_QC_FILES)\n\t{\n\t\tPF_Warningf(prinst, \"PF_fsize: File out of range\\n\");\n\t\treturn;\t//out of range\n\t}\n\tif (!pf_fopen_files[fnum].prinst)\n\t{\n\t\tPF_Warningf(prinst, \"PF_fsize: File is not open\\n\");\n\t\treturn;\t//not open\n\t}\n\tif (pf_fopen_files[fnum].prinst != prinst)\n\t{\n\t\tPF_Warningf(prinst, \"PF_fsize: File is from wrong instance\\n\");\n\t\treturn;\t//this just isn't ours.\n\t}\n\n\tif (pf_fopen_files[fnum].accessmode == FRIK_FILE_READ_DELAY)\n\t{\t//on first read, convert into a regular file.\n\t\tpf_fopen_files[fnum].accessmode = FRIK_FILE_READ;\n\t\tpf_fopen_files[fnum].data = BZ_Malloc(pf_fopen_files[fnum].len+1);\n\t\tpf_fopen_files[fnum].data[pf_fopen_files[fnum].len] = 0;\n\t\tpf_fopen_files[fnum].len = pf_fopen_files[fnum].bufferlen = VFS_READ(pf_fopen_files[fnum].file, pf_fopen_files[fnum].data, pf_fopen_files[fnum].len);\n\t\tVFS_CLOSE(pf_fopen_files[fnum].file);\n\t\tpf_fopen_files[fnum].file = NULL;\n\t}\n\n\tif (pf_fopen_files[fnum].file)\n\t{\n\t\tG_INT64(OFS_RETURN) = VFS_GETLEN(pf_fopen_files[fnum].file);\n\t\tif (prinst->callargc>1 && G_INT64(OFS_PARM1) >= 0)\n\t\t\tPF_Warningf(prinst, \"PF_fsize: truncation/extension is not supported for stream file types\\n\");\n\t}\n\telse\n\t{\n\t\tG_INT64(OFS_RETURN) = pf_fopen_files[fnum].len;\n\t\tif (prinst->callargc>1 && G_INT64(OFS_PARM1) >= 0)\n\t\t{\n\t\t\tsize_t newlen = G_INT64(OFS_PARM1);\n\t\t\tPF_fresizebuffer_internal(&pf_fopen_files[fnum], newlen);\n\t\t\tpf_fopen_files[fnum].len = min(pf_fopen_files[fnum].bufferlen, newlen);\n\t\t}\n\t}\n}\nvoid QCBUILTIN PF_fsize32 (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tG_INT64(OFS_PARM1) = G_INT(OFS_PARM1);\n\tPF_fsize64(prinst, pr_globals);\n\tG_INT(OFS_RETURN) = G_INT64(OFS_RETURN);\n}\n\nvoid PF_fcloseall (pubprogfuncs_t *prinst)\n{\n\tqboolean write;\n\tint i;\n\tfor (i = 0; i < MAX_QC_FILES; i++)\n\t{\n\t\tif (pf_fopen_files[i].prinst != prinst)\n\t\t\tcontinue;\n\n\t\tswitch(pf_fopen_files[i].accessmode)\n\t\t{\n\t\tcase FRIK_FILE_STREAM:\n\t\tcase FRIK_FILE_APPEND:\n\t\tcase FRIK_FILE_WRITE:\n\t\tcase FRIK_FILE_MMAP_RW:\n\t\t\twrite = true;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\twrite = false;\n\t\t\tbreak;\n\t\t}\n\t\tif (developer.ival || write)\n\t\t\tCon_Printf(\"qc file %s was still open\\n\", pf_fopen_files[i].name);\n\t\tPF_fclose_i(i);\n\t}\n\ttokenizeqc(\"\", false);\n\tPF_buf_shutdown(prinst);\t//might as well put this here\n}\n\nvoid QCBUILTIN PF_fcopy (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char *srcname = PR_GetStringOfs(prinst, OFS_PARM0);\n\tconst char *dstname = PR_GetStringOfs(prinst, OFS_PARM1);\n\tconst char *fallbackread, *fallbackwrite;\n\tvfsfile_t *src, *dst;\n\tchar buffer[65536];\n\tint sz;\n\tG_FLOAT(OFS_RETURN) = -1;\n\tif (QC_FixFileName(srcname, &srcname, &fallbackread))\n\t{\n\t\tif (QC_FixFileName(dstname, &dstname, &fallbackwrite))\n\t\t{\n\t\t\tsrc = FS_OpenVFS(srcname, \"rb\", FS_GAME);\n\t\t\tif (!src)\n\t\t\t\tsrc = FS_OpenVFS(fallbackread, \"rb\", FS_GAME);\n\t\t\tif (src)\n\t\t\t{\n\t\t\t\tdst = FS_OpenVFS(srcname, \"wbp\", FS_GAMEONLY);\t//lets mark it as persistent. this is probably profile data after all.\n\t\t\t\tif (dst)\n\t\t\t\t{\n\t\t\t\t\twhile ((sz = VFS_READ(src, buffer, sizeof(buffer)))>0)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (sz != VFS_WRITE(dst, buffer, sz))\n\t\t\t\t\t\t\tG_FLOAT(OFS_RETURN) = -3;\t//weird errors...\n\t\t\t\t\t}\n\t\t\t\t\tG_FLOAT(OFS_RETURN) = 0;\t//success...\n\t\t\t\t\tVFS_CLOSE(dst);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tG_FLOAT(OFS_RETURN) = -2;\t//output failure\n\t\t\t\tVFS_CLOSE(src);\n\t\t\t}\n\t\t}\n\t}\n}\nvoid QCBUILTIN PF_frename (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char *srcname = PR_GetStringOfs(prinst, OFS_PARM0);\n\tconst char *dstname = PR_GetStringOfs(prinst, OFS_PARM1);\n\tconst char *fallbackread, *fallbackwrite; //not actually used, but present because QC_FixFileName wants a fallback\n\tG_FLOAT(OFS_RETURN) = -1; //some kind of dodgy path problem\n\tif (QC_FixFileName(srcname, &srcname, &fallbackread))\n\t\tif (QC_FixFileName(dstname, &dstname, &fallbackwrite))\n\t\t{\n\t\t\tif (FS_Rename(srcname, dstname, FS_GAMEONLY))\n\t\t\t\tG_FLOAT(OFS_RETURN) = 0;\n\t\t\telse\n\t\t\t\tG_FLOAT(OFS_RETURN) = -5; //random, but whatever\n\t\t}\n}\nvoid QCBUILTIN PF_fremove (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char *fname = PR_GetStringOfs(prinst, OFS_PARM0);\n\tconst char *fallbackread; //not actually used, but present because QC_FixFileName wants a fallback\n\tG_FLOAT(OFS_RETURN) = -1; //some kind of dodgy path problem\n\tif (QC_FixFileName(fname, &fname, &fallbackread))\n\t{\n\t\tif (FS_Remove(fname, FS_GAMEONLY))\n\t\t\tG_FLOAT(OFS_RETURN) = 0;\n\t\telse\n\t\t\tG_FLOAT(OFS_RETURN) = -5; //random, but whatever\n\t}\n}\nvoid QCBUILTIN PF_fexists (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char *srcname = PR_GetStringOfs(prinst, OFS_PARM0);\n\tflocation_t loc;\n\n\tint depth = FS_FLocateFile(srcname, FSLF_DEPTH_INEXPLICIT, &loc);\n\n\tif (depth == 1)\n\t\tG_FLOAT(OFS_RETURN) = true;\t\t//exists and should be in the writable path.\n\telse\n\t\tG_FLOAT(OFS_RETURN) = false;\t//doesn't exist / not writable / etc, should match wrath.\n}\nvoid QCBUILTIN PF_rmtree (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char *fname = PR_GetStringOfs(prinst, OFS_PARM0);\n\tCon_Printf(\"rmtree(\\\"%s\\\"): rmtree is not implemented at this time\\n\", fname);\n\n\t/*flocation_t loc;\n\tG_FLOAT(OFS_RETURN) = -1; //error\n\tif (FS_FLocateFile(fname, FSLF_IGNORELINKS|FSLF_DONTREFERENCE, &loc))\t//find the right gamedir for it...\n\t{\n\t\tfname = va(\"%s/\", fname);\t//its meant to be a directory, make sure that's explicit\n\t\tif (FS_RemoveTree(loc.search->handle, fname))\n\t\t\tG_FLOAT(OFS_RETURN) = 0;\n\t}*/\n}\n\n//DP_QC_WHICHPACK\nvoid QCBUILTIN PF_whichpack (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char *srcname = PR_GetStringOfs(prinst, OFS_PARM0);\n\tunsigned int flags = prinst->callargc>1?G_FLOAT(OFS_PARM1):WP_REFERENCE;\n\tflocation_t loc;\n\n\tif (FS_FLocateFile(srcname, FSLF_IFFOUND, &loc))\n\t{\n\t\tsrcname = FS_WhichPackForLocation(&loc, flags);\n\t\tif (srcname == NULL)\n\t\t\tsrcname = \"\";\n\t\tRETURN_TSTRING(srcname);\n\t}\n\telse\n\t{\n\t\tG_INT(OFS_RETURN) = 0;\t//null/empty\n\t}\n\n}\n\n\nenum\n{\n//\tQCSEARCH_INSENSITIVE = 1u<<0,\t//for dp, we're always insensitive you prick.\n\tQCSEARCH_FULLPACKAGE = 1u<<1,\t//package names include gamedir prefix etc.\n\tQCSEARCH_ALLOWDUPES  = 1u<<2,\t//don't filter out dupes, allowing entries hidden by later packages to be shown.\n\tQCSEARCH_FORCESEARCH = 1u<<3,\t//force the search to succeed even if the gamedir/package is not active.\n\tQCSEARCH_MULTISEARCH = 1u<<4,\t//to avoid possible string manipulation exploits?\n\tQCSEARCH_NAMESORT    = 1u<<5,\t//sort results by filename, instead of by filesystem priority/randomness\n};\nsearchpathfuncs_t *COM_EnumerateFilesPackage (char *matches, const char *package, unsigned int flags, int (QDECL *func)(const char *, qofs_t, time_t mtime, void *, searchpathfuncs_t*), void *parm);\ntypedef struct prvmsearch_s {\n\tpubprogfuncs_t *fromprogs;\t//share across menu/server\n\n\tsearchpath_t searchinfo;\n\n\tint entries;\n\tstruct prvmsearchentry_s\n\t{\n\t\tchar *name;\n\t\tqofs_t size;\n\t\ttime_t mtime;\n\t\tsearchpathfuncs_t *package;\n\t} *entry;\n\tchar *pattern;\n\tunsigned int flags;\n\tunsigned int fsflags;\n} prvmsearch_t;\nstatic prvmsearch_t *pr_searches;\t//realloced to extend\nstatic size_t numpr_searches;\n\nvoid search_close (pubprogfuncs_t *prinst, int handle)\n{\n\tint i;\n\tprvmsearch_t *s;\n\n\tif (handle < 0 || handle >= numpr_searches || pr_searches[handle].fromprogs != prinst)\n\t{\n\t\tPF_Warningf(prinst, \"search_close: Invalid search handle %i\\n\", handle);\n\t\treturn;\n\t}\n\ts = &pr_searches[handle];\n\n\tfor (i = 0; i < s->entries; i++)\n\t\tBZ_Free(s->entry[i].name);\n\tZ_Free(s->pattern);\n\tBZ_Free(s->entry);\n\tif (s->searchinfo.handle)\n\t\ts->searchinfo.handle->ClosePath(s->searchinfo.handle);\n\tmemset(s, 0, sizeof(*s));\n}\n//a progs was closed... hunt down it's searches, and warn about any searches left open.\nvoid search_close_progs(pubprogfuncs_t *prinst, qboolean complain)\n{\n\tint i, j;\n\tprvmsearch_t *s;\n\tqboolean stillactive = false;\n\n\tfor (j = 0; j < numpr_searches; j++)\n\t{\n\t\ts = &pr_searches[j];\n\t\tif (s->fromprogs == prinst)\n\t\t{\t//close it down.\n\n\t\t\tif (complain)\n\t\t\t\tCon_DPrintf(\"Warning: Progs search was still active (pattern: %s)\\n\", s->pattern);\n\n\t\t\tfor (i = 0; i < s->entries; i++)\n\t\t\t\tBZ_Free(s->entry[i].name);\n\t\t\tZ_Free(s->pattern);\n\t\t\tBZ_Free(s->entry);\n\t\t\tmemset(s, 0, sizeof(*s));\n\t\t}\n\t\telse if (s->fromprogs)\n\t\t\tstillactive = true;\n\t}\n\n\tif (!stillactive)\n\t{\t//none left, we might as well release the memory.\n\t\tBZ_Free(pr_searches);\n\t\tpr_searches = NULL;\n\t\tnumpr_searches = 0;\n\t}\n}\n\nstatic int QDECL search_name_sort(const void *av, const void *bv)\n{\n\tconst struct prvmsearchentry_s *a = av, *b = bv;\n\tint ret = strcmp(a->name, b->name);\n\t//FIXME: if equal sort by original order!\n\treturn ret;\n}\n\nstatic int QDECL search_enumerate(const char *name, qofs_t fsize, time_t mtime, void *parm, searchpathfuncs_t *spath)\n{\n\tprvmsearch_t *s = parm;\n\n\tsize_t i;\n\tif (!(s->flags & QCSEARCH_ALLOWDUPES))\n\t{\n\t\tfor (i = 0; i < s->entries; i++)\n\t\t{\n\t\t\tif (!Q_strcasecmp(name, s->entry[i].name))\n\t\t\t\treturn true;\t//already in the list, apparently. try to avoid dupes.\n\t\t}\n\t}\n\n\ts->entry = BZ_Realloc(s->entry, ((s->entries+64)&~63) * sizeof(*s->entry));\n\ts->entry[s->entries].name = BZ_Malloc(strlen(name)+1);\n\tstrcpy(s->entry[s->entries].name, name);\n\ts->entry[s->entries].size = fsize;\n\ts->entry[s->entries].mtime = mtime;\n\ts->entry[s->entries].package = spath;\n\n\ts->entries++;\n\treturn true;\n}\n\n//float\tsearch_begin(string pattern, float flags, float quiet) = #74;\nvoid QCBUILTIN PF_search_begin (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\t//< 0 for error, >= 0 for handle.\n\t//error includes bad search patterns, but not no files\n\tconst char *pattern = PR_GetStringOfs(prinst, OFS_PARM0);\n\tunsigned int flags = G_FLOAT(OFS_PARM1);\n//\tqboolean quiet = G_FLOAT(OFS_PARM2);\t//fte is not noisy\n\tconst char *package = (prinst->callargc>3)?PR_GetStringOfs(prinst, OFS_PARM3):NULL;\n\tprvmsearch_t *s;\n\tsize_t j;\n\n\tif (!*pattern || (*pattern == '.' && pattern[1] == '.') || *pattern == '/' || *pattern == '\\\\' || (!(flags&QCSEARCH_MULTISEARCH)&&strchr(pattern, ':')))\n\t{\n\t\tPF_Warningf(prinst, \"PF_search_begin: bad search pattern \\\"%s\\\"\\n\", pattern);\n\t\tG_FLOAT(OFS_RETURN) = -1;\n\t\treturn;\n\t}\n\n\tfor (j = 0; j < numpr_searches; j++)\n\t\tif (!pr_searches[j].fromprogs)\n\t\t\tbreak;\n\tif (j == numpr_searches)\n\t{\n\t\tif (!ZF_ReallocElements((void**)&pr_searches, &numpr_searches, j+1, sizeof(*s)))\n\t\t{\t//o.O\n\t\t\tG_FLOAT(OFS_RETURN) = -1;\n\t\t\treturn;\n\t\t}\n\t}\n\ts = &pr_searches[j];\n\n\ts->pattern = Z_StrDup(pattern);\n\ts->fromprogs = prinst;\n\ts->flags = flags;\n\ts->fsflags = 0;\n\tif (flags&QCSEARCH_FULLPACKAGE)\n\t\ts->fsflags |= WP_FULLPATH;\n\tif (flags&QCSEARCH_FORCESEARCH)\n\t\ts->fsflags |= WP_FORCE;\n\n\tQ_strncpyz(s->searchinfo.purepath, package?package:\"\", sizeof(s->searchinfo.purepath));\n\ts->searchinfo.handle = COM_EnumerateFilesPackage(s->pattern, package?s->searchinfo.purepath:NULL, s->fsflags, search_enumerate, s);\n\n\tif (flags&QCSEARCH_NAMESORT)\n\t\tqsort(s->entry, s->entries, sizeof(*s->entry), search_name_sort);\n\n\tG_FLOAT(OFS_RETURN) = j;\n}\n//void\tsearch_end(float handle) = #75;\nvoid QCBUILTIN PF_search_end (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint handle = G_FLOAT(OFS_PARM0);\n\tsearch_close(prinst, handle);\n}\n//float\tsearch_getsize(float handle) = #76;\nvoid QCBUILTIN PF_search_getsize (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint handle = G_FLOAT(OFS_PARM0);\n\tprvmsearch_t *s;\n\tG_FLOAT(OFS_RETURN) = 0;\n\n\tif (handle < 0 || handle >= numpr_searches || pr_searches[handle].fromprogs != prinst)\n\t{\n\t\tPF_Warningf(prinst, \"PF_search_getsize: Invalid search handle %i\\n\", handle);\n\t\treturn;\n\t}\n\ts = &pr_searches[handle];\n\n\tG_FLOAT(OFS_RETURN) = s->entries;\n}\n//string\tsearch_getfilename(float handle, float num) = #77;\nvoid QCBUILTIN PF_search_getfilename (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint handle = G_FLOAT(OFS_PARM0);\n\tint num = G_FLOAT(OFS_PARM1);\n\tprvmsearch_t *s;\n\tG_INT(OFS_RETURN) = 0;\n\n\tif (handle < 0 || handle >= numpr_searches || pr_searches[handle].fromprogs != prinst)\n\t{\n\t\tPF_Warningf(prinst, \"PF_search_getfilename: Invalid search handle %i\\n\", handle);\n\t\treturn;\n\t}\n\ts = &pr_searches[handle];\n\n\tif (num < 0 || num >= s->entries)\n\t\treturn;\n\tRETURN_TSTRING(s->entry[num].name);\n}\nvoid QCBUILTIN PF_search_getfilesize (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint handle = G_FLOAT(OFS_PARM0);\n\tint num = G_FLOAT(OFS_PARM1);\n\tprvmsearch_t *s;\n\tG_INT(OFS_RETURN) = 0;\n\n\tif (handle < 0 || handle >= numpr_searches || pr_searches[handle].fromprogs != prinst)\n\t{\n\t\tPF_Warningf(prinst, \"PF_search_getfilesize: Invalid search handle %i\\n\", handle);\n\t\treturn;\n\t}\n\ts = &pr_searches[handle];\n\n\tif (num < 0 || num >= s->entries)\n\t\treturn;\n\tG_FLOAT(OFS_RETURN) = s->entry[num].size;\n}\nvoid QCBUILTIN PF_search_getfilemtime (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint handle = G_FLOAT(OFS_PARM0);\n\tint num = G_FLOAT(OFS_PARM1);\n\tprvmsearch_t *s;\n\tchar timestr[128];\n\tG_INT(OFS_RETURN) = 0;\n\n\tif (handle < 0 || handle >= numpr_searches || pr_searches[handle].fromprogs != prinst)\n\t{\n\t\tPF_Warningf(prinst, \"PF_search_getfilemtime: Invalid search handle %i\\n\", handle);\n\t\treturn;\n\t}\n\ts = &pr_searches[handle];\n\n\tif (num < 0 || num >= s->entries)\n\t\treturn;\n\tif (s->entry[num].mtime != 0)\t//return null/empty if the time isn't set/known.\n\t{\n\t\tstrftime(timestr, sizeof(timestr), \"%Y-%m-%d %H:%M:%S\", localtime(&s->entry[num].mtime));\n\t\tRETURN_TSTRING(timestr);\n\t}\n}\nstatic qboolean PF_search_getloc(flocation_t *loc, prvmsearch_t *s, int num)\n{\n\tconst char *fname = s->entry[num].name;\n\tif (s->searchinfo.handle)\t//we were only searching a single package...\n\t{\t//fail if its a directory, or a (pk3)symlink that we'd have to resolve.\n\t\tloc->search = &s->searchinfo;\n\t\treturn loc->search->handle->FindFile(loc->search->handle, loc, fname, NULL) == FF_FOUND;\n\t}\n\telse if (!s->entry[num].package)\n\t{\n\t\tloc->search = &s->searchinfo;\n\n\t\tQ_snprintfz(loc->rawname, sizeof(loc->rawname), \"%s/%s\", s->searchinfo.purepath, fname);\n\t\treturn true;\n\t}\n\telse\n\t\treturn FS_GetLocationForPackageHandle(loc, s->entry[num].package, fname);\n}\nvoid QCBUILTIN PF_search_getpackagename (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint handle = G_FLOAT(OFS_PARM0);\n\tint num = G_FLOAT(OFS_PARM1);\n\tprvmsearch_t *s;\n\tflocation_t loc;\n\tconst char *pkgname;\n\tG_INT(OFS_RETURN) = 0;\n\n\tif (handle < 0 || handle >= numpr_searches || pr_searches[handle].fromprogs != prinst)\n\t{\n\t\tPF_Warningf(prinst, \"PF_search_getpackagename: Invalid search handle %i\\n\", handle);\n\t\treturn;\n\t}\n\ts = &pr_searches[handle];\n\n\tif (num < 0 || num >= s->entries)\n\t\treturn;\n\tif (PF_search_getloc(&loc, s, num))\n\t{\n\t\tpkgname = FS_WhichPackForLocation(&loc, s->fsflags);\n\t\tif (pkgname)\n\t\t\tRETURN_TSTRING(pkgname);\n\t}\n}\nvoid QCBUILTIN PF_search_fopen (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint handle = G_FLOAT(OFS_PARM0);\n\tint num = G_FLOAT(OFS_PARM1);\n\tprvmsearch_t *s;\n\tflocation_t loc;\n\tG_FLOAT(OFS_RETURN) = -1;\n\n\tif (handle < 0 || handle >= numpr_searches || pr_searches[handle].fromprogs != prinst)\n\t{\n\t\tPF_Warningf(prinst, \"PF_search_getpackagename: Invalid search handle %i\\n\", handle);\n\t\treturn;\n\t}\n\ts = &pr_searches[handle];\n\n\tif (num < 0 || num >= s->entries)\n\t\treturn;\n\tif (PF_search_getloc(&loc, s, num))\n\t\tG_FLOAT(OFS_RETURN) = PF_fopen_search (prinst, s->entry[num].name, &loc);\n}\n\n//closes filesystem type stuff for when a progs has stopped needing it.\nvoid PR_fclose_progs (pubprogfuncs_t *prinst)\n{\n\tPF_fcloseall(prinst);\n\tsearch_close_progs(prinst, true);\n\tPF_Hash_DestroyAll(prinst);\n}\n\n//File access\n////////////////////////////////////////////////////\n//reflection\n\n//float\tisfunction(string function_name)\nvoid QCBUILTIN PF_isfunction (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char\t*name = PR_GetStringOfs(prinst, OFS_PARM0);\n\tG_FLOAT(OFS_RETURN) = !!PR_FindFunction(prinst, name, PR_ANY);\n}\n\n//void\tcallfunction(...)\nvoid QCBUILTIN PF_callfunction (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char\t*name;\n\tfunc_t f;\n\tif (prinst->callargc < 1)\n\t\tPR_BIError(prinst, \"callfunction needs at least one argument\\n\");\n\tname = PR_GetStringOfs(prinst, OFS_PARM0+(prinst->callargc-1)*3);\n\tprinst->callargc -= 1;\n\tf = PR_FindFunction(prinst, name, PR_ANY);\n\tif (f)\n\t\tPR_ExecuteProgram(prinst, f);\n}\n\n//void\tloadfromfile(string file)\nvoid QCBUILTIN PF_loadfromfile (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char\t*filename = PR_GetStringOfs(prinst, OFS_PARM0);\n\tconst char *file = COM_LoadTempFile(filename, 0, NULL);\n\n\tsize_t size;\n\n\tif (!file)\n\t{\n\t\tG_FLOAT(OFS_RETURN) = -1;\n\t\treturn;\n\t}\n\n\twhile(prinst->restoreent(prinst, file, &size, NULL))\n\t{\n\t\tfile += size;\n\t}\n\n\tG_FLOAT(OFS_RETURN) = 0;\n}\n\nvoid QCBUILTIN PF_writetofile(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint fnum = G_FLOAT(OFS_PARM0)-FIRST_QC_FILE_INDEX;\n\tvoid *ed = G_EDICT(prinst, OFS_PARM1);\n\n\tchar buffer[65536];\n\tchar *entstr;\n\tsize_t buflen;\n\n\tbuflen = 0;\n\tentstr = prinst->saveent(prinst, buffer, &buflen, sizeof(buffer), ed);\t//will save just one entities vars\n\tif (entstr)\n\t{\n\t\tPF_fwrite_internal (prinst, fnum, entstr, buflen);\n\t}\n}\n\n//read (multiple) {entity data} into new entities, with no real indication of how much was read.\nvoid QCBUILTIN PF_loadfromdata (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char\t*file = PR_GetStringOfs(prinst, OFS_PARM0);\n\n\tsize_t size;\n\n\tif (!*file)\n\t{\n\t\tG_FLOAT(OFS_RETURN) = -1;\n\t\treturn;\n\t}\n\n\twhile(prinst->restoreent(prinst, file, &size, NULL))\n\t{\n\t\tfile += size;\n\t}\n\n\tG_FLOAT(OFS_RETURN) = 0;\n}\n\nvoid QCBUILTIN PF_generateentitydata(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tvoid *ed = G_EDICT(prinst, OFS_PARM0);\n\n\tchar buffer[65536];\n\tchar *entstr;\n\tsize_t buflen;\n\n\tbuflen = 0;\n\tentstr = prinst->saveent(prinst, buffer, &buflen, sizeof(buffer), ed);\n\n\tif (entstr)\n\t\tRETURN_TSTRING(entstr);\n\telse\n\t\tG_INT(OFS_RETURN) = 0;\n}\n\nvoid QCBUILTIN PF_parseentitydata(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tvoid\t*ed = G_EDICT(prinst, OFS_PARM0);\n\tconst char\t*file = PR_GetStringOfs(prinst, OFS_PARM1);\n\tint offset = (prinst->callargc>=3)?G_FLOAT(OFS_PARM2):0;\n\n\tsize_t size;\n\n\tif (offset < 0)\n\t\toffset = 0;\n\telse if (offset)\n\t{\n\t\tint boffset = offset;\n\t\tif (VMUTF8)\n\t\t\tboffset = unicode_byteofsfromcharofs(file, offset, VMUTF8MARKUP);\n\t\telse\n\t\t{\n\t\t\tint l = strlen(file);\n\t\t\tif (boffset > l)\n\t\t\t\tboffset = l;\n\t\t}\n\t\tfile += boffset;\n\t}\n\n\tif (!*file)\n\t{\n\t\tG_FLOAT(OFS_RETURN) = 0;\n\t\treturn;\n\t}\n\n\tif (!prinst->restoreent(prinst, file, &size, ed))\n\t{\n\t\tif (prinst->callargc<3)\n\t\t\tPF_Warningf(prinst, \"parseentitydata: missing opening data\\n\");\n\t\tG_FLOAT(OFS_RETURN) = 0;\n\t\treturn;\n\t}\n\telse if (prinst->callargc<3)\n\t{\n\t\tfile += size;\n\t\twhile(*file < ' ' && *file)\n\t\t\tfile++;\n\t\tif (*file)\n\t\t\tPF_Warningf(prinst, \"parseentitydata: too much data\\n\");\n\t}\n\n\tif (VMUTF8)\n\t\tsize = unicode_charofsfrombyteofs(file, size, VMUTF8MARKUP);\n\n\tG_FLOAT(OFS_RETURN) = offset + size;\n}\n//reflection\n////////////////////////////////////////////////////\n//Entities\n\nvoid QCBUILTIN PF_WasFreed (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\twedict_t\t*ent;\n\tent = G_WEDICT(prinst, OFS_PARM0);\n\tif (!ent)\n\t\tPR_BIError(prinst, \"PF_WasFreed: invalid entity\");\n\tG_FLOAT(OFS_RETURN) = ED_ISFREE(ent);\n}\n\nvoid QCBUILTIN PF_num_for_edict (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\twedict_t\t*ent;\n\tent = G_WEDICT(prinst, OFS_PARM0);\n\tG_FLOAT(OFS_RETURN) = ent->entnum;\n}\n\nvoid QCBUILTIN PF_edict_for_num(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tworld_t *w = prinst->parms->user;\n\tunsigned int num = G_FLOAT(OFS_PARM0);\n\tif (num >= w->num_edicts)\n\t\tRETURN_EDICT(prinst, w->edicts);\n\telse\n\t{\n\t\tG_INT(OFS_RETURN) = num;\t//just directly store it. if its not spawned yet we'll need to catch that elsewhere anyway.\n\t\tif (!G_WEDICT(prinst, OFS_RETURN))\n\t\t\tRETURN_EDICT(prinst, w->edicts);\t//hoi! it wasn't valid!\n\t}\n}\n\n/*\n=================\nPF_findradius\n\nReturns a chain of entities that have origins within a spherical area\n\nfindradius (origin, radius)\n=================\n*/\nvoid QCBUILTIN PF_findradius (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tworld_t *w = prinst->parms->user;\n\textern cvar_t sv_gameplayfix_blowupfallenzombies;\n\textern cvar_t dpcompat_findradiusarealinks;\n\twedict_t\t*ent, *chain;\n\tfloat\trad;\n\tpvec_t\t*org;\n\tpvec3_t\teorg;\n\tint\t\ti, j;\n\tint f;\n\n\tchain = w->edicts;\n\n\torg = G_VECTOR(OFS_PARM0);\n\trad = G_FLOAT(OFS_PARM1);\n\n\tif (prinst->callargc > 2)\n\t\tf = G_INT(OFS_PARM2)+prinst->fieldadjust;\n\telse\n\t\tf = &((comentvars_t*)NULL)->chain - (pint_t*)NULL;\n\n\tif (dpcompat_findradiusarealinks.ival)\n\t{\n\t\tstatic wedict_t *nearent[32768];\n\t\tvec3_t mins, maxs;\n\t\tint numents;\n\n\t\tmins[0] = org[0] - rad;\n\t\tmins[1] = org[1] - rad;\n\t\tmins[2] = org[2] - rad;\n\t\tmaxs[0] = org[0] + rad;\n\t\tmaxs[1] = org[1] + rad;\n\t\tmaxs[2] = org[2] + rad;\n\n\t\tnuments = World_AreaEdicts(w, mins, maxs, nearent, countof(nearent), AREA_ALL);\n\t\trad = rad*rad;\n\t\tfor (i=0 ; i<numents ; i++)\n\t\t{\n\t\t\tent = nearent[i];\n\t\t\tif (ent->v->solid == SOLID_NOT && (!((pint_t)ent->v->flags & FL_FINDABLE_NONSOLID)) && !sv_gameplayfix_blowupfallenzombies.ival)\n\t\t\t\tcontinue;\n\t\t\tif (sv_gameplayfix_findradiusdistancetobox.ival)\n\t\t\t{\n\t\t\t\tfor (j=0 ; j<3 ; j++)\n\t\t\t\t{\n\t\t\t\t\teorg[j] = org[j] - ent->v->origin[j];\n\t\t\t\t\teorg[j] -= bound(ent->v->mins[j], eorg[j], ent->v->maxs[j]);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tfor (j=0 ; j<3 ; j++)\n\t\t\t\t\teorg[j] = org[j] - (ent->v->origin[j] + (ent->v->mins[j] + ent->v->maxs[j])*0.5);\n\t\t\t}\n\t\t\tif (DotProduct(eorg,eorg) > rad)\n\t\t\t\tcontinue;\n\n\t\t\t((pint_t*)ent->v)[f] = EDICT_TO_PROG(prinst, chain);\n\t\t\tchain = ent;\n\t\t}\n\t}\n\telse\n\t{\n\t\trad = rad*rad;\n\t\tfor (i=1 ; i<w->num_edicts ; i++)\n\t\t{\n\t\t\tent = WEDICT_NUM_PB(prinst, i);\n\t\t\tif (ED_ISFREE(ent))\n\t\t\t\tcontinue;\n\t\t\tif (ent->v->solid == SOLID_NOT && (!((pint_t)ent->v->flags & FL_FINDABLE_NONSOLID)) && !sv_gameplayfix_blowupfallenzombies.value)\n\t\t\t\tcontinue;\n\t\t\tif (sv_gameplayfix_findradiusdistancetobox.ival)\n\t\t\t{\n\t\t\t\tfor (j=0 ; j<3 ; j++)\n\t\t\t\t{\n\t\t\t\t\teorg[j] = org[j] - ent->v->origin[j];\n\t\t\t\t\teorg[j] -= bound(ent->v->mins[j], eorg[j], ent->v->maxs[j]);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tfor (j=0 ; j<3 ; j++)\n\t\t\t\t\teorg[j] = org[j] - (ent->v->origin[j] + (ent->v->mins[j] + ent->v->maxs[j])*0.5);\n\t\t\t}\n\t\t\tif (DotProduct(eorg,eorg) > rad)\n\t\t\t\tcontinue;\n\n\t\t\t((pint_t*)ent->v)[f] = EDICT_TO_PROG(prinst, chain);\n\t\t\tchain = ent;\n\t\t}\n\t}\n\n\tRETURN_EDICT(prinst, chain);\n}\n\n#ifdef QCGC\nvoid QCBUILTIN PF_findradius_list (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tworld_t *w = prinst->parms->user;\n\twedict_t\t*ent;\n\tpvec_t\trad;\n\tfloat\t*org;\n\tpvec3_t\teorg;\n\tint\t\ti, j;\n\twedict_t **nearent;\n\tvec3_t mins, maxs;\n\tint\t\tnuments, count = 0;\n\tint *temp;\n\n\torg = G_VECTOR(OFS_PARM0);\n\trad = G_FLOAT(OFS_PARM1);\n\n\t//find out how many ents there are within the box specified.\n\tmins[0] = org[0] - rad;\n\tmins[1] = org[1] - rad;\n\tmins[2] = org[2] - rad;\n\tmaxs[0] = org[0] + rad;\n\tmaxs[1] = org[1] + rad;\n\tmaxs[2] = org[2] + rad;\n\tnearent = alloca(sizeof(wedict_t)*w->num_edicts);\t//guess at a max\n\tnuments = World_AreaEdicts(w, mins, maxs, nearent, w->num_edicts, AREA_ALL);\n\n\t//allocate space for a result (overestimating slightly still)\n\tG_INT(OFS_RETURN) = prinst->AllocTempString(prinst, (char**)&temp, (1+numents)*sizeof(*temp));\n\n\trad = rad*rad;\n\tfor (i=0 ; i<numents ; i++)\n\t{\n\t\tent = nearent[i];\n\t\tif (ent->v->solid == SOLID_NOT && !((pint_t)ent->v->flags & FL_FINDABLE_NONSOLID))\n\t\t\tcontinue;\n\t\tfor (j=0 ; j<3 ; j++)\n\t\t{\n\t\t\teorg[j] = org[j] - ent->v->origin[j];\n\t\t\teorg[j] -= bound(ent->v->mins[j], org[j], ent->v->maxs[j]);\n\t\t}\n\t\tif (DotProduct(eorg,eorg) > rad)\n\t\t\tcontinue;\n\n\t\ttemp[count++] = EDICT_TO_PROG(prinst, ent);\n\t}\n\n\ttemp[count] = 0;\n\n\tG_INT(OFS_PARM2) = count;\n}\n#endif\n\n//entity nextent(entity)\nvoid QCBUILTIN PF_nextent (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint\t\ti;\n\twedict_t\t*ent;\n\n\ti = G_EDICTNUM(prinst, OFS_PARM0);\n\twhile (1)\n\t{\n\t\ti++;\n\t\tif (i == *prinst->parms->num_edicts)\n\t\t{\n\t\t\tRETURN_EDICT(prinst, *prinst->parms->edicts);\n\t\t\treturn;\n\t\t}\n\t\tent = WEDICT_NUM_PB(prinst, i);\n\t\tif (!ED_ISFREE(ent))\n\t\t{\n\t\t\tRETURN_EDICT(prinst, ent);\n\t\t\treturn;\n\t\t}\n\t}\n}\n\n//entity() spawn\nvoid QCBUILTIN PF_Spawn (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tstruct edict_s\t*ed;\n\ted = ED_Alloc(prinst, false, 0);\n\tpr_globals = PR_globals(prinst, PR_CURRENT);\n\tRETURN_EDICT(prinst, ed);\n}\n\nvoid QCBUILTIN PF_spawn_object (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint size = G_INT(OFS_PARM0);\n\tstruct edict_s\t*ed;\n\tif (prinst->callargc > 1)\n\t{\n\t\tint idx = G_INT(OFS_PARM1);\n\t\ted = EDICT_NUM_UB(prinst, idx);\n\t\tG_FLOAT(OFS_PARM2) = (ed && ed->ereftype == ER_ENTITY);\n\t\ted = prinst->EntAllocIndex(prinst, idx, true, size);\n\t}\n\telse\n\t\ted = ED_Alloc(prinst, true, size);\n\tpr_globals = PR_globals(prinst, PR_CURRENT);\n\tRETURN_EDICT(prinst, ed);\n}\n\nvoid QCBUILTIN PF_respawnedict (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint idx = G_FLOAT(OFS_PARM0);\n\tstruct edict_s\t*ed = EDICT_NUM_UB(prinst, idx);\n\tG_FLOAT(OFS_PARM1) = (ed && ed->ereftype == ER_ENTITY);\n\ted = prinst->EntAllocIndex(prinst, idx, false, 0);\n\tpr_globals = PR_globals(prinst, PR_CURRENT);\n\tRETURN_EDICT(prinst, ed);\n}\n\n//EXTENSION: DP_QC_COPYENTITY\n\n//void(entity from, entity to) copyentity = #400\n//copies data from one entity to another\nvoid QCBUILTIN PF_copyentity (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tworld_t *w = prinst->parms->user;\n\twedict_t *in, *out;\n\n\tin = G_WEDICT(prinst, OFS_PARM0);\n\tif (prinst->callargc <= 1)\n\t\tout = (wedict_t*)ED_Alloc(prinst, false, 0);\n\telse\n\t\tout = G_WEDICT(prinst, OFS_PARM1);\n\n\tif (ED_ISFREE(in))\n\t\tPR_BIError(prinst, \"PF_copyentity: source is free\");\n\tif (!out || ED_ISFREE(out))\n\t\tPR_BIError(prinst, \"PF_copyentity: destination is free\");\n\tif (out->readonly)\n\t\tPR_BIError(prinst, \"PF_copyentity: destination is read-only\");\n\tif (out->fieldsize != in->fieldsize)\n\t\tPR_BIError(prinst, \"PF_copyentity: different object types\");\n\n\tmemcpy(out->v, in->v, out->fieldsize);\n\tWorld_LinkEdict(w, out, false);\n\n\tRETURN_EDICT(prinst, out);\n}\n\nvoid QCBUILTIN PF_entityprotection (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\twedict_t *e = G_WEDICT(prinst, OFS_PARM0);\n\tint prot = G_FLOAT(OFS_PARM1);\n\n\tif (ED_ISFREE(e))\n\t\tPR_BIError(prinst, \"PF_entityprotection: entity is free\");\n\n\tG_FLOAT(OFS_RETURN) = prot;\n\tif (prot < 0 || prot > 1)\n\t\treturn;\n\te->readonly = prot;\n}\n\n//Entities\n////////////////////////////////////////////////////\n//String functions\n\n//PF_dprint\nvoid QCBUILTIN PF_dprint (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tCon_DPrintf (\"%s\",PF_VarString(prinst, 0, pr_globals));\n}\n\n//PF_print\nvoid QCBUILTIN PF_print (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tCon_Printf (\"%s\",PF_VarString(prinst, 0, pr_globals));\n}\n\n//FTE_STRINGS\n//C style strncasecmp (compare first n characters - case insensitive)\n//C style strcasecmp (case insensitive string compare)\nvoid QCBUILTIN PF_strncasecmp (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char *a = PR_GetStringOfs(prinst, OFS_PARM0);\n\tconst char *b = PR_GetStringOfs(prinst, OFS_PARM1);\n\n\tif (prinst->callargc > 2)\n\t{\n\t\tint len = G_FLOAT(OFS_PARM2);\n\t\tint aofs = prinst->callargc>3?G_FLOAT(OFS_PARM3):0;\n\t\tint bofs = prinst->callargc>4?G_FLOAT(OFS_PARM4):0;\n\n\t\tif (VMUTF8)\n\t\t{\n\t\t\taofs = aofs?unicode_byteofsfromcharofs(a, aofs, VMUTF8MARKUP):0;\n\t\t\tbofs = bofs?unicode_byteofsfromcharofs(b, bofs, VMUTF8MARKUP):0;\n\t\t\tlen = max(unicode_byteofsfromcharofs(a+aofs, len, VMUTF8MARKUP), unicode_byteofsfromcharofs(b+bofs, len, VMUTF8MARKUP));\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (aofs < 0 || (aofs && aofs > strlen(a)))\n\t\t\t\taofs = strlen(a);\n\t\t\tif (bofs < 0 || (bofs && bofs > strlen(b)))\n\t\t\t\tbofs = strlen(b);\n\t\t}\n\n\t\tG_FLOAT(OFS_RETURN) = Q_strncasecmp(a+aofs, b+bofs, len);\n\t}\n\telse\n\t\tG_FLOAT(OFS_RETURN) = Q_strcasecmp(a, b);\n}\n\n//FTE_STRINGS\n//C style strncmp (compare first n characters - case sensitive. Note that there is no strcmp provided)\nvoid QCBUILTIN PF_strncmp (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char *a = PR_GetStringOfs(prinst, OFS_PARM0);\n\tconst char *b = PR_GetStringOfs(prinst, OFS_PARM1);\n\n\tif (prinst->callargc > 2)\n\t{\n\t\tint len = G_FLOAT(OFS_PARM2);\n\t\tint aofs = prinst->callargc>3?G_FLOAT(OFS_PARM3):0;\n\t\tint bofs = prinst->callargc>4?G_FLOAT(OFS_PARM4):0;\n\n\t\tif (VMUTF8)\n\t\t{\n\t\t\taofs = aofs?unicode_byteofsfromcharofs(a, aofs, VMUTF8MARKUP):0;\n\t\t\tbofs = bofs?unicode_byteofsfromcharofs(b, bofs, VMUTF8MARKUP):0;\n\t\t\tlen = max(unicode_byteofsfromcharofs(a+aofs, len, VMUTF8MARKUP), unicode_byteofsfromcharofs(b+bofs, len, VMUTF8MARKUP));\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (aofs < 0 || (aofs && aofs > strlen(a)))\n\t\t\t\taofs = strlen(a);\n\t\t\tif (bofs < 0 || (bofs && bofs > strlen(b)))\n\t\t\t\tbofs = strlen(b);\n\t\t}\n\n\t\tG_FLOAT(OFS_RETURN) = Q_strncmp(a + aofs, b, len);\n\t}\n\telse\n\t\tG_FLOAT(OFS_RETURN) = Q_strcmp(a, b);\n}\n\n//uses qw style \\key\\value strings\nvoid QCBUILTIN PF_infoget (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char *info = PR_GetStringOfs(prinst, OFS_PARM0);\n\tconst char *key = PR_GetStringOfs(prinst, OFS_PARM1);\n\n\tkey = Info_ValueForKey(info, key);\n\n\tRETURN_TSTRING(key);\n}\n\n//uses qw style \\key\\value strings\nvoid QCBUILTIN PF_infoadd (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char *info = PR_GetStringOfs(prinst, OFS_PARM0);\n\tconst char *key = PR_GetStringOfs(prinst, OFS_PARM1);\n\tconst char *value = PF_VarString(prinst, 2, pr_globals);\n\tchar temp[8192];\n\n\tQ_strncpyz(temp, info, MAXTEMPBUFFERLEN);\n\n\tInfo_SetValueForStarKey(temp, key, value, MAXTEMPBUFFERLEN);\n\n\tRETURN_TSTRING(temp);\n}\n\n//string(float pad, string str1, ...) strpad\nvoid QCBUILTIN PF_strpad (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tchar destbuf[4096];\n\tchar *dest = destbuf;\n\tint pad = G_FLOAT(OFS_PARM0);\n\tconst char *src = PF_VarString(prinst, 1, pr_globals);\n\n\t//UTF-8-FIXME: pad is chars not bytes...\n\n\tif (pad < 0)\n\t{\t//pad left\n\t\tpad = -pad - strlen(src);\n\t\tif (pad>=sizeof(destbuf))\n\t\t\tpad = sizeof(destbuf)-1;\n\t\tif (pad < 0)\n\t\t\tpad = 0;\n\n\t\tQ_strncpyz(dest+pad, src, sizeof(destbuf)-pad);\n\t\twhile(pad)\n\t\t{\n\t\t\tdest[--pad] = ' ';\n\t\t}\n\t}\n\telse\n\t{\t//pad right\n\t\tif (pad>=sizeof(destbuf))\n\t\t\tpad = sizeof(destbuf)-1;\n\t\tpad -= strlen(src);\n\t\tif (pad < 0)\n\t\t\tpad = 0;\n\n\t\tQ_strncpyz(dest, src, sizeof(destbuf));\n\t\tdest+=strlen(dest);\n\n\t\twhile(pad-->0)\n\t\t\t*dest++ = ' ';\n\t\t*dest = '\\0';\n\t}\n\n\tRETURN_TSTRING(destbuf);\n}\n\nvoid QCBUILTIN PF_strtrim (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char *str = PR_GetStringOfs(prinst, OFS_PARM0);\n\tconst char *end;\n\tchar *news;\n\t\n\t//figure out the new start\n\twhile (*str == ' ' || *str == '\\t' || *str == '\\n' || *str == '\\r')\n\t\tstr++;\n\n\t//figure out the new end.\n\tend = str + strlen(str);\n\twhile(end > str && (end[-1] == ' ' || end[-1] == '\\t' || end[-1] == '\\n' || end[-1] == '\\r'))\n\t\tend--;\n\n\t//copy that substring into a tempstring.\n\t((int *)pr_globals)[OFS_RETURN] = prinst->AllocTempString(prinst, &news, end - str + 1);\n\tmemcpy(news, str, end-str);\n\tnews[end-str] = 0;\n}\n\n//part of PF_strconv\nstatic int chrconv_number(int i, int base, int conv)\n{\n\ti -= base;\n\tswitch (conv)\n\t{\n\tdefault:\n\tcase 5:\n\tcase 6:\n\tcase 0:\n\t\tbreak;\n\tcase 1:\n\t\tbase = '0';\n\t\tbreak;\n\tcase 2:\n\t\tbase = '0'+128;\n\t\tbreak;\n\tcase 3:\n\t\tbase = '0'-30;\n\t\tbreak;\n\tcase 4:\n\t\tbase = '0'+128-30;\n\t\tbreak;\n\t}\n\treturn i + base;\n}\n//part of PF_strconv\nstatic int chrconv_punct(int i, int base, int conv)\n{\n\ti -= base;\n\tswitch (conv)\n\t{\n\tdefault:\n\tcase 0:\n\t\tbreak;\n\tcase 1:\n\t\tbase = 0;\n\t\tbreak;\n\tcase 2:\n\t\tbase = 128;\n\t\tbreak;\n\t}\n\treturn i + base;\n}\n//part of PF_strconv\nstatic int chrchar_alpha(int i, int basec, int baset, int convc, int convt, int charnum)\n{\n\t//convert case and colour seperatly...\n\n\ti -= baset + basec;\n\tswitch (convt)\n\t{\n\tdefault:\n\tcase 0:\n\t\tbreak;\n\tcase 1:\n\t\tbaset = 0;\n\t\tbreak;\n\tcase 2:\n\t\tbaset = 128;\n\t\tbreak;\n\n\tcase 5:\n\tcase 6:\n\t\tbaset = 128*((charnum&1) == (convt-5));\n\t\tbreak;\n\t}\n\n\tswitch (convc)\n\t{\n\tdefault:\n\tcase 0:\n\t\tbreak;\n\tcase 1:\n\t\tbasec = 'a';\n\t\tbreak;\n\tcase 2:\n\t\tbasec = 'A';\n\t\tbreak;\n\t}\n\treturn i + basec + baset;\n}\n//FTE_STRINGS\n//bulk convert a string. change case or colouring.\nvoid QCBUILTIN PF_strconv (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint ccase = G_FLOAT(OFS_PARM0);\t\t//0 same, 1 lower, 2 upper\n\tint redalpha = G_FLOAT(OFS_PARM1);\t//0 same, 1 white, 2 red,  5 alternate, 6 alternate-alternate\n\tint rednum = G_FLOAT(OFS_PARM2);\t//0 same, 1 white, 2 red, 3 redspecial, 4 whitespecial, 5 alternate, 6 alternate-alternate\n\tconst unsigned char *string = PF_VarString(prinst, 3, pr_globals);\n\tint len = strlen(string);\n\tint i;\n\tunsigned char resbuf[8192];\n\tunsigned char *result = resbuf;\n\n\t//UTF-8-FIXME: cope with utf+^U etc\n\n\tif (len >= MAXTEMPBUFFERLEN)\n\t\tlen = MAXTEMPBUFFERLEN-1;\n\n\tfor (i = 0; i < len; i++, string++, result++)\t//should this be done backwards?\n\t{\n\t\tif (*string >= '0' && *string <= '9')\t//normal numbers...\n\t\t\t*result = chrconv_number(*string, '0', rednum);\n\t\telse if (*string >= '0'+128 && *string <= '9'+128)\n\t\t\t*result = chrconv_number(*string, '0'+128, rednum);\n\t\telse if (*string >= '0'+128-30 && *string <= '9'+128-30)\n\t\t\t*result = chrconv_number(*string, '0'+128-30, rednum);\n\t\telse if (*string >= '0'-30 && *string <= '9'-30)\n\t\t\t*result = chrconv_number(*string, '0'-30, rednum);\n\n\t\telse if (*string >= 'a' && *string <= 'z')\t//normal numbers...\n\t\t\t*result = chrchar_alpha(*string, 'a', 0, ccase, redalpha, i);\n\t\telse if (*string >= 'A' && *string <= 'Z')\t//normal numbers...\n\t\t\t*result = chrchar_alpha(*string, 'A', 0, ccase, redalpha, i);\n\t\telse if (*string >= 'a'+128 && *string <= 'z'+128)\t//normal numbers...\n\t\t\t*result = chrchar_alpha(*string, 'a', 128, ccase, redalpha, i);\n\t\telse if (*string >= 'A'+128 && *string <= 'Z'+128)\t//normal numbers...\n\t\t\t*result = chrchar_alpha(*string, 'A', 128, ccase, redalpha, i);\n\n\t\telse if ((*string & 127) < 16 || !redalpha)\t//special chars..\n\t\t\t*result = *string;\n\t\telse if (*string < 128)\n\t\t\t*result = chrconv_punct(*string, 0, redalpha);\n\t\telse\n\t\t\t*result = chrconv_punct(*string, 128, redalpha);\n\t}\n\t*result = '\\0';\n\n\tRETURN_TSTRING(((char*)resbuf));\n}\n\n//FTE_STRINGS\n//returns a string containing one character per parameter (up to the qc max params of 8).\nvoid QCBUILTIN PF_chr2str (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint ch;\n\tint i;\n\tchar string[128], *s = string;\n\tfor (i = 0; i < prinst->callargc; i++)\n\t{\n\t\tch = G_FLOAT(OFS_PARM0 + i*3);\n\t\tif (VMUTF8 || ch > 0xff)\n\t\t\ts += unicode_encode(s, ch, (string+sizeof(string)-1)-s, VMUTF8MARKUP||(ch>0xff));\n\t\telse\n\t\t\t*s++ = G_FLOAT(OFS_PARM0 + i*3);\n\t}\n\t*s++ = '\\0';\n\tRETURN_TSTRING(string);\n}\n\n//FTE_STRINGS\n//returns character at position X\nvoid QCBUILTIN PF_str2chr (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint err;\n\tconst char *next;\n\tconst char *instr = PR_GetStringOfs(prinst, OFS_PARM0);\n\tint ofs = (prinst->callargc>1)?G_FLOAT(OFS_PARM1):0;\n\n\tif (VMUTF8)\n\t{\n\t\tif (ofs < 0)\n\t\t\tofs = unicode_charcount(instr, 1<<30, VMUTF8MARKUP)+ofs;\n\t\tofs = unicode_byteofsfromcharofs(instr, ofs, VMUTF8MARKUP);\n\t}\n\telse\n\t{\n\t\tif (ofs < 0)\n\t\t\tofs = strlen(instr)+ofs;\n\t}\n\n\tif (ofs && (ofs < 0 || ofs > strlen(instr)))\n\t\tG_FLOAT(OFS_RETURN) = '\\0';\n\telse\n\t{\n\t\tif (VMUTF8)\n\t\t\tG_FLOAT(OFS_RETURN) = unicode_decode(&err, instr+ofs, &next, VMUTF8MARKUP);\n\t\telse\n\t\t\tG_FLOAT(OFS_RETURN) = (unsigned char)instr[ofs];\n\t}\n}\n\n//FTE_STRINGS\n//strstr, without generating a new string. Use in conjunction with FRIK_FILE's substring for more similar strstr.\nvoid QCBUILTIN PF_strstrofs (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char *instr = PR_GetStringOfs(prinst, OFS_PARM0);\n\tconst char *match = PR_GetStringOfs(prinst, OFS_PARM1);\n\n\tint firstofs = (prinst->callargc>2)?G_FLOAT(OFS_PARM2):0;\n\n\tif (VMUTF8)\n\t\tfirstofs = unicode_byteofsfromcharofs(instr, firstofs, VMUTF8MARKUP);\n\n\tif (firstofs && (firstofs < 0 || firstofs > strlen(instr)))\n\t{\n\t\tG_FLOAT(OFS_RETURN) = -1;\n\t\treturn;\n\t}\n\n\tmatch = strstr(instr+firstofs, match);\n\tif (!match)\n\t\tG_FLOAT(OFS_RETURN) = -1;\n\telse\n\t\tG_FLOAT(OFS_RETURN) = VMUTF8?unicode_charofsfrombyteofs(instr, match-instr, VMUTF8MARKUP):(match - instr);\n}\n\n//float(string input) stof\nvoid QCBUILTIN PF_stof (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char\t*s;\n\n\ts = PR_GetStringOfs(prinst, OFS_PARM0);\n\n\tG_FLOAT(OFS_RETURN) = atof(s);\n}\n\n//tstring(float input) ftos\nvoid QCBUILTIN PF_ftos (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tfloat\tv;\n\tchar pr_string_temp[64];\n\tv = G_FLOAT(OFS_PARM0);\n\n\tif (v == (int)v)\n\t\tsprintf (pr_string_temp, \"%d\",(int)v);\n\telse if (pr_brokenfloatconvert.value)\n\t\tsprintf (pr_string_temp, \"%5.1f\",v);\n\telse\n\t\tQ_ftoa (pr_string_temp, v);\n\tRETURN_TSTRING(pr_string_temp);\n}\n\nvoid QCBUILTIN PF_ftou (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tG_UINT(OFS_RETURN) = G_FLOAT(OFS_PARM0);\n}\nvoid QCBUILTIN PF_utof (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tif (prinst->callargc > 1)\n\t{\n\t\tunsigned int value = G_UINT(OFS_PARM0);\n\t\tunsigned int shift = G_FLOAT(OFS_PARM1);\n\t\tunsigned int count = G_FLOAT(OFS_PARM2);\n\t\tvalue >>= shift;\n\t\tif (count != 32)\n\t\t\tvalue &= ((1u<<count)-1u);\n\t\tG_FLOAT(OFS_RETURN) = value;\n\t}\n\telse\n\t\tG_FLOAT(OFS_RETURN) = G_UINT(OFS_PARM0);\n}\n\nvoid QCBUILTIN PF_ftoi (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tG_INT(OFS_RETURN) = G_FLOAT(OFS_PARM0);\n}\nvoid QCBUILTIN PF_itof (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tif (prinst->callargc > 1)\n\t{\n\t\tunsigned int value = G_INT(OFS_PARM0);\n\t\tunsigned int shift = G_FLOAT(OFS_PARM1);\n\t\tunsigned int count = G_FLOAT(OFS_PARM2);\n\t\tvalue >>= shift;\n\t\tif (count != 32)\n\t\t\tvalue &= ((1u<<count)-1u);\n\t\tG_FLOAT(OFS_RETURN) = value;\n\t}\n\telse\n\t\tG_FLOAT(OFS_RETURN) = G_INT(OFS_PARM0);\n}\n\n//tstring(integer input) itos\nvoid QCBUILTIN PF_itos (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint\tv;\n\tchar pr_string_temp[64];\n\tv = G_INT(OFS_PARM0);\n\n\tsprintf (pr_string_temp, \"%d\",v);\n\tRETURN_TSTRING(pr_string_temp);\n}\n\n//int(string input) stoi\nvoid QCBUILTIN PF_stoi (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char *input = PR_GetStringOfs(prinst, OFS_PARM0);\n\n\tG_INT(OFS_RETURN) = atoi(input);\n}\n\n//tstring(integer input) htos\nvoid QCBUILTIN PF_htos (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tunsigned int\tv;\n\tchar pr_string_temp[64];\n\tv = G_INT(OFS_PARM0);\n\n\tsprintf (pr_string_temp, \"%08x\",v);\n\tRETURN_TSTRING(pr_string_temp);\n}\n\n//int(string input) stoh\nvoid QCBUILTIN PF_stoh (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char *input = PR_GetStringOfs(prinst, OFS_PARM0);\n\n\tG_INT(OFS_RETURN) = strtoul(input, NULL, 16);\n}\n\n//vector(string s) stov = #117\n//returns vector value from a string\nvoid QCBUILTIN PF_stov (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint i;\n\tconst char *s;\n\tfloat *out;\n\n\ts = PF_VarString(prinst, 0, pr_globals);\n\tout = G_VECTOR(OFS_RETURN);\n\tout[0] = out[1] = out[2] = 0;\n\n\tif (*s == '\\'')\n\t\ts++;\n\n\tfor (i = 0; i < 3; i++)\n\t{\n\t\twhile (*s == ' ' || *s == '\\t')\n\t\t\ts++;\n\t\tout[i] = atof (s);\n\t\tif (!out[i] && *s != '-' && *s != '+' && (*s < '0' || *s > '9'))\n\t\t\tbreak; // not a number\n\t\twhile (*s && *s != ' ' && *s !='\\t' && *s != '\\'')\n\t\t\ts++;\n\t\tif (*s == '\\'')\n\t\t\tbreak;\n\t}\n}\n\n//tstring(vector input) vtos\nvoid QCBUILTIN PF_vtos (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tchar pr_string_temp[64];\n\t//sprintf (pr_string_temp, \"'%5.1f %5.1f %5.1f'\", G_VECTOR(OFS_PARM0)[0], G_VECTOR(OFS_PARM0)[1], G_VECTOR(OFS_PARM0)[2]);\n\tsprintf (pr_string_temp, \"'%f %f %f'\", G_VECTOR(OFS_PARM0)[0], G_VECTOR(OFS_PARM0)[1], G_VECTOR(OFS_PARM0)[2]);\n\tRETURN_TSTRING(pr_string_temp);\n}\n\nvoid QCBUILTIN PF_Logarithm (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\t//log2(v) = ln(v)/ln(2)\n\tdouble r;\n\tr = log(G_FLOAT(OFS_PARM0));\n\tif (prinst->callargc > 1)\n\t\tr /= log(G_FLOAT(OFS_PARM1));\n\tG_FLOAT(OFS_RETURN) = r;\n}\n\n\nvoid QCBUILTIN PF_strunzone(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n#ifdef QCGC\n\t//gc frees everything for us.\n\t//FIXME: explicitly free it anyway, to save running the gc quite so often.\n#else\n\tprinst->AddressableFree(prinst, prinst->stringtable + G_INT(OFS_PARM0));\n#endif\n}\n\nvoid QCBUILTIN PF_strzone(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\t//frik_file\n{\n#ifdef QCGC\n\t//just allocate a tempstring instead, because we can.\n\tPF_strcat(prinst, pr_globals);\n#else\n\tchar *buf;\n\tint len = 0;\n\tconst char *s[8];\n\tint l[8];\n\tint i;\n\tfor (i = 0; i < prinst->callargc; i++)\n\t{\n\t\ts[i] = PR_GetStringOfs(prinst, OFS_PARM0+i*3);\n\t\tl[i] = strlen(s[i]);\n\t\tlen += l[i];\n\t}\n\tlen++; /*for the null*/\n\n\tbuf = prinst->AddressableAlloc(prinst, len);\n\tif (!buf)\n\t{\n\t\tG_INT(OFS_RETURN) = 0;\n\t\treturn;\n\t}\n\tG_INT(OFS_RETURN) = (char*)buf - prinst->stringtable;\n\t\n\tlen = 0;\n\tfor (i = 0; i < prinst->callargc; i++)\n\t{\n\t\tmemcpy(buf, s[i], l[i]);\n\t\tbuf += l[i];\n\t}\n\t*buf = '\\0';\n#endif\n}\n\n#ifdef QCGC\nvoid QCBUILTIN PF_createbuffer (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint len = G_INT(OFS_PARM0);\n\tchar *buf;\n\tif (len <= 0)\n\t\tG_INT(OFS_RETURN) = 0;\n\telse\n\t{\n\t\tlen++;\n\t\tG_INT(OFS_RETURN) = prinst->AllocTempString(prinst, &buf, len);\n\t\tmemset(buf, 0, len);\n\t}\n}\n#endif\n\n//string(string str1, string str2, str3, etc) strcat\nvoid QCBUILTIN PF_strcat (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tchar *buf;\n\tsize_t len = 0;\n\tconst char *s[8];\n\tint l[8];\n\tint i;\n\tfor (i = 0; i < prinst->callargc; i++)\n\t{\n\t\ts[i] = PR_GetStringOfs(prinst, OFS_PARM0+i*3);\n\t\tl[i] = strlen(s[i]);\n\t\tlen += l[i];\n\n#ifdef HAVE_LEGACY\n\t\tif (dpcompat_strcat_limit.ival && len > dpcompat_strcat_limit.ival)\n\t\t{\n\t\t\tl[i]-= len-dpcompat_strcat_limit.ival;\n\t\t\tlen -= len-dpcompat_strcat_limit.ival;\n\t\t}\n#endif\n\t}\n\tlen++; /*for the null*/\n\t((int *)pr_globals)[OFS_RETURN] = prinst->AllocTempString(prinst, &buf, len);\n\tif (buf)\n\t{\n\t\tfor (i = 0; i < prinst->callargc; i++)\n\t\t{\n\t\t\tmemcpy(buf, s[i], l[i]);\n\t\t\tbuf += l[i];\n\t\t}\n\t\t*buf = '\\0';\n\t}\n}\n\n//returns a section of a string as a tempstring\nvoid QCBUILTIN PF_substring (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint start, length, slen;\n\tconst char *s;\n\tchar *string;\n\n\ts = PR_GetStringOfs(prinst, OFS_PARM0);\n\tstart = G_FLOAT(OFS_PARM1);\n\tlength = G_FLOAT(OFS_PARM2);\n\n\t//UTF-8-FIXME: start+length are chars not bytes...\n\n\tif (VMUTF8)\n\t\tslen = unicode_charcount(s, 1<<30, VMUTF8MARKUP);\n\telse\n\t\tslen = strlen(s);\n\n\tif (start < 0)\n\t\tstart = slen+start;\n\tif (length < 0)\n\t\tlength = slen-start+(length+1);\n\tif (start < 0)\n\t{\n\t//\tlength += start;\n\t\tstart = 0;\n\t}\n\n\tif (start >= slen || length<=0)\n\t{\n\t\tRETURN_TSTRING(\"\");\n\t\treturn;\n\t}\n\n\tslen -= start;\n\tif (length > slen)\n\t\tlength = slen;\n\n\tif (VMUTF8)\n\t{\n\t\tstart = unicode_byteofsfromcharofs(s, start, VMUTF8MARKUP);\n\t\tlength = unicode_byteofsfromcharofs(s+start, length, VMUTF8MARKUP);\n\t}\n\ts += start;\n\n\t((int *)pr_globals)[OFS_RETURN] = prinst->AllocTempString(prinst, &string, length+1);\n\n\tmemcpy(string, s, length);\n\tstring[length] = '\\0';\n}\n\nvoid QCBUILTIN PF_strlen(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tif (VMUTF8)\n\t\tG_FLOAT(OFS_RETURN) = unicode_charcount(PR_GetStringOfs(prinst, OFS_PARM0), 1<<30, VMUTF8MARKUP);\n\telse\n\t\tG_FLOAT(OFS_RETURN) = strlen(PR_GetStringOfs(prinst, OFS_PARM0));\n}\n\n//float(string input, string token) instr\nvoid QCBUILTIN PF_instr (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char *sub;\n\tconst char *s1;\n\tconst char *s2;\n\n\ts1 = PR_GetStringOfs(prinst, OFS_PARM0);\n\ts2 = PF_VarString(prinst, 1, pr_globals);\n\n\tif (!s1 || !s2)\n\t{\n\t\tPR_BIError(prinst, \"Null string in \\\"instr\\\"\\n\");\n\t\treturn;\n\t}\n\n\tsub = strstr(s1, s2);\n\n\tif (sub == NULL)\n\t\tG_INT(OFS_RETURN) = 0;\n\telse\n\t\tRETURN_SSTRING(sub);\t//last as long as the original string\n}\n\nvoid QCBUILTIN PF_strreplace (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tchar resultbuf[4096];\n\tchar *result = resultbuf;\n\tconst char *search = PR_GetStringOfs(prinst, OFS_PARM0);\n\tconst char *replace = PR_GetStringOfs(prinst, OFS_PARM1);\n\tconst char *subject = PR_GetStringOfs(prinst, OFS_PARM2);\n\tint searchlen = strlen(search);\n\tint replacelen = strlen(replace);\n\n\tif (searchlen)\n\t{\n\t\twhile (*subject && result < resultbuf + sizeof(resultbuf) - replacelen - 2)\n\t\t{\n\t\t\tif (!strncmp(subject, search, searchlen))\n\t\t\t{\n\t\t\t\tsubject += searchlen;\n\t\t\t\tmemcpy(result, replace, replacelen);\n\t\t\t\tresult += replacelen;\n\t\t\t}\n\t\t\telse\n\t\t\t\t*result++ = *subject++;\n\t\t}\n\t\t*result = 0;\n\t\tRETURN_TSTRING(resultbuf);\n\t}\n\telse\n\t\tRETURN_TSTRING(subject);\n}\nvoid QCBUILTIN PF_strireplace (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tchar resultbuf[4096];\n\tchar *result = resultbuf;\n\tconst char *search = PR_GetStringOfs(prinst, OFS_PARM0);\n\tconst char *replace = PR_GetStringOfs(prinst, OFS_PARM1);\n\tconst char *subject = PR_GetStringOfs(prinst, OFS_PARM2);\n\tint searchlen = strlen(search);\n\tint replacelen = strlen(replace);\n\n\tif (searchlen)\n\t{\n\t\twhile (*subject && result < resultbuf + sizeof(resultbuf) - replacelen - 2)\n\t\t{\n\t\t\t//UTF-8-FIXME: case insensitivity is awkward...\n\t\t\tif (!strnicmp(subject, search, searchlen))\n\t\t\t{\n\t\t\t\tsubject += searchlen;\n\t\t\t\tmemcpy(result, replace, replacelen);\n\t\t\t\tresult += replacelen;\n\t\t\t}\n\t\t\telse\n\t\t\t\t*result++ = *subject++;\n\t\t}\n\t\t*result = 0;\n\t\tRETURN_TSTRING(resultbuf);\n\t}\n\telse\n\t\tRETURN_TSTRING(subject);\n}\n\n//string(entity ent) etos = #65\nvoid QCBUILTIN PF_etos (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tchar s[64];\n\tsnprintf (s, sizeof(s), \"entity %i\", G_EDICTNUM(prinst, OFS_PARM0));\n\tRETURN_TSTRING(s);\n}\n\n//DP_QC_STRINGCOLORFUNCTIONS\n// #476 float(string s) strlennocol - returns how many characters are in a string, minus color codes\nvoid QCBUILTIN PF_strlennocol (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char *in = PR_GetStringOfs(prinst, OFS_PARM0);\n\tchar result[8192];\n\tconchar_t flagged[8192];\n\tunsigned int len = 0;\n\tCOM_ParseFunString(CON_WHITEMASK, in, flagged, sizeof(flagged), false);\n\tCOM_DeFunString(flagged, NULL, result, sizeof(result), true, false);\n\n\tfor (len = 0; result[len]; len++)\n\t\t;\n\tG_FLOAT(OFS_RETURN) = len;\n}\n\n//DP_QC_STRINGCOLORFUNCTIONS\n// string (string s) strdecolorize - returns the passed in string with color codes stripped\nvoid QCBUILTIN PF_strdecolorize (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char *in = PR_GetStringOfs(prinst, OFS_PARM0);\n\tchar result[8192];\n\tconchar_t flagged[8192];\n\tCOM_ParseFunString(CON_WHITEMASK, in, flagged, sizeof(flagged), false);\n\tCOM_DeFunString(flagged, NULL, result, sizeof(result), true, false);\n\n\tRETURN_TSTRING(result);\n}\n\n//DP_QC_STRING_CASE_FUNCTIONS\nvoid QCBUILTIN PF_strtolower (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char *in = PR_GetStringOfs(prinst, OFS_PARM0);\n\tchar result[8192];\n\n\tunicode_strtolower(in, result, sizeof(result), VMUTF8MARKUP);\n\n\tRETURN_TSTRING(result);\n}\n\n//DP_QC_STRING_CASE_FUNCTIONS\nvoid QCBUILTIN PF_strtoupper (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char *in = PR_GetStringOfs(prinst, OFS_PARM0);\n\tchar result[8192];\n\n\tunicode_strtoupper(in, result, sizeof(result), VMUTF8MARKUP);\n\n\tRETURN_TSTRING(result);\n}\n\n//DP_QC_STRFTIME\nvoid QCBUILTIN PF_strftime (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char *in = PF_VarString(prinst, 1, pr_globals);\n\tchar result[8192];\n\n\ttime_t ctime;\n\tstruct tm *tm;\n\n\tctime = time(NULL);\n\n\tif (G_FLOAT(OFS_PARM0))\n\t\ttm = localtime(&ctime);\n\telse\n\t\ttm = gmtime(&ctime);\n\n\t//msvc sucks.\n\tif (!strcmp(in, \"%R\"))\n\t\tin = \"%H:%M\";\n\telse if (!strcmp(in, \"%F\"))\n\t\tin = \"%Y-%m-%d\";\n\n\tstrftime(result, sizeof(result), in, tm);\n\n\tRETURN_TSTRING(result);\n}\n\n//String functions\n////////////////////////////////////////////////////\n//515's String functions\n\nstruct strbuf {\n\tpubprogfuncs_t *prinst;\n\tchar **strings;\n\tsize_t used;\n\tsize_t allocated;\n\tint flags;\n};\n\n#define BUFFLAG_SAVED 1\n#define BUFSTRBASE 1\t//officially these are 0-based (ie: use negatives for not-a-buffer), but fte biases it to catch qc bugs.\nstruct strbuf\t*strbuflist;\nsize_t\t\t\tstrbufmax;\n\nstatic void PR_buf_savegame(vfsfile_t *f, pubprogfuncs_t *prinst, qboolean binary)\n{\n\tunsigned int i, bufno;\n\tchar *tmp = NULL;\n\tsize_t tmpsize = 0, ns;\n\n\tfor (bufno = 0; bufno < strbufmax; bufno++)\n\t{\n\t\tif (strbuflist[bufno].prinst == prinst && (strbuflist[bufno].flags & BUFFLAG_SAVED))\n\t\t{\n\t\t\tVFS_PRINTF (f, \"buffer %u %i %i %u\\n\", bufno+BUFSTRBASE, strbuflist[bufno].flags, ev_string, (unsigned int)strbuflist[bufno].used);\n\t\t\tVFS_PRINTF (f, \"{\\n\");\n\t\t\tfor (i = 0; i < strbuflist[bufno].used; i++)\n\t\t\t\tif (strbuflist[bufno].strings[i])\n\t\t\t\t{\n\t\t\t\t\tns = strlen(strbuflist[bufno].strings[i])*2 + 4;\n\t\t\t\t\tif (ns > tmpsize)\n\t\t\t\t\t\tZ_ReallocElements((void**)&tmp, &tmpsize, ns, sizeof(char));\n\t\t\t\t\tVFS_PRINTF (f, \"%u %s\\n\", i, COM_QuotedString(strbuflist[bufno].strings[i], tmp, tmpsize, false));\n\t\t\t\t}\n\t\t\tVFS_PRINTF (f, \"}\\n\");\n\t\t}\n\t}\n\tfree(tmp);\n}\nstatic const char *PR_buf_loadgame(pubprogfuncs_t *prinst, const char *l)\n{\n\tchar token[65536];\n\tint bufno;\n\tunsigned int flags;\n\tsize_t buffersize, index;\n\tcom_tokentype_t tt;\n\tstruct strbuf *buf;\n\tl = COM_ParseTokenOut(l, NULL, token, sizeof(token), &tt);if (tt != TTP_RAWTOKEN)return NULL;\n\tbufno = atoi(token)-BUFSTRBASE;\n\tl = COM_ParseTokenOut(l, NULL, token, sizeof(token), &tt);if (tt != TTP_RAWTOKEN)return NULL;\n\tflags = atoi(token);\n\tl = COM_ParseTokenOut(l, NULL, token, sizeof(token), &tt);if (tt != TTP_RAWTOKEN)return NULL;\n\tif (atoi(token) != ev_string) return NULL;\t//we only support string buffers right now. FIXME: if the token was \"string\" then create an empty strbuf.\n\tl = COM_ParseTokenOut(l, NULL, token, sizeof(token), &tt);if (tt != TTP_RAWTOKEN)return NULL;\n\tbuffersize = atoi(token);\n\n\tl = COM_ParseTokenOut(l, NULL, token, sizeof(token), &tt);if (tt != TTP_LINEENDING)return NULL;\n\tl = COM_ParseTokenOut(l, NULL, token, sizeof(token), &tt);if (tt != TTP_PUNCTUATION)return NULL;\n\tif (strcmp(token, \"{\"))\treturn NULL;\n\n\tif (bufno < 0 || bufno >= 1<<16)\n\t\treturn NULL;\n\tif (bufno >= strbufmax)\n\t\tZ_ReallocElements((void**)&strbuflist, &strbufmax, bufno+1, sizeof(*strbuflist));\n\n\tbuf = &strbuflist[bufno];\n\tif (buf->prinst)\n\t{\t//already alive... wipe it.\n\t\tfor (index = 0; index < buf->used; index++)\n\t\t\tZ_Free(buf->strings[index]);\n\t\tZ_Free(buf->strings);\n\n\t\tbuf->strings = NULL;\n\t\tbuf->used = 0;\n\t\tbuf->allocated = 0;\n\t}\n\tbuf->prinst = prinst;\n\tbuf->flags = flags;\n\n\tif (buffersize)\n\t{\t//preallocate the buffer to match what it used to be.\n\t\tif (buffersize < 1<<20)\n\t\t{\n\t\t\tZ_ReallocElements((void**)&buf->strings, &buf->allocated, buffersize, sizeof(*buf->strings));\n\t\t\tbuf->used = buf->allocated;\n\t\t}\n\t}\n\n\tfor(;;)\n\t{\n\t\tl = COM_ParseTokenOut(l, NULL, token, sizeof(token), &tt);\n\t\tif (tt == TTP_LINEENDING)\n\t\t\tcontinue;\n\t\tif (tt == TTP_PUNCTUATION && !strcmp(token, \"}\"))\n\t\t\tbreak;\n\t\tif (tt != TTP_RAWTOKEN)\n\t\t\tbreak;\n\t\tindex = atoi(token);\n\t\tl = COM_ParseTokenOut(l, NULL, token, sizeof(token), &tt);if (tt != TTP_STRING)return NULL;\n\n\t\tif (index < 0 || index >= buf->allocated)\n\t\t\tcontinue;\t//some sort of error.\n\n\t\tif (buf->strings[index])\t//shouldn't really happen, but just in case we're given bad input...\n\t\t\tZ_Free(buf->strings[index]);\n\t\tbuf->strings[index] = Z_Malloc(strlen(token)+1);\n\t\tstrcpy(buf->strings[index], token);\n\n\t\tif (index >= buf->used)\n\t\t\tbuf->used = index+1;\n\t}\n\treturn l;\n}\n\nvoid PF_buf_shutdown(pubprogfuncs_t *prinst)\n{\n\tsize_t i, bufno;\n\n\tfor (bufno = 0; bufno < strbufmax; bufno++)\n\t{\n\t\tif (strbuflist[bufno].prinst == prinst)\n\t\t{\n\t\t\tfor (i = 0; i < strbuflist[bufno].used; i++)\n\t\t\t\tZ_Free(strbuflist[bufno].strings[i]);\n\t\t\tZ_Free(strbuflist[bufno].strings);\n\n\t\t\tstrbuflist[bufno].strings = NULL;\n\t\t\tstrbuflist[bufno].used = 0;\n\t\t\tstrbuflist[bufno].allocated = 0;\n\n\t\t\tstrbuflist[bufno].prinst = NULL;\n\t\t}\n\t}\n\twhile (strbufmax>0 && !strbuflist[strbufmax-1].prinst)\n\t\tstrbufmax--;\n\tif (!strbufmax)\n\t{\n\t\tZ_Free(strbuflist);\n\t\tstrbuflist = NULL;\n\t}\n}\n\n// #440 float() buf_create (DP_QC_STRINGBUFFERS)\nvoid QCBUILTIN PF_buf_create  (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tsize_t i;\n\n\tconst char *type = ((prinst->callargc>0)?PR_GetStringOfs(prinst, OFS_PARM0):\"string\");\n\tunsigned int flags = ((prinst->callargc>1)?G_FLOAT(OFS_PARM1):BUFFLAG_SAVED);\n\tflags &= BUFFLAG_SAVED;\n\n\tif (!Q_strcasecmp(type, \"string\"))\n\t\t;\n\telse\n\t{\n\t\tG_FLOAT(OFS_RETURN) = -1;\n\t\treturn;\n\t}\n\n\t//flags&1 == saved. apparently.\n\n\tfor (i = 0; i < strbufmax; i++)\n\t{\n\t\tif (!strbuflist[i].prinst)\n\t\t\tbreak;\n\t}\n\tif (i == strbufmax)\n\t{\n\t\tif (!ZF_ReallocElements((void**)&strbuflist, &strbufmax, strbufmax+1, sizeof(*strbuflist)))\n\t\t{\n\t\t\tG_FLOAT(OFS_RETURN) = -1;\n\t\t\treturn;\n\t\t}\n\t}\n\tstrbuflist[i].prinst = prinst;\n\tstrbuflist[i].used = 0;\n\tstrbuflist[i].allocated = 0;\n\tstrbuflist[i].strings = NULL;\n\tstrbuflist[i].flags = flags;\n\tG_FLOAT(OFS_RETURN) = i+BUFSTRBASE;\n}\n// #441 void(float bufhandle) buf_del (DP_QC_STRINGBUFFERS)\nvoid QCBUILTIN PF_buf_del  (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tsize_t i;\n\tsize_t bufno = G_FLOAT(OFS_PARM0)-BUFSTRBASE;\n\n\tif (bufno >= strbufmax)\n\t\treturn;\n\tif (strbuflist[bufno].prinst != prinst)\n\t\treturn;\n\n\tfor (i = 0; i < strbuflist[bufno].used; i++)\n\t\tZ_Free(strbuflist[bufno].strings[i]);\n\tZ_Free(strbuflist[bufno].strings);\n\n\tstrbuflist[bufno].strings = NULL;\n\tstrbuflist[bufno].used = 0;\n\tstrbuflist[bufno].allocated = 0;\n\n\tstrbuflist[bufno].prinst = NULL;\n}\n// #442 float(float bufhandle) buf_getsize (DP_QC_STRINGBUFFERS)\nvoid QCBUILTIN PF_buf_getsize  (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tsize_t bufno = G_FLOAT(OFS_PARM0)-BUFSTRBASE;\n\n\tif (bufno >= strbufmax)\n\t\treturn;\n\tif (strbuflist[bufno].prinst != prinst)\n\t\treturn;\n\n\tG_FLOAT(OFS_RETURN) = strbuflist[bufno].used;\n}\n// #443 void(float bufhandle_from, float bufhandle_to) buf_copy (DP_QC_STRINGBUFFERS)\nvoid QCBUILTIN PF_buf_copy  (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tsize_t buffrom = G_FLOAT(OFS_PARM0)-BUFSTRBASE;\n\tsize_t bufto = G_FLOAT(OFS_PARM1)-BUFSTRBASE;\n\tsize_t i;\n\n\tif (bufto == buffrom)\t//err...\n\t\treturn;\n\tif (buffrom >= strbufmax)\n\t\treturn;\n\tif (strbuflist[buffrom].prinst != prinst)\n\t\treturn;\n\tif (bufto >= strbufmax)\n\t\treturn;\n\tif (strbuflist[bufto].prinst != prinst)\n\t\treturn;\n\n\t//obliterate any and all existing data.\n\tfor (i = 0; i < strbuflist[bufto].used; i++)\n\t\tZ_Free(strbuflist[bufto].strings[i]);\n\tZ_Free(strbuflist[bufto].strings);\n\n\t//copy new data over.\n\tstrbuflist[bufto].used = strbuflist[bufto].allocated = strbuflist[buffrom].used;\n\tstrbuflist[bufto].strings = BZ_Malloc(strbuflist[buffrom].used * sizeof(char*));\n\tfor (i = 0; i < strbuflist[buffrom].used; i++)\n\t\tstrbuflist[bufto].strings[i] = strbuflist[buffrom].strings[i]?Z_StrDup(strbuflist[buffrom].strings[i]):NULL;\n}\nstatic int PF_buf_sort_sortprefixlen;\nstatic int QDECL PF_buf_sort_ascending_prefix(const void *a, const void *b)\n{\n\treturn strncmp(*(char**)a, *(char**)b, PF_buf_sort_sortprefixlen);\n}\nstatic int QDECL PF_buf_sort_descending_prefix(const void *b, const void *a)\n{\n\treturn strncmp(*(char**)a, *(char**)b, PF_buf_sort_sortprefixlen);\n}\n// #444 void(float bufhandle, float sortprefixlen, float backward) buf_sort (DP_QC_STRINGBUFFERS)\nvoid QCBUILTIN PF_buf_sort  (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tsize_t bufno = G_FLOAT(OFS_PARM0)-BUFSTRBASE;\n\tint sortprefixlen = G_FLOAT(OFS_PARM1);\n\tqboolean backwards = G_FLOAT(OFS_PARM2);\n\tint s,d;\n\tchar **strings;\n\n\tif (bufno >= strbufmax)\n\t\treturn;\n\tif (strbuflist[bufno].prinst != prinst)\n\t\treturn;\n\n\tif (sortprefixlen <= 0)\n\t\tsortprefixlen = 0x7fffffff;\n\n\t//take out the nulls first, to avoid weird/crashy sorting\n\tfor (s = 0, d = 0, strings = strbuflist[bufno].strings; s < strbuflist[bufno].used; )\n\t{\n\t\tif (!strings[s])\n\t\t{\n\t\t\ts++;\n\t\t\tcontinue;\n\t\t}\n\t\tstrings[d++] = strings[s++];\n\t}\n\tstrbuflist[bufno].used = d;\n\n\t//no nulls now, sort it.\n\tPF_buf_sort_sortprefixlen = sortprefixlen;\t//eww, a global. burn in hell.\n\tif (backwards)\t//z first\n\t\tqsort(strings, strbuflist[bufno].used, sizeof(char*), PF_buf_sort_descending_prefix);\n\telse\t//a first\n\t\tqsort(strings, strbuflist[bufno].used, sizeof(char*), PF_buf_sort_ascending_prefix);\n}\n// #445 string(float bufhandle, string glue) buf_implode (DP_QC_STRINGBUFFERS)\nvoid QCBUILTIN PF_buf_implode  (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tsize_t bufno = G_FLOAT(OFS_PARM0)-BUFSTRBASE;\n\tconst char *glue = PR_GetStringOfs(prinst, OFS_PARM1);\n\tunsigned int gluelen = strlen(glue);\n\tsize_t retlen, l, i;\n\tchar **strings;\n\tchar *ret;\n\n\tif (bufno >= strbufmax)\n\t\treturn;\n\tif (strbuflist[bufno].prinst != prinst)\n\t\treturn;\n\n\t//count neededlength\n\tstrings = strbuflist[bufno].strings;\n\tfor (i = 0, retlen = 0; i < strbuflist[bufno].used; i++)\n\t{\n\t\tif (strings[i])\n\t\t{\n\t\t\tif (retlen)\n\t\t\t\tretlen += gluelen;\n\t\t\tretlen += strlen(strings[i]);\n\t\t}\n\t}\n\n\t//generate the output\n\tret = malloc(retlen+1);\n\tfor (i = 0, retlen = 0; i < strbuflist[bufno].used; i++)\n\t{\n\t\tif (strings[i])\n\t\t{\n\t\t\tif (retlen)\n\t\t\t{\n\t\t\t\tmemcpy(ret+retlen, glue, gluelen);\n\t\t\t\tretlen += gluelen;\n\t\t\t}\n\t\t\tl = strlen(strings[i]);\n\t\t\tmemcpy(ret+retlen, strings[i], l);\n\t\t\tretlen += l;\n\t\t}\n\t}\n\n\t//add the null and return\n\tret[retlen] = 0;\n\tRETURN_TSTRING(ret);\n\tfree(ret);\n}\n// #446 string(float bufhandle, float string_index) bufstr_get (DP_QC_STRINGBUFFERS)\nvoid QCBUILTIN PF_bufstr_get  (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tsize_t bufno = G_FLOAT(OFS_PARM0)-BUFSTRBASE;\n\tsize_t index = G_FLOAT(OFS_PARM1);\n\n\tif ((unsigned int)bufno >= strbufmax)\n\t{\n\t\tG_INT(OFS_RETURN) = 0;\n\t\treturn;\n\t}\n\tif (strbuflist[bufno].prinst != prinst)\n\t{\n\t\tG_INT(OFS_RETURN) = 0;\n\t\treturn;\n\t}\n\n\tif (index >= strbuflist[bufno].used)\n\t{\n\t\tG_INT(OFS_RETURN) = 0;\n\t\treturn;\n\t}\n\n\tRETURN_TSTRING(strbuflist[bufno].strings[index]);\n}\n// #447 void(float bufhandle, float string_index, string str) bufstr_set (DP_QC_STRINGBUFFERS)\nvoid QCBUILTIN PF_bufstr_set  (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tsize_t bufno = G_FLOAT(OFS_PARM0)-BUFSTRBASE;\n\tsize_t index = G_FLOAT(OFS_PARM1);\n\tconst char *string = PR_GetStringOfs(prinst, OFS_PARM2);\n\tsize_t oldcount;\n\n\tif (bufno >= strbufmax)\n\t\treturn;\n\tif (strbuflist[bufno].prinst != prinst)\n\t\treturn;\n\n\tif (index >= strbuflist[bufno].allocated)\n\t{\n\t\tif (index > 1024*1024)\n\t\t{\n\t\t\tPR_RunWarning(prinst, \"index outside sanity range\\n\");\n\t\t\treturn;\n\t\t}\n\t\toldcount = strbuflist[bufno].allocated;\n\t\tstrbuflist[bufno].allocated = (index + 256);\n\t\tstrbuflist[bufno].strings = BZ_Realloc(strbuflist[bufno].strings, strbuflist[bufno].allocated*sizeof(char*));\n\t\tmemset(strbuflist[bufno].strings+oldcount, 0, (strbuflist[bufno].allocated - oldcount) * sizeof(char*));\n\t}\n\tif (strbuflist[bufno].strings[index])\n\t\tZ_Free(strbuflist[bufno].strings[index]);\n\tstrbuflist[bufno].strings[index] = Z_Malloc(strlen(string)+1);\n\tstrcpy(strbuflist[bufno].strings[index], string);\n\n\tif (index >= strbuflist[bufno].used)\n\t\tstrbuflist[bufno].used = index+1;\n}\n\nstatic size_t PF_bufstr_add_internal(int bufno, const char *string, int appendonend)\n{\n\tsize_t index;\n\tif (appendonend)\n\t{\n\t\t//add on end\n\t\tindex = strbuflist[bufno].used;\n\t}\n\telse\n\t{\n\t\t//find a hole\n\t\tfor (index = 0; index < strbuflist[bufno].used; index++)\n\t\t\tif (!strbuflist[bufno].strings[index])\n\t\t\t\tbreak;\n\t}\n\n\t//expand it if needed\n\tif (index >= strbuflist[bufno].allocated)\n\t{\n\t\tint oldcount;\n\t\toldcount = strbuflist[bufno].allocated;\n\t\tstrbuflist[bufno].allocated = (index + 256);\n\t\tstrbuflist[bufno].strings = BZ_Realloc(strbuflist[bufno].strings, strbuflist[bufno].allocated*sizeof(char*));\n\t\tmemset(strbuflist[bufno].strings+oldcount, 0, (strbuflist[bufno].allocated - oldcount) * sizeof(char*));\n\t}\n\n\t//add in the new string.\n\tif (strbuflist[bufno].strings[index])\n\t\tZ_Free(strbuflist[bufno].strings[index]);\n\tstrbuflist[bufno].strings[index] = Z_Malloc(strlen(string)+1);\n\tstrcpy(strbuflist[bufno].strings[index], string);\n\n\tif (index >= strbuflist[bufno].used)\n\t\tstrbuflist[bufno].used = index+1;\n\n\treturn index;\n}\n\n// #448 float(float bufhandle, string str, float order) bufstr_add (DP_QC_STRINGBUFFERS)\nvoid QCBUILTIN PF_bufstr_add  (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tsize_t bufno = G_FLOAT(OFS_PARM0)-BUFSTRBASE;\n\tconst char *string = PR_GetStringOfs(prinst, OFS_PARM1);\n\tint order = G_FLOAT(OFS_PARM2);\n\n\tif (bufno >= strbufmax)\n\t\treturn;\n\tif (strbuflist[bufno].prinst != prinst)\n\t\treturn;\n\n\tG_FLOAT(OFS_RETURN) = PF_bufstr_add_internal(bufno, string, order);\n}\n// #449 void(float bufhandle, float string_index) bufstr_free (DP_QC_STRINGBUFFERS)\nvoid QCBUILTIN PF_bufstr_free  (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tsize_t bufno = G_FLOAT(OFS_PARM0)-BUFSTRBASE;\n\tsize_t index = G_FLOAT(OFS_PARM1);\n\n\tif (bufno >= strbufmax)\n\t\treturn;\n\tif (strbuflist[bufno].prinst != prinst)\n\t\treturn;\n\n\tif (index >= strbuflist[bufno].used)\n\t\treturn;\t//not valid anyway.\n\n\tif (strbuflist[bufno].strings[index])\n\t\tZ_Free(strbuflist[bufno].strings[index]);\n\tstrbuflist[bufno].strings[index] = NULL;\n}\n\nstatic int QDECL PF_buf_sort_ascending(const void *a, const void *b)\n{\n\treturn strcmp(*(char**)a, *(char**)b);\n}\nvoid QCBUILTIN PF_buf_cvarlist  (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tsize_t bufno = G_FLOAT(OFS_PARM0)-BUFSTRBASE;\n\tconst char *pattern = PR_GetStringOfs(prinst, OFS_PARM1);\n\tconst char *antipattern = PR_GetStringOfs(prinst, OFS_PARM2);\n\tint i;\n\tcvar_group_t\t*grp;\n\tcvar_t\t*var;\n\textern cvar_group_t *cvar_groups;\n\tint plen = strlen(pattern), alen = strlen(antipattern);\n\tqboolean pwc = strchr(pattern, '*')||strchr(pattern, '?'),\n\t\t\t awc = strchr(antipattern, '*')||strchr(antipattern, '?');\n\n\tif (bufno >= strbufmax)\n\t\treturn;\n\tif (strbuflist[bufno].prinst != prinst)\n\t\treturn;\n\n\t//obliterate any and all existing data.\n\tfor (i = 0; i < strbuflist[bufno].used; i++)\n\t\tZ_Free(strbuflist[bufno].strings[i]);\n\tZ_Free(strbuflist[bufno].strings);\n\tstrbuflist[bufno].strings = NULL;\n\tstrbuflist[bufno].used = strbuflist[bufno].allocated = 0;\n\n\t//ignore name2, no point listing it twice.\n\tfor (grp=cvar_groups ; grp ; grp=grp->next)\n\t\tfor (var=grp->cvars ; var ; var=var->next)\n\t\t{\n\t\t\tif (plen && (pwc?!wildcmp(pattern, var->name):strncmp(var->name, pattern, plen)))\n\t\t\t\tcontinue;\n\t\t\tif (alen && (awc?wildcmp(antipattern, var->name):!strncmp(var->name, antipattern, alen)))\n\t\t\t\tcontinue;\n\n\t\t\tPF_bufstr_add_internal(bufno, var->name, true);\n\t\t}\n\n\tqsort(strbuflist[bufno].strings, strbuflist[bufno].used, sizeof(char*), PF_buf_sort_ascending);\n}\n\nenum matchmethod_e\n{\n\tMATCH_AUTO=0,\n\tMATCH_EXACT=1,\n\tMATCH_LEFT=2,\n\tMATCH_RIGHT=3,\n\tMATCH_MIDDLE=4,\n\tMATCH_PATTERN=5,\n};\nstatic qboolean domatch(const char *str, const char *pattern, enum matchmethod_e method)\n{\n\tswitch(method)\n\t{\n\tcase MATCH_EXACT:\n\t\treturn !strcmp(str, pattern);\n\tcase MATCH_LEFT:\n\t\treturn !strncmp(str, pattern, strlen(pattern));\n\tcase MATCH_RIGHT:\n\t\t{\n\t\t\tsize_t slen = strlen(str);\n\t\t\tsize_t plen = strlen(pattern);\n\t\t\tif (plen > slen)\n\t\t\t\treturn false;\n\t\t\treturn !strcmp(str + slen-plen, pattern);\n\t\t}\n\tcase MATCH_MIDDLE:\n\t\treturn !!strstr(str, pattern);\n\tcase MATCH_AUTO:\t//just treat as MATCH_PATTERN. we could optimise it a bit, but mneh\n\tcase MATCH_PATTERN:\n\tdefault:\n\t\treturn wildcmp(pattern, str);\n\t}\n}\nvoid QCBUILTIN PF_bufstr_find  (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tsize_t bufno = G_FLOAT(OFS_PARM0)-BUFSTRBASE;\n\tconst char *pattern = PR_GetStringOfs(prinst, OFS_PARM1);\n\tenum matchmethod_e matchmethod = G_FLOAT(OFS_PARM2);\n\tint idx = (prinst->callargc > 3)?G_FLOAT(OFS_PARM3):0;\n\tint step = (prinst->callargc > 4)?G_FLOAT(OFS_PARM4):1;\n\tconst char *s;\n\n\tG_FLOAT(OFS_RETURN) = -1;\t//assume the worst\n\n\tif (bufno >= strbufmax)\n\t\treturn;\n\tif (strbuflist[bufno].prinst != prinst)\n\t\treturn;\n\n\tif (idx < 0 || step <= 0)\n\t\treturn;\n\tfor (; idx < strbuflist[bufno].used; idx += step)\n\t{\n\t\ts = strbuflist[bufno].strings[idx];\n\t\tif (!s) continue;\n\t\tif (domatch(s, pattern, matchmethod))\n\t\t{\n\t\t\tG_FLOAT(OFS_RETURN) = idx;\n\t\t\tbreak;\n\t\t}\n\t}\n}\n\n//directly reads a file into a stringbuffer\nvoid QCBUILTIN PF_buf_loadfile  (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char *fname = PR_GetStringOfs(prinst, OFS_PARM0);\n\tsize_t bufno = G_FLOAT(OFS_PARM1)-BUFSTRBASE;\n\tvfsfile_t *file;\n\tchar line[8192];\n\tconst char *fallback;\n\n\tG_FLOAT(OFS_RETURN) = 0;\n\n\tif (bufno >= strbufmax)\n\t\treturn;\n\tif (strbuflist[bufno].prinst != prinst)\n\t\treturn;\n\n\tif (!QC_FixFileName(fname, &fname, &fallback))\n\t{\n\t\tCon_Printf(\"qcfopen: Access denied: %s\\n\", fname);\n\t\treturn;\n\t}\n\n\tfile = FS_OpenVFS(fname, \"rb\", FS_GAME);\n\tif (!file && fallback)\n\t\tfile = FS_OpenVFS(fallback, \"rb\", FS_GAME);\n\tif (!file)\n\t\treturn;\n\n\twhile(VFS_GETS(file, line, sizeof(line)))\n\t{\n\t\tPF_bufstr_add_internal(bufno, line, true);\n\t}\n\tVFS_CLOSE(file);\n\n\tG_FLOAT(OFS_RETURN) = 1;\n}\n\nvoid QCBUILTIN PF_buf_writefile  (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tsize_t fnum = G_FLOAT(OFS_PARM0) - FIRST_QC_FILE_INDEX;\n\tsize_t bufno = G_FLOAT(OFS_PARM1)-BUFSTRBASE;\n\tchar **strings;\n\tint idx, midx;\n\n\tG_FLOAT(OFS_RETURN) = 0;\n\n\tif (bufno >= strbufmax)\n\t\treturn;\n\tif (strbuflist[bufno].prinst != prinst)\n\t\treturn;\n\n\tif (fnum >= MAX_QC_FILES)\n\t\treturn;\n\tif (pf_fopen_files[fnum].prinst != prinst)\n\t\treturn;\n\n\tif (prinst->callargc >= 3)\n\t\tidx = G_FLOAT(OFS_PARM2);\n\telse\n\t\tidx = 0;\n\tif (prinst->callargc >= 4)\n\t\tmidx = idx + G_FLOAT(OFS_PARM3);\n\telse\n\t\tmidx = strbuflist[bufno].used - idx;\n\tidx = bound(0, idx, (int)strbuflist[bufno].used);\n\tmidx = min(midx, (int)strbuflist[bufno].used);\n\tfor(strings = strbuflist[bufno].strings; idx < midx; idx++)\n\t{\n\t\tif (strings[idx])\n\t\t{\n\t\t\tPF_fwrite_internal (prinst, fnum, strings[idx], strlen(strings[idx]));\n\t\t\tPF_fwrite_internal (prinst, fnum, \"\\n\", 1);\n\t\t}\n\t}\n\tG_FLOAT(OFS_RETURN) = 1;\n}\n\n//515's String functions\n////////////////////////////////////////////////////\n\n//float(float caseinsensitive, string s, ...) crc16 = #494 (DP_QC_CRC16)\nvoid QCBUILTIN PF_crc16 (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint insens = G_FLOAT(OFS_PARM0);\n\tconst char *str = PF_VarString(prinst, 1, pr_globals);\n\tint len = strlen(str);\n\n\tif (insens)\n\t\tG_FLOAT(OFS_RETURN) = CalcHashInt(&hash_crc16_lower, str, len);\n\telse\n\t\tG_FLOAT(OFS_RETURN) = CalcHashInt(&hash_crc16, str, len);\n}\n\nstatic void QCBUILTIN PF_digest_internal (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals, const char *hashtype, const void *str, size_t len)\n{\n\tint digestsize, i;\n\tunsigned char digest[64];\n\tunsigned char hexdig[sizeof(digest)*2+1];\n\n\tif (0)\n\t\t;\n\telse if (!strcmp(hashtype, \"MD4\"))\n\t\tdigestsize = CalcHash(&hash_md4, digest, sizeof(digest), str, len);\n\telse if (!strcmp(hashtype, \"MD5\"))\n\t\tdigestsize = CalcHash(&hash_md5, digest, sizeof(digest), str, len);\n\telse if (!strcmp(hashtype, \"SHA1\"))\n\t\tdigestsize = CalcHash(&hash_sha1, digest, sizeof(digest), str, len);\n\telse if (!strcmp(hashtype, \"SHA2-224\") || !strcmp(hashtype, \"SHA224\"))\n\t\tdigestsize = CalcHash(&hash_sha2_224, digest, sizeof(digest), str, len);\n\telse if (!strcmp(hashtype, \"SHA2-256\") || !strcmp(hashtype, \"SHA256\"))\n\t\tdigestsize = CalcHash(&hash_sha2_256, digest, sizeof(digest), str, len);\n\telse if (!strcmp(hashtype, \"SHA2-384\") || !strcmp(hashtype, \"SHA384\"))\n\t\tdigestsize = CalcHash(&hash_sha2_384, digest, sizeof(digest), str, len);\n\telse if (!strcmp(hashtype, \"SHA2-512\") || !strcmp(hashtype, \"SHA512\"))\n\t\tdigestsize = CalcHash(&hash_sha2_512, digest, sizeof(digest), str, len);\n\telse if (!strcmp(hashtype, \"CRC16\"))\n\t\tdigestsize = CalcHash(&hash_crc16, digest, sizeof(digest), str, len);\n\telse\n\t\tdigestsize = 0;\n\n\tif (digestsize)\n\t{\n\t\tfor (i = 0; i < digestsize; i++)\n\t\t{\n\t\t\tconst char *hex = \"0123456789abcdef\";\n\t\t\thexdig[i*2+0] = hex[digest[i]>>4];\n\t\t\thexdig[i*2+1] = hex[digest[i]&0xf];\n\t\t}\n\t\thexdig[i*2] = 0;\n\t\tRETURN_TSTRING(hexdig);\n\t}\n\telse\n\t\tG_INT(OFS_RETURN) = 0;\n}\n\nvoid QCBUILTIN PF_digest_hex (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char *hashtype = PR_GetStringOfs(prinst, OFS_PARM0);\n\tconst char *str = PF_VarString(prinst, 1, pr_globals);\n\tPF_digest_internal(prinst, pr_globals, hashtype, str, strlen(str));\n}\n\nvoid QCBUILTIN PF_digest_ptr (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char *hashtype = PR_GetStringOfs(prinst, OFS_PARM0);\n\tint qcptr = G_INT(OFS_PARM1);\n\tint size = G_INT(OFS_PARM2);\n\tint offset = (prinst->callargc>3)?G_INT(OFS_PARM3):0;\n\tconst void *input = PR_PointerToNative_MoInvalidate(prinst, qcptr, offset, size);\t//make sure the input is a valid qc pointer.\n\tif (!input)\n\t{\n\t\tPR_BIError(prinst, \"PF_digest_ptr: invalid pointer\\n\");\n\t\tG_INT(OFS_RETURN) = 0;\n\t\treturn;\n\t}\n\tPF_digest_internal(prinst, pr_globals, hashtype, input, size);\n}\n\nvoid QCBUILTIN PF_base64encode (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tsize_t bytes = G_INT(OFS_PARM1);\t\t\t\t\t\t\t\t\t\t//input blob size.\n\tconst void *input = PR_GetReadQCPtr(prinst, G_INT(OFS_PARM0), bytes);\t//make sure the input is a valid qc pointer.\n\tsize_t chars = ((bytes+2)/3)*4+1;\t\t\t\t\t\t\t\t\t\t//size required\n\tchar *temp;\n\tG_INT(OFS_RETURN) = prinst->AllocTempString(prinst, (char**)&temp, chars);\n\tBase64_EncodeBlock(input, bytes, temp, chars);\t\t\t\t\t\t\t//spew out the string.\n}\nvoid QCBUILTIN PF_base64decode (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst unsigned char *s = PR_GetStringOfs(prinst, OFS_PARM0);\t//grab the input string\n\tsize_t bytes = Base64_DecodeBlock(s, NULL, NULL, 0);\t\t\t//figure out how long the output needs to be\n\tqbyte *ptr = prinst->AddressableAlloc(prinst, bytes);\t\t\t//grab some qc memory\n\tbytes = Base64_DecodeBlock(s, NULL, ptr, bytes);\t\t\t\t//decode it.\n\tptr[bytes] = 0;\t\t\t\t\t\t\t\t\t\t\t\t\t//make sure its null terminated or whatever.\n\n\tG_INT(OFS_RETURN) = (char*)ptr - prinst->stringtable;\t\t\t//let the qc know where it is.\n\tG_INT(OFS_PARM1) = bytes;\t\t\t\t\t\t\t\t\t\t//let the qc know how many bytes were actually read.\n}\n\n// #510 string(string in) uri_escape = #510;\nvoid QCBUILTIN PF_uri_escape  (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tstatic const char *hex = \"0123456789ABCDEF\";\n\n\tunsigned char result[8192];\n\tunsigned char *o = result;\n\tconst unsigned char *s = PR_GetStringOfs(prinst, OFS_PARM0);\n\t*result = 0;\n\twhile (*s && o < result+sizeof(result)-4)\n\t{\n\t\t//unreserved chars according to RFC3986\n\t\tif ((*s >= 'a' && *s <= 'z') || (*s >= 'A' && *s <= 'Z') || (*s >= '0' && *s <= '9')\n\t\t\t\t|| *s == '.' || *s == '-' || *s == '_' || *s == '~')\n\t\t\t*o++ = *s++;\n\t\telse\n\t\t{\n\t\t\t*o++ = '%';\n\t\t\t*o++ = hex[*s>>4];\n\t\t\t*o++ = hex[*s&0xf];\n\t\t\ts++;\n\t\t}\n\t}\n\t*o = 0;\n\tRETURN_TSTRING(result);\n}\n\n// #511 string(string in) uri_unescape = #511;\nvoid QCBUILTIN PF_uri_unescape  (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tunsigned char *s = (unsigned char*)PR_GetStringOfs(prinst, OFS_PARM0);\n\tunsigned char resultbuf[8192];\n\tunsigned char *i, *o;\n\tunsigned char hex;\n\ti = s; o = resultbuf;\n\twhile (*i && o < resultbuf+sizeof(resultbuf)-2)\n\t{\n\t\tif (*i == '%')\n\t\t{\n\t\t\thex = 0;\n\t\t\tif (i[1] >= 'A' && i[1] <= 'F')\n\t\t\t\thex += i[1]-'A'+10;\n\t\t\telse if (i[1] >= 'a' && i[1] <= 'f')\n\t\t\t\thex += i[1]-'a'+10;\n\t\t\telse if (i[1] >= '0' && i[1] <= '9')\n\t\t\t\thex += i[1]-'0';\n\t\t\telse\n\t\t\t{\n\t\t\t\t*o++ = *i++;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\thex <<= 4;\n\t\t\tif (i[2] >= 'A' && i[2] <= 'F')\n\t\t\t\thex += i[2]-'A'+10;\n\t\t\telse if (i[2] >= 'a' && i[2] <= 'f')\n\t\t\t\thex += i[2]-'a'+10;\n\t\t\telse if (i[2] >= '0' && i[2] <= '9')\n\t\t\t\thex += i[2]-'0';\n\t\t\telse\n\t\t\t{\n\t\t\t\t*o++ = *i++;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t*o++ = hex;\n\t\t\ti += 3;\n\t\t}\n\t\telse\n\t\t\t*o++ = *i++;\n\t}\n\t*o = 0;\n\tRETURN_TSTRING(resultbuf);\n}\n\n#ifdef WEBCLIENT\ntypedef struct\n{\n\tworld_t *w;\n\tfloat id;\n\tint selfnum;\n\tint spawncount;\n\n\tint response;\n\tvfsfile_t *file;\n} urigetcbctx_t;\nstatic void PR_uri_get_callback2(int iarg, void *data)\n{\n\turigetcbctx_t *ctx = data;\n\tworld_t *w = ctx->w;\n\tpubprogfuncs_t *prinst = w->progs;\n\tfloat id = ctx->id;\n\tint selfnum = ctx->selfnum;\n\tint replycode = ctx->response;\n\tfunc_t func;\n\n\t//make sure the progs vm is still active and okay.\n\tif (prinst && ctx->spawncount == w->spawncount)\n\t{\n\t\tfunc = PR_FindFunction(prinst, \"URI_Get_Callback\", PR_ANY);\n\t\tif (!func)\n\t\t\tCon_Printf(\"URI_Get_Callback missing\\n\");\n\t\telse if (func)\n\t\t{\n\t\t\tint len;\n\t\t\tchar *buffer;\n\t\t\tstruct globalvars_s *pr_globals = PR_globals(prinst, PR_CURRENT);\n\t\t\tint oldself = *w->g.self;\n\t\t\t*w->g.self = selfnum;\n\t\t\tG_FLOAT(OFS_PARM0) = id;\n\t\t\tG_FLOAT(OFS_PARM1) = (replycode!=200)?replycode:0;\t//for compat with DP, we change any 200s to 0.\n\t\t\tG_INT(OFS_PARM2) = 0;\n\t\t\tG_INT(OFS_PARM3) = 0;\n\n\t\t\tif (ctx->file)\n\t\t\t{\n\t\t\t\tlen = VFS_GETLEN(ctx->file);\n\t\t\t\tG_INT(OFS_PARM2) = prinst->AllocTempString(prinst, &buffer, len+1);\n\t\t\t\tlen = VFS_READ(ctx->file, buffer, len);\n\t\t\t\tif (len < 0)\n\t\t\t\t\tlen = 0;\n\t\t\t\tbuffer[len] = 0;\n\t\t\t\tG_INT(OFS_PARM3) = len;\n\t\t\t}\n\n\t\t\tPR_ExecuteProgram(prinst, func);\n\t\t\t*w->g.self = oldself;\n\t\t}\n\t}\n\tif (ctx->file)\n\t\tVFS_CLOSE(ctx->file);\n}\nstatic void PR_uri_get_callback(struct dl_download *dl)\n{\t//we might be sitting on a setmodel etc call (or really anywhere). don't call qc while the qc is already busy.\n\turigetcbctx_t ctx;\n\n\tctx.w = dl->user_ctx;\n\tctx.id = dl->user_float;\n\tctx.selfnum = dl->user_num;\n\tctx.spawncount = dl->user_sequence;\n\tctx.response = dl->replycode;\n\tctx.file = dl->file;\n\tdl->file = NULL;\t//steal the file pointer, so the download code can't close it before we can read it.\n\n\t//now post it to the timer queue, it'll get processed within a frame, ish, without screwing up any other qc state.\n\tCmd_AddTimer(0, PR_uri_get_callback2, 0, &ctx, sizeof(ctx));\n}\n#endif\n\n// uri_get() gets content from an URL and calls a callback \"uri_get_callback\" with it set as string; an unique ID of the transfer is returned\n// returns 1 on success, and then calls the callback with the ID, 0 or the HTTP status code, and the received data in a string\n//float(string uril, float id) uri_get = #513;\nvoid QCBUILTIN PF_uri_get  (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n#ifndef WEBCLIENT\n\tCon_Printf(\"uri_get is not implemented in this build\\n\");\n\tG_FLOAT(OFS_RETURN) = 0;\n#else\n\tworld_t *w = prinst->parms->user;\n\tstruct dl_download *dl = NULL;\n\n\tconst unsigned char *url = PR_GetStringOfs(prinst, OFS_PARM0);\n\tfloat id = G_FLOAT(OFS_PARM1);\n\tconst char *mimetype = (prinst->callargc >= 3)?PR_GetStringOfs(prinst, OFS_PARM2):\"\";\n\tconst char *dataorsep = (prinst->callargc >= 4)?PR_GetStringOfs(prinst, OFS_PARM3):\"\";\n\tint strbufid = (prinst->callargc >= 5)?G_FLOAT(OFS_PARM4):0;\n\t//float cryptokeyidx = (prinst->callargc >= 5)?G_FLOAT(OFS_PARM5):0;\t//DP feature, not supported in FTE. adds a X-D0-Blind-ID-Detached-Signature header signing [postdata\\0]querystring\n\n\tif (!pr_enable_uriget.ival)\n\t{\n\t\tCon_Printf(\"%s: blocking \\\"%s\\\"\\n\", pr_enable_uriget.name, url);\n\t\tG_FLOAT(OFS_RETURN) = 0;\n\t\treturn;\n\t}\n\n\tif (HTTP_CL_GetActiveDownloads() > 32)\n\t{\n\t\t//don't spam. I don't like it when you spam.\n\t\tCon_Printf(\"PF_uri_get(\\\"%s\\\",%g): too many pending downloads\\n\", url, id);\n\t\tG_FLOAT(OFS_RETURN) = 0;\n\t\treturn;\n\t}\n\n\tif (*mimetype)\n\t{\n\t\tCon_DPrintf(\"PF_uri_post(%s,%g)\\n\", url, id);\n\t\tif (strbufid)\n\t\t{\n\t\t\tsize_t bufno = strbufid-BUFSTRBASE;\n\t\t\tif (bufno < strbufmax && strbuflist[bufno].prinst == prinst)\n\t\t\t{\n\t\t\t\tconst char *glue = dataorsep;\n\t\t\t\tunsigned int gluelen = strlen(dataorsep);\n\t\t\t\tsize_t datalen, l, i;\n\t\t\t\tchar **strings;\n\t\t\t\tchar *data;\n\n\t\t\t\t//count neededlength\n\t\t\t\tstrings = strbuflist[bufno].strings;\n\t\t\t\tfor (i = 0, datalen = 0; i < strbuflist[bufno].used; i++)\n\t\t\t\t{\n\t\t\t\t\tif (strings[i])\n\t\t\t\t\t{\n\t\t\t\t\t\tif (datalen)\n\t\t\t\t\t\t\tdatalen += gluelen;\n\t\t\t\t\t\tdatalen += strlen(strings[i]);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t//concat it, with dataorsep separating each element\n\t\t\t\tdata = malloc(datalen+1);\n\t\t\t\tfor (i = 0, datalen = 0; i < strbuflist[bufno].used; i++)\n\t\t\t\t{\n\t\t\t\t\tif (strings[i])\n\t\t\t\t\t{\n\t\t\t\t\t\tif (datalen)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tmemcpy(data+datalen, glue, gluelen);\n\t\t\t\t\t\t\tdatalen += gluelen;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tl = strlen(strings[i]);\n\t\t\t\t\t\tmemcpy(data+datalen, strings[i], l);\n\t\t\t\t\t\tdatalen += l;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t//add the null and send it\n\t\t\t\tdata[datalen] = 0;\n\t\t\t\tdl = HTTP_CL_Put(url, mimetype, data, strlen(data), PR_uri_get_callback);\n\t\t\t\tfree(data);\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\t//simple data post.\n\t\t\tdl = HTTP_CL_Put(url, mimetype, dataorsep, strlen(dataorsep), PR_uri_get_callback);\n\t\t}\n\t}\n\telse\n\t{\n\t\tCon_DPrintf(\"PF_uri_get(%s,%g)\\n\", url, id);\n\t\tdl = HTTP_CL_Get(url, NULL, PR_uri_get_callback);\n\t}\n\tif (dl)\n\t{\n\t\tdl->user_ctx = w;\n\t\tdl->user_float = id;\n\t\tdl->user_num = *w->g.self;\n\t\tdl->user_sequence = w->spawncount;\n\t\tdl->isquery = true;\n\n#ifdef MULTITHREAD\n\t\tDL_CreateThread(dl, NULL, NULL);\n#endif\n\t\tG_FLOAT(OFS_RETURN) = 1;\n\t}\n\telse\n\t\tG_FLOAT(OFS_RETURN) = 0;\n#endif\n}\nvoid QCBUILTIN PF_netaddress_resolve(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char *address = PR_GetStringOfs(prinst, OFS_PARM0);\n\tint defaultport = (prinst->callargc > 1)?G_FLOAT(OFS_PARM1):0;\n\tnetadr_t adr;\n\tchar result[256];\n\tif (NET_StringToAdr(address, defaultport, &adr))\n\t\tRETURN_TSTRING(NET_AdrToString (result, sizeof(result), &adr));\n\telse\n\t\tRETURN_TSTRING(\"\");\n}\n\n////////////////////////////////////////////////////\n//Console functions\n\nstatic struct {\n\tchar *token;\n\tunsigned int start;\n\tunsigned int end;\n} *qctoken;\nstatic unsigned int qctoken_count;\nstatic unsigned int qctoken_max;\n\nvoid tokenize_flush(void)\n{\n\twhile(qctoken_count > 0)\n\t{\n\t\tqctoken_count--;\n\t\tfree(qctoken[qctoken_count].token);\n\t}\n\n\tqctoken_count = 0;\n\tfree(qctoken);\n\tqctoken = NULL;\n\tqctoken_max = 0;\n}\n\nvoid QCBUILTIN PF_ArgC  (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\t\t\t\t//85\t\t\t//float() argc;\n{\n\tG_FLOAT(OFS_RETURN) = qctoken_count;\n}\n\nint tokenizeqc(const char *str, qboolean dpfuckage)\n{\n\tconst char *start = str;\n\twhile(qctoken_count > 0)\n\t{\n\t\tqctoken_count--;\n\t\tfree(qctoken[qctoken_count].token);\n\t}\n\tfor (qctoken_count = 0; ; )\n\t{\n\t\tif (qctoken_count == qctoken_max)\n\t\t{\n\t\t\tvoid *n = realloc(qctoken, sizeof(*qctoken)*(qctoken_max+8));\n\t\t\tif (!n)\n\t\t\t\tbreak;\n\t\t\tqctoken_max+=8;\n\t\t\tqctoken = n;\n\t\t}\n\t\t/*skip whitespace here so the token's start is accurate*/\n\t\twhile (*str && *(unsigned char*)str <= ' ')\n\t\t\tstr++;\n\n\t\tif (!*str)\n\t\t\tbreak;\n\n\t\tqctoken[qctoken_count].start = str - start;\n\t\tstr = COM_StringParse (str, com_token, sizeof(com_token), false, dpfuckage);\n\t\tif (!str)\n\t\t\tbreak;\n\n\t\tqctoken[qctoken_count].token = strdup(com_token);\n\n\t\tqctoken[qctoken_count].end = str - start;\n\t\tqctoken_count++;\n\t}\n\treturn qctoken_count;\n}\n\n/*KRIMZON_SV_PARSECLIENTCOMMAND added these two - note that for compatibility with DP, this tokenize builtin is veeery vauge and doesn't match the console*/\nvoid QCBUILTIN PF_Tokenize  (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\t\t\t//84\t\t\t//void(string str) tokanize;\n{\n\tG_FLOAT(OFS_RETURN) = tokenizeqc(PR_GetStringOfs(prinst, OFS_PARM0), true);\n}\n\nvoid QCBUILTIN PF_tokenize_console  (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\t\t\t//84\t\t\t//void(string str) tokanize;\n{\n\tG_FLOAT(OFS_RETURN) = tokenizeqc(PR_GetStringOfs(prinst, OFS_PARM0), false);\n}\n\nvoid QCBUILTIN PF_tokenizebyseparator  (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char *str = PR_GetStringOfs(prinst, OFS_PARM0);\n\tconst char *sep[7];\n\tint seplen[7];\n\tint seps = 0, s;\n\tconst char *start = str;\n\tint tlen;\n\tint found;\n\n\twhile (seps < prinst->callargc - 1 && seps < 7)\n\t{\n\t\tsep[seps] = PR_GetStringOfs(prinst, OFS_PARM1 + seps*3);\n\t\tseplen[seps] = strlen(sep[seps]);\n\t\tseps++;\n\t}\n\n\twhile(qctoken_count > 0)\n\t{\n\t\tqctoken_count--;\n\t\tfree(qctoken[qctoken_count].token);\n\t}\n\n\tif (qctoken_count == qctoken_max)\n\t{\n\t\tvoid *n = realloc(qctoken, sizeof(*qctoken)*(qctoken_max+8));\n\t\tif (!n)\n\t\t{\n\t\t\tG_FLOAT(OFS_RETURN) = qctoken_count;\n\t\t\treturn;\n\t\t}\n\t\tqctoken_max+=8;\n\t\tqctoken = n;\n\t}\n\n\tqctoken[qctoken_count].start = 0;\n\tif (*str)\n\tfor(;;)\n\t{\n\t\tfound = false;\n\n\t\t/*see if its a separator*/\n\t\tif (!*str)\n\t\t{\n\t\t\tqctoken[qctoken_count].end = str - start;\n\t\t\tfound = -1;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tfor (s = 0; s < seps; s++)\n\t\t\t{\n\t\t\t\tif (!strncmp(str, sep[s], seplen[s]))\n\t\t\t\t{\n\t\t\t\t\tqctoken[qctoken_count].end = str - start;\n\t\t\t\t\tstr += seplen[s];\n\t\t\t\t\tfound = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t/*it was, split it out*/\n\t\tif (found)\n\t\t{\n\t\t\ttlen = qctoken[qctoken_count].end - qctoken[qctoken_count].start;\n\t\t\tqctoken[qctoken_count].token = malloc(tlen + 1);\n\t\t\tmemcpy(qctoken[qctoken_count].token, start + qctoken[qctoken_count].start, tlen);\n\t\t\tqctoken[qctoken_count].token[tlen] = 0;\n\n\t\t\tqctoken_count++;\n\t\t\tif (qctoken_count == qctoken_max)\n\t\t\t{\n\t\t\t\tvoid *n = realloc(qctoken, sizeof(*qctoken)*(qctoken_max+8));\n\t\t\t\tif (!n)\n\t\t\t\t\tbreak;\n\t\t\t\tqctoken_max+=8;\n\t\t\t\tqctoken = n;\n\t\t\t}\n\n\t\t\tif (found==-1)\n\t\t\t\tbreak;\n\t\t\tqctoken[qctoken_count].start = str - start;\n\t\t}\n\t\telse\n\t\t\tstr++;\n\t}\n\tG_FLOAT(OFS_RETURN) = qctoken_count;\n}\n\nvoid QCBUILTIN PF_argv_start_index  (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint idx = G_FLOAT(OFS_PARM0);\n\n\t/*negative indexes are relative to the end*/\n\tif (idx < 0)\n\t\tidx += qctoken_count;\t\n\n\tif ((unsigned int)idx >= qctoken_count)\n\t\tG_FLOAT(OFS_RETURN) = -1;\n\telse\n\t\tG_FLOAT(OFS_RETURN) = qctoken[idx].start;\n}\n\nvoid QCBUILTIN PF_argv_end_index  (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint idx = G_FLOAT(OFS_PARM0);\n\n\t/*negative indexes are relative to the end*/\n\tif (idx < 0)\n\t\tidx += qctoken_count;\t\n\n\tif ((unsigned int)idx >= qctoken_count)\n\t\tG_FLOAT(OFS_RETURN) = -1;\n\telse\n\t\tG_FLOAT(OFS_RETURN) = qctoken[idx].end;\n}\n\nvoid QCBUILTIN PF_ArgV  (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\t\t\t\t//86\t\t\t//string(float num) argv;\n{\n\tint idx = G_FLOAT(OFS_PARM0);\n\n\t/*negative indexes are relative to the end*/\n\tif (idx < 0)\n\t\tidx += qctoken_count;\n\n\tif ((unsigned int)idx >= qctoken_count)\n\t\tG_INT(OFS_RETURN) = 0;\n\telse\n\t\tRETURN_TSTRING(qctoken[idx].token);\n}\n\nvoid QCBUILTIN PF_argescape(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tchar temp[8192];\n\tconst char *str = PR_GetStringOfs(prinst, OFS_PARM0);\n\tRETURN_TSTRING(COM_QuotedString(str, temp, sizeof(temp), false));\n}\n\n//Console functions\n////////////////////////////////////////////////////\n//Maths functions\n\nvoid QCBUILTIN PF_random (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\t//don't return 1 (it would break array[random()*array.length];\n\t//don't return 0 either, it would break the self.nextthink = time+random()*foo; lines in walkmonster_start, resulting rarely in statue-monsters.\n\tG_FLOAT(OFS_RETURN) = (rand ()&0x7fff) / ((float)0x08000) + (0.5/0x08000);\n\n\tif (prinst->callargc)\n\t{\n\t\tif (prinst->callargc == 1)\n\t\t\tG_FLOAT(OFS_RETURN) *= G_FLOAT(OFS_PARM0);\n\t\telse\n\t\t\tG_FLOAT(OFS_RETURN) = G_FLOAT(OFS_PARM0) + G_FLOAT(OFS_RETURN)*(G_FLOAT(OFS_PARM1)-G_FLOAT(OFS_PARM0));\n\t}\n}\n\n//float(float number, float quantity) bitshift = #218;\nvoid QCBUILTIN PF_bitshift(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint bitmask;\n\tint shift;\n\n\tbitmask = G_FLOAT(OFS_PARM0);\n\tshift = G_FLOAT(OFS_PARM1);\n\n\tif (shift < 0)\n\t\tbitmask >>= -shift;\n\telse\n\t\tbitmask <<= shift;\n\n\tG_FLOAT(OFS_RETURN) = bitmask;\n}\n\n//float(float a, floats) min = #94\nvoid QCBUILTIN PF_min (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint i;\n\tfloat f;\n\n\tif (prinst->callargc == 2)\n\t{\n\t\tG_FLOAT(OFS_RETURN) = min(G_FLOAT(OFS_PARM0), G_FLOAT(OFS_PARM1));\n\t}\n\telse if (prinst->callargc >= 3)\n\t{\n\t\tf = G_FLOAT(OFS_PARM0);\n\t\tfor (i = 1; i < prinst->callargc; i++)\n\t\t{\n\t\t\tif (G_FLOAT((OFS_PARM0 + i * 3)) < f)\n\t\t\t\tf = G_FLOAT((OFS_PARM0 + i * 3));\n\t\t}\n\t\tG_FLOAT(OFS_RETURN) = f;\n\t}\n\telse\n\t\tPR_BIError(prinst, \"PF_min: must supply at least 2 floats\\n\");\n}\n\n//float(float a, floats) max = #95\nvoid QCBUILTIN PF_max (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint i;\n\tfloat f;\n\n\tif (prinst->callargc == 2)\n\t{\n\t\tG_FLOAT(OFS_RETURN) = max(G_FLOAT(OFS_PARM0), G_FLOAT(OFS_PARM1));\n\t}\n\telse if (prinst->callargc >= 3)\n\t{\n\t\tf = G_FLOAT(OFS_PARM0);\n\t\tfor (i = 1; i < prinst->callargc; i++) {\n\t\t\tif (G_FLOAT((OFS_PARM0 + i * 3)) > f)\n\t\t\t\tf = G_FLOAT((OFS_PARM0 + i * 3));\n\t\t}\n\t\tG_FLOAT(OFS_RETURN) = f;\n\t}\n\telse\n\t{\n\t\tPR_BIError(prinst, \"PF_min: must supply at least 2 floats\\n\");\n\t}\n}\n\n//float(float minimum, float val, float maximum) bound = #96\nvoid QCBUILTIN PF_bound (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tif (G_FLOAT(OFS_PARM1) > G_FLOAT(OFS_PARM2))\n\t\tG_FLOAT(OFS_RETURN) = G_FLOAT(OFS_PARM2);\n\telse if (G_FLOAT(OFS_PARM1) < G_FLOAT(OFS_PARM0))\n\t\tG_FLOAT(OFS_RETURN) = G_FLOAT(OFS_PARM0);\n\telse\n\t\tG_FLOAT(OFS_RETURN) = G_FLOAT(OFS_PARM1);\n}\n\nvoid QCBUILTIN PF_mod (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tfloat a = G_FLOAT(OFS_PARM0);\n\tfloat n = G_FLOAT(OFS_PARM1);\n\n\tif (n == 0)\n\t{\n\t\tPR_RunWarning(prinst, \"mod by zero\\n\");\n\t\tG_FLOAT(OFS_RETURN) = 0;\n\t}\n\telse\n\t{\n\t\t//because QC is inherantly floaty, lets use floats.\n\t\tG_FLOAT(OFS_RETURN) = a - (n * (int)(a/n));\n//\t\tG_FLOAT(OFS_RETURN) = a % n;\n\t}\n}\n\nvoid QCBUILTIN PF_Sin (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tG_FLOAT(OFS_RETURN) = sin(G_FLOAT(OFS_PARM0));\n}\nvoid QCBUILTIN PF_Cos (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tG_FLOAT(OFS_RETURN) = cos(G_FLOAT(OFS_PARM0));\n}\nvoid QCBUILTIN PF_Sqrt (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tG_FLOAT(OFS_RETURN) = sqrt(G_FLOAT(OFS_PARM0));\n}\nvoid QCBUILTIN PF_pow (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tG_FLOAT(OFS_RETURN) = pow(G_FLOAT(OFS_PARM0), G_FLOAT(OFS_PARM1));\n}\nvoid QCBUILTIN PF_asin (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tG_FLOAT(OFS_RETURN) = asin(G_FLOAT(OFS_PARM0));\n}\nvoid QCBUILTIN PF_acos (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tG_FLOAT(OFS_RETURN) = acos(G_FLOAT(OFS_PARM0));\n}\nvoid QCBUILTIN PF_atan (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tG_FLOAT(OFS_RETURN) = atan(G_FLOAT(OFS_PARM0));\n}\nvoid QCBUILTIN PF_atan2 (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tG_FLOAT(OFS_RETURN) = atan2(G_FLOAT(OFS_PARM0), G_FLOAT(OFS_PARM1));\n}\nvoid QCBUILTIN PF_tan (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tG_FLOAT(OFS_RETURN) = tan(G_FLOAT(OFS_PARM0));\n}\n\nvoid QCBUILTIN PF_fabs (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tfloat\tv;\n\tv = G_FLOAT(OFS_PARM0);\n\tG_FLOAT(OFS_RETURN) = fabs(v);\n}\n\nvoid QCBUILTIN PF_rint (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tfloat\tf;\n\tf = G_FLOAT(OFS_PARM0);\n\tif (f > 0)\n\t\tG_FLOAT(OFS_RETURN) = (int)(f + 0.5);\n\telse\n\t\tG_FLOAT(OFS_RETURN) = (int)(f - 0.5);\n}\n\nvoid QCBUILTIN PF_floor (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tG_FLOAT(OFS_RETURN) = floor(G_FLOAT(OFS_PARM0));\n}\n\nvoid QCBUILTIN PF_ceil (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tG_FLOAT(OFS_RETURN) = ceil(G_FLOAT(OFS_PARM0));\n}\n\nvoid QCBUILTIN PF_anglemod (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tfloat v = G_FLOAT(OFS_PARM0);\n\n\twhile (v >= 360)\n\t\tv = v - 360;\n\twhile (v < 0)\n\t\tv = v + 360;\n\n\tG_FLOAT(OFS_RETURN) = v;\n}\nvoid QCBUILTIN PF_anglesub (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tfloat v = G_FLOAT(OFS_PARM0) - G_FLOAT(OFS_PARM1);\n\n\twhile (v > 180)\n\t\tv = v - 360;\n\twhile (v < -180)\n\t\tv = v + 360;\n\n\tG_FLOAT(OFS_RETURN) = v;\n}\n\n//void(vector dir) vectorvectors\n//Writes new values for v_forward, v_up, and v_right based on the given forward vector\nvoid QCBUILTIN PF_vectorvectors (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tworld_t *world = prinst->parms->user;\n\n\tVectorCopy(G_VECTOR(OFS_PARM0), world->g.v_forward);\n\tVectorNormalize(world->g.v_forward);\n\tVectorVectors(world->g.v_forward, world->g.v_right, world->g.v_up);\n}\n\nvoid QCBUILTIN PF_crossproduct (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tVM_VECTORARG(v0, OFS_PARM0);\n\tVM_VECTORARG(v1, OFS_PARM1);\n\tCrossProduct(v0, v1, G_VECTOR(OFS_RETURN));\n}\n\n//Maths functions\n////////////////////////////////////////////////////\n\n#ifdef HAVE_LEGACY\nunsigned int FTEToDPContents(unsigned int contents)\n{\n\tunsigned int r = 0;\n\tif (contents & FTECONTENTS_SOLID)\n\t\tr |= DPCONTENTS_SOLID;\n\tif (contents & FTECONTENTS_WATER)\n\t\tr |= DPCONTENTS_WATER;\n\tif (contents & FTECONTENTS_SLIME)\n\t\tr |= DPCONTENTS_SLIME;\n\tif (contents & FTECONTENTS_LAVA)\n\t\tr |= DPCONTENTS_LAVA;\n\tif (contents & FTECONTENTS_SKY)\n\t\tr |= DPCONTENTS_SKY;\n\tif (contents & FTECONTENTS_BODY)\n\t\tr |= DPCONTENTS_BODY;\n\tif (contents & FTECONTENTS_CORPSE)\n\t\tr |= DPCONTENTS_CORPSE;\n\tif (contents & Q3CONTENTS_NODROP)\n\t\tr |= DPCONTENTS_NODROP;\n\tif (contents & FTECONTENTS_PLAYERCLIP)\n\t\tr |= DPCONTENTS_PLAYERCLIP;\n\tif (contents & FTECONTENTS_MONSTERCLIP)\n\t\tr |= DPCONTENTS_MONSTERCLIP;\n\tif (contents & Q3CONTENTS_DONOTENTER)\n\t\tr |= DPCONTENTS_DONOTENTER;\n\tif (contents & Q3CONTENTS_BOTCLIP)\n\t\tr |= DPCONTENTS_BOTCLIP;\n//\tif (contents & FTECONTENTS_OPAQUE)\n//\t\tr |= DPCONTENTS_OPAQUE;\n\treturn r;\n}\n#endif\n\n/*\n===============\nPF_droptofloor\n\nvoid() droptofloor\n===============\n*/\nvoid QCBUILTIN PF_droptofloor (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\textern cvar_t pr_droptofloorunits;\n\tworld_t *world = prinst->parms->user;\n\twedict_t\t*ent;\n\tvec3_t\t\tend;\n\tvec3_t\t\tstart;\n\ttrace_t\t\ttrace;\n\tconst pvec_t *gravitydir;\n\n\tent = PROG_TO_WEDICT(prinst, *world->g.self);\n\n\tif (ent->xv->gravitydir[2] || ent->xv->gravitydir[1] || ent->xv->gravitydir[0])\n\t\tgravitydir = ent->xv->gravitydir;\n\telse\n\t\tgravitydir = world->g.defaultgravitydir;\n\n\tVectorCopy (ent->v->origin, end);\n\tif (pr_droptofloorunits.value > 0)\n\t\tVectorMA(end, pr_droptofloorunits.value, gravitydir, end);\n\telse\n\t\tVectorMA(end, 256, gravitydir, end);\n\n\tVectorCopy (ent->v->origin, start);\n\ttrace = World_Move (world, start, ent->v->mins, ent->v->maxs, end, MOVE_NORMAL, ent);\n\n\tif (trace.allsolid && sv_gameplayfix_droptofloorstartsolid.ival && gravitydir[2] == -1)\n\t{\n\t\t//try again but with a traceline, something is better than nothing.\n\t\tvec3_t offset;\n\t\tVectorAvg(ent->v->maxs, ent->v->mins, offset);\n\t\toffset[2] = ent->v->mins[2];\n\t\tVectorAdd(start, offset, start);\n\t\tVectorAdd(end, offset, end);\n\t\ttrace = World_Move (world, start, vec3_origin, vec3_origin, end, MOVE_NORMAL, ent);\n\t\tif (trace.fraction < 1)\n\t\t{\n\t\t\tVectorSubtract (trace.endpos, offset, ent->v->origin);\n\t\t\tWorld_LinkEdict (world, ent, false);\n\t\t\tent->v->flags = (int)ent->v->flags | FL_ONGROUND;\n\t\t\tent->v->groundentity = EDICT_TO_PROG(prinst, trace.ent);\n\t\t\tG_FLOAT(OFS_RETURN) = 1;\n\t\t}\n\t}\n\telse if (trace.fraction == 1 || trace.allsolid)\n\t\tG_FLOAT(OFS_RETURN) = 0;\n\telse\n\t{\n\t\tVectorCopy (trace.endpos, ent->v->origin);\n\t\tWorld_LinkEdict (world, ent, false);\n\t\tent->v->flags = (int)ent->v->flags | FL_ONGROUND;\n\t\tent->v->groundentity = EDICT_TO_PROG(prinst, trace.ent);\n\t\tG_FLOAT(OFS_RETURN) = 1;\n\t}\n}\n\n/*\n=============\nPF_checkbottom\n=============\n*/\nvoid QCBUILTIN PF_checkbottom (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tworld_t *world = prinst->parms->user;\n\twedict_t\t*ent;\n\tvec3_t\t\tgravup;\n\n\tent = G_WEDICT(prinst, OFS_PARM0);\n\n\tif (ent->xv->gravitydir[0] || ent->xv->gravitydir[1] || ent->xv->gravitydir[2])\n\t\tVectorNegate(ent->xv->gravitydir, gravup);\n\telse\n\t\tVectorSet(gravup, 0, 0, 1);\n\n\tG_FLOAT(OFS_RETURN) = World_CheckBottom (world, ent, gravup);\n}\n\n////////////////////////////////////////////////////\n//Vector functions\n\n//vector() randomvec = #91\nvoid QCBUILTIN PF_randomvector (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tvec3_t temp;\n\tdo\n\t{\n\t\ttemp[0] = (rand() & 32767) * (2.0 / 32767.0) - 1.0;\n\t\ttemp[1] = (rand() & 32767) * (2.0 / 32767.0) - 1.0;\n\t\ttemp[2] = (rand() & 32767) * (2.0 / 32767.0) - 1.0;\n\t} while (DotProduct(temp, temp) >= 1);\n\tVectorCopy (temp, G_VECTOR(OFS_RETURN));\n}\n\n\n/*\n==============\nPF_changeyaw\n\nThis was a major timewaster in progs, so it was converted to C\n\nFIXME: add gravitydir support\n==============\n*/\nfloat World_changeyaw (wedict_t *ent);\nvoid QCBUILTIN PF_changeyaw (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tworld_t *w = prinst->parms->user;\n\twedict_t\t\t*ent;\n//\tfloat\t\tideal, current, move, speed;\n\n\tent = PROG_TO_WEDICT(prinst, *w->g.self);\n\n\tWorld_changeyaw(ent);\n}\n\n//void() changepitch = #63;\n//FIXME: support gravitydir\nvoid QCBUILTIN PF_changepitch (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tworld_t *w = prinst->parms->user;\n\twedict_t\t\t*ent;\n\tfloat\t\tideal, current, move, speed;\n\n\tent = PROG_TO_WEDICT(prinst, *w->g.self);\n\tcurrent = anglemod( ent->v->angles[0] );\n\tideal = ent->xv->idealpitch;\n\tspeed = ent->xv->pitch_speed;\n\n\tif (current == ideal)\n\t\treturn;\n\tmove = ideal - current;\n\tif (ideal > current)\n\t{\n\t\tif (move >= 180)\n\t\t\tmove = move - 360;\n\t}\n\telse\n\t{\n\t\tif (move <= -180)\n\t\t\tmove = move + 360;\n\t}\n\tif (move > 0)\n\t{\n\t\tif (move > speed)\n\t\t\tmove = speed;\n\t}\n\telse\n\t{\n\t\tif (move < -speed)\n\t\t\tmove = -speed;\n\t}\n\n\tent->v->angles[0] = anglemod (current + move);\n}\n\n//float vectoyaw(vector, optional entity reference)\nvoid QCBUILTIN PF_vectoyaw (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tfloat\t*value1;\n\tfloat\tx, y;\n\tfloat\tyaw;\n\n\tvalue1 = G_VECTOR(OFS_PARM0);\n\n\tif (prinst->callargc >= 2)\n\t{\n\t\tvec3_t axis[3];\n\t\tWorld_GetEntGravityAxis(G_WEDICT(prinst, OFS_PARM1), axis);\n\t\tx = DotProduct(value1, axis[0]);\n\t\ty = DotProduct(value1, axis[1]);\n\t}\n\telse\n\t{\n\t\tx = value1[0];\n\t\ty = value1[1];\n\t}\n\n\tif (y == 0 && x == 0)\n\t\tyaw = 0;\n\telse\n\t{\n\t\tyaw = (int) (atan2(y, x) * 180 / M_PI);\n\t\tif (yaw < 0)\n\t\t\tyaw += 360;\n\t}\n\n\tG_FLOAT(OFS_RETURN) = yaw;\n}\n\n//float(vector) vlen\nvoid QCBUILTIN PF_vlen (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tfloat\t*value1 = G_VECTOR(OFS_PARM0);\n\tG_FLOAT(OFS_RETURN) = sqrt(DotProduct(value1, value1));\n}\n//float(vector) vhlen\nvoid QCBUILTIN PF_vhlen (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tfloat\t*value1 = G_VECTOR(OFS_PARM0);\n\tG_FLOAT(OFS_RETURN) = sqrt(DotProduct2(value1, value1));\n}\n\n//vector vectoangles(vector)\nvoid QCBUILTIN PF_vectoangles (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tfloat\t*value1, *up;\n\n\tvalue1 = G_VECTOR(OFS_PARM0);\n\tif (prinst->callargc >= 2)\n\t\tup = G_VECTOR(OFS_PARM1);\n\telse\n\t\tup = NULL;\n\n\tVectorAngles(value1, up, G_VECTOR(OFS_RETURN), true);\n}\n\n//vector normalize(vector)\nvoid QCBUILTIN PF_normalize (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tfloat\t*value1;\n\tvec3_t\tnewvalue;\n\tfloat\tnewf;\n\n\tvalue1 = G_VECTOR(OFS_PARM0);\n\n\tnewf = value1[0] * value1[0] + value1[1] * value1[1] + value1[2]*value1[2];\n\tnewf = sqrt(newf);\n\n\tif (newf == 0)\n\t\tnewvalue[0] = newvalue[1] = newvalue[2] = 0;\n\telse\n\t{\n\t\tnewf = 1/newf;\n\t\tnewvalue[0] = value1[0] * newf;\n\t\tnewvalue[1] = value1[1] * newf;\n\t\tnewvalue[2] = value1[2] * newf;\n\t}\n\n\tVectorCopy (newvalue, G_VECTOR(OFS_RETURN));\n}\n\nvoid QCBUILTIN PF_rotatevectorsbyangles (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tworld_t *w = prinst->parms->user;\n\n\tfloat *ang = G_VECTOR(OFS_PARM0);\n\tvec3_t src[3], trans[3], res[3];\n\tAngleVectorsMesh(ang, trans[0], trans[1], trans[2]);\n\tVectorInverse(trans[1]);\n\n\tVectorCopy(w->g.v_forward, src[0]);\n\tVectorNegate(w->g.v_right, src[1]);\n\tVectorCopy(w->g.v_up, src[2]);\n\n\tR_ConcatRotations(trans, src, res);\n\n\tVectorCopy(res[0], w->g.v_forward);\n\tVectorNegate(res[1], w->g.v_right);\n\tVectorCopy(res[2], w->g.v_up);\n}\n\nvoid QCBUILTIN PF_rotatevectorsbymatrix (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tworld_t *w = prinst->parms->user;\n\tvec3_t base[3], trans[3], res[3];\n\n\tVectorCopy(G_VECTOR(OFS_PARM0), base[0]);\n\tVectorNegate(G_VECTOR(OFS_PARM1), base[1]);\n\tVectorCopy(G_VECTOR(OFS_PARM2), base[2]);\n\n\tVectorCopy(w->g.v_forward, trans[0]);\n\tVectorNegate(w->g.v_right, trans[1]);\n\tVectorCopy(w->g.v_up, trans[2]);\n\n\tR_ConcatRotations(trans, base, res);\n\n\tVectorCopy(res[0], w->g.v_forward);\n\tVectorNegate(res[1], w->g.v_right);\n\tVectorCopy(res[2], w->g.v_up);\n}\n\n//Vector functions\n////////////////////////////////////////////////////\n//Progs internals\n\nqcstate_t *PR_CreateThread(pubprogfuncs_t *prinst, float retval, float resumetime, qboolean wait)\n{\n\tworld_t *world = prinst->parms->user;\n\tqcstate_t *state;\n\tedict_t *ed;\n\n\tstate = prinst->parms->memalloc(sizeof(qcstate_t));\n\tstate->next = world->qcthreads;\n\tworld->qcthreads = state;\n\tstate->resumetime = resumetime;\n\n\tif (prinst->edicttable_length)\n\t{\n\t\ted = PROG_TO_EDICT(prinst, world->g.self?*world->g.self:0);\n\t\tstate->self = NUM_FOR_EDICT(prinst, ed);\n\t\tstate->selfid = (prinst==svprogfuncs&&ed->ereftype==ER_ENTITY)?ed->xv->uniquespawnid:0;\n\t\ted = PROG_TO_EDICT(prinst, world->g.self?*world->g.self:0);\n\t\tstate->other = NUM_FOR_EDICT(prinst, ed);\n\t\tstate->otherid = (prinst==svprogfuncs&&ed->ereftype==ER_ENTITY)?ed->xv->uniquespawnid:0;\n\t}\n\telse\t//allows us to call this during init().\n\t\tstate->self = state->other = state->selfid = state->otherid = 0;\n\tstate->thread = prinst->Fork(prinst);\n\tstate->waiting = wait;\n\tstate->returnval = retval;\n\treturn state;\n}\nvoid PR_RunThreads(world_t *world)\n{\n\tstruct globalvars_s *pr_globals;\n\tedict_t *ed;\n\n\tqcstate_t *state = world->qcthreads, *next;\n\tworld->qcthreads = NULL;\n\twhile(state)\n\t{\n\t\tpubprogfuncs_t *prinst = world->progs;\n\t\tnext = state->next;\n\n\t\tif (state->resumetime > (world->g.time?*world->g.time:0) || state->waiting)\n\t\t{\t//not time yet, reform original list.\n\t\t\tstate->next = world->qcthreads;\n\t\t\tworld->qcthreads = state;\n\t\t}\n\t\telse\n\t\t{\t//call it and forget it ever happened. The Sleep biltin will recreate if needed.\n\t\t\tpr_globals = PR_globals(prinst, PR_CURRENT);\n\n\t\t\tif (world->g.self)\n\t\t\t{\n\t\t\t\t//restore the thread's self variable, if applicable.\n\t\t\t\ted = PROG_TO_EDICT(prinst, state->self);\n\t\t\t\tif ((prinst==svprogfuncs?ed->xv->uniquespawnid:0) != state->selfid)\n\t\t\t\t\ted = prinst->edicttable[0];\n\t\t\t\t*world->g.self = EDICT_TO_PROG(prinst, ed);\n\t\t\t}\n\n\t\t\tif (world->g.other)\n\t\t\t{\n\t\t\t\t//restore the thread's other variable, if applicable\n\t\t\t\ted = PROG_TO_EDICT(prinst, state->other);\n\t\t\t\tif ((prinst==svprogfuncs?ed->xv->uniquespawnid:0) != state->otherid)\n\t\t\t\t\ted = prinst->edicttable[0];\n\t\t\t\t*world->g.other = EDICT_TO_PROG(prinst, ed);\n\t\t\t}\n\n\t\t\tG_FLOAT(OFS_RETURN) = state->returnval;\t//return value of fork or sleep\n\n\t\t\tprinst->RunThread(prinst, state->thread);\n\t\t\tprinst->parms->memfree(state->thread);\n\t\t\tprinst->parms->memfree(state);\n\t\t}\n\n\t\tstate = next;\n\t}\n}\nvoid PR_ClearThreads(pubprogfuncs_t *prinst)\n{\n\tworld_t *world;\n\tqcstate_t *state, *next;\n\tif (!prinst)\n\t\treturn; //shoo!\n\tworld = prinst->parms->user;\n\tstate = world->qcthreads;\n\tworld->qcthreads = NULL;\n\twhile(state)\n\t{\n\t\tnext = state->next;\n\n\t\t//free the memory.\n\t\tprinst->parms->memfree(state->thread);\n\t\tprinst->parms->memfree(state);\n\n\t\tstate = next;\n\t}\n}\nvoid QCBUILTIN PF_Abort(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tprinst->AbortStack(prinst);\n}\nvoid QCBUILTIN PF_Sleep(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tworld_t *world = prinst->parms->user;\n\tfloat sleeptime;\n\n\tsleeptime = G_FLOAT(OFS_PARM0);\n\n\tPR_CreateThread(prinst, 1, (world->g.time?*world->g.time:0) + sleeptime, false);\n\n\tprinst->AbortStack(prinst);\n}\nvoid QCBUILTIN PF_Fork(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tworld_t *world = prinst->parms->user;\n\tfloat sleeptime;\n\n\tif (svprogfuncs->callargc >= 1)\n\t\tsleeptime = G_FLOAT(OFS_PARM0);\n\telse\n\t\tsleeptime = 0;\n\n\tPR_CreateThread(prinst, 1, (world->g.time?*world->g.time:0) + sleeptime, false);\n\n//\tPRSV_RunThreads();\n\n\tG_FLOAT(OFS_RETURN) = 0;\n}\n\n//this func calls a function in another progs\n//it works in the same way as the above func, except that it calls by reference to a function, as opposed to by it's name\n//used for entity function variables - not actually needed anymore\nvoid QCBUILTIN PF_externrefcall (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n//\tint progsnum;\n\tfunc_t f;\n\tint i;\n//\tprogsnum = G_PROG(OFS_PARM0);\n\tf = G_INT(OFS_PARM1);\n\n\tfor (i = OFS_PARM0; i < OFS_PARM5; i+=3)\n\t\tVectorCopy(G_VECTOR(i+(2*3)), G_VECTOR(i));\n\n\tPR_ExecuteProgram(prinst, f);\n}\n\nvoid QCBUILTIN PF_externset (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\t//set a value in another progs\n{\n\tint n = G_PROG(OFS_PARM0);\n\teval_t *v = (eval_t*)&G_INT(OFS_PARM1);\n\tconst char *varname = PF_VarString(prinst, 2, pr_globals);\n\teval_t *var;\n\tetype_t t = ev_void;\n\n\tvar = PR_FindGlobal(prinst, varname, n, &t);\n\n\tif (var)\n\t{\n\t\tif (t == ev_vector)\n\t\t\tVectorCopy(v->_vector, var->_vector);\n\t\telse if (t == ev_int64 || t == ev_uint64 || t == ev_double)\n\t\t\tvar->i64 = v->i64;\n\t\telse\n\t\t\tvar->_int = v->_int;\n\t}\n}\n\nvoid QCBUILTIN PF_externvalue (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\t//return a value in another progs\n{\n\tint n = G_PROG(OFS_PARM0);\n\tconst char *varname = PF_VarString(prinst, 1, pr_globals);\n\teval_t *var;\n\n\tif (*varname == '&')\n\t{\n\t\t//return its address instead of its value, for pointer use.\n\t\tvar = prinst->FindGlobal(prinst, varname+1, n, NULL);\n\t\tif (var)\n\t\t\tG_INT(OFS_RETURN) = (char*)var - prinst->stringtable;\n\t\telse\n\t\t\tG_INT(OFS_RETURN) = 0;\n\t\tG_INT(OFS_RETURN+1) = 0;\n\t\tG_INT(OFS_RETURN+2) = 0;\n\t}\n\telse\n\t{\n\t\tvar = prinst->FindGlobal(prinst, varname, n, NULL);\n\n\t\tif (var)\n\t\t{\n\t\t\tG_INT(OFS_RETURN+0) = ((int*)&var->_int)[0];\n\t\t\tG_INT(OFS_RETURN+1) = ((int*)&var->_int)[1];\n\t\t\tG_INT(OFS_RETURN+2) = ((int*)&var->_int)[2];\n\t\t}\n\t\telse\n\t\t{\n\t\t\tn = prinst->FindFunction(prinst, varname, n);\n\t\t\tG_INT(OFS_RETURN) = n;\n\t\t\tG_INT(OFS_RETURN+1) = 0;\n\t\t\tG_INT(OFS_RETURN+2) = 0;\n\t\t}\n\t}\n}\n\nvoid QCBUILTIN PF_externcall (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\t//this func calls a function in another progs (by name)\n{\n\tint progsnum;\n\tconst char *funcname;\n\tint i;\n\tstring_t failedst = G_INT(OFS_PARM1);\n\tfunc_t f;\n\n\tprogsnum = G_PROG(OFS_PARM0);\n\tfuncname = PR_GetStringOfs(prinst, OFS_PARM1);\n\n\tf = PR_FindFunction(prinst, funcname, progsnum);\n\tif (f)\n\t{\n\t\tfor (i = OFS_PARM0; i < OFS_PARM5; i+=3)\n\t\t\tVectorCopy(G_VECTOR(i+(2*3)), G_VECTOR(i));\n\n\t\tPR_ExecuteProgram(prinst, f);\n\t}\n\telse\n\t{\n\t\tf = PR_FindFunction(prinst, \"MissingFunc\", progsnum);\n\t\tif (!f)\n\t\t{\n\t\t\tPR_BIError(prinst, \"Couldn't find function %s\", funcname);\n\t\t\treturn;\n\t\t}\n\n\t\tfor (i = OFS_PARM0; i < OFS_PARM6; i+=3)\n\t\t\tVectorCopy(G_VECTOR(i+(1*3)), G_VECTOR(i));\n\t\tG_INT(OFS_PARM0) = failedst;\n\n\t\tPR_ExecuteProgram(prinst, f);\n\t}\n}\n\nvoid QCBUILTIN PF_setwatchpoint (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char *desc = PR_GetStringOfs(prinst, OFS_PARM0);\n\tint type = G_FLOAT(OFS_PARM1);\n\tint qcptr = G_INT(OFS_PARM2);\n\tchar variable[64];\n\n\tif (type == ev_float)\n\t\tQ_snprintfz(variable,sizeof(variable), \"*(float*)%#x\", qcptr);\n\telse if (type == ev_string)\n\t\tQ_snprintfz(variable,sizeof(variable), \"*(string*)%#x\", qcptr);\n\telse\n\t\tQ_snprintfz(variable,sizeof(variable), \"*(int*)%#x\", qcptr);\n\n\tif (prinst->SetWatchPoint(prinst, desc, variable))\n\t\tCon_DPrintf(\"Watchpoint set\\n\");\n\telse\n\t\tCon_Printf(\"Watchpoint failure\\n\");\n}\n\nvoid QCBUILTIN PF_traceon (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tprinst->debug_trace = DEBUG_TRACE_INTO;\n}\n\nvoid QCBUILTIN PF_traceoff (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tprinst->debug_trace = DEBUG_TRACE_OFF;\n}\nvoid QCBUILTIN PF_coredump (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tsize_t size = 1024*1024*8;\n\tchar *buffer = BZ_Malloc(size);\n\tprinst->save_ents(prinst, buffer, &size, size, 3);\n\tCOM_WriteFile(\"core.txt\", FS_GAMEONLY, buffer, size);\n\tBZ_Free(buffer);\n}\nvoid QCBUILTIN PF_eprint (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tsize_t max = 1024*1024;\n\tsize_t size = 0;\n\tchar *buffer = BZ_Malloc(max);\n\tchar *buf;\n\tbuf = prinst->saveent(prinst, buffer, &size, max, (struct edict_s*)G_WEDICT(prinst, OFS_PARM0));\n\tCon_Printf(\"Entity %i:\\n%s\\n\", G_EDICTNUM(prinst, OFS_PARM0), buf);\n\tBZ_Free(buffer);\n}\n\nvoid QCBUILTIN PF_break (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tPR_RunWarning (prinst, \"break statement\");\n}\n\nvoid QCBUILTIN PF_error (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char\t*s;\n\n\ts = PF_VarString(prinst, 0, pr_globals);\n/*\tCon_Printf (\"======SERVER ERROR in %s:\\n%s\\n\", PR_GetString(pr_xfunction->s_name) ,s);\n\ted = PROG_TO_EDICT(pr_global_struct->self);\n\tED_Print (ed);\n*/\n\n\tPR_StackTrace(prinst, false);\n\n\tCon_Printf(\"%s\\n\", s);\n\n\tif (developer.value)\n\t{\n//\t\tSV_Error (\"Program error: %s\", s);\n\t\tPF_break(prinst, pr_globals);\n\t\tprinst->debug_trace = DEBUG_TRACE_INTO;\n\t}\n\telse\n\t{\n\t\tPR_AbortStack(prinst);\n\t\tPR_BIError (prinst, \"Program error: %s\", s);\n\t}\n}\n\n//Progs internals\n////////////////////////////////////////////////////\n//System\n\n//Sends text over to the client's execution buffer\nvoid QCBUILTIN PF_localcmd (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char\t*str;\n\n\tstr = PF_VarString(prinst, 0, pr_globals);\n\tif (developer.ival >= 2)\n\t{\n\t\tPR_StackTrace(prinst, false);\n\t\tCon_Printf(\"localcmd: %s\\n\", str);\n\t}\n\tif (!strcmp(str, \"host_framerate 0\\n\"))\n\t\tCbuf_AddText (\"sv_mintic 0\\n\", RESTRICT_INSECURE);\t//hmm... do this better...\n\telse\n\t\tCbuf_AddText (str, RESTRICT_INSECURE);\n}\n\nvoid QCBUILTIN PF_gettimed (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\t//usng doubles is for longer uptimes rather than extra precision. we artificially limit precision to reduce spectre sidebands, though I'm not sure how useful it'd be.\n\tint timer = (prinst->callargc > 0)?G_INT(OFS_PARM0):0;\n\tswitch(timer)\n\t{\n\tdefault:\n\tcase 0:\t\t//cached time at start of frame\n\t\tG_DOUBLE(OFS_RETURN) = realtime;\n\t\tbreak;\n\tcase 1:\t\t//actual time, ish. we round to milliseconds to reduce spectre exposure\n\t\tG_DOUBLE(OFS_RETURN) = (qint64_t)Sys_Milliseconds()/1000.0;\n\t\tbreak;\n\t//case 2:\t//highres.. looks like time into the frame\n\t//case 3:\t//uptime\n\t//case 4:\t//cd track\n#ifndef SERVERONLY\n\tcase 5:\t\t//sim time\n\t\tG_DOUBLE(OFS_RETURN) = cl.time;\n\t\tbreak;\n#endif\n\t}\n}\nvoid QCBUILTIN PF_gettimef (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tG_INT(OFS_PARM0) = G_FLOAT(OFS_PARM0);\n\tPF_gettimed (prinst, pr_globals);\n\tG_FLOAT(OFS_RETURN) = G_DOUBLE(OFS_RETURN);\n}\n\nvoid QCBUILTIN PF_calltimeofday (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tdate_t date;\n\tfunc_t f;\n\n\tf = PR_FindFunction(prinst, \"timeofday\", PR_ANY);\n\tif (f)\n\t{\n\t\tCOM_TimeOfDay(&date);\n\n\t\tG_FLOAT(OFS_PARM0) = (float)date.sec;\n\t\tG_FLOAT(OFS_PARM1) = (float)date.min;\n\t\tG_FLOAT(OFS_PARM2) = (float)date.hour;\n\t\tG_FLOAT(OFS_PARM3) = (float)date.day;\n\t\tG_FLOAT(OFS_PARM4) = (float)date.mon;\n\t\tG_FLOAT(OFS_PARM5) = (float)date.year;\n\t\tG_INT(OFS_PARM6) = (int)PR_TempString(prinst, date.str);\n\n\t\tPR_ExecuteProgram(prinst, f);\n\t}\n}\n\nvoid QCBUILTIN PF_sprintf_internal (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals, const char *s, int firstarg, char *outbuf, int outbuflen)\n{\n\tconst char *s0;\n\tchar *o = outbuf, *end = outbuf + outbuflen, *err;\n\tint width, precision, thisarg, flags;\n\tchar formatbuf[16];\n\tchar *f;\n\tint argpos = firstarg;\n\tint isfloat, is64bit;\n\tstatic int dummyivec[3] = {0, 0, 0};\n\tstatic float dummyvec[3] = {0, 0, 0};\n\n#define PRINTF_ALTERNATE 1\n#define PRINTF_ZEROPAD 2\n#define PRINTF_LEFT 4\n#define PRINTF_SPACEPOSITIVE 8\n#define PRINTF_SIGNPOSITIVE 16\n\n\tformatbuf[0] = '%';\n\n#define GETARG_FLOAT(a) (((a)>=firstarg && (a)<prinst->callargc) ? (G_FLOAT(OFS_PARM0 + 3 * (a))) : 0)\n#define GETARG_DOUBLE(a) (((a)>=firstarg && (a)<prinst->callargc) ? (G_DOUBLE(OFS_PARM0 + 3 * (a))) : 0)\n#define GETARG_VECTOR(a) (((a)>=firstarg && (a)<prinst->callargc) ? (G_VECTOR(OFS_PARM0 + 3 * (a))) : dummyvec)\n#define GETARG_INT(a) (((a)>=firstarg && (a)<prinst->callargc) ? (G_INT(OFS_PARM0 + 3 * (a))) : 0)\n#define GETARG_INT64(a) (((a)>=firstarg && (a)<prinst->callargc) ? (G_INT64(OFS_PARM0 + 3 * (a))) : 0)\n#define GETARG_UINT(a) (((a)>=firstarg && (a)<prinst->callargc) ? (G_UINT(OFS_PARM0 + 3 * (a))) : 0)\n#define GETARG_UINT64(a) (((a)>=firstarg && (a)<prinst->callargc) ? (G_UINT64(OFS_PARM0 + 3 * (a))) : 0)\n#define GETARG_INTVECTOR(a) (((a)>=firstarg && (a)<prinst->callargc) ? ((int*) G_VECTOR(OFS_PARM0 + 3 * (a))) : dummyivec)\n#define GETARG_STRING(a) (((a)>=firstarg && (a)<prinst->callargc) ? (PR_GetStringOfs(prinst, OFS_PARM0 + 3 * (a))) : \"\")\n\n#define GETARG_SNUMERIC(t, a) (is64bit?(isfloat ? (t) GETARG_DOUBLE(a) : (t) GETARG_INT64 (a)):(isfloat ? (t) GETARG_FLOAT(a) : (t) GETARG_INT (a)))\n#define GETARG_UNUMERIC(t, a) (is64bit?(isfloat ? (t) GETARG_DOUBLE(a) : (t) GETARG_UINT64(a)):(isfloat ? (t) GETARG_FLOAT(a) : (t) GETARG_UINT(a)))\n\n\tfor(;;)\n\t{\n\t\ts0 = s;\n\t\tswitch(*s)\n\t\t{\n\t\t\tcase 0:\n\t\t\t\tgoto finished;\n\t\t\tcase '%':\n\t\t\t\t++s;\n\n\t\t\t\tif(*s == '%')\n\t\t\t\t\tgoto verbatim;\n\n\t\t\t\t// complete directive format:\n\t\t\t\t// %3$*1$.*2$ld\n\t\t\t\t\n\t\t\t\twidth = -1;\n\t\t\t\tprecision = -1;\n\t\t\t\tthisarg = -1;\n\t\t\t\tflags = 0;\n\t\t\t\tisfloat = -1;\n\t\t\t\tis64bit = 0;\n\n\t\t\t\t// is number following?\n\t\t\t\tif(*s >= '0' && *s <= '9')\n\t\t\t\t{\n\t\t\t\t\twidth = strtol(s, &err, 10);\n\t\t\t\t\tif(!err)\n\t\t\t\t\t{\n\t\t\t\t\t\tCon_Printf(\"PF_sprintf: bad format string: %s\\n\", s0);\n\t\t\t\t\t\tgoto finished;\n\t\t\t\t\t}\n\t\t\t\t\tif(*err == '$')\n\t\t\t\t\t{\n\t\t\t\t\t\tthisarg = width + (firstarg-1);\n\t\t\t\t\t\twidth = -1;\n\t\t\t\t\t\ts = err + 1;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tif(*s == '0')\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tflags |= PRINTF_ZEROPAD;\n\t\t\t\t\t\t\tif(width == 0)\n\t\t\t\t\t\t\t\twidth = -1; // it was just a flag\n\t\t\t\t\t\t}\n\t\t\t\t\t\ts = err;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif(width < 0)\n\t\t\t\t{\n\t\t\t\t\tfor(;;)\n\t\t\t\t\t{\n\t\t\t\t\t\tswitch(*s)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase '#': flags |= PRINTF_ALTERNATE; break;\n\t\t\t\t\t\t\tcase '0': flags |= PRINTF_ZEROPAD; break;\n\t\t\t\t\t\t\tcase '-': flags |= PRINTF_LEFT; break;\n\t\t\t\t\t\t\tcase ' ': flags |= PRINTF_SPACEPOSITIVE; break;\n\t\t\t\t\t\t\tcase '+': flags |= PRINTF_SIGNPOSITIVE; break;\n\t\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\t\tgoto noflags;\n\t\t\t\t\t\t}\n\t\t\t\t\t\t++s;\n\t\t\t\t\t}\nnoflags:\n\t\t\t\t\tif(*s == '*')\n\t\t\t\t\t{\n\t\t\t\t\t\t++s;\n\t\t\t\t\t\tif(*s >= '0' && *s <= '9')\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\twidth = strtol(s, &err, 10);\n\t\t\t\t\t\t\tif(!err || *err != '$')\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tCon_Printf(\"PF_sprintf: invalid format string: %s\\n\", s0);\n\t\t\t\t\t\t\t\tgoto finished;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\ts = err + 1;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\twidth = argpos++;\n\t\t\t\t\t\twidth = GETARG_FLOAT(width);\n\t\t\t\t\t\tif(width < 0)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tflags |= PRINTF_LEFT;\n\t\t\t\t\t\t\twidth = -width;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\telse if(*s >= '0' && *s <= '9')\n\t\t\t\t\t{\n\t\t\t\t\t\twidth = strtol(s, &err, 10);\n\t\t\t\t\t\tif(!err)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tCon_Printf(\"PF_sprintf: invalid format string: %s\\n\", s0);\n\t\t\t\t\t\t\tgoto finished;\n\t\t\t\t\t\t}\n\t\t\t\t\t\ts = err;\n\t\t\t\t\t\tif(width < 0)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tflags |= PRINTF_LEFT;\n\t\t\t\t\t\t\twidth = -width;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t// otherwise width stays -1\n\t\t\t\t}\n\n\t\t\t\tif(*s == '.')\n\t\t\t\t{\n\t\t\t\t\t++s;\n\t\t\t\t\tif(*s == '*')\n\t\t\t\t\t{\n\t\t\t\t\t\t++s;\n\t\t\t\t\t\tif(*s >= '0' && *s <= '9')\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tprecision = strtol(s, &err, 10);\n\t\t\t\t\t\t\tif(!err || *err != '$')\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tCon_Printf(\"PF_sprintf: invalid format string: %s\\n\", s0);\n\t\t\t\t\t\t\t\tgoto finished;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\ts = err + 1;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tprecision = argpos++;\n\t\t\t\t\t\tprecision = GETARG_FLOAT(precision);\n\t\t\t\t\t}\n\t\t\t\t\telse if(*s >= '0' && *s <= '9')\n\t\t\t\t\t{\n\t\t\t\t\t\tprecision = strtol(s, &err, 10);\n\t\t\t\t\t\tif(!err)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tCon_Printf(\"PF_sprintf: invalid format string: %s\\n\", s0);\n\t\t\t\t\t\t\tgoto finished;\n\t\t\t\t\t\t}\n\t\t\t\t\t\ts = err;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tCon_Printf(\"PF_sprintf: invalid format string: %s\\n\", s0);\n\t\t\t\t\t\tgoto finished;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tfor(;;)\n\t\t\t\t{\n\t\t\t\t\tswitch(*s)\n\t\t\t\t\t{\n\t\t\t\t\t\t/*\tnote: this is technically a dp extension, so for our inputs:\n\t\t\t\t\t\t\tdp defined %lx for entity numbers (it not having actual ints)\n\t\t\t\t\t\t\tit later defined %llx for Actual ints, not that it makes a difference in DP.\n\t\t\t\t\t\t\tThis leaves us unable to use C's 'l' for our int64 type (aka long), nor 'll' either.\n\t\t\t\t\t\t\tSo lean on BSD's 'q'.\n\n\t\t\t\t\t\t\tfor our outputs, %llx is standard for int64 with MS violating c99 and requiring %I64x instead.\n\t\t\t\t\t\t*/\n\t\t\t\t\t\tcase 'h': isfloat = 1; break;\t//short in C, interpreted as float here. doubled for char.\n\t\t\t\t\t\tcase 'l': isfloat = 0; break;\t//long, twice for long long... we interpret as int32.\n\t\t\t\t\t\tcase 'L': isfloat = 0; break;\t//'long double'\n\t\t\t\t\t\tcase 'q': is64bit = 1; break;\t//BSD synonym for long long. or more specifically int64 and NOT int128.\n\t\t\t\t\t\tcase 'j': break;\t//intmax_t\n\t\t\t\t\t\t//case 'Z':\t//synonym for 'z'. do not use\n\t\t\t\t\t\tcase 'z': break;\t//size_t\n\t\t\t\t\t\tcase 't': break;\t//ptrdiff_t\n\n\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\tgoto nolength;\n\t\t\t\t\t}\n\t\t\t\t\t++s;\n\t\t\t\t}\nnolength:\n\n\t\t\t\t// now s points to the final directive char and is no longer changed\n\t\t\t\tif (*s == 'p' || *s == 'P')\n\t\t\t\t{\n\t\t\t\t\t//%p is slightly different from %x.\n\t\t\t\t\t//always 8-bytes wide with 0 padding, always ints.\n\t\t\t\t\tflags |= PRINTF_ZEROPAD;\n\t\t\t\t\tif (width < 0) width = 8;\n\t\t\t\t\tif (isfloat < 0) isfloat = 0;\n\t\t\t\t}\n\t\t\t\telse if (*s == 'i')\n\t\t\t\t{\n\t\t\t\t\t//%i defaults to ints, not floats.\n\t\t\t\t\tif(isfloat < 0) isfloat = 0;\n\t\t\t\t}\n\n\t\t\t\t//assume floats, not ints.\n\t\t\t\tif(isfloat < 0)\n\t\t\t\t\tisfloat = 1;\n\n\t\t\t\tif(thisarg < 0)\n\t\t\t\t\tthisarg = argpos++;\n\n\t\t\t\tif(o < end - 1)\n\t\t\t\t{\n\t\t\t\t\tf = &formatbuf[1];\n\t\t\t\t\tif(*s != 's' && *s != 'c' && *s != 'S')\n\t\t\t\t\t\tif(flags & PRINTF_ALTERNATE) *f++ = '#';\n\t\t\t\t\tif(flags & PRINTF_ZEROPAD) *f++ = '0';\n\t\t\t\t\tif(flags & PRINTF_LEFT) *f++ = '-';\n\t\t\t\t\tif(flags & PRINTF_SPACEPOSITIVE) *f++ = ' ';\n\t\t\t\t\tif(flags & PRINTF_SIGNPOSITIVE) *f++ = '+';\n\t\t\t\t\t*f++ = '*';\n\t\t\t\t\tif(precision >= 0)\n\t\t\t\t\t{\n\t\t\t\t\t\t*f++ = '.';\n\t\t\t\t\t\t*f++ = '*';\n\t\t\t\t\t}\n\n\t\t\t\t\tswitch(*s)\n\t\t\t\t\t{\n\t\t\t\t\t\tcase 'd': case 'i': case 'I':\n\t\t\t\t\t\tcase 'o': case 'u': case 'x': case 'X': case 'p': case 'P':\n#ifdef _WIN32\t\t\t\t//not c99\n\t\t\t\t\t\t\t*f++ = 'I';\n\t\t\t\t\t\t\t*f++ = '6';\n\t\t\t\t\t\t\t*f++ = '4';\n#else\t\t\t\t\t\t//c99\n\t\t\t\t\t\t\t*f++ = 'l';\n\t\t\t\t\t\t\tif (sizeof(long) == 4)\n\t\t\t\t\t\t\t\t*f++ = 'l';\t//go for long long instead\n#endif\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\n\t\t\t\t\tif (*s == 'p')\n\t\t\t\t\t\t*f++ = 'x';\n\t\t\t\t\telse if (*s == 'P')\n\t\t\t\t\t\t*f++ = 'X';\n\t\t\t\t\telse if (*s == 'S')\n\t\t\t\t\t\t*f++ = 's';\n\t\t\t\t\telse\n\t\t\t\t\t\t*f++ = *s;\n\t\t\t\t\t*f++ = 0;\n\n\t\t\t\t\tif(width < 0) // not set\n\t\t\t\t\t\twidth = 0;\n\n\t\t\t\t\tswitch(*s)\n\t\t\t\t\t{\n\t\t\t\t\t\tcase 'd': case 'i': case 'I':\n\t\t\t\t\t\t\tif(precision < 0) // not set\n\t\t\t\t\t\t\t\tQ_snprintfz(o, end - o, formatbuf, width, GETARG_SNUMERIC(qint64_t, thisarg));\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\tQ_snprintfz(o, end - o, formatbuf, width, precision, GETARG_SNUMERIC(qint64_t, thisarg));\n\t\t\t\t\t\t\to += strlen(o);\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 'o': case 'u': case 'x': case 'X': case 'p': case 'P':\n\t\t\t\t\t\t\tif(precision < 0) // not set\n\t\t\t\t\t\t\t\tQ_snprintfz(o, end - o, formatbuf, width, GETARG_UNUMERIC(quint64_t, thisarg));\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\tQ_snprintfz(o, end - o, formatbuf, width, precision, GETARG_UNUMERIC(quint64_t, thisarg));\n\t\t\t\t\t\t\to += strlen(o);\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 'e': case 'E': case 'f': case 'F': case 'g': case 'G':\n\t\t\t\t\t\t\tif(precision < 0) // not set\n\t\t\t\t\t\t\t\tQ_snprintfz(o, end - o, formatbuf, width, GETARG_SNUMERIC(double, thisarg));\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\tQ_snprintfz(o, end - o, formatbuf, width, precision, GETARG_SNUMERIC(double, thisarg));\n\t\t\t\t\t\t\to += strlen(o);\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 'v': case 'V':\n\t\t\t\t\t\t\tf[-2] += 'g' - 'v';\n\t\t\t\t\t\t\tif(precision < 0) // not set\n\t\t\t\t\t\t\t\tQ_snprintfz(o, end - o, va(\"%s %s %s\", /* NESTED SPRINTF IS NESTED */ formatbuf, formatbuf, formatbuf),\n\t\t\t\t\t\t\t\t\twidth, (isfloat ? (double) GETARG_VECTOR(thisarg)[0] : (double) GETARG_INTVECTOR(thisarg)[0]),\n\t\t\t\t\t\t\t\t\twidth, (isfloat ? (double) GETARG_VECTOR(thisarg)[1] : (double) GETARG_INTVECTOR(thisarg)[1]),\n\t\t\t\t\t\t\t\t\twidth, (isfloat ? (double) GETARG_VECTOR(thisarg)[2] : (double) GETARG_INTVECTOR(thisarg)[2])\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\tQ_snprintfz(o, end - o, va(\"%s %s %s\", /* NESTED SPRINTF IS NESTED */ formatbuf, formatbuf, formatbuf),\n\t\t\t\t\t\t\t\t\twidth, precision, (isfloat ? (double) GETARG_VECTOR(thisarg)[0] : (double) GETARG_INTVECTOR(thisarg)[0]),\n\t\t\t\t\t\t\t\t\twidth, precision, (isfloat ? (double) GETARG_VECTOR(thisarg)[1] : (double) GETARG_INTVECTOR(thisarg)[1]),\n\t\t\t\t\t\t\t\t\twidth, precision, (isfloat ? (double) GETARG_VECTOR(thisarg)[2] : (double) GETARG_INTVECTOR(thisarg)[2])\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\to += strlen(o);\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 'c':\n\t\t\t\t\t\t\tif((flags & PRINTF_ALTERNATE) || !VMUTF8)\n\t\t\t\t\t\t\t{\t//precision+width are in bytes\n\t\t\t\t\t\t\t\tif(precision < 0) // not set\n\t\t\t\t\t\t\t\t\tQ_snprintfz(o, end - o, formatbuf, width, (isfloat ? (unsigned int) GETARG_FLOAT(thisarg) : (unsigned int) GETARG_INT(thisarg)));\n\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t\tQ_snprintfz(o, end - o, formatbuf, width, precision, (isfloat ? (unsigned int) GETARG_FLOAT(thisarg) : (unsigned int) GETARG_INT(thisarg)));\n\t\t\t\t\t\t\t\to += strlen(o);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t{\t//precision+width are in chars\n\t\t\t\t\t\t\t\tunsigned int c = (isfloat ? (unsigned int) GETARG_FLOAT(thisarg) : (unsigned int) GETARG_INT(thisarg));\n\t\t\t\t\t\t\t\tchar buf[16];\n\t\t\t\t\t\t\t\tc = unicode_encode(buf, c, sizeof(buf), VMUTF8MARKUP);\n\t\t\t\t\t\t\t\tbuf[c] = 0;\n\t\t\t\t\t\t\t\tif(precision < 0) // not set\n\t\t\t\t\t\t\t\t\tprecision = end - o - 1;\n\t\t\t\t\t\t\t\tunicode_strpad(o, end - o, buf, (flags & PRINTF_LEFT) != 0, width, precision, VMUTF8MARKUP);\n\t\t\t\t\t\t\t\to += strlen(o);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 'S':\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tconst char *quotedarg = GETARG_STRING(thisarg);\n\t\t\t\t\t\t\t\tchar quotedbuf[65536];\t//FIXME: no idea how big this actually needs to be.\n\t\t\t\t\t\t\t\tquotedarg = COM_QuotedString(quotedarg, quotedbuf, sizeof(quotedbuf), false);\n\t\t\t\t\t\t\t\tif((flags & PRINTF_ALTERNATE) || !VMUTF8)\n\t\t\t\t\t\t\t\t{\t//precision+width are in bytes\n\t\t\t\t\t\t\t\t\tif(precision < 0) // not set\n\t\t\t\t\t\t\t\t\t\tQ_snprintfz(o, end - o, formatbuf, width, quotedarg);\n\t\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t\t\tQ_snprintfz(o, end - o, formatbuf, width, precision, quotedarg);\n\t\t\t\t\t\t\t\t\to += strlen(o);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t{\t//precision+width are in chars\n\t\t\t\t\t\t\t\t\tif(precision < 0) // not set\n\t\t\t\t\t\t\t\t\t\tprecision = end - o - 1;\n\t\t\t\t\t\t\t\t\tunicode_strpad(o, end - o, quotedarg, (flags & PRINTF_LEFT) != 0, width, precision, VMUTF8MARKUP);\n\t\t\t\t\t\t\t\t\to += strlen(o);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 's':\t\n\t\t\t\t\t\t\tif((flags & PRINTF_ALTERNATE) || !VMUTF8)\n\t\t\t\t\t\t\t{\t//precision+width are in bytes\n\t\t\t\t\t\t\t\tif(precision < 0) // not set\n\t\t\t\t\t\t\t\t\tQ_snprintfz(o, end - o, formatbuf, width, GETARG_STRING(thisarg));\n\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t\tQ_snprintfz(o, end - o, formatbuf, width, precision, GETARG_STRING(thisarg));\n\t\t\t\t\t\t\t\to += strlen(o);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t{\t//precision+width are in chars\n\t\t\t\t\t\t\t\tif(precision < 0) // not set\n\t\t\t\t\t\t\t\t\tprecision = end - o - 1;\n\t\t\t\t\t\t\t\tunicode_strpad(o, end - o, GETARG_STRING(thisarg), (flags & PRINTF_LEFT) != 0, width, precision, VMUTF8MARKUP);\n\t\t\t\t\t\t\t\to += strlen(o);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\tCon_Printf(\"PF_sprintf: invalid format string: %s\\n\", s0);\n\t\t\t\t\t\t\tgoto finished;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t++s;\n\t\t\t\tbreak;\n\t\t\tdefault:\nverbatim:\n\t\t\t\tif(o < end - 1)\n\t\t\t\t\t*o++ = *s;\n\t\t\t\ts++;\n\t\t\t\tbreak;\n\t\t}\n\t}\nfinished:\n\t*o = 0;\n}\n\nvoid QCBUILTIN PF_sprintf (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tchar outbuf[65536];\t//FIXME: no idea how big this actually needs to be.\n\tPF_sprintf_internal(prinst, pr_globals, PR_GetStringOfs(prinst, OFS_PARM0), 1, outbuf, sizeof(outbuf));\n\tRETURN_TSTRING(outbuf);\n}\n\n//float()\nvoid QCBUILTIN PF_numentityfields (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tunsigned int count = 0;\n\tprinst->FieldInfo(prinst, &count);\n\tG_FLOAT(OFS_RETURN) = count;\n}\nvoid QCBUILTIN PF_findentityfield (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char *fieldname = PR_GetStringOfs(prinst, OFS_PARM0);\n\tunsigned int count = 0, fidx;\n\tfdef_t *fdef;\n\tfdef = prinst->FieldInfo(prinst, &count);\n\tG_FLOAT(OFS_RETURN) = 0;\n\tfor (fidx = 0; fidx < count; fidx++)\n\t{\n\t\tif (!strcmp(fdef[fidx].name, fieldname))\n\t\t{\n\t\t\tG_FLOAT(OFS_RETURN) = fidx;\n\t\t\tbreak;\n\t\t}\n\t}\n}\nvoid QCBUILTIN PF_entityfieldref (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tunsigned int fidx = G_FLOAT(OFS_PARM0);\n\tunsigned int count = 0;\n\tfdef_t *fdef;\n\tfdef = prinst->FieldInfo(prinst, &count);\n\tG_INT(OFS_RETURN) = 0;\n\tif (fidx < count)\n\t{\n\t\tG_INT(OFS_RETURN) = fdef[fidx].ofs;\n\t}\n}\n//string(float fieldnum)\nvoid QCBUILTIN PF_entityfieldname (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tunsigned int fidx = G_FLOAT(OFS_PARM0);\n\tunsigned int count = 0;\n\tfdef_t *fdef;\n\tfdef = prinst->FieldInfo(prinst, &count);\n\tif (fidx < count)\n\t{\n\t\tRETURN_TSTRING(fdef[fidx].name);\n\t}\n\telse\n\t\tG_INT(OFS_RETURN) = 0;\n}\n//float(float fieldnum)\nvoid QCBUILTIN PF_entityfieldtype (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tunsigned int fidx = G_FLOAT(OFS_PARM0);\n\tunsigned int count = 0;\n\tfdef_t *fdef = prinst->FieldInfo(prinst, &count);\n\tif (fidx < count)\n\t{\n\t\tG_FLOAT(OFS_RETURN) = fdef[fidx].type;\n\t}\n\telse\n\t\tG_FLOAT(OFS_RETURN) = 0;\n}\n//string(float fieldnum, entity ent)\nvoid QCBUILTIN PF_getentityfieldstring (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tunsigned int fidx = G_FLOAT(OFS_PARM0);\n\twedict_t *ent = (wedict_t *)G_EDICT(prinst, OFS_PARM1);\n\teval_t *eval;\n\tunsigned int count = 0;\n\tfdef_t *fdef = prinst->FieldInfo(prinst, &count);\n\tG_INT(OFS_RETURN) = 0;\n\tif (fidx < count)\n\t{\n#if !defined(CLIENTONLY) && defined(HAVE_LEGACY)\n\t\tqboolean isserver = (prinst == sv.world.progs);\n#endif\n\t\teval = (eval_t *)&((pvec_t *)ent->v)[fdef[fidx].ofs];\n#ifdef HAVE_LEGACY\t//extra code to be lazy so that xonotic doesn't go crazy and spam the fuck out of e\n\t\tif ((fdef->type & 0xff) == ev_vector)\n\t\t{\n\t\t\tif (eval->_vector[0]==0&&eval->_vector[1]==0&&eval->_vector[2]==0)\n\t\t\t\treturn;\n\t\t}\n#ifndef CLIENTONLY\n#ifdef HEXEN2\n\t\telse if (isserver && (pvec_t*)eval == &((edict_t*)ent)->xv->drawflags && eval->_float == 96)\n\t\t\treturn;\n#endif\n\t\telse if (isserver && (pvec_t*)eval == &((edict_t*)ent)->xv->uniquespawnid)\n\t\t\treturn;\n#endif\n\t\telse if (((pvec_t*)eval == &ent->xv->dimension_solid ||\n\t\t\t\t  (pvec_t*)eval == &ent->xv->dimension_hit\n#ifndef CLIENTONLY\n\t\t\t\t  || (isserver && ((pvec_t*)eval == &((edict_t*)ent)->xv->dimension_see\n\t\t\t\t  || (pvec_t*)eval == &((edict_t*)ent)->xv->dimension_seen))\n#endif\n\t\t\t\t  )  && eval->_float == 255)\n\t\t\treturn;\n\t\telse\n\t\t{\n\t\t\tif (!eval->_int)\n\t\t\t\treturn;\n\t\t}\n#endif\n\t\tRETURN_TSTRING(prinst->UglyValueString(prinst, fdef[fidx].type, eval));\n\t}\n}\n//float(float fieldnum, entity ent, string s)\nvoid QCBUILTIN PF_putentityfieldstring (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tunsigned int fidx = G_FLOAT(OFS_PARM0);\n\twedict_t *ent = (wedict_t *)G_EDICT(prinst, OFS_PARM1);\n\tconst char *str = PR_GetStringOfs(prinst, OFS_PARM2);\n\teval_t *eval;\n\tunsigned int count = 0;\n\tfdef_t *fdef = prinst->FieldInfo(prinst, &count);\n\tif (fidx < count)\n\t{\n\t\teval = (eval_t *)&((float *)ent->v)[fdef[fidx].ofs];\n\t\tG_FLOAT(OFS_RETURN) = prinst->ParseEval(prinst, eval, fdef[fidx].type, str);\n\t}\n\telse\n\t\tG_FLOAT(OFS_RETURN) = 0;\n}\n\n//must match ordering in Cmd_ExecuteString.\nvoid QCBUILTIN PF_checkcommand (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char *str = PR_GetStringOfs(prinst, OFS_PARM0);\n\t//functions, aliases, cvars. in that order.\n\tif (Cmd_Exists(str))\n\t{\n\t\tG_FLOAT(OFS_RETURN) = 1;\n\t\treturn;\n\t}\n\tif (Cmd_AliasExist(str, RESTRICT_INSECURE))\n\t{\n\t\tG_FLOAT(OFS_RETURN) = 2;\n\t\treturn;\n\t}\n\tif (Cvar_FindVar(str))\n\t{\n\t\tG_FLOAT(OFS_RETURN) = 3;\n\t\treturn;\n\t}\n\tG_FLOAT(OFS_RETURN) = 0;\n}\n\nvoid QCBUILTIN PF_physics_supported(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n#ifdef USERBE\n\tworld_t *world = prinst->parms->user;\n\tif (prinst->callargc)\n\t{\n\t\tif (G_FLOAT(OFS_PARM0) && !world->rbe)\n\t\t\tWorld_RBE_Start(world);\n\t\telse if (!G_FLOAT(OFS_PARM0) && world->rbe)\n\t\t\tWorld_RBE_Shutdown(world);\n\t}\n\tif (world->rbe)\n\t\tG_FLOAT(OFS_RETURN) = 1;\n\telse\n#endif\n\t\tG_FLOAT(OFS_RETURN) = 0;\n}\n#ifdef USERBE\nvoid QCBUILTIN PF_physics_enable(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\twedict_t*e\t\t\t= G_WEDICT(prinst, OFS_PARM0);\n\tint\t\tisenable\t= G_FLOAT(OFS_PARM1);\n\tworld_t *world = prinst->parms->user;\n\trbecommandqueue_t cmd;\n\t\n\tcmd.command = isenable?RBECMD_ENABLE:RBECMD_DISABLE;\n\tcmd.edict = e;\n\n\tif (world->rbe)\n\t\tworld->rbe->PushCommand(world, &cmd);\n}\nvoid QCBUILTIN PF_physics_addforce(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\twedict_t*e\t\t\t\t= G_WEDICT(prinst, OFS_PARM0);\n\tfloat\t*force\t\t\t= G_VECTOR(OFS_PARM1);\n\tfloat\t*impactpos\t\t= G_VECTOR(OFS_PARM2);\t//world coord of impact.\n\tworld_t *world = prinst->parms->user;\n\trbecommandqueue_t cmd;\n\n\tcmd.command = RBECMD_FORCE;\n\tcmd.edict = e;\n\tVectorCopy(force, cmd.v1);\n\tVectorCopy(impactpos, cmd.v2);\n\n\tif (world->rbe)\n\t\tworld->rbe->PushCommand(world, &cmd);\n}\nvoid QCBUILTIN PF_physics_addtorque(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\twedict_t*e\t\t\t\t= G_WEDICT(prinst, OFS_PARM0);\n\tfloat\t*torque\t\t\t= G_VECTOR(OFS_PARM1);\n\tworld_t *world = prinst->parms->user;\n\trbecommandqueue_t cmd;\n\n\tcmd.command = RBECMD_TORQUE;\n\tcmd.edict = e;\n\tVectorCopy(torque, cmd.v1);\n\n\tif (world->rbe)\n\t\tworld->rbe->PushCommand(world, &cmd);\n}\n#endif\n\n/*\n=============\nPF_pushmove\n=============\n*/\nvoid QCBUILTIN PF_pushmove (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tworld_t *world = prinst->parms->user;\n\twedict_t *ent;\n\tfloat *move;\n\tfloat *amove;\n\t\n\tent = G_WEDICT(prinst, OFS_PARM0);\n\tmove = G_VECTOR(OFS_PARM1);\n\tamove = G_VECTOR(OFS_PARM2);\n\n\tG_FLOAT(OFS_RETURN) = WPhys_Push(world, ent, move, amove);\n}\n\nvoid PR_Common_Shutdown(pubprogfuncs_t *progs, qboolean errored)\n{\n\tPR_ClearThreads(progs);\n#if defined(SKELETALOBJECTS) || defined(RAGDOLLS)\n\tskel_reset(progs->parms->user);\n#endif\n\tPR_fclose_progs(progs);\n\tsearch_close_progs(progs, !errored);\n#ifdef ENGINE_ROUTING\n\tPR_Route_Shutdown (progs->parms->user);\n#endif\n#ifdef TEXTEDITOR\n\tEditor_ProgsKilled(progs);\n#endif\n#ifdef SQL\n\tSQL_KillServers(progs);\n#endif\n\ttokenize_flush();\n}\n\nqboolean PR_Common_LoadGame(pubprogfuncs_t *prinst, char *command, const char **file)\n{\n\tconst char *l = *file;\n\tif (!strcmp(command, \"buffer\"))\n\t{\n\t\tl = PR_buf_loadgame(prinst, l);\n\t\tif (!l)\n\t\t\treturn false;\n\t}\n\telse if (!strcmp(command, \"hashtable\"))\n\t{\n\t\tl = PR_hash_loadgame(prinst, l);\n\t\tif (!l)\n\t\t\treturn false;\n\t}\n\telse\n\t\treturn false;\n\t*file = l;\n\treturn true;\n}\nvoid PR_Common_SaveGame(vfsfile_t *f, pubprogfuncs_t *prinst, qboolean binary)\n{\n\tPR_buf_savegame(f, prinst, binary);\n\tPR_hash_savegame(f, prinst, binary);\n}\n\n\nvoid *PR_GetWriteQCPtr(pubprogfuncs_t *prinst, int qcptr, int qcsize)\n{\n//\tvoid *r;\n\tif (qcsize < 0)\n\t\treturn NULL;\n\tif (!qcptr)\n\t\treturn NULL;\n\tif (qcptr >= 0 && qcptr <= prinst->stringtablemaxsize)\n\t{\n\t\tif (qcptr + qcsize <= prinst->stringtablemaxsize)\n\t\t\treturn prinst->stringtable+qcptr;\t//its in bounds\n\t}\n\t/*else\n\t{\n\t\tr = PR_GetString(prinst, qcptr);\n\t\tif (qcsize < strlen(r))\n\t\t\treturn r;\n\t}*/\n\treturn NULL;\n}\nconst void *PR_GetReadQCPtr(pubprogfuncs_t *prinst, int qcptr, int qcsize)\n{\n\tconst char *r;\n\tif (qcsize < 0)\n\t\treturn NULL;\n\tif (!qcptr)\n\t\treturn NULL;\n\tif (qcptr >= 0 && qcptr <= prinst->stringtablemaxsize)\n\t{\n\t\tif (qcptr + qcsize <= prinst->stringtablemaxsize)\n\t\t\treturn prinst->stringtable+qcptr;\t//its in bounds\n\t}\n\telse\n\t{\n\t\tr = PR_GetString(prinst, qcptr);\n\t\tif (qcsize < strlen(r))\n\t\t\treturn r;\n\t}\n\treturn NULL;\n}\n\n\n#define DEF_SAVEGLOBAL (1u<<15)\nstatic void PR_AutoCvarApply(pubprogfuncs_t *prinst, eval_t *val, etype_t type, cvar_t *var)\n{\n\tswitch(type & ~DEF_SAVEGLOBAL)\n\t{\n\tcase ev_float:\n\t\tval->_float = var->value;\n\t\tbreak;\n\tcase ev_integer:\n\t\tval->_int = var->ival;\n\t\tbreak;\n\tcase ev_string:\n#ifdef QCGC\n\t\tif (*var->string)\n\t\t\tval->_int = PR_NewString(prinst, var->string);\n\t\telse\n\t\t\tval->_int = 0;\n#else\n\t\tif (val->_int)\n\t\t\tprinst->RemoveProgsString(prinst, val->_int);\n\t\tif (*var->string)\n\t\t\tval->_int = PR_SetString(prinst, var->string);\n\t\telse\n\t\t\tval->_int = 0;\n#endif\n\t\tbreak;\n\tcase ev_vector:\n\t\t{\n\t\t\tchar res[128];\n\t\t\tchar *vs = var->string;\n\t\t\tvs = COM_ParseOut(vs, res, sizeof(res));\n\t\t\tval->_vector[0] = atof(res);\n\t\t\tvs = COM_ParseOut(vs, res, sizeof(res));\n\t\t\tval->_vector[1] = atof(res);\n\t\t\tvs = COM_ParseOut(vs, res, sizeof(res));\n\t\t\tval->_vector[2] = atof(res);\n\t\t}\n\t\tbreak;\n\t}\n}\n/*called when a var has changed*/\nvoid PR_AutoCvar(pubprogfuncs_t *prinst, cvar_t *var)\n{\n\tchar *gname;\n\teval_t *val;\n\tetype_t type;\n\tint n, p;\n\n\tif (var->flags & CVAR_NOUNSAFEEXPAND)\n\t\treturn;\n\n\tfor (n = 0; n < 2; n++)\n\t{\n\t\tgname = n?var->name2:var->name;\n\t\tif (!gname)\n\t\t\tcontinue;\n\t\tgname = va(\"autocvar_%s\", gname);\n\t\t\n\t\tfor (p = 0; p < prinst->numprogs; p++)\n\t\t{\n\t\t\tval = PR_FindGlobal(prinst, gname, p, &type);\n\t\t\tif (val)\n\t\t\t\tPR_AutoCvarApply(prinst, val, type, var);\n\t\t}\n\t}\n}\n\nstatic void PDECL PR_FoundAutoCvarGlobal(pubprogfuncs_t *progfuncs, char *name, eval_t *val, etype_t type, void *ctx)\n{\n\tcvar_t *var;\n\tconst char *vals;\n\tint nlen;\n\tname += 9; //autocvar_\n\t\n\tswitch(type & ~DEF_SAVEGLOBAL)\n\t{\n\tcase ev_float:\n\t\t//ignore individual vector componants. let the vector itself do all the work.\n\t\tnlen = strlen(name);\n\t\tif(nlen >= 2 && name[nlen-2] == '_' && (name[nlen-1] == 'x' || name[nlen-1] == 'y' || name[nlen-1] == 'z'))\n\t\t\treturn;\n\n\t\tvals = va(\"%g\", val->_float);\n\t\tbreak;\n\tcase ev_integer:\n\t\tvals = va(\"%i\", val->_int);\n\t\tbreak;\n\tcase ev_vector:\n\t\tvals = va(\"%g %g %g\", val->_vector[0], val->_vector[1], val->_vector[2]);\n\t\tbreak;\n\tcase ev_string:\n\t\tvals = PR_GetString(progfuncs, val->string);\n\t\tval->_int = 0;\n\t\tbreak;\n\tdefault:\n\t\treturn;\n\t}\n\tvar = Cvar_Get(name, vals, 0, \"autocvars\");\n\tif (!var || (var->flags & CVAR_NOUNSAFEEXPAND))\n\t\treturn;\n\n\tvar->flags |= CVAR_TELLGAMECODE;\n\n\tPR_AutoCvarApply(progfuncs, val, type, var);\n}\n\nvoid PDECL PR_FoundDoTranslateGlobal(pubprogfuncs_t *progfuncs, char *name, eval_t *val, etype_t type, void *ctx)\n{\n\tconst char *olds;\n\tconst char *news;\n\tif ((type & ~DEF_SAVEGLOBAL) != ev_string)\n\t\treturn;\n\n\tolds = PR_GetString(progfuncs, val->string);\n\tnews = PO_GetText(ctx, olds);\n\tif (news != olds)\n\t\tval->string = PR_NewString(progfuncs, news);\n}\n\n//called after each progs is loaded\nvoid PR_ProgsAdded(pubprogfuncs_t *prinst, int newprogs, const char *modulename)\n{\n\tvfsfile_t *f = NULL, *f2 = NULL;\n\tchar lang[64], *h;\n\textern cvar_t language;\n\tif (!prinst || newprogs < 0)\n\t\treturn;\n\n\tQ_strncpyz(lang, language.string, sizeof(lang));\n\twhile ((h = strchr(lang, '-')))\n\t\t*h = '_';\n\tfor(;;)\n\t{\n\t\tif (!f)\n\t\t\tf = FS_OpenVFS(va(\"%s.%s.po\", modulename, *lang?lang:\"default\"), \"rb\", FS_GAME);\n\t\tif (!f2)\n\t\t\tf2 = FS_OpenVFS(va(\"common.%s.po\", *lang?lang:\"default\"), \"rb\", FS_GAME);\n\t\tif (f && f2)\n\t\t\tbreak;\n\t\tif (!*lang)\n\t\t\tbreak;\n\t\th = strchr(lang, '_');\n\t\tif (h)\n\t\t\t*h = 0;\n\t\telse if (*lang)\n\t\t\t*lang = 0;\n\t\telse\n\t\t\tbreak;\n\t}\n\n\tif (f || f2)\n\t{\n\t\tvoid *pofile = PO_Create();\n\t\tPO_Merge(pofile, f);\n\t\tPO_Merge(pofile, f2);\n\t\tprinst->FindPrefixGlobals (prinst, newprogs, \"dotranslate_\", PR_FoundDoTranslateGlobal, pofile);\n\t\tPO_Close(pofile);\n\t}\n\tprinst->FindPrefixGlobals (prinst, newprogs, \"autocvar_\", PR_FoundAutoCvarGlobal, NULL);\n\n\tQCLoadBreakpoints(\"\", modulename);\n}\n\n//rpd provides its own version of many of the earlier protocols, so stuff might work even without those.\nstatic qboolean check_pext_rpd\t\t\t\t(extcheck_t *extcheck, unsigned int pext1) {return (extcheck->pext1 & pext1) || (extcheck->pext2&PEXT2_REPLACEMENTDELTAS);}\n\nstatic qboolean check_pext_setview\t\t\t(extcheck_t *extcheck) {return !!(extcheck->pext1 & PEXT_SETVIEW);}\nstatic qboolean check_pext_scale\t\t\t(extcheck_t *extcheck) {return check_pext_rpd(extcheck,PEXT_SCALE);}\nstatic qboolean check_pext_lightstylecol\t(extcheck_t *extcheck) {return !!(extcheck->pext1 & PEXT_LIGHTSTYLECOL);}\nstatic qboolean check_pext_trans\t\t\t(extcheck_t *extcheck) {return check_pext_rpd(extcheck,PEXT_TRANS);}\nstatic qboolean check_pext_view2\t\t\t(extcheck_t *extcheck) {return !!(extcheck->pext1 & PEXT_VIEW2_);}\n//static qboolean check_pext_scale\t\t\t(extcheck_t *extcheck) {return !!(extcheck->pext1 & PEXT_BULLETENS);}\n//static qboolean check_pext_senttimings\t\t(extcheck_t *extcheck) {return check_pext_rpd(extcheck,PEXT_ACCURATETIMINGS);}\n//static qboolean check_pext_sounddbl\t\t\t(extcheck_t *extcheck) {return !!(extcheck->pext1 & PEXT_SOUNDDBL);}\nstatic qboolean check_pext_fatness\t\t\t(extcheck_t *extcheck) {return check_pext_rpd(extcheck,PEXT_FATNESS);}\n//static qboolean check_pext_hlbsp\t\t\t(extcheck_t *extcheck) {return !!(extcheck->pext1 & PEXT_HLBSP);}\nstatic qboolean check_pext_tebullet\t\t\t(extcheck_t *extcheck) {return !!(extcheck->pext1 & PEXT_TE_BULLET);}\n//static qboolean check_pext_hullsize\t\t\t(extcheck_t *extcheck) {return check_pext_rpd(extcheck,PEXT_HULLSIZE);}\n//static qboolean check_pext_modeldbl\t\t\t(extcheck_t *extcheck) {return check_pext_rpd(extcheck,PEXT_MODELDBL);}\n//static qboolean check_pext_entitydbl\t\t(extcheck_t *extcheck) {return check_pext_rpd(extcheck,PEXT_ENTITYDBL);}\n//static qboolean check_pext_entitydbl2\t\t(extcheck_t *extcheck) {return check_pext_rpd(extcheck,PEXT_ENTITYDBL2);}\nstatic qboolean check_pext_floatcoords\t\t(extcheck_t *extcheck) {return !!(extcheck->pext1 & PEXT_FLOATCOORDS);}\n//static qboolean check_pext_vweap\t\t\t(extcheck_t *extcheck) {return !!(extcheck->pext1 & PEXT_VWEAP);}\nstatic qboolean check_pext_q2bsp\t\t\t(extcheck_t *extcheck) {return !!(extcheck->pext1 & PEXT_Q2BSP_);}\nstatic qboolean check_pext_q3bsp\t\t\t(extcheck_t *extcheck) {return !!(extcheck->pext1 & PEXT_Q3BSP_);}\nstatic qboolean check_pext_colourmod\t\t(extcheck_t *extcheck) {return check_pext_rpd(extcheck,PEXT_COLOURMOD);}\n//static qboolean check_pext_splitscreen\t\t(extcheck_t *extcheck) {return !!(extcheck->pext1 & PEXT_SPLITSCREEN);}\nstatic qboolean check_pext_hexen2\t\t\t(extcheck_t *extcheck) {return !!(extcheck->pext1 & PEXT_HEXEN2);}\nstatic qboolean check_pext_spawnstatic\t\t(extcheck_t *extcheck) {return check_pext_rpd(extcheck,PEXT_SPAWNSTATIC2);}\nstatic qboolean check_pext_customtempeffects(extcheck_t *extcheck) {return !!(extcheck->pext1 & PEXT_CUSTOMTEMPEFFECTS);}\nstatic qboolean check_pext_256packetentities(extcheck_t *extcheck) {return check_pext_rpd(extcheck,PEXT_256PACKETENTITIES);}\nstatic qboolean check_pext_showpic\t\t\t(extcheck_t *extcheck) {return !!(extcheck->pext1 & PEXT_SHOWPIC);}\nstatic qboolean check_pext_setattachment\t(extcheck_t *extcheck) {return check_pext_rpd(extcheck,PEXT_SETATTACHMENT);}\n//static qboolean check_pext_chunkeddownloads\t(extcheck_t *extcheck) {return !!(extcheck->pext1 & PEXT_CHUNKEDDOWNLOADS);}\nstatic qboolean check_pext_csqc\t\t\t\t(extcheck_t *extcheck) {return !!(extcheck->pext1 & PEXT_CSQC);}\n//static qboolean check_pext_dpflags\t\t\t(extcheck_t *extcheck) {return check_pext_rpd(extcheck,PEXT_DPFLAGS);}\n#define check_pext_pointparticle\t\t\tNULL\n\n//static qboolean check_pext2_prydoncursor\t(extcheck_t *extcheck) {return !!(extcheck->pext2 & PEXT2_PRYDONCURSOR);}\n//static qboolean check_pext2_voicechat\t\t(extcheck_t *extcheck) {return !!(extcheck->pext2 & PEXT2_VOICECHAT);}\n//static qboolean check_pext2_setangledelta\t(extcheck_t *extcheck) {return !!(extcheck->pext2 & PEXT2_SETANGLEDELTA);}\n//static qboolean check_pext2_replacementdeltas(extcheck_t *extcheck) {return !!(extcheck->pext2 & PEXT2_REPLACEMENTDELTAS);}\n//static qboolean check_pext2_maxplayers\t\t(extcheck_t *extcheck) {return !!(extcheck->pext2 & PEXT2_MAXPLAYERS);}\n//static qboolean check_pext2_predinfo\t\t(extcheck_t *extcheck) {return !!(extcheck->pext2 & PEXT2_PREDINFO);}\n//static qboolean check_pext2_newsizeencoding\t(extcheck_t *extcheck) {return !!(extcheck->pext2 & PEXT2_NEWSIZEENCODING);}\nstatic qboolean check_pext2_infoblobs\t\t(extcheck_t *extcheck) {return !!(extcheck->pext2 & PEXT2_INFOBLOBS);}\n//static qboolean check_pext2_stunaware\t\t(extcheck_t *extcheck) {return !!(extcheck->pext2 & PEXT2_STUNAWARE);}\nstatic qboolean check_pext2_vrinputs\t\t(extcheck_t *extcheck) {return !!(extcheck->pext2 & PEXT2_VRINPUTS);}\n\n//rerelease stomped on things. make sure our earlier extension reports correctly.\nstatic qboolean check_notrerelease\t\t(extcheck_t *extcheck) {return !extcheck->world->remasterlogic;}\n//static qboolean check_rerelease\t\t\t(extcheck_t *extcheck) {return !!extcheck->world->remasterlogic;}\n\n#define NOBI NULL, 0,{NULL},\nqc_extension_t QSG_Extensions[] = {\n\t//these don't have well-defined names...\n\t{\"??TOMAZ_STRINGS\",\t\t\t\t\tcheck_notrerelease,\t6,{\"tq_zone\", \"tq_unzone\",  \"tq_strcat\", \"tq_substring\", \"tq_stof\", \"tq_stov\"}},\n\t{\"??TOMAZ_FILE\",\t\t\t\t\tNULL,\t4,{\"tq_fopen\", \"tq_fclose\", \"tq_fgets\", \"tq_fputs\"}},\n\t{\"??MVDSV_BUILTINS\",\t\t\t\tNULL,\t21,{\"executecommand\", \"mvdtokenize\", \"mvdargc\", \"mvdargv\",\n\t\t\t\t\t\t\t\t\t\t\t\t\"teamfield\", \"substr\", \"mvdstrcat\", \"mvdstrlen\", \"str2byte\",\n\t\t\t\t\t\t\t\t\t\t\t\t\"str2short\", \"mvdnewstr\", \"mvdfreestr\", \"conprint\", \"readcmd\",\n\t\t\t\t\t\t\t\t\t\t\t\t\"mvdstrcpy\", \"strstr\", \"mvdstrncpy\", \"logtext\", \"redirectcmd\",\n\t\t\t\t\t\t\t\t\t\t\t\t\"mvdcalltimeofday\", \"forcedemoframe\"}},\n\t{\"BX_COLOREDTEXT\"},\n\t{\"DP_CON_SET\",\t\t\t\t\t\tNULL,\t0,{NULL}, \"The 'set' console command exists, and can be used to create/set cvars.\"},\n#ifndef SERVERONLY\n\t{\"DP_CON_SETA\",\t\t\t\t\t\tNULL,\t0,{NULL}, \"The 'seta' console command exists, like the 'set' command, but also marks the cvar for archiving, allowing it to be written into the user's config. Use this command in your default.cfg file.\"},\n#endif\n\t{\"DP_CSQC_ROTATEMOVES\"},\n\t{\"DP_EF_ADDITIVE\",\t\t\t\t\tcheck_notrerelease},\n\t{\"DP_EF_BLUE\",\t\t\t\t\t\tcheck_notrerelease},\t\t\t\t\t\t//hah!! This is QuakeWorld!!!\n\t{\"DP_EF_FULLBRIGHT\"},\t\t\t\t//Rerouted to hexen2 support.\n\t{\"DP_EF_NODEPTHTEST\"},\t\t\t\t//for cheats\n\t{\"DP_EF_NODRAW\",\t\t\t\t\tcheck_notrerelease},\t\t\t\t\t//implemented by sending it with no modelindex\n\t{\"DP_EF_NOGUNBOB\"},\t\t\t\t\t//nogunbob. sane people should use csqc instead.\n\t{\"DP_EF_NOSHADOW\"},\n\t{\"DP_EF_RED\",\t\t\t\t\t\tcheck_notrerelease},\n\t{\"DP_ENT_ALPHA\",\t\t\t\t\tcheck_pext_trans},\t\t\t//transparent entites\n\t{\"DP_ENT_COLORMOD\",\t\t\t\t\tcheck_pext_colourmod},\n\t{\"DP_ENT_CUSTOMCOLORMAP\"},\n\t{\"DP_ENT_EXTERIORMODELTOCLIENT\"},\n\t{\"DP_ENT_SCALE\",\t\t\t\t\tcheck_pext_scale},\t//listed above\n\t{\"DP_ENT_TRAILEFFECTNUM\",\t\t\tNULL,\t1,{\"particleeffectnum\"}, \"self.traileffectnum=particleeffectnum(\\\"myeffectname\\\"); can be used to attach a particle trail to the given server entity. This is equivelent to calling trailparticles each frame.\"},\n\t//only in dp6 currently {\"DP_ENT_GLOW\"},\n\t{\"DP_ENT_VIEWMODEL\"},\n\t{\"DP_GECKO_SUPPORT\",\t\t\t\tNULL,\t7,{\"gecko_create\", \"gecko_destroy\", \"gecko_navigate\", \"gecko_keyevent\", \"gecko_mousemove\", \"gecko_resize\", \"gecko_get_texture_extent\"}},\n\t{\"DP_GFX_FONTS\",\t\t\t\t\tNULL,\t2,{\"findfont\", \"loadfont\"}},\t//note: font slot numbers/names are not special in fte.\n//\t{\"DP_GFX_FONTS_FREETYPE\"},\t//extra cvars are not supported.\n\t{\"DP_GFX_QUAKE3MODELTAGS\",\t\t\tcheck_pext_setattachment,\t1,{\"setattachment\"}},\n\t{\"DP_GFX_SKINFILES\"},\n\t{\"DP_GFX_SKYBOX\"},\t//according to the spec. :)\n\t{\"DP_HALFLIFE_MAP\"},\t\t//entity can visit a hl bsp\n\t{\"DP_HALFLIFE_MAP_CVAR\"},\n\t//to an extent {\"DP_HALFLIFE_SPRITE\"},\n\t{\"DP_INPUTBUTTONS\"},\n\t{\"DP_LIGHTSTYLE_STATICVALUE\"},\n\t{\"DP_LITSUPPORT\"},\n\t{\"DP_MONSTERWALK\",\t\t\t\t\tNULL,\t0,{NULL}, \"MOVETYPE_WALK is valid on non-player entities. Note that only players receive acceleration etc in line with none/bounce/fly/noclip movetypes on the player, thus you will have to provide your own accelerations (incluing gravity) yourself.\"},\n\t{\"DP_MOVETYPEBOUNCEMISSILE\",\t\tcheck_notrerelease},\t\t//I added the code for hexen2 support.\n\t{\"DP_MOVETYPEFOLLOW\"},\n\t{\"DP_QC_ASINACOSATANATAN2TAN\",\t\tNULL,\t5,{\"asin\", \"acos\", \"atan\", \"atan2\", \"tan\"}},\n\t{\"DP_QC_CHANGEPITCH\",\t\t\t\tNULL,\t1,{\"changepitch\"}},\n\t{\"DP_QC_COPYENTITY\",\t\t\t\tNULL,\t1,{\"copyentity\"}},\n\t{\"DP_QC_CRC16\",\t\t\t\t\t\tNULL,\t1,{\"crc16\"}},\n\t{\"DP_QC_CVAR_DEFSTRING\",\t\t\tNULL,\t1,{\"cvar_defstring\"}},\n\t{\"DP_QC_CVAR_STRING\",\t\t\t\tNULL,\t1,{\"cvar_string\"}},\t//448 builtin.\n\t{\"DP_QC_CVAR_TYPE\",\t\t\t\t\tNULL,\t1,{\"cvar_type\"}},\n\t{\"DP_QC_DIGEST_SHA256\"},\n\t{\"DP_QC_EDICT_NUM\",\t\t\t\t\tNULL,\t1,{\"edict_num\"}},\n\t{\"DP_QC_ENTITYDATA\",\t\t\t\tNULL,\t5,{\"numentityfields\", \"entityfieldname\", \"entityfieldtype\", \"getentityfieldstring\", \"putentityfieldstring\"}},\n\t{\"DP_QC_ETOS\",\t\t\t\t\t\tNULL,\t1,{\"etos\"}},\n\t{\"DP_QC_FINDCHAIN\",\t\t\t\t\tNULL,\t1,{\"findchain\"}},\n\t{\"DP_QC_FINDCHAINFLOAT\",\t\t\tNULL,\t1,{\"findchainfloat\"}},\n\t{\"DP_QC_FINDFLAGS\",\t\t\t\t\tNULL,\t1,{\"findflags\"}},\n\t{\"DP_QC_FINDCHAINFLAGS\",\t\t\tNULL,\t1,{\"findchainflags\"}},\n\t{\"DP_QC_FINDFLOAT\",\t\t\t\t\tNULL,\t1,{\"findfloat\"}},\n\t{\"DP_QC_FS_SEARCH\",\t\t\t\t\tNULL,\t4,{\"search_begin\", \"search_end\", \"search_getsize\", \"search_getfilename\"}},\n\t{\"DP_QC_FS_SEARCH_PACKFILE\",\t\tNULL,\t4,{\"search_begin\", \"search_end\", \"search_getsize\", \"search_getfilename\"}},\n\t{\"DP_QC_GETLIGHT\",\t\t\t\t\tcheck_notrerelease,\t1,{\"getlight\"}},\n\t{\"DP_QC_GETSURFACE\",\t\t\t\tNULL,\t6,{\"getsurfacenumpoints\", \"getsurfacepoint\", \"getsurfacenormal\", \"getsurfacetexture\", \"getsurfacenearpoint\", \"getsurfaceclippedpoint\"}},\n\t{\"DP_QC_GETSURFACEPOINTATTRIBUTE\",\tNULL,\t1,{\"getsurfacepointattribute\"}},\n\t{\"DP_QC_GETTAGINFO\",\t\t\t\tNULL,\t2,{\"gettagindex\", \"gettaginfo\"}},\n\t{\"DP_QC_I18N\",\t\t\t\t\t\tNULL,\t0,{NULL}, \"Specifies that the engine uses $MODULE.dat.$LANG.po files that translates the dotranslate_* globals on load - these are usually created via the _(\\\"foo\\\") qcc intrinsic.\"},\n\t{\"DP_QC_MINMAXBOUND\",\t\t\t\tNULL,\t3,{\"min\", \"max\", \"bound\"}},\n\t{\"DP_QC_MULTIPLETEMPSTRINGS\",\t\tNULL,\t0,{NULL}, \"Superseded by DP_QC_UNLIMITEDTEMPSTRINGS. Functions that return a temporary string will not overwrite/destroy previous temporary strings until at least 16 strings are returned (or control returns to the engine).\"},\n\t{\"DP_QC_RANDOMVEC\",\t\t\t\t\tcheck_notrerelease,\t1,{\"randomvec\"}},\n\t{\"DP_QC_RENDER_SCENE\",\t\t\t\tNULL,\t0,{NULL}, \"clearscene+addentity+setviewprop+renderscene+setmodel are available to menuqc. WARNING: DP advertises this extension without actually supporting it, FTE does actually support it.\"},\n\t{\"DP_QC_SINCOSSQRTPOW\",\t\t\t\tNULL,\t4,{\"sin\", \"cos\", \"sqrt\", \"pow\"}},\n\t{\"DP_QC_SPRINTF\",\t\t\t\t\tNULL,\t1,{\"sprintf\"}, \"Provides the sprintf builtin, which allows for rich formatting along the lines of C's function with the same name. Not to be confused with QC's sprint builtin.\"},\n\t{\"DP_QC_STRFTIME\",\t\t\t\t\tNULL,\t1,{\"strftime\"}},\n\t{\"DP_QC_STRING_CASE_FUNCTIONS\",\t\tNULL,\t2,{\"strtolower\", \"strtoupper\"}},\n\t{\"DP_QC_STRINGBUFFERS\",\t\t\t\tNULL,\t10,{\"buf_create\", \"buf_del\", \"buf_getsize\", \"buf_copy\", \"buf_sort\", \"buf_implode\", \"bufstr_get\", \"bufstr_set\", \"bufstr_add\", \"bufstr_free\"}},\n\t{\"DP_QC_STRINGCOLORFUNCTIONS\",\t\tNULL,\t2,{\"strlennocol\", \"strdecolorize\"}},\n\t{\"DP_QC_STRREPLACE\",\t\t\t\tNULL,\t2,{\"strreplace\", \"strireplace\"}},\n\t{\"DP_QC_TOKENIZEBYSEPARATOR\",\t\tNULL,\t1,{\"tokenizebyseparator\"}},\n\t{\"DP_QC_TRACEBOX\",\t\t\t\t\tcheck_notrerelease,\t1,{\"tracebox\"}},\n\t{\"DP_QC_TRACETOSS\"},\n\t{\"DP_QC_TRACE_MOVETYPE_HITMODEL\"},\n\t{\"DP_QC_TRACE_MOVETYPE_WORLDONLY\"},\n\t{\"DP_QC_TRACE_MOVETYPES\"},\t\t//this one is just a lame excuse to add another extension...\n\t{\"DP_QC_UNLIMITEDTEMPSTRINGS\",\t\tNULL,\t0,{NULL}, \"Supersedes DP_QC_MULTIPLETEMPSTRINGS, superseded by FTE_QC_PERSISTENTTEMPSTRINGS. Specifies that all temp strings will be valid at least until the QCVM returns.\"},\n\t{\"DP_QC_URI_ESCAPE\",\t\t\t\tNULL,\t2,{\"uri_escape\", \"uri_unescape\"}},\n#ifdef WEBCLIENT\n\t{\"DP_QC_URI_GET\",\t\t\t\t\tNULL,\t1,{\"uri_get\"}},\n\t{\"DP_QC_URI_POST\",\t\t\t\t\tNULL,\t1,{\"uri_get\"}},\n#endif\n\t{\"DP_QC_VECTOANGLES_WITH_ROLL\"},\n\t{\"DP_QC_VECTORVECTORS\",\t\t\t\tNULL,\t1,{\"vectorvectors\"}},\n\t{\"DP_QC_WHICHPACK\",\t\t\t\t\tNULL,\t1,{\"whichpack\"}},\n\t{\"DP_QUAKE2_MODEL\"},\n\t{\"DP_QUAKE2_SPRITE\"},\n\t{\"DP_QUAKE3_MAP\"},\n\t{\"DP_QUAKE3_MODEL\"},\n\t{\"DP_REGISTERCVAR\",\t\t\t\t\tNULL,\t1,{\"registercvar\"}},\n\t{\"DP_SND_SOUND7_WIP2\"},\t//listed only to silence xonotic, if anyone tries running that.\n\t{\"DP_SND_STEREOWAV\"},\n\t{\"DP_SND_OGGVORBIS\"},\n\t{\"DP_SOLIDCORPSE\"},\n\t{\"DP_SPRITE32\"},\t\t\t\t//hmm... is it legal to advertise this one?\n\t{\"DP_SV_BOTCLIENT\",\t\t\t\t\tNULL,\t2,{\"spawnclient\", \"clienttype\"}},\n\t{\"DP_SV_CLIENTCAMERA\",\t\t\t\tNULL,\t0,{NULL}, \"Works like svc_setview except also handles pvs.\"},\n\t{\"DP_SV_CLIENTCOLORS\",\t\t\t\tNULL,\t0,{NULL}, \"Provided only for compatibility with DP.\"},\n\t{\"DP_SV_CLIENTNAME\",\t\t\t\tNULL,\t0,{NULL}, \"Provided only for compatibility with DP.\"},\n\t{\"DP_SV_CUSTOMIZEENTITYFORCLIENT\",\tNULL,\t0,{NULL}, \"Deprecated feature for compat with DP. Can be slow, incompatible with splitscreen, usually malfunctions with mvds.\"},\n\t{\"DP_SV_DRAWONLYTOCLIENT\"},\n\t{\"DP_SV_DROPCLIENT\",\t\t\t\tNULL,\t1,{\"dropclient\"}, \"Equivelent to quakeworld's stuffcmd(self,\\\"disconnect\\\\n\\\"); hack\"},\n\t{\"DP_SV_EFFECT\",\t\t\t\t\tNULL,\t1,{\"effect\"}},\n\t{\"DP_SV_NODRAWTOCLIENT\"},\t\t//I prefer my older system. Guess I might as well remove that older system at some point.\n\t{\"DP_SV_PLAYERPHYSICS\",\t\t\t\tNULL,\t0,{NULL}, \"Allows reworking parts of NQ player physics. USE AT OWN RISK - this necessitates NQ physics and is thus guarenteed to break prediction.\"},\n//\t{\"DP_SV_POINTPARTICLES\",\t\t\tcheck_pext_pointparticle,\t3,{\"particleeffectnum\", \"pointparticles\", \"trailparticles\"}, \"Specifies that pointparticles (and trailparticles) exists in ssqc as well as csqc (and that dp's trailparticles argument fuckup will normally work). ssqc values can be passed to csqc for use, the reverse is not true. Does NOT mean that DP's effectinfo.txt is supported, only that ssqc has functionality equivelent to csqc.\"},\n\t{\"DP_SV_POINTSOUND\",\t\t\t\tNULL,\t1,{\"pointsound\"}},\n\t{\"DP_SV_PRECACHEANYTIME\",\t\t\tNULL,\t0,{NULL}, \"Specifies that the various precache builtins can be called at any time. WARNING: precaches are sent reliably while sound events, modelindexes, and particle events are not. This can mean sounds and particles might not work the first time around, or models may take a while to appear (after the reliables are received and the model is loaded from disk). Always attempt to precache a little in advance in order to reduce these issues (preferably at the start of the map...)\"},\n\t{\"DP_SV_PRINT\",\t\t\t\t\t\tNULL,\t1,{\"print\"}, \"Says that the print builtin can be used from nqssqc (as well as just csqc), bypassing the developer cvar issues.\"},\n\t{\"DP_SV_ROTATINGBMODEL\",\t\t\tcheck_notrerelease,0,{NULL},\t\t\"Engines that support this support avelocity on MOVETYPE_PUSH entities, pushing entities out of the way as needed.\"},\n\t{\"DP_SV_SETCOLOR\",\t\t\t\t\tNULL,\t1,{\"setcolor\"}},\n\t{\"DP_SV_SHUTDOWN\"},\n\t{\"DP_SV_SPAWNFUNC_PREFIX\"},\n\t{\"DP_SV_WRITEPICTURE\",\t\t\t\tNULL,\t1,{\"WritePicture\"}},\n\t{\"DP_SV_WRITEUNTERMINATEDSTRING\",\tNULL,\t1,{\"WriteUnterminatedString\"}},\n\t{\"DP_TE_BLOOD\",\t\t\t\t\t\tNULL,\t1,{\"te_blood\"}},\n\t{\"_DP_TE_BLOODSHOWER\",\t\t\t\tNULL,\t1,{\"te_bloodshower\"}, \"Requires external particle config.\"},\n\t{\"DP_TE_CUSTOMFLASH\",\t\t\t\tNULL,\t1,{\"te_customflash\"}},\n\t{\"DP_TE_EXPLOSIONRGB\",\t\t\t\tNULL,\t1,{\"te_explosionrgb\"}},\n\t{\"_DP_TE_FLAMEJET\",\t\t\t\t\tNULL,\t1,{\"te_flamejet\"}, \"Requires external particle config.\"},\n\t{\"DP_TE_PARTICLECUBE\",\t\t\t\tNULL,\t1,{\"te_particlecube\"}},\n\t{\"DP_TE_PARTICLERAIN\",\t\t\t\tNULL,\t1,{\"te_particlerain\"}},\n\t{\"DP_TE_PARTICLESNOW\",\t\t\t\tNULL,\t1,{\"te_particlesnow\"}},\n\t{\"_DP_TE_PLASMABURN\",\t\t\t\tNULL,\t1,{\"te_plasmaburn\"}, \"Requires external particle config.\"},\n\t{\"_DP_TE_QUADEFFECTS1\",\t\t\t\tNULL,\t4,{\"te_gunshotquad\", \"te_spikequad\", \"te_superspikequad\", \"te_explosionquad\"}, \"Requires external particle config.\"},\n\t{\"DP_TE_SMALLFLASH\",\t\t\t\tNULL,\t1,{\"te_smallflash\"}},\n\t{\"DP_TE_SPARK\",\t\t\t\t\t\tNULL,\t1,{\"te_spark\"}},\n\t{\"DP_TE_STANDARDEFFECTBUILTINS\",\tNULL,\t14,{\t\"te_gunshot\", \"te_spike\", \"te_superspike\", \"te_explosion\", \"te_tarexplosion\", \"te_wizspike\", \"te_knightspike\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\"te_lavasplash\", \"te_teleport\", \"te_explosion2\", \"te_lightning1\", \"te_lightning2\", \"te_lightning3\", \"te_beam\"}},\n\t{\"DP_VIEWZOOM\"},\n\t{\"EXT_BITSHIFT\",\t\t\t\t\tNULL,\t1,{\"bitshift\"}},\n\t{\"EXT_CSQC\",\t\t\t\t\t\tcheck_pext_csqc,\t2,{\"multicast\",\"clientstat\"}},\t//this is the base csqc extension. I'm not sure what needs to be separate and what does not.\n\t{\"EXT_CSQC_SHARED\",\t\t\t\t\tcheck_pext_csqc},\t\t\t\t//this is a separate extension because it requires protocol modifications. note: this is also the extension that extends the allowed stats.\n\t{\"EXT_DIMENSION_VISIBILITY\"},\n\t{\"EXT_DIMENSION_PHYSICS\"},\n\t{\"EXT_DIMENSION_GHOST\"},\n//\t{\"EX_EXTENDED_EF\",\t\t\t\t\tNULL,\t0,{NULL}, \"Provides EF_CANDLELIGHT...\"},\n//\t{\"EX_MOVETYPE_GIB\",\t\t\t\t\tNULL,\t0,{NULL}, \"Adds MOVETYPE_GIB - basically MOVETYPE_BOUNCE with gravity controlled by a cvar instead of just setting the .gravity field...\"},\n\t{\"EX_PROMPT\",\t\t\t\t\t\tNULL,\t3,{\"ex_prompt\", \"ex_promptchoice\", \"ex_clearprompt\"}, \"Engine-driven alternative to centerprint menus.\"},\n\t{\"FRIK_FILE\",\t\t\t\t\t\tcheck_notrerelease,\t11,{\"stof\", \"fopen\",\"fclose\",\"fgets\",\"fputs\",\"strlen\",\"strcat\",\"substring\",\"stov\",\"strzone\",\"strunzone\"}},\n\t{\"FTE_CALLTIMEOFDAY\",\t\t\t\tNULL,\t1,{\"calltimeofday\"}, \"Replication of mvdsv functionality (call calltimeofday to cause 'timeofday' to be called, with arguments that can be saved off to a global). Generally strftime is simpler to use.\"},\n\t{\"FTE_CSQC_ALTCONSOLES\",\t\t\tNULL,\t4,{\"con_getset\", \"con_printf\", \"con_draw\", \"con_input\"}, \"The engine tracks multiple consoles. These may or may not be directly visible to the user.\"},\n\t{\"FTE_CSQC_BASEFRAME\",\t\t\t\tNULL,\t0,{NULL}, \"Specifies that .basebone, .baseframe2, .baselerpfrac, baseframe1time, etc exist in csqc. These fields affect all bones in the entity's model with a lower index than the .basebone field, allowing you to give separate control to the legs of a skeletal model, without affecting the torso animations.\"},\n#ifdef HALFLIFEMODELS\n\t{\"FTE_CSQC_HALFLIFE_MODELS\"},\t\t//hl-specific skeletal model control\n#endif\n\t{\"FTE_CSQC_SERVERBROWSER\",\t\t\tNULL,\t12,{\"gethostcachevalue\", \"gethostcachestring\", \"resethostcachemasks\", \"sethostcachemaskstring\", \"sethostcachemasknumber\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\"resorthostcache\", \"sethostcachesort\", \"refreshhostcache\", \"gethostcachenumber\", \"gethostcacheindexforkey\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\"addwantedhostcachekey\", \"getextresponse\"}, \"Provides builtins to query the engine's serverbrowser servers list from ssqc. Note that these builtins are always available in menuqc.\"},\n\t{\"FTE_CSQC_SKELETONOBJECTS\",\t\tNULL,\t15,{\"skel_create\", \"skel_build\", \"skel_get_numbones\", \"skel_get_bonename\", \"skel_get_boneparent\", \"skel_find_bone\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\"skel_get_bonerel\", \"skel_get_boneabs\", \"skel_set_bone\", \"skel_premul_bone\", \"skel_premul_bones\", \"skel_copybones\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\"skel_delete\", \"frameforname\", \"frameduration\"}, \"Provides container objects for skeletal bone data, which can be modified on a per bone basis if needed. This allows you to dynamically generate animations (or just blend them with greater customisation) instead of being limited to a single animation or two.\"},\n\t{\"FTE_CSQC_RAWIMAGES\",\t\t\t\tNULL,\t2,{\"r_uploadimage\",\"r_readimage\"}, \"Provides raw rgba image access to csqc. With this, the csprogs can read textures into qc-accessible memory, modify it, and then upload it to the renderer.\"},\n\t{\"FTE_CSQC_RENDERTARGETS\",\t\t\tNULL,\t0,{NULL}, \"VF_RT_DESTCOLOUR exists and can be used to redirect any rendering to a texture instead of the screen.\"},\n\t{\"FTE_CSQC_REVERB\",\t\t\t\t\tNULL,\t1,{\"setup_reverb\"}, \"Specifies that the mod can create custom reverb effects. Whether they will actually be used or not depends upon the sound driver.\"},\n\t{\"FTE_CSQC_WINDOWCAPTION\",\t\t\tNULL,\t1,{\"setwindowcaption\"}, \"Provides csqc with the ability to change the window caption as displayed when running windowed or in the task bar when switched out.\"},\n\t{\"FTE_ENT_SKIN_CONTENTS\",\t\t\tNULL,\t0,{NULL}, \"self.skin = CONTENTS_WATER; makes a brush entity into water. use -16 for a ladder.\"},\n\t{\"FTE_ENT_UNIQUESPAWNID\"},\n\t{\"FTE_EXTENDEDTEXTCODES\"},\n\t{\"FTE_FORCESHADER\",\t\t\t\t\tNULL,\t1,{\"shaderforname\"}, \"Allows csqc to override shaders on models with an explicitly named replacement. Also allows you to define shaders with a fallback if it does not exist on disk.\"},\t//I'd rename this to _CSQC_ but it does technically provide this builtin to menuqc too, not that the forceshader entity field exists there... but whatever.\n\t{\"FTE_FORCEINFOKEY\",\t\t\t\tNULL,\t1,{\"forceinfokey\"},\t\"Provides an easy way to change a user's userinfo from the server.\"},\n\t{\"FTE_GFX_QUAKE3SHADERS\",\t\t\tNULL,\t0,{NULL},\t\"specifies that the engine has full support for vanilla quake3 shaders\"},\n\t{\"FTE_GFX_REMAPSHADER\",\t\t\t\tNULL,\t0,{NULL},\t\"With the raw power of stuffcmds, the r_remapshader console command is exposed! This mystical command can be used to remap any shader to another. Remapped shaders that specify $diffuse etc in some form will inherit the textures implied by the surface.\"},\n\t{\"FTE_GFX_IQM_HITMESH\",\t\t\t\tNULL,\t0,{NULL},\t\"Supports hitmesh iqm extensions. Also supports geomsets and embedded events.\"},\n\t{\"FTE_GFX_MODELEVENTS\",\t\t\t\tNULL,\t1,{\"processmodelevents\", \"getnextmodelevent\", \"getmodeleventidx\"},\t\"Provides a query for per-animation events in model files, including from progs/foo.mdl.events files.\"},\n\t{\"FTE_ISBACKBUFFERED\",\t\t\t\tNULL,\t1,{\"isbackbuffered\"}, \"Allows you to check if a client has too many reliable messages pending.\"},\n\t{\"FTE_MEMALLOC\",\t\t\t\t\tNULL,\t4,{\"memalloc\", \"memfree\", \"memcpy\", \"memfill8\"}, \"Allows dynamically allocating memory. Use pointers to access this memory. Memory will not be saved into saved games.\"},\n#ifdef HAVE_MEDIA_DECODER\n\t#if defined(_WIN32) && !defined(WINRT)\n\t{\"FTE_MEDIA_AVI\",\t\t\t\t\tNULL,\t0,{NULL}, \"playfilm command supports avi files.\"},\n\t#endif\n\t#ifdef Q2CLIENT\n\t{\"FTE_MEDIA_CIN\",\t\t\t\t\tNULL,\t0,{NULL}, \"playfilm command supports q2 cin files.\"},\n\t#endif\n\t#ifdef Q3CLIENT\n\t{\"FTE_MEDIA_ROQ\",\t\t\t\t\tNULL,\t0,{NULL}, \"playfilm command supports q3 roq files.\"},\n\t#endif\n#endif\n\t{\"FTE_MULTIPROGS\",\t\t\t\t\tNULL,\t5,{\"externcall\", \"addprogs\", \"externvalue\", \"externset\", \"instr\"}, \"Multiple progs.dat files can be loaded inside the same qcvm. Insert new ones with addprogs inside the 'init' function, and use externvalue+externset to rewrite globals (and hook functions) to link them together. Note that the result is generally not very clean unless you carefully design for it beforehand.\"},\t//multiprogs functions are available.\n\t{\"FTE_MULTITHREADED\",\t\t\t\tNULL,\t3,{\"sleep\", \"fork\", \"abort\"}, \"Faux multithreading, allowing multiple contexts to run in sequence.\"},\n\t{\"FTE_MVD_PLAYERSTATS\",\t\t\t\tNULL,\t0,{NULL}, \"In csqc, getplayerstat can be used to query any player's stats when playing back MVDs. isdemo will return 2 in this case.\"},\n#ifdef SERVER_DEMO_PLAYBACK\n\t{\"FTE_MVD_PLAYBACK\",\t\t\t\tNOBI\t\"The server itself is able to play back MVDs.\"},\n#endif\n#ifdef SVCHAT\n\t{\"FTE_QC_NPCCHAT\",\t\t\t\t\tNULL,\t1,{\"chat\"}},\t//server looks at chat files. It automagically branches through calling qc functions as requested.\n#endif\n#ifdef PSET_SCRIPT\n\t{\"FTE_PART_SCRIPT\",\t\t\t\t\tNULL,\t0,{NULL}, \"Specifies that the r_particledesc cvar can be used to select a list of particle effects to load from particles/foo.cfg, the format of which is documented elsewhere.\"},\n\t{\"FTE_PART_NAMESPACES\",\t\t\t\tNULL,\t0,{NULL}, \"Specifies that the engine can use foo.bar to load effect foo from particle description bar. When used via ssqc, this should cause the client to download whatever effects as needed.\"},\n#ifdef HAVE_LEGACY\n\t{\"FTE_PART_NAMESPACE_EFFECTINFO\",\tNULL,\t0,{NULL}, \"Specifies that effectinfo.bar can load effects from effectinfo.txt for DP compatibility.\"},\n#endif\n#endif\n\n\t{\"FTE_PEXT_SETVIEW\",\t\t\t\tcheck_pext_setview, 0,{NULL}, \"NQ's svc_setview works correctly even in quakeworld\"},\n\t{\"FTE_PEXT_LIGHTSTYLECOL\",\t\t\tcheck_pext_lightstylecol},\t//lightstyles may have colours.\n\t{\"FTE_PEXT_VIEW2\",\t\t\t\t\tcheck_pext_view2},\t\t//secondary view.\n//\t{\"FTE_PEXT_ACURATETIMINGS\",\t\t\tcheck_pext_senttimings},\t\t//allows full interpolation\n//\t{\"FTE_PEXT_SOUNDDBL\",\t\t\t\tcheck_pext_sounddbl},\t//twice the sound indexes\n\t{\"FTE_PEXT_FATNESS\",\t\t\t\tcheck_pext_fatness},\t\t//entities may be expanded along their vertex normals\n\t{\"FTE_PEXT_TE_BULLET\",\t\t\t\tcheck_pext_tebullet},\t//additional particle effect. Like TE_SPIKE and TE_SUPERSPIKE\n//\t{\"FTE_PEXT_HULLSIZE\",\t\t\t\tcheck_pext_},\t//means we can tell a client to go to crouching hull\n//\t{\"FTE_PEXT_MODELDBL\",\t\t\t\tcheck_pext_modeldbl},\t//max of 512 models\n//\t{\"FTE_PEXT_ENTITYDBL\",\t\t\t\tcheck_pext_},\t//max of 1024 ents\n//\t{\"FTE_PEXT_ENTITYDBL2\",\t\t\t\tcheck_pext_},\t//max of 2048 ents\n\t{\"FTE_PEXT_FLOATCOORDS\",\t\t\tcheck_pext_floatcoords},\n\t{\"FTE_PEXT_Q2BSP\",\t\t\t\t\tcheck_pext_q2bsp, 0,{NULL},\"Specifies that the client supports q2bsps.\"},\n\t{\"FTE_PEXT_Q3BSP\",\t\t\t\t\tcheck_pext_q3bsp, 0,{NULL},\"Specifies that the client supports q3bsps.\"},\t//quake3 bsp support. dp probably has an equivelent, but this is queryable per client.\n\t{\"FTE_HEXEN2\",\t\t\t\t\t\tcheck_pext_hexen2,\t3,{\"particle2\", \"particle3\", \"particle4\"}},\t\t\t\t//client can use hexen2 maps. server can use hexen2 progs\n\t{\"FTE_PEXT_SPAWNSTATIC\",\t\t\tcheck_pext_spawnstatic},\t//means that static entities can have alpha/scale and anything else the engine supports on normal ents. (Added for >256 models, while still being compatible - previous system failed with -1 skins)\n\t{\"FTE_PEXT_CUSTOMTENTS\",\t\t\tcheck_pext_customtempeffects,\t2,{\"RegisterTempEnt\", \"CustomTempEnt\"}},\n\t{\"FTE_PEXT_256PACKETENTITIES\",\t\tcheck_pext_256packetentities, 0,{NULL},\"Specifies that the client is not limited to vanilla's limit of only 64 ents visible at once.\"},\t//client is able to receive unlimited packet entities (server caps itself to 256 to prevent insanity).\n//\t{\"PEXT_CHUNKEDDOWNLOADS\",\t\t\tcheck_pext_chunkeddownloads},\n//\t{\"PEXT_DPFLAGS\",\t\t\t\t\tcheck_pext_dpflags},\n\n\n\t{\"FTE_QC_BASEFRAME\",\t\t\t\tNULL,\t0,{NULL}, \"Specifies that .basebone and .baseframe exist in ssqc. These fields affect all bones in the entity's model with a lower index than the .basebone field, allowing you to give separate control to the legs of a skeletal model, without affecting the torso animations, from ssqc.\"},\n\t{\"FTE_QC_FILE_BINARY\",\t\t\t\tNULL,\t4,{\"fread\",\"fwrite\",\"fseek\",\"fsize\"}, \"Extends FRIK_FILE with binary read+write, as well as allowing seeking. Requires pointers.\"},\n\t{\"FTE_QC_CHANGELEVEL_HUB\",\t\t\tNULL,\t0,{NULL}, \"Adds an extra argument to changelevel which is carried over to the next map in the 'startspot' global. Maps will be saved+reloaded until the extra argument is omitted again, purging all saved maps. Saved games will contain a copy of each preserved map. parm1-parm64 globals can be used, giving more space to transfer more player data.\"},\n\t{\"FTE_QC_CHECKCOMMAND\",\t\t\t\tNULL,\t1,{\"checkcommand\"}, \"Provides a way to test if a console command exists, and whether its a command/alias/cvar. Does not say anything about the expected meanings of any arguments or values.\"},\n\t{\"FTE_QC_CHECKPVS\",\t\t\t\t\tNULL,\t1,{\"checkpvs\"}},\n\t{\"FTE_QC_CROSSPRODUCT\",\t\t\t\tNULL,\t1,{\"crossproduct\"}},\n\t{\"FTE_QC_CUSTOMSKINS\",\t\t\t\tNULL,\t1,{\"setcustomskin\", \"loadcustomskin\", \"applycustomskin\", \"releasecustomskin\"}, \"The engine supports the use of q3 skins, as well as the use of such skin 'files' to specify rich top+bottom colours, qw skins, geomsets, or texture composition even on non-players..\"},\n\t{\"FTE_QC_DIGEST_SHA1\",\t\t\t\tNOBI\t\"The digest_hex builtin supports 160-bit sha1 hashes.\"},\n\t{\"FTE_QC_DIGEST_SHA224\",\t\t\tNOBI\t\"The digest_hex builtin supports 224-bit sha2 hashes.\"},\n\t{\"FTE_QC_DIGEST_SHA384\",\t\t\tNOBI\t\"The digest_hex builtin supports 384-bit sha2 hashes.\"},\n\t{\"FTE_QC_DIGEST_SHA512\",\t\t\tNOBI\t\"The digest_hex builtin supports 512-bit sha2 hashes.\"},\n\t{\"FTE_QC_FS_SEARCH_SIZEMTIME\",\t\tNULL,\t2,{\"search_getfilesize\", \"search_getfilemtime\"}},\n\t{\"FTE_QC_HARDWARECURSORS\",\t\t\tNULL,\t0,{NULL}, \"setcursormode exists in both csqc+menuqc, and accepts additional arguments to specify a cursor image to use when this module has focus. If the image exceeds hardware limits (or hardware cursors are unsupported), it will be emulated using regular draws - this at least still avoids conflicting cursors as only one will ever be used, even if console+menu+csqc are all overlayed.\"},\n\t{\"FTE_QC_HASHTABLES\",\t\t\t\tNULL,\t6,{\"hash_createtab\", \"hash_destroytab\", \"hash_add\", \"hash_get\", \"hash_delete\", \"hash_getkey\"}, \"Provides efficient string-based lookups.\"},\n\t{\"FTE_QC_INFOKEY\",\t\t\t\t\tcheck_notrerelease,\t2,{\"infokey\", \"stof\"}, \"QuakeWorld's infokey builtin works, and reports at least name+topcolor+bottomcolor+ping(in ms)+ip(unmasked, but not always ipv4)+team(aka bottomcolor in nq). Does not require actual localinfo/serverinfo/userinfo, but they're _highly_ recommended to any engines with csqc\"},\n\t{\"FTE_QC_INTCONV\",\t\t\t\t\tNULL,\t6,{\"stoi\", \"itos\", \"stoh\", \"htos\", \"itof\", \"ftoi\"}, \"Provides string<>int conversions, including hex representations.\"},\n\t{\"FTE_QC_MATCHCLIENTNAME\",\t\t\tNULL,\t1,{\"matchclientname\"}},\n\t{\"FTE_QC_MULTICAST\",\t\t\t\tNULL,\t1,{\"multicast\"}, \"QuakeWorld's multicast builtin works along with MSG_MULTICAST, but also with unicast support.\"},\n//\t{\"FTE_QC_MESHOBJECTS\",\t\t\t\tNULL,\t0,{\"mesh_create\", \"mesh_build\", \"mesh_getvertex\", \"mesh_getindex\", \"mesh_setvertex\", \"mesh_setindex\", \"mesh_destroy\"}, \"Provides qc with the ability to create its own meshes.\"},\n\t{\"FTE_QC_PAUSED\"},\n#ifdef QCGC\n\t{\"FTE_QC_PERSISTENTTEMPSTRINGS\",\tNOBI\t \"Supersedes DP_QC_MULTIPLETEMPSTRINGS. Temp strings are garbage collected automatically, and do not expire while they're still in use. This makes strzone redundant.\"},\n#endif\n#ifdef RAGDOLL\n\t{\"FTE_QC_RAGDOLL_WIP\",\t\t\t\tNULL,\t1,{\"skel_ragupdate\", \"skel_set_bone_world\", \"skel_mmap\"}},\n#endif\n\t{\"FTE_QC_SENDPACKET\",\t\t\t\tNULL,\t1,{\"sendpacket\"}, \"Allows the use of out-of-band udp packets to/from other hosts. Includes the SV_ParseConnectionlessPacket event.\"},\n\t{\"FTE_QC_STUFFCMDFLAGS\",\t\t\tNULL,\t1,{\"stuffcmdflags\"}, \"Variation on regular stuffcmd that gives control over how spectators/mvds should be treated.\"},\n\t{\"FTE_QC_TRACETRIGGER\"},\n#ifdef Q2CLIENT\n\t{\"FTE_QUAKE2_CLIENT\",\t\t\t\tNULL,\t0,{NULL}, \"This engine is able to act as a quake2 client\"},\n#endif\n#ifdef Q2SERVER\n\t{\"FTE_QUAKE2_SERVER\",\t\t\t\tNULL,\t0,{NULL}, \"This engine is able to act as a quake2 server\"},\n#endif\n#ifdef Q3CLIENT\n\t{\"FTE_QUAKE3_CLIENT\",\t\t\t\tNULL,\t0,{NULL}, \"This engine is able to act as a quake3 client\"},\n#endif\n#ifdef Q3SERVER\n\t{\"FTE_QUAKE3_SERVER\",\t\t\t\tNULL,\t0,{NULL}, \"This engine is able to act as a quake3 server\"},\n#endif\n\t{\"FTE_SOLID_BSPTRIGGER\",\t\t\tNOBI\t\t\t\"Allows for mappers to use shaped triggers instead of being limited to axially aligned triggers.\"},\n\t{\"FTE_SOLID_LADDER\",\t\t\t\tNOBI\t\t\t\"Allows a simple trigger to remove effects of gravity (solid 20). obsolete. will prolly be removed at some point as it is not networked properly. Use FTE_ENT_SKIN_CONTENTS\"},\n\t{\"FTE_SPLITSCREEN\",\t\t\t\t\tNOBI\t\t\t\"Client supports splitscreen, controlled via cl_splitscreen. Servers require allow_splitscreen 1 if splitscreen is to be used over the internet. Mods that use csqc will need to be aware for this to work properly. per-client networking may be problematic.\"},\n\n#ifdef SQL\n\t// serverside SQL functions for managing an SQL database connection\n\t{\"FTE_SQL\",\t\t\t\t\t\t\tNULL,\t9,{\"sqlconnect\",\"sqldisconnect\",\"sqlopenquery\",\"sqlclosequery\",\"sqlreadfield\",\"sqlerror\",\"sqlescape\",\"sqlversion\",\n\t\t\t\t\t\t\t\t\t\t\t\t  \"sqlreadfloat\"}, \"Provides sql* builtins which can be used for sql database access\"},\n#ifdef USE_MYSQL\n\t{\"FTE_SQL_MYSQL\",\t\t\t\t\tNULL,\t0, NULL, {NULL}, \"SQL functionality is able to utilise mysql\"},\n#endif\n#ifdef USE_SQLITE\n\t{\"FTE_SQL_SQLITE\",\t\t\t\t\tNULL,\t0,{NULL}, \"SQL functionality is able to utilise sqlite databases\"},\n#endif\n#endif\n\n\t//eperimental advanced strings functions.\n\t//reuses the FRIK_FILE builtins (with substring extension)\n\t{\"FTE_STRINGS\",\t\t\t\t\t\tcheck_notrerelease,\t17,{\"stof\", \"strlen\",\"strcat\",\"substring\",\"stov\",\"strzone\",\"strunzone\",\n\t\t\t\t\t\t\t\t\t\t\t\t   \"strstrofs\", \"str2chr\", \"chr2str\", \"strconv\", \"infoadd\", \"infoget\", \"strncmp\", \"strcasecmp\", \"strncasecmp\", \"strpad\"}, \"Extra builtins (and additional behaviour) to make string manipulation easier\"},\n\t{\"FTE_SV_POINTPARTICLES\",\t\t\tcheck_pext_pointparticle,\t3,{\"particleeffectnum\", \"pointparticles\", \"trailparticles\"}, \"Specifies that particleeffectnum, pointparticles, and trailparticles exist in ssqc as well as csqc. particleeffectnum acts as a precache, allowing ssqc values to be networked up with csqc for use. Use in combination with FTE_PART_SCRIPT+FTE_PART_NAMESPACES to use custom effects. This extension is functionally identical to the DP version, but avoids any misplaced assumptions about the format of the client's particle descriptions.\"},\n\t{\"FTE_SV_REENTER\"},\n\t{\"FTE_TE_STANDARDEFFECTBUILTINS\",\tNULL,\t14,{\"te_gunshot\", \"te_spike\", \"te_superspike\", \"te_explosion\", \"te_tarexplosion\", \"te_wizspike\", \"te_knightspike\", \"te_lavasplash\",\n\t\t\t\t\t\t\t\t\t\t\t\t   \"te_teleport\", \"te_lightning1\", \"te_lightning2\", \"te_lightning3\", \"te_lightningblood\", \"te_bloodqw\"}, \"Provides builtins to replace writebytes, with a QW compatible twist.\"},\n#ifdef TERRAIN\n\t{\"FTE_TERRAIN_MAP\",\t\t\t\t\tNULL,\t1,{\"terrain_edit\"}, \"This engine supports .hmp files, as well as terrain embedded within bsp files.\"},\n\t{\"FTE_RAW_MAP\",\t\t\t\t\t\tNULL,\t7,{\"brush_get\",\"brush_create\",\"brush_delete\",\"brush_selected\",\"brush_getfacepoints\",\"brush_calcfacepoints\",\"brush_findinvolume\"}, \"This engine supports directly loading .map files, as well as realtime editing of the various brushes.\"},\n#endif\n\t{\"FTE_INFOBLOBS\",\t\t\t\t\tcheck_pext2_infoblobs,\t0,{\"forceinfokeyblob\", \"getplayerkeyblob\", \"setlocaluserinfoblob\", \"getlocaluserinfoblob\", \"serverkeyblob\"}, \"Removes the length limits on user/server/local info strings, and allows embedded nulls and other otherwise-reserved characters. This can be used to network avatar images and the like, or other binary data.\"},\n\t{\"FTE_VRINPUTS\",\t\t\t\t\tcheck_pext2_vrinputs,\t0,{NULL}, \"input_weapon, input_left_*, input_right_*, input_head_* work, both in csqc (as inputs with suitable plugin/hardware support) and ssqc (available in PlayerPreThink).\"},\n\n\t{\"KRIMZON_SV_PARSECLIENTCOMMAND\",\tNULL,\t3,{\"clientcommand\", \"tokenize\", \"argv\"}, \"SSQC's SV_ParseClientCommand function is able to handle client 'cmd' commands. The tokenizing parts also work in csqc.\"},\t//very very similar to the mvdsv system.\n\t{\"NEH_CMD_PLAY2\"},\n\t{\"NEH_RESTOREGAME\"},\n\t//{\"PRYDON_CLIENTCURSOR\",\t\t\tcheck_pext2_prydoncursor},\n//\t{\"QEX_QC_DRAWING\",\t\t\t\t\tNULL,\n//\t{\"QEX_QC_FORMATTEDPRINTS\",\t\t\tcheck_rerelease,\t3,{\"centerprint_qex\",\"sprint_qex\",\"bprint_qex\"}},\n\t{\"QSG_CVARSTRING\",\t\t\t\t\tNULL,\t1,{\"qsg_cvar_string\"}},\n//\t{\"QUAKE_EX\",\t\t\t\t\t\tcheck_rerelease},\t//we arn't. there'll be lots of other conflicts, so just don't advertise it.\n\t{\"QW_ENGINE\",\t\t\t\t\t\tcheck_notrerelease,\t3,{\"infokey\", \"stof\", \"logfrag\"}},\t//warning: interpretation of .skin on players can be dodgy, as can some other QW features that differ from NQ.\n\t{\"QWE_MVD_RECORD\",\t\t\t\t\tNOBI\t\"You can use the easyrecord command to record MVD demos serverside.\"},\t//Quakeworld extended get the credit for this one. (mvdsv)\n\t{\"TEI_MD3_MODEL\"},\n\t{\"TEI_SHOWLMP2\",\t\t\t\t\tcheck_pext_showpic,\t6,{\"showpic\", \"hidepic\", \"movepic\", \"changepic\", \"showpicent\", \"hidepicent\"}},\t//telejano doesn't actually export the moveent/changeent (we don't want to either cos it would stop frik_file stuff being autoregistered)\n\t{\"TENEBRAE_GFX_DLIGHTS\",\t\t\tNULL,\t0,{NULL}, \"Allows ssqc to attach rtlights to entities with various special properties.\"},\n//\t{\"TQ_RAILTRAIL\"},\t//treat this as the ZQ style railtrails which the client already supports, okay so the preparse stuff needs strengthening.\n\t{\"ZQ_MOVETYPE_FLY\",\t\t\t\t\tNOBI\t\"MOVETYPE_FLY works on players.\"},\n\t{\"ZQ_MOVETYPE_NOCLIP\",\t\t\t\tNOBI\t\"MOVETYPE_NOCLIP works on players.\"},\n\t{\"ZQ_MOVETYPE_NONE\",\t\t\t\tNOBI\t\"MOVETYPE_NONE works on players.\"},\n//\t{\"ZQ_QC_PARTICLE\"},\t//particle builtin works in QW ( we don't mimic ZQ fully though)\n\t{\"ZQ_VWEP\",\t\t\t\t\t\t\tNULL,\t1,{\"precache_vwep_model\"}},\n\n\n\t{\"ZQ_QC_STRINGS\",\t\t\t\t\tcheck_notrerelease,\t7,{\"stof\", \"strlen\",\"strcat\",\"substring\",\"stov\",\"strzone\",\"strunzone\"}, \"The strings-only subset of FRIK_FILE is supported.\"}\t//a trimmed down FRIK_FILE.\n};\nunsigned int QSG_Extensions_count = sizeof(QSG_Extensions)/sizeof(QSG_Extensions[0]);\n#endif\n"
  },
  {
    "path": "engine/common/pr_common.h",
    "content": "#ifndef PR_COMMON_H\n#define PR_COMMON_H\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include \"progtype.h\"\n#include \"progslib.h\"\n\n#define AREAGRIDPERENT 16\n\nstruct wedict_s\n{\n\tenum ereftype_e\tereftype;\n\tfloat\t\t\tfreetime; // sv.time when the object was freed\n\tint\t\t\t\tentnum;\n\tunsigned int\tfieldsize;\n\tpbool\t\t\treadonly;\t//world\n#ifdef VM_Q1\n\tcomentvars_t\t*v;\n\tcomextentvars_t\t*xv;\n#else\n\tunion {\n\t\tcomentvars_t\t*v;\n\t\tcomentvars_t\t*xv;\n\t};\n#endif\n\t/*the above is shared with qclib*/\n#ifdef USEAREAGRID\n\tareagridlink_t\tgridareas[AREAGRIDPERENT];\t//on overflow, use the inefficient overflow list.\n\tsize_t\t\t\tgridareasequence;\t//used to avoid iterrating the same ent twice.\n#else\n\tlink_t\tarea;\n#endif\n\tpvscache_t pvsinfo;\n\tint lastruntime;\n\tint solidsize;\n\n#ifdef USERBE\n\tentityrbe_t rbe;\n#endif\n\t/*the above is shared with ssqc*/\n};\n\n#define G_PROG G_FLOAT\n\n//the checkextension system asks for a name for the extension.\n//the ebfs version is a function that returns a builtin number.\n//thus this system requires various builtins to exist at specific numbers.\n//this competes against checkbuiltin(funcreference).\ntypedef struct {\n\tworld_t *world;\n\tunsigned int pext1, pext2;\n} extcheck_t;\ntypedef struct qc_extension_s {\n\tchar *name;\n\tqboolean(*extensioncheck)(extcheck_t *info);\n\tint numbuiltins;\n\tchar *builtinnames[21];\t//extend freely\n\tchar *description;\n} qc_extension_t;\n\nextern qc_extension_t QSG_Extensions[];\nextern unsigned int QSG_Extensions_count;\n\npbool QDECL QC_WriteFile(const char *name, void *data, int len);\nvoid *VARGS PR_CB_Malloc(int size);\t//these functions should be tracked by the library reliably, so there should be no need to track them ourselves.\nvoid VARGS PR_CB_Free(void *mem);\n\nint PR_Printf (const char *fmt, ...);\nint PR_DPrintf (const char *fmt, ...);\nvoid PF_InitTempStrings(pubprogfuncs_t *prinst);\nstring_t PR_TempString(pubprogfuncs_t *prinst, const char *str);\t//returns a tempstring containing str\nchar *PF_TempStr(pubprogfuncs_t *prinst);\t//returns a tempstring which can be filled in with whatever junk you want.\n\n#ifdef QCVM_64\n\t#define VM_VECTORARG(name, ofs) vec3_t name = {G_FLOAT(ofs+0),G_FLOAT(ofs+1),G_FLOAT(ofs+2)}\n#else\n\t#define VM_VECTORARG(name, ofs) pvec_t *name = G_VECTOR(ofs)\n#endif\n\n#define\tRETURN_SSTRING(s) (((int *)pr_globals)[OFS_RETURN] = PR_SetString(prinst, s))\t//static - exe will not change it.\n#define\tRETURN_TSTRING(s) (((int *)pr_globals)[OFS_RETURN] = PR_TempString(prinst, s))\t//temp (static but cycle buffers)\nextern cvar_t pr_tempstringsize;\nextern cvar_t pr_tempstringcount;\nextern cvar_t pr_enable_profiling;\nextern cvar_t pr_fixbrokenqccarrays;\nextern cvar_t pr_gc_threaded;\n\nextern int qcinput_scan;\nextern int qcinput_unicode;\nint MP_TranslateFTEtoQCCodes(keynum_t code);\nkeynum_t MP_TranslateQCtoFTECodes(int code);\nqboolean WPhys_Push (world_t *w, wedict_t *pusher, vec3_t move, vec3_t amove);\n\n#ifdef ENGINE_ROUTING\n//sv_move.c routing\nvoid QCBUILTIN PF_route_calculate (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid PR_Route_Shutdown (world_t *world);\nvoid PR_Route_Visualise (void);\nvoid PR_Route_Init (void);\n#endif\n\n//known progs versions...\nenum\n{\n\tPROGHEADER_CRC_QW\t\t= 54730,\n\tPROGHEADER_CRC_NQ\t\t= 5927,\n\tPROGHEADER_CRC_PREREL\t= 26940,\t//prerelease\n\tPROGHEADER_CRC_TENEBRAE\t= 32401,\t//tenebrae\n\tPROGHEADER_CRC_H2\t\t= 38488,\t//basic hexen2\n\tPROGHEADER_CRC_H2MP\t\t= 26905,\t//hexen2 mission pack uses slightly different defs... *sigh*...\n\tPROGHEADER_CRC_H2DEMO\t= 14046,\t//I'm guessing this is from the original release or something\n\tPROGHEADER_CRC_CSQC\t\t= 22390,\n\tPROGHEADER_CRC_CSQC_DP\t= 52195,\n\tPROGHEADER_CRC_MENUQC\t= 10020\n};\n\n//pr_cmds.c builtins that need to be moved to a common.\nvoid VARGS PR_BIError(pubprogfuncs_t *progfuncs, char *format, ...) LIKEPRINTF(2);\nvoid QCBUILTIN PF_print (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_dprint (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_error (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_rint (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_floor (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_ceil (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_anglemod (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_anglesub (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_vectorvectors (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_crossproduct (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_Tokenize  (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_tokenizebyseparator  (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_tokenize_console  (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_ArgV  (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_argv_start_index  (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_argv_end_index  (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_FindString (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_FindList (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_nextent (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_Sin (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_Cos (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_Sqrt (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_bound (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_mod (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_strlen(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_strcat (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_createbuffer (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_ftos (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_fabs (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_vtos (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_etos (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_stof (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_mod (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_substring (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_stov (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_strzone(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_strunzone(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_Spawn (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_spawn_object (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_respawnedict (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_entityprotection (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_copyentity (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_droptofloor (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_checkbottom (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_min (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_max (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_registercvar (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_pow (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_Logarithm (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_asin (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_acos (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_atan (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_atan2 (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_tan (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_localcmd (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_sprintf_internal (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals, const char *s, int firstarg, char *outbuf, int outbuflen);\nvoid QCBUILTIN PF_sprintf (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_random (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_fclose (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_fputs (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_fgets (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_fwrite (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_fread (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_fseek32 (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_fsize32 (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_fseek64 (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_fsize64 (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_normalize (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_vlen (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_vhlen (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_changeyaw (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_changepitch (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_vectoyaw (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_vectoangles (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_rotatevectorsbyangles (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_rotatevectorsbymatrix (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_findchain (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_findchainfloat (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_coredump (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_setwatchpoint (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_traceon (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_traceoff (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_eprint (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_search_begin (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_search_end (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_search_getsize (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_search_getfilename (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_search_getfilesize (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_search_getfilemtime (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_search_getpackagename (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_search_fopen (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_isfunction (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_callfunction (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_writetofile(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_loadfromfile (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_loadfromdata (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_parseentitydata(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_generateentitydata(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_WasFreed (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_break (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_crc16 (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\n\nenum\n{\t//return values for cvar_type builtin.\n\tCVAR_TYPEFLAG_EXISTS\t\t=1u<<0, //cvar actually exists.\n\tCVAR_TYPEFLAG_SAVED\t\t\t=1u<<1, //cvar is flaged for archival (might need cfg_save to actually save)\n\tCVAR_TYPEFLAG_PRIVATE\t\t=1u<<2, //QC is not allowed to read.\n\tCVAR_TYPEFLAG_ENGINE\t\t=1u<<3, //cvar was created by the engine itself (not user/mod created)\n\tCVAR_TYPEFLAG_HASDESCRIPTION=1u<<4, //cvar_description will return something (hopefully) useful\n\tCVAR_TYPEFLAG_READONLY\t\t=1u<<5, //cvar may not be changed by qc.\n\t//any extras added here should be shared with DP.\n};\nvoid QCBUILTIN PF_cvar_type (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_uri_escape  (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_uri_unescape  (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_uri_get  (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_itos (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_stoi (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_stoh (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_htos (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_ftoi (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_itof (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_ftou (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_utof (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid PR_fclose_progs (pubprogfuncs_t *prinst);\nconst char *PF_VarString (pubprogfuncs_t *prinst, int\tfirst, struct globalvars_s *pr_globals);\nvoid PR_ProgsAdded(pubprogfuncs_t *prinst, int newprogs, const char *modulename);\nvoid PR_AutoCvar(pubprogfuncs_t *prinst, cvar_t *var);\nvoid QCBUILTIN PF_numentityfields (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_findentityfield (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_entityfieldref (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_entityfieldname (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_entityfieldtype (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_getentityfieldstring (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_putentityfieldstring (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_checkcommand (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_argescape(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\n\n\nvoid QCBUILTIN PF_getsurfacenumpoints(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_getsurfacepoint(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_getsurfacenormal(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_getsurfacetexture(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_getsurfacenearpoint(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_getsurfaceclippedpoint(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_getsurfacenumtriangles(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_getsurfacetriangle(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_getsurfacepointattribute(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_checkpvs(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_setattachment(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\n\n#ifndef SKELETALOBJECTS\n\t#define PF_gettaginfo\t\t\tPF_Fixme\n\t#define PF_gettagindex\t\t\tPF_Fixme\n\t#define PF_skintoname\t\t\tPF_Fixme\n\t#define PF_frametoname\t\t\tPF_Fixme\n\t#define PF_skel_set_bone_world\tPF_Fixme\n\t#define PF_skel_mmap\t\t\tPF_Fixme\n\t#define PF_skel_ragedit\t\t\tPF_Fixme\n\t#define PF_frameduration\t\tPF_Fixme\n\t#define PF_modelframecount\t\tPF_Fixme\n\t#define PF_frameforname\t\t\tPF_Fixme\n\t#define PF_frameforaction\t\tPF_Fixme\n\t#define PF_skel_delete\t\t\tPF_Fixme\n\t#define PF_skel_copybones\t\tPF_Fixme\n\t#define PF_skel_premul_bones\tPF_Fixme\n\t#define PF_skel_premul_bone\t\tPF_Fixme\n\t#define PF_skel_postmul_bones\tPF_Fixme\n\t#define PF_skel_postmul_bone\tPF_Fixme\n\t#define PF_skel_set_bone\t\tPF_Fixme\n\t#define PF_skel_get_boneabs\t\tPF_Fixme\n\t#define PF_skel_get_bonerel\t\tPF_Fixme\n\t#define PF_skel_find_bone\t\tPF_Fixme\n\t#define PF_skel_get_boneparent\tPF_Fixme\n\t#define PF_skel_get_bonename\tPF_Fixme\n\t#define PF_skel_get_numbones\tPF_Fixme\n\t#define PF_skel_build\t\t\tPF_Fixme\n\t#define PF_skel_build_ptr\t\tPF_Fixme\n\t#define PF_skel_create\t\t\tPF_Fixme\n\t#define PF_skinforname\t\t\tPF_Fixme\n\n\t#define PF_processmodelevents\tPF_Fixme\n\t#define PF_getnextmodelevent\tPF_Fixme\n\t#define PF_getmodeleventidx\t\tPF_Fixme\n#else\n\tvoid QCBUILTIN PF_skel_set_bone_world (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\n\tvoid QCBUILTIN PF_skel_mmap(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\n\tvoid QCBUILTIN PF_skel_ragedit(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\n\tvoid QCBUILTIN PF_skel_create (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\n\tvoid QCBUILTIN PF_skel_build (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\n\tvoid QCBUILTIN PF_skel_build_ptr (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\n\tvoid QCBUILTIN PF_skel_get_numbones (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\n\tvoid QCBUILTIN PF_skel_get_bonename (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\n\tvoid QCBUILTIN PF_skel_get_boneparent (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\n\tvoid QCBUILTIN PF_skel_find_bone (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\n\tvoid QCBUILTIN PF_skel_get_bonerel (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\n\tvoid QCBUILTIN PF_skel_get_boneabs (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\n\tvoid QCBUILTIN PF_skel_set_bone (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\n\tvoid QCBUILTIN PF_skel_premul_bone (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\n\tvoid QCBUILTIN PF_skel_premul_bones (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\n\tvoid QCBUILTIN PF_skel_postmul_bone (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\n\tvoid QCBUILTIN PF_skel_postmul_bones (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\n\tvoid QCBUILTIN PF_skel_copybones (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\n\tvoid QCBUILTIN PF_skel_delete (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\n\tvoid QCBUILTIN PF_frametoname (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\n\tvoid QCBUILTIN PF_skintoname (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\n\tvoid QCBUILTIN PF_frameforname (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\n\tvoid QCBUILTIN PF_frameforaction (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\n\tvoid QCBUILTIN PF_frameduration (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\n\tvoid QCBUILTIN PF_modelframecount (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\n\tvoid QCBUILTIN PF_skinforname (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\n\tvoid QCBUILTIN PF_gettaginfo (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\n\tvoid QCBUILTIN PF_gettagindex (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\n\n\tvoid QCBUILTIN PF_processmodelevents (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\n\tvoid QCBUILTIN PF_getnextmodelevent (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\n\tvoid QCBUILTIN PF_getmodeleventidx (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\n#endif\n\n#if defined(SKELETALOBJECTS) || defined(RAGDOLL)\n\tvoid skel_lookup(world_t *prinst, int skelidx, framestate_t *fte_restrict out);\n\tvoid skel_dodelete(world_t *world);\n\tvoid skel_reset(world_t *world);\n\tvoid skel_reload(void);\n\tvoid skel_updateentbounds(world_t *w, wedict_t *ent);\n#endif\nvoid QCBUILTIN PF_physics_supported(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_physics_enable(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_physics_addforce(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_physics_addtorque(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\n\nvoid QCBUILTIN PF_pushmove(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\n\n#ifdef TERRAIN\nvoid QCBUILTIN PF_terrain_edit(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_brush_get(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_brush_create(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_brush_delete(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_brush_selected(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_brush_getfacepoints(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_brush_calcfacepoints(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_brush_findinvolume(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_patch_getcp(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_patch_getmesh(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_patch_create(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_patch_evaluate(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\n#endif\n\nvoid QCBUILTIN PF_touchtriggers(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\n\n//pr_cmds.c builtins that need to be moved to a common.\nvoid VARGS PR_BIError(pubprogfuncs_t *progfuncs, char *format, ...) LIKEPRINTF(2);\ncvar_t *PF_Cvar_FindOrGet(const char *var_name);\nvoid QCBUILTIN PF_cvar_string (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_cvars_haveunsaved (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_cvar_set (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_cvar_setf (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_ArgC (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_strreplace (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_strireplace (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_randomvector (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nint PR_QCFile_From_VFS (pubprogfuncs_t *prinst, const char *name, vfsfile_t *f, qboolean write);\nint PR_QCFile_From_Buffer (pubprogfuncs_t *prinst, const char *name, void *buffer, size_t offset, size_t len);\nvoid QCBUILTIN PF_fopen (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\n\nvoid QCBUILTIN PF_fcopy (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_frename (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_fremove (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_fexists (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_rmtree (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\n\nvoid QCBUILTIN PF_FindString (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_FindFloat (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_FindFlags (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_findchain (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_findchainfloat (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_findchainflags (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_bitshift(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\n\nstruct qcstate_s *PR_CreateThread(pubprogfuncs_t *prinst, float retval, float resumetime, qboolean wait);\nvoid PR_ClearThreads(pubprogfuncs_t *prinst);\nvoid PR_RunThreads(world_t *world);\nvoid QCBUILTIN PF_Abort(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_Fork(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_Sleep(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_externcall (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_externrefcall (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_externvalue (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_externset (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_instr (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\n\nvoid QCBUILTIN PF_strlennocol (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_strdecolorize (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_strtolower (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_strtoupper (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_strftime (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\n\nvoid QCBUILTIN PF_strstrofs (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_str2chr (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_chr2str (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_strconv (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_infoadd (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_infoget (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_strncmp (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_strncasecmp (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_strpad (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_strtrim (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\n\nvoid QCBUILTIN PF_digest_hex (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_digest_ptr (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\n\nvoid QCBUILTIN PF_findradius_list (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_findradius (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_edict_for_num (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_num_for_edict (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_cvar_defstring (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_cvar_description (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\n\n//these functions are from pr_menu.c\n#define DRAWFLAG_NORMAL\t\t0\n#define DRAWFLAG_ADD\t\t1\n#define DRAWFLAG_MODULATE\t2\n//#define DRAWFLAG_MODULATE2\t3\n#define DRAWFLAG_2D\t\t\t(1u<<2)\n#define DRAWFLAG_TWOSIDED\t0x400\n#define DRAWFLAG_LINES\t\t0x800\nvoid QCBUILTIN PF_SubConGetSet (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_SubConPrintf (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_SubConDraw (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_SubConInput (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_CL_is_cached_pic (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_CL_precache_pic (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_CL_free_pic (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_CL_uploadimage (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_CL_readimage (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_CL_drawcharacter (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_CL_drawrawstring (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_CL_drawcolouredstring (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_CL_drawpic (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_CL_drawline (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_CL_drawfill (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_CL_drawsetcliparea (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_CL_drawresetcliparea (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_CL_drawgetimagesize (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_CL_stringwidth (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_CL_drawsubpic (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_CL_drawrotpic (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_CL_drawrotsubpic (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\n#ifdef HAVE_LEGACY\nvoid QCBUILTIN PF_CL_drawrotpic_dp (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\n#endif\nvoid QCBUILTIN PF_CL_findfont (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_CL_loadfont (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\n#if defined(CSQC_DAT) && !defined(SERVERONLY)\nvoid QCBUILTIN PF_R_PolygonBegin(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_R_PolygonVertex(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_R_PolygonEnd(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\n#else\n#define PF_R_PolygonBegin PF_Fixme\n#define PF_R_PolygonVertex PF_Fixme\n#define PF_R_PolygonEnd PF_Fixme\n#endif\n\nvoid QCBUILTIN PF_cl_getresolution (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_cl_gethostcachevalue (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_cl_gethostcachestring (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_cl_resethostcachemasks(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_cl_sethostcachemaskstring(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_cl_sethostcachemasknumber(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_cl_resorthostcache(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_cl_sethostcachesort(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_cl_refreshhostcache(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_cl_gethostcachenumber(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_cl_gethostcacheindexforkey(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_cl_addwantedhostcachekey(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_cl_getextresponse(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_netaddress_resolve(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_cl_getmousepos (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_cl_GetBindMap (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_cl_SetBindMap (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_cl_keynumtostring (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_cl_findkeysforcommand (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_cl_findkeysforcommandex (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_cl_stringtokeynum(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_cl_getkeybind (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_cl_setkeybind (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_cl_setmousetarget (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_cl_getmousetarget (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_cl_setmousepos (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_cl_setcursormode (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_cl_getcursormode (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_cl_clipboard_set(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_cl_setwindowcaption (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_cl_playingdemo (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_cl_runningserver (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_cl_getgamedirinfo (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_cl_getpackagemanagerinfo (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_cl_addprogs (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_cs_media_create (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_cs_media_destroy (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_cs_media_command (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_cs_media_keyevent (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_cs_media_mousemove (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_cs_media_resize (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_cs_media_get_texture_extent (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_cs_media_getposition (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_cs_media_getproperty (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_cs_media_setstate (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_cs_media_getstate (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_cs_media_restart (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\n\ntypedef enum{\n\tSLIST_HOSTCACHEVIEWCOUNT,\n\tSLIST_HOSTCACHETOTALCOUNT,\n\tSLIST_MASTERQUERYCOUNT,\n\tSLIST_MASTERREPLYCOUNT,\n\tSLIST_SERVERQUERYCOUNT,\n\tSLIST_SERVERREPLYCOUNT,\n\tSLIST_SORTFIELD,\n\tSLIST_SORTDESCENDING\n} hostcacheglobal_t;\nvoid QCBUILTIN PF_shaderforname (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_remapshader (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\n\nvoid QCBUILTIN PF_cl_sprint (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_cl_bprint (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_cl_clientcount (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_cl_localsound(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_cl_queueaudio(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_cl_getqueuedaudiotime(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_cl_SendPacket(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_cl_getlocaluserinfoblob (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_cl_getlocaluserinfostring (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_cl_setlocaluserinfo (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nconst char *PF_cl_serverkey_internal(const char *keyname);\nvoid QCBUILTIN PF_cl_serverkey (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_cl_serverkeyfloat (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_cl_serverkeyblob (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\n\nvoid QCBUILTIN PF_cl_gp_querywithcb(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_cl_gp_getbuttontype(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_cl_gp_rumble(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_cl_gp_rumbletriggers(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_cl_gp_setledcolor(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_cl_gp_settriggerfx(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\n\nvoid search_close_progs(pubprogfuncs_t *prinst, qboolean complain);\n\nvoid QCBUILTIN PF_buf_create  (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_buf_del  (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_buf_getsize  (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_buf_copy  (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_buf_sort  (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_buf_implode  (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_bufstr_get  (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_bufstr_set  (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_bufstr_add  (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_bufstr_free  (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_bufstr_find  (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_buf_cvarlist  (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_buf_loadfile  (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_buf_writefile  (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\n\nvoid QCBUILTIN PF_hash_createtab\t(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_hash_destroytab\t(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_hash_add\t(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_hash_get\t(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_hash_getcb\t(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_hash_delete\t(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_hash_getkey\t(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\n\nvoid QCBUILTIN PF_json_parse\t\t\t\t(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_json_get_value_type\t\t(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_json_get_integer\t\t\t(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_json_get_float\t\t\t(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_json_get_string\t\t\t(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_json_find_object_child\t(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_json_get_length\t\t\t(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_json_get_child_at_index\t(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_json_get_name\t\t\t\t(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\n#ifdef FTE_TARGET_WEB\nvoid QCBUILTIN PF_js_run_script\t\t\t\t(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\n#else\n#define PF_js_run_script PF_Ignore\n#endif\nvoid QCBUILTIN PF_base64encode(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_base64decode(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\n\nvoid QCBUILTIN PF_memalloc\t(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_memrealloc(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_memfree\t(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_memcmp\t(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_memcpy\t(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_memfill8\t(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_memgetval\t(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_memsetval\t(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_memptradd\t(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_memstrsize(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\n\nvoid QCBUILTIN PF_soundlength (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_calltimeofday (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_gettimef (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_gettimed (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\n\nvoid QCBUILTIN PF_whichpack (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\n\nint QDECL QCEditor (pubprogfuncs_t *prinst, const char *filename, int *line, int *statement, int firststatement, char *reason, pbool fatal);\nvoid PR_Common_Shutdown(pubprogfuncs_t *progs, qboolean errored);\nvoid PR_Common_SaveGame(vfsfile_t *f, pubprogfuncs_t *prinst, qboolean binary);\nqboolean PR_Common_LoadGame(pubprogfuncs_t *prinst, char *command, const char **file);\n\nconst void *PR_GetReadQCPtr(pubprogfuncs_t *prinst, int qcptr, int qcsize);\nvoid *PR_GetWriteQCPtr(pubprogfuncs_t *prinst, int qcptr, int qcsize);\n\nuploadfmt_t PR_TranslateTextureFormat(int qcformat);\nint PR_UnTranslateTextureFormat(uploadfmt_t fteformat);\n\n//FIXME\npbool PR_RunWarning (pubprogfuncs_t *ppf, char *error, ...);\n\n\n/*these are server ones, provided by pr_cmds.c, as required by pr_q1qvm.c*/\nint PF_ForceInfoKey_Internal(unsigned int entnum, const char *key, const char *value, size_t valsize);\n#ifdef VM_Q1\nvoid PR_SV_FillWorldGlobals(world_t *w);\nmodel_t *QDECL SVPR_GetCModel(world_t *w, int modelindex);\nvoid QCBUILTIN PF_WriteByte (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_WriteChar (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_WriteShort (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_WriteLong (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_WriteAngle (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_WriteCoord (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_WriteFloat (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_WriteEntity (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_multicast (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_svtraceline (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_changelevel (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_applylightstyle(int style, const char *val, vec3_t rgb);\nvoid PF_ambientsound_Internal (float *pos, const char *samp, float vol, float attenuation);\nvoid QCBUILTIN PF_makestatic (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_logfrag (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_ExecuteCommand  (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_setspawnparms (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_precache_vwep_model(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nint PF_checkclient_Internal (pubprogfuncs_t *prinst);\nint PF_precache_sound_Internal (pubprogfuncs_t *prinst, const char *s, qboolean queryonly);\nint PF_precache_model_Internal (pubprogfuncs_t *prinst, const char *s, qboolean queryonly);\nvoid PF_setmodel_Internal (pubprogfuncs_t *prinst, edict_t *e, const char *m);\nchar *PF_infokey_Internal (int entnum, const char *value);\nvoid PF_stuffcmd_Internal(int entnum, const char *str, unsigned int flags);\nvoid PF_centerprint_Internal (int entnum, qboolean plaque, const char *s);\nvoid PF_WriteString_Internal (int target, const char *str);\npbool QDECL ED_CanFree (edict_t *ed);\n#endif\n\n#ifdef HAVE_LEGACY\nunsigned int FTEToDPContents(unsigned int contents);\n#endif\n\n#define\tMOVETYPE_NONE\t\t\t0\t\t// never moves\n#define\tMOVETYPE_ANGLENOCLIP\t1\n#define\tMOVETYPE_ANGLECLIP\t\t2\n#define\tMOVETYPE_WALK\t\t\t3\t\t// gravity\n#define\tMOVETYPE_STEP\t\t\t4\t\t// gravity, special edge handling\n#define\tMOVETYPE_FLY\t\t\t5\n#define\tMOVETYPE_TOSS\t\t\t6\t\t// gravity\n#define\tMOVETYPE_PUSH\t\t\t7\t\t// no clip to world, push and crush\n#define\tMOVETYPE_NOCLIP\t\t\t8\n#define\tMOVETYPE_FLYMISSILE\t\t9\t\t// extra size to monsters\n#define\tMOVETYPE_BOUNCE\t\t\t10\n#define MOVETYPE_BOUNCEMISSILE\t11\t\t// bounce w/o gravity\n#define MOVETYPE_FOLLOW\t\t\t12\t\t// track movement of aiment\n#define MOVETYPE_H2PUSHPULL\t\t13\t\t// pushable/pullable object\n#define MOVETYPE_H2SWIM\t\t\t14\t\t// should keep the object in water\n#define MOVETYPE_6DOF\t\t\t30\t\t// flightsim mode\n#define MOVETYPE_WALLWALK\t\t31\t\t// walks up walls and along ceilings\n#define MOVETYPE_PHYSICS\t\t32\n#define MOVETYPE_FLY_WORLDONLY\t33\t\t//fly that collides only with world, keeping spectators within the world but free to pass through doors. avoids pvs issues with q3map2.\n\n// edict->solid values\n#define\tSOLID_NOT\t\t\t\t0\t\t// no interaction with other objects\n#define\tSOLID_TRIGGER\t\t\t1\t\t// touch on edge, but not blocking\n#define\tSOLID_BBOX\t\t\t\t2\t\t// touch on edge, block\n#define\tSOLID_SLIDEBOX\t\t\t3\t\t// touch on edge, but not an onground\n#define\tSOLID_BSP\t\t\t\t4\t\t// bsp clip, touch on edge, block\n#define\tSOLID_PHASEH2\t\t\t5\t\t// hexen2 flag - this is apparently a modifier for movetype rather than solidity - passes through FL_MONSTER or MOVETYPE_WALK ents\n#define\tSOLID_CORPSE\t\t\t5\t\t// non-solid to solid_slidebox entities and itself.\n#define SOLID_LADDER\t\t\t20\t\t//spike: legacy. forces FTECONTENTS_LADDER.\n#define SOLID_PORTAL\t\t\t21\t\t//1: traces always use point-size. 2: various movetypes automatically transform entities. 3: traces that impact portal bbox use a union. 4. traces ignore part of the world within the portal's box\n#define SOLID_BSPTRIGGER\t\t22\t\t//spike: like solid trigger, except uses bsp checks instead of just aabb.\n#define\tSOLID_PHYSICS_BOX\t\t32\t\t// deprecated. physics object (mins, maxs, mass, origin, axis_forward, axis_left, axis_up, velocity, spinvelocity)\n#define\tSOLID_PHYSICS_SPHERE\t33\t\t// deprecated. physics object (mins, maxs, mass, origin, axis_forward, axis_left, axis_up, velocity, spinvelocity)\n#define\tSOLID_PHYSICS_CAPSULE\t34\t\t// deprecated. physics object (mins, maxs, mass, origin, axis_forward, axis_left, axis_up, velocity, spinvelocity)\n#define SOLID_PHYSICS_TRIMESH\t35\n#define SOLID_PHYSICS_CYLINDER  36\n\n#define\tGEOMTYPE_NONE      -1\n#define\tGEOMTYPE_SOLID      0\n#define\tGEOMTYPE_BOX\t\t1\n#define\tGEOMTYPE_SPHERE\t\t2\n#define\tGEOMTYPE_CAPSULE\t3\n#define\tGEOMTYPE_TRIMESH\t4\n#define\tGEOMTYPE_CYLINDER\t5\n#define\tGEOMTYPE_CAPSULE_X\t6\n#define\tGEOMTYPE_CAPSULE_Y\t7\n#define\tGEOMTYPE_CAPSULE_Z\t8\n#define\tGEOMTYPE_CYLINDER_X\t9\n#define\tGEOMTYPE_CYLINDER_Y\t10\n#define\tGEOMTYPE_CYLINDER_Z\t11\n\n\n#define JOINTTYPE_POINT 1\n#define JOINTTYPE_HINGE 2\n#define JOINTTYPE_SLIDER 3\n#define JOINTTYPE_UNIVERSAL 4\n#define JOINTTYPE_HINGE2 5\n#define JOINTTYPE_FIXED -1\n\ntypedef struct\n{\n\tint version;\n\tint wedictsize;\t//sizeof(wedict_t)\n\n\tqboolean (QDECL *RegisterPhysicsEngine)(const char *enginename, void(QDECL*start_physics)(world_t*world));\t//returns false if there's already one active.\n\tvoid (QDECL *UnregisterPhysicsEngine)(const char *enginename);\t//returns false if there's already one active.\n\tqboolean (QDECL *GenerateCollisionMesh)(world_t *world, model_t *mod, wedict_t *ed, vec3_t geomcenter);\n\tvoid (QDECL *ReleaseCollisionMesh) (wedict_t *ed);\n\tvoid (QDECL *LinkEdict)(world_t *w, wedict_t *ed, qboolean touchtriggers);\n\n\tvoid (QDECL *VectorAngles)(const float *forward, const float *up, float *result, qboolean meshpitch);\n\tvoid (QDECL *AngleVectors)(const vec3_t angles, vec3_t forward, vec3_t right, vec3_t up);\n#define plugrbefuncs_name \"RBE\"\n} rbeplugfuncs_t;\n#define RBEPLUGFUNCS_VERSION 1\n\nqofs_t PR_ReadBytesString(char *str);\n\n\n#define\tDAMAGE_NO\t\t\t\t0\n#define\tDAMAGE_YES\t\t\t\t1\n#define\tDAMAGE_AIM\t\t\t\t2\n\n#define CLIENTTYPE_DISCONNECTED\t0\n#define CLIENTTYPE_REAL\t\t\t1\n#define CLIENTTYPE_BOT\t\t\t2\n#define CLIENTTYPE_NOTACLIENT\t3\n\nenum\n{\n\tRESSTATE_NOTKNOWN = 0,\n\tRESSTATE_NOTLOADED = 1,\n\tRESSTATE_LOADING = 2,\n\tRESSTATE_FAILED = 3,\n\tRESSTATE_LOADED = 4,\n\n\tRESSTATE_UNSUPPORTED = -1\n};\nenum\n{\n\tRESTYPE_MODEL,\n\tRESTYPE_SOUND,\n\tRESTYPE_PARTICLE,\n\tRESTYPE_SHADER,\n\tRESTYPE_SKIN,\n\tRESTYPE_TEXTURE\n};\n\n//shared constants\ntypedef enum\n{\n\tVF_MIN = 1,\n\tVF_MIN_X = 2,\n\tVF_MIN_Y = 3,\n\tVF_SIZE = 4,\n\tVF_SIZE_X = 5,\n\tVF_SIZE_Y = 6,\n\tVF_VIEWPORT = 7,\n\tVF_FOV = 8,\n\tVF_FOV_X = 9,\n\tVF_FOV_Y = 10,\n\tVF_ORIGIN = 11,\n\tVF_ORIGIN_X = 12,\n\tVF_ORIGIN_Y = 13,\n\tVF_ORIGIN_Z = 14,\n\tVF_ANGLES = 15,\n\tVF_ANGLES_X = 16,\n\tVF_ANGLES_Y = 17,\n\tVF_ANGLES_Z = 18,\n\tVF_DRAWWORLD = 19,\n\tVF_ENGINESBAR = 20,\n\tVF_DRAWCROSSHAIR = 21,\n\tVF_CARTESIAN_ANGLES = 22,\n\tVF_MINDIST = 23,\n\tVF_MAXDIST = 24,\n\n\t//this is a DP-compatibility hack.\n\tVF_CL_VIEWANGLES_V = 33,\n\tVF_CL_VIEWANGLES_X = 34,\n\tVF_CL_VIEWANGLES_Y = 35,\n\tVF_CL_VIEWANGLES_Z = 36,\n\n\n\t//33-36 used by DP...\n\tVF_PERSPECTIVE = 200,\n\t//201 used by DP... WTF? CLEARSCREEN\n\tVF_ACTIVESEAT = 202,\n\tVF_AFOV = 203,\t//aproximate fov (match what the engine would normally use for the fov cvar). p0=fov, p1=zoom\n\tVF_SCREENVSIZE = 204,\n\tVF_SCREENPSIZE = 205,\n\tVF_VIEWENTITY = 206,\n\tVF_STATSENTITY = 207,\t//the player number for the stats.\n\tVF_SCREENVOFFSET = 208,\n\n\tVF_RT_SOURCECOLOUR\t= 209,\n\tVF_RT_DEPTH\t\t\t= 210,\n\tVF_RT_RIPPLE\t\t= 211,\t/**/\n\tVF_RT_DESTCOLOUR0\t= 212,\n\tVF_RT_DESTCOLOUR1\t= 213,\n\tVF_RT_DESTCOLOUR2\t= 214,\n\tVF_RT_DESTCOLOUR3\t= 215,\n\tVF_RT_DESTCOLOUR4\t= 216,\n\tVF_RT_DESTCOLOUR5\t= 217,\n\tVF_RT_DESTCOLOUR6\t= 218,\n\tVF_RT_DESTCOLOUR7\t= 219,\n\tVF_ENVMAP\t\t\t= 220,\t//cubemap image for reflectcube\n\tVF_USERDATA\t\t\t= 221,\n\tVF_SKYROOM_CAMERA\t= 222,\n\tVF_PIXELPSCALE\t\t= 223,\t//[dpi_x, dpi_y, dpi_y/dpi_x]\n\tVF_PROJECTIONOFFSET\t= 224,\t//allows for off-axis projections.\n\tVF_VRBASEORIENTATION= 225,\t//specifies the worldspace coords+angles of the VR room space.\n\t//WARNING: update fteqcc when new entries are added.\n\n\n\tVF_DP_CLEARSCREEN\t\t= 201, //misnomer - NOTOVERLAY would be a better name. when set to false prevents any and all post-proc things that might write colour values in areas with no geometry there.\n\tVF_DP_FOG_DENSITY\t\t= 202, //misassigned - fuck DP and their complete lack of respect for existing implemenetations\n\tVF_DP_FOG_COLOR\t\t\t= 203, //misassigned\n\tVF_DP_FOG_COLOR_R\t\t= 204, //misassigned\n\tVF_DP_FOG_COLOR_G\t\t= 205, //misassigned\n\tVF_DP_FOG_COLOR_B\t\t= 206, //misassigned\n\tVF_DP_FOG_ALPHA\t\t\t= 207, //misassigned\n\tVF_DP_FOG_START\t\t\t= 208, //misassigned\n\tVF_DP_FOG_END   \t\t= 209, //misassigned\n\tVF_DP_FOG_HEIGHT\t\t= 210, //misassigned\n\tVF_DP_FOG_FADEDEPTH\t\t= 211, //misassigned\n\tVF_DP_MAINVIEW\t\t\t= 400, // defective. should have been a 1-based viewid instead, allowing for per-view motionblur instead of disabling it outright\n\tVF_DP_MINFPS_QUALITY\t= 401,\t//multiplier for lod and culling to try to reduce costs.\n} viewflags;\n\n/*FIXME: this should be changed*/\n#define CSQC_API_VERSION 1.0f\n\n#define CSQCRF_VIEWMODEL\t\t\t\t1 //Not drawn in mirrors\n#define CSQCRF_EXTERNALMODEL\t\t\t2 //drawn ONLY in mirrors\n#define CSQCRF_DEPTHHACK\t\t\t\t4 //fun depthhack\n#define CSQCRF_ADDITIVE\t\t\t\t\t8 //add instead of blend\n#define CSQCRF_USEAXIS\t\t\t\t\t16 //use v_forward/v_right/v_up as an axis/matrix - predraw is needed to use this properly\n#define CSQCRF_NOSHADOW\t\t\t\t\t32 //don't cast shadows upon other entities (can still be self shadowing, if the engine wishes, and not additive)\n#define CSQCRF_FRAMETIMESARESTARTTIMES\t64 //EXT_CSQC_1: frame times should be read as (time-frametime).\n#define CSQCRF_FIRSTPERSON\t\t\t\t1024 //also not drawn in mirrors, just without the VIEWMODEL hacks attached\n//#define CSQCRFDP_USETRANSPARENTOFFSET\t64 // Allows QC to customize origin used for transparent sorting via transparent_origin global, helps to fix transparent sorting bugs on a very large entities\n////#define CSQCRF_NOAUTOADD\t\t\t128 // removed in favour of predraw return values.\n//#define CSQCRFDP_WORLDOBJECT\t\t\t128 // for large outdoor entities that should not be culled.\n//#define CSQCRFDP_FULLBRIGHT\t\t\t256\n//#define CSQCRFDP_NOSHADOW\t\t\t\t512\n//#define CSQCRF_UNUSED\t\t\t\t\t2048\n//#define CSQCRFDP_MODELLIGHT\t\t\t4096 // CSQC-set model light\n//#define CSQCRFDP_DYNAMICMODELLIGHT\t8192 // origin-dependent model light\n\n/*only read+append+write are standard frik_file*/\n#define FRIK_FILE_READ\t\t0 /*read-only*/\n#define FRIK_FILE_APPEND\t1 /*append (write-only, but offset begins at end of previous file)*/\n#define FRIK_FILE_WRITE\t\t2 /*write-only*/\n#define FRIK_FILE_INVALID\t3 /*no idea what this is for, presume placeholder*/\n#define FRIK_FILE_READNL\t4 /*fgets ignores newline chars, returning the entire thing in one lump*/\n#define FRIK_FILE_MMAP_READ\t5 /*fgets returns a pointer. memory is not guarenteed to be released.*/\n#define FRIK_FILE_MMAP_RW\t6 /*fgets returns a pointer. file is written upon close. memory is not guarenteed to be released.*/\n\n#define FRIK_FILE_READ_DELAY (7) /*internal special mode where the file is not read until the first read. this avoids extra slowness with xonotic (where it uses fopen to see if (large) binary file exists, resulting in large binary files getting decompressed repeatedly then discarded without reading)*/\n#define FRIK_FILE_STREAM\t(8) /*access goes via the vfs, we don't need to track the read/write info here*/\n\n#define MASK_DELTA 1\n#define MASK_STDVIEWMODEL 2\n\nenum lightfield_e\n{\n\tlfield_origin=0,\n\tlfield_colour=1,\n\tlfield_radius=2,\n\tlfield_flags=3,\n\tlfield_style=4,\n\tlfield_angles=5,\n\tlfield_fov=6,\n\tlfield_corona=7,\n\tlfield_coronascale=8,\n\tlfield_cubemapname=9,\n\tlfield_ambientscale=10,\n\tlfield_diffusescale=11,\n\tlfield_specularscale=12,\n\tlfield_rotation=13,\n\tlfield_dietime=14,\n\tlfield_rgbdecay=15,\n\tlfield_radiusdecay=16,\n\tlfield_stylestring=17,\n\tlfield_nearclip=18,\n\tlfield_owner=19,\n};\nenum csqc_input_event\n{\n\t/*devid is the player id (on android, its the multitouch id and is always present even in single player)*/\n\tCSIE_KEYDOWN = 0,\t\t/*syscode, unicode, devid\tthe two codes are not both guarenteed to be set at the same time, and may happen as separate events*/\n\tCSIE_KEYUP = 1,\t\t\t/*syscode, unicode, devid\tas keydown, unicode up events are not guarenteed*/\n\tCSIE_MOUSEDELTA = 2,\t/*x, y, devid\t\t\t\tmouse motion. x+y are relative*/\n\tCSIE_MOUSEABS = 3,\t\t/*x, y, devid\t\t\t\t*/\n\tCSIE_ACCELEROMETER = 4,\t/*x, y, z*/\n\tCSIE_FOCUS = 5,\t\t\t/*mouse, key, devid.\t\tif has, the game window has focus. (true/false/-1)*/\n\tCSIE_JOYAXIS = 6,\t\t/*axis, value, devid*/\n\tCSIE_GYROSCOPE = 7,\t\t/*x, y, z\t\t\t\t\trotational acceleration*/\n\tCSIE_PASTE = 8,\t\t\t/*syscode, unicode, devid\tlike keydown, but no scancodes*/\n};\n\nenum getgamedirinfo_e\n{\n\tGGDI_GAMEDIR=0,\t\t\t//the publically visible gamedir reported by servers.\n\tGGDI_DESCRIPTION=1,\t\t//some text from the .fmf or a gamedirin\n\tGGDI_OVERRIDES=2,\t\t//some text you can parse for custom info.\n\tGGDI_LOADCOMMAND=3,\t\t//returns a string which can be localcmded to load the mod, with whatever quirks are needed to activate it properly.\n\tGGDI_ICON=4,\t\t\t//returns a string which can be drawpiced.\n\tGGDI_ALLGAMEDIRS=5,\t\t//; delimited list basegames;gamedirs ordering\n};\nenum packagemanagerinfo_e\n{\n\tGPMI_NAME,\t\t\t//name of the package, for use with the pkg command.\n\tGPMI_CATEGORY,\t\t//category text\n\tGPMI_TITLE,\t\t\t//name of the package, for showing the user.\n\tGPMI_VERSION,\t\t//version info (may have multiple with the same name but different versions)\n\tGPMI_DESCRIPTION,\t//some blurb\n\tGPMI_LICENSE,\t\t//what license its distributed under\n\tGPMI_AUTHOR,\t\t//name of the person(s) who created it\n\tGPMI_WEBSITE,\t\t//where to contribute/find out more info/etc\n\tGPMI_INSTALLED,\t\t//current state\n\tGPMI_ACTION,\t\t//desired state\n\tGPMI_AVAILABLE,\t\t//whether it may be downloaded or not.\n\tGPMI_FILESIZE,\t\t//size of the download.\n\tGPMI_GAMEDIR,\t\t//so you know which mod(s) its relevant for\n\tGPMI_MAPS,\t\t\t//list of maps provided by this package (can be used with the `map $PackageName:$MapName` command).\n\tGPMI_PREVIEWIMG,\t//a uri usable with drawpic etc\n};\n\n#ifdef TERRAIN\nenum terrainedit_e\n{\n\tter_reload,\t\t\t//\n\tter_save,\t\t\t//\n\tter_sethole,\t\t//vector pos, float radius, floatbool hole\n\tter_height_set,\t\t//vector pos, float radius, float newheight\n\tter_height_smooth,\t//vector pos, float radius, float percent\n\tter_height_spread,\t//vector pos, float radius, float percent\n\tter_raise,\t\t\t//vector pos, float radius, float heightchange\n\tter_lower,\t\t\t//vector pos, float radius, float heightchange\n\tter_tex_kill,\t\t//vector pos, void junk, void junk, string texname\n\tter_tex_get,\t\t//vector pos, void junk, float imagenum\n\tter_tex_blend,\t\t//vector pos, float radius, float percent, string texname\n\tter_tex_concentrate,\t//vector pos, float radius, float percent\n\tter_tex_noise,\t\t//vector pos, float radius, float percent\n\tter_tex_blur,\t\t//vector pos, float radius, float percent\n\tter_water_set,\t\t//vector pos, float radius, float newwaterheight\n\tter_mesh_add,\t\t//entity ent\n\tter_mesh_kill,\t\t//vector pos, float radius\n\tter_tint,\t\t\t//vector pos, float radius, float percent, vector newcol, float newalph\n\tter_height_flatten,\t//vector pos, float radius, float percent\n\tter_tex_replace,\t//vector pos, float radius, string texname\n\tter_reset,\t\t\t//vector pos, float radius\n\tter_reloadsect,\t\t//vector pos, float radius\n\n\tter_ents_wipe_deprecated,\t\t//none\n\tter_ents_concat_deprecated,\t//string\n\tter_ents_get,\t\t//none, returns the map's entity string.\n\n//\tter_poly_add,\t\t//add a poly, woo\n//\tter_poly_remove,\t//remove polys\n\n//\tter_autopaint_h,\t//vector pos, float radius, float percent, string tex1, string tex2\t\t\t\t(paint tex1/tex2\n//\tter_autopaint_n\t//vector pos, float radius, float percent, string tex1, string tex2\n\n\tter_tex_mask,\t\t//string tex\n\n\tter_ent_get,\t\t//int idx -> string\n\tter_ent_set,\t\t//int idx, string keyvals\n\tter_ent_add,\t\t//string keyvals -> int\n\tter_ent_count,\t\t// -> int\n\n//\tter_cmd_count\n};\n#endif\n\nenum\n{\n\tGE_MAXENTS\t\t\t= -1,\n\tGE_ACTIVE\t\t\t= 0,\n\tGE_ORIGIN\t\t\t= 1,\n\tGE_FORWARD\t\t\t= 2,\n\tGE_RIGHT\t\t\t= 3,\n\tGE_UP\t\t\t\t= 4,\n\tGE_SCALE\t\t\t= 5,\n\tGE_ORIGINANDVECTORS\t= 6,\n\tGE_ALPHA\t\t\t= 7,\n\tGE_COLORMOD\t\t\t= 8,\n\tGE_PANTSCOLOR\t\t= 9,\n\tGE_SHIRTCOLOR\t\t= 10,\n\tGE_SKIN\t\t\t\t= 11,\n\tGE_MINS\t\t\t\t= 12,\n\tGE_MAXS\t\t\t\t= 13,\n\tGE_ABSMIN\t\t\t= 14,\n\tGE_ABSMAX\t\t\t= 15,\n\tGE_LIGHT\t\t\t= 16,\n\n\tGE_MODELINDEX\t\t= 200,\n\tGE_MODELINDEX2\t\t= 201,\n\tGE_EFFECTS\t\t\t= 202,\n\tGE_FRAME\t\t\t= 203,\n\tGE_ANGLES\t\t\t= 204,\n\tGE_FATNESS\t\t\t= 205,\n\tGE_DRAWFLAGS\t\t= 206,\n\tGE_ABSLIGHT\t\t\t= 207,\n\tGE_GLOWMOD\t\t\t= 208,\n\tGE_GLOWSIZE\t\t\t= 209,\n\tGE_GLOWCOLOUR\t\t= 210,\n\tGE_RTSTYLE\t\t\t= 211,\n\tGE_RTPFLAGS\t\t\t= 212,\n\tGE_RTCOLOUR\t\t\t= 213,\n\tGE_RTRADIUS\t\t\t= 214,\n\tGE_TAGENTITY\t\t= 215,\n\tGE_TAGINDEX\t\t\t= 216,\n\tGE_GRAVITYDIR\t\t= 217,\n\tGE_TRAILEFFECTNUM\t= 218,\n\n//\tGE_MOVETYPE,\n//\tGE_LATENCY,\n//\tGE_VIEWANGLES\n//\tGE_MOVEMENT,\n//\tGE_VELOCITY,\n};\n\n//If I do it like this, I'll never forget to register something...\n#define ENDLIST\n#ifndef HAVE_LEGACY\n#define legacycsqcglobals\n#else\n#define legacycsqcglobals\t\\\n\tglobalstring(trace_dphittexturename)\t\t\t/*for dp compat*/\t\\\n\tglobalfloatdep (trace_dpstartcontents,\t\t\"Does not support mod-specific contents.\")\t\t\t/*for dp compat*/\t\\\n\tglobalfloatdep (trace_dphitcontents,\t\t\"Does not support mod-specific contents.\")\t\t\t\t/*for dp compat*/\t\\\n\tglobalfloatdep (trace_dphitq3surfaceflags,\t\"Does not support mod-specific surface flags\")\t/*for dp compat*/\t\\\n\tglobalfloatdep (trace_surfaceflagsf,\t\t\"Does not support all mod-specific surface flags.\")\t\t\t\t/*float\t\twritten by traceline, for mods that lack ints*/\t\\\n\tglobalfloatdep (trace_endcontentsf,\t\t\t\"Does not support all mod-specific contents.\")\t\t\t\t/*float\t\twritten by traceline EXT_CSQC_1, for mods that lack ints*/\t\\\n\tENDLIST\n#endif\n#define csqcglobals\t\\\n\tglobalfunction(CSQC_Init,\t\t\t\t\"void(float apilevel, string enginename, float engineversion)\")\t\\\n\tglobalfunction(CSQC_WorldLoaded,\t\t\"void()\")\t\\\n\tglobalfunction(CSQC_Shutdown,\t\t\t\"void()\")\t\\\n\tglobalfunction(CSQC_UpdateView,\t\t\t\"void(float vwidth, float vheight, float notmenu)\")\t\\\n\tglobalfunction(CSQC_UpdateViewLoading,\t\"void(float vwidth, float vheight, float notmenu)\")\t\\\n\tglobalfunction(CSQC_DrawHud,\t\t\t\"void(vector viewsize, float scoresshown)\")/*simple csqc*/\t\\\n\tglobalfunction(CSQC_DrawScores,\t\t\t\"void(vector viewsize, float scoresshown)\")/*simple csqc*/\t\\\n\tglobalfunction(CSQC_Parse_StuffCmd,\t\t\"void(string msg)\")\t\\\n\tglobalfunction(CSQC_Parse_CenterPrint,\t\"float(string msg)\")\t\\\n\tglobalfunction(CSQC_Parse_Print,\t\t\"void(string printmsg, float printlvl)\")\t\\\n\tglobalfunction(CSQC_Parse_Event,\t\t\"void()\")\t\\\n\tglobalfunction(CSQC_Parse_Damage,\t\t\"float(float save, float take, vector inflictororg)\")\t\\\n\tglobalfunction(CSQC_Parse_SetAngles,\t\"float(vector angles, float isdelta)\")\t\\\n\tglobalfunction(CSQC_PlayerInfoChanged,\t\"void(float playernum)\")\t\\\n\tglobalfunction(CSQC_ServerInfoChanged,\t\"void()\")\t\\\n\tglobalfunction(CSQC_InputEvent,\t\t\t\"float(float evtype, float scanx, float chary, float devid)\")\t\\\n\tglobalfunction(CSQC_Input_Frame,\t\t\"void()\")/*EXT_CSQC_1*/\t\\\n\tglobalfunction(CSQC_RendererRestarted,\t\"void(string rendererdescription)\")\t\\\n\tglobalfunction(CSQC_GenerateMaterial,\t\"string(string shadername)\")\t\\\n\tglobalfunction(CSQC_ConsoleCommand,\t\t\"float(string cmd)\")\t\\\n\tglobalfunction(CSQC_ConsoleLink,\t\t\"float(string text, string info)\")\t\\\n\tglobalfunction(GameCommand,\t\t\t\t\"void(string cmdtext)\")\t/*DP extension*/\\\n\t\\\n\tglobalfunction(CSQC_Ent_Spawn,\t\t\t\"void(float newentnum)\")\t\\\n\tglobalfunction(CSQC_Ent_Update,\t\t\t\"void(float isnew)\")\t\\\n\tglobalfunction(CSQC_Ent_Remove,\t\t\t\"void()\")\t\\\n\t\\\n\tglobalfunction(CSQC_Event_Sound,\t\t\"float(float entnum, float channel, string soundname, float vol, float attenuation, vector pos, float pitchmod, float flags\"/*\", float timeofs*/\")\")\t\\\n\tglobalfunction(CSQC_ServerSound,\t\t\"DEP(\\\"use CSQC_Event_Sound\\\") float(float channel, string soundname, vector pos, float vol, float attenuation, float flags\"/*\", float timeofs*/\")\")/*obsolete, use event_sound*/\t\\\n\t/*globalfunction(CSQC_LoadResource,\t\t\"float(string resname, string restype)\")*//*EXT_CSQC_1*/\t\\\n\tglobalfunction(CSQC_Parse_TempEntity,\t\"float()\")/*EXT_CSQC_ABSOLUTLY_VILE*/\t\\\n\t\\\n\tglobalfunction(CSQC_MapEntityEdited,\t\"void(int entidx, string newentdata)\")\\\n\tglobalfunction(StartFrame,\t\t\t\t\"void()\")\\\n\tglobalfunction(EndFrame,\t\t\t\t\"void()\")\\\n\t\\\n\t/*These are pointers to the csqc's globals.*/\t\\\n\tglobalfloat\t(time)\t\t\t\t/*float\t\tThe simulation(aka: smoothed server) time, speed drifts based upon latency*/\t\\\n\tglobalfloat\t(frametime)\t\t\t/*float\t\tClient render frame interval*/\t\\\n\tglobalfloat\t(gamespeed)\t\t\t/*float\t\tMultiplier for real time -> simulation time*/\t\\\n\tglobalfloat\t(cltime)\t\t\t\t/*float\t\tClientside map uptime indepent of gamespeed, latency, and the server in general*/\t\\\n\tglobalfloat\t(clframetime)\t\t\t/*float\t\ttime since last video frame*/\t\\\n\tglobalfloat\t(servertime)\t\t\t/*float\t\tServer time of latest inbound network frame*/\t\\\n\tglobalfloat\t(serverprevtime)\t\t/*float\t\tServer time of previous inbound network frame */\t\\\n\tglobalfloat\t(serverdeltatime)\t\t/*float\t\tnew-old */\t\\\n\tglobalfloat\t(physics_mode)\t\t/*float\t\tWritten before entering most qc functions*/\t\\\n\tglobalentity(self)\t\t\t\t/*entity\tWritten before entering most qc functions*/\t\\\n\tglobalentity(other)\t\t\t\t/*entity\tWritten before entering most qc functions*/\t\\\n\t\\\n\tglobalfloat\t(deathmatch)\t\t\t/*for simplecsqc*/\t\\\n\tglobalfloat\t(coop)\t\t\t\t/*for simplecsqc*/\t\\\n\t\\\n\tglobalfloat\t(maxclients)\t\t\t\t/*float\t\tmax number of players allowed*/\t\\\n\tglobalfloat\t(numclientseats)\t\t\t/*float\t\tnumber of seats/splitscreen clients running on this client*/\t\\\n\t\\\n\tglobalvector(v_forward)\t\t\t\t\t/*vector\twritten by anglevectors*/\t\\\n\tglobalvector(v_right)\t\t\t\t\t/*vector\twritten by anglevectors*/\t\\\n\tglobalvector(v_up)\t\t\t\t\t\t/*vector\twritten by anglevectors*/\t\\\n\t\\\n\tglobalfloat\t(trace_allsolid)\t\t\t/*bool\t\twritten by traceline*/\t\\\n\tglobalfloat\t(trace_startsolid)\t\t\t/*bool\t\twritten by traceline*/\t\\\n\tglobalfloat\t(trace_fraction)\t\t\t/*float\t\twritten by traceline*/\t\\\n\tglobalfloat\t(trace_inwater)\t\t\t\t/*bool\t\twritten by traceline*/\t\\\n\tglobalfloat\t(trace_inopen)\t\t\t\t/*bool\t\twritten by traceline*/\t\\\n\tglobalvector(trace_endpos)\t\t\t\t/*vector\twritten by traceline*/\t\\\n\tglobalvector(trace_plane_normal)\t\t/*vector\twritten by traceline*/\t\\\n\tglobalfloat\t(trace_plane_dist)\t\t\t/*float\t\twritten by traceline*/\t\\\n\tglobalentity(trace_ent)\t\t\t\t\t/*entity\twritten by traceline*/\t\\\n\tglobalint\t(trace_surfaceflagsi)\t\t/*int\t\twritten by traceline*/\t\\\n\tglobalstring(trace_surfacename)\t\t\t/*string\twritten by traceline*/\t\\\n\tglobalint\t(trace_endcontentsi)\t\t/*int\t\twritten by traceline EXT_CSQC_1*/\t\\\n\tglobalint\t(trace_brush_id)\t\t\t/*int\t\twritten by traceline*/\t\\\n\tglobalint\t(trace_brush_faceid)\t\t/*int\t\twritten by traceline*/\t\\\n\tglobalint\t(trace_surface_id)\t\t\t/*int\t\twritten by traceline*/\t\\\n\tglobalint\t(trace_bone_id)\t\t\t\t/*int\t\twritten by traceline*/\t\\\n\tglobalint\t(trace_triangle_id)\t\t\t/*int\t\twritten by traceline*/\t\\\n\tglobalfloat (trace_networkentity)\t\t/*float\t\twritten by traceline*/\t\\\n\tlegacycsqcglobals \\\n\t\\\n\tglobalfloat(clientcommandframe)\t\t\t/*float\t\tthe next frame that will be sent*/ \\\n\tglobalfloat(servercommandframe)\t\t\t/*float\t\tthe most recent frame received from the server*/ \\\n\t\\\n\tglobalfloat(player_localentnum)\t\t\t/*float\t\tthe entity number the local player is looking out from*/\t\\\n\tglobalfloat(player_localnum)\t\t\t/*float\t\tthe player number of the local player*/\t\\\n\tglobalfloat(intermission)\t\t\t\t/*float\t\tset when the client receives svc_intermission*/\t\\\n\tglobalfloat(intermission_time)\t\t\t/*float\t\tset when the client receives svc_intermission*/\t\\\n\tglobalvector(view_angles)\t\t\t\t/*float\t\tset to the view angles at the start of each new frame (EXT_CSQC_1)*/ \\\n\t\\\n\tglobalvector(pmove_org)\t\t\t\t\t/*deprecated. read/written by runplayerphysics*/ \\\n\tglobalvector(pmove_vel)\t\t\t\t\t/*deprecated. read/written by runplayerphysics*/ \\\n\tglobalvector(pmove_mins)\t\t\t\t/*deprecated. read/written by runplayerphysics*/ \\\n\tglobalvector(pmove_maxs)\t\t\t\t/*deprecated. read/written by runplayerphysics*/ \\\n\tglobalfloat (pmove_jump_held)\t\t\t/*deprecated. read/written by runplayerphysics*/ \\\n\tglobalfloat (pmove_waterjumptime)\t\t/*deprecated. read/written by runplayerphysics*/ \\\n\tglobalfloat (pmove_onground)\t\t\t/*deprecated. read/written by runplayerphysics*/ \\\n\t\\\n\tglobalfloat (input_sequence)\t\t\t/*float\t\tfilled by getinputstate, read by runplayerphysics*/ \\\n\tglobalfloat (input_timelength)\t\t\t/*float\t\tfilled by getinputstate, read by runplayerphysics*/ \\\n\tglobalvector(input_angles)\t\t\t\t/*vector\tfilled by getinputstate, read by runplayerphysics*/ \\\n\tglobalvector(input_movevalues)\t\t\t/*vector\tfilled by getinputstate, read by runplayerphysics*/ \\\n\tglobalfloat (input_buttons)\t\t\t\t/*float\t\tfilled by getinputstate, read by runplayerphysics*/ \\\n\tglobalfloat (input_impulse)\t\t\t\t/*float\t\tfilled by getinputstate, read by runplayerphysics*/ \\\n\tglobalfloat (input_lightlevel)\t\t\t/*unused float\t\tfilled by getinputstate, read by runplayerphysics*/ \\\n\tglobaluint64(input_weapon)\t\t\t\t/*unused float\t\tfilled by getinputstate, read by runplayerphysics*/ \\\n\tglobalfloat (input_servertime)\t\t\t/*float\t\tfilled by getinputstate, read by runplayerphysics*/ \\\n\tglobalfloat (input_clienttime)\t\t\t/*float\t\tfilled by getinputstate, read by runplayerphysics*/ \\\n\tglobalvector(input_cursor_screen)\t\t/*float\t\tfilled by getinputstate*/ \\\n\tglobalvector(input_cursor_trace_start)\t/*float\t\tfilled by getinputstate*/ \\\n\tglobalvector(input_cursor_trace_endpos)\t/*float\t\tfilled by getinputstate*/ \\\n\tglobalfloat (input_cursor_entitynumber)\t/*float\t\tfilled by getinputstate*/ \\\n\tglobaluint64(input_head_status)\t\t\t/*filled by getinputstate, for vr*/ \\\n\tglobalvector(input_head_origin)\t\t\t/*filled by getinputstate, for vr*/ \\\n\tglobalvector(input_head_angles)\t\t\t/*filled by getinputstate, for vr*/ \\\n\tglobalvector(input_head_velocity)\t\t/*filled by getinputstate, for vr*/ \\\n\tglobalvector(input_head_avelocity)\t\t/*filled by getinputstate, for vr*/ \\\n\tglobaluint64(input_head_weapon)\t\t\t/*filled by getinputstate, for vr*/ \\\n\tglobaluint64(input_left_status)\t\t\t/*filled by getinputstate, for vr*/ \\\n\tglobalvector(input_left_origin)\t\t\t/*filled by getinputstate, for vr*/ \\\n\tglobalvector(input_left_angles)\t\t\t/*filled by getinputstate, for vr*/ \\\n\tglobalvector(input_left_velocity)\t\t/*filled by getinputstate, for vr*/ \\\n\tglobalvector(input_left_avelocity)\t\t/*filled by getinputstate, for vr*/ \\\n\tglobaluint64(input_left_weapon)\t\t\t/*filled by getinputstate, for vr*/ \\\n\tglobaluint64(input_right_status)\t\t/*filled by getinputstate, for vr*/ \\\n\tglobalvector(input_right_origin)\t\t/*filled by getinputstate, for vr*/ \\\n\tglobalvector(input_right_angles)\t\t/*filled by getinputstate, for vr*/ \\\n\tglobalvector(input_right_velocity)\t\t/*filled by getinputstate, for vr*/ \\\n\tglobalvector(input_right_avelocity)\t\t/*filled by getinputstate, for vr*/ \\\n\tglobaluint64(input_right_weapon)\t\t/*filled by getinputstate, for vr*/ \\\n\t\\\n\tglobalvector(global_gravitydir)\t\t\t/*vector\tused when .gravitydir is 0 0 0 */ \\\n\tglobalfloat (dimension_default)\t\t\t/*float\t\tdefault value for dimension_hit+dimension_solid*/ \\\n\tglobalfloat (autocvar_vid_conwidth)\t\t/*float\t\thackfix for dp mods*/\t\\\n\tglobalfloat (autocvar_vid_conheight)\t/*float\t\thackfix for dp mods*/\t\\\n\tglobalfloat (cycle_wrapped)\t\\\n\tENDLIST\n\n\n//note: doesn't even have to match the clprogs.dat :)\ntypedef struct {\n\n#define comfieldfloat(csqcname,desc) float csqcname;\n#define comfieldvector(csqcname,desc) vec3_t csqcname;\n#define comfieldentity(csqcname,desc) int csqcname;\n#define comfieldstring(csqcname,desc) string_t csqcname;\n#define comfieldfunction(csqcname, typestr,desc) func_t csqcname;\ncomqcfields\n#undef comfieldfloat\n#undef comfieldvector\n#undef comfieldentity\n#undef comfieldstring\n#undef comfieldfunction\n\n#ifdef VM_Q1\n} csqcentvars_t;\ntypedef struct {\n#endif\n\n#define comfieldfloat(name,desc) float name;\n#define comfieldint(name,desc) int name;\n#define comfieldvector(name,desc) vec3_t name;\n#define comfieldentity(name,desc) int name;\n#define comfieldstring(name,desc) string_t name;\n#define comfieldfunction(name, typestr,desc) func_t name;\ncomextqcfields\ncsqcextfields\n#undef comfieldfloat\n#undef comfieldint\n#undef comfieldvector\n#undef comfieldentity\n#undef comfieldstring\n#undef comfieldfunction\n\n#ifdef VM_Q1\n} csqcextentvars_t;\n#else\n} csqcentvars_t;\n#endif\n\ntypedef struct csqcedict_s\n{\n\tenum ereftype_e\tereftype;\n\tfloat\t\t\tfreetime; // sv.time when the object was freed\n\tint\t\t\t\tentnum;\n\tunsigned int\tfieldsize;\n\tpbool\t\t\treadonly;\t//world\n#ifdef VM_Q1\n\tcsqcentvars_t\t*v;\n\tcsqcextentvars_t\t*xv;\n#else\n\tunion {\n\t\tcsqcentvars_t\t*v;\n\t\tcsqcentvars_t\t*xv;\n\t};\n#endif\n\t/*the above is shared with qclib*/\n#ifdef USEAREAGRID\n\tareagridlink_t\tgridareas[AREAGRIDPERENT];\t//on overflow, use the inefficient overflow list.\n\tsize_t\t\t\tgridareasequence;\t//used to avoid iterrating the same ent twice.\n#else\n\tlink_t\tarea;\n#endif\n\tpvscache_t pvsinfo;\n\tint lastruntime;\n\tint solidsize;\n#ifdef USERBE\n\tentityrbe_t rbe;\n#endif\n\t/*the above is shared with ssqc*/\n\n\t//add whatever you wish here\n\ttrailkey_t trailstate;\n\tint\t\tskinobject;\n} csqcedict_t;\n\ntypedef struct qcstate_s\n{\n\tfloat resumetime;\n\tqboolean waiting;\n\tstruct qcthread_s *thread;\n\tint self;\n\tint selfid;\n\tint other;\n\tint otherid;\n\tfloat returnval;\n\n\tstruct qcstate_s *next;\n} qcstate_t;\n\n#ifdef __cplusplus\n};\n#endif\n#endif\n"
  },
  {
    "path": "engine/common/protocol.h",
    "content": "/*\nCopyright (C) 1996-1997 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n*/\n// protocol.h -- communications protocols\n\n#include \"bothdefs.h\"\n\n#define PEXT_SETVIEW\t\t\t0x00000001\n\n#define PEXT_SCALE\t\t\t\t0x00000002\n#define PEXT_LIGHTSTYLECOL\t\t0x00000004\n#define PEXT_TRANS\t\t\t\t0x00000008\n#define PEXT_VIEW2_\t\t\t\t0x00000010\n//#define PEXT_BULLETENS\t\t\t0x00000020 //obsolete\n#define PEXT_ACCURATETIMINGS\t0x00000040\n#define PEXT_SOUNDDBL\t\t\t0x00000080\t//revised startsound protocol\n#define PEXT_FATNESS\t\t\t0x00000100\t//GL only (or servers)\n#define PEXT_HLBSP\t\t\t\t0x00000200\n#define PEXT_TE_BULLET\t\t\t0x00000400\n#define PEXT_HULLSIZE\t\t\t0x00000800\n#define PEXT_MODELDBL\t\t\t0x00001000\n#define PEXT_ENTITYDBL\t\t\t0x00002000\t//max of 1024 ents instead of 512\n#define PEXT_ENTITYDBL2\t\t\t0x00004000\t//max of 1024 ents instead of 512\n#define PEXT_FLOATCOORDS\t\t0x00008000\t//supports floating point origins.\n//#define PEXT_VWEAP\t\t\t\t0x00010000\t//cause an extra qbyte to be sent, and an extra list of models for vweaps.\n#define PEXT_Q2BSP_\t\t\t\t0x00020000\n#define PEXT_Q3BSP_\t\t\t\t0x00040000\n\n#define PEXT_COLOURMOD\t\t\t0x00080000\t//this replaces an older value which would rarly have caried any actual data.\n\n#define PEXT_SPLITSCREEN\t\t0x00100000\n#define PEXT_HEXEN2\t\t\t\t0x00200000\t//more stats and working particle builtin.\n#define PEXT_SPAWNSTATIC2\t\t0x00400000\t//Sends an entity delta instead of a baseline.\n#define PEXT_CUSTOMTEMPEFFECTS\t0x00800000\t//supports custom temp ents.\n#define PEXT_256PACKETENTITIES\t0x01000000\t//Client can recieve 256 packet entities.\n//#define PEXT_NEVERUSED\t\t0x02000000\t//reserved for a future multicastmask\n#define PEXT_SHOWPIC\t\t\t0x04000000\n#define PEXT_SETATTACHMENT\t\t0x08000000\t//md3 tags (needs networking, they need to lerp).\n//#define PEXT_NEVERUSED\t\t0x10000000\t//reserved for a future multicastmask\n#define PEXT_CHUNKEDDOWNLOADS\t0x20000000\t//alternate file download method. Hopefully it'll give quadroupled download speed, especially on higher pings.\n#define PEXT_CSQC\t\t\t\t0x40000000\t//csqc additions\n#define PEXT_DPFLAGS\t\t\t0x80000000\t//extra flags for viewmodel/externalmodel and possible other persistant style flags.\n#define PEXT_SERVERADVERTISE\t~0u\n#define PEXT_CLIENTSUPPORT\t\t(PEXT_SETVIEW|PEXT_SCALE|PEXT_LIGHTSTYLECOL|PEXT_TRANS|PEXT_VIEW2_|PEXT_ACCURATETIMINGS|PEXT_SOUNDDBL|PEXT_FATNESS|PEXT_HLBSP|PEXT_TE_BULLET|PEXT_HULLSIZE|PEXT_MODELDBL|PEXT_ENTITYDBL|PEXT_ENTITYDBL2|PEXT_FLOATCOORDS|PEXT_Q2BSP_|PEXT_Q3BSP_|PEXT_COLOURMOD|PEXT_SPLITSCREEN|PEXT_HEXEN2|PEXT_SPAWNSTATIC2|PEXT_CUSTOMTEMPEFFECTS|PEXT_256PACKETENTITIES|PEXT_SHOWPIC|PEXT_SETATTACHMENT|PEXT_CHUNKEDDOWNLOADS|PEXT_CSQC|PEXT_DPFLAGS)\n\n#ifdef CSQC_DAT\n\t#define PEXT_BIGUSERINFOS\tPEXT_CSQC\t//FIXME: while useful for csqc, we should include something else that isn't so often stripped, or is available in ezquake, or something.\n#else\n\t#define PEXT_BIGUSERINFOS\t0xffffffff\n#endif\n#ifdef SIDEVIEWS\n\t#define PEXT_VIEW2\t\t\tPEXT_VIEW2_\n#endif\n#ifdef Q2BSPS\n\t#define PEXT_Q2BSP\t\t\tPEXT_Q2BSP_\n#endif\n#ifdef Q3BSPS\n\t#define PEXT_Q3BSP\t\t\tPEXT_Q3BSP_\n#endif\n#define PEXT1_HIDEPROTOCOLS\t\t(PEXT_Q3BSP_|PEXT_Q2BSP_|PEXT_HLBSP)\t//These are hints for the server, and not useful to the client (they can figure stuff out themselves)\n//#define\tPEXT1_DEPRECATED\t\t\t(PEXT_SCALE|PEXT_TRANS|PEXT_ACCURATETIMINGS|PEXT_SOUNDDBL|PEXT_FATNESS|PEXT_HULLSIZE|PEXT_MODELDBL|PEXT_ENTITYDBL|PEXT_ENTITYDBL2|PEXT_COLOURMOD|PEXT_SPAWNSTATIC2|PEXT_256PACKETENTITIES|PEXT_SETATTACHMENT|PEXT_DPFLAGS)\t//deprecated by replacementdeltas\n#define PEXT1_MVDSUPPORT\t\t\t(PEXT1_CLIENTSUPPORT&~PEXT1_DEPRECATED&~PEXT1_HIDEPROTOCOLS)\t//pext2 extensions to use when recording mvds.\n\n#define PEXT2_PRYDONCURSOR\t\t\t0x00000001\n#define PEXT2_VOICECHAT\t\t\t\t0x00000002\n#define PEXT2_SETANGLEDELTA\t\t\t0x00000004\n#define PEXT2_REPLACEMENTDELTAS\t\t0x00000008\t//weaponframe was part of the entity state. that flag is now the player's v_angle.\n#define PEXT2_MAXPLAYERS\t\t\t0x00000010\t//Client is able to cope with more players than 32. abs max becomes 255, due to colormap issues.\n#define PEXT2_PREDINFO\t\t\t\t0x00000020\t//movevar stats, NQ input sequences+acks.\n#define PEXT2_NEWSIZEENCODING\t\t0x00000040\t//richer size encoding.\n#define PEXT2_INFOBLOBS\t\t\t\t0x00000080\t//serverinfo+userinfo lengths can be MUCH higher (protocol is unbounded, but expect low sanity limits on userinfo), and contain nulls etc.\n#define PEXT2_STUNAWARE\t\t\t\t0x00000100\t//changes the netchan to biased-bigendian (so lead two bits are 1 and not stun's 0, so we don't get confused)\n#define PEXT2_VRINPUTS\t\t\t\t0x00000200\t//clc_move changes, more buttons etc. vr stuff!\n#define PEXT2_LERPTIME\t\t\t\t0x00000400\t//fitz-bloat parity. redefines UF_16BIT as UF_LERPEND in favour of length coding.\n#define PEXT2_SERVERADVERTISE\t\t~0u\n#define PEXT2_CLIENTSUPPORT\t\t\t(PEXT2_PRYDONCURSOR|PEXT2_VOICECHAT|PEXT2_SETANGLEDELTA|PEXT2_REPLACEMENTDELTAS|PEXT2_MAXPLAYERS|PEXT2_PREDINFO|PEXT2_NEWSIZEENCODING|PEXT2_INFOBLOBS|PEXT2_STUNAWARE|PEXT2_VRINPUTS|PEXT2_LERPTIME) //warn if we see bits not listed here.\n#define PEXT2_DEPRECATEDORNEW\t\t(PEXT2_INFOBLOBS|PEXT2_VRINPUTS|PEXT2_LERPTIME) //extensions that are outdated\n#define PEXT2_MVDSUPPORT\t\t\t(PEXT2_CLIENTSUPPORT&~PEXT2_DEPRECATED&~PEXT2_STUNAWARE)\t//pext2 extensions to use when recording mvds.\n\n#define PEXT2_LONGINDEXES\t\t\t0\t//boosts the maximum player+stat index.\n\n//EzQuake/Mvdsv extensions. (use ezquake name, to avoid confusion about .mvd format and its protocol differences)\n#define EZPEXT1_FLOATENTCOORDS\t\t0x00000001\t//quirky - doesn't apply to broadcasts, just players+ents. this gives more precision, but will bug out if you try using it to increase map bounds in ways that may not be immediately apparent. iiuc this was added instead of fixing some inconsistent rounding...\n#define EZPEXT1_SETANGLEREASON\t\t0x00000002\t//specifies the reason for an svc_setangles call. the mvdsv implementation will fuck over any mods that writebyte them. we'd need to modify our preparse stuff to work around the issue.\n//#define EZPEXT1_SERVERSIDEWEAPON\t0x00000004\t//looks half-baked. would be better to predict grabs clientside (oh noes! backpack knowledge!).\n//#define EZPEXT1_DEBUG_WEAPON\t\t0x00000008\t//debug? not gonna bother.\n//#define EZPEXT1_DEBUG_ANTILAG\t\t0x00000010\t//debug? not gonna bother.\n#define EZPEXT1_HIDDEN_MESSAGES\t\t0x00000020\t//mvd bloat. shouldn't be seen on actual servers.\n\n\n//#define MVD_PEXT1_SERVERSIDEWEAPON  (1 <<  2) // Server-side weapon selection\n#define MVD_PEXT1_DEBUG_WEAPON      (1 <<  3) // Send weapon-choice explanation to server for logging\n#define MVD_PEXT1_DEBUG_ANTILAG     (1 <<  4) // Send predicted positions to server (compare to antilagged positions)\n#define MVD_PEXT1_HIDDEN_MESSAGES   (1 <<  5) // dem_multiple(0) packets are in format (<length> <type-id>+ <packet-data>)*\n\n\n#define EZPEXT1_SERVERADVERTISE\t\t(EZPEXT1_FLOATENTCOORDS/* - implemented, but interactions with replacementdeltas is not defined*/ /*|EZPEXT1_SETANGLEREASON - potentially causes compat issues with mods that stuffcmd it (common in nq)*/)\n#define EZPEXT1_CLIENTADVERTISE\t\tEZPEXT1_FLOATENTCOORDS\t\t\t//might as well ask for it, as a way around mvdsv's writecoord/PM_NudgePosition rounding difference bug.\n#define EZPEXT1_CLIENTSUPPORT\t\t(EZPEXT1_FLOATENTCOORDS|EZPEXT1_SETANGLEREASON|EZPEXT1_HIDDEN_MESSAGES)\t//ones we can support in demos. warning if other bits.\n\n//ZQuake transparent protocol extensions.\n#define Z_EXT_PM_TYPE\t\t(1<<0)\t// basic PM_TYPE functionality (reliable jump_held)\n#define Z_EXT_PM_TYPE_NEW\t(1<<1)\t// adds PM_FLY, PM_SPECTATOR\n#define Z_EXT_VIEWHEIGHT\t(1<<2)\t// STAT_VIEWHEIGHT\n#define Z_EXT_SERVERTIME\t(1<<3)\t// STAT_TIME\n#define Z_EXT_PITCHLIMITS\t(1<<4)\t// serverinfo maxpitch & minpitch\n#define Z_EXT_JOIN_OBSERVE\t(1<<5)\t// server: \"join\" and \"observe\" commands are supported\n\t\t\t\t\t\t\t\t\t// client: on-the-fly spectator <-> player switching supported\n\n#define Z_EXT_PF_ONGROUND\t(1<<6)\t// server: PF_ONGROUND is valid for all svc_playerinfo\n#define Z_EXT_VWEP\t\t\t(1<<7)\n#define Z_EXT_PF_SOLID\t\t(1<<8)\t//conflicts with many FTE extensions.\n\n#ifdef QUAKESTATS\n#define SERVER_SUPPORTED_Z_EXTENSIONS (Z_EXT_PM_TYPE|Z_EXT_PM_TYPE_NEW|Z_EXT_VIEWHEIGHT|Z_EXT_SERVERTIME|Z_EXT_PITCHLIMITS|Z_EXT_JOIN_OBSERVE|Z_EXT_PF_ONGROUND|Z_EXT_VWEP|Z_EXT_PF_SOLID)\n#else\n#define SERVER_SUPPORTED_Z_EXTENSIONS (Z_EXT_PM_TYPE|Z_EXT_PM_TYPE_NEW|Z_EXT_PITCHLIMITS|Z_EXT_JOIN_OBSERVE|Z_EXT_PF_ONGROUND|Z_EXT_VWEP|Z_EXT_PF_SOLID)\n#endif\n#define BUGGY_EZQUAKE_Z_EXTENSIONS    (Z_EXT_PF_ONGROUND|Z_EXT_PF_SOLID) //ezquake bugs out on these when ANY fteextension is present. hack the serverinfo to hide these.\n#define CLIENT_SUPPORTED_Z_EXTENSIONS (SERVER_SUPPORTED_Z_EXTENSIONS|Z_EXT_PF_ONGROUND|Z_EXT_PF_SOLID)\n\n\n#define PROTOCOL_VERSION_VARLENGTH\t\t(('v'<<0) + ('l'<<8) + ('e'<<16) + ('n' << 24))\t//variable length handshake\n\n#define PROTOCOL_VERSION_FTE1\t\t\t(('F'<<0) + ('T'<<8) + ('E'<<16) + ('X' << 24))\t//fte extensions.\n#define PROTOCOL_VERSION_FTE2\t\t\t(('F'<<0) + ('T'<<8) + ('E'<<16) + ('2' << 24))\t//fte extensions.\n#define PROTOCOL_VERSION_EZQUAKE1\t\t(('M'<<0) + ('V'<<8) + ('D'<<16) + ('1' << 24)) //ezquake/mvdsv extensions\n#define PROTOCOL_VERSION_HUFFMAN\t\t(('H'<<0) + ('U'<<8) + ('F'<<16) + ('F' << 24))\t//packet compression\n#define PROTOCOL_VERSION_FRAGMENT\t\t(('F'<<0) + ('R'<<8) + ('A'<<16) + ('G' << 24))\t//supports fragmentation/packets larger than 1450\n#ifdef HAVE_DTLS\n#define PROTOCOL_VERSION_DTLSUPGRADE\t(('D'<<0) + ('T'<<8) + ('L'<<16) + ('S' << 24))\t//server supports dtls. clients should dtlsconnect THEN continue connecting (also allows dtls rcon!).\n#endif\n\n#define PROTOCOL_INFO_GUID\t\t\t\t(('G'<<0) + ('U'<<8) + ('I'<<16) + ('D' << 24))\t//globally 'unique' client id info.\n\n#define\tPROTOCOL_VERSION_QW\t\t\t\t28\n\n#define\tPROTOCOL_VERSION_Q2_DEMO_MIN\t26\t//we can parse this server\n#define\tPROTOCOL_VERSION_Q2_MIN\t\t\t31\t//we can join these outdated servers\n#define\tPROTOCOL_VERSION_Q2\t\t\t\t34\t//we host this\n#define\tPROTOCOL_VERSION_Q2EXDEMO\t\t2022\n#define\tPROTOCOL_VERSION_Q2EX\t\t\t2023\n#define\tPROTOCOL_VERSION_R1Q2\t\t\t35\n#define\tPROTOCOL_VERSION_Q2PRO\t\t\t36\n\n#define\tPROTOCOL_VERSION_Q2_DEMO_MAX\tPROTOCOL_VERSION_Q2PRO\n\n//=========================================\n\n//#define\tGAME_DEFAULTPORT\tXXXXX\t//rebranding allows selection of a different default port, which slightly reduces protocol conflicts.\n#define\tPORT_NQSERVER\t26000\n#define PORT_DPMASTER\tPORT_Q3MASTER\n#define\tPORT_QWCLIENT\t27001\n#define\tPORT_QWMASTER\t27000\n#define\tPORT_QWSERVER\t27500\n#define PORT_H2SERVER\t26900\n#define PORT_Q2MASTER\t27900\n#define PORT_Q2CLIENT\t27901\n#define PORT_Q2SERVER\t27910\n#define PORT_Q2EXSERVER\t5069\n#define PORT_Q3MASTER\t27950\n#define PORT_Q3SERVER\t27960\n#define PORT_ICEBROKER\tPORT_DPMASTER\n\n#ifdef GAME_DEFAULTPORT\n\t#define PORT_DEFAULTSERVER\tGAME_DEFAULTPORT\n#else\n\t#define PORT_DEFAULTSERVER\tPORT_QWSERVER\n#endif\n\n//=========================================\n\n// out of band message id bytes\n\n// M = master, S = server, C = client, A = any\n// the second character will always be \\n if the message isn't a single\n// qbyte long (?? not true anymore?)\n\n#define\tS2C_CHALLENGE\t\t'c'\n#define\tS2C_CONNECTION\t\t'j'\n#define\tA2A_PING\t\t\t'k'\t// respond with an A2A_ACK\n#define\tA2A_ACK\t\t\t\t'l'\t// general acknowledgement without info\n#define\tA2A_NACK\t\t\t'm'\t// [+ comment] general failure\n#define A2A_ECHO\t\t\t'e' // for echoing\n#define\tA2C_PRINT\t\t\t'n'\t// print a message on client\n\n#define\tS2M_HEARTBEAT\t\t'a'\t// + serverinfo + userlist + fraglist\n#define\tA2C_CLIENT_COMMAND\t'B'\t// + command line\n#define\tS2M_SHUTDOWN\t\t'C'\n\n#define C2M_MASTER_REQUEST  'c'\n#define M2C_MASTER_REPLY\t'd'\t// + \\n + qw server port list\n\n//for S2C 'status' packets.\n#define\tSTATUS_OLDSTYLE\t\t\t\t\t0 //equivelent to STATUS_SERVERINFO|STATUS_PLAYERS\n#define\tSTATUS_SERVERINFO\t\t\t\t1\n#define\tSTATUS_PLAYERS\t\t\t\t\t2\n#define\tSTATUS_SPECTATORS\t\t\t\t4\n#define\tSTATUS_SPECTATORS_AS_PLAYERS\t8 //for ASE - change only frags: show as \"S\"\n#define\tSTATUS_SHOWTEAMS\t\t\t\t16\n#define\tSTATUS_QTVLIST\t\t\t\t\t32 //qtv destid \"name\" \"streamid@host:port\" numviewers\n#define STATUS_LOGININFO\t\t\t\t64\n\n//==================\n// note that there are some defs.qc that mirror to these numbers\n// also related to svc_strings[] in cl_parse\n//==================\n\n//\n// server to client\n//\n#define\tsvc_bad\t\t\t\t\t\t0\n#define\tsvc_nop\t\t\t\t\t\t1\n#define\tsvc_disconnect\t\t\t\t2\n#define\tsvcqw_updatestatbyte\t\t3\t// [qbyte] [qbyte]\n#define\tsvcnq_updatestatlong\t\t3\t// [qbyte] [long]\n#define\tsvc_version\t\t\t\t\t4\t// [long] server version\n#define\tsvc_setview\t\t\t\t\t5\t// [short] entity number\n#define\tsvc_sound\t\t\t\t\t6\t// <see code>\n#define\tsvc_time\t\t\t\t\t7\t// [float] server time\n#define\tsvc_print\t\t\t\t\t8\t// [qbyte] id [string] null terminated string\n#define\tsvc_stufftext\t\t\t\t9\t// [string] stuffed into client's console buffer\n\t\t\t\t\t\t\t\t\t\t// the string should be \\n terminated\n#define\tsvc_setangle\t\t\t\t10\t// [angle3] set the view angle to this absolute value\n\n#define\tsvc_serverdata\t\t\t\t11\t// [long] protocol ...\n#define\tsvc_lightstyle\t\t\t\t12\t// [qbyte] [string]\n#define\tsvc_updatename\t\t\t\t13\t// [qbyte] [string]\n#define\tsvc_updatefrags\t\t\t\t14\t// [qbyte] [short]\n#define\tsvcnq_clientdata\t\t\t15\t// <shortbits + data>\n#define\tsvc_stopsound\t\t\t\t16\t// <see code>\n#define\tsvc_updatecolors\t\t\t17\t// [qbyte] [qbyte] [qbyte]\n#define\tsvc_particle\t\t\t\t18\t// [vec3] <variable>\n#define\tsvc_damage\t\t\t\t\t19\n\n#define\tsvc_spawnstatic\t\t\t\t20\n#define\tsvcfte_spawnstatic2\t\t\t21\n#define\tsvc_spawnbaseline\t\t\t22\n\n#define\tsvc_temp_entity\t\t\t\t23\t// variable\n#define\tsvc_setpause\t\t\t\t24\t// [qbyte] on / off\n#define\tsvcnq_signonnum\t\t\t\t25\t// [qbyte]  used for the signon sequence\n#define\tsvcfte_splitscreenconfig\t25\t// [qbyte] seats, player[seat]. spectator flags passed via userinfo.\n\n#define\tsvc_centerprint\t\t\t\t26\t// [string] to put in center of the screen\n\n#define\tsvc_killedmonster\t\t\t27\n#define\tsvc_foundsecret\t\t\t\t28\n\n#define\tsvc_spawnstaticsound\t\t29\t// [coord3] [qbyte] samp [qbyte] vol [qbyte] aten\n\n#define\tsvc_intermission\t\t\t30\t\t// [vec3_t] origin [vec3_t] angle\n#define\tsvc_finale\t\t\t\t\t31\t\t// [string] text\n\n#define\tsvc_cdtrack\t\t\t\t\t32\t\t// [qbyte] track\n#define svc_sellscreen\t\t\t\t33\n\n#define svc_cutscene\t\t\t\t34\t//hmm... nq only... added after qw tree splitt?\n\n\n\n//QW svcs\n#define\tsvc_smallkick\t\t\t\t34\t\t// set client punchangle to 2\n#define\tsvc_bigkick\t\t\t\t\t35\t\t// set client punchangle to 4\n\n#define\tsvc_updateping\t\t\t\t36\t\t// [qbyte] [short]\n#define\tsvc_updateentertime\t\t\t37\t\t// [qbyte] [float]\n\n#define\tsvcqw_updatestatlong\t\t38\t\t// [qbyte] [long]\n\n#define\tsvc_muzzleflash\t\t\t\t39\t\t// [short] entity\n\n#define\tsvc_updateuserinfo\t\t\t40\t\t// [qbyte] slot [long] uid [string] userinfo\n\n#define\tsvc_download\t\t\t\t41\t\t// [short] size [size bytes]\n#define\tsvc_playerinfo\t\t\t\t42\t\t// variable\n#define\tsvc_nails\t\t\t\t\t43\t\t// [qbyte] num [48 bits] xyzpy 12 12 12 4 8\n#define\tsvc_chokecount\t\t\t\t44\t\t// [qbyte] packets choked\n#define\tsvc_modellist\t\t\t\t45\t\t// [strings]\n#define\tsvc_soundlist\t\t\t\t46\t\t// [strings]\n#define\tsvc_packetentities\t\t\t47\t\t// [...]\n#define\tsvc_deltapacketentities\t\t48\t\t// [...]\n#define svc_maxspeed\t\t\t\t49\t\t// maxspeed change, for prediction\n#define svc_entgravity\t\t\t\t50\t\t// gravity change, for prediction\n#define svc_setinfo\t\t\t\t\t51\t\t// setinfo on a client\n#define svc_serverinfo\t\t\t\t52\t\t// serverinfo\n#define svc_updatepl\t\t\t\t53\t\t// [qbyte] [qbyte]\n\n//mvdsv extended svcs (for mvd playback)\n#define svc_nails2\t\t\t\t\t54\t\t//qwe - [qbyte] num [52 bits] nxyzpy 8 12 12 12 4 8\n\n//FTE extended svcs\n#ifdef PEXT_SOUNDDBL\n#define svcfte_soundextended\t\t55\n#define svcfte_soundlistshort\t\t56\n#endif\n#ifdef PEXT_LIGHTSTYLECOL\n#define svcfte_lightstylecol\t\t57\n#endif\n\n//#define svcfte_svcremoved\t\t\t58\n\n//#define\tsvcfte_svcremoved\t\t59\n\n#ifdef PEXT_MODELDBL\n#define\tsvcfte_modellistshort\t\t60\t\t// [strings]\n#endif\n\n//#define svc_ftesetclientpersist\t61\t//ushort DATA\n\n#define svc_setportalstate\t\t\t62\n\n#define\tsvcfte_particle2\t\t\t63\n#define\tsvcfte_particle3\t\t\t64\n#define\tsvcfte_particle4\t\t\t65\n#define svcfte_spawnbaseline2\t\t66\n\n#define\tsvcfte_customtempent\t\t67\n\n#define svcfte_choosesplitclient\t68\n#define svcfte_showpic\t\t\t\t69\n#define svcfte_hidepic\t\t\t\t70\n#define svcfte_movepic\t\t\t\t71\n#define svcfte_updatepic\t\t\t72\n\n//73\n\n#define svcfte_effect\t\t\t\t74\t\t// [vector] org [byte] modelindex [byte] startframe [byte] framecount [byte] framerate\n#define svcfte_effect2\t\t\t\t75\t\t// [vector] org [short] modelindex [short] startframe [byte] framecount [byte] framerate\n\n#define svcfte_csqcentities\t\t\t76\t//entity lump for csqc\n\n#define svcfte_precache\t\t\t\t77\n\n#define svcfte_updatestatstring\t\t78\n#define svcfte_updatestatfloat\t\t79\n\n#define svcfte_trailparticles\t\t80\t\t// [short] entnum [short] effectnum [vector] start [vector] end\n#define svcfte_pointparticles\t\t81\t\t// [short] effectnum [vector] start [vector] velocity [short] count\n#define svcfte_pointparticles1\t\t82\t\t// [short] effectnum [vector] start, same as svc_pointparticles except velocity is zero and count is 1\n\n#define svcfte_cgamepacket\t\t\t83\n#define svcfte_voicechat\t\t\t84\n#define\tsvcfte_setangledelta\t\t85\t// [angle3] add this to the current viewangles\n#define svcfte_updateentities\t\t86\n#define svcfte_brushedit\t\t\t87\t// networked brush editing, paired with clcfte_brushedit.\n#define\tsvcfte_updateseats\t\t\t88\t// byte count, byte playernum[count]\n#define svcfte_setinfoblob\t\t\t89\t// [8] 1-based index [string] key [32] isfinal<<31|offset [16] chunksize [chunksize] data\n#define svcfte_cgamepacket_sized\t90\t//svcfte_cgamepacket with an extra short size right after the svc.\n#define\tsvcfte_temp_entity_sized\t91\t//svc_temp_entity with an extra short size right after the svc (high bit means nq, unset means qw).\n#define svcfte_csqcentities_sized\t92\t//entity lump for csqc (with size info)\n#define svcfte_setanglebase\t\t\t93\t//updates the base angle (and optionally locks the view, otherwise nudging it without race conditions.)\n#define svcfte_spawnstaticsound2\t94\t//*sigh*\n\n//fitz svcs\n#define svcfitz_skybox\t\t\t\t37\n#define svcfitz_bf\t\t\t\t\t40\n#define svcfitz_fog\t\t\t\t\t41\n#define svcfitz_spawnbaseline2\t\t42\n#define svcfitz_spawnstatic2\t\t43\n#define svcfitz_spawnstaticsound2\t44\n\n//nehahra svcs\n#define svcneh_skyboxsize\t\t\t50  // [coord] size (default is 4096)\n#define svcneh_fog\t\t\t\t\t51\t// [byte] enable <optional past this point, only included if enable is true> [float] density [byte] red [byte] green [byte] blue\n\n//QuakeEx(aka: rerelease) svcs.\n//\tthese are not really documented anywhere. we're trying to stick with protocol 15 (because that's the only documented protocol it supports properly thanks to demos)\n//\thowever we still need some special case svcs\n//  (there's also some problematic c2s differences too)\n#define svcqex_updateping\t\t\t46\t// [byte] slot, [signed_qe] ping\n#define svcqex_updatesocial\t\t\t47\t// [byte] slot, 8 bytes of unknown\n#define svcqex_updateplinfo\t\t\t48\t// [byte] slot, [leb128] health, [leb128] armour\n#define svcqex_locprint\t\t\t\t49\t// uses qe's localised string formatting, otherwise treat as svc_print.\n#define svcqex_servervars\t\t\t50\t// [leb128] changedvalues, [???] value...\n#define svcqex_seq\t\t\t\t\t51\t// [leb128] input sequence ack\n#define svcqex_achievement\t\t\t52\t// [string] codename\n#define svcqex_chat\t\t\t\t\t53\n#define svcqex_levelcompleted\t\t54\n#define svcqex_backtolobby\t\t\t55\n#define svcqex_localsound\t\t\t56\n#define svcqex_prompt\t\t\t\t57\n#define svcqex_loccenterprint\t\t58\t// [string] codename\n\n//DP extended svcs\n#define svcdp_downloaddata\t\t\t50\n#define svcdp_updatestatbyte\t\t51\n#define svcnq_effect\t\t\t\t52\t\t// [vector] org [byte] modelindex [byte] startframe [byte] framecount [byte] framerate\n#define svcnq_effect2\t\t\t\t53\t\t// [vector] org [short] modelindex [short] startframe [byte] framecount [byte] framerate\n#define\tsvcdp_precache\t\t\t\t54\t\t// [short] precacheindex [string] filename, precacheindex is + 0 for modelindex and +32768 for soundindex\n#define svcdp_spawnbaseline2\t\t55\n#define svcdp_spawnstatic2\t\t\t56\n#define svcdp_entities\t\t\t\t57\n#define svcdp_csqcentities\t\t\t58\n#define\tsvcdp_spawnstaticsound2\t\t59\t// [coord3] [short] samp [byte] vol [byte] aten\n#define svcdp_trailparticles\t\t60\t\t// [short] entnum [short] effectnum [vector] start [vector] end\n#define svcdp_pointparticles\t\t61\t\t// [short] effectnum [vector] start [vector] velocity [short] count\n#define svcdp_pointparticles1\t\t62\t\t// [short] effectnum [vector] start, same as svc_pointparticles except velocity is zero and count is 1\n\n\n\n#define svc_invalid\t\t\t256\n\n\nenum clustercmdops_e\n{\n\tccmd_bad = 0,\t\t\t//abort!\n\tccmd_stuffcmd,\t\t//regular ol stuffcmd\n\t\t\t//string concommand\n\tccmd_setcvar,\t\t//master->server to order a cvar change.\n\tccmd_print,\n\t\t\t//string message\n\tccmd_acceptserver,\n\t\t\t//serverid\n\tccmd_lostplayer,\t//player dropped/timed out\n\t\t\t//long plid\n\tccmd_foundplayer,\t//server->master, saying that a player tried to connect directly (and we need their info)\n\t\t\t//string name\n\t\t\t//string clientaddress\n\t\t\t//string guid\n\tccmd_takeplayer,\t//master->server, saying to allocate a slot for a player.\n\t\t\t//long plid\n\t\t\t//long fromsvid (0=no reply needed)\n\t\t\t//byte statcount\n\t\t\t//float stats[statcount]\n\tccmd_transferplayer,\t//server->master, asking to move them to a new server.\n\t\t\t//long plid\n\t\t\t//string map\n\t\t\t//byte ipv4=0, ipv6=1\n\t\t\t//byte statcount\n\t\t\t//float stats[statcount]\n\tccmd_transferedplayer,\t//master->server, saying the transfer was completed. original server no longer owns the player.\n\t\t\t//long toserver,\n\t\t\t//long playerid\n\tccmd_tookplayer,\t//server->master->server, saying that a player was taken.\n\t\t\t//long svid (this is always the *other* server)\n\t\t\t//long plid\n\t\t\t//string addr (this is the client's address when sent to the master, and the server's address that took the message in the message to the source server)\n\tccmd_transferabort,\t//server->master->server, saying that a player was rejected.\n\t\t\t//long plid\n\t\t\t//long fromsvid\n\t\t\t//string server\n\tccmd_saveplayer,\t//server->master, saves a player's stats.\n\t\t\t//long plid\n\t\t\t//byte statcount\n\t\t\t//float stats[statcount]\n\tccmd_serveraddress,\t//server->master, contains a few net addresses\n\t\t\t//string addresses[]\n\t\t\t//byte 0\n\tccmd_stringcmd,\n\t\t\t//string dest (black = broadcast to all)\n\t\t\t//string source (player name)\n\t\t\t//string cmd (type of event, handled by receiving server/forwarded to client)\n\t\t\t//string msg (extra info, like the typed text)\n};\n\n\nenum svcq2_ops_e\n{\n\tsvcq2_bad,\t//0\n\n\t// these ops are known to the game dll\n\tsvcq2_muzzleflash,\t//1\n\tsvcq2_muzzleflash2,\t//2\n\tsvcq2_temp_entity,\t//3\n\tsvcq2_layout,\t\t//4\n\tsvcq2_inventory,\t//5\n\n\t// the rest are private to the client and server\n\tsvcq2_nop,\t\t\t//6\n\tsvcq2_disconnect,\t//7\n\tsvcq2_reconnect,\t//8\n\tsvcq2_sound,\t\t//9\t\t\t// <see code>\n\tsvcq2_print,\t\t//10\t\t\t// [qbyte] id [string] null terminated string\n\tsvcq2_stufftext,\t//11\t\t\t// [string] stuffed into client's console buffer, should be \\n terminated\n\tsvcq2_serverdata,\t//12\t\t\t// [long] protocol ...\n\tsvcq2_configstring,\t//13\t\t// [short] [string]\n\tsvcq2_spawnbaseline,//14\n\tsvcq2_centerprint,\t//15\t\t// [string] to put in center of the screen\n\tsvcq2_download,\t\t//16\t\t// [short] size [size bytes]\n\tsvcq2_playerinfo,\t//17\t\t\t// variable\n\tsvcq2_packetentities,//18\t\t\t// [...]\n\tsvcq2_deltapacketentities,//19\t// [...]\n\tsvcq2_frame,\t\t\t//20 (the bastard to implement.)\n\n\tsvcq2ex_splitclient = 21,\n\tsvcq2ex_configblast = 22,\n\tsvcq2ex_spawnbaselineblast = 23,\n\tsvcq2ex_levelrestart = 24,\n\tsvcq2ex_danage = 25,\n\tsvcq2ex_locprint = 26,\n\tsvcq2ex_fog = 27,\n\tsvcq2ex_waiting = 28,\n\tsvcq2ex_botchat = 29,\n\tsvcq2ex_mapmarker = 30,\n\tsvcq2ex_routemarker = 31,\n\tsvcq2ex_muzzleflash3 = 32,\n\tsvcq2ex_achievement = 33,\n\n\n\tsvcr1q2_zpacket = 21,\n\tsvcr1q2_zdownload = 22,\n\tsvcr1q2_playerupdate = 23,\n\tsvcr1q2_setting = 24,\n\tsvcq2pro_gamestate = 23,\n\tsvcq2pro_setting = 24,\n};\n\nenum clcq2_ops_e\n{\n\tclcq2_bad = 0,\n\tclcq2_nop = 1,\n\tclcq2_move = 2,\t\t\t// [[usercmd_t]\n\tclcq2_userinfo = 3,\t\t// [[userinfo string]\n\tclcq2_stringcmd = 4,\t// [string] message\n\n\tclcr1q2_setting = 5,\t// [setting][value] R1Q2 settings support.\n\tclcr1q2_multimoves = 6,\t// for crappy clients that can't lerp\n\n\t//fte-extended\n\tclcq2_stringcmd_seat = 30,\n#ifdef VOICECHAT\n\tclcq2_voicechat = 31\n#endif\n};\n\nenum {\n\tR1Q2_CLSET_NOGUN\t\t\t= 0,\n\tR1Q2_CLSET_NOBLEND\t\t\t= 1,\n\tR1Q2_CLSET_RECORDING\t\t= 2,\n\tR1Q2_CLSET_PLAYERUPDATES\t= 3,\n\tR1Q2_CLSET_FPS\t\t\t\t= 4\n};\nenum {\n\tR1Q2_SVSET_PLAYERUPDATES\t= 0,\n\tR1Q2_SVSET_FPS\t\t\t\t= 1\n};\n\n//==============================================\n\n//\n// client to server\n//\n#define\tclc_bad\t\t\t\t0\n#define\tclc_nop \t\t\t1\n#define\tclc_disconnect\t\t2\t//nq only\n#define\tclc_move\t\t\t3\t// [[usercmd_t]\n#define\tclc_stringcmd\t\t4\t// [string] message\n#define\tclc_delta\t\t\t5\t// [qbyte] sequence number, requests delta compression of message\n#define clc_tmove\t\t\t6\t// teleport request, spectator only\n#define clc_upload\t\t\t7\t// \n\n#define clcdp_ackframe\t\t\t50\n#define clcdp_ackdownloaddata\t51\n\n#define clcfte_qcrequest\t\t81\n#define clcfte_prydoncursor\t\t82\n#define clcfte_voicechat\t\t83\n#define clcfte_brushedit\t\t84\n#define clcfte_move\t\t\t\t85\t//part of PEXT2_VRINPUTS. replaces clc_move+clcfte_prydoncursor+clcdp_ackframe\n#define clcfte_stringcmd_seat\t86\n\n#define VRM_LOSS\t(1u<<0)\t//for server packetloss reports\n#define VRM_DELAY\t(1u<<1)\t//for server to compute lag properly.\n#define VRM_SEATS\t(1u<<2) //for splitscreen to work properly\n#define VRM_FRAMES\t(1u<<3) //number of input frames in this packet.\n#define VRM_ACKS\t(1u<<4)\t//number of sequence acks included in message.\n\n//==============================================\n\n#define DLERR_FILENOTFOUND\t\t-1\t//server cannot serve the file\n#define DLERR_PERMISSIONS\t\t-2\t//server refuses to serve the file\n#define DLERR_UNKNOWN\t\t\t-3\t//server bugged out while trying to serve the request\n#define DLERR_REDIRECTFILE\t\t-4\t//client should download the specified file instead.\n#define DLERR_SV_REDIRECTPACK\t-5\t//client should download the specified package instead. may also be an http(s):// location.\n#define DLERR_SV_PACKAGE\t\t-6\t//not networked. packages require special file access.\n\n#define DLBLOCKSIZE\t\t\t1024 //chunked downloads use fixed-size chunks (which I somewhat regret, but too late now I guess, really ought to use ranges.).\n\n//these flags are sent as part of the svc_precache index, for any-time precaches. using the upper two bits means we still have 16k available models/sounds/etc\n#define PC_TYPE\t\t\t0xc000\n#define PC_MODEL\t\t0x0000\n#define PC_SOUND\t\t0x8000\n#define PC_PARTICLE\t\t0x4000\n#define PC_UNUSED\t\t0xc000\n\n#define GAME_COOP\t\t0\n#define GAME_DEATHMATCH\t1\n\n\n// playerinfo flags from server\n// playerinfo always sends: playernum, flags, origin[] and framenumber\n\n#define\tPF_MSEC\t\t\t(1<<0)\t//msecs says how long the player command was sitting on the server before it was sent back to the client\n#define\tPF_COMMAND\t\t(1<<1)\t//angles and movement values for other players (no msec or impulse)\n#define\tPF_VELOCITY1\t(1<<2)\n#define\tPF_VELOCITY2\t(1<<3)\n#define\tPF_VELOCITY3\t(1<<4)\n#define\tPF_MODEL\t\t(1<<5)\n#define\tPF_SKINNUM\t\t(1<<6)\n#define\tPF_EFFECTS\t\t(1<<7)\n#define\tPF_WEAPONFRAME\t(1<<8)\t\t// only sent for view player\n#define\tPF_DEAD\t\t\t(1<<9)\t\t// don't block movement any more\n#define\tPF_GIB\t\t\t(1<<10)\t\t// offset the view height differently\n\n//ZQuake.\n#define PF_PMC_SHIFT\t11\n#define\tPF_PMC_MASK\t\t((1<<11) | \\\n\t\t\t\t\t\t (1<<12) | \\\n\t\t\t\t\t\t (1<<13))\n#ifdef PEXT_HULLSIZE\n#define\tPF_HULLSIZE_Z\t(1<<14)\n#endif\n#define PF_EXTRA_PFS\t(1<<15)\n\n#ifdef PEXT_SCALE\n#define\tPF_SCALE\t\t(1<<16)\n#endif\n#ifdef PEXT_TRANS\n#define\tPF_TRANS\t\t(1<<17)\n#endif\n#ifdef PEXT_FATNESS\n#define\tPF_FATNESS\t\t(1<<18)\n#endif\n\n#define\tPF_COLOURMOD\t(1<<19)\n//#define\tPF_UNUSED\t(1<<20)\t//remember to faff with zext\n//#define\tPF_UNUSED\t(1<<21)\t//remember to faff with zext\n//note that if you add any more, you may need to change the check in the client so more can be parsed\n#define PF_ONGROUND\t\t(1<<22)\t//or 14, depending on extensions... messy.\n#define PF_SOLID\t\t(1<<23) //or 15, depending on extensions... messy.\n\n\n//not networked\n#define PF_INWATER\t\t(1u<<31) //for network smartjump.\n\n\n\n// player move types\n#define PMC_NORMAL\t\t\t0\t\t// normal ground movement\n#define PMC_NORMAL_JUMP_HELD\t1\t// normal ground novement + jump_held\n#define PMC_OLD_SPECTATOR\t2\t\t// fly through walls (QW compatibility mode)\n#define PMC_SPECTATOR\t\t3\t\t// fly through walls\n#define PMC_FLY\t\t\t\t4\t\t// fly, bump into walls\n#define PMC_NONE\t\t\t5\t\t// can't move (client had better lerp the origin...)\n#define PMC_FREEZE\t\t\t6\t\t// TODO: lerp movement and viewangles\n#define PMC_WALLWALK\t\t7\t\t// future extension\n\n//any more will require a different protocol message.\n\n//==============================================\n\n// if the high bit of the client to server qbyte is set, the low bits are\n// client move cmd bits\n// ms and angle2 are always sent, the others are optional\n#define\tCM_ANGLE1 \t(1<<0)\n#define\tCM_ANGLE3 \t(1<<1)\n#define\tCM_FORWARD\t(1<<2)\n#define\tCM_SIDE\t\t(1<<3)\n#define\tCM_UP\t\t(1<<4)\n#define\tCM_BUTTONS\t(1<<5)\n#define\tCM_IMPULSE\t(1<<6)\n#define\tCM_ANGLE2 \t(1<<7)\n\n//sigh...\n#define\tQ2CM_ANGLE1 \t(1<<0)\n#define\tQ2CM_ANGLE2 \t(1<<1)\n#define\tQ2CM_ANGLE3 \t(1<<2)\n#define\tQ2CM_FORWARD\t(1<<3)\n#define\tQ2CM_SIDE\t\t(1<<4)\n#define\tQ2CM_UP\t\t\t(1<<5)\n#define\tQ2CM_BUTTONS\t(1<<6)\n#define\tQ2CM_IMPULSE\t(1<<7)\n\n#define R1Q2_BUTTON_BYTE_FORWARD  4\n#define R1Q2_BUTTON_BYTE_SIDE     8\n#define R1Q2_BUTTON_BYTE_UP       16\n#define R1Q2_BUTTON_BYTE_ANGLE1   32\n#define R1Q2_BUTTON_BYTE_ANGLE2   64\n\n//==============================================\n\n// the first 16 bits of a packetentities update holds 9 bits\n// of entity number and 7 bits of flags\n#define\tU_UNUSABLE\t(1<<8)\n#define\tU_ORIGIN1\t(1<<9)\n#define\tU_ORIGIN2\t(1<<10)\n#define\tU_ORIGIN3\t(1<<11)\n#define\tU_ANGLE2\t(1<<12)\n#define\tU_FRAME\t\t(1<<13)\n#define\tU_REMOVE\t(1<<14)\t\t// REMOVE this entity, don't add it\n#define\tU_MOREBITS\t(1<<15)\n\n// if MOREBITS is set, these additional flags are read in next\n#define\tU_ANGLE1\t(1<<0)\n#define\tU_ANGLE3\t(1<<1)\n#define\tU_MODEL\t\t(1<<2)\n#define\tU_COLORMAP\t(1<<3)\n#define\tU_SKIN\t\t(1<<4)\n#define\tU_EFFECTS\t(1<<5)\n#define\tU_SOLID\t\t(1<<6)\t\t// the entity should be solid for prediction\n#ifdef PROTOCOLEXTENSIONS\n#define U_EVENMORE\t(1<<7)\t//extension info follows\n\n//fte extensions\n//EVENMORE flags\n#ifdef PEXT_SCALE\n#define U_SCALE\t\t(1<<0)\t//scaler of alias models\n#endif\n#ifdef PEXT_TRANS\n#define U_TRANS\t\t(1<<1)\t//transparency value\n#endif\n#ifdef PEXT_FATNESS\n#define U_FATNESS\t(1<<2)\t//qbyte describing how fat an alias model should be. (moves verticies along normals). Useful for vacuum chambers...\n#endif\n#ifdef PEXT_MODELDBL\n#define U_MODELDBL\t(1<<3)\t//extra bit for modelindexes\n#endif\n#define U_UNUSED1\t(1<<4)\n//FIXME: IMPLEMENT\n#ifdef PEXT_ENTITYDBL\n#define U_ENTITYDBL\t(1<<5)\t//use an extra qbyte for origin parts, cos one of them is off\n#endif\n#ifdef PEXT_ENTITYDBL2\n#define U_ENTITYDBL2 (1<<6)\t//use an extra qbyte for origin parts, cos one of them is off\n#endif\n#define U_YETMORE\t(1<<7)\t//even more extension info stuff.\n\n#define U_DRAWFLAGS\t(1<<8)\t//use an extra qbyte for origin parts, cos one of them is off\n#define U_ABSLIGHT\t(1<<9)\t//Force a lightlevel\n\n#define U_COLOURMOD\t(1<<10)\t//rgb\n\n#define U_DPFLAGS (1<<11)\n\n\n#define U_TAGINFO (1<<12)\n#define U_LIGHT (1<<13)\n#define\tU_EFFECTS16\t(1<<14)\n\n#define U_FARMORE (1<<15)\n\n#endif\n\n//FTE Replacement Deltas\n//first byte contains the stuff that's most likely to change constantly\n#define UF_FRAME\t\t(1u<<0)\n#define UF_ORIGINXY\t\t(1u<<1)\n#define UF_ORIGINZ\t\t(1u<<2)\n#define UF_ANGLESXZ\t\t(1u<<3)\n#define UF_ANGLESY\t\t(1u<<4)\n#define UF_EFFECTS\t\t(1u<<5)\n#define UF_PREDINFO\t\t(1u<<6)\t/*ent is predicted, probably a player*/\n#define UF_EXTEND1\t\t(1u<<7)\n\n/*stuff which is common on ent spawning*/\n#define UF_RESET\t\t(1u<<8)\t/*client will reset entire strict to its baseline*/\n#define UF_16BIT_LERPTIME\t(1u<<9)\t/*either included frame/skin/model is 16bit (not part of the deltaing itself), or there's nextthink info*/\n#define UF_MODEL\t\t(1u<<10)\n#define UF_SKIN\t\t\t(1u<<11)\n#define UF_COLORMAP\t\t(1u<<12)\n#define UF_SOLID\t\t(1u<<13)\n#define UF_FLAGS\t\t(1u<<14)\n#define UF_EXTEND2\t\t(1u<<15)\n\n/*the rest is optional extensions*/\n#define UF_ALPHA\t\t(1u<<16)\n#define UF_SCALE\t\t(1u<<17)\n#define UF_BONEDATA\t\t(1u<<18)\n#define UF_DRAWFLAGS\t(1u<<19)\n#define UF_TAGINFO\t\t(1u<<20)\n#define UF_LIGHT\t\t(1u<<21)\n#define UF_TRAILEFFECT\t(1u<<22)\n#define UF_EXTEND3\t\t(1u<<23)\n\n#define UF_COLORMOD\t\t(1u<<24)\n#define UF_GLOW\t\t\t(1u<<25)\n#define UF_FATNESS\t\t(1u<<26)\n#define UF_MODELINDEX2  (1u<<27)\n#define UF_GRAVITYDIR\t(1u<<28)\n#define UF_EFFECTS2_OLD\t(1u<<29) /*specified >8bit effects, replaced with variable length*/\n#define UF_UNUSED1\t\t(1u<<30)\n#define UF_EXTEND4\t\t(1u<<31)\n\n/*these flags are generally not deltaed as they're changing constantly*/\n#define UFP_FORWARD\t\t(1u<<0)\n#define UFP_SIDE\t\t(1u<<1)\n#define UFP_UP\t\t\t(1u<<2)\n#define UFP_MOVETYPE\t(1u<<3)\t/*deltaed*/\n#define UFP_VELOCITYXY\t(1u<<4)\n#define UFP_VELOCITYZ\t(1u<<5)\n#define UFP_MSEC\t\t(1u<<6)\n#define UFP_WEAPONFRAME_OLD\t(1u<<7)\t//no longer used. just a stat now that I rewrote stat deltas.\n#define UFP_VIEWANGLE\t(1u<<7)\n\n#define UF_SV_REMOVE   UF_16BIT_LERPTIME\t/*special flag  - lerptime isn't delta tracked serverside (reset sent as required with other fields)*/\n\n\n\n#ifdef NQPROT\n//NQ svc_clientdata stat updates.\n#define\tSU_VIEWHEIGHT\t(1<<0)\n#define\tSU_IDEALPITCH\t(1<<1)\n#define\tSU_PUNCH1\t\t(1<<2)\n#define\tSU_PUNCH2\t\t(1<<3)\n#define\tSU_PUNCH3\t\t(1<<4)\n#define\tSU_VELOCITY1\t(1<<5)\n#define\tSU_VELOCITY2\t(1<<6)\n#define\tSU_VELOCITY3\t(1<<7)\n//define\tSU_AIMENT\t\t(1<<8)  AVAILABLE BIT\n#define\tSU_ITEMS\t\t(1<<9)\n#define\tSU_ONGROUND\t\t(1<<10)\t\t// no data follows, the bit is it\n#define\tSU_INWATER\t\t(1<<11)\t\t// no data follows, the bit is it\n#define\tSU_WEAPONFRAME\t(1<<12)\n#define\tSU_ARMOR\t\t(1<<13)\n#define\tSU_WEAPONMODEL\t(1<<14)\n#define\tSU_EXTEND1\t\t(1<<15)\n\n#define FITZSU_WEAPONMODEL2\t(1<<16) // 1 byte, this is .weaponmodel & 0xFF00 (second byte)\n#define FITZSU_ARMOR2\t\t(1<<17) // 1 byte, this is .armorvalue & 0xFF00 (second byte)\n#define FITZSU_AMMO2\t\t(1<<18) // 1 byte, this is .currentammo & 0xFF00 (second byte)\n#define FITZSU_SHELLS2\t\t(1<<19) // 1 byte, this is .ammo_shells & 0xFF00 (second byte)\n#define FITZSU_NAILS2\t\t(1<<20) // 1 byte, this is .ammo_nails & 0xFF00 (second byte)\n#define FITZSU_ROCKETS2\t\t(1<<21) // 1 byte, this is .ammo_rockets & 0xFF00 (second byte)\n#define FITZSU_CELLS2\t\t(1<<22) // 1 byte, this is .ammo_cells & 0xFF00 (second byte)\n#define SU_EXTEND2\t\t(1<<23) // another byte to follow\n\n#define FITZSU_WEAPONFRAME2\t(1<<24) // 1 byte, this is .weaponframe & 0xFF00 (second byte)\n#define FITZSU_WEAPONALPHA\t(1<<25) // 1 byte, this is alpha for weaponmodel, uses ENTALPHA_ENCODE, not sent if ENTALPHA_DEFAULT\n#define FITZSU_UNUSED26\t\t(1<<26)\n#define FITZSU_UNUSED27\t\t(1<<27)\n#define FITZSU_UNUSED28\t\t(1<<28)\n#define FITZSU_UNUSED29\t\t(1<<29)\n#define FITZSU_UNUSED30\t\t(1<<30)\n#define SU_EXTEND3\t\t(1<<31) // another byte to follow, future expansion\n\n//builds on top of fitz\n#define\tQEX_SU_FLOATCOORDS\t(1<<8)\n#define QEX_SU_ENTFLAGS\t\t(1<<26)\t// ULEB128 copy of the player's .flags field\n\n// first extend byte\n#define DPSU_PUNCHVEC1\t\t(1<<16)\n#define DPSU_PUNCHVEC2\t\t(1<<17)\n#define DPSU_PUNCHVEC3\t\t(1<<18)\n#define DPSU_VIEWZOOM\t\t(1<<19) // byte factor (0 = 0.0 (not valid), 255 = 1.0)\n#define DPSU_UNUSED20\t\t(1<<20)\n#define DPSU_UNUSED21\t\t(1<<21)\n#define DPSU_UNUSED22\t\t(1<<22)\n// second extend byte\n#define DPSU_UNUSED24\t\t(1<<24)\n#define DPSU_UNUSED25\t\t(1<<25)\n#define DPSU_UNUSED26\t\t(1<<26)\n#define DPSU_UNUSED27\t\t(1<<27)\n#define DPSU_UNUSED28\t\t(1<<28)\n#define DPSU_UNUSED29\t\t(1<<29)\n#define DPSU_UNUSED30\t\t(1<<30)\n\n\n\n//NQ fast updates\n#define\tNQU_MOREBITS\t(1<<0)\n#define\tNQU_ORIGIN1\t(1<<1)\n#define\tNQU_ORIGIN2\t(1<<2)\n#define\tNQU_ORIGIN3\t(1<<3)\n#define\tNQU_ANGLE2\t(1<<4)\n#define\tNQU_NOLERP\t(1<<5)\t\t// don't interpolate movement\n#define\tNQU_FRAME\t\t(1<<6)\n#define NQU_SIGNAL\t(1<<7)\t\t// just differentiates from other updates\n\n// svc_update can pass all of the fast update bits, plus more\n#define\tNQU_ANGLE1\t(1<<8)\n#define\tNQU_ANGLE3\t(1<<9)\n#define\tNQU_MODEL\t\t(1<<10)\n#define\tNQU_COLORMAP\t(1<<11)\n#define\tNQU_SKIN\t\t(1<<12)\n#define\tNQU_EFFECTS\t(1<<13)\n#define\tNQU_LONGENTITY\t(1<<14)\n\n\n// LordHavoc's: protocol extension\n#define DPU_EXTEND1\t\t(1<<15)\n// LordHavoc: first extend byte\n#define DPU_DELTA\t\t\t(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)\n#define DPU_ALPHA\t\t\t(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)\n#define DPU_SCALE\t\t\t(1<<18) // 1 byte, scale / 16 positive, not sent if 1.0\n#define DPU_EFFECTS2\t\t(1<<19) // 1 byte, this is .effects & 0xFF00 (second byte)\n#define DPU_GLOWSIZE\t\t(1<<20) // 1 byte, encoding is float/4.0, unsigned, not sent if 0\n#define DPU_GLOWCOLOR\t\t(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)\n// LordHavoc: colormod feature has been removed, because no one used it\n#define DPU_COLORMOD\t\t(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...\n#define DPU_EXTEND2\t\t(1<<23) // another byte to follow\n// LordHavoc: second extend byte\n#define DPU_GLOWTRAIL\t\t(1<<24) // leaves a trail of particles (of color .glowcolor, or black if it is a negative glowsize)\n#define DPU_VIEWMODEL\t\t(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\n#define DPU_FRAME2\t\t(1<<26) // 1 byte, this is .frame & 0xFF00 (second byte)\n#define DPU_MODEL2\t\t(1<<27) // 1 byte, this is .modelindex & 0xFF00 (second byte)\n#define DPU_EXTERIORMODEL\t(1<<28) // causes this model to not be drawn when using a first person view (third person will draw it, first person will not)\n#define DPU_UNUSED29\t\t(1<<29) // future expansion\n#define DPU_UNUSED30\t\t(1<<30) // future expansion\n#define DPU_EXTEND3\t\t(1<<31) // another byte to follow, future expansion\n\n#define FITZU_ALPHA\t\t\t(1<<16)\n#define FITZU_FRAME2\t\t(1<<17)\n#define FITZU_MODEL2\t\t(1<<18)\n#define FITZU_LERPFINISH\t(1<<19)\n#define RMQU_SCALE\t\t\t(1<<20)\n\n#define QE_U_FLOATCOORDS\t(1<<21)\t//set on the local player entity, to boost precision for prediction.\n#define QE_U_SOLIDTYPE\t\t(1<<22)\t//for prediction I suppose.\n//#define QE_U_EXTEND\t\t(1<<23)\n#define QE_U_ENTFLAGS\t\t(1<<24)\t//not sure why this needs to be networked, oh well. redundant with clientdata\n#define QE_U_HEALTH\t\t\t(1<<25)\t//not sure why this needs to be networked, oh well.\n#define QE_U_UNKNOWN26\t\t(1<<26)\t//seems to be some sort of nodraw flag (presumably for solid bmodels that need a modelindex for collisions).\n#define QE_U_UNUSED27\t\t(1<<27)\n#define QE_U_UNUSED28\t\t(1<<28)\n#define QE_U_UNUSED29\t\t(1<<29)\n#define QE_U_UNUSED30\t\t(1<<30)\n#define QE_U_UNUSED31\t\t(1u<<31)\n\n#endif\n\n\n\n#define\tQ2U_ORIGIN1\t\t(1<<0)\n#define\tQ2U_ORIGIN2\t\t(1<<1)\n#define\tQ2U_ANGLE2\t\t(1<<2)\n#define\tQ2U_ANGLE3\t\t(1<<3)\n#define\tQ2U_FRAME8\t\t(1<<4)\t\t// frame is a qbyte\n#define\tQ2U_EVENT\t\t(1<<5)\n#define\tQ2U_REMOVE\t\t(1<<6)\t\t// REMOVE this entity, don't add it\n#define\tQ2U_MOREBITS1\t(1<<7)\t\t// read one additional qbyte\n\n// second qbyte\n#define\tQ2U_NUMBER16\t(1<<8)\t\t// NUMBER8 is implicit if not set\n#define\tQ2U_ORIGIN3\t\t(1<<9)\n#define\tQ2U_ANGLE1\t\t(1<<10)\n#define\tQ2U_MODEL\t\t(1<<11)\n#define Q2U_RENDERFX8\t(1<<12)\t\t// fullbright, etc\n#define Q2UX_ANGLE16\t(1<<13)\n#define\tQ2U_EFFECTS8\t(1<<14)\t\t// autorotate, trails, etc\n#define\tQ2U_MOREBITS2\t(1<<15)\t\t// read one additional qbyte\n\n// third qbyte\n#define\tQ2U_SKIN8\t\t\t(1<<16)\n#define\tQ2U_FRAME16\t\t\t(1<<17)\t\t// frame is a short\n#define\tQ2U_RENDERFX16\t\t(1<<18)\t\t// 8 + 16 = 32\n#define\tQ2U_EFFECTS16\t\t(1<<19)\t\t// 8 + 16 = 32\n#define\tQ2U_MODEL2\t\t\t(1<<20)\t\t// weapons, flags, etc\n#define\tQ2U_MODEL3\t\t\t(1<<21)\n#define\tQ2U_MODEL4\t\t\t(1<<22)\n#define\tQ2U_MOREBITS3\t\t(1<<23)\t\t// read one additional qbyte\n\n// fourth qbyte\n#define\tQ2U_OLDORIGIN\t\t(1<<24)\t\t// FIXME: get rid of this\n#define\tQ2U_SKIN16\t\t\t(1<<25)\n#define\tQ2U_SOUND\t\t\t(1<<26)\n#define\tQ2U_SOLID\t\t\t(1<<27)\n#define Q2UX_INDEX16\t\t(1<<28)\t\t//model or sound is 16bit\n#define Q2UEX_EFFECTS64\t\t(1<<29)\n#define Q2UEX_ALPHA\t\t\t(1<<30)\n#define Q2UEX_MOREBITS4\t\t(1u<<31)\n#define Q2UEX_SCALE\t\t\t(1ull<<32)\n#define Q2UEX_INSTANCE\t\t(1ull<<33)\n#define Q2UEX_OWNER\t\t\t(1ull<<34)\n#define Q2UEX_OLDFRAME\t\t(1ull<<35)\n\n#define Q2UX_UNUSED\t\t(Q2UX_UNUSED1|Q2UX_UNUSED2|Q2UX_UNUSED3|Q2UX_UNUSED4)\n\n//QuakeEx-specific stuff\n//gamevar info\n#define QEX_GV_DEATHMATCH\t\t(1<<0)\n#define QEX_GV_IDEALPITCHSCALE\t(1<<1)\n#define QEX_GV_FRICTION\t\t\t(1<<2)\n#define QEX_GV_EDGEFRICTION\t\t(1<<3)\n#define QEX_GV_STOPSPEED\t\t(1<<4)\n#define QEX_GV_MAXVELOCITY\t\t(1<<5)\n#define QEX_GV_GRAVITY\t\t\t(1<<6)\n#define QEX_GV_NOSTEP\t\t\t(1<<7)\n#define QEX_GV_MAXSPEED\t\t\t(1<<8)\n#define QEX_GV_ACCELERATE\t\t(1<<9)\n#define QEX_GV_CONTROLLERONLY\t(1<<10)\n#define QEX_GV_TIMELIMIT\t\t(1<<11)\n#define QEX_GV_FRAGLIMIT\t\t(1<<12)\n#define QEX_GV_TEAMPLAY\t\t\t(1<<13)\n#define QEX_GV_ALL\t\t\t\t((1<<14)-1)\n\n//==============================================\n//obsolete demo players info\n#define DF_ORIGINX\t\t(1u<<0)\n#define DF_ORIGINY\t\t(1u<<1)\n#define DF_ORIGINZ\t\t(1u<<2)\n#define DF_ORIGINALL\t(DF_ORIGINX|DF_ORIGINY|DF_ORIGINZ)\n#define DF_ANGLEX\t\t(1u<<3)\n#define DF_ANGLEY\t\t(1u<<4)\n#define DF_ANGLEZ\t\t(1u<<5)\n#define DF_ANGLESALL\t(DF_ANGLEX|DF_ANGLEY|DF_ANGLEZ)\n#define DF_EFFECTS\t\t(1u<<6)\n#define DF_SKINNUM\t\t(1u<<7)\n#define DF_DEAD\t\t\t(1u<<8)\n#define DF_GIB\t\t\t(1u<<9)\n#define DF_WEAPONFRAME\t(1u<<10)\n#define DF_MODEL\t\t(1u<<11)\n#define DF_RESET (DF_ORIGINALL|DF_ANGLESALL|DF_EFFECTS|DF_SKINNUM|DF_WEAPONFRAME|DF_MODEL)\n\n//==============================================\n\n// a sound with no channel is a local only sound\n// the sound field has bits 0-2: channel, 3-12: entity, 13: unused, 14-15: flags\n#define\tQWSND_VOLUME\t\t(1<<15)\t\t// a qbyte\n#define\tQWSND_ATTENUATION\t(1<<14)\t\t// a qbyte\n\n#define\tNQSND_VOLUME\t\t(1<<0)\t\t// a qbyte\n#define\tNQSND_ATTENUATION\t(1<<1)\t\t// a qbyte\n//#define DPSND_LOOPING\t\t(1<<2)\t\t// a long, supposedly\n#define FTESND_MOREFLAGS\t(1<<2)\t\t// actually, chan flags, mostly.\n#define NQSND_LARGEENTITY\t(1<<3)\t\t//both dp+fitz\n#define NQSND_LARGESOUND\t(1<<4)\t\t//both dp+fitz\n#define\tDPSND_SPEEDUSHORT4000\t(1<<5)\t\t// ushort speed*4000 (speed is usually 1.0, a value of 0.0 is the same as 1.0)\n#define FTESND_TIMEOFS\t\t(1<<6)\t\t//signed short, in milliseconds.\n#define FTESND_PITCHADJ\t\t(1<<7)\t\t\t//a byte (speed percent (0=100%))\n//more flags are weird.\n#define FTESND_VELOCITY\t\t(CF_NET_SENTVELOCITY<<8)\t//borrowed.\n//FTESND_NOSPACIALISE\t\t(CF_NOSPACIALISE<<8)\n//FTESND_NOREVERB\t\t\t(CF_NOREVERB<<8)\n//FTESND_FORCELOOP\t\t\t(CF_FORCELOOP<<8)\n//FTESND_FOLLOW\t\t\t\t(CF_FOLLOW<<8)\n//FTESND_RESERVED\t\t\t(CF_RESERVEDN<<8)\n\n#define DEFAULT_SOUND_PACKET_VOLUME 255\n#define DEFAULT_SOUND_PACKET_ATTENUATION 1.0\n\n//baseline flags\n#define FITZ_B_LARGEMODEL\t(1<<0)\n#define FITZ_B_LARGEFRAME\t(1<<1)\n#define FITZ_B_ALPHA\t\t(1<<2)\n#define RMQFITZ_B_SCALE\t\t(1<<3)\n\n#define QEX_B_SOLID\t\t\t(1<<3)\n#define QEX_B_UNKNOWN4\t\t(1<<4)\n#define QEX_B_UNKNOWN5\t\t(1<<5)\n#define QEX_B_UNKNOWN6\t\t(1<<6)\n#define QEX_B_UNKNOWN7\t\t(1<<7)\n\n#define\tDEFAULT_VIEWHEIGHT\t22\n\n\n// svc_print messages have an id, so messages can be filtered\n#define\tPRINT_LOW\t\t\t0\n#define\tPRINT_MEDIUM\t\t1\n#define\tPRINT_HIGH\t\t\t2\n#define\tPRINT_CHAT\t\t\t3\t// also go to chat buffer\n\n//\n// temp entity events\n//\nenum {\n\tTE_SPIKE\t\t\t\t= 0,\n\tTE_SUPERSPIKE\t\t\t= 1,\n\tTEQW_QWGUNSHOT\t\t\t= 2,\t//qw has count byte, nq does not\n\tTENQ_NQGUNSHOT\t\t\t= 2,\t//nq has no count byte\n\tTEQW_QWEXPLOSION\t\t= 3,\t//remapped to TEQW_EXPLOSIONNOSPRITE for nq.\n\tTENQ_NQEXPLOSION\t\t= 3,\t//remapped to TEQW_EXPLOSIONNOSPRITE for nq.\n\tTE_TAREXPLOSION\t\t\t= 4,\n\tTE_LIGHTNING1\t\t\t= 5,\n\tTE_LIGHTNING2\t\t\t= 6,\n\tTE_WIZSPIKE\t\t\t\t= 7,\n\tTE_KNIGHTSPIKE\t\t\t= 8,\n\tTE_LIGHTNING3\t\t\t= 9,\n\tTE_LAVASPLASH\t\t\t= 10,\n\tTE_TELEPORT\t\t\t\t= 11,\n\n\tTEQW_QWBLOOD\t\t\t= 12,\t//implemented as a particle() in nq\n\tTENQ_EXPLOSION2\t\t\t= 12,\t//remapped to TEQW_EXPLOSION2 for qw\n\tTEQW_LIGHTNINGBLOOD\t\t= 13,\t//implemented as a particle() in nq\n\tTENQ_BEAM\t\t\t\t= 13,\t//remapped to TEQW_BEAM for qw\n\n#ifdef PEXT_TE_BULLET\n\tTE_BULLET\t\t\t\t= 14,\n\tTEQW_SUPERBULLET\t\t= 15,\n#endif\n\tTENQ_RAILTRAIL\t\t\t= 15,\t//gah\t[vector] origin [coord] red [coord] green [coord] blue\n\tTE_EXPLOSION3_NEH\t\t= 16,\t//gah\t[vector] origin [coord] red [coord] green [coord] blue\n\tTEQW_RAILTRAIL\t\t\t= 17,\t//use the builtin, luke.\n\tTENQ_NEHLIGHTNING4\t\t= 17,\t//gah\t[string] model [entity] entity [vector] start [vector] end\n\tTEQW_NEHLIGHTNING4\t\t= 1000,\t//give a real value if its ever properly implemented\n\tTEQW_BEAM\t\t\t\t= 18,\t//use the builtin, luke.\n\tTENQ_NEHSMOKE\t\t\t= 18,\t//gah\t[vector] origin [byte] palette\n\tTEQW_EXPLOSION2\t\t\t= 19,\t//use the builtin, luke.\n\tTEQW_NQEXPLOSION\t\t= 20,\t//nq-style explosion over qw\n\tTENQ_QWEXPLOSION\t\t= 20,\t//qw-style explosion over nq\n\tTEQW_NQGUNSHOT\t\t\t= 21,\t//nq has count byte, qw does not\n\tTENQ_QWGUNSHOT\t\t\t= 21,\t//nq has count byte, qw does not\n\n\t// hexen 2\n\tTEH2_STREAM_LIGHTNING_SMALL\t= 24,\n\tTEH2_STREAM_CHAIN\t\t\t= 25,\n\tTEH2_STREAM_SUNSTAFF1\t\t= 26,\n\tTEH2_STREAM_SUNSTAFF2\t\t= 27,\n\tTEH2_STREAM_LIGHTNING\t\t= 28,\n\tTEH2_STREAM_COLORBEAM\t\t= 29,\n\tTEH2_STREAM_ICECHUNKS\t\t= 30,\n\tTEH2_STREAM_GAZE\t\t\t= 31,\n\tTEH2_STREAM_FAMINE\t\t= 32,\n\tTEH2_PARTICLEEXPLOSION = 33,\n\n\tTEDP_BLOOD\t\t\t= 50, // [coord*3] origin [byte*3] vel [byte] count\n\tTEDP_SPARK\t\t\t= 51,\n\tTEDP_BLOODSHOWER\t= 52,\n\tTEDP_EXPLOSIONRGB\t= 53,\n\tTEDP_PARTICLECUBE\t= 54,\n\tTEDP_PARTICLERAIN\t= 55, // [vector] min [vector] max [vector] dir [short] count [byte] color\n\tTEDP_PARTICLESNOW\t= 56, // [vector] min [vector] max [vector] dir [short] count [byte] color\n\tTEDP_GUNSHOTQUAD\t= 57, // [vector] origin\n\tTEDP_SPIKEQUAD\t\t= 58, // [vector] origin\n\tTEDP_SUPERSPIKEQUAD\t= 59, // [vector] origin\n\tTEDP_EXPLOSIONQUAD\t= 70, // [vector] origin\n\tTEDP_SMALLFLASH\t\t= 72, // [vector] origin\n\tTEDP_CUSTOMFLASH\t= 73,\n\tTEDP_FLAMEJET\t\t= 74,\n\tTEDP_PLASMABURN\t\t= 75,\n\tTEDP_TEI_G3\t\t\t= 76,\n\tTEDP_SMOKE\t\t\t= 77,\n\tTEDP_TEI_BIGEXPLOSION = 78,\n\tTEDP_TEI_PLASMAHIT\t= 79,\n};\n\n\n#define CTE_CUSTOMCOUNT\t\t1\n#define CTE_CUSTOMDIRECTION\t2\n#define CTE_STAINS\t\t\t4\n#define CTE_GLOWS\t\t\t8\n#define CTE_CHANNELFADE\t\t16\n#define CTE_CUSTOMVELOCITY\t32\n#define CTE_PERSISTANT\t\t64\n#define CTE_ISBEAM\t\t\t128\n//CTE_ISBEAM && (CTE_CUSTOMVELOCITY||CTE_CUSTOMDIRECTION) = BOX particles.\n\n//FTE's version of TEI_SHOWLMP2. tei's values and coding scheme which makes no sense to anyone but him.\n#define SL_ORG_NW\t0\n#define SL_ORG_NE\t1\n#define SL_ORG_SW\t2\n#define SL_ORG_SE\t3\n#define SL_ORG_CC\t4\n#define SL_ORG_CN\t5\n#define SL_ORG_CS\t6\n#define SL_ORG_CW\t7\n#define SL_ORG_CE\t8\n\n//relative coding where offsets are predictable\n#define SL_ORG_TL\t20\n#define SL_ORG_TR\t21\n#define SL_ORG_BL\t22\n#define SL_ORG_BR\t23\n#define SL_ORG_MM\t24\n#define SL_ORG_TM\t25\n#define SL_ORG_BM\t26\n#define SL_ORG_ML\t27\n#define SL_ORG_MR\t28\n\n/*\n==========================================================\n\n  ELEMENTS COMMUNICATED ACROSS THE NET\n\n==========================================================\n*/\n\n#ifndef MAX_CLIENTS\n#define\tMAX_CLIENTS\t\t255\t/*max 255, min 32*/\n#endif\n#define\tQWMAX_CLIENTS\t32 /*QW's standard max. clients might have issues above this value*/\n#define\tNQMAX_CLIENTS\t16 /*NQ's standard max. clients might have issues above this value*/\n\n#define\tUPDATE_BACKUP\t64\t// copies of entity_state_t to keep buffered\n\t\t\t\t\t\t\t// must be power of two\n#define\tUPDATE_MASK\t\t(UPDATE_BACKUP-1)\n\n#define\tQ2UPDATE_BACKUP\t16\t// copies of entity_state_t to keep buffered\n\t\t\t\t\t\t\t// must be power of two\n#define\tQ2UPDATE_MASK\t\t(Q2UPDATE_BACKUP-1)\n\n#define\tQ3UPDATE_BACKUP\t32\t// copies of entity_state_t to keep buffered\n\t\t\t\t\t\t\t// must be power of two\n#define\tQ3UPDATE_MASK\t\t(Q3UPDATE_BACKUP-1)\n\n\n// entity_state_t is the information conveyed from the server\n// in an update message\n\ntypedef struct entity_state_s\n{\n\tunsigned int\t\tnumber;\t\t\t// edict index\n\tunsigned int\t\tsequence;\n\n\tunsigned short\t\tmodelindex;\n\tqbyte\t\t\t\tinactiveflag;\n\tqbyte\t\t\t\tbonecount;\t\t//for networked bones\n\tunsigned int\t\tboneoffset;\t\t//offset into the frame (not a pointer, to avoid issues with reallocing extra storage)\n\n//\tunsigned int\t\teflags;\t\t\t// nolerp, etc\n\n\tunsigned int\t\teffects;\n\n\tvec3_t\torigin;\n\tvec3_t\tangles;\n\tunion\n\t{\n#if defined(Q2CLIENT) || defined(Q2SERVER)\n\t\tstruct\n\t\t{\n\t\t\tunsigned int\t\trenderfx;\t\t//q2\n\t\t\tvec3_t\told_origin;\t\t//q2/q3\n\n\t\t\tunsigned short\t\tmodelindex3;\t//q2\n\t\t\tunsigned short\t\tmodelindex4;\t//q2\n\t\t\tunsigned short\t\tsound;\t\t\t//q2\n\t\t\tqbyte\t\t\t\tevent;\t\t\t//q2\n\t\t\tqbyte\t\t\t\tinstance;\t\t//q2ex (splitcreen, so specific seats see it or not)\n\t\t\tunsigned short\t\towner;\t\t\t//q2ex for splitscreen prediction I guess (can't just network it as non-solid).\n\t\t\tunsigned short\t\toldframe;\t\t//q2ex\n\t\t} q2;\n#endif\n\t\tstruct\n\t\t{\n\t\t\t/*info to predict other players, so I don't get yelled at if fte were to stop supporting it*/\n\t\t\tqbyte pmovetype;\t//&128 means onground.\n\t\t\tqbyte msec;\n\t\t\tshort vangle[3];\n\n\t\t\tshort movement[3];\n\t\t\tshort velocity[3]; // 1/8th\n\n\t\t\tunsigned short weaponframe;\n\t\t\tunsigned char gravitydir[2];\t//pitch/yaw, no roll\n\n\t\t\tunsigned short traileffectnum;\n\t\t\tunsigned short emiteffectnum;\n\n\t\t\tvec3_t predorg;\n\t\t} q1;\n\t} u;\n\n\tunsigned short\t\tmodelindex2;\t//q2/vweps\n\tunsigned short\t\tframe;\n\n\tunsigned short\t\tbaseframe;\n\tqbyte\t\t\t\tbasebone;\n\tqbyte\t\t\t\tsolidtype;\n#define EST_FTE\t\t0\t//q2pro/r1q2, also used for fte's replacement deltas etc.\n#define EST_Q2EX\t1\t//q2ex packs it differently.\n#define EST_Q2\t\t2\t//16bit\n\n\tunsigned int solidsize;\n#define ES_SOLID_NOT 0\n#define ES_SOLID_BSP 31\n#define ES_SOLID_HULL1 0x80201810\n#define ES_SOLID_HULL2 0x80401820\n#define ES_SOLID_HAS_EXTRA_BITS(solid) ((solid&0x0707) || (((solid>>16)-32768+32) & 7))\t//needs to be 32bit.\n\n\tunsigned int\t\tskinnum; /*for q2 this often contains rgba*/\n\n\tunsigned short\t\tcolormap;\n\tqbyte glowsize;\n\tqbyte glowcolour;\n\n\tqbyte\tscale;\t//4.4 precision\n\tchar\tfatness; //1/16th\n\tqbyte\thexen2flags;\n\tqbyte\tabslight;\n\n\tqbyte\tdpflags;\n\tqbyte\tcolormod[3];//3.5 precision\n\n\tqbyte\tglowmod[3];\t//3.5 precision\n\tqbyte\ttrans;\t//254==1, 255==1-or-wateralpha\n\n\tunsigned short light[4];\n\n\tfloat lerpend;\t//fitz rubbish\n\n\tqbyte lightstyle;\n\tqbyte lightpflags;\n\tunsigned short tagindex;\t//~0 == weird portal thing.\n\n\tunsigned int tagentity;\n} entity_state_t;\nextern entity_state_t nullentitystate;\n\n\n#define MAX_EXTENDED_PACKET_ENTITIES\t256\t//sanity limit.\n#define\tMAX_STANDARD_PACKET_ENTITIES\t64\t// doesn't count nails\n#define\tMAX_MVDPACKET_ENTITIES\t196\t// doesn't count nails\ntypedef struct\n{\n\tfloat\t\t\tservertime;\n\tint\t\t\t\tnum_entities;\n\tint\t\t\t\tmax_entities;\n\tentity_state_t\t*entities;\n\tint\t\t\t\tfixangles[MAX_SPLITS];\t//these should not be in here\n\tvec3_t\t\t\tfixedangles[MAX_SPLITS];\n\tvec3_t\t\t\tpunchangle[MAX_SPLITS];\n\tvec3_t\t\t\tpunchorigin[MAX_SPLITS];\n\n\tqbyte\t\t\t*bonedata;\n\tsize_t\t\t\tbonedatacur;\n\tsize_t\t\t\tbonedatamax;\n} packet_entities_t;\n\nstruct vrdevinfo_s\n{\n\tunsigned int\tstatus;\n#define VRSTATUS_ORG\t(1u<<0)\n#define VRSTATUS_ANG\t(1u<<1)\n#define VRSTATUS_VEL\t(1u<<2)\n#define VRSTATUS_AVEL\t(1u<<3)\n\tshort\t\t\tangles[3];\n\tshort\t\t\tavelocity[3];\n\tvec3_t\t\t\torigin;\n\tvec3_t\t\t\tvelocity;\n\tquint64_t\t\tweapon;\n#define VRDEV_LEFT\t0\n#define VRDEV_RIGHT\t1\n#define VRDEV_HEAD\t2\n#define VRDEV_COUNT\t3\n};\ntypedef struct usercmd_s\n{\n\tshort\tangles[3];\n\tsigned int\t\tforwardmove,sidemove,upmove;\n\tquint64_t\timpulse;\n\tunsigned int\tlightlevel;\n\n\tunsigned int\tsequence;\t// just for debugging prints\n\tfloat\tmsec;\t\t//replace msec, but with more precision\n\tquint64_t buttons;\t//replaces buttons, but with more bits.\n\tquint64_t weapon;//q3 has a separate weapon field to supplement impulse.\n\tunsigned int servertime;\t//q3 networks the time in order to calculate msecs\n\tdouble\tfservertime;//used as part of nq msec calcs\n\tdouble\tfclienttime;//not used?\n\n\t//prydon cursor crap\n\tvec2_t\tcursor_screen;\n\tvec3_t\tcursor_start;\n\tvec3_t\tcursor_impact;\n\tunsigned int\tcursor_entitynumber;\n\n\t//vr things\n\tstruct vrdevinfo_s vr[VRDEV_COUNT];\t//left, right, head.\n} usercmd_t;\n\ntypedef struct q2usercmd_s\n{\t//visible to gamecode so can't be changed (and the prediction code)\n\tqbyte\tmsec;\n\tqbyte\tbuttons;\n\tshort\tangles[3];\n\tshort\tforwardmove, sidemove, upmove;\n\tqbyte\timpulse;\n\tqbyte\tlightlevel;\n} q2usercmd_t;\n\ntypedef struct q1usercmd_s\n{\t//as written to qwd demos so can't be changed.\n\tqbyte\tmsec;\n\tvec3_t\tangles;\n\tshort\tforwardmove, sidemove, upmove;\n\tqbyte\tbuttons;\n\tqbyte\timpulse;\n} q1usercmd_t;\n#define SHORT2ANGLE(x) (x) * (360.0/65536)\n#define ANGLE2SHORT(x) (x) * (65536/360.0)\n\n\n\n\n//\n// per-level limits\n//\n#define\tQ2MAX_CLIENTS\t\t\t256\t\t// absolute limit\n#define\tQ2MAX_EDICTS\t\t\t1024\t// must change protocol to increase more\n#define\tQ2MAX_LIGHTSTYLES\t\t256\n#define\tQ2MAX_MODELS\t\t\t256\t\t// these are sent over the net as bytes\n#define\tQ2MAX_SOUNDS\t\t\t256\t\t// so they cannot be blindly increased\n#define\tQ2MAX_IMAGES\t\t\t256\n#define\tQ2MAX_ITEMS\t\t\t\t256\n#define Q2MAX_GENERAL\t\t\t(Q2MAX_CLIENTS*2)\t// general config strings\n\n\n#define\tQ2CS_NAME\t\t\t\t0\n#define\tQ2CS_CDTRACK\t\t\t1\n#define\tQ2CS_SKY\t\t\t\t2\n#define\tQ2CS_SKYAXIS\t\t\t3\t\t// %f %f %f format\n#define\tQ2CS_SKYROTATE\t\t\t4\n#define\tQ2CS_STATUSBAR\t\t\t5\t\t// display program string\n\n#define Q2CS_AIRACCEL\t\t\t29\t\t// air acceleration control\n#define\tQ2CS_MAXCLIENTS\t\t\t30\n#define\tQ2CS_MAPCHECKSUM\t\t31\t\t// for catching cheater maps\n\n#define\tQ2CS_MODELS\t\t\t\t32\n#define\tQ2CS_SOUNDS\t\t\t\t(Q2CS_MODELS\t+Q2MAX_MODELS)\n#define\tQ2CS_IMAGES\t\t\t\t(Q2CS_SOUNDS\t+Q2MAX_SOUNDS)\n#define\tQ2CS_LIGHTS\t\t\t\t(Q2CS_IMAGES\t+Q2MAX_IMAGES)\n#define\tQ2CS_ITEMS\t\t\t\t(Q2CS_LIGHTS\t+Q2MAX_LIGHTSTYLES)\n#define\tQ2CS_PLAYERSKINS\t\t(Q2CS_ITEMS\t\t+Q2MAX_ITEMS)\n#define Q2CS_GENERAL\t\t\t(Q2CS_PLAYERSKINS\t+Q2MAX_CLIENTS)\n#define\tQ2MAX_CONFIGSTRINGS\t\t(Q2CS_GENERAL\t+Q2MAX_GENERAL)\n\n\n#define\tQ2EXMAX_CLIENTS\t\t\tQ2MAX_CLIENTS\t\t// absolute limit\n#define\tQ2EXMAX_EDICTS\t\t\t8192\t// must change protocol to increase more\n#define\tQ2EXMAX_LIGHTSTYLES\t\t256\n#define\tQ2EXMAX_RTLIGHTS\t\t256\n#define\tQ2EXMAX_MODELS\t\t\t8192\t\t// these are sent over the net as bytes\n#define\tQ2EXMAX_SOUNDS\t\t\t2048\t\t// so they cannot be blindly increased\n#define\tQ2EXMAX_IMAGES\t\t\t512\n#define\tQ2EXMAX_ITEMS\t\t\t256\n#define\tQ2EXMAX_WWHEEL\t\t\t32\n#define Q2EXMAX_GENERAL\t\t\t(Q2EXMAX_CLIENTS*2)\t// general config strings\n\n#define\tQ2EXCS_NAME\t\t\t\tQ2CS_NAME\n#define\tQ2EXCS_CDTRACK\t\t\tQ2CS_CDTRACK\n#define\tQ2EXCS_SKY\t\t\t\tQ2CS_SKY\n#define\tQ2EXCS_SKYAXIS\t\t\tQ2CS_SKYAXIS\t\t// %f %f %f format\n#define\tQ2EXCS_SKYROTATE\t\tQ2CS_SKYROTATE\n#define\tQ2EXCS_STATUSBAR\t\tQ2CS_STATUSBAR\t\t// display program string\n#define Q2EXCS_AIRACCEL\t\t\t59\t\t// air acceleration control\n#define\tQ2EXCS_MAXCLIENTS\t\t60\n#define\tQ2EXCS_MAPCHECKSUM\t\t61\t\t// for catching cheater maps\n#define\tQ2EXCS_MODELS\t\t\t62\n#define\tQ2EXCS_SOUNDS\t\t\t(Q2EXCS_MODELS\t\t\t+Q2EXMAX_MODELS)\n#define\tQ2EXCS_IMAGES\t\t\t(Q2EXCS_SOUNDS\t\t\t+Q2EXMAX_SOUNDS)\n#define\tQ2EXCS_LIGHTS\t\t\t(Q2EXCS_IMAGES\t\t\t+Q2EXMAX_IMAGES)\n#define\tQ2EXCS_RTLIGHTS\t\t\t(Q2EXCS_LIGHTS\t\t\t+Q2EXMAX_LIGHTSTYLES)\n#define\tQ2EXCS_ITEMS\t\t\t(Q2EXCS_RTLIGHTS\t\t+Q2EXMAX_RTLIGHTS)\n#define\tQ2EXCS_PLAYERSKINS\t\t(Q2EXCS_ITEMS\t\t\t+Q2EXMAX_ITEMS)\n#define Q2EXCS_GENERAL\t\t\t(Q2EXCS_PLAYERSKINS\t\t+Q2EXMAX_CLIENTS)\n#define\tQ2ECS_WHEEL_WEAPONS\t\t(Q2EXCS_GENERAL\t\t\t+Q2EXMAX_GENERAL)\t//item|icon|ammotype|minammo|powerup|sortid|warnammo|droppable\n#define\tQ2ECS_WHEEL_AMMO\t\t(Q2ECS_WHEEL_WEAPONS\t+Q2EXMAX_WWHEEL)\t//item|icon\n#define\tQ2ECS_WHEEL_POWERUPS\t(Q2ECS_WHEEL_AMMO\t\t+Q2EXMAX_WWHEEL)\t//item|icon|toggled|sortid|droppable|ammotype\n#define\tQ2ECS_CD_LOOP_COUNT\t\t(Q2ECS_WHEEL_POWERUPS\t+Q2EXMAX_WWHEEL)\n#define\tQ2ECS_GAME_STYLE\t\t(Q2ECS_CD_LOOP_COUNT\t+1)\n#define\tQ2EXMAX_CONFIGSTRINGS\t(Q2ECS_GAME_STYLE\t\t+1)\n\n// player_state->stats[] indexes\n#define Q2STAT_HEALTH_ICON\t\t0\n#define\tQ2STAT_HEALTH\t\t\t\t1\n#define\tQ2STAT_AMMO_ICON\t\t\t2\n#define\tQ2STAT_AMMO\t\t\t\t3\n#define\tQ2STAT_ARMOR_ICON\t\t\t4\n#define\tQ2STAT_ARMOR\t\t\t\t5\n#define\tQ2STAT_SELECTED_ICON\t\t6\n#define\tQ2STAT_PICKUP_ICON\t\t7\n#define\tQ2STAT_PICKUP_STRING\t\t8\n#define\tQ2STAT_TIMER_ICON\t\t\t9\n#define\tQ2STAT_TIMER\t\t\t\t10\n#define\tQ2STAT_HELPICON\t\t\t11\n#define\tQ2STAT_SELECTED_ITEM\t\t12\n#define\tQ2STAT_LAYOUTS\t\t\t13\n#define\tQ2STAT_FRAGS\t\t\t\t14\n#define\tQ2STAT_FLASHES\t\t\t15\t\t// cleared each frame, 1 = health, 2 = armor\n#define Q2STAT_CHASE\t\t\t\t16\n#define Q2STAT_SPECTATOR\t\t\t17\n\n#define\tQ2MAX_STATS\t\t\t\t32\n\n\n//for the local player\n#define\tQ2PS_M_TYPE\t\t\t\t(1<<0)\n#define\tQ2PS_M_ORIGIN\t\t\t(1<<1)\n#define\tQ2PS_M_VELOCITY\t\t\t(1<<2)\n#define\tQ2PS_M_TIME\t\t\t\t(1<<3)\n#define\tQ2PS_M_FLAGS\t\t\t(1<<4)\n#define\tQ2PS_M_GRAVITY\t\t\t(1<<5)\n#define\tQ2PS_M_DELTA_ANGLES\t\t(1<<6)\n#define\tQ2PS_VIEWOFFSET\t\t\t(1<<7)\n#define\tQ2PS_VIEWANGLES\t\t\t(1<<8)\n#define\tQ2PS_KICKANGLES\t\t\t(1<<9)\n#define\tQ2PS_BLEND\t\t\t\t(1<<10)\n#define\tQ2PS_FOV\t\t\t\t(1<<11)\n#define\tQ2PS_WEAPONINDEX\t\t(1<<12)\n#define\tQ2PS_WEAPONFRAME\t\t(1<<13)\n#define\tQ2PS_RDFLAGS\t\t\t(1<<14)\n#define\tQ2PS_EXTRABITS\t\t\t(1<<15)\n#define Q2FTEPS_INDEX16\t\t\t(1<<16)\n#define Q2EXPS_DAMAGEBLEND\t\t(1<<16)\n#define Q2FTEPS_CLIENTNUM\t\t(1<<17)\n#define Q2EXPS_TEAMID\t\t\t(1<<17)\n#define Q2PS_UNUSED6\t\t\t(1<<18)\n#define Q2PS_UNUSED5\t\t\t(1<<19)\n#define Q2PS_UNUSED4\t\t\t(1<<20)\n#define Q2PS_UNUSED3\t\t\t(1<<21)\n#define Q2PS_UNUSED2\t\t\t(1<<22)\n#define Q2PS_UNUSED1\t\t\t(1<<23)\n\n#define Q2PSX_GUNOFFSET\t\t\t(1<<0)\n#define Q2PSX_GUNANGLES\t\t\t(1<<1)\n#define Q2PSX_M_VELOCITY2\t\t(1<<2)\n#define Q2PSX_M_ORIGIN2\t\t\t(1<<3)\n#define Q2PSX_VIEWANGLE2\t\t(1<<4)\n#define Q2PSX_STATS\t\t\t\t(1<<5)\n#define Q2PSX_CLIENTNUM\t\t\t(1<<6)\n#define Q2PSX_OLD\t\t\t\t(1<<8)\t//not part of the protocol, just lazy handling.\n\n\n// entity_state_t->renderfx flags\n#define\tQ2RF_MINLIGHT\t\t\t(1u<<0)\t\t//ni\talways have some light (viewmodel)\n#define\tRF_EXTERNALMODEL\t\t(1u<<1)\t\t//i \tdon't draw through eyes, only mirrors\n#define\tRF_WEAPONMODEL\t\t\t(1u<<2)\t\t//i \tonly draw through eyes\n#define\tRF_FULLBRIGHT\t\t\t(1u<<3)\t\t//i \talways draw full intensity\n#define\tRF_DEPTHHACK\t\t\t(1u<<4)\t\t//i \tfor view weapon Z crunching\n#define\tRF_TRANSLUCENT\t\t\t(1u<<5)\t\t//forces shader sort order and BEF_FORCETRANSPARENT\n#define\tQ2RF_FRAMELERP\t\t\t(1u<<6)\t\t//q2only\n#define Q2RF_BEAM\t\t\t\t(1u<<7)\t\t//mostly q2only\n//\n#define\tQ2RF_CUSTOMSKIN\t\t\t(1u<<8)\t\t//not even in q2\t\tskin is an index in image_precache\n#define\tQ2RF_GLOW\t\t\t\t(1u<<9)\t\t//i\t\tpulse lighting for bonus items\n#define Q2RF_SHELL_RED\t\t\t(1u<<10)\t//q2only\n#define\tQ2RF_SHELL_GREEN\t\t(1u<<11)\t//q2only\n#define Q2RF_SHELL_BLUE\t\t\t(1u<<12)\t//q2only\n//\n#define RF_NOSHADOW\t\t\t\t(1u<<13)\n#define Q2REX_CASTSHADOW\t\t(1u<<14)\n//ROGUE start\n#define Q2RF_IR_VISIBLE\t\t\t(1u<<15)\t// shows red with Q2RDF_IRGOGGLES\n#define\tQ2RF_SHELL_DOUBLE\t\t(1u<<16)\t//q2only\n#define\tQ2RF_SHELL_HALF_DAM\t\t(1u<<17)\t//q2only\n#define Q2RF_USE_DISGUISE\t\t(1u<<18)\t//ni\tentity is displayed with skin 'players/$MODEL/disguise.pcx' instead\n//ROGUE end\n//#define Q2EXRF_SHELL_LITE_GREEN\t(1u<<19)\n#define Q2EXRF_CUSTOM_LIGHT\t\t(1u<<20)\n#define Q2EXRF_FLARE\t\t\t(1u<<21)\t//changes the interpretation of a lot of fields, basically replacing the entire ent.\n//#define Q2EXRF_OLD_FRAME_LERP\t(1u<<22)\t//This flag signals that `s.old_frame` should be used for the next frame and respected by the client. This can be used for custom frame interpolation; its use in this engine is specific to fixing interpolation bugs on certain monster animations.\n//#define Q2EXRF_BLOB_SHADOW\t\t(1u<<23)\t//\n//#define Q2EXRF_LOW_PRIORITY\t\t(1u<<24)\t//\n//#define Q2EXRF_NO_LOD\t\t\t\t(1u<<25)\t//\n//#define Q2EXRF_STAIRSTEP\t\t\t(1u<<26)\t//\n\n#define RF_ADDITIVE\t\t\t\t(1u<<27)\t//forces shader sort order and BEF_FORCEADDITIVE\n#define RF_NODEPTHTEST\t\t\t(1u<<28)\t//forces shader sort order and BEF_FORCENODEPTH\n#define RF_FORCECOLOURMOD\t\t(1u<<29)\t//forces BEF_FORCECOLOURMOD\n#define RF_WEAPONMODELNOBOB\t\t(1u<<30)\n#define RF_FIRSTPERSON\t\t\t(1u<<31)\t//only draw through eyes\n#define\tRF_XFLIP\t\t\t\tQ2EXRF_FLARE\t//flip horizontally (for q2's left-handed weapons)\n\n// player_state_t->refdef flags\n#define\tRDF_UNDERWATER\t\t\t(1u<<0)\t\t// warp the screen as apropriate (fov trick)\n#define RDF_NOWORLDMODEL\t\t(1u<<1)\t\t// used for player configuration screen\n//ROGUE\n#define\tQ2RDF_IRGOGGLES\t\t\t(1u<<2)\t\t//ents with Q2RF_IR_VISIBLE show up pure red.\n#define Q2RDF_UVGOGGLES\t\t\t(1u<<3)\t\t//usused / reserved\n//ROGUE\n\n#define RDF_BLOOM\t\t\t\t(1u<<16)\n#define RDF_FISHEYE\t\t\t\t(1u<<17)\n#define RDF_WATERWARP\t\t\t(1u<<18)\n#define RDF_CUSTOMPOSTPROC\t\t(1u<<19)\n#define RDF_ANTIALIAS\t\t\t(1u<<20)\t//fxaa, or possibly even just fsaa\n#define RDF_RENDERSCALE\t\t\t(1u<<21)\n#define RDF_SCENEGAMMA\t\t\t(1u<<22)\n#define RDF_DISABLEPARTICLES\t(1u<<23)\t//mostly for skyrooms\n#define RDF_SKIPSKY\t\t\t\t(1u<<24)\t//we drew a skyroom, skip drawing sky chains for this scene.\n#define RDF_SKYROOMENABLED\t\t(1u<<25)\t//skyroom position is known, be prepared to draw the skyroom if its visible.\n\n#define RDF_ALLPOSTPROC\t\t\t(RDF_BLOOM|RDF_FISHEYE|RDF_WATERWARP|RDF_CUSTOMPOSTPROC|RDF_ANTIALIAS|RDF_SCENEGAMMA)\t//these flags require rendering to an fbo for the various different post-processing shaders.\n\n\n\n\n\n#define\tQ2SND_VOLUME\t\t(1u<<0)\t\t// a qbyte\n#define\tQ2SND_ATTENUATION\t(1u<<1)\t\t// a qbyte\n#define\tQ2SND_POS\t\t\t(1u<<2)\t\t// three coordinates\n#define\tQ2SND_ENT\t\t\t(1u<<3)\t\t// a short 0-2: channel, 3-12: entity\n#define\tQ2SND_OFFSET\t\t(1u<<4)\t\t// a qbyte, msec offset from frame start\n#define Q2SNDFTE_LARGEIDX\t(1u<<5)\t\t// idx is a short\n#define Q2SNDEX_EXPLICITPOS\t(1u<<5)\t\t// ?\n#define Q2SNDEX_LARGEENT\t(1u<<6)\t\t// 32bit index\n#define Q2SND_EXTRABITS\t\t(1u<<7)\t\t// unused for now, reserved.\n\n#define Q2DEFAULT_SOUND_PACKET_VOLUME\t1.0\n#define Q2DEFAULT_SOUND_PACKET_ATTENUATION 1.0\n\n\n#define ATTN_NONE\t0\n#define ATTN_NORM\t1\n#define CHAN_AUTO   0\n#define CHAN_WEAPON 1\n#define CHAN_VOICE  2\n#define CHAN_ITEM   3\n#define CHAN_BODY   4\n\n#define\tQ2MZ_BLASTER\t\t\t0\n#define Q2MZ_MACHINEGUN\t\t1\n#define\tQ2MZ_SHOTGUN\t\t\t2\n#define\tQ2MZ_CHAINGUN1\t\t3\n#define\tQ2MZ_CHAINGUN2\t\t4\n#define\tQ2MZ_CHAINGUN3\t\t5\n#define\tQ2MZ_RAILGUN\t\t\t6\n#define\tQ2MZ_ROCKET\t\t\t7\n#define\tQ2MZ_GRENADE\t\t\t8\n#define\tQ2MZ_LOGIN\t\t\t9\n#define\tQ2MZ_LOGOUT\t\t\t10\n#define\tQ2MZ_RESPAWN\t\t\t11\n#define\tQ2MZ_BFG\t\t\t\t12\n#define\tQ2MZ_SSHOTGUN\t\t\t13\n#define\tQ2MZ_HYPERBLASTER\t\t14\n#define\tQ2MZ_ITEMRESPAWN\t\t15\n// RAFAEL\n#define Q2MZ_IONRIPPER\t\t16\n#define Q2MZ_BLUEHYPERBLASTER 17\n#define Q2MZ_PHALANX\t\t\t18\n#define Q2MZ_SILENCED\t\t\t128\t\t// bit flag ORed with one of the above numbers\n\n//ROGUE\n#define Q2MZ_ETF_RIFLE\t\t30\n//#define Q2MZ_UNUSED\t\t\t31\n#define Q2MZ_SHOTGUN2\t\t\t32\n#define Q2MZ_HEATBEAM\t\t\t33\n#define Q2MZ_BLASTER2\t\t\t34\n#define\tQ2MZ_TRACKER\t\t\t35\n#define\tQ2MZ_NUKE1\t\t\t36\n#define\tQ2MZ_NUKE2\t\t\t37\n#define\tQ2MZ_NUKE4\t\t\t38\n#define\tQ2MZ_NUKE8\t\t\t39\n//ROGUE\n\n#define Q2EXMZ_BFG2\t\t19\n#define Q2EXMZ_PHALANX2\t\t20\n#define Q2EXMZ_PROX\t\t31\n#define Q2EXMZ_ETF_RIFLE_2\t\t32\n\n\n//\n// monster muzzle flashes\n//\n#define Q2MZ2_TANK_BLASTER_1\t\t\t\t1\n#define Q2MZ2_TANK_BLASTER_2\t\t\t\t2\n#define Q2MZ2_TANK_BLASTER_3\t\t\t\t3\n#define Q2MZ2_TANK_MACHINEGUN_1\t\t\t4\n#define Q2MZ2_TANK_MACHINEGUN_2\t\t\t5\n#define Q2MZ2_TANK_MACHINEGUN_3\t\t\t6\n#define Q2MZ2_TANK_MACHINEGUN_4\t\t\t7\n#define Q2MZ2_TANK_MACHINEGUN_5\t\t\t8\n#define Q2MZ2_TANK_MACHINEGUN_6\t\t\t9\n#define Q2MZ2_TANK_MACHINEGUN_7\t\t\t10\n#define Q2MZ2_TANK_MACHINEGUN_8\t\t\t11\n#define Q2MZ2_TANK_MACHINEGUN_9\t\t\t12\n#define Q2MZ2_TANK_MACHINEGUN_10\t\t\t13\n#define Q2MZ2_TANK_MACHINEGUN_11\t\t\t14\n#define Q2MZ2_TANK_MACHINEGUN_12\t\t\t15\n#define Q2MZ2_TANK_MACHINEGUN_13\t\t\t16\n#define Q2MZ2_TANK_MACHINEGUN_14\t\t\t17\n#define Q2MZ2_TANK_MACHINEGUN_15\t\t\t18\n#define Q2MZ2_TANK_MACHINEGUN_16\t\t\t19\n#define Q2MZ2_TANK_MACHINEGUN_17\t\t\t20\n#define Q2MZ2_TANK_MACHINEGUN_18\t\t\t21\n#define Q2MZ2_TANK_MACHINEGUN_19\t\t\t22\n#define Q2MZ2_TANK_ROCKET_1\t\t\t\t23\n#define Q2MZ2_TANK_ROCKET_2\t\t\t\t24\n#define Q2MZ2_TANK_ROCKET_3\t\t\t\t25\n\n#define Q2MZ2_INFANTRY_MACHINEGUN_1\t\t26\n#define Q2MZ2_INFANTRY_MACHINEGUN_2\t\t27\n#define Q2MZ2_INFANTRY_MACHINEGUN_3\t\t28\n#define Q2MZ2_INFANTRY_MACHINEGUN_4\t\t29\n#define Q2MZ2_INFANTRY_MACHINEGUN_5\t\t30\n#define Q2MZ2_INFANTRY_MACHINEGUN_6\t\t31\n#define Q2MZ2_INFANTRY_MACHINEGUN_7\t\t32\n#define Q2MZ2_INFANTRY_MACHINEGUN_8\t\t33\n#define Q2MZ2_INFANTRY_MACHINEGUN_9\t\t34\n#define Q2MZ2_INFANTRY_MACHINEGUN_10\t\t35\n#define Q2MZ2_INFANTRY_MACHINEGUN_11\t\t36\n#define Q2MZ2_INFANTRY_MACHINEGUN_12\t\t37\n#define Q2MZ2_INFANTRY_MACHINEGUN_13\t\t38\n\n#define Q2MZ2_SOLDIER_BLASTER_1\t\t\t39\n#define Q2MZ2_SOLDIER_BLASTER_2\t\t\t40\n#define Q2MZ2_SOLDIER_SHOTGUN_1\t\t\t41\n#define Q2MZ2_SOLDIER_SHOTGUN_2\t\t\t42\n#define Q2MZ2_SOLDIER_MACHINEGUN_1\t\t43\n#define Q2MZ2_SOLDIER_MACHINEGUN_2\t\t44\n\n#define Q2MZ2_GUNNER_MACHINEGUN_1\t\t\t45\n#define Q2MZ2_GUNNER_MACHINEGUN_2\t\t\t46\n#define Q2MZ2_GUNNER_MACHINEGUN_3\t\t\t47\n#define Q2MZ2_GUNNER_MACHINEGUN_4\t\t\t48\n#define Q2MZ2_GUNNER_MACHINEGUN_5\t\t\t49\n#define Q2MZ2_GUNNER_MACHINEGUN_6\t\t\t50\n#define Q2MZ2_GUNNER_MACHINEGUN_7\t\t\t51\n#define Q2MZ2_GUNNER_MACHINEGUN_8\t\t\t52\n#define Q2MZ2_GUNNER_GRENADE_1\t\t\t53\n#define Q2MZ2_GUNNER_GRENADE_2\t\t\t54\n#define Q2MZ2_GUNNER_GRENADE_3\t\t\t55\n#define Q2MZ2_GUNNER_GRENADE_4\t\t\t56\n\n#define Q2MZ2_CHICK_ROCKET_1\t\t\t\t57\n\n#define Q2MZ2_FLYER_BLASTER_1\t\t\t\t58\n#define Q2MZ2_FLYER_BLASTER_2\t\t\t\t59\n\n#define Q2MZ2_MEDIC_BLASTER_1\t\t\t\t60\n\n#define Q2MZ2_GLADIATOR_RAILGUN_1\t\t\t61\n\n#define Q2MZ2_HOVER_BLASTER_1\t\t\t\t62\n\n#define Q2MZ2_ACTOR_MACHINEGUN_1\t\t\t63\n\n#define Q2MZ2_SUPERTANK_MACHINEGUN_1\t\t64\n#define Q2MZ2_SUPERTANK_MACHINEGUN_2\t\t65\n#define Q2MZ2_SUPERTANK_MACHINEGUN_3\t\t66\n#define Q2MZ2_SUPERTANK_MACHINEGUN_4\t\t67\n#define Q2MZ2_SUPERTANK_MACHINEGUN_5\t\t68\n#define Q2MZ2_SUPERTANK_MACHINEGUN_6\t\t69\n#define Q2MZ2_SUPERTANK_ROCKET_1\t\t\t70\n#define Q2MZ2_SUPERTANK_ROCKET_2\t\t\t71\n#define Q2MZ2_SUPERTANK_ROCKET_3\t\t\t72\n\n#define Q2MZ2_BOSS2_MACHINEGUN_L1\t\t\t73\n#define Q2MZ2_BOSS2_MACHINEGUN_L2\t\t\t74\n#define Q2MZ2_BOSS2_MACHINEGUN_L3\t\t\t75\n#define Q2MZ2_BOSS2_MACHINEGUN_L4\t\t\t76\n#define Q2MZ2_BOSS2_MACHINEGUN_L5\t\t\t77\n#define Q2MZ2_BOSS2_ROCKET_1\t\t\t\t78\n#define Q2MZ2_BOSS2_ROCKET_2\t\t\t\t79\n#define Q2MZ2_BOSS2_ROCKET_3\t\t\t\t80\n#define Q2MZ2_BOSS2_ROCKET_4\t\t\t\t81\n\n#define Q2MZ2_FLOAT_BLASTER_1\t\t\t\t82\n\n#define Q2MZ2_SOLDIER_BLASTER_3\t\t\t83\n#define Q2MZ2_SOLDIER_SHOTGUN_3\t\t\t84\n#define Q2MZ2_SOLDIER_MACHINEGUN_3\t\t85\n#define Q2MZ2_SOLDIER_BLASTER_4\t\t\t86\n#define Q2MZ2_SOLDIER_SHOTGUN_4\t\t\t87\n#define Q2MZ2_SOLDIER_MACHINEGUN_4\t\t88\n#define Q2MZ2_SOLDIER_BLASTER_5\t\t\t89\n#define Q2MZ2_SOLDIER_SHOTGUN_5\t\t\t90\n#define Q2MZ2_SOLDIER_MACHINEGUN_5\t\t91\n#define Q2MZ2_SOLDIER_BLASTER_6\t\t\t92\n#define Q2MZ2_SOLDIER_SHOTGUN_6\t\t\t93\n#define Q2MZ2_SOLDIER_MACHINEGUN_6\t\t94\n#define Q2MZ2_SOLDIER_BLASTER_7\t\t\t95\n#define Q2MZ2_SOLDIER_SHOTGUN_7\t\t\t96\n#define Q2MZ2_SOLDIER_MACHINEGUN_7\t\t97\n#define Q2MZ2_SOLDIER_BLASTER_8\t\t\t98\n#define Q2MZ2_SOLDIER_SHOTGUN_8\t\t\t99\n#define Q2MZ2_SOLDIER_MACHINEGUN_8\t\t100\n\n// --- Xian shit below ---\n#define\tQ2MZ2_MAKRON_BFG\t\t\t\t\t101\n#define Q2MZ2_MAKRON_BLASTER_1\t\t\t102\n#define Q2MZ2_MAKRON_BLASTER_2\t\t\t103\n#define Q2MZ2_MAKRON_BLASTER_3\t\t\t104\n#define Q2MZ2_MAKRON_BLASTER_4\t\t\t105\n#define Q2MZ2_MAKRON_BLASTER_5\t\t\t106\n#define Q2MZ2_MAKRON_BLASTER_6\t\t\t107\n#define Q2MZ2_MAKRON_BLASTER_7\t\t\t108\n#define Q2MZ2_MAKRON_BLASTER_8\t\t\t109\n#define Q2MZ2_MAKRON_BLASTER_9\t\t\t110\n#define Q2MZ2_MAKRON_BLASTER_10\t\t\t111\n#define Q2MZ2_MAKRON_BLASTER_11\t\t\t112\n#define Q2MZ2_MAKRON_BLASTER_12\t\t\t113\n#define Q2MZ2_MAKRON_BLASTER_13\t\t\t114\n#define Q2MZ2_MAKRON_BLASTER_14\t\t\t115\n#define Q2MZ2_MAKRON_BLASTER_15\t\t\t116\n#define Q2MZ2_MAKRON_BLASTER_16\t\t\t117\n#define Q2MZ2_MAKRON_BLASTER_17\t\t\t118\n#define Q2MZ2_MAKRON_RAILGUN_1\t\t\t119\n#define\tQ2MZ2_JORG_MACHINEGUN_L1\t\t\t120\n#define\tQ2MZ2_JORG_MACHINEGUN_L2\t\t\t121\n#define\tQ2MZ2_JORG_MACHINEGUN_L3\t\t\t122\n#define\tQ2MZ2_JORG_MACHINEGUN_L4\t\t\t123\n#define\tQ2MZ2_JORG_MACHINEGUN_L5\t\t\t124\n#define\tQ2MZ2_JORG_MACHINEGUN_L6\t\t\t125\n#define\tQ2MZ2_JORG_MACHINEGUN_R1\t\t\t126\n#define\tQ2MZ2_JORG_MACHINEGUN_R2\t\t\t127\n#define\tQ2MZ2_JORG_MACHINEGUN_R3\t\t\t128\n#define\tQ2MZ2_JORG_MACHINEGUN_R4\t\t\t129\n#define Q2MZ2_JORG_MACHINEGUN_R5\t\t\t130\n#define\tQ2MZ2_JORG_MACHINEGUN_R6\t\t\t131\n#define Q2MZ2_JORG_BFG_1\t\t\t\t\t132\n#define Q2MZ2_BOSS2_MACHINEGUN_R1\t\t\t133\n#define Q2MZ2_BOSS2_MACHINEGUN_R2\t\t\t134\n#define Q2MZ2_BOSS2_MACHINEGUN_R3\t\t\t135\n#define Q2MZ2_BOSS2_MACHINEGUN_R4\t\t\t136\n#define Q2MZ2_BOSS2_MACHINEGUN_R5\t\t\t137\n\n//ROGUE\n#define\tQ2MZ2_CARRIER_MACHINEGUN_L1\t\t138\n#define\tQ2MZ2_CARRIER_MACHINEGUN_R1\t\t139\n#define\tQ2MZ2_CARRIER_GRENADE\t\t\t\t140\n#define Q2MZ2_TURRET_MACHINEGUN\t\t\t141\n#define Q2MZ2_TURRET_ROCKET\t\t\t\t142\n#define Q2MZ2_TURRET_BLASTER\t\t\t\t143\n#define Q2MZ2_STALKER_BLASTER\t\t\t\t144\n#define Q2MZ2_DAEDALUS_BLASTER\t\t\t145\n#define Q2MZ2_MEDIC_BLASTER_2\t\t\t\t146\n#define\tQ2MZ2_CARRIER_RAILGUN\t\t\t\t147\n#define\tQ2MZ2_WIDOW_DISRUPTOR\t\t\t\t148\n#define\tQ2MZ2_WIDOW_BLASTER\t\t\t\t149\n#define\tQ2MZ2_WIDOW_RAIL\t\t\t\t\t150\n#define\tQ2MZ2_WIDOW_PLASMABEAM\t\t\t151\t\t// PMM - not used\n#define\tQ2MZ2_CARRIER_MACHINEGUN_L2\t\t152\n#define\tQ2MZ2_CARRIER_MACHINEGUN_R2\t\t153\n#define\tQ2MZ2_WIDOW_RAIL_LEFT\t\t\t\t154\n#define\tQ2MZ2_WIDOW_RAIL_RIGHT\t\t\t155\n#define\tQ2MZ2_WIDOW_BLASTER_SWEEP1\t\t156\n#define\tQ2MZ2_WIDOW_BLASTER_SWEEP2\t\t157\n#define\tQ2MZ2_WIDOW_BLASTER_SWEEP3\t\t158\n#define\tQ2MZ2_WIDOW_BLASTER_SWEEP4\t\t159\n#define\tQ2MZ2_WIDOW_BLASTER_SWEEP5\t\t160\n#define\tQ2MZ2_WIDOW_BLASTER_SWEEP6\t\t161\n#define\tQ2MZ2_WIDOW_BLASTER_SWEEP7\t\t162\n#define\tQ2MZ2_WIDOW_BLASTER_SWEEP8\t\t163\n#define\tQ2MZ2_WIDOW_BLASTER_SWEEP9\t\t164\n#define\tQ2MZ2_WIDOW_BLASTER_100\t\t\t165\n#define\tQ2MZ2_WIDOW_BLASTER_90\t\t\t166\n#define\tQ2MZ2_WIDOW_BLASTER_80\t\t\t167\n#define\tQ2MZ2_WIDOW_BLASTER_70\t\t\t168\n#define\tQ2MZ2_WIDOW_BLASTER_60\t\t\t169\n#define\tQ2MZ2_WIDOW_BLASTER_50\t\t\t170\n#define\tQ2MZ2_WIDOW_BLASTER_40\t\t\t171\n#define\tQ2MZ2_WIDOW_BLASTER_30\t\t\t172\n#define\tQ2MZ2_WIDOW_BLASTER_20\t\t\t173\n#define\tQ2MZ2_WIDOW_BLASTER_10\t\t\t174\n#define\tQ2MZ2_WIDOW_BLASTER_0\t\t\t\t175\n#define\tQ2MZ2_WIDOW_BLASTER_10L\t\t\t176\n#define\tQ2MZ2_WIDOW_BLASTER_20L\t\t\t177\n#define\tQ2MZ2_WIDOW_BLASTER_30L\t\t\t178\n#define\tQ2MZ2_WIDOW_BLASTER_40L\t\t\t179\n#define\tQ2MZ2_WIDOW_BLASTER_50L\t\t\t180\n#define\tQ2MZ2_WIDOW_BLASTER_60L\t\t\t181\n#define\tQ2MZ2_WIDOW_BLASTER_70L\t\t\t182\n#define\tQ2MZ2_WIDOW_RUN_1\t\t\t\t\t183\n#define\tQ2MZ2_WIDOW_RUN_2\t\t\t\t\t184\n#define\tQ2MZ2_WIDOW_RUN_3\t\t\t\t\t185\n#define\tQ2MZ2_WIDOW_RUN_4\t\t\t\t\t186\n#define\tQ2MZ2_WIDOW_RUN_5\t\t\t\t\t187\n#define\tQ2MZ2_WIDOW_RUN_6\t\t\t\t\t188\n#define\tQ2MZ2_WIDOW_RUN_7\t\t\t\t\t189\n#define\tQ2MZ2_WIDOW_RUN_8\t\t\t\t\t190\n#define\tQ2MZ2_CARRIER_ROCKET_1\t\t\t191\n#define\tQ2MZ2_CARRIER_ROCKET_2\t\t\t192\n#define\tQ2MZ2_CARRIER_ROCKET_3\t\t\t193\n#define\tQ2MZ2_CARRIER_ROCKET_4\t\t\t194\n#define\tQ2MZ2_WIDOW2_BEAMER_1\t\t\t\t195\n#define\tQ2MZ2_WIDOW2_BEAMER_2\t\t\t\t196\n#define\tQ2MZ2_WIDOW2_BEAMER_3\t\t\t\t197\n#define\tQ2MZ2_WIDOW2_BEAMER_4\t\t\t\t198\n#define\tQ2MZ2_WIDOW2_BEAMER_5\t\t\t\t199\n#define\tQ2MZ2_WIDOW2_BEAM_SWEEP_1\t\t\t200\n#define\tQ2MZ2_WIDOW2_BEAM_SWEEP_2\t\t\t201\n#define\tQ2MZ2_WIDOW2_BEAM_SWEEP_3\t\t\t202\n#define\tQ2MZ2_WIDOW2_BEAM_SWEEP_4\t\t\t203\n#define\tQ2MZ2_WIDOW2_BEAM_SWEEP_5\t\t\t204\n#define\tQ2MZ2_WIDOW2_BEAM_SWEEP_6\t\t\t205\n#define\tQ2MZ2_WIDOW2_BEAM_SWEEP_7\t\t\t206\n#define\tQ2MZ2_WIDOW2_BEAM_SWEEP_8\t\t\t207\n#define\tQ2MZ2_WIDOW2_BEAM_SWEEP_9\t\t\t208\n#define\tQ2MZ2_WIDOW2_BEAM_SWEEP_10\t\t209\n#define\tQ2MZ2_WIDOW2_BEAM_SWEEP_11\t\t210\n\n\n\n\n\n\n#define MAX_MAP_AREA_BYTES\t\t32\n\n// edict->drawflags (hexen2 stuff)\n#define MLS_MASK\t\t\t\t7\n#define MLS_NONE\t\t\t\t0\n#define MLS_LIGHTSTYLE25\t\t1\t//indexes style 25 instead of using real lighting info\n#define MLS_LIGHTSTYLE26\t\t2\t//indexes style 26\n#define MLS_LIGHTSTYLE27\t\t3\t//27\n#define MLS_LIGHTSTYLE28\t\t4\t//...\n#define MLS_LIGHTSTYLE29\t\t5\t//duh\n#define MLS_ADDLIGHT\t\t\t6\t//adds abslight to normal lighting\n#define MLS_ABSLIGHT\t\t\t(MLS_MASK)\t//uses abslight specifically.\n#define SCALE_TYPE_MASK\t\t\t(SCALE_TYPE_UNIFORM|SCALE_TYPE_XYONLY|SCALE_TYPE_ZONLY)\n#define SCALE_TYPE_UNIFORM\t\t0\t// Scale X, Y, and Z\n#define SCALE_TYPE_XYONLY\t\t8\t// Scale X and Y\n#define SCALE_TYPE_ZONLY\t\t16\t// Scale Z\n#define SCALE_TYPE_UNUSED\t\t(SCALE_TYPE_XYONLY|SCALE_TYPE_ZONLY)\n#define SCALE_ORIGIN_MASK\t\t(SCALE_ORIGIN_TOP|SCALE_ORIGIN_BOTTOM|SCALE_ORIGIN_CENTER)\n#define SCALE_ORIGIN_CENTER\t\t0\t// Scaling origin at object center\n#define SCALE_ORIGIN_BOTTOM\t\t32\t// Scaling origin at object bottom\n#define SCALE_ORIGIN_TOP\t\t64\t// Scaling origin at object top\n#define SCALE_ORIGIN_ORIGIN\t\t(SCALE_ORIGIN_TOP|SCALE_ORIGIN_BOTTOM)\t// Scaling origin at object origin\n#define DRF_TRANSLUCENT\t\t\t128\t//alpha is controlled by r_wateralpha\n\n\n//TENEBRAE_GFX_DLIGHTS\n#define PFLAGS_NOSHADOW\t\t1\n#define PFLAGS_CORONA\t\t2\n#define PFLAGS_FULLDYNAMIC\t128\t//NOTE: this is a dp-ism. for tenebrae compat, this should be effects&16 and not pflags&128, as effects&16 already means something else\n\n#define RENDER_STEP 1\n#define RENDER_GLOWTRAIL 2\n#define RENDER_VIEWMODEL 4\n#define RENDER_EXTERIORMODEL 8\n#define RENDER_LOWPRECISION 16 // send as low precision coordinates to save bandwidth\n#define RENDER_COLORMAPPED 32\t//networked colormap field is a direct (top<<4)|bottom value rather than a player slot (the |1024 thing d does)\n//#define RENDER_WORLDOBJECT 64\n#define RENDER_COMPLEXANIMATION 128\n\n//darkplaces protocols 5 to 7 use these\n// reset all entity fields (typically used if status changed)\n#define E5_FULLUPDATE (1<<0)\n// E5_ORIGIN32=0: short[3] = s->origin[0] * 8, s->origin[1] * 8, s->origin[2] * 8\n// E5_ORIGIN32=1: float[3] = s->origin[0], s->origin[1], s->origin[2]\n#define E5_ORIGIN (1<<1)\n// E5_ANGLES16=0: byte[3] = s->angle[0] * 256 / 360, s->angle[1] * 256 / 360, s->angle[2] * 256 / 360\n// E5_ANGLES16=1: short[3] = s->angle[0] * 65536 / 360, s->angle[1] * 65536 / 360, s->angle[2] * 65536 / 360\n#define E5_ANGLES (1<<2)\n// E5_MODEL16=0: byte = s->modelindex\n// E5_MODEL16=1: short = s->modelindex\n#define E5_MODEL (1<<3)\n// E5_FRAME16=0: byte = s->frame\n// E5_FRAME16=1: short = s->frame\n#define E5_FRAME (1<<4)\n// byte = s->skin\n#define E5_SKIN (1<<5)\n// E5_EFFECTS16=0 && E5_EFFECTS32=0: byte = s->effects\n// E5_EFFECTS16=1 && E5_EFFECTS32=0: short = s->effects\n// E5_EFFECTS16=0 && E5_EFFECTS32=1: int = s->effects\n// E5_EFFECTS16=1 && E5_EFFECTS32=1: int = s->effects\n#define E5_EFFECTS (1<<6)\n// bits >= (1<<8)\n#define E5_EXTEND1 (1<<7)\n\n// byte = s->renderflags\n#define E5_FLAGS (1<<8)\n// byte = bound(0, s->alpha * 255, 255)\n#define E5_ALPHA (1<<9)\n// byte = bound(0, s->scale * 16, 255)\n#define E5_SCALE (1<<10)\n// flag\n#define E5_ORIGIN32 (1<<11)\n// flag\n#define E5_ANGLES16 (1<<12)\n// flag\n#define E5_MODEL16 (1<<13)\n// byte = s->colormap\n#define E5_COLORMAP (1<<14)\n// bits >= (1<<16)\n#define E5_EXTEND2 (1<<15)\n\n// short = s->tagentity\n// byte = s->tagindex\n#define E5_ATTACHMENT (1<<16)\n// short[4] = s->light[0], s->light[1], s->light[2], s->light[3]\n// byte = s->lightstyle\n// byte = s->lightpflags\n#define E5_LIGHT (1<<17)\n// byte = s->glowsize\n// byte = s->glowcolor\n#define E5_GLOW (1<<18)\n// short = s->effects\n#define E5_EFFECTS16 (1<<19)\n// int = s->effects\n#define E5_EFFECTS32 (1<<20)\n// flag\n#define E5_FRAME16 (1<<21)\n// unused\n#define E5_COLORMOD (1<<22)\n// bits >= (1<<24)\n#define E5_EXTEND3 (1<<23)\n\n// unused\n#define E5_GLOWMOD (1<<24)\n// unused\n#define E5_COMPLEXANIMATION (1<<25)\n// unused\n#define E5_TRAILEFFECTNUM (1<<26)\n// unused\n#define E5_UNUSED27 (1<<27)\n// unused\n#define E5_UNUSED28 (1<<28)\n// unused\n#define E5_UNUSED29 (1<<29)\n// unused\n#define E5_UNUSED30 (1<<30)\n// bits2 > 0\n#define E5_EXTEND4 (1u<<31)\n\n#define E5_ALLUNUSED (E5_UNUSED27|E5_UNUSED28|E5_UNUSED29|E5_UNUSED30)\n#define E5_SERVERPRIVATE (E5_EXTEND1|E5_EXTEND2|E5_EXTEND3|E5_EXTEND4)\n#define E5_SERVERREMOVE E5_EXTEND1\n"
  },
  {
    "path": "engine/common/q1bsp.c",
    "content": "#include \"quakedef.h\"\n\n#include \"pr_common.h\"\n\n#include \"shader.h\"\n#include \"com_bih.h\"\n\nextern cvar_t r_decal_noperpendicular;\nextern cvar_t mod_loadsurfenvmaps;\nextern cvar_t mod_loadmappackages;\n\n/*\nDecal functions\n*/\n\n#define MAXFRAGMENTVERTS 360\nint Fragment_ClipPolyToPlane(float *inverts, float *outverts, int incount, float *plane, float planedist)\n{\n#define C (sizeof(vecV_t)/sizeof(vec_t))\n\tfloat dotv[MAXFRAGMENTVERTS+1];\n\tchar keep[MAXFRAGMENTVERTS+1];\n#define KEEP_KILL 0\n#define KEEP_KEEP 1\n#define KEEP_BORDER 2\n\tint i;\n\tint outcount = 0;\n\tint clippedcount = 0;\n\tfloat d, *p1, *p2, *out;\n#define FRAG_EPSILON (1.0/32) //0.5\n\n\tfor (i = 0; i < incount; i++)\n\t{\n\t\tdotv[i] = DotProduct((inverts+i*C), plane) - planedist;\n\t\tif (dotv[i]<-FRAG_EPSILON)\n\t\t{\n\t\t\tkeep[i] = KEEP_KILL;\n\t\t\tclippedcount++;\n\t\t}\n\t\telse if (dotv[i] > FRAG_EPSILON)\n\t\t\tkeep[i] = KEEP_KEEP;\n\t\telse\n\t\t\tkeep[i] = KEEP_BORDER;\n\t}\n\tdotv[i] = dotv[0];\n\tkeep[i] = keep[0];\n\n\tif (clippedcount == incount)\n\t\treturn 0;\t//all were clipped\n\tif (clippedcount == 0)\n\t{\t//none were clipped\n\t\tfor (i = 0; i < incount; i++)\n\t\t\tVectorCopy((inverts+i*C), (outverts+i*C));\n\t\treturn incount;\n\t}\n\n\tfor (i = 0; i < incount; i++)\n\t{\n\t\tp1 = inverts+i*C;\n\t\tif (keep[i] == KEEP_BORDER)\n\t\t{\n\t\t\tout = outverts+outcount++*C;\n\t\t\tVectorCopy(p1, out);\n\t\t\tcontinue;\n\t\t}\n\t\tif (keep[i] == KEEP_KEEP)\n\t\t{\n\t\t\tout = outverts+outcount++*C;\n\t\t\tVectorCopy(p1, out);\n\t\t}\n\t\tif (keep[i+1] == KEEP_BORDER || keep[i] == keep[i+1])\n\t\t\tcontinue;\n\t\tp2 = inverts+((i+1)%incount)*C;\n\t\td = dotv[i] - dotv[i+1];\n\t\tif (d)\n\t\t\td = dotv[i] / d;\n\n\t\tout = outverts+outcount++*C;\n\t\tVectorInterpolate(p1, d, p2, out);\n\t}\n\treturn outcount;\n}\n\n//the plane itself must be a vec4_t, but can have other data packed between\nsize_t Fragment_ClipPlaneToBrush(vecV_t *points, size_t maxpoints, void *planes, size_t planestride, size_t numplanes, vec4_t face)\n{\n\tint p, a;\n\tvecV_t verts[MAXFRAGMENTVERTS];\n\tvecV_t verts2[MAXFRAGMENTVERTS];\n\tvecV_t *cverts;\n\tint flip;\n//\tvec3_t d1, d2, n;\n\tsize_t numverts;\n\n\t//generate some huge quad/poly aligned with the plane\n\tvec3_t tmp;\n\tvec3_t right, forward;\n\tdouble t;\n\tfloat *plane;\n\n//\tif (face[2] != 1)\n//\t\treturn 0;\n\n\tt = fabs(face[2]);\n\tif (t > fabs(face[0]) && t > fabs(face[1]))\n\t\tVectorSet(tmp, 1, 0, 0);\n\telse\n\t\tVectorSet(tmp, 0, 0, 1);\n\n\tCrossProduct(face, tmp, right);\n\tVectorNormalize(right);\n\tCrossProduct(face, right, forward);\n\tVectorNormalize(forward);\n\n\tVectorScale(face, face[3],\t\t\tverts[0]);\n\tVectorMA(verts[0], 32767, right,\t\tverts[0]);\n\tVectorMA(verts[0], 32767, forward,\tverts[0]);\n\n\tVectorScale(face, face[3],\t\t\tverts[1]);\n\tVectorMA(verts[1], 32767, right,\t\tverts[1]);\n\tVectorMA(verts[1], -32767, forward,\tverts[1]);\n\n\tVectorScale(face, face[3],\t\t\tverts[2]);\n\tVectorMA(verts[2], -32767, right,\tverts[2]);\n\tVectorMA(verts[2], -32767, forward,\tverts[2]);\n\n\tVectorScale(face, face[3],\t\t\tverts[3]);\n\tVectorMA(verts[3], -32767, right,\tverts[3]);\n\tVectorMA(verts[3], 32767, forward,\tverts[3]);\n\n\tnumverts = 4;\n\n\n\t//clip the quad to the various other planes\n\tflip = 0;\n\tfor (p = 0; p < numplanes; p++)\n\t{\n\t\tplane = (float*)((qbyte*)planes + p*planestride);\n\t\tif (plane != face)\n\t\t{\n\t\t\tvec3_t norm;\n\t\t\tflip^=1;\n\t\t\tVectorNegate(plane, norm);\n\t\t\tif (flip)\n\t\t\t\tnumverts = Fragment_ClipPolyToPlane((float*)verts, (float*)verts2, numverts, norm, -plane[3]);\n\t\t\telse\n\t\t\t\tnumverts = Fragment_ClipPolyToPlane((float*)verts2, (float*)verts, numverts, norm, -plane[3]);\n\n\t\t\tif (numverts < 3)\t//totally clipped.\n\t\t\t\treturn 0;\n\t\t}\n\t}\n\n\tif (numverts > maxpoints)\n\t\treturn 0;\n\n\tif (flip)\n\t\tcverts = verts2;\n\telse\n\t\tcverts = verts;\n\tfor (p = 0; p < numverts; p++)\n\t{\n\t\tfor (a = 0; a < 3; a++)\n\t\t{\n\t\t\tfloat f = cverts[p][a];\n\t\t\tint rounded = floor(f + 0.5);\n\t\t\t//if its within 1/1000th of a qu, just round it.\n\t\t\tif (fabs(f - rounded) < 0.001)\n\t\t\t\tpoints[p][a] = rounded;\n\t\t\telse\n\t\t\t\tpoints[p][a] = f;\n\t\t}\n\t}\n\n\treturn numverts;\n}\n\n#ifdef HAVE_CLIENT\n\n#define MAXFRAGMENTTRIS 256\nvec3_t decalfragmentverts[MAXFRAGMENTTRIS*3];\n\nstruct fragmentdecal_s\n{\n\tvec3_t center;\n\n\tvec3_t normal;\n//\tvec3_t tangent1;\n//\tvec3_t tangent2;\n\n\tvec3_t planenorm[6];\n\tfloat planedist[6];\n\tint numplanes;\n\n\tvec_t radius;\n\n\t//will only appear on surfaces with the matching surfaceflag\n\tunsigned int surfflagmask;\n\tunsigned int surfflagmatch;\n\n\tvoid (*callback)(void *ctx, vec3_t *fte_restrict points, size_t numpoints, shader_t *shader);\n\tvoid *ctx;\n};\n\n//#define SHOWCLIPS\n//#define FRAGMENTASTRIANGLES\t//works, but produces more fragments.\n\n#ifdef FRAGMENTASTRIANGLES\n\n//if the triangle is clipped away, go recursive if there are tris left.\nstatic void Fragment_ClipTriToPlane(int trinum, float *plane, float planedist, fragmentdecal_t *dec)\n{\n\tfloat *point[3];\n\tfloat dotv[3];\n\n\tvec3_t impact1, impact2;\n\tfloat t;\n\n\tint i, i2, i3;\n\tint clippedverts = 0;\n\n\tfor (i = 0; i < 3; i++)\n\t{\n\t\tpoint[i] = decalfragmentverts[trinum*3+i];\n\t\tdotv[i] = DotProduct(point[i], plane)-planedist;\n\t\tclippedverts += dotv[i] < 0;\n\t}\n\n\t//if they're all clipped away, scrap the tri\n\tswitch (clippedverts)\n\t{\n\tcase 0:\n\t\treturn;\t//plane does not clip the triangle.\n\n\tcase 1:\t//split into 3, disregard the clipped vert\n\t\tfor (i = 0; i < 3; i++)\n\t\t{\n\t\t\tif (dotv[i] < 0)\n\t\t\t{\t//This is the vertex that's getting clipped.\n\n\t\t\t\tif (dotv[i] > -DIST_EPSILON)\n\t\t\t\t\treturn;\t//it's only over the line by a tiny ammount.\n\n\t\t\t\ti2 = (i+1)%3;\n\t\t\t\ti3 = (i+2)%3;\n\n\t\t\t\tif (dotv[i2] < DIST_EPSILON)\n\t\t\t\t\treturn;\n\t\t\t\tif (dotv[i3] < DIST_EPSILON)\n\t\t\t\t\treturn;\n\n\t\t\t\t//work out where the two lines impact the plane\n\t\t\t\tt = (dotv[i]) / (dotv[i]-dotv[i2]);\n\t\t\t\tVectorInterpolate(point[i], t, point[i2], impact1);\n\n\t\t\t\tt = (dotv[i]) / (dotv[i]-dotv[i3]);\n\t\t\t\tVectorInterpolate(point[i], t, point[i3], impact2);\n\n#ifdef SHOWCLIPS\n\t\t\t\tif (dec->numtris != MAXFRAGMENTTRIS)\n\t\t\t\t{\n\t\t\t\t\tVectorCopy(impact2,\t\t\t\t\tdecalfragmentverts[dec->numtris*3+0]);\n\t\t\t\t\tVectorCopy(decalfragmentverts[trinum*3+i],\tdecalfragmentverts[dec->numtris*3+1]);\n\t\t\t\t\tVectorCopy(impact1,\t\t\t\t\tdecalfragmentverts[dec->numtris*3+2]);\n\t\t\t\t\tdec->numtris++;\n\t\t\t\t}\n#endif\n\n\n\t\t\t\t//shrink the tri, putting the impact into the killed vertex.\n\t\t\t\tVectorCopy(impact2, point[i]);\n\n\n\t\t\t\tif (dec->numtris == MAXFRAGMENTTRIS)\n\t\t\t\t\treturn;\t//:(\n\n\t\t\t\t//build the second tri\n\t\t\t\tVectorCopy(impact1,\t\t\t\t\tdecalfragmentverts[dec->numtris*3+0]);\n\t\t\t\tVectorCopy(decalfragmentverts[trinum*3+i2],\tdecalfragmentverts[dec->numtris*3+1]);\n\t\t\t\tVectorCopy(impact2,\t\t\t\t\tdecalfragmentverts[dec->numtris*3+2]);\n\t\t\t\tdec->numtris++;\n\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tSys_Error(\"Fragment_ClipTriToPlane: Clipped vertex not founc\\n\");\n\t\treturn;\t//can't handle it\n\tcase 2:\t//split into 3, disregarding both the clipped.\n\t\tfor (i = 0; i < 3; i++)\n\t\t{\n\t\t\tif (!(dotv[i] < 0))\n\t\t\t{\t//This is the vertex that's staying.\n\n\t\t\t\tif (dotv[i] < DIST_EPSILON)\n\t\t\t\t\tbreak;\t//only just inside\n\n\t\t\t\ti2 = (i+1)%3;\n\t\t\t\ti3 = (i+2)%3;\n\n\t\t\t\t//work out where the two lines impact the plane\n\t\t\t\tt = (dotv[i]) / (dotv[i]-dotv[i2]);\n\t\t\t\tVectorInterpolate(point[i], t, point[i2], impact1);\n\n\t\t\t\tt = (dotv[i]) / (dotv[i]-dotv[i3]);\n\t\t\t\tVectorInterpolate(point[i], t, point[i3], impact2);\n\n\t\t\t\t//shrink the tri, putting the impact into the killed vertex.\n\n#ifdef SHOWCLIPS\n\t\t\t\tif (dec->numtris != MAXFRAGMENTTRIS)\n\t\t\t\t{\n\t\t\t\t\tVectorCopy(impact1,\t\t\t\t\tdecalfragmentverts[dec->numtris*3+0]);\n\t\t\t\t\tVectorCopy(point[i2],\tdecalfragmentverts[dec->numtris*3+1]);\n\t\t\t\t\tVectorCopy(point[i3],\t\t\t\t\tdecalfragmentverts[dec->numtris*3+2]);\n\t\t\t\t\tdec->numtris++;\n\t\t\t\t}\n\t\t\t\tif (dec->numtris != MAXFRAGMENTTRIS)\n\t\t\t\t{\n\t\t\t\t\tVectorCopy(impact1,\t\t\t\t\tdecalfragmentverts[dec->numtris*3+0]);\n\t\t\t\t\tVectorCopy(point[i3],\tdecalfragmentverts[dec->numtris*3+1]);\n\t\t\t\t\tVectorCopy(impact2,\t\t\t\t\tdecalfragmentverts[dec->numtris*3+2]);\n\t\t\t\t\tdec->numtris++;\n\t\t\t\t}\n#endif\n\n\t\t\t\tVectorCopy(impact1, point[i2]);\n\t\t\t\tVectorCopy(impact2, point[i3]);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\tcase 3://scrap it\n\t\t//fill the verts with the verts of the last and go recursive (due to the nature of Fragment_ClipTriangle, which doesn't actually know if we clip them away)\n#ifndef SHOWCLIPS\n\t\tdec->numtris--;\n\t\tVectorCopy(decalfragmentverts[dec->numtris*3+0], decalfragmentverts[trinum*3+0]);\n\t\tVectorCopy(decalfragmentverts[dec->numtris*3+1], decalfragmentverts[trinum*3+1]);\n\t\tVectorCopy(decalfragmentverts[dec->numtris*3+2], decalfragmentverts[trinum*3+2]);\n\t\tif (trinum < dec->numtris)\n\t\t\tFragment_ClipTriToPlane(trinum, plane, planedist, dec);\n#endif\n\t\treturn;\n\t}\n}\n\nstatic void Fragment_ClipTriangle(fragmentdecal_t *dec, float *a, float *b, float *c)\n{\n\t//emit the triangle, and clip it's fragments.\n\tint start, i;\n\n\tint p;\n\n\tif (dec->numtris == MAXFRAGMENTTRIS)\n\t\treturn;\t//:(\n\n\tstart = dec->numtris;\n\n\tVectorCopy(a, decalfragmentverts[dec->numtris*3+0]);\n\tVectorCopy(b, decalfragmentverts[dec->numtris*3+1]);\n\tVectorCopy(c, decalfragmentverts[dec->numtris*3+2]);\n\tdec->numtris++;\n\n\t//clip all the fragments to all of the planes.\n\t//This will produce a quad if the source triangle was big enough.\n\n\tfor (p = 0; p < 6; p++)\n\t{\n\t\tfor (i = start; i < dec->numtris; i++)\n\t\t\tFragment_ClipTriToPlane(i, dec->planenorm[p], dec->plantdist[p], dec);\n\t}\n}\n\n#else\n\nvoid Fragment_ClipPoly(fragmentdecal_t *dec, int numverts, float *inverts, shader_t *surfshader)\n{\n\t//emit the triangle, and clip it's fragments.\n\tint p;\n\tfloat verts[MAXFRAGMENTVERTS*C];\n\tfloat verts2[MAXFRAGMENTVERTS*C];\n\tfloat *cverts;\n\tint flip;\n\tvec3_t d1, d2, n;\n\tsize_t numtris;\n\n\tif (numverts > MAXFRAGMENTTRIS)\n\t\treturn;\n\n\tif (r_decal_noperpendicular.ival)\n\t{\n\t\tVectorSubtract(inverts+C*1, inverts+C*0, d1);\n\t\tfor (p = 2; ; p++)\n\t\t{\n\t\t\tif (p >= numverts)\n\t\t\t\treturn;\n\t\t\tVectorSubtract(inverts+C*p, inverts+C*0, d2);\n\t\t\tCrossProduct(d1, d2, n);\n\t\t\tif (DotProduct(n,n)>.1)\n\t\t\t\tbreak;\n\t\t}\n\t\tVectorNormalizeFast(n);\n\t\tif (DotProduct(n, dec->normal) < 0.1)\n\t\t\treturn;\t//faces too far way from the normal\n\t}\n\n\t//clip to the first plane specially, so we don't have extra copys\n\tnumverts = Fragment_ClipPolyToPlane(inverts, verts, numverts, dec->planenorm[0], dec->planedist[0]);\n\n\t//clip the triangle to the 6 planes.\n\tflip = 0;\n\tfor (p = 1; p < dec->numplanes; p++)\n\t{\n\t\tflip^=1;\n\t\tif (flip)\n\t\t\tnumverts = Fragment_ClipPolyToPlane(verts, verts2, numverts, dec->planenorm[p], dec->planedist[p]);\n\t\telse\n\t\t\tnumverts = Fragment_ClipPolyToPlane(verts2, verts, numverts, dec->planenorm[p], dec->planedist[p]);\n\n\t\tif (numverts < 3)\t//totally clipped.\n\t\t\treturn;\n\t}\n\n\tif (flip)\n\t\tcverts = verts2;\n\telse\n\t\tcverts = verts;\n\n\t//decompose the resultant polygon into triangles.\n\n\tnumtris = 0;\n\twhile(numverts-->2)\n\t{\n\t\tif (numtris == MAXFRAGMENTTRIS)\n\t\t{\n\t\t\tdec->callback(dec->ctx, decalfragmentverts, numtris, NULL);\n\t\t\tnumtris = 0;\n\t\t\tbreak;\n\t\t}\n\n\t\tVectorCopy((cverts+C*0),\t\t\tdecalfragmentverts[numtris*3+0]);\n\t\tVectorCopy((cverts+C*(numverts-1)),\tdecalfragmentverts[numtris*3+1]);\n\t\tVectorCopy((cverts+C*numverts),\t\tdecalfragmentverts[numtris*3+2]);\n\t\tnumtris++;\n\t}\n\tif (numtris)\n\t\tdec->callback(dec->ctx, decalfragmentverts, numtris, surfshader);\n}\n\n#endif\n\n//this could be inlined, but I'm lazy.\nstatic void Fragment_Mesh (fragmentdecal_t *dec, mesh_t *mesh, mtexinfo_t *texinfo)\n{\n\tint i;\n\tvecV_t verts[3];\n\tshader_t *surfshader = texinfo->texture->shader;\n\n\tif ((surfshader->flags & SHADER_NOMARKS) || !mesh)\n\t\treturn;\n\n\tif (dec->surfflagmask)\n\t{\n\t\tif ((texinfo->flags & dec->surfflagmask) != dec->surfflagmatch)\n\t\t\treturn;\n\t}\n\n\t/*if its a triangle fan/poly/quad then we can just submit the entire thing without generating extra fragments*/\n\tif (mesh->istrifan)\n\t{\n\t\tFragment_ClipPoly(dec, mesh->numvertexes, mesh->xyz_array[0], surfshader);\n\t\treturn;\n\t}\n\n\t//Fixme: optimise q3 patches (quad strips with bends between each strip)\n\n\t/*otherwise it goes in and out in weird places*/\n\tfor (i = 0; i < mesh->numindexes; i+=3)\n\t{\n\t\tVectorCopy(mesh->xyz_array[mesh->indexes[i+0]], verts[0]);\n\t\tVectorCopy(mesh->xyz_array[mesh->indexes[i+1]], verts[1]);\n\t\tVectorCopy(mesh->xyz_array[mesh->indexes[i+2]], verts[2]);\n\t\tFragment_ClipPoly(dec, 3, verts[0], surfshader);\n\t}\n}\n\n#ifdef Q1BSPS\nstatic void Q1BSP_ClipDecalToNodes (model_t *mod, fragmentdecal_t *dec, mnode_t *node)\n{\n\tmplane_t\t*splitplane;\n\tfloat\t\tdist;\n\tmsurface_t\t*surf;\n\tint\t\t\ti;\n\n\tif (node->contents < 0)\n\t\treturn;\n\n\tsplitplane = node->plane;\n\tdist = DotProduct (dec->center, splitplane->normal) - splitplane->dist;\n\n\tif (dist > dec->radius)\n\t{\n\t\tQ1BSP_ClipDecalToNodes (mod, dec, node->children[0]);\n\t\treturn;\n\t}\n\tif (dist < -dec->radius)\n\t{\n\t\tQ1BSP_ClipDecalToNodes (mod, dec, node->children[1]);\n\t\treturn;\n\t}\n\n// mark the polygons\n\tsurf = mod->surfaces + node->firstsurface;\n\tif (r_decal_noperpendicular.ival)\n\t{\n\t\tfor (i=0 ; i<node->numsurfaces ; i++, surf++)\n\t\t{\n\t\t\tif (surf->flags & SURF_PLANEBACK)\n\t\t\t{\n\t\t\t\tif (-DotProduct(surf->plane->normal, dec->normal) > -0.5)\n\t\t\t\t\tcontinue;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (DotProduct(surf->plane->normal, dec->normal) > -0.5)\n\t\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tFragment_Mesh(dec, surf->mesh, surf->texinfo);\n\t\t}\n\t}\n\telse\n\t{\n\t\tfor (i=0 ; i<node->numsurfaces ; i++, surf++)\n\t\t\tFragment_Mesh(dec, surf->mesh, surf->texinfo);\n\t}\n\n\tQ1BSP_ClipDecalToNodes (mod, dec, node->children[0]);\n\tQ1BSP_ClipDecalToNodes (mod, dec, node->children[1]);\n}\n#endif\n\n#ifdef RTLIGHTS\nextern int sh_shadowframe;\n#else\nstatic int sh_shadowframe;\n#endif\n#ifdef Q3BSPS\nstatic void Q3BSP_ClipDecalToNodes (fragmentdecal_t *dec, mnode_t *node)\n{\n\tmplane_t\t*splitplane;\n\tfloat\t\tdist;\n\tmsurface_t\t**msurf;\n\tmsurface_t\t*surf;\n\tmleaf_t\t\t*leaf;\n\tint\t\t\ti;\n\n\tif (node->contents != -1)\n\t{\n\t\tleaf = (mleaf_t *)node;\n\t// mark the polygons\n\t\tmsurf = leaf->firstmarksurface;\n\t\tfor (i=0 ; i<leaf->nummarksurfaces ; i++, msurf++)\n\t\t{\n\t\t\tsurf = *msurf;\n\n#ifdef RTLIGHTS\n\t\t\t//only check each surface once. it can appear in multiple leafs.\n\t\t\tif (surf->shadowframe == sh_shadowframe)\n\t\t\t\tcontinue;\n\t\t\tsurf->shadowframe = sh_shadowframe;\n#endif\n\t\t\tFragment_Mesh(dec, surf->mesh, surf->texinfo);\n\t\t}\n\t\treturn;\n\t}\n\n\tsplitplane = node->plane;\n\tdist = DotProduct (dec->center, splitplane->normal) - splitplane->dist;\n\n\tif (dist > dec->radius)\n\t{\n\t\tQ3BSP_ClipDecalToNodes (dec, node->children[0]);\n\t\treturn;\n\t}\n\tif (dist < -dec->radius)\n\t{\n\t\tQ3BSP_ClipDecalToNodes (dec, node->children[1]);\n\t\treturn;\n\t}\n\tQ3BSP_ClipDecalToNodes (dec, node->children[0]);\n\tQ3BSP_ClipDecalToNodes (dec, node->children[1]);\n}\n#endif\n\nvoid Mod_ClipDecal(struct model_s *mod, vec3_t center, vec3_t normal, vec3_t tangent1, vec3_t tangent2, float size, unsigned int surfflagmask, unsigned int surfflagmatch, void (*callback)(void *ctx, vec3_t *fte_restrict points, size_t numpoints, shader_t *shader), void *ctx)\n{\t//quad marks a full, independant quad\n\tint p;\n\tfloat r;\n\tfragmentdecal_t dec;\n\n\tVectorCopy(center, dec.center);\n\tVectorCopy(normal, dec.normal);\n\tdec.radius = 0;\n\tdec.callback = callback;\n\tdec.ctx = ctx;\n\tdec.surfflagmask = surfflagmask;\n\tdec.surfflagmatch = surfflagmatch;\n\n\tVectorCopy(tangent1,\tdec.planenorm[0]);\n\tVectorNegate(tangent1,\tdec.planenorm[1]);\n\tVectorCopy(tangent2,\tdec.planenorm[2]);\n\tVectorNegate(tangent2,\tdec.planenorm[3]);\n\tVectorCopy(dec.normal,\t\tdec.planenorm[4]);\n\tVectorNegate(dec.normal,\tdec.planenorm[5]);\n\tfor (p = 0; p < 6; p++)\n\t{\n\t\tr = sqrt(DotProduct(dec.planenorm[p], dec.planenorm[p]));\n\t\tVectorScale(dec.planenorm[p], 1/r, dec.planenorm[p]);\n\t\tr*= size/2;\n\t\tif (r > dec.radius)\n\t\t\tdec.radius = r;\n\t\tdec.planedist[p] = -(r - DotProduct(dec.center, dec.planenorm[p]));\n\t}\n\tdec.numplanes = 6;\n\n\tsh_shadowframe++;\n\n\tif (!mod || mod->loadstate != MLS_LOADED)\n\t\treturn;\n\telse if (mod->type != mod_brush)\n\t\t;\n#ifdef Q1BSPS\n\telse if (mod->fromgame == fg_quake || mod->fromgame == fg_halflife)\n\t\tQ1BSP_ClipDecalToNodes(mod, &dec, mod->rootnode);\n#endif\n#ifdef Q3BSPS\n\telse if (mod->fromgame == fg_quake3 || mod->fromgame == fg_quake2 || mod->fromgame == fg_new)\n\t{\n\t\tif (mod->submodelof)\n\t\t{\n\t\t\tmsurface_t *surf;\n\t\t\tfor (surf = mod->surfaces+mod->firstmodelsurface, p = 0; p < mod->nummodelsurfaces; p++, surf++)\n\t\t\t\tFragment_Mesh(&dec, surf->mesh, surf->texinfo);\n\t\t}\n\t\telse\n\t\t\tQ3BSP_ClipDecalToNodes(&dec, mod->rootnode);\n\t}\n#endif\n\n#ifdef TERRAIN\n\tif (mod->terrain)\n\t\tTerrain_ClipDecal(&dec, center, dec.radius, mod);\n#endif\n}\n#endif\n\n/*\nDecal functions\n\n============================================================================\n\nPhysics functions (common)\n*/\n#ifdef Q1BSPS\n\nvoid Q1BSP_CheckHullNodes(hull_t *hull)\n{\n\tint num, c;\n\tmclipnode_t\t*node;\n\tfor (num = hull->firstclipnode; num < hull->lastclipnode; num++)\n\t{\n\t\tnode = hull->clipnodes + num;\n\t\tfor (c = 0; c < 2; c++)\n\t\t\tif (node->children[c] >= 0)\n\t\t\t\tif (node->children[c] < hull->firstclipnode || node->children[c] > hull->lastclipnode)\n\t\t\t\t\tSys_Error (\"Q1BSP_CheckHull: bad node number\");\n\n\t}\n}\n\n\nstatic int Q1_ModelPointContents (mnode_t *node, const vec3_t p)\n{\n\tfloat d;\n\tmplane_t *plane;\n\twhile(node->contents >= 0)\n\t{\n\t\tplane = node->plane;\n\t\tif (plane->type < 3)\n\t\t\td = p[plane->type] - plane->dist;\n\t\telse\n\t\t\td = DotProduct(plane->normal, p) - plane->dist;\n\t\tnode = node->children[d<0];\n\t}\n\treturn node->contents;\n}\n\n#endif//Q1BSPS\n/*\n==================\nSV_HullPointContents\n\n==================\n*/\nstatic int Q1_HullPointContents (hull_t *hull, int num, const vec3_t p)\n{\n\tfloat\t\td;\n\tmclipnode_t\t*node;\n\tmplane_t\t*plane;\n\n\twhile (num >= 0)\n\t{\n\t\tnode = hull->clipnodes + num;\n\t\tplane = hull->planes + node->planenum;\n\n\t\tif (plane->type < 3)\n\t\t\td = p[plane->type] - plane->dist;\n\t\telse\n\t\t\td = DotProduct (plane->normal, p) - plane->dist;\n\t\tif (d < 0)\n\t\t\tnum = node->children[1];\n\t\telse\n\t\t\tnum = node->children[0];\n\t}\n\n\treturn num;\n}\n\n#define\tDIST_EPSILON\t(0.03125)\n#if 1\n\nstatic const unsigned int q1toftecontents[] =\n{\n\t0,//EMPTY\n\tFTECONTENTS_SOLID,//SOLID\n\tFTECONTENTS_WATER,//WATER\n\tFTECONTENTS_SLIME,//SLIME\n\tFTECONTENTS_LAVA,//LAVA\n\tFTECONTENTS_SKY,//SKY\n\tFTECONTENTS_SOLID,//STRIPPED\n\tFTECONTENTS_PLAYERCLIP,//CLIP\n\tQ2CONTENTS_CURRENT_0,//FLOW_1\n\tQ2CONTENTS_CURRENT_90,//FLOW_2\n\tQ2CONTENTS_CURRENT_180,//FLOW_3\n\tQ2CONTENTS_CURRENT_270,//FLOW_4\n\tQ2CONTENTS_CURRENT_UP,//FLOW_5\n\tQ2CONTENTS_CURRENT_DOWN,//FLOW_6\n\tQ2CONTENTS_WINDOW,//TRANS\n\tFTECONTENTS_LADDER,//LADDER\n};\n\nenum\n{\n\trht_solid,\n\trht_empty,\n\trht_impact\n};\nstruct rhtctx_s\n{\n\tunsigned int checkcontents;\n\tvec3_t start, end;\n\tmclipnode_t\t*clipnodes;\n\tmplane_t\t*planes;\n};\nstatic int Q1BSP_RecursiveHullTrace (struct rhtctx_s *ctx, int num, float p1f, float p2f, const vec3_t p1, const vec3_t p2, trace_t *trace)\n{\n\tmclipnode_t\t*node;\n\tmplane_t\t*plane;\n\tfloat\t\tt1, t2;\n\tvec3_t\t\tmid;\n\tint\t\t\tside;\n\tfloat\t\tmidf;\n\tint rht;\n\nreenter:\n\n\tif (num < 0)\n\t{\n\t\tunsigned int c = q1toftecontents[-1-num];\n\t\t/*hit a leaf*/\n\t\tif (c & ctx->checkcontents)\n\t\t{\n\t\t\ttrace->contents = c;\n\t\t\tif (trace->allsolid)\n\t\t\t\ttrace->startsolid = true;\n\t\t\treturn rht_solid;\n\t\t}\n\t\telse\n\t\t{\n\t\t\ttrace->allsolid = false;\n\t\t\tif (c & FTECONTENTS_FLUID)\n\t\t\t\ttrace->inwater = true;\n\t\t\telse\n\t\t\t\ttrace->inopen = true;\n\t\t\treturn rht_empty;\n\t\t}\n\t}\n\n\t/*its a node*/\n\n\t/*get the node info*/\n\tnode = ctx->clipnodes + num;\n\tplane = ctx->planes + node->planenum;\n\n\tif (plane->type < 3)\n\t{\n\t\tt1 = p1[plane->type] - plane->dist;\n\t\tt2 = p2[plane->type] - plane->dist;\n\t}\n\telse\n\t{\n\t\tt1 = DotProduct (plane->normal, p1) - plane->dist;\n\t\tt2 = DotProduct (plane->normal, p2) - plane->dist;\n\t}\n\n\t/*if its completely on one side, resume on that side*/\n\tif (t1 >= 0 && t2 >= 0)\n\t{\n\t\t//return Q1BSP_RecursiveHullTrace (hull, node->children[0], p1f, p2f, p1, p2, trace);\n\t\tnum = node->children[0];\n\t\tgoto reenter;\n\t}\n\tif (t1 < 0 && t2 < 0)\n\t{\n\t\t//return Q1BSP_RecursiveHullTrace (hull, node->children[1], p1f, p2f, p1, p2, trace);\n\t\tnum = node->children[1];\n\t\tgoto reenter;\n\t}\n\n\tif (plane->type < 3)\n\t{\n\t\tt1 = ctx->start[plane->type] - plane->dist;\n\t\tt2 = ctx->end[plane->type] - plane->dist;\n\t}\n\telse\n\t{\n\t\tt1 = DotProduct (plane->normal, ctx->start) - plane->dist;\n\t\tt2 = DotProduct (plane->normal, ctx->end) - plane->dist;\n\t}\n\n\tside = t1 < 0;\n\n\tmidf = t1 / (t1 - t2);\n\tif (midf < p1f) midf = p1f;\n\tif (midf > p2f) midf = p2f;\n\tVectorInterpolate(ctx->start, midf, ctx->end, mid);\n\n\t//check the near side\n\trht = Q1BSP_RecursiveHullTrace(ctx, node->children[side], p1f, midf, p1, mid, trace);\n\tif (rht != rht_empty && !trace->allsolid)\n\t\treturn rht;\n\t//check the far side\n\trht = Q1BSP_RecursiveHullTrace(ctx, node->children[side^1], midf, p2f, mid, p2, trace);\n\tif (rht != rht_solid)\n\t\treturn rht;\n\n\tif (side)\n\t{\n\t\t/*we impacted the back of the node, so flip the plane*/\n\t\ttrace->plane.dist = -plane->dist;\n\t\tVectorNegate(plane->normal, trace->plane.normal);\n\t\tmidf = (t1 + DIST_EPSILON) / (t1 - t2);\n\t}\n\telse\n\t{\n\t\t/*we impacted the front of the node*/\n\t\ttrace->plane.dist = plane->dist;\n\t\tVectorCopy(plane->normal, trace->plane.normal);\n\t\tmidf = (t1 - DIST_EPSILON) / (t1 - t2);\n\t}\n\n\tt1 = DotProduct (trace->plane.normal, ctx->start) - trace->plane.dist;\n\tt2 = DotProduct (trace->plane.normal, ctx->end) - trace->plane.dist;\n\tmidf = (t1 - DIST_EPSILON) / (t1 - t2);\n\n\tmidf = bound(0, midf, 1);\n\ttrace->fraction = midf;\n\tVectorCopy (mid, trace->endpos);\n\tVectorInterpolate(ctx->start, midf, ctx->end, trace->endpos);\n\n\treturn rht_impact;\n}\n\nqboolean Q1BSP_RecursiveHullCheck (hull_t *hull, int num, const vec3_t p1, const vec3_t p2, unsigned int hitcontents, trace_t *trace)\n{\n\tif (VectorEquals(p1, p2))\n\t{\n\t\t/*points cannot cross planes, so do it faster*/\n\t\tint q1 = Q1_HullPointContents(hull, num, p1);\n\t\tunsigned int c = q1toftecontents[-1-q1];\n\t\ttrace->contents = c;\n\t\tif (c & hitcontents)\n\t\t\ttrace->startsolid = true;\n\t\telse if (c & FTECONTENTS_FLUID)\n\t\t{\n\t\t\ttrace->allsolid = false;\n\t\t\ttrace->inwater = true;\n\t\t}\n\t\telse\n\t\t{\n\t\t\ttrace->allsolid = false;\n\t\t\ttrace->inopen = true;\n\t\t}\n\t\treturn true;\n\t}\n\telse\n\t{\n\t\tstruct rhtctx_s ctx;\n\t\tVectorCopy(p1, ctx.start);\n\t\tVectorCopy(p2, ctx.end);\n\t\tctx.clipnodes = hull->clipnodes;\n\t\tctx.planes = hull->planes;\n\t\tctx.checkcontents = hitcontents;\n\t\treturn Q1BSP_RecursiveHullTrace(&ctx, num, 0, 1, p1, p2, trace) != rht_impact;\n\t}\n}\n\n#else\nqboolean Q1BSP_RecursiveHullCheck (hull_t *hull, int num, float p1f, float p2f, vec3_t p1, vec3_t p2, trace_t *trace)\n{\n\tmclipnode_t\t*node;\n\tmplane_t\t*plane;\n\tfloat\t\tt1, t2;\n\tfloat\t\tfrac;\n\tint\t\t\ti;\n\tvec3_t\t\tmid;\n\tint\t\t\tside;\n\tfloat\t\tmidf;\n\n// check for empty\n\tif (num < 0)\n\t{\n\t\tif (num != Q1CONTENTS_SOLID)\n\t\t{\n\t\t\ttrace->allsolid = false;\n\t\t\tif (num == Q1CONTENTS_EMPTY)\n\t\t\t\ttrace->inopen = true;\n\t\t\telse\n\t\t\t\ttrace->inwater = true;\n\t\t}\n\t\telse\n\t\t\ttrace->startsolid = true;\n\t\treturn true;\t\t// empty\n\t}\n\n//\n// find the point distances\n//\n\tnode = hull->clipnodes + num;\n\tplane = hull->planes + node->planenum;\n\n\tif (plane->type < 3)\n\t{\n\t\tt1 = p1[plane->type] - plane->dist;\n\t\tt2 = p2[plane->type] - plane->dist;\n\t}\n\telse\n\t{\n\t\tt1 = DotProduct (plane->normal, p1) - plane->dist;\n\t\tt2 = DotProduct (plane->normal, p2) - plane->dist;\n\t}\n\n#if 1\n\tif (t1 >= 0 && t2 >= 0)\n\t\treturn Q1BSP_RecursiveHullCheck (hull, node->children[0], p1f, p2f, p1, p2, trace);\n\tif (t1 < 0 && t2 < 0)\n\t\treturn Q1BSP_RecursiveHullCheck (hull, node->children[1], p1f, p2f, p1, p2, trace);\n#else\n\tif ( (t1 >= DIST_EPSILON && t2 >= DIST_EPSILON) || (t2 > t1 && t1 >= 0) )\n\t\treturn Q1BSP_RecursiveHullCheck (hull, node->children[0], p1f, p2f, p1, p2, trace);\n\tif ( (t1 <= -DIST_EPSILON && t2 <= -DIST_EPSILON) || (t2 < t1 && t1 <= 0) )\n\t\treturn Q1BSP_RecursiveHullCheck (hull, node->children[1], p1f, p2f, p1, p2, trace);\n#endif\n\n// put the crosspoint DIST_EPSILON pixels on the near side\n\tif (t1 < 0)\n\t\tfrac = (t1 + DIST_EPSILON)/(t1-t2);\n\telse\n\t\tfrac = (t1 - DIST_EPSILON)/(t1-t2);\n\tif (frac < 0)\n\t\tfrac = 0;\n\tif (frac > 1)\n\t\tfrac = 1;\n\n\tmidf = p1f + (p2f - p1f)*frac;\n\tfor (i=0 ; i<3 ; i++)\n\t\tmid[i] = p1[i] + frac*(p2[i] - p1[i]);\n\n\tside = (t1 < 0);\n\n// move up to the node\n\tif (!Q1BSP_RecursiveHullCheck (hull, node->children[side], p1f, midf, p1, mid, trace) )\n\t\treturn false;\n\n#ifdef PARANOID\n\tif (Q1BSP_RecursiveHullCheck (sv_hullmodel, mid, node->children[side])\n\t== Q1CONTENTS_SOLID)\n\t{\n\t\tCon_Printf (\"mid PointInHullSolid\\n\");\n\t\treturn false;\n\t}\n#endif\n\n\tif (Q1_HullPointContents (hull, node->children[side^1], mid)\n\t!= Q1CONTENTS_SOLID)\n// go past the node\n\t\treturn Q1BSP_RecursiveHullCheck (hull, node->children[side^1], midf, p2f, mid, p2, trace);\n\n\tif (trace->allsolid)\n\t\treturn false;\t\t// never got out of the solid area\n\n//==================\n// the other side of the node is solid, this is the impact point\n//==================\n\tif (!side)\n\t{\n\t\tVectorCopy (plane->normal, trace->plane.normal);\n\t\ttrace->plane.dist = plane->dist;\n\t}\n\telse\n\t{\n\t\tVectorNegate (plane->normal, trace->plane.normal);\n\t\ttrace->plane.dist = -plane->dist;\n\t}\n\n\twhile (Q1_HullPointContents (hull, hull->firstclipnode, mid)\n\t== Q1CONTENTS_SOLID)\n\t{ // shouldn't really happen, but does occasionally\n\t\tif (!(frac < 10000000) && !(frac > -10000000))\n\t\t{\n\t\t\ttrace->fraction = 0;\n\t\t\tVectorClear (trace->endpos);\n\t\t\tCon_Printf (\"nan in traceline\\n\");\n\t\t\treturn false;\n\t\t}\n\t\tfrac -= 0.1;\n\t\tif (frac < 0)\n\t\t{\n\t\t\ttrace->fraction = midf;\n\t\t\tVectorCopy (mid, trace->endpos);\n\t\t\tCon_DPrintf (\"backup past 0\\n\");\n\t\t\treturn false;\n\t\t}\n\t\tmidf = p1f + (p2f - p1f)*frac;\n\t\tfor (i=0 ; i<3 ; i++)\n\t\t\tmid[i] = p1[i] + frac*(p2[i] - p1[i]);\n\t}\n\n\ttrace->fraction = midf;\n\tVectorCopy (mid, trace->endpos);\n\n\treturn false;\n}\n#endif\n\n#if 0//def Q1BSPS\n\n/*\nthe bsp tree we're walking through is the renderable hull\nwe need to trace a box through the world.\nby its very nature, this will reach more nodes than we really want, and as we can follow a node sideways, the underlying bsp structure is no longer 100% reliable (meaning we cross planes that are entirely to one side, and follow its children too)\nso all contents and solidity must come from the brushes and ONLY the brushes.\n*/\nstruct traceinfo_s\n{\n\tunsigned int solidcontents;\n\ttrace_t trace;\n\n\tqboolean capsule;\n\tfloat radius;\n\t/*set even for sphere traces (used for bbox tests)*/\n\tvec3_t mins;\n\tvec3_t maxs;\n\n\tvec3_t start;\n\tvec3_t end;\n\n\tvec3_t\tup;\n\tvec3_t\tcapsulesize;\n\tvec3_t\textents;\n};\n\nstatic void Q1BSP_ClipToBrushes(struct traceinfo_s *traceinfo, mbrush_t *brush)\n{\n\tstruct mbrushplane_s *plane;\n\tstruct mbrushplane_s *enterplane;\n\tint i, j;\n\tvec3_t ofs;\n\tqboolean startout, endout;\n\tfloat d1,d2,dist,enterdist=0;\n\tfloat f, enterfrac, exitfrac;\n\n\tfor (; brush; brush = brush->next)\n\t{\n\t\t/*ignore if its not solid to us*/\n\t\tif (!(traceinfo->solidcontents & brush->contents))\n\t\t\tcontinue;\n\n\t\tstartout = false;\n\t\tendout = false;\n\t\tenterplane= NULL;\n\t\tenterfrac = -1;\n\t\texitfrac = 10;\n\t\tfor (i = brush->numplanes, plane = brush->planes; i; i--, plane++)\n\t\t{\n\t\t\t/*calculate the distance based upon the shape of the object we're tracing for*/\n\t\t\tif (traceinfo->capsule)\n\t\t\t{\n\t\t\t\tdist = DotProduct(traceinfo->up, plane->normal);\n\t\t\t\tdist = dist*(traceinfo->capsulesize[(dist<0)?1:2]) - traceinfo->capsulesize[0];\n\t\t\t\tdist = plane->dist - dist;\n\n\t\t\t\t//dist = plane->dist + traceinfo->radius;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tfor (j=0 ; j<3 ; j++)\n\t\t\t\t{\n\t\t\t\t\tif (plane->normal[j] < 0)\n\t\t\t\t\t\tofs[j] = traceinfo->maxs[j];\n\t\t\t\t\telse\n\t\t\t\t\t\tofs[j] = traceinfo->mins[j];\n\t\t\t\t}\n\t\t\t\tdist = DotProduct (ofs, plane->normal);\n\t\t\t\tdist = plane->dist - dist;\n\t\t\t}\n\n\t\t\td1 = DotProduct (traceinfo->start, plane->normal) - dist;\n\t\t\td2 = DotProduct (traceinfo->end, plane->normal) - dist;\n\n\t\t\tif (d1 >= 0)\n\t\t\t\tstartout = true;\n\t\t\tif (d2 > 0)\n\t\t\t\tendout = true;\n\n\t\t\t//if we're fully outside any plane, then we cannot possibly enter the brush, skip to the next one\n\t\t\tif (d1 > 0 && d2 >= 0)\n\t\t\t\tgoto nextbrush;\n\n\t\t\t//if we're fully inside the plane, then whatever is happening is not relevent for this plane\n\t\t\tif (d1 < 0 && d2 <= 0)\n\t\t\t\tcontinue;\n\n\t\t\tf = d1 / (d1-d2);\n\t\t\tif (d1 > d2)\n\t\t\t{\n\t\t\t\t//entered the brush. favour the furthest fraction to avoid extended edges (yay for convex shapes)\n\t\t\t\tif (enterfrac < f)\n\t\t\t\t{\n\t\t\t\t\tenterfrac = f;\n\t\t\t\t\tenterplane = plane;\n\t\t\t\t\tenterdist = dist;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t//left the brush, favour the nearest plane (smallest frac)\n\t\t\t\tif (exitfrac > f)\n\t\t\t\t{\n\t\t\t\t\texitfrac = f;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (!startout)\n\t\t{\n\t\t\ttraceinfo->trace.startsolid = true;\n\t\t\tif (!endout)\n\t\t\t\ttraceinfo->trace.allsolid = true;\n\t\t\ttraceinfo->trace.contents |= brush->contents;\n\t\t\treturn;\n\t\t}\n\t\tif (enterfrac != -1 && enterfrac < exitfrac)\n\t\t{\n\t\t\t//impact!\n\t\t\tif (enterfrac < traceinfo->trace.fraction)\n\t\t\t{\n\t\t\t\ttraceinfo->trace.fraction = enterfrac;\n\t\t\t\ttraceinfo->trace.plane.dist = enterdist;\n\t\t\t\tVectorCopy(enterplane->normal, traceinfo->trace.plane.normal);\n\t\t\t\ttraceinfo->trace.contents = brush->contents;\n\t\t\t}\n\t\t}\nnextbrush:\n\t\t;\n\t}\n}\nstatic void Q1BSP_InsertBrush(mnode_t *node, mbrush_t *brush, vec3_t bmins, vec3_t bmaxs)\n{\n\tvec3_t nearp, farp;\n\tfloat nd, fd;\n\tint i;\n\twhile(1)\n\t{\n\t\tif (node->contents < 0) /*leaf, so no smaller node to put it in (I'd be surprised if it got this far)*/\n\t\t{\n\t\t\tbrush->next = node->brushes;\n\t\t\tnode->brushes = brush;\n\t\t\treturn;\n\t\t}\n\n\t\tfor (i = 0; i < 3; i++)\n\t\t{\n\t\t\tif (node->plane->normal[i] > 0)\n\t\t\t{\n\t\t\t\tnearp[i] = bmins[i];\n\t\t\t\tfarp[i] = bmaxs[i];\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tnearp[i] = bmaxs[i];\n\t\t\t\tfarp[i] = bmins[i];\n\t\t\t}\n\t\t}\n\n\t\tnd = DotProduct(node->plane->normal, nearp) - node->plane->dist;\n\t\tfd = DotProduct(node->plane->normal, farp) - node->plane->dist;\n\n\t\t/*if its fully on either side, continue walking*/\n\t\tif (nd < 0 && fd < 0)\n\t\t\tnode = node->children[1];\n\t\telse if (nd > 0 && fd > 0)\n\t\t\tnode = node->children[0];\n\t\telse\n\t\t{\n\t\t\t/*plane crosses bbox, so insert here*/\n\t\t\tbrush->next = node->brushes;\n\t\t\tnode->brushes = brush;\n\t\t\treturn;\n\t\t}\n\t}\n}\nstatic void Q1BSP_RecursiveBrushCheck (struct traceinfo_s *traceinfo, mnode_t *node, float p1f, float p2f, const vec3_t p1, const vec3_t p2)\n{\n\tmplane_t\t*plane;\n\tfloat\t\tt1, t2;\n\tfloat\t\tfrac;\n\tint\t\t\ti;\n\tvec3_t\t\tmid;\n\tint\t\t\tside;\n\tfloat\t\tmidf;\n\tfloat\t\toffset;\n\n\tif (node->brushes)\n\t{\n\t\tQ1BSP_ClipToBrushes(traceinfo, node->brushes);\n\t}\n\n\tif (traceinfo->trace.fraction < p1f)\n\t{\n\t\t//already hit something closer than this node\n\t\treturn;\n\t}\n\n\tif (node->contents < 0)\n\t{\n\t\t//we're in a leaf\n\t\treturn;\n\t}\n\n//\n// find the point distances\n//\n\tplane = node->plane;\n\n\tif (plane->type < 3)\n\t{\n\t\tt1 = p1[plane->type] - plane->dist;\n\t\tt2 = p2[plane->type] - plane->dist;\n\t\tif (plane->normal[plane->type] < 0)\n\t\t\toffset = -traceinfo->mins[plane->type];\n\t\telse\n\t\t\toffset = traceinfo->maxs[plane->type];\n\t}\n\telse\n\t{\n\t\tt1 = DotProduct (plane->normal, p1) - plane->dist;\n\t\tt2 = DotProduct (plane->normal, p2) - plane->dist;\n\t\toffset = 0;\n\t\tfor (i = 0; i < 3; i++)\n\t\t{\n\t\t\tif (plane->normal[i] < 0)\n\t\t\t\toffset += plane->normal[i] * -traceinfo->mins[i];\n\t\t\telse\n\t\t\t\toffset += plane->normal[i] * traceinfo->maxs[i];\n\t\t}\n\t}\n\n\t/*if we're fully on one side of the trace, go only down that side*/\n\tif (t1 >= offset && t2 >= offset)\n\t{\n\t\tQ1BSP_RecursiveBrushCheck (traceinfo, node->children[0], p1f, p2f, p1, p2);\n\t\treturn;\n\t}\n\tif (t1 < -offset && t2 < -offset)\n\t{\n\t\tQ1BSP_RecursiveBrushCheck (traceinfo, node->children[1], p1f, p2f, p1, p2);\n\t\treturn;\n\t}\n\n// put the crosspoint DIST_EPSILON pixels on the near side\n\tif (t1 == t2)\n\t{\n\t\tside = 0;\n\t\tfrac = 0;\n\t}\n\telse if (t1 < 0)\n\t{\n\t\tfrac = (t1 + DIST_EPSILON)/(t1-t2);\n\t\tside = 1;\n\t}\n\telse\n\t{\n\t\tfrac = (t1 - DIST_EPSILON)/(t1-t2);\n\t\tside = 0;\n\t}\n\tif (frac < 0)\n\t\tfrac = 0;\n\tif (frac > 1)\n\t\tfrac = 1;\n\n\tmidf = p1f + (p2f - p1f)*frac;\n\tfor (i=0 ; i<3 ; i++)\n\t\tmid[i] = p1[i] + frac*(p2[i] - p1[i]);\n\n// move up to the node\n\tQ1BSP_RecursiveBrushCheck (traceinfo, node->children[side], p1f, midf, p1, mid);\n\n// go past the node\n\tQ1BSP_RecursiveBrushCheck (traceinfo, node->children[side^1], midf, p2f, mid, p2);\n}\n#endif\t//Q1BSPS\n\nstatic unsigned int Q1BSP_TranslateContents(enum q1contents_e contents)\n{\n\tsafeswitch(contents)\n\t{\n\tcase Q1CONTENTS_EMPTY:\t\t\treturn FTECONTENTS_EMPTY;\n\tcase Q1CONTENTS_SOLID:\t\t\treturn FTECONTENTS_SOLID;\n\tcase Q1CONTENTS_WATER:\t\t\treturn FTECONTENTS_WATER;\n\tcase Q1CONTENTS_SLIME:\t\t\treturn FTECONTENTS_SLIME;\n\tcase Q1CONTENTS_LAVA:\t\t\treturn FTECONTENTS_LAVA;\n\tcase Q1CONTENTS_SKY:\t\t\treturn FTECONTENTS_SKY|FTECONTENTS_PLAYERCLIP|FTECONTENTS_MONSTERCLIP;\n\tcase Q1CONTENTS_LADDER:\t\t\treturn FTECONTENTS_LADDER;\n\tcase HLCONTENTS_CLIP:\t\t\treturn FTECONTENTS_PLAYERCLIP|FTECONTENTS_MONSTERCLIP;\n\tcase HLCONTENTS_CURRENT_0:\t\treturn FTECONTENTS_WATER|Q2CONTENTS_CURRENT_0;\t\t//q2 is better than nothing, right?\n\tcase HLCONTENTS_CURRENT_90:\t\treturn FTECONTENTS_WATER|Q2CONTENTS_CURRENT_90;\n\tcase HLCONTENTS_CURRENT_180:\treturn FTECONTENTS_WATER|Q2CONTENTS_CURRENT_180;\n\tcase HLCONTENTS_CURRENT_270:\treturn FTECONTENTS_WATER|Q2CONTENTS_CURRENT_270;\n\tcase HLCONTENTS_CURRENT_UP:\t\treturn FTECONTENTS_WATER|Q2CONTENTS_CURRENT_UP;\n\tcase HLCONTENTS_CURRENT_DOWN:\treturn FTECONTENTS_WATER|Q2CONTENTS_CURRENT_DOWN;\n\tcase HLCONTENTS_TRANS:\t\t\treturn FTECONTENTS_EMPTY;\n\tcase Q1CONTENTS_MONSTERCLIP:\treturn FTECONTENTS_MONSTERCLIP;\n\tcase Q1CONTENTS_PLAYERCLIP:\t\treturn FTECONTENTS_PLAYERCLIP;\n\tcase Q1CONTENTS_CORPSE:\t\t\treturn FTECONTENTS_CORPSE;\n\n\tsafedefault:\n\t\tCon_Printf(\"Q1BSP_TranslateContents: Unknown contents type - %i\", contents);\n\t\treturn FTECONTENTS_SOLID;\n\t}\n}\n\nint Q1BSP_HullPointContents(hull_t *hull, const vec3_t p)\n{\n\treturn Q1BSP_TranslateContents(Q1_HullPointContents(hull, hull->firstclipnode, p));\n}\n\n#ifdef Q1BSPS\nunsigned int Q1BSP_PointContents(model_t *model, const vec3_t axis[3], const vec3_t point)\n{\n\tint contents;\n\tif (axis)\n\t{\n\t\tvec3_t transformed;\n\t\ttransformed[0] = DotProduct(point, axis[0]);\n\t\ttransformed[1] = DotProduct(point, axis[1]);\n\t\ttransformed[2] = DotProduct(point, axis[2]);\n\t\treturn Q1BSP_PointContents(model, NULL, transformed);\n\t}\n\telse\n\t{\n\t\tif (!model->firstmodelsurface)\n\t\t{\n\t\t\tcontents = Q1BSP_TranslateContents(Q1_ModelPointContents(model->nodes, point));\n\t\t}\n\t\telse\n\t\t\tcontents = Q1BSP_HullPointContents(&model->hulls[0], point);\n\t}\n#ifdef TERRAIN\n\tif (model->terrain)\n\t\tcontents |= Heightmap_PointContents(model, NULL, point);\n#endif\n\treturn contents;\n}\n\nvoid Q1BSP_LoadBrushes(model_t *model, bspx_header_t *bspx, void *mod_base)\n{\n\tconst struct {\n\t\tunsigned int ver;\n\t\tunsigned int modelnum;\n\t\tunsigned int numbrushes;\n\t\tunsigned int numplanes;\n\t} *srcmodel;\n\tconst struct {\n\t\tfloat mins[3];\n\t\tfloat maxs[3];\n\t\tsigned short contents;\n\t\tunsigned short numplanes;\n\t} *srcbrush;\n\t/*\n\tNote to implementors:\n\ta pointy brush with angles pointier than 90 degrees will extend further than any adjacent brush, thus creating invisible walls with larger expansions.\n\tthe engine inserts 6 axial planes acording to the bbox, thus the qbsp need not write any axial planes\n\tnote that doing it this way probably isn't good if you want to query textures...\n\t*/\n\tconst struct{\n\t\tvec3_t normal;\n\t\tfloat dist;\n\t} *srcplane;\n\n\tstatic vec3_t axis[3] = {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}};\n\tunsigned int br, pl;\n\tq2cbrush_t *brush;\n\tq2cbrushside_t *sides;\t//grr!\n\tmplane_t *planes;\t//bulky?\n\tsize_t lumpsizeremaining;\n\tunsigned int numplanes;\n\n\tunsigned int srcver, srcmodelidx, modbrushes, modplanes;\n\n\tsrcmodel = BSPX_FindLump(bspx, mod_base, \"BRUSHLIST\", &lumpsizeremaining);\n\tif (!srcmodel)\n\t\treturn;\n\n\twhile (lumpsizeremaining)\n\t{\n\t\tif (lumpsizeremaining < sizeof(*srcmodel))\n\t\t\treturn;\n\t\tsrcver = LittleLong(srcmodel->ver);\n\t\tsrcmodelidx = LittleLong(srcmodel->modelnum);\n\t\tmodbrushes = LittleLong(srcmodel->numbrushes);\n\t\tmodplanes = LittleLong(srcmodel->numplanes);\n\t\tif (srcver != 1 || lumpsizeremaining < sizeof(*srcmodel) + modbrushes*sizeof(*srcmodel) + modplanes*sizeof(*srcplane))\n\t\t\treturn;\n\t\tlumpsizeremaining -= ((const char*)(srcmodel+1) + modbrushes*sizeof(*srcbrush) + modplanes*sizeof(*srcplane)) - (const char*)srcmodel;\n\n\t\tif (srcmodelidx > model->numsubmodels)\n\t\t\treturn;\n\n\t\tbrush = ZG_Malloc(&model->memgroup, sizeof(*brush)*modbrushes +\n\t\t\t\t\t\t\t\t\t\t\tsizeof(*sides)*(modbrushes*6+modplanes) +\n\t\t\t\t\t\t\t\t\t\t\tsizeof(*planes)*(modbrushes*6+modplanes));\n\t\tsides = (void*)(brush + modbrushes);\n\t\tplanes = (void*)(sides + modbrushes*6+modplanes);\n\t\tmodel->submodels[srcmodelidx].brushes = brush;\n\t\tsrcbrush = (const void*)(srcmodel+1);\n\t\tfor (br = 0; br < modbrushes; br++, srcbrush = (const void*)srcplane)\n\t\t{\n\t\t\t/*byteswap it all in place*/\n\t\t\tbrush->absmins[0] = LittleFloat(srcbrush->mins[0]);\n\t\t\tbrush->absmins[1] = LittleFloat(srcbrush->mins[1]);\n\t\t\tbrush->absmins[2] = LittleFloat(srcbrush->mins[2]);\n\t\t\tbrush->absmaxs[0] = LittleFloat(srcbrush->maxs[0]);\n\t\t\tbrush->absmaxs[1] = LittleFloat(srcbrush->maxs[1]);\n\t\t\tbrush->absmaxs[2] = LittleFloat(srcbrush->maxs[2]);\n\t\t\tnumplanes = (unsigned short)LittleShort(srcbrush->numplanes);\n\n\t\t\t/*make sure planes don't overflow*/\n\t\t\tif (numplanes > modplanes)\n\t\t\t\treturn;\n\t\t\tmodplanes-=numplanes;\n\n\t\t\t/*set up the mbrush from the file*/\n\t\t\tbrush->contents = Q1BSP_TranslateContents(LittleShort(srcbrush->contents));\n\t\t\tbrush->brushside = sides;\n\t\t\tfor (srcplane = (const void*)(srcbrush+1); numplanes --> 0; srcplane++)\n\t\t\t{\n\t\t\t\tplanes->normal[0] = LittleFloat(srcplane->normal[0]);\n\t\t\t\tplanes->normal[1] = LittleFloat(srcplane->normal[1]);\n\t\t\t\tplanes->normal[2] = LittleFloat(srcplane->normal[2]);\n\t\t\t\tplanes->dist = LittleFloat(srcplane->dist);\n\t\t\t\tCategorizePlane(planes);\n\n\t\t\t\tsides->surface = NULL;\n\t\t\t\tsides++->plane = planes++;\n\t\t\t}\n\n\t\t\t/*and add axial planes acording to the brush's bbox*/\n\t\t\tfor (pl = 0; pl < 3; pl++)\n\t\t\t{\n\t\t\t\tVectorCopy(axis[pl], planes->normal);\n\t\t\t\tplanes->dist = brush->absmaxs[pl];\n\t\t\t\tCategorizePlane(planes);\n\n\t\t\t\tsides->surface = NULL;\n\t\t\t\tsides++->plane = planes++;\n\t\t\t}\n\t\t\tfor (pl = 0; pl < 3; pl++)\n\t\t\t{\n\t\t\t\tVectorNegate(axis[pl], planes->normal);\n\t\t\t\tplanes->dist = -brush->absmins[pl];\n\t\t\t\tCategorizePlane(planes);\n\n\t\t\t\tsides->surface = NULL;\n\t\t\t\tsides++->plane = planes++;\n\t\t\t}\n\t\t\tbrush->numsides = sides - brush->brushside;\n\t\t\tbrush++;\n\t\t}\n\t\tmodel->submodels[srcmodelidx].numbrushes = brush-model->submodels[srcmodelidx].brushes;\n\n\t\t/*move on to the next model*/\n\t\tsrcmodel = (const void*)srcbrush;\n\t}\n}\n\nhull_t *Q1BSP_ChooseHull(model_t *model, int forcehullnum, const vec3_t mins, const vec3_t maxs, vec3_t offset)\n{\n\thull_t *hull;\n\tvec3_t size;\n\tVectorSubtract (maxs, mins, size);\n\tif (forcehullnum >= 1 && forcehullnum <= MAX_MAP_HULLSM && model->hulls[forcehullnum-1].available)\n\t\thull = &model->hulls[forcehullnum-1];\n\telse\n\t{\n#ifdef HEXEN2\n\t\tif (model->hulls[5].available)\n\t\t{\t//choose based on hexen2 sizes.\n\n\t\t\tif (size[0] < 3) // Point\n\t\t\t\thull = &model->hulls[0];\n\t\t\telse if (size[0] <= 8.1 && model->hulls[4].available)\n\t\t\t\thull = &model->hulls[4];\t//Pentacles\n\t\t\telse if (size[0] <= 32.1 && size[2] <= 28.1)  // Half Player\n\t\t\t\thull = &model->hulls[3];\n\t\t\telse if (size[0] <= 32.1)  // Full Player\n\t\t\t\thull = &model->hulls[1];\n\t\t\telse // Golumn\n\t\t\t\thull = &model->hulls[5];\n\t\t}\n\t\telse\n#endif\n\t\t{\n\t\t\tif (size[0] < 3 || !model->hulls[1].available)\n\t\t\t\thull = &model->hulls[0];\n\t\t\telse if (size[0] <= 32.1 || !model->hulls[2].available)\n\t\t\t{\n\t\t\t\tif (size[2] < 54.1 && model->hulls[3].available)\n\t\t\t\t\thull = &model->hulls[3]; // 32x32x36 (half-life's crouch)\n\t\t\t\telse if (model->hulls[1].available)\n\t\t\t\t\thull = &model->hulls[1];\n\t\t\t\telse\n\t\t\t\t\thull = &model->hulls[0];\n\t\t\t}\n\t\t\telse\n\t\t\t\thull = &model->hulls[2];\n\t\t}\n\t}\n\n\tVectorSubtract (hull->clip_mins, mins, offset);\n\treturn hull;\n}\nqboolean Q1BSP_Trace(model_t *model, int forcehullnum, const framestate_t *framestate, const vec3_t axis[3], const vec3_t start, const vec3_t end, const vec3_t mins, const vec3_t maxs, qboolean capsule, unsigned int hitcontentsmask, trace_t *trace)\n{\n\thull_t *hull;\n\tvec3_t start_l, end_l;\n\tvec3_t offset;\n\n\tmemset (trace, 0, sizeof(trace_t));\n\ttrace->fraction = 1;\n\ttrace->allsolid = true;\n\n\thull = Q1BSP_ChooseHull(model, forcehullnum, mins, maxs, offset);\n\n//fix for hexen2 monsters half-walking into walls.\n//\tif (forent.flags & FL_MONSTER)\n//\t{\n//\t\toffset[0] = 0;\n//\t\toffset[1] = 0;\n//\t}\n\n\tif (axis)\n\t{\n\t\tvec3_t tmp;\n\t\tVectorSubtract(start, offset, tmp);\n\t\tstart_l[0] = DotProduct(tmp, axis[0]);\n\t\tstart_l[1] = DotProduct(tmp, axis[1]);\n\t\tstart_l[2] = DotProduct(tmp, axis[2]);\n\t\tVectorSubtract(end, offset, tmp);\n\t\tend_l[0] = DotProduct(tmp, axis[0]);\n\t\tend_l[1] = DotProduct(tmp, axis[1]);\n\t\tend_l[2] = DotProduct(tmp, axis[2]);\n\t\tQ1BSP_RecursiveHullCheck(hull, hull->firstclipnode, start_l, end_l, hitcontentsmask, trace);\n\n\t\tif (trace->fraction == 1)\n\t\t{\n\t\t\tVectorCopy (end, trace->endpos);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tvec3_t iaxis[3];\n\t\t\tvec3_t norm;\n\t\t\tMatrix3x3_RM_Invert_Simple((void *)axis, iaxis);\n\t\t\tVectorCopy(trace->plane.normal, norm);\n\t\t\ttrace->plane.normal[0] = DotProduct(norm, iaxis[0]);\n\t\t\ttrace->plane.normal[1] = DotProduct(norm, iaxis[1]);\n\t\t\ttrace->plane.normal[2] = DotProduct(norm, iaxis[2]);\n\n\t\t\t/*just interpolate it, its easier than inverse matrix rotations*/\n\t\t\tVectorInterpolate(start, trace->fraction, end, trace->endpos);\n\t\t}\n\t}\n\telse\n\t{\n\t\tVectorSubtract(start, offset, start_l);\n\t\tVectorSubtract(end, offset, end_l);\n\t\tQ1BSP_RecursiveHullCheck(hull, hull->firstclipnode, start_l, end_l, hitcontentsmask, trace);\n\n\t\tif (trace->fraction == 1)\n\t\t{\n\t\t\tVectorCopy (end, trace->endpos);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tVectorAdd (trace->endpos, offset, trace->endpos);\n\t\t}\n\t}\n\n#ifdef TERRAIN\n\tif (model->terrain && trace->fraction)\n\t{\n\t\ttrace_t hmt;\n\t\tHeightmap_Trace(model, forcehullnum, framestate, axis, start, end, mins, maxs, capsule, hitcontentsmask, &hmt);\n\t\tif (hmt.fraction < trace->fraction)\n\t\t\t*trace = hmt;\n\t}\n#endif\n\n\treturn trace->fraction != 1;\n}\n\n/*\nPhysics functions (common)\n\n============================================================================\n\nRendering functions (Client only)\n*/\n#ifdef HAVE_CLIENT\n\nextern int\tr_dlightframecount;\n\n//goes through the nodes marking the surfaces near the dynamic light as lit.\nvoid Q1BSP_MarkLights (dlight_t *light, dlightbitmask_t bit, mnode_t *node)\n{\n\tmplane_t\t*splitplane;\n\tfloat\t\tdist;\n\tmsurface_t\t*surf;\n\tint\t\t\ti;\n\n\tfloat\t\tl, maxdist;\n\tint\t\t\tj, s, t;\n\tvec3_t\t\timpact;\n\tvec4_t\t\t*lmvecs;\n\tfloat\t\t*lmvecscale;\n\n\tif (node->contents < 0)\n\t\treturn;\n\n\tsplitplane = node->plane;\n\tif (splitplane->type < 3)\n\t\tdist = light->origin[splitplane->type] - splitplane->dist;\n\telse\n\t\tdist = DotProduct (light->origin, splitplane->normal) - splitplane->dist;\n\n\tif (dist > light->radius)\n\t{\n\t\tQ1BSP_MarkLights (light, bit, node->children[0]);\n\t\treturn;\n\t}\n\tif (dist < -light->radius)\n\t{\n\t\tQ1BSP_MarkLights (light, bit, node->children[1]);\n\t\treturn;\n\t}\n\n\tmaxdist = light->radius*light->radius;\n\n// mark the polygons\n\tsurf = currentmodel->surfaces + node->firstsurface;\n\tfor (i=0 ; i<node->numsurfaces ; i++, surf++)\n\t{\n\t\t//Yeah, you can blame LordHavoc for this alternate code here.\n\t\tfor (j=0 ; j<3 ; j++)\n\t\t\timpact[j] = light->origin[j] - surf->plane->normal[j]*dist;\n\n\t\tif (currentmodel->facelmvecs)\n\t\t\tlmvecs = currentmodel->facelmvecs[surf-currentmodel->surfaces].lmvecs, lmvecscale = currentmodel->facelmvecs[surf-currentmodel->surfaces].lmvecscale;\n\t\telse\n\t\t\tlmvecs = surf->texinfo->vecs, lmvecscale = surf->texinfo->vecscale;\n\n\t\t// clamp center of light to corner and check brightness\n\t\tl = DotProduct (impact, lmvecs[0]) + lmvecs[0][3] - surf->texturemins[0];\n\t\ts = l+0.5;if (s < 0) s = 0;else if (s > surf->extents[0]) s = surf->extents[0];\n\t\ts = (l - s)*lmvecscale[0]; //FIXME\n\t\tl = DotProduct (impact, lmvecs[1]) + lmvecs[1][3] - surf->texturemins[1];\n\t\tt = l+0.5;if (t < 0) t = 0;else if (t > surf->extents[1]) t = surf->extents[1];\n\t\tt = (l - t)*lmvecscale[1];\n\t\t// compare to minimum light\n\t\tif ((s*s+t*t+dist*dist) < maxdist)\n\t\t{\n\t\t\tif (surf->dlightframe != r_dlightframecount)\n\t\t\t{\n\t\t\t\tsurf->dlightbits = bit;\n\t\t\t\tsurf->dlightframe = r_dlightframecount;\n\t\t\t}\n\t\t\telse\n\t\t\t\tsurf->dlightbits |= bit;\n\t\t}\n\t}\n\n\tQ1BSP_MarkLights (light, bit, node->children[0]);\n\tQ1BSP_MarkLights (light, bit, node->children[1]);\n}\n\n//combination of R_AddDynamicLights and R_MarkLights\nstatic void Q1BSP_StainNode_r (model_t *model, mnode_t *node, float *parms)\n{\n\tmplane_t\t*splitplane;\n\tfloat\t\tdist;\n\tmsurface_t\t*surf;\n\tint\t\t\ti;\n\n\tif (node->contents < 0)\n\t\treturn;\n\n\tsplitplane = node->plane;\n\tdist = DotProduct ((parms+1), splitplane->normal) - splitplane->dist;\n\n\tif (dist > (*parms))\n\t{\n\t\tQ1BSP_StainNode_r (model, node->children[0], parms);\n\t\treturn;\n\t}\n\tif (dist < (-*parms))\n\t{\n\t\tQ1BSP_StainNode_r (model, node->children[1], parms);\n\t\treturn;\n\t}\n\n// mark the polygons\n\tsurf = model->surfaces + node->firstsurface;\n\tfor (i=0 ; i<node->numsurfaces ; i++, surf++)\n\t{\n\t\tif (surf->flags&~(SURF_DRAWALPHA|SURF_DONTWARP|SURF_PLANEBACK))\n\t\t\tcontinue;\n\t\tSurf_StainSurf(model, surf, parms);\n\t}\n\n\tQ1BSP_StainNode_r (model, node->children[0], parms);\n\tQ1BSP_StainNode_r (model, node->children[1], parms);\n}\nstatic void Q1BSP_StainNode (model_t *model, float *parms)\n{\n\tQ1BSP_StainNode_r(model, model->rootnode, parms);\n}\n\n\nstatic qbyte *q1frustumvis;\t//temp use\nstatic int q1_visframecount;//temp use\nstatic int q1_framecount;\t//temp use\nstruct q1bspprv_s\n{\n\tint visframecount;\n\tint framecount;\n\tint oldviewclusters[2];\n};\n#define BACKFACE_EPSILON\t0.01\nstatic void Q1BSP_RecursiveWorldNode (mnode_t *node, unsigned int clipflags)\n{\n\tint\t\t\tc, side, clipped;\n\tmplane_t\t*plane, *clipplane;\n\tmsurface_t\t*surf, **mark;\n\tmleaf_t\t\t*pleaf;\n\tdouble\t\tdot;\n\nstart:\n\n\tif (node->contents == Q1CONTENTS_SOLID)\n\t\treturn;\t\t// solid\n\n\tif (node->visframe != q1_visframecount)\n\t\treturn;\n\n\tfor (c = 0, clipplane = r_refdef.frustum; c < r_refdef.frustum_numworldplanes; c++, clipplane++)\n\t{\n\t\tif (!(clipflags & (1 << c)))\n\t\t\tcontinue;\t// don't need to clip against it\n\n\t\tclipped = BOX_ON_PLANE_SIDE (node->minmaxs, node->minmaxs + 3, clipplane);\n\t\tif (clipped == 2)\n\t\t\treturn;\n\t\telse if (clipped == 1)\n\t\t\tclipflags -= (1<<c);\t// node is entirely on screen\n\t}\n\n// if a leaf node, draw stuff\n\tif (node->contents < 0)\n\t{\n\t\tpleaf = (mleaf_t *)node;\n\n\t\tc = (pleaf - cl.worldmodel->leafs)-1;\n\t\tq1frustumvis[c>>3] |= 1<<(c&7);\n\n\t\tmark = pleaf->firstmarksurface;\n\t\tc = pleaf->nummarksurfaces;\n\n\t\tif (c)\n\t\t{\n\t\t\tdo\n\t\t\t{\n\t\t\t\t(*mark++)->visframe = q1_framecount;\n\t\t\t} while (--c);\n\t\t}\n\t\treturn;\n\t}\n\n// node is just a decision point, so go down the apropriate sides\n\n// find which side of the node we are on\n\tplane = node->plane;\n\n\tswitch (plane->type)\n\t{\n\tcase PLANE_X:\n\t\tdot = r_origin[0] - plane->dist;\n\t\tbreak;\n\tcase PLANE_Y:\n\t\tdot = r_origin[1] - plane->dist;\n\t\tbreak;\n\tcase PLANE_Z:\n\t\tdot = r_origin[2] - plane->dist;\n\t\tbreak;\n\tdefault:\n\t\tdot = DotProduct (r_origin, plane->normal) - plane->dist;\n\t\tbreak;\n\t}\n\n\tif (dot >= 0)\n\t\tside = 0;\n\telse\n\t\tside = 1;\n\n// recurse down the children, front side first\n\tQ1BSP_RecursiveWorldNode (node->children[side], clipflags);\n\n// draw stuff\n\tc = node->numsurfaces;\n\n\tif (c)\n\t{\n\t\tsurf = cl.worldmodel->surfaces + node->firstsurface;\n\n\t\tif (dot < 0 -BACKFACE_EPSILON)\n\t\t\tside = SURF_PLANEBACK;\n\t\telse if (dot > BACKFACE_EPSILON)\n\t\t\tside = 0;\n\t\t{\n\t\t\tfor ( ; c ; c--, surf++)\n\t\t\t{\n\t\t\t\tif (surf->visframe != q1_framecount)\n\t\t\t\t\tcontinue;\n\n\t\t\t\tif (((dot < 0) ^ !!(surf->flags & SURF_PLANEBACK)))\n\t\t\t\t\tcontinue;\t\t// wrong side\n\n\t\t\t\tSurf_RenderDynamicLightmaps (surf);\n\t\t\t\tsurf->sbatch->mesh[surf->sbatch->meshes++] = surf->mesh;\n\t\t\t}\n\t\t}\n\t}\n\n// recurse down the back side\n\t//GLR_RecursiveWorldNode (node->children[!side], clipflags);\n\tnode = node->children[!side];\n\tgoto start;\n}\n\nstatic void Q1BSP_OrthoRecursiveWorldNode (mnode_t *node, unsigned int clipflags)\n{\n\t//when rendering as ortho the front and back sides are technically equal. the only culling comes from frustum culling.\n\n\tint\t\t\tc, clipped;\n\tmplane_t\t*clipplane;\n\tmsurface_t\t*surf, **mark;\n\tmleaf_t\t\t*pleaf;\n\n\tif (node->contents == Q1CONTENTS_SOLID)\n\t\treturn;\t\t// solid\n\n\tif (node->visframe != q1_visframecount)\n\t\treturn;\n\n\tfor (c = 0, clipplane = r_refdef.frustum; c < r_refdef.frustum_numworldplanes; c++, clipplane++)\n\t{\n\t\tif (!(clipflags & (1 << c)))\n\t\t\tcontinue;\t// don't need to clip against it\n\n\t\tclipped = BOX_ON_PLANE_SIDE (node->minmaxs, node->minmaxs + 3, clipplane);\n\t\tif (clipped == 2)\n\t\t\treturn;\n\t\telse if (clipped == 1)\n\t\t\tclipflags -= (1<<c);\t// node is entirely on screen\n\t}\n\n// if a leaf node, draw stuff\n\tif (node->contents < 0)\n\t{\n\t\tpleaf = (mleaf_t *)node;\n\n\t\tmark = pleaf->firstmarksurface;\n\t\tc = pleaf->nummarksurfaces;\n\n\t\tif (c)\n\t\t{\n\t\t\tdo\n\t\t\t{\n\t\t\t\t(*mark++)->visframe = q1_framecount;\n\t\t\t} while (--c);\n\t\t}\n\t\treturn;\n\t}\n\n// recurse down the children\n\tQ1BSP_OrthoRecursiveWorldNode (node->children[0], clipflags);\n\tQ1BSP_OrthoRecursiveWorldNode (node->children[1], clipflags);\n\n// draw stuff\n\tc = node->numsurfaces;\n\n\tif (c)\n\t{\n\t\tsurf = cl.worldmodel->surfaces + node->firstsurface;\n\n\t\tfor ( ; c ; c--, surf++)\n\t\t{\n\t\t\tif (surf->visframe != q1_framecount)\n\t\t\t\tcontinue;\n\n\t\t\tSurf_RenderDynamicLightmaps (surf);\n\t\t\tsurf->sbatch->mesh[surf->sbatch->meshes++] = surf->mesh;\n\t\t}\n\t}\n\treturn;\n}\n\nstatic qbyte *Q1BSP_MarkLeaves (model_t *model, int clusters[2])\n{\n\tstatic qbyte\t*cvis;\n\tqbyte *vis;\n\tmnode_t\t*node;\n\tint\t\ti;\n\tint portal = r_refdef.recurse;\n\tstatic pvsbuffer_t pvsbuf;\n\tstruct q1bspprv_s *prv = model->meshinfo;\n\n\tq1_framecount = ++prv->framecount;\n\n\t//for portals to work, we need two sets of any pvs caches\n\t//this means lights can still check pvs at the end of the frame despite recursing in the mean time\n\t//however, we still need to invalidate the cache because we only have one 'visframe' field in nodes.\n\n\tif (r_refdef.forcevis)\n\t{\n\t\tvis = r_refdef.forcedvis;\n\n\t\tprv->oldviewclusters[0] = -1;\n\t\tprv->oldviewclusters[1] = -2;\n\t\tcvis = NULL;\n\t}\n\telse\n\t{\n\t\tif (!portal)\n\t\t{\n\t\t\tif (((prv->oldviewclusters[0] == clusters[0] && prv->oldviewclusters[1] == clusters[1]) && !r_novis.ival) || r_novis.ival & 2)\n\t\t\t\tif (cvis)\n\t\t\t\t{\n\t\t\t\t\tq1_visframecount = prv->visframecount;\n\t\t\t\t\treturn cvis;\n\t\t\t\t}\n\n\t\t\tprv->oldviewclusters[0] = clusters[0];\n\t\t\tprv->oldviewclusters[1] = clusters[1];\n\t\t}\n\t\telse\n\t\t{\n\t\t\tprv->oldviewclusters[0] = -1;\n\t\t\tprv->oldviewclusters[1] = -2;\n\t\t\tcvis = NULL;\n\t\t}\n\n\t\tif (r_novis.ival)\n\t\t{\n\t\t\tif (pvsbuf.buffersize < model->pvsbytes)\n\t\t\t\tpvsbuf.buffer = BZ_Realloc(pvsbuf.buffer, pvsbuf.buffersize=model->pvsbytes);\n\t\t\tvis = cvis = pvsbuf.buffer;\n\t\t\tmemset (pvsbuf.buffer, 0xff, pvsbuf.buffersize);\n\n\t\t\tprv->oldviewclusters[0] = -1;\n\t\t\tprv->oldviewclusters[1] = -2;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (clusters[1] >= 0 && clusters[1] != clusters[0])\n\t\t\t{\n\t\t\t\tvis = cvis = model->funcs.ClusterPVS(model, clusters[0], &pvsbuf, PVM_REPLACE);\n\t\t\t\tvis = cvis = model->funcs.ClusterPVS(model, clusters[1], &pvsbuf, PVM_MERGE);\n\t\t\t}\n\t\t\telse\n\t\t\t\tvis = cvis = model->funcs.ClusterPVS(model, clusters[0], &pvsbuf, PVM_FAST);\n\t\t}\n\t}\n\n\tprv->visframecount++;\n\tq1_visframecount = prv->visframecount;\n\n\tif (clusters[0] < 0)\n\t{\n\t\t//to improve spectating, when the camera is in a wall, we ignore any sky leafs.\n\t\t//this prevents seeing the upwards-facing sky surfaces within the sky volumes.\n\t\t//this will not affect inwards facing sky, so sky will basically appear as though it is identical to solid brushes.\n\t\tfor (i=0 ; i<model->numclusters ; i++)\n\t\t{\n\t\t\tif (vis[i>>3] & (1<<(i&7)))\n\t\t\t{\n\t\t\t\tif (model->leafs[i+1].contents == Q1CONTENTS_SKY)\n\t\t\t\t\tcontinue;\n\t\t\t\tnode = (mnode_t *)&model->leafs[i+1];\n\t\t\t\tdo\n\t\t\t\t{\n\t\t\t\t\tif (node->visframe == q1_visframecount)\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tnode->visframe = q1_visframecount;\n\t\t\t\t\tnode = node->parent;\n\t\t\t\t} while (node);\n\t\t\t}\n\t\t}\n\t}\n\telse\n\t{\n\t\tfor (i=0 ; i<model->numclusters ; i++)\n\t\t{\n\t\t\tif (vis[i>>3] & (1<<(i&7)))\n\t\t\t{\n\t\t\t\tnode = (mnode_t *)&model->leafs[i+1];\n\t\t\t\tdo\n\t\t\t\t{\n\t\t\t\t\tif (node->visframe == q1_visframecount)\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tnode->visframe = q1_visframecount;\n\t\t\t\t\tnode = node->parent;\n\t\t\t\t} while (node);\n\t\t\t}\n\t\t}\n\t}\n\treturn vis;\n}\n\nstatic void Q1BSP_PrepareFrame(model_t *model, refdef_t *refdef, int area, int clusters[2], pvsbuffer_t *vis, qbyte **entvis_out, qbyte **surfvis_out)\n{\n\t*entvis_out = Q1BSP_MarkLeaves (model, clusters);\n\n\tif (vis->buffersize < model->pvsbytes)\n\t\tvis->buffer = BZ_Realloc(vis->buffer, vis->buffersize=model->pvsbytes);\n\tq1frustumvis = vis->buffer;\n\tmemset(q1frustumvis, 0, model->pvsbytes);\n\n\tif (model != cl.worldmodel)\n\t\t; //global abuse...\n\telse if (r_refdef.useperspective)\n\t\tQ1BSP_RecursiveWorldNode (model->nodes, 0x1f);\n\telse\n\t\tQ1BSP_OrthoRecursiveWorldNode (model->nodes, 0x1f);\n\t*surfvis_out = q1frustumvis;\n}\n\n\n#endif\n/*\nRendering functions (Client only)\n\n==============================================================================\n\nServer only functions\n*/\n#ifdef HAVE_SERVER\nstatic qbyte *Q1BSP_ClusterPVS (model_t *model, int cluster, pvsbuffer_t *buffer, pvsmerge_t merge);\n\n//does the recursive work of Q1BSP_FatPVS\nstatic void SV_Q1BSP_AddToFatPVS (model_t *mod, const vec3_t org, mnode_t *node, pvsbuffer_t *pvsbuffer)\n{\n\tmplane_t\t*plane;\n\tfloat\td;\n\n\twhile (1)\n\t{\n\t// if this is a leaf, accumulate the pvs bits\n\t\tif (node->contents < 0)\n\t\t{\n\t\t\tif (node->contents != Q1CONTENTS_SOLID)\n\t\t\t{\n\t\t\t\tQ1BSP_ClusterPVS(mod, ((mleaf_t *)node - mod->leafs)-1, pvsbuffer, PVM_MERGE);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tplane = node->plane;\n\t\td = DotProduct (org, plane->normal) - plane->dist;\n\t\tif (d > 8)\n\t\t\tnode = node->children[0];\n\t\telse if (d < -8)\n\t\t\tnode = node->children[1];\n\t\telse\n\t\t{\t// go down both\n\t\t\tSV_Q1BSP_AddToFatPVS (mod, org, node->children[0], pvsbuffer);\n\t\t\tnode = node->children[1];\n\t\t}\n\t}\n}\n\n/*\n=============\nQ1BSP_FatPVS\n\nCalculates a PVS that is the inclusive or of all leafs within 8 pixels of the\ngiven point.\n=============\n*/\nstatic unsigned int Q1BSP_FatPVS (model_t *mod, const vec3_t org, pvsbuffer_t *pvsbuffer, qboolean add)\n{\n\tif (pvsbuffer->buffersize < mod->pvsbytes)\n\t\tpvsbuffer->buffer = BZ_Realloc(pvsbuffer->buffer, pvsbuffer->buffersize=mod->pvsbytes);\n\tif (!add)\n\t\tQ_memset (pvsbuffer->buffer, 0, mod->pvsbytes);\n\tSV_Q1BSP_AddToFatPVS (mod, org, mod->nodes, pvsbuffer);\n\treturn mod->pvsbytes;\n}\n\n#endif\nstatic qboolean Q1BSP_EdictInFatPVS(model_t *mod, const struct pvscache_s *ent, const qbyte *pvs, const int *areas)\n{\n\tint i;\n\n\t//if (areas)areas[0] is the area count... but q1bsp has no areas so we ignore it entirely.\n\n\tif (ent->num_leafs < 0)\n\t\treturn true;\t//it's in too many leafs for us to cope with. Just trivially accept it.\n\n\tfor (i=0 ; i < ent->num_leafs ; i++)\n\t\tif (pvs[ent->leafnums[i] >> 3] & (1 << (ent->leafnums[i]&7) ))\n\t\t\treturn true;\t//we might be able to see this one.\n\n\treturn false;\t//none of this ents leafs were visible, so neither is the ent.\n}\n\n/*\n===============\nSV_FindTouchedLeafs\n\nLinks the edict to the right leafs so we can get it's potential visability.\n===============\n*/\nstatic void Q1BSP_RFindTouchedLeafs (model_t *wm, struct pvscache_s *ent, mnode_t *node, const float *mins, const float *maxs)\n{\n\tmplane_t\t*splitplane;\n\tmleaf_t\t\t*leaf;\n\tint\t\t\tsides;\n\tint\t\t\tleafnum;\n\n// add an efrag if the node is a leaf\n\n\tif (node->contents < 0)\n\t{\n\t\t//ignore solid leafs. this should include leaf 0 (which has no pvs info)\n\t\tif (node->contents == Q1CONTENTS_SOLID)\n\t\t\treturn;\n\n\t\tif ((unsigned)ent->num_leafs >= MAX_ENT_LEAFS)\n\t\t{\n\t\t\tent->num_leafs = -1;\t//too many. mark it as such so we can trivially accept huge mega-big brush models.\n\t\t\treturn;\n\t\t}\n\n\t\tleaf = (mleaf_t *)node;\n\t\tleafnum = leaf - wm->leafs - 1;\n\n\t\tent->leafnums[ent->num_leafs] = leafnum;\n\t\tent->num_leafs++;\n\t\treturn;\n\t}\n\n// NODE_MIXED\n\n\tsplitplane = node->plane;\n\tsides = BOX_ON_PLANE_SIDE(mins, maxs, splitplane);\n\n// recurse down the contacted sides\n\tif (sides & 1)\n\t\tQ1BSP_RFindTouchedLeafs (wm, ent, node->children[0], mins, maxs);\n\n\tif (sides & 2)\n\t\tQ1BSP_RFindTouchedLeafs (wm, ent, node->children[1], mins, maxs);\n}\nstatic void Q1BSP_FindTouchedLeafs(model_t *mod, struct pvscache_s *ent, const float *mins, const float *maxs)\n{\n\tent->num_leafs = 0;\n\tif (mins && maxs)\n\t\tQ1BSP_RFindTouchedLeafs (mod, ent, mod->nodes, mins, maxs);\n}\n\n\n/*\nServer only functions\n\n==============================================================================\n\nPVS type stuff\n*/\n\n/*\n===================\nMod_DecompressVis\n===================\n*/\nstatic qbyte *Q1BSP_DecompressVis (qbyte *in, model_t *model, qbyte *decompressed, unsigned int buffersize, qboolean merge)\n{\n\tint\t\tc;\n\tqbyte\t*out;\n\tint\t\trow;\n\n\trow = (model->numclusters+7)>>3;\n\tout = decompressed;\n\n\tif (buffersize < row)\n\t\trow = buffersize;\n\n\tif (!in)\n\t{\t// no vis info, so make all visible\n\t\twhile (row)\n\t\t{\n\t\t\t*out++ = 0xff;\n\t\t\trow--;\n\t\t}\n\t\treturn decompressed;\n\t}\n\n\tif (merge)\n\t{\n\t\tdo\n\t\t{\n\t\t\tif (*in)\n\t\t\t{\n\t\t\t\t*out++ |= *in++;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tout += in[1];\n\t\t\tin += 2;\n\t\t} while (out - decompressed < row);\n\t}\n\telse\n\t{\n\t\tdo\n\t\t{\n\t\t\tif (*in)\n\t\t\t{\n\t\t\t\t*out++ = *in++;\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tc = in[1];\n\t\t\tin += 2;\n\t\t\tif ((out - decompressed) + c > row)\n\t\t\t{\n\t\t\t\tc = row - (out - decompressed);\n\t\t\t\tCon_DPrintf (\"warning: Vis decompression overrun\\n\");\n\t\t\t}\n\t\t\twhile (c)\n\t\t\t{\n\t\t\t\t*out++ = 0;\n\t\t\t\tc--;\n\t\t\t}\n\t\t} while (out - decompressed < row);\n\t}\n\n\treturn decompressed;\n}\n\nstatic pvsbuffer_t\tmod_novis;\nstatic pvsbuffer_t\tmod_tempvis;\n\nvoid Q1BSP_Shutdown(void)\n{\n\tZ_Free(mod_novis.buffer);\n\tmemset(&mod_novis, 0, sizeof(mod_novis));\n\tZ_Free(mod_tempvis.buffer);\n\tmemset(&mod_tempvis, 0, sizeof(mod_tempvis));\n}\n\n//pvs is 1-based. clusters are 0-based. otherwise, q1bsp has a 1:1 mapping.\nstatic qbyte *Q1BSP_ClusterPVS (model_t *model, int cluster, pvsbuffer_t *buffer, pvsmerge_t merge)\n{\n\tif (cluster == -1)\n\t{\n\t\tif (merge == PVM_FAST)\n\t\t{\n\t\t\tif (mod_novis.buffersize < model->pvsbytes)\n\t\t\t{\n\t\t\t\tmod_novis.buffer = BZ_Realloc(mod_novis.buffer, mod_novis.buffersize=model->pvsbytes);\n\t\t\t\tmemset(mod_novis.buffer, 0xff, mod_novis.buffersize);\n\t\t\t}\n\t\t\treturn mod_novis.buffer;\n\t\t}\n\t\tif (buffer->buffersize < model->pvsbytes)\n\t\t\tbuffer->buffer = BZ_Realloc(buffer->buffer, buffer->buffersize=model->pvsbytes);\n\t\tmemset(buffer->buffer, 0xff, model->pvsbytes);\n\t\treturn buffer->buffer;\n\t}\n\n\tif (merge == PVM_FAST && model->pvs)\n\t\treturn model->pvs + cluster * model->pvsbytes;\n\n\tcluster++;\n\n\tif (!buffer)\n\t\tbuffer = &mod_tempvis;\n\n\tif (buffer->buffersize < model->pvsbytes)\n\t\tbuffer->buffer = BZ_Realloc(buffer->buffer, buffer->buffersize=model->pvsbytes);\n\n\treturn Q1BSP_DecompressVis (model->leafs[cluster].compressed_vis, model, buffer->buffer, buffer->buffersize, merge==PVM_MERGE);\n}\n\nstatic qbyte *Q1BSP_ClusterPHS (model_t *model, int cluster, pvsbuffer_t *buffer)\n{\n\treturn model->phs + cluster*model->pvsbytes;\n}\n\n//returns the leaf number, which is used as a bit index into the pvs.\nstatic int Q1BSP_LeafnumForPoint (model_t *model, vec3_t p)\n{\n\tmnode_t\t\t*node;\n\tfloat\t\td;\n\tmplane_t\t*plane;\n\n\tif (!model)\n\t{\n\t\tSys_Error (\"Q1BSP_LeafnumForPoint: bad model\");\n\t}\n\tif (!model->nodes)\n\t\treturn 0;\n\n\tnode = model->nodes;\n\twhile (1)\n\t{\n\t\tif (node->contents < 0)\n\t\t\treturn (mleaf_t *)node - model->leafs;\n\t\tplane = node->plane;\n\t\td = DotProduct (p,plane->normal) - plane->dist;\n\t\tif (d > 0)\n\t\t\tnode = node->children[0];\n\t\telse\n\t\t\tnode = node->children[1];\n\t}\n\n\treturn 0;\t// never reached\n}\n\nmleaf_t *Q1BSP_LeafForPoint (model_t *model, vec3_t p)\n{\n\treturn model->leafs + Q1BSP_LeafnumForPoint(model, p);\n}\n\nstatic void Q1BSP_ClustersInSphere_Union(mleaf_t *firstleaf, const vec3_t center, float radius, mnode_t *node, qbyte *out, qbyte *unionwith)\n{\t//this is really for rtlights.\n\tfloat\t\tt1, t2;\n\tmplane_t\t*plane;\n\twhile (1)\n\t{\n\t\tif (node->contents < 0)\n\t\t{\t//leaf! mark/merge it.\n\t\t\tsize_t c = (mleaf_t *)node - firstleaf;\n\t\t\tif (c == -1)\n\t\t\t\treturn;\n\t\t\tif (unionwith)\n\t\t\t\tout[c>>3] |= (1<<(c&7)) & unionwith[c>>3];\n\t\t\telse\n\t\t\t\tout[c>>3] |= (1<<(c&7));\n\t\t\treturn;\n\t\t}\n\n\t\tplane = node->plane;\n\t\tif (plane->type < 3)\n\t\t\tt1 = center[plane->type] - plane->dist;\n\t\telse\n\t\t\tt1 = DotProduct (plane->normal, center) - plane->dist;\n\t\tt2 = t1 - radius;\n\t\tt1 = t1 + radius;\n\n\t\t//if the sphere is fully to one side, only walk that side.\n\t\tif (t1 > 0 && t2 > 0)\n\t\t{\n\t\t\tnode = node->children[0];\n\t\t\tcontinue;\n\t\t}\n\t\tif (t1 < 0 && t2 < 0)\n\t\t{\n\t\t\tnode = node->children[1];\n\t\t\tcontinue;\n\t\t}\n\n\t\t//both sides are within the sphere\n\t\tQ1BSP_ClustersInSphere_Union(firstleaf, center, radius, node->children[0], out, unionwith);\n\t\tnode = node->children[1];\n\t\tcontinue;\n\t}\n}\nstatic qbyte *Q1BSP_ClustersInSphere(model_t *mod, const vec3_t center, float radius, pvsbuffer_t *fte_restrict pvsbuffer, const qbyte *fte_restrict unionwith)\n{\n\tif (!mod)\n\t\tSys_Error (\"Q1BSP_ClustersInSphere: bad model\");\n\tif (!mod->nodes)\n\t\treturn NULL;\n\n\tif (pvsbuffer->buffersize < mod->pvsbytes)\n\t\tpvsbuffer->buffer = BZ_Realloc(pvsbuffer->buffer, pvsbuffer->buffersize=mod->pvsbytes);\n\tQ_memset (pvsbuffer->buffer, 0, mod->pvsbytes);\n\tQ1BSP_ClustersInSphere_Union(mod->leafs+1, center, radius, mod->nodes, pvsbuffer->buffer, NULL);//unionwith);\n\treturn pvsbuffer->buffer;\n}\n\n//returns the leaf number, which is used as a direct bit index into the pvs.\n//-1 for invalid\nstatic int Q1BSP_ClusterForPoint (model_t *model, const vec3_t p, int *area)\n{\n\tmnode_t\t\t*node;\n\tfloat\t\td;\n\tmplane_t\t*plane;\n\n\tif (!model)\n\t{\n\t\tSys_Error (\"Q1BSP_ClusterForPoint: bad model\");\n\t}\n\tif (area)\n\t\t*area = 0;\t//no areas with q1bsp.\n\tif (!model->nodes)\n\t\treturn -1;\n\n\tnode = model->nodes;\n\twhile (1)\n\t{\n\t\tif (node->contents < 0)\n\t\t\treturn ((mleaf_t *)node - model->leafs) - 1;\n\t\tplane = node->plane;\n\t\td = DotProduct (p,plane->normal) - plane->dist;\n\t\tif (d > 0)\n\t\t\tnode = node->children[0];\n\t\telse\n\t\t\tnode = node->children[1];\n\t}\n\n\treturn -1;\t// never reached\n}\n\n\nstatic void Q1BSP_InfoForPoint (struct model_s *mod, vec3_t pos, int *area, int *cluster, unsigned int *contentbits)\n{\n\tmnode_t\t\t*node;\n\tfloat\t\td;\n\tmplane_t\t*plane;\n\n\t*area = 0;\t//no areas with q1bsp.\n\t*cluster = -1;\n\t*contentbits = FTECONTENTS_SOLID;\n\tif (!mod->nodes)\n\t\treturn;\n\n\tnode = mod->nodes;\n\twhile (1)\n\t{\n\t\tif (node->contents < 0)\n\t\t{\n\t\t\t*cluster = ((mleaf_t *)node - mod->leafs) - 1;\n\t\t\t*contentbits = Q1BSP_TranslateContents(((mleaf_t *)node)->contents);\n\t\t\treturn;\t//we're done\n\t\t}\n\t\tplane = node->plane;\n\t\td = DotProduct (pos,plane->normal) - plane->dist;\n\t\tif (d > 0)\n\t\t\tnode = node->children[0];\n\t\telse\n\t\t\tnode = node->children[1];\n\t}\n}\n\n\n/*\nPVS type stuff\n\n==============================================================================\n\nInit stuff\n*/\n\n\nvoid Q1BSP_Init(void)\n{\n}\n\n//sets up the functions a server needs.\n//fills in bspfuncs_t\nvoid Q1BSP_SetModelFuncs(model_t *mod)\n{\n#ifdef HAVE_SERVER\n\tmod->funcs.FatPVS\t\t\t\t= Q1BSP_FatPVS;\n#endif\n\tmod->funcs.EdictInFatPVS\t\t= Q1BSP_EdictInFatPVS;\n\tmod->funcs.FindTouchedLeafs\t\t= Q1BSP_FindTouchedLeafs;\n\n\tmod->funcs.ClustersInSphere\t\t= Q1BSP_ClustersInSphere;\n\tmod->funcs.ClusterForPoint\t\t= Q1BSP_ClusterForPoint;\n\tmod->funcs.ClusterPVS\t\t\t= Q1BSP_ClusterPVS;\n\tmod->funcs.ClusterPHS\t\t\t= Q1BSP_ClusterPHS;\n\tmod->funcs.NativeTrace\t\t\t= Q1BSP_Trace;\n\tmod->funcs.PointContents\t\t= Q1BSP_PointContents;\n\n\tmod->funcs.InfoForPoint\t\t\t= Q1BSP_InfoForPoint;\n#ifdef HAVE_CLIENT\n\tmod->funcs.LightPointValues\t\t= GLQ1BSP_LightPointValues;\n\tmod->funcs.MarkLights\t\t\t= Q1BSP_MarkLights;\n\tmod->funcs.StainNode\t\t\t= Q1BSP_StainNode;\n#ifdef RTLIGHTS\n\tmod->funcs.GenerateShadowMesh\t= Q1BSP_GenerateShadowMesh;\n#endif\n\tmod->funcs.PrepareFrame\t\t\t= Q1BSP_PrepareFrame;\n\n\t{\n\t\tstruct q1bspprv_s *prv = mod->meshinfo = ZG_Malloc(&mod->memgroup, sizeof(struct q1bspprv_s));\n\t\tprv->oldviewclusters[0] = -1;\t//make sure its reset properly.\n\t\tprv->oldviewclusters[1] = -1;\n\t}\n#endif\n}\n#endif\n\n/*\nInit stuff\n\n==============================================================================\n\nBSPX Stuff\n*/\n#include \"fs.h\"\n\ntypedef struct {\n    char lumpname[24]; // up to 23 chars, zero-padded\n    unsigned int fileofs;  // from file start\n    unsigned int filelen;\n} bspx_lump_t;\nstruct bspx_header_s {\n    char id[4];  // 'BSPX'\n    unsigned int numlumps;\n\tbspx_lump_t lumps[1];\n};\n//supported lumps (read specs/bspx.txt for more details):\n//RGBLIGHTING (.lit)\n//LIGHTING_E5BGR9 (hdr lit)\n//LIGHTINGDIR (.lux)\n//LMSHIFT (lightmap scaling, obsoleted bby DECOUPLED_LM)\n//LMOFFSET (lightmap scaling, redundant without LMSHIFT)\n//LMSTYLE (for when 4 styles per face are not enough)\n//LMSTYLE16 (for when you need more than 256 different lightswitches)\n//VERTEXNORMALS (smooth specular)\n//BRUSHLIST (no hull size issues)\n//ENVMAP (cubemaps)\n//SURFENVMAP (cubemaps)\n//FACENORMALS (because Quetoo's normals were rejected by ericw for some reason)\n//DECOUPLED_LM (upgraded alternative to LM_SHIFT with float scaling and explicit lm sizes)\n//LIGHTGRID_OCTREE (lightgrid alternative to floor-based model lighting, but still stuck with 4 8bit ldr styles)\nvoid *BSPX_FindLump(bspx_header_t *bspxheader, void *mod_base, char *lumpname, size_t *lumpsize)\n{\n\tint i;\n\t*lumpsize = 0;\n\tif (!bspxheader)\n\t\treturn NULL;\n\n\tfor (i = 0; i < bspxheader->numlumps; i++)\n\t{\n\t\tif (!strncmp(bspxheader->lumps[i].lumpname, lumpname, 24))\n\t\t{\n\t\t\t*lumpsize = bspxheader->lumps[i].filelen;\n\t\t\treturn (char*)mod_base + bspxheader->lumps[i].fileofs;\n\t\t}\n\t}\n\treturn NULL;\n}\nbspx_header_t *BSPX_Setup(model_t *mod, char *filebase, size_t filelen, lump_t *lumps, size_t numlumps)\n{\n\tsize_t i;\n\tsize_t offs = 0;\n\tbspx_header_t *h;\n\n\tfor (i = 0; i < numlumps; i++, lumps++)\n\t{\n\t\tif (offs < lumps->fileofs + lumps->filelen)\n\t\t\toffs = lumps->fileofs + lumps->filelen;\n\t}\n\toffs = (offs + 3) & ~3;\n\tif (offs + sizeof(*h) > filelen)\n\t\th = NULL; /*no space for it*/\n\telse\n\t{\n\t\th = (bspx_header_t*)(filebase + offs);\n\n\t\ti = LittleLong(h->numlumps);\n\t\t/*verify the header*/\n\t\tif (*(int*)h->id != (('B'<<0)|('S'<<8)|('P'<<16)|('X'<<24)) ||\n\t\t\ti < 0 ||\n\t\t\toffs + sizeof(*h) + sizeof(h->lumps[0])*(i-1) > filelen)\n\t\t\th = NULL;\n\t\telse\n\t\t{\n\t\t\th->numlumps = i;\n\t\t\twhile(i-->0)\n\t\t\t{\n\t\t\t\th->lumps[i].fileofs = LittleLong(h->lumps[i].fileofs);\n\t\t\t\th->lumps[i].filelen = LittleLong(h->lumps[i].filelen);\n\t\t\t\tif (h->lumps[i].fileofs + h->lumps[i].filelen > filelen)\n\t\t\t\t\treturn NULL;\t//some sort of corruption/truncation.\n\n\t\t\t\tif (offs < h->lumps[i].fileofs + h->lumps[i].filelen)\n\t\t\t\t\toffs = h->lumps[i].fileofs + h->lumps[i].filelen;\n\t\t\t}\n\t\t}\n\t}\n\n\tif (offs < filelen && mod && !mod->archive && mod_loadmappackages.ival && filelen-offs > 22)//end-of-central-dir being 22 bytes sets a minimum zip size, which should slightly reduce false-positives.\n\t{\t//we have some sort of trailing junk... is it a zip?...\n\t\tMod_LoadMapArchive(mod, filebase+offs, filelen-offs);\n\t}\n\n\treturn h;\n}\n\n#ifndef HAVE_CLIENT\nvoid BSPX_LoadEnvmaps(model_t *mod, bspx_header_t *bspx, void *mod_base)\n{\n}\n#else\nvoid BSPX_LoadEnvmaps(model_t *mod, bspx_header_t *bspx, void *mod_base)\n{\n\tunsigned int *envidx, idx;\n\tsize_t i;\n\tchar base[MAX_QPATH];\n\tchar imagename[MAX_QPATH];\n\tmenvmap_t *out;\n\tsize_t count;\n\tdenvmap_t *in = BSPX_FindLump(bspx, mod_base, \"ENVMAP\", &count);\n\tmod->envmaps = NULL;\n\tmod->numenvmaps = 0;\n\tif (!mod_loadsurfenvmaps.ival)\n\t\treturn;\n\tif (count%sizeof(*in))\n\t\treturn;\t//erk\n\tcount /= sizeof(*in);\n\tif (!count)\n\t\treturn;\n\tout = ZG_Malloc(&mod->memgroup, sizeof(*out)*count);\n\n\tmod->envmaps = out;\n\tmod->numenvmaps = count;\n\n\tCOM_FileBase(mod->name, base, sizeof(base));\n\tfor (i = 0; i < count; i++)\n\t{\n\t\tout[i].origin[0] = LittleFloat(in[i].origin[0]);\n\t\tout[i].origin[1] = LittleFloat(in[i].origin[1]);\n\t\tout[i].origin[2] = LittleFloat(in[i].origin[2]);\n\t\tout[i].cubesize = LittleLong(in[i].cubesize);\n\n\t\tQ_snprintfz(imagename, sizeof(imagename), \"textures/env/%s_%i_%i_%i\", base, (int)mod->envmaps[i].origin[0], (int)mod->envmaps[i].origin[1], (int)mod->envmaps[i].origin[2]);\n\t\tout[i].image = Image_GetTexture(imagename, NULL, IF_TEXTYPE_CUBE|IF_LINEAR|IF_NOREPLACE, NULL, NULL, out[i].cubesize, out[i].cubesize, PTI_INVALID);\n\t}\n\n\n\n\t//now update surface lists.\n\tenvidx = BSPX_FindLump(bspx, mod_base, \"SURFENVMAP\", &i);\n\tif (i/sizeof(*envidx) == mod->numsurfaces)\n\t{\n\t\tfor (i = 0; i < mod->numsurfaces; i++)\n\t\t{\n\t\t\tidx = LittleLong(envidx[i]);\n\t\t\tif (idx < (unsigned int)count)\n\t\t\t\tmod->surfaces[i].envmap = out[idx].image;\n\t\t}\n\t}\n}\n\nstruct bspxrw\n{\n\tconst char *fname;\n\tchar *origfile;\n\tqofs_t origsize;\n\tint lumpofs;\n\tfromgame_t fg;\n\n\tsize_t corelumps;\n\tsize_t totallumps;\n\n\tvoid *archivedata;\n\tqofs_t archivesize;\n\n\tstruct\n\t{\n\t\tchar lumpname[24]; // up to 23 chars, zero-padded\n\t\tvoid *data;  // from file start\n\t\tqofs_t filelen;\n\t} *lumps;\n};\nstatic void Mod_BSPXRW_Free(struct bspxrw *ctx)\n{\n\tFS_FreeFile(ctx->origfile);\n\tZ_Free(ctx->lumps);\n\tctx->corelumps = ctx->totallumps = 0;\n\tctx->origfile = NULL;\n}\nstatic void Mod_BSPXRW_Write(struct bspxrw *ctx)\n{\n#if 1\n\tvfsfile_t *f = FS_OpenVFS(ctx->fname, \"wb\", FS_GAMEONLY);\n\tif (f)\n\t{\n\t\tqofs_t bspxofs;\n\t\tsize_t i, j;\n\t\tint pad, paddata = 0;\n\t\tint nxlumps = ctx->totallumps-ctx->corelumps;\n\t\tlump_t *lumps = alloca(sizeof(*lumps)*ctx->corelumps);\n\t\tbspx_lump_t *xlumps = alloca(sizeof(*xlumps)*(ctx->totallumps-ctx->corelumps));\n\t\t//bsp header info\n\t\tVFS_WRITE(f, ctx->origfile, ctx->lumpofs);\n\t\tVFS_WRITE(f, lumps, sizeof(lumps[0])*ctx->corelumps);\t//placeholder\n\t\t//orig lumps\n\t\tfor (i = 0; i < ctx->corelumps; i++)\n\t\t{\n\t\t\tlumps[i].fileofs = VFS_TELL(f);\n\t\t\tlumps[i].filelen = ctx->lumps[i].filelen;\n\n\t\t\tVFS_WRITE(f, ctx->lumps[i].data, ctx->lumps[i].filelen);\n\t\t\t//ALL lumps must be 4-aligned, so pad if needed.\n\t\t\tpad = ((ctx->lumps[i].filelen+3)&~3)-ctx->lumps[i].filelen;\n\t\t\tVFS_WRITE(f, &paddata, pad);\n\t\t}\n\t\t//bspx header\n\t\tVFS_WRITE(f, \"BSPX\", 4);\n\t\tVFS_WRITE(f, &nxlumps, sizeof(nxlumps));\n\t\tbspxofs = VFS_TELL(f);\n\t\tVFS_WRITE(f, xlumps, sizeof(xlumps[0])*(ctx->totallumps-ctx->corelumps)); //placeholder\n\t\t//bspx data\n\t\tfor (i = 0; i < nxlumps; i++)\n\t\t{\n\t\t\tj = ctx->corelumps+i;\n\t\t\txlumps[i].fileofs = VFS_TELL(f);\n\t\t\txlumps[i].filelen = ctx->lumps[j].filelen;\n\t\t\tmemcpy(xlumps[i].lumpname, ctx->lumps[j].lumpname, sizeof(xlumps[i].lumpname));\n\n\t\t\tVFS_WRITE(f, ctx->lumps[j].data, ctx->lumps[j].filelen);\n\t\t\t//ALL lumps must be 4-aligned, so pad if needed.\n\t\t\tpad = ((ctx->lumps[j].filelen+3)&~3)-ctx->lumps[j].filelen;\n\t\t\tVFS_WRITE(f, &paddata, pad);\n\t\t}\n\n\t\t//now reinsert any concatenated zip.\n\t\tVFS_WRITE(f, ctx->archivedata, ctx->archivesize);\n\n\t\t//now rewrite both sets of offsets.\n\t\tVFS_SEEK(f, ctx->lumpofs);\n\t\tVFS_WRITE(f, lumps, sizeof(lumps[0])*ctx->corelumps);\n\t\tVFS_SEEK(f, bspxofs);\n\t\tVFS_WRITE(f, xlumps, sizeof(xlumps[0])*(ctx->totallumps-ctx->corelumps));\n\n\t\tVFS_CLOSE(f);\n\t}\n#endif\n\tMod_BSPXRW_Free(ctx);\n}\n\nstatic qboolean Mod_BSPXRW_SetLump(struct bspxrw *ctx, const char *lumpname, void *data, size_t datasize)\n{\n\tint i;\n\tfor (i = 0; i < ctx->totallumps; i++)\n\t{\n\t\tif (!strcmp(ctx->lumps[i].lumpname, lumpname))\n\t\t{\t//replace the existing lump\n\t\t\tif (ctx->lumps[i].filelen == datasize && !memcmp(ctx->lumps[i].data, data, datasize))\n\t\t\t\treturn false;\t//nothing changed.\n\t\t\tctx->lumps[i].data = data;\n\t\t\tctx->lumps[i].filelen = datasize;\n\t\t\treturn true;\n\t\t}\n\t}\n\n\tZ_ReallocElements((void**)&ctx->lumps, &ctx->totallumps, ctx->totallumps+1, sizeof(*ctx->lumps));\n\tQ_strncpyz(ctx->lumps[i].lumpname, lumpname, sizeof(ctx->lumps[i].lumpname));\n\tctx->lumps[i].data = data;\n\tctx->lumps[i].filelen = datasize;\n\treturn true;\n}\n\nstatic qboolean Mod_BSPXRW_Read(struct bspxrw *ctx, const char *fname)\n{\n\tint i;\n\tlump_t *l;\n\tconst char **corelumpnames = NULL;\n\tbspx_header_t *bspxheader;\n#ifdef Q3BSPS\n\tstatic const char *q3corelumpnames[Q3LUMPS_TOTAL] = {\"entities\",\"shaders\",\"planes\",\"nodes\",\"leafs\",\"leafsurfs\",\"leafbrushes\",\"submodels\",\"brushes\",\"brushsides\",\"verts\",\"indexes\",\"fogs\",\"surfaces\",\"lightmaps\",\"lightgrid\",\"visibility\"\n\t\t#ifdef RFBSPS\n\t\t\t,\"lightgrididx\"\n\t\t#endif\n\t\t};\n#endif\n#ifdef Q2BSPS\n\tstatic const char *q2corelumpnames[Q2HEADER_LUMPS] = {\"entities\",\"planes\",\"vertexes\",\"visibility\",\"nodes\",\"texinfo\",\"faces\",\"lighting\",\"leafs\",\"leaffaces\",\"leafbrushes\",\"edges\",\"surfedges\",\"models\",\"brushes\",\"brushsides\",\"pop\",\"areas\",\"areaportals\"};\n#endif\n\tstatic const char *q1corelumpnames[HEADER_LUMPS] = {\"entities\",\"planes\",\"textures\",\"vertexes\",\"visibility\",\"nodes\",\"texinfo\",\"faces\",\"lighting\",\"clipnodes\",\"leafs\",\"marksurfaces\",\"edges\",\"surfedges\",\"models\"};\n\tctx->archivedata = NULL;\n\tctx->archivesize = 0;\n\tctx->fname = fname;\n\tctx->origfile = FS_MallocFile(ctx->fname, FS_GAME, &ctx->origsize);\n\tif (!ctx->origfile)\n\t\treturn false;\n\tctx->lumps = 0;\n\tctx->totallumps = 0;\n\n\tif (ctx->origsize < 4)\n\t\ti = 0;\n\telse\n\t\ti = LittleLong(*(int*)ctx->origfile);\n\tswitch(i)\n\t{\n\tcase 29:\n\tcase 30:\n\t\tctx->fg = ((i==30)?fg_halflife:fg_quake);\n\t\tctx->lumpofs = 4;\n\t\tctx->corelumps = HEADER_LUMPS;\n\t\tcorelumpnames = q1corelumpnames;\n\t\tbreak;\n\tcase ('I'<<0)+('B'<<8)+('S'<<16)+('P'<<24):\t//starting with q2.\n\t\ti = LittleLong(*(int*)(ctx->origfile+4));\n\t\tctx->lumpofs = 8;\n\t\tswitch(i)\n\t\t{\n#ifdef Q2BSPS\n\t\tcase BSPVERSION_Q2:\n//\t\tcase BSPVERSION_Q2W:\n\t\t\tctx->fg = fg_quake2;\n\t\t\tctx->corelumps = Q2HEADER_LUMPS;\n\t\t\tcorelumpnames = q2corelumpnames;\n\t\t\tbreak;\n#endif\n#ifdef Q3BSPS\n\t\tcase BSPVERSION_Q3:\n\t\tcase BSPVERSION_RTCW:\n\t\t\tctx->fg = fg_quake3;\n\t\t\tctx->corelumps = 17;\n\t\t\tcorelumpnames = q3corelumpnames;\n\t\t\tbreak;\n#endif\n\t\tdefault:\n\t\t\tMod_BSPXRW_Free(ctx);\n\t\t\tCon_Printf(CON_ERROR\"%s: Unknown 'IBSP' revision\\n\", fname);\n\t\t\treturn false;\n\t\t}\n\t\tbreak;\n#ifdef RFBSPS\n\tcase ('R'<<0)+('B'<<8)+('S'<<16)+('P'<<24):\n\tcase ('F'<<0)+('B'<<8)+('S'<<16)+('P'<<24):\n\t\ti = LittleLong(*(int*)(ctx->origfile+4));\n\t\tctx->lumpofs = 8;\n\t\tswitch(i)\n\t\t{\n\t\tcase BSPVERSION_RBSP:\t//both rbsp+fbsp are version 1, with the only difference being lightmap sizes.\n\t\t\tctx->fg = fg_quake3;\n\t\t\tctx->corelumps = 18;\n\t\t\tcorelumpnames = q3corelumpnames;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tMod_BSPXRW_Free(ctx);\n\t\t\tCon_Printf(CON_ERROR\"%s: Unknown 'RBSP'/'FBSP' revision\\n\", fname);\n\t\t\treturn false;\n\t\t}\n\t\tbreak;\n#endif\n\tdefault:\n\t\tMod_BSPXRW_Free(ctx);\n\t\tCon_Printf(CON_ERROR\"%s: Unknown file magic\\n\", fname);\n\t\treturn false;\n\t}\n\n\tl = (lump_t*)(ctx->origfile+ctx->lumpofs);\n\tfor (i = 0; i < ctx->corelumps; i++)\n\t{\n\t\tZ_ReallocElements((void**)&ctx->lumps, &ctx->totallumps, ctx->totallumps+1, sizeof(*ctx->lumps));\n\t\tctx->lumps[ctx->totallumps-1].data = ctx->origfile+l[i].fileofs;\n\t\tctx->lumps[ctx->totallumps-1].filelen = l[i].filelen;\n\t\tif (corelumpnames)\n\t\t\tQ_snprintfz(ctx->lumps[ctx->totallumps-1].lumpname, sizeof(ctx->lumps[0].lumpname), \"%s\", corelumpnames[i]);\n\t\telse\n\t\t\tQ_snprintfz(ctx->lumps[ctx->totallumps-1].lumpname, sizeof(ctx->lumps[0].lumpname), \"lump%u\", i);\n\t}\n\n\tbspxheader = BSPX_Setup(NULL, ctx->origfile, ctx->origsize, l, ctx->corelumps);\n\tif (bspxheader)\n\t{\n\t\tfor (i = 0; i < bspxheader->numlumps; i++)\n\t\t{\n\t\t\tZ_ReallocElements((void**)&ctx->lumps, &ctx->totallumps, ctx->totallumps+1, sizeof(*ctx->lumps));\n\t\t\tctx->lumps[ctx->totallumps-1].data = ctx->origfile+bspxheader->lumps[i].fileofs;\n\t\t\tctx->lumps[ctx->totallumps-1].filelen = bspxheader->lumps[i].filelen;\n\t\t\tmemcpy(ctx->lumps[ctx->totallumps-1].lumpname, bspxheader->lumps[i].lumpname, sizeof(ctx->lumps[0].lumpname));\n\t\t}\n\t}\n\treturn true;\n}\n\nstatic unsigned int Mod_NearestCubeForSurf(msurface_t *surf, denvmap_t *envmap, size_t nenvmap)\n{\t//this is slow, yes.\n\tsize_t n, v;\n\tunsigned int best = ~0;\n\tfloat bestdist = FLT_MAX, dist;\n\tvec3_t diff, mins, maxs, mid;\n\n\tif (surf->mesh && surf->mesh->numvertexes)\n\t{\n\t\tVectorCopy(surf->mesh->xyz_array[0], mins);\n\t\tVectorCopy(surf->mesh->xyz_array[0], maxs);\n\t\tfor (v = 1; v < surf->mesh->numvertexes; v++)\n\t\t\tAddPointToBounds(surf->mesh->xyz_array[v], mins, maxs);\n\t\tVectorAvg(mins, maxs, mid);\n\n\t\tfor (n = 0; n < nenvmap; n++)\n\t\t{\n\t\t\tVectorSubtract(envmap[n].origin, mid, diff);\n#if 0\n\t\t\t//axial distance\n\t\t\tdist = fabs(diff[0]) + fabs(diff[1]) + fabs(diff[2]);\n#else\n\t\t\t//radial distance (squared)\n\t\t\tdist = DotProduct(diff,diff);\n#endif\n\t\t\tif (bestdist > dist)\n\t\t\t{\n\t\t\t\tbest = n;\n\t\t\t\tbestdist = dist;\n\t\t\t}\n\t\t}\n\t}\n\treturn best;\n}\n\nstatic int QDECL envmapsort(const void *av, const void *bv)\n{\t//sorts cubemaps in order of size, to make texturearrays easier, if ever. The loader can then just make runs.\n\tconst denvmap_t *a=av, *b=bv;\n\tif (a->cubesize == b->cubesize)\n\t\treturn 0;\n\tif (a->cubesize > b->cubesize)\n\t\treturn 1;\n\treturn -1;\n}\n\nvoid SCR_ScreenShot_Cubemap(vec3_t origin, int size);\n\nstatic void Mod_FindCubemaps(qboolean build)\n{\n\tstruct bspxrw bspctx;\n\tif (Mod_BSPXRW_Read(&bspctx, cl.worldmodel->name))\n\t{\n\t\tconst char *entlump = Mod_GetEntitiesString(cl.worldmodel), *lmp;\n\t\tint nest;\n\t\tchar key[1024];\n\t\tchar value[1024];\n\n\t\tqboolean isenvmap;\n\t\tfloat size;\n\t\tvec3_t origin;\n\n\t\tdenvmap_t *envmap = NULL; //*nenvmap\n\t\tsize_t\tnenvmap = 0;\n\t\tunsigned int *envmapidx = NULL;\t//*numsurfaces\n\t\tsize_t nenvmapidx = 0, i;\n\n\t\t//find targetnames, and store their origins so that we can deal with spotlights.\n\t\tfor (lmp = entlump; ;)\n\t\t{\n\t\t\tlmp = COM_Parse(lmp);\n\t\t\tif (com_token[0] != '{')\n\t\t\t\tbreak;\n\n\t\t\tisenvmap = false;\n\t\t\tsize = 128;\n\t\t\tVectorClear(origin);\n\n\t\t\tnest = 1;\n\t\t\twhile (1)\n\t\t\t{\n\t\t\t\tlmp = COM_ParseOut(lmp, key, sizeof(key));\n\t\t\t\tif (!lmp)\n\t\t\t\t\tbreak; // error\n\t\t\t\tif (key[0] == '{')\n\t\t\t\t{\n\t\t\t\t\tnest++;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (key[0] == '}')\n\t\t\t\t{\n\t\t\t\t\tnest--;\n\t\t\t\t\tif (!nest)\n\t\t\t\t\t\tbreak; // end of entity\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (nest!=1)\n\t\t\t\t\tcontinue;\n\t\t\t\tif (key[0] == '_')\n\t\t\t\t\tmemmove(key, key+1, strlen(key));\n\t\t\t\twhile (key[strlen(key)-1] == ' ') // remove trailing spaces\n\t\t\t\t\tkey[strlen(key)-1] = 0;\n\t\t\t\tlmp = COM_ParseOut(lmp, value, sizeof(value));\n\t\t\t\tif (!lmp)\n\t\t\t\t\tbreak; // error\n\n\t\t\t\t// now that we have the key pair worked out...\n\t\t\t\tif (!strcmp(\"classname\", key) && !strcmp(value, \"env_cubemap\"))\n\t\t\t\t\tisenvmap = true;\n\t\t\t\telse if (!strcmp(\"origin\", key))\n\t\t\t\t\tsscanf(value, \"%f %f %f\", &origin[0], &origin[1], &origin[2]);\n\t\t\t\telse if (!strcmp(\"size\", key))\n\t\t\t\t\tsscanf(value, \"%f\", &size);\n\t\t\t}\n\n\t\t\tif (isenvmap)\n\t\t\t{\n\t\t\t\tint e = nenvmap;\n\t\t\t\tif (ZF_ReallocElements((void**)&envmap, &nenvmap, nenvmap+1, sizeof(*envmap)))\n\t\t\t\t{\n\t\t\t\t\tVectorCopy(origin, envmap[e].origin);\n\t\t\t\t\tenvmap[e].cubesize = size;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (nenvmap)\n\t\t{\n\t\t\tqsort(envmap, nenvmap, sizeof(*envmap), envmapsort);\t//sort them by size\n\t\t\tif (ZF_ReallocElements((void**)&envmapidx, &nenvmapidx, cl.worldmodel->numsurfaces, sizeof(*envmapidx)))\n\t\t\t{\n\t\t\t\tfor(i = 0; i < cl.worldmodel->numsurfaces; i++)\n\t\t\t\t\tenvmapidx[i] = Mod_NearestCubeForSurf(cl.worldmodel->surfaces+i, envmap, nenvmap);\n\t\t\t}\n\n\t\t\tMod_BSPXRW_SetLump(&bspctx, \"ENVMAP\", envmap, nenvmap*sizeof(*envmap));\n\t\t\tMod_BSPXRW_SetLump(&bspctx, \"SURFENVMAP\", envmapidx, cl.worldmodel->numsurfaces*sizeof(*envmapidx));\n\t\t\tMod_BSPXRW_Write(&bspctx);\n\n\t\t\tif (build)\n\t\t\t{\n\t\t\t\t// cubemaps present. take screenshots for each one.\n\t\t\t\tfor (i = 0; i < nenvmap; i++)\n\t\t\t\t\tSCR_ScreenShot_Cubemap(envmap[i].origin, envmap[i].cubesize);\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tCon_Printf(\"No cubemaps found on map\\n\");\n\t\t\tMod_BSPXRW_Free(&bspctx);\n\t\t}\n\n\t\tZ_Free(envmapidx);\n\t\tZ_Free(envmap);\n\t}\n}\n\nstatic void Mod_FindCubemaps_f(void)\n{\n\tMod_FindCubemaps(false);\n}\n\nstatic void Mod_BuildCubemaps_f(void)\n{\n\tsize_t i;\n\n\tif (!cls.state || !cl.worldmodel || cl.worldmodel->loadstate != MLS_LOADED)\n\t{\n\t\tCon_Printf(\"Please start a map first\\n\");\n\t\treturn;\n\t}\n\n\tif (!cl.worldmodel->numenvmaps)\n\t{\n\t\t// lump is not present or is empty. attempt to find cubemaps and then take screenshots.\n\t\tMod_FindCubemaps(true);\n\t\treturn;\n\t}\n\n\t// cubemaps present. take screenshots for each one.\n\tfor (i = 0; i < cl.worldmodel->numenvmaps; i++)\n\t\tSCR_ScreenShot_Cubemap(cl.worldmodel->envmaps[i].origin, cl.worldmodel->envmaps[i].cubesize);\n}\n\nstatic void Mod_Realign_f(void)\n{\n\tstruct bspxrw bspctx;\n\tif (Mod_BSPXRW_Read(&bspctx, cl.worldmodel->name))\n\t\tMod_BSPXRW_Write(&bspctx);\n}\nstatic searchpathfuncs_t *Mod_BSPX_List_ArchiveFile_handle;\nstatic void QDECL Mod_BSPX_List_ArchiveFile(int depth, const char *fname, fsbucket_t *filehandle, void *pathhandle)\n{\n\tsearchpathfuncs_t *archive = Mod_BSPX_List_ArchiveFile_handle;\n\tflocation_t loc;\n\ttime_t mtime;\n\tchar timestr[MAX_QPATH], sizestr[MAX_QPATH];\n\tarchive->FindFile(archive, &loc, fname, pathhandle);\n\tarchive->FileStat(archive, &loc, &mtime);\n\n\t*timestr = 0;\n\tstrftime(timestr,sizeof(timestr), \"%Y-%m-%d %H:%M:%S\", localtime(&mtime));\n\tCon_Printf(\"\\t\\t%20s: %-12s %s\\n\", fname, FS_AbbreviateSize(sizestr,sizeof(sizestr), loc.len), timestr);\n}\nstatic void Mod_BSPX_List_f(void)\n{\n\tint i;\n\tstruct bspxrw ctx;\n\tchar *fname = Cmd_Argv(1);\n\tif (!*fname && cl.worldmodel)\n\t\tfname = cl.worldmodel->name;\n\tif (Mod_BSPXRW_Read(&ctx, fname))\n\t{\n\t\tCon_Printf(\"%s:\\n\", fname);\n\t\tfor (i = 0; i < ctx.totallumps; i++)\n\t\t\tCon_Printf(\"\\t%20s: %-12\"PRIuQOFS\" csum:%08x\\n\", ctx.lumps[i].lumpname, ctx.lumps[i].filelen, LittleLong (CalcHashInt(&hash_md4, ctx.lumps[i].data, ctx.lumps[i].filelen)));\n\t\tif (ctx.archivesize)\n\t\t{\n\t\t\tsearchpathfuncs_t *f;\n\t\t\tvfsfile_t *tmp = VFSPIPE_Open(1,true);\n\t\t\tif (tmp)\n\t\t\t{\n\t\t\t\tVFS_WRITE(tmp, ctx.archivedata, ctx.archivesize);\n\t\t\t\tf = FSZIP_LoadArchive(tmp, NULL, ctx.fname, ctx.fname, NULL);\n\t\t\t\tif (!f)\n\t\t\t\t\tVFS_CLOSE(tmp);\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tCon_Printf(\"\\t%20s: %-12\"PRIuQOFS\"\\n\", \"archive\", ctx.archivesize);\n\t\t\t\t\tMod_BSPX_List_ArchiveFile_handle = f;\n\t\t\t\t\tf->BuildHash(f, 0, Mod_BSPX_List_ArchiveFile);\n\t\t\t\t\tf->ClosePath(f);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tMod_BSPXRW_Free(&ctx);\n\t}\n}\nvoid Mod_BSPX_Strip_f(void)\n{\n\tint i;\n\tstruct bspxrw ctx;\n\tqboolean found = false;\n\tif (Cmd_Argc() != 3)\n\t\tCon_Printf(\"%s FILENAME NAME: removes an extended lump from the named bsp file\\n\", Cmd_Argv(0));\n\telse if (Mod_BSPXRW_Read(&ctx, Cmd_Argv(1)))\n\t{\n\t\tfor (i = ctx.corelumps; i < ctx.totallumps;)\n\t\t{\n\t\t\tif (!Q_strcasecmp(ctx.lumps[i].lumpname, Cmd_Argv(2)))\n\t\t\t{\n\t\t\t\tfound = true;\n\t\t\t\tmemmove(&ctx.lumps[i], &ctx.lumps[i+1], sizeof(ctx.lumps[0])*(ctx.totallumps-(i+1)));\n\t\t\t\tctx.totallumps--;\n\t\t\t}\n\t\t\telse\n\t\t\t\ti++;\n\t\t}\n\t\tif (found)\n\t\t\tMod_BSPXRW_Write(&ctx);\n\t\telse\n\t\t\tMod_BSPXRW_Free(&ctx);\n\t}\n}\n\nstatic qbyte *Mod_BSPX_Injest_Read(qofs_t *sz, char *name, char *fnamefmt, ...)\n{\n\tva_list\t\targptr;\n\tchar\t\tfname[MAX_QPATH];\n\tint i;\n\n\tva_start (argptr, fnamefmt);\n\tif (Q_vsnprintfz (fname,sizeof(fname), fnamefmt,argptr))\n\t\t*fname = 0;\n\tva_end (argptr);\n\tif (!*fname)\n\t\treturn NULL;\n\n\tfor (i = 2; i < Cmd_Argc(); i++)\n\t{\n\t\tif (!Q_strcasecmp(Cmd_Argv(i), \"all\") || !Q_strcasecmp(Cmd_Argv(i), name))\n\t\t\treturn FS_MallocFile(fname, FS_GAME, sz);\n\t}\n\tCon_Printf(\"%s not requested\\n\", name);\n\treturn NULL;\n}\nstatic void Mod_BSPX_Injest_f(void)\n{\n\tqbyte *tmp;\n\tstruct bspxrw ctx;\n\tqboolean found = false;\n\tqofs_t sz;\n\tif (Cmd_Argc() <= 2)\n\t\tCon_Printf(\"%s FILENAME <all|LUMPNAMELIST>: inserts external supplementary resources into the bsp. \"CON_WARNING\"REMEMBER TO BACK-UP FIRST!\\n\", Cmd_Argv(0));\n\telse if (Mod_BSPXRW_Read(&ctx, Cmd_Argv(1)))\n\t{\n\t\tchar noext[MAX_QPATH];\n\t\tCOM_StripExtension(ctx.fname, noext,sizeof(noext));\n\t\tif (ctx.fg == fg_quake)\n\t\t{\n\t\t\ttmp = Mod_BSPX_Injest_Read(&sz, \"lit\", \"%s.lit\", noext);\n\t\t\tif (tmp)\n\t\t\t{\n\t\t\t\tif (sz == 8+ctx.lumps[LUMP_LIGHTING].filelen*3 && !memcmp(tmp, \"QLIT\\0x01\\x00\\x00\\x00\", 8))\n\t\t\t\t\tCon_Printf(\"Injesting sdr lit file\\n\"), found|=Mod_BSPXRW_SetLump(&ctx, \"RGBLIGHTING\", tmp+8, sz-8);\t\t//ldr\n\t\t\t\telse if (sz == 8+ctx.lumps[LUMP_LIGHTING].filelen*4 && !memcmp(tmp, \"QLIT\\0x01\\x00\\x01\\x00\", 8))\n\t\t\t\t\tCon_Printf(\"Injesting hdr lit file\\n\"), found|=Mod_BSPXRW_SetLump(&ctx, \"LIGHTING_E5BGR9\", tmp+8, sz-8);\t//hdr\n\t\t\t\tFS_FreeFile(tmp);\n\t\t\t}\n\n\t\t\ttmp = Mod_BSPX_Injest_Read(&sz, \"lux\", \"%s.lux\", noext);\n\t\t\tif (tmp)\n\t\t\t{\n\t\t\t\tif (sz == 8+ctx.lumps[LUMP_LIGHTING].filelen*3 && !memcmp(tmp, \"QLIT\\0x01\\x00\\x00\\x00\", 8))\n\t\t\t\t\tCon_Printf(\"Injesting lux file\\n\"), found|=Mod_BSPXRW_SetLump(&ctx, \"LIGHTINGDIR\", tmp+8, sz-8);\n\t\t\t\tFS_FreeFile(tmp);\n\t\t\t}\n\n\t\t\ttmp = Mod_BSPX_Injest_Read(&sz, \"ent\", \"%s.ent\", noext);\n\t\t\tif (tmp)\n\t\t\t{\n\t\t\t\tif (sz)\n\t\t\t\t\tCon_Printf(\"Injesting ent file\\n\"), found|=Mod_BSPXRW_SetLump(&ctx, \"entities\", tmp, sz);\n\t\t\t\tFS_FreeFile(tmp);\n\t\t\t}\n\n//\t\t\ttmp = Mod_BSPX_Injest_Read(&sz, \"vis\", \"%s.vis\", noext);\n//\t\t\ttmp = Mod_BSPX_Injest_Read(&sz, \"rtlights\", \"%s.rtlights\", noext);\n//\t\t\ttmp = Mod_BSPX_Injest_Read(&sz, \"way\", \"%s.way\", noext);\n\t\t\t//fixme: cubemaps\n\t\t}\n\n\t\tif (found)\n\t\t\tMod_BSPXRW_Write(&ctx);\n\t\telse\n\t\t{\n\t\t\tMod_BSPXRW_Free(&ctx);\n\t\t\tCon_Printf(\"%s \\\"%s\\\": nothing changed.\\n\", Cmd_Argv(0), Cmd_Argv(1));\n\t\t}\n\t}\n}\n\nimage_t *Mod_CubemapForOrigin(model_t *wmodel, vec3_t org)\n{\n\tint i;\n\tmenvmap_t       *e;\n\tfloat bestdist = FLT_MAX, dist;\n\timage_t *ret = NULL;\n\tvec3_t move;\n\tif (!wmodel || wmodel->loadstate != MLS_LOADED)\n\t\treturn NULL;\n\tfor ( i=0 , e=wmodel->envmaps ; i<wmodel->numenvmaps ; i++, e++)\n\t{\n\t\tVectorSubtract(org, e->origin, move);\n\t\tdist = DotProduct(move,move);\n\t\tif (bestdist > dist)\n\t\t{\n\t\t\tbestdist = dist;\n\t\t\tret = e->image;\n\t\t}\n\t}\n\treturn ret;\n}\n\nvoid Mod_BSPX_Init(void)\n{\n\tCmd_AddCommandD(\"mod_findcubemaps\", Mod_FindCubemaps_f, \"Scans the entities of a map to find reflection env_cubemap sites and determines the nearest one to each surface.\");\n\tCmd_AddCommandD(\"mod_buildcubemaps\", Mod_BuildCubemaps_f, \"Automatically builds cubemaps for each env_cubemap present in the map.\");\n\tCmd_AddCommandD(\"mod_realign\", Mod_Realign_f, \"Reads the named bsp and writes it back out with only alignment changes.\");\n\tCmd_AddCommandD(\"mod_bspx_list\", Mod_BSPX_List_f, \"Lists all lumps (and their sizes) in the specified bsp.\");\n\tCmd_AddCommandD(\"mod_bspx_strip\", Mod_BSPX_Strip_f, \"Strips a named extension lump from a bsp file.\");\n\tCmd_AddCommandD(\"mod_bspx_injest\", Mod_BSPX_Injest_f, \"Injests supplemental files (like .lits) into a new bspx file. \"CON_WARNING\"REMEMBER TO BACK-UP FIRST!\");\n}\n\n#endif\n"
  },
  {
    "path": "engine/common/q2pmove.c",
    "content": "/*\nCopyright (C) 1997-2001 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  \n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n*/\n#include \"quakedef.h\"\n\nfloat\tpm_q2stepheight = PM_DEFAULTSTEPHEIGHT;\n#if defined(Q2CLIENT) || defined(Q2SERVER)\n\n#define\tQ2PMF_DUCKED\t\t\t1\n#define\tQ2PMF_JUMP_HELD\t\t2\n#define\tQ2PMF_ON_GROUND\t\t4\n#define\tQ2PMF_TIME_WATERJUMP\t8\t// pm_time is waterjump\n#define\tQ2PMF_TIME_LAND\t\t16\t// pm_time is time before rejump\n#define\tQ2PMF_TIME_TELEPORT\t32\t// pm_time is non-moving time\n#define Q2PMF_NO_PREDICTION\t64\t// temporarily disables prediction (used for grappling hook)\n\n// all of the locals will be zeroed before each\n// pmove, just to make damn sure we don't have\n// any differences when running on client or server\n\ntypedef struct\n{\n\tvec3_t\t\torigin;\t\t\t// full float precision\n\tvec3_t\t\tvelocity;\t\t// full float precision\n\n\tvec3_t\t\tforward, right, up;\n\tfloat\t\tframetime;\n\n\n\tconst q2csurface_t\t*groundsurface;\n\tcplane_t\tgroundplane;\n\tint\t\t\tgroundcontents;\n\n\tvec3_t\t\tprevious_origin;\n\tqboolean\tladder;\n} q2pml_t;\n\nstatic q2pmove_t\t\t*q2pm;\nstatic q2pml_t\tq2pml;\n\n\n// movement parameters\nstatic float\tpm_stopspeed = 100;\nstatic float\tpm_maxspeed = 300;\nstatic float\tpm_duckspeed = 100;\nstatic float\tpm_accelerate = 10;\nfloat\tpm_airaccelerate = 0;\nstatic float\tpm_wateraccelerate = 10;\nstatic float\tpm_friction = 6;\nstatic float\tpm_waterfriction = 1;\nstatic float\tpm_waterspeed = 400;\n//float\tpm_stepheight;\n\n/*\n\n  walking up a step should kill some velocity\n\n*/\n\n\n/*\n==================\nPMQ2_ClipVelocity\n\nSlide off of the impacting object\nreturns the blocked flags (1 = floor, 2 = step / wall)\n==================\n*/\n#define\tSTOP_EPSILON\t0.1\n\nvoid PMQ2_ClipVelocity (vec3_t in, vec3_t normal, vec3_t out, float overbounce)\n{\n\tfloat\tbackoff;\n\tfloat\tchange;\n\tint\t\ti;\n\t\n\tbackoff = DotProduct (in, normal) * overbounce;\n\n\tfor (i=0 ; i<3 ; i++)\n\t{\n\t\tchange = normal[i]*backoff;\n\t\tout[i] = in[i] - change;\n\t\tif (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON)\n\t\t\tout[i] = 0;\n\t}\n}\n\n\n\n\n/*\n==================\nPMQ2_StepSlideMove\n\nEach intersection will try to step over the obstruction instead of\nsliding along it.\n\nReturns a new origin, velocity, and contact entity\nDoes not modify any world state?\n==================\n*/\n#define\tMIN_STEP_NORMAL\t0.7\t\t// can't step up onto very steep slopes\n#define\tMAX_CLIP_PLANES\t5\nvoid PMQ2_StepSlideMove_ (void)\n{\n\tint\t\t\tbumpcount, numbumps;\n\tvec3_t\t\tdir;\n\tfloat\t\td;\n\tint\t\t\tnumplanes;\n\tvec3_t\t\tplanes[MAX_CLIP_PLANES];\n\tvec3_t\t\tprimal_velocity;\n\tint\t\t\ti, j;\n\tq2trace_t\ttrace;\n\tvec3_t\t\tend;\n\tfloat\t\ttime_left;\n\t\n\tnumbumps = 4;\n\t\n\tVectorCopy (q2pml.velocity, primal_velocity);\n\tnumplanes = 0;\n\t\n\ttime_left = q2pml.frametime;\n\n\tfor (bumpcount=0 ; bumpcount<numbumps ; bumpcount++)\n\t{\n\t\tfor (i=0 ; i<3 ; i++)\n\t\t\tend[i] = q2pml.origin[i] + time_left * q2pml.velocity[i];\n\n\t\ttrace = q2pm->trace (q2pml.origin, q2pm->mins, q2pm->maxs, end);\n\n\t\tif (trace.allsolid)\n\t\t{\t// entity is trapped in another solid\n\t\t\tq2pml.velocity[2] = 0;\t// don't build up falling damage\n\t\t\treturn;\n\t\t}\n\n\t\tif (trace.fraction > 0)\n\t\t{\t// actually covered some distance\n\t\t\tVectorCopy (trace.endpos, q2pml.origin);\n\t\t\tnumplanes = 0;\n\t\t}\n\n\t\tif (trace.fraction == 1)\n\t\t\t break;\t\t// moved the entire distance\n\n\t\t// save entity for contact\n\t\tif (q2pm->numtouch < MAXTOUCH && trace.ent)\n\t\t{\n\t\t\tq2pm->touchents[q2pm->numtouch] = trace.ent;\n\t\t\tq2pm->numtouch++;\n\t\t}\n\t\t\n\t\ttime_left -= time_left * trace.fraction;\n\n\t\t// slide along this plane\n\t\tif (numplanes >= MAX_CLIP_PLANES)\n\t\t{\t// this shouldn't really happen\n\t\t\tVectorClear (q2pml.velocity);\n\t\t\tbreak;\n\t\t}\n\n\t\tVectorCopy (trace.plane.normal, planes[numplanes]);\n\t\tnumplanes++;\n\n#if 0\n\tfloat\t\trub;\n\n\t\t//\n\t\t// modify velocity so it parallels all of the clip planes\n\t\t//\n\t\tif (numplanes == 1)\n\t\t{\t// go along this plane\n\t\t\tVectorCopy (q2pml.velocity, dir);\n\t\t\tVectorNormalize (dir);\n\t\t\trub = 1.0 + 0.5 * DotProduct (dir, planes[0]);\n\n\t\t\t// slide along the plane\n\t\t\tPMQ2_ClipVelocity (q2pml.velocity, planes[0], q2pml.velocity, 1.01);\n\t\t\t// rub some extra speed off on xy axis\n\t\t\t// not on Z, or you can scrub down walls\n\t\t\tq2pml.velocity[0] *= rub;\n\t\t\tq2pml.velocity[1] *= rub;\n\t\t\tq2pml.velocity[2] *= rub;\n\t\t}\n\t\telse if (numplanes == 2)\n\t\t{\t// go along the crease\n\t\t\tVectorCopy (q2pml.velocity, dir);\n\t\t\tVectorNormalize (dir);\n\t\t\trub = 1.0 + 0.5 * DotProduct (dir, planes[0]);\n\n\t\t\t// slide along the plane\n\t\t\tCrossProduct (planes[0], planes[1], dir);\n\t\t\td = DotProduct (dir, q2pml.velocity);\n\t\t\tVectorScale (dir, d, q2pml.velocity);\n\n\t\t\t// rub some extra speed off\n\t\t\tVectorScale (q2pml.velocity, rub, q2pml.velocity);\n\t\t}\n\t\telse\n\t\t{\n//\t\t\tCon_Printf (\"clip velocity, numplanes == %i\\n\",numplanes);\n\t\t\tVectorClear (q2pml.velocity);\n\t\t\tbreak;\n\t\t}\n\n#else\n//\n// modify original_velocity so it parallels all of the clip planes\n//\n\t\tfor (i=0 ; i<numplanes ; i++)\n\t\t{\n\t\t\tPMQ2_ClipVelocity (q2pml.velocity, planes[i], q2pml.velocity, 1.01);\n\t\t\tfor (j=0 ; j<numplanes ; j++)\n\t\t\t\tif (j != i)\n\t\t\t\t{\n\t\t\t\t\tif (DotProduct (q2pml.velocity, planes[j]) < 0)\n\t\t\t\t\t\tbreak;\t// not ok\n\t\t\t\t}\n\t\t\tif (j == numplanes)\n\t\t\t\tbreak;\n\t\t}\n\t\t\n\t\tif (i != numplanes)\n\t\t{\t// go along this plane\n\t\t}\n\t\telse\n\t\t{\t// go along the crease\n\t\t\tif (numplanes != 2)\n\t\t\t{\n//\t\t\t\tCon_Printf (\"clip velocity, numplanes == %i\\n\",numplanes);\n\t\t\t\tVectorClear (q2pml.velocity);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tCrossProduct (planes[0], planes[1], dir);\n\t\t\td = DotProduct (dir, q2pml.velocity);\n\t\t\tVectorScale (dir, d, q2pml.velocity);\n\t\t}\n#endif\n\t\t//\n\t\t// if velocity is against the original velocity, stop dead\n\t\t// to avoid tiny occilations in sloping corners\n\t\t//\n\t\tif (DotProduct (q2pml.velocity, primal_velocity) <= 0)\n\t\t{\n\t\t\tVectorClear (q2pml.velocity);\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tif (q2pm->s.pm_time)\n\t{\n\t\tVectorCopy (primal_velocity, q2pml.velocity);\n\t}\n}\n\n/*\n==================\nPMQ2_StepSlideMove\n\n==================\n*/\nvoid PMQ2_StepSlideMove (void)\n{\n\tvec3_t\t\tstart_o, start_v;\n\tvec3_t\t\tdown_o, down_v;\n\tq2trace_t\t\ttrace;\n\tfloat\t\tdown_dist, up_dist;\n//\tvec3_t\t\tdelta;\n\tvec3_t\t\tup, down;\n\n\tVectorCopy (q2pml.origin, start_o);\n\tVectorCopy (q2pml.velocity, start_v);\n\n\tPMQ2_StepSlideMove_ ();\n\n\tVectorCopy (q2pml.origin, down_o);\n\tVectorCopy (q2pml.velocity, down_v);\n\n\tVectorCopy (start_o, up);\n\tup[2] += pm_q2stepheight;\n\n\ttrace = q2pm->trace (up, q2pm->mins, q2pm->maxs, up);\n\tif (trace.allsolid)\n\t\treturn;\t\t// can't step up\n\n\t// try sliding above\n\tVectorCopy (up, q2pml.origin);\n\tVectorCopy (start_v, q2pml.velocity);\n\n\tPMQ2_StepSlideMove_ ();\n\n\t// push down the final amount\n\tVectorCopy (q2pml.origin, down);\n\tdown[2] -= pm_q2stepheight;\n\ttrace = q2pm->trace (q2pml.origin, q2pm->mins, q2pm->maxs, down);\n\tif (!trace.allsolid)\n\t{\n\t\tVectorCopy (trace.endpos, q2pml.origin);\n\t}\n\n#if 0\n\tVectorSubtract (q2pml.origin, up, delta);\n\tup_dist = DotProduct (delta, start_v);\n\n\tVectorSubtract (down_o, start_o, delta);\n\tdown_dist = DotProduct (delta, start_v);\n#else\n\tVectorCopy(q2pml.origin, up);\n\n\t// decide which one went farther\n    down_dist = (down_o[0] - start_o[0])*(down_o[0] - start_o[0])\n        + (down_o[1] - start_o[1])*(down_o[1] - start_o[1]);\n    up_dist = (up[0] - start_o[0])*(up[0] - start_o[0])\n        + (up[1] - start_o[1])*(up[1] - start_o[1]);\n#endif\n\n\tif (down_dist > up_dist || trace.plane.normal[2] < MIN_STEP_NORMAL)\n\t{\n\t\tVectorCopy (down_o, q2pml.origin);\n\t\tVectorCopy (down_v, q2pml.velocity);\n\t\treturn;\n\t}\n\t//!! Special case\n\t// if we were walking along a plane, then we need to copy the Z over\n\tq2pml.velocity[2] = down_v[2];\n}\n\n\n/*\n==================\nPMQ2_Friction\n\nHandles both ground friction and water friction\n==================\n*/\nvoid PMQ2_Friction (void)\n{\n\tfloat\t*vel;\n\tfloat\tspeed, newspeed, control;\n\tfloat\tfriction;\n\tfloat\tdrop;\n\t\n\tvel = q2pml.velocity;\n\t\n\tspeed = sqrt(vel[0]*vel[0] +vel[1]*vel[1] + vel[2]*vel[2]);\n\tif (speed < 1)\n\t{\n\t\tvel[0] = 0;\n\t\tvel[1] = 0;\n\t\treturn;\n\t}\n\n\tdrop = 0;\n\n// apply ground friction\n\tif ((q2pm->groundentity && q2pml.groundsurface && !(q2pml.groundsurface->flags & TI_SLICK) ) || (q2pml.ladder) )\n\t{\n\t\tfriction = pm_friction;\n\t\tcontrol = speed < pm_stopspeed ? pm_stopspeed : speed;\n\t\tdrop += control*friction*q2pml.frametime;\n\t}\n\n// apply water friction\n\tif (q2pm->waterlevel && !q2pml.ladder)\n\t\tdrop += speed*pm_waterfriction*q2pm->waterlevel*q2pml.frametime;\n\n// scale the velocity\n\tnewspeed = speed - drop;\n\tif (newspeed < 0)\n\t{\n\t\tnewspeed = 0;\n\t}\n\tnewspeed /= speed;\n\n\tvel[0] = vel[0] * newspeed;\n\tvel[1] = vel[1] * newspeed;\n\tvel[2] = vel[2] * newspeed;\n}\n\n\n/*\n==============\nPMQ2_Accelerate\n\nHandles user intended acceleration\n==============\n*/\nvoid PMQ2_Accelerate (vec3_t wishdir, float wishspeed, float accel)\n{\n\tint\t\t\ti;\n\tfloat\t\taddspeed, accelspeed, currentspeed;\n\n\tcurrentspeed = DotProduct (q2pml.velocity, wishdir);\n\taddspeed = wishspeed - currentspeed;\n\tif (addspeed <= 0)\n\t\treturn;\n\taccelspeed = accel*q2pml.frametime*wishspeed;\n\tif (accelspeed > addspeed)\n\t\taccelspeed = addspeed;\n\t\n\tfor (i=0 ; i<3 ; i++)\n\t\tq2pml.velocity[i] += accelspeed*wishdir[i];\t\n}\n\nvoid PMQ2_AirAccelerate (vec3_t wishdir, float wishspeed, float accel)\n{\n\tint\t\t\ti;\n\tfloat\t\taddspeed, accelspeed, currentspeed, wishspd = wishspeed;\n\t\t\n\tif (wishspd > 30)\n\t\twishspd = 30;\n\tcurrentspeed = DotProduct (q2pml.velocity, wishdir);\n\taddspeed = wishspd - currentspeed;\n\tif (addspeed <= 0)\n\t\treturn;\n\taccelspeed = accel * wishspeed * q2pml.frametime;\n\tif (accelspeed > addspeed)\n\t\taccelspeed = addspeed;\n\t\n\tfor (i=0 ; i<3 ; i++)\n\t\tq2pml.velocity[i] += accelspeed*wishdir[i];\t\n}\n\n/*\n=============\nPMQ2_AddCurrents\n=============\n*/\nvoid PMQ2_AddCurrents (vec3_t\twishvel)\n{\n\tvec3_t\tv;\n\tfloat\ts;\n\n\t//\n\t// account for ladders\n\t//\n\n\tif (q2pml.ladder && fabs(q2pml.velocity[2]) <= 200)\n\t{\n\t\tif ((q2pm->viewangles[PITCH] <= -15) && (q2pm->cmd.forwardmove > 0))\n\t\t\twishvel[2] = 200;\n\t\telse if ((q2pm->viewangles[PITCH] >= 15) && (q2pm->cmd.forwardmove > 0))\n\t\t\twishvel[2] = -200;\n\t\telse if (q2pm->cmd.upmove > 0)\n\t\t\twishvel[2] = 200;\n\t\telse if (q2pm->cmd.upmove < 0)\n\t\t\twishvel[2] = -200;\n\t\telse\n\t\t\twishvel[2] = 0;\n\n\t\t// limit horizontal speed when on a ladder\n\t\tif (wishvel[0] < -25)\n\t\t\twishvel[0] = -25;\n\t\telse if (wishvel[0] > 25)\n\t\t\twishvel[0] = 25;\n\n\t\tif (wishvel[1] < -25)\n\t\t\twishvel[1] = -25;\n\t\telse if (wishvel[1] > 25)\n\t\t\twishvel[1] = 25;\n\t}\n\n\n\t//\n\t// add water currents\n\t//\n\n\tif (q2pm->watertype & Q2MASK_CURRENT) /*FIXME: q3bsp*/\n\t{\n\t\tmemset(v, 0, sizeof(vec3_t));\n\n\t\tif (q2pm->watertype & Q2CONTENTS_CURRENT_0)\n\t\t\tv[0] += 1;\n\t\tif (q2pm->watertype & Q2CONTENTS_CURRENT_90)\n\t\t\tv[1] += 1;\n\t\tif (q2pm->watertype & Q2CONTENTS_CURRENT_180)\n\t\t\tv[0] -= 1;\n\t\tif (q2pm->watertype & Q2CONTENTS_CURRENT_270)\n\t\t\tv[1] -= 1;\n\t\tif (q2pm->watertype & Q2CONTENTS_CURRENT_UP)\n\t\t\tv[2] += 1;\n\t\tif (q2pm->watertype & Q2CONTENTS_CURRENT_DOWN)\n\t\t\tv[2] -= 1;\n\n\t\ts = pm_waterspeed;\n\t\tif ((q2pm->waterlevel == 1) && (q2pm->groundentity))\n\t\t\ts /= 2;\n\n\t\tVectorMA (wishvel, s, v, wishvel);\n\t}\n\n\t//\n\t// add conveyor belt velocities\n\t//\n\n\tif (q2pm->groundentity)\n\t{\n\t\tmemset(v, 0, sizeof(vec3_t));\n\n\t\tif (q2pml.groundcontents & Q2CONTENTS_CURRENT_0)\n\t\t\tv[0] += 1;\n\t\tif (q2pml.groundcontents & Q2CONTENTS_CURRENT_90)\n\t\t\tv[1] += 1;\n\t\tif (q2pml.groundcontents & Q2CONTENTS_CURRENT_180)\n\t\t\tv[0] -= 1;\n\t\tif (q2pml.groundcontents & Q2CONTENTS_CURRENT_270)\n\t\t\tv[1] -= 1;\n\t\tif (q2pml.groundcontents & Q2CONTENTS_CURRENT_UP)\n\t\t\tv[2] += 1;\n\t\tif (q2pml.groundcontents & Q2CONTENTS_CURRENT_DOWN)\n\t\t\tv[2] -= 1;\n\n\t\tVectorMA (wishvel, 100 /* q2pm->groundentity->speed */, v, wishvel);\n\t}\n}\n\n\n/*\n===================\nPMQ2_WaterMove\n\n===================\n*/\nvoid PMQ2_WaterMove (void)\n{\n\tint\t\ti;\n\tvec3_t\twishvel;\n\tfloat\twishspeed;\n\tvec3_t\twishdir;\n\n//\n// user intentions\n//\n\tfor (i=0 ; i<3 ; i++)\n\t\twishvel[i] = q2pml.forward[i]*q2pm->cmd.forwardmove + q2pml.right[i]*q2pm->cmd.sidemove;\n\n\tif (!q2pm->cmd.forwardmove && !q2pm->cmd.sidemove && !q2pm->cmd.upmove)\n\t\twishvel[2] -= 60;\t\t// drift towards bottom\n\telse\n\t\twishvel[2] += q2pm->cmd.upmove;\n\n\tPMQ2_AddCurrents (wishvel);\n\n\tVectorCopy (wishvel, wishdir);\n\twishspeed = VectorNormalize(wishdir);\n\n\tif (wishspeed > pm_maxspeed)\n\t{\n\t\tVectorScale (wishvel, pm_maxspeed/wishspeed, wishvel);\n\t\twishspeed = pm_maxspeed;\n\t}\n\twishspeed *= 0.5;\n\n\tPMQ2_Accelerate (wishdir, wishspeed, pm_wateraccelerate);\n\n\tPMQ2_StepSlideMove ();\n}\n\n\n/*\n===================\nPMQ2_AirMove\n\n===================\n*/\nvoid PMQ2_AirMove (void)\n{\n\tint\t\t\ti;\n\tvec3_t\t\twishvel;\n\tfloat\t\tfmove, smove;\n\tvec3_t\t\twishdir;\n\tfloat\t\twishspeed;\n\tfloat\t\tmaxspeed;\n\n\tfmove = q2pm->cmd.forwardmove;\n\tsmove = q2pm->cmd.sidemove;\n\t\n//!!!!! pitch should be 1/3 so this isn't needed??!\n#if 0\n\tq2pml.forward[2] = 0;\n\tq2pml.right[2] = 0;\n\tVectorNormalize (q2pml.forward);\n\tVectorNormalize (q2pml.right);\n#endif\n\n\tfor (i=0 ; i<2 ; i++)\n\t\twishvel[i] = q2pml.forward[i]*fmove + q2pml.right[i]*smove;\n\twishvel[2] = 0;\n\n\tPMQ2_AddCurrents (wishvel);\n\n\tVectorCopy (wishvel, wishdir);\n\twishspeed = VectorNormalize(wishdir);\n\n//\n// clamp to server defined max speed\n//\n\tmaxspeed = (q2pm->s.pm_flags & Q2PMF_DUCKED) ? pm_duckspeed : pm_maxspeed;\n\n\tif (wishspeed > maxspeed)\n\t{\n\t\tVectorScale (wishvel, maxspeed/wishspeed, wishvel);\n\t\twishspeed = maxspeed;\n\t}\n\t\n\tif ( q2pml.ladder )\n\t{\n\t\tPMQ2_Accelerate (wishdir, wishspeed, pm_accelerate);\n\t\tif (!wishvel[2])\n\t\t{\n\t\t\tif (q2pml.velocity[2] > 0)\n\t\t\t{\n\t\t\t\tq2pml.velocity[2] -= q2pm->s.gravity * q2pml.frametime;\n\t\t\t\tif (q2pml.velocity[2] < 0)\n\t\t\t\t\tq2pml.velocity[2]  = 0;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tq2pml.velocity[2] += q2pm->s.gravity * q2pml.frametime;\n\t\t\t\tif (q2pml.velocity[2] > 0)\n\t\t\t\t\tq2pml.velocity[2]  = 0;\n\t\t\t}\n\t\t}\n\t\tPMQ2_StepSlideMove ();\n\t}\n\telse if ( q2pm->groundentity )\n\t{\t// walking on ground\n\t\tq2pml.velocity[2] = 0; //!!! this is before the accel\n\t\tPMQ2_Accelerate (wishdir, wishspeed, pm_accelerate);\n\n// PGM\t-- fix for negative trigger_gravity fields\n//\t\tq2pml.velocity[2] = 0;\n\t\tif(q2pm->s.gravity > 0)\n\t\t\tq2pml.velocity[2] = 0;\n\t\telse\n\t\t\tq2pml.velocity[2] -= q2pm->s.gravity * q2pml.frametime;\n// PGM\n\n\t\tif (!q2pml.velocity[0] && !q2pml.velocity[1])\n\t\t\treturn;\n\t\tPMQ2_StepSlideMove ();\n\t}\n\telse\n\t{\t// not on ground, so little effect on velocity\n\t\tif (pm_airaccelerate)\n\t\t\tPMQ2_AirAccelerate (wishdir, wishspeed, pm_accelerate);\n\t\telse\n\t\t\tPMQ2_Accelerate (wishdir, wishspeed, 1);\n\t\t// add gravity\n\t\tq2pml.velocity[2] -= q2pm->s.gravity * q2pml.frametime;\n\t\tPMQ2_StepSlideMove ();\n\t}\n}\n\n\n\n/*\n=============\nPMQ2_CatagorizePosition\n=============\n*/\nvoid PMQ2_CatagorizePosition (void)\n{\n\tvec3_t\t\tpoint;\n\tint\t\t\tcont;\n\tq2trace_t\t\ttrace;\n\tint\t\t\tsample1;\n\tint\t\t\tsample2;\n\n// if the player hull point one unit down is solid, the player\n// is on ground\n\n// see if standing on something solid\t\n\tpoint[0] = q2pml.origin[0];\n\tpoint[1] = q2pml.origin[1];\n\tpoint[2] = q2pml.origin[2] - 0.25;\n\tif (q2pml.velocity[2] > 180) //!!ZOID changed from 100 to 180 (ramp accel)\n\t{\n\t\tq2pm->s.pm_flags &= ~Q2PMF_ON_GROUND;\n\t\tq2pm->groundentity = NULL;\n\t}\n\telse\n\t{\n\t\ttrace = q2pm->trace (q2pml.origin, q2pm->mins, q2pm->maxs, point);\n\t\tq2pml.groundplane = trace.plane;\n\t\tq2pml.groundsurface = trace.surface;\n\t\tq2pml.groundcontents = trace.contents;\n\n\t\tif (!trace.ent || (trace.plane.normal[2] < 0.7 && !trace.startsolid) )\n\t\t{\n\t\t\tq2pm->groundentity = NULL;\n\t\t\tq2pm->s.pm_flags &= ~Q2PMF_ON_GROUND;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tq2pm->groundentity = trace.ent;\n\n\t\t\t// hitting solid ground will end a waterjump\n\t\t\tif (q2pm->s.pm_flags & Q2PMF_TIME_WATERJUMP)\n\t\t\t{\n\t\t\t\tq2pm->s.pm_flags &= ~(Q2PMF_TIME_WATERJUMP | Q2PMF_TIME_LAND | Q2PMF_TIME_TELEPORT);\n\t\t\t\tq2pm->s.pm_time = 0;\n\t\t\t}\n\n\t\t\tif (! (q2pm->s.pm_flags & Q2PMF_ON_GROUND) )\n\t\t\t{\t// just hit the ground\n\t\t\t\tq2pm->s.pm_flags |= Q2PMF_ON_GROUND;\n\t\t\t\t// don't do landing time if we were just going down a slope\n\t\t\t\tif (q2pml.velocity[2] < -200)\n\t\t\t\t{\n\t\t\t\t\tq2pm->s.pm_flags |= Q2PMF_TIME_LAND;\n\t\t\t\t\t// don't allow another jump for a little while\n\t\t\t\t\tif (q2pml.velocity[2] < -400)\n\t\t\t\t\t\tq2pm->s.pm_time = 25;\t\n\t\t\t\t\telse\n\t\t\t\t\t\tq2pm->s.pm_time = 18;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n#if 0\n\t\tif (trace.fraction < 1.0 && trace.ent && q2pml.velocity[2] < 0)\n\t\t\tq2pml.velocity[2] = 0;\n#endif\n\n\t\tif (q2pm->numtouch < MAXTOUCH && trace.ent)\n\t\t{\n\t\t\tq2pm->touchents[q2pm->numtouch] = trace.ent;\n\t\t\tq2pm->numtouch++;\n\t\t}\n\t}\n\n//\n// get waterlevel, accounting for ducking\n//\n\tq2pm->waterlevel = 0;\n\tq2pm->watertype = 0;\n\n\tsample2 = q2pm->viewheight - q2pm->mins[2];\n\tsample1 = sample2 / 2;\n\n\tpoint[2] = q2pml.origin[2] + q2pm->mins[2] + 1;\t\n\tcont = q2pm->pointcontents (point);\n\n\tif (cont & MASK_WATER)\n\t{\n\t\tq2pm->watertype = cont;\n\t\tq2pm->waterlevel = 1;\n\t\tpoint[2] = q2pml.origin[2] + q2pm->mins[2] + sample1;\n\t\tcont = q2pm->pointcontents (point);\n\t\tif (cont & MASK_WATER)\n\t\t{\n\t\t\tq2pm->waterlevel = 2;\n\t\t\tpoint[2] = q2pml.origin[2] + q2pm->mins[2] + sample2;\n\t\t\tcont = q2pm->pointcontents (point);\n\t\t\tif (cont & MASK_WATER)\n\t\t\t\tq2pm->waterlevel = 3;\n\t\t}\n\t}\n\n}\n\n\n/*\n=============\nPMQ2_CheckJump\n=============\n*/\nvoid PMQ2_CheckJump (void)\n{\n\tif (q2pm->s.pm_flags & Q2PMF_TIME_LAND)\n\t{\t// hasn't been long enough since landing to jump again\n\t\treturn;\n\t}\n\n\tif (q2pm->cmd.upmove < 10)\n\t{\t// not holding jump\n\t\tq2pm->s.pm_flags &= ~Q2PMF_JUMP_HELD;\n\t\treturn;\n\t}\n\n\t// must wait for jump to be released\n\tif (q2pm->s.pm_flags & Q2PMF_JUMP_HELD)\n\t\treturn;\n\n\tif (q2pm->s.pm_type == Q2PM_DEAD)\n\t\treturn;\n\n\tif (q2pm->waterlevel >= 2)\n\t{\t// swimming, not jumping\n\t\tq2pm->groundentity = NULL;\n\n\t\tif (q2pml.velocity[2] <= -300)\n\t\t\treturn;\n\n\t\tif (q2pm->watertype == Q2CONTENTS_WATER)\n\t\t\tq2pml.velocity[2] = 100;\n\t\telse if (q2pm->watertype == Q2CONTENTS_SLIME)\n\t\t\tq2pml.velocity[2] = 80;\n\t\telse\n\t\t\tq2pml.velocity[2] = 50;\n\t\treturn;\n\t}\n\n\tif (q2pm->groundentity == NULL)\n\t\treturn;\t\t// in air, so no effect\n\n\tq2pm->s.pm_flags |= Q2PMF_JUMP_HELD;\n\n\tq2pm->groundentity = NULL;\n\tq2pml.velocity[2] += 270;\n\tif (q2pml.velocity[2] < 270)\n\t\tq2pml.velocity[2] = 270;\n}\n\n\n/*\n=============\nPMQ2_CheckSpecialMovement\n=============\n*/\nvoid PMQ2_CheckSpecialMovement (void)\n{\n\tvec3_t\tspot;\n\tint\t\tcont;\n\tvec3_t\tflatforward;\n\tq2trace_t\ttrace;\n\n\tif (q2pm->s.pm_time)\n\t\treturn;\n\n\tq2pml.ladder = false;\n\n\t// check for ladder\n\tflatforward[0] = q2pml.forward[0];\n\tflatforward[1] = q2pml.forward[1];\n\tflatforward[2] = 0;\n\tVectorNormalize (flatforward);\n\n\tVectorMA (q2pml.origin, 1, flatforward, spot);\n\ttrace = q2pm->trace (q2pml.origin, q2pm->mins, q2pm->maxs, spot);\n\tif ((trace.fraction < 1) && (trace.contents & Q2CONTENTS_LADDER))\n\t\tq2pml.ladder = true;\n\n\t// check for water jump\n\tif (q2pm->waterlevel != 2)\n\t\treturn;\n\n\tVectorMA (q2pml.origin, 30, flatforward, spot);\n\tspot[2] += 4;\n\tcont = q2pm->pointcontents (spot);\n\tif (!(cont & Q2CONTENTS_SOLID))\n\t\treturn;\n\n\tspot[2] += 16;\n\tcont = q2pm->pointcontents (spot);\n\tif (cont)\n\t\treturn;\n\t// jump out of water\n\tVectorScale (flatforward, 50, q2pml.velocity);\n\tq2pml.velocity[2] = 350;\n\n\tq2pm->s.pm_flags |= Q2PMF_TIME_WATERJUMP;\n\tq2pm->s.pm_time = 255;\n}\n\n\n/*\n===============\nPMQ2_FlyMove\n===============\n*/\nvoid PMQ2_FlyMove (qboolean doclip)\n{\n\tfloat\tspeed, drop, friction, control, newspeed;\n\tfloat\tcurrentspeed, addspeed, accelspeed;\n\tint\t\t\ti;\n\tvec3_t\t\twishvel;\n\tfloat\t\tfmove, smove;\n\tvec3_t\t\twishdir;\n\tfloat\t\twishspeed;\n\tvec3_t\t\tend;\n\tq2trace_t\ttrace;\n\n\tq2pm->viewheight = DEFAULT_VIEWHEIGHT;\n\n\t// friction\n\n\tspeed = Length (q2pml.velocity);\n\tif (speed < 1)\n\t{\n\t\tVectorClear (q2pml.velocity);\n\t}\n\telse\n\t{\n\t\tdrop = 0;\n\n\t\tfriction = pm_friction*1.5;\t// extra friction\n\t\tcontrol = speed < pm_stopspeed ? pm_stopspeed : speed;\n\t\tdrop += control*friction*q2pml.frametime;\n\n\t\t// scale the velocity\n\t\tnewspeed = speed - drop;\n\t\tif (newspeed < 0)\n\t\t\tnewspeed = 0;\n\t\tnewspeed /= speed;\n\n\t\tVectorScale (q2pml.velocity, newspeed, q2pml.velocity);\n\t}\n\n\t// accelerate\n\tfmove = q2pm->cmd.forwardmove;\n\tsmove = q2pm->cmd.sidemove;\n\t\n\tVectorNormalize (q2pml.forward);\n\tVectorNormalize (q2pml.right);\n\n\tfor (i=0 ; i<3 ; i++)\n\t\twishvel[i] = q2pml.forward[i]*fmove + q2pml.right[i]*smove;\n\twishvel[2] += q2pm->cmd.upmove;\n\n\tVectorCopy (wishvel, wishdir);\n\twishspeed = VectorNormalize(wishdir);\n\n\t//\n\t// clamp to server defined max speed\n\t//\n\tif (wishspeed > pm_maxspeed)\n\t{\n\t\tVectorScale (wishvel, pm_maxspeed/wishspeed, wishvel);\n\t\twishspeed = pm_maxspeed;\n\t}\n\n\n\tcurrentspeed = DotProduct(q2pml.velocity, wishdir);\n\taddspeed = wishspeed - currentspeed;\n\tif (addspeed <= 0)\n\t\treturn;\n\taccelspeed = pm_accelerate*q2pml.frametime*wishspeed;\n\tif (accelspeed > addspeed)\n\t\taccelspeed = addspeed;\n\t\n\tfor (i=0 ; i<3 ; i++)\n\t\tq2pml.velocity[i] += accelspeed*wishdir[i];\t\n\n\tif (doclip) {\n\t\tfor (i=0 ; i<3 ; i++)\n\t\t\tend[i] = q2pml.origin[i] + q2pml.frametime * q2pml.velocity[i];\n\n\t\ttrace = q2pm->trace (q2pml.origin, q2pm->mins, q2pm->maxs, end);\n\n\t\tVectorCopy (trace.endpos, q2pml.origin);\n\t} else {\n\t\t// move\n\t\tVectorMA (q2pml.origin, q2pml.frametime, q2pml.velocity, q2pml.origin);\n\t}\n}\n\n\n/*\n==============\nPMQ2_CheckDuck\n\nSets mins, maxs, and q2pm->viewheight\n==============\n*/\nvoid PMQ2_CheckDuck (void)\n{\n\tq2trace_t\ttrace;\n\n\tq2pm->mins[0] = -16;\n\tq2pm->mins[1] = -16;\n\n\tq2pm->maxs[0] = 16;\n\tq2pm->maxs[1] = 16;\n\n\tif (q2pm->s.pm_type == Q2PM_GIB)\n\t{\n\t\tq2pm->mins[2] = 0;\n\t\tq2pm->maxs[2] = 16;\n\t\tq2pm->viewheight = 8;\n\t\treturn;\n\t}\n\n\tq2pm->mins[2] = -24;\n\n\tif (q2pm->s.pm_type == Q2PM_DEAD)\n\t{\n\t\tq2pm->s.pm_flags |= Q2PMF_DUCKED;\n\t}\n\telse if (q2pm->cmd.upmove < 0 && (q2pm->s.pm_flags & Q2PMF_ON_GROUND) )\n\t{\t// duck\n\t\tq2pm->s.pm_flags |= Q2PMF_DUCKED;\n\t}\n\telse\n\t{\t// stand up if possible\n\t\tif (q2pm->s.pm_flags & Q2PMF_DUCKED)\n\t\t{\n\t\t\t// try to stand up\n\t\t\tq2pm->maxs[2] = 32;\n\t\t\ttrace = q2pm->trace (q2pml.origin, q2pm->mins, q2pm->maxs, q2pml.origin);\n\t\t\tif (!trace.allsolid)\n\t\t\t\tq2pm->s.pm_flags &= ~Q2PMF_DUCKED;\n\t\t}\n\t}\n\n\tif (q2pm->s.pm_flags & Q2PMF_DUCKED)\n\t{\n\t\tq2pm->maxs[2] = 4;\n\t\tq2pm->viewheight = -2;\n\t}\n\telse\n\t{\n\t\tq2pm->maxs[2] = 32;\n\t\tq2pm->viewheight = DEFAULT_VIEWHEIGHT;\n\t}\n}\n\n\n/*\n==============\nPMQ2_DeadMove\n==============\n*/\nvoid PMQ2_DeadMove (void)\n{\n\tfloat\tforward;\n\n\tif (!q2pm->groundentity)\n\t\treturn;\n\n\t// extra friction\n\n\tforward = Length (q2pml.velocity);\n\tforward -= 20;\n\tif (forward <= 0)\n\t{\n\t\tmemset(q2pml.velocity, 0, sizeof(vec3_t));\n\t}\n\telse\n\t{\n\t\tVectorNormalize (q2pml.velocity);\n\t\tVectorScale (q2pml.velocity, forward, q2pml.velocity);\n\t}\n}\n\n\nqboolean\tPMQ2_GoodPosition (void)\n{\n\tq2trace_t\ttrace;\n\tvec3_t\torigin, end;\n\tint\t\ti;\n\n\tif (q2pm->s.pm_type == Q2PM_SPECTATOR)\n\t\treturn true;\n\n\tfor (i=0 ; i<3 ; i++)\n\t\torigin[i] = end[i] = q2pm->s.origin[i]*0.125;\n\ttrace = q2pm->trace (origin, q2pm->mins, q2pm->maxs, end);\n\n\treturn !trace.allsolid;\n}\n\n/*\n================\nPMQ2_SnapPosition\n\nOn exit, the origin will have a value that is pre-quantized to the 0.125\nprecision of the network channel and in a valid position.\n================\n*/\nvoid PMQ2_SnapPosition (void)\n{\n\tint\t\tsign[3];\n\tint\t\ti, j, bits;\n\tshort\tbase[3];\n\t// try all single bits first\n\tstatic int jitterbits[8] = {0,4,1,2,3,5,6,7};\n\n\t// snap velocity to eigths\n\tfor (i=0 ; i<3 ; i++)\n\t\tq2pm->s.velocity[i] = (int)(q2pml.velocity[i]*8);\n\n\tfor (i=0 ; i<3 ; i++)\n\t{\n\t\tif (q2pml.origin[i] >= 0)\n\t\t\tsign[i] = 1;\n\t\telse \n\t\t\tsign[i] = -1;\n\t\tq2pm->s.origin[i] = (int)(q2pml.origin[i]*8);\n\t\tif (q2pm->s.origin[i]*0.125 == q2pml.origin[i])\n\t\t\tsign[i] = 0;\n\t}\n\tVectorCopy (q2pm->s.origin, base);\n\n\t// try all combinations\n\tfor (j=0 ; j<8 ; j++)\n\t{\n\t\tbits = jitterbits[j];\n\t\tVectorCopy (base, q2pm->s.origin);\n\t\tfor (i=0 ; i<3 ; i++)\n\t\t\tif (bits & (1<<i) )\n\t\t\t\tq2pm->s.origin[i] += sign[i];\n\n\t\tif (PMQ2_GoodPosition ())\n\t\t\treturn;\n\t}\n\n\t// go back to the last position\n\tVectorCopy (q2pml.previous_origin, q2pm->s.origin);\n//\tCon_DPrintf (\"using previous_origin\\n\");\n}\n\n#if 0\n//NO LONGER USED\n/*\n================\nPMQ2_InitialSnapPosition\n\n================\n*/\nvoid PMQ2_InitialSnapPosition (void)\n{\n\tint\t\tx, y, z;\n\tshort\tbase[3];\n\n\tVectorCopy (q2pm->s.origin, base);\n\n\tfor (z=1 ; z>=-1 ; z--)\n\t{\n\t\tq2pm->s.origin[2] = base[2] + z;\n\t\tfor (y=1 ; y>=-1 ; y--)\n\t\t{\n\t\t\tq2pm->s.origin[1] = base[1] + y;\n\t\t\tfor (x=1 ; x>=-1 ; x--)\n\t\t\t{\n\t\t\t\tq2pm->s.origin[0] = base[0] + x;\n\t\t\t\tif (PMQ2_GoodPosition ())\n\t\t\t\t{\n\t\t\t\t\tq2pml.origin[0] = q2pm->s.origin[0]*0.125;\n\t\t\t\t\tq2pml.origin[1] = q2pm->s.origin[1]*0.125;\n\t\t\t\t\tq2pml.origin[2] = q2pm->s.origin[2]*0.125;\n\t\t\t\t\tVectorCopy (q2pm->s.origin, q2pml.previous_origin);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tCon_DPrintf (\"Bad InitialSnapPosition\\n\");\n}\n#else\n/*\n================\nPMQ2_InitialSnapPosition\n\n================\n*/\nvoid PMQ2_InitialSnapPosition(void)\n{\n\tint        x, y, z;\n\tshort      base[3];\n\tstatic int offset[3] = { 0, -1, 1 };\n\n\tVectorCopy (q2pm->s.origin, base);\n\n\tfor ( z = 0; z < 3; z++ ) {\n\t\tq2pm->s.origin[2] = base[2] + offset[ z ];\n\t\tfor ( y = 0; y < 3; y++ ) {\n\t\t\tq2pm->s.origin[1] = base[1] + offset[ y ];\n\t\t\tfor ( x = 0; x < 3; x++ ) {\n\t\t\t\tq2pm->s.origin[0] = base[0] + offset[ x ];\n\t\t\t\tif (PMQ2_GoodPosition ()) {\n\t\t\t\t\tq2pml.origin[0] = q2pm->s.origin[0]*0.125;\n\t\t\t\t\tq2pml.origin[1] = q2pm->s.origin[1]*0.125;\n\t\t\t\t\tq2pml.origin[2] = q2pm->s.origin[2]*0.125;\n\t\t\t\t\tVectorCopy (q2pm->s.origin, q2pml.previous_origin);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tCon_DPrintf (\"Bad InitialSnapPosition\\n\");\n}\n\n#endif\n\n/*\n================\nPMQ2_ClampAngles\n\n================\n*/\nvoid PMQ2_ClampAngles (void)\n{\n\tshort\ttemp;\n\tint\t\ti;\n\n\tif (q2pm->s.pm_flags & Q2PMF_TIME_TELEPORT)\n\t{\n\t\tq2pm->viewangles[YAW] = SHORT2ANGLE(q2pm->cmd.angles[YAW] + q2pm->s.delta_angles[YAW]);\n\t\tq2pm->viewangles[PITCH] = 0;\n\t\tq2pm->viewangles[ROLL] = 0;\n\t}\n\telse\n\t{\n\t\t// circularly clamp the angles with deltas\n\t\tfor (i=0 ; i<3 ; i++)\n\t\t{\n\t\t\ttemp = q2pm->cmd.angles[i] + q2pm->s.delta_angles[i];\n\t\t\tq2pm->viewangles[i] = SHORT2ANGLE(temp);\n\t\t}\n\n\t\t// don't let the player look up or down more than 90 degrees\n\t\tif (q2pm->viewangles[PITCH] > 89 && q2pm->viewangles[PITCH] < 180)\n\t\t\tq2pm->viewangles[PITCH] = 89;\n\t\telse if (q2pm->viewangles[PITCH] < 271 && q2pm->viewangles[PITCH] >= 180)\n\t\t\tq2pm->viewangles[PITCH] = 271;\n\t}\n\tAngleVectors (q2pm->viewangles, q2pml.forward, q2pml.right, q2pml.up);\n}\n\n/*\n================\nPmove\n\nCan be called by either the server or the client\n================\n*/\nvoid VARGS Q2_Pmove (q2pmove_t *pmove)\n{\n\tq2pm = pmove;\n\n\t// clear results\n\tq2pm->numtouch = 0;\n\tmemset (q2pm->viewangles, 0, sizeof(vec3_t));\n\tq2pm->viewheight = 0;\n\tq2pm->groundentity = 0;\n\tq2pm->watertype = 0;\n\tq2pm->waterlevel = 0;\n\n\t// clear all pmove local vars\n\tmemset (&q2pml, 0, sizeof(q2pml));\n\n\t// convert origin and velocity to float values\n\tq2pml.origin[0] = q2pm->s.origin[0]*0.125;\n\tq2pml.origin[1] = q2pm->s.origin[1]*0.125;\n\tq2pml.origin[2] = q2pm->s.origin[2]*0.125;\n\n\tq2pml.velocity[0] = q2pm->s.velocity[0]*0.125;\n\tq2pml.velocity[1] = q2pm->s.velocity[1]*0.125;\n\tq2pml.velocity[2] = q2pm->s.velocity[2]*0.125;\n\n\t// save old org in case we get stuck\n\tVectorCopy (q2pm->s.origin, q2pml.previous_origin);\n\n\tq2pml.frametime = q2pm->cmd.msec * 0.001;\n\n\tPMQ2_ClampAngles ();\n\n\tif (q2pm->s.pm_type == Q2PM_SPECTATOR)\n\t{\n\t\tPMQ2_FlyMove (false);\n\t\tPMQ2_SnapPosition ();\n\t\treturn;\n\t}\n\n\tif (q2pm->s.pm_type >= Q2PM_DEAD)\n\t{\n\t\tq2pm->cmd.forwardmove = 0;\n\t\tq2pm->cmd.sidemove = 0;\n\t\tq2pm->cmd.upmove = 0;\n\t}\n\n\tif (q2pm->s.pm_type == Q2PM_FREEZE)\n\t\treturn;\t\t// no movement at all\n\n\t// set mins, maxs, and viewheight\n\tPMQ2_CheckDuck ();\n\n\tif (q2pm->snapinitial)\n\t\tPMQ2_InitialSnapPosition ();\n\n\t// set groundentity, watertype, and waterlevel\n\tPMQ2_CatagorizePosition ();\n\n\tif (q2pm->s.pm_type == Q2PM_DEAD)\n\t\tPMQ2_DeadMove ();\n\n\tPMQ2_CheckSpecialMovement ();\n\n\t// drop timing counter\n\tif (q2pm->s.pm_time)\n\t{\n\t\tint\t\tmsec;\n\n\t\tmsec = q2pm->cmd.msec >> 3;\n\t\tif (!msec)\n\t\t\tmsec = 1;\n\t\tif ( msec >= q2pm->s.pm_time) \n\t\t{\n\t\t\tq2pm->s.pm_flags &= ~(Q2PMF_TIME_WATERJUMP | Q2PMF_TIME_LAND | Q2PMF_TIME_TELEPORT);\n\t\t\tq2pm->s.pm_time = 0;\n\t\t}\n\t\telse\n\t\t\tq2pm->s.pm_time -= msec;\n\t}\n\n\tif (q2pm->s.pm_flags & Q2PMF_TIME_TELEPORT)\n\t{\t// teleport pause stays exactly in place\n\t}\n\telse if (q2pm->s.pm_flags & Q2PMF_TIME_WATERJUMP)\n\t{\t// waterjump has no control, but falls\n\t\tq2pml.velocity[2] -= q2pm->s.gravity * q2pml.frametime;\n\t\tif (q2pml.velocity[2] < 0)\n\t\t{\t// cancel as soon as we are falling down again\n\t\t\tq2pm->s.pm_flags &= ~(Q2PMF_TIME_WATERJUMP | Q2PMF_TIME_LAND | Q2PMF_TIME_TELEPORT);\n\t\t\tq2pm->s.pm_time = 0;\n\t\t}\n\n\t\tPMQ2_StepSlideMove ();\n\t}\n\telse\n\t{\n\t\tPMQ2_CheckJump ();\n\n\t\tPMQ2_Friction ();\n\n\t\tif (q2pm->waterlevel >= 2)\n\t\t\tPMQ2_WaterMove ();\n\t\telse {\n\t\t\tvec3_t\tangles;\n\n\t\t\tVectorCopy(q2pm->viewangles, angles);\n\t\t\tif (angles[PITCH] > 180)\n\t\t\t\tangles[PITCH] = angles[PITCH] - 360;\n\t\t\tangles[PITCH] /= 3;\n\n\t\t\tAngleVectors (angles, q2pml.forward, q2pml.right, q2pml.up);\n\n\t\t\tPMQ2_AirMove ();\n\t\t}\n\t}\n\n\t// set groundentity, watertype, and waterlevel for final spot\n\tPMQ2_CatagorizePosition ();\n\n\tPMQ2_SnapPosition ();\n}\n\n\n#endif\n\n"
  },
  {
    "path": "engine/common/q3api.h",
    "content": "\n#if defined(Q3CLIENT) || defined(Q3SERVER)\nstruct sfx_s;\nstruct server_static_s;\nstruct server_s;\nstruct usercmd_s;\nstruct q3gamecode_s\n{\n\tstruct\n\t{\n\t\tvoid (*SendAuthPacket)(struct ftenet_connections_s *socket, netadr_t *gameserver);\n\t\tvoid (*SendConnectPacket)(struct ftenet_connections_s *socket, netadr_t *to, int challenge, int qport, infobuf_t *userinfo);\n\t\tvoid (*Established)(void);\n\t\tvoid (VARGS *SendClientCommand)(const char *fmt, ...) LIKEPRINTF(1);\n\t\tvoid (*SendCmd)(struct ftenet_connections_s *socket, struct usercmd_s *cmd, unsigned int movesequence, double gametime);\n\t\tint (*ParseServerMessage) (sizebuf_t *msg);\n\t\tvoid (*Disconnect) (struct ftenet_connections_s *socket);\t//disconnects from the server, killing all connection+cgame state.\n\t} cl;\n\n\tstruct\n\t{\n\t\tvoid\t\t\t(*VideoRestarted)\t\t(void);\n\t\tint\t\t\t\t(*Redraw)\t\t\t\t(double time);\n\t\tqboolean\t\t(*ConsoleCommand)\t\t(void);\n\t\tqboolean\t\t(*KeyPressed)\t\t\t(int key, int unicode, int down);\n\t\tunsigned int\t(*GatherLoopingSounds)\t(vec3_t *positions, unsigned int *entnums, struct sfx_s **sounds, unsigned int max);\n\t} cg;\n\n\tstruct\n\t{\n\t\tqboolean (*IsRunning)(void);\n\t\tqboolean (*ConsoleCommand)(void);\n\t\tvoid (*Start) (void);\n\t\tqboolean (*OpenMenu)(void);\n\t\tvoid (*Reset)(void);\n\t} ui;\n\n//server stuff\n\tstruct\n\t{\n\t\tvoid\t\t(*ShutdownGame)\t\t\t\t(qboolean restart);\n\t\tqboolean\t(*InitGame)\t\t\t\t\t(struct server_static_s *server_state_static, struct server_s *server_state, qboolean restart);\n\t\tqboolean\t(*ConsoleCommand)\t\t\t(void);\n\t\tqboolean\t(*PrefixedConsoleCommand)\t(void);\n\t\tqboolean\t(*HandleClient)\t\t\t\t(netadr_t *from, sizebuf_t *msg);\n\t\tvoid\t\t(*DirectConnect)\t\t\t(netadr_t *from, sizebuf_t *msg);\n\t\tvoid\t\t(*NewMapConnects)\t\t\t(void);\n\t\tvoid\t\t(*DropClient)\t\t\t\t(struct client_s *cl);\n\t\tvoid\t\t(*RunFrame)\t\t\t\t\t(void);\n\t\tvoid\t\t(*SendMessage)\t\t\t\t(struct client_s  *client);\n\t\tqboolean\t(*RestartGamecode)\t\t\t(void);\n\t\tvoid\t\t(*ServerinfoChanged)\t\t(const char *key);\n\t} sv;\n};\n\nextern struct q3gamecode_s *q3;\n#endif\n"
  },
  {
    "path": "engine/common/qvm.c",
    "content": "/*************************************************************************\n** QVM\n** Copyright (C) 2003 by DarkOne\n**\n** This program is free software; you can redistribute it and/or\n** modify it under the terms of the GNU General Public License\n** as published by the Free Software Foundation; either version 2\n** of the License, or (at your option) any later version.\n**\n** This program is distributed in the hope that it will be useful,\n** but WITHOUT ANY WARRANTY; without even the implied warranty of\n** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n** GNU General Public License for more details.\n**************************************************************************\n** Quake3 compatible virtual machine\n*************************************************************************/\n\n/*\nspike's changes.\nmasks are now done by modulus rather than and\nVM_POINTER contains a mask check.\nds_mask is set to the mem allocated for stack and data.\nbuiltins range check written to buffers.\n\nQVM_Step placed in QVM_Exec for efficiency.\n\nan invalid statement was added at the end of the statements to prevent the qvm walking off.\nstack pops/pushes are all tested. An extra stack entry was faked to prevent stack checks on double-stack operators from overwriting.\n\n\nFixme: there is always the possibility that I missed a potential virus loophole..\nAlso, can efficiency be improved much?\n*/\n\n\n#include \"quakedef.h\"\n\n#ifdef VM_ANY\n\n#if defined(_MSC_VER)\t//fix this please\n\t#ifdef inline\n\t\t#undef inline\n\t#endif\n\t#define inline __forceinline\n#endif\n\n\n\n#define RETURNOFFSETMARKER NULL\n\ntypedef enum vm_type_e\n{\n\tVM_NONE,\n\tVM_NATIVE,\n\tVM_BYTECODE,\n\tVM_BUILTIN\n} vm_type_t;\n\nstruct vm_s {\n// common\n\tvm_type_t type;\n\tchar filename[MAX_OSPATH];\n\tsys_calldll_t syscalldll;\n\tsys_callqvm_t syscallqvm;\n\n// shared\n\tvoid *hInst;\n\n// native\n\tqintptr_t (EXPORT_FN *vmMain)(qintptr_t command, qintptr_t arg0, qintptr_t arg1, qintptr_t arg2, qintptr_t arg3, qintptr_t arg4, qintptr_t arg5, qintptr_t arg6, qintptr_t arg7);\n};\n\n//this is a bit weird. qvm plugins always come from $basedir/$mod/plugins/$foo.qvm\n//but native plugins never come from $basedir/$mod/ - too many other engines blindly allow dll downloads etc. Its simply far too insecure if people use other engines.\n//q3 gamecode allows it however. yes you could probably get fte to connect via q3 instead and get such a dll.\nqboolean QVM_LoadDLL(vm_t *vm, const char *name, qboolean binroot, void **vmMain, sys_calldll_t syscall)\n{\n\tvoid (EXPORT_FN *dllEntry)(sys_calldll_t syscall);\n\tdllhandle_t *hVM;\n\n\tchar fname[MAX_OSPATH*2];\n\tchar gpath[MAX_OSPATH];\n\tchar displaypath[MAX_OSPATH*2];\n\tvoid *iterator;\n\n\tdllfunction_t funcs[] =\n\t{\n\t\t{(void*)&dllEntry, \"dllEntry\"},\n\t\t{(void*)vmMain, \"vmMain\"},\n\t\t{NULL, NULL},\n\t};\n\n\thVM=NULL;\n\t*fname = 0;\n\n\tif (binroot)\n\t{\n\t\t//load eg: basedir/module_gamedir_arch.ext\n\t\tCon_DPrintf(\"Attempting to load native library: %s\\n\", name);\n\n\t\t// run through the search paths\n\t\titerator = NULL;\n\t\twhile (!hVM && COM_IteratePaths(&iterator, NULL, 0, gpath, sizeof(gpath)))\n\t\t{\n#ifndef ANDROID\n\t\t\tif (!hVM && FS_SystemPath(va(\"%s_%s_\"ARCH_CPU_POSTFIX ARCH_DL_POSTFIX, name, gpath), FS_BINARYPATH, fname, sizeof(fname)))\n\t\t\t{\n\t\t\t\tif (FS_DisplayPath(fname, FS_SYSTEM, displaypath, sizeof(displaypath)))\n\t\t\t\t\tCon_DLPrintf(2, \"Loading native: %s\\n\", displaypath);\n\t\t\t\thVM = Sys_LoadLibrary(fname, funcs);\n\t\t\t}\n#endif\n\t\t\tif (!hVM && FS_SystemPath(va(\"%s_%s\"ARCH_DL_POSTFIX, name, gpath), FS_BINARYPATH, fname, sizeof(fname)))\n\t\t\t{\n\t\t\t\tif (FS_DisplayPath(fname, FS_SYSTEM, displaypath, sizeof(displaypath)))\n\t\t\t\t\tCon_DLPrintf(2, \"Loading native: %s\\n\", displaypath);\n\t\t\t\thVM = Sys_LoadLibrary(fname, funcs);\n\t\t\t}\n\n#ifndef ANDROID\n\t\t\tif (!hVM && FS_SystemPath(va(\"%s_%s_\"ARCH_CPU_POSTFIX ARCH_DL_POSTFIX, name, gpath), FS_ROOT, fname, sizeof(fname)))\n\t\t\t{\n\t\t\t\tif (FS_DisplayPath(fname, FS_SYSTEM, displaypath, sizeof(displaypath)))\n\t\t\t\t\tCon_DLPrintf(2, \"Loading native: %s\\n\", displaypath);\n\t\t\t\thVM = Sys_LoadLibrary(fname, funcs);\n\t\t\t}\n#endif\n\t\t\tif (!hVM && FS_SystemPath(va(\"%s_%s\"ARCH_DL_POSTFIX, name, gpath), FS_ROOT, fname, sizeof(fname)))\n\t\t\t{\n\t\t\t\tif (FS_DisplayPath(fname, FS_SYSTEM, displaypath, sizeof(displaypath)))\n\t\t\t\t\tCon_DLPrintf(2, \"Loading native: %s\\n\", displaypath);\n\t\t\t\thVM = Sys_LoadLibrary(fname, funcs);\n\t\t\t}\n\t\t}\n\n#ifdef ANDROID\n\t\tif (!hVM && FS_SystemPath(va(\"%s\" ARCH_DL_POSTFIX, name), FS_BINARYPATH, fname, sizeof(fname)))\n\t\t\thVM = Sys_LoadLibrary(fname, funcs);\n#else\n\n\t\tif (!hVM && FS_SystemPath(va(\"%s_\"ARCH_CPU_POSTFIX ARCH_DL_POSTFIX, name), FS_BINARYPATH, fname, sizeof(fname)))\n\t\t\thVM = Sys_LoadLibrary(fname, funcs);\n\t\tif (!hVM && FS_SystemPath(va(\"%s\"ARCH_CPU_POSTFIX ARCH_DL_POSTFIX, name), FS_BINARYPATH, fname, sizeof(fname)))\n\t\t\thVM = Sys_LoadLibrary(fname, funcs);\n\t\tif (!hVM && FS_SystemPath(va(\"%s\" ARCH_DL_POSTFIX, name), FS_BINARYPATH, fname, sizeof(fname)))\n\t\t\thVM = Sys_LoadLibrary(fname, funcs);\n\n\t\tif (!hVM && FS_SystemPath(va(\"%s_\"ARCH_CPU_POSTFIX ARCH_DL_POSTFIX, name), FS_ROOT, fname, sizeof(fname)))\n\t\t\thVM = Sys_LoadLibrary(fname, funcs);\n\t\tif (!hVM && FS_SystemPath(va(\"%s\"ARCH_CPU_POSTFIX ARCH_DL_POSTFIX, name), FS_ROOT, fname, sizeof(fname)))\n\t\t\thVM = Sys_LoadLibrary(fname, funcs);\n\t\tif (!hVM && FS_SystemPath(va(\"%s\" ARCH_DL_POSTFIX, name), FS_ROOT, fname, sizeof(fname)))\n\t\t\thVM = Sys_LoadLibrary(fname, funcs);\n#endif\n\t}\n\telse\n\t{\n\t\t//load eg: basedir/gamedir/module_arch.ext\n\t\tCon_DPrintf(\"Attempting to load (unsafe) native library: %s\\n\", name);\n\n\t\t// run through the search paths\n\t\titerator = NULL;\n\t\twhile (!hVM && COM_IteratePaths(&iterator, gpath, sizeof(gpath), NULL, 0))\n\t\t{\n\t\t\tif (!hVM)\n\t\t\t{\n\t\t\t\tsnprintf (fname, sizeof(fname), \"%s%s_\"ARCH_CPU_POSTFIX ARCH_DL_POSTFIX, gpath, name);\n\t\t\t\tif (FS_DisplayPath(fname, FS_SYSTEM, displaypath, sizeof(displaypath)))\n\t\t\t\t\tCon_DLPrintf(2, \"Loading native: %s\\n\", displaypath);\n\t\t\t\thVM = Sys_LoadLibrary(fname, funcs);\n\t\t\t}\n\t\t\tif (!hVM)\n\t\t\t{\n\t\t\t\tsnprintf (fname, sizeof(fname), \"%s%s\"ARCH_CPU_POSTFIX ARCH_DL_POSTFIX, gpath, name);\n\t\t\t\tif (FS_DisplayPath(fname, FS_SYSTEM, displaypath, sizeof(displaypath)))\n\t\t\t\t\tCon_DLPrintf(2, \"Loading native: %s\\n\", displaypath);\n\t\t\t\thVM = Sys_LoadLibrary(fname, funcs);\n\t\t\t}\n\n#ifdef ARCH_ALTCPU_POSTFIX\n\t\t\tif (!hVM)\n\t\t\t{\n\t\t\t\tsnprintf (fname, sizeof(fname), \"%s%s_\"ARCH_ALTCPU_POSTFIX ARCH_DL_POSTFIX, gpath, name);\n\t\t\t\tif (FS_DisplayPath(fname, FS_SYSTEM, displaypath, sizeof(displaypath)))\n\t\t\t\t\tCon_DLPrintf(2, \"Loading native: %s\\n\", displaypath);\n\t\t\t\thVM = Sys_LoadLibrary(fname, funcs);\n\t\t\t}\n\t\t\tif (!hVM)\n\t\t\t{\n\t\t\t\tsnprintf (fname, sizeof(fname), \"%s%s\"ARCH_ALTCPU_POSTFIX ARCH_DL_POSTFIX, gpath, name);\n\t\t\t\tif (FS_DisplayPath(fname, FS_SYSTEM, displaypath, sizeof(displaypath)))\n\t\t\t\t\tCon_DLPrintf(2, \"Loading native: %s\\n\", displaypath);\n\t\t\t\thVM = Sys_LoadLibrary(fname, funcs);\n\t\t\t}\n#endif\n\n\t\t\tif (!hVM)\n\t\t\t{\n\t\t\t\tsnprintf (fname, sizeof(fname), \"%s%s\"ARCH_DL_POSTFIX, gpath, name);\n\t\t\t\tif (FS_DisplayPath(fname, FS_SYSTEM, displaypath, sizeof(displaypath)))\n\t\t\t\t\tCon_DLPrintf(2, \"Loading native: %s\\n\", displaypath);\n\t\t\t\thVM = Sys_LoadLibrary(fname, funcs);\n\t\t\t}\n\t\t}\n\t}\n\n\tif(!hVM) return false;\n\n\tQ_strncpyz(vm->filename, fname, sizeof(vm->filename));\n\tvm->hInst = hVM;\n\n\t(*dllEntry)(syscall);\n\n\treturn true;\n}\n\n/*\n** Sys_UnloadDLL\n*/\nvoid QVM_UnloadDLL(dllhandle_t *handle)\n{\n\tif(handle)\n\t{\n\t\tSys_CloseLibrary(handle);\n\t}\n}\n\n\n\n\n\n\n\n\n\n\n// ------------------------- * QVM files * -------------------------\n#define\tVM_MAGIC\t0x12721444\n#define\tVM_MAGIC2\t0x12721445\n\n#pragma pack(push,1)\ntypedef struct vmHeader_s\n{\n\tint vmMagic;\n\n\tint instructionCount;\n\n\tint codeOffset;\n\tint codeLength;\n\n\tint dataOffset;\n\tint dataLength;\t// should be byteswapped on load\n\tint litLength;\t// copy as is\n\tint bssLength;\t// zero filled memory appended to datalength\n\n\t//valid only in V2.\n\tint\t\tjtrgLength;\t\t\t// number of jump table targets\n} vmHeader_t;\n#pragma pack(pop)\n\n// ------------------------- * in memory representation * -------------------------\n\ntypedef struct qvm_s\n{\n// segments\n\tunsigned int *cs;\t// code  segment, each instruction is 2 ints\n\tqbyte *ds;\t// data  segment, partially filled on load (data, lit, bss)\n\tqbyte *ss;\t// stack segment, (follows immediatly after ds, corrupting data before vm)\n\n// pointer registers\n\tunsigned int *pc;\t// program counter, points to cs, goes up\n\tunsigned int *sp;\t// stack pointer, initially points to end of ss, goes down\n\tunsigned int bp;\t// base pointer, initially len_ds+len_ss/2\n\n\tunsigned int *min_sp;\n\tunsigned int *max_sp;\n\tunsigned int min_bp;\n\tunsigned int max_bp;\n\n// status\n\tunsigned int len_cs;\t// size of cs\n\tunsigned int len_ds;\t// size of ds\n\tunsigned int len_ss;\t// size of ss\n\tunsigned int ds_mask; // ds mask (ds+ss)\n\n// memory\n\tunsigned int mem_size;\n\tqbyte *mem_ptr;\n\n//\tunsigned int cycles;\t// command cicles executed\n\tsys_callqvm_t syscall;\n} qvm_t;\n\nqboolean QVM_LoadVM(vm_t *vm, const char *name, sys_callqvm_t syscall);\nvoid QVM_UnLoadVM(qvm_t *qvm);\nint QVM_ExecVM(qvm_t *qvm, int command, int arg0, int arg1, int arg2, int arg3, int arg4, int arg5, int arg6, int arg7);\n\n\n// ------------------------- * OP.CODES * -------------------------\n\ntypedef enum qvm_op_e\n{\n\tOP_UNDEF,\n\tOP_NOP,\n\tOP_BREAK,\n\n\tOP_ENTER, // b32\n\tOP_LEAVE,\t// b32\n\tOP_CALL,\n\tOP_PUSH,\n\tOP_POP,\n\n\tOP_CONST,\t// b32\n\tOP_LOCAL,\t// b32\n\n\tOP_JUMP,\n\n// -------------------\n\n\tOP_EQ,\t// b32\n\tOP_NE,\t// b32\n\n\tOP_LTI,\t// b32\n\tOP_LEI,\t// b32\n\tOP_GTI,\t// b32\n\tOP_GEI,\t// b32\n\n\tOP_LTU,\t// b32\n\tOP_LEU,\t// b32\n\tOP_GTU,\t// b32\n\tOP_GEU,\t// b32\n\n\tOP_EQF,\t// b32\n\tOP_NEF,\t// b32\n\n\tOP_LTF,\t// b32\n\tOP_LEF,\t// b32\n\tOP_GTF,\t// b32\n\tOP_GEF,\t// b32\n\n// -------------------\n\n\tOP_LOAD1,\n\tOP_LOAD2,\n\tOP_LOAD4,\n\tOP_STORE1,\n\tOP_STORE2,\n\tOP_STORE4,\n\tOP_ARG,\t// b8\n\tOP_BLOCK_COPY,\t// b32\n\n//-------------------\n\n\tOP_SEX8,\n\tOP_SEX16,\n\n\tOP_NEGI,\n\tOP_ADD,\n\tOP_SUB,\n\tOP_DIVI,\n\tOP_DIVU,\n\tOP_MODI,\n\tOP_MODU,\n\tOP_MULI,\n\tOP_MULU,\n\n\tOP_BAND,\n\tOP_BOR,\n\tOP_BXOR,\n\tOP_BCOM,\n\n\tOP_LSH,\n\tOP_RSHI,\n\tOP_RSHU,\n\n\tOP_NEGF,\n\tOP_ADDF,\n\tOP_SUBF,\n\tOP_DIVF,\n\tOP_MULF,\n\n\tOP_CVIF,\n\tOP_CVFI\n} qvm_op_t;\n\n\n\n\n\n\n\n\n\n// ------------------------- * Init & ShutDown * -------------------------\n\n/*\n** QVM_Load\n*/\nqboolean QVM_LoadVM(vm_t *vm, const char *name, sys_callqvm_t syscall)\n{\n\tchar path[MAX_QPATH];\n\tvmHeader_t header, *srcheader;\n\tqvm_t *qvm;\n\tqbyte *raw;\n\tint n;\n\tunsigned int i;\n\n\tQ_snprintfz(path, sizeof(path), \"%s.qvm\", name);\n\tFS_LoadFile(path, (void **)&raw);\n// file not found\n\tif(!raw) return false;\n\tsrcheader=(vmHeader_t*)raw;\n\n\theader.vmMagic = LittleLong(srcheader->vmMagic);\n\theader.instructionCount = LittleLong(srcheader->instructionCount);\n\theader.codeOffset = LittleLong(srcheader->codeOffset);\n\theader.codeLength = LittleLong(srcheader->codeLength);\n\theader.dataOffset = LittleLong(srcheader->dataOffset);\n\theader.dataLength = LittleLong(srcheader->dataLength);\n\theader.litLength = LittleLong(srcheader->litLength);\n\theader.bssLength = LittleLong(srcheader->bssLength);\n\n\tif (header.vmMagic==VM_MAGIC2)\n\t{\t//version2 cotains a jump table of sorts\n\t\t//it is redundant information and can be ignored\n\t\t//its also more useful for jit rather than bytecode\n\t\theader.jtrgLength = LittleLong(srcheader->jtrgLength);\n\t}\n\n// check file\n\tif( (header.vmMagic!=VM_MAGIC && header.vmMagic!=VM_MAGIC2) || header.instructionCount<=0 || header.codeLength<=0)\n\t{\n\t\tCon_Printf(\"%s: invalid qvm file\\n\", name);\n\t\tFS_FreeFile(raw);\n\t\treturn false;\n\t}\n\n// create vitrual machine\n\tqvm=Z_Malloc(sizeof(qvm_t));\n\tqvm->len_cs=header.instructionCount+1;\t//bad opcode padding.\n\tqvm->len_ds=header.dataLength+header.litLength+header.bssLength;\n\tqvm->len_ss=256*1024;\t\t\t\t\t\t\t\t\t// 256KB stack space\n\n// memory\n\tqvm->ds_mask = qvm->len_ds*sizeof(qbyte)+(qvm->len_ss+16*4)*sizeof(qbyte);//+4 for a stack check decrease\n\tfor (i = 0; i < sizeof(qvm->ds_mask)*8-1; i++)\n\t{\n\t\tif ((1<<i) >= qvm->ds_mask)\t//is this bit greater than our minimum?\n\t\t\tbreak;\n\t}\n\tqvm->len_ss = (1<<i) - qvm->len_ds*(int)sizeof(qbyte) - 4;\t//expand the stack space to fill it.\n\tqvm->ds_mask = qvm->len_ds*sizeof(qbyte)+(qvm->len_ss+4)*sizeof(qbyte);\n\tqvm->len_ss -= qvm->len_ss&7;\n\n\n\tqvm->mem_size=qvm->len_cs*sizeof(int)*2 + qvm->ds_mask;\n\tqvm->mem_ptr=Z_Malloc(qvm->mem_size);\n// set pointers\n\tqvm->cs=(unsigned int*)qvm->mem_ptr;\n\tqvm->ds=(qbyte*)(qvm->mem_ptr+qvm->len_cs*sizeof(int)*2);\n\tqvm->ss=(qbyte*)((qbyte*)qvm->ds+qvm->len_ds*sizeof(qbyte));\n\t\t//waste 32 bits here.\n\t\t//As the opcodes often check stack 0 and 1, with a backwards stack, 1 can leave the stack area. This is where we compensate for it.\n// setup registers\n\tqvm->pc=qvm->cs;\n\tqvm->sp=(unsigned int*)(qvm->ss+qvm->len_ss);\n\tqvm->bp=qvm->len_ds+qvm->len_ss/2;\n//\tqvm->cycles=0;\n\tqvm->syscall=syscall;\n\n\tqvm->ds_mask--;\n\n\tqvm->min_sp = (unsigned int*)(qvm->ds+qvm->len_ds+qvm->len_ss/2);\n\tqvm->max_sp = (unsigned int*)(qvm->ds+qvm->len_ds+qvm->len_ss);\n\tqvm->min_bp = qvm->len_ds;\n\tqvm->max_bp = qvm->len_ds+qvm->len_ss/2;\n\n\tqvm->bp = qvm->max_bp;\n\n// load instructions\n{\n\tqbyte *src=raw+header.codeOffset;\n\tint *dst=(int*)qvm->cs;\n\tint total=header.instructionCount;\n\tqvm_op_t op;\n\n\tfor(n=0; n<total; n++)\n\t{\n\t\top=*src++;\n\t\t*dst++=(int)op;\n\t\tswitch(op)\n\t\t{\n\t\tcase OP_ENTER:\n\t\tcase OP_LEAVE:\n\t\tcase OP_CONST:\n\t\tcase OP_LOCAL:\n\t\tcase OP_EQ:\n\t\tcase OP_NE:\n\t\tcase OP_LTI:\n\t\tcase OP_LEI:\n\t\tcase OP_GTI:\n\t\tcase OP_GEI:\n\t\tcase OP_LTU:\n\t\tcase OP_LEU:\n\t\tcase OP_GTU:\n\t\tcase OP_GEU:\n\t\tcase OP_EQF:\n\t\tcase OP_NEF:\n\t\tcase OP_LTF:\n\t\tcase OP_LEF:\n\t\tcase OP_GTF:\n\t\tcase OP_GEF:\n\t\tcase OP_BLOCK_COPY:\n\t\t\t*dst++=LittleLong(*(int*)src);\n\t\t\tsrc+=4;\n\t\t\tbreak;\n\t\tcase OP_ARG:\n\t\t\t*dst++=(int)*src++;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\t*dst++=0;\n\t\t\tbreak;\n\t\t}\n\t}\n\t*dst++=OP_BREAK;\t//in case someone 'forgot' the return on the last function.\n\t*dst++=0;\n}\n\n// load data segment\n{\n\tint *src=(int*)(raw+header.dataOffset);\n\tint *dst=(int*)qvm->ds;\n\tint total=header.dataLength/4;\n\n\tfor(n=0; n<total; n++)\n\t\t*dst++=LittleLong(*src++);\n\n\tmemcpy(dst, src, header.litLength);\n}\n\n\tFS_FreeFile(raw);\n\tQ_strncpyz(vm->filename, path, sizeof(vm->filename));\n\tvm->hInst = qvm;\n\treturn true;\n}\n\n/*\n** QVM_UnLoad\n*/\nvoid QVM_UnLoadVM(qvm_t *qvm)\n{\n\tZ_Free(qvm->mem_ptr);\n\tZ_Free(qvm);\n}\n\n\n// ------------------------- * private execution stuff * -------------------------\n\n/*\n** QVM_Goto\n\n(inlined this the old fashioned way)\n*/\n#define QVM_Goto(vm,addr)\t\\\ndo{\t\t\t\t\t\t\t\\\n\tif(addr<0 || addr>vm->len_cs)\t\\\n\t\tSys_Error(\"VM run time error: program jumped off to hyperspace\\n\");\t\\\n\tvm->pc=vm->cs+addr*2;\t\\\n} while(0)\n\n//static void inline QVM_Goto(qvm_t *vm, int addr)\n//{\n//\tif(addr<0 || addr>vm->len_cs)\n//\t\tSys_Error(\"VM run time error: program jumped off to hyperspace\\n\");\n//\tvm->pc=vm->cs+addr*2;\n//}\n\n/*\n** QVM_Call\n**\n** calls function\n*/\ninline static void QVM_Call(qvm_t *vm, int addr)\n{\n\tvm->sp--;\n\tif (vm->sp < vm->min_sp) Sys_Error(\"QVM Stack underflow\");\n\n\tif(addr<0)\n\t{\n\t// system trap function\n\t\t{\n\t\t\tint *fp;\n\n\t\t\tfp=(int*)(vm->ds+vm->bp)+2;\n\t\t\tvm->sp[0] = vm->syscall(vm->ds, vm->ds_mask, -addr-1, fp);\n\t\t\treturn;\n\t\t}\n\t}\n\n\tif(addr>=vm->len_cs)\n\t\tSys_Error(\"VM run time error: program jumped off to hyperspace\\n\");\n\n\tvm->sp[0]=(vm->pc-vm->cs); // push pc /return address/\n\tvm->pc=vm->cs+addr*2;\n\tif (!vm->pc)\n\t\tSys_Error(\"VM run time error: program called the void\\n\");\n}\n\n/*\n** QVM_Enter\n**\n** [oPC][0][.......]| <- oldBP\n** ^BP\n*/\ninline static void QVM_Enter(qvm_t *vm, int size)\n{\n\tint *fp;\n\n\tvm->bp-=size;\n\tif(vm->bp<vm->min_bp)\n\t\tSys_Error(\"VM run time error: out of stack\\n\");\n\n\tfp=(int*)(vm->ds+vm->bp);\n\tfp[0]=vm->sp-vm->max_sp;\t\t\t\t\t// unknown /maybe size/\n\tfp[1]=*vm->sp++;\t// saved PC\n\n\tif (vm->sp > vm->max_sp) Sys_Error(\"QVM Stack overflow\");\n}\n\n/*\n** QVM_Return\n** returns failure when returning to the engine.\n*/\ninline static qboolean QVM_Return(qvm_t *vm, int size)\n{\n\tint *fp;\n\n\tfp=(int*)(vm->ds+vm->bp);\n\tvm->bp+=size;\n\n\tif(vm->bp>vm->max_bp)\n\t\tSys_Error(\"VM run time error: freed too much stack\\n\");\n\n\tif (vm->sp-vm->max_sp != fp[0])\n\t\tSys_Error(\"VM run time error: stack push/pop mismatch \\n\");\n\tvm->pc=vm->cs+fp[1]; // restore PC\n\n\tif((unsigned int)fp[1]>=(unsigned int)(vm->len_cs*2))\t//explicit casts to make sure the C compiler can't make assumptions about overflows.\n\t{\n\t\tif (fp[1] == -1)\n\t\t\treturn false;\t//return to engine.\n\t\tSys_Error(\"VM run time error: program returned to hyperspace (%p, %#x)\\n\", (char*)vm->cs, fp[1]);\n\t}\n\treturn true;\n}\n\n// ------------------------- * execution * -------------------------\n\n/*\n** VM_Exec\n*/\nint QVM_ExecVM(register qvm_t *qvm, int command, int arg0, int arg1, int arg2, int arg3, int arg4, int arg5, int arg6, int arg7)\n{\n//remember that the stack is backwards. push takes 1.\n\n//FIXME: does it matter that our stack pointer (qvm->sp) is backwards compared to q3?\n//We are more consistant of course, but this simply isn't what q3 does.\n\n//all stack shifts in this function are referenced through these 2 macros.\n#define POP(t)\tqvm->sp+=t;if (qvm->sp > qvm->max_sp) Sys_Error(\"QVM Stack underflow\");\n#define PUSH(v) qvm->sp--;if (qvm->sp < qvm->min_sp) Sys_Error(\"QVM Stack overflow\");*qvm->sp=v\n\tqvm_op_t op=-1;\n\tunsigned int param;\n\n\tint *fp;\n\tunsigned int *oldpc;\n\n\toldpc = qvm->pc;\n\n// setup execution environment\n\tqvm->pc=qvm->cs-1;\n//\tqvm->cycles=0;\n// prepare local stack\n\tqvm->bp -= 15*4;\t//we have to do this each call for the sake of (reliable) recursion.\n\tfp=(int*)(qvm->ds+qvm->bp);\n// push all params\n\tfp[0]=0;\n\tfp[1]=0;\n\tfp[2]=command;\n\tfp[3]=arg0;\n\tfp[4]=arg1;\n\tfp[5]=arg2;\n\tfp[6]=arg3;\n\tfp[7]=arg4;\n\tfp[8]=arg5;\n\tfp[9]=arg6;\n\tfp[10]=arg7;\t// arg7;\n\tfp[11]=0;\t// arg8;\n\tfp[12]=0;\t// arg9;\n\tfp[13]=0;\t// arg10;\n\tfp[14]=0;\t// arg11;\n\n\tQVM_Call(qvm, 0);\n\n\tfor(;;)\n\t{\n\t// fetch next command\n \t\top=*qvm->pc++;\n\t\tparam=*qvm->pc++;\n//\t\tqvm->cycles++;\n\n\t\tswitch(op)\n\t\t{\n\t// aux\n\t\tcase OP_UNDEF:\n\t\tcase OP_NOP:\n\t\t\tbreak;\n\t\tdefault:\n\t\tcase OP_BREAK: // break to debugger\n\t\t\tSys_Error(\"VM hit an OP_BREAK opcode\");\n\t\t\tbreak;\n\n\t// subroutines\n\t\tcase OP_ENTER:\n\t\t\tQVM_Enter(qvm, param);\n\t\t\tbreak;\n\t\tcase OP_LEAVE:\n\t\t\tif (!QVM_Return(qvm, param))\n\t\t\t{\n\t\t\t\t// pick return value from C stack\n\t\t\t\tqvm->pc = oldpc;\n\n\t\t\t\tqvm->bp += 15*4;\n//\t\t\t\tif(qvm->bp!=qvm->max_bp)\n//\t\t\t\t\tSys_Error(\"VM run time error: freed too much stack\\n\");\n\t\t\t\tparam = qvm->sp[0];\n\t\t\t\tPOP(1);\n\t\t\t\treturn param;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase OP_CALL:\n\t\t\tparam = *qvm->sp;\n\t\t\tPOP(1);\n\t\t\tQVM_Call(qvm, param);\n\t\t\tbreak;\n\n\t// stack\n\t\tcase OP_PUSH:\n\t\t\tPUSH(*qvm->sp);\n\t\t\tbreak;\n\t\tcase OP_POP:\n\t\t\tPOP(1);\n\t\t\tbreak;\n\t\tcase OP_CONST:\n\t\t\tPUSH(param);\n\t\t\tbreak;\n\t\tcase OP_LOCAL:\n\t\t\tPUSH(param+qvm->bp);\n\t\t\tbreak;\n\n\t// branching\n\t\tcase OP_JUMP:\n\t\t\tparam = *qvm->sp;\n\t\t\tPOP(1);\n\t\t\tQVM_Goto(qvm, param);\n\t\t\tbreak;\n\t\tcase OP_EQ:\n\t\t\tif(qvm->sp[1]==qvm->sp[0]) QVM_Goto(qvm, param);\n\t\t\tPOP(2);\n\t\t\tbreak;\n\t\tcase OP_NE:\n\t\t\tif(qvm->sp[1]!=qvm->sp[0]) QVM_Goto(qvm, param);\n\t\t\tPOP(2);\n\t\t\tbreak;\n\t\tcase OP_LTI:\n\t\t\tif(*(signed int*)&qvm->sp[1]<*(signed int*)&qvm->sp[0]) QVM_Goto(qvm, param);\n\t\t\tPOP(2);\n\t\t\tbreak;\n\t\tcase OP_LEI:\n\t\t\tif(*(signed int*)&qvm->sp[1]<=*(signed int*)&qvm->sp[0]) QVM_Goto(qvm, param);\n\t\t\tPOP(2);\n\t\t\tbreak;\n\t\tcase OP_GTI:\n\t\t\tif(*(signed int*)&qvm->sp[1]>*(signed int*)&qvm->sp[0]) QVM_Goto(qvm, param);\n\t\t\tPOP(2);\n\t\t\tbreak;\n\t\tcase OP_GEI:\n\t\t\tif(*(signed int*)&qvm->sp[1]>=*(signed int*)&qvm->sp[0]) QVM_Goto(qvm, param);\n\t\t\tPOP(2);\n\t\t\tbreak;\n\t\tcase OP_LTU:\n\t\t\tif(*(unsigned int*)&qvm->sp[1]<*(unsigned int*)&qvm->sp[0]) QVM_Goto(qvm, param);\n\t\t\tPOP(2);\n\t\t\tbreak;\n\t\tcase OP_LEU:\n\t\t\tif(*(unsigned int*)&qvm->sp[1]<=*(unsigned int*)&qvm->sp[0]) QVM_Goto(qvm, param);\n\t\t\tPOP(2);\n\t\t\tbreak;\n\t\tcase OP_GTU:\n\t\t\tif(*(unsigned int*)&qvm->sp[1]>*(unsigned int*)&qvm->sp[0]) QVM_Goto(qvm, param);\n\t\t\tPOP(2);\n\t\t\tbreak;\n\t\tcase OP_GEU:\n\t\t\tif(*(unsigned int*)&qvm->sp[1]>=*(unsigned int*)&qvm->sp[0]) QVM_Goto(qvm, param);\n\t\t\tPOP(2);\n\t\t\tbreak;\n\t\tcase OP_EQF:\n\t\t\tif(*(float*)&qvm->sp[1]==*(float*)&qvm->sp[0]) QVM_Goto(qvm, param);\n\t\t\tPOP(2);\n\t\t\tbreak;\n\t\tcase OP_NEF:\n\t\t\tif(*(float*)&qvm->sp[1]!=*(float*)&qvm->sp[0]) QVM_Goto(qvm, param);\n\t\t\tPOP(2);\n\t\t\tbreak;\n\t\tcase OP_LTF:\n\t\t\tif(*(float*)&qvm->sp[1]<*(float*)&qvm->sp[0]) QVM_Goto(qvm, param);\n\t\t\tPOP(2);\n\t\t\tbreak;\n\t\tcase OP_LEF:\n\t\t\tif(*(float*)&qvm->sp[1]<=*(float*)&qvm->sp[0]) QVM_Goto(qvm, param);\n\t\t\tPOP(2);\n\t\t\tbreak;\n\t\tcase OP_GTF:\n\t\t\tif(*(float*)&qvm->sp[1]>*(float*)&qvm->sp[0]) QVM_Goto(qvm, param);\n\t\t\tPOP(2);\n\t\t\tbreak;\n\t\tcase OP_GEF:\n\t\t\tif(*(float*)&qvm->sp[1]>=*(float*)&qvm->sp[0]) QVM_Goto(qvm, param);\n\t\t\tPOP(2);\n\t\t\tbreak;\n\n\t// memory I/O: masks protect main memory\n\t\tcase OP_LOAD1:\n\t\t\t*(unsigned int*)&qvm->sp[0]=*(unsigned char*)&qvm->ds[qvm->sp[0]&qvm->ds_mask];\n\t\t\tbreak;\n\t\tcase OP_LOAD2:\n\t\t\t*(unsigned int*)&qvm->sp[0]=*(unsigned short*)&qvm->ds[qvm->sp[0]&qvm->ds_mask];\n\t\t\tbreak;\n\t\tcase OP_LOAD4:\n\t\t\t*(unsigned int*)&qvm->sp[0]=*(unsigned int*)&qvm->ds[qvm->sp[0]&qvm->ds_mask];\n\t\t\tbreak;\n\t\tcase OP_STORE1:\n\t\t\t*(qbyte*)&qvm->ds[qvm->sp[1]&qvm->ds_mask]=(qbyte)(qvm->sp[0]&0xFF);\n\t\t\tPOP(2);\n\t\t\tbreak;\n\t\tcase OP_STORE2:\n\t\t\t*(unsigned short*)&qvm->ds[qvm->sp[1]&qvm->ds_mask]=(unsigned short)(qvm->sp[0]&0xFFFF);\n\t\t\tPOP(2);\n\t\t\tbreak;\n\t\tcase OP_STORE4:\n\t\t\t*(unsigned int*)&qvm->ds[qvm->sp[1]&qvm->ds_mask]=*(unsigned int*)&qvm->sp[0];\n\t\t\tPOP(2);\n\t\t\tbreak;\n\t\tcase OP_ARG:\n\t\t\t*(unsigned int*)&qvm->ds[(param+qvm->bp)&qvm->ds_mask]=*(unsigned int*)&qvm->sp[0];\n\t\t\tPOP(1);\n\t\t\tbreak;\n\t\tcase OP_BLOCK_COPY:\n\t\t\tif (qvm->sp[1]+param < qvm->ds_mask && qvm->sp[0] + param < qvm->ds_mask)\n\t\t\t{\n\t\t\t\tmemmove(qvm->ds+(qvm->sp[1]&qvm->ds_mask), qvm->ds+(qvm->sp[0]&qvm->ds_mask), param);\n\t\t\t}\n\t\t\tPOP(2);\n\t\t\tbreak;\n\n\t// integer arithmetic\n\t\tcase OP_SEX8:\n\t\t\tif(*(signed int*)&qvm->sp[0]&0x80) *(signed int*)&qvm->sp[0]|=0xFFFFFF00;\n\t\t\tbreak;\n\t\tcase OP_SEX16:\n\t\t\tif(*(signed int*)&qvm->sp[0]&0x8000) *(signed int*)&qvm->sp[0]|=0xFFFF0000;\n\t\t\tbreak;\n\t\tcase OP_NEGI:\n\t\t\t*(signed int*)&qvm->sp[0]=-*(signed int*)&qvm->sp[0];\n\t\t\tbreak;\n\t\tcase OP_ADD:\n\t\t\t*(signed int*)&qvm->sp[1]+=*(signed int*)&qvm->sp[0];\n\t\t\tPOP(1);\n\t\t\tbreak;\n\t\tcase OP_SUB:\n\t\t\t*(signed int*)&qvm->sp[1]-=*(signed int*)&qvm->sp[0];\n\t\t\tPOP(1);\n\t\t\tbreak;\n\t\tcase OP_DIVI:\n\t\t\t*(signed int*)&qvm->sp[1]/=*(signed int*)&qvm->sp[0];\n\t\t\tPOP(1);\n\t\t\tbreak;\n\t\tcase OP_DIVU:\n\t\t\t*(unsigned int*)&qvm->sp[1]/=(*(unsigned int*)&qvm->sp[0]);\n\t\t\tPOP(1);\n\t\t\tbreak;\n\t\tcase OP_MODI:\n\t\t\t*(signed int*)&qvm->sp[1]%=*(signed int*)&qvm->sp[0];\n\t\t\tPOP(1);\n\t\t\tbreak;\n\t\tcase OP_MODU:\n\t\t\t*(unsigned int*)&qvm->sp[1]%=(*(unsigned int*)&qvm->sp[0]);\n\t\t\tPOP(1);\n\t\t\tbreak;\n\t\tcase OP_MULI:\n\t\t\t*(signed int*)&qvm->sp[1]*=*(signed int*)&qvm->sp[0];\n\t\t\tPOP(1);\n\t\t\tbreak;\n\t\tcase OP_MULU:\n\t\t\t*(unsigned int*)&qvm->sp[1]*=(*(unsigned int*)&qvm->sp[0]);\n\t\t\tPOP(1);\n\t\t\tbreak;\n\n\t// logic\n\t\tcase OP_BAND:\n\t\t\t*(unsigned int*)&qvm->sp[1]&=*(unsigned int*)&qvm->sp[0];\n\t\t\tPOP(1);\n\t\t\tbreak;\n\t\tcase OP_BOR:\n\t\t\t*(unsigned int*)&qvm->sp[1]|=*(unsigned int*)&qvm->sp[0];\n\t\t\tPOP(1);\n\t\t\tbreak;\n\t\tcase OP_BXOR:\n\t\t\t*(unsigned int*)&qvm->sp[1]^=*(unsigned int*)&qvm->sp[0];\n\t\t\tPOP(1);\n\t\t\tbreak;\n\t\tcase OP_BCOM:\n\t\t\t*(unsigned int*)&qvm->sp[0]=~*(unsigned int*)&qvm->sp[0];\n\t\t\tbreak;\n\t\tcase OP_LSH:\n\t\t\t*(unsigned int*)&qvm->sp[1]<<=*(unsigned int*)&qvm->sp[0];\n\t\t\tPOP(1);\n\t\t\tbreak;\n\t\tcase OP_RSHI:\n\t\t\t*(signed int*)&qvm->sp[1]>>=*(signed int*)&qvm->sp[0];\n\t\t\tPOP(1);\n\t\t\tbreak;\n\t\tcase OP_RSHU:\n\t\t\t*(unsigned int*)&qvm->sp[1]>>=*(unsigned int*)&qvm->sp[0];\n\t\t\tPOP(1);\n\t\t\tbreak;\n\n\t// floating point arithmetic\n\t\tcase OP_NEGF:\n\t\t\t*(float*)&qvm->sp[0]=-*(float*)&qvm->sp[0];\n\t\t\tbreak;\n\t\tcase OP_ADDF:\n\t\t\t*(float*)&qvm->sp[1]+=*(float*)&qvm->sp[0];\n\t\t\tPOP(1);\n\t\t\tbreak;\n\t\tcase OP_SUBF:\n\t\t\t*(float*)&qvm->sp[1]-=*(float*)&qvm->sp[0];\n\t\t\tPOP(1);\n\t\t\tbreak;\n\t\tcase OP_DIVF:\n\t\t\t*(float*)&qvm->sp[1]/=*(float*)&qvm->sp[0];\n\t\t\tPOP(1);\n\t\t\tbreak;\n\t\tcase OP_MULF:\n\t\t\t*(float*)&qvm->sp[1]*=*(float*)&qvm->sp[0];\n\t\t\tPOP(1);\n\t\t\tbreak;\n\n\t// format conversion\n\t\tcase OP_CVIF:\n\t\t\t*(float*)&qvm->sp[0]=(float)(signed int)qvm->sp[0];\n\t\t\tbreak;\n\t\tcase OP_CVFI:\n\t\t\t*(signed int*)&qvm->sp[0]=(signed int)(*(float*)&qvm->sp[0]);\n\t\t\tbreak;\n\t\t}\n\t}\n}\n\n\n\n\n\n\n\n\n\n\n\n// ------------------------- * interface * -------------------------\n\n/*\n** VM_PrintInfo\n*/\nvoid VM_PrintInfo(vm_t *vm)\n{\n\tqvm_t *qvm;\n\n//\tCon_Printf(\"%s (%p): \", vm->name, vm->hInst);\n\tCon_Printf(\"^2%s\", vm->filename);\n\n\tswitch(vm->type)\n\t{\n\tcase VM_NATIVE:\n\t\tCon_Printf(\": native\\n\");\n\t\tbreak;\n\tcase VM_BUILTIN:\n\t\tCon_Printf(\": built in\\n\");\n\t\tbreak;\n\n\tcase VM_BYTECODE:\n\t\tCon_Printf(\": interpreted\\n\");\n\t\tif((qvm=vm->hInst))\n\t\t{\n\t\t\tCon_Printf(\"  code  length: %d\\n\", qvm->len_cs);\n\t\t\tCon_Printf(\"  data  length: %d\\n\", qvm->len_ds);\n\t\t\tCon_Printf(\"  stack length: %d\\n\", qvm->len_ss);\n\t\t}\n\t\tbreak;\n\n\tdefault:\n\t\tCon_Printf(\": unknown\\n\");\n\t\tbreak;\n\t}\n}\n\nconst char *VM_GetFilename(vm_t *vm)\n{\n\treturn vm->filename;\n}\n\nvm_t *VM_CreateBuiltin(const char *name, sys_calldll_t syscalldll, qintptr_t (*init)(qintptr_t *args))\n{\n\tvm_t *vm = Z_Malloc(sizeof(vm_t));\n\tQ_strncpyz(vm->filename, name, sizeof(vm->filename));\n\tvm->syscalldll\t= syscalldll;\n\tvm->syscallqvm\t= NULL;\n\tvm->hInst\t\t= init;\n\tvm->type\t\t= VM_BUILTIN;\n\treturn vm;\n}\n\n/*\n** VM_Create\n*/\nvm_t *VM_Create(const char *dllname, sys_calldll_t syscalldll, const char *qvmname, sys_callqvm_t syscallqvm)\n{\n\tvm_t *vm;\n\n\tvm = Z_Malloc(sizeof(vm_t));\n\n// prepare vm struct\n\tmemset(vm, 0, sizeof(vm_t));\n\tQ_strncpyz(vm->filename, \"\", sizeof(vm->filename));\n\tvm->syscalldll=syscalldll;\n\tvm->syscallqvm=syscallqvm;\n\n\n\n\tif (syscalldll)\n\t{\n\t\tif (!COM_CheckParm(\"-nodlls\") && !COM_CheckParm(\"-nosos\"))\t//:)\n\t\t{\n\t\t\tif(QVM_LoadDLL(vm, dllname, !syscallqvm, (void**)&vm->vmMain, syscalldll))\n\t\t\t{\n\t\t\t\tCon_DPrintf(\"Creating native machine \\\"%s\\\"\\n\", dllname);\n\t\t\t\tvm->type=VM_NATIVE;\n\t\t\t\treturn vm;\n\t\t\t}\n\t\t}\n\t}\n\n\n\tif (syscallqvm)\n\t{\n\t\tif(QVM_LoadVM(vm, qvmname, syscallqvm))\n\t\t{\n\t\t\tCon_DPrintf(\"Creating virtual machine \\\"%s\\\"\\n\", qvmname);\n\t\t\tvm->type=VM_BYTECODE;\n\t\t\treturn vm;\n\t\t}\n\t}\n\n\tZ_Free(vm);\n\treturn NULL;\n}\n\n/*\n** VM_Destroy\n*/\nvoid VM_Destroy(vm_t *vm)\n{\n\tif(!vm) return;\n\n\tswitch(vm->type)\n\t{\n\tcase VM_NATIVE:\n\t\tif(vm->hInst) QVM_UnloadDLL(vm->hInst);\n\t\tbreak;\n\n\tcase VM_BYTECODE:\n\t\tif(vm->hInst) QVM_UnLoadVM(vm->hInst);\n\t\tbreak;\n\n\tcase VM_BUILTIN:\n\tcase VM_NONE:\n\t\tbreak;\n\t}\n\n\tZ_Free(vm);\n}\n\n/*\n** VM_Restart\n*/\n/*qboolean VM_Restart(vm_t *vm)\n{\n\tchar name[MAX_QPATH];\n\tsys_calldll_t syscalldll;\n\tsys_callqvm_t syscallqvm;\n\n\tif(!vm) return false;\n\n// save params\n\tQ_strncpyz(name, vm->name, sizeof(name));\n\tsyscalldll=vm->syscalldll;\n\tsyscallqvm=vm->syscallqvm;\n\n// restart\n\tswitch(vm->type)\n\t{\n\tcase VM_NATIVE:\n\t\tif(vm->hInst) QVM_UnloadDLL(vm->hInst);\n\t\tbreak;\n\n\tcase VM_BYTECODE:\n\t\tif(vm->hInst) QVM_UnLoadVM(vm->hInst);\n\t\tbreak;\n\n\tcase VM_NONE:\n\t\tbreak;\n\t}\n\n\treturn VM_Create(vm, name, syscalldll, syscallqvm)!=NULL;\n}*/\n\nvoid *VM_MemoryBase(vm_t *vm)\n{\n\tswitch(vm->type)\n\t{\n\tcase VM_NATIVE:\n\tcase VM_BUILTIN:\n\t\treturn NULL;\n\tcase VM_BYTECODE:\n\t\treturn ((qvm_t*)vm->hInst)->ds;\n\tdefault:\n\t\treturn NULL;\n\t}\n}\nquintptr_t VM_MemoryMask(vm_t *vm)\n{\n\tswitch(vm->type)\n\t{\n\tcase VM_BYTECODE:\n\t\treturn ((qvm_t*)vm->hInst)->ds_mask;\n\tdefault:\n\t\treturn ~(quintptr_t)0;\n\t}\n}\n\n/*returns true if we're running a 32bit vm on a 64bit host (in case we need workarounds)*/\nqboolean VM_NonNative(vm_t *vm)\n{\n\tswitch(vm->type)\n\t{\n\tcase VM_BYTECODE:\n\t\treturn sizeof(int) != sizeof(void*);\n\tcase VM_NATIVE:\n\tcase VM_BUILTIN:\n\t\treturn false;\n\tdefault:\n\t\treturn false;\n\t}\n}\n\n/*\n** VM_Call\n*/\nqintptr_t VARGS VM_Call(vm_t *vm, qintptr_t instruction, ...)\n{\n\tva_list argptr;\n\tqintptr_t arg[8];\n\n\tif(!vm) Sys_Error(\"VM_Call with NULL vm\");\n\n\tva_start(argptr, instruction);\n\targ[0]=va_arg(argptr, qintptr_t);\n\targ[1]=va_arg(argptr, qintptr_t);\n\targ[2]=va_arg(argptr, qintptr_t);\n\targ[3]=va_arg(argptr, qintptr_t);\n\targ[4]=va_arg(argptr, qintptr_t);\n\targ[5]=va_arg(argptr, qintptr_t);\n\targ[6]=va_arg(argptr, qintptr_t);\n\targ[7]=va_arg(argptr, qintptr_t);\n\tva_end(argptr);\n\n\tswitch(vm->type)\n\t{\n\tcase VM_NATIVE:\n\t\treturn vm->vmMain(instruction, arg[0], arg[1], arg[2], arg[3], arg[4], arg[5], arg[6], arg[7]);\n\n\tcase VM_BYTECODE:\n\t\treturn QVM_ExecVM(vm->hInst, instruction, arg[0]&0xffffffff, arg[1]&0xffffffff, arg[2]&0xffffffff, arg[3]&0xffffffff, arg[4]&0xffffffff, arg[5]&0xffffffff, arg[6]&0xffffffff, arg[7]&0xffffffff);\n\n\tcase VM_BUILTIN:\n\t\tif (!instruction)\n\t\t\tinstruction = (qintptr_t)vm->hInst;\n\t\treturn ((qintptr_t(*)(qintptr_t*))instruction)(arg);\n\n\tcase VM_NONE:\n\t\treturn 0;\n\t}\n\treturn 0;\n}\n\n#endif\n"
  },
  {
    "path": "engine/common/sha1.c",
    "content": "/*\r\nSHA-1 in C\r\nBy Steve Reid <steve@edmweb.com>\r\n100% Public Domain\r\n\r\nTest Vectors (from FIPS PUB 180-1)\r\n\"abc\"\r\nA9993E36 4706816A BA3E2571 7850C26C 9CD0D89D\r\n\"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq\"\r\n84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1\r\nA million repetitions of \"a\"\r\n34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F\r\n\r\nThis file came to FTE via EzQuake.\r\n*/\r\n\r\n#include \"quakedef.h\"\r\n#include <string.h>\r\n\r\n/* #define SHA1HANDSOFF * Copies data before messing with it. */\r\n#define SHA1HANDSOFF\r\n\r\ntypedef struct\r\n{\r\n    unsigned int state[5];\r\n    unsigned int count[2];\r\n    unsigned char buffer[64];\r\n} SHA1_CTX;\r\n#define SHA1_DIGEST_SIZE 20\r\n\r\n#define ShaBigLong(l)  (((unsigned char*)&l)[0]<<24) | (((unsigned char*)&l)[1]<<16) | (((unsigned char*)&l)[2]<<8) | (((unsigned char*)&l)[3]<<0)\r\n\r\n\r\n#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))\r\n\r\n#define blk0(i) (block->l[i] = ShaBigLong(block->l[i]))\r\n\r\n#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \\\r\n^block->l[(i+2)&15]^block->l[i&15],1))\r\n\r\n/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */\r\n#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30);\r\n#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30);\r\n#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30);\r\n#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30);\r\n#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30);\r\n\r\n/* Hash a single 512-bit block. This is the core of the algorithm. */\r\n\r\nstatic void SHA1Transform(unsigned int state[5], const unsigned char buffer[64])\r\n{\r\n\tunsigned int a, b, c, d, e;\r\n\ttypedef union\r\n\t{\r\n\t\tunsigned char c[64];\r\n\t\tunsigned int l[16];\r\n\t} CHAR64LONG16;\r\n\tCHAR64LONG16* block;\r\n\t#ifdef SHA1HANDSOFF\r\n\tunsigned char workspace[64];\r\n\tblock = (CHAR64LONG16*)workspace;\r\n\tmemcpy(block, buffer, 64);\r\n\t#else\r\n\tblock = (CHAR64LONG16*)buffer;\r\n\t#endif\r\n\t/* Copy context->state[] to working vars */\r\n\ta = state[0];\r\n\tb = state[1];\r\n\tc = state[2];\r\n\td = state[3];\r\n\te = state[4];\r\n\t/* 4 rounds of 20 operations each. Loop unrolled. */\r\n\tR0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3);\r\n\tR0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7);\r\n\tR0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11);\r\n\tR0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15);\r\n\tR1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19);\r\n\tR2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23);\r\n\tR2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27);\r\n\tR2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31);\r\n\tR2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35);\r\n\tR2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39);\r\n\tR3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43);\r\n\tR3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47);\r\n\tR3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51);\r\n\tR3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55);\r\n\tR3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59);\r\n\tR4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63);\r\n\tR4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67);\r\n\tR4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71);\r\n\tR4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75);\r\n\tR4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79);\r\n\t/* Add the working vars back into context.state[] */\r\n\tstate[0] += a;\r\n\tstate[1] += b;\r\n\tstate[2] += c;\r\n\tstate[3] += d;\r\n\tstate[4] += e;\r\n\t/* Wipe variables */\r\n\ta = b = c = d = e = 0;\r\n}\r\n\r\n\r\n/* SHA1Init - Initialize new context */\r\n\r\nstatic void SHA1Init(void *ctx)\r\n{\r\n\tSHA1_CTX *context = ctx;\r\n    /* SHA1 initialization constants */\r\n    context->state[0] = 0x67452301;\r\n    context->state[1] = 0xEFCDAB89;\r\n    context->state[2] = 0x98BADCFE;\r\n    context->state[3] = 0x10325476;\r\n    context->state[4] = 0xC3D2E1F0;\r\n    context->count[0] = context->count[1] = 0;\r\n}\r\n\r\n\r\n/* Run your data through this. */\r\n\r\nstatic void SHA1Update(void *ctx, const void* data, size_t len)\r\n{\r\n\tSHA1_CTX *context = ctx;\r\n\tsize_t i, j;\r\n\r\n\tj = (context->count[0] >> 3) & 63;\r\n\tif ((context->count[0] += len << 3) < (len << 3)) context->count[1]++;\r\n\tcontext->count[1] += (len >> 29);\r\n\tif ((j + len) > 63)\r\n\t{\r\n\t\tmemcpy(&context->buffer[j], data, (i = 64-j));\r\n\t\tSHA1Transform(context->state, context->buffer);\r\n\t\tfor ( ; i + 63 < len; i += 64)\r\n\t\t{\r\n\t\t\tSHA1Transform(context->state, (const qbyte*)data + i);\r\n\t\t}\r\n\t\tj = 0;\r\n\t}\r\n\telse\r\n\t\ti = 0;\r\n\tmemcpy(&context->buffer[j], (const qbyte*)data + i, len - i);\r\n}\r\n\r\n\r\n/* Add padding and return the message digest. */\r\n\r\nstatic void SHA1Final(unsigned char digest[SHA1_DIGEST_SIZE], void *ctx)\r\n{\r\n\tSHA1_CTX *context = ctx;\r\n\tunsigned int i, j;\r\n\tunsigned char finalcount[8];\r\n\r\n\tfor (i = 0; i < 8; i++)\r\n\t{\r\n\t\tfinalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)] >> ((3-(i & 3)) * 8) ) & 255); /* Endian independent */\r\n\t}\r\n\tSHA1Update(context, (unsigned char *)\"\\200\", 1);\r\n\twhile ((context->count[0] & 504) != 448)\r\n\t{\r\n\t\tSHA1Update(context, (unsigned char *)\"\\0\", 1);\r\n\t}\r\n\tSHA1Update(context, finalcount, 8); /* Should cause a SHA1Transform() */\r\n\tfor (i = 0; i < SHA1_DIGEST_SIZE; i++)\r\n\t{\r\n\t\tdigest[i] = (unsigned char)\r\n\t\t((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);\r\n\t}\r\n\t/* Wipe variables */\r\n\ti = j = 0;\r\n\tmemset(context->buffer, 0, 64);\r\n\tmemset(context->state, 0, 20);\r\n\tmemset(context->count, 0, 8);\r\nmemset(&finalcount, 0, 8);\r\n#ifdef SHA1HANDSOFF /* make SHA1Transform overwrite it's own static vars */\r\n\tSHA1Transform(context->state, context->buffer);\r\n#endif\r\n}\r\n\r\nhashfunc_t hash_sha1 =\r\n{\r\n\tSHA1_DIGEST_SIZE,\r\n\tsizeof(SHA1_CTX),\r\n\tSHA1Init,\r\n\tSHA1Update,\r\n\tSHA1Final,\r\n};\r\n\r\nunsigned int hashfunc_terminate_uint(const hashfunc_t *func, void *context)\r\n{\r\n\tunsigned int r = 0, l;\r\n\tunsigned char *digest = alloca(func->digestsize);\r\n\tfunc->terminate(digest, context);\r\n\tfor (l = 0; l < func->digestsize; l++)\r\n\t\tr ^= digest[l]<<((l%sizeof(r))*8);\r\n\treturn r;\r\n}\r\nunsigned int CalcHashInt(const hashfunc_t *func, const void *data, size_t datasize)\r\n{\r\n\tvoid *ctx = alloca(func->contextsize);\r\n\tfunc->init(ctx);\r\n\tfunc->process(ctx, data, datasize);\r\n\treturn hashfunc_terminate_uint(func, ctx);\r\n}\r\nsize_t CalcHash(const hashfunc_t *func, unsigned char *digest, size_t maxdigestsize, const unsigned char *string, size_t stringlen)\r\n{\r\n\tvoid *ctx = alloca(func->contextsize);\r\n\tif (maxdigestsize < func->digestsize)\r\n\t\treturn 0;\t//panic\r\n\tfunc->init(ctx);\r\n\tfunc->process(ctx, string, stringlen);\r\n\tfunc->terminate(digest, ctx);\r\n\treturn func->digestsize;\r\n}\r\n\r\n/* hmac-sha1.c -- hashed message authentication codes\r\nCopyright (C) 2005, 2006 Free Software Foundation, Inc.\r\n\r\nThis program is free software; you can redistribute it and/or modify\r\nit under the terms of the GNU General Public License as published by\r\nthe Free Software Foundation; either version 2, or (at your option)\r\nany later version.\r\n\r\nThis program is distributed in the hope that it will be useful,\r\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\r\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r\nGNU General Public License for more details.\r\n\r\nYou should have received a copy of the GNU General Public License\r\nalong with this program; if not, write to the Free Software Foundation,\r\nInc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */\r\n\r\n/* Written by Simon Josefsson.\r\nhacked up a bit by someone else...\r\n*/\r\n\r\n#define IPAD 0x36\r\n#define OPAD 0x5c\r\n\r\nstatic void memxor(char *dest, const char *src, size_t length)\r\n{\r\n\tsize_t i;\r\n\tfor (i = 0; i < length; i++)\r\n\t{\r\n\t\tdest[i] ^= src[i];\r\n\t}\r\n}\r\n\r\n//typedef size_t hashfunc_t(unsigned char *digest, size_t maxdigestsize, size_t numstrings, const unsigned char **strings, size_t *stringlens);\r\nsize_t CalcHMAC(const hashfunc_t *hashfunc, unsigned char *digest, size_t maxdigestsize,\r\n\t\t\t\t const unsigned char *data, size_t datalen,\r\n\t\t\t\t const unsigned char *key, size_t keylen)\r\n{\r\n#define HMAC_DIGEST_MAXSIZE 64\r\n\tqbyte optkeybuf[HMAC_DIGEST_MAXSIZE];\r\n\tqbyte innerhash[HMAC_DIGEST_MAXSIZE];\r\n\r\n\tqbyte block[64];\r\n\r\n\tif (hashfunc->digestsize > HMAC_DIGEST_MAXSIZE || hashfunc->digestsize > maxdigestsize)\r\n\t\treturn 0;\r\n\r\n\t/* Reduce the key's size, so that it is never larger than a block. */\r\n\r\n\tif (keylen > sizeof(block))\r\n\t{\r\n\t\tqbyte *ctx = alloca(hashfunc->contextsize);\r\n\t\thashfunc->init(ctx);\r\n\t\thashfunc->process(ctx, key, keylen);\r\n\t\thashfunc->terminate(optkeybuf, ctx);\r\n\t\tkey=optkeybuf;\r\n\t}\r\n\r\n\t/* Compute INNERHASH from KEY and IN. */\r\n\r\n\tmemset (block, IPAD, sizeof (block));\r\n\tmemxor (block, key, keylen);\r\n\r\n\t{\r\n\t\tqbyte *ctx = alloca(hashfunc->contextsize);\r\n\t\thashfunc->init(ctx);\r\n\t\thashfunc->process(ctx, block, sizeof(block));\r\n\t\thashfunc->process(ctx, data, datalen);\r\n\t\thashfunc->terminate(innerhash, ctx);\r\n\t}\r\n\r\n\t/* Compute result from KEY and INNERHASH. */\r\n\r\n\tmemset (block, OPAD, sizeof (block));\r\n\tmemxor (block, key, keylen);\r\n\r\n\t{\r\n\t\tqbyte *ctx = alloca(hashfunc->contextsize);\r\n\t\thashfunc->init(ctx);\r\n\t\thashfunc->process(ctx, block, sizeof(block));\r\n\t\thashfunc->process(ctx, innerhash, hashfunc->digestsize);\r\n\t\thashfunc->terminate(digest, ctx);\r\n\t\treturn hashfunc->digestsize;\r\n\t}\r\n}\r\n"
  },
  {
    "path": "engine/common/sha2.c",
    "content": "/* sha512.c - SHA384 and SHA512 hash functions\n * Copyright (C) 2003, 2008, 2009 Free Software Foundation, Inc.\n *\n * This file is part of Libgcrypt.\n *\n * Libgcrypt is free software; you can redistribute it and/or modify\n * it under the terms of the GNU Lesser general Public License as\n * published by the Free Software Foundation; either version 2.1 of\n * the License, or (at your option) any later version.\n *\n * Libgcrypt is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Lesser General Public License for more details.\n *\n * You should have received a copy of the GNU Lesser General Public\n * License along with this program; if not, see <http://www.gnu.org/licenses/>.\n */\n\n\n/*  Test vectors from FIPS-180-2:\n *\n *  \"abc\"\n * 384:\n *  CB00753F 45A35E8B B5A03D69 9AC65007 272C32AB 0EDED163\n *  1A8B605A 43FF5BED 8086072B A1E7CC23 58BAECA1 34C825A7\n * 512:\n *  DDAF35A1 93617ABA CC417349 AE204131 12E6FA4E 89A97EA2 0A9EEEE6 4B55D39A\n *  2192992A 274FC1A8 36BA3C23 A3FEEBBD 454D4423 643CE80E 2A9AC94F A54CA49F\n *\n *  \"abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu\"\n * 384:\n *  09330C33 F71147E8 3D192FC7 82CD1B47 53111B17 3B3B05D2\n *  2FA08086 E3B0F712 FCC7C71A 557E2DB9 66C3E9FA 91746039\n * 512:\n *  8E959B75 DAE313DA 8CF4F728 14FC143F 8F7779C6 EB9F7FA1 7299AEAD B6889018\n *  501D289E 4900F7E4 331B99DE C4B5433A C7D329EE B6DD2654 5E96E55B 874BE909\n *\n *  \"a\" x 1000000\n * 384:\n *  9D0E1809 716474CB 086E834E 310A4A1C ED149E9C 00F24852\n *  7972CEC5 704C2A5B 07B8B3DC 38ECC4EB AE97DDD8 7F3D8985\n * 512:\n *  E718483D 0CE76964 4E2E42C7 BC15B463 8E1F98B1 3B204428 5632A803 AFA973EB\n *  DE0FF244 877EA60A 4CB0432C E577C31B EB009C5C 2C49AA2E 4EADB217 AD8CC09B\n */\n\n#include \"quakedef.h\"\n\n#ifndef SHA2\n#define SHA2 256\n#include \"sha2.c\"\n#undef SHA2\n#define SHA2 512\n#endif\n\n#undef U64_C\n#undef U64_C_LOW\n#undef u64\n#undef ROUNDS\n#undef SHA2_CONTEXT\n#undef sha2trunc_init\n#undef sha2_init\n#if SHA2==256\n\t#define U64_C(n) (n##ull>>32)\n\t#define U64_C_LOW(n) (u64)(n##ull)\n\t#define u64 quint32_t\n\t#define ROUNDS 64\n\t#define SHA2_CONTEXT SHA256_CONTEXT\n\t#define sha2trunc_init sha224_init\n\t#define sha2_init sha256_init\n#else\n\t#define U64_C(n) n##ull\n\t#define U64_C_LOW(n) n##ull\n\t#define u64 quint64_t\n\t#define ROUNDS 80\n\t#define SHA2_CONTEXT SHA512_CONTEXT\n\t#define ROTR ROTR64\n\t#define Ch Ch64\n\t#define Maj Maj64\n\t#define Sum0 Sum0_64\n\t#define Sum1 Sum1_64\n\t#define transform transform_64\n\t#define sha2trunc_init sha384_init\n\t#define sha2_init sha512_init\n\t#define sha2_write sha512_write\n\t#define sha2_final sha512_final\n#endif\n#define BLOCKBYTES (16*sizeof(u64))\n#define byte qbyte\n\ntypedef struct\n{\n\t  u64 h0, h1, h2, h3, h4, h5, h6, h7;\n\t  u64 nblocks;\n\t  byte buf[BLOCKBYTES];\n\t  int count;\n} SHA2_CONTEXT;\n\nstatic void\nsha2_init (void *context)\n{\n  SHA2_CONTEXT *hd = context;\n\n  hd->h0 = U64_C(0x6a09e667f3bcc908);\n  hd->h1 = U64_C(0xbb67ae8584caa73b);\n  hd->h2 = U64_C(0x3c6ef372fe94f82b);\n  hd->h3 = U64_C(0xa54ff53a5f1d36f1);\n  hd->h4 = U64_C(0x510e527fade682d1);\n  hd->h5 = U64_C(0x9b05688c2b3e6c1f);\n  hd->h6 = U64_C(0x1f83d9abfb41bd6b);\n  hd->h7 = U64_C(0x5be0cd19137e2179);\n\n  hd->nblocks = 0;\n  hd->count = 0;\n}\nstatic void sha2trunc_init (void *context)\n{\n  SHA2_CONTEXT *hd = context;\n\n  //sha224 uses only the low parts.\n  hd->h0 = U64_C_LOW(0xcbbb9d5dc1059ed8);\n  hd->h1 = U64_C_LOW(0x629a292a367cd507);\n  hd->h2 = U64_C_LOW(0x9159015a3070dd17);\n  hd->h3 = U64_C_LOW(0x152fecd8f70e5939);\n  hd->h4 = U64_C_LOW(0x67332667ffc00b31);\n  hd->h5 = U64_C_LOW(0x8eb44a8768581511);\n  hd->h6 = U64_C_LOW(0xdb0c2e0d64f98fa7);\n  hd->h7 = U64_C_LOW(0x47b5481dbefa4fa4);\n\n  hd->nblocks = 0;\n  hd->count = 0;\n}\n\nfte_inlinestatic u64\nROTR (u64 x, u64 n)\n{\n  return ((x >> n) | (x << (sizeof(u64)*8 - n)));\n}\n\nfte_inlinestatic u64\nCh (u64 x, u64 y, u64 z)\n{\n  return ((x & y) ^ ( ~x & z));\n}\n\nfte_inlinestatic u64\nMaj (u64 x, u64 y, u64 z)\n{\n  return ((x & y) ^ (x & z) ^ (y & z));\n}\n\n#undef S0\n#undef S1\n#if SHA2==256\n#define S0(x) (ROTR((x),7) ^ ROTR((x),18) ^ ((x)>>3))\n#define S1(x) (ROTR((x),17) ^ ROTR((x),19) ^ ((x)>>10))\n\nfte_inlinestatic u64\nSum0 (u64 x)\n{\n  return (ROTR (x, 2) ^ ROTR (x, 13) ^ ROTR (x, 22));\n}\n\nfte_inlinestatic u64\nSum1 (u64 x)\n{\n  return (ROTR (x, 6) ^ ROTR (x, 11) ^ ROTR (x, 25));\n}\n#else\n#define S0(x) (ROTR((x),1) ^ ROTR((x),8) ^ ((x)>>7))\n#define S1(x) (ROTR((x),19) ^ ROTR((x),61) ^ ((x)>>6))\n\nfte_inlinestatic u64\nSum0 (u64 x)\n{\n  return (ROTR (x, 28) ^ ROTR (x, 34) ^ ROTR (x, 39));\n}\n\nfte_inlinestatic u64\nSum1 (u64 x)\n{\n  return (ROTR (x, 14) ^ ROTR (x, 18) ^ ROTR (x, 41));\n}\n#endif\n\n/****************\n * Transform the message W which consists of 16 64-bit-words\n */\nstatic void\ntransform (SHA2_CONTEXT *hd, const unsigned char *data)\n{\n  u64 a, b, c, d, e, f, g, h;\n  u64 w[ROUNDS];\n  int t;\n  static const u64 k[] =\n    {\n      U64_C(0x428a2f98d728ae22), U64_C(0x7137449123ef65cd),\n      U64_C(0xb5c0fbcfec4d3b2f), U64_C(0xe9b5dba58189dbbc),\n      U64_C(0x3956c25bf348b538), U64_C(0x59f111f1b605d019),\n      U64_C(0x923f82a4af194f9b), U64_C(0xab1c5ed5da6d8118),\n      U64_C(0xd807aa98a3030242), U64_C(0x12835b0145706fbe),\n      U64_C(0x243185be4ee4b28c), U64_C(0x550c7dc3d5ffb4e2),\n      U64_C(0x72be5d74f27b896f), U64_C(0x80deb1fe3b1696b1),\n      U64_C(0x9bdc06a725c71235), U64_C(0xc19bf174cf692694),\n      U64_C(0xe49b69c19ef14ad2), U64_C(0xefbe4786384f25e3),\n      U64_C(0x0fc19dc68b8cd5b5), U64_C(0x240ca1cc77ac9c65),\n      U64_C(0x2de92c6f592b0275), U64_C(0x4a7484aa6ea6e483),\n      U64_C(0x5cb0a9dcbd41fbd4), U64_C(0x76f988da831153b5),\n      U64_C(0x983e5152ee66dfab), U64_C(0xa831c66d2db43210),\n      U64_C(0xb00327c898fb213f), U64_C(0xbf597fc7beef0ee4),\n      U64_C(0xc6e00bf33da88fc2), U64_C(0xd5a79147930aa725),\n      U64_C(0x06ca6351e003826f), U64_C(0x142929670a0e6e70),\n      U64_C(0x27b70a8546d22ffc), U64_C(0x2e1b21385c26c926),\n      U64_C(0x4d2c6dfc5ac42aed), U64_C(0x53380d139d95b3df),\n      U64_C(0x650a73548baf63de), U64_C(0x766a0abb3c77b2a8),\n      U64_C(0x81c2c92e47edaee6), U64_C(0x92722c851482353b),\n      U64_C(0xa2bfe8a14cf10364), U64_C(0xa81a664bbc423001),\n      U64_C(0xc24b8b70d0f89791), U64_C(0xc76c51a30654be30),\n      U64_C(0xd192e819d6ef5218), U64_C(0xd69906245565a910),\n      U64_C(0xf40e35855771202a), U64_C(0x106aa07032bbd1b8),\n      U64_C(0x19a4c116b8d2d0c8), U64_C(0x1e376c085141ab53),\n      U64_C(0x2748774cdf8eeb99), U64_C(0x34b0bcb5e19b48a8),\n      U64_C(0x391c0cb3c5c95a63), U64_C(0x4ed8aa4ae3418acb),\n      U64_C(0x5b9cca4f7763e373), U64_C(0x682e6ff3d6b2b8a3),\n      U64_C(0x748f82ee5defb2fc), U64_C(0x78a5636f43172f60),\n      U64_C(0x84c87814a1f0ab72), U64_C(0x8cc702081a6439ec),\n      U64_C(0x90befffa23631e28), U64_C(0xa4506cebde82bde9),\n      U64_C(0xbef9a3f7b2c67915), U64_C(0xc67178f2e372532b),\n      U64_C(0xca273eceea26619c), U64_C(0xd186b8c721c0c207),\n      U64_C(0xeada7dd6cde0eb1e), U64_C(0xf57d4f7fee6ed178),\n      U64_C(0x06f067aa72176fba), U64_C(0x0a637dc5a2c898a6),\n      U64_C(0x113f9804bef90dae), U64_C(0x1b710b35131c471b),\n      U64_C(0x28db77f523047d84), U64_C(0x32caab7b40c72493),\n      U64_C(0x3c9ebe0a15c9bebc), U64_C(0x431d67c49c100d4c),\n      U64_C(0x4cc5d4becb3e42b6), U64_C(0x597f299cfc657e2a),\n      U64_C(0x5fcb6fab3ad6faec), U64_C(0x6c44198c4a475817)\n    };\n\n  /* get values from the chaining vars */\n  a = hd->h0;\n  b = hd->h1;\n  c = hd->h2;\n  d = hd->h3;\n  e = hd->h4;\n  f = hd->h5;\n  g = hd->h6;\n  h = hd->h7;\n\n#ifdef WORDS_BIGENDIAN\n  memcpy (w, data, BLOCKBYTES);\n#else\n  {\n    int i;\n    byte *p2;\n\n    for (i = 0, p2 = (byte *) w; i < 16; i++, p2 += sizeof(*w))\n      {\n#if SHA2==512\n\tp2[7] = *data++;\n\tp2[6] = *data++;\n\tp2[5] = *data++;\n\tp2[4] = *data++;\n#endif\n\tp2[3] = *data++;\n\tp2[2] = *data++;\n\tp2[1] = *data++;\n\tp2[0] = *data++;\n      }\n  }\n#endif\n\n  for (t = 16; t < ROUNDS; t++)\n    w[t] = S1 (w[t - 2]) + w[t - 7] + S0 (w[t - 15]) + w[t - 16];\n\n\n  for (t = 0; t < ROUNDS; )\n    {\n      u64 t1, t2;\n\n      /* Performance on a AMD Athlon(tm) Dual Core Processor 4050e\n         with gcc 4.3.3 using gcry_md_hash_buffer of each 10000 bytes\n         initialized to 0,1,2,3...255,0,... and 1000 iterations:\n         Not unrolled with macros:  440ms\n         Unrolled with macros:      350ms\n         Unrolled with inline:      330ms\n      */\n#if 0 /* Not unrolled.  */\n      t1 = h + Sum1 (e) + Ch (e, f, g) + k[t] + w[t];\n      t2 = Sum0 (a) + Maj (a, b, c);\n      h = g;\n      g = f;\n      f = e;\n      e = d + t1;\n      d = c;\n      c = b;\n      b = a;\n      a = t1 + t2;\n      t++;\n#else /* Unrolled to interweave the chain variables.  */\n      t1 = h + Sum1 (e) + Ch (e, f, g) + k[t] + w[t];\n      t2 = Sum0 (a) + Maj (a, b, c);\n      d += t1;\n      h  = t1 + t2;\n\n      t1 = g + Sum1 (d) + Ch (d, e, f) + k[t+1] + w[t+1];\n      t2 = Sum0 (h) + Maj (h, a, b);\n      c += t1;\n      g  = t1 + t2;\n\n      t1 = f + Sum1 (c) + Ch (c, d, e) + k[t+2] + w[t+2];\n      t2 = Sum0 (g) + Maj (g, h, a);\n      b += t1;\n      f  = t1 + t2;\n\n      t1 = e + Sum1 (b) + Ch (b, c, d) + k[t+3] + w[t+3];\n      t2 = Sum0 (f) + Maj (f, g, h);\n      a += t1;\n      e  = t1 + t2;\n\n      t1 = d + Sum1 (a) + Ch (a, b, c) + k[t+4] + w[t+4];\n      t2 = Sum0 (e) + Maj (e, f, g);\n      h += t1;\n      d  = t1 + t2;\n\n      t1 = c + Sum1 (h) + Ch (h, a, b) + k[t+5] + w[t+5];\n      t2 = Sum0 (d) + Maj (d, e, f);\n      g += t1;\n      c  = t1 + t2;\n\n      t1 = b + Sum1 (g) + Ch (g, h, a) + k[t+6] + w[t+6];\n      t2 = Sum0 (c) + Maj (c, d, e);\n      f += t1;\n      b  = t1 + t2;\n\n      t1 = a + Sum1 (f) + Ch (f, g, h) + k[t+7] + w[t+7];\n      t2 = Sum0 (b) + Maj (b, c, d);\n      e += t1;\n      a  = t1 + t2;\n\n      t += 8;\n#endif\n    }\n\n  /* Update chaining vars.  */\n  hd->h0 += a;\n  hd->h1 += b;\n  hd->h2 += c;\n  hd->h3 += d;\n  hd->h4 += e;\n  hd->h5 += f;\n  hd->h6 += g;\n  hd->h7 += h;\n}\n\n\n/* Update the message digest with the contents\n * of INBUF with length INLEN.\n */\nstatic void\nsha2_write (void *context, const void *inbuf_arg, size_t inlen)\n{\n  const unsigned char *inbuf = inbuf_arg;\n  SHA2_CONTEXT *hd = context;\n\n  if (hd->count == BLOCKBYTES)\n    {\t\t\t\t/* flush the buffer */\n      transform (hd, hd->buf);\n      hd->count = 0;\n      hd->nblocks++;\n    }\n  if (!inbuf)\n    return;\n  if (hd->count)\n    {\n      for (; inlen && hd->count < BLOCKBYTES; inlen--)\n\t    hd->buf[hd->count++] = *inbuf++;\n      sha2_write (context, NULL, 0);\n      if (!inlen)\n\t    return;\n    }\n\n  while (inlen >= BLOCKBYTES)\n    {\n      transform (hd, inbuf);\n      hd->count = 0;\n      hd->nblocks++;\n      inlen -= BLOCKBYTES;\n      inbuf += BLOCKBYTES;\n    }\n  for (; inlen && hd->count < BLOCKBYTES; inlen--)\n    hd->buf[hd->count++] = *inbuf++;\n}\n\n\n/* The routine final terminates the computation and\n * returns the digest.\n * The handle is prepared for a new cycle, but adding bytes to the\n * handle will the destroy the returned buffer.\n * Returns: 64 bytes representing the digest.  When used for sha384,\n * we take the leftmost 48 of those bytes.\n */\n\nstatic void\nsha2_final (void *context)\n{\n  SHA2_CONTEXT *hd = context;\n  u64 t, msb, lsb;\n  byte *p;\n\n  sha2_write (context, NULL, 0); /* flush */ ;\n\n  t = hd->nblocks;\n  /* multiply by 128 to make a byte count */\n  lsb = t * BLOCKBYTES;\n  msb = t >> (sizeof(u64)*8-((BLOCKBYTES==128)?7:6));\n  /* add the count */\n  t = lsb;\n  if ((lsb += hd->count) < t)\n    msb++;\n  /* multiply by 8 to make a bit count */\n  t = lsb;\n  lsb <<= 3;\n  msb <<= 3;\n  msb |= t >> (sizeof(u64)*8-3);\n\n  if (hd->count < BLOCKBYTES-sizeof(u64)*2)\n    {\t\t\t\t/* enough room */\n      hd->buf[hd->count++] = 0x80;\t/* pad */\n      while (hd->count < BLOCKBYTES-sizeof(u64)*2)\n\t    hd->buf[hd->count++] = 0;\t/* pad */\n    }\n  else\n    {\t\t\t\t/* need one extra block */\n      hd->buf[hd->count++] = 0x80;\t/* pad character */\n      while (hd->count < BLOCKBYTES)\n        hd->buf[hd->count++] = 0;\n      sha2_write (context, NULL, 0); /* flush */ ;\n      memset (hd->buf, 0, BLOCKBYTES-sizeof(u64)*2);\t/* fill next block with zeroes */\n    }\n\n#if SHA2==256\n#define X(a) do { *p++ = hd->h##a >> 24; *p++ = hd->h##a >> 16;\t      \\\n                  *p++ = hd->h##a >> 8;  *p++ = hd->h##a; } while (0)\n\n  /* append the 128 bit count */\n  hd->buf[56] = msb >> 24;\n  hd->buf[57] = msb >> 16;\n  hd->buf[58] = msb >> 8;\n  hd->buf[59] = msb;\n\n  hd->buf[60] = lsb >> 24;\n  hd->buf[61] = lsb >> 16;\n  hd->buf[62] = lsb >> 8;\n  hd->buf[63] = lsb;\n#else\n#define X(a) do { *p++ = hd->h##a >> 56; *p++ = hd->h##a >> 48;\t      \\\n                  *p++ = hd->h##a >> 40; *p++ = hd->h##a >> 32;\t      \\\n                  *p++ = hd->h##a >> 24; *p++ = hd->h##a >> 16;\t      \\\n                  *p++ = hd->h##a >> 8;  *p++ = hd->h##a; } while (0)\n\n  /* append the 128 bit count */\n  hd->buf[112] = msb >> 56;\n  hd->buf[113] = msb >> 48;\n  hd->buf[114] = msb >> 40;\n  hd->buf[115] = msb >> 32;\n  hd->buf[116] = msb >> 24;\n  hd->buf[117] = msb >> 16;\n  hd->buf[118] = msb >> 8;\n  hd->buf[119] = msb;\n\n  hd->buf[120] = lsb >> 56;\n  hd->buf[121] = lsb >> 48;\n  hd->buf[122] = lsb >> 40;\n  hd->buf[123] = lsb >> 32;\n  hd->buf[124] = lsb >> 24;\n  hd->buf[125] = lsb >> 16;\n  hd->buf[126] = lsb >> 8;\n  hd->buf[127] = lsb;\n#endif\n  transform (hd, hd->buf);\n\n  p = hd->buf;\n#ifdef WORDS_BIGENDIAN\n#undef X\n#define X(a) do { *(u64*)p = hd->h##a ; p += sizeof(u64); } while (0)\n#endif\n  X (0);\n  X (1);\n  X (2);\n  X (3);\n  X (4);\n  X (5);\n  /* Note that these last two chunks are included even for SHA384.\n     We just ignore them. */\n  X (6);\n  X (7);\n#undef X\n}\n\n#if SHA2==256\nstatic void sha224_finish (qbyte *digest, void *context)\n{\n\tSHA2_CONTEXT *hd = (SHA2_CONTEXT *) context;\n\tsha2_final(context);\n\tmemcpy(digest, hd->buf, 224/8);\t//only the first 224 bits of the result...\n}\nstatic void sha256_finish (qbyte *digest, void *context)\n{\n\tSHA2_CONTEXT *hd = (SHA2_CONTEXT *) context;\n\tsha2_final(context);\n\tmemcpy(digest, hd->buf, 256/8);\n}\n\nhashfunc_t hash_sha2_224 =\n{\n\t224/8,\n\tsizeof(SHA2_CONTEXT),\n\tsha224_init,\n\tsha2_write,\n\tsha224_finish\n};\nhashfunc_t hash_sha2_256 =\n{\n\t256/8,\n\tsizeof(SHA2_CONTEXT),\n\tsha256_init,\n\tsha2_write,\n\tsha256_finish\n};\n\n/*#if defined(HAVE_SERVER) && !defined(HAVE_CLIENT)\n__attribute__((constructor)) void sha2_256_unit_test(void)\n{\n\tqbyte digest[256/8];\n\tqbyte need[sizeof(digest)];\n\n\tCalcHash(&hash_sha2_256, digest, sizeof(digest), (qbyte*)(volatile qbyte*)\"\", 0);\n\tBase16_DecodeBlock(\"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855\", need, sizeof(need));\n\tif (memcmp(digest, need, sizeof(digest)))\n\t\tprintf(\"%s %i fail\\n\", __func__, __LINE__), abort();\n\n\tCalcHash(&hash_sha2_256, digest, sizeof(digest), (qbyte*)(volatile qbyte*)\"abc\", 3);\n\tBase16_DecodeBlock(\"ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad\", need, sizeof(need));\n\tif (memcmp(digest, need, sizeof(digest)))\n\t\tprintf(\"%s %i fail\\n\", __func__, __LINE__), abort();\n}\n#endif*/\n#endif\n#if SHA2==512\nstatic void sha384_finish (qbyte *digest, void *context)\n{\n\tSHA2_CONTEXT *hd = (SHA2_CONTEXT *) context;\n\tsha2_final(context);\n\tmemcpy(digest, hd->buf, 384/8);\n}\n\nstatic void sha512_finish (qbyte *digest, void *context)\n{\n\tSHA2_CONTEXT *hd = (SHA2_CONTEXT *) context;\n\tsha2_final(context);\n\tmemcpy(digest, hd->buf, 512/8);\n}\n\nhashfunc_t hash_sha2_384 =\n{\n\t384/8,\n\tsizeof(SHA2_CONTEXT),\n\tsha384_init,\n\tsha2_write,\n\tsha384_finish\n};\nhashfunc_t hash_sha2_512 =\n{\n\t512/8,\n\tsizeof(SHA2_CONTEXT),\n\tsha512_init,\n\tsha2_write,\n\tsha512_finish\n};\n#endif\n"
  },
  {
    "path": "engine/common/sys.h",
    "content": "/*\nCopyright (C) 1996-1997 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  \n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n*/\n// sys.h -- non-portable functions\n\n//\n// file IO\n// for the most part, we use stdio.\n// if your system doesn't have stdio then urm... well.\n//\nvoid Sys_mkdir (const char *path);\t//not all pre-unix systems have directories (including dos 1)\nqboolean Sys_rmdir (const char *path);\nqboolean Sys_remove (const char *path);\nqboolean Sys_Rename (const char *oldfname, const char *newfname);\nqboolean Sys_GetFreeDiskSpace(const char *path, quint64_t *freespace);\t//false for not-implemented or other error. path will be a system path, but may be relative (if basedir isn't properly known). path MAY be a file, or may be a slash-terminated directory.\n\n//\n// memory protection\n//\nvoid Sys_MakeCodeWriteable (void * startaddr, unsigned long length);\n\n//\n// system IO\n//\nint VARGS Sys_DebugLog(char *file, char *fmt, ...) LIKEPRINTF(2);\n\nNORETURN void VARGS Sys_Error (const char *error, ...) LIKEPRINTF(1);\n// an error will cause the entire program to exit\n\nvoid VARGS Sys_Printf (char *fmt, ...) LIKEPRINTF(1);\n// send text to the console\nvoid Sys_Warn (char *fmt, ...) LIKEPRINTF(1);\n//like Sys_Printf. dunno why there needs to be two of em.\n\nchar *Sys_URIScheme_NeedsRegistering(void);\t//returns the name of one of the current manifests uri schemes that isn't registered (but should be registerable).\nvoid Sys_Quit (void);\nvoid Sys_RecentServer(char *command, char *target, char *title, char *desc);\nqboolean Sys_RunInstaller(void);\n\ntypedef struct dllfunction_s {\n\tvoid **funcptr;\n\tchar *name;\n} dllfunction_t;\n#define dllhandle_t void\nextern qboolean sys_nounload;\t//blocks Sys_CloseLibrary. set before stack trace fatal shutdowns.\ndllhandle_t *Sys_LoadLibrary(const char *name, dllfunction_t *funcs);\nvoid Sys_CloseLibrary(dllhandle_t *lib);\nvoid *Sys_GetAddressForName(dllhandle_t *module, const char *exportname);\nchar *Sys_GetNameForAddress(dllhandle_t *module, void *address);\n\nqboolean LibZ_Init(void);\nqboolean LibJPEG_Init(void);\nqboolean LibPNG_Init(void);\n\nqboolean Sys_RunFile(const char *fname, int nlen);\n\nunsigned int Sys_Milliseconds (void);\ndouble Sys_DoubleTime (void);\nqboolean Sys_RandomBytes(qbyte *string, int len);\n\nqboolean Sys_ResolveFileURL(const char *inurl, int inlen, char *out, int outlen);\n\nchar *Sys_ConsoleInput (void);\n\ntypedef enum\n{\n\tCBT_CLIPBOARD,\t//ctrl+c, ctrl+v\n\tCBT_SELECTION,\t//select-to-copy, middle-to-paste\n} clipboardtype_t;\nvoid Sys_Clipboard_PasteText(clipboardtype_t clipboardtype, void (*callback)(void *ctx, const char *utf8), void *ctx);\t//calls the callback once the text is available (maybe instantly). utf8 arg may be NULL if the clipboard was unavailable.\nvoid Sys_SaveClipboard(clipboardtype_t clipboardtype, const char *text); //a stub would do nothing.\n\n//stuff for dynamic dedicated console -> gfx and back.\nvoid Sys_CloseTerminal (void);\nqboolean Sys_InitTerminal (void);\nvoid Con_PrintToSys(void);\n\nvoid Sys_ServerActivity(void);\n//make window flash on the taskbar - someone said something/connected\n\nvoid Sys_SendKeyEvents (void);\n// Perform Key_Event () callbacks until the input que is empty\n\nint Sys_EnumerateFiles (const char *gpath, const char *match, int (QDECL *func)(const char *fname, qofs_t fsize, time_t modtime, void *parm, searchpathfuncs_t *spath), void *parm, searchpathfuncs_t *spath);\n\nvoid Sys_Vibrate(float count);\nstruct searchpathfuncs_s *Sys_OpenTitleStore(void); //sdl3\n\nqboolean Sys_GetDesktopParameters(int *width, int *height, int *bpp, int *refreshrate);\n\n#if defined(__GNUC__)\n\t#define qatomic32_t qint32_t\n\t#define FTE_Atomic32_Inc(ptr) __sync_add_and_fetch(ptr, 1)\t//returns the AFTER the operation.\n\t#define FTE_Atomic32_Dec(ptr) __sync_add_and_fetch(ptr, -1)\t//returns the AFTER the operation.\n\t#define FTE_Atomic_Insert(head, newnode, newnodenext) do newnodenext = head; while(!__sync_bool_compare_and_swap(&head, newnodenext, newnode)) //atomically insert into a linked list, being sure to not corrupt the pointers\n#elif defined(_WIN32)\n\t#define qatomic32_t long\n\t#define FTE_Atomic32_Inc(ptr) _InterlockedIncrement(ptr)\n\t#define FTE_Atomic32_Dec(ptr) _InterlockedDecrement(ptr)\n\t#define FTE_Atomic_Insert(head, newnode, newnodenext) do newnodenext = head; while(newnodenext != _InterlockedCompareExchangePointer(&head, newnode, newnodenext))\n#else\n\t#define qatomic32_t qint32_t\n\t#define FTE_Atomic32_Inc(ptr) FTE_Atomic32Mutex_Add(ptr, 1)\n\t#define FTE_Atomic32_Dec(ptr) FTE_Atomic32Mutex_Add(ptr, -1)\n\t#define FTE_Atomic_Insert(head, newnode, newnodenext) do newnodenext = head; while(!FTE_AtomicPtr_ConditionalReplace(&head, newnodenext, newnode))\n#endif\n\n\ntypedef enum wgroup_e\n{\n\tWG_MAIN\t\t= 0,\n\tWG_LOADER\t= 1,\n\tWG_COUNT\t= 2 //main and loaders\n} wgroup_t;\ntypedef struct\n{\n\tvoid *(QDECL *CreateMutex)(void);\n\tqboolean (QDECL *LockMutex)(void *mutex);\n\tqboolean (QDECL *UnlockMutex)(void *mutex);\n\tvoid (QDECL *DestroyMutex)(void *mutex);\n\n\tvoid (*AddWork)(wgroup_t thread, void(*func)(void *ctx, void *data, size_t a, size_t b), void *ctx, void *data, size_t a, size_t b);\t//low priority\n\tvoid (*WaitForCompletion)(void *priorityctx, int *address, int sleepwhilevalue);\n#define plugthreadfuncs_name \"Threading\"\n} plugthreadfuncs_t;\n\n#ifdef MULTITHREAD\n#if defined(_WIN32) && defined(_DEBUG)\nvoid Sys_SetThreadName(unsigned int dwThreadID, char *threadName);\n#endif\n\nvoid Sys_ThreadsInit(void);\n//qboolean Sys_IsThread(void *thread);\nqboolean Sys_IsMainThread(void);\nqboolean Sys_IsThread(void *thread);\nvoid *Sys_CreateThread(char *name, int (*func)(void *), void *args, int priority, int stacksize);\nvoid Sys_WaitOnThread(void *thread);\nvoid Sys_DetachThread(void *thread);\nvoid Sys_ThreadAbort(void);\n\n#define THREADP_IDLE -5\n#define THREADP_NORMAL 0\n#define THREADP_HIGHEST 5\n\nvoid *QDECL Sys_CreateMutex(void);\nqboolean Sys_TryLockMutex(void *mutex);\nqboolean QDECL Sys_LockMutex(void *mutex);\nqboolean QDECL Sys_UnlockMutex(void *mutex);\nvoid QDECL Sys_DestroyMutex(void *mutex);\n\n/* Conditional wait calls */\nvoid *Sys_CreateConditional(void);\nqboolean Sys_LockConditional(void *condv);\nqboolean Sys_UnlockConditional(void *condv);\nqboolean Sys_ConditionWait(void *condv);\t\t//lock first\nqboolean Sys_ConditionSignal(void *condv);\t\t//lock first\nqboolean Sys_ConditionBroadcast(void *condv);\t//lock first\nvoid Sys_DestroyConditional(void *condv);\n\n//to try to catch leaks more easily.\n#ifdef USE_MSVCRT_DEBUG\nvoid *Sys_CreateMutexNamed(char *file, int line);\n#define Sys_CreateMutex() Sys_CreateMutexNamed(__FILE__, __LINE__)\n#endif\n\n#else\n\t#ifdef __GNUC__\t//gcc complains about if (true) when these are maros. msvc complains about static not being called in headers. gah.\n\t\tstatic inline qboolean Sys_MutexStub(void) {return qtrue;}\n\t\tstatic inline void *Sys_CreateMutex(void) {return NULL;}\n\t\t#define Sys_IsMainThread() Sys_MutexStub()\n\t\t#define Sys_DestroyMutex(m) Sys_MutexStub()\n\t\t#define Sys_IsMainThread() Sys_MutexStub()\n\t\t#define Sys_LockMutex(m) Sys_MutexStub()\n\t\t#define Sys_UnlockMutex(m) Sys_MutexStub()\n\t\t#ifndef __cplusplus\n\t\t\tstatic inline qboolean Sys_IsThread(void *thread) {return (!thread)?qtrue:qfalse;}\n\t\t#endif\n\t#else\n\t\t#define Sys_IsMainThread() (qboolean)(qtrue)\n\t\t#define Sys_CreateMutex() (void*)(NULL)\n\t\t#define Sys_LockMutex(m) (qboolean)(qtrue)\n\t\t#define Sys_UnlockMutex(m) (qboolean)(qtrue)\n\t\t#define Sys_DestroyMutex(m) (void)0\n\t\t#define Sys_IsThread(t) (!t)\n\t#endif\n#endif\n\nvoid Sys_Sleep(double seconds);\n\n\n#define UPD_OFF 0\n#define UPD_STABLE 1\n#define UPD_TESTING 2\n\n#if defined(WEBCLIENT) && defined(PACKAGEMANAGER)\n\t#if defined(_WIN32) && !defined(SERVERONLY) && !defined(_XBOX)\n\t\t#define HAVEAUTOUPDATE\n\t#endif\n\t#if defined(__linux__) && !defined(ANDROID)\n\t\t#define HAVEAUTOUPDATE\n\t#endif\n#endif\n\n#ifdef HAVEAUTOUPDATE\nqboolean Sys_SetUpdatedBinary(const char *fname);\t//attempts to overwrite the working binary.\nqboolean Sys_EngineMayUpdate(void);\t\t\t\t\t//says whether the system code is able/allowed to overwrite itself.\n#else\n#define Sys_EngineMayUpdate() false\n#define Sys_SetUpdatedBinary(n) false\n#endif\n\nvoid Sys_Init (void);\nvoid Sys_Shutdown(void);\n\n"
  },
  {
    "path": "engine/common/sys_linux_threads.c",
    "content": "/*\r\nCopyright (C) 1996-1997 Id Software, Inc.\r\n\r\nThis program is free software; you can redistribute it and/or\r\nmodify it under the terms of the GNU General Public License\r\nas published by the Free Software Foundation; either version 2\r\nof the License, or (at your option) any later version.\r\n\r\nThis program is distributed in the hope that it will be useful,\r\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\r\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\r\n\r\nSee the GNU General Public License for more details.\r\n\r\nYou should have received a copy of the GNU General Public License\r\nalong with this program; if not, write to the Free Software\r\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\r\n\r\n*/\r\n\r\n//well, linux or cygwin (windows with posix emulation layer), anyway...\r\n\r\n#define _GNU_SOURCE\r\n#include \"quakedef.h\"\r\n\t\r\n#ifdef MULTITHREAD\r\n#include <limits.h>\r\n#include <pthread.h>\r\n/* Thread creation calls */\r\ntypedef void *(*pfunction_t)(void *);\r\n\r\nstatic pthread_t mainthread;\r\n\r\nvoid Sys_ThreadsInit(void)\r\n{\r\n\tmainthread = pthread_self();\r\n}\r\nqboolean Sys_IsThread(void *thread)\r\n{\r\n\treturn pthread_equal(pthread_self(), *(pthread_t*)thread);\r\n}\r\nqboolean Sys_IsMainThread(void)\r\n{\r\n\treturn Sys_IsThread(&mainthread);\r\n}\r\nvoid Sys_ThreadAbort(void)\r\n{\r\n\tpthread_exit(NULL);\r\n}\r\n\r\n#if 1\r\ntypedef struct {\r\n\tint (*func)(void *);\r\n\tvoid *args;\r\n} qthread_t;\r\nstatic void *Sys_CreatedThread(void *v)\r\n{\r\n\tqthread_t *qthread = v;\r\n\tqintptr_t r;\r\n\r\n\tr = qthread->func(qthread->args);\r\n\r\n\treturn (void*)r;\r\n}\r\n\r\nvoid *Sys_CreateThread(char *name, int (*func)(void *), void *args, int priority, int stacksize)\r\n{\r\n\tpthread_t *thread;\r\n\tqthread_t *qthread;\r\n\tpthread_attr_t attr;\r\n\r\n\tthread = (pthread_t *)malloc(sizeof(pthread_t)+sizeof(qthread_t));\r\n\tif (!thread)\r\n\t\treturn NULL;\r\n\r\n\tqthread = (qthread_t*)(thread+1);\r\n\tqthread->func = func;\r\n\tqthread->args = args;\r\n\r\n\tpthread_attr_init(&attr);\r\n\tpthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);\r\n\tif (stacksize != -1)\r\n\t{\r\n\t\tif (stacksize < PTHREAD_STACK_MIN*2)\r\n\t\t\tstacksize = PTHREAD_STACK_MIN*2;\r\n\t\tif (stacksize < PTHREAD_STACK_MIN+65536*16)\r\n\t\t\tstacksize = PTHREAD_STACK_MIN+65536*16;\r\n\t\tpthread_attr_setstacksize(&attr, stacksize);\r\n\t}\r\n\tif (pthread_create(thread, &attr, (pfunction_t)Sys_CreatedThread, qthread))\r\n\t{\r\n\t\tfree(thread);\r\n\t\tthread = NULL;\r\n\t}\r\n\tpthread_attr_destroy(&attr);\r\n#if defined(_DEBUG) && defined(__USE_GNU) && defined(__GLIBC_PREREQ)\r\n#if __GLIBC_PREREQ(2,12)\r\n\tpthread_setname_np(*thread, name);\r\n#endif\r\n#endif\r\n\r\n\treturn (void *)thread;\r\n}\r\n#else\r\nvoid *Sys_CreateThread(char *name, int (*func)(void *), void *args, int priority, int stacksize)\r\n{\r\n\tpthread_t *thread;\r\n\tpthread_attr_t attr;\r\n\r\n\tthread = (pthread_t *)malloc(sizeof(pthread_t));\r\n\tif (!thread)\r\n\t\treturn NULL;\r\n\r\n\tpthread_attr_init(&attr);\r\n\tpthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);\r\n\tif (stacksize < PTHREAD_STACK_MIN*2)\r\n\t\tstacksize = PTHREAD_STACK_MIN*2;\r\n\tpthread_attr_setstacksize(&attr, stacksize);\r\n\tif (pthread_create(thread, &attr, (pfunction_t)func, args))\r\n\t{\r\n\t\tfree(thread);\r\n\t\tthread = NULL;\r\n\t}\r\n\tpthread_attr_destroy(&attr);\r\n\r\n#if defined(_DEBUG) && defined(__USE_GNU) && defined(__GLIBC_PREREQ)\r\n#if __GLIBC_PREREQ(2,12)\r\n\tpthread_setname_np(*thread, name);\r\n#endif\r\n#endif\r\n\r\n\treturn (void *)thread;\r\n}\r\n#endif\r\n\r\nvoid Sys_WaitOnThread(void *thread)\r\n{\r\n\tint err;\r\n\terr = pthread_join(*(pthread_t *)thread, NULL);\r\n\tif (err)\r\n\t\tprintf(\"pthread_join(%p) failed, error %s\\n\", thread, strerror(err));\r\n\t\t\r\n\tfree(thread);\r\n}\r\n\r\n/* Mutex calls */\r\nvoid *Sys_CreateMutex(void)\r\n{\r\n\tpthread_mutex_t *mutex = (pthread_mutex_t *)malloc(sizeof(pthread_mutex_t));\r\n\r\n\tif (mutex && !pthread_mutex_init(mutex, NULL))\r\n\t\treturn mutex;\r\n\treturn NULL;\r\n}\r\n\r\nqboolean Sys_TryLockMutex(void *mutex)\r\n{\r\n\treturn !pthread_mutex_trylock(mutex);\r\n}\r\n\r\nqboolean Sys_LockMutex(void *mutex)\r\n{\r\n\treturn !pthread_mutex_lock(mutex);\r\n}\r\n\r\nqboolean Sys_UnlockMutex(void *mutex)\r\n{\r\n\treturn !pthread_mutex_unlock(mutex);\r\n}\r\n\r\nvoid Sys_DestroyMutex(void *mutex)\r\n{\r\n\tpthread_mutex_destroy(mutex);\r\n\tfree(mutex);\r\n}\r\n\r\n/* Conditional wait calls */\r\ntypedef struct condvar_s\r\n{\r\n\tpthread_mutex_t *mutex;\r\n\tpthread_cond_t *cond;\r\n} condvar_t;\r\n\r\nvoid *Sys_CreateConditional(void)\r\n{\r\n\tcondvar_t *condv;\r\n\tpthread_mutex_t *mutex;\r\n\tpthread_cond_t *cond;\r\n\r\n\tcondv = (condvar_t *)malloc(sizeof(condvar_t));\r\n\tif (!condv)\r\n\t\treturn NULL;\r\n\r\n\tmutex = (pthread_mutex_t *)malloc(sizeof(pthread_mutex_t));\r\n\tif (!mutex)\r\n\t\treturn NULL;\r\n\r\n\tcond = (pthread_cond_t *)malloc(sizeof(pthread_cond_t));\r\n\tif (!cond)\r\n\t\treturn NULL;\r\n\r\n\tif (!pthread_mutex_init(mutex, NULL))\r\n\t{\r\n\t\tif (!pthread_cond_init(cond, NULL))\r\n\t\t{\r\n\t\t\tcondv->cond = cond;\r\n\t\t\tcondv->mutex = mutex;\r\n\r\n\t\t\treturn (void *)condv;\r\n\t\t}\r\n\t\telse\r\n\t\t\tpthread_mutex_destroy(mutex);\r\n\t}\r\n\r\n\tfree(cond);\r\n\tfree(mutex);\r\n\tfree(condv);\r\n\treturn NULL;\r\n}\r\n\r\nqboolean Sys_LockConditional(void *condv)\r\n{\r\n\treturn !pthread_mutex_lock(((condvar_t *)condv)->mutex);\r\n}\r\n\r\nqboolean Sys_UnlockConditional(void *condv)\r\n{\r\n\treturn !pthread_mutex_unlock(((condvar_t *)condv)->mutex);\r\n}\r\n\r\nqboolean Sys_ConditionWait(void *condv)\r\n{\r\n\treturn !pthread_cond_wait(((condvar_t *)condv)->cond, ((condvar_t *)condv)->mutex);\r\n}\r\n\r\nqboolean Sys_ConditionSignal(void *condv)\r\n{\r\n\treturn !pthread_cond_signal(((condvar_t *)condv)->cond);\r\n}\r\n\r\nqboolean Sys_ConditionBroadcast(void *condv)\r\n{\r\n\treturn !pthread_cond_broadcast(((condvar_t *)condv)->cond);\r\n}\r\n\r\nvoid Sys_DestroyConditional(void *condv)\r\n{\r\n\tcondvar_t *cv = (condvar_t *)condv;\r\n\r\n\tpthread_cond_destroy(cv->cond);\r\n\tpthread_mutex_destroy(cv->mutex);\r\n\tfree(cv->cond);\r\n\tfree(cv->mutex);\r\n\tfree(cv);\r\n}\r\n#endif\r\n\r\nvoid Sys_Sleep (double seconds)\r\n{\r\n\tstruct timespec ts;\r\n\r\n\tts.tv_sec = (time_t)seconds;\r\n\tseconds -= ts.tv_sec;\r\n\tts.tv_nsec = seconds * 1000000000.0;\r\n\r\n\tnanosleep(&ts, NULL);\r\n}\r\n\r\n\r\n\r\n\r\n#ifdef SUBSERVERS\r\n#include <spawn.h>\r\n#include <unistd.h>\r\n#include <fcntl.h>\r\n#include <errno.h>\r\n#include <sys/wait.h>\r\n\r\ntypedef struct\r\n{\r\n\tvfsfile_t pub;\r\n\r\n\tint inpipe;\r\n\tint outpipe;\r\n\tpid_t pid;\t//so we don't end up with zombie processes\r\n} linsubserver_t;\r\n\r\nstatic int QDECL Sys_MSV_ReadBytes (struct vfsfile_s *file, void *buffer, int bytestoread)\r\n{\r\n\tlinsubserver_t *s = (linsubserver_t*)file;\r\n\tssize_t avail = read(s->inpipe, buffer, bytestoread);\r\n\tif (!avail)\r\n\t\treturn -1;\t//EOF\r\n\tif (avail < 0)\r\n\t{\r\n\t\tint e = errno;\r\n\t\tif (e == EAGAIN || e == EWOULDBLOCK || e == EINTR)\r\n\t\t\treturn 0;\t//no data available\r\n\t\telse\r\n\t\t{\r\n\t\t\tperror(\"subserver read\");\r\n\t\t\treturn -1;\t//some sort of error\r\n\t\t}\r\n\t}\r\n\treturn avail;\r\n}\r\nstatic int QDECL Sys_MSV_WriteBytes (struct vfsfile_s *file, const void *buffer, int bytestowrite)\r\n{\r\n\tlinsubserver_t *s = (linsubserver_t*)file;\r\n\tssize_t wrote = write(s->outpipe, buffer, bytestowrite);\r\n\tif (!wrote)\r\n\t\treturn -1;\t//EOF\r\n\tif (wrote < 0)\r\n\t{\r\n\t\tint e = errno;\r\n\t\tif (e == EAGAIN || e == EWOULDBLOCK || e == EINTR)\r\n\t\t\treturn 0;\t//no space available\r\n\t\telse\r\n\t\t{\r\n\t\t\tperror(\"subserver write\");\r\n\t\t\treturn -1;\t//some sort of error\r\n\t\t}\r\n\t}\r\n\treturn wrote;\r\n}\r\nstatic qboolean QDECL Sys_MSV_Close (struct vfsfile_s *file)\r\n{\r\n\tlinsubserver_t *s = (linsubserver_t*)file;\r\n\r\n\tclose(s->inpipe);\r\n\tclose(s->outpipe);\r\n\ts->inpipe = -1;\r\n\ts->outpipe = -1;\r\n\twaitpid(s->pid, NULL, 0);\r\n\tZ_Free(s);\r\n\treturn true;\r\n}\r\n\r\n#ifdef SQL\r\n#include \"sv_sql.h\"\r\n#endif\r\n#include \"netinc.h\"\r\n\r\nvfsfile_t *Sys_ForkServer(void)\r\n{\r\n#ifdef SERVERONLY\r\n\textern  jmp_buf \tsys_sv_serverforked;\r\n\t\t\r\n\tint toslave[2];\r\n\tint tomaster[2];\r\n\tlinsubserver_t *ctx;\r\n\tpid_t pid;\r\n\r\n\t//make sure we're fully synced, so that workers can't mess up\r\n\tCvar_Set(Cvar_FindVar(\"worker_count\"), \"0\");\r\n\tCOM_WorkerFullSync();\r\n#ifdef WEBCLIENT\r\n\tDL_DeThread();\r\n#endif\r\n#ifdef SQL\r\n\tSQL_KillServers(NULL);\t//FIXME: this is bad...\r\n#endif\r\n\t//FIXME: we should probably use posix_atfork for those.\r\n\r\n\tpipe(toslave);\r\n\tpipe(tomaster);\r\n\r\n\t//make the reads non-blocking.\r\n\tfcntl(toslave[1], F_SETFL, fcntl(toslave[1], F_GETFL, 0)|O_NONBLOCK);\r\n\tfcntl(tomaster[0], F_SETFL, fcntl(tomaster[0], F_GETFL, 0)|O_NONBLOCK);\r\n\r\n\tpid = fork();\r\n\r\n\tif (!pid)\r\n\t{\t//this is the child\r\n\t\tdup2(toslave[0], STDIN_FILENO);\r\n\t\tclose(toslave[1]);\r\n\t\tclose(toslave[0]);\r\n\t\tdup2(tomaster[1], STDOUT_FILENO);\r\n\r\n\t\tSSV_SetupControlPipe(Sys_GetStdInOutStream(), false);\r\n\r\n\t\tFS_UnloadPackFiles();\t//these handles got wiped. make sure they're all properly wiped before loading new handles.\r\n\t\tNET_Shutdown();\t\t\t//make sure we close any of the parent's network fds ...\r\n\t\tFS_ReloadPackFiles();\r\n\r\n\t\t//jump out into the main work loop\r\n\t\tlongjmp(sys_sv_serverforked, 1);\r\n\t\texit(0);\t//err...\r\n\t}\r\n\telse\r\n\t{\t//this is the parent\r\n\t\tclose(toslave[0]);\r\n\t\tclose(tomaster[1]);\r\n\t\tif (pid == -1)\r\n\t\t{\t//fork failed. make sure everything is destroyed.\r\n\t\t\tclose(toslave[1]);\r\n\t\t\tclose(tomaster[0]);\r\n\t\t\treturn NULL;\r\n\t\t}\r\n\r\n\t\tCon_DPrintf(\"Forked new server node\\n\");\r\n\t\tctx = Z_Malloc(sizeof(*ctx));\r\n\t}\r\n\r\n\t\r\n\r\n#else\r\n\tint toslave[2];\r\n\tint tomaster[2];\r\n\tchar exename[MAX_OSPATH];\r\n\tposix_spawn_file_actions_t action;\r\n\tlinsubserver_t *ctx;\r\n\tchar *argv[64];\r\n\tint argc = 0;\r\n\tint l;\r\n\r\n\targv[argc++] = exename;\r\n\targv[argc++] = \"-clusterslave\";\r\n\targc += FS_GetManifestArgv((const char**)argv+argc, countof(argv)-argc-1);\r\n\targv[argc++] = NULL;\r\n\r\n#if 0\r\n\tstrcpy(exename, \"/bin/ls\");\r\n\targs[1] = NULL;\r\n#elif 0\r\n\tstrcpy(exename, \"/tmp/ftedbg/fteqw.sv\");\r\n#else\r\n\tl = readlink(\"/proc/self/exe\", exename, sizeof(exename)-1);\r\n\tif (l <= 0)\r\n\t\treturn NULL;\r\n\texename[l] = 0;\r\n#endif\r\n\tCon_DPrintf(\"Execing %s\\n\", exename);\r\n\r\n\tctx = Z_Malloc(sizeof(*ctx));\r\n\r\n\tpipe(toslave);\r\n\tpipe(tomaster);\r\n\r\n\t//make the reads non-blocking.\r\n\tfcntl(toslave[1], F_SETFL, fcntl(toslave[1], F_GETFL, 0)|O_NONBLOCK);\r\n\tfcntl(tomaster[0], F_SETFL, fcntl(tomaster[0], F_GETFL, 0)|O_NONBLOCK);\r\n\r\n\tposix_spawn_file_actions_init(&action);\r\n\tposix_spawn_file_actions_addclose(&action, toslave[1]);\r\n\tposix_spawn_file_actions_addclose(&action, tomaster[0]);\r\n\r\n\tposix_spawn_file_actions_adddup2(&action, toslave[0],\tSTDIN_FILENO);\r\n\tposix_spawn_file_actions_adddup2(&action, tomaster[1],\tSTDOUT_FILENO);\r\n//\tposix_spawn_file_actions_adddup2(&action, tomaster[1],\tSTDERR_FILENO);\r\n\r\n\tposix_spawn_file_actions_addclose(&action, toslave[0]);\r\n\tposix_spawn_file_actions_addclose(&action, tomaster[1]);\r\n\r\n\tposix_spawn(&ctx->pid, exename, &action, NULL, argv, NULL);\r\n#endif\r\n\r\n\tctx->inpipe = tomaster[0];\r\n\tclose(tomaster[1]);\r\n\tclose(toslave[0]);\r\n\tctx->outpipe = toslave[1];\r\n\r\n\tctx->pub.ReadBytes = Sys_MSV_ReadBytes;\r\n\tctx->pub.WriteBytes = Sys_MSV_WriteBytes;\r\n\tctx->pub.Close = Sys_MSV_Close;\r\n\treturn &ctx->pub;\r\n}\r\n\r\n\r\nstatic int QDECL Sys_StdoutWrite (struct vfsfile_s *file, const void *buffer, int bytestowrite)\r\n{\r\n\tssize_t r = write(STDOUT_FILENO, buffer, bytestowrite);\r\n\tif (r == 0 && bytestowrite)\r\n\t\treturn -1;\t//eof\r\n\tif (r < 0)\r\n\t{\r\n\t\tint e = errno;\r\n\t\tif (e == EINTR || e == EAGAIN || e == EWOULDBLOCK)\r\n\t\t\treturn 0;\r\n\t}\r\n\treturn r;\r\n}\r\nstatic int QDECL Sys_StdinRead (struct vfsfile_s *file, void *buffer, int bytestoread)\r\n{\r\n\tssize_t r;\r\n#if defined(__linux__) && defined(_DEBUG)\r\n\tint fl = fcntl (STDIN_FILENO, F_GETFL, 0);\r\n\tif (!(fl & O_NONBLOCK))\r\n\t{\r\n\t\tfcntl(STDIN_FILENO, F_SETFL, fl | O_NONBLOCK);\r\n\t\tSys_Printf(CON_WARNING \"stdin flags became blocking - gdb bug?\\n\");\r\n\t}\r\n#endif\r\n\r\n\tr = read(STDIN_FILENO, buffer, bytestoread);\r\n\tif (r == 0 && bytestoread)\r\n\t\treturn -1;\t//eof\r\n\tif (r < 0)\r\n\t{\r\n\t\tint e = errno;\r\n\t\tif (e == EINTR || e == EAGAIN || e == EWOULDBLOCK)\r\n\t\t\treturn 0;\r\n\t}\r\n\treturn r;\r\n}\r\nqboolean QDECL Sys_StdinOutClose(vfsfile_t *fs)\r\n{\r\n\tSys_Error(\"Shutdown\\n\");\r\n}\r\nvfsfile_t *Sys_GetStdInOutStream(void)\r\n{\r\n\tvfsfile_t *stream = Z_Malloc(sizeof(*stream));\r\n\r\n\t//make sure nothing bad is going to happen.\r\n\tfcntl(STDIN_FILENO, F_SETFL, fcntl(STDIN_FILENO, F_GETFL, 0)|O_NONBLOCK);\r\n\tfcntl(STDOUT_FILENO, F_SETFL, fcntl(STDOUT_FILENO, F_GETFL, 0)|O_NONBLOCK);\r\n\r\n\tstream->WriteBytes = Sys_StdoutWrite;\r\n\tstream->ReadBytes = Sys_StdinRead;\r\n\tstream->Close = Sys_StdinOutClose;\r\n\treturn stream;\r\n}\r\n#endif\r\n\r\n\r\n\r\n\r\n\r\n#ifdef HAVEAUTOUPDATE\r\n#include <sys/stat.h>\r\n#include <unistd.h>\r\nqboolean Sys_SetUpdatedBinary(const char *newbinary)\r\n{\r\n\tchar enginebinary[MAX_OSPATH];\r\n\tchar tmpbinary[MAX_OSPATH];\r\n//\tchar enginebinarybackup[MAX_OSPATH+4];\r\n//\tsize_t len;\r\n\tint i;\r\n\tstruct stat src, dst;\r\n\r\n\t//update blocked via commandline. just in case.\r\n\tif (COM_CheckParm(\"-noupdate\") || COM_CheckParm(\"--noupdate\") || COM_CheckParm(\"-noautoupdate\") || COM_CheckParm(\"--noautoupdate\"))\r\n\t\treturn false;\r\n\r\n\t//get the binary name\r\n\ti = readlink(\"/proc/self/exe\", enginebinary, sizeof(enginebinary)-1);\r\n\tif (i <= 0)\r\n\t\treturn false;\r\n\tenginebinary[i] = 0;\r\n\r\n\t//generate the temp name\r\n\t/*memcpy(enginebinarybackup, enginebinary, sizeof(enginebinary));\r\n\tlen = strlen(enginebinarybackup);\r\n\tif (len > 4 && !strcasecmp(enginebinarybackup+len-4, \".bin\"))\r\n\t\tlen -= 4;\t//swap its extension over, if we can.\r\n\tstrcpy(enginebinarybackup+len, \".bak\");*/\r\n\r\n\t//copy over file permissions (don't ignore the user)\r\n\tif (stat(enginebinary, &dst)<0)\r\n\t\tdst.st_mode = 0777;\r\n\tif (stat(newbinary, &src)<0)\r\n\t\tsrc.st_mode = 0777;\r\n\r\n//\tif (src.st_dev != dst.st_dev)\r\n\t{\t//oops, its on a different filesystem. create a copy\r\n\t\tQ_snprintfz(tmpbinary, sizeof(tmpbinary), \"%s.new\", enginebinary);\r\n\t\tif (!FS_Copy(newbinary, tmpbinary, FS_SYSTEM, FS_SYSTEM))\r\n\t\t\treturn false;\r\n\t\tnewbinary = tmpbinary;\r\n\t}\r\n\tchmod(newbinary, dst.st_mode|S_IXUSR);\t//but make sure its executable, just in case...\r\n\r\n\t//overwrite the name we were started through. this is supposed to be atomic.\r\n\tif (rename(newbinary, enginebinary) < 0)\r\n\t{\r\n\t\tCon_Printf(\"Failed to overwrite %s with %s\\n\", enginebinary, newbinary);\r\n\t\treturn false;\t//failed\r\n\t}\r\n\treturn true;\t//succeeded.\r\n}\r\nqboolean Sys_EngineMayUpdate(void)\r\n{\r\n\tchar enginebinary[MAX_OSPATH];\r\n\tint len;\r\n\r\n\t//update blocked via commandline\r\n\tif (COM_CheckParm(\"-noupdate\") || COM_CheckParm(\"--noupdate\") || COM_CheckParm(\"-noautoupdate\") || COM_CheckParm(\"--noautoupdate\"))\r\n\t\treturn false;\r\n\r\n\t//check that we can actually do it.\r\n\tlen = readlink(\"/proc/self/exe\", enginebinary, sizeof(enginebinary)-1);\r\n\tif (len <= 0)\r\n\t\treturn false;\r\n\r\n\t//if we can't get a revision number from our cflags then don't allow updates (unless forced on).\r\n\tif (!COM_CheckParm(\"-allowupdate\"))\r\n\t{\r\n\t\tchar *s;\r\n\t\tif (revision_number(true)<=0)\r\n\t\t\treturn false;\r\n\r\n\t\t//if there's 3 consecutive digits or digit.digit then assume the user is doing their own versioning, and disallow engine updates (unless they use the -allowupdate arg).\r\n\t\t//(that or they ran one of our older builds directly)\r\n\t\tfor (s=COM_SkipPath(enginebinary); *s; s++)\r\n\t\t{\r\n\t\t\tif ( s[0] >= '0' && s[0] <= '9')\r\n\t\t\tif ((s[1] >= '0' && s[1] <= '9') || s[1] == '.' || s[1] == '_' || s[1] == '-')\r\n\t\t\tif ( s[2] >= '0' && s[2] <= '9')\r\n\t\t\t\treturn false;\r\n\t\t}\r\n\t}\r\n\r\n\tenginebinary[len] = 0;\r\n\tif (access(enginebinary, R_OK|W_OK|X_OK) < 0)\r\n\t\treturn false;\t//can't write it. don't try downloading updates.\r\n\t*COM_SkipPath(enginebinary) = 0;\r\n\tif (access(enginebinary, R_OK|W_OK) < 0)\r\n\t\treturn false;\t//can't write to the containing directory. this does not bode well for moves/overwrites.\r\n\r\n\r\n\treturn true;\r\n}\r\n#endif\r\n"
  },
  {
    "path": "engine/common/sys_win_threads.c",
    "content": "/*\r\nCopyright (C) 1996-1997 Id Software, Inc.\r\n\r\nThis program is free software; you can redistribute it and/or\r\nmodify it under the terms of the GNU General Public License\r\nas published by the Free Software Foundation; either version 2\r\nof the License, or (at your option) any later version.\r\n\r\nThis program is distributed in the hope that it will be useful,\r\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\r\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\r\n\r\nSee the GNU General Public License for more details.\r\n\r\nYou should have received a copy of the GNU General Public License\r\nalong with this program; if not, write to the Free Software\r\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\r\n\r\n*/\r\n#include \"quakedef.h\"\r\n#include <sys/types.h>\r\n#include <sys/timeb.h>\r\n\r\n#include \"winquake.h\"\r\n#include <conio.h>\r\n\r\n#if (defined(_DEBUG) || defined(DEBUG))\r\n#if !defined(_MSC_VER) || _MSC_VER > 1200\r\n#define CATCHCRASH\r\n#endif\r\n#ifdef _MSC_VER\r\n#define MSVC_SEH\r\nDWORD CrashExceptionHandler (qboolean iswatchdog, DWORD exceptionCode, LPEXCEPTION_POINTERS exceptionInfo);\r\n#else\r\nLONG CALLBACK nonmsvc_CrashExceptionHandler(PEXCEPTION_POINTERS ExceptionInfo);\r\n#endif\r\n#endif\r\n\r\n\r\n\r\n#if defined(_DEBUG) && defined(_MSC_VER)\r\nconst DWORD MS_VC_EXCEPTION=0x406D1388;\r\n#pragma pack(push,8)\r\ntypedef struct tagTHREADNAME_INFO\r\n{\r\n\tDWORD dwType; // Must be 0x1000.\r\n\tLPCSTR szName; // Pointer to name (in user addr space).\r\n\tDWORD dwThreadID; // Thread ID (-1=caller thread).\r\n\tDWORD dwFlags; // Reserved for future use, must be zero.\r\n} THREADNAME_INFO;\r\n#pragma pack(pop)\r\nvoid Sys_SetThreadName(unsigned int dwThreadID, char *threadName)\r\n{\r\n\tTHREADNAME_INFO info;\r\n\tinfo.dwType = 0x1000;\r\n\tinfo.szName = threadName;\r\n\tinfo.dwThreadID = dwThreadID;\r\n\tinfo.dwFlags = 0;\r\n\r\n\t__try\r\n\t{\r\n\t\tRaiseException( MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(ULONG_PTR), (ULONG_PTR*)&info );\r\n\t}\r\n\t__except(EXCEPTION_EXECUTE_HANDLER)\r\n\t{\r\n\t}\r\n}\r\n#endif\r\n\r\n#if !defined(WINRT) && defined(MULTITHREAD)\r\n#include <process.h>\r\n/* Thread creation calls */\r\ntypedef struct threadwrap_s\r\n{\r\n\tint (*func)(void *);\r\n\tvoid *args;\r\n\tchar name[1];\r\n} threadwrap_t;\r\n\r\ntypedef struct\r\n{\r\n\tHANDLE handle;\r\n\tDWORD threadid;\r\n} threadctx_t;\r\n\r\n// the thread call is wrapped so we don't need WINAPI everywhere\r\nunsigned int WINAPI threadwrapper(void *args)\r\n{\r\n\tthreadwrap_t tw;\r\n\ttw.func = ((threadwrap_t *)args)->func;\r\n\ttw.args = ((threadwrap_t *)args)->args;\r\n\r\n#if defined(_DEBUG) && defined(_MSC_VER)\r\n\tSys_SetThreadName(GetCurrentThreadId(), ((threadwrap_t *)args)->name);\r\n#endif\r\n#ifdef CATCHCRASH\r\n\tif (strcmp(((threadwrap_t *)args)->name, \"watchdog\"))\t//don't do this for the watchdog timer, as it just breaks the 'no' option.\r\n\t{\r\n#ifdef MSVC_SEH\r\n\t\t__try\r\n\t\t{\r\n\t\t\tfree(args);\r\n\t\t\ttw.func(tw.args);\r\n\t\t}\r\n\t\t__except (CrashExceptionHandler(false, GetExceptionCode(), GetExceptionInformation()))\r\n\t\t{\r\n\t\t}\r\n#else\r\n\t\tPVOID (WINAPI *pAddVectoredExceptionHandler)(ULONG\tFirstHandler,\tPVECTORED_EXCEPTION_HANDLER VectoredHandler);\r\n\t\tdllfunction_t dbgfuncs[] = {{(void*)&pAddVectoredExceptionHandler, \"AddVectoredExceptionHandler\"}, {NULL,NULL}};\r\n\t\tif (Sys_LoadLibrary(\"kernel32.dll\", dbgfuncs) && pAddVectoredExceptionHandler)\r\n\t\t\tpAddVectoredExceptionHandler(0, nonmsvc_CrashExceptionHandler);\r\n\t\tfree(args);\r\n\t\ttw.func(tw.args);\r\n#endif\r\n\t}\r\n\telse\r\n#endif\r\n\t{\r\n\t\tfree(args);\r\n\t\ttw.func(tw.args);\r\n\t}\r\n\r\n#ifndef WIN32CRTDLL\r\n\t_endthreadex(0);\r\n#endif\r\n\treturn 0;\r\n}\r\n\r\nvoid *Sys_CreateThread(char *name, int (*func)(void *), void *args, int priority, int stacksize)\r\n{\r\n\tthreadctx_t *ctx = (threadctx_t *)malloc(sizeof(*ctx));\r\n\tthreadwrap_t *tw = (threadwrap_t *)malloc(sizeof(threadwrap_t)+strlen(name));\r\n\r\n\tif (!tw || !ctx)\r\n\t{\r\n\t\tfree(tw);\r\n\t\tfree(ctx);\r\n\t\treturn NULL;\r\n\t}\r\n\r\n\tstacksize += 128; // wrapper overhead, also prevent default stack size\r\n\ttw->func = func;\r\n\ttw->args = args;\r\n\tstrcpy(tw->name, name);\r\n#ifdef WIN32CRTDLL\r\n\tctx->handle = (HANDLE)CreateThread(NULL, stacksize, &threadwrapper, (void *)tw, 0, &ctx->threadid);\r\n#else\r\n\tctx->handle = (HANDLE)_beginthreadex(NULL, stacksize, &threadwrapper, (void *)tw, 0, (unsigned int*)&ctx->threadid);\r\n#endif\r\n\tif (!ctx->handle)\r\n\t{\r\n\t\tfree(tw);\r\n\t\tfree(ctx);\r\n\t\treturn NULL;\r\n\t}\r\n\r\n\treturn (void *)ctx;\r\n}\r\n\r\nvoid Sys_DetachThread(void *thread)\r\n{\r\n\tthreadctx_t *ctx = thread;\r\n\tCloseHandle(ctx->handle);\r\n\tfree(ctx);\r\n}\r\n\r\nvoid Sys_WaitOnThread(void *thread)\r\n{\r\n\tthreadctx_t *ctx = thread;\r\n#ifdef SERVERONLY\r\n\tWaitForSingleObject(ctx->handle, INFINITE);\r\n#else\r\n\twhile (WAIT_OBJECT_0+1 == MsgWaitForMultipleObjects(1, &ctx->handle, false, INFINITE, QS_SENDMESSAGE))\r\n\t{\r\n\t\tMSG msg;\r\n\t\tif (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))\r\n\t\t\tDispatchMessage (&msg);\r\n\t}\r\n#endif\r\n\tCloseHandle(ctx->handle);\r\n\tfree(ctx);\r\n}\r\n\r\n//used on fatal errors.\r\nvoid Sys_ThreadAbort(void)\r\n{\r\n\tExitThread(0);\r\n}\r\n\r\nstatic DWORD mainthread;\r\nvoid Sys_ThreadsInit(void)\r\n{\r\n\tmainthread = GetCurrentThreadId();\r\n}\r\nqboolean Sys_IsMainThread(void)\r\n{\r\n\treturn mainthread == GetCurrentThreadId();\r\n}\r\n\r\nqboolean Sys_IsThread(void *thread)\r\n{\r\n\tthreadctx_t *ctx = thread;\r\n\treturn ctx->threadid == GetCurrentThreadId();\r\n}\r\n\r\n\r\n/* Mutex calls */\r\n/*\r\nNote that a 'mutex' in win32 terminology is a cross-process/kernel object\r\nA critical section is a single-process object, and thus can be provided more cheaply\r\n*/\r\n#ifdef USE_MSVCRT_DEBUG\r\nvoid *Sys_CreateMutexNamed(char *file, int line)\r\n{\r\n#ifdef _DEBUG\r\n\t//linux's pthread code doesn't like me recursively locking mutexes, so add some debug-only code to catch that on windows too so that we don't get nasty surprises.\r\n\tCRITICAL_SECTION *mutex = _malloc_dbg(sizeof(*mutex)+sizeof(int), _NORMAL_BLOCK, file, line);\r\n\t*(int*)(1+(CRITICAL_SECTION*)mutex) = 0;\r\n#else\r\n\tCRITICAL_SECTION *mutex = _malloc_dbg(sizeof(*mutex), _NORMAL_BLOCK, file, line);\r\n#endif\r\n\tInitializeCriticalSection(mutex);\r\n\treturn (void *)mutex;\r\n}\r\n#undef Sys_CreateMutex\r\n#endif\r\nvoid *QDECL Sys_CreateMutex(void)\r\n{\r\n#ifdef _DEBUG\r\n\t//linux's pthread code doesn't like me recursively locking mutexes, so add some debug-only code to catch that on windows too so that we don't get nasty surprises.\r\n\tCRITICAL_SECTION *mutex = malloc(sizeof(*mutex)+sizeof(int));\r\n\t*(int*)(1+(CRITICAL_SECTION*)mutex) = 0;\r\n#else\r\n\tCRITICAL_SECTION *mutex = malloc(sizeof(*mutex));\r\n#endif\r\n\tInitializeCriticalSection(mutex);\r\n\treturn (void *)mutex;\r\n}\r\n\r\n/*qboolean Sys_TryLockMutex(void *mutex)\r\n{\r\n#ifdef _DEBUG\r\n\tif (!mutex)\r\n\t{\r\n\t\tCon_Printf(\"Invalid mutex\\n\");\r\n\t\treturn false;\r\n\t}\r\n#endif\r\n\tif (TryEnterCriticalSection(mutex))\r\n\t{\r\n#ifdef _DEBUG\r\n\t\tif (*(int*)(1+(CRITICAL_SECTION*)mutex))\r\n\t\t\tCon_Printf(\"Double lock\\n\");\r\n\t\t*(int*)(1+(CRITICAL_SECTION*)mutex)+=1;\r\n#endif\r\n\t\treturn true;\r\n\t}\r\n\treturn false;\r\n}*/\r\n\r\nqboolean QDECL Sys_LockMutex(void *mutex)\r\n{\r\n#if 0//def _DEBUG\r\n\tif (!mutex)\r\n\t{\r\n\t\tCon_Printf(\"Invalid mutex\\n\");\r\n\t\treturn false;\r\n\t}\r\n#endif\r\n\tEnterCriticalSection(mutex);\r\n#ifdef _DEBUG\r\n\tif (*(int*)(1+(CRITICAL_SECTION*)mutex))\r\n\t\tCon_Printf(\"Double lock\\n\");\r\n\t*(int*)(1+(CRITICAL_SECTION*)mutex)+=1;\r\n#endif\r\n\treturn true;\r\n}\r\n\r\nqboolean QDECL Sys_UnlockMutex(void *mutex)\r\n{\r\n#ifdef _DEBUG\r\n\t*(int*)(1+(CRITICAL_SECTION*)mutex)-=1;\r\n#endif\r\n\tLeaveCriticalSection(mutex);\r\n\treturn true;\r\n}\r\n\r\nvoid QDECL Sys_DestroyMutex(void *mutex)\r\n{\r\n\tDeleteCriticalSection(mutex);\r\n\tfree(mutex);\r\n}\r\n\r\n/* Conditional wait calls */\r\n/*\r\nTODO: Windows Vista has condition variables as documented here:\r\nhttp://msdn.microsoft.com/en-us/library/ms682052(VS.85).aspx\r\nNote this uses Slim Reader/Writer locks (Vista+ exclusive)\r\nor critical sections.\r\n\r\nThe condition variable implementation is based on http://www.cs.wustl.edu/~schmidt/win32-cv-1.html.\r\n(the libsdl-based stuff was too buggy)\r\n*/\r\ntypedef struct condvar_s\r\n{\r\n\tint waiters;\r\n\tint release;\r\n\tint waitgeneration;\r\n\tCRITICAL_SECTION countlock;\r\n\tCRITICAL_SECTION mainlock;\r\n\tHANDLE evnt;\r\n} condvar_t;\r\n\r\nvoid *Sys_CreateConditional(void)\r\n{\r\n\tcondvar_t *cv;\r\n\r\n\tcv = (condvar_t *)malloc(sizeof(condvar_t));\r\n\tif (!cv)\r\n\t\treturn NULL;\r\n\r\n\tcv->waiters = 0;\r\n\tcv->release = 0;\r\n\tcv->waitgeneration = 0;\r\n\tInitializeCriticalSection (&cv->mainlock);\r\n\tInitializeCriticalSection (&cv->countlock);\r\n\tcv->evnt = CreateEvent(NULL, TRUE, FALSE, NULL);\r\n\r\n\tif (cv->evnt)\r\n\t\treturn (void *)cv;\r\n\r\n\t// something failed so deallocate everything\r\n\tDeleteCriticalSection(&cv->countlock);\r\n\tDeleteCriticalSection(&cv->mainlock);\r\n\tfree(cv);\r\n\r\n\treturn NULL;\r\n}\r\n\r\nqboolean Sys_LockConditional(void *condv)\r\n{\r\n\tEnterCriticalSection(&((condvar_t *)condv)->mainlock);\r\n\treturn true;\r\n}\r\n\r\nqboolean Sys_UnlockConditional(void *condv)\r\n{\r\n\tLeaveCriticalSection(&((condvar_t *)condv)->mainlock);\r\n\treturn true;\r\n}\r\n\r\nqboolean Sys_ConditionWait(void *condv)\r\n{\r\n\tqboolean done;\r\n\tcondvar_t *cv = (condvar_t *)condv;\r\n\tqboolean success;\r\n\tint mygen;\r\n\r\n\t// increase count for non-signaled waiting threads\r\n\tEnterCriticalSection(&cv->countlock);\r\n\tcv->waiters++;\r\n\tmygen = cv->waitgeneration;\r\n\tLeaveCriticalSection(&cv->countlock);\r\n\r\n\tLeaveCriticalSection(&cv->mainlock); // unlock as per condition variable definition\r\n\r\n\t// wait on a signal\r\n\tfor(;;)\r\n\t{\r\n#if 1\r\n\t\tsuccess = (WaitForSingleObject(cv->evnt, INFINITE) != WAIT_FAILED);\r\n#else\r\n\t\tdo\r\n\t\t{\r\n\t\t\tMSG msg;\r\n\t\t\twhile (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE))\r\n\t\t\t\tDispatchMessage (&msg);\r\n\t\t\tstatus = MsgWaitForMultipleObjects(1, &cv->evnt, FALSE, INFINITE, QS_SENDMESSAGE|QS_POSTMESSAGE);\r\n\t\t} while (status == (WAIT_OBJECT_0+1));\r\n\t\tsuccess = status != WAIT_FAILED;\r\n#endif\r\n\t\tEnterCriticalSection(&cv->countlock);\r\n\t\tdone = cv->release > 0 && cv->waitgeneration != mygen;\r\n\t\tif (done)\r\n\t\t{\r\n\t\t\tcv->waiters--;\r\n\t\t\tcv->release--;\r\n\t\t\tdone = cv->release == 0;\r\n\t\t\tif (done)\r\n\t\t\t\tResetEvent(cv->evnt);\r\n\t\t\tLeaveCriticalSection(&cv->countlock);\r\n\t\t\tbreak;\r\n\t\t}\r\n\t\tLeaveCriticalSection(&cv->countlock);\r\n\t}\r\n\r\n\tEnterCriticalSection(&cv->mainlock); // lock as per condition variable definition\r\n\treturn success;\r\n}\r\n\r\nqboolean Sys_ConditionSignal(void *condv)\r\n{\r\n\tcondvar_t *cv = (condvar_t *)condv;\r\n\r\n\tEnterCriticalSection(&cv->mainlock);\r\n\r\n\t// if there are non-signaled waiting threads, we signal one and wait on the response\r\n\tEnterCriticalSection(&cv->countlock);\r\n\tif (cv->waiters > cv->release)\r\n\t{\r\n\t\tif (!cv->release)\r\n\t\t\tSetEvent(cv->evnt);\r\n\t\tcv->release++;\r\n\t\tcv->waitgeneration++;\r\n\t}\r\n\tLeaveCriticalSection(&cv->countlock);\r\n\r\n\tLeaveCriticalSection(&cv->mainlock);\r\n\r\n\treturn true;\r\n}\r\n\r\nqboolean Sys_ConditionBroadcast(void *condv)\r\n{\r\n\tcondvar_t *cv = (condvar_t *)condv;\r\n\r\n\tEnterCriticalSection(&cv->mainlock);\r\n\r\n\t// if there are non-signaled waiting threads, we signal all of them and wait on all the responses back\r\n\tEnterCriticalSection(&cv->countlock);\r\n\tif (cv->waiters > 0)\r\n\t{\r\n\t\tif (!cv->release)\r\n\t\t\tSetEvent(cv->evnt);\r\n\t\tcv->release = cv->waiters;\r\n\t\tcv->waitgeneration++;\r\n\t}\r\n\tLeaveCriticalSection(&cv->countlock);\r\n\r\n\tLeaveCriticalSection(&cv->mainlock);\r\n\r\n\treturn true;\r\n}\r\n\r\nvoid Sys_DestroyConditional(void *condv)\r\n{\r\n\tcondvar_t *cv = (condvar_t *)condv;\r\n\r\n\t//make sure noone is still trying to poke it while shutting down\r\n//\tSys_LockConditional(condv);\r\n//\tSys_UnlockConditional(condv);\r\n\r\n\tCloseHandle(cv->evnt);\r\n\tDeleteCriticalSection(&cv->countlock);\r\n\tDeleteCriticalSection(&cv->mainlock);\r\n\tfree(cv);\r\n}\r\n\r\n#endif\r\n\r\n#ifdef SUBSERVERS\r\ntypedef struct\r\n{\r\n\tvfsfile_t pub;\r\n\r\n\tHANDLE inpipe;\r\n\tHANDLE outpipe;\r\n} winsubserver_t;\r\n\r\nstatic int QDECL Sys_MSV_ReadBytes (struct vfsfile_s *file, void *buffer, int bytestoread)\r\n{\r\n\twinsubserver_t *s = (winsubserver_t*)file;\r\n\tDWORD avail;\r\n\t//trying to do this stuff without blocking is a real pain.\r\n\tif (!PeekNamedPipe(s->inpipe, NULL, 0, NULL, &avail, NULL))\r\n\t{\r\n\t\t/*switch(GetLastError())\r\n\t\t{\r\n\t\tcase ERROR_BROKEN_PIPE:\r\n\t\tcase ERROR_PIPE_NOT_CONNECTED:\r\n\t\tcase ERROR_INVALID_HANDLE:\r\n\t\t\tbreak;\r\n\t\t}*/\r\n\t\treturn -1;\t//EOF\r\n\t}\r\n\tif (avail)\r\n\t{\r\n\t\tif (avail > bytestoread)\r\n\t\t\tavail = bytestoread;\r\n\t\tif (ReadFile(s->inpipe, buffer, avail, &avail, NULL))\r\n\t\t\treturn avail;\r\n\t}\r\n\treturn 0;\r\n}\r\nstatic int QDECL Sys_MSV_WriteBytes (struct vfsfile_s *file, const void *buffer, int bytestowrite)\r\n{\r\n\twinsubserver_t *s = (winsubserver_t*)file;\r\n\tDWORD wrote = 0;\r\n\t//blocks. life sucks.\r\n\tif (!WriteFile(s->outpipe, buffer, bytestowrite, &wrote, NULL))\r\n\t\treturn -1;\r\n\treturn wrote;\r\n}\r\nstatic qboolean QDECL Sys_MSV_Close (struct vfsfile_s *file)\r\n{\r\n\twinsubserver_t *s = (winsubserver_t*)file;\r\n\r\n\tCloseHandle(s->inpipe);\r\n\tCloseHandle(s->outpipe);\r\n\t//we already closed any process handles. the child will detect its input became unreadable and take that as a signal to die.\r\n\tZ_Free(s);\r\n\treturn true;\r\n}\r\n\r\nvfsfile_t *Sys_ForkServer(void)\r\n{\r\n\twchar_t exename[256];\r\n\twchar_t curdir[256];\r\n\tchar cmdline[8192];\r\n\twchar_t wtmp[countof(cmdline)];\r\n\tPROCESS_INFORMATION childinfo;\r\n\tSTARTUPINFOW startinfo;\r\n\tSECURITY_ATTRIBUTES pipesec = {sizeof(pipesec), NULL, TRUE};\r\n\twinsubserver_t *ctx = Z_Malloc(sizeof(*ctx));\r\n\r\n\tGetModuleFileNameW(NULL, exename, countof(exename));\r\n\tGetCurrentDirectoryW(countof(curdir), curdir);\r\n\tQ_snprintfz(cmdline, sizeof(cmdline), \"foo -noreset -clusterslave %s\", FS_GetManifestArgs());\t//fixme: include which manifest is in use, so configs get set up the same.\r\n\r\n\tmemset(&startinfo, 0, sizeof(startinfo));\r\n\tstartinfo.cb = sizeof(startinfo);\r\n\tstartinfo.hStdInput = INVALID_HANDLE_VALUE;\r\n\tstartinfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);\r\n\tstartinfo.hStdOutput = INVALID_HANDLE_VALUE;\r\n\tstartinfo.dwFlags |= STARTF_USESTDHANDLES;\r\n\r\n\t//create pipes for the stdin/stdout.\r\n\tCreatePipe(&ctx->inpipe, &startinfo.hStdOutput, &pipesec, 0);\r\n\tCreatePipe(&startinfo.hStdInput, &ctx->outpipe, &pipesec, 0);\r\n\r\n\tSetHandleInformation(ctx->inpipe, HANDLE_FLAG_INHERIT, 0);\r\n\tSetHandleInformation(ctx->outpipe, HANDLE_FLAG_INHERIT, 0);\r\n\tSetHandleInformation(startinfo.hStdOutput, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT);\r\n\tSetHandleInformation(startinfo.hStdInput, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT);\r\n\r\n\tCreateProcessW(exename, widen(wtmp, sizeof(wtmp), cmdline), NULL, NULL, TRUE, 0, NULL, curdir, &startinfo, &childinfo);\r\n\r\n\t//child will close when its pipes are closed. we don't need to hold on to the child process handle.\r\n\tCloseHandle(childinfo.hProcess);\r\n\tCloseHandle(childinfo.hThread);\r\n\r\n\t//these ends of the pipes were inherited by now, so we can discard them in the caller.\r\n\tCloseHandle(startinfo.hStdOutput);\r\n\tCloseHandle(startinfo.hStdInput);\r\n\r\n\tctx->pub.ReadBytes = Sys_MSV_ReadBytes;\r\n\tctx->pub.WriteBytes = Sys_MSV_WriteBytes;\r\n\tctx->pub.Close = Sys_MSV_Close;\r\n\treturn &ctx->pub;\r\n}\r\n\r\nvfsfile_t *Sys_GetStdInOutStream(void)\r\n{\r\n\twinsubserver_t *ctx = Z_Malloc(sizeof(*ctx));\r\n\tctx->inpipe = GetStdHandle(STD_INPUT_HANDLE);\r\n\tctx->outpipe = GetStdHandle(STD_OUTPUT_HANDLE);\r\n\r\n\t//cause some errors if something else uses them (also avoid problems when closing).\r\n\tSetStdHandle(STD_INPUT_HANDLE, NULL);\r\n\tSetStdHandle(STD_OUTPUT_HANDLE, NULL);\r\n\r\n\tctx->pub.ReadBytes = Sys_MSV_ReadBytes;\r\n\tctx->pub.WriteBytes = Sys_MSV_WriteBytes;\r\n\tctx->pub.Close = Sys_MSV_Close;\r\n\treturn &ctx->pub;\r\n}\r\n\r\n#endif\r\n\r\n"
  },
  {
    "path": "engine/common/translate.c",
    "content": "#include \"quakedef.h\"\n#include <wctype.h>\n\n//#define COLOURMISSINGSTRINGS\t\t//for english people to more easily see what's not translatable (text still white)\n//#define COLOURUNTRANSLATEDSTRINGS\t//show empty translations as alt-text versions of the original string\n\n//client may remap messages from the server to a regional bit of text.\n//server may remap progs messages\n\n//basic language is english (cos that's what (my version of) Quake uses).\n//translate is english->lang\n//untranslate is lang->english for console commands.\n\nstatic void FilterPurge(void);\nstatic void FilterInit(const char *file);\n\nint com_language;\nchar sys_language[64] = \"\";\nstatic char langpath[MAX_OSPATH] = \"\";\nstruct language_s languages[MAX_LANGUAGES];\n\nstatic void QDECL TL_LanguageChanged(struct cvar_s *var, char *oldvalue)\n{\n\tcom_language = TL_FindLanguage(var->string);\n}\n\ncvar_t language = CVARAFCD(\"lang\", sys_language, \"prvm_language\", CVAR_USERINFO|CVAR_NORESET/*otherwise gamedir switches will be annoying*/, TL_LanguageChanged, \"This cvar contains the language_dialect code of your language, used to find localisation strings.\");\n\nstatic void Filter_Reload_f(void)\n{\n\tchar *file = FS_MallocFile(\"filter.txt\", FS_ROOT, NULL);\n\tFilterInit(file?file:\"\");\n\tFS_FreeFile(file);\n}\nvoid TranslateInit(void)\n{\n\tCmd_AddCommand(\"com_reloadfilter\", Filter_Reload_f);\n\tCvar_Register(&language, \"Internationalisation\");\n}\n\nvoid TL_Shutdown(void)\n{\n\tint j;\n\n\tfor (j = 0; j < MAX_LANGUAGES; j++)\n\t{\n\t\tif (!languages[j].name)\n\t\t\tcontinue;\n\t\tfree(languages[j].name);\n\t\tlanguages[j].name = NULL;\n\t\tPO_Close(languages[j].po);\n\t\tlanguages[j].po = NULL;\n\n\t\tPO_Close(languages[j].po_qex);\n\t\tlanguages[j].po_qex = NULL;\n\t}\n\tFilterPurge();\n}\n\nstatic int TL_LoadLanguage(char *lang)\n{\n\tvfsfile_t *f;\n\tint j;\n\tchar *u;\n\tfor (j = 0; j < MAX_LANGUAGES; j++)\n\t{\n\t\tif (!languages[j].name)\n\t\t\tbreak;\n\t\tif (!stricmp(languages[j].name, lang))\n\t\t\treturn j;\n\t}\n\n\t//err... oops, ran out of languages...\n\tif (j == MAX_LANGUAGES)\n\t\treturn 0;\n\n\tif (*lang)\n\t\tf = FS_OpenVFS(va(\"%sfteqw.%s.po\", langpath, lang), \"rb\", FS_SYSTEM);\n\telse\n\t\tf = NULL;\n\tif (!f && *lang)\n\t{\n\t\t//keep truncating until we can find a name that works\n\t\tu = strrchr(lang, '_');\n\t\tif (u)\n\t\t{\n\t\t\t*u = 0;\n\t\t\treturn TL_LoadLanguage(lang);\n\t\t}\n\t}\n\tlanguages[j].name = strdup(lang);\n\tlanguages[j].po = NULL;\n\t\n#if !defined(COLOURUNTRANSLATEDSTRINGS) && !defined(COLOURMISSINGSTRINGS)\n\tif (f)\n#endif\n\t{\n\t\tlanguages[j].po = PO_Create();\n\t\tPO_Merge(languages[j].po, f);\n\t}\n\n\treturn j;\n}\nint TL_FindLanguage(const char *lang)\n{\n\tchar trimname[64];\n\tQ_strncpyz(trimname, lang, sizeof(trimname));\n\treturn TL_LoadLanguage(trimname);\n}\n\n//need to set up default languages for any early prints before cvars are inited.\nvoid TL_InitLanguages(const char *newlangpath)\n{\n\tint i;\n\tchar *lang;\n\n\tif (!newlangpath)\n\t\tnewlangpath = \"\";\n\n\tQ_strncpyz(langpath, newlangpath, sizeof(langpath));\n\n\t//lang can override any environment or system settings.\n\tif ((i = COM_CheckParm(\"-lang\")))\n\t\tQ_strncpyz(sys_language, com_argv[i+1], sizeof(sys_language));\n\telse\n\t{\n\t\tlang = NULL;\n\t\tif (!lang)\n\t\t\tlang = getenv(\"LANGUAGE\");\n\t\tif (!lang)\n\t\t\tlang = getenv(\"LC_ALL\");\n\t\tif (!lang)\n\t\t\tlang = getenv(\"LC_MESSAGES\");\n\t\tif (!lang)\n\t\t\tlang = getenv(\"LANG\");\n\t\tif (!lang)\n\t\t\tlang = \"\";\n\t\tif (!strcmp(lang, \"C\") || !strcmp(lang, \"POSIX\"))\n\t\t\tlang = \"\";\n\n\t\t//windows will have already set the locale from the windows settings, so only replace it if its actually valid.\n\t\tif (*lang)\n\t\t\tQ_strncpyz(sys_language, lang, sizeof(sys_language));\n\t}\n\n\t//clean it up.\n\t//takes the form: [language[_territory][.codeset][@modifier]]\n\t//we don't understand modifiers\n\tlang = strrchr(sys_language, '@');\n\tif (lang)\n\t\t*lang = 0;\n\t//we don't understand codesets sadly.\n\tlang = strrchr(sys_language, '.');\n\tif (lang)\n\t\t*lang = 0;\n\t//we also only support the single primary locale (no fallbacks, we're just using the language[+territory])\n\tlang = strrchr(sys_language, ':');\n\tif (lang)\n\t\t*lang = 0;\n\t//but we do support territories.\n\t\n\tcom_language = TL_FindLanguage(sys_language);\n\n\t//make sure a fallback exists, but not as language 0\n\tTL_FindLanguage(\"\");\n}\n\n\n\n\n#ifdef HEXEN2\n//this stuff is for hexen2 translation strings.\n//(hexen2 is uuuuggllyyyy...)\nstatic char *strings_list;\nstatic char **strings_table;\nstatic int strings_count;\nstatic qboolean strings_loaded;\nvoid T_FreeStrings(void)\n{\t//on map change, following gamedir change\n\tif (strings_loaded)\n\t{\n\t\tBZ_Free(strings_list);\n\t\tBZ_Free(strings_table);\n\t\tstrings_count = 0;\n\t\tstrings_loaded = false;\n\t}\n}\nvoid T_LoadString(void)\n{\n\tint i;\n\tchar *s, *s2;\n\t//count new lines\n\tstrings_loaded = true;\n\tstrings_count = 0;\n\tstrings_list = FS_LoadMallocFile(\"strings.txt\", NULL);\n\tif (!strings_list)\n\t\treturn;\n\n\tfor (s = strings_list; *s; s++)\n\t{\n\t\tif (*s == '\\n')\n\t\t\tstrings_count++;\n\t}\n\tstrings_table = BZ_Malloc(sizeof(char*)*strings_count);\n\n\ts = strings_list;\n\tfor (i = 0; i < strings_count; i++)\n\t{\n\t\tstrings_table[i] = s;\n\t\ts2 = strchr(s, '\\n');\n\t\tif (!s2)\n\t\t\tbreak;\n\n\t\twhile (s < s2)\n\t\t{\n\t\t\tif (*s == '\\r')\n\t\t\t\t*s = '\\0';\n\t\t\telse if (*s == '^' || *s == '@')\t//becomes new line\n\t\t\t\t*s = '\\n';\n\t\t\ts++;\n\t\t}\n\t\ts = s2+1;\n\t\t*s2 = '\\0';\n\t}\n}\nchar *T_GetString(int num)\n{\n\tif (!strings_loaded)\n\t{\n\t\tT_LoadString();\n\t}\n\tif (num<0 || num >= strings_count)\n\t\treturn \"BAD STRING\";\n\n\treturn strings_table[num];\n}\n\n#ifdef HAVE_CLIENT\n//for hexen2's objectives and stuff.\nstatic char *info_strings_list;\nstatic char **info_strings_table;\nstatic int info_strings_count;\nstatic qboolean info_strings_loaded;\nvoid T_FreeInfoStrings(void)\n{\t//on map change, following gamedir change\n\tif (info_strings_loaded)\n\t{\n\t\tBZ_Free(info_strings_list);\n\t\tBZ_Free(info_strings_table);\n\t\tinfo_strings_count = 0;\n\t\tinfo_strings_loaded = false;\n\t}\n}\nvoid T_LoadInfoString(void)\n{\n\tint i;\n\tchar *s, *s2;\n\t//count new lines\n\tinfo_strings_loaded = true;\n\tinfo_strings_count = 0;\n\tinfo_strings_list = FS_LoadMallocFile(\"infolist.txt\", NULL);\n\tif (!info_strings_list)\n\t\treturn;\n\n\tfor (s = info_strings_list; *s; s++)\n\t{\n\t\tif (*s == '\\n')\n\t\t\tinfo_strings_count++;\n\t}\n\tinfo_strings_table = BZ_Malloc(sizeof(char*)*info_strings_count);\n\n\ts = info_strings_list;\n\tfor (i = 0; i < info_strings_count; i++)\n\t{\n\t\tinfo_strings_table[i] = s;\n\t\ts2 = strchr(s, '\\n');\n\t\tif (!s2)\n\t\t\tbreak;\n\n\t\twhile (s < s2)\n\t\t{\n\t\t\tif (*s == '\\r')\n\t\t\t\t*s = '\\0';\n\t\t\telse if (*s == '^' || *s == '@')\t//becomes new line\n\t\t\t\t*s = '\\n';\n\t\t\ts++;\n\t\t}\n\t\ts = s2+1;\n\t\t*s2 = '\\0';\n\t}\n}\nchar *T_GetInfoString(int num)\n{\n\tif (!info_strings_loaded)\n\t{\n\t\tT_LoadInfoString();\n\t}\n\tif (num<0 || num >= info_strings_count)\n\t\treturn \"BAD STRING\";\n\n\treturn info_strings_table[num];\n}\n#endif\n#endif\n\nstruct poline_s\n{\n\tbucket_t buck;\n\tstruct poline_s *next;\n\tchar *orig;\n\tchar *translated;\n};\n\nstruct po_s\n{\n\thashtable_t hash;\n\n\tstruct poline_s *lines;\n};\n\nstatic struct poline_s *PO_AddText(struct po_s *po, const char *orig, const char *trans)\n{\t//input is assumed to be utf-8, but that's not always what quake uses. on the plus side we do have our own silly markup to handle unicode (and colours etc).\n\tsize_t olen = strlen(orig)+1;\n\tsize_t tlen;\n\tstruct poline_s *line;\n\tconst char *s;\n\tchar temp[64];\n\n\t//figure out the required length for the encoding we're actually going to use\n\tif (com_parseutf8.ival != 1)\n\t{\n\t\ttlen = 0;\n\t\tfor (s = trans, tlen = 0; *s; )\n\t\t{\n\t\t\tunsigned int err;\n\t\t\tunsigned int chr = utf8_decode(&err, s, &s);\n\t\t\ttlen += unicode_encode(temp, chr, sizeof(temp), true);\n\t\t}\n\t\ttlen++;\n\t}\n\telse\n\t\ttlen = strlen(trans)+1;\n\n\tline = Z_Malloc(sizeof(*line)+olen+tlen);\n\tmemcpy(line+1, orig, olen);\n\torig = (const char*)(line+1);\n\tline->translated = (char*)(line+1)+olen;\n\tif (com_parseutf8.ival != 1)\n\t{\n\t\t//do the loop again now we know we've got enough space for it.\n\t\tfor (s = trans, tlen = 0; *s; )\n\t\t{\n\t\t\tunsigned int err;\n\t\t\tunsigned int chr = utf8_decode(&err, s, &s);\n\t\t\ttlen += unicode_encode(line->translated+tlen, chr, sizeof(temp), true);\n\t\t}\n\t\tline->translated[tlen] = 0;\n\t}\n\telse\n\t\tmemcpy(line->translated, trans, tlen);\n\ttrans = (const char*)(line->translated);\n\tHash_Add(&po->hash, orig, line, &line->buck);\n\n\tline->next = po->lines;\n\tpo->lines = line;\n\treturn line;\n}\nvoid PO_Merge(struct po_s *po, vfsfile_t *file)\n{\n\tchar *instart, *in, *end;\n\tint inlen;\n\tchar msgid[32768];\n\tchar msgstr[32768];\n\tstruct {\n\t\tquint32_t magic;\n\t\tquint32_t revision;\n\t\tquint32_t numstrings;\n\t\tquint32_t offset_orig;\n\t\tquint32_t offset_trans;\n//\t\tquint32_t hashsize;\n//\t\tquint32_t offset_hash;\n\t} *moheader;\n\n\tqboolean allowblanks = !!COM_CheckParm(\"-translatetoblank\");\n\tif (!file)\n\t\treturn;\n\n\tinlen = file?VFS_GETLEN(file):0;\n\tinstart = in = BZ_Malloc(inlen+1);\n\tif (file)\n\t\tVFS_READ(file, in, inlen);\n\tin[inlen] = 0;\n\tif (file)\n\t\tVFS_CLOSE(file);\n\n\tmoheader = (void*)in;\n\tif (inlen >= sizeof(*moheader) && moheader->magic == 0x950412de)\n\t{\n\t\tstruct\n\t\t{\n\t\t\tquint32_t length;\n\t\t\tquint32_t offset;\n\t\t} *src = (void*)(in+moheader->offset_orig), *dst = (void*)(in+moheader->offset_trans);\n\t\tquint32_t i;\n\t\tfor (i = moheader->numstrings; i-- > 0; src++, dst++)\n\t\t\tPO_AddText(po, in+src->offset, in+dst->offset);\n\t}\n    else\n\t{\n\t\tend = in + inlen;\n\t\twhile(in < end)\n\t\t{\n\t\t\twhile(*in == ' ' || *in == '\\n' || *in == '\\r' || *in == '\\t')\n\t\t\t\tin++;\n\t\t\tif (*in == '#')\n\t\t\t{\n\t\t\t\twhile (*in && *in != '\\n')\n\t\t\t\t\tin++;\n\t\t\t}\n\t\t\telse if (!strncmp(in, \"msgid\", 5) && (in[5] == ' ' || in[5] == '\\t' || in[5] == '\\r' || in[5] == '\\n'))\n\t\t\t{\n\t\t\t\tsize_t start = 0;\n\t\t\t\tsize_t ofs = 0;\n\t\t\t\tin += 5;\n\t\t\t\twhile(1)\n\t\t\t\t{\n\t\t\t\t\twhile(*in == ' ' || *in == '\\n' || *in == '\\r' || *in == '\\t')\n\t\t\t\t\t\tin++;\n\t\t\t\t\tif (*in == '\\\"')\n\t\t\t\t\t{\n\t\t\t\t\t\tin = COM_ParseCString(in, msgid+start, sizeof(msgid) - start, &ofs);\n\t\t\t\t\t\tstart += ofs;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (!strncmp(in, \"msgstr\", 6) && (in[6] == ' ' || in[6] == '\\t' || in[6] == '\\r' || in[6] == '\\n'))\n\t\t\t{\n\t\t\t\tsize_t start = 0;\n\t\t\t\tsize_t ofs = 0;\n\t\t\t\tin += 6;\n\t\t\t\twhile(1)\n\t\t\t\t{\n\t\t\t\t\twhile(*in == ' ' || *in == '\\n' || *in == '\\r' || *in == '\\t')\n\t\t\t\t\t\tin++;\n\t\t\t\t\tif (*in == '\\\"')\n\t\t\t\t\t{\n\t\t\t\t\t\tin = COM_ParseCString(in, msgstr+start, sizeof(msgstr) - start, &ofs);\n\t\t\t\t\t\tstart += ofs;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tif ((*msgid && start) || allowblanks)\n\t\t\t\t\tPO_AddText(po, msgid, msgstr);\n#ifdef COLOURUNTRANSLATEDSTRINGS\n\t\t\t\telse if (!start)\n\t\t\t\t{\n\t\t\t\t\tchar temp[1024];\n\t\t\t\t\tint i;\n\t\t\t\t\tQ_snprintfz(temp, sizeof(temp), \"%s\", *msgstr?msgstr:msgid);\n\t\t\t\t\tfor (i = 0; temp[i]; i++)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (temp[i] == '%')\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\twhile (temp[i] > ' ')\n\t\t\t\t\t\t\t\ti++;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (temp[i] >= ' ')\n\t\t\t\t\t\t\ttemp[i] |= 0x80;\n\t\t\t\t\t}\n\t\t\t\t\tPO_AddText(po, msgid, temp);\n\t\t\t\t}\n#endif\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t//some sort of junk?\n\t\t\t\tin++;\n\t\t\t\twhile (*in && *in != '\\n')\n\t\t\t\t\tin++;\n\t\t\t}\n\t\t}\n\t}\n\n\tBZ_Free(instart);\n}\nstruct po_s *PO_Create(void)\n{\n\tstruct po_s *po;\n\tunsigned int buckets = 1024;\n\n\tpo = Z_Malloc(sizeof(*po) + Hash_BytesForBuckets(buckets));\n\tHash_InitTable(&po->hash, buckets, po+1);\n\treturn po;\n}\nvoid PO_Close(struct po_s *po)\n{\n\tif (!po)\n\t\treturn;\n\twhile(po->lines)\n\t{\n\t\tstruct poline_s *r = po->lines;\n\t\tpo->lines = r->next;\n\t\tZ_Free(r);\n\t}\n\tZ_Free(po);\n}\n\nconst char *PO_GetText(struct po_s *po, const char *msg)\n{\n\tstruct poline_s *line;\n\tif (!po || !msg)\n\t\treturn msg;\n\tline = Hash_Get(&po->hash, msg);\n\n#ifdef COLOURMISSINGSTRINGS\n\tif (!line)\n\t{\n\t\tchar temp[1024];\n\t\tint i;\n\t\tconst char *in = msg;\n\t\tfor (i = 0; *in && i < sizeof(temp)-1; )\n\t\t{\n\t\t\tif (*in == '%')\n\t\t\t{\t//don't mess up % formatting too much\n\t\t\t\twhile (*in > ' ' && i < sizeof(temp)-1)\n\t\t\t\t\ttemp[i++] = *in++;\n\t\t\t}\n\t\t\telse if (in > ' ' && *in < 128)\t//otherwise force any ascii chars to the 0xe0XX range so it doesn't use any freetype fonts so its instantly recognisable as bad.\n\t\t\t\ti += utf8_encode(temp+i, *in++|0xe080, sizeof(temp)-1-i);\n\t\t\telse\n\t\t\t\ttemp[i++] = *in++;\t//don't mess with any c0/extended codepoints\n\t\t}\n\t\ttemp[i] = 0;\n\t\tline = PO_AddText(po, msg, temp);\n\t}\n#endif\n\n\tif (line)\n\t\treturn line->translated;\n\treturn msg;\n}\n\n\nstatic void PO_Merge_Rerelease(struct po_s *po, const char *langname, const char *fmt)\n{\n\t//FOO <plat,plat> = \"CString\"\n\tchar line[32768];\n\tchar key[256];\n\tchar val[32768];\n\tchar *s;\n\tvfsfile_t *file = NULL;\n\n\tif (!file && *langname)\t//use system locale names\n\t\tfile = FS_OpenVFS(va(fmt, langname), \"rb\", FS_GAME);\n\tif (!file)\t//make a guess\n\t{\n\t\ts = NULL;\n\t\tif (langname[0] && langname[1] && (!langname[2] || langname[2] == '-' || langname[2] == '_'))\n\t\t{\t//try to map the user's formal locale to the rerelease's arbitrary names (at least from the perspective of anyone who doesn't speak english).\n\t\t\tif (!strncmp(langname, \"fr\", 2))\n\t\t\t\ts = \"french\";\n\t\t\telse if (!strncmp(langname, \"de\", 2))\n\t\t\t\ts = \"german\";\n\t\t\telse if (!strncmp(langname, \"it\", 2))\n\t\t\t\ts = \"italian\";\n\t\t\telse if (!strncmp(langname, \"ru\", 2))\n\t\t\t\ts = \"russian\";\n\t\t\telse if (!strncmp(langname, \"es\", 2))\n\t\t\t\ts = \"spanish\";\n\t\t}\n\t\tif (s)\n\t\t\tfile = FS_OpenVFS(va(fmt, s), \"rb\", FS_GAME);\n\t}\n\tif (!file)\t//fall back on misnamed american, for lack of a better default.\n\t\tfile = FS_OpenVFS(va(fmt, \"english\"), \"rb\", FS_GAME);\n\tif (file)\n\t{\n\t\t*key = '$';\n\t\twhile(VFS_GETS(file, line, sizeof(line)))\n\t\t{\n\t\t\ts = COM_ParseOut(line, key+1, sizeof(key)-1);\n\t\t\ts = COM_ParseOut(s, val, sizeof(val));\n\t\t\tif (strcmp(val,\"=\"))\n\t\t\t\tcontinue;\n\t\t\ts = COM_ParseCString(s, val, sizeof(val), NULL);\n\t\t\tif (!s)\n\t\t\t\tcontinue;\n\t\t\tPO_AddText(po, key, val);\n\t\t}\n\t\tVFS_CLOSE(file);\n\t}\n}\n\nconst char *TL_Translate(int language, const char *src)\n{\n\tif (*src == '$')\n\t{\n\t\tif (!languages[language].po_qex)\n\t\t{\n\t\t\tchar lang[64], *h;\n\t\t\tvfsfile_t *f = NULL;\n\t\t\tlanguages[language].po_qex = PO_Create();\n\t\t\tPO_Merge_Rerelease(languages[language].po_qex, languages[language].name, \"localization/loc_%s.txt\");\n\n\t\t\tQ_strncpyz(lang, languages[language].name, sizeof(lang));\n\t\t\twhile ((h = strchr(lang, '-')))\n\t\t\t\t*h = '_';\t//standardise it\n\t\t\tif (*lang)\n\t\t\t\tf = FS_OpenVFS(va(\"localisation/%s.po\", lang), \"rb\", FS_GAME);\t//long/specific form\n\t\t\tif (!f)\n\t\t\t{\n\t\t\t\tif ((h = strchr(lang, '_')))\n\t\t\t\t{\n\t\t\t\t\t*h = 0;\n\t\t\t\t\tif (*lang)\n\t\t\t\t\t\tf = FS_OpenVFS(va(\"localisation/%s.po\", lang), \"rb\", FS_GAME);\t//short/general form\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (f)\n\t\t\t\tPO_Merge(languages[language].po_qex, f);\n\t\t}\n\t\tsrc = PO_GetText(languages[language].po_qex, src);\n\t}\n\treturn src;\n}\nvoid TL_Reformat(int language, char *out, size_t outsize, size_t numargs, const char **arg)\n{\n\tconst char *fmt;\n\tconst char *a;\n\tsize_t alen;\n\tunsigned int lastindex = 0;\n\n\tfmt = (numargs>0&&arg[0])?arg[0]:\"\";\n\tfmt = TL_Translate(language, fmt);\n\n\toutsize--;\n\twhile (outsize > 0)\n\t{\n\t\tif (!*fmt)\n\t\t\tbreak;\n\t\telse if (*fmt == '{' && fmt[1] == '{')\n\t\t\t*out++ = '{', fmt+=2, outsize--;\n\t\telse if (*fmt == '}' && fmt[1] == '}')\n\t\t\t*out++ = '}', fmt+=2, outsize--;\n\t\telse if (*fmt == '{')\n\t\t{\n\t\t\tconst char *idxstr = fmt+1;\n\t\t\tunsigned int index = strtoul(idxstr, (char**)&fmt, 10)+1;\n\t\t\tint size = 0;\n\t\t\tif (idxstr == fmt)\t//when no index value was specified, just go for the next one\n\t\t\t\tindex = lastindex+1;\n\t\t\tif (*fmt == ',')\n\t\t\t\tsize = strtol(fmt+1, (char**)&fmt, 10);\n\t\t\tif (*fmt == ':')\n\t\t\t{\t//formatting, which we don't support because its all strings.\n\t\t\t\tfmt = fmt+1;\n\t\t\t\twhile (*fmt && *fmt != '}')\n\t\t\t\t\tfmt++;\n\t\t\t}\n\t\t\tif (*fmt == '}')\n\t\t\t\tfmt++;\n\t\t\telse\n\t\t\t\tbreak;\t//some formatting error\n\n\t\t\tif (index >= numargs || !arg[index])\n\t\t\t\ta = \"\";\n\t\t\telse\n\t\t\t\ta = TL_Translate(language, arg[index]);\n\n\t\t\tlastindex = index;\n\n\t\t\talen = strlen(a);\n\t\t\tif (alen > outsize)\n\t\t\t\talen = outsize;\n\t\t\tif (size > 0)\n\t\t\t{\t//right aligned\n\t\t\t\tif (alen > size)\n\t\t\t\t\talen = size;\n\t\t\t\tmemcpy(out, a, alen);\n\t\t\t}\n\t\t\telse if (size < 0)\n\t\t\t{\t//left aligned\n\t\t\t\tif (alen > -size)\n\t\t\t\t\talen = -size;\n\t\t\t\tmemcpy(out, a, alen);\n\t\t\t}\n\t\t\telse //no alignment, no padding.\n\t\t\t\tmemcpy(out, a, alen);\n\t\t\tout += alen;\n\t\t\toutsize -= alen;\n\t\t}\n\t\telse\n\t\t\t*out++ = *fmt++, outsize--;\n\t}\n\t*out = 0;\n}\n\n#include <ctype.h>\nstatic qbyte *filter[256]; //one list per lead char, simple optimisation instead of some big decision tree.\nstatic qbyte *filtermem;\nstatic int FilterCompareWords(const void *v1, const void *v2)\n{\n\tconst char *s1 = *(const char*const*)v1;\n\tconst char *s2 = *(const char*const*)v2;\n\treturn strcmp(s2,s1);\n}\nstatic void FilterPurge(void)\n{\n\tmemset(filter, 0, sizeof(filter));\n\tfree(filtermem);\n\tfiltermem = NULL;\n}\nstatic void FilterInit(const char *file)\n{\n\tqbyte *tempmemstart = malloc(strlen(file)+1);\n\tqbyte *tempmem = tempmemstart;\n\tconst char **words;\n\tsize_t count = 1, i, l;\n\tsize_t bytes;\n\tconst char *c;\n\n\tFilterPurge();\n\n\tfor (c = file; *c; c++)\n\t\tif (*c == '\\n')\n\t\t\tcount++;\n\n\twords = malloc(sizeof(qbyte*)*count);\n\tcount = 0;\n\tfor (c = file; *c; )\n\t{\n\t\twhile (*c == '\\n')\n\t\t\tc++;\t//don't add 0-byte strings...\n\t\twords[count] = tempmem;\n\t\tfor (; *c; c++)\n\t\t{\n\t\t\tif (*c == ' ')\n\t\t\t\tcontinue; //block even if they omit the spaces.\n\t\t\tif (*c == '\\n')\n\t\t\t\tbreak;\n\t\t\t*tempmem++ = tolower(*c);\n\t\t}\n\t\t*tempmem++ = 0;\n\t\tif (*words[count])\n\t\t\tcount++;\n\t}\n\tqsort(words, count, sizeof(words[0]), FilterCompareWords);\t//sort by lead byte... and longest first...\n\ti = 0;\n\tfor (i = 0, bytes = 0; i < count; i++)\n\t\tbytes += strlen(words[i])+1;\n\tbytes += countof(filter);\n\tfiltermem = tempmem = malloc(bytes);\n\n\tfor (l = countof(filter), i = 0; l-- > 0; )\n\t{\n\t\tif (i < count && words[i][0] == l)\n\t\t{\n\t\t\tfilter[l] = tempmem;\n\t\t\twhile (i < count && *words[i] == l)\n\t\t\t{\t//second copy... urgh. can forget the first char and replace with a length.\n\t\t\t\t*tempmem++ = strlen(words[i]+1);\n\t\t\t\tmemcpy(tempmem, words[i]+1, tempmem[-1]);\t//just the text, no null needed. tighly packed.\n\t\t\t\ttempmem += tempmem[-1];\n\t\t\t\ti++;\n\t\t\t}\n\t\t\t*tempmem++ = 0;\n\t\t}\n\t\telse\n\t\t\tfilter[l] = NULL;\n\t}\n\tfree(tempmemstart);\n\tfree(words);\n}\n#define whiteish(c) (c == ',' || c == '.' || c == ' ' || c == '\\t' || c == '\\r' || c == '\\n')\nchar *FilterObsceneString(const qbyte *in, char *outbuf, size_t bufsize)\n{\t//input must be utf-8... if there's any ^ crap in there then strip it first. no bypassing filters with colour codes.\n\tchar *ret = outbuf;\n\tif (strlen(in) >= bufsize)\n\t\tSys_Error(\"output buffer too small!\");\n\tif (!filtermem)\n\t\tFilter_Reload_f();\nrestart:\n\twhile (*in)\n\t{\n\t\tqbyte c = tolower(*in);\n\t\tif (filter[c])\n\t\t{\n\t\t\tqbyte *m = filter[c];\n\t\t\twhile (*m)\n\t\t\t{\t//for each word starting with this letter...\n\t\t\t\tconst qbyte *test = in+1;\n\t\t\t\tqbyte len = *m;\n\t\t\t\tconst qbyte *match = m+1;\n\t\t\t\tm += 1+len;\n\t\t\t\twhile (*test)\n\t\t\t\t{\t//don't let 'foo bar' through when 'foobar' is a bad word.\n\t\t\t\t\tif (whiteish(*test))\n\t\t\t\t\t{\n\t\t\t\t\t\ttest++;\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (tolower(*test) == *match)\n\t\t\t\t\t{\n\t\t\t\t\t\ttest++, match++;\n\t\t\t\t\t\tif (--len == 0)\n\t\t\t\t\t\t{\t//a match.\n\t\t\t\t\t\t\tif (*test && !whiteish(*test))\n\t\t\t\t\t\t\t\tbreak;\t//assassinate!\n\t\t\t\t\t\t\twhile (test > in)\n\t\t\t\t\t\t\t{\t//censor it.\n\t\t\t\t\t\t\t\t*outbuf = \"#*@$\"[(outbuf-ret)&3];\n\t\t\t\t\t\t\t\toutbuf++;\n\t\t\t\t\t\t\t\tin++;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tgoto restart; //double breaks suck\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\twhile (*in)\n\t\t{\n\t\t\tif (whiteish(*in))\n\t\t\t{\n\t\t\t\t*outbuf++ = *in++;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\t*outbuf++ = *in++;\n\t\t}\n\t}\n\t*outbuf++ = 0;\t//make sure its null terminated.\n\treturn ret;\n}\nqboolean TL_FilterObsceneCCStringInplace(conchar_t *in, conchar_t *end)\n{\t//FIXME: filters are meant to be utf-8, but our strings are not.\n\tqboolean obscene = false;\n//\tconchar_t *start = in;\n\tconchar_t *next;\n\tif (!filtermem)\n\t\tFilter_Reload_f();\nrestart:\n\twhile(in < end)\n\t{\n\t\tunsigned int c, cflags;\n\t\tnext = Font_Decode(in, &cflags, &c);\n\t\tc = towlower(c);\n\t\tif (c < 255 && filter[c])\n\t\t{\n\t\t\tqbyte *m = filter[c];\n\t\t\twhile (*m)\n\t\t\t{\t//for each word starting with this letter...\n\t\t\t\tconchar_t *test = next;\n\t\t\t\tqbyte len = *m;\n\t\t\t\tconst qbyte *match = m+1;\n\t\t\t\tint err;\n\t\t\t\tm += 1+len;\n\t\t\t\twhile(*match && test < end)\n\t\t\t\t{\t//don't let 'foo bar' through when 'foobar' is a bad word.\n\t\t\t\t\ttest = Font_Decode(test, &cflags, &c);\n\t\t\t\t\tif (whiteish(c))\n\t\t\t\t\t\tcontinue;\n\n\t\t\t\t\tif (towlower(c) == utf8_decode(&err, match, (char const**)&match))\n\t\t\t\t\t{\n\t\t\t\t\t\tif (--len == 0)\n\t\t\t\t\t\t{\t//a match.\n\n\t\t\t\t\t\t\t//peek the next and reject it if we're still mid word\n\t\t\t\t\t\t\tif (test < end)\n\t\t\t\t\t\t\t\tFont_Decode(test, &cflags, &c);\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\tc = 0;\n\t\t\t\t\t\t\tif (c && !whiteish(c))\n\t\t\t\t\t\t\t\tbreak;\t//assassinate!\n\n\t\t\t\t\t\t\t//okay, not mid-word, obuscate the swears.\n\t\t\t\t\t\t\twhile (test > in)\n\t\t\t\t\t\t\t{\t//censor it.\n\t\t\t\t\t\t\t\tif (*in & CON_LONGCHAR && !(*in & CON_RICHFORECOLOUR))\n\t\t\t\t\t\t\t\t\t*in = CON_LONGCHAR;\t//no other flags here.\n\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t//*in = \"#@*$\"[(in-start)&3] | CON_WHITEMASK;\n\t\t\t\t\t\t\t\t\t*in\t= 0x26a0 | CON_WHITEMASK | (*in&CON_HIDDEN);\n\t\t\t\t\t\t\t\t\tobscene = true;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tin++;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tgoto restart; //double breaks suck\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tfor(; next < end; next = Font_Decode(next, &cflags, &c))\n\t\t{\n\t\t\tif (whiteish(c))\n\t\t\t\tbreak;\n\t\t}\n\t\tin = next;\n\t}\n\treturn obscene;\n}\n"
  },
  {
    "path": "engine/common/translate.h",
    "content": "#ifndef TRANSLATE_H\n#define TRANSLATE_H\ntypedef const char* translation_t;\n\n#define MAX_LANGUAGES 64\n\nvoid TranslateInit(void);\n\nvoid SV_InitLanguages(void);\n\nstruct language_s\n{\n\tchar *name;\n\tstruct po_s *po;\n\tstruct po_s *po_qex;\n};\nextern struct language_s languages[MAX_LANGUAGES];\nextern int com_language;\nextern cvar_t language;\n#define langtext(t,l) PO_GetText(languages[l].po, t)\n#define localtext(t) PO_GetText(languages[com_language].po, t)\nint TL_FindLanguage(const char *lang);\n\nqboolean TL_FilterObsceneCCStringInplace(conchar_t *in, conchar_t *end);\n\n#endif\n"
  },
  {
    "path": "engine/common/ui_public.h",
    "content": "/*\n===========================================================================\nCopyright (C) 1999-2005 Id Software, Inc.\n\nThis file is part of Quake III Arena source code.\n\nQuake III Arena source code is free software; you can redistribute it\nand/or modify it under the terms of the GNU General Public License as\npublished by the Free Software Foundation; either version 2 of the License,\nor (at your option) any later version.\n\nQuake III Arena source code is distributed in the hope that it will be\nuseful, but WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Foobar; if not, write to the Free Software\nFoundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n===========================================================================\n*/\n\n\n//these structures are shared with the exe.\n\n#define UIMAX_SCOREBOARDNAME 16\n#define UIMAX_INFO_STRING EXTENDED_INFO_STRING\n\ntypedef struct {\n\tint userid;\n\tchar name[UIMAX_SCOREBOARDNAME];\t//for faster reading.\n\tfloat starttime;\n\tint frags;\n\tint ping;\n\tint pl;\n\n\tint topcolour;\n\tint bottomcolour;\n\n\tchar userinfo[UIMAX_INFO_STRING];\t//should this size be enforced?\n\t\t\t\t\t\t\t\t\t//you can get all sorts of stuff like names.\n} vmuiclientinfo_t;\n\n//useful for it's width/height. The others are a little pointless to be honest.\ntypedef struct {\n\tunsigned int width;\n\tunsigned int height;\n\tunsigned int bpp;\n\tunsigned int refreshrate;\t//quakeworld normally only draws 30 frames per second dontcha know?\n\tqboolean fullscreen;\t//oposite of windowed.\n\tchar renderername[256];\t\t//Human readable\n\n\tint vidbugs;\t//flags for the buggy implementations of opengl or whatever.\n} vidinfo_t;\n\n//is there any point to these?\nenum {\n\tVB_NOSCALE\t\t\t= 1<<0,\t//software rendering, incapable of scaling.\n\tVB_NOCOLOUR\t\t\t= 1<<1,\t//software rendering that doesn't allow belnding colours. (8 bit paletted)\n\tVB_NOCOLOURINTERP\t\t= 1<<2,\t//software rendering that supports a blending of colours, but not per vertex.\n\n\tVB_NOINTERPOLATEALPHA\t= 1<<3,\t//riva128\n\tVB_NOMODULATEALPHA\t= 1<<4,\t//ragepro\n\tVB_NOSRCTIMESDST\t\t= 1<<5,\t//permedia\n};\n\ntypedef enum {\n\tSID_Q2STATUSBAR\t\t= -4,\n\tSID_Q2LAYOUT\t\t= -3,\n\tSID_CENTERPRINTTEXT\t= -2,\n\tSID_SERVERNAME\t\t= -1,\n\t//q2's config strings come here.\n} stringid_e;\n\ntypedef enum {\n\tQ3CA_UNINITIALIZED,\n\tQ3CA_DISCONNECTED, \t// not talking to a server\n\tQ3CA_AUTHORIZING,\t\t// not used any more, was checking cd key \n\tQ3CA_CONNECTING,\t\t// sending request packets to the server\n\tQ3CA_CHALLENGING,\t\t// sending challenge packets to the server\n\tQ3CA_CONNECTED,\t\t// netchan_t established, getting gamestate\n\tQ3CA_LOADING,\t\t\t// only during cgame initialization, never during main loop\n\tQ3CA_PRIMED,\t\t\t// got gamestate, waiting for first frame\n\tQ3CA_ACTIVE,\t\t\t// game views should be displayed\n\tQ3CA_CINEMATIC\t\t// playing a cinematic or a static pic, not connected to a server\n} q3connstate_t;\ntypedef struct {\n\tq3connstate_t\t\tconnState;\n\tint\t\t\t\tconnectPacketCount;\n\tint\t\t\t\tclientNum;\n\tchar\t\t\tservername[MAX_STRING_CHARS];\n\tchar\t\t\tupdateInfoString[MAX_STRING_CHARS];\n\tchar\t\t\tmessageString[MAX_STRING_CHARS];\n} uiClientState_t;\n\ntypedef enum {\n\tUI_GETAPIVERSION = 0,\t// system reserved\n\n\tUI_INIT,\n//\tvoid\tUI_Init( void );\n\n\tUI_SHUTDOWN,\n//\tvoid\tUI_Shutdown( void );\n\n\tUI_KEY_EVENT,\n//\tvoid\tUI_KeyEvent( int key );\n\n\tUI_MOUSE_EVENT,\n//\tvoid\tUI_MouseEvent( int dx, int dy );\n\n\tUI_REFRESH,\n//\tvoid\tUI_Refresh( int time );\n\n\tUI_IS_FULLSCREEN,\n//\tqboolean UI_IsFullscreen( void );\n\n\tUI_SET_ACTIVE_MENU,\n//\tvoid\tUI_SetActiveMenu( uiMenuCommand_t menu );\n\n\tUI_CONSOLE_COMMAND,\n//\tqboolean UI_ConsoleCommand( int realTime );\n\n\tUI_DRAW_CONNECT_SCREEN,\n//\tvoid\tUI_DrawConnectScreen( qboolean overlay );\n\tUI_HASUNIQUECDKEY,\n// if !overlay, the background will be drawn, otherwise it will be\n// overlayed over whatever the cgame has drawn.\n// a GetClientState syscall will be made to get the current strings\n\n} uiExport_t;\n\ntypedef enum {\n\tUI_ERROR,\n\tUI_PRINT,\n\tUI_MILLISECONDS,\n\tUI_CVAR_SET,\n\tUI_CVAR_VARIABLEVALUE,\n\tUI_CVAR_VARIABLESTRINGBUFFER,\n\tUI_CVAR_SETVALUE,\n\tUI_CVAR_RESET,\n\tUI_CVAR_CREATE,\n\tUI_CVAR_INFOSTRINGBUFFER,\n\tUI_ARGC,//10\n\tUI_ARGV,\n\tUI_CMD_EXECUTETEXT,\n\tUI_FS_FOPENFILE,\n\tUI_FS_READ,\n\tUI_FS_WRITE,\n\tUI_FS_FCLOSEFILE,\n\tUI_FS_GETFILELIST,\n\tUI_R_REGISTERMODEL,\n\tUI_R_REGISTERSKIN,\n\tUI_R_REGISTERSHADERNOMIP,//20\n\tUI_R_CLEARSCENE,\n\tUI_R_ADDREFENTITYTOSCENE,\n\tUI_R_ADDPOLYTOSCENE,\n\tUI_R_ADDLIGHTTOSCENE,\n\tUI_R_RENDERSCENE,\n\tUI_R_SETCOLOR,\n\tUI_R_DRAWSTRETCHPIC,\n\tUI_UPDATESCREEN,\n\tUI_CM_LERPTAG,\n\tUI_CM_LOADMODEL,//30\n\tUI_S_REGISTERSOUND,\n\tUI_S_STARTLOCALSOUND,\n\tUI_KEY_KEYNUMTOSTRINGBUF,\n\tUI_KEY_GETBINDINGBUF,\n\tUI_KEY_SETBINDING,\n\tUI_KEY_ISDOWN,\n\tUI_KEY_GETOVERSTRIKEMODE,\n\tUI_KEY_SETOVERSTRIKEMODE,\n\tUI_KEY_CLEARSTATES,\n\tUI_KEY_GETCATCHER,//40\n\tUI_KEY_SETCATCHER,\n\tUI_GETCLIPBOARDDATA,\n\tUI_GETGLCONFIG,\n\tUI_GETCLIENTSTATE,\n\tUI_GETCONFIGSTRING,\n\tUI_LAN_GETPINGQUEUECOUNT,\n\tUI_LAN_CLEARPING,\n\tUI_LAN_GETPING,\n\tUI_LAN_GETPINGINFO,\n\tUI_CVAR_REGISTER,//50\n\tUI_CVAR_UPDATE,\n\tUI_MEMORY_REMAINING,\n\tUI_GET_CDKEY,\n\tUI_SET_CDKEY,\n\tUI_R_REGISTERFONT,\n\tUI_R_MODELBOUNDS,\n\tUI_PC_ADD_GLOBAL_DEFINE,\n\tUI_PC_LOAD_SOURCE,\n\tUI_PC_FREE_SOURCE,\n\tUI_PC_READ_TOKEN,//60\n\tUI_PC_SOURCE_FILE_AND_LINE,\n\tUI_S_STOPBACKGROUNDTRACK,\n\tUI_S_STARTBACKGROUNDTRACK,\n\tUI_REAL_TIME,\n\tUI_LAN_GETSERVERCOUNT,\n\tUI_LAN_GETSERVERADDRESSSTRING,\n\tUI_LAN_GETSERVERINFO,\n\tUI_LAN_MARKSERVERVISIBLE,\n\tUI_LAN_UPDATEVISIBLEPINGS,\n\tUI_LAN_RESETPINGS,//70\n\tUI_LAN_LOADCACHEDSERVERS,\n\tUI_LAN_SAVECACHEDSERVERS,\n\tUI_LAN_ADDSERVER,\n\tUI_LAN_REMOVESERVER,\n\tUI_CIN_PLAYCINEMATIC,\n\tUI_CIN_STOPCINEMATIC,\n\tUI_CIN_RUNCINEMATIC,\n\tUI_CIN_DRAWCINEMATIC,\n\tUI_CIN_SETEXTENTS,\n\tUI_R_REMAP_SHADER,//80\n\tUI_VERIFY_CDKEY,\n\tUI_LAN_SERVERSTATUS,\n\tUI_LAN_GETSERVERPING,\n\tUI_LAN_SERVERISVISIBLE,\n\tUI_LAN_COMPARESERVERS,\n\t// 1.32\n\tUI_FS_SEEK,\n\tUI_SET_PBCLSTATUS,//87\n\n\tUI_MEMSET = 100,\n\tUI_MEMCPY,\n\tUI_STRNCPY,\n\tUI_SIN,\n\tUI_COS,\n\tUI_ATAN2,\n\tUI_SQRT,\n\tUI_FLOOR,\n\tUI_CEIL,\n/*\n\tUI_CACHE_PIC\t\t= 500,\n\tUI_PICFROMWAD\t\t= 501,\n\tUI_GETPLAYERINFO\t= 502,\n\tUI_GETSTAT\t\t\t= 503,\n\tUI_GETVIDINFO\t\t= 504,\n\tUI_GET_STRING\t\t= 510,\n*/\n} uiImport_t;\n"
  },
  {
    "path": "engine/common/vm.h",
    "content": "#ifndef _VM_H\n#define _VM_H\n\n#ifdef _WIN32\n\t#define EXPORT_FN __cdecl\n#else\n\t#define EXPORT_FN\n#endif\n\ntypedef qintptr_t (EXPORT_FN *sys_calldll_t) (qintptr_t arg, ...);\ntypedef int (*sys_callqvm_t) (void *offset, quintptr_t mask, int fn, const int *arg);\n\ntypedef struct vm_s vm_t;\n\n// for syscall users\n#define VM_LONG(x)\t\t(*(int*)&(x))\t//note: on 64bit platforms, the later bits can contain junk\n#define VM_FLOAT(x)\t\t(*(float*)&(x))\t//note: on 64bit platforms, the later bits can contain junk\n#define VM_POINTER(x)\t((x)?(void*)((char *)offset+((x)%mask)):NULL)\n#define VM_OOB(p,l)\t\t(p + l >= mask || VM_POINTER(p) < offset)\n// ------------------------- * interface * -------------------------\n\nvoid VM_PrintInfo(vm_t *vm);\nvm_t *VM_CreateBuiltin(const char *name, sys_calldll_t syscalldll, qintptr_t (*init)(qintptr_t *args));\nvm_t *VM_Create(const char *dllname, sys_calldll_t syscalldll, const char *qvmname, sys_callqvm_t syscallqvm);\nconst char *VM_GetFilename(vm_t *vm);\nvoid VM_Destroy(vm_t *vm);\n//qboolean VM_Restart(vm_t *vm);\nqintptr_t VARGS VM_Call(vm_t *vm, qintptr_t instruction, ...);\nqboolean VM_NonNative(vm_t *vm);\nvoid *VM_MemoryBase(vm_t *vm);\nquintptr_t VM_MemoryMask(vm_t *vm);\n\n#define VM_FS_READ 0\n#define VM_FS_WRITE 1\n#define VM_FS_APPEND 2\n#define VM_FS_APPEND_SYNC 3\t//I don't know, don't ask me. look at q3 source\nqofs_t VM_fopen (const char *name, int *handle, int fmode, int owner);\nint VM_FRead (char *dest, int quantity, int fnum, int owner);\nint VM_FWrite (const char *dest, int quantity, int fnum, int owner);\nqboolean VM_FSeek (int fnum, qofs_t offset, int seektype, int owner);\nqofs_t VM_FTell (int fnum, int owner);\nvoid VM_fclose (int fnum, int owner);\nvoid VM_fcloseall (int owner);\nint VM_GetFileList(const char *path, const char *ext, char *output, int buffersize);\n\ntypedef struct {\n\tint\t\t\thandle;\n\tint\t\t\tmodificationCount;\n\tfloat\t\tvalue;\n\tint\t\t\tinteger;\n\tchar\t\tstring[256];\n} q3vmcvar_t;\nint VMQ3_Cvar_Register(q3vmcvar_t *v, char *name, char *defval, int flags);\nint VMQ3_Cvar_Update(q3vmcvar_t *v);\n\ntypedef struct {\n\tint tm_sec;\n\tint tm_min;\n\tint tm_hour;\n\tint tm_mday;\n\tint tm_mon;\n\tint tm_year;\n\tint tm_wday;\n\tint tm_yday;\n\tint tm_isdst;\n} q3time_t;\nqint64_t Q3VM_GetRealtime(q3time_t *qtime);\n\n#endif\n"
  },
  {
    "path": "engine/common/world.h",
    "content": "/*\nCopyright (C) 1996-1997 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n*/\n// world.h\n\n#include \"../client/quakedef.h\"\n\ntypedef struct plane_s\n{\n\tvec3_t\tnormal;\n\tfloat\tdist;\n} plane_t;\n\ntypedef struct csurface_s\n{\n\tchar\t\tname[16];\n\tint\t\t\tflags;\n\tint\t\t\tvalue;\n} q2csurface_t;\n\ntypedef struct cplane_s\n{\n\tvec3_t\tnormal;\n\tfloat\tdist;\n\tqbyte\ttype;\t\t\t// for fast side tests\n\tqbyte\tsignbits;\t\t// signx + (signy<<1) + (signz<<1)\n\tqbyte\tpad[2];\n} cplane_t;\n\n/*\ntypedef struct trace_s\n{\n\tqboolean\tallsolid;\t// if true, plane is not valid\n\tqboolean\tstartsolid;\t// if true, the initial point was in a solid area\n\tqboolean\tinopen, inwater;\n\tfloat\tfraction;\t\t// time completed, 1.0 = didn't hit anything\n\tvec3_t\tendpos;\t\t\t// final position\n\tplane_t\tplane;\t\t\t// surface normal at impact\n\tedict_t\t*ent;\t\t\t// entity the surface is on\n} trace_t;\n*/\n\n//these two structures must match (except for extra qw members)\ntypedef struct trace_s\n{\n//DON'T ADD ANYTHING BETWEEN THIS LINE\n//q2 game dll code will memcpy the lot from trace_t to q2trace_t.\n\tqboolean\t\t\tallsolid;\t// if true, plane is not valid\n\tqboolean\t\t\tstartsolid;\t// if true, the initial point was in a solid area\n\tfloat\t\t\t\tfraction;\t// time completed, 1.0 = didn't hit anything (nudged closer to the start point to cover precision issues)\n\tvec3_t\t\t\t\tendpos;\t\t// final position\n\tcplane_t\t\t\tplane;\t\t// surface normal at impact\n\tconst q2csurface_t\t*surface;\t// q2-compat surface hit\n\tunsigned int\t\tcontents;\t// contents on other side of surface hit\n\tvoid\t\t\t\t*ent;\t\t// not set by CM_*() functions\n//AND THIS LINE\n\tint\t\t\t\t\tentnum;\n\n\tqboolean\t\t\tinopen, inwater;\n\tfloat\t\t\t\ttruefraction;\t//can be negative, also has floating point precision issues, etc.\n\tint\t\t\t\t\tbrush_id;\n\tint\t\t\t\t\tbrush_face;\n\tint\t\t\t\t\tsurface_id;\n\tint\t\t\t\t\ttriangle_id;\n\tint\t\t\t\t\tbone_id;\n} trace_t;\n\ntypedef struct q2trace_s\n{\n\tqboolean\tallsolid;\t// if true, plane is not valid\n\tqboolean\tstartsolid;\t// if true, the initial point was in a solid area\n\tfloat\t\tfraction;\t// time completed, 1.0 = didn't hit anything\n\tvec3_t\t\tendpos;\t\t// final position\n\tcplane_t\tplane;\t\t// surface normal at impact\n\tconst q2csurface_t\t*surface;\t// surface hit\n\tint\t\t\tcontents;\t// contents on other side of surface hit\n\tvoid\t*ent;\t\t// not set by CM_*() functions\n} q2trace_t;\n\n\n// edict->flags\n#define\tFL_FLY\t\t\t\t\t(1<<0)\n#define\tFL_SWIM\t\t\t\t\t(1<<1)\n//#define\tFL_GLIMPSE\t\t\t\t(1<<2)\n#define\tFL_CLIENT\t\t\t\t(1<<3)\n#define\tFL_INWATER\t\t\t\t(1<<4)\n#define\tFL_MONSTER\t\t\t\t(1<<5)\n#define\tFL_GODMODE\t\t\t\t(1<<6)\n#define\tFL_NOTARGET\t\t\t\t(1<<7)\n#define\tFL_ITEM\t\t\t\t\t(1<<8)\n#define\tFL_ONGROUND\t\t\t\t(1<<9)\n#define\tFL_PARTIALGROUND\t\t(1<<10)\t// not all corners are valid\n#define\tFL_WATERJUMP\t\t\t(1<<11)\t// player jumping out of water\n#define\tFL_JUMPRELEASED\t\t\t(1<<12)\n//#define FLRE_ISBOT\t\t\t(1<<13)\n#define FL_FINDABLE_NONSOLID\t(1<<14)\t//a cpqwsv feature\n#define FL_MOVECHAIN_ANGLE\t\t(1<<15) // hexen2 - when in a move chain, will update the angle\n#define FLQW_LAGGEDMOVE\t\t\t(1<<16)\n#define FLH2_HUNTFACE\t\t\t(1<<16)\n#define FLH2_NOZ\t\t\t\t(1<<17)\n\t\t\t\t\t\t\t\t//18\n\t\t\t\t\t\t\t\t//19\n#define\tFL_HUBSAVERESET\t\t\t(1<<20) //hexen2, ent is reverted to original state on map changes.\n#define FL_CLASS_DEPENDENT\t\t(1<<21)\t//hexen2\n\t\t\t\t\t\t\t\t//22\n\t\t\t\t\t\t\t\t//23\n\n\n\n#define\tMOVE_NORMAL\t\t0\n#define\tMOVE_NOMONSTERS\t(1<<0)\n#define\tMOVE_MISSILE\t(1<<1)\n#ifdef HAVE_LEGACY\n#define MOVE_WORLDONLY\t(MOVE_NOMONSTERS|MOVE_MISSILE) //use MOVE_OTHERONLY instead\n#endif\n#define\tMOVE_HITMODEL\t(1<<2)\n#define MOVE_RESERVED\t(1<<3)\t\t\t//so we are less likly to get into tricky situations when we want to steal another future DP extension.\n#define MOVE_TRIGGERS\t(1<<4)\t\t\t//triggers must be marked with FINDABLE_NONSOLID\t(an alternative to solid-corpse)\n#define MOVE_EVERYTHING\t(1<<5)\t\t\t//can return triggers and non-solid items if they're marked with FINDABLE_NONSOLID (works even if the items are not properly linked)\n#define MOVE_LAGGED\t\t(1<<6)\t\t\t//trace touches current last-known-state, instead of actual ents (just affects players for now)\n#define MOVE_ENTCHAIN\t(1<<7)\t\t\t//chain of impacted ents, otherwise result shows only world\n#define MOVE_OTHERONLY\t(1<<8)\t\t\t//test the trace against a single entity, ignoring non-solid/owner/etc flags (but respecting contents).\n#define MOVE_IGNOREHULL\t(1u<<31)\t//used on tracelines etc to simplify the code a little\n\n#ifdef USEAREAGRID\n//this macro does it in two steps to avoid float precision issues.\n//it also ensures that it will return at least one grid section.\n#define CALCAREAGRIDBOUNDS(w,min,max) \\\n\t\tming[0] = floor(((min)[0]+(w)->gridbias[0]) / (w)->gridscale[0]);\t\\\n\t\tming[1] = floor(((min)[1]+(w)->gridbias[1]) / (w)->gridscale[1]);\t\\\n\t\tmaxg[0] = floor(((max)[0]+(w)->gridbias[0]) / (w)->gridscale[0]);\t\\\n\t\tmaxg[1] = floor(((max)[1]+(w)->gridbias[1]) / (w)->gridscale[1]);\t\\\n\t\tming[0] = bound(0, ming[0], (w)->gridsize[0]-1);\t\\\n\t\tming[1] = bound(0, ming[1], (w)->gridsize[1]-1);\t\\\n\t\tmaxg[0] = bound(ming[0], maxg[0], (w)->gridsize[0]-1)+1;\t\\\n\t\tmaxg[1] = bound(ming[1], maxg[1], (w)->gridsize[1]-1)+1;\n#else\n#define\tEDICT_FROM_AREA(l) STRUCT_FROM_LINK(l,wedict_t,area)\n#endif\n\n#if defined(Q2SERVER) || !defined(USEAREAGRID)\n//q2 game code embeds a link_t struct inside the public edicts.\n//this is why we can't have nice things.\n\n//a binary tree. ents straddling a node are inserted into the parent.\n//this is a problem when your root note passes through '0 0 0' and you have shitty mods with 2000 such ents.\ntypedef struct areanode_s\n{\n\tint\t\taxis;\t\t// -1 = leaf node\n\tfloat\tdist;\n\tstruct areanode_s\t*children[2];\n\tlink_t\tedicts;\n} areanode_t;\n#endif\n\ntypedef struct wedict_s wedict_t;\n#define PROG_TO_WEDICT (wedict_t*)PROG_TO_EDICT\n#define WEDICT_NUM_UB (wedict_t *)EDICT_NUM_UB\t//ent number isn't bounded\n#define WEDICT_NUM_PB (wedict_t *)EDICT_NUM_PB\t//pre-bound\n#define G_WEDICT (wedict_t *)G_EDICT\n\ntypedef struct\n{\n\tqboolean present;\n\tvec3_t origin;\n\tvec3_t angles;\n} laggedentinfo_t;\n\n#ifdef USERBE\ntypedef struct\n{\n\tvoid (QDECL *End)(struct world_s *world);\n\tvoid (QDECL *RemoveJointFromEntity)(struct world_s *world, wedict_t *ed);\n\tvoid (QDECL *RemoveFromEntity)(struct world_s *world, wedict_t *ed);\n\tqboolean (QDECL *RagMatrixToBody)(rbebody_t *bodyptr, float *mat);\n\tqboolean (QDECL *RagCreateBody)(struct world_s *world, rbebody_t *bodyptr, rbebodyinfo_t *bodyinfo, float *mat, wedict_t *ent);\n\tvoid (QDECL *RagMatrixFromJoint)(rbejoint_t *joint, rbejointinfo_t *info, float *mat);\n\tvoid (QDECL *RagMatrixFromBody)(struct world_s *world, rbebody_t *bodyptr, float *mat);\n\tvoid (QDECL *RagEnableJoint)(rbejoint_t *joint, qboolean enabled);\n\tvoid (QDECL *RagCreateJoint)(struct world_s *world, rbejoint_t *joint, rbejointinfo_t *info, rbebody_t *body1, rbebody_t *body2, vec3_t aaa2[3]);\n\tvoid (QDECL *RagDestroyBody)(struct world_s *world, rbebody_t *bodyptr);\n\tvoid (QDECL *RagDestroyJoint)(struct world_s *world, rbejoint_t *joint);\n\tvoid (QDECL *RunFrame)(struct world_s *world, double frametime, double gravity);\n\tvoid (QDECL *PushCommand)(struct world_s *world, rbecommandqueue_t *cmd);\n//\tvoid (QDECL *ExpandBodyAABB)(struct world_s *world, rbebody_t *bodyptr, float *mins, float *maxs);\t//expands an aabb to include the size of the body.\n\tvoid (QDECL *Trace) (struct world_s *world, wedict_t *ed, vec3_t start, vec3_t end, trace_t *trace);\n} rigidbodyengine_t;\n#endif\n\nstruct world_s\n{\n\tvoid (QDECL *Event_Touch)(struct world_s *w, wedict_t *s, wedict_t *o, trace_t *trace);\n\tvoid (QDECL *Event_Think)(struct world_s *w, wedict_t *s);\n\tvoid (QDECL *Event_Sound) (float *origin, wedict_t *entity, int channel, const char *sample, int volume, float attenuation, float pitchadj, float timeoffset, unsigned int flags);\n\tqboolean (QDECL *Event_ContentsTransition) (struct world_s *w, wedict_t *ent, int oldwatertype, int newwatertype);\n\tmodel_t *(QDECL *Get_CModel)(struct world_s *w, int modelindex);\n\tvoid (QDECL *Get_FrameState)(struct world_s *w, wedict_t *s, framestate_t *fstate);\n\tvoid (QDECL *Event_Backdate)(struct world_s *w, wedict_t *s, float timestamp);\t//called for MOVE_LAGGED+MOVE_HITMODEL traces\n\n\tunsigned int\tkeydestmask;\t//menu:kdm_menu, csqc:kdm_game, server:0\n\tunsigned int\tmax_edicts;\t//limiting factor... 1024 fields*4*MAX_EDICTS == a heck of a lot.\n\tunsigned int\tnum_edicts;\t\t\t// increases towards MAX_EDICTS\n/*FTE_DEPRECATED*/\tunsigned int\tedict_size; //still used in copyentity\n\twedict_t\t\t*edicts;\t\t\t// can NOT be array indexed.\n\tstruct pubprogfuncs_s *progs;\n\tqboolean\t\tusesolidcorpse;\t//to disable SOLID_CORPSE when running hexen2 due to conflict.\n\tmodel_t\t\t\t*worldmodel;\n\tunsigned int\tspawncount;\t//number of times it got restarted, so we can stop events from happening after vm restarts\n\tqboolean\t\tremasterlogic;\t//workarounds needed\n\n#ifdef USEAREAGRID\n\tvec2_t\t\t\tgridbias;\n\tvec2_t\t\t\tgridscale;\n\tsize_t\t\t\tgridsize[2];\n\tareagridlink_t\t*gridareas;\t\t//[gridsize[0]*gridsize[1]]\n\tareagridlink_t\tjumboarea;\t\t//node containing ents too large to fit.\n\tareagridlink_t\tportallist;\n#endif\n\n\tdouble\t\tphysicstime;\t\t// the last time global physics were run\n\tunsigned int    framenum;\n\tint\t\t\tlastcheck;\t\t\t// used by PF_checkclient\n\tdouble\t\tlastchecktime;\t\t// for monster ai\n\tqbyte\t\t*lastcheckpvs;\t\t// for monster ai\n\n\t/*antilag*/\n\tfloat\tlagentsfrac;\n\tfloat\tlagentstime;\n\tlaggedentinfo_t *lagents;\n\tunsigned int maxlagents;\n\n\t/*qc globals*/\n\tstruct {\n\t\tpint_t     *self;\n\t\tpint_t     *other;\n\t\tpint_t     *newmis;\n\t\tpvec_t\t*time;\n\t\tpvec_t\t*frametime;\n\t\tpvec_t\t*force_retouch;\n\t\tpvec_t\t*physics_mode;\n\t\tpvec_t\t*v_forward;\n\t\tpvec_t\t*v_right;\n\t\tpvec_t\t*v_up;\n\t\tpvec_t\t*defaultgravitydir;\n\n\t\t//used by menu+csqc.\n\t\tpvec_t *drawfont;\n\t\tpvec_t *drawfontscale;\n\t} g;\n\tstruct qcstate_s *qcthreads;\n\n#ifdef USERBE\n\tqboolean rbe_hasphysicsents;\n\trigidbodyengine_t *rbe;\n#endif\n\n#if defined(Q2SERVER) || !defined(USEAREAGRID)\n\tareanode_t\t\t*areanodes;\n\tint\t\t\t\tareanodedepth;\n\tint\t\t\t\tnumareanodes;\n#ifndef USEAREAGRID\n\tareanode_t\t\tportallist;\n#endif\n#endif\n\n#ifdef ENGINE_ROUTING\n\tvoid *waypoints;\n#endif\n};\ntypedef struct world_s world_t;\n\nvoid PF_Common_RegisterCvars(void);\n\n\n\n\nqboolean QDECL World_RegisterPhysicsEngine(const char *enginename, void(QDECL*World_Bullet_Start)(world_t*world));\nvoid QDECL World_UnregisterPhysicsEngine(const char *enginename);\nqboolean QDECL World_GenerateCollisionMesh(world_t *world, model_t *mod, wedict_t *ed, vec3_t geomcenter);\nvoid QDECL World_ReleaseCollisionMesh(wedict_t *ed);\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nvoid World_Destroy (world_t *w);\nvoid World_RBE_Start(world_t *world);\nvoid World_RBE_Shutdown(world_t *world);\n\n\nvoid World_ClearWorld (world_t *w, qboolean relink);\n// called after the world model has been loaded, before linking any entities\nvoid World_ClearWorld_Nodes (world_t *w, qboolean relink); //legacy code for q2 compat.\n\nvoid World_UnlinkEdict (wedict_t *ent);\n// call before removing an entity, and before trying to move one,\n// so it doesn't clip against itself\n// flags ent->v.modified\n\nvoid QDECL World_LinkEdict (world_t *w, wedict_t *ent, qboolean touch_triggers);\n// Needs to be called any time an entity changes origin, mins, maxs, or solid\n// flags ent->v.modified\n// sets ent->v.absmin and ent->v.absmax\n// if touchtriggers, calls prog functions for the intersected triggers\n\n#define AREA_ALL 0\n#define AREA_SOLID 1\n#define AREA_TRIGGER 2\nextern int World_AreaEdicts (world_t *w, vec3_t mins, vec3_t maxs, wedict_t **list, int maxcount, int areatype);\n\n#ifdef USEAREAGRID\nvoid World_TouchAllLinks (world_t *w, wedict_t *ent);\nextern size_t areagridsequence;\n#else\nvoid World_TouchLinks (world_t *w, wedict_t *ent, areanode_t *node);\n#define World_TouchAllLinks(w,e) World_TouchLinks(w,e,(w)->areanodes)\n#endif\n\nint World_PointContentsWorldOnly (world_t *w, vec3_t p);\nint World_PointContentsAllBSPs (world_t *w, vec3_t p);\n// returns the CONTENTS_* value from the world at the given point.\n// does not check any entities at all\n\nwedict_t\t*World_TestEntityPosition (world_t *w, wedict_t *ent);\n\nqboolean World_TransformedTrace (struct model_s *model, int hulloverride, framestate_t *framestate, vec3_t start, vec3_t end, vec3_t mins, vec3_t maxs, qboolean capsule, struct trace_s *trace, vec3_t origin, vec3_t angles, unsigned int hitcontentsmask);\n\n/*\n World_Move:\n mins and maxs are reletive\n\n if the entire move stays in a solid volume, trace.allsolid will be set\n\n if the starting point is in a solid, it will be allowed to move out\n to an open area\n\n nomonsters is used for line of sight or edge testing, where mosnters\n shouldn't be considered solid objects\n\n passedict is explicitly excluded from clipping checks (normally NULL)\n*/\ntrace_t World_Move (world_t *w, vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, int type, wedict_t *passedict);\n\n\n#ifdef Q2SERVER\ntypedef struct q2edict_s q2edict_t;\n\nvoid VARGS WorldQ2_LinkEdict(world_t *w, q2edict_t *ent);\nvoid VARGS WorldQ2_UnlinkEdict(world_t *w, q2edict_t *ent);\nint VARGS WorldQ2_AreaEdicts (world_t *w, const vec3_t mins, const vec3_t maxs, q2edict_t **list,\n\tint maxcount, int areatype);\ntrace_t WorldQ2_Move (world_t *w, vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, int hitcontentsmask, q2edict_t *passedict);\n#endif\n\n\n/*sv_move.c*/\n#if defined(CSQC_DAT) || !defined(CLIENTONLY)\nqboolean World_CheckBottom (world_t *world, wedict_t *ent, vec3_t up);\nqboolean World_movestep (world_t *world, wedict_t *ent, vec3_t move, vec3_t axis[3], qboolean relink, qboolean noenemy, void (*set_move_trace)(pubprogfuncs_t *inst, trace_t *trace));\nqboolean World_MoveToGoal (world_t *world, wedict_t *ent, float dist);\nqboolean World_GetEntGravityAxis(wedict_t *ent, vec3_t axis[3]);\n#endif\n\n//\n// sv_phys.c\n//\nvoid WPhys_Init(void);\nvoid World_Physics_Frame(world_t *w);\nvoid SV_SetMoveVars(void);\nvoid WPhys_RunNewmis (world_t *w);\nqboolean SV_Physics (void);\nvoid WPhys_CheckVelocity (world_t *w, wedict_t *ent);\ntrace_t WPhys_Trace_Toss (world_t *w, wedict_t *ent, wedict_t *ignore);\nvoid SV_ProgStartFrame (void);\nvoid WPhys_RunEntity (world_t *w, wedict_t *ent);\nqboolean WPhys_RunThink (world_t *w, wedict_t *ent);\n"
  },
  {
    "path": "engine/common/zone.c",
    "content": "/*\nCopyright (C) 1996-1997 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  \n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n*/\n// Z_zone.c\n\n#include \"quakedef.h\"\n#ifdef _WIN32\n#include \"winquake.h\"\n#endif\n#ifdef __GLIBC__\n#include <malloc.h>\n#endif\n\n#define NOZONE\n\n#ifdef _DEBUG\n//#define MEMDEBUG\t8192 //Debugging adds sentinels (the number is the size - I have the ram) \n#endif\n\n//must be multiple of 4.\n//#define TEMPDEBUG 4\n//#define ZONEDEBUG 4\n//#define HUNKDEBUG 4\n\n//these need to be defined because it makes some bits of code simpler\n#ifndef HUNKDEBUG\n#define HUNKDEBUG 0\n#endif\n#ifndef ZONEDEBUG\n#define ZONEDEBUG 0\n#endif\n#ifndef TEMPDEBUG\n#define TEMPDEBUG 0\n#endif\n\n#if ZONEDEBUG>0 || HUNKDEBUG>0 || TEMPDEBUG>0\nqbyte sentinelkey;\n#endif\n\n#define TAGLESS 1\n\nsize_t zmemtotal;\nsize_t zmemdelta;\n\ntypedef struct memheader_s {\n\tsize_t size;\n\tint tag;\n} memheader_t;\n\ntypedef struct zone_s {\n\tstruct zone_s *next;\n\tstruct zone_s *pvdn; // down if first, previous if not\n\tmemheader_t mh;\n} zone_t;\nzone_t *zone_head;\n#ifdef MULTITHREAD\nvoid *zonelock;\n#endif\n\n#if 0\nstatic void Z_DumpTree(void)\n{\n\tzone_t *zone;\n\tzone_t *nextlist;\n\tzone_t *t;\n\tzone_t *prev;\n\n\tzone = zone_head;\n\twhile(zone)\n\t{\n\t\tnextlist = zone->pvdn;\n\n\t\tfprintf(stderr, \" +-+ %016x (tag: %08x)\\n\", zone, zone->mh.tag);\n\n\t\tprev = zone;\n\t\tt = zone->next;\n\t\twhile(t)\n\t\t{\n\t\t\tif (t->pvdn != prev)\n\t\t\t\tfprintf(stderr, \"Previous link failure\\n\");\n\n\t\t\tprev = t;\n\t\t\tt = t->next;\n\t\t}\n\n\t\twhile(zone)\n\t\t{\n\t\t\tfprintf(stderr, \"   +-- %016x\\n\", zone);\n\n\t\t\tzone = zone->next;\n\t\t}\n\n\t\tzone = nextlist;\n\t}\n}\n#endif\n\nvoid *Z_TagMalloc(size_t size, int tag)\n{\n\tzone_t *zone;\n\n\tzone = (zone_t *)malloc(size + sizeof(zone_t));\n\tif (!zone)\n\t\tSys_Error(\"Z_Malloc: Failed on allocation of %\"PRIuSIZE\" bytes\", size);\n\tQ_memset(zone, 0, size + sizeof(zone_t));\n\tzone->mh.tag = tag;\n\tzone->mh.size = size;\n\n#ifdef MULTITHREAD\n\tif (zonelock)\n\t\tSys_LockMutex(zonelock);\n#endif\n\n#if 0\n\tfprintf(stderr, \"Before alloc:\\n\");\n\tZ_DumpTree();\n\tfprintf(stderr, \"\\n\");\n#endif\n\n\tif (zone_head == NULL)\n\t\tzone_head = zone;\n\telse\n\t{\n\t\tzone_t *s = zone_head;\n\n\t\twhile (s && s->mh.tag != tag)\n\t\t\ts = s->pvdn;\n\n\t\tif (s)\n\t\t{ // tag match\n\t\t\tzone->next = s->next;\n\t\t\tif (s->next)\n\t\t\t\ts->next->pvdn = zone;\n\t\t\tzone->pvdn = s;\n\t\t\ts->next = zone;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tzone->pvdn = zone_head;\n\t\t//\tif (s->next)\n\t\t//\t\ts->next->pvdn = zone;\n\t\t\tzone_head = zone;\n\t\t}\n\t}\n\n#if 0\n\tfprintf(stderr, \"After alloc:\\n\");\n\tZ_DumpTree();\n\tfprintf(stderr, \"\\n\");\n#endif\n\n#ifdef MULTITHREAD\n\tif (zonelock)\n\t\tSys_UnlockMutex(zonelock);\n#endif\n\n\treturn (void *)(zone + 1);\n}\n\n#ifdef USE_MSVCRT_DEBUG\nvoid *ZF_MallocNamed(int size, char *file, int line)\n{\n\treturn _calloc_dbg(size, 1, _NORMAL_BLOCK, file, line);\n}\nvoid *Z_MallocNamed(int size, char *file, int line)\n{\n\tvoid *mem = ZF_MallocNamed(size, file, line);\n\tif (!mem)\n\t\tSys_Error(\"Z_Malloc: Failed on allocation of %i bytes\", size);\n\n\treturn mem;\n}\n#else\nvoid *ZF_Malloc(size_t size)\n{\n#ifdef ANDROID\n\tvoid *ret = NULL;\n\t//android is linux, but not gnu and thus not posix...\n\tret = memalign(max(sizeof(float)*4, sizeof(void*)), size);\n\tif (ret)\n\t\tmemset(ret, 0, size);\n\treturn ret;\n#elif defined(__linux__)\n\tvoid *ret = NULL;\n\n\tif (!posix_memalign(&ret, max(sizeof(float)*4, sizeof(void*)), size))\n\t\tmemset(ret, 0, size);\n\treturn ret;\n#else\n\treturn calloc(size, 1);\n#endif\n}\nvoid *Z_Malloc(size_t size)\n{\n\tvoid *mem = ZF_Malloc(size);\n\tif (!mem)\n\t\tSys_Error(\"Z_Malloc: Failed on allocation of %\"PRIuSIZE\" bytes\", size);\n\n\treturn mem;\n}\n#endif\n\nchar\t*Z_StrDupf(const char *format, ...)\n{\n\tva_list\t\targptr;\n\tchar\t\t*string;\n\tint n;\n\n#if defined(_MSC_VER) || defined(_WIN32)\n\tint try;\n\t//msvc is shitty shit shit and doesn't even do c99. sadly mingw uses the same libraries.\n\ttry = 256;\n\tfor(;;)\n\t{\n\t\tstring = Z_Malloc(try+1);\n\t\tva_start (argptr, format);\n\t\tn = _vsnprintf (string,try, format,argptr);\n\t\tva_end (argptr);\n\t\tif (n >= 0 && n < try)\n\t\t\tbreak;\n\t\tZ_Free(string);\n\t\ttry *= 2;\n\t}\n\tstring[n] = 0;\n#else\n\tva_start (argptr, format);\n\tn = vsnprintf (NULL,0, format,argptr);\n\tva_end (argptr);\n\tif (n < 0)\n\t\tn = 1024;\t//windows bullshit. whatever. good luck with that.\n\n\tstring = Z_Malloc(n+1);\n\tva_start (argptr, format);\n\tvsnprintf (string,n+1, format,argptr);\n\tva_end (argptr);\n\tstring[n] = 0;\n#endif\n\n\treturn string;\n}\nvoid Z_StrCatLen(char **ptr, const char *append, size_t newlen)\n{\n\tsize_t oldlen = *ptr?strlen(*ptr):0;\n\tchar *newptr = BZ_Malloc(oldlen+newlen+1);\n\tmemcpy(newptr, *ptr, oldlen);\n\tmemcpy(newptr+oldlen, append, newlen);\n\tnewptr[oldlen+newlen] = 0;\n\tBZ_Free(*ptr);\n\t*ptr = newptr;\n}\nvoid Z_StrCat(char **ptr, const char *append)\n{\n\tsize_t oldlen = *ptr?strlen(*ptr):0;\n\tsize_t newlen = strlen(append);\n\tchar *newptr = BZ_Malloc(oldlen+newlen+1);\n\tmemcpy(newptr, *ptr, oldlen);\n\tmemcpy(newptr+oldlen, append, newlen);\n\tnewptr[oldlen+newlen] = 0;\n\tBZ_Free(*ptr);\n\t*ptr = newptr;\n}\n\nvoid VARGS Z_TagFree(void *mem)\n{\n\tzone_t *zone = ((zone_t *)mem) - 1;\n\n#if 0\n\tfprintf(stderr, \"Before free:\\n\");\n\tZ_DumpTree();\n\tfprintf(stderr, \"\\n\");\n#endif\n\n#ifdef MULTITHREAD\n\tif (zonelock)\n\t\tSys_LockMutex(zonelock);\n#endif\n\tif (zone->next)\n\t\tzone->next->pvdn = zone->pvdn;\n\n\tif (zone->pvdn && zone->pvdn->mh.tag == zone->mh.tag)\n\t\tzone->pvdn->next = zone->next;\n\telse\n\t{ // zone is first entry in a tag list \n\t\tzone_t *s = zone_head;\n\n\t\tif (zone != s)\n\t\t{ // traverse and update down list\n\t\t\twhile (s->pvdn != zone) \n\t\t\t\ts = s->pvdn;\n\n\t\t\tif (zone->next)\n\t\t\t\ts->pvdn = zone->next;\n\t\t\telse\n\t\t\t\ts->pvdn = zone->pvdn;\n\t\t}\n\t}\n\n\tif (zone == zone_head)\n\t{ // freeing head node so update head pointer\n\t\tif (zone->next) // move to next, pvdn should be maintained properly\n\t\t\tzone_head = zone->next;\n\t\telse // no more entries with this tag so move head down\n\t\t\tzone_head = zone->pvdn;\n\t}\n\n#if 0\n\tfprintf(stderr, \"After free:\\n\");\n\tZ_DumpTree();\n\tfprintf(stderr, \"\\n\");\n#endif\n\n#ifdef MULTITHREAD\n\tif (zonelock)\n\t\tSys_UnlockMutex(zonelock);\n#endif\n\n\tfree(zone);\n}\n\nvoid VARGS Z_Free(void *mem)\n{\n\tfree(mem);\n}\n\nvoid VARGS Z_FreeTags(int tag)\n{\n\tzone_t *taglist;\n\tzone_t *t;\n\n#ifdef MULTITHREAD\n\tif (zonelock)\n\t\tSys_LockMutex(zonelock);\n#endif\n\tif (zone_head)\n\t{\n\t\tif (zone_head->mh.tag == tag)\n\t\t{ // just pull off the head\n\t\t\ttaglist = zone_head;\n\t\t\tzone_head = zone_head->pvdn;\n\t\t}\n\t\telse\n\t\t{ // search for tag list and isolate it\n\t\t\tzone_t *z;\n\t\t\tz = zone_head;\n\t\t\twhile (z->pvdn != NULL && z->pvdn->mh.tag != tag)\n\t\t\t\tz = z->pvdn;\n\n\t\t\tif (z->pvdn == NULL)\n\t\t\t\ttaglist = NULL;\n\t\t\telse\n\t\t\t{\n\t\t\t\ttaglist = z->pvdn;\n\t\t\t\tz->pvdn = z->pvdn->pvdn;\n\t\t\t}\n\t\t}\n\t}\n\telse\n\t\ttaglist = NULL;\n#ifdef MULTITHREAD\n\tif (zonelock)\n\t\tSys_UnlockMutex(zonelock);\n#endif\n\n\t// actually free list\n\twhile (taglist != NULL)\n\t{\n\t\tt = taglist->next;\n\t\tfree(taglist);\n\t\ttaglist = t;\n\t}\n}\n\n//close enough\n#ifndef SIZE_MAX\n\t#define SIZE_MAX ((size_t)-1)\n#endif\n\n#ifdef USE_MSVCRT_DEBUG\nqboolean ZF_ReallocElementsNamed(void **ptr, size_t *elements, size_t newelements, size_t elementsize, const char *file, int line)\n#else\nqboolean ZF_ReallocElements(void **ptr, size_t *elements, size_t newelements, size_t elementsize)\n#endif\n{\n\tvoid *n;\n\tsize_t oldsize;\n\tsize_t newsize;\n\n\t//protect against malicious overflows\n\tif (newelements > SIZE_MAX / elementsize)\n\t\treturn false;\n\n\toldsize = *elements * elementsize;\n\tnewsize = newelements * elementsize;\n\n#ifdef USE_MSVCRT_DEBUG\n\tn = BZ_ReallocNamed(*ptr, newsize, file, line);\n#else\n\tn = BZ_Realloc(*ptr, newsize);\n#endif\n\tif (!n)\n\t\treturn false;\n\tif (newsize > oldsize)\n\t\tmemset((char*)n+oldsize, 0, newsize - oldsize);\n\t*elements = newelements;\n\t*ptr = n;\n\treturn true;\n}\n\n/*\nvoid *Z_Realloc(void *data, int newsize)\n{\n\tmemheader_t *memref;\n\n\tif (!data)\n\t\treturn Z_Malloc(newsize);\n\n\tmemref = ((memheader_t *)data) - 1;\n\n\tif (memref[0].tag != TAGLESS)\n\t{ // allocate a new block and copy since we need to maintain the lists\n\t\tzone_t *zone = ((zone_t *)data) - 1;\n\t\tint size = zone->mh.size;\n\t\tif (size != newsize)\n\t\t{\n\t\t\tvoid *newdata = Z_Malloc(newsize);\n\n\t\t\tif (size > newsize)\n\t\t\t\tsize = newsize;\n\t\t\tmemcpy(newdata, data, size);\n\n\t\t\tZ_Free(data);\n\t\t\tdata = newdata;\n\t\t}\n\t}\n\telse\n\t{\n\t\tint oldsize = memref[0].size;\n\t\tmemref = realloc(memref, newsize + sizeof(memheader_t));\n\t\tmemref->size = newsize;\n\t\tif (newsize > oldsize)\n\t\t\tmemset((qbyte *)memref + sizeof(memheader_t) + oldsize, 0, newsize - oldsize);\n\t\tdata = ((memheader_t *)memref) + 1;\n\t}\n\n\treturn data;\n}\n*/\n\n#ifdef USE_MSVCRT_DEBUG\nvoid *BZF_MallocNamed(int size, const char *file, int line)\t//BZ_MallocNamed but allowed to fail - like straight malloc.\n{\n\tvoid *mem;\n\tmem = _malloc_dbg(size, _NORMAL_BLOCK, file, line);\n\tif (mem)\n\t{\n\t\tzmemdelta += size;\n\t\tzmemtotal += size;\n\t}\n\treturn mem;\n}\n#else\nvoid *BZF_Malloc(size_t size)\t//BZ_Malloc but allowed to fail - like straight malloc.\n{\n\tvoid *mem;\n\tmem = malloc(size);\n\tif (mem)\n\t{\n\t\tzmemdelta += size;\n\t\tzmemtotal += size;\n\t}\n\treturn mem;\n}\n#endif\n\n#ifdef USE_MSVCRT_DEBUG\nvoid *BZ_MallocNamed(int size, const char *file, int line)\t//BZ_MallocNamed but allowed to fail - like straight malloc.\n{\n\tvoid *mem = BZF_MallocNamed(size, file, line);\n\tif (!mem)\n\t\tSys_Error(\"BZ_Malloc: Failed on allocation of %i bytes\", size);\n\n\treturn mem;\n}\n#else\nvoid *BZ_Malloc(size_t size)\t//Doesn't clear. The expectation is a large file, rather than sensitive data structures.\n{\n\tvoid *mem = BZF_Malloc(size);\n\tif (!mem)\n\t\tSys_Error(\"BZ_Malloc: Failed on allocation of %\"PRIuSIZE\" bytes\", size);\n\n\treturn mem;\n}\n#endif\n\n#ifdef USE_MSVCRT_DEBUG\nvoid *BZF_ReallocNamed(void *data, int newsize, const char *file, int line)\n{\n\treturn _realloc_dbg(data, newsize, _NORMAL_BLOCK, file, line);\n}\n\nvoid *BZ_ReallocNamed(void *data, int newsize, const char *file, int line)\n{\n\tvoid *mem = BZF_ReallocNamed(data, newsize, file, line);\n\n\tif (!mem)\n\t\tSys_Error(\"BZ_Realloc: Failed on reallocation of %i bytes\", newsize);\n\n\treturn mem;\n}\n#else\nvoid *BZF_Realloc(void *data, size_t newsize)\n{\n\treturn realloc(data, newsize);\n}\n\nvoid *BZ_Realloc(void *data, size_t newsize)\n{\n\tvoid *mem = BZF_Realloc(data, newsize);\n\n\tif (!mem)\n\t\tSys_Error(\"BZ_Realloc: Failed on reallocation of %\"PRIuSIZE\" bytes\", newsize);\n\n\treturn mem;\n}\n#endif\n\nvoid BZ_Free(void *data)\n{\n\tfree(data);\n}\n\n\ntypedef struct zonegroupblock_s\n{\n\tunion\n\t{\n\t\tstruct\n\t\t{\n\t\t\tstruct zonegroupblock_s *next;\n\t\t\tstruct zonegroupblock_s *prev;\n#ifdef _DEBUG\n\t\t\t#define ZONEGROUP_SENTINAL 0x709ef00d\n\t\t\tunsigned int sentinal;\n\t\t\tunsigned int size;\n#endif\n\t\t};\n\t\tvec4_t align16;\n\t};\n} zonegroupblock_t;\n\n#ifdef USE_MSVCRT_DEBUG\n#undef ZG_Malloc\nvoid *QDECL ZG_Malloc(zonegroup_t *ctx, int size){return ZG_MallocNamed(ctx, size, \"ZG_Malloc\", size);}\nvoid *ZG_MallocNamed(zonegroup_t *ctx, int size, char *file, int line)\n#else\nvoid *QDECL ZG_Malloc(zonegroup_t *ctx, size_t size)\n#endif\n{\n\tzonegroupblock_t *newm;\n\tsize += sizeof(zonegroupblock_t);\t//well, at least the memory will be pointer aligned...\n#ifdef USE_MSVCRT_DEBUG\n\tnewm = Z_MallocNamed(size, file, line);\n#else\n\tnewm = Z_Malloc(size);\n#endif\n\n\tnewm->prev = NULL;\n\tif (ctx->first)\n\t\t((zonegroupblock_t*)ctx->first)->prev = newm;\n\tnewm->next = ctx->first;\n\tctx->first = newm;\n\tctx->totalbytes += size;\n\n#ifdef _DEBUG\n\tnewm->sentinal = ZONEGROUP_SENTINAL;\n\tnewm->size = size;\n#endif\n\treturn(void*)(newm+1);\n}\nvoid ZG_Free(zonegroup_t *ctx, void *ptr)\n{\n\tzonegroupblock_t *mem = (zonegroupblock_t*)ptr-1;\n\tif (mem->prev)\n\t\tmem->prev->next = mem->next;\n\telse\n\t\tctx->first = mem->next;\n\tif (mem->next)\n\t\tmem->next->prev = mem->prev;\n\n#ifdef _DEBUG\n\tif (mem->sentinal != ZONEGROUP_SENTINAL)\n\t\tSys_Error(\"Corrupt memory\");\n\tctx->totalbytes -= mem->size;\n#endif\n\tBZ_Free(mem);\n}\nvoid ZG_FreeGroup(zonegroup_t *ctx)\n{\n\tzonegroupblock_t *old;\n\twhile(ctx->first)\n\t{\n\t\told = ctx->first;\n\t\tctx->first = old->next;\n\t\tBZ_Free(old);\n\t}\n\tctx->totalbytes = 0;\n}\n\n//============================================================================\n\n/*\n=================\nHunk_TempAlloc\n\nReturn space from the top of the hunk\nclears old temp.\n=================\n*/\ntypedef struct hnktemps_s {\n\tstruct hnktemps_s *next;\n#if TEMPDEBUG>0\n\tint len;\n#endif\n} hnktemps_t;\nhnktemps_t *hnktemps;\n\nvoid Hunk_TempFree(void)\n{\n\thnktemps_t *nt;\n\n\tCOM_AssertMainThread(\"Hunk_TempFree\");\n\n\twhile (hnktemps)\n\t{\n#if TEMPDEBUG>0\n\t\tint i;\n\t\tqbyte *buf;\n\t\tbuf = (qbyte *)(hnktemps+1);\n\t\tfor (i = 0; i < TEMPDEBUG; i++)\n\t\t{\n\t\t\tif (buf[i] != sentinelkey)\n\t\t\t\tSys_Error (\"Hunk_Check: corrupt sentinel\");\n\t\t}\n\t\tbuf+=TEMPDEBUG;\n\t\t//app data\n\t\tbuf += hnktemps->len;\n\t\tfor (i = 0; i < TEMPDEBUG; i++)\n\t\t{\n\t\t\tif (buf[i] != sentinelkey)\n\t\t\t\tSys_Error (\"Hunk_Check: corrupt sentinel\");\n\t\t}\n#endif\n\n\t\tnt = hnktemps->next;\n\n\t\tfree(hnktemps);\n\t\thnktemps = nt;\n\t}\n}\n\n\n//allocates without clearing previous temp.\n//safer than my hack that fuh moaned about...\nvoid *Hunk_TempAllocMore (size_t size)\n{\n\tvoid\t*buf;\n\n#if TEMPDEBUG>0\n\thnktemps_t *nt;\n\tCOM_AssertMainThread(\"Hunk_TempAllocMore\");\n\tnt = (hnktemps_t*)malloc(size + sizeof(hnktemps_t) + TEMPDEBUG*2);\n\tif (!nt)\n\t\treturn NULL;\n\tnt->next = hnktemps;\n\tnt->len = size;\n\thnktemps = nt;\n\tbuf = (void *)(nt+1);\n\tmemset(buf, sentinelkey, TEMPDEBUG);\n\tbuf = (char *)buf + TEMPDEBUG;\n\tmemset(buf, 0, size);\n\tmemset((char *)buf + size, sentinelkey, TEMPDEBUG);\n\treturn buf;\n#else\n\thnktemps_t *nt;\n\tCOM_AssertMainThread(\"Hunk_TempAllocMore\");\n\tnt = (hnktemps_t*)malloc(size + sizeof(hnktemps_t));\n\tif (!nt)\n\t\treturn NULL;\n\tnt->next = hnktemps;\n\thnktemps = nt;\n\tbuf = (void *)(nt+1);\n\tmemset(buf, 0, size);\n\treturn buf;\n#endif\n}\n\n\nvoid *Hunk_TempAlloc (size_t size)\n{\n\tHunk_TempFree();\n\n\treturn Hunk_TempAllocMore(size);\n}\n\n/*\n===============================================================================\n\nCACHE MEMORY\n\n===============================================================================\n*/\nvoid Cache_Flush(void)\n{\n\t//this generically named function is hyjacked to flush models and sounds, as well as ragdolls etc\n#ifdef HAVE_CLIENT\n\tS_Purge(false);\n#endif\n#if defined(HAVE_CLIENT) || defined(HAVE_SERVER)\n#ifdef RAGDOLL\n\trag_flushdolls(true);\n#endif\n\tMod_Purge(MP_FLUSH);\n#endif\n#ifdef HAVE_CLIENT\n\tImage_Purge();\n#endif\n\n#ifdef __GLIBC__\n\tmalloc_trim(0);\n#endif\n}\n\nstatic void Hunk_Print_f (void)\n{\n\tCon_Printf(\"Z Delta: %\"PRIuSIZE\"KB\\n\", zmemdelta/1024); zmemdelta = 0;\n\tCon_Printf(\"Z Total: %\"PRIuSIZE\"KB\\n\", zmemtotal/1024);\n\t//note: Zone memory isn't tracked reliably. we don't track the mem that is freed, so it'll just climb and climb\n\t//we don't track reallocs either.\n\n#if 0\n\t{\n\t\tzone_t *zone;\n\t\tint zoneused = 0;\n\t\tint zoneblocks = 0;\n\n\t\tfor(zone = zone_head; zone; zone=zone->next)\n\t\t{\n\t\t\tzoneused += zone->size + sizeof(zone_t);\n\t\t\tzoneblocks++;\n\t\t}\n\t\tCon_Printf(\"Zone: %i containing %iKB\\n\", zoneblocks, zoneused/1024);\n\t}\n#endif\n\n#ifdef USE_MSVCRT_DEBUG\n\t{\n\t\tstatic struct _CrtMemState savedstate;\n\t\tstatic qboolean statesaved;\n\n\t\t_CrtMemDumpAllObjectsSince(statesaved?&savedstate:NULL);\n\t\t_CrtMemCheckpoint(&savedstate);\n\t\tstatesaved = true;\n\t}\n#endif\n}\nvoid Cache_Init(void)\n{\n\tCmd_AddCommand (\"flush\", Cache_Flush);\n\tCmd_AddCommand (\"hunkprint\", Hunk_Print_f);\n#if 0\n\tCmd_AddCommand (\"zoneprint\", Zone_Print_f);\n#endif\n#ifdef NAMEDMALLOCS\n\tCmd_AddCommand (\"zonegroups\", Zone_Groups_f);\n#endif\n}\n\n//============================================================================\n\n/*\n========================\nMemory_Init\n========================\n*/\nvoid Memory_Init (void)\n{\n#if 0 //ndef NOZONE\n\tint p;\n\tint zonesize = DYNAMIC_SIZE;\n#endif\n\n#if ZONEDEBUG>0 || HUNKDEBUG>0 || TEMPDEBUG>0||CACHEDEBUG>0\n\tsrand(time(0));\n\tsentinelkey = rand() & 0xff;\n#endif\n\n\tCache_Init ();\n\n#ifdef MULTITHREAD\n\tif (!zonelock)\n\t\tzonelock = Sys_CreateMutex(); // this can fail!\n#endif\n\n#if 0 //ndef NOZONE\n\tp = COM_CheckParm (\"-zone\");\n\tif (p)\n\t{\n\t\tif (p < com_argc-1)\n\t\t\tzonesize = Q_atoi (com_argv[p+1]) * 1024;\n\t\telse\n\t\t\tSys_Error (\"Memory_Init: you must specify a size in KB after -zone\");\n\t}\n\tmainzone = Hunk_AllocName ( zonesize, \"zone\" );\n\tZ_ClearZone (mainzone, zonesize);\n#endif\n\n#ifdef MULTITHREAD\n\tSys_ThreadsInit();\n#endif\n}\n\nvoid Memory_DeInit(void)\n{\n\tHunk_TempFree();\n\tCache_Flush();\n\n#ifdef MULTITHREAD\n\tif (zonelock)\n\t{\n\t\tSys_DestroyMutex(zonelock);\n\t\tzonelock = NULL;\n\t}\n#endif\n}\n\n"
  },
  {
    "path": "engine/common/zone.h",
    "content": "/*\nCopyright (C) 1996-1997 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  \n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n*/\n/*\n memory allocation\n\n\nH_??? The hunk manages the entire memory block given to quake.  It must be\ncontiguous.  Memory can be allocated from either the low or high end in a\nstack fashion.  The only way memory is released is by resetting one of the\npointers.\n\nHunk allocations should be given a name, so the Hunk_Print () function\ncan display usage.\n\nHunk allocations are guaranteed to be 16 byte aligned.\n\nThe video buffers are allocated high to avoid leaving a hole underneath\nserver allocations when changing to a higher video mode.\n\n\nZ_??? Zone memory functions used for small, dynamic allocations like text\nstrings from command input.  There is only about 48K for it, allocated at\nthe very bottom of the hunk.\n\nCache_??? Cache memory is for objects that can be dynamically loaded and\ncan usefully stay persistant between levels.  The size of the cache\nfluctuates from level to level.\n\nTo allocate a cachable object\n\n\nTemp_??? Temp memory is used for file loading and surface caching.  The size\nof the cache memory is adjusted so that there is a minimum of 512k remaining\nfor temp memory.\n\n\n------ Top of Memory -------\n\nhigh hunk allocations\n\n<--- high hunk reset point held by vid\n\nvideo buffer\n\nz buffer\n\nsurface cache\n\n<--- high hunk used\n\ncachable memory\n\n<--- low hunk used\n\nclient and server low hunk allocations\n\n<-- low hunk reset point held by host\n\nstartup hunk allocations\n\nZone block\n\n----- Bottom of Memory -----\n\n\n\n*/\n\n#if 0//defined(_DEBUG) && defined(__linux__) && !defined(ANDROID)\n\t#include <valgrind/memcheck.h>\n#else\n\t#define VALGRIND_MAKE_MEM_UNDEFINED(ptr,sz)\t//as an alternative to memzero..\n\t#define VALGRIND_MAKE_MEM_NOACCESS(ptr,sz)\n\t#define VALGRIND_MAKE_MEM_DEFINED_IF_ADDRESSABLE(ptr,sz) //undo VALGRIND_MAKE_MEM_UNDEFINED, to make sure we don't read past the end of buffers.\n#endif\n\nvoid Memory_Init (void);\nvoid Memory_DeInit(void);\n\n//Prefixes:\n//Z - just general 'zone' memory.\n//B - allocated memory is not zero-filled.\n//F - allocation can return NULL (otherwise sys_errors)\n//G - special set of functions with its own rules. Frees must not be mixed.\n//Tag - additional special set of functions with their own rules. Frees must not be mixed.\nvoid VARGS Z_Free (void *ptr);\nvoid *Z_Malloc (size_t size); // returns 0 filled memory\nvoid *ZF_Malloc (size_t size); // allowed to fail\nvoid *Z_MallocNamed (size_t size, char *file, int line); // returns 0 filled memory\nvoid *ZF_MallocNamed (size_t size, char *file, int line); // allowed to fail\n//#define Z_Malloc(x) Z_MallocNamed2(x, __FILE__, __LINE__ )\nvoid *Z_TagMalloc (size_t size, int tag);\nvoid VARGS Z_TagFree(void *ptr);\nvoid VARGS Z_FreeTags(int tag);\nqboolean ZF_ReallocElements(void **ptr, size_t *elements, size_t newelements, size_t elementsize);\t//returns false on error\nqboolean ZF_ReallocElementsNamed(void **ptr, size_t *elements, size_t newelements, size_t elementsize, const char *file, int line);\t//returns false on error\n#define Z_ReallocElements(ptr,elements,newelements,elementsize) do{if (!ZF_ReallocElements(ptr,elements,newelements,elementsize))Sys_Error(\"Z_ReallocElements failed (%s %i)\\n\", __FILE__, __LINE__);}while(0)\t//returns false on error\n\n//Big Zone: allowed to fail, doesn't clear. The expectation is a large file, rather than sensitive data structures.\n//(this is a nicer name for malloc)\nvoid *BZ_Malloc(size_t size);\nvoid *BZF_Malloc(size_t size);\nvoid *BZ_MallocNamed (size_t size, const char *file, int line); // returns 0 filled memory\nvoid *BZF_MallocNamed (size_t size, const char *file, int line); // allowed to fail\nvoid *BZ_Realloc(void *ptr, size_t size);\nvoid *BZ_ReallocNamed(void *data, size_t newsize, const char *file, int line);\nvoid *BZF_Realloc(void *data, size_t newsize);\nvoid *BZF_ReallocNamed(void *data, size_t newsize, const char *file, int line);\nvoid BZ_Free(void *ptr);\n\n//ctx should start off as void*ctx=NULL\ntypedef struct zonegroup_s\n{\n\tvoid *first;\n\tint totalbytes;\t//combined size of all mallocs in this group\n} zonegroup_t;\nvoid *QDECL ZG_Malloc(zonegroup_t *ctx, size_t size);\nvoid *ZG_MallocNamed(zonegroup_t *ctx, size_t size, char *file, int line);\nvoid QDECL ZG_Free(zonegroup_t *ctx, void *ptr);\nvoid QDECL ZG_FreeGroup(zonegroup_t *ctx);\n\n#ifdef USE_MSVCRT_DEBUG\n#define BZ_Malloc(size) BZ_MallocNamed(size, __FILE__, __LINE__)\n#define Z_Malloc(size) Z_MallocNamed(size, __FILE__, __LINE__)\n#define BZ_Realloc(ptr, size) BZ_ReallocNamed(ptr, size, __FILE__, __LINE__)\n#define BZF_Malloc(size) BZF_MallocNamed(size, __FILE__, __LINE__)\n#define ZF_Malloc(size) ZF_MallocNamed(size, __FILE__, __LINE__)\n#define BZF_Realloc(ptr, size) BZF_ReallocNamed(ptr, size, __FILE__, __LINE__)\n#define ZG_Malloc(ctx, size) ZG_MallocNamed(ctx, size, __FILE__, __LINE__)\n#define ZF_ReallocElements(p,e,n,s) ZF_ReallocElementsNamed(p,e,n,s,__FILE__,__LINE__)\n#endif\n#define Z_StrDup(s) strcpy(Z_Malloc(strlen(s)+1), s)\n#define Z_StrDupPtr(v,s) do{Z_Free(*v),*(v) = strcpy(Z_Malloc(strlen(s)+1), s);}while(0)\n\nchar\t*Z_StrDupf(const char *format, ...);\nvoid Z_StrCat(char **ptr, const char *append);\nvoid Z_StrCatLen(char **ptr, const char *append, size_t newlen); //still doesn't allow nulls, but src doesn't need null termination.\n\n/*\nvoid *Hunk_Alloc (int size);\t\t// returns 0 filled memory\nvoid *Hunk_AllocName (int size, char *name);\n*/\n\nvoid *Hunk_TempAlloc (size_t size);\nvoid *Hunk_TempAllocMore (size_t size); //Don't clear old temp\n\n/*\ntypedef struct cache_user_s\n{\n\tvoid\t*data;\n\tqboolean fake;\n} cache_user_t;\n*/\nvoid Cache_Flush (void);\n/*\nvoid *Cache_Check (cache_user_t *c);\n// returns the cached data, and moves to the head of the LRU list\n// if present, otherwise returns NULL\n\nvoid Cache_Free (cache_user_t *c);\n\nvoid *Cache_Alloc (cache_user_t *c, int size, char *name);\n// Returns NULL if all purgable data was tossed and there still\n// wasn't enough room.\n\nvoid Cache_Report (void);\n*/\n"
  },
  {
    "path": "engine/d3d/d3d11_backend.c",
    "content": "#include \"quakedef.h\"\r\n#ifdef D3D11QUAKE\r\n#include \"glquake.h\"\r\n#include \"gl_draw.h\"\r\n#include \"shader.h\"\r\n\r\n#define COBJMACROS\r\n#include <d3d11.h>\r\n\r\nextern ID3D11Device *pD3DDev11;\r\nextern ID3D11DeviceContext *d3ddevctx;\r\nextern ID3D11DepthStencilView *fb_backdepthstencil;\r\n\r\nextern cvar_t r_shadow_realtime_world_lightmaps;\r\nextern cvar_t gl_overbright;\r\nextern cvar_t r_portalrecursion, r_wireframe;\r\n\r\nextern cvar_t r_polygonoffset_shadowmap_offset, r_polygonoffset_shadowmap_factor;\r\n\r\nvoid D3D11_TerminateShadowMap(void);\r\nvoid D3D11BE_BeginShadowmapFace(void);\r\n\r\n#ifdef _DEBUG\r\n#define d3dcheck(foo) do{HRESULT err = foo; if (FAILED(err)) Sys_Error(\"D3D11 reported error on backend line %i - error 0x%x\\n\", __LINE__, (unsigned int)err);} while(0)\r\n#else\r\n#define d3dcheck(foo) foo\r\n#endif\r\n\r\n#define MAX_TMUS 16\r\n\r\nstatic void BE_RotateForEntity (const entity_t *e, const model_t *mod);\r\nvoid D3D11BE_SetupLightCBuffer(dlight_t *l, vec3_t colour);\r\ntexid_t D3D11_GetShadowMap(int id);\r\n\r\n/*========================================== tables for deforms =====================================*/\r\n#define frand() (rand()*(1.0/RAND_MAX))\r\n#define FTABLE_SIZE\t\t1024\r\n#define FTABLE_CLAMP(x)\t(((int)((x)*FTABLE_SIZE) & (FTABLE_SIZE-1)))\r\n#define FTABLE_EVALUATE(table,x) (table ? table[FTABLE_CLAMP(x)] : frand()*((x)-floor(x)))\r\n#define R_FastSin(x) r_sintable[FTABLE_CLAMP(x)]\r\n\r\nstatic\tfloat\tr_sintable[FTABLE_SIZE];\r\nstatic\tfloat\tr_triangletable[FTABLE_SIZE];\r\nstatic\tfloat\tr_squaretable[FTABLE_SIZE];\r\nstatic\tfloat\tr_sawtoothtable[FTABLE_SIZE];\r\nstatic\tfloat\tr_inversesawtoothtable[FTABLE_SIZE];\r\n\r\nstatic float *FTableForFunc ( unsigned int func )\r\n{\r\n\tswitch (func)\r\n\t{\r\n\t\tcase SHADER_FUNC_SIN:\r\n\t\t\treturn r_sintable;\r\n\r\n\t\tcase SHADER_FUNC_TRIANGLE:\r\n\t\t\treturn r_triangletable;\r\n\r\n\t\tcase SHADER_FUNC_SQUARE:\r\n\t\t\treturn r_squaretable;\r\n\r\n\t\tcase SHADER_FUNC_SAWTOOTH:\r\n\t\t\treturn r_sawtoothtable;\r\n\r\n\t\tcase SHADER_FUNC_INVERSESAWTOOTH:\r\n\t\t\treturn r_inversesawtoothtable;\r\n\t}\r\n\r\n\t//bad values allow us to crash (so I can debug em)\r\n\treturn NULL;\r\n}\r\n\r\nstatic void FTable_Init(void)\r\n{\r\n\tunsigned int i;\r\n\tdouble t;\r\n\tfor (i = 0; i < FTABLE_SIZE; i++)\r\n\t{\r\n\t\tt = (double)i / (double)FTABLE_SIZE;\r\n\r\n\t\tr_sintable[i] = sin(t * 2*M_PI);\r\n\r\n\t\tif (t < 0.25)\r\n\t\t\tr_triangletable[i] = t * 4.0;\r\n\t\telse if (t < 0.75)\r\n\t\t\tr_triangletable[i] = 2 - 4.0 * t;\r\n\t\telse\r\n\t\t\tr_triangletable[i] = (t - 0.75) * 4.0 - 1.0;\r\n\r\n\t\tif (t < 0.5)\r\n\t\t\tr_squaretable[i] = 1.0f;\r\n\t\telse\r\n\t\t\tr_squaretable[i] = -1.0f;\r\n\r\n\t\tr_sawtoothtable[i] = t;\r\n\t\tr_inversesawtoothtable[i] = 1.0 - t;\r\n\t}\r\n}\r\n\r\ntypedef vec3_t mat3_t[3];\r\nstatic mat3_t axisDefault={{1, 0, 0},\r\n\t\t\t\t\t{0, 1, 0},\r\n\t\t\t\t\t{0, 0, 1}};\r\n\r\nstatic void Matrix3_Transpose (mat3_t in, mat3_t out)\r\n{\r\n\tout[0][0] = in[0][0];\r\n\tout[1][1] = in[1][1];\r\n\tout[2][2] = in[2][2];\r\n\r\n\tout[0][1] = in[1][0];\r\n\tout[0][2] = in[2][0];\r\n\tout[1][0] = in[0][1];\r\n\tout[1][2] = in[2][1];\r\n\tout[2][0] = in[0][2];\r\n\tout[2][1] = in[1][2];\r\n}\r\nstatic void Matrix3_Multiply_Vec3 (const mat3_t a, const vec3_t b, vec3_t product)\r\n{\r\n\tproduct[0] = a[0][0]*b[0] + a[0][1]*b[1] + a[0][2]*b[2];\r\n\tproduct[1] = a[1][0]*b[0] + a[1][1]*b[1] + a[1][2]*b[2];\r\n\tproduct[2] = a[2][0]*b[0] + a[2][1]*b[1] + a[2][2]*b[2];\r\n}\r\n\r\nstatic int Matrix3_Compare(const mat3_t in, const mat3_t out)\r\n{\r\n\treturn !memcmp(in, out, sizeof(mat3_t));\r\n}\r\n\r\n/*================================================*/\r\n\r\n//global constant-buffer\r\ntypedef struct\r\n{\r\n\tfloat m_view[16];\r\n\tfloat m_projection[16];\r\n\tvec3_t v_eyepos; float v_time;\r\n\tvec3_t e_light_ambient; float pad1;\r\n\tvec3_t e_light_dir; float pad2;\r\n\tvec3_t e_light_mul; float pad3;\r\n} dx11_cbuf_view_t;\r\n\r\ntypedef struct\r\n{\r\n\tfloat l_cubematrix[16];\r\n\tvec3_t l_lightposition; float padl1;\r\n\tvec3_t l_colour; float pad2;\r\n\tvec3_t l_lightcolourscale; float l_lightradius;\r\n\tvec4_t l_shadowmapproj;\r\n\tvec2_t l_shadowmapscale; vec2_t pad3;\r\n} dx11_cbuf_light_t;\r\n\r\n//entity-specific constant-buffer\r\ntypedef struct\r\n{\r\n\tfloat m_model[16];\r\n\tvec3_t e_eyepos;\r\n\tfloat e_time;\r\n\tvec3_t e_light_ambient; float pad1;\r\n\tvec3_t e_light_dir; float pad2;\r\n\tvec3_t e_light_mul; float pad3;\r\n\tvec4_t e_lmscale[4];\r\n\tvec4_t e_uppercolour;\r\n\tvec4_t e_lowercolour;\r\n\tvec4_t e_colourmod;\r\n\tvec4_t e_glowmod;\r\n} dx11_cbuf_entity_t;\r\n\r\n//vertex attributes\r\ntypedef struct\r\n{\r\n\tvecV_t coord;\r\n\tvec2_t tex;\r\n\tvec2_t lm;\r\n\tvec3_t ndir;\r\n\tvec3_t sdir;\r\n\tvec3_t tdir;\r\n\tbyte_vec4_t colorsb;\r\n} dx11_vbovdata_t;\r\n\r\nenum\r\n{\r\n\t//pos1 is always used\r\n\tD3D_VDEC_COL4B = 1<<0,\r\n\tD3D_VDEC_ST0 = 1<<1,\t//base or tcgen/tcmod\r\n\tD3D_VDEC_ST1 = 1<<2,\t//lm or so\r\n//\tD3D_VDEC_ST2 = 1<<3,\r\n//\tD3D_VDEC_ST3 = 1<<4,\r\n\tD3D_VDEC_NORM = 1<<3,\t//normal+sdir+tdir\r\n//\tD3D_VDEC_SKEL = 1<<4,\r\n//\tD3D_VDEC_POS2 = 1<<5,\r\n\tD3D_VDEC_MAX = 1<<5,\r\n};\r\n\r\ntypedef struct blendstates_s\r\n{\r\n\tstruct blendstates_s *next;\r\n\tID3D11BlendState *val;\r\n\tunsigned int bits;\r\n} blendstates_t;\r\n\r\n/*\r\ntypedef struct d3d11renderqueue_s\r\n{\r\n\tID3D11SamplerState *cursamplerstate[MAX_TMUS];\r\n\tID3D11ShaderResourceView *pendingtextures[MAX_TMUS];\r\n\tunsigned int shaderbits;\r\n\r\n\tID3D11Buffer\t\t*stream_buffer[D3D11_BUFF_MAX];\r\n\tunsigned int\t\tstream_stride[D3D11_BUFF_MAX];\r\n\tunsigned int\t\tstream_offset[D3D11_BUFF_MAX];\r\n} d3d11renderqueue_t;\r\n*/\r\n\r\ntypedef struct\r\n{\r\n\tunsigned int inited;\r\n\r\n\tbackendmode_t mode;\r\n\tunsigned int flags;\r\n\r\n\tfloat\tidentitylighting;\r\n\tfloat\t\tcurtime;\r\n\tconst entity_t\t*curentity;\r\n\tconst dlight_t\t*curdlight;\r\n\tvec3_t\t\tcurdlight_colours;\r\n\tshader_t\t*curshader;\r\n\tshader_t\t*depthonly;\r\n\ttexnums_t\t*curtexnums;\r\n\tint\t\t\tcurvertdecl;\r\n\tunsigned int shaderbits;\r\n\tunsigned int curcull;\r\n\tfloat depthbias;\r\n\tfloat depthfactor;\r\n\tfloat m_model[16];\r\n\tunsigned int lastpasscount;\r\n\tvbo_t *batchvbo;\r\n\tbatch_t *curbatch;\r\n\tbatch_t dummybatch;\r\n\tvec4_t lightshadowmapproj;\r\n\tvec2_t lightshadowmapscale;\r\n\r\n\tunsigned int curlmode;\r\n\tshader_t\t*shader_rtlight[LSHADER_MODES];\r\n\tunsigned int texflags[MAX_TMUS];\r\n\tunsigned int tmuflags[MAX_TMUS];\r\n\tID3D11SamplerState *cursamplerstate[MAX_TMUS];\r\n\tID3D11SamplerState *sampstate[SHADER_PASS_IMAGE_FLAGS_D3D11+1];\r\n\tID3D11DepthStencilState *depthstates[1u<<4];\t//index, its fairly short.\r\n\tblendstates_t *blendstates;\t//list. this could get big.\r\n\r\n\t//pending buffer state\r\n\tID3D11Buffer\t\t*stream_buffer[D3D11_BUFF_MAX];\r\n\tunsigned int\t\tstream_stride[D3D11_BUFF_MAX];\r\n\tunsigned int\t\tstream_offset[D3D11_BUFF_MAX];\r\n\tqboolean\t\t\tstream_rgbaf;\r\n\r\n\tprogram_t\t\t\t*programfixedemu[4];\r\n\r\n\tmesh_t\t\t**meshlist;\r\n\tunsigned int nummeshes;\r\n\r\n#define NUMECBUFFERS 8\r\n\tID3D11Buffer *lcbuffer;\r\n\tID3D11Buffer *vcbuffer;\r\n\tID3D11Buffer *ecbuffers[NUMECBUFFERS];\r\n\tint ecbufferidx;\r\n\r\n\tunsigned int wbatch;\r\n\tunsigned int maxwbatches;\r\n\tbatch_t *wbatches;\r\n\r\n\tqboolean textureschanged;\r\n\tID3D11ShaderResourceView *pendingtextures[MAX_TMUS];\r\n\r\n\tfloat depthrange;\r\n\r\n\tqboolean purgevertexstream;\r\n#define NUMVBUFFERS 3\r\n\tID3D11Buffer *vertexstream[NUMVBUFFERS];\r\n\tint vertexstreamcycle;\r\n\tint vertexstreamoffset;\r\n\tqboolean purgeindexstream;\r\n#define NUMIBUFFERS 3\r\n\tID3D11Buffer *indexstream[NUMIBUFFERS];\r\n\tint indexstreamcycle;\r\n\tint indexstreamoffset;\r\n\r\n\ttexid_t currentrender;\r\n\r\n\r\n\tint numlivevbos;\r\n\tint numliveshadowbuffers;\r\n} d3d11backend_t;\r\nextern D3D_FEATURE_LEVEL d3dfeaturelevel;\r\n\r\n#define VERTEXSTREAMSIZE (1024*1024*2)\t//2mb = 1 PAE jumbo page\r\n\r\n#define DYNVBUFFSIZE 65536\r\n#define DYNIBUFFSIZE 65536*3\r\n\r\nstatic vecV_t tmpbuf[65536];\t//max verts per mesh\r\n\r\nstatic d3d11backend_t shaderstate;\r\n\r\nextern int be_maxpasses;\r\n\r\nvoid D3D11_UpdateFiltering(image_t *imagelist, int filtermip[3], int filterpic[3], int mipcap[2], float lodbias, float anis)\r\n{\r\n\tD3D11_SAMPLER_DESC sampdesc;\r\n\tint flags;\r\n\tfor (flags = 0; flags <= (SHADER_PASS_IMAGE_FLAGS_D3D11); flags++)\r\n\t{\r\n\t\tint *filter;\r\n\t\tsampdesc.Filter = 0;\r\n\t\tfilter = (flags & SHADER_PASS_UIPIC)?filterpic:filtermip;\r\n\t\tif ((filter[2] && !(flags & SHADER_PASS_NEAREST)) || (flags & SHADER_PASS_LINEAR))\r\n\t\t\tsampdesc.Filter |= D3D11_FILTER_TYPE_LINEAR<<D3D11_MAG_FILTER_SHIFT;\r\n\t\telse\r\n\t\t\tsampdesc.Filter |= D3D11_FILTER_TYPE_POINT<<D3D11_MAG_FILTER_SHIFT;\r\n\t\t//d3d11 has no no-mip feature\r\n\t\tif ((filter[1]==1 && !(flags & SHADER_PASS_NEAREST)) || (flags & SHADER_PASS_LINEAR))\r\n\t\t\tsampdesc.Filter |= D3D11_FILTER_TYPE_LINEAR<<D3D11_MIP_FILTER_SHIFT;\r\n\t\telse\r\n\t\t\tsampdesc.Filter |= D3D11_FILTER_TYPE_POINT<<D3D11_MIP_FILTER_SHIFT;\r\n\t\tif ((filter[0] && !(flags & SHADER_PASS_NEAREST)) || (flags & SHADER_PASS_LINEAR))\r\n\t\t\tsampdesc.Filter |= D3D11_FILTER_TYPE_LINEAR<<D3D11_MIN_FILTER_SHIFT;\r\n\t\telse\r\n\t\t\tsampdesc.Filter |= D3D11_FILTER_TYPE_POINT<<D3D11_MIN_FILTER_SHIFT;\r\n\t\t//switch to anisotropic filtering if all three filters are linear and anis is set\r\n\t\tif (sampdesc.Filter == D3D11_FILTER_MIN_MAG_MIP_LINEAR && anis > 1)\r\n\t\t\tsampdesc.Filter = D3D11_FILTER_ANISOTROPIC;\r\n\t\tif (flags & SHADER_PASS_DEPTHCMP)\r\n\t\t\tsampdesc.Filter |= D3D11_COMPARISON_FILTERING_BIT;\r\n\r\n\t\tif (flags & SHADER_PASS_DEPTHCMP)\r\n\t\t\tsampdesc.ComparisonFunc = D3D11_COMPARISON_LESS_EQUAL;\r\n\t\telse\r\n\t\t\tsampdesc.ComparisonFunc = D3D11_COMPARISON_NEVER;\r\n\t\tif (flags & SHADER_PASS_CLAMP)\r\n\t\t{\r\n\t\t\tsampdesc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP;\r\n\t\t\tsampdesc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP;\r\n\t\t\tsampdesc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP;\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tsampdesc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP;\r\n\t\t\tsampdesc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP;\r\n\t\t\tsampdesc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP;\r\n\t\t}\r\n\t\tsampdesc.MipLODBias = lodbias;\r\n\t\tsampdesc.MaxAnisotropy = bound(1, anis, 16);\r\n\t\tsampdesc.BorderColor[0] = 0;\r\n\t\tsampdesc.BorderColor[1] = 0;\r\n\t\tsampdesc.BorderColor[2] = 0;\r\n\t\tsampdesc.BorderColor[3] = 0;\r\n\t\tif (flags & SHADER_PASS_NOMIPMAP)\r\n\t\t{\t//only ever use the biggest mip if multiple are present\r\n\t\t\tsampdesc.MinLOD = 0;\r\n\t\t\tsampdesc.MaxLOD = 0;\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tsampdesc.MinLOD = mipcap[0];\r\n\t\t\tsampdesc.MaxLOD = mipcap[1];\r\n\t\t}\r\n\r\n\t\tif (d3dfeaturelevel < D3D_FEATURE_LEVEL_10_0)\t//9.* sucks\r\n\t\t\tsampdesc.MaxLOD = FLT_MAX;\r\n\r\n\t\tif (shaderstate.sampstate[flags])\r\n\t\t\tID3D11SamplerState_Release(shaderstate.sampstate[flags]);\r\n\t\tshaderstate.sampstate[flags] = NULL;\r\n\r\n\t\t//returns the same pointer for dupes, supposedly, so no need to check that\r\n\t\tID3D11Device_CreateSamplerState(pD3DDev11, &sampdesc, &shaderstate.sampstate[flags]);\r\n\t}\r\n}\r\nstatic void BE_DestroyVariousStates(void)\r\n{\r\n\tblendstates_t *bs;\r\n\tint flags;\r\n\tint i;\r\n\t\r\n\tfor (i = 0; i < MAX_TMUS/*shaderstate.lastpasscount*/; i++)\r\n\t{\r\n\t\tshaderstate.cursamplerstate[i] = NULL;\r\n\t}\r\n\tif (d3ddevctx && i)\r\n\t\tID3D11DeviceContext_PSSetSamplers(d3ddevctx, 0, i, shaderstate.cursamplerstate);\r\n\r\n\tfor (flags = 0; flags <= SHADER_PASS_IMAGE_FLAGS_D3D11; flags++)\r\n\t{\r\n\t\tif (shaderstate.sampstate[flags])\r\n\t\t\tID3D11SamplerState_Release(shaderstate.sampstate[flags]);\r\n\t\tshaderstate.sampstate[flags] = NULL;\r\n\t}\r\n\r\n\tif (d3ddevctx)\r\n\t\tID3D11DeviceContext_OMSetDepthStencilState(d3ddevctx, NULL, 0);\r\n\tfor (i = 0; i < (1u<<4); i++)\r\n\t{\r\n\t\tif (shaderstate.depthstates[i])\r\n\t\t\tID3D11DepthStencilState_Release(shaderstate.depthstates[i]);\r\n\t\tshaderstate.depthstates[i] = NULL;\r\n\t}\r\n\r\n\tif (d3ddevctx)\r\n\t\tID3D11DeviceContext_OMSetBlendState(d3ddevctx, NULL, NULL, 0xffffffff);\r\n\t//hopefully the caches inside shaders should get flushed too...\r\n\twhile(shaderstate.blendstates)\r\n\t{\r\n\t\tbs = shaderstate.blendstates;\r\n\t\tshaderstate.blendstates = bs->next;\r\n\r\n\t\tif (bs->val)\r\n\t\t\tID3D11BlendState_Release(bs->val);\r\n\t\tBZ_Free(bs);\r\n\t}\r\n\r\n\tfor (i = 0; i < NUMIBUFFERS; i++)\r\n\t{\r\n\t\tif (shaderstate.indexstream[i])\r\n\t\t\tID3D11Buffer_Release(shaderstate.indexstream[i]);\r\n\t\tshaderstate.indexstream[i] = NULL;\r\n\t}\r\n\t\r\n\tfor (i = 0; i < NUMVBUFFERS; i++)\r\n\t{\r\n\t\tif (shaderstate.vertexstream[i])\r\n\t\t\tID3D11Buffer_Release(shaderstate.vertexstream[i]);\r\n\t\tshaderstate.vertexstream[i] = NULL;\r\n\t}\r\n\r\n\tif (shaderstate.lcbuffer)\r\n\t\tID3D11Buffer_Release(shaderstate.lcbuffer);\r\n\tshaderstate.lcbuffer = NULL;\r\n\r\n\tif (shaderstate.vcbuffer)\r\n\t\tID3D11Buffer_Release(shaderstate.vcbuffer);\r\n\tshaderstate.vcbuffer = NULL;\r\n\r\n\tfor (i = 0; i < NUMECBUFFERS; i++)\r\n\t{\r\n\t\tif (shaderstate.ecbuffers[i])\r\n\t\t\tID3D11Buffer_Release(shaderstate.ecbuffers[i]);\r\n\t\tshaderstate.ecbuffers[i] = NULL;\r\n\t}\r\n\r\n\tD3D11_DestroyTexture(shaderstate.currentrender);\r\n\r\n\t//make sure the device doesn't have any textures still referenced.\r\n\tfor (i = 0; i < MAX_TMUS/*shaderstate.lastpasscount*/; i++)\r\n\t{\r\n\t\tshaderstate.pendingtextures[i] = NULL;\r\n\t}\r\n\tif (d3ddevctx && i)\r\n\t\tID3D11DeviceContext_PSSetShaderResources(d3ddevctx, 0, i, shaderstate.pendingtextures);\r\n}\r\n\r\nstatic void BE_ApplyTMUState(unsigned int tu, unsigned int flags)\r\n{\r\n\tID3D11SamplerState *nstate;\r\n\r\n\tflags = (flags & SHADER_PASS_IMAGE_FLAGS_D3D11);\r\n\tflags |= shaderstate.texflags[tu];\r\n\tnstate = shaderstate.sampstate[flags];\r\n\tif (nstate != shaderstate.cursamplerstate[tu])\r\n\t{\r\n\t\tshaderstate.cursamplerstate[tu] = nstate;\r\n\r\n\t\t//fixme: is it significant to bulk-apply this later?\r\n\t\tID3D11DeviceContext_PSSetSamplers(d3ddevctx, tu, 1, &nstate);\r\n\t}\r\n\t/*\r\n\tif ((flags ^ shaderstate.tmuflags[tu]) & (SHADER_PASS_NEAREST|SHADER_PASS_CLAMP))\r\n\t{\r\n\t\tD3D11_SAMPLER_DESC sampdesc;\r\n\t\tID3D11SamplerState *sstate;\r\n\r\n\t\tshaderstate.tmuflags[tu] = flags;\r\n\r\n\t\tif (flags & SHADER_PASS_NEAREST)\r\n\t\t\tsampdesc.Filter = D3D11_FILTER_MIN_LINEAR_MAG_POINT_MIP_LINEAR;\r\n\t\telse\r\n\t\t\tsampdesc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;\r\n\t\tif (flags & SHADER_PASS_CLAMP)\r\n\t\t{\r\n\t\t\tsampdesc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP;\r\n\t\t\tsampdesc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP;\r\n\t\t\tsampdesc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP;\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tsampdesc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP;\r\n\t\t\tsampdesc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP;\r\n\t\t\tsampdesc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP;\r\n\t\t}\r\n\t\tsampdesc.MipLODBias = 0.0f;\r\n\t\tsampdesc.MaxAnisotropy = 1;\r\n\t\tsampdesc.ComparisonFunc = D3D11_COMPARISON_ALWAYS;\r\n\t\tsampdesc.BorderColor[0] = 0;\r\n\t\tsampdesc.BorderColor[1] = 0;\r\n\t\tsampdesc.BorderColor[2] = 0;\r\n\t\tsampdesc.BorderColor[3] = 0;\r\n\t\tsampdesc.MinLOD = 0;\r\n\t\tsampdesc.MaxLOD = D3D11_FLOAT32_MAX;\r\n\r\n\t\tif (!FAILED(ID3D11Device_CreateSamplerState(pD3DDev11, &sampdesc, &sstate)))\r\n\t\t{\r\n\t\t\tID3D11DeviceContext_PSSetSamplers(d3ddevctx, tu, 1, &sstate);\r\n\t\t\tID3D11SamplerState_Release(sstate);\r\n\t\t}\r\n\t}\r\n\t*/\r\n}\r\n\r\nstatic void *D3D11BE_GenerateBlendState(unsigned int bits)\r\n{\r\n\tD3D11_BLEND_DESC  blend = {0};\r\n\tID3D11BlendState *newblendstate;\r\n\tblend.IndependentBlendEnable = FALSE;\r\n\tblend.AlphaToCoverageEnable = FALSE;\t//FIXME\r\n\r\n\tif (bits & SBITS_BLEND_BITS)\r\n\t{\r\n\t\tswitch(bits & SBITS_SRCBLEND_BITS)\r\n\t\t{\r\n\t\tcase SBITS_SRCBLEND_ZERO:\t\t\t\t\tblend.RenderTarget[0].SrcBlend = D3D11_BLEND_ZERO;\t\t\t\tblend.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ZERO;\t\t\t\tbreak;\r\n\t\tcase SBITS_SRCBLEND_ONE:\t\t\t\t\tblend.RenderTarget[0].SrcBlend = D3D11_BLEND_ONE;\t\t\t\tblend.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE;\t\t\t\tbreak;\r\n\t\tcase SBITS_SRCBLEND_DST_COLOR:\t\t\t\tblend.RenderTarget[0].SrcBlend = D3D11_BLEND_DEST_COLOR;\t\tblend.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_DEST_ALPHA;\t\tbreak;\r\n\t\tcase SBITS_SRCBLEND_ONE_MINUS_DST_COLOR:\tblend.RenderTarget[0].SrcBlend = D3D11_BLEND_INV_DEST_COLOR;\tblend.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_INV_DEST_ALPHA;\tbreak;\r\n\t\tcase SBITS_SRCBLEND_SRC_ALPHA:\t\t\t\tblend.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA;\t\t\tblend.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_SRC_ALPHA;\t\tbreak;\r\n\t\tcase SBITS_SRCBLEND_ONE_MINUS_SRC_ALPHA:\tblend.RenderTarget[0].SrcBlend = D3D11_BLEND_INV_SRC_ALPHA;\t\tblend.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_INV_SRC_ALPHA;\tbreak;\r\n\t\tcase SBITS_SRCBLEND_DST_ALPHA:\t\t\t\tblend.RenderTarget[0].SrcBlend = D3D11_BLEND_DEST_ALPHA;\t\tblend.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_DEST_ALPHA;\t\tbreak;\r\n\t\tcase SBITS_SRCBLEND_ONE_MINUS_DST_ALPHA:\tblend.RenderTarget[0].SrcBlend = D3D11_BLEND_INV_DEST_ALPHA;\tblend.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_INV_DEST_ALPHA;\tbreak;\r\n\t\tcase SBITS_SRCBLEND_ALPHA_SATURATE:\t\t\tblend.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA_SAT;\t\tblend.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_SRC_ALPHA_SAT;\tbreak;\r\n\t\tdefault:\tSys_Error(\"Bad shader blend src\\n\"); return NULL;\r\n\t\t}\r\n\t\tswitch(bits & SBITS_DSTBLEND_BITS)\r\n\t\t{\r\n\t\tcase SBITS_DSTBLEND_ZERO:\t\t\t\t\tblend.RenderTarget[0].DestBlend = D3D11_BLEND_ZERO;\t\t\t\tblend.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ZERO;\t\t\tbreak;\r\n\t\tcase SBITS_DSTBLEND_ONE:\t\t\t\t\tblend.RenderTarget[0].DestBlend = D3D11_BLEND_ONE;\t\t\t\tblend.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ONE;\t\t\t\tbreak;\r\n\t\tcase SBITS_DSTBLEND_SRC_ALPHA:\t\t\t\tblend.RenderTarget[0].DestBlend = D3D11_BLEND_SRC_ALPHA;\t\tblend.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_SRC_ALPHA;\t\tbreak;\r\n\t\tcase SBITS_DSTBLEND_ONE_MINUS_SRC_ALPHA:\tblend.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;\tblend.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_INV_SRC_ALPHA;\tbreak;\r\n\t\tcase SBITS_DSTBLEND_DST_ALPHA:\t\t\t\tblend.RenderTarget[0].DestBlend = D3D11_BLEND_DEST_ALPHA;\t\tblend.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_DEST_ALPHA;\t\tbreak;\r\n\t\tcase SBITS_DSTBLEND_ONE_MINUS_DST_ALPHA:\tblend.RenderTarget[0].DestBlend = D3D11_BLEND_INV_DEST_ALPHA;\tblend.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_INV_DEST_ALPHA;\tbreak;\r\n\t\tcase SBITS_DSTBLEND_SRC_COLOR:\t\t\t\tblend.RenderTarget[0].DestBlend = D3D11_BLEND_SRC_COLOR;\t\tblend.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_SRC_ALPHA;\t\tbreak;\r\n\t\tcase SBITS_DSTBLEND_ONE_MINUS_SRC_COLOR:\tblend.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_COLOR;\tblend.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_INV_SRC_ALPHA;\tbreak;\r\n\t\tdefault:\tSys_Error(\"Bad shader blend dst\\n\"); return NULL;\r\n\t\t}\r\n\t\tblend.RenderTarget[0].BlendEnable = TRUE;\r\n\t}\r\n\telse\r\n\t{\r\n\t\tblend.RenderTarget[0].SrcBlend = D3D11_BLEND_ONE;\r\n\t\tblend.RenderTarget[0].DestBlend = D3D11_BLEND_ZERO;\r\n\t\tblend.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE;\r\n\t\tblend.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ZERO;\r\n\t\tblend.RenderTarget[0].BlendEnable = FALSE;\r\n\t}\r\n\tblend.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;\r\n\tblend.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;\r\n\r\n\tif (bits&SBITS_MASK_BITS)\r\n\t{\r\n\t\tblend.RenderTarget[0].RenderTargetWriteMask = 0;\r\n\t\tif (!(bits&SBITS_MASK_RED))\r\n\t\t\tblend.RenderTarget[0].RenderTargetWriteMask |= D3D11_COLOR_WRITE_ENABLE_RED;\r\n\t\tif (!(bits&SBITS_MASK_GREEN))\r\n\t\t\tblend.RenderTarget[0].RenderTargetWriteMask |= D3D11_COLOR_WRITE_ENABLE_GREEN;\r\n\t\tif (!(bits&SBITS_MASK_BLUE))\r\n\t\t\tblend.RenderTarget[0].RenderTargetWriteMask |= D3D11_COLOR_WRITE_ENABLE_BLUE;\r\n\t\tif (!(bits&SBITS_MASK_ALPHA))\r\n\t\t\tblend.RenderTarget[0].RenderTargetWriteMask |= D3D11_COLOR_WRITE_ENABLE_ALPHA;\r\n\t}\r\n\telse\r\n\t\tblend.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL;\r\n\r\n\tif (!FAILED(ID3D11Device_CreateBlendState(pD3DDev11, &blend, &newblendstate)))\r\n\t\treturn newblendstate;\r\n\treturn NULL;\r\n}\r\n\r\nstatic void D3D11BE_ApplyShaderBits(unsigned int bits, void **blendstatecache)\r\n{\r\n\tunsigned int delta;\r\n\r\n\tif (shaderstate.flags & (BEF_FORCEADDITIVE|BEF_FORCETRANSPARENT|BEF_FORCENODEPTH|BEF_FORCEDEPTHTEST|BEF_FORCEDEPTHWRITE))\r\n\t{\r\n\t\tblendstatecache = NULL;\r\n\r\n\t\tif (shaderstate.flags & BEF_FORCEADDITIVE)\r\n\t\t\tbits = (bits & ~(SBITS_MISC_DEPTHWRITE|SBITS_BLEND_BITS|SBITS_ATEST_BITS))\r\n\t\t\t\t\t\t| (SBITS_SRCBLEND_SRC_ALPHA | SBITS_DSTBLEND_ONE);\r\n\t\telse if (shaderstate.flags & BEF_FORCETRANSPARENT)\r\n\t\t{\r\n\t\t\tif ((bits & SBITS_BLEND_BITS) == (SBITS_SRCBLEND_ONE|SBITS_DSTBLEND_ZERO) || !(bits & SBITS_BLEND_BITS)) \t/*if transparency is forced, clear alpha test bits*/\r\n\t\t\t\tbits = (bits & ~(SBITS_MISC_DEPTHWRITE|SBITS_BLEND_BITS|SBITS_ATEST_BITS))\r\n\t\t\t\t\t\t\t| (SBITS_SRCBLEND_SRC_ALPHA | SBITS_DSTBLEND_ONE_MINUS_SRC_ALPHA);\r\n\t\t}\r\n\r\n\t\tif (shaderstate.flags & BEF_FORCENODEPTH) \t/*EF_NODEPTHTEST dp extension*/\r\n\t\t\tbits |= SBITS_MISC_NODEPTHTEST;\r\n\t\telse\r\n\t\t{\r\n\t\t\tif (shaderstate.flags & BEF_FORCEDEPTHTEST)\r\n\t\t\t\tbits &= ~SBITS_MISC_NODEPTHTEST;\r\n\t\t\tif (shaderstate.flags & BEF_FORCEDEPTHWRITE)\r\n\t\t\t\tbits |= SBITS_MISC_DEPTHWRITE;\r\n\t\t}\r\n\t}\r\n\r\n\tdelta = bits ^ shaderstate.shaderbits;\r\n\tif (!delta)\r\n\t\treturn;\r\n\tshaderstate.shaderbits = bits;\r\n\r\n\tif (delta & (SBITS_BLEND_BITS|SBITS_MASK_BITS))\r\n\t{\r\n\t\tint sbits = bits & (SBITS_BLEND_BITS|SBITS_MASK_BITS);\r\n\t\tif (blendstatecache && *blendstatecache)\r\n\t\t\tID3D11DeviceContext_OMSetBlendState(d3ddevctx, *blendstatecache, NULL, 0xffffffff);\r\n\t\telse\r\n\t\t{\r\n\t\t\tblendstates_t *bs;\r\n\t\t\tfor (bs = shaderstate.blendstates; bs; bs = bs->next)\r\n\t\t\t{\r\n\t\t\t\tif (bs->bits == sbits)\r\n\t\t\t\t\tbreak;\r\n\t\t\t}\r\n\t\t\tif (!bs)\r\n\t\t\t{\r\n\t\t\t\tbs = BZ_Malloc(sizeof(*bs));\r\n\t\t\t\tbs->next = shaderstate.blendstates;\r\n\t\t\t\tshaderstate.blendstates = bs;\r\n\t\t\t\tbs->bits = sbits;\r\n\t\t\t\tbs->val = D3D11BE_GenerateBlendState(sbits);\r\n\t\t\t}\r\n\t\t\tID3D11DeviceContext_OMSetBlendState(d3ddevctx, bs->val, NULL, 0xffffffff);\r\n\t\t\tif (blendstatecache)\r\n\t\t\t\t*blendstatecache = bs->val;\r\n\t\t}\r\n\t}\r\n\r\n\tif (delta & SBITS_ATEST_BITS)\r\n\t{\r\n/*\r\n\t\tswitch(bits & SBITS_ATEST_BITS)\r\n\t\t{\r\n\t\tcase SBITS_ATEST_NONE:\r\n\t\t\tIDirect3DDevice9_SetRenderState(pD3DDev9, D3DRS_ALPHATESTENABLE, FALSE);\r\n\t//\t\tIDirect3DDevice9_SetRenderState(pD3DDev9, D3DRS_ALPHAREF, 0);\r\n\t//\t\tIDirect3DDevice9_SetRenderState(pD3DDev9, D3DRS_ALPHAFUNC, 0);\r\n\t\t\tbreak;\r\n\t\tcase SBITS_ATEST_GT0:\r\n\t\t\tIDirect3DDevice9_SetRenderState(pD3DDev9, D3DRS_ALPHATESTENABLE, TRUE);\r\n\t\t\tIDirect3DDevice9_SetRenderState(pD3DDev9, D3DRS_ALPHAREF, 0);\r\n\t\t\tIDirect3DDevice9_SetRenderState(pD3DDev9, D3DRS_ALPHAFUNC, D3DCMP_GREATER);\r\n\t\t\tbreak;\r\n\t\tcase SBITS_ATEST_LT128:\r\n\t\t\tIDirect3DDevice9_SetRenderState(pD3DDev9, D3DRS_ALPHATESTENABLE, TRUE);\r\n\t\t\tIDirect3DDevice9_SetRenderState(pD3DDev9, D3DRS_ALPHAREF, 128);\r\n\t\t\tIDirect3DDevice9_SetRenderState(pD3DDev9, D3DRS_ALPHAFUNC, D3DCMP_LESS);\r\n\t\t\tbreak;\r\n\t\tcase SBITS_ATEST_GE128:\r\n\t\t\tIDirect3DDevice9_SetRenderState(pD3DDev9, D3DRS_ALPHATESTENABLE, TRUE);\r\n\t\t\tIDirect3DDevice9_SetRenderState(pD3DDev9, D3DRS_ALPHAREF, 128);\r\n\t\t\tIDirect3DDevice9_SetRenderState(pD3DDev9, D3DRS_ALPHAFUNC, D3DCMP_GREATEREQUAL);\r\n\t\t\tbreak;\r\n\t\t}\r\n*/\r\n\t}\r\n\r\n\tif (delta & (SBITS_DEPTHFUNC_BITS|SBITS_MISC_NODEPTHTEST|SBITS_MISC_DEPTHWRITE))\r\n\t{\r\n\t\tunsigned int key = (bits&(SBITS_DEPTHFUNC_BITS|SBITS_MISC_NODEPTHTEST|SBITS_MISC_DEPTHWRITE))>>16;\r\n\t\tif (shaderstate.depthstates[key])\r\n\t\t\tID3D11DeviceContext_OMSetDepthStencilState(d3ddevctx, shaderstate.depthstates[key], 0);\r\n\t\telse\r\n\t\t{\r\n\t\t\tD3D11_DEPTH_STENCIL_DESC depthdesc;\r\n\t\t\tif (bits & SBITS_MISC_NODEPTHTEST)\r\n\t\t\t\tdepthdesc.DepthEnable = false;\r\n\t\t\telse\r\n\t\t\t\tdepthdesc.DepthEnable = true;\r\n\t\t\tif (bits & SBITS_MISC_DEPTHWRITE)\r\n\t\t\t\tdepthdesc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL;\r\n\t\t\telse\r\n\t\t\t\tdepthdesc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ZERO;\r\n\r\n\t\t\tswitch(bits & SBITS_DEPTHFUNC_BITS)\r\n\t\t\t{\r\n\t\t\tdefault:\r\n\t\t\tcase SBITS_DEPTHFUNC_CLOSEREQUAL:\r\n\t\t\t\tdepthdesc.DepthFunc = D3D11_COMPARISON_LESS_EQUAL;\r\n\t\t\t\tbreak;\r\n\t\t\tcase SBITS_DEPTHFUNC_EQUAL:\r\n\t\t\t\tdepthdesc.DepthFunc = D3D11_COMPARISON_EQUAL;\r\n\t\t\t\tbreak;\r\n\t\t\tcase SBITS_DEPTHFUNC_CLOSER:\r\n\t\t\t\tdepthdesc.DepthFunc = D3D11_COMPARISON_LESS;\r\n\t\t\t\tbreak;\r\n\t\t\tcase SBITS_DEPTHFUNC_FURTHER:\r\n\t\t\t\tdepthdesc.DepthFunc = D3D11_COMPARISON_GREATER;\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\r\n\t\t\t//make sure the stencil part is actually valid, even if we're not using it.\r\n\t\t\tdepthdesc.StencilEnable = false;\r\n\t\t\tdepthdesc.StencilReadMask = 0xFF;\r\n\t\t\tdepthdesc.StencilWriteMask = 0xFF;\r\n\t\t\tdepthdesc.FrontFace.StencilFailOp = D3D11_STENCIL_OP_KEEP;\r\n\t\t\tdepthdesc.FrontFace.StencilDepthFailOp = D3D11_STENCIL_OP_KEEP;\r\n\t\t\tdepthdesc.FrontFace.StencilPassOp = D3D11_STENCIL_OP_KEEP;\r\n\t\t\tdepthdesc.FrontFace.StencilFunc = D3D11_COMPARISON_ALWAYS;\r\n\t\t\tdepthdesc.BackFace.StencilFailOp = D3D11_STENCIL_OP_KEEP;\r\n\t\t\tdepthdesc.BackFace.StencilDepthFailOp = D3D11_STENCIL_OP_KEEP;\r\n\t\t\tdepthdesc.BackFace.StencilPassOp = D3D11_STENCIL_OP_KEEP;\r\n\t\t\tdepthdesc.BackFace.StencilFunc = D3D11_COMPARISON_ALWAYS;\r\n\r\n\t\t\t//and change it\r\n\t\t\tif (!FAILED(ID3D11Device_CreateDepthStencilState(pD3DDev11, &depthdesc, &shaderstate.depthstates[key])))\r\n\t\t\t\tID3D11DeviceContext_OMSetDepthStencilState(d3ddevctx, shaderstate.depthstates[key], 0);\r\n\t\t}\r\n\t}\r\n}\r\n\r\nvoid D3D11BE_Reset(qboolean before)\r\n{\r\n\tint i;\r\n\tif (!shaderstate.inited)\r\n\t\treturn;\r\n\r\n\tif (before)\r\n\t{\r\n\t\t/*backbuffer is going away, release stuff so it can be destroyed cleanly*/\r\n\t}\r\n\telse\r\n\t{\r\n\t\t/*we have a new backbuffer etc, reassert state*/\r\n\t\tfor (i = 0; i < MAX_TMUS; i++)\r\n\t\t{\r\n\t\t\tshaderstate.tmuflags[i] = ~0;\r\n\t\t\tBE_ApplyTMUState(i, 0);\r\n\t\t}\r\n\r\n\t\t/*force all state to change, thus setting a known state*/\r\n\t\tshaderstate.shaderbits = ~0;\r\n\t\tD3D11BE_ApplyShaderBits(0, NULL);\r\n\t}\r\n}\r\n\r\nstatic const char LIGHTPASS_SHADER[] = \"\\\r\n{\\n\\\r\n\tprogram rtlight\\n\\\r\n\t{\\n\\\r\n\t\tblendfunc add\\n\\\r\n\t}\\n\\\r\n}\";\r\n\r\nvoid D3D11BE_Init(void)\r\n{\r\n\tD3D11_BUFFER_DESC bd;\r\n\tint i;\r\n\r\n\tbe_maxpasses = 1;\r\n\tmemset(&shaderstate, 0, sizeof(shaderstate));\r\n\tshaderstate.inited = true;\r\n\tshaderstate.curvertdecl = -1;\r\n\tfor (i = 0; i < MAXRLIGHTMAPS; i++)\r\n\t\tshaderstate.dummybatch.lightmap[i] = -1;\r\n\r\n\tFTable_Init();\r\n\r\n//\tBE_CreateSamplerStates();\r\n\r\n//\tFTable_Init();\r\n\r\n/*\tshaderstate.dynxyz_size = sizeof(vecV_t) * DYNVBUFFSIZE;\r\n\tshaderstate.dyncol_size = sizeof(byte_vec4_t) * DYNVBUFFSIZE;\r\n\tshaderstate.dynst_size = sizeof(vec2_t) * DYNVBUFFSIZE;\r\n\tshaderstate.dynidx_size = sizeof(index_t) * DYNIBUFFSIZE;\r\n*/\r\n\tD3D11BE_Reset(false);\r\n\r\n\t//set up the constant buffers\r\n\tfor (i = 0; i < NUMECBUFFERS; i++)\r\n\t{\r\n\t\tbd.Usage = D3D11_USAGE_DYNAMIC;\r\n\t\tbd.ByteWidth = sizeof(dx11_cbuf_entity_t);\r\n\t\tbd.BindFlags = D3D11_BIND_CONSTANT_BUFFER;\r\n\t\tbd.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;\r\n\t\tbd.MiscFlags = 0;\r\n\t\tbd.StructureByteStride = 0;\r\n\t\tif (FAILED(ID3D11Device_CreateBuffer(pD3DDev11, &bd, NULL, &shaderstate.ecbuffers[i])))\r\n\t\t\treturn;\r\n\t}\r\n\tbd.Usage = D3D11_USAGE_DYNAMIC;\r\n\tbd.ByteWidth = sizeof(dx11_cbuf_view_t);\r\n\tbd.BindFlags = D3D11_BIND_CONSTANT_BUFFER;\r\n\tbd.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;\r\n\tbd.MiscFlags = 0;\r\n\tbd.StructureByteStride = 0;\r\n\tif (FAILED(ID3D11Device_CreateBuffer(pD3DDev11, &bd, NULL, &shaderstate.vcbuffer)))\r\n\t\treturn;\r\n\r\n\tbd.Usage = D3D11_USAGE_DYNAMIC;\r\n\tbd.ByteWidth = sizeof(dx11_cbuf_light_t);\r\n\tbd.BindFlags = D3D11_BIND_CONSTANT_BUFFER;\r\n\tbd.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;\r\n\tbd.MiscFlags = 0;\r\n\tbd.StructureByteStride = 0;\r\n\tif (FAILED(ID3D11Device_CreateBuffer(pD3DDev11, &bd, NULL, &shaderstate.lcbuffer)))\r\n\t\treturn;\r\n\r\n\t//generate the streaming buffers for stuff that doesn't provide info in nice static vbos\r\n\tfor (i = 0; i < NUMIBUFFERS; i++)\r\n\t{\r\n\t\tbd.BindFlags = D3D11_BIND_INDEX_BUFFER;\r\n\t\tbd.ByteWidth = VERTEXSTREAMSIZE;\r\n\t\tbd.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;\r\n\t\tbd.MiscFlags = 0;\r\n\t\tbd.StructureByteStride = 0;\r\n\t\tbd.Usage = D3D11_USAGE_DYNAMIC;\r\n\t\tif (FAILED(ID3D11Device_CreateBuffer(pD3DDev11, &bd, NULL, &shaderstate.indexstream[i])))\r\n\t\t\treturn;\r\n\t}\r\n\tfor (i = 0; i < NUMVBUFFERS; i++)\r\n\t{\r\n\t\tbd.BindFlags = D3D11_BIND_VERTEX_BUFFER;\r\n\t\tbd.ByteWidth = VERTEXSTREAMSIZE;\r\n\t\tbd.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;\r\n\t\tbd.MiscFlags = 0;\r\n\t\tbd.StructureByteStride = 0;\r\n\t\tbd.Usage = D3D11_USAGE_DYNAMIC;\r\n\t\tif (FAILED(ID3D11Device_CreateBuffer(pD3DDev11, &bd, NULL, &shaderstate.vertexstream[i])))\r\n\t\t\treturn;\r\n\t}\r\n\r\n\t/*\r\n\tfor (i = 0; i < LSHADER_MODES; i++)\r\n\t{\r\n\t\tif ((i & LSHADER_CUBE) && (i & LSHADER_SPOT))\r\n\t\t\tcontinue;\r\n\t\tshaderstate.shader_rtlight[i] = R_RegisterShader(va(\"rtlight%s%s%s\", \r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(i & LSHADER_SMAP)?\"#PCF\":\"\",\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(i & LSHADER_SPOT)?\"#SPOT\":\"\",\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(i & LSHADER_CUBE)?\"#CUBE\":\"\")\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t, SUF_NONE, LIGHTPASS_SHADER);\r\n\t}\r\n\t*/\r\n//\tshaderstate.shader_rtlight = R_RegisterShader(\"rtlight\", SUF_NONE, LIGHTPASS_SHADER);\r\n\tshaderstate.depthonly = R_RegisterShader(\"depthonly\", SUF_NONE, \r\n\t\t\t\t\"{\\n\"\r\n\t\t\t\t\t\"program depthonly\\n\"\r\n\t\t\t\t\t\"{\\n\"\r\n\t\t\t\t\t\t\"depthwrite\\n\"\r\n\t\t\t\t\t\t\"maskcolor\\n\"\r\n\t\t\t\t\t\"}\\n\"\r\n\t\t\t\t\"}\\n\");\r\n\r\n\tR_InitFlashblends();\r\n}\r\n\r\nvoid D3D11BE_Shutdown(void)\r\n{\r\n\tunsigned int i;\r\n\tshaderstate.inited = false;\r\n#ifdef RTLIGHTS\r\n\tD3D11_TerminateShadowMap();\r\n#endif\r\n\tBE_DestroyVariousStates();\r\n\tZ_Free(shaderstate.wbatches);\r\n\tshaderstate.wbatches = NULL;\r\n\r\n\tfor (i = 0; i < countof(shaderstate.programfixedemu); i++)\r\n\t\tShader_ReleaseGeneric(shaderstate.programfixedemu[i]);\r\n}\r\n\r\nstatic void allocvertexbuffer(ID3D11Buffer **buf, unsigned int *offset, void **dest, unsigned int sz)\r\n{\r\n\tD3D11_MAPPED_SUBRESOURCE msr;\r\n\tunsigned int maptype;\r\n\tsz = (sz+63)&~63;\t//try to align it to a cacheline. I assume the memory is write-through cached.\r\n\tif (shaderstate.purgevertexstream || shaderstate.vertexstreamoffset + sz > VERTEXSTREAMSIZE)\r\n\t{\r\n\t\tshaderstate.purgevertexstream = false;\r\n\t\tshaderstate.vertexstreamoffset = 0;\r\n\t\tmaptype = D3D11_MAP_WRITE_DISCARD;\r\n\t\tshaderstate.vertexstreamcycle++;\r\n\t\tif (shaderstate.vertexstreamcycle == NUMVBUFFERS)\r\n\t\t\tshaderstate.vertexstreamcycle = 0;\r\n\t}\r\n\telse\r\n\t{\r\n\t\tmaptype = D3D11_MAP_WRITE_NO_OVERWRITE;\r\n\t}\r\n\t*buf = shaderstate.vertexstream[shaderstate.vertexstreamcycle];\r\n\tif (FAILED(ID3D11DeviceContext_Map(d3ddevctx, (ID3D11Resource*)*buf, 0, maptype, 0, &msr)))\r\n\t{\r\n\t\t*offset = 0;\r\n\t\t*buf = NULL;\r\n\t\t*dest = NULL;\r\n\t\tCon_Printf(\"allocvertexbuffer: failed to map vertex buffer\\n\");\r\n\t\treturn;\r\n\t}\r\n\r\n\t*dest = (index_t*)((qbyte*)msr.pData + shaderstate.vertexstreamoffset);\r\n\t*offset = shaderstate.vertexstreamoffset;\r\n\tshaderstate.vertexstreamoffset += sz;\r\n\treturn;\r\n}\r\n\r\nstatic unsigned int allocindexbuffer(index_t **dest, ID3D11Buffer **buf, unsigned int entries)\r\n{\r\n\tD3D11_MAPPED_SUBRESOURCE msr;\r\n\tunsigned int sz = sizeof(index_t) * entries;\r\n\tunsigned int maptype;\r\n\tunsigned int ret;\r\n\tif (shaderstate.purgeindexstream || shaderstate.indexstreamoffset + sz > VERTEXSTREAMSIZE)\r\n\t{\r\n\t\tshaderstate.purgeindexstream = false;\r\n\t\tshaderstate.indexstreamoffset = 0;\r\n\t\tmaptype = D3D11_MAP_WRITE_DISCARD;\r\n\t\tshaderstate.indexstreamcycle++;\r\n\t\tif (shaderstate.indexstreamcycle == NUMIBUFFERS)\r\n\t\t\tshaderstate.indexstreamcycle = 0;\r\n\t}\r\n\telse\r\n\t{\r\n\t\tmaptype = D3D11_MAP_WRITE_NO_OVERWRITE;\r\n\t}\r\n\t*buf = shaderstate.indexstream[shaderstate.indexstreamcycle];\r\n\tif (FAILED(ID3D11DeviceContext_Map(d3ddevctx, (ID3D11Resource*)*buf, 0, maptype, 0, &msr)))\r\n\t{\r\n\t\t*buf = NULL;\r\n\t\t*dest = NULL;\r\n\t\tCon_Printf(\"allocindexbuffer: failed to map index buffer\\n\");\r\n\t\treturn 0;\r\n\t}\r\n\r\n\t*dest = (index_t*)((qbyte*)msr.pData + shaderstate.indexstreamoffset);\r\n\tret = shaderstate.indexstreamoffset;\r\n\tshaderstate.indexstreamoffset += sz;\r\n\treturn ret;\r\n}\r\n\r\nID3D11ShaderResourceView *D3D11_Image_View(const texid_t id);\r\nstatic void BindTexture(unsigned int tu, const texid_t id)\r\n{\r\n\tID3D11ShaderResourceView *view = D3D11_Image_View(id);\r\n\tif (shaderstate.pendingtextures[tu] != view)\r\n\t{\r\n\t\tshaderstate.textureschanged = true;\r\n\t\tshaderstate.pendingtextures[tu] = view;\r\n\t\tif (id)\r\n\t\t\tshaderstate.texflags[tu] = id->flags&SHADER_PASS_IMAGE_FLAGS_D3D11;\r\n\t}\r\n}\r\n\r\nvoid D3D11BE_UnbindAllTextures(void)\r\n{\r\n\tint i;\r\n\tfor (i = 0; i < shaderstate.lastpasscount; i++)\r\n\t\tshaderstate.pendingtextures[i] = NULL;\r\n\tif (i)\r\n\t{\r\n\t\tID3D11DeviceContext_PSSetShaderResources(d3ddevctx, 0, i, shaderstate.pendingtextures);\r\n\t\tshaderstate.lastpasscount = 0;\r\n\t}\r\n}\r\n\r\n#define IID_ID3D11Texture2D gahzomgwtf\r\nstatic const GUID IID_ID3D11Texture2D = { 0x6f15aaf2, 0xd208, 0x4e89, { 0x9a,0xb4,0x48,0x95,0x35,0xd3,0x4f,0x9c } };\r\n\r\nstatic texid_t T_Gen_CurrentRender(void)\r\n{\r\n//\tint vwidth, vheight;\r\n//\tint pwidth = vid.fbpwidth;\r\n//\tint pheight = vid.fbpheight;\r\n\r\n\tID3D11Texture2D *backbuf;\r\n#ifdef WINRT\r\n\textern IDXGISwapChain1 *d3dswapchain;\r\n#else\r\n\textern IDXGISwapChain *d3dswapchain;\r\n#endif\r\n\tD3D11_TEXTURE2D_DESC tdesc = {0};\r\n\r\n\tif (r_refdef.recurse)\r\n\t\treturn r_nulltex;\r\n\r\n\t//FIXME: should be willing to use the current rendertargets instead\r\n\tIDXGISwapChain_GetBuffer(d3dswapchain, 0, &IID_ID3D11Texture2D, (LPVOID*)&backbuf);\r\n\tID3D11Texture2D_GetDesc(backbuf, &tdesc);\r\n\r\n\t// copy the scene to texture\r\n\tif (!TEXVALID(shaderstate.currentrender) || tdesc.Width != shaderstate.currentrender->width || tdesc.Height != shaderstate.currentrender->height)\r\n\t{\r\n\t\tif (!shaderstate.currentrender)\r\n\t\t\tshaderstate.currentrender = Image_CreateTexture(\"***$currentrender***\", NULL, 0);\r\n\t\tif (shaderstate.currentrender)\r\n\t\t{\r\n\t\t\tshaderstate.currentrender->width = tdesc.Width;\r\n\t\t\tshaderstate.currentrender->height = tdesc.Height;\r\n\r\n\t\t\ttdesc.BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE;\r\n\t\t\ttdesc.CPUAccessFlags = 0;\r\n\r\n\t\t\tif (shaderstate.currentrender->ptr)\r\n\t\t\t\tID3D11Texture2D_Release((ID3D11Texture2D*)shaderstate.currentrender->ptr);\r\n\t\t\tshaderstate.currentrender->ptr = NULL;\r\n\t\t\tID3D11Device_CreateTexture2D(pD3DDev11, &tdesc, NULL, (ID3D11Texture2D**)&shaderstate.currentrender->ptr);\r\n\t\t}\r\n\t}\r\n\tID3D11DeviceContext_CopyResource(d3ddevctx, (ID3D11Resource*)shaderstate.currentrender->ptr, (ID3D11Resource*)backbuf);\r\n\r\n\tif (shaderstate.currentrender->ptr2)\r\n\t\tID3D11ShaderResourceView_Release((ID3D11ShaderResourceView*)shaderstate.currentrender->ptr2);\r\n\tshaderstate.currentrender->ptr2 = NULL;\r\n\r\n\tID3D11Texture2D_Release(backbuf);\r\n\r\n\treturn shaderstate.currentrender;\r\n}\r\n\r\nstatic void SelectPassTexture(unsigned int tu, const shaderpass_t *pass)\r\n{\r\n\textern texid_t r_whiteimage, missing_texture_gloss, missing_texture_normal;\r\n\tswitch(pass->texgen)\r\n\t{\r\n\tdefault:\r\n\r\n\tcase T_GEN_DIFFUSE:\r\n\t\tBindTexture(tu, shaderstate.curtexnums->base);\r\n\t\tbreak;\r\n\tcase T_GEN_NORMALMAP:\r\n\t\tif (TEXLOADED(shaderstate.curtexnums->bump))\r\n\t\t\tBindTexture(tu, shaderstate.curtexnums->bump);\r\n\t\telse\r\n\t\t\tBindTexture(tu, missing_texture_normal);\r\n\t\tbreak;\r\n\tcase T_GEN_SPECULAR:\r\n\t\tif (TEXLOADED(shaderstate.curtexnums->specular))\r\n\t\t\tBindTexture(tu, shaderstate.curtexnums->specular);\r\n\t\telse\r\n\t\t\tBindTexture(tu, missing_texture_gloss);\r\n\t\tbreak;\r\n\tcase T_GEN_UPPEROVERLAY:\r\n\t\tBindTexture(tu, shaderstate.curtexnums->upperoverlay);\r\n\t\tbreak;\r\n\tcase T_GEN_LOWEROVERLAY:\r\n\t\tBindTexture(tu, shaderstate.curtexnums->loweroverlay);\r\n\t\tbreak;\r\n\tcase T_GEN_FULLBRIGHT:\r\n\t\tBindTexture(tu, shaderstate.curtexnums->fullbright);\r\n\t\tbreak;\r\n\tcase T_GEN_REFLECTCUBE:\r\n\t\tBindTexture(tu, shaderstate.curtexnums->reflectcube);\r\n\t\tbreak;\r\n\tcase T_GEN_REFLECTMASK:\r\n\t\tBindTexture(tu, shaderstate.curtexnums->reflectmask);\r\n\t\tbreak;\r\n\tcase T_GEN_ANIMMAP:\r\n\t\tBindTexture(tu, pass->anim_frames[(int)(pass->anim_fps * shaderstate.curtime) % pass->anim_numframes]);\r\n\t\tbreak;\r\n\tcase T_GEN_SINGLEMAP:\r\n\t\tBindTexture(tu, pass->anim_frames[0]);\r\n\t\tbreak;\r\n\tcase T_GEN_DELUXMAP:\r\n\t\t{\r\n\t\t\tint lmi = shaderstate.curbatch->lightmap[0];\r\n\t\t\tif (lmi < 0 || !lightmap[lmi]->hasdeluxe)\r\n\t\t\t\tBindTexture(tu, r_nulltex);\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tlmi+=1;\r\n\t\t\t\tBindTexture(tu, lightmap[lmi]->lightmap_texture);\r\n\t\t\t}\r\n\t\t}\r\n\t\tbreak;\r\n\tcase T_GEN_LIGHTMAP:\r\n\t\t{\r\n\t\t\tint lmi = shaderstate.curbatch->lightmap[0];\r\n\t\t\tif (lmi < 0)\r\n\t\t\t\tBindTexture(tu, r_whiteimage);\r\n\t\t\telse\r\n\t\t\t\tBindTexture(tu, lightmap[lmi]->lightmap_texture);\r\n\t\t}\r\n\t\tbreak;\r\n\r\n\tcase T_GEN_CURRENTRENDER:\r\n\t\tBindTexture(tu, T_Gen_CurrentRender());\r\n\t\tbreak;\r\n\tcase T_GEN_VIDEOMAP:\r\n#ifdef HAVE_MEDIA_DECODER\r\n\t\tif (pass->cin)\r\n\t\t{\r\n\t\t\tBindTexture(tu, Media_UpdateForShader(pass->cin));\r\n\t\t\tbreak;\r\n\t\t}\r\n#endif\r\n\t\tBindTexture(tu, r_nulltex);\r\n\t\tbreak;\r\n\r\n\tcase T_GEN_LIGHTCUBEMAP:\t//light's projected cubemap\r\n\t\tif (shaderstate.curdlight)\r\n\t\t\tBindTexture(tu, shaderstate.curdlight->cubetexture);\r\n\t\telse\r\n\t\t\tBindTexture(tu, r_nulltex);\r\n\t\tbreak;\r\n\r\n\tcase T_GEN_SHADOWMAP:\t//light's depth values.\r\n#ifdef RTLIGHTS\r\n\t\tif (shaderstate.curdlight)\r\n\t\t{\r\n\t\t\tBindTexture(tu, D3D11_GetShadowMap(shaderstate.curdlight->fov>0));\r\n\t\t\tbreak;\r\n\t\t}\r\n#endif\r\n\t\tBindTexture(tu, r_nulltex);\r\n\t\tbreak;\r\n\r\n\tcase T_GEN_SOURCECOLOUR: //used for render-to-texture targets\r\n\tcase T_GEN_SOURCEDEPTH:\t//used for render-to-texture targets\r\n\r\n\tcase T_GEN_REFLECTION:\t//reflection image (mirror-as-fbo)\r\n\tcase T_GEN_REFRACTION:\t//refraction image (portal-as-fbo)\r\n\tcase T_GEN_REFRACTIONDEPTH:\t//refraction image (portal-as-fbo)\r\n\tcase T_GEN_RIPPLEMAP:\t//ripplemap image (water surface distortions-as-fbo)\r\n\r\n\tcase T_GEN_SOURCECUBE:\t//used for render-to-texture targets\r\n\t\tBindTexture(tu, r_nulltex);\r\n\t\tbreak;\r\n\t}\r\n\r\n\tBE_ApplyTMUState(tu, pass->flags);\r\n\r\n\t//pass blend modes are skipped - they're really only useful for fixed function. we should just use blend modes instead.\r\n}\r\n\r\nstatic void colourgenbyte(const shaderpass_t *pass, int cnt, byte_vec4_t *srcb, vec4_t *srcf, byte_vec4_t *dst, const mesh_t *mesh)\r\n{\r\n#define D3DCOLOR unsigned int\r\n#define D3DCOLOR_ARGB(a,r,g,b)\t((D3DCOLOR)((((a)&0xff)<<24)|(((r)&0xff)<<16)|(((g)&0xff)<<8)|((b)&0xff)))\r\n#define D3DCOLOR_COLORVALUE(r,g,b,a)\tD3DCOLOR_RGBA((DWORD)((r)*255.f),(DWORD)((g)*255.f),(DWORD)((b)*255.f),(DWORD)((a)*255.f))\r\n#define D3DCOLOR_RGBA(r,g,b,a)\tD3DCOLOR_ARGB(a,r,g,b)\r\n\tD3DCOLOR block;\r\n\r\n\tswitch (pass->rgbgen)\r\n\t{\r\n\tcase RGB_GEN_ENTITY:\r\n\t\tblock = D3DCOLOR_COLORVALUE(shaderstate.curentity->shaderRGBAf[0], shaderstate.curentity->shaderRGBAf[1], shaderstate.curentity->shaderRGBAf[2], shaderstate.curentity->shaderRGBAf[3]);\r\n\t\twhile((cnt)--)\r\n\t\t{\r\n\t\t\t((D3DCOLOR*)dst)[cnt] = block;\r\n\t\t}\r\n\t\tbreak;\r\n\tcase RGB_GEN_ONE_MINUS_ENTITY:\r\n\t\tblock = D3DCOLOR_COLORVALUE(1-shaderstate.curentity->shaderRGBAf[0], 1-shaderstate.curentity->shaderRGBAf[1], 1-shaderstate.curentity->shaderRGBAf[2], 1-shaderstate.curentity->shaderRGBAf[3]);\r\n\t\twhile((cnt)--)\r\n\t\t{\r\n\t\t\t((D3DCOLOR*)dst)[cnt] = block;\r\n\t\t}\r\n\t\tbreak;\r\n\tcase RGB_GEN_VERTEX_LIGHTING:\r\n\tcase RGB_GEN_VERTEX_EXACT:\r\n\t\tif (srcb)\r\n\t\t{\r\n\t\t\twhile((cnt)--)\r\n\t\t\t{\r\n\t\t\t\tqbyte r, g, b;\r\n\t\t\t\tr=srcb[cnt][0];\r\n\t\t\t\tg=srcb[cnt][1];\r\n\t\t\t\tb=srcb[cnt][2];\r\n\t\t\t\tdst[cnt][0] = b;\r\n\t\t\t\tdst[cnt][1] = g;\r\n\t\t\t\tdst[cnt][2] = r;\r\n\t\t\t}\r\n\t\t}\r\n\t\telse if (srcf)\r\n\t\t{\r\n\t\t\twhile((cnt)--)\r\n\t\t\t{\r\n\t\t\t\tint r, g, b;\r\n\t\t\t\tr=srcf[cnt][0]*255;\r\n\t\t\t\tg=srcf[cnt][1]*255;\r\n\t\t\t\tb=srcf[cnt][2]*255;\r\n\t\t\t\tdst[cnt][0] = bound(0, b, 255);\r\n\t\t\t\tdst[cnt][1] = bound(0, g, 255);\r\n\t\t\t\tdst[cnt][2] = bound(0, r, 255);\r\n\t\t\t}\r\n\t\t}\r\n\t\telse\r\n\t\t\tgoto identity;\r\n\t\tbreak;\r\n\tcase RGB_GEN_ONE_MINUS_VERTEX:\r\n\t\tif (srcb)\r\n\t\t{\r\n\t\t\twhile((cnt)--)\r\n\t\t\t{\r\n\t\t\t\tqbyte r, g, b;\r\n\t\t\t\tr=255-srcb[cnt][0];\r\n\t\t\t\tg=255-srcb[cnt][1];\r\n\t\t\t\tb=255-srcb[cnt][2];\r\n\t\t\t\tdst[cnt][0] = b;\r\n\t\t\t\tdst[cnt][1] = g;\r\n\t\t\t\tdst[cnt][2] = r;\r\n\t\t\t}\r\n\t\t}\r\n\t\telse if (srcf)\r\n\t\t{\r\n\t\t\twhile((cnt)--)\r\n\t\t\t{\r\n\t\t\t\tint r, g, b;\r\n\t\t\t\tr=255-srcf[cnt][0]*255;\r\n\t\t\t\tg=255-srcf[cnt][1]*255;\r\n\t\t\t\tb=255-srcf[cnt][2]*255;\r\n\t\t\t\tdst[cnt][0] = bound(0, b, 255);\r\n\t\t\t\tdst[cnt][1] = bound(0, g, 255);\r\n\t\t\t\tdst[cnt][2] = bound(0, r, 255);\r\n\t\t\t}\r\n\t\t}\r\n\t\telse\r\n\t\t\tgoto identity;\r\n\t\tbreak;\r\n\tcase RGB_GEN_IDENTITY_LIGHTING:\r\n\t\t//compensate for overbrights\r\n\t\tblock = D3DCOLOR_RGBA(255, 255, 255, 255); //shaderstate.identitylighting\r\n\t\twhile((cnt)--)\r\n\t\t{\r\n\t\t\t((D3DCOLOR*)dst)[cnt] = block;\r\n\t\t}\r\n\t\tbreak;\r\n\tdefault:\r\n\tidentity:\r\n\tcase RGB_GEN_IDENTITY_OVERBRIGHT:\r\n\tcase RGB_GEN_IDENTITY:\r\n\t\tblock = D3DCOLOR_RGBA(255, 255, 255, 255);\r\n\t\twhile((cnt)--)\r\n\t\t{\r\n\t\t\t((D3DCOLOR*)dst)[cnt] = block;\r\n\t\t}\r\n\t\tbreak;\r\n\tcase RGB_GEN_CONST:\r\n\t\tblock = D3DCOLOR_COLORVALUE(pass->rgbgen_func.args[0], pass->rgbgen_func.args[1], pass->rgbgen_func.args[2], 1);\r\n\t\twhile((cnt)--)\r\n\t\t{\r\n\t\t\t((D3DCOLOR*)dst)[cnt] = block;\r\n\t\t}\r\n\t\tbreak;\r\n\tcase RGB_GEN_LIGHTING_DIFFUSE:\r\n\t\t//collect lighting details for mobile entities\r\n\t\tif (!mesh->normals_array)\r\n\t\t{\r\n\t\t\tblock = D3DCOLOR_RGBA(255, 255, 255, 255);\r\n\t\t\twhile((cnt)--)\r\n\t\t\t{\r\n\t\t\t\t((D3DCOLOR*)dst)[cnt] = block;\r\n\t\t\t}\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tR_LightArraysByte_BGR(shaderstate.curentity , mesh->xyz_array, dst, cnt, mesh->normals_array, pass->rgbgen==RGB_GEN_ENTITY_LIGHTING_DIFFUSE);\r\n\t\t}\r\n\t\tbreak;\r\n\tcase RGB_GEN_WAVE:\r\n\t\t{\r\n\t\t\tfloat *table;\r\n\t\t\tfloat c;\r\n\r\n\t\t\ttable = FTableForFunc(pass->rgbgen_func.type);\r\n\t\t\tc = pass->rgbgen_func.args[2] + shaderstate.curtime * pass->rgbgen_func.args[3];\r\n\t\t\tc = FTABLE_EVALUATE(table, c) * pass->rgbgen_func.args[1] + pass->rgbgen_func.args[0];\r\n\t\t\tc = bound(0.0f, c, 1.0f);\r\n\t\t\tblock = D3DCOLOR_COLORVALUE(c, c, c, 1);\r\n\r\n\t\t\twhile((cnt)--)\r\n\t\t\t{\r\n\t\t\t\t((D3DCOLOR*)dst)[cnt] = block;\r\n\t\t\t}\r\n\t\t}\r\n\t\tbreak;\r\n\r\n\tcase RGB_GEN_TOPCOLOR:\r\n\tcase RGB_GEN_BOTTOMCOLOR:\r\n#ifdef warningmsg\r\n#pragma warningmsg(\"fix 24bit player colours\")\r\n#endif\r\n\t\tblock = D3DCOLOR_RGBA(255, 255, 255, 255);\r\n\t\twhile((cnt)--)\r\n\t\t{\r\n\t\t\t((D3DCOLOR*)dst)[cnt] = block;\r\n\t\t}\r\n\t//\tCon_Printf(\"RGB_GEN %i not supported\\n\", pass->rgbgen);\r\n\t\tbreak;\r\n\t}\r\n}\r\n\r\nstatic void alphagenbyte(const shaderpass_t *pass, int cnt, byte_vec4_t *srcb, vec4_t *srcf, byte_vec4_t *dst, const mesh_t *mesh)\r\n{\r\n\tfloat *table;\r\n\tunsigned char t;\r\n\tfloat f;\r\n\tvec3_t v1, v2;\r\n\r\n\tswitch (pass->alphagen)\r\n\t{\r\n\tdefault:\r\n\tcase ALPHA_GEN_IDENTITY:\r\n\t\tif (shaderstate.flags & BEF_FORCETRANSPARENT)\r\n\t\t{\r\n\t\t\tf = shaderstate.curentity->shaderRGBAf[3];\r\n\t\t\tif (f < 0)\r\n\t\t\t\tt = 0;\r\n\t\t\telse if (f >= 1)\r\n\t\t\t\tt = 255;\r\n\t\t\telse\r\n\t\t\t\tt = f*255;\r\n\t\t\twhile(cnt--)\r\n\t\t\t\tdst[cnt][3] = t;\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\twhile(cnt--)\r\n\t\t\t\tdst[cnt][3] = 255;\r\n\t\t}\r\n\t\tbreak;\r\n\r\n\tcase ALPHA_GEN_CONST:\r\n\t\tt = pass->alphagen_func.args[0]*255;\r\n\t\twhile(cnt--)\r\n\t\t\tdst[cnt][3] = t;\r\n\t\tbreak;\r\n\r\n\tcase ALPHA_GEN_WAVE:\r\n\t\ttable = FTableForFunc(pass->alphagen_func.type);\r\n\t\tf = pass->alphagen_func.args[2] + shaderstate.curtime * pass->alphagen_func.args[3];\r\n\t\tf = FTABLE_EVALUATE(table, f) * pass->alphagen_func.args[1] + pass->alphagen_func.args[0];\r\n\t\tt = bound(0.0f, f, 1.0f)*255;\r\n\t\twhile(cnt--)\r\n\t\t\tdst[cnt][3] = t;\r\n\t\tbreak;\r\n\r\n\tcase ALPHA_GEN_PORTAL:\r\n\t\t//FIXME: should this be per-vert?\r\n\t\tVectorAdd(mesh->xyz_array[0], shaderstate.curentity->origin, v1);\r\n\t\tVectorSubtract(r_origin, v1, v2);\r\n\t\tf = VectorLength(v2) * (1.0 / 255.0);\r\n\t\tt = bound(0.0f, f, 1.0f)*255;\r\n\r\n\t\twhile(cnt--)\r\n\t\t\tdst[cnt][3] = t;\r\n\t\tbreak;\r\n\r\n\tcase ALPHA_GEN_VERTEX:\r\n\t\tif (srcb)\r\n\t\t{\r\n\t\t\twhile(cnt--)\r\n\t\t\t{\r\n\t\t\t\tdst[cnt][3] = srcb[cnt][3];\r\n\t\t\t}\r\n\t\t}\r\n\t\telse if (srcf)\r\n\t\t{\r\n\t\t\twhile(cnt--)\r\n\t\t\t{\r\n\t\t\t\tdst[cnt][3] = bound(0, srcf[cnt][3]*255, 255);\r\n\t\t\t}\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\twhile(cnt--)\r\n\t\t\t{\r\n\t\t\t\tdst[cnt][3] = 255;\r\n\t\t\t}\r\n\t\t}\r\n\t\tbreak;\r\n\r\n\tcase ALPHA_GEN_ENTITY:\r\n\t\tt = bound(0, shaderstate.curentity->shaderRGBAf[3], 1)*255;\r\n\t\twhile(cnt--)\r\n\t\t{\r\n\t\t\tdst[cnt][3] = t;\r\n\t\t}\r\n\t\tbreak;\r\n\r\n\tcase ALPHA_GEN_SPECULAR:\r\n\t\t{\r\n\t\t\tint i;\r\n\t\t\tVectorSubtract(r_origin, shaderstate.curentity->origin, v1);\r\n\r\n\t\t\tif (!Matrix3_Compare(shaderstate.curentity->axis, (void *)axisDefault))\r\n\t\t\t{\r\n\t\t\t\tMatrix3_Multiply_Vec3(shaderstate.curentity->axis, v1, v2);\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tVectorCopy(v1, v2);\r\n\t\t\t}\r\n\r\n\t\t\tfor (i = 0; i < cnt; i++)\r\n\t\t\t{\r\n\t\t\t\tVectorSubtract(v2, mesh->xyz_array[i], v1);\r\n\t\t\t\tf = DotProduct(v1, mesh->normals_array[i]) * Q_rsqrt(DotProduct(v1,v1));\r\n\t\t\t\tf = f * f * f * f * f;\r\n\t\t\t\tdst[i][3] = bound (0, (int)(f*255), 255);\r\n\t\t\t}\r\n\t\t}\r\n\t\tbreak;\r\n\t}\r\n}\r\n\r\n//true if we used an array (flag to use uniforms for it instead if false)\r\nstatic void BE_GenerateColourMods(unsigned int vertcount, const shaderpass_t *pass)\r\n{\r\n\tconst mesh_t *m = shaderstate.meshlist[0];\r\n\tif (pass->flags & SHADER_PASS_NOCOLORARRAY)\r\n\t{\r\n#if 1\r\n\t\tID3D11Buffer *buf;\r\n\t\tunsigned char *map;\r\n\t\tunsigned int offset;\r\n\t\tbyte_vec4_t passcolour;\r\n\t\tstatic byte_vec4_t fakesource = {0xff,0xff,0xff,0xff};\r\n\t\tallocvertexbuffer(&buf, &offset, (void**)&map, sizeof(byte_vec4_t));\r\n\r\n\t\tcolourgenbyte(pass, 1, (byte_vec4_t*)&fakesource, NULL, (byte_vec4_t*)&passcolour, m);\r\n\t\talphagenbyte(pass, 1, (byte_vec4_t*)&fakesource, NULL, (byte_vec4_t*)&passcolour, m);\r\n\t\t*(int*)map = *(int*)passcolour;\r\n\r\n\t\tID3D11DeviceContext_Unmap(d3ddevctx, (ID3D11Resource*)buf, 0);\r\n\t\tshaderstate.stream_buffer[D3D11_BUFF_COL] = buf;\r\n\t\tshaderstate.stream_offset[D3D11_BUFF_COL] = offset;\r\n\t\tshaderstate.stream_stride[D3D11_BUFF_COL] = 0;\t//omg that's so lame!\r\n\t\tshaderstate.stream_rgbaf = false;\r\n#else\r\n\t\tID3D11Buffer *buf;\r\n\t\tunsigned char *map;\r\n\t\tunsigned int offset;\r\n\t\tvec4_t passcolour;\r\n\t\tstatic vec4_t fakesource = {1,1,1,1};\r\n\t\tallocvertexbuffer(&buf, &offset, (void**)&map, sizeof(fakesource));\r\n\r\n\t\tcolourgen(pass, 1, (vec4_t*)&fakesource, NULL, (vec4_t*)&passcolour, m);\r\n\t\talphagen(pass, 1, (vec4_t*)&fakesource, NULL, (vec4_t*)&passcolour, m);\r\n\t\tVector4Copy(passcolour, map);\r\n\r\n\t\tID3D11DeviceContext_Unmap(d3ddevctx, (ID3D11Resource*)buf, 0);\r\n\t\tshaderstate.stream_buffer[D3D11_BUFF_COL] = buf;\r\n\t\tshaderstate.stream_offset[D3D11_BUFF_COL] = offset;\r\n\t\tshaderstate.stream_stride[D3D11_BUFF_COL] = 0;\t//omg that's so lame!\r\n\t\tshaderstate.stream_rgbaf = true;\r\n#endif\r\n\t}\r\n\telse\r\n\t{\r\n\t\tif (shaderstate.batchvbo && (m->colors4f_array[0] &&\r\n\t\t\t\t\t\t((pass->rgbgen == RGB_GEN_VERTEX_LIGHTING) ||\r\n\t\t\t\t\t\t(pass->rgbgen == RGB_GEN_VERTEX_EXACT) ||\r\n\t\t\t\t\t\t(pass->rgbgen == RGB_GEN_ONE_MINUS_VERTEX)) &&\r\n\t\t\t\t\t\t(pass->alphagen == ALPHA_GEN_VERTEX)))\r\n\t\t{\r\n\t\t\tshaderstate.stream_buffer[D3D11_BUFF_COL] = shaderstate.batchvbo->colours[0].d3d.buff;\r\n\t\t\tshaderstate.stream_offset[D3D11_BUFF_COL] = shaderstate.batchvbo->colours[0].d3d.offs;\r\n\t\t\tshaderstate.stream_stride[D3D11_BUFF_COL] = sizeof(dx11_vbovdata_t);\r\n\t\t\tshaderstate.stream_rgbaf = false;\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tID3D11Buffer *buf;\r\n\t\t\tbyte_vec4_t *map;\r\n\t\t\tunsigned int mno;\r\n\t\t\tunsigned int offset;\r\n\t\t\tallocvertexbuffer(&buf, &offset, (void**)&map, vertcount*sizeof(byte_vec4_t));\r\n\t\t\tfor (mno = 0; mno < shaderstate.nummeshes; mno++)\r\n\t\t\t{\r\n\t\t\t\tm = shaderstate.meshlist[mno];\r\n\t\t\t\tcolourgenbyte(pass, m->numvertexes, m->colors4b_array, m->colors4f_array[0], map, m);\r\n\t\t\t\talphagenbyte(pass, m->numvertexes, m->colors4b_array, m->colors4f_array[0], map, m);\r\n\t\t\t\tmap += m->numvertexes;\r\n\t\t\t}\r\n\t\t\tID3D11DeviceContext_Unmap(d3ddevctx, (ID3D11Resource*)buf, 0);\r\n\t\t\tshaderstate.stream_buffer[D3D11_BUFF_COL] = buf;\r\n\t\t\tshaderstate.stream_offset[D3D11_BUFF_COL] = offset;\r\n\t\t\tshaderstate.stream_stride[D3D11_BUFF_COL] = sizeof(byte_vec4_t);\r\n\t\t\tshaderstate.stream_rgbaf = false;\r\n\t\t}\r\n\t}\r\n}\r\n\r\n/*********************************************************************************************************/\r\n/*========================================== texture coord generation =====================================*/\r\nstatic void tcgen_environment(float *st, unsigned int numverts, float *xyz, float *normal)\r\n{\r\n\tint\t\t\ti;\r\n\tvec3_t\t\tviewer, reflected;\r\n\tfloat\t\td;\r\n\r\n\tvec3_t\t\trorg;\r\n\r\n\tRotateLightVector(shaderstate.curentity->axis, shaderstate.curentity->origin, r_origin, rorg);\r\n\r\n\tfor (i = 0 ; i < numverts ; i++, xyz += sizeof(vecV_t)/sizeof(vec_t), normal += 3, st += 2 )\r\n\t{\r\n\t\tVectorSubtract (rorg, xyz, viewer);\r\n\t\tVectorNormalizeFast (viewer);\r\n\r\n\t\td = DotProduct (normal, viewer);\r\n\r\n\t\treflected[0] = normal[0]*2*d - viewer[0];\r\n\t\treflected[1] = normal[1]*2*d - viewer[1];\r\n\t\treflected[2] = normal[2]*2*d - viewer[2];\r\n\r\n\t\tst[0] = 0.5 + reflected[1] * 0.5;\r\n\t\tst[1] = 0.5 - reflected[2] * 0.5;\r\n\t}\r\n}\r\n\r\nstatic float *tcgen(const shaderpass_t *pass, int cnt, float *dst, const mesh_t *mesh)\r\n{\r\n\tint i;\r\n\tvecV_t *src;\r\n\tswitch (pass->tcgen)\r\n\t{\r\n\tdefault:\r\n\tcase TC_GEN_BASE:\r\n\t\treturn (float*)mesh->st_array;\r\n\tcase TC_GEN_LIGHTMAP:\r\n\t\treturn (float*)mesh->lmst_array[0];\r\n\tcase TC_GEN_NORMAL:\r\n\t\treturn (float*)mesh->normals_array;\r\n\tcase TC_GEN_SVECTOR:\r\n\t\treturn (float*)mesh->snormals_array;\r\n\tcase TC_GEN_TVECTOR:\r\n\t\treturn (float*)mesh->tnormals_array;\r\n\tcase TC_GEN_ENVIRONMENT:\r\n\t\ttcgen_environment(dst, cnt, (float*)mesh->xyz_array, (float*)mesh->normals_array);\r\n\t\treturn dst;\r\n\r\n\tcase TC_GEN_DOTPRODUCT:\r\n\t\treturn dst;//mesh->st_array[0];\r\n\tcase TC_GEN_VECTOR:\r\n\t\tsrc = mesh->xyz_array;\r\n\t\tfor (i = 0; i < cnt; i++, dst += 2)\r\n\t\t{\r\n\t\t\tdst[0] = DotProduct(pass->tcgenvec[0], src[i]);\r\n\t\t\tdst[1] = DotProduct(pass->tcgenvec[1], src[i]);\r\n\t\t}\r\n\t\treturn dst;\r\n\t}\r\n}\r\n\r\n/*src and dst can be the same address when tcmods are chained*/\r\nstatic void tcmod(const tcmod_t *tcmod, int cnt, const float *src, float *dst, const mesh_t *mesh)\r\n{\r\n\tfloat *table;\r\n\tfloat t1, t2;\r\n\tfloat cost, sint;\r\n\tint j;\r\n\r\n\tswitch (tcmod->type)\r\n\t{\r\n\t\tcase SHADER_TCMOD_ROTATE:\r\n\t\t\tcost = tcmod->args[0] * shaderstate.curtime;\r\n\t\t\tsint = R_FastSin(cost);\r\n\t\t\tcost = R_FastSin(cost + 0.25);\r\n\r\n\t\t\tfor (j = 0; j < cnt; j++, dst+=2,src+=2)\r\n\t\t\t{\r\n\t\t\t\tt1 = cost * (src[0] - 0.5f) - sint * (src[1] - 0.5f) + 0.5f;\r\n\t\t\t\tt2 = cost * (src[1] - 0.5f) + sint * (src[0] - 0.5f) + 0.5f;\r\n\t\t\t\tdst[0] = t1;\r\n\t\t\t\tdst[1] = t2;\r\n\t\t\t}\r\n\t\t\tbreak;\r\n\r\n\t\tcase SHADER_TCMOD_SCALE:\r\n\t\t\tt1 = tcmod->args[0];\r\n\t\t\tt2 = tcmod->args[1];\r\n\r\n\t\t\tfor (j = 0; j < cnt; j++, dst+=2,src+=2)\r\n\t\t\t{\r\n\t\t\t\tdst[0] = src[0] * t1;\r\n\t\t\t\tdst[1] = src[1] * t2;\r\n\t\t\t}\r\n\t\t\tbreak;\r\n\r\n\t\tcase SHADER_TCMOD_TURB:\r\n\t\t\tt1 = tcmod->args[2] + shaderstate.curtime * tcmod->args[3];\r\n\t\t\tt2 = tcmod->args[1];\r\n\r\n\t\t\tfor (j = 0; j < cnt; j++, dst+=2,src+=2)\r\n\t\t\t{\r\n\t\t\t\tdst[0] = src[0] + R_FastSin (src[0]*t2+t1) * t2;\r\n\t\t\t\tdst[1] = src[1] + R_FastSin (src[1]*t2+t1) * t2;\r\n\t\t\t}\r\n\t\t\tbreak;\r\n\r\n\t\tcase SHADER_TCMOD_STRETCH:\r\n\t\t\ttable = FTableForFunc(tcmod->args[0]);\r\n\t\t\tt2 = tcmod->args[3] + shaderstate.curtime * tcmod->args[4];\r\n\t\t\tt1 = FTABLE_EVALUATE(table, t2) * tcmod->args[2] + tcmod->args[1];\r\n\t\t\tt1 = t1 ? 1.0f / t1 : 1.0f;\r\n\t\t\tt2 = 0.5f - 0.5f * t1;\r\n\t\t\tfor (j = 0; j < cnt; j++, dst+=2,src+=2)\r\n\t\t\t{\r\n\t\t\t\tdst[0] = src[0] * t1 + t2;\r\n\t\t\t\tdst[1] = src[1] * t1 + t2;\r\n\t\t\t}\r\n\t\t\tbreak;\r\n\r\n\t\tcase SHADER_TCMOD_SCROLL:\r\n\t\t\tt1 = tcmod->args[0] * shaderstate.curtime;\r\n\t\t\tt2 = tcmod->args[1] * shaderstate.curtime;\r\n\r\n\t\t\tfor (j = 0; j < cnt; j++, dst += 2, src+=2)\r\n\t\t\t{\r\n\t\t\t\tdst[0] = src[0] + t1;\r\n\t\t\t\tdst[1] = src[1] + t2;\r\n\t\t\t}\r\n\t\t\tbreak;\r\n\r\n\t\tcase SHADER_TCMOD_TRANSFORM:\r\n\t\t\tfor (j = 0; j < cnt; j++, dst+=2, src+=2)\r\n\t\t\t{\r\n\t\t\t\tt1 = src[0];\r\n\t\t\t\tt2 = src[1];\r\n\t\t\t\tdst[0] = t1 * tcmod->args[0] + t2 * tcmod->args[2] + tcmod->args[4];\r\n\t\t\t\tdst[1] = t1 * tcmod->args[1] + t2 * tcmod->args[3] + tcmod->args[5];\r\n\t\t\t}\r\n\t\t\tbreak;\r\n\r\n\t\tdefault:\r\n\t\t\tbreak;\r\n\t}\r\n}\r\n\r\nstatic void BE_GenerateTCMods(const shaderpass_t *pass, float *dest)\r\n{\r\n\tmesh_t *mesh;\r\n\tunsigned int mno;\r\n\tint i;\r\n\tfloat *src;\r\n\tfor (mno = 0; mno < shaderstate.nummeshes; mno++)\r\n\t{\r\n\t\tmesh = shaderstate.meshlist[mno];\r\n\t\tsrc = tcgen(pass, mesh->numvertexes, dest, mesh);\r\n\t\t//tcgen might return unmodified info\r\n\t\tif (pass->numtcmods)\r\n\t\t{\r\n\t\t\ttcmod(&pass->tcmods[0], mesh->numvertexes, src, dest, mesh);\r\n\t\t\tfor (i = 1; i < pass->numtcmods; i++)\r\n\t\t\t{\r\n\t\t\t\ttcmod(&pass->tcmods[i], mesh->numvertexes, dest, dest, mesh);\r\n\t\t\t}\r\n\t\t}\r\n\t\telse if (src != dest)\r\n\t\t{\r\n\t\t\tmemcpy(dest, src, sizeof(vec2_t)*mesh->numvertexes);\r\n\t\t}\r\n\t\tdest += mesh->numvertexes*2;\r\n\t}\r\n}\r\n\r\n//end texture coords\r\n/*******************************************************************************************************************/\r\nstatic void deformgen(const deformv_t *deformv, int cnt, vecV_t *src, vecV_t *dst, const mesh_t *mesh)\r\n{\r\n\tfloat *table;\r\n\tint j, k;\r\n\tfloat args[4];\r\n\tfloat deflect;\r\n\tswitch (deformv->type)\r\n\t{\r\n\tdefault:\r\n\tcase DEFORMV_NONE:\r\n\t\tif (src != dst)\r\n\t\t\tmemcpy(dst, src, sizeof(*src)*cnt);\r\n\t\tbreak;\r\n\r\n\tcase DEFORMV_WAVE:\r\n\t\tif (!mesh->normals_array)\r\n\t\t{\r\n\t\t\tif (src != dst)\r\n\t\t\t\tmemcpy(dst, src, sizeof(*src)*cnt);\r\n\t\t\treturn;\r\n\t\t}\r\n\t\targs[0] = deformv->func.args[0];\r\n\t\targs[1] = deformv->func.args[1];\r\n\t\targs[3] = deformv->func.args[2] + deformv->func.args[3] * shaderstate.curtime;\r\n\t\ttable = FTableForFunc(deformv->func.type);\r\n\r\n\t\tfor ( j = 0; j < cnt; j++ )\r\n\t\t{\r\n\t\t\tdeflect = deformv->args[0] * (src[j][0]+src[j][1]+src[j][2]) + args[3];\r\n\t\t\tdeflect = FTABLE_EVALUATE(table, deflect) * args[1] + args[0];\r\n\r\n\t\t\t// Deflect vertex along its normal by wave amount\r\n\t\t\tVectorMA(src[j], deflect, mesh->normals_array[j], dst[j]);\r\n\t\t}\r\n\t\tbreak;\r\n\r\n\tcase DEFORMV_NORMAL:\r\n\t\t//normal does not actually move the verts, but it does change the normals array\r\n\t\t//we don't currently support that.\r\n\t\tif (src != dst)\r\n\t\t\tmemcpy(dst, src, sizeof(*src)*cnt);\r\n/*\r\n\t\targs[0] = deformv->args[1] * shaderstate.curtime;\r\n\r\n\t\tfor ( j = 0; j < cnt; j++ )\r\n\t\t{\r\n\t\t\targs[1] = normalsArray[j][2] * args[0];\r\n\r\n\t\t\tdeflect = deformv->args[0] * R_FastSin(args[1]);\r\n\t\t\tnormalsArray[j][0] *= deflect;\r\n\t\t\tdeflect = deformv->args[0] * R_FastSin(args[1] + 0.25);\r\n\t\t\tnormalsArray[j][1] *= deflect;\r\n\t\t\tVectorNormalizeFast(normalsArray[j]);\r\n\t\t}\r\n*/\t\tbreak;\r\n\r\n\tcase DEFORMV_MOVE:\r\n\t\ttable = FTableForFunc(deformv->func.type);\r\n\t\tdeflect = deformv->func.args[2] + shaderstate.curtime * deformv->func.args[3];\r\n\t\tdeflect = FTABLE_EVALUATE(table, deflect) * deformv->func.args[1] + deformv->func.args[0];\r\n\r\n\t\tfor ( j = 0; j < cnt; j++ )\r\n\t\t\tVectorMA(src[j], deflect, deformv->args, dst[j]);\r\n\t\tbreak;\r\n\r\n\tcase DEFORMV_BULGE:\r\n\t\targs[0] = deformv->args[0]/(2*M_PI);\r\n\t\targs[1] = deformv->args[1];\r\n\t\targs[2] = shaderstate.curtime * deformv->args[2]/(2*M_PI);\r\n\r\n\t\tfor (j = 0; j < cnt; j++)\r\n\t\t{\r\n\t\t\tdeflect = R_FastSin(mesh->st_array[j][0]*args[0] + args[2])*args[1];\r\n\t\t\tdst[j][0] = src[j][0]+deflect*mesh->normals_array[j][0];\r\n\t\t\tdst[j][1] = src[j][1]+deflect*mesh->normals_array[j][1];\r\n\t\t\tdst[j][2] = src[j][2]+deflect*mesh->normals_array[j][2];\r\n\t\t}\r\n\t\tbreak;\r\n\r\n\tcase DEFORMV_AUTOSPRITE:\r\n\t\tif (mesh->numindexes < 6)\r\n\t\t\tbreak;\r\n\r\n\t\tfor (j = 0; j < cnt-3; j+=4, src+=4, dst+=4)\r\n\t\t{\r\n\t\t\tvec3_t mid, d;\r\n\t\t\tfloat radius;\r\n\t\t\tmid[0] = 0.25*(src[0][0] + src[1][0] + src[2][0] + src[3][0]);\r\n\t\t\tmid[1] = 0.25*(src[0][1] + src[1][1] + src[2][1] + src[3][1]);\r\n\t\t\tmid[2] = 0.25*(src[0][2] + src[1][2] + src[2][2] + src[3][2]);\r\n\t\t\tVectorSubtract(src[0], mid, d);\r\n\t\t\tradius = 2*VectorLength(d);\r\n\r\n\t\t\tfor (k = 0; k < 4; k++)\r\n\t\t\t{\r\n\t\t\t\tdst[k][0] = mid[0] + radius*((mesh->st_array[j+k][0]-0.5)*r_refdef.m_view[0+0]-(mesh->st_array[j+k][1]-0.5)*r_refdef.m_view[0+1]);\r\n\t\t\t\tdst[k][1] = mid[1] + radius*((mesh->st_array[j+k][0]-0.5)*r_refdef.m_view[4+0]-(mesh->st_array[j+k][1]-0.5)*r_refdef.m_view[4+1]);\r\n\t\t\t\tdst[k][2] = mid[2] + radius*((mesh->st_array[j+k][0]-0.5)*r_refdef.m_view[8+0]-(mesh->st_array[j+k][1]-0.5)*r_refdef.m_view[8+1]);\r\n\t\t\t}\r\n\t\t}\r\n\t\tbreak;\r\n\r\n\tcase DEFORMV_AUTOSPRITE2:\r\n\t\tif (mesh->numindexes < 6)\r\n\t\t\tbreak;\r\n\r\n\t\tfor (k = 0; k < mesh->numindexes; k += 6)\r\n\t\t{\r\n\t\t\tint long_axis, short_axis;\r\n\t\t\tvec3_t axis;\r\n\t\t\tfloat len[3];\r\n\t\t\tmat3_t m0, m1, m2, result;\r\n\t\t\tfloat *quad[4];\r\n\t\t\tvec3_t rot_centre, tv, tv2;\r\n\r\n\t\t\tquad[0] = (float *)(src + mesh->indexes[k+0]);\r\n\t\t\tquad[1] = (float *)(src + mesh->indexes[k+1]);\r\n\t\t\tquad[2] = (float *)(src + mesh->indexes[k+2]);\r\n\r\n\t\t\tfor (j = 2; j >= 0; j--)\r\n\t\t\t{\r\n\t\t\t\tquad[3] = (float *)(src + mesh->indexes[k+3+j]);\r\n\t\t\t\tif (!VectorEquals (quad[3], quad[0]) &&\r\n\t\t\t\t\t!VectorEquals (quad[3], quad[1]) &&\r\n\t\t\t\t\t!VectorEquals (quad[3], quad[2]))\r\n\t\t\t\t{\r\n\t\t\t\t\tbreak;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\t// build a matrix were the longest axis of the billboard is the Y-Axis\r\n\t\t\tVectorSubtract(quad[1], quad[0], m0[0]);\r\n\t\t\tVectorSubtract(quad[2], quad[0], m0[1]);\r\n\t\t\tVectorSubtract(quad[2], quad[1], m0[2]);\r\n\t\t\tlen[0] = DotProduct(m0[0], m0[0]);\r\n\t\t\tlen[1] = DotProduct(m0[1], m0[1]);\r\n\t\t\tlen[2] = DotProduct(m0[2], m0[2]);\r\n\r\n\t\t\tif ((len[2] > len[1]) && (len[2] > len[0]))\r\n\t\t\t{\r\n\t\t\t\tif (len[1] > len[0])\r\n\t\t\t\t{\r\n\t\t\t\t\tlong_axis = 1;\r\n\t\t\t\t\tshort_axis = 0;\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\tlong_axis = 0;\r\n\t\t\t\t\tshort_axis = 1;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\telse if ((len[1] > len[2]) && (len[1] > len[0]))\r\n\t\t\t{\r\n\t\t\t\tif (len[2] > len[0])\r\n\t\t\t\t{\r\n\t\t\t\t\tlong_axis = 2;\r\n\t\t\t\t\tshort_axis = 0;\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\tlong_axis = 0;\r\n\t\t\t\t\tshort_axis = 2;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\telse //if ( (len[0] > len[1]) && (len[0] > len[2]) )\r\n\t\t\t{\r\n\t\t\t\tif (len[2] > len[1])\r\n\t\t\t\t{\r\n\t\t\t\t\tlong_axis = 2;\r\n\t\t\t\t\tshort_axis = 1;\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\tlong_axis = 1;\r\n\t\t\t\t\tshort_axis = 2;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\tif (DotProduct(m0[long_axis], m0[short_axis]))\r\n\t\t\t{\r\n\t\t\t\tVectorNormalize2(m0[long_axis], axis);\r\n\t\t\t\tVectorCopy(axis, m0[1]);\r\n\r\n\t\t\t\tif (axis[0] || axis[1])\r\n\t\t\t\t{\r\n\t\t\t\t\tVectorVectors(m0[1], m0[2], m0[0]);\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\tVectorVectors(m0[1], m0[0], m0[2]);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tVectorNormalize2(m0[long_axis], axis);\r\n\t\t\t\tVectorNormalize2(m0[short_axis], m0[0]);\r\n\t\t\t\tVectorCopy(axis, m0[1]);\r\n\t\t\t\tCrossProduct(m0[0], m0[1], m0[2]);\r\n\t\t\t}\r\n\r\n\t\t\tfor (j = 0; j < 3; j++)\r\n\t\t\t\trot_centre[j] = (quad[0][j] + quad[1][j] + quad[2][j] + quad[3][j]) * 0.25;\r\n\r\n\t\t\tif (shaderstate.curentity)\r\n\t\t\t{\r\n\t\t\t\tVectorAdd(shaderstate.curentity->origin, rot_centre, tv);\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tVectorCopy(rot_centre, tv);\r\n\t\t\t}\r\n\t\t\tVectorSubtract(r_origin, tv, tv);\r\n\r\n\t\t\t// filter any longest-axis-parts off the camera-direction\r\n\t\t\tdeflect = -DotProduct(tv, axis);\r\n\r\n\t\t\tVectorMA(tv, deflect, axis, m1[2]);\r\n\t\t\tVectorNormalizeFast(m1[2]);\r\n\t\t\tVectorCopy(axis, m1[1]);\r\n\t\t\tCrossProduct(m1[1], m1[2], m1[0]);\r\n\r\n\t\t\tMatrix3_Transpose(m1, m2);\r\n\t\t\tMatrix3_Multiply(m2, m0, result);\r\n\r\n\t\t\tfor (j = 0; j < 4; j++)\r\n\t\t\t{\r\n\t\t\t\tint v = ((vecV_t*)quad[j]-src);\r\n\t\t\t\tVectorSubtract(quad[j], rot_centre, tv);\r\n\t\t\t\tMatrix3_Multiply_Vec3((void *)result, tv, tv2);\r\n\t\t\t\tVectorAdd(rot_centre, tv2, dst[v]);\r\n\t\t\t}\r\n\t\t}\r\n\t\tbreak;\r\n\r\n//\tcase DEFORMV_PROJECTION_SHADOW:\r\n//\t\tbreak;\r\n\t}\r\n}\r\n\r\n\r\nstatic void BE_ApplyUniforms(program_t *prog, struct programpermu_s *permu)\r\n{\r\n\tID3D11Buffer *cbuf[3] =\r\n\t{\r\n\t\tshaderstate.ecbuffers[shaderstate.ecbufferidx],\t//entity buffer\r\n\t\tshaderstate.vcbuffer,\t\t\t\t\t\t\t//view buffer that changes rarely\r\n\t\tshaderstate.lcbuffer\t\t\t\t\t\t\t//light buffer that changes rarelyish\r\n\t};\r\n\t//FIXME: how many of these calls can we avoid?\r\n\tID3D11DeviceContext_IASetInputLayout(d3ddevctx, permu->h.hlsl.layouts[shaderstate.stream_rgbaf]);\r\n\tID3D11DeviceContext_VSSetShader(d3ddevctx, permu->h.hlsl.vert, NULL, 0);\r\n\tID3D11DeviceContext_HSSetShader(d3ddevctx, permu->h.hlsl.hull, NULL, 0);\r\n\tID3D11DeviceContext_DSSetShader(d3ddevctx, permu->h.hlsl.domain, NULL, 0);\r\n\tID3D11DeviceContext_GSSetShader(d3ddevctx, permu->h.hlsl.geom, NULL, 0);\r\n\tID3D11DeviceContext_PSSetShader(d3ddevctx, permu->h.hlsl.frag, NULL, 0);\r\n\tID3D11DeviceContext_IASetPrimitiveTopology(d3ddevctx, permu->h.hlsl.topology);\r\n\r\n\tID3D11DeviceContext_VSSetConstantBuffers(d3ddevctx, 0, 3, cbuf);\r\n\tif (permu->h.hlsl.hull)\r\n\t\tID3D11DeviceContext_HSSetConstantBuffers(d3ddevctx, 0, 3, cbuf);\r\n\tif (permu->h.hlsl.domain)\r\n\t\tID3D11DeviceContext_DSSetConstantBuffers(d3ddevctx, 0, 3, cbuf);\r\n\tif (permu->h.hlsl.geom)\r\n\t\tID3D11DeviceContext_GSSetConstantBuffers(d3ddevctx, 0, 3, cbuf);\r\n\tID3D11DeviceContext_PSSetConstantBuffers(d3ddevctx, 0, 3, cbuf);\r\n}\r\n\r\nstatic void BE_RenderMeshProgram(program_t *p, shaderpass_t *pass, unsigned int vertcount, unsigned int idxfirst, unsigned int idxcount)\r\n{\r\n\tint passno;\r\n\tint perm = 0;\r\n\r\n\tstruct programpermu_s *pp;\r\n\r\n\tif (TEXLOADED(shaderstate.curtexnums->bump))\r\n\t\tperm |= PERMUTATION_BUMPMAP;\r\n\tif (TEXLOADED(shaderstate.curtexnums->fullbright))\r\n\t\tperm |= PERMUTATION_FULLBRIGHT;\r\n\tif ((TEXLOADED(shaderstate.curtexnums->upperoverlay) || TEXLOADED(shaderstate.curtexnums->loweroverlay)))\r\n\t\tperm |= PERMUTATION_UPPERLOWER;\r\n\tif (r_refdef.globalfog.density)\r\n\t\tperm |= PERMUTATION_FOG;\r\n//\tif (r_glsl_offsetmapping.ival && TEXLOADED(shaderstate.curtexnums->bump))\r\n//\t\tperm |= PERMUTATION_OFFSET;\r\n\r\n\tperm &= p->supportedpermutations;\r\n\tpp = p->permu[perm];\r\n\tif (!pp)\r\n\t{\r\n\t\tp->permu[perm] = pp = Shader_LoadPermutation(p, perm);\r\n\t\tif (!pp)\r\n\t\t{\t//failed? copy from 0 so we don't keep re-failing\r\n\t\t\tpp = p->permu[perm] = p->permu[0];\r\n\t\t}\r\n\t}\r\n\r\n\tBE_ApplyUniforms(p, pp);\r\n\r\n\tD3D11BE_ApplyShaderBits(pass->shaderbits, &pass->becache);\r\n\r\n\t/*activate tmus*/\r\n\tfor (passno = 0; passno < pass->numMergedPasses; passno++)\r\n\t{\r\n\t\tSelectPassTexture(passno, pass+passno);\r\n\t}\r\n\t/*deactivate any extras*/\r\n\tfor (; passno < shaderstate.lastpasscount; passno++)\r\n\t{\r\n\t\tshaderstate.pendingtextures[passno] = NULL;\r\n\t\tshaderstate.textureschanged = true;\r\n\t}\r\n\tif (shaderstate.textureschanged)\r\n\t\tID3D11DeviceContext_PSSetShaderResources(d3ddevctx, 0, max(passno, pass->numMergedPasses), shaderstate.pendingtextures);\r\n\tshaderstate.lastpasscount = pass->numMergedPasses;\r\n\r\n\tID3D11DeviceContext_DrawIndexed(d3ddevctx, idxcount, idxfirst, 0);\r\n\tRQuantAdd(RQUANT_DRAWS, 1);\r\n}\r\n\r\nstatic void D3D11BE_Cull(unsigned int cullflags, int depthbias, float depthfactor)\r\n{\r\n\tHRESULT hr;\r\n\tD3D11_RASTERIZER_DESC rasterdesc;\r\n\tID3D11RasterizerState *newrasterizerstate;\r\n\r\n\tif (shaderstate.flags & BEF_FORCETWOSIDED)\r\n\t\tcullflags = 0;\r\n\telse if (cullflags)\r\n\t\tcullflags ^= r_refdef.flipcull;\r\n\r\n#define SHADER_CULL_WIREFRAME SHADER_NODRAW //borrow a flag...\r\n\tif (shaderstate.mode == BEM_WIREFRAME)\r\n\t\tcullflags |= SHADER_CULL_WIREFRAME;\r\n\r\n\tif (shaderstate.curcull != cullflags || shaderstate.depthbias != depthbias || shaderstate.depthfactor != depthfactor)\r\n\t{\r\n\t\tshaderstate.curcull = cullflags;\r\n\t\tshaderstate.depthbias = depthbias;\r\n\t\tshaderstate.depthfactor = depthfactor;\r\n\r\n\r\n\t\trasterdesc.AntialiasedLineEnable = false;\r\n\r\n\t\tif (shaderstate.curcull & 1)\r\n\t\t{\r\n\t\t\tif (shaderstate.curcull & SHADER_CULL_FRONT)\r\n\t\t\t\trasterdesc.CullMode = D3D11_CULL_FRONT;\r\n\t\t\telse if (shaderstate.curcull & SHADER_CULL_BACK)\r\n\t\t\t\trasterdesc.CullMode = D3D11_CULL_BACK;\r\n\t\t\telse\r\n\t\t\t\trasterdesc.CullMode = D3D11_CULL_NONE;\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tif (shaderstate.curcull & SHADER_CULL_FRONT)\r\n\t\t\t\trasterdesc.CullMode = D3D11_CULL_BACK;\r\n\t\t\telse if (shaderstate.curcull & SHADER_CULL_BACK)\r\n\t\t\t\trasterdesc.CullMode = D3D11_CULL_FRONT;\r\n\t\t\telse\r\n\t\t\t\trasterdesc.CullMode = D3D11_CULL_NONE;\r\n\t\t}\r\n\r\n\r\n\t\trasterdesc.DepthBias = shaderstate.depthbias;\r\n\t\trasterdesc.SlopeScaledDepthBias = shaderstate.depthfactor;\r\n\t\trasterdesc.DepthBiasClamp = 0.0f;\r\n\t\trasterdesc.DepthClipEnable = true;\r\n\t\trasterdesc.FillMode = (shaderstate.curcull&SHADER_CULL_WIREFRAME)?D3D11_FILL_WIREFRAME:D3D11_FILL_SOLID;\r\n\t\trasterdesc.FrontCounterClockwise = false;\r\n\t\trasterdesc.MultisampleEnable = false;\r\n\t\trasterdesc.ScissorEnable = true;\r\n\r\n\t\tif (FAILED(hr=ID3D11Device_CreateRasterizerState(pD3DDev11, &rasterdesc, &newrasterizerstate)))\r\n\t\t{\r\n\t\t\tif (hr == DXGI_ERROR_DEVICE_REMOVED)\r\n\t\t\t{\r\n\t\t\t\thr = ID3D11Device_GetDeviceRemovedReason(pD3DDev11);\r\n\t\t\t\tswitch(hr)\r\n\t\t\t\t{\r\n\t\t\t\tcase DXGI_ERROR_DEVICE_HUNG:\r\n\t\t\t\t\tSys_Error(\"DXGI_ERROR_DEVICE_HUNG\\nThe application's device failed due to badly formed commands sent by the application.\\n\");\r\n\t\t\t\t\tbreak;\r\n\t\t\t\tcase DXGI_ERROR_DEVICE_REMOVED:\r\n\t\t\t\t\tSys_Error(\"DXGI_ERROR_DEVICE_REMOVED\\nThe video card has been physically removed from the system, or a driver upgrade for the video card has occurred.\\n\");\r\n\t\t\t\t\tbreak;\r\n\t\t\t\tcase DXGI_ERROR_DEVICE_RESET:\r\n\t\t\t\t\tSys_Error(\"DXGI_ERROR_DEVICE_RESET\\nThe device failed due to a badly formed command.\\n\");\r\n\t\t\t\t\tbreak;\r\n\t\t\t\tcase DXGI_ERROR_DRIVER_INTERNAL_ERROR:\r\n\t\t\t\t\tSys_Error(\"DXGI_ERROR_DRIVER_INTERNAL_ERROR\\nThe driver encountered a problem and was put into the device removed state.\\n\");\r\n\t\t\t\t\tbreak;\r\n\t\t\t\tcase DXGI_ERROR_INVALID_CALL:\r\n\t\t\t\t\tSys_Error(\"invalid call! oh noes!\\n\");\r\n\t\t\t\t\tbreak;\r\n\t\t\t\tdefault:\r\n\t\t\t\t\tbreak;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t\tCon_Printf(\"ID3D11Device_CreateRasterizerState failed\\n\");\r\n\t\t\treturn;\r\n\t\t}\r\n\t\tID3D11DeviceContext_RSSetState(d3ddevctx, newrasterizerstate);\r\n\t\tID3D11RasterizerState_Release(newrasterizerstate);\r\n\t}\r\n}\r\n\r\nstatic void BE_DrawMeshChain_Internal(void)\r\n{\r\n\tshader_t *altshader;\r\n\tunsigned int vertcount, idxcount, idxfirst;\r\n\tmesh_t *m;\r\n\tqboolean vblends;\t//software\r\n//\tvoid *map;\r\n//\tint i;\r\n\tunsigned int mno;\r\n\tunsigned int passno;\r\n//\textern cvar_t r_polygonoffset_submodel_factor;\r\n//\tfloat pushdepth;\r\n//\tfloat pushfactor;\r\n\r\n\taltshader = shaderstate.curshader;\r\n\tswitch (shaderstate.mode)\r\n\t{\r\n\tcase BEM_LIGHT:\r\n\t\taltshader = shaderstate.shader_rtlight[shaderstate.curlmode];\r\n\t\tbreak;\r\n\tcase BEM_DEPTHONLY:\r\n\t\taltshader = shaderstate.curshader->bemoverrides[bemoverride_depthonly];\r\n\t\tif (!altshader)\r\n\t\t\taltshader = shaderstate.depthonly;\r\n\t\tbreak;\r\n\tdefault:\r\n\tcase BEM_STANDARD:\r\n\t\taltshader = shaderstate.curshader;\r\n\t\tbreak;\r\n\tcase BEM_WIREFRAME:\r\n\t\taltshader = R_RegisterShader(\"wireframe\", SUF_NONE, \r\n\t\t\t\"{\\n\"\r\n\t\t\t\t\"{\\n\"\r\n\t\t\t\t\t\"map $whiteimage\\n\"\r\n\t\t\t\t\"}\\n\"\r\n\t\t\t\"}\\n\"\r\n\t\t\t);\r\n\t\tbreak;\r\n\t}\r\n\tif (!altshader)\r\n\t\treturn;\r\n\r\n\tif (0)//shaderstate.force2d)\r\n\t{\r\n\t\tRQuantAdd(RQUANT_2DBATCHES, 1);\r\n\t}\r\n\telse if (shaderstate.curentity == &r_worldentity)\r\n\t{\r\n\t\tRQuantAdd(RQUANT_WORLDBATCHES, 1);\r\n\t}\r\n\telse\r\n\t{\r\n\t\tRQuantAdd(RQUANT_ENTBATCHES, 1);\r\n\t}\r\n\r\n\tD3D11BE_Cull(shaderstate.curshader->flags & (SHADER_CULL_FRONT | SHADER_CULL_BACK), shaderstate.curshader->polyoffset.unit, shaderstate.curshader->polyoffset.factor);\r\n\r\n\tmemset(shaderstate.stream_buffer, 0, sizeof(shaderstate.stream_buffer));\r\n\r\n\t//if this flag is set, then we have to generate our own arrays. to avoid processing extra verticies this may require that we re-pack the verts\r\n\tif (shaderstate.meshlist[0]->xyz2_array)// && !altshader->prog)\r\n\t{\r\n\t\tvblends = true;\r\n\t\tshaderstate.batchvbo = NULL;\r\n\t}\r\n\telse\r\n\t{\r\n\t\tvblends = false;\r\n\t\tif (altshader->flags & SHADER_NEEDSARRAYS)\r\n\t\t\tshaderstate.batchvbo = NULL;\r\n\t}\r\n\r\n\r\n\t/*so are index buffers*/\r\n\tif (shaderstate.batchvbo && shaderstate.nummeshes == 1)\r\n\t{\r\n\t\tm = shaderstate.meshlist[0];\r\n\r\n\t\tID3D11DeviceContext_IASetIndexBuffer(d3ddevctx, shaderstate.batchvbo->indicies.d3d.buff, DXGI_FORMAT_INDEX_UINT, shaderstate.batchvbo->indicies.d3d.offs);\r\n\t\tidxfirst = m->vbofirstelement;\r\n\r\n\t\tvertcount = m->vbofirstvert + m->numvertexes;\r\n\t\tidxcount = m->numindexes;\r\n\t}\r\n\telse if (shaderstate.batchvbo)\r\n\t{\t/*however, we still want to try to avoid discontinuities, because that would otherwise be more draw calls. we can have gaps in verts though*/\r\n\t\tindex_t *map;\r\n\t\tID3D11Buffer *buf;\r\n\t\tunsigned int i;\r\n\t\tunsigned int byteofs;\r\n\t\tvertcount = shaderstate.batchvbo->vertcount;\r\n\t\tfor (mno = 0, idxcount = 0; mno < shaderstate.nummeshes; mno++)\r\n\t\t{\r\n\t\t\tm = shaderstate.meshlist[mno];\r\n\t\t\tidxcount += m->numindexes;\r\n\t\t}\r\n\t\tbyteofs = allocindexbuffer(&map, &buf, idxcount);\r\n\t\tfor (mno = 0; mno < shaderstate.nummeshes; mno++)\r\n\t\t{\r\n\t\t\tm = shaderstate.meshlist[mno];\r\n\t\t\tfor (i = 0; i < m->numindexes; i++)\r\n\t\t\t\tmap[i] = m->indexes[i]+m->vbofirstvert;\r\n\t\t\tmap += m->numindexes;\r\n\t\t}\r\n\t\tID3D11DeviceContext_Unmap(d3ddevctx, (ID3D11Resource*)buf, 0);\r\n\t\tID3D11DeviceContext_IASetIndexBuffer(d3ddevctx, buf, DXGI_FORMAT_INDEX_UINT, byteofs);\r\n\t\tidxfirst = 0;\r\n\t}\r\n\telse\r\n\t{\t/*we're going to be using dynamic array stuff here, so generate an index array list that has no vertex gaps*/\r\n\t\tindex_t *map;\r\n\t\tID3D11Buffer *buf;\r\n\t\tunsigned int i;\r\n\t\tunsigned int byteofs;\r\n\t\tfor (mno = 0, vertcount = 0, idxcount = 0; mno < shaderstate.nummeshes; mno++)\r\n\t\t{\r\n\t\t\tm = shaderstate.meshlist[mno];\r\n\t\t\tvertcount += m->numvertexes;\r\n\t\t\tidxcount += m->numindexes;\r\n\t\t}\r\n\r\n\t\tbyteofs = allocindexbuffer(&map, &buf, idxcount);\r\n\t\tfor (mno = 0, vertcount = 0; mno < shaderstate.nummeshes; mno++)\r\n\t\t{\r\n\t\t\tm = shaderstate.meshlist[mno];\r\n\t\t\tfor (i = 0; i < m->numindexes; i++)\r\n\t\t\t\tmap[i] = m->indexes[i]+vertcount;\r\n\t\t\tmap += m->numindexes;\r\n\t\t\tvertcount += m->numvertexes;\r\n\t\t}\r\n\t\tID3D11DeviceContext_Unmap(d3ddevctx, (ID3D11Resource*)buf, 0);\r\n\t\tID3D11DeviceContext_IASetIndexBuffer(d3ddevctx, buf, DXGI_FORMAT_INDEX_UINT, byteofs);\r\n\t\tidxfirst = 0;\r\n\t}\r\n\r\n\t/*vertex buffers are common to all passes*/\r\n\tif (shaderstate.batchvbo && !vblends)\r\n\t{\r\n\t\tshaderstate.stream_buffer[D3D11_BUFF_POS] = shaderstate.batchvbo->coord.d3d.buff;\r\n\t\tshaderstate.stream_offset[D3D11_BUFF_POS] = shaderstate.batchvbo->coord.d3d.offs;\r\n\t\tshaderstate.stream_stride[D3D11_BUFF_POS] = sizeof(dx11_vbovdata_t);\r\n\t}\r\n\telse\r\n\t{\r\n\t\tID3D11Buffer *buf;\r\n\t\tvecV_t *map;\r\n\t\tconst mesh_t *m;\r\n\t\tunsigned int mno;\r\n\t\tunsigned int offset, i;\r\n\t\tallocvertexbuffer(&buf, &offset, (void**)&map, vertcount*sizeof(vecV_t));\r\n\r\n\t\tif (vblends)\r\n\t\t{\r\n\t\t\tfor (mno = 0; mno < shaderstate.nummeshes; mno++)\r\n\t\t\t{\r\n\t\t\t\tconst mesh_t *m = shaderstate.meshlist[mno];\r\n\t\t\t\tvecV_t *ov = shaderstate.curshader->numdeforms?tmpbuf:map;\r\n\t\t\t\tvecV_t *iv1 = m->xyz_array;\r\n\t\t\t\tvecV_t *iv2 = m->xyz2_array;\r\n\t\t\t\tfloat w1 = m->xyz_blendw[0];\r\n\t\t\t\tfloat w2 = m->xyz_blendw[1];\r\n\t\t\t\tfor (i = 0; i < m->numvertexes; i++)\r\n\t\t\t\t{\r\n\t\t\t\t\tov[i][0] = iv1[i][0]*w1 + iv2[i][0]*w2;\r\n\t\t\t\t\tov[i][1] = iv1[i][1]*w1 + iv2[i][1]*w2;\r\n\t\t\t\t\tov[i][2] = iv1[i][2]*w1 + iv2[i][2]*w2;\r\n\t\t\t\t}\r\n\t\t\t\tif (shaderstate.curshader->numdeforms)\r\n\t\t\t\t{\r\n\t\t\t\t\tfor (i = 0; i < shaderstate.curshader->numdeforms-1; i++)\r\n\t\t\t\t\t\tdeformgen(&shaderstate.curshader->deforms[i], m->numvertexes, tmpbuf, tmpbuf, m);\r\n\t\t\t\t\tdeformgen(&shaderstate.curshader->deforms[i], m->numvertexes, tmpbuf, (vecV_t*)map, m);\r\n\t\t\t\t}\r\n\t\t\t\tmap += m->numvertexes;\r\n\t\t\t}\r\n\t\t}\r\n\t\telse if (shaderstate.curshader->numdeforms > 1)\r\n\t\t{\t//horrible code, because multiple deforms would otherwise READ from the gpu memory\r\n\t\t\tfor (mno = 0; mno < shaderstate.nummeshes; mno++)\r\n\t\t\t{\r\n\t\t\t\tm = shaderstate.meshlist[mno];\r\n\t\t\t\tdeformgen(&shaderstate.curshader->deforms[0], m->numvertexes, m->xyz_array, tmpbuf, m);\r\n\t\t\t\tfor (i = 1; i < shaderstate.curshader->numdeforms-1; i++)\r\n\t\t\t\t\tdeformgen(&shaderstate.curshader->deforms[i], m->numvertexes, tmpbuf, tmpbuf, m);\r\n\t\t\t\tdeformgen(&shaderstate.curshader->deforms[i], m->numvertexes, tmpbuf, (vecV_t*)map, m);\r\n\t\t\t\tmap += m->numvertexes;\r\n\t\t\t}\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tfor (mno = 0; mno < shaderstate.nummeshes; mno++)\r\n\t\t\t{\r\n\t\t\t\tm = shaderstate.meshlist[mno];\r\n\t\t\t\tdeformgen(&shaderstate.curshader->deforms[0], m->numvertexes, m->xyz_array, (vecV_t*)map, m);\r\n\t\t\t\tmap += m->numvertexes;\r\n\t\t\t}\r\n\t\t}\r\n\t\tID3D11DeviceContext_Unmap(d3ddevctx, (ID3D11Resource*)buf, 0);\r\n\t\tshaderstate.stream_buffer[D3D11_BUFF_POS] = buf;\r\n\t\tshaderstate.stream_offset[D3D11_BUFF_POS] = offset;\r\n\t\tshaderstate.stream_stride[D3D11_BUFF_POS] = sizeof(vecV_t);\r\n\t}\r\n\r\n\tif (altshader->prog)\r\n\t{\r\n\t\tif (shaderstate.batchvbo)\r\n\t\t{\r\n\t\t\tshaderstate.stream_buffer[D3D11_BUFF_COL] = shaderstate.batchvbo->colours[0].d3d.buff;\r\n\t\t\tshaderstate.stream_offset[D3D11_BUFF_COL] = shaderstate.batchvbo->colours[0].d3d.offs;\r\n\t\t\tshaderstate.stream_stride[D3D11_BUFF_COL] = sizeof(dx11_vbovdata_t);\r\n\t\t\tshaderstate.stream_rgbaf = false;\r\n\t\t\tshaderstate.stream_buffer[D3D11_BUFF_TC] = shaderstate.batchvbo->texcoord.d3d.buff;\r\n\t\t\tshaderstate.stream_offset[D3D11_BUFF_TC] = shaderstate.batchvbo->texcoord.d3d.offs;\r\n\t\t\tshaderstate.stream_stride[D3D11_BUFF_TC] = sizeof(dx11_vbovdata_t);\r\n\t\t\tshaderstate.stream_buffer[D3D11_BUFF_LMTC] = shaderstate.batchvbo->lmcoord[0].d3d.buff;\r\n\t\t\tshaderstate.stream_offset[D3D11_BUFF_LMTC] = shaderstate.batchvbo->lmcoord[0].d3d.offs;\r\n\t\t\tshaderstate.stream_stride[D3D11_BUFF_LMTC] = sizeof(dx11_vbovdata_t);\r\n\t\t\tshaderstate.stream_buffer[D3D11_BUFF_NORM] = shaderstate.batchvbo->normals.d3d.buff;\r\n\t\t\tshaderstate.stream_offset[D3D11_BUFF_NORM] = shaderstate.batchvbo->normals.d3d.offs;\r\n\t\t\tshaderstate.stream_stride[D3D11_BUFF_NORM] = sizeof(dx11_vbovdata_t);\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tID3D11Buffer *buf;\r\n\t\t\tvec2_t *map;\r\n\t\t\tvec2_t *lmmap;\r\n\t\t\tconst mesh_t *m;\r\n\t\t\tunsigned int mno;\r\n\t\t\tunsigned int offset, i;\r\n\r\n\t\t\tif (shaderstate.meshlist[0]->colors4f_array[0])\r\n\t\t\t{\r\n\t\t\t\tvec4_t *map;\r\n\t\t\t\tallocvertexbuffer(&buf, &offset, (void**)&map, vertcount*sizeof(*map));\r\n\t\t\t\tfor (mno = 0; mno < shaderstate.nummeshes; mno++)\r\n\t\t\t\t{\r\n\t\t\t\t\tm = shaderstate.meshlist[mno];\r\n\t\t\t\t\tmemcpy(map, m->colors4f_array[0], sizeof(*map)*m->numvertexes);\r\n\t\t\t\t\tmap += m->numvertexes;\r\n\t\t\t\t}\r\n\t\t\t\tID3D11DeviceContext_Unmap(d3ddevctx, (ID3D11Resource*)buf, 0);\r\n\t\t\t\tshaderstate.stream_buffer[D3D11_BUFF_COL] = buf;\r\n\t\t\t\tshaderstate.stream_offset[D3D11_BUFF_COL] = offset;\r\n\t\t\t\tshaderstate.stream_stride[D3D11_BUFF_COL] = sizeof(*map);\r\n\t\t\t\tshaderstate.stream_rgbaf = true;\r\n\t\t\t}\r\n\t\t\telse if (shaderstate.meshlist[0]->colors4b_array)\r\n\t\t\t{\r\n\t\t\t\tbyte_vec4_t *map;\r\n\t\t\t\tallocvertexbuffer(&buf, &offset, (void**)&map, vertcount*sizeof(byte_vec4_t));\r\n\t\t\t\tfor (mno = 0; mno < shaderstate.nummeshes; mno++)\r\n\t\t\t\t{\r\n\t\t\t\t\tm = shaderstate.meshlist[mno];\r\n\t\t\t\t\tfor (i = 0; i < m->numvertexes; i++)\r\n\t\t\t\t\t\tmemcpy(map, m->colors4b_array, vertcount*sizeof(byte_vec4_t));\r\n\t\t\t\t\tmap += m->numvertexes;\r\n\t\t\t\t}\r\n\t\t\t\tID3D11DeviceContext_Unmap(d3ddevctx, (ID3D11Resource*)buf, 0);\r\n\t\t\t\tshaderstate.stream_buffer[D3D11_BUFF_COL] = buf;\r\n\t\t\t\tshaderstate.stream_offset[D3D11_BUFF_COL] = offset;\r\n\t\t\t\tshaderstate.stream_stride[D3D11_BUFF_COL] = sizeof(byte_vec4_t);\r\n\t\t\t\tshaderstate.stream_rgbaf = false;\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tshaderstate.stream_buffer[D3D11_BUFF_COL] = 0;\r\n\t\t\t\tshaderstate.stream_offset[D3D11_BUFF_COL] = 0;\r\n\t\t\t\tshaderstate.stream_stride[D3D11_BUFF_COL] = 0;\r\n\t\t\t\tshaderstate.stream_rgbaf = false;\r\n\t\t\t}\r\n\r\n\t\t\tif (shaderstate.meshlist[0]->lmst_array[0])\r\n\t\t\t{\r\n\t\t\t\tallocvertexbuffer(&buf, &offset, (void**)&map, vertcount*sizeof(vec4_t));\r\n\t\t\t\tlmmap = map + vertcount;\r\n\t\t\t\tfor (mno = 0; mno < shaderstate.nummeshes; mno++)\r\n\t\t\t\t{\r\n\t\t\t\t\tm = shaderstate.meshlist[mno];\r\n\t\t\t\t\tfor (i = 0; i < m->numvertexes; i++)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tmap[i][0] = m->st_array[i][0];\r\n\t\t\t\t\t\tmap[i][1] = m->st_array[i][1];\r\n\t\t\t\t\t\tlmmap[i][0] = m->lmst_array[0][i][0];\r\n\t\t\t\t\t\tlmmap[i][1] = m->lmst_array[0][i][1];\r\n\t\t\t\t\t}\r\n\t\t\t\t\tmap += m->numvertexes;\r\n\t\t\t\t\tlmmap += m->numvertexes;\r\n\t\t\t\t}\r\n\t\t\t\tID3D11DeviceContext_Unmap(d3ddevctx, (ID3D11Resource*)buf, 0);\r\n\t\t\t\tshaderstate.stream_buffer[D3D11_BUFF_TC] = buf;\r\n\t\t\t\tshaderstate.stream_offset[D3D11_BUFF_TC] = offset;\r\n\t\t\t\tshaderstate.stream_stride[D3D11_BUFF_TC] = sizeof(vec2_t);\r\n\t\t\t\tshaderstate.stream_buffer[D3D11_BUFF_LMTC] = buf;\r\n\t\t\t\tshaderstate.stream_offset[D3D11_BUFF_LMTC] = offset+vertcount*sizeof(vec2_t);\r\n\t\t\t\tshaderstate.stream_stride[D3D11_BUFF_LMTC] = sizeof(vec2_t);\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tallocvertexbuffer(&buf, &offset, (void**)&map, vertcount*sizeof(vec2_t));\r\n\t\t\t\tfor (mno = 0; mno < shaderstate.nummeshes; mno++)\r\n\t\t\t\t{\r\n\t\t\t\t\tm = shaderstate.meshlist[mno];\r\n\t\t\t\t\tfor (i = 0; i < m->numvertexes; i++)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tmap[i][0] = m->st_array[i][0];\r\n\t\t\t\t\t\tmap[i][1] = m->st_array[i][1];\r\n\t\t\t\t\t}\r\n\t\t\t\t\tmap += m->numvertexes;\r\n\t\t\t\t}\r\n\t\t\t\tID3D11DeviceContext_Unmap(d3ddevctx, (ID3D11Resource*)buf, 0);\r\n\t\t\t\tshaderstate.stream_buffer[D3D11_BUFF_TC] = buf;\r\n\t\t\t\tshaderstate.stream_offset[D3D11_BUFF_TC] = offset;\r\n\t\t\t\tshaderstate.stream_stride[D3D11_BUFF_TC] = sizeof(vec2_t);\r\n\r\n\t\t\t\tshaderstate.stream_buffer[D3D11_BUFF_LMTC] = NULL;\r\n\t\t\t\tshaderstate.stream_offset[D3D11_BUFF_LMTC] = 0;\r\n\t\t\t\tshaderstate.stream_stride[D3D11_BUFF_LMTC] = 0;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tID3D11DeviceContext_IASetVertexBuffers(d3ddevctx, 0, D3D11_BUFF_MAX, (ID3D11Buffer**)shaderstate.stream_buffer, shaderstate.stream_stride, shaderstate.stream_offset);\r\n\t\tBE_RenderMeshProgram(altshader->prog, altshader->passes, vertcount, idxfirst, idxcount);\r\n\t}\r\n\telse if (1)\r\n\t{\r\n\t\tshaderpass_t *p;\r\n\r\n\t\t//d3d11 has no fixed function pipeline. we emulate it.\r\n\r\n\t\tfor (passno = 0; passno < altshader->numpasses; passno += p->numMergedPasses)\r\n\t\t{\r\n\t\t\tint emumode;\r\n\t\t\tp = &altshader->passes[passno];\r\n\r\n\t\t\temumode = 0;\r\n\t\t\temumode = (p->shaderbits & SBITS_ATEST_BITS) >> SBITS_ATEST_SHIFT;\r\n\r\n\t\t\tBE_GenerateColourMods(vertcount, p);\r\n\r\n\t\t\tif (shaderstate.batchvbo)\r\n\t\t\t{\t//texcoords are all compatible with static arrays, supposedly\r\n\t\t\t\tif (p->tcgen == TC_GEN_LIGHTMAP)\r\n\t\t\t\t{\r\n\t\t\t\t\tshaderstate.stream_buffer[D3D11_BUFF_TC] = shaderstate.batchvbo->lmcoord[0].d3d.buff;\r\n\t\t\t\t\tshaderstate.stream_offset[D3D11_BUFF_TC] = shaderstate.batchvbo->lmcoord[0].d3d.offs;\r\n\t\t\t\t\tshaderstate.stream_stride[D3D11_BUFF_TC] = sizeof(dx11_vbovdata_t);\r\n\t\t\t\t}\r\n\t\t\t\telse if (p->tcgen == TC_GEN_BASE)\r\n\t\t\t\t{\r\n\t\t\t\t\tshaderstate.stream_buffer[D3D11_BUFF_TC] = shaderstate.batchvbo->texcoord.d3d.buff;\r\n\t\t\t\t\tshaderstate.stream_offset[D3D11_BUFF_TC] = shaderstate.batchvbo->texcoord.d3d.offs;\r\n\t\t\t\t\tshaderstate.stream_stride[D3D11_BUFF_TC] = sizeof(dx11_vbovdata_t);\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\tCon_Printf(\"should be unreachable\\n\");\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tID3D11Buffer *buf;\r\n\t\t\t\tfloat *map;\r\n\t\t\t\tunsigned int offset;\r\n\t\t\t\tallocvertexbuffer(&buf, &offset, (void**)&map, vertcount*sizeof(vec2_t));\r\n\t\t\t\tBE_GenerateTCMods(p, map);\r\n\t\t\t\tID3D11DeviceContext_Unmap(d3ddevctx, (ID3D11Resource*)buf, 0);\r\n\t\t\t\tshaderstate.stream_buffer[D3D11_BUFF_TC] = buf;\r\n\t\t\t\tshaderstate.stream_offset[D3D11_BUFF_TC] = offset;\r\n\t\t\t\tshaderstate.stream_stride[D3D11_BUFF_TC] = sizeof(vec2_t);\r\n\t\t\t}\r\n\r\n\t\t\tif (!shaderstate.programfixedemu[emumode])\r\n\t\t\t{\r\n\t\t\t\tchar *modes[] = {\r\n\t\t\t\t\t\"\",\"#ALPHATEST=>0.0\",\"#ALPHATEST=<0.5\",\"#ALPHATEST=>=0.5\"\r\n\t\t\t\t};\r\n\t\t\t\tshaderstate.programfixedemu[emumode] = Shader_FindGeneric(va(\"fixedemu%s\", modes[emumode]), QR_DIRECT3D11);\r\n\t\t\t\tif (!shaderstate.programfixedemu[emumode])\r\n\t\t\t\t\tbreak;\r\n\t\t\t}\r\n\r\n\t\t\tID3D11DeviceContext_IASetVertexBuffers(d3ddevctx, 0, D3D11_BUFF_MAX, (ID3D11Buffer**)shaderstate.stream_buffer, shaderstate.stream_stride, shaderstate.stream_offset);\r\n\t\t\tBE_RenderMeshProgram(shaderstate.programfixedemu[emumode], p, vertcount, idxfirst, idxcount);\r\n\t\t}\r\n\t}\r\n}\r\n\r\nvoid D3D11BE_SelectMode(backendmode_t mode)\r\n{\r\n\tshaderstate.mode = mode;\r\n\r\n\tif (mode == BEM_STENCIL)\r\n\t\tD3D11BE_ApplyShaderBits(SBITS_MASK_BITS, NULL);\r\n}\r\nqboolean D3D11BE_GenerateRTLightShader(unsigned int lmode)\r\n{\r\n\tif (!shaderstate.shader_rtlight[lmode])\r\n\t{\r\n\t\tshaderstate.shader_rtlight[lmode] = R_RegisterShader(va(\"rtlight%s%s%s\", \r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(lmode & LSHADER_SMAP)?\"#PCF\":\"\",\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(lmode & LSHADER_SPOT)?\"#SPOT\":\"\",\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(lmode & LSHADER_CUBE)?\"#CUBE\":\"\")\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t, SUF_NONE, LIGHTPASS_SHADER);\r\n\t}\r\n\tif (!shaderstate.shader_rtlight[lmode]->prog)\r\n\t\treturn false;\r\n\treturn true;\r\n}\r\nqboolean D3D11BE_SelectDLight(dlight_t *dl, vec3_t colour, vec3_t axis[3], unsigned int lmode)\r\n{\r\n\tif (!D3D11BE_GenerateRTLightShader(lmode))\r\n\t{\r\n\t\tlmode &= ~(LSHADER_SMAP|LSHADER_CUBE);\r\n\t\tif (!D3D11BE_GenerateRTLightShader(lmode))\r\n\t\t\treturn false;\r\n\t}\r\n\tshaderstate.curdlight = dl;\r\n\tshaderstate.curlmode = lmode;\r\n\tVectorCopy(colour, shaderstate.curdlight_colours);\r\n\r\n\tD3D11BE_SetupLightCBuffer(dl, colour);\r\n\treturn true;\r\n}\r\n\r\n#ifdef RTLIGHTS\r\nvoid D3D11BE_SetupForShadowMap(dlight_t *dl, int texwidth, int texheight, float shadowscale)\r\n{\r\n#define SHADOWMAP_SIZE 512\r\n\textern cvar_t r_shadow_shadowmapping_nearclip, r_shadow_shadowmapping_bias;\r\n\tfloat nc = r_shadow_shadowmapping_nearclip.value;\r\n\tfloat bias = r_shadow_shadowmapping_bias.value;\r\n\r\n\t//much of the projection matrix cancels out due to symmetry and stuff\r\n\t//we need to scale between -0.5,0.5 within the sub-image. the fragment shader will center on the subimage based upon the major axis.\r\n\t//in d3d, the depth value is scaled between 0 and 1 (gl is -1 to 1).\r\n\t//d3d's framebuffer is upside down or something annoying like that.\r\n\tshaderstate.lightshadowmapproj[0] = shadowscale * (1.0-(1.0/texwidth)) * 0.5/3.0;\t//pinch x inwards\r\n\tshaderstate.lightshadowmapproj[1] = -shadowscale * (1.0-(1.0/texheight)) * 0.5/2.0;\t//pinch y inwards\r\n\tshaderstate.lightshadowmapproj[2] = 0.5*(dl->radius+nc)/(nc-dl->radius);\t//proj matrix 10\r\n\tshaderstate.lightshadowmapproj[3] = (dl->radius*nc)/(nc-dl->radius) - bias*nc*(1024/texheight);\t//proj matrix 14\t\r\n\r\n\tshaderstate.lightshadowmapscale[0] = 1.0/(SHADOWMAP_SIZE*3);\r\n\tshaderstate.lightshadowmapscale[1] = -1.0/(SHADOWMAP_SIZE*2);\r\n}\r\n#endif\r\n\r\nvoid D3D11BE_SelectEntity(entity_t *ent)\r\n{\r\n\tBE_RotateForEntity(ent, ent->model);\r\n}\r\n\r\nvoid D3D11BE_GenBatchVBOs(vbo_t **vbochain, batch_t *firstbatch, batch_t *stopbatch)\r\n{\r\n\tint maxvboelements;\r\n\tint maxvboverts;\r\n\tint vert = 0, idx = 0;\r\n\tbatch_t *batch;\r\n\tvbo_t *vbo;\r\n\tint i, j;\r\n\tmesh_t *m;\r\n\tID3D11Buffer *vbuff;\r\n\tID3D11Buffer *ebuff;\r\n\tindex_t *vboedata, *vboedatastart;\r\n\tdx11_vbovdata_t *vbovdata, *vbovdatastart;\r\n\tD3D11_BUFFER_DESC vbodesc;\r\n\tD3D11_BUFFER_DESC ebodesc;\r\n\tD3D11_SUBRESOURCE_DATA srd;\r\n\r\n\tvbo = Z_Malloc(sizeof(*vbo));\r\n\r\n\tmaxvboverts = 0;\r\n\tmaxvboelements = 0;\r\n\tfor(batch = firstbatch; batch != stopbatch; batch = batch->next)\r\n\t{\r\n\t\tfor (i=0 ; i<batch->maxmeshes ; i++)\r\n\t\t{\r\n\t\t\tm = batch->mesh[i];\r\n\t\t\tmaxvboelements += m->numindexes;\r\n\t\t\tmaxvboverts += m->numvertexes;\r\n\t\t}\r\n\t}\r\n\r\n\tvbovdatastart = vbovdata = BZ_Malloc(sizeof(*vbovdata) * maxvboverts);\r\n\tvboedatastart = vboedata = BZ_Malloc(sizeof(*vboedata) * maxvboelements);\r\n\r\n\tfor(batch = firstbatch; batch != stopbatch; batch = batch->next)\r\n\t{\r\n\t\tbatch->vbo = vbo;\r\n\t\tfor (j=0 ; j<batch->maxmeshes ; j++)\r\n\t\t{\r\n\t\t\tm = batch->mesh[j];\r\n\t\t\tm->vbofirstvert = vert;\r\n\t\t\tfor (i = 0; i < m->numvertexes; i++)\r\n\t\t\t{\r\n\t\t\t\tVectorCopy(m->xyz_array[i],\t\t\tvbovdata->coord);\r\n\t\t\t\tvbovdata->coord[3] = 1;\r\n\t\t\t\tVector2Copy(m->st_array[i],\t\t\tvbovdata->tex);\r\n\t\t\t\tif (m->lmst_array[0])\r\n\t\t\t\t\tVector2Copy(m->lmst_array[0][i],\t\tvbovdata->lm);\r\n\t\t\t\telse\r\n\t\t\t\t\tVector2Copy(m->st_array[i],\t\t\tvbovdata->tex);\r\n\t\t\t\tif (m->normals_array)\r\n\t\t\t\t\tVectorCopy(m->normals_array[i],\t\tvbovdata->ndir);\r\n\t\t\t\telse\r\n\t\t\t\t\tVectorSet(vbovdata->ndir, 0, 0, 1);\r\n\t\t\t\tif (m->snormals_array)\r\n\t\t\t\t\tVectorCopy(m->snormals_array[i],\tvbovdata->sdir);\r\n\t\t\t\telse\r\n\t\t\t\t\tVectorSet(vbovdata->sdir, 1, 0, 0);\r\n\t\t\t\tif (m->tnormals_array)\r\n\t\t\t\t\tVectorCopy(m->tnormals_array[i],\tvbovdata->tdir);\r\n\t\t\t\telse\r\n\t\t\t\t\tVectorSet(vbovdata->tdir, 0, 1, 0);\r\n\t\t\t\tif (m->colors4f_array[0])\r\n\t\t\t\t\tVector4Scale(m->colors4f_array[0][i],\t255, vbovdata->colorsb);\r\n\t\t\t\telse if (m->colors4b_array)\r\n\t\t\t\t\tVector4Copy(m->colors4b_array[i],\tvbovdata->colorsb);\r\n\t\t\t\telse\r\n\t\t\t\t\tVector4Set(vbovdata->colorsb, 255, 255, 255, 255);\r\n\r\n\t\t\t\tvbovdata++;\r\n\t\t\t}\r\n\r\n\t\t\tm->vbofirstelement = idx;\r\n\t\t\tfor (i = 0; i < m->numindexes; i++)\r\n\t\t\t{\r\n\t\t\t\t*vboedata++ = vert + m->indexes[i];\r\n\t\t\t}\r\n\t\t\tidx += m->numindexes;\r\n\t\t\tvert += m->numvertexes;\r\n\t\t}\r\n\t}\r\n\r\n\t//generate the ebo, and submit the data to the driver\r\n\tebodesc.BindFlags = D3D11_BIND_INDEX_BUFFER;\r\n\tebodesc.ByteWidth = sizeof(*vboedata) * maxvboelements;\r\n\tebodesc.CPUAccessFlags = 0;\r\n\tebodesc.MiscFlags = 0;\r\n\tebodesc.StructureByteStride = 0;\r\n\tebodesc.Usage = D3D11_USAGE_DEFAULT;\r\n\tsrd.pSysMem = vboedatastart;\r\n\tsrd.SysMemPitch = 0;\r\n\tsrd.SysMemSlicePitch = 0;\r\n\tID3D11Device_CreateBuffer(pD3DDev11, &ebodesc, &srd, &ebuff);\r\n\tshaderstate.numlivevbos++;\r\n\tBZ_Free(vboedatastart);\r\n\r\n\t//generate the vbo, and submit the data to the driver\r\n\tvbodesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;\r\n\tvbodesc.ByteWidth = sizeof(*vbovdata) * maxvboverts;\r\n\tvbodesc.CPUAccessFlags = 0;\r\n\tvbodesc.MiscFlags = 0;\r\n\tvbodesc.StructureByteStride = 0;\r\n\tvbodesc.Usage = D3D11_USAGE_DEFAULT;\r\n\tsrd.pSysMem = vbovdatastart;\r\n\tsrd.SysMemPitch = 0;\r\n\tsrd.SysMemSlicePitch = 0;\r\n\tID3D11Device_CreateBuffer(pD3DDev11, &vbodesc, &srd, &vbuff);\r\n\tshaderstate.numlivevbos++;\r\n\tBZ_Free(vbovdatastart);\r\n\r\n\tvbovdata = NULL;\r\n\tvbo->coord.d3d.buff = vbuff;\r\n\tvbo->coord.d3d.offs = (quintptr_t)&vbovdata->coord;\r\n\tvbo->texcoord.d3d.buff = vbuff;\r\n\tvbo->texcoord.d3d.offs = (quintptr_t)&vbovdata->tex;\r\n\tvbo->lmcoord[0].d3d.buff = vbuff;\r\n\tvbo->lmcoord[0].d3d.offs = (quintptr_t)&vbovdata->lm;\r\n\tvbo->normals.d3d.buff = vbuff;\r\n\tvbo->normals.d3d.offs = (quintptr_t)&vbovdata->ndir;\r\n\tvbo->svector.d3d.buff = vbuff;\r\n\tvbo->svector.d3d.offs = (quintptr_t)&vbovdata->sdir;\r\n\tvbo->tvector.d3d.buff = vbuff;\r\n\tvbo->tvector.d3d.offs = (quintptr_t)&vbovdata->tdir;\r\n\tvbo->colours[0].d3d.buff = vbuff;\r\n\tvbo->colours[0].d3d.offs = (quintptr_t)&vbovdata->colorsb;\r\n\tvbo->indicies.d3d.buff = ebuff;\r\n\tvbo->indicies.d3d.offs = 0;\r\n\r\n\tvbo->indexcount = maxvboelements;\r\n\tvbo->vertcount = maxvboverts;\r\n\r\n\tvbo->next = *vbochain;\r\n\t*vbochain = vbo;\r\n}\r\n\r\nvoid D3D11BE_GenBrushModelVBO(model_t *mod)\r\n{\r\n\tunsigned int vcount, cvcount;\r\n\r\n\r\n\tbatch_t *batch, *fbatch;\r\n\tint sortid;\r\n\tint i;\r\n\r\n\tfbatch = NULL;\r\n\tvcount = 0;\r\n\tfor (sortid = 0; sortid < SHADER_SORT_COUNT; sortid++)\r\n\t{\r\n\t\tif (!mod->batches[sortid])\r\n\t\t\tcontinue;\r\n\r\n\t\tfor (fbatch = batch = mod->batches[sortid]; batch != NULL; batch = batch->next)\r\n\t\t{\r\n\t\t\tfor (i = 0, cvcount = 0; i < batch->maxmeshes; i++)\r\n\t\t\t\tcvcount += batch->mesh[i]->numvertexes;\r\n\r\n\t\t\t//firstmesh got reused as the number of verticies in each batch\r\n\t\t\tif (vcount + cvcount > MAX_INDICIES)\r\n\t\t\t{\r\n\t\t\t\tD3D11BE_GenBatchVBOs(&mod->vbos, fbatch, batch);\r\n\t\t\t\tfbatch = batch;\r\n\t\t\t\tvcount = 0;\r\n\t\t\t}\r\n\t\t\tvcount += cvcount;\r\n\t\t}\r\n\r\n\t\tD3D11BE_GenBatchVBOs(&mod->vbos, fbatch, batch);\r\n\t}\r\n}\r\n\r\n/*Wipes a vbo*/\r\nvoid D3D11BE_ClearVBO(vbo_t *vbo, qboolean dataonly)\r\n{\r\n\tID3D11Buffer *vbuff = vbo->coord.d3d.buff;\r\n\tID3D11Buffer *ebuff = vbo->indicies.d3d.buff;\r\n\tif (vbuff)\r\n\t{\r\n\t\tID3D11Buffer_Release(vbuff);\r\n\t\tshaderstate.numlivevbos--;\r\n\t}\r\n\tif (ebuff)\r\n\t{\r\n\t\tID3D11Buffer_Release(ebuff);\r\n\t\tshaderstate.numlivevbos--;\r\n\t}\r\n\tvbo->coord.d3d.buff = NULL;\r\n\tvbo->indicies.d3d.buff = NULL;\r\n\r\n\tif (!dataonly)\r\n\t\tBZ_Free(vbo);\r\n}\r\n\r\n/*upload all lightmaps at the start to reduce lags*/\r\nstatic void BE_UploadLightmaps(qboolean force)\r\n{\r\n\tint i;\r\n\r\n\tfor (i = 0; i < numlightmaps; i++)\r\n\t{\r\n\t\tif (!lightmap[i])\r\n\t\t\tcontinue;\r\n\r\n\t\tif (force && !lightmap[i]->external)\r\n\t\t{\r\n\t\t\tlightmap[i]->rectchange.l = 0;\r\n\t\t\tlightmap[i]->rectchange.t = 0;\r\n\t\t\tlightmap[i]->rectchange.r = lightmap[i]->width;\r\n\t\t\tlightmap[i]->rectchange.b = lightmap[i]->height;\r\n\t\t\tlightmap[i]->modified = true;\r\n\t\t}\r\n\r\n\t\tif (lightmap[i]->modified)\r\n\t\t{\r\n\t\t\tD3D11_UploadLightmap(lightmap[i]);\r\n\t\t}\r\n\t}\r\n}\r\n\r\nvoid D3D11BE_UploadAllLightmaps(void)\r\n{\r\n\tBE_UploadLightmaps(true);\r\n}\r\n\r\nqboolean D3D11BE_LightCullModel(vec3_t org, model_t *model)\r\n{\r\n#ifdef RTLIGHTS\r\n\tif ((shaderstate.mode == BEM_LIGHT || shaderstate.mode == BEM_STENCIL))\r\n\t{\r\n\t\t/*true if hidden from current light*/\r\n\t\t/*we have no rtlight support, so mneh*/\r\n\t}\r\n#endif\r\n\treturn false;\r\n}\r\n\r\nbatch_t *D3D11BE_GetTempBatch(void)\r\n{\r\n\tif (shaderstate.wbatch >= shaderstate.maxwbatches)\r\n\t{\r\n\t\tshaderstate.wbatch++;\r\n\t\treturn NULL;\r\n\t}\r\n\treturn &shaderstate.wbatches[shaderstate.wbatch++];\r\n}\r\n\r\n/*static float projd3dtogl[16] = \r\n{\r\n\t1.0, 0.0, 0.0, 0.0,\r\n\t0.0, 1.0, 0.0, 0.0,\r\n\t0.0, 0.0, 2.0, 0.0,\r\n\t0.0, 0.0, -1.0, 1.0\r\n};*/\r\nstatic float projgltod3d[16] = \r\n{\r\n\t1.0, 0.0, 0.0, 0.0,\r\n\t0.0, 1.0, 0.0, 0.0,\r\n\t0.0, 0.0, 0.5, 0.0,\r\n\t0.0, 0.0, 0.5, 1.0\r\n};\r\nstatic void D3D11BE_SetupViewCBuffer(void)\r\n{\r\n\tdx11_cbuf_view_t *cbv;\r\n\tD3D11_MAPPED_SUBRESOURCE msr;\r\n\tif (FAILED(ID3D11DeviceContext_Map(d3ddevctx, (ID3D11Resource*)shaderstate.vcbuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &msr)))\r\n\t{\r\n\t\tCon_Printf(\"BE_RotateForEntity: failed to map constant buffer\\n\");\r\n\t\treturn;\r\n\t}\r\n\tcbv = (dx11_cbuf_view_t*)msr.pData;\r\n\r\n\t//we internally use gl-style projection matricies.\r\n\t//gl's viewport is based upon -1 to 1 depth.\r\n\t//d3d uses 0 to 1 depth.\r\n\t//so we scale the projection matrix by a bias\r\n#if 1\r\n\tMatrix4_Multiply(projgltod3d, (shaderstate.depthrange<1)?r_refdef.m_projection_view:r_refdef.m_projection_std, cbv->m_projection);\r\n#else\r\n\tmemcpy(cbv->m_projection, r_refdef.m_projection_std, sizeof(cbv->m_projection));\r\n\tcbv->m_projection[10] = r_refdef.m_projection_std[10] * 0.5;\r\n#endif\r\n\tmemcpy(cbv->m_view, r_refdef.m_view, sizeof(cbv->m_view));\r\n\tVectorCopy(r_origin, cbv->v_eyepos);\r\n\tcbv->v_time = r_refdef.time;\r\n\r\n\tID3D11DeviceContext_Unmap(d3ddevctx, (ID3D11Resource*)shaderstate.vcbuffer, 0);\r\n}\r\nvoid D3D11BE_Set2D(void)\r\n{\r\n\tD3D11_VIEWPORT vport;\r\n\tvport.TopLeftX = 0;\r\n\tvport.TopLeftY = 0;\r\n\tvport.Width = vid.pixelwidth;\r\n\tvport.Height = vid.pixelheight;\r\n\tvport.MinDepth = 0;\r\n\tvport.MaxDepth = shaderstate.depthrange = 1;\r\n\r\n\tID3D11DeviceContext_RSSetViewports(d3ddevctx, 1, &vport);\r\n\tD3D11BE_SetupViewCBuffer();\r\n\tD3D11BE_Scissor(NULL);\r\n}\r\nvoid D3D11BE_SetupLightCBuffer(dlight_t *l, vec3_t colour)\r\n{\r\n\textern cvar_t gl_specular;\r\n\tdx11_cbuf_light_t *cbl;\r\n\tD3D11_MAPPED_SUBRESOURCE msr;\r\n\tif (FAILED(ID3D11DeviceContext_Map(d3ddevctx, (ID3D11Resource*)shaderstate.lcbuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &msr)))\r\n\t{\r\n\t\tCon_Printf(\"BE_RotateForEntity: failed to map constant buffer\\n\");\r\n\t\treturn;\r\n\t}\r\n\tcbl = (dx11_cbuf_light_t*)msr.pData;\r\n\r\n\tcbl->l_lightradius = l->radius;\r\n\r\n\tMatrix4x4_CM_LightMatrixFromAxis(cbl->l_cubematrix, l->axis[0], l->axis[1], l->axis[2], l->origin);\r\n\tVectorCopy(l->origin, cbl->l_lightposition);\r\n\tcbl->padl1 = 0;\r\n\tVectorCopy(colour, cbl->l_colour);\r\n#ifdef RTLIGHTS\r\n\tVectorCopy(l->lightcolourscales, cbl->l_lightcolourscale);\r\n\tcbl->l_lightcolourscale[0] = l->lightcolourscales[0];\r\n\tcbl->l_lightcolourscale[1] = l->lightcolourscales[1];\r\n\tcbl->l_lightcolourscale[2] = l->lightcolourscales[2] * gl_specular.value;\r\n#endif\r\n\tcbl->l_lightradius = l->radius;\r\n\tVector4Copy(shaderstate.lightshadowmapproj, cbl->l_shadowmapproj);\r\n\tVector2Copy(shaderstate.lightshadowmapscale, cbl->l_shadowmapscale);\r\n\r\n\tID3D11DeviceContext_Unmap(d3ddevctx, (ID3D11Resource*)shaderstate.lcbuffer, 0);\r\n}\r\n\r\nstatic void R_FetchPlayerColour(unsigned int cv, vec4_t rgba)\r\n{\r\n\tint i;\r\n\r\n\tif (cv >= 16)\r\n\t{\r\n\t\trgba[0] = (((cv&0xff0000)>>16)**((unsigned char*)&d_8to24rgbtable[15]+0)) / (256.0*256);\r\n\t\trgba[1] = (((cv&0x00ff00)>>8)**((unsigned char*)&d_8to24rgbtable[15]+1)) / (256.0*256);\r\n\t\trgba[2] = (((cv&0x0000ff)>>0)**((unsigned char*)&d_8to24rgbtable[15]+2)) / (256.0*256);\r\n\t\trgba[3] = 1.0;\r\n\t\treturn;\r\n\t}\r\n\ti = cv;\r\n\tif (i >= 8)\r\n\t{\r\n\t\ti<<=4;\r\n\t}\r\n\telse\r\n\t{\r\n\t\ti<<=4;\r\n\t\ti+=15;\r\n\t}\r\n\ti*=3;\r\n\trgba[0] = host_basepal[i+0] / 255.0;\r\n\trgba[1] = host_basepal[i+1] / 255.0;\r\n\trgba[2] = host_basepal[i+2] / 255.0;\r\n\trgba[3] = 1.0;\r\n}\r\n\r\n//also updates the entity constant buffer\r\nstatic void BE_RotateForEntity (const entity_t *e, const model_t *mod)\r\n{\r\n\tint i;\r\n\tfloat ndr;\r\n\tfloat modelinv[16];\r\n\tfloat *m = shaderstate.m_model;\r\n\tdx11_cbuf_entity_t *cbe;\r\n\tD3D11_MAPPED_SUBRESOURCE msr;\r\n\tshaderstate.ecbufferidx = (shaderstate.ecbufferidx + 1) & (NUMECBUFFERS-1);\r\n\tif (FAILED(ID3D11DeviceContext_Map(d3ddevctx, (ID3D11Resource*)shaderstate.ecbuffers[shaderstate.ecbufferidx], 0, D3D11_MAP_WRITE_DISCARD, 0, &msr)))\r\n\t{\r\n\t\tCon_Printf(\"BE_RotateForEntity: failed to map constant buffer\\n\");\r\n\t\treturn;\r\n\t}\r\n\tcbe = (dx11_cbuf_entity_t*)msr.pData;\r\n\r\n\r\n\tshaderstate.curentity = e;\r\n\r\n\r\n\tif ((e->flags & RF_WEAPONMODEL) && r_refdef.playerview->viewentity > 0)\r\n\t{\r\n\t\tfloat em[16];\r\n\t\tfloat vm[16];\r\n\r\n\t\tif (e->flags & RF_WEAPONMODELNOBOB)\r\n\t\t{\r\n\t\t\tvm[0] = r_refdef.weaponmatrix[0][0];\r\n\t\t\tvm[1] = r_refdef.weaponmatrix[0][1];\r\n\t\t\tvm[2] = r_refdef.weaponmatrix[0][2];\r\n\t\t\tvm[3] = 0;\r\n\r\n\t\t\tvm[4] = r_refdef.weaponmatrix[1][0];\r\n\t\t\tvm[5] = r_refdef.weaponmatrix[1][1];\r\n\t\t\tvm[6] = r_refdef.weaponmatrix[1][2];\r\n\t\t\tvm[7] = 0;\r\n\r\n\t\t\tvm[8] = r_refdef.weaponmatrix[2][0];\r\n\t\t\tvm[9] = r_refdef.weaponmatrix[2][1];\r\n\t\t\tvm[10] = r_refdef.weaponmatrix[2][2];\r\n\t\t\tvm[11] = 0;\r\n\r\n\t\t\tvm[12] = r_refdef.weaponmatrix[3][0];\r\n\t\t\tvm[13] = r_refdef.weaponmatrix[3][1];\r\n\t\t\tvm[14] = r_refdef.weaponmatrix[3][2];\r\n\t\t\tvm[15] = 1;\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tvm[0] = r_refdef.weaponmatrix_bob[0][0];\r\n\t\t\tvm[1] = r_refdef.weaponmatrix_bob[0][1];\r\n\t\t\tvm[2] = r_refdef.weaponmatrix_bob[0][2];\r\n\t\t\tvm[3] = 0;\r\n\r\n\t\t\tvm[4] = r_refdef.weaponmatrix_bob[1][0];\r\n\t\t\tvm[5] = r_refdef.weaponmatrix_bob[1][1];\r\n\t\t\tvm[6] = r_refdef.weaponmatrix_bob[1][2];\r\n\t\t\tvm[7] = 0;\r\n\r\n\t\t\tvm[8] = r_refdef.weaponmatrix_bob[2][0];\r\n\t\t\tvm[9] = r_refdef.weaponmatrix_bob[2][1];\r\n\t\t\tvm[10] = r_refdef.weaponmatrix_bob[2][2];\r\n\t\t\tvm[11] = 0;\r\n\r\n\t\t\tvm[12] = r_refdef.weaponmatrix_bob[3][0];\r\n\t\t\tvm[13] = r_refdef.weaponmatrix_bob[3][1];\r\n\t\t\tvm[14] = r_refdef.weaponmatrix_bob[3][2];\r\n\t\t\tvm[15] = 1;\r\n\t\t}\r\n\r\n\t\tem[0] = e->axis[0][0];\r\n\t\tem[1] = e->axis[0][1];\r\n\t\tem[2] = e->axis[0][2];\r\n\t\tem[3] = 0;\r\n\r\n\t\tem[4] = e->axis[1][0];\r\n\t\tem[5] = e->axis[1][1];\r\n\t\tem[6] = e->axis[1][2];\r\n\t\tem[7] = 0;\r\n\r\n\t\tem[8] = e->axis[2][0];\r\n\t\tem[9] = e->axis[2][1];\r\n\t\tem[10] = e->axis[2][2];\r\n\t\tem[11] = 0;\r\n\r\n\t\tem[12] = e->origin[0];\r\n\t\tem[13] = e->origin[1];\r\n\t\tem[14] = e->origin[2];\r\n\t\tem[15] = 1;\r\n\r\n\t\tMatrix4_Multiply(vm, em, m);\r\n\t}\r\n\telse\r\n\t{\r\n\t\tm[0] = e->axis[0][0];\r\n\t\tm[1] = e->axis[0][1];\r\n\t\tm[2] = e->axis[0][2];\r\n\t\tm[3] = 0;\r\n\r\n\t\tm[4] = e->axis[1][0];\r\n\t\tm[5] = e->axis[1][1];\r\n\t\tm[6] = e->axis[1][2];\r\n\t\tm[7] = 0;\r\n\r\n\t\tm[8] = e->axis[2][0];\r\n\t\tm[9] = e->axis[2][1];\r\n\t\tm[10] = e->axis[2][2];\r\n\t\tm[11] = 0;\r\n\r\n\t\tm[12] = e->origin[0];\r\n\t\tm[13] = e->origin[1];\r\n\t\tm[14] = e->origin[2];\r\n\t\tm[15] = 1;\r\n\t}\r\n\r\n\tif (e->scale != 1 && e->scale != 0)\t//hexen 2 stuff\r\n\t{\r\n#ifdef HEXEN2\r\n\t\tfloat z;\r\n\t\tfloat escale;\r\n\t\tescale = e->scale;\r\n\t\tswitch(e->drawflags&SCALE_TYPE_MASK)\r\n\t\t{\r\n\t\tdefault:\r\n\t\tcase SCALE_TYPE_UNIFORM:\r\n\t\t\tVectorScale((m+0), escale, (m+0));\r\n\t\t\tVectorScale((m+4), escale, (m+4));\r\n\t\t\tVectorScale((m+8), escale, (m+8));\r\n\t\t\tbreak;\r\n\t\tcase SCALE_TYPE_XYONLY:\r\n\t\t\tVectorScale((m+0), escale, (m+0));\r\n\t\t\tVectorScale((m+4), escale, (m+4));\r\n\t\t\tbreak;\r\n\t\tcase SCALE_TYPE_ZONLY:\r\n\t\t\tVectorScale((m+8), escale, (m+8));\r\n\t\t\tbreak;\r\n\t\t}\r\n\t\tif (mod && (e->drawflags&SCALE_TYPE_MASK) != SCALE_TYPE_XYONLY)\r\n\t\t{\r\n\t\t\tswitch(e->drawflags&SCALE_ORIGIN_MASK)\r\n\t\t\t{\r\n\t\t\tcase SCALE_ORIGIN_CENTER:\r\n\t\t\t\tz = ((mod->maxs[2] + mod->mins[2]) * (1-escale))/2;\r\n\t\t\t\tVectorMA((m+12), z, e->axis[2], (m+12));\r\n\t\t\t\tbreak;\r\n\t\t\tcase SCALE_ORIGIN_BOTTOM:\r\n\t\t\t\tVectorMA((m+12), mod->mins[2]*(1-escale), e->axis[2], (m+12));\r\n\t\t\t\tbreak;\r\n\t\t\tcase SCALE_ORIGIN_TOP:\r\n\t\t\t\tVectorMA((m+12), -mod->maxs[2], e->axis[2], (m+12));\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\t\t}\r\n#else\r\n\t\tVectorScale((m+0), e->scale, (m+0));\r\n\t\tVectorScale((m+4), e->scale, (m+4));\r\n\t\tVectorScale((m+8), e->scale, (m+8));\r\n#endif\r\n\t}\r\n\telse if (mod && !strcmp(mod->name, \"progs/eyes.mdl\"))\r\n\t{\r\n\t\t/*resize eyes, to make them easier to see*/\r\n\t\tm[14] -= (22 + 8);\r\n\t\tVectorScale((m+0), 2, (m+0));\r\n\t\tVectorScale((m+4), 2, (m+4));\r\n\t\tVectorScale((m+8), 2, (m+8));\r\n\t}\r\n\tif (mod && !ruleset_allow_larger_models.ival && mod->clampscale != 1)\r\n\t{\t//possibly this should be on a per-frame basis, but that's a real pain to do\r\n\t\tCon_DPrintf(\"Rescaling %s by %f\\n\", mod->name, mod->clampscale);\r\n\t\tVectorScale((m+0), mod->clampscale, (m+0));\r\n\t\tVectorScale((m+4), mod->clampscale, (m+4));\r\n\t\tVectorScale((m+8), mod->clampscale, (m+8));\r\n\t}\r\n\r\n\tmemcpy(cbe->m_model, m, sizeof(cbe->m_model));\r\n\r\n\tMatrix4_Invert(shaderstate.m_model, modelinv);\r\n\r\n\tcbe->e_time = r_refdef.time - shaderstate.curentity->shaderTime;\r\n\r\n\tVectorCopy(e->light_avg, cbe->e_light_ambient);\r\n\tVectorCopy(e->light_dir, cbe->e_light_dir);\r\n\tVectorCopy(e->light_range, cbe->e_light_mul);\r\n\r\n\tR_FetchPlayerColour(e->topcolour, cbe->e_uppercolour);\r\n\tR_FetchPlayerColour(e->bottomcolour, cbe->e_lowercolour);\r\n\tR_FetchPlayerColour(e->bottomcolour, cbe->e_colourmod);\r\n\tif (shaderstate.flags & BEF_FORCECOLOURMOD)\r\n\t\tVector4Copy(e->shaderRGBAf, cbe->e_colourmod);\r\n\telse\r\n\t\tVector4Set(cbe->e_colourmod, 1, 1, 1, e->shaderRGBAf[3]);\r\n\tVectorCopy(e->glowmod, cbe->e_glowmod);cbe->e_glowmod[3] = 1;\r\n\r\n\t//various stuff in modelspace\r\n\tMatrix4x4_CM_Transform3(modelinv, r_origin, cbe->e_eyepos);\r\n\r\n\tfor (i = 0; i < MAXRLIGHTMAPS ; i++)\r\n\t{\r\n\t\textern cvar_t gl_overbright;\r\n\t\tunsigned char s = shaderstate.curbatch?shaderstate.curbatch->lmlightstyle[i]:0;\r\n\t\tfloat sc;\r\n\t\tif (s == 255)\r\n\t\t{\r\n\t\t\tif (i == 0)\r\n\t\t\t{\r\n\t\t\t\tif (shaderstate.curentity->model && shaderstate.curentity->model->engineflags & MDLF_NEEDOVERBRIGHT)\r\n\t\t\t\t\tsc = (1<<bound(0, gl_overbright.ival, 2)) * shaderstate.identitylighting;\r\n\t\t\t\telse\r\n\t\t\t\t\tsc = shaderstate.identitylighting;\r\n\t\t\t\tcbe->e_lmscale[i][0] = sc;\r\n\t\t\t\tcbe->e_lmscale[i][1] = sc;\r\n\t\t\t\tcbe->e_lmscale[i][2] = sc;\r\n\t\t\t\tcbe->e_lmscale[i][3] = 1;\r\n\t\t\t\ti++;\r\n\t\t\t}\r\n\t\t\tfor (; i < MAXRLIGHTMAPS ; i++)\r\n\t\t\t{\r\n\t\t\t\tcbe->e_lmscale[i][0] = 0;\r\n\t\t\t\tcbe->e_lmscale[i][1] = 0;\r\n\t\t\t\tcbe->e_lmscale[i][2] = 0;\r\n\t\t\t\tcbe->e_lmscale[i][3] = 1;\r\n\t\t\t}\r\n\t\t\tbreak;\r\n\t\t}\r\n\t\tif (shaderstate.curentity->model && shaderstate.curentity->model->engineflags & MDLF_NEEDOVERBRIGHT)\r\n\t\t\tsc = (1<<bound(0, gl_overbright.ival, 2)) * shaderstate.identitylighting;\r\n\t\telse\r\n\t\t\tsc = shaderstate.identitylighting;\r\n\t\tsc *= d_lightstylevalue[s]/256.0f;\r\n\t\tVector4Set(cbe->e_lmscale[i], sc, sc, sc, 1);\r\n\t}\r\n\r\n\tID3D11DeviceContext_Unmap(d3ddevctx, (ID3D11Resource*)shaderstate.ecbuffers[shaderstate.ecbufferidx], 0);\r\n\r\n\tndr = (e->flags & RF_DEPTHHACK)?0.333:1;\r\n\tif (ndr != shaderstate.depthrange)\r\n\t{\r\n\t\tD3D11_VIEWPORT vport;\r\n\r\n\t\tshaderstate.depthrange = ndr;\r\n\r\n\t\tvport.TopLeftX = r_refdef.pxrect.x;\r\n\t\tvport.TopLeftY = r_refdef.pxrect.y;\r\n\t\tvport.Width = r_refdef.pxrect.width;\r\n\t\tvport.Height = r_refdef.pxrect.height;\r\n\t\tvport.MinDepth = 0;\r\n\t\tvport.MaxDepth = shaderstate.depthrange;\r\n\t\tID3D11DeviceContext_RSSetViewports(d3ddevctx, 1, &vport);\r\n\r\n\t\tD3D11BE_SetupViewCBuffer();\r\n\t}\r\n}\r\n\r\nvoid D3D11BE_SubmitBatch(batch_t *batch)\r\n{\r\n\tshader_t *shader = batch->shader;\r\n\tshaderstate.nummeshes = batch->meshes - batch->firstmesh;\r\n\tif (!shaderstate.nummeshes)\r\n\t\treturn;\r\n\tshaderstate.curbatch = batch;\r\n\tshaderstate.batchvbo = batch->vbo;\r\n\tshaderstate.meshlist = batch->mesh + batch->firstmesh;\r\n\tshaderstate.curshader = shader;\r\n\tif (shaderstate.curentity != batch->ent)\r\n\t{\r\n\t\tBE_RotateForEntity(batch->ent, batch->ent->model);\r\n\t\tshaderstate.curtime = r_refdef.time - shaderstate.curentity->shaderTime;\r\n\t}\r\n\tif (batch->skin)\r\n\t\tshaderstate.curtexnums = batch->skin;\r\n\telse if (shader->numdefaulttextures)\r\n\t\tshaderstate.curtexnums = shader->defaulttextures + ((int)(shader->defaulttextures_fps * shaderstate.curtime) % shader->numdefaulttextures);\r\n\telse\r\n\t\tshaderstate.curtexnums = shader->defaulttextures;\r\n\tshaderstate.flags = batch->flags;\r\n\r\n\tBE_DrawMeshChain_Internal();\r\n}\r\n\r\nvoid D3D11BE_DrawMesh_List(shader_t *shader, int nummeshes, mesh_t **meshlist, vbo_t *vbo, texnums_t *texnums, unsigned int beflags)\r\n{\r\n\tshaderstate.curbatch = &shaderstate.dummybatch;\r\n\tshaderstate.batchvbo = vbo;\r\n\tshaderstate.curshader = shader;\r\n\tif (texnums)\r\n\t\tshaderstate.curtexnums = texnums;\r\n\telse if (shader->numdefaulttextures)\r\n\t\tshaderstate.curtexnums = shader->defaulttextures + ((int)(shader->defaulttextures_fps * shaderstate.curtime) % shader->numdefaulttextures);\r\n\telse\r\n\t\tshaderstate.curtexnums = shader->defaulttextures;\r\n\tshaderstate.meshlist = meshlist;\r\n\tshaderstate.nummeshes = nummeshes;\r\n\tshaderstate.flags = beflags;\r\n\r\n\tBE_DrawMeshChain_Internal();\r\n}\r\n\r\nvoid D3D11BE_DrawMesh_Single(shader_t *shader, mesh_t *meshchain, vbo_t *vbo, unsigned int beflags)\r\n{\r\n\tshaderstate.curbatch = &shaderstate.dummybatch;\r\n\tshaderstate.batchvbo = vbo;\r\n\tshaderstate.curtime = realtime;\r\n\tshaderstate.curshader = shader;\r\n\tif (shader->numdefaulttextures)\r\n\t\tshaderstate.curtexnums = shader->defaulttextures + ((int)(shader->defaulttextures_fps * shaderstate.curtime) % shader->numdefaulttextures);\r\n\telse\r\n\t\tshaderstate.curtexnums = shader->defaulttextures;\r\n\tshaderstate.meshlist = &meshchain;\r\n\tshaderstate.nummeshes = 1;\r\n\tshaderstate.flags = beflags;\r\n\r\n\tBE_DrawMeshChain_Internal();\r\n}\r\n\r\nstatic void BE_SubmitMeshesSortList(batch_t *sortlist)\r\n{\r\n\tbatch_t *batch;\r\n\tfor (batch = sortlist; batch; batch = batch->next)\r\n\t{\r\n\t\tif (batch->meshes == batch->firstmesh)\r\n\t\t\tcontinue;\r\n\r\n\t\tif (batch->buildmeshes)\r\n\t\t\tbatch->buildmeshes(batch);\r\n\r\n\t\tif (batch->shader->flags & SHADER_NODLIGHT)\r\n\t\t\tif (shaderstate.mode == BEM_LIGHT)\r\n\t\t\t\tcontinue;\r\n\r\n\t\tif (batch->shader->flags & SHADER_SKY)\r\n\t\t{\r\n\t\t\tif (shaderstate.mode == BEM_STANDARD || shaderstate.mode == BEM_DEPTHDARK)\r\n\t\t\t{\r\n\t\t\t\tif (R_DrawSkyChain (batch))\r\n\t\t\t\t\tcontinue;\r\n\t\t\t}\r\n\t\t\telse if (shaderstate.mode != BEM_FOG && shaderstate.mode != BEM_CREPUSCULAR && shaderstate.mode != BEM_WIREFRAME)\r\n\t\t\t\tcontinue;\r\n\t\t}\r\n\r\n\t\tBE_SubmitBatch(batch);\r\n\t}\r\n}\r\n\r\n\r\n/*generates a new modelview matrix, as well as vpn vectors*/\r\nstatic void R_MirrorMatrix(plane_t *plane)\r\n{\r\n\tfloat mirror[16];\r\n\tfloat view[16];\r\n\tfloat result[16];\r\n\r\n\tvec3_t pnorm;\r\n\tVectorNegate(plane->normal, pnorm);\r\n\r\n\tmirror[0] = 1-2*pnorm[0]*pnorm[0];\r\n\tmirror[1] = -2*pnorm[0]*pnorm[1];\r\n\tmirror[2] = -2*pnorm[0]*pnorm[2];\r\n\tmirror[3] = 0;\r\n\r\n\tmirror[4] = -2*pnorm[1]*pnorm[0];\r\n\tmirror[5] = 1-2*pnorm[1]*pnorm[1];\r\n\tmirror[6] = -2*pnorm[1]*pnorm[2] ;\r\n\tmirror[7] = 0;\r\n\r\n\tmirror[8]  = -2*pnorm[2]*pnorm[0];\r\n\tmirror[9]  = -2*pnorm[2]*pnorm[1];\r\n\tmirror[10] = 1-2*pnorm[2]*pnorm[2];\r\n\tmirror[11] = 0;\r\n\r\n\tmirror[12] = -2*pnorm[0]*plane->dist;\r\n\tmirror[13] = -2*pnorm[1]*plane->dist;\r\n\tmirror[14] = -2*pnorm[2]*plane->dist;\r\n\tmirror[15] = 1;\r\n\r\n\tview[0] = vpn[0];\r\n\tview[1] = vpn[1];\r\n\tview[2] = vpn[2];\r\n\tview[3] = 0;\r\n\r\n\tview[4] = -vright[0];\r\n\tview[5] = -vright[1];\r\n\tview[6] = -vright[2];\r\n\tview[7] = 0;\r\n\r\n\tview[8]  = vup[0];\r\n\tview[9]  = vup[1];\r\n\tview[10] = vup[2];\r\n\tview[11] = 0;\r\n\r\n\tview[12] = r_refdef.vieworg[0];\r\n\tview[13] = r_refdef.vieworg[1];\r\n\tview[14] = r_refdef.vieworg[2];\r\n\tview[15] = 1;\r\n\r\n\tVectorMA(r_refdef.vieworg, 0.25, plane->normal, r_refdef.pvsorigin);\r\n\r\n\tMatrix4_Multiply(mirror, view, result);\r\n\r\n\tvpn[0] = result[0];\r\n\tvpn[1] = result[1];\r\n\tvpn[2] = result[2];\r\n\r\n\tvright[0] = -result[4];\r\n\tvright[1] = -result[5];\r\n\tvright[2] = -result[6];\r\n\r\n\tvup[0] = result[8];\r\n\tvup[1] = result[9];\r\n\tvup[2] = result[10];\r\n\r\n\tr_refdef.vieworg[0] = result[12];\r\n\tr_refdef.vieworg[1] = result[13];\r\n\tr_refdef.vieworg[2] = result[14];\r\n}\r\nstatic entity_t *R_NearestPortal(plane_t *plane)\r\n{\r\n\tint i;\r\n\tentity_t *best = NULL;\r\n\tfloat dist, bestd = 0;\r\n\t//for q3-compat, portals on world scan for a visedict to use for their view.\r\n\tfor (i = 0; i < cl_numvisedicts; i++)\r\n\t{\r\n\t\tif (cl_visedicts[i].rtype == RT_PORTALSURFACE)\r\n\t\t{\r\n\t\t\tdist = DotProduct(cl_visedicts[i].origin, plane->normal)-plane->dist;\r\n\t\t\tdist = fabs(dist);\r\n\t\t\tif (dist < 64 && (!best || dist < bestd))\r\n\t\t\t\tbest = &cl_visedicts[i];\r\n\t\t}\r\n\t}\r\n\treturn best;\r\n}\r\n\r\nstatic void TransformCoord(vec3_t in, vec3_t planea[3], vec3_t planeo, vec3_t viewa[3], vec3_t viewo, vec3_t result)\r\n{\r\n\tint\t\ti;\r\n\tvec3_t\tlocal;\r\n\tvec3_t\ttransformed;\r\n\tfloat\td;\r\n\r\n\tlocal[0] = in[0] - planeo[0];\r\n\tlocal[1] = in[1] - planeo[1];\r\n\tlocal[2] = in[2] - planeo[2];\r\n\r\n\tVectorClear(transformed);\r\n\tfor ( i = 0 ; i < 3 ; i++ )\r\n\t{\r\n\t\td = DotProduct(local, planea[i]);\r\n\t\tVectorMA(transformed, d, viewa[i], transformed);\r\n\t}\r\n\r\n\tresult[0] = transformed[0] + viewo[0];\r\n\tresult[1] = transformed[1] + viewo[1];\r\n\tresult[2] = transformed[2] + viewo[2];\r\n}\r\nstatic void TransformDir(vec3_t in, vec3_t planea[3], vec3_t viewa[3], vec3_t result)\r\n{\r\n\tint\t\ti;\r\n\tfloat\td;\r\n\tvec3_t tmp;\r\n\r\n\tVectorCopy(in, tmp);\r\n\r\n\tVectorClear(result);\r\n\tfor ( i = 0 ; i < 3 ; i++ )\r\n\t{\r\n\t\td = DotProduct(tmp, planea[i]);\r\n\t\tVectorMA(result, d, viewa[i], result);\r\n\t}\r\n}\r\n\r\nstatic void R_DrawPortal(batch_t *batch, batch_t **blist)\r\n{\r\n\tentity_t *view;\r\n//\tfloat glplane[4];\r\n\tplane_t plane;\r\n\trefdef_t oldrefdef;\r\n\tmesh_t *mesh = batch->mesh[batch->firstmesh];\r\n\tint sort;\r\n\r\n\tif (r_refdef.recurse)// || !r_portalrecursion.ival)\r\n\t\treturn;\r\n\r\n\tVectorCopy(mesh->normals_array[0], plane.normal);\r\n\tplane.dist = DotProduct(mesh->xyz_array[0], plane.normal);\r\n\r\n\t//if we're too far away from the surface, don't draw anything\r\n\tif (batch->shader->flags & SHADER_AGEN_PORTAL)\r\n\t{\r\n\t\t/*there's a portal alpha blend on that surface, that fades out after this distance*/\r\n\t\tif (DotProduct(r_refdef.vieworg, plane.normal)-plane.dist > batch->shader->portaldist)\r\n\t\t\treturn;\r\n\t}\r\n\t//if we're behind it, then also don't draw anything.\r\n\tif (DotProduct(r_refdef.vieworg, plane.normal)-plane.dist < 0)\r\n\t\treturn;\r\n\r\n\tview = R_NearestPortal(&plane);\r\n\t//if (!view)\r\n\t//\treturn;\r\n\r\n\toldrefdef = r_refdef;\r\n\tr_refdef.recurse = true;\r\n\r\n\tr_refdef.externalview = true;\r\n\r\n\tif (!view || VectorCompare(view->origin, view->oldorigin))\r\n\t{\r\n\t\tr_refdef.flipcull ^= SHADER_CULL_FLIP;\r\n\t\tR_MirrorMatrix(&plane);\r\n\t}\r\n\telse\r\n\t{\r\n\t\tfloat d;\r\n\t\tvec3_t paxis[3], porigin, vaxis[3], vorg;\r\n\t\tvoid PerpendicularVector( vec3_t dst, const vec3_t src );\r\n\r\n\t\t/*calculate where the surface is meant to be*/\r\n\t\tVectorCopy(mesh->normals_array[0], paxis[0]);\r\n\t\tPerpendicularVector(paxis[1], paxis[0]);\r\n\t\tCrossProduct(paxis[0], paxis[1], paxis[2]);\r\n\t\td = DotProduct(view->origin, plane.normal) - plane.dist;\r\n\t\tVectorMA(view->origin, -d, paxis[0], porigin);\r\n\r\n\t\t/*grab the camera origin*/\r\n\t\tVectorNegate(view->axis[0], vaxis[0]);\r\n\t\tVectorNegate(view->axis[1], vaxis[1]);\r\n\t\tVectorCopy(view->axis[2], vaxis[2]);\r\n\t\tVectorCopy(view->oldorigin, vorg);\r\n\r\n\t\tVectorCopy(vorg, r_refdef.pvsorigin);\r\n\r\n\t\t/*rotate it a bit*/\r\n\t\tRotatePointAroundVector(vaxis[1], vaxis[0], view->axis[1], sin(realtime)*4);\r\n\t\tCrossProduct(vaxis[0], vaxis[1], vaxis[2]);\r\n\r\n\t\tTransformCoord(oldrefdef.vieworg, paxis, porigin, vaxis, vorg, r_refdef.vieworg);\r\n\t\tTransformDir(vpn, paxis, vaxis, vpn);\r\n\t\tTransformDir(vright, paxis, vaxis, vright);\r\n\t\tTransformDir(vup, paxis, vaxis, vup);\r\n\t}\r\n\tMatrix4x4_CM_ModelViewMatrixFromAxis(r_refdef.m_view, vpn, vright, vup, r_refdef.vieworg);\r\n\tVectorAngles(vpn, vup, r_refdef.viewangles, true);\r\n\tVectorCopy(r_refdef.vieworg, r_origin);\r\n\r\n/*FIXME: the batch stuff should be done in renderscene*/\r\n\r\n\t/*fixup the first mesh index*/\r\n\tfor (sort = 0; sort < SHADER_SORT_COUNT; sort++)\r\n\tfor (batch = blist[sort]; batch; batch = batch->next)\r\n\t{\r\n\t\tbatch->firstmesh = batch->meshes;\r\n\t}\r\n\r\n\r\n\r\n\tR_SetFrustum (r_refdef.m_projection_std, r_refdef.m_view);\r\n\t/*FIXME: we need to borrow pretty much everything about portal positioning from the gl renderer. make it common code or something, because this is horrendous.\r\n\tif (r_refdef.frustum_numplanes < MAXFRUSTUMPLANES)\r\n\t{\r\n\t\textern int SignbitsForPlane (mplane_t *out);\r\n\t\tmplane_t fp;\r\n\t\tVectorCopy(plane.normal, fp.normal);\r\n\t\tfp.dist = plane.dist;\r\n\r\n\t\tfp.type = PLANE_ANYZ;\r\n\t\tfp.signbits = SignbitsForPlane (&fp);\r\n\r\n\t\tif (portaltype == 1 || portaltype == 2)\r\n\t\t\tR_ObliqueNearClip(r_refdef.m_view, &fp);\r\n\r\n\t\t//our own culling should be an epsilon forwards so we don't still draw things behind the line due to precision issues.\r\n\t\tfp.dist += 0.01;\r\n\t\tr_refdef.frustum[r_refdef.frustum_numplanes++] = fp;\r\n\t}\r\n\t*/\r\n\r\n\tshaderstate.curentity = NULL;\r\n\tSurf_SetupFrame();\r\n\tSurf_DrawWorld();\r\n\r\n\tfor (sort = 0; sort < SHADER_SORT_COUNT; sort++)\r\n\tfor (batch = blist[sort]; batch; batch = batch->next)\r\n\t{\r\n\t\tbatch->firstmesh = 0;\r\n\t}\r\n\tr_refdef = oldrefdef;\r\n\r\n\t/*broken stuff*/\r\n\tAngleVectors (r_refdef.viewangles, vpn, vright, vup);\r\n\tVectorCopy (r_refdef.vieworg, r_origin);\r\n\r\n\tR_SetFrustum (r_refdef.m_projection_std, r_refdef.m_view);\r\n\tshaderstate.curentity = NULL;\r\n\r\n\tD3D11BE_SetupViewCBuffer();\r\n\r\n\t//fixme: should be some other variable...\r\n\tID3D11DeviceContext_ClearDepthStencilView(d3ddevctx, fb_backdepthstencil, D3D11_CLEAR_DEPTH, 1, 0);\t//is it faster to clear the stencil too?\r\n\t//fixme: mask the batch's depth so that later batches don't break it.\r\n}\r\n\r\nstatic void BE_SubmitMeshesPortals(batch_t **worldlist, batch_t *dynamiclist)\r\n{\r\n\tbatch_t *batch, *old;\r\n\tint i;\r\n\t/*attempt to draw portal shaders*/\r\n\tif (shaderstate.mode == BEM_STANDARD)\r\n\t{\r\n\t\tfor (i = 0; i < 2; i++)\r\n\t\t{\r\n\t\t\tfor (batch = i?dynamiclist:worldlist[SHADER_SORT_PORTAL]; batch; batch = batch->next)\r\n\t\t\t{\r\n\t\t\t\tif (batch->meshes == batch->firstmesh)\r\n\t\t\t\t\tcontinue;\r\n\r\n\t\t\t\tif (batch->buildmeshes)\r\n\t\t\t\t\tbatch->buildmeshes(batch);\r\n\r\n\t\t\t\t/*draw already-drawn portals as depth-only, to ensure that their contents are not harmed*/\r\n\t\t\t\tBE_SelectMode(BEM_DEPTHONLY);\r\n\t\t\t\tfor (old = worldlist[SHADER_SORT_PORTAL]; old && old != batch; old = old->next)\r\n\t\t\t\t{\r\n\t\t\t\t\tif (old->meshes == old->firstmesh)\r\n\t\t\t\t\t\tcontinue;\r\n\t\t\t\t\tBE_SubmitBatch(old);\r\n\t\t\t\t}\r\n\t\t\t\tif (!old)\r\n\t\t\t\t{\r\n\t\t\t\t\tfor (old = dynamiclist; old != batch; old = old->next)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tif (old->meshes == old->firstmesh)\r\n\t\t\t\t\t\t\tcontinue;\r\n\t\t\t\t\t\tBE_SubmitBatch(old);\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t\tBE_SelectMode(BEM_STANDARD);\r\n\r\n\t\t\t\tR_DrawPortal(batch, worldlist);\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n}\r\n\r\nvoid D3D11BE_SubmitMeshes (batch_t **worldbatches, batch_t **blist, int first, int stop)\r\n{\r\n\tint i;\r\n\r\n\tfor (i = first; i < stop; i++)\r\n\t{\r\n\t\tif (worldbatches)\r\n\t\t{\r\n\t\t\tif (i == SHADER_SORT_PORTAL /*&& !r_noportals.ival*/ && !r_refdef.recurse)\r\n\t\t\t\tBE_SubmitMeshesPortals(worldbatches, blist[i]);\r\n\r\n\t\t\tBE_SubmitMeshesSortList(worldbatches[i]);\r\n\t\t}\r\n\t\tBE_SubmitMeshesSortList(blist[i]);\r\n\t}\r\n}\r\n\r\n#ifdef RTLIGHTS\r\nvoid D3D11BE_BaseEntTextures(const qbyte *worldpvs, const int *worldareas)\r\n{\r\n\tbatch_t *batches[SHADER_SORT_COUNT];\r\n\tBE_GenModelBatches(batches, shaderstate.curdlight, shaderstate.mode, worldpvs, worldareas);\r\n\tD3D11BE_SubmitMeshes(NULL, batches, SHADER_SORT_PORTAL, SHADER_SORT_SEETHROUGH+1);\r\n\tBE_SelectEntity(&r_worldentity);\r\n}\r\n\r\nvoid D3D11BE_GenerateShadowBuffer(void **vbuf_out, vecV_t *verts, int numverts, void **ibuf_out, index_t *indicies, int numindicies)\r\n{\r\n\tD3D11_BUFFER_DESC desc;\r\n\tD3D11_SUBRESOURCE_DATA srd;\r\n\tID3D11Buffer *vbuf;\r\n\tID3D11Buffer *ibuf;\r\n\r\n\r\n\t//generate the ebo, and submit the data to the driver\r\n\tdesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;\r\n\tdesc.ByteWidth = sizeof(*verts) * numverts;\r\n\tdesc.CPUAccessFlags = 0;\r\n\tdesc.MiscFlags = 0;\r\n\tdesc.StructureByteStride = 0;\r\n\tdesc.Usage = D3D11_USAGE_DEFAULT;\r\n\tsrd.pSysMem = verts;\r\n\tsrd.SysMemPitch = 0;\r\n\tsrd.SysMemSlicePitch = 0;\r\n\tID3D11Device_CreateBuffer(pD3DDev11, &desc, &srd, &vbuf);\r\n\r\n\t//generate the vbo, and submit the data to the driver\r\n\tdesc.BindFlags = D3D11_BIND_INDEX_BUFFER;\r\n\tdesc.ByteWidth = sizeof(*indicies) * numindicies;\r\n\tdesc.CPUAccessFlags = 0;\r\n\tdesc.MiscFlags = 0;\r\n\tdesc.StructureByteStride = 0;\r\n\tdesc.Usage = D3D11_USAGE_DEFAULT;\r\n\tsrd.pSysMem = indicies;\r\n\tsrd.SysMemPitch = 0;\r\n\tsrd.SysMemSlicePitch = 0;\r\n\tID3D11Device_CreateBuffer(pD3DDev11, &desc, &srd, &ibuf);\r\n\r\n\tshaderstate.numliveshadowbuffers++;\r\n\r\n\t*vbuf_out = vbuf;\r\n\t*ibuf_out = ibuf;\r\n}\r\nvoid D3D11_DestroyShadowBuffer(void *vbuf_in, void *ibuf_in)\r\n{\r\n\tID3D11Buffer *vbuf = vbuf_in;\r\n\tID3D11Buffer *ibuf = ibuf_in;\r\n\r\n\tif (vbuf && ibuf)\r\n\t{\r\n\t\tID3D11Buffer_Release(vbuf);\r\n\t\tID3D11Buffer_Release(ibuf);\r\n\t\tshaderstate.numliveshadowbuffers--;\r\n\t}\r\n}\r\n//draws all depth-only surfaces from the perspective of the light.\r\nvoid D3D11BE_RenderShadowBuffer(unsigned int numverts, void *vbuf, unsigned int numindicies, void *ibuf)\r\n{\r\n\tID3D11Buffer *vbufs[] = {vbuf};\r\n\tint vstrides[] = {sizeof(vecV_t)};\r\n\tint voffsets[] = {0};\r\n\tint i;\r\n\r\n\tif (!shaderstate.depthonly->prog)\r\n\t\treturn;\r\n\r\n\tD3D11BE_SetupViewCBuffer();\r\n\r\n\tD3D11BE_Cull(SHADER_CULL_FRONT, r_polygonoffset_shadowmap_offset.ival, r_polygonoffset_shadowmap_factor.value);\r\n\r\n\tfor (i = 0; i < shaderstate.lastpasscount; i++)\r\n\t{\r\n\t\tshaderstate.pendingtextures[i] = NULL;\r\n\t\tshaderstate.textureschanged = true;\r\n\t}\r\n\tif (shaderstate.textureschanged)\r\n\t\tID3D11DeviceContext_PSSetShaderResources(d3ddevctx, 0, shaderstate.lastpasscount, shaderstate.pendingtextures);\r\n\tshaderstate.lastpasscount = 0;\r\n\r\n\tID3D11DeviceContext_IASetVertexBuffers(d3ddevctx, 0, 1, vbufs, vstrides, voffsets);\r\n\tID3D11DeviceContext_IASetIndexBuffer(d3ddevctx, ibuf, DXGI_FORMAT_INDEX_UINT, 0);\r\n\r\n\tBE_ApplyUniforms(shaderstate.depthonly->prog, shaderstate.depthonly->prog->permu[0]);\r\n\r\n\tID3D11DeviceContext_DrawIndexed(d3ddevctx, numindicies, 0, 0);\r\n}\r\nvoid D3D11BE_DoneShadows(void)\r\n{\r\n\tD3D11BE_SetupViewCBuffer();\r\n\tBE_SelectEntity(&r_worldentity);\r\n\r\n\tD3D11BE_BeginShadowmapFace();\r\n}\r\n#endif\r\n\r\nvoid D3D11BE_DrawWorld (batch_t **worldbatches)\r\n{\r\n\tbatch_t *batches[SHADER_SORT_COUNT];\r\n\tRSpeedLocals();\r\n\r\n\tshaderstate.curentity = NULL;\r\n\r\n\tshaderstate.depthrange = 0;\r\n\r\n\tif (!r_refdef.recurse)\r\n\t{\r\n\t\tif (shaderstate.wbatch > shaderstate.maxwbatches)\r\n\t\t{\r\n\t\t\tint newm = shaderstate.wbatch;\r\n\t\t\tZ_Free(shaderstate.wbatches);\r\n\t\t\tshaderstate.wbatches = Z_Malloc(newm * sizeof(*shaderstate.wbatches));\r\n\t\t\tmemset(shaderstate.wbatches + shaderstate.maxwbatches, 0, (newm - shaderstate.maxwbatches) * sizeof(*shaderstate.wbatches));\r\n\t\t\tshaderstate.maxwbatches = newm;\r\n\t\t}\r\n\t\tshaderstate.wbatch = 0;\r\n\t}\r\n\r\n\tD3D11BE_SetupViewCBuffer();\r\n\r\n\tshaderstate.curdlight = NULL;\r\n\tBE_GenModelBatches(batches, shaderstate.curdlight, BEM_STANDARD, r_refdef.scenevis, r_refdef.sceneareas);\r\n\r\n\tif (r_refdef.scenevis)\r\n\t{\r\n\t\tBE_UploadLightmaps(false);\r\n\r\n\t\t//make sure the world draws correctly\r\n\t\tr_worldentity.shaderRGBAf[0] = 1;\r\n\t\tr_worldentity.shaderRGBAf[1] = 1;\r\n\t\tr_worldentity.shaderRGBAf[2] = 1;\r\n\t\tr_worldentity.shaderRGBAf[3] = 1;\r\n\t\tr_worldentity.axis[0][0] = 1;\r\n\t\tr_worldentity.axis[1][1] = 1;\r\n\t\tr_worldentity.axis[2][2] = 1;\r\n\r\n#ifdef RTLIGHTS\r\n\t\tif (r_refdef.scenevis && r_shadow_realtime_world.ival)\r\n\t\t\tshaderstate.identitylighting = r_shadow_realtime_world_lightmaps.value;\r\n\t\telse\r\n#endif\r\n\t\t\tshaderstate.identitylighting = r_lightmap_scale.value;\r\n\t\tshaderstate.identitylighting *= r_refdef.hdr_value;\r\n//\t\tshaderstate.identitylightmap = shaderstate.identitylighting / (1<<gl_overbright.ival);\r\n\r\n\t\tBE_SelectMode(BEM_STANDARD);\r\n\r\n\t\tRSpeedRemark();\r\n\t\tD3D11BE_SubmitMeshes(worldbatches, batches, SHADER_SORT_PORTAL, SHADER_SORT_SEETHROUGH+1);\r\n\t\tRSpeedEnd(RSPEED_OPAQUE);\r\n\r\n#ifdef RTLIGHTS\r\n\t\tRSpeedRemark();\r\n\t\tD3D11BE_SelectEntity(&r_worldentity);\r\n\t\tSh_DrawLights(r_refdef.scenevis);\r\n\t\tRSpeedEnd(RSPEED_RTLIGHTS);\r\n#endif\r\n\r\n\t\tRSpeedRemark();\r\n\t\tD3D11BE_SubmitMeshes(worldbatches, batches, SHADER_SORT_SEETHROUGH+1, SHADER_SORT_COUNT);\r\n\t\tRSpeedEnd(RSPEED_TRANSPARENTS);\r\n\r\n\t\tif (r_wireframe.ival)\r\n\t\t{\r\n\t\t\tD3D11BE_SelectMode(BEM_WIREFRAME);\r\n\t\t\tD3D11BE_SubmitMeshes(worldbatches, batches, SHADER_SORT_PORTAL, SHADER_SORT_NEAREST);\r\n\t\t\tD3D11BE_SelectMode(BEM_STANDARD);\r\n\t\t}\r\n\t}\r\n\telse\r\n\t{\r\n\t\tRSpeedRemark();\r\n\t\tshaderstate.identitylighting = 1;\r\n\t\tD3D11BE_SubmitMeshes(NULL, batches, SHADER_SORT_PORTAL, SHADER_SORT_COUNT);\r\n\t\tRSpeedEnd(RSPEED_OPAQUE);\r\n\t}\r\n\r\n\tR_RenderDlights ();\r\n\r\n\tBE_RotateForEntity(&r_worldentity, NULL);\r\n}\r\n\r\nvoid D3D11BE_VBO_Begin(vbobctx_t *ctx, size_t maxsize)\r\n{\r\n}\r\nvoid D3D11BE_VBO_Data(vbobctx_t *ctx, void *data, size_t size, vboarray_t *varray)\r\n{\r\n}\r\nvoid D3D11BE_VBO_Finish(vbobctx_t *ctx, void *edata, size_t esize, vboarray_t *earray, void **vbomem, void **ebomem)\r\n{\r\n}\r\nvoid D3D11BE_VBO_Destroy(vboarray_t *vearray, void *mem)\r\n{\r\n}\r\n\r\nvoid D3D11BE_Scissor(srect_t *rect)\r\n{\r\n\tD3D11_RECT drect;\r\n\tif (rect)\r\n\t{\r\n\t\tdrect.left = (rect->x)*vid.fbpwidth;\r\n\t\tdrect.right = (rect->x + rect->width)*vid.fbpwidth;\r\n\t\tdrect.bottom = (1-(rect->y))*vid.fbpheight;\r\n\t\tdrect.top = (1-(rect->y + rect->height))*vid.fbpheight;\r\n\r\n\t\tif (drect.left < 0)\r\n\t\t\tdrect.left = 0;\r\n\t\tif (drect.top < 0)\r\n\t\t\tdrect.top = 0;\r\n\t}\r\n\telse\r\n\t{\r\n\t\tdrect.left = 0;\r\n\t\tdrect.right = vid.fbpwidth;\r\n\t\tdrect.top = 0;\r\n\t\tdrect.bottom = vid.fbpheight;\r\n\t}\r\n\tID3D11DeviceContext_RSSetScissorRects(d3ddevctx, 1, &drect);\r\n}\r\n\r\n#ifdef RTLIGHTS\r\nvoid D3D11BE_BeginShadowmapFace(void)\r\n{\r\n\tD3D11_VIEWPORT vport;\r\n\r\n\tvport.TopLeftX = r_refdef.pxrect.x;\r\n\tvport.TopLeftY = r_refdef.pxrect.y;\r\n\tvport.Width = r_refdef.pxrect.width;\r\n\tvport.Height = r_refdef.pxrect.height;\r\n\tvport.MinDepth = 0;\r\n\tvport.MaxDepth = 1;\r\n\tID3D11DeviceContext_RSSetViewports(d3ddevctx, 1, &vport);\r\n\r\n\tD3D11BE_Cull(SHADER_CULL_FRONT, r_polygonoffset_shadowmap_offset.ival, r_polygonoffset_shadowmap_factor.value);\r\n}\r\n#endif\r\n\r\n#endif\r\n"
  },
  {
    "path": "engine/d3d/d3d11_image.c",
    "content": "#include \"quakedef.h\"\r\n#ifdef D3D11QUAKE\r\n#include \"winquake.h\"\r\n#include \"shader.h\"\r\n#define COBJMACROS\r\n#include <d3d11.h>\r\nextern ID3D11Device *pD3DDev11;\r\nextern ID3D11DeviceContext *d3ddevctx;\r\n\r\nextern D3D_FEATURE_LEVEL d3dfeaturelevel;\r\n#define D3D_HAVE_FULL_NPOT() (d3dfeaturelevel>=D3D_FEATURE_LEVEL_10_0)\r\n\r\nvoid D3D11BE_UnbindAllTextures(void);\r\n\r\n\r\nID3D11ShaderResourceView *D3D11_Image_View(const texid_t id)\r\n{\r\n\tif (!id || !id->ptr)\r\n\t\treturn NULL;\r\n\tif (!id->ptr2)\r\n\t\tID3D11Device_CreateShaderResourceView(pD3DDev11, (ID3D11Resource *)id->ptr, NULL, (ID3D11ShaderResourceView**)&id->ptr2);\r\n\treturn id->ptr2;\r\n}\r\n\r\nvoid    D3D11_DestroyTexture (texid_t tex)\r\n{\r\n\tif (!tex)\r\n\t\treturn;\r\n\r\n\tif (tex->ptr2)\r\n\t\tID3D11ShaderResourceView_Release((ID3D11ShaderResourceView*)tex->ptr2);\r\n\ttex->ptr2 = NULL;\r\n\r\n\tif (tex->ptr)\r\n\t\tID3D11Texture2D_Release((ID3D11Texture2D*)tex->ptr);\r\n\ttex->ptr = NULL;\r\n}\r\n\r\nqboolean D3D11_LoadTextureMips(image_t *tex, const struct pendingtextureinfo *mips)\r\n{\r\n\tunsigned int blockbytes, blockwidth, blockheight, blockdepth;\r\n\tHRESULT hr;\r\n\tD3D11_TEXTURE2D_DESC tdesc = {0};\r\n\tD3D11_SUBRESOURCE_DATA *subresdesc;\r\n\tint i, layer;\r\n\r\n\tif (!sh_config.texfmt[mips->encoding])\r\n\t{\r\n\t\tCon_Printf(\"Texture encoding %i not supported by d3d11\\n\", mips->encoding);\r\n\t\treturn false;\r\n\t}\r\n\tif (mips->type != (tex->flags & IF_TEXTYPEMASK)>>IF_TEXTYPESHIFT)\r\n\t\treturn false;\r\n\r\n\ttdesc.Width = mips->mip[0].width;\r\n\ttdesc.Height = mips->mip[0].height;\r\n\ttdesc.ArraySize = 1;\r\n\ttdesc.SampleDesc.Count = 1;\r\n\ttdesc.SampleDesc.Quality = 0;\r\n\ttdesc.Usage = mips->mip[0].data?D3D11_USAGE_IMMUTABLE:D3D11_USAGE_DYNAMIC;\r\n\ttdesc.BindFlags = D3D11_BIND_SHADER_RESOURCE;\r\n\ttdesc.CPUAccessFlags = (mips->mip[0].data)?0:D3D11_CPU_ACCESS_WRITE;\r\n\ttdesc.MiscFlags = 0;\r\n\ttdesc.Format = DXGI_FORMAT_UNKNOWN;\r\n\r\n\tif (tex->flags & IF_RENDERTARGET)\r\n\t{\r\n\t\ttdesc.BindFlags |= D3D11_BIND_RENDER_TARGET;\r\n\t\ttdesc.Usage = D3D11_USAGE_DEFAULT;\r\n\t\ttdesc.CPUAccessFlags = 0;\r\n\t}\r\n\r\n\tif (mips->type == PTI_CUBE)\r\n\t{\r\n\t\ttdesc.ArraySize *= 6;\r\n\t\ttdesc.MiscFlags |= D3D11_RESOURCE_MISC_TEXTURECUBE;\r\n\t}\r\n//\telse if (mips->type == PTI_2D_ARRAY)\r\n//\t{\r\n//\t\ttdesc.ArraySize *= mips->mip[0].depth;\r\n//\t}\r\n\telse if (mips->type != PTI_2D)\r\n\t\treturn false;\t//nyi\r\n\r\n//d3d11.1 formats\r\n#define DXGI_FORMAT_B4G4R4A4_UNORM 115\r\n\r\n\t//dxgi formats are expressed in little-endian bit order. byte-aligned formats are always in byte order and are thus little-endian even on big-endian machines.\r\n\t//so byte aligned have the same order, while misligned need reversed order.\r\n\tswitch(mips->encoding)\r\n\t{\r\n\tcase PTI_DEPTH16:\r\n\t\ttdesc.Format = DXGI_FORMAT_D16_UNORM;\r\n\t\ttdesc.BindFlags = D3D11_BIND_DEPTH_STENCIL;\r\n\t\tbreak;\r\n\tcase PTI_DEPTH24:\r\n\t\ttdesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;\r\n\t\ttdesc.BindFlags = D3D11_BIND_DEPTH_STENCIL;\r\n\t\tbreak;\r\n\tcase PTI_DEPTH32:\r\n\t\ttdesc.Format = DXGI_FORMAT_D32_FLOAT;\r\n\t\ttdesc.BindFlags = D3D11_BIND_DEPTH_STENCIL;\r\n\t\tbreak;\r\n\tcase PTI_DEPTH24_8:\r\n\t\ttdesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;\r\n\t\ttdesc.BindFlags = D3D11_BIND_DEPTH_STENCIL;\r\n\t\tbreak;\r\n\tcase PTI_RGB565:\r\n\t\ttdesc.Format = DXGI_FORMAT_B5G6R5_UNORM;\r\n\t\tbreak;\r\n\tcase PTI_ARGB1555:\r\n\t\ttdesc.Format = DXGI_FORMAT_B5G5R5A1_UNORM;\r\n\t\tbreak;\r\n\tcase PTI_RGBA5551:\r\n//\t\ttdesc.Format = DXGI_FORMAT_A1B5G5R5_UNORM;\r\n\t\tbreak;\r\n\tcase PTI_ARGB4444:\r\n\t\ttdesc.Format = DXGI_FORMAT_B4G4R4A4_UNORM;\t//DX11.1\r\n\t\tbreak;\r\n\tcase PTI_RGBA4444:\r\n//\t\ttdesc.Format = DXGI_FORMAT_A4B4G4R4_UNORM;\r\n\t\tbreak;\r\n\tcase PTI_RGB8:\r\n//\t\ttdesc.Format = DXGI_FORMAT_R8G8B8_UNORM;\r\n\t\tbreak;\r\n\tcase PTI_BGR8:\r\n//\t\ttdesc.Format = DXGI_FORMAT_B8G8R8_UNORM;\r\n\t\tbreak;\r\n\tcase PTI_RGB8_SRGB:\r\n//\t\ttdesc.Format = DXGI_FORMAT_R8G8B8_SRGB;\r\n\t\tbreak;\r\n\tcase PTI_BGR8_SRGB:\r\n//\t\ttdesc.Format = DXGI_FORMAT_B8G8R8_SRGB;\r\n\t\tbreak;\r\n\tcase PTI_RGBA8:\r\n\t\ttdesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;\r\n\t\tbreak;\r\n\tcase PTI_RGBX8:\t//d3d11 has no alphaless format. be sure to proprly disable alpha in the shader. \r\n\t\ttdesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;\r\n\t\tbreak;\r\n\tcase PTI_BGRA8:\r\n\t\ttdesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;\r\n\t\tbreak;\r\n\tcase PTI_BGRX8:\r\n\t\ttdesc.Format = DXGI_FORMAT_B8G8R8X8_UNORM;\r\n\t\tbreak;\r\n\tcase PTI_A2BGR10:\t//mostly for rendertargets, might also be useful for overbight lightmaps.\r\n\t\ttdesc.Format = DXGI_FORMAT_R10G10B10A2_UNORM;\r\n\t\tbreak;\r\n\tcase PTI_E5BGR9:\r\n\t\ttdesc.Format = DXGI_FORMAT_R9G9B9E5_SHAREDEXP;\r\n\t\tbreak;\r\n\tcase PTI_B10G11R11F:\r\n\t\ttdesc.Format = DXGI_FORMAT_R11G11B10_FLOAT;\r\n\t\tbreak;\r\n\tcase PTI_RGBA8_SRGB:\r\n\t\ttdesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM_SRGB;\r\n\t\tbreak;\r\n\tcase PTI_RGBX8_SRGB:\t//d3d11 has no alphaless format. be sure to proprly disable alpha in the shader. \r\n\t\ttdesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM_SRGB;\r\n\t\tbreak;\r\n\tcase PTI_BGRA8_SRGB:\r\n\t\ttdesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM_SRGB;\r\n\t\tbreak;\r\n\tcase PTI_BGRX8_SRGB:\r\n\t\ttdesc.Format = DXGI_FORMAT_B8G8R8X8_UNORM_SRGB;\r\n\t\tbreak;\r\n\r\n\tcase PTI_BC1_RGB:\t//d3d11 provides no way to disable alpha with dxt1. be sure to proprly disable alpha in the shader. \r\n\tcase PTI_BC1_RGBA:\r\n\t\ttdesc.Format = DXGI_FORMAT_BC1_UNORM;\r\n\t\tbreak;\r\n\tcase PTI_BC2_RGBA:\r\n\t\ttdesc.Format = DXGI_FORMAT_BC2_UNORM;\r\n\t\tbreak;\r\n\tcase PTI_BC3_RGBA:\r\n\t\ttdesc.Format = DXGI_FORMAT_BC3_UNORM;\r\n\t\tbreak;\r\n\tcase PTI_BC1_RGB_SRGB:\t//d3d11 provides no way to disable alpha with dxt1. be sure to proprly disable alpha in the shader. \r\n\tcase PTI_BC1_RGBA_SRGB:\r\n\t\ttdesc.Format = DXGI_FORMAT_BC1_UNORM_SRGB;\r\n\t\tbreak;\r\n\tcase PTI_BC2_RGBA_SRGB:\r\n\t\ttdesc.Format = DXGI_FORMAT_BC2_UNORM_SRGB;\r\n\t\tbreak;\r\n\tcase PTI_BC3_RGBA_SRGB:\r\n\t\ttdesc.Format = DXGI_FORMAT_BC3_UNORM_SRGB;\r\n\t\tbreak;\r\n\tcase PTI_BC4_R:\r\n\t\ttdesc.Format = DXGI_FORMAT_BC4_UNORM;\r\n\t\tbreak;\r\n\tcase PTI_BC4_R_SNORM:\r\n\t\ttdesc.Format = DXGI_FORMAT_BC4_SNORM;\r\n\t\tbreak;\r\n\tcase PTI_BC5_RG:\r\n\t\ttdesc.Format = DXGI_FORMAT_BC5_UNORM;\r\n\t\tbreak;\r\n\tcase PTI_BC5_RG_SNORM:\r\n\t\ttdesc.Format = DXGI_FORMAT_BC5_SNORM;\r\n\t\tbreak;\r\n\tcase PTI_BC6_RGB_UFLOAT:\r\n\t\ttdesc.Format = DXGI_FORMAT_BC6H_UF16;\r\n\t\tbreak;\r\n\tcase PTI_BC6_RGB_SFLOAT:\r\n\t\ttdesc.Format = DXGI_FORMAT_BC6H_SF16;\r\n\t\tbreak;\r\n\tcase PTI_BC7_RGBA:\r\n\t\ttdesc.Format = DXGI_FORMAT_BC7_UNORM;\r\n\t\tbreak;\r\n\tcase PTI_BC7_RGBA_SRGB:\r\n\t\ttdesc.Format = DXGI_FORMAT_BC7_UNORM_SRGB;\r\n\t\tbreak;\r\n\r\n\tcase PTI_R16:\r\n\t\ttdesc.Format = DXGI_FORMAT_R16_UNORM;\r\n\t\tbreak;\r\n\tcase PTI_RGBA16:\r\n\t\ttdesc.Format = DXGI_FORMAT_R16G16B16A16_UNORM;\r\n\t\tbreak;\r\n\r\n\tcase PTI_R16F:\r\n\t\ttdesc.Format = DXGI_FORMAT_R16_FLOAT;\r\n\t\tbreak;\r\n\tcase PTI_R32F:\r\n\t\ttdesc.Format = DXGI_FORMAT_R32_FLOAT;\r\n\t\tbreak;\r\n\tcase PTI_RGBA16F:\r\n\t\ttdesc.Format = DXGI_FORMAT_R16G16B16A16_FLOAT;\r\n\t\tbreak;\r\n\tcase PTI_RGBA32F:\r\n\t\ttdesc.Format = DXGI_FORMAT_R32G32B32A32_FLOAT;\r\n\t\tbreak;\r\n\tcase PTI_RGB32F:\r\n\t\ttdesc.Format = DXGI_FORMAT_R32G32B32_FLOAT;\r\n\t\tbreak;\r\n\tcase PTI_L8:\t//UNSUPPORTED\r\n\tcase PTI_P8:\t//R8, but different usage.\r\n\tcase PTI_R8:\r\n\t\ttdesc.Format = DXGI_FORMAT_R8_UNORM;\r\n\t\tbreak;\r\n\tcase PTI_L8A8: //UNSUPPORTED\r\n\tcase PTI_RG8:\r\n\t\ttdesc.Format = DXGI_FORMAT_R8G8_UNORM;\r\n\t\tbreak;\r\n\tcase PTI_R8_SNORM:\r\n\t\ttdesc.Format = DXGI_FORMAT_R8_SNORM;\r\n\t\tbreak;\r\n\tcase PTI_RG8_SNORM:\r\n\t\ttdesc.Format = DXGI_FORMAT_R8G8_SNORM;\r\n\t\tbreak;\r\n\r\n\tcase PTI_L8_SRGB:\t\t\t//no swizzles / single-channel srgb\r\n\tcase PTI_L8A8_SRGB:\t\t\t//no swizzles / single-channel srgb\r\n\tcase PTI_ETC1_RGB8:\t\t\t//not invented here...\r\n\tcase PTI_ETC2_RGB8:\r\n\tcase PTI_ETC2_RGB8A1:\r\n\tcase PTI_ETC2_RGB8A8:\r\n\tcase PTI_ETC2_RGB8_SRGB:\r\n\tcase PTI_ETC2_RGB8A1_SRGB:\r\n\tcase PTI_ETC2_RGB8A8_SRGB:\r\n\tcase PTI_EAC_R11:\r\n\tcase PTI_EAC_R11_SNORM:\r\n\tcase PTI_EAC_RG11:\r\n\tcase PTI_EAC_RG11_SNORM:\r\n\tcase PTI_ASTC_4X4_LDR:\t\t\t//not invented here...\r\n\tcase PTI_ASTC_5X4_LDR:\r\n\tcase PTI_ASTC_5X5_LDR:\r\n\tcase PTI_ASTC_6X5_LDR:\r\n\tcase PTI_ASTC_6X6_LDR:\r\n\tcase PTI_ASTC_8X5_LDR:\r\n\tcase PTI_ASTC_8X6_LDR:\r\n\tcase PTI_ASTC_10X5_LDR:\r\n\tcase PTI_ASTC_10X6_LDR:\r\n\tcase PTI_ASTC_8X8_LDR:\r\n\tcase PTI_ASTC_10X8_LDR:\r\n\tcase PTI_ASTC_10X10_LDR:\r\n\tcase PTI_ASTC_12X10_LDR:\r\n\tcase PTI_ASTC_12X12_LDR:\r\n\tcase PTI_ASTC_4X4_HDR:\r\n\tcase PTI_ASTC_5X4_HDR:\r\n\tcase PTI_ASTC_5X5_HDR:\r\n\tcase PTI_ASTC_6X5_HDR:\r\n\tcase PTI_ASTC_6X6_HDR:\r\n\tcase PTI_ASTC_8X5_HDR:\r\n\tcase PTI_ASTC_8X6_HDR:\r\n\tcase PTI_ASTC_10X5_HDR:\r\n\tcase PTI_ASTC_10X6_HDR:\r\n\tcase PTI_ASTC_8X8_HDR:\r\n\tcase PTI_ASTC_10X8_HDR:\r\n\tcase PTI_ASTC_10X10_HDR:\r\n\tcase PTI_ASTC_12X10_HDR:\r\n\tcase PTI_ASTC_12X12_HDR:\r\n\tcase PTI_ASTC_4X4_SRGB:\r\n\tcase PTI_ASTC_5X4_SRGB:\r\n\tcase PTI_ASTC_5X5_SRGB:\r\n\tcase PTI_ASTC_6X5_SRGB:\r\n\tcase PTI_ASTC_6X6_SRGB:\r\n\tcase PTI_ASTC_8X5_SRGB:\r\n\tcase PTI_ASTC_8X6_SRGB:\r\n\tcase PTI_ASTC_10X5_SRGB:\r\n\tcase PTI_ASTC_10X6_SRGB:\r\n\tcase PTI_ASTC_8X8_SRGB:\r\n\tcase PTI_ASTC_10X8_SRGB:\r\n\tcase PTI_ASTC_10X10_SRGB:\r\n\tcase PTI_ASTC_12X10_SRGB:\r\n\tcase PTI_ASTC_12X12_SRGB:\r\n#ifdef FTE_TARGET_WEB\r\n\tcase PTI_WHOLEFILE:\t\t\t//basically webgl only...\r\n#endif\r\n\tcase PTI_MAX:\t\t\t\t//not actually valid...\r\n\tcase PTI_EMULATED:\t\t\t//not hardware-compatible.\r\n\t\tbreak;\r\n\t}\r\n\tif (tdesc.Format == DXGI_FORMAT_UNKNOWN)\r\n\t{\r\n\t\treturn false;\r\n\t}\r\n\r\n\tImage_BlockSizeForEncoding(mips->encoding, &blockbytes, &blockwidth, &blockheight, &blockdepth);\r\n\r\n\tif (!mips->mip[0].data)\r\n\t{\r\n\t\tsubresdesc = alloca(sizeof(*subresdesc));\r\n\t\tsubresdesc[0].pSysMem = NULL;\r\n\t\tsubresdesc[0].SysMemPitch = 0;\r\n\t\tsubresdesc[0].SysMemSlicePitch = 0;\r\n\t\t//one mip, but no data. happens with rendertargets\r\n\t\ttdesc.MipLevels = 1;\r\n\t}\r\n\telse\r\n\t{\r\n\t\tsubresdesc = alloca(tdesc.ArraySize*mips->mipcount*sizeof(*subresdesc));\r\n\t\tfor (layer = 0; layer < tdesc.ArraySize; layer++)\r\n\t\t{\r\n\t\t\tfor (i = 0; i < mips->mipcount; i++)\r\n\t\t\t{\r\n\t\t\t\tsubresdesc[i+layer*mips->mipcount].SysMemPitch = ((mips->mip[i].width+blockwidth-1)/blockwidth) * blockbytes;\r\n\t\t\t\tsubresdesc[i+layer*mips->mipcount].SysMemSlicePitch = subresdesc[i].SysMemPitch * ((mips->mip[i].width+blockheight-1)/blockheight);\r\n\t\t\t\tsubresdesc[i+layer*mips->mipcount].pSysMem = (qbyte*)mips->mip[i].data + subresdesc[i+layer*mips->mipcount].SysMemSlicePitch*layer;\r\n\t\t\t}\r\n\t\t}\r\n\t\ttdesc.MipLevels = mips->mipcount;\r\n\t}\r\n\r\n\tD3D11_DestroyTexture(tex);\r\n\thr = ID3D11Device_CreateTexture2D(pD3DDev11, &tdesc, (mips->mip[0].data?subresdesc:NULL), (ID3D11Texture2D**)&tex->ptr);\r\n\r\n\treturn !FAILED(hr);\r\n}\r\nvoid D3D11_UploadLightmap(lightmapinfo_t *lm)\r\n{\r\n\textern cvar_t r_lightmap_nearest;\r\n\tstruct pendingtextureinfo mips;\r\n\timage_t *tex;\r\n\tlm->modified = false;\r\n\tif (!TEXVALID(lm->lightmap_texture))\r\n\t{\r\n\t\tlm->lightmap_texture = Image_CreateTexture(\"***lightmap***\", NULL, (r_lightmap_nearest.ival?IF_NEAREST:IF_LINEAR));\r\n\t\tif (!lm->lightmap_texture)\r\n\t\t\treturn;\r\n\t}\r\n\ttex = lm->lightmap_texture;\r\n\r\n\tmips.extrafree = NULL;\r\n\tmips.type = PTI_2D;\r\n\tmips.mip[0].data = lm->lightmaps;\r\n\tmips.mip[0].needfree = false;\r\n\tmips.mip[0].width = lm->width;\r\n\tmips.mip[0].height = lm->height;\r\n\tmips.mip[0].datasize = lm->width*lm->height*4;\r\n\tswitch (lm->fmt)\r\n\t{\r\n\tdefault:\r\n\tcase PTI_A2BGR10:\r\n\tcase PTI_E5BGR9:\r\n\tcase PTI_B10G11R11F:\r\n\tcase PTI_RGBA16F:\r\n\tcase PTI_RGBA32F:\r\n\t\tmips.encoding = lm->fmt;\r\n\t\tbreak;\r\n\tcase PTI_BGRA8:\r\n\t\tmips.encoding = PTI_BGRX8;\r\n\t\tbreak;\r\n\tcase PTI_RGBA8:\r\n\t\tmips.encoding = PTI_RGBX8;\r\n\t\tbreak;\r\n\tcase PTI_L8:\r\n\t\tmips.encoding = PTI_R8;\t//FIXME: unspported\r\n\t\tbreak;\r\n\t}\r\n\tmips.mipcount = 1;\r\n\tD3D11_LoadTextureMips(tex, &mips);\r\n\ttex->width = lm->width;\r\n\ttex->height = lm->height;\r\n\r\n\tlm->lightmap_texture = tex;\r\n}\r\n\r\n#ifdef RTLIGHTS\r\nstatic const int shadowfmt = 1;\r\nstatic const int shadowfmts[][3] =\r\n{\r\n\t//sampler,\t\t\t\tcreation,\t\t\trender\r\n\t{DXGI_FORMAT_R24_UNORM_X8_TYPELESS, DXGI_FORMAT_R24G8_TYPELESS, DXGI_FORMAT_D24_UNORM_S8_UINT},\r\n\t{DXGI_FORMAT_R16_UNORM, DXGI_FORMAT_R16_TYPELESS, DXGI_FORMAT_D16_UNORM},\r\n\t{DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_R8G8B8A8_UNORM}\r\n};\r\nimage_t shadowmap_texture[2];\r\nID3D11DepthStencilView *shadowmap_dsview[2];\r\nID3D11RenderTargetView *shadowmap_rtview[2];\r\ntexid_t D3D11_GetShadowMap(int id)\r\n{\r\n\ttexid_t tex = &shadowmap_texture[id];\r\n\tif (!tex->ptr)\r\n\t{\r\n\t\treturn r_nulltex;\r\n\t}\r\n\tif (!tex->ptr2)\r\n\t{\r\n\t\tD3D11_SHADER_RESOURCE_VIEW_DESC desc;\r\n\t\tdesc.Format = shadowfmts[shadowfmt][0];\r\n\t\tdesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;\r\n\t\tdesc.Texture2D.MostDetailedMip = 0;\r\n\t\tdesc.Texture2D.MipLevels = -1;\r\n\t\tID3D11Device_CreateShaderResourceView(pD3DDev11, (ID3D11Resource *)tex->ptr, &desc, (ID3D11ShaderResourceView**)&tex->ptr2);\r\n\t}\r\n\treturn tex;\r\n}\r\nvoid D3D11_TerminateShadowMap(void)\r\n{\r\n\tint i;\r\n\tfor (i = 0; i < sizeof(shadowmap_texture)/sizeof(shadowmap_texture[0]); i++)\r\n\t{\r\n\t\tif (shadowmap_dsview[i])\r\n\t\t\tID3D11DepthStencilView_Release(shadowmap_dsview[i]);\r\n\t\tshadowmap_dsview[i] = NULL;\r\n\t\tD3D11_DestroyTexture(&shadowmap_texture[i]);\r\n\t}\r\n}\r\nqboolean D3D11_BeginShadowMap(int id, int w, int h)\r\n{\r\n\tD3D11_TEXTURE2D_DESC texdesc;\r\n\tHRESULT hr;\r\n\r\n\tif (!shadowmap_dsview[id] && !shadowmap_rtview[id])\r\n\t{\r\n\t\tmemset(&texdesc, 0, sizeof(texdesc));\r\n\r\n\t\ttexdesc.Width = w;\r\n\t\ttexdesc.Height = h;\r\n\t\ttexdesc.MipLevels = 1;\r\n\t\ttexdesc.ArraySize = 1;\r\n\t\ttexdesc.Format = shadowfmts[shadowfmt][1];\r\n\t\ttexdesc.SampleDesc.Count = 1;\r\n\t\ttexdesc.SampleDesc.Quality = 0;\r\n\t\ttexdesc.Usage = D3D11_USAGE_DEFAULT;\r\n\t\ttexdesc.BindFlags = D3D11_BIND_DEPTH_STENCIL | D3D11_BIND_SHADER_RESOURCE;\r\n\t\ttexdesc.CPUAccessFlags = 0;\r\n\t\ttexdesc.MiscFlags = 0;\r\n\r\n\t\tif (shadowfmt == 2)\r\n\t\t\ttexdesc.BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE;\r\n\r\n\t\t// Create the texture\r\n\t\tif (!shadowmap_texture[id].ptr)\r\n\t\t{\r\n\t\t\thr = ID3D11Device_CreateTexture2D(pD3DDev11, &texdesc, NULL, (ID3D11Texture2D **)&shadowmap_texture[id].ptr);\r\n\t\t\tif (FAILED(hr))\r\n\t\t\t\treturn false;\r\n\t\t}\r\n\r\n\r\n\t\tif (shadowfmt == 2)\r\n\t\t{\r\n\t\t\thr = ID3D11Device_CreateRenderTargetView(pD3DDev11, (ID3D11Resource *)shadowmap_texture[id].ptr, NULL, &shadowmap_rtview[id]);\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tD3D11_DEPTH_STENCIL_VIEW_DESC rtdesc;\r\n\t\t\trtdesc.Format = shadowfmts[shadowfmt][2];\r\n\t\t\trtdesc.Flags = 0;\r\n\t\t\trtdesc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D;\r\n\t\t\trtdesc.Texture2D.MipSlice = 0;\r\n\t\t\thr = ID3D11Device_CreateDepthStencilView(pD3DDev11, (ID3D11Resource *)shadowmap_texture[id].ptr, &rtdesc, &shadowmap_dsview[id]);\r\n\t\t}\r\n\t\tif (FAILED(hr))\r\n\t\t\treturn false;\r\n\t}\r\n\tD3D11BE_UnbindAllTextures();\r\n\tif (shadowfmt == 2)\r\n\t{\r\n\t\tfloat colours[4] = {0, 1, 0, 0};\r\n\t\tcolours[0] = frandom();\r\n\t\tcolours[1] = frandom();\r\n\t\tcolours[2] = frandom();\r\n\t\tID3D11DeviceContext_OMSetRenderTargets(d3ddevctx, 1, &shadowmap_rtview[id], shadowmap_dsview[id]);\r\n\t\tID3D11DeviceContext_ClearRenderTargetView(d3ddevctx, shadowmap_rtview[id], colours);\r\n\t}\r\n\telse\r\n\t{\r\n\t\tID3D11DeviceContext_OMSetRenderTargets(d3ddevctx, 0, NULL, shadowmap_dsview[id]);\r\n\t\tID3D11DeviceContext_ClearDepthStencilView(d3ddevctx, shadowmap_dsview[id], D3D11_CLEAR_DEPTH, 1.0f, 0);\r\n\t}\r\n\treturn true;\r\n}\r\nvoid D3D11_EndShadowMap(void)\r\n{\r\n\textern ID3D11RenderTargetView *fb_backbuffer;\r\n\textern ID3D11DepthStencilView *fb_backdepthstencil;\r\n\tID3D11DeviceContext_OMSetRenderTargets(d3ddevctx, 1, &fb_backbuffer, fb_backdepthstencil);\r\n}\r\n#endif\r\n\r\n#endif\r\n"
  },
  {
    "path": "engine/d3d/d3d11_shader.c",
    "content": "#include \"quakedef.h\"\n\n#ifdef D3D11QUAKE\n#include \"shader.h\"\n#include \"winquake.h\"\n#define COBJMACROS\n#include <d3d11.h>\nextern ID3D11Device *pD3DDev11;\n\n//#include <D3D11Shader.h>\t//apparently requires win8 sdk, despite being a win7 thing.\n\n#ifndef IID_ID3DBlob\n\t//microsoft can be such a pain sometimes.\n\ttypedef struct _D3D_SHADER_MACRO\n\t{\n\t\tLPCSTR Name;\n\t\tLPCSTR Definition;\n\n\t} D3D_SHADER_MACRO, *LPD3D_SHADER_MACRO;\n\n\ttypedef enum _D3D_INCLUDE_TYPE { \n\t  D3D_INCLUDE_LOCAL         = 0,\n\t  D3D_INCLUDE_SYSTEM        = ( D3D_INCLUDE_LOCAL + 1 ),\n\t  D3D_INCLUDE_FORCE_DWORD   = 0x7fffffff \n\t} D3D_INCLUDE_TYPE;\n\n\t#undef INTERFACE\n\t#define INTERFACE ID3DInclude\n\tDECLARE_INTERFACE_(INTERFACE, IUnknown)\n\t{\n\t\tSTDMETHOD(Open)(THIS_ D3D_INCLUDE_TYPE IncludeType, LPCSTR pFileName, LPCVOID pParentData, LPCVOID *ppData, UINT *pBytes) PURE;\n\t\tSTDMETHOD(Close)(THIS_ LPCVOID pData) PURE;\n\t};\n\n\t#undef INTERFACE\n\t#define INTERFACE ID3DBlob\n\tDECLARE_INTERFACE_(INTERFACE, IUnknown)\n\t{\n\t\tSTDMETHOD(QueryInterface)(THIS_ REFIID iid, LPVOID *ppv) PURE;\n\t\tSTDMETHOD_(ULONG, AddRef)(THIS) PURE;\n\t\tSTDMETHOD_(ULONG, Release)(THIS) PURE;\n\t\tSTDMETHOD_(LPVOID, GetBufferPointer)(THIS) PURE;\n\t\tSTDMETHOD_(SIZE_T, GetBufferSize)(THIS) PURE;\n\t};\n\t#undef INTERFACE\n/*\n\t#define D3D11_SHADER_VARIABLE_DESC void\n\ttypedef unsigned int D3D_SHADER_INPUT_TYPE;\n\ttypedef unsigned int D3D_RESOURCE_RETURN_TYPE;\n\ttypedef unsigned int D3D_SRV_DIMENSION;\n\ttypedef struct D3D11_SHADER_INPUT_BIND_DESC {\n\t\tLPCSTR\t\t\t\t\t\tName;\n\t\tD3D_SHADER_INPUT_TYPE\t\tType;\n\t\tUINT\t\t\t\t\t\tBindPoint;\n\t\tUINT\t\t\t\t\t\tBindCount;\n\t\tUINT\t\t\t\t\t\tuFlags;\n\t\tD3D_RESOURCE_RETURN_TYPE\tReturnType;\n\t\tD3D_SRV_DIMENSION\t\t\tDimension;\n\t\tUINT\t\t\t\t\t\tNumSamples;\n\t} D3D11_SHADER_INPUT_BIND_DESC;\n\t#define ID3D11ShaderReflectionConstantBuffer void\n\t#define ID3D11ShaderReflectionType void\n\t#define INTERFACE ID3D11ShaderReflectionVariable\n\tDECLARE_INTERFACE(ID3D11ShaderReflectionVariable)\n\t{\n\t\tSTDMETHOD(GetDesc)(THIS_ D3D11_SHADER_VARIABLE_DESC *pDesc) PURE;\n\n\t\tSTDMETHOD_(ID3D11ShaderReflectionType*, GetType)(THIS) PURE;\n\t\tSTDMETHOD_(ID3D11ShaderReflectionConstantBuffer*, GetBuffer)(THIS) PURE;\n\n\t\tSTDMETHOD_(UINT, GetInterfaceSlot)(THIS_ UINT uArrayIndex) PURE;\n\t};\n\t#undef INTERFACE\n\t#define D3D11_SHADER_DESC void\n\t#define D3D11_SIGNATURE_PARAMETER_DESC void\n\t#define INTERFACE ID3D11ShaderReflection\n\tDECLARE_INTERFACE_(INTERFACE, IUnknown)\n\t{\n\t\tSTDMETHOD(QueryInterface)(THIS_ REFIID iid, LPVOID *ppv) PURE;\n\t\tSTDMETHOD_(ULONG, AddRef)(THIS) PURE;\n\t\tSTDMETHOD_(ULONG, Release)(THIS) PURE;\n\n\t\tSTDMETHOD(GetDesc)(THIS_ D3D11_SHADER_DESC *pDesc) PURE;\n\n\t\tSTDMETHOD_(ID3D11ShaderReflectionConstantBuffer*, GetConstantBufferByIndex)(THIS_ UINT Index) PURE;\n\t\tSTDMETHOD_(ID3D11ShaderReflectionConstantBuffer*, GetConstantBufferByName)(THIS_ LPCSTR Name) PURE;\n\n\t\tSTDMETHOD(GetResourceBindingDesc)(THIS_ UINT ResourceIndex,\n\t\t\t\t\t\t\t\t\t\t  D3D11_SHADER_INPUT_BIND_DESC *pDesc) PURE;\n\n\t\tSTDMETHOD(GetInputParameterDesc)(THIS_ UINT ParameterIndex,\n\t\t\t\t\t\t\t\t\t\t D3D11_SIGNATURE_PARAMETER_DESC *pDesc) PURE;\n\t\tSTDMETHOD(GetOutputParameterDesc)(THIS_ UINT ParameterIndex,\n\t\t\t\t\t\t\t\t\t\t  D3D11_SIGNATURE_PARAMETER_DESC *pDesc) PURE;\n\t\tSTDMETHOD(GetPatchConstantParameterDesc)(THIS_ UINT ParameterIndex,\n\t\t\t\t\t\t\t\t\t\t\t\t D3D11_SIGNATURE_PARAMETER_DESC *pDesc) PURE;\n\n\t\tSTDMETHOD_(ID3D11ShaderReflectionVariable*, GetVariableByName)(THIS_ LPCSTR Name) PURE;\n\t\tSTDMETHOD(GetResourceBindingDescByName)(THIS_ LPCSTR Name, D3D11_SHADER_INPUT_BIND_DESC *pDesc) PURE;\n\t\t//more stuff\n\t};\n\t#define ID3D11ShaderReflection_GetVariableByName(r,v) r->lpVtbl->GetVariableByName(r,v)\n\t#undef INTERFACE\n\t*/\n#else\n#include <d3d11shader.h>\n#endif\n\n//const GUID IID_ID3D11ShaderReflection = {0x8d536ca1, 0x0cca, 0x4956, {0xa8, 0x37, 0x78, 0x69, 0x63, 0x75, 0x55, 0x84}};\n//const GUID IID_ID3D11ShaderReflection = {0x0a233719, 0x3960, 0x4578, {0x9d, 0x7c, 0x20, 0x3b, 0x8b, 0x1d, 0x9c, 0xc1}};\n#define ID3DBlob_GetBufferPointer(b) b->lpVtbl->GetBufferPointer(b)\n#define ID3DBlob_Release(b) b->lpVtbl->Release(b)\n#define ID3DBlob_GetBufferSize(b) b->lpVtbl->GetBufferSize(b)\n//#define ID3D11ShaderReflection_Release IUnknown_Release\n\nHRESULT (WINAPI *pD3DCompile) (\n\tLPCVOID pSrcData,\n\tSIZE_T SrcDataSize,\n\tLPCSTR pSourceName,\n\tconst D3D_SHADER_MACRO *pDefines,\n\tID3DInclude *pInclude,\n\tLPCSTR pEntrypoint,\n\tLPCSTR pTarget,\n\tUINT Flags1,\n\tUINT Flags2,\n\tID3DBlob **ppCode,\n\tID3DBlob **ppErrorMsgs\n);\n\nstatic dllhandle_t *shaderlib;\n\n\nD3D_FEATURE_LEVEL d3dfeaturelevel;\n\n\nHRESULT STDMETHODCALLTYPE d3dinclude_Close(ID3DInclude *this, LPCVOID pData)\n{\n\tfree((void*)pData);\n\treturn S_OK;\n}\nHRESULT STDMETHODCALLTYPE d3dinclude_Open(ID3DInclude *this, D3D_INCLUDE_TYPE IncludeType, LPCSTR pFileName, LPCVOID pParentData, LPCVOID *ppData, UINT *pBytes)\n{\n\tif (IncludeType == D3D_INCLUDE_SYSTEM)\n\t{\n\t\tif (!strcmp(pFileName, \"ftedefs.h\") || !strcmp(pFileName, \"sys/defs.h\"))\n\t\t{\n\t\t\tstatic const char *defstruct =\n\t\t\t\t\"cbuffer ftemodeldefs : register(b0)\\n\"\n\t\t\t\t\"{\\n\"\n\t\t\t\t\t\"matrix m_model;\\n\"\n\t\t\t\t\t\"float3 e_eyepos; float e_time;\\n\"\n\t\t\t\t\t\"float3 e_light_ambient; float pad1;\\n\"\n\t\t\t\t\t\"float3 e_light_dir; float pad2;\\n\"\n\t\t\t\t\t\"float3 e_light_mul; float pad3;\\n\"\n\t\t\t\t\t\"float4 e_lmscale[4];\\n\"\n\t\t\t\t\t\"float4 e_uppercolour;\\n\"\n\t\t\t\t\t\"float4 e_lowercolour;\\n\"\n\t\t\t\t\t\"float4 e_colourmod;\\n\"\n\t\t\t\t\t\"float4 e_glowmod;\\n\"\n\t\t\t\t\"};\\n\"\n\t\t\t\t\"cbuffer fteviewdefs : register(b1)\\n\"\n\t\t\t\t\"{\\n\"\n\t\t\t\t\t\"matrix m_view;\\n\"\n\t\t\t\t\t\"matrix m_projection;\\n\"\n\t\t\t\t\t\"float3 v_eyepos; float v_time;\\n\"\n\t\t\t\t\"};\\n\"\n\t\t\t\t\"cbuffer ftelightdefs : register(b2)\\n\"\n\t\t\t\t\"{\\n\"\n\t\t\t\t\t\"matrix l_cubematrix;\\n\"\n\t\t\t\t\t\"float3 l_lightposition; float padl1;\\n\"\n\t\t\t\t\t\"float3 l_colour; float padl2;\\n\"\n\t\t\t\t\t\"float3 l_lightcolourscale;float l_lightradius;\\n\"\n\t\t\t\t\t\"float4 l_shadowmapproj;\\n\"\n\t\t\t\t\t\"float2 l_shadowmapscale; float2 padl3;\\n\"\n\t\t\t\t\"};\\n\"\n\t\t\t\t;\n\t\t\t*ppData = strdup(defstruct);\n\t\t\t*pBytes = strlen(*ppData);\n\t\t\treturn S_OK;\n\t\t}\n\t\tif (!strcmp(pFileName, \"sys/skeletal.h\"))\n\t\t{\n\t\t\tstatic const char *defstruct =\n\t\t\t\t\"\"\n\t\t\t\t;\n\t\t\t*ppData = strdup(defstruct);\n\t\t\t*pBytes = strlen(*ppData);\n\t\t\treturn S_OK;\n\t\t}\n\t\t//fog\n\t}\n\telse\n\t{\n\t\t\n\t}\n\treturn E_FAIL;\n}\nID3DIncludeVtbl myd3dincludetab =\n{\n\td3dinclude_Open,\n\td3dinclude_Close\n};\nID3DInclude myd3dinclude =\n{\n\t&myd3dincludetab\n};\n\nvoid D3D11Shader_DeleteProg(program_t *prog)\n{\n\tID3D11InputLayout *layout;\n\tID3D11PixelShader *frag;\n\tID3D11VertexShader *vert;\n\tunsigned int permu, l;\n\tstruct programpermu_s *pp;\n\tfor (permu = countof(prog->permu); permu-- > 0; )\n\t{\n\t\tpp = prog->permu[permu];\n\t\tif (!pp)\n\t\t\tcontinue;\n\t\tprog->permu[permu] = NULL;\n\t\tif (pp == prog->permu[0] && permu)\n\t\t\tcontinue;\t//entry 0 (only) can get copied to avoid constant recompile failures (0 is always precompiled)\n\n\t\tvert = pp->h.hlsl.vert;\n\t\tfrag = pp->h.hlsl.frag;\n\t\tif (vert)\n\t\t\tID3D11VertexShader_Release(vert);\n\t\tif (frag)\n\t\t\tID3D11PixelShader_Release(frag);\n\t\tfor (l = 0; l < countof(pp->h.hlsl.layouts); l++)\n\t\t{\n\t\t\tlayout = pp->h.hlsl.layouts[l];\n\t\t\tif (layout)\n\t\t\t\tID3D11InputLayout_Release(layout);\n\t\t}\n\t\tZ_Free(pp);\n\t}\n}\n\n//create a program from two blobs.\nstatic qboolean D3D11Shader_CreateShaders(program_t *prog, const char *name, struct programpermu_s *permu,\n\t\t\t\t\t\t\t\t\t\t  void *vblob, size_t vsize,\n\t\t\t\t\t\t\t\t\t\t  void *hblob, size_t hsize,\n\t\t\t\t\t\t\t\t\t\t  void *dblob, size_t dsize,\n\t\t\t\t\t\t\t\t\t\t  void *gblob, size_t gsize,\n\t\t\t\t\t\t\t\t\t\t  void *fblob, size_t fsize)\n{\n\tint l;\n\tqboolean success = true;\n\n\tif (FAILED(ID3D11Device_CreateVertexShader(pD3DDev11, vblob, vsize, NULL, (ID3D11VertexShader**)&permu->h.hlsl.vert)))\n\t\tsuccess = false;\n\n\tif (hblob || dblob)\n\t{\n\t\tpermu->h.hlsl.topology = D3D11_PRIMITIVE_TOPOLOGY_3_CONTROL_POINT_PATCHLIST;\n\t\tif (FAILED(ID3D11Device_CreateHullShader(pD3DDev11, hblob, hsize, NULL, (ID3D11HullShader**)&permu->h.hlsl.hull)))\n\t\t\tsuccess = false;\n\n\t\tif (FAILED(ID3D11Device_CreateDomainShader(pD3DDev11, dblob, dsize, NULL, (ID3D11DomainShader**)&permu->h.hlsl.domain)))\n\t\t\tsuccess = false;\n\t}\n\telse\n\t\tpermu->h.hlsl.topology = D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST;\n\n\tif (gblob && FAILED(ID3D11Device_CreateGeometryShader(pD3DDev11, gblob, gsize, NULL, (ID3D11GeometryShader**)&permu->h.hlsl.geom)))\n\t\tsuccess = false;\n\n\tif (FAILED(ID3D11Device_CreatePixelShader(pD3DDev11, fblob, fsize, NULL, (ID3D11PixelShader**)&permu->h.hlsl.frag)))\n\t\tsuccess = false;\n\n\tfor (l = 0; l < 2 && success; l++)\n\t{\n\t\tD3D11_INPUT_ELEMENT_DESC decl[13];\n\t\tint elements = 0;\n\n\t\tdecl[elements].SemanticName = \"POSITION\";\n\t\tdecl[elements].SemanticIndex = 0;\n\t\tdecl[elements].Format = DXGI_FORMAT_R32G32B32_FLOAT;\n\t\tdecl[elements].InputSlot = D3D11_BUFF_POS;\n\t\tdecl[elements].AlignedByteOffset = 0;\n\t\tdecl[elements].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;\n\t\tdecl[elements].InstanceDataStepRate = 0;\n\t\telements++;\n\n\t\tdecl[elements].SemanticName = \"TEXCOORD\";\n\t\tdecl[elements].SemanticIndex = 0;\n\t\tdecl[elements].Format = DXGI_FORMAT_R32G32_FLOAT;\n\t\tdecl[elements].InputSlot = D3D11_BUFF_TC;\n\t\tdecl[elements].AlignedByteOffset = 0;\n\t\tdecl[elements].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;\n\t\tdecl[elements].InstanceDataStepRate = 0;\n\t\telements++;\n\n\t\tdecl[elements].SemanticName = \"TEXCOORD\";\n\t\tdecl[elements].SemanticIndex = 1;\n\t\tdecl[elements].Format = DXGI_FORMAT_R32G32_FLOAT;\n\t\tdecl[elements].InputSlot = D3D11_BUFF_LMTC;\n\t\tdecl[elements].AlignedByteOffset = 0;\n\t\tdecl[elements].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;\n\t\tdecl[elements].InstanceDataStepRate = 0;\n\t\telements++;\n\n\t\tdecl[elements].SemanticName = \"COLOR\";\n\t\tdecl[elements].SemanticIndex = 0;\n\t\tdecl[elements].Format = l?DXGI_FORMAT_R32G32B32A32_FLOAT:DXGI_FORMAT_R8G8B8A8_UNORM;\n\t\tdecl[elements].InputSlot = D3D11_BUFF_COL;\n\t\tdecl[elements].AlignedByteOffset = 0;\n\t\tdecl[elements].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;\n\t\tdecl[elements].InstanceDataStepRate = 0;\n\t\telements++;\n\n\t\tdecl[elements].SemanticName = \"NORMAL\";\n\t\tdecl[elements].SemanticIndex = 0;\n\t\tdecl[elements].Format = DXGI_FORMAT_R32G32B32_FLOAT;\n\t\tdecl[elements].InputSlot = D3D11_BUFF_NORM;\n\t\tdecl[elements].AlignedByteOffset = sizeof(vec3_t)*0;\n\t\tdecl[elements].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;\n\t\tdecl[elements].InstanceDataStepRate = 0;\n\t\telements++;\n\n\t\tdecl[elements].SemanticName = \"TANGENT\";\n\t\tdecl[elements].SemanticIndex = 0;\n\t\tdecl[elements].Format = DXGI_FORMAT_R32G32B32_FLOAT;\n\t\tdecl[elements].InputSlot = D3D11_BUFF_NORM;\n\t\tdecl[elements].AlignedByteOffset = sizeof(vec3_t)*1;\n\t\tdecl[elements].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;\n\t\tdecl[elements].InstanceDataStepRate = 0;\n\t\telements++;\n\n\t\tdecl[elements].SemanticName = \"BINORMAL\";\n\t\tdecl[elements].SemanticIndex = 0;\n\t\tdecl[elements].Format = DXGI_FORMAT_R32G32B32_FLOAT;\n\t\tdecl[elements].InputSlot = D3D11_BUFF_NORM;\n\t\tdecl[elements].AlignedByteOffset = sizeof(vec3_t)*2;\n\t\tdecl[elements].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;\n\t\tdecl[elements].InstanceDataStepRate = 0;\n\t\telements++;\n\n/*\n\t\tdecl[elements].SemanticName = \"BLENDWEIGHT\";\n\t\tdecl[elements].SemanticIndex = 0;\n\t\tdecl[elements].Format = DXGI_FORMAT_R32G32B32A32_FLOAT;\n\t\tdecl[elements].InputSlot = D3D11_BUFF_SKEL;\n\t\tdecl[elements].AlignedByteOffset = 0;\n\t\tdecl[elements].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;\n\t\tdecl[elements].InstanceDataStepRate = 0;\n\t\telements++;\n\n\t\tdecl[elements].SemanticName = \"BLENDINDICIES\";\n\t\tdecl[elements].SemanticIndex = 0;\n\t\tdecl[elements].Format = DXGI_FORMAT_R8G8B8A8_UINT;\n\t\tdecl[elements].InputSlot = D3D11_BUFF_SKEL;\n\t\tdecl[elements].AlignedByteOffset = 0;\n\t\tdecl[elements].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;\n\t\tdecl[elements].InstanceDataStepRate = 0;\n\t\telements++;\n*/\n\t\tif (FAILED(ID3D11Device_CreateInputLayout(pD3DDev11, decl, elements, vblob, vsize, (ID3D11InputLayout**)&permu->h.hlsl.layouts[l])))\n\t\t{\n\t\t\tCon_Printf(\"HLSL Shader %s requires unsupported inputs\\n\", name);\n\t\t\tsuccess = false;\n\t\t}\n\t}\n\treturn success;\n}\n\nstatic qboolean D3D11Shader_LoadBlob(program_t *prog, unsigned int permu, vfsfile_t *blobfile)\n{\n\tqboolean success;\n\tchar *vblob, *hblob, *dblob, *gblob, *fblob;\n\tunsigned int vsz, hsz, dsz, gsz, fsz;\n\n\tVFS_READ(blobfile, &vsz, sizeof(vsz));\n\tvblob = Z_Malloc(vsz);\n\tVFS_READ(blobfile, vblob, vsz);\n\n\tVFS_READ(blobfile, &hsz, sizeof(hsz));\n\tif (hsz != ~0u)\n\t{\n\t\thblob = Z_Malloc(hsz);\n\t\tVFS_READ(blobfile, hblob, hsz);\n\t}\n\telse\n\t\thblob = NULL;\n\n\tVFS_READ(blobfile, &dsz, sizeof(dsz));\n\tif (dsz != ~0u)\n\t{\n\t\tdblob = Z_Malloc(dsz);\n\t\tVFS_READ(blobfile, dblob, dsz);\n\t}\n\telse\n\t\tdblob = NULL;\n\n\tVFS_READ(blobfile, &gsz, sizeof(gsz));\n\tif (dsz != ~0u)\n\t{\n\t\tgblob = Z_Malloc(gsz);\n\t\tVFS_READ(blobfile, gblob, gsz);\n\t}\n\telse\n\t\tgblob = NULL;\n\n\tVFS_READ(blobfile, &fsz, sizeof(fsz));\n\tfblob = Z_Malloc(fsz);\n\tVFS_READ(blobfile, fblob, fsz);\n\n\n\tsuccess = D3D11Shader_CreateShaders(prog, prog->name, prog->permu[permu], vblob, vsz, hblob, hsz, dblob, dsz, gblob, gsz, fblob, fsz);\n\tZ_Free(vblob);\n\tZ_Free(hblob);\n\tZ_Free(dblob);\n\tZ_Free(gblob);\n\tZ_Free(fblob);\n\treturn success;\n}\n\nqboolean D3D11Shader_CreateProgram (program_t *prog, struct programpermu_s *permu, int ver, const char **precompilerconstants, const char *vert, const char *hull, const char *domain, const char *geom, const char *frag, qboolean silenterrors, vfsfile_t *blobfile)\n{\n\tchar *vsformat;\n\tchar *hsformat = NULL;\n\tchar *dsformat = NULL;\n\tchar *fsformat;\n\tchar *gsformat = NULL;\n\tchar *tmp;\n\tD3D_SHADER_MACRO defines[64];\n\tID3DBlob *vcode = NULL, *hcode = NULL, *dcode = NULL, *gcode = NULL, *fcode = NULL, *errors = NULL;\n\tqboolean success = false;\n//\tID3D11ShaderReflection *freflect;\n//\tint i;\n\n\tif (d3dfeaturelevel >= D3D_FEATURE_LEVEL_11_0)\t//and 11.1\n\t{\n\t\tvsformat = \"vs_5_0\";\n\t\thsformat = \"hs_5_0\";\n\t\tdsformat = \"ds_5_0\";\n\t\tgsformat = \"gs_5_0\";\n\t\tfsformat = \"ps_5_0\";\n\t}\n\telse if (d3dfeaturelevel >= D3D_FEATURE_LEVEL_10_1)\n\t{\n\t\tvsformat = \"vs_4_1\";\n\t\tgsformat = \"gs_4_1\";\n\t\tfsformat = \"ps_4_1\";\n\t}\n\telse if (d3dfeaturelevel >= D3D_FEATURE_LEVEL_10_0)\n\t{\n\t\tvsformat = \"vs_4_0\";\n\t\tgsformat = \"gs_4_0\";\n\t\tfsformat = \"ps_4_0\";\n\t}\n\telse if (d3dfeaturelevel >= D3D_FEATURE_LEVEL_9_3)\n\t{\t//dx10-compatible output for 9.3 hardware\n\t\tvsformat = \"vs_4_0_level_9_3\";\n\t\tfsformat = \"ps_4_0_level_9_3\";\n\t}\n\telse\n\t{\t//dx10-compatible output for 9.1|9.2 hardware\n\t\tvsformat = \"vs_4_0_level_9_1\";\n\t\tfsformat = \"ps_4_0_level_9_1\";\n\t}\n\n\tpermu->h.hlsl.vert = NULL;\n\tpermu->h.hlsl.frag = NULL;\n\tpermu->h.hlsl.hull = NULL;\n\tpermu->h.hlsl.domain = NULL;\n\tpermu->h.hlsl.geom = NULL;\n\tmemset(permu->h.hlsl.layouts, 0, sizeof(permu->h.hlsl.layouts));\n\n\tif (pD3DCompile)\n\t{\n\t\tint consts;\n\t\tfor (consts = 0; precompilerconstants[consts]; consts++)\n\t\t\t;\n\t\tif (consts+3 >= sizeof(defines) / sizeof(defines[0]))\n\t\t\treturn success;\n\n\t\tconsts = 0;\n\t\tdefines[consts].Name = NULL; /*shader type*/\n\t\tdefines[consts].Definition = \"1\";\n\t\tconsts++;\n\n\t\tdefines[consts].Name = \"ENGINE_\"DISTRIBUTION;\n#ifdef SVNREVISION\n\t\tdefines[consts].Definition = STRINGIFY(SVNREVISION);\n#else\n\t\tdefines[consts].Definition = \"0\";\n#endif\n\t\tconsts++;\n\n\t\ttmp = Z_Malloc(64);\n\t\tQ_snprintfz(tmp, 64, \"0x%x\", d3dfeaturelevel);\n\t\tdefines[consts].Name = Z_StrDup(\"LEVEL\");\n\t\tdefines[consts].Definition = tmp;\n\t\tconsts++;\n\n\t\tfor (; *precompilerconstants; precompilerconstants++)\n\t\t{\n\t\t\tconst char *t, *nl;\n\t\t\tchar *v;\n\t\t\tfor (t = *precompilerconstants; *t; t++)\n\t\t\t{\n\t\t\t\tt = COM_Parse(t);\n\t\t\t\tt = COM_Parse(t);\n\t\t\t\tdefines[consts].Name = Z_StrDup(com_token);\n\t\t\t\twhile (*t == ' ' || *t == '\\t')\n\t\t\t\t\tt++;\n\t\t\t\tnl = strchr(t, '\\n');\n\t\t\t\tif (!nl)\n\t\t\t\t\tnl = t+strlen(t);\n\n\t\t\t\tif (nl && nl != t)\n\t\t\t\t{\n\t\t\t\t\tv = BZ_Malloc(nl-t+1);\n\t\t\t\t\tmemcpy(v, t, nl-t);\n\t\t\t\t\tv[nl-t] = 0;\n\t\t\t\t\tdefines[consts].Definition = v;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tdefines[consts].Definition = t?Z_StrDup(t):NULL;\n\t\t\t\tconsts++;\n\t\t\t\tt = nl;\n\t\t\t}\n\t\t}\n\n\t\tdefines[consts].Name = NULL;\n\t\tdefines[consts].Definition = NULL;\n\n\t\tsuccess = true;\n\n\t\tdefines[0].Name = \"VERTEX_SHADER\";\n\t\tif (FAILED(pD3DCompile(vert, strlen(vert), prog->name, defines, &myd3dinclude, \"main\", vsformat, 0, 0, &vcode, &errors)))\n\t\t\tsuccess = false;\n\t\tif (errors && !silenterrors)\n\t\t{\n\t\t\tchar *messages = ID3DBlob_GetBufferPointer(errors);\n\t\t\tCon_Printf(\"vertex shader %s:\\n%s\", prog->name, messages);\n\t\t\tID3DBlob_Release(errors);\n\t\t}\n\n\t\tif (hull)\n\t\t{\n\t\t\tif (!hsformat)\n\t\t\t\tsuccess = false;\n\t\t\telse\n\t\t\t{\n\t\t\t\tdefines[0].Name = \"HULL_SHADER\";\n\t\t\t\tif (FAILED(pD3DCompile(hull, strlen(hull), prog->name, defines, &myd3dinclude, \"main\", hsformat, 0, 0, &hcode, &errors)))\n\t\t\t\t\tsuccess = false;\n\t\t\t\tif (errors && !silenterrors)\n\t\t\t\t{\n\t\t\t\t\tchar *messages = ID3DBlob_GetBufferPointer(errors);\n\t\t\t\t\tCon_Printf(\"hull shader %s:\\n%s\", prog->name, messages);\n\t\t\t\t\tID3DBlob_Release(errors);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (domain)\n\t\t{\n\t\t\tif (!dsformat)\n\t\t\t\tsuccess = false;\n\t\t\telse\n\t\t\t{\n\t\t\t\tdefines[0].Name = \"DOMAIN_SHADER\";\n\t\t\t\tif (FAILED(pD3DCompile(domain, strlen(domain), prog->name, defines, &myd3dinclude, \"main\", dsformat, 0, 0, &dcode, &errors)))\n\t\t\t\t\tsuccess = false;\n\t\t\t\tif (errors && !silenterrors)\n\t\t\t\t{\n\t\t\t\t\tchar *messages = ID3DBlob_GetBufferPointer(errors);\n\t\t\t\t\tCon_Printf(\"domain shader %s:\\n%s\", prog->name, messages);\n\t\t\t\t\tID3DBlob_Release(errors);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (geom)\n\t\t{\n\t\t\tif (!dsformat)\n\t\t\t\tsuccess = false;\n\t\t\telse\n\t\t\t{\n\t\t\t\tdefines[0].Name = \"GEOMETRY_SHADER\";\n\t\t\t\tif (FAILED(pD3DCompile(domain, strlen(domain), prog->name, defines, &myd3dinclude, \"main\", gsformat, 0, 0, &gcode, &errors)))\n\t\t\t\t\tsuccess = false;\n\t\t\t\tif (errors && !silenterrors)\n\t\t\t\t{\n\t\t\t\t\tchar *messages = ID3DBlob_GetBufferPointer(errors);\n\t\t\t\t\tCon_Printf(\"geometry shader %s:\\n%s\", prog->name, messages);\n\t\t\t\t\tID3DBlob_Release(errors);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tdefines[0].Name = \"FRAGMENT_SHADER\";\n\t\tif (FAILED(pD3DCompile(frag, strlen(frag), prog->name, defines, &myd3dinclude, \"main\", fsformat, 0, 0, &fcode, &errors)))\n\t\t\tsuccess = false;\n\t\tif (errors && !silenterrors)\n\t\t{\n\t\t\tchar *messages = ID3DBlob_GetBufferPointer(errors);\n\t\t\tCon_Printf(\"fragment shader %s:\\n%s\", prog->name, messages);\n\t\t\tID3DBlob_Release(errors);\n\t\t}\n\n\t\twhile(consts-->2)\n\t\t{\n\t\t\tZ_Free((void*)defines[consts].Name);\n\t\t\tZ_Free((void*)defines[consts].Definition);\n\t\t}\n\n\t\tif (success)\n\t\t\tsuccess = D3D11Shader_CreateShaders(prog, prog->name, permu,\n\t\t\t\tID3DBlob_GetBufferPointer(vcode), ID3DBlob_GetBufferSize(vcode),\n\t\t\t\thcode?ID3DBlob_GetBufferPointer(hcode):NULL, hcode?ID3DBlob_GetBufferSize(hcode):0,\n\t\t\t\tdcode?ID3DBlob_GetBufferPointer(dcode):NULL, dcode?ID3DBlob_GetBufferSize(dcode):0,\n\t\t\t\tgcode?ID3DBlob_GetBufferPointer(gcode):NULL, gcode?ID3DBlob_GetBufferSize(gcode):0,\n\t\t\t\tID3DBlob_GetBufferPointer(fcode), ID3DBlob_GetBufferSize(fcode));\n\n\t\tif (success && blobfile)\n\t\t{\n\t\t\tunsigned int sz;\n\t\t\tsz = ID3DBlob_GetBufferSize(vcode);\n\t\t\tVFS_WRITE(blobfile, &sz, sizeof(sz));\n\t\t\tVFS_WRITE(blobfile, ID3DBlob_GetBufferPointer(vcode), sz);\n\n\t\t\tif (!hcode)\n\t\t\t{\n\t\t\t\tsz = ~0u;\n\t\t\t\tVFS_WRITE(blobfile, &sz, sizeof(sz));\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tsz = ID3DBlob_GetBufferSize(hcode);\n\t\t\t\tVFS_WRITE(blobfile, &sz, sizeof(sz));\n\t\t\t\tVFS_WRITE(blobfile, ID3DBlob_GetBufferPointer(hcode), sz);\n\t\t\t}\n\n\t\t\tif (!dcode)\n\t\t\t{\n\t\t\t\tsz = ~0u;\n\t\t\t\tVFS_WRITE(blobfile, &sz, sizeof(sz));\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tsz = ID3DBlob_GetBufferSize(dcode);\n\t\t\t\tVFS_WRITE(blobfile, &sz, sizeof(sz));\n\t\t\t\tVFS_WRITE(blobfile, ID3DBlob_GetBufferPointer(dcode), sz);\n\t\t\t}\n\n\t\t\tif (!gcode)\n\t\t\t{\n\t\t\t\tsz = ~0u;\n\t\t\t\tVFS_WRITE(blobfile, &sz, sizeof(sz));\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tsz = ID3DBlob_GetBufferSize(gcode);\n\t\t\t\tVFS_WRITE(blobfile, &sz, sizeof(sz));\n\t\t\t\tVFS_WRITE(blobfile, ID3DBlob_GetBufferPointer(gcode), sz);\n\t\t\t}\n\n\t\t\tsz = ID3DBlob_GetBufferSize(fcode);\n\t\t\tVFS_WRITE(blobfile, &sz, sizeof(sz));\n\t\t\tVFS_WRITE(blobfile, ID3DBlob_GetBufferPointer(fcode), sz);\n\t\t}\n\n\n/*\t\tif (fcode)\n\t\t{\n\t\t\tpD3DReflect(ID3DBlob_GetBufferPointer(fcode), ID3DBlob_GetBufferSize(fcode), &IID_ID3D11ShaderReflection, (void**)&freflect);\n\t\t\tif (freflect)\n\t\t\t{\n\t\t\t\tint tmu;\n\t\t\t\tD3D11_SHADER_INPUT_BIND_DESC bdesc = {0};\n\t\t\t\tfor (i = prog->numsamplers; i < 16; i++)\n\t\t\t\t{\n\t\t\t\t\tif (SUCCEEDED(freflect->lpVtbl->GetResourceBindingDescByName(freflect, va(\"t_t%i\", i), &bdesc)))\n\t\t\t\t\t\tprog->numsamplers = i+1;\n\t\t\t\t}\n\n\t\t\t\ttmu = prog->numsamplers;\n\t\t\t\tfor (i = 0; sh_defaultsamplers[i]; i++)\n\t\t\t\t{\n//\t\t\t\t\tif (prog->defaulttextures & (1u<<i))\n//\t\t\t\t\t\tcontinue;\n\t\t\t\t\tif (SUCCEEDED(freflect->lpVtbl->GetResourceBindingDescByName(freflect, va(\"t%s\", sh_defaultsamplers[i]+1), &bdesc)))\n\t\t\t\t\t\tprog->defaulttextures |= (1u<<i);\n\t\t\t\t\tif (!(prog->defaulttextures & (1u<<i)))\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\ttmu++;\n\t\t\t\t}\n\t\t\t\tID3D11ShaderReflection_Release(freflect);\n\t\t\t}\n\t\t\telse\n\t\t\t\tCon_Printf(\"%s: D3DReflect failed, unable to get reflection info\\n\", name);\n\t\t}\n*/\n\t\tif (vcode)\n\t\t\tID3DBlob_Release(vcode);\n\t\tif (hcode)\n\t\t\tID3DBlob_Release(hcode);\n\t\tif (dcode)\n\t\t\tID3DBlob_Release(dcode);\n\t\tif (gcode)\n\t\t\tID3DBlob_Release(gcode);\n\t\tif (fcode)\n\t\t\tID3DBlob_Release(fcode);\n\t}\n\treturn success;\n}\n\nqboolean D3D11Shader_Init(unsigned int flevel)\n{\n\t//FIXME: if the feature level is below 10, make sure the compiler supports all the right targets etc\n\tint ver;\n\tdllfunction_t funcsold[] =\n\t{\n\t\t{(void**)&pD3DCompile, \"D3DCompileFromMemory\"},\n\t\t{NULL,NULL}\n\t};\n\tdllfunction_t funcsnew[] =\n\t{\n\t\t{(void**)&pD3DCompile, \"D3DCompile\"},\n\t\t{NULL,NULL}\n\t};\n\n\t//FIXME: wine's d3dcompiler dlls don't work properly right now, and winetricks installs ms' only up to 43 (which works, but we loaded 47 instead)\n\tfor (ver = 47; ver >= 33; ver--)\n\t{\n\t\tshaderlib = Sys_LoadLibrary(va(\"D3dcompiler_%i.dll\", ver), (ver>=40)?funcsnew:funcsold);\n\t\tif (shaderlib)\n\t\t\tbreak;\n\t}\n\n\tif (!shaderlib)\n\t{\n\t\t//no shader library available. at least make sure that there's a 2d blob that we can use.\n\t\tif (!COM_FCheckExists(\"hlsl11/default2d.blob\"))\n\t\t\treturn false;\n\t}\n\n\tsh_config.minver = 11;\n\tsh_config.maxver = 11;\n\tsh_config.blobpath = \"hlsl11/%s.blob\";\n\tsh_config.progpath = shaderlib?\"hlsl11/%s.hlsl\":NULL;\n\tsh_config.shadernamefmt = \"%s_hlsl11\";\n\n\tsh_config.progs_supported\t= true;\n\tsh_config.progs_required\t= true;\n\n\tsh_config.pDeleteProg\t\t= D3D11Shader_DeleteProg;\n\tsh_config.pLoadBlob\t\t\t= D3D11Shader_LoadBlob;\n\tsh_config.pCreateProgram\t= D3D11Shader_CreateProgram;\n\tsh_config.pProgAutoFields\t= NULL;\n\n\tsh_config.can_mipcap\t\t= true;\t//at creation time\n\tsh_config.havecubemaps\t\t= true;\n\n//\tsh_config.tex_env_combine\t\t= 1;\n//\tsh_config.nv_tex_env_combine4\t= 1;\n//\tsh_config.env_add\t\t\t\t= 1;\n\n\td3dfeaturelevel = flevel;\n\treturn true;\n}\n\n#endif\n\n"
  },
  {
    "path": "engine/d3d/d3d8_backend.c",
    "content": "#include \"quakedef.h\"\r\n#include \"glquake.h\"\r\n#include \"gl_draw.h\"\r\n#ifdef D3D8QUAKE\r\n//#define FIXME //for Eukara to fix, if he ever wants to get the d3d8 renderer working fully.\r\n#include \"shader.h\"\r\n#if !defined(HMONITOR_DECLARED) && (WINVER < 0x0500)\r\n    #define HMONITOR_DECLARED\r\n    DECLARE_HANDLE(HMONITOR);\r\n#endif\r\n\r\n#ifdef _XBOX\r\n\t#include <xtl.h>\r\n\t#define D3DLOCK_DISCARD 0\r\n#endif\r\n\r\n#include <d3d8.h>\r\n\r\n/*\r\nd3d8 supports no per-buffer offsets, and the fixed function stuff only supports a single vbo anyway.\r\nto work around this, anything with any mods that can't be expressed by the vbo will result in everything being pulled over into a temporary buffer\r\n*/\r\n\r\n\r\n\r\n//#define FORCESTATE\r\n\r\n#ifdef FORCESTATE\r\n#pragma warningmsg(\"D3D8 FORCESTATE is active\")\r\n#endif\r\n\r\n\r\nextern LPDIRECT3DDEVICE8 pD3DDev8;\r\n\r\n//#define d3dcheck(foo) foo\r\n#ifdef _DEBUG\r\n#define d3dcheck(foo) do{HRESULT err = foo; if (FAILED(err)) Sys_Error(\"D3D reported error on backend line %i - error 0x%x\\n\", __LINE__, (unsigned int)err);} while(0)\r\n#else\r\n#define d3dcheck(foo) foo\r\n#endif\r\n\r\n#define MAX_TMUS 1\r\n\r\n#define MAX_TC_TMUS 1\r\n\r\nextern float d3d_trueprojection[16];\r\n\r\nstatic void BE_RotateForEntity (const entity_t *e, const model_t *mod);\r\n\r\n/*========================================== tables for deforms =====================================*/\r\n#define R_FastSin(x) sin((x)*(2*M_PI))\r\n#define frand() (rand()*(1.0/RAND_MAX))\r\n#define FTABLE_SIZE\t\t1024\r\n#define FTABLE_CLAMP(x)\t(((int)((x)*FTABLE_SIZE) & (FTABLE_SIZE-1)))\r\n#define FTABLE_EVALUATE(table,x) (table ? table[FTABLE_CLAMP(x)] : frand()*((x)-floor(x)))\r\n\r\nstatic\tfloat\tr_sintable[FTABLE_SIZE];\r\nstatic\tfloat\tr_triangletable[FTABLE_SIZE];\r\nstatic\tfloat\tr_squaretable[FTABLE_SIZE];\r\nstatic\tfloat\tr_sawtoothtable[FTABLE_SIZE];\r\nstatic\tfloat\tr_inversesawtoothtable[FTABLE_SIZE];\r\n\r\n#if FIXME\r\nstatic float *FTableForFunc ( unsigned int func )\r\n{\r\n\tswitch (func)\r\n\t{\r\n\t\tcase SHADER_FUNC_SIN:\r\n\t\t\treturn r_sintable;\r\n\r\n\t\tcase SHADER_FUNC_TRIANGLE:\r\n\t\t\treturn r_triangletable;\r\n\r\n\t\tcase SHADER_FUNC_SQUARE:\r\n\t\t\treturn r_squaretable;\r\n\r\n\t\tcase SHADER_FUNC_SAWTOOTH:\r\n\t\t\treturn r_sawtoothtable;\r\n\r\n\t\tcase SHADER_FUNC_INVERSESAWTOOTH:\r\n\t\t\treturn r_inversesawtoothtable;\r\n\t}\r\n\r\n\t//bad values allow us to crash (so I can debug em)\r\n\treturn NULL;\r\n}\r\n#endif\r\n\r\nstatic void FTable_Init(void)\r\n{\r\n\tunsigned int i;\r\n\tdouble t;\r\n\tfor (i = 0; i < FTABLE_SIZE; i++)\r\n\t{\r\n\t\tt = (double)i / (double)FTABLE_SIZE;\r\n\r\n\t\tr_sintable[i] = sin(t * 2*M_PI);\r\n\r\n\t\tif (t < 0.25)\r\n\t\t\tr_triangletable[i] = t * 4.0;\r\n\t\telse if (t < 0.75)\r\n\t\t\tr_triangletable[i] = 2 - 4.0 * t;\r\n\t\telse\r\n\t\t\tr_triangletable[i] = (t - 0.75) * 4.0 - 1.0;\r\n\r\n\t\tif (t < 0.5)\r\n\t\t\tr_squaretable[i] = 1.0f;\r\n\t\telse\r\n\t\t\tr_squaretable[i] = -1.0f;\r\n\r\n\t\tr_sawtoothtable[i] = t;\r\n\t\tr_inversesawtoothtable[i] = 1.0 - t;\r\n\t}\r\n}\r\n\r\n#if FIXME\r\ntypedef vec3_t mat3_t[3];\r\nstatic mat3_t axisDefault={{1, 0, 0},\r\n\t\t\t\t\t{0, 1, 0},\r\n\t\t\t\t\t{0, 0, 1}};\r\n\r\nstatic void Matrix3_Transpose (mat3_t in, mat3_t out)\r\n{\r\n\tout[0][0] = in[0][0];\r\n\tout[1][1] = in[1][1];\r\n\tout[2][2] = in[2][2];\r\n\r\n\tout[0][1] = in[1][0];\r\n\tout[0][2] = in[2][0];\r\n\tout[1][0] = in[0][1];\r\n\tout[1][2] = in[2][1];\r\n\tout[2][0] = in[0][2];\r\n\tout[2][1] = in[1][2];\r\n}\r\nstatic void Matrix3_Multiply_Vec3 (const mat3_t a, const vec3_t b, vec3_t product)\r\n{\r\n\tproduct[0] = a[0][0]*b[0] + a[0][1]*b[1] + a[0][2]*b[2];\r\n\tproduct[1] = a[1][0]*b[0] + a[1][1]*b[1] + a[1][2]*b[2];\r\n\tproduct[2] = a[2][0]*b[0] + a[2][1]*b[1] + a[2][2]*b[2];\r\n}\r\n\r\nstatic int Matrix3_Compare(const mat3_t in, const mat3_t out)\r\n{\r\n\treturn !memcmp(in, out, sizeof(mat3_t));\r\n}\r\n#endif\r\n\r\n/*================================================*/\r\n\r\ntypedef struct\r\n{\r\n\tbackendmode_t mode;\r\n\tunsigned int flags;\r\n\r\n\tfloat\t\tcurtime;\r\n\tconst entity_t\t*curentity;\r\n\tconst dlight_t\t*curdlight;\r\n\tbatch_t\t\t*curbatch, dummybatch;\r\n\tvec3_t\t\tcurdlight_colours;\r\n\tshader_t\t*curshader;\r\n\ttexnums_t\t*curtexnums;\r\n\ttexid_t\t\tcurlightmap;\r\n\ttexid_t\t\tcurdeluxmap;\r\n\tunsigned int shaderbits;\r\n\tunsigned int curcull;\r\n\tfloat depthbias;\r\n\tfloat depthfactor;\r\n\tfloat m_model[16];\r\n\tunsigned int lastpasscount;\r\n\tvbo_t *batchvbo;\r\n\r\n\tshader_t\t*shader_rtlight;\r\n\tIDirect3DTexture8\t*curtex[MAX_TMUS];\r\n\tunsigned int curtexflags[MAX_TMUS];\r\n\tunsigned int tmuflags[MAX_TMUS];\r\n\r\n\tmesh_t\t\t**meshlist;\r\n\tunsigned int nummeshes;\r\n\r\n\tD3DCOLOR\tpasscolour;\r\n\tqboolean\tpasssinglecolour;\r\n\r\n\t/*FIXME: we shouldn't lock these so much - we need to cache which batches have been submitted and set up streams separately from the vertex data*/\r\n\tIDirect3DVertexBuffer8 *dynvbo_buff;\r\n\tunsigned int dynvbo_offs;\r\n\tunsigned int dynvbo_size;\r\n\r\n\tIDirect3DIndexBuffer8 *dynidx_buff;\r\n\tunsigned int dynidx_offs;\r\n\tunsigned int dynidx_size;\r\n\r\n\tunsigned int wbatch;\r\n\tunsigned int maxwbatches;\r\n\tbatch_t *wbatches;\r\n\r\n\tint mipfilter[3];\r\n\tint picfilter[3];\r\n} d3d8backend_t;\r\n\r\ntypedef struct\r\n{\r\n\tvec3_t coord;\r\n\tbyte_vec4_t colorsb;\r\n\tvec2_t tc[2];\r\n} vbovdata_t;\r\n#define D3DFVF_QVBO (D3DFVF_XYZ|D3DFVF_DIFFUSE|D3DFVF_TEX2)\r\n\r\n#define DYNVBUFFSIZE 65536\r\n#define DYNIBUFFSIZE 65536\r\n\r\nstatic d3d8backend_t shaderstate;\r\n\r\nextern int be_maxpasses;\r\n\r\nstatic void BE_ApplyTMUState(unsigned int tu, unsigned int flags)\r\n{\r\n\tunsigned int delta = shaderstate.tmuflags[tu] ^ flags;\r\n\tif (!delta)\r\n\t\treturn;\r\n\tif (delta & SHADER_PASS_CLAMP)\r\n\t{\r\n\t\tif (flags & SHADER_PASS_CLAMP)\r\n\t\t{\r\n\t\t\tIDirect3DDevice8_SetTextureStageState(pD3DDev8, tu, D3DTSS_ADDRESSU, D3DTADDRESS_CLAMP);\r\n\t\t\tIDirect3DDevice8_SetTextureStageState(pD3DDev8, tu, D3DTSS_ADDRESSV, D3DTADDRESS_CLAMP);\r\n\t\t\tIDirect3DDevice8_SetTextureStageState(pD3DDev8, tu, D3DTSS_ADDRESSW, D3DTADDRESS_CLAMP);\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tIDirect3DDevice8_SetTextureStageState(pD3DDev8, tu, D3DTSS_ADDRESSU, D3DTADDRESS_WRAP);\r\n\t\t\tIDirect3DDevice8_SetTextureStageState(pD3DDev8, tu, D3DTSS_ADDRESSV, D3DTADDRESS_WRAP);\r\n\t\t\tIDirect3DDevice8_SetTextureStageState(pD3DDev8, tu, D3DTSS_ADDRESSW, D3DTADDRESS_WRAP);\r\n\t\t}\r\n\t}\r\n\tif (delta & (SHADER_PASS_NOMIPMAP|SHADER_PASS_NEAREST|SHADER_PASS_LINEAR|SHADER_PASS_UIPIC))\r\n\t{\r\n\t\tint min, mip, mag;\r\n\t\tint *filter = (flags & SHADER_PASS_UIPIC)?shaderstate.picfilter:shaderstate.mipfilter;\r\n\r\n\t\tif ((filter[2] && !(flags & SHADER_PASS_NEAREST)) || (flags & SHADER_PASS_LINEAR))\r\n\t\t\tmag = D3DTEXF_ANISOTROPIC;//D3DTEXF_LINEAR;\r\n\t\telse\r\n\t\t\tmag = D3DTEXF_POINT;\r\n\t\tif (filter[1] == -1 || (flags & IF_NOMIPMAP))\r\n\t\t\tmip = D3DTEXF_NONE;\r\n\t\telse if ((filter[1] && !(flags & SHADER_PASS_NEAREST)) || (flags & SHADER_PASS_LINEAR))\r\n\t\t\tmip = D3DTEXF_ANISOTROPIC;//D3DTEXF_LINEAR;\r\n\t\telse\r\n\t\t\tmip = D3DTEXF_POINT;\r\n\t\tif ((filter[0] && !(flags & SHADER_PASS_NEAREST)) || (flags & SHADER_PASS_LINEAR))\r\n\t\t\tmin = D3DTEXF_ANISOTROPIC;//D3DTEXF_LINEAR;\r\n\t\telse\r\n\t\t\tmin = D3DTEXF_POINT;\r\n\r\n\t\tIDirect3DDevice8_SetTextureStageState(pD3DDev8, tu, D3DTSS_MINFILTER, min);\r\n\t\tIDirect3DDevice8_SetTextureStageState(pD3DDev8, tu, D3DTSS_MIPFILTER, mip);\r\n\t\tIDirect3DDevice8_SetTextureStageState(pD3DDev8, tu, D3DTSS_MAGFILTER, mag);\r\n\t}\r\n\tshaderstate.tmuflags[tu] = flags;\r\n}\r\n\r\n//d3d8 is all sampler state\r\nvoid D3D8_UpdateFiltering(image_t *imagelist, int filtermip[3], int filterpic[3], int mipcap[2], float lodbias, float anis)\r\n{\r\n\tint i;\r\n\tmemcpy(shaderstate.mipfilter, filtermip, sizeof(shaderstate.mipfilter));\r\n\tmemcpy(shaderstate.picfilter, filterpic, sizeof(shaderstate.picfilter));\r\n\r\n\tfor (i = 0; i < MAX_TMUS; i++)\r\n\t{\r\n\t\tshaderstate.tmuflags[i] = ~shaderstate.tmuflags[i];\r\n\t\tBE_ApplyTMUState(i, ~shaderstate.tmuflags[i]);\r\n\r\n\t\tIDirect3DDevice8_SetTextureStageState(pD3DDev8, i, D3DTSS_MAXANISOTROPY, anis);\r\n\t}\r\n}\r\n\r\nstatic void BE_ApplyShaderBits(unsigned int bits)\r\n{\r\n\tunsigned int delta;\r\n\r\n\tif (shaderstate.flags & (BEF_FORCEADDITIVE|BEF_FORCETRANSPARENT|BEF_FORCENODEPTH|BEF_FORCEDEPTHTEST|BEF_FORCEDEPTHWRITE))\r\n\t{\r\n\t\tif (shaderstate.flags & BEF_FORCEADDITIVE)\r\n\t\t\tbits = (bits & ~(SBITS_MISC_DEPTHWRITE|SBITS_BLEND_BITS|SBITS_ATEST_BITS))\r\n\t\t\t\t\t\t| (SBITS_SRCBLEND_SRC_ALPHA | SBITS_DSTBLEND_ONE);\r\n\t\telse if (shaderstate.flags & BEF_FORCETRANSPARENT)\r\n\t\t{\r\n\t\t\tif ((bits & SBITS_BLEND_BITS) == (SBITS_SRCBLEND_ONE|SBITS_DSTBLEND_ZERO) || !(bits & SBITS_BLEND_BITS)) \t/*if transparency is forced, clear alpha test bits*/\r\n\t\t\t\tbits = (bits & ~(SBITS_MISC_DEPTHWRITE|SBITS_BLEND_BITS|SBITS_ATEST_BITS))\r\n\t\t\t\t\t\t\t| (SBITS_SRCBLEND_SRC_ALPHA | SBITS_DSTBLEND_ONE_MINUS_SRC_ALPHA);\r\n\t\t}\r\n\r\n\t\tif (shaderstate.flags & BEF_FORCENODEPTH) \t/*EF_NODEPTHTEST dp extension*/\r\n\t\t\tbits |= SBITS_MISC_NODEPTHTEST;\r\n\t\telse\r\n\t\t{\r\n\t\t\tif (shaderstate.flags & BEF_FORCEDEPTHTEST)\r\n\t\t\t\tbits &= ~SBITS_MISC_NODEPTHTEST;\r\n\t\t\tif (shaderstate.flags & BEF_FORCEDEPTHWRITE)\r\n\t\t\t\tbits |= SBITS_MISC_DEPTHWRITE;\r\n\t\t}\r\n\t}\r\n\r\n#ifdef FORCESTATE\r\n\tdelta = ~0;\r\n#else\r\n\tdelta = bits ^ shaderstate.shaderbits;\r\n#endif\r\n\tif (!delta)\r\n\t\treturn;\r\n\tshaderstate.shaderbits = bits;\r\n\r\n\tif (delta & SBITS_BLEND_BITS)\r\n\t{\r\n\t\tif (bits & SBITS_BLEND_BITS)\r\n\t\t{\r\n\t\t\tD3DBLEND src;\r\n\t\t\tD3DBLEND dst;\r\n\r\n\t\t\tswitch(bits & SBITS_SRCBLEND_BITS)\r\n\t\t\t{\r\n\t\t\tcase SBITS_SRCBLEND_ZERO:\t\t\t\t\tsrc = D3DBLEND_ZERO; break;\r\n\t\t\tcase SBITS_SRCBLEND_ONE:\t\t\t\t\tsrc = D3DBLEND_ONE; break;\r\n\t\t\tcase SBITS_SRCBLEND_DST_COLOR:\t\t\t\tsrc = D3DBLEND_DESTCOLOR; break;\r\n\t\t\tcase SBITS_SRCBLEND_ONE_MINUS_DST_COLOR:\tsrc = D3DBLEND_INVDESTCOLOR; break;\r\n\t\t\tcase SBITS_SRCBLEND_SRC_ALPHA:\t\t\t\tsrc = D3DBLEND_SRCALPHA; break;\r\n\t\t\tcase SBITS_SRCBLEND_ONE_MINUS_SRC_ALPHA:\tsrc = D3DBLEND_INVSRCALPHA; break;\r\n\t\t\tcase SBITS_SRCBLEND_DST_ALPHA:\t\t\t\tsrc = D3DBLEND_DESTALPHA; break;\r\n\t\t\tcase SBITS_SRCBLEND_ONE_MINUS_DST_ALPHA:\tsrc = D3DBLEND_INVDESTALPHA; break;\r\n\t\t\tcase SBITS_SRCBLEND_ALPHA_SATURATE:\t\t\tsrc = D3DBLEND_SRCALPHASAT; break;\r\n\t\t\tdefault:\tSys_Error(\"Bad shader blend src\\n\"); return;\r\n\t\t\t}\r\n\t\t\tswitch(bits & SBITS_DSTBLEND_BITS)\r\n\t\t\t{\r\n\t\t\tcase SBITS_DSTBLEND_ZERO:\t\t\t\t\tdst = D3DBLEND_ZERO; break;\r\n\t\t\tcase SBITS_DSTBLEND_ONE:\t\t\t\t\tdst = D3DBLEND_ONE; break;\r\n\t\t\tcase SBITS_DSTBLEND_SRC_ALPHA:\t\t\t\tdst = D3DBLEND_SRCALPHA; break;\r\n\t\t\tcase SBITS_DSTBLEND_ONE_MINUS_SRC_ALPHA:\tdst = D3DBLEND_INVSRCALPHA; break;\r\n\t\t\tcase SBITS_DSTBLEND_DST_ALPHA:\t\t\t\tdst = D3DBLEND_DESTALPHA; break;\r\n\t\t\tcase SBITS_DSTBLEND_ONE_MINUS_DST_ALPHA:\tdst = D3DBLEND_INVDESTALPHA; break;\r\n\t\t\tcase SBITS_DSTBLEND_SRC_COLOR:\t\t\t\tdst = D3DBLEND_SRCCOLOR; break;\r\n\t\t\tcase SBITS_DSTBLEND_ONE_MINUS_SRC_COLOR:\tdst = D3DBLEND_INVSRCCOLOR; break;\r\n\t\t\tdefault:\tSys_Error(\"Bad shader blend dst\\n\"); return;\r\n\t\t\t}\r\n\t\t\tIDirect3DDevice8_SetRenderState(pD3DDev8, D3DRS_ALPHABLENDENABLE, TRUE);\r\n\t\t\tIDirect3DDevice8_SetRenderState(pD3DDev8, D3DRS_SRCBLEND, src);\r\n\t\t\tIDirect3DDevice8_SetRenderState(pD3DDev8, D3DRS_DESTBLEND, dst);\r\n\t\t}\r\n\t\telse\r\n\t\t\tIDirect3DDevice8_SetRenderState(pD3DDev8, D3DRS_ALPHABLENDENABLE, FALSE);\r\n\t}\r\n\r\n\tif (delta & SBITS_ATEST_BITS)\r\n\t{\r\n\t\tswitch(bits & SBITS_ATEST_BITS)\r\n\t\t{\r\n\t\tcase SBITS_ATEST_NONE:\r\n\t\t\tIDirect3DDevice8_SetRenderState(pD3DDev8, D3DRS_ALPHATESTENABLE, FALSE);\r\n\t//\t\tIDirect3DDevice8_SetRenderState(pD3DDev8, D3DRS_ALPHAREF, 0);\r\n\t//\t\tIDirect3DDevice8_SetRenderState(pD3DDev8, D3DRS_ALPHAFUNC, 0);\r\n\t\t\tbreak;\r\n\t\tcase SBITS_ATEST_GT0:\r\n\t\t\tIDirect3DDevice8_SetRenderState(pD3DDev8, D3DRS_ALPHATESTENABLE, TRUE);\r\n\t\t\tIDirect3DDevice8_SetRenderState(pD3DDev8, D3DRS_ALPHAREF, 0);\r\n\t\t\tIDirect3DDevice8_SetRenderState(pD3DDev8, D3DRS_ALPHAFUNC, D3DCMP_GREATER);\r\n\t\t\tbreak;\r\n\t\tcase SBITS_ATEST_LT128:\r\n\t\t\tIDirect3DDevice8_SetRenderState(pD3DDev8, D3DRS_ALPHATESTENABLE, TRUE);\r\n\t\t\tIDirect3DDevice8_SetRenderState(pD3DDev8, D3DRS_ALPHAREF, 128);\r\n\t\t\tIDirect3DDevice8_SetRenderState(pD3DDev8, D3DRS_ALPHAFUNC, D3DCMP_LESS);\r\n\t\t\tbreak;\r\n\t\tcase SBITS_ATEST_GE128:\r\n\t\t\tIDirect3DDevice8_SetRenderState(pD3DDev8, D3DRS_ALPHATESTENABLE, TRUE);\r\n\t\t\tIDirect3DDevice8_SetRenderState(pD3DDev8, D3DRS_ALPHAREF, 128);\r\n\t\t\tIDirect3DDevice8_SetRenderState(pD3DDev8, D3DRS_ALPHAFUNC, D3DCMP_GREATEREQUAL);\r\n\t\t\tbreak;\r\n\t\t}\r\n\t}\r\n\r\n\tif (delta & SBITS_MISC_DEPTHWRITE)\r\n\t{\r\n\t\tif (bits & SBITS_MISC_DEPTHWRITE)\r\n\t\t\tIDirect3DDevice8_SetRenderState(pD3DDev8, D3DRS_ZWRITEENABLE, TRUE);\r\n\t\telse\r\n\t\t\tIDirect3DDevice8_SetRenderState(pD3DDev8, D3DRS_ZWRITEENABLE, FALSE);\r\n\t}\r\n\r\n\tif(delta & SBITS_MISC_NODEPTHTEST)\r\n\t{\r\n\t\tif(bits & SBITS_MISC_NODEPTHTEST)\r\n\t\t\tIDirect3DDevice8_SetRenderState(pD3DDev8, D3DRS_ZENABLE, FALSE);\r\n\t\telse\r\n\t\t\tIDirect3DDevice8_SetRenderState(pD3DDev8, D3DRS_ZENABLE, TRUE);\r\n\t}\r\n\r\n\tif (delta & SBITS_DEPTHFUNC_BITS)\r\n\t{\r\n\t\tswitch(bits & SBITS_DEPTHFUNC_BITS)\r\n\t\t{\r\n\t\tdefault:\r\n\t\tcase SBITS_DEPTHFUNC_CLOSEREQUAL:\r\n\t\t\tIDirect3DDevice8_SetRenderState(pD3DDev8, D3DRS_ZFUNC, D3DCMP_LESSEQUAL);\r\n\t\t\tbreak;\r\n\t\tcase SBITS_DEPTHFUNC_EQUAL:\r\n\t\t\tIDirect3DDevice8_SetRenderState(pD3DDev8, D3DRS_ZFUNC, D3DCMP_EQUAL);\r\n\t\t\tbreak;\r\n\t\tcase SBITS_DEPTHFUNC_CLOSER:\r\n\t\t\tIDirect3DDevice8_SetRenderState(pD3DDev8, D3DRS_ZFUNC, D3DCMP_LESS);\r\n\t\t\tbreak;\r\n\t\tcase SBITS_DEPTHFUNC_FURTHER:\r\n\t\t\tIDirect3DDevice8_SetRenderState(pD3DDev8, D3DRS_ZFUNC, D3DCMP_GREATER);\r\n\t\t\tbreak;\r\n\t\t}\r\n\t}\r\n\r\n\tif (delta & (SBITS_MASK_BITS))\r\n\t{\r\n\t\tIDirect3DDevice8_SetRenderState(pD3DDev8, D3DRS_COLORWRITEENABLE,\r\n\t\t\t\t((bits&SBITS_MASK_RED)?0:D3DCOLORWRITEENABLE_RED) |\r\n\t\t\t\t((bits&SBITS_MASK_GREEN)?0:D3DCOLORWRITEENABLE_GREEN) |\r\n\t\t\t\t((bits&SBITS_MASK_BLUE)?0:D3DCOLORWRITEENABLE_BLUE) |\r\n\t\t\t\t((bits&SBITS_MASK_ALPHA)?0:D3DCOLORWRITEENABLE_ALPHA));\r\n\t}\r\n}\r\n\r\nvoid D3D8BE_Reset(qboolean before)\r\n{\r\n\tint i;\r\n\tif (before)\r\n\t{\r\n\t\tIDirect3DDevice8_SetVertexShader(pD3DDev8, 0);\r\n\t\tIDirect3DDevice8_SetStreamSource(pD3DDev8, 0, NULL, 0);\r\n\t\tIDirect3DDevice8_SetIndices(pD3DDev8, NULL, 0);\r\n\r\n\t\tif (shaderstate.dynvbo_buff)\r\n\t\t\tIDirect3DVertexBuffer8_Release(shaderstate.dynvbo_buff);\r\n\t\tshaderstate.dynvbo_buff = NULL;\r\n\t\tif (shaderstate.dynidx_buff)\r\n\t\t\tIDirect3DIndexBuffer8_Release(shaderstate.dynidx_buff);\r\n\t\tshaderstate.dynidx_buff = NULL;\r\n\t}\r\n\telse\r\n\t{\r\n\t\tif (shaderstate.dynidx_buff)\r\n\t\t\treturn;\r\n\r\n\t\tIDirect3DDevice8_CreateVertexBuffer(pD3DDev8, shaderstate.dynvbo_size, D3DUSAGE_DYNAMIC|D3DUSAGE_WRITEONLY, 0, D3DPOOL_DEFAULT, &shaderstate.dynvbo_buff);\r\n\t\tIDirect3DDevice8_CreateIndexBuffer(pD3DDev8, shaderstate.dynidx_size, D3DUSAGE_DYNAMIC|D3DUSAGE_WRITEONLY, D3DFMT_QINDEX, D3DPOOL_DEFAULT, &shaderstate.dynidx_buff);\r\n\r\n\t\tfor (i = 0; i < MAX_TMUS; i++)\r\n\t\t{\r\n\t\t\tshaderstate.tmuflags[i] = ~0;\r\n\t\t\tBE_ApplyTMUState(i, 0);\r\n\t\t}\r\n\r\n\t\t/*force all state to change, thus setting a known state*/\r\n\t\tshaderstate.shaderbits = ~0;\r\n\t\tBE_ApplyShaderBits(0);\r\n\t}\r\n}\r\n\r\nstatic const char LIGHTPASS_SHADER[] = \"\\\r\n{\\n\\\r\n\tprogram rtlight\\n\\\r\n\t{\\n\\\r\n\t\tmap $diffuse\\n\\\r\n\t\tblendfunc add\\n\\\r\n\t}\\n\\\r\n\t{\\n\\\r\n\t\tmap $normalmap\\n\\\r\n\t}\\n\\\r\n\t{\\n\\\r\n\t\tmap $specular\\n\\\r\n\t}\\n\\\r\n}\";\r\n\r\nvoid D3D8BE_Init(void)\r\n{\r\n\tbe_maxpasses = MAX_TMUS;\r\n\tmemset(&shaderstate, 0, sizeof(shaderstate));\r\n\r\n\tFTable_Init();\r\n\r\n\tshaderstate.dynvbo_size = sizeof(vbovdata_t) * DYNVBUFFSIZE;\r\n\tshaderstate.dynidx_size = sizeof(index_t) * DYNIBUFFSIZE;\r\n\r\n\tD3D8BE_Reset(false);\r\n\r\n\tshaderstate.shader_rtlight = R_RegisterShader(\"rtlight\", SUF_NONE, LIGHTPASS_SHADER);\r\n\r\n\tR_InitFlashblends();\r\n}\r\n\r\nvoid D3D8BE_Set2D(void)\r\n{\t//start of some 2d rendering code.\r\n\tr_refdef.time = realtime;\r\n\tshaderstate.curtime = r_refdef.time;\r\n}\r\n\r\nstatic void allocvertexbuffer(IDirect3DVertexBuffer8 *buff, unsigned int bmaxsize, unsigned int *offset, void **data, unsigned int bytes)\r\n{\r\n\tunsigned int boff;\r\n\tif (*offset + bytes > bmaxsize)\r\n\t{\r\n\t\tboff = 0;\r\n\t\t*offset = bytes;\r\n\t}\r\n\telse\r\n\t{\r\n\t\tboff = *offset;\r\n\t\t*offset += bytes;\r\n\t}\r\n\td3dcheck(IDirect3DVertexBuffer8_Lock(buff, boff, bytes, (BYTE**)data, boff?D3DLOCK_NOOVERWRITE:D3DLOCK_DISCARD));\r\n}\r\n\r\nstatic unsigned int allocindexbuffer(void **dest, unsigned int entries)\r\n{\r\n\tunsigned int bytes = entries*sizeof(index_t);\r\n\tunsigned int offset;\r\n\r\n\tif (shaderstate.dynidx_offs + bytes > DYNIBUFFSIZE)\r\n\t{\r\n\t\toffset = 0;\r\n\t\tshaderstate.dynidx_offs = 0;\r\n\t}\r\n\telse\r\n\t{\r\n\t\toffset = shaderstate.dynidx_offs;\r\n\t\tshaderstate.dynidx_offs += bytes;\r\n\t}\r\n\r\n\td3dcheck(IDirect3DIndexBuffer8_Lock(shaderstate.dynidx_buff, offset, (unsigned int)entries, (BYTE**)dest, offset?D3DLOCK_NOOVERWRITE:D3DLOCK_DISCARD));\r\n\treturn offset/sizeof(index_t);\r\n}\r\n\r\nstatic void BindTexture(unsigned int tu, texid_t tex)\r\n{\r\n\tIDirect3DTexture8 *dt;\r\n\tif (tex)\r\n\t{\r\n\t\tdt = tex->ptr;\r\n\t\tshaderstate.curtexflags[tu] = tex->flags & SHADER_PASS_IMAGE_FLAGS_D3D8;\r\n\t}\r\n\telse\r\n\t\tdt = NULL;\r\n\tif (shaderstate.curtex[tu] != dt)\r\n\t{\r\n\t\tshaderstate.curtex[tu] = dt;\r\n\t\tIDirect3DDevice8_SetTexture (pD3DDev8, tu, (void*)dt);\r\n\t}\r\n}\r\n\r\nstatic void SelectPassTexture(unsigned int tu, shaderpass_t *pass)\r\n{\r\n\tint last;\r\n\textern texid_t missing_texture;\r\n\r\n\tswitch(pass->texgen)\r\n\t{\r\n\tdefault:\r\n\tcase T_GEN_DIFFUSE:\r\n\t\tif (shaderstate.curtexnums->base)\r\n\t\t\tBindTexture(tu, shaderstate.curtexnums->base);\r\n\t\telse\r\n\t\t\tBindTexture(tu, missing_texture);\r\n\t\tbreak;\r\n\tcase T_GEN_NORMALMAP:\r\n\t\tBindTexture( tu, shaderstate.curtexnums->bump);\r\n\t\tbreak;\r\n\tcase T_GEN_SPECULAR:\r\n\t\tBindTexture(tu, shaderstate.curtexnums->specular);\r\n\t\tbreak;\r\n\tcase T_GEN_UPPEROVERLAY:\r\n\t\tBindTexture(tu, shaderstate.curtexnums->upperoverlay);\r\n\t\tbreak;\r\n\tcase T_GEN_LOWEROVERLAY:\r\n\t\tBindTexture(tu, shaderstate.curtexnums->loweroverlay);\r\n\t\tbreak;\r\n\tcase T_GEN_FULLBRIGHT:\r\n\t\tBindTexture(tu, shaderstate.curtexnums->fullbright);\r\n\t\tbreak;\r\n\tcase T_GEN_ANIMMAP:\r\n\t\tBindTexture(tu, pass->anim_frames[(int)(pass->anim_fps * shaderstate.curtime) % pass->anim_numframes]);\r\n\t\tbreak;\r\n\tcase T_GEN_SINGLEMAP:\r\n\t\tBindTexture(tu, pass->anim_frames[0]);\r\n\t\tbreak;\r\n\tcase T_GEN_DELUXMAP:\r\n\t\tBindTexture(tu, shaderstate.curdeluxmap);\r\n\t\tbreak;\r\n\tcase T_GEN_LIGHTMAP:\r\n\t\tBindTexture(tu, shaderstate.curlightmap);\r\n\t\tbreak;\r\n\r\n\t/*case T_GEN_CURRENTRENDER:\r\n\t\tFIXME: no code to grab the current screen and convert to a texture\r\n\t\tbreak;*/\r\n\tcase T_GEN_VIDEOMAP:\r\n#ifdef HAVE_MEDIA_DECODER\r\n\t\tBindTexture(tu, Media_UpdateForShader(pass->cin));\r\n#else\r\n\t\tBindTexture(tu, missing_texture);\r\n#endif\r\n\t\tbreak;\r\n\t}\r\n\r\n\tBE_ApplyTMUState(tu, shaderstate.curtexflags[tu]|pass->flags);\r\n\r\n\tif (tu == 0)\r\n\t{\r\n\t\t/*if (shaderstate.passsinglecolour)\r\n\t\t{\r\n\t\t\tIDirect3DDevice8_SetTextureStageState(pD3DDev8, tu, D3DTSS_CONSTANT, shaderstate.passcolour);\r\n\t\t\tlast = D3DTA_CONSTANT;\r\n\t\t\tIDirect3DDevice8_SetRenderState(pD3DDev8, D3DRS_COLORVERTEX, FALSE);\r\n\t\t}\r\n\t\telse*/\r\n\t\t{\r\n\t\t\tIDirect3DDevice8_SetRenderState(pD3DDev8, D3DRS_COLORVERTEX, TRUE);\r\n//\t\t\tIDirect3DDevice8_SetRenderState(pD3DDev8, D3DRS_DIFFUSEMATERIALSOURCE, D3DMCS_COLOR1);\t//default state anyway.\r\n\t\t\tlast = D3DTA_DIFFUSE;\r\n\t\t}\r\n\t}\r\n\telse\r\n\t\tlast = D3DTA_CURRENT;\r\n\r\n\tswitch (pass->blendmode)\r\n\t{\r\n\tcase PBM_DOTPRODUCT:\r\n\t\tIDirect3DDevice8_SetTextureStageState(pD3DDev8, tu, D3DTSS_COLORARG1, last);\r\n\t\tIDirect3DDevice8_SetTextureStageState(pD3DDev8, tu, D3DTSS_COLORARG2, D3DTA_TEXTURE);\r\n\t\tIDirect3DDevice8_SetTextureStageState(pD3DDev8, tu, D3DTSS_COLOROP, D3DTOP_DOTPRODUCT3);\r\n\r\n\t\tIDirect3DDevice8_SetTextureStageState(pD3DDev8, tu, D3DTSS_ALPHAARG1, last);\r\n\t\tIDirect3DDevice8_SetTextureStageState(pD3DDev8, tu, D3DTSS_ALPHAARG2, D3DTA_TEXTURE);\r\n\t\tIDirect3DDevice8_SetTextureStageState(pD3DDev8, tu, D3DTSS_ALPHAOP, D3DTOP_SELECTARG2);\r\n\t\tbreak;\r\n\tcase PBM_REPLACE:\r\n\t\tIDirect3DDevice8_SetTextureStageState(pD3DDev8, tu, D3DTSS_COLORARG1, last);\r\n\t\tIDirect3DDevice8_SetTextureStageState(pD3DDev8, tu, D3DTSS_COLORARG2, D3DTA_TEXTURE);\r\n\t\tIDirect3DDevice8_SetTextureStageState(pD3DDev8, tu, D3DTSS_COLOROP, D3DTOP_SELECTARG2);\r\n\r\n\t\tif (shaderstate.flags & (BEF_FORCETRANSPARENT | BEF_FORCEADDITIVE))\r\n\t\t{\r\n\t\t\tIDirect3DDevice8_SetTextureStageState(pD3DDev8, tu, D3DTSS_ALPHAARG1, last);\r\n\t\t\tIDirect3DDevice8_SetTextureStageState(pD3DDev8, tu, D3DTSS_ALPHAARG2, D3DTA_TEXTURE);\r\n\t\t\tIDirect3DDevice8_SetTextureStageState(pD3DDev8, tu, D3DTSS_ALPHAOP, D3DTOP_MODULATE);\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tIDirect3DDevice8_SetTextureStageState(pD3DDev8, tu, D3DTSS_ALPHAARG1, last);\r\n\t\t\tIDirect3DDevice8_SetTextureStageState(pD3DDev8, tu, D3DTSS_ALPHAARG2, D3DTA_TEXTURE);\r\n\t\t\tIDirect3DDevice8_SetTextureStageState(pD3DDev8, tu, D3DTSS_ALPHAOP, D3DTOP_MODULATE);\r\n\t\t}\r\n\t\tbreak;\r\n\tcase PBM_ADD:\r\n\t\tif (tu == 0)\r\n\t\t\tgoto forcemod;\r\n\t\tIDirect3DDevice8_SetTextureStageState(pD3DDev8, tu, D3DTSS_COLORARG1, last);\r\n\t\tIDirect3DDevice8_SetTextureStageState(pD3DDev8, tu, D3DTSS_COLORARG2, D3DTA_TEXTURE);\r\n\t\tIDirect3DDevice8_SetTextureStageState(pD3DDev8, tu, D3DTSS_COLOROP, D3DTOP_ADD);\r\n\r\n\t\tIDirect3DDevice8_SetTextureStageState(pD3DDev8, tu, D3DTSS_ALPHAARG1, last);\r\n\t\tIDirect3DDevice8_SetTextureStageState(pD3DDev8, tu, D3DTSS_ALPHAARG2, D3DTA_TEXTURE);\r\n\t\tIDirect3DDevice8_SetTextureStageState(pD3DDev8, tu, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);\r\n\t\tbreak;\r\n\tcase PBM_DECAL:\r\n\t\tif (!tu)\r\n\t\t\tgoto forcemod;\r\n\t\tIDirect3DDevice8_SetTextureStageState(pD3DDev8, tu, D3DTSS_COLORARG1, D3DTA_TEXTURE);\r\n\t\tIDirect3DDevice8_SetTextureStageState(pD3DDev8, tu, D3DTSS_COLORARG2, last);\r\n\t\tIDirect3DDevice8_SetTextureStageState(pD3DDev8, tu, D3DTSS_COLOROP, D3DTOP_BLENDTEXTUREALPHA);\r\n\r\n\t\tIDirect3DDevice8_SetTextureStageState(pD3DDev8, tu, D3DTSS_ALPHAARG1, last);\r\n\t\tIDirect3DDevice8_SetTextureStageState(pD3DDev8, tu, D3DTSS_ALPHAARG2, D3DTA_TEXTURE);\r\n\t\tIDirect3DDevice8_SetTextureStageState(pD3DDev8, tu, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);\r\n\t\tbreak;\r\n\tcase PBM_OVERBRIGHT:\r\n\t\tIDirect3DDevice8_SetTextureStageState(pD3DDev8, tu, D3DTSS_COLORARG1, last);\r\n\t\tIDirect3DDevice8_SetTextureStageState(pD3DDev8, tu, D3DTSS_COLORARG2, D3DTA_TEXTURE);\r\n\t\t{\r\n\t\t\textern cvar_t gl_overbright;\r\n\t\t\tswitch (gl_overbright.ival)\r\n\t\t\t{\r\n\t\t\tcase 1:\r\n\t\t\t\tIDirect3DDevice8_SetTextureStageState(pD3DDev8, tu, D3DTSS_COLOROP, D3DTOP_MODULATE2X);\r\n\t\t\t\tbreak;\r\n\t\t\tcase 2:\r\n\t\t\tcase 3:\r\n\t\t\t\tIDirect3DDevice8_SetTextureStageState(pD3DDev8, tu, D3DTSS_COLOROP, D3DTOP_MODULATE4X);\r\n\t\t\t\tbreak;\r\n\t\t\tdefault:\r\n\t\t\t\tIDirect3DDevice8_SetTextureStageState(pD3DDev8, tu, D3DTSS_COLOROP, D3DTOP_MODULATE);\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\t\t}\r\n\t\tIDirect3DDevice8_SetTextureStageState(pD3DDev8, tu, D3DTSS_ALPHAARG1, last);\r\n\t\tIDirect3DDevice8_SetTextureStageState(pD3DDev8, tu, D3DTSS_ALPHAARG2, D3DTA_TEXTURE);\r\n\t\tIDirect3DDevice8_SetTextureStageState(pD3DDev8, tu, D3DTSS_ALPHAOP, D3DTOP_MODULATE);\r\n\t\tbreak;\r\n\tdefault:\r\n\tcase PBM_MODULATE:\r\n\tforcemod:\r\n\t\tIDirect3DDevice8_SetTextureStageState(pD3DDev8, tu, D3DTSS_COLORARG1, last);\r\n\t\tIDirect3DDevice8_SetTextureStageState(pD3DDev8, tu, D3DTSS_COLORARG2, D3DTA_TEXTURE);\r\n\t\tIDirect3DDevice8_SetTextureStageState(pD3DDev8, tu, D3DTSS_COLOROP, D3DTOP_MODULATE);\r\n\r\n\t\tIDirect3DDevice8_SetTextureStageState(pD3DDev8, tu, D3DTSS_ALPHAARG1, last);\r\n\t\tIDirect3DDevice8_SetTextureStageState(pD3DDev8, tu, D3DTSS_ALPHAARG2, D3DTA_TEXTURE);\r\n\t\tIDirect3DDevice8_SetTextureStageState(pD3DDev8, tu, D3DTSS_ALPHAOP, D3DTOP_MODULATE);\r\n\t\tbreak;\r\n\t}\r\n}\r\n\r\n#if FIXME\r\nstatic void colourgenbyte(const shaderpass_t *pass, int cnt, byte_vec4_t *srcb, vec4_t *srcf, byte_vec4_t *dst, const mesh_t *mesh)\r\n{\r\n\tD3DCOLOR block;\r\n\tswitch (pass->rgbgen)\r\n\t{\r\n\tcase RGB_GEN_ENTITY:\r\n\t\tblock = D3DCOLOR_COLORVALUE(shaderstate.curentity->shaderRGBAf[0], shaderstate.curentity->shaderRGBAf[1], shaderstate.curentity->shaderRGBAf[2], shaderstate.curentity->shaderRGBAf[3]);\r\n\t\twhile((cnt)--)\r\n\t\t{\r\n\t\t\t((D3DCOLOR*)dst)[cnt] = block;\r\n\t\t}\r\n\t\tbreak;\r\n\tcase RGB_GEN_ONE_MINUS_ENTITY:\r\n\t\tblock = D3DCOLOR_COLORVALUE(1-shaderstate.curentity->shaderRGBAf[0], 1-shaderstate.curentity->shaderRGBAf[1], 1-shaderstate.curentity->shaderRGBAf[2], 1-shaderstate.curentity->shaderRGBAf[3]);\r\n\t\twhile((cnt)--)\r\n\t\t{\r\n\t\t\t((D3DCOLOR*)dst)[cnt] = block;\r\n\t\t}\r\n\t\tbreak;\r\n\tcase RGB_GEN_VERTEX_LIGHTING:\r\n\tcase RGB_GEN_VERTEX_EXACT:\r\n\t\tif (srcb)\r\n\t\t{\r\n\t\t\twhile((cnt)--)\r\n\t\t\t{\r\n\t\t\t\tqbyte r, g, b;\r\n\t\t\t\tr=srcb[cnt][0];\r\n\t\t\t\tg=srcb[cnt][1];\r\n\t\t\t\tb=srcb[cnt][2];\r\n\t\t\t\tdst[cnt][0] = b;\r\n\t\t\t\tdst[cnt][1] = g;\r\n\t\t\t\tdst[cnt][2] = r;\r\n\t\t\t}\r\n\t\t}\r\n\t\telse if (srcf)\r\n\t\t{\r\n\t\t\twhile((cnt)--)\r\n\t\t\t{\r\n\t\t\t\tint r, g, b;\r\n\t\t\t\tr=srcf[cnt][0]*255;\r\n\t\t\t\tg=srcf[cnt][1]*255;\r\n\t\t\t\tb=srcf[cnt][2]*255;\r\n\t\t\t\tdst[cnt][0] = bound(0, b, 255);\r\n\t\t\t\tdst[cnt][1] = bound(0, g, 255);\r\n\t\t\t\tdst[cnt][2] = bound(0, r, 255);\r\n\t\t\t}\r\n\t\t}\r\n\t\telse\r\n\t\t\tgoto identity;\r\n\t\tbreak;\r\n\tcase RGB_GEN_ONE_MINUS_VERTEX:\r\n\t\tif (srcb)\r\n\t\t{\r\n\t\t\twhile((cnt)--)\r\n\t\t\t{\r\n\t\t\t\tqbyte r, g, b;\r\n\t\t\t\tr=255-srcb[cnt][0];\r\n\t\t\t\tg=255-srcb[cnt][1];\r\n\t\t\t\tb=255-srcb[cnt][2];\r\n\t\t\t\tdst[cnt][0] = b;\r\n\t\t\t\tdst[cnt][1] = g;\r\n\t\t\t\tdst[cnt][2] = r;\r\n\t\t\t}\r\n\t\t}\r\n\t\telse if (srcf)\r\n\t\t{\r\n\t\t\twhile((cnt)--)\r\n\t\t\t{\r\n\t\t\t\tint r, g, b;\r\n\t\t\t\tr=255-srcf[cnt][0]*255;\r\n\t\t\t\tg=255-srcf[cnt][1]*255;\r\n\t\t\t\tb=255-srcf[cnt][2]*255;\r\n\t\t\t\tdst[cnt][0] = bound(0, b, 255);\r\n\t\t\t\tdst[cnt][1] = bound(0, g, 255);\r\n\t\t\t\tdst[cnt][2] = bound(0, r, 255);\r\n\t\t\t}\r\n\t\t}\r\n\t\telse\r\n\t\t\tgoto identity;\r\n\t\tbreak;\r\n\tcase RGB_GEN_IDENTITY_LIGHTING:\r\n\t\t//compensate for overbrights\r\n\t\tblock = D3DCOLOR_RGBA(255, 255, 255, 255); //shaderstate.identitylighting\r\n\t\twhile((cnt)--)\r\n\t\t{\r\n\t\t\t((D3DCOLOR*)dst)[cnt] = block;\r\n\t\t}\r\n\t\tbreak;\r\n\tdefault:\r\n\tidentity:\r\n\tcase RGB_GEN_IDENTITY:\r\n\t\tblock = D3DCOLOR_RGBA(255, 255, 255, 255);\r\n\t\twhile((cnt)--)\r\n\t\t{\r\n\t\t\t((D3DCOLOR*)dst)[cnt] = block;\r\n\t\t}\r\n\t\tbreak;\r\n\tcase RGB_GEN_CONST:\r\n\t\tblock = D3DCOLOR_COLORVALUE(pass->rgbgen_func.args[0], pass->rgbgen_func.args[1], pass->rgbgen_func.args[2], 1);\r\n\t\twhile((cnt)--)\r\n\t\t{\r\n\t\t\t((D3DCOLOR*)dst)[cnt] = block;\r\n\t\t}\r\n\t\tbreak;\r\n\tcase RGB_GEN_LIGHTING_DIFFUSE:\r\n\t\t//collect lighting details for mobile entities\r\n\t\tif (!mesh->normals_array)\r\n\t\t{\r\n\t\t\tblock = D3DCOLOR_RGBA(255, 255, 255, 255);\r\n\t\t\twhile((cnt)--)\r\n\t\t\t{\r\n\t\t\t\t((D3DCOLOR*)dst)[cnt] = block;\r\n\t\t\t}\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tR_LightArraysByte_BGR(shaderstate.curentity , mesh->xyz_array, dst, cnt, mesh->normals_array);\r\n\t\t}\r\n\t\tbreak;\r\n\tcase RGB_GEN_WAVE:\r\n\t\t{\r\n\t\t\tfloat *table;\r\n\t\t\tfloat c;\r\n\r\n\t\t\ttable = FTableForFunc(pass->rgbgen_func.type);\r\n\t\t\tc = pass->rgbgen_func.args[2] + shaderstate.curtime * pass->rgbgen_func.args[3];\r\n\t\t\tc = FTABLE_EVALUATE(table, c) * pass->rgbgen_func.args[1] + pass->rgbgen_func.args[0];\r\n\t\t\tc = bound(0.0f, c, 1.0f);\r\n\t\t\tblock = D3DCOLOR_COLORVALUE(c, c, c, 1);\r\n\r\n\t\t\twhile((cnt)--)\r\n\t\t\t{\r\n\t\t\t\t((D3DCOLOR*)dst)[cnt] = block;\r\n\t\t\t}\r\n\t\t}\r\n\t\tbreak;\r\n\r\n\tcase RGB_GEN_TOPCOLOR:\r\n\tcase RGB_GEN_BOTTOMCOLOR:\r\n#ifdef warningmsg\r\n#pragma warningmsg(\"fix 24bit player colours\")\r\n#endif\r\n\t\tblock = D3DCOLOR_RGBA(255, 255, 255, 255);\r\n\t\twhile((cnt)--)\r\n\t\t{\r\n\t\t\t((D3DCOLOR*)dst)[cnt] = block;\r\n\t\t}\r\n\t//\tCon_Printf(\"RGB_GEN %i not supported\\n\", pass->rgbgen);\r\n\t\tbreak;\r\n\t}\r\n}\r\n\r\nstatic void alphagenbyte(const shaderpass_t *pass, int cnt, byte_vec4_t *srcb, vec4_t *srcf, byte_vec4_t *dst, const mesh_t *mesh)\r\n{\r\n\t/*FIXME: Skip this if the rgbgen did it*/\r\n\tfloat *table;\r\n\tunsigned char t;\r\n\tfloat f;\r\n\tvec3_t v1, v2;\r\n\r\n\tswitch (pass->alphagen)\r\n\t{\r\n\tdefault:\r\n\tcase ALPHA_GEN_IDENTITY:\r\n\t\tif (shaderstate.flags & BEF_FORCETRANSPARENT)\r\n\t\t{\r\n\t\t\tf = shaderstate.curentity->shaderRGBAf[3];\r\n\t\t\tif (f < 0)\r\n\t\t\t\tt = 0;\r\n\t\t\telse if (f >= 1)\r\n\t\t\t\tt = 255;\r\n\t\t\telse\r\n\t\t\t\tt = f*255;\r\n\t\t\twhile(cnt--)\r\n\t\t\t\tdst[cnt][3] = t;\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\twhile(cnt--)\r\n\t\t\t\tdst[cnt][3] = 255;\r\n\t\t}\r\n\t\tbreak;\r\n\r\n\tcase ALPHA_GEN_CONST:\r\n\t\tt = pass->alphagen_func.args[0]*255;\r\n\t\twhile(cnt--)\r\n\t\t\tdst[cnt][3] = t;\r\n\t\tbreak;\r\n\r\n\tcase ALPHA_GEN_WAVE:\r\n\t\ttable = FTableForFunc(pass->alphagen_func.type);\r\n\t\tf = pass->alphagen_func.args[2] + shaderstate.curtime * pass->alphagen_func.args[3];\r\n\t\tf = FTABLE_EVALUATE(table, f) * pass->alphagen_func.args[1] + pass->alphagen_func.args[0];\r\n\t\tt = bound(0.0f, f, 1.0f)*255;\r\n\t\twhile(cnt--)\r\n\t\t\tdst[cnt][3] = t;\r\n\t\tbreak;\r\n\r\n\tcase ALPHA_GEN_PORTAL:\r\n\t\t//FIXME: should this be per-vert?\r\n\t\tVectorAdd(mesh->xyz_array[0], shaderstate.curentity->origin, v1);\r\n\t\tVectorSubtract(r_origin, v1, v2);\r\n\t\tf = VectorLength(v2) * (1.0 / 255.0);\r\n\t\tt = bound(0.0f, f, 1.0f)*255;\r\n\r\n\t\twhile(cnt--)\r\n\t\t\tdst[cnt][3] = t;\r\n\t\tbreak;\r\n\r\n\tcase ALPHA_GEN_VERTEX:\r\n\t\tif (srcb)\r\n\t\t{\r\n\t\t\twhile(cnt--)\r\n\t\t\t{\r\n\t\t\t\tdst[cnt][3] = srcb[cnt][3];\r\n\t\t\t}\r\n\t\t}\r\n\t\telse if (srcf)\r\n\t\t{\r\n\t\t\twhile(cnt--)\r\n\t\t\t{\r\n\t\t\t\tdst[cnt][3] = bound(0, srcf[cnt][3]*255, 255);\r\n\t\t\t}\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\twhile(cnt--)\r\n\t\t\t{\r\n\t\t\t\tdst[cnt][3] = 255;\r\n\t\t\t}\r\n\t\t}\r\n\t\tbreak;\r\n\r\n\tcase ALPHA_GEN_ENTITY:\r\n\t\tt = bound(0, shaderstate.curentity->shaderRGBAf[3], 1)*255;\r\n\t\twhile(cnt--)\r\n\t\t{\r\n\t\t\tdst[cnt][3] = t;\r\n\t\t}\r\n\t\tbreak;\r\n\r\n\tcase ALPHA_GEN_SPECULAR:\r\n\t\t{\r\n\t\t\tint i;\r\n\t\t\tVectorSubtract(r_origin, shaderstate.curentity->origin, v1);\r\n\r\n\t\t\tif (!Matrix3_Compare(shaderstate.curentity->axis, (void *)axisDefault))\r\n\t\t\t{\r\n\t\t\t\tMatrix3_Multiply_Vec3(shaderstate.curentity->axis, v1, v2);\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tVectorCopy(v1, v2);\r\n\t\t\t}\r\n\r\n\t\t\tfor (i = 0; i < cnt; i++)\r\n\t\t\t{\r\n\t\t\t\tVectorSubtract(v2, mesh->xyz_array[i], v1);\r\n\t\t\t\tf = DotProduct(v1, mesh->normals_array[i]) * Q_rsqrt(DotProduct(v1,v1));\r\n\t\t\t\tf = f * f * f * f * f;\r\n\t\t\t\tdst[i][3] = bound (0, (int)(f*255), 255);\r\n\t\t\t}\r\n\t\t}\r\n\t\tbreak;\r\n\t}\r\n}\r\n\r\nstatic unsigned int BE_GenerateColourMods(unsigned int vertcount, const shaderpass_t *pass)\r\n{\r\n\tunsigned int ret = 0;\r\n#ifdef FIXME\r\n\tunsigned char *map;\r\n\tconst mesh_t *m;\r\n\tunsigned int mno;\r\n\r\n\tm = shaderstate.meshlist[0];\r\n\r\n\tif (pass->flags & SHADER_PASS_NOCOLORARRAY)\r\n\t{\r\n\t\tshaderstate.passsinglecolour = true;\r\n\t\tshaderstate.passcolour = D3DCOLOR_RGBA(255,255,255,255);\r\n\t\tcolourgenbyte(pass, 1, (byte_vec4_t*)&shaderstate.passcolour, NULL, (byte_vec4_t*)&shaderstate.passcolour, m);\r\n\t\talphagenbyte(pass, 1, (byte_vec4_t*)&shaderstate.passcolour, NULL, (byte_vec4_t*)&shaderstate.passcolour, m);\r\n\t\t/*FIXME: just because there's no rgba set, there's no reason to assume it should be a single colour (unshaded ents)*/\r\n\t\td3dcheck(IDirect3DDevice8_SetStreamSource(pD3DDev8, STRM_COL, NULL, 0, 0));\r\n\t}\r\n\telse\r\n\t{\r\n\t\tshaderstate.passsinglecolour = false;\r\n\r\n\t\tret |= D3D_VDEC_COL4B;\r\n\t\tif (shaderstate.batchvbo && (m->colors4f_array[0] &&\r\n\t\t\t\t\t\t((pass->rgbgen == RGB_GEN_VERTEX_LIGHTING) ||\r\n\t\t\t\t\t\t(pass->rgbgen == RGB_GEN_VERTEX_EXACT) ||\r\n\t\t\t\t\t\t(pass->rgbgen == RGB_GEN_ONE_MINUS_VERTEX)) &&\r\n\t\t\t\t\t\t(pass->alphagen == ALPHA_GEN_VERTEX)))\r\n\t\t{\r\n\t\t\t//fixme\r\n\t\t\td3dcheck(IDirect3DDevice8_SetStreamSource(pD3DDev8, STRM_COL, shaderstate.batchvbo->colours[0].d3d.buff, shaderstate.batchvbo->colours[0].d3d.offs, sizeof(vbovdata_t)));\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tallocvertexbuffer(shaderstate.dyncol_buff, shaderstate.dyncol_size, &shaderstate.dyncol_offs, (void**)&map, vertcount*sizeof(D3DCOLOR));\r\n\t\t\tfor (mno = 0; mno < shaderstate.nummeshes; mno++)\r\n\t\t\t{\r\n\t\t\t\tm = shaderstate.meshlist[mno];\r\n\t\t\t\tcolourgenbyte(pass, m->numvertexes, m->colors4b_array, m->colors4f_array[0], (byte_vec4_t*)map, m);\r\n\t\t\t\talphagenbyte(pass, m->numvertexes, m->colors4b_array, m->colors4f_array[0], (byte_vec4_t*)map, m);\r\n\t\t\t\tmap += m->numvertexes*4;\r\n\t\t\t}\r\n\t\t\td3dcheck(IDirect3DVertexBuffer8_Unlock(shaderstate.dyncol_buff));\r\n\t\t\td3dcheck(IDirect3DDevice8_SetStreamSource(pD3DDev8, STRM_COL, shaderstate.dyncol_buff, shaderstate.dyncol_offs - vertcount*sizeof(D3DCOLOR), sizeof(D3DCOLOR)));\r\n\t\t}\r\n\t}\r\n#endif\r\n\treturn ret;\r\n}\r\n#endif\r\n/*********************************************************************************************************/\r\n/*========================================== texture coord generation =====================================*/\r\n\r\n#if FIXME\r\nstatic void tcgen_environment(float *st, unsigned int numverts, float *xyz, float *normal)\r\n{\r\n\tint\t\t\ti;\r\n\tvec3_t\t\tviewer, reflected;\r\n\tfloat\t\td;\r\n\r\n\tvec3_t\t\trorg;\r\n\r\n\tif (!normal)\r\n\t{\r\n\t\tfor (i = 0 ; i < numverts ; i++, st += 2 )\r\n\t\t{\r\n\t\t\tst[0] = xyz[0];\r\n\t\t\tst[1] = xyz[1];\r\n\t\t}\r\n\t\treturn;\r\n\t}\r\n\r\n\tRotateLightVector(shaderstate.curentity->axis, shaderstate.curentity->origin, r_origin, rorg);\r\n\r\n\tfor (i = 0 ; i < numverts ; i++, xyz += sizeof(vecV_t)/sizeof(vec_t), normal += 3, st += 2 )\r\n\t{\r\n\t\tVectorSubtract (rorg, xyz, viewer);\r\n\t\tVectorNormalizeFast (viewer);\r\n\r\n\t\td = DotProduct (normal, viewer);\r\n\r\n\t\treflected[0] = normal[0]*2*d - viewer[0];\r\n\t\treflected[1] = normal[1]*2*d - viewer[1];\r\n\t\treflected[2] = normal[2]*2*d - viewer[2];\r\n\r\n\t\tst[0] = 0.5 + reflected[1] * 0.5;\r\n\t\tst[1] = 0.5 - reflected[2] * 0.5;\r\n\t}\r\n}\r\n\r\nstatic float *tcgen(const shaderpass_t *pass, int cnt, float *dst, const mesh_t *mesh)\r\n{\r\n\tint i;\r\n\tvecV_t *src;\r\n\tswitch (pass->tcgen)\r\n\t{\r\n\tdefault:\r\n\tcase TC_GEN_BASE:\r\n\t\treturn (float*)mesh->st_array;\r\n\tcase TC_GEN_LIGHTMAP:\r\n\t\treturn (float*)mesh->lmst_array[0];\r\n\tcase TC_GEN_NORMAL:\r\n\t\treturn (float*)mesh->normals_array;\r\n\tcase TC_GEN_SVECTOR:\r\n\t\treturn (float*)mesh->snormals_array;\r\n\tcase TC_GEN_TVECTOR:\r\n\t\treturn (float*)mesh->tnormals_array;\r\n\tcase TC_GEN_ENVIRONMENT:\r\n\t\ttcgen_environment(dst, cnt, (float*)mesh->xyz_array, (float*)mesh->normals_array);\r\n\t\treturn dst;\r\n\r\n\tcase TC_GEN_DOTPRODUCT:\r\n\t\treturn dst;//mesh->st_array[0];\r\n\tcase TC_GEN_VECTOR:\r\n\t\tsrc = mesh->xyz_array;\r\n\t\tfor (i = 0; i < cnt; i++, dst += 2)\r\n\t\t{\r\n\t\t\tdst[0] = DotProduct(pass->tcgenvec[0], src[i]);\r\n\t\t\tdst[1] = DotProduct(pass->tcgenvec[1], src[i]);\r\n\t\t}\r\n\t\treturn dst;\r\n\t}\r\n}\r\n\r\n/*src and dst can be the same address when tcmods are chained*/\r\nstatic void tcmod(const tcmod_t *tcmod, int cnt, const float *src, float *dst, const mesh_t *mesh)\r\n{\r\n\tfloat *table;\r\n\tfloat t1, t2;\r\n\tfloat cost, sint;\r\n\tint j;\r\n\tswitch (tcmod->type)\r\n\t{\r\n\t\tcase SHADER_TCMOD_ROTATE:\r\n\t\t\tcost = tcmod->args[0] * shaderstate.curtime;\r\n\t\t\tsint = R_FastSin(cost);\r\n\t\t\tcost = R_FastSin(cost + 0.25);\r\n\r\n\t\t\tfor (j = 0; j < cnt; j++, dst+=2,src+=2)\r\n\t\t\t{\r\n\t\t\t\tt1 = cost * (src[0] - 0.5f) - sint * (src[1] - 0.5f) + 0.5f;\r\n\t\t\t\tt2 = cost * (src[1] - 0.5f) + sint * (src[0] - 0.5f) + 0.5f;\r\n\t\t\t\tdst[0] = t1;\r\n\t\t\t\tdst[1] = t2;\r\n\t\t\t}\r\n\t\t\tbreak;\r\n\r\n\t\tcase SHADER_TCMOD_SCALE:\r\n\t\t\tt1 = tcmod->args[0];\r\n\t\t\tt2 = tcmod->args[1];\r\n\r\n\t\t\tfor (j = 0; j < cnt; j++, dst+=2,src+=2)\r\n\t\t\t{\r\n\t\t\t\tdst[0] = src[0] * t1;\r\n\t\t\t\tdst[1] = src[1] * t2;\r\n\t\t\t}\r\n\t\t\tbreak;\r\n\r\n\t\tcase SHADER_TCMOD_TURB:\r\n\t\t\tt1 = tcmod->args[2] + shaderstate.curtime * tcmod->args[3];\r\n\t\t\tt2 = tcmod->args[1];\r\n\r\n\t\t\tfor (j = 0; j < cnt; j++, dst+=2,src+=2)\r\n\t\t\t{\r\n\t\t\t\tdst[0] = src[0] + R_FastSin (src[0]*t2+t1) * t2;\r\n\t\t\t\tdst[1] = src[1] + R_FastSin (src[1]*t2+t1) * t2;\r\n\t\t\t}\r\n\t\t\tbreak;\r\n\r\n\t\tcase SHADER_TCMOD_STRETCH:\r\n\t\t\ttable = FTableForFunc(tcmod->args[0]);\r\n\t\t\tt2 = tcmod->args[3] + shaderstate.curtime * tcmod->args[4];\r\n\t\t\tt1 = FTABLE_EVALUATE(table, t2) * tcmod->args[2] + tcmod->args[1];\r\n\t\t\tt1 = t1 ? 1.0f / t1 : 1.0f;\r\n\t\t\tt2 = 0.5f - 0.5f * t1;\r\n\t\t\tfor (j = 0; j < cnt; j++, dst+=2,src+=2)\r\n\t\t\t{\r\n\t\t\t\tdst[0] = src[0] * t1 + t2;\r\n\t\t\t\tdst[1] = src[1] * t1 + t2;\r\n\t\t\t}\r\n\t\t\tbreak;\r\n\r\n\t\tcase SHADER_TCMOD_SCROLL:\r\n\t\t\tt1 = tcmod->args[0] * shaderstate.curtime;\r\n\t\t\tt2 = tcmod->args[1] * shaderstate.curtime;\r\n\r\n\t\t\tfor (j = 0; j < cnt; j++, dst += 2, src+=2)\r\n\t\t\t{\r\n\t\t\t\tdst[0] = src[0] + t1;\r\n\t\t\t\tdst[1] = src[1] + t2;\r\n\t\t\t}\r\n\t\t\tbreak;\r\n\r\n\t\tcase SHADER_TCMOD_TRANSFORM:\r\n\t\t\tfor (j = 0; j < cnt; j++, dst+=2, src+=2)\r\n\t\t\t{\r\n\t\t\t\tt1 = src[0];\r\n\t\t\t\tt2 = src[1];\r\n\t\t\t\tdst[0] = t1 * tcmod->args[0] + t2 * tcmod->args[2] + tcmod->args[4];\r\n\t\t\t\tdst[1] = t1 * tcmod->args[1] + t2 * tcmod->args[3] + tcmod->args[5];\r\n\t\t\t}\r\n\t\t\tbreak;\r\n\r\n\t\tdefault:\r\n\t\t\tbreak;\r\n\t}\r\n}\r\n\r\nstatic void GenerateTCMods(const shaderpass_t *pass, float *dest)\r\n{\r\n\tmesh_t *mesh;\r\n\tunsigned int mno;\r\n\t// unsigned int fvertex = 0; //unused variable\r\n//\tint i;\r\n\tfloat *src;\r\n\tfloat *out;\r\n\tfor (mno = 0; mno < shaderstate.nummeshes; mno++)\r\n\t{\r\n\t\tmesh = shaderstate.meshlist[mno];\r\n\r\n#if 0\r\n\t\tout = dest + mesh->vbofirstvert*2;\r\n#else\r\n\t\tout = dest;\r\n\t\tdest += mesh->numvertexes*2;\r\n#endif\r\n\r\n\t\tsrc = tcgen(pass, mesh->numvertexes, out, mesh);\r\n\t\t//tcgen might return unmodified info\r\n\t\t/*if (pass->numtcmods)\r\n\t\t{\r\n\t\t\tfor (i = 0; i < pass->numtcmods; i++)\r\n\t\t\t{\r\n\t\t\t\ttcmod(&pass->tcmods[i], mesh->numvertexes, src, out, mesh);\r\n\t\t\t\tsrc = out;\r\n\t\t\t}\r\n\t\t}\r\n\t\telse*/ if (src != out)\r\n\t\t{\r\n\t\t\t//memcpy(out, src, sizeof(vec2_t)*mesh->numvertexes);\r\n\t\t}\r\n\t}\r\n}\r\n\r\n//end texture coords\r\n/*******************************************************************************************************************/\r\n\r\nstatic void deformgen(const deformv_t *deformv, int cnt, vecV_t *src, vecV_t *dst, const mesh_t *mesh)\r\n{\r\n\tfloat *table;\r\n\tint j, k;\r\n\tfloat args[4];\r\n\tfloat deflect;\r\n\tswitch (deformv->type)\r\n\t{\r\n\tdefault:\r\n\tcase DEFORMV_NONE:\r\n\t\tif (src != dst)\r\n\t\t\tmemcpy(dst, src, sizeof(*src)*cnt);\r\n\t\tbreak;\r\n\r\n\tcase DEFORMV_WAVE:\r\n\t\tif (!mesh->normals_array)\r\n\t\t{\r\n\t\t\tif (src != dst)\r\n\t\t\t\tmemcpy(dst, src, sizeof(*src)*cnt);\r\n\t\t\treturn;\r\n\t\t}\r\n\t\targs[0] = deformv->func.args[0];\r\n\t\targs[1] = deformv->func.args[1];\r\n\t\targs[3] = deformv->func.args[2] + deformv->func.args[3] * shaderstate.curtime;\r\n\t\ttable = FTableForFunc(deformv->func.type);\r\n\r\n\t\tfor ( j = 0; j < cnt; j++ )\r\n\t\t{\r\n\t\t\tdeflect = deformv->args[0] * (src[j][0]+src[j][1]+src[j][2]) + args[3];\r\n\t\t\tdeflect = FTABLE_EVALUATE(table, deflect) * args[1] + args[0];\r\n\r\n\t\t\t// Deflect vertex along its normal by wave amount\r\n\t\t\tVectorMA(src[j], deflect, mesh->normals_array[j], dst[j]);\r\n\t\t}\r\n\t\tbreak;\r\n\r\n\tcase DEFORMV_NORMAL:\r\n\t\t//normal does not actually move the verts, but it does change the normals array\r\n\t\t//we don't currently support that.\r\n\t\tif (src != dst)\r\n\t\t\tmemcpy(dst, src, sizeof(*src)*cnt);\r\n/*\r\n\t\targs[0] = deformv->args[1] * shaderstate.curtime;\r\n\r\n\t\tfor ( j = 0; j < cnt; j++ )\r\n\t\t{\r\n\t\t\targs[1] = normalsArray[j][2] * args[0];\r\n\r\n\t\t\tdeflect = deformv->args[0] * R_FastSin(args[1]);\r\n\t\t\tnormalsArray[j][0] *= deflect;\r\n\t\t\tdeflect = deformv->args[0] * R_FastSin(args[1] + 0.25);\r\n\t\t\tnormalsArray[j][1] *= deflect;\r\n\t\t\tVectorNormalizeFast(normalsArray[j]);\r\n\t\t}\r\n*/\t\tbreak;\r\n\r\n\tcase DEFORMV_MOVE:\r\n\t\ttable = FTableForFunc(deformv->func.type);\r\n\t\tdeflect = deformv->func.args[2] + shaderstate.curtime * deformv->func.args[3];\r\n\t\tdeflect = FTABLE_EVALUATE(table, deflect) * deformv->func.args[1] + deformv->func.args[0];\r\n\r\n\t\tfor ( j = 0; j < cnt; j++ )\r\n\t\t\tVectorMA(src[j], deflect, deformv->args, dst[j]);\r\n\t\tbreak;\r\n\r\n\tcase DEFORMV_BULGE:\r\n\t\targs[0] = deformv->args[0]/(2*M_PI);\r\n\t\targs[1] = deformv->args[1];\r\n\t\targs[2] = shaderstate.curtime * deformv->args[2]/(2*M_PI);\r\n\r\n\t\tfor (j = 0; j < cnt; j++)\r\n\t\t{\r\n\t\t\tdeflect = R_FastSin(mesh->st_array[j][0]*args[0] + args[2])*args[1];\r\n\t\t\tdst[j][0] = src[j][0]+deflect*mesh->normals_array[j][0];\r\n\t\t\tdst[j][1] = src[j][1]+deflect*mesh->normals_array[j][1];\r\n\t\t\tdst[j][2] = src[j][2]+deflect*mesh->normals_array[j][2];\r\n\t\t}\r\n\t\tbreak;\r\n\r\n\tcase DEFORMV_AUTOSPRITE:\r\n\t\tif (mesh->numindexes < 6)\r\n\t\t\tbreak;\r\n\r\n\t\tfor (j = 0; j < cnt-3; j+=4, src+=4, dst+=4)\r\n\t\t{\r\n\t\t\tvec3_t mid, d;\r\n\t\t\tfloat radius;\r\n\t\t\tmid[0] = 0.25*(src[0][0] + src[1][0] + src[2][0] + src[3][0]);\r\n\t\t\tmid[1] = 0.25*(src[0][1] + src[1][1] + src[2][1] + src[3][1]);\r\n\t\t\tmid[2] = 0.25*(src[0][2] + src[1][2] + src[2][2] + src[3][2]);\r\n\t\t\tVectorSubtract(src[0], mid, d);\r\n\t\t\tradius = 2*VectorLength(d);\r\n\r\n\t\t\tfor (k = 0; k < 4; k++)\r\n\t\t\t{\r\n\t\t\t\tdst[k][0] = mid[0] + radius*((mesh->st_array[j+k][0]-0.5)*r_refdef.m_view[0+0]-(mesh->st_array[j+k][1]-0.5)*r_refdef.m_view[0+1]);\r\n\t\t\t\tdst[k][1] = mid[1] + radius*((mesh->st_array[j+k][0]-0.5)*r_refdef.m_view[4+0]-(mesh->st_array[j+k][1]-0.5)*r_refdef.m_view[4+1]);\r\n\t\t\t\tdst[k][2] = mid[2] + radius*((mesh->st_array[j+k][0]-0.5)*r_refdef.m_view[8+0]-(mesh->st_array[j+k][1]-0.5)*r_refdef.m_view[8+1]);\r\n\t\t\t}\r\n\t\t}\r\n\t\tbreak;\r\n\r\n\tcase DEFORMV_AUTOSPRITE2:\r\n\t\tif (mesh->numindexes < 6)\r\n\t\t\tbreak;\r\n\r\n\t\tfor (k = 0; k < mesh->numindexes; k += 6)\r\n\t\t{\r\n\t\t\tint long_axis, short_axis;\r\n\t\t\tvec3_t axis;\r\n\t\t\tfloat len[3];\r\n\t\t\tmat3_t m0, m1, m2, result;\r\n\t\t\tfloat *quad[4];\r\n\t\t\tvec3_t rot_centre, tv, tv2;\r\n\r\n\t\t\tquad[0] = (float *)(src + mesh->indexes[k+0]);\r\n\t\t\tquad[1] = (float *)(src + mesh->indexes[k+1]);\r\n\t\t\tquad[2] = (float *)(src + mesh->indexes[k+2]);\r\n\r\n\t\t\tfor (j = 2; j >= 0; j--)\r\n\t\t\t{\r\n\t\t\t\tquad[3] = (float *)(src + mesh->indexes[k+3+j]);\r\n\t\t\t\tif (!VectorEquals (quad[3], quad[0]) &&\r\n\t\t\t\t\t!VectorEquals (quad[3], quad[1]) &&\r\n\t\t\t\t\t!VectorEquals (quad[3], quad[2]))\r\n\t\t\t\t{\r\n\t\t\t\t\tbreak;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\t// build a matrix were the longest axis of the billboard is the Y-Axis\r\n\t\t\tVectorSubtract(quad[1], quad[0], m0[0]);\r\n\t\t\tVectorSubtract(quad[2], quad[0], m0[1]);\r\n\t\t\tVectorSubtract(quad[2], quad[1], m0[2]);\r\n\t\t\tlen[0] = DotProduct(m0[0], m0[0]);\r\n\t\t\tlen[1] = DotProduct(m0[1], m0[1]);\r\n\t\t\tlen[2] = DotProduct(m0[2], m0[2]);\r\n\r\n\t\t\tif ((len[2] > len[1]) && (len[2] > len[0]))\r\n\t\t\t{\r\n\t\t\t\tif (len[1] > len[0])\r\n\t\t\t\t{\r\n\t\t\t\t\tlong_axis = 1;\r\n\t\t\t\t\tshort_axis = 0;\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\tlong_axis = 0;\r\n\t\t\t\t\tshort_axis = 1;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\telse if ((len[1] > len[2]) && (len[1] > len[0]))\r\n\t\t\t{\r\n\t\t\t\tif (len[2] > len[0])\r\n\t\t\t\t{\r\n\t\t\t\t\tlong_axis = 2;\r\n\t\t\t\t\tshort_axis = 0;\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\tlong_axis = 0;\r\n\t\t\t\t\tshort_axis = 2;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\telse //if ( (len[0] > len[1]) && (len[0] > len[2]) )\r\n\t\t\t{\r\n\t\t\t\tif (len[2] > len[1])\r\n\t\t\t\t{\r\n\t\t\t\t\tlong_axis = 2;\r\n\t\t\t\t\tshort_axis = 1;\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\tlong_axis = 1;\r\n\t\t\t\t\tshort_axis = 2;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\tif (DotProduct(m0[long_axis], m0[short_axis]))\r\n\t\t\t{\r\n\t\t\t\tVectorNormalize2(m0[long_axis], axis);\r\n\t\t\t\tVectorCopy(axis, m0[1]);\r\n\r\n\t\t\t\tif (axis[0] || axis[1])\r\n\t\t\t\t{\r\n\t\t\t\t\tVectorVectors(m0[1], m0[2], m0[0]);\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\tVectorVectors(m0[1], m0[0], m0[2]);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tVectorNormalize2(m0[long_axis], axis);\r\n\t\t\t\tVectorNormalize2(m0[short_axis], m0[0]);\r\n\t\t\t\tVectorCopy(axis, m0[1]);\r\n\t\t\t\tCrossProduct(m0[0], m0[1], m0[2]);\r\n\t\t\t}\r\n\r\n\t\t\tfor (j = 0; j < 3; j++)\r\n\t\t\t\trot_centre[j] = (quad[0][j] + quad[1][j] + quad[2][j] + quad[3][j]) * 0.25;\r\n\r\n\t\t\tif (shaderstate.curentity)\r\n\t\t\t{\r\n\t\t\t\tVectorAdd(shaderstate.curentity->origin, rot_centre, tv);\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tVectorCopy(rot_centre, tv);\r\n\t\t\t}\r\n\t\t\tVectorSubtract(r_origin, tv, tv);\r\n\r\n\t\t\t// filter any longest-axis-parts off the camera-direction\r\n\t\t\tdeflect = -DotProduct(tv, axis);\r\n\r\n\t\t\tVectorMA(tv, deflect, axis, m1[2]);\r\n\t\t\tVectorNormalizeFast(m1[2]);\r\n\t\t\tVectorCopy(axis, m1[1]);\r\n\t\t\tCrossProduct(m1[1], m1[2], m1[0]);\r\n\r\n\t\t\tMatrix3_Transpose(m1, m2);\r\n\t\t\tMatrix3_Multiply(m2, m0, result);\r\n\r\n\t\t\tfor (j = 0; j < 4; j++)\r\n\t\t\t{\r\n\t\t\t\tint v = ((vecV_t*)quad[j]-src);\r\n\t\t\t\tVectorSubtract(quad[j], rot_centre, tv);\r\n\t\t\t\tMatrix3_Multiply_Vec3((void *)result, tv, tv2);\r\n\t\t\t\tVectorAdd(rot_centre, tv2, dst[v]);\r\n\t\t\t}\r\n\t\t}\r\n\t\tbreak;\r\n\r\n//\tcase DEFORMV_PROJECTION_SHADOW:\r\n//\t\tbreak;\r\n\t}\r\n}\r\n#endif\r\n\r\n\r\n/*does not do the draw call, does not consider indicies (except for billboard generation) */\r\nstatic qboolean BE_DrawMeshChain_SetupPass(shaderpass_t *pass, unsigned int vertcount, unsigned int *vertfirst)\r\n{\r\n\tint vdec;\r\n\tvbovdata_t *map;\r\n\tint i, mno;\r\n\tunsigned int passno = 0, tmu;\r\n\tmesh_t *mesh;\r\n\r\n\tint lastpass = pass->numMergedPasses;\r\n\r\n\tfor (i = 0; i < lastpass; i++)\r\n\t{\r\n\t\tif (pass[i].texgen == T_GEN_UPPEROVERLAY && !TEXLOADED(shaderstate.curtexnums->upperoverlay))\r\n\t\t\tcontinue;\r\n\t\tif (pass[i].texgen == T_GEN_LOWEROVERLAY && !TEXLOADED(shaderstate.curtexnums->loweroverlay))\r\n\t\t\tcontinue;\r\n\t\tif (pass[i].texgen == T_GEN_FULLBRIGHT && !TEXLOADED(shaderstate.curtexnums->fullbright))\r\n\t\t\tcontinue;\r\n\t\tbreak;\r\n\t}\r\n\tif (i == lastpass)\r\n\t\treturn false;\r\n\r\n\t/*all meshes in a chain must have the same features*/\r\n\tvdec = D3DFVF_QVBO;\r\n\r\n\tallocvertexbuffer(shaderstate.dynvbo_buff, shaderstate.dynvbo_size, &shaderstate.dynvbo_offs, (void**)&map, vertcount*sizeof(*map));\r\n\t*vertfirst = (shaderstate.dynvbo_offs - vertcount*sizeof(*map))/sizeof(*map);\r\n\r\n\r\n\tfor (mno = 0; mno < shaderstate.nummeshes; mno++)\r\n\t{\r\n\t\tmesh = shaderstate.meshlist[mno];\r\n\r\n\t\tfor(i = 0; i < mesh->numvertexes; i++, map++)\r\n\t\t{\r\n\t\t\tVectorCopy(mesh->xyz_array[i], map->coord);\r\n\t\t\tif (mesh->colors4f_array[0])\r\n\t\t\t{\r\n\t\t\t\tmap->colorsb[0] = mesh->colors4f_array[0][i][2]*255;\r\n\t\t\t\tmap->colorsb[1] = mesh->colors4f_array[0][i][1]*255;\r\n\t\t\t\tmap->colorsb[2] = mesh->colors4f_array[0][i][0]*255;\r\n\t\t\t\tmap->colorsb[3] = mesh->colors4f_array[0][i][3]*255;\r\n\t\t\t}\r\n\t\t\telse if (mesh->colors4b_array)\r\n\t\t\t{\r\n\t\t\t\tmap->colorsb[0] = mesh->colors4b_array[i][2];\r\n\t\t\t\tmap->colorsb[1] = mesh->colors4b_array[i][1];\r\n\t\t\t\tmap->colorsb[2] = mesh->colors4b_array[i][0];\r\n\t\t\t\tmap->colorsb[3] = mesh->colors4b_array[i][3];\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t\tVector4Set(map->colorsb, 255, 255, 255, 255);\r\n\t\t\tVector2Copy(mesh->st_array[i], map->tc[0]);\r\n\t\t\tif (mesh->lmst_array[0])\r\n\t\t\t\tVector2Copy(mesh->lmst_array[0][i], map->tc[1]);\r\n\t\t}\r\n\t}\r\n\td3dcheck(IDirect3DVertexBuffer8_Unlock(shaderstate.dynvbo_buff));\r\n\r\n\td3dcheck(IDirect3DDevice8_SetStreamSource(pD3DDev8, 0, shaderstate.dynvbo_buff, sizeof(vbovdata_t)));\r\n\td3dcheck(IDirect3DDevice8_SetIndices(pD3DDev8, shaderstate.dynidx_buff, *vertfirst));\t//base vertex is considered part of ebo state in d3d8, I guess\r\n\r\n\t/*we only use one colour, generated from the first pass*/\r\n\t//vdec |= BE_GenerateColourMods(vertcount, pass);\r\n\r\n\ttmu = 0;\r\n\t/*activate tmus*/\r\n\tfor (passno = 0; passno < lastpass; passno++)\r\n\t{\r\n\t\tif (pass[passno].texgen == T_GEN_UPPEROVERLAY && !TEXLOADED(shaderstate.curtexnums->upperoverlay))\r\n\t\t\tcontinue;\r\n\t\tif (pass[passno].texgen == T_GEN_LOWEROVERLAY && !TEXLOADED(shaderstate.curtexnums->loweroverlay))\r\n\t\t\tcontinue;\r\n\t\tif (pass[passno].texgen == T_GEN_FULLBRIGHT && !TEXLOADED(shaderstate.curtexnums->fullbright))\r\n\t\t\tcontinue;\r\n\r\n\t\tSelectPassTexture(tmu, pass+passno);\r\n\r\n\t\tif (pass[passno].tcgen == TC_GEN_BASE)\r\n\t\t\td3dcheck(IDirect3DDevice8_SetTextureStageState(pD3DDev8, tmu, D3DTSS_TEXCOORDINDEX, 0));\r\n\t\telse\r\n\t\t\td3dcheck(IDirect3DDevice8_SetTextureStageState(pD3DDev8, tmu, D3DTSS_TEXCOORDINDEX, 1));\r\n\r\n\r\n/*\t\tvdec |= D3D_VDEC_ST0<<tmu;\r\n\t\tif (shaderstate.batchvbo && pass[passno].tcgen == TC_GEN_BASE && !pass[passno].numtcmods)\r\n\t\t\td3dcheck(IDirect3DDevice8_SetStreamSource(pD3DDev8, STRM_TC0+tmu, shaderstate.batchvbo->texcoord.d3d.buff, sizeof(vbovdata_t)));\r\n\t\telse if (shaderstate.batchvbo && pass[passno].tcgen == TC_GEN_LIGHTMAP && !pass[passno].numtcmods)\r\n\t\t\td3dcheck(IDirect3DDevice8_SetStreamSource(pD3DDev8, STRM_TC0+tmu, shaderstate.batchvbo->lmcoord[0].d3d.buff, sizeof(vbovdata_t)));\r\n\t\telse\r\n\t\t{\r\n\t\t\tallocvertexbuffer(shaderstate.dynst_buff[tmu], shaderstate.dynst_size, &shaderstate.dynst_offs[tmu], &map, vertcount*sizeof(vec2_t));\r\n\t\t\tGenerateTCMods(pass+passno, map);\r\n\t\t\td3dcheck(IDirect3DVertexBuffer8_Unlock(shaderstate.dynst_buff[tmu]));\r\n\t\t\td3dcheck(IDirect3DDevice8_SetStreamSource(pD3DDev8, STRM_TC0+tmu, shaderstate.dynst_buff[tmu], shaderstate.dynst_offs[tmu] - vertcount*sizeof(vec2_t), sizeof(vec2_t)));\r\n\t\t}*/\r\n\t\ttmu++;\r\n\t}\r\n\t/*deactivate any extras*/\r\n\tfor (; tmu < shaderstate.lastpasscount; )\r\n\t{\r\n\t\tBindTexture(tmu, NULL);\r\n\t\td3dcheck(IDirect3DDevice8_SetTextureStageState(pD3DDev8, tmu, D3DTSS_COLOROP, D3DTOP_DISABLE));\r\n\t\td3dcheck(IDirect3DDevice8_SetTextureStageState(pD3DDev8, tmu, D3DTSS_ALPHAOP, D3DTOP_DISABLE));\r\n\t\ttmu++;\r\n\t}\r\n\tshaderstate.lastpasscount = tmu;\r\n\r\n\tIDirect3DDevice8_SetVertexShader(pD3DDev8, vdec);\r\n\r\n\tBE_ApplyShaderBits(pass->shaderbits);\r\n\treturn true;\r\n}\r\n\r\nstatic void BE_SubmitMeshChain(unsigned int firstvert, unsigned int vertcount, unsigned int idxfirst, int unsigned idxcount)\r\n{\r\n\tif (shaderstate.flags & BEF_LINES)\r\n\t\tIDirect3DDevice8_DrawIndexedPrimitive(pD3DDev8, D3DPT_LINELIST, firstvert, vertcount, idxfirst, idxcount/2);\r\n\telse\r\n\t\tIDirect3DDevice8_DrawIndexedPrimitive(pD3DDev8, D3DPT_TRIANGLELIST, firstvert, vertcount, idxfirst, idxcount/3);\r\n\r\n\tRQuantAdd(RQUANT_DRAWS, 1);\r\n\r\n\tRQuantAdd(RQUANT_PRIMITIVEINDICIES, idxcount);\r\n}\r\n\r\n#ifdef FIXME\r\nstatic void R_FetchPlayerColour(unsigned int cv, vec3_t rgb)\r\n{\r\n\tint i;\r\n\r\n\tif (cv >= 16)\r\n\t{\r\n\t\trgb[0] = (((cv&0xff0000)>>16)**((unsigned char*)&d_8to24rgbtable[15]+0)) / (256.0*256);\r\n\t\trgb[1] = (((cv&0x00ff00)>>8)**((unsigned char*)&d_8to24rgbtable[15]+1)) / (256.0*256);\r\n\t\trgb[2] = (((cv&0x0000ff)>>0)**((unsigned char*)&d_8to24rgbtable[15]+2)) / (256.0*256);\r\n\t\treturn;\r\n\t}\r\n\ti = cv;\r\n\tif (i >= 8)\r\n\t{\r\n\t\ti<<=4;\r\n\t}\r\n\telse\r\n\t{\r\n\t\ti<<=4;\r\n\t\ti+=15;\r\n\t}\r\n\ti*=3;\r\n\trgb[0] = host_basepal[i+0] / 255.0;\r\n\trgb[1] = host_basepal[i+1] / 255.0;\r\n\trgb[2] = host_basepal[i+2] / 255.0;\r\n/*\tif (!gammaworks)\r\n\t{\r\n\t\t*retred = gammatable[*retred];\r\n\t\t*retgreen = gammatable[*retgreen];\r\n\t\t*retblue = gammatable[*retblue];\r\n\t}*/\r\n}\r\n\r\nstatic void BE_ApplyUniforms(program_t *prog, int permu)\r\n{\r\n\tstruct programpermu_s *perm = &prog->permu[permu];\r\n\tshaderprogparm_t *pp;\r\n\tvec4_t param4;\r\n\tint h;\r\n\tint i;\r\n\tIDirect3DDevice8_SetVertexShader(pD3DDev8, perm->h.hlsl.vert);\r\n\tIDirect3DDevice8_SetPixelShader(pD3DDev8, perm->h.hlsl.frag);\r\n\tfor (i = 0, pp = perm->parm; i < perm->numparms; i++, pp++)\r\n\t{\r\n\t\th = pp->handle;\r\n\t\tswitch (pp->type)\r\n\t\t{\r\n\t\tcase SP_M_PROJECTION:\r\n\t\t\tIDirect3DDevice8_SetVertexShaderConstantF(pD3DDev8, h, d3d_trueprojection, 4);\r\n\t\t\tbreak;\r\n\t\tcase SP_M_VIEW:\r\n\t\t\tIDirect3DDevice8_SetVertexShaderConstantF(pD3DDev8, h, r_refdef.m_view, 4);\r\n\t\t\tbreak;\r\n\t\tcase SP_M_MODEL:\r\n\t\t\tIDirect3DDevice8_SetVertexShaderConstantF(pD3DDev8, h, shaderstate.m_model, 4);\r\n\t\t\tbreak;\r\n\t\tcase SP_E_VBLEND:\r\n\t\t\tIDirect3DDevice8_SetVertexShaderConstantF(pD3DDev8, h, shaderstate.meshlist[0]->xyz_blendw, 2);\r\n\t\t\tbreak;\r\n\r\n\r\n\t\tcase SP_V_EYEPOS:\r\n\t\t\tIDirect3DDevice8_SetPixelShaderConstantF(pD3DDev8, h, r_origin, 1);\r\n\t\t\tbreak;\r\n\t\tcase SP_E_EYEPOS:\r\n\t\t\t{\r\n\t\t\t\tvec4_t t2;\r\n\t\t\t\tfloat m16[16];\r\n\t\t\t\tMatrix4x4_CM_ModelMatrixFromAxis(m16, shaderstate.curentity->axis[0], shaderstate.curentity->axis[1], shaderstate.curentity->axis[2], shaderstate.curentity->origin);\r\n\t\t\t\tMatrix4x4_CM_Transform3(m16, r_origin, t2);\r\n\t\t\t\tIDirect3DDevice8_SetPixelShaderConstantF(pD3DDev8, h, t2, 1);\r\n\t\t\t}\r\n\t\t\tbreak;\r\n\t\tcase SP_E_TIME:\r\n\t\t\t{\r\n\t\t\t\tvec4_t t1 = {shaderstate.curtime};\r\n\t\t\t\tIDirect3DDevice8_SetPixelShaderConstantF(pD3DDev8, h, t1, 1);\r\n\t\t\t}\r\n\t\t\tbreak;\r\n\t\tcase SP_M_MODELVIEWPROJECTION:\r\n\t\t\t{\r\n\t\t\t\tfloat mv[16], mvp[16];\r\n\t\t\t\tMatrix4_Multiply(r_refdef.m_view, shaderstate.m_model, mv);\r\n\t\t\t\tMatrix4_Multiply(d3d_trueprojection, mv, mvp);\r\n\t\t\t\tIDirect3DDevice8_SetVertexShaderConstantF(pD3DDev8, h, mvp, 4);\r\n\t\t\t}\r\n\t\t\tbreak;\r\n\r\n\t\tcase SP_LIGHTPOSITION:\r\n\t\t\t{\r\n\t\t\t\t/*light position in model space*/\r\n\t\t\t\tfloat inv[16];\r\n\t\t\t\tvec3_t t2;\r\n\t\t\t\tqboolean Matrix4_Invert(const float *m, float *out);\r\n\r\n\t\t\t\tMatrix4_Invert(shaderstate.m_model, inv);\r\n\t\t\t\tMatrix4x4_CM_Transform3(inv, shaderstate.curdlight->origin, t2);\r\n\t\t\t\tIDirect3DDevice8_SetVertexShaderConstantF(pD3DDev8, h, t2, 1);\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\r\n\t\tcase SP_LIGHTRADIUS:\r\n\t\t\tIDirect3DDevice8_SetPixelShaderConstantF(pD3DDev8, h, &shaderstate.curdlight->radius, 1);\r\n\t\t\tbreak;\r\n\t\tcase SP_LIGHTCOLOUR:\r\n\t\t\tIDirect3DDevice8_SetPixelShaderConstantF(pD3DDev8, h, shaderstate.curdlight_colours, 1);\r\n\t\t\tbreak;\r\n\r\n\t\tcase SP_E_L_DIR:\r\n\t\t\tIDirect3DDevice8_SetVertexShaderConstantF(pD3DDev8, h, shaderstate.curentity->light_dir, 1);\r\n\t\t\tbreak;\r\n\t\tcase SP_E_L_MUL:\r\n\t\t\tIDirect3DDevice8_SetVertexShaderConstantF(pD3DDev8, h, shaderstate.curentity->light_range, 1);\r\n\t\t\tbreak;\r\n\t\tcase SP_E_L_AMBIENT:\r\n\t\t\tIDirect3DDevice8_SetVertexShaderConstantF(pD3DDev8, h, shaderstate.curentity->light_avg, 1);\r\n\t\t\tbreak;\r\n\r\n\t\tcase SP_E_COLOURS:\r\n\t\t\tIDirect3DDevice8_SetPixelShaderConstantF(pD3DDev8, h, shaderstate.curentity->shaderRGBAf, 1);\r\n\t\t\tbreak;\r\n\t\tcase SP_E_COLOURSIDENT:\r\n\t\t\tif (shaderstate.flags & BEF_FORCECOLOURMOD)\r\n\t\t\t\tIDirect3DDevice8_SetPixelShaderConstantF(pD3DDev8, h, shaderstate.curentity->shaderRGBAf, 1);\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tvec4_t tmp = {1, 1, 1, shaderstate.curentity->shaderRGBAf[3]};\r\n\t\t\t\tIDirect3DDevice8_SetPixelShaderConstantF(pD3DDev8, h, tmp, 1);\r\n\t\t\t}\r\n\t\t\tbreak;\r\n\r\n\t\tcase SP_E_TOPCOLOURS:\r\n\t\t\tR_FetchPlayerColour(shaderstate.curentity->topcolour, param4);\r\n\t\t\tIDirect3DDevice8_SetPixelShaderConstantF(pD3DDev8, h, param4, 3);\r\n\t\t\tbreak;\r\n\t\tcase SP_E_BOTTOMCOLOURS:\r\n\t\t\tR_FetchPlayerColour(shaderstate.curentity->bottomcolour, param4);\r\n\t\t\tIDirect3DDevice8_SetPixelShaderConstantF(pD3DDev8, h, param4, 3);\r\n\t\t\tbreak;\r\n\r\n\t\tcase SP_E_LMSCALE:\r\n\t\t\tVector4Set(param4, 1, 1, 1, 1);\r\n\t\t\tif (shaderstate.curentity->model && (shaderstate.curentity->model->engineflags & MDLF_NEEDOVERBRIGHT))\r\n\t\t\t{\r\n\t\t\t\textern cvar_t gl_overbright;\r\n\t\t\t\tconst float identitylighting = 1;\r\n\t\t\t\tfloat sc = (1<<bound(0, gl_overbright.ival, 2)) * identitylighting;\r\n\t\t\t\tVectorSet(param4, sc, sc, sc);\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tconst float identitylighting = 1;\r\n\t\t\t\tVectorSet(param4, identitylighting, identitylighting, identitylighting);\r\n\t\t\t}\r\n\t\t\tparam4[3] = 1;\r\n\t\t\tIDirect3DDevice8_SetVertexShaderConstantF(pD3DDev8, h, param4, 3);\r\n\t\t\tbreak;\r\n\r\n\t\tcase SP_M_ENTBONES:\r\n\t\tcase SP_M_MODELVIEW:\r\n\t\tcase SP_E_VLSCALE:\r\n\t\tcase SP_E_ORIGIN:\r\n\t\tcase SP_E_GLOWMOD:\r\n\t\tcase SP_W_FOG:\r\n\t\tcase SP_M_INVVIEWPROJECTION:\r\n\t\tcase SP_M_INVMODELVIEWPROJECTION:\r\n\t\tcase SP_SOURCESIZE:\r\n\t\tcase SP_S_COLOUR:\r\n\t\tcase SP_LIGHTCOLOURSCALE:\r\n\t\tcase SP_LIGHTSCREEN:\r\n\t\tcase SP_LIGHTCUBEMATRIX:\r\n\t\tcase SP_LIGHTSHADOWMAPPROJ:\r\n\t\tcase SP_LIGHTSHADOWMAPSCALE:\r\n\r\n\t\tcase SP_RENDERTEXTURESCALE:\r\n\r\n\t\tcase SP_FIRSTIMMEDIATE:\r\n\t\tcase SP_CONSTI:\r\n\t\tcase SP_CONSTF:\r\n\t\tcase SP_CVARI:\r\n\t\tcase SP_CVARF:\r\n\t\tcase SP_CVAR3F:\r\n\t\tcase SP_TEXTURE:\r\n\t\tcase SP_BAD:\r\n\t\t\tCon_Printf(\"shader property %i not implemented\\n\", pp->type);\r\n\t\t\tbreak;\r\n\t\t}\r\n\t}\r\n}\r\n\r\nstatic void BE_RenderMeshProgram(shader_t *s, unsigned int vertbase, unsigned int vertfirst, unsigned int vertcount, unsigned int idxfirst, unsigned int idxcount)\r\n{\r\n\tint vdec = D3D_VDEC_ST0|D3D_VDEC_ST1|D3D_VDEC_NORM;\r\n\tint passno;\r\n\tint perm = 0;\r\n\r\n\tprogram_t *p = s->prog;\r\n\r\n#ifdef SKELETALMODELS\r\n\tif (shaderstate.batchvbo && shaderstate.batchvbo->numbones)\r\n\t{\r\n\t\tif (p->permu[perm|PERMUTATION_SKELETAL].h.loaded)\r\n\t\t\tperm |= PERMUTATION_SKELETAL;\r\n\t\telse\r\n\t\t\treturn;\r\n\t}\r\n#endif\r\n\tif (TEXVALID(shaderstate.curtexnums->bump) && p->permu[perm|PERMUTATION_BUMPMAP].h.loaded)\r\n\t\tperm |= PERMUTATION_BUMPMAP;\r\n\tif (TEXVALID(shaderstate.curtexnums->fullbright) && p->permu[perm|PERMUTATION_FULLBRIGHT].h.loaded)\r\n\t\tperm |= PERMUTATION_FULLBRIGHT;\r\n\tif (p->permu[perm|PERMUTATION_UPPERLOWER].h.loaded && (TEXLOADED(shaderstate.curtexnums->upperoverlay) || TEXLOADED(shaderstate.curtexnums->loweroverlay)))\r\n\t\tperm |= PERMUTATION_UPPERLOWER;\r\n\tif (r_refdef.globalfog.density && p->permu[perm|PERMUTATION_FOG].h.loaded)\r\n\t\tperm |= PERMUTATION_FOG;\r\n#ifdef NONSKELETALMODELS\r\n\tif (p->permu[perm|PERMUTATION_FRAMEBLEND].h.loaded && shaderstate.batchvbo && shaderstate.batchvbo->coord2.d3d.buff)\r\n\t{\r\n\t\tperm |= PERMUTATION_FRAMEBLEND;\r\n\t\tvdec |= D3D_VDEC_POS2;\r\n\t}\r\n#endif\r\n//\tif (p->permu[perm|PERMUTATION_DELUXE].h.loaded && TEXVALID(shaderstate.curtexnums->bump) && shaderstate.curbatch->lightmap[0] >= 0 && lightmap[shaderstate.curbatch->lightmap[0]]->hasdeluxe)\r\n//\t\tperm |= PERMUTATION_DELUXE;\r\n#if MAXRLIGHTMAPS > 1\r\n\tif (shaderstate.curbatch->lightmap[1] >= 0 && p->permu[perm|PERMUTATION_LIGHTSTYLES].h.loaded)\r\n\t\tperm |= PERMUTATION_LIGHTSTYLES;\r\n#endif\r\n\r\n\tBE_ApplyUniforms(p, perm);\r\n\r\n\r\n\tBE_ApplyShaderBits(s->passes->shaderbits);\r\n\r\n\t/*activate tmus*/\r\n\tfor (passno = 0; passno < s->numpasses; passno++)\r\n\t{\r\n\t\tSelectPassTexture(passno, s->passes+passno);\r\n\t}\r\n\t/*deactivate any extras*/\r\n\tfor (; passno < shaderstate.lastpasscount; passno++)\r\n\t{\r\n\t\tBindTexture(passno, NULL);\r\n\t\td3dcheck(IDirect3DDevice8_SetTextureStageState(pD3DDev8, passno, D3DTSS_COLOROP, D3DTOP_DISABLE));\r\n\t\td3dcheck(IDirect3DDevice8_SetTextureStageState(pD3DDev8, passno, D3DTSS_ALPHAOP, D3DTOP_DISABLE));\r\n\t}\r\n\tshaderstate.lastpasscount = passno;\r\n\r\n\t/*colours*/\r\n\tif (vdec & D3D_VDEC_COL4B)\r\n\t{\r\n\t\tif (shaderstate.batchvbo)\r\n\t\t{\r\n\t\t\t//fixme\r\n\t\t\td3dcheck(IDirect3DDevice8_SetStreamSource(pD3DDev8, STRM_COL, shaderstate.batchvbo->colours[0].d3d.buff, shaderstate.batchvbo->colours[0].d3d.offs, sizeof(vbovdata_t)));\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tint mno,v;\r\n\t\t\tvoid *map;\r\n\t\t\tmesh_t *m;\r\n\t\t\tallocvertexbuffer(shaderstate.dynst_buff[0], shaderstate.dynst_size, &shaderstate.dynst_offs[0], &map, vertcount*sizeof(byte_vec4_t));\r\n\t\t\tfor (mno = 0, vertcount = 0; mno < shaderstate.nummeshes; mno++)\r\n\t\t\t{\r\n\t\t\t\tbyte_vec4_t *dest = (byte_vec4_t*)((char*)map+vertcount*sizeof(byte_vec4_t));\r\n\t\t\t\tm = shaderstate.meshlist[mno];\r\n\t\t\t\tif (m->colors4f_array[0])\r\n\t\t\t\t{\r\n\t\t\t\t\tfor (v = 0; v < m->numvertexes; v++)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\t//fixme:\r\n\t\t\t\t\t\tdest[v][0] = bound(0, m->colors4f_array[0][v][0] * 255, 255);\r\n\t\t\t\t\t\tdest[v][1] = bound(0, m->colors4f_array[0][v][1] * 255, 255);\r\n\t\t\t\t\t\tdest[v][2] = bound(0, m->colors4f_array[0][v][2] * 255, 255);\r\n\t\t\t\t\t\tdest[v][3] = bound(0, m->colors4f_array[0][v][3] * 255, 255);\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t\telse if (m->colors4b_array)\r\n\t\t\t\t\tmemcpy(dest, m->colors4b_array, m->numvertexes*sizeof(byte_vec4_t));\r\n\t\t\t\telse\r\n\t\t\t\t\tmemset(dest, 0, m->numvertexes*sizeof(byte_vec4_t));\r\n\t\t\t\tvertcount += m->numvertexes;\r\n\t\t\t}\r\n\t\t\td3dcheck(IDirect3DVertexBuffer8_Unlock(shaderstate.dynst_buff[0]));\r\n\t\t\td3dcheck(IDirect3DDevice8_SetStreamSource(pD3DDev8, STRM_COL, shaderstate.dynst_buff[0], shaderstate.dynst_offs[0] - vertcount*sizeof(byte_vec4_t), sizeof(byte_vec4_t)));\r\n\t\t}\r\n\t}\r\n\r\n\t/*texture coords*/\r\n\tif (vdec & D3D_VDEC_ST0)\r\n\t{\r\n\t\tif (shaderstate.batchvbo)\r\n\t\t{\r\n\t\t\tif (shaderstate.batchvbo && !shaderstate.batchvbo->vaodynamic)\r\n\t\t\t\td3dcheck(IDirect3DDevice8_SetStreamSource(pD3DDev8, STRM_TC0, shaderstate.batchvbo->texcoord.d3d.buff, shaderstate.batchvbo->texcoord.d3d.offs, sizeof(vbovdata_t)));\r\n\t\t\telse\r\n\t\t\t\td3dcheck(IDirect3DDevice8_SetStreamSource(pD3DDev8, STRM_TC0, shaderstate.batchvbo->texcoord.d3d.buff, shaderstate.batchvbo->texcoord.d3d.offs, sizeof(vec2_t)));\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tint mno;\r\n\t\t\tvoid *map;\r\n\t\t\tmesh_t *m;\r\n\t\t\tallocvertexbuffer(shaderstate.dynst_buff[0], shaderstate.dynst_size, &shaderstate.dynst_offs[0], &map, vertcount*sizeof(vec2_t));\r\n\t\t\tfor (mno = 0, vertcount = 0; mno < shaderstate.nummeshes; mno++)\r\n\t\t\t{\r\n\t\t\t\tvec2_t *dest = (vec2_t*)((char*)map+vertcount*sizeof(vec2_t));\r\n\t\t\t\tm = shaderstate.meshlist[mno];\r\n\t\t\t\tmemcpy(dest, m->st_array, m->numvertexes*sizeof(vec2_t));\r\n\t\t\t\tvertcount += m->numvertexes;\r\n\t\t\t}\r\n\t\t\td3dcheck(IDirect3DVertexBuffer8_Unlock(shaderstate.dynst_buff[0]));\r\n\t\t\td3dcheck(IDirect3DDevice8_SetStreamSource(pD3DDev8, STRM_TC0, shaderstate.dynst_buff[0], shaderstate.dynst_offs[0] - vertcount*sizeof(vec2_t), sizeof(vec2_t)));\r\n\t\t}\r\n\t}\r\n\t/*lm coords*/\r\n\tif (vdec & D3D_VDEC_ST1)\r\n\t{\r\n\t\tif (shaderstate.batchvbo)\r\n\t\t\td3dcheck(IDirect3DDevice8_SetStreamSource(pD3DDev8, STRM_TC1, shaderstate.batchvbo->lmcoord[0].d3d.buff, shaderstate.batchvbo->lmcoord[0].d3d.offs, sizeof(vbovdata_t)));\r\n\t\telse\r\n\t\t{\r\n\t\t\tint mno;\r\n\t\t\tvoid *map;\r\n\t\t\tmesh_t *m;\r\n\t\t\tallocvertexbuffer(shaderstate.dynst_buff[1], shaderstate.dynst_size, &shaderstate.dynst_offs[1], &map, vertcount*sizeof(vec2_t));\r\n\t\t\tfor (mno = 0, vertcount = 0; mno < shaderstate.nummeshes; mno++)\r\n\t\t\t{\r\n\t\t\t\tvec2_t *dest = (vec2_t*)((char*)map+vertcount*sizeof(vec2_t));\r\n\t\t\t\tm = shaderstate.meshlist[mno];\r\n\t\t\t\tmemcpy(dest, m->lmst_array, m->numvertexes*sizeof(vec2_t));\r\n\t\t\t\tvertcount += m->numvertexes;\r\n\t\t\t}\r\n\t\t\td3dcheck(IDirect3DVertexBuffer8_Unlock(shaderstate.dynst_buff[1]));\r\n\t\t\td3dcheck(IDirect3DDevice8_SetStreamSource(pD3DDev8, STRM_TC1, shaderstate.dynst_buff[1], shaderstate.dynst_offs[1] - vertcount*sizeof(vec2_t), sizeof(vec2_t)));\r\n\t\t}\r\n\t}\r\n\r\n\t/*normals/tangents/bitangents*/\r\n\tif (vdec & D3D_VDEC_NORM)\r\n\t{\r\n\t\tif (shaderstate.batchvbo)\r\n\t\t{\r\n\t\t\tif (shaderstate.batchvbo && !shaderstate.batchvbo->vaodynamic)\r\n\t\t\t{\r\n\t\t\t\td3dcheck(IDirect3DDevice8_SetStreamSource(pD3DDev8, STRM_NORM, shaderstate.batchvbo->normals.d3d.buff, shaderstate.batchvbo->normals.d3d.offs, sizeof(vbovdata_t)));\r\n\t\t\t\td3dcheck(IDirect3DDevice8_SetStreamSource(pD3DDev8, STRM_NORMS, shaderstate.batchvbo->svector.d3d.buff, shaderstate.batchvbo->svector.d3d.offs, sizeof(vbovdata_t)));\r\n\t\t\t\td3dcheck(IDirect3DDevice8_SetStreamSource(pD3DDev8, STRM_NORMT, shaderstate.batchvbo->tvector.d3d.buff, shaderstate.batchvbo->tvector.d3d.offs, sizeof(vbovdata_t)));\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\td3dcheck(IDirect3DDevice8_SetStreamSource(pD3DDev8, STRM_NORM, shaderstate.batchvbo->normals.d3d.buff, shaderstate.batchvbo->normals.d3d.offs, sizeof(vec3_t)));\r\n\t\t\t\td3dcheck(IDirect3DDevice8_SetStreamSource(pD3DDev8, STRM_NORMS, shaderstate.batchvbo->svector.d3d.buff, shaderstate.batchvbo->svector.d3d.offs, sizeof(vec3_t)));\r\n\t\t\t\td3dcheck(IDirect3DDevice8_SetStreamSource(pD3DDev8, STRM_NORMT, shaderstate.batchvbo->tvector.d3d.buff, shaderstate.batchvbo->tvector.d3d.offs, sizeof(vec3_t)));\r\n\t\t\t}\r\n\t\t}\r\n\t\telse if (shaderstate.meshlist[0]->normals_array && shaderstate.meshlist[0]->snormals_array && shaderstate.meshlist[0]->tnormals_array)\r\n\t\t{\r\n\t\t\tint mno;\r\n\t\t\tvoid *map;\r\n\t\t\tmesh_t *m;\r\n\t\t\tint tv = vertcount;\r\n\r\n\t\t\tallocvertexbuffer(shaderstate.dynnorm_buff, shaderstate.dynnorm_size, &shaderstate.dynnorm_offs, &map, vertcount*3*sizeof(vec3_t));\r\n\t\t\tfor (mno = 0, vertcount = 0; mno < shaderstate.nummeshes; mno++)\r\n\t\t\t{\r\n\t\t\t\tfloat *dest;\r\n\t\t\t\tm = shaderstate.meshlist[mno];\r\n\r\n\t\t\t\tdest = (float*)((char*)map+vertcount*sizeof(vec3_t));\r\n\t\t\t\tmemcpy(dest, m->normals_array, m->numvertexes*sizeof(vec3_t));\r\n\r\n\t\t\t\tdest += tv*3;\r\n\t\t\t\tmemcpy(dest, m->snormals_array, m->numvertexes*sizeof(vec3_t));\r\n\r\n\t\t\t\tdest += tv*3;\r\n\t\t\t\tmemcpy(dest, m->tnormals_array, m->numvertexes*sizeof(vec3_t));\r\n\r\n\t\t\t\tvertcount += m->numvertexes;\r\n\t\t\t}\r\n\t\t\td3dcheck(IDirect3DVertexBuffer8_Unlock(shaderstate.dynnorm_buff));\r\n\t\t\td3dcheck(IDirect3DDevice8_SetStreamSource(pD3DDev8, STRM_NORM, shaderstate.dynnorm_buff, shaderstate.dynnorm_offs - vertcount*sizeof(vec3_t)*3, sizeof(vec3_t)));\r\n\t\t\td3dcheck(IDirect3DDevice8_SetStreamSource(pD3DDev8, STRM_NORMS, shaderstate.dynnorm_buff, shaderstate.dynnorm_offs - vertcount*sizeof(vec3_t)*2, sizeof(vec3_t)));\r\n\t\t\td3dcheck(IDirect3DDevice8_SetStreamSource(pD3DDev8, STRM_NORMT, shaderstate.dynnorm_buff, shaderstate.dynnorm_offs - vertcount*sizeof(vec3_t)*1, sizeof(vec3_t)));\r\n\t\t}\r\n\t\telse\r\n\t\t\tvdec &= ~D3D_VDEC_NORM;\r\n\t}\r\n\r\n\t/*bone weights+indexes*/\r\n\tif (vdec & D3D_VDEC_SKEL)\r\n\t{\r\n\t\t/*FIXME*/\r\n\t\tvdec &= ~D3D_VDEC_SKEL;\r\n\t}\r\n\r\n\tif (vdec != shaderstate.curvertdecl)\r\n\t{\r\n\t\tshaderstate.curvertdecl = vdec;\r\n\t\td3dcheck(IDirect3DDevice8_SetVertexDeclaration(pD3DDev8, vertexdecls[shaderstate.curvertdecl]));\r\n\t}\r\n\r\n//\tIDirect3DDevice8_SetVertexShaderConstantF(pD3DDev8,\r\n\tBE_SubmitMeshChain(vertbase, vertfirst, vertcount, idxfirst, idxcount);\r\n\r\n\tIDirect3DDevice8_SetVertexShader(pD3DDev8, NULL);\r\n\tIDirect3DDevice8_SetPixelShader(pD3DDev8, NULL);\r\n}\r\n#endif\r\n\r\nvoid D3D8BE_Cull(unsigned int cullflags)\r\n{\r\n\tif (shaderstate.flags & BEF_FORCETWOSIDED)\r\n\t\tcullflags = 0;\r\n\telse if (cullflags)\r\n\t\tcullflags ^= r_refdef.flipcull;\r\n\r\n\tif (shaderstate.curcull != cullflags)\r\n\t{\r\n\t\tshaderstate.curcull = cullflags;\r\n\t\tif (shaderstate.curcull & 1)\r\n\t\t{\r\n\t\t\tif (shaderstate.curcull & SHADER_CULL_FRONT)\r\n\t\t\t\tIDirect3DDevice8_SetRenderState(pD3DDev8, D3DRS_CULLMODE, D3DCULL_CW);\r\n\t\t\telse if (shaderstate.curcull & SHADER_CULL_BACK)\r\n\t\t\t\tIDirect3DDevice8_SetRenderState(pD3DDev8, D3DRS_CULLMODE, D3DCULL_CCW);\r\n\t\t\telse\r\n\t\t\t\tIDirect3DDevice8_SetRenderState(pD3DDev8, D3DRS_CULLMODE, D3DCULL_NONE);\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tif (shaderstate.curcull & SHADER_CULL_FRONT)\r\n\t\t\t\tIDirect3DDevice8_SetRenderState(pD3DDev8, D3DRS_CULLMODE, D3DCULL_CCW);\r\n\t\t\telse if (shaderstate.curcull & SHADER_CULL_BACK)\r\n\t\t\t\tIDirect3DDevice8_SetRenderState(pD3DDev8, D3DRS_CULLMODE, D3DCULL_CW);\r\n\t\t\telse\r\n\t\t\t\tIDirect3DDevice8_SetRenderState(pD3DDev8, D3DRS_CULLMODE, D3DCULL_NONE);\r\n\t\t}\r\n\t}\r\n}\r\n\r\nstatic void BE_DrawMeshChain_Internal(void)\r\n{\r\n\tunsigned int vertfirst, vertcount, idxcount, idxfirst;\r\n\tmesh_t *m;\r\n\tvoid *map;\r\n\tint i;\r\n\tunsigned int mno;\r\n\tunsigned int passno = 0;\r\n\tshaderpass_t *pass;\r\n\tshader_t *useshader = shaderstate.curshader;\r\n\tfloat pushdepth = shaderstate.curshader->polyoffset.factor;\r\n//\tfloat pushfactor;\r\n\r\n#ifdef BEF_PUSHDEPTH\r\n\tif (shaderstate.flags & BEF_PUSHDEPTH)\r\n\t{\r\n\t\textern cvar_t r_polygonoffset_submodel_factor;\r\n\t\tpushdepth += r_polygonoffset_submodel_factor.value;\r\n\t}\r\n#endif\r\n\tpushdepth /= 0xffff;\r\n\r\n\tD3D8BE_Cull(shaderstate.curshader->flags & (SHADER_CULL_FRONT | SHADER_CULL_BACK));\r\n\r\n#ifdef FIXME\r\n\tif (pushdepth != shaderstate.depthbias)\r\n\t{\r\n\t\tshaderstate.depthbias = pushdepth;\r\n\t\tIDirect3DDevice8_SetRenderState(pD3DDev8, D3DRS_DEPTHBIAS, *(DWORD*)&shaderstate.depthbias);\r\n\t}\r\n//\tpushdepth = shaderstate.curshader->polyoffset.unit/-1;// + ((shaderstate.flags & BEF_PUSHDEPTH)?8:0);\r\n//\tpushfactor = shaderstate.curshader->polyoffset.factor/-1;\r\n//\tif (pushfactor != shaderstate.depthfactor)\r\n//\t{\r\n//\t\tshaderstate.depthfactor = pushfactor;\r\n//\t\tIDirect3DDevice8_SetRenderState(pD3DDev8, D3DRS_SLOPESCALEDEPTHBIAS, *(DWORD*)&shaderstate.depthfactor);\r\n//\t}\r\n#endif\r\n\r\n\tswitch (shaderstate.mode)\r\n\t{\r\n\tcase BEM_LIGHT:\r\n//\t\tuseshader = shaderstate.curshader->bemoverrides[shaderstate.lightmode];\r\n//\t\tif (!useshader)\r\n\t\t\tuseshader = shaderstate.shader_rtlight;\r\n\t\tif (!useshader->prog)\r\n\t\t\treturn;\r\n\t\tbreak;\r\n\tcase BEM_DEPTHDARK:\r\n//\t\tuseshader = shaderstate.shader_depthblack;\r\n\t\treturn;\r\n\t\tbreak;\r\n\tcase BEM_STENCIL:\r\n\t\treturn;\r\n\tcase BEM_DEPTHONLY:\r\n//\t\tuseshader = shaderstate.shader_depthonly;\r\n\t\treturn;\r\n\t\tbreak;\r\n\tdefault:\r\n\t\tuseshader = shaderstate.curshader;\r\n\t\tbreak;\t//no need to switch the shader\r\n\t}\r\n\r\n\t//if anything is dynamic ALL must be dynamic\r\n\t//might want to flag this for multi-mesh batches on pre-t&l cards too, so that there's no gaps.\r\n//\tif ((useshader->flags & SHADER_NEEDSARRAYS) && shaderstate.nummeshes > 0)\r\n\t\tshaderstate.batchvbo = NULL;\r\n\r\n\tif (shaderstate.batchvbo)\r\n\t{\r\n\t\tvertfirst = 0;\r\n\t\tvertcount = shaderstate.batchvbo->vertcount;\r\n\t\tidxfirst = 0;\r\n\t\tidxcount = shaderstate.batchvbo->indexcount;\r\n\t}\r\n\telse\r\n\t{\r\n\t\tvertfirst = 0;\r\n\t\tfor (mno = 0, vertcount = 0, idxcount = 0; mno < shaderstate.nummeshes; mno++)\r\n\t\t{\r\n\t\t\tm = shaderstate.meshlist[mno];\r\n\t\t\tvertcount += m->numvertexes;\r\n\t\t\tidxcount += m->numindexes;\r\n\t\t}\r\n\t}\r\n\r\n\t/*vertex buffers are common to all passes*/\r\n/*\tif (shaderstate.batchvbo && !shaderstate.batchvbo->vaodynamic)\r\n\t{\r\n\t\td3dcheck(IDirect3DDevice8_SetStreamSource(pD3DDev8, STRM_VERT, shaderstate.batchvbo->coord.d3d.buff, sizeof(vbovdata_t)));\r\n\t\tvertfirst = 0;\r\n\t}\r\n\telse if (shaderstate.batchvbo)\r\n\t{\r\n\t\td3dcheck(IDirect3DDevice8_SetStreamSource(pD3DDev8, STRM_VERT, shaderstate.batchvbo->coord.d3d.buff, shaderstate.batchvbo->coord.d3d.offs, sizeof(vecV_t)));\r\n\t\tvertfirst = 0;\r\n\t}\r\n\telse\r\n\t{\r\n\t\tvertfirst = 0;\r\n\t\tallocvertexbuffer(shaderstate.dynxyz_buff, shaderstate.dynxyz_size, &shaderstate.dynxyz_offs, &map, vertcount*sizeof(vecV_t));\r\n\t\tfor (mno = 0, vertcount = 0; mno < shaderstate.nummeshes; mno++)\r\n\t\t{\r\n\t\t\tvecV_t *dest = (vecV_t*)((char*)map+vertcount*sizeof(vecV_t));\r\n\t\t\tm = shaderstate.meshlist[mno];\r\n\t\t\tdeformgen(&shaderstate.curshader->deforms[0], m->numvertexes, m->xyz_array, dest, m);\r\n\t\t\tfor (i = 1; i < shaderstate.curshader->numdeforms; i++)\r\n\t\t\t{\r\n\t\t\t\tdeformgen(&shaderstate.curshader->deforms[i], m->numvertexes, dest, dest, m);\r\n\t\t\t}\r\n\t\t\tvertcount += m->numvertexes;\r\n\t\t}\r\n\t\td3dcheck(IDirect3DVertexBuffer8_Unlock(shaderstate.dynxyz_buff));\r\n\t\td3dcheck(IDirect3DDevice8_SetStreamSource(pD3DDev8, STRM_VERT, shaderstate.dynxyz_buff, shaderstate.dynxyz_offs - vertcount*sizeof(vecV_t), sizeof(vecV_t)));\r\n\t\td3dcheck(IDirect3DDevice8_SetStreamSource(pD3DDev8, STRM_VERT2, NULL, 0, sizeof(vecV_t)));\r\n\t}\r\n*/\r\n\t/*index buffers are also common (note that we may still need to stream these when dealing with bsp geometry, to cope with gaps. this is faster than using multiple draw calls.)*/\r\n\tif (shaderstate.batchvbo)\r\n\t{\r\n\t\tif (shaderstate.nummeshes != 1 || (useshader->flags & SHADER_NEEDSARRAYS))\r\n\t\t{\t//in this case, the vertex data is static, but the index data can have gaps.\r\n\t\t\t//we're streaming index buffer data only so that we can avoid repeated draw calls. if this stuff was properly built in the first place we wouldn't need to do this. :s\r\n\t\t\tidxcount = 0;\r\n\t\t\tfor (mno = 0; mno < shaderstate.nummeshes; mno++)\r\n\t\t\t{\r\n\t\t\t\tm = shaderstate.meshlist[mno];\r\n\t\t\t\tidxcount += m->numindexes;\r\n\t\t\t}\r\n\t\t\tidxfirst = allocindexbuffer(&map, idxcount);\r\n\t\t\tfor (mno = 0; mno < shaderstate.nummeshes; mno++)\r\n\t\t\t{\r\n\t\t\t\tm = shaderstate.meshlist[mno];\r\n\t\t\t\tfor (i = 0; i < m->numindexes; i++)\r\n\t\t\t\t\t((index_t*)map)[i] = m->vbofirstvert + m->indexes[i];\r\n\t\t\t\tmap = (char*)map + m->numindexes*sizeof(index_t);\r\n\t\t\t}\r\n\t\t\td3dcheck(IDirect3DIndexBuffer8_Unlock(shaderstate.dynidx_buff));\r\n\t\t\td3dcheck(IDirect3DDevice8_SetIndices(pD3DDev8, shaderstate.dynidx_buff, idxfirst));\r\n\r\n\t\t\t//we could constrain vertfirst+vertcount, but I suspect those only matter on pre t&l cards, of which there are very few.\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tm = shaderstate.meshlist[0];\r\n\t\t\tidxfirst = m->vbofirstelement;\r\n\t\t\tidxcount = m->numindexes;\r\n\t\t\tvertfirst = m->vbofirstvert;\r\n\t\t\tvertcount = m->numvertexes;\r\n\r\n\t\t\td3dcheck(IDirect3DDevice8_SetIndices(pD3DDev8, shaderstate.batchvbo->indicies.d3d.buff, 0));\r\n\t\t}\r\n\t}\r\n\telse\r\n\t{\r\n\t\tidxfirst = allocindexbuffer(&map, idxcount);\r\n\t\tfor (mno = 0, vertcount = 0; mno < shaderstate.nummeshes; mno++)\r\n\t\t{\r\n\t\t\tm = shaderstate.meshlist[mno];\r\n\t\t\tfor (i = 0; i < m->numindexes; i++)\r\n\t\t\t\t((index_t*)map)[i] = m->indexes[i]+vertcount;\r\n\t\t\tmap = (char*)map + m->numindexes*sizeof(index_t);\r\n\t\t\tvertcount += m->numvertexes;\r\n\t\t}\r\n\t\td3dcheck(IDirect3DIndexBuffer8_Unlock(shaderstate.dynidx_buff));\r\n\t}\r\n\r\n\tswitch (shaderstate.mode)\r\n\t{\r\n#if 0\r\n\tcase BEM_LIGHT:\r\n\t\tif (shaderstate.shader_rtlight->prog)\r\n\t\t\tBE_RenderMeshProgram(shaderstate.shader_rtlight, vertbase, vertfirst, vertcount, idxfirst, idxcount);\r\n\t\tbreak;\r\n\tcase BEM_DEPTHDARK:\r\n\t\tshaderstate.lastpasscount = 0;\r\n\t\ti = 0;\r\n\t\tif (i != shaderstate.curvertdecl)\r\n\t\t{\r\n\t\t\tshaderstate.curvertdecl = i;\r\n\t\t\td3dcheck(IDirect3DDevice8_SetVertexDeclaration(pD3DDev8, vertexdecls[shaderstate.curvertdecl]));\r\n\t\t}\r\n\t\t/*deactivate any extras*/\r\n\t\tfor (passno = 1; passno < shaderstate.lastpasscount; )\r\n\t\t{\r\n\t\t\td3dcheck(IDirect3DDevice8_SetStreamSource(pD3DDev8, STRM_TC0+passno, NULL, 0, 0));\r\n\t\t\tBindTexture(passno, NULL);\r\n\t\t\td3dcheck(IDirect3DDevice8_SetTextureStageState(pD3DDev8, passno, D3DTSS_COLOROP, D3DTOP_DISABLE));\r\n\t\t\td3dcheck(IDirect3DDevice8_SetTextureStageState(pD3DDev8, passno, D3DTSS_ALPHAOP, D3DTOP_DISABLE));\r\n\t\t\tpassno++;\r\n\t\t}\r\n\t\tBindTexture(passno, NULL);\r\n\t\td3dcheck(IDirect3DDevice8_SetTextureStageState(pD3DDev8, 0, D3DTSS_COLOROP, D3DTOP_SELECTARG1));\r\n\t\td3dcheck(IDirect3DDevice8_SetTextureStageState(pD3DDev8, 0, D3DTSS_COLORARG1, D3DTA_CONSTANT));\r\n\t\td3dcheck(IDirect3DDevice8_SetTextureStageState(pD3DDev8, 0, D3DTSS_CONSTANT, D3DCOLOR_RGBA(0,0,0,255)));\r\n\t\td3dcheck(IDirect3DDevice8_SetTextureStageState(pD3DDev8, 0, D3DTSS_ALPHAOP, D3DTOP_DISABLE));\r\n\t\tshaderstate.lastpasscount = 1;\r\n\t\tBE_ApplyShaderBits(SBITS_MISC_DEPTHWRITE);\r\n\r\n\t\tBE_SubmitMeshChain(vertbase, vertfirst, vertcount, idxfirst, idxcount);\r\n\t\tbreak;\r\n\tcase BEM_STENCIL:\r\n\t\tbreak;\r\n\tcase BEM_DEPTHONLY:\r\n\t\tshaderstate.lastpasscount = 0;\r\n\t\ti = 0;\r\n\t\tif (i != shaderstate.curvertdecl)\r\n\t\t{\r\n\t\t\tshaderstate.curvertdecl = i;\r\n\t\t\td3dcheck(IDirect3DDevice8_SetVertexDeclaration(pD3DDev8, vertexdecls[shaderstate.curvertdecl]));\r\n\t\t}\r\n\t\tIDirect3DDevice8_SetRenderState(pD3DDev8, D3DRS_COLORWRITEENABLE, 0);\r\n\t\t/*deactivate any extras*/\r\n\t\tfor (passno = 0; passno < shaderstate.lastpasscount; )\r\n\t\t{\r\n\t\t\td3dcheck(IDirect3DDevice8_SetStreamSource(pD3DDev8, STRM_TC0+passno, NULL, 0, 0));\r\n\t\t\tBindTexture(passno, NULL);\r\n\t\t\td3dcheck(IDirect3DDevice8_SetTextureStageState(pD3DDev8, passno, D3DTSS_COLOROP, D3DTOP_DISABLE));\r\n\t\t\td3dcheck(IDirect3DDevice8_SetTextureStageState(pD3DDev8, passno, D3DTSS_ALPHAOP, D3DTOP_DISABLE));\r\n\t\t\tpassno++;\r\n\t\t}\r\n\t\tshaderstate.lastpasscount = 0;\r\n\t\tBE_ApplyShaderBits(SBITS_MISC_DEPTHWRITE);\r\n\t\tBE_SubmitMeshChain(vertbase, vertfirst, vertcount, idxfirst, idxcount);\r\n\t\tIDirect3DDevice8_SetRenderState(pD3DDev8, D3DRS_COLORWRITEENABLE, D3DCOLORWRITEENABLE_RED|D3DCOLORWRITEENABLE_GREEN|D3DCOLORWRITEENABLE_BLUE|D3DCOLORWRITEENABLE_ALPHA);\r\n\t\tbreak;\r\n#endif\r\n\tdefault:\r\n\t\tbreak;\r\n\tcase BEM_STANDARD:\r\n#ifdef FIXME\r\n\t\tif (useshader->prog)\r\n\t\t{\r\n\t\t\tBE_RenderMeshProgram(useshader, vertfirst, vertcount, idxfirst, idxcount);\r\n\t\t}\r\n\t\telse\r\n#endif\r\n\t\t{\r\n\t\t\t/*now go through and flush each pass*/\r\n\t\t\tfor (passno = 0, pass = useshader->passes; passno < shaderstate.curshader->numpasses; passno += pass->numMergedPasses)\r\n\t\t\t{\r\n\t\t\t\tif (!BE_DrawMeshChain_SetupPass(pass+passno, vertcount, &vertfirst))\r\n\t\t\t\t\tcontinue;\r\n\t\t#ifdef BENCH\r\n\t\t\t\tshaderstate.bench.draws++;\r\n\t\t\t\tif (shaderstate.bench.clamp && shaderstate.bench.clamp < shaderstate.bench.draws)\r\n\t\t\t\t\tcontinue;\r\n\t\t#endif\r\n\t\t\t\tBE_SubmitMeshChain(vertfirst, vertcount, idxfirst, idxcount);\r\n//\t\t\t\td3dcheck(IDirect3DDevice8_DrawIndexedPrimitive(pD3DDev8, D3DPT_TRIANGLELIST, 0, 0, vertcount, idxfirst, idxcount/3));\r\n\t\t\t}\r\n\t\t}\r\n\t\tbreak;\r\n\t}\r\n}\r\n\r\nvoid D3D8BE_SelectMode(backendmode_t mode)\r\n{\r\n\tshaderstate.mode = mode;\r\n\r\n\tif (mode == BEM_STENCIL)\r\n\t\tBE_ApplyShaderBits(SBITS_MASK_BITS);\r\n}\r\n\r\nqboolean D3D8BE_SelectDLight(dlight_t *dl, vec3_t colour, vec3_t axis[3], unsigned int lmode)\r\n{\r\n\tshaderstate.curdlight = dl;\r\n\tVectorCopy(colour, shaderstate.curdlight_colours);\r\n\r\n\tif (lmode != LSHADER_STANDARD)\r\n\t\treturn false;\r\n\treturn true;\r\n}\r\n\r\nvoid D3D8BE_SelectEntity(entity_t *ent)\r\n{\r\n\tshaderstate.curentity = ent;\r\n\tBE_RotateForEntity(ent, ent->model);\r\n}\r\n\r\n#if 1\r\nstatic void D3D8BE_GenBatchVBOs(vbo_t **vbochain, batch_t *firstbatch, batch_t *stopbatch)\r\n{\r\n\tint maxvboelements;\r\n\tint maxvboverts;\r\n\tint vert = 0, idx = 0;\r\n\tbatch_t *batch;\r\n\tvbo_t *vbo;\r\n\tint i, j;\r\n\tmesh_t *m;\r\n\tIDirect3DVertexBuffer8 *vbuff;\r\n\tIDirect3DIndexBuffer8 *ebuff;\r\n\tindex_t *vboedata;\r\n\tvbovdata_t *vbovdata;\r\n\r\n\tvbo = Z_Malloc(sizeof(*vbo));\r\n\r\n\tmaxvboverts = 0;\r\n\tmaxvboelements = 0;\r\n\tfor(batch = firstbatch; batch != stopbatch; batch = batch->next)\r\n\t{\r\n\t\tfor (i=0 ; i<batch->maxmeshes ; i++)\r\n\t\t{\r\n\t\t\tm = batch->mesh[i];\r\n\t\t\tmaxvboelements += m->numindexes;\r\n\t\t\tmaxvboverts += m->numvertexes;\r\n\t\t}\r\n\t}\r\n\r\n\tIDirect3DDevice8_CreateIndexBuffer(pD3DDev8, sizeof(index_t) * maxvboelements, 0,\t\t\t\t\tD3DFMT_QINDEX, D3DPOOL_MANAGED, &ebuff);\r\n\tIDirect3DDevice8_CreateVertexBuffer(pD3DDev8, sizeof(*vbovdata) * maxvboverts, D3DUSAGE_WRITEONLY,\tD3DFVF_QVBO, D3DPOOL_MANAGED, &vbuff);\r\n\r\n\tvbovdata = NULL;\r\n\tvbo->vaodynamic = 0;\r\n\tvbo->coord.d3d.buff = vbuff;\r\n\tvbo->coord.d3d.offs = (quintptr_t)&vbovdata->coord;\r\n\tvbo->indicies.d3d.buff = ebuff;\r\n\tvbo->indicies.d3d.offs = 0;\r\n\r\n\tIDirect3DIndexBuffer8_Lock(ebuff, 0, sizeof(index_t) * maxvboelements, (BYTE**)&vboedata, D3DLOCK_DISCARD);\r\n\tIDirect3DVertexBuffer8_Lock(vbuff, 0, sizeof(*vbovdata) * maxvboverts, (BYTE**)&vbovdata, D3DLOCK_DISCARD);\r\n\r\n\tfor(batch = firstbatch; batch != stopbatch; batch = batch->next)\r\n\t{\r\n\t\tbatch->vbo = vbo;\r\n\t\tfor (j=0 ; j<batch->maxmeshes ; j++)\r\n\t\t{\r\n\t\t\tm = batch->mesh[j];\r\n\t\t\tm->vbofirstvert = vert;\r\n\t\t\tfor (i = 0; i < m->numvertexes; i++)\r\n\t\t\t{\r\n\t\t\t\tVectorCopy(m->xyz_array[i],\t\t\tvbovdata->coord);\r\n\t\t\t\t//vbovdata->coord[3] = 1;\r\n\t\t\t\tVector2Copy(m->st_array[i],\t\t\tvbovdata->tc[0]);\r\n\t\t\t\tVector2Copy(m->lmst_array[0][i],\t\tvbovdata->tc[1]);\r\n\t\t\t\tVector4Scale(m->colors4f_array[0][i],\t255, vbovdata->colorsb);\r\n\r\n\t\t\t\tvbovdata++;\r\n\t\t\t}\r\n\r\n\t\t\tm->vbofirstelement = idx;\r\n\t\t\tfor (i = 0; i < m->numindexes; i++)\r\n\t\t\t{\r\n\t\t\t\t*vboedata++ = vert + m->indexes[i];\r\n\t\t\t}\r\n\t\t\tidx += m->numindexes;\r\n\t\t\tvert += m->numvertexes;\r\n\t\t}\r\n\t}\r\n\r\n\tvbo->indexcount = idx;\r\n\tvbo->vertcount = vert;\r\n\r\n\tIDirect3DIndexBuffer8_Unlock(ebuff);\r\n\tIDirect3DVertexBuffer8_Unlock(vbuff);\r\n\r\n\tvbo->next = *vbochain;\r\n\t*vbochain = vbo;\r\n}\r\n\r\nvoid D3D8BE_GenBrushModelVBO(model_t *mod)\r\n{\r\n\tunsigned int vcount;\r\n\r\n\r\n\tbatch_t *batch, *fbatch;\r\n\tint sortid;\r\n\tint i;\r\n\r\n\tfbatch = NULL;\r\n\tvcount = 0;\r\n\tfor (sortid = 0; sortid < SHADER_SORT_COUNT; sortid++)\r\n\t{\r\n\t\tif (!mod->batches[sortid])\r\n\t\t\tcontinue;\r\n\r\n\t\tfor (fbatch = batch = mod->batches[sortid]; batch != NULL; batch = batch->next)\r\n\t\t{\r\n\t\t\t//firstmesh got reused as the number of verticies in each batch\r\n\t\t\tif (vcount + batch->firstmesh > MAX_INDICIES)\r\n\t\t\t{\r\n\t\t\t\tD3D8BE_GenBatchVBOs(&mod->vbos, fbatch, batch);\r\n\t\t\t\tfbatch = batch;\r\n\t\t\t\tvcount = 0;\r\n\t\t\t}\r\n\r\n\t\t\tfor (i = 0; i < batch->maxmeshes; i++)\r\n\t\t\t\tvcount += batch->mesh[i]->numvertexes;\r\n\t\t}\r\n\r\n\t\tD3D8BE_GenBatchVBOs(&mod->vbos, fbatch, batch);\r\n\t}\r\n}\r\n#else\r\n/*Generates an optimised vbo for each of the given model's textures*/\r\nvoid D3D8BE_GenBrushModelVBO(model_t *mod)\r\n{\r\n#if 1\r\n\tunsigned int maxvboverts;\r\n\tunsigned int maxvboelements;\r\n\r\n\tunsigned int t;\r\n\tunsigned int i;\r\n\tunsigned int v;\r\n\tunsigned int vcount, ecount;\r\n\tunsigned int pervertsize;\t//erm, that name wasn't intentional\r\n\tunsigned int meshes;\r\n\r\n\tvbo_t *vbo;\r\n\tindex_t *vboedata;\r\n\tmesh_t *m;\r\n\tchar *vbovdata;\r\n\tIDirect3DVertexBuffer8 *vbuff;\r\n\tIDirect3DIndexBuffer8 *ebuff;\r\n\r\n\tvecV_t *coord;\r\n\tvec2_t *texcoord;\r\n\tvec2_t *lmcoord;\r\n\tvec3_t *normals;\r\n\tvec3_t *svector;\r\n\tvec3_t *tvector;\r\n\tbyte_vec4_t *colours;\r\n\r\n\tif (!mod->numsurfaces)\r\n\t\treturn;\r\n\r\n\tfor (t = 0; t < mod->numtextures; t++)\r\n\t{\r\n\t\tif (!mod->textures[t])\r\n\t\t\tcontinue;\r\n\t\tvbo = &mod->textures[t]->vbo;\r\n\t\tBE_ClearVBO(vbo);\r\n\r\n\t\tmaxvboverts = 0;\r\n\t\tmaxvboelements = 0;\r\n\t\tmeshes = 0;\r\n\t\tfor (i=0 ; i<mod->numsurfaces ; i++)\r\n\t\t{\r\n\t\t\tif (mod->surfaces[i].texinfo->texture != mod->textures[t])\r\n\t\t\t\tcontinue;\r\n\t\t\tm = mod->surfaces[i].mesh;\r\n\t\t\tif (!m)\r\n\t\t\t\tcontinue;\r\n\r\n\t\t\tmeshes++;\r\n\t\t\tmaxvboelements += m->numindexes;\r\n\t\t\tmaxvboverts += m->numvertexes;\r\n\t\t}\r\n#if sizeof_index_t == 2\r\n\t\tif (maxvboverts > (1u<<(sizeof(index_t)*8))-1)\r\n\t\t\tcontinue;\r\n#endif\r\n\t\tif (!maxvboverts)\r\n\t\t\tcontinue;\r\n\r\n\t\t//fixme: stop this from leaking!\r\n\t\tvcount = 0;\r\n\t\tecount = 0;\r\n\r\n\t\tpervertsize =\tsizeof(vecV_t)+\t//coord\r\n\t\t\t\t\tsizeof(vec2_t)+\t//tex\r\n\t\t\t\t\tsizeof(vec2_t)+\t//lm\r\n\t\t\t\t\tsizeof(vec3_t)+\t//normal\r\n\t\t\t\t\tsizeof(vec3_t)+\t//sdir\r\n\t\t\t\t\tsizeof(vec3_t)+\t//tdir\r\n\t\t\t\t\tsizeof(vec4_t);\t//colours\r\n\r\n\t\tIDirect3DDevice8_CreateIndexBuffer(pD3DDev8, sizeof(index_t) * maxvboelements, 0, D3DFMT_QINDEX, D3DPOOL_MANAGED, &ebuff, NULL);\r\n\t\tIDirect3DDevice8_CreateVertexBuffer(pD3DDev8, pervertsize * maxvboverts, D3DUSAGE_WRITEONLY, 0, D3DPOOL_MANAGED, &vbuff, NULL);\r\n\r\n\t\tIDirect3DIndexBuffer8_Lock(ebuff, 0, sizeof(index_t) * maxvboelements, &vboedata, D3DLOCK_DISCARD);\r\n\t\tIDirect3DVertexBuffer8_Lock(vbuff, 0, pervertsize * maxvboverts, &vbovdata, D3DLOCK_DISCARD);\r\n\r\n\t\tvbo->vertdata = vbuff;\r\n\t\tvbo->indexcount = maxvboelements;\r\n\t\tvbo->vertcount = maxvboverts;\r\n\r\n\t\tvbo->coord.d3d.buff = vbuff;\r\n\t\tvbo->coord.d3d.offs = 0;\r\n\t\tvbo->texcoord.d3d.buff = vbuff;\r\n\t\tvbo->texcoord.d3d.offs = vbo->coord.d3d.offs+maxvboverts*sizeof(vecV_t);\r\n\t\tvbo->lmcoord.d3d.buff = vbuff;\r\n\t\tvbo->lmcoord.d3d.offs = vbo->texcoord.d3d.offs+maxvboverts*sizeof(vec2_t);\r\n\t\tvbo->normals.d3d.buff = vbuff;\r\n\t\tvbo->normals.d3d.offs = vbo->lmcoord.d3d.offs+maxvboverts*sizeof(vec2_t);\r\n\t\tvbo->svector.d3d.buff = vbuff;\r\n\t\tvbo->svector.d3d.offs = vbo->normals.d3d.offs+maxvboverts*sizeof(vec3_t);\r\n\t\tvbo->tvector.d3d.buff = vbuff;\r\n\t\tvbo->tvector.d3d.offs = vbo->svector.d3d.offs+maxvboverts*sizeof(vec3_t);\r\n\t\tvbo->colours.d3d.buff = vbuff;\r\n\t\tvbo->colours.d3d.offs = vbo->tvector.d3d.offs+maxvboverts*sizeof(vec3_t);\r\n\t\tvbo->indicies.d3d.buff = ebuff;\r\n\t\tvbo->indicies.d3d.offs = 0;\r\n\r\n\t\tcoord = (void*)(vbovdata + vbo->coord.d3d.offs);\r\n\t\ttexcoord = (void*)(vbovdata + vbo->texcoord.d3d.offs);\r\n\t\tlmcoord = (void*)(vbovdata + vbo->lmcoord.d3d.offs);\r\n\t\tnormals = (void*)(vbovdata + vbo->normals.d3d.offs);\r\n\t\tsvector = (void*)(vbovdata + vbo->svector.d3d.offs);\r\n\t\ttvector = (void*)(vbovdata + vbo->tvector.d3d.offs);\r\n\t\tcolours = (void*)(vbovdata + vbo->colours.d3d.offs);\r\n\r\n\t\tvbo->meshcount = meshes;\r\n\t\tvbo->meshlist = BZ_Malloc(meshes*sizeof(*vbo->meshlist));\r\n\r\n\t\tmeshes = 0;\r\n\r\n\t\tfor (i=0 ; i<mod->numsurfaces ; i++)\r\n\t\t{\r\n\t\t\tif (mod->surfaces[i].texinfo->texture != mod->textures[t])\r\n\t\t\t\tcontinue;\r\n\t\t\tm = mod->surfaces[i].mesh;\r\n\t\t\tif (!m)\r\n\t\t\t\tcontinue;\r\n\r\n\t\t\tmod->surfaces[i].mark = &vbo->meshlist[meshes++];\r\n\t\t\t*mod->surfaces[i].mark = NULL;\r\n\r\n\t\t\tm->vbofirstvert = vcount;\r\n\t\t\tm->vbofirstelement = ecount;\r\n\t\t\tfor (v = 0; v < m->numindexes; v++)\r\n\t\t\t\tvboedata[ecount++] = vcount + m->indexes[v];\r\n\t\t\tfor (v = 0; v < m->numvertexes; v++)\r\n\t\t\t{\r\n\t\t\t\tcoord[vcount+v][0] = m->xyz_array[v][0];\r\n\t\t\t\tcoord[vcount+v][1] = m->xyz_array[v][1];\r\n\t\t\t\tcoord[vcount+v][2] = m->xyz_array[v][2];\r\n\t\t\t\tcoord[vcount+v][3] = 1;\r\n\t\t\t\tif (m->st_array)\r\n\t\t\t\t{\r\n\t\t\t\t\ttexcoord[vcount+v][0] = m->st_array[v][0];\r\n\t\t\t\t\ttexcoord[vcount+v][1] = m->st_array[v][1];\r\n\t\t\t\t}\r\n\t\t\t\tif (m->lmst_array)\r\n\t\t\t\t{\r\n\t\t\t\t\tlmcoord[vcount+v][0] = m->lmst_array[v][0];\r\n\t\t\t\t\tlmcoord[vcount+v][1] = m->lmst_array[v][1];\r\n\t\t\t\t}\r\n\t\t\t\tif (m->normals_array)\r\n\t\t\t\t{\r\n\t\t\t\t\tnormals[vcount+v][0] = m->normals_array[v][0];\r\n\t\t\t\t\tnormals[vcount+v][1] = m->normals_array[v][1];\r\n\t\t\t\t\tnormals[vcount+v][2] = m->normals_array[v][2];\r\n\t\t\t\t}\r\n\t\t\t\tif (m->snormals_array)\r\n\t\t\t\t{\r\n\t\t\t\t\tsvector[vcount+v][0] = m->snormals_array[v][0];\r\n\t\t\t\t\tsvector[vcount+v][1] = m->snormals_array[v][1];\r\n\t\t\t\t\tsvector[vcount+v][2] = m->snormals_array[v][2];\r\n\t\t\t\t}\r\n\t\t\t\tif (m->tnormals_array)\r\n\t\t\t\t{\r\n\t\t\t\t\ttvector[vcount+v][0] = m->tnormals_array[v][0];\r\n\t\t\t\t\ttvector[vcount+v][1] = m->tnormals_array[v][1];\r\n\t\t\t\t\ttvector[vcount+v][2] = m->tnormals_array[v][2];\r\n\t\t\t\t}\r\n\t\t\t\tif (m->colors4b_array)\r\n\t\t\t\t{\r\n\t\t\t\t\tcolours[vcount+v][0] = m->colors4b_array[v][0];\r\n\t\t\t\t\tcolours[vcount+v][1] = m->colors4b_array[v][1];\r\n\t\t\t\t\tcolours[vcount+v][2] = m->colors4b_array[v][2];\r\n\t\t\t\t\tcolours[vcount+v][3] = m->colors4b_array[v][3];\r\n\t\t\t\t}\r\n\t\t\t\tif (m->colors4f_array[0])\r\n\t\t\t\t{\r\n\t\t\t\t\tcolours[vcount+v][0] = m->colors4f_array[0][v][0] * 255;\r\n\t\t\t\t\tcolours[vcount+v][1] = m->colors4f_array[0][v][1] * 255;\r\n\t\t\t\t\tcolours[vcount+v][2] = m->colors4f_array[0][v][2] * 255;\r\n\t\t\t\t\tcolours[vcount+v][3] = m->colors4f_array[0][v][3] * 255;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\tvcount += v;\r\n\t\t}\r\n\r\n\t\tIDirect3DIndexBuffer8_Unlock(ebuff);\r\n\t\tIDirect3DVertexBuffer8_Unlock(vbuff);\r\n\t}\r\n#endif\r\n}\r\n#endif\r\n/*Wipes a vbo*/\r\nvoid D3D8BE_ClearVBO(vbo_t *vbo, qboolean dataonly)\r\n{\r\n\tIDirect3DVertexBuffer8 *vbuff = vbo->coord.d3d.buff;\r\n\tIDirect3DIndexBuffer8 *ebuff = vbo->indicies.d3d.buff;\r\n\tif (vbuff)\r\n\t\tIDirect3DVertexBuffer8_Release(vbuff);\r\n\tif (ebuff)\r\n\t\tIDirect3DIndexBuffer8_Release(ebuff);\r\n\tvbo->coord.d3d.buff = NULL;\r\n\tvbo->indicies.d3d.buff = NULL;\r\n\r\n\tif (!dataonly)\r\n\t\tfree(vbo);\r\n}\r\n\r\n/*upload all lightmaps at the start to reduce lags*/\r\nstatic void BE_UploadLightmaps(qboolean force)\r\n{\r\n\tint i;\r\n\tlightmapinfo_t *lm;\r\n\r\n\tfor (i = 0; i < numlightmaps; i++)\r\n\t{\r\n\t\tlm = lightmap[i];\r\n\t\tif (!lm)\r\n\t\t\tcontinue;\r\n\r\n\t\tif (force)\r\n\t\t{\r\n\t\t\tlm->rectchange.l = 0;\r\n\t\t\tlm->rectchange.t = 0;\r\n\t\t\tlm->rectchange.r = lm->width;\r\n\t\t\tlm->rectchange.b = lm->height;\r\n\t\t}\r\n\r\n\t\tif (lightmap[i]->modified)\r\n\t\t{\r\n\t\t\textern cvar_t r_lightmap_nearest;\r\n\t\t\tIDirect3DTexture8 *tex;\r\n\t\t\tD3DLOCKED_RECT lock;\r\n\t\t\tRECT rect;\r\n\t\t\tglRect_t *theRect = &lm->rectchange;\r\n\t\t\tint r;\r\n\t\t\tint w;\r\n\r\n\t\t\tif (!TEXLOADED(lm->lightmap_texture))\r\n\t\t\t\tlm->lightmap_texture = Image_CreateTexture(\"***lightmap***\", NULL, (r_lightmap_nearest.ival?IF_NEAREST:IF_LINEAR)|IF_NOMIPMAP);\r\n\t\t\ttex = lm->lightmap_texture->ptr;\r\n\t\t\tif (lm->fmt != PTI_BGRA8)\r\n\t\t\t\tcontinue;\t//erk!\r\n\t\t\tif (!tex)\r\n\t\t\t{\r\n\t\t\t\tIDirect3DDevice8_CreateTexture(pD3DDev8, lm->width, lm->height, 1, 0, D3DFMT_A8R8G8B8, D3DPOOL_MANAGED, &tex);\r\n\t\t\t\tif (!tex)\r\n\t\t\t\t\tcontinue;\r\n\t\t\t\tlm->lightmap_texture->ptr = tex;\r\n\t\t\t\tlm->lightmap_texture->status = TEX_LOADED;\r\n\t\t\t}\r\n\t\t\t\r\n\t\t\tlm->modified = 0;\r\n\t\t\trect.left = theRect->l;\r\n\t\t\trect.right = theRect->r;\r\n\t\t\trect.top = theRect->t;\r\n\t\t\trect.bottom = theRect->b;\r\n\r\n\t\t\tIDirect3DTexture8_LockRect(tex, 0, &lock, &rect, 0);\r\n\t\t\tfor (r = 0, w = theRect->r-theRect->l; r < lightmap[i]->rectchange.b-lightmap[i]->rectchange.t; r++)\r\n\t\t\t{\r\n\t\t\t\tmemcpy((char*)lock.pBits + r*lock.Pitch, lightmap[i]->lightmaps+(theRect->l+((r+theRect->t)*lm->width))*4, w*4);\r\n\t\t\t}\r\n\t\t\tIDirect3DTexture8_UnlockRect(tex, 0);\r\n\t\t\ttheRect->l = lm->width;\r\n\t\t\ttheRect->t = lm->height;\r\n\t\t\ttheRect->r = 0;\r\n\t\t\ttheRect->b = 0;\r\n\t\t}\r\n\t}\r\n}\r\n\r\nvoid D3D8BE_UploadAllLightmaps(void)\r\n{\r\n\tBE_UploadLightmaps(true);\r\n}\r\n\r\nqboolean D3D8BE_LightCullModel(vec3_t org, model_t *model)\r\n{\r\n#ifdef RTLIGHTS\r\n\tif ((shaderstate.mode == BEM_LIGHT || shaderstate.mode == BEM_STENCIL))\r\n\t{\r\n\t\t/*true if hidden from current light*/\r\n\t\t/*we have no rtlight support, so mneh*/\r\n\t}\r\n#endif\r\n\treturn false;\r\n}\r\n\r\nbatch_t *D3D8BE_GetTempBatch(void)\r\n{\r\n\tif (shaderstate.wbatch >= shaderstate.maxwbatches)\r\n\t{\r\n\t\tshaderstate.wbatch++;\r\n\t\treturn NULL;\r\n\t}\r\n\treturn &shaderstate.wbatches[shaderstate.wbatch++];\r\n}\r\n\r\nstatic void BE_RotateForEntity (const entity_t *e, const model_t *mod)\r\n{\r\n//\tfloat mv[16];\r\n\tfloat *m = shaderstate.m_model;\r\n\r\n\tshaderstate.curentity = e;\r\n\r\n\tif ((e->flags & RF_WEAPONMODEL) && r_refdef.playerview->viewentity > 0)\r\n\t{\r\n\t\tfloat em[16];\r\n\t\tfloat vm[16];\r\n\r\n\t\tif (e->flags & RF_WEAPONMODELNOBOB)\r\n\t\t{\r\n\t\t\tvm[0] = vpn[0];\r\n\t\t\tvm[1] = vpn[1];\r\n\t\t\tvm[2] = vpn[2];\r\n\t\t\tvm[3] = 0;\r\n\r\n\t\t\tvm[4] = -vright[0];\r\n\t\t\tvm[5] = -vright[1];\r\n\t\t\tvm[6] = -vright[2];\r\n\t\t\tvm[7] = 0;\r\n\r\n\t\t\tvm[8] = vup[0];\r\n\t\t\tvm[9] = vup[1];\r\n\t\t\tvm[10] = vup[2];\r\n\t\t\tvm[11] = 0;\r\n\r\n\t\t\tvm[12] = r_refdef.vieworg[0];\r\n\t\t\tvm[13] = r_refdef.vieworg[1];\r\n\t\t\tvm[14] = r_refdef.vieworg[2];\r\n\t\t\tvm[15] = 1;\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tvm[0] = r_refdef.playerview->vw_axis[0][0];\r\n\t\t\tvm[1] = r_refdef.playerview->vw_axis[0][1];\r\n\t\t\tvm[2] = r_refdef.playerview->vw_axis[0][2];\r\n\t\t\tvm[3] = 0;\r\n\r\n\t\t\tvm[4] = r_refdef.playerview->vw_axis[1][0];\r\n\t\t\tvm[5] = r_refdef.playerview->vw_axis[1][1];\r\n\t\t\tvm[6] = r_refdef.playerview->vw_axis[1][2];\r\n\t\t\tvm[7] = 0;\r\n\r\n\t\t\tvm[8] = r_refdef.playerview->vw_axis[2][0];\r\n\t\t\tvm[9] = r_refdef.playerview->vw_axis[2][1];\r\n\t\t\tvm[10] = r_refdef.playerview->vw_axis[2][2];\r\n\t\t\tvm[11] = 0;\r\n\r\n\t\t\tvm[12] = r_refdef.playerview->vw_origin[0];\r\n\t\t\tvm[13] = r_refdef.playerview->vw_origin[1];\r\n\t\t\tvm[14] = r_refdef.playerview->vw_origin[2];\r\n\t\t\tvm[15] = 1;\r\n\t\t}\r\n\r\n\t\tem[0] = e->axis[0][0];\r\n\t\tem[1] = e->axis[0][1];\r\n\t\tem[2] = e->axis[0][2];\r\n\t\tem[3] = 0;\r\n\r\n\t\tem[4] = e->axis[1][0];\r\n\t\tem[5] = e->axis[1][1];\r\n\t\tem[6] = e->axis[1][2];\r\n\t\tem[7] = 0;\r\n\r\n\t\tem[8] = e->axis[2][0];\r\n\t\tem[9] = e->axis[2][1];\r\n\t\tem[10] = e->axis[2][2];\r\n\t\tem[11] = 0;\r\n\r\n\t\tem[12] = e->origin[0];\r\n\t\tem[13] = e->origin[1];\r\n\t\tem[14] = e->origin[2];\r\n\t\tem[15] = 1;\r\n\r\n\t\tMatrix4_Multiply(vm, em, m);\r\n\t}\r\n\telse\r\n\t{\r\n\t\tm[0] = e->axis[0][0];\r\n\t\tm[1] = e->axis[0][1];\r\n\t\tm[2] = e->axis[0][2];\r\n\t\tm[3] = 0;\r\n\r\n\t\tm[4] = e->axis[1][0];\r\n\t\tm[5] = e->axis[1][1];\r\n\t\tm[6] = e->axis[1][2];\r\n\t\tm[7] = 0;\r\n\r\n\t\tm[8] = e->axis[2][0];\r\n\t\tm[9] = e->axis[2][1];\r\n\t\tm[10] = e->axis[2][2];\r\n\t\tm[11] = 0;\r\n\r\n\t\tm[12] = e->origin[0];\r\n\t\tm[13] = e->origin[1];\r\n\t\tm[14] = e->origin[2];\r\n\t\tm[15] = 1;\r\n\t}\r\n\r\n\tif (e->scale != 1 && e->scale != 0)\t//hexen 2 stuff\r\n\t{\r\n#ifdef HEXEN2\r\n\t\tfloat z;\r\n\t\tfloat escale;\r\n\t\tescale = e->scale;\r\n\t\tswitch(e->drawflags&SCALE_TYPE_MASK)\r\n\t\t{\r\n\t\tdefault:\r\n\t\tcase SCALE_TYPE_UNIFORM:\r\n\t\t\tVectorScale((m+0), escale, (m+0));\r\n\t\t\tVectorScale((m+4), escale, (m+4));\r\n\t\t\tVectorScale((m+8), escale, (m+8));\r\n\t\t\tbreak;\r\n\t\tcase SCALE_TYPE_XYONLY:\r\n\t\t\tVectorScale((m+0), escale, (m+0));\r\n\t\t\tVectorScale((m+4), escale, (m+4));\r\n\t\t\tbreak;\r\n\t\tcase SCALE_TYPE_ZONLY:\r\n\t\t\tVectorScale((m+8), escale, (m+8));\r\n\t\t\tbreak;\r\n\t\t}\r\n\t\tif (mod && (e->drawflags&SCALE_TYPE_MASK) != SCALE_TYPE_XYONLY)\r\n\t\t{\r\n\t\t\tswitch(e->drawflags&SCALE_ORIGIN_MASK)\r\n\t\t\t{\r\n\t\t\tcase SCALE_ORIGIN_CENTER:\r\n\t\t\t\tz = ((mod->maxs[2] + mod->mins[2]) * (1-escale))/2;\r\n\t\t\t\tVectorMA((m+12), z, e->axis[2], (m+12));\r\n\t\t\t\tbreak;\r\n\t\t\tcase SCALE_ORIGIN_BOTTOM:\r\n\t\t\t\tVectorMA((m+12), mod->mins[2]*(1-escale), e->axis[2], (m+12));\r\n\t\t\t\tbreak;\r\n\t\t\tcase SCALE_ORIGIN_TOP:\r\n\t\t\t\tVectorMA((m+12), -mod->maxs[2], e->axis[2], (m+12));\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\t\t}\r\n#else\r\n\t\tVectorScale((m+0), e->scale, (m+0));\r\n\t\tVectorScale((m+4), e->scale, (m+4));\r\n\t\tVectorScale((m+8), e->scale, (m+8));\r\n#endif\r\n\t}\r\n\telse if (mod && !strcmp(mod->name, \"progs/eyes.mdl\"))\r\n\t{\r\n\t\t/*resize eyes, to make them easier to see*/\r\n\t\tm[14] -= (22 + 8);\r\n\t\tVectorScale((m+0), 2, (m+0));\r\n\t\tVectorScale((m+4), 2, (m+4));\r\n\t\tVectorScale((m+8), 2, (m+8));\r\n\t}\r\n\tif (mod && !ruleset_allow_larger_models.ival && mod->clampscale != 1)\r\n\t{\t//possibly this should be on a per-frame basis, but that's a real pain to do\r\n\t\tCon_DPrintf(\"Rescaling %s by %f\\n\", mod->name, mod->clampscale);\r\n\t\tVectorScale((m+0), mod->clampscale, (m+0));\r\n\t\tVectorScale((m+4), mod->clampscale, (m+4));\r\n\t\tVectorScale((m+8), mod->clampscale, (m+8));\r\n\t}\r\n\r\n\tIDirect3DDevice8_SetTransform(pD3DDev8, D3DTS_WORLD, (D3DMATRIX*)m);\r\n\r\n\t{\r\n\tD3DVIEWPORT8 vport;\r\n\tIDirect3DDevice8_GetViewport(pD3DDev8, &vport);\r\n\tvport.MaxZ = (e->flags & RF_DEPTHHACK)?0.333:1;\r\n\tIDirect3DDevice8_SetViewport(pD3DDev8, &vport);\r\n\t}\r\n}\r\n\r\nvoid D3D8BE_SubmitBatch(batch_t *batch)\r\n{\r\n\tshader_t *shader = batch->shader;\r\n\tshaderstate.nummeshes = batch->meshes - batch->firstmesh;\r\n\tif (!shaderstate.nummeshes)\r\n\t\treturn;\r\n\tif (shaderstate.curentity != batch->ent)\r\n\t{\r\n\t\tBE_RotateForEntity(batch->ent, batch->ent->model);\r\n\t\tshaderstate.curtime = r_refdef.time - shaderstate.curentity->shaderTime;\r\n\t}\r\n\tshaderstate.batchvbo = batch->vbo;\r\n\tshaderstate.meshlist = batch->mesh + batch->firstmesh;\r\n\tshaderstate.curshader = shader;\r\n\tif (batch->skin)\r\n\t\tshaderstate.curtexnums = batch->skin;\r\n\telse if (shader->numdefaulttextures)\r\n\t\tshaderstate.curtexnums = shader->defaulttextures + ((int)(shader->defaulttextures_fps * shaderstate.curtime) % shader->numdefaulttextures);\r\n\telse\r\n\t\tshaderstate.curtexnums = shader->defaulttextures;\r\n\tshaderstate.curbatch = batch;\r\n\tshaderstate.flags = batch->flags;\r\n\tif ((unsigned)batch->lightmap[0] < (unsigned)numlightmaps)\r\n\t\tshaderstate.curlightmap = lightmap[batch->lightmap[0]]->lightmap_texture;\r\n\telse\r\n\t\tshaderstate.curlightmap = r_nulltex;\r\n\r\n\tBE_DrawMeshChain_Internal();\r\n}\r\n\r\nvoid D3D8BE_DrawMesh_List(shader_t *shader, int nummeshes, mesh_t **meshlist, vbo_t *vbo, texnums_t *texnums, unsigned int beflags)\r\n{\r\n\tshaderstate.batchvbo = vbo;\r\n\tshaderstate.curshader = shader;\r\n\tif (texnums)\r\n\t\tshaderstate.curtexnums = texnums;\r\n\telse if (shader->numdefaulttextures)\r\n\t\tshaderstate.curtexnums = shader->defaulttextures + ((int)(shader->defaulttextures_fps * shaderstate.curtime) % shader->numdefaulttextures);\r\n\telse\r\n\t\tshaderstate.curtexnums = shader->defaulttextures;\r\n\tshaderstate.curlightmap = r_nulltex;\r\n\tshaderstate.curbatch = &shaderstate.dummybatch;\r\n\tshaderstate.meshlist = meshlist;\r\n\tshaderstate.nummeshes = nummeshes;\r\n\tshaderstate.flags = beflags;\r\n\r\n\tBE_DrawMeshChain_Internal();\r\n}\r\n\r\nvoid D3D8BE_DrawMesh_Single(shader_t *shader, mesh_t *meshchain, vbo_t *vbo, unsigned int beflags)\r\n{\r\n\tshaderstate.batchvbo = vbo;\r\n\tshaderstate.curtime = realtime;\r\n\tshaderstate.curshader = shader;\r\n\tif (shader->numdefaulttextures)\r\n\t\tshaderstate.curtexnums = shader->defaulttextures + ((int)(shader->defaulttextures_fps * shaderstate.curtime) % shader->numdefaulttextures);\r\n\telse\r\n\t\tshaderstate.curtexnums = shader->defaulttextures;\r\n\tshaderstate.curlightmap = r_nulltex;\r\n\tshaderstate.meshlist = &meshchain;\r\n\tshaderstate.nummeshes = 1;\r\n\tshaderstate.flags = beflags;\r\n\r\n\tBE_DrawMeshChain_Internal();\r\n}\r\n\r\nstatic void BE_SubmitMeshesSortList(batch_t *sortlist)\r\n{\r\n\tbatch_t *batch;\r\n\tfor (batch = sortlist; batch; batch = batch->next)\r\n\t{\r\n\t\tif (batch->meshes == batch->firstmesh)\r\n\t\t\tcontinue;\r\n\r\n\t\tif (batch->buildmeshes)\r\n\t\t\tbatch->buildmeshes(batch);\r\n\r\n\t\tif (batch->shader->flags & SHADER_NODLIGHT)\r\n\t\t\tif (shaderstate.mode == BEM_LIGHT)\r\n\t\t\t\tcontinue;\r\n\r\n\t\tif (batch->shader->flags & SHADER_SKY)\r\n\t\t{\r\n\t\t\tif (!batch->shader->prog)\r\n\t\t\t{\r\n\t\t\t\tif (shaderstate.mode == BEM_STANDARD)\r\n\t\t\t\t\tR_DrawSkyChain (batch);\r\n\t\t\t\tcontinue;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tBE_SubmitBatch(batch);\r\n\t}\r\n}\r\n\r\n\r\n/*generates a new modelview matrix, as well as vpn vectors*/\r\nstatic void R_MirrorMatrix(plane_t *plane)\r\n{\r\n\tfloat mirror[16];\r\n\tfloat view[16];\r\n\tfloat result[16];\r\n\r\n\tvec3_t pnorm;\r\n\tVectorNegate(plane->normal, pnorm);\r\n\r\n\tmirror[0] = 1-2*pnorm[0]*pnorm[0];\r\n\tmirror[1] = -2*pnorm[0]*pnorm[1];\r\n\tmirror[2] = -2*pnorm[0]*pnorm[2];\r\n\tmirror[3] = 0;\r\n\r\n\tmirror[4] = -2*pnorm[1]*pnorm[0];\r\n\tmirror[5] = 1-2*pnorm[1]*pnorm[1];\r\n\tmirror[6] = -2*pnorm[1]*pnorm[2] ;\r\n\tmirror[7] = 0;\r\n\r\n\tmirror[8]  = -2*pnorm[2]*pnorm[0];\r\n\tmirror[9]  = -2*pnorm[2]*pnorm[1];\r\n\tmirror[10] = 1-2*pnorm[2]*pnorm[2];\r\n\tmirror[11] = 0;\r\n\r\n\tmirror[12] = -2*pnorm[0]*plane->dist;\r\n\tmirror[13] = -2*pnorm[1]*plane->dist;\r\n\tmirror[14] = -2*pnorm[2]*plane->dist;\r\n\tmirror[15] = 1;\r\n\r\n\tview[0] = vpn[0];\r\n\tview[1] = vpn[1];\r\n\tview[2] = vpn[2];\r\n\tview[3] = 0;\r\n\r\n\tview[4] = -vright[0];\r\n\tview[5] = -vright[1];\r\n\tview[6] = -vright[2];\r\n\tview[7] = 0;\r\n\r\n\tview[8]  = vup[0];\r\n\tview[9]  = vup[1];\r\n\tview[10] = vup[2];\r\n\tview[11] = 0;\r\n\r\n\tview[12] = r_refdef.vieworg[0];\r\n\tview[13] = r_refdef.vieworg[1];\r\n\tview[14] = r_refdef.vieworg[2];\r\n\tview[15] = 1;\r\n\r\n\tVectorMA(r_refdef.vieworg, 0.25, plane->normal, r_refdef.pvsorigin);\r\n\r\n\tMatrix4_Multiply(mirror, view, result);\r\n\r\n\tvpn[0] = result[0];\r\n\tvpn[1] = result[1];\r\n\tvpn[2] = result[2];\r\n\r\n\tvright[0] = -result[4];\r\n\tvright[1] = -result[5];\r\n\tvright[2] = -result[6];\r\n\r\n\tvup[0] = result[8];\r\n\tvup[1] = result[9];\r\n\tvup[2] = result[10];\r\n\r\n\tr_refdef.vieworg[0] = result[12];\r\n\tr_refdef.vieworg[1] = result[13];\r\n\tr_refdef.vieworg[2] = result[14];\r\n}\r\nstatic entity_t *R_NearestPortal(plane_t *plane)\r\n{\r\n\tint i;\r\n\tentity_t *best = NULL;\r\n\tfloat dist, bestd = 0;\r\n\tfor (i = 0; i < cl_numvisedicts; i++)\r\n\t{\r\n\t\tif (cl_visedicts[i].rtype == RT_PORTALSURFACE)\r\n\t\t{\r\n\t\t\tdist = DotProduct(cl_visedicts[i].origin, plane->normal)-plane->dist;\r\n\t\t\tdist = fabs(dist);\r\n\t\t\tif (dist < 64 && (!best || dist < bestd))\r\n\t\t\t\tbest = &cl_visedicts[i];\r\n\t\t}\r\n\t}\r\n\treturn best;\r\n}\r\n\r\nstatic void TransformCoord(vec3_t in, vec3_t planea[3], vec3_t planeo, vec3_t viewa[3], vec3_t viewo, vec3_t result)\r\n{\r\n\tint\t\ti;\r\n\tvec3_t\tlocal;\r\n\tvec3_t\ttransformed;\r\n\tfloat\td;\r\n\r\n\tlocal[0] = in[0] - planeo[0];\r\n\tlocal[1] = in[1] - planeo[1];\r\n\tlocal[2] = in[2] - planeo[2];\r\n\r\n\tVectorClear(transformed);\r\n\tfor ( i = 0 ; i < 3 ; i++ )\r\n\t{\r\n\t\td = DotProduct(local, planea[i]);\r\n\t\tVectorMA(transformed, d, viewa[i], transformed);\r\n\t}\r\n\r\n\tresult[0] = transformed[0] + viewo[0];\r\n\tresult[1] = transformed[1] + viewo[1];\r\n\tresult[2] = transformed[2] + viewo[2];\r\n}\r\nstatic void TransformDir(vec3_t in, vec3_t planea[3], vec3_t viewa[3], vec3_t result)\r\n{\r\n\tint\t\ti;\r\n\tfloat\td;\r\n\tvec3_t tmp;\r\n\r\n\tVectorCopy(in, tmp);\r\n\r\n\tVectorClear(result);\r\n\tfor ( i = 0 ; i < 3 ; i++ )\r\n\t{\r\n\t\td = DotProduct(tmp, planea[i]);\r\n\t\tVectorMA(result, d, viewa[i], result);\r\n\t}\r\n}\r\nstatic void R_RenderScene(void)\r\n{\r\n\tIDirect3DDevice8_SetTransform(pD3DDev8, D3DTS_PROJECTION, (D3DMATRIX*)d3d_trueprojection);\r\n\tIDirect3DDevice8_SetTransform(pD3DDev8, D3DTS_VIEW, (D3DMATRIX*)r_refdef.m_view);\r\n\tR_SetFrustum (r_refdef.m_projection_std, r_refdef.m_view);\r\n\tSurf_DrawWorld();\r\n}\r\n\r\nstatic void R_DrawPortal(batch_t *batch, batch_t **blist)\r\n{\r\n\tentity_t *view;\r\n\tfloat glplane[4];\r\n\tplane_t plane;\r\n\trefdef_t oldrefdef;\r\n\tmesh_t *mesh = batch->mesh[batch->firstmesh];\r\n\tint sort;\r\n\r\n\tif (r_refdef.recurse)\r\n\t\treturn;\r\n\r\n\tVectorCopy(mesh->normals_array[0], plane.normal);\r\n\tplane.dist = DotProduct(mesh->xyz_array[0], plane.normal);\r\n\r\n\t//if we're too far away from the surface, don't draw anything\r\n\tif (batch->shader->flags & SHADER_AGEN_PORTAL)\r\n\t{\r\n\t\t/*there's a portal alpha blend on that surface, that fades out after this distance*/\r\n\t\tif (DotProduct(r_refdef.vieworg, plane.normal)-plane.dist > batch->shader->portaldist)\r\n\t\t\treturn;\r\n\t}\r\n\t//if we're behind it, then also don't draw anything.\r\n\tif (DotProduct(r_refdef.vieworg, plane.normal)-plane.dist < 0)\r\n\t\treturn;\r\n\r\n\tview = R_NearestPortal(&plane);\r\n\t//if (!view)\r\n\t//\treturn;\r\n\r\n\toldrefdef = r_refdef;\r\n\tr_refdef.recurse = true;\r\n\r\n\tr_refdef.externalview = true;\r\n\r\n\tif (!view || VectorCompare(view->origin, view->oldorigin))\r\n\t{\r\n\t\tr_refdef.flipcull ^= SHADER_CULL_FLIP;\r\n\t\tR_MirrorMatrix(&plane);\r\n\t}\r\n\telse\r\n\t{\r\n\t\tfloat d;\r\n\t\tvec3_t paxis[3], porigin, vaxis[3], vorg;\r\n\t\tvoid PerpendicularVector( vec3_t dst, const vec3_t src );\r\n\r\n\t\t/*calculate where the surface is meant to be*/\r\n\t\tVectorCopy(mesh->normals_array[0], paxis[0]);\r\n\t\tPerpendicularVector(paxis[1], paxis[0]);\r\n\t\tCrossProduct(paxis[0], paxis[1], paxis[2]);\r\n\t\td = DotProduct(view->origin, plane.normal) - plane.dist;\r\n\t\tVectorMA(view->origin, -d, paxis[0], porigin);\r\n\r\n\t\t/*grab the camera origin*/\r\n\t\tVectorNegate(view->axis[0], vaxis[0]);\r\n\t\tVectorNegate(view->axis[1], vaxis[1]);\r\n\t\tVectorCopy(view->axis[2], vaxis[2]);\r\n\t\tVectorCopy(view->oldorigin, vorg);\r\n\r\n\t\tVectorCopy(vorg, r_refdef.pvsorigin);\r\n\r\n\t\t/*rotate it a bit*/\r\n\t\tRotatePointAroundVector(vaxis[1], vaxis[0], view->axis[1], sin(realtime)*4);\r\n\t\tCrossProduct(vaxis[0], vaxis[1], vaxis[2]);\r\n\r\n\t\tTransformCoord(oldrefdef.vieworg, paxis, porigin, vaxis, vorg, r_refdef.vieworg);\r\n\t\tTransformDir(vpn, paxis, vaxis, vpn);\r\n\t\tTransformDir(vright, paxis, vaxis, vright);\r\n\t\tTransformDir(vup, paxis, vaxis, vup);\r\n\t}\r\n\tMatrix4x4_CM_ModelViewMatrixFromAxis(r_refdef.m_view, vpn, vright, vup, r_refdef.vieworg);\r\n\tVectorAngles(vpn, vup, r_refdef.viewangles, true);\r\n\tVectorCopy(r_refdef.vieworg, r_origin);\r\n\r\n/*FIXME: the batch stuff should be done in renderscene*/\r\n\r\n\t/*fixup the first mesh index*/\r\n\tfor (sort = 0; sort < SHADER_SORT_COUNT; sort++)\r\n\tfor (batch = blist[sort]; batch; batch = batch->next)\r\n\t{\r\n\t\tbatch->firstmesh = batch->meshes;\r\n\t}\r\n\r\n\t/*FIXME: can we get away with stenciling the screen?*/\r\n\t/*Add to frustum culling instead of clip planes?*/\r\n\tglplane[0] = plane.normal[0];\r\n\tglplane[1] = plane.normal[1];\r\n\tglplane[2] = plane.normal[2];\r\n\tglplane[3] = -plane.dist;\r\n#ifndef _XBOX\r\n\tIDirect3DDevice8_SetClipPlane(pD3DDev8, 0, glplane);\r\n\tIDirect3DDevice8_SetRenderState(pD3DDev8, D3DRS_CLIPPLANEENABLE, D3DCLIPPLANE0);\r\n#endif\r\n\tSurf_SetupFrame();\r\n\tR_RenderScene();\r\n\r\n#ifndef _XBOX\r\n\tIDirect3DDevice8_SetRenderState(pD3DDev8, D3DRS_CLIPPLANEENABLE, 0);\r\n#endif\r\n\tfor (sort = 0; sort < SHADER_SORT_COUNT; sort++)\r\n\tfor (batch = blist[sort]; batch; batch = batch->next)\r\n\t{\r\n\t\tbatch->firstmesh = 0;\r\n\t}\r\n\tr_refdef = oldrefdef;\r\n\r\n\t/*broken stuff*/\r\n\tAngleVectors (r_refdef.viewangles, vpn, vright, vup);\r\n\tVectorCopy (r_refdef.vieworg, r_origin);\r\n\r\n\tIDirect3DDevice8_SetTransform(pD3DDev8, D3DTS_PROJECTION, (D3DMATRIX*)d3d_trueprojection);\r\n\tIDirect3DDevice8_SetTransform(pD3DDev8, D3DTS_VIEW, (D3DMATRIX*)r_refdef.m_view);\r\n\tR_SetFrustum (r_refdef.m_projection_std, r_refdef.m_view);\r\n}\r\n\r\nstatic void BE_SubmitMeshesPortals(batch_t **worldlist, batch_t *dynamiclist)\r\n{\r\n\tbatch_t *batch, *old;\r\n\tint i;\r\n\t/*attempt to draw portal shaders*/\r\n\tif (shaderstate.mode == BEM_STANDARD)\r\n\t{\r\n\t\tfor (i = 0; i < 2; i++)\r\n\t\t{\r\n\t\t\tfor (batch = i?dynamiclist:worldlist[SHADER_SORT_PORTAL]; batch; batch = batch->next)\r\n\t\t\t{\r\n\t\t\t\tif (batch->meshes == batch->firstmesh)\r\n\t\t\t\t\tcontinue;\r\n\r\n\t\t\t\tif (batch->buildmeshes)\r\n\t\t\t\t\tbatch->buildmeshes(batch);\r\n\r\n\t\t\t\t/*draw already-drawn portals as depth-only, to ensure that their contents are not harmed*/\r\n\t\t\t\tBE_SelectMode(BEM_DEPTHONLY);\r\n\t\t\t\tfor (old = worldlist[SHADER_SORT_PORTAL]; old && old != batch; old = old->next)\r\n\t\t\t\t{\r\n\t\t\t\t\tif (old->meshes == old->firstmesh)\r\n\t\t\t\t\t\tcontinue;\r\n\t\t\t\t\tBE_SubmitBatch(old);\r\n\t\t\t\t}\r\n\t\t\t\tif (!old)\r\n\t\t\t\t{\r\n\t\t\t\t\tfor (old = dynamiclist; old != batch; old = old->next)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tif (old->meshes == old->firstmesh)\r\n\t\t\t\t\t\t\tcontinue;\r\n\t\t\t\t\t\tBE_SubmitBatch(old);\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t\tBE_SelectMode(BEM_STANDARD);\r\n\r\n\t\t\t\tR_DrawPortal(batch, worldlist);\r\n\r\n\t\t\t\t/*clear depth again*/\r\n\t\t\t\tIDirect3DDevice8_Clear(pD3DDev8, 0, NULL, D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0,0,0), 1, 0);\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n}\r\n\r\nvoid D3D8BE_SubmitMeshes (batch_t **worldbatches, batch_t **blist, int first, int stop)\r\n{\r\n\tint i;\r\n\r\n\tfor (i = first; i < stop; i++)\r\n\t{\r\n\t\tif (worldbatches)\r\n\t\t{\r\n\t\t\tif (i == SHADER_SORT_PORTAL /*&& !r_noportals.ival*/ && !r_refdef.recurse)\r\n\t\t\t\tBE_SubmitMeshesPortals(worldbatches, blist[i]);\r\n\r\n\t\t\tBE_SubmitMeshesSortList(worldbatches[i]);\r\n\t\t}\r\n\t\tBE_SubmitMeshesSortList(blist[i]);\r\n\t}\r\n}\r\n\r\n#ifdef RTLIGHTS\r\nvoid D3D8BE_BaseEntTextures(void)\r\n{\r\n\tbatch_t *batches[SHADER_SORT_COUNT];\r\n\tBE_GenModelBatches(batches, shaderstate.curdlight, shaderstate.mode, r_refdef.scenevis);\r\n\tD3D8BE_SubmitMeshes(NULL, batches, SHADER_SORT_PORTAL, SHADER_SORT_SEETHROUGH+1);\r\n\tBE_SelectEntity(&r_worldentity);\r\n}\r\n\r\nvoid D3D8BE_RenderShadowBuffer(unsigned int numverts, IDirect3DVertexBuffer8 *vbuf, unsigned int numindicies, IDirect3DIndexBuffer8 *ibuf)\r\n{\r\n#ifdef FIXME\r\n\tfloat pushdepth = shaderstate.curshader->polyoffset.factor;\r\n#ifdef BEF_PUSHDEPTH\r\n\textern cvar_t r_polygonoffset_submodel_factor;\r\n//\tif (shaderstate.flags & BEF_PUSHDEPTH)\r\n\t\tpushdepth += r_polygonoffset_submodel_factor.value;\r\n#endif\r\n//\tD3D8BE_Cull(0);//shaderstate.curshader->flags & (SHADER_CULL_FRONT | SHADER_CULL_BACK));\r\n\tpushdepth /= 0xffff;\r\n\r\n\tif (pushdepth != shaderstate.depthbias)\r\n\t{\r\n\t\tshaderstate.depthbias = pushdepth;\r\n\t\tIDirect3DDevice8_SetRenderState(pD3DDev8, D3DRS_DEPTHBIAS, *(DWORD*)&shaderstate.depthbias);\r\n\t}\r\n#endif\r\n\r\n\r\n\tIDirect3DDevice8_SetStreamSource(pD3DDev8, 0, vbuf, sizeof(vbovdata_t));\r\n\tIDirect3DDevice8_SetIndices(pD3DDev8, ibuf, 0);\r\n\r\n\tIDirect3DDevice8_DrawIndexedPrimitive(pD3DDev8, D3DPT_TRIANGLELIST, 0, numverts, 0, numindicies/3);\r\n}\r\n#endif\r\n\r\nvoid D3D8BE_DrawWorld (batch_t **worldbatches)\r\n{\r\n\tbatch_t *batches[SHADER_SORT_COUNT];\r\n\tRSpeedLocals();\r\n\r\n\tshaderstate.curentity = NULL;\r\n\r\n\tif (!r_refdef.recurse)\r\n\t{\r\n\t\tif (shaderstate.wbatch > shaderstate.maxwbatches)\r\n\t\t{\r\n\t\t\tint newm = shaderstate.wbatch;\r\n\t\t\tshaderstate.wbatches = BZ_Realloc(shaderstate.wbatches, newm * sizeof(*shaderstate.wbatches));\r\n\t\t\tmemset(shaderstate.wbatches + shaderstate.maxwbatches, 0, (newm - shaderstate.maxwbatches) * sizeof(*shaderstate.wbatches));\r\n\t\t\tshaderstate.maxwbatches = newm;\r\n\t\t}\r\n\t\tshaderstate.wbatch = 0;\r\n\t}\r\n\r\n\tshaderstate.curdlight = NULL;\r\n\tBE_GenModelBatches(batches, shaderstate.curdlight, BEM_STANDARD, r_refdef.scenevis);\r\n\r\n\tif (worldbatches)\r\n\t{\r\n\t\tfloat shaderstate_identitylighting;\r\n\t\tBE_UploadLightmaps(false);\r\n\r\n\t\t//make sure the world draws correctly\r\n\t\tr_worldentity.shaderRGBAf[0] = 1;\r\n\t\tr_worldentity.shaderRGBAf[1] = 1;\r\n\t\tr_worldentity.shaderRGBAf[2] = 1;\r\n\t\tr_worldentity.shaderRGBAf[3] = 1;\r\n\t\tr_worldentity.axis[0][0] = 1;\r\n\t\tr_worldentity.axis[1][1] = 1;\r\n\t\tr_worldentity.axis[2][2] = 1;\r\n\r\n#ifdef RTLIGHTS\r\n\t\tif (worldbatches && r_shadow_realtime_world.ival)\r\n\t\t\tshaderstate_identitylighting = r_shadow_realtime_world_lightmaps.value;\r\n\t\telse\r\n#endif\r\n\t\t\tshaderstate_identitylighting = r_lightmap_scale.value;\r\n\t\tshaderstate_identitylighting *= r_refdef.hdr_value;\r\n//\t\tshaderstate_identitylightmap = shaderstate.identitylighting / (1<<gl_overbright.ival);\r\n\r\n\t\tif (shaderstate_identitylighting == 0)\r\n\t\t\tBE_SelectMode(BEM_DEPTHDARK);\r\n\t\telse\r\n\t\t\tBE_SelectMode(BEM_STANDARD);\r\n\r\n\t\tRSpeedRemark();\r\n\t\tD3D8BE_SubmitMeshes(worldbatches, batches, SHADER_SORT_PORTAL, SHADER_SORT_SEETHROUGH+1);\r\n\t\tRSpeedEnd(RSPEED_OPAQUE);\r\n\r\n#ifdef RTLIGHTS\r\n\t\tif (r_refdef.scenevis)\r\n\t\t{\r\n\t\t\tRSpeedRemark();\r\n\t\t\tD3D8BE_SelectEntity(&r_worldentity);\r\n\t\t\tSh_DrawLights(r_refdef.scenevis);\r\n\t\t\tRSpeedEnd(RSPEED_RTLIGHTS);\r\n\t\t}\r\n#endif\r\n\r\n\t\tBE_SelectMode(BEM_STANDARD);\r\n\r\n\t\tRSpeedRemark();\r\n\t\tD3D8BE_SubmitMeshes(worldbatches, batches, SHADER_SORT_SEETHROUGH+1, SHADER_SORT_COUNT);\r\n\t\tRSpeedEnd(RSPEED_TRANSPARENTS);\r\n\t}\r\n\telse\r\n\t{\r\n\t\tRSpeedRemark();\r\n\t\tD3D8BE_SubmitMeshes(NULL, batches, SHADER_SORT_PORTAL, SHADER_SORT_COUNT);\r\n\t\tRSpeedEnd(RSPEED_OPAQUE);\r\n\t}\r\n\r\n\tR_RenderDlights ();\r\n\r\n\tBE_RotateForEntity(&r_worldentity, NULL);\r\n}\r\nvoid D3D8BE_VBO_Begin(vbobctx_t *ctx, size_t maxsize)\r\n{\r\n\tIDirect3DVertexBuffer8 *buf;\r\n\tIDirect3DDevice8_CreateVertexBuffer(pD3DDev8, maxsize, D3DUSAGE_WRITEONLY, 0, D3DPOOL_MANAGED, &buf);\r\n\tctx->vboptr[0] = buf;\r\n\r\n\tIDirect3DVertexBuffer8_Lock(buf, 0, maxsize, (BYTE**)&ctx->fallback, D3DLOCK_DISCARD);\r\n\r\n\tctx->pos = 0;\r\n}\r\nvoid D3D8BE_VBO_Data(vbobctx_t *ctx, void *data, size_t size, vboarray_t *varray)\r\n{\r\n\tIDirect3DVertexBuffer8 *buf = ctx->vboptr[0];\r\n\tmemcpy((char*)ctx->fallback + ctx->pos, data, size);\r\n\tvarray->d3d.buff = buf;\r\n\tvarray->d3d.offs = ctx->pos;\r\n\tctx->pos += size;\r\n}\r\nvoid D3D8BE_VBO_Finish(vbobctx_t *ctx, void *edata, size_t esize, vboarray_t *earray, void **vbomem, void **ebomem)\r\n{\r\n\tIDirect3DIndexBuffer8 *buf;\r\n\tIDirect3DVertexBuffer8 *vbuf = ctx->vboptr[0];\r\n\tIDirect3DVertexBuffer8_Unlock(vbuf);\r\n\tctx->fallback = NULL;\r\n\r\n\tIDirect3DDevice8_CreateIndexBuffer(pD3DDev8, esize, 0, D3DFMT_QINDEX, D3DPOOL_MANAGED, &buf);\r\n\tctx->vboptr[1] = buf;\r\n\tIDirect3DIndexBuffer8_Lock(buf, 0, esize, (BYTE**)&ctx->fallback, D3DLOCK_DISCARD);\r\n\tmemcpy(ctx->fallback, edata, esize);\r\n\tIDirect3DIndexBuffer8_Unlock(buf);\r\n\tctx->fallback = NULL;\r\n\r\n\tearray->d3d.buff = buf;\r\n\tearray->d3d.offs = 0;\r\n}\r\nvoid D3D8BE_VBO_Destroy(vboarray_t *vearray, void *mem)\r\n{\r\n\tIUnknown *ebuf = vearray->d3d.buff;\r\n\tif (ebuf)\r\n\t\tebuf->lpVtbl->Release(ebuf);\r\n}\r\n\r\nvoid D3D8BE_Scissor(srect_t *srect)\r\n{\r\n\t//d3d8 has no scissor\r\n\t//FIXME: we can supposedly emulate it with some projection matrix tweaks and viewport stuff\r\n#if 0\r\n\tRECT rect;\r\n\tif (srect)\r\n\t{\r\n\t\trect.left = (srect->x) * vid.fbpwidth;\r\n\t\trect.right = (srect->x + srect->width) * vid.fbpwidth;\r\n\t\trect.top = (srect->y) * vid.fbpheight;\r\n\t\trect.bottom = (srect->y + srect->height) * vid.fbpheight;\r\n\t}\r\n\telse\r\n\t{\r\n\t\trect.left = 0;\r\n\t\trect.right = vid.pixelwidth;\r\n\t\trect.top = 0;\r\n\t\trect.bottom = vid.pixelheight;\r\n\t}\r\n\tIDirect3DDevice8_SetScissorRect(pD3DDev8, &rect);\r\n\tIDirect3DDevice8_SetRenderState(pD3DDev8, D3DRS_SCISSORTESTENABLE, TRUE);\r\n#endif\r\n}\r\n\r\n#endif\r\n"
  },
  {
    "path": "engine/d3d/d3d8_image.c",
    "content": "#include \"quakedef.h\"\r\n#include \"winquake.h\"\r\n#ifdef D3D8QUAKE\r\n#if !defined(HMONITOR_DECLARED) && (WINVER < 0x0500)\r\n    #define HMONITOR_DECLARED\r\n    DECLARE_HANDLE(HMONITOR);\r\n#endif\r\n\r\n#ifdef _XBOX\r\n\t#include <xtl.h>\r\n\t#define D3DLOCK_NOSYSLOCK 0\r\n\t#define D3DLOCK_DISCARD 0\r\n#endif\r\n\r\n#include <d3d8.h>\r\nextern LPDIRECT3DDEVICE8 pD3DDev8;\r\n\r\nvoid D3D8_DestroyTexture (texid_t tex)\r\n{\r\n\tif (!tex)\r\n\t\treturn;\r\n\r\n\tif (tex->ptr)\r\n\t\tIDirect3DTexture8_Release((IDirect3DTexture8*)tex->ptr);\r\n\ttex->ptr = NULL;\r\n}\r\n\r\nqboolean D3D8_LoadTextureMips(image_t *tex, const struct pendingtextureinfo *mips)\r\n{\r\n\tqbyte *fte_restrict out, *fte_restrict in;\r\n\tint x, y, i;\r\n\tD3DLOCKED_RECT lock;\r\n\tD3DFORMAT fmt = D3DFMT_UNKNOWN;\r\n\tD3DSURFACE_DESC desc;\r\n\tIDirect3DTexture8 *dt;\r\n\tqboolean swap = false;\r\n\tunsigned int blockwidth, blockheight, blockbytes = 1;\r\n\r\n\tif (mips->type != PTI_2D)\r\n\t\treturn false;\t//fixme: cube and volumes should work\r\n\r\n\tswitch(mips->encoding)\r\n\t{\r\n\tcase PTI_RGB565:\r\n\t\tfmt = D3DFMT_R5G6B5;\r\n\t\tbreak;\r\n\tcase PTI_RGBA4444://not supported on d3d9\r\n\t\treturn false;\r\n\tcase PTI_ARGB4444:\r\n\t\tfmt = D3DFMT_A4R4G4B4;\r\n\t\tbreak;\r\n\tcase PTI_RGBA5551://not supported on d3d9\r\n\t\treturn false;\r\n\tcase PTI_ARGB1555:\r\n\t\tfmt = D3DFMT_A1R5G5B5;\r\n\t\tbreak;\r\n\tcase PTI_RGBA8:\r\n//\t\tfmt = D3DFMT_A8B8G8R8;\t//how do we check \r\n\t\tfmt = D3DFMT_A8R8G8B8;\r\n\t\tswap = true;\r\n\t\tbreak;\r\n\tcase PTI_RGBX8:\r\n//\t\tfmt = D3DFMT_X8B8G8R8;\r\n\t\tfmt = D3DFMT_X8R8G8B8;\r\n\t\tswap = true;\r\n\t\tbreak;\r\n\tcase PTI_BGRA8:\r\n\t\tfmt = D3DFMT_A8R8G8B8;\r\n\t\tbreak;\r\n\tcase PTI_BGRX8:\r\n\t\tfmt = D3DFMT_X8R8G8B8;\r\n\t\tbreak;\r\n\r\n\t//too lazy to support these for now\r\n\tcase PTI_BC1_RGB:\r\n\tcase PTI_BC1_RGBA:\t//d3d doesn't distinguish between these\r\n\t\tfmt = D3DFMT_DXT1;\r\n\t\tbreak;\r\n\tcase PTI_BC2_RGBA:\r\n\t\tfmt = D3DFMT_DXT3;\r\n\t\tbreak;\r\n\tcase PTI_BC3_RGBA:\r\n\t\tfmt = D3DFMT_DXT5;\r\n\t\tbreak;\r\n\r\n\tcase PTI_EMULATED:\r\n\tdefault:\t//no idea\r\n\t\tbreak;\r\n\t}\r\n\tif (fmt == D3DFMT_UNKNOWN)\r\n\t\treturn false;\r\n\r\n\tImage_BlockSizeForEncoding(mips->encoding, &blockbytes, &blockwidth, &blockheight);\r\n\r\n\tif (!pD3DDev8)\r\n\t\treturn false;\t//can happen on errors\r\n\tif (FAILED(IDirect3DDevice8_CreateTexture(pD3DDev8, mips->mip[0].width, mips->mip[0].height, mips->mipcount, 0, fmt, D3DPOOL_MANAGED, &dt)))\r\n\t\treturn false;\r\n\r\n\tfor (i = 0; i < mips->mipcount; i++)\r\n\t{\r\n\t\tIDirect3DTexture8_GetLevelDesc(dt, i, &desc);\r\n\r\n\t\tif (mips->mip[i].height != desc.Height || mips->mip[i].width != desc.Width)\r\n\t\t{\r\n\t\t\tIDirect3DTexture8_Release(dt);\r\n\t\t\treturn false;\r\n\t\t}\r\n\r\n\t\tIDirect3DTexture8_LockRect(dt, i, &lock, NULL, D3DLOCK_NOSYSLOCK|D3DLOCK_DISCARD);\r\n\t\t//can't do it in one go. pitch might contain padding or be upside down.\r\n\t\tif (!mips->mip[i].data)\r\n\t\t\t;\r\n\t\telse if (swap)\r\n\t\t{\r\n\t\t\tsize_t rowbytes = ((mips->mip[i].width+blockwidth-1)/blockwidth)*blockbytes;\r\n\t\t\tfor (y = 0, out = lock.pBits, in = mips->mip[i].data; y < mips->mip[i].height; y++, out += lock.Pitch, in += rowbytes)\r\n\t\t\t{\r\n\t\t\t\tfor (x = 0; x < rowbytes; x+=4)\r\n\t\t\t\t{\r\n\t\t\t\t\tout[x+0] = in[x+2];\r\n\t\t\t\t\tout[x+1] = in[x+1];\r\n\t\t\t\t\tout[x+2] = in[x+0];\r\n\t\t\t\t\tout[x+3] = in[x+3];\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tsize_t rowbytes = ((mips->mip[i].width+blockwidth-1)/blockwidth)*blockbytes;\r\n\t\t\tfor (y = 0, out = lock.pBits, in = mips->mip[i].data; y < mips->mip[i].height; y+=blockheight, out += lock.Pitch, in += rowbytes)\r\n\t\t\t\tmemcpy(out, in, rowbytes);\r\n\t\t}\r\n\t\tIDirect3DTexture8_UnlockRect(dt, i);\r\n\t}\r\n\r\n\tD3D8_DestroyTexture(tex);\r\n\ttex->ptr = dt;\r\n\r\n\treturn true;\r\n}\r\nvoid D3D8_UploadLightmap(lightmapinfo_t *lm)\r\n{\r\n}\r\n\r\n#endif\r\n"
  },
  {
    "path": "engine/d3d/d3d_backend.c",
    "content": "#include \"quakedef.h\"\n#include \"glquake.h\"\n#include \"gl_draw.h\"\n#ifdef D3D9QUAKE\n#include \"shader.h\"\n#include <d3d9.h>\n#if !defined(HMONITOR_DECLARED) && (WINVER < 0x0500)\n    #define HMONITOR_DECLARED\n    DECLARE_HANDLE(HMONITOR);\n#endif\n\n/*\nThings to improve:\n\tmmapping:\n\t\tthis code is fairly simple. the caller gives a series of batches/meshes, and this code pushes the data to the gpu in its entirety before moving on to the next batch\n\t\tfor the world, this is fast enough where its pretty much all vboed (except for weird shaders).\n\t\tbut for lightning beams and models etc, we're mmapping new data regions in the dynamic vbo for every single quad.\n\t\tnot just one mmap, but one mmap for every single individual attribute that is submitted\n\t\tby using a single buffer for all dynamic data, and filling that buffer before flushing anything to the hardware we ought to get higher framerates\n\t\tespecially if this means D3DLOCK_NOOVERWRITE checks etc are not doing weird slow things.\n\t\tThis won't affect the world too much, but should stop everything else destroying the framerate.\n\n\t\tlong story short, make this batch properly, and use D3DLOCK_DISCARD only.\n\n\tmodels:\n\t\tmodels should already be in a buffer (with different offsets for different poses). This is a problem with gl too.\n\n\thlsl:\n\t\twe don't really use this as much as we should. Should be possible to use as a fast path to avoid having to do any weird shader/dynamic stuff.\n\t\tcurrently used for water(required to mimic software rendering) and sky(because its muuuch faster than a skydome).\n\t\thlsl programs ought to have per-hlsl vertex declarations, and only one stream source.\n*/\n\n\n\n//#define FORCESTATE\n\n#ifdef FORCESTATE\n#pragma warningmsg(\"D3D9 FORCESTATE is active\")\n#endif\n\n\nextern LPDIRECT3DDEVICE9 pD3DDev9;\n\n//#define d3dcheck(foo) foo\n#ifdef _DEBUG\n#define d3dcheck(foo) do{HRESULT err = foo; if (FAILED(err)) Sys_Error(\"D3D reported error on backend line %i - error 0x%x\\n\", __LINE__, (unsigned int)err);} while(0)\n#else\n#define d3dcheck(foo) foo\n#endif\n\n#define MAX_TMUS 16\n\n#define MAX_TC_TMUS 4\n\nextern texid_t r_whiteimage;\nextern float d3d_trueprojection_std[16];\nextern float d3d_trueprojection_view[16];\n\nstatic void D3D9BE_RotateForEntity (const entity_t *e, const model_t *mod);\n\n/*========================================== tables for deforms =====================================*/\n#define frand() (rand()*(1.0/RAND_MAX))\n#define FTABLE_SIZE\t\t1024\n#define FTABLE_CLAMP(x)\t(((int)((x)*FTABLE_SIZE) & (FTABLE_SIZE-1)))\n#define FTABLE_EVALUATE(table,x) (table ? table[FTABLE_CLAMP(x)] : frand()*((x)-floor(x)))\n\nstatic\tfloat\tr_sintable[FTABLE_SIZE];\nstatic\tfloat\tr_triangletable[FTABLE_SIZE];\nstatic\tfloat\tr_squaretable[FTABLE_SIZE];\nstatic\tfloat\tr_sawtoothtable[FTABLE_SIZE];\nstatic\tfloat\tr_inversesawtoothtable[FTABLE_SIZE];\n\nstatic float *FTableForFunc ( unsigned int func )\n{\n\tswitch (func)\n\t{\n\t\tcase SHADER_FUNC_SIN:\n\t\t\treturn r_sintable;\n\n\t\tcase SHADER_FUNC_TRIANGLE:\n\t\t\treturn r_triangletable;\n\n\t\tcase SHADER_FUNC_SQUARE:\n\t\t\treturn r_squaretable;\n\n\t\tcase SHADER_FUNC_SAWTOOTH:\n\t\t\treturn r_sawtoothtable;\n\n\t\tcase SHADER_FUNC_INVERSESAWTOOTH:\n\t\t\treturn r_inversesawtoothtable;\n\t}\n\n\t//bad values allow us to crash (so I can debug em)\n\treturn NULL;\n}\n\nstatic void FTable_Init(void)\n{\n\tunsigned int i;\n\tdouble t;\n\tfor (i = 0; i < FTABLE_SIZE; i++)\n\t{\n\t\tt = (double)i / (double)FTABLE_SIZE;\n\n\t\tr_sintable[i] = sin(t * 2*M_PI);\n\n\t\tif (t < 0.25)\n\t\t\tr_triangletable[i] = t * 4.0;\n\t\telse if (t < 0.75)\n\t\t\tr_triangletable[i] = 2 - 4.0 * t;\n\t\telse\n\t\t\tr_triangletable[i] = (t - 0.75) * 4.0 - 1.0;\n\n\t\tif (t < 0.5)\n\t\t\tr_squaretable[i] = 1.0f;\n\t\telse\n\t\t\tr_squaretable[i] = -1.0f;\n\n\t\tr_sawtoothtable[i] = t;\n\t\tr_inversesawtoothtable[i] = 1.0 - t;\n\t}\n}\n\ntypedef vec3_t mat3_t[3];\nstatic mat3_t axisDefault={{1, 0, 0},\n\t\t\t\t\t{0, 1, 0},\n\t\t\t\t\t{0, 0, 1}};\n\nstatic void Matrix3_Transpose (mat3_t in, mat3_t out)\n{\n\tout[0][0] = in[0][0];\n\tout[1][1] = in[1][1];\n\tout[2][2] = in[2][2];\n\n\tout[0][1] = in[1][0];\n\tout[0][2] = in[2][0];\n\tout[1][0] = in[0][1];\n\tout[1][2] = in[2][1];\n\tout[2][0] = in[0][2];\n\tout[2][1] = in[1][2];\n}\nstatic void Matrix3_Multiply_Vec3 (const mat3_t a, const vec3_t b, vec3_t product)\n{\n\tproduct[0] = a[0][0]*b[0] + a[0][1]*b[1] + a[0][2]*b[2];\n\tproduct[1] = a[1][0]*b[0] + a[1][1]*b[1] + a[1][2]*b[2];\n\tproduct[2] = a[2][0]*b[0] + a[2][1]*b[1] + a[2][2]*b[2];\n}\n\nstatic int Matrix3_Compare(const mat3_t in, const mat3_t out)\n{\n\treturn !memcmp(in, out, sizeof(mat3_t));\n}\n\n/*================================================*/\n\ntypedef struct\n{\n\tbackendmode_t mode;\n\tunsigned int flags;\n\n\tD3DVIEWPORT9 vport;\n\tfloat\t\t*curprojection;\n\n\tfloat\t\tcurtime;\n\tconst entity_t\t*curentity;\n\tconst dlight_t\t*curdlight;\n\tbatch_t\t\t*curbatch, dummybatch;\n\tvec3_t\t\tcurdlight_colours;\n\tshader_t\t*curshader;\n\ttexnums_t\t*curtexnums;\n\ttexid_t\t\tcurlightmap;\n\ttexid_t\t\tcurdeluxmap;\n\tint\t\t\tcurvertdecl;\n\tunsigned int shaderbits;\n\tunsigned int curcull;\n\tpolyoffset_t curpolyoffset;\n\tfloat\t\t gltod3d_depthunit; //multiplier to convert from gl's depthunit notches to d3d's range values\n\tfloat m_model[16];\n\tunsigned int lastpasscount;\n\tvbo_t *batchvbo;\n\tstruct programpermu_s\t*curperm;\n\n\tshader_t\t*shader_rtlight;\n\tIDirect3DTexture9\t*curtex[MAX_TMUS];\n\tunsigned int curtexflags[MAX_TMUS];\n\tunsigned int tmuflags[MAX_TMUS];\n\n\tmesh_t\t\t**meshlist;\n\tunsigned int nummeshes;\n\n\tD3DCOLOR\tpasscolour;\n\tqboolean\tpasssinglecolour;\n\n\t/*FIXME: we shouldn't lock these so much - we need to cache which batches have been submitted and set up streams separately from the vertex data*/\n\tIDirect3DVertexBuffer9 *dynxyz_buff;\n\tunsigned int dynxyz_offs;\n\tunsigned int dynxyz_size;\n\n\tIDirect3DVertexBuffer9 *dynst_buff[MAX_TC_TMUS];\n\tunsigned int dynst_offs[MAX_TC_TMUS];\n\tunsigned int dynst_size;\n\n\tIDirect3DVertexBuffer9 *dyncol_buff;\n\tunsigned int dyncol_offs;\n\tunsigned int dyncol_size;\n\n\tIDirect3DVertexBuffer9 *dynnorm_buff;\n\tunsigned int dynnorm_offs;\n\tunsigned int dynnorm_size;\n\n\tIDirect3DIndexBuffer9 *dynidx_buff;\n\tunsigned int dynidx_offs;\n\tunsigned int dynidx_size;\n\n\tunsigned int wbatch;\n\tunsigned int maxwbatches;\n\tbatch_t *wbatches;\n\n\tint mipfilter[3];\n\tint picfilter[3];\n\tD3DSAMPLERSTATETYPE anisfilter;\n\n\ttexid_t fogtexture;\n\tfloat fogdensity;\n\tfloat fogdepth;\n\tfloat fogfar;\n} d3dbackend_t;\n\ntypedef struct\n{\n\tvecV_t coord;\n\tvec2_t tex;\n\tvec2_t lm;\n\tvec3_t ndir;\n\tvec3_t sdir;\n\tvec3_t tdir;\n\tbyte_vec4_t colorsb;\n} vbovdata_t;\n\n#define DYNVBUFFSIZE 65536*8\n#define DYNIBUFFSIZE 65536*24\n\nstatic d3dbackend_t shaderstate;\n\nextern int be_maxpasses;\n\nenum\n{\n\tD3D_VDEC_COL4B\t= 1<<0,\n\tD3D_VDEC_ST0\t= 1<<1,\n\tD3D_VDEC_ST1\t= 1<<2,\n\tD3D_VDEC_ST2\t= 1<<3,\n\tD3D_VDEC_ST3\t= 1<<4,\n\tD3D_VDEC_NORM\t= 1<<5,\n\tD3D_VDEC_SKEL\t= 1<<6,\n\tD3D_VDEC_POS2\t= 1<<7,\n\tD3D_VDEC_CM\t\t= 1<<8,\n\tD3D_VDEC_MAX\t= 1<<9,\n};\n#define STRM_VERT\t0\n#define STRM_COL\t1\n#define STRM_TC0\t2\n#define STRM_TC1\t3\n#define STRM_TC2\t4\n#define STRM_TC3\t5\n#define STRM_NORM\t6\n#define STRM_NORMS\t7\n#define STRM_NORMT\t8\n#define STRM_BONENUM\t9\n#define STRM_BONEWEIGHT\t10\n#define STRM_VERT2\t11\n#define STRM_MAX 12\nstatic IDirect3DVertexDeclaration9 *vertexdecls[D3D_VDEC_MAX];\n\nstatic void BE_ApplyTMUState(unsigned int tu, unsigned int flags)\n{\n\tunsigned int delta = shaderstate.tmuflags[tu] ^ flags;\n\tif (!delta)\n\t\treturn;\n\tif (delta & SHADER_PASS_SRGB)\n\t\tIDirect3DDevice9_SetSamplerState(pD3DDev9, tu, D3DSAMP_SRGBTEXTURE, (flags & SHADER_PASS_SRGB)?TRUE:FALSE);\n\tif (delta & SHADER_PASS_CLAMP)\n\t{\n\t\tif (flags & SHADER_PASS_CLAMP)\n\t\t{\n\t\t\tIDirect3DDevice9_SetSamplerState(pD3DDev9, tu, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP);\n\t\t\tIDirect3DDevice9_SetSamplerState(pD3DDev9, tu, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP);\n\t\t\tIDirect3DDevice9_SetSamplerState(pD3DDev9, tu, D3DSAMP_ADDRESSW, D3DTADDRESS_CLAMP);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tIDirect3DDevice9_SetSamplerState(pD3DDev9, tu, D3DSAMP_ADDRESSU, D3DTADDRESS_WRAP);\n\t\t\tIDirect3DDevice9_SetSamplerState(pD3DDev9, tu, D3DSAMP_ADDRESSV, D3DTADDRESS_WRAP);\n\t\t\tIDirect3DDevice9_SetSamplerState(pD3DDev9, tu, D3DSAMP_ADDRESSW, D3DTADDRESS_WRAP);\n\t\t}\n\t}\n\tif (delta & (SHADER_PASS_NOMIPMAP|SHADER_PASS_NEAREST|SHADER_PASS_LINEAR|SHADER_PASS_UIPIC))\n\t{\n\t\tint min, mip, mag;\n\t\tint *filter = (flags & SHADER_PASS_UIPIC)?shaderstate.picfilter:shaderstate.mipfilter;\n\n\t\tif ((filter[2] && !(flags & SHADER_PASS_NEAREST)) || (flags & SHADER_PASS_LINEAR))\n\t\t\tmag = shaderstate.anisfilter;\n\t\telse\n\t\t\tmag = D3DTEXF_POINT;\n\t\tif (filter[1] == -1 || (flags & IF_NOMIPMAP))\n\t\t\tmip = D3DTEXF_NONE;\n\t\telse if ((filter[1] && !(flags & SHADER_PASS_NEAREST)) || (flags & SHADER_PASS_LINEAR))\n\t\t\tmip = D3DTEXF_LINEAR;\n\t\telse\n\t\t\tmip = D3DTEXF_POINT;\n\t\tif ((filter[0] && !(flags & SHADER_PASS_NEAREST)) || (flags & SHADER_PASS_LINEAR))\n\t\t\tmin = shaderstate.anisfilter;\n\t\telse\n\t\t\tmin = D3DTEXF_POINT;\n\n\t\tIDirect3DDevice9_SetSamplerState(pD3DDev9, tu, D3DSAMP_MINFILTER, min);\n\t\tIDirect3DDevice9_SetSamplerState(pD3DDev9, tu, D3DSAMP_MIPFILTER, mip);\n\t\tIDirect3DDevice9_SetSamplerState(pD3DDev9, tu, D3DSAMP_MAGFILTER, mag);\n\t}\n\tshaderstate.tmuflags[tu] = flags;\n}\n\n//d3d9 is all sampler state\nvoid D3D9_UpdateFiltering(image_t *imagelist, int filtermip[3], int filterpic[3], int mipcap[2], float miplod, float anis)\n{\n\tint i;\n\tmemcpy(shaderstate.mipfilter, filtermip, sizeof(shaderstate.mipfilter));\n\tmemcpy(shaderstate.picfilter, filterpic, sizeof(shaderstate.picfilter));\n\tif (anis <= 1)\n\t{\n\t\tanis = 1;\n\t\tshaderstate.anisfilter = D3DTEXF_LINEAR;\n\t}\n\telse\n\t\tshaderstate.anisfilter = D3DTEXF_ANISOTROPIC;\n\n\tfor (i = 0; i < MAX_TMUS; i++)\n\t{\n\t\tIDirect3DDevice9_SetSamplerState(pD3DDev9, i, D3DSAMP_MIPMAPLODBIAS, miplod);\n\t\tIDirect3DDevice9_SetSamplerState(pD3DDev9, i, D3DSAMP_MAXANISOTROPY, anis);\n//\t\tIDirect3DDevice9_SetSamplerState(pD3DDev9, i, D3DSAMP_MIPMAPLODBIAS, 0);\t//negative lod bias? :s\n\n//\t\tshaderstate.tmuflags[i] = ~shaderstate.tmuflags[i];\n\t\tBE_ApplyTMUState(i, ~shaderstate.tmuflags[i]);\n\t}\n}\n\nstatic void BE_ApplyShaderBits(unsigned int bits)\n{\n\tunsigned int delta;\n\n\tif (shaderstate.flags & (BEF_FORCEADDITIVE|BEF_FORCETRANSPARENT|BEF_FORCENODEPTH|BEF_FORCEDEPTHTEST|BEF_FORCEDEPTHWRITE))\n\t{\n\t\tif (shaderstate.flags & BEF_FORCEADDITIVE)\n\t\t\tbits = (bits & ~(SBITS_MISC_DEPTHWRITE|SBITS_BLEND_BITS|SBITS_ATEST_BITS))\n\t\t\t\t\t\t| (SBITS_SRCBLEND_SRC_ALPHA | SBITS_DSTBLEND_ONE);\n\t\telse if (shaderstate.flags & BEF_FORCETRANSPARENT)\n\t\t{\n\t\t\tif ((bits & SBITS_BLEND_BITS) == (SBITS_SRCBLEND_ONE|SBITS_DSTBLEND_ZERO) || !(bits & SBITS_BLEND_BITS)) \t/*if transparency is forced, clear alpha test bits*/\n\t\t\t\tbits = (bits & ~(SBITS_MISC_DEPTHWRITE|SBITS_BLEND_BITS|SBITS_ATEST_BITS))\n\t\t\t\t\t\t\t| (SBITS_SRCBLEND_SRC_ALPHA | SBITS_DSTBLEND_ONE_MINUS_SRC_ALPHA);\n\t\t}\n\n\t\tif (shaderstate.flags & BEF_FORCENODEPTH) \t/*EF_NODEPTHTEST dp extension*/\n\t\t\tbits |= SBITS_MISC_NODEPTHTEST;\n\t\telse\n\t\t{\n\t\t\tif (shaderstate.flags & BEF_FORCEDEPTHTEST)\n\t\t\t\tbits &= ~SBITS_MISC_NODEPTHTEST;\n\t\t\tif (shaderstate.flags & BEF_FORCEDEPTHWRITE)\n\t\t\t\tbits |= SBITS_MISC_DEPTHWRITE;\n\t\t}\n\t}\n\n#ifdef FORCESTATE\n\tdelta = ~0;\n#else\n\tdelta = bits ^ shaderstate.shaderbits;\n#endif\n\tif (!delta)\n\t\treturn;\n\tshaderstate.shaderbits = bits;\n\n\tif (delta & SBITS_BLEND_BITS)\n\t{\n\t\tif (bits & SBITS_BLEND_BITS)\n\t\t{\n\t\t\tD3DBLEND src;\n\t\t\tD3DBLEND dst;\n\n\t\t\tswitch(bits & SBITS_SRCBLEND_BITS)\n\t\t\t{\n\t\t\tcase SBITS_SRCBLEND_ZERO:\t\t\t\t\tsrc = D3DBLEND_ZERO; break;\n\t\t\tcase SBITS_SRCBLEND_ONE:\t\t\t\t\tsrc = D3DBLEND_ONE; break;\n\t\t\tcase SBITS_SRCBLEND_DST_COLOR:\t\t\t\tsrc = D3DBLEND_DESTCOLOR; break;\n\t\t\tcase SBITS_SRCBLEND_ONE_MINUS_DST_COLOR:\tsrc = D3DBLEND_INVDESTCOLOR; break;\n\t\t\tcase SBITS_SRCBLEND_SRC_ALPHA:\t\t\t\tsrc = D3DBLEND_SRCALPHA; break;\n\t\t\tcase SBITS_SRCBLEND_ONE_MINUS_SRC_ALPHA:\tsrc = D3DBLEND_INVSRCALPHA; break;\n\t\t\tcase SBITS_SRCBLEND_DST_ALPHA:\t\t\t\tsrc = D3DBLEND_DESTALPHA; break;\n\t\t\tcase SBITS_SRCBLEND_ONE_MINUS_DST_ALPHA:\tsrc = D3DBLEND_INVDESTALPHA; break;\n\t\t\tcase SBITS_SRCBLEND_ALPHA_SATURATE:\t\t\tsrc = D3DBLEND_SRCALPHASAT; break;\n\t\t\tdefault:\tSys_Error(\"Bad shader blend src\\n\"); return;\n\t\t\t}\n\t\t\tswitch(bits & SBITS_DSTBLEND_BITS)\n\t\t\t{\n\t\t\tcase SBITS_DSTBLEND_ZERO:\t\t\t\t\tdst = D3DBLEND_ZERO; break;\n\t\t\tcase SBITS_DSTBLEND_ONE:\t\t\t\t\tdst = D3DBLEND_ONE; break;\n\t\t\tcase SBITS_DSTBLEND_SRC_ALPHA:\t\t\t\tdst = D3DBLEND_SRCALPHA; break;\n\t\t\tcase SBITS_DSTBLEND_ONE_MINUS_SRC_ALPHA:\tdst = D3DBLEND_INVSRCALPHA; break;\n\t\t\tcase SBITS_DSTBLEND_DST_ALPHA:\t\t\t\tdst = D3DBLEND_DESTALPHA; break;\n\t\t\tcase SBITS_DSTBLEND_ONE_MINUS_DST_ALPHA:\tdst = D3DBLEND_INVDESTALPHA; break;\n\t\t\tcase SBITS_DSTBLEND_SRC_COLOR:\t\t\t\tdst = D3DBLEND_SRCCOLOR; break;\n\t\t\tcase SBITS_DSTBLEND_ONE_MINUS_SRC_COLOR:\tdst = D3DBLEND_INVSRCCOLOR; break;\n\t\t\tdefault:\tSys_Error(\"Bad shader blend dst\\n\"); return;\n\t\t\t}\n\t\t\tIDirect3DDevice9_SetRenderState(pD3DDev9, D3DRS_ALPHABLENDENABLE, TRUE);\n\t\t\tIDirect3DDevice9_SetRenderState(pD3DDev9, D3DRS_SRCBLEND, src);\n\t\t\tIDirect3DDevice9_SetRenderState(pD3DDev9, D3DRS_DESTBLEND, dst);\n\t\t}\n\t\telse\n\t\t\tIDirect3DDevice9_SetRenderState(pD3DDev9, D3DRS_ALPHABLENDENABLE, FALSE);\n\t}\n\n\tif (delta & SBITS_ATEST_BITS)\n\t{\n\t\tswitch(bits & SBITS_ATEST_BITS)\n\t\t{\n\t\tcase SBITS_ATEST_NONE:\n\t\t\tIDirect3DDevice9_SetRenderState(pD3DDev9, D3DRS_ALPHATESTENABLE, FALSE);\n\t//\t\tIDirect3DDevice9_SetRenderState(pD3DDev9, D3DRS_ALPHAREF, 0);\n\t//\t\tIDirect3DDevice9_SetRenderState(pD3DDev9, D3DRS_ALPHAFUNC, 0);\n\t\t\tbreak;\n\t\tcase SBITS_ATEST_GT0:\n\t\t\tIDirect3DDevice9_SetRenderState(pD3DDev9, D3DRS_ALPHATESTENABLE, TRUE);\n\t\t\tIDirect3DDevice9_SetRenderState(pD3DDev9, D3DRS_ALPHAREF, 0);\n\t\t\tIDirect3DDevice9_SetRenderState(pD3DDev9, D3DRS_ALPHAFUNC, D3DCMP_GREATER);\n\t\t\tbreak;\n\t\tcase SBITS_ATEST_LT128:\n\t\t\tIDirect3DDevice9_SetRenderState(pD3DDev9, D3DRS_ALPHATESTENABLE, TRUE);\n\t\t\tIDirect3DDevice9_SetRenderState(pD3DDev9, D3DRS_ALPHAREF, 128);\n\t\t\tIDirect3DDevice9_SetRenderState(pD3DDev9, D3DRS_ALPHAFUNC, D3DCMP_LESS);\n\t\t\tbreak;\n\t\tcase SBITS_ATEST_GE128:\n\t\t\tIDirect3DDevice9_SetRenderState(pD3DDev9, D3DRS_ALPHATESTENABLE, TRUE);\n\t\t\tIDirect3DDevice9_SetRenderState(pD3DDev9, D3DRS_ALPHAREF, 128);\n\t\t\tIDirect3DDevice9_SetRenderState(pD3DDev9, D3DRS_ALPHAFUNC, D3DCMP_GREATEREQUAL);\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tif (delta & SBITS_MISC_DEPTHWRITE)\n\t{\n\t\tif (bits & SBITS_MISC_DEPTHWRITE)\n\t\t\tIDirect3DDevice9_SetRenderState(pD3DDev9, D3DRS_ZWRITEENABLE, TRUE);\n\t\telse\n\t\t\tIDirect3DDevice9_SetRenderState(pD3DDev9, D3DRS_ZWRITEENABLE, FALSE);\n\t}\n\n\tif(delta & SBITS_MISC_NODEPTHTEST)\n\t{\n\t\tif(bits & SBITS_MISC_NODEPTHTEST)\n\t\t\tIDirect3DDevice9_SetRenderState(pD3DDev9, D3DRS_ZENABLE, FALSE);\n\t\telse\n\t\t\tIDirect3DDevice9_SetRenderState(pD3DDev9, D3DRS_ZENABLE, TRUE);\n\t}\n\n\tif (delta & SBITS_DEPTHFUNC_BITS)\n\t{\n\t\tswitch(bits & SBITS_DEPTHFUNC_BITS)\n\t\t{\n\t\tdefault:\n\t\tcase SBITS_DEPTHFUNC_CLOSEREQUAL:\n\t\t\tIDirect3DDevice9_SetRenderState(pD3DDev9, D3DRS_ZFUNC, D3DCMP_LESSEQUAL);\n\t\t\tbreak;\n\t\tcase SBITS_DEPTHFUNC_EQUAL:\n\t\t\tIDirect3DDevice9_SetRenderState(pD3DDev9, D3DRS_ZFUNC, D3DCMP_EQUAL);\n\t\t\tbreak;\n\t\tcase SBITS_DEPTHFUNC_CLOSER:\n\t\t\tIDirect3DDevice9_SetRenderState(pD3DDev9, D3DRS_ZFUNC, D3DCMP_LESS);\n\t\t\tbreak;\n\t\tcase SBITS_DEPTHFUNC_FURTHER:\n\t\t\tIDirect3DDevice9_SetRenderState(pD3DDev9, D3DRS_ZFUNC, D3DCMP_GREATER);\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tif (delta & (SBITS_MASK_BITS))\n\t{\n\t\tIDirect3DDevice9_SetRenderState(pD3DDev9, D3DRS_COLORWRITEENABLE,\n\t\t\t\t((bits&SBITS_MASK_RED)?0:D3DCOLORWRITEENABLE_RED) |\n\t\t\t\t((bits&SBITS_MASK_GREEN)?0:D3DCOLORWRITEENABLE_GREEN) |\n\t\t\t\t((bits&SBITS_MASK_BLUE)?0:D3DCOLORWRITEENABLE_BLUE) |\n\t\t\t\t((bits&SBITS_MASK_ALPHA)?0:D3DCOLORWRITEENABLE_ALPHA));\n\t}\n}\n\nvoid D3D9BE_Reset(qboolean before)\n{\n\tint i, tmu;\n\tif (before)\n\t{\n\t\tIDirect3DDevice9_SetVertexDeclaration(pD3DDev9, NULL);\n\t\tshaderstate.curvertdecl = 0;\n\t\tfor (i = 0; i < STRM_MAX; i++)\n\t\t\tIDirect3DDevice9_SetStreamSource(pD3DDev9, i, NULL, 0, 0);\n\t\tIDirect3DDevice9_SetIndices(pD3DDev9, NULL);\n\n\t\tif (shaderstate.dynxyz_buff)\n\t\t\tIDirect3DVertexBuffer9_Release(shaderstate.dynxyz_buff);\n\t\tshaderstate.dynxyz_buff = NULL;\n\t\tfor (tmu = 0; tmu < MAX_TC_TMUS; tmu++)\n\t\t{\n\t\t\tif (shaderstate.dynst_buff[tmu])\n\t\t\t\tIDirect3DVertexBuffer9_Release(shaderstate.dynst_buff[tmu]);\n\t\t\tshaderstate.dynst_buff[tmu] = NULL;\n\t\t}\n\t\tif (shaderstate.dynnorm_buff)\n\t\t\tIDirect3DVertexBuffer9_Release(shaderstate.dynnorm_buff);\n\t\tshaderstate.dynnorm_buff = NULL;\n\t\tif (shaderstate.dyncol_buff)\n\t\t\tIDirect3DVertexBuffer9_Release(shaderstate.dyncol_buff);\n\t\tshaderstate.dyncol_buff = NULL;\n\t\tif (shaderstate.dynidx_buff)\n\t\t\tIDirect3DIndexBuffer9_Release(shaderstate.dynidx_buff);\n\t\tshaderstate.dynidx_buff = NULL;\n\n\t\tfor (i = 0; i < D3D_VDEC_MAX; i++)\n\t\t{\n\t\t\tif (vertexdecls[i])\n\t\t\t\tIDirect3DVertexDeclaration9_Release(vertexdecls[i]);\n\t\t\tvertexdecls[i] = NULL;\n\t\t}\n\t}\n\telse\n\t{\n\t\tD3DVERTEXELEMENT9 decl[13], declend=D3DDECL_END();\n\t\tint elements;\n\n\t\tif (shaderstate.dynidx_buff)\n\t\t\treturn;\n\n\t\tfor (i = 0; i < D3D_VDEC_MAX; i++)\n\t\t{\n\t\t\telements = 0;\n\t\t\tdecl[elements].Stream = STRM_VERT;\n\t\t\tdecl[elements].Offset = 0;\n\t\t\tdecl[elements].Type = D3DDECLTYPE_FLOAT3;\n\t\t\tdecl[elements].Method = D3DDECLMETHOD_DEFAULT;\n\t\t\tdecl[elements].Usage = D3DDECLUSAGE_POSITION;\n\t\t\tdecl[elements].UsageIndex = 0;\n\t\t\telements++;\n\n\t\t\tif (i & D3D_VDEC_POS2)\n\t\t\t{\n\t\t\t\tdecl[elements].Stream = STRM_VERT2;\n\t\t\t\tdecl[elements].Offset = 0;\n\t\t\t\tdecl[elements].Type = D3DDECLTYPE_FLOAT3;\n\t\t\t\tdecl[elements].Method = D3DDECLMETHOD_DEFAULT;\n\t\t\t\tdecl[elements].Usage = D3DDECLUSAGE_POSITION;\n\t\t\t\tdecl[elements].UsageIndex = 1;\n\t\t\t\telements++;\n\t\t\t}\n\n\t\t\tif (i & D3D_VDEC_COL4B)\n\t\t\t{\n\t\t\t\tdecl[elements].Stream = STRM_COL;\n\t\t\t\tdecl[elements].Offset = 0;\n\t\t\t\tdecl[elements].Type = D3DDECLTYPE_D3DCOLOR;\n\t\t\t\tdecl[elements].Method = D3DDECLMETHOD_DEFAULT;\n\t\t\t\tdecl[elements].Usage = D3DDECLUSAGE_COLOR;\n\t\t\t\tdecl[elements].UsageIndex = 0;\n\t\t\t\telements++;\n\t\t\t}\n\n\t\t\tif (i & D3D_VDEC_NORM)\n\t\t\t{\n\t\t\t\tdecl[elements].Stream = STRM_NORM;\n\t\t\t\tdecl[elements].Offset = 0;\n\t\t\t\tdecl[elements].Type = D3DDECLTYPE_FLOAT3;\n\t\t\t\tdecl[elements].Method = D3DDECLMETHOD_DEFAULT;\n\t\t\t\tdecl[elements].Usage = D3DDECLUSAGE_NORMAL;\n\t\t\t\tdecl[elements].UsageIndex = 0;\n\t\t\t\telements++;\n\n\t\t\t\tdecl[elements].Stream = STRM_NORMS;\n\t\t\t\tdecl[elements].Offset = 0;\n\t\t\t\tdecl[elements].Type = D3DDECLTYPE_FLOAT3;\n\t\t\t\tdecl[elements].Method = D3DDECLMETHOD_DEFAULT;\n\t\t\t\tdecl[elements].Usage = D3DDECLUSAGE_TANGENT;\n\t\t\t\tdecl[elements].UsageIndex = 0;\n\t\t\t\telements++;\n\n\t\t\t\tdecl[elements].Stream = STRM_NORMT;\n\t\t\t\tdecl[elements].Offset = 0;\n\t\t\t\tdecl[elements].Type = D3DDECLTYPE_FLOAT3;\n\t\t\t\tdecl[elements].Method = D3DDECLMETHOD_DEFAULT;\n\t\t\t\tdecl[elements].Usage = D3DDECLUSAGE_BINORMAL;\n\t\t\t\tdecl[elements].UsageIndex = 0;\n\t\t\t\telements++;\n\t\t\t}\n\n\t\t\tfor (tmu = 0; tmu < MAX_TC_TMUS; tmu++)\n\t\t\t{\n\t\t\t\tif (i & (D3D_VDEC_ST0<<tmu))\n\t\t\t\t{\n\t\t\t\t\tdecl[elements].Stream = STRM_TC0+tmu;\n\t\t\t\t\tdecl[elements].Offset = 0;\n\t\t\t\t\tif (i & D3D_VDEC_CM)\n\t\t\t\t\t\tdecl[elements].Type = D3DDECLTYPE_FLOAT3;\n\t\t\t\t\telse\n\t\t\t\t\t\tdecl[elements].Type = D3DDECLTYPE_FLOAT2;\n\t\t\t\t\tdecl[elements].Method = D3DDECLMETHOD_DEFAULT;\n\t\t\t\t\tdecl[elements].Usage = D3DDECLUSAGE_TEXCOORD;\n\t\t\t\t\tdecl[elements].UsageIndex = tmu;\n\t\t\t\t\telements++;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (i & D3D_VDEC_SKEL)\n\t\t\t{\n\t\t\t\tdecl[elements].Stream = STRM_BONEWEIGHT;\n\t\t\t\tdecl[elements].Offset = 0;\n\t\t\t\tdecl[elements].Type = D3DDECLTYPE_FLOAT3;\n\t\t\t\tdecl[elements].Method = D3DDECLMETHOD_DEFAULT;\n\t\t\t\tdecl[elements].Usage = D3DDECLUSAGE_BLENDWEIGHT;\n\t\t\t\tdecl[elements].UsageIndex = 0;\n\t\t\t\telements++;\n\n\t\t\t\tdecl[elements].Stream = STRM_BONENUM;\n\t\t\t\tdecl[elements].Offset = 0;\n\t\t\t\tdecl[elements].Type = D3DDECLTYPE_FLOAT3;\n\t\t\t\tdecl[elements].Method = D3DDECLMETHOD_DEFAULT;\n\t\t\t\tdecl[elements].Usage = D3DDECLUSAGE_BLENDINDICES;\n\t\t\t\tdecl[elements].UsageIndex = 0;\n\t\t\t\telements++;\n\t\t\t}\n\n\t\t\tdecl[elements] = declend;\n\t\t\telements++;\n\n\t\t\tIDirect3DDevice9_CreateVertexDeclaration(pD3DDev9, decl, &vertexdecls[i]);\n\t\t}\n\n\t\tIDirect3DDevice9_CreateVertexBuffer(pD3DDev9, shaderstate.dynxyz_size, D3DUSAGE_DYNAMIC|D3DUSAGE_WRITEONLY, 0, D3DPOOL_DEFAULT, &shaderstate.dynxyz_buff, NULL);\n\t\tfor (tmu = 0; tmu < MAX_TC_TMUS; tmu++)\n\t\t\tIDirect3DDevice9_CreateVertexBuffer(pD3DDev9, shaderstate.dynst_size, D3DUSAGE_DYNAMIC|D3DUSAGE_WRITEONLY, 0, D3DPOOL_DEFAULT, &shaderstate.dynst_buff[tmu], NULL);\n\t\tIDirect3DDevice9_CreateVertexBuffer(pD3DDev9, shaderstate.dynnorm_size, D3DUSAGE_DYNAMIC|D3DUSAGE_WRITEONLY, 0, D3DPOOL_DEFAULT, &shaderstate.dynnorm_buff, NULL);\n\t\tIDirect3DDevice9_CreateVertexBuffer(pD3DDev9, shaderstate.dyncol_size, D3DUSAGE_DYNAMIC|D3DUSAGE_WRITEONLY, 0, D3DPOOL_DEFAULT, &shaderstate.dyncol_buff, NULL);\n\t\tIDirect3DDevice9_CreateIndexBuffer(pD3DDev9, shaderstate.dynidx_size, D3DUSAGE_DYNAMIC|D3DUSAGE_WRITEONLY, D3DFMT_QINDEX, D3DPOOL_DEFAULT, &shaderstate.dynidx_buff, NULL);\n\n\t\tfor (i = 0; i < MAX_TMUS; i++)\n\t\t{\n\t\t\tshaderstate.tmuflags[i] = ~0;\n\t\t\tBE_ApplyTMUState(i, 0);\n\t\t}\n\n\t\t/*force all state to change, thus setting a known state*/\n\t\tshaderstate.shaderbits = ~0;\n\t\tBE_ApplyShaderBits(0);\n\n\t\tshaderstate.gltod3d_depthunit = 1.0/((1u<<16) - 1u);\t//erk, panic.\n\t\t{\n\t\t\tIDirect3DSurface9 *surf = NULL;\n\t\t\tD3DSURFACE_DESC desc = {D3DFMT_D16};\n\t\t\tif (SUCCEEDED(IDirect3DDevice9_GetDepthStencilSurface(pD3DDev9, &surf)))\n\t\t\t{\n\t\t\t\tIDirect3DSurface9_GetDesc(surf, &desc);\n\t\t\t\tIDirect3DSurface9_Release(surf);\n\t\t\t\tswitch(desc.Format)\n\t\t\t\t{\n\t\t\t\tcase D3DFMT_D15S1:\n\t\t\t\t\tshaderstate.gltod3d_depthunit = 1.0/((1u<<15) - 1u);\n\t\t\t\t\tbreak;\n\t\t\t\tcase D3DFMT_D16_LOCKABLE:\n\t\t\t\tcase D3DFMT_D16:\n\t\t\t\t\tshaderstate.gltod3d_depthunit = 1.0/((1u<<24) - 1u);\n\t\t\t\t\tbreak;\n\t\t\t\tcase D3DFMT_D24S8:\n\t\t\t\tcase D3DFMT_D24X8:\n\t\t\t\tcase D3DFMT_D24X4S4:\n\t\t\t\t\tshaderstate.gltod3d_depthunit = 1.0/((1u<<24) - 1u);\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\tcase D3DFMT_D32:\n\t\t\t\t\tshaderstate.gltod3d_depthunit = 1.0/(quint32_t)(-1);\n\t\t\t\t\tbreak;\n\n\t\t\t\t//floating point depth formats are just messy as heck.\n\t\t\t\t//I guess the definition issue here is more with opengl, and I don't know what it would say for 1 'notch', so lets just go by mantissa size.\n\t\t\t\tcase D3DFMT_D24FS8:\t\t\t//20e4\n\t\t\t\t\tshaderstate.gltod3d_depthunit = 1.0/((1u<<20) - 1u);\n\t\t\t\t\tbreak;\n\t\t\t\tcase D3DFMT_D32F_LOCKABLE:\t//23e8s1\n\t\t\t\t\tshaderstate.gltod3d_depthunit = 1.0/((1u<<23) - 1u);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nstatic const char LIGHTPASS_SHADER[] = \"\\\n{\\n\\\n\tprogram rtlight\\n\\\n\t{\\n\\\n\t\tmap $diffuse\\n\\\n\t\tblendfunc add\\n\\\n\t}\\n\\\n\t{\\n\\\n\t\tmap $normalmap\\n\\\n\t}\\n\\\n\t{\\n\\\n\t\tmap $specular\\n\\\n\t}\\n\\\n}\";\n\nvoid D3D9BE_Init(void)\n{\n\tbe_maxpasses = MAX_TC_TMUS;\n\tmemset(&shaderstate, 0, sizeof(shaderstate));\n\tshaderstate.curvertdecl = -1;\n\tshaderstate.curentity = &r_worldentity;\n\n\tFTable_Init();\n\n\tshaderstate.dynxyz_size = sizeof(vecV_t) * DYNVBUFFSIZE;\n\tshaderstate.dyncol_size = sizeof(byte_vec4_t) * DYNVBUFFSIZE;\n\tshaderstate.dynnorm_size = sizeof(vec3_t)*3 * DYNVBUFFSIZE;\n\tshaderstate.dynst_size = sizeof(vec2_t) * DYNVBUFFSIZE;\n\tshaderstate.dynidx_size = sizeof(index_t) * DYNIBUFFSIZE;\n\n\tD3D9BE_Reset(false);\n\n\tshaderstate.shader_rtlight = R_RegisterShader(\"rtlight\", SUF_NONE, LIGHTPASS_SHADER);\n\n\tR_InitFlashblends();\n}\n\nvoid D3D9BE_Set2D(void)\n{\t//start of some 2d rendering code.\n\tr_refdef.time = realtime;\n\tshaderstate.curtime = r_refdef.time;\n}\n\nvoid D3D9BE_SetViewport(int x, int w, int y, int h)\n{\n\tshaderstate.vport.X = x;\n\tshaderstate.vport.Y = y;\n\tshaderstate.vport.Width = w;\n\tshaderstate.vport.Height = h;\n\tshaderstate.vport.MinZ = 0;\n\tshaderstate.vport.MaxZ = 1;\n\tIDirect3DDevice9_SetViewport(pD3DDev9, &shaderstate.vport);\n\n\tshaderstate.curprojection = d3d_trueprojection_std;\n\tIDirect3DDevice9_SetTransform(pD3DDev9, D3DTS_PROJECTION, (D3DMATRIX*)shaderstate.curprojection);\n}\n\nstatic void allocvertexbuffer(IDirect3DVertexBuffer9 *buff, unsigned int bmaxsize, unsigned int *offset, void **data, unsigned int bytes)\n{\n\tunsigned int boff;\n\tif (*offset + bytes > bmaxsize)\n\t{\n\t\tboff = 0;\n\t\t*offset = bytes;\n\t}\n\telse\n\t{\n\t\tboff = *offset;\n\t\t*offset += bytes;\n\t}\n\td3dcheck(IDirect3DVertexBuffer9_Lock(buff, boff, bytes, data, boff?D3DLOCK_NOOVERWRITE:D3DLOCK_DISCARD));\n}\n\nstatic unsigned int allocindexbuffer(void **dest, unsigned int entries)\n{\n\tunsigned int bytes = entries*sizeof(index_t);\n\tunsigned int offset;\n\n\tif (shaderstate.dynidx_offs + bytes > shaderstate.dynidx_size)\n\t{\n\t\toffset = 0;\n\t\tshaderstate.dynidx_offs = 0;\n\t}\n\telse\n\t{\n\t\toffset = shaderstate.dynidx_offs;\n\t\tshaderstate.dynidx_offs += bytes;\n\t}\n\n\td3dcheck(IDirect3DIndexBuffer9_Lock(shaderstate.dynidx_buff, offset, (unsigned int)entries, dest, offset?D3DLOCK_NOOVERWRITE:D3DLOCK_DISCARD));\n\treturn offset/sizeof(index_t);\n}\n\nstatic void BindTexture(unsigned int tu, texid_t tex)\n{\n\textern texid_t r_whiteimage;\n\tIDirect3DTexture9 *dt;\n\tif (tex)\n\t{\n\t\tdt = tex->ptr;\n\t\tif (!dt)\n\t\t\tdt = r_whiteimage->ptr;\n\t\tshaderstate.curtexflags[tu] = tex->flags & SHADER_PASS_IMAGE_FLAGS_D3D9;\n\t}\n\telse\n\t\tdt = NULL;\n\tif (shaderstate.curtex[tu] != dt)\n\t{\n\t\tshaderstate.curtex[tu] = dt;\n\t\tIDirect3DDevice9_SetTexture (pD3DDev9, tu, (void*)dt);\n\t}\n}\n\nstatic void SelectPassTexture(unsigned int tu, shaderpass_t *pass)\n{\n\tint last;\n\textern texid_t missing_texture;\n\textern texid_t missing_texture_gloss;\n\textern texid_t missing_texture_normal;\n\textern texid_t r_blackimage;\n\n\tswitch(pass->texgen)\n\t{\n\tdefault:\n\tcase T_GEN_DIFFUSE:\n\t\tif (TEXLOADED(shaderstate.curtexnums->base))\n\t\t\tBindTexture(tu, shaderstate.curtexnums->base);\n\t\telse\n\t\t\tBindTexture(tu, missing_texture);\n\t\tbreak;\n\tcase T_GEN_NORMALMAP:\n\t\tif (TEXLOADED(shaderstate.curtexnums->bump))\n\t\t\tBindTexture(tu, shaderstate.curtexnums->bump);\n\t\telse\n\t\t\tBindTexture(tu, missing_texture_normal);\n\t\tbreak;\n\tcase T_GEN_SPECULAR:\n\t\tif (TEXLOADED(shaderstate.curtexnums->specular))\n\t\t\tBindTexture(tu, shaderstate.curtexnums->specular);\n\t\telse\n\t\t\tBindTexture(tu, missing_texture_gloss);\n\t\tbreak;\n\tcase T_GEN_UPPEROVERLAY:\n\t\tif (TEXLOADED(shaderstate.curtexnums->upperoverlay))\n\t\t\tBindTexture(tu, shaderstate.curtexnums->upperoverlay);\n\t\telse\n\t\t\tBindTexture(tu, r_blackimage);\n\t\tbreak;\n\tcase T_GEN_LOWEROVERLAY:\n\t\tif (TEXLOADED(shaderstate.curtexnums->loweroverlay))\n\t\t\tBindTexture(tu, shaderstate.curtexnums->loweroverlay);\n\t\telse\n\t\t\tBindTexture(tu, r_blackimage);\n\t\tbreak;\n\tcase T_GEN_FULLBRIGHT:\n\t\tif (TEXLOADED(shaderstate.curtexnums->fullbright))\n\t\t\tBindTexture(tu, shaderstate.curtexnums->fullbright);\n\t\telse\n\t\t\tBindTexture(tu, r_blackimage);\n\t\tbreak;\n\tcase T_GEN_REFLECTCUBE:\n\t\tif (TEXLOADED(shaderstate.curtexnums->reflectcube))\n\t\t\tBindTexture(tu, shaderstate.curtexnums->reflectcube);\n\t\telse\n\t\t\tBindTexture(tu, r_whiteimage);\n\t\tbreak;\n\tcase T_GEN_REFLECTMASK:\n\t\tif (TEXLOADED(shaderstate.curtexnums->reflectmask))\n\t\t\tBindTexture(tu, shaderstate.curtexnums->reflectmask);\n\t\telse\n\t\t\tBindTexture(tu, r_whiteimage);\n\t\tbreak;\n\tcase T_GEN_ANIMMAP:\n\t\tBindTexture(tu, pass->anim_frames[(int)(pass->anim_fps * shaderstate.curtime) % pass->anim_numframes]);\n\t\tbreak;\n\tcase T_GEN_SINGLEMAP:\n\t\tBindTexture(tu, pass->anim_frames[0]);\n\t\tbreak;\n\tcase T_GEN_DELUXMAP:\n\t\tBindTexture(tu, shaderstate.curdeluxmap);\n\t\tbreak;\n\tcase T_GEN_LIGHTMAP:\n\t\tBindTexture(tu, shaderstate.curlightmap);\n\t\tbreak;\n\n\t/*case T_GEN_CURRENTRENDER:\n\t\tFIXME: no code to grab the current screen and convert to a texture\n\t\tbreak;*/\n\tcase T_GEN_VIDEOMAP:\n#ifdef HAVE_MEDIA_DECODER\n\t\tBindTexture(tu, Media_UpdateForShader(pass->cin));\n#else\n\t\tBindTexture(tu, missing_texture);\n#endif\n\t\tbreak;\n\t}\n\n\tBE_ApplyTMUState(tu, shaderstate.curtexflags[tu]|pass->flags);\n\n\tif (tu == 0)\n\t{\n\t\tif (shaderstate.passsinglecolour)\n\t\t{\n\t\t\tIDirect3DDevice9_SetTextureStageState(pD3DDev9, tu, D3DTSS_CONSTANT, shaderstate.passcolour);\n\t\t\tlast = D3DTA_CONSTANT;\n\t\t\tIDirect3DDevice9_SetRenderState(pD3DDev9, D3DRS_COLORVERTEX, FALSE);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tIDirect3DDevice9_SetRenderState(pD3DDev9, D3DRS_COLORVERTEX, TRUE);\n//\t\t\tIDirect3DDevice9_SetRenderState(pD3DDev9, D3DRS_DIFFUSEMATERIALSOURCE, D3DMCS_COLOR1);\t//default state anyway.\n\t\t\tlast = D3DTA_DIFFUSE;\n\t\t}\n\t}\n\telse\n\t\tlast = D3DTA_CURRENT;\n\n\tswitch (pass->blendmode)\n\t{\n\tcase PBM_DOTPRODUCT:\n\t\tIDirect3DDevice9_SetTextureStageState(pD3DDev9, tu, D3DTSS_COLORARG1, last);\n\t\tIDirect3DDevice9_SetTextureStageState(pD3DDev9, tu, D3DTSS_COLORARG2, D3DTA_TEXTURE);\n\t\tIDirect3DDevice9_SetTextureStageState(pD3DDev9, tu, D3DTSS_COLOROP, D3DTOP_DOTPRODUCT3);\n\n\t\tIDirect3DDevice9_SetTextureStageState(pD3DDev9, tu, D3DTSS_ALPHAARG1, last);\n\t\tIDirect3DDevice9_SetTextureStageState(pD3DDev9, tu, D3DTSS_ALPHAARG2, D3DTA_TEXTURE);\n\t\tIDirect3DDevice9_SetTextureStageState(pD3DDev9, tu, D3DTSS_ALPHAOP, D3DTOP_SELECTARG2);\n\t\tbreak;\n\tcase PBM_REPLACE:\n\t\tIDirect3DDevice9_SetTextureStageState(pD3DDev9, tu, D3DTSS_COLORARG1, last);\n\t\tIDirect3DDevice9_SetTextureStageState(pD3DDev9, tu, D3DTSS_COLORARG2, D3DTA_TEXTURE);\n\t\tIDirect3DDevice9_SetTextureStageState(pD3DDev9, tu, D3DTSS_COLOROP, D3DTOP_SELECTARG2);\n\n\t\tif (shaderstate.flags & (BEF_FORCETRANSPARENT | BEF_FORCEADDITIVE))\n\t\t{\n\t\t\tIDirect3DDevice9_SetTextureStageState(pD3DDev9, tu, D3DTSS_ALPHAARG1, last);\n\t\t\tIDirect3DDevice9_SetTextureStageState(pD3DDev9, tu, D3DTSS_ALPHAARG2, D3DTA_TEXTURE);\n\t\t\tIDirect3DDevice9_SetTextureStageState(pD3DDev9, tu, D3DTSS_ALPHAOP, D3DTOP_MODULATE);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tIDirect3DDevice9_SetTextureStageState(pD3DDev9, tu, D3DTSS_ALPHAARG1, last);\n\t\t\tIDirect3DDevice9_SetTextureStageState(pD3DDev9, tu, D3DTSS_ALPHAARG2, D3DTA_TEXTURE);\n\t\t\tIDirect3DDevice9_SetTextureStageState(pD3DDev9, tu, D3DTSS_ALPHAOP, D3DTOP_MODULATE);\n\t\t}\n\t\tbreak;\n\tcase PBM_ADD:\n\t\tif (tu == 0)\n\t\t\tgoto forcemod;\n\t\tIDirect3DDevice9_SetTextureStageState(pD3DDev9, tu, D3DTSS_COLORARG1, last);\n\t\tIDirect3DDevice9_SetTextureStageState(pD3DDev9, tu, D3DTSS_COLORARG2, D3DTA_TEXTURE);\n\t\tIDirect3DDevice9_SetTextureStageState(pD3DDev9, tu, D3DTSS_COLOROP, D3DTOP_ADD);\n\n\t\tIDirect3DDevice9_SetTextureStageState(pD3DDev9, tu, D3DTSS_ALPHAARG1, last);\n\t\tIDirect3DDevice9_SetTextureStageState(pD3DDev9, tu, D3DTSS_ALPHAARG2, D3DTA_TEXTURE);\n\t\tIDirect3DDevice9_SetTextureStageState(pD3DDev9, tu, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);\n\t\tbreak;\n\tcase PBM_DECAL:\n\t\tif (!tu)\n\t\t\tgoto forcemod;\n\t\tIDirect3DDevice9_SetTextureStageState(pD3DDev9, tu, D3DTSS_COLORARG1, D3DTA_TEXTURE);\n\t\tIDirect3DDevice9_SetTextureStageState(pD3DDev9, tu, D3DTSS_COLORARG2, last);\n\t\tIDirect3DDevice9_SetTextureStageState(pD3DDev9, tu, D3DTSS_COLOROP, D3DTOP_BLENDTEXTUREALPHA);\n\n\t\tIDirect3DDevice9_SetTextureStageState(pD3DDev9, tu, D3DTSS_ALPHAARG1, last);\n\t\tIDirect3DDevice9_SetTextureStageState(pD3DDev9, tu, D3DTSS_ALPHAARG2, D3DTA_TEXTURE);\n\t\tIDirect3DDevice9_SetTextureStageState(pD3DDev9, tu, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);\n\t\tbreak;\n\tcase PBM_OVERBRIGHT:\n\t\tIDirect3DDevice9_SetTextureStageState(pD3DDev9, tu, D3DTSS_COLORARG1, last);\n\t\tIDirect3DDevice9_SetTextureStageState(pD3DDev9, tu, D3DTSS_COLORARG2, D3DTA_TEXTURE);\n\t\t{\n\t\t\textern cvar_t gl_overbright;\n\t\t\tswitch (gl_overbright.ival)\n\t\t\t{\n\t\t\tcase 1:\n\t\t\t\tIDirect3DDevice9_SetTextureStageState(pD3DDev9, tu, D3DTSS_COLOROP, D3DTOP_MODULATE2X);\n\t\t\t\tbreak;\n\t\t\tcase 2:\n\t\t\tcase 3:\n\t\t\t\tIDirect3DDevice9_SetTextureStageState(pD3DDev9, tu, D3DTSS_COLOROP, D3DTOP_MODULATE4X);\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tIDirect3DDevice9_SetTextureStageState(pD3DDev9, tu, D3DTSS_COLOROP, D3DTOP_MODULATE);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tIDirect3DDevice9_SetTextureStageState(pD3DDev9, tu, D3DTSS_ALPHAARG1, last);\n\t\tIDirect3DDevice9_SetTextureStageState(pD3DDev9, tu, D3DTSS_ALPHAARG2, D3DTA_TEXTURE);\n\t\tIDirect3DDevice9_SetTextureStageState(pD3DDev9, tu, D3DTSS_ALPHAOP, D3DTOP_MODULATE);\n\t\tbreak;\n\tdefault:\n\tcase PBM_MODULATE:\n\tforcemod:\n\t\tIDirect3DDevice9_SetTextureStageState(pD3DDev9, tu, D3DTSS_COLORARG1, last);\n\t\tIDirect3DDevice9_SetTextureStageState(pD3DDev9, tu, D3DTSS_COLORARG2, D3DTA_TEXTURE);\n\t\tIDirect3DDevice9_SetTextureStageState(pD3DDev9, tu, D3DTSS_COLOROP, D3DTOP_MODULATE);\n\n\t\tIDirect3DDevice9_SetTextureStageState(pD3DDev9, tu, D3DTSS_ALPHAARG1, last);\n\t\tIDirect3DDevice9_SetTextureStageState(pD3DDev9, tu, D3DTSS_ALPHAARG2, D3DTA_TEXTURE);\n\t\tIDirect3DDevice9_SetTextureStageState(pD3DDev9, tu, D3DTSS_ALPHAOP, D3DTOP_MODULATE);\n\t\tbreak;\n\t}\n}\n\nstatic void colourgenbyte(const shaderpass_t *pass, int cnt, byte_vec4_t *srcb, vec4_t *srcf, byte_vec4_t *dst, const mesh_t *mesh)\n{\n\tD3DCOLOR block;\n\tswitch (pass->rgbgen)\n\t{\n\tcase RGB_GEN_ENTITY:\n\t\tblock = D3DCOLOR_COLORVALUE(shaderstate.curentity->shaderRGBAf[0], shaderstate.curentity->shaderRGBAf[1], shaderstate.curentity->shaderRGBAf[2], shaderstate.curentity->shaderRGBAf[3]);\n\t\twhile((cnt)--)\n\t\t{\n\t\t\t((D3DCOLOR*)dst)[cnt] = block;\n\t\t}\n\t\tbreak;\n\tcase RGB_GEN_ONE_MINUS_ENTITY:\n\t\tblock = D3DCOLOR_COLORVALUE(1-shaderstate.curentity->shaderRGBAf[0], 1-shaderstate.curentity->shaderRGBAf[1], 1-shaderstate.curentity->shaderRGBAf[2], 1-shaderstate.curentity->shaderRGBAf[3]);\n\t\twhile((cnt)--)\n\t\t{\n\t\t\t((D3DCOLOR*)dst)[cnt] = block;\n\t\t}\n\t\tbreak;\n\tcase RGB_GEN_VERTEX_LIGHTING:\n\tcase RGB_GEN_VERTEX_EXACT:\n\t\tif (srcb)\n\t\t{\n\t\t\twhile((cnt)--)\n\t\t\t{\n\t\t\t\tqbyte r, g, b;\n\t\t\t\tr=srcb[cnt][0];\n\t\t\t\tg=srcb[cnt][1];\n\t\t\t\tb=srcb[cnt][2];\n\t\t\t\tdst[cnt][0] = b;\n\t\t\t\tdst[cnt][1] = g;\n\t\t\t\tdst[cnt][2] = r;\n\t\t\t}\n\t\t}\n\t\telse if (srcf)\n\t\t{\n\t\t\twhile((cnt)--)\n\t\t\t{\n\t\t\t\tint r, g, b;\n\t\t\t\tr=srcf[cnt][0]*255;\n\t\t\t\tg=srcf[cnt][1]*255;\n\t\t\t\tb=srcf[cnt][2]*255;\n\t\t\t\tdst[cnt][0] = bound(0, b, 255);\n\t\t\t\tdst[cnt][1] = bound(0, g, 255);\n\t\t\t\tdst[cnt][2] = bound(0, r, 255);\n\t\t\t}\n\t\t}\n\t\telse\n\t\t\tgoto identity;\n\t\tbreak;\n\tcase RGB_GEN_ONE_MINUS_VERTEX:\n\t\tif (srcb)\n\t\t{\n\t\t\twhile((cnt)--)\n\t\t\t{\n\t\t\t\tqbyte r, g, b;\n\t\t\t\tr=255-srcb[cnt][0];\n\t\t\t\tg=255-srcb[cnt][1];\n\t\t\t\tb=255-srcb[cnt][2];\n\t\t\t\tdst[cnt][0] = b;\n\t\t\t\tdst[cnt][1] = g;\n\t\t\t\tdst[cnt][2] = r;\n\t\t\t}\n\t\t}\n\t\telse if (srcf)\n\t\t{\n\t\t\twhile((cnt)--)\n\t\t\t{\n\t\t\t\tint r, g, b;\n\t\t\t\tr=255-srcf[cnt][0]*255;\n\t\t\t\tg=255-srcf[cnt][1]*255;\n\t\t\t\tb=255-srcf[cnt][2]*255;\n\t\t\t\tdst[cnt][0] = bound(0, b, 255);\n\t\t\t\tdst[cnt][1] = bound(0, g, 255);\n\t\t\t\tdst[cnt][2] = bound(0, r, 255);\n\t\t\t}\n\t\t}\n\t\telse\n\t\t\tgoto identity;\n\t\tbreak;\n\tcase RGB_GEN_IDENTITY_LIGHTING:\n\t\t//compensate for overbrights\n\t\tblock = D3DCOLOR_RGBA(255, 255, 255, 255); //shaderstate.identitylighting\n\t\twhile((cnt)--)\n\t\t{\n\t\t\t((D3DCOLOR*)dst)[cnt] = block;\n\t\t}\n\t\tbreak;\n\tdefault:\n\tidentity:\n\tcase RGB_GEN_UNKNOWN:\n\tcase RGB_GEN_IDENTITY_OVERBRIGHT:\n\tcase RGB_GEN_IDENTITY:\n\t\tblock = D3DCOLOR_RGBA(255, 255, 255, 255);\n\t\twhile((cnt)--)\n\t\t{\n\t\t\t((D3DCOLOR*)dst)[cnt] = block;\n\t\t}\n\t\tbreak;\n\tcase RGB_GEN_CONST:\n\t\tblock = D3DCOLOR_COLORVALUE(pass->rgbgen_func.args[0], pass->rgbgen_func.args[1], pass->rgbgen_func.args[2], 1);\n\t\twhile((cnt)--)\n\t\t{\n\t\t\t((D3DCOLOR*)dst)[cnt] = block;\n\t\t}\n\t\tbreak;\n\tcase RGB_GEN_ENTITY_LIGHTING_DIFFUSE:\n\t\tR_LightArraysByte_BGR(shaderstate.curentity , mesh->xyz_array, dst, cnt, mesh->normals_array, true);\n\t\tbreak;\n\tcase RGB_GEN_LIGHTING_DIFFUSE:\n\t\tR_LightArraysByte_BGR(shaderstate.curentity , mesh->xyz_array, dst, cnt, mesh->normals_array, false);\n\t\tbreak;\n\tcase RGB_GEN_WAVE:\n\t\t{\n\t\t\tfloat *table;\n\t\t\tfloat c;\n\n\t\t\ttable = FTableForFunc(pass->rgbgen_func.type);\n\t\t\tc = pass->rgbgen_func.args[2] + shaderstate.curtime * pass->rgbgen_func.args[3];\n\t\t\tc = FTABLE_EVALUATE(table, c) * pass->rgbgen_func.args[1] + pass->rgbgen_func.args[0];\n\t\t\tc = bound(0.0f, c, 1.0f);\n\t\t\tblock = D3DCOLOR_COLORVALUE(c, c, c, 1);\n\n\t\t\twhile((cnt)--)\n\t\t\t{\n\t\t\t\t((D3DCOLOR*)dst)[cnt] = block;\n\t\t\t}\n\t\t}\n\t\tbreak;\n\n\tcase RGB_GEN_TOPCOLOR:\n\tcase RGB_GEN_BOTTOMCOLOR:\n#ifdef warningmsg\n#pragma warningmsg(\"fix 24bit player colours\")\n#endif\n\t\tblock = D3DCOLOR_RGBA(255, 255, 255, 255);\n\t\twhile((cnt)--)\n\t\t{\n\t\t\t((D3DCOLOR*)dst)[cnt] = block;\n\t\t}\n\t//\tCon_Printf(\"RGB_GEN %i not supported\\n\", pass->rgbgen);\n\t\tbreak;\n\t}\n}\n\nstatic void alphagenbyte(const shaderpass_t *pass, int cnt, byte_vec4_t *srcb, vec4_t *srcf, byte_vec4_t *dst, const mesh_t *mesh)\n{\n\t/*FIXME: Skip this if the rgbgen did it*/\n\tfloat *table;\n\tunsigned char t;\n\tfloat f;\n\tvec3_t v1, v2;\n\n\tswitch (pass->alphagen)\n\t{\n\tdefault:\n\tcase ALPHA_GEN_IDENTITY:\n\t\tif (shaderstate.flags & BEF_FORCETRANSPARENT)\n\t\t{\n\t\t\tf = shaderstate.curentity->shaderRGBAf[3];\n\t\t\tif (f < 0)\n\t\t\t\tt = 0;\n\t\t\telse if (f >= 1)\n\t\t\t\tt = 255;\n\t\t\telse\n\t\t\t\tt = f*255;\n\t\t\twhile(cnt--)\n\t\t\t\tdst[cnt][3] = t;\n\t\t}\n\t\telse\n\t\t{\n\t\t\twhile(cnt--)\n\t\t\t\tdst[cnt][3] = 255;\n\t\t}\n\t\tbreak;\n\n\tcase ALPHA_GEN_CONST:\n\t\tt = pass->alphagen_func.args[0]*255;\n\t\twhile(cnt--)\n\t\t\tdst[cnt][3] = t;\n\t\tbreak;\n\n\tcase ALPHA_GEN_WAVE:\n\t\ttable = FTableForFunc(pass->alphagen_func.type);\n\t\tf = pass->alphagen_func.args[2] + shaderstate.curtime * pass->alphagen_func.args[3];\n\t\tf = FTABLE_EVALUATE(table, f) * pass->alphagen_func.args[1] + pass->alphagen_func.args[0];\n\t\tt = bound(0.0f, f, 1.0f)*255;\n\t\twhile(cnt--)\n\t\t\tdst[cnt][3] = t;\n\t\tbreak;\n\n\tcase ALPHA_GEN_PORTAL:\n\t\t//FIXME: should this be per-vert?\n\t\tVectorAdd(mesh->xyz_array[0], shaderstate.curentity->origin, v1);\n\t\tVectorSubtract(r_origin, v1, v2);\n\t\tf = VectorLength(v2) * (1.0 / 255.0);\n\t\tt = bound(0.0f, f, 1.0f)*255;\n\n\t\twhile(cnt--)\n\t\t\tdst[cnt][3] = t;\n\t\tbreak;\n\n\tcase ALPHA_GEN_VERTEX:\n\t\tif (srcb)\n\t\t{\n\t\t\twhile(cnt--)\n\t\t\t{\n\t\t\t\tdst[cnt][3] = srcb[cnt][3];\n\t\t\t}\n\t\t}\n\t\telse if (srcf)\n\t\t{\n\t\t\twhile(cnt--)\n\t\t\t{\n\t\t\t\tdst[cnt][3] = bound(0, srcf[cnt][3]*255, 255);\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\twhile(cnt--)\n\t\t\t{\n\t\t\t\tdst[cnt][3] = 255;\n\t\t\t}\n\t\t}\n\t\tbreak;\n\n\tcase ALPHA_GEN_ENTITY:\n\t\tt = bound(0, shaderstate.curentity->shaderRGBAf[3], 1)*255;\n\t\twhile(cnt--)\n\t\t{\n\t\t\tdst[cnt][3] = t;\n\t\t}\n\t\tbreak;\n\n\tcase ALPHA_GEN_SPECULAR:\n\t\t{\n\t\t\tint i;\n\t\t\tVectorSubtract(r_origin, shaderstate.curentity->origin, v1);\n\n\t\t\tif (!Matrix3_Compare(shaderstate.curentity->axis, (void *)axisDefault))\n\t\t\t{\n\t\t\t\tMatrix3_Multiply_Vec3(shaderstate.curentity->axis, v1, v2);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tVectorCopy(v1, v2);\n\t\t\t}\n\n\t\t\tfor (i = 0; i < cnt; i++)\n\t\t\t{\n\t\t\t\tVectorSubtract(v2, mesh->xyz_array[i], v1);\n\t\t\t\tf = DotProduct(v1, mesh->normals_array[i]) * Q_rsqrt(DotProduct(v1,v1));\n\t\t\t\tf = f * f * f * f * f;\n\t\t\t\tdst[i][3] = bound (0, (int)(f*255), 255);\n\t\t\t}\n\t\t}\n\t\tbreak;\n\t}\n}\n\nstatic unsigned int BE_GenerateColourMods(unsigned int vertcount, const shaderpass_t *pass)\n{\n\tunsigned int ret = 0;\n\tunsigned char *map;\n\tconst mesh_t *m;\n\tunsigned int mno;\n\n\tm = shaderstate.meshlist[0];\n\n\tif (pass->flags & SHADER_PASS_NOCOLORARRAY)\n\t{\n\t\tshaderstate.passsinglecolour = true;\n\t\tshaderstate.passcolour = D3DCOLOR_RGBA(255,255,255,255);\n\t\tcolourgenbyte(pass, 1, (byte_vec4_t*)&shaderstate.passcolour, NULL, (byte_vec4_t*)&shaderstate.passcolour, m);\n\t\talphagenbyte(pass, 1, (byte_vec4_t*)&shaderstate.passcolour, NULL, (byte_vec4_t*)&shaderstate.passcolour, m);\n\t\t/*FIXME: just because there's no rgba set, there's no reason to assume it should be a single colour (unshaded ents)*/\n\t\td3dcheck(IDirect3DDevice9_SetStreamSource(pD3DDev9, STRM_COL, NULL, 0, 0));\n\t}\n\telse\n\t{\n\t\tshaderstate.passsinglecolour = false;\n\n\t\tret |= D3D_VDEC_COL4B;\n\t\tif (shaderstate.batchvbo && (m->colors4f_array[0] &&\n\t\t\t\t\t\t((pass->rgbgen == RGB_GEN_VERTEX_LIGHTING) ||\n\t\t\t\t\t\t(pass->rgbgen == RGB_GEN_VERTEX_EXACT) ||\n\t\t\t\t\t\t(pass->rgbgen == RGB_GEN_ONE_MINUS_VERTEX)) &&\n\t\t\t\t\t\t(pass->alphagen == ALPHA_GEN_VERTEX)) && shaderstate.batchvbo->colours_bytes)\n\t\t{\n\t\t\t//fixme\n\t\t\td3dcheck(IDirect3DDevice9_SetStreamSource(pD3DDev9, STRM_COL, shaderstate.batchvbo->colours[0].d3d.buff, shaderstate.batchvbo->colours[0].d3d.offs, sizeof(vbovdata_t)));\n\t\t}\n\t\telse\n\t\t{\n\t\t\tallocvertexbuffer(shaderstate.dyncol_buff, shaderstate.dyncol_size, &shaderstate.dyncol_offs, (void**)&map, vertcount*sizeof(D3DCOLOR));\n\t\t\tfor (mno = 0; mno < shaderstate.nummeshes; mno++)\n\t\t\t{\n\t\t\t\tm = shaderstate.meshlist[mno];\n\t\t\t\tcolourgenbyte(pass, m->numvertexes, m->colors4b_array, m->colors4f_array[0], (byte_vec4_t*)map, m);\n\t\t\t\talphagenbyte(pass, m->numvertexes, m->colors4b_array, m->colors4f_array[0], (byte_vec4_t*)map, m);\n\t\t\t\tmap += m->numvertexes*4;\n\t\t\t}\n\t\t\td3dcheck(IDirect3DVertexBuffer9_Unlock(shaderstate.dyncol_buff));\n\t\t\td3dcheck(IDirect3DDevice9_SetStreamSource(pD3DDev9, STRM_COL, shaderstate.dyncol_buff, shaderstate.dyncol_offs - vertcount*sizeof(D3DCOLOR), sizeof(D3DCOLOR)));\n\t\t}\n\t}\n\treturn ret;\n}\n/*********************************************************************************************************/\n/*========================================== texture coord generation =====================================*/\n\nstatic void tcgen_environment(float *st, unsigned int numverts, float *xyz, float *normal)\n{\n\tint\t\t\ti;\n\tvec3_t\t\tviewer, reflected;\n\tfloat\t\td;\n\n\tvec3_t\t\trorg;\n\n\tif (!normal)\n\t{\n\t\tfor (i = 0 ; i < numverts ; i++, st += 2 )\n\t\t{\n\t\t\tst[0] = xyz[0];\n\t\t\tst[1] = xyz[1];\n\t\t}\n\t\treturn;\n\t}\n\n\tRotateLightVector(shaderstate.curentity->axis, shaderstate.curentity->origin, r_origin, rorg);\n\n\tfor (i = 0 ; i < numverts ; i++, xyz += sizeof(vecV_t)/sizeof(vec_t), normal += 3, st += 2 )\n\t{\n\t\tVectorSubtract (rorg, xyz, viewer);\n\t\tVectorNormalizeFast (viewer);\n\n\t\td = DotProduct (normal, viewer);\n\n\t\treflected[0] = normal[0]*2*d - viewer[0];\n\t\treflected[1] = normal[1]*2*d - viewer[1];\n\t\treflected[2] = normal[2]*2*d - viewer[2];\n\n\t\tst[0] = 0.5 + reflected[1] * 0.5;\n\t\tst[1] = 0.5 - reflected[2] * 0.5;\n\t}\n}\n\nstatic float *tcgen(const shaderpass_t *pass, int cnt, float *dst, const mesh_t *mesh)\n{\n\tint i;\n\tvecV_t *src;\n\tswitch (pass->tcgen)\n\t{\n\tdefault:\n\tcase TC_GEN_BASE:\n\t\treturn (float*)mesh->st_array;\n\tcase TC_GEN_LIGHTMAP:\n\t\treturn (float*)mesh->lmst_array[0];\n\tcase TC_GEN_NORMAL:\n\t\treturn (float*)mesh->normals_array;\n\tcase TC_GEN_SVECTOR:\n\t\treturn (float*)mesh->snormals_array;\n\tcase TC_GEN_TVECTOR:\n\t\treturn (float*)mesh->tnormals_array;\n\tcase TC_GEN_ENVIRONMENT:\n\t\ttcgen_environment(dst, cnt, (float*)mesh->xyz_array, (float*)mesh->normals_array);\n\t\treturn dst;\n\n\tcase TC_GEN_DOTPRODUCT:\n\t\treturn dst;//mesh->st_array[0];\n\tcase TC_GEN_VECTOR:\n\t\tsrc = mesh->xyz_array;\n\t\tfor (i = 0; i < cnt; i++, dst += 2)\n\t\t{\n\t\t\tdst[0] = DotProduct(pass->tcgenvec[0], src[i]);\n\t\t\tdst[1] = DotProduct(pass->tcgenvec[1], src[i]);\n\t\t}\n\t\treturn dst;\n\t}\n}\n\nstatic float *tcgen3(const shaderpass_t *pass, int cnt, float *dst, const mesh_t *mesh)\n{\n\tint i;\n\tvecV_t *src;\n\tswitch (pass->tcgen)\n\t{\n\tdefault:\n\tcase TC_GEN_SKYBOX:\n\t\tsrc = mesh->xyz_array;\n\t\tfor (i = 0; i < cnt; i++, dst += 3)\n\t\t{\n\t\t\tdst[0] = src[i][0] - r_refdef.vieworg[0];\n\t\t\tdst[1] = r_refdef.vieworg[1] - src[i][1];\n\t\t\tdst[2] = src[i][2] - r_refdef.vieworg[2];\n\t\t}\n\t\treturn dst-cnt*3;\n\t}\n}\n\n/*src and dst can be the same address when tcmods are chained*/\nstatic void tcmod(const tcmod_t *tcmod, int cnt, const float *src, float *dst, const mesh_t *mesh)\n{\n\tfloat *table;\n\tfloat t1, t2;\n\tfloat cost, sint;\n\tint j;\n#define R_FastSin(x) sin((x)*(2*M_PI))\n\tswitch (tcmod->type)\n\t{\n\t\tcase SHADER_TCMOD_ROTATE:\n\t\t\tcost = tcmod->args[0] * shaderstate.curtime;\n\t\t\tsint = R_FastSin(cost);\n\t\t\tcost = R_FastSin(cost + 0.25);\n\n\t\t\tfor (j = 0; j < cnt; j++, dst+=2,src+=2)\n\t\t\t{\n\t\t\t\tt1 = cost * (src[0] - 0.5f) - sint * (src[1] - 0.5f) + 0.5f;\n\t\t\t\tt2 = cost * (src[1] - 0.5f) + sint * (src[0] - 0.5f) + 0.5f;\n\t\t\t\tdst[0] = t1;\n\t\t\t\tdst[1] = t2;\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase SHADER_TCMOD_SCALE:\n\t\t\tt1 = tcmod->args[0];\n\t\t\tt2 = tcmod->args[1];\n\n\t\t\tfor (j = 0; j < cnt; j++, dst+=2,src+=2)\n\t\t\t{\n\t\t\t\tdst[0] = src[0] * t1;\n\t\t\t\tdst[1] = src[1] * t2;\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase SHADER_TCMOD_TURB:\n\t\t\tt1 = tcmod->args[2] + shaderstate.curtime * tcmod->args[3];\n\t\t\tt2 = tcmod->args[1];\n\n\t\t\tfor (j = 0; j < cnt; j++, dst+=2,src+=2)\n\t\t\t{\n\t\t\t\tdst[0] = src[0] + R_FastSin (src[0]*t2+t1) * t2;\n\t\t\t\tdst[1] = src[1] + R_FastSin (src[1]*t2+t1) * t2;\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase SHADER_TCMOD_STRETCH:\n\t\t\ttable = FTableForFunc(tcmod->args[0]);\n\t\t\tt2 = tcmod->args[3] + shaderstate.curtime * tcmod->args[4];\n\t\t\tt1 = FTABLE_EVALUATE(table, t2) * tcmod->args[2] + tcmod->args[1];\n\t\t\tt1 = t1 ? 1.0f / t1 : 1.0f;\n\t\t\tt2 = 0.5f - 0.5f * t1;\n\t\t\tfor (j = 0; j < cnt; j++, dst+=2,src+=2)\n\t\t\t{\n\t\t\t\tdst[0] = src[0] * t1 + t2;\n\t\t\t\tdst[1] = src[1] * t1 + t2;\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase SHADER_TCMOD_SCROLL:\n\t\t\tt1 = tcmod->args[0] * shaderstate.curtime;\n\t\t\tt2 = tcmod->args[1] * shaderstate.curtime;\n\n\t\t\tfor (j = 0; j < cnt; j++, dst += 2, src+=2)\n\t\t\t{\n\t\t\t\tdst[0] = src[0] + t1;\n\t\t\t\tdst[1] = src[1] + t2;\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase SHADER_TCMOD_TRANSFORM:\n\t\t\tfor (j = 0; j < cnt; j++, dst+=2, src+=2)\n\t\t\t{\n\t\t\t\tt1 = src[0];\n\t\t\t\tt2 = src[1];\n\t\t\t\tdst[0] = t1 * tcmod->args[0] + t2 * tcmod->args[2] + tcmod->args[4];\n\t\t\t\tdst[1] = t1 * tcmod->args[1] + t2 * tcmod->args[3] + tcmod->args[5];\n\t\t\t}\n\t\t\tbreak;\n\n\t\tdefault:\n\t\t\tfor (j = 0; j < cnt; j++, dst+=2,src+=2)\n\t\t\t{\n\t\t\t\tdst[0] = src[0];\n\t\t\t\tdst[1] = src[1];\n\t\t\t}\n\t\t\tbreak;\n\t}\n}\n\nstatic void GenerateTCMods(const shaderpass_t *pass, float *dest)\n{\n\tmesh_t *mesh;\n\tunsigned int mno;\n\t// unsigned int fvertex = 0; //unused variable\n\tint i;\n\tfloat *src;\n\tfloat *out;\n\tfor (mno = 0; mno < shaderstate.nummeshes; mno++)\n\t{\n\t\tmesh = shaderstate.meshlist[mno];\n\n#if 0\n\t\tout = dest + mesh->vbofirstvert*2;\n#else\n\t\tout = dest;\n\t\tdest += mesh->numvertexes*2;\n#endif\n\n\t\tsrc = tcgen(pass, mesh->numvertexes, out, mesh);\n\t\t//tcgen might return unmodified info\n\t\tif (pass->numtcmods)\n\t\t{\n\t\t\tfor (i = 0; i < pass->numtcmods; i++)\n\t\t\t{\n\t\t\t\ttcmod(&pass->tcmods[i], mesh->numvertexes, src, out, mesh);\n\t\t\t\tsrc = out;\n\t\t\t}\n\t\t}\n\t\telse if (src != out)\n\t\t{\n\t\t\tmemcpy(out, src, sizeof(vec2_t)*mesh->numvertexes);\n\t\t}\n\t}\n}\nstatic void GenerateTCMods3(const shaderpass_t *pass, float *dest)\n{\n\tmesh_t *mesh;\n\tunsigned int mno;\n\t// unsigned int fvertex = 0; //unused variable\n//\tint i;\n\tfloat *src;\n\tfloat *out;\n\tfor (mno = 0; mno < shaderstate.nummeshes; mno++)\n\t{\n\t\tmesh = shaderstate.meshlist[mno];\n\n#if 0\n\t\tout = dest + mesh->vbofirstvert*3;\n#else\n\t\tout = dest;\n\t\tdest += mesh->numvertexes*3;\n#endif\n\n\t\tsrc = tcgen3(pass, mesh->numvertexes, out, mesh);\n\t\t//tcgen might return unmodified info\n\t\t/*if (pass->numtcmods)\n\t\t{\n\t\t\tfor (i = 0; i < pass->numtcmods; i++)\n\t\t\t{\n\t\t\t\ttcmod3(&pass->tcmods[i], mesh->numvertexes, src, out, mesh);\n\t\t\t\tsrc = out;\n\t\t\t}\n\t\t}\n\t\telse */if (src != out)\n\t\t{\n\t\t\tmemcpy(out, src, sizeof(vec2_t)*mesh->numvertexes);\n\t\t}\n\t}\n}\n\nstatic void tcgen_fog(float *st, unsigned int numverts, float *xyz, mfog_t *fog)\n{\n\tint\t\t\ti;\n\n\tfloat z;\n\tfloat eye, point;\n\tvec4_t zmat;\n\tvec4_t distmat;\n\n\t//FIXME\n\tfloat modelviewmatrix[16];\n\tMatrix4_Multiply(r_refdef.m_view, shaderstate.m_model, modelviewmatrix);\n\n\t//generate a simple matrix to calc only the projected z coord\n\tzmat[0] = -modelviewmatrix[2];\n\tzmat[1] = -modelviewmatrix[6];\n\tzmat[2] = -modelviewmatrix[10];\n\tzmat[3] = -modelviewmatrix[14];\n\n\tVector4Scale(zmat, shaderstate.fogfar, zmat);\n\n\tif (fog && fog->visibleplane)\n\t{\n\t\tVectorCopy(fog->visibleplane->normal, distmat);\n\t\tdistmat[3] = fog->visibleplane->dist;\n\n\t\teye = (DotProduct(r_refdef.vieworg, distmat) - distmat[3]);\n\t\tif (eye < 1)\n\t\t\teye = 1;\n\n\t\tfor (i = 0 ; i < numverts ; i++, xyz += sizeof(vecV_t)/sizeof(vec_t), st += 2 )\n\t\t{\n\t\t\tz = DotProduct(xyz, zmat) + zmat[3];\n\t\t\tst[0] = z;\n\n\t\t\tif (fog->visibleplane)\n\t\t\t\tpoint = (DotProduct(xyz, distmat) - distmat[3]);\n\t\t\telse\n\t\t\t\tpoint = 1;\n\t\t\tst[1] = point / (point - eye);\n\t\t}\n\t}\n\telse\n\t{\n\t\tfor (i = 0 ; i < numverts ; i++, xyz += sizeof(vecV_t)/sizeof(vec_t), st += 2 )\n\t\t{\n\t\t\tz = DotProduct(xyz, zmat) + zmat[3];\n\t\t\tst[0] = z;\n\t\t\tst[1] = 1.0;//31/32.0;\n\t\t}\n\t}\n}\n\n//end texture coords\n/*******************************************************************************************************************/\n\n\nstatic void GenerateFogTexture(texid_t *tex, float density, float zscale)\n{\n#define FOGS 256\n#define FOGT 32\n\tbyte_vec4_t fogdata[FOGS*FOGT];\n\tint s, t;\n\tfloat f, z;\n\n\tfor(s = 0; s < FOGS; s++)\n\t\tfor(t = 0; t < FOGT; t++)\n\t\t{\n\t\t\tz = (float)s / (FOGS-1);\n\t\t\tz *= zscale;\n\n\t\t\tif (0)//q3\n\t\t\t\tf = pow(z, 0.5);\n\t\t\telse if (1)//GL_EXP\n\t\t\t\tf = 1-exp(-density * z);\n\t\t\telse //GL_EXP2\n\t\t\t\tf = 1-exp(-(density*density) * z);\n\t\t\tif (f < 0)\n\t\t\t\tf = 0;\n\t\t\tif (f > 1)\n\t\t\t\tf = 1;\n\t\t\tf *= (float)t / (FOGT-1);\n\n\t\t\tfogdata[t*FOGS + s][0] = 255;\n\t\t\tfogdata[t*FOGS + s][1] = 255;\n\t\t\tfogdata[t*FOGS + s][2] = 255;\n\t\t\tfogdata[t*FOGS + s][3] = 255*f;\n\t\t}\n\n\tif (!TEXVALID(*tex))\n\t\t*tex = Image_CreateTexture(\"***fog***\", NULL, IF_CLAMP|IF_NOMIPMAP);\n\tImage_Upload(*tex, TF_RGBA32, fogdata, NULL, FOGS, FOGT, 1, IF_CLAMP|IF_NOMIPMAP);\n}\n\n\n\nstatic void deformgen(const deformv_t *deformv, int cnt, vecV_t *src, vecV_t *dst, const mesh_t *mesh)\n{\n\tfloat *table;\n\tint j, k;\n\tfloat args[4];\n\tfloat deflect;\n\tswitch (deformv->type)\n\t{\n\tdefault:\n\tcase DEFORMV_NONE:\n\t\tif (src != dst)\n\t\t\tmemcpy(dst, src, sizeof(*src)*cnt);\n\t\tbreak;\n\n\tcase DEFORMV_WAVE:\n\t\tif (!mesh->normals_array)\n\t\t{\n\t\t\tif (src != dst)\n\t\t\t\tmemcpy(dst, src, sizeof(*src)*cnt);\n\t\t\treturn;\n\t\t}\n\t\targs[0] = deformv->func.args[0];\n\t\targs[1] = deformv->func.args[1];\n\t\targs[3] = deformv->func.args[2] + deformv->func.args[3] * shaderstate.curtime;\n\t\ttable = FTableForFunc(deformv->func.type);\n\n\t\tfor ( j = 0; j < cnt; j++ )\n\t\t{\n\t\t\tdeflect = deformv->args[0] * (src[j][0]+src[j][1]+src[j][2]) + args[3];\n\t\t\tdeflect = FTABLE_EVALUATE(table, deflect) * args[1] + args[0];\n\n\t\t\t// Deflect vertex along its normal by wave amount\n\t\t\tVectorMA(src[j], deflect, mesh->normals_array[j], dst[j]);\n\t\t}\n\t\tbreak;\n\n\tcase DEFORMV_NORMAL:\n\t\t//normal does not actually move the verts, but it does change the normals array\n\t\t//we don't currently support that.\n\t\tif (src != dst)\n\t\t\tmemcpy(dst, src, sizeof(*src)*cnt);\n/*\n\t\targs[0] = deformv->args[1] * shaderstate.curtime;\n\n\t\tfor ( j = 0; j < cnt; j++ )\n\t\t{\n\t\t\targs[1] = normalsArray[j][2] * args[0];\n\n\t\t\tdeflect = deformv->args[0] * R_FastSin(args[1]);\n\t\t\tnormalsArray[j][0] *= deflect;\n\t\t\tdeflect = deformv->args[0] * R_FastSin(args[1] + 0.25);\n\t\t\tnormalsArray[j][1] *= deflect;\n\t\t\tVectorNormalizeFast(normalsArray[j]);\n\t\t}\n*/\t\tbreak;\n\n\tcase DEFORMV_MOVE:\n\t\ttable = FTableForFunc(deformv->func.type);\n\t\tdeflect = deformv->func.args[2] + shaderstate.curtime * deformv->func.args[3];\n\t\tdeflect = FTABLE_EVALUATE(table, deflect) * deformv->func.args[1] + deformv->func.args[0];\n\n\t\tfor ( j = 0; j < cnt; j++ )\n\t\t\tVectorMA(src[j], deflect, deformv->args, dst[j]);\n\t\tbreak;\n\n\tcase DEFORMV_BULGE:\n\t\targs[0] = deformv->args[0]/(2*M_PI);\n\t\targs[1] = deformv->args[1];\n\t\targs[2] = shaderstate.curtime * deformv->args[2]/(2*M_PI);\n\n\t\tfor (j = 0; j < cnt; j++)\n\t\t{\n\t\t\tdeflect = R_FastSin(mesh->st_array[j][0]*args[0] + args[2])*args[1];\n\t\t\tdst[j][0] = src[j][0]+deflect*mesh->normals_array[j][0];\n\t\t\tdst[j][1] = src[j][1]+deflect*mesh->normals_array[j][1];\n\t\t\tdst[j][2] = src[j][2]+deflect*mesh->normals_array[j][2];\n\t\t}\n\t\tbreak;\n\n\tcase DEFORMV_AUTOSPRITE:\n\t\tif (mesh->numindexes < 6)\n\t\t\tbreak;\n\n\t\tfor (j = 0; j < cnt-3; j+=4, src+=4, dst+=4)\n\t\t{\n\t\t\tvec3_t mid, d;\n\t\t\tfloat radius;\n\t\t\tmid[0] = 0.25*(src[0][0] + src[1][0] + src[2][0] + src[3][0]);\n\t\t\tmid[1] = 0.25*(src[0][1] + src[1][1] + src[2][1] + src[3][1]);\n\t\t\tmid[2] = 0.25*(src[0][2] + src[1][2] + src[2][2] + src[3][2]);\n\t\t\tVectorSubtract(src[0], mid, d);\n\t\t\tradius = 2*VectorLength(d);\n\n\t\t\tfor (k = 0; k < 4; k++)\n\t\t\t{\n\t\t\t\tdst[k][0] = mid[0] + radius*((mesh->st_array[j+k][0]-0.5)*r_refdef.m_view[0+0]-(mesh->st_array[j+k][1]-0.5)*r_refdef.m_view[0+1]);\n\t\t\t\tdst[k][1] = mid[1] + radius*((mesh->st_array[j+k][0]-0.5)*r_refdef.m_view[4+0]-(mesh->st_array[j+k][1]-0.5)*r_refdef.m_view[4+1]);\n\t\t\t\tdst[k][2] = mid[2] + radius*((mesh->st_array[j+k][0]-0.5)*r_refdef.m_view[8+0]-(mesh->st_array[j+k][1]-0.5)*r_refdef.m_view[8+1]);\n\t\t\t}\n\t\t}\n\t\tbreak;\n\n\tcase DEFORMV_AUTOSPRITE2:\n\t\tif (mesh->numindexes < 6)\n\t\t\tbreak;\n\n\t\tfor (k = 0; k < mesh->numindexes; k += 6)\n\t\t{\n\t\t\tint long_axis, short_axis;\n\t\t\tvec3_t axis;\n\t\t\tfloat len[3];\n\t\t\tmat3_t m0, m1, m2, result;\n\t\t\tfloat *quad[4];\n\t\t\tvec3_t rot_centre, tv, tv2;\n\n\t\t\tquad[0] = (float *)(src + mesh->indexes[k+0]);\n\t\t\tquad[1] = (float *)(src + mesh->indexes[k+1]);\n\t\t\tquad[2] = (float *)(src + mesh->indexes[k+2]);\n\n\t\t\tfor (j = 2; j >= 0; j--)\n\t\t\t{\n\t\t\t\tquad[3] = (float *)(src + mesh->indexes[k+3+j]);\n\t\t\t\tif (!VectorEquals (quad[3], quad[0]) &&\n\t\t\t\t\t!VectorEquals (quad[3], quad[1]) &&\n\t\t\t\t\t!VectorEquals (quad[3], quad[2]))\n\t\t\t\t{\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// build a matrix were the longest axis of the billboard is the Y-Axis\n\t\t\tVectorSubtract(quad[1], quad[0], m0[0]);\n\t\t\tVectorSubtract(quad[2], quad[0], m0[1]);\n\t\t\tVectorSubtract(quad[2], quad[1], m0[2]);\n\t\t\tlen[0] = DotProduct(m0[0], m0[0]);\n\t\t\tlen[1] = DotProduct(m0[1], m0[1]);\n\t\t\tlen[2] = DotProduct(m0[2], m0[2]);\n\n\t\t\tif ((len[2] > len[1]) && (len[2] > len[0]))\n\t\t\t{\n\t\t\t\tif (len[1] > len[0])\n\t\t\t\t{\n\t\t\t\t\tlong_axis = 1;\n\t\t\t\t\tshort_axis = 0;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tlong_axis = 0;\n\t\t\t\t\tshort_axis = 1;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if ((len[1] > len[2]) && (len[1] > len[0]))\n\t\t\t{\n\t\t\t\tif (len[2] > len[0])\n\t\t\t\t{\n\t\t\t\t\tlong_axis = 2;\n\t\t\t\t\tshort_axis = 0;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tlong_axis = 0;\n\t\t\t\t\tshort_axis = 2;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse //if ( (len[0] > len[1]) && (len[0] > len[2]) )\n\t\t\t{\n\t\t\t\tif (len[2] > len[1])\n\t\t\t\t{\n\t\t\t\t\tlong_axis = 2;\n\t\t\t\t\tshort_axis = 1;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tlong_axis = 1;\n\t\t\t\t\tshort_axis = 2;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (DotProduct(m0[long_axis], m0[short_axis]))\n\t\t\t{\n\t\t\t\tVectorNormalize2(m0[long_axis], axis);\n\t\t\t\tVectorCopy(axis, m0[1]);\n\n\t\t\t\tif (axis[0] || axis[1])\n\t\t\t\t{\n\t\t\t\t\tVectorVectors(m0[1], m0[2], m0[0]);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tVectorVectors(m0[1], m0[0], m0[2]);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tVectorNormalize2(m0[long_axis], axis);\n\t\t\t\tVectorNormalize2(m0[short_axis], m0[0]);\n\t\t\t\tVectorCopy(axis, m0[1]);\n\t\t\t\tCrossProduct(m0[0], m0[1], m0[2]);\n\t\t\t}\n\n\t\t\tfor (j = 0; j < 3; j++)\n\t\t\t\trot_centre[j] = (quad[0][j] + quad[1][j] + quad[2][j] + quad[3][j]) * 0.25;\n\n\t\t\tVectorAdd(shaderstate.curentity->origin, rot_centre, tv);\n\t\t\tVectorSubtract(r_origin, tv, tv);\n\n\t\t\t// filter any longest-axis-parts off the camera-direction\n\t\t\tdeflect = -DotProduct(tv, axis);\n\n\t\t\tVectorMA(tv, deflect, axis, m1[2]);\n\t\t\tVectorNormalizeFast(m1[2]);\n\t\t\tVectorCopy(axis, m1[1]);\n\t\t\tCrossProduct(m1[1], m1[2], m1[0]);\n\n\t\t\tMatrix3_Transpose(m1, m2);\n\t\t\tMatrix3_Multiply(m2, m0, result);\n\n\t\t\tfor (j = 0; j < 4; j++)\n\t\t\t{\n\t\t\t\tint v = ((vecV_t*)quad[j]-src);\n\t\t\t\tVectorSubtract(quad[j], rot_centre, tv);\n\t\t\t\tMatrix3_Multiply_Vec3((void *)result, tv, tv2);\n\t\t\t\tVectorAdd(rot_centre, tv2, dst[v]);\n\t\t\t}\n\t\t}\n\t\tbreak;\n\n//\tcase DEFORMV_PROJECTION_SHADOW:\n//\t\tbreak;\n\t}\n}\n\nstatic void R_FetchPlayerColour(unsigned int cv, vec3_t rgb)\n{\n\tint i;\n\n\tif (cv >= 16)\n\t{\n\t\trgb[0] = (((cv&0xff0000)>>16)**((unsigned char*)&d_8to24rgbtable[15]+0)) / (256.0*256);\n\t\trgb[1] = (((cv&0x00ff00)>>8)**((unsigned char*)&d_8to24rgbtable[15]+1)) / (256.0*256);\n\t\trgb[2] = (((cv&0x0000ff)>>0)**((unsigned char*)&d_8to24rgbtable[15]+2)) / (256.0*256);\n\t\treturn;\n\t}\n\ti = cv;\n\tif (i >= 8)\n\t{\n\t\ti<<=4;\n\t}\n\telse\n\t{\n\t\ti<<=4;\n\t\ti+=15;\n\t}\n\ti*=3;\n\trgb[0] = host_basepal[i+0] / 255.0;\n\trgb[1] = host_basepal[i+1] / 255.0;\n\trgb[2] = host_basepal[i+2] / 255.0;\n/*\tif (!gammaworks)\n\t{\n\t\t*retred = gammatable[*retred];\n\t\t*retgreen = gammatable[*retgreen];\n\t\t*retblue = gammatable[*retblue];\n\t}*/\n}\nstatic void BE_ApplyUniforms(program_t *prog, struct programpermu_s *perm)\n{\n\tshaderprogparm_t *pp;\n\tvec4_t param4;\n\tint h;\n\tint i;\n\tif (shaderstate.curperm != perm)\n\t{\n\t\tshaderstate.curperm = perm;\n\t\tIDirect3DDevice9_SetVertexShader(pD3DDev9, perm->h.hlsl.vert);\n\t\tIDirect3DDevice9_SetPixelShader(pD3DDev9, perm->h.hlsl.frag);\n\t}\n\tfor (i = 0, pp = perm->parm; i < perm->numparms; i++, pp++)\n\t{\n\t\th = pp->handle;\n\t\tswitch (pp->type)\n\t\t{\n\t\tcase SP_M_PROJECTION:\n\t\t\tIDirect3DDevice9_SetVertexShaderConstantF(pD3DDev9, h, shaderstate.curprojection, 4);\n\t\t\tbreak;\n\t\tcase SP_M_VIEW:\n\t\t\tIDirect3DDevice9_SetVertexShaderConstantF(pD3DDev9, h, r_refdef.m_view, 4);\n\t\t\tbreak;\n\t\tcase SP_M_MODEL:\n\t\t\tIDirect3DDevice9_SetVertexShaderConstantF(pD3DDev9, h, shaderstate.m_model, 4);\n\t\t\tbreak;\n\t\tcase SP_E_VBLEND:\n\t\t\tIDirect3DDevice9_SetVertexShaderConstantF(pD3DDev9, h, shaderstate.meshlist[0]->xyz_blendw, 2);\n\t\t\tbreak;\n\n\n\t\tcase SP_V_EYEPOS:\n\t\t\tIDirect3DDevice9_SetPixelShaderConstantF(pD3DDev9, h, r_origin, 1);\n\t\t\tbreak;\n\t\tcase SP_E_EYEPOS:\n\t\t\t{\n\t\t\t\tvec4_t t2;\n\t\t\t\tfloat m16[16];\n\t\t\t\tMatrix4x4_CM_ModelMatrixFromAxis(m16, shaderstate.curentity->axis[0], shaderstate.curentity->axis[1], shaderstate.curentity->axis[2], shaderstate.curentity->origin);\n\t\t\t\tMatrix4x4_CM_Transform3(m16, r_origin, t2);\n\t\t\t\tIDirect3DDevice9_SetPixelShaderConstantF(pD3DDev9, h, t2, 1);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase SP_E_TIME:\n\t\t\t{\n\t\t\t\tvec4_t t1 = {shaderstate.curtime};\n\t\t\t\tIDirect3DDevice9_SetPixelShaderConstantF(pD3DDev9, h, t1, 1);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase SP_M_MODELVIEWPROJECTION:\n\t\t\t{\n\t\t\t\tfloat mv[16], mvp[16];\n\t\t\t\tMatrix4_Multiply(r_refdef.m_view, shaderstate.m_model, mv);\n\t\t\t\tMatrix4_Multiply(shaderstate.curprojection, mv, mvp);\n\t\t\t\tIDirect3DDevice9_SetVertexShaderConstantF(pD3DDev9, h, mvp, 4);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase SP_M_MODELVIEW:\n\t\t\t{\n\t\t\t\tfloat mv[16];\n\t\t\t\tMatrix4_Multiply(r_refdef.m_view, shaderstate.m_model, mv);\n\t\t\t\tIDirect3DDevice9_SetVertexShaderConstantF(pD3DDev9, h, mv, 4);\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase SP_LIGHTPOSITION:\n\t\t\t{\n\t\t\t\t/*light position in model space*/\n\t\t\t\tfloat inv[16];\n\t\t\t\tvec3_t t2;\n\t\t\t\tqboolean Matrix4_Invert(const float *m, float *out);\n\n\t\t\t\tMatrix4_Invert(shaderstate.m_model, inv);\n\t\t\t\tMatrix4x4_CM_Transform3(inv, shaderstate.curdlight->origin, t2);\n\t\t\t\tIDirect3DDevice9_SetVertexShaderConstantF(pD3DDev9, h, t2, 1);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\tcase SP_LIGHTRADIUS:\n\t\t\tIDirect3DDevice9_SetPixelShaderConstantF(pD3DDev9, h, &shaderstate.curdlight->radius, 1);\n\t\t\tbreak;\n\t\tcase SP_LIGHTCOLOUR:\n\t\t\tIDirect3DDevice9_SetPixelShaderConstantF(pD3DDev9, h, shaderstate.curdlight_colours, 1);\n\t\t\tbreak;\n\n\t\tcase SP_E_L_DIR:\n\t\t\tIDirect3DDevice9_SetVertexShaderConstantF(pD3DDev9, h, shaderstate.curentity->light_dir, 1);\n\t\t\tbreak;\n\t\tcase SP_E_L_MUL:\n\t\t\tIDirect3DDevice9_SetVertexShaderConstantF(pD3DDev9, h, shaderstate.curentity->light_range, 1);\n\t\t\tbreak;\n\t\tcase SP_E_L_AMBIENT:\n\t\t\tIDirect3DDevice9_SetVertexShaderConstantF(pD3DDev9, h, shaderstate.curentity->light_avg, 1);\n\t\t\tbreak;\n\n\t\tcase SP_E_COLOURS:\n\t\t\tIDirect3DDevice9_SetPixelShaderConstantF(pD3DDev9, h, shaderstate.curentity->shaderRGBAf, 1);\n\t\t\tbreak;\n\t\tcase SP_E_COLOURSIDENT:\n\t\t\tif (shaderstate.flags & BEF_FORCECOLOURMOD)\n\t\t\t\tIDirect3DDevice9_SetPixelShaderConstantF(pD3DDev9, h, shaderstate.curentity->shaderRGBAf, 1);\n\t\t\telse\n\t\t\t{\n\t\t\t\tvec4_t tmp = {1, 1, 1, shaderstate.curentity->shaderRGBAf[3]};\n\t\t\t\tIDirect3DDevice9_SetPixelShaderConstantF(pD3DDev9, h, tmp, 1);\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase SP_E_TOPCOLOURS:\n\t\t\tR_FetchPlayerColour(shaderstate.curentity->topcolour, param4);\n\t\t\tparam4[3] = 1;\n\t\t\tIDirect3DDevice9_SetPixelShaderConstantF(pD3DDev9, h, param4, 1);\n\t\t\tbreak;\n\t\tcase SP_E_BOTTOMCOLOURS:\n\t\t\tR_FetchPlayerColour(shaderstate.curentity->bottomcolour, param4);\n\t\t\tparam4[3] = 1;\n\t\t\tIDirect3DDevice9_SetPixelShaderConstantF(pD3DDev9, h, param4, 1);\n\t\t\tbreak;\n\n\t\tcase SP_E_LMSCALE:\n\t\t\tVector4Set(param4, 1, 1, 1, 1);\n\t\t\tif (shaderstate.curentity->model && (shaderstate.curentity->model->engineflags & MDLF_NEEDOVERBRIGHT))\n\t\t\t{\n\t\t\t\textern cvar_t gl_overbright;\n\t\t\t\tconst float identitylighting = 1;\n\t\t\t\tfloat sc = (1<<bound(0, gl_overbright.ival, 2)) * identitylighting;\n\t\t\t\tVectorSet(param4, sc, sc, sc);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tconst float identitylighting = 1;\n\t\t\t\tVectorSet(param4, identitylighting, identitylighting, identitylighting);\n\t\t\t}\n\t\t\tparam4[3] = 1;\n\t\t\tIDirect3DDevice9_SetVertexShaderConstantF(pD3DDev9, h, param4, 1);\n\t\t\tbreak;\n\n\t\tcase SP_W_FOG:\n\t\t\tIDirect3DDevice9_SetPixelShaderConstantF(pD3DDev9, h, r_refdef.globalfog.colour, 2);\t//colour and density\n\t\t\tbreak;\n\t\tcase SP_W_USER://FIXME: needs seperate vertex+fragment handles!\n\t\t\tIDirect3DDevice9_SetPixelShaderConstantF(pD3DDev9, h, r_refdef.userdata[0], countof(r_refdef.userdata));\n\t\t\tbreak;\n\n\t\tcase SP_M_ENTBONES_MAT3X4:\n\t\tcase SP_M_ENTBONES_PACKED:\n\t\tcase SP_M_ENTBONES_MAT4:\n\t\tcase SP_E_VLSCALE:\n\t\tcase SP_E_ORIGIN:\n\t\tcase SP_E_GLOWMOD:\n\t\tcase SP_M_INVVIEWPROJECTION:\n\t\tcase SP_M_INVMODELVIEW:\n\t\tcase SP_M_INVMODELVIEWPROJECTION:\n\t\tcase SP_SOURCESIZE:\n\t\tcase SP_S_COLOUR:\n\t\tcase SP_LIGHTCOLOURSCALE:\n\t\tcase SP_LIGHTSCREEN:\n\t\tcase SP_LIGHTCUBEMATRIX:\n\t\tcase SP_LIGHTSHADOWMAPPROJ:\n\t\tcase SP_LIGHTSHADOWMAPSCALE:\n\t\tcase SP_LIGHTDIRECTION:\n\n\t\tcase SP_RENDERTEXTURESCALE:\n\n\t\tcase SP_FIRSTIMMEDIATE:\n\t\tcase SP_CONST1I:\n\t\tcase SP_CONST2I:\n\t\tcase SP_CONST3I:\n\t\tcase SP_CONST4I:\n\t\tcase SP_CONST1F:\n\t\tcase SP_CONST2F:\n\t\tcase SP_CONST3F:\n\t\tcase SP_CONST4F:\n\t\tcase SP_CVARI:\n\t\tcase SP_CVARF:\n\t\tcase SP_CVAR3F:\n\t\tcase SP_CVAR4F:\n\t\tcase SP_TEXTURE:\n\t\tcase SP_BAD:\n\t\t\tCon_Printf(\"shader property %i not implemented\\n\", pp->type);\n\t\t\tbreak;\n\t\t}\n\t}\n}\n\nstatic unsigned int BE_DrawMeshChain_SetupProgram(program_t *p)\n{\n\tunsigned int vdec = 0;\n\tunsigned int perm = 0;\n\tstruct programpermu_s *pp;\n#ifdef SKELETALMODELS\n\tif (shaderstate.batchvbo && shaderstate.batchvbo->numbones)\n\t\tperm |= PERMUTATION_SKELETAL;\n#endif\n\tif (TEXLOADED(shaderstate.curtexnums->bump))\n\t\tperm |= PERMUTATION_BUMPMAP;\n\tif (TEXLOADED(shaderstate.curtexnums->fullbright))\n\t\tperm |= PERMUTATION_FULLBRIGHT;\n\tif ((TEXLOADED(shaderstate.curtexnums->upperoverlay) || TEXLOADED(shaderstate.curtexnums->loweroverlay)))\n\t\tperm |= PERMUTATION_UPPERLOWER;\n\tif (r_refdef.globalfog.density)\n\t\tperm |= PERMUTATION_FOG;\n#ifdef NONSKELETALMODELS\n\tif (shaderstate.batchvbo && shaderstate.batchvbo->coord2.d3d.buff)\n\t\tperm |= PERMUTATION_FRAMEBLEND;\n#endif\n//\tif (p->permu[perm|PERMUTATION_DELUXE].h.loaded && TEXVALID(shaderstate.curtexnums->bump) && shaderstate.curbatch->lightmap[0] >= 0 && lightmap[shaderstate.curbatch->lightmap[0]]->hasdeluxe)\n//\t\tperm |= PERMUTATION_DELUXE;\n#if MAXRLIGHTMAPS > 1\n\tif (shaderstate.curbatch && shaderstate.curbatch->lightmap[1] >= 0)\n\t\tperm |= PERMUTATION_LIGHTSTYLES;\n#endif\n\n\tvdec |= D3D_VDEC_COL4B;//BE_GenerateColourMods(vertcount, s->passes);\n\n\tperm &= p->supportedpermutations;\n\tpp = p->permu[perm];\n\tif (!pp)\n\t{\n\t\tp->permu[perm] = pp = Shader_LoadPermutation(p, perm);\n\t\tif (!pp)\n\t\t{\t//failed? copy from 0 so we don't keep re-failing\n\t\t\tpp = p->permu[perm] = p->permu[0];\n\t\t}\n\t}\n\tperm = pp->permutation;\n\n\tif (perm & PERMUTATION_FRAMEBLEND)\n\t\tvdec |= D3D_VDEC_POS2;\n\tBE_ApplyUniforms(p, pp);\n\n\treturn vdec;\n}\n\n/*does not do the draw call, does not consider indicies (except for billboard generation) */\nstatic qboolean BE_DrawMeshChain_SetupPass(shaderpass_t *pass, unsigned int vertcount)\n{\n\tint vdec;\n\tvoid *map;\n\tint i;\n\tunsigned int passno = 0, tmu;\n\n\tint lastpass = pass->numMergedPasses;\n\n\tfor (i = 0; i < lastpass; i++)\n\t{\n\t\tif (pass[i].texgen == T_GEN_UPPEROVERLAY && !TEXLOADED(shaderstate.curtexnums->upperoverlay))\n\t\t\tcontinue;\n\t\tif (pass[i].texgen == T_GEN_LOWEROVERLAY && !TEXLOADED(shaderstate.curtexnums->loweroverlay))\n\t\t\tcontinue;\n\t\tif (pass[i].texgen == T_GEN_FULLBRIGHT && !TEXLOADED(shaderstate.curtexnums->fullbright))\n\t\t\tcontinue;\n\t\tbreak;\n\t}\n\tif (i == lastpass)\n\t\treturn false;\n\n\t/*all meshes in a chain must have the same features*/\n\tvdec = 0;\n\n\t/*we only use one colour, generated from the first pass*/\n\tvdec |= BE_GenerateColourMods(vertcount, pass);\n\n\tif (pass->prog)\n\t{\n\t\tvdec |= BE_DrawMeshChain_SetupProgram(pass->prog);\n\n\t\ttmu = 0;\n\t\t/*activate tmus*/\n\t\tfor (passno = 0; passno < lastpass; passno++)\n\t\t{\n\t\t\tSelectPassTexture(tmu, pass+passno);\n\t\t\ttmu++;\n\t\t}\n\t\t/*deactivate any extras*/\n\t\tfor (; tmu < shaderstate.lastpasscount; )\n\t\t{\n\t\t\tBindTexture(tmu, NULL);\n\t\t\td3dcheck(IDirect3DDevice9_SetTextureStageState(pD3DDev9, tmu, D3DTSS_COLOROP, D3DTOP_DISABLE));\n\t\t\td3dcheck(IDirect3DDevice9_SetTextureStageState(pD3DDev9, tmu, D3DTSS_ALPHAOP, D3DTOP_DISABLE));\n\t\t\ttmu++;\n\t\t}\n\n\t\tif (1)\n\t\t{\n\t\t\tvdec |= D3D_VDEC_ST0|D3D_VDEC_ST1;\n\t\t\tif (shaderstate.batchvbo)\n\t\t\t{\n\t\t\t\td3dcheck(IDirect3DDevice9_SetStreamSource(pD3DDev9, STRM_TC0, shaderstate.batchvbo->texcoord.d3d.buff, shaderstate.batchvbo->texcoord.d3d.offs, sizeof(vbovdata_t)));\n\t\t\t\td3dcheck(IDirect3DDevice9_SetStreamSource(pD3DDev9, STRM_TC1, shaderstate.batchvbo->lmcoord[0].d3d.buff, shaderstate.batchvbo->lmcoord[0].d3d.offs, sizeof(vbovdata_t)));\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tmesh_t *mesh;\n\t\t\t\tunsigned int mno;\n\t\t\t\tfloat *outtc, *outlm;\n\n\t\t\t\tallocvertexbuffer(shaderstate.dynst_buff[0], shaderstate.dynst_size, &shaderstate.dynst_offs[0], &map, vertcount*sizeof(vec4_t));\n\t\t\t\touttc = map;\n\t\t\t\toutlm = outtc + vertcount*2;\n\t\t\t\tfor (mno = 0; mno < shaderstate.nummeshes; mno++)\n\t\t\t\t{\n\t\t\t\t\tmesh = shaderstate.meshlist[mno];\n\n\t\t\t\t\tmemcpy(outtc, mesh->st_array, sizeof(vec2_t)*mesh->numvertexes);\n\t\t\t\t\tmemcpy(outlm, mesh->lmst_array[0], sizeof(vec2_t)*mesh->numvertexes);\n\t\t\t\t}\n\t\t\t\td3dcheck(IDirect3DVertexBuffer9_Unlock(shaderstate.dynst_buff[0]));\n\t\t\t\td3dcheck(IDirect3DDevice9_SetStreamSource(pD3DDev9, STRM_TC0, shaderstate.dynst_buff[0], shaderstate.dynst_offs[0] - vertcount*sizeof(vec4_t), sizeof(vec2_t)));\n\t\t\t\td3dcheck(IDirect3DDevice9_SetStreamSource(pD3DDev9, STRM_TC1, shaderstate.dynst_buff[0], shaderstate.dynst_offs[0] - vertcount*sizeof(vec2_t), sizeof(vec2_t)));\n\t\t\t}\n\t\t\tshaderstate.lastpasscount = max(2, tmu);\n\t\t}\n\t}\n\telse\n\t{\n\t\tif (shaderstate.curperm)\n\t\t{\n\t\t\tshaderstate.curperm = NULL;\n\t\t\tIDirect3DDevice9_SetVertexShader(pD3DDev9, NULL);\n\t\t\tIDirect3DDevice9_SetPixelShader(pD3DDev9, NULL);\n\t\t}\n\t\n\t\ttmu = 0;\n\t\t/*activate tmus*/\n\t\tfor (passno = 0; passno < lastpass; passno++)\n\t\t{\n\t\t\tif (pass[passno].texgen == T_GEN_UPPEROVERLAY && !TEXLOADED(shaderstate.curtexnums->upperoverlay))\n\t\t\t\tcontinue;\n\t\t\tif (pass[passno].texgen == T_GEN_LOWEROVERLAY && !TEXLOADED(shaderstate.curtexnums->loweroverlay))\n\t\t\t\tcontinue;\n\t\t\tif (pass[passno].texgen == T_GEN_FULLBRIGHT && !TEXLOADED(shaderstate.curtexnums->fullbright))\n\t\t\t\tcontinue;\n\n\t\t\tSelectPassTexture(tmu, pass+passno);\n\n\t\t\tvdec |= D3D_VDEC_ST0<<tmu;\n\t\t\tif (shaderstate.batchvbo && pass[passno].tcgen == TC_GEN_BASE/* && !pass[passno].numtcmods*/)\n\t\t\t\td3dcheck(IDirect3DDevice9_SetStreamSource(pD3DDev9, STRM_TC0+tmu, shaderstate.batchvbo->texcoord.d3d.buff, shaderstate.batchvbo->texcoord.d3d.offs, sizeof(vbovdata_t)));\n\t\t\telse if (shaderstate.batchvbo)// && pass[passno].tcgen == TC_GEN_LIGHTMAP/* && !pass[passno].numtcmods*/)\n\t\t\t\td3dcheck(IDirect3DDevice9_SetStreamSource(pD3DDev9, STRM_TC0+tmu, shaderstate.batchvbo->lmcoord[0].d3d.buff, shaderstate.batchvbo->lmcoord[0].d3d.offs, sizeof(vbovdata_t)));\n\t\t\telse if (pass[passno].tcgen == TC_GEN_SKYBOX)\n\t\t\t{\n\t\t\t\tvdec |= D3D_VDEC_CM;\n\t\t\t\tallocvertexbuffer(shaderstate.dynst_buff[tmu], shaderstate.dynst_size, &shaderstate.dynst_offs[tmu], &map, vertcount*sizeof(vec3_t));\n\t\t\t\tGenerateTCMods3(pass+passno, map);\n\t\t\t\td3dcheck(IDirect3DVertexBuffer9_Unlock(shaderstate.dynst_buff[tmu]));\n\t\t\t\td3dcheck(IDirect3DDevice9_SetStreamSource(pD3DDev9, STRM_TC0+tmu, shaderstate.dynst_buff[tmu], shaderstate.dynst_offs[tmu] - vertcount*sizeof(vec3_t), sizeof(vec3_t)));\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tallocvertexbuffer(shaderstate.dynst_buff[tmu], shaderstate.dynst_size, &shaderstate.dynst_offs[tmu], &map, vertcount*sizeof(vec2_t));\n\t\t\t\tGenerateTCMods(pass+passno, map);\n\t\t\t\td3dcheck(IDirect3DVertexBuffer9_Unlock(shaderstate.dynst_buff[tmu]));\n\t\t\t\td3dcheck(IDirect3DDevice9_SetStreamSource(pD3DDev9, STRM_TC0+tmu, shaderstate.dynst_buff[tmu], shaderstate.dynst_offs[tmu] - vertcount*sizeof(vec2_t), sizeof(vec2_t)));\n\t\t\t}\n\t\t\ttmu++;\n\t\t}\n\t\t/*deactivate any extras*/\n\t\tfor (; tmu < shaderstate.lastpasscount; )\n\t\t{\n\t\t\td3dcheck(IDirect3DDevice9_SetStreamSource(pD3DDev9, STRM_TC0+tmu, NULL, 0, 0));\n\t\t\tBindTexture(tmu, NULL);\n\t\t\td3dcheck(IDirect3DDevice9_SetTextureStageState(pD3DDev9, tmu, D3DTSS_COLOROP, D3DTOP_DISABLE));\n\t\t\td3dcheck(IDirect3DDevice9_SetTextureStageState(pD3DDev9, tmu, D3DTSS_ALPHAOP, D3DTOP_DISABLE));\n\t\t\ttmu++;\n\t\t}\n\t\tshaderstate.lastpasscount = tmu;\n\t}\n\n//\tif (meshchain->normals_array &&\n//\t\tmeshchain->2 &&\n//\t\tmeshchain->tnormals_array)\n//\t\tvdec |= D3D_VDEC_NORMS;\n\n\tif (vdec != shaderstate.curvertdecl)\n\t{\n\t\tshaderstate.curvertdecl = vdec;\n\t\td3dcheck(IDirect3DDevice9_SetVertexDeclaration(pD3DDev9, vertexdecls[shaderstate.curvertdecl]));\n\t}\n\n\tBE_ApplyShaderBits(pass->shaderbits);\n\treturn true;\n}\n\nstatic void BE_SubmitMeshChain(unsigned int vertbase, unsigned int firstvert, unsigned int vertcount, unsigned int idxfirst, int unsigned idxcount)\n{\n\tif (shaderstate.flags & BEF_LINES)\n\t\tIDirect3DDevice9_DrawIndexedPrimitive(pD3DDev9, D3DPT_LINELIST, vertbase, firstvert, vertcount, idxfirst, idxcount/2);\n\telse\n\t\tIDirect3DDevice9_DrawIndexedPrimitive(pD3DDev9, D3DPT_TRIANGLELIST, vertbase, firstvert, vertcount, idxfirst, idxcount/3);\n\tRQuantAdd(RQUANT_DRAWS, 1);\n\n\tRQuantAdd(RQUANT_PRIMITIVEINDICIES, idxcount);\n}\n\nstatic void BE_RenderMeshProgram(shader_t *s, unsigned int vertbase, unsigned int vertfirst, unsigned int vertcount, unsigned int idxfirst, unsigned int idxcount)\n{\n\tint vdec = D3D_VDEC_ST0|D3D_VDEC_ST1|D3D_VDEC_NORM;\n\tint passno;\n\tint perm = 0;\n\n\tprogram_t *p = s->prog;\n\tstruct programpermu_s *pp;\n\n#ifdef SKELETALMODELS\n\tif (shaderstate.batchvbo && shaderstate.batchvbo->numbones)\n\t{\n\t\tif (p->supportedpermutations & PERMUTATION_SKELETAL)\n\t\t\tperm |= PERMUTATION_SKELETAL;\n\t\telse\n\t\t\treturn;\n\t}\n#endif\n\tif (TEXLOADED(shaderstate.curtexnums->bump))\n\t\tperm |= PERMUTATION_BUMPMAP;\n\tif (TEXLOADED(shaderstate.curtexnums->fullbright))\n\t\tperm |= PERMUTATION_FULLBRIGHT;\n\tif ((TEXLOADED(shaderstate.curtexnums->upperoverlay) || TEXLOADED(shaderstate.curtexnums->loweroverlay)))\n\t\tperm |= PERMUTATION_UPPERLOWER;\n\tif (r_refdef.globalfog.density)\n\t\tperm |= PERMUTATION_FOG;\n#ifdef NONSKELETALMODELS\n\tif (shaderstate.batchvbo && shaderstate.batchvbo->coord2.d3d.buff)\n\t\tperm |= PERMUTATION_FRAMEBLEND;\n#endif\n//\tif (TEXVALID(shaderstate.curtexnums->bump) && shaderstate.curbatch->lightmap[0] >= 0 && lightmap[shaderstate.curbatch->lightmap[0]]->hasdeluxe)\n//\t\tperm |= PERMUTATION_DELUXE;\n#if MAXRLIGHTMAPS > 1\n\tif (shaderstate.curbatch && shaderstate.curbatch->lightmap[1] >= 0)\n\t\tperm |= PERMUTATION_LIGHTSTYLES;\n#endif\n\n\tvdec |= D3D_VDEC_COL4B;//BE_GenerateColourMods(vertcount, s->passes);\n\n\tperm &= p->supportedpermutations;\n\tpp = p->permu[perm];\n\tif (!pp)\n\t{\n\t\tp->permu[perm] = pp = Shader_LoadPermutation(p, perm);\n\t\tif (!pp)\n\t\t{\t//failed? copy from 0 so we don't keep re-failing\n\t\t\tpp = p->permu[perm] = p->permu[0];\n\t\t}\n\t}\n\tperm = pp->permutation;\n\n\tif (perm & PERMUTATION_FRAMEBLEND)\n\t\tvdec |= D3D_VDEC_POS2;\n\tBE_ApplyUniforms(p, pp);\n\n\n\tBE_ApplyShaderBits(s->passes->shaderbits);\n\n\t/*activate tmus*/\n\tfor (passno = 0; passno < s->numpasses; passno++)\n\t{\n\t\tSelectPassTexture(passno, s->passes+passno);\n\t}\n\t/*deactivate any extras*/\n\tfor (; passno < shaderstate.lastpasscount; passno++)\n\t{\n\t\tBindTexture(passno, NULL);\n\t\td3dcheck(IDirect3DDevice9_SetTextureStageState(pD3DDev9, passno, D3DTSS_COLOROP, D3DTOP_DISABLE));\n\t\td3dcheck(IDirect3DDevice9_SetTextureStageState(pD3DDev9, passno, D3DTSS_ALPHAOP, D3DTOP_DISABLE));\n\t}\n\tshaderstate.lastpasscount = passno;\n\n\t/*colours*/\n\tif (vdec & D3D_VDEC_COL4B)\n\t{\n\t\tif (shaderstate.batchvbo)\n\t\t{\n\t\t\t//fixme\n\t\t\td3dcheck(IDirect3DDevice9_SetStreamSource(pD3DDev9, STRM_COL, shaderstate.batchvbo->colours[0].d3d.buff, shaderstate.batchvbo->colours[0].d3d.offs, sizeof(vbovdata_t)));\n\t\t}\n\t\telse\n\t\t{\n\t\t\tint mno,v;\n\t\t\tvoid *map;\n\t\t\tmesh_t *m;\n\t\t\tallocvertexbuffer(shaderstate.dynst_buff[0], shaderstate.dynst_size, &shaderstate.dynst_offs[0], &map, vertcount*sizeof(byte_vec4_t));\n\t\t\tfor (mno = 0, vertcount = 0; mno < shaderstate.nummeshes; mno++)\n\t\t\t{\n\t\t\t\tbyte_vec4_t *dest = (byte_vec4_t*)((char*)map+vertcount*sizeof(byte_vec4_t));\n\t\t\t\tm = shaderstate.meshlist[mno];\n\t\t\t\tif (m->colors4f_array[0])\n\t\t\t\t{\n\t\t\t\t\tfor (v = 0; v < m->numvertexes; v++)\n\t\t\t\t\t{\n\t\t\t\t\t\t//fixme:\n\t\t\t\t\t\tdest[v][0] = bound(0, m->colors4f_array[0][v][0] * 255, 255);\n\t\t\t\t\t\tdest[v][1] = bound(0, m->colors4f_array[0][v][1] * 255, 255);\n\t\t\t\t\t\tdest[v][2] = bound(0, m->colors4f_array[0][v][2] * 255, 255);\n\t\t\t\t\t\tdest[v][3] = bound(0, m->colors4f_array[0][v][3] * 255, 255);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse if (m->colors4b_array)\n\t\t\t\t\tmemcpy(dest, m->colors4b_array, m->numvertexes*sizeof(byte_vec4_t));\n\t\t\t\telse\n\t\t\t\t\tmemset(dest, 0, m->numvertexes*sizeof(byte_vec4_t));\n\t\t\t\tvertcount += m->numvertexes;\n\t\t\t}\n\t\t\td3dcheck(IDirect3DVertexBuffer9_Unlock(shaderstate.dynst_buff[0]));\n\t\t\td3dcheck(IDirect3DDevice9_SetStreamSource(pD3DDev9, STRM_COL, shaderstate.dynst_buff[0], shaderstate.dynst_offs[0] - vertcount*sizeof(byte_vec4_t), sizeof(byte_vec4_t)));\n\t\t}\n\t}\n\n\t/*texture coords*/\n\tif (vdec & D3D_VDEC_ST0)\n\t{\n\t\tif (shaderstate.batchvbo)\n\t\t{\n\t\t\tif (shaderstate.batchvbo && !shaderstate.batchvbo->vaodynamic)\n\t\t\t\td3dcheck(IDirect3DDevice9_SetStreamSource(pD3DDev9, STRM_TC0, shaderstate.batchvbo->texcoord.d3d.buff, shaderstate.batchvbo->texcoord.d3d.offs, sizeof(vbovdata_t)));\n\t\t\telse\n\t\t\t\td3dcheck(IDirect3DDevice9_SetStreamSource(pD3DDev9, STRM_TC0, shaderstate.batchvbo->texcoord.d3d.buff, shaderstate.batchvbo->texcoord.d3d.offs, sizeof(vec2_t)));\n\t\t}\n\t\telse\n\t\t{\n\t\t\tint mno;\n\t\t\tvoid *map;\n\t\t\tmesh_t *m;\n\t\t\tallocvertexbuffer(shaderstate.dynst_buff[0], shaderstate.dynst_size, &shaderstate.dynst_offs[0], &map, vertcount*sizeof(vec2_t));\n\t\t\tfor (mno = 0, vertcount = 0; mno < shaderstate.nummeshes; mno++)\n\t\t\t{\n\t\t\t\tvec2_t *dest = (vec2_t*)((char*)map+vertcount*sizeof(vec2_t));\n\t\t\t\tm = shaderstate.meshlist[mno];\n\t\t\t\tmemcpy(dest, m->st_array, m->numvertexes*sizeof(vec2_t));\n\t\t\t\tvertcount += m->numvertexes;\n\t\t\t}\n\t\t\td3dcheck(IDirect3DVertexBuffer9_Unlock(shaderstate.dynst_buff[0]));\n\t\t\td3dcheck(IDirect3DDevice9_SetStreamSource(pD3DDev9, STRM_TC0, shaderstate.dynst_buff[0], shaderstate.dynst_offs[0] - vertcount*sizeof(vec2_t), sizeof(vec2_t)));\n\t\t}\n\t}\n\t/*lm coords*/\n\tif (vdec & D3D_VDEC_ST1)\n\t{\n\t\tif (shaderstate.batchvbo)\n\t\t\td3dcheck(IDirect3DDevice9_SetStreamSource(pD3DDev9, STRM_TC1, shaderstate.batchvbo->lmcoord[0].d3d.buff, shaderstate.batchvbo->lmcoord[0].d3d.offs, sizeof(vbovdata_t)));\n\t\telse\n\t\t{\n\t\t\tint mno;\n\t\t\tvoid *map;\n\t\t\tmesh_t *m;\n\t\t\tallocvertexbuffer(shaderstate.dynst_buff[1], shaderstate.dynst_size, &shaderstate.dynst_offs[1], &map, vertcount*sizeof(vec2_t));\n\t\t\tfor (mno = 0, vertcount = 0; mno < shaderstate.nummeshes; mno++)\n\t\t\t{\n\t\t\t\tvec2_t *dest = (vec2_t*)((char*)map+vertcount*sizeof(vec2_t));\n\t\t\t\tm = shaderstate.meshlist[mno];\n\t\t\t\tmemcpy(dest, m->lmst_array, m->numvertexes*sizeof(vec2_t));\n\t\t\t\tvertcount += m->numvertexes;\n\t\t\t}\n\t\t\td3dcheck(IDirect3DVertexBuffer9_Unlock(shaderstate.dynst_buff[1]));\n\t\t\td3dcheck(IDirect3DDevice9_SetStreamSource(pD3DDev9, STRM_TC1, shaderstate.dynst_buff[1], shaderstate.dynst_offs[1] - vertcount*sizeof(vec2_t), sizeof(vec2_t)));\n\t\t}\n\t}\n\n\t/*normals/tangents/bitangents*/\n\tif (vdec & D3D_VDEC_NORM)\n\t{\n\t\tif (shaderstate.batchvbo)\n\t\t{\n\t\t\tif (shaderstate.batchvbo && !shaderstate.batchvbo->vaodynamic)\n\t\t\t{\n\t\t\t\td3dcheck(IDirect3DDevice9_SetStreamSource(pD3DDev9, STRM_NORM, shaderstate.batchvbo->normals.d3d.buff, shaderstate.batchvbo->normals.d3d.offs, sizeof(vbovdata_t)));\n\t\t\t\td3dcheck(IDirect3DDevice9_SetStreamSource(pD3DDev9, STRM_NORMS, shaderstate.batchvbo->svector.d3d.buff, shaderstate.batchvbo->svector.d3d.offs, sizeof(vbovdata_t)));\n\t\t\t\td3dcheck(IDirect3DDevice9_SetStreamSource(pD3DDev9, STRM_NORMT, shaderstate.batchvbo->tvector.d3d.buff, shaderstate.batchvbo->tvector.d3d.offs, sizeof(vbovdata_t)));\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\td3dcheck(IDirect3DDevice9_SetStreamSource(pD3DDev9, STRM_NORM, shaderstate.batchvbo->normals.d3d.buff, shaderstate.batchvbo->normals.d3d.offs, sizeof(vec3_t)));\n\t\t\t\td3dcheck(IDirect3DDevice9_SetStreamSource(pD3DDev9, STRM_NORMS, shaderstate.batchvbo->svector.d3d.buff, shaderstate.batchvbo->svector.d3d.offs, sizeof(vec3_t)));\n\t\t\t\td3dcheck(IDirect3DDevice9_SetStreamSource(pD3DDev9, STRM_NORMT, shaderstate.batchvbo->tvector.d3d.buff, shaderstate.batchvbo->tvector.d3d.offs, sizeof(vec3_t)));\n\t\t\t}\n\t\t}\n\t\telse if (shaderstate.meshlist[0]->normals_array && shaderstate.meshlist[0]->snormals_array && shaderstate.meshlist[0]->tnormals_array)\n\t\t{\n\t\t\tint mno;\n\t\t\tvoid *map;\n\t\t\tmesh_t *m;\n\t\t\tint tv = vertcount;\n\n\t\t\tallocvertexbuffer(shaderstate.dynnorm_buff, shaderstate.dynnorm_size, &shaderstate.dynnorm_offs, &map, vertcount*3*sizeof(vec3_t));\n\t\t\tfor (mno = 0, vertcount = 0; mno < shaderstate.nummeshes; mno++)\n\t\t\t{\n\t\t\t\tfloat *dest;\n\t\t\t\tm = shaderstate.meshlist[mno];\n\n\t\t\t\tdest = (float*)((char*)map+vertcount*sizeof(vec3_t));\n\t\t\t\tmemcpy(dest, m->normals_array, m->numvertexes*sizeof(vec3_t));\n\n\t\t\t\tdest += tv*3;\n\t\t\t\tmemcpy(dest, m->snormals_array, m->numvertexes*sizeof(vec3_t));\n\n\t\t\t\tdest += tv*3;\n\t\t\t\tmemcpy(dest, m->tnormals_array, m->numvertexes*sizeof(vec3_t));\n\n\t\t\t\tvertcount += m->numvertexes;\n\t\t\t}\n\t\t\td3dcheck(IDirect3DVertexBuffer9_Unlock(shaderstate.dynnorm_buff));\n\t\t\td3dcheck(IDirect3DDevice9_SetStreamSource(pD3DDev9, STRM_NORM, shaderstate.dynnorm_buff, shaderstate.dynnorm_offs - vertcount*sizeof(vec3_t)*3, sizeof(vec3_t)));\n\t\t\td3dcheck(IDirect3DDevice9_SetStreamSource(pD3DDev9, STRM_NORMS, shaderstate.dynnorm_buff, shaderstate.dynnorm_offs - vertcount*sizeof(vec3_t)*2, sizeof(vec3_t)));\n\t\t\td3dcheck(IDirect3DDevice9_SetStreamSource(pD3DDev9, STRM_NORMT, shaderstate.dynnorm_buff, shaderstate.dynnorm_offs - vertcount*sizeof(vec3_t)*1, sizeof(vec3_t)));\n\t\t}\n\t\telse\n\t\t\tvdec &= ~D3D_VDEC_NORM;\n\t}\n\n\t/*bone weights+indexes*/\n\tif (vdec & D3D_VDEC_SKEL)\n\t{\n\t\t/*FIXME*/\n\t\tvdec &= ~D3D_VDEC_SKEL;\n\t}\n\n\tif (vdec != shaderstate.curvertdecl)\n\t{\n\t\tshaderstate.curvertdecl = vdec;\n\t\td3dcheck(IDirect3DDevice9_SetVertexDeclaration(pD3DDev9, vertexdecls[shaderstate.curvertdecl]));\n\t}\n\n//\tIDirect3DDevice9_SetVertexShaderConstantF(pD3DDev9,\n\tBE_SubmitMeshChain(vertbase, vertfirst, vertcount, idxfirst, idxcount);\n\n\tIDirect3DDevice9_SetVertexShader(pD3DDev9, NULL);\n\tIDirect3DDevice9_SetPixelShader(pD3DDev9, NULL);\n}\n\nvoid D3D9BE_Cull(unsigned int cullflags)\n{\n\tif (shaderstate.flags & BEF_FORCETWOSIDED)\n\t\tcullflags = 0;\n\telse if (cullflags)\n\t\tcullflags ^= r_refdef.flipcull;\n\n\tif (shaderstate.curcull != cullflags)\n\t{\n\t\tshaderstate.curcull = cullflags;\n\t\tif (shaderstate.curcull & 1)\n\t\t{\n\t\t\tif (shaderstate.curcull & SHADER_CULL_FRONT)\n\t\t\t\tIDirect3DDevice9_SetRenderState(pD3DDev9, D3DRS_CULLMODE, D3DCULL_CW);\n\t\t\telse if (shaderstate.curcull & SHADER_CULL_BACK)\n\t\t\t\tIDirect3DDevice9_SetRenderState(pD3DDev9, D3DRS_CULLMODE, D3DCULL_CCW);\n\t\t\telse\n\t\t\t\tIDirect3DDevice9_SetRenderState(pD3DDev9, D3DRS_CULLMODE, D3DCULL_NONE);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (shaderstate.curcull & SHADER_CULL_FRONT)\n\t\t\t\tIDirect3DDevice9_SetRenderState(pD3DDev9, D3DRS_CULLMODE, D3DCULL_CCW);\n\t\t\telse if (shaderstate.curcull & SHADER_CULL_BACK)\n\t\t\t\tIDirect3DDevice9_SetRenderState(pD3DDev9, D3DRS_CULLMODE, D3DCULL_CW);\n\t\t\telse\n\t\t\t\tIDirect3DDevice9_SetRenderState(pD3DDev9, D3DRS_CULLMODE, D3DCULL_NONE);\n\t\t}\n\t}\n}\n\nstatic void BE_DrawMeshChain_Internal(void)\n{\n\tunsigned int vertbase, vertfirst, vertcount, idxcount, idxfirst;\n\tmesh_t *m;\n\tvoid *map;\n\tint i;\n\tunsigned int mno;\n\tunsigned int passno = 0;\n\tshaderpass_t *pass;\n\tshader_t *useshader = shaderstate.curshader;\n\tpolyoffset_t po = shaderstate.curshader->polyoffset;\n\n#ifdef BEF_PUSHDEPTH\n\tif (shaderstate.flags & BEF_PUSHDEPTH)\n\t{\n\t\textern cvar_t r_polygonoffset_submodel_factor, r_polygonoffset_submodel_offset;\n\t\tpo.factor += r_polygonoffset_submodel_factor.value;\n\t\tpo.unit += r_polygonoffset_submodel_offset.value;\n\t}\n#endif\n\n\tD3D9BE_Cull(shaderstate.curshader->flags & (SHADER_CULL_FRONT | SHADER_CULL_BACK));\n\n\tif (po.factor != shaderstate.curpolyoffset.factor)\n\t{\n\t\tshaderstate.curpolyoffset.factor = po.factor;\n\t\tIDirect3DDevice9_SetRenderState(pD3DDev9, D3DRS_SLOPESCALEDEPTHBIAS, *(DWORD*)&po.factor);\n\t}\n\tif (po.unit != shaderstate.curpolyoffset.unit)\n\t{\n\t\tshaderstate.curpolyoffset.unit = po.unit;\n\t\tpo.unit *= shaderstate.gltod3d_depthunit;\n\t\tIDirect3DDevice9_SetRenderState(pD3DDev9, D3DRS_DEPTHBIAS, *(DWORD*)&po.unit);\n\t}\n\n\tswitch (shaderstate.mode)\n\t{\n\tcase BEM_LIGHT:\n//\t\tuseshader = shaderstate.curshader->bemoverrides[shaderstate.lightmode];\n//\t\tif (!useshader)\n\t\t\tuseshader = shaderstate.shader_rtlight;\n\t\tif (!useshader->prog)\n\t\t\treturn;\n\t\tbreak;\n\tcase BEM_DEPTHDARK:\n//\t\tuseshader = shaderstate.shader_depthblack;\n\t\treturn;\n\t\tbreak;\n\tcase BEM_STENCIL:\n\t\treturn;\n\tcase BEM_DEPTHONLY:\n//\t\tuseshader = shaderstate.shader_depthonly;\n\t\treturn;\n\t\tbreak;\n\tdefault:\n\t\tuseshader = shaderstate.curshader;\n\t\tbreak;\t//no need to switch the shader\n\t}\n\n\t//if anything is dynamic ALL must be dynamic\n\t//might want to flag this for multi-mesh batches on pre-t&l cards too, so that there's no gaps.\n\tif ((useshader->flags & SHADER_NEEDSARRAYS || (shaderstate.curbatch && shaderstate.curbatch->fog)) && shaderstate.nummeshes > 0)\n\t\tshaderstate.batchvbo = NULL;\n\n\tif (shaderstate.batchvbo)\n\t{\n\t\tvertfirst = 0;\n\t\tvertcount = shaderstate.batchvbo->vertcount;\n\t\tidxfirst = 0;\n\t\tidxcount = shaderstate.batchvbo->indexcount;\n\t}\n\telse\n\t{\n\t\tvertfirst = 0;\n\t\tfor (mno = 0, vertcount = 0, idxcount = 0; mno < shaderstate.nummeshes; mno++)\n\t\t{\n\t\t\tm = shaderstate.meshlist[mno];\n\t\t\tvertcount += m->numvertexes;\n\t\t\tidxcount += m->numindexes;\n\t\t}\n\t}\n\n\t/*vertex buffers are common to all passes*/\n\tif (shaderstate.batchvbo && !shaderstate.batchvbo->vaodynamic)\n\t{\n\t\td3dcheck(IDirect3DDevice9_SetStreamSource(pD3DDev9, STRM_VERT, shaderstate.batchvbo->coord.d3d.buff, shaderstate.batchvbo->coord.d3d.offs, sizeof(vbovdata_t)));\n\t\td3dcheck(IDirect3DDevice9_SetStreamSource(pD3DDev9, STRM_VERT2, shaderstate.batchvbo->coord2.d3d.buff, shaderstate.batchvbo->coord2.d3d.offs, sizeof(vbovdata_t)));\n\t\tvertfirst = 0;\n\t}\n\telse if (shaderstate.batchvbo)\n\t{\n\t\td3dcheck(IDirect3DDevice9_SetStreamSource(pD3DDev9, STRM_VERT, shaderstate.batchvbo->coord.d3d.buff, shaderstate.batchvbo->coord.d3d.offs, sizeof(vecV_t)));\n\t\td3dcheck(IDirect3DDevice9_SetStreamSource(pD3DDev9, STRM_VERT2, shaderstate.batchvbo->coord2.d3d.buff, shaderstate.batchvbo->coord2.d3d.offs, sizeof(vecV_t)));\n\t\tvertfirst = 0;\n\t}\n\telse\n\t{\n\t\tvertfirst = 0;\n\t\tallocvertexbuffer(shaderstate.dynxyz_buff, shaderstate.dynxyz_size, &shaderstate.dynxyz_offs, &map, vertcount*sizeof(vecV_t));\n\t\tfor (mno = 0, vertcount = 0; mno < shaderstate.nummeshes; mno++)\n\t\t{\n\t\t\tvecV_t *dest = (vecV_t*)((char*)map+vertcount*sizeof(vecV_t));\n\t\t\tm = shaderstate.meshlist[mno];\n\t\t\tdeformgen(&shaderstate.curshader->deforms[0], m->numvertexes, m->xyz_array, dest, m);\n\t\t\tfor (i = 1; i < shaderstate.curshader->numdeforms; i++)\n\t\t\t{\n\t\t\t\tdeformgen(&shaderstate.curshader->deforms[i], m->numvertexes, dest, dest, m);\n\t\t\t}\n\t\t\tvertcount += m->numvertexes;\n\t\t}\n\t\td3dcheck(IDirect3DVertexBuffer9_Unlock(shaderstate.dynxyz_buff));\n\t\td3dcheck(IDirect3DDevice9_SetStreamSource(pD3DDev9, STRM_VERT, shaderstate.dynxyz_buff, shaderstate.dynxyz_offs - vertcount*sizeof(vecV_t), sizeof(vecV_t)));\n\t\td3dcheck(IDirect3DDevice9_SetStreamSource(pD3DDev9, STRM_VERT2, NULL, 0, sizeof(vecV_t)));\n\t}\n\n\t/*index buffers are also common (note that we may still need to stream these when dealing with bsp geometry, to cope with gaps. this is faster than using multiple draw calls.)*/\n\tif (shaderstate.batchvbo)\n\t{\n\t\tif (shaderstate.nummeshes != 1 || (useshader->flags & SHADER_NEEDSARRAYS))\n\t\t{\t//in this case, the vertex data is static, but the index data can have gaps.\n\t\t\t//we're streaming index buffer data only so that we can avoid repeated draw calls. if this stuff was properly built in the first place we wouldn't need to do this. :s\n\t\t\tidxcount = 0;\n\t\t\tfor (mno = 0; mno < shaderstate.nummeshes; mno++)\n\t\t\t{\n\t\t\t\tm = shaderstate.meshlist[mno];\n\t\t\t\tidxcount += m->numindexes;\n\t\t\t}\n\t\t\tidxfirst = allocindexbuffer(&map, idxcount);\n\t\t\tfor (mno = 0; mno < shaderstate.nummeshes; mno++)\n\t\t\t{\n\t\t\t\tm = shaderstate.meshlist[mno];\n\t\t\t\tfor (i = 0; i < m->numindexes; i++)\n\t\t\t\t\t((index_t*)map)[i] = m->vbofirstvert + m->indexes[i];\n\t\t\t\tmap = (char*)map + m->numindexes*sizeof(index_t);\n\t\t\t}\n\t\t\td3dcheck(IDirect3DIndexBuffer9_Unlock(shaderstate.dynidx_buff));\n\t\t\td3dcheck(IDirect3DDevice9_SetIndices(pD3DDev9, shaderstate.dynidx_buff));\n\n\t\t\t//we could constrain vertfirst+vertcount, but I suspect those only matter on pre t&l cards, of which there are very few.\n\n\t\t\tvertbase = 0;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tm = shaderstate.meshlist[0];\n\t\t\tvertbase = 0;//-m->vbofirstvert;\n\t\t\tidxfirst = m->vbofirstelement;\n\t\t\tidxcount = m->numindexes;\n\t\t\tvertfirst = m->vbofirstvert;\n\t\t\tvertcount = m->numvertexes;\n\n\t\t\td3dcheck(IDirect3DDevice9_SetIndices(pD3DDev9, shaderstate.batchvbo->indicies.d3d.buff));\n\t\t}\n\t}\n\telse\n\t{\n\t\tidxfirst = allocindexbuffer(&map, idxcount);\n\t\tfor (mno = 0, vertcount = 0; mno < shaderstate.nummeshes; mno++)\n\t\t{\n\t\t\tm = shaderstate.meshlist[mno];\n\t\t\tfor (i = 0; i < m->numindexes; i++)\n\t\t\t\t((index_t*)map)[i] = m->indexes[i]+vertcount;\n\t\t\tmap = (char*)map + m->numindexes*sizeof(index_t);\n\t\t\tvertcount += m->numvertexes;\n\t\t}\n\t\td3dcheck(IDirect3DIndexBuffer9_Unlock(shaderstate.dynidx_buff));\n\t\td3dcheck(IDirect3DDevice9_SetIndices(pD3DDev9, shaderstate.dynidx_buff));\n\t\tvertbase = 0;\n\t}\n\n\tswitch (shaderstate.mode)\n\t{\n#if 0\n\tcase BEM_LIGHT:\n\t\tif (shaderstate.shader_rtlight->prog)\n\t\t\tBE_RenderMeshProgram(shaderstate.shader_rtlight, vertbase, vertfirst, vertcount, idxfirst, idxcount);\n\t\tbreak;\n\tcase BEM_DEPTHDARK:\n\t\tshaderstate.lastpasscount = 0;\n\t\ti = 0;\n\t\tif (i != shaderstate.curvertdecl)\n\t\t{\n\t\t\tshaderstate.curvertdecl = i;\n\t\t\td3dcheck(IDirect3DDevice9_SetVertexDeclaration(pD3DDev9, vertexdecls[shaderstate.curvertdecl]));\n\t\t}\n\t\t/*deactivate any extras*/\n\t\tfor (passno = 1; passno < shaderstate.lastpasscount; )\n\t\t{\n\t\t\td3dcheck(IDirect3DDevice9_SetStreamSource(pD3DDev9, STRM_TC0+passno, NULL, 0, 0));\n\t\t\tBindTexture(passno, NULL);\n\t\t\td3dcheck(IDirect3DDevice9_SetTextureStageState(pD3DDev9, passno, D3DTSS_COLOROP, D3DTOP_DISABLE));\n\t\t\td3dcheck(IDirect3DDevice9_SetTextureStageState(pD3DDev9, passno, D3DTSS_ALPHAOP, D3DTOP_DISABLE));\n\t\t\tpassno++;\n\t\t}\n\t\tBindTexture(passno, NULL);\n\t\td3dcheck(IDirect3DDevice9_SetTextureStageState(pD3DDev9, 0, D3DTSS_COLOROP, D3DTOP_SELECTARG1));\n\t\td3dcheck(IDirect3DDevice9_SetTextureStageState(pD3DDev9, 0, D3DTSS_COLORARG1, D3DTA_CONSTANT));\n\t\td3dcheck(IDirect3DDevice9_SetTextureStageState(pD3DDev9, 0, D3DTSS_CONSTANT, D3DCOLOR_RGBA(0,0,0,255)));\n\t\td3dcheck(IDirect3DDevice9_SetTextureStageState(pD3DDev9, 0, D3DTSS_ALPHAOP, D3DTOP_DISABLE));\n\t\tshaderstate.lastpasscount = 1;\n\t\tBE_ApplyShaderBits(SBITS_MISC_DEPTHWRITE);\n\n\t\tBE_SubmitMeshChain(vertbase, vertfirst, vertcount, idxfirst, idxcount);\n\t\tbreak;\n\tcase BEM_STENCIL:\n\t\tbreak;\n\tcase BEM_DEPTHONLY:\n\t\tshaderstate.lastpasscount = 0;\n\t\ti = 0;\n\t\tif (i != shaderstate.curvertdecl)\n\t\t{\n\t\t\tshaderstate.curvertdecl = i;\n\t\t\td3dcheck(IDirect3DDevice9_SetVertexDeclaration(pD3DDev9, vertexdecls[shaderstate.curvertdecl]));\n\t\t}\n\t\tIDirect3DDevice9_SetRenderState(pD3DDev9, D3DRS_COLORWRITEENABLE, 0);\n\t\t/*deactivate any extras*/\n\t\tfor (passno = 0; passno < shaderstate.lastpasscount; )\n\t\t{\n\t\t\td3dcheck(IDirect3DDevice9_SetStreamSource(pD3DDev9, STRM_TC0+passno, NULL, 0, 0));\n\t\t\tBindTexture(passno, NULL);\n\t\t\td3dcheck(IDirect3DDevice9_SetTextureStageState(pD3DDev9, passno, D3DTSS_COLOROP, D3DTOP_DISABLE));\n\t\t\td3dcheck(IDirect3DDevice9_SetTextureStageState(pD3DDev9, passno, D3DTSS_ALPHAOP, D3DTOP_DISABLE));\n\t\t\tpassno++;\n\t\t}\n\t\tshaderstate.lastpasscount = 0;\n\t\tBE_ApplyShaderBits(SBITS_MISC_DEPTHWRITE);\n\t\tBE_SubmitMeshChain(vertbase, vertfirst, vertcount, idxfirst, idxcount);\n\t\tIDirect3DDevice9_SetRenderState(pD3DDev9, D3DRS_COLORWRITEENABLE, D3DCOLORWRITEENABLE_RED|D3DCOLORWRITEENABLE_GREEN|D3DCOLORWRITEENABLE_BLUE|D3DCOLORWRITEENABLE_ALPHA);\n\t\tbreak;\n#endif\n\tcase BEM_FOG:\n\t\tif (!TEXVALID(shaderstate.fogtexture) || shaderstate.fogdensity != r_refdef.globalfog.density || shaderstate.fogdepth != 2048)\n\t\t{\n\t\t\tshaderstate.fogdensity = r_refdef.globalfog.density;\n\t\t\tshaderstate.fogdepth = 2048;\n\t\t\tshaderstate.fogfar = 1.0f/shaderstate.fogdepth; /*scaler for z coords*/\n\t\t\tGenerateFogTexture(&shaderstate.fogtexture, shaderstate.fogdensity, shaderstate.fogdepth);\n\t\t}\n\n\t\twhile(shaderstate.lastpasscount>1)\n\t\t{\n\t\t\tpassno = --shaderstate.lastpasscount;\n\t\t\td3dcheck(IDirect3DDevice9_SetStreamSource(pD3DDev9, STRM_TC0+passno, NULL, 0, 0));\n\t\t\tBindTexture(passno, NULL);\n\t\t\td3dcheck(IDirect3DDevice9_SetTextureStageState(pD3DDev9, passno, D3DTSS_COLOROP, D3DTOP_DISABLE));\n\t\t\td3dcheck(IDirect3DDevice9_SetTextureStageState(pD3DDev9, passno, D3DTSS_ALPHAOP, D3DTOP_DISABLE));\n\t\t}\n\t\tshaderstate.lastpasscount = 1;\n\t\tpassno = 0;\n\t\tBindTexture(passno, shaderstate.fogtexture);\n\t\tBE_ApplyTMUState(passno, shaderstate.curtexflags[passno]);\n\n\t\tVector4Set((qbyte*)&shaderstate.passcolour, r_refdef.globalfog.colour[2]*255, r_refdef.globalfog.colour[1]*255, r_refdef.globalfog.colour[0]*255, r_refdef.globalfog.alpha*255);\n\t\tIDirect3DDevice9_SetTextureStageState(pD3DDev9, passno, D3DTSS_CONSTANT, shaderstate.passcolour);\n\t\tIDirect3DDevice9_SetRenderState(pD3DDev9, D3DRS_COLORVERTEX, FALSE);\n\t\tIDirect3DDevice9_SetTextureStageState(pD3DDev9, passno, D3DTSS_COLORARG1, D3DTA_CONSTANT);\n\t\tIDirect3DDevice9_SetTextureStageState(pD3DDev9, passno, D3DTSS_COLORARG2, D3DTA_TEXTURE);\n\t\tIDirect3DDevice9_SetTextureStageState(pD3DDev9, passno, D3DTSS_COLOROP, D3DTOP_MODULATE);\n\t\tIDirect3DDevice9_SetTextureStageState(pD3DDev9, passno, D3DTSS_ALPHAARG1, D3DTA_CONSTANT);\n\t\tIDirect3DDevice9_SetTextureStageState(pD3DDev9, passno, D3DTSS_ALPHAARG2, D3DTA_TEXTURE);\n\t\tIDirect3DDevice9_SetTextureStageState(pD3DDev9, passno, D3DTSS_ALPHAOP, D3DTOP_MODULATE);\n\t\tBE_ApplyShaderBits(SBITS_SRCBLEND_SRC_ALPHA | SBITS_DSTBLEND_ONE_MINUS_SRC_ALPHA | (useshader->numpasses?SBITS_DEPTHFUNC_EQUAL:0));\n\n\t\tallocvertexbuffer(shaderstate.dynst_buff[passno], shaderstate.dynst_size, &shaderstate.dynst_offs[passno], &map, vertcount*sizeof(vec2_t));\n\t\tfor (mno = 0, vertcount = 0; mno < shaderstate.nummeshes; mno++)\n\t\t{\n\t\t\tm = shaderstate.meshlist[mno];\n\t\t\ttcgen_fog((float*)map+vertcount*2, m->numvertexes, (float*)m->xyz_array, NULL); \n\t\t\tvertcount += m->numvertexes;\n\t\t}\n\t\tIDirect3DVertexBuffer9_Unlock(shaderstate.dynst_buff[passno]);\n\t\tIDirect3DDevice9_SetStreamSource(pD3DDev9, STRM_TC0+passno, shaderstate.dynst_buff[passno], shaderstate.dynst_offs[passno] - vertcount*sizeof(vec2_t), sizeof(vec2_t));\n\n\t\tif (D3D_VDEC_ST0 != shaderstate.curvertdecl)\n\t\t{\n\t\t\tshaderstate.curvertdecl = D3D_VDEC_ST0;\n\t\t\td3dcheck(IDirect3DDevice9_SetVertexDeclaration(pD3DDev9, vertexdecls[shaderstate.curvertdecl]));\n\t\t}\n\n\t\tBE_SubmitMeshChain(vertbase, vertfirst, vertcount, idxfirst, idxcount);\n\t\tbreak;\n\tdefault:\n\tcase BEM_STANDARD:\n\t\tif (useshader->prog)\n\t\t{\n\t\t\tBE_RenderMeshProgram(useshader, vertbase, vertfirst, vertcount, idxfirst, idxcount);\n\t\t}\n\t\telse\n\t\t{\n\t\t\t/*now go through and flush each pass*/\n\t\t\tfor (passno = 0, pass = useshader->passes; passno < useshader->numpasses; passno += pass->numMergedPasses)\n\t\t\t{\n\t\t\t\tif (!BE_DrawMeshChain_SetupPass(pass+passno, vertcount))\n\t\t\t\t\tcontinue;\n\t\t#ifdef BENCH\n\t\t\t\tshaderstate.bench.draws++;\n\t\t\t\tif (shaderstate.bench.clamp && shaderstate.bench.clamp < shaderstate.bench.draws)\n\t\t\t\t\tcontinue;\n\t\t#endif\n\t\t\t\tBE_SubmitMeshChain(vertbase, vertfirst, vertcount, idxfirst, idxcount);\n//\t\t\t\td3dcheck(IDirect3DDevice9_DrawIndexedPrimitive(pD3DDev9, D3DPT_TRIANGLELIST, 0, 0, vertcount, idxfirst, idxcount/3));\n\t\t\t}\n\t\t}\n\n\t\tif (shaderstate.curbatch && shaderstate.curbatch->fog && shaderstate.curbatch->fog->shader)\n\t\t{\n\t\t\tif (!TEXVALID(shaderstate.fogtexture) || shaderstate.fogdensity != shaderstate.curbatch->fog->shader->fog_dist || shaderstate.fogdepth != 2048)\n\t\t\t{\n\t\t\t\tshaderstate.fogdensity = shaderstate.curbatch->fog->shader->fog_dist;\n\t\t\t\tshaderstate.fogdepth = 2048;\n\t\t\t\tshaderstate.fogfar = 1.0f/shaderstate.fogdepth; /*scaler for z coords*/\n\t\t\t\tGenerateFogTexture(&shaderstate.fogtexture, shaderstate.fogdensity, shaderstate.fogdepth);\n\t\t\t}\n\n\t\t\twhile(shaderstate.lastpasscount>1)\n\t\t\t{\n\t\t\t\tpassno = --shaderstate.lastpasscount;\n\t\t\t\td3dcheck(IDirect3DDevice9_SetStreamSource(pD3DDev9, STRM_TC0+passno, NULL, 0, 0));\n\t\t\t\tBindTexture(passno, NULL);\n\t\t\t\td3dcheck(IDirect3DDevice9_SetTextureStageState(pD3DDev9, passno, D3DTSS_COLOROP, D3DTOP_DISABLE));\n\t\t\t\td3dcheck(IDirect3DDevice9_SetTextureStageState(pD3DDev9, passno, D3DTSS_ALPHAOP, D3DTOP_DISABLE));\n\t\t\t}\n\t\t\tshaderstate.lastpasscount = 1;\n\t\t\tpassno = 0;\n\t\t\tBindTexture(passno, shaderstate.fogtexture);\n\t\t\tBE_ApplyTMUState(passno, shaderstate.curtexflags[passno]);\n\n\t\t\tVector4Set((qbyte*)&shaderstate.passcolour, shaderstate.curbatch->fog->shader->fog_color[2], shaderstate.curbatch->fog->shader->fog_color[1], shaderstate.curbatch->fog->shader->fog_color[0], shaderstate.curbatch->fog->shader->fog_color[3]);\n\t\t\tIDirect3DDevice9_SetTextureStageState(pD3DDev9, passno, D3DTSS_CONSTANT, shaderstate.passcolour);\n\t\t\tIDirect3DDevice9_SetRenderState(pD3DDev9, D3DRS_COLORVERTEX, FALSE);\n\t\t\tIDirect3DDevice9_SetTextureStageState(pD3DDev9, passno, D3DTSS_COLORARG1, D3DTA_CONSTANT);\n\t\t\tIDirect3DDevice9_SetTextureStageState(pD3DDev9, passno, D3DTSS_COLORARG2, D3DTA_TEXTURE);\n\t\t\tIDirect3DDevice9_SetTextureStageState(pD3DDev9, passno, D3DTSS_COLOROP, D3DTOP_MODULATE);\n\t\t\tIDirect3DDevice9_SetTextureStageState(pD3DDev9, passno, D3DTSS_ALPHAARG1, D3DTA_CONSTANT);\n\t\t\tIDirect3DDevice9_SetTextureStageState(pD3DDev9, passno, D3DTSS_ALPHAARG2, D3DTA_TEXTURE);\n\t\t\tIDirect3DDevice9_SetTextureStageState(pD3DDev9, passno, D3DTSS_ALPHAOP, D3DTOP_MODULATE);\n\t\t\tBE_ApplyShaderBits(SBITS_SRCBLEND_SRC_ALPHA | SBITS_DSTBLEND_ONE_MINUS_SRC_ALPHA | (useshader->numpasses?SBITS_DEPTHFUNC_EQUAL:0));\n\n\t\t\tallocvertexbuffer(shaderstate.dynst_buff[passno], shaderstate.dynst_size, &shaderstate.dynst_offs[passno], &map, vertcount*sizeof(vec2_t));\n\t\t\tfor (mno = 0, vertcount = 0; mno < shaderstate.nummeshes; mno++)\n\t\t\t{\n\t\t\t\tm = shaderstate.meshlist[mno];\n\t\t\t\ttcgen_fog((float*)map+vertcount*2, m->numvertexes, (float*)m->xyz_array, shaderstate.curbatch->fog); \n\t\t\t\tvertcount += m->numvertexes;\n\t\t\t}\n\t\t\tIDirect3DVertexBuffer9_Unlock(shaderstate.dynst_buff[passno]);\n\t\t\tIDirect3DDevice9_SetStreamSource(pD3DDev9, STRM_TC0+passno, shaderstate.dynst_buff[passno], shaderstate.dynst_offs[passno] - vertcount*sizeof(vec2_t), sizeof(vec2_t));\n\n\t\t\tif (D3D_VDEC_ST0 != shaderstate.curvertdecl)\n\t\t\t{\n\t\t\t\tshaderstate.curvertdecl = D3D_VDEC_ST0;\n\t\t\t\td3dcheck(IDirect3DDevice9_SetVertexDeclaration(pD3DDev9, vertexdecls[shaderstate.curvertdecl]));\n\t\t\t}\n\n\t\t\tBE_SubmitMeshChain(vertbase, vertfirst, vertcount, idxfirst, idxcount);\n\t\t}\n\t\tbreak;\n\t}\n}\n\nvoid D3D9BE_SelectMode(backendmode_t mode)\n{\n\tshaderstate.mode = mode;\n\n\tif (mode == BEM_STENCIL)\n\t\tBE_ApplyShaderBits(SBITS_MASK_BITS);\n}\n\nqboolean D3D9BE_SelectDLight(dlight_t *dl, vec3_t colour, vec3_t axis[3], unsigned int lmode)\n{\n\tshaderstate.curdlight = dl;\n\tVectorCopy(colour, shaderstate.curdlight_colours);\n\n\tif (lmode != LSHADER_STANDARD)\n\t\treturn false;\n\treturn true;\n}\n\nvoid D3D9BE_SelectEntity(entity_t *ent)\n{\n\tshaderstate.curentity = ent;\n\tD3D9BE_RotateForEntity(ent, ent->model);\n}\n\n#if 1\nvoid D3D9BE_GenBatchVBOs(vbo_t **vbochain, batch_t *firstbatch, batch_t *stopbatch)\n{\n\tint maxvboelements;\n\tint maxvboverts;\n\tint vert = 0, idx = 0;\n\tbatch_t *batch;\n\tvbo_t *vbo;\n\tint i, j;\n\tmesh_t *m;\n\tIDirect3DVertexBuffer9 *vbuff;\n\tIDirect3DIndexBuffer9 *ebuff;\n\tindex_t *vboedata;\n\tvbovdata_t *vbovdata;\n\n\tmaxvboverts = 0;\n\tmaxvboelements = 0;\n\tfor(batch = firstbatch; batch != stopbatch; batch = batch->next)\n\t{\n\t\tfor (i=0 ; i<batch->maxmeshes ; i++)\n\t\t{\n\t\t\tm = batch->mesh[i];\n\t\t\tmaxvboelements += m->numindexes;\n\t\t\tmaxvboverts += m->numvertexes;\n\t\t}\n\t}\n\tif (maxvboverts > MAX_INDICIES)\n\t\tSys_Error(\"Building a vbo with too many verticies\\n\");\n\n\tif (!maxvboelements)\n\t\treturn;\n\n\tvbo = Z_Malloc(sizeof(*vbo));\n\n\tIDirect3DDevice9_CreateIndexBuffer(pD3DDev9, sizeof(index_t) * maxvboelements, 0, D3DFMT_QINDEX, D3DPOOL_MANAGED, &ebuff, NULL);\n\tIDirect3DDevice9_CreateVertexBuffer(pD3DDev9, sizeof(*vbovdata) * maxvboverts, D3DUSAGE_WRITEONLY, 0, D3DPOOL_MANAGED, &vbuff, NULL);\n\n\tvbovdata = NULL;\n\tvbo->vaodynamic = 0;\n\tvbo->coord.d3d.buff = vbuff;\n\tvbo->coord.d3d.offs = (quintptr_t)&vbovdata->coord;\n\tvbo->texcoord.d3d.buff = vbuff;\n\tvbo->texcoord.d3d.offs = (quintptr_t)&vbovdata->tex;\n\tvbo->lmcoord[0].d3d.buff = vbuff;\n\tvbo->lmcoord[0].d3d.offs = (quintptr_t)&vbovdata->lm;\n\tvbo->normals.d3d.buff = vbuff;\n\tvbo->normals.d3d.offs = (quintptr_t)&vbovdata->ndir;\n\tvbo->svector.d3d.buff = vbuff;\n\tvbo->svector.d3d.offs = (quintptr_t)&vbovdata->sdir;\n\tvbo->tvector.d3d.buff = vbuff;\n\tvbo->tvector.d3d.offs = (quintptr_t)&vbovdata->tdir;\n\tvbo->colours[0].d3d.buff = vbuff;\n\tvbo->colours[0].d3d.offs = (quintptr_t)&vbovdata->colorsb;\n\tvbo->indicies.d3d.buff = ebuff;\n\tvbo->indicies.d3d.offs = 0;\n\n\tIDirect3DIndexBuffer9_Lock(ebuff, 0, sizeof(index_t) * maxvboelements, (void**)&vboedata, D3DLOCK_DISCARD);\n\tIDirect3DVertexBuffer9_Lock(vbuff, 0, sizeof(*vbovdata) * maxvboverts, (void**)&vbovdata, D3DLOCK_DISCARD);\n\n\tfor(batch = firstbatch; batch != stopbatch; batch = batch->next)\n\t{\n\t\tbatch->vbo = vbo;\n\t\tfor (j=0 ; j<batch->maxmeshes ; j++)\n\t\t{\n\t\t\tm = batch->mesh[j];\n\t\t\tm->vbofirstvert = vert;\n\t\t\tfor (i = 0; i < m->numvertexes; i++)\n\t\t\t{\n\t\t\t\tVectorCopy(m->xyz_array[i],\t\t\tvbovdata->coord);\n\t\t\t\tvbovdata->coord[3] = 1;\n\t\t\t\tVector2Copy(m->st_array[i],\t\t\tvbovdata->tex);\n\t\t\t\tVector2Copy(m->lmst_array[0][i],\t\tvbovdata->lm);\n\t\t\t\tif (m->normals_array)\n\t\t\t\t{\n\t\t\t\t\tVectorCopy(m->normals_array[i],\t\tvbovdata->ndir);\n\t\t\t\t\tVectorCopy(m->snormals_array[i],\tvbovdata->sdir);\n\t\t\t\t\tVectorCopy(m->tnormals_array[i],\tvbovdata->tdir);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tVectorSet(vbovdata->ndir, 0, 0, 1);\n\t\t\t\t\tVectorSet(vbovdata->sdir, 1, 0, 0);\n\t\t\t\t\tVectorSet(vbovdata->tdir, 0, 1, 0);\n\t\t\t\t}\n\t\t\t\tVector4Scale(m->colors4f_array[0][i],\t255, vbovdata->colorsb);\n\n\t\t\t\tvbovdata++;\n\t\t\t}\n\n\t\t\tm->vbofirstelement = idx;\n\t\t\tfor (i = 0; i < m->numindexes; i++)\n\t\t\t{\n\t\t\t\t*vboedata++ = vert + m->indexes[i];\n\t\t\t}\n\t\t\tidx += m->numindexes;\n\t\t\tvert += m->numvertexes;\n\t\t}\n\t}\n\n\tvbo->indexcount = idx;\n\tvbo->vertcount = vert;\n\n\tIDirect3DIndexBuffer9_Unlock(ebuff);\n\tIDirect3DVertexBuffer9_Unlock(vbuff);\n\n\tvbo->next = *vbochain;\n\t*vbochain = vbo;\n}\n\nvoid D3D9BE_GenBrushModelVBO(model_t *mod)\n{\n\tunsigned int vcount, cvcount;\n\n\n\tbatch_t *batch, *fbatch;\n\tint sortid;\n\tint i;\n\n\tfbatch = NULL;\n\tvcount = 0;\n\tfor (sortid = 0; sortid < SHADER_SORT_COUNT; sortid++)\n\t{\n\t\tif (!mod->batches[sortid])\n\t\t\tcontinue;\n\n\t\tfor (fbatch = batch = mod->batches[sortid]; batch != NULL; batch = batch->next)\n\t\t{\n\t\t\tfor (i = 0, cvcount = 0; i < batch->maxmeshes; i++)\n\t\t\t\tcvcount += batch->mesh[i]->numvertexes;\n\n\t\t\t//firstmesh got reused as the number of verticies in each batch\n\t\t\tif (vcount + cvcount > MAX_INDICIES)\n\t\t\t{\n\t\t\t\tD3D9BE_GenBatchVBOs(&mod->vbos, fbatch, batch);\n\t\t\t\tfbatch = batch;\n\t\t\t\tvcount = 0;\n\t\t\t}\n\n\t\t\tvcount += cvcount;\n\t\t}\n\n\t\tD3D9BE_GenBatchVBOs(&mod->vbos, fbatch, batch);\n\t}\n}\n#else\n/*Generates an optimised vbo for each of the given model's textures*/\nvoid D3D9BE_GenBrushModelVBO(model_t *mod)\n{\n#if 1\n\tunsigned int maxvboverts;\n\tunsigned int maxvboelements;\n\n\tunsigned int t;\n\tunsigned int i;\n\tunsigned int v;\n\tunsigned int vcount, ecount;\n\tunsigned int pervertsize;\t//erm, that name wasn't intentional\n\tunsigned int meshes;\n\n\tvbo_t *vbo;\n\tindex_t *vboedata;\n\tmesh_t *m;\n\tchar *vbovdata;\n\tIDirect3DVertexBuffer9 *vbuff;\n\tIDirect3DIndexBuffer9 *ebuff;\n\n\tvecV_t *coord;\n\tvec2_t *texcoord;\n\tvec2_t *lmcoord;\n\tvec3_t *normals;\n\tvec3_t *svector;\n\tvec3_t *tvector;\n\tbyte_vec4_t *colours;\n\n\tif (!mod->numsurfaces)\n\t\treturn;\n\n\tfor (t = 0; t < mod->numtextures; t++)\n\t{\n\t\tif (!mod->textures[t])\n\t\t\tcontinue;\n\t\tvbo = &mod->textures[t]->vbo;\n\t\tBE_ClearVBO(vbo, false);\n\n\t\tmaxvboverts = 0;\n\t\tmaxvboelements = 0;\n\t\tmeshes = 0;\n\t\tfor (i=0 ; i<mod->numsurfaces ; i++)\n\t\t{\n\t\t\tif (mod->surfaces[i].texinfo->texture != mod->textures[t])\n\t\t\t\tcontinue;\n\t\t\tm = mod->surfaces[i].mesh;\n\t\t\tif (!m)\n\t\t\t\tcontinue;\n\n\t\t\tmeshes++;\n\t\t\tmaxvboelements += m->numindexes;\n\t\t\tmaxvboverts += m->numvertexes;\n\t\t}\n#if sizeof_index_t == 2\n\t\tif (maxvboverts > (1u<<(sizeof(index_t)*8))-1)\n\t\t\tcontinue;\n#endif\n\t\tif (!maxvboverts)\n\t\t\tcontinue;\n\n\t\t//fixme: stop this from leaking!\n\t\tvcount = 0;\n\t\tecount = 0;\n\n\t\tpervertsize =\tsizeof(vecV_t)+\t//coord\n\t\t\t\t\tsizeof(vec2_t)+\t//tex\n\t\t\t\t\tsizeof(vec2_t)+\t//lm\n\t\t\t\t\tsizeof(vec3_t)+\t//normal\n\t\t\t\t\tsizeof(vec3_t)+\t//sdir\n\t\t\t\t\tsizeof(vec3_t)+\t//tdir\n\t\t\t\t\tsizeof(vec4_t);\t//colours\n\n\t\tIDirect3DDevice9_CreateIndexBuffer(pD3DDev9, sizeof(index_t) * maxvboelements, 0, D3DFMT_QINDEX, D3DPOOL_MANAGED, &ebuff, NULL);\n\t\tIDirect3DDevice9_CreateVertexBuffer(pD3DDev9, pervertsize * maxvboverts, D3DUSAGE_WRITEONLY, 0, D3DPOOL_MANAGED, &vbuff, NULL);\n\n\t\tIDirect3DIndexBuffer9_Lock(ebuff, 0, sizeof(index_t) * maxvboelements, &vboedata, D3DLOCK_DISCARD);\n\t\tIDirect3DVertexBuffer9_Lock(vbuff, 0, pervertsize * maxvboverts, &vbovdata, D3DLOCK_DISCARD);\n\n\t\tvbo->vertdata = vbuff;\n\t\tvbo->indexcount = maxvboelements;\n\t\tvbo->vertcount = maxvboverts;\n\n\t\tvbo->coord.d3d.buff = vbuff;\n\t\tvbo->coord.d3d.offs = 0;\n\t\tvbo->texcoord.d3d.buff = vbuff;\n\t\tvbo->texcoord.d3d.offs = vbo->coord.d3d.offs+maxvboverts*sizeof(vecV_t);\n\t\tvbo->lmcoord.d3d.buff = vbuff;\n\t\tvbo->lmcoord.d3d.offs = vbo->texcoord.d3d.offs+maxvboverts*sizeof(vec2_t);\n\t\tvbo->normals.d3d.buff = vbuff;\n\t\tvbo->normals.d3d.offs = vbo->lmcoord.d3d.offs+maxvboverts*sizeof(vec2_t);\n\t\tvbo->svector.d3d.buff = vbuff;\n\t\tvbo->svector.d3d.offs = vbo->normals.d3d.offs+maxvboverts*sizeof(vec3_t);\n\t\tvbo->tvector.d3d.buff = vbuff;\n\t\tvbo->tvector.d3d.offs = vbo->svector.d3d.offs+maxvboverts*sizeof(vec3_t);\n\t\tvbo->colours.d3d.buff = vbuff;\n\t\tvbo->colours.d3d.offs = vbo->tvector.d3d.offs+maxvboverts*sizeof(vec3_t);\n\t\tvbo->indicies.d3d.buff = ebuff;\n\t\tvbo->indicies.d3d.offs = 0;\n\n\t\tcoord = (void*)(vbovdata + vbo->coord.d3d.offs);\n\t\ttexcoord = (void*)(vbovdata + vbo->texcoord.d3d.offs);\n\t\tlmcoord = (void*)(vbovdata + vbo->lmcoord.d3d.offs);\n\t\tnormals = (void*)(vbovdata + vbo->normals.d3d.offs);\n\t\tsvector = (void*)(vbovdata + vbo->svector.d3d.offs);\n\t\ttvector = (void*)(vbovdata + vbo->tvector.d3d.offs);\n\t\tcolours = (void*)(vbovdata + vbo->colours.d3d.offs);\n\n\t\tvbo->meshcount = meshes;\n\t\tvbo->meshlist = BZ_Malloc(meshes*sizeof(*vbo->meshlist));\n\n\t\tmeshes = 0;\n\n\t\tfor (i=0 ; i<mod->numsurfaces ; i++)\n\t\t{\n\t\t\tif (mod->surfaces[i].texinfo->texture != mod->textures[t])\n\t\t\t\tcontinue;\n\t\t\tm = mod->surfaces[i].mesh;\n\t\t\tif (!m)\n\t\t\t\tcontinue;\n\n\t\t\tmod->surfaces[i].mark = &vbo->meshlist[meshes++];\n\t\t\t*mod->surfaces[i].mark = NULL;\n\n\t\t\tm->vbofirstvert = vcount;\n\t\t\tm->vbofirstelement = ecount;\n\t\t\tfor (v = 0; v < m->numindexes; v++)\n\t\t\t\tvboedata[ecount++] = vcount + m->indexes[v];\n\t\t\tfor (v = 0; v < m->numvertexes; v++)\n\t\t\t{\n\t\t\t\tcoord[vcount+v][0] = m->xyz_array[v][0];\n\t\t\t\tcoord[vcount+v][1] = m->xyz_array[v][1];\n\t\t\t\tcoord[vcount+v][2] = m->xyz_array[v][2];\n\t\t\t\tcoord[vcount+v][3] = 1;\n\t\t\t\tif (m->st_array)\n\t\t\t\t{\n\t\t\t\t\ttexcoord[vcount+v][0] = m->st_array[v][0];\n\t\t\t\t\ttexcoord[vcount+v][1] = m->st_array[v][1];\n\t\t\t\t}\n\t\t\t\tif (m->lmst_array)\n\t\t\t\t{\n\t\t\t\t\tlmcoord[vcount+v][0] = m->lmst_array[v][0];\n\t\t\t\t\tlmcoord[vcount+v][1] = m->lmst_array[v][1];\n\t\t\t\t}\n\t\t\t\tif (m->normals_array)\n\t\t\t\t{\n\t\t\t\t\tnormals[vcount+v][0] = m->normals_array[v][0];\n\t\t\t\t\tnormals[vcount+v][1] = m->normals_array[v][1];\n\t\t\t\t\tnormals[vcount+v][2] = m->normals_array[v][2];\n\t\t\t\t}\n\t\t\t\tif (m->snormals_array)\n\t\t\t\t{\n\t\t\t\t\tsvector[vcount+v][0] = m->snormals_array[v][0];\n\t\t\t\t\tsvector[vcount+v][1] = m->snormals_array[v][1];\n\t\t\t\t\tsvector[vcount+v][2] = m->snormals_array[v][2];\n\t\t\t\t}\n\t\t\t\tif (m->tnormals_array)\n\t\t\t\t{\n\t\t\t\t\ttvector[vcount+v][0] = m->tnormals_array[v][0];\n\t\t\t\t\ttvector[vcount+v][1] = m->tnormals_array[v][1];\n\t\t\t\t\ttvector[vcount+v][2] = m->tnormals_array[v][2];\n\t\t\t\t}\n\t\t\t\tif (m->colors4b_array)\n\t\t\t\t{\n\t\t\t\t\tcolours[vcount+v][0] = m->colors4b_array[v][0];\n\t\t\t\t\tcolours[vcount+v][1] = m->colors4b_array[v][1];\n\t\t\t\t\tcolours[vcount+v][2] = m->colors4b_array[v][2];\n\t\t\t\t\tcolours[vcount+v][3] = m->colors4b_array[v][3];\n\t\t\t\t}\n\t\t\t\tif (m->colors4f_array[0])\n\t\t\t\t{\n\t\t\t\t\tcolours[vcount+v][0] = m->colors4f_array[0][v][0] * 255;\n\t\t\t\t\tcolours[vcount+v][1] = m->colors4f_array[0][v][1] * 255;\n\t\t\t\t\tcolours[vcount+v][2] = m->colors4f_array[0][v][2] * 255;\n\t\t\t\t\tcolours[vcount+v][3] = m->colors4f_array[0][v][3] * 255;\n\t\t\t\t}\n\t\t\t}\n\t\t\tvcount += v;\n\t\t}\n\n\t\tIDirect3DIndexBuffer9_Unlock(ebuff);\n\t\tIDirect3DVertexBuffer9_Unlock(vbuff);\n\t}\n#endif\n}\n#endif\n/*Wipes a vbo*/\nvoid D3D9BE_ClearVBO(vbo_t *vbo, qboolean dataonly)\n{\n\tIDirect3DVertexBuffer9 *vbuff = vbo->coord.d3d.buff;\n\tIDirect3DIndexBuffer9 *ebuff = vbo->indicies.d3d.buff;\n\tif (vbuff)\n\t\tIDirect3DVertexBuffer9_Release(vbuff);\n\tif (ebuff)\n\t\tIDirect3DIndexBuffer9_Release(ebuff);\n\tvbo->coord.d3d.buff = NULL;\n\tvbo->indicies.d3d.buff = NULL;\n\n\tif (!dataonly)\n\t\tfree(vbo);\n}\n\n/*upload all lightmaps at the start to reduce lags*/\nstatic void BE_UploadLightmaps(qboolean force)\n{\n\tint i;\n\tlightmapinfo_t *lm;\n\n\tfor (i = 0; i < numlightmaps; i++)\n\t{\n\t\tlm = lightmap[i];\n\t\tif (!lm)\n\t\t\tcontinue;\n\n\t\tif (force)\n\t\t{\n\t\t\tlm->rectchange.l = 0;\n\t\t\tlm->rectchange.t = 0;\n\t\t\tlm->rectchange.r = lm->width;\n\t\t\tlm->rectchange.b = lm->height;\n\t\t}\n\n\t\tif (lightmap[i]->modified)\n\t\t{\n\t\t\textern cvar_t r_lightmap_nearest;\n\t\t\tIDirect3DTexture9 *tex;\n\t\t\tD3DLOCKED_RECT lock;\n\t\t\tRECT rect;\n\t\t\tglRect_t *theRect = &lm->rectchange;\n\t\t\tint r;\n\t\t\tint w;\n\t\t\tint pixbytes;\n\n\t\t\tif (!TEXLOADED(lm->lightmap_texture))\n\t\t\t\tlm->lightmap_texture = Image_CreateTexture(\"***lightmap***\", NULL, (r_lightmap_nearest.ival?IF_NEAREST:IF_LINEAR)|IF_NOMIPMAP);\n\t\t\ttex = lm->lightmap_texture->ptr;\n\t\t\tif (!tex)\n\t\t\t{\n\t\t\t\tswitch(lm->fmt)\n\t\t\t\t{\n\t\t\t\tdefault:\n\t\t\t\t\tbreak;\n\t\t\t\tcase PTI_BGRA8:\n\t\t\t\t\tIDirect3DDevice9_CreateTexture(pD3DDev9, lm->width, lm->height, 1, 0, D3DFMT_A8R8G8B8, D3DPOOL_MANAGED, &tex, NULL);\n\t\t\t\t\tbreak;\n\t\t\t\tcase PTI_BGRX8:\n\t\t\t\t\tIDirect3DDevice9_CreateTexture(pD3DDev9, lm->width, lm->height, 1, 0, D3DFMT_X8R8G8B8, D3DPOOL_MANAGED, &tex, NULL);\n\t\t\t\t\tbreak;\n\t\t\t\tcase PTI_RGB565:\n\t\t\t\t\tIDirect3DDevice9_CreateTexture(pD3DDev9, lm->width, lm->height, 1, 0, D3DFMT_R5G6B5, D3DPOOL_MANAGED, &tex, NULL);\n\t\t\t\t\tbreak;\n\t\t\t\tcase PTI_ARGB4444:\n\t\t\t\t\tIDirect3DDevice9_CreateTexture(pD3DDev9, lm->width, lm->height, 1, 0, D3DFMT_A4R4G4B4, D3DPOOL_MANAGED, &tex, NULL);\n\t\t\t\t\tbreak;\n\t\t\t\tcase PTI_ARGB1555:\n\t\t\t\t\tIDirect3DDevice9_CreateTexture(pD3DDev9, lm->width, lm->height, 1, 0, D3DFMT_A1R5G5B5, D3DPOOL_MANAGED, &tex, NULL);\n\t\t\t\t\tbreak;\n\t\t\t\tcase PTI_A2BGR10:\n\t\t\t\t\tIDirect3DDevice9_CreateTexture(pD3DDev9, lm->width, lm->height, 1, 0, D3DFMT_A2B10G10R10, D3DPOOL_MANAGED, &tex, NULL);\n\t\t\t\t\tbreak;\n\t\t\t\tcase PTI_L8:\n\t\t\t\t\tIDirect3DDevice9_CreateTexture(pD3DDev9, lm->width, lm->height, 1, 0, D3DFMT_L8, D3DPOOL_MANAGED, &tex, NULL);\n\t\t\t\t\tbreak;\n\t\t\t\tcase PTI_RGBA16F:\n\t\t\t\t\tIDirect3DDevice9_CreateTexture(pD3DDev9, lm->width, lm->height, 1, 0, D3DFMT_A16B16G16R16F, D3DPOOL_MANAGED, &tex, NULL);\n\t\t\t\t\tbreak;\n\t\t\t\tcase PTI_RGBA32F:\n\t\t\t\t\tIDirect3DDevice9_CreateTexture(pD3DDev9, lm->width, lm->height, 1, 0, D3DFMT_A32B32G32R32F, D3DPOOL_MANAGED, &tex, NULL);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tif (!tex)\n\t\t\t\t\tcontinue;\n\t\t\t\tlm->lightmap_texture->ptr = tex;\n\t\t\t\tlm->lightmap_texture->status = TEX_LOADED;\n\t\t\t}\n\t\t\t\n\t\t\tlm->modified = 0;\n\t\t\trect.left = theRect->l;\n\t\t\trect.right = theRect->r;\n\t\t\trect.top = theRect->t;\n\t\t\trect.bottom = theRect->b;\n\n\t\t\tpixbytes = lightmap[i]->pixbytes;\n\t\t\tIDirect3DTexture9_LockRect(tex, 0, &lock, &rect, 0);\n\t\t\tfor (r = 0, w = theRect->r-theRect->l; r < lightmap[i]->rectchange.b-lightmap[i]->rectchange.t; r++)\n\t\t\t{\n\t\t\t\tmemcpy((char*)lock.pBits + r*lock.Pitch, lightmap[i]->lightmaps+(theRect->l+((r+theRect->t)*lm->width))*pixbytes, w*pixbytes);\n\t\t\t}\n\t\t\tIDirect3DTexture9_UnlockRect(tex, 0);\n\t\t\ttheRect->l = lm->width;\n\t\t\ttheRect->t = lm->height;\n\t\t\ttheRect->r = 0;\n\t\t\ttheRect->b = 0;\n\t\t}\n\t}\n}\n\nvoid D3D9BE_UploadAllLightmaps(void)\n{\n\tBE_UploadLightmaps(true);\n}\n\nqboolean D3D9BE_LightCullModel(vec3_t org, model_t *model)\n{\n#ifdef RTLIGHTS\n\tif ((shaderstate.mode == BEM_LIGHT || shaderstate.mode == BEM_STENCIL))\n\t{\n\t\t/*true if hidden from current light*/\n\t\t/*we have no rtlight support, so mneh*/\n\t}\n#endif\n\treturn false;\n}\n\nbatch_t *D3D9BE_GetTempBatch(void)\n{\n\tif (shaderstate.wbatch >= shaderstate.maxwbatches)\n\t{\n\t\tshaderstate.wbatch++;\n\t\treturn NULL;\n\t}\n\treturn &shaderstate.wbatches[shaderstate.wbatch++];\n}\n\nstatic void D3D9BE_RotateForEntity (const entity_t *e, const model_t *mod)\n{\n//\tfloat mv[16];\n\tfloat *m = shaderstate.m_model;\n\tfloat maxz;\n\n\tshaderstate.curentity = e;\n\n\tif ((e->flags & RF_WEAPONMODEL) && r_refdef.playerview->viewentity > 0)\n\t{\n\t\tfloat em[16];\n\t\tfloat vm[16];\n\n\t\tif (e->flags & RF_WEAPONMODELNOBOB)\n\t\t{\n\t\t\tvm[0] = r_refdef.weaponmatrix[0][0];\n\t\t\tvm[1] = r_refdef.weaponmatrix[0][1];\n\t\t\tvm[2] = r_refdef.weaponmatrix[0][2];\n\t\t\tvm[3] = 0;\n\n\t\t\tvm[4] = r_refdef.weaponmatrix[1][0];\n\t\t\tvm[5] = r_refdef.weaponmatrix[1][1];\n\t\t\tvm[6] = r_refdef.weaponmatrix[1][2];\n\t\t\tvm[7] = 0;\n\n\t\t\tvm[8] = r_refdef.weaponmatrix[2][0];\n\t\t\tvm[9] = r_refdef.weaponmatrix[2][1];\n\t\t\tvm[10] = r_refdef.weaponmatrix[2][2];\n\t\t\tvm[11] = 0;\n\n\t\t\tvm[12] = r_refdef.weaponmatrix[3][0];\n\t\t\tvm[13] = r_refdef.weaponmatrix[3][1];\n\t\t\tvm[14] = r_refdef.weaponmatrix[3][2];\n\t\t\tvm[15] = 1;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tvm[0] = r_refdef.weaponmatrix_bob[0][0];\n\t\t\tvm[1] = r_refdef.weaponmatrix_bob[0][1];\n\t\t\tvm[2] = r_refdef.weaponmatrix_bob[0][2];\n\t\t\tvm[3] = 0;\n\n\t\t\tvm[4] = r_refdef.weaponmatrix_bob[1][0];\n\t\t\tvm[5] = r_refdef.weaponmatrix_bob[1][1];\n\t\t\tvm[6] = r_refdef.weaponmatrix_bob[1][2];\n\t\t\tvm[7] = 0;\n\n\t\t\tvm[8] = r_refdef.weaponmatrix_bob[2][0];\n\t\t\tvm[9] = r_refdef.weaponmatrix_bob[2][1];\n\t\t\tvm[10] = r_refdef.weaponmatrix_bob[2][2];\n\t\t\tvm[11] = 0;\n\n\t\t\tvm[12] = r_refdef.weaponmatrix_bob[3][0];\n\t\t\tvm[13] = r_refdef.weaponmatrix_bob[3][1];\n\t\t\tvm[14] = r_refdef.weaponmatrix_bob[3][2];\n\t\t\tvm[15] = 1;\n\t\t}\n\n\t\tem[0] = e->axis[0][0];\n\t\tem[1] = e->axis[0][1];\n\t\tem[2] = e->axis[0][2];\n\t\tem[3] = 0;\n\n\t\tem[4] = e->axis[1][0];\n\t\tem[5] = e->axis[1][1];\n\t\tem[6] = e->axis[1][2];\n\t\tem[7] = 0;\n\n\t\tem[8] = e->axis[2][0];\n\t\tem[9] = e->axis[2][1];\n\t\tem[10] = e->axis[2][2];\n\t\tem[11] = 0;\n\n\t\tem[12] = e->origin[0];\n\t\tem[13] = e->origin[1];\n\t\tem[14] = e->origin[2];\n\t\tem[15] = 1;\n\n\t\tMatrix4_Multiply(vm, em, m);\n\t}\n\telse\n\t{\n\t\tm[0] = e->axis[0][0];\n\t\tm[1] = e->axis[0][1];\n\t\tm[2] = e->axis[0][2];\n\t\tm[3] = 0;\n\n\t\tm[4] = e->axis[1][0];\n\t\tm[5] = e->axis[1][1];\n\t\tm[6] = e->axis[1][2];\n\t\tm[7] = 0;\n\n\t\tm[8] = e->axis[2][0];\n\t\tm[9] = e->axis[2][1];\n\t\tm[10] = e->axis[2][2];\n\t\tm[11] = 0;\n\n\t\tm[12] = e->origin[0];\n\t\tm[13] = e->origin[1];\n\t\tm[14] = e->origin[2];\n\t\tm[15] = 1;\n\t}\n\n\tif (e->scale != 1 && e->scale != 0)\t//hexen 2 stuff\n\t{\n#ifdef HEXEN2\n\t\tfloat z;\n\t\tfloat escale;\n\t\tescale = e->scale;\n\t\tswitch(e->drawflags&SCALE_TYPE_MASK)\n\t\t{\n\t\tdefault:\n\t\tcase SCALE_TYPE_UNIFORM:\n\t\t\tVectorScale((m+0), escale, (m+0));\n\t\t\tVectorScale((m+4), escale, (m+4));\n\t\t\tVectorScale((m+8), escale, (m+8));\n\t\t\tbreak;\n\t\tcase SCALE_TYPE_XYONLY:\n\t\t\tVectorScale((m+0), escale, (m+0));\n\t\t\tVectorScale((m+4), escale, (m+4));\n\t\t\tbreak;\n\t\tcase SCALE_TYPE_ZONLY:\n\t\t\tVectorScale((m+8), escale, (m+8));\n\t\t\tbreak;\n\t\t}\n\t\tif (mod && (e->drawflags&SCALE_TYPE_MASK) != SCALE_TYPE_XYONLY)\n\t\t{\n\t\t\tswitch(e->drawflags&SCALE_ORIGIN_MASK)\n\t\t\t{\n\t\t\tcase SCALE_ORIGIN_CENTER:\n\t\t\t\tz = ((mod->maxs[2] + mod->mins[2]) * (1-escale))/2;\n\t\t\t\tVectorMA((m+12), z, e->axis[2], (m+12));\n\t\t\t\tbreak;\n\t\t\tcase SCALE_ORIGIN_BOTTOM:\n\t\t\t\tVectorMA((m+12), mod->mins[2]*(1-escale), e->axis[2], (m+12));\n\t\t\t\tbreak;\n\t\t\tcase SCALE_ORIGIN_TOP:\n\t\t\t\tVectorMA((m+12), -mod->maxs[2], e->axis[2], (m+12));\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n#else\n\t\tVectorScale((m+0), e->scale, (m+0));\n\t\tVectorScale((m+4), e->scale, (m+4));\n\t\tVectorScale((m+8), e->scale, (m+8));\n#endif\n\t}\n\telse if (mod && !strcmp(mod->name, \"progs/eyes.mdl\"))\n\t{\n\t\t/*resize eyes, to make them easier to see*/\n\t\tm[14] -= (22 + 8);\n\t\tVectorScale((m+0), 2, (m+0));\n\t\tVectorScale((m+4), 2, (m+4));\n\t\tVectorScale((m+8), 2, (m+8));\n\t}\n\tif (mod && !ruleset_allow_larger_models.ival && mod->clampscale != 1)\n\t{\t//possibly this should be on a per-frame basis, but that's a real pain to do\n\t\tCon_DPrintf(\"Rescaling %s by %f\\n\", mod->name, mod->clampscale);\n\t\tVectorScale((m+0), mod->clampscale, (m+0));\n\t\tVectorScale((m+4), mod->clampscale, (m+4));\n\t\tVectorScale((m+8), mod->clampscale, (m+8));\n\t}\n\n\tIDirect3DDevice9_SetTransform(pD3DDev9, D3DTS_WORLD, (D3DMATRIX*)m);\n\n\tif (e->flags & RF_DEPTHHACK)\n\t\tmaxz = 0.333;\n\telse\n\t\tmaxz = 1;\n\n\tif (shaderstate.vport.MaxZ != maxz)\n\t{\n\t\tshaderstate.vport.MaxZ = maxz;\n\n\t\tif (maxz < 1)\n\t\t\tshaderstate.curprojection = d3d_trueprojection_view;\n\t\telse\n\t\t\tshaderstate.curprojection = d3d_trueprojection_std;\n\n\t\tIDirect3DDevice9_SetViewport(pD3DDev9, &shaderstate.vport);\n\t\tIDirect3DDevice9_SetTransform(pD3DDev9, D3DTS_PROJECTION, (D3DMATRIX*)shaderstate.curprojection);\n\t}\n}\n\nvoid D3D9BE_SubmitBatch(batch_t *batch)\n{\n\tshader_t *shader = batch->shader;\n\tshaderstate.nummeshes = batch->meshes - batch->firstmesh;\n\tif (!shaderstate.nummeshes)\n\t\treturn;\n\tif (shaderstate.curentity != batch->ent)\n\t{\n\t\tD3D9BE_RotateForEntity(batch->ent, batch->ent->model);\n\t\tshaderstate.curtime = r_refdef.time - shaderstate.curentity->shaderTime;\n\t}\n\tshaderstate.batchvbo = batch->vbo;\n\tshaderstate.meshlist = batch->mesh + batch->firstmesh;\n\tshaderstate.curshader = shader;\n\tif (batch->skin)\n\t\tshaderstate.curtexnums = batch->skin;\n\telse if (shader->numdefaulttextures)\n\t\tshaderstate.curtexnums = shader->defaulttextures + ((int)(shader->defaulttextures_fps * shaderstate.curtime) % shader->numdefaulttextures);\n\telse\n\t\tshaderstate.curtexnums = shader->defaulttextures;\n\tshaderstate.curbatch = batch;\n\tshaderstate.flags = batch->flags;\n\tif ((unsigned)batch->lightmap[0] < (unsigned)numlightmaps)\n\t\tshaderstate.curlightmap = lightmap[batch->lightmap[0]]->lightmap_texture;\n\telse\n\t\tshaderstate.curlightmap = r_nulltex;\n\n\tBE_DrawMeshChain_Internal();\n}\n\nvoid D3D9BE_DrawMesh_List(shader_t *shader, int nummeshes, mesh_t **meshlist, vbo_t *vbo, texnums_t *texnums, unsigned int beflags)\n{\n\tshaderstate.batchvbo = vbo;\n\tshaderstate.curshader = shader;\n\tif (texnums)\n\t\tshaderstate.curtexnums = texnums;\n\telse if (shader->numdefaulttextures)\n\t\tshaderstate.curtexnums = shader->defaulttextures + ((int)(shader->defaulttextures_fps * shaderstate.curtime) % shader->numdefaulttextures);\n\telse\n\t\tshaderstate.curtexnums = shader->defaulttextures;\n\tshaderstate.curlightmap = r_nulltex;\n\tshaderstate.curbatch = &shaderstate.dummybatch;\n\tshaderstate.meshlist = meshlist;\n\tshaderstate.nummeshes = nummeshes;\n\tshaderstate.flags = beflags;\n\n\tBE_DrawMeshChain_Internal();\n}\n\nvoid D3D9BE_DrawMesh_Single(shader_t *shader, mesh_t *meshchain, vbo_t *vbo, unsigned int beflags)\n{\n\tshaderstate.batchvbo = vbo;\n\tshaderstate.curtime = realtime;\n\tshaderstate.curshader = shader;\n\tshaderstate.curbatch = NULL;\n\tif (shader->numdefaulttextures)\n\t\tshaderstate.curtexnums = shader->defaulttextures + ((int)(shader->defaulttextures_fps * shaderstate.curtime) % shader->numdefaulttextures);\n\telse\n\t\tshaderstate.curtexnums = shader->defaulttextures;\n\tshaderstate.curlightmap = r_nulltex;\n\tshaderstate.meshlist = &meshchain;\n\tshaderstate.nummeshes = 1;\n\tshaderstate.flags = beflags;\n\n\tBE_DrawMeshChain_Internal();\n}\n\nstatic void BE_SubmitMeshesSortList(batch_t *sortlist)\n{\n\tbatch_t *batch;\n\tfor (batch = sortlist; batch; batch = batch->next)\n\t{\n\t\tif (batch->meshes == batch->firstmesh)\n\t\t\tcontinue;\n\n\t\tif (batch->buildmeshes)\n\t\t\tbatch->buildmeshes(batch);\n\n\t\tif (batch->shader->flags & SHADER_NODLIGHT)\n\t\t\tif (shaderstate.mode == BEM_LIGHT)\n\t\t\t\tcontinue;\n\n\t\tif (batch->shader->flags & SHADER_SKY)\n\t\t{\n\t\t\tif (shaderstate.mode == BEM_STANDARD || shaderstate.mode == BEM_DEPTHDARK)\n\t\t\t{\n\t\t\t\tif (R_DrawSkyChain (batch))\n\t\t\t\t\tcontinue;\n\t\t\t}\n\t\t\telse if (/*shaderstate.mode != BEM_FOG &&*/ shaderstate.mode != BEM_CREPUSCULAR && shaderstate.mode != BEM_WIREFRAME)\n\t\t\t\tcontinue;\n\t\t}\n\n\t\tBE_SubmitBatch(batch);\n\t}\n}\n\n\n/*generates a new modelview matrix, as well as vpn vectors*/\nstatic void R_MirrorMatrix(plane_t *plane)\n{\n\tfloat mirror[16];\n\tfloat view[16];\n\tfloat result[16];\n\n\tvec3_t pnorm;\n\tVectorNegate(plane->normal, pnorm);\n\n\tmirror[0] = 1-2*pnorm[0]*pnorm[0];\n\tmirror[1] = -2*pnorm[0]*pnorm[1];\n\tmirror[2] = -2*pnorm[0]*pnorm[2];\n\tmirror[3] = 0;\n\n\tmirror[4] = -2*pnorm[1]*pnorm[0];\n\tmirror[5] = 1-2*pnorm[1]*pnorm[1];\n\tmirror[6] = -2*pnorm[1]*pnorm[2] ;\n\tmirror[7] = 0;\n\n\tmirror[8]  = -2*pnorm[2]*pnorm[0];\n\tmirror[9]  = -2*pnorm[2]*pnorm[1];\n\tmirror[10] = 1-2*pnorm[2]*pnorm[2];\n\tmirror[11] = 0;\n\n\tmirror[12] = -2*pnorm[0]*plane->dist;\n\tmirror[13] = -2*pnorm[1]*plane->dist;\n\tmirror[14] = -2*pnorm[2]*plane->dist;\n\tmirror[15] = 1;\n\n\tview[0] = vpn[0];\n\tview[1] = vpn[1];\n\tview[2] = vpn[2];\n\tview[3] = 0;\n\n\tview[4] = -vright[0];\n\tview[5] = -vright[1];\n\tview[6] = -vright[2];\n\tview[7] = 0;\n\n\tview[8]  = vup[0];\n\tview[9]  = vup[1];\n\tview[10] = vup[2];\n\tview[11] = 0;\n\n\tview[12] = r_refdef.vieworg[0];\n\tview[13] = r_refdef.vieworg[1];\n\tview[14] = r_refdef.vieworg[2];\n\tview[15] = 1;\n\n\tVectorMA(r_refdef.vieworg, 0.25, plane->normal, r_refdef.pvsorigin);\n\n\tMatrix4_Multiply(mirror, view, result);\n\n\tvpn[0] = result[0];\n\tvpn[1] = result[1];\n\tvpn[2] = result[2];\n\n\tvright[0] = -result[4];\n\tvright[1] = -result[5];\n\tvright[2] = -result[6];\n\n\tvup[0] = result[8];\n\tvup[1] = result[9];\n\tvup[2] = result[10];\n\n\tr_refdef.vieworg[0] = result[12];\n\tr_refdef.vieworg[1] = result[13];\n\tr_refdef.vieworg[2] = result[14];\n}\nstatic entity_t *R_NearestPortal(plane_t *plane)\n{\n\tint i;\n\tentity_t *best = NULL;\n\tfloat dist, bestd = 0;\n\tfor (i = 0; i < cl_numvisedicts; i++)\n\t{\n\t\tif (cl_visedicts[i].rtype == RT_PORTALSURFACE)\n\t\t{\n\t\t\tdist = DotProduct(cl_visedicts[i].origin, plane->normal)-plane->dist;\n\t\t\tdist = fabs(dist);\n\t\t\tif (dist < 64 && (!best || dist < bestd))\n\t\t\t\tbest = &cl_visedicts[i];\n\t\t}\n\t}\n\treturn best;\n}\n\nstatic void TransformCoord(vec3_t in, vec3_t planea[3], vec3_t planeo, vec3_t viewa[3], vec3_t viewo, vec3_t result)\n{\n\tint\t\ti;\n\tvec3_t\tlocal;\n\tvec3_t\ttransformed;\n\tfloat\td;\n\n\tlocal[0] = in[0] - planeo[0];\n\tlocal[1] = in[1] - planeo[1];\n\tlocal[2] = in[2] - planeo[2];\n\n\tVectorClear(transformed);\n\tfor ( i = 0 ; i < 3 ; i++ )\n\t{\n\t\td = DotProduct(local, planea[i]);\n\t\tVectorMA(transformed, d, viewa[i], transformed);\n\t}\n\n\tresult[0] = transformed[0] + viewo[0];\n\tresult[1] = transformed[1] + viewo[1];\n\tresult[2] = transformed[2] + viewo[2];\n}\nstatic void TransformDir(vec3_t in, vec3_t planea[3], vec3_t viewa[3], vec3_t result)\n{\n\tint\t\ti;\n\tfloat\td;\n\tvec3_t tmp;\n\n\tVectorCopy(in, tmp);\n\n\tVectorClear(result);\n\tfor ( i = 0 ; i < 3 ; i++ )\n\t{\n\t\td = DotProduct(tmp, planea[i]);\n\t\tVectorMA(result, d, viewa[i], result);\n\t}\n}\nstatic void R_RenderScene(void)\n{\n//\tIDirect3DDevice9_SetTransform(pD3DDev9, D3DTS_PROJECTION, (D3DMATRIX*)d3d_trueprojection);\n\tIDirect3DDevice9_SetTransform(pD3DDev9, D3DTS_VIEW, (D3DMATRIX*)r_refdef.m_view);\n\tR_SetFrustum (r_refdef.m_projection_std, r_refdef.m_view);\n\tSurf_DrawWorld();\n}\n\nstatic void R_DrawPortal(batch_t *batch, batch_t **blist)\n{\n\tentity_t *view;\n\tfloat glplane[4];\n\tplane_t plane;\n\trefdef_t oldrefdef;\n\tmesh_t *mesh = batch->mesh[batch->firstmesh];\n\tint sort;\n\n\tif (r_refdef.recurse)\n\t\treturn;\n\n\tVectorCopy(mesh->normals_array[0], plane.normal);\n\tplane.dist = DotProduct(mesh->xyz_array[0], plane.normal);\n\n\t//if we're too far away from the surface, don't draw anything\n\tif (batch->shader->flags & SHADER_AGEN_PORTAL)\n\t{\n\t\t/*there's a portal alpha blend on that surface, that fades out after this distance*/\n\t\tif (DotProduct(r_refdef.vieworg, plane.normal)-plane.dist > batch->shader->portaldist)\n\t\t\treturn;\n\t}\n\t//if we're behind it, then also don't draw anything.\n\tif (DotProduct(r_refdef.vieworg, plane.normal)-plane.dist < 0)\n\t\treturn;\n\n\tview = R_NearestPortal(&plane);\n\t//if (!view)\n\t//\treturn;\n\n\toldrefdef = r_refdef;\n\tr_refdef.recurse = true;\n\n\tr_refdef.externalview = true;\n\n\tif (!view || VectorCompare(view->origin, view->oldorigin))\n\t{\n\t\tr_refdef.flipcull ^= SHADER_CULL_FLIP;\n\t\tR_MirrorMatrix(&plane);\n\t}\n\telse\n\t{\n\t\tfloat d;\n\t\tvec3_t paxis[3], porigin, vaxis[3], vorg;\n\t\tvoid PerpendicularVector( vec3_t dst, const vec3_t src );\n\n\t\t/*calculate where the surface is meant to be*/\n\t\tVectorCopy(mesh->normals_array[0], paxis[0]);\n\t\tPerpendicularVector(paxis[1], paxis[0]);\n\t\tCrossProduct(paxis[0], paxis[1], paxis[2]);\n\t\td = DotProduct(view->origin, plane.normal) - plane.dist;\n\t\tVectorMA(view->origin, -d, paxis[0], porigin);\n\n\t\t/*grab the camera origin*/\n\t\tVectorNegate(view->axis[0], vaxis[0]);\n\t\tVectorNegate(view->axis[1], vaxis[1]);\n\t\tVectorCopy(view->axis[2], vaxis[2]);\n\t\tVectorCopy(view->oldorigin, vorg);\n\n\t\tVectorCopy(vorg, r_refdef.pvsorigin);\n\n\t\t/*rotate it a bit*/\n\t\tRotatePointAroundVector(vaxis[1], vaxis[0], view->axis[1], sin(realtime)*4);\n\t\tCrossProduct(vaxis[0], vaxis[1], vaxis[2]);\n\n\t\tTransformCoord(oldrefdef.vieworg, paxis, porigin, vaxis, vorg, r_refdef.vieworg);\n\t\tTransformDir(vpn, paxis, vaxis, vpn);\n\t\tTransformDir(vright, paxis, vaxis, vright);\n\t\tTransformDir(vup, paxis, vaxis, vup);\n\t}\n\tMatrix4x4_CM_ModelViewMatrixFromAxis(r_refdef.m_view, vpn, vright, vup, r_refdef.vieworg);\n\tVectorAngles(vpn, vup, r_refdef.viewangles, true);\n\tVectorCopy(r_refdef.vieworg, r_origin);\n\n/*FIXME: the batch stuff should be done in renderscene*/\n\n\t/*fixup the first mesh index*/\n\tfor (sort = 0; sort < SHADER_SORT_COUNT; sort++)\n\tfor (batch = blist[sort]; batch; batch = batch->next)\n\t{\n\t\tbatch->firstmesh = batch->meshes;\n\t}\n\n\t/*FIXME: can we get away with stenciling the screen?*/\n\t/*Add to frustum culling instead of clip planes?*/\n\tglplane[0] = plane.normal[0];\n\tglplane[1] = plane.normal[1];\n\tglplane[2] = plane.normal[2];\n\tglplane[3] = -plane.dist;\n\tIDirect3DDevice9_SetClipPlane(pD3DDev9, 0, glplane);\n\tIDirect3DDevice9_SetRenderState(pD3DDev9, D3DRS_CLIPPLANEENABLE, D3DCLIPPLANE0);\n\tSurf_SetupFrame();\n\tR_RenderScene();\n\tIDirect3DDevice9_SetRenderState(pD3DDev9, D3DRS_CLIPPLANEENABLE, 0);\n\n\tfor (sort = 0; sort < SHADER_SORT_COUNT; sort++)\n\tfor (batch = blist[sort]; batch; batch = batch->next)\n\t{\n\t\tbatch->firstmesh = 0;\n\t}\n\tr_refdef = oldrefdef;\n\n\t/*broken stuff*/\n\tAngleVectors (r_refdef.viewangles, vpn, vright, vup);\n\tVectorCopy (r_refdef.vieworg, r_origin);\n\n//\tIDirect3DDevice9_SetTransform(pD3DDev9, D3DTS_PROJECTION, (D3DMATRIX*)d3d_trueprojection);\n\tIDirect3DDevice9_SetTransform(pD3DDev9, D3DTS_VIEW, (D3DMATRIX*)r_refdef.m_view);\n\tR_SetFrustum (r_refdef.m_projection_std, r_refdef.m_view);\n}\n\nstatic void BE_SubmitMeshesPortals(batch_t **worldlist, batch_t *dynamiclist)\n{\n\tbatch_t *batch, *old;\n\tint i;\n\t/*attempt to draw portal shaders*/\n\tif (shaderstate.mode == BEM_STANDARD)\n\t{\n\t\tfor (i = 0; i < 2; i++)\n\t\t{\n\t\t\tfor (batch = i?dynamiclist:worldlist[SHADER_SORT_PORTAL]; batch; batch = batch->next)\n\t\t\t{\n\t\t\t\tif (batch->meshes == batch->firstmesh)\n\t\t\t\t\tcontinue;\n\n\t\t\t\tif (batch->buildmeshes)\n\t\t\t\t\tbatch->buildmeshes(batch);\n\n\t\t\t\t/*draw already-drawn portals as depth-only, to ensure that their contents are not harmed*/\n\t\t\t\tBE_SelectMode(BEM_DEPTHONLY);\n\t\t\t\tfor (old = worldlist[SHADER_SORT_PORTAL]; old && old != batch; old = old->next)\n\t\t\t\t{\n\t\t\t\t\tif (old->meshes == old->firstmesh)\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\tBE_SubmitBatch(old);\n\t\t\t\t}\n\t\t\t\tif (!old)\n\t\t\t\t{\n\t\t\t\t\tfor (old = dynamiclist; old != batch; old = old->next)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (old->meshes == old->firstmesh)\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\tBE_SubmitBatch(old);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tBE_SelectMode(BEM_STANDARD);\n\n\t\t\t\tR_DrawPortal(batch, worldlist);\n\n\t\t\t\t/*clear depth again*/\n\t\t\t\tIDirect3DDevice9_Clear(pD3DDev9, 0, NULL, D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0,0,0), 1, 0);\n\t\t\t}\n\t\t}\n\t}\n}\n\nvoid D3D9BE_SubmitMeshes (batch_t **worldbatches, batch_t **blist, int first, int stop)\n{\n\tint i;\n\n\tfor (i = first; i < stop; i++)\n\t{\n\t\tif (worldbatches)\n\t\t{\n\t\t\tif (i == SHADER_SORT_PORTAL /*&& !r_noportals.ival*/ && !r_refdef.recurse)\n\t\t\t\tBE_SubmitMeshesPortals(worldbatches, blist[i]);\n\n\t\t\tBE_SubmitMeshesSortList(worldbatches[i]);\n\t\t}\n\t\tBE_SubmitMeshesSortList(blist[i]);\n\t}\n}\n\n#ifdef RTLIGHTS\nvoid D3D9BE_BaseEntTextures(const qbyte *worldpvs, const int *worldareas)\n{\n\tbatch_t *batches[SHADER_SORT_COUNT];\n\tBE_GenModelBatches(batches, shaderstate.curdlight, shaderstate.mode, worldpvs, worldareas);\n\tD3D9BE_SubmitMeshes(NULL, batches, SHADER_SORT_PORTAL, SHADER_SORT_SEETHROUGH+1);\n\tBE_SelectEntity(&r_worldentity);\n}\n\nvoid D3D9BE_RenderShadowBuffer(unsigned int numverts, IDirect3DVertexBuffer9 *vbuf, unsigned int numindicies, IDirect3DIndexBuffer9 *ibuf)\n{\n\tpolyoffset_t po = shaderstate.curshader->polyoffset;\n#ifdef BEF_PUSHDEPTH\n\tif (shaderstate.flags & BEF_PUSHDEPTH)\n\t{\n\t\textern cvar_t r_polygonoffset_submodel_factor, r_polygonoffset_submodel_offset;\n\t\tpo.factor += r_polygonoffset_submodel_factor.value;\n\t\tpo.unit += r_polygonoffset_submodel_offset.value;\n\t}\n#endif\n\tif (po.factor != shaderstate.curpolyoffset.factor)\n\t{\n\t\tshaderstate.curpolyoffset.factor = po.factor;\n\t\tIDirect3DDevice9_SetRenderState(pD3DDev9, D3DRS_SLOPESCALEDEPTHBIAS, *(DWORD*)&po.factor);\n\t}\n\tif (po.unit != shaderstate.curpolyoffset.unit)\n\t{\n\t\tshaderstate.curpolyoffset.unit = po.unit;\n\t\tpo.unit *= shaderstate.gltod3d_depthunit;\n\t\tIDirect3DDevice9_SetRenderState(pD3DDev9, D3DRS_DEPTHBIAS, *(DWORD*)&po.unit);\n\t}\n\n//\tD3D9BE_Cull(0);\n\n\n\tIDirect3DDevice9_SetStreamSource(pD3DDev9, STRM_VERT, vbuf, 0, sizeof(vecV_t));\n\tIDirect3DDevice9_SetIndices(pD3DDev9, ibuf);\n\n\tif (0 != shaderstate.curvertdecl)\n\t{\n\t\tshaderstate.curvertdecl = 0;\n\t\td3dcheck(IDirect3DDevice9_SetVertexDeclaration(pD3DDev9, vertexdecls[shaderstate.curvertdecl]));\n\t}\n\n\tIDirect3DDevice9_DrawIndexedPrimitive(pD3DDev9, D3DPT_TRIANGLELIST, 0, 0, numverts, 0, numindicies/3);\n}\n#endif\n\nvoid D3D9BE_DrawWorld (batch_t **worldbatches)\n{\n\tbatch_t *batches[SHADER_SORT_COUNT];\n\tRSpeedLocals();\n\n\tshaderstate.curentity = &r_worldentity;\n\n\tif (!r_refdef.recurse)\n\t{\n\t\tif (shaderstate.wbatch > shaderstate.maxwbatches)\n\t\t{\n\t\t\tint newm = shaderstate.wbatch;\n\t\t\tshaderstate.wbatches = BZ_Realloc(shaderstate.wbatches, newm * sizeof(*shaderstate.wbatches));\n\t\t\tmemset(shaderstate.wbatches + shaderstate.maxwbatches, 0, (newm - shaderstate.maxwbatches) * sizeof(*shaderstate.wbatches));\n\t\t\tshaderstate.maxwbatches = newm;\n\t\t}\n\t\tshaderstate.wbatch = 0;\n\t}\n\n\tshaderstate.curdlight = NULL;\n\tBE_GenModelBatches(batches, shaderstate.curdlight, BEM_STANDARD, r_refdef.scenevis, r_refdef.sceneareas);\n\n\tif (worldbatches)\n\t{\n\t\tfloat shaderstate_identitylighting;\n\t\tBE_UploadLightmaps(false);\n\n\t\t//make sure the world draws correctly\n\t\tr_worldentity.shaderRGBAf[0] = 1;\n\t\tr_worldentity.shaderRGBAf[1] = 1;\n\t\tr_worldentity.shaderRGBAf[2] = 1;\n\t\tr_worldentity.shaderRGBAf[3] = 1;\n\t\tr_worldentity.axis[0][0] = 1;\n\t\tr_worldentity.axis[1][1] = 1;\n\t\tr_worldentity.axis[2][2] = 1;\n\n#ifdef RTLIGHTS\n\t\tif (worldbatches && r_shadow_realtime_world.ival)\n\t\t\tshaderstate_identitylighting = r_shadow_realtime_world_lightmaps.value;\n\t\telse\n#endif\n\t\t\tshaderstate_identitylighting = r_lightmap_scale.value;\n\t\tshaderstate_identitylighting *= r_refdef.hdr_value;\n//\t\tshaderstate_identitylightmap = shaderstate.identitylighting / (1<<gl_overbright.ival);\n\n\t\tif (shaderstate_identitylighting == 0)\n\t\t\tBE_SelectMode(BEM_DEPTHDARK);\n\t\telse\n\t\t\tBE_SelectMode(BEM_STANDARD);\n\n\t\tRSpeedRemark();\n\t\tD3D9BE_SubmitMeshes(worldbatches, batches, SHADER_SORT_PORTAL, SHADER_SORT_SEETHROUGH+1);\n\t\tRSpeedEnd(RSPEED_OPAQUE);\n\n#ifdef RTLIGHTS\n\t\tif (r_refdef.scenevis)\n\t\t{\n\t\t\tRSpeedRemark();\n\t\t\tD3D9BE_SelectEntity(&r_worldentity);\n\t\t\tSh_DrawLights(r_refdef.scenevis);\n\t\t\tRSpeedEnd(RSPEED_RTLIGHTS);\n\t\t}\n#endif\n\n\t\tif (r_refdef.globalfog.density && (!sh_config.progs_supported || !r_fog_permutation.ival))\n\t\t{\t//fixed function-only. with global fog. that means we need to hack something in.\n\t\t\t//FIXME: should really be doing this on a per-shader basis, for custom shaders that don't use glsl\n\t\t\tBE_SelectMode(BEM_FOG);\n\n//\t\t\tBE_SelectFog(r_refdef.globalfog.colour, r_refdef.globalfog.alpha, r_refdef.globalfog.density);\n\t\t\tD3D9BE_SubmitMeshes(worldbatches, batches, SHADER_SORT_PORTAL, SHADER_SORT_NEAREST);\n\t\t}\n\n\t\tBE_SelectMode(BEM_STANDARD);\n\n\t\tRSpeedRemark();\n\t\tD3D9BE_SubmitMeshes(worldbatches, batches, SHADER_SORT_SEETHROUGH+1, SHADER_SORT_COUNT);\n\t\tRSpeedEnd(RSPEED_TRANSPARENTS);\n\t}\n\telse\n\t{\n\t\tRSpeedRemark();\n\t\tD3D9BE_SubmitMeshes(NULL, batches, SHADER_SORT_PORTAL, SHADER_SORT_COUNT);\n\t\tRSpeedEnd(RSPEED_OPAQUE);\n\t}\n\n\tR_RenderDlights ();\n\n\tD3D9BE_RotateForEntity(&r_worldentity, NULL);\n}\nvoid D3D9BE_VBO_Begin(vbobctx_t *ctx, size_t maxsize)\n{\n\tIDirect3DVertexBuffer9 *buf;\n\tIDirect3DDevice9_CreateVertexBuffer(pD3DDev9, maxsize, D3DUSAGE_WRITEONLY, 0, D3DPOOL_MANAGED, &buf, NULL);\n\tctx->vboptr[0] = buf;\n\n\tIDirect3DVertexBuffer9_Lock(buf, 0, maxsize, &ctx->fallback, D3DLOCK_DISCARD);\n\n\tctx->pos = 0;\n}\nvoid D3D9BE_VBO_Data(vbobctx_t *ctx, void *data, size_t size, vboarray_t *varray)\n{\n\tIDirect3DVertexBuffer9 *buf = ctx->vboptr[0];\n\tmemcpy((char*)ctx->fallback + ctx->pos, data, size);\n\tvarray->d3d.buff = buf;\n\tvarray->d3d.offs = ctx->pos;\n\tctx->pos += size;\n}\nvoid D3D9BE_VBO_Finish(vbobctx_t *ctx, void *edata, size_t esize, vboarray_t *earray, void **vbomem, void **ebomem)\n{\n\tIDirect3DIndexBuffer9 *buf;\n\tIDirect3DVertexBuffer9 *vbuf = ctx->vboptr[0];\n\tIDirect3DVertexBuffer9_Unlock(vbuf);\n\tctx->fallback = NULL;\n\n\tIDirect3DDevice9_CreateIndexBuffer(pD3DDev9, esize, 0, D3DFMT_QINDEX, D3DPOOL_MANAGED, &buf, NULL);\n\tctx->vboptr[1] = buf;\n\tIDirect3DIndexBuffer9_Lock(buf, 0, esize, &ctx->fallback, D3DLOCK_DISCARD);\n\tmemcpy(ctx->fallback, edata, esize);\n\tIDirect3DIndexBuffer9_Unlock(buf);\n\tctx->fallback = NULL;\n\n\tearray->d3d.buff = buf;\n\tearray->d3d.offs = 0;\n}\nvoid D3D9BE_VBO_Destroy(vboarray_t *vearray, void *mem)\n{\n\tIUnknown *ebuf = vearray->d3d.buff;\n\tif (ebuf)\n\t\tebuf->lpVtbl->Release(ebuf);\n}\n\nvoid D3D9BE_Scissor(srect_t *srect)\n{\n\tRECT rect;\n\n\tif (srect)\n\t{\n\t\trect.left = (srect->x) * vid.fbpwidth;\n\t\trect.right = (srect->x + srect->width) * vid.fbpwidth;\n\t\trect.top = (1 - (srect->height + srect->y))*vid.fbpheight;  //our api was made for gl. :(\n\t\trect.bottom = (1 - (srect->y))*vid.fbpheight;  //our api was made for gl. :(\n\t}\n\telse\n\t{\n\t\trect.left = 0;\n\t\trect.right = vid.pixelwidth;\n\t\trect.top = 0;\n\t\trect.bottom = vid.pixelheight;\n\t}\n\tIDirect3DDevice9_SetScissorRect(pD3DDev9, &rect);\n\tIDirect3DDevice9_SetRenderState(pD3DDev9, D3DRS_SCISSORTESTENABLE, TRUE);\n}\n\n#endif\n"
  },
  {
    "path": "engine/d3d/d3d_image.c",
    "content": "#include \"quakedef.h\"\r\n#include \"winquake.h\"\r\n#ifdef D3D9QUAKE\r\n#if !defined(HMONITOR_DECLARED) && (WINVER < 0x0500)\r\n    #define HMONITOR_DECLARED\r\n    DECLARE_HANDLE(HMONITOR);\r\n#endif\r\n#include <d3d9.h>\r\nextern LPDIRECT3DDEVICE9 pD3DDev9;\r\n\r\nvoid D3D9_DestroyTexture (texid_t tex)\r\n{\r\n\tif (!tex)\r\n\t\treturn;\r\n\r\n\tif (tex->ptr)\r\n\t\tIDirect3DBaseTexture9_Release((IDirect3DBaseTexture9*)tex->ptr);\r\n\ttex->ptr = NULL;\r\n}\r\n\r\nqboolean D3D9_LoadTextureMips(image_t *tex, const struct pendingtextureinfo *mips)\r\n{\r\n\tqbyte *fte_restrict out, *fte_restrict in;\r\n\tint x, y, i;\r\n\tD3DLOCKED_RECT lock;\r\n\tD3DFORMAT fmt = D3DFMT_UNKNOWN;\r\n\tD3DSURFACE_DESC desc;\r\n\tIDirect3DBaseTexture9 *dbt;\r\n\tqboolean swap = false;\r\n\tunsigned int blockwidth, blockheight, blockdepth, blockbytes;\r\n\r\n\t//NOTE: d3d9 formats are written as little-endian packed formats, so RR GG BB AA -> 0xAABBGGRR\r\n\t//whereas fte formats vary depending on whether they're packed or byte-aligned.\r\n\tswitch(mips->encoding)\r\n\t{\r\n\tcase PTI_L8_SRGB:\r\n\tcase PTI_L8:\r\n\t\tfmt = D3DFMT_L8;\r\n\t\tbreak;\r\n\tcase PTI_L8A8_SRGB:\r\n\tcase PTI_L8A8:\r\n\t\tfmt = D3DFMT_A8L8;\r\n\t\tbreak;\r\n\tcase PTI_RGB565:\r\n\t\tfmt = D3DFMT_R5G6B5;\r\n\t\tbreak;\r\n\tcase PTI_RGBA4444://not supported on d3d9\r\n\t\tbreak;\r\n\tcase PTI_ARGB4444:\r\n\t\tfmt = D3DFMT_A4R4G4B4;\r\n\t\tbreak;\r\n\tcase PTI_RGBA5551://not supported on d3d9\r\n\t\tbreak;\r\n\tcase PTI_ARGB1555:\r\n\t\tfmt = D3DFMT_A1R5G5B5;\r\n\t\tbreak;\r\n\tcase PTI_RGBA8_SRGB:\r\n\tcase PTI_RGBA8:\r\n//\t\tfmt = D3DFMT_A8B8G8R8;\t//not supported by most drivers, for some reason. we need to emulate it with some swapping\r\n\t\tfmt = D3DFMT_A8R8G8B8;\r\n\t\tswap = true;\r\n\t\tbreak;\r\n\tcase PTI_RGBX8_SRGB:\r\n\tcase PTI_RGBX8:\r\n//\t\tfmt = D3DFMT_X8B8G8R8;\t//not supported by most drivers, for some reason. we need to emulate it with some swapping\r\n\t\tfmt = D3DFMT_X8R8G8B8;\r\n\t\tswap = true;\r\n\t\tbreak;\r\n\tcase PTI_BGRA8_SRGB:\r\n\tcase PTI_BGRA8:\r\n\t\tfmt = D3DFMT_A8R8G8B8;\r\n\t\tbreak;\r\n\tcase PTI_BGRX8_SRGB:\r\n\tcase PTI_BGRX8:\r\n\t\tfmt = D3DFMT_X8R8G8B8;\r\n\t\tbreak;\r\n\r\n\tcase PTI_A2BGR10:\r\n\t\tfmt = D3DFMT_A2B10G10R10;\r\n\t\tbreak;\r\n\r\n\t//too lazy to support these for now\r\n\tcase PTI_BC1_RGB_SRGB:\r\n\tcase PTI_BC1_RGBA_SRGB:\t//d3d doesn't distinguish between these\r\n\tcase PTI_BC1_RGB:\r\n\tcase PTI_BC1_RGBA:\t//d3d doesn't distinguish between these\r\n\t\tfmt = D3DFMT_DXT1;\r\n\t\tbreak;\r\n\tcase PTI_BC2_RGBA_SRGB:\r\n\tcase PTI_BC2_RGBA:\r\n\t\tfmt = D3DFMT_DXT3;\r\n\t\tbreak;\r\n\tcase PTI_BC3_RGBA_SRGB:\r\n\tcase PTI_BC3_RGBA:\r\n\t\tfmt = D3DFMT_DXT5;\r\n\t\tbreak;\r\n\r\n\t//bc4-7 not supported on d3d9.\r\n\t//etc2 have no chance.\r\n\t//astc? lul!\r\n\r\n\tcase PTI_EMULATED:\t//no idea\r\n\tdefault:\r\n\t\tbreak;\r\n\t}\r\n\tif (fmt == D3DFMT_UNKNOWN)\r\n\t\treturn false;\r\n\r\n\tif (mips->type != ((tex->flags&IF_TEXTYPEMASK)>>IF_TEXTYPESHIFT))\r\n\t\treturn false;\r\n\r\n\tImage_BlockSizeForEncoding(mips->encoding, &blockbytes, &blockwidth, &blockheight, &blockdepth);\r\n\r\n\tif (!pD3DDev9)\r\n\t\treturn false;\t//can happen on errors\r\n\tif (mips->type == PTI_CUBE)\r\n\t{\r\n\t\tunsigned int face;\r\n\t\tIDirect3DCubeTexture9 *dt;\r\n\t\tif (FAILED(IDirect3DDevice9_CreateCubeTexture(pD3DDev9, mips->mip[0].width, mips->mipcount/6, 0, fmt, D3DPOOL_MANAGED, &dt, NULL)))\r\n\t\t\treturn false;\r\n\t\tdbt = (IDirect3DBaseTexture9*)dt;\r\n\r\n\t\tfor (i = 0; i < mips->mipcount; i++)\r\n\t\t{\r\n\t\t\tIDirect3DCubeTexture9_GetLevelDesc(dt, i, &desc);\r\n\r\n\t\t\tif (mips->mip[i].height != desc.Height || mips->mip[i].width != desc.Width)\r\n\t\t\t{\r\n\t\t\t\tIDirect3DCubeTexture9_Release(dt);\r\n\t\t\t\treturn false;\r\n\t\t\t}\r\n\t\t\tif (!mips->mip[i].data)\r\n\t\t\t\tcontinue;\r\n\r\n\t\t\tfor (face = 0, in = mips->mip[i].data; face < mips->mip[i].depth; face++)\r\n\t\t\t{\r\n\t\t\t\tIDirect3DCubeTexture9_LockRect(dt, face, i, &lock, NULL, D3DLOCK_NOSYSLOCK|D3DLOCK_DISCARD);\r\n\t\t\t\t//can't do it in one go. pitch might contain padding or be upside down.\r\n\t\t\t\tif (swap)\r\n\t\t\t\t{\t//only works for blockbytes=4\r\n\t\t\t\t\tsize_t rowbytes = ((mips->mip[i].width+blockwidth-1)/blockwidth)*blockbytes;\r\n\t\t\t\t\tfor (y = 0, out = lock.pBits; y < mips->mip[i].height; y+=blockheight, out += lock.Pitch, in += rowbytes)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tfor (x = 0; x < rowbytes; x+=4)\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tout[x+0] = in[x+2];\r\n\t\t\t\t\t\t\tout[x+1] = in[x+1];\r\n\t\t\t\t\t\t\tout[x+2] = in[x+0];\r\n\t\t\t\t\t\t\tout[x+3] = in[x+3];\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\tsize_t rowbytes = ((mips->mip[i].width+blockwidth-1)/blockwidth)*blockbytes;\r\n\t\t\t\t\tfor (y = 0, out = lock.pBits, in = mips->mip[i].data; y < mips->mip[i].height; y+=blockheight, out += lock.Pitch, in += rowbytes)\r\n\t\t\t\t\t\tmemcpy(out, in, rowbytes);\r\n\t\t\t\t}\r\n\t\t\t\tIDirect3DCubeTexture9_UnlockRect(dt, i%6, i/6);\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\telse if (mips->type == PTI_2D)\r\n\t{\r\n\t\tIDirect3DTexture9 *dt;\r\n\t\tint mipcount = mips->mipcount;\r\n\t\tfor (i = 1; i < mipcount; i++)\r\n\t\t{\t//OpenGL and Direct3D have different interpretations of mipmap sizes.\r\n\t\t\t//OpenGL rounds up, direct3D rounds down. (d3d:3->1, gl:3->2)\r\n\t\t\t//so if the mips are incompatible, just drop the smaller ones.\r\n\t\t\tif (mips->mip[i].width != max(1,(mips->mip[i-1].width)>>1) ||\r\n\t\t\t\tmips->mip[i].height != max(1,(mips->mip[i-1].height)>>1))\r\n\t\t\t{\r\n\t\t\t\tmipcount = i;\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t//Microsoft's code will reject any dxt texture with a mip[0] width/height that is not a multiple of 4.\r\n\r\n\t\tif (FAILED(IDirect3DDevice9_CreateTexture(pD3DDev9, mips->mip[0].width, mips->mip[0].height, mipcount, 0, fmt, D3DPOOL_MANAGED, &dt, NULL)))\r\n\t\t\treturn false;\r\n\t\tdbt = (IDirect3DBaseTexture9*)dt;\r\n\t\r\n\t\tfor (i = 0; i < mipcount; i++)\r\n\t\t{\r\n\t\t\tIDirect3DTexture9_GetLevelDesc(dt, i, &desc);\r\n\r\n\t\t\tif (mips->mip[i].height != desc.Height || mips->mip[i].width != desc.Width)\r\n\t\t\t{\t//we got the above mipcount clamping wrong!\r\n\t\t\t\tIDirect3DTexture9_Release(dt);\r\n\t\t\t\treturn false;\r\n\t\t\t}\r\n\r\n\t\t\tIDirect3DTexture9_LockRect(dt, i, &lock, NULL, D3DLOCK_NOSYSLOCK|D3DLOCK_DISCARD);\r\n\t\t\t//can't do it in one go. pitch might contain padding or be upside down.\r\n\t\t\tif (!mips->mip[i].data)\r\n\t\t\t\t;\r\n\t\t\telse if (swap)\r\n\t\t\t{\r\n\t\t\t\tsize_t rowbytes = ((mips->mip[i].width+blockwidth-1)/blockwidth)*blockbytes;\r\n\t\t\t\tfor (y = 0, out = lock.pBits, in = mips->mip[i].data; y < mips->mip[i].height; y+=blockheight, out += lock.Pitch, in += rowbytes)\r\n\t\t\t\t{\r\n\t\t\t\t\tfor (x = 0; x < rowbytes; x+=4)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tout[x+0] = in[x+2];\r\n\t\t\t\t\t\tout[x+1] = in[x+1];\r\n\t\t\t\t\t\tout[x+2] = in[x+0];\r\n\t\t\t\t\t\tout[x+3] = in[x+3];\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tsize_t rowbytes = ((mips->mip[i].width+blockwidth-1)/blockwidth)*blockbytes;\r\n\t\t\t\tfor (y = 0, out = lock.pBits, in = mips->mip[i].data; y < mips->mip[i].height; y+=blockheight, out += lock.Pitch, in += rowbytes)\r\n\t\t\t\t\tmemcpy(out, in, rowbytes);\r\n\t\t\t}\r\n\t\t\tIDirect3DTexture9_UnlockRect(dt, i);\r\n\t\t}\r\n\t}\r\n\telse\r\n\t\tdbt = NULL;\r\n\tD3D9_DestroyTexture(tex);\r\n\ttex->ptr = dbt;\r\n\r\n\treturn true;\r\n}\r\nvoid D3D9_UploadLightmap(lightmapinfo_t *lm)\r\n{\r\n}\r\n\r\n#endif\r\n"
  },
  {
    "path": "engine/d3d/d3d_shader.c",
    "content": "#include \"quakedef.h\"\n\n#ifdef D3D9QUAKE\n#include \"shader.h\"\n#include \"winquake.h\"\n#if !defined(HMONITOR_DECLARED) && (WINVER < 0x0500)\n    #define HMONITOR_DECLARED\n    DECLARE_HANDLE(HMONITOR);\n#endif\n#include <d3d9.h>\nextern LPDIRECT3DDEVICE9 pD3DDev9;\nextern cvar_t d3d9_hlsl;\n\ntypedef struct {\n\tLPCSTR Name;\n\tLPCSTR Definition;\n} D3DXMACRO;\n\n#define D3DXHANDLE void *\ntypedef enum D3DXINCLUDE_TYPE { \n\tD3DXINC_LOCAL        = 0,\n\tD3DXINC_SYSTEM       = 1,\n\tD3DXINC_FORCE_DWORD  = 0x7fffffff\n} D3DXINCLUDE_TYPE, *LPD3DXINCLUDE_TYPE;\n\n#undef INTERFACE\n#define INTERFACE myID3DXInclude\nDECLARE_INTERFACE(myID3DXInclude)\n{\n\tSTDMETHOD_(HRESULT,Open)(THIS_ D3DXINCLUDE_TYPE IncludeType, LPCSTR pFileName, LPCVOID pParentData, LPCVOID *ppData, UINT *pBytes) PURE;\n\tSTDMETHOD_(HRESULT,Close)(THIS_ LPCVOID pData) PURE;\n};\ntypedef struct myID3DXInclude *LPD3DXINCLUDE;\n\n#undef INTERFACE\n#define INTERFACE d3dxbuffer\nDECLARE_INTERFACE_(d3dxbuffer,IUnknown)\n{\n\tSTDMETHOD(QueryInterface)(THIS_ REFIID,PVOID*) PURE;\n\tSTDMETHOD_(ULONG,AddRef)(THIS) PURE;\n\tSTDMETHOD_(ULONG,Release)(THIS) PURE;\n\n\tSTDMETHOD_(LPVOID,GetBufferPointer)(THIS) PURE;\n\tSTDMETHOD_(SIZE_T,GetBufferSize)(THIS) PURE;\n};\ntypedef struct d3dxbuffer *LPD3DXBUFFER;\n\ntypedef enum _D3DXREGISTER_SET \n{ \n    D3DXRS_BOOL, \n    D3DXRS_INT4, \n    D3DXRS_FLOAT4, \n    D3DXRS_SAMPLER, \n    D3DXRS_FORCE_DWORD = 0x7fffffff \n} D3DXREGISTER_SET, *LPD3DXREGISTER_SET; \ntypedef enum _D3DXPARAMETER_CLASS \n{ \n    D3DXPC_SCALAR, \n    D3DXPC_VECTOR, \n    D3DXPC_MATRIX_ROWS, \n    D3DXPC_MATRIX_COLUMNS, \n    D3DXPC_OBJECT, \n    D3DXPC_STRUCT, \n    D3DXPC_FORCE_DWORD = 0x7fffffff \n} D3DXPARAMETER_CLASS, *LPD3DXPARAMETER_CLASS;\ntypedef enum _D3DXPARAMETER_TYPE \n{ \n    D3DXPT_VOID, \n    D3DXPT_BOOL, \n    D3DXPT_INT, \n    D3DXPT_FLOAT, \n    D3DXPT_STRING, \n    D3DXPT_TEXTURE, \n    D3DXPT_TEXTURE1D, \n    D3DXPT_TEXTURE2D, \n    D3DXPT_TEXTURE3D, \n    D3DXPT_TEXTURECUBE, \n    D3DXPT_SAMPLER, \n    D3DXPT_SAMPLER1D, \n    D3DXPT_SAMPLER2D, \n    D3DXPT_SAMPLER3D, \n    D3DXPT_SAMPLERCUBE, \n    D3DXPT_PIXELSHADER, \n    D3DXPT_VERTEXSHADER, \n    D3DXPT_PIXELFRAGMENT, \n    D3DXPT_VERTEXFRAGMENT,\n} D3DXPARAMETER_TYPE, *LPD3DXPARAMETER_TYPE;\ntypedef struct _D3DXCONSTANT_DESC \n{ \n    LPCSTR Name;                        // Constant name \n\n    D3DXREGISTER_SET RegisterSet;       // Register set \n    UINT RegisterIndex;                 // Register index \n    UINT RegisterCount;                 // Number of registers occupied \n \n    D3DXPARAMETER_CLASS Class;          // Class \n    D3DXPARAMETER_TYPE Type;            // Component type \n \n    UINT Rows;                          // Number of rows \n    UINT Columns;                       // Number of columns \n    UINT Elements;                      // Number of array elements \n    UINT StructMembers;                 // Number of structure member sub-parameters \n \n    UINT Bytes;                         // Data size, in bytes \n    LPCVOID DefaultValue;               // Pointer to default value \n \n} D3DXCONSTANT_DESC, *LPD3DXCONSTANT_DESC; \ntypedef struct _D3DXCONSTANTTABLE_DESC \n{ \n    LPCSTR Creator;                     // Creator string \n    DWORD Version;                      // Shader version \n    UINT Constants;                     // Number of constants \n \n} D3DXCONSTANTTABLE_DESC, *LPD3DXCONSTANTTABLE_DESC;\n \n#undef INTERFACE\n#define INTERFACE d3dxconstanttable\nDECLARE_INTERFACE_(d3dxconstanttable,IUnknown)\n{\n\tSTDMETHOD(QueryInterface)(THIS_ REFIID,PVOID*) PURE;\n\tSTDMETHOD_(ULONG,AddRef)(THIS) PURE;\n\tSTDMETHOD_(ULONG,Release)(THIS) PURE;\n\n\tSTDMETHOD_(LPVOID,GetBufferPointer)(THIS) PURE;\n\tSTDMETHOD_(SIZE_T,GetBufferSize)(THIS) PURE;\n\n    STDMETHOD(GetDesc)(THIS_ D3DXCONSTANTTABLE_DESC *pDesc) PURE; \n    STDMETHOD(GetConstantDesc)(THIS_ D3DXHANDLE hConstant, D3DXCONSTANT_DESC *pConstantDesc, UINT *pCount) PURE; \n \n    STDMETHOD_(D3DXHANDLE, GetConstant)(THIS_ D3DXHANDLE hConstant, UINT Index) PURE; \n    STDMETHOD_(D3DXHANDLE, GetConstantByName)(THIS_ D3DXHANDLE hConstant, LPCSTR pName) PURE; \n\tSTDMETHOD_(D3DXHANDLE, GetConstantElement)(THIS_ D3DXHANDLE hConstant, UINT Index) PURE;\n\n\t/*more stuff not included here cos I don't need it*/\n};\ntypedef struct d3dxconstanttable *LPD3DXCONSTANTTABLE;\n\n\nHRESULT (WINAPI *pD3DXCompileShader) (\n\tLPCSTR pSrcData,\n\tUINT SrcDataLen,\n\tconst D3DXMACRO *pDefines,\n\tLPD3DXINCLUDE pInclude,\n\tLPCSTR pEntrypoint,\n\tLPCSTR pTarget,\n\tUINT Flags,\n\tLPD3DXBUFFER *ppCode,\n\tLPD3DXBUFFER *ppErrorMsgs,\n\tLPD3DXCONSTANTTABLE *constants\n);\nstatic dllhandle_t *shaderlib;\n\n#ifndef IUnknown_Release\n#define IUnknown_Release(This)\t\\\n    (This)->lpVtbl -> Release(This)\n#endif\n\nstatic HRESULT STDMETHODCALLTYPE myID3DXIncludeVtbl_Close(myID3DXInclude *cls, LPCVOID pData)\n{\n\treturn S_OK;\n}\nstatic HRESULT STDMETHODCALLTYPE myID3DXIncludeVtbl_Open(myID3DXInclude *cls, D3DXINCLUDE_TYPE IncludeType, LPCSTR pFileName, LPCVOID pParentData, LPCVOID *ppData, UINT *pBytes)\n{\n\tchar *file = NULL;\n//\t\"sys/defs.h\"\n//\t\"sys/skeletal.h\"\n//\t\"sys/offsetmapping.h\"\n\tif (!strcmp(pFileName, \"sys/fog.h\"))\n\t{\n\t\tfile =\n\t\t\t\t\"#ifdef FRAGMENT_SHADER\\n\"\n\t\t\t\t\"#ifdef FOG\\n\"\n\t\t\t\t\t\"#ifndef DEFS_DEFINED\\n\"\n\t\t\t\t\t\t\"float4 w_fog[2];\\n\"\n\t\t\t\t\t\t\"#define w_fogcolour\tw_fog[0].rgb\\n\"\n\t\t\t\t\t\t\"#define w_fogalpha\t\tw_fog[0].a\\n\"\n\t\t\t\t\t\t\"#define w_fogdensity\tw_fog[1].x\\n\"\n\t\t\t\t\t\t\"#define w_fogdepthbias\tw_fog[1].y\\n\"\n\t\t\t\t\t\"#endif\\n\"\n\t\t\t\t\t\"static const float r_fog_exp2 = 1.0;\\n\"\n\n\t\t\t\t\t\"float3 fog3(float3 regularcolour, float distance)\"\n\t\t\t\t\t\"{\"\n\t\t\t\t\t\t\"float z = w_fogdensity * distance;\\n\"\n\t\t\t\t\t\t\"z = max(0.0,z-w_fogdepthbias);\\n\"\n\t\t\t\t\t\t\"if (r_fog_exp2)\\n\"\n\t\t\t\t\t\t\t\"z *= z;\\n\"\n\t\t\t\t\t\t\"float fac = exp2(-(z * 1.442695));\\n\"\n\t\t\t\t\t\t\"fac = (1.0-w_fogalpha) + (clamp(fac, 0.0, 1.0)*w_fogalpha);\\n\"\n\t\t\t\t\t\t\"return lerp(w_fogcolour, regularcolour, fac);\\n\"\n\t\t\t\t\t\"}\\n\"\n\t\t\t\t\t\"float3 fog3additive(float3 regularcolour, float distance)\"\n\t\t\t\t\t\"{\"\n\t\t\t\t\t\t\"float z = w_fogdensity * distance;\\n\"\n\t\t\t\t\t\t\"z = max(0.0,z-w_fogdepthbias);\\n\"\n\t\t\t\t\t\t\"if (r_fog_exp2)\\n\"\n\t\t\t\t\t\t\t\"z *= z;\\n\"\n\t\t\t\t\t\t\"float fac = exp2(-(z * 1.442695));\\n\"\n\t\t\t\t\t\t\"fac = (1.0-w_fogalpha) + (clamp(fac, 0.0, 1.0)*w_fogalpha);\\n\"\n\t\t\t\t\t\t\"return regularcolour * fac;\\n\"\n\t\t\t\t\t\"}\\n\"\n\t\t\t\t\t\"float4 fog4(float4 regularcolour, float distance)\"\n\t\t\t\t\t\"{\"\n\t\t\t\t\t\t\"return float4(fog3(regularcolour.rgb, distance), 1.0) * regularcolour.a;\\n\"\n\t\t\t\t\t\"}\\n\"\n\t\t\t\t\t\"float4 fog4additive(float4 regularcolour, float distance)\"\n\t\t\t\t\t\"{\"\n\t\t\t\t\t\t\"float z = w_fogdensity * distance;\\n\"\n\t\t\t\t\t\t\"z = max(0.0,z-w_fogdepthbias);\\n\"\n\t\t\t\t\t\t\"if (r_fog_exp2)\\n\"\n\t\t\t\t\t\t\t\"z *= z;\\n\"\n\t\t\t\t\t\t\"float fac = exp2(-(z * 1.442695));\\n\"\n\t\t\t\t\t\t\"fac = (1.0-w_fogalpha) + (clamp(fac, 0.0, 1.0)*w_fogalpha);\\n\"\n\t\t\t\t\t\t\"return regularcolour * float4(fac, fac, fac, 1.0);\\n\"\n\t\t\t\t\t\"}\\n\"\n\t\t\t\t\t\"float4 fog4blend(float4 regularcolour, float distance)\"\n\t\t\t\t\t\"{\"\n\t\t\t\t\t\t\"float z = w_fogdensity * distance;\\n\"\n\t\t\t\t\t\t\"z = max(0.0,z-w_fogdepthbias);\\n\"\n\t\t\t\t\t\t\"if (r_fog_exp2)\\n\"\n\t\t\t\t\t\t\t\"z *= z;\\n\"\n\t\t\t\t\t\t\"float fac = exp2(-(z * 1.442695));\\n\"\n\t\t\t\t\t\t\"fac = (1.0-w_fogalpha) + (clamp(fac, 0.0, 1.0)*w_fogalpha);\\n\"\n\t\t\t\t\t\t\"return regularcolour * float4(1.0, 1.0, 1.0, fac);\\n\"\n\t\t\t\t\t\"}\\n\"\n\t\t\t\t\"#else\\n\"\n\t\t\t\t\t\"float3 fog3(float3 regularcolour, float distance) { return regularcolour; }\\n\"\n\t\t\t\t\t\"float3 fog3additive(float3 regularcolour, float distance) { return regularcolour; }\\n\"\n\t\t\t\t\t\"float4 fog4(float4 regularcolour, float distance) { return regularcolour; }\\n\"\n\t\t\t\t\t\"float4 fog4additive(float4 regularcolour, float distance) { return regularcolour; }\\n\"\n\t\t\t\t\t\"float4 fog4blend(float4 regularcolour, float distance) { return regularcolour; }\\n\"\n\t\t\t\t\"#endif\\n\"\n\t\t\t\"#endif\\n\"\n\t\t\t;\n\t}\n\telse if (!strcmp(pFileName, \"sys/pcf.h\"))\n\t{\n\t\tfile =\n\t\t\t\"#define ShadowmapFilter(smap,proj) 1.0\\n\"\n\t\t\t;\n\t}\n\t\n\tif (file)\n\t{\n\t\t*ppData = file;\n\t\t*pBytes = strlen(file);\n\t\treturn S_OK;\n\t}\n\telse\n\t\treturn E_FAIL;\n}\nstatic struct myID3DXIncludeVtbl myID3DXIncludeVtbl_C = \n{\n\tmyID3DXIncludeVtbl_Open,\n\tmyID3DXIncludeVtbl_Close\n};\nstatic struct myID3DXInclude myID3DXIncludeVtbl_Instance = {&myID3DXIncludeVtbl_C};\n\nstatic qboolean D3D9Shader_CreateProgram (program_t *prog, struct programpermu_s *permu, int ver, const char **precompilerconstants, const char *vert, const char *tcs, const char *tes, const char *geom, const char *frag, qboolean silent, vfsfile_t *blobfile)\n{\n\n\tD3DXMACRO defines[64];\n\tLPD3DXBUFFER code = NULL, errors = NULL;\n\tqboolean success = false;\n\tchar defbuf[2048];\n\tchar *defbufe;\n\n\tif (geom || tcs || tes)\n\t{\n\t\tCon_Printf(\"geometry and tessellation shaders are not availale in d3d9 (%s)\\n\", prog->name);\n\t\treturn false;\n\t}\n\n\tpermu->h.hlsl.vert = NULL;\n\tpermu->h.hlsl.frag = NULL;\n\n\tif (pD3DXCompileShader)\n\t{\n\t\tint consts;\n\t\tfor (consts = 0; precompilerconstants[consts]; consts++)\n\t\t\t;\n\t\tconsts+=2;\n\t\tif (consts >= sizeof(defines) / sizeof(defines[0]))\n\t\t\treturn success;\n\n\t\tconsts = 0;\n\t\tdefines[consts].Name = NULL; /*shader type*/\n\t\tdefines[consts].Definition = \"1\";\n\t\tconsts++;\n\n\t\tdefines[consts].Name = \"ENGINE_\"DISTRIBUTION;\n#ifdef SVNREVISION\n\t\tdefines[consts].Definition = STRINGIFY(SVNREVISION);\n#else\n\t\tdefines[consts].Definition = \"0\";\n#endif\n\t\tconsts++;\n\n\t\tfor (defbufe = defbuf; *precompilerconstants; precompilerconstants++)\n\t\t{\n\t\t\tconst char *l, *nl;\n\t\t\tfor(l = *precompilerconstants; *l; l = nl)\n\t\t\t{\n\t\t\t\tl += 7;\t//skip over the assumed #define\n\n\t\t\t\tl = COM_ParseOut(l, defbufe, defbuf+sizeof(defbuf) - defbufe-1);\n\t\t\t\tdefines[consts].Name = defbufe;\n\t\t\t\tdefbufe += strlen(defbufe)+1;\n\n\t\t\t\twhile (*l == ' ' || *l == '\\t')\n\t\t\t\t\tl++;\n\n\t\t\t\tnl = strchr(l, '\\n');\n\t\t\t\tif (nl && *nl)\n\t\t\t\t{\n\t\t\t\t\tdefines[consts++].Definition = defbufe;\n\t\t\t\t\tmemcpy(defbufe, l, nl-l);\n\t\t\t\t\tdefbufe[nl-l] = 0;\n\t\t\t\t\tdefbufe += nl++-l+1;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tdefines[consts++].Definition = l;\n\t\t\t}\n\t\t}\n\n\t\tdefines[consts].Name = NULL;\n\t\tdefines[consts].Definition = NULL;\n\n\t\tsuccess = true;\n\n\t\tdefines[0].Name = \"VERTEX_SHADER\";\n\t\tif (FAILED(pD3DXCompileShader(vert, strlen(vert), defines, &myID3DXIncludeVtbl_Instance, \"main\", \"vs_2_0\", 0, &code, &errors, (LPD3DXCONSTANTTABLE*)&permu->h.hlsl.ctabv)))\n\t\t\tsuccess = false;\n\t\telse\n\t\t{\n\t\t\tIDirect3DDevice9_CreateVertexShader(pD3DDev9, code->lpVtbl->GetBufferPointer(code), (IDirect3DVertexShader9**)&permu->h.hlsl.vert);\n\t\t\tcode->lpVtbl->Release(code);\n\t\t}\n\t\tif (errors)\n\t\t{\n\t\t\tchar *messages = errors->lpVtbl->GetBufferPointer(errors);\n\t\t\t//int i;\n\t\t//\tfor (i = 0; i < consts; i++)\n\t//\t\t\tCon_Printf(\"%s %i: %s==%s\\n\", prog->name, i, defines[i].Name, defines[i].Definition);\n//\t\t\tCon_Printf(\"%s\\n\", vert);\n\t\t\tCon_Printf(\"Error compiling vertex shader %s:\\n%s\", prog->name, messages);\n\t\t\terrors->lpVtbl->Release(errors);\n\t\t}\n\n\t\tdefines[0].Name = \"FRAGMENT_SHADER\";\n\t\tif (FAILED(pD3DXCompileShader(frag, strlen(frag), defines, &myID3DXIncludeVtbl_Instance, \"main\", \"ps_2_0\", 0, &code, &errors, (LPD3DXCONSTANTTABLE*)&permu->h.hlsl.ctabf)))\n\t\t\tsuccess = false;\n\t\telse\n\t\t{\n\t\t\tIDirect3DDevice9_CreatePixelShader(pD3DDev9, code->lpVtbl->GetBufferPointer(code), (IDirect3DPixelShader9**)&permu->h.hlsl.frag);\n\t\t\tcode->lpVtbl->Release(code);\n\t\t}\n\t\tif (errors)\n\t\t{\n\t\t\tchar *messages = errors->lpVtbl->GetBufferPointer(errors);\n\t\t\tCon_Printf(\"Error compiling pixel shader %s:\\n%s\", prog->name, messages);\n\t\t\terrors->lpVtbl->Release(errors);\n\t\t}\n\t}\n\treturn success;\n}\n\nstatic int D3D9Shader_FindUniform_(LPD3DXCONSTANTTABLE ct, const char *name)\n{\n\tif (ct)\n\t{\n\t\tUINT dc = 1;\n\t\tD3DXCONSTANT_DESC d;\n\t\tif (!FAILED(ct->lpVtbl->GetConstantDesc(ct, (void*)name, &d, &dc)))\n\t\t\treturn d.RegisterIndex;\n\t}\n\treturn -1;\n}\n\nstatic int D3D9Shader_FindUniform(union programhandle_u *h, int type, const char *name)\n{\n\tint offs;\n\n\tif (!type || type == 1)\n\t{\n\t\toffs = D3D9Shader_FindUniform_(h->hlsl.ctabv, name);\n\t\tif (offs >= 0)\n\t\t\treturn offs;\n\t}\n\tif (!type || type == 2)\n\t{\n\t\toffs = D3D9Shader_FindUniform_(h->hlsl.ctabf, name);\n\t\tif (offs >= 0)\n\t\t\treturn offs;\n\t}\n\n\treturn -1;\n}\n\nstatic void D3D9Shader_ProgAutoFields(program_t *prog, struct programpermu_s *pp, char **cvarnames, int *cvartypes)\n{\n\tunsigned int i;\n\tint uniformloc;\n\tchar tmpbuffer[256];\n\n#define ALTLIGHTMAPSAMP 13\n#define ALTDELUXMAPSAMP 16\n\n/*\tprog->nofixedcompat = true;\n\tprog->defaulttextures = 0;\n\tprog->numsamplers = 0;\n*/\n\tint maxparms = 0;\n\tpp->parm = NULL;\n\tpp->numparms = 0;\n\tIDirect3DDevice9_SetVertexShader(pD3DDev9, pp->h.hlsl.vert);\n\tIDirect3DDevice9_SetPixelShader(pD3DDev9, pp->h.hlsl.frag);\n\n\tfor (i = 0; shader_unif_names[i].name; i++)\n\t{\n\t\tuniformloc = D3D9Shader_FindUniform(&pp->h, 0, shader_unif_names[i].name);\n\t\tif (uniformloc != -1)\n\t\t{\n\t\t\tif (pp->numparms == maxparms)\n\t\t\t{\n\t\t\t\tmaxparms = maxparms?maxparms*2:8;\n\t\t\t\tpp->parm = BZ_Realloc(pp->parm, sizeof(*pp->parm) * maxparms);\n\t\t\t}\n\t\t\tpp->parm[pp->numparms].handle = uniformloc;\n\t\t\tpp->parm[pp->numparms].type = shader_unif_names[i].ptype;\n\t\t\tpp->numparms++;\n\t\t}\n\t}\n\n\tfor (i = 0; cvarnames[i]; i++)\n\t{\n\t\tcvar_t *cvarref;\n\t\tif (cvartypes[i] < SP_CVARI)\n\t\t\tcontinue;\n\t\tcvarref = Cvar_FindVar(cvarnames[i]);\n\t\tif (!cvarref)\n\t\t\tcontinue;\n\t\t//just directly sets uniforms. can't cope with cvars dynamically changing.\n\t\tcvarref->flags |= CVAR_SHADERSYSTEM;\n\n\t\tQ_snprintfz(tmpbuffer, sizeof(tmpbuffer), \"cvar_%s\", cvarnames[i]);\n\t\tuniformloc = D3D9Shader_FindUniform(&pp->h, 1, tmpbuffer);\n\t\tif (uniformloc != -1)\n\t\t{\n\t\t\tif (cvartypes[i] == SP_CVARI)\n\t\t\t{\n\t\t\t\tint v[4] = {cvarref->ival, 0, 0, 0};\n\t\t\t\tIDirect3DDevice9_SetVertexShaderConstantI(pD3DDev9, 0, v, 1);\n\t\t\t}\n\t\t\telse\n\t\t\t\tIDirect3DDevice9_SetVertexShaderConstantF(pD3DDev9, 0, cvarref->vec4, 1);\n\t\t}\n\t\tuniformloc = D3D9Shader_FindUniform(&pp->h, 2, tmpbuffer);\n\t\tif (uniformloc != -1)\n\t\t{\n\t\t\tif (cvartypes[i] == SP_CVARI)\n\t\t\t{\n\t\t\t\tint v[4] = {cvarref->ival, 0, 0, 0};\n\t\t\t\tIDirect3DDevice9_SetPixelShaderConstantI(pD3DDev9, 0, v, 1);\n\t\t\t}\n\t\t\telse\n\t\t\t\tIDirect3DDevice9_SetPixelShaderConstantF(pD3DDev9, 0, cvarref->vec4, 1);\n\t\t}\n\t}\n\n\t/*set texture uniforms*/\n\tfor (i = 0; i < prog->numsamplers; i++)\n\t{\n\t\tQ_snprintfz(tmpbuffer, sizeof(tmpbuffer), \"s_t%i\", i);\n\t\tuniformloc = D3D9Shader_FindUniform(&pp->h, 2, tmpbuffer);\n\t\tif (uniformloc != -1)\n\t\t{\n\t\t\tint v[4] = {i};\n\t\t\tIDirect3DDevice9_SetPixelShader(pD3DDev9, pp->h.hlsl.frag);\n\t\t\tIDirect3DDevice9_SetPixelShaderConstantI(pD3DDev9, 0, v, 1);\n\n//\t\t\tif (prog->numsamplers < i+1)\n//\t\t\t\tprog->numsamplers = i+1;\n\t\t}\n\t}\n\n/*\tfor (i = 0; sh_defaultsamplers[i].name; i++)\n\t{\n\t\t//figure out which ones are needed.\n\t\tif (prog->defaulttextures & sh_defaultsamplers[i].defaulttexbits)\n\t\t\tcontinue;\t//don't spam\n\t\tuniformloc = D3D9Shader_FindUniform(&pp->h, 2, sh_defaultsamplers[i].name);\n\t\tif (uniformloc != -1)\n\t\t\tprog->defaulttextures |= sh_defaultsamplers[i].defaulttexbits;\n\t}\n*/\n\n\tif (prog->defaulttextures)\n\t{\n\t\tunsigned int sampnum;\n\t\t/*set default texture uniforms*/\n\t\tsampnum = prog->numsamplers;\n\t\tfor (i = 0; sh_defaultsamplers[i].name; i++)\n\t\t{\n\t\t\tif (prog->defaulttextures & sh_defaultsamplers[i].defaulttexbits)\n\t\t\t{\n\t\t\t\tuniformloc = D3D9Shader_FindUniform(&pp->h, 2, sh_defaultsamplers[i].name);\n\t\t\t\tif (uniformloc != -1)\n\t\t\t\t{\n\t\t\t\t\tint v[4] = {sampnum};\n\t\t\t\t\tIDirect3DDevice9_SetPixelShader(pD3DDev9, pp->h.hlsl.frag);\n\t\t\t\t\tIDirect3DDevice9_SetPixelShaderConstantI(pD3DDev9, 0, v, 1);\n\t\t\t\t}\n\t\t\t\tsampnum++;\n\t\t\t}\n\t\t}\n\t}\n\tIDirect3DDevice9_SetVertexShader(pD3DDev9, NULL);\n\tIDirect3DDevice9_SetPixelShader(pD3DDev9, NULL);\n}\n\nvoid D3D9Shader_DeleteProg(program_t *prog)\n{\n\tunsigned int permu;\n\tstruct programpermu_s *pp;\n\tfor (permu = countof(prog->permu); permu-- > 0; )\n\t{\n\t\tpp = prog->permu[permu];\n\t\tif (!pp)\n\t\t\tcontinue;\n\t\tprog->permu[permu] = NULL;\n\t\tif (pp == prog->permu[0] && permu)\n\t\t\tcontinue;\t//entry 0 (only) can get copied to avoid constant recompile failures (0 is always precompiled)\n\t\tif (pp->h.hlsl.vert)\n\t\t{\n\t\t\tIDirect3DVertexShader9 *vs = pp->h.hlsl.vert;\n\t\t\tpp->h.hlsl.vert = NULL;\n\t\t\tIDirect3DVertexShader9_Release(vs);\n\t\t}\n\t\tif (pp->h.hlsl.frag)\n\t\t{\n\t\t\tIDirect3DPixelShader9 *fs = pp->h.hlsl.frag;\n\t\t\tpp->h.hlsl.frag = NULL;\n\t\t\tIDirect3DPixelShader9_Release(fs);\n\t\t}\n\t\tif (pp->h.hlsl.ctabv)\n\t\t{\n\t\t\tLPD3DXCONSTANTTABLE vct = pp->h.hlsl.ctabv;\n\t\t\tpp->h.hlsl.ctabv = NULL;\n\t\t\tIUnknown_Release(vct);\n\t\t}\n\t\tif (pp->h.hlsl.ctabf)\n\t\t{\n\t\t\tLPD3DXCONSTANTTABLE fct = pp->h.hlsl.ctabf;\n\t\t\tpp->h.hlsl.ctabf = NULL;\n\t\t\tIUnknown_Release(fct);\n\t\t}\n\t\tpp->numparms = 0;\n\t\tBZ_Free(pp->parm);\n\t\tZ_Free(pp);\n\t}\n}\n\nvoid D3D9Shader_Init(void)\n{\n\tD3DCAPS9 caps;\n\n\tdllfunction_t funcs[] =\n\t{\n\t\t{(void**)&pD3DXCompileShader, \"D3DXCompileShader\"},\n\t\t{NULL,NULL}\n\t};\n\n\tif (!shaderlib)\n\t\tshaderlib = Sys_LoadLibrary(\"d3dx9_32\", funcs);\n\tif (!shaderlib)\n\t\tshaderlib = Sys_LoadLibrary(\"d3dx9_34\", funcs);\n\n\tmemset(&sh_config, 0, sizeof(sh_config));\n\tsh_config.minver = 9;\n\tsh_config.maxver = 9;\n\tsh_config.blobpath = \"hlsl/%s.blob\";\n\tsh_config.progpath = shaderlib?\"hlsl/%s.hlsl\":NULL;\n\tsh_config.shadernamefmt = \"%s_hlsl9\";\n\n\tsh_config.progs_supported\t= !!shaderlib && d3d9_hlsl.ival;\n\tsh_config.progs_required\t= false;\n\n\tsh_config.pDeleteProg\t\t= D3D9Shader_DeleteProg;\n\tsh_config.pLoadBlob\t\t\t= NULL;//D3D9Shader_LoadBlob;\n\tsh_config.pCreateProgram\t= D3D9Shader_CreateProgram;\n\tsh_config.pProgAutoFields\t= D3D9Shader_ProgAutoFields;\n\n\tsh_config.tex_env_combine\t\t= 1;\n\tsh_config.nv_tex_env_combine4\t= 1;\n\tsh_config.env_add\t\t\t\t= 1;\n\n\tsh_config.can_mipcap\t\t= true;\t//at creation time, I think.\n\tsh_config.havecubemaps\t\t= true;\n\n\tIDirect3DDevice9_GetDeviceCaps(pD3DDev9, &caps);\n\n\tsh_config.texture_allow_block_padding = false;\t//microsoft blocks this.\n\tif (caps.TextureCaps & D3DPTEXTURECAPS_POW2)\n\t{\t//this flag is a LIMITATION, not a capability.\n\t\tsh_config.texture_non_power_of_two = false;\n\t\tsh_config.texture_non_power_of_two_pic = !!(caps.TextureCaps & D3DPTEXTURECAPS_NONPOW2CONDITIONAL);\t//pic npot is supported if both flags are set.\n\t}\n\telse\n\t{\t//modern cards support npot\n\t\tsh_config.texture_non_power_of_two_pic = true;\n\t\tsh_config.texture_non_power_of_two = true;\n\t}\n\n\tsh_config.texturecube_maxsize = sh_config.texture2d_maxsize = min(caps.MaxTextureWidth, caps.MaxTextureHeight);\n}\n#endif\n\n"
  },
  {
    "path": "engine/d3d/vid_d3d.c",
    "content": "#include \"quakedef.h\"\n#include \"gl_draw.h\"\n#include \"shader.h\"\n#include \"renderque.h\"\n\n#include \"glquake.h\"\n#include \"resource.h\"\n\n#ifdef D3D9QUAKE\n#include \"winquake.h\"\n\n#if !defined(HMONITOR_DECLARED) && (WINVER < 0x0500)\n\t#define HMONITOR_DECLARED\n\tDECLARE_HANDLE(HMONITOR);\n#endif\n\n#include    <d3d9.h>\n#ifndef D3DPRESENT_LINEAR_CONTENT\n#define D3DPRESENT_LINEAR_CONTENT 2\n#endif\n\n//#pragma comment(lib, \"../libs/dxsdk9/lib/d3d9.lib\")\n\n\n/*Fixup outdated windows headers*/\n#ifndef WM_XBUTTONDOWN\n   #define WM_XBUTTONDOWN      0x020B\n   #define WM_XBUTTONUP      0x020C\n#endif\n#ifndef MK_XBUTTON1\n   #define MK_XBUTTON1         0x0020\n#endif\n#ifndef MK_XBUTTON2\n   #define MK_XBUTTON2         0x0040\n#endif\n// copied from DarkPlaces in an attempt to grab more buttons\n#ifndef MK_XBUTTON3\n   #define MK_XBUTTON3         0x0080\n#endif\n#ifndef MK_XBUTTON4\n   #define MK_XBUTTON4         0x0100\n#endif\n#ifndef MK_XBUTTON5\n   #define MK_XBUTTON5         0x0200\n#endif\n#ifndef MK_XBUTTON6\n   #define MK_XBUTTON6         0x0400\n#endif\n#ifndef MK_XBUTTON7\n   #define MK_XBUTTON7         0x0800\n#endif\n\n#ifndef WM_INPUT\n\t#define WM_INPUT 255\n#endif\n\n//static void D3D9_GetBufferSize(int *width, int *height); //not defined\nstatic void resetD3D9(void);\nstatic LPDIRECT3D9 pD3D;\nLPDIRECT3DDEVICE9 pD3DDev9;\nstatic D3DPRESENT_PARAMETERS d3dpp;\nfloat d3d_trueprojection_std[16];\nfloat d3d_trueprojection_view[16];\n\nstatic qboolean vid_initializing;\n\nextern qboolean\t\tscr_initialized;                // ready to draw\nextern qboolean\t\tscr_drawloading;\nextern qboolean\t\tscr_con_forcedraw;\nstatic qboolean d3d_resized;\n\nextern cvar_t vid_hardwaregamma;\nextern cvar_t vid_srgb;\n\n\n/*void BuildGammaTable (float g, float c);\nstatic void\tD3D9_VID_GenPaletteTables (unsigned char *palette)\n{\n\textern unsigned short\t\tramps[3][256];\n\tqbyte\t*pal;\n\tunsigned r,g,b;\n\tunsigned v;\n\tunsigned short i;\n\tunsigned\t*table;\n\textern qbyte gammatable[256];\n\n\tif (palette)\n\t{\n\t\textern cvar_t v_contrast;\n\t\tBuildGammaTable(v_gamma.value, v_contrast.value);\n\n\t\t//\n\t\t// 8 8 8 encoding\n\t\t//\n\t\tif (1)//vid_hardwaregamma.value)\n\t\t{\n\t\t//\tdon't built in the gamma table\n\n\t\t\tpal = palette;\n\t\t\ttable = d_8to24rgbtable;\n\t\t\tfor (i=0 ; i<256 ; i++)\n\t\t\t{\n\t\t\t\tr = pal[0];\n\t\t\t\tg = pal[1];\n\t\t\t\tb = pal[2];\n\t\t\t\tpal += 3;\n\n\t\t//\t\tv = (255<<24) + (r<<16) + (g<<8) + (b<<0);\n\t\t//\t\tv = (255<<0) + (r<<8) + (g<<16) + (b<<24);\n\t\t\t\tv = (255<<24) + (r<<0) + (g<<8) + (b<<16);\n\t\t\t\t*table++ = v;\n\t\t\t}\n\t\t\td_8to24rgbtable[255] &= 0xffffff;\t// 255 is transparent\n\t\t}\n\t\telse\n\t\t{\n\t//computer has no hardware gamma (poor suckers) increase table accordingly\n\n\t\t\tpal = palette;\n\t\t\ttable = d_8to24rgbtable;\n\t\t\tfor (i=0 ; i<256 ; i++)\n\t\t\t{\n\t\t\t\tr = gammatable[pal[0]];\n\t\t\t\tg = gammatable[pal[1]];\n\t\t\t\tb = gammatable[pal[2]];\n\t\t\t\tpal += 3;\n\n\t\t//\t\tv = (255<<24) + (r<<16) + (g<<8) + (b<<0);\n\t\t//\t\tv = (255<<0) + (r<<8) + (g<<16) + (b<<24);\n\t\t\t\tv = (255<<24) + (r<<0) + (g<<8) + (b<<16);\n\t\t\t\t*table++ = v;\n\t\t\t}\n\t\t\td_8to24rgbtable[255] &= 0xffffff;\t// 255 is transparent\n\t\t}\n\n\t\tif (LittleLong(1) != 1)\n\t\t{\n\t\t\tfor (i=0 ; i<256 ; i++)\n\t\t\t\td_8to24rgbtable[i] = LittleLong(d_8to24rgbtable[i]);\n\t\t}\n\t}\n\n\tif (pD3DDev9)\n\t\tIDirect3DDevice9_SetGammaRamp(pD3DDev9, 0, D3DSGR_NO_CALIBRATION, (D3DGAMMARAMP *)ramps);\n}\n*/\ntypedef enum {MS_WINDOWED, MS_FULLSCREEN, MS_FULLDIB, MS_UNINIT} modestate_t;\nstatic modestate_t modestate;\n\n\nstatic void D3DVID_UpdateWindowStatus (HWND hWnd)\n{\n\tPOINT p;\n\tRECT nr;\n\tint window_x, window_y;\n\tint window_width, window_height;\n\tGetClientRect(hWnd, &nr);\n\n\t//if its bad then we're probably minimised\n\tif (nr.right <= nr.left)\n\t\treturn;\n\tif (nr.bottom <= nr.top)\n\t\treturn;\n\n\tp.x = 0;\n\tp.y = 0;\n\tClientToScreen(hWnd, &p);\n\twindow_x = p.x;\n\twindow_y = p.y;\n\twindow_width = nr.right - nr.left;\n\twindow_height = nr.bottom - nr.top;\n//\tvid.pixelwidth = window_width;\n//\tvid.pixelheight = window_height;\n\n\twindow_rect.left = window_x;\n\twindow_rect.top = window_y;\n\twindow_rect.right = window_x + window_width;\n\twindow_rect.bottom = window_y + window_height;\n\twindow_center_x = (window_rect.left + window_rect.right) / 2;\n\twindow_center_y = (window_rect.top + window_rect.bottom) / 2;\n\n\tINS_UpdateClipCursor ();\n}\n\nstatic qboolean D3D9AppActivate(BOOL fActive, BOOL minimize)\n/****************************************************************************\n*\n* Function:     AppActivate\n* Parameters:   fActive - True if app is activating\n*\n* Description:  If the application is activating, then swap the system\n*               into SYSPAL_NOSTATIC mode so that our palettes will display\n*               correctly.\n*\n****************************************************************************/\n{\n\tstatic BOOL\tsound_active;\n\n\tif (vid.activeapp == fActive && Minimized == minimize)\n\t\treturn false;\t//so windows doesn't crash us over and over again.\n\n\tvid.activeapp = fActive;\n\tMinimized = minimize;\n\n// enable/disable sound on focus gain/loss\n\tif (!vid.activeapp && sound_active)\n\t{\n\t\tS_BlockSound ();\n\t\tsound_active = false;\n\t}\n\telse if (vid.activeapp && !sound_active)\n\t{\n\t\tS_UnblockSound ();\n\t\tsound_active = true;\n\t}\n\n\tINS_UpdateGrabs(modestate != MS_WINDOWED, vid.activeapp);\n\n\tif (fActive)\n\t{\n\t\tCvar_ForceCallback(&v_gamma);\n\t}\n\tif (!fActive)\n\t{\n\t\tCvar_ForceCallback(&v_gamma);\t//wham bam thanks.\n\t}\n\n\treturn true;\n}\n\n\n\n\n\nstatic LRESULT WINAPI D3D9_WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)\n{\n\tLONG    lRet = 1;\n\tint\t\tfActive, fMinimized, temp;\n\textern unsigned int uiWheelMessage;\n\n\tif ( uMsg == uiWheelMessage )\n\t\tuMsg = WM_MOUSEWHEEL;\n\n\tswitch (uMsg)\n\t{\n\t\tcase WM_KILLFOCUS:\n\t\t\tif (modestate == MS_FULLDIB)\n\t\t\t\tShowWindow(mainwindow, SW_SHOWMINNOACTIVE);\n\t\t\tbreak;\n\n\t\tcase WM_CREATE:\n\t\t\tbreak;\n\n\t\tcase WM_MOVE:\n\t\t\tD3DVID_UpdateWindowStatus (hWnd);\n\t\t\tbreak;\n\n\t\tcase WM_KEYDOWN:\n\t\tcase WM_SYSKEYDOWN:\n\t\t\tif (!vid_initializing)\n\t\t\t\tINS_TranslateKeyEvent (wParam, lParam, true, 0, false);\n\t\t\tbreak;\n\n\t\tcase WM_KEYUP:\n\t\tcase WM_SYSKEYUP:\n\t\t\tif (!vid_initializing)\n\t\t\t\tINS_TranslateKeyEvent (wParam, lParam, false, 0, false);\n\t\t\tbreak;\n\n\t\tcase WM_SYSCHAR:\n\t\t// keep Alt-Space from happening\n\t\t\tbreak;\n\n\t\tcase WM_APPCOMMAND:\n\t\t\tlRet = INS_AppCommand(lParam);\n\t\t\tbreak;\n\n\t// this is complicated because Win32 seems to pack multiple mouse events into\n\t// one update sometimes, so we always check all states and look for events\n\t\tcase WM_LBUTTONDOWN:\n\t\tcase WM_LBUTTONUP:\n\t\tcase WM_RBUTTONDOWN:\n\t\tcase WM_RBUTTONUP:\n\t\tcase WM_MBUTTONDOWN:\n\t\tcase WM_MBUTTONUP:\n\t\tcase WM_MOUSEMOVE:\n\t\tcase WM_XBUTTONDOWN:\n\t\tcase WM_XBUTTONUP:\n\t\t\ttemp = 0;\n\n\t\t\tif (wParam & MK_LBUTTON)\n\t\t\t\ttemp |= 1;\n\n\t\t\tif (wParam & MK_RBUTTON)\n\t\t\t\ttemp |= 2;\n\n\t\t\tif (wParam & MK_MBUTTON)\n\t\t\t\ttemp |= 4;\n\n\t\t\tif (wParam & MK_XBUTTON1)\n\t\t\t\ttemp |= 8;\n\n\t\t\tif (wParam & MK_XBUTTON2)\n\t\t\t\ttemp |= 16;\n\n\t\t\tif (wParam & MK_XBUTTON3)\n\t\t\t\ttemp |= 32;\n\n\t\t\tif (wParam & MK_XBUTTON4)\n\t\t\t\ttemp |= 64;\n\n\t\t\tif (wParam & MK_XBUTTON5)\n\t\t\t\ttemp |= 128;\n\n\t\t\tif (wParam & MK_XBUTTON6)\n\t\t\t\ttemp |= 256;\n\n\t\t\tif (wParam & MK_XBUTTON7)\n\t\t\t\ttemp |= 512;\n\n\t\t\tif (!vid_initializing)\n\t\t\t\tINS_MouseEvent (temp);\n\n\t\t\tbreak;\n\n\t\t// JACK: This is the mouse wheel with the Intellimouse\n\t\t// Its delta is either positive or neg, and we generate the proper\n\t\t// Event.\n\t\tcase WM_MOUSEWHEEL:\n\t\t\tif (!vid_initializing)\n\t\t\t{\n\t\t\t\tif ((short) HIWORD(wParam) > 0)\n\t\t\t\t{\n\t\t\t\t\tKey_Event(0, K_MWHEELUP, 0, true);\n\t\t\t\t\tKey_Event(0, K_MWHEELUP, 0, false);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tKey_Event(0, K_MWHEELDOWN, 0, true);\n\t\t\t\t\tKey_Event(0, K_MWHEELDOWN, 0, false);\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase WM_INPUT:\n\t\t\t// raw input handling\n\t\t\tif (!vid_initializing)\n\t\t\t\tINS_RawInput_Read((HANDLE)lParam);\n\t\t\tbreak;\n\t\tcase WM_DEVICECHANGE:\n\t\t\tCOM_AddWork(WG_MAIN, INS_DeviceChanged, NULL, NULL, uMsg, 0);\n\t\t\tlRet = TRUE;\n\t\t\tbreak;\n\n\t\tcase WM_SETCURSOR:\n\t\t\t//only use a custom cursor if the cursor is inside the client area\n\t\t\tswitch(lParam&0xffff)\n\t\t\t{\n\t\t\tcase 0:\n\t\t\t\tbreak;\n\t\t\tcase HTCLIENT:\n\t\t\t\tif (hCustomCursor)\t//custom cursor enabled\n\t\t\t\t\tSetCursor(hCustomCursor);\n\t\t\t\telse\t\t\t\t//fallback on an arrow cursor, just so we have something visible at startup or so\n\t\t\t\t\tSetCursor(hArrowCursor);\n\t\t\t\tlRet = TRUE;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tlRet = DefWindowProcW (hWnd, uMsg, wParam, lParam);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase WM_GETMINMAXINFO:\n\t\t\t{\n\t\t\t\tRECT windowrect;\n\t\t\t\tRECT clientrect;\n\t\t\t\tMINMAXINFO *mmi = (MINMAXINFO *) lParam;\n\n\t\t\t\tGetWindowRect (hWnd, &windowrect);\n\t\t\t\tGetClientRect (hWnd, &clientrect);\n\n\t\t\t\tmmi->ptMinTrackSize.x = 320 + ((windowrect.right - windowrect.left) - (clientrect.right - clientrect.left));\n\t\t\t\tmmi->ptMinTrackSize.y = 200 + ((windowrect.bottom - windowrect.top) - (clientrect.bottom - clientrect.top));\n\t\t\t}\n\t\t\treturn 0;\n\t\tcase WM_SIZE:\n\t\t\td3d_resized = true;\n\t\t\tbreak;\n\n\t\tcase WM_CLOSE:\n\t\t\tif (!vid_initializing)\n\t\t\t\tif (MessageBox (mainwindow, \"Are you sure you want to quit?\", \"Confirm Exit\",\n\t\t\t\t\t\t\tMB_YESNO | MB_SETFOREGROUND | MB_ICONQUESTION) == IDYES)\n\t\t\t\t{\n\t\t\t\t\tCbuf_AddText(\"\\nquit\\n\", RESTRICT_LOCAL);\n\t\t\t\t}\n\n\t\t\tbreak;\n\n\t\tcase WM_ACTIVATE:\n\t\t\tfActive = LOWORD(wParam);\n\t\t\tfMinimized = (BOOL) HIWORD(wParam);\n\t\t\tif (!D3D9AppActivate(!(fActive == WA_INACTIVE), fMinimized))\n\t\t\t\tbreak;//so, urm, tell me microsoft, what changed?\n\t\t\tif (modestate == MS_FULLDIB)\n\t\t\t\tShowWindow(mainwindow, SW_SHOWNORMAL);\n\n\t\t// fix the leftover Alt from any Alt-Tab or the like that switched us away\n//\t\t\tClearAllStates ();\n\n\t\t\tbreak;\n\n\t\tcase WM_DESTROY:\n\t\t{\n//\t\t\tif (dibwindow)\n//\t\t\t\tDestroyWindow (dibwindow);\n\t\t}\n\t\tbreak;\n\n#ifdef HAVE_CDPLAYER\n\t\tcase MM_MCINOTIFY:\n\t\t\tlRet = CDAudio_MessageHandler (hWnd, uMsg, wParam, lParam);\n\t\t\tbreak;\n#endif\n\n\t\tdefault:\n\t\t\t/* pass all unhandled messages to DefWindowProc */\n\t\t\tlRet = DefWindowProc (hWnd, uMsg, wParam, lParam);\n\t\tbreak;\n\t}\n\n\t/* return 1 if handled message, 0 if not */\n\treturn lRet;\n}\n\nstatic void D3D9_VID_SwapBuffers(void)\n{\n\tif (vid.flags&VID_SRGB_FB_FAKED)\n\t{\n\t\tIDirect3DSwapChain9 *swapchain;\n\t\tIDirect3DDevice9_GetSwapChain(pD3DDev9, 0, &swapchain);\n\t\tIDirect3DSwapChain9_Present(swapchain, NULL, NULL, NULL, NULL, D3DPRESENT_LINEAR_CONTENT);\n\t\tIDirect3DSwapChain9_Release(swapchain);\n\t}\n\telse\n\t\tIDirect3DDevice9_Present(pD3DDev9, NULL, NULL, NULL, NULL);\n}\n\nstatic void resetD3D9(void)\n{\n\tHRESULT res;\n\tres = IDirect3DDevice9_Reset(pD3DDev9, &d3dpp);\n\tif (FAILED(res))\n\t{\n\t\tCon_Printf(\"IDirect3DDevice9_Reset failed (%u)\\n\", (unsigned)res&0xffff);\n\t\treturn;\n\t}\n\n\n\t/*clear the screen to black as soon as we start up, so there's no lingering framebuffer state*/\n\tIDirect3DDevice9_BeginScene(pD3DDev9);\n\tIDirect3DDevice9_Clear(pD3DDev9, 0, NULL, D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);\n\tIDirect3DDevice9_EndScene(pD3DDev9);\n\tD3D9_VID_SwapBuffers();\n\n\n\n\n\n\n\n\t//IDirect3DDevice9_SetRenderState(pD3DDev9, D3DRENDERSTATE_DITHERENABLE, FALSE);\n\t//IDirect3DDevice9_SetRenderState(pD3DDev9, D3DRENDERSTATE_SPECULARENABLE, FALSE);\n\t//IDirect3DDevice9_SetRenderState(pD3DDev9, D3DRENDERSTATE_TEXTUREPERSPECTIVE, TRUE);\n\tIDirect3DDevice9_SetRenderState(pD3DDev9, D3DRS_LIGHTING, FALSE);\n}\n\n#if (WINVER < 0x500) && !defined(__GNUC__)\ntypedef struct tagMONITORINFO\n{\n\tDWORD   cbSize;\n\tRECT    rcMonitor;\n\tRECT    rcWork;\n\tDWORD   dwFlags;\n} MONITORINFO, *LPMONITORINFO;\n#endif\n\nstatic qboolean initD3D9Device(HWND hWnd, rendererstate_t *info, unsigned int devno, unsigned int devtype)\n{\n\tint err;\n\tRECT rect;\n\tD3DADAPTER_IDENTIFIER9 inf;\n\tD3DCAPS9 caps;\n\tunsigned int cflags;\n\n\tmemset(&inf, 0, sizeof(inf));\n\tif (FAILED(IDirect3D9_GetAdapterIdentifier(pD3D, devno, 0, &inf)))\n\t\treturn false;\n\n\tif (FAILED(IDirect3D9_GetDeviceCaps(pD3D, devno, devtype, &caps)))\n\t\treturn false;\n\n\tmemset(&d3dpp, 0, sizeof(d3dpp));    // clear out the struct for use\n\td3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;    // discard old frames\n\td3dpp.hDeviceWindow = hWnd;    // set the window to be used by Direct3D\n\td3dpp.BackBufferWidth = info->width;\n\td3dpp.BackBufferHeight = info->height;\n\td3dpp.MultiSampleType = info->multisample;\n\td3dpp.BackBufferCount = 1 + info->triplebuffer;\n\td3dpp.FullScreen_RefreshRateInHz = info->fullscreen?info->rate:0;\t//don't pass a rate if not fullscreen, d3d doesn't like it.\n\td3dpp.Windowed = !info->fullscreen;\n\n\td3dpp.EnableAutoDepthStencil = true;\n\tif (info->depthbits==16)\n\t\td3dpp.AutoDepthStencilFormat = D3DFMT_D16;\n\telse if (info->depthbits==32)\n\t\td3dpp.AutoDepthStencilFormat = D3DFMT_D32;\n\telse\n\t\td3dpp.AutoDepthStencilFormat = D3DFMT_D24S8;\n\td3dpp.BackBufferFormat = D3DFMT_UNKNOWN;\n\tif (info->fullscreen)\n\t{\n\t\tif (info->bpp == 16)\n\t\t\td3dpp.BackBufferFormat = D3DFMT_R5G6B5;\n\t\telse if (info->bpp == 30)\n\t\t\td3dpp.BackBufferFormat = D3DFMT_A2R10G10B10;\n\t\telse\n\t\t\td3dpp.BackBufferFormat = D3DFMT_X8R8G8B8;\n\t}\n\n\tswitch(info->wait)\n\t{\n\tdefault:\n\t\td3dpp.PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT;\n\t\tbreak;\n\tcase 0:\n\t\td3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;\n\t\tbreak;\n\tcase 1:\n\t\td3dpp.PresentationInterval = D3DPRESENT_INTERVAL_ONE;\n\t\tbreak;\n\tcase 2:\n\t\td3dpp.PresentationInterval = D3DPRESENT_INTERVAL_TWO;\n\t\tbreak;\n\tcase 3:\n\t\td3dpp.PresentationInterval = D3DPRESENT_INTERVAL_THREE;\n\t\tbreak;\n\tcase 4:\n\t\td3dpp.PresentationInterval = D3DPRESENT_INTERVAL_FOUR;\n\t\tbreak;\n\t}\n\n\tcflags = D3DCREATE_FPU_PRESERVE;\n\tif ((caps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT) && (caps.DevCaps & D3DDEVCAPS_PUREDEVICE))\n\t\tcflags |= D3DCREATE_HARDWARE_VERTEXPROCESSING;\n\telse\n\t\tcflags |= D3DCREATE_SOFTWARE_VERTEXPROCESSING;\n\t//cflags |= D3DCREATE_DISABLE_DRIVER_MANAGEMENT;\n\n\tpD3DDev9 = NULL;\n\t// create a device class using this information and information from the d3dpp stuct\n\terr = IDirect3D9_CreateDevice(pD3D,\n\t\t\tdevno,\n\t\t\tdevtype,\n\t\t\thWnd,\n\t\t\tcflags,\n\t\t\t&d3dpp,\n\t\t\t&pD3DDev9);\n\n\tif (pD3DDev9)\n\t{\n\t\tHMONITOR hm;\n\t\tMONITORINFO mi;\n\t\tchar *s;\n\t\tfor (s = inf.Description + strlen(inf.Description)-1; s >= inf.Description && *s <= ' '; s--)\n\t\t\t*s = 0;\n\t\tCon_Printf(\"D3D9 Driver: %s\\n\", inf.Description);\n\n\t\tvid.numpages = d3dpp.BackBufferCount;\n\n\t\tvid.flags = VID_SRGB_CAPABLE;\n\n\t\tif (d3dpp.Windowed)\t//fullscreen we get positioned automagically.\n\t\t{\t\t\t\t\t//windowed, we get positioned at 0,0... which is often going to be on the wrong screen\n\t\t\t\t\t\t\t//the user can figure it out from here\n\t\t\tstatic HANDLE huser32;\n\t\t\tBOOL (WINAPI *pGetMonitorInfoA)(HMONITOR hMonitor, LPMONITORINFO lpmi);\n\t\t\tif (!huser32)\n\t\t\t\thuser32 = LoadLibrary(\"user32.dll\");\n\t\t\tif (!huser32)\n\t\t\t\treturn false;\n\t\t\tpGetMonitorInfoA = (void*)GetProcAddress(huser32, \"GetMonitorInfoA\");\n\t\t\tif (!pGetMonitorInfoA)\n\t\t\t\treturn false;\n\n\t\t\thm = IDirect3D9_GetAdapterMonitor(pD3D, devno);\n\t\t\tmemset(&mi, 0, sizeof(mi));\n\t\t\tmi.cbSize = sizeof(mi);\n\t\t\tpGetMonitorInfoA(hm, &mi);\n\t\t\trect.left = mi.rcWork.left + ((mi.rcWork.right - mi.rcWork.left) - info->width) / 2;\n\t\t\trect.top = mi.rcWork.top + ((mi.rcWork.bottom - mi.rcWork.top) - info->height) / 2;\n\t\t\trect.right = rect.left+d3dpp.BackBufferWidth;\n\t\t\trect.bottom = rect.top+d3dpp.BackBufferHeight;\n\t\t\tAdjustWindowRectEx(&rect, WS_OVERLAPPEDWINDOW, FALSE, 0);\n\t\t\tMoveWindow(d3dpp.hDeviceWindow, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, false);\n\t\t}\n\t\tD3D9Shader_Init();\n\n\t\t{\n\t\t\tint i;\n\t\t\tstruct {\n\t\t\t\tunsigned int pti;\n\t\t\t\tunsigned int d3d9;\n\t\t\t\tunsigned int usage;\n\t\t\t} fmts[] =\n\t\t\t{\n\t\t\t\t{PTI_BGRX8,\t\t\tD3DFMT_X8R8G8B8,\tD3DUSAGE_QUERY_FILTER},\n\t\t\t\t{PTI_BGRA8,\t\t\tD3DFMT_A8R8G8B8,\tD3DUSAGE_QUERY_FILTER},\n\t\t\t\t{PTI_RGB565,\t\tD3DFMT_R5G6B5,\t\tD3DUSAGE_QUERY_FILTER},\n\t\t\t\t{PTI_ARGB1555,\t\tD3DFMT_A1R5G5B5,\tD3DUSAGE_QUERY_FILTER},\n\t\t\t\t{PTI_ARGB4444,\t\tD3DFMT_A4R4G4B4,\tD3DUSAGE_QUERY_FILTER},\n\t\t\t\t{PTI_A2BGR10,\t\tD3DFMT_A2B10G10R10,\tD3DUSAGE_QUERY_FILTER},\n\t\t\t\t{PTI_RGBA16F,\t\tD3DFMT_A16B16G16R16F,\tD3DUSAGE_QUERY_FILTER},\n\t\t\t\t{PTI_RGBA32F,\t\tD3DFMT_A32B32G32R32F,\tD3DUSAGE_QUERY_FILTER},\n\t\t\t\t{PTI_L8,\t\t\tD3DFMT_L8,\t\t\tD3DUSAGE_QUERY_FILTER},\n\n\t\t\t\t{PTI_BGRX8_SRGB,\tD3DFMT_X8R8G8B8,\tD3DUSAGE_QUERY_FILTER|D3DUSAGE_QUERY_SRGBREAD},\n\t\t\t\t{PTI_BGRA8_SRGB,\tD3DFMT_A8R8G8B8,\tD3DUSAGE_QUERY_FILTER|D3DUSAGE_QUERY_SRGBREAD},\n\n\t\t\t\t{PTI_BC1_RGB,\t\tD3DFMT_DXT1,\t\tD3DUSAGE_QUERY_FILTER},\n\t\t\t\t{PTI_BC1_RGBA,\t\tD3DFMT_DXT1,\t\tD3DUSAGE_QUERY_FILTER},\n\t\t\t\t{PTI_BC2_RGBA,\t\tD3DFMT_DXT3,\t\tD3DUSAGE_QUERY_FILTER},\n\t\t\t\t{PTI_BC3_RGBA,\t\tD3DFMT_DXT5,\t\tD3DUSAGE_QUERY_FILTER},\n\t\t\t\t{PTI_BC1_RGB_SRGB,\tD3DFMT_DXT1,\t\tD3DUSAGE_QUERY_FILTER|D3DUSAGE_QUERY_SRGBREAD},\n\t\t\t\t{PTI_BC1_RGBA_SRGB,\tD3DFMT_DXT1,\t\tD3DUSAGE_QUERY_FILTER|D3DUSAGE_QUERY_SRGBREAD},\n\t\t\t\t{PTI_BC2_RGBA_SRGB,\tD3DFMT_DXT3,\t\tD3DUSAGE_QUERY_FILTER|D3DUSAGE_QUERY_SRGBREAD},\n\t\t\t\t{PTI_BC3_RGBA_SRGB,\tD3DFMT_DXT5,\t\tD3DUSAGE_QUERY_FILTER|D3DUSAGE_QUERY_SRGBREAD},\n\t\t\t};\n\t\t\tfor (i = 0; i < countof(fmts); i++)\n\t\t\t\tif (SUCCEEDED(IDirect3D9_CheckDeviceFormat(pD3D, devno, devtype, d3dpp.BackBufferFormat, fmts[i].usage, D3DRTYPE_TEXTURE, fmts[i].d3d9)))\n\t\t\t\t\tsh_config.texfmt[fmts[i].pti] = true;\n\t\t}\n\n\t\t//fixme: the engine kinda insists on rgba textures, which d3d9 does NOT support.\n\t\t//we currently have some swapping, so these load, just slowly.\n\t\tsh_config.texfmt[PTI_RGBX8] |= sh_config.texfmt[PTI_BGRX8];\n\t\tsh_config.texfmt[PTI_RGBA8] |= sh_config.texfmt[PTI_BGRA8];\n\t\tsh_config.texfmt[PTI_RGBX8_SRGB] |= sh_config.texfmt[PTI_BGRX8_SRGB];\n\t\tsh_config.texfmt[PTI_RGBA8_SRGB] |= sh_config.texfmt[PTI_BGRA8_SRGB];\n\n\t\treturn true;\t//successful\n\t}\n\telse\n\t{\n\t\tchar *s;\n\t\tswitch(err)\n\t\t{\n\t\tdefault: s = \"Unkown error\"; break;\n\t\tcase D3DERR_DEVICELOST: s = \"Device lost\"; break;\n\t\tcase D3DERR_INVALIDCALL: s = \"Invalid call\"; break;\n\t\tcase D3DERR_NOTAVAILABLE: s = \"Not available\"; break;\n\t\tcase D3DERR_OUTOFVIDEOMEMORY: s = \"Out of video memory\"; break;\n\t\t}\n\t\tCon_Printf(\"IDirect3D9_CreateDevice failed: %s.\\n\", s);\n\t}\n\treturn false;\n}\n\nstatic void initD3D9(HWND hWnd, rendererstate_t *info)\n{\n\tint i;\n\tint numadaptors;\n\tD3DADAPTER_IDENTIFIER9 inf;\n\n\tstatic HMODULE d3d9dll;\n\tLPDIRECT3D9 (WINAPI *pDirect3DCreate9) (int version);\n\n\tif (!d3d9dll)\n\t\td3d9dll = LoadLibrary(\"d3d9.dll\");\n\tif (!d3d9dll)\n\t{\n\t\tCon_Printf(CON_ERROR \"Direct3d 9 does not appear to be installed\\n\");\n\t\treturn;\n\t}\n\tpDirect3DCreate9 = (void*)GetProcAddress(d3d9dll, \"Direct3DCreate9\");\n\tif (!pDirect3DCreate9)\n\t{\n\t\tCon_Printf(CON_ERROR \"Direct3d 9 does not appear to be installed properly\\n\");\n\t\treturn;\n\t}\n\n\tpD3D = pDirect3DCreate9(D3D_SDK_VERSION);    // create the Direct3D interface\n\tif (!pD3D)\n\t\treturn;\n\n\tnumadaptors = IDirect3D9_GetAdapterCount(pD3D);\n\tfor (i = 0; i < numadaptors; i++)\n\t{\t//NVIDIA's debug app requires that we use a specific device\n\t\tmemset(&inf, 0, sizeof(inf));\n\t\tIDirect3D9_GetAdapterIdentifier(pD3D, i, 0, &inf);\n\t\tif (strstr(inf.Description, \"PerfHUD\"))\n\t\t\tif (initD3D9Device(hWnd, info, i, D3DDEVTYPE_REF))\n\t\t\t\treturn;\n\t}\n\tfor (i = 0; i < numadaptors; i++)\n\t{\t//try each adaptor in turn until we get one that actually works\n\t\tif (initD3D9Device(hWnd, info, i, D3DDEVTYPE_HAL))\n\t\t\treturn;\n\t}\n\tfor (i = 0; i < numadaptors; i++)\n\t{\t//try each adaptor in turn until we get one that actually works\n\t\tif (initD3D9Device(hWnd, info, i, D3DDEVTYPE_SW))\n\t\t\treturn;\n\t}\n\tfor (i = 0; i < numadaptors; i++)\n\t{\t//try each adaptor in turn until we get one that actually works\n\t\tif (initD3D9Device(hWnd, info, i, D3DDEVTYPE_REF))\n\t\t\treturn;\n\t}\n}\n\nstatic qboolean D3D9_VID_Init(rendererstate_t *info, unsigned char *palette)\n{\n\tDWORD width = info->width;\n\tDWORD height = info->height;\n\t//DWORD bpp = info->bpp;\n\t//DWORD zbpp = 16;\n\t//DWORD flags = 0;\n\tDWORD wstyle;\n\tRECT rect;\n\tMSG msg;\n\tHICON hIcon = LoadIcon (global_hInstance, MAKEINTRESOURCE (IDI_ICON1));\n\n\t//DDGAMMARAMP gammaramp;\n\t//int i;\n\n\tchar *CLASSNAME = \"FTED3D9QUAKE\";\n\tWNDCLASS wc = {\n\t\t0,\n\t\t&D3D9_WindowProc,\n\t\t0,\n\t\t0,\n\t\tNULL,\n\t\thIcon,\n\t\tNULL,\n\t\tNULL,\n\t\tNULL,\n\t\tCLASSNAME\n\t};\n\n\twc.hCursor       = hArrowCursor = LoadCursor (NULL,IDC_ARROW);\n\n\tvid_initializing = true;\n\n\tRegisterClass(&wc);\n\n\tif (info->fullscreen)\n\t\twstyle = 0;\n\telse\n\t\twstyle = WS_OVERLAPPEDWINDOW;\n\n\trect.left = (GetSystemMetrics(SM_CXSCREEN) - info->width) / 2;\n\trect.top = (GetSystemMetrics(SM_CYSCREEN) - info->height) / 2;\n\trect.right = rect.left+info->width;\n\trect.bottom = rect.top+info->height;\n\tAdjustWindowRectEx(&rect, wstyle, FALSE, 0);\n\tmainwindow = CreateWindow(CLASSNAME, \"Direct3D9\", wstyle, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, NULL, NULL, NULL, NULL);\n\n\t// Try as specified.\n\n\tinitD3D9(mainwindow, info);\n\tif (!pD3DDev9)\n\t{\n\t\tCon_Printf(\"No suitable D3D9 device found\\n\");\n\t\treturn false;\n\t}\n\n\tCL_UpdateWindowTitle();\n\n\twhile (PeekMessage(&msg, NULL,  0, 0, PM_REMOVE))\n\t{\n\t\tTranslateMessage(&msg);\n\t\tDispatchMessage(&msg);\n\t}\n\n\tShowWindow(mainwindow, SW_NORMAL);\n\n\tIDirect3DDevice9_Clear(pD3DDev9, 0, NULL, D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);\n\tIDirect3DDevice9_BeginScene(pD3DDev9);\n\tIDirect3DDevice9_EndScene(pD3DDev9);\n\tD3D9_VID_SwapBuffers();\n\n\tD3D9_Set2D();\n\n//\tpD3DX->lpVtbl->GetBufferSize((void*)pD3DX, &width, &height);\n\tvid.pixelwidth = width;\n\tvid.pixelheight = height;\n\n\tvid.width = width;\n\tvid.height = height;\n\n\tvid_initializing = false;\n\n\tIDirect3DDevice9_SetRenderState(pD3DDev9, D3DRS_LIGHTING, FALSE);\n\n\tGetWindowRect(mainwindow, &window_rect);\n\n\n//\tD3D9_VID_GenPaletteTables(palette);\n\n\t{\n\t\textern qboolean\tmouseactive;\n\t\tmouseactive = false;\n\t}\n\n//\tD3D9BE_Reset(false);\n\n\t//FIXME: old hardware is not guarenteed to support hardware cursors.\n\t//this should not be a problem on dx9+ hardware, but might on earlier stuff.\n\trf->VID_CreateCursor = WIN_CreateCursor;\n\trf->VID_DestroyCursor = WIN_DestroyCursor;\n\trf->VID_SetCursor = WIN_SetCursor;\n\n\treturn true;\n}\n\nstatic void\t (D3D9_VID_DeInit)\t\t\t\t(void)\n{\n\tImage_Shutdown();\n\n\t/*final shutdown, kill the video stuff*/\n\tif (pD3DDev9)\n\t{\n\t\tD3D9BE_Reset(true);\n\n\t\t/*try and knock it back into windowed mode to avoid d3d bugs*/\n\t\td3dpp.Windowed = true;\n\t\tIDirect3DDevice9_Reset(pD3DDev9, &d3dpp);\n\n\t\tIDirect3DDevice9_Release(pD3DDev9);\n\t\tpD3DDev9 = NULL;\n\t}\n\tif (pD3D)\n\t{\n\t\tIDirect3D9_Release(pD3D);\n\t\tpD3D = NULL;\n\t}\n\tif (mainwindow)\n\t{\n\t\tDestroyWindow(mainwindow);\n\t\tmainwindow = NULL;\n\t}\n\n//\tCvar_Unhook(&v_gamma);\n//\tCvar_Unhook(&v_contrast);\n//\tCvar_Unhook(&v_brightness);\n}\n\nqboolean D3D9_VID_ApplyGammaRamps\t\t(unsigned int gammarampsize, unsigned short *ramps)\n{\n\tif (d3dpp.Windowed)\n\t\treturn false;\n\tif (pD3DDev9 && ramps && gammarampsize == 256)\n\t\tIDirect3DDevice9_SetGammaRamp(pD3DDev9, 0, D3DSGR_NO_CALIBRATION, (D3DGAMMARAMP *)ramps);\n\treturn true;\n}\nstatic char\t*(D3D9_VID_GetRGBInfo)\t\t\t(int *bytestride, int *truevidwidth, int *truevidheight, enum uploadfmt *fmt)\n{\n\tIDirect3DSurface9 *backbuf, *surf;\n\tD3DLOCKED_RECT rect;\n\tD3DSURFACE_DESC desc;\n\tint i, j, c;\n\tqbyte *ret = NULL;\n\tqbyte *p;\n\n\t/*DON'T read the front buffer.\n\tthis function can be used by the quakeworld remote screenshot 'snap' feature,\n\tso DO NOT read the frontbuffer because it can show other information than just quake to third parties*/\n\n\tif (!FAILED(IDirect3DDevice9_GetRenderTarget(pD3DDev9, 0, &backbuf)))\n\t{\n\t\tif (!FAILED(IDirect3DSurface9_GetDesc(backbuf, &desc)))\n\t\tif (desc.Format == D3DFMT_X8R8G8B8 || desc.Format == D3DFMT_A8R8G8B8)\n\t\tif (!FAILED(IDirect3DDevice9_CreateOffscreenPlainSurface(pD3DDev9,\n\t\t\t\t\tdesc.Width, desc.Height, desc.Format,\n\t\t\t\t\tD3DPOOL_SYSTEMMEM, &surf, NULL))\n\t\t\t)\n\t\t{\n\n\t\t\tif (!FAILED(IDirect3DDevice9_GetRenderTargetData(pD3DDev9, backbuf, surf)))\n\t\t\tif (!FAILED(IDirect3DSurface9_LockRect(surf, &rect, NULL, D3DLOCK_NO_DIRTY_UPDATE|D3DLOCK_READONLY|D3DLOCK_NOSYSLOCK)))\n\t\t\t{\n\t\t\t\tret = BZ_Malloc(desc.Width*desc.Height*3);\n\t\t\t\tif (ret)\n\t\t\t\t{\n\t\t\t\t\t*fmt = TF_RGB24;\n\n\t\t\t\t\t// read surface rect and convert 32 bgra to 24 rgb and flip\n\t\t\t\t\tc = desc.Width*desc.Height*3;\n\t\t\t\t\tp = (qbyte *)rect.pBits;\n\n\t\t\t\t\tfor (i=c-(3*desc.Width); i>=0; i-=(3*desc.Width))\n\t\t\t\t\t{\n\t\t\t\t\t\tfor (j=0; j<desc.Width; j++)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tret[i+j*3+0] = p[j*4+2];\n\t\t\t\t\t\t\tret[i+j*3+1] = p[j*4+1];\n\t\t\t\t\t\t\tret[i+j*3+2] = p[j*4+0];\n\t\t\t\t\t\t}\n\t\t\t\t\t\tp += rect.Pitch;\n\t\t\t\t\t}\n\t\t\t\t\t*bytestride = desc.Width*3;\n\t\t\t\t\t*truevidwidth = desc.Width;\n\t\t\t\t\t*truevidheight = desc.Height;\n\t\t\t\t}\n\n\t\t\t\tIDirect3DSurface9_UnlockRect(surf);\n\t\t\t}\n\t\t\tIDirect3DSurface9_Release(surf);\n\t\t}\n\t\tIDirect3DSurface9_Release(backbuf);\n\t}\n\n\treturn ret;\n}\nstatic void\t(D3D9_VID_SetWindowCaption)\t\t(const char *msg)\n{\n\tSetWindowText(mainwindow, msg);\n}\n\nvoid D3D9_Set2D (void)\n{\n\tfloat m[16];\n//\tIDirect3DDevice9_EndScene(pD3DDev9);\n\n\tMatrix4x4_CM_OrthographicD3D(d3d_trueprojection_std, 0 + (0.5*vid.width/vid.pixelwidth), vid.width + (0.5*vid.width/vid.pixelwidth), 0 + (0.5*vid.height/vid.pixelheight), vid.height + (0.5*vid.height/vid.pixelheight), 0, 100);\n\n\tMatrix4x4_Identity(m);\n\tIDirect3DDevice9_SetTransform(pD3DDev9, D3DTS_WORLD, (D3DMATRIX*)m);\n\n\tMatrix4x4_Identity(m);\n\tIDirect3DDevice9_SetTransform(pD3DDev9, D3DTS_VIEW, (D3DMATRIX*)m);\n\n\tvid.fbvwidth = vid.width;\n\tvid.fbvheight = vid.height;\n\tvid.fbpwidth = vid.pixelwidth;\n\tvid.fbpheight = vid.pixelheight;\n\n\tr_refdef.pxrect.x = 0;\n\tr_refdef.pxrect.y = 0;\n\tr_refdef.pxrect.width = vid.fbpwidth;\n\tr_refdef.pxrect.height = vid.fbpheight;\n\n\tD3D9BE_Set2D();\n\tD3D9BE_SetViewport(0, vid.fbpwidth, 0, vid.fbpheight);\n}\n\nstatic int d3d9error(int i)\n{\n\tif (FAILED(i))// != D3D_OK)\n\t\tCon_Printf(\"D3D error: %i\\n\", i);\n\treturn i;\n}\n\nstatic qboolean\t(D3D9_SCR_UpdateScreen)\t\t\t(void)\n{\n\t//extern int keydown[];\n\t//extern cvar_t vid_conheight;\n#ifdef TEXTEDITOR\n\t//extern qboolean editormodal;\n#endif\n\tqboolean nohud, noworld;\n\n\tif (!scr_initialized || !con_initialized)\n\t{\n\t\treturn false;                         // not initialized yet\n\t}\n\n\tif (d3d_resized/* && d3dpp.Windowed*/)\n\t{\n\t\textern cvar_t vid_conautoscale, vid_conwidth;\n\t\td3d_resized = false;\n\n\t\t// force width/height to be updated\n\t\t//vid.pixelwidth = window_rect.right - window_rect.left;\n\t\t//vid.pixelheight = window_rect.bottom - window_rect.top;\n\t\tD3DVID_UpdateWindowStatus(mainwindow);\n\n\t\tD3D9BE_Reset(true);\n\t\tvid.pixelwidth = d3dpp.BackBufferWidth = window_rect.right - window_rect.left;\n\t\tvid.pixelheight = d3dpp.BackBufferHeight = window_rect.bottom - window_rect.top;\n\t\tresetD3D9();\n\t\tD3D9BE_Reset(false);\n\n\t\tCvar_ForceCallback(&vid_conautoscale);\n\t\tCvar_ForceCallback(&vid_conwidth);\n\t}\n\n\tif (vid_srgb.modified)\n\t{\n\t\tvid_srgb.modified = false;\n\t\t//VID_SRGBAWARE defines whether textures are meant to be srgb or not.\n\t\t//as it requires a vid_reload, we don't mess with it here.\n\t\t//that said, we can still change srgb freely otherwise.\n\t\tvid.flags &= VID_SRGBAWARE;\n\t\tif ((vid.flags & VID_SRGBAWARE) || vid_srgb.ival<0)\n\t\t\tvid.flags |= (d3dpp.Windowed)?VID_SRGB_FB_FAKED:VID_SRGB_FB_LINEAR;\n\t\tIDirect3DDevice9_SetRenderState(pD3DDev9, D3DRS_SRGBWRITEENABLE, !!(vid.flags&VID_SRGB_FB_LINEAR));\n\t}\n\n\tswitch (IDirect3DDevice9_TestCooperativeLevel(pD3DDev9))\n\t{\n\tcase D3DERR_DEVICELOST:\n\t\t//the user has task switched away from us or something, don't draw anything until they switch back to us\n\t\tD3D9BE_Reset(true);\n\t\treturn false;\n\tcase D3DERR_DEVICENOTRESET:\n\t\tD3D9BE_Reset(true);\n\t\tresetD3D9();\n\t\tif (FAILED(IDirect3DDevice9_TestCooperativeLevel(pD3DDev9)))\n\t\t{\n\t\t\tCon_Printf(\"Device lost, restarting video\\n\");\n\t\t\tCmd_ExecuteString(\"vid_restart\", RESTRICT_LOCAL);\n\t\t\treturn false;\n\t\t}\n\n\t\tCvar_ForceCallback(&v_gamma);\n\t\tbreak;\n\tdefault:\n\t\tbreak;\n\t}\n\n\tD3D9BE_Reset(false);\n\n\tif (r_clear.ival)\n\t{\n\t\td3d9error(IDirect3DDevice9_Clear(pD3DDev9, 0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB((r_clear.ival&1)?255:0,(r_clear.ival&2)?255:0,(r_clear.ival&4)?255:0), 1, 0));\n\t}\n\n\tif (scr_disabled_for_loading)\n\t{\n\t\textern float scr_disabled_time;\n\t\tif (Sys_DoubleTime() - scr_disabled_time > 60 || Key_Dest_Has(~kdm_game))\n\t\t{\n\t\t\tscr_disabled_for_loading = false;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tIDirect3DDevice9_BeginScene(pD3DDev9);\n\t\t\tscr_drawloading = true;\n\t\t\tSCR_DrawLoading (true);\n\t\t\tscr_drawloading = false;\n\t\t\tif (R2D_Flush)\n\t\t\t\tR2D_Flush();\n\t\t\tIDirect3DDevice9_EndScene(pD3DDev9);\n\t\t\tD3D9_VID_SwapBuffers();\n\t\t\treturn true;\n\t\t}\n\t}\n\n\tShader_DoReload();\n\n\td3d9error(IDirect3DDevice9_BeginScene(pD3DDev9));\n/*\n#ifdef TEXTEDITOR\n\tif (editormodal)\n\t{\n\t\tEditor_Draw();\n\t\tV_UpdatePalette (false);\n\t\tMedia_RecordFrame();\n\t\tR2D_BrightenScreen();\n\n\t\tif (key_dest == key_console)\n\t\t\tCon_DrawConsole(vid_conheight.value/2, false);\n\t\tGL_EndRendering ();\n\t\tGL_DoSwap();\n\t\tRSpeedEnd(RSPEED_TOTALREFRESH);\n\t\treturn true;\n\t}\n#endif\n*/\n\n\tD3D9_Set2D();\n\n//\n// do 3D refresh drawing, and then update the screen\n//\n\tSCR_SetUpToDrawConsole ();\n\n\tnoworld = false;\n\tnohud = false;\n\n\tif (topmenu && topmenu->isopaque)\n\t\tnohud = true;\n#ifdef VM_CG\n\telse if (q3 && q3->cg.Redraw(cl.time))\n\t\tnohud = true;\n#endif\n#ifdef CSQC_DAT\n\telse if (CSQC_DrawView())\n\t\tnohud = true;\n#endif\n\telse if (r_worldentity.model && cls.state == ca_active)\n\t\tV_RenderView (nohud);\n\telse\n\t\tnoworld = true;\n\n\tR2D_BrightenScreen();\n\n\tscr_con_forcedraw = false;\n\tif (noworld)\n\t{\n\t\tif (scr_con_current != vid.height)\n\t\t\tR2D_ConsoleBackground(0, vid.height, true);\n\t\telse\n\t\t\tscr_con_forcedraw = true;\n\n\t\tnohud = true;\n\t}\n\n\tSCR_DrawTwoDimensional(nohud);\n\n\tV_UpdatePalette (false);\n\tMedia_RecordFrame();\n\n\tRSpeedShow();\n\n\tif (R2D_Flush)\n\t\tR2D_Flush();\n\n\td3d9error(IDirect3DDevice9_EndScene(pD3DDev9));\n\t{\n\t\tRSpeedMark();\n\t\tD3D9_VID_SwapBuffers();\n\t\tRSpeedEnd(RSPEED_PRESENT);\n\t}\n\n\twindow_center_x = (window_rect.left + window_rect.right)/2;\n\twindow_center_y = (window_rect.top + window_rect.bottom)/2;\n\n\n\tINS_UpdateGrabs(modestate != MS_WINDOWED, vid.activeapp);\n\treturn true;\n}\n\n\n\n\n\n\n\nstatic void\t(D3D9_Draw_Init)\t\t\t\t(void)\n{\n\t{\n\t\tvid_srgb.modified = false;\n\t\t//VID_SRGBAWARE defines whether textures are meant to be srgb or not.\n\t\t//we're doing a vid_reload here, so we can change it here easily enough\n\t\tif (vid_srgb.ival > 0)\t//d3d9 does srgb using post-process stuff for us.\n\t\t\tvid.flags |= VID_SRGBAWARE;\n\t\telse\n\t\t\tvid.flags = 0;\n\t\tif ((vid.flags & VID_SRGBAWARE) || vid_srgb.ival<0)\n\t\t\tvid.flags |= (d3dpp.Windowed)?VID_SRGB_FB_FAKED:VID_SRGB_FB_LINEAR;\n\n\t\tIDirect3DDevice9_SetRenderState(pD3DDev9, D3DRS_SRGBWRITEENABLE, !!(vid.flags&VID_SRGB_FB_LINEAR));\n\t}\n\n\tR2D_Init();\n}\nstatic void\t(D3D9_Draw_Shutdown)\t\t\t\t(void)\n{\n\tR2D_Shutdown();\n\tImage_Shutdown();\n}\n\nstatic void\t(D3D9_R_Init)\t\t\t\t\t(void)\n{\n}\nstatic void\t(D3D9_R_DeInit)\t\t\t\t\t(void)\n{\n\tR_GAliasFlushSkinCache(true);\n\tSurf_DeInit();\n\tShader_Shutdown();\n}\n\n\n\nstatic void D3D9_SetupViewPortProjection(void)\n{\n\tint\t\tx, x2, y2, y, w, h;\n\n\tfloat fov_x, fov_y;\n\tfloat fovv_x, fovv_y;\n\n\tAngleVectors (r_refdef.viewangles, vpn, vright, vup);\n\tVectorCopy (r_refdef.vieworg, r_origin);\n\n\t//\n\t// set up viewpoint\n\t//\n\tx = r_refdef.vrect.x * vid.pixelwidth/(int)vid.width;\n\tx2 = (r_refdef.vrect.x + r_refdef.vrect.width) * vid.pixelwidth/(int)vid.width;\n\ty = (r_refdef.vrect.y) * vid.pixelheight/(int)vid.height;\n\ty2 = ((int)(r_refdef.vrect.y + r_refdef.vrect.height)) * vid.pixelheight/(int)vid.height;\n\n\t// fudge around because of frac screen scale\n\tif (x > 0)\n\t\tx--;\n\tif (x2 < vid.pixelwidth)\n\t\tx2++;\n\tif (y < 0)\n\t\ty--;\n\tif (y2 < vid.pixelheight)\n\t\ty2++;\n\n\tw = x2 - x;\n\th = y2 - y;\n\n\tfov_x = r_refdef.fov_x;//+sin(cl.time)*5;\n\tfov_y = r_refdef.fov_y;//-sin(cl.time+1)*5;\n\tfovv_x = r_refdef.fovv_x;\n\tfovv_y = r_refdef.fovv_y;\n\n\tif ((r_refdef.flags & RDF_UNDERWATER) && !(r_refdef.flags & RDF_WATERWARP))\n\t{\n\t\tfov_x *= 1 + (((sin(cl.time * 4.7) + 1) * 0.015) * r_waterwarp.value);\n\t\tfov_y *= 1 + (((sin(cl.time * 3.0) + 1) * 0.015) * r_waterwarp.value);\n\t\tfovv_x *= 1 + (((sin(cl.time * 4.7) + 1) * 0.015) * r_waterwarp.value);\n\t\tfovv_y *= 1 + (((sin(cl.time * 3.0) + 1) * 0.015) * r_waterwarp.value);\n\t}\n\n\t/*view matrix*/\n\tMatrix4x4_CM_ModelViewMatrixFromAxis(r_refdef.m_view, vpn, vright, vup, r_refdef.vieworg);\n\td3d9error(IDirect3DDevice9_SetTransform(pD3DDev9, D3DTS_VIEW, (D3DMATRIX*)r_refdef.m_view));\n\n\tif (r_xflip.ival)\n\t{\n\t\tfov_x *= -1;\n\t\tr_refdef.flipcull ^= SHADER_CULL_FLIP;\n\t\tfovv_x *= -1;\n\t}\n\n\tif (r_refdef.maxdist)\n\t{\n\t\t/*d3d projection matricies scale depth to 0 to 1*/\n\t\tMatrix4x4_CM_Projection_Far(d3d_trueprojection_std, fov_x, fov_y, r_refdef.mindist, r_refdef.maxdist, true);\n\t\tMatrix4x4_CM_Projection_Far(d3d_trueprojection_view, fovv_x, fovv_y, r_refdef.mindist, r_refdef.maxdist, true);\n\t\t/*ogl projection matricies scale depth to -1 to 1, and I would rather my code used consistant culling*/\n\t\tMatrix4x4_CM_Projection_Far(r_refdef.m_projection_std, fov_x, fov_y, r_refdef.mindist, r_refdef.maxdist, false);\n\t}\n\telse\n\t{\n\t\t/*d3d projection matricies scale depth to 0 to 1*/\n\t\tMatrix4x4_CM_Projection_Inf(d3d_trueprojection_std, fov_x, fov_y, r_refdef.mindist, true);\n\t\tMatrix4x4_CM_Projection_Inf(d3d_trueprojection_view, fovv_x, fovv_y, r_refdef.mindist, true);\n\t\t/*ogl projection matricies scale depth to -1 to 1, and I would rather my code used consistant culling*/\n\t\tMatrix4x4_CM_Projection_Inf(r_refdef.m_projection_std, fov_x, fov_y, r_refdef.mindist, false);\n\t}\n\n\tD3D9BE_SetViewport(x, w, y, h);\n}\n\nstatic void\t(D3D9_R_RenderView)\t\t\t\t(void)\n{\n\tif (!r_norefresh.value)\n\t{\n\t\tint cull = r_refdef.flipcull;\n\t\tSurf_SetupFrame();\n\n\t\tif (!r_refdef.globalfog.density)\n\t\t{\n\t\t\textern cvar_t r_fog_linear;\n\n\t\t\tint fogtype = ((r_refdef.flags & RDF_UNDERWATER) && cl.fog[FOGTYPE_WATER].density)?FOGTYPE_WATER:FOGTYPE_AIR;\n\t\t\tCL_BlendFog(&r_refdef.globalfog, &cl.oldfog[fogtype], realtime, &cl.fog[fogtype]);\n\t\t\tif (!r_fog_linear.ival)\n\t\t\t\tr_refdef.globalfog.density /= 64;\t//FIXME\n\t\t}\n\n\t\t//check if we can do underwater warp\n\t\tif (cls.protocol != CP_QUAKE2)\t//quake2 tells us directly\n\t\t{\n\t\t\tif (r_viewcontents & FTECONTENTS_FLUID)\n\t\t\t\tr_refdef.flags |= RDF_UNDERWATER;\n\t\t\telse\n\t\t\t\tr_refdef.flags &= ~RDF_UNDERWATER;\n\t\t}\n\t\tif (r_refdef.flags & RDF_UNDERWATER)\n\t\t{\n\t\t\textern cvar_t r_projection;\n\t\t\tif (!r_waterwarp.value || r_projection.ival)\n\t\t\t\tr_refdef.flags &= ~RDF_UNDERWATER;\t//no warp at all\n\t//\t\telse if (r_waterwarp.value > 0 && scenepp_waterwarp)\n\t//\t\t\tr_refdef.flags |= RDF_WATERWARP;\t//try fullscreen warp instead if we can\n\t\t}\n\n\t\tD3D9_SetupViewPortProjection();\n\n\t//\tif (r_clear.ival && !(r_refdef.flags & RDF_NOWORLDMODEL))\n\t//\t\td3d9error(IDirect3DDevice9_Clear(pD3DDev9, 0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(255,0,0), 1, 0));\n\t//\telse\n\t\t\td3d9error(IDirect3DDevice9_Clear(pD3DDev9, 0, NULL, D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0,0,0), 1, 0));\n\n\t\tR_SetFrustum (r_refdef.m_projection_std, r_refdef.m_view);\n\t\tRQ_BeginFrame();\n//\t\tif (!(r_refdef.flags & RDF_NOWORLDMODEL))\n//\t\t{\n//\t\t\tif (!r_refdef.recurse && !(r_refdef.flags & RDF_DISABLEPARTICLES))\n//\t\t\t\tP_DrawParticles ();\n//\t\t}\n\t\tSurf_DrawWorld();\n\t\tRQ_RenderBatchClear();\n\t\tr_refdef.flipcull = cull;\n\t}\n\tD3D9_Set2D ();\n}\n\nvoid\t(D3D9_R_PreNewMap)\t\t\t\t(void);\n\nvoid\t(D3D9_R_PushDlights)\t\t\t(void);\nvoid\t(D3D9_R_AddStain)\t\t\t\t(vec3_t org, float red, float green, float blue, float radius);\nvoid\t(D3D9_R_LessenStains)\t\t\t(void);\n\nqboolean (D3D9_VID_Init)\t\t\t\t(rendererstate_t *info, unsigned char *palette);\nvoid\t (D3D9_VID_DeInit)\t\t\t\t(void);\nvoid\t(D3D9_VID_SetPalette)\t\t\t(unsigned char *palette);\nvoid\t(D3D9_VID_ShiftPalette)\t\t\t(unsigned char *palette);\nvoid\t(D3D9_VID_SetWindowCaption)\t\t(const char *msg);\n\nvoid D3D9BE_RenderToTextureUpdate2d(qboolean destchanged)\n{\n}\n\nrendererinfo_t d3d9rendererinfo =\n{\n\t\"Direct3D9\",\n\t{\n\t\t\"D3D9\",\n\t\t\"D3D\",\n\t\t\"Direct3d\",\n\t\t\"DirectX\",\n\t\t\"2\"\t//this is evil, but worth a laugh.\n\t},\n\tQR_DIRECT3D9,\n\n\tD3D9_Draw_Init,\n\tD3D9_Draw_Shutdown,\n\n\tD3D9_UpdateFiltering,\n\tD3D9_LoadTextureMips,\n\tD3D9_DestroyTexture,\n\n\tD3D9_R_Init,\n\tD3D9_R_DeInit,\n\tD3D9_R_RenderView,\n\n\tD3D9_VID_Init,\n\tD3D9_VID_DeInit,\n\tD3D9_VID_SwapBuffers,\n\tD3D9_VID_ApplyGammaRamps,\n\tNULL,\n\tNULL,\n\tNULL,\n\tD3D9_VID_SetWindowCaption,\n\tD3D9_VID_GetRGBInfo,\n\n\tD3D9_SCR_UpdateScreen,\n\n\tD3D9BE_SelectMode,\n\tD3D9BE_DrawMesh_List,\n\tD3D9BE_DrawMesh_Single,\n\tD3D9BE_SubmitBatch,\n\tD3D9BE_GetTempBatch,\n\tD3D9BE_DrawWorld,\n\tD3D9BE_Init,\n\tD3D9BE_GenBrushModelVBO,\n\tD3D9BE_ClearVBO,\n\tD3D9BE_UploadAllLightmaps,\n\tD3D9BE_SelectEntity,\n\tD3D9BE_SelectDLight,\n\tD3D9BE_Scissor,\n\tD3D9BE_LightCullModel,\n\n\tD3D9BE_VBO_Begin,\n\tD3D9BE_VBO_Data,\n\tD3D9BE_VBO_Finish,\n\tD3D9BE_VBO_Destroy,\n\n\tD3D9BE_RenderToTextureUpdate2d,\n\n\t\"no more\"\n};\n\n#endif\n"
  },
  {
    "path": "engine/d3d/vid_d3d11.c",
    "content": "#include \"quakedef.h\"\n#ifdef D3D11QUAKE\n#include \"winquake.h\"\n#include \"gl_draw.h\"\n#include \"glquake.h\"\n#include \"shader.h\"\n#include \"renderque.h\"\n#include \"resource.h\"\n#include \"vr.h\"\n\n#define FUCKDXGI\n\n#define COBJMACROS\n#include <d3d11.h>\n\nID3D11Device *pD3DDev11;\nID3D11DeviceContext *d3ddevctx;\n\n//#include <d3d11_1.h>\n//ID3D11DeviceContext1 *d3ddevctx1;\n\n#ifdef WINRT\t//winrt crap has its own non-hwnd window crap, after years of microsoft forcing everyone to use hwnds for everything. I wonder why they don't have that many winrt apps.\n#pragma comment(lib, \"dxgi.lib\")\n#pragma comment(lib, \"D3D11.lib\")\n#include \"dxgi1_2.h\"\n#else\n\n/*Fixup outdated windows headers*/\n#ifndef WM_XBUTTONDOWN\n   #define WM_XBUTTONDOWN      0x020B\n   #define WM_XBUTTONUP      0x020C\n#endif\n#ifndef MK_XBUTTON1\n   #define MK_XBUTTON1         0x0020\n#endif\n#ifndef MK_XBUTTON2\n   #define MK_XBUTTON2         0x0040\n#endif\n// copied from DarkPlaces in an attempt to grab more buttons\n#ifndef MK_XBUTTON3\n   #define MK_XBUTTON3         0x0080\n#endif\n#ifndef MK_XBUTTON4\n   #define MK_XBUTTON4         0x0100\n#endif\n#ifndef MK_XBUTTON5\n   #define MK_XBUTTON5         0x0200\n#endif\n#ifndef MK_XBUTTON6\n   #define MK_XBUTTON6         0x0400\n#endif\n#ifndef MK_XBUTTON7\n   #define MK_XBUTTON7         0x0800\n#endif\n\n#ifndef WM_INPUT\n\t#define WM_INPUT 255\n#endif\n#endif\n\n#define DEFINE_QGUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \\\n\t\tconst GUID DECLSPEC_SELECTANY name \\\n\t\t\t\t= { l, w1, w2, { b1, b2,  b3,  b4,  b5,  b6,  b7,  b8 } }\n\nDEFINE_QGUID(qIID_ID3D11Texture2D,0x6f15aaf2,0xd208,0x4e89,0x9a,0xb4,0x48,0x95,0x35,0xd3,0x4f,0x9c);\n\n#ifdef WINRT\nIDXGISwapChain1 *d3dswapchain;\n#else\nIDXGISwapChain *d3dswapchain;\n#endif\nIDXGIOutput *d3dscreen;\n\nID3D11RenderTargetView *fb_backbuffer;\nID3D11DepthStencilView *fb_backdepthstencil;\nstatic DXGI_FORMAT\tdepthformat;\n\nvoid *d3d11mod;\nstatic unsigned int d3d11multisample_count, d3d11multisample_quality;\n\nstatic qboolean vid_initializing;\n\nextern qboolean\t\tscr_initialized;                // ready to draw\nextern qboolean\t\tscr_drawloading;\nextern qboolean\t\tscr_con_forcedraw;\nstatic qboolean d3d_resized;\n\nstatic void released3dbackbuffer(void);\nstatic qboolean resetd3dbackbuffer(int width, int height);\n\n#if 0//def _DEBUG\n#include <dxgidebug.h>\nconst GUID IID_IDXGIDebug = { 0x119E7452,0xDE9E,0x40fe, { 0x88,0x06,0x88,0xF9,0x0C,0x12,0xB4,0x41 } };\n\nconst GUID DXGI_DEBUG_ALL = { 0xe48ae283, 0xda80, 0x490b, {0x87, 0xe6, 0x43, 0xe9, 0xa9, 0xcf, 0xda, 0x8 }};\n\nvoid DoDXGIDebug(void)\n{\n\tIDXGIDebug *dbg = NULL;\n\n\tHRESULT (WINAPI *pDXGIGetDebugInterface)(REFIID riid, void **ppDebug);\n\tdllfunction_t dxdidebugfuncs[] =\n\t{\n\t\t{(void**)&pDXGIGetDebugInterface, \"DXGIGetDebugInterface\"},\n\t\t{NULL}\n\t};\n\tpDXGIGetDebugInterface = NULL;\n\tSys_LoadLibrary(\"dxgidebug\", dxdidebugfuncs);\n\tpDXGIGetDebugInterface(&IID_IDXGIDebug, &dbg);\n\tif (dbg)\n\t{\n\t\tIDXGIDebug_ReportLiveObjects(dbg, DXGI_DEBUG_ALL, DXGI_DEBUG_RLO_ALL);\n\t\tIDXGIDebug_Release(dbg);\n\t}\n}\n#else\n#define DoDXGIDebug()\n#endif\n\nchar *D3D_NameForResult(HRESULT hr)\n{\n\tif (hr == DXGI_ERROR_DEVICE_REMOVED && pD3DDev11)\n\t\thr = ID3D11Device_GetDeviceRemovedReason(pD3DDev11);\n\n\tswitch(hr)\n\t{\n\tcase E_OUTOFMEMORY:\t\t\t\t\t\treturn \"E_OUTOFMEMORY\";\n\tcase E_NOINTERFACE:\t\t\t\t\t\treturn \"E_NOINTERFACE\";\n\tcase DXGI_ERROR_DEVICE_HUNG:\t\t\treturn \"DXGI_ERROR_DEVICE_HUNG\";\n\tcase DXGI_ERROR_DEVICE_REMOVED:\t\t\treturn \"DXGI_ERROR_DEVICE_REMOVED\";\n\tcase DXGI_ERROR_DEVICE_RESET:\t\t\treturn \"DXGI_ERROR_DEVICE_RESET\";\n\tcase DXGI_ERROR_DRIVER_INTERNAL_ERROR:\treturn \"DXGI_ERROR_DRIVER_INTERNAL_ERROR\";\n\tcase DXGI_ERROR_INVALID_CALL:\t\t\treturn \"DXGI_ERROR_INVALID_CALL\";\n\tdefault:\t\t\t\t\t\t\t\treturn va(\"%lx\", hr);\n\t}\n}\n\nstatic void D3D11_PresentOrCrash(void)\n{\n\textern cvar_t vid_vsync;\n\tRSpeedMark();\n\tHRESULT hr = IDXGISwapChain_Present(d3dswapchain, max(0,vid_vsync.ival), 0);\n\tif (FAILED(hr))\n\t\tSys_Error(\"IDXGISwapChain_Present: %s\\n\", D3D_NameForResult(hr));\n\tRSpeedEnd(RSPEED_PRESENT);\n}\n\ntypedef enum {MS_WINDOWED, MS_FULLSCREEN, MS_FULLWINDOW, MS_UNINIT} dx11modestate_t;\nstatic dx11modestate_t modestate;\n\n//FIXME: need to push/pop render targets like gl does, to not harm shadowmaps/refraction/etc.\nvoid D3D11_ApplyRenderTargets(qboolean usedepth)\n{\n\tunsigned int width = 0, height = 0;\n\tint i;\n\ttexid_t textures[1];\n\ttexid_t depth;\n\tID3D11RenderTargetView *rtv[sizeof(textures)/sizeof(textures[0])];\n\tID3D11DepthStencilView *dsv;\n\n\tfor (i = 0; i < sizeof(textures)/sizeof(textures[0]); i++)\n\t{\n\t\tif (!*r_refdef.rt_destcolour[i].texname)\n\t\t\tbreak;\n\t\ttextures[i] = R2D_RT_GetTexture(r_refdef.rt_destcolour[i].texname, &width, &height);\n\t\tif (textures[i]->ptr2)\n\t\t{\n\t\t\tID3D11ShaderResourceView_Release((ID3D11ShaderResourceView*)textures[i]->ptr2);\n\t\t\ttextures[i]->ptr2 = NULL;\n\t\t}\n\t\tID3D11Device_CreateRenderTargetView(pD3DDev11, textures[i]->ptr, NULL, &rtv[i]);\n\t}\n\n\tif (usedepth)\n\t{\n\t\tif (*r_refdef.rt_depth.texname)\n\t\t\tdepth = R2D_RT_GetTexture(r_refdef.rt_depth.texname, &width, &height);\n\t\telse\n\t\t\tdepth = R2D_RT_Configure(\"depth\", width, height, PTI_DEPTH24, RT_IMAGEFLAGS);\n\t}\n\telse\n\t\tdepth = NULL;\n\tif (depth && depth->ptr)\n\t{\n\t\tif (depth->ptr2)\n\t\t{\n\t\t\tID3D11DepthStencilView_Release((ID3D11DepthStencilView*)depth->ptr2);\n\t\t\tdepth->ptr2 = NULL;\n\t\t}\n\t\tID3D11Device_CreateDepthStencilView(pD3DDev11, depth->ptr, NULL, &dsv);\n\t}\n\telse\n\t\tdsv = NULL;\n\n\tID3D11DeviceContext_OMSetRenderTargets(d3ddevctx, i, rtv, dsv);\n\tfor (i = 0; i < sizeof(textures)/sizeof(textures[0]); i++)\n\t\tif (rtv[i])\n\t\t\tID3D11RenderTargetView_Release(rtv[i]);\n\tif (dsv)\n\t{\n\t\tID3D11DeviceContext_ClearDepthStencilView(d3ddevctx, dsv, D3D11_CLEAR_DEPTH, 1, 0);\t//is it faster to clear the stencil too?\n\t\tID3D11DepthStencilView_Release(dsv);\n\t}\n}\n\n\n#ifndef WINRT\t//winrt crap has its own non-hwnd window crap, after years of microsoft forcing everyone to use hwnds for everything. I wonder why they don't have that many winrt apps.\nstatic void D3DVID_UpdateWindowStatus (HWND hWnd)\n{\n\tPOINT p;\n\tRECT nr;\n\tint window_x, window_y;\n\tint window_width, window_height;\n\tGetClientRect(hWnd, &nr);\n\n//\tSys_Printf(\"Update: %i %i %i %i\\n\", nr.left, nr.top, nr.right, nr.bottom);\n\n\t//if its bad then we're probably minimised\n\tif (nr.right <= nr.left)\n\t\treturn;\n\tif (nr.bottom <= nr.top)\n\t\treturn;\n\n\tp.x = 0;\n\tp.y = 0;\n\tClientToScreen(hWnd, &p);\n\twindow_x = p.x;\n\twindow_y = p.y;\n\twindow_width = nr.right - nr.left;\n\twindow_height = nr.bottom - nr.top;\n//\tvid.pixelwidth = window_width;\n//\tvid.pixelheight = window_height;\n\n\twindow_rect.left = window_x;\n\twindow_rect.top = window_y;\n\twindow_rect.right = window_x + window_width;\n\twindow_rect.bottom = window_y + window_height;\n\twindow_center_x = (window_rect.left + window_rect.right) / 2;\n\twindow_center_y = (window_rect.top + window_rect.bottom) / 2;\n\n//\tSys_Printf(\"Window: %i %i %i %i\\n\", window_x, window_y, window_width, window_height);\n\n\n\tINS_UpdateClipCursor ();\n}\n\nstatic qboolean D3D11AppActivate(BOOL fActive, BOOL minimize)\n/****************************************************************************\n*\n* Function:     AppActivate\n* Parameters:   fActive - True if app is activating\n*\n* Description:  If the application is activating, then swap the system\n*               into SYSPAL_NOSTATIC mode so that our palettes will display\n*               correctly.\n*\n****************************************************************************/\n{\n\tstatic BOOL\tsound_active;\n\n\tif (vid.activeapp == fActive && Minimized == minimize)\n\t\treturn false;\t//so windows doesn't crash us over and over again.\n\n\tvid.activeapp = fActive;\n\tMinimized = minimize;\n\n// enable/disable sound on focus gain/loss\n\tif (!vid.activeapp && sound_active)\n\t{\n\t\tS_BlockSound ();\n\t\tsound_active = false;\n\t}\n\telse if (vid.activeapp && !sound_active)\n\t{\n\t\tS_UnblockSound ();\n\t\tsound_active = true;\n\t}\n\n\tINS_UpdateGrabs(modestate != MS_WINDOWED, vid.activeapp);\n\n\treturn true;\n}\n\n#ifndef FUCKDXGI\nstatic void D3D11_DoResize(void)\n{\n\td3d_resized = true;\n\n\tD3DVID_UpdateWindowStatus(mainwindow);\n\n\tif (d3dscreen)\n\t{\t//seriously? this is disgusting.\n\t\tDXGI_OUTPUT_DESC desc;\n\t\tIDXGIOutput_GetDesc(d3dscreen, &desc);\n\t\tvid.pixelwidth = desc.DesktopCoordinates.right - desc.DesktopCoordinates.left;\n\t\tvid.pixelheight = desc.DesktopCoordinates.bottom - desc.DesktopCoordinates.top;\n\t}\n\telse\n\t{\n\t\tvid.pixelwidth = window_rect.right - window_rect.left;\n\t\tvid.pixelheight = window_rect.bottom - window_rect.top;\n\t}\n//\tCon_Printf(\"Resizing buffer to %i*%i\\n\", vid.pixelwidth, vid.pixelheight);\n\treleased3dbackbuffer();\n\tIDXGISwapChain_ResizeBuffers(d3dswapchain, 0, vid.pixelwidth, vid.pixelheight, DXGI_FORMAT_UNKNOWN, DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH);\n\n\tD3D11BE_Reset(true);\n\tresetd3dbackbuffer(vid.pixelwidth, vid.pixelheight);\n\tD3D11BE_Reset(false);\n}\n#endif\n\nstatic void ClearAllStates (void)\n{\n\tint\t\ti;\n\n// send an up event for each key, to make sure the server clears them all\n\tfor (i=0 ; i<256 ; i++)\n\t{\n\t\tKey_Event (0, i, 0, false);\n\t}\n\n\tKey_ClearStates ();\n\tINS_ClearStates ();\n}\n\nstatic LRESULT WINAPI D3D11_WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)\n{\n\tLONG    lRet = 0;\n\tint\t\ttemp;\n\textern unsigned int uiWheelMessage;\n#ifndef FUCKDXGI\n\textern qboolean\tkeydown[K_MAX];\n#endif\n\n\tif ( uMsg == uiWheelMessage )\n\t\tuMsg = WM_MOUSEWHEEL;\n\n\tswitch (uMsg)\n\t{\n#if 1\n\t\tcase WM_KILLFOCUS:\n\t\t\tif (modestate == MS_FULLWINDOW)\n\t\t\t\tShowWindow(mainwindow, SW_SHOWMINNOACTIVE);\n\t\t\tD3D11AppActivate(false, false);\n\t\t\tbreak;\n\n\t\tcase WM_SETFOCUS:\n\t\t\tif (modestate == MS_FULLWINDOW)\n\t\t\t\tShowWindow(mainwindow, SW_SHOWMAXIMIZED);\n\t\t\tD3D11AppActivate(true, false);\n\n\t\t\tif (modestate == MS_FULLSCREEN && d3dswapchain)\n\t\t\t\tIDXGISwapChain_SetFullscreenState(d3dswapchain, vid.activeapp, (vid.activeapp)?d3dscreen:NULL);\n\t\t\tCvar_ForceCallback(&v_gamma);\n\n\t\t\tClearAllStates ();\n\t\t\tbreak;\n\n//\t\tcase WM_CREATE:\n//\t\t\tbreak;\n\n\t\tcase WM_MOVE:\n\t\t\tD3DVID_UpdateWindowStatus (hWnd);\n\t\t\tlRet = DefWindowProc (hWnd, uMsg, wParam, lParam);\n\t\t\tbreak;\n\n\t\tcase WM_KEYDOWN:\n\t\tcase WM_SYSKEYDOWN:\n#if 0\n\t\t\tif (keydown[K_LALT] && wParam == '\\r')\n\t\t\t{\n\t\t\t\tif (d3dscreen)\n\t\t\t\t{\n\t\t\t\t\tIDXGIOutput_Release(d3dscreen);\n\t\t\t\t\td3dscreen = NULL;\n\t\t\t\t}\n\n\t\t\t\tif (modestate == MS_FULLSCREEN)\n\t\t\t\t\tmodestate = MS_WINDOWED;\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tRECT rect;\n\t\t\t\t\textern cvar_t vid_width, vid_height;\n\t\t\t\t\tint width = vid_width.ival;\n\t\t\t\t\tint height = vid_height.ival;\n\t\t\t\t\tif (!width || !height)\n\t\t\t\t\t{\n\t\t\t\t\t\tDXGI_OUTPUT_DESC desc;\n\t\t\t\t\t\tIDXGISwapChain_GetContainingOutput(d3dswapchain, &d3dscreen);\n\t\t\t\t\t\tIDXGIOutput_GetDesc(d3dscreen, &desc);\n\t\t\t\t\t\trect = desc.DesktopCoordinates;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\trect.left = (GetSystemMetrics(SM_CXSCREEN) - width) / 2;\n\t\t\t\t\t\trect.top = (GetSystemMetrics(SM_CYSCREEN) - height) / 2;\n\t\t\t\t\t\trect.right = rect.left+width;\n\t\t\t\t\t\trect.bottom = rect.top+height;\n\t\t\t\t\t}\n\t\t\t\t\tAdjustWindowRectEx(&rect, WS_OVERLAPPED, FALSE, 0);\n\t\t\t\t\tSetWindowPos(hWnd, NULL, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, SWP_SHOWWINDOW|SWP_FRAMECHANGED);\n\t\t\t\t\tmodestate = MS_FULLSCREEN;\n\t\t\t\t}\n\n\t\t\t\tif (!d3dscreen && modestate == MS_FULLSCREEN)\n\t\t\t\t\tIDXGISwapChain_GetContainingOutput(d3dswapchain, &d3dscreen);\n\t\t\t\tIDXGISwapChain_SetFullscreenState(d3dswapchain, modestate == MS_FULLSCREEN, (modestate == MS_FULLSCREEN)?d3dscreen:NULL);\n\n\t\t\t\tif (modestate == MS_WINDOWED)\n\t\t\t\t{\n\t\t\t\t\tRECT rect;\n\t\t\t\t\tint width = 640;\n\t\t\t\t\tint height = 480;\n\t\t\t\t\trect.left = (GetSystemMetrics(SM_CXSCREEN) - width) / 2;\n\t\t\t\t\trect.top = (GetSystemMetrics(SM_CYSCREEN) - height) / 2;\n\t\t\t\t\trect.right = rect.left+width;\n\t\t\t\t\trect.bottom = rect.top+height;\n\t\t\t\t\tAdjustWindowRectEx(&rect, WS_OVERLAPPEDWINDOW, FALSE, 0);\n\t\t\t\t\tSetWindowLong(hWnd, GWL_STYLE, WS_OVERLAPPEDWINDOW);\t//make sure dxgi didn't break us.\n\t\t\t\t\tSetWindowPos(hWnd, HWND_TOP, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, SWP_SHOWWINDOW|SWP_FRAMECHANGED);\n\t\t\t\t\tSetForegroundWindow(hWnd);\n\t\t\t\t\tSetFocus(hWnd);\n\n\t\t\t\t\t//work around a windows bug by forcing all windows to be repainted.\n\t\t\t\t\tInvalidateRect(NULL, NULL, false);\n\t\t\t\t}\n\t\t\t\tD3D11_DoResize();\n\t\t\t\tCvar_ForceCallback(&v_gamma);\n\t\t\t}\n\t\t\telse\n#endif\n\t\t\tif (!vid_initializing)\n\t\t\t\tINS_TranslateKeyEvent (wParam, lParam, true, 0, false);\n\t\t\tbreak;\n\n\t\tcase WM_KEYUP:\n\t\tcase WM_SYSKEYUP:\n\t\t\tif (!vid_initializing)\n\t\t\t\tINS_TranslateKeyEvent (wParam, lParam, false, 0, false);\n\t\t\tbreak;\n\n\t\tcase WM_APPCOMMAND:\n\t\t\tlRet = INS_AppCommand(lParam);\n\t\t\tbreak;\n\n\t\tcase WM_SYSCHAR:\n\t\t// keep Alt-Space from happening\n\t\t\tbreak;\n\n\t// this is complicated because Win32 seems to pack multiple mouse events into\n\t// one update sometimes, so we always check all states and look for events\n\t\tcase WM_LBUTTONDOWN:\n\t\tcase WM_LBUTTONUP:\n\t\tcase WM_RBUTTONDOWN:\n\t\tcase WM_RBUTTONUP:\n\t\tcase WM_MBUTTONDOWN:\n\t\tcase WM_MBUTTONUP:\n\t\tcase WM_MOUSEMOVE:\n\t\tcase WM_XBUTTONDOWN:\n\t\tcase WM_XBUTTONUP:\n\t\t\ttemp = 0;\n\n\t\t\tif (wParam & MK_LBUTTON)\n\t\t\t\ttemp |= 1;\n\n\t\t\tif (wParam & MK_RBUTTON)\n\t\t\t\ttemp |= 2;\n\n\t\t\tif (wParam & MK_MBUTTON)\n\t\t\t\ttemp |= 4;\n\n\t\t\tif (wParam & MK_XBUTTON1)\n\t\t\t\ttemp |= 8;\n\n\t\t\tif (wParam & MK_XBUTTON2)\n\t\t\t\ttemp |= 16;\n\n\t\t\tif (wParam & MK_XBUTTON3)\n\t\t\t\ttemp |= 32;\n\n\t\t\tif (wParam & MK_XBUTTON4)\n\t\t\t\ttemp |= 64;\n\n\t\t\tif (wParam & MK_XBUTTON5)\n\t\t\t\ttemp |= 128;\n\n\t\t\tif (wParam & MK_XBUTTON6)\n\t\t\t\ttemp |= 256;\n\n\t\t\tif (wParam & MK_XBUTTON7)\n\t\t\t\ttemp |= 512;\n\n\t\t\tif (!vid_initializing)\n\t\t\t\tINS_MouseEvent (temp);\n\n\t\t\tbreak;\n\n\t\t// JACK: This is the mouse wheel with the Intellimouse\n\t\t// Its delta is either positive or neg, and we generate the proper\n\t\t// Event.\n\t\tcase WM_MOUSEWHEEL:\n\t\t\tif (!vid_initializing)\n\t\t\t{\n\t\t\t\tif ((short) HIWORD(wParam) > 0)\n\t\t\t\t{\n\t\t\t\t\tKey_Event(0, K_MWHEELUP, 0, true);\n\t\t\t\t\tKey_Event(0, K_MWHEELUP, 0, false);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tKey_Event(0, K_MWHEELDOWN, 0, true);\n\t\t\t\t\tKey_Event(0, K_MWHEELDOWN, 0, false);\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase WM_INPUT:\n\t\t\t// raw input handling\n\t\t\tif (!vid_initializing)\n\t\t\t\tINS_RawInput_Read((HANDLE)lParam);\n\t\t\tbreak;\n\t\tcase WM_DEVICECHANGE:\n\t\t\tCOM_AddWork(WG_MAIN, INS_DeviceChanged, NULL, NULL, uMsg, 0);\n\t\t\tlRet = TRUE;\n\t\t\tbreak;\n\n\t\tcase WM_SETCURSOR:\n\t\t\t//only use a custom cursor if the cursor is inside the client area\n\t\t\tswitch(lParam&0xffff)\n\t\t\t{\n\t\t\tcase 0:\n\t\t\t\tbreak;\n\t\t\tcase HTCLIENT:\n\t\t\t\tif (hCustomCursor)\t//custom cursor enabled\n\t\t\t\t\tSetCursor(hCustomCursor);\n\t\t\t\telse\t\t\t\t//fallback on an arrow cursor, just so we have something visible at startup or so\n\t\t\t\t\tSetCursor(hArrowCursor);\n\t\t\t\tlRet = TRUE;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tlRet = DefWindowProcW (hWnd, uMsg, wParam, lParam);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase WM_GETMINMAXINFO:\n\t\t\t{\n\t\t\t\tRECT windowrect;\n\t\t\t\tRECT clientrect;\n\t\t\t\tMINMAXINFO *mmi = (MINMAXINFO *) lParam;\n\n\t\t\t\tGetWindowRect (hWnd, &windowrect);\n\t\t\t\tGetClientRect (hWnd, &clientrect);\n\n\t\t\t\tmmi->ptMinTrackSize.x = 320 + ((windowrect.right - windowrect.left) - (clientrect.right - clientrect.left));\n\t\t\t\tmmi->ptMinTrackSize.y = 200 + ((windowrect.bottom - windowrect.top) - (clientrect.bottom - clientrect.top));\n\t\t\t}\n\t\t\treturn 0;\n\t\tcase WM_SIZE:\n\t\t\tif (d3dswapchain)\n\t\t\t{\n\t\t\t\td3d_resized = true;\n\n\t\t\t\tD3DVID_UpdateWindowStatus(mainwindow);\n\n\t\t\t\treleased3dbackbuffer();\n\t\t\t\tIDXGISwapChain_ResizeBuffers(d3dswapchain, 0, 0, 0, DXGI_FORMAT_UNKNOWN, DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH);\n\n\t\t\t\tD3D11BE_Reset(true);\n\t\t\t\tvid.pixelwidth = window_rect.right - window_rect.left;\n\t\t\t\tvid.pixelheight = window_rect.bottom - window_rect.top;\n\t\t\t\tresetd3dbackbuffer(vid.pixelwidth, vid.pixelheight);\n\t\t\t\tD3D11BE_Reset(false);\n\t\t\t}\n\t\t\tlRet = DefWindowProc(hWnd, uMsg, wParam, lParam);\n\t\t\tbreak;\n\n\t\tcase WM_CLOSE:\n\t\t\tif (!vid_initializing)\n\t\t\t\tif (MessageBox (mainwindow, \"Are you sure you want to quit?\", \"Confirm Exit\",\n\t\t\t\t\t\t\tMB_YESNO | MB_SETFOREGROUND | MB_ICONQUESTION) == IDYES)\n\t\t\t\t{\n\t\t\t\t\tCbuf_AddText(\"\\nquit\\n\", RESTRICT_LOCAL);\n\t\t\t\t}\n\n\t\t\tbreak;\n\n\t\tcase WM_DESTROY:\n\t\t{\n//\t\t\tif (dibwindow)\n//\t\t\t\tDestroyWindow (dibwindow);\n\t\t}\n\t\tbreak;\n#ifdef HAVE_CDPLAYER\n\t\tcase MM_MCINOTIFY:\n\t\t\tlRet = CDAudio_MessageHandler (hWnd, uMsg, wParam, lParam);\n\t\t\tbreak;\n#endif\n#endif\n\t\tcase WM_ERASEBKGND:\n\t\t\treturn 1;\n\t\tdefault:\n\t\t\t/* pass all unhandled messages to DefWindowProc */\n\t\t\tlRet = DefWindowProc (hWnd, uMsg, wParam, lParam);\n\t\tbreak;\n\t}\n\n\t/* return 1 if handled message, 0 if not */\n\treturn lRet;\n}\n#endif\n\n#if (WINVER < 0x500) && !defined(__GNUC__)\ntypedef struct tagMONITORINFO\n{\n\tDWORD   cbSize;\n\tRECT    rcMonitor;\n\tRECT    rcWork;\n\tDWORD   dwFlags;\n} MONITORINFO, *LPMONITORINFO;\n#endif\n\nstatic void released3dbackbuffer(void)\n{\n\tif (d3ddevctx)\n\t\tID3D11DeviceContext_OMSetRenderTargets(d3ddevctx, 0, NULL, NULL);\n\tif (fb_backbuffer)\n\t\tID3D11RenderTargetView_Release(fb_backbuffer);\n\tfb_backbuffer = NULL;\n\tif (fb_backdepthstencil)\n\t\tID3D11DepthStencilView_Release(fb_backdepthstencil);\n\tfb_backdepthstencil = NULL;\n}\n\nstatic qboolean resetd3dbackbuffer(int width, int height)\n{\n\tD3D11_TEXTURE2D_DESC t2ddesc;\n//\tD3D11_DEPTH_STENCIL_VIEW_DESC dsvd;\n\tID3D11Texture2D *backbuftex, *depthtex;\n\n\treleased3dbackbuffer();\n\n\t//get a proper handle to the backbuffer (silly hurdles)\n\tif (FAILED(IDXGISwapChain_GetBuffer(d3dswapchain, 0, &qIID_ID3D11Texture2D, (LPVOID*)&backbuftex)))\n\t\treturn false;\n\tif (FAILED(ID3D11Device_CreateRenderTargetView(pD3DDev11, (ID3D11Resource*)backbuftex, NULL, &fb_backbuffer)))\n\t\treturn false;\n\tID3D11Texture2D_Release(backbuftex);\n\n\t//set up a depth buffer.\n\tmemset(&t2ddesc, 0, sizeof(t2ddesc));\n\tt2ddesc.Width = width;\n\tt2ddesc.Height = height;\n\tt2ddesc.MipLevels = 1;\n\tt2ddesc.ArraySize = 1;\n\tt2ddesc.Format = depthformat;\n\tt2ddesc.SampleDesc.Count = d3d11multisample_count;\n\tt2ddesc.SampleDesc.Quality = d3d11multisample_quality;\n\tt2ddesc.Usage = D3D11_USAGE_DEFAULT;\n\tt2ddesc.BindFlags = D3D11_BIND_DEPTH_STENCIL;\n\tt2ddesc.CPUAccessFlags = 0;\n\tt2ddesc.MiscFlags = 0;\n\tif(FAILED(ID3D11Device_CreateTexture2D(pD3DDev11, &t2ddesc, NULL, &depthtex)))\n\t\treturn false;\n//\tdsvd.Format = t2ddesc.Format;\n//\tdsvd.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D;\n//\tdsvd.Texture2D.MipSlice = 0;\n\tif(FAILED(ID3D11Device_CreateDepthStencilView(pD3DDev11, (ID3D11Resource*)depthtex, NULL/*&dsvd*/, &fb_backdepthstencil)))\n\t\treturn false;\n\tID3D11Texture2D_Release(depthtex);\n\n\t//now tell d3d which render targets to use.\n\tID3D11DeviceContext_OMSetRenderTargets(d3ddevctx, 1, &fb_backbuffer, fb_backdepthstencil);\n\n\treturn true;\n}\n\n#ifdef WINRT\t//winrt crap has its own non-hwnd window crap, after years of microsoft forcing everyone to use hwnds for everything. I wonder why they don't have that many winrt apps.\nvoid D3D11_DoResize(int newwidth, int newheight)\n{\n\td3d_resized = true;\n\n//\tCon_Printf(\"Resizing buffer to %i*%i\\n\", vid.pixelwidth, vid.pixelheight);\n\treleased3dbackbuffer();\n\tif (d3dswapchain)\n\t{\n\t\tIDXGISwapChain_ResizeBuffers(d3dswapchain, 0, vid.pixelwidth, vid.pixelheight, DXGI_FORMAT_UNKNOWN, DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH);\n\n\t\tD3D11BE_Reset(true);\n\t\tresetd3dbackbuffer(vid.pixelwidth, vid.pixelheight);\n\t\tD3D11BE_Reset(false);\n\t}\n}\nvoid *RT_GetCoreWindow(int *width, int *height);\nstatic qboolean D3D11_VID_Init(rendererstate_t *info, unsigned char *palette)\n{\n\tstatic IID factiid1 = {0x770aae78, 0xf26f, 0x4dba, 0xa8, 0x29, 0x25, 0x3c, 0x83, 0xd1, 0xb3, 0x87};\n\tstatic IID factiid2 = {0x50c83a1c, 0xe072, 0x4c48, 0x87, 0xb0, 0x36, 0x30, 0xfa, 0x36, 0xa6, 0xd0};\n\tIDXGIFactory2 *fact = NULL;\n\tHRESULT hr;\n\tD3D_FEATURE_LEVEL flevel, flevels[] =\n\t{\n\t\t//D3D_FEATURE_LEVEL_11_1,\n\t\tD3D_FEATURE_LEVEL_11_0,\n\t\tD3D_FEATURE_LEVEL_10_1,\n\t\tD3D_FEATURE_LEVEL_10_0,\n\n\t\tD3D_FEATURE_LEVEL_9_3,\n\t\tD3D_FEATURE_LEVEL_9_2,\n\t\tD3D_FEATURE_LEVEL_9_1\n\t};\n\tDXGI_SWAP_CHAIN_DESC1 scd = {0};\n\n\tIUnknown *window = RT_GetCoreWindow(&info->width, &info->height);\n\n\tmodestate = MS_FULLSCREEN;\n\n\tif (info->depthbits == 16)\n\t\tdepthformat = DXGI_FORMAT_D16_UNORM;\n\telse if (info->depthbits == 32)\n\t\tdepthformat = DXGI_FORMAT_D32_FLOAT;\n\telse\n\t\tdepthformat = DXGI_FORMAT_D24_UNORM_S8_UINT;\n\n\t//fill scd\n\tscd.Width = info->width;\n\tscd.Height = info->height;\n\tscd.Format = info->srgb?DXGI_FORMAT_R8G8B8A8_UNORM_SRGB:DXGI_FORMAT_B8G8R8A8_UNORM;\n\tscd.Stereo = info->stereo;\n\tscd.SampleDesc.Count = d3d11multisample_count = max(1,info->multisample);\n\tscd.SampleDesc.Quality = d3d11multisample_quality = (d3d11multisample_count>1)?~0:0;\n\tscd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;\n\tscd.BufferCount = 2+info->triplebuffer;\t//rt only supports fullscreen, so the frontbuffer needs to be created by us.\n\tscd.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;\n\tscd.Flags = 0;\n\n\t//create d3d stuff\n\thr = CreateDXGIFactory1(&factiid2, &fact);\n\tif (FAILED(D3D11CreateDevice(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, 0, flevels, sizeof(flevels)/sizeof(flevels[0]), D3D11_SDK_VERSION, &pD3DDev11, &flevel, &d3ddevctx)))\n\t\tSys_Error(\"D3D11CreateDevice failed\\n\");\n\telse\n\t{\n\t\tif (FAILED(IDXGIFactory2_CreateSwapChainForCoreWindow(fact, (IUnknown*)pD3DDev11, window, &scd, NULL, &d3dswapchain)))\n\t\t\tSys_Error(\"IDXGIFactory2_CreateSwapChainForCoreWindow failed\\n\");\n\t\telse\n\t\t{\n\t\t\tvid.numpages = scd.BufferCount;\n\t\t\tif (!resetd3dbackbuffer(info->width, info->height))\n\t\t\t\tSys_Error(\"unable to reset back buffer\\n\");\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (!D3D11Shader_Init(flevel))\n\t\t\t\t\tCon_Printf(\"Unable to intialise a suitable HLSL compiler, please install the DirectX runtime.\\n\");\n\t\t\t\telse\n\t\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t}\n\treturn false;\n}\n#else\nstatic qboolean initD3D11Device(HWND hWnd, rendererstate_t *info, PFN_D3D11_CREATE_DEVICE_AND_SWAP_CHAIN func, IDXGIAdapter *adapt)\n{\n\tUINT support;\n\tint flags = 0;//= D3D11_CREATE_DEVICE_SINGLETHREADED;\n\tD3D_DRIVER_TYPE drivertype;\n\tDXGI_SWAP_CHAIN_DESC scd;\n\tD3D_FEATURE_LEVEL flevel, flevels[] =\n\t{\n\t\t//D3D_FEATURE_LEVEL_11_1,\n\t\tD3D_FEATURE_LEVEL_11_0,\n\t\tD3D_FEATURE_LEVEL_10_1,\n\t\tD3D_FEATURE_LEVEL_10_0,\n\n\t\t//FIXME: need npot.\n\t\tD3D_FEATURE_LEVEL_9_3,\n\t\tD3D_FEATURE_LEVEL_9_2,\n\t\tD3D_FEATURE_LEVEL_9_1\n\t};\n\tmemset(&scd, 0, sizeof(scd));\n\n\tif (!stricmp(info->subrenderer, \"debug\"))\n\t\tflags |= D3D11_CREATE_DEVICE_DEBUG;\n\n\tif (!stricmp(info->subrenderer, \"warp\"))\n\t\tdrivertype = D3D_DRIVER_TYPE_WARP;\n\telse if (!stricmp(info->subrenderer, \"ref\"))\n\t\tdrivertype = D3D_DRIVER_TYPE_REFERENCE;\n\telse if (!stricmp(info->subrenderer, \"hw\"))\n\t\tdrivertype = D3D_DRIVER_TYPE_HARDWARE;\n\telse if (!stricmp(info->subrenderer, \"null\"))\n\t\tdrivertype = D3D_DRIVER_TYPE_NULL;\n\telse if (!stricmp(info->subrenderer, \"software\"))\n\t\tdrivertype = D3D_DRIVER_TYPE_SOFTWARE;\n\telse if (!stricmp(info->subrenderer, \"unknown\"))\n\t\tdrivertype = D3D_DRIVER_TYPE_UNKNOWN;\n\telse\n\t\tdrivertype = adapt?D3D_DRIVER_TYPE_UNKNOWN:D3D_DRIVER_TYPE_HARDWARE;\n\n\tif (info->depthbits == 16)\n\t\tdepthformat = DXGI_FORMAT_D16_UNORM;\n\telse if (info->depthbits == 32)\n\t\tdepthformat = DXGI_FORMAT_D32_FLOAT;\n\telse\n\t\tdepthformat = DXGI_FORMAT_D24_UNORM_S8_UINT;\n\n\t//for stereo support, we would have to rewrite all of this in a way that would make us dependant upon windows 8 or 7+platform update, which would exclude vista.\n\tscd.BufferDesc.Width = info->width;\n\tscd.BufferDesc.Height = info->height;\n\tscd.BufferDesc.RefreshRate.Numerator = 0;\n\tscd.BufferDesc.RefreshRate.Denominator = 0;\n\tscd.BufferCount = 1+info->triplebuffer;\t//back buffer count\n\tif (info->srgb)\n\t{\n\t\tif (info->srgb >= 2)\t//fixme: detect properly.\n\t\t\tscd.BufferDesc.Format = DXGI_FORMAT_R16G16B16A16_FLOAT;\t//on nvidia, outputs linear rgb to srgb devices, which means info->srgb is effectively set\n\t\telse\n\t\t\tscd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM_SRGB;\n\t}\n\telse if (info->bpp == 30)\t//fixme: detect properly.\n\t\tscd.BufferDesc.Format = DXGI_FORMAT_R10G10B10A2_UNORM;\n\telse\n\t\tscd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;\n\tscd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;\n\tscd.OutputWindow = hWnd;\n\tscd.SampleDesc.Count = d3d11multisample_count = max(1, info->multisample);\t//as we're starting up windowed (and switching to fullscreen after), the frontbuffer is handled by windows.\n\tscd.SampleDesc.Quality = d3d11multisample_quality = 0;\n\tscd.Windowed = TRUE;\n\tscd.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;// | DXGI_SWAP_CHAIN_FLAG_NONPREROTATED;\n\n#ifdef _DEBUG\n//\tflags |= D3D11_CREATE_DEVICE_DEBUG;\n#endif\n\n\tif (drivertype == D3D_DRIVER_TYPE_UNKNOWN && adapt)\n\t{\n\t\tDXGI_ADAPTER_DESC adesc;\n\t\tIDXGIAdapter_GetDesc(adapt, &adesc);\n\t\tCon_Printf(\"D3D11 Adaptor: %S\\n\", adesc.Description);\n\t}\n\telse\n\t{\n\t\tadapt = NULL;\n\t\tCon_Printf(\"D3D11 Adaptor: %s\\n\", \"Unspecified\");\n\t}\n\n\tif (FAILED(func(adapt, drivertype, NULL, flags,\n\t\t\t\tflevels, sizeof(flevels)/sizeof(flevels[0]),\n\t\t\t\tD3D11_SDK_VERSION,\n\t\t\t\t&scd,\n\t\t\t\t&d3dswapchain,\n\t\t\t\t&pD3DDev11,\n\t\t\t\t&flevel,\n\t\t\t\t&d3ddevctx)))\n\t\treturn false;\n\n\tif (!pD3DDev11)\n\t\treturn false;\n\n\tCon_Printf(\"D3D11 Feature level: %i_%i\\n\", flevel>>12, (flevel>>8) & 0xf);\n\n\tif (!resetd3dbackbuffer(info->width, info->height))\n\t\treturn false;\n\n\tif (info->fullscreen)\n\t{\n\t}\n\n\tmemset(&sh_config, 0, sizeof(sh_config));\n\tsh_config.texture_non_power_of_two = flevel>=D3D_FEATURE_LEVEL_10_0;\t//npot MUST be supported on all d3d10+ cards.\n\tsh_config.texture_non_power_of_two_pic = true;\t//always supported in d3d11, supposedly, even with d3d9 devices.\n\tsh_config.texture_allow_block_padding = false;\t//microsoft blocks this.\n\tsh_config.npot_rounddown = false;\n\tif (flevel>=D3D_FEATURE_LEVEL_11_0)\n\t\tsh_config.texture2d_maxsize = 16384;\n\telse if (flevel>=D3D_FEATURE_LEVEL_10_0)\n\t\tsh_config.texture2d_maxsize = 8192;\n\telse if (flevel>=D3D_FEATURE_LEVEL_9_3)\n\t\tsh_config.texture2d_maxsize = 4096;\n\telse\n\t\tsh_config.texture2d_maxsize = 2048;\n\n\tif (flevel>=D3D_FEATURE_LEVEL_9_3)\n\t\tsh_config.texture2d_maxsize = 4096;\n\telse\n\t\tsh_config.texture2d_maxsize = 512;\n\n//11.1 formats\n#define DXGI_FORMAT_B4G4R4A4_UNORM 115\n\n\t//why does d3d11 have no rgbx format? anyone else think that weird?\n\n\tif (SUCCEEDED(ID3D11Device_CheckFormatSupport(pD3DDev11, DXGI_FORMAT_B5G6R5_UNORM, &support)))\t\t\t//crippled to win8+ only.\n\t\tsh_config.texfmt[PTI_RGB565] = !!(support & D3D11_FORMAT_SUPPORT_TEXTURE2D);\n\tif (SUCCEEDED(ID3D11Device_CheckFormatSupport(pD3DDev11, DXGI_FORMAT_B5G5R5A1_UNORM, &support)))\t\t//crippled to win8+ only.\n\t\tsh_config.texfmt[PTI_ARGB1555] = !!(support & D3D11_FORMAT_SUPPORT_TEXTURE2D);\n\tif (SUCCEEDED(ID3D11Device_CheckFormatSupport(pD3DDev11, DXGI_FORMAT_B4G4R4A4_UNORM, &support)))\t\t//crippled to win8+ only.\n\t\tsh_config.texfmt[PTI_ARGB4444] = !!(support & D3D11_FORMAT_SUPPORT_TEXTURE2D);\n\n\tif (SUCCEEDED(ID3D11Device_CheckFormatSupport(pD3DDev11, DXGI_FORMAT_R8G8B8A8_UNORM, &support)))\n\t\tsh_config.texfmt[PTI_RGBA8] = !!(support & D3D11_FORMAT_SUPPORT_TEXTURE2D);\n\tif (SUCCEEDED(ID3D11Device_CheckFormatSupport(pD3DDev11, DXGI_FORMAT_R8G8B8A8_UNORM_SRGB, &support)))\n\t\tsh_config.texfmt[PTI_RGBA8_SRGB] = !!(support & D3D11_FORMAT_SUPPORT_TEXTURE2D);\n\tif (SUCCEEDED(ID3D11Device_CheckFormatSupport(pD3DDev11, DXGI_FORMAT_R10G10B10A2_UNORM, &support)))\n\t\tsh_config.texfmt[PTI_A2BGR10] = !!(support & D3D11_FORMAT_SUPPORT_TEXTURE2D) && !!(support & D3D11_FORMAT_SUPPORT_RENDER_TARGET);\n\tif (SUCCEEDED(ID3D11Device_CheckFormatSupport(pD3DDev11, DXGI_FORMAT_R9G9B9E5_SHAREDEXP, &support)))\n\t\tsh_config.texfmt[PTI_E5BGR9] = !!(support & D3D11_FORMAT_SUPPORT_TEXTURE2D);\n\tif (SUCCEEDED(ID3D11Device_CheckFormatSupport(pD3DDev11, DXGI_FORMAT_R11G11B10_FLOAT, &support)))\n\t\tsh_config.texfmt[PTI_B10G11R11F] = !!(support & D3D11_FORMAT_SUPPORT_TEXTURE2D);\n\tif (SUCCEEDED(ID3D11Device_CheckFormatSupport(pD3DDev11, DXGI_FORMAT_R16G16B16A16_FLOAT, &support)))\n\t\tsh_config.texfmt[PTI_RGBA16F] = !!(support & D3D11_FORMAT_SUPPORT_TEXTURE2D) && !!(support & D3D11_FORMAT_SUPPORT_RENDER_TARGET);\n\tif (SUCCEEDED(ID3D11Device_CheckFormatSupport(pD3DDev11, DXGI_FORMAT_R32G32B32A32_FLOAT, &support)))\n\t\tsh_config.texfmt[PTI_RGBA32F] = !!(support & D3D11_FORMAT_SUPPORT_TEXTURE2D) && !!(support & D3D11_FORMAT_SUPPORT_RENDER_TARGET);\n\n\tif (SUCCEEDED(ID3D11Device_CheckFormatSupport(pD3DDev11, DXGI_FORMAT_B8G8R8A8_UNORM, &support)))\n\t\tsh_config.texfmt[PTI_BGRA8] = !!(support & D3D11_FORMAT_SUPPORT_TEXTURE2D);\n\tif (SUCCEEDED(ID3D11Device_CheckFormatSupport(pD3DDev11, DXGI_FORMAT_B8G8R8A8_UNORM_SRGB, &support)))\n\t\tsh_config.texfmt[PTI_BGRA8_SRGB] = !!(support & D3D11_FORMAT_SUPPORT_TEXTURE2D);\n\tif (SUCCEEDED(ID3D11Device_CheckFormatSupport(pD3DDev11, DXGI_FORMAT_B8G8R8X8_UNORM, &support)))\n\t\tsh_config.texfmt[PTI_BGRX8] = !!(support & D3D11_FORMAT_SUPPORT_TEXTURE2D);\n\tif (SUCCEEDED(ID3D11Device_CheckFormatSupport(pD3DDev11, DXGI_FORMAT_B8G8R8X8_UNORM_SRGB, &support)))\n\t\tsh_config.texfmt[PTI_BGRX8_SRGB] = !!(support & D3D11_FORMAT_SUPPORT_TEXTURE2D);\n\n\t//compressed formats\n\tif (SUCCEEDED(ID3D11Device_CheckFormatSupport(pD3DDev11, DXGI_FORMAT_BC1_UNORM, &support)))\n\t\tsh_config.texfmt[PTI_BC1_RGBA] = !!(support & D3D11_FORMAT_SUPPORT_TEXTURE2D);\n\tif (SUCCEEDED(ID3D11Device_CheckFormatSupport(pD3DDev11, DXGI_FORMAT_BC1_UNORM_SRGB, &support)))\n\t\tsh_config.texfmt[PTI_BC1_RGBA_SRGB] = !!(support & D3D11_FORMAT_SUPPORT_TEXTURE2D);\n\tif (SUCCEEDED(ID3D11Device_CheckFormatSupport(pD3DDev11, DXGI_FORMAT_BC2_UNORM, &support)))\n\t\tsh_config.texfmt[PTI_BC2_RGBA] = !!(support & D3D11_FORMAT_SUPPORT_TEXTURE2D);\n\tif (SUCCEEDED(ID3D11Device_CheckFormatSupport(pD3DDev11, DXGI_FORMAT_BC2_UNORM_SRGB, &support)))\n\t\tsh_config.texfmt[PTI_BC2_RGBA_SRGB] = !!(support & D3D11_FORMAT_SUPPORT_TEXTURE2D);\n\tif (SUCCEEDED(ID3D11Device_CheckFormatSupport(pD3DDev11, DXGI_FORMAT_BC3_UNORM, &support)))\n\t\tsh_config.texfmt[PTI_BC3_RGBA] = !!(support & D3D11_FORMAT_SUPPORT_TEXTURE2D);\n\tif (SUCCEEDED(ID3D11Device_CheckFormatSupport(pD3DDev11, DXGI_FORMAT_BC3_UNORM_SRGB, &support)))\n\t\tsh_config.texfmt[PTI_BC3_RGBA_SRGB] = !!(support & D3D11_FORMAT_SUPPORT_TEXTURE2D);\n\tif (SUCCEEDED(ID3D11Device_CheckFormatSupport(pD3DDev11, DXGI_FORMAT_BC4_UNORM, &support)))\n\t\tsh_config.texfmt[PTI_BC4_R] = !!(support & D3D11_FORMAT_SUPPORT_TEXTURE2D);\n\tif (SUCCEEDED(ID3D11Device_CheckFormatSupport(pD3DDev11, DXGI_FORMAT_BC4_SNORM, &support)))\n\t\tsh_config.texfmt[PTI_BC4_R_SNORM] = !!(support & D3D11_FORMAT_SUPPORT_TEXTURE2D);\n\tif (SUCCEEDED(ID3D11Device_CheckFormatSupport(pD3DDev11, DXGI_FORMAT_BC5_UNORM, &support)))\n\t\tsh_config.texfmt[PTI_BC5_RG] = !!(support & D3D11_FORMAT_SUPPORT_TEXTURE2D);\n\tif (SUCCEEDED(ID3D11Device_CheckFormatSupport(pD3DDev11, DXGI_FORMAT_BC5_SNORM, &support)))\n\t\tsh_config.texfmt[PTI_BC5_RG_SNORM] = !!(support & D3D11_FORMAT_SUPPORT_TEXTURE2D);\n\tif (SUCCEEDED(ID3D11Device_CheckFormatSupport(pD3DDev11, DXGI_FORMAT_BC6H_UF16, &support)))\n\t\tsh_config.texfmt[PTI_BC6_RGB_UFLOAT] = !!(support & D3D11_FORMAT_SUPPORT_TEXTURE2D);\n\tif (SUCCEEDED(ID3D11Device_CheckFormatSupport(pD3DDev11, DXGI_FORMAT_BC6H_SF16, &support)))\n\t\tsh_config.texfmt[PTI_BC6_RGB_SFLOAT] = !!(support & D3D11_FORMAT_SUPPORT_TEXTURE2D);\n\tif (SUCCEEDED(ID3D11Device_CheckFormatSupport(pD3DDev11, DXGI_FORMAT_BC7_UNORM, &support)))\n\t\tsh_config.texfmt[PTI_BC7_RGBA] = !!(support & D3D11_FORMAT_SUPPORT_TEXTURE2D);\n\tif (SUCCEEDED(ID3D11Device_CheckFormatSupport(pD3DDev11, DXGI_FORMAT_BC7_UNORM_SRGB, &support)))\n\t\tsh_config.texfmt[PTI_BC7_RGBA_SRGB] = !!(support & D3D11_FORMAT_SUPPORT_TEXTURE2D);\n\n\t//these formats are not officially supported as specified, but noone cares\n\tsh_config.texfmt[PTI_RGBX8] = sh_config.texfmt[PTI_RGBA8];\n\tsh_config.texfmt[PTI_RGBX8_SRGB] = sh_config.texfmt[PTI_RGBA8_SRGB];\n\tsh_config.texfmt[PTI_BC1_RGB] = sh_config.texfmt[PTI_BC1_RGBA];\n\tsh_config.texfmt[PTI_BC1_RGB_SRGB] = sh_config.texfmt[PTI_BC1_RGBA_SRGB];\n\n\tswitch(scd.BufferDesc.Format)\n\t{\n\tcase DXGI_FORMAT_R16G16B16A16_FLOAT:\n\t\tvid.flags |= VID_SRGB_FB_LINEAR|VID_FP16;\t//these are apparently linear already.\n\t\tbreak;\n\tcase DXGI_FORMAT_R8G8B8A8_UNORM_SRGB:\n\tcase DXGI_FORMAT_B8G8R8A8_UNORM_SRGB:\n\tcase DXGI_FORMAT_B8G8R8X8_UNORM_SRGB:\n\t\tvid.flags |= VID_SRGB_FB_LINEAR;\t//effectively linear.\n\t\tbreak;\n\tdefault:\n\t\t//non-linear formats.\n\t\tbreak;\n\t}\n\tif ((vid.flags & VID_SRGB_FB) && info->srgb >= 0)\n\t\tvid.flags |= VID_SRGBAWARE;\n\n\tvid.numpages = scd.BufferCount;\n\tif (!D3D11Shader_Init(flevel))\n\t{\n\t\tCon_Printf(\"Unable to intialise a suitable HLSL compiler, please install the DirectX runtime.\\n\");\n\t\treturn false;\n\t}\n\treturn true;\n}\n\nstatic void initD3D11(HWND hWnd, rendererstate_t *info)\n{\n\tstatic dllhandle_t *dxgi;\n\tstatic PFN_D3D11_CREATE_DEVICE_AND_SWAP_CHAIN fnc;\n\tstatic HRESULT (WINAPI *pCreateDXGIFactory1)(IID *riid, void **ppFactory);\n\tstatic IID factiid = {0x770aae78, 0xf26f, 0x4dba, {0xa8, 0x29, 0x25, 0x3c, 0x83, 0xd1, 0xb3, 0x87}};\n\tIDXGIFactory1 *fact = NULL;\n\tIDXGIAdapter *adapt = NULL;\n\tvrsetup_t vrsetup = {sizeof(vrsetup)};\n\tdllfunction_t d3d11funcs[] =\n\t{\n\t\t{(void**)&fnc, \"D3D11CreateDeviceAndSwapChain\"},\n\t\t{NULL}\n\t};\n\tdllfunction_t dxgifuncs[] =\n\t{\n\t\t{(void**)&pCreateDXGIFactory1, \"CreateDXGIFactory1\"},\n\t\t{NULL}\n\t};\n\n\tif (!dxgi)\n\t\tdxgi = Sys_LoadLibrary(\"dxgi\", dxgifuncs);\n\tif (!d3d11mod)\n\t\td3d11mod = Sys_LoadLibrary(\"d3d11\", d3d11funcs);\n\n\tif (!d3d11mod)\n\t\treturn;\n\n\tvrsetup.vrplatform = VR_D3D11;\n\tif (pCreateDXGIFactory1)\n\t{\n\t\tHRESULT hr;\n\t\thr = pCreateDXGIFactory1(&factiid, (void**)&fact);\n\t\tif (FAILED(hr))\n\t\t\tCon_Printf(\"CreateDXGIFactory1 failed: %s\\n\", D3D_NameForResult(hr));\n\t\tif (fact)\n\t\t{\n\t\t\tULONGLONG devid = strtoull(info->subrenderer, NULL, 16);\n\t\t\tvrsetup.deviceid[0] = (devid    )&0xffffffff;\n\t\t\tvrsetup.deviceid[1] = (devid>>32)&0xffffffff;\n\t\t\tif (info->vr)\n\t\t\t{\n\t\t\t\tif (!info->vr->Prepare(&vrsetup))\n\t\t\t\t{\n\t\t\t\t\tinfo->vr->Shutdown();\n\t\t\t\t\tinfo->vr = NULL;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (vrsetup.deviceid[0] || vrsetup.deviceid[1])\n\t\t\t{\n\t\t\t\tint id = 0;\n\t\t\t\twhile (S_OK==IDXGIFactory1_EnumAdapters(fact, id++, &adapt))\n\t\t\t\t{\n\t\t\t\t\tDXGI_ADAPTER_DESC desc;\n\t\t\t\t\tIDXGIAdapter_GetDesc(adapt, &desc);\n\t\t\t\t\tif (desc.AdapterLuid.LowPart == vrsetup.deviceid[0] && desc.AdapterLuid.HighPart == vrsetup.deviceid[1])\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tIDXGIAdapter_Release(adapt);\n\t\t\t\t\tadapt = NULL;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t\tIDXGIFactory1_EnumAdapters(fact, 0, &adapt);\n\t\t}\n\t}\n\telse\n\t\tinfo->vr = NULL;\t//o.O\n\n\t\n\tinitD3D11Device(hWnd, info, fnc, adapt);\n\n\tif (adapt)\n\t\tIDXGIAdapter_Release(adapt);\n\tif (fact)\n\t{\n\t\t//DXGI SUCKS and fucks up alt+tab every single time. its pointless to go from fullscreen to fullscreen-with-taskbar-obscuring-half-the-window.\n\t\t//I'm just going to handle that stuff myself.\n\t\t//IDXGIFactory1_MakeWindowAssociation(fact, hWnd, DXGI_MWA_NO_WINDOW_CHANGES|DXGI_MWA_NO_ALT_ENTER|DXGI_MWA_NO_PRINT_SCREEN);\n\t\tIDXGIFactory1_Release(fact);\n\t}\n}\n\nstatic qboolean D3D11_VID_EnumerateDevices(void *usercontext, void(*callback)(void *context, const char *devicename, const char *outputname, const char *desc))\n{\n\tstatic dllhandle_t *dxgi;\n\tstatic HRESULT (WINAPI *pCreateDXGIFactory1)(IID *riid, void **ppFactory);\n\tstatic IID factiid = {0x770aae78, 0xf26f, 0x4dba, {0xa8, 0x29, 0x25, 0x3c, 0x83, 0xd1, 0xb3, 0x87}};\n\tIDXGIFactory1 *fact = NULL;\n\tdllfunction_t dxgifuncs[] =\n\t{\n\t\t{(void**)&pCreateDXGIFactory1, \"CreateDXGIFactory1\"},\n\t\t{NULL}\n\t};\n\n\tif (!dxgi)\n\t\tdxgi = Sys_LoadLibrary(\"dxgi\", dxgifuncs);\n\tif (!dxgi)\n\t\treturn true;\n\n\tif (pCreateDXGIFactory1)\n\t{\n\t\tHRESULT hr;\n\t\thr = pCreateDXGIFactory1(&factiid, (void**)&fact);\n\t\tif (FAILED(hr))\n\t\t\tCon_Printf(\"CreateDXGIFactory1 failed: %s\\n\", D3D_NameForResult(hr));\n\t\tif (fact)\n\t\t{\n\t\t\tIDXGIAdapter *adapt = NULL;\n\t\t\tint id = 0;\n\t\t\tchar devname[128*4];\n\t\t\tchar dev[128*4];\n\t\t\tDXGI_ADAPTER_DESC desc;\n\t\t\twhile (S_OK==IDXGIFactory1_EnumAdapters(fact, id++, &adapt))\n\t\t\t{\n\t\t\t\tIDXGIAdapter_GetDesc(adapt, &desc);\n\n\t\t\t\tQ_snprintfz(devname,sizeof(devname), \"Direct3D11 - %s\", narrowen(dev,sizeof(dev), desc.Description));\n\t\t\t\tQ_snprintfz(dev,sizeof(dev), \"%\"PRIx64, ((ULONGLONG)desc.AdapterLuid.HighPart<<32)|desc.AdapterLuid.LowPart);\n\t\t\t\tcallback(usercontext, dev, \"\"/*FIXME*/, devname);\n\n\t\t\t\tIDXGIAdapter_Release(adapt);\n\t\t\t\tadapt = NULL;\n\t\t\t}\n\n\t\t\tIDXGIFactory1_Release(fact);\n\t\t}\n\t}\n\treturn true;\n}\n\nstatic qboolean D3D11_VID_Init(rendererstate_t *info, unsigned char *palette)\n{\n\tDWORD width = info->width;\n\tDWORD height = info->height;\n\t//DWORD bpp = info->bpp;\n\t//DWORD zbpp = 16;\n\t//DWORD flags = 0;\n\tDWORD wstyle;\n\tRECT rect;\n\tMSG msg;\n\tHICON hIcon = LoadIcon (global_hInstance, MAKEINTRESOURCE (IDI_ICON1));\n\n\t//DDGAMMARAMP gammaramp;\n\t//int i;\n\n\tchar *CLASSNAME = \"FTED3D11QUAKE\";\n\tWNDCLASS wc = {\n\t\t0,\n\t\t&D3D11_WindowProc,\n\t\t0,\n\t\t0,\n\t\tNULL,\n\t\thIcon,\n\t\tNULL,\n\t\tNULL,\n\t\tNULL,\n\t\tCLASSNAME\n\t};\n\n\twc.style = CS_HREDRAW | CS_VREDRAW;\n\twc.hCursor   = hArrowCursor = LoadCursor (NULL,IDC_ARROW);\n\twc.hInstance = global_hInstance; \n\n\tvid_initializing = true;\n\n\tRegisterClass(&wc);\n\n\tif (info->fullscreen == 2)\n\t\tmodestate = MS_FULLWINDOW;\n\telse if (info->fullscreen)\n\t\tmodestate = MS_FULLSCREEN;\t//FIXME: I'm done with fighting dxgi. I'm just going to pick the easy method that doesn't end up with totally fucked up behaviour.\n\telse\n\t\tmodestate = MS_WINDOWED;\n\n\tif (modestate == MS_FULLWINDOW)\n\t{\n\t\twstyle = WS_POPUP;\n\t\trect.right = GetSystemMetrics(SM_CXSCREEN);\n\t\trect.bottom = GetSystemMetrics(SM_CYSCREEN);\n\t\trect.left = 0;\n\t\trect.top = 0;\n\t}\n\telse\n\t{\n\t\twstyle = WS_OVERLAPPEDWINDOW;\n\t\trect.left = (GetSystemMetrics(SM_CXSCREEN) - width) / 2;\n\t\trect.top = (GetSystemMetrics(SM_CYSCREEN) - height) / 2;\n\t\trect.right = rect.left+width;\n\t\trect.bottom = rect.top+height;\n\t\tAdjustWindowRectEx(&rect, wstyle, FALSE, 0);\n\t}\n\tmainwindow = CreateWindow(CLASSNAME, \"Direct3D11\", wstyle, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, NULL, NULL, NULL, NULL);\n\n\t// Try as specified.\n\n\tDoDXGIDebug();\n\n\tinitD3D11(mainwindow, info);\n\tif (!pD3DDev11)\n\t{\n\t\tDoDXGIDebug();\n\t\tCon_Printf(\"No suitable D3D11 device found\\n\");\n\t\treturn false;\n\t}\n\n\tvid.pixelwidth = width;\n\tvid.pixelheight = height;\n\n\twhile (PeekMessage(&msg, NULL,  0, 0, PM_REMOVE))\n\t{\n\t\tTranslateMessage(&msg);\n\t\tDispatchMessage(&msg);\n\t}\n\n\tCL_UpdateWindowTitle();\n\n\tif (modestate == MS_FULLWINDOW)\n\t\tShowWindow(mainwindow, SW_SHOWMAXIMIZED);\n\telse\n\t\tShowWindow(mainwindow, SW_SHOWNORMAL);\n\n\tvid.width = vid.pixelwidth;\n\tvid.height = vid.pixelheight;\n\n\tif (modestate == MS_FULLSCREEN)\n\t{\n\t\tif (!d3dscreen)\n\t\t\tIDXGISwapChain_GetContainingOutput(d3dswapchain, &d3dscreen);\n\t\tIDXGISwapChain_SetFullscreenState(d3dswapchain, true, d3dscreen);\n\t}\n\n\tvid_initializing = false;\n\n\tGetWindowRect(mainwindow, &window_rect);\n\n\t{\n\t\textern qboolean\tmouseactive;\n\t\tmouseactive = false;\n\t}\n\n\trf->VID_CreateCursor = WIN_CreateCursor;\n\trf->VID_DestroyCursor = WIN_DestroyCursor;\n\trf->VID_SetCursor = WIN_SetCursor;\n\n\treturn true;\n}\n#endif\n\nstatic void\t (D3D11_VID_DeInit)\t\t\t\t(void)\n{\n\tImage_Shutdown();\n\n\t/*we cannot shut down cleanly while in fullscreen, supposedly*/\n\tif(d3dswapchain)\n\t\tIDXGISwapChain_SetFullscreenState(d3dswapchain, false, NULL);\n\n\treleased3dbackbuffer();\n\tif(d3dswapchain)\n\t\tIDXGISwapChain_Release(d3dswapchain);\n\td3dswapchain = NULL;\n\tif (pD3DDev11)\n\t\tID3D11Device_Release(pD3DDev11);\n\tpD3DDev11 = NULL;\n\n\tif (d3ddevctx)\n\t{\n\t\tID3D11DeviceContext_ClearState(d3ddevctx);\n\t\tID3D11DeviceContext_Flush(d3ddevctx);\n\t\tID3D11DeviceContext_Release(d3ddevctx);\n\t}\n\td3ddevctx = NULL;\n\n#ifndef WINRT\n\tif (mainwindow)\n\t{\n\t\tDestroyWindow(mainwindow);\n\t\tmainwindow = NULL;\n\t}\n#endif\n\n\tif (d3dscreen)\n\t\tIUnknown_Release(d3dscreen);\n\td3dscreen = NULL;\n\n\tDoDXGIDebug();\n}\n\nextern float\t\thw_blend[4];\t\t// rgba 0.0 - 1.0\n\nstatic void D3D11_BuildRamps(int points, DXGI_RGB *out)\n{\n//FIXME: repack input rather than recalculating.\n\tint i;\n\tvec3_t cshift;\n\tvec3_t c;\n\tfloat sc;\n\tVectorScale(hw_blend, hw_blend[3], cshift);\n\tfor (i = 0; i < points; i++)\n\t{\n\t\tsc = i / (float)(points-1);\n\t\tVectorSet(c, sc, sc, sc);\n\t\tVectorAdd(c, cshift, c);\n\t\tVectorScale(c, v_contrast.value, c);\n\n\t\tout[i].Red\t\t= pow (c[0], v_gamma.value) + v_brightness.value;\n\t\tout[i].Green\t= pow (c[1], v_gamma.value) + v_brightness.value;\n\t\tout[i].Blue\t\t= pow (c[2], v_gamma.value) + v_brightness.value;\n\t}\n}\n\nstatic qboolean\tD3D11_VID_ApplyGammaRamps(unsigned int gammarampsize, unsigned short *ramps)\n{\n\tHRESULT hr;\n\tDXGI_GAMMA_CONTROL_CAPABILITIES caps;\n\tDXGI_GAMMA_CONTROL gam;\n\t//cache the screen, so we don't get too confused.\n\tif (!d3dscreen)\n\t\treturn false;\t//don't do it when we're running windowed.\n\n\tif (d3dscreen)\n\t{\n\t\tgam.Scale.Red = 1;\n\t\tgam.Scale.Green = 1;\n\t\tgam.Scale.Blue = 1;\n\t\tgam.Offset.Red = 0;\n\t\tgam.Offset.Green = 0;\n\t\tgam.Offset.Blue = 0;\n\n\t\tif (FAILED(IDXGIOutput_GetGammaControlCapabilities(d3dscreen, &caps)))\n\t\t\treturn false;\n\t\tif (caps.NumGammaControlPoints > 1025)\n\t\t\tcaps.NumGammaControlPoints = 1025;\n\t\tD3D11_BuildRamps(caps.NumGammaControlPoints, gam.GammaCurve);\n\n\t\thr = IDXGIOutput_SetGammaControl(d3dscreen, &gam);\n\t\tif (SUCCEEDED(hr))\n\t\t\treturn true;\n\t}\n\treturn false;\n}\nstatic char\t*D3D11_VID_GetRGBInfo(int *bytestride, int *truevidwidth, int *truevidheight, enum uploadfmt *fmt)\n{\n\t//don't directly map the frontbuffer, as that can hold other things.\n\t//create a texture, copy the (gpu)backbuffer to that (cpu)texture\n\t//then map the (cpu)texture and copy out the parts we need, reordering as needed.\n\tD3D11_MAPPED_SUBRESOURCE lock;\n\tqbyte *rgb, *in, *r = NULL;\n\tunsigned int x,y;\n\tD3D11_TEXTURE2D_DESC texDesc;\n\tID3D11Texture2D *texture;\n\tID3D11Resource *backbuffer;\n\ttexDesc.ArraySize = 1;\n\ttexDesc.BindFlags = 0;\n\ttexDesc.CPUAccessFlags = 0;\n\ttexDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;\n\ttexDesc.Width = vid.pixelwidth;\n\ttexDesc.Height = vid.pixelheight;\n\ttexDesc.MipLevels = 1;\n\ttexDesc.MiscFlags = 0;\n\ttexDesc.SampleDesc.Count = 1;\n\ttexDesc.SampleDesc.Quality = 0;\n\ttexDesc.Usage = D3D11_USAGE_STAGING;\n\ttexDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;\n\tif (FAILED(ID3D11Device_CreateTexture2D(pD3DDev11, &texDesc, 0, &texture)))\n\t\treturn NULL;\n\tID3D11RenderTargetView_GetResource(fb_backbuffer, &backbuffer);\n\tID3D11DeviceContext_CopyResource(d3ddevctx, (ID3D11Resource*)texture, backbuffer);\n\tID3D11Resource_Release(backbuffer);\n\tif (!FAILED(ID3D11DeviceContext_Map(d3ddevctx, (ID3D11Resource*)texture, 0, D3D11_MAP_READ, 0, &lock)))\n\t{\n\t\tr = rgb = BZ_Malloc(3 * vid.pixelwidth * vid.pixelheight);\n\t\tfor (y = vid.pixelheight; y-- > 0; )\n\t\t{\n\t\t\tin = lock.pData;\n\t\t\tin += y * lock.RowPitch;\n\t\t\tfor (x = 0; x < vid.pixelwidth; x++, rgb+=3, in+=4)\n\t\t\t{\n\t\t\t\trgb[0] = in[0];\n\t\t\t\trgb[1] = in[1];\n\t\t\t\trgb[2] = in[2];\n\t\t\t}\n\t\t}\n\t\tID3D11DeviceContext_Unmap(d3ddevctx, (ID3D11Resource*)texture, 0);\n\t}\n\tID3D11Texture2D_Release(texture);\n\t*bytestride = vid.pixelwidth*3;\n\t*truevidwidth = vid.pixelwidth;\n\t*truevidheight = vid.pixelheight;\n\t*fmt = TF_RGB24;\n\treturn r;\n}\nstatic void\t(D3D11_VID_SetWindowCaption)\t\t(const char *msg)\n{\n#ifndef WINRT\n\tSetWindowText(mainwindow, msg);\n#endif\n}\n\nvoid D3D11_Set2D (void)\n{\n//\tMatrix4x4_CM_Orthographic(r_refdef.m_projection, 0 + (0.5*vid.width/vid.pixelwidth), vid.width + (0.5*vid.width/vid.pixelwidth), 0 + (0.5*vid.height/vid.pixelheight), vid.height + (0.5*vid.height/vid.pixelheight), 0, 100);\n\tMatrix4x4_CM_Orthographic(r_refdef.m_projection_std, 0, vid.width, vid.height, 0, 0, 99999);\n\tMatrix4x4_Identity(r_refdef.m_view);\n\n\n\tvid.fbvwidth = vid.width;\n\tvid.fbvheight = vid.height;\n\tvid.fbpwidth = vid.pixelwidth;\n\tvid.fbpheight = vid.pixelheight;\n\n\tr_refdef.pxrect.x = 0;\n\tr_refdef.pxrect.y = 0;\n\tr_refdef.pxrect.width = vid.fbpwidth;\n\tr_refdef.pxrect.height = vid.fbpheight;\n\n\tD3D11BE_Set2D();\n}\n\nstatic qboolean\t(D3D11_SCR_UpdateScreen)\t\t\t(void)\n{\n\t//extern int keydown[];\n\t//extern cvar_t vid_conheight;\n#ifdef TEXTEDITOR\n\t//extern qboolean editormodal;\n#endif\n\tqboolean nohud, noworld;\n\n\tif (r_clear.ival)\n\t{\n\t\tfloat colours[4] = {(r_clear.ival&1)?1:0, (r_clear.ival&2)?1:0, (r_clear.ival&4)?1:0, 1};\n\t\tID3D11DeviceContext_ClearRenderTargetView(d3ddevctx, fb_backbuffer, colours);\n\t}\n\n\tif (d3d_resized)\n\t{\n\t\textern cvar_t vid_conautoscale, vid_conwidth;\n\t\td3d_resized = false;\n\n\t\t// force width/height to be updated\n\t\t//vid.pixelwidth = window_rect.right - window_rect.left;\n\t\t//vid.pixelheight = window_rect.bottom - window_rect.top;\n\t\tCvar_ForceCallback(&vid_conautoscale);\n\t\tCvar_ForceCallback(&vid_conwidth);\n\t}\n\tR2D_Font_Changed();\n\n\tif (scr_disabled_for_loading)\n\t{\n\t\textern float scr_disabled_time;\n\t\tif (Sys_DoubleTime() - scr_disabled_time > 60 || Key_Dest_Has(~kdm_game))\n\t\t{\n\t\t\tscr_disabled_for_loading = false;\n\t\t}\n\t\telse\n\t\t{\n//\t\t\tIDirect3DDevice9_BeginScene(pD3DDev9);\n\t\t\tscr_drawloading = true;\n\t\t\tSCR_DrawLoading (true);\n\t\t\tscr_drawloading = false;\n\t\t\tif (R2D_Flush)\n\t\t\t\tR2D_Flush();\n//\t\t\tIDirect3DDevice9_EndScene(pD3DDev9);\n\t\t\tD3D11_PresentOrCrash();\n\t\t\treturn true;\n\t\t}\n\t}\n\n\tif (!scr_initialized || !con_initialized)\n\t{\n\t\treturn false;                         // not initialized yet\n\t}\n\n\tShader_DoReload();\n\n//\td3d11error(IDirect3DDevice9_BeginScene(pD3DDev9));\n/*\n#ifdef TEXTEDITOR\n\tif (editormodal)\n\t{\n\t\tEditor_Draw();\n\t\tV_UpdatePalette (false);\n\t\tMedia_RecordFrame();\n\t\tR2D_BrightenScreen();\n\n\t\tif (key_dest == key_console)\n\t\t\tCon_DrawConsole(vid_conheight.value/2, false);\n\t\tGL_EndRendering ();\n\t\tGL_DoSwap();\n\t\tRSpeedEnd(RSPEED_TOTALREFRESH);\n\t\treturn;\n\t}\n#endif\n*/\n\n//\n// do 3D refresh drawing, and then update the screen\n//\n\tSCR_SetUpToDrawConsole ();\n\n\tnoworld = false;\n\tnohud = false;\n\n\tif (topmenu && topmenu->isopaque)\n\t\tnohud = true;\n#ifdef VM_CG\n\telse if (q3 && q3->cg.Redraw(cl.time))\n\t\tnohud = true;\n#endif\n#ifdef CSQC_DAT\n\telse if (CSQC_DrawView())\n\t\tnohud = true;\n#endif\n\telse if (r_worldentity.model && cls.state == ca_active)\n\t\tV_RenderView (nohud);\n\telse\n\t\tnoworld = true;\n\n\tD3D11_Set2D();\n\n\tif (!noworld)\n\t{\n\t\tR2D_BrightenScreen();\n\t}\n\n\tscr_con_forcedraw = false;\n\tif (noworld)\n\t{\n\t\tif ((!Key_Dest_Has(~(kdm_game|kdm_console))) && SCR_GetLoadingStage() == LS_NONE)\n\t\t\tscr_con_current = vid.height;\n\n\t\tif (scr_con_current != vid.height)\n\t\t\tR2D_ConsoleBackground(0, vid.height, true);\n\t\telse\n\t\t\tscr_con_forcedraw = true;\n\n\t\tnohud = true;\n\t}\n\n\tSCR_DrawTwoDimensional(nohud);\n\n\tV_UpdatePalette (false);\n\n\tMedia_RecordFrame();\n\n\tRSpeedShow();\n\n\tD3D11_PresentOrCrash();\n\n\twindow_center_x = (window_rect.left + window_rect.right)/2;\n\twindow_center_y = (window_rect.top + window_rect.bottom)/2;\n\n\n\tINS_UpdateGrabs(modestate != MS_WINDOWED, vid.activeapp);\n\treturn true;\n}\n\n\n\n\n\n\n\nstatic void\tD3D11_Draw_Init\t\t\t\t(void)\n{\n\tR2D_Init();\n}\nstatic void\tD3D11_Draw_Shutdown\t\t\t\t(void)\n{\n\tR2D_Shutdown();\n}\n\nstatic void\tD3D11_R_Init\t\t\t\t\t(void)\n{\n}\nstatic void\tD3D11_R_DeInit\t\t\t\t\t(void)\n{\n\tR_GAliasFlushSkinCache(true);\n\tSurf_DeInit();\n\tD3D11BE_Shutdown();\n\tShader_Shutdown();\n}\n\n\n\nstatic void D3D11_SetupViewPort(void)\n{\n\tint\t\tx, x2, y2, y;\n\n\tfloat fov_x, fov_y;\n\tfloat fovv_x, fovv_y;\n\n\tAngleVectors (r_refdef.viewangles, vpn, vright, vup);\n\tVectorCopy (r_refdef.vieworg, r_origin);\n\n\t//\n\t// set up viewpoint\n\t//\n\tx = r_refdef.vrect.x * vid.pixelwidth/(int)vid.width;\n\tx2 = (r_refdef.vrect.x + r_refdef.vrect.width) * vid.pixelwidth/(int)vid.width;\n\ty = (r_refdef.vrect.y) * vid.pixelheight/(int)vid.height;\n\ty2 = ((int)(r_refdef.vrect.y + r_refdef.vrect.height)) * vid.pixelheight/(int)vid.height;\n\n\t// fudge around because of frac screen scale\n\tif (x > 0)\n\t\tx--;\n\tif (x2 < vid.pixelwidth)\n\t\tx2++;\n\tif (y < 0)\n\t\ty--;\n\tif (y2 < vid.pixelheight)\n\t\ty2++;\n\n\tfov_x = r_refdef.fov_x;//+sin(cl.time)*5;\n\tfov_y = r_refdef.fov_y;//-sin(cl.time+1)*5;\n\tfovv_x = r_refdef.fovv_x;\n\tfovv_y = r_refdef.fovv_y;\n\n\tif ((r_refdef.flags & RDF_UNDERWATER) && !(r_refdef.flags & RDF_WATERWARP))\n\t{\n\t\tfov_x *= 1 + (((sin(cl.time * 4.7) + 1) * 0.015) * r_waterwarp.value);\n\t\tfov_y *= 1 + (((sin(cl.time * 3.0) + 1) * 0.015) * r_waterwarp.value);\n\t\tfovv_x *= 1 + (((sin(cl.time * 4.7) + 1) * 0.015) * r_waterwarp.value);\n\t\tfovv_y *= 1 + (((sin(cl.time * 3.0) + 1) * 0.015) * r_waterwarp.value);\n\t}\n\n\tif (r_xflip.ival)\n\t{\n\t\tfov_x *= -1;\n\t\tr_refdef.flipcull ^= SHADER_CULL_FLIP;\n\t\tfovv_x *= -1;\n\t}\n\n\t/*view matrix*/\n\tMatrix4x4_CM_ModelViewMatrixFromAxis(r_refdef.m_view, vpn, vright, vup, r_refdef.vieworg);\n\n\t/*projection matricies (main game, and viewmodel)*/\n\tMatrix4x4_CM_Projection_Offset(r_refdef.m_projection_std,  -fov_x/2, fov_x/2, -fov_y/2, fov_y/2, r_refdef.mindist, r_refdef.maxdist, true);\n\tMatrix4x4_CM_Projection_Offset(r_refdef.m_projection_view, -fovv_x/2, fovv_x/2, -fovv_y/2, fovv_y/2, r_refdef.mindist, r_refdef.maxdist, true);\n}\n\nstatic void\t(D3D11_R_RenderView)\t\t\t\t(void)\n{\n\tfloat x, x2, y, y2;\n\tdouble time1 = 0, time2 = 0;\n\tqboolean dofbo = *r_refdef.rt_destcolour[0].texname || *r_refdef.rt_depth.texname;\n\tint cull = r_refdef.flipcull;\n//\ttexid_t colourrt[1];\n\n\tif (!r_norefresh.value)\n\t{\n\t\tif (r_speeds.ival)\n\t\t\ttime1 = Sys_DoubleTime();\n\n\t\tif (dofbo)\n\t\t\tD3D11_ApplyRenderTargets(true);\n\t\telse\n\t\t\tID3D11DeviceContext_ClearDepthStencilView(d3ddevctx, fb_backdepthstencil, D3D11_CLEAR_DEPTH, 1, 0);\t//is it faster to clear the stencil too?\n\n\t\t//check if we can do underwater warp\n\t\tif (cls.protocol != CP_QUAKE2)\t//quake2 tells us directly\n\t\t{\n\t\t\tif (r_viewcontents & FTECONTENTS_FLUID)\n\t\t\t\tr_refdef.flags |= RDF_UNDERWATER;\n\t\t\telse\n\t\t\t\tr_refdef.flags &= ~RDF_UNDERWATER;\n\t\t}\n\t\tif (r_refdef.flags & RDF_UNDERWATER)\n\t\t{\n\t\t\textern cvar_t r_projection;\n\t\t\tif (!r_waterwarp.value || r_projection.ival)\n\t\t\t\tr_refdef.flags &= ~RDF_UNDERWATER;\t//no warp at all\n\t//\t\telse if (r_waterwarp.value > 0 && scenepp_waterwarp)\n\t//\t\t\tr_refdef.flags |= RDF_WATERWARP;\t//try fullscreen warp instead if we can\n\t\t}\n\n\t\tD3D11_SetupViewPort();\n\t\t//unlike gl, we clear colour beforehand, because that seems more sane.\n\t\t//always clear depth\n\n\t\tx = (r_refdef.vrect.x * (int)vid.pixelwidth)/(int)vid.width;\n\t\tx2 = (r_refdef.vrect.x + r_refdef.vrect.width) * (int)vid.pixelwidth/(int)vid.width;\n\t\ty = (r_refdef.vrect.y * (int)vid.pixelheight)/(int)vid.height;\n\t\ty2 = (r_refdef.vrect.y + r_refdef.vrect.height) * (int)vid.pixelheight/(int)vid.height;\n\t\tr_refdef.pxrect.x = floor(x);\n\t\tr_refdef.pxrect.y = floor(y);\n\t\tr_refdef.pxrect.width = (int)ceil(x2) - r_refdef.pxrect.x;\n\t\tr_refdef.pxrect.height = (int)ceil(y2) - r_refdef.pxrect.y;\n\n\t\tSurf_SetupFrame();\n\n\t\t//fixme: waterwarp fov\n\n\t\tR_SetFrustum (r_refdef.m_projection_std, r_refdef.m_view);\n\t\tRQ_BeginFrame();\n\t\t\n\t\tif (!(r_refdef.flags & RDF_NOWORLDMODEL))\n\t\t\tif (!r_worldentity.model || r_worldentity.model->loadstate != MLS_LOADED || !cl.worldmodel)\n\t\t\t{\n\t\t\t\tD3D11_Set2D ();\n\t\t\t\tR2D_ImageColours(0, 0, 0, 1);\n\t\t\t\tR2D_FillBlock(r_refdef.vrect.x, r_refdef.vrect.y, r_refdef.vrect.width, r_refdef.vrect.height);\n\t\t\t\tR2D_ImageColours(1, 1, 1, 1);\n\n\t\t\t\tr_refdef.flipcull = cull;\n\t\t\t\tif (dofbo)\n\t\t\t\t\tD3D11_ApplyRenderTargets(false);\n\t\t\t\treturn;\n\t\t\t}\n//\t\tif (!(r_refdef.flags & RDF_NOWORLDMODEL))\n//\t\t{\n//\t\t\tif (!r_refdef.recurse && !(r_refdef.flags & RDF_DISABLEPARTICLES))\n//\t\t\t\tP_DrawParticles ();\n//\t\t}\n\t\tSurf_DrawWorld();\n\t\tRQ_RenderBatchClear();\n\n\t\tr_refdef.flipcull = cull;\n\n\t\tif (r_speeds.ival)\n\t\t{\n\t\t\ttime2 = Sys_DoubleTime();\n\t\t\tRQuantAdd(RQUANT_MSECS, (int)((time2-time1)*1000000));\n\t\t}\n\n\t\tif (dofbo)\n\t\t\tD3D11_ApplyRenderTargets(false);\n\t}\n\tD3D11_Set2D ();\n}\n\nvoid D3D11BE_RenderToTextureUpdate2d(qboolean destchanged)\n{\n\tif (destchanged)\n\t{\n\t\tif (*r_refdef.rt_destcolour[0].texname)\n\t\t\tD3D11_ApplyRenderTargets(false);\n\t\telse\n\t\t\tID3D11DeviceContext_OMSetRenderTargets(d3ddevctx, 1, &fb_backbuffer, fb_backdepthstencil);\n\n\t\tD3D11_Set2D();\n\t}\n\telse\n\t{\n//\t\tshaderstate.tex_sourcecol = R2D_RT_GetTexture(r_refdef.rt_sourcecolour.texname, &width, &height);\n//\t\tshaderstate.tex_sourcedepth = R2D_RT_GetTexture(r_refdef.rt_depth.texname, &width, &height);\n\t}\n}\n\nrendererinfo_t d3d11rendererinfo =\n{\n\t\"Direct3D11\",\n\t{\n\t\t\"D3D11\",\n\t\t\"Direct3d11\",\n\t\t\"DirectX11\",\n\t\t\"DX11\"\n\t},\n\tQR_DIRECT3D11,\n\n\tD3D11_Draw_Init,\n\tD3D11_Draw_Shutdown,\n\n\tD3D11_UpdateFiltering,\n\tD3D11_LoadTextureMips,\n\tD3D11_DestroyTexture,\n/*\n\tD3D11_LoadTexture,\n\tD3D11_LoadTexture8Pal24,\n\tD3D11_LoadTexture8Pal32,\n\tD3D11_LoadCompressed,\n\tD3D11_FindTexture,\n\tD3D11_AllocNewTexture,\n\tD3D11_Upload,\n\tD3D11_DestroyTexture,\n*/\n\tD3D11_R_Init,\n\tD3D11_R_DeInit,\n\tD3D11_R_RenderView,\n\n\tD3D11_VID_Init,\n\tD3D11_VID_DeInit,\n\tD3D11_PresentOrCrash,\n\tD3D11_VID_ApplyGammaRamps,\n\tNULL,\n\tNULL,\n\tNULL,\n\tD3D11_VID_SetWindowCaption,\n\tD3D11_VID_GetRGBInfo,\n\n\tD3D11_SCR_UpdateScreen,\n\n\tD3D11BE_SelectMode,\n\tD3D11BE_DrawMesh_List,\n\tD3D11BE_DrawMesh_Single,\n\tD3D11BE_SubmitBatch,\n\tD3D11BE_GetTempBatch,\n\tD3D11BE_DrawWorld,\n\tD3D11BE_Init,\n\tD3D11BE_GenBrushModelVBO,\n\tD3D11BE_ClearVBO,\n\tD3D11BE_UploadAllLightmaps,\n\tD3D11BE_SelectEntity,\n\tD3D11BE_SelectDLight,\n\tD3D11BE_Scissor,\n\tD3D11BE_LightCullModel,\n\n\tD3D11BE_VBO_Begin,\n\tD3D11BE_VBO_Data,\n\tD3D11BE_VBO_Finish,\n\tD3D11BE_VBO_Destroy,\n\n\tD3D11BE_RenderToTextureUpdate2d,\n\n\t\"no more\",\n\tNULL,\t//VID_GetPriority\n\tNULL,\t//VID_EnumerateVideoModes\n\tD3D11_VID_EnumerateDevices\n};\n#endif\n"
  },
  {
    "path": "engine/d3d/vid_d3d8.c",
    "content": "#include \"quakedef.h\"\r\n#include \"gl_draw.h\"\r\n#include \"shader.h\"\r\n#include \"renderque.h\"\r\n#include \"resource.h\"\r\n\r\n#include \"glquake.h\"\r\n\r\n#ifdef D3D8QUAKE\r\n//#define FIXME\r\n#include \"winquake.h\"\r\n\r\n#ifdef _XBOX\r\n\t#include <xtl.h>\r\n#endif\r\n\r\n#if !defined(HMONITOR_DECLARED) && (WINVER < 0x0500)\r\n\t#define HMONITOR_DECLARED\r\n\tDECLARE_HANDLE(HMONITOR);\r\n#endif\r\n\r\n#include    <d3d8.h>\r\n#ifndef D3DSGR_NO_CALIBRATION\r\n#define D3DSGR_NO_CALIBRATION 0\t//mingw fails to define this.\r\n#endif\r\n\r\n/*Fixup outdated windows headers*/\r\n#ifndef WM_XBUTTONDOWN\r\n   #define WM_XBUTTONDOWN      0x020B\r\n   #define WM_XBUTTONUP      0x020C\r\n#endif\r\n#ifndef MK_XBUTTON1\r\n   #define MK_XBUTTON1         0x0020\r\n#endif\r\n#ifndef MK_XBUTTON2\r\n   #define MK_XBUTTON2         0x0040\r\n#endif\r\n// copied from DarkPlaces in an attempt to grab more buttons\r\n#ifndef MK_XBUTTON3\r\n   #define MK_XBUTTON3         0x0080\r\n#endif\r\n#ifndef MK_XBUTTON4\r\n   #define MK_XBUTTON4         0x0100\r\n#endif\r\n#ifndef MK_XBUTTON5\r\n   #define MK_XBUTTON5         0x0200\r\n#endif\r\n#ifndef MK_XBUTTON6\r\n   #define MK_XBUTTON6         0x0400\r\n#endif\r\n#ifndef MK_XBUTTON7\r\n   #define MK_XBUTTON7         0x0800\r\n#endif\r\n\r\n#ifndef WM_INPUT\r\n\t#define WM_INPUT 255\r\n#endif\r\n\r\n//static void D3D8_GetBufferSize(int *width, int *height); //not defined\r\nstatic void resetD3D8(void);\r\nstatic LPDIRECT3D8 pD3D;\r\nLPDIRECT3DDEVICE8 pD3DDev8;\r\nstatic D3DPRESENT_PARAMETERS d3dpp;\r\nfloat d3d_trueprojection[16];\r\n\r\nstatic qboolean vid_initializing;\r\n\r\nextern qboolean\t\tscr_initialized;                // ready to draw\r\nextern qboolean\t\tscr_drawloading;\r\nextern qboolean\t\tscr_con_forcedraw;\r\nstatic qboolean d3d_resized;\r\n\r\nextern cvar_t vid_hardwaregamma;\r\n\r\n/*void BuildGammaTable (float g, float c);\r\nstatic void\tD3D8_VID_GenPaletteTables (unsigned char *palette)\r\n{\r\n\textern unsigned short\t\tramps[3][256];\r\n\tqbyte\t*pal;\r\n\tunsigned r,g,b;\r\n\tunsigned v;\r\n\tunsigned short i;\r\n\tunsigned\t*table;\r\n\textern qbyte gammatable[256];\r\n\r\n\tif (palette)\r\n\t{\r\n\t\textern cvar_t v_contrast;\r\n\t\tBuildGammaTable(v_gamma.value, v_contrast.value);\r\n\r\n\t\t//\r\n\t\t// 8 8 8 encoding\r\n\t\t//\r\n\t\tif (1)//vid_hardwaregamma.value)\r\n\t\t{\r\n\t\t//\tdon't built in the gamma table\r\n\r\n\t\t\tpal = palette;\r\n\t\t\ttable = d_8to24rgbtable;\r\n\t\t\tfor (i=0 ; i<256 ; i++)\r\n\t\t\t{\r\n\t\t\t\tr = pal[0];\r\n\t\t\t\tg = pal[1];\r\n\t\t\t\tb = pal[2];\r\n\t\t\t\tpal += 3;\r\n\r\n\t\t//\t\tv = (255<<24) + (r<<16) + (g<<8) + (b<<0);\r\n\t\t//\t\tv = (255<<0) + (r<<8) + (g<<16) + (b<<24);\r\n\t\t\t\tv = (255<<24) + (r<<0) + (g<<8) + (b<<16);\r\n\t\t\t\t*table++ = v;\r\n\t\t\t}\r\n\t\t\td_8to24rgbtable[255] &= 0xffffff;\t// 255 is transparent\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t//computer has no hardware gamma (poor suckers) increase table accordingly\r\n\r\n\t\t\tpal = palette;\r\n\t\t\ttable = d_8to24rgbtable;\r\n\t\t\tfor (i=0 ; i<256 ; i++)\r\n\t\t\t{\r\n\t\t\t\tr = gammatable[pal[0]];\r\n\t\t\t\tg = gammatable[pal[1]];\r\n\t\t\t\tb = gammatable[pal[2]];\r\n\t\t\t\tpal += 3;\r\n\r\n\t\t//\t\tv = (255<<24) + (r<<16) + (g<<8) + (b<<0);\r\n\t\t//\t\tv = (255<<0) + (r<<8) + (g<<16) + (b<<24);\r\n\t\t\t\tv = (255<<24) + (r<<0) + (g<<8) + (b<<16);\r\n\t\t\t\t*table++ = v;\r\n\t\t\t}\r\n\t\t\td_8to24rgbtable[255] &= 0xffffff;\t// 255 is transparent\r\n\t\t}\r\n\r\n\t\tif (LittleLong(1) != 1)\r\n\t\t{\r\n\t\t\tfor (i=0 ; i<256 ; i++)\r\n\t\t\t\td_8to24rgbtable[i] = LittleLong(d_8to24rgbtable[i]);\r\n\t\t}\r\n\t}\r\n\r\n\tif (pD3DDev8)\r\n\t\tIDirect3DDevice8_SetGammaRamp(pD3DDev8, 0, D3DSGR_NO_CALIBRATION, (D3DGAMMARAMP *)ramps);\r\n}\r\n*/\r\ntypedef enum {MS_WINDOWED, MS_FULLSCREEN, MS_FULLDIB, MS_UNINIT} modestate_t;\r\nstatic modestate_t modestate;\r\n\r\n\r\nstatic void D3DVID_UpdateWindowStatus (HWND hWnd)\r\n{\r\n#ifndef _XBOX\r\n\tPOINT p;\r\n\tRECT nr;\r\n\tint window_width, window_height;\r\n\tGetClientRect(hWnd, &nr);\r\n\r\n\t//if its bad then we're probably minimised\r\n\tif (nr.right <= nr.left)\r\n\t\treturn;\r\n\tif (nr.bottom <= nr.top)\r\n\t\treturn;\r\n\r\n\tp.x = 0;\r\n\tp.y = 0;\r\n\tClientToScreen(hWnd, &p);\r\n\twindow_x = p.x;\r\n\twindow_y = p.y;\r\n\twindow_width = nr.right - nr.left;\r\n\twindow_height = nr.bottom - nr.top;\r\n//\tvid.pixelwidth = window_width;\r\n//\tvid.pixelheight = window_height;\r\n\r\n\twindow_rect.left = window_x;\r\n\twindow_rect.top = window_y;\r\n\twindow_rect.right = window_x + window_width;\r\n\twindow_rect.bottom = window_y + window_height;\r\n\twindow_center_x = (window_rect.left + window_rect.right) / 2;\r\n\twindow_center_y = (window_rect.top + window_rect.bottom) / 2;\r\n\r\n\tINS_UpdateClipCursor ();\r\n#endif\r\n}\r\n\r\nstatic qboolean D3D8AppActivate(BOOL fActive, BOOL minimize)\r\n/****************************************************************************\r\n*\r\n* Function:     AppActivate\r\n* Parameters:   fActive - True if app is activating\r\n*\r\n* Description:  If the application is activating, then swap the system\r\n*               into SYSPAL_NOSTATIC mode so that our palettes will display\r\n*               correctly.\r\n*\r\n****************************************************************************/\r\n{\r\n\tstatic BOOL\tsound_active;\r\n\r\n\tif (vid.activeapp == fActive && Minimized == minimize)\r\n\t\treturn false;\t//so windows doesn't crash us over and over again.\r\n\r\n\tvid.activeapp = fActive;\r\n\tMinimized = minimize;\r\n\r\n// enable/disable sound on focus gain/loss\r\n\tif (!vid.activeapp && sound_active)\r\n\t{\r\n\t\tS_BlockSound ();\r\n\t\tsound_active = false;\r\n\t}\r\n\telse if (vid.activeapp && !sound_active)\r\n\t{\r\n\t\tS_UnblockSound ();\r\n\t\tsound_active = true;\r\n\t}\r\n\r\n\tINS_UpdateGrabs(modestate != MS_WINDOWED, vid.activeapp);\r\n\r\n\tif (fActive)\r\n\t{\r\n\t\tCvar_ForceCallback(&v_gamma);\r\n\t}\r\n\tif (!fActive)\r\n\t{\r\n\t\tCvar_ForceCallback(&v_gamma);\t//wham bam thanks.\r\n\t}\r\n\r\n\treturn true;\r\n}\r\n\r\n#ifndef _XBOX\r\nstatic LRESULT WINAPI D3D8_WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)\r\n{\r\n\tLONG    lRet = 1;\r\n\tint\t\tfActive, fMinimized, temp;\r\n\textern unsigned int uiWheelMessage;\r\n\r\n\tif ( uMsg == uiWheelMessage )\r\n\t\tuMsg = WM_MOUSEWHEEL;\r\n\r\n\tswitch (uMsg)\r\n\t{\r\n\t\tcase WM_KILLFOCUS:\r\n\t\t\tif (modestate == MS_FULLDIB)\r\n\t\t\t\tShowWindow(mainwindow, SW_SHOWMINNOACTIVE);\r\n\t\t\tbreak;\r\n\r\n\t\tcase WM_CREATE:\r\n\t\t\tbreak;\r\n\r\n\t\tcase WM_MOVE:\r\n\t\t\tD3DVID_UpdateWindowStatus (hWnd);\r\n\t\t\tbreak;\r\n\r\n\t\tcase WM_KEYDOWN:\r\n\t\tcase WM_SYSKEYDOWN:\r\n\t\t\tif (!vid_initializing)\r\n\t\t\t\tINS_TranslateKeyEvent (wParam, lParam, true, 0, false);\r\n\t\t\tbreak;\r\n\r\n\t\tcase WM_KEYUP:\r\n\t\tcase WM_SYSKEYUP:\r\n\t\t\tif (!vid_initializing)\r\n\t\t\t\tINS_TranslateKeyEvent (wParam, lParam, false, 0, false);\r\n\t\t\tbreak;\r\n\r\n\t\tcase WM_SYSCHAR:\r\n\t\t// keep Alt-Space from happening\r\n\t\t\tbreak;\r\n\r\n\t\tcase WM_APPCOMMAND:\r\n\t\t\tlRet = INS_AppCommand(lParam);\r\n\t\t\tbreak;\r\n\r\n\t// this is complicated because Win32 seems to pack multiple mouse events into\r\n\t// one update sometimes, so we always check all states and look for events\r\n\t\tcase WM_LBUTTONDOWN:\r\n\t\tcase WM_LBUTTONUP:\r\n\t\tcase WM_RBUTTONDOWN:\r\n\t\tcase WM_RBUTTONUP:\r\n\t\tcase WM_MBUTTONDOWN:\r\n\t\tcase WM_MBUTTONUP:\r\n\t\tcase WM_MOUSEMOVE:\r\n\t\tcase WM_XBUTTONDOWN:\r\n\t\tcase WM_XBUTTONUP:\r\n\t\t\ttemp = 0;\r\n\r\n\t\t\tif (wParam & MK_LBUTTON)\r\n\t\t\t\ttemp |= 1;\r\n\r\n\t\t\tif (wParam & MK_RBUTTON)\r\n\t\t\t\ttemp |= 2;\r\n\r\n\t\t\tif (wParam & MK_MBUTTON)\r\n\t\t\t\ttemp |= 4;\r\n\r\n\t\t\tif (wParam & MK_XBUTTON1)\r\n\t\t\t\ttemp |= 8;\r\n\r\n\t\t\tif (wParam & MK_XBUTTON2)\r\n\t\t\t\ttemp |= 16;\r\n\r\n\t\t\tif (wParam & MK_XBUTTON3)\r\n\t\t\t\ttemp |= 32;\r\n\r\n\t\t\tif (wParam & MK_XBUTTON4)\r\n\t\t\t\ttemp |= 64;\r\n\r\n\t\t\tif (wParam & MK_XBUTTON5)\r\n\t\t\t\ttemp |= 128;\r\n\r\n\t\t\tif (wParam & MK_XBUTTON6)\r\n\t\t\t\ttemp |= 256;\r\n\r\n\t\t\tif (wParam & MK_XBUTTON7)\r\n\t\t\t\ttemp |= 512;\r\n\r\n\t\t\tif (!vid_initializing)\r\n\t\t\t\tINS_MouseEvent (temp);\r\n\r\n\t\t\tbreak;\r\n\r\n\t\t// JACK: This is the mouse wheel with the Intellimouse\r\n\t\t// Its delta is either positive or neg, and we generate the proper\r\n\t\t// Event.\r\n\t\tcase WM_MOUSEWHEEL:\r\n\t\t\tif (!vid_initializing)\r\n\t\t\t{\r\n\t\t\t\tif ((short) HIWORD(wParam) > 0)\r\n\t\t\t\t{\r\n\t\t\t\t\tKey_Event(0, K_MWHEELUP, 0, true);\r\n\t\t\t\t\tKey_Event(0, K_MWHEELUP, 0, false);\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\tKey_Event(0, K_MWHEELDOWN, 0, true);\r\n\t\t\t\t\tKey_Event(0, K_MWHEELDOWN, 0, false);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\tbreak;\r\n\r\n\t\tcase WM_INPUT:\r\n\t\t\t// raw input handling\r\n\t\t\tif (!vid_initializing)\r\n\t\t\t\tINS_RawInput_Read((HANDLE)lParam);\r\n\t\t\tbreak;\r\n\t\tcase WM_DEVICECHANGE:\r\n\t\t\tCOM_AddWork(WG_MAIN, INS_DeviceChanged, NULL, NULL, uMsg, 0);\r\n\t\t\tlRet = TRUE;\r\n\t\t\tbreak;\r\n\r\n\t\tcase WM_SETCURSOR:\r\n\t\t\t//only use a custom cursor if the cursor is inside the client area\r\n\t\t\tswitch(lParam&0xffff)\r\n\t\t\t{\r\n\t\t\tcase 0:\r\n\t\t\t\tbreak;\r\n\t\t\tcase HTCLIENT:\r\n\t\t\t\tif (hCustomCursor)\t//custom cursor enabled\r\n\t\t\t\t\tSetCursor(hCustomCursor);\r\n\t\t\t\telse\t\t\t\t//fallback on an arrow cursor, just so we have something visible at startup or so\r\n\t\t\t\t\tSetCursor(hArrowCursor);\r\n\t\t\t\tlRet = TRUE;\r\n\t\t\t\tbreak;\r\n\t\t\tdefault:\r\n\t\t\t\tlRet = DefWindowProcW (hWnd, uMsg, wParam, lParam);\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\t\t\tbreak;\r\n\r\n\t\tcase WM_GETMINMAXINFO:\r\n\t\t\t{\r\n\t\t\t\tRECT windowrect;\r\n\t\t\t\tRECT clientrect;\r\n\t\t\t\tMINMAXINFO *mmi = (MINMAXINFO *) lParam;\r\n\r\n\t\t\t\tGetWindowRect (hWnd, &windowrect);\r\n\t\t\t\tGetClientRect (hWnd, &clientrect);\r\n\r\n\t\t\t\tmmi->ptMinTrackSize.x = 320 + ((windowrect.right - windowrect.left) - (clientrect.right - clientrect.left));\r\n\t\t\t\tmmi->ptMinTrackSize.y = 200 + ((windowrect.bottom - windowrect.top) - (clientrect.bottom - clientrect.top));\r\n\t\t\t}\r\n\t\t\treturn 0;\r\n\t\tcase WM_SIZE:\r\n\t\t\td3d_resized = true;\r\n\t\t\tbreak;\r\n\r\n\t\tcase WM_CLOSE:\r\n\t\t\tif (!vid_initializing)\r\n\t\t\t\tif (MessageBox (mainwindow, \"Are you sure you want to quit?\", \"Confirm Exit\",\r\n\t\t\t\t\t\t\tMB_YESNO | MB_SETFOREGROUND | MB_ICONQUESTION) == IDYES)\r\n\t\t\t\t{\r\n\t\t\t\t\tCbuf_AddText(\"\\nquit\\n\", RESTRICT_LOCAL);\r\n\t\t\t\t}\r\n\r\n\t\t\tbreak;\r\n\r\n\t\tcase WM_ACTIVATE:\r\n\t\t\tfActive = LOWORD(wParam);\r\n\t\t\tfMinimized = (BOOL) HIWORD(wParam);\r\n\t\t\tif (!D3D8AppActivate(!(fActive == WA_INACTIVE), fMinimized))\r\n\t\t\t\tbreak;//so, urm, tell me microsoft, what changed?\r\n\t\t\tif (modestate == MS_FULLDIB)\r\n\t\t\t\tShowWindow(mainwindow, SW_SHOWNORMAL);\r\n\r\n\t\t// fix the leftover Alt from any Alt-Tab or the like that switched us away\r\n//\t\t\tClearAllStates ();\r\n\r\n\t\t\tbreak;\r\n\r\n\t\tcase WM_DESTROY:\r\n\t\t{\r\n//\t\t\tif (dibwindow)\r\n//\t\t\t\tDestroyWindow (dibwindow);\r\n\t\t}\r\n\t\tbreak;\r\n\r\n#ifdef HAVE_CDPLAYER\r\n\t\tcase MM_MCINOTIFY:\r\n\t\t\tlRet = CDAudio_MessageHandler (hWnd, uMsg, wParam, lParam);\r\n\t\t\tbreak;\r\n#endif\r\n\r\n\t\tdefault:\r\n\t\t\t/* pass all unhandled messages to DefWindowProc */\r\n\t\t\tlRet = DefWindowProc (hWnd, uMsg, wParam, lParam);\r\n\t\tbreak;\r\n\t}\r\n\r\n\t/* return 1 if handled message, 0 if not */\r\n\treturn lRet;\r\n}\r\n#endif\r\n\r\nstatic void D3D8_VID_SwapBuffers(void)\r\n{\r\n\tIDirect3DDevice8_Present(pD3DDev8, NULL, NULL, NULL, NULL);\r\n}\r\n\r\nstatic void resetD3D8(void)\r\n{\r\n\tHRESULT res;\r\n\tres = IDirect3DDevice8_Reset(pD3DDev8, &d3dpp);\r\n\tif (FAILED(res))\r\n\t{\r\n\t\tCon_Printf(\"IDirect3DDevice8_Reset failed (%u)\\n\", (unsigned)res&0xffff);\r\n\t\treturn;\r\n\t}\r\n\r\n\t/*clear the screen to black as soon as we start up, so there's no lingering framebuffer state*/\r\n\tIDirect3DDevice8_BeginScene(pD3DDev8);\r\n\tIDirect3DDevice8_Clear(pD3DDev8, 0, NULL, D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);\r\n\tIDirect3DDevice8_EndScene(pD3DDev8);\r\n\tIDirect3DDevice8_Present(pD3DDev8, NULL, NULL, NULL, NULL);\r\n\t//IDirect3DDevice8_SetRenderState(pD3DDev8, D3DRENDERSTATE_DITHERENABLE, FALSE);\r\n\t//IDirect3DDevice8_SetRenderState(pD3DDev8, D3DRENDERSTATE_SPECULARENABLE, FALSE);\r\n\t//IDirect3DDevice8_SetRenderState(pD3DDev8, D3DRENDERSTATE_TEXTUREPERSPECTIVE, TRUE);\r\n\tIDirect3DDevice8_SetRenderState(pD3DDev8, D3DRS_LIGHTING, FALSE);\r\n}\r\n\r\nvoid D3D8Shader_Init(void)\r\n{\r\n\tD3DCAPS8 caps;\r\n\r\n\tmemset(&sh_config, 0, sizeof(sh_config));\r\n\tsh_config.minver = 8;\r\n\tsh_config.maxver = 8;\r\n\tsh_config.blobpath = \"hlsl/%s.blob\";\r\n\tsh_config.progpath = NULL;\r\n\tsh_config.shadernamefmt = \"%s_hlsl8\";\r\n\r\n\tsh_config.progs_supported\t= false;\r\n\tsh_config.progs_required\t= false;\r\n\r\n\tsh_config.pDeleteProg\t\t= NULL;//D3D8Shader_DeleteProg;\r\n\tsh_config.pLoadBlob\t\t\t= NULL;//D3D8Shader_LoadBlob;\r\n\tsh_config.pCreateProgram\t= NULL;//D3D8Shader_CreateProgram;\r\n\tsh_config.pProgAutoFields\t= NULL;//D3D8Shader_ProgAutoFields;\r\n\r\n\tsh_config.tex_env_combine\t\t= 1;\r\n\tsh_config.nv_tex_env_combine4\t= 1;\r\n\tsh_config.env_add\t\t\t\t= 1;\r\n\r\n\t//FIXME: check caps\r\n\tsh_config.texfmt[PTI_RGBX8] = true;\t//fixme: shouldn't support\r\n\tsh_config.texfmt[PTI_RGBA8] = true;\t//fixme: shouldn't support\r\n\tsh_config.texfmt[PTI_BGRX8] = true;\r\n\tsh_config.texfmt[PTI_BGRA8] = true;\r\n\tsh_config.texfmt[PTI_RGB565] = true;\r\n\tsh_config.texfmt[PTI_ARGB1555] = true;\r\n\tsh_config.texfmt[PTI_ARGB4444] = true;\r\n\r\n\tsh_config.can_mipcap\t\t= true;\t//at creation time, I think.\r\n\r\n\tIDirect3DDevice8_GetDeviceCaps(pD3DDev8, &caps);\r\n\r\n\tsh_config.texture_allow_block_padding = false;\t//microsoft blocks this.\r\n\tif (caps.TextureCaps & D3DPTEXTURECAPS_POW2)\r\n\t{\t//this flag is a LIMITATION, not a capability.\r\n\t\tsh_config.texture_non_power_of_two = false;\r\n\t\tsh_config.texture_non_power_of_two_pic = !!(caps.TextureCaps & D3DPTEXTURECAPS_NONPOW2CONDITIONAL);\t//pic npot is supported if both flags are set.\r\n\t}\r\n\telse\r\n\t{\t//modern cards support npot\r\n\t\tsh_config.texture_non_power_of_two_pic = true;\r\n\t\tsh_config.texture_non_power_of_two = true;\r\n\t}\r\n\r\n\tsh_config.texture2d_maxsize = min(caps.MaxTextureWidth, caps.MaxTextureHeight);\r\n}\r\n\r\n#if (WINVER < 0x500) && !defined(__GNUC__)\r\ntypedef struct tagMONITORINFO\r\n{\r\n\tDWORD   cbSize;\r\n\tRECT    rcMonitor;\r\n\tRECT    rcWork;\r\n\tDWORD   dwFlags;\r\n} MONITORINFO, *LPMONITORINFO;\r\n#endif\r\n\r\nstatic qboolean initD3D8Device(HWND hWnd, rendererstate_t *info, unsigned int devno, unsigned int devtype)\r\n{\r\n\tint err;\r\n\tRECT rect;\r\n\tD3DADAPTER_IDENTIFIER8 inf;\r\n\tD3DCAPS8 caps;\r\n\tunsigned int cflags;\r\n\r\n\tmemset(&inf, 0, sizeof(inf));\r\n\tif (FAILED(IDirect3D8_GetAdapterIdentifier(pD3D, devno, 0, &inf)))\r\n\t\treturn false;\r\n\r\n\tif (FAILED(IDirect3D8_GetDeviceCaps(pD3D, devno, devtype, &caps)))\r\n\t\treturn false;\r\n\r\n\tmemset(&d3dpp, 0, sizeof(d3dpp));    // clear out the struct for use\r\n\td3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;    // discard old frames\r\n\td3dpp.hDeviceWindow = hWnd;    // set the window to be used by Direct3D\r\n\td3dpp.BackBufferWidth = info->width;\r\n\td3dpp.BackBufferHeight = info->height;\r\n\td3dpp.MultiSampleType = info->multisample;\r\n\td3dpp.BackBufferCount = 1 + info->triplebuffer;\r\n\td3dpp.FullScreen_RefreshRateInHz = info->fullscreen?info->rate:0;\t//don't pass a rate if not fullscreen, d3d doesn't like it.\r\n\td3dpp.Windowed = !info->fullscreen;\r\n\r\n\td3dpp.EnableAutoDepthStencil = true;\r\n\td3dpp.AutoDepthStencilFormat = (info->depthbits==16)?D3DFMT_D16:D3DFMT_D24S8;;\r\n\td3dpp.BackBufferFormat = D3DFMT_UNKNOWN;\r\n\tif (info->bpp == 16)\r\n\t\td3dpp.BackBufferFormat = D3DFMT_R5G6B5;\r\n\telse\r\n\t\td3dpp.BackBufferFormat = D3DFMT_X8R8G8B8;\r\n\r\n\tif (d3dpp.Windowed)\r\n\t\td3dpp.FullScreen_PresentationInterval = 0;\r\n\telse switch(info->wait)\r\n\t{\r\n\tdefault:\r\n\t\td3dpp.FullScreen_PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT;\r\n\t\tbreak;\r\n\tcase 0:\r\n\t\td3dpp.FullScreen_PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;\r\n\t\tbreak;\r\n\tcase 1:\r\n\t\td3dpp.FullScreen_PresentationInterval = D3DPRESENT_INTERVAL_ONE;\r\n\t\tbreak;\r\n\tcase 2:\r\n\t\td3dpp.FullScreen_PresentationInterval = D3DPRESENT_INTERVAL_TWO;\r\n\t\tbreak;\r\n\tcase 3:\r\n\t\td3dpp.FullScreen_PresentationInterval = D3DPRESENT_INTERVAL_THREE;\r\n\t\tbreak;\r\n\tcase 4:\r\n\t\td3dpp.FullScreen_PresentationInterval = D3DPRESENT_INTERVAL_FOUR;\r\n\t\tbreak;\r\n\t}\r\n\r\n#ifdef _XBOX\r\n\tcflags = 0;\r\n#else \r\n\tcflags = D3DCREATE_FPU_PRESERVE;\r\n#endif\r\n\tif ((caps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT) && (caps.DevCaps & D3DDEVCAPS_PUREDEVICE))\r\n\t\tcflags |= D3DCREATE_HARDWARE_VERTEXPROCESSING;\r\n\telse\r\n\t\tcflags |= D3DCREATE_SOFTWARE_VERTEXPROCESSING;\r\n\t//cflags |= D3DCREATE_DISABLE_DRIVER_MANAGEMENT;\r\n\r\n\tpD3DDev8 = NULL;\r\n\t// create a device class using this information and information from the d3dpp stuct\r\n\terr = IDirect3D8_CreateDevice(pD3D,\r\n\t\t\tdevno,\r\n\t\t\tdevtype,\r\n\t\t\thWnd,\r\n\t\t\tcflags,\r\n\t\t\t&d3dpp,\r\n\t\t\t&pD3DDev8);\r\n\r\n\tif (pD3DDev8)\r\n\t{\r\n#ifndef _XBOX\r\n\t\tHMONITOR hm;\r\n\t\tMONITORINFO mi;\r\n\t\tchar *s;\r\n\t\tfor (s = inf.Description + strlen(inf.Description)-1; s >= inf.Description && *s <= ' '; s--)\r\n\t\t\t*s = 0;\r\n\t\tCon_Printf(\"D3D8 Driver: %s\\n\", inf.Description);\r\n\r\n\t\tvid.numpages = d3dpp.BackBufferCount;\r\n\r\n\t\tif (d3dpp.Windowed)\t//fullscreen we get positioned automagically.\r\n\t\t{\t\t\t\t\t//windowed, we get positioned at 0,0... which is often going to be on the wrong screen\r\n\t\t\t\t\t\t\t//the user can figure it out from here\r\n\t\t\tstatic HANDLE huser32;\r\n\t\t\tBOOL (WINAPI *pGetMonitorInfoA)(HMONITOR hMonitor, LPMONITORINFO lpmi);\r\n\t\t\tif (!huser32)\r\n\t\t\t\thuser32 = LoadLibrary(\"user32.dll\");\r\n\t\t\tif (!huser32)\r\n\t\t\t\treturn false;\r\n\t\t\tpGetMonitorInfoA = (void*)GetProcAddress(huser32, \"GetMonitorInfoA\");\r\n\t\t\tif (!pGetMonitorInfoA)\r\n\t\t\t\treturn false;\r\n\r\n\t\t\thm = IDirect3D8_GetAdapterMonitor(pD3D, devno);\r\n\t\t\tmemset(&mi, 0, sizeof(mi));\r\n\t\t\tmi.cbSize = sizeof(mi);\r\n\t\t\tpGetMonitorInfoA(hm, &mi);\r\n\t\t\trect.left = mi.rcWork.left + ((mi.rcWork.right - mi.rcWork.left) - info->width) / 2;\r\n\t\t\trect.top = mi.rcWork.top + ((mi.rcWork.bottom - mi.rcWork.top) - info->height) / 2;\r\n\t\t\trect.right = rect.left+d3dpp.BackBufferWidth;\r\n\t\t\trect.bottom = rect.top+d3dpp.BackBufferHeight;\r\n\t\t\tAdjustWindowRectEx(&rect, WS_OVERLAPPEDWINDOW, FALSE, 0);\r\n\t\t\tMoveWindow(d3dpp.hDeviceWindow, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, false);\r\n\t\t}\r\n#endif\r\n\t\tD3D8Shader_Init();\r\n\t\treturn true;\t//successful\r\n\t}\r\n\telse\r\n\t{\r\n\t\tchar *s;\r\n\t\tswitch(err)\r\n\t\t{\r\n\t\tdefault: s = \"Unkown error\"; break;\r\n\t\tcase D3DERR_DEVICELOST: s = \"Device lost\"; break;\r\n\t\tcase D3DERR_INVALIDCALL: s = \"Invalid call\"; break;\r\n\t\tcase D3DERR_NOTAVAILABLE: s = \"Not available\"; break;\r\n\t\tcase D3DERR_OUTOFVIDEOMEMORY: s = \"Out of video memory\"; break;\r\n\t\t}\r\n\t\tCon_Printf(\"IDirect3D8_CreateDevice failed: %s.\\n\", s);\r\n\t}\r\n\treturn false;\r\n}\r\n\r\nstatic void initD3D8(HWND hWnd, rendererstate_t *info)\r\n{\r\n#ifdef _XBOX\r\n\tpD3D = Direct3DCreate8( D3D_SDK_VERSION );\r\n\tinitD3D8Device(NULL, info, 0, D3DDEVTYPE_HAL);\r\n#else\r\n\tint i;\r\n\tint numadaptors;\r\n\tD3DADAPTER_IDENTIFIER8 inf;\r\n\r\n\tstatic HMODULE d3d8dll;\r\n\tLPDIRECT3D8 (WINAPI *pDirect3DCreate8) (int version);\r\n\r\n\r\n\tif (!d3d8dll)\r\n\t\td3d8dll = LoadLibrary(\"d3d8.dll\");\r\n\tif (!d3d8dll)\r\n\t{\r\n\t\tCon_Printf(CON_ERROR \"Direct3d 8 does not appear to be installed\\n\");\r\n\t\treturn;\r\n\t}\r\n\tpDirect3DCreate8 = (void*)GetProcAddress(d3d8dll, \"Direct3DCreate8\");\r\n\tif (!pDirect3DCreate8)\r\n\t{\r\n\t\tCon_Printf(CON_ERROR \"Direct3d 8 does not appear to be installed properly\\n\");\r\n\t\treturn;\r\n\t}\r\n\r\n\tpD3D = pDirect3DCreate8(D3D_SDK_VERSION);    // create the Direct3D interface\r\n\tif (!pD3D)\r\n\t\treturn;\r\n\r\n\tnumadaptors = IDirect3D8_GetAdapterCount(pD3D);\r\n\tfor (i = 0; i < numadaptors; i++)\r\n\t{\t//NVIDIA's debug app requires that we use a specific device\r\n\t\tmemset(&inf, 0, sizeof(inf));\r\n\t\tIDirect3D8_GetAdapterIdentifier(pD3D, i, 0, &inf);\r\n\t\tif (strstr(inf.Description, \"PerfHUD\"))\r\n\t\t\tif (initD3D8Device(hWnd, info, i, D3DDEVTYPE_REF))\r\n\t\t\t\treturn;\r\n\t}\r\n\tfor (i = 0; i < numadaptors; i++)\r\n\t{\t//try each adaptor in turn until we get one that actually works\r\n\t\tif (initD3D8Device(hWnd, info, i, D3DDEVTYPE_HAL))\r\n\t\t\treturn;\r\n\t}\r\n\tfor (i = 0; i < numadaptors; i++)\r\n\t{\t//try each adaptor in turn until we get one that actually works\r\n\t\tif (initD3D8Device(hWnd, info, i, D3DDEVTYPE_SW))\r\n\t\t\treturn;\r\n\t}\r\n\tfor (i = 0; i < numadaptors; i++)\r\n\t{\t//try each adaptor in turn until we get one that actually works\r\n\t\tif (initD3D8Device(hWnd, info, i, D3DDEVTYPE_REF))\r\n\t\t\treturn;\r\n\t}\r\n#endif\r\n}\r\n\r\nstatic qboolean D3D8_VID_Init(rendererstate_t *info, unsigned char *palette)\r\n{\r\n#ifdef _XBOX\r\n\tvid_initializing = true;\r\n\r\n\tinitD3D8( NULL, info );\r\n\r\n\tIDirect3DDevice8_Clear(pD3DDev8, 0, NULL, D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(255, 0, 255), 1.0f, 0);\r\n\tIDirect3DDevice8_BeginScene(pD3DDev8);\r\n\tIDirect3DDevice8_EndScene(pD3DDev8);\r\n\tIDirect3DDevice8_Present(pD3DDev8, NULL, NULL, NULL, NULL);\r\n\r\n\tD3D8_Set2D();\r\n\r\n\tvid.pixelwidth = 640;\r\n\tvid.pixelheight = 480;\r\n\tvid.width = 640;\r\n\tvid.height = 480;\r\n\tvid.srgb = false;\r\n\r\n\tvid_initializing = false;\r\n\r\n\tIDirect3DDevice8_SetRenderState(pD3DDev8, D3DRS_LIGHTING, FALSE);\r\n\r\n\treturn TRUE;\r\n#else\r\n\tDWORD width = info->width;\r\n\tDWORD height = info->height;\r\n\t//DWORD bpp = info->bpp;\r\n\t//DWORD zbpp = 16;\r\n\t//DWORD flags = 0;\r\n\tDWORD wstyle;\r\n\tRECT rect;\r\n\tMSG msg;\r\n\tHICON hIcon = LoadIcon (global_hInstance, MAKEINTRESOURCE (IDI_ICON1));\r\n\r\n\t//DDGAMMARAMP gammaramp;\r\n\t//int i;\r\n\r\n\tchar *CLASSNAME = \"FTED3D8QUAKE\";\r\n\tWNDCLASS wc = {\r\n\t\t0,\r\n\t\t&D3D8_WindowProc,\r\n\t\t0,\r\n\t\t0,\r\n\t\tNULL,\r\n\t\thIcon,\r\n\t\tNULL,\r\n\t\tNULL,\r\n\t\tNULL,\r\n\t\tCLASSNAME\r\n\t};\r\n\r\n\twc.hCursor       = hArrowCursor = LoadCursor (NULL,IDC_ARROW);\r\n\r\n\tvid_initializing = true;\r\n\r\n\tRegisterClass(&wc);\r\n\r\n\tif (info->fullscreen)\r\n\t\twstyle = 0;\r\n\telse\r\n\t\twstyle = WS_OVERLAPPEDWINDOW;\r\n\r\n\trect.left = (GetSystemMetrics(SM_CXSCREEN) - info->width) / 2;\r\n\trect.top = (GetSystemMetrics(SM_CYSCREEN) - info->height) / 2;\r\n\trect.right = rect.left+info->width;\r\n\trect.bottom = rect.top+info->height;\r\n\tAdjustWindowRectEx(&rect, wstyle, FALSE, 0);\r\n\tmainwindow = CreateWindow(CLASSNAME, \"Direct3D9\", wstyle, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, NULL, NULL, NULL, NULL);\r\n\r\n\t// Try as specified.\r\n\r\n\tinitD3D8(mainwindow, info);\r\n\tif (!pD3DDev8)\r\n\t{\r\n\t\tCon_Printf(\"No suitable D3D8 device found\\n\");\r\n\t\treturn false;\r\n\t}\r\n\r\n\tCL_UpdateWindowTitle();\r\n\r\n\twhile (PeekMessage(&msg, NULL,  0, 0, PM_REMOVE))\r\n\t{\r\n\t\tTranslateMessage(&msg);\r\n\t\tDispatchMessage(&msg);\r\n\t}\r\n\r\n\tShowWindow(mainwindow, SW_NORMAL);\r\n\r\n\tIDirect3DDevice8_Clear(pD3DDev8, 0, NULL, D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);\r\n\tIDirect3DDevice8_BeginScene(pD3DDev8);\r\n\tIDirect3DDevice8_EndScene(pD3DDev8);\r\n\tIDirect3DDevice8_Present(pD3DDev8, NULL, NULL, NULL, NULL);\r\n\r\n\tD3D8_Set2D();\r\n\r\n\r\n\r\n//\tpD3DX->lpVtbl->GetBufferSize((void*)pD3DX, &width, &height);\r\n\tvid.pixelwidth = width;\r\n\tvid.pixelheight = height;\r\n\r\n\tvid.width = width;\r\n\tvid.height = height;\r\n\r\n\tvid_initializing = false;\r\n\r\n\tIDirect3DDevice8_SetRenderState(pD3DDev8, D3DRS_LIGHTING, FALSE);\r\n\r\n\tGetWindowRect(mainwindow, &window_rect);\r\n\r\n\r\n//\tD3D8_VID_GenPaletteTables(palette);\r\n\r\n\t{\r\n\t\textern qboolean\tmouseactive;\r\n\t\tmouseactive = false;\r\n\t}\r\n\r\n//\tD3D8BE_Reset(false);\r\n\r\n\t//FIXME: old hardware is not guarenteed to support hardware cursors.\r\n\t//this should not be a problem on dx9+ hardware, but might on earlier stuff.\r\n\trf->VID_CreateCursor = WIN_CreateCursor;\r\n\trf->VID_DestroyCursor = WIN_DestroyCursor;\r\n\trf->VID_SetCursor = WIN_SetCursor;\r\n\r\n\treturn true;\r\n#endif\r\n}\r\n\r\nstatic void\t (D3D8_VID_DeInit)\t\t\t\t(void)\r\n{\r\n\tImage_Shutdown();\r\n\r\n\t/*final shutdown, kill the video stuff*/\r\n\tif (pD3DDev8)\r\n\t{\r\n\t\tD3D8BE_Reset(true);\r\n\r\n\t\t/*try and knock it back into windowed mode to avoid d3d bugs*/\r\n\t\t#ifndef _XBOX\r\n\t\td3dpp.Windowed = true;\r\n\t\t#endif\r\n\t\tIDirect3DDevice8_Reset(pD3DDev8, &d3dpp);\r\n\r\n\t\tIDirect3DDevice8_Release(pD3DDev8);\r\n\t\tpD3DDev8 = NULL;\r\n\t}\r\n\tif (pD3D)\r\n\t{\r\n\t\tIDirect3D8_Release(pD3D);\r\n\t\tpD3D = NULL;\r\n\t}\r\n\r\n\t#ifndef _XBOX\r\n\tif (mainwindow)\r\n\t{\r\n\t\tDestroyWindow(mainwindow);\r\n\t\tmainwindow = NULL;\r\n\t}\r\n\t#endif\r\n//\tCvar_Unhook(&v_gamma);\r\n//\tCvar_Unhook(&v_contrast);\r\n//\tCvar_Unhook(&v_brightness);\r\n}\r\n\r\nqboolean D3D8_VID_ApplyGammaRamps\t\t(unsigned int gammarampsize, unsigned short *ramps)\r\n{\r\n\tif (d3dpp.Windowed)\r\n\t\treturn false;\r\n\tif (pD3DDev8 && ramps && gammarampsize == 256)\r\n\t\tIDirect3DDevice8_SetGammaRamp(pD3DDev8, D3DSGR_NO_CALIBRATION, (D3DGAMMARAMP *)ramps);\r\n\treturn true;\r\n}\r\nstatic char\t*(D3D8_VID_GetRGBInfo)\t\t\t(int *bytestride, int *truevidwidth, int *truevidheight, enum uploadfmt *fmt)\r\n{\r\n//FIXME: no screenshots\r\n\treturn NULL;\r\n#ifdef FIXME\r\n\tIDirect3DSurface8 *backbuf, *surf;\r\n\tD3DLOCKED_RECT rect;\r\n\tD3DSURFACE_DESC desc;\r\n\tint i, j, c;\r\n\tqbyte *ret = NULL;\r\n\tqbyte *p;\r\n\r\n\t/*DON'T read the front buffer.\r\n\tthis function can be used by the quakeworld remote screenshot 'snap' feature,\r\n\tso DO NOT read the frontbuffer because it can show other information than just quake to third parties*/\r\n\r\n\tif (!FAILED(IDirect3DDevice8_GetRenderTarget(pD3DDev8, 0, &backbuf)))\r\n\t{\r\n\t\tif (!FAILED(IDirect3DSurface8_GetDesc(backbuf, &desc)))\r\n\t\tif (desc.Format == D3DFMT_X8R8G8B8 || desc.Format == D3DFMT_A8R8G8B8)\r\n\t\tif (!FAILED(IDirect3DDevice8_CreateOffscreenPlainSurface(pD3DDev8,\r\n\t\t\t\t\tdesc.Width, desc.Height, desc.Format,\r\n\t\t\t\t\tD3DPOOL_SYSTEMMEM, &surf, NULL))\r\n\t\t\t)\r\n\t\t{\r\n\r\n\t\t\tif (!FAILED(IDirect3DDevice8_GetRenderTargetData(pD3DDev8, backbuf, surf)))\r\n\t\t\tif (!FAILED(IDirect3DSurface8_LockRect(surf, &rect, NULL, D3DLOCK_NO_DIRTY_UPDATE|D3DLOCK_READONLY|D3DLOCK_NOSYSLOCK)))\r\n\t\t\t{\r\n\t\t\t\tret = BZ_Malloc(desc.Width*desc.Height*3);\r\n\t\t\t\tif (ret)\r\n\t\t\t\t{\r\n\t\t\t\t\t*fmt = TF_RGB24;\r\n\r\n\t\t\t\t\t// read surface rect and convert 32 bgra to 24 rgb and flip\r\n\t\t\t\t\t//FIXME: shouldn't need to convert+flip any more. just return it as upside-down bgra or whatever\r\n\t\t\t\t\tc = desc.Width*desc.Height*3;\r\n\t\t\t\t\tp = (qbyte *)rect.pBits;\r\n\r\n\t\t\t\t\tfor (i=c-(3*desc.Width); i>=0; i-=(3*desc.Width))\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tfor (j=0; j<desc.Width; j++)\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tret[i+j*3+0] = p[j*4+2];\r\n\t\t\t\t\t\t\tret[i+j*3+1] = p[j*4+1];\r\n\t\t\t\t\t\t\tret[i+j*3+2] = p[j*4+0];\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\tp += rect.Pitch;\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\t*bytestride = desc.Width*3;\r\n\t\t\t\t\t*truevidwidth = desc.Width;\r\n\t\t\t\t\t*truevidheight = desc.Height;\r\n\t\t\t\t}\r\n\r\n\t\t\t\tIDirect3DSurface8_UnlockRect(surf);\r\n\t\t\t}\r\n\t\t\tIDirect3DSurface8_Release(surf);\r\n\t\t}\r\n\t\tIDirect3DSurface8_Release(backbuf);\r\n\t}\r\n\r\n\treturn ret;\r\n#endif\r\n}\r\nstatic void\t(D3D8_VID_SetWindowCaption)\t\t(const char *msg)\r\n{\r\n#ifndef _XBOX\r\n\tSetWindowText(mainwindow, msg);\r\n#endif\r\n}\r\n\r\nvoid D3D8_Set2D (void)\r\n{\r\n\tfloat m[16];\r\n\tD3DVIEWPORT8 vport;\r\n//\tIDirect3DDevice8_EndScene(pD3DDev8);\r\n\r\n\tMatrix4x4_CM_OrthographicD3D(m, 0 + (0.5*vid.width/vid.pixelwidth), vid.width + (0.5*vid.width/vid.pixelwidth), 0 + (0.5*vid.height/vid.pixelheight), vid.height + (0.5*vid.height/vid.pixelheight), 0, 100);\r\n\tIDirect3DDevice8_SetTransform(pD3DDev8, D3DTS_PROJECTION, (D3DMATRIX*)m);\r\n\r\n\tMatrix4x4_Identity(m);\r\n\tIDirect3DDevice8_SetTransform(pD3DDev8, D3DTS_WORLD, (D3DMATRIX*)m);\r\n\r\n\tMatrix4x4_Identity(m);\r\n\tIDirect3DDevice8_SetTransform(pD3DDev8, D3DTS_VIEW, (D3DMATRIX*)m);\r\n\r\n\tvport.X = 0;\r\n\tvport.Y = 0;\r\n\tvport.Width = vid.pixelwidth;\r\n\tvport.Height = vid.pixelheight;\r\n\tvport.MinZ = 0;\r\n\tvport.MaxZ = 1;\r\n\tIDirect3DDevice8_SetViewport(pD3DDev8, &vport);\r\n\r\n\r\n\tvid.fbvwidth = vid.width;\r\n\tvid.fbvheight = vid.height;\r\n\tvid.fbpwidth = vid.pixelwidth;\r\n\tvid.fbpheight = vid.pixelheight;\r\n\r\n\tr_refdef.pxrect.x = 0;\r\n\tr_refdef.pxrect.y = 0;\r\n\tr_refdef.pxrect.width = vid.fbpwidth;\r\n\tr_refdef.pxrect.height = vid.fbpheight;\r\n\r\n\tD3D8BE_Set2D();\r\n}\r\n\r\nstatic int d3d8error(int i)\r\n{\r\n\tif (FAILED(i))// != D3D_OK)\r\n\t\tCon_Printf(\"D3D error: %i\\n\", i);\r\n\treturn i;\r\n}\r\n\r\nstatic qboolean\t(D3D8_SCR_UpdateScreen)\t\t\t(void)\r\n{\r\n\t//extern int keydown[];\r\n\t//extern cvar_t vid_conheight;\r\n\tint uimenu;\r\n#ifdef TEXTEDITOR\r\n\t//extern qboolean editormodal;\r\n#endif\r\n\tqboolean nohud, noworld;\r\n\r\n\tif (!scr_initialized || !con_initialized)\r\n\t{\r\n\t\treturn false;                         // not initialized yet\r\n\t}\r\n\r\n\tif (d3d_resized && d3dpp.Windowed)\r\n\t{\r\n\t\textern cvar_t vid_conautoscale, vid_conwidth;\r\n\t\td3d_resized = false;\r\n\r\n\t\t// force width/height to be updated\r\n\t\t//vid.pixelwidth = window_rect.right - window_rect.left;\r\n\t\t//vid.pixelheight = window_rect.bottom - window_rect.top;\r\n\t\tD3DVID_UpdateWindowStatus(mainwindow);\r\n\r\n\t\tD3D8BE_Reset(true);\r\n\t\tvid.pixelwidth = d3dpp.BackBufferWidth = window_rect.right - window_rect.left;\r\n\t\tvid.pixelheight = d3dpp.BackBufferHeight = window_rect.bottom - window_rect.top;\r\n\t\tresetD3D8();\r\n\t\tD3D8BE_Reset(false);\r\n\r\n\t\tCvar_ForceCallback(&vid_conautoscale);\r\n\t\tCvar_ForceCallback(&vid_conwidth);\r\n\t}\r\n\r\n#ifndef _XBOX\r\n\tswitch (IDirect3DDevice8_TestCooperativeLevel(pD3DDev8))\r\n\t{\r\n\tcase D3DERR_DEVICELOST:\r\n\t\t//the user has task switched away from us or something, don't draw anything until they switch back to us\r\n\t\tD3D8BE_Reset(true);\r\n\t\treturn false;\r\n\tcase D3DERR_DEVICENOTRESET:\r\n\t\tD3D8BE_Reset(true);\r\n\t\tresetD3D8();\r\n\t\tif (FAILED(IDirect3DDevice8_TestCooperativeLevel(pD3DDev8)))\r\n\t\t{\r\n\t\t\tCon_Printf(\"Device lost, restarting video\\n\");\r\n\t\t\tCmd_ExecuteString(\"vid_restart\", RESTRICT_LOCAL);\r\n\t\t\treturn false;\r\n\t\t}\r\n\r\n\t\tCvar_ForceCallback(&v_gamma);\r\n\t\tbreak;\r\n\tdefault:\r\n\t\tbreak;\r\n\t}\r\n#endif\r\n\r\n\tD3D8BE_Reset(false);\r\n\r\n\tif (r_clear.ival)\r\n\t{\r\n\t\td3d8error(IDirect3DDevice8_Clear(pD3DDev8, 0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB((r_clear.ival&1)?255:0,(r_clear.ival&2)?255:0,(r_clear.ival&4)?255:0), 1, 0));\r\n\t}\r\n\r\n\tif (scr_disabled_for_loading)\r\n\t{\r\n\t\textern float scr_disabled_time;\r\n\t\tif (Sys_DoubleTime() - scr_disabled_time > 60 || Key_Dest_Has(~kdm_game))\r\n\t\t{\r\n\t\t\tscr_disabled_for_loading = false;\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tIDirect3DDevice8_BeginScene(pD3DDev8);\r\n\t\t\tscr_drawloading = true;\r\n\t\t\tSCR_DrawLoading (true);\r\n\t\t\tscr_drawloading = false;\r\n\t\t\tif (R2D_Flush)\r\n\t\t\t\tR2D_Flush();\r\n\t\t\tIDirect3DDevice8_EndScene(pD3DDev8);\r\n\t\t\tIDirect3DDevice8_Present(pD3DDev8, NULL, NULL, NULL, NULL);\r\n\t\t\treturn true;\r\n\t\t}\r\n\t}\r\n\r\n\tShader_DoReload();\r\n\r\n#ifdef VM_UI\r\n\tuimenu = UI_MenuState();\r\n#else\r\n\tuimenu = 0;\r\n#endif\r\n\r\n\td3d8error(IDirect3DDevice8_BeginScene(pD3DDev8));\r\n/*\r\n#ifdef TEXTEDITOR\r\n\tif (editormodal)\r\n\t{\r\n\t\tEditor_Draw();\r\n\t\tV_UpdatePalette (false);\r\n\t\tMedia_RecordFrame();\r\n\t\tR2D_BrightenScreen();\r\n\r\n\t\tif (key_dest == key_console)\r\n\t\t\tCon_DrawConsole(vid_conheight.value/2, false);\r\n\t\tGL_EndRendering ();\r\n\t\tGL_DoSwap();\r\n\t\tRSpeedEnd(RSPEED_TOTALREFRESH);\r\n\t\treturn true;\r\n\t}\r\n#endif\r\n*/\r\n\r\n//\r\n// do 3D refresh drawing, and then update the screen\r\n//\r\n\tSCR_SetUpToDrawConsole ();\r\n\r\n\tnoworld = false;\r\n\tnohud = false;\r\n\r\n\tD3D8_Set2D();\r\n\r\n\tif (topmenu && topmenu->isopaque)\r\n\t\tnohud = true;\r\n#ifdef VM_CG\r\n\telse if (CG_Refresh())\r\n\t\tnohud = true;\r\n#endif\r\n#ifdef CSQC_DAT\r\n\telse if (CSQC_DrawView())\r\n\t\tnohud = true;\r\n#endif\r\n\telse if (uimenu != 1)\r\n\t{\r\n\t\tif (r_worldentity.model && cls.state == ca_active)\r\n\t\t\tV_RenderView (nohud);\r\n\t\telse\r\n\t\t\tnoworld = true;\r\n\t}\r\n\r\n\tR2D_BrightenScreen();\r\n\r\n\tscr_con_forcedraw = false;\r\n\tif (noworld)\r\n\t{\r\n\t\tif (scr_con_current != vid.height)\r\n\t\t\tR2D_ConsoleBackground(0, vid.height, true);\r\n\t\telse\r\n\t\t\tscr_con_forcedraw = true;\r\n\r\n\t\tnohud = true;\r\n\t}\r\n\r\n\tSCR_DrawTwoDimensional(uimenu, nohud);\r\n\r\n\tV_UpdatePalette (false);\r\n\tMedia_RecordFrame();\r\n\r\n\tRSpeedShow();\r\n\r\n\tif (R2D_Flush)\r\n\t\tR2D_Flush();\r\n\r\n\td3d8error(IDirect3DDevice8_EndScene(pD3DDev8));\r\n\t{\r\n\t\tRSpeedMark();\r\n\t\td3d8error(IDirect3DDevice8_Present(pD3DDev8, NULL, NULL, NULL, NULL));\r\n\t\tRSpeedEnd(RSPEED_PRESENT);\r\n\t}\r\n\r\n\twindow_center_x = (window_rect.left + window_rect.right)/2;\r\n\twindow_center_y = (window_rect.top + window_rect.bottom)/2;\r\n\r\n\r\n\tINS_UpdateGrabs(modestate != MS_WINDOWED, vid.activeapp);\r\n\treturn true;\r\n}\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\nstatic void\t(D3D8_Draw_Init)\t\t\t\t(void)\r\n{\r\n\tR2D_Init();\r\n}\r\nstatic void\t(D3D8_Draw_Shutdown)\t\t\t\t(void)\r\n{\r\n\tR2D_Shutdown();\r\n\tImage_Shutdown();\r\n}\r\n\r\nstatic void\t(D3D8_R_Init)\t\t\t\t\t(void)\r\n{\r\n}\r\nstatic void\t(D3D8_R_DeInit)\t\t\t\t\t(void)\r\n{\r\n\tR_GAliasFlushSkinCache(true);\r\n\tSurf_DeInit();\r\n\tShader_Shutdown();\r\n}\r\n\r\n\r\n\r\nstatic void D3D8_SetupViewPortProjection(void)\r\n{\r\n\tint\t\tx, x2, y2, y, w, h;\r\n\r\n\tfloat fov_x, fov_y;\r\n\r\n\tD3DVIEWPORT8 vport;\r\n\r\n\tAngleVectors (r_refdef.viewangles, vpn, vright, vup);\r\n\tVectorCopy (r_refdef.vieworg, r_origin);\r\n\r\n\t//\r\n\t// set up viewpoint\r\n\t//\r\n\tx = r_refdef.vrect.x * vid.pixelwidth/(int)vid.width;\r\n\tx2 = (r_refdef.vrect.x + r_refdef.vrect.width) * vid.pixelwidth/(int)vid.width;\r\n\ty = (r_refdef.vrect.y) * vid.pixelheight/(int)vid.height;\r\n\ty2 = ((int)(r_refdef.vrect.y + r_refdef.vrect.height)) * vid.pixelheight/(int)vid.height;\r\n\r\n\t// fudge around because of frac screen scale\r\n\tif (x > 0)\r\n\t\tx--;\r\n\tif (x2 < vid.pixelwidth)\r\n\t\tx2++;\r\n\tif (y < 0)\r\n\t\ty--;\r\n\tif (y2 < vid.pixelheight)\r\n\t\ty2++;\r\n\r\n\tw = x2 - x;\r\n\th = y2 - y;\r\n\r\n\tvport.X = x;\r\n\tvport.Y = y;\r\n\tvport.Width = w;\r\n\tvport.Height = h;\r\n\tvport.MinZ = 0;\r\n\tvport.MaxZ = 1;\r\n\tIDirect3DDevice8_SetViewport(pD3DDev8, &vport);\r\n\r\n\tfov_x = r_refdef.fov_x;//+sin(cl.time)*5;\r\n\tfov_y = r_refdef.fov_y;//-sin(cl.time+1)*5;\r\n\r\n\tif ((r_refdef.flags & RDF_UNDERWATER) && !(r_refdef.flags & RDF_WATERWARP))\r\n\t{\r\n\t\tfov_x *= 1 + (((sin(cl.time * 4.7) + 1) * 0.015) * r_waterwarp.value);\r\n\t\tfov_y *= 1 + (((sin(cl.time * 3.0) + 1) * 0.015) * r_waterwarp.value);\r\n\t}\r\n\r\n\t/*view matrix*/\r\n\tMatrix4x4_CM_ModelViewMatrixFromAxis(r_refdef.m_view, vpn, vright, vup, r_refdef.vieworg);\r\n\td3d8error(IDirect3DDevice8_SetTransform(pD3DDev8, D3DTS_VIEW, (D3DMATRIX*)r_refdef.m_view));\r\n\r\n\tif (r_refdef.maxdist)\r\n\t{\r\n\t\t/*d3d projection matricies scale depth to 0 to 1*/\r\n\t\tMatrix4x4_CM_Projection_Far(d3d_trueprojection, fov_x, fov_y, r_refdef.mindist, r_refdef.maxdist, true);\r\n\t\t/*ogl projection matricies scale depth to -1 to 1, and I would rather my code used consistant culling*/\r\n\t\tMatrix4x4_CM_Projection_Far(r_refdef.m_projection_std, fov_x, fov_y, r_refdef.mindist, r_refdef.maxdist, false);\r\n\t}\r\n\telse\r\n\t{\r\n\t\t/*d3d projection matricies scale depth to 0 to 1*/\r\n\t\tMatrix4x4_CM_Projection_Inf(d3d_trueprojection, fov_x, fov_y, r_refdef.mindist, true);\r\n\t\t/*ogl projection matricies scale depth to -1 to 1, and I would rather my code used consistant culling*/\r\n\t\tMatrix4x4_CM_Projection_Inf(r_refdef.m_projection_std, fov_x, fov_y, r_refdef.mindist, false);\r\n\t}\r\n\r\n\td3d8error(IDirect3DDevice8_SetTransform(pD3DDev8, D3DTS_PROJECTION, (D3DMATRIX*)d3d_trueprojection));\r\n}\r\n\r\nstatic void\t(D3D8_R_RenderView)\t\t\t\t(void)\r\n{\r\n\tif (!r_norefresh.value)\r\n\t{\r\n\t\tSurf_SetupFrame();\r\n\r\n\t\t//check if we can do underwater warp\r\n\t\tif (cls.protocol != CP_QUAKE2)\t//quake2 tells us directly\r\n\t\t{\r\n\t\t\tif (r_viewcontents & FTECONTENTS_FLUID)\r\n\t\t\t\tr_refdef.flags |= RDF_UNDERWATER;\r\n\t\t\telse\r\n\t\t\t\tr_refdef.flags &= ~RDF_UNDERWATER;\r\n\t\t}\r\n\t\tif (r_refdef.flags & RDF_UNDERWATER)\r\n\t\t{\r\n\t\t\textern cvar_t r_projection;\r\n\t\t\tif (!r_waterwarp.value || r_projection.ival)\r\n\t\t\t\tr_refdef.flags &= ~RDF_UNDERWATER;\t//no warp at all\r\n\t//\t\telse if (r_waterwarp.value > 0 && scenepp_waterwarp)\r\n\t//\t\t\tr_refdef.flags |= RDF_WATERWARP;\t//try fullscreen warp instead if we can\r\n\t\t}\r\n\r\n\t\tD3D8_SetupViewPortProjection();\r\n\r\n\t//\tif (r_clear.ival && !(r_refdef.flags & RDF_NOWORLDMODEL))\r\n\t//\t\td3d8error(IDirect3DDevice8_Clear(pD3DDev8, 0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(255,0,0), 1, 0));\r\n\t//\telse\r\n\t\t\td3d8error(IDirect3DDevice8_Clear(pD3DDev8, 0, NULL, D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0,0,0), 1, 0));\r\n\r\n\t\tR_SetFrustum (r_refdef.m_projection_std, r_refdef.m_view);\r\n\t\tRQ_BeginFrame();\r\n\t\tif (!(r_refdef.flags & RDF_NOWORLDMODEL))\r\n\t\t{\r\n\t\t\tif (cl.worldmodel)\r\n\t\t\t\tP_DrawParticles ();\r\n\t\t}\r\n\t\tSurf_DrawWorld();\r\n\t\tRQ_RenderBatchClear();\r\n\t}\r\n\tD3D8_Set2D ();\r\n}\r\n\r\nvoid\t(D3D8_R_PreNewMap)\t\t\t\t(void);\r\n\r\nvoid\t(D3D8_R_PushDlights)\t\t\t(void);\r\nvoid\t(D3D8_R_AddStain)\t\t\t\t(vec3_t org, float red, float green, float blue, float radius);\r\nvoid\t(D3D8_R_LessenStains)\t\t\t(void);\r\n\r\nqboolean (D3D8_VID_Init)\t\t\t\t(rendererstate_t *info, unsigned char *palette);\r\nvoid\t (D3D8_VID_DeInit)\t\t\t\t(void);\r\nvoid\t(D3D8_VID_SetPalette)\t\t\t(unsigned char *palette);\r\nvoid\t(D3D8_VID_ShiftPalette)\t\t\t(unsigned char *palette);\r\nvoid\t(D3D8_VID_SetWindowCaption)\t\t(const char *msg);\r\n\r\nvoid D3D8BE_RenderToTextureUpdate2d(qboolean destchanged)\r\n{\r\n}\r\n\r\nrendererinfo_t d3d8rendererinfo =\r\n{\r\n\t\"Direct3D8\",\r\n\t{\r\n\t\t\"D3D8\"\r\n\t},\r\n\tQR_DIRECT3D8,\r\n\r\n\tD3D8_Draw_Init,\r\n\tD3D8_Draw_Shutdown,\r\n\r\n\tD3D8_UpdateFiltering,\r\n\tD3D8_LoadTextureMips,\r\n\tD3D8_DestroyTexture,\r\n\r\n\tD3D8_R_Init,\r\n\tD3D8_R_DeInit,\r\n\tD3D8_R_RenderView,\r\n\r\n\tD3D8_VID_Init,\r\n\tD3D8_VID_DeInit,\r\n\tD3D8_VID_SwapBuffers,\r\n\tD3D8_VID_ApplyGammaRamps,\r\n\tNULL,\r\n\tNULL,\r\n\tNULL,\r\n\tD3D8_VID_SetWindowCaption,\r\n\tD3D8_VID_GetRGBInfo,\r\n\r\n\tD3D8_SCR_UpdateScreen,\r\n\r\n\tD3D8BE_SelectMode,\r\n\tD3D8BE_DrawMesh_List,\r\n\tD3D8BE_DrawMesh_Single,\r\n\tD3D8BE_SubmitBatch,\r\n\tD3D8BE_GetTempBatch,\r\n\tD3D8BE_DrawWorld,\r\n\tD3D8BE_Init,\r\n\tD3D8BE_GenBrushModelVBO,\r\n\tD3D8BE_ClearVBO,\r\n\tD3D8BE_UploadAllLightmaps,\r\n\tD3D8BE_SelectEntity,\r\n\tD3D8BE_SelectDLight,\r\n\tD3D8BE_Scissor,\r\n\tD3D8BE_LightCullModel,\r\n\r\n\tD3D8BE_VBO_Begin,\r\n\tD3D8BE_VBO_Data,\r\n\tD3D8BE_VBO_Finish,\r\n\tD3D8BE_VBO_Destroy,\r\n\r\n\tD3D8BE_RenderToTextureUpdate2d,\r\n\r\n\t\"no more\"\r\n};\r\n\r\n#endif\r\n"
  },
  {
    "path": "engine/droid/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n\t\tpackage=\"com.fteqw\"\n\t\tandroid:versionCode=\"1\"\n\t\tandroid:versionName=\"1.05\"\n\t\tandroid:installLocation=\"auto\">\n\t<uses-sdk android:minSdkVersion=\"9\" android:targetSdkVersion=\"24\"/>\n\t<uses-permission android:name=\"android.permission.INTERNET\"></uses-permission>\n\t<uses-permission android:name=\"android.permission.WRITE_EXTERNAL_STORAGE\"></uses-permission>\n\t<uses-permission android:name=\"android.permission.VIBRATE\"></uses-permission>\n\t<uses-feature android:glEsVersion=\"0x00020000\" android:required=\"true\"/>\n\n<!--\n\t<application android:icon=\"@drawable/icon\" android:label=\"@string/app_name\">\n\t\t<activity android:name=\".FTEDroidFooBar\"\n\t\t\t  android:label=\"@string/app_name\"\n\t\t\t  android:configChanges=\"keyboardHidden|orientation\">\n-->\n<!-- hack the comments to enable only one of the xml blocks above or below this comment -->\n<!-- -->\n\t<application android:icon=\"@drawable/icon\" android:label=\"@string/app_name\" android:hasCode=\"true\">\n\t\t<activity android:name=\"com.fteqw.FTENativeActivity\"\n\t\t\t  android:label=\"@string/app_name\"\n\t\t\t  android:configChanges=\"keyboardHidden|orientation|screenLayout\">\n<!-- -->\n\n\t\t\t<!-- launcher icon -->\n\t\t\t<intent-filter>\n\t\t\t\t<action android:name=\"android.intent.action.MAIN\" />\n\t\t\t\t<category android:name=\"android.intent.category.LAUNCHER\" />\n\t\t\t</intent-filter>\n\n\t\t\t<!-- custom url scheme, because someone didn't like using mime types properly -->\n\t\t\t<intent-filter>\n\t\t\t\t<action android:name=\"android.intent.action.VIEW\" />\n\t\t\t\t<category android:name=\"android.intent.category.DEFAULT\" />\n\t\t\t\t<category android:name=\"android.intent.category.BROWSABLE\" />\n\t\t\t\t<data android:scheme=\"qw\" />\n\t\t\t</intent-filter>\t\t\t\n\n\t\t\t<!-- mime types with unspecified schemes (please don't explode). one for each mime type. because I'm paranoid. -->\n\t\t\t<intent-filter>\n\t\t\t\t<action android:name=\"android.intent.action.VIEW\" />\n\t\t\t\t<category android:name=\"android.intent.category.DEFAULT\" />\n\t\t\t\t<category android:name=\"android.intent.category.BROWSABLE\" />\n\t\t\t\t<data android:mimeType=\"application/x-qtv\" />\n\t\t\t</intent-filter>\n\t\t\t<intent-filter>\n\t\t\t\t<action android:name=\"android.intent.action.VIEW\" />\n\t\t\t\t<category android:name=\"android.intent.category.DEFAULT\" />\n\t\t\t\t<category android:name=\"android.intent.category.BROWSABLE\" />\n\t\t\t\t<data android:mimeType=\"text/x-quaketvident\" />\n\t\t\t</intent-filter>\n\t\t\t<intent-filter>\n\t\t\t\t<action android:name=\"android.intent.action.VIEW\" />\n\t\t\t\t<category android:name=\"android.intent.category.DEFAULT\" />\n\t\t\t\t<category android:name=\"android.intent.category.BROWSABLE\" />\n\t\t\t\t<data android:mimeType=\"application/x-ftemanifest\" />\n\t\t\t</intent-filter>\n\n\t\t\t<!-- various file extensions for when people forgot the mime type or simply don't know it -->\n\t\t\t<intent-filter>\n\t\t\t\t<action android:name=\"android.intent.action.VIEW\" />\n\t\t\t\t<category android:name=\"android.intent.category.DEFAULT\" />\n\t\t\t\t<category android:name=\"android.intent.category.BROWSABLE\" />\n\t\t\t\t<data android:scheme=\"http\" android:host=\"*\" android:pathPattern=\".*\\\\.qtv\" />\n\t\t\t\t<data android:scheme=\"content\" android:host=\"*\" android:pathPattern=\".*\\\\.qtv\" />\n\t\t\t\t<data android:scheme=\"file\" android:host=\"*\" android:pathPattern=\".*\\\\.qtv\" />\n\t\t\t\t<data android:scheme=\"http\" android:host=\"*\" android:pathPattern=\".*\\\\.mvd\" />\n\t\t\t\t<data android:scheme=\"content\" android:host=\"*\" android:pathPattern=\".*\\\\.mvd\" />\n\t\t\t\t<data android:scheme=\"file\" android:host=\"*\" android:pathPattern=\".*\\\\.mvd\" />\n\t\t\t\t<data android:scheme=\"http\" android:host=\"*\" android:pathPattern=\".*\\\\.dem\" />\n\t\t\t\t<data android:scheme=\"content\" android:host=\"*\" android:pathPattern=\".*\\\\.dem\" />\n\t\t\t\t<data android:scheme=\"file\" android:host=\"*\" android:pathPattern=\".*\\\\.dem\" />\n\t\t\t\t<data android:scheme=\"http\" android:host=\"*\" android:pathPattern=\".*\\\\.qwd\" />\n\t\t\t\t<data android:scheme=\"content\" android:host=\"*\" android:pathPattern=\".*\\\\.qwd\" />\n\t\t\t\t<data android:scheme=\"file\" android:host=\"*\" android:pathPattern=\".*\\\\.qwd\" />\n\t\t\t\t<data android:scheme=\"http\" android:host=\"*\" android:pathPattern=\".*\\\\.pak\" />\n\t\t\t\t<data android:scheme=\"content\" android:host=\"*\" android:pathPattern=\".*\\\\.pak\" />\n\t\t\t\t<data android:scheme=\"file\" android:host=\"*\" android:pathPattern=\".*\\\\.pak\" />\n\t\t\t\t<data android:scheme=\"http\" android:host=\"*\" android:pathPattern=\".*\\\\.pk3\" />\n\t\t\t\t<data android:scheme=\"content\" android:host=\"*\" android:pathPattern=\".*\\\\.pk3\" />\n\t\t\t\t<data android:scheme=\"file\" android:host=\"*\" android:pathPattern=\".*\\\\.pk3\" />\n\t\t\t\t<data android:scheme=\"http\" android:host=\"*\" android:pathPattern=\".*\\\\.bsp\" />\n\t\t\t\t<data android:scheme=\"content\" android:host=\"*\" android:pathPattern=\".*\\\\.bsp\" />\n\t\t\t\t<data android:scheme=\"file\" android:host=\"*\" android:pathPattern=\".*\\\\.bsp\" />\n\t\t\t\t<data android:scheme=\"http\" android:host=\"*\" android:pathPattern=\".*\\\\.fmf\" />\n\t\t\t\t<data android:scheme=\"content\" android:host=\"*\" android:pathPattern=\".*\\\\.fmf\" />\n\t\t\t\t<data android:scheme=\"file\" android:host=\"*\" android:pathPattern=\".*\\\\.fmf\" />\n\t\t\t</intent-filter>\n\t\t</activity>\n\t</application>\n</manifest>\n\n"
  },
  {
    "path": "engine/droid/configs/touch.cfg",
    "content": "showpic_removeall\r\n\r\nsv_aim 0.90\t//quake style, avoid needing to pitch too much\r\nshowpic res/drawable/touch_moveforward.tga\tforward\t-16\t-88\tbm\t32\t32\t+forward\r\nshowpic res/drawable/touch_moveback.tga\t\tmoveback\t-16\t-56\tbm\t32\t32\t+back\r\nshowpic res/drawable/touch_moveleft.tga\tmoveleft\t-48\t-56\tbm\t32\t32\t+moveleft\r\nshowpic res/drawable/touch_moveright.tga\tmoveright\t16\t-56\tbm\t32\t32\t+moveright\r\n\r\nshowpic res/drawable/touch_attack.tga\t\tattack\t-80\t-56\tbm\t32\t32\t+attack\r\nshowpic res/drawable/touch_jump.tga\t\tjump\t\t48\t-56\tbm\t32\t32\t+jump\r\n\r\nshowpic res/drawable/touch_turnleft.tga\tturnleft\t-112\t-56\tbm\t32\t32\t+left\r\nshowpic res/drawable/touch_turnright.tga\tturnright\t80\t-56\tbm\t32\t32\t+right\r\n"
  },
  {
    "path": "engine/droid/default.fmf",
    "content": "//documentation on this file can be found at https://sourceforge.net/p/fteqw/code/HEAD/tree/trunk/specs/fte_manifests.txt\r\n\r\nFTEManifestVer 1\r\ngame afterquake\r\nname \"AfterQuake\"\r\n//listing dp here allows listing public dp servers, but still won't be listed inside dp\r\nprotocolname \"FTE-Quake DarkPlaces-Quake\"\r\nbasegame id1\r\nbasegame qw\r\nbasegame *fte\r\ndisablehomedir 1\r\n\r\n//first, so we don't favour it over pak1.pak\r\n//avoids distributing the demo as anything other than the demo.\r\npackage id1/gpl_maps.pk3\r\narchivedpackage id1/pak0.pak - id1/pak0.pak http://triptohell.info/moodles/live/QUAKE_SW.zip\r\npackage id1/pak1.pak\r\npackage id1/new_playermodel_only.pak\r\n\r\n//autoupdate stuff\r\n//FIME FIXME FIXME - on android we have no https support\r\ndownloadsurl \"http://fte.triptohell.info/downloadables.php\"\r\ninstall \"AfterQuake - Minimum\"\r\n"
  },
  {
    "path": "engine/droid/fte.cfg",
    "content": "// file supplied by mushi\r\n\r\nbind mouse1      +attack       //left area of the screen\r\nbind mouse2      +jump         //right area of the screen\r\nbind menu        \"toggleconsole\"\r\nbind volup       inc volume 0.1\r\nbind voldown     inc volume -0.1\r\n\r\n         \r\n// Appearance settings\r\nbrightness       \"0.2\"\r\ncontrast         \"1.2\"\r\n\r\nvid_conwidth     \"0\"   //make something up based upon aspect ratio\r\nvid_conheight    \"300\" //not using autoscale as it can make the menu unusable.\r\nvid_conautoscale \"0\" // Text/Menu size. 2 is the default. 4 is bigger\r\n\r\nscr_consize 0.4\t//android's onscreen keyboard can take up over half the screen (and we don't know exactly where it is).\r\nexec touch.cfg\r\n"
  },
  {
    "path": "engine/droid/res/values/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"app_name\">FTEDroid</string>\n</resources>\n"
  },
  {
    "path": "engine/droid/src/com/fteqw/FTEDroidActivity.java",
    "content": "package com.fteqw;\r\n\r\nimport android.app.Activity;\r\nimport android.content.Context;\r\nimport android.os.Bundle;\r\nimport android.view.Window;\r\nimport android.view.WindowManager;\r\n\r\nimport android.view.SurfaceView;\r\nimport android.view.KeyEvent;\r\nimport android.view.MotionEvent;\r\nimport android.app.AlertDialog;\r\nimport android.content.DialogInterface;\r\nimport android.database.Cursor;\r\n\r\nimport android.hardware.SensorManager;\r\nimport android.hardware.Sensor;\r\nimport android.hardware.SensorEvent;\r\nimport android.hardware.SensorEventListener;\r\n\r\nimport android.media.AudioFormat;\r\nimport android.media.AudioManager;\r\nimport android.media.AudioTrack;\r\nimport android.os.Environment;\r\n\r\nimport android.view.inputmethod.InputMethodManager;\r\n\r\nimport android.os.Vibrator;\r\n\r\npublic class FTEDroidActivity extends Activity\r\n{\r\n\tprivate SensorManager sensorman;\r\n\tprivate Sensor sensoracc;\r\n\tprivate Sensor sensorgyro;\r\n\tprivate FTEView view;\r\n\tprivate String basedir, userdir;\r\n\r\n\r\n\tprivate audiothreadclass audiothread;\r\n\tprivate class audiothreadclass extends Thread\r\n\t{\r\n\t\tboolean timetodie;\r\n\t\tint schannels;\r\n\t\tint sspeed;\r\n\t\tint sbits;\r\n\t\t@Override\r\n\t\tpublic void run()\r\n\t\t{\r\n\t\t\tbyte[] audbuf = new byte[2048];\r\n\t\t\tint avail;\r\n\t\t\tAudioTrack at;\r\n\t\t\t\r\n\t\t\tint chans;\r\n\t\t\ttry\r\n\t\t\t{\r\n\t\t\t\tif (schannels >= 8)\t//the OUT enumeration allows specific speaker control. but also api level 5+\r\n\t\t\t\t\tchans = AudioFormat.CHANNEL_OUT_7POINT1;\r\n\t\t\t\telse if (schannels >= 6)\r\n\t\t\t\t\tchans = AudioFormat.CHANNEL_OUT_5POINT1;\r\n\t\t\t\telse if (schannels >= 4)\r\n\t\t\t\t\tchans = AudioFormat.CHANNEL_OUT_QUAD;\r\n\t\t\t\telse if (schannels >= 2)\r\n\t\t\t\t\tchans = AudioFormat.CHANNEL_CONFIGURATION_STEREO;\r\n\t\t\t\telse\r\n\t\t\t\t\tchans = AudioFormat.CHANNEL_CONFIGURATION_MONO;\r\n\t\t\t\tint enc = (sbits == 8)?AudioFormat.ENCODING_PCM_8BIT:AudioFormat.ENCODING_PCM_16BIT;\r\n\t\t\t\t\r\n\t\t\t\tint sz = 2*AudioTrack.getMinBufferSize(sspeed, chans, enc);\r\n\r\n//\t\t\t\tif (sz < sspeed * 0.05)\r\n//\t\t\t\t\tsz = sspeed * 0.05;\r\n\r\n\t\t\t\tat = new AudioTrack(AudioManager.STREAM_MUSIC, sspeed, chans, enc, sz, AudioTrack.MODE_STREAM);\r\n\t\t\t}\r\n\t\t\tcatch(IllegalArgumentException e)\r\n\t\t\t{\r\n\t\t\t\t//fixme: tell the engine that its bad and that it should configure some different audio attributes, instead of simply muting.\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\tat.setStereoVolume(1, 1);\r\n\t\t\tat.play();\r\n\t\t\r\n\t\t\twhile(!timetodie)\r\n\t\t\t{\r\n\t\t\t\tavail = FTEDroidEngine.paintaudio(audbuf, audbuf.length);\r\n\t\t\t\tat.write(audbuf, 0, avail);\r\n\t\t\t}\r\n\t\t\t\r\n\t\t\tat.stop();\r\n\t\t}\r\n\t\tpublic void killoff()\r\n\t\t{\r\n\t\t\ttimetodie = true;\r\n\t\t\ttry\r\n\t\t\t{\r\n\t\t\t\tjoin();\r\n\t\t\t}\r\n\t\t\tcatch(InterruptedException e)\r\n\t\t\t{\r\n\t\t\t}\r\n\t\t\ttimetodie = false;\r\n\t\t}\r\n\t};\r\n\tpublic void audioStop()\r\n\t{\r\n\t\tif (audiothread != null)\r\n\t\t{\r\n\t\t\taudiothread.killoff();\r\n\t\t\taudiothread = null;\r\n\t\t}\r\n\t}\r\n\tprivate void audioInit(int sspeed, int schannels, int sbits)\r\n\t{\r\n\t\tif (audiothread == null)\r\n\t\t{\r\n\t\t\taudiothread = new audiothreadclass();\r\n\t\t\taudiothread.schannels = schannels;\r\n\t\t\taudiothread.sspeed = sspeed;\r\n\t\t\taudiothread.sbits = sbits;\r\n\t\t\taudiothread.start();\r\n\t\t}\r\n\t}\r\n\tpublic void audioResume()\r\n\t{\r\n\t\tif (audiothread != null)\r\n\t\t{\r\n\t\t\taudiothread.killoff();\r\n\t\t\taudiothread.start();\r\n\t\t}\r\n\t}\r\n\r\n\r\n\r\n\tclass FTEMultiTouchInputEvent extends FTELegacyInputEvent\r\n\t{\r\n\t\t/*Requires API level 5+ (android 2.0+)*/\r\n\t\tprivate void domove(MotionEvent event)\r\n\t\t{\r\n\t\t\tfinal int pointerCount = event.getPointerCount();\r\n\t\t\tint i;\r\n\t\t\tfor (i = 0; i < pointerCount; i++)\r\n\t\t\t\tFTEDroidEngine.motion(0, event.getPointerId(i), event.getX(i), event.getY(i), event.getSize(i));\r\n\t\t}\r\n\t\t\r\n\t\tpublic boolean go(MotionEvent event)\r\n\t\t{\r\n\t\t\tint id;\r\n\t\t\tfloat x, y, size;\r\n\t\t\tfinal int act = event.getAction();\r\n\r\n\t\t\tdomove(event);\r\n\r\n\t\t\tswitch(act & event.ACTION_MASK)\r\n\t\t\t{\r\n\t\t\tcase MotionEvent.ACTION_DOWN:\r\n\t\t\tcase MotionEvent.ACTION_POINTER_DOWN:\r\n\t\t\t\tid = ((act&event.ACTION_POINTER_ID_MASK) >> event.ACTION_POINTER_ID_SHIFT);\r\n\t\t\t\tx = event.getX(id);\r\n\t\t\t\ty = event.getY(id);\r\n\t\t\t\tsize = event.getSize(id);\r\n\t\t\t\tid = event.getPointerId(id);\r\n\t\t\t\tFTEDroidEngine.motion(1, id, x, y, size);\r\n\t\t\t\tbreak;\r\n\t\t\tcase MotionEvent.ACTION_UP:\r\n\t\t\tcase MotionEvent.ACTION_POINTER_UP:\r\n\t\t\t\tid = ((act&event.ACTION_POINTER_ID_MASK) >> event.ACTION_POINTER_ID_SHIFT);\r\n\t\t\t\tx = event.getX(id);\r\n\t\t\t\ty = event.getY(id);\r\n\t\t\t\tsize = event.getSize(id);\r\n\t\t\t\tid = event.getPointerId(id);\r\n\t\t\t\tFTEDroidEngine.motion(2, id, x, y, size);\r\n\t\t\t\tbreak;\r\n\t\t\tcase MotionEvent.ACTION_MOVE:\r\n\t\t\t\tbreak;\r\n\t\t\tdefault:\r\n\t\t\t\treturn false;\r\n\t\t\t}\r\n\t\t\treturn true;\r\n\t\t}\r\n\t}\r\n\tclass FTELegacyInputEvent\r\n\t{\r\n\t\tpublic boolean go(MotionEvent event)\r\n\t\t{\r\n\t\t\tfinal int act = event.getAction();\r\n\t\t\tfinal float x = event.getX();\r\n\t\t\tfinal float y = event.getY();\r\n\t\t\tfinal float size = event.getSize();\r\n\r\n\t\t\tFTEDroidEngine.motion(0, 0, x, y, size);\r\n\r\n\t\t\tswitch(act)\r\n\t\t\t{\r\n\t\t\tcase MotionEvent.ACTION_DOWN:\r\n\t\t\t\tFTEDroidEngine.motion(1, 0, x, y, size);\r\n\t\t\t\tbreak;\r\n\t\t\tcase MotionEvent.ACTION_UP:\r\n\t\t\t\tFTEDroidEngine.motion(2, 0, x, y, size);\r\n\t\t\t\tbreak;\r\n\t\t\tcase MotionEvent.ACTION_MOVE:\r\n\t\t\t\tbreak;\r\n\t\t\tdefault:\r\n\t\t\t\treturn false;\r\n\t\t\t}\r\n\t\t\treturn true;\r\n\t\t}\r\n\t}\r\n\t\r\n\tprivate class FTEView extends SurfaceView implements SensorEventListener,android.view.SurfaceHolder.Callback\r\n\t{\r\n\t\tpublic int notifiedflags;\r\n\t\tfinal private FTEDroidActivity act;\r\n\t\tprivate FTELegacyInputEvent inputevent;\r\n\t\t\r\n\t\tprivate FTERenderThread rthread;\r\n\t\tpublic boolean inited;\r\n\r\n\t\tclass FTERenderThread extends Thread\r\n\t\t{\r\n\t\t\tprivate FTEView theview;\r\n\t\t\tprivate android.view.SurfaceHolder surfaceholder;\r\n\t\t\tprivate boolean doquit;\r\n\t\t\tpublic FTERenderThread(FTEView parent, android.view.SurfaceHolder sh)\r\n\t\t\t{\r\n\t\t\t\tsurfaceholder = sh;\r\n\t\t\t\ttheview = parent;\r\n\t\t\t}\r\n\t\t\t@Override\r\n\t\t\tpublic void run()\r\n\t\t\t{\r\n\t\t\t\tFTEDroidEngine.setwindow(surfaceholder.getSurface());\r\n\t\t\t\tif (!theview.inited)\r\n\t\t\t\t{\r\n\t\t\t\t\tFTEDroidEngine.init(0, 0, basedir, userdir);\r\n\t\t\t\t\ttheview.inited = true;\r\n\t\t\t\t}\r\n\r\n\t\t\t\twhile (!doquit)\r\n\t\t\t\t{\r\n\t\t\t\t\tint flags = FTEDroidEngine.frame();\r\n\t\t\t\t\t//fixme: check return value for events to respond to\r\n\t\t\t\t\t\r\n\t\t\t\t\tif (flags != notifiedflags)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tif (((flags ^ notifiedflags) & 1) != 0)\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tfinal int fl = flags;\r\n\t\t\t\t\t\t\tRunnable r = new Runnable() \r\n\t\t\t\t\t\t\t{\t//doing this on the ui thread because android sucks.\r\n\t\t\t\t\t\t\t\tpublic void run()\r\n\t\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\t\tInputMethodManager im = (InputMethodManager) act.getSystemService(Context.INPUT_METHOD_SERVICE);\r\n\t\t\t\t\t\t\t\t\tif (im != null)\r\n\t\t\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\t\t\tif ((fl & 1) != 0)\r\n\t\t\t\t\t\t\t\t\t\t{\r\n\t\t\t//\t\t\t\t\t\t\t\tgetWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);\r\n\t\t\t\t\t\t\t\t\t\t\tim.showSoftInput(theview, 0);//InputMethodManager.SHOW_FORCED);\r\n\t\t\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t\t\t\telse\r\n\t\t\t\t\t\t\t\t\t\t{\r\n\t\t\t//\t\t\t\t\t\t\t\tgetWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);\r\n\t\t\t\t\t\t\t\t\t\t\tim.hideSoftInputFromWindow(theview.getWindowToken(), 0);\r\n\t\t\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t\t\telse\r\n\t\t\t\t\t\t\t\t\t\tandroid.util.Log.i(\"FTEDroid\", \"IMM failed\");\r\n\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t};\r\n\t\t\t\t\t\t\tact.runOnUiThread(r);\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\tif (((flags ^ notifiedflags) & 2) != 0)\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tint dur = FTEDroidEngine.getvibrateduration();\r\n\t\t\t\t\t\t\tflags &= ~2;\r\n\t\t\t\t\t\t\tVibrator vib = (Vibrator) act.getSystemService(Context.VIBRATOR_SERVICE);\r\n\t\t\t\t\t\t\tif (vib != null)\r\n\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\tandroid.util.Log.i(\"FTEDroid\", \"Vibrate \" + dur + \"ms\");\r\n\t\t\t\t\t\t\t\tvib.vibrate(dur);\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\telse\r\n\t\t\t\t\t\t\t\tandroid.util.Log.i(\"FTEDroid\", \"No vibrator device\");\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\tif (((flags ^ notifiedflags) & 4) != 0)\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tfinal int fl = flags;\r\n\t\t\t\t\t\t\tRunnable r = new Runnable() \r\n\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\tpublic void run()\r\n\t\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\t\tif ((fl & 4) != 0)\r\n\t\t\t\t\t\t\t\t\t\tact.getWindow().setFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON, WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);\r\n\t\t\t\t\t\t\t\t\telse\r\n\t\t\t\t\t\t\t\t\t\tact.getWindow().setFlags(0, WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);\r\n\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t};\r\n\t\t\t\t\t\t\tact.runOnUiThread(r);\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\tif (((flags ^ notifiedflags) & 8) != 0)\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tfinal String errormsg = FTEDroidEngine.geterrormessage();\r\n\t\t\t\t\t\t\t\r\n\t\t\t\t\t\t\tinited = false;\r\n\t\t\t\t\t\t\t\r\n\t\t\t\t\t\t\tif (errormsg.equals(\"\"))\r\n\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\tfinish();\r\n\t\t\t\t\t\t\t\tSystem.exit(0);\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t\r\n\t\t\t\t\t\t\t//8 means sys error\r\n\t\t\t\t\t\t\tRunnable r = new Runnable() \r\n\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\tpublic void run()\r\n\t\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\t\ttheview.setVisibility(theview.GONE);\r\n\t\t\t\t\t\t\t\t\tAlertDialog ad = new AlertDialog.Builder(act).create();\r\n\t\t\t\t\t\t\t\t\tad.setTitle(\"FTE ERROR\");\r\n\t\t\t\t\t\t\t\t\tad.setMessage(errormsg);\r\n\t\t\t\t\t\t\t\t\tad.setCancelable(false);\r\n\t\t\t\t\t\t\t\t\tad.setButton(\"Ok\", new DialogInterface.OnClickListener()\r\n\t\t\t\t\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\t\t\t\t\tpublic void onClick(DialogInterface dialog, int which)\r\n\t\t\t\t\t\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\t\t\t\t\t\tfinish();\r\n\t\t\t\t\t\t\t\t\t\t\t\t\tSystem.exit(0);\r\n\t\t\t\t\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t\t\t\t);\r\n\t\t\t\t\t\t\t\t\tad.show();\r\n\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t};\r\n\t\t\t\t\t\t\tact.runOnUiThread(r);\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\tif (((flags ^ notifiedflags) & 16) != 0)\r\n\t\t\t\t\t\t{\t\t\r\n\t\t\t\t\t\t\t//16 means orientation cvar change\t\t\t\t\r\n\t\t\t\t\t\t\tRunnable r = new Runnable()\r\n\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\tpublic void run()\r\n\t\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\t\tString ors = FTEDroidEngine.getpreferedorientation();\r\n\t\t\t\t\t\t\t\t\tint ori = android.content.pm.ActivityInfo.SCREEN_ORIENTATION_SENSOR;\r\n\t\t\t\t\t\t\t\t\tif (ors.equalsIgnoreCase(\"unspecified\"))\r\n\t\t\t\t\t\t\t\t\t\tori = android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;\r\n\t\t\t\t\t\t\t\t\telse if (ors.equalsIgnoreCase(\"landscape\"))\r\n\t\t\t\t\t\t\t\t\t\tori = android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;\r\n\t\t\t\t\t\t\t\t\telse if (ors.equalsIgnoreCase(\"portrait\"))\r\n\t\t\t\t\t\t\t\t\t\tori = android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;\r\n\t\t\t\t\t\t\t\t\telse if (ors.equalsIgnoreCase(\"user\"))\r\n\t\t\t\t\t\t\t\t\t\tori = android.content.pm.ActivityInfo.SCREEN_ORIENTATION_USER;\r\n\t\t\t\t\t\t\t\t\telse if (ors.equalsIgnoreCase(\"behind\"))\r\n\t\t\t\t\t\t\t\t\t\tori = android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;\r\n\t\t\t\t\t\t\t\t\telse if (ors.equalsIgnoreCase(\"sensor\"))\r\n\t\t\t\t\t\t\t\t\t\tori = android.content.pm.ActivityInfo.SCREEN_ORIENTATION_SENSOR;\r\n\t\t\t\t\t\t\t\t\telse if (ors.equalsIgnoreCase(\"nosensor\"))\r\n\t\t\t\t\t\t\t\t\t\tori = android.content.pm.ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;\r\n\t\t\t\t\t\t\t\t\t//the following are api level 9+\r\n\t\t\t\t\t\t\t\t\telse if (ors.equalsIgnoreCase(\"sensorlandscape\"))\r\n\t\t\t\t\t\t\t\t\t\tori = 6;//android.content.pm.ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE;\r\n\t\t\t\t\t\t\t\t\telse if (ors.equalsIgnoreCase(\"sensorportrait\"))\r\n\t\t\t\t\t\t\t\t\t\tori = 7;//android.content.pm.ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT;\r\n\t\t\t\t\t\t\t\t\telse if (ors.equalsIgnoreCase(\"reverselandscape\"))\r\n\t\t\t\t\t\t\t\t\t\tori = 8;//android.content.pm.ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;\r\n\t\t\t\t\t\t\t\t\telse if (ors.equalsIgnoreCase(\"reverseportrait\"))\r\n\t\t\t\t\t\t\t\t\t\tori = 9;//android.content.pm.ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT;\r\n\t\t\t\t\t\t\t\t\telse if (ors.equalsIgnoreCase(\"fullsensor\"))\r\n\t\t\t\t\t\t\t\t\t\tori = 10;//android.content.pm.ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR;\r\n\t\t\t\t\t\t\t\t\t//and the default, because specifying it again is always useless.\r\n\t\t\t\t\t\t\t\t\telse\r\n\t\t\t\t\t\t\t\t\t\tori = android.content.pm.ActivityInfo.SCREEN_ORIENTATION_SENSOR;\r\n\t\t\t\t\t\t\t\t\tandroid.util.Log.i(\"FTEDroid\", \"Orientation changed to \" + ori + \" (\" + ors + \").\");\r\n\t\t\t\t\t\t\t\t\tact.setRequestedOrientation(ori);\r\n\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t};\r\n\t\t\t\t\t\t\tact.runOnUiThread(r);\r\n\t\t\t\t\t\t}\r\n\r\n\t\t\t\t\t\tif (((flags ^ notifiedflags) & 32) != 0)\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tfinal int fl = flags;\r\n\t\t\t\t\t\t\tRunnable r = new Runnable()\r\n\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\tpublic void run()\r\n\t\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\t\tif ((fl & 32) != 0)\r\n\t\t\t\t\t\t\t\t\t\tact.audioInit(FTEDroidEngine.audioinfo(0), FTEDroidEngine.audioinfo(1), FTEDroidEngine.audioinfo(2));\r\n\t\t\t\t\t\t\t\t\telse\r\n\t\t\t\t\t\t\t\t\t\tact.audioStop();\r\n\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t};\r\n\t\t\t\t\t\t\tact.runOnUiThread(r);\r\n\t\t\t\t\t\t}\r\n\r\n\t\t\t\t\t\t//clear anything which is an impulse\r\n\t\t\t\t\t\tnotifiedflags = flags;\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t\tFTEDroidEngine.setwindow(null);\r\n\t\t\t}\r\n\r\n\t\t\tpublic void requestExitAndWait()\r\n\t\t\t{\r\n\t\t\t\tdoquit = true;\r\n\t\t\t\ttry { join(); } catch(InterruptedException e) {}\r\n\t\t\t}\r\n\t\t}\r\n\t\r\n\t\t//SensorEventListener\r\n\t\tpublic void onAccuracyChanged(Sensor sensor, int accuracy)\r\n\t\t{\r\n\t\t}\r\n\r\n\t\t//SensorEventListener\r\n\t\tpublic void onSensorChanged(final SensorEvent event)\r\n\t\t{\r\n\t\t\tif (event.sensor == sensoracc)\r\n\t\t\t{\r\n\t\t\t\tFTEDroidEngine.accelerometer(event.values[0], event.values[1], event.values[2]);\r\n\t\t\t}\r\n\t\t\telse if (event.sensor == sensorgyro)\r\n\t\t\t{\r\n\t\t\t\tFTEDroidEngine.gyroscope(event.values[0], event.values[1], event.values[2]);\r\n\t\t\t}\r\n\t\t}\r\n\r\n/*\t\tprivate FTEJoystickInputEvent joystickevent;\r\n\t\tclass FTEJoystickInputEvent\r\n\t\t{\r\n\t\t\t//API level 12+\r\n\t\t\tpublic boolean go(MotionEvent event)\r\n\t\t\t{\r\n\t\t\t\tif (event.isFromSource(InputDevice.SOURCE_CLASS_JOYSTICK))\r\n\t\t\t\t{\r\n\t\t\t\t\t//FIXME: get MotionRange values from the device, so we can query the ideal size of the deadzone\r\n\t\t\t\t\tFTEDroidEngine.axischange(0, event.getAxisValue(MotionEvent.AXIS_X));\r\n\t\t\t\t\tFTEDroidEngine.axischange(1, event.getAxisValue(MotionEvent.AXIS_Y));\r\n\t\t\t\t\tFTEDroidEngine.axischange(2, event.getAxisValue(MotionEvent.AXIS_Z));\r\n\t\t\t\t\tFTEDroidEngine.axischange(3, event.getAxisValue(MotionEvent.AXIS_RZ));\r\n\t\t\t\t\tFTEDroidEngine.axischange(4, event.getAxisValue(MotionEvent.AXIS_HAT_X));\r\n\t\t\t\t\tFTEDroidEngine.axischange(5, event.getAxisValue(MotionEvent.AXIS_HAT_Y));\r\n\t\t\t\t\tFTEDroidEngine.axischange(6, event.getAxisValue(MotionEvent.AXIS_LTRIGGER));\r\n\t\t\t\t\tFTEDroidEngine.axischange(7, event.getAxisValue(MotionEvent.AXIS_RTRIGGER));\r\n\t\t\t\t\tFTEDroidEngine.axischange(8, event.getAxisValue(MotionEvent.AXIS_BREAK));\r\n\t\t\t\t\tFTEDroidEngine.axischange(9, event.getAxisValue(MotionEvent.AXIS_GAS));\r\n\t\t\t\t\treturn true;\r\n\t\t\t\t}\r\n\t\t\t\treturn false;\r\n\t\t\t}\r\n\t\t}\r\n*/\r\n\t\tpublic FTEView(FTEDroidActivity context)\r\n\t\t{\r\n\t\t\tsuper(context);\r\n\t\t\tact = context;\r\n\t\t\tgetHolder().addCallback(this);\r\n\t\t\t\r\n\t\t\tif (android.os.Build.VERSION.SDK_INT >= 5)\r\n\t\t\t\tinputevent = new FTEMultiTouchInputEvent();\r\n\t\t\telse\r\n\t\t\t\tinputevent = new FTELegacyInputEvent();\r\n\t\t\t\t\r\n//\t\t\tif (android.os.Build.VERSION.SDK_INT >= 12)\r\n//\t\t\t\tjoystickevent = new FTEJoystickInputEvent();\r\n\r\n\t\t\tsetFocusable(true);\r\n\t\t\tsetFocusableInTouchMode(true);\r\n\t\t}\r\n\r\n\t\t@Override\r\n\t\tprotected void onDraw(android.graphics.Canvas canvas)\r\n\t\t{\t//no race conditions please.\r\n\t\t}\r\n\r\n\t\t@Override\r\n\t\tprotected void onAttachedToWindow()\r\n\t\t{\r\n\t\t\tandroid.util.Log.i(\"FTEDroid\", \"onAttachedToWindow\");\r\n\t\t\tsuper.onAttachedToWindow();\r\n/*\r\n\t\t\tif (rthread != null)\r\n\t\t\t\trthread.requestExitAndWait();\r\n\t\t\trthread = new FTERenderThread(this);\r\n\t\t\trthread.start();\r\n*/\r\n\t\t}\r\n\r\n\t\t@Override\r\n\t\tprotected void onDetachedFromWindow()\r\n\t\t{\r\n\t\t\tandroid.util.Log.i(\"FTEDroid\", \"onDetachedFromWindow\");\r\n\t\t\tif (rthread != null)\r\n\t\t\t\trthread.requestExitAndWait();\r\n\t\t\trthread = null;\r\n\r\n\t\t\tsuper.onDetachedFromWindow();\r\n\t\t}\r\n\t\tpublic void surfaceDestroyed(android.view.SurfaceHolder holder)\r\n\t\t{\r\n\t\t\tandroid.util.Log.i(\"FTEDroid\", \"surfaceDestroyed\");\r\n\t\t\tif (rthread != null)\r\n\t\t\t\trthread.requestExitAndWait();\r\n\t\t\trthread = null;\r\n\t\t}\r\n\t\tpublic void surfaceChanged(android.view.SurfaceHolder holder, int format, int width, int height)\r\n\t\t{\r\n\t\t\tandroid.util.Log.i(\"FTEDroid\", \"surfaceChanged\");\r\n\t\t}\r\n\t\tpublic void surfaceCreated(android.view.SurfaceHolder holder)\r\n\t\t{\r\n\t\t\tandroid.util.Log.i(\"FTEDroid\", \"surfaceCreated\");\r\n\t\t\tif (rthread != null)\r\n\t\t\t\trthread.requestExitAndWait();\r\n\t\t\trthread = new FTERenderThread(this, holder);\r\n\t\t\trthread.start();\r\n\t\t}\r\n\r\n\r\n\t\tprivate boolean sendKey(final boolean presseddown, final int qcode, final int unicode)\r\n\t\t{\r\n\t\t\treturn 0!=FTEDroidEngine.keypress(presseddown?1:0, qcode, unicode);\r\n\t\t}\r\n\t\t\r\n\t\t//view (inputs)\r\n\t\t@Override\r\n\t\tpublic boolean onTouchEvent(MotionEvent event)\r\n\t\t{\r\n\t\t\treturn inputevent.go(event);\r\n\t\t}\r\n\r\n\t\tprivate static final int K_ENTER\t\t= 13;\r\n\t\tprivate static final int K_ESCAPE\t\t= 27;\r\n\t\tprivate static final int K_DEL\t\t\t= 127;\r\n\t\tprivate static final int K_POWER\t\t= 130;\r\n\t\tprivate static final int K_UPARROW\t\t= 132;\r\n\t\tprivate static final int K_DOWNARROW\t= 133;\r\n\t\tprivate static final int K_LEFTARROW\t= 134;\r\n\t\tprivate static final int K_RIGHTARROW\t= 135;\r\n\t\tprivate static final int K_APP\t\t\t= 241;\r\n\t\tprivate static final int K_SEARCH\t\t= 242;\r\n\t\tprivate static final int K_VOLUP\t\t= 243;\r\n\t\tprivate static final int K_VOLDOWN\t\t= 244;\r\n\r\n/*\t\tprivate static final int K_JOY1\t\t\t= 203;\r\n\t\tprivate static final int K_JOY2\t\t\t= 204;\r\n\t\tprivate static final int K_JOY3\t\t\t= 205;\r\n\t\tprivate static final int K_JOY4\t\t\t= 206;\r\n\t\tprivate static final int K_AUX1\t\t\t= 207;\r\n\t\tprivate static final int K_AUX2\t\t\t= 208;\r\n\t\tprivate static final int K_AUX3\t\t\t= 209;\r\n\t\tprivate static final int K_AUX4\t\t\t= 210;\r\n\t\tprivate static final int K_AUX5\t\t\t= 211;\r\n*/\r\n\t\tprivate static final int K_GP_A\t\t\t\t= 190;\r\n\t\tprivate static final int K_GP_B\t\t\t\t= 191;\r\n\t\tprivate static final int K_GP_X\t\t\t\t= 192;\r\n\t\tprivate static final int K_GP_Y\t\t\t\t= 193;\r\n\t\tprivate static final int K_GP_LEFT_SHOULDER\t= 194;\r\n\t\tprivate static final int K_GP_RIGHT_SHOULDER= 195;\r\n\t\tprivate static final int K_GP_LEFT_TRIGGER\t= 196;\r\n\t\tprivate static final int K_GP_RIGHT_TRIGGER\t= 197;\r\n\t\tprivate static final int K_GP_BACK\t\t\t= 198;\r\n\t\tprivate static final int K_GP_START\t\t\t= 199;\r\n\t\tprivate static final int K_GP_LEFT_THUMB\t= 200;\r\n\t\tprivate static final int K_GP_RIGHT_THUMB\t= 201;\r\n\t\tprivate static final int K_GP_DPAD_UP\t\t= 251;\r\n\t\tprivate static final int K_GP_DPAD_DOWN\t\t= 252;\r\n\t\tprivate static final int K_GP_DPAD_LEFT\t\t= 253;\r\n\t\tprivate static final int K_GP_DPAD_RIGHT\t= 254;\r\n\t\tprivate static final int K_GP_GUIDE\t\t\t= 202;\r\n\t\tprivate static final int K_GP_UNKNOWN\t\t= 255;\r\n\r\n\t\tprivate int mapKey(int acode, int unicode)\r\n\t\t{\r\n\t\t\tswitch(acode)\r\n\t\t\t{\r\n\t\t\tcase 280/*KeyEvent.KEYCODE_SYSTEM_NAVIGATION_UP*/:\r\n\t\t\t\treturn K_UPARROW;\r\n\t\t\tcase 281/*KeyEvent.KEYCODE_SYSTEM_NAVIGATION_DOWN*/:\r\n\t\t\t\treturn K_DOWNARROW;\r\n\t\t\tcase 282/*KeyEvent.KEYCODE_SYSTEM_NAVIGATION_LEFT*/:\r\n\t\t\t\treturn K_LEFTARROW;\r\n\t\t\tcase 283/*KeyEvent.KEYCODE_SYSTEM_NAVIGATION_RIGHT*/:\r\n\t\t\t\treturn K_RIGHTARROW;\r\n\r\n\t\t\tcase KeyEvent.KEYCODE_DPAD_CENTER:\r\n\t\t\tcase KeyEvent.KEYCODE_ENTER:\r\n\t\t\t\treturn K_ENTER;\r\n\t\t\tcase KeyEvent.KEYCODE_BACK:\r\n\t\t\t\treturn K_ESCAPE;\t//escape, cannot be rebound\r\n\t\t\tcase KeyEvent.KEYCODE_MENU:\r\n\t\t\t\treturn K_APP;\t\t//\"app\"\r\n\t\t\tcase KeyEvent.KEYCODE_DEL:\r\n\t\t\t\treturn K_DEL;\t\t//\"del\"\r\n\t\t\tcase KeyEvent.KEYCODE_SEARCH:\r\n\t\t\t\treturn K_SEARCH;\t//\"search\"\r\n\t\t\tcase KeyEvent.KEYCODE_POWER:\r\n\t\t\t\treturn K_POWER;\t\t//\"power\"\r\n\t\t\tcase KeyEvent.KEYCODE_VOLUME_DOWN:\r\n\t\t\t\treturn K_VOLDOWN;\t//\"voldown\"\r\n\t\t\tcase KeyEvent.KEYCODE_VOLUME_UP:\r\n\t\t\t\treturn K_VOLUP;\t\t//\"volup\"\r\n\r\n\t\t\tcase KeyEvent.KEYCODE_DPAD_UP:\r\n\t\t\t\treturn K_GP_DPAD_UP;\r\n\t\t\tcase KeyEvent.KEYCODE_DPAD_DOWN:\r\n\t\t\t\treturn K_GP_DPAD_DOWN;\r\n\t\t\tcase KeyEvent.KEYCODE_DPAD_LEFT:\r\n\t\t\t\treturn K_GP_DPAD_LEFT;\r\n\t\t\tcase KeyEvent.KEYCODE_DPAD_RIGHT:\r\n\t\t\t\treturn K_GP_DPAD_RIGHT;\r\n\t\t\tcase 96/*KeyEvent.KEYCODE_BUTTON_A*/:\r\n\t\t\t\treturn K_GP_A;\r\n\t\t\tcase 97/*KeyEvent.KEYCODE_BUTTON_B*/:\r\n\t\t\t\treturn K_GP_B;\r\n//\t\t\tcase 98/*KeyEvent.KEYCODE_BUTTON_C*/:\r\n//\t\t\t\treturn K_GP_C;\r\n\t\t\tcase 99/*KeyEvent.KEYCODE_BUTTON_X*/:\r\n\t\t\t\treturn K_GP_X;\r\n\t\t\tcase 100/*KeyEvent.KEYCODE_BUTTON_Y*/:\r\n\t\t\t\treturn K_GP_Y;\r\n//\t\t\tcase 101/*KeyEvent.KEYCODE_BUTTON_Z*/:\r\n//\t\t\t\treturn K_GP_Z;\r\n\t\t\tcase 102/*KeyEvent.KEYCODE_BUTTON_L1*/:\r\n\t\t\t\treturn K_GP_LEFT_SHOULDER;\r\n\t\t\tcase 103/*KeyEvent.KEYCODE_BUTTON_R1*/:\r\n\t\t\t\treturn K_GP_RIGHT_SHOULDER;\r\n\t\t\tcase 104/*KeyEvent.KEYCODE_BUTTON_L2*/:\r\n\t\t\t\treturn K_GP_LEFT_TRIGGER;\r\n\t\t\tcase 105/*KeyEvent.KEYCODE_BUTTON_R2*/:\r\n\t\t\t\treturn K_GP_RIGHT_TRIGGER;\r\n\t\t\tcase 106/*KeyEvent.KEYCODE_BUTTON_THUMBL*/:\r\n\t\t\t\treturn K_GP_LEFT_THUMB;\r\n\t\t\tcase 107/*KeyEvent.KEYCODE_BUTTON_THUMBR*/:\r\n\t\t\t\treturn K_GP_RIGHT_THUMB;\r\n\t\t\tcase 108/*KeyEvent.KEYCODE_BUTTON_START*/:\r\n\t\t\t\treturn K_GP_START;\r\n\t\t\tcase 109/*KeyEvent.KEYCODE_BUTTON_SELECT*/:\r\n\t\t\t\treturn K_GP_BACK;\r\n\t\t\tcase 110/*KeyEvent.KEYCODE_BUTTON_MODE*/:\r\n\t\t\t\treturn K_GP_GUIDE;\r\n//\t\t\tcase KeyEvent.KEYCODE_BUTTON_OTHER:\r\n//\t\t\t\treturn K_GP_UNKNOWN;\r\n\r\n\t\t\tdefault:\r\n\t\t\t\tif (unicode < 128)\r\n\t\t\t\t\treturn Character.toLowerCase(unicode);\r\n\t\t\t}\r\n\t\t\treturn 0;\r\n\t\t}\r\n\r\n\t\t//view (inputs)\r\n\t\t@Override\r\n\t\tpublic boolean onKeyDown(int keyCode, KeyEvent event)\r\n\t\t{\r\n\t\t\tint uc = event.getUnicodeChar();\r\n\t\t\tint qc = mapKey(keyCode, uc);\r\n\t\t\treturn sendKey(true, qc, uc);\r\n\t\t}\r\n\r\n\t\t//view (inputs)\r\n\t\t@Override\r\n\t\tpublic boolean onKeyUp(int keyCode, KeyEvent event)\r\n\t\t{\r\n\t\t\tint uc = event.getUnicodeChar();\r\n\t\t\tint qc = mapKey(keyCode, uc);\r\n\t\t\treturn sendKey(false, qc, uc);\r\n\t\t}\r\n\t}\r\n\r\n\tprivate boolean runningintheemulator()\r\n\t{\r\n\t\tandroid.util.Log.i(\"FTEDroid\", \"model: \" + android.os.Build.MODEL + \" product: \" + android.os.Build.PRODUCT + \" device: \" + android.os.Build.DEVICE);\r\n\t\treturn android.os.Build.MODEL.equals(\"sdk\") && android.os.Build.PRODUCT.equals(\"sdk\") && android.os.Build.DEVICE.equals(\"generic\");\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void onCreate(Bundle savedInstanceState)\r\n\t{\r\n\t\tandroid.util.Log.i(\"FTEDroid\", \"onCreate\");\r\n\t\t\r\n\t\ttry\r\n\t\t{\r\n\t\t\tString packagename = this.getComponentName().getPackageName();\t//\"com.fteqw\", but not hardcoded.\r\n\t\t\tandroid.util.Log.i(\"FTEDroid\", \"Installed in package \\\"\" + packagename + \"\\\".\");\r\n\t\t\tandroid.content.pm.PackageInfo info = this.getPackageManager().getPackageInfo(packagename, 0);\r\n\t\t\tbasedir = info.applicationInfo.sourceDir;\r\n\t\t}\r\n\t\tcatch(android.content.pm.PackageManager.NameNotFoundException e)\r\n\t\t{\r\n\t\t\t/*oh well, can just use the homedir instead*/\r\n\t\t}\r\n\t\tuserdir = Environment.getExternalStorageDirectory().getPath() + \"/fte\";\r\n\t\tandroid.util.Log.i(\"FTEDroid\", \"Base dir is \\\"\" + basedir + \"\\\".\");\r\n\t\tandroid.util.Log.i(\"FTEDroid\", \"User dir is \\\"\" + userdir + \"\\\".\");\r\n\r\n\t\t//go full-screen\r\n\t\tgetWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);    \t\r\n\t\trequestWindowFeature(Window.FEATURE_NO_TITLE);\r\n\r\n\t\tsuper.onCreate(savedInstanceState);\r\n\r\n\t\tandroid.util.Log.i(\"FTEDroid\", \"create view\");\r\n\t\tview = new FTEView(this);\r\n\t\tsetContentView(view);\r\n\r\n\r\n\t\tif (runningintheemulator())\r\n\t\t{\r\n\t\t\tandroid.util.Log.i(\"FTEDroid\", \"emulator detected - skipping sensors to avoid emulator bugs\");\r\n\t\t\tsensorman = null;\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tandroid.util.Log.i(\"FTEDroid\", \"init sensor manager\");\r\n\t\t\tsensorman = (SensorManager)getSystemService(SENSOR_SERVICE);\r\n\t\t}\r\n\t\tif (sensorman != null)\r\n\t\t{\r\n\t\t\tandroid.util.Log.i(\"FTEDroid\", \"init accelerometer\");\r\n\t\t\tsensoracc = sensorman.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);\r\n\t\t\tandroid.util.Log.i(\"FTEDroid\", \"init gyro\");\r\n\t\t\tsensorgyro = sensorman.getDefaultSensor(Sensor.TYPE_GYROSCOPE);\r\n\t\t}\r\n\t\tandroid.util.Log.i(\"FTEDroid\", \"done\");\r\n\t}\r\n\r\n\t@Override\r\n\tprotected void onResume()\r\n\t{\r\n\t\tsuper.onResume();\r\n\t\tif (sensorman != null && sensoracc != null)\r\n\t\t\tsensorman.registerListener((SensorEventListener)view, sensoracc, SensorManager.SENSOR_DELAY_GAME);\r\n\t\tif (sensorman != null && sensorgyro != null)\r\n\t\t\tsensorman.registerListener((SensorEventListener)view, sensorgyro, SensorManager.SENSOR_DELAY_GAME);\r\n\r\n\t\taudioResume();\r\n\t}\r\n\r\n\t@Override\r\n\tprotected void onStart()\r\n\t{\r\n\t\tsuper.onStart();\r\n\t\tfinal android.content.Intent intent = getIntent();\r\n\t\tif (intent != null)\r\n\t\t{\r\n\t\t\tfinal android.net.Uri data = intent.getData();\r\n\t\t\tif (data != null)\r\n\t\t\t{\r\n\t\t\t\tString myloc;\r\n\t\t\t\tif (data.getScheme().equals(\"content\"))\r\n\t\t\t\t{\t//wtf.\r\n\t\t\t\t\tCursor cursor = this.getContentResolver().query(data, null, null, null, null);\r\n\t\t\t\t\tcursor.moveToFirst();   \r\n\t\t\t\t\tmyloc = cursor.getString(0);\r\n\t\t\t\t\tcursor.close();\r\n\t\t\t\t\tandroid.util.Log.i(\"FTEDroid\", \"intent content: \" + myloc);\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\tmyloc = data.toString();\r\n\t\t\t\t\tandroid.util.Log.i(\"FTEDroid\", \"intent url: \" + myloc);\r\n\t\t\t\t}\r\n\t\t\t\tFTEDroidEngine.openfile(myloc);\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\t@Override\r\n\tprotected void onStop()\r\n\t{\r\n\t\tif (sensorman != null && (sensoracc != null || sensorgyro != null))\r\n\t\t\tsensorman.unregisterListener(view);\r\n\t\taudioStop();\r\n\t\tsuper.onStop();\r\n\t}\r\n\r\n\t@Override\r\n\tprotected void onPause()\r\n\t{\r\n\t\tif (sensorman != null && (sensoracc != null || sensorgyro != null))\r\n\t\t\tsensorman.unregisterListener(view);\r\n\t\taudioStop();\r\n\t\tsuper.onPause();\r\n\t}\r\n}\r\n"
  },
  {
    "path": "engine/droid/src/com/fteqw/FTEDroidEngine.java",
    "content": "package com.fteqw;\r\n\r\npublic class FTEDroidEngine\r\n{\r\n\tpublic static native void init(float dpix, float dpiy, String apkpath, String usrpath); /* init/reinit */\r\n\tpublic static native int frame();\r\n\tpublic static native int openfile(String filename);\r\n\tpublic static native int getvibrateduration();\t//in ms\r\n\tpublic static native int keypress(int down, int qkey, int unicode);\r\n\tpublic static native void motion(int act, int pointerid, float x, float y, float size);\r\n\tpublic static native void accelerometer(float ax, float ay, float az);\r\n\tpublic static native void gyroscope(float gx, float gy, float gz);\r\n\tpublic static native int paintaudio(byte[] stream, int len);\r\n\tpublic static native int audioinfo(int arg);\r\n\tpublic static native String geterrormessage();\r\n\tpublic static native String getpreferedorientation();\r\n\tpublic static native void setwindow(android.view.Surface window);\r\n\tpublic static native void windowresized(int w, int h);\r\n\r\n\tstatic\r\n\t{\r\n\t\t\tSystem.loadLibrary(\"ftedroid\");\r\n\t}\r\n}\r\n"
  },
  {
    "path": "engine/droid/src/com/fteqw/FTENativeActivity.java",
    "content": "package com.fteqw;\nimport android.view.inputmethod.InputMethodManager;\nimport android.view.KeyEvent;\nimport android.view.MotionEvent;\nimport android.view.InputDevice;\nimport android.view.WindowManager;\n\npublic class FTENativeActivity extends android.app.Activity implements android.view.SurfaceHolder.Callback2, android.view.ViewTreeObserver.OnGlobalLayoutListener\n{\n\t//Native functions and stuff\n\tprivate native boolean startup(String externalDataPath, String libraryPath);\n\tprivate native void openfile(String url);\n\tprivate native void surfacechange(boolean teardown, boolean restart, android.view.SurfaceHolder holder, android.view.Surface surface);\n\tprivate native void shutdown();\n\n\tprivate static native void keypress(int devid, boolean down, int androidkey, int unicode);\n\tprivate static native void mousepress(int devid, int buttonbits);\n\tprivate static native void motion(int devid, int action, float x, float y, float z, float size);\n\tprivate static native boolean wantrelative();\n\tprivate static native void axis(int devid, int axisid, float value);\n//\tprivate static native void oncreate(String bindir, String basedir, byte[] savedstate);\n\tstatic\n\t{\n\t\tSystem.loadLibrary(\"ftedroid\");\t//registers the methods properly.\n\t}\n\n\tstatic class NativeContentView extends android.view.View\n\t{\n\t\tFTENativeActivity mActivity;\n\t\tpublic NativeContentView(android.content.Context context)\n\t\t{\n\t\t\tsuper(context);\n\t\t}\n\t\tpublic NativeContentView(android.content.Context context, android.util.AttributeSet attrs)\n\t\t{\n\t\t\tsuper(context, attrs);\n\t\t}\n\t}\n\tprivate NativeContentView mNativeContentView;\n\n//SurfaceHolder.Callback2 methods\n\tpublic void surfaceRedrawNeeded(android.view.SurfaceHolder holder)\n\t{\t//we constantly redraw.\n\t}\n\tpublic void surfaceCreated(android.view.SurfaceHolder holder)\n\t{\n//\t\tsurfacechange(false, true, holder.getSurface());\n\t}\n\tpublic void surfaceChanged(android.view.SurfaceHolder holder, int format, int width, int height)\n\t{\n\t\tsurfacechange(true, true, holder, holder.getSurface());\n\t}\n\tpublic void surfaceDestroyed(android.view.SurfaceHolder holder)\n\t{\n\t\tsurfacechange(true, false, null, null);\n\t}\n\n//OnGlobalLayoutListener methods\n    public void onGlobalLayout()\n\t{\n/*\t\tmNativeContentView.getLocationInWindow(mLocation);\n\t\tint w = mNativeContentView.getWidth();\n\t\tint h = mNativeContentView.getHeight();\n\t\tif (mLocation[0] != mLastContentX || mLocation[1] != mLastContentY || w != mLastContentWidth || h != mLastContentHeight)\n\t\t{\n\t\t\tmLastContentX = mLocation[0];\n\t\t\tmLastContentY = mLocation[1];\n\t\t\tmLastContentWidth = w;\n\t\t\tmLastContentHeight = h;\n\t\t\tif (!mDestroyed) {\n\t\t\t\tonContentRectChangedNative(mNativeHandle, mLastContentX,\n\t\t\t\t\t\tmLastContentY, mLastContentWidth, mLastContentHeight);\n\t\t\t}\n\t\t}\n*/\t}\n\n//Activity methods\n\t@Override\n\tprotected void onCreate(android.os.Bundle savedInstanceState)\n\t{\n\t\tgetWindow().takeSurface(this);\n\t\tgetWindow().setFormat(android.graphics.PixelFormat.RGB_565);\n\t\tgetWindow().setSoftInputMode(\n\t\t\t\tWindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED\n\t\t\t\t| WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);\n\t\tmNativeContentView = new NativeContentView(this);\n\t\tmNativeContentView.mActivity = this;\n\t\tsetContentView(mNativeContentView);\n\t\tmNativeContentView.requestFocus();\n\t\tmNativeContentView.getViewTreeObserver().addOnGlobalLayoutListener(this);\n\n//\t\tbyte[] nativeSavedState = savedInstanceState != null\n//\t\t\t? savedInstanceState.getByteArray(KEY_NATIVE_SAVED_STATE) : null;\n\t\tstartup(getAbsolutePath(getExternalFilesDir(null)), getNativeLibraryDirectory());\n\t\thandleIntent(getIntent());\n\n//\t\tif (Build.VERSION.SDK_INT >= 19)\n//\t\t{\n//\t\t\tint flags = 0;\n//\t\t\tflags |= 4096/*SYSTEM_UI_FLAG_IMMERSIVE_STICKY, api 19*/;\n//\t\t\tflags |= 4/*SYSTEM_UI_FLAG_FULLSCREEN, api 16*/;\n//\t\t\tflags |= 2/*SYSTEM_UI_FLAG_HIDE_NAVIGATION, api 14*/;\t\t\t\n//\t\t\tmNativeContentView.setSystemUiVisibility(flags); /*api 11*/\n//\t\t}\n\n\t\tsuper.onCreate(savedInstanceState);\n\t}\n\t\n\t//random helpers\n\tprivate void handleIntent(android.content.Intent intent)\n\t{\n\t\tString s = intent.getScheme();\n\t\tif (s==\"content\")\n\t\t{\n\t\t\tandroid.database.Cursor cursor = this.getContentResolver().query(intent.getData(), null, null, null, null);\n\t\t\tcursor.moveToFirst();   \n\t\t\tString myloc = cursor.getString(0);\n\t\t\tcursor.close();\n\t\t}\n\t\telse\n\t\t\topenfile(intent.getDataString());\n\t}\n\tprivate static String getAbsolutePath(java.io.File file)\n\t{\n\t\treturn (file != null) ? file.getAbsolutePath() : null;\n\t}\n\tpublic String getNativeLibraryDirectory()\n\t{\n\t\tandroid.content.Context context = getApplicationContext();\n\t\tint sdk_level = android.os.Build.VERSION.SDK_INT;\n\t\tif (sdk_level >= android.os.Build.VERSION_CODES.GINGERBREAD)\n\t\t{\n\t\t\ttry\n\t\t\t{\n\t\t\t\tString secondary = (String) android.content.pm.ApplicationInfo.class.getField(\"nativeLibraryDir\").get(context.getApplicationInfo());\n\t\t\t\treturn secondary;\n\t\t\t}\n\t\t\tcatch (Exception e)\n\t\t\t{\n\t\t\t\te.printStackTrace();\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\t\tif (sdk_level >= android.os.Build.VERSION_CODES.DONUT)\n\t\t\treturn context.getApplicationInfo().dataDir + \"/lib\";\n\t\treturn \"/data/data/\" + context.getPackageName() + \"/lib\";\n\t}\n\n\t//called by C code on errors / quitting.\n\tpublic void showMessageAndQuit(String errormessage)\n\t{\n\t\tfinal android.app.Activity act = this;\n\t\tfinal String errormsg = errormessage;\n\t\tif (errormsg.equals(\"\"))\n\t\t{\t//just quit\n\t\t\tfinish();\n\t\t\tSystem.exit(0);\n\t\t}\n\t\telse runOnUiThread(new Runnable()\n\t\t{\t//show an error message, then quit.\n\t\t\tpublic void run()\n\t\t\t{\n//\t\t\t\tact.getView().setVisibility(android.view.View.GONE);\n\t\t\t\tandroid.app.AlertDialog ad = new android.app.AlertDialog.Builder(act).create();\n\t\t\t\tad.setTitle(\"Fatal Error\");\n\t\t\t\tad.setMessage(errormsg);\n\t\t\t\tad.setCancelable(false);\n\t\t\t\tad.setButton(\"Ok\", new android.content.DialogInterface.OnClickListener()\n\t\t\t\t{\n\t\t\t\t\tpublic void onClick(android.content.DialogInterface dialog, int which)\n\t\t\t\t\t{\n\t\t\t\t\t\tfinish();\n\t\t\t\t\t\tSystem.exit(0);\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t\tad.show();\n\t\t\t}\n\t\t});\n\t}\n\tpublic void updateScreenKeepOn(final boolean keepon)\n\t{\n\t\tfinal android.app.Activity act = this;\n\t\trunOnUiThread(new Runnable()\n\t\t{\n\t\t\tpublic void run()\n\t\t\t{\n\t\t\t\tif (keepon)\n\t\t\t\t\tact.getWindow().setFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON, WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);\n\t\t\t\telse\n\t\t\t\t\tact.getWindow().setFlags(0, WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);\n\t\t\t}\n\t\t});\n\t}\n\n\n\t//called by C code to set orientation.\n\tpublic void updateOrientation(String orientation)\n\t{\n\t\tfinal String ors = orientation;\n\t\trunOnUiThread(new Runnable()\n\t\t{\n\t\t\tpublic void run()\n\t\t\t{\n\t\t\t\tint ori = android.content.pm.ActivityInfo.SCREEN_ORIENTATION_SENSOR;\n\t\t\t\tif (ors.equalsIgnoreCase(\"unspecified\"))\n\t\t\t\t\tori = android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;\n\t\t\t\telse if (ors.equalsIgnoreCase(\"landscape\"))\n\t\t\t\t\tori = android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;\n\t\t\t\telse if (ors.equalsIgnoreCase(\"portrait\"))\n\t\t\t\t\tori = android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;\n\t\t\t\telse if (ors.equalsIgnoreCase(\"user\"))\n\t\t\t\t\tori = android.content.pm.ActivityInfo.SCREEN_ORIENTATION_USER;\n\t\t\t\telse if (ors.equalsIgnoreCase(\"behind\"))\n\t\t\t\t\tori = android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;\n\t\t\t\telse if (ors.equalsIgnoreCase(\"sensor\"))\n\t\t\t\t\tori = android.content.pm.ActivityInfo.SCREEN_ORIENTATION_SENSOR;\n\t\t\t\telse if (ors.equalsIgnoreCase(\"nosensor\"))\n\t\t\t\t\tori = android.content.pm.ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;\n\t\t\t\t//the following are api level 9+\n\t\t\t\telse if (ors.equalsIgnoreCase(\"sensorlandscape\"))\n\t\t\t\t\tori = 6;//android.content.pm.ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE;\n\t\t\t\telse if (ors.equalsIgnoreCase(\"sensorportrait\"))\n\t\t\t\t\tori = 7;//android.content.pm.ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT;\n\t\t\t\telse if (ors.equalsIgnoreCase(\"reverselandscape\"))\n\t\t\t\t\tori = 8;//android.content.pm.ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;\n\t\t\t\telse if (ors.equalsIgnoreCase(\"reverseportrait\"))\n\t\t\t\t\tori = 9;//android.content.pm.ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT;\n\t\t\t\telse if (ors.equalsIgnoreCase(\"fullsensor\"))\n\t\t\t\t\tori = 10;//android.content.pm.ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR;\n\t\t\t\t//and the default, because specifying it again is always useless.\n\t\t\t\telse\n\t\t\t\t\tori = android.content.pm.ActivityInfo.SCREEN_ORIENTATION_SENSOR;\n\t\t\t\tandroid.util.Log.i(\"FTEDroid\", \"Orientation changed to \" + ori + \" (\" + ors + \").\");\n\t\t\t\tsetRequestedOrientation(ori);\n\t\t\t}\n\t\t});\n\t};\n\n\n\t//keyboard stuff, called from C.\n\tpublic void showKeyboard(int softkeyflags)\n\t{\t//needed because the ndk's ANativeActivity_showSoftInput is defective\n\t\tfinal android.app.Activity act = this;\n\t\tfinal int flags = softkeyflags;\n\t\trunOnUiThread(new Runnable()\n\t\t{\n\t\t\tpublic void run()\n\t\t\t{\n\t\t\t\tif (flags != 0)\n\t\t\t\t{\n\t\t\t\t\tInputMethodManager imm = (InputMethodManager)getSystemService(android.content.Context.INPUT_METHOD_SERVICE);\n\t\t\t\t\timm.showSoftInput(act.getWindow().getDecorView(), InputMethodManager.SHOW_FORCED);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tInputMethodManager imm = (InputMethodManager)getSystemService(android.content.Context.INPUT_METHOD_SERVICE);\n\t\t\t\t\timm.hideSoftInputFromWindow(act.getWindow().getDecorView().getWindowToken(), 0);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t}\n\n\t@Override\n\tpublic boolean dispatchKeyEvent(KeyEvent event)\n\t{\t//needed because AKeyEvent_getUnicode is missing completely.\n\t\tint act = event.getAction();\n\t\tif (act == KeyEvent.ACTION_DOWN)\n\t\t{\n\t\t\tint metastate = event.getMetaState();\n\t\t\tint unichar = event.getUnicodeChar(metastate);\n\t\t\tif (unichar == 0)\n\t\t\t\tunichar = event.getUnicodeChar();\n\t\t\tif (unichar == 0)\n\t\t\t\tunichar = event.getDisplayLabel();\n\n\t\t\tkeypress(event.getDeviceId(), true, event.getKeyCode(), unichar);\n\t\t\treturn true;\n\t\t}\n\t\telse if (act == KeyEvent.ACTION_UP)\n\t\t{\n\t\t\tkeypress(event.getDeviceId(), false, event.getKeyCode(), 0);\n\t\t\treturn true;\n\t\t}\n\t\telse\n\t\t\tandroid.util.Log.i(\"FTEDroid\", \"other type of event\");\n\t\t//ignore ACTION_MULTIPLE or whatever it is, apparently its deprecated anyway.\n\n\t\treturn super.dispatchKeyEvent(event);\n\t}\n\n\tprivate static boolean canrelative;\n\tprivate static int AXIS_RELATIVE_X;//MotionEvent 24\n\tprivate static int AXIS_RELATIVE_Y;//MotionEvent 24\n\tprivate static java.lang.reflect.Method MotionEvent_getAxisValueP; //MotionEvent 12\n\tprivate static int SOURCE_MOUSE = 0x00002002;\t//InputDevice 9\n\t//private static int SOURCE_STYLUS = 0x00004002;\t//InputDevice 14\n\t//private static int SOURCE_STYLUS = 0x00004002;\t//InputDevice 14\n\tprivate static int SOURCE_MOUSE_RELATIVE = 0x00020004;\t//InputDevice 26\n\tprivate static boolean canbuttons;\n\tprivate static java.lang.reflect.Method MotionEvent_getButtonState; //MotionEvent 14\n\n\tprivate static boolean canjoystick;\n\tprivate static java.lang.reflect.Method MotionEvent_getAxisValueJ;\n\tprivate static java.lang.reflect.Method InputDevice_getMotionRange;\n\tprivate static int SOURCE_JOYSTICK = 0x01000010; //InputDevice 12\n\tprivate static int SOURCE_GAMEPAD = 0x00000401; //InputDevice 12\n\tprivate static int AXIS_X;\n\tprivate static int AXIS_Y;\n\tprivate static int AXIS_LTRIGGER;\n\tprivate static int AXIS_Z;\n\tprivate static int AXIS_RZ;\n\tprivate static int AXIS_RTRIGGER;\n\tstatic\n\t{\n\t\t//if (android.os.Build.VERSION.SDK_INT >= 12)\n\t\ttry\n\t\t{\n\t\t\tMotionEvent_getAxisValueP = MotionEvent.class.getMethod(\"getAxisValue\", int.class, int.class); //api12\n\t\t\tjava.lang.reflect.Field relX = MotionEvent.class.getField(\"AXIS_RELATIVE_X\");\t//api24ish\n\t\t\tjava.lang.reflect.Field relY = MotionEvent.class.getField(\"AXIS_RELATIVE_Y\");\t//api24ish\n\t\t\tAXIS_RELATIVE_X = (Integer)relX.get(null);\n\t\t\tAXIS_RELATIVE_Y = (Integer)relY.get(null);\n//\t\t\tSOURCE_MOUSE = (Integer)InputDevice.class.getField(\"SOURCE_MOUSE\").get(null);\n//\t\t\tSOURCE_MOUSE_RELATIVE = (Integer)InputDevice.class.getField(\"SOURCE_MOUSE_RELATIVE\").get(null);\n\t\t\tcanrelative = true;\t//yay, no exceptions.\n\t\t\tandroid.util.Log.i(\"FTEDroid\", \"relative mouse supported\");\n\n\t\t\tMotionEvent_getButtonState = MotionEvent.class.getMethod(\"getButtonState\");\t\t//api14ish\n\t\t\tcanbuttons = true;\n\t\t\tandroid.util.Log.i(\"FTEDroid\", \"mouse buttons supported\");\n\t\t} catch(Exception e) {\n\t\t\tcanrelative = false;\n\t\t\tandroid.util.Log.i(\"FTEDroid\", \"relative mouse not supported\");\n\t\t}\n\t\ttry\n\t\t{\n\t\t\tMotionEvent_getAxisValueJ = MotionEvent.class.getMethod(\"getAxisValue\", int.class); //api12\n\t\t\tInputDevice_getMotionRange = InputDevice.class.getMethod(\"getMotionRange\", int.class); //api12\n\t\t\tAXIS_X = (Integer)MotionEvent.class.getField(\"AXIS_X\").get(null);\n\t\t\tAXIS_Y = (Integer)MotionEvent.class.getField(\"AXIS_Y\").get(null);\n\t\t\tAXIS_LTRIGGER = (Integer)MotionEvent.class.getField(\"AXIS_LTRIGGER\").get(null);\n\t\t\tAXIS_Z = (Integer)MotionEvent.class.getField(\"AXIS_Z\").get(null);\n\t\t\tAXIS_RZ = (Integer)MotionEvent.class.getField(\"AXIS_RZ\").get(null);\n\t\t\tAXIS_RTRIGGER = (Integer)MotionEvent.class.getField(\"AXIS_RTRIGGER\").get(null);\n//\t\t\tSOURCE_JOYSTICK = (Integer)InputDevice.class.getField(\"SOURCE_JOYSTICK\").get(null);\n//\t\t\tSOURCE_GAMEPAD = (Integer)InputDevice.class.getField(\"SOURCE_GAMEPAD\").get(null);\n\t\t\tcanjoystick = true;\n\t\t\tandroid.util.Log.i(\"FTEDroid\", \"gamepad supported\");\n\t\t} catch(Exception e) {\n\t\t\tcanjoystick = false;\n\t\t\tandroid.util.Log.i(\"FTEDroid\", \"gamepad not supported\");\n\t\t}\n\t}\n\tprivate static void handleJoystickAxis(MotionEvent event, InputDevice dev, int aaxis, int qaxis)\n\t{\n\t\ttry\n\t\t{\n\t\t\tfinal InputDevice.MotionRange range = (InputDevice.MotionRange)InputDevice_getMotionRange.invoke(dev, aaxis, event.getSource());\n\t\t\tif (range != null)\n\t\t\t{\n\t\t\t\tfinal float flat = range.getFlat();\n\t\t\t\tfloat v = (Float)MotionEvent_getAxisValueJ.invoke(event, aaxis, 0);\n\t\t\t\tif (Math.abs(v) < flat)\n\t\t\t\t\tv = 0;\t//read as 0 if its within the deadzone.\n\t\t\t\taxis(event.getDeviceId(), qaxis, v);\n\t\t\t}\n\t\t}\n\t\tcatch(Exception e)\n\t\t{\n\t\t}\n\t}\n\tprivate boolean motionEvent(MotionEvent event)\n\t{\n\t\tint id;\n\t\tfloat x, y, size;\n\t\tfinal int act = event.getAction();\n\t\tfinal int src = event.getSource();\n\n\t\t//handle gamepad axis\n\t\tif ((event.getSource() & (SOURCE_GAMEPAD|SOURCE_JOYSTICK))!=0 && event.getAction() == MotionEvent.ACTION_MOVE)\n\t\t{\n\t\t\tInputDevice dev = event.getDevice();\n\t\t\thandleJoystickAxis(event, dev, AXIS_X, 0);\n\t\t\thandleJoystickAxis(event, dev, AXIS_Y, 1);\n\t\t\thandleJoystickAxis(event, dev, AXIS_LTRIGGER, 2);\n\t\t\t\n\t\t\thandleJoystickAxis(event, dev, AXIS_Z, 3);\n\t\t\thandleJoystickAxis(event, dev, AXIS_RZ, 4);\n\t\t\thandleJoystickAxis(event, dev, AXIS_RTRIGGER, 5);\n\n\t\t\treturn true;\n\t\t}\n\n\t\tfinal int pointerCount = event.getPointerCount();\n\t\tint i;\n\t\tfor (i = 0; i < pointerCount; i++)\n\t\t{\n\t\t\tif (canrelative && src == SOURCE_MOUSE && wantrelative())\n\t\t\t{\n\t\t\t\ttry\n\t\t\t\t{\n\t\t\t\t\tx = (Float)MotionEvent_getAxisValueP.invoke(event, AXIS_RELATIVE_X, i);\n\t\t\t\t\ty = (Float)MotionEvent_getAxisValueP.invoke(event, AXIS_RELATIVE_Y, i);\n\t\t\t\t\tmotion(event.getPointerId(i), 1, x, y, 0, event.getSize(i));\n\t\t\t\t}\n\t\t\t\tcatch(Exception e)\n\t\t\t\t{\n\t\t\t\t\tandroid.util.Log.i(\"FTEDroid\", \"exception using relative mouse\");\n\t\t\t\t\tcanrelative=false;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tmotion(event.getPointerId(i), 0, event.getX(i), event.getY(i), 0, event.getSize(i));\n\t\t\t}\n\t\t}\n\n\t\tswitch(act & event.ACTION_MASK)\n\t\t{\n\t\tcase MotionEvent.ACTION_DOWN:\n\t\tcase MotionEvent.ACTION_POINTER_DOWN:\n\t\t\tid = ((act&event.ACTION_POINTER_ID_MASK) >> event.ACTION_POINTER_ID_SHIFT);\n\t\t\tx = event.getX(id);\n\t\t\ty = event.getY(id);\n\t\t\tsize = event.getSize(id);\n\t\t\tid = event.getPointerId(id);\n\t\t\tif (canbuttons && src == SOURCE_MOUSE)\n\t\t\t{\n\t\t\t\ttry {mousepress(id, (Integer)MotionEvent_getButtonState.invoke(event));}\n\t\t\t\tcatch(Exception e){}\n\t\t\t}\n\t\t\telse\n\t\t\t\tmotion(id, 2, x, y, 0, size);\n\t\t\tbreak;\n\t\tcase MotionEvent.ACTION_UP:\n\t\tcase MotionEvent.ACTION_POINTER_UP:\n\t\t\tid = ((act&event.ACTION_POINTER_ID_MASK) >> event.ACTION_POINTER_ID_SHIFT);\n\t\t\tx = event.getX(id);\n\t\t\ty = event.getY(id);\n\t\t\tsize = event.getSize(id);\n\t\t\tid = event.getPointerId(id);\n\t\t\tif (canbuttons && event.getSource() == SOURCE_MOUSE)\n\t\t\t{\n\t\t\t\ttry {mousepress(id, (Integer)MotionEvent_getButtonState.invoke(event));}\n\t\t\t\tcatch(Exception e){}\n\t\t\t}\n\t\t\telse\n\t\t\t\tmotion(id, 3, x, y, 0, size);\n\t\t\tbreak;\n\t\tcase MotionEvent.ACTION_MOVE:\n\t\t\tbreak;\n\t\tdefault:\n\t\t\treturn false;\n\t\t}\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic boolean dispatchTouchEvent(MotionEvent event)\n\t{\t//works when mouse is pressed...\n\t\treturn motionEvent(event);\n\t}\n//\t@Override\n\tpublic boolean dispatchGenericMotionEvent(MotionEvent event)\n\t{\t//works even when mouse is not pressed\n\t\treturn motionEvent(event);\n\t}\n\n\n\t//launching stuff\n\tprivate static native int unicodeKeyPress(int unicode);\n\t@Override\n\tprotected void onNewIntent(android.content.Intent intent)\n\t{\n\t\thandleIntent(intent);\n\t\tsuper.onNewIntent(intent);\n\t}\n}\n\n"
  },
  {
    "path": "engine/gl/gl_alias.c",
    "content": "\n//a note about dedicated servers:\n//In the server-side gamecode, a couple of q1 extensions require knowing something about models.\n//So we load models serverside, if required.\n\n//things we need:\n//tag/bone names and indexes so we can have reasonable modding with tags. :)\n//tag/bone positions so we can shoot from the actual gun or other funky stuff\n//vertex positions so we can trace against the mesh rather than the bbox.\n\n//we use the gl renderer's model code because it supports more sorts of models than the sw renderer. Sad but true.\n\n\n\n\n#include \"quakedef.h\"\n#include \"glquake.h\"\n#ifndef SERVERONLY\n\n#include \"com_mesh.h\"\n\n#if defined(RTLIGHTS)\nstatic int numProjectedShadowVerts;\nstatic vec3_t *ProjectedShadowVerts;\nstatic int numFacing;\nstatic qbyte *triangleFacing;\n#endif\n\n//FIXME\ntypedef struct\n{\n\tfloat\t\tscale[3];\t// multiply qbyte verts by this\n\tfloat\t\ttranslate[3];\t// then add this\n\tchar\t\tname[16];\t// frame name from grabbing\n\tdtrivertx_t\tverts[1];\t// variable sized\n} dmd2aliasframe_t;\n\n\n\nextern cvar_t gl_part_flame, r_fullbrightSkins, r_fb_models, ruleset_allow_fbmodels, gl_overbright_models;\nextern cvar_t r_noaliasshadows;\nextern cvar_t r_lodscale, r_lodbias;\n\nextern cvar_t gl_ati_truform;\nextern cvar_t r_vertexdlights;\nextern cvar_t mod_md3flags;\n//extern cvar_t r_skin_overlays;\nextern cvar_t r_globalskin_first, r_globalskin_count;\n\n#ifndef SERVERONLY\nstatic hashtable_t skincolourmapped;\n\n//q3 .skin support\nstatic skinfile_t **registeredskins;\nstatic skinid_t numregisteredskins;\nstruct cctx_s\n{\n\ttexid_t diffuse;\n\tint width;\n\tint height;\n};\nvoid Mod_FlushSkin(skinid_t id)\n{\n#ifdef QWSKINS\n\tskinfile_t *sk;\n\tid--;\n\tif (id >= numregisteredskins)\n\t\treturn;\t//invalid!\n\tsk = registeredskins[id];\n\tif (!sk)\n\t\treturn;\n\tsk->qwskin = NULL;\n#endif\n}\nvoid Mod_WipeSkin(skinid_t id, qboolean force)\n{\n\t//FIXME: skin objects should persist for a frame.\n\tskinfile_t *sk;\n\tint i;\n\tid--;\n\tif (id >= numregisteredskins)\n\t\treturn;\t//invalid!\n\tsk = registeredskins[id];\n\tif (!sk)\n\t\treturn;\n\tif (!force && --sk->refcount > 0)\n\t\treturn; //still in use.\n\tregisteredskins[id] = NULL;\n\n\tfor (i = 0; i < sk->nummappings; i++)\n\t{\n\t\tif (sk->mappings[i].needsfree)\n\t\t{\n\t\t\tImage_UnloadTexture(sk->mappings[i].texnums.base);\n\t\t\tsk->mappings[i].texnums.base = r_nulltex;\n\t\t}\n\t\tR_UnloadShader(sk->mappings[i].shader);\n\t}\n\tZ_Free(sk);\n}\nstatic void Mod_WipeAllSkins(qboolean final)\n{\n\tskinid_t id;\n\tif (final)\n\t{\n\t\tfor (id = 0; id < numregisteredskins; )\n\t\t\tMod_WipeSkin(++id, true);\n\t\tZ_Free(registeredskins);\n\t\tregisteredskins = NULL;\n\t\tnumregisteredskins = 0;\n\t}\n\telse\n\t{\n\t\tfor (id = 0; id < numregisteredskins; )\n\t\t\tMod_FlushSkin(++id);\n\t}\n}\nskinfile_t *Mod_LookupSkin(skinid_t id)\n{\n\tid--;\n\tif (id < numregisteredskins)\n\t\treturn registeredskins[id];\n\treturn NULL;\n}\nstruct composeline_s\n{\n\tshader_t *sourceimg;\n\n\tvec2_t pos, size;\n\tvec4_t tc;\n\tvec4_t rgba;\n};\nstatic void Mod_ParseComposeLine(char *texture, struct composeline_s *line)\n{\n\tint l;\n\tchar *s, tname[MAX_QPATH];\n\tfor (s = texture; *s; s++)\n\t{\n\t\tif (*s == '@' || *s == ':' || *s == '?' || *s == '*')\n\t\t\tbreak;\n\t}\n\n\tl = s - texture;\n\tif (l > MAX_QPATH-1)\n\t\tl = MAX_QPATH-1;\n\tmemcpy(tname, texture, l);\n\ttname[l] = 0;\n\n\t//load the image and set some default sizes, etc.\n\tif (*tname)\n\t\tline->sourceimg = R2D_SafeCachePic(tname);\n\telse\n\t\tline->sourceimg = NULL;\n\n\tVector2Set(line->pos, 0, 0);\n\tVector2Set(line->size, -1, -1);\n\tVector4Set(line->tc, 0, 0, 1, 1);\n\tVector4Set(line->rgba, 1, 1, 1, 1);\n\n\twhile(*s)\n\t{\n\t\tswitch(*s)\n\t\t{\n\t\tcase '@':\n\t\t\tline->pos[0] = strtod(s+1, &s); \n\t\t\tif (*s == ',')\n\t\t\t\ts++;\n\t\t\tline->pos[1] = strtod(s, &s); \n\t\t\tbreak;\n\t\tcase ':':\n\t\t\tline->size[0] = strtod(s+1, &s); \n\t\t\tif (*s == ',')\n\t\t\t\ts++;\n\t\t\tline->size[1] = strtod(s, &s); \n\t\t\tbreak;\n\t\tcase '$':\n\t\t\tline->tc[0] = strtod(s+1, &s); \n\t\t\tif (*s == ',')\n\t\t\t\ts++;\n\t\t\tline->tc[1] = strtod(s, &s);\n\t\t\tif (*s == ',')\n\t\t\t\ts++;\n\t\t\tline->tc[2] = strtod(s, &s); \n\t\t\tif (*s == ',')\n\t\t\t\ts++;\n\t\t\tline->tc[3] = strtod(s, &s); \n\t\t\tbreak;\n\t\tcase '?':\n\t\t\tline->rgba[0] = strtod(s+1, &s); \n\t\t\tif (*s == ',')\n\t\t\t\ts++;\n\t\t\tline->rgba[1] = strtod(s, &s); \n\t\t\tif (*s == ',')\n\t\t\t\ts++;\n\t\t\tline->rgba[2] = strtod(s, &s); \n\t\t\tif (*s == ',')\n\t\t\t\ts++;\n\t\t\tline->rgba[3] = strtod(s, &s);\n\t\t\tbreak;\n//\t\tcase '*':\n//\t\t\tbreak;\n\t\tdefault:\n\t\t\ts+=strlen(s);\t//some sort of error or other thing we don't support\n\t\t\tbreak;\n\t\t}\n\t}\n}\nstatic void Mod_ComposeSkin(char *texture, struct cctx_s *cctx, struct composeline_s *line)\n{\n\tint iw=0, ih=0;\n\n\tif (!line->sourceimg || R_GetShaderSizes(line->sourceimg, &iw, &ih, false) != 1)\t//no shader? no point in doing anything.\n\t{\n\t\tiw = ih = 0;\n\t\tline->sourceimg = NULL;\n\t}\n\tif (line->size[0] < 0)\n\t\tline->size[0] = iw;\n\tif (line->size[1] < 0)\n\t\tline->size[1] = ih;\n\n\tif (line->size[0]>0 && line->size[1]>0)\n\t{\n\t\t//create a render target if one is not already selected\n\t\tif (!TEXVALID(cctx->diffuse))\n\t\t{\n\t\t\tif (R2D_Flush)\n\t\t\t\tR2D_Flush();\n\n\t\t\tstrcpy(r_refdef.rt_destcolour[0].texname, \"-\");\n\t\t\tcctx->width = line->pos[0]+line->size[0];\n\t\t\tcctx->height = line->pos[1]+line->size[1];\n\t\t\tcctx->diffuse = R2D_RT_Configure(r_refdef.rt_destcolour[0].texname, cctx->width, cctx->height, TF_RGBA32, RT_IMAGEFLAGS);\n\t\t\tBE_RenderToTextureUpdate2d(true);\n\t\t}\n\n\t\tif (line->sourceimg)\n\t\t{\n\t\t\tR2D_ImageColours(line->rgba[0],line->rgba[1],line->rgba[2],line->rgba[3]);\n\t\t\tR2D_Image(line->pos[0], line->pos[1], line->size[0], line->size[1], line->tc[0], line->tc[1], line->tc[2], line->tc[3], line->sourceimg);\n\t\t}\n\t}\n\n\tif (R2D_Flush)\n\t\tR2D_Flush();\n\tR_UnloadShader(line->sourceimg);\t//we're done with it now\n}\n//create a new skin with explicit name and text. even if its already loaded. this means you can free it safely.\nskinid_t Mod_ReadSkinFile(const char *skinname, const char *skintext)\n{\n\tskinid_t id;\n\tchar *nl;\n\tskinfile_t *skin;\n\tchar shadername[MAX_QPATH];\n\n\tfor (id = 0; id < numregisteredskins; id++)\n\t{\n\t\tif (!registeredskins[id])\n\t\t\tbreak;\n\t}\n\tif (id == numregisteredskins)\n\t{\t\n\t\tint newn = numregisteredskins + 64;\n\t\tregisteredskins = BZ_Realloc(registeredskins, sizeof(*registeredskins) * newn);\n\t\tmemset(registeredskins + numregisteredskins, 0, (newn-numregisteredskins)*sizeof(*registeredskins));\n\t\tnumregisteredskins = newn;\n\t}\n\n\tskin = Z_Malloc(sizeof(*skin) - sizeof(skin->mappings) + sizeof(skin->mappings[0])*4);\n\tskin->refcount++;\n\tskin->maxmappings = 4;\n\tQ_strncpyz(skin->skinname, skinname, sizeof(skin->skinname));\n#ifdef QWSKINS\n\tskin->q1lower = Q1UNSPECIFIED;\n\tskin->q1upper = Q1UNSPECIFIED;\n#ifdef HEXEN2\n\tskin->h2class = Q1UNSPECIFIED;\n#endif\n#endif\n\n\n\twhile(skintext)\n\t{\n\t\tif (skin->nummappings == skin->maxmappings)\n\t\t{\n\t\t\tskin->maxmappings += 4;\n\t\t\tskin = BZ_Realloc(skin, sizeof(*skin) - sizeof(skin->mappings) + sizeof(skin->mappings[0])*skin->maxmappings);\n\t\t}\n\n\t\tskintext = COM_ParseToken(skintext, \",\");\n\t\tif (!skintext)\n\t\t\tbreak;\n\t\tif (!strcmp(com_token, \"replace\"))\n\t\t{\n\t\t\tskintext = COM_ParseToken(skintext, NULL);\n\n\t\t\tif (com_tokentype != TTP_LINEENDING)\n\t\t\t{\n\t\t\t\tQ_strncpyz(skin->mappings[skin->nummappings].surface, com_token, sizeof(skin->mappings[skin->nummappings].surface));\n\t\t\t\tskintext = COM_ParseToken(skintext, NULL);\n\t\t\t\tQ_strncpyz(shadername, com_token, sizeof(shadername));\n\t\t\t\tskin->mappings[skin->nummappings].shader = R_RegisterSkin(NULL, shadername);\n\t\t\t\tR_BuildDefaultTexnums(NULL, skin->mappings[skin->nummappings].shader, 0);\n\t\t\t\tskin->mappings[skin->nummappings].texnums = *skin->mappings[skin->nummappings].shader->defaulttextures;\n\t\t\t\tskin->mappings[skin->nummappings].needsfree = false;\n\t\t\t\tskin->nummappings++;\n\t\t\t}\n\t\t}\n\t\telse if (!strcmp(com_token, \"compose\"))\n\t\t{\n\t\t\tskintext = COM_ParseToken(skintext, NULL);\n\t\t\t//body\n\t\t\tif (com_tokentype != TTP_LINEENDING)\n\t\t\t{\n\t\t\t\tsize_t l,lines;\n\t\t\t\tstruct composeline_s line[64];\n\t\t\t\t//fixme: this blocks waiting for the textures to load.\n\t\t\t\tstruct cctx_s cctx;\n\t\t\t\tmemset(&cctx, 0, sizeof(cctx));\n\n\t\t\t\tQ_strncpyz(skin->mappings[skin->nummappings].surface, com_token, sizeof(skin->mappings[skin->nummappings].surface));\n\t\t\t\tskintext = COM_ParseToken(skintext, NULL);\n\t\t\t\tQ_strncpyz(shadername, com_token, sizeof(shadername));\n\t\t\t\tskin->mappings[skin->nummappings].shader = R_RegisterSkin(NULL, shadername);\n\t\t\t\tR_BuildDefaultTexnums(NULL, skin->mappings[skin->nummappings].shader, 0);\n\t\t\t\tskin->mappings[skin->nummappings].texnums = *skin->mappings[skin->nummappings].shader->defaulttextures;\n\n\t\t\t\t//parse the lines, and start to load the various shaders.\n\t\t\t\tfor(lines = 0;lines<countof(line);)\n\t\t\t\t{\n\t\t\t\t\twhile(*skintext == ' ' || *skintext == '\\t')\n\t\t\t\t\t\tskintext++;\n\t\t\t\t\tif (*skintext == '+')\n\t\t\t\t\t\tskintext++;\n\t\t\t\t\telse\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tskintext = COM_Parse(skintext);\n\t\t\t\t\tMod_ParseComposeLine(com_token, &line[lines++]);\n\t\t\t\t}\n\t\t\t\t//all the textures should be loading now... block while waiting for them (sucks)\n\t\t\t\tfor (l = 0; l < lines; l++)\n\t\t\t\t\tR_GetShaderSizes(line[l].sourceimg, NULL, NULL, true);\n\t\t\t\t//okay, they're loaded, do the compose now.\n\t\t\t\tfor (l = 0; l < lines; l++)\n\t\t\t\t\tMod_ComposeSkin(com_token, &cctx, &line[l]);\n\t\t\t\t*r_refdef.rt_destcolour[0].texname = 0;\n\t\t\t\tBE_RenderToTextureUpdate2d(true);\n\n\t\t\t\tskin->mappings[skin->nummappings].needsfree = 1;\n\t\t\t\tskin->mappings[skin->nummappings].texnums.base = cctx.diffuse;\n\t\t\t\tskin->nummappings++;\n\t\t\t}\n\t\t}\n\t\telse if (!strcmp(com_token, \"geomset\") || !strcmp(com_token, \"meshgroup\"))\n\t\t{\n\t\t\tunsigned int set;\n\t\t\tskintext = COM_ParseToken(skintext, NULL);\n\t\t\tset = atoi(com_token);\n\n\t\t\tif (com_tokentype != TTP_LINEENDING)\n\t\t\t{\n\t\t\t\tskintext = COM_ParseToken(skintext, NULL);\n\t\t\t\tif (set < MAX_GEOMSETS)\n\t\t\t\t\tskin->geomset[set] = atoi(com_token);\n\t\t\t}\n\t\t}\n\t\telse if (!strncmp(com_token, \"tag_\", 4))\n\t\t{\n\t\t\t//ignore it. matches q3.\n\t\t}\n#ifdef QWSKINS\n\t\telse if (!strcmp(com_token, \"qwskin\"))\n\t\t{\n\t\t\tskintext = COM_ParseToken(skintext, NULL);\n\t\t\tQ_strncpyz(skin->qwskinname, com_token, sizeof(skin->qwskinname));\n\t\t}\n\t\telse if (!strcmp(com_token, \"q1lower\"))\n\t\t{\n\t\t\tskintext = COM_ParseToken(skintext, NULL);\n\t\t\tif (!strncmp(com_token, \"0x\", 2))\n\t\t\t\tskin->q1lower = 0xff000000|strtoul(com_token+2, NULL, 16);\n\t\t\telse\n\t\t\t\tskin->q1lower = atoi(com_token);\n\t\t}\n\t\telse if (!strcmp(com_token, \"q1upper\"))\n\t\t{\n\t\t\tskintext = COM_ParseToken(skintext, NULL);\n\t\t\tif (!strncmp(com_token, \"0x\", 2))\n\t\t\t\tskin->q1upper = 0xff000000|strtoul(com_token+2, NULL, 16);\n\t\t\telse\n\t\t\t\tskin->q1upper = atoi(com_token);\n\t\t}\n#ifdef HEXEN2\n\t\telse if (!strcmp(com_token, \"h2class\"))\n\t\t{\n\t\t\tskintext = COM_ParseToken(skintext, NULL);\n\t\t\tskin->h2class = atoi(com_token);\n\t\t}\n#endif\n#endif\n\t\telse\n\t\t{\n\t\t\twhile(*skintext == ' ' || *skintext == '\\t')\n\t\t\t\tskintext++;\n\t\t\tif (*skintext == ',')\n\t\t\t{\n\t\t\t\tQ_strncpyz(skin->mappings[skin->nummappings].surface, com_token, sizeof(skin->mappings[skin->nummappings].surface));\n\t\t\t\tskintext = COM_ParseToken(skintext+1, NULL);\n\t\t\t\tQ_strncpyz(shadername, com_token, sizeof(shadername));\n\t\t\t\tskin->mappings[skin->nummappings].shader = R_RegisterCustom (NULL, shadername, 0, Shader_DefaultSkin, NULL);\n\t\t\t\tR_BuildDefaultTexnums(NULL, skin->mappings[skin->nummappings].shader, 0);\n\t\t\t\tskin->mappings[skin->nummappings].texnums = *skin->mappings[skin->nummappings].shader->defaulttextures;\n\t\t\t\tskin->mappings[skin->nummappings].needsfree = false;\n\t\t\t\tskin->nummappings++;\n\t\t\t}\n\t\t}\n\n\t\tif (com_tokentype == TTP_LINEENDING || !skintext)\n\t\t\tcontinue;\n\t\tnl = strchr(skintext, '\\n');\n\t\tif (!nl)\n\t\t\tskintext = skintext+strlen(skintext);\n\t\telse\n\t\t\tskintext = nl+1;\n\t\tif (!*skintext)\n\t\t\tbreak;\n\t}\n\n\tregisteredskins[id] = skin;\n\n#ifdef QWSKINS\n\t//warm it up, hopefully before its stictly necessary.\n\tif (*skin->qwskinname)\n\t\tSkin_Lookup (skin->qwskinname);\n#endif\n\n\treturn id+1;\n}\n//registers a skin. loads it if there's not one with that name already registered.\n//returns 0 if it failed.\nskinid_t Mod_RegisterSkinFile(const char *skinname)\n{\n\tchar *f;\n\tskinid_t id;\n\t//block duplicates\n\tfor (id = 0; id < numregisteredskins; id++)\n\t{\n\t\tif (!registeredskins[id])\n\t\t\tcontinue;\n\t\tif (!strcmp(skinname, registeredskins[id]->skinname))\n\t\t{\n\t\t\tregisteredskins[id]->refcount++;\n\t\t\treturn id+1;\n\t\t}\n\t}\n\tf = FS_LoadMallocFile(skinname, NULL);\n\tif (!f)\n\t\treturn 0;\n\tid = Mod_ReadSkinFile(skinname, f);\n\tZ_Free(f);\n\treturn id;\n}\n\n\n//changes vertex lighting values\n#if 0\nstatic void R_GAliasApplyLighting(mesh_t *mesh, vec3_t org, vec3_t angles, float *colormod)\n{\n\tint l, v;\n\tvec3_t rel;\n\tvec3_t dir;\n\tfloat dot, d, a;\n\n\tif (mesh->colors4f_array)\n\t{\n\t\tfloat l;\n\t\tint temp;\n\t\tint i;\n\t\tavec4_t *colours = mesh->colors4f_array;\n\t\tvec3_t *normals = mesh->normals_array;\n\t\tvec3_t ambient, shade;\n\t\tqbyte alphab = bound(0, colormod[3], 1);\n\t\tif (!mesh->normals_array)\n\t\t{\n\t\t\tmesh->colors4f_array = NULL;\n\t\t\treturn;\n\t\t}\n\n\t\tVectorCopy(ambientlight, ambient);\n\t\tVectorCopy(shadelight, shade);\n\n\t\tfor (i = 0; i < 3; i++)\n\t\t{\n\t\t\tambient[i] *= colormod[i];\n\t\t\tshade[i] *= colormod[i];\n\t\t}\n\n\n\t\tfor (i = mesh->numvertexes-1; i >= 0; i--)\n\t\t{\n\t\t\tl = DotProduct(normals[i], shadevector);\n\n\t\t\ttemp = l*ambient[0]+shade[0];\n\t\t\tcolours[i][0] = temp;\n\n\t\t\ttemp = l*ambient[1]+shade[1];\n\t\t\tcolours[i][1] = temp;\n\n\t\t\ttemp = l*ambient[2]+shade[2];\n\t\t\tcolours[i][2] = temp;\n\n\t\t\tcolours[i][3] = alphab;\n\t\t}\n\t}\n\n\tif (r_vertexdlights.value && mesh->colors4f_array)\n\t{\n\t\t//don't include world lights\n\t\tfor (l=rtlights_first ; l<RTL_FIRST; l++)\n\t\t{\n\t\t\tif (cl_dlights[l].radius)\n\t\t\t{\n\t\t\t\tVectorSubtract (cl_dlights[l].origin,\n\t\t\t\t\t\t\t\torg,\n\t\t\t\t\t\t\t\tdir);\n\t\t\t\tif (Length(dir)>cl_dlights[l].radius+mesh->radius)\t//far out man!\n\t\t\t\t\tcontinue;\n\n\t\t\t\trel[0] = -DotProduct(dir, currententity->axis[0]);\n\t\t\t\trel[1] = -DotProduct(dir, currententity->axis[1]);\t//quake's crazy.\n\t\t\t\trel[2] = -DotProduct(dir, currententity->axis[2]);\n\t/*\n\t\t\t\tglBegin(GL_LINES);\n\t\t\t\tglVertex3f(0,0,0);\n\t\t\t\tglVertex3f(rel[0],rel[1],rel[2]);\n\t\t\t\tglEnd();\n\t*/\n\t\t\t\tfor (v = 0; v < mesh->numvertexes; v++)\n\t\t\t\t{\n\t\t\t\t\tVectorSubtract(mesh->xyz_array[v], rel, dir);\n\t\t\t\t\tdot = DotProduct(dir, mesh->normals_array[v]);\n\t\t\t\t\tif (dot>0)\n\t\t\t\t\t{\n\t\t\t\t\t\td = DotProduct(dir, dir);\n\t\t\t\t\t\ta = 1/d;\n\t\t\t\t\t\tif (a>0)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ta *= 10000000*dot/sqrt(d);\n\t\t\t\t\t\t\tmesh->colors4f_array[v][0] += a*cl_dlights[l].color[0];\n\t\t\t\t\t\t\tmesh->colors4f_array[v][1] += a*cl_dlights[l].color[1];\n\t\t\t\t\t\t\tmesh->colors4f_array[v][2] += a*cl_dlights[l].color[2];\n\t\t\t\t\t\t}\n\t//\t\t\t\t\telse\n\t//\t\t\t\t\t\tmesh->colors4f_array[v][1] = 1;\n\t\t\t\t\t}\n\t//\t\t\t\telse\n\t//\t\t\t\t\tmesh->colors4f_array[v][2] = 1;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n#endif\n/*\nvoid GL_GAliasFlushOneSkin(char *skinname)\n{\n\tint i;\n\tbucket_t **l;\n\tgaliascolourmapped_t *cm;\n\tfor (i = 0; i < skincolourmapped.numbuckets; i++)\n\t{\n\t\tfor(l = &skincolourmapped.bucket[i]; *l; )\n\t\t{\n\t\t\tcm = (*l)->data;\n\t\t\tif (strstr(cm->name, skinname))\n\t\t\t{\n\t\t\t\t*l = cm->bucket.next;\n\t\t\t\tBZ_Free(cm);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tl = &(*l)->next;\n\t\t}\n\t}\n}*/\n\n//final is set when the renderer is going down.\n//if not set, this is mid-map, and everything should be regeneratable.\nvoid R_GAliasFlushSkinCache(qboolean final)\n{\n\tint i;\n\tbucket_t *b;\n\tfor (i = 0; i < skincolourmapped.numbuckets; i++)\n\t{\n\t\twhile((b = skincolourmapped.bucket[i]))\n\t\t{\n\t\t\tskincolourmapped.bucket[i] = b->next;\n\t\t\tBZ_Free(b->data);\n\t\t}\n\t}\n\tif (skincolourmapped.bucket)\n\t\tBZ_Free(skincolourmapped.bucket);\n\tskincolourmapped.bucket = NULL;\n\tskincolourmapped.numbuckets = 0;\n\n#ifdef RTLIGHTS\n\tBZ_Free(ProjectedShadowVerts);\n\tProjectedShadowVerts = NULL;\n\tnumProjectedShadowVerts = 0;\n\t\n\tBZ_Free(triangleFacing);\n\ttriangleFacing = NULL;\n\tnumFacing = 0;\n#endif\n\n\tMod_WipeAllSkins(final);\n}\n\nstatic shader_t *GL_ChooseSkin(galiasinfo_t *inf, model_t *model, int surfnum, entity_t *e, texnums_t **forcedtex)\n{\n\tgaliasskin_t *skins;\n#ifdef QWSKINS\n\tshader_t *shader;\n\tqwskin_t *plskin = NULL;\n\tunsigned int subframe;\n\tunsigned int tc = e->topcolour, bc = e->bottomcolour;\n#ifdef HEXEN2\n\tunsigned int pc = e->h2playerclass;\n#else\n\tunsigned int pc = 0;\n#endif\n\tqboolean generateupperlower = false;\n\tqboolean forced;\n\textern int cl_playerindex;\t//so I don't have to strcmp\n#endif\n\tint frame;\n\n\t*forcedtex = NULL;\n\t/*q3 .skin files*/\n\tif (e->customskin)\n\t{\n\t\tskinfile_t *sk = Mod_LookupSkin(e->customskin);\n\t\tif (sk)\n\t\t{\n\t\t\tint i, fallback=-1;\n\t\t\tif (inf->geomset < MAX_GEOMSETS && sk->geomset[inf->geomset] != inf->geomid)\n\t\t\t\treturn NULL;\t//don't allow this surface to be drawn.\n\t\t\tfor (i = 0; i < sk->nummappings; i++)\n\t\t\t{\n\t\t\t\tif (!strcmp(sk->mappings[i].surface, inf->surfacename))\n\t\t\t\t{\n\t\t\t\t\t*forcedtex = &sk->mappings[i].texnums;\n\t\t\t\t\treturn sk->mappings[i].shader;\n\t\t\t\t}\n\t\t\t\tif (!*sk->mappings[i].surface)\n\t\t\t\t\tfallback = i;\n\t\t\t}\n\t\t\tif (fallback >= 0)\n\t\t\t{\n\t\t\t\t*forcedtex = &sk->mappings[fallback].texnums;\n\t\t\t\treturn sk->mappings[fallback].shader;\n\t\t\t}\n#ifdef QWSKINS\n\t\t\tif (sk->q1lower != Q1UNSPECIFIED)\n\t\t\t\tbc = e->bottomcolour = sk->q1lower;\n\t\t\tif (sk->q1upper != Q1UNSPECIFIED)\n\t\t\t\ttc = e->topcolour = sk->q1upper;\n#ifdef HEXEN2\n\t\t\tif (sk->h2class != Q1UNSPECIFIED)\n\t\t\t\tpc = sk->h2class;\n#endif\n\t\t\tif (!sk->qwskin && *sk->qwskinname)\n\t\t\t\tsk->qwskin = Skin_Lookup(sk->qwskinname);\n\t\t\tplskin = sk->qwskin;\n#endif\n\t\t}\n\t}\n\telse if (inf->geomset < MAX_GEOMSETS && 0 != inf->geomid)\n\t\treturn NULL;\n\n\n\t/*hexen2 feature: global skins */\n\tif (inf->numskins < e->skinnum && e->skinnum >= r_globalskin_first.ival && e->skinnum < r_globalskin_first.ival+r_globalskin_count.ival)\n\t{\n\t\tshader_t *s;\n\t\ts = R_RegisterSkin(NULL, va(\"gfx/skin%d.lmp\", e->skinnum));\n\t\tif (s)\n\t\t{\n\t\t\tif (!TEXVALID(s->defaulttextures->base))\n\t\t\t\tR_BuildDefaultTexnums(NULL, s, 0);\n\t\t\treturn s;\n\t\t}\n\t}\n\n#ifdef QWSKINS\n\tif ((e->model->engineflags & MDLF_NOTREPLACEMENTS) && !ruleset_allow_sensitive_texture_replacements.ival)\n\t\tforced = true;\n\telse\n\t\tforced = false;\n\n\tif (!gl_nocolors.ival || forced)\n\t{\n\t\tif (plskin && plskin->loadstate == SKIN_LOADING)\n\t\t\tplskin = NULL;\n\t\telse if (!plskin || plskin->loadstate != SKIN_LOADED)\n\t\t{\n\t\t\tif (e->playerindex >= 0 && e->playerindex <= MAX_CLIENTS)\n\t\t\t{\n\t\t\t\t//heads don't get skinned, only players (and weaponless players), they do still get recoloured.\n\t\t\t\tif (model==cl.model_precache[cl_playerindex]\n#ifdef HAVE_LEGACY\n\t\t\t\t\t|| model==cl.model_precache_vwep[0]\n#endif\n\t\t\t\t)\n\t\t\t\t{\n\t\t\t\t\tif (!cl.players[e->playerindex].qwskin)\n\t\t\t\t\t\tSkin_Find(&cl.players[e->playerindex]);\n\t\t\t\t\tplskin = cl.players[e->playerindex].qwskin;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tplskin = NULL;\n\n\t\t\t\tif (plskin && plskin->loadstate < SKIN_LOADED)\n\t\t\t\t{\n\t\t\t\t\tSkin_TryCache8(plskin);\t//we're not going to use it, but make sure its status is updated when it is finally loaded..\n\t\t\t\t\tif (plskin->loadstate < SKIN_LOADED)\n\t\t\t\t\t{\n\t\t\t\t\t\tplskin = cl.players[e->playerindex].lastskin;\n\t\t\t\t\t\tif (!plskin || plskin->loadstate < SKIN_LOADED)\n\t\t\t\t\t\t\treturn NULL;\t//just wait for it to finish loading so we don't generate pointless skins.\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tcl.players[e->playerindex].lastskin = plskin;\n\t\t\t}\n\t\t\telse\n\t\t\t\tplskin = NULL;\n\t\t}\n\n\t\tif (forced || tc != TOP_DEFAULT || bc != BOTTOM_DEFAULT || plskin)\n\t\t{\n\t\t\tint\t\t\tinwidth, inheight;\n\t\t\tint\t\t\ttinwidth, tinheight;\n\t\t\tchar *skinname;\n\t\t\tqbyte\t*original;\n\t\t\tgaliascolourmapped_t *cm;\n\t\t\tchar hashname[512];\n\n\t\t\tif (!skincolourmapped.numbuckets)\n\t\t\t{\n\t\t\t\tint bucketcount = 256;\n\t\t\t\tvoid *buckets = BZ_Malloc(Hash_BytesForBuckets(bucketcount));\n\t\t\t\tmemset(buckets, 0, Hash_BytesForBuckets(bucketcount));\n\t\t\t\tHash_InitTable(&skincolourmapped, bucketcount, buckets);\n\t\t\t}\n\n\t\t\tif (!inf->numskins)\n\t\t\t{\n\t\t\t\t/*model has no skins*/\n\t\t\t\tskins = NULL;\n\t\t\t\tsubframe = 0;\n\t\t\t\tshader = NULL;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tskins = inf->ofsskins;\n\t\t\t\tif (e->skinnum >= 0 && e->skinnum < inf->numskins)\n\t\t\t\t\tskins += e->skinnum;\n\n\t\t\t\tif (!skins->numframes)\n\t\t\t\t{\n\t\t\t\t\t/*model has a skin, but has no framegroups*/\n\t\t\t\t\tskins = NULL;\n\t\t\t\t\tsubframe = 0;\n\t\t\t\t\tshader = NULL;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tsubframe = cl.time*skins->skinspeed;\n\t\t\t\t\tsubframe = subframe%skins->numframes;\n\n\t\t\t\t\tshader = skins->frame[subframe].shader;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (shader)\n\t\t\t{\n\t\t\t\tif (!plskin)\n\t\t\t\t{\n\t\t\t\t\ttexnums_t *tex = shader->defaulttextures;\n\t\t\t\t\t//do this for the loading case too, in the hope that it'll avoid generating a per-player skin at all\n\t\t\t\t\tif ((tex->base && (tex->base->status == TEX_LOADING)) ||\n\t\t\t\t\t\t(tex->loweroverlay && (tex->loweroverlay->status == TEX_LOADING || tex->loweroverlay->status == TEX_LOADED)) ||\n\t\t\t\t\t\t(tex->upperoverlay && (tex->upperoverlay->status == TEX_LOADING || tex->upperoverlay->status == TEX_LOADED)))\n\t\t\t\t\t\treturn shader;\n\t\t\t\t\t//if we've got a replacement texture (read: its size differs from the proper skin size) then don't use the base texels for colourmapping.\n\t\t\t\t\tif (tex->base && skins &&\n\t\t\t\t\t\t(tex->base->width!=skins->skinwidth || tex->base->height!=skins->skinheight))\n\t\t\t\t\t\treturn shader;\n\t\t\t\t}\n\t\t\t\tif ((shader->flags & SHADER_HASTOPBOTTOM) && !h2playertranslations)\n\t\t\t\t{\t//this shader will try to do top+bottom colours. this means we can generate only a black image, with separate top+bottom textures.\n\t\t\t\t\ttc = 0xfe000000;\n\t\t\t\t\tbc = 0xfe000000;\n\t\t\t\t\tgenerateupperlower = true;\n\t\t\t\t}\n\t\t\t\tif (!skins || !skins->numframes || !skins->frame[subframe].texels)\n\t\t\t\t{\t//no top/bottom colourmapping possible here. don't cache a million different values.\n\t\t\t\t\ttc = 0xfe000000;\n\t\t\t\t\tbc = 0xfe000000;\n\t\t\t\t\tgenerateupperlower = true;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (plskin)\n\t\t\t{\n\t\t\t\tsnprintf(hashname, sizeof(hashname), \"%s$%s$%i\", model->name, plskin->name, surfnum);\n\t\t\t\tskinname = hashname;\n\t\t\t}\n\t\t\telse if (surfnum)\n\t\t\t{\n\t\t\t\tsnprintf(hashname, sizeof(hashname), \"%s$%i\", model->name, surfnum);\n\t\t\t\tskinname = hashname;\n\t\t\t}\n\t\t\telse\n\t\t\t\tskinname = model->name;\n\n\t\t\tfor (cm = Hash_Get(&skincolourmapped, skinname); cm; cm = Hash_GetNext(&skincolourmapped, skinname, cm))\n\t\t\t{\n\t\t\t\tif (cm->tcolour == tc && cm->bcolour == bc && cm->skinnum == e->skinnum && cm->subframe == subframe && cm->pclass == pc)\n\t\t\t\t{\n\t\t\t\t\t*forcedtex = &cm->texnum;\n\t\t\t\t\tif (!shader)\n\t\t\t\t\t\tshader = R_RegisterSkin(model, skinname);\n\t\t\t\t\treturn shader;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (plskin)\n\t\t\t{\n\t\t\t\toriginal = Skin_TryCache8(plskin);\t//will start it loading if not already loaded.\n\t\t\t\tif (plskin->loadstate == SKIN_LOADING)\n\t\t\t\t\treturn shader;\n\t\t\t\tinwidth = plskin->width;\n\t\t\t\tinheight = plskin->height;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\toriginal = NULL;\n\t\t\t\tinwidth = 0;\n\t\t\t\tinheight = 0;\n\t\t\t}\n\n\t\t\t//colourmap isn't present yet.\n\t\t\tcm = Z_Malloc(sizeof(*cm));\n\t\t\t*forcedtex = &cm->texnum;\n\t\t\tQ_strncpyz(cm->name, skinname, sizeof(cm->name));\n\t\t\tHash_Add(&skincolourmapped, cm->name, cm, &cm->bucket);\n\t\t\tcm->tcolour = tc;\n\t\t\tcm->bcolour = bc;\n\t\t\tcm->pclass = pc;\t//is this needed? surely it'll be baked as part of the modelname?\n\t\t\tcm->skinnum = e->skinnum;\n\t\t\tcm->subframe = subframe;\n\n\t\t\t//q2 has no surfaces in its player models, so don't crash from that\n\t\t\t//note that q2 should also always have a custom skin set. its not our problem (here) if it doesn't.\n\t\t\tif (!shader)\n\t\t\t\tshader = R_RegisterSkin(model, skinname);\n\n\t\t\tcm->texnum.bump = shader->defaulttextures->bump;\t//can't colour bumpmapping\n\t\t\tif (plskin)\n\t\t\t{\n\t\t\t\tif (TEXLOADED(plskin->textures.base))\n\t\t\t\t{\n\t\t\t\t\tcm->texnum.loweroverlay = plskin->textures.loweroverlay;\n\t\t\t\t\tcm->texnum.upperoverlay = plskin->textures.upperoverlay;\n\t\t\t\t\tcm->texnum.base = plskin->textures.base;\n\t\t\t\t\tcm->texnum.fullbright = plskin->textures.fullbright;\n\t\t\t\t\tcm->texnum.specular = plskin->textures.specular;\n\t\t\t\t\tcm->texnum.paletted = r_nulltex;\n\t\t\t\t\tcm->texnum.reflectcube = r_nulltex;\n\t\t\t\t\tcm->texnum.reflectmask = r_nulltex;\n\t\t\t\t\tcm->texnum.displacement = r_nulltex;\n\t\t\t\t\treturn shader;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!original)\n\t\t\t{\n\t\t\t\tif (skins && skins->numframes && skins->frame[subframe].texels)\n\t\t\t\t{\n\t\t\t\t\toriginal = skins->frame[subframe].texels;\n\t\t\t\t\tinwidth = skins->skinwidth;\n\t\t\t\t\tinheight = skins->skinheight;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\toriginal = NULL;\n\t\t\t\t\tinwidth = 0;\n\t\t\t\t\tinheight = 0;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (skins)\n\t\t\t{\n\t\t\t\ttinwidth = skins->skinwidth;\n\t\t\t\ttinheight = skins->skinheight;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\ttinwidth = inwidth;\n\t\t\t\ttinheight = inheight;\n\t\t\t}\n\t\t\tif (original)\n\t\t\t{\n\t\t\t\tint i, j;\n\t\t\t\tunsigned translate32[256];\n\t\t\t\tunsigned\t*pixels;\n\t\t\t\tunsigned\t*out;\n\t\t\t\tunsigned\tfrac, fracstep;\n\n\t\t\t\tunsigned\tscaled_width, scaled_height;\n\t\t\t\tqbyte\t\t*inrow;\n\n\t\t\t\tcm->texnum.base = r_nulltex;\n\t\t\t\tcm->texnum.fullbright = r_nulltex;\n\n\t\t\t\tscaled_width = gl_max_size.value < 512 ? gl_max_size.value : 512;\n\t\t\t\tscaled_height = gl_max_size.value < 512 ? gl_max_size.value : 512;\n\n\t\t\t\t//handle the case of an external skin being smaller than the texture that its meant to replace\n\t\t\t\t//(to support the evil hackage of the padding on the outside of common qw skins)\n\t\t\t\tif (tinwidth > inwidth)\n\t\t\t\t\ttinwidth = inwidth;\n\t\t\t\tif (tinheight > inheight)\n\t\t\t\t\ttinheight = inheight;\n\n\t\t\t\t//don't make scaled width any larger than it needs to be\n\t\t\t\tif (sh_config.texture_non_power_of_two)\n\t\t\t\t{\n\t\t\t\t\tscaled_width = tinwidth;\n\t\t\t\t\tscaled_height = tinheight;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tfor (i = 0; i < 10; i++)\n\t\t\t\t\t{\n\t\t\t\t\t\tscaled_width = (1<<i);\n\t\t\t\t\t\tif (scaled_width >= tinwidth)\n\t\t\t\t\t\t\tbreak;\t//its covered\n\t\t\t\t\t}\n\t\t\t\t\tfor (i = 0; i < 10; i++)\n\t\t\t\t\t{\n\t\t\t\t\t\tscaled_height = (1<<i);\n\t\t\t\t\t\tif (scaled_height >= tinheight)\n\t\t\t\t\t\t\tbreak;\t//its covered\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (scaled_width > gl_max_size.value)\n\t\t\t\t\tscaled_width = gl_max_size.value;\t//whoops, we made it too big\n\t\t\t\tif (scaled_height > gl_max_size.value)\n\t\t\t\t\tscaled_height = gl_max_size.value;\t//whoops, we made it too big\n\n\t\t\t\tif (scaled_width < 4)\n\t\t\t\t\tscaled_width = 4;\n\t\t\t\tif (scaled_height < 4)\n\t\t\t\t\tscaled_height = 4;\n\n#ifdef HEXEN2\n\t\t\t\tif (h2playertranslations && pc && pc >= 1 && pc < 6)\n\t\t\t\t{\n\t\t\t\t\tunsigned int color_offsets[5] = {2*14*256,0,1*14*256,2*14*256,2*14*256};\n\t\t\t\t\tunsigned char *colorA, *colorB, *sourceA, *sourceB;\n\t\t\t\t\tcolorA = h2playertranslations + 256 + color_offsets[pc-1];\n\t\t\t\t\tcolorB = colorA + 256;\n\t\t\t\t\tsourceA = colorB + ((tc>10)?0:(tc * 256)); //hexen2 allows only colours 0..10 inclusive.\n\t\t\t\t\tsourceB = colorB + ((bc>10)?0:(bc * 256));\n\t\t\t\t\tfor(i=0;i<256;i++)\n\t\t\t\t\t{\n\t\t\t\t\t\ttranslate32[i] = d_8to24rgbtable[i];\n\t\t\t\t\t\tif (tc > 0 && (colorA[i] != 255))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (tc >= 16)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tunsigned int v = d_8to24rgbtable[colorA[i]];\n\t\t\t\t\t\t\t\tv = max(max((v>>0)&0xff, (v>>8)&0xff), (v>>16)&0xff);\n\t\t\t\t\t\t\t\t*((unsigned char*)&translate32[i]+0) = (((tc&0xff0000)>>16)*v)>>8;\n\t\t\t\t\t\t\t\t*((unsigned char*)&translate32[i]+1) = (((tc&0x00ff00)>> 8)*v)>>8;\n\t\t\t\t\t\t\t\t*((unsigned char*)&translate32[i]+2) = (((tc&0x0000ff)>> 0)*v)>>8;\n\t\t\t\t\t\t\t\t*((unsigned char*)&translate32[i]+3) = 0xff;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\ttranslate32[i] = d_8to24rgbtable[sourceA[i]];\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (bc > 0 && (colorB[i] != 255))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (bc >= 16)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tunsigned int v = d_8to24rgbtable[colorB[i]];\n\t\t\t\t\t\t\t\tv = max(max((v>>0)&0xff, (v>>8)&0xff), (v>>16)&0xff);\n\t\t\t\t\t\t\t\t*((unsigned char*)&translate32[i]+0) = (((bc&0xff0000)>>16)*v)>>8;\n\t\t\t\t\t\t\t\t*((unsigned char*)&translate32[i]+1) = (((bc&0x00ff00)>> 8)*v)>>8;\n\t\t\t\t\t\t\t\t*((unsigned char*)&translate32[i]+2) = (((bc&0x0000ff)>> 0)*v)>>8;\n\t\t\t\t\t\t\t\t*((unsigned char*)&translate32[i]+3) = 0xff;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\ttranslate32[i] = d_8to24rgbtable[sourceB[i]];\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\ttranslate32[0] = 0;\n\t\t\t\t}\n\t\t\t\telse\n#endif\n\t\t\t\t{\n\t\t\t\t\tfor (i=0 ; i<256 ; i++)\n\t\t\t\t\t\ttranslate32[i] = d_8to24rgbtable[i];\n\n\t\t\t\t\tfor (i = 0; i < 16; i++)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (tc >= 16)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t//assumption: row 0 is pure white.\n\t\t\t\t\t\t\t*((unsigned char*)&translate32[TOP_RANGE+i]+0) = (((tc&0xff0000)>>16)**((unsigned char*)&d_8to24rgbtable[i]+0))>>8;\n\t\t\t\t\t\t\t*((unsigned char*)&translate32[TOP_RANGE+i]+1) = (((tc&0x00ff00)>> 8)**((unsigned char*)&d_8to24rgbtable[i]+1))>>8;\n\t\t\t\t\t\t\t*((unsigned char*)&translate32[TOP_RANGE+i]+2) = (((tc&0x0000ff)>> 0)**((unsigned char*)&d_8to24rgbtable[i]+2))>>8;\n\t\t\t\t\t\t\t*((unsigned char*)&translate32[TOP_RANGE+i]+3) = 0xff;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (tc < 8)\n\t\t\t\t\t\t\t\ttranslate32[TOP_RANGE+i] = d_8to24rgbtable[(tc<<4)+i];\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\ttranslate32[TOP_RANGE+i] = d_8to24rgbtable[(tc<<4)+15-i];\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (bc >= 16)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t*((unsigned char*)&translate32[BOTTOM_RANGE+i]+0) = (((bc&0xff0000)>>16)**((unsigned char*)&d_8to24rgbtable[i]+0))>>8;\n\t\t\t\t\t\t\t*((unsigned char*)&translate32[BOTTOM_RANGE+i]+1) = (((bc&0x00ff00)>> 8)**((unsigned char*)&d_8to24rgbtable[i]+1))>>8;\n\t\t\t\t\t\t\t*((unsigned char*)&translate32[BOTTOM_RANGE+i]+2) = (((bc&0x0000ff)>> 0)**((unsigned char*)&d_8to24rgbtable[i]+2))>>8;\n\t\t\t\t\t\t\t*((unsigned char*)&translate32[BOTTOM_RANGE+i]+3) = 0xff;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (bc < 8)\n\t\t\t\t\t\t\t\ttranslate32[BOTTOM_RANGE+i] = d_8to24rgbtable[(bc<<4)+i];\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\ttranslate32[BOTTOM_RANGE+i] = d_8to24rgbtable[(bc<<4)+15-i];\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tpixels = malloc(sizeof(*pixels) * scaled_height*scaled_width);\n\t\t\t\tout = pixels;\n\t\t\t\tfracstep = tinwidth*0x10000/scaled_width;\n\t\t\t\tfor (i=0 ; i<scaled_height ; i++, out += scaled_width)\n\t\t\t\t{\n\t\t\t\t\tinrow = original + inwidth*(i*inheight/scaled_height);\n\t\t\t\t\tfrac = fracstep >> 1;\n\t\t\t\t\tfor (j=0 ; j<scaled_width ; j+=4)\n\t\t\t\t\t{\n\t\t\t\t\t\tout[j] = translate32[inrow[frac>>16]];\n\t\t\t\t\t\tfrac += fracstep;\n\t\t\t\t\t\tout[j+1] = translate32[inrow[frac>>16]];\n\t\t\t\t\t\tfrac += fracstep;\n\t\t\t\t\t\tout[j+2] = translate32[inrow[frac>>16]];\n\t\t\t\t\t\tfrac += fracstep;\n\t\t\t\t\t\tout[j+3] = translate32[inrow[frac>>16]];\n\t\t\t\t\t\tfrac += fracstep;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tcm->texnum.base = R_LoadTexture(va(\"base$%x$%x$%i$%i$%i$%s\", tc, bc, cm->skinnum, subframe, pc, cm->name),\n\t\t\t\t\t\t\t\tscaled_width, scaled_height, h2playertranslations?TF_RGBA32:TF_RGBX32, pixels, IF_NOMIPMAP);\n\n\t\t\t\tcm->texnum.bump = shader->defaulttextures->bump;\n\t\t\t\tcm->texnum.fullbright = shader->defaulttextures->fullbright;\n\t\t\t\tcm->texnum.specular = shader->defaulttextures->specular;\n\t\t\t\tcm->texnum.reflectcube = shader->defaulttextures->reflectcube;\n\t\t\t\tcm->texnum.reflectmask = shader->defaulttextures->reflectmask;\n\t\t\t\tcm->texnum.paletted = shader->defaulttextures->paletted;\n\t\t\t\tcm->texnum.displacement = shader->defaulttextures->displacement;\n\n#ifdef HEXEN2\t//too lazy to do this\n\t\t\t\tif (h2playertranslations && pc)\n\t\t\t\t\t;\n\t\t\t\telse\n#endif\n\t\t\t\tif (r_softwarebanding)\n\t\t\t\t{\n\t\t\t\t\tqbyte *pixels8 = (void*)pixels;\n\t\t\t\t\tqbyte *out8;\n\t\t\t\t\tfor (i=0 ; i<256 ; i++)\n\t\t\t\t\t\ttranslate32[i] = i;\n\n\t\t\t\t\t//fancy colours are not supported here. try to aproximate them.\n\t\t\t\t\tif (tc >= 16)\n\t\t\t\t\t\ttc = GetPaletteIndexNoFB((tc>>16)&0xff, (tc>>8)&0xff, (tc>>0)&0xff)/16;\n\t\t\t\t\tif (bc >= 16)\n\t\t\t\t\t\tbc = GetPaletteIndexNoFB((bc>>16)&0xff, (bc>>8)&0xff, (bc>>0)&0xff)/16;\n\n\t\t\t\t\tfor (i = 0; i < 16; i++)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (tc < 8)\n\t\t\t\t\t\t\ttranslate32[TOP_RANGE+i] = (tc<<4)+i;\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\ttranslate32[TOP_RANGE+i] = (tc<<4)+15-i;\n\n\t\t\t\t\t\tif (bc < 8)\n\t\t\t\t\t\t\ttranslate32[BOTTOM_RANGE+i] = (bc<<4)+i;\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\ttranslate32[BOTTOM_RANGE+i] = (bc<<4)+15-i;\n\t\t\t\t\t}\n\n\t\t\t\t\tfracstep = tinwidth*0x10000/scaled_width;\n\t\t\t\t\tfor (i=0, out8=pixels8 ; i<scaled_height ; i++, out8 += scaled_width)\n\t\t\t\t\t{\n\t\t\t\t\t\tinrow = original + inwidth*(i*inheight/scaled_height);\n\t\t\t\t\t\tfrac = fracstep >> 1;\n\t\t\t\t\t\tfor (j=0 ; j<scaled_width ; j+=4)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tout8[j] = translate32[inrow[frac>>16]];\n\t\t\t\t\t\t\tfrac += fracstep;\n\t\t\t\t\t\t\tout8[j+1] = translate32[inrow[frac>>16]];\n\t\t\t\t\t\t\tfrac += fracstep;\n\t\t\t\t\t\t\tout8[j+2] = translate32[inrow[frac>>16]];\n\t\t\t\t\t\t\tfrac += fracstep;\n\t\t\t\t\t\t\tout8[j+3] = translate32[inrow[frac>>16]];\n\t\t\t\t\t\t\tfrac += fracstep;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\n\t\t\t\t\tcm->texnum.paletted = R_LoadTexture(va(\"paletted$%x$%x$%i$%i$%i$%s\", tc, bc, cm->skinnum, subframe, pc, cm->name),\n\t\t\t\t\t\t\t\tscaled_width, scaled_height, PTI_P8, pixels8, IF_PALETTIZE|IF_NEAREST|IF_NOMIPMAP);\n\n\t\t\t\t}\n\n\t\t\t\t/*if (!h2playertranslations)\n\t\t\t\t{\n\t\t\t\t\tqboolean valid = false;\n\t\t\t\t\t//now do the fullbrights.\n\t\t\t\t\tout = pixels;\n\t\t\t\t\tfracstep = tinwidth*0x10000/scaled_width;\n\t\t\t\t\tfor (i=0 ; i<scaled_height ; i++, out += scaled_width)\n\t\t\t\t\t{\n\t\t\t\t\t\tinrow = original + inwidth*(i*inheight/scaled_height);\n\t\t\t\t\t\tfrac = fracstep >> 1;\n\t\t\t\t\t\tfor (j=0 ; j<scaled_width ; j+=1)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (inrow[frac>>16] < 255-vid.fullbright)\n\t\t\t\t\t\t\t\t((char *) (&out[j]))[3] = 0;\t//alpha 0\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\tvalid = true;\n\t\t\t\t\t\t\tfrac += fracstep;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif (valid)\n\t\t\t\t\t\tcm->texnum.fullbright = R_LoadTexture(va(\"fb$%x$%x$%i$%i$%i$%s\", tc, bc, cm->skinnum, subframe, pc, cm->name),\n\t\t\t\t\t\t\t\t\t\tscaled_width, scaled_height, TF_RGBA32, pixels, IF_NOMIPMAP);\n\t\t\t\t}*/\n\n\t\t\t\tif (generateupperlower)\n\t\t\t\t{\n\t\t\t\t\tfor (i=0 ; i<256 ; i++)\n\t\t\t\t\t\ttranslate32[i] = 0xff000000;\n\t\t\t\t\tfor (i = 0; i < 16; i++)\n\t\t\t\t\t\ttranslate32[TOP_RANGE+i] = d_8to24rgbtable[i];\n\t\t\t\t\tout = pixels;\n\t\t\t\t\tfracstep = tinwidth*0x10000/scaled_width;\n\t\t\t\t\tfor (i=0 ; i<scaled_height ; i++, out += scaled_width)\n\t\t\t\t\t{\n\t\t\t\t\t\tinrow = original + inwidth*(i*inheight/scaled_height);\n\t\t\t\t\t\tfrac = fracstep >> 1;\n\t\t\t\t\t\tfor (j=0 ; j<scaled_width ; j+=4)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tout[j] = translate32[inrow[frac>>16]];\n\t\t\t\t\t\t\tfrac += fracstep;\n\t\t\t\t\t\t\tout[j+1] = translate32[inrow[frac>>16]];\n\t\t\t\t\t\t\tfrac += fracstep;\n\t\t\t\t\t\t\tout[j+2] = translate32[inrow[frac>>16]];\n\t\t\t\t\t\t\tfrac += fracstep;\n\t\t\t\t\t\t\tout[j+3] = translate32[inrow[frac>>16]];\n\t\t\t\t\t\t\tfrac += fracstep;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tcm->texnum.upperoverlay = R_LoadTexture(va(\"up$%i$%i$%i$%s\", cm->skinnum, subframe, pc, cm->name),\n\t\t\t\t\t\t\t\t\tscaled_width, scaled_height, TF_RGBA32, pixels, IF_NOMIPMAP);\n\n\t\t\t\t\tfor (i=0 ; i<256 ; i++)\n\t\t\t\t\t\ttranslate32[i] = 0xff000000;\n\t\t\t\t\tfor (i = 0; i < 16; i++)\n\t\t\t\t\t\ttranslate32[BOTTOM_RANGE+i] = d_8to24rgbtable[i];\n\t\t\t\t\tout = pixels;\n\t\t\t\t\tfracstep = tinwidth*0x10000/scaled_width;\n\t\t\t\t\tfor (i=0 ; i<scaled_height ; i++, out += scaled_width)\n\t\t\t\t\t{\n\t\t\t\t\t\tinrow = original + inwidth*(i*inheight/scaled_height);\n\t\t\t\t\t\tfrac = fracstep >> 1;\n\t\t\t\t\t\tfor (j=0 ; j<scaled_width ; j+=4)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tout[j] = translate32[inrow[frac>>16]];\n\t\t\t\t\t\t\tfrac += fracstep;\n\t\t\t\t\t\t\tout[j+1] = translate32[inrow[frac>>16]];\n\t\t\t\t\t\t\tfrac += fracstep;\n\t\t\t\t\t\t\tout[j+2] = translate32[inrow[frac>>16]];\n\t\t\t\t\t\t\tfrac += fracstep;\n\t\t\t\t\t\t\tout[j+3] = translate32[inrow[frac>>16]];\n\t\t\t\t\t\t\tfrac += fracstep;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tcm->texnum.loweroverlay = R_LoadTexture(va(\"lo$%i$%i$%i$%s\", cm->skinnum, subframe, pc, cm->name),\n\t\t\t\t\t\t\t\t\tscaled_width, scaled_height, TF_RGBA32, pixels, IF_NOMIPMAP);\n\n\t\t\t\t}\n\t\t\t\tfree(pixels);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t/*model has no original skin info and thus cannot be reskinned, copy over the default textures so that the skincache doesn't break things when it gets reused*/\n\t\t\t\tcm->texnum = *shader->defaulttextures;\n\t\t\t}\n\t\t\treturn shader;\n\t\t}\n\t}\n#endif\n\tif (!inf->numskins)\n\t\treturn NULL;\n\n\tskins = inf->ofsskins;\n\tif (e->skinnum >= 0 && e->skinnum < inf->numskins)\n\t\tskins += e->skinnum;\n\telse\n\t{\n\t\tstatic float timer;\n\t\tCon_ThrottlePrintf(&timer, 1, \"Skin number out of range (%u >= %u - %s)\\n\", e->skinnum, inf->numskins, model->name);\n\t\tif (!inf->numskins)\n\t\t\treturn NULL;\n\t}\n\n\tif (!skins->numframes)\n\t\treturn NULL;\n\n\tframe = cl.time*skins->skinspeed;\n\tframe = frame%skins->numframes;\n\treturn skins->frame[frame].shader;\n}\n\n#if defined(RTLIGHTS)\nstatic void R_CalcFacing(mesh_t *mesh, vec3_t lightpos)\n{\n\tfloat *v1, *v2, *v3;\n\tvec3_t d1, d2, norm;\n\n\tint i;\n\n\tindex_t *indexes = mesh->indexes;\n\tint numtris = mesh->numindexes/3;\n\n\n\tif (numFacing < numtris)\n\t{\n\t\tif (triangleFacing)\n\t\t\tBZ_Free(triangleFacing);\n\t\ttriangleFacing = BZ_Malloc(sizeof(*triangleFacing)*numtris);\n\t\tnumFacing = numtris;\n\t}\n\n\tfor (i = 0; i < numtris; i++, indexes+=3)\n\t{\n\t\tv1 = (float *)(mesh->xyz_array + indexes[0]);\n\t\tv2 = (float *)(mesh->xyz_array + indexes[1]);\n\t\tv3 = (float *)(mesh->xyz_array + indexes[2]);\n\n\t\tVectorSubtract(v1, v2, d1);\n\t\tVectorSubtract(v3, v2, d2);\n\t\tCrossProduct(d1, d2, norm);\n\n\t\ttriangleFacing[i] = (( lightpos[0] - v1[0] ) * norm[0] + ( lightpos[1] - v1[1] ) * norm[1] + ( lightpos[2] - v1[2] ) * norm[2]) > 0;\n\t}\n}\n\n#define PROJECTION_DISTANCE 30000\nstatic void R_ProjectShadowVolume(mesh_t *mesh, vec3_t lightpos)\n{\n\tint numverts = mesh->numvertexes;\n\tint i;\n\tvecV_t *input = mesh->xyz_array;\n\tvec3_t *projected;\n\tif (numProjectedShadowVerts < numverts)\n\t{\n\t\tif (ProjectedShadowVerts)\n\t\t\tBZ_Free(ProjectedShadowVerts);\n\t\tProjectedShadowVerts = BZ_Malloc(sizeof(*ProjectedShadowVerts)*numverts);\n\t\tnumProjectedShadowVerts = numverts;\n\t}\n\tprojected = ProjectedShadowVerts;\n\tfor (i = 0; i < numverts; i++)\n\t{\n\t\tprojected[i][0] = input[i][0] + (input[i][0]-lightpos[0])*PROJECTION_DISTANCE;\n\t\tprojected[i][1] = input[i][1] + (input[i][1]-lightpos[1])*PROJECTION_DISTANCE;\n\t\tprojected[i][2] = input[i][2] + (input[i][2]-lightpos[2])*PROJECTION_DISTANCE;\n\t}\n}\n\nstatic void R_DrawShadowVolume(mesh_t *mesh)\n{\n#ifdef GLQUAKE\n\tint t;\n\tvec3_t *proj = ProjectedShadowVerts;\n\tvecV_t *verts = mesh->xyz_array;\n\tindex_t *indexes = mesh->indexes;\n\tint *neighbours = mesh->trneighbors;\n\tint numtris = mesh->numindexes/3;\n\n\tqglBegin(GL_TRIANGLES);\n\tfor (t = 0; t < numtris; t++)\n\t{\n\t\tif (triangleFacing[t])\n\t\t{\n\t\t\t//draw front\n\t\t\tqglVertex3fv(verts[indexes[t*3+0]]);\n\t\t\tqglVertex3fv(verts[indexes[t*3+1]]);\n\t\t\tqglVertex3fv(verts[indexes[t*3+2]]);\n\n\t\t\t//draw back\n\t\t\tqglVertex3fv(proj[indexes[t*3+1]]);\n\t\t\tqglVertex3fv(proj[indexes[t*3+0]]);\n\t\t\tqglVertex3fv(proj[indexes[t*3+2]]);\n\n\t\t\t//draw side caps\n\t\t\tif (neighbours[t*3+0] < 0 || !triangleFacing[neighbours[t*3+0]])\n\t\t\t{\n\t\t\t\tqglVertex3fv(verts[indexes[t*3+1]]);\n\t\t\t\tqglVertex3fv(verts[indexes[t*3+0]]);\n\t\t\t\tqglVertex3fv(proj [indexes[t*3+0]]);\n\t\t\t\tqglVertex3fv(verts[indexes[t*3+1]]);\n\t\t\t\tqglVertex3fv(proj [indexes[t*3+0]]);\n\t\t\t\tqglVertex3fv(proj [indexes[t*3+1]]);\n\t\t\t}\n\n\t\t\tif (neighbours[t*3+1] < 0 || !triangleFacing[neighbours[t*3+1]])\n\t\t\t{\n\t\t\t\tqglVertex3fv(verts[indexes[t*3+2]]);\n\t\t\t\tqglVertex3fv(verts[indexes[t*3+1]]);\n\t\t\t\tqglVertex3fv(proj [indexes[t*3+1]]);\n\t\t\t\tqglVertex3fv(verts[indexes[t*3+2]]);\n\t\t\t\tqglVertex3fv(proj [indexes[t*3+1]]);\n\t\t\t\tqglVertex3fv(proj [indexes[t*3+2]]);\n\t\t\t}\n\n\t\t\tif (neighbours[t*3+2] < 0 || !triangleFacing[neighbours[t*3+2]])\n\t\t\t{\n\t\t\t\tqglVertex3fv(verts[indexes[t*3+0]]);\n\t\t\t\tqglVertex3fv(verts[indexes[t*3+2]]);\n\t\t\t\tqglVertex3fv(proj [indexes[t*3+2]]);\n\t\t\t\tqglVertex3fv(verts[indexes[t*3+0]]);\n\t\t\t\tqglVertex3fv(proj [indexes[t*3+2]]);\n\t\t\t\tqglVertex3fv(proj [indexes[t*3+0]]);\n\t\t\t}\n\t\t}\n\t}\n\tqglEnd();\n#endif\n}\n#endif\n\n//true if no shading is to be used.\nqboolean R_CalcModelLighting(entity_t *e, model_t *clmodel)\n{\n\tvec3_t lightdir;\n\tint i;\n\tvec3_t dist;\n\tfloat add, m;\n\tvec3_t shadelight, ambientlight;\n\n\tif (e->light_known)\n\t\treturn e->light_known-1;\n\n\te->light_dir[0] = 0; e->light_dir[1] = 1; e->light_dir[2] = 0;\n#ifdef HEXEN2\n\tif ((e->drawflags & MLS_MASK) == MLS_ABSLIGHT)\n\t{\t//per-entity fixed lighting\n\t\te->light_range[0] = e->light_range[1] = e->light_range[2] =\n\t\te->light_avg[0] = e->light_avg[1] = e->light_avg[2] = e->abslight/255.f;\n\t\te->light_known = 2;\n\t\treturn e->light_known-1;\n\t}\n\tif ((e->drawflags & MLS_MASK) && (e->drawflags & MLS_MASK) != MLS_ADDLIGHT)\n\t{\t//these use some qc-defined lightstyles.\n\t\ti = 24 + (e->drawflags & MLS_MASK); //saves knowing the proper patterns at least.\n\t\tVectorScale(cl_lightstyle[i].colours, d_lightstylevalue[i]/255.f, e->light_range);\n\t\tVectorScale(cl_lightstyle[i].colours, d_lightstylevalue[i]/255.f, e->light_avg);\n\t\te->light_known = 2;\n\t\treturn e->light_known-1;\n\t}\n#endif\n\tif ((clmodel->engineflags & MDLF_FLAME) ||\t//stuff on fire should generally have enough light...\n\t\tr_fullbright.ival ||\t//vanila cheat\n\t\t(e->flags & RF_FULLBRIGHT) ||\t//DP feature\n\t\t(r_fb_models.ival == 1 && ruleset_allow_fbmodels.ival && (clmodel->engineflags & MDLF_EZQUAKEFBCHEAT) && cls.protocol == CP_QUAKEWORLD && cl.deathmatch && cls.allow_fbskins>0))\t//ezquake cheat\n\t{\n\t\te->light_avg[0] = e->light_avg[1] = e->light_avg[2] = 1;\n\t\te->light_range[0] = e->light_range[1] = e->light_range[2] = 0;\n\t\te->light_known = 2;\n\t\treturn e->light_known-1;\n\t}\n\n\tif (!(r_refdef.flags & RDF_NOWORLDMODEL) && cl.worldmodel)\n\t{\n\t\tif (e->flags & RF_WEAPONMODEL)\n\t\t{\n\t\t\tcl.worldmodel->funcs.LightPointValues(cl.worldmodel, r_refdef.vieworg, shadelight, ambientlight, lightdir);\n\t\t\tfor (i = 0; i < 3; i++)\n\t\t\t{\t/*viewmodels may not be pure black*/\n\t\t\t\tif (ambientlight[i] < 24)\n\t\t\t\t\tambientlight[i] = 24;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tvec3_t center;\n\t\t\t#if 0 /*hexen2*/\n\t\t\tVectorAvg(clmodel->mins, clmodel->maxs, center);\n\t\t\tVectorAdd(e->origin, center, center);\n\t\t\t#else\n\t\t\tVectorCopy(e->origin, center);\n\t\t\tcenter[2] += 24;\n\t\t\t#endif\n\t\t\tcl.worldmodel->funcs.LightPointValues(cl.worldmodel, center, shadelight, ambientlight, lightdir);\n\t\t}\n\t}\n\telse\n\t{\n\t\tambientlight[0] = ambientlight[1] = ambientlight[2] = shadelight[0] = shadelight[1] = shadelight[2] = 128;\n\t\tlightdir[0] = 0;\n\t\tlightdir[1] = 1;\n\t\tlightdir[2] = 1;\n\t}\n\n#ifdef HEXEN2\n\tif ((e->drawflags & MLS_MASK) == MLS_ADDLIGHT)\n\t{\n\t\tambientlight[0] += e->abslight;\n\t\tambientlight[1] += e->abslight;\n\t\tambientlight[2] += e->abslight;\n\t\tshadelight[0] += e->abslight;\n\t\tshadelight[1] += e->abslight;\n\t\tshadelight[2] += e->abslight;\n\t}\n#endif\n\n\tif (r_softwarebanding)\n\t{\n\t\t//mimic software rendering as closely as possible\n\t\tlightdir[2] = 0;\t//horizontal light only.\n\n//\t\tVectorMA(vec3_origin, 0.5, shadelight, ambientlight);\n//\t\tVectorCopy(ambientlight, shadelight);\n\n\t\tif (!r_vertexdlights.ival && r_dlightlightmaps)\n\t\t{\n\t\t\tfloat *org = e->origin;\n\t\t\tif (e->flags & RF_WEAPONMODEL)\n\t\t\t\torg = r_refdef.vieworg;\n\t\t\t//don't do world lights, although that might be funny\n\t\t\tfor (i=rtlights_first; i<RTL_FIRST; i++)\n\t\t\t{\n\t\t\t\tif (!(*cl_dlights[i].cubemapname) && cl_dlights[i].radius)\n\t\t\t\t{\n\t\t\t\t\tVectorSubtract (org,\n\t\t\t\t\t\t\t\t\tcl_dlights[i].origin,\n\t\t\t\t\t\t\t\t\tdist);\n\t\t\t\t\tadd = cl_dlights[i].radius - Length(dist);\n#ifdef RTLIGHTS\n\t\t\t\t\tif (r_shadow_realtime_world.ival)\t//if world lighting is on, there may be no lightmap influence even if r_dynamic is on.\n\t\t\t\t\t\tadd *= r_shadow_realtime_world_lightmaps.value;\n#endif\n\t\t\t\t\tif (add > 0)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (r_dynamic.ival == 2)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tambientlight[0] += add * 2;\n\t\t\t\t\t\t\tambientlight[1] += add * 2;\n\t\t\t\t\t\t\tambientlight[2] += add * 2;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tambientlight[0] += add * cl_dlights[i].color[0];\n\t\t\t\t\t\t\tambientlight[1] += add * cl_dlights[i].color[1];\n\t\t\t\t\t\t\tambientlight[2] += add * cl_dlights[i].color[2];\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tfor (i = 0; i < 3; i++)\n\t\t{\n\t\t\tif (ambientlight[i] > 128)\n\t\t\t\tambientlight[i] = 128;\n\t\t\tif (ambientlight[i] + shadelight[i] > 192)\n\t\t\t\tshadelight[i] = 192 - ambientlight[i];\n\t\t}\n\t}\n\telse\n\t{\n\t\tif (!r_vertexdlights.ival && r_dlightlightmaps)\n\t\t{\n\t\t\tfloat *org = e->origin;\n\t\t\tif (e->flags & RF_WEAPONMODEL)\n\t\t\t\torg = r_refdef.vieworg;\n\n\t\t\t//don't do world lights, although that might be funny\n\t\t\tfor (i=rtlights_first; i<RTL_FIRST; i++)\n\t\t\t{\n\t\t\t\tif (!(*cl_dlights[i].cubemapname) && cl_dlights[i].radius)\n\t\t\t\t{\n\t\t\t\t\tVectorSubtract (org,\n\t\t\t\t\t\t\t\t\tcl_dlights[i].origin,\n\t\t\t\t\t\t\t\t\tdist);\n\t\t\t\t\tadd = cl_dlights[i].radius - Length(dist);\n#ifdef RTLIGHTS\n\t\t\t\t\tif (r_shadow_realtime_world.ival)\t//if world lighting is on, there may be no lightmap influence even if r_dynamic is on.\n\t\t\t\t\t\tadd *= r_shadow_realtime_world_lightmaps.value;\n#endif\n\n\t\t\t\t\tif (add > 0)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (r_dynamic.ival == 2)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tambientlight[0] += add * 2;\n\t\t\t\t\t\t\tambientlight[1] += add * 2;\n\t\t\t\t\t\t\tambientlight[2] += add * 2;\n\t\t\t\t\t\t\t//ZOID models should be affected by dlights as well\n\t\t\t\t\t\t\tshadelight[0] += add * 2;\n\t\t\t\t\t\t\tshadelight[1] += add * 2;\n\t\t\t\t\t\t\tshadelight[2] += add * 2;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tambientlight[0] += add * cl_dlights[i].color[0];\n\t\t\t\t\t\t\tambientlight[1] += add * cl_dlights[i].color[1];\n\t\t\t\t\t\t\tambientlight[2] += add * cl_dlights[i].color[2];\n\t\t\t\t\t\t\t//ZOID models should be affected by dlights as well\n\t\t\t\t\t\t\tshadelight[0] += add * cl_dlights[i].color[0];\n\t\t\t\t\t\t\tshadelight[1] += add * cl_dlights[i].color[1];\n\t\t\t\t\t\t\tshadelight[2] += add * cl_dlights[i].color[2];\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tswitch(PTI_E5BGR9)//lightmap_fmt)\n\t\t{\n\t\t//don't clamp model lighting if we're not clamping world lighting either.\n\t\tcase PTI_E5BGR9:\n\t\tcase PTI_RGBA16F:\n\t\tcase PTI_RGBA32F:\n\t\t\tbreak;\n\t\tdefault:\t//non-hdr lightmap format. clamp model lighting to match the lightmap's clamps.\n\t\t\tm = max(max(ambientlight[0], ambientlight[1]), ambientlight[2]);\n\t\t\tif (m > 255)\n\t\t\t{\n\t\t\t\tambientlight[0] *= 255.0/m;\n\t\t\t\tambientlight[1] *= 255.0/m;\n\t\t\t\tambientlight[2] *= 255.0/m;\n\t\t\t}\n\t\t\tm = max(max(shadelight[0], shadelight[1]), shadelight[2]);\n\t\t\tif (m > 128)\n\t\t\t{\n\t\t\t\tshadelight[0] *= 128.0/m;\n\t\t\t\tshadelight[1] *= 128.0/m;\n\t\t\t\tshadelight[2] *= 128.0/m;\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\n\t\t//MORE HUGE HACKS! WHEN WILL THEY CEASE!\n\t\t// clamp lighting so it doesn't overbright as much\n\t\t// ZOID: never allow players to go totally black\n\t\tif (e->playerindex >= 0 && !(e->flags & (RF_WEAPONMODEL|RF_WEAPONMODELNOBOB|RF_DEPTHHACK)))\n\t\t{\n\t\t\tfloat fb = r_fullbrightSkins.value;\n\t\t\tif (fb > cls.allow_fbskins)\n\t\t\t\tfb = cls.allow_fbskins;\n\t\t\tif (fb < 0)\n\t\t\t\tfb = 0;\n\t\t\tif (fb)\n\t\t\t{\n\t\t\t\textern cvar_t r_fb_models;\n\n\t\t\t\tif (fb >= 1 && r_fb_models.value)\n\t\t\t\t{\n\t\t\t\t\tambientlight[0] = ambientlight[1] = ambientlight[2] = 1;\n\t\t\t\t\tshadelight[0] = shadelight[1] = shadelight[2] = 1;\n\n\t\t\t\t\tVectorSet(e->light_dir, 1, 0, 0);\n\t\t\t\t\tVectorClear(e->light_range);\n\t\t\t\t\tVectorScale(shadelight, fb, e->light_avg);\n\n\t\t\t\t\te->light_known = 2;\n\t\t\t\t\treturn e->light_known-1;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tfor (i = 0; i < 3; i++)\n\t\t\t\t\t{\n\t\t\t\t\t\tambientlight[i] = max(ambientlight[i], 8 + fb * 120);\n\t\t\t\t\t\tshadelight[i] = max(shadelight[i], 8 + fb * 120);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tfor (i = 0; i < 3; i++)\n\t\t\t{\n\t\t\t\tif (ambientlight[i] < 8)\n\t\t\t\t\tambientlight[i] = 8;\n\t\t\t}\n\t\t}\n\n\n#if 1\t//match quakespasm here.\n\t\tif (gl_overbright_models.value > 0.f && ruleset_allow_fbmodels.ival)\n\t\t{\n\t\t\tm = (96*6) / (shadelight[0]+shadelight[1]+shadelight[2]+ambientlight[0]+ambientlight[1]+ambientlight[2]);\n\t\t\tif (m > 1.0)\n\t\t\t\tm = 1;\t//we only want to let it darken here.\n\t\t\tm *= 1 + min(1,gl_overbright_models.value);\n\t\t}\n\t\telse\n\t\t\tm = 1;\n\t\tm /= 200.0/255;\t//a legacy quake fudge-factor.\n\t\tVectorScale(shadelight, m, shadelight);\n\t\tVectorScale(ambientlight, m, ambientlight);\n#else\n\t\tfor (i = 0; i < 3; i++)\n\t\t{\n\t\t\tif (ambientlight[i] > 128)\n\t\t\t\tambientlight[i] = 128;\n\n\t\t\tshadelight[i] /= 200.0/255;\n\t\t\tambientlight[i] /= 200.0/255;\n\t\t}\n#endif\n\n\t\tif ((e->model->flags & MF_ROTATE) && cl.hexen2pickups)\n\t\t{\n\t\t\tshadelight[0] = shadelight[1] = shadelight[2] =\n\t\t\tambientlight[0] = ambientlight[1] = ambientlight[2] = 128+sin(cl.servertime*4)*64;\n\t\t}\n\t}\n\n\tif (e->flags & RF_WEAPONMODEL)\n\t{\n\t\tvec3_t temp;\n\t\ttemp[0] = DotProduct(lightdir, vpn);\n\t\ttemp[1] = -DotProduct(lightdir, vright);\n\t\ttemp[2] = DotProduct(lightdir, vup);\n\n\t\te->light_dir[0] = DotProduct(temp, e->axis[0]);\n\t\te->light_dir[1] = DotProduct(temp, e->axis[1]);\n\t\te->light_dir[2] = DotProduct(temp, e->axis[2]);\n\t}\n\telse\n\t{\n\t\te->light_dir[0] = DotProduct(lightdir, e->axis[0]);\n\t\te->light_dir[1] = DotProduct(lightdir, e->axis[1]);\n\t\te->light_dir[2] = DotProduct(lightdir, e->axis[2]);\n\t}\n\tVectorNormalize(e->light_dir);\n\n\tshadelight[0] *= 1/255.0f;\n\tshadelight[1] *= 1/255.0f;\n\tshadelight[2] *= 1/255.0f;\n\tambientlight[0] *= 1/255.0f;\n\tambientlight[1] *= 1/255.0f;\n\tambientlight[2] *= 1/255.0f;\n\n\tif (e->flags & Q2RF_GLOW)\n\t{\n\t\tfloat scale = 1 + 0.2 * sin(cl.time*7);\n\t\tVectorScale(ambientlight, scale, ambientlight);\n\t\tVectorScale(shadelight, scale, shadelight);\n\t}\n\n\tif (r_softwarebanding)\n\t{\t//overbright the models.\n\t\tVectorScale(ambientlight, 2, e->light_avg);\n\t\tVectorScale(shadelight, 2, e->light_range);\n\t}\n\telse if (1)\n\t{\t//calculate average and range, to allow for negative lighting dotproducts\n\t\tVectorCopy(shadelight, e->light_avg);\n\t\tVectorCopy(ambientlight, e->light_range);\n\t}\n\telse\n\t{\t//calculate average and range, to allow for negative lighting dotproducts\n\t\tVectorMA(ambientlight, 0.5, shadelight, e->light_avg);\n\t\tVectorSubtract(shadelight, ambientlight, e->light_range);\n\t}\n\n\te->light_known = 1;\n\treturn e->light_known-1;\n}\n\nvoid R_GAlias_DrawBatch(batch_t *batch)\n{\n\tentity_t *e;\n\n\tgaliasinfo_t *inf;\n\tmodel_t *clmodel;\n\tunsigned int surfnum;\n\n\tstatic mesh_t mesh;\n\tstatic mesh_t *meshl = &mesh;\n\n//\tqboolean needrecolour;\n//\tqboolean nolightdir;\n\n\te = batch->ent;\n\tclmodel = e->model;\n\n\tcurrententity = e;\n\t/*nolightdir =*/ R_CalcModelLighting(e, clmodel);\n\n\tinf = Mod_Extradata (clmodel);\n\tif (inf)\n\t{\n\t\tmemset(&mesh, 0, sizeof(mesh));\n\t\tfor(surfnum=0; inf; inf=inf->nextsurf, surfnum++)\n\t\t{\n\t\t\tif (batch->user.alias.surfrefs[0] == surfnum)\n\t\t\t{\n\t\t\t\t/*needrecolour =*/ Alias_GAliasBuildMesh(&mesh, &batch->vbo, inf, surfnum, e, batch->shader->prog && (batch->shader->prog->supportedpermutations & PERMUTATION_SKELETAL));\n\t\t\t\tbatch->mesh = &meshl;\n\t\t\t\tif (!mesh.numindexes)\n\t\t\t\t{\n\t\t\t\t\tbatch->meshes = 0;\t//something went screwy\n\t\t\t\t\tbatch->mesh = NULL;\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n\tbatch->meshes = 0;\n\tCon_Printf(\"Broken model surfaces mid-frame\\n\");\n\treturn;\n}\n\nvoid R_GAlias_GenerateBatches(entity_t *e, batch_t **batches)\n{\n\tgaliasinfo_t *inf;\n\tmodel_t *clmodel;\n\tshader_t *shader, *regshader;\n\tbatch_t *b;\n\tint surfnum, j;\n\tshadersort_t sort;\n\tfloat lod;\n\n\ttexnums_t *skin;\n\n\tif ((r_refdef.externalview || r_refdef.recurse) && (e->flags & RF_WEAPONMODEL))\n\t\treturn;\n\n\tclmodel = e->model;\n#ifdef QWSKINS\n\t/*switch model if we're the player model, and the player skin says a new model*/\n\t{\n\t\textern int cl_playerindex;\n\t\tif (e->playerindex >= 0 && e->model == cl.model_precache[cl_playerindex])\n\t\t{\n\t\t\tclmodel = cl.players[e->playerindex].model;\n\t\t\tif (!clmodel || clmodel->type != mod_alias)\n\t\t\t\tclmodel = e->model;\t//oops, never mind\n\t\t}\n\t}\n#endif\n\n\tif (!(e->flags & RF_WEAPONMODEL)\n#ifdef SKELETALMODELS\n\t\t&& !e->framestate.bonestate\n#endif\n\t\t)\n\t{\n\t\tif (R_CullEntityBox (e, clmodel->mins, clmodel->maxs))\n\t\t\treturn;\n#ifdef RTLIGHTS\n\t\tif (BE_LightCullModel(e->origin, clmodel))\n\t\t\treturn;\n\t}\n\telse\n\t{\n\t\tif (BE_LightCullModel(r_origin, clmodel))\n\t\t\treturn;\n#endif\n\t}\n\n\tif (clmodel->tainted)\n\t{\n\t\tif (!ruleset_allow_modified_eyes.ival && !strcmp(clmodel->name, \"progs/eyes.mdl\"))\n\t\t\treturn;\n\t}\n\n\tinf = Mod_Extradata (clmodel);\n\n\tif (clmodel->maxlod)\n\t{\n\t\tvec3_t v;\n\t\tfloat z;\n\n\t\tVectorSubtract(e->origin, r_refdef.vieworg, v);\n\t\tz = DotProduct(v, vpn);\n\t\tif (z < -clmodel->radius)\n\t\t\treturn;\t\t//furthest extent of bounding sphere is nearer than the near clip plane, and thus completely invisible\n\t\telse if (z < 0)\n\t\t\tlod = 0;\t//nearer than the camera, use the highest lod\n\t\telse\n\t\t{\n\t\t\t//if the ent is in the middle of the screen, then the right edge of its sphere is at what percentage of the width of the screen...?\n\n\t\t\t//simplified Matrix4x4_CM_Transform4\n\t\t\tfloat coverage = (r_refdef.m_projection_std[5]*clmodel->radius + r_refdef.m_projection_std[ 9]*-z + r_refdef.m_projection_std[13]) /\n\t\t\t\t\t\t\t (r_refdef.m_projection_std[7]*clmodel->radius + r_refdef.m_projection_std[11]*-z + r_refdef.m_projection_std[15]);\n\t\t\tlod = 1-(coverage*r_lodscale.value);\n\t\t\tlod = bound(0, lod, 1);\t//so lodbias is a little more reliable.\n\t\t\tlod *= clmodel->maxlod;\n\t\t\tlod += r_lodbias.value;\n\t\t\tlod = max(0, lod);\t//never nearer than 0, the min value check wouldn't cope.\n\t\t}\n\t}\n\telse\n\t\tlod = 0;\n\n\tfor(surfnum=0; inf; inf=inf->nextsurf, surfnum++)\n\t{\n\t\tif (!inf->numindexes)\n\t\t\tcontinue;\n\t\tif (lod < inf->mindist || (inf->maxdist && lod >= inf->maxdist))\n\t\t\tcontinue;\n\n\t\tregshader = GL_ChooseSkin(inf, clmodel, surfnum, e, &skin);\n\t\tif (!regshader)\n\t\t\tcontinue;\n\t\tskin = skin?skin:NULL;\n\t\tshader = e->forcedshader?e->forcedshader:regshader;\n\t\tif (shader && !(shader->flags & SHADER_NODRAW))\n\t\t{\n\t\t\tb = BE_GetTempBatch();\n\t\t\tif (!b)\n\t\t\t\tbreak;\n\n\t\t\tb->buildmeshes = R_GAlias_DrawBatch;\n\t\t\tb->ent = e;\n\t\t\tb->envmap = Mod_CubemapForOrigin(cl.worldmodel, e->origin);\n#if defined(Q3BSPS) || defined(RFBSPS)\n\t\t\tb->fog = Mod_FogForOrigin(cl.worldmodel, e->origin);\n#endif\n\t\t\tb->mesh = NULL;\n\t\t\tb->firstmesh = 0;\n\t\t\tb->meshes = 1;\n\t\t\tb->skin = skin;\n\t\t\tb->texture = NULL;\n\t\t\tb->shader = shader;\n\t\t\tfor (j = 0; j < MAXRLIGHTMAPS; j++)\n\t\t\t{\n\t\t\t\tb->lightmap[j] = -1;\n\t\t\t\tb->lmlightstyle[j] = INVALID_LIGHTSTYLE;\n\t\t\t}\n\t\t\tb->user.alias.surfrefs[0] = surfnum;\n\t\t\tb->flags = 0;\n\t\t\tsort = shader->sort;\n\t\t\tif (e->flags & RF_FORCECOLOURMOD)\n\t\t\t\tb->flags |= BEF_FORCECOLOURMOD;\n\t\t\tif (e->flags & RF_ADDITIVE)\n\t\t\t{\n\t\t\t\tb->flags |= BEF_FORCEADDITIVE;\n\t\t\t\tif (sort < SHADER_SORT_ADDITIVE)\n\t\t\t\t\tsort = SHADER_SORT_ADDITIVE;\n\t\t\t}\n\t\t\tif (e->flags & RF_TRANSLUCENT)\n\t\t\t{\n\t\t\t\tb->flags |= BEF_FORCETRANSPARENT;\n\t\t\t\tif (SHADER_SORT_PORTAL < sort && sort < SHADER_SORT_BLEND)\n\t\t\t\t\tsort = SHADER_SORT_BLEND;\n\t\t\t}\n#ifdef HEXEN2\n\t\t\telse if (e->drawflags & DRF_TRANSLUCENT)\n\t\t\t{\n\t\t\t\tb->flags |= BEF_FORCETRANSPARENT;\n\t\t\t\tif (SHADER_SORT_PORTAL < sort && sort < SHADER_SORT_BLEND)\n\t\t\t\t\tsort = SHADER_SORT_BLEND;\n\t\t\t\te->shaderRGBAf[3] = r_wateralpha.value;\n\t\t\t}\n#endif\n\t\t\tif (e->flags & RF_NODEPTHTEST)\n\t\t\t{\n\t\t\t\tb->flags |= BEF_FORCENODEPTH;\n\t\t\t\tif (sort < SHADER_SORT_NEAREST)\n\t\t\t\t\tsort = SHADER_SORT_NEAREST;\n\t\t\t}\n\t\t\tif ((e->flags & RF_NOSHADOW) || (clmodel->engineflags & MDLF_NOSHADOWS))\n\t\t\t\tb->flags |= BEF_NOSHADOWS;\n\t\t\tb->vbo = NULL;\n\t\t\tb->next = batches[sort];\n\t\t\tbatches[sort] = b;\n\t\t}\n\t}\n}\n\n#if 0\nvoid R_Sprite_GenerateBatches(entity_t *e, batch_t **batches)\n{\n\tgaliasinfo_t *inf;\n\tmodel_t *clmodel;\n\tshader_t *shader;\n\tbatch_t *b;\n\tint surfnum;\n\n\ttexnums_t *skin;\n\n\tif (r_refdef.externalview && e->flags & Q2RF_WEAPONMODEL)\n\t\treturn;\n\n\tclmodel = e->model;\n\n\tif (!(e->flags & Q2RF_WEAPONMODEL))\n\t{\n\t\tif (R_CullEntityBox (e, clmodel->mins, clmodel->maxs))\n\t\t\treturn;\n#ifdef RTLIGHTS\n\t\tif (BE_LightCullModel(e->origin, clmodel))\n\t\t\treturn;\n\t}\n\telse\n\t{\n\t\tif (BE_LightCullModel(r_origin, clmodel))\n\t\t\treturn;\n#endif\n\t}\n\n\tif (clmodel->tainted)\n\t{\n\t\tif (!ruleset_allow_modified_eyes.ival && !strcmp(clmodel->name, \"progs/eyes.mdl\"))\n\t\t\treturn;\n\t}\n\n\tinf = RMod_Extradata (clmodel);\n\n\tif (!e->model || e->forcedshader)\n\t{\n\t\t//fixme\n\t\treturn;\n\t}\n\telse\n\t{\n\t\tframe = R_GetSpriteFrame (e);\n\t\tpsprite = e->model->cache.data;\n\t\tsprtype = psprite->type;\n\t\tshader = frame->shader;\n\t}\n\n\tif (shader)\n\t{\n\t\tb = BE_GetTempBatch();\n\t\tif (!b)\n\t\t\tbreak;\n\n\t\tb->buildmeshes = R_Sprite_DrawBatch;\n\t\tb->ent = e;\n\t\tb->mesh = NULL;\n\t\tb->firstmesh = 0;\n\t\tb->meshes = 1;\n\t\tb->skin = frame-;\n\t\tb->texture = NULL;\n\t\tb->shader = frame->shader;\n\t\tfor (j = 0; j < MAXRLIGHTMAPS; j++)\n\t\t\tb->lightmap[j] = -1;\n\t\tb->surf_first = surfnum;\n\t\tb->flags = 0;\n\t\tb->vbo = NULL;\n\t\tb->next = batches[shader->sort];\n\t\tbatches[shader->sort] = b;\n\t}\n}\n#endif\n\n//returns the rotated offset of the two points in result\nvoid RotateLightVector(const vec3_t *axis, const vec3_t origin, const vec3_t lightpoint, vec3_t result)\n{\n\tvec3_t offs;\n\n\toffs[0] = lightpoint[0] - origin[0];\n\toffs[1] = lightpoint[1] - origin[1];\n\toffs[2] = lightpoint[2] - origin[2];\n\n\tresult[0] = DotProduct (offs, axis[0]);\n\tresult[1] = DotProduct (offs, axis[1]);\n\tresult[2] = DotProduct (offs, axis[2]);\n}\n\n#if defined(RTLIGHTS)\n/*\nstatic void GL_LightMesh (mesh_t *mesh, vec3_t lightpos, vec3_t colours, float radius)\n{\n\tvec3_t dir;\n\tint i;\n\tfloat dot, d, f, a;\n\n\tvecV_t *xyz = mesh->xyz_array;\n\tvec3_t *normals = mesh->normals_array;\n\tvec4_t *out = mesh->colors4f_array;\n\n\tif (!out)\n\t\treturn;\t//urm..\n\n\tif (normals)\n\t{\n\t\tfor (i = 0; i < mesh->numvertexes; i++)\n\t\t{\n\t\t\tVectorSubtract(lightpos, xyz[i], dir);\n\t\t\tdot = DotProduct(dir, normals[i]);\n\t\t\tif (dot > 0)\n\t\t\t{\n\t\t\t\td = DotProduct(dir, dir)/radius;\n\t\t\t\ta = 1/d;\n\t\t\t\tif (a>0)\n\t\t\t\t{\n\t\t\t\t\ta *= dot/sqrt(d);\n\t\t\t\t\tf = a*colours[0];\n\t\t\t\t\tout[i][0] = f;\n\n\t\t\t\t\tf = a*colours[1];\n\t\t\t\t\tout[i][1] = f;\n\n\t\t\t\t\tf = a*colours[2];\n\t\t\t\t\tout[i][2] = f;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tout[i][0] = 0;\n\t\t\t\t\tout[i][1] = 0;\n\t\t\t\t\tout[i][2] = 0;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tout[i][0] = 0;\n\t\t\t\tout[i][1] = 0;\n\t\t\t\tout[i][2] = 0;\n\t\t\t}\n\t\t\tout[i][3] = 1;\n\t\t}\n\t}\n\telse\n\t{\n\t\tfor (i = 0; i < mesh->numvertexes; i++)\n\t\t{\n\t\t\tVectorSubtract(lightpos, xyz[i], dir);\n\t\t\tout[i][0] = colours[0];\n\t\t\tout[i][1] = colours[1];\n\t\t\tout[i][2] = colours[2];\n\t\t\tout[i][3] = 1;\n\t\t}\n\t}\n}\n*/\n\n//courtesy of DP\nstatic void R_BuildBumpVectors(const float *v0, const float *v1, const float *v2, const float *tc0, const float *tc1, const float *tc2, float *fte_restrict svector3f, float *fte_restrict tvector3f, float *fte_restrict normal3f)\n{\n\tfloat f, tangentcross[3], v10[3], v20[3], tc10[2], tc20[2];\n\t// 79 add/sub/negate/multiply (1 cycle), 1 compare (3 cycle?), total cycles not counting load/store/exchange roughly 82 cycles\n\t// 6 add, 28 subtract, 39 multiply, 1 compare, 50% chance of 6 negates\n\n\t// 6 multiply, 9 subtract\n\tVectorSubtract(v1, v0, v10);\n\tVectorSubtract(v2, v0, v20);\n\tnormal3f[0] = v10[1] * v20[2] - v10[2] * v20[1];\n\tnormal3f[1] = v10[2] * v20[0] - v10[0] * v20[2];\n\tnormal3f[2] = v10[0] * v20[1] - v10[1] * v20[0];\n\t// 12 multiply, 10 subtract\n\ttc10[1] = tc1[1] - tc0[1];\n\ttc20[1] = tc2[1] - tc0[1];\n\tsvector3f[0] = tc10[1] * v20[0] - tc20[1] * v10[0];\n\tsvector3f[1] = tc10[1] * v20[1] - tc20[1] * v10[1];\n\tsvector3f[2] = tc10[1] * v20[2] - tc20[1] * v10[2];\n\ttc10[0] = tc1[0] - tc0[0];\n\ttc20[0] = tc2[0] - tc0[0];\n\ttvector3f[0] = tc10[0] * v20[0] - tc20[0] * v10[0];\n\ttvector3f[1] = tc10[0] * v20[1] - tc20[0] * v10[1];\n\ttvector3f[2] = tc10[0] * v20[2] - tc20[0] * v10[2];\n\t// 12 multiply, 4 add, 6 subtract\n\tf = DotProduct(svector3f, normal3f);\n\tsvector3f[0] -= f * normal3f[0];\n\tsvector3f[1] -= f * normal3f[1];\n\tsvector3f[2] -= f * normal3f[2];\n\tf = DotProduct(tvector3f, normal3f);\n\ttvector3f[0] -= f * normal3f[0];\n\ttvector3f[1] -= f * normal3f[1];\n\ttvector3f[2] -= f * normal3f[2];\n\t// if texture is mapped the wrong way (counterclockwise), the tangents\n\t// have to be flipped, this is detected by calculating a normal from the\n\t// two tangents, and seeing if it is opposite the surface normal\n\t// 9 multiply, 2 add, 3 subtract, 1 compare, 50% chance of: 6 negates\n\tCrossProduct(tvector3f, svector3f, tangentcross);\n\tif (DotProduct(tangentcross, normal3f) < 0)\n\t{\n\t\tVectorNegate(svector3f, svector3f);\n\t\tVectorNegate(tvector3f, tvector3f);\n\t}\n}\n\n#if 0\n//courtesy of DP\nvoid R_AliasGenerateTextureVectors(mesh_t *mesh, float *fte_restrict normal3f, float *fte_restrict svector3f, float *fte_restrict tvector3f)\n{\n\tint i;\n\tfloat sdir[3], tdir[3], normal[3], *v;\n\tindex_t *e;\n\tfloat *vertex3f = (float*)mesh->xyz_array;\n\tfloat *texcoord2f = (float*)mesh->st_array;\n\t// clear the vectors\n//\tif (svector3f)\n\t\tmemset(svector3f, 0, mesh->numvertexes * sizeof(float[3]));\n//\tif (tvector3f)\n\t\tmemset(tvector3f, 0, mesh->numvertexes * sizeof(float[3]));\n//\tif (normal3f)\n\t\tmemset(normal3f, 0, mesh->numvertexes * sizeof(float[3]));\n\t// process each vertex of each triangle and accumulate the results\n\tfor (e = mesh->indexes; e < mesh->indexes+mesh->numindexes; e += 3)\n\t{\n\t\tR_BuildBumpVectors(vertex3f + e[0] * 3, vertex3f + e[1] * 3, vertex3f + e[2] * 3, texcoord2f + e[0] * 2, texcoord2f + e[1] * 2, texcoord2f + e[2] * 2, sdir, tdir, normal);\n//\t\tif (!areaweighting)\n//\t\t{\n//\t\t\tVectorNormalize(sdir);\n//\t\t\tVectorNormalize(tdir);\n//\t\t\tVectorNormalize(normal);\n//\t\t}\n//\t\tif (svector3f)\n\t\t\tfor (i = 0;i < 3;i++)\n\t\t\t\tVectorAdd(svector3f + e[i]*3, sdir, svector3f + e[i]*3);\n//\t\tif (tvector3f)\n\t\t\tfor (i = 0;i < 3;i++)\n\t\t\t\tVectorAdd(tvector3f + e[i]*3, tdir, tvector3f + e[i]*3);\n//\t\tif (normal3f)\n\t\t\tfor (i = 0;i < 3;i++)\n\t\t\t\tVectorAdd(normal3f + e[i]*3, normal, normal3f + e[i]*3);\n\t}\n\t// now we could divide the vectors by the number of averaged values on\n\t// each vertex...  but instead normalize them\n\t// 4 assignments, 1 divide, 1 sqrt, 2 adds, 6 multiplies\n\tif (svector3f)\n\t\tfor (i = 0, v = svector3f;i < mesh->numvertexes;i++, v += 3)\n\t\t\tVectorNormalize(v);\n\t// 4 assignments, 1 divide, 1 sqrt, 2 adds, 6 multiplies\n\tif (tvector3f)\n\t\tfor (i = 0, v = tvector3f;i < mesh->numvertexes;i++, v += 3)\n\t\t\tVectorNormalize(v);\n\t// 4 assignments, 1 divide, 1 sqrt, 2 adds, 6 multiplies\n\tif (normal3f)\n\t\tfor (i = 0, v = normal3f;i < mesh->numvertexes;i++, v += 3)\n\t\t\tVectorNormalize(v);\n}\n#endif\n\n//calculate S+T vectors without also breaking the normals\nvoid R_Generate_Mesh_ST_Vectors(mesh_t *mesh)\n{\n\tint i;\n\tvec3_t sdir, tdir, normal, *s, *t, *n;\n\tindex_t *e;\n\tvecV_t *vertex3f = mesh->xyz_array;\n\tvec2_t *texcoord2f = mesh->st_array;\n\tvec3_t *normal3f = mesh->normals_array;\n\tvec3_t *fte_restrict svector3f = mesh->snormals_array;\n\tvec3_t *fte_restrict tvector3f = mesh->tnormals_array;\n\tfloat frac;\n\t// clear the vectors\n\tmemset(svector3f, 0, mesh->numvertexes * sizeof(float[3]));\n\tmemset(tvector3f, 0, mesh->numvertexes * sizeof(float[3]));\n\t// process each vertex of each triangle and accumulate the results\n\tfor (e = mesh->indexes; e < mesh->indexes+mesh->numindexes; e += 3)\n\t{\n\t\tR_BuildBumpVectors(vertex3f[e[0]], vertex3f[e[1]], vertex3f[e[2]], texcoord2f[e[0]], texcoord2f[e[1]], texcoord2f[e[2]], sdir, tdir, normal);\n//\t\tif (!areaweighting)\n//\t\t{\n//\t\t\tVectorNormalize(sdir);\n//\t\t\tVectorNormalize(tdir);\n//\t\t}\n\t\tfor (i = 0;i < 3;i++)\n\t\t\tVectorAdd(svector3f[e[i]], sdir, svector3f[e[i]]);\n\t\tfor (i = 0;i < 3;i++)\n\t\t\tVectorAdd(tvector3f[e[i]], tdir, tvector3f[e[i]]);\n\t}\n\tfor (i = 0, s = svector3f, t = tvector3f, n = normal3f;i < mesh->numvertexes;i++, s++, t++, n++)\n\t{\n\t\tfrac = -DotProduct(*s, *n);\n\t\tVectorMA(*s, frac, *n, *s);\n\t\tVectorNormalize(*s);\n\n\t\tfrac = -DotProduct(*t, *n);\n\t\tVectorMA(*t, frac, *n, *t);\n\t\tVectorNormalize(*t);\n\t}\n}\n\n//FIXME: Be less agressive.\n//This function will have to be called twice (for geforce cards), with the same data, so do the building once and rendering twice.\nvoid R_DrawGAliasShadowVolume(entity_t *e, vec3_t lightpos, float radius)\n{\n\tmodel_t *clmodel = e->model;\n\tgaliasinfo_t *inf;\n\tmesh_t mesh;\n\tvec3_t lightorg;\n\tint surfnum = 0;\n\n\tif (qrenderer != QR_OPENGL)\n\t\treturn;\n\n\tif (clmodel->engineflags & MDLF_NOSHADOWS)\n\t\treturn;\n\tif (r_noaliasshadows.ival)\n\t\treturn;\n\n//\tif (e->shaderRGBAf[3] < 0.5)\n//\t\treturn;\n\n\tRotateLightVector((void *)e->axis, e->origin, lightpos, lightorg);\n\n\tif (Length(lightorg) > radius + clmodel->radius)\n\t\treturn;\n\n\tBE_SelectEntity(e);\n\n\tinf = Mod_Extradata (clmodel);\n\twhile(inf)\n\t{\n\t\tif (inf->ofs_trineighbours)\n\t\t{\n\t\t\tAlias_GAliasBuildMesh(&mesh, NULL, inf, surfnum, e, false);\n\t\t\tR_CalcFacing(&mesh, lightorg);\n\t\t\tR_ProjectShadowVolume(&mesh, lightorg);\n\t\t\tR_DrawShadowVolume(&mesh);\n\t\t}\n\n\t\tinf = inf->nextsurf;\n\n\t\tsurfnum++;\n\t}\n}\n#endif\n\n\n\n\n\n#if 0\nstatic int R_FindTriangleWithEdge ( index_t *indexes, int numtris, index_t start, index_t end, int ignore)\n{\n\tint i;\n\tint match, count;\n\n\tcount = 0;\n\tmatch = -1;\n\n\tfor (i = 0; i < numtris; i++, indexes += 3)\n\t{\n\t\tif ( (indexes[0] == start && indexes[1] == end)\n\t\t\t|| (indexes[1] == start && indexes[2] == end)\n\t\t\t|| (indexes[2] == start && indexes[0] == end) ) {\n\t\t\tif (i != ignore)\n\t\t\t\tmatch = i;\n\t\t\tcount++;\n\t\t} else if ( (indexes[1] == start && indexes[0] == end)\n\t\t\t|| (indexes[2] == start && indexes[1] == end)\n\t\t\t|| (indexes[0] == start && indexes[2] == end) ) {\n\t\t\tcount++;\n\t\t}\n\t}\n\n\t// detect edges shared by three triangles and make them seams\n\tif (count > 2)\n\t\tmatch = -1;\n\n\treturn match;\n}\n#endif\n\n#if 0\nstatic void R_BuildTriangleNeighbours ( int *neighbours, index_t *indexes, int numtris )\n{\n\tint i, *n;\n\tindex_t *index;\n\n\tfor (i = 0, index = indexes, n = neighbours; i < numtris; i++, index += 3, n += 3)\n\t{\n\t\tn[0] = R_FindTriangleWithEdge (indexes, numtris, index[1], index[0], i);\n\t\tn[1] = R_FindTriangleWithEdge (indexes, numtris, index[2], index[1], i);\n\t\tn[2] = R_FindTriangleWithEdge (indexes, numtris, index[0], index[2], i);\n\t}\n}\n#endif\n\n\n\n\n#if 0\nvoid GL_GenerateNormals(float *orgs, float *normals, int *indicies, int numtris, int numverts)\n{\n\tvec3_t d1, d2;\n\tvec3_t norm;\n\tint t, i, v1, v2, v3;\n\tint tricounts[MD2MAX_VERTS];\n\tvec3_t combined[MD2MAX_VERTS];\n\tint triremap[MD2MAX_VERTS];\n\tif (numverts > MD2MAX_VERTS)\n\t\treturn;\t//not an issue, you just loose the normals.\n\n\tmemset(triremap, 0, numverts*sizeof(triremap[0]));\n\n\tv2=0;\n\tfor (i = 0; i < numverts; i++)\t//weld points\n\t{\n\t\tfor (v1 = 0; v1 < v2; v1++)\n\t\t{\n\t\t\tif (orgs[i*3+0] == combined[v1][0] &&\n\t\t\t\torgs[i*3+1] == combined[v1][1] &&\n\t\t\t\torgs[i*3+2] == combined[v1][2])\n\t\t\t{\n\t\t\t\ttriremap[i] = v1;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (v1 == v2)\n\t\t{\n\t\t\tcombined[v1][0] = orgs[i*3+0];\n\t\t\tcombined[v1][1] = orgs[i*3+1];\n\t\t\tcombined[v1][2] = orgs[i*3+2];\n\t\t\tv2++;\n\n\t\t\ttriremap[i] = v1;\n\t\t}\n\t}\n\tmemset(tricounts, 0, v2*sizeof(tricounts[0]));\n\tmemset(combined, 0, v2*sizeof(*combined));\n\n\tfor (t = 0; t < numtris; t++)\n\t{\n\t\tv1 = triremap[indicies[t*3]];\n\t\tv2 = triremap[indicies[t*3+1]];\n\t\tv3 = triremap[indicies[t*3+2]];\n\n\t\tVectorSubtract((orgs+v2*3), (orgs+v1*3), d1);\n\t\tVectorSubtract((orgs+v3*3), (orgs+v1*3), d2);\n\t\tCrossProduct(d1, d2, norm);\n\t\tVectorNormalize(norm);\n\n\t\tVectorAdd(norm, combined[v1], combined[v1]);\n\t\tVectorAdd(norm, combined[v2], combined[v2]);\n\t\tVectorAdd(norm, combined[v3], combined[v3]);\n\n\t\ttricounts[v1]++;\n\t\ttricounts[v2]++;\n\t\ttricounts[v3]++;\n\t}\n\n\tfor (i = 0; i < numverts; i++)\n\t{\n\t\tif (tricounts[triremap[i]])\n\t\t{\n\t\t\tVectorScale(combined[triremap[i]], 1.0f/tricounts[triremap[i]], normals+i*3);\n\t\t}\n\t}\n}\n#endif\n#endif\n\n\n\n\n\n\n\n#if defined(Q2CLIENT) || defined(Q3CLIENT)\n//q3 lightning gun / q3 railgun / q2 beams\nstatic void R_Beam_GenerateTrisoup(entity_t *e, int bemode)\n{\n\tfloat lightmap;\n\tunsigned int batchflags = 0;\n\tvecV_t *xyz;\n\tvec2_t *st;\n\tvec4_t *rgba;\n\tscenetris_t *t;\n\tshader_t *shader = NULL;\n\tfloat scale, length;\n\tvec3_t dir, v, cr;\n\n\tshader = e->forcedshader;\n\tif (!shader)\n\t\tshader = R_RegisterShader(\"q2beam\", SUF_NONE,\n\t\t\t\"{\\n\"\n\t\t\t\t\"{\\n\"\n\t\t\t\t\t\"map $whiteimage\\n\"\n\t\t\t\t\t\"rgbgen vertex\\n\"\n\t\t\t\t\t\"alphagen vertex\\n\"\n\t\t\t\t\t\"blendfunc blend\\n\"\n\t\t\t\t\"}\\n\"\n\t\t\t\"}\\n\"\n\t\t\t);\n\n\tbatchflags = 0;\n//\tif (e->flags & RF_NOSHADOW)\n\t\tbatchflags |= BEF_NOSHADOWS;\n\tif (e->flags & RF_ADDITIVE)\n\t\tbatchflags |= BEF_FORCEADDITIVE;\n\tif (e->flags & RF_TRANSLUCENT)\n\t\tbatchflags |= BEF_FORCETRANSPARENT;\n\tif (e->flags & RF_NODEPTHTEST)\n\t\tbatchflags |= BEF_FORCENODEPTH;\n\tif (e->flags & RF_FORCECOLOURMOD)\n\t\tbatchflags |= BEF_FORCECOLOURMOD;\n\tif (shader->flags & SHADER_NODLIGHT)\n\t\tbatchflags |= BEF_NODLIGHT;\n\n\tif ((batchflags & BEF_NODLIGHT) || (shader->flags & SHADER_NODLIGHT) || bemode != BEM_STANDARD)\n\t{\n\t\t//unlit sprites are just fullbright\n\t\tlightmap = 1;\n\t}\n\telse\n\t{\n#ifdef RTLIGHTS\n\t\textern cvar_t r_shadow_realtime_world_lightmaps;\n\t\t//lit sprites need to sample the world lighting. with rtlights that generally means they're 0.\n\t\tif (r_shadow_realtime_world.ival)\n\t\t\tlightmap = r_shadow_realtime_world_lightmaps.value;\n\t\telse\n#endif\n\t\t\tlightmap = 1;\n\t}\n\n\tif (cl_numstris && cl_stris[cl_numstris-1].shader == shader && cl_stris[cl_numstris-1].flags == batchflags)\n\t\tt = &cl_stris[cl_numstris-1];\n\telse\n\t{\n\t\tif (cl_numstris == cl_maxstris)\n\t\t{\n\t\t\tcl_maxstris += 8;\n\t\t\tcl_stris = BZ_Realloc(cl_stris, sizeof(*cl_stris)*cl_maxstris);\n\t\t}\n\t\tt = &cl_stris[cl_numstris++];\n\t\tt->shader = shader;\n\t\tt->numidx = 0;\n\t\tt->numvert = 0;\n\t\tt->firstidx = cl_numstrisidx;\n\t\tt->firstvert = cl_numstrisvert;\n\t\tt->flags = batchflags;\n\t}\n\n\tif (cl_numstrisidx+6 > cl_maxstrisidx)\n\t{\n\t\tcl_maxstrisidx=cl_numstrisidx+6 + 64;\n\t\tcl_strisidx = BZ_Realloc(cl_strisidx, sizeof(*cl_strisidx)*cl_maxstrisidx);\n\t}\n\tif (cl_numstrisvert+4 > cl_maxstrisvert)\n\t\tcl_stris_ExpandVerts(cl_numstrisvert+64);\n\n\txyz = &cl_strisvertv[cl_numstrisvert];\n\tst = &cl_strisvertt[cl_numstrisvert];\n\trgba = &cl_strisvertc[cl_numstrisvert];\n\n\tcl_strisidx[cl_numstrisidx++] = t->numvert+0;\n\tcl_strisidx[cl_numstrisidx++] = t->numvert+1;\n\tcl_strisidx[cl_numstrisidx++] = t->numvert+2;\n\tcl_strisidx[cl_numstrisidx++] = t->numvert+0;\n\tcl_strisidx[cl_numstrisidx++] = t->numvert+2;\n\tcl_strisidx[cl_numstrisidx++] = t->numvert+3;\n\tt->numidx += 6;\n\tt->numvert += 4;\n\tcl_numstrisvert += 4;\n\n\tscale = e->scale*5;\n\tif (!scale)\n\t\tscale = 5;\n\n\tif (shader->flags & SHADER_CULL_FRONT)\n\t\tscale *= -1;\n\n\tVectorSubtract(e->origin, e->oldorigin, dir);\n\tlength = Length(dir);\n\n\tVector2Set(st[0], 0, 1);\n\tVector2Set(st[1], 0, 0);\n\tVector2Set(st[2], length/128, 0);\n\tVector2Set(st[3], length/128, 1);\n\n\tVectorSubtract(r_refdef.vieworg, e->origin, v);\n\tCrossProduct(v, dir, cr);\n\tVectorNormalize(cr);\n\n\tVectorMA(e->origin, -scale/2, cr, xyz[0]);\n\tVectorMA(e->origin, scale/2, cr, xyz[1]);\n\n\tVectorSubtract(r_refdef.vieworg, e->oldorigin, v);\n\tCrossProduct(v, dir, cr);\n\tVectorNormalize(cr);\n\n\tVectorMA(e->oldorigin, scale/2, cr, xyz[2]);\n\tVectorMA(e->oldorigin, -scale/2, cr, xyz[3]);\n\n\tif (e->shaderRGBAf[0] != 0 || e->shaderRGBAf[1] != 0 || e->shaderRGBAf[2] != 0 || (batchflags & BEF_FORCECOLOURMOD))\n\t{\n\t\tif (e->shaderRGBAf[0] > 1)\n\t\t\te->shaderRGBAf[0] = 1;\n\t\tif (e->shaderRGBAf[1] > 1)\n\t\t\te->shaderRGBAf[1] = 1;\n\t\tif (e->shaderRGBAf[2] > 1)\n\t\t\te->shaderRGBAf[2] = 1;\n\t}\n\telse\n\t{\n\t\te->shaderRGBAf[0] = 1;\n\t\te->shaderRGBAf[1] = 1;\n\t\te->shaderRGBAf[2] = 1;\n\t}\n\n\tVectorScale(e->shaderRGBAf, lightmap, rgba[0]);\n\trgba[0][3] = e->shaderRGBAf[3];\n\tVector4Copy(rgba[0], rgba[1]);\n\tVector4Copy(rgba[0], rgba[2]);\n\tVector4Copy(rgba[0], rgba[3]);\n}\n#endif\n\nstatic void R_Sprite_GenerateTrisoup(entity_t *e, int bemode)\n{\n\tvec3_t\tpoint;\n\tmspriteframe_t\tgenframe;\n\tvec3_t\t\tspraxis[3];\n\tmsprite_t\t\t*psprite;\n\tvec3_t sprorigin;\n\tunsigned int sprtype;\n\tfloat lightmap;\n\tunsigned int batchflags = 0;\n\tvecV_t *xyz;\n\tvec2_t *st;\n\tvec4_t *rgba;\n\tscenetris_t *t;\n\n\tshader_t *shader = NULL;\n\tmspriteframe_t *frame;\n\n\tif (!e->model || e->model->type != mod_sprite || e->forcedshader)\n\t{\n\t\tframe = NULL;\n\t\tshader = e->forcedshader;\n\t\tif (!shader)\n\t\t\tshader = R_RegisterShader(\"q2beam\", SUF_NONE,\n\t\t\t\t\"{\\n\"\n\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\"map $whiteimage\\n\"\n\t\t\t\t\t\t\"rgbgen vertex\\n\"\n\t\t\t\t\t\t\"alphagen vertex\\n\"\n\t\t\t\t\t\t\"blendfunc blend\\n\"\n\t\t\t\t\t\"}\\n\"\n\t\t\t\t\"}\\n\"\n\t\t\t\t);\n\t}\n\telse\n\t{\n\t\tif (!(e->flags & RF_WEAPONMODEL))\n\t\t{\n\t\t\tif (R_CullEntityBox (e, e->model->mins, e->model->maxs))\n\t\t\t\treturn;\n\t#ifdef RTLIGHTS\n\t\t\tif (BE_LightCullModel(e->origin, e->model))\n\t\t\t\treturn;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (BE_LightCullModel(r_origin, e->model))\n\t\t\t\treturn;\n\t#endif\n\t\t}\n\n\t\t// don't even bother culling, because it's just a single\n\t\t// polygon without a surface cache\n\t\tframe = R_GetSpriteFrame(e);\n\t\tshader = frame->shader;\n\t}\n\n\tbatchflags = 0;\n//\tif (e->flags & RF_NOSHADOW)\n\t\tbatchflags |= BEF_NOSHADOWS;\n\tif (e->flags & RF_ADDITIVE)\n\t\tbatchflags |= BEF_FORCEADDITIVE;\n\tif (e->flags & RF_TRANSLUCENT)\n\t\tbatchflags |= BEF_FORCETRANSPARENT;\n\tif (e->flags & RF_NODEPTHTEST)\n\t\tbatchflags |= BEF_FORCENODEPTH;\n\tif (e->flags & RF_FORCECOLOURMOD)\n\t\tbatchflags |= BEF_FORCECOLOURMOD;\n\tif (shader->flags & SHADER_NODLIGHT)\n\t\tbatchflags |= BEF_NODLIGHT;\n//\tif (shader->flags & RF_TWOSIDED)\n//\t\tbatchflags |= BEF_FORCETWOSIDED;\n\n\tif ((batchflags & BEF_NODLIGHT) || (shader->flags & SHADER_NODLIGHT) || bemode != BEM_STANDARD)\n\t{\n\t\t//unlit sprites are just fullbright\n\t\tlightmap = 1;\n\t}\n\telse\n\t{\n#ifdef RTLIGHTS\n\t\textern cvar_t r_shadow_realtime_world_lightmaps;\n\t\t//lit sprites need to sample the world lighting. with rtlights that generally means they're 0.\n\t\tif (r_shadow_realtime_world.ival)\n\t\t\tlightmap = r_shadow_realtime_world_lightmaps.value;\n\t\telse\n#endif\n\t\t\tlightmap = 1;\n\t}\n\n\tif (e->flags & RF_WEAPONMODELNOBOB)\n\t{\n\t\tsprorigin[0] = r_refdef.weaponmatrix[3][0];\n\t\tsprorigin[1] = r_refdef.weaponmatrix[3][1];\n\t\tsprorigin[2] = r_refdef.weaponmatrix[3][2];\n\t\tVectorMA(sprorigin, e->origin[0], r_refdef.weaponmatrix[0], sprorigin);\n\t\tVectorMA(sprorigin, e->origin[1], r_refdef.weaponmatrix[1], sprorigin);\n\t\tVectorMA(sprorigin, e->origin[2], r_refdef.weaponmatrix[2], sprorigin);\n//\t\tVectorMA(sprorigin, 12, vpn, sprorigin);\n\n//\t\tbatchflags |= BEF_FORCENODEPTH;\n\t}\n\telse if (e->flags & RF_WEAPONMODEL)\n\t{\n\t\tsprorigin[0] = r_refdef.weaponmatrix_bob[3][0];\n\t\tsprorigin[1] = r_refdef.weaponmatrix_bob[3][1];\n\t\tsprorigin[2] = r_refdef.weaponmatrix_bob[3][2];\n\t\tVectorMA(sprorigin, e->origin[0], r_refdef.weaponmatrix_bob[0], sprorigin);\n\t\tVectorMA(sprorigin, e->origin[1], r_refdef.weaponmatrix_bob[1], sprorigin);\n\t\tVectorMA(sprorigin, e->origin[2], r_refdef.weaponmatrix_bob[2], sprorigin);\n//\t\tVectorMA(sprorigin, 12, vpn, sprorigin);\n\n//\t\tbatchflags |= BEF_FORCENODEPTH;\n\t}\n\telse\n\t\tVectorCopy(e->origin, sprorigin);\n\n\n\tif (cl_numstris && cl_stris[cl_numstris-1].shader == shader && cl_stris[cl_numstris-1].flags == batchflags)\n\t\tt = &cl_stris[cl_numstris-1];\n\telse\n\t{\n\t\tif (cl_numstris == cl_maxstris)\n\t\t{\n\t\t\tcl_maxstris += 8;\n\t\t\tcl_stris = BZ_Realloc(cl_stris, sizeof(*cl_stris)*cl_maxstris);\n\t\t}\n\t\tt = &cl_stris[cl_numstris++];\n\t\tt->shader = shader;\n\t\tt->numidx = 0;\n\t\tt->numvert = 0;\n\t\tt->firstidx = cl_numstrisidx;\n\t\tt->firstvert = cl_numstrisvert;\n\t\tt->flags = batchflags;\n\t}\n\n\tif (cl_numstrisidx+6 > cl_maxstrisidx)\n\t{\n\t\tcl_maxstrisidx=cl_numstrisidx+6 + 64;\n\t\tcl_strisidx = BZ_Realloc(cl_strisidx, sizeof(*cl_strisidx)*cl_maxstrisidx);\n\t}\n\tif (cl_numstrisvert+4 > cl_maxstrisvert)\n\t\tcl_stris_ExpandVerts(cl_numstrisvert+64);\n\n\txyz = &cl_strisvertv[cl_numstrisvert];\n\tst = &cl_strisvertt[cl_numstrisvert];\n\trgba = &cl_strisvertc[cl_numstrisvert];\n\n\tcl_strisidx[cl_numstrisidx++] = t->numvert+0;\n\tcl_strisidx[cl_numstrisidx++] = t->numvert+1;\n\tcl_strisidx[cl_numstrisidx++] = t->numvert+2;\n\tcl_strisidx[cl_numstrisidx++] = t->numvert+0;\n\tcl_strisidx[cl_numstrisidx++] = t->numvert+2;\n\tcl_strisidx[cl_numstrisidx++] = t->numvert+3;\n\tt->numidx += 6;\n\tt->numvert += 4;\n\tcl_numstrisvert += 4;\n\n\tif (!frame)\n\t{\n\t\tgenframe.down = genframe.left = -1;\n\t\tgenframe.up = genframe.right = 1;\n\t\tgenframe.xmirror = false;\n\t\tgenframe.lit = false;\n\t\tsprtype = SPR_VP_PARALLEL;\n\t\tframe = &genframe;\n\t}\n\telse\n\t{\n\t\t// don't even bother culling, because it's just a single\n\t\t// polygon without a surface cache\n\t\tpsprite = e->model->meshinfo;\n\t\tsprtype = psprite->type;\n\t}\n\n\tsafeswitch(sprtype)\n\t{\n\tcase SPR_ORIENTED:\n\t\t// bullet marks on walls\n\t\tif (e->flags & RF_WEAPONMODELNOBOB)\n\t\t\tMatrix3_Multiply(e->axis, r_refdef.weaponmatrix, spraxis);\n\t\telse if (e->flags & RF_WEAPONMODEL)\n\t\t\tMatrix3_Multiply(e->axis, r_refdef.weaponmatrix_bob, spraxis);\n\t\telse\n\t\t\tmemcpy(spraxis, e->axis, sizeof(spraxis));\n\t\tbreak;\n\tcase SPR_ORIENTED_BACKFACE:\n\t\t// bullet marks on walls, invisible to anyone in the direction that its facing...\n\t\tif (e->flags & RF_WEAPONMODELNOBOB)\n\t\t\tMatrix3_Multiply(e->axis, r_refdef.weaponmatrix, spraxis);\n\t\telse  if ((e->flags & RF_WEAPONMODEL) && r_refdef.playerview->viewentity > 0)\n\t\t\tMatrix3_Multiply(e->axis, r_refdef.weaponmatrix_bob, spraxis);\n\t\telse\n\t\t\tmemcpy(spraxis, e->axis, sizeof(spraxis));\n\t\tVectorNegate(spraxis[1], spraxis[1]);\n\t\tbreak;\n\n\tcase SPR_FACING_UPRIGHT:\n\t\t//up vector is worldspace up\n\t\t//side is crossproduct of (org-vieworg),up\n\t\tspraxis[2][0] = 0;spraxis[2][1] = 0;spraxis[2][2]=1;\n\t\tspraxis[1][0] = sprorigin[1] - r_origin[1];\n\t\tspraxis[1][1] = -(sprorigin[0] - r_origin[0]);\n\t\tspraxis[1][2] = 0;\n\t\tVectorNormalize (spraxis[1]);\n\t\tbreak;\n\tcase SPR_VP_PARALLEL_UPRIGHT:\n\t\t//up vector is worldspace up\n\t\t//side vector matches view\n\t\tspraxis[2][0] = 0;spraxis[2][1] = 0;spraxis[2][2]=1;\n\t\tVectorCopy (vright, spraxis[1]);\n\t\tbreak;\n\n\tcase SPR_VP_PARALLEL_ORIENTED:\n\t\t//normal sprite, except rotating with roll angles\n\t\t{\n\t\t\tvec3_t ang;\n\t\t\tint i;\n\t\t\tfloat cr,sr;\n\t\t\tVectorAngles(e->axis[0], e->axis[2], ang, false);\t//bah, slow.\n\t\t\tcr = cos(ang[2] * M_PI/180);\n\t\t\tsr = sin(ang[2]);\n\t\t\tfor (i=0 ; i<3 ; i++)\n\t\t\t{\n\t\t\t\tspraxis[1][i] = vright[i] * cr + vup[i] * sr;\n\t\t\t\tspraxis[2][i] = vright[i] * -sr + vup[i] * cr;\n\t\t\t}\n        }\n\t\tbreak;\n\tcase SPR_VP_PARALLEL:\n\t\t//normal sprite\n\tsafedefault:\n\t\tVectorCopy(vup, spraxis[2]);\n\t\tVectorCopy(vright, spraxis[1]);\n\t\tbreak;\n\t}\n\tif (e->scale)\n\t{\n\t\tspraxis[2][0]*=e->scale;\n\t\tspraxis[2][1]*=e->scale;\n\t\tspraxis[2][2]*=e->scale;\n\t\tspraxis[1][0]*=e->scale;\n\t\tspraxis[1][1]*=e->scale;\n\t\tspraxis[1][2]*=e->scale;\n\t}\n\n\tif (e->shaderRGBAf[0] != 0 || e->shaderRGBAf[1] != 0 || e->shaderRGBAf[2] != 0 || (batchflags & BEF_FORCECOLOURMOD))\n\t{\n//\t\tif (e->shaderRGBAf[0] > 1)\n//\t\t\te->shaderRGBAf[0] = 1;\n//\t\tif (e->shaderRGBAf[1] > 1)\n//\t\t\te->shaderRGBAf[1] = 1;\n//\t\tif (e->shaderRGBAf[2] > 1)\n//\t\t\te->shaderRGBAf[2] = 1;\n\t}\n\telse\n\t{\n\t\te->shaderRGBAf[0] = 1;\n\t\te->shaderRGBAf[1] = 1;\n\t\te->shaderRGBAf[2] = 1;\n\t}\n\n\tVectorScale(e->shaderRGBAf, lightmap, rgba[0]);\n\tif (frame && frame->lit && !(r_refdef.flags & RDF_NOWORLDMODEL) && cl.worldmodel && cl.worldmodel->funcs.LightPointValues)\n\t{\n\t\tR_CalcModelLighting(e, e->model);\n\t\tVectorMul(rgba[0], e->light_avg, rgba[0]);\n\t\tVectorMA(rgba[0], 0.5, e->light_range, rgba[0]);\n\t}\n\trgba[0][3] = e->shaderRGBAf[3];\n\tVector4Copy(rgba[0], rgba[1]);\n\tVector4Copy(rgba[0], rgba[2]);\n\tVector4Copy(rgba[0], rgba[3]);\n\n\tif (frame->xmirror)\n\t{\n\t\tVector2Set(st[0], 1, 1);\n\t\tVector2Set(st[1], 1, 0);\n\t\tVector2Set(st[2], 0, 0);\n\t\tVector2Set(st[3], 0, 1);\n\t}\n\telse\n\t{\n\t\tVector2Set(st[0], 0, 1);\n\t\tVector2Set(st[1], 0, 0);\n\t\tVector2Set(st[2], 1, 0);\n\t\tVector2Set(st[3], 1, 1);\n\t}\n\n\tVectorMA (sprorigin, frame->down, spraxis[2], point);\n\tVectorMA (point, frame->left, spraxis[1], xyz[0]);\n\n\tVectorMA (sprorigin, frame->up, spraxis[2], point);\n\tVectorMA (point, frame->left, spraxis[1], xyz[1]);\n\n\tVectorMA (sprorigin, frame->up, spraxis[2], point);\n\tVectorMA (point, frame->right, spraxis[1], xyz[2]);\n\n\tVectorMA (sprorigin, frame->down, spraxis[2], point);\n\tVectorMA (point, frame->right, spraxis[1], xyz[3]);\n}\n\nstatic void R_DB_Poly(batch_t *batch)\n{\n\tstatic mesh_t mesh;\n\tstatic mesh_t *meshptr = &mesh;\n\tunsigned int i = batch->user.poly.surface;\n\n\tbatch->mesh = &meshptr;\n\n\tmesh.xyz_array = cl_strisvertv + cl_stris[i].firstvert;\n\tmesh.st_array = cl_strisvertt + cl_stris[i].firstvert;\n//\tmesh.normals_array = cl_strisvertn[0];// + cl_stris[i].firstvert;\n//\tmesh.snormals_array = cl_strisvertn[1];// + cl_stris[i].firstvert;\n//\tmesh.tnormals_array = cl_strisvertn[2];// + cl_stris[i].firstvert;\n\tmesh.colors4f_array[0] = cl_strisvertc + cl_stris[i].firstvert;\n\tmesh.indexes = cl_strisidx + cl_stris[i].firstidx;\n\tmesh.numindexes = cl_stris[i].numidx;\n\tmesh.numvertexes = cl_stris[i].numvert;\n}\nstatic void BE_GenPolyBatches(batch_t **batches)\n{\n\tshader_t *shader = NULL;\n\tbatch_t *b;\n\tunsigned int i = cl_numstris, j;\n\tunsigned int sort;\n\n\twhile (i-- > 0)\n\t{\n\t\tif (!cl_stris[i].numidx)\n\t\t\tcontinue;\n\n\t\tb = BE_GetTempBatch();\n\t\tif (!b)\n\t\t\treturn;\n\n\t\tshader = cl_stris[i].shader;\n\t\tif (!shader)\n\t\t\tcontinue;\n\n\t\tb->buildmeshes = R_DB_Poly;\n\t\tb->ent = &r_worldentity;\n\t\tb->mesh = NULL;\n\t\tb->firstmesh = 0;\n\t\tb->meshes = 1;\n\t\tb->skin = NULL;\n\t\tb->texture = NULL;\n\t\tb->shader = shader;\n\t\tfor (j = 0; j < MAXRLIGHTMAPS; j++)\n\t\t\tb->lightmap[j] = -1;\n\t\tb->user.poly.surface = i;\n\t\tb->flags = cl_stris[i].flags;\n\t\tb->vbo = 0;\n\n\t\tsort = shader->sort;\n\t\tif ((b->flags & BEF_FORCEADDITIVE) && sort < SHADER_SORT_ADDITIVE)\n\t\t\tsort = SHADER_SORT_ADDITIVE;\n\t\tif ((b->flags & BEF_FORCETRANSPARENT) && SHADER_SORT_PORTAL < sort && sort < SHADER_SORT_BLEND)\n\t\t\tsort = SHADER_SORT_BLEND;\n\t\tif ((b->flags & BEF_FORCENODEPTH) && sort < SHADER_SORT_BANNER)\n\t\t\tsort = SHADER_SORT_BANNER;\n\n\t\tb->next = batches[sort];\n\t\tbatches[sort] = b;\n\t}\n}\nvoid PR_Route_Visualise(void);\nvoid BE_GenModelBatches(batch_t **batches, const dlight_t *dl, unsigned int bemode, const qbyte *worldpvs, const int *worldareas)\n{\n\tint\t\ti;\n\tentity_t *ent;\n\tmodel_t *emodel;\n\tunsigned int orig_numstris = cl_numstris;\n\tunsigned int orig_numvisedicts = cl_numvisedicts;\n//\tunsigned int orig_numstrisidx = cl_numstrisidx;\n//\tunsigned int orig_numstrisvert = cl_numstrisvert;\n\textern cvar_t r_ignoreentpvs; //legacy value is 1...\n\n\tif (r_ignoreentpvs.ival)\n\t{\n\t\tworldpvs = NULL;\n\t\tworldareas = NULL;\n\t}\n\n\t/*clear the batch list*/\n\tfor (i = 0; i < SHADER_SORT_COUNT; i++)\n\t\tbatches[i] = NULL;\n\n\tif (cl.worldmodel && !(r_refdef.flags & RDF_NOWORLDMODEL))\n\t{\n\t\tif (cl.worldmodel->terrain)\n#if defined(TERRAIN)\n\t\t\tTerr_DrawTerrainModel(batches, &r_worldentity);\n#endif\n\t\tif (cl.worldmodel->type == mod_alias)\n\t\t{\n\t\t\tr_worldentity.framestate.g[FS_REG].lerpweight[0] = 1;\n\t\t\tr_worldentity.scale = 1;\n\n\t\t\tVectorSet(r_worldentity.light_avg,   1.0, 1.0, 1.0);\n\t\t\tVectorSet(r_worldentity.light_range, 0.5, 0.5, 0.5);\n\t\t\tVectorSet(r_worldentity.light_dir,   0.0, 0.196, 0.98);\n\t\t\tr_worldentity.light_known = 1;\n\n\t\t\tR_GAlias_GenerateBatches(&r_worldentity, batches);\n\t\t}\n\t}\n\n\tR_Clutter_Emit(batches);\n\n\tif (!r_drawentities.ival)\n\t\treturn;\n\n\tif (bemode == BEM_STANDARD)\n\t{\n#ifndef CLIENTONLY\n\t\tSV_AddDebugPolygons();\n#endif\n#ifdef ENGINE_ROUTING\n\t\tPR_Route_Visualise();\n#endif\n\n\t\t//the alias cache is a backend thing that provides support for multiple entities using the same skeleton.\n\t\t//thus it needs to be cleared so that it won't reuse the cache over multiple frames.\n\t\tAlias_FlushCache();\n\t}\n\n\t// draw sprites seperately, because of alpha blending\n\tfor (i=r_refdef.firstvisedict ; i<cl_numvisedicts ; i++)\n\t{\n\t\tent = &cl_visedicts[i];\n\n\t\tif ((r_refdef.externalview || chase_active.ival) && (ent->flags & RF_FIRSTPERSON))\n\t\t\tcontinue;\n\t\tif (!r_refdef.externalview && (ent->flags & RF_EXTERNALMODEL) && !chase_active.ival)\n\t\t\tcontinue;\n\n#ifdef RTLIGHTS\n\t\tif (bemode == BEM_STENCIL || bemode == BEM_DEPTHONLY)\n\t\t{\n\t\t\tif (ent->flags & (RF_NOSHADOW | RF_ADDITIVE | RF_NODEPTHTEST | RF_TRANSLUCENT))\t//noshadow often isn't enough for legacy content.\n\t\t\t\tcontinue;\n\t\t\tif (ent->flags & RF_EXTERNALMODEL && !r_shadow_playershadows.ival)\t//noshadow often isn't enough for legacy content.\n\t\t\t\tcontinue;\n\t\t\tif (ent->keynum == dl->key && ent->keynum)\t//shadows are not cast from the entity that owns the light. it is expected to be inside.\n\t\t\t\tcontinue;\n\t\t\tif (ent->model && ent->model->engineflags & MDLF_FLAME)\n\t\t\t\tcontinue;\n\t\t}\n#endif\n\n\t\tif (worldpvs && !cl.worldmodel->funcs.EdictInFatPVS(cl.worldmodel, &ent->pvscache, worldpvs, worldareas))\n\t\t\tcontinue;\n\n\t\tswitch(ent->rtype)\n\t\t{\n\t\tcase RT_MODEL:\n\t\tdefault:\n\t\t\temodel = ent->model;\n\t\t\tif (!emodel)\n\t\t\t\tcontinue;\n\t\t\tif (emodel->loadstate == MLS_NOTLOADED)\n\t\t\t{\n\t\t\t\tif (!Mod_LoadModel(emodel, MLV_WARN))\n\t\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (emodel->loadstate != MLS_LOADED)\n\t\t\t\tcontinue;\n\n\t\t\tif (cl.lerpents && (cls.allow_anyparticles))\t//allowed or static\n\t\t\t{\n\t\t\t\tif (gl_part_flame.value)\n\t\t\t\t{\n\t\t\t\t\tif (emodel->engineflags & MDLF_EMITREPLACE)\n\t\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (emodel->engineflags & MDLF_NOTREPLACEMENTS)\n\t\t\t{\n\t\t\t\tif (emodel->fromgame != fg_quake || emodel->type != mod_alias)\n\t\t\t\t\tif (!ruleset_allow_sensitive_texture_replacements.value)\n\t\t\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tsafeswitch(emodel->type)\n\t\t\t{\n\t\t\tcase mod_brush:\n\t\t\t\tif (r_drawentities.ival == 2 && cls.allow_cheats)\t//2 is considered a cheat, because it can be used as a wallhack (whereas mdls are not normally considered as occluding).\n\t\t\t\t\tcontinue;\n\t\t\t\tif (emodel->lightmaps.maxstyle >= cl_max_lightstyles)\n\t\t\t\t\tR_BumpLightstyles(emodel->lightmaps.maxstyle);\n\t\t\t\tSurf_GenBrushBatches(batches, ent);\n\t\t\t\tbreak;\n\t\t\tcase mod_alias:\n\t\t\t\tif (r_drawentities.ival == 3)\n\t\t\t\t\tcontinue;\n\t\t\t\tR_GAlias_GenerateBatches(ent, batches);\n\t\t\t\tbreak;\n\t\t\tcase mod_sprite:\n\t\t\t\tR_Sprite_GenerateTrisoup(ent, bemode);\n\t\t\t\tbreak;\n\t\t\tcase mod_halflife:\n#ifdef HALFLIFEMODELS\n\t\t\t\tR_HalfLife_GenerateBatches(ent, batches);\n#endif\n\t\t\t\tbreak;\n\t\t\tcase mod_dummy:\n\t\t\tcase mod_heightmap:\n#if defined(TERRAIN)\n\t\t\t\tif (emodel->terrain && !(r_refdef.flags & RDF_NOWORLDMODEL))\n\t\t\t\t\tTerr_DrawTerrainModel(batches, ent);\n#endif\n\t\t\t\tbreak;\n\t\t\tsafedefault:\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase RT_SPRITE:\n\t\t\tR_Sprite_GenerateTrisoup(ent, bemode);\n\t\t\tbreak;\n\n\t\tcase RT_BEAM:\n\t\tcase RT_RAIL_RINGS:\n\t\tcase RT_LIGHTNING:\n\t\tcase RT_RAIL_CORE:\n#if defined(Q2CLIENT) || defined(Q3CLIENT)\n\t\t\tR_Beam_GenerateTrisoup(ent, bemode);\n#endif\n\t\t\tbreak;\n\n\t\tcase RT_POLY:\n\t\t\t/*not implemented*/\n\t\t\tbreak;\n\t\tcase RT_PORTALSURFACE:\n\t\t\t/*nothing*/\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tif (cl_numstris && !(r_refdef.flags & RDF_DISABLEPARTICLES))\n\t\tBE_GenPolyBatches(batches);\n\n\twhile(orig_numstris < cl_numstris)\n\t\tcl_stris[orig_numstris++].shader = NULL;\n\tcl_numstris = orig_numstris;\n/*\tcl_numstrisidx = orig_numstrisidx;\n\tcl_numstrisvert = orig_numstrisvert;\n\tif (cl_numstris)\n\t{\t//fix this up, in case they got merged.\n\t\tcl_stris[cl_numstris-1].numvert = cl_numstrisvert - cl_stris[cl_numstris-1].firstvert;\n\t\tcl_stris[cl_numstris-1].numidx = cl_numstrisidx - cl_stris[cl_numstris-1].firstidx;\n\t}\n*/\tcl_numvisedicts = orig_numvisedicts;\n}\n\n#endif\t// defined(GLQUAKE)\n"
  },
  {
    "path": "engine/gl/gl_backend.c",
    "content": "#include \"quakedef.h\"\n\n//#define FORCESTATE\n\nvoid DumpGLState(void);\n\n#ifdef GLQUAKE\n\n#define r_refract_fboival (gl_config.ext_framebuffer_objects && r_refract_fbo.ival)\n\n#include \"glquake.h\"\n#include \"shader.h\"\n\n#ifdef FORCESTATE\n#pragma warningmsg(\"FORCESTATE is active\")\n#endif\n\n#ifdef ANDROID\n/*android appears to have a bug, and requires f and not i*/\n#define qglTexEnvi qglTexEnvf\n#endif\n\nextern cvar_t gl_overbright;\nextern cvar_t r_tessellation;\nextern cvar_t r_wireframe;\nextern cvar_t r_outline;\nextern cvar_t r_outline_width;\nextern cvar_t r_refract_fbo;\n\nextern texid_t missing_texture;\nextern texid_t missing_texture_gloss;\nextern texid_t missing_texture_normal;\nextern texid_t scenepp_postproc_cube;\nextern texid_t r_whiteimage;\n\n#ifdef GLQUAKE\nstatic texid_t shadowmap[3];\nstatic int shadow_fbo_id;\nstatic int shadow_fbo_depth_num;\n#endif\n\n#ifndef GLSLONLY\nstatic void GenerateTCMods(const shaderpass_t *pass, int passnum);\n#endif\n\nstatic const char LIGHTPASS_SHADER[] = \"\\\n{\\n\\\n\tprogram rtlight%s\\n\\\n\\\n\t{\\n\\\n\t\tmap $diffuse\\n\\\n\t\tnodepth\\n\\\n\t\tblendfunc add\\n\\\n\t}\\n\\\n\t{\\n\\\n\t\tmap $normalmap\\n\\\n\t}\\n\\\n\t{\\n\\\n\t\tmap $specular\\n\\\n\t}\\n\\\n\t{\\n\\\n\t\tmap $lightcubemap\\n\\\n\t}\\n\\\n\t{\\n\\\n\t\tmap $shadowmap\\n\\\n\t}\\n\\\n\t{\\n\\\n\t\tmap $loweroverlay\\n\\\n\t}\\n\\\n\t{\\n\\\n\t\tmap $upperoverlay\\n\\\n\t}\\n\\\n}\";\n\nextern cvar_t r_glsl_offsetmapping, r_portalrecursion, r_portalonly;\n\nstatic void BE_SendPassBlendDepthMask(unsigned int sbits);\nvoid GLBE_SubmitBatch(batch_t *batch);\n#ifdef RTLIGHTS\nstatic qboolean GLBE_RegisterLightShader(int mode);\n#endif\n\nstatic struct {\n\t//internal state\n\tstruct {\n\t\tint lastpasstmus;\n//\t\tint vbo_colour;\n//\t\tint vbo_texcoords[SHADER_PASS_MAX];\n//\t\tint vbo_deforms;\t//holds verticies... in case you didn't realise.\n\n\t\tconst shader_t *shader_light[LSHADER_MODES];\n\t\tqboolean inited_shader_light[LSHADER_MODES];\n\n\t\tconst shader_t *crepskyshader;\n\t\tconst shader_t *crepopaqueshader;\n\t\tconst shader_t *depthonlyshader;\n\t\tconst shader_t *wireframeshader;\n\n\t\tunion programhandle_u\tallblackshader;\n\t\tint allblack_mvp;\n\n\t\tprogram_t *programfixedemu[8];\n\n\t\ttexid_t tex_gbuf[GBUFFER_COUNT];\n\t\tint fbo_current;\t//the one currently being rendered to\n\t\ttexid_t tex_sourcecol; /*this is used by $sourcecolour tgen*/\n\t\ttexid_t tex_sourcedepth;\n\t\ttexid_t tex_reflectcube;/*used where $reflectcube was invalid or failed*/\n\t\tfbostate_t fbo_2dfbo;\n\t\tfbostate_t fbo_reflectrefrac[R_MAX_RECURSE];\n\t\tfbostate_t fbo_lprepass;\n\t\ttexid_t tex_reflection[R_MAX_RECURSE];\t/*basically a portal rendered to texture*/\n\t\ttexid_t tex_refraction[R_MAX_RECURSE];\t/*the (culled) underwater view*/\n\t\ttexid_t tex_refractiondepth[R_MAX_RECURSE];\t/*the (culled) underwater view*/\n\t\ttexid_t tex_ripplemap[R_MAX_RECURSE];\t/*temp image for waves and things.*/\n\n\t\tint curpatchverts;\n\t\tqboolean force2d;\n\t\tint currenttmu;\n\t\tint blendmode[SHADER_TMU_MAX];\n\t\tint texenvmode[SHADER_TMU_MAX];\n\t\tint currenttextures[SHADER_TMU_MAX];\n\t\tGLenum curtexturetype[SHADER_TMU_MAX];\n\n\t\tpolyoffset_t curpolyoffset;\n\t\tunsigned int curcull;\n\t\ttexid_t curshadowmap;\n\n\t\tunsigned int shaderbits;\n\t\tunsigned int sha_attr;\n\t\tint currentprogram;\n\t\tint lastuniform; /*program which was last set, so using the same prog for multiple surfaces on the same ent (ie: world) does not require lots of extra uniform chnges*/\n\n\t\tbatch_t dummybatch;\n\t\tvbo_t dummyvbo;\n\t\tint currentvbo;\n\t\tint currentebo;\n\t\tint currentvao;\n\n\t\tmesh_t **meshes;\n\t\tunsigned int meshcount;\n\t\tfloat modelmatrix[16];\n\t\tfloat modelmatrixinv[16];\n\t\tfloat modelviewmatrix[16];\n\t\tfloat projectionmatrix[16];\n\n\t\tint colourarraytype;\n\t\tvec4_t pendingcolourflat;\n\t\tint pendingcolourvbo;\n\t\tvoid *pendingcolourpointer;\n\t\tint curcolourvbo;\n\t\tvoid *curcolourpointer;\n\n\t\tint pendingvertexvbo;\n\t\tvoid *pendingvertexpointer;\n\t\tint curvertexvbo;\n\t\tvoid *curvertexpointer;\n\n\t\tint streamvbo[64];\n\t\tint streamebo[64];\n\t\tint streamvao[64];\n\t\tint streamid;\n\n\t\tint pendingtexcoordparts[SHADER_TMU_MAX];\n\t\tint pendingtexcoordvbo[SHADER_TMU_MAX];\n\t\tvoid *pendingtexcoordpointer[SHADER_TMU_MAX];\n\n\t\tfloat identitylighting;\t//set to how bright world lighting should be (reduced by realtime_world_lightmaps)\n\t\tfloat identitylightmap;\t//set to how bright lightmaps should be (reduced by overbrights+realtime_world_lightmaps)\n\t\tpolyoffset_t polyoffset; //mode-specific polygon offsets...\n\n\t\ttexid_t temptexture; //$current\n\t\ttexid_t fogtexture;\n\t\ttexid_t normalisationcubemap;\n\t\tfloat fogfar;\n\t\tint usingweaponviewmatrix;\n\n\t\tbatch_t **mbatches;\t//model batches (ie: not world)\n\t};\n\n\t//exterior state (paramters)\n\tstruct {\n\t\tbackendmode_t mode;\n\t\tunsigned int flags;\n\t\tint oldwidth, oldheight;\n\n\t\tvbo_t *sourcevbo;\n\t\tconst material_t *curshader;\n\t\tconst entity_t *curentity;\n\t\tconst batch_t *curbatch;\n\t\tconst texnums_t *curtexnums;\n\t\tconst mfog_t *fog;\n\t\tconst dlight_t *curdlight;\n\n\t\tfloat curtime;\n\t\tfloat updatetime;\n\n\t\tint lightmode;\n\t\tvec3_t lightorg;\n\t\tvec3_t lightdir;\n\t\tvec3_t lightcolours;\n\t\tvec3_t lightcolourscale;\n\t\tfloat lightradius;\n\t\ttexid_t lighttexture;\n\t\ttexid_t lightcubemap;\n\t\tfloat lightprojmatrix[16]; /*world space*/\n\t\tvec2_t lightshadowmapscale;\n\t\tvec4_t lightshadowmapinfo;\n\t\tvec4_t lightshadowmapproj;\n\t};\n\n\tint wbatch;\n\tint maxwbatches;\n\tbatch_t *wbatches;\n} shaderstate;\n\n#ifdef _DEBUG\n#define DRAWCALL(f) if (sh_config.showbatches) BE_PrintDrawCall(f)\n#include \"pr_common.h\"\nstatic void BE_PrintDrawCall(const char *msg)\n{\n\tchar shadername[512];\n\tchar modelname[512];\n\tQ_snprintfz(shadername, sizeof(shadername), \"^[%-16s\\\\tipimg\\\\%s\\\\tipimgtype\\\\%i\\\\tip\\\\%s^]\",\n\t\t\tshaderstate.curshader->name,\n\t\t\tshaderstate.curshader->name,shaderstate.curshader->usageflags,\n\t\t\tshaderstate.curshader->name);\n\n\tif (shaderstate.curbatch && shaderstate.curbatch->ent)\n\t{\n#ifdef HAVE_SERVER\n\t\tint num = shaderstate.curbatch->ent->keynum;\n#endif\n\t\tif (shaderstate.curbatch->ent->model)\n\t\t\tQ_snprintfz(modelname, sizeof(modelname), \" - ^[%s\\\\modelviewer\\\\%s^]\",\n\t\t\t\tshaderstate.curbatch->ent->model->name, shaderstate.curbatch->ent->model->name);\n\t\telse\n\t\t\t*modelname = 0;\n#ifdef HAVE_SERVER\n\t\tif (num >= 1 && num < sv.world.num_edicts)\n\t\t\tCon_Printf(\"%s shader %s ent %i%s - \\\"%s\\\"\\n\", msg, shadername, shaderstate.curbatch->ent->keynum, modelname, sv.world.progs->StringToNative(sv.world.progs, (WEDICT_NUM_PB(sv.world.progs, num))->v->classname));\n\t\telse\n#endif\n\t\t\tCon_Printf(\"%s shader %s ent %i%s\\n\", msg, shadername, shaderstate.curbatch->ent->keynum, modelname);\n\t}\n\telse\n\t\tCon_Printf(\"%s shader %s\\n\", msg, shadername);\n}\n#else\n#define DRAWCALL(f)\n#endif\n\nstatic void BE_PolyOffset(void)\n{\n\tpolyoffset_t po;\n\tpo.factor = shaderstate.curshader->polyoffset.factor;\n\tpo.unit = shaderstate.curshader->polyoffset.unit;\n\n#ifdef BEF_PUSHDEPTH\n\tif (shaderstate.flags & BEF_PUSHDEPTH)\n\t{\n\t\t/*some quake doors etc are flush with the walls that they're meant to be hidden behind, or plats the same height as the floor, etc\n\t\twe move them back very slightly using polygonoffset to avoid really ugly z-fighting*/\n\t\textern cvar_t r_polygonoffset_submodel_offset, r_polygonoffset_submodel_factor;\n\t\tpo.factor += r_polygonoffset_submodel_factor.value;\n\t\tpo.unit += r_polygonoffset_submodel_offset.value;\n\t}\n#endif\n\tpo.factor += shaderstate.polyoffset.factor;\n\tpo.unit += shaderstate.polyoffset.unit;\n\n#ifndef FORCESTATE\n\tif (shaderstate.curpolyoffset.factor != po.factor || shaderstate.curpolyoffset.unit != po.unit)\n#endif\n\t{\n\t\tshaderstate.curpolyoffset = po;\n\t\tif (shaderstate.curpolyoffset.factor || shaderstate.curpolyoffset.unit)\n\t\t{\n\t\t\tqglEnable(GL_POLYGON_OFFSET_FILL);\n\t\t\tqglPolygonOffset(shaderstate.curpolyoffset.factor, shaderstate.curpolyoffset.unit);\n\t\t}\n\t\telse\n\t\t\tqglDisable(GL_POLYGON_OFFSET_FILL);\n\t}\n}\n\nvoid GLBE_PolyOffsetStencilShadow\n\t\t\t\t\t#ifdef BEF_PUSHDEPTH\n\t\t\t\t\t\t\t(qboolean pushdepth)\n\t\t\t\t\t#else\n\t\t\t\t\t\t\t(void)\n\t\t\t\t\t#endif\n{\n\textern cvar_t r_polygonoffset_stencil_offset, r_polygonoffset_stencil_factor;\n\tpolyoffset_t po;\n\tpo.factor = r_polygonoffset_stencil_factor.value;\n\tpo.unit = r_polygonoffset_stencil_offset.value;\n\n#ifdef BEF_PUSHDEPTH\n\tif (pushdepth)\n\t{\n\t\t/*some quake doors etc are flush with the walls that they're meant to be hidden behind, or plats the same height as the floor, etc\n\t\twe move them back very slightly using polygonoffset to avoid really ugly z-fighting*/\n\t\textern cvar_t r_polygonoffset_submodel_offset, r_polygonoffset_submodel_factor;\n\t\tpo.factor += r_polygonoffset_submodel_factor.value;\n\t\tpo.unit += r_polygonoffset_submodel_offset.value;\n\t}\n#endif\n\n#ifndef FORCESTATE\n\tif (shaderstate.curpolyoffset.factor != po.factor || shaderstate.curpolyoffset.unit != po.unit)\n#endif\n\t{\n\t\tshaderstate.curpolyoffset = po;\n\t\tif (shaderstate.curpolyoffset.factor || shaderstate.curpolyoffset.unit)\n\t\t{\n\t\t\tqglEnable(GL_POLYGON_OFFSET_FILL);\n\t\t\tqglPolygonOffset(shaderstate.curpolyoffset.factor, shaderstate.curpolyoffset.unit);\n\t\t}\n\t\telse\n\t\t\tqglDisable(GL_POLYGON_OFFSET_FILL);\n\t}\n}\nstatic void GLBE_PolyOffsetShadowMap(void)\n{\n\textern cvar_t r_polygonoffset_shadowmap_offset, r_polygonoffset_shadowmap_factor;\n\tpolyoffset_t po;\n#if 0//def BEF_PUSHDEPTH\n\tif (pushdepth)\n\t{\n\t\t/*some quake doors etc are flush with the walls that they're meant to be hidden behind, or plats the same height as the floor, etc\n\t\twe move them back very slightly using polygonoffset to avoid really ugly z-fighting*/\n\t\textern cvar_t r_polygonoffset_submodel_offset, r_polygonoffset_submodel_factor;\n\t\tpo.factor = r_polygonoffset_submodel_factor.value + r_polygonoffset_shadowmap_factor.value;\n\t\tpo.unit = r_polygonoffset_submodel_offset.value + r_polygonoffset_shadowmap_offset.value;\n\t}\n\telse\n#endif\n\t{\n\t\tpo.factor = r_polygonoffset_shadowmap_factor.value;\n\t\tpo.unit = r_polygonoffset_shadowmap_offset.value;\n\t}\n\n#ifndef FORCESTATE\n\tif (shaderstate.curpolyoffset.factor != po.factor || shaderstate.curpolyoffset.unit != po.unit)\n#endif\n\t{\n\t\tshaderstate.curpolyoffset = po;\n\t\tif (shaderstate.curpolyoffset.factor || shaderstate.curpolyoffset.unit)\n\t\t{\n\t\t\tqglEnable(GL_POLYGON_OFFSET_FILL);\n\t\t\tqglPolygonOffset(shaderstate.curpolyoffset.factor, shaderstate.curpolyoffset.unit);\n\t\t}\n\t\telse\n\t\t\tqglDisable(GL_POLYGON_OFFSET_FILL);\n\t}\n}\n\n#ifndef GLSLONLY\nvoid GL_TexEnv(GLenum mode)\n{\n#ifndef FORCESTATE\n\tif (mode != shaderstate.texenvmode[shaderstate.currenttmu])\n#endif\n\t{\n\t\tqglTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, mode);\n\t\tshaderstate.texenvmode[shaderstate.currenttmu] = mode;\n\t}\n}\n\nstatic void BE_SetPassBlendMode(int tmu, int pbm)\n{\n#ifndef FORCESTATE\n\tif (shaderstate.blendmode[tmu] != pbm)\n#endif\n\t{\n\t\tshaderstate.blendmode[tmu] = pbm;\n#ifndef FORCESTATE\n\t\tif (shaderstate.currenttmu != tmu)\n#endif\n\t\t\tGL_SelectTexture(tmu);\n\n\t\tswitch (pbm)\n\t\t{\n\t\tcase PBM_DOTPRODUCT:\n\t\t\tGL_TexEnv(GL_COMBINE_ARB);\n\t\t\tqglTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE);\n\t\t\tqglTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PREVIOUS_ARB);\n\t\t\tqglTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_DOT3_RGB_ARB);\n\t\t\tqglTexEnvf(GL_TEXTURE_ENV, GL_RGB_SCALE_ARB, 1);\n\t\t\tbreak;\n\t\tcase PBM_MODULATE_PREV_COLOUR:\n\t\t\tGL_TexEnv(GL_COMBINE_ARB);\n\t\t\tqglTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PRIMARY_COLOR_ARB);\n\t\t\tqglTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PREVIOUS_ARB);\n\t\t\tqglTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE);\n\t\t\tqglTexEnvf(GL_TEXTURE_ENV, GL_RGB_SCALE_ARB, 1);\n\t\t\tbreak;\n\t\tcase PBM_REPLACELIGHT:\n\t\t//\tif (shaderstate.identitylighting != 1)\n\t\t\t\tgoto forcemod;\n\t\t\tGL_TexEnv(GL_REPLACE);\n\t\t\tbreak;\n\t\tcase PBM_REPLACE:\n\t\t\tGL_TexEnv(GL_REPLACE);\n\t\t\tbreak;\n\t\tcase PBM_DECAL:\n\t\t\tif (tmu == 0)\n\t\t\t\tgoto forcemod;\n\t\t\tGL_TexEnv(GL_DECAL);\n\t\t\tbreak;\n\t\tcase PBM_ADD:\n\t\t\tif (tmu == 0)\n\t\t\t\tgoto forcemod;\n\t\t\tGL_TexEnv(GL_ADD);\n\t\t\tbreak;\n\t\tcase PBM_OVERBRIGHT:\n\t\t\tGL_TexEnv(GL_COMBINE_ARB);\n\t\t\tqglTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE);\n\t\t\tqglTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PREVIOUS_ARB);\n\t\t\tqglTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE);\n\t\t\tqglTexEnvf(GL_TEXTURE_ENV, GL_RGB_SCALE_ARB, 1<<gl_overbright.ival);\n\t\t\tbreak;\n\t\tdefault:\n\t\tcase PBM_MODULATE:\n\t\tforcemod:\n\t\t\tGL_TexEnv(GL_MODULATE);\n\t\t\tbreak;\n\t\t}\n\t}\n}\n#endif\n\n/*OpenGL requires glDepthMask(GL_TRUE) or glClear(GL_DEPTH_BUFFER_BIT) will fail*/\nvoid GL_ForceDepthWritable(void)\n{\n#ifndef FORCESTATE\n\tif (!(shaderstate.shaderbits & SBITS_MISC_DEPTHWRITE))\n#endif\n\t{\n\t\tshaderstate.shaderbits |= SBITS_MISC_DEPTHWRITE;\n\t\tqglDepthMask(GL_TRUE);\n\t}\n\n\tqglColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);\n\tshaderstate.shaderbits |= SBITS_MASK_BITS;\n}\n\nvoid GL_SetShaderState2D(qboolean is2d)\n{\n\tshaderstate.usingweaponviewmatrix = -1;\t//force projection matrix info to get reset\n\tshaderstate.updatetime = realtime;\n\tshaderstate.force2d = is2d;\n\tif (is2d)\n\t{\n\t\tmemcpy(shaderstate.modelviewmatrix, r_refdef.m_view, sizeof(shaderstate.modelviewmatrix));\n\t\tif (qglLoadMatrixf)\n\t\t\tqglLoadMatrixf(r_refdef.m_view);\n\t}\n\tBE_SelectMode(BEM_STANDARD);\n\n\n//\tif (cl.paused || cls.state < ca_active)\n\t\tshaderstate.updatetime = r_refdef.time;\n//\telse\n//\t\tshaderstate.updatetime = cl.servertime;\n\tBE_SelectEntity(&r_worldentity);\n\tshaderstate.curtime = shaderstate.updatetime - shaderstate.curentity->shaderTime;\n}\n\nvoid GL_SelectTexture(int target)\n{\n\tshaderstate.currenttmu = target;\n\tif (qglActiveTextureARB)\n\t\tqglActiveTextureARB(target + mtexid0);\n}\n\nvoid GL_SelectVBO(int vbo)\n{\n#ifndef FORCESTATE\n\tif (shaderstate.currentvbo != vbo)\n#endif\n\t{\n\t\tshaderstate.currentvbo = vbo;\n\t\tqglBindBufferARB(GL_ARRAY_BUFFER_ARB, shaderstate.currentvbo);\n\t}\n}\nvoid GL_DeselectVAO(void)\n{\n\tif (shaderstate.currentvao)\n\t{\n\t\tqglBindVertexArray(0);\n\t\tshaderstate.currentvao = 0;\n\t}\n}\nvoid GL_SelectEBO(int vbo)\n{\n\t//EBO is part of the current VAO, so keep things matching that\n#ifndef FORCESTATE\n\tif (shaderstate.currentebo != vbo)\n#endif\n\t{\n\t\tshaderstate.currentebo = vbo;\n\t\tqglBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, shaderstate.currentebo);\n\t}\n}\n\nvoid GL_MTBind(int tmu, int target, texid_t texnum)\n{\n\tGL_SelectTexture(tmu);\n\n#ifndef FORCESTATE\n\tif (shaderstate.currenttextures[tmu] == texnum->num)\n\t\treturn;\n#endif\n\n\tshaderstate.currenttextures[tmu] = texnum->num;\n\tif (target)\n\t\tqglBindTexture (target, texnum->num);\n\n\tif (\n#ifndef FORCESTATE\n\tshaderstate.curtexturetype[tmu] != target &&\n#endif\n !gl_config_nofixedfunc)\n\t{\n\n\t\tif (shaderstate.curtexturetype[tmu])\n\t\t\tqglDisable(shaderstate.curtexturetype[tmu]);\n\t\tshaderstate.curtexturetype[tmu] = target;\n\t\tif (target)\n\t\t\tqglEnable(target);\n\t}\n}\n\n#if 0//def GLSLONLY\nstatic void GL_LazyBind(int tmu, texid_t texnum)\n{\n\tint glnum = texnum?texnum->num:0;\n\tif (shaderstate.currenttextures[tmu] != glnum)\n\t{\n\t\tqglBindTextureUnit(tmu, glnum);\n\t\tshaderstate.currenttextures[tmu] = glnum;\n\t}\n}\n#else\nstatic void GL_LazyBind(int tmu, texid_t texnum)\n{\n\tint glnum;\n\tGLenum target;\n\tif (texnum)\n\t{\n\t\tstatic GLenum imgtab[] = {GL_TEXTURE_2D, GL_TEXTURE_3D, GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_2D_ARRAY, GL_TEXTURE_CUBE_MAP_ARRAY_ARB};\n\t\tglnum = texnum->num, target = imgtab[(enum imgtype_e)((texnum->flags & IF_TEXTYPEMASK)>>IF_TEXTYPESHIFT)];\n\t}\n\telse\n\t\tglnum = 0, target = 0;\n\n#ifndef FORCESTATE\n\tif (shaderstate.currenttextures[tmu] != glnum)\n#endif\n\t{\n\t\tGL_SelectTexture(tmu);\n\n\t\tshaderstate.currenttextures[shaderstate.currenttmu] = glnum;\n\n#ifndef FORCESTATE\n\t\tif (shaderstate.curtexturetype[tmu] != target)\n#endif\n\t\t{\n\t\t\tif (gl_config_nofixedfunc)\n\t\t\t{\n\t\t\t\tshaderstate.curtexturetype[tmu] = target;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (shaderstate.curtexturetype[tmu])\n\t\t\t\t{\n\t\t\t\t\tqglBindTexture (shaderstate.curtexturetype[tmu], 0);\n\t\t\t\t\tqglDisable(shaderstate.curtexturetype[tmu]);\n\t\t\t\t}\n\t\t\t\tshaderstate.curtexturetype[tmu] = target;\n\t\t\t\tif (target)\n\t\t\t\t\tqglEnable(target);\n\t\t\t}\n\t\t}\n\n\t\tif (target)\n\t\t\tqglBindTexture (target, glnum);\n\t}\n}\n#endif\n\nstatic void BE_ApplyAttributes(unsigned int bitstochange, unsigned int bitstoendisable)\n{\n\tunsigned int i;\n\n#ifndef GLSLONLY\n\t//legacy colour attribute (including flat shaded)\n\tif ((bitstochange) & (1u<<VATTR_LEG_COLOUR))\n\t{\n\t\tif (!shaderstate.pendingcolourpointer && !shaderstate.pendingcolourvbo)\n\t\t{\n\t\t\tif (shaderstate.curcolourpointer || shaderstate.curcolourvbo)\n\t\t\t{\n\t\t\t\tqglShadeModel(GL_FLAT);\n\t\t\t\tqglDisableClientState(GL_COLOR_ARRAY);\n\t\t\t}\n\t\t\tshaderstate.curcolourpointer = NULL;\n\t\t\tshaderstate.curcolourvbo = 0;\n\t\t\tqglColor4fv(shaderstate.pendingcolourflat);\n\t\t}\n\t\telse\n\t\t{\n\t\t#ifndef FORCESTATE\n\t\t\tif (shaderstate.curcolourpointer != shaderstate.pendingcolourpointer || shaderstate.pendingcolourvbo != shaderstate.curcolourvbo)\n\t\t#endif\n\t\t\t{\n\t\t\t\tif (!shaderstate.curcolourpointer && !shaderstate.curcolourvbo)\n\t\t\t\t{\n\t\t\t\t\tif (qglShadeModel)\n\t\t\t\t\t\tqglShadeModel(GL_SMOOTH);\n\t\t\t\t\tbitstoendisable |= (1u<<VATTR_LEG_COLOUR);\n\t\t\t\t}\n\t\t\t\tshaderstate.curcolourpointer = shaderstate.pendingcolourpointer;\n\t\t\t\tshaderstate.curcolourvbo = shaderstate.pendingcolourvbo;\n\t\t\t\tGL_SelectVBO(shaderstate.curcolourvbo);\n\t\t\t\tqglColorPointer(4, shaderstate.colourarraytype, 0, shaderstate.curcolourpointer);\n\t\t\t}\n\n\t\t\tif ((bitstoendisable) & (1u<<VATTR_LEG_COLOUR))\n\t\t\t{\n\t\t\t\tqglEnableClientState(GL_COLOR_ARRAY);\n\t\t\t}\n\t\t}\n\t}\n\telse\n\t{\n\t\tif ((bitstoendisable) & (1u<<VATTR_LEG_COLOUR))\n\t\t{\n\t\t\tqglDisableClientState(GL_COLOR_ARRAY);\n\t\t}\n\t}\n\n\t//legacy tmus\n\tif ((bitstoendisable|bitstochange) >= (1u<<VATTR_LEG_TMU0))\n\t{\n\t\tfor (i = VATTR_LEG_TMU0; (bitstoendisable|bitstochange) >= (1u<<i); i++)\n\t\t{\n\t\t\tif ((bitstochange) & (1u<<i))\n\t\t\t{\n\t\t\t\tqglClientActiveTextureARB(i-VATTR_LEG_TMU0 + mtexid0);\n\t\t\t\tif (bitstoendisable & (1u<<i))\n\t\t\t\t\tqglEnableClientState(GL_TEXTURE_COORD_ARRAY);\n\t\t\t\tGL_SelectVBO(shaderstate.pendingtexcoordvbo[i-VATTR_LEG_TMU0]);\n\t\t\t\tqglTexCoordPointer(shaderstate.pendingtexcoordparts[i-VATTR_LEG_TMU0], GL_FLOAT, 0, shaderstate.pendingtexcoordpointer[i-VATTR_LEG_TMU0]);\n\t\t\t}\n\n#ifndef FORCESTATE\n\t\t\telse if (bitstoendisable & (1u<<i))\n#endif\n\t\t\t{\n\t\t\t\tqglClientActiveTextureARB(i-VATTR_LEG_TMU0 + mtexid0);\n\t\t\t\tif (bitstochange & (1u<<i))\n\t\t\t\t\tqglEnableClientState(GL_TEXTURE_COORD_ARRAY);\n\t\t\t\telse\n\t\t\t\t\tqglDisableClientState(GL_TEXTURE_COORD_ARRAY);\n\t\t\t}\n\t\t}\n\t}\n\n\t//legacy vertex coords\n\tif ((bitstochange) & (1u<<VATTR_LEG_VERTEX))\n\t{\n\t#ifndef FORCESTATE\n\t\tif (shaderstate.currentvao || shaderstate.curvertexpointer != shaderstate.pendingvertexpointer || shaderstate.pendingvertexvbo != shaderstate.curvertexvbo)\n\t#endif\n\t\t{\n\t\t\tshaderstate.curvertexpointer = shaderstate.pendingvertexpointer;\n\t\t\tshaderstate.curvertexvbo = shaderstate.pendingvertexvbo;\n\t\t\tGL_SelectVBO(shaderstate.curvertexvbo);\n\t\t\tqglVertexPointer(3, GL_FLOAT, VECV_STRIDE, shaderstate.curvertexpointer);\n\t\t}\n\t\tif ((bitstoendisable) & (1u<<VATTR_LEG_VERTEX))\n\t\t{\n\t\t\tqglEnableClientState(GL_VERTEX_ARRAY);\n\t\t}\n\t}\n\telse\n\t{\n\t\tif ((bitstoendisable) & (1u<<VATTR_LEG_VERTEX))\n\t\t{\n\t\t\tqglDisableClientState(GL_VERTEX_ARRAY);\n\t\t}\n\t}\n#endif\n\n\tif (!((bitstochange|bitstoendisable) & ((1u<<VATTR_LEG_FIRST)-1)))\n\t\treturn;\n\n\tfor (i = 0; i < VATTR_LEG_FIRST; i++)\n\t{\n\t\tif ((bitstochange) & (1u<<i))\n\t\t{\n\t\t\tswitch (i)\n\t\t\t{\n\t\t\tcase VATTR_VERTEX1:\n\t\t\t\t/*we still do vertex transforms for billboards and shadows and such*/\n\t\t\t\tGL_SelectVBO(shaderstate.pendingvertexvbo);\n\t\t\t\tqglVertexAttribPointer(i, 3, GL_FLOAT, GL_FALSE, VECV_STRIDE, shaderstate.pendingvertexpointer);\n\t\t\t\tbreak;\n\t\t\tcase VATTR_VERTEX2:\n\t\t\t\tif (!shaderstate.sourcevbo->coord2.gl.vbo && !shaderstate.sourcevbo->coord2.gl.addr)\n\t\t\t\t{\n\t\t\t\t\tGL_SelectVBO(shaderstate.pendingvertexvbo);\n\t\t\t\t\tqglVertexAttribPointer(i, 3, GL_FLOAT, GL_FALSE, VECV_STRIDE, shaderstate.pendingvertexpointer);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tGL_SelectVBO(shaderstate.sourcevbo->coord2.gl.vbo);\n\t\t\t\t\tqglVertexAttribPointer(VATTR_VERTEX2, 3, GL_FLOAT, GL_FALSE, VECV_STRIDE, shaderstate.sourcevbo->coord2.gl.addr);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase VATTR_COLOUR:\n\t\t\t\tif (!shaderstate.pendingcolourvbo && !shaderstate.pendingcolourpointer)\n\t\t\t\t{\n\t\t\t\t\tshaderstate.sha_attr &= ~(1u<<i);\n\t\t\t\t\tqglDisableVertexAttribArray(i);\n\t\t\t\t\tqglVertexAttrib4f(VATTR_COLOUR, 1, 1, 1, 1);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tGL_SelectVBO(shaderstate.pendingcolourvbo);\n\t\t\t\tqglVertexAttribPointer(VATTR_COLOUR, 4, shaderstate.colourarraytype, ((shaderstate.colourarraytype==GL_FLOAT)?GL_FALSE:GL_TRUE), 0, shaderstate.pendingcolourpointer);\n\t\t\t\tbreak;\n#if MAXRLIGHTMAPS > 1\n\t\t\tcase VATTR_COLOUR2:\n\t\t\t\tGL_SelectVBO(shaderstate.sourcevbo->colours[1].gl.vbo);\n\t\t\t\tqglVertexAttribPointer(VATTR_COLOUR2, 4, shaderstate.colourarraytype, ((shaderstate.colourarraytype==GL_FLOAT)?GL_FALSE:GL_TRUE), 0, shaderstate.sourcevbo->colours[1].gl.addr);\n\t\t\t\tbreak;\n\t\t\tcase VATTR_COLOUR3:\n\t\t\t\tGL_SelectVBO(shaderstate.sourcevbo->colours[2].gl.vbo);\n\t\t\t\tqglVertexAttribPointer(VATTR_COLOUR3, 4, shaderstate.colourarraytype, ((shaderstate.colourarraytype==GL_FLOAT)?GL_FALSE:GL_TRUE), 0, shaderstate.sourcevbo->colours[2].gl.addr);\n\t\t\t\tbreak;\n\t\t\tcase VATTR_COLOUR4:\n\t\t\t\tGL_SelectVBO(shaderstate.sourcevbo->colours[3].gl.vbo);\n\t\t\t\tqglVertexAttribPointer(VATTR_COLOUR4, 4, shaderstate.colourarraytype, ((shaderstate.colourarraytype==GL_FLOAT)?GL_FALSE:GL_TRUE), 0, shaderstate.sourcevbo->colours[3].gl.addr);\n\t\t\t\tbreak;\n#endif\n\t\t\tcase VATTR_TEXCOORD:\n\t\t\t\tif (!shaderstate.pendingtexcoordvbo[0] && !shaderstate.pendingtexcoordpointer[0])\n\t\t\t\t{\n\t\t\t\t\tshaderstate.sha_attr &= ~(1u<<i);\n\t\t\t\t\tqglDisableVertexAttribArray(i);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tGL_SelectVBO(shaderstate.pendingtexcoordvbo[0]);\n\t\t\t\tqglVertexAttribPointer(VATTR_TEXCOORD, shaderstate.pendingtexcoordparts[0], GL_FLOAT, GL_FALSE, 0, shaderstate.pendingtexcoordpointer[0]);\n\t\t\t\tbreak;\n\t\t\tcase VATTR_LMCOORD:\n\t\t\t\tif (!shaderstate.sourcevbo->lmcoord[0].gl.vbo && !shaderstate.sourcevbo->lmcoord[0].gl.addr)\n\t\t\t\t{\n\t\t\t\t\tGL_SelectVBO(shaderstate.sourcevbo->texcoord.gl.vbo);\n\t\t\t\t\tqglVertexAttribPointer(VATTR_LMCOORD, 2, GL_FLOAT, GL_FALSE, 0, shaderstate.sourcevbo->texcoord.gl.addr);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tGL_SelectVBO(shaderstate.sourcevbo->lmcoord[0].gl.vbo);\n\t\t\t\t\tqglVertexAttribPointer(VATTR_LMCOORD, 2, GL_FLOAT, GL_FALSE, 0, shaderstate.sourcevbo->lmcoord[0].gl.addr);\n\t\t\t\t}\n\t\t\t\tbreak;\n#if MAXRLIGHTMAPS > 1\n\t\t\tcase VATTR_LMCOORD2:\n\t\t\t\tGL_SelectVBO(shaderstate.sourcevbo->lmcoord[1].gl.vbo);\n\t\t\t\tqglVertexAttribPointer(VATTR_LMCOORD2, 2, GL_FLOAT, GL_FALSE, 0, shaderstate.sourcevbo->lmcoord[1].gl.addr);\n\t\t\t\tbreak;\n\t\t\tcase VATTR_LMCOORD3:\n\t\t\t\tGL_SelectVBO(shaderstate.sourcevbo->lmcoord[2].gl.vbo);\n\t\t\t\tqglVertexAttribPointer(VATTR_LMCOORD3, 2, GL_FLOAT, GL_FALSE, 0, shaderstate.sourcevbo->lmcoord[2].gl.addr);\n\t\t\t\tbreak;\n\t\t\tcase VATTR_LMCOORD4:\n\t\t\t\tGL_SelectVBO(shaderstate.sourcevbo->lmcoord[3].gl.vbo);\n\t\t\t\tqglVertexAttribPointer(VATTR_LMCOORD4, 2, GL_FLOAT, GL_FALSE, 0, shaderstate.sourcevbo->lmcoord[3].gl.addr);\n\t\t\t\tbreak;\n#endif\n\t\t\tcase VATTR_NORMALS:\n\t\t\t\tif (!shaderstate.sourcevbo->normals.gl.addr)\n\t\t\t\t{\n\t\t\t\t\tshaderstate.sha_attr &= ~(1u<<i);\n\t\t\t\t\tqglDisableVertexAttribArray(i);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tGL_SelectVBO(shaderstate.sourcevbo->normals.gl.vbo);\n\t\t\t\tqglVertexAttribPointer(VATTR_NORMALS, 3, GL_FLOAT, GL_FALSE, 0, shaderstate.sourcevbo->normals.gl.addr);\n\t\t\t\tbreak;\n\t\t\tcase VATTR_SNORMALS:\n\t\t\t\tif (!shaderstate.sourcevbo->svector.gl.addr)\n\t\t\t\t{\n\t\t\t\t\tshaderstate.sha_attr &= ~(1u<<i);\n\t\t\t\t\tqglDisableVertexAttribArray(i);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tGL_SelectVBO(shaderstate.sourcevbo->svector.gl.vbo);\n\t\t\t\tqglVertexAttribPointer(VATTR_SNORMALS, 3, GL_FLOAT, GL_FALSE, 0, shaderstate.sourcevbo->svector.gl.addr);\n\t\t\t\tbreak;\n\t\t\tcase VATTR_TNORMALS:\n\t\t\t\tif (!shaderstate.sourcevbo->tvector.gl.addr)\n\t\t\t\t{\n\t\t\t\t\tshaderstate.sha_attr &= ~(1u<<i);\n\t\t\t\t\tqglDisableVertexAttribArray(i);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tGL_SelectVBO(shaderstate.sourcevbo->tvector.gl.vbo);\n\t\t\t\tqglVertexAttribPointer(VATTR_TNORMALS, 3, GL_FLOAT, GL_FALSE, 0, shaderstate.sourcevbo->tvector.gl.addr);\n\t\t\t\tbreak;\n\t\t\tcase VATTR_BONENUMS:\n\t\t\t\tif (!shaderstate.sourcevbo->bonenums.gl.vbo && !shaderstate.sourcevbo->bonenums.gl.addr)\n\t\t\t\t{\n\t\t\t\t\tshaderstate.sha_attr &= ~(1u<<i);\n\t\t\t\t\tqglDisableVertexAttribArray(i);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tGL_SelectVBO(shaderstate.sourcevbo->bonenums.gl.vbo);\n\t\t\t\tqglVertexAttribPointer(VATTR_BONENUMS, 4, GL_BONE_INDEX_TYPE, GL_FALSE, 0, shaderstate.sourcevbo->bonenums.gl.addr);\n\t\t\t\tbreak;\n\t\t\tcase VATTR_BONEWEIGHTS:\n\t\t\t\tif (!shaderstate.sourcevbo->boneweights.gl.vbo && !shaderstate.sourcevbo->boneweights.gl.addr)\n\t\t\t\t{\n\t\t\t\t\tshaderstate.sha_attr &= ~(1u<<i);\n\t\t\t\t\tqglDisableVertexAttribArray(i);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tGL_SelectVBO(shaderstate.sourcevbo->boneweights.gl.vbo);\n\t\t\t\tqglVertexAttribPointer(VATTR_BONEWEIGHTS, 4, GL_FLOAT, GL_FALSE, 0, shaderstate.sourcevbo->boneweights.gl.addr);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif ((bitstoendisable) & (1u<<i))\n\t\t\t{\n\t\t\t\tqglEnableVertexAttribArray(i);\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif ((bitstoendisable) & (1u<<i))\n\t\t\t{\n\t\t\t\tqglDisableVertexAttribArray(i);\n\t\t\t}\n\t\t}\n\t}\n}\n\nstatic void BE_EnableShaderAttributes(unsigned int progattrmask, int usevao)\n{\n\tunsigned int bitstochange, bitstoendisable;\n\n\tif (shaderstate.currentvao != usevao)\n\t{\n\t\tshaderstate.currentvao = usevao;\n\t\tqglBindVertexArray(usevao); \n\t}\n\n\tif (shaderstate.currentvao)\n\t{\n\t\tbitstochange = shaderstate.sourcevbo->vaodynamic&progattrmask;\n#if 0\n\t\tbitstoendisable = 0;\n#else\n\t\tbitstoendisable = shaderstate.sourcevbo->vaoenabled^progattrmask;\n\t\tif (bitstoendisable)\n\t\t\tbitstochange |= bitstoendisable;\n\t\tshaderstate.sourcevbo->vaoenabled = progattrmask;\n#endif\n\n\t\tif (bitstochange & (1u<<VATTR_LEG_ELEMENTS))\n\t\t{\n\t\t\tqglBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, shaderstate.sourcevbo->indicies.gl.vbo);\n\t\t}\n\t}\n\telse\n\t{\n\t\tbitstochange = progattrmask;\n\t\tbitstoendisable = progattrmask^shaderstate.sha_attr;\n\n\t\tshaderstate.sha_attr = progattrmask;\n\n/*\n#ifndef FORCESTATE\n\t\tif (shaderstate.currentebo != shaderstate.sourcevbo->indicies.gl.vbo)\n#endif\n\t\t{\n\t\t\tshaderstate.currentebo = shaderstate.sourcevbo->indicies.gl.vbo;\n\t\t\tqglBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, shaderstate.currentebo);\n\t\t}\n*/\n\t}\n\n\tif (bitstochange || bitstoendisable)\n\t\tBE_ApplyAttributes(bitstochange, bitstoendisable);\n}\n\nvoid GLBE_SetupVAO(vbo_t *vbo, unsigned int vaodynamic, unsigned int vaostatic)\n{\n\tif (qglGenVertexArrays)\n\t{\n\t\tqglGenVertexArrays(1, &vbo->vao);\n\n\t\tif ((vaostatic & VATTR_VERTEX1) && !gl_config_nofixedfunc)\n\t\t\tvaostatic = (vaostatic & ~VATTR_VERTEX1) | VATTR_LEG_VERTEX;\n\t\tif ((vaodynamic & VATTR_VERTEX1) && !gl_config_nofixedfunc)\n\t\t\tvaodynamic = (vaodynamic & ~VATTR_VERTEX1) | VATTR_LEG_VERTEX;\n\n\t\tshaderstate.curvertexpointer = NULL;\n\t\tshaderstate.curvertexvbo = 0;\n\n\t\tshaderstate.sourcevbo = vbo;\n\t\tshaderstate.pendingvertexvbo = shaderstate.sourcevbo->coord.gl.vbo;\n\t\tshaderstate.pendingvertexpointer = shaderstate.sourcevbo->coord.gl.addr;\n\t\tshaderstate.colourarraytype = shaderstate.sourcevbo->colours_bytes?GL_UNSIGNED_BYTE:GL_FLOAT;\n\t\tshaderstate.pendingtexcoordvbo[0] = shaderstate.sourcevbo->texcoord.gl.vbo;\n\t\tshaderstate.pendingtexcoordpointer[0] = shaderstate.sourcevbo->texcoord.gl.addr;\n\t\tshaderstate.pendingtexcoordparts[0] = 2;\n\n\t\tshaderstate.currentvao = vbo->vao;\n\t\tqglBindVertexArray(vbo->vao);\n\t\tqglBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, shaderstate.sourcevbo->indicies.gl.vbo);\n\t\tBE_ApplyAttributes(vaostatic, vaodynamic|vaostatic);\n\t\tGL_SelectVBO(shaderstate.sourcevbo->coord.gl.vbo);\n\t\tvbo->vaoenabled = vaodynamic|vaostatic;\n\t\tvbo->vaodynamic = vaodynamic;\n\n\t\tshaderstate.curvertexpointer = NULL;\n\t\tshaderstate.curvertexvbo = 0;\n\t}\n\telse\n\t{\n\t\tGL_DeselectVAO();\n\n\t\t/*always select the coord vbo and indicies ebo, for easy bufferdata*/\n\t\tGL_SelectEBO(vbo->indicies.gl.vbo);\n\t\tGL_SelectVBO(vbo->coord.gl.vbo);\n\t}\n}\n\nvoid GL_SelectProgram(GLuint program)\n{\n\tif (shaderstate.currentprogram != program)\n\t{\n\t\tqglUseProgramObjectARB(program);\n\t\tshaderstate.currentprogram = program;\n\t}\n}\nstatic void GL_DeSelectProgram(void)\n{\n\tif (shaderstate.currentprogram != 0)\n\t{\n\t\tqglUseProgramObjectARB(0);\n\t\tshaderstate.currentprogram = 0;\n\t}\n}\n\n\nvoid GLBE_RenderShadowBuffer(unsigned int numverts, int vbo, vecV_t *verts, unsigned numindicies, int ibo, index_t *indicies)\n{\n\tshaderstate.pendingvertexvbo = vbo;\n\tshaderstate.pendingvertexpointer = verts;\n\n\tshaderstate.sourcevbo = &shaderstate.dummyvbo;\n\tshaderstate.dummyvbo.indicies.gl.vbo = ibo;\n\n\tif (shaderstate.mode != BEM_STENCIL)\n\t\tGLBE_PolyOffsetShadowMap();\n\n\tif (shaderstate.allblackshader.glsl.handle)\n\t{\n\t\tGL_SelectProgram(shaderstate.allblackshader.glsl.handle);\n\n\t\tBE_EnableShaderAttributes(gl_config_nofixedfunc?(1u<<VATTR_VERTEX1):(1u<<VATTR_LEG_VERTEX), 0);\n\n\t\tif (shaderstate.allblackshader.glsl.handle != shaderstate.lastuniform && shaderstate.allblack_mvp != -1)\n\t\t{\n\t\t\tfloat m16[16];\n\t\t\tMatrix4_Multiply(shaderstate.projectionmatrix, shaderstate.modelviewmatrix, m16);\n\t\t\tqglUniformMatrix4fvARB(shaderstate.allblack_mvp, 1, false, m16);\n\t\t}\n\t\tshaderstate.lastuniform = shaderstate.allblackshader.glsl.handle;\n\n\t\tGL_SelectEBO(ibo);\n\t\tqglDrawRangeElements(GL_TRIANGLES, 0, numverts - 1, numindicies, GL_INDEX_TYPE, indicies);\n\t}\n\telse\n\t{\n\t\tGL_DeSelectProgram();\n\t\tBE_EnableShaderAttributes((1u<<VATTR_LEG_VERTEX), 0);\n\n\t\t//draw cached world shadow mesh\n\t\tGL_SelectEBO(ibo);\n\t\tqglDrawRangeElements(GL_TRIANGLES, 0, numverts - 1, numindicies, GL_INDEX_TYPE, indicies);\n\t}\n\tRQuantAdd(RQUANT_DRAWS, 1);\n\tDRAWCALL(\"GLBE_RenderShadowBuffer\");\n\tRQuantAdd(RQUANT_SHADOWINDICIES, numindicies);\n\tshaderstate.dummyvbo.indicies.gl.vbo = 0;\n\tshaderstate.sourcevbo = NULL;\n}\n\nvoid GL_CullFace(unsigned int sflags)\n{\n\tif (shaderstate.flags & BEF_FORCETWOSIDED)\n\t\tsflags = 0;\n\telse if (sflags)\n\t\tsflags ^= r_refdef.flipcull;\n\n#ifndef FORCESTATE\n\tif (shaderstate.curcull == sflags)\n\t\treturn;\n#endif\n\tshaderstate.curcull = sflags;\n\n\tif (shaderstate.curcull & SHADER_CULL_FRONT)\n\t{\n\t\tqglEnable(GL_CULL_FACE);\n\t\tqglCullFace(GL_FRONT);\n\t}\n\telse if (shaderstate.curcull & SHADER_CULL_BACK)\n\t{\n\t\tqglEnable(GL_CULL_FACE);\n\t\tqglCullFace(GL_BACK);\n\t}\n\telse\n\t{\n\t\tqglDisable(GL_CULL_FACE);\n\t}\n}\n\nstatic void R_FetchPlayerColour(unsigned int cv, vec3_t rgb)\n{\n\tint i;\n\n\tif (cv >= 16)\n\t{\n\t\trgb[0] = (((cv&0xff0000)>>16)**((unsigned char*)&d_8to24rgbtable[15]+0)) / (256.0*256);\n\t\trgb[1] = (((cv&0x00ff00)>>8)**((unsigned char*)&d_8to24rgbtable[15]+1)) / (256.0*256);\n\t\trgb[2] = (((cv&0x0000ff)>>0)**((unsigned char*)&d_8to24rgbtable[15]+2)) / (256.0*256);\n\t\treturn;\n\t}\n\ti = cv;\n\tif (i >= 8)\n\t{\n\t\ti<<=4;\n\t}\n\telse\n\t{\n\t\ti<<=4;\n\t\ti+=15;\n\t}\n\ti*=3;\n\trgb[0] = host_basepal[i+0] / 255.0;\n\trgb[1] = host_basepal[i+1] / 255.0;\n\trgb[2] = host_basepal[i+2] / 255.0;\n/*\tif (!gammaworks)\n\t{\n\t\t*retred = gammatable[*retred];\n\t\t*retgreen = gammatable[*retgreen];\n\t\t*retblue = gammatable[*retblue];\n\t}*/\n}\n\n#ifdef RTLIGHTS\nvoid GLBE_SetupForShadowMap(dlight_t *dl, int texwidth, int texheight, float shadowscale)\n{\n\textern cvar_t r_shadow_shadowmapping_bias;\n\textern cvar_t r_shadow_shadowmapping_nearclip;\n\tfloat n = dl->nearclip?dl->nearclip:r_shadow_shadowmapping_nearclip.value;\n\tfloat f = dl->radius;\n\tfloat b = r_shadow_shadowmapping_bias.value;\n\n\tshaderstate.lightshadowmapproj[0] = shadowscale * (1.0-(1.0/texwidth)) * 0.5/3.0;\n\tshaderstate.lightshadowmapproj[1] = shadowscale * (1.0-(1.0/texheight)) * 0.5/2.0;\n\tshaderstate.lightshadowmapproj[2] = 0.5*(f+n)/(n-f);\n\tshaderstate.lightshadowmapproj[3] = (f*n)/(n-f) - b*n*(1024/texheight);\n\n\tshaderstate.lightshadowmapscale[0] = 1.0/texwidth;\n\tshaderstate.lightshadowmapscale[1] = 1.0/texheight;\n}\n\nint GLBE_BeginRenderBuffer_DepthOnly(texid_t depthtexture);\nqboolean GLBE_BeginShadowMap(int id, int w, int h, uploadfmt_t encoding, int *restorefbo)\n{\n\tif (!gl_config.ext_framebuffer_objects)\n\t\treturn false;\n\n\tif (!TEXVALID(shadowmap[id]) || shadowmap[id]->width != w || shadowmap[id]->height != h || shadowmap[id]->format != encoding || shadowmap[id]->status != TEX_LOADED)\n\t{\n\t\ttexid_t tex;\n\t\tif (shadowmap[id])\n\t\t\tImage_DestroyTexture(shadowmap[id]);\n\t\ttex = shadowmap[id] = Image_CreateTexture(va(\"***shadowmap2d%i***\", id), NULL, IF_NOPURGE|IF_CLAMP|IF_NOMIPMAP|IF_RENDERTARGET);\n\t\ttex->width = w;\n\t\ttex->height = h;\n\t\ttex->format = encoding;\n\t\tqglGenTextures(1, &tex->num);\n\t\tGL_MTBind(0, GL_TEXTURE_2D, tex);\n#ifdef SHADOWDBG_COLOURNOTDEPTH\n\t\tqglTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);\n#else\n\t\tif (qglTexStorage2D)\n\t\t\tqglTexStorage2D(GL_TEXTURE_2D, 1, gl_config.formatinfo[encoding].sizedformat, w, h);\n\t\telse if (gl_config.formatinfo[encoding].type)\n\t\t\tqglTexImage2D\t\t\t\t(GL_TEXTURE_2D, 0, gl_config.formatinfo[encoding].sizedformat, w, h, 0, gl_config.formatinfo[encoding].format, gl_config.formatinfo[encoding].type,\tNULL);\n\t\telse\n\t\t\tqglCompressedTexImage2D\t\t(GL_TEXTURE_2D, 0, gl_config.formatinfo[encoding].sizedformat, w, h, 0,\t0, NULL);\n#endif\n\n\t\tqglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);\n\t\tqglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);\n#if 0//def SHADOWDBG_COLOURNOTDEPTH\n\t\tqglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);\n\t\tqglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);\n#else\n\t\tqglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);\n\t\tqglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);\n#endif\n\t\t//in case we're using shadow samplers\n\t\tif (gl_config.arb_shadow)\n\t\t{\n\t\t\tqglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE_ARB, GL_COMPARE_R_TO_TEXTURE_ARB);\n\t\t\tqglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC_ARB, GL_LEQUAL);\n\t\t\t//qglTexParameteri(GL_TEXTURE_2D, GL_DEPTH_TEXTURE_MODE_ARB, GL_LUMINANCE);\n\t\t}\n\t\ttex->status = TEX_LOADED;\n\t}\n\tshaderstate.curshadowmap = shadowmap[id];\n\n\t/*set framebuffer*/\n\t*restorefbo = GLBE_BeginRenderBuffer_DepthOnly(shaderstate.curshadowmap);\n\n\tshaderstate.usingweaponviewmatrix = -1;\t\t//make sure the projection matrix is updated.\n\n\twhile(shaderstate.lastpasstmus>0)\n\t{\n\t\tGL_LazyBind(--shaderstate.lastpasstmus, r_nulltex);\n\t}\n\n\tshaderstate.shaderbits &= ~SBITS_MISC_DEPTHWRITE;\n\n\tBE_SelectMode(BEM_DEPTHONLY);\n\n\tBE_Scissor(NULL);\n\tqglViewport(0, 0, w, h);\n\tGL_ForceDepthWritable();\n\tqglClear (GL_DEPTH_BUFFER_BIT);\n#ifdef SHADOWDBG_COLOURNOTDEPTH\n\tqglColorMask(TRUE,TRUE,TRUE,TRUE);\n\tqglClearColor(1,1,1,1);\n\tqglClear (GL_COLOR_BUFFER_BIT);\n#endif\n\n\treturn true;\n}\nvoid GLBE_EndShadowMap(int restorefbo)\n{\n\tGLBE_FBO_Pop(restorefbo);\n\tshaderstate.usingweaponviewmatrix = -1;\t\t//make sure the projection matrix is updated.\n}\n#endif\n\nstatic void T_Gen_CurrentRender(int tmu)\n{\n\tint vwidth, vheight;\n\tint pwidth = vid.fbpwidth;\n\tint pheight = vid.fbpheight;\n\tGLenum fmt;\n\tif (r_refdef.recurse)\n\t\treturn;\n\n\tif (sh_config.texture_non_power_of_two_pic)\n\t{\n\t\tvwidth = pwidth;\n\t\tvheight = pheight;\n\t}\n\telse\n\t{\n\t\tvwidth = 1;\n\t\tvheight = 1;\n\t\twhile (vwidth < pwidth)\n\t\t{\n\t\t\tvwidth *= 2;\n\t\t}\n\t\twhile (vheight < pheight)\n\t\t{\n\t\t\tvheight *= 2;\n\t\t}\n\t}\n\n\tif (vid.flags&VID_FP16)\n\t\tfmt = GL_RGBA16F;\n\telse if (vid.flags&VID_SRGB_FB)\n\t\tfmt = GL_SRGB8_EXT;\n\telse\n\t\tfmt = GL_RGB;\n\n\t// copy the scene to texture\n\tif (!TEXVALID(shaderstate.temptexture))\n\t{\n\t\tTEXASSIGN(shaderstate.temptexture, Image_CreateTexture(\"***$currentrender***\", NULL, 0));\n\t\tqglGenTextures(1, &shaderstate.temptexture->num);\n\t}\n\tGL_MTBind(tmu, GL_TEXTURE_2D, shaderstate.temptexture);\n\tqglCopyTexImage2D(GL_TEXTURE_2D, 0, fmt, 0, 0, vwidth, vheight, 0);\n\tqglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);\n\tqglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);\n\tqglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);\n\tqglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);\n}\n\nstatic void Shader_BindTextureForPass(int tmu, const shaderpass_t *pass)\n{\n\textern cvar_t gl_specular_fallback;\n\n\ttexid_t t;\n\tswitch(pass->texgen)\n\t{\n\tdefault:\n\tcase T_GEN_SINGLEMAP:\n\t\tt = pass->anim_frames[0];\n\t\tbreak;\n\tcase T_GEN_ANIMMAP:\n\t\tt = pass->anim_frames[(int)(pass->anim_fps * shaderstate.curtime) % pass->anim_numframes];\n\t\tbreak;\n\tcase T_GEN_LIGHTMAP:\n\t\tif ((unsigned short)shaderstate.curbatch->lightmap[0] >= numlightmaps)\n\t\t\tt = r_whiteimage;\n\t\telse\n\t\t\tt = lightmap[shaderstate.curbatch->lightmap[0]]->lightmap_texture;\n\t\tbreak;\n\tcase T_GEN_DELUXMAP:\n\t\t{\n\t\t\tunsigned int lmi = shaderstate.curbatch->lightmap[0];\n\t\t\tif (lmi >= numlightmaps || !lightmap[lmi]->hasdeluxe)\n\t\t\t\tt = missing_texture_normal;\n\t\t\telse\n\t\t\t\tt = lightmap[lmi+1]->lightmap_texture;\n\t\t}\n\t\tbreak;\n\tcase T_GEN_DIFFUSE:\n\t\tif (shaderstate.curtexnums && TEXLOADED(shaderstate.curtexnums->base))\n\t\t\tt = shaderstate.curtexnums->base;\n\t\telse\n\t\t\tt = missing_texture;\n\t\tbreak;\n\tcase T_GEN_PALETTED:\n\t\tif (shaderstate.curtexnums && TEXLOADED(shaderstate.curtexnums->paletted))\n\t\t\tt = shaderstate.curtexnums->paletted;\n\t\telse\n\t\t\tt = r_whiteimage;\n\t\tbreak;\n\tcase T_GEN_NORMALMAP:\n\t\tt = (shaderstate.curtexnums && TEXLOADED(shaderstate.curtexnums->bump))?shaderstate.curtexnums->bump:missing_texture_normal;\n\t\tbreak;\n\tcase T_GEN_SPECULAR:\n\t\tif (shaderstate.curtexnums && TEXLOADED(shaderstate.curtexnums->specular))\n\t\t\tt = shaderstate.curtexnums->specular;\n\t\telse if (gl_specular_fallback.value<0 && shaderstate.curtexnums && TEXLOADED(shaderstate.curtexnums->base))\n\t\t\tt = shaderstate.curtexnums->base;\n\t\telse\n\t\t\tt = missing_texture_gloss;\n\t\tbreak;\n\tcase T_GEN_UPPEROVERLAY:\n\t\tif (shaderstate.curtexnums && TEXLOADED(shaderstate.curtexnums->upperoverlay))\n\t\t\tt = shaderstate.curtexnums->upperoverlay;\n\t\telse\n\t\t\tt = r_nulltex;\n\t\tbreak;\n\tcase T_GEN_LOWEROVERLAY:\n\t\tif (shaderstate.curtexnums && TEXLOADED(shaderstate.curtexnums->loweroverlay))\n\t\t\tt = shaderstate.curtexnums->loweroverlay;\n\t\telse\n\t\t\tt = r_nulltex;\n\t\tbreak;\n\tcase T_GEN_FULLBRIGHT:\n\t\tt = shaderstate.curtexnums->fullbright;\n\t\tbreak;\n\tcase T_GEN_REFLECTCUBE:\n\t\tif (shaderstate.curtexnums && TEXLOADED(shaderstate.curtexnums->reflectcube))\n\t\t\tt = shaderstate.curtexnums->reflectcube;\n\t\telse if (shaderstate.curbatch->envmap)\n\t\t\tt = shaderstate.curbatch->envmap;\n\t\telse\n\t\t\tt = shaderstate.tex_reflectcube;\n\t\tbreak;\n\tcase T_GEN_REFLECTMASK:\n\t\tif (shaderstate.curtexnums && TEXLOADED(shaderstate.curtexnums->reflectmask))\n\t\t\tt = shaderstate.curtexnums->reflectmask;\n\t\telse if (shaderstate.curtexnums && TEXLOADED(shaderstate.curtexnums->base))\n\t\t\tt = shaderstate.curtexnums->base;\n\t\telse\n\t\t\tt = r_whiteimage;\n\t\tbreak;\n\tcase T_GEN_DISPLACEMENT:\n\t\tif (shaderstate.curtexnums && TEXLOADED(shaderstate.curtexnums->displacement))\n\t\t\tt = shaderstate.curtexnums->displacement;\n\t\telse\n\t\t\tt = r_whiteimage;\n\t\tbreak;\n\tcase T_GEN_OCCLUSION:\n\t\tif (shaderstate.curtexnums && TEXLOADED(shaderstate.curtexnums->occlusion))\n\t\t\tt = shaderstate.curtexnums->occlusion;\n\t\telse\n\t\t\tt = r_whiteimage;\n\t\tbreak;\n\tcase T_GEN_TRANSMISSION:\n\t\tif (shaderstate.curtexnums && TEXLOADED(shaderstate.curtexnums->transmission))\n\t\t\tt = shaderstate.curtexnums->transmission;\n\t\telse\n\t\t\tt = r_whiteimage;\n\t\tbreak;\n\tcase T_GEN_THICKNESS:\n\t\tif (shaderstate.curtexnums && TEXLOADED(shaderstate.curtexnums->thickness))\n\t\t\tt = shaderstate.curtexnums->thickness;\n\t\telse\n\t\t\tt = r_whiteimage;\n\t\tbreak;\n\tcase T_GEN_SHADOWMAP:\n\t\tt = shaderstate.curshadowmap;\n\t\tbreak;\n\n\tcase T_GEN_LIGHTCUBEMAP:\n\t\tt = shaderstate.lightcubemap;\n\t\tbreak;\n\tcase T_GEN_SOURCECUBE:\n\t\tt = scenepp_postproc_cube;\n\t\tbreak;\n\n#ifdef HAVE_MEDIA_DECODER\n\tcase T_GEN_VIDEOMAP:\n\t\tt = Media_UpdateForShader(pass->cin);\n\t\tif (!TEXLOADED(t))\n\t\t\tt = shaderstate.curtexnums?shaderstate.curtexnums->base:r_nulltex;\n\t\tbreak;\n#endif\n\n\tcase T_GEN_CURRENTRENDER:\n\t\tT_Gen_CurrentRender(tmu);\n\t\treturn;\n\n\tcase T_GEN_SOURCECOLOUR:\n\t\tt = shaderstate.tex_sourcecol;\n\t\tbreak;\n\tcase T_GEN_SOURCEDEPTH:\n\t\tt = shaderstate.tex_sourcedepth;\n\t\tbreak;\n\tcase T_GEN_REFLECTION:\n\t\tt = shaderstate.tex_reflection[r_refdef.recurse];\n\t\tbreak;\n\tcase T_GEN_REFRACTION:\n\t\tif (!r_refract_fboival)\n\t\t{\n\t\t\tT_Gen_CurrentRender(tmu);\n\t\t\treturn;\n\t\t}\n\t\tt = shaderstate.tex_refraction[r_refdef.recurse];\n\t\tbreak;\n\tcase T_GEN_REFRACTIONDEPTH:\n\t\tt = shaderstate.tex_refractiondepth[r_refdef.recurse];\n\t\tbreak;\n\tcase T_GEN_RIPPLEMAP:\n\t\tt = shaderstate.tex_ripplemap[r_refdef.recurse];\n\t\tbreak;\n\tcase T_GEN_GBUFFERCASE:\n\t\tt = shaderstate.tex_gbuf[pass->texgen-T_GEN_GBUFFER0];\n\t\tbreak;\n\t}\n\tGL_LazyBind(tmu, t);\n}\n\n/*========================================== matrix functions =====================================*/\n\ntypedef vec3_t mat3_t[3];\nstatic mat3_t axisDefault={{1, 0, 0},\n\t\t\t\t\t{0, 1, 0},\n\t\t\t\t\t{0, 0, 1}};\n\nstatic void Matrix3_Transpose (mat3_t in, mat3_t out)\n{\n\tout[0][0] = in[0][0];\n\tout[1][1] = in[1][1];\n\tout[2][2] = in[2][2];\n\n\tout[0][1] = in[1][0];\n\tout[0][2] = in[2][0];\n\tout[1][0] = in[0][1];\n\tout[1][2] = in[2][1];\n\tout[2][0] = in[0][2];\n\tout[2][1] = in[1][2];\n}\nstatic void Matrix3_Multiply_Vec3 (const mat3_t a, const vec3_t b, vec3_t product)\n{\n\tproduct[0] = a[0][0]*b[0] + a[0][1]*b[1] + a[0][2]*b[2];\n\tproduct[1] = a[1][0]*b[0] + a[1][1]*b[1] + a[1][2]*b[2];\n\tproduct[2] = a[2][0]*b[0] + a[2][1]*b[1] + a[2][2]*b[2];\n}\n\nstatic int Matrix3_Compare(const mat3_t in, const mat3_t out)\n{\n\treturn memcmp(in, out, sizeof(mat3_t));\n}\n\n//end matrix functions\n/*========================================== tables for deforms =====================================*/\n#define frand() (rand()*(1.0/RAND_MAX))\n#define FTABLE_SIZE\t\t1024\n#define FTABLE_CLAMP(x)\t(((int)((x)*FTABLE_SIZE) & (FTABLE_SIZE-1)))\n#define FTABLE_EVALUATE(table,x) (table ? table[FTABLE_CLAMP(x)] : frand()*((x)-floor(x)))\n\nstatic\tfloat\tr_sintable[FTABLE_SIZE];\nstatic\tfloat\tr_triangletable[FTABLE_SIZE];\nstatic\tfloat\tr_squaretable[FTABLE_SIZE];\nstatic\tfloat\tr_sawtoothtable[FTABLE_SIZE];\nstatic\tfloat\tr_inversesawtoothtable[FTABLE_SIZE];\n\n//#define R_FastSin(x) sin((x)*(2*M_PI))\n#define R_FastSin(x) r_sintable[FTABLE_CLAMP(x)]\n\nstatic float *FTableForFunc ( unsigned int func )\n{\n\tswitch (func)\n\t{\n\t\tdefault:\n\t\tcase SHADER_FUNC_SIN:\n\t\t\treturn r_sintable;\n\n\t\tcase SHADER_FUNC_TRIANGLE:\n\t\t\treturn r_triangletable;\n\n\t\tcase SHADER_FUNC_SQUARE:\n\t\t\treturn r_squaretable;\n\n\t\tcase SHADER_FUNC_SAWTOOTH:\n\t\t\treturn r_sawtoothtable;\n\n\t\tcase SHADER_FUNC_INVERSESAWTOOTH:\n\t\t\treturn r_inversesawtoothtable;\n\n\t\tcase SHADER_FUNC_NOISE:\n\t\t\treturn NULL;\n\t}\n}\n\nvoid Shader_LightPass(struct shaderparsestate_s *ps, const char *shortname, const void *args)\n{\n\tchar shadertext[8192*2];\n\textern cvar_t r_drawflat;\n\tsprintf(shadertext, LIGHTPASS_SHADER, (r_lightmap.ival||r_drawflat.ival)?\"#FLAT=1.0\":\"\");\n\tShader_DefaultScript(ps, shortname, shadertext);\n}\n\nextern cvar_t r_fog_linear;\nextern cvar_t r_fog_exp2;\nvoid GenerateFogTexture(texid_t *tex, float density, float zscale)\n{\n#define FOGS 256\n#define FOGT 32\n\tbyte_vec4_t fogdata[FOGS*FOGT];\n\tint s, t;\n\tfloat f, z;\n\tstatic float fogdensity, fogzscale;\n\tif (TEXVALID(*tex) && density == fogdensity && zscale == fogzscale)\n\t\treturn;\n\tfogdensity = density;\n\tfogzscale = zscale;\n\n\tfor(s = 0; s < FOGS; s++)\n\t\tfor(t = 0; t < FOGT; t++)\n\t\t{\n\t\t\tz = (float)s / (FOGS-1);\n\t\t\tz *= zscale;\n\n\t\t\tif (r_fog_linear.ival) {\n\t\t\t\tf = 1.0 - ((density - z) / (density/* - r_refdef.globalfog.depthbias*/)); //pow(z, 0.5);\n\t\t\t} else {\n\t\t\t\tif (!r_fog_exp2.ival)//GL_EXP\n\t\t\t\t\tf = 1-exp(-density * z);\n\t\t\t\telse //GL_EXP2\n\t\t\t\t\tf = 1-exp(-(density*density) * z);\n\t\t\t}\n\n\t\t\tif (f < 0)\n\t\t\t\tf = 0;\n\t\t\tif (f > 1)\n\t\t\t\tf = 1;\n\t\t\tf *= (float)t / (FOGT-1);\n\n\t\t\tfogdata[t*FOGS + s][0] = 255;\n\t\t\tfogdata[t*FOGS + s][1] = 255;\n\t\t\tfogdata[t*FOGS + s][2] = 255;\n\t\t\tfogdata[t*FOGS + s][3] = 255*f;\n\t\t}\n\n\tif (!TEXVALID(*tex))\n\t\t*tex = Image_CreateTexture(\"***fog***\", NULL, IF_CLAMP|IF_NOMIPMAP);\n\tImage_Upload(*tex, TF_RGBA32, fogdata, NULL, FOGS, FOGT, 1, IF_CLAMP|IF_NOMIPMAP);\n}\n\nvoid GLBE_DestroyFBOs(void)\n{\n\tsize_t i;\n\tGLBE_FBO_Destroy(&shaderstate.fbo_lprepass);\n\n\tfor (i = 0; i < R_MAX_RECURSE; i++)\n\t{\n\t\tGLBE_FBO_Destroy(&shaderstate.fbo_reflectrefrac[i]);\n\t\tif (shaderstate.tex_reflection[i])\n\t\t{\n\t\t\tImage_DestroyTexture(shaderstate.tex_reflection[i]);\n\t\t\tshaderstate.tex_reflection[i] = r_nulltex;\n\t\t}\n\t\tif (shaderstate.tex_refraction[i])\n\t\t{\n\t\t\tImage_DestroyTexture(shaderstate.tex_refraction[i]);\n\t\t\tshaderstate.tex_refraction[i] = r_nulltex;\n\t\t}\n\t\tif (shaderstate.tex_refractiondepth[i])\n\t\t{\n\t\t\tImage_DestroyTexture(shaderstate.tex_refractiondepth[i]);\n\t\t\tshaderstate.tex_refractiondepth[i] = r_nulltex;\n\t\t}\n\t}\n\tif (shaderstate.temptexture)\n\t{\n\t\tImage_DestroyTexture(shaderstate.temptexture);\n\t\tshaderstate.temptexture = r_nulltex;\n\t}\n\n\t//shadowmapping stuff\n\tif (shadow_fbo_id)\n\t{\n\t\tqglDeleteFramebuffersEXT(1, &shadow_fbo_id);\n\t\tshadow_fbo_id = 0;\n\t\tshadow_fbo_depth_num = 0;\n\t}\n\tfor (i = 0; i < countof(shadowmap); i++)\n\t{\n\t\tif (shadowmap[i])\n\t\t{\n\t\t\tImage_DestroyTexture(shadowmap[i]);\n\t\t\tshadowmap[i] = r_nulltex;\n\t\t}\n\t}\n\n\t//nuke deferred rendering stuff\n\tfor (i = 0; i < countof(shaderstate.tex_gbuf); i++)\n\t{\n\t\tif (shaderstate.tex_gbuf[i])\n\t\t{\n\t\t\tImage_DestroyTexture(shaderstate.tex_gbuf[i]);\n\t\t\tshaderstate.tex_gbuf[i] = r_nulltex;\n\t\t}\n\t}\n}\n\nvoid GLBE_Shutdown(void)\n{\n\tsize_t u;\n\tGLBE_FBO_Destroy(&shaderstate.fbo_2dfbo);\n\tGLBE_DestroyFBOs();\n\n\tfor (u = 0; u < countof(shaderstate.programfixedemu); u++)\n\t{\n\t\tShader_ReleaseGeneric(shaderstate.programfixedemu[u]);\n\t\tshaderstate.programfixedemu[u] = NULL;\n\t}\n\n\tBZ_Free(shaderstate.wbatches);\n\tshaderstate.wbatches = NULL;\n\tshaderstate.maxwbatches = 0;\n\n\t//on vid_reload, the gl drivers might have various things bound that have since been destroyed/etc\n\t//so reset that state to avoid any issues with state\n\tBE_EnableShaderAttributes(0, 0);\n\tGL_SelectVBO(0);\n\tGL_SelectEBO(0);\n\n\tfor (u = 0; u < countof(shaderstate.currenttextures); u++)\n\t\tGL_LazyBind(u, r_nulltex);\n\tGL_SelectTexture(0);\n}\n\nvoid GLBE_Init(void)\n{\n\tint i;\n\tdouble t;\n\n\tGLBE_Shutdown();\n\n\tmemset(&shaderstate, 0, sizeof(shaderstate));\n\n\tshaderstate.curentity = &r_worldentity;\n\tbe_maxpasses = gl_config_nofixedfunc?1:gl_mtexarbable;\n\tbe_maxpasses = min(SHADER_TMU_MAX, min(be_maxpasses, 32-VATTR_LEG_TMU0));\n\tsh_config.stencilbits = 0;\n#ifndef GLESONLY\n\tif (!gl_config_gles && gl_config.glversion >= 3.0 && gl_config_nofixedfunc)\n\t{\n\t\t//docs say this line should be okay in gl3+. nvidia do not seem to agree. GL_STENCIL_BITS is depricated however. so for now, just assume.\n\t\tqglGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER_EXT, GL_STENCIL, GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE, &sh_config.stencilbits);\n\t\tif (qglGetError())\n\t\t\tsh_config.stencilbits = 8;\n\t}\n\telse\n#endif\n\t\tqglGetIntegerv(GL_STENCIL_BITS, &sh_config.stencilbits);\n\tfor (i = 0; i < FTABLE_SIZE; i++)\n\t{\n\t\tt = (double)i / (double)FTABLE_SIZE;\n\n\t\tr_sintable[i] = sin(t * 2*M_PI);\n\n\t\tif (t < 0.25)\n\t\t\tr_triangletable[i] = t * 4.0;\n\t\telse if (t < 0.75)\n\t\t\tr_triangletable[i] = 2 - 4.0 * t;\n\t\telse\n\t\t\tr_triangletable[i] = (t - 0.75) * 4.0 - 1.0;\n\n\t\tif (t < 0.5)\n\t\t\tr_squaretable[i] = 1.0f;\n\t\telse\n\t\t\tr_squaretable[i] = -1.0f;\n\n\t\tr_sawtoothtable[i] = t;\n\t\tr_inversesawtoothtable[i] = 1.0 - t;\n\t}\n\n\tshaderstate.identitylighting = 1;\n\tshaderstate.identitylightmap = 1;\n\tfor (i = 0; i < MAXRLIGHTMAPS; i++)\n\t{\n\t\tshaderstate.dummybatch.lightmap[i] = -1;\n\t\tshaderstate.dummybatch.lmlightstyle[i] = INVALID_LIGHTSTYLE;\n\t\tshaderstate.dummybatch.vtlightstyle[i] = ~0;\n\t}\n\n#ifdef RTLIGHTS\n\tSh_CheckSettings();\n\tif ((r_shadow_realtime_dlight.ival && r_shadow_shadowmapping.ival) ||\n\t\t(r_shadow_realtime_world.ival  && r_shadow_shadowmapping.ival) )\n\t{\t//we expect some lights that will need shadowmapped shadows.\n\t\tGLBE_RegisterLightShader(LSHADER_SMAP);\n\t\tGLBE_RegisterLightShader(LSHADER_SMAP|LSHADER_CUBE);\n\t\tGLBE_RegisterLightShader(LSHADER_SMAP|LSHADER_SPOT);\n\t}\n\tif ((r_shadow_realtime_dlight.ival && (!r_shadow_shadowmapping.ival || !r_shadow_realtime_dlight_shadows.ival)) ||\n\t\t(r_shadow_realtime_world.ival  && (!r_shadow_shadowmapping.ival || !r_shadow_realtime_world_shadows.ival )) )\n\t{\t//these are also used when there's no shadow.\n\t\t//FIXME: should also happen if there's static world lights without shadows. Move elsewhere?\n\t\tGLBE_RegisterLightShader(LSHADER_STANDARD);\n\t\tGLBE_RegisterLightShader(LSHADER_STANDARD|LSHADER_CUBE);\n\t\tGLBE_RegisterLightShader(LSHADER_STANDARD|LSHADER_SPOT);\n\t}\n#endif\n\n\tgl_overbright.modified = true; /*in case the d3d renderer does the same*/\n\t/*lock the cvar down if the backend can't actually do it*/\n\tif (\n#if 1//defined(QUAKETC)\n\t\t//TCs are expected to be using glsl and weird overbright things etc, don't take the risk.\n\t\t(!sh_config.progs_supported)\n#else\n\t\t//Q3 can get away with tex_env_combine for everything, if only because the content allows everything to be flattened to a single pass if needed...\n\t\t//some shaders might screw up from our approach though...\n\t\t(!gl_config.tex_env_combine && !gl_config_nofixedfunc)\n#endif\n\t\t&& gl_overbright.ival)\n\t\tCvar_ApplyLatchFlag(&gl_overbright, \"0\", CVAR_RENDERERLATCH, 0);\n\tshaderstate.shaderbits = ~SBITS_ATEST_BITS;\n\tBE_SendPassBlendDepthMask(0);\n\tcurrententity = &r_worldentity;\n\n\tshaderstate.fogtexture = r_nulltex;\n\n\tshaderstate.depthonlyshader = R_RegisterShader(\"depthonly\", SUF_NONE,\n\t\t\t\t\"{\\n\"\n\t\t\t\t\t\"program depthonly\\n\"\n\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\"depthwrite\\n\"\n\t\t\t\t\t\t\"maskcolor\\n\"\n\t\t\t\t\t\"}\\n\"\n\t\t\t\t\"}\\n\"\n\t\t\t);\n\t//if the shader doesn't work, give up with that.\n\tif (!shaderstate.depthonlyshader || !shaderstate.depthonlyshader->prog)\n\t\tshaderstate.depthonlyshader = NULL;\n\n\t//make sure the world draws correctly\n\tr_worldentity.shaderRGBAf[0] = 1;\n\tr_worldentity.shaderRGBAf[1] = 1;\n\tr_worldentity.shaderRGBAf[2] = 1;\n\tr_worldentity.shaderRGBAf[3] = 1;\n\tr_worldentity.axis[0][0] = 1;\n\tr_worldentity.axis[1][1] = 1;\n\tr_worldentity.axis[2][2] = 1;\n\tr_worldentity.light_avg[0] = 1;\n\tr_worldentity.light_avg[1] = 1;\n\tr_worldentity.light_avg[2] = 1;\n\n\tR_InitFlashblends();\n\n\tmemset(&shaderstate.streamvbo, 0, sizeof(shaderstate.streamvbo));\n\tmemset(&shaderstate.streamebo, 0, sizeof(shaderstate.streamebo));\n\tmemset(&shaderstate.streamvao, 0, sizeof(shaderstate.streamvao));\n\t//only do this where we have to.\n\tif (qglBufferDataARB && gl_config_nofixedfunc\n#ifndef FTE_TARGET_WEB\n\t\t&& !gl_config_gles\n#endif\n\t\t)\n\t{\n\t\tqglGenBuffersARB(sizeof(shaderstate.streamvbo)/sizeof(shaderstate.streamvbo[0]), shaderstate.streamvbo);\n\t\tqglGenBuffersARB(sizeof(shaderstate.streamebo)/sizeof(shaderstate.streamebo[0]), shaderstate.streamebo);\n\t\tif (qglGenVertexArrays)\n\t\t\tqglGenVertexArrays(sizeof(shaderstate.streamvao)/sizeof(shaderstate.streamvao[0]), shaderstate.streamvao);\n\t}\n}\n\n//end tables\n\n#define MAX_ARRAY_VERTS 65536\nstatic vecV_t\t\tvertexarray[MAX_ARRAY_VERTS];\n#if 1//ndef GLSLONLY\nstatic avec4_t\t\tcoloursarray[MAX_ARRAY_VERTS];\n#ifdef FTE_TARGET_WEB\nstatic float\t\ttexcoordarray[1][MAX_ARRAY_VERTS*2];\n#else\nstatic float\t\ttexcoordarray[SHADER_PASS_MAX][MAX_ARRAY_VERTS*2];\n#endif\n\n/*========================================== texture coord generation =====================================*/\n\nstatic void tcgen_environment(float *st, unsigned int numverts, float *xyz, float *normal)\n{\n\tint\t\t\ti;\n\tvec3_t\t\tviewer, reflected;\n\tfloat\t\td;\n\n\tvec3_t\t\trorg;\n\n\n\tRotateLightVector(shaderstate.curentity->axis, shaderstate.curentity->origin, r_origin, rorg);\n\n\tfor (i = 0 ; i < numverts ; i++, xyz += sizeof(vecV_t)/sizeof(vec_t), normal += 3, st += 2 )\n\t{\n\t\tVectorSubtract (rorg, xyz, viewer);\n\t\tVectorNormalizeFast (viewer);\n\n\t\td = DotProduct (normal, viewer);\n\n\t\treflected[0] = normal[0]*2*d - viewer[0];\n\t\treflected[1] = normal[1]*2*d - viewer[1];\n\t\treflected[2] = normal[2]*2*d - viewer[2];\n\n\t\tst[0] = 0.5 + reflected[1] * 0.5;\n\t\tst[1] = 0.5 - reflected[2] * 0.5;\n\t}\n}\n\n#ifndef GLSLONLY\nstatic void tcgen_fog(float *st, unsigned int numverts, float *xyz, mfog_t *fog)\n{\n\tint\t\t\ti;\n\n\tfloat z;\n\tfloat eye, point;\n\tvec4_t zmat;\n\n\t//generate a simple matrix to calc only the projected z coord\n\tzmat[0] = -shaderstate.modelviewmatrix[2];\n\tzmat[1] = -shaderstate.modelviewmatrix[6];\n\tzmat[2] = -shaderstate.modelviewmatrix[10];\n\tzmat[3] = -shaderstate.modelviewmatrix[14];\n\n\tVector4Scale(zmat, shaderstate.fogfar, zmat);\n\n\tif (fog && fog->visibleplane)\n\t{\n\t\teye = (DotProduct(r_refdef.vieworg, fog->visibleplane->normal) - fog->visibleplane->dist);\n\t\tif (eye < 1)\n\t\t\teye = 1;\t//avoids a nan\n\n\t\tfor (i = 0 ; i < numverts ; i++, xyz += sizeof(vecV_t)/sizeof(vec_t), st += 2 )\n\t\t{\n\t\t\tz = DotProduct(xyz, zmat) + zmat[3];\n\t\t\tst[0] = z;\n\n\t\t\tif (fog->visibleplane)\n\t\t\t\tpoint = (DotProduct(xyz, fog->visibleplane->normal) - fog->visibleplane->dist);\n\t\t\telse\n\t\t\t\tpoint = 1;\n\t\t\tst[1] = point / (point - eye);\n\t\t\tif (st[1] > 31/32.0)\n\t\t\t\tst[1] = 31/32.0;\n\t\t}\n\t}\n\telse\n\t{\n\t\tfor (i = 0 ; i < numverts ; i++, xyz += sizeof(vecV_t)/sizeof(vec_t), st += 2 )\n\t\t{\n\t\t\tz = DotProduct(xyz, zmat) + zmat[3];\n\t\t\tst[0] = z;\n\t\t\tst[1] = 31/32.0;\n\t\t}\n\t}\n}\nstatic void GenerateTCFog(int passnum, mfog_t *fog)\n{\n\tint m;\n\tmesh_t *mesh;\n\tfor (m = 0; m < shaderstate.meshcount; m++)\n\t{\n\t\tmesh = shaderstate.meshes[m];\n\t\tif (!mesh->xyz_array)\n\t\t\tmemset(texcoordarray[passnum]+mesh->vbofirstvert*2, 0, mesh->numvertexes*sizeof(float)*2);\n\t\telse\n\t\t\ttcgen_fog(texcoordarray[passnum]+mesh->vbofirstvert*2, mesh->numvertexes, (float*)mesh->xyz_array, fog);\n\t}\n\n\tshaderstate.pendingtexcoordparts[passnum] = 2;\n\tshaderstate.pendingtexcoordvbo[passnum] = 0;\n\tshaderstate.pendingtexcoordpointer[passnum] = texcoordarray[passnum];\n}\n#endif\n\nstatic float *tcgen3(const shaderpass_t *pass, int cnt, float *dst, const mesh_t *mesh)\n{\n\tint i;\n\tvecV_t *src;\n\tswitch (pass->tcgen)\n\t{\n\tdefault:\n\tcase TC_GEN_SKYBOX:\n\t\tsrc = mesh->xyz_array;\n\t\tfor (i = 0; i < cnt; i++, dst += 3)\n\t\t{\n\t\t\tdst[0] = src[i][0] - r_refdef.vieworg[0];\n\t\t\tdst[1] = src[i][1] - r_refdef.vieworg[1];\n\t\t\tdst[2] = src[i][2] - r_refdef.vieworg[2];\n\t\t}\n\t\treturn dst-cnt*3;\n\n//\tcase TC_GEN_WOBBLESKY:\n//\tcase TC_GEN_REFLECT:\n//\t\tbreak;\n\t}\n}\nstatic float *tcgen(const shaderpass_t *pass, int cnt, float *dst, const mesh_t *mesh)\n{\n\tint i;\n\tvecV_t *src;\n\tswitch (pass->tcgen)\n\t{\n\tdefault:\n\tcase TC_GEN_BASE:\n\t\treturn (float*)mesh->st_array;\n\tcase TC_GEN_LIGHTMAP:\n\t\tif (!mesh->lmst_array[0])\n\t\t\treturn (float*)mesh->st_array;\n\t\telse\n\t\t\treturn (float*)mesh->lmst_array[0];\n\tcase TC_GEN_NORMAL:\n\t\treturn (float*)mesh->normals_array;\n\tcase TC_GEN_SVECTOR:\n\t\treturn (float*)mesh->snormals_array;\n\tcase TC_GEN_TVECTOR:\n\t\treturn (float*)mesh->tnormals_array;\n\tcase TC_GEN_ENVIRONMENT:\n\t\tif (!mesh->normals_array)\n\t\t\treturn (float*)mesh->st_array;\n\t\ttcgen_environment(dst, cnt, (float*)mesh->xyz_array, (float*)mesh->normals_array);\n\t\treturn dst;\n\n//\tcase TC_GEN_DOTPRODUCT:\n//\t\treturn mesh->st_array[0];\n\tcase TC_GEN_VECTOR:\n\t\tsrc = mesh->xyz_array;\n\t\tfor (i = 0; i < cnt; i++, dst += 2)\n\t\t{\n\t\t\tdst[0] = DotProduct(pass->tcgenvec[0], src[i]);\n\t\t\tdst[1] = DotProduct(pass->tcgenvec[1], src[i]);\n\t\t}\n\t\treturn dst-cnt*2;\n\n//\tcase TC_GEN_SKYBOX:\n//\tcase TC_GEN_WOBBLESKY:\n//\tcase TC_GEN_REFLECT:\n//\t\tbreak;\n\t}\n}\n\n/*src and dst can be the same address when tcmods are chained*/\nstatic void tcmod(const tcmod_t *tcmod, int cnt, const float *src, float *dst, const mesh_t *mesh)\n{\n\tfloat *table;\n\tfloat t1, t2;\n\tfloat cost, sint;\n\tint j;\n\tswitch (tcmod->type)\n\t{\n\t\tcase SHADER_TCMOD_ROTATE:\n\t\t\tcost = tcmod->args[0] * shaderstate.curtime;\n\t\t\tsint = R_FastSin(cost);\n\t\t\tcost = R_FastSin(cost + 0.25);\n\n\t\t\tfor (j = 0; j < cnt; j++, dst+=2,src+=2)\n\t\t\t{\n\t\t\t\tt1 = cost * (src[0] - 0.5f) - sint * (src[1] - 0.5f) + 0.5f;\n\t\t\t\tt2 = cost * (src[1] - 0.5f) + sint * (src[0] - 0.5f) + 0.5f;\n\t\t\t\tdst[0] = t1;\n\t\t\t\tdst[1] = t2;\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase SHADER_TCMOD_SCALE:\n\t\t\tt1 = tcmod->args[0];\n\t\t\tt2 = tcmod->args[1];\n\n\t\t\tfor (j = 0; j < cnt; j++, dst+=2,src+=2)\n\t\t\t{\n\t\t\t\tdst[0] = src[0] * t1;\n\t\t\t\tdst[1] = src[1] * t2;\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase SHADER_TCMOD_TURB:\n\t\t\tt1 = tcmod->args[2] + shaderstate.curtime * tcmod->args[3];\n\t\t\tt2 = tcmod->args[1];\n\n\t\t\tfor (j = 0; j < cnt; j++, dst+=2,src+=2)\n\t\t\t{\n\t\t\t\tdst[0] = src[0] + R_FastSin (src[0]*t2+t1) * t2;\n\t\t\t\tdst[1] = src[1] + R_FastSin (src[1]*t2+t1) * t2;\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase SHADER_TCMOD_STRETCH:\n\t\t\ttable = FTableForFunc(tcmod->args[0]);\n\t\t\tt2 = tcmod->args[3] + shaderstate.curtime * tcmod->args[4];\n\t\t\tt1 = FTABLE_EVALUATE(table, t2) * tcmod->args[2] + tcmod->args[1];\n\t\t\tt1 = t1 ? 1.0f / t1 : 1.0f;\n\t\t\tt2 = 0.5f - 0.5f * t1;\n\t\t\tfor (j = 0; j < cnt; j++, dst+=2,src+=2)\n\t\t\t{\n\t\t\t\tdst[0] = src[0] * t1 + t2;\n\t\t\t\tdst[1] = src[1] * t1 + t2;\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase SHADER_TCMOD_SCROLL:\n\t\t\tt1 = tcmod->args[0] * shaderstate.curtime;\n\t\t\tt2 = tcmod->args[1] * shaderstate.curtime;\n\n\t\t\tfor (j = 0; j < cnt; j++, dst += 2, src+=2)\n\t\t\t{\n\t\t\t\tdst[0] = src[0] + t1;\n\t\t\t\tdst[1] = src[1] + t2;\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase SHADER_TCMOD_TRANSFORM:\n\t\t\tfor (j = 0; j < cnt; j++, dst+=2, src+=2)\n\t\t\t{\n\t\t\t\tt1 = src[0];\n\t\t\t\tt2 = src[1];\n\t\t\t\tdst[0] = t1 * tcmod->args[0] + t2 * tcmod->args[2] + tcmod->args[4];\n\t\t\t\tdst[1] = t1 * tcmod->args[1] + t2 * tcmod->args[3] + tcmod->args[5];\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase SHADER_TCMOD_PAGE:\n\t\t\t{\t//simple atlas bias with no scaling. use a separate tcmod for that.\n\t\t\t\tint w = tcmod->args[0];\n\t\t\t\tint h = tcmod->args[1];\n\t\t\t\tfloat f = shaderstate.curtime / (tcmod->args[2]*w*h);\n\t\t\t\tint idx = (f - floor(f))*w*h;\n\t\t\t\tt1 = (idx%w)/tcmod->args[0];\n\t\t\t\tt2 = (idx/w)/tcmod->args[1];\n\n\t\t\t\tfor (j = 0; j < cnt; j++, dst += 2, src+=2)\n\t\t\t\t{\n\t\t\t\t\tdst[0] = src[0] + t1;\n\t\t\t\t\tdst[1] = src[1] + t2;\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\n\t\tdefault:\n\t\t\tbreak;\n\t}\n}\n\nstatic void GenerateTCMods3(const shaderpass_t *pass, int passnum)\n{\n#if 1\n\tint m;\n\tfloat *src;\n\tmesh_t *mesh;\n\tfor (m = 0; m < shaderstate.meshcount; m++)\n\t{\n\t\tmesh = shaderstate.meshes[m];\n\n\t\tsrc = tcgen3(pass, mesh->numvertexes, texcoordarray[passnum]+mesh->vbofirstvert*3, mesh);\n\n\t\tif (src != texcoordarray[passnum]+mesh->vbofirstvert*3)\n\t\t{\n\t\t\t//this shouldn't actually ever be true\n\t\t\tmemcpy(texcoordarray[passnum]+mesh->vbofirstvert*3, src, sizeof(vec3_t)*mesh->numvertexes);\n\t\t}\n\t}\n\tshaderstate.pendingtexcoordparts[passnum] = 3;\n\tshaderstate.pendingtexcoordvbo[passnum] = 0;\n\tshaderstate.pendingtexcoordpointer[passnum] = texcoordarray[passnum];\n#else\n\tGL_DeselectVAO();\n\tif (!shaderstate.vbo_texcoords[passnum])\n\t{\n\t\tshaderstate.vbo_texcoords[passnum] = 0;\n\t\tqglGenBuffersARB(1, &shaderstate.vbo_texcoords[passnum]);\n\t}\n\tGL_SelectVBO(shaderstate.vbo_texcoords[passnum]);\n\n\t{\n\t\tqglBufferDataARB(GL_ARRAY_BUFFER_ARB, MAX_ARRAY_VERTS*sizeof(float)*3, NULL, GL_STREAM_DRAW_ARB);\n\t\tfor (; meshlist; meshlist = meshlist->next)\n\t\t{\n\t\t\tint i;\n\t\t\tfloat *src;\n\t\t\tsrc = tcge3n(pass, meshlist->numvertexes, texcoordarray[passnum], meshlist);\n\t\t\tqglBufferSubDataARB(GL_ARRAY_BUFFER_ARB, meshlist->vbofirstvert*8, meshlist->numvertexes*8, src);\n\t\t}\n\t}\n\n\tshaderstate.pendingtexcoordparts[passnum] = 2;\n\tshaderstate.pendingtexcoordvbo[passnum] = shaderstate.vbo_texcoords[passnum];\n\tshaderstate.pendingtexcoordpointer[passnum] = NULL;\n#endif\n}\n\nstatic void GenerateTCMods(const shaderpass_t *pass, int passnum)\n{\n#if 1\n\tint i, m;\n\tfloat *src;\n\tmesh_t *mesh;\n\tfor (m = 0; m < shaderstate.meshcount; m++)\n\t{\n\t\tmesh = shaderstate.meshes[m];\n\n\t\tsrc = tcgen(pass, mesh->numvertexes, texcoordarray[passnum]+mesh->vbofirstvert*2, mesh);\n\t\t//tcgen might return unmodified info\n\t\tif (!src)\n\t\t{\t//don't crash... not much else we can do.\n\t\t\tshaderstate.pendingtexcoordparts[passnum] = 2;\n\t\t\tshaderstate.pendingtexcoordvbo[passnum] = shaderstate.sourcevbo->texcoord.gl.vbo;\n\t\t\tshaderstate.pendingtexcoordpointer[passnum] = shaderstate.sourcevbo->texcoord.gl.addr;\n\t\t}\n\t\telse if (pass->numtcmods)\n\t\t{\n\t\t\ttcmod(&pass->tcmods[0], mesh->numvertexes, src, texcoordarray[passnum]+mesh->vbofirstvert*2, mesh);\n\t\t\tfor (i = 1; i < pass->numtcmods; i++)\n\t\t\t{\n\t\t\t\ttcmod(&pass->tcmods[i], mesh->numvertexes, texcoordarray[passnum]+mesh->vbofirstvert*2, texcoordarray[passnum]+mesh->vbofirstvert*2, mesh);\n\t\t\t}\n\t\t\tsrc = texcoordarray[passnum]+mesh->vbofirstvert*2;\n\t\t}\n\t\telse if (src != texcoordarray[passnum]+mesh->vbofirstvert*2)\n\t\t{\n\t\t\t//this shouldn't actually ever be true\n\t\t\tmemcpy(texcoordarray[passnum]+mesh->vbofirstvert*2, src, 8*mesh->numvertexes);\n\t\t}\n\t}\n\tshaderstate.pendingtexcoordparts[passnum] = 2;\n\tshaderstate.pendingtexcoordvbo[passnum] = 0;\n\tshaderstate.pendingtexcoordpointer[passnum] = texcoordarray[passnum];\n#else\n\tGL_DeselectVAO();\n\tif (!shaderstate.vbo_texcoords[passnum])\n\t{\n\t\tshaderstate.vbo_texcoords[passnum] = 0;\n\t\tqglGenBuffersARB(1, &shaderstate.vbo_texcoords[passnum]);\n\t}\n\tGL_SelectVBO(shaderstate.vbo_texcoords[passnum]);\n\n\t{\n\t\tqglBufferDataARB(GL_ARRAY_BUFFER_ARB, MAX_ARRAY_VERTS*sizeof(float)*2, NULL, GL_STREAM_DRAW_ARB);\n\t\tfor (; meshlist; meshlist = meshlist->next)\n\t\t{\n\t\t\tint i;\n\t\t\tfloat *src;\n\t\t\tsrc = tcgen(pass, meshlist->numvertexes, texcoordarray[passnum], meshlist);\n\t\t\t//tcgen might return unmodified info\n\t\t\tif (pass->numtcmods)\n\t\t\t{\n\t\t\t\ttcmod(&pass->tcmods[0], meshlist->numvertexes, src, texcoordarray[passnum], meshlist);\n\t\t\t\tfor (i = 1; i < pass->numtcmods; i++)\n\t\t\t\t{\n\t\t\t\t\ttcmod(&pass->tcmods[i], meshlist->numvertexes, texcoordarray[passnum], texcoordarray[passnum], meshlist);\n\t\t\t\t}\n\t\t\t\tsrc = texcoordarray[passnum];\n\t\t\t}\n\t\t\tqglBufferSubDataARB(GL_ARRAY_BUFFER_ARB, meshlist->vbofirstvert*8, meshlist->numvertexes*8, src);\n\t\t}\n\t}\n\n\tshaderstate.pendingtexcoordparts[passnum] = 2;\n\tshaderstate.pendingtexcoordvbo[passnum] = shaderstate.vbo_texcoords[passnum];\n\tshaderstate.pendingtexcoordpointer[passnum] = NULL;\n#endif\n}\n\n//end texture coords\n/*========================================== colour generation =====================================*/\n\n//source is always packed\n//dest is packed too\nstatic void colourgen(const shaderpass_t *pass, int cnt, vec4_t *src, vec4_t *dst, const mesh_t *mesh)\n{\n\tswitch (pass->rgbgen)\n\t{\n\tcase RGB_GEN_ENTITY:\n\t\twhile((cnt)--)\n\t\t{\n\t\t\tdst[cnt][0] = shaderstate.curentity->shaderRGBAf[0];\n\t\t\tdst[cnt][1] = shaderstate.curentity->shaderRGBAf[1];\n\t\t\tdst[cnt][2] = shaderstate.curentity->shaderRGBAf[2];\n\t\t}\n\t\tbreak;\n\tcase RGB_GEN_ONE_MINUS_ENTITY:\n\t\twhile((cnt)--)\n\t\t{\n\t\t\tdst[cnt][0] = 1-shaderstate.curentity->shaderRGBAf[0];\n\t\t\tdst[cnt][1] = 1-shaderstate.curentity->shaderRGBAf[1];\n\t\t\tdst[cnt][2] = 1-shaderstate.curentity->shaderRGBAf[2];\n\t\t}\n\t\tbreak;\n\tcase RGB_GEN_VERTEX_LIGHTING:\n#if MAXRLIGHTMAPS > 1\n\t\tif (mesh->colors4f_array[1])\n\t\t{\n\t\t\tfloat lm[MAXRLIGHTMAPS];\n\t\t\tlm[0] = d_lightstylevalue[shaderstate.curbatch->vtlightstyle[0]]/256.0f*shaderstate.identitylighting;\n\t\t\tlm[1] = d_lightstylevalue[shaderstate.curbatch->vtlightstyle[1]]/256.0f*shaderstate.identitylighting;\n\t\t\tlm[2] = d_lightstylevalue[shaderstate.curbatch->vtlightstyle[2]]/256.0f*shaderstate.identitylighting;\n\t\t\tlm[3] = d_lightstylevalue[shaderstate.curbatch->vtlightstyle[3]]/256.0f*shaderstate.identitylighting;\n\t\t\twhile((cnt)--)\n\t\t\t{\n\t\t\t\tVectorScale(\t\tmesh->colors4f_array[0][cnt], lm[0], dst[cnt]);\n\t\t\t\tVectorMA(dst[cnt],\tlm[1], mesh->colors4f_array[1][cnt], dst[cnt]);\n\t\t\t\tVectorMA(dst[cnt],\tlm[2], mesh->colors4f_array[2][cnt], dst[cnt]);\n\t\t\t\tVectorMA(dst[cnt],\tlm[3], mesh->colors4f_array[3][cnt], dst[cnt]);\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n#endif\n\n\t\tif (shaderstate.identitylighting != 1)\n\t\t{\n\t\t\tif (!src)\n\t\t\t{\n\t\t\t\twhile((cnt)--)\n\t\t\t\t{\n\t\t\t\t\tdst[cnt][0] = shaderstate.identitylighting;\n\t\t\t\t\tdst[cnt][1] = shaderstate.identitylighting;\n\t\t\t\t\tdst[cnt][2] = shaderstate.identitylighting;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\twhile((cnt)--)\n\t\t\t{\n\t\t\t\tdst[cnt][0] = src[cnt][0]*shaderstate.identitylighting;\n\t\t\t\tdst[cnt][1] = src[cnt][1]*shaderstate.identitylighting;\n\t\t\t\tdst[cnt][2] = src[cnt][2]*shaderstate.identitylighting;\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\tcase RGB_GEN_VERTEX_EXACT:\n\t\tif (!src)\n\t\t{\n\t\t\twhile((cnt)--)\n\t\t\t{\n\t\t\t\tdst[cnt][0] = 1;\n\t\t\t\tdst[cnt][1] = 1;\n\t\t\t\tdst[cnt][2] = 1;\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\n\t\twhile((cnt)--)\n\t\t{\n\t\t\tdst[cnt][0] = src[cnt][0];\n\t\t\tdst[cnt][1] = src[cnt][1];\n\t\t\tdst[cnt][2] = src[cnt][2];\n\t\t}\n\t\tbreak;\n\tcase RGB_GEN_ONE_MINUS_VERTEX:\n\t\twhile((cnt)--)\n\t\t{\n\t\t\tdst[cnt][0] = 1-src[cnt][0];\n\t\t\tdst[cnt][1] = 1-src[cnt][1];\n\t\t\tdst[cnt][2] = 1-src[cnt][2];\n\t\t}\n\t\tbreak;\n\tcase RGB_GEN_IDENTITY_LIGHTING:\n\t\tif (shaderstate.curbatch->vtlightstyle[0] != 255 && d_lightstylevalue[shaderstate.curbatch->vtlightstyle[0]] != 256)\n\t\t{\n\t\t\t//FIXME: \n\t\t\twhile((cnt)--)\n\t\t\t{\n\t\t\t\tdst[cnt][0] = shaderstate.identitylighting * d_lightstylevalue[shaderstate.curbatch->vtlightstyle[0]]/256.0f;\n\t\t\t\tdst[cnt][1] = shaderstate.identitylighting * d_lightstylevalue[shaderstate.curbatch->vtlightstyle[0]]/256.0f;\n\t\t\t\tdst[cnt][2] = shaderstate.identitylighting * d_lightstylevalue[shaderstate.curbatch->vtlightstyle[0]]/256.0f;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\t//compensate for overbrights\n\t\t\twhile((cnt)--)\n\t\t\t{\n\t\t\t\tdst[cnt][0] = shaderstate.identitylighting;\n\t\t\t\tdst[cnt][1] = shaderstate.identitylighting;\n\t\t\t\tdst[cnt][2] = shaderstate.identitylighting;\n\t\t\t}\n\t\t}\n\t\tbreak;\n\tcase RGB_GEN_IDENTITY_OVERBRIGHT:\n\t\twhile((cnt)--)\n\t\t{\n\t\t\tdst[cnt][0] = shaderstate.identitylightmap;\n\t\t\tdst[cnt][1] = shaderstate.identitylightmap;\n\t\t\tdst[cnt][2] = shaderstate.identitylightmap;\n\t\t}\n\t\tbreak;\n\tdefault:\n\tcase RGB_GEN_IDENTITY:\n\t\twhile((cnt)--)\n\t\t{\n\t\t\tdst[cnt][0] = shaderstate.identitylighting;\n\t\t\tdst[cnt][1] = shaderstate.identitylighting;\n\t\t\tdst[cnt][2] = shaderstate.identitylighting;\n\t\t}\n\t\tbreak;\n\tcase RGB_GEN_CONST:\n\t\twhile((cnt)--)\n\t\t{\n\t\t\tdst[cnt][0] = pass->rgbgen_func.args[0];\n\t\t\tdst[cnt][1] = pass->rgbgen_func.args[1];\n\t\t\tdst[cnt][2] = pass->rgbgen_func.args[2];\n\t\t}\n\t\tbreak;\n\tcase RGB_GEN_ENTITY_LIGHTING_DIFFUSE:\n\t\tR_LightArrays(shaderstate.curentity, mesh->xyz_array, dst, cnt, mesh->normals_array, shaderstate.identitylighting, true);\n\t\tbreak;\n\tcase RGB_GEN_LIGHTING_DIFFUSE:\n\t\tR_LightArrays(shaderstate.curentity, mesh->xyz_array, dst, cnt, mesh->normals_array, shaderstate.identitylighting, false);\n\t\tbreak;\n\tcase RGB_GEN_WAVE:\n\t\t{\n\t\t\tfloat *table;\n\t\t\tfloat c;\n\n\t\t\ttable = FTableForFunc(pass->rgbgen_func.type);\n\t\t\tc = pass->rgbgen_func.args[2] + shaderstate.curtime * pass->rgbgen_func.args[3];\n\t\t\tc = FTABLE_EVALUATE(table, c) * pass->rgbgen_func.args[1] + pass->rgbgen_func.args[0];\n\t\t\tc = bound(0.0f, c, 1.0f);\n\n\t\t\twhile((cnt)--)\n\t\t\t{\n\t\t\t\tdst[cnt][0] = c;\n\t\t\t\tdst[cnt][1] = c;\n\t\t\t\tdst[cnt][2] = c;\n\t\t\t}\n\t\t}\n\t\tbreak;\n\n\tcase RGB_GEN_TOPCOLOR:\n\t\tif (cnt)\n\t\t{\n\t\t\tR_FetchPlayerColour(shaderstate.curentity->topcolour, dst[0]);\n\t\t\twhile((cnt)--)\n\t\t\t{\n\t\t\t\tdst[cnt][0] = dst[0][0];\n\t\t\t\tdst[cnt][1] = dst[0][1];\n\t\t\t\tdst[cnt][2] = dst[0][2];\n\t\t\t}\n\t\t}\n\t\tbreak;\n\tcase RGB_GEN_BOTTOMCOLOR:\n\t\tif (cnt)\n\t\t{\n\t\t\tR_FetchPlayerColour(shaderstate.curentity->bottomcolour, dst[0]);\n\t\t\twhile((cnt)--)\n\t\t\t{\n\t\t\t\tdst[cnt][0] = dst[0][0];\n\t\t\t\tdst[cnt][1] = dst[0][1];\n\t\t\t\tdst[cnt][2] = dst[0][2];\n\t\t\t}\n\t\t}\n\t\tbreak;\n\t}\n}\n#endif\n\nstatic void BE_GenTempMeshVBO(vbo_t **vbo, mesh_t *m);\nstatic void DeformGen_Text(int stringid, int cnt, vecV_t *src, vecV_t *dst, const mesh_t *mesh)\n{\n#define maxlen 32\n\tvecV_t *textverts = vertexarray;\n\tstatic vec2_t texttc[maxlen*4];\n\textern index_t\tr_quad_indexes[];\n\tstatic mesh_t textmesh, *meshptr = &textmesh;\n\tint i;\n\tvec3_t org;\n\tvec3_t right;\n\tvec3_t down;\n\tfloat s, t, d;\n\tchar cvarname[64];\n\tconst char *text;\n\tQ_snprintfz(cvarname, sizeof(cvarname), \"r_shadertext_%i\", stringid);\n\ttext = Cvar_Get(cvarname, \"\", 0, \"Shader System\")->string;\n\tVectorCopy(mesh->snormals_array[0], right);\n\tVectorNegate(mesh->tnormals_array[0], down);\n\tCrossProduct(right, down, org);\n\tif (DotProduct(mesh->normals_array[0], org) > 0)\n\t\tVectorNegate(right, right);\n\tVectorClear(org);\n\tfor (i = 0; i < cnt; i++)\n\t\tVectorAdd(org, src[i], org);\n\tVectorScale(org, 1.0/i, org);\n\tfor (i = 0, s = 0; i < 4; i++)\n\t{\n\t\td = DotProduct(right, src[i]) - DotProduct(right, org);\n\t\tif (s < d)\n\t\t\ts = d;\n\t}\n\ti = strlen(text);\n\tVectorScale(right, 2*s/i, right);\n\tVectorScale(down, 2*s/i, down);\n\tVectorMA(org, -i*0.5, right, org);\n\n\tmemset(&textmesh, 0, sizeof(textmesh));\n\ttextmesh.indexes = r_quad_indexes;\n\ttextmesh.xyz_array = textverts;\n\ttextmesh.st_array = texttc;\n\n\torg[1] += 0;\n\n\tfor (i = 0; i < maxlen; )\n\t{\n\t\tqbyte c = *text++;\n\t\tif (!c)\n\t\t\tbreak;\n\t\tif (c != ' ')\n\t\t{\n\t\t\tconst float sz = 1 / 16.0f;\n\t\t\ts = (c&15)*sz;\n\t\t\tt = (c>>4)*sz;\n\t\t\tVectorCopy(org, textverts[i*4+0]);\n\t\t\tVector2Set(texttc[i*4+0], s, t);\n\n\t\t\tVectorAdd(textverts[i*4+0], right, textverts[i*4+1]);\n\t\t\tVector2Set(texttc[i*4+1], s+sz, t);\n\n\t\t\tVectorAdd(textverts[i*4+1], down, textverts[i*4+2]);\n\t\t\tVector2Set(texttc[i*4+2], s+sz, t+sz);\n\n\t\t\tVectorAdd(textverts[i*4+0], down, textverts[i*4+3]);\n\t\t\tVector2Set(texttc[i*4+3], s, t+sz);\n\t\t\ti++;\n\t\t}\n\t\tVectorAdd(org, right, org);\n\t}\n\ttextmesh.numindexes = i*6;\n\ttextmesh.numvertexes = i*4;\n\n\tBE_GenTempMeshVBO(&shaderstate.sourcevbo, &textmesh);\n\n\tshaderstate.meshcount = 1;\n\tshaderstate.meshes = &meshptr;\n#undef maxlen\n}\nstatic void deformgen(const deformv_t *deformv, int cnt, vecV_t *src, vecV_t *dst, const mesh_t *mesh)\n{\n\tfloat *table;\n\tint j, k;\n\tfloat args[4];\n\tfloat deflect;\n\tswitch (deformv->type)\n\t{\n\tdefault:\n\tcase DEFORMV_NONE:\n\t\tif (src != dst)\n\t\t\tmemcpy(dst, src, sizeof(*src)*cnt);\n\t\tbreak;\n\n\tcase DEFORMV_WAVE:\n\t\tif (!mesh->normals_array)\n\t\t{\n\t\t\tif (src != dst)\n\t\t\t\tmemcpy(dst, src, sizeof(*src)*cnt);\n\t\t\treturn;\n\t\t}\n\t\targs[0] = deformv->func.args[0];\n\t\targs[1] = deformv->func.args[1];\n\t\targs[3] = deformv->func.args[2] + deformv->func.args[3] * shaderstate.curtime;\n\t\ttable = FTableForFunc(deformv->func.type);\n\n\t\tfor ( j = 0; j < cnt; j++ )\n\t\t{\n\t\t\tdeflect = deformv->args[0] * (src[j][0]+src[j][1]+src[j][2]) + args[3];\n\t\t\tdeflect = FTABLE_EVALUATE(table, deflect) * args[1] + args[0];\n\n\t\t\t// Deflect vertex along its normal by wave amount\n\t\t\tVectorMA(src[j], deflect, mesh->normals_array[j], dst[j]);\n\t\t}\n\t\tbreak;\n\n\tcase DEFORMV_NORMAL:\n\t\t//normal does not actually move the verts, but it does change the normals array\n\t\t//we don't currently support that.\n\t\tif (src != dst)\n\t\t\tmemcpy(dst, src, sizeof(*src)*cnt);\n/*\n\t\targs[0] = deformv->args[1] * shaderstate.curtime;\n\n\t\tfor ( j = 0; j < cnt; j++ )\n\t\t{\n\t\t\targs[1] = normalsArray[j][2] * args[0];\n\n\t\t\tdeflect = deformv->args[0] * R_FastSin(args[1]);\n\t\t\tnormalsArray[j][0] *= deflect;\n\t\t\tdeflect = deformv->args[0] * R_FastSin(args[1] + 0.25);\n\t\t\tnormalsArray[j][1] *= deflect;\n\t\t\tVectorNormalizeFast(normalsArray[j]);\n\t\t}\n*/\t\tbreak;\n\n\tcase DEFORMV_MOVE:\n\t\ttable = FTableForFunc(deformv->func.type);\n\t\tdeflect = deformv->func.args[2] + shaderstate.curtime * deformv->func.args[3];\n\t\tdeflect = FTABLE_EVALUATE(table, deflect) * deformv->func.args[1] + deformv->func.args[0];\n\n\t\tfor ( j = 0; j < cnt; j++ )\n\t\t\tVectorMA(src[j], deflect, deformv->args, dst[j]);\n\t\tbreak;\n\n\tcase DEFORMV_BULGE:\n\t\tif (!mesh->normals_array)\n\t\t{\n\t\t\tif (src != dst)\n\t\t\t\tmemcpy(dst, src, sizeof(*src)*cnt);\n\t\t\tbreak;\n\t\t}\n\t\targs[0] = deformv->args[0]/(2*M_PI);\n\t\targs[1] = deformv->args[1];\n\t\targs[2] = shaderstate.curtime * deformv->args[2]/(2*M_PI);\n\n\t\tfor (j = 0; j < cnt; j++)\n\t\t{\n\t\t\tdeflect = R_FastSin(mesh->st_array[j][0]*args[0] + args[2])*args[1];\n\t\t\tdst[j][0] = src[j][0]+deflect*mesh->normals_array[j][0];\n\t\t\tdst[j][1] = src[j][1]+deflect*mesh->normals_array[j][1];\n\t\t\tdst[j][2] = src[j][2]+deflect*mesh->normals_array[j][2];\n\t\t}\n\t\tbreak;\n\n\tcase DEFORMV_AUTOSPRITE:\n\t\tif (mesh->numindexes < 6)\n\t\t\tbreak;\n\n\t\tfor (j = 0; j+3 < cnt; j+=4, src+=4, dst+=4)\n\t\t{\n\t\t\tvec3_t mid, d;\n\t\t\tvec2_t mid2;\n\t\t\tfloat radius, s,t;\n\t\t\tvec2_t *fte_restrict st = &mesh->st_array[j];\n\t\t\tmid[0] = 0.25*(src[0][0] + src[1][0] + src[2][0] + src[3][0]);\n\t\t\tmid[1] = 0.25*(src[0][1] + src[1][1] + src[2][1] + src[3][1]);\n\t\t\tmid[2] = 0.25*(src[0][2] + src[1][2] + src[2][2] + src[3][2]);\n\t\t\tVectorSubtract(src[0], mid, d);\n\t\t\tradius = VectorLength(d) * 0.707;\n\n\t\t\tmid2[0] = 0.25*(st[0][0] + st[1][0] + st[2][0] + st[3][0]);\n\t\t\tmid2[1] = 0.25*(st[0][1] + st[1][1] + st[2][1] + st[3][1]);\n\n\t\t\tfor (k = 0; k < 4; k++)\n\t\t\t{\n\t\t\t\t//q3 fully regenerates verts. we don't because that destroys ST coords.\n\t\t\t\t//even so, if the ST coords are non-centered for some reason then we still need to get the right values as if they were.\n\t\t\t\t//hence the mid2.\n\t\t\t\ts = (st[k][0] > mid2[0])?1:-1;\n\t\t\t\tt = (st[k][1] > mid2[1])?1:-1;\n\t\t\t\tdst[k][0] = mid[0] + radius*(s*shaderstate.modelviewmatrix[0+0]-t*shaderstate.modelviewmatrix[0+1]);\n\t\t\t\tdst[k][1] = mid[1] + radius*(s*shaderstate.modelviewmatrix[4+0]-t*shaderstate.modelviewmatrix[4+1]);\n\t\t\t\tdst[k][2] = mid[2] + radius*(s*shaderstate.modelviewmatrix[8+0]-t*shaderstate.modelviewmatrix[8+1]);\n\t\t\t}\n\t\t}\n\t\tbreak;\n\n\tcase DEFORMV_AUTOSPRITE2:\n\t\tif (mesh->numindexes < 6)\n\t\t\tbreak;\n\n\t\tfor (k = 0; k < mesh->numindexes; k += 6)\n\t\t{\n\t\t\tint long_axis, short_axis;\n\t\t\tvec3_t axis;\n\t\t\tfloat len[3];\n\t\t\tmat3_t m0, m1, m2, result;\n\t\t\tfloat *quad[4];\n\t\t\tvec3_t rot_centre, tv, tv2;\n\n\t\t\tquad[0] = (float *)(src + mesh->indexes[k+0]);\n\t\t\tquad[1] = (float *)(src + mesh->indexes[k+1]);\n\t\t\tquad[2] = (float *)(src + mesh->indexes[k+2]);\n\n\t\t\tfor (j = 2; j >= 0; j--)\n\t\t\t{\n\t\t\t\tquad[3] = (float *)(src + mesh->indexes[k+3+j]);\n\t\t\t\tif (!VectorEquals (quad[3], quad[0]) &&\n\t\t\t\t\t!VectorEquals (quad[3], quad[1]) &&\n\t\t\t\t\t!VectorEquals (quad[3], quad[2]))\n\t\t\t\t{\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// build a matrix were the longest axis of the billboard is the Y-Axis\n\t\t\tVectorSubtract(quad[1], quad[0], m0[0]);\n\t\t\tVectorSubtract(quad[2], quad[0], m0[1]);\n\t\t\tVectorSubtract(quad[2], quad[1], m0[2]);\n\t\t\tlen[0] = DotProduct(m0[0], m0[0]);\n\t\t\tlen[1] = DotProduct(m0[1], m0[1]);\n\t\t\tlen[2] = DotProduct(m0[2], m0[2]);\n\n\t\t\tif ((len[2] > len[1]) && (len[2] > len[0]))\n\t\t\t{\n\t\t\t\tif (len[1] > len[0])\n\t\t\t\t{\n\t\t\t\t\tlong_axis = 1;\n\t\t\t\t\tshort_axis = 0;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tlong_axis = 0;\n\t\t\t\t\tshort_axis = 1;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if ((len[1] > len[2]) && (len[1] > len[0]))\n\t\t\t{\n\t\t\t\tif (len[2] > len[0])\n\t\t\t\t{\n\t\t\t\t\tlong_axis = 2;\n\t\t\t\t\tshort_axis = 0;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tlong_axis = 0;\n\t\t\t\t\tshort_axis = 2;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse //if ( (len[0] > len[1]) && (len[0] > len[2]) )\n\t\t\t{\n\t\t\t\tif (len[2] > len[1])\n\t\t\t\t{\n\t\t\t\t\tlong_axis = 2;\n\t\t\t\t\tshort_axis = 1;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tlong_axis = 1;\n\t\t\t\t\tshort_axis = 2;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (DotProduct(m0[long_axis], m0[short_axis]))\n\t\t\t{\n\t\t\t\tVectorNormalize2(m0[long_axis], axis);\n\t\t\t\tVectorCopy(axis, m0[1]);\n\n\t\t\t\tif (axis[0] || axis[1])\n\t\t\t\t{\n\t\t\t\t\tVectorVectors(m0[1], m0[2], m0[0]);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tVectorVectors(m0[1], m0[0], m0[2]);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tVectorNormalize2(m0[long_axis], axis);\n\t\t\t\tVectorNormalize2(m0[short_axis], m0[0]);\n\t\t\t\tVectorCopy(axis, m0[1]);\n\t\t\t\tCrossProduct(m0[0], m0[1], m0[2]);\n\t\t\t}\n\n\t\t\tfor (j = 0; j < 3; j++)\n\t\t\t\trot_centre[j] = (quad[0][j] + quad[1][j] + quad[2][j] + quad[3][j]) * 0.25;\n\n\t\t\tif (shaderstate.curentity)\n\t\t\t{\n\t\t\t\tVectorAdd(shaderstate.curentity->origin, rot_centre, tv);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tVectorCopy(rot_centre, tv);\n\t\t\t}\n\t\t\tVectorSubtract(r_origin, tv, tv);\n\n\t\t\t// filter any longest-axis-parts off the camera-direction\n\t\t\tdeflect = -DotProduct(tv, axis);\n\n\t\t\tVectorMA(tv, deflect, axis, m1[2]);\n\t\t\tVectorNormalizeFast(m1[2]);\n\t\t\tVectorCopy(axis, m1[1]);\n\t\t\tCrossProduct(m1[1], m1[2], m1[0]);\n\n\t\t\tMatrix3_Transpose(m1, m2);\n\t\t\tMatrix3_Multiply(m2, m0, result);\n\n\t\t\tfor (j = 0; j < 4; j++)\n\t\t\t{\n\t\t\t\tint v = ((vecV_t*)quad[j]-src);\n\t\t\t\tVectorSubtract(quad[j], rot_centre, tv);\n\t\t\t\tMatrix3_Multiply_Vec3((const vec3_t*)result, tv, tv2);\n\t\t\t\tVectorAdd(rot_centre, tv2, dst[v]);\n\t\t\t}\n\t\t}\n\t\tbreak;\n\n//\tcase DEFORMV_PROJECTION_SHADOW:\n//\t\tbreak;\n\n\tcase DEFORMV_TEXT:\n\t\tDeformGen_Text(deformv->args[0], cnt, src, dst, mesh);\n\t\tbreak;\n\t}\n}\n\nstatic void GenerateVertexBlends(const shader_t *shader)\n{\n\tint i, m;\n\tmesh_t *meshlist;\n\tvecV_t *ov, *iv1, *iv2;\n\tfloat w1, w2;\n\tfor (m = 0; m < shaderstate.meshcount; m++)\n\t{\n\t\tmeshlist = shaderstate.meshes[m];\n\n\t\tov = vertexarray+meshlist->vbofirstvert;\n\t\tiv1 = meshlist->xyz_array;\n\t\tiv2 = meshlist->xyz2_array;\n\t\tw1 = meshlist->xyz_blendw[0];\n\t\tw2 = meshlist->xyz_blendw[1];\n\t\tfor (i = 0; i < meshlist->numvertexes; i++)\n\t\t{\n\t\t\tov[i][0] = iv1[i][0]*w1 + iv2[i][0]*w2;\n\t\t\tov[i][1] = iv1[i][1]*w1 + iv2[i][1]*w2;\n\t\t\tov[i][2] = iv1[i][2]*w1 + iv2[i][2]*w2;\n\t\t}\n\t\tfor (i = 0; i < shader->numdeforms; i++)\n\t\t{\n\t\t\tdeformgen(&shader->deforms[i], meshlist->numvertexes, vertexarray+meshlist->vbofirstvert, vertexarray+meshlist->vbofirstvert, meshlist);\n\t\t}\n\t}\n\n\tshaderstate.pendingvertexpointer = vertexarray;\n\tshaderstate.pendingvertexvbo = 0;\n}\nstatic void GenerateVertexDeforms(const shader_t *shader)\n{\n\tint i, m;\n\tmesh_t *meshlist;\n\tfor (m = 0; m < shaderstate.meshcount; m++)\n\t{\n\t\tmeshlist = shaderstate.meshes[m];\n\n\t\tdeformgen(&shader->deforms[0], meshlist->numvertexes, meshlist->xyz_array, vertexarray+meshlist->vbofirstvert, meshlist);\n\t\tfor (i = 1; i < shader->numdeforms; i++)\n\t\t{\n\t\t\tdeformgen(&shader->deforms[i], meshlist->numvertexes, vertexarray+meshlist->vbofirstvert, vertexarray+meshlist->vbofirstvert, meshlist);\n\t\t}\n\t}\n\n\tshaderstate.pendingvertexpointer = vertexarray;\n\tshaderstate.pendingvertexvbo = 0;\n}\n\n#if 1//ndef GLSLONLY\n\n/*======================================alpha ===============================*/\n\nstatic void alphagen(const shaderpass_t *pass, int cnt, avec4_t *const src, avec4_t *dst, const mesh_t *mesh)\n{\n\tfloat *table;\n\tfloat t;\n\tfloat f;\n\tvec3_t v1, v2;\n\tint i;\n\n\tswitch (pass->alphagen)\n\t{\n\tdefault:\n\tcase ALPHA_GEN_IDENTITY:\n\t\tif (shaderstate.flags & BEF_FORCETRANSPARENT)\n\t\t{\n\t\t\twhile(cnt--)\n\t\t\t\tdst[cnt][3] = shaderstate.curentity->shaderRGBAf[3];\n\t\t}\n\t\telse\n\t\t{\n\t\t\twhile(cnt--)\n\t\t\t\tdst[cnt][3] = 1;\n\t\t}\n\t\tbreak;\n\n\tcase ALPHA_GEN_CONST:\n\t\tt = pass->alphagen_func.args[0];\n\t\twhile(cnt--)\n\t\t\tdst[cnt][3] = t;\n\t\tbreak;\n\n\tcase ALPHA_GEN_WAVE:\n\t\ttable = FTableForFunc(pass->alphagen_func.type);\n\t\tf = pass->alphagen_func.args[2] + shaderstate.curtime * pass->alphagen_func.args[3];\n\t\tf = FTABLE_EVALUATE(table, f) * pass->alphagen_func.args[1] + pass->alphagen_func.args[0];\n\t\tt = bound(0.0f, f, 1.0f);\n\t\twhile(cnt--)\n\t\t\tdst[cnt][3] = t;\n\t\tbreak;\n\n\tcase ALPHA_GEN_PORTAL:\n\t\t//FIXME: should this be per-vert?\n\t\tif (r_refdef.recurse || !mesh->xyz_array)\n\t\t\tf = 1;\n\t\telse\n\t\t{\n\t\t\tVectorAdd(mesh->xyz_array[0], shaderstate.curentity->origin, v1);\n\t\t\tVectorSubtract(r_origin, v1, v2);\n\t\t\tf = VectorLength(v2) * (1.0 / shaderstate.curshader->portaldist);\n\t\t\tf = bound(0.0f, f, 1.0f);\n\t\t}\n\n\t\twhile(cnt--)\n\t\t\tdst[cnt][3] = f;\n\t\tbreak;\n\n\tcase ALPHA_GEN_VERTEX:\n\t\tif (!src)\n\t\t{\n\t\t\twhile(cnt--)\n\t\t\t{\n\t\t\t\tdst[cnt][3] = 1;\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\n\t\twhile(cnt--)\n\t\t{\n\t\t\tdst[cnt][3] = src[cnt][3];\n\t\t}\n\t\tbreak;\n\n\tcase ALPHA_GEN_ENTITY:\n\t\tf = bound(0, shaderstate.curentity->shaderRGBAf[3], 1);\n\t\twhile(cnt--)\n\t\t{\n\t\t\tdst[cnt][3] = f;\n\t\t}\n\t\tbreak;\n\n\n\tcase ALPHA_GEN_SPECULAR:\n\t\tif (!mesh->normals_array)\n\t\t{\n\t\t\twhile(cnt--)\n\t\t\t\tdst[cnt][3] = 0.2;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tVectorSubtract(r_origin, shaderstate.curentity->origin, v1);\n\n\t\t\tif (!Matrix3_Compare((const vec3_t*)shaderstate.curentity->axis, (const vec3_t*)axisDefault))\n\t\t\t{\n\t\t\t\tMatrix3_Multiply_Vec3(shaderstate.curentity->axis, v1, v2);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tVectorCopy(v1, v2);\n\t\t\t}\n\n\t\t\tfor (i = 0; i < cnt; i++)\n\t\t\t{\n\t\t\t\tVectorSubtract(v2, mesh->xyz_array[i], v1);\n\t\t\t\tf = DotProduct(v1, mesh->normals_array[i] ) * Q_rsqrt(DotProduct(v1,v1));\n\t\t\t\tf = f * f * f * f * f;\n\t\t\t\tdst[i][3] = bound (0.0f, f, 1.0f);\n\t\t\t}\n\t\t}\n\t\tbreak;\n\t}\n}\n\nstatic void GenerateColourMods(const shaderpass_t *pass)\n{\n\tunsigned int m;\n\tmesh_t *meshlist;\n\tmeshlist = shaderstate.meshes[0];\n\n\tif (pass->flags & SHADER_PASS_NOCOLORARRAY)\n\t{\n\t\tcolourgen(pass, 1, meshlist->colors4f_array[0], &shaderstate.pendingcolourflat, meshlist);\n\t\talphagen(pass, 1, meshlist->colors4f_array[0], &shaderstate.pendingcolourflat, meshlist);\n\t\tshaderstate.colourarraytype = 0;\n\t\tshaderstate.pendingcolourvbo = 0;\n\t\tshaderstate.pendingcolourpointer = NULL;\n\t}\n\telse\n\t{\n\t\textern cvar_t r_nolightdir;\n\t\tif (pass->rgbgen == RGB_GEN_LIGHTING_DIFFUSE || pass->rgbgen == RGB_GEN_ENTITY_LIGHTING_DIFFUSE)\n\t\t{\n\t\t\tif (shaderstate.mode == BEM_DEPTHDARK || shaderstate.mode == BEM_DEPTHONLY)\n\t\t\t{\n\t\t\t\tshaderstate.pendingcolourflat[0] = shaderstate.pendingcolourflat[1] = shaderstate.pendingcolourflat[2] = 0;\n\t\t\t\talphagen(pass, 1, meshlist->colors4f_array[0], &shaderstate.pendingcolourflat, meshlist);\n\t\t\t\tshaderstate.colourarraytype = 0;\n\t\t\t\tshaderstate.pendingcolourvbo = 0;\n\t\t\t\tshaderstate.pendingcolourpointer = NULL;\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (shaderstate.mode == BEM_LIGHT)\n\t\t\t{\n\t\t\t\tshaderstate.pendingcolourflat[0] = shaderstate.pendingcolourflat[1] = shaderstate.pendingcolourflat[2] = 1;\n\t\t\t\talphagen(pass, 1, meshlist->colors4f_array[0], &shaderstate.pendingcolourflat, meshlist);\n\t\t\t\tshaderstate.colourarraytype = 0;\n\t\t\t\tshaderstate.pendingcolourvbo = 0;\n\t\t\t\tshaderstate.pendingcolourpointer = NULL;\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (r_nolightdir.ival || (!shaderstate.curentity->light_range[0] && !shaderstate.curentity->light_range[1] && !shaderstate.curentity->light_range[2]))\n\t\t\t{\n\t\t\t\tVectorCopy(shaderstate.curentity->light_avg, shaderstate.pendingcolourflat);\n\t\t\t\talphagen(pass, 1, meshlist->colors4f_array[0], &shaderstate.pendingcolourflat, meshlist);\n\t\t\t\tshaderstate.colourarraytype = 0;\n\t\t\t\tshaderstate.pendingcolourvbo = 0;\n\t\t\t\tshaderstate.pendingcolourpointer = NULL;\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\t//if its vetex lighting, just use the vbo\n\t\tif (((pass->rgbgen == RGB_GEN_VERTEX_LIGHTING && shaderstate.identitylighting == 1) || pass->rgbgen == RGB_GEN_VERTEX_EXACT) && pass->alphagen == ALPHA_GEN_VERTEX)\n\t\t{\n\t\t\tshaderstate.colourarraytype = shaderstate.sourcevbo->colours_bytes?GL_UNSIGNED_BYTE:GL_FLOAT;\n\t\t\tshaderstate.pendingcolourvbo = shaderstate.sourcevbo->colours[0].gl.vbo;\n\t\t\tshaderstate.pendingcolourpointer = shaderstate.sourcevbo->colours[0].gl.addr;\n\t\t\treturn;\n\t\t}\n\n\t\tfor (m = 0; m < shaderstate.meshcount; m++)\n\t\t{\n\t\t\tmeshlist = shaderstate.meshes[m];\n\n\t\t\tcolourgen(pass, meshlist->numvertexes, meshlist->colors4f_array[0], coloursarray + meshlist->vbofirstvert, meshlist);\n\t\t\talphagen(pass, meshlist->numvertexes, meshlist->colors4f_array[0], coloursarray + meshlist->vbofirstvert, meshlist);\n\t\t}\n\n\t\tshaderstate.colourarraytype = GL_FLOAT;\n\t\tshaderstate.pendingcolourvbo = 0;\n\t\tshaderstate.pendingcolourpointer = coloursarray;\n\t}\n}\n\nstatic void BE_GeneratePassTC(const shaderpass_t *pass, int tmu)\n{\n\tif (!pass->numtcmods)\n\t{\n\t\t//if there are no tcmods, pass through here as fast as possible\n\t\tswitch(pass->tcgen)\n\t\t{\n\t\tcase TC_GEN_BASE:\n\t\t\tshaderstate.pendingtexcoordparts[tmu] = 2;\n\t\t\tshaderstate.pendingtexcoordvbo[tmu] = shaderstate.sourcevbo->texcoord.gl.vbo;\n\t\t\tshaderstate.pendingtexcoordpointer[tmu] = shaderstate.sourcevbo->texcoord.gl.addr;\n\t\t\tbreak;\n\t\tcase TC_GEN_LIGHTMAP:\n\t\t\tif (!shaderstate.sourcevbo->lmcoord[0].gl.addr)\n\t\t\t{\n\t\t\t\tshaderstate.pendingtexcoordparts[tmu] = 2;\n\t\t\t\tshaderstate.pendingtexcoordvbo[tmu] = shaderstate.sourcevbo->texcoord.gl.vbo;\n\t\t\t\tshaderstate.pendingtexcoordpointer[tmu] = shaderstate.sourcevbo->texcoord.gl.addr;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tshaderstate.pendingtexcoordparts[tmu] = 2;\n\t\t\t\tshaderstate.pendingtexcoordvbo[tmu] = shaderstate.sourcevbo->lmcoord[0].gl.vbo;\n\t\t\t\tshaderstate.pendingtexcoordpointer[tmu] = shaderstate.sourcevbo->lmcoord[0].gl.addr;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase TC_GEN_NORMAL:\n\t\t\tshaderstate.pendingtexcoordparts[tmu] = 3;\n\t\t\tshaderstate.pendingtexcoordvbo[tmu] = shaderstate.sourcevbo->normals.gl.vbo;\n\t\t\tshaderstate.pendingtexcoordpointer[tmu] = shaderstate.sourcevbo->normals.gl.addr;\n\t\t\tbreak;\n\t\tcase TC_GEN_SVECTOR:\n\t\t\tshaderstate.pendingtexcoordparts[tmu] = 3;\n\t\t\tshaderstate.pendingtexcoordvbo[tmu] = shaderstate.sourcevbo->svector.gl.vbo;\n\t\t\tshaderstate.pendingtexcoordpointer[tmu] = shaderstate.sourcevbo->svector.gl.addr;\n\t\t\tbreak;\n\t\tcase TC_GEN_TVECTOR:\n\t\t\tshaderstate.pendingtexcoordparts[tmu] = 3;\n\t\t\tshaderstate.pendingtexcoordvbo[tmu] = shaderstate.sourcevbo->tvector.gl.vbo;\n\t\t\tshaderstate.pendingtexcoordpointer[tmu] = shaderstate.sourcevbo->tvector.gl.addr;\n\t\t\tbreak;\n\t\tcase TC_GEN_SKYBOX:\n\t\t\tGenerateTCMods3(pass, tmu);\n\t\t\tbreak;\n\t\t\t//position - viewpos\n//\tcase TC_GEN_WOBBLESKY:\n//\tcase TC_GEN_REFLECT:\n\t\tdefault:\n\t\t\t//specular highlights and reflections have no fixed data, and must be generated.\n\t\t\tGenerateTCMods(pass, tmu);\n\t\t\tbreak;\n\t\t}\n\t}\n\telse\n\t{\n\t\tGenerateTCMods(pass, tmu);\n\t}\n}\n#endif\n\nstatic void BE_SendPassBlendDepthMask(unsigned int sbits)\n{\n\tunsigned int delta;\n\n\t/*2d mode doesn't depth test or depth write*/\n\tif (shaderstate.force2d)\n\t{\n#ifdef warningmsg\n#pragma warningmsg(\"fixme: q3 doesn't seem to have this, why do we need it?\")\n#endif\n\t\tsbits &= ~(SBITS_MISC_DEPTHWRITE|SBITS_DEPTHFUNC_BITS);\n\t\tsbits |= SBITS_MISC_NODEPTHTEST;\n\t}\n\tif (shaderstate.flags & (BEF_FORCEADDITIVE|BEF_FORCETRANSPARENT|BEF_FORCENODEPTH|BEF_FORCEDEPTHTEST|BEF_FORCEDEPTHWRITE))\n\t{\n\t\tif (shaderstate.flags & BEF_FORCEADDITIVE)\n\t\t\tsbits = (sbits & ~(SBITS_MISC_DEPTHWRITE|SBITS_BLEND_BITS|SBITS_ATEST_BITS))\n\t\t\t\t\t\t| (SBITS_SRCBLEND_SRC_ALPHA | SBITS_DSTBLEND_ONE);\n\t\telse if (shaderstate.flags & BEF_FORCETRANSPARENT)\n\t\t{\n\t\t\tif ((sbits & SBITS_BLEND_BITS) == (SBITS_SRCBLEND_ONE| SBITS_DSTBLEND_ZERO) || !(sbits & SBITS_BLEND_BITS) || (sbits & SBITS_ATEST_GE128)) \t/*if transparency is forced, clear alpha test bits*/\n\t\t\t\tsbits = (sbits & ~(SBITS_BLEND_BITS|SBITS_ATEST_BITS))\n\t\t\t\t\t\t\t| (SBITS_SRCBLEND_SRC_ALPHA | SBITS_DSTBLEND_ONE_MINUS_SRC_ALPHA | SBITS_ATEST_GT0);\n\t\t}\n\n\t\tif (shaderstate.flags & BEF_FORCENODEPTH) \t/*EF_NODEPTHTEST dp extension*/\n\t\t\tsbits |= SBITS_MISC_NODEPTHTEST;\n\t\telse\n\t\t{\n\t\t\tif (shaderstate.flags & BEF_FORCEDEPTHTEST)\n\t\t\t\tsbits &= ~SBITS_MISC_NODEPTHTEST;\n\t\t\tif (shaderstate.flags & BEF_FORCEDEPTHWRITE)\n\t\t\t\tsbits |= SBITS_MISC_DEPTHWRITE;\n\t\t}\n\t}\n\tsbits |= r_refdef.colourmask;\n\n\n\tdelta = sbits^shaderstate.shaderbits;\n\n#ifdef FORCESTATE\n\tdelta |= ~0;\n#endif\n\tif (!delta)\n\t\treturn;\n\tshaderstate.shaderbits = sbits;\n\tif (delta & SBITS_BLEND_BITS)\n\t{\n\t\tif (sbits & SBITS_BLEND_BITS)\n\t\t{\n\t\t\tint src, dst;\n\t\t\t/*unpack the src and dst factors*/\n\t\t\tswitch(sbits & SBITS_SRCBLEND_BITS)\n\t\t\t{\n\t\t\tcase SBITS_SRCBLEND_ZERO:\t\t\t\t\tsrc = GL_ZERO;\tbreak;\n\t\t\tdefault:\n\t\t\tcase SBITS_SRCBLEND_ONE:\t\t\t\t\tsrc = GL_ONE;\tbreak;\n\t\t\tcase SBITS_SRCBLEND_DST_COLOR:\t\t\t\tsrc = GL_DST_COLOR;\tbreak;\n\t\t\tcase SBITS_SRCBLEND_ONE_MINUS_DST_COLOR:\tsrc = GL_ONE_MINUS_DST_COLOR;\tbreak;\n\t\t\tcase SBITS_SRCBLEND_SRC_ALPHA:\t\t\t\tsrc = GL_SRC_ALPHA;\tbreak;\n\t\t\tcase SBITS_SRCBLEND_ONE_MINUS_SRC_ALPHA:\tsrc = GL_ONE_MINUS_SRC_ALPHA;\tbreak;\n\t\t\tcase SBITS_SRCBLEND_DST_ALPHA:\t\t\t\tsrc = GL_DST_ALPHA;\tbreak;\n\t\t\tcase SBITS_SRCBLEND_ONE_MINUS_DST_ALPHA:\tsrc = GL_ONE_MINUS_DST_ALPHA;\tbreak;\n\t\t\tcase SBITS_SRCBLEND_ALPHA_SATURATE:\t\t\tsrc = GL_SRC_ALPHA_SATURATE;\tbreak;\n\t\t\t}\n\t\t\tswitch((sbits & SBITS_DSTBLEND_BITS)>>4)\n\t\t\t{\n\t\t\tcase SBITS_DSTBLEND_ZERO>>4:\t\t\t\tdst = GL_ZERO;\tbreak;\n\t\t\tdefault:\n\t\t\tcase SBITS_DSTBLEND_ONE>>4:\t\t\t\t\tdst = GL_ONE;\tbreak;\n\t\t\tcase SBITS_DSTBLEND_SRC_COLOR>>4:\t\t\tdst = GL_SRC_COLOR;\tbreak;\n\t\t\tcase SBITS_DSTBLEND_ONE_MINUS_SRC_COLOR>>4:\tdst = GL_ONE_MINUS_SRC_COLOR;\tbreak;\n\t\t\tcase SBITS_DSTBLEND_SRC_ALPHA>>4:\t\t\tdst = GL_SRC_ALPHA;\tbreak;\n\t\t\tcase SBITS_DSTBLEND_ONE_MINUS_SRC_ALPHA>>4:\tdst = GL_ONE_MINUS_SRC_ALPHA;\tbreak;\n\t\t\tcase SBITS_DSTBLEND_DST_ALPHA>>4:\t\t\tdst = GL_DST_ALPHA;\tbreak;\n\t\t\tcase SBITS_DSTBLEND_ONE_MINUS_DST_ALPHA>>4:\tdst = GL_ONE_MINUS_DST_ALPHA;\tbreak;\n\t\t\t}\n\t\t\tqglEnable(GL_BLEND);\n\t\t\tqglBlendFunc(src, dst);\n\t\t}\n\t\telse\n\t\t\tqglDisable(GL_BLEND);\n\t}\n\n#ifdef GL_ALPHA_TEST\t//alpha test doesn't exist in gles2\n\tif ((delta & SBITS_ATEST_BITS) && !gl_config_nofixedfunc)\n\t{\n\t\tswitch (sbits & SBITS_ATEST_BITS)\n\t\t{\n\t\tdefault:\n\t\t\tqglDisable(GL_ALPHA_TEST);\n\t\t\tbreak;\n\t\tcase SBITS_ATEST_GT0:\n\t\t\tqglEnable(GL_ALPHA_TEST);\n\t\t\tqglAlphaFunc(GL_GREATER, 0);\n\t\t\tbreak;\n\t\tcase SBITS_ATEST_LT128:\n\t\t\tqglEnable(GL_ALPHA_TEST);\n\t\t\tqglAlphaFunc(GL_LESS, 0.5f);\n\t\t\tbreak;\n\t\tcase SBITS_ATEST_GE128:\n\t\t\tqglEnable(GL_ALPHA_TEST);\n\t\t\tqglAlphaFunc(GL_GEQUAL, 0.5f);\n\t\t\tbreak;\n\t\t}\n\t}\n#endif\n\n\tif (delta & SBITS_MISC_NODEPTHTEST)\n\t{\n\t\tif (sbits & SBITS_MISC_NODEPTHTEST)\n\t\t\tqglDisable(GL_DEPTH_TEST);\n\t\telse\n\t\t\tqglEnable(GL_DEPTH_TEST);\n\t}\n\tif (delta & SBITS_MISC_DEPTHWRITE)\n\t{\n\t\tif (sbits & SBITS_MISC_DEPTHWRITE)\n\t\t\tqglDepthMask(GL_TRUE);\n\t\telse\n\t\t\tqglDepthMask(GL_FALSE);\n\t}\n\tif (delta & (SBITS_DEPTHFUNC_BITS))\n\t{\n\t\textern int gldepthfunc;\n\t\tswitch (sbits & SBITS_DEPTHFUNC_BITS)\n\t\t{\n\t\tcase SBITS_DEPTHFUNC_EQUAL:\n\t\t\tqglDepthFunc(GL_EQUAL);\n\t\t\tbreak;\n\t\tcase SBITS_DEPTHFUNC_FURTHER:\n\t\t\tif (gldepthfunc == GL_LEQUAL)\n\t\t\t\tqglDepthFunc(GL_GREATER);\n\t\t\telse\n\t\t\t\tqglDepthFunc(GL_LESS);\n\t\t\tbreak;\n\t\tcase SBITS_DEPTHFUNC_CLOSER:\n\t\t\tif (gldepthfunc == GL_LEQUAL)\n\t\t\t\tqglDepthFunc(GL_LESS);\n\t\t\telse\n\t\t\t\tqglDepthFunc(GL_GREATER);\n\t\t\tbreak;\n\t\tdefault:\n\t\tcase SBITS_DEPTHFUNC_CLOSEREQUAL:\n\t\t\tqglDepthFunc(gldepthfunc);\n\t\t\tbreak;\n\t\t}\n\t}\n\tif (delta & (SBITS_MASK_BITS))\n\t{\n\t\tqglColorMask(\n\t\t\t\t(sbits&SBITS_MASK_RED)?GL_FALSE:GL_TRUE,\n\t\t\t\t(sbits&SBITS_MASK_GREEN)?GL_FALSE:GL_TRUE,\n\t\t\t\t(sbits&SBITS_MASK_BLUE)?GL_FALSE:GL_TRUE,\n\t\t\t\t(sbits&SBITS_MASK_ALPHA)?GL_FALSE:GL_TRUE\n\t\t\t\t);\n\t}\n\tif ((delta & SBITS_TESSELLATION) && qglPNTrianglesiATI)\n\t{\n\t\tif ((sbits & SBITS_TESSELLATION) && r_tessellation.ival)\n\t\t\tqglEnable(GL_PN_TRIANGLES_ATI);\n\t\telse\n\t\t\tqglDisable(GL_PN_TRIANGLES_ATI);\n\t}\n\n#ifdef GL_PERSPECTIVE_CORRECTION_HINT\n\tif ((delta & SBITS_AFFINE) && qglHint)\n\t{\n\t\tif (!qglHint || (gl_config_gles && gl_config.glversion >= 2) || (!gl_config_gles && gl_config_nofixedfunc))\n\t\t\t;\t//doesn't exist in gles2 nor gl3+core contexts\n\t\telse if (sbits & SBITS_AFFINE)\n\t\t\tqglHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST);\n\t\telse\n\t\t\tqglHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);\n\t}\n#endif\n}\n\nstatic void BE_SubmitMeshChain(qboolean usetesselation)\n{\n\tint startv, starti, endv, endi;\n\tint m;\n\tmesh_t *mesh;\n\tint batchtype;\n\n\tif (usetesselation)\n\t{\n\t\tm = (shaderstate.flags & BEF_LINES)?2:3;\n\t\tif (shaderstate.curpatchverts != m)\n\t\t{\n\t\t\tshaderstate.curpatchverts = m;\n\t\t\tqglPatchParameteriARB(GL_PATCH_VERTICES_ARB, m);\n\t\t}\n\t\tbatchtype = GL_PATCHES_ARB;\n\t}\n\telse\n\t\tbatchtype = (shaderstate.flags & BEF_LINES)?GL_LINES:GL_TRIANGLES;\n\n\tif (!shaderstate.streamvbo[0])\t//only if we're not forcing vbos elsewhere.\n\t{\n\t\t//q3map2 sucks. it splits static meshes randomly rather than with any pvs consistancy, and then splays them out over 1000 different surfaces.\n\t\t//this means we end up needing a boatload of draw calls whenever the batch got split.\n\t\t//so skip all that and splurge out a usable index list on demand.\n\t\tif (shaderstate.meshcount == 1)\n\t\t{\n\t\t\tGL_SelectEBO(shaderstate.sourcevbo->indicies.gl.vbo);\n\t\t\tmesh = shaderstate.meshes[0];\n\t\t\tqglDrawRangeElements(batchtype, mesh->vbofirstvert, mesh->vbofirstvert+mesh->numvertexes - 1, mesh->numindexes, GL_INDEX_TYPE, (index_t*)shaderstate.sourcevbo->indicies.gl.addr + mesh->vbofirstelement);\n\t\t\tRQuantAdd(RQUANT_DRAWS, 1);\n\t\t\tDRAWCALL(\"BE_SubmitMeshChain 1\");\n\t\t\tRQuantAdd(RQUANT_PRIMITIVEINDICIES, mesh->numindexes);\n\t\t\treturn;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tindex_t *fte_restrict ilst; //FIXME: this should be cached for multiple-pass shaders.\n\t\t\tGL_SelectEBO(0);\n\n\t\t\t//FIXME: use a coherant persistently mapped buffer.\n\n\t\t\tmesh = shaderstate.meshes[0];\n\t\t\tstartv = mesh->vbofirstvert;\n\t\t\tendv = startv + mesh->numvertexes;\n\t\t\tendi = mesh->numindexes;\n\t\t\tfor (m = 1; m < shaderstate.meshcount; m++)\n\t\t\t{\n\t\t\t\tmesh = shaderstate.meshes[m];\n\t\t\t\tendi += mesh->numindexes;\n\n\t\t\t\tif (startv > mesh->vbofirstvert)\n\t\t\t\t\tstartv = mesh->vbofirstvert;\n\t\t\t\tif (endv < mesh->vbofirstvert+mesh->numvertexes)\n\t\t\t\t\tendv = mesh->vbofirstvert+mesh->numvertexes;\n\t\t\t}\n\n\t\t\tilst = alloca(endi*sizeof(index_t));\n\t\t\tendi = 0;\n\t\t\tfor (m = 0; m < shaderstate.meshcount; m++)\n\t\t\t{\n\t\t\t\tmesh = shaderstate.meshes[m];\n\t\t\t\tfor (starti = 0; starti < mesh->numindexes; )\n\t\t\t\t\tilst[endi++] = mesh->vbofirstvert + mesh->indexes[starti++];\n\t\t\t}\n\t\t\tqglDrawRangeElements(batchtype, startv, endv - 1, endi, GL_INDEX_TYPE, ilst);\n\t\t\tRQuantAdd(RQUANT_DRAWS, 1);\n\t\t\tDRAWCALL(\"BE_SubmitMeshChain N\");\n\t\t\tRQuantAdd(RQUANT_PRIMITIVEINDICIES, endi);\n\t\t}\n\t\treturn;\n\t}\n\telse if (qglMultiDrawElements)\n\t{\t//if we're drawing via a VBO then we don't really need DrawRangeElements any more.\n\t\t//and avoiding so many calls into the driver also gives the driver a chance to optimise the draws instead of constantly checking if anything changed.\n\t\tstatic GLsizei counts[1024];\n\t\tstatic const GLvoid *indicies[countof(counts)];\n\t\tGLsizei drawcount = 0;\n\t\tGL_SelectEBO(shaderstate.sourcevbo->indicies.gl.vbo);\n\n\t\tfor (m = 0, mesh = shaderstate.meshes[0]; m < shaderstate.meshcount; )\n\t\t{\n\t\t\tstartv = mesh->vbofirstvert;\n\t\t\tstarti = mesh->vbofirstelement;\n\n\t\t\tendv = startv+mesh->numvertexes;\n\t\t\tendi = starti+mesh->numindexes;\n\n\t\t\t//find consecutive surfaces\n\t\t\tfor (++m; m < shaderstate.meshcount; m++)\n\t\t\t{\n\t\t\t\tmesh = shaderstate.meshes[m];\n\t\t\t\tif (endi == mesh->vbofirstelement)\n\t\t\t\t{\n\t\t\t\t\tendv = mesh->vbofirstvert+mesh->numvertexes;\n\t\t\t\t\tendi = mesh->vbofirstelement+mesh->numindexes;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (drawcount == countof(counts))\n\t\t\t{\n\t\t\t\tqglMultiDrawElements(batchtype, counts, GL_INDEX_TYPE, indicies, drawcount);\n\t\t\t\tRQuantAdd(RQUANT_DRAWS, drawcount);\n\t\t\t\tDRAWCALL(\"BE_SubmitMeshChain MultiDraw\");\n\t\t\t\tdrawcount = 0;\n\t\t\t}\n\t\t\tcounts[drawcount] = endi-starti;\n\t\t\tindicies[drawcount] = (index_t*)shaderstate.sourcevbo->indicies.gl.addr + starti;\n\t\t\tdrawcount++;\n\t\t\tRQuantAdd(RQUANT_PRIMITIVEINDICIES, endi-starti);\n\t\t}\n\t\tqglMultiDrawElements(batchtype, counts, GL_INDEX_TYPE, indicies, drawcount);\n\t\tRQuantAdd(RQUANT_DRAWS, drawcount);\n\t\tDRAWCALL(\"BE_SubmitMeshChain MultiDraw\");\n\t}\n#if 0 //def FTE_TARGET_WEB\n\telse if (shaderstate.meshcount > 1)\n\t{\t//FIXME: not really needed if index lists are consecutive\n\t\tindex_t *tmp;\n\t\tint ebo;\n\n\t\tfor (endi = 0, m = 0; m < shaderstate.meshcount; m++)\n\t\t{\n\t\t\tmesh = shaderstate.meshes[m];\n\t\t\tendi += mesh->numindexes;\n\t\t}\n\t\ttmp = alloca(endi * sizeof(*tmp));\n\t\tfor (endi = 0, m = 0; m < shaderstate.meshcount; m++)\n\t\t{\n\t\t\tmesh = shaderstate.meshes[m];\n\t\t\tfor (starti = 0; starti < mesh->numindexes; starti++)\n\t\t\t\ttmp[endi++] = mesh->vbofirstvert + mesh->indexes[starti];\n\t\t}\n\n\t\tshaderstate.streamid = (shaderstate.streamid + 1) & (sizeof(shaderstate.streamvbo)/sizeof(shaderstate.streamvbo[0]) - 1);\n\t\tebo = shaderstate.streamebo[shaderstate.streamid];\n\t\tGL_SelectEBO(ebo);\n\t\tqglBufferDataARB(GL_ELEMENT_ARRAY_BUFFER_ARB, sizeof(*tmp) * endi, tmp, GL_STREAM_DRAW_ARB);\n\t\tqglDrawElements(batchtype, endi, GL_INDEX_TYPE, NULL);\n\t\tRQuantAdd(RQUANT_DRAWS, 1);\n\t\tRQuantAdd(RQUANT_PRIMITIVEINDICIES, endi);\n\t}\n#endif\n\telse\n\t{\n\t\tGL_SelectEBO(shaderstate.sourcevbo->indicies.gl.vbo);\n\n/*\n\t\tif (qglLockArraysEXT)\n\t\t{\n\t\t\tendv = 0;\n\t\t\tstartv = 0x7fffffff;\n\t\t\tfor (m = 0; m < shaderstate.meshcount; m++)\n\t\t\t{\n\t\t\t\tmesh = shaderstate.meshes[m];\n\t\t\t\tstarti = mesh->vbofirstvert;\n\t\t\t\tif (starti < startv)\n\t\t\t\t\tstartv = starti;\n\t\t\t\tendi = mesh->vbofirstvert+mesh->numvertexes;\n\t\t\t\tif (endi > endv)\n\t\t\t\t\tendv = endi;\n\t\t\t}\n\t\t\tqglLockArraysEXT(startv, endv);\n\t\t}\n*/\n\n\t\tfor (m = 0, mesh = shaderstate.meshes[0]; m < shaderstate.meshcount; )\n\t\t{\n\t\t\tstartv = mesh->vbofirstvert;\n\t\t\tstarti = mesh->vbofirstelement;\n\n\t\t\tendv = startv+mesh->numvertexes;\n\t\t\tendi = starti+mesh->numindexes;\n\n\t\t\t//find consecutive surfaces\n\t\t\tfor (++m; m < shaderstate.meshcount; m++)\n\t\t\t{\n\t\t\t\tmesh = shaderstate.meshes[m];\n\t\t\t\tif (endi == mesh->vbofirstelement)\n\t\t\t\t{\n\t\t\t\t\tendv = mesh->vbofirstvert+mesh->numvertexes;\n\t\t\t\t\tendi = mesh->vbofirstelement+mesh->numindexes;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tqglDrawRangeElements(batchtype, startv, endv - 1, endi-starti, GL_INDEX_TYPE, (index_t*)shaderstate.sourcevbo->indicies.gl.addr + starti);\n\t\t\tRQuantAdd(RQUANT_DRAWS, 1);\n\t\t\tDRAWCALL(\"BE_SubmitMeshChain Fallback\");\n\t\t\tRQuantAdd(RQUANT_PRIMITIVEINDICIES, endi-starti);\n \t\t}\n/*\n\t\tif (qglUnlockArraysEXT)\n\t\t\tqglUnlockArraysEXT();\n*/\n\t}\n}\n\n#ifndef GLSLONLY\nstatic void DrawPass(const shaderpass_t *pass)\n{\n\tint i;\n#if MAXRLIGHTMAPS > 1\n\tint j, k;\n#endif\n\tint tmu;\n\tint lastpass = pass->numMergedPasses;\n\tunsigned int attr = (1u<<VATTR_LEG_VERTEX) | (1u<<VATTR_LEG_COLOUR);\n\n\tfor (i = 0; i < lastpass; i++)\n\t{\n\t\tif (pass[i].texgen == T_GEN_UPPEROVERLAY && !TEXLOADED(shaderstate.curtexnums->upperoverlay))\n\t\t\tcontinue;\n\t\tif (pass[i].texgen == T_GEN_LOWEROVERLAY && !TEXLOADED(shaderstate.curtexnums->loweroverlay))\n\t\t\tcontinue;\n\t\tif (pass[i].texgen == T_GEN_FULLBRIGHT && !TEXLOADED(shaderstate.curtexnums->fullbright))\n\t\t\tcontinue;\n\t\tbreak;\n\t}\n\tif (i == lastpass)\n\t\treturn;\n\tBE_SendPassBlendDepthMask(pass[i].shaderbits);\n\tGenerateColourMods(pass+i);\n\ttmu = 0;\n\tfor (; i < lastpass; i++)\n\t{\n\t\tif (pass[i].texgen == T_GEN_UPPEROVERLAY && !TEXLOADED(shaderstate.curtexnums->upperoverlay))\n\t\t\tcontinue;\n\t\tif (pass[i].texgen == T_GEN_LOWEROVERLAY && !TEXLOADED(shaderstate.curtexnums->loweroverlay))\n\t\t\tcontinue;\n\t\tif (pass[i].texgen == T_GEN_FULLBRIGHT && !TEXLOADED(shaderstate.curtexnums->fullbright))\n\t\t\tcontinue;\n\t\tShader_BindTextureForPass(tmu, pass+i);\n\t\tattr |= (1u<<(VATTR_LEG_TMU0+tmu));\n\n\t\tBE_GeneratePassTC(pass+i, tmu);\n\n\t\tBE_SetPassBlendMode(tmu, pass[i].blendmode);\n\n\t\ttmu++;\n\n\t\t//add in \n\t\tif (pass[i].texgen == T_GEN_LIGHTMAP)\n\t\t{\n\t\t\t//first pass should have been REPLACE\n\t\t\t//second pass should be an ADD\n\t\t\t//this depends upon rgbgens for light levels, so each pass *must* be pushed to hardware individually\n\n#if MAXRLIGHTMAPS > 1\n\t\t\tfor (j = 1; j < MAXRLIGHTMAPS && shaderstate.curbatch->lightmap[j] >= 0; j++)\n\t\t\t{\n\t\t\t\tif (j == 1)\n\t\t\t\t\tBE_SetPassBlendMode(tmu, PBM_REPLACE);\n\n\t\t\t\t/*make sure no textures linger*/\n\t\t\t\tfor (k = tmu; k < shaderstate.lastpasstmus; k++)\n\t\t\t\t{\n\t\t\t\t\tGL_LazyBind(k, r_nulltex);\n\t\t\t\t}\n\t\t\t\tshaderstate.lastpasstmus = tmu;\n\n\t\t\t\t/*push it*/\n\t\t\t\tBE_EnableShaderAttributes(attr, 0);\n\t\t\t\tBE_SubmitMeshChain(false);\n\t\t\t\ttmu = 0;\n\n\t\t\t\t/*bind the light texture*/\n\t\t\t\tGL_LazyBind(tmu, lightmap[shaderstate.curbatch->lightmap[j]]->lightmap_texture);\n\n\t\t\t\t/*set up the colourmod for this style's lighting*/\n\t\t\t\tshaderstate.pendingcolourvbo = 0;\n\t\t\t\tshaderstate.pendingcolourpointer = NULL;\n\n\t\t\t\tshaderstate.pendingcolourflat[0] = shaderstate.identitylighting * d_lightstylevalue[shaderstate.curbatch->lmlightstyle[j]]/256.0f;\n\t\t\t\tshaderstate.pendingcolourflat[1] = shaderstate.identitylighting * d_lightstylevalue[shaderstate.curbatch->lmlightstyle[j]]/256.0f;\n\t\t\t\tshaderstate.pendingcolourflat[2] = shaderstate.identitylighting * d_lightstylevalue[shaderstate.curbatch->lmlightstyle[j]]/256.0f;\n\t\t\t\tshaderstate.pendingcolourflat[3] = 1;\n\n\t\t\t\t/*pick the correct st coords for this lightmap pass*/\n\t\t\t\tshaderstate.pendingtexcoordparts[tmu] = 2;\n\t\t\t\tshaderstate.pendingtexcoordvbo[tmu] = shaderstate.sourcevbo->lmcoord[j].gl.vbo;\n\t\t\t\tshaderstate.pendingtexcoordpointer[tmu] = shaderstate.sourcevbo->lmcoord[j].gl.addr;\n\n\t\t\t\tBE_SetPassBlendMode(tmu, PBM_ADD);\n\t\t\t\tBE_SendPassBlendDepthMask((pass[0].shaderbits & ~SBITS_BLEND_BITS) | SBITS_SRCBLEND_ONE | SBITS_DSTBLEND_ONE);\n\n\t\t\t\tattr = (1u<<VATTR_LEG_VERTEX) | (1u<<VATTR_LEG_COLOUR);\n\t\t\t\tattr |= (1u<<(VATTR_LEG_TMU0+tmu));\n\n\t\t\t\ttmu++;\n\t\t\t}\n\n\t\t\t//might need to break the pass here\n\t\t\tif (j > 1 && i != lastpass)\n\t\t\t{\n\t\t\t\tfor (k = tmu; k < shaderstate.lastpasstmus; k++)\n\t\t\t\t{\n\t\t\t\t\tGL_LazyBind(k, r_nulltex);\n\t\t\t\t}\n\t\t\t\tshaderstate.lastpasstmus = tmu;\n\t\t\t\tBE_EnableShaderAttributes(attr, 0);\n\n\t\t\t\tBE_SubmitMeshChain(false);\n\t\t\t\ttmu = 0;\n\n\t\t\t\tBE_SendPassBlendDepthMask(pass[i+1].shaderbits);\n\t\t\t\tGenerateColourMods(&pass[i+1]);\n\t\t\t}\n#endif\n\t\t}\n\t}\n\n\tif (!tmu)\n\t\treturn;\n\n\tfor (i = tmu; i < shaderstate.lastpasstmus; i++)\n\t{\n\t\tGL_LazyBind(i, r_nulltex);\n\t}\n\tshaderstate.lastpasstmus = tmu;\n\tBE_EnableShaderAttributes(attr, 0);\n\n\tBE_SubmitMeshChain(false);\n}\n#endif\n\nstatic void BE_Program_Set_Attributes(const program_t *prog, struct programpermu_s *perm, qboolean entunchanged)\n{\n\tvec4_t param4;\n\tint r, g;//, b;\n\tint i;\n\tunsigned int ph;\n\tconst shaderprogparm_t *p;\n\n\tif (perm->factorsuniform != -1)\n\t\tqglUniform4fvARB(perm->factorsuniform, countof(shaderstate.curshader->factors), shaderstate.curshader->factors[0]);\n\n\t/*don't bother setting it if the ent properties are unchanged (but do if the mesh changed)*/\n\tif (entunchanged)\n\t\treturn;\n\n\tp = perm->parm;\n\tfor (i = perm->numparms; i > 0; i--, p++)\n\t{\n\t\tph = p->handle;\n\t\tswitch(p->type)\n\t\t{\n/*\n\t\tcase SP_UBO_ENTITYINFO:\n\t\t\tstruct\n\t\t\t{\n\t\t\t\tvec2_t\t\tblendweights;\n\t\t\t\t\tvec2_t\tpad1;\n\t\t\t\tvec3_t\t\tglowmod;\n\t\t\t\t\tvec_t\tpad2;\n\t\t\t\tvec3_t\t\torigin;\n\t\t\t\t\tvec_t\tpad3;\n\t\t\t\tvec4_t\t\tcolormod;\n\t\t\t\tvec3_t\t\tglowmod;\n\t\t\t\t\tvec_t\tpad4;\n\t\t\t\tvec3_t\t\tuppercolour;\n\t\t\t\t\tvec_t\tpad5;\n\t\t\t\tvec3_t\t\tlowercolour;\n\t\t\t\t\tvec_t\tpad6;\n\t\t\t\tvec3_t\t\tfogcolours;\n\t\t\t\tvec_t\t\tfogalpha;\n\t\t\t\tvec3_t\t\tvlightdir;\n\t\t\t\tvec_t\t\tfogdensity;\n\t\t\t\tvec3_t\t\tvlightmul;\n\t\t\t\tvec_t\t\tfogdepthbias;\n\t\t\t\tvec3_t\t\tvlightadd;\n\t\t\t\tvec_t\t\ttime;\n\t\t\t} u_entityinfo;\n\n\t\t\tVector2Copy(shaderstate.meshes[0]->xyz_blendw, u_entityinfo.blendweights);\n\t\t\tVectorCopy(shaderstate.curentity->glowmod, u_entityinfo.glowmod);\n\t\t\tVectorCopy(shaderstate.curentity->origin, u_entityinfo.origin);\n\t\t\tVector4Copy(shaderstate.curentity->shaderRGBAf, u_entityinfo.colormod);\n\t\t\tR_FetchPlayerColour(shaderstate.curentity->topcolour, u_entityinfo.uppercolour);\n\t\t\tR_FetchPlayerColour(shaderstate.curentity->bottomcolour, u_entityinfo.lowercolour);\n\t\t\tVector3Copy(r_refdef.globalfog.colour, u_entityinfo.fogcolours);\n\t\t\tu_entityinfo.fogalpha = r_refdef.globalfog.alpha;\n\t\t\tu_entityinfo.fogdensity = r_refdef.globalfog.density;\n\t\t\tu_entityinfo.fogdepthbias = r_refdef.globalfog.depthbias;\n\t\t\tu_entityinfo.time = shaderstate.curtime;\n\t\t\tVector3Copy(shaderstate.curentity->light_dir, u_entityinfo.vlightdir);\n\t\t\tVector3Copy(shaderstate.curentity->light_range, u_entityinfo.vlightmul);\n\t\t\tVector3Copy(shaderstate.curentity->light_avg, u_entityinfo.vlightadd);\n\t\t\tbreak;\n*/\n\n/*\n\t\tcase SP_UBO_LIGHTINFO:\n\t\t\tstruct\n\t\t\t{\n\t\t\t\tvec3_t\t\ttoscreen;\n\t\t\t\tvec_t\t\tlightradius;\n\t\t\t\tvec3_t\t\tlightcolours;\n\t\t\t\t\tvec_t\tpad1;\n\t\t\t\tvec3_t\t\tlightcolourscale;\n\t\t\t\t\tvec_t\tpad2;\n\t\t\t\tvec3_t\t\tlightorigin_modelspace;\n\t\t\t\t\tvec_t\tpad3;\n\t\t\t\tmatrix4x4_t\tlightcubematrix;\n\t\t\t\tvec4_t\t\tlightshadowmapproj;\n\t\t\t\tvec2_t\t\tlightshadowmapscale;\n\t\t\t\t\tvec2_t\tpad4;\n\t\t\t} u_lightinfo;\n\t\t\t{\n\t\t\t\tfloat v[4], tempv[4];\n\n\t\t\t\tv[0] = shaderstate.lightorg[0];\n\t\t\t\tv[1] = shaderstate.lightorg[1];\n\t\t\t\tv[2] = shaderstate.lightorg[2];\n\t\t\t\tv[3] = 1;\n\n\t\t\t\tMatrix4x4_CM_Transform4(shaderstate.modelviewmatrix, v, tempv); \n\t\t\t\tMatrix4x4_CM_Transform4(r_refdef.m_projection, tempv, v);\n\n\t\t\t\tv[3] *= 2;\n\t\t\t\tu_lightinfo.toscreen[0] = (v[0]/v[3]) + 0.5;\n\t\t\t\tu_lightinfo.toscreen[1] = (v[1]/v[3]) + 0.5;\n\t\t\t\tu_lightinfo.toscreen[2] = (v[2]/v[3]) + 0.5;\n\t\t\t}\n\t\t\tu_lightinfo.lightradius = shaderstate.lightradius;\n\t\t\tVector3Copy(shaderstate.lightcolours, u_lightinfo.lightcolours);\n\n\t\t\tMatrix4x4_CM_Transform3(shaderstate.modelmatrixinv, shaderstate.lightorg, u_lightinfo.lightorigin_modelspace);\n\t\t\tVector3Copy(shaderstate.lightcolourscale, u_lightinfo.lightcolourscale);\n\t\t\tMatrix4_Multiply(shaderstate.lightprojmatrix, shaderstate.modelmatrix, u_lightinfo.lightcubematrix);\n\t\t\tVector4Copy(shaderstate.lightshadowmapproj, u_lightinfo.lightshadowmapproj);\n\t\t\tVector2Copy(shaderstate.lightshadowmapscale, u_lightinfo.lightshadowmapscale);\n\t\t\tbreak;\n*/\n\t\tcase SP_M_VIEW:\n\t\t\tqglUniformMatrix4fvARB(ph, 1, false, r_refdef.m_view);\n\t\t\tbreak;\n\t\tcase SP_M_PROJECTION:\n\t\t\tqglUniformMatrix4fvARB(ph, 1, false, shaderstate.projectionmatrix);\n\t\t\tbreak;\n\t\tcase SP_M_MODELVIEW:\n\t\t\tqglUniformMatrix4fvARB(ph, 1, false, shaderstate.modelviewmatrix);\n\t\t\tbreak;\n\t\tcase SP_M_MODELVIEWPROJECTION:\n\t\t\t{\n\t\t\t\tfloat m16[16];\n\t\t\t\tMatrix4_Multiply(shaderstate.projectionmatrix, shaderstate.modelviewmatrix, m16);\n\t\t\t\tqglUniformMatrix4fvARB(ph, 1, false, m16);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase SP_M_INVMODELVIEWPROJECTION:\n\t\t\t{\n\t\t\t\tfloat m16[16], inv[16];\n\t\t\t\tMatrix4_Multiply(shaderstate.projectionmatrix, shaderstate.modelviewmatrix, m16);\n\t\t\t\tMatrix4_Invert(m16, inv);\n\t\t\t\tqglUniformMatrix4fvARB(ph, 1, false, inv);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase SP_M_INVMODELVIEW:\n\t\t\t{\n\t\t\t\tfloat inv[9];\n\t\t\t\tMatrix3x4_InvertTo3x3(shaderstate.modelviewmatrix, inv);\n\t\t\t\tqglUniformMatrix3fvARB(ph, 1, false, inv);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase SP_M_MODEL:\n\t\t\tqglUniformMatrix4fvARB(ph, 1, false, shaderstate.modelmatrix);\n\t\t\tbreak;\n\t\tcase SP_M_ENTBONES_PACKED:\n\t\t\tqglUniform4fvARB(ph, shaderstate.sourcevbo->numbones*3, shaderstate.sourcevbo->bones);\n\t\t\tbreak;\n\t\tcase SP_M_ENTBONES_MAT3X4:\n\t\t\tqglUniformMatrix3x4fv(ph, shaderstate.sourcevbo->numbones, false, shaderstate.sourcevbo->bones);\n\t\t\tbreak;\n\t\tcase SP_M_INVVIEWPROJECTION:\n\t\t\t{\n\t\t\t\tfloat m16[16], inv[16];\n\t\t\t\tMatrix4_Multiply(shaderstate.projectionmatrix, r_refdef.m_view, m16);\n\t\t\t\tMatrix4_Invert(m16, inv);\n\t\t\t\tqglUniformMatrix4fvARB(ph, 1, false, inv);\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase SP_E_VBLEND:\n\t\t\tqglUniform2fvARB(ph, 1, shaderstate.meshes[0]->xyz_blendw);\n\t\t\tbreak;\n\n\t\tcase SP_E_VLSCALE:\n#if MAXRLIGHTMAPS > 1\n\t\t\tif (perm->permutation & PERMUTATION_LIGHTSTYLES)\n\t\t\t{\n\t\t\t\tvec4_t colscale[MAXRLIGHTMAPS];\n\t\t\t\tint j, s;\n\t\t\t\tfor (j = 0; j < MAXRLIGHTMAPS ; j++)\n\t\t\t\t{\n\t\t\t\t\ts = shaderstate.curbatch->vtlightstyle[j];\n\t\t\t\t\tif (s == 255)\n\t\t\t\t\t{\n\t\t\t\t\t\tfor (; j < MAXRLIGHTMAPS ; j++)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcolscale[j][0] = 0;\n\t\t\t\t\t\t\tcolscale[j][1] = 0;\n\t\t\t\t\t\t\tcolscale[j][2] = 0;\n\t\t\t\t\t\t\tcolscale[j][3] = 1;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tif (shaderstate.curentity->model && shaderstate.curentity->model->engineflags & MDLF_NEEDOVERBRIGHT)\n\t\t\t\t\t{\n\t\t\t\t\t\tfloat sc = (1<<bound(0, gl_overbright.ival, 2)) * shaderstate.identitylighting;\n\t\t\t\t\t\tVectorSet(colscale[j], sc, sc, sc);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tVectorSet(colscale[j], shaderstate.identitylighting, shaderstate.identitylighting, shaderstate.identitylighting);\n\t\t\t\t\t}\n\t\t\t\t\tcolscale[j][3] = 1;\n\n\t\t\t\t\tVectorScale(colscale[j], d_lightstylevalue[s]/256.0f, colscale[j]);\n\t\t\t\t}\n\n\t\t\t\tqglUniform4fvARB(ph, j, (GLfloat*)colscale);\n\t\t\t\tshaderstate.lastuniform = 0;\n\t\t\t}\n\t\t\telse\n#endif\n\t\t\t{\n\t\t\t\tvec4_t param4;\n\t\t\t\tif (shaderstate.curbatch->flags & BEF_NODLIGHT)\n\t\t\t\t{\n\t\t\t\t\tVector4Set(param4, 1, 1, 1, 1);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tVector4Set(param4, shaderstate.identitylighting, shaderstate.identitylighting, shaderstate.identitylighting, 1);\n\t\t\t\t}\n\t\t\t\tqglUniform4fvARB(ph, 1, (GLfloat*)param4);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase SP_E_LMSCALE:\n#if MAXRLIGHTMAPS > 1\n\t\t\tif (perm->permutation & PERMUTATION_LIGHTSTYLES)\n\t\t\t{\n\t\t\t\tvec4_t colscale[MAXRLIGHTMAPS];\n\t\t\t\tint j, s;\n\t\t\t\tfor (j = 0; j < MAXRLIGHTMAPS ; j++)\n\t\t\t\t{\n\t\t\t\t\ts = shaderstate.curbatch->lmlightstyle[j];\n\t\t\t\t\tif (s == INVALID_LIGHTSTYLE)\n\t\t\t\t\t{\n\t\t\t\t\t\tfor (; j < MAXRLIGHTMAPS ; j++)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcolscale[j][0] = 0;\n\t\t\t\t\t\t\tcolscale[j][1] = 0;\n\t\t\t\t\t\t\tcolscale[j][2] = 0;\n\t\t\t\t\t\t\tcolscale[j][3] = 1;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tif (shaderstate.curentity->model && (shaderstate.curentity->model->engineflags & MDLF_NEEDOVERBRIGHT) && !shaderstate.force2d)\n\t\t\t\t\t{\n\t\t\t\t\t\tfloat sc = (1<<bound(0, gl_overbright.ival, 2)) * shaderstate.identitylighting;\n\t\t\t\t\t\tVectorSet(colscale[j], sc, sc, sc);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tVectorSet(colscale[j], shaderstate.identitylighting, shaderstate.identitylighting, shaderstate.identitylighting);\n\t\t\t\t\t}\n\t\t\t\t\tcolscale[j][3] = 1;\n\n\t\t\t\t\tVectorScale(colscale[j], d_lightstylevalue[s]/256.0f, colscale[j]);\n\t\t\t\t}\n\n\t\t\t\tqglUniform4fvARB(ph, j, (GLfloat*)colscale);\n\t\t\t\tshaderstate.lastuniform = 0;\n\t\t\t}\n\t\t\telse\n#endif\n\t\t\t{\n\t\t\t\tunsigned short s;\n\t\t\t\tif (shaderstate.curentity->model && (shaderstate.curentity->model->engineflags & MDLF_NEEDOVERBRIGHT) && !shaderstate.force2d)\n\t\t\t\t{\n\t\t\t\t\tfloat sc = (1<<bound(0, gl_overbright.ival, 2)) * shaderstate.identitylighting;\n\t\t\t\t\tVector4Set(param4, sc, sc, sc, 1);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tVector4Set(param4, shaderstate.identitylighting, shaderstate.identitylighting, shaderstate.identitylighting, 1);\n\t\t\t\t}\n\n\t\t\t\ts = shaderstate.curbatch->lmlightstyle[0];\t//only one style.\n\t\t\t\tif (s != INVALID_LIGHTSTYLE)\n\t\t\t\t\tVectorScale(param4, d_lightstylevalue[s]/256.0f, param4);\n\n\t\t\t\tqglUniform4fvARB(ph, 1, (GLfloat*)param4);\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase SP_E_GLOWMOD:\n\t\t\tqglUniform3fvARB(ph, 1, (GLfloat*)shaderstate.curentity->glowmod);\n\t\t\tbreak;\n\t\tcase SP_E_ORIGIN:\n\t\t\tqglUniform3fvARB(ph, 1, (GLfloat*)shaderstate.curentity->origin);\n\t\t\tbreak;\n\t\tcase SP_E_COLOURS:\n\t\t\tqglUniform4fvARB(ph, 1, (GLfloat*)shaderstate.curentity->shaderRGBAf);\n\t\t\tbreak;\n\t\tcase SP_S_COLOUR:\n\t\t\tif (shaderstate.colourarraytype)\n\t\t\t\tqglUniform4fARB(ph, 1, 1, 1, 1);\t//invalid use\n\t\t\telse\n\t\t\t\tqglUniform4fvARB(ph, 1, (GLfloat*)shaderstate.pendingcolourflat);\n\t\t\tbreak;\n\t\tcase SP_E_COLOURSIDENT:\n\t\t\tif (shaderstate.flags & BEF_FORCECOLOURMOD)\n\t\t\t\tqglUniform4fvARB(ph, 1, (GLfloat*)shaderstate.curentity->shaderRGBAf);\n\t\t\telse\n\t\t\t\tqglUniform4fARB(ph, 1, 1, 1, shaderstate.curentity->shaderRGBAf[3]);\n\t\t\tbreak;\n\t\tcase SP_E_TOPCOLOURS:\n\t\t\tR_FetchPlayerColour(shaderstate.curentity->topcolour, param4);\n\t\t\tqglUniform3fvARB(ph, 1, param4);\n\t\t\tbreak;\n\t\tcase SP_E_BOTTOMCOLOURS:\n\t\t\tR_FetchPlayerColour(shaderstate.curentity->bottomcolour, param4);\n\t\t\tqglUniform3fvARB(ph, 1, param4);\n\t\t\tbreak;\n\n\t\tcase SP_SOURCESIZE:\n\t\t\tif (shaderstate.tex_sourcecol)\n\t\t\t{\n\t\t\t\tparam4[0] = shaderstate.tex_sourcecol->width;\n\t\t\t\tparam4[1] = shaderstate.tex_sourcecol->height;\n\t\t\t}\n\t\t\telse if (shaderstate.tex_sourcedepth)\n\t\t\t{\n\t\t\t\tparam4[0] = shaderstate.tex_sourcedepth->width;\n\t\t\t\tparam4[1] = shaderstate.tex_sourcedepth->height;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tparam4[0] = 1;\n\t\t\t\tparam4[1] = 1;\n\t\t\t}\n\t\t\tqglUniform2fvARB(ph, 1, param4);\n\t\t\tbreak;\n\t\tcase SP_RENDERTEXTURESCALE:\n\t\t\tif (sh_config.texture_non_power_of_two_pic)\n\t\t\t{\n\t\t\t\tparam4[0] = 1;\n\t\t\t\tparam4[1] = 1;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tr = 1;\n\t\t\t\tg = 1;\n\t\t\t\twhile (r < vid.pixelwidth)\n\t\t\t\t\tparam4[0] *= 2;\n\t\t\t\twhile (g < vid.pixelheight)\n\t\t\t\t\tparam4[1] *= 2;\n\t\t\t\tparam4[0] = vid.pixelwidth/param4[0];\n\t\t\t\tparam4[1] = vid.pixelheight/param4[1];\n\t\t\t}\n\t\t\tparam4[2] = 0;\n\t\t\tparam4[3] = 0;\n\t\t\tqglUniform4fvARB(ph, 1, param4);\n\t\t\tbreak;\n\n\t\tcase SP_LIGHTSCREEN:\n\t\t\t{\n\t\t\t\tfloat v[4], tempv[4];\n\n\t\t\t\tv[0] = shaderstate.lightorg[0];\n\t\t\t\tv[1] = shaderstate.lightorg[1];\n\t\t\t\tv[2] = shaderstate.lightorg[2];\n\t\t\t\tv[3] = 1;\n\n\t\t\t\tMatrix4x4_CM_Transform4(shaderstate.modelviewmatrix, v, tempv); \n\t\t\t\tMatrix4x4_CM_Transform4(shaderstate.projectionmatrix, tempv, v);\n\n\t\t\t\tv[3] *= 2;\n\t\t\t\tv[0] = (v[0]/v[3]) + 0.5;\n\t\t\t\tv[1] = (v[1]/v[3]) + 0.5;\n\t\t\t\tv[2] = (v[2]/v[3]) + 0.5;\n\n\t\t\t\tqglUniform3fvARB(ph, 1, v);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase SP_LIGHTRADIUS:\n\t\t\tqglUniform1fARB(ph, shaderstate.lightradius);\n\t\t\tbreak;\n\t\tcase SP_LIGHTCOLOUR:\n\t\t\tqglUniform3fvARB(ph, 1, shaderstate.lightcolours);\n\t\t\tbreak;\n\t\tcase SP_W_FOG:\n\t\t\tqglUniform4fvARB(ph, 2, r_refdef.globalfog.colour);\t//and density\n\t\t\tbreak;\n\t\tcase SP_W_USER:\n\t\t\tqglUniform4fvARB(ph, countof(r_refdef.userdata), r_refdef.userdata[0]);\t//and density\n\t\t\tbreak;\n\t\tcase SP_V_EYEPOS:\n\t\t\tqglUniform3fvARB(ph, 1, r_origin);\n\t\t\tbreak;\n\t\tcase SP_E_EYEPOS:\n\t\t\t{\n\t\t\t\t/*eye position in model space*/\n\t\t\t\tvec3_t t2;\n\t\t\t\tMatrix4x4_CM_Transform3(shaderstate.modelmatrixinv, r_origin, t2);\n\t\t\t\tqglUniform3fvARB(ph, 1, t2);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase SP_LIGHTPOSITION:\n\t\t\t{\n\t\t\t\t/*light position in model space*/\n\t\t\t\tvec3_t t2;\n\t\t\t\tMatrix4x4_CM_Transform3(shaderstate.modelmatrixinv, shaderstate.lightorg, t2);\n\t\t\t\tqglUniform3fvARB(ph, 1, t2);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase SP_LIGHTDIRECTION:\n\t\t\t{\n\t\t\t\t/*light position in model space*/\n\t\t\t\tvec3_t t2;\n\t\t\t\tMatrix4x4_CM_Transform3x3(shaderstate.modelmatrixinv, shaderstate.lightdir, t2);\n\t\t\t\tqglUniform3fvARB(ph, 1, t2);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase SP_LIGHTCOLOURSCALE:\n\t\t\tqglUniform3fvARB(ph, 1, shaderstate.lightcolourscale);\n\t\t\tbreak;\n\t\tcase SP_LIGHTCUBEMATRIX:\n\t\t\t/*light's texture projection matrix*/\n\t\t\t{\n\t\t\t\tfloat t[16];\n\t\t\t\tMatrix4_Multiply(shaderstate.lightprojmatrix, shaderstate.modelmatrix, t);\n\t\t\t\tqglUniformMatrix4fvARB(ph, 1, false, t);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase SP_LIGHTSHADOWMAPPROJ:\n\t\t\tqglUniform4fvARB(ph, 1, shaderstate.lightshadowmapproj);\n\t\t\tbreak;\n\t\tcase SP_LIGHTSHADOWMAPSCALE:\n\t\t\tqglUniform2fvARB(ph, 1, shaderstate.lightshadowmapscale);\n\t\t\tbreak;\n\n\t\t/*static lighting info*/\n\t\tcase SP_E_L_DIR:\n\t\t\tqglUniform3fvARB(ph, 1, (float*)shaderstate.curentity->light_dir);\n\t\t\tbreak;\n\t\tcase SP_E_L_MUL:\n\t\t\tif (shaderstate.mode == BEM_DEPTHDARK)\n\t\t\t\tqglUniform3fvARB(ph, 1, vec3_origin);\n\t\t\telse\n\t\t\t\tqglUniform3fvARB(ph, 1, (float*)shaderstate.curentity->light_range);\n\t\t\tbreak;\n\t\tcase SP_E_L_AMBIENT:\n\t\t\tif (shaderstate.mode == BEM_DEPTHDARK)\n\t\t\t\tqglUniform3fvARB(ph, 1, vec3_origin);\n\t\t\telse\n\t\t\t\tqglUniform3fvARB(ph, 1, (float*)shaderstate.curentity->light_avg);\n\t\t\tbreak;\n\n\t\tcase SP_E_TIME:\n\t\t\tqglUniform1fARB(ph, shaderstate.curtime);\n\t\t\tbreak;\n\t\tcase SP_CONST1I:\n\t\tcase SP_TEXTURE:\n\t\t\tqglUniform1iARB(ph, p->ival[0]);\n\t\t\tbreak;\n\t\tcase SP_CONST1F:\n\t\t\tqglUniform1fARB(ph, p->fval[0]);\n\t\t\tbreak;\n\t\tcase SP_CONST2F:\n\t\t\tqglUniform3fARB(ph, p->fval[0], p->fval[1], 0);\n\t\t\tbreak;\n\t\tcase SP_CONST3F:\n\t\t\tqglUniform3fARB(ph, p->fval[0], p->fval[1], p->fval[2]);\n\t\t\tbreak;\n\t\tcase SP_CONST4F:\n\t\t\tqglUniform4fARB(ph, p->fval[0], p->fval[1], p->fval[2], p->fval[3]);\n\t\t\tbreak;\n\t\tcase SP_CVARI:\n\t\t\tqglUniform1iARB(ph, ((cvar_t*)p->pval)->ival);\n\t\t\tbreak;\n\t\tcase SP_CVARF:\n\t\t\tqglUniform1fARB(ph, ((cvar_t*)p->pval)->value);\n\t\t\tbreak;\n\t\tcase SP_CVAR3F:\n\t\t\tqglUniform3fvARB(ph, 1, ((cvar_t*)p->pval)->vec4);\n\t\t\tbreak;\n\t\tcase SP_CVAR4F:\n\t\t\tqglUniform3fvARB(ph, 1, ((cvar_t*)p->pval)->vec4);\n\t\t\tbreak;\n\n\t\tdefault:\n\t\t\tHost_EndGame(\"Bad shader program parameter type (%i)\", p->type);\n\t\t\tbreak;\n\t\t}\n\t}\n}\n\nstatic void BE_RenderMeshProgram(const shader_t *shader, const shaderpass_t *pass, program_t *p)\n{\n\tint\ti;\n\n\tint perm;\n\n\tstruct programpermu_s *permu;\n\n\tperm = 0;\n\tif (shaderstate.sourcevbo->numbones)\n\t\tperm |= PERMUTATION_SKELETAL;\n#ifdef NONSKELETALMODELS\n\tif (shaderstate.sourcevbo->coord2.gl.addr)\n\t\tperm |= PERMUTATION_FRAMEBLEND;\n#endif\n\tif (TEXLOADED(shaderstate.curtexnums->bump))\n\t\tperm |= PERMUTATION_BUMPMAP;\n\tif (TEXLOADED(shaderstate.curtexnums->fullbright))\n\t\tperm |= PERMUTATION_FULLBRIGHT;\n\tif ((TEXLOADED(shaderstate.curtexnums->loweroverlay) || TEXLOADED(shaderstate.curtexnums->upperoverlay)))\n\t\tperm |= PERMUTATION_UPPERLOWER;\n\tif (r_refdef.globalfog.density)\n\t\tperm |= PERMUTATION_FOG;\n//\tif (TEXLOADED(shaderstate.curtexnums->bump) && shaderstate.curbatch->lightmap[0] >= 0 && lightmap[shaderstate.curbatch->lightmap[0]]->hasdeluxe)\n//\t\tperm |= PERMUTATION_DELUXE;\n\tif ((TEXLOADED(shaderstate.curtexnums->reflectcube) || TEXLOADED(shaderstate.curtexnums->reflectmask)))\n\t\tperm |= PERMUTATION_REFLECTCUBEMASK;\n#if MAXRLIGHTMAPS > 1\n\tif (shaderstate.curbatch->lightmap[1] >= 0)\n\t\tperm |= PERMUTATION_LIGHTSTYLES;\n#endif\n\n\tperm &= p->supportedpermutations;\n\tpermu = p->permu[perm];\n\tif (!permu)\n\t{\n\t\tp->permu[perm] = permu = Shader_LoadPermutation(p, perm);\n\t\tif (!permu)\n\t\t{\t//failed? copy from 0 so we don't keep re-failing\n\t\t\tpermu = p->permu[perm] = p->permu[0];\n\t\t}\n\t}\n\n\tGL_SelectProgram(permu->h.glsl.handle);\n#ifndef FORCESTATE\n\tif (shaderstate.lastuniform == shaderstate.currentprogram)\n\t\ti = true;\n\telse\n#endif\n\t{\n\t\ti = false;\n\t\tshaderstate.lastuniform = shaderstate.currentprogram;\n\t}\n\tBE_Program_Set_Attributes(p, permu, i);\n\n\tBE_SendPassBlendDepthMask(pass->shaderbits);\n\n#ifndef GLSLONLY\n\tif (p->calcgens)\n\t{\n\t\tGenerateColourMods(pass);\n\t\tfor (i = 0; i < pass->numMergedPasses; i++)\n\t\t{\n\t\t\tShader_BindTextureForPass(i, pass+i);\n\t\t\tBE_GeneratePassTC(pass+i, i);\n\t\t}\n\t}\n\telse\n#endif\n\t{\n\t\tfor (i = 0; i < pass->numMergedPasses; i++)\n\t\t{\n\t\t\tShader_BindTextureForPass(i, pass+i);\n\t\t}\n\t}\n#if MAXRLIGHTMAPS > 1\n\tif (perm & PERMUTATION_LIGHTSTYLES)\n\t{\n\t\tGL_LazyBind(i++, shaderstate.curbatch->lightmap[1]>=0?lightmap[shaderstate.curbatch->lightmap[1]]->lightmap_texture:r_nulltex);\n\t\tGL_LazyBind(i++, shaderstate.curbatch->lightmap[2]>=0?lightmap[shaderstate.curbatch->lightmap[2]]->lightmap_texture:r_nulltex);\n\t\tGL_LazyBind(i++, shaderstate.curbatch->lightmap[3]>=0?lightmap[shaderstate.curbatch->lightmap[3]]->lightmap_texture:r_nulltex);\n\t\tGL_LazyBind(i++, (shaderstate.curbatch->lightmap[1]>=0&&lightmap[shaderstate.curbatch->lightmap[1]]->hasdeluxe)?lightmap[shaderstate.curbatch->lightmap[1]+1]->lightmap_texture:missing_texture_normal);\n\t\tGL_LazyBind(i++, (shaderstate.curbatch->lightmap[2]>=0&&lightmap[shaderstate.curbatch->lightmap[2]]->hasdeluxe)?lightmap[shaderstate.curbatch->lightmap[2]+1]->lightmap_texture:missing_texture_normal);\n\t\tGL_LazyBind(i++, (shaderstate.curbatch->lightmap[3]>=0&&lightmap[shaderstate.curbatch->lightmap[3]]->hasdeluxe)?lightmap[shaderstate.curbatch->lightmap[3]+1]->lightmap_texture:missing_texture_normal);\n\t}\n#endif\n\twhile (shaderstate.lastpasstmus > i)\n\t\tGL_LazyBind(--shaderstate.lastpasstmus, r_nulltex);\n\tshaderstate.lastpasstmus = i;\n\n\tBE_EnableShaderAttributes(permu->attrmask, shaderstate.sourcevbo->vao);\n\tBE_SubmitMeshChain(permu->h.glsl.usetesselation);\n}\n\nqboolean GLBE_LightCullModel(vec3_t org, model_t *model)\n{\n#ifdef RTLIGHTS\n\tif ((shaderstate.mode == BEM_LIGHT || shaderstate.mode == BEM_STENCIL || shaderstate.mode == BEM_DEPTHONLY))\n\t{\n\t\tfloat dist;\n\t\tvec3_t disp;\n\t\tif (model->type == mod_alias)\n\t\t{\n\t\t\tVectorSubtract(org, shaderstate.lightorg, disp);\n\t\t\tdist = DotProduct(disp, disp);\n\t\t\tif (dist > model->radius*model->radius + shaderstate.lightradius*shaderstate.lightradius)\n\t\t\t\treturn true;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tint i;\n\n\t\t\tfor (i = 0; i < 3; i++)\n\t\t\t{\n\t\t\t\tif (shaderstate.lightorg[i]-shaderstate.lightradius > org[i] + model->maxs[i])\n\t\t\t\t\treturn true;\n\t\t\t\tif (shaderstate.lightorg[i]+shaderstate.lightradius < org[i] + model->mins[i])\n\t\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t}\n#endif\n\treturn false;\n}\n\n//Note: Be cautious about using BEM_LIGHT here, as it won't select the light.\nvoid GLBE_SelectMode(backendmode_t mode)\n{\n\textern cvar_t r_polygonoffset_shadowmap_offset, r_polygonoffset_shadowmap_factor;\n\textern int gldepthfunc;\n\n//\tshaderstate.lastuniform = 0;\n#ifndef FORCESTATE\n\tif (mode != shaderstate.mode)\n#endif\n\t{\n\t\tshaderstate.mode = mode;\n\t\tshaderstate.flags = 0;\n\t\tshaderstate.polyoffset.factor = 0;\n\t\tshaderstate.polyoffset.unit = 0;\n\t\tswitch (mode)\n\t\t{\n\t\tdefault:\n\t\t\tbreak;\n\t\tcase BEM_WIREFRAME:\n\t\t\tif (!shaderstate.wireframeshader && gl_config.arb_shader_objects)\n\t\t\t\tshaderstate.wireframeshader = R_RegisterShader(\"wireframe\", SUF_NONE,\n\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\"program wireframe\\n\"\n\t\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\t\"nodepthtest\\n\"\n\t\t\t\t\t\t\"}\\n\"\n\t\t\t\t\t\"}\\n\"\n\t\t\t\t);\n\t\t\tbreak;\n\t\tcase BEM_DEPTHONLY:\n\t\t\tshaderstate.polyoffset.factor = r_polygonoffset_shadowmap_factor.value;\n\t\t\tshaderstate.polyoffset.unit = r_polygonoffset_shadowmap_offset.value;\n#ifndef GLSLONLY\n\t\t\tif (!gl_config_nofixedfunc)\n\t\t\t{\n\t\t\t\tBE_SetPassBlendMode(0, PBM_REPLACE);\n\t\t\t\tGL_DeSelectProgram();\n\t\t\t}\n\t\t\telse\n#endif\n\t\t\t\tif (!shaderstate.allblackshader.glsl.handle)\n\t\t\t{\n\t\t\t\tconst char *defs[] = {NULL};\n\t\t\t\tshaderstate.allblackshader = GLSlang_CreateProgram(NULL, \"allblackprogram\", sh_config.minver, defs, \"#include \\\"sys/skeletal.h\\\"\\nvoid main(){gl_Position = skeletaltransform();}\", NULL, NULL, NULL, \"void main(){gl_FragColor=vec4(0.0,0.0,0.0,1.0);}\", false, NULL);\n\t\t\t\tshaderstate.allblack_mvp = qglGetUniformLocationARB(shaderstate.allblackshader.glsl.handle, \"m_modelviewprojection\");\n\t\t\t}\n\t\t\t/*BEM_DEPTHONLY does support mesh writing, but its not the only way its used... FIXME!*/\n\t\t\twhile(shaderstate.lastpasstmus>0)\n\t\t\t{\n\t\t\t\tGL_LazyBind(--shaderstate.lastpasstmus, r_nulltex);\n\t\t\t}\n\n\t\t\t//we don't write or blend anything (maybe alpha test... but mneh)\n\t\t\tBE_SendPassBlendDepthMask(SBITS_MISC_DEPTHWRITE | SBITS_MASK_BITS);\n\n//\t\t\tBE_PolyOffset(false);\n\n\t\t\tGL_CullFace(SHADER_CULL_FRONT);\n\t\t\tbreak;\n\n#ifdef RTLIGHTS\n\t\tcase BEM_STENCIL:\n\t\t\t/*BEM_STENCIL doesn't support mesh writing*/\n#ifdef BEF_PUSHDEPTH\n\t\t\tGLBE_PolyOffsetStencilShadow(false);\n#else\n\t\t\tGLBE_PolyOffsetStencilShadow();\n#endif\n\n\t\t\tif (gl_config_nofixedfunc && !shaderstate.allblackshader.glsl.handle)\n\t\t\t{\n\t\t\t\tconst char *defs[] = {NULL};\n\t\t\t\tshaderstate.allblackshader = GLSlang_CreateProgram(NULL, \"allblackprogram\", sh_config.minver, defs, \"#include \\\"sys/skeletal.h\\\"\\nvoid main(){gl_Position = skeletaltransform();}\", NULL, NULL, NULL, \"void main(){gl_FragColor=vec4(0.0,0.0,0.0,1.0);}\", false, NULL);\n\t\t\t\tshaderstate.allblack_mvp = qglGetUniformLocationARB(shaderstate.allblackshader.glsl.handle, \"m_modelviewprojection\");\n\t\t\t}\n\n\t\t\t//disable all tmus\n\t\t\twhile(shaderstate.lastpasstmus>0)\n\t\t\t{\n\t\t\t\tGL_LazyBind(--shaderstate.lastpasstmus, r_nulltex);\n\t\t\t}\n#ifndef GLSLONLY\n\t\t\tif (!gl_config_nofixedfunc)\n\t\t\t{\n\t\t\t\tGL_DeSelectProgram();\n\n\t\t\t//replace mode please\n\t\t\t\tBE_SetPassBlendMode(0, PBM_REPLACE);\n\t\t\t}\n#endif\n\n\t\t\t//we don't write or blend anything (maybe alpha test... but mneh)\n\t\t\tBE_SendPassBlendDepthMask(SBITS_DEPTHFUNC_CLOSER | SBITS_MASK_BITS);\n\t\t\tGL_CullFace(0);\n\n\t\t\t//don't change cull stuff, and\n\t\t\t//don't actually change stencil stuff - caller needs to be\n\t\t\t//aware of how many times stuff is drawn, so they can do that themselves.\n\t\t\tbreak;\n\t\tcase BEM_CREPUSCULAR:\n\t\t\tif (!shaderstate.crepopaqueshader)\n\t\t\t{\n\t\t\t\tshaderstate.crepopaqueshader = R_RegisterShader(\"crepuscular_opaque\", SUF_NONE,\n\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\"program crepuscular_opaque\\n\"\n\t\t\t\t\t\"}\\n\"\n\t\t\t\t\t);\n\t\t\t}\n\t\t\tif (!shaderstate.crepskyshader)\n\t\t\t{\n\t\t\t\tshaderstate.crepskyshader = R_RegisterShader(\"crepuscular_sky\", SUF_NONE,\n\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\"program crepuscular_sky\\n\"\n\t\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\t\"map $diffuse\\n\"\n\t\t\t\t\t\t\"}\\n\"\n\t\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\t\"map $fullbright\\n\"\n\t\t\t\t\t\t\"}\\n\"\n\t\t\t\t\t\"}\\n\"\n\t\t\t\t\t);\n\t\t\t}\n\t\t\tbreak;\n#endif\n\t\tcase BEM_FOG:\n\t\t\twhile(shaderstate.lastpasstmus>0)\n\t\t\t{\n\t\t\t\tGL_LazyBind(--shaderstate.lastpasstmus, r_nulltex);\n\t\t\t}\n\t\t\tGL_LazyBind(0, shaderstate.fogtexture);\n\t\t\tshaderstate.lastpasstmus = 1;\n\n\t\t\tVector4Set(shaderstate.pendingcolourflat, 1, 1, 1, 1);\n\t\t\tshaderstate.pendingcolourvbo = 0;\n\t\t\tshaderstate.pendingcolourpointer = NULL;\n#ifndef GLSLONLY\n\t\t\tif (!gl_config_nofixedfunc)\n\t\t\t\tBE_SetPassBlendMode(0, PBM_MODULATE);\n#endif\n\t\t\tBE_SendPassBlendDepthMask(SBITS_SRCBLEND_SRC_ALPHA | SBITS_DSTBLEND_ONE_MINUS_SRC_ALPHA | SBITS_DEPTHFUNC_EQUAL);\n\t\t\tbreak;\n\t\t}\n\t}\n}\n\nvoid GLBE_SelectEntity(entity_t *ent)\n{\n\tunsigned int fl;\n\tshaderstate.curentity = ent;\n\tcurrententity = ent;\n\tR_RotateForEntity(shaderstate.modelmatrix, shaderstate.modelviewmatrix, shaderstate.curentity, shaderstate.curentity->model);\n\tMatrix4_Invert(shaderstate.modelmatrix, shaderstate.modelmatrixinv);\n\tif (qglLoadMatrixf)\n\t\tqglLoadMatrixf(shaderstate.modelviewmatrix);\n\n\n\tfl = shaderstate.curentity->flags&(RF_DEPTHHACK|RF_XFLIP);\n\tif (shaderstate.usingweaponviewmatrix != fl)\n\t{\n\t\tif ((shaderstate.usingweaponviewmatrix & RF_XFLIP) && shaderstate.usingweaponviewmatrix != -1)\n\t\t\tr_refdef.flipcull ^= SHADER_CULL_FLIP;\n\t\tshaderstate.usingweaponviewmatrix = fl;\n\n\t\tif (fl)\n\t\t\tmemcpy(shaderstate.projectionmatrix, r_refdef.m_projection_view, sizeof(shaderstate.projectionmatrix));\n\t\telse\n\t\t\tmemcpy(shaderstate.projectionmatrix, r_refdef.m_projection_std, sizeof(shaderstate.projectionmatrix));\n\n\t\tif (fl&RF_XFLIP)\n\t\t{\n\t\t\tshaderstate.projectionmatrix[0] *= -1;\n\t\t\tshaderstate.projectionmatrix[4] *= -1;\n\t\t\tshaderstate.projectionmatrix[8] *= -1;\n\t\t\tshaderstate.projectionmatrix[12] *= -1;\n\t\t\tr_refdef.flipcull ^= SHADER_CULL_FLIP;\n\t\t}\n\n\t\tif (qglLoadMatrixf)\n\t\t{\n\t\t\tqglMatrixMode(GL_PROJECTION);\n\t\t\tqglLoadMatrixf(shaderstate.projectionmatrix);\n\t\t\tqglMatrixMode(GL_MODELVIEW);\n\t\t}\n\t}\n\n\tshaderstate.lastuniform = 0;\n}\n\n#ifndef GLSLONLY\nstatic void BE_SelectFog(vec3_t colour, float alpha, float density)\n{\n\tfloat zscale;\n\n\tGL_DeSelectProgram();\n\n\tzscale = 2048;\t/*this value is meant to be the distance at which fog the value becomes as good as fully fogged, just hack it to 2048...*/\n\tGenerateFogTexture(&shaderstate.fogtexture, density, zscale);\n\tshaderstate.fogfar = 1/zscale; /*scaler for z coords*/\n\n\tVectorCopy(colour, shaderstate.pendingcolourflat);\n\tshaderstate.pendingcolourflat[3] = alpha;\n\tshaderstate.pendingcolourvbo = 0;\n\tshaderstate.pendingcolourpointer = NULL;\n}\n#endif\n#ifdef RTLIGHTS\nstatic qboolean GLBE_RegisterLightShader(int mode)\n{\n\tif (!shaderstate.inited_shader_light[mode])\n\t{\n\t\tchar *name = va(\"rtlight%s%s%s%s%s\", \n\t\t\t(mode & LSHADER_SMAP)?\"#PCF\":\"\",\n\t\t\t(mode & LSHADER_SPOT)?\"#SPOT\":\"\",\n\t\t\t(mode & LSHADER_CUBE)?\"#CUBE\":\"\",\n\t\t\t(mode & LSHADER_ORTHO)?\"#ORTHO\":\"\",\n\t\t\t(gl_config.arb_shadow && (mode & (LSHADER_SMAP|LSHADER_SPOT|LSHADER_CUBE|LSHADER_ORTHO)))?\"#USE_ARB_SHADOW\":\"\"\n\t\t\t);\n\n\t\tshaderstate.inited_shader_light[mode] = true;\n\t\tshaderstate.shader_light[mode] = R_RegisterCustom(NULL, name, SUF_NONE, Shader_LightPass, NULL);\n\t}\n\n\tif (shaderstate.shader_light[mode])\n\t{\n\t\t//make sure it has a program and forget it if it doesn't, to save a compare.\n\t\tif (!shaderstate.shader_light[mode]->prog)\n\t\t{\n\t\t\tshaderstate.shader_light[mode] = NULL;\n\t\t\treturn false;\n\t\t}\n\t\treturn true;\n\t}\n\treturn false;\n}\n#endif\n\nqboolean GLBE_SelectDLight(dlight_t *dl, vec3_t colour, vec3_t axis[3], unsigned int lmode)\n{\n#ifdef RTLIGHTS\n\textern cvar_t gl_specular;\n#endif\n\n\tshaderstate.lastuniform = 0;\n\tshaderstate.curdlight = dl;\n\tshaderstate.lightmode =\tlmode;\n\n\t/*simple info*/\n\tshaderstate.lightradius = dl->radius;\n\tVectorCopy(dl->origin, shaderstate.lightorg);\n\tVectorCopy(axis[0], shaderstate.lightdir);\n\tVectorCopy(colour, shaderstate.lightcolours);\n#ifdef RTLIGHTS\n\tVectorCopy(dl->lightcolourscales, shaderstate.lightcolourscale);\n\tshaderstate.lightcolourscale[2] *= gl_specular.value;\n\tif (lmode & (LSHADER_SPOT|LSHADER_ORTHO))\n\t\tshaderstate.lightcubemap = r_nulltex;\n\telse\n\t\tshaderstate.lightcubemap = dl->cubetexture;\n\n\tif (TEXLOADED(shaderstate.lightcubemap) && GLBE_RegisterLightShader(shaderstate.lightmode | LSHADER_CUBE))\n\t\tshaderstate.lightmode |= LSHADER_CUBE;\n\tif (!GLBE_RegisterLightShader(shaderstate.lightmode))\n\t\treturn false;\n\n\t/*generate light projection information*/\n\tif (shaderstate.lightmode & LSHADER_ORTHO)\n\t{\n\t\tfloat view[16];\n\t\tfloat proj[16];\n\t\tfloat xmin = -dl->radius;\n\t\tfloat ymin = -dl->radius;\n\t\tfloat znear = -dl->radius;\n\t\tfloat xmax = dl->radius;\n\t\tfloat ymax = dl->radius;\n\t\tfloat zfar = dl->radius;\n\t\tMatrix4x4_CM_Orthographic(proj, xmin, xmax, ymax, ymin, znear, zfar);\n\t\tMatrix4x4_CM_ModelViewMatrixFromAxis(view, axis[0], axis[2], axis[1], dl->origin);\n\t\tMatrix4_Multiply(proj, view, shaderstate.lightprojmatrix);\n//\t\tMatrix4x4_CM_LightMatrixFromAxis(shaderstate.lightprojmatrix, axis[0], axis[1], axis[2], dl->origin);\n\t}\n\telse if (shaderstate.lightmode & LSHADER_SPOT)\n\t{\n\t\tfloat view[16];\n\t\tfloat proj[16];\n\t\textern cvar_t r_shadow_shadowmapping_nearclip;\n\t\tMatrix4x4_CM_Projection_Far(proj, dl->fov, dl->fov, dl->nearclip?dl->nearclip:r_shadow_shadowmapping_nearclip.value, dl->radius, false);\n\t\tMatrix4x4_CM_ModelViewMatrixFromAxis(view, axis[0], axis[1], axis[2], dl->origin);\n\t\tMatrix4_Multiply(proj, view, shaderstate.lightprojmatrix);\n\t}\n\telse if (shaderstate.lightmode & (LSHADER_SMAP|LSHADER_CUBE))\n\t{\n\t\tMatrix4x4_CM_LightMatrixFromAxis(shaderstate.lightprojmatrix, axis[0], axis[1], axis[2], dl->origin);\n\t\t/*\n\t\tvec3_t down;\n\t\tvec3_t back;\n\t\tvec3_t right;\n\t\tVectorScale(axis[2], -1, down);\n\t\tVectorScale(axis[1], 1, right);\n\t\tVectorScale(axis[0], 1, back);\n\t\tMatrix4x4_CM_ModelViewMatrixFromAxis(shaderstate.lightprojmatrix, down, back, right, dl->origin);\n\t\t*/\n\t}\n#endif\n\n\treturn true;\n}\n\nvoid GLBE_Scissor(srect_t *rect)\n{\n\tif (rect)\n\t{\n\t\tqglScissor(\n\t\t\tfloor(r_refdef.pxrect.x + rect->x*r_refdef.pxrect.width),\n//\t\t\tfloor(r_refdef.pxrect.y + rect->y*r_refdef.pxrect.height),// - r_refdef.pxrect.maxheight),\n\t\t\tfloor(rect->y*r_refdef.pxrect.height + (r_refdef.pxrect.maxheight-r_refdef.pxrect.y)-r_refdef.pxrect.height),\n\t\t\tceil(rect->width * r_refdef.pxrect.width),\n\t\t\tceil(rect->height * r_refdef.pxrect.height));\n\t\tqglEnable(GL_SCISSOR_TEST);\n\n\t\tif (qglDepthBoundsEXT)\n\t\t{\n\t\t\tqglDepthBoundsEXT(rect->dmin, rect->dmax);\n\t\t\tqglEnable(GL_DEPTH_BOUNDS_TEST_EXT);\n\t\t}\n\t}\n\telse\n\t{\n/*\t\tqglScissor(\n\t\t\tr_refdef.pxrect.x,\n\t\t\tr_refdef.pxrect.y - r_refdef.pxrect.height,\n\t\t\tr_refdef.pxrect.width,\n\t\t\tr_refdef.pxrect.height);\n*/\t\tqglDisable(GL_SCISSOR_TEST);\n\t\tif (qglDepthBoundsEXT)\n\t\t\tqglDisable(GL_DEPTH_BOUNDS_TEST_EXT);\n\n//\t\tif (qglDepthBoundsEXT)\n//\t\t\tqglDepthBoundsEXT(0, 1);\n\t}\n}\n\n#if defined(RTLIGHTS) && !defined(GLSLONLY)\ntexid_t GenerateNormalisationCubeMap(void);\nstatic void BE_LegacyLighting(void)\n{\n\t//bigfoot wants rtlight support without glsl, so here goes madness...\n\t//register combiners for bumpmapping using 4 tmus...\n\tfloat *col;\n\tfloat *ldir;\n\tvec3_t lightdir, rellight;\n\tfloat scale;\n\tint i, m;\n\tmesh_t *mesh;\n\tunsigned int attr = (1u<<VATTR_LEG_VERTEX) | (1u<<VATTR_LEG_COLOUR);\n\tint tmu = 0;\n\n\tBE_SendPassBlendDepthMask(SBITS_SRCBLEND_ONE | SBITS_DSTBLEND_ONE);\n\n\t//rotate this into modelspace\n\tMatrix4x4_CM_Transform3(shaderstate.modelmatrixinv, shaderstate.lightorg, rellight);\n\n\tfor (m = 0; m < shaderstate.meshcount; m++)\n\t{\n\t\tmesh = shaderstate.meshes[m];\n\n\t\t//vbo-only mesh.\n\t\tif (!mesh->xyz_array)\n\t\t\treturn;\n\t\tif (!mesh->normals_array)\n\t\t\treturn;\n\n\t\tcol = coloursarray[0] + mesh->vbofirstvert*4;\n\t\tldir = texcoordarray[0] + mesh->vbofirstvert*3;\n\t\tfor (i = 0; i < mesh->numvertexes; i++, col+=4, ldir+=3)\n\t\t{\n\t\t\tVectorSubtract(rellight, mesh->xyz_array[i], lightdir);\n\t\t\tscale = VectorNormalize(lightdir);\n\t\t\tscale = 1 - (scale/shaderstate.lightradius);\n\t\t\tVectorScale(shaderstate.lightcolours, scale, col);\n\t\t\tcol[3] = 1;\n\t\t\tldir[0] = -DotProduct(lightdir, mesh->snormals_array[i]);\n\t\t\tldir[1] = DotProduct(lightdir, mesh->tnormals_array[i]);\n\t\t\tldir[2] = DotProduct(lightdir, mesh->normals_array[i]);\n\t\t}\n\t}\n\n\tif (TEXLOADED(shaderstate.curtexnums->bump) && sh_config.havecubemaps && gl_config.arb_texture_env_dot3 && gl_config.arb_texture_env_combine && be_maxpasses >= 4)\n\t{\t//we could get this down to 2 tmus by arranging for the dot3 result to be written the alpha buffer. But then we'd need to have an alpha buffer too.\n\n\t\tif (!shaderstate.normalisationcubemap)\n\t\t\tshaderstate.normalisationcubemap = GenerateNormalisationCubeMap();\n\n\t\t//tmu0: normalmap+replace+regular tex coords\n\t\tGL_LazyBind(tmu, shaderstate.curtexnums->bump);\n\t\tBE_SetPassBlendMode(tmu, PBM_REPLACE);\n\t\tshaderstate.pendingtexcoordparts[tmu] = 2;\n\t\tshaderstate.pendingtexcoordvbo[tmu] = shaderstate.sourcevbo->texcoord.gl.vbo;\n\t\tshaderstate.pendingtexcoordpointer[tmu] = shaderstate.sourcevbo->texcoord.gl.addr;\n\t\tattr |= (1u<<(VATTR_LEG_TMU0+tmu));\n\t\ttmu++;\n\n\t\t//tmu1: normalizationcubemap+dot3+lightdir\n\t\tGL_LazyBind(tmu, shaderstate.normalisationcubemap);\n\t\tBE_SetPassBlendMode(tmu, PBM_DOTPRODUCT);\n\t\tshaderstate.pendingtexcoordparts[tmu] = 3;\n\t\tshaderstate.pendingtexcoordvbo[tmu] = 0;\n\t\tshaderstate.pendingtexcoordpointer[tmu] = texcoordarray[0];\n\t\tattr |= (1u<<(VATTR_LEG_TMU0+tmu));\n\t\ttmu++;\n\n\t\t//tmu2: $diffuse+multiply+regular tex coords\n\t\tGL_LazyBind(tmu, shaderstate.curtexnums->base);\t//texture not used, its just to make sure the code leaves it enabled.\n\t\tBE_SetPassBlendMode(tmu, PBM_MODULATE);\n\t\tshaderstate.pendingtexcoordparts[tmu] = 2;\n\t\tshaderstate.pendingtexcoordvbo[tmu] = shaderstate.sourcevbo->texcoord.gl.vbo;\n\t\tshaderstate.pendingtexcoordpointer[tmu] = shaderstate.sourcevbo->texcoord.gl.addr;\n\t\tattr |= (1u<<(VATTR_LEG_TMU0+tmu));\n\t\ttmu++;\n\t\n\t\t//tmu3: $any+multiply-by-colour+notc\n\t\tGL_LazyBind(tmu, shaderstate.curtexnums->base);\t//texture not used, its just to make sure the code leaves it enabled.\n\t\tBE_SetPassBlendMode(tmu, PBM_MODULATE_PREV_COLOUR);\n\t\tshaderstate.pendingtexcoordparts[tmu] = 0;\n\t\tshaderstate.pendingtexcoordvbo[tmu] = 0;\n\t\tshaderstate.pendingtexcoordpointer[tmu] = NULL;\n\t\ttmu++;\n\n\t\t//note we need 4 combiners in the first because we can't use the colour argument in the first without breaking the normals.\n\n\t\tfor (i = tmu; i < shaderstate.lastpasstmus; i++)\n\t\t{\n\t\t\tGL_LazyBind(i, r_nulltex);\n\t\t}\n\t\tshaderstate.lastpasstmus = tmu;\n\t}\n\telse\n\t{\n\t\tattr |= (1u<<(VATTR_LEG_TMU0));\n\n\t\t//tmu0: $diffuse+multiply+regular tex coords\n\t\t//multiplies by vertex colours\n\t\tGL_LazyBind(0, shaderstate.curtexnums->base);\t//texture not used, its just to make sure the code leaves it enabled.\n\t\tBE_SetPassBlendMode(0, PBM_MODULATE);\n\t\tshaderstate.pendingtexcoordvbo[0] = shaderstate.sourcevbo->texcoord.gl.vbo;\n\t\tshaderstate.pendingtexcoordpointer[0] = shaderstate.sourcevbo->texcoord.gl.addr;\n\n\t\tfor (i = 1; i < shaderstate.lastpasstmus; i++)\n\t\t{\n\t\t\tGL_LazyBind(i, r_nulltex);\n\t\t}\n\t\tshaderstate.lastpasstmus = 1;\n\t}\n\n\tshaderstate.colourarraytype = GL_FLOAT;\n\tshaderstate.pendingcolourvbo = 0;\n\tshaderstate.pendingcolourpointer = coloursarray;\n\n\tGL_DeSelectProgram();\n\tBE_EnableShaderAttributes(attr, 0);\n\n\tBE_SubmitMeshChain(false);\n\n//\tGL_LazyBind(1, 0, r_nulltex);\n//\tGL_LazyBind(2, 0, r_nulltex);\n//\tGL_LazyBind(3, 0, r_nulltex);\n}\n#endif\n\nstatic void DrawMeshes(void)\n{\n\tconst shader_t *altshader;\n\tconst shaderpass_t *p;\n\tint passno;\n\tint flags;\n\tpassno = 0;\n\n\tif (shaderstate.force2d)\n\t{\n\t\tRQuantAdd(RQUANT_2DBATCHES, 1);\n\t}\n\telse if (shaderstate.curentity == &r_worldentity)\n\t{\n\t\tRQuantAdd(RQUANT_WORLDBATCHES, 1);\n\t}\n\telse\n\t{\n\t\tRQuantAdd(RQUANT_ENTBATCHES, 1);\n\t}\n\n\tflags = shaderstate.curshader->flags;\n\tGL_CullFace(flags & (SHADER_CULL_FRONT|SHADER_CULL_BACK));\n\n\tif (shaderstate.sourcevbo->coord2.gl.addr && (shaderstate.curshader->numdeforms || !shaderstate.curshader->prog))\n\t\tGenerateVertexBlends(shaderstate.curshader);\n\telse if (shaderstate.curshader->numdeforms)\n\t\tGenerateVertexDeforms(shaderstate.curshader);\n\telse\n\t{\n\t\tshaderstate.pendingvertexpointer = shaderstate.sourcevbo->coord.gl.addr;\n\t\tshaderstate.pendingvertexvbo = shaderstate.sourcevbo->coord.gl.vbo;\n\t}\n\n#ifdef _DEBUG\n\tif (!shaderstate.pendingvertexpointer && !shaderstate.pendingvertexvbo)\n\t{\n\t\tCon_Printf(CON_ERROR \"pendingvertexpointer+vbo are both null! shader is %s\\n\", shaderstate.curshader->name);\n\t\treturn;\n\t}\n#endif\n\n#ifdef FTE_TARGET_WEB\n\tif (!shaderstate.pendingvertexvbo)\n\t{\n\t\tint len = 0, m;\n\t\tmesh_t *meshlist;\n\t\tfor (m = 0; m < shaderstate.meshcount; m++)\n\t\t{\n\t\t\tmeshlist = shaderstate.meshes[m];\n\t\t\tif (len < meshlist->vbofirstvert + meshlist->numvertexes)\n\t\t\t\tlen = meshlist->vbofirstvert + meshlist->numvertexes;\n\t\t}\n\t\tlen *= sizeof(vecV_t);\n\n\t\tshaderstate.streamid = (shaderstate.streamid + 1) & (sizeof(shaderstate.streamvbo)/sizeof(shaderstate.streamvbo[0]) - 1);\n\t\tGL_SelectVBO(shaderstate.pendingvertexvbo = shaderstate.streamvbo[shaderstate.streamid]);\n\t\tqglBufferDataARB(GL_ARRAY_BUFFER_ARB, len, shaderstate.pendingvertexpointer, GL_STREAM_DRAW_ARB);\n\t\tshaderstate.pendingvertexpointer = NULL;\n\t}\n\tif (!shaderstate.sourcevbo->indicies.gl.vbo)\n\t\treturn;\n#endif\n\n\tBE_PolyOffset();\n\tswitch(shaderstate.mode)\n\t{\n\tcase BEM_STENCIL:\n\t\tHost_Error(\"Shader system is not meant to accept stencil meshes\\n\");\n\t\tbreak;\n#ifdef RTLIGHTS\n\tcase BEM_LIGHT:\n\t\taltshader = shaderstate.curshader->bemoverrides[shaderstate.lightmode];\n\t\tif (!altshader)\n\t\t\taltshader = shaderstate.shader_light[shaderstate.lightmode];\n\t\tif (altshader && altshader->prog)\n\t\t{\n\t\t\tshaderstate.pendingcolourvbo = shaderstate.sourcevbo->colours[0].gl.vbo;\n\t\t\tshaderstate.pendingcolourpointer = shaderstate.sourcevbo->colours[0].gl.addr;\n\t\t\tshaderstate.colourarraytype = shaderstate.sourcevbo->colours_bytes?GL_UNSIGNED_BYTE:GL_FLOAT;\n\t\t\tshaderstate.pendingtexcoordparts[0] = 2;\n\t\t\tshaderstate.pendingtexcoordvbo[0] = shaderstate.sourcevbo->texcoord.gl.vbo;\n\t\t\tshaderstate.pendingtexcoordpointer[0] = shaderstate.sourcevbo->texcoord.gl.addr;\n\t\t\tBE_RenderMeshProgram(altshader, altshader->passes, altshader->prog);\n\t\t}\n#ifndef GLSLONLY\n\t\telse\n\t\t\tBE_LegacyLighting();\n#endif\n\t\tbreak;\n\tcase BEM_GBUFFER:\n\t\taltshader = shaderstate.curshader->bemoverrides[bemoverride_gbuffer];\n\t\tif (altshader)\n\t\t{\n\t\t\tif (altshader->prog)\n\t\t\t{\n\t\t\t\tshaderstate.pendingcolourvbo = shaderstate.sourcevbo->colours[0].gl.vbo;\n\t\t\t\tshaderstate.pendingcolourpointer = shaderstate.sourcevbo->colours[0].gl.addr;\n\t\t\t\tshaderstate.colourarraytype = shaderstate.sourcevbo->colours_bytes?GL_UNSIGNED_BYTE:GL_FLOAT;\n\t\t\t\tshaderstate.pendingtexcoordparts[0] = 2;\n\t\t\t\tshaderstate.pendingtexcoordvbo[0] = shaderstate.sourcevbo->texcoord.gl.vbo;\n\t\t\t\tshaderstate.pendingtexcoordpointer[0] = shaderstate.sourcevbo->texcoord.gl.addr;\n\t\t\t\tBE_RenderMeshProgram(altshader, altshader->passes, altshader->prog);\n\t\t\t}\n\t\t\telse if (altshader->numpasses && altshader->passes[0].prog)\n\t\t\t{\n\t\t\t\tshaderstate.pendingcolourvbo = shaderstate.sourcevbo->colours[0].gl.vbo;\n\t\t\t\tshaderstate.pendingcolourpointer = shaderstate.sourcevbo->colours[0].gl.addr;\n\t\t\t\tshaderstate.colourarraytype = shaderstate.sourcevbo->colours_bytes?GL_UNSIGNED_BYTE:GL_FLOAT;\n\t\t\t\tshaderstate.pendingtexcoordparts[0] = 2;\n\t\t\t\tshaderstate.pendingtexcoordvbo[0] = shaderstate.sourcevbo->texcoord.gl.vbo;\n\t\t\t\tshaderstate.pendingtexcoordpointer[0] = shaderstate.sourcevbo->texcoord.gl.addr;\n\t\t\t\tBE_RenderMeshProgram(altshader, altshader->passes, altshader->passes[0].prog);\n\t\t\t}\n\t\t}\n\n\t\tbreak;\n#endif\n\tcase BEM_CREPUSCULAR:\n\t\taltshader = shaderstate.curshader->bemoverrides[bemoverride_crepuscular];\n\t\tif (!altshader && (shaderstate.curshader->flags & SHADER_SKY))\n\t\t\taltshader = shaderstate.crepskyshader;\n\t\tif (!altshader)\n\t\t\taltshader = shaderstate.crepopaqueshader;\n\n\t\tif (altshader && altshader->prog)\n\t\t{\n\t\t\tshaderstate.pendingcolourvbo = shaderstate.sourcevbo->colours[0].gl.vbo;\n\t\t\tshaderstate.pendingcolourpointer = shaderstate.sourcevbo->colours[0].gl.addr;\n\t\t\tshaderstate.colourarraytype = shaderstate.sourcevbo->colours_bytes?GL_UNSIGNED_BYTE:GL_FLOAT;\n\t\t\tshaderstate.pendingtexcoordparts[0] = 2;\n\t\t\tshaderstate.pendingtexcoordvbo[0] = shaderstate.sourcevbo->texcoord.gl.vbo;\n\t\t\tshaderstate.pendingtexcoordpointer[0] = shaderstate.sourcevbo->texcoord.gl.addr;\n\t\t\tBE_RenderMeshProgram(altshader, altshader->passes, altshader->prog);\n\t\t}\n\t\tbreak;\n\tcase BEM_DEPTHONLY:\n\t\taltshader = shaderstate.curshader->bemoverrides[bemoverride_depthonly];\n\t\tif (!altshader)\n\t\t\taltshader = shaderstate.depthonlyshader;\n\n\t\tif (altshader && altshader->prog)\n\t\t{\n\t\t\tshaderstate.pendingcolourvbo = shaderstate.sourcevbo->colours[0].gl.vbo;\n\t\t\tshaderstate.pendingcolourpointer = shaderstate.sourcevbo->colours[0].gl.addr;\n\t\t\tshaderstate.colourarraytype = shaderstate.sourcevbo->colours_bytes?GL_UNSIGNED_BYTE:GL_FLOAT;\n\t\t\tshaderstate.pendingtexcoordparts[0] = 2;\n\t\t\tshaderstate.pendingtexcoordvbo[0] = shaderstate.sourcevbo->texcoord.gl.vbo;\n\t\t\tshaderstate.pendingtexcoordpointer[0] = shaderstate.sourcevbo->texcoord.gl.addr;\n\t\t\tBE_RenderMeshProgram(altshader, altshader->passes, altshader->prog);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tGL_DeSelectProgram();\n#ifdef warningmsg\n#pragma warningmsg(\"fixme: support alpha test\")\n#endif\n\t\t\tBE_EnableShaderAttributes((1u<<VATTR_LEG_VERTEX), 0);\n\t\t\tBE_SubmitMeshChain(false);\t//fixme: dangerous\n\t\t}\n\t\tbreak;\n\n\tcase BEM_FOG:\n#ifndef GLSLONLY\n\t\tGenerateTCFog(0, NULL);\n\t\tBE_EnableShaderAttributes((1u<<VATTR_LEG_VERTEX) | (1u<<VATTR_LEG_COLOUR) | (1u<<VATTR_LEG_TMU0), 0);\n\t\tBE_SubmitMeshChain(false);\n#endif\n\t\tbreak;\n\n\tcase BEM_WIREFRAME:\n\t\tif (shaderstate.wireframeshader && shaderstate.wireframeshader->prog)\n\t\t{\n\t\t\tshaderstate.pendingcolourvbo = shaderstate.sourcevbo->colours[0].gl.vbo;\n\t\t\tshaderstate.pendingcolourpointer = shaderstate.sourcevbo->colours[0].gl.addr;\n\t\t\tshaderstate.colourarraytype = shaderstate.sourcevbo->colours_bytes?GL_UNSIGNED_BYTE:GL_FLOAT;\n\t\t\tshaderstate.pendingtexcoordparts[0] = 2;\n\t\t\tshaderstate.pendingtexcoordvbo[0] = shaderstate.sourcevbo->texcoord.gl.vbo;\n\t\t\tshaderstate.pendingtexcoordpointer[0] = shaderstate.sourcevbo->texcoord.gl.addr;\n\t\t\tBE_RenderMeshProgram(shaderstate.wireframeshader, shaderstate.wireframeshader->passes, shaderstate.wireframeshader->prog);\n\t\t}\n#ifndef GLSLONLY\n\t\telse if (!gl_config_nofixedfunc)\n\t\t{\n\t\t\tBE_SetPassBlendMode(0, PBM_REPLACE);\n\t\t\tGL_DeSelectProgram();\n\n\t\t\tshaderstate.pendingcolourvbo = 0;\n\t\t\tshaderstate.pendingcolourpointer = NULL;\n\t\t\tVector4Set(shaderstate.pendingcolourflat, 1, 1, 1, 1);\n\t\t\twhile(shaderstate.lastpasstmus>0)\n\t\t\t{\n\t\t\t\tGL_LazyBind(--shaderstate.lastpasstmus, r_nulltex);\n\t\t\t}\n\t\t\tBE_SendPassBlendDepthMask((shaderstate.curshader->passes[0].shaderbits & ~SBITS_BLEND_BITS) | SBITS_SRCBLEND_SRC_ALPHA | SBITS_DSTBLEND_ONE_MINUS_SRC_ALPHA | ((r_wireframe.ival == 1)?SBITS_MISC_NODEPTHTEST:0));\n\n\t\t\tBE_EnableShaderAttributes((1u<<VATTR_LEG_VERTEX) | (1u<<VATTR_LEG_COLOUR), 0);\n\t\t\tBE_SubmitMeshChain(false);\n\t\t}\n#endif\n\t\tbreak;\n\tcase BEM_DEPTHDARK:\n\t\tif ((shaderstate.curshader->flags & (SHADER_HASLIGHTMAP|SHADER_NODLIGHT))==SHADER_HASLIGHTMAP && !TEXVALID(shaderstate.curtexnums->fullbright))\n\t\t{\n\t\t\tif (gl_config.arb_shader_objects)\n\t\t\t{\n\t\t\t\tif (!shaderstate.allblackshader.glsl.handle)\n\t\t\t\t{\n\t\t\t\t\tconst char *defs[] = {NULL};\n\t\t\t\t\tshaderstate.allblackshader = GLSlang_CreateProgram(NULL, \"allblackprogram\", sh_config.minver, defs, \"#include \\\"sys/skeletal.h\\\"\\nvoid main(){gl_Position = skeletaltransform();}\", NULL, NULL, NULL, \"void main(){gl_FragColor=vec4(0.0,0.0,0.0,1.0);}\", false, NULL);\n\t\t\t\t\tshaderstate.allblack_mvp = qglGetUniformLocationARB(shaderstate.allblackshader.glsl.handle, \"m_modelviewprojection\");\n\t\t\t\t}\n\n\t\t\t\tGL_SelectProgram(shaderstate.allblackshader.glsl.handle);\n\t\t\t\tBE_SendPassBlendDepthMask(shaderstate.curshader->passes[0].shaderbits);\n\t\t\t\tBE_EnableShaderAttributes(gl_config_nofixedfunc?(1u<<VATTR_VERTEX1):(1u<<VATTR_LEG_VERTEX), 0);\n\t\t\t\tif (shaderstate.allblackshader.glsl.handle != shaderstate.lastuniform && shaderstate.allblack_mvp != -1)\n\t\t\t\t{\n\t\t\t\t\tfloat m16[16];\n\t\t\t\t\tMatrix4_Multiply(shaderstate.projectionmatrix, shaderstate.modelviewmatrix, m16);\n\t\t\t\t\tqglUniformMatrix4fvARB(shaderstate.allblack_mvp, 1, false, m16);\n\t\t\t\t}\n\t\t\t\tBE_SubmitMeshChain(shaderstate.allblackshader.glsl.usetesselation);\n\n\t\t\t\tshaderstate.lastuniform = shaderstate.allblackshader.glsl.handle;\n\t\t\t\tbreak;\n\t\t\t}\n#ifndef GLSLONLY\n\t\t\telse if (!gl_config_nofixedfunc)\n\t\t\t{\n\t\t\t\tGL_DeSelectProgram();\n\t\t\t\tshaderstate.pendingcolourvbo = 0;\n\t\t\t\tshaderstate.pendingcolourpointer = NULL;\n\t\t\t\tVector4Set(shaderstate.pendingcolourflat, 0, 0, 0, 1);\n\t\t\t\twhile(shaderstate.lastpasstmus>0)\n\t\t\t\t{\n\t\t\t\t\tGL_LazyBind(--shaderstate.lastpasstmus, r_nulltex);\n\t\t\t\t}\n\n\t\t\t\tBE_SetPassBlendMode(0, PBM_REPLACE);\n\t\t\t\tBE_SendPassBlendDepthMask(shaderstate.curshader->passes[0].shaderbits);\n\n\t\t\t\tBE_EnableShaderAttributes((1u<<VATTR_LEG_VERTEX) | (1u<<VATTR_LEG_COLOUR), 0);\n\t\t\t\tBE_SubmitMeshChain(false);\n\t\t\t\tbreak;\n\t\t\t}\n#endif\n\t\t}\n\t\t//fallthrough\n\tcase BEM_STANDARD:\n\tdefault:\n\t\tif (shaderstate.curshader->prog)\n\t\t{\n\t\t\tshaderstate.pendingcolourvbo = shaderstate.sourcevbo->colours[0].gl.vbo;\n\t\t\tshaderstate.pendingcolourpointer = shaderstate.sourcevbo->colours[0].gl.addr;\n\t\t\tshaderstate.colourarraytype = shaderstate.sourcevbo->colours_bytes?GL_UNSIGNED_BYTE:GL_FLOAT;\n\n\t\t\tshaderstate.pendingtexcoordparts[0] = 2;\n\t\t\tshaderstate.pendingtexcoordvbo[0] = shaderstate.sourcevbo->texcoord.gl.vbo;\n\t\t\tshaderstate.pendingtexcoordpointer[0] = shaderstate.sourcevbo->texcoord.gl.addr;\n\n\t\t\tBE_RenderMeshProgram(shaderstate.curshader, shaderstate.curshader->passes, shaderstate.curshader->prog);\n\t\t}\n\t\telse if (gl_config_nofixedfunc)\n\t\t{\n#ifdef FTE_TARGET_WEB\n\t\t\tint maxverts = 0, m;\n\t\t\tmesh_t *meshlist;\n\t\t\tfor (m = 0; m < shaderstate.meshcount; m++)\n\t\t\t{\n\t\t\t\tmeshlist = shaderstate.meshes[m];\n\t\t\t\tif (maxverts < meshlist->vbofirstvert + meshlist->numvertexes)\n\t\t\t\t\tmaxverts = meshlist->vbofirstvert + meshlist->numvertexes;\n\t\t\t}\n#endif\n\n\t\t\twhile (passno < shaderstate.curshader->numpasses)\n\t\t\t{\n\t\t\t\tint emumode;\n\t\t\t\tp = &shaderstate.curshader->passes[passno];\n\t\t\t\tpassno += p->numMergedPasses;\n\n\t\t\t\tif (p->prog)\n\t\t\t\t{\n\t\t\t\t\tshaderstate.pendingcolourvbo = shaderstate.sourcevbo->colours[0].gl.vbo;\n\t\t\t\t\tshaderstate.pendingcolourpointer = shaderstate.sourcevbo->colours[0].gl.addr;\n\t\t\t\t\tshaderstate.colourarraytype = shaderstate.sourcevbo->colours_bytes?GL_UNSIGNED_BYTE:GL_FLOAT;\n\n\t\t\t\t\tshaderstate.pendingtexcoordparts[0] = 2;\n\t\t\t\t\tshaderstate.pendingtexcoordvbo[0] = shaderstate.sourcevbo->texcoord.gl.vbo;\n\t\t\t\t\tshaderstate.pendingtexcoordpointer[0] = shaderstate.sourcevbo->texcoord.gl.addr;\n\n\t\t\t\t\tBE_RenderMeshProgram(shaderstate.curshader, p, p->prog);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\temumode = 0;\n\t\t\t\temumode = (p->shaderbits & SBITS_ATEST_BITS) >> SBITS_ATEST_SHIFT;\n\n\t\t\t\tGenerateColourMods(p);\n\t\t\t\tif (!shaderstate.colourarraytype)\n\t\t\t\t{\n\t\t\t\t\temumode |= 4;\n\t\t\t\t\tshaderstate.lastuniform = 0;\t//FIXME: s_colour uniform might be wrong.\n\t\t\t\t}\n#ifdef FTE_TARGET_WEB\n\t\t\t\telse if (!shaderstate.pendingcolourvbo && shaderstate.pendingcolourpointer)\n\t\t\t\t{\n\t\t\t\t\tshaderstate.streamid = (shaderstate.streamid + 1) & (sizeof(shaderstate.streamvbo)/sizeof(shaderstate.streamvbo[0]) - 1);\n\t\t\t\t\tGL_SelectVBO(shaderstate.pendingcolourvbo = shaderstate.streamvbo[shaderstate.streamid]);\n\t\t\t\t\tswitch(shaderstate.colourarraytype)\n\t\t\t\t\t{\n\t\t\t\t\tcase GL_FLOAT:\n\t\t\t\t\t\tqglBufferDataARB(GL_ARRAY_BUFFER_ARB, maxverts * sizeof(vec4_t), shaderstate.pendingcolourpointer, GL_STREAM_DRAW_ARB);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase GL_UNSIGNED_BYTE:\n\t\t\t\t\t\tqglBufferDataARB(GL_ARRAY_BUFFER_ARB, maxverts * sizeof(byte_vec4_t), shaderstate.pendingcolourpointer, GL_STREAM_DRAW_ARB);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tshaderstate.pendingcolourpointer = NULL;\n\t\t\t\t}\n#endif\n\n\t\t\t\tBE_GeneratePassTC(p, 0);\n#ifdef FTE_TARGET_WEB\n\t\t\t\tif (!shaderstate.pendingtexcoordvbo[0] && shaderstate.pendingtexcoordpointer[0])\n\t\t\t\t{\n\t\t\t\t\tshaderstate.streamid = (shaderstate.streamid + 1) & (sizeof(shaderstate.streamvbo)/sizeof(shaderstate.streamvbo[0]) - 1);\n\t\t\t\t\tGL_SelectVBO(shaderstate.pendingtexcoordvbo[0] = shaderstate.streamvbo[shaderstate.streamid]);\n\t\t\t\t\tqglBufferDataARB(GL_ARRAY_BUFFER_ARB, maxverts * sizeof(float) * shaderstate.pendingtexcoordparts[0], shaderstate.pendingtexcoordpointer[0], GL_STREAM_DRAW_ARB);\n\t\t\t\t\tshaderstate.pendingtexcoordpointer[0] = NULL;\n\t\t\t\t}\n#endif\n\n\t\t\t\tif (!shaderstate.programfixedemu[emumode])\n\t\t\t\t{\n\t\t\t\t\tchar *modes[] = {\n\t\t\t\t\t\t\"\",\"#ALPHATEST=>0.0\",\"#ALPHATEST=<0.5\",\"#ALPHATEST=>=0.5\",\n\t\t\t\t\t\t\"#UC\",\"#ALPHATEST=>0.0#UC\",\"#ALPHATEST=<0.5#UC\",\"#ALPHATEST=>=0.5#UC\"\n\t\t\t\t\t};\n\t\t\t\t\tshaderstate.programfixedemu[emumode] = Shader_FindGeneric(va(\"fixedemu%s\", modes[emumode]), QR_OPENGL);\n\t\t\t\t\tif (!shaderstate.programfixedemu[emumode])\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tBE_RenderMeshProgram(shaderstate.curshader, p, shaderstate.programfixedemu[emumode]);\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n#ifndef GLSLONLY\n\t\telse\n\t\t{\n\t\t\twhile (passno < shaderstate.curshader->numpasses)\n\t\t\t{\n\t\t\t\tp = &shaderstate.curshader->passes[passno];\n\t\t\t\tpassno += p->numMergedPasses;\n\t\t//\t\tif (p->flags & SHADER_PASS_DETAIL)\n\t\t//\t\t\tcontinue;\n\n\t\t\t\tif (p->prog)\n\t\t\t\t{\n\t\t\t\t\tshaderstate.pendingcolourvbo = shaderstate.sourcevbo->colours[0].gl.vbo;\n\t\t\t\t\tshaderstate.pendingcolourpointer = shaderstate.sourcevbo->colours[0].gl.addr;\n\t\t\t\t\tshaderstate.colourarraytype = shaderstate.sourcevbo->colours_bytes?GL_UNSIGNED_BYTE:GL_FLOAT;\n\n\t\t\t\t\tshaderstate.pendingtexcoordparts[0] = 2;\n\t\t\t\t\tshaderstate.pendingtexcoordvbo[0] = shaderstate.sourcevbo->texcoord.gl.vbo;\n\t\t\t\t\tshaderstate.pendingtexcoordpointer[0] = shaderstate.sourcevbo->texcoord.gl.addr;\n\n\t\t\t\t\tBE_RenderMeshProgram(shaderstate.curshader, p, p->prog);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tGL_DeSelectProgram();\n\t\t\t\t\tDrawPass(p);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (shaderstate.curbatch->fog && shaderstate.curbatch->fog->shader)\n\t\t{\n\t\t\t//FIXME: if glsl, do this fog volume crap properly!\n\n\t\t\tGL_DeSelectProgram();\n\n\t\t\tGenerateFogTexture(&shaderstate.fogtexture, shaderstate.curbatch->fog->shader->fog_dist, 2048);\n\t\t\tshaderstate.fogfar = 1.0f/2048; /*scaler for z coords*/\n\n\t\t\twhile(shaderstate.lastpasstmus>1)\n\t\t\t{\n\t\t\t\tGL_LazyBind(--shaderstate.lastpasstmus, r_nulltex);\n\t\t\t}\n\t\t\tGL_LazyBind(0, shaderstate.fogtexture);\n\t\t\tshaderstate.lastpasstmus = 1;\n\n\t\t\tVector4Scale(shaderstate.curbatch->fog->shader->fog_color, (1/255.0), shaderstate.pendingcolourflat);\n\t\t\tshaderstate.pendingcolourvbo = 0;\n\t\t\tshaderstate.pendingcolourpointer = NULL;\n\t\t\tBE_SetPassBlendMode(0, PBM_MODULATE);\n\t\t\tBE_SendPassBlendDepthMask(SBITS_SRCBLEND_SRC_ALPHA | SBITS_DSTBLEND_ONE_MINUS_SRC_ALPHA | (shaderstate.curshader->numpasses?SBITS_DEPTHFUNC_EQUAL:0));\n\n\t\t\tGenerateTCFog(0, shaderstate.curbatch->fog);\n\t\t\tBE_EnableShaderAttributes((1u<<VATTR_LEG_VERTEX) | (1u<<VATTR_LEG_COLOUR) | (1u<<VATTR_LEG_TMU0), 0);\n\t\t\tBE_SubmitMeshChain(false);\n\t\t}\n\t\tbreak;\n#endif\n\t}\n}\n\nstatic void BE_GenTempMeshVBO(vbo_t **vbo, mesh_t *m)\n{\n\t*vbo = &shaderstate.dummyvbo;\n\n\t//this code is shit shit shit.\n\tif (shaderstate.streamvbo[0])\n\t{\n\t\t//use a local. can't use a static, that crashes the compiler due to memory use, so lets eat the malloc or stack or whatever because we really don't have a choice.\n\t\tstatic char *buffer;\n\t\tsize_t len = 0;\n\t\tif (!buffer)\n\t\t\tbuffer = malloc(65536 * 33 * sizeof(float));\n\n\t\t//we're not doing vao... but just in case someone added that as an extension...\n\t\tGL_DeselectVAO();\n\n\t\t//cycle the vbo. this reduces the likelyhood that we'll have to wait for the vbo to no longer be in use.\n\t\t//remember, we can't do nice things like orphan in webgl. we have to create a new buffer every single time.\n\t\t//we can't stream with BufferSubData, because browsers are pure shite, and probably mmap and read back huge blocks of code as they translate the calls to direct3d nonsense.\n\t\t//we can't use client memory and let the driver do the right thing, because lets face it, our driver is technically a browser. its safer to run everything in a scripted language than to fight the nonsense.\n\t\t//how many buffers do we need? no idea. all this memory allocation is going to be shit for performance.\n\t\t//but we really do not have a choice. This is the only way to 'stream' without dropping down to <1fps.\n\t\t//although arguably we should build our entire hud+2d stuff into a single vbo each frame... meh. At least this keeps the memory use in the driver's 64bit memory space instead of the browser's 32bit one...\n\t\tshaderstate.streamid = (shaderstate.streamid + 1) & (sizeof(shaderstate.streamvbo)/sizeof(shaderstate.streamvbo[0]) - 1);\n\t\tshaderstate.dummyvbo.vao = shaderstate.streamvao[shaderstate.streamid];\n\t\tshaderstate.dummyvbo.vaodynamic = ~0;\n\t\tshaderstate.dummyvbo.vaoenabled = 0;\n\t\tif (shaderstate.dummyvbo.vao)\n\t\t{\n\t\t\tqglBindVertexArray(shaderstate.dummyvbo.vao);\n\t\t\tshaderstate.currentvao = shaderstate.dummyvbo.vao;\n\t\t}\n\t\tGL_SelectVBO(shaderstate.streamvbo[shaderstate.streamid]);\n\t\tGL_SelectEBO(shaderstate.streamebo[shaderstate.streamid]);\n\n\t\tmemcpy(buffer+len, m->xyz_array, sizeof(*m->xyz_array) * m->numvertexes);\n\t\tshaderstate.dummyvbo.coord.gl.addr = (void*)len;\n\t\tshaderstate.dummyvbo.coord.gl.vbo = shaderstate.streamvbo[shaderstate.streamid];\n\t\tlen += sizeof(*m->xyz_array) * m->numvertexes;\n\n\t\tif (m->xyz2_array)\n\t\t{\n\t\t\tmemcpy(buffer+len, m->xyz2_array, sizeof(*m->xyz2_array) * m->numvertexes);\n\t\t\tshaderstate.dummyvbo.coord2.gl.addr = (void*)len;\n\t\t\tshaderstate.dummyvbo.coord2.gl.vbo = shaderstate.streamvbo[shaderstate.streamid];\n\t\t\tlen += sizeof(*m->xyz2_array) * m->numvertexes;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tshaderstate.dummyvbo.coord2.gl.addr = NULL;\n\t\t\tshaderstate.dummyvbo.coord2.gl.vbo = 0;\n\t\t}\n\n\t\tmemcpy(buffer+len, m->st_array, sizeof(*m->st_array) * m->numvertexes);\n\t\tshaderstate.dummyvbo.texcoord.gl.addr = (void*)len;\n\t\tshaderstate.dummyvbo.texcoord.gl.vbo = shaderstate.streamvbo[shaderstate.streamid];\n\t\tlen += sizeof(*m->st_array) * m->numvertexes;\n\n\t\t//FIXME: lightmaps\n\n\t\tif (m->colors4f_array[0])\n\t\t{\n\t\t\tmemcpy(buffer+len, m->colors4f_array[0], sizeof(*m->colors4f_array[0]) * m->numvertexes);\n\t\t\tshaderstate.dummyvbo.colours[0].gl.addr = (void*)len;\n\t\t\tshaderstate.dummyvbo.colours[0].gl.vbo = shaderstate.streamvbo[shaderstate.streamid];\n\t\t\tlen += sizeof(*m->colors4f_array[0]) * m->numvertexes;\n\t\t\tshaderstate.dummyvbo.colours_bytes = false;\n\t\t}\n\t\telse if (m->colors4b_array)\n\t\t{\n\t\t\tmemcpy(buffer+len, m->colors4b_array, sizeof(*m->colors4b_array) * m->numvertexes);\n\t\t\tshaderstate.dummyvbo.colours[0].gl.addr = (void*)len;\n\t\t\tshaderstate.dummyvbo.colours[0].gl.vbo = shaderstate.streamvbo[shaderstate.streamid];\n\t\t\tlen += sizeof(*m->colors4b_array) * m->numvertexes;\n\t\t\tshaderstate.dummyvbo.colours_bytes = true;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tshaderstate.dummyvbo.colours[0].gl.addr = NULL;\n\t\t\tshaderstate.dummyvbo.colours[0].gl.vbo = 0;\n\t\t\tshaderstate.dummyvbo.colours_bytes = false;\n\t\t}\n\n\t\tif (m->normals_array)\n\t\t{\n\t\t\tmemcpy(buffer+len, m->normals_array, sizeof(*m->normals_array) * m->numvertexes);\n\t\t\tshaderstate.dummyvbo.normals.gl.addr = (void*)len;\n\t\t\tshaderstate.dummyvbo.normals.gl.vbo = shaderstate.streamvbo[shaderstate.streamid];\n\t\t\tlen += sizeof(*m->normals_array) * m->numvertexes;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tshaderstate.dummyvbo.normals.gl.addr = NULL;\n\t\t\tshaderstate.dummyvbo.normals.gl.vbo = 0;\n\t\t}\n\n\t\tif (m->snormals_array)\n\t\t{\n\t\t\tmemcpy(buffer+len, m->snormals_array, sizeof(*m->snormals_array) * m->numvertexes);\n\t\t\tshaderstate.dummyvbo.svector.gl.addr = (void*)len;\n\t\t\tshaderstate.dummyvbo.svector.gl.vbo = shaderstate.streamvbo[shaderstate.streamid];\n\t\t\tlen += sizeof(*m->snormals_array) * m->numvertexes;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tshaderstate.dummyvbo.svector.gl.addr = NULL;\n\t\t\tshaderstate.dummyvbo.svector.gl.vbo = 0;\n\t\t}\n\n\t\tif (m->tnormals_array)\n\t\t{\n\t\t\tmemcpy(buffer+len, m->tnormals_array, sizeof(*m->tnormals_array) * m->numvertexes);\n\t\t\tshaderstate.dummyvbo.tvector.gl.addr = (void*)len;\n\t\t\tshaderstate.dummyvbo.tvector.gl.vbo = shaderstate.streamvbo[shaderstate.streamid];\n\t\t\tlen += sizeof(*m->tnormals_array) * m->numvertexes;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tshaderstate.dummyvbo.tvector.gl.addr = NULL;\n\t\t\tshaderstate.dummyvbo.tvector.gl.vbo = 0;\n\t\t}\n\n\t\tif (m->bonenums)\n\t\t{\n\t\t\tmemcpy(buffer+len, m->bonenums, sizeof(*m->bonenums) * m->numvertexes);\n\t\t\tshaderstate.dummyvbo.bonenums.gl.addr = (void*)len;\n\t\t\tshaderstate.dummyvbo.bonenums.gl.vbo = shaderstate.streamvbo[shaderstate.streamid];\n\t\t\tlen += sizeof(*m->bonenums) * m->numvertexes;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tshaderstate.dummyvbo.bonenums.gl.addr = NULL;\n\t\t\tshaderstate.dummyvbo.bonenums.gl.vbo = 0;\n\t\t}\n\n\t\tif (m->boneweights)\n\t\t{\n\t\t\tmemcpy(buffer+len, m->boneweights, sizeof(*m->boneweights) * m->numvertexes);\n\t\t\tshaderstate.dummyvbo.boneweights.gl.addr = (void*)len;\n\t\t\tshaderstate.dummyvbo.boneweights.gl.vbo = shaderstate.streamvbo[shaderstate.streamid];\n\t\t\tlen += sizeof(*m->boneweights) * m->numvertexes;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tshaderstate.dummyvbo.boneweights.gl.addr = NULL;\n\t\t\tshaderstate.dummyvbo.boneweights.gl.vbo = 0;\n\t\t}\n\n\t\t//FIXME: normals, svector, tvector, bone nums, bone weights\n\n\t\t//now we've got a single buffer in a single place, update the buffer\n\t\tqglBufferDataARB(GL_ARRAY_BUFFER_ARB, len, buffer, GL_STREAM_DRAW_ARB);\n\n\n\n\t\t//and finally the elements array, which is a much simpler affair\n\t\tqglBufferDataARB(GL_ELEMENT_ARRAY_BUFFER_ARB, sizeof(*m->indexes) * m->numindexes, m->indexes, GL_STREAM_DRAW_ARB);\n\t\tshaderstate.dummyvbo.indicies.gl.addr = (void*)NULL;\n\t\tshaderstate.dummyvbo.indicies.gl.vbo = shaderstate.streamebo[shaderstate.streamid];\n\t}\n\telse\n\t{\n\t\t//client memory. may be slower. will probably be faster.\n\t\tshaderstate.dummyvbo.coord.gl.addr = m->xyz_array;\n\t\tshaderstate.dummyvbo.coord2.gl.addr = m->xyz2_array;\n\t\tshaderstate.dummyvbo.texcoord.gl.addr = m->st_array;\n\t\tshaderstate.dummyvbo.indicies.gl.addr = m->indexes;\n\t\tshaderstate.dummyvbo.normals.gl.addr = m->normals_array;\n\t\tshaderstate.dummyvbo.svector.gl.addr = m->snormals_array;\n\t\tshaderstate.dummyvbo.tvector.gl.addr = m->tnormals_array;\n\t\tif (m->colors4f_array[0])\n\t\t{\n\t\t\tshaderstate.dummyvbo.colours_bytes = false;\n\t\t\tshaderstate.dummyvbo.colours[0].gl.addr = m->colors4f_array[0];\n\t\t}\n\t\telse\n\t\t{\n\t\t\tshaderstate.dummyvbo.colours_bytes = true;\n\t\t\tshaderstate.dummyvbo.colours[0].gl.addr = m->colors4b_array;\n\t\t}\n\t\tshaderstate.dummyvbo.bonenums.gl.addr = m->bonenums;\n\t\tshaderstate.dummyvbo.boneweights.gl.addr = m->boneweights;\n\t}\n\tshaderstate.dummyvbo.bones = m->bones;\n\tshaderstate.dummyvbo.numbones = m->numbones;\n}\n\nvoid GLBE_DrawMesh_List(shader_t *shader, int nummeshes, mesh_t **meshlist, vbo_t *vbo, texnums_t *texnums, unsigned int beflags)\n{\n\tshaderstate.curbatch = &shaderstate.dummybatch;\n\tshaderstate.curshader = shader->remapto;\n\n\tif (!vbo)\n\t{\n\t\tmesh_t *m;\n\t\tshaderstate.flags = beflags;\n\t\tTRACE((\"GLBE_DrawMesh_List: shader %s\\n\", shader->name));\n\t\tif (shaderstate.curentity != &r_worldentity)\n\t\t\tGLBE_SelectEntity(&r_worldentity);\n\t\tshaderstate.curtime = shaderstate.updatetime - (shaderstate.curentity->shaderTime + shader->remaptime);\n\t\tif (texnums)\n\t\t\tshaderstate.curtexnums = texnums;\n\t\telse if (shader->numdefaulttextures)\n\t\t\tshaderstate.curtexnums = shader->defaulttextures + ((int)(shader->defaulttextures_fps * shaderstate.curtime) % shader->numdefaulttextures);\n\t\telse\n\t\t\tshaderstate.curtexnums = shader->defaulttextures;\n\n\t\twhile (nummeshes--)\n\t\t{\n\t\t\tm = *meshlist++;\n\n\t\t\tBE_GenTempMeshVBO(&shaderstate.sourcevbo, m);\n\n\t\t\tshaderstate.meshcount = 1;\n\t\t\tshaderstate.meshes = &m;\n\t\t\tDrawMeshes();\n\t\t}\n\t}\n\telse\n\t{\n\t\tshaderstate.sourcevbo = vbo;\n\t\tshaderstate.flags = beflags;\n\t\tif (shaderstate.curentity != &r_worldentity)\n\t\t\tGLBE_SelectEntity(&r_worldentity);\n\t\tshaderstate.curtime = shaderstate.updatetime - (shaderstate.curentity->shaderTime + shader->remaptime);\n\t\tif (texnums)\n\t\t\tshaderstate.curtexnums = texnums;\n\t\telse if (shader->numdefaulttextures)\n\t\t\tshaderstate.curtexnums = shader->defaulttextures + ((int)(shader->defaulttextures_fps * shaderstate.curtime) % shader->numdefaulttextures);\n\t\telse\n\t\t\tshaderstate.curtexnums = shader->defaulttextures;\n\n\t\tshaderstate.meshcount = nummeshes;\n\t\tshaderstate.meshes = meshlist;\n\t\tDrawMeshes();\n\t}\n}\nvoid GLBE_DrawMesh_Single(shader_t *shader, mesh_t *mesh, vbo_t *vbo, unsigned int beflags)\n{\n\tshader->next = NULL;\n\tGLBE_DrawMesh_List(shader, 1, &mesh, NULL, NULL, beflags);\n}\n\nvoid GLBE_SubmitBatch(batch_t *batch)\n{\n\tshader_t *sh;\n\tshaderstate.curbatch = batch;\n\tif (batch->vbo)\n\t{\n\t\tshaderstate.sourcevbo = batch->vbo;\n\n\t\tif (!batch->vbo->vao)\n\t\t\tbatch->vbo->vao = shaderstate.streamvao[0];\n\t\tbatch->vbo->vaodynamic = ~0;\n\t\tbatch->vbo->vaoenabled = 0;\n\t}\n\telse\n\t{\n\t\t//we're only allowed one mesh per batch if there's no vbo info.\n\t\tBE_GenTempMeshVBO(&shaderstate.sourcevbo, batch->mesh[0]);\n\t}\n\n\tsh = batch->shader;\n\tshaderstate.curshader = sh->remapto;\n\tshaderstate.flags = batch->flags;\n\tif (shaderstate.curentity != batch->ent)\n\t\tGLBE_SelectEntity(batch->ent);\n\tshaderstate.curtime = shaderstate.updatetime - (shaderstate.curentity->shaderTime + sh->remaptime);\n\tif (batch->skin)\n\t\tshaderstate.curtexnums = batch->skin;\n\telse if (sh->numdefaulttextures)\n\t\tshaderstate.curtexnums = sh->defaulttextures + ((int)(sh->defaulttextures_fps * shaderstate.curtime) % sh->numdefaulttextures);\n\telse\n\t\tshaderstate.curtexnums = sh->defaulttextures;\n\n\tif (0)\n\t{\n\t\tint i;\n\t\tfor (i = batch->firstmesh; i < batch->meshes; i++)\n\t\t{\n\t\t\tshaderstate.meshcount = 1;\n\t\t\tshaderstate.meshes = &batch->mesh[i];\n\t\t\tDrawMeshes();\n\t\t}\n\t}\n\telse\n\t{\n\t\tshaderstate.meshcount = batch->meshes - batch->firstmesh;\n\t\tshaderstate.meshes = batch->mesh+batch->firstmesh;\n\t\tDrawMeshes();\n\t}\n}\n\nstatic void GLBE_SubmitMeshesPortals(batch_t **worldlist, batch_t *dynamiclist)\n{\n\tbatch_t *batch, *masklists[2];\n\tint i;\n\tfloat il;\n\n\tif (!dynamiclist && !worldlist[SHADER_SORT_PORTAL])\n\t\treturn;\t//no portals to draw\n\n\t/*attempt to draw portal shaders*/\n\tif (shaderstate.mode == BEM_STANDARD)\n\t{\n\t\tfor (i = 0; i < 2; i++)\n\t\t{\n\t\t\tfor (batch = i?dynamiclist:worldlist[SHADER_SORT_PORTAL]; batch; batch = batch->next)\n\t\t\t{\n\t\t\t\tif (batch->meshes == batch->firstmesh)\n\t\t\t\t\tcontinue;\n\n\t\t\t\tif (batch->buildmeshes)\n\t\t\t\t\tbatch->buildmeshes(batch);\n\n\t\t\t\til = shaderstate.identitylighting;\n\t\t\t\tmasklists[0] = worldlist[SHADER_SORT_PORTAL];\n\t\t\t\tmasklists[1] = dynamiclist;\n\t\t\t\tGLR_DrawPortal(batch, worldlist, masklists, 0);\n\t\t\t\tshaderstate.identitylighting = il;\n\n\t\t\t\t/*clear depth again*/\n\t\t\t\tGL_ForceDepthWritable();\n\t\t\t\tqglClear(GL_DEPTH_BUFFER_BIT);\n\t\t\t}\n\t\t}\n\t\t//make sure the current scene doesn't draw over the portal where its not meant to. clamp depth so the near clip plane doesn't cause problems.\n\t\tif (gl_config.arb_depth_clamp)\n\t\t\tqglEnable(GL_DEPTH_CLAMP_ARB);\n\t\t/*draw depth only, to mask it off*/\n\t\tGLBE_SelectMode(BEM_DEPTHONLY);\n\t\tfor (i = 0; i < 2; i++)\n\t\t{\n\t\t\tfor (batch = i?dynamiclist:worldlist[SHADER_SORT_PORTAL]; batch; batch = batch->next)\n\t\t\t{\n//\t\t\t\tif (batch->meshes == batch->firstmesh)\n//\t\t\t\t\tcontinue;\n\n\t\t\t\tGLBE_SubmitBatch(batch);\n\t\t\t}\n\t\t}\n\t\tGLBE_SelectMode(BEM_STANDARD);\n\t\tif (gl_config.arb_depth_clamp)\n\t\t\tqglDisable(GL_DEPTH_CLAMP_ARB);\n\t}\n}\n\nstatic qboolean GLBE_GenerateBatchTextures(batch_t *batch, shader_t *bs)\n{\n\tint oldfbo;\n\tfloat oldil;\n\tint oldbem;\n\tif (r_refdef.recurse >= r_portalrecursion.ival || r_refdef.recurse == R_MAX_RECURSE)\n\t\treturn false;\n\t//these flags require rendering some view as an fbo\n\t//(BEM_DEPTHDARK is used when lightmap scale is 0, but still shows any emissive stuff)\n\tif (shaderstate.mode != BEM_STANDARD && shaderstate.mode != BEM_DEPTHDARK)\n\t\treturn false;\n\toldbem = shaderstate.mode;\n\toldil = shaderstate.identitylighting;\n\n\tif ((bs->flags & SHADER_HASREFLECT) && gl_config.ext_framebuffer_objects)\n\t{\n\t\tfloat renderscale = bs->portalfboscale;\n\t\tvrect_t orect = r_refdef.vrect;\n\t\tpxrect_t oprect = r_refdef.pxrect;\n\t\tif (!shaderstate.tex_reflection[r_refdef.recurse])\n\t\t{\n\t\t\tshaderstate.tex_reflection[r_refdef.recurse] = Image_CreateTexture(\"***tex_reflection***\", NULL, 0);\n\t\t\tif (!shaderstate.tex_reflection[r_refdef.recurse]->num)\n\t\t\t\tqglGenTextures(1, &shaderstate.tex_reflection[r_refdef.recurse]->num);\n\t\t}\n\n\t\tr_refdef.vrect.x = 0;\n\t\tr_refdef.vrect.y = 0;\n\t\tr_refdef.vrect.width = max(1, vid.fbvwidth * renderscale);\n\t\tr_refdef.vrect.height = max(1, vid.fbvheight * renderscale);\n\t\tr_refdef.pxrect.x = 0;\n\t\tr_refdef.pxrect.y = 0;\n\t\tr_refdef.pxrect.width = max(1, vid.fbpwidth * renderscale);\n\t\tr_refdef.pxrect.height = max(1, vid.fbpheight * renderscale);\n\t\tif (shaderstate.tex_reflection[r_refdef.recurse]->width!=r_refdef.pxrect.width || shaderstate.tex_reflection[r_refdef.recurse]->height!=r_refdef.pxrect.height)\n\t\t{\n\t\t\tshaderstate.tex_reflection[r_refdef.recurse]->width = r_refdef.pxrect.width;\n\t\t\tshaderstate.tex_reflection[r_refdef.recurse]->height = r_refdef.pxrect.height;\n\t\t\tGL_MTBind(0, GL_TEXTURE_2D, shaderstate.tex_reflection[r_refdef.recurse]);\n\n\t\t\tif ((vid.flags&VID_FP16) && sh_config.texfmt[PTI_RGBA16F])\n\t\t\t\tqglTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, shaderstate.tex_reflection[r_refdef.recurse]->width, shaderstate.tex_reflection[r_refdef.recurse]->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);\n\t\t\telse if ((vid.flags&(VID_SRGBAWARE|VID_FP16)) && sh_config.texfmt[PTI_RGBA8_SRGB])\n\t\t\t\tqglTexImage2D(GL_TEXTURE_2D, 0, GL_SRGB8_ALPHA8_EXT, shaderstate.tex_reflection[r_refdef.recurse]->width, shaderstate.tex_reflection[r_refdef.recurse]->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);\n\t\t\telse\n\t\t\t\tqglTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, shaderstate.tex_reflection[r_refdef.recurse]->width, shaderstate.tex_reflection[r_refdef.recurse]->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);\n\t\t\tqglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);\n\t\t\tqglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);\n\t\t\tqglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);\n\t\t\tqglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);\n\t\t}\n\t\toldfbo = GLBE_FBO_Update(&shaderstate.fbo_reflectrefrac[r_refdef.recurse], FBO_RB_DEPTH, &shaderstate.tex_reflection[r_refdef.recurse], 1, r_nulltex, shaderstate.tex_reflection[r_refdef.recurse]->width, shaderstate.tex_reflection[r_refdef.recurse]->height, 0);\n\t\tr_refdef.pxrect.maxheight = shaderstate.fbo_reflectrefrac[r_refdef.recurse].rb_size[1];\n\t\tGL_ViewportUpdate();\n\t\tGL_ForceDepthWritable();\n\t\tqglClearColor(0, 0, 0, 1);\n\t\tqglClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);\n\t\tGLR_DrawPortal(batch, cl.worldmodel->batches, NULL, 1);\n\t\tGLBE_FBO_Pop(oldfbo);\n\t\tr_refdef.vrect = orect;\n\t\tr_refdef.pxrect = oprect;\n\t\tGL_ViewportUpdate();\n\t}\n\tif (bs->flags & (SHADER_HASREFRACT|SHADER_HASREFRACTDEPTH))\n\t{\n\t\tif (r_refract_fboival || (bs->flags&SHADER_HASPORTAL))\n\t\t{\n\t\t\tfloat renderscale = min(1, bs->portalfboscale);\n\t\t\tvrect_t ovrect = r_refdef.vrect;\n\t\t\tpxrect_t oprect = r_refdef.pxrect;\n\t\t\tr_refdef.vrect.x = 0;\n\t\t\tr_refdef.vrect.y = 0;\n\t\t\tr_refdef.vrect.width = max(1, vid.fbvwidth * renderscale);\n\t\t\tr_refdef.vrect.height = max(1, vid.fbvheight * renderscale);\n\t\t\tr_refdef.pxrect.x = 0;\n\t\t\tr_refdef.pxrect.y = 0;\n\t\t\tr_refdef.pxrect.width = max(1, vid.fbpwidth * renderscale);\n\t\t\tr_refdef.pxrect.height = max(1, vid.fbpheight * renderscale);\n\n\t\t\tif (!shaderstate.tex_refraction[r_refdef.recurse])\n\t\t\t{\n\t\t\t\tshaderstate.tex_refraction[r_refdef.recurse] = Image_CreateTexture(\"***tex_refraction***\", NULL, 0);\n\t\t\t\tif (!shaderstate.tex_refraction[r_refdef.recurse]->num)\n\t\t\t\t\tqglGenTextures(1, &shaderstate.tex_refraction[r_refdef.recurse]->num);\n\t\t\t}\n\t\t\tif (shaderstate.tex_refraction[r_refdef.recurse]->width != r_refdef.pxrect.width || shaderstate.tex_refraction[r_refdef.recurse]->height != r_refdef.pxrect.height)\n\t\t\t{\n\t\t\t\tshaderstate.tex_refraction[r_refdef.recurse]->width = r_refdef.pxrect.width;\n\t\t\t\tshaderstate.tex_refraction[r_refdef.recurse]->height = r_refdef.pxrect.height;\n\t\t\t\tGL_MTBind(0, GL_TEXTURE_2D, shaderstate.tex_refraction[r_refdef.recurse]);\n\t\t\t\tif ((vid.flags&VID_FP16) && sh_config.texfmt[PTI_RGBA16F])\n\t\t\t\t\tqglTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, r_refdef.pxrect.width, r_refdef.pxrect.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);\n\t\t\t\telse if ((vid.flags&(VID_SRGBAWARE|VID_FP16)) && sh_config.texfmt[PTI_RGBA16F])\n\t\t\t\t\tqglTexImage2D(GL_TEXTURE_2D, 0, GL_SRGB8_ALPHA8_EXT, r_refdef.pxrect.width, r_refdef.pxrect.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);\n\t\t\t\telse\n\t\t\t\t\tqglTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, r_refdef.pxrect.width, r_refdef.pxrect.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);\n\t\t\t\tqglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);\n\t\t\t\tqglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);\n\t\t\t\tqglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);\n\t\t\t\tqglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);\n\t\t\t}\n\t\t\tif (bs->flags & SHADER_HASREFRACTDEPTH)\n\t\t\t{\n\t\t\t\tif (!shaderstate.tex_refractiondepth[r_refdef.recurse])\n\t\t\t\t{\n\t\t\t\t\tshaderstate.tex_refractiondepth[r_refdef.recurse] = Image_CreateTexture(\"***tex_refractiondepth***\", NULL, 0);\n\t\t\t\t\tif (!shaderstate.tex_refractiondepth[r_refdef.recurse]->num)\n\t\t\t\t\t\tqglGenTextures(1, &shaderstate.tex_refractiondepth[r_refdef.recurse]->num);\n\t\t\t\t}\n\t\t\t\tif (shaderstate.tex_refractiondepth[r_refdef.recurse]->width != r_refdef.pxrect.width || shaderstate.tex_refractiondepth[r_refdef.recurse]->height != r_refdef.pxrect.height)\n\t\t\t\t{\n\t\t\t\t\tshaderstate.tex_refractiondepth[r_refdef.recurse]->width = r_refdef.pxrect.width;\n\t\t\t\t\tshaderstate.tex_refractiondepth[r_refdef.recurse]->height = r_refdef.pxrect.height;\n\t\t\t\t\tGL_MTBind(0, GL_TEXTURE_2D, shaderstate.tex_refractiondepth[r_refdef.recurse]);\n\t\t\t\t\tqglTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24_ARB, r_refdef.pxrect.width, r_refdef.pxrect.height, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, NULL);\n\t\t\t\t\tqglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);\n\t\t\t\t\tqglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);\n\t\t\t\t\tqglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);\n\t\t\t\t\tqglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);\n\t\t\t\t}\n\t\t\t\toldfbo = GLBE_FBO_Update(&shaderstate.fbo_reflectrefrac[r_refdef.recurse], FBO_TEX_DEPTH, &shaderstate.tex_refraction[r_refdef.recurse], 1, shaderstate.tex_refractiondepth[r_refdef.recurse], r_refdef.pxrect.width, r_refdef.pxrect.height, 0);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\toldfbo = GLBE_FBO_Update(&shaderstate.fbo_reflectrefrac[r_refdef.recurse], FBO_RB_DEPTH, &shaderstate.tex_refraction[r_refdef.recurse], 1, r_nulltex, r_refdef.pxrect.width, r_refdef.pxrect.height, 0);\n\t\t\t}\n\t\t\tr_refdef.pxrect.maxheight = shaderstate.fbo_reflectrefrac[r_refdef.recurse].rb_size[1];\n\t\t\tGL_ViewportUpdate();\n\n\t\t\tGL_ForceDepthWritable();\n\t\t\tqglClearColor(0, 0, 0, 1);\n\t\t\tqglClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);\n\t\t\tif (bs->flags&SHADER_HASPORTAL)\n\t\t\t\tGLR_DrawPortal(batch, cl.worldmodel->batches, NULL, 0);\n\t\t\telse\n\t\t\t\tGLR_DrawPortal(batch, cl.worldmodel->batches, NULL, ((bs->flags & SHADER_HASREFRACTDEPTH)?3:2));\t//fixme\n\t\t\tGLBE_FBO_Pop(oldfbo);\n\n\t\t\tr_refdef.vrect = ovrect;\n\t\t\tr_refdef.pxrect = oprect;\n\t\t\tGL_ViewportUpdate();\n\t\t}\n\t\telse\n\t\t\tGLR_DrawPortal(batch, cl.worldmodel->batches, NULL, 3);\n\t}\n\tif ((bs->flags & SHADER_HASRIPPLEMAP) && gl_config.ext_framebuffer_objects)\n\t{\n\t\tfloat renderscale = bs->portalfboscale;\n\t\tvrect_t orect = r_refdef.vrect;\n\t\tpxrect_t oprect = r_refdef.pxrect;\n\t\tr_refdef.vrect.x = 0;\n\t\tr_refdef.vrect.y = 0;\n\t\tr_refdef.vrect.width = max(1, vid.fbvwidth * renderscale);\n\t\tr_refdef.vrect.height = max(1, vid.fbvheight * renderscale);\n\t\tr_refdef.pxrect.x = 0;\n\t\tr_refdef.pxrect.y = 0;\n\t\tr_refdef.pxrect.width = max(1, vid.fbpwidth * renderscale);\n\t\tr_refdef.pxrect.height = max(1, vid.fbpheight * renderscale);\n\n\t\tif (!shaderstate.tex_ripplemap[r_refdef.recurse])\n\t\t{\n\t\t\t//FIXME: can we use RGB8 instead?\n\t\t\tshaderstate.tex_ripplemap[r_refdef.recurse] = Image_CreateTexture(\"***tex_ripplemap***\", NULL, 0);\n\t\t\tif (!shaderstate.tex_ripplemap[r_refdef.recurse]->num)\n\t\t\t\tqglGenTextures(1, &shaderstate.tex_ripplemap[r_refdef.recurse]->num);\n\t\t}\n\t\tif (shaderstate.tex_ripplemap[r_refdef.recurse]->width != r_refdef.pxrect.width || shaderstate.tex_ripplemap[r_refdef.recurse]->height != r_refdef.pxrect.height)\n\t\t{\n\t\t\tshaderstate.tex_ripplemap[r_refdef.recurse]->width = r_refdef.pxrect.width;\n\t\t\tshaderstate.tex_ripplemap[r_refdef.recurse]->height = r_refdef.pxrect.height;\n\t\t\tGL_MTBind(0, GL_TEXTURE_2D, shaderstate.tex_ripplemap[r_refdef.recurse]);\n\t\t\tqglTexImage2D(GL_TEXTURE_2D, 0, /*(gl_config.glversion>3.1)?GL_RGBA8_SNORM:*/GL_RGBA16F, r_refdef.pxrect.width, r_refdef.pxrect.height, 0, GL_RGBA, GL_HALF_FLOAT, NULL);\n\t\t\tqglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);\n\t\t\tqglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);\n\t\t\tqglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);\n\t\t\tqglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);\n\t\t}\n\t\toldfbo = GLBE_FBO_Update(&shaderstate.fbo_reflectrefrac[r_refdef.recurse], 0, &shaderstate.tex_ripplemap[r_refdef.recurse], 1, r_nulltex, r_refdef.pxrect.width, r_refdef.pxrect.height, 0);\n\t\tr_refdef.pxrect.maxheight = shaderstate.fbo_reflectrefrac[r_refdef.recurse].rb_size[1];\n\t\tGL_ViewportUpdate();\n\n\t\tqglClearColor(0, 0, 0, 1);\n\t\tqglClear(GL_COLOR_BUFFER_BIT);\n\n//\t\tr_refdef.waterheight = DotProduct(batch->mesh[0]->xyz_array[0], batch->mesh[0]->normals_array[0]);\n\n\t\tr_refdef.recurse+=1; //paranoid, should stop potential infinite loops\n\t\tGLBE_SubmitMeshes(cl.worldmodel->batches, SHADER_SORT_RIPPLE, SHADER_SORT_RIPPLE);\n\t\tr_refdef.recurse-=1;\n\t\tGLBE_FBO_Pop(oldfbo);\n\n\t\tr_refdef.vrect = orect;\n\t\tr_refdef.pxrect = oprect;\n\t\tGL_ViewportUpdate();\n\t}\n\tBE_SelectMode(oldbem);\n\tshaderstate.identitylighting = oldil;\n\treturn true;\n}\nstatic void GLBE_SubmitMeshesSortList(batch_t *sortlist)\n{\n\tbatch_t *batch;\n\tshader_t *bs;\n\tfor (batch = sortlist; batch; batch = batch->next)\n\t{\n\t\tif (batch->meshes == batch->firstmesh)\n\t\t\tcontinue;\n\n\t\tif (batch->flags & BEF_NODLIGHT)\n\t\t\tif (shaderstate.mode == BEM_LIGHT)\n\t\t\t\tcontinue;\n\t\tif (batch->flags & BEF_NOSHADOWS)\n\t\t\tif (shaderstate.mode == BEM_STENCIL || shaderstate.mode == BEM_DEPTHONLY)\t//fixme: depthonly is not just shadows.\n\t\t\t\tcontinue;\n\n\t\t//buildmeshes updates shaders and generates pose information for sufaces that need it.\n\t\t//the shader flags checked *after* this call may be a performance issue if it generated lots of new mesh data.\n\t\t//FIXME: should we assume that the batch's shader will have the same flags?\n\t\tif (batch->buildmeshes)\n\t\t{\n\t\t\tTRACE((\"GLBE_SubmitMeshesSortList: build\\n\"));\n\t\t\tbatch->buildmeshes(batch);\n\t\t}\n\n\t\tbs = batch->shader;\n\n\t\tTRACE((\"GLBE_SubmitMeshesSortList: shader %s\\n\", bs->name));\n\n\t\t//FIXME:!!\n\t\tif (!bs)\n\t\t{\n\t\t\tCon_Printf(\"Shader not set...\\n\");\n\t\t\tif (batch->texture)\n\t\t\t\tbs = R_TextureAnimation(0, batch->texture)->shader;\n\t\t\telse\n\t\t\t\tcontinue;\n\t\t}\n\n\t\tif ((bs->flags & SHADER_NODRAW) || !batch->meshes)\n\t\t\tcontinue;\n\t\tif (bs->flags & SHADER_NODLIGHT)\n\t\t\tif (shaderstate.mode == BEM_LIGHT)\n\t\t\t\tcontinue;\n\t\tif (bs->flags & SHADER_NOSHADOWS)\n\t\t\tif (shaderstate.mode == BEM_STENCIL || shaderstate.mode == BEM_DEPTHONLY)\t//fixme: depthonly is not just shadows.\n\t\t\t\tcontinue;\n\t\tif (bs->flags & SHADER_SKY)\n\t\t{\n\t\t\tif (shaderstate.mode == BEM_STANDARD || shaderstate.mode == BEM_DEPTHDARK)// || shaderstate.mode == BEM_WIREFRAME)\n\t\t\t{\n\t\t\t\tfloat il = shaderstate.identitylighting;\t//this stuff sucks!\n\t\t\t\tif (R_DrawSkyChain(batch))\n\t\t\t\t{\n\t\t\t\t\tshaderstate.identitylighting = il;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tshaderstate.identitylighting = il;\n\t\t\t}\n\t\t\telse if (/*shaderstate.mode != BEM_FOG &&*/ shaderstate.mode != BEM_CREPUSCULAR && shaderstate.mode != BEM_WIREFRAME)\n\t\t\t\tcontinue;\n\t\t}\n\n\t\tif ((bs->flags & (SHADER_HASREFLECT | SHADER_HASREFRACT | SHADER_HASRIPPLEMAP)) && shaderstate.mode != BEM_WIREFRAME)\n\t\t{\n\t\t\tif (!GLBE_GenerateBatchTextures(batch, bs))\n\t\t\t\tcontinue;\n\t\t\tif ((bs->flags&SHADER_HASPORTAL) && shaderstate.mode != BEM_DEPTHONLY && gl_config.arb_depth_clamp)\n\t\t\t{\t//this little bit of code is meant to prevent issues when the near clip plane intersects the portal surface, allowing us to be that little bit closer to the portal.\n\t\t\t\tqglEnable(GL_DEPTH_CLAMP_ARB);\n\t\t\t\tGLBE_SubmitBatch(batch);\n\t\t\t\tqglDisable(GL_DEPTH_CLAMP_ARB);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\n\t\tGLBE_SubmitBatch(batch);\n\t}\n}\n\nvoid GLBE_SubmitMeshes (batch_t **worldbatches, int start, int stop)\n{\n\tint i;\n\tint portaldepth = r_portalrecursion.ival;\n\n\tfor (i = start; i <= stop; i++)\n\t{\n\t\tif (worldbatches)\n\t\t{\n\t\t\tif (i == SHADER_SORT_PORTAL && r_refdef.recurse < portaldepth)\n\t\t\t{\n\t\t\t\tGLBE_SubmitMeshesPortals(worldbatches, shaderstate.mbatches[i]);\n\n\t\t\t\tif (!r_refdef.recurse && r_portalonly.ival)\n\t\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tGLBE_SubmitMeshesSortList(worldbatches[i]);\n\t\t}\n\t\tGLBE_SubmitMeshesSortList(shaderstate.mbatches[i]);\n\t}\n}\n\n#if (defined(GLQUAKE) || defined(VKQUAKE)) && defined(MULTITHREAD)\n#define THREADEDWORLD\n#endif\n\nextern double r_loaderstalltime;\nvoid GLBE_UpdateLightmaps(void)\n{\n\tdouble starttime;\n\tlightmapinfo_t *lm;\n\tint lmidx;\n\n#ifdef THREADEDWORLD\n\textern int webo_blocklightmapupdates;\n\tif (webo_blocklightmapupdates == 3)\n\t\treturn;\t//we've not had a new scene to render yet. don't bother uploading while something's still painting, its going to be redundant.\n\twebo_blocklightmapupdates |= 2;\t//FIXME: round-robin it? one lightmap per frame?\n#endif\n\n\tstarttime = Sys_DoubleTime();\n\tfor (lmidx = 0; lmidx < numlightmaps; lmidx++)\n\t{\n\t\tlm = lightmap[lmidx];\n\t\tif (!lm)\n\t\t\tcontinue;\n\t\tif (lm->modified)\n\t\t{\n\t\t\tint t = lm->rectchange.t;\t//pull them out now, in the hopes that it'll be more robust with respect to r_dynamic -1\n\t\t\tint b = lm->rectchange.b;\n#ifdef _DEBUG\n\t\t\tif (t >= b)\n\t\t\t\tCon_Printf(\"Dodgy lightmaps\\n\");\n\t\t\telse\n#endif\n\t\t\tif (!TEXVALID(lm->lightmap_texture))\n\t\t\t{\n\t\t\t\textern cvar_t r_lightmap_nearest;\n\t\t\t\tTEXASSIGN(lm->lightmap_texture, Image_CreateTexture(va(\"***lightmap %i***\", lmidx), NULL, (r_lightmap_nearest.ival?IF_NEAREST:IF_LINEAR)|IF_NOMIPMAP));\n\t\t\t\tqglGenTextures(1, &lm->lightmap_texture->num);\n\t\t\t\tGL_MTBind(0, GL_TEXTURE_2D, lm->lightmap_texture);\n\t\t\t\tqglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);\n\t\t\t\tqglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);\n\t\t\t\tif (qglTexStorage2D && gl_config.formatinfo[lm->fmt].sizedformat)\n\t\t\t\t{\n\t\t\t\t\tqglTexStorage2D(GL_TEXTURE_2D, 1, gl_config.formatinfo[lm->fmt].sizedformat, lm->width, lm->height);\n\t\t\t\t\tif (lm->pbo_handle)\n\t\t\t\t\t{\n\t\t\t\t\t\tqglBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, lm->pbo_handle);\n\t\t\t\t\t\tqglTexSubImage2D(GL_TEXTURE_2D, 0, 0, t, lm->width, b-t, gl_config.formatinfo[lm->fmt].format, gl_config.formatinfo[lm->fmt].type, (char*)NULL + t*lm->width*lm->pixbytes);\n\t\t\t\t\t\tqglBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\tqglTexSubImage2D(GL_TEXTURE_2D, 0, 0, t, lm->width, b-t, gl_config.formatinfo[lm->fmt].format, gl_config.formatinfo[lm->fmt].type, lm->lightmaps+t*lm->width*lm->pixbytes);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tqglTexImage2D(GL_TEXTURE_2D, 0, gl_config.formatinfo[lm->fmt].internalformat,\tlm->width, lm->height, 0, gl_config.formatinfo[lm->fmt].format, gl_config.formatinfo[lm->fmt].type,\tlm->lightmaps);\n\n#ifndef FTE_TARGET_WEB\n\t\t\t\tif (gl_config.glversion >= (gl_config.gles?3.0:3.3))\n\t\t\t\t{\n\t\t\t\t\tqglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, gl_config.formatinfo[lm->fmt].swizzle_r);\n\t\t\t\t\tqglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_G, gl_config.formatinfo[lm->fmt].swizzle_g);\n\t\t\t\t\tqglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, gl_config.formatinfo[lm->fmt].swizzle_b);\n\t\t\t\t\tqglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_A, gl_config.formatinfo[lm->fmt].swizzle_a);\n\t\t\t\t}\n#endif\n\n\t\t\t\t//for completeness.\n\t\t\t\tlm->lightmap_texture->format = lm->fmt;\n\t\t\t\tlm->lightmap_texture->width = lm->width;\n\t\t\t\tlm->lightmap_texture->height = lm->height;\n\t\t\t\tlm->lightmap_texture->depth = 1;\n\t\t\t\tlm->lightmap_texture->status = TEX_LOADED;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tGL_MTBind(0, GL_TEXTURE_2D, lm->lightmap_texture);\n\t\t\t\tif (lm->pbo_handle)\n\t\t\t\t{\n\t\t\t\t\tqglBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, lm->pbo_handle);\n\t\t\t\t\tqglTexSubImage2D(GL_TEXTURE_2D, 0, 0, t, lm->width, b-t, gl_config.formatinfo[lm->fmt].format, gl_config.formatinfo[lm->fmt].type, (char*)NULL + t*lm->width*lm->pixbytes);\n\t\t\t\t\tqglBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tqglTexSubImage2D(GL_TEXTURE_2D, 0, 0, t, lm->width, b-t, gl_config.formatinfo[lm->fmt].format, gl_config.formatinfo[lm->fmt].type, lm->lightmaps+t*lm->width*lm->pixbytes);\n\t\t\t}\n\t\t\tlm->modified = false;\n\t\t\tlm->rectchange.l = lm->width;\n\t\t\tlm->rectchange.t = lm->height;\n\t\t\tlm->rectchange.r = 0;\n\t\t\tlm->rectchange.b = 0;\n\t\t}\n\t}\n\tr_loaderstalltime += Sys_DoubleTime()-starttime;\n}\n\nbatch_t *GLBE_GetTempBatch(void)\n{\n\tbatch_t *b;\n\tif (shaderstate.wbatch >= shaderstate.maxwbatches)\n\t{\n\t\tshaderstate.wbatch++;\n\t\treturn NULL;\n\t}\n\tb = &shaderstate.wbatches[shaderstate.wbatch++];\n\tb->fog = NULL;\n\treturn b;\n}\n\n/*called from shadowmapping code*/\n#ifdef RTLIGHTS\nvoid GLBE_BaseEntTextures(const qbyte *worldpvs, const int *worldareas)\n{\n\tbatch_t *batches[SHADER_SORT_COUNT];\n\tbatch_t **ob = shaderstate.mbatches;\n\tshaderstate.mbatches = batches;\n\tBE_GenModelBatches(batches, shaderstate.curdlight, shaderstate.mode, worldpvs, worldareas);\n\tGLBE_SubmitMeshes(NULL, SHADER_SORT_PORTAL, SHADER_SORT_SEETHROUGH+1);\n\tGLBE_SelectEntity(&r_worldentity);\n\tshaderstate.mbatches = ob;\n}\n#endif\n\nvoid GLBE_RenderToTextureUpdate2d(qboolean destchanged)\n{\n\tunsigned int width = 0, height = 0;\n\tif (destchanged)\n\t{\n\t\tif (*r_refdef.rt_destcolour[0].texname)\n\t\t{\n\t\t\ttexid_t tex = R2D_RT_GetTexture(r_refdef.rt_destcolour[0].texname, &width, &height);\n\t\t\tGLBE_FBO_Update(&shaderstate.fbo_2dfbo, 0, &tex, 1, r_nulltex, width, height, 0);\n\t\t}\n\t\telse\n\t\t\tGLBE_FBO_Push(NULL);\n\n\t\tGL_Set2D(false);\n\t}\n\telse\n\t{\n\t\tshaderstate.tex_sourcecol = R2D_RT_GetTexture(r_refdef.rt_sourcecolour.texname, &width, &height);\n\t\tshaderstate.tex_sourcedepth = R2D_RT_GetTexture(r_refdef.rt_depth.texname, &width, &height);\n\n\t\tshaderstate.tex_reflectcube = R_GetDefaultEnvmap();\n\t}\n}\nvoid GLBE_FBO_Sources(texid_t sourcecolour, texid_t sourcedepth)\n{\n\tshaderstate.tex_sourcecol = sourcecolour;\n\tshaderstate.tex_sourcedepth = sourcedepth;\n}\nint GLBE_FBO_Push(fbostate_t *state)\n{\n\tint newfbo;\n\tint oldfbo = shaderstate.fbo_current;\n\tif (state)\n\t\tnewfbo = state->fbo;\n\telse\n\t\tnewfbo = 0;\n\tif (shaderstate.fbo_current == newfbo)\t//don't bother if its not changed (also avoids crashes when fbos are not supported)\n\t\treturn oldfbo;\n\tqglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, shaderstate.fbo_current=newfbo);\n\treturn oldfbo;\n}\nvoid GLBE_FBO_Pop(int oldfbo)\n{\n\tqglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, oldfbo);\n\tshaderstate.fbo_current = oldfbo;\n}\n\nvoid GLBE_FBO_Destroy(fbostate_t *state)\n{\n\tif (state->fbo == shaderstate.fbo_current)\n\t\tGLBE_FBO_Push(NULL);\n\n\t//wasn't initialised anyway.\n\tif (!state->fbo)\n\t\treturn;\n\n\tqglDeleteFramebuffersEXT(1, &state->fbo);\n\tstate->fbo = 0;\n\n\tif (state->rb_depth)\n\t\tqglDeleteRenderbuffersEXT(1, &state->rb_depth);\n\tstate->rb_depth = 0;\n\tif (state->rb_stencil)\n\t\tqglDeleteRenderbuffersEXT(1, &state->rb_stencil);\n\tstate->rb_stencil = 0;\n\tif (state->rb_depthstencil)\n\t\tqglDeleteRenderbuffersEXT(1, &state->rb_depthstencil);\n\tstate->rb_depthstencil = 0;\n\n\tstate->enables = 0;\n}\n\n#ifdef RTLIGHTS\n#ifdef SHADOWDBG_COLOURNOTDEPTH\nvoid GLBE_BeginRenderBuffer_DepthOnly(texid_t depthtexture)\n{\n\tif (gl_config.ext_framebuffer_objects)\n\t{\n\t\tif (!shadow_fbo_id)\n\t\t{\n\t\t\tint drb;\n\t\t\tqglGenFramebuffersEXT(1, &shadow_fbo_id);\n\t\t\tqglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, shadow_fbo_id);\n\n\t\t\t//create an unnamed depth buffer\n//\t\t\tqglGenRenderbuffersEXT(1, &drb);\n//\t\t\tqglBindRenderbufferEXT(GL_RENDERBUFFER_EXT, drb);\n//\t\t\tqglRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT24_ARB, SHADOWMAP_SIZE*3, SHADOWMAP_SIZE*2);\n//\t\t\tqglFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, drb);\n\n\t\t\tif (qglDrawBuffer)\n\t\t\t\tqglDrawBuffer(GL_COLOR_ATTACHMENT0_EXT);\n\t\t\tif (qglReadBuffer)\n\t\t\t\tqglReadBuffer(GL_NONE);\n\t\t}\n\t\telse\n\t\t\tqglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, shadow_fbo_id);\n\n\t\tif (TEXVALID(depthtexture))\n\t\t\tqglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, depthtexture->num, 0);\n\t}\n}\n#else\nint GLBE_BeginRenderBuffer_DepthOnly(texid_t depthtexture)\n{\n\tint old = shaderstate.fbo_current;\n\tif (gl_config.ext_framebuffer_objects)\n\t{\n\t\tif (!shadow_fbo_id)\n\t\t{\n\t\t\tqglGenFramebuffersEXT(1, &shadow_fbo_id);\n\t\t\tqglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, shadow_fbo_id);\n\t\t\tif (qglDrawBuffers)\n\t\t\t\tqglDrawBuffers(0, NULL);\n\t\t\telse if (qglDrawBuffer)\n\t\t\t\tqglDrawBuffer(GL_NONE);\n\t\t\tif (qglReadBuffer)\n\t\t\t\tqglReadBuffer(GL_NONE);\n\t\t}\n\t\telse\n\t\t\tqglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, shadow_fbo_id);\n\t\tshaderstate.fbo_current = shadow_fbo_id;\n\n\t\tif (shadow_fbo_depth_num != depthtexture->num)\n\t\t{\n\t\t\tshadow_fbo_depth_num = depthtexture->num;\n\t\t\tif (TEXVALID(depthtexture))\n\t\t\t\tqglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, depthtexture->num, 0);\n\t\t}\n\t}\n\treturn old;\n}\n#endif\n#endif\n\n//state->colour is created if usedepth is set and it doesn't previously exist\nint GLBE_FBO_Update(fbostate_t *state, unsigned int enables, texid_t *destcol, int mrt, texid_t destdepth, int width, int height, int layer)\n{\n\tstatic GLenum allcolourattachments[] ={GL_COLOR_ATTACHMENT0_EXT,GL_COLOR_ATTACHMENT1_EXT,GL_COLOR_ATTACHMENT2_EXT,GL_COLOR_ATTACHMENT3_EXT,\n\t\t\t\t\t\t\t\t\tGL_COLOR_ATTACHMENT4_EXT,GL_COLOR_ATTACHMENT5_EXT,GL_COLOR_ATTACHMENT6_EXT,GL_COLOR_ATTACHMENT7_EXT};\n\tint i;\n\tint old;\n\n\tif (TEXVALID(destdepth))\n\t{\n\t\tenables |= FBO_TEX_DEPTH;\n\t\tenables &= ~FBO_RB_DEPTH;\n\t}\n\n\tenables |= (mrt<<16);\n\n\tif ((state->enables ^ enables) & ~FBO_RESET)\n\t{\n\t\tGLBE_FBO_Destroy(state);\n\t\tstate->enables = enables & ~FBO_RESET;\n\t\tenables |= FBO_RESET;\n\t}\n\n\tif (!state->fbo)\n\t{\n\t\tqglGenFramebuffersEXT(1, &state->fbo);\n\t\told = GLBE_FBO_Push(state);\n\t\tenables |= FBO_RESET;\n\t}\n\telse\n\t\told = GLBE_FBO_Push(state);\n\tif (state->rb_size[0] != width || state->rb_size[1] != height || (enables & FBO_RESET))\n\t{\n\t\tif (state->rb_depth && !(enables & FBO_RB_DEPTH))\n\t\t{\n\t\t\tqglDeleteRenderbuffersEXT(1, &state->rb_depth);\n\t\t\tstate->rb_depth = 0;\n\t\t}\n\t\tif (state->rb_stencil && !(enables & FBO_RB_STENCIL))\n\t\t{\n\t\t\tqglDeleteRenderbuffersEXT(1, &state->rb_stencil);\n\t\t\tstate->rb_stencil = 0;\n\t\t}\n\t\tstate->rb_size[0] = width;\n\t\tstate->rb_size[1] = height;\n\n\t\tenables |= FBO_RESET;\n\t\tif (mrt)\n\t\t{\t//be careful here, gles2 doesn't support glDrawBuffer. hopefully it'll make things up, but this is worrying.\n\t\t\tif (qglDrawBuffers)\n\t\t\t\tqglDrawBuffers(mrt, allcolourattachments);\n\t\t\telse if (qglDrawBuffer)\n\t\t\t\tqglDrawBuffer(GL_COLOR_ATTACHMENT0_EXT);\n\t\t\tif (qglReadBuffer)\n\t\t\t\tqglReadBuffer(GL_COLOR_ATTACHMENT0_EXT);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (qglDrawBuffers)\n\t\t\t\tqglDrawBuffers(0, NULL);\n\t\t\telse if (qglDrawBuffer)\n\t\t\t\tqglDrawBuffer(GL_NONE);\n\t\t\tif (qglReadBuffer)\n\t\t\t\tqglReadBuffer(GL_NONE);\n\t\t}\n\t}\n\tif (enables & FBO_TEX_DEPTH)\n\t{\n\t\tqglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, destdepth->num, 0);\n\t\t//fixme: no stencil\n\t}\n\telse if (enables & FBO_RB_DEPTH)\n\t{\n\t\tif (!state->rb_depth)\n\t\t{\n\t\t\t//create an unnamed depth buffer\n\t\t\tqglGenRenderbuffersEXT(1, &state->rb_depth);\n//\t\t\tif (!gl_config.ext_packed_depth_stencil)\n//\t\t\t\tqglGenRenderbuffersEXT(1, &state->rb_stencil);\n\t\t\tenables |= FBO_RESET;\t//make sure it gets instanciated\n\t\t}\n\n\t\tif (enables & FBO_RESET)\n\t\t{\n\t\t\tqglBindRenderbufferEXT(GL_RENDERBUFFER_EXT, state->rb_depth);\n\t\t\tif (gl_config.ext_packed_depth_stencil)\n\t\t\t\tqglRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH24_STENCIL8_EXT, state->rb_size[0], state->rb_size[1]);\n\t\t\telse\n\t\t\t{\n\t\t\t\tqglRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT16_ARB, state->rb_size[0], state->rb_size[1]);\n//\t\t\t\tqglBindRenderbufferEXT(GL_RENDERBUFFER_EXT, state->rb_stencil);\n//\t\t\t\tqglRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_STENCIL_INDEX8_EXT, state->rb_size[0], state->rb_size[1]);\n\t\t\t}\n\t\t\tqglFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, state->rb_depth);\n\t\t\tif (gl_config.ext_packed_depth_stencil)\n\t\t\t\tqglFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, state->rb_depth);\n\t\t\telse\n\t\t\t\tqglFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, state->rb_stencil);\n\t\t}\n\t}\n\n\tfor (i = 0; i < mrt; i++)\n\t{\n\t\tif ((destcol[i]->flags & IF_TEXTYPEMASK) == IF_TEXTYPE_CUBE)\n\t\t{\n\t\t\t//fixme: we should probably support whole-cubemap rendering for shadowmaps or something.\n\t\t\tqglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT+i, GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB + layer, destcol[i]->num, 0);\n\t\t}\n\t\telse\n\t\t{\t//layer does not make sense here\n\t\t\tqglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT+i, GL_TEXTURE_2D, destcol[i]->num, 0);\n\t\t}\n\t}\n\n\ti = qglCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);\n\tif (GL_FRAMEBUFFER_COMPLETE_EXT != i)\n\t{\n\t\tswitch(i)\n\t\t{\n\t\tcase GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT:\n\t\t\tCon_Printf(\"glCheckFramebufferStatus reported GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT\\n\");\n\t\t\tbreak;\n\t\tcase GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT:\n\t\t\tCon_Printf(\"glCheckFramebufferStatus reported GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT\\n\");\n\t\t\tbreak;\n\t\tcase GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT:\n\t\t\tCon_Printf(\"glCheckFramebufferStatus reported GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS\\n\");\n\t\t\tbreak;\n\t\tcase GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT:\n\t\t\tCon_Printf(\"glCheckFramebufferStatus reported GL_FRAMEBUFFER_INCOMPLETE_FORMATS\\n\");\n\t\t\tbreak;\n\t\tcase GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT:\n\t\t\tCon_Printf(\"glCheckFramebufferStatus reported GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER\\n\");\n\t\t\tbreak;\n\t\tcase GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT:\n\t\t\tCon_Printf(\"glCheckFramebufferStatus reported GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER\\n\");\n\t\t\tbreak;\n\t\tcase GL_FRAMEBUFFER_UNSUPPORTED_EXT:\n\t\t\tCon_Printf(\"glCheckFramebufferStatus reported GL_FRAMEBUFFER_UNSUPPORTED\\n\");\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tCon_Printf(\"glCheckFramebufferStatus returned %#x\\n\", i);\n\t\t\tbreak;\n\t\t}\n\n\t\tCon_Printf(\"FBO Enables: %x %i*%i\\n\", enables, width, height);\n\t\tfor (i = 0; i < mrt; i++)\n\t\t{\n\t\t\tif (destcol[i])\n\t\t\t{\n\t\t\t\tCon_Printf(\"^[\\\\imgptr\\\\%#\"PRIxSIZE\"^]\", (quintptr_t)destcol[i]);\n\t\t\t\tCon_Printf(\"Colour%i: \\\"%s\\\" %x %i*%i*%i%s %s\\n\", i, destcol[i]->ident, destcol[i]->flags, destcol[i]->width, destcol[i]->height, destcol[i]->depth, (destcol[i]->status != TEX_LOADED)?\" NOT LOADED\":\"\",  Image_FormatName(destcol[i]->format));\n\t\t\t}\n\t\t\telse\n\t\t\t\tCon_Printf(\"Colour%i: <NO IMAGE>\\n\", i);\n\t\t}\n\t\tif (enables & FBO_TEX_DEPTH && destdepth)\n\t\t{\n\t\t\tCon_Printf(\"^[\\\\imgptr\\\\%#\"PRIxSIZE\"^]\", (quintptr_t)destdepth);\n\t\t\tCon_Printf(\"Depth: \\\"%s\\\" %x %i*%i*%i%s %s\\n\", destdepth->ident, destdepth->flags, destdepth->width, destdepth->height, destdepth->depth, (destdepth->status != TEX_LOADED)?\" NOT LOADED\":\"\",  Image_FormatName(destdepth->format));\n\t\t}\n\t\telse if (enables & FBO_TEX_DEPTH)\n\t\t\tCon_Printf(\"Depth: <NO IMAGE>\\n\");\n\t\telse if (enables & FBO_RB_DEPTH)\n\t\t\tCon_Printf(\"Depth: <RenderBuffer>\\n\");\n\t}\n\treturn old;\n}\n/*\nvoid GLBE_RenderToTexture(texid_t sourcecol, texid_t sourcedepth, texid_t destcol, texid_t destdepth, qboolean usedepth)\n{\n\tshaderstate.tex_sourcecol = sourcecol;\n\tshaderstate.tex_sourcedepth = sourcedepth;\n\tif (!destcol.num)\n\t{\n\t\tshaderstate.fbo_current = 0;\n\t\tqglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, shaderstate.fbo_current);\n\t}\n\telse\n\t{\n\t\tif (usedepth)\n\t\t{\n\t\t\tif (!shaderstate.fbo_diffuse)\n\t\t\t{\n\t\t\t\tqglGenFramebuffersEXT(1, &shaderstate.fbo_diffuse);\n\t\t\t\tqglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, shaderstate.fbo_diffuse);\n\n\t\t\t\t//create an unnamed depth buffer\n\t\t\t\tqglGenRenderbuffersEXT(1, &shaderstate.rb_depth);\n\t\t\t\tqglBindRenderbufferEXT(GL_RENDERBUFFER_EXT, shaderstate.rb_depth);\n\t\t\t\tif (gl_config.ext_packed_depth_stencil)\n\t\t\t\t{\n\t\t\t\t\tqglFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, shaderstate.rb_depth);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tqglGenRenderbuffersEXT(1, &shaderstate.rb_stencil);\n\t\t\t\t\tqglBindRenderbufferEXT(GL_RENDERBUFFER_EXT, shaderstate.rb_stencil);\n\t\t\t\t\tqglFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, shaderstate.rb_stencil);\n\t\t\t\t}\n\t\t\t\tqglFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, shaderstate.rb_depth);\n\n\t\t\t\tqglDrawBuffer(GL_COLOR_ATTACHMENT0_EXT);\n\t\t\t\tqglReadBuffer(GL_NONE);\n\t\t\t}\n\t\t\telse\n\t\t\t\tqglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, shaderstate.fbo_diffuse);\n\t\t\tshaderstate.fbo_current = shaderstate.fbo_diffuse;\n\n\t\t\tif (destdepth.num)\n\t\t\t{\n\t\t\t\tqglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, destdepth.num, 0);\n\t\t\t\tqglFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, 0);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t//resize the depth renderbuffer if its the wrong size now\n\t\t\t\tif (shaderstate.rb_depth_size[0] != r_refdef.fbo_width || shaderstate.rb_depth_size[1] != r_refdef.fbo_height)\n\t\t\t\t{\n\t\t\t\t\tshaderstate.rb_depth_size[0] = r_refdef.fbo_width;\n\t\t\t\t\tshaderstate.rb_depth_size[1] = r_refdef.fbo_height;\n\n\t\t\t\t\tqglBindRenderbufferEXT(GL_RENDERBUFFER_EXT, shaderstate.rb_depth);\n\t\t\t\t\tif (gl_config.ext_packed_depth_stencil)\n\t\t\t\t\t{\n\t\t\t\t\t\tqglRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH24_STENCIL8_EXT, r_refdef.fbo_width, r_refdef.fbo_height);\n\t\t\t\t\t\tqglFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, shaderstate.rb_depth);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tqglRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT24_ARB, r_refdef.fbo_width, r_refdef.fbo_height);\n\n\t\t\t\t\t\tqglBindRenderbufferEXT(GL_RENDERBUFFER_EXT, shaderstate.rb_stencil);\n\t\t\t\t\t\tqglRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_STENCIL_INDEX8_EXT, r_refdef.fbo_width, r_refdef.fbo_height);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tqglFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, shaderstate.rb_depth);\n\t\t\t\tif (gl_config.ext_packed_depth_stencil)\n\t\t\t\t\tqglFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, shaderstate.rb_depth);\n\t\t\t\telse\n\t\t\t\t\tqglFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, shaderstate.rb_stencil);\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (!shaderstate.fbo_depthless)\n\t\t\t{\n\t\t\t\tqglGenFramebuffersEXT(1, &shaderstate.fbo_depthless);\n\t\t\t\tqglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, shaderstate.fbo_depthless);\n\n\t\t\t\tqglDrawBuffer(GL_COLOR_ATTACHMENT0_EXT);\n\t\t\t\tqglReadBuffer(GL_NONE);\n\t\t\t}\n\t\t\telse\n\t\t\t\tqglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, shaderstate.fbo_depthless);\n\n\t\t\tshaderstate.fbo_current = shaderstate.fbo_depthless;\n\t\t}\n\t\t\n\t\tqglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, destcol.num, 0);\n\t}\n}\n*/\n\nvoid GLBE_DrawLightPrePass(void)\n{\n\tcvar_t *var;\n\tunsigned int i;\n\tqboolean redefine = false;\n\ttexid_t depth, targets[countof(shaderstate.tex_gbuf)];\n\tconst char *s;\n\tint w = r_refdef.pxrect.width, h = r_refdef.pxrect.height;\n\t/*\n\twalls(bumps) -> normalbuffer\n\tlights+normalbuffer -> lightlevelbuffer\n\twalls(diffuse)+lightlevelbuffer -> screen\n\n\tnormalbuffer contains depth in the alpha channel. an actual depthbuffer is also generated at this time, which is used for depth test stuff but not as a shader input.\n\t*/\n\tint oldfbo;\n\n\tif (r_refdef.recurse)\n\t\treturn;\t//fixme: messy stuff...\n\n\t/*do portals*/\n\tBE_SelectMode(BEM_STANDARD);\n\tGLBE_SubmitMeshes(cl.worldmodel->batches, SHADER_SORT_PORTAL, SHADER_SORT_PORTAL);\n\n\tBE_SelectMode(BEM_GBUFFER);\n\tfor (i = 0; i < countof(shaderstate.tex_gbuf); i++)\n\t{\n\t\tif (!TEXVALID(shaderstate.tex_gbuf[i]) || w != shaderstate.tex_gbuf[i]->width || h != shaderstate.tex_gbuf[i]->height)\n\t\t{\n\t\t\tif (!shaderstate.tex_gbuf[i])\n\t\t\t{\n\t\t\t\tshaderstate.tex_gbuf[i] = Image_CreateTexture(va(\"***gbuffer %u***\", i), NULL, IF_CLAMP|IF_NEAREST|IF_NOMIPMAP|IF_RENDERTARGET);\n\t\t\t\tqglGenTextures(1, &shaderstate.tex_gbuf[i]->num);\n\t\t\t}\n\t\t\tshaderstate.tex_gbuf[i]->width = w;\n\t\t\tshaderstate.tex_gbuf[i]->height = h;\n\t\t\tredefine = true;\n\t\t}\n\t}\n\n\t//something changed, redefine the textures.\n\tif (redefine)\n\t{\n\t\tstatic const char *defualtfmts[countof(shaderstate.tex_gbuf)] =\n\t\t\t//depth,\tnormals,\tdifflight,\tspeclight\n\t\t\t{\"depth\",\t\"rgba16f\",\t\"rgba16f\",\t\"rgba8\",\t\"\", \"\", \"\", \"\"};\n\n\t\tcheckglerror();\n\n\t\tfor (i = 0; i < countof(shaderstate.tex_gbuf); i++)\n\t\t{\n\t\t\tGLint ifmt = 0;\n\t\t\tGLenum dfmt = GL_RGBA;\n\t\t\tGLenum dtype = GL_UNSIGNED_BYTE;\n\t\t\tvar = Cvar_Get(va(\"gl_deferred_gbuffmt_%i\", i), defualtfmts[i]?defualtfmts[i]:\"\", 0, \"Deferred Rendering\");\n\t\t\tif (!var)\n\t\t\t\tcontinue;\n\t\t\tif (!strcmp(var->string, \"rgba32f\"))\n\t\t\t{\n\t\t\t\tif (gl_config_gles)\n\t\t\t\t{\t//gles3\n\t\t\t\t\tifmt = GL_RGBA32F;\n\t\t\t\t\tdfmt = GL_RGBA;\n\t\t\t\t\tdtype = GL_FLOAT;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tifmt = GL_RGBA32F;\n\t\t\t}\n\t\t\telse if (!strcmp(var->string, \"rgba16f\"))\n\t\t\t{\n\t\t\t\tif (gl_config_gles)\n\t\t\t\t{\t//gles3\n\t\t\t\t\tifmt = GL_RGBA16F;\n\t\t\t\t\tdfmt = GL_RGBA;\n\t\t\t\t\tdtype = GL_HALF_FLOAT;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tifmt = GL_RGBA16F;\n\t\t\t}\n//\t\t\telse if (!strcmp(var->string, \"rgba8s\"))\n//\t\t\t\tifmt = GL_RGBA8_SNORM;\n\t\t\telse if (!strcmp(var->string, \"depth\"))\n\t\t\t{\n\t\t\t\tdtype = GL_UNSIGNED_INT;\n\t\t\t\tifmt = GL_DEPTH_COMPONENT;\n\t\t\t\tdfmt = GL_DEPTH_COMPONENT;\n\t\t\t}\n\t\t\telse if (!strcmp(var->string, \"depth16\"))\n\t\t\t{\n\t\t\t\tif (gl_config_gles)\n\t\t\t\t{\n\t\t\t\t\tdtype = GL_UNSIGNED_SHORT;\n\t\t\t\t\tifmt = GL_DEPTH_COMPONENT;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tifmt = GL_DEPTH_COMPONENT16_ARB;\n\t\t\t\tdfmt = GL_DEPTH_COMPONENT;\n\t\t\t}\n\t\t\telse if (!strcmp(var->string, \"depth24\"))\n\t\t\t{\n\t\t\t\tif (gl_config_gles)\n\t\t\t\t{\n\t\t\t\t\tdtype = GL_UNSIGNED_INT;\n\t\t\t\t\tifmt = GL_DEPTH_COMPONENT;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tifmt = GL_DEPTH_COMPONENT24_ARB;\n\t\t\t\tdfmt = GL_DEPTH_COMPONENT;\n\t\t\t}\n\t\t\telse if (!strcmp(var->string, \"depth32\"))\n\t\t\t{\n\t\t\t\tif (gl_config_gles)\n\t\t\t\t{\n\t\t\t\t\tdtype = GL_FLOAT;\n\t\t\t\t\tifmt = GL_DEPTH_COMPONENT;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tifmt = GL_DEPTH_COMPONENT32_ARB;\n\t\t\t\tdfmt = GL_DEPTH_COMPONENT;\n\t\t\t}\n\t\t\telse if (!strcmp(var->string, \"rgb565\"))\n\t\t\t{\n\t\t\t\tdtype = GL_UNSIGNED_SHORT_5_6_5;\n\t\t\t\tifmt = GL_RGB;\n\t\t\t\tdfmt = GL_RGB;\n\t\t\t}\n\t\t\telse if (!strcmp(var->string, \"rgba4\"))\n\t\t\t{\n\t\t\t\tdtype = GL_UNSIGNED_SHORT_4_4_4_4;\n\t\t\t\tifmt = GL_RGBA;\n\t\t\t\tdfmt = GL_RGBA;\n\t\t\t}\n\t\t\telse if (!strcmp(var->string, \"rgba5551\"))\n\t\t\t{\n\t\t\t\tdtype = GL_UNSIGNED_SHORT_5_5_5_1;\n\t\t\t\tifmt = GL_RGBA;\n\t\t\t\tdfmt = GL_RGBA;\n\t\t\t}\n\t\t\telse if (!strcmp(var->string, \"rgba8\") || *var->string)\n\t\t\t{\n#ifndef GLESONLY\n\t\t\t\tif (!gl_config_gles)\n\t\t\t\t\tifmt = GL_RGBA8;\n\t\t\t\telse\n#endif\n\t\t\t\t\tifmt = GL_RGBA;\n\t\t\t\tdfmt = GL_RGBA;\n\t\t\t}\n\t\t\telse\n\t\t\t\tcontinue;\n\n\t\t\tshaderstate.tex_gbuf[i]->status = TEX_LOADED;\n\t\t\tGL_MTBind(0, GL_TEXTURE_2D, shaderstate.tex_gbuf[i]);\n\t\t\tqglTexImage2D(GL_TEXTURE_2D, 0, ifmt, w, h, 0, dfmt, dtype, NULL);\n\t\t\tqglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);\n\t\t\tqglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);\n\n\t\t\tif (qglGetError())\n\t\t\t\tCon_Printf(\"unable to configure gbuffer image as '%s'\\n\", var->string);\n\t\t}\n\t}\n\n\t/*set the FB up to draw surface info*/\n\tvar = Cvar_Get2(\"gl_deferred_pre_depth\", \"0\", 0, \"gbuffer index used for depth. negative means to use an annonamous renderbuffer\", \"Deferred Rendering\");\n\tif (var->ival < 0 || var->ival >= countof(shaderstate.tex_gbuf))\n\t\tdepth = r_nulltex;\n\telse\n\t\tdepth = shaderstate.tex_gbuf[var->ival];\n\tvar = Cvar_Get2(\"gl_deferred_pre_targets\", \"1\", 0, \"space-separated list of gbuffer indexes to use for deferred surface information\", \"Deferred Rendering\");\n\tfor (i = 0, s = var->string; *s && i < countof(targets); )\n\t{\n\t\tchar token[32];\n\t\tint b;\n\t\ts = COM_ParseOut(s, token, sizeof(token));\n\t\tif (!*token)\n\t\t\tcontinue;\n\t\tb = atoi(token);\n\t\tif (b >= 0 && b < countof(shaderstate.tex_gbuf))\n\t\t\ttargets[i++] = shaderstate.tex_gbuf[b];\n\t}\n\n\toldfbo = GLBE_FBO_Update(&shaderstate.fbo_lprepass, depth?FBO_TEX_DEPTH:FBO_RB_DEPTH, targets, i, depth, w, h, 0);\n\tif (GL_FRAMEBUFFER_COMPLETE_EXT != qglCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT))\n\t{\n\t\tCon_Printf(\"Bad framebuffer\\n\");\n\t\treturn;\n\t}\n\tGL_ForceDepthWritable();\n\t//FIXME: should probably clear colour buffer too.\n\tqglClear(GL_DEPTH_BUFFER_BIT);\n\n\t/*draw surfaces that can be drawn this way*/\n\tGLBE_SubmitMeshes(cl.worldmodel->batches, SHADER_SORT_OPAQUE, SHADER_SORT_OPAQUE);\n\n\t/*reconfigure - now drawing diffuse light info using the previous fb image as a source image*/\n\tvar = Cvar_Get2(\"gl_deferred_light_targets\", \"2 3\", 0, \"space-separated list of gbuffer indexes for lighting to write to\", \"Deferred Rendering\");\n\tfor (i = 0, s = var->string; *s && i < countof(targets); )\n\t{\n\t\tchar token[32];\n\t\tint b;\n\t\ts = COM_ParseOut(s, token, sizeof(token));\n\t\tif (!*token)\n\t\t\tcontinue;\n\t\tb = atoi(token);\n\t\tif (b >= 0 && b < countof(shaderstate.tex_gbuf))\n\t\t\ttargets[i++] = shaderstate.tex_gbuf[b];\n\t}\n\tGLBE_FBO_Update(&shaderstate.fbo_lprepass, depth?FBO_TEX_DEPTH:FBO_RB_DEPTH, targets, i, depth, w, h, 0);\n\n\tBE_SelectMode(BEM_STANDARD);\n\tqglClearColor (0,0,0,0);\n\tqglClear(GL_COLOR_BUFFER_BIT);\n\n\tGLBE_SelectEntity(&r_worldentity);\n\t/*now draw the prelights*/\n\tGLBE_SubmitMeshes(cl.worldmodel->batches, SHADER_SORT_DEFERREDLIGHT, SHADER_SORT_DEFERREDLIGHT);\n\n\t/*final reconfigure - now drawing final surface data onto true framebuffer*/\n\tGLBE_FBO_Pop(oldfbo);\n\tif (!oldfbo && qglDrawBuffer)\n\t\tqglDrawBuffer(GL_BACK);\n\n\t/*now draw the postlight passes (this includes blended stuff which will NOT be lit)*/\n\tGLBE_SelectEntity(&r_worldentity);\n\tGLBE_SubmitMeshes(cl.worldmodel->batches, SHADER_SORT_SKY, SHADER_SORT_NEAREST);\n\n#ifdef RTLIGHTS\n\t/*regular lighting now*/\n\tGLBE_SelectEntity(&r_worldentity);\n\tSh_DrawLights(r_refdef.scenevis);\n#endif\n\n\tqglClearColor (1,0,0,1);\n}\n\nqboolean R_DrawSkyroom(shader_t *skyshader);\n\nvoid GLBE_DrawWorld (batch_t **worldbatches)\n{\n#ifdef RTLIGHTS\n\textern cvar_t r_shadow_realtime_world, r_shadow_realtime_world_lightmaps;\n#endif\n\tbatch_t *batches[SHADER_SORT_COUNT];\n\tbatch_t **ob = shaderstate.mbatches;\n\tRSpeedLocals();\n\tshaderstate.mbatches = batches;\n\n\tshaderstate.usingweaponviewmatrix = -1;\n\n\tTRACE((\"GLBE_DrawWorld: %p\\n\", worldbatches));\n\n\t//reset batches if we needed more mem, to avoid allocations mid-frame.\n\tif (!r_refdef.recurse)\n\t{\n\t\tif (shaderstate.wbatch + 50 > shaderstate.maxwbatches)\n\t\t{\n\t\t\tint newm = shaderstate.wbatch + 100;\n\t\t\tshaderstate.wbatches = BZ_Realloc(shaderstate.wbatches, newm * sizeof(*shaderstate.wbatches));\n\t\t\tmemset(shaderstate.wbatches + shaderstate.maxwbatches, 0, (newm - shaderstate.maxwbatches) * sizeof(*shaderstate.wbatches));\n\t\t\tshaderstate.maxwbatches = newm;\n\t\t}\n\n\t\tshaderstate.wbatch = 0;\n\t}\n\t//if the video mode changed, update any fbos (hopefully this won't happen on mirrors)\n\tif (shaderstate.oldwidth != vid.pixelwidth || shaderstate.oldheight != vid.pixelheight)\n\t{\n\t\tGLBE_DestroyFBOs();\t//will be recreated on demand\n\t\tshaderstate.oldwidth = vid.pixelwidth;\n\t\tshaderstate.oldheight = vid.pixelheight;\n\n\t\twhile(shaderstate.lastpasstmus>0)\n\t\t{\n\t\t\tGL_LazyBind(--shaderstate.lastpasstmus, r_nulltex);\n\t\t}\n#ifdef RTLIGHTS\n\t\tSh_Reset();\n#endif\n\t}\n\n\t//memset(batches, 0, sizeof(batches));\n\tBE_GenModelBatches(batches, shaderstate.curdlight, BEM_STANDARD, r_refdef.scenevis, r_refdef.sceneareas);\n\tR_GenDlightBatches(batches);\n\tshaderstate.curentity = &r_worldentity;\n//\tif (cl.paused || cls.state < ca_active)\n\t\tshaderstate.updatetime = r_refdef.time;\n//\telse\n//\t\tshaderstate.updatetime = cl.servertime;\n\n\tGLBE_UpdateLightmaps();\n\tif (worldbatches)\n\t{\n\t\tif (worldbatches[SHADER_SORT_SKY] && r_refdef.skyroom_enabled)\n\t\t{\n\t\t\tbatch_t *b;\n\t\t\tfor (b = worldbatches[SHADER_SORT_SKY]; b; b = b->next)\n\t\t\t\tif (R_DrawSkyroom(b->shader))\n\t\t\t\t{\n\t\t\t\t\tGL_CullFace(0);//make sure flipcull reversion takes effect\n\t\t\t\t\tcurrententity = NULL;\n\t\t\t\t\tGLBE_SelectEntity(&r_worldentity);\n\t\t\t\t\tGL_ForceDepthWritable();\n\t\t\t\t\tqglClear(GL_DEPTH_BUFFER_BIT);\n\t\t\t\t\tr_refdef.flags |= RDF_SKIPSKY;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t}\n\t\tif (gl_overbright.modified)\n\t\t{\n\t\t\tint i;\n\t\t\tgl_overbright.modified = false;\n\t\t\tif (gl_overbright.ival > 2)\n\t\t\t\tgl_overbright.ival = 2;\n\n\t\t\tfor (i = 0; i < SHADER_TMU_MAX; i++)\n\t\t\t\tshaderstate.blendmode[i] = -1;\n\t\t}\n\n#ifdef RTLIGHTS\n\t\tif (worldbatches && r_shadow_realtime_world.ival)\n\t\t\tshaderstate.identitylighting = r_shadow_realtime_world_lightmaps.value;\n\t\telse\n#endif\n\t\t\tshaderstate.identitylighting = r_lightmap_scale.value;\n\t\tshaderstate.identitylighting *= r_refdef.hdr_value;\n\t\tshaderstate.identitylightmap = shaderstate.identitylighting;\n//\t\tshaderstate.identitylightmap *= 1<<gl_overbright.ival;\n\n//\t\tif (cl.worldmodel && cl.worldmodel->fromgame == fg_quake3)\n//\t\t\tshaderstate.identitylighting *= 2;\n\n#ifdef RTLIGHTS\n\t\tif (r_lightprepass)\n\t\t{\n\t\t\tGLBE_SelectEntity(&r_worldentity);\n\t\t\tGLBE_DrawLightPrePass();\n\t\t}\n\t\telse\n#endif\n\t\t{\n#ifdef RTLIGHTS\n\t\t\tif (r_fakeshadows)\n\t\t\t\tSh_GenerateFakeShadows();\n#endif\n\t\t\tGLBE_SelectEntity(&r_worldentity);\n\n\t\t\tif (shaderstate.identitylighting == 0)\n\t\t\t\tBE_SelectMode(BEM_DEPTHDARK);\n\t\t\telse\n\t\t\t\tBE_SelectMode(BEM_STANDARD);\n\n\t\t\tRSpeedRemark();\n\t\t\tGLBE_SubmitMeshes(worldbatches, SHADER_SORT_PORTAL, SHADER_SORT_SEETHROUGH+1);\n\t\t\tRSpeedEnd(RSPEED_OPAQUE);\n\n#ifdef RTLIGHTS\n\t\t\tif (worldbatches)\n\t\t\t{\n\t\t\t\tRSpeedRemark();\n\t\t\t\tTRACE((\"GLBE_DrawWorld: drawing lights\\n\"));\n\t\t\t\tGLBE_SelectEntity(&r_worldentity);\n\t\t\t\tSh_DrawLights(r_refdef.scenevis);\n\t\t\t\tRSpeedEnd(RSPEED_RTLIGHTS);\n\t\t\t\tTRACE((\"GLBE_DrawWorld: lights drawn\\n\"));\n\t\t\t}\n#endif\n\t\t}\n\n#ifndef GLSLONLY\n\t\tif (r_outline.ival && !r_wireframe.ival && qglPolygonMode && qglLineWidth)\n\t\t{\n\t\t\tint oc = r_refdef.flipcull;\n\t\t\tshaderstate.identitylighting = 0;\n\t\t\tshaderstate.identitylightmap = 0;\n\t\t\tr_refdef.flipcull ^= SHADER_CULL_FLIP;\n\t\t\tGLBE_SelectMode(BEM_DEPTHDARK);\n\t\t\tshaderstate.polyoffset.unit = 1;\n\t\t\tshaderstate.polyoffset.factor = 1;\n\n\t\t\tqglEnable(GL_POLYGON_OFFSET_LINE);\n\t\t\tqglLineWidth (bound(0.1, r_outline_width.value, 3.0));\n\t\t\tqglPolygonMode(GL_FRONT_AND_BACK, GL_LINE);\n\t\t\tGLBE_SubmitMeshes(NULL, SHADER_SORT_PORTAL, SHADER_SORT_OPAQUE+1);\n\t\t\tr_refdef.flipcull = oc;\n\t\t\tGLBE_SelectMode(BEM_STANDARD);\n\t\t\tqglDisable(GL_POLYGON_OFFSET_LINE);\n\t\t\tqglPolygonMode(GL_FRONT_AND_BACK, GL_FILL);\n\t\t\tqglLineWidth (1);\n\t\t}\n#endif\n\n\t\tshaderstate.identitylighting = 1;\n\n\t\tRSpeedRemark();\n\t\tGLBE_SubmitMeshes(worldbatches, SHADER_SORT_SEETHROUGH+1, SHADER_SORT_NEAREST);\n\t\tRSpeedEnd(RSPEED_TRANSPARENTS);\n\n#ifndef GLSLONLY\n\t\tif (r_refdef.globalfog.density && (!gl_config.arb_shader_objects || !r_fog_permutation.ival))\n\t\t{\t//fixed function-only. with global fog. that means we need to hack something in.\n\t\t\t//FIXME: should really be doing this on a per-shader basis, for custom shaders that don't use glsl\n\t\t\tBE_SelectMode(BEM_FOG);\n\n\t\t\tBE_SelectFog(r_refdef.globalfog.colour, r_refdef.globalfog.alpha, r_refdef.globalfog.density);\n\t\t\tGLBE_SubmitMeshes(worldbatches, SHADER_SORT_PORTAL, SHADER_SORT_NEAREST);\n\t\t}\n#endif\n\n#ifdef GL_LINE\t//no gles\n\t\tif (r_wireframe.ival && qglPolygonMode)\n\t\t{\n\t\t\tBE_SelectMode(BEM_WIREFRAME);\n\t\t\tqglPolygonMode(GL_FRONT_AND_BACK, GL_LINE);\n\t\t\tGLBE_SubmitMeshes(worldbatches, SHADER_SORT_PORTAL, SHADER_SORT_NEAREST);\n\t\t\tBE_SelectMode(BEM_STANDARD);\n\t\t\tqglPolygonMode(GL_FRONT_AND_BACK, GL_FILL);\n\t\t}\n#endif\n\t\tshaderstate.curdlight = NULL;\n\t}\n\telse\n\t{\n\t\tGLBE_SelectEntity(&r_worldentity);\n\t\tGLBE_SubmitMeshes(NULL, SHADER_SORT_PORTAL, SHADER_SORT_NEAREST);\n\n#ifdef GL_LINE\t//no gles\n\t\tif (r_wireframe.ival && qglPolygonMode)\n\t\t{\n\t\t\tBE_SelectMode(BEM_WIREFRAME);\n\t\t\tqglPolygonMode(GL_FRONT_AND_BACK, GL_LINE);\n\t\t\tGLBE_SubmitMeshes(NULL, SHADER_SORT_PORTAL, SHADER_SORT_NEAREST);\n\t\t\tBE_SelectMode(BEM_STANDARD);\n\t\t\tqglPolygonMode(GL_FRONT_AND_BACK, GL_FILL);\n\t\t}\n#endif\n\t}\n\n\tGLBE_SelectEntity(&r_worldentity);\n//\tshaderstate.curtime = shaderstate.updatetime = realtime;\n\tshaderstate.usingweaponviewmatrix = -1;\n\n\tshaderstate.identitylighting = 1;\n\n\tshaderstate.mbatches = ob;\n\n\tTRACE((\"GLBE_DrawWorld: drawn everything\\n\"));\n}\n\nvoid GLBE_VBO_Begin(vbobctx_t *ctx, size_t maxsize)\n{\n\tCOM_AssertMainThread(\"GLBE_VBO_Begin\");\t//actually, we should probably just build this in memory and throw it to the main thread as needed, but we still need some buffers indexes. I guess we could build a list of varrays. the other option is to just create a large persistant-mapped buffer, and then just append+reuse from any thread, but that makes destruction messy.\n\n\tctx->maxsize = maxsize;\n\tctx->pos = 0;\n\tctx->fallback = NULL;\n\tif (qglBufferStorage)\n\t{\n\t\tctx->vboid[0] = ctx->vboid[1] = 0;\n\t\tqglGenBuffersARB(2, ctx->vboid);\n\t\tctx->fallback = BZ_Malloc(maxsize);\n\t}\n\telse if (qglBufferDataARB)\n\t{\n\t\tctx->vboid[0] = ctx->vboid[1] = 0;\n\t\tqglGenBuffersARB(2, ctx->vboid);\n\t\tGL_SelectVBO(ctx->vboid[0]);\n\t\t//WARNING: in emscripten/webgl, we should probably not pass null.\n\t\tqglBufferDataARB(GL_ARRAY_BUFFER_ARB, ctx->maxsize, NULL, GL_STATIC_DRAW_ARB);\n\t}\n\telse\n\t\tctx->fallback = BZ_Malloc(maxsize);\n}\nvoid GLBE_VBO_Data(vbobctx_t *ctx, void *data, size_t size, vboarray_t *varray)\n{\n\tif (qglBufferStorage)\n\t{\n\t\tmemcpy((char*)ctx->fallback + ctx->pos, data, size);\n\t\tvarray->gl.vbo = ctx->vboid[0];\n\t\tvarray->gl.addr = (void*)ctx->pos;\n\t}\n\telse if (ctx->fallback)\n\t{\n\t\tmemcpy((char*)ctx->fallback + ctx->pos, data, size);\n\t\tvarray->gl.vbo = 0;\n\t\tvarray->gl.addr = (char*)ctx->fallback + ctx->pos;\n\t}\n\telse\n\t{\n\t\tqglBufferSubDataARB(GL_ARRAY_BUFFER_ARB, ctx->pos, size, data);\n\t\tvarray->gl.vbo = ctx->vboid[0];\n\t\tvarray->gl.addr = (void*)ctx->pos;\n\t}\n\tctx->pos += size;\n}\n\nvoid GLBE_VBO_Finish(vbobctx_t *ctx, void *edata, size_t esize, vboarray_t *earray, void **vbomem, void **ebomem)\n{\n\tif (ctx->pos > ctx->maxsize)\n\t\tSys_Error(\"BE_VBO_Finish: too much data given\\n\");\n\tif (qglBufferStorage)\n\t{\n\t\tGL_SelectVBO(ctx->vboid[0]);\n\t\tqglBufferStorage(GL_ARRAY_BUFFER_ARB, ctx->pos, ctx->fallback, 0);\n\t\tBZ_Free(ctx->fallback);\n\t\tctx->fallback = NULL;\n\t\tGL_SelectEBO(ctx->vboid[1]);\n\t\tqglBufferStorage(GL_ELEMENT_ARRAY_BUFFER_ARB, esize, edata, 0);\n\t\tearray->gl.vbo = ctx->vboid[1];\n\t\tearray->gl.addr = NULL;\n\t}\n\telse if (ctx->fallback)\n\t{\n\t\tvoid *d = BZ_Malloc(esize);\n\t\tmemcpy(d, edata, esize);\n\t\tearray->gl.vbo = 0;\n\t\tearray->gl.addr = d;\n\t}\n\telse\n\t{\n\t\tGL_SelectEBO(ctx->vboid[1]);\n\t\tqglBufferDataARB(GL_ELEMENT_ARRAY_BUFFER_ARB, esize, edata, GL_STATIC_DRAW_ARB);\n\t\tearray->gl.vbo = ctx->vboid[1];\n\t\tearray->gl.addr = NULL;\n\t}\n}\nvoid GLBE_VBO_Destroy(vboarray_t *vearray, void *vbomem)\n{\n\tif (vearray->gl.vbo)\n\t{\n\t\tqglDeleteBuffersARB(1, &vearray->gl.vbo);\n\t\tvearray->gl.vbo = 0;\n\t}\n\telse\n\t\tBZ_Free(vearray->gl.addr);\n\tvearray->gl.addr = NULL;\n}\n#endif\n"
  },
  {
    "path": "engine/gl/gl_bloom.c",
    "content": "/*\nCopyright (C) 1997-2001 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n*/\n// gl_bloom.c: 2D lighting post process effect\n\n\n/*\ninfo about bloom algo:\nbloom is basically smudging.\nscreen is nearest-downsampled to some usable scale and filtered to remove low-value light (this is what stops non-bright stuff from blooming)\nthis filtered image is then downsized multiple times\nthe downsized image is then blured \nthe downsized images are then blured horizontally, and then vertically.\nfinal pass simply adds each blured level to the original image.\nall samples are then added together for final rendering (with some kind of tone mapping if you want proper hdr).\n\nnote: the horizontal/vertical bluring is a guassian filter\nnote: bloom comes from the fact that the most downsampled image doesn't have too many pixels. the pixels that it does have are spread over a large area.\n\nhttp://prideout.net/archive/bloom/ contains some sample code\n*/\n\n\n//http://www.quakesrc.org/forums/viewtopic.php?t=4340&start=0\n\n#include \"quakedef.h\"\n\n#if defined(GLQUAKE) || defined(VKQUAKE)\n#include \"shader.h\"\n#include \"glquake.h\"\n#include \"gl_draw.h\"\n\ncvar_t\t\tr_bloom = CVARAFD(\"r_bloom\", \"0\", \"gl_bloom\", CVAR_ARCHIVE, \"Enables bloom (light bleeding from bright objects). Fractional values reduce the amount shown.\");\ncvar_t\t\tr_bloom_retain = CVARD(\"r_bloom_retain\", \"1\", \"How much of the regular scene to retain when bloom is active.\");\ncvar_t\t\tr_bloom_filter = CVARD(\"r_bloom_filter\", \"0.7 0.7 0.7\", \"Controls how bright the image must get before it will bloom (3 separate values, in RGB order).\");\ncvar_t\t\tr_bloom_size = CVARD(\"r_bloom_size\", \"4\", \"Target bloom kernel size (assuming a video width of 320).\");\ncvar_t\t\tr_bloom_downsize = CVARD(\"r_bloom_downsize\", \"0\", \"Technically more correct with a value of 1, but you probably won't notice.\");\ncvar_t\t\tr_bloom_initialscale = CVARD(\"r_bloom_initialscale\", \"1\", \"Initial scaling factor for bloom. should be either 1 or 0.5\");\n\nstatic shader_t *bloomfilter;\nstatic shader_t *bloomrescale;\nstatic shader_t *bloomblur;\nstatic shader_t *bloomfinal;\n\n#define MAXLEVELS 16\ntexid_t pingtex[2][MAXLEVELS];\nfbostate_t fbo_bloom;\nstatic int scrwidth, scrheight;\nstatic int texwidth[MAXLEVELS], texheight[MAXLEVELS];\n\n\nstatic void R_InitBloomTextures(void)\n{\n\tint i;\n\n\tbloomfilter = NULL;\n\tbloomblur = NULL;\n\tbloomfinal = NULL;\n\tscrwidth = 0, scrheight = 0;\n\n\tfor (i = 0; i < MAXLEVELS; i++)\n\t{\n\t\tpingtex[0][i] = r_nulltex;\n\t\tpingtex[1][i] = r_nulltex;\n\t}\n}\nvoid R_BloomRegister(void)\n{\n\tCvar_Register (&r_bloom, \"bloom\");\n\tCvar_Register (&r_bloom_retain, \"bloom\");\n\tCvar_Register (&r_bloom_filter, \"bloom\");\n\tCvar_Register (&r_bloom_size, \"bloom\");\n\tCvar_Register (&r_bloom_downsize, \"bloom\");\n\tCvar_Register (&r_bloom_initialscale, \"bloom\");\n}\nstatic void R_SetupBloomTextures(int w, int h)\n{\n\tint i, j;\n\tif (w == scrwidth && h == scrheight && !r_bloom_initialscale.modified)\n\t\treturn;\n\tr_bloom_initialscale.modified = false;\n\tscrwidth = w;\n\tscrheight = h;\n\t//I'm depending on npot here\n\tw *= r_bloom_initialscale.value;\n\th *= r_bloom_initialscale.value;\n\tfor (i = 0; i < MAXLEVELS; i++)\n\t{\n\t\t/*I'm paranoid*/\n\t\tif (w < 4)\n\t\t\tw = 4;\n\t\tif (h < 4)\n\t\t\th = 4;\n\n\t\ttexwidth[i] = w;\n\t\ttexheight[i] = h;\n\n\t\tw /= 2;\n\t\th /= 2;\n\t}\n\n\t/*destroy textures for each level, to ensure they're created fresh as needed*/\n\tfor (j = 0; j < MAXLEVELS; j++)\n\t{\n\t\tfor (i = 0; i < 2; i++)\n\t\t{\n\t\t\tif (TEXVALID(pingtex[i][j]))\n\t\t\t\tImage_UnloadTexture(pingtex[i][j]);\n\t\t\tpingtex[i][j] = r_nulltex;\n\t\t}\n\t}\n\n\n\tbloomfilter = R_RegisterShader(\"bloom_filter\", SUF_NONE,\n\t\t\"{\\n\"\n\t\t\t\"cull none\\n\"\n\t\t\t\"program bloom_filter\\n\"\n\t\t\t\"{\\n\"\n\t\t\t\t\"map $sourcecolour\\n\"\n\t\t\t\"}\\n\"\n\t\t\"}\\n\");\n\tbloomrescale = R_RegisterShader(\"bloom_rescale\", SUF_NONE,\n\t\t\"{\\n\"\n\t\t\t\"cull none\\n\"\n\t\t\t\"program default2d\\n\"\n\t\t\t\"{\\n\"\n\t\t\t\t\"map $sourcecolour\\n\"\n\t\t\t\"}\\n\"\n\t\t\"}\\n\");\n\tbloomblur = R_RegisterShader(\"bloom_blur\", SUF_NONE,\n\t\t\"{\\n\"\n\t\t\t\"cull none\\n\"\n\t\t\t\"program bloom_blur\\n\"\n\t\t\t\"{\\n\"\n\t\t\t\t\"map $sourcecolour\\n\"\n\t\t\t\"}\\n\"\n\t\t\"}\\n\");\n\tbloomfinal = R_RegisterShader(\"bloom_final\", SUF_NONE,\n\t\t\"{\\n\"\n\t\t\t\"cull none\\n\"\n\t\t\t\"program bloom_final\\n\"\n\t\t\t\"{\\n\"\n\t\t\t\t\"map $sourcecolour\\n\"\n\t\t\t\"}\\n\"\n\t\t\t\"{\\n\"\n\t\t\t\t\"map $diffuse\\n\"\n\t\t\t\"}\\n\"\n\t\t\t\"{\\n\"\n\t\t\t\t\"map $loweroverlay\\n\"\n\t\t\t\"}\\n\"\n\t\t\t\"{\\n\"\n\t\t\t\t\"map $upperoverlay\\n\"\n\t\t\t\"}\\n\"\n\t\t\"}\\n\");\n}\nqboolean R_CanBloom(void)\n{\n\tif (!r_bloom.value)\n\t\treturn false;\n\tswitch(qrenderer)\n\t{\n#ifdef GLQUAKE\n\tcase QR_OPENGL:\n\t\tif (!gl_config.ext_framebuffer_objects)\n\t\t\treturn false;\n\t\tif (!gl_config.arb_shader_objects)\n\t\t\treturn false;\n\t\tif (!sh_config.texture_non_power_of_two_pic)\n\t\t\treturn false;\n\t\tbreak;\n#endif\n#ifdef VKQUAKE\n\tcase QR_VULKAN:\n\t\tbreak;\n#endif\n\tdefault:\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\n#ifdef VKQUAKE\n#include \"../vk/vkrenderer.h\"\nstruct vk_rendertarg vk_rt_bloom[2][MAXLEVELS], vk_rt_filter;\nvoid VK_R_BloomBlend (texid_t source, int x, int y, int w, int h)\n{\n\tint i;\n//\tstruct vk_rendertarg *oldfbo = vk.rendertarg;\n\ttexid_t intex;\n\tint pixels = 1;\n\tint targetpixels = r_bloom_size.value * vid.pixelwidth / 320;\n\n\ttargetpixels *= r_bloom_initialscale.value;\n\n\t/*whu?*/\n\tif (!w || !h)\n\t\treturn;\n\n\t/*update textures if we need to resize them*/\n\tR_SetupBloomTextures(w, h);\n\n\tif (R2D_Flush)\n\t\tR2D_Flush();\n#if 1\n\t/*filter the screen into a downscaled image*/\n\tVKBE_RT_Gen(&vk_rt_filter, NULL, texwidth[0], texheight[0], false, RT_IMAGEFLAGS);\n\tVKBE_RT_Begin(&vk_rt_filter);\n\tvk.sourcecolour = source;\n\tR2D_ScalePic(0, 0, vid.width, vid.height, bloomfilter);\n\tVKBE_RT_End(&vk_rt_filter);\n\tintex = &vk_rt_filter.q_colour;\n#else\n\tintex = source;\n#endif\n\n\tfor (pixels = 1, i = 0; pixels < targetpixels && i < MAXLEVELS; i++, pixels <<= 1)\n\t{\n\t\t//downsize the blur, for added accuracy\n\t\t/*if (i > 0 && r_bloom_downsize.ival)\n\t\t{\n\t\t\t//simple downscale that multiple times\n\t\t\tVKBE_RT_Gen(&vk_rt_bloom[0][i], texwidth[i], texheight[i], false);\n\t\t\tVKBE_RT_Begin(&vk_rt_bloom[0][i]);\n\t\t\tvk.sourcecolour = source;\n\n\t\t\tR2D_ScalePic(0, vid.height, vid.width, -(int)vid.height, bloomrescale);\n\t\t\tif (R2D_Flush)\n\t\t\t\tR2D_Flush();\n\t\t\tintex = &vk_rt_bloom[0][i];\n\t\t\tr_worldentity.glowmod[0] = 1.0 / intex->width;\n\t\t}\n\t\telse*/\n\t\t\tr_worldentity.glowmod[0] = 2.0 / intex->width;\n\n\t\tr_worldentity.glowmod[1] = 0;\n\n\t\tVKBE_RT_Gen(&vk_rt_bloom[1][i], NULL, texwidth[i], texheight[i], false, RT_IMAGEFLAGS);\n\t\tVKBE_RT_Begin(&vk_rt_bloom[1][i]);\n\t\tvk.sourcecolour = intex;\n\t\tBE_SelectEntity(&r_worldentity);\n\t\tR2D_ScalePic(0, 0, vid.width, vid.height, bloomblur);\n\t\tVKBE_RT_End(&vk_rt_bloom[1][i]);\n\n\t\tr_worldentity.glowmod[0] = 0;\n\t\tr_worldentity.glowmod[1] = 1.0 / texheight[i];\n\n\t\tVKBE_RT_Gen(&vk_rt_bloom[0][i], NULL, texwidth[i], texheight[i], false, RT_IMAGEFLAGS);\n\t\tVKBE_RT_Begin(&vk_rt_bloom[0][i]);\n\t\tvk.sourcecolour = &vk_rt_bloom[1][i].q_colour;\n\t\tBE_SelectEntity(&r_worldentity);\n\t\tR2D_ScalePic(0, 0, vid.width, vid.height, bloomblur);\n\t\tVKBE_RT_End(&vk_rt_bloom[0][i]);\n\n\t\tintex = &vk_rt_bloom[0][i].q_colour;\n\t}\n\tr_worldentity.glowmod[0] = 0;\n\tr_worldentity.glowmod[1] = 0;\n\n\t/*combine them onto the screen*/\n\tbloomfinal->defaulttextures->base\t\t\t= intex;\n\tbloomfinal->defaulttextures->loweroverlay\t= (i >= 2)?&vk_rt_bloom[0][i-2].q_colour:0;\n\tbloomfinal->defaulttextures->upperoverlay\t= (i >= 3)?&vk_rt_bloom[0][i-3].q_colour:0;\n\tvk.sourcecolour = source;\n\tR2D_ScalePic(x, y, w, h, bloomfinal);\n\tR2D_Flush();\n}\nvoid VK_R_BloomShutdown(void)\n{\n\tint i;\n\tfor (i = 0; i < MAXLEVELS; i++)\n\t{\n\t\tVKBE_RT_Gen(&vk_rt_bloom[0][i], NULL, 0, 0, false, RT_IMAGEFLAGS);\n\t\tVKBE_RT_Gen(&vk_rt_bloom[1][i], NULL, 0, 0, false, RT_IMAGEFLAGS);\n\t}\n\tVKBE_RT_Gen(&vk_rt_filter, NULL, 0, 0, false, RT_IMAGEFLAGS);\n\n\tR_InitBloomTextures();\n}\n#endif\n#ifdef GLQUAKE\nvoid R_BloomBlend (texid_t source, int x, int y, int w, int h)\n{\n\tint i;\n\tint oldfbo = 0;\n\ttexid_t intex;\n\tint pixels = 1;\n\tint targetpixels = r_bloom_size.value * vid.pixelwidth / 320;\n\tchar name[64];\n\n\ttargetpixels *= r_bloom_initialscale.value;\n\n\t/*whu?*/\n\tif (!w || !h)\n\t\treturn;\n\n\t/*update textures if we need to resize them*/\n\tR_SetupBloomTextures(r_refdef.pxrect.width, r_refdef.pxrect.height);\n\n\t/*filter the screen into a downscaled image*/\n\tif (!TEXVALID(pingtex[0][0]))\n\t{\n\t\tsprintf(name, \"***bloom*%c*%i***\", 'a'+0, 0);\n\t\tTEXASSIGN(pingtex[0][0], Image_CreateTexture(name, NULL, IF_CLAMP|IF_NOMIPMAP|IF_NOPICMIP|IF_LINEAR|IF_NOPURGE));\n\t\tImage_Upload(pingtex[0][0], PTI_RGBA8, NULL, NULL, texwidth[0], texheight[0], 1, IF_CLAMP|IF_NOMIPMAP|IF_NOPICMIP|IF_LINEAR|IF_NOSRGB);\n\t}\n\n\tif (R2D_Flush)\n\t\tR2D_Flush();\n\n\toldfbo = GLBE_FBO_Update(&fbo_bloom, 0, &pingtex[0][0], 1, r_nulltex, 0, 0, 0);\n\tGLBE_FBO_Sources(source, r_nulltex);\n\tqglViewport (0, 0, texwidth[0], texheight[0]);\n\tR2D_ScalePic(0, vid.height, vid.width, -(int)vid.height, bloomfilter);\n\n\tif (R2D_Flush)\n\t\tR2D_Flush();\n\n\tintex = pingtex[0][0];\n\n\tfor (pixels = 1, i = 0; pixels < targetpixels && i < MAXLEVELS; i++, pixels <<= 1)\n\t{\n\t\t/*create any textures if they're not valid yet*/\n\t\tif (!TEXVALID(pingtex[0][i]))\n\t\t{\n\t\t\tsprintf(name, \"***bloom*%c*%i***\", 'a'+0, i);\n\t\t\tTEXASSIGN(pingtex[0][i], Image_CreateTexture(name, NULL, IF_CLAMP|IF_NOMIPMAP|IF_NOPICMIP|IF_LINEAR|IF_NOPURGE));\n\t\t\tImage_Upload(pingtex[0][i], PTI_RGBA8, NULL, NULL, texwidth[i], texheight[i], 1, IF_CLAMP|IF_NOMIPMAP|IF_NOPICMIP|IF_LINEAR|IF_NOSRGB);\n\t\t}\n\t\tif (!TEXVALID(pingtex[1][i]))\n\t\t{\n\t\t\tsprintf(name, \"***bloom*%c*%i***\", 'a'+1, i);\n\t\t\tTEXASSIGN(pingtex[1][i], Image_CreateTexture(name, NULL, IF_CLAMP|IF_NOMIPMAP|IF_NOPICMIP|IF_LINEAR|IF_NOPURGE));\n\t\t\tImage_Upload(pingtex[1][i], PTI_RGBA8, NULL, NULL, texwidth[i], texheight[i], 1, IF_CLAMP|IF_NOMIPMAP|IF_NOPICMIP|IF_LINEAR|IF_NOSRGB);\n\t\t}\n\n\t\tif (R2D_Flush)\n\t\t\tR2D_Flush();\n\t\t//downsize the blur, for added accuracy\n\t\tif (i > 0 && r_bloom_downsize.ival)\n\t\t{\n\t\t\t/*simple downscale that multiple times*/\n\t\t\tGLBE_FBO_Update(&fbo_bloom, 0, &pingtex[0][i], 1, r_nulltex, 0, 0, 0);\n\t\t\tGLBE_FBO_Sources(pingtex[0][i-1], r_nulltex);\n\t\t\tqglViewport (0, 0, texwidth[i], texheight[i]);\n\t\t\tR2D_ScalePic(0, vid.height, vid.width, -(int)vid.height, bloomrescale);\n\t\t\tif (R2D_Flush)\n\t\t\t\tR2D_Flush();\n\t\t\tintex = pingtex[0][i];\n\t\t\tr_worldentity.glowmod[0] = 1.0 / intex->width;\n\t\t}\n\t\telse\n\t\t\tr_worldentity.glowmod[0] = 2.0 / intex->width;\n\n\t\tr_worldentity.glowmod[1] = 0;\n\t\tGLBE_FBO_Update(&fbo_bloom, 0, &pingtex[1][i], 1, r_nulltex, 0, 0, 0);\n\t\tGLBE_FBO_Sources(intex, r_nulltex);\n\t\tqglViewport (0, 0, pingtex[1][i]->width, pingtex[1][i]->height);\n\t\tBE_SelectEntity(&r_worldentity);\n\t\tR2D_ScalePic(0, vid.height, vid.width, -(int)vid.height, bloomblur);\n\t\tif (R2D_Flush)\n\t\t\tR2D_Flush();\n\n\t\tr_worldentity.glowmod[0] = 0;\n\t\tr_worldentity.glowmod[1] = 1.0 / pingtex[1][i]->height;\n\t\tGLBE_FBO_Update(&fbo_bloom, 0, &pingtex[0][i], 1, r_nulltex, 0, 0, 0);\n\t\tGLBE_FBO_Sources(pingtex[1][i], r_nulltex);\n\t\tqglViewport (0, 0, pingtex[0][i]->width, pingtex[0][i]->height);\n\t\tBE_SelectEntity(&r_worldentity);\n\t\tR2D_ScalePic(0, vid.height, vid.width, -(int)vid.height, bloomblur);\n\n\t\tintex = pingtex[0][i];\n\t}\n\n\tif (R2D_Flush)\n\t\tR2D_Flush();\n\n\tr_worldentity.glowmod[0] = 0;\n\tr_worldentity.glowmod[1] = 0;\n\n\tbloomfinal->defaulttextures->base\t\t\t= intex;\n\tbloomfinal->defaulttextures->loweroverlay\t= (i >= 2)?pingtex[0][i-2]:0;\n\tbloomfinal->defaulttextures->upperoverlay\t= (i >= 3)?pingtex[0][i-3]:0;\n\n\t/*combine them onto the screen*/\n\tGLBE_FBO_Pop(oldfbo);\n\tGLBE_FBO_Sources(source, r_nulltex);\n\tGL_Set2D(false);\n\tR2D_ScalePic(x, y + h, w, -h, bloomfinal);\n}\nvoid R_BloomShutdown(void)\n{\n\tGLBE_FBO_Destroy(&fbo_bloom);\n\n\tR_InitBloomTextures();\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "engine/gl/gl_draw.c",
    "content": "/*\nCopyright (C) 1996-1997 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n*/\n\n// draw.c -- this is the only file outside the refresh that touches the\n// vid buffer\n\n#include \"quakedef.h\"\n#ifdef GLQUAKE\n#include \"glquake.h\"\n#include \"shader.h\"\n#include \"gl_draw.h\"\n\n\nextern cvar_t\t\tgl_max_size;\nextern cvar_t\t\tgl_picmip;\nextern cvar_t\t\tgl_lerpimages;\nextern cvar_t\t\tgl_picmip2d;\nextern cvar_t\t\tgl_compress;\nextern cvar_t\t\tgl_smoothcrosshair;\nextern cvar_t\t\tgl_texturemode, gl_texture_anisotropic_filtering;\ncvar_t\t\tgl_blacklist_texture_compression = CVARFD(\"gl_blacklist_texture_compression\", \"0\", CVAR_RENDERERLATCH, \"When enabled, blocks recognition of all compressed-texture formats. Does NOT block support for gl3 texture types like e5bgr9 nor rgba16f.\");\ncvar_t\t\tgl_blacklist_generatemipmap = CVARFD(\"gl_blacklist_generatemipmap\", \"1\", CVAR_RENDERERLATCH, \"Self-generate mipmaps, instead of letting the graphics driver do it.\");\n\nfloat\tgl_anisotropy_factor;\n\nstatic int\tgl_filter_pic[3];\t//ui elements\nstatic int\tgl_filter_mip[3];\t//everything else\nint\t\tgl_mipcap_min = 0;\nint\t\tgl_mipcap_max = 1000;\nint\t\tgl_mip_lod_bias = 0;\n\nvoid GL_DestroyTexture(texid_t tex)\n{\n\tif (!tex)\n\t\treturn;\n\tif (tex->num)\n\t\tqglDeleteTextures(1, &tex->num);\n\ttex->num = 0;\n}\n\n#define glfmtsw(qfmt,sz,in,fm,ty,cf,sr,sg,sb,sa)\t\t\\\n\tdo {\t\t\t\t\t\t\t\t\t\t\t\t\\\n\t\tgl_config.formatinfo[qfmt].sizedformat = sz;\t\\\n\t\tgl_config.formatinfo[qfmt].cformat = cf;\t\t\\\n\t\tgl_config.formatinfo[qfmt].internalformat = in;\t\\\n\t\tgl_config.formatinfo[qfmt].format = fm;\t\t\t\\\n\t\tgl_config.formatinfo[qfmt].type = ty;\t\t\t\\\n\t\tgl_config.formatinfo[qfmt].swizzle_r = sr;\t\t\\\n\t\tgl_config.formatinfo[qfmt].swizzle_g = sg;\t\t\\\n\t\tgl_config.formatinfo[qfmt].swizzle_b = sb;\t\t\\\n\t\tgl_config.formatinfo[qfmt].swizzle_a = sa;\t\t\\\n\t\tsh_config.texfmt[qfmt] = true;\t\t\t\t\t\\\n\t} while(0)\n\n#define glfmt(qfmt,sz,in,fm,ty)\t\t\tglfmtsw(qfmt, sz, in, fm, ty, 0,  GL_RED, GL_GREEN, GL_BLUE, GL_ALPHA)\n#define glfmtc(qfmt,sz,in,fm,ty,cf)\t\tglfmtsw(qfmt, sz, in, fm, ty, cf, GL_RED, GL_GREEN, GL_BLUE, GL_ALPHA)\n#define glfmtb(qfmt,in)\t\t\t\t\tglfmtsw(qfmt, in, in, 0,  0,  0,  GL_RED, GL_GREEN, GL_BLUE, GL_ALPHA)\n\n#ifndef GL_RGB565\n#define GL_RGB565                         0x8D62\n#endif\n\nvoid GL_SetupFormats(void)\n{\n\tint tc_ru = 0, tc_rs = 0, tc_rgu = 0, tc_rgs = 0, tc_rgb = 0, tc_rgba1 = 0, tc_rgba8 = 0, tc_srgb = 0, tc_srgba8 = 0;\n\n\tqboolean bc1=false, bc2=false, bc3=false, bc45=false, bc67=false;\n\tfloat ver = gl_config.glversion;\n\tqboolean srgb = (gl_config.glversion >= (gl_config_gles?3.0:2.1)) || GL_CheckExtension(\"GL_EXT_texture_sRGB\");\n\n\tmemset(&gl_config.formatinfo, 0, sizeof(gl_config.formatinfo));\n\tmemset(&sh_config.texfmt, 0, sizeof(sh_config.texfmt));\n\tsh_config.hw_bc = sh_config.hw_etc = sh_config.hw_astc = 0;\n\n\tif (gl_config_gles && ver >= 3.0 && ver <= 3.3)\n\t\tver = 3.3;\t//treat gles3.0 as desktop 3.3, they're roughly equivelent in feature set.\n\n\tif (GL_CheckExtension(\"GL_EXT_texture_compression_s3tc\"))\n\t\tbc1=bc2=bc3=true;\n\tif ((!gl_config_gles && ver >= 3.0) || GL_CheckExtension(\"GL_ARB_texture_compression_rgtc\") || GL_CheckExtension(\"GL_EXT_texture_compression_rgtc\"))\n\t\tbc45 = true;\n\tif ((!gl_config_gles && ver >= 4.2) || GL_CheckExtension(\"GL_ARB_texture_compression_bptc\") || GL_CheckExtension(\"GL_EXT_texture_compression_bptc\"))\n\t\tbc67 = true;\n\n\tif (bc45)\n\t\ttc_ru = GL_COMPRESSED_RED_RGTC1;\n\tif (bc45)\n\t\ttc_rs = GL_COMPRESSED_SIGNED_RED_RGTC1;\n\tif (bc45)\n\t\ttc_rgu = GL_COMPRESSED_RG_RGTC2;\n\tif (bc45)\n\t\ttc_rgs = GL_COMPRESSED_SIGNED_RG_RGTC2;\n\n\tif (bc1)\n\t\ttc_rgb = GL_COMPRESSED_RGB_S3TC_DXT1_EXT;\n\tif (bc1)\n\t\ttc_srgb = GL_COMPRESSED_SRGB_S3TC_DXT1_EXT;\n\tif (bc3)\n\t\ttc_rgba8 = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;\n\tif (bc3)\n\t\ttc_srgba8 = GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT;\n\tif (bc1)\n\t\ttc_rgba1 = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;\n//\tif (bc1)\n//\t\ttc_srgba1 = GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT;\n\n\tbc1 |= GL_CheckExtension(\"GL_EXT_texture_compression_dxt1\");\n\tbc2 |= GL_CheckExtension(\"GL_ANGLE_texture_compression_dxt3\");\t//WARNING: can only use these if mip0 is a multiple of 4\n\tbc3 |= GL_CheckExtension(\"GL_ANGLE_texture_compression_dxt5\");\n\n#ifdef FTE_TARGET_WEB\n\tif (GL_CheckExtension(\"GL_WEBGL_compressed_texture_s3tc\"))\n\t\tbc1 = bc2 = bc3 = true;\n\tif (GL_CheckExtension(\"GL_WEBGL_compressed_texture_s3tc\"))\n\t\tbc1 = bc2 = bc3 = true;\n#endif\n\n\n\n\t/*else if (sh_config.texfmt[PTI_ETC2_RGB8A8])\n\t{\t//these are probably a bad choice...\n\t\ttc_ru = GL_COMPRESSED_R11_EAC;\n\t\ttc_rgu = GL_COMPRESSED_RG11_EAC;\n\t\ttc_rgb = GL_COMPRESSED_RGB8_ETC2;\n\t\ttc_rgba = GL_COMPRESSED_RGBA8_ETC2_EAC;\n\t}*/\n\n#ifdef FTE_TARGET_WEB\n\tglfmt(PTI_WHOLEFILE, 0, 0, 0, 0);\n//\tsh_config.texfmt[PTI_WHOLEFILE] = true;\n#endif\n\n\tif (gl_config_gles)\n\t{\n\t\t//pre-3 gles doesn't support sized formats, and only a limited number of them too\n\t\tglfmtc(PTI_RGB8,\t(ver>=3)?GL_RGB8:0,\t\t\t\tGL_RGB,\t\t\t\t\tGL_RGB,\t\t\t\t\tGL_UNSIGNED_BYTE,\t\t\ttc_rgb);\n\t\tglfmtc(PTI_RGBA8,\t(ver>=3)?GL_RGBA8:0,\t\t\tGL_RGBA,\t\t\t\tGL_RGBA,\t\t\t\tGL_UNSIGNED_BYTE,\t\t\ttc_rgba8);\n\t\tglfmt(PTI_L8A8,\t\t0,\t\t\t\t\t\t\t\tGL_LUMINANCE_ALPHA,\t\tGL_LUMINANCE_ALPHA,\t\tGL_UNSIGNED_BYTE);\n\t\tglfmt(PTI_L8,\t\t0,\t\t\t\t\t\t\t\tGL_LUMINANCE,\t\t\tGL_LUMINANCE,\t\t\tGL_UNSIGNED_BYTE);\n//\t\tglfmt(PTI_A8,\t\t0,\t\t\t\t\t\t\t\tGL_ALPHA,\t\t\t\tGL_ALPHA,\t\t\t\tGL_UNSIGNED_BYTE);\n\n\t\tif (!gl_config.webgl_ie)\n\t\t{\t//these should work on all gles2+webgl1 devices, but microsoft doesn't give a shit.\n\t\t\tglfmtc(PTI_RGB565,\t(ver>=3)?GL_RGB565:0,\t\t\tGL_RGB,\t\t\t\t\tGL_RGB,\t\t\t\t\tGL_UNSIGNED_SHORT_5_6_5,\ttc_rgb);\n\t\t\tglfmtc(PTI_RGBA4444,(ver>=3)?GL_RGBA4:0,\t\t\tGL_RGBA,\t\t\t\tGL_RGBA,\t\t\t\tGL_UNSIGNED_SHORT_4_4_4_4,\ttc_rgba8);\n\t\t\tglfmtc(PTI_RGBA5551,(ver>=3)?GL_RGB5_A1:0,\t\t\tGL_RGBA,\t\t\t\tGL_RGBA,\t\t\t\tGL_UNSIGNED_SHORT_5_5_5_1,\ttc_rgba1);\n\t\t}\n\t\tif (GL_CheckExtension(\"GL_OES_texture_half_float\"))\n\t\t\tglfmt(PTI_RGBA16F,\t(ver>=3)?GL_RGBA16F:0,\t\t\tGL_RGBA,\t\t\t\tGL_RGBA,\t\t\t\tGL_HALF_FLOAT_OES);\t//not to be confused with GL_HALF_FLOAT[_ARB] which has a different value\n\t\tif (GL_CheckExtension(\"GL_OES_texture_float\"))\n\t\t{\n\t\t\tglfmt(PTI_RGBA32F,\t(ver>=3)?GL_RGBA32F:0,\t\t\tGL_RGBA,\t\t\t\tGL_RGBA,\t\t\t\tGL_FLOAT);\n\t\t\tglfmt(PTI_RGB32F,\t(ver>=3)?GL_RGB32F:0,\t\t\tGL_RGB,\t\t\t\t\tGL_RGB,\t\t\t\t\tGL_FLOAT);\n\t\t}\n\n\t\tif (GL_CheckExtension(\"GL_WEBGL_depth_texture\"))\n\t\t{\t//24bit is okay with this one.\n\t\t\tglfmt(PTI_DEPTH16,\t(ver>=3)?GL_DEPTH_COMPONENT:0,GL_DEPTH_COMPONENT,\tGL_DEPTH_COMPONENT,\t\tGL_UNSIGNED_SHORT);\n\t\t\tglfmt(PTI_DEPTH24,\t(ver>=3)?GL_DEPTH_COMPONENT:0,GL_DEPTH_COMPONENT,\tGL_DEPTH_COMPONENT,\t\tGL_UNSIGNED_INT_24_8);\n\t\t\tglfmt(PTI_DEPTH32,\t(ver>=3)?GL_DEPTH_COMPONENT:0,GL_DEPTH_COMPONENT,\tGL_DEPTH_COMPONENT,\t\tGL_UNSIGNED_INT);\n\t\t}\n\t\telse if (GL_CheckExtension(\"GL_OES_depth_texture\") || GL_CheckExtension(\"GL_ANGLE_depth_texture\"))\n\t\t{\t//16+32, not 24.\n\t\t\tglfmt(PTI_DEPTH16,\t(ver>=3)?GL_DEPTH_COMPONENT:0,GL_DEPTH_COMPONENT,\tGL_DEPTH_COMPONENT,\t\tGL_UNSIGNED_SHORT);\n\t\t\tglfmt(PTI_DEPTH32,\t(ver>=3)?GL_DEPTH_COMPONENT:0,GL_DEPTH_COMPONENT,\tGL_DEPTH_COMPONENT,\t\tGL_UNSIGNED_INT);\n\t\t}\n\n\t\tif (GL_CheckExtension(\"GL_EXT_texture_format_BGRA8888\"))\n\t\t\tglfmtc(PTI_BGRA8,\t/*(ver>=3)?GL_BGRA8_EXT:*/0,\tGL_BGRA_EXT,\t\t\tGL_BGRA_EXT,\t\t\tGL_UNSIGNED_BYTE,\t\ttc_rgba8);\n\t\tif (GL_CheckExtension(\"GL_EXT_texture_type_2_10_10_10_REV\"))\n\t\t\tglfmtc(PTI_A2BGR10,\t0,\t\t\t\t\t\t\t\tGL_RGBA,\t\t\t\tGL_RGBA,\t\t\t\tGL_UNSIGNED_INT_2_10_10_10_REV,\t\ttc_rgba8);\n\t}\n\tif (!gl_config_gles || ver >= 3.0)\n\t{\n\t\tif (ver >= 1.4 || GL_CheckExtension(\"GL_ARB_depth_texture\"))\n\t\t{\t//depth formats\n\t\t\tglfmt(PTI_DEPTH16,\tGL_DEPTH_COMPONENT16_ARB, GL_DEPTH_COMPONENT,\tGL_DEPTH_COMPONENT,\t\tGL_UNSIGNED_SHORT);\n\t\t\tglfmt(PTI_DEPTH24,\tGL_DEPTH_COMPONENT24_ARB, GL_DEPTH_COMPONENT,\tGL_DEPTH_COMPONENT,\t\tGL_UNSIGNED_INT/*FIXME*/);\n\t\t\tif (gl_config_gles)// || ver >= 3.0)\n\t\t\t\tglfmt(PTI_DEPTH32,\tGL_DEPTH_COMPONENT32F, GL_DEPTH_COMPONENT,\tGL_DEPTH_COMPONENT,\t\tGL_FLOAT);\n\t\t\telse\n\t\t\t\tglfmt(PTI_DEPTH32,\tGL_DEPTH_COMPONENT32_ARB, GL_DEPTH_COMPONENT,\tGL_DEPTH_COMPONENT,\t\tGL_FLOAT);\n\t\t}\n//\t\tif (ver >= 3.0)\n//\t\t\tglfmt(PTI_DEPTH32,\tGL_DEPTH_COMPONENT32F, GL_DEPTH_COMPONENT,\tGL_DEPTH_COMPONENT,\t\tGL_FLOAT);\n\t\tif (GL_CheckExtension(\"GL_EXT_packed_depth_stencil\"))\n\t\t\tglfmt(PTI_DEPTH24_8,GL_DEPTH24_STENCIL8_EXT,  GL_DEPTH_STENCIL_EXT,\tGL_DEPTH_STENCIL_EXT,\tGL_UNSIGNED_INT_24_8_EXT);\n\n\t\tglfmtc(PTI_RGBA8,\t\tGL_RGBA8,\t\t\tGL_RGBA,\t\t\t\tGL_RGBA,\t\t\t\tGL_UNSIGNED_BYTE,\ttc_rgba8);\n\t\tif (srgb)\n\t\t\tglfmtc(PTI_RGBA8_SRGB,\tGL_SRGB8_ALPHA8_EXT,GL_SRGB_ALPHA_EXT,\t\tGL_RGBA,\t\t\t\tGL_UNSIGNED_BYTE,\ttc_srgba8);\n\t\tif (!gl_config_gles)\n\t\t{\n\t\t\tif (ver >= 3.3)\t//I'm paranoid about performance, so lets swizzle the alpha to 1 to make the alignment explicit.\n\t\t\t\tglfmtsw(PTI_RGBX8,\t\tGL_RGBA8,\t\t\tGL_RGBA,\t\t\t\tGL_RGBA,\t\t\t\tGL_UNSIGNED_BYTE,\ttc_rgb,\t\tGL_RED, GL_GREEN, GL_BLUE, GL_ONE);\n\t\t\telse\n\t\t\t\tglfmtc(PTI_RGBX8,\t\tGL_RGB8,\t\t\tGL_RGB,\t\t\t\t\tGL_RGBA,\t\t\t\tGL_UNSIGNED_BYTE,\ttc_rgb);\n\t\t\tif (srgb)\n\t\t\t\tglfmtc(PTI_RGBX8_SRGB,\tGL_SRGB8_EXT,\t\tGL_SRGB_EXT,\t\t\tGL_RGBA,\t\t\t\tGL_UNSIGNED_BYTE,\ttc_srgb);\n\t\t}\n\t\tif (ver >= 1.2 && !gl_config_gles)\n\t\t{\n\t\t\tglfmt(PTI_BGR8,\t\t\tGL_RGB8,\t\t\tGL_RGB,\t\t\t\t\tGL_BGR_EXT,\t\t\t\tGL_UNSIGNED_BYTE);\n\t\t\tglfmtc(PTI_BGRX8,\t\tGL_RGB8,\t\t\tGL_RGB,\t\t\t\t\tGL_BGRA_EXT,\t\t\tGL_UNSIGNED_INT_8_8_8_8_REV,\ttc_rgb);\n\t\t\tglfmtc(PTI_BGRA8,\t\tGL_RGBA8,\t\t\tGL_RGBA,\t\t\t\tGL_BGRA_EXT,\t\t\tGL_UNSIGNED_INT_8_8_8_8_REV,\ttc_rgba8);\n\t\t\tif (srgb)\n\t\t\t{\n\t\t\t\tglfmtc(PTI_BGRX8_SRGB,\tGL_SRGB8_EXT,\t\tGL_SRGB_EXT,\t\t\tGL_BGRA_EXT,\t\t\tGL_UNSIGNED_BYTE,\ttc_srgb);\n\t\t\t\tglfmtc(PTI_BGRA8_SRGB,\tGL_SRGB8_ALPHA8_EXT,GL_SRGB_ALPHA_EXT,\t\tGL_BGRA_EXT,\t\t\tGL_UNSIGNED_BYTE,\ttc_srgba8);\n\t\t\t}\n\t\t}\n\t\telse if (ver >= 3.3 && !gl_config_gles)\n\t\t{\n\t\t\tglfmtsw(PTI_BGR8,\t\tGL_RGB8,\t\t\tGL_RGB,\t\t\t\t\tGL_RGB,\t\t\t\tGL_UNSIGNED_BYTE,\t0,\t\t\tGL_BLUE, GL_GREEN, GL_RED, GL_ONE);\n\t\t\tglfmtsw(PTI_BGRX8,\t\tGL_RGB8,\t\t\tGL_RGB,\t\t\t\t\tGL_RGBA,\t\t\tGL_UNSIGNED_BYTE,\ttc_rgb,\t\tGL_BLUE, GL_GREEN, GL_RED, GL_ONE);\n\t\t\tglfmtsw(PTI_BGRA8,\t\tGL_RGBA8,\t\t\tGL_RGBA,\t\t\t\tGL_RGBA,\t\t\tGL_UNSIGNED_BYTE,\ttc_rgba8,\tGL_BLUE, GL_GREEN, GL_RED, GL_ALPHA);\n\t\t\tif (srgb)\n\t\t\t{\n\t\t\t\tglfmtc(PTI_BGRX8_SRGB,\tGL_SRGB8_EXT,\t\tGL_SRGB_EXT,\t\t\tGL_BGRA_EXT,\t\t\tGL_UNSIGNED_BYTE,\ttc_srgb);\n\t\t\t\tglfmtc(PTI_BGRA8_SRGB,\tGL_SRGB8_ALPHA8_EXT,GL_SRGB_ALPHA_EXT,\t\tGL_BGRA_EXT,\t\t\tGL_UNSIGNED_BYTE,\ttc_srgba8);\n\t\t\t}\n\t\t}\n\t\tif (ver >= 3.0 || GL_CheckExtension(\"GL_EXT_texture_shared_exponent\"))\n\t\t\tglfmt(PTI_E5BGR9,\t\tGL_RGB9_E5,\t\t\tGL_RGB9_E5,\t\t\t\tGL_RGB,\t\t\t\t\tGL_UNSIGNED_INT_5_9_9_9_REV);\n\t\tif (ver >= 3.0 || GL_CheckExtension(\"GL_EXT_packed_float\"))\n\t\t\tglfmt(PTI_B10G11R11F,\tGL_R11F_G11F_B10F,\tGL_R11F_G11F_B10F,\tGL_RGB,\t\t\t\t\tGL_UNSIGNED_INT_10F_11F_11F_REV);\n\t\tif (ver >= 3.0 || GL_CheckExtension(\"GL_EXT_packed_pixels\"))\t//so gl1.2 then.\n\t\t\tglfmt(PTI_A2BGR10,\t\tGL_RGB10_A2,\t\tGL_RGB10_A2,\t\t\tGL_RGBA,\t\t\t\tGL_UNSIGNED_INT_2_10_10_10_REV);\n\t\tif (ver >= 3.0 || GL_CheckExtension(\"GL_ARB_texture_rg\"))\n\t\t{\n\t\t\tglfmtc(PTI_P8,\t\t\tGL_R8,\t\t\t\tGL_RED,\t\t\t\t\tGL_RED,\t\t\t\t\tGL_UNSIGNED_BYTE,\ttc_ru);\n\t\t\tglfmtc(PTI_R8,\t\t\tGL_R8,\t\t\t\tGL_RED,\t\t\t\t\tGL_RED,\t\t\t\t\tGL_UNSIGNED_BYTE,\ttc_ru);\n\t\t\tglfmtc(PTI_RG8,\t\t\tGL_RG8,\t\t\t\tGL_RG,\t\t\t\t\tGL_RG,\t\t\t\t\tGL_UNSIGNED_BYTE,\ttc_rs);\n\t\t}\n\t\tif (ver >= 3.1 || (GL_CheckExtension(\"GL_EXT_texture_snorm\") && GL_CheckExtension(\"GL_ARB_texture_rg\")))\n\t\t{\n\t\t\tglfmtc(PTI_R8_SNORM,\tGL_R8_SNORM,\t\tGL_R8_SNORM,\t\t\tGL_RED,\t\t\t\t\tGL_BYTE,\t\t\ttc_rgu);\n\t\t\tglfmtc(PTI_RG8_SNORM,\tGL_RG8_SNORM,\t\tGL_RG8_SNORM,\t\t\tGL_RG,\t\t\t\t\tGL_BYTE,\t\t\ttc_rgs);\n\t\t}\n\n\t\tif (ver >= 3.0)\n\t\t{\n\t\t\tglfmtc(PTI_R16,\t\t\tGL_R16,\t\t\t\tGL_RED,\t\t\t\t\tGL_RED,\t\t\t\t\tGL_UNSIGNED_SHORT,\t0);\n\t\t\tglfmtc(PTI_RGBA16,\t\tGL_RGBA16,\t\t\tGL_RGBA,\t\t\t\tGL_RGBA,\t\t\t\tGL_UNSIGNED_SHORT,\t0);\n\n\t\t\tglfmtc(PTI_R16F,\t\tGL_R16F,\t\t\tGL_RED,\t\t\t\t\tGL_RED,\t\t\t\t\tGL_HALF_FLOAT,\t\t0);\n\t\t\tglfmtc(PTI_R32F,\t\tGL_R32F,\t\t\tGL_RED,\t\t\t\t\tGL_RED,\t\t\t\t\tGL_FLOAT,\t\t\t0);\n\n\t\t\tglfmtc(PTI_RGBA16F,\t\tGL_RGBA16F,\t\t\tGL_RGBA,\t\t\t\tGL_RGBA,\t\t\t\tGL_HALF_FLOAT,\t\t0);\n\t\t\tglfmtc(PTI_RGBA32F,\t\tGL_RGBA32F,\t\t\tGL_RGBA,\t\t\t\tGL_RGBA,\t\t\t\tGL_FLOAT,\t\t\t0);\n\n\t\t\tglfmt(PTI_RGB32F,\t\tGL_RGB32F,\t\t\tGL_RGB,\t\t\t\t\tGL_RGB,\t\t\t\t\tGL_FLOAT);\n\t\t}\n\t\tif (ver >= 1.2 && !gl_config_gles)\n\t\t{\n\t\t\tglfmtc(PTI_RGBA4444,\tGL_RGBA4,\t\t\tGL_RGBA,\t\t\t\tGL_RGBA,\t\t\t\tGL_UNSIGNED_SHORT_4_4_4_4,\t\ttc_srgba8);\n\t\t\tglfmtc(PTI_RGBA5551,\tGL_RGB5_A1,\t\t\tGL_RGBA,\t\t\t\tGL_RGBA,\t\t\t\tGL_UNSIGNED_SHORT_5_5_5_1,\t\ttc_rgba1);\n\n\t\t\tglfmtc(PTI_ARGB4444,\tGL_RGBA4,\t\t\tGL_RGBA,\t\t\t\tGL_BGRA_EXT,\t\t\tGL_UNSIGNED_SHORT_4_4_4_4_REV,\ttc_srgba8);\n\t\t\tglfmtc(PTI_ARGB1555,\tGL_RGB5_A1,\t\t\tGL_RGBA,\t\t\t\tGL_BGRA_EXT,\t\t\tGL_UNSIGNED_SHORT_1_5_5_5_REV,\ttc_rgba1);\n\t\t}\n\t\tif (gl_config_gles || ver >= 4.1)\t//rgb565 was a gles thing, desktop gl just has a 555 internal format despite the 565 data...\n\t\t\tglfmtc(PTI_RGB565,\tGL_RGB565,\t\t\tGL_RGB,\t\t\t\t\tGL_RGB,\t\t\t\t\tGL_UNSIGNED_SHORT_5_6_5,\t\ttc_rgb);\n\t\telse\n\t\t\tglfmtc(PTI_RGB565,\tGL_RGB5,\t\t\tGL_RGB,\t\t\t\t\tGL_RGB,\t\t\t\t\tGL_UNSIGNED_SHORT_5_6_5,\t\ttc_rgb);\n\t\tglfmt(PTI_RGB8,\t\t\tGL_RGB8,\t\t\tGL_RGB,\t\t\t\t\tGL_RGB,\t\t\t\t\tGL_UNSIGNED_BYTE);\n\t\tif (!gl_config_nofixedfunc)\n\t\t{\t//if we have fixed function, then we still have proper support. the driver can emulate with swizzles if it wants.\n\t\t\tglfmtc(PTI_L8,\t\tGL_LUMINANCE8,\t\tGL_LUMINANCE,\t\t\tGL_LUMINANCE,\t\t\tGL_UNSIGNED_BYTE,\t0);\n\t\t\tglfmtc(PTI_L8A8,\tGL_LUMINANCE8_ALPHA8,GL_LUMINANCE_ALPHA,\tGL_LUMINANCE_ALPHA,\t\tGL_UNSIGNED_BYTE,\t0);\n\t\t\tif (ver >= 2.1)\n\t\t\t{\n\t\t\t\tglfmtc(PTI_L8_SRGB,\tGL_SLUMINANCE8_EXT,\t\tGL_SLUMINANCE_EXT,\t\t\tGL_LUMINANCE,\t\t\tGL_UNSIGNED_BYTE,\t0);\n\t\t\t\tglfmtc(PTI_L8A8_SRGB,GL_SLUMINANCE8_ALPHA8_EXT,GL_SLUMINANCE_ALPHA_EXT,\tGL_LUMINANCE_ALPHA,\t\tGL_UNSIGNED_BYTE,\t0);\n\t\t\t}\n\t\t}\n\t\telse if (ver >= 3.3)\n\t\t{\t//can emulate them with swizzles.\n\t\t\tglfmtsw(PTI_L8,\t\tGL_R8,\t\t\t\tGL_RED,\t\t\t\t\tGL_RED,\t\t\t\t\tGL_UNSIGNED_BYTE,\ttc_ru,\tGL_RED, GL_RED, GL_RED, GL_ONE);\n\t\t\tglfmtsw(PTI_L8A8,\tGL_RG8,\t\t\t\tGL_RG,\t\t\t\t\tGL_RG,\t\t\t\t\tGL_UNSIGNED_BYTE,\ttc_rgu,\tGL_RED, GL_RED, GL_RED, GL_GREEN);\n\t\t\tif (GL_CheckExtension(\"GL_EXT_texture_sRGB_R8\"))\n\t\t\t\tglfmtsw(PTI_L8_SRGB,GL_SR8_EXT,\t\tGL_RED,\t\t\t\t\tGL_RED,\t\t\t\t\tGL_UNSIGNED_BYTE,\t0,\tGL_RED, GL_RED, GL_RED, GL_ONE);\n\t\t\t//can't do PTI_L8A8_SRGB with RG - either its all linear or the alpha is srgb, both are wrong.\n\t\t}\n\t}\n\n\t//provide a cvar for ignoring all texture compression extensions.\n\t//this is more so that I can debug fallbacks.\n\tif (gl_blacklist_texture_compression.ival)\n\t{\n\t\tCon_Printf(\"gl_blacklist_texture_compression: driver/hardware texture compression is \"CON_WARNING\"blocked\"CON_DEFAULT\".\\n\");\n\t\treturn;\n\t}\n\n\tif (bc1&&bc2&&bc3)\n\t{\n\t\tif (bc45)\n\t\t{\n\t\t\tif (bc67)\n\t\t\t\tsh_config.hw_bc = 3;\n\t\t\telse\n\t\t\t\tsh_config.hw_bc = 2;\n\t\t}\n\t\telse\n\t\t\tsh_config.hw_bc = 1;\n\t}\n\n\t//block compresion formats.\n\tif (bc1)\n\t{\n\t\tglfmtb(PTI_BC1_RGB,\t\t\t\tGL_COMPRESSED_RGB_S3TC_DXT1_EXT);\n\t\tglfmtb(PTI_BC1_RGBA,\t\t\tGL_COMPRESSED_RGBA_S3TC_DXT1_EXT);\n\t\tif (srgb)\n\t\t{\n\t\t\tglfmtb(PTI_BC1_RGB_SRGB,\t\tGL_COMPRESSED_SRGB_S3TC_DXT1_EXT);\n\t\t\tglfmtb(PTI_BC1_RGBA_SRGB,\t\tGL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT);\n\t\t}\n\t}\n\tif (bc2)\n\t{\n\t\tglfmtb(PTI_BC2_RGBA,\t\t\tGL_COMPRESSED_RGBA_S3TC_DXT3_EXT);\n\t\tif (srgb)\n\t\t\tglfmtb(PTI_BC2_RGBA_SRGB,\t\tGL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT);\n\t}\n\tif (bc3)\n\t{\n\t\tglfmtb(PTI_BC3_RGBA,\t\t\tGL_COMPRESSED_RGBA_S3TC_DXT5_EXT);\n\t\tif (srgb)\n\t\t\tglfmtb(PTI_BC3_RGBA_SRGB,\t\tGL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT);\n\t}\n\tif (bc45)\n\t{\n\t\tglfmtb(PTI_BC4_R,\t\t\t\tGL_COMPRESSED_RED_RGTC1);\n\t\tglfmtb(PTI_BC4_R_SNORM,\t\t\tGL_COMPRESSED_SIGNED_RED_RGTC1);\n\t\tglfmtb(PTI_BC5_RG,\t\t\t\tGL_COMPRESSED_RG_RGTC2);\n\t\tglfmtb(PTI_BC5_RG_SNORM,\t\tGL_COMPRESSED_SIGNED_RG_RGTC2);\n\t}\n\tif (bc67)\n\t{\n\t\tglfmtb(PTI_BC6_RGB_UFLOAT,\t\tGL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB);\n\t\tglfmtb(PTI_BC6_RGB_SFLOAT,\t\tGL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB);\n\t\tglfmtb(PTI_BC7_RGBA,\t\t\tGL_COMPRESSED_RGBA_BPTC_UNORM_ARB);\n\t\tglfmtb(PTI_BC7_RGBA_SRGB,\t\tGL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB);\n\t}\n\n\tif (gl_config_gles && gl_config.glversion >= 3.0 && !sh_config.hw_bc)\n\t\tsh_config.hw_etc = 2;\t//assume that mobile chips have actual support. the bc1 check prevents this from being true on desktop chips.\n#ifdef FTE_TARGET_WEB\n\telse if (GL_CheckExtension(\"GL_WEBGL_compressed_texture_etc\"))\n\t\tsh_config.hw_etc = 2;\t//full etc2+eac\n#endif\n\tif (sh_config.hw_etc>=2 || (!gl_config_gles && (gl_config.glversion >= 4.3 || GL_CheckExtension(\"GL_ARB_ES3_compatibility\"))))\n\t{\t//note that desktop drivers will not necessarily support these in hardware, but are required to at least pretend that they do.\n\t\tglfmtb(PTI_ETC1_RGB8,\t\t\tGL_COMPRESSED_RGB8_ETC2);\n\t\tglfmtb(PTI_ETC2_RGB8,\t\t\tGL_COMPRESSED_RGB8_ETC2);\n\t\tglfmtb(PTI_ETC2_RGB8A1,\t\t\tGL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2);\n\t\tglfmtb(PTI_ETC2_RGB8A8,\t\t\tGL_COMPRESSED_RGBA8_ETC2_EAC);\n\t\tglfmtb(PTI_ETC2_RGB8_SRGB,\t\tGL_COMPRESSED_SRGB8_ETC2);\n\t\tglfmtb(PTI_ETC2_RGB8A1_SRGB,\tGL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2);\n\t\tglfmtb(PTI_ETC2_RGB8A8_SRGB,\tGL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC);\n\t\tglfmtb(PTI_EAC_R11,\t\t\t\tGL_COMPRESSED_R11_EAC);\n\t\tglfmtb(PTI_EAC_R11_SNORM,\t\tGL_COMPRESSED_SIGNED_R11_EAC);\n\t\tglfmtb(PTI_EAC_RG11,\t\t\tGL_COMPRESSED_RG11_EAC);\n\t\tglfmtb(PTI_EAC_RG11_SNORM,\t\tGL_COMPRESSED_SIGNED_RG11_EAC);\n\t}\n\telse\n\t{\n\t\tif (GL_CheckExtension(\"GL_OES_compressed_ETC1_RGB8_texture\"))\n\t\t\tglfmtb(PTI_ETC1_RGB8,\t\t\tGL_ETC1_RGB8_OES);\n\t\tif (GL_CheckExtension(\"GL_OES_compressed_ETC2_RGB8_texture\"))\n\t\t\tglfmtb(PTI_ETC2_RGB8,\t\t\tGL_COMPRESSED_RGB8_ETC2);\n\t\tif (GL_CheckExtension(\"GL_OES_compressed_ETC2_sRGB8_texture\"))\n\t\t\tglfmtb(PTI_ETC2_RGB8_SRGB,\t\tGL_COMPRESSED_SRGB8_ETC2);\n\t\tif (GL_CheckExtension(\"GL_OES_compressed_ETC2_punchthroughA_RGBA8_texture\"))\n\t\t\tglfmtb(PTI_ETC2_RGB8A1,\t\t\tGL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2);\n\t\tif (GL_CheckExtension(\"GL_OES_compressed_ETC2_punchthroughA_sRGB8_alpha_texture\"))\n\t\t\tglfmtb(PTI_ETC2_RGB8A1_SRGB,\tGL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2);\n\t\tif (GL_CheckExtension(\"GL_OES_compressed_ETC2_RGBA8_texture\"))\n\t\t\tglfmtb(PTI_ETC2_RGB8A8,\t\t\tGL_COMPRESSED_RGBA8_ETC2_EAC);\n\t\tif (GL_CheckExtension(\"GL_OES_compressed_ETC2_sRGB8_alpha8_texture\"))\n\t\t\tglfmtb(PTI_ETC2_RGB8A8_SRGB,\tGL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC);\n\t\tif (GL_CheckExtension(\"GL_OES_compressed_EAC_R11_unsigned_texture\"))\n\t\t\tglfmtb(PTI_EAC_R11,\t\t\t\tGL_COMPRESSED_R11_EAC);\n\t\tif (GL_CheckExtension(\"GL_OES_compressed_EAC_R11_signed_texture\"))\n\t\t\tglfmtb(PTI_EAC_R11_SNORM,\t\tGL_COMPRESSED_SIGNED_R11_EAC);\n\t\tif (GL_CheckExtension(\"GL_OES_compressed_EAC_RG11_unsigned_texture\"))\n\t\t\tglfmtb(PTI_EAC_RG11,\t\t\tGL_COMPRESSED_RG11_EAC);\n\t\tif (GL_CheckExtension(\"GL_OES_compressed_EAC_RG11_signed_texture\"))\n\t\t\tglfmtb(PTI_EAC_RG11_SNORM,\t\tGL_COMPRESSED_SIGNED_RG11_EAC);\n\n\t\tif (gl_config.formatinfo[PTI_ETC2_RGB8].internalformat && gl_config.formatinfo[PTI_EAC_RG11].internalformat &&\n\t\t\tgl_config.formatinfo[PTI_ETC2_RGB8A1].internalformat && gl_config.formatinfo[PTI_ETC2_RGB8A8].internalformat)\n\t\t\tsh_config.hw_etc = 2;\n\t\telse if (gl_config.formatinfo[PTI_ETC2_RGB8].internalformat || gl_config.formatinfo[PTI_ETC1_RGB8].internalformat)\n\t\t\tsh_config.hw_etc = 1;\n\t}\n\n\tif (GL_CheckExtension(\"GL_OES_texture_compression_astc\"))\n\t\tsh_config.hw_astc = 3;\t//3d textures\n\telse if (GL_CheckExtension(\"GL_KHR_texture_compression_astc_hdr\"))\n\t\tsh_config.hw_astc = 2;\t//hdr textures\n\telse if (GL_CheckExtension(\"GL_KHR_texture_compression_astc_ldr\"))\n\t\tsh_config.hw_astc = 1;\t//ldr textures only.\n#ifdef FTE_TARGET_WEB\n\telse if (GL_CheckExtension(\"GL_WEBGL_compressed_texture_astc\"))\n\t\tsh_config.hw_astc = 1;\t//need to use js getSupportedProfiles() to find the profiles, which is outside of our scope\n#endif\n\tif (sh_config.hw_astc || (gl_config_gles && gl_config.glversion >= 3.2) || GL_CheckExtension(\"GL_ARB_ES3_2_compatibility\"))\n\t{\t//astc ldr profile is a core part of gles 3.2\n\t\t//note that this does not necessarily mean the hardware itself supports it.\n\t\tglfmtb(PTI_ASTC_4X4_LDR,\t\tGL_COMPRESSED_RGBA_ASTC_4x4_KHR);\n\t\tglfmtb(PTI_ASTC_5X4_LDR,\t\tGL_COMPRESSED_RGBA_ASTC_5x4_KHR);\n\t\tglfmtb(PTI_ASTC_5X5_LDR,\t\tGL_COMPRESSED_RGBA_ASTC_5x5_KHR);\n\t\tglfmtb(PTI_ASTC_6X5_LDR,\t\tGL_COMPRESSED_RGBA_ASTC_6x5_KHR);\n\t\tglfmtb(PTI_ASTC_6X6_LDR,\t\tGL_COMPRESSED_RGBA_ASTC_6x6_KHR);\n\t\tglfmtb(PTI_ASTC_8X5_LDR,\t\tGL_COMPRESSED_RGBA_ASTC_8x5_KHR);\n\t\tglfmtb(PTI_ASTC_8X6_LDR,\t\tGL_COMPRESSED_RGBA_ASTC_8x6_KHR);\n\t\tglfmtb(PTI_ASTC_10X5_LDR,\t\tGL_COMPRESSED_RGBA_ASTC_10x5_KHR);\n\t\tglfmtb(PTI_ASTC_10X6_LDR,\t\tGL_COMPRESSED_RGBA_ASTC_10x6_KHR);\n\t\tglfmtb(PTI_ASTC_8X8_LDR,\t\tGL_COMPRESSED_RGBA_ASTC_8x8_KHR);\n\t\tglfmtb(PTI_ASTC_10X8_LDR,\t\tGL_COMPRESSED_RGBA_ASTC_10x8_KHR);\n\t\tglfmtb(PTI_ASTC_10X10_LDR,\t\tGL_COMPRESSED_RGBA_ASTC_10x10_KHR);\n\t\tglfmtb(PTI_ASTC_12X10_LDR,\t\tGL_COMPRESSED_RGBA_ASTC_12x10_KHR);\n\t\tglfmtb(PTI_ASTC_12X12_LDR,\t\tGL_COMPRESSED_RGBA_ASTC_12x12_KHR);\n\t\tglfmtb(PTI_ASTC_4X4_SRGB,\t\tGL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR);\n\t\tglfmtb(PTI_ASTC_5X4_SRGB,\t\tGL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR);\n\t\tglfmtb(PTI_ASTC_5X5_SRGB,\t\tGL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR);\n\t\tglfmtb(PTI_ASTC_6X5_SRGB,\t\tGL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR);\n\t\tglfmtb(PTI_ASTC_6X6_SRGB,\t\tGL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR);\n\t\tglfmtb(PTI_ASTC_8X5_SRGB,\t\tGL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR);\n\t\tglfmtb(PTI_ASTC_8X6_SRGB,\t\tGL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR);\n\t\tglfmtb(PTI_ASTC_10X5_SRGB,\t\tGL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR);\n\t\tglfmtb(PTI_ASTC_10X6_SRGB,\t\tGL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR);\n\t\tglfmtb(PTI_ASTC_8X8_SRGB,\t\tGL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR);\n\t\tglfmtb(PTI_ASTC_10X8_SRGB,\t\tGL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR);\n\t\tglfmtb(PTI_ASTC_10X10_SRGB,\t\tGL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR);\n\t\tglfmtb(PTI_ASTC_12X10_SRGB,\t\tGL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR);\n\t\tglfmtb(PTI_ASTC_12X12_SRGB,\t\tGL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR);\n\t}\n\tif (sh_config.hw_astc >= 2)\n\t{\t//astc hdr profile uses the same texture formats, which is kinda annoying...\n\t\tglfmtb(PTI_ASTC_4X4_HDR,\t\tGL_COMPRESSED_RGBA_ASTC_4x4_KHR);\n\t\tglfmtb(PTI_ASTC_5X4_HDR,\t\tGL_COMPRESSED_RGBA_ASTC_5x4_KHR);\n\t\tglfmtb(PTI_ASTC_5X5_HDR,\t\tGL_COMPRESSED_RGBA_ASTC_5x5_KHR);\n\t\tglfmtb(PTI_ASTC_6X5_HDR,\t\tGL_COMPRESSED_RGBA_ASTC_6x5_KHR);\n\t\tglfmtb(PTI_ASTC_6X6_HDR,\t\tGL_COMPRESSED_RGBA_ASTC_6x6_KHR);\n\t\tglfmtb(PTI_ASTC_8X5_HDR,\t\tGL_COMPRESSED_RGBA_ASTC_8x5_KHR);\n\t\tglfmtb(PTI_ASTC_8X6_HDR,\t\tGL_COMPRESSED_RGBA_ASTC_8x6_KHR);\n\t\tglfmtb(PTI_ASTC_10X5_HDR,\t\tGL_COMPRESSED_RGBA_ASTC_10x5_KHR);\n\t\tglfmtb(PTI_ASTC_10X6_HDR,\t\tGL_COMPRESSED_RGBA_ASTC_10x6_KHR);\n\t\tglfmtb(PTI_ASTC_8X8_HDR,\t\tGL_COMPRESSED_RGBA_ASTC_8x8_KHR);\n\t\tglfmtb(PTI_ASTC_10X8_HDR,\t\tGL_COMPRESSED_RGBA_ASTC_10x8_KHR);\n\t\tglfmtb(PTI_ASTC_10X10_HDR,\t\tGL_COMPRESSED_RGBA_ASTC_10x10_KHR);\n\t\tglfmtb(PTI_ASTC_12X10_HDR,\t\tGL_COMPRESSED_RGBA_ASTC_12x10_KHR);\n\t\tglfmtb(PTI_ASTC_12X12_HDR,\t\tGL_COMPRESSED_RGBA_ASTC_12x12_KHR);\n\t}\n#ifdef ASTC3D\n\tif (sh_config.hw_astc >= 3)\n\t{\t//the full profile gives 3d texture support too\n\t\tglfmtb(PTI_ASTC_3X3X3_LDR,\t\tGL_COMPRESSED_RGBA_ASTC_3x3x3_OES);\n\t\tglfmtb(PTI_ASTC_4X3X3_LDR,\t\tGL_COMPRESSED_RGBA_ASTC_4x3x3_OES);\n\t\tglfmtb(PTI_ASTC_4X4X3_LDR,\t\tGL_COMPRESSED_RGBA_ASTC_4x4x3_OES);\n\t\tglfmtb(PTI_ASTC_4X4X4_LDR,\t\tGL_COMPRESSED_RGBA_ASTC_4x4x4_OES);\n\t\tglfmtb(PTI_ASTC_5X4X4_LDR,\t\tGL_COMPRESSED_RGBA_ASTC_5x4x4_OES);\n\t\tglfmtb(PTI_ASTC_5X5X4_LDR,\t\tGL_COMPRESSED_RGBA_ASTC_5x5x4_OES);\n\t\tglfmtb(PTI_ASTC_5X5X5_LDR,\t\tGL_COMPRESSED_RGBA_ASTC_5x5x5_OES);\n\t\tglfmtb(PTI_ASTC_6X5X5_LDR,\t\tGL_COMPRESSED_RGBA_ASTC_6x5x5_OES);\n\t\tglfmtb(PTI_ASTC_6X6X5_LDR,\t\tGL_COMPRESSED_RGBA_ASTC_6x6x5_OES);\n\t\tglfmtb(PTI_ASTC_6X6X6_LDR,\t\tGL_COMPRESSED_RGBA_ASTC_6x6x6_OES);\n\t\tglfmtb(PTI_ASTC_3X3X3_SRGB,\t\tGL_COMPRESSED_SRGB8_ALPHA8_ASTC_3x3x3_OES);\n\t\tglfmtb(PTI_ASTC_4X3X3_SRGB,\t\tGL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x3x3_OES);\n\t\tglfmtb(PTI_ASTC_4X4X3_SRGB,\t\tGL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4x3_OES);\n\t\tglfmtb(PTI_ASTC_4X4X4_SRGB,\t\tGL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4x4_OES);\n\t\tglfmtb(PTI_ASTC_5X4X4_SRGB,\t\tGL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4x4_OES);\n\t\tglfmtb(PTI_ASTC_5X5X4_SRGB,\t\tGL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5x4_OES);\n\t\tglfmtb(PTI_ASTC_5X5X5_SRGB,\t\tGL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5x5_OES);\n\t\tglfmtb(PTI_ASTC_6X5X5_SRGB,\t\tGL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5x5_OES);\n\t\tglfmtb(PTI_ASTC_6X6X5_SRGB,\t\tGL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6x5_OES);\n\t\tglfmtb(PTI_ASTC_6X6X6_SRGB,\t\tGL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6x6_OES);\n\t\tglfmtb(PTI_ASTC_3X3X3_HDR,\t\tGL_COMPRESSED_RGBA_ASTC_3x3x3_OES);\n\t\tglfmtb(PTI_ASTC_4X3X3_HDR,\t\tGL_COMPRESSED_RGBA_ASTC_4x3x3_OES);\n\t\tglfmtb(PTI_ASTC_4X4X3_HDR,\t\tGL_COMPRESSED_RGBA_ASTC_4x4x3_OES);\n\t\tglfmtb(PTI_ASTC_4X4X4_HDR,\t\tGL_COMPRESSED_RGBA_ASTC_4x4x4_OES);\n\t\tglfmtb(PTI_ASTC_5X4X4_HDR,\t\tGL_COMPRESSED_RGBA_ASTC_5x4x4_OES);\n\t\tglfmtb(PTI_ASTC_5X5X4_HDR,\t\tGL_COMPRESSED_RGBA_ASTC_5x5x4_OES);\n\t\tglfmtb(PTI_ASTC_5X5X5_HDR,\t\tGL_COMPRESSED_RGBA_ASTC_5x5x5_OES);\n\t\tglfmtb(PTI_ASTC_6X5X5_HDR,\t\tGL_COMPRESSED_RGBA_ASTC_6x5x5_OES);\n\t\tglfmtb(PTI_ASTC_6X6X5_HDR,\t\tGL_COMPRESSED_RGBA_ASTC_6x6x5_OES);\n\t\tglfmtb(PTI_ASTC_6X6X6_HDR,\t\tGL_COMPRESSED_RGBA_ASTC_6x6x6_OES);\n\t}\n#endif\n}\n\n/*\n===============\nDraw_Init\n===============\n*/\nvoid GLDraw_Init (void)\n{\n\textern cvar_t scr_showloading;\n\textern cvar_t vid_srgb;\n\tif ((vid.flags & VID_SRGB_CAPABLE) && gl_config.arb_framebuffer_srgb)\n\t{\t//srgb-capable\n\t\tif (vid.vr)\n\t\t{\t//try to force our rendering to be linear when using vr output.\n\t\t\tvid.flags |= VID_SRGB_FB_LINEAR;\n\t\t\tqglEnable(GL_FRAMEBUFFER_SRGB);\n\t\t}\n\t\telse if (!vid_srgb.ival)\n\t\t{\t//srgb framebuffer not wanted...\n\t\t\tqglDisable(GL_FRAMEBUFFER_SRGB);\n\t\t\tvid.flags &= ~VID_SRGB_FB_LINEAR;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tvid.flags |= VID_SRGB_FB_LINEAR;\n\t\t\tqglEnable(GL_FRAMEBUFFER_SRGB);\n\t\t}\n\t}\n\tif ((vid.flags & VID_SRGB_FB) && vid_srgb.ival >= 0)\n\t\tvid.flags |= VID_SRGBAWARE;\n\telse\n\t\tvid.flags &= ~VID_SRGBAWARE;\n\n\n\tsh_config.can_genmips = qglGenerateMipmap && !gl_blacklist_generatemipmap.ival;\n\t//figure out which extra features we can support on these drivers.\n\tr_deluxemapping = r_deluxemapping_cvar.ival;\n\tr_lightprepass = r_lightprepass_cvar.ival && sh_config.progs_supported;\n\tr_softwarebanding = r_softwarebanding_cvar.ival && sh_config.progs_supported;\n\tif (gl_config.gles && gl_config.glversion < 3.0)\n\t\tr_softwarebanding = false;\n\n\tGL_SetupFormats();\n\n\tR2D_Init();\n\n\tqglDisable(GL_SCISSOR_TEST);\n\tGL_Set2D(false);\n\n\tif (scr_showloading.ival && (!VID_MayRefresh || VID_MayRefresh()))\n\t{\n\t\textern cvar_t scr_loadingscreen_picture;\n\t\tmpic_t *pic = R2D_SafeCachePic (scr_loadingscreen_picture.string);\n\t\tif (pic && R_GetShaderSizes(pic, NULL, NULL, true))\n\t\t{\t//if its too big for the screen, letterbox it.\n\t\t\tqglClearColor(0, 0, 0, 1);\n\t\t\tqglClear(GL_COLOR_BUFFER_BIT);\n\t\t\tif (pic->width > vid.width || pic->height > vid.height)\n\t\t\t\tR2D_Letterbox(0, 0, vid.width, vid.height, pic, pic->width, pic->height);\n\t\t\telse\t//otherwise draw it centred\n\t\t\t\tR2D_ScalePic ( ((int)vid.width - pic->width)/2, ((int)vid.height - 48 - pic->height)/2, pic->width, pic->height, pic);\n\t\t}\n\n\t\tif (R2D_Flush)\n\t\t\tR2D_Flush();\n\t\tVID_SwapBuffers();\n\t}\n\n\tGL_SetupSceneProcessingTextures();\n\n\t//\n\t// get the other pics we need\n\t//\n\tTRACE((\"dbg: GLDraw_ReInit: R2D_SafePicFromWad\\n\"));\n\tdraw_disc = R2D_SafePicFromWad (\"disc\");\n\n#ifdef GL_USE8BITTEX\n\tinited15to8 = false;\n#endif\n\n\tqglClearColor (1,0,0,1);\n\n\tTRACE((\"dbg: GLDraw_ReInit: PPL_LoadSpecularFragmentProgram\\n\"));\n\tGL_InitSceneProcessingShaders();\n}\n\nvoid GLDraw_DeInit (void)\n{\n\tR2D_Shutdown();\n\n\tR_GAliasFlushSkinCache(true);\n\n\tdraw_disc = NULL;\n\tGL_ShutdownPostProcessing();\n\n#ifdef RTLIGHTS\n\tSh_Shutdown();\n#endif\n\n\tGLBE_Shutdown();\t//to release its images.\n\tShader_Shutdown();\n\n\tImage_Shutdown();\n}\n\n\n\n//=============================================================================\n\n/*\n================\nGL_Set2D\n\nSetup as if the screen was 320*200\n================\n*/\nvoid GL_Set2D (unsigned int flags)\n{\n\textern cvar_t gl_screenangle;\n\tfloat rad, ang;\n\tfloat tmp[16], tmp2[16];\n\tfloat w = vid.width, h = vid.height;\n\tqboolean fbo = !!*r_refdef.rt_destcolour[0].texname;\n\tqboolean flipped = flags&1;\n\tqboolean norotate = flags&2;\n\n\tif (vid.framebuffer)\n\t{\n\t\tvid.fbvwidth = vid.width;\n\t\tvid.fbvheight = vid.height;\n\t\tvid.fbpwidth = vid.framebuffer->width;\n\t\tvid.fbpheight = vid.framebuffer->height;\n\t}\n\telse if (fbo)\n\t{\n\t\tR2D_RT_GetTexture(r_refdef.rt_destcolour[0].texname, &vid.fbpwidth, &vid.fbpheight);\n\t\tvid.fbvwidth = vid.fbpwidth;\n\t\tvid.fbvheight = vid.fbpheight;\n\n\t\tif (strcmp(r_refdef.rt_destcolour[0].texname, \"megascreeny\"))\n\t\t\tflipped ^= true;\n\t}\n\telse\n\t{\n\t\tvid.fbvwidth = vid.width;\n\t\tvid.fbvheight = vid.height;\n\t\tvid.fbpwidth = vid.pixelwidth;\n\t\tvid.fbpheight = vid.pixelheight;\n\t}\n\n\tang = (gl_screenangle.value>0?(gl_screenangle.value+45):(gl_screenangle.value-45))/90;\n\tang = (int)ang * 90;\n\tif (ang && !fbo && !norotate)\n\t{ /*more expensive maths*/\n\t\trad = (ang * M_PI) / 180;\n\n\t\tw = fabs(cos(rad)) * (vid.width) + fabs(sin(rad)) * (vid.height);\n\t\th = fabs(sin(rad)) * (vid.width) + fabs(cos(rad)) * (vid.height);\n\n\t\tMatrix4x4_CM_Orthographic(r_refdef.m_projection_std, w/-2.0f, w/2.0f, h/2.0f, h/-2.0f, -99999, 99999);\n\n\t\tMatrix4x4_Identity(tmp);\n\t\tMatrix4_Multiply(Matrix4x4_CM_NewTranslation((vid.width/-2.0f), (vid.height/-2.0f), 0), tmp, tmp2);\n\t\tMatrix4_Multiply(Matrix4x4_CM_NewRotation(-ang,  0, 0, 1), tmp2, r_refdef.m_view);\n\t}\n\telse\n\t{\n\t\tw = vid.fbvwidth;\n\t\th = vid.fbvheight;\n\t\tif (flipped)\n\t\t\tMatrix4x4_CM_Orthographic(r_refdef.m_projection_std, 0, w, 0, h, -99999, 99999);\n\t\telse\n\t\t\tMatrix4x4_CM_Orthographic(r_refdef.m_projection_std, 0, w, h, 0, -99999, 99999);\n\t\tMatrix4x4_Identity(r_refdef.m_view);\n\t}\n\t//current physical position on the current render target.\n\tr_refdef.pxrect.x = 0;\n\tr_refdef.pxrect.y = 0;\n\tr_refdef.pxrect.width = vid.fbpwidth;\n\tr_refdef.pxrect.height = vid.fbpheight;\n\tr_refdef.pxrect.maxheight = vid.fbpheight;\n\tr_refdef.time = realtime;\n\t/*flush that gl state*/\n\tGL_ViewportUpdate();\n\n\tif (flipped)\n\t\tr_refdef.flipcull = SHADER_CULL_FLIP;\n\telse\n\t\tr_refdef.flipcull = 0;\n\tr_refdef.colourmask = 0u;\n\n\tGL_SetShaderState2D(true);\n}\n\n//====================================================================\n\n//note: needs to be bound first, so the 'targ' argument shouldn't be a problem.\nstatic void GL_Texturemode_Apply(GLenum targ, unsigned int flags)\n{\n\tint min, mag;\n\tint *filter = (flags & IF_UIPIC)?gl_filter_pic:gl_filter_mip;\n\n\tif (targ == GL_TEXTURE_CUBE_MAP_ARB)\n\t\tflags |= IF_NOMIPMAP;\n\n\tif ((filter[2] && !(flags & IF_NEAREST)) || (flags & IF_LINEAR))\n\t\tmag = GL_LINEAR;\n\telse\n\t\tmag = GL_NEAREST;\n\tif (filter[1] == -1 || (flags & IF_NOMIPMAP))\n\t{\n\t\tif ((filter[0] && !(flags & IF_NEAREST)) || (flags & IF_LINEAR))\n\t\t\tmin = GL_LINEAR;\n\t\telse\n\t\t\tmin = GL_NEAREST;\n\t}\n\telse\n\t{\n\t\tif ((filter[1] && !(flags & IF_NEAREST)) || (flags & IF_LINEAR))\n\t\t{\n\t\t\tif ((filter[0] && !(flags & IF_NEAREST)) || (flags & IF_LINEAR))\n\t\t\t\tmin = GL_LINEAR_MIPMAP_LINEAR;\n\t\t\telse\n\t\t\t\tmin = GL_NEAREST_MIPMAP_LINEAR;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif ((filter[0] && !(flags & IF_NEAREST)) || (flags & IF_LINEAR))\n\t\t\t\tmin = GL_LINEAR_MIPMAP_NEAREST;\n\t\t\telse\n\t\t\t\tmin = GL_NEAREST_MIPMAP_NEAREST;\n\t\t}\n\t}\n\n\tif (sh_config.can_mipbias)\n\t#define GL_TEXTURE_LOD_BIAS               0x8501\n\tif (flags & IF_MIPCAP)\n\t\tqglTexParameterf(targ, GL_TEXTURE_LOD_BIAS, (flags & IF_MIPCAP)?gl_mip_lod_bias:0);\n\n\tqglTexParameteri(targ, GL_TEXTURE_MIN_FILTER, min);\n\tqglTexParameteri(targ, GL_TEXTURE_MAG_FILTER, mag);\n\tif (gl_anisotropy_factor)\t//0 means driver doesn't support\n\t{\n\t\t//only use anisotrophy when using linear any linear, because of drivers that forces linear sampling when anis is active (annoyingly this is allowed by the spec - even for the mag filter).\n\t\t//WARNING: older intel drivers can be expected to glitch with anisotropic lego mode.\n\t\t//  Note that we do not check mag filters here - 'gl_texturemode nll' will have crunch anistrophy, unfortunately swbanding requires nnn right now (and should be picking mips based on surface distance rather than pixel coverage instead).\n\t\tif (min == GL_LINEAR_MIPMAP_LINEAR || min == GL_LINEAR_MIPMAP_NEAREST)\n\t\t\tqglTexParameterf(targ, GL_TEXTURE_MAX_ANISOTROPY_EXT, gl_anisotropy_factor);\n\t\telse\n\t\t\tqglTexParameterf(targ, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1);\n\t}\n}\nqboolean GL_LoadTextureMips(texid_t tex, const struct pendingtextureinfo *mips)\n{\n\tstatic int cubeface[] =\n\t{\n\t\tGL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB,\n\t\tGL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB,\n\t\tGL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB,\n\t\tGL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB,\n\t\tGL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB,\n\t\tGL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB\n\t};\n\tint targ;\n\tint i, j, ifmt;\n\tint nummips = mips->mipcount;\n\tuploadfmt_t encoding = mips->encoding;\n\tqboolean compress;\n\tqboolean storage = true;\n\tunsigned int bb, bw, bh, bd;\n\tint levels = 0, genlevels;\n\tint ttype = (tex->flags & IF_TEXTYPEMASK)>>IF_TEXTYPESHIFT;\n\n\tif (gl_config.gles)\n\t{\n\t\t//gles requires that the internal format must match format\n\t\t//this means we can't specify 24.0 modes with a 24.8 datatype.\n\t\t//arguably we shouldn't do this anyway, but there are differences that q3 shaders can notice.\n\t\t//fixme: move elsewhere?\n\t\tif (encoding == PTI_RGBX8)\n\t\t\tencoding = PTI_RGBA8;\n\t\tif (encoding == PTI_BGRX8)\n\t\t\tencoding = PTI_BGRA8;\n\t}\n\n\tif (ttype != mips->type)\n\t\treturn false;\n\n\tswitch(ttype)\n\t{\n\tdefault:\n\t\treturn false;\n\tcase PTI_2D:\n\t\ttarg = GL_TEXTURE_2D;\n\t\tbreak;\n\tcase PTI_3D:\n\t\ttarg = GL_TEXTURE_3D;\n\t\tbreak;\n\tcase PTI_CUBE:\n\t\ttarg = GL_TEXTURE_CUBE_MAP_ARB;\n\t\tbreak;\n\tcase PTI_CUBE_ARRAY:\n\t\ttarg = GL_TEXTURE_CUBE_MAP_ARRAY_ARB;\n\t\tbreak;\n\tcase PTI_2D_ARRAY:\n\t\ttarg = GL_TEXTURE_2D_ARRAY;\n\t\tbreak;\n\t}\n\tgenlevels = levels = mips->mipcount;\n\tif (!(tex->flags & IF_NOMIPMAP) && sh_config.can_genmips && gl_config.formatinfo[encoding].type && genlevels == 1 && mips->mip[0].data && mips->encoding != PTI_P8)\n\t{\n\t\twhile ((mips->mip[0].width>>genlevels) || (mips->mip[0].height>>genlevels))\n\t\t\tgenlevels++;\n\t}\n\n\tGL_MTBind(0, targ, tex);\n\n\tif (tex->num && qglTexStorage2D)\n\t{\n\t\tqglDeleteTextures(1, &tex->num);\n\t\tqglGenTextures(1, &tex->num);\n\t\tGL_MTBind(0, targ, tex);\n\t\tqglBindTexture (targ, tex->num);\t//GL_MTBind caches, which is problematic when things are getting deleted.\n\t}\n\telse\n\t{\n\t\tif (!tex->num)\n\t\t\tqglGenTextures(1, &tex->num);\n\t\tGL_MTBind(0, targ, tex);\n\t}\n\n\t//this is annoying.\n\tif (mips->encoding >= PTI_ASTC_4X4_LDR && mips->encoding <= PTI_ASTC_12X12_LDR && gl_config.astc_decodeprecision)\n\t\tqglTexParameteri(targ, GL_TEXTURE_ASTC_DECODE_PRECISION_EXT, GL_RGBA8);\n\n\tif (tex->flags&IF_CLAMP)\n\t{\n\t\tif (gl_config.glversion < 1.2 && !gl_config_gles)\n\t\t{\t//warning: crappy path! gl1.1 is shite and doesn't support clamp-to-edge! there's ALWAYS some wrap component!\n\t\t\tqglTexParameteri(targ, GL_TEXTURE_WRAP_S, GL_CLAMP);\n\t\t\tqglTexParameteri(targ, GL_TEXTURE_WRAP_T, GL_CLAMP);\n\t\t\tif (targ != GL_TEXTURE_2D)\n\t\t\t\tqglTexParameteri(targ, GL_TEXTURE_WRAP_R, GL_CLAMP);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tqglTexParameteri(targ, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);\n\t\t\tqglTexParameteri(targ, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);\n\t\t\tif (targ != GL_TEXTURE_2D)\n\t\t\t\tqglTexParameteri(targ, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);\n\t\t}\n\t}\n\telse\n\t{\n\t\tqglTexParameteri(targ, GL_TEXTURE_WRAP_S, GL_REPEAT);\n\t\tqglTexParameteri(targ, GL_TEXTURE_WRAP_T, GL_REPEAT);\n\t\tif (targ != GL_TEXTURE_2D)\n\t\t\tqglTexParameteri(targ, GL_TEXTURE_WRAP_R, GL_REPEAT);\n\t}\n\n\tif (ttype != PTI_3D && nummips > 1)\n\t{\t//npot mipmapped textures are awkward.\n\t\t//opengl floors.\n\t\tfor (i = 1; i < nummips; i++)\n\t\t{\n\t\t\tif (mips->mip[i].width != max(1,(mips->mip[i-1].width>>1)) ||\n\t\t\t\tmips->mip[i].height != max(1,(mips->mip[i-1].height>>1)))\n\t\t\t{\t//okay, this mip looks like it was sized wrongly. I've seen this happen with some dds files.\n\t\t\t\tnummips = i;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\t//make sure the texture is complete even if the mips are not.\n\t//note that some drivers will just ignore levels that are not valid.\n\t//this means that we can't make this setting dynamic, so we might as well let the drivers know BEFORE we do the uploads, to be kind to those that are buggy..\n\t//this is available in gles3\n\tif (sh_config.can_mipcap)\n\t{\n\t\tif (targ != GL_TEXTURE_CUBE_MAP_ARB)\n\t\t{\n\t\t\tif (tex->flags & IF_MIPCAP)\n\t\t\t{\n\t\t\t\tqglTexParameteri(targ, GL_TEXTURE_BASE_LEVEL, min(genlevels-1, gl_mipcap_min));\n\t\t\t\tqglTexParameteri(targ, GL_TEXTURE_MAX_LEVEL, min(genlevels-1, gl_mipcap_max));\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tqglTexParameteri(targ, GL_TEXTURE_BASE_LEVEL, 0);\n\t\t\t\tqglTexParameteri(targ, GL_TEXTURE_MAX_LEVEL, genlevels-1);\n\t\t\t}\n\t\t}\n\t}\n\tif (sh_config.can_mipbias)\n\t\tqglTexParameteri(targ, GL_TEXTURE_LOD_BIAS, (tex->flags & IF_MIPCAP)?gl_mip_lod_bias:0);\n\n//\ttex->width = mips->mip[0].width;\n//\ttex->height = mips->mip[0].height;\n\tGL_Texturemode_Apply(targ, tex->flags);\n\n#ifdef FTE_TARGET_WEB\n\tif (encoding == PTI_WHOLEFILE)\n\t{\n\t\temscriptenfte_gl_loadtexturefile(tex->num, &tex->width, &tex->height, mips->mip[0].data, mips->mip[0].datasize, tex->ident, !!(tex->flags & IF_PREMULTIPLYALPHA), !(tex->flags & IF_NOMIPMAP));\n\t\treturn true;\n\t}\n#endif\n\n\t//arb_texture_compression is core in gl1.3\n\t//gles doesn't support autocompression as of gles3.\n\t//only autocompress if we have actually have data (gl errors otherwise).\n\tif (gl_config.arb_texture_compression && mips->mip[0].data && !(tex->flags & IF_RENDERTARGET))\n\t\tcompress = !!gl_compress.ival;\n\telse\n\t\tcompress = false;\n\tif (compress && gl_config.formatinfo[encoding].cformat)\n\t\tifmt = gl_config.formatinfo[encoding].cformat;\n\telse\n\t\tifmt = gl_config.formatinfo[encoding].sizedformat;\n\n\tif (!ifmt)\n\t{\n\t\tifmt = gl_config.formatinfo[encoding].internalformat;\n\t\tif (!ifmt)\n\t\t\treturn false;\t//dude, everything bad is happening today.\n\t\tstorage = false;\t//no sized format means we can't use glTexStorageND\n\t}\n\n\tif (gl_config.formatinfo[encoding].swizzle_r != GL_RED || gl_config.formatinfo[encoding].swizzle_g != GL_GREEN ||\n\t\tgl_config.formatinfo[encoding].swizzle_b != GL_BLUE || gl_config.formatinfo[encoding].swizzle_a != GL_ALPHA)\n\t{\n\t\tqglTexParameteri(targ, GL_TEXTURE_SWIZZLE_R, gl_config.formatinfo[encoding].swizzle_r);\n\t\tqglTexParameteri(targ, GL_TEXTURE_SWIZZLE_G, gl_config.formatinfo[encoding].swizzle_g);\n\t\tqglTexParameteri(targ, GL_TEXTURE_SWIZZLE_B, gl_config.formatinfo[encoding].swizzle_b);\n\t\tqglTexParameteri(targ, GL_TEXTURE_SWIZZLE_A, gl_config.formatinfo[encoding].swizzle_a);\n\t}\n\n\tImage_BlockSizeForEncoding(encoding, &bb, &bw, &bh, &bd);\n\tswitch(bb)\n\t{\n\tcase 3:\n\tdefault:\n\t\tbb = 1;\t//rows are completely unaligned\n\t\tbreak;\n\tcase 1:\n\tcase 2:\n\tcase 4:\n\tcase 8:\n\t\tbreak;\t//rows are aligned naturally\n\tcase 16:\n\tcase 32:\n\tcase 64:\n\t\tbb = 8;\t//row alignment is capped to 8 by opengl, for some reason.\n\t\tbreak;\n\t}\n\tif (gl_config.unpackalignment != bb)\n\t{\n\t\tgl_config.unpackalignment = bb;\n\t\tqglPixelStorei(GL_UNPACK_ALIGNMENT, bb);\n\t}\n\n\tif (ttype == PTI_CUBE)\n\t{\n\t\tif (qglTexStorage2D && storage)\n\t\t{\n\t\t\tqglTexStorage2D(targ, genlevels, ifmt, mips->mip[0].width, mips->mip[0].height);\n\t\t\tfor (i = 0; i < nummips; i++)\n\t\t\t{\n\t\t\t\tsize_t sz = mips->mip[i].datasize / mips->mip[i].depth;\n\t\t\t\tif (!mips->mip[i].data)\t//already specified by gltexstorage, don't bother wiping it or anything.\n\t\t\t\t\tcontinue;\n\t\t\t\tfor (j = 0; j < min(6, mips->mip[i].depth); j++)\n\t\t\t\t{\n\t\t\t\t\tif (gl_config.formatinfo[encoding].type)\n\t\t\t\t\t\tqglTexSubImage2D\t\t\t(cubeface[j], i, 0, 0, mips->mip[i].width, mips->mip[i].height, gl_config.formatinfo[encoding].format, gl_config.formatinfo[encoding].type, (qbyte*)mips->mip[i].data + sz*j);\n\t\t\t\t\telse\n\t\t\t\t\t\tqglCompressedTexSubImage2D\t(cubeface[j], i, 0, 0, mips->mip[i].width, mips->mip[i].height,\tifmt,\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tsz, (qbyte*)mips->mip[i].data + sz*j);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tfor (i = 0; i < nummips; i++)\n\t\t\t{\n\t\t\t\tsize_t sz = mips->mip[i].datasize / mips->mip[i].depth;\n\t\t\t\tif (!mips->mip[i].data)\t//already specified by gltexstorage, don't bother wiping it or anything.\n\t\t\t\t\tcontinue;\n\t\t\t\tfor (j = 0; j < min(6, mips->mip[i].depth); j++)\n\t\t\t\t{\n\t\t\t\t\tif (gl_config.formatinfo[encoding].type)\n\t\t\t\t\t\tqglTexImage2D\t\t\t(cubeface[j], i, ifmt, mips->mip[i].width, mips->mip[i].height, 0, gl_config.formatinfo[encoding].format, gl_config.formatinfo[encoding].type, (qbyte*)mips->mip[i].data + sz*j);\n\t\t\t\t\telse\n\t\t\t\t\t\tqglCompressedTexImage2D\t(cubeface[j], i, ifmt, mips->mip[i].width, mips->mip[i].height,\t0,                                                                         sz, (qbyte*)mips->mip[i].data + sz*j);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (genlevels > levels)\n\t\t\tqglGenerateMipmap(targ);\n\t}\n\telse if (ttype == PTI_3D || ttype == PTI_2D_ARRAY || ttype == PTI_CUBE_ARRAY)\n\t{\n\t\tif (qglTexStorage3D && storage)\n\t\t{\n\t\t\tqglTexStorage3D(targ, genlevels, ifmt, mips->mip[0].width, mips->mip[0].height, mips->mip[0].depth);\n\n\t\t\tfor (i = 0; i < nummips; i++)\n\t\t\t{\n\t\t\t\tif (!mips->mip[i].data)\t//already specified by gltexstorage\n\t\t\t\t\tcontinue;\n\n\t\t\t\tif (gl_config.formatinfo[encoding].type)\n\t\t\t\t\tqglTexSubImage3D\t\t\t(targ, i, 0, 0, 0, mips->mip[i].width, mips->mip[i].height, mips->mip[0].depth, gl_config.formatinfo[encoding].format, gl_config.formatinfo[encoding].type,\tmips->mip[i].data);\n\t\t\t\telse\n\t\t\t\t\tqglCompressedTexSubImage3D\t(targ, i, 0, 0, 0, mips->mip[i].width, mips->mip[i].height, mips->mip[0].depth,\t\t\t\t\t\t\t\t\tifmt, mips->mip[i].datasize,\t\t\t\tmips->mip[i].data);\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tfor (i = 0; i < nummips; i++)\n\t\t\t{\n\t\t\t\tif (gl_config.formatinfo[encoding].type)\n\t\t\t\t\tqglTexImage3D\t\t\t\t(targ, i, ifmt, mips->mip[i].width, mips->mip[i].height, mips->mip[0].depth, 0, gl_config.formatinfo[encoding].format, gl_config.formatinfo[encoding].type,\tmips->mip[i].data);\n\t\t\t\telse\n\t\t\t\t\tqglCompressedTexImage3D\t\t(targ, i, ifmt, mips->mip[i].width, mips->mip[i].height, mips->mip[0].depth, 0,\t\t\t\t\t\t\t\tmips->mip[i].datasize,\t\t\t\t\t\t\tmips->mip[i].data);\n\t\t\t}\n\t\t}\n\n\t\tif (genlevels > levels)\n\t\t\tqglGenerateMipmap(targ);\n\t}\n\telse if (ttype == PTI_2D)\n\t{\n\t\tif (qglTexStorage2D && storage)\n\t\t{\n\t\t\tqglTexStorage2D(targ, genlevels, ifmt, mips->mip[0].width, mips->mip[0].height);\n\t\t\tfor (i = 0; i < nummips; i++)\n\t\t\t{\n\t\t\t\tif (!mips->mip[i].data)\t//already specified by gltexstorage, don't bother wiping it or anything.\n\t\t\t\t\tcontinue;\n\n\t\t\t\tif (gl_config.formatinfo[encoding].type)\n\t\t\t\t\tqglTexSubImage2D\t\t\t(targ, i, 0, 0, mips->mip[i].width, mips->mip[i].height, gl_config.formatinfo[encoding].format, gl_config.formatinfo[encoding].type, mips->mip[i].data);\n\t\t\t\telse\n\t\t\t\t\tqglCompressedTexSubImage2D\t(targ, i, 0, 0, mips->mip[i].width, mips->mip[i].height,\t\t\t\t\t\t\t\tifmt, mips->mip[i].datasize,\t\t\t\t mips->mip[i].data);\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tfor (i = 0; i < nummips; i++)\n\t\t\t{\n\t\t\t\tif (gl_config.formatinfo[encoding].type)\n\t\t\t\t\tqglTexImage2D\t\t\t\t(targ, i, ifmt, mips->mip[i].width, mips->mip[i].height, 0, gl_config.formatinfo[encoding].format, gl_config.formatinfo[encoding].type,\tmips->mip[i].data);\n\t\t\t\telse\n\t\t\t\t\tqglCompressedTexImage2D\t\t(targ, i, ifmt, mips->mip[i].width, mips->mip[i].height, 0,\t\t\t\t\t\t\t\tmips->mip[i].datasize,\t\t\t\t\t\t\tmips->mip[i].data);\n\t\t\t}\n\t\t}\n\n\t\tif (genlevels > levels)\n\t\t\tqglGenerateMipmap(targ);\n\t}\n\telse\n\t{\n\t\t//erk!\n\t}\n\n#ifdef IMAGEFMT_KTX\n\tif (compress && gl_compress.ival>1 && gl_config.formatinfo[encoding].type)\n\t{\n\t\tGLint fmt;\n\t\tGLint csize;\n\t\tstruct pendingtextureinfo out = {mips->type};\n\t\tout.type = mips->type;\n\t\tout.mipcount = mips->mipcount;\n\t\tout.encoding = 0;\n\t\tout.extrafree = NULL;\n\n\t\tqglGetTexLevelParameteriv(targ, 0, GL_TEXTURE_INTERNAL_FORMAT, &fmt);\n\n\t\tswitch(fmt)\n\t\t{\n\t\tcase GL_COMPRESSED_RGB_S3TC_DXT1_EXT:\t\t\t\tout.encoding = PTI_BC1_RGB;\t\t\t\tbreak;\n\t\tcase GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:\t\t\t\tout.encoding = PTI_BC1_RGBA;\t\t\tbreak;\n\t\tcase GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:\t\t\t\tout.encoding = PTI_BC2_RGBA;\t\t\tbreak;\n\t\tcase GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:\t\t\t\tout.encoding = PTI_BC3_RGBA;\t\t\tbreak;\n\t\tcase GL_COMPRESSED_SRGB_S3TC_DXT1_EXT:\t\t\t\tout.encoding = PTI_BC1_RGB_SRGB;\t\tbreak;\n\t\tcase GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT:\t\tout.encoding = PTI_BC1_RGBA_SRGB;\t\tbreak;\n\t\tcase GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT:\t\tout.encoding = PTI_BC2_RGBA_SRGB;\t\tbreak;\n\t\tcase GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT:\t\tout.encoding = PTI_BC3_RGBA_SRGB;\t\tbreak;\n\t\tcase GL_COMPRESSED_RED_RGTC1:\t\t\t\t\t\tout.encoding = PTI_BC4_R;\t\t\t\tbreak;\n\t\tcase GL_COMPRESSED_SIGNED_RED_RGTC1:\t\t\t\tout.encoding = PTI_BC4_R_SNORM;\t\t\tbreak;\n\t\tcase GL_COMPRESSED_RG_RGTC2:\t\t\t\t\t\tout.encoding = PTI_BC5_RG;\t\t\t\tbreak;\n\t\tcase GL_COMPRESSED_SIGNED_RG_RGTC2:\t\t\t\t\tout.encoding = PTI_BC5_RG_SNORM;\t\tbreak;\n\t\tcase GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB:\t\tout.encoding = PTI_BC6_RGB_UFLOAT;\t\tbreak;\n\t\tcase GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB:\t\tout.encoding = PTI_BC6_RGB_SFLOAT;\t\tbreak;\n\t\tcase GL_COMPRESSED_RGBA_BPTC_UNORM_ARB:\t\t\t\tout.encoding = PTI_BC7_RGBA;\t\t\tbreak;\n\t\tcase GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB:\t\tout.encoding = PTI_BC7_RGBA_SRGB;\t\tbreak;\n\t\tcase GL_ETC1_RGB8_OES:\t\t\t\t\t\t\t\tout.encoding = PTI_ETC1_RGB8;\t\t\tbreak;\n\t\tcase GL_COMPRESSED_RGB8_ETC2:\t\t\t\t\t\tout.encoding = PTI_ETC2_RGB8;\t\t\tbreak;\n\t\tcase GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2:\tout.encoding = PTI_ETC2_RGB8A1;\t\t\tbreak;\n\t\tcase GL_COMPRESSED_RGBA8_ETC2_EAC:\t\t\t\t\tout.encoding = PTI_ETC2_RGB8A8;\t\t\tbreak;\n\t\tcase GL_COMPRESSED_SRGB8_ETC2:\t\t\t\t\t\tout.encoding = PTI_ETC2_RGB8_SRGB;\t\tbreak;\n\t\tcase GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2:\tout.encoding = PTI_ETC2_RGB8A1_SRGB;\tbreak;\n\t\tcase GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC:\t\t\tout.encoding = PTI_ETC2_RGB8A8_SRGB;\tbreak;\n\t\tcase GL_COMPRESSED_R11_EAC:\t\t\t\t\t\t\tout.encoding = PTI_EAC_R11;\t\t\t\tbreak;\n\t\tcase GL_COMPRESSED_SIGNED_R11_EAC:\t\t\t\t\tout.encoding = PTI_EAC_R11_SNORM;\t\tbreak;\n\t\tcase GL_COMPRESSED_RG11_EAC:\t\t\t\t\t\tout.encoding = PTI_EAC_RG11;\t\t\tbreak;\n\t\tcase GL_COMPRESSED_SIGNED_RG11_EAC:\t\t\t\t\tout.encoding = PTI_EAC_RG11_SNORM;\t\tbreak;\n\t\tcase GL_COMPRESSED_RGBA_ASTC_4x4_KHR:\t\t\t\tout.encoding = PTI_ASTC_4X4_HDR;\t\tbreak;\t//play it safe and assume hdr.\n\t\tcase GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR:\t\tout.encoding = PTI_ASTC_4X4_SRGB;\t\tbreak;\n\t\tcase GL_COMPRESSED_RGBA_ASTC_5x4_KHR:\t\t\t\tout.encoding = PTI_ASTC_5X4_HDR;\t\tbreak;\n\t\tcase GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR:\t\tout.encoding = PTI_ASTC_5X4_SRGB;\t\tbreak;\n\t\tcase GL_COMPRESSED_RGBA_ASTC_5x5_KHR:\t\t\t\tout.encoding = PTI_ASTC_5X5_HDR;\t\tbreak;\n\t\tcase GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR:\t\tout.encoding = PTI_ASTC_5X5_SRGB;\t\tbreak;\n\t\tcase GL_COMPRESSED_RGBA_ASTC_6x5_KHR:\t\t\t\tout.encoding = PTI_ASTC_6X5_HDR;\t\tbreak;\n\t\tcase GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR:\t\tout.encoding = PTI_ASTC_6X5_SRGB;\t\tbreak;\n\t\tcase GL_COMPRESSED_RGBA_ASTC_6x6_KHR:\t\t\t\tout.encoding = PTI_ASTC_6X6_HDR;\t\tbreak;\n\t\tcase GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR:\t\tout.encoding = PTI_ASTC_6X6_SRGB;\t\tbreak;\n\t\tcase GL_COMPRESSED_RGBA_ASTC_8x5_KHR:\t\t\t\tout.encoding = PTI_ASTC_8X5_HDR;\t\tbreak;\n\t\tcase GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR:\t\tout.encoding = PTI_ASTC_8X5_SRGB;\t\tbreak;\n\t\tcase GL_COMPRESSED_RGBA_ASTC_8x6_KHR:\t\t\t\tout.encoding = PTI_ASTC_8X6_HDR;\t\tbreak;\n\t\tcase GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR:\t\tout.encoding = PTI_ASTC_8X6_SRGB;\t\tbreak;\n\t\tcase GL_COMPRESSED_RGBA_ASTC_10x5_KHR:\t\t\t\tout.encoding = PTI_ASTC_10X5_HDR;\t\tbreak;\n\t\tcase GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR:\t\tout.encoding = PTI_ASTC_10X5_SRGB;\t\tbreak;\n\t\tcase GL_COMPRESSED_RGBA_ASTC_10x6_KHR:\t\t\t\tout.encoding = PTI_ASTC_10X6_HDR;\t\tbreak;\n\t\tcase GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR:\t\tout.encoding = PTI_ASTC_10X6_SRGB;\t\tbreak;\n\t\tcase GL_COMPRESSED_RGBA_ASTC_8x8_KHR:\t\t\t\tout.encoding = PTI_ASTC_8X8_HDR;\t\tbreak;\n\t\tcase GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR:\t\tout.encoding = PTI_ASTC_8X8_SRGB;\t\tbreak;\n\t\tcase GL_COMPRESSED_RGBA_ASTC_10x8_KHR:\t\t\t\tout.encoding = PTI_ASTC_10X8_HDR;\t\tbreak;\n\t\tcase GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR:\t\tout.encoding = PTI_ASTC_10X8_SRGB;\t\tbreak;\n\t\tcase GL_COMPRESSED_RGBA_ASTC_10x10_KHR:\t\t\t\tout.encoding = PTI_ASTC_10X10_HDR;\t\tbreak;\n\t\tcase GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR:\t\tout.encoding = PTI_ASTC_10X10_SRGB;\t\tbreak;\n\t\tcase GL_COMPRESSED_RGBA_ASTC_12x10_KHR:\t\t\t\tout.encoding = PTI_ASTC_12X10_HDR;\t\tbreak;\n\t\tcase GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR:\t\tout.encoding = PTI_ASTC_12X10_SRGB;\t\tbreak;\n\t\tcase GL_COMPRESSED_RGBA_ASTC_12x12_KHR:\t\t\t\tout.encoding = PTI_ASTC_12X12_HDR;\t\tbreak;\n\t\tcase GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR:\t\tout.encoding = PTI_ASTC_12X12_SRGB;\t\tbreak;\n\t\t}\n\n\t\tif (out.encoding)\n\t\t{\n\t\t\tfor (i = 0; i < nummips; i++)\n\t\t\t{\n\t\t\t\tif (ttype == PTI_CUBE)\n\t\t\t\t{\t//gl's cubemaps are annoying\n\t\t\t\t\tqglGetTexLevelParameteriv(cubeface[0], i, GL_TEXTURE_COMPRESSED_IMAGE_SIZE_ARB, &csize);\n\t\t\t\t\tif (!csize)\n\t\t\t\t\t\tbreak;\t//some kind of error. the gpu didn't store it?\n\t\t\t\t\tout.mip[i].datasize = csize;\n\t\t\t\t\tout.mip[i].data = BZ_Malloc(csize*6);\n\t\t\t\t\tout.mip[i].needfree = true;\n\t\t\t\t\tout.mip[i].width = mips->mip[i].width;\n\t\t\t\t\tout.mip[i].height = mips->mip[i].height;\n\t\t\t\t\tout.mip[i].depth = 6;\n\t\t\t\t\tfor (j = 0; j < 6; j++)\n\t\t\t\t\t\tqglGetCompressedTexImage(targ, j, (qbyte*)out.mip[i].data + csize*j);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tqglGetTexLevelParameteriv(targ, i, GL_TEXTURE_COMPRESSED_IMAGE_SIZE_ARB, &csize);\n\t\t\t\t\tif (!csize)\n\t\t\t\t\t\tbreak;\t//some kind of error. the gpu didn't store it?\n\t\t\t\t\tout.mip[i].datasize = csize;\n\t\t\t\t\tout.mip[i].data = BZ_Malloc(csize);\n\t\t\t\t\tout.mip[i].needfree = true;\n\t\t\t\t\tout.mip[i].width = mips->mip[i].width;\n\t\t\t\t\tout.mip[i].height = mips->mip[i].height;\n\t\t\t\t\tout.mip[i].depth = mips->mip[i].depth;\n\t\t\t\t\tqglGetCompressedTexImage(targ, i, out.mip[i].data);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (i)\n\t\t\t{\n\t\t\t\tout.mipcount = i;\n\t\t\t\tImage_WriteKTXFile(va(\"textures/%s.ktx\", tex->ident), FS_GAMEONLY, &out);\n\t\t\t}\n\t\t\twhile (i-- > 0)\n\t\t\t\tif (out.mip[i].needfree)\n\t\t\t\t\tBZ_Free(out.mip[i].data);\n\t\t}\n\t}\n#endif\n\n\treturn true;\n}\n\nvoid GL_UpdateFiltering(image_t *imagelist, int filtermip[3], int filterpic[3], int mipcap[2], float lodbias, float anis)\n{\n\tint targ;\n\timage_t *img;\n\n\tgl_mipcap_min = mipcap[0];\n\tgl_mipcap_max = mipcap[1];\n\tgl_mip_lod_bias = lodbias;\n\n\tVectorCopy(filterpic, gl_filter_pic);\n\tVectorCopy(filtermip, gl_filter_mip);\n\n\t//bound carefully, so that we get 0 if anisrophy is not supported at all (1 is fine). we can then test against 0 (which is an otherwise-invalid value) avoiding gl errors.\n\tif (anis > gl_config.ext_texture_filter_anisotropic)\n\t\tgl_anisotropy_factor = gl_config.ext_texture_filter_anisotropic;\n\telse if (anis < 1)\n\t\tgl_anisotropy_factor = 1;\n\telse\n\t\tgl_anisotropy_factor = anis;\n\n\t// change all the existing mipmap texture objects\n\tfor (img=imagelist ; img ; img=img->next)\n\t{\n\t\tif (img->status != TEX_LOADED)\n\t\t\tcontinue;\n\t\tswitch((img->flags & IF_TEXTYPEMASK) >> IF_TEXTYPESHIFT)\n\t\t{\n\t\tcase PTI_2D:\n\t\t\ttarg = GL_TEXTURE_2D;\n\t\t\tbreak;\n\t\tcase PTI_3D:\n\t\t\ttarg = GL_TEXTURE_3D;\n\t\t\tbreak;\n\t\tcase PTI_CUBE:\n\t\t\ttarg = GL_TEXTURE_CUBE_MAP_ARB;\n\t\t\tbreak;\n\t\tcase PTI_CUBE_ARRAY:\n\t\t\ttarg = GL_TEXTURE_CUBE_MAP_ARRAY_ARB;\n\t\t\tbreak;\n\t\tcase PTI_2D_ARRAY:\n\t\t\ttarg = GL_TEXTURE_2D_ARRAY;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tcontinue;\n\t\t}\n\n\t\tGL_MTBind(0, targ, img);\n\t\tGL_Texturemode_Apply(targ, img->flags);\n\n\t\t//should we do dynamic mipcap settings? this bugs out ATI.\n\t\t/*\n\t\tif (!gl_config.gles && (tex->flags & IF_MIPCAP))\n\t\t{\n\t\t\tqglTexParameteri(targ, GL_TEXTURE_BASE_LEVEL, gl_mipcap_min);\n\t\t\tqglTexParameteri(targ, GL_TEXTURE_MAX_LEVEL, gl_mipcap_max);\n\t\t}\n\t\t*/\n\t}\n}\n\n#endif\n"
  },
  {
    "path": "engine/gl/gl_draw.h",
    "content": "/*\nCopyright (C) 1996-1997 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  \n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n*/\n\n// draw.h -- these are the only functions outside the refresh allowed\n// to touch the vid buffer\n\nvoid GLDraw_Init (void);\nvoid GLDraw_DeInit (void);\nvoid Surf_DeInit (void);\n\nvoid R2D_Init(void);\nmpic_t\t*R2D_SafeCachePic (const char *path);\nmpic_t *R2D_SafePicFromWad (const char *name);\nvoid R2D_ImageColours(float r, float g, float b, float a);\nvoid R2D_Image(float x, float y, float w, float h, float s1, float t1, float s2, float t2, mpic_t *pic);\nvoid R2D_Line(float x1, float y1, float x2, float y2, mpic_t *pic);\nvoid R2D_ScalePic (float x, float y, float width, float height, mpic_t *pic);\nvoid R2D_SubPic(float x, float y, float width, float height, mpic_t *pic, float srcx, float srcy, float srcwidth, float srcheight);\nvoid R2D_Letterbox(float sx, float sy, float sw, float sh, mpic_t *pic, float pw, float ph);\nvoid R2D_ConsoleBackground (int firstline, int lastline, qboolean forceopaque);\nvoid R2D_EditorBackground (void);\nvoid R2D_TileClear (float x, float y, float w, float h);\nvoid R2D_FadeScreen (void);\nvoid R2D_Init(void);\nvoid R2D_Shutdown(void);\n\nextern void (*R2D_Flush)(void);\t//if set, something is queued and must be flushed at some point. if its set to what you will set it to, then you can build onto your batch yourself.\n\nvoid R2D_PolyBlend (void);\nvoid R2D_BrightenScreen (void);\n\nvoid QDECL R2D_Conback_Callback(struct cvar_s *var, char *oldvalue);\n"
  },
  {
    "path": "engine/gl/gl_font.c",
    "content": "#include \"quakedef.h\"\n\n#ifndef SERVERONLY\n#include \"shader.h\"\n#include \"gl_draw.h\"\n\n#ifdef _WIN32\n\t#ifdef _XBOX\n\t\t#include <xtl.h>\n\t#else\n\t\t#include <windows.h>\n\t#endif\n#endif\n#include <ctype.h>\n\n/* Font Names:\n\tprimaryface[,altface[...]][?primaryarg[&arg[...]]][:[secondaryface[,altface[...]]][?secondaryargs]]\n\n\targs:\n\t\tcol=r,g,b -- tint the font (overriding the game's normal tint). only arg allowed when the secondary face is omitted.\n\t\tfmt=q\t-- quake-style raster font with quake's own codepage (no fallbacks needed for the weird glyphs).\n\t\tfmt=l\t-- quake-style raster font with io8859-1(latin-1) codepage\n\t\tfmt=w\t-- quake-style raster font with windows1252 codepage (for more glyphs than latin-1)\n\t\tfmt=k\t-- quake-style raster font with koi8-u codepage (apparently its somewhat common in the quake community)\n\t\tfmt=h\t-- halflife-style all-on-one-line raster font\n\t\taspect=0.5 -- raster font is squished horizontally\n\t\tstyle\t-- list of modifiers for inexact family font matching for system fonts\n*/\n\nvoid Font_Init(void);\nvoid Font_Shutdown(void);\nstruct font_s *Font_LoadFont(const char *fontfilename, float height, float scale, int outline, unsigned int flags);\nvoid Font_Free(struct font_s *f);\nvoid Font_BeginString(struct font_s *font, float vx, float vy, int *px, int *py);\nvoid Font_BeginScaledString(struct font_s *font, float vx, float vy, float szx, float szy, float *px, float *py); /*avoid using*/\nvoid Font_Transform(float vx, float vy, int *px, int *py);\nint Font_CharHeight(void);\nfloat Font_CharScaleHeight(void);\n\n//FIXME: if we want to do emoji properly, then we're going to need support for variants somehow\n//       easiest is probably to make the next codepoint available too (and consumable)\n//       handling variants in the font cache is another issue. gah. not worth it.\nint Font_CharWidth(unsigned int charflags, unsigned int codepoint);\nfloat Font_CharScaleWidth(unsigned int charflags, unsigned int codepoint);\nint Font_CharEndCoord(struct font_s *font, int x, unsigned int charflags, unsigned int codepoint);\nint Font_DrawChar(int px, int py, unsigned int charflags, unsigned int codepoint);\nfloat Font_DrawScaleChar(float px, float py, unsigned int charflags, unsigned int codepoint); /*avoid using*/\n\nvoid Font_EndString(struct font_s *font);\nint Font_LineBreaks(conchar_t *start, conchar_t *end, int maxpixelwidth, int maxlines, conchar_t **starts, conchar_t **ends);\nstruct font_s *font_menu;\nstruct font_s *font_default;\nstruct font_s *font_console;\nstruct font_s *font_tiny;\nstatic int font_be_flags;\nextern unsigned int r2d_be_flags;\n\n#ifdef AVAIL_FREETYPE\n#include <ft2build.h>\n#include FT_FREETYPE_H \nstruct fontface_s;\nint Font_ChangeFTSize(struct fontface_s *qface, int pixelheight);\n\n#ifndef FT_LOAD_COLOR\n#define FT_LOAD_COLOR (1L<<20)\n#endif\n\nstatic FT_Library fontlib;\n\n#ifdef FREETYPE_STATIC\n#define pFT_Init_FreeType\tFT_Init_FreeType\n#define pFT_Load_Char\t\tFT_Load_Char\n#define pFT_Get_Char_Index\tFT_Get_Char_Index\n#define pFT_Set_Pixel_Sizes\tFT_Set_Pixel_Sizes\n#define pFT_Select_Size\t\tFT_Select_Size\n#define pFT_New_Face\t\tFT_New_Face\n#define pFT_New_Memory_Face\tFT_New_Memory_Face\n#define pFT_Done_Face\t\tFT_Done_Face\n#define pFT_Error_String\tFT_Error_String\n#else\nqboolean triedtoloadfreetype;\ndllhandle_t *fontmodule;\nFT_Error (VARGS *pFT_Init_FreeType)\t\t(FT_Library  *alibrary);\nFT_Error (VARGS *pFT_Load_Char)\t\t\t(FT_Face face, FT_ULong char_code, FT_Int32 load_flags);\nFT_UInt\t (VARGS *pFT_Get_Char_Index)\t(FT_Face face, FT_ULong charcode);\nFT_Error (VARGS *pFT_Set_Pixel_Sizes)\t(FT_Face face, FT_UInt pixel_width, FT_UInt pixel_height);\nFT_Error (VARGS *pFT_Select_Size)\t\t(FT_Face face, FT_Int strike_index);\nFT_Error (VARGS *pFT_New_Face)\t\t\t(FT_Library library, const char *pathname, FT_Long face_index, FT_Face *aface);\nFT_Error (VARGS *pFT_New_Memory_Face)\t(FT_Library library, const FT_Byte* file_base, FT_Long file_size, FT_Long face_index, FT_Face *aface);\nFT_Error (VARGS *pFT_Done_Face)\t\t\t(FT_Face face);\nconst char *(VARGS *pFT_Error_String)\t(FT_Error  error_code);\n#endif\n#else\ntypedef unsigned int FT_Pixel_Mode; //for consistency even without freetype support.\n#endif\n\n#ifndef FT_PIXEL_MODE_MONO\n#define FT_PIXEL_MODE_MONO 1\n#endif\n#ifndef FT_PIXEL_MODE_GRAY\n#define FT_PIXEL_MODE_GRAY 2\n#endif\n#ifndef FT_PIXEL_MODE_BGRA\n#define FT_PIXEL_MODE_BGRA 7\t//added in FT 2.5\n#endif\n#define FT_PIXEL_MODE_RGBA_SA (100)\t//RGBA, straight alpha. not in freetype.\n#define FT_PIXEL_MODE_RGBA (101)\t//RGBA, premultiplied alpha. not in freetype.\n\n#ifdef QUAKEHUD\nstatic const char *imgs[] =\n{\n\t//0xe10X\n\t\"e100\",\"e101\",\n\t\"inv_shotgun\",\n\t\"inv_sshotgun\",\n\t\"inv_nailgun\",\n\t\"inv_snailgun\",\n\t\"inv_rlaunch\",\n\t\"inv_srlaunch\",\n\t\"inv_lightng\",\t//8\n\t\"e109\",\"e10a\",\"e10b\",\"e10c\",\"e10d\",\"e10e\",\"e10f\",\n\n\t//0xe11X\n\t\"e110\",\"e111\",\n\t\"inv2_shotgun\",\n\t\"inv2_sshotgun\",\n\t\"inv2_nailgun\",\n\t\"inv2_snailgun\",\n\t\"inv2_rlaunch\",\n\t\"inv2_srlaunch\",\n\t\"inv2_lightng\",\n\t\"e119\",\"e11a\",\"e11b\",\"e11c\",\"e11d\",\"e11e\",\"e11f\",\n\n\t//0xe12X\n\t\"sb_shells\",\n\t\"sb_nails\",\n\t\"sb_rocket\",\n\t\"sb_cells\",\n\n\t\"sb_armor1\",\n\t\"sb_armor2\",\n\t\"sb_armor3\",\n\t\"e127\",\"e128\",\"e129\",\"e12a\",\"e12b\",\"e12c\",\"e12d\",\"e12e\",\"e12f\",\n\n\t//0xe13X\n\t\"sb_key1\",\n\t\"sb_key2\",\n\t\"sb_invis\",\n\t\"sb_invuln\",\n\t\"sb_suit\",\n\t\"sb_quad\",\n\n\t\"sb_sigil1\",\n\t\"sb_sigil2\",\n\t\"sb_sigil3\",\n\t\"sb_sigil4\",\n\t\"e13a\",\"e13b\",\"e13c\",\"e13d\",\"e13e\",\"e13f\",\n\n\t//0xe14X\n\t\"face1\",\n\t\"face_p1\",\n\t\"face2\",\n\t\"face_p2\",\n\t\"face3\",\n\t\"face_p3\",\n\t\"face4\",\n\t\"face_p4\",\n\t\"face5\",\n\t\"face_p5\",\n\n\t\"face_invis\",\n\t\"face_invul2\",\n\t\"face_inv2\",\n\t\"face_quad\",\n\t\"e14e\",\n\t\"e14f\",\n\n\t//0xe15X\n\t\"e150\",\n\t\"e151\",\n\t\"e152\",\n\t\"e153\",\n\t\"e154\",\n\t\"e155\",\n\t\"e156\",\n\t\"e157\",\n\t\"e158\",\n\t\"e159\",\n\t\"e15a\",\n\t\"e15b\",\n\t\"e15c\",\n\t\"e15d\",\n\t\"e15e\",\n\t\"e15f\",\n\n\t//0xe16X\n\t\"e160\",\n\t\"e161\",\n\t\"e162\",\n\t\"e163\",\n\t\"e164\",\n\t\"e165\",\n\t\"e166\",\n\t\"e167\",\n\t\"e168\",\n\t\"e169\",\n\t\"e16a\",\n\t\"e16b\",\n\t\"e16c\",\n\t\"e16d\",\n\t\"e16e\",\n\t\"e16f\"\n};\n#endif\n\n#define FONT_MAXCHARS 0x110000 //as defined by UTF-16, and thus applied to all unicode because UTF-16 is the crappy limited one.\n#define FONTBLOCKS ((FONT_MAXCHARS+FONTBLOCKSIZE-1)/FONTBLOCKSIZE)\n#define FONTBLOCKSIZE 0x100\t//must be power-of-two\n#define FONTBLOCKMASK (FONTBLOCKSIZE-1)\n#define FONTIMAGES (1<<2)\t//this is total, not per font.\n#define FIMAGEIDXTYPE unsigned char\n#define CHARIDXTYPE unsigned int\n\n#define INVALIDPLANE ((1<<(8*sizeof(FIMAGEIDXTYPE)))-1)\n#define BITMAPPLANE ((1<<(8*sizeof(FIMAGEIDXTYPE)))-2)\n#define DEFAULTPLANE ((1<<(8*sizeof(FIMAGEIDXTYPE)))-3)\n#define SINGLEPLANE ((1<<(8*sizeof(FIMAGEIDXTYPE)))-4)\n#define TRACKERIMAGE ((1<<(8*sizeof(FIMAGEIDXTYPE)))-5)\n#define FIMAGEWIDTH (1<<10)\n#define FIMAGEHEIGHT FIMAGEWIDTH\n\n#define FONTPLANES FONTIMAGES\n#define PLANEWIDTH FIMAGEWIDTH\n#define PLANEHEIGHT FIMAGEHEIGHT\n\n//windows' font linking allows linking multiple extra fonts to a main font.\n//this means that a single selected font can use chars from lots of different files if the first one(s) didn't provide that font.\n//they're provided as fallbacks.\n#define MAX_FACES 32\n\nenum fontfmt_e\n{\n\tFMT_AUTO,\t\t//freetype, or quake\n\tFMT_QUAKE,\t\t//first is default\n\tFMT_ISO88591,\t//latin-1 (first 256 chars of unicode too, c1 glyphs are usually invisible)\n\tFMT_WINDOWS1252,//variation of latin-1 with extra glyphs\n\tFMT_KOI8U,\t\t//image is 16*16 koi8-u codepage.\n\tFMT_HORIZONTAL,\t//unicode, charcount=width/(height-2). single strip of chars, like halflife.\n};\n\ntypedef struct fontface_s\n{\n\tstruct fontface_s *fnext;\n\tstruct fontface_s **flink;\t//like prev, but not.\n\tchar name[MAX_OSPATH];\n\tint refs;\n\n\tstruct\n\t{\n\t\tqbyte *data;\n\t\tsize_t rows;\t//urgh.\n\t\tsize_t stride;\n\t\tsize_t charheight;\n\t\tenum fontfmt_e codepage;\n\t\tqboolean paletted;\n\t} horiz;\n\n#ifdef HALFLIFEMODELS\n\tstruct\n\t{\n\t\tint fontheight1;\n\t\tint imgheight;\n\t\tint rows;\n\t\tint fontheight2;\n\n\t\tstruct\n\t\t{\n\t\t\tqbyte x;\n\t\t\tqbyte y;\n\t\t\tqbyte width;\n\t\t\tqbyte pad;\n\t\t} chartab[256];\n\n//\t\tint unk[4];\n\n\t\tqbyte data[1];//[256*imgheight];\n\t\t//int pad\n\t\t//palette[256*3];\n\t} *halflife;\n#endif\n\n#ifdef AVAIL_FREETYPE\n\tstruct\n\t{\n\t\tint activeheight;\t//needs reconfiguring when different sizes are used (so the same face can be used at multiple different sizes\n\t\tint actualsize;\t\t//sometimes that activeheight isn't usable and we need to manually rescale. :(\n\t\tFT_Face face;\n\t\tvoid *membuf;\n\t} ft;\n#endif\n} fontface_t;\nstatic fontface_t *faces;\n\n\n#define GEN_CONCHAR_GLYPHS 0\t//set to 0 or 1 to define whether to generate glyphs from conchars too, or if it should just draw them as glquake always used to\nextern cvar_t cl_noblink;\nextern cvar_t con_ocranaleds;\n\ntypedef struct font_s\n{\n\t//FIXME: use a hash table? will need it if we go beyond ucs-2.\n\t//currently they're in a single block so the font can be checked from scanning the active chars when shutting down. we could instead scan all 65k chars in every font instead...\n\tstruct charcache_s\n\t{\n\t\tstruct charcache_s *nextchar;\n\n\t\tFIMAGEIDXTYPE texplane;\n\t\tunsigned char advance;\t//how wide this char is, when drawn\n\t\tunsigned short block;\t//to quickly find the char again\n\n\t\t//texture offsets.\n\t\tunsigned short bmx;\n\t\tunsigned short bmy;\n\n\t\t//texture/screen pixel sizes.\n\t\tunsigned char bmw;\n\t\tunsigned char bmh;\n\t\tunsigned short flags;\n#define CHARF_FORCEWHITE (1u<<0)\t//coloured emoji should not be tinted.\n\n\t\t//screen offsets.\n\t\tshort top;\n\t\tshort left;\n\t} *chars[FONTBLOCKS];\n\tchar name[MAX_OSPATH];\n\n\ttexid_t singletexture;\n\tunsigned short charheight;\t\t//requested height (space between lines)\n\tunsigned short truecharheight;\t//what you actually got, for compat with dp's lets-use-the-wrong-size-for-double-padding-between-lines thing.\n\tfloat scale;\t//some sort of poop\n\tshort outline;\n\tunsigned int flags;\n\n\tunsigned short faces;\n\tfontface_t *face[MAX_FACES];\n\n\tstruct font_s *alt;\n\tvec3_t tint;\n\tvec3_t alttint;\n} font_t;\n\nunion byte_vec4_u\n{\n\tbyte_vec4_t rgba;\n\tquint32_t c;\n};\n\n//shared between fonts.\ntypedef struct {\n\ttexid_t texnum[FONTIMAGES];\n\ttexid_t defaultfont;\n\ttexid_t trackerimage;\n\n\tunion byte_vec4_u plane[PLANEWIDTH*PLANEHEIGHT];\t//tracks the current plane\n\tFIMAGEIDXTYPE activeplane;\n\tunsigned short planerowx;\n\tunsigned short planerowy;\n\tunsigned short planerowh;\n\tqboolean planechanged;\n\n\tstruct charcache_s *oldestchar;\n\tstruct charcache_s *newestchar;\n\tshader_t *shader;\n\tshader_t *backshader;\n} fontplanes_t;\nstatic fontplanes_t fontplanes;\n\n#define FONT_CHAR_BUFFER 512\nstatic index_t font_indicies[FONT_CHAR_BUFFER*6];\nstatic vecV_t font_coord[FONT_CHAR_BUFFER*4];\nstatic vecV_t font_backcoord[FONT_CHAR_BUFFER*4];\nstatic vec2_t font_texcoord[FONT_CHAR_BUFFER*4];\nstatic union byte_vec4_u font_forecoloura[FONT_CHAR_BUFFER*4];\nstatic union byte_vec4_u font_backcoloura[FONT_CHAR_BUFFER*4];\nstatic mesh_t font_foremesh;\nstatic mesh_t font_backmesh;\nstatic texid_t font_texture;\nstatic int font_colourmask;\nstatic union byte_vec4_u font_forecolour;\nstatic union byte_vec4_u font_backcolour;\nstatic avec4_t\tfont_foretint;\n\nstatic struct font_s *curfont;\nstatic float curfont_scale[2];\n//static qboolean curfont_scaled;\n\nextern cvar_t r_font_linear;\n\n//^Ue2XX\nstatic struct\n{\n\timage_t *image;\n\tchar name[64];\n} trackerimages[256];\nstatic int numtrackerimages;\n#define TRACKERFIRST 0xe200\n#define TRACKERCOUNT  0x100\t//an upper bound. so misused codepoints won't go weird.\nint Font_RegisterTrackerImage(const char *image)\n{\n\tint i;\n\tfor (i = 0; i < numtrackerimages; i++)\n\t{\n\t\tif (!strcmp(trackerimages[i].name, image))\n\t\t\treturn TRACKERFIRST + i;\n\t}\n\tif (numtrackerimages == TRACKERCOUNT)\n\t\treturn 0;\n\ttrackerimages[i].image = NULL;\t//actually load it elsewhere, because we're lazy.\n\tQ_strncpyz(trackerimages[i].name, image, sizeof(trackerimages[i].name));\n\tnumtrackerimages++;\n\treturn TRACKERFIRST + i;\n}\n//called from the font display code for tracker images\nstatic image_t *Font_GetTrackerImage(unsigned int imid)\n{\n\tif (!trackerimages[imid].image)\n\t{\n\t\tif (!*trackerimages[imid].name)\n\t\t\treturn NULL;\n\t\ttrackerimages[imid].image = Image_GetTexture(trackerimages[imid].name, NULL, IF_PREMULTIPLYALPHA|IF_UIPIC|IF_NOPURGE, NULL, NULL, 0, 0, TF_INVALID);\n\t}\n\tif (!trackerimages[imid].image)\n\t\treturn NULL;\n\tif (trackerimages[imid].image->status != TEX_LOADED)\n\t\treturn NULL;\n\treturn trackerimages[imid].image;\n}\nqboolean Font_TrackerValid(unsigned int imid)\n{\n\timid -= TRACKERFIRST;\n\tif (imid >= countof(trackerimages))\n\t\treturn false;\n\tif (!trackerimages[imid].image)\n\t{\n\t\tif (!*trackerimages[imid].name)\n\t\t\treturn false;\n\t\ttrackerimages[imid].image = Image_GetTexture(trackerimages[imid].name, NULL, IF_PREMULTIPLYALPHA|IF_UIPIC|IF_NOPURGE, NULL, NULL, 0, 0, TF_INVALID);\n\t}\n\tif (!trackerimages[imid].image)\n\t\treturn false;\n\tif (trackerimages[imid].image->status == TEX_LOADING)\n\t\tCOM_WorkerPartialSync(trackerimages[imid].image, &trackerimages[imid].image->status, TEX_LOADING);\n\tif (trackerimages[imid].image->status != TEX_LOADED)\n\t\treturn false;\n\treturn true;\n}\n\n//called at load time - initalises font buffers\nvoid Font_Init(void)\n{\n\tint i;\n\tTEXASSIGN(fontplanes.defaultfont, r_nulltex);\n\n\t//clear tracker images, just in case they were still set for the previous renderer context\n\tfor (i = 0; i < sizeof(trackerimages)/sizeof(trackerimages[0]); i++)\n\t\ttrackerimages[i].image = NULL;\n\n\tfont_foremesh.indexes = font_indicies;\n\tfont_foremesh.xyz_array = font_coord;\n\tfont_foremesh.st_array = font_texcoord;\n\tfont_foremesh.colors4b_array = &font_forecoloura->rgba;\n\n\tfont_backmesh.indexes = font_indicies;\n\tfont_backmesh.xyz_array = font_backcoord;\n\tfont_backmesh.st_array = font_texcoord;\n\tfont_backmesh.colors4b_array = &font_backcoloura->rgba;\n\n\tfor (i = 0; i < FONT_CHAR_BUFFER; i++)\n\t{\n\t\tfont_indicies[i*6+0] = i*4+0;\n\t\tfont_indicies[i*6+1] = i*4+1;\n\t\tfont_indicies[i*6+2] = i*4+2;\n\t\tfont_indicies[i*6+3] = i*4+0;\n\t\tfont_indicies[i*6+4] = i*4+2;\n\t\tfont_indicies[i*6+5] = i*4+3;\n\t}\n\n\tfor (i = 0; i < FONTPLANES; i++)\n\t{\n\t\tTEXASSIGN(fontplanes.texnum[i], Image_CreateTexture(\"***fontplane***\", NULL, IF_UIPIC|(r_font_linear.ival?IF_LINEAR:IF_NEAREST|IF_NOPURGE)|IF_NOPICMIP|IF_NOMIPMAP|IF_NOGAMMA|IF_NOPURGE));\n\t}\n\n\tfontplanes.shader = R_RegisterShader(\"ftefont\", SUF_2D,\n\t\t\"{\\n\"\n\t\t\t\"fullrate\\n\"\t//don't hurt readability of text.\n\t\t\t\"if $nofixed\\n\"\n\t\t\t\t\"program default2d\\n\"\n\t\t\t\"endif\\n\"\n\t\t\t\"nomipmaps\\n\"\n\t\t\t\"{\\n\"\n\t\t\t\t\"if r_font_linear\\n\"\n\t\t\t\t\t\"map $linear:$diffuse\\n\"\n\t\t\t\t\"else\\n\"\n\t\t\t\t\t\"map $nearest:$diffuse\\n\"\n\t\t\t\t\"endif\\n\"\n\t\t\t\t\"rgbgen vertex\\n\"\n\t\t\t\t\"alphagen vertex\\n\"\n\t\t\t\t\"blendfunc gl_one gl_one_minus_src_alpha\\n\"\n\t\t\t\"}\\n\"\n\t\t\"}\\n\"\n\t\t);\n\n\tfontplanes.backshader = R_RegisterShader(\"ftefontback\", SUF_2D,\n\t\t\"{\\n\"\n\t\t\t\"nomipmaps\\n\"\n\t\t\t\"{\\n\"\n\t\t\t\t\"map $whiteimage\\n\"\n\t\t\t\t\"rgbgen vertex\\n\"\n\t\t\t\t\"alphagen vertex\\n\"\n\t\t\t\t\"blendfunc gl_one gl_one_minus_src_alpha\\n\"\n\t\t\t\"}\\n\"\n\t\t\"}\\n\"\n\t\t);\n\n\tfont_colourmask = ~0;\n}\n\n//flush the font buffer, by drawing it to the screen\nstatic void Font_Flush(void)\n{\n\tR2D_Flush = NULL;\n\tif (!font_foremesh.numindexes)\n\t\treturn;\n\tif (fontplanes.planechanged)\n\t{\n\t\tImage_Upload(fontplanes.texnum[fontplanes.activeplane], TF_RGBA32, (void*)fontplanes.plane, NULL, PLANEWIDTH, PLANEHEIGHT, 1, IF_UIPIC|IF_NEAREST|IF_NOPICMIP|IF_NOMIPMAP|IF_NOGAMMA|IF_NOPURGE);\n\n\t\tfontplanes.planechanged = false;\n\t}\n\tfont_foremesh.istrifan = (font_foremesh.numvertexes == 4);\n\tif ((font_colourmask & (CON_RICHFORECOLOUR|CON_NONCLEARBG)) == CON_NONCLEARBG && font_foremesh.numindexes)\n\t{\n\t\tfont_backmesh.numindexes = font_foremesh.numindexes;\n\t\tfont_backmesh.numvertexes = font_foremesh.numvertexes;\n\t\tfont_backmesh.istrifan = font_foremesh.istrifan;\n\n\t\tBE_DrawMesh_Single(fontplanes.backshader, &font_backmesh, NULL, font_be_flags);\n\t}\n\tTEXASSIGN(fontplanes.shader->defaulttextures->base, font_texture);\n\tBE_DrawMesh_Single(fontplanes.shader, &font_foremesh, NULL, font_be_flags);\n\tfont_foremesh.numindexes = 0;\n\tfont_foremesh.numvertexes = 0;\n}\n\nstatic int Font_BeginChar(texid_t tex)\n{\n\tint fvert;\n\n\tif (font_foremesh.numindexes >= FONT_CHAR_BUFFER*6 || font_texture != tex)\n\t{\n\t\tFont_Flush();\n\t\tTEXASSIGNF(font_texture, tex);\n\t}\n\n\tfvert = font_foremesh.numvertexes;\n\t\n\tfont_foremesh.numindexes += 6;\n\tfont_foremesh.numvertexes += 4;\n\n\treturn fvert;\n}\n\n//clear the shared planes and free memory etc\nvoid Font_Shutdown(void)\n{\n\tint i;\n\n\tfor (i = 0; i < FONTPLANES; i++)\n\t\tTEXASSIGN(fontplanes.texnum[i], r_nulltex);\n\tfontplanes.activeplane = 0;\n\tfontplanes.oldestchar = NULL;\n\tfontplanes.newestchar = NULL;\n\tfontplanes.planechanged = 0;\n\tfontplanes.planerowx = 0;\n\tfontplanes.planerowy = 0;\n\tfontplanes.planerowh = 0;\n}\n\n//we got too many chars and switched to a new plane - purge the chars in that plane\nvoid Font_FlushPlane(void)\n{\n\t/*\n\tassumption:\n\toldest chars must be of the oldest plane\n\t*/\n\n\t//we've not broken anything yet, flush while we can\n\tFont_Flush();\n\n\tif (fontplanes.planechanged)\n\t{\n\t\tImage_Upload(fontplanes.texnum[fontplanes.activeplane], TF_RGBA32, (void*)fontplanes.plane, NULL, PLANEWIDTH, PLANEHEIGHT, 1, IF_UIPIC|IF_NEAREST|IF_NOPICMIP|IF_NOMIPMAP|IF_NOGAMMA|IF_NOPURGE);\n\n\t\tfontplanes.planechanged = false;\n\t}\n\n\tfontplanes.activeplane++;\n\tfontplanes.activeplane = fontplanes.activeplane % FONTPLANES;\n\tfontplanes.planerowh = 0;\n\tfontplanes.planerowx = 0;\n\tfontplanes.planerowy = 0;\n\n\twhile (fontplanes.oldestchar)\n\t{\n\t\tif (fontplanes.oldestchar->texplane != fontplanes.activeplane)\n\t\t\tbreak;\n\n\t\t//remove it from the list of active chars, and invalidate it\n\t\tfontplanes.oldestchar->texplane = INVALIDPLANE;\n\t\tfontplanes.oldestchar = fontplanes.oldestchar->nextchar;\n\t}\n\tif (!fontplanes.oldestchar)\n\t\tfontplanes.newestchar = NULL;\n}\n\nstatic struct charcache_s *Font_GetCharIfLoaded(font_t *f, unsigned int charidx)\n{\n\tstruct charcache_s *c = f->chars[charidx/FONTBLOCKSIZE];\n\tif (c)\n\t{\n\t\tc += charidx&FONTBLOCKMASK;\n\t\tif (c->texplane == INVALIDPLANE)\n\t\t\tc = NULL;\n\t}\n\treturn c;\n}\nstatic struct charcache_s *Font_GetCharStore(font_t *f, unsigned int charidx)\n{\t//should only be called if generating a char cache\n\tstruct charcache_s *c;\n\tsize_t i;\n\tc = f->chars[charidx/FONTBLOCKSIZE];\n\tif (!c)\n\t{\n\t\tc = Z_Malloc(sizeof(struct charcache_s) * FONTBLOCKSIZE);\n\t\tf->chars[charidx/FONTBLOCKSIZE] = c;\n\t\tfor (i = 0; i < FONTBLOCKSIZE; i++)\n\t\t{\n\t\t\tc[i].texplane = INVALIDPLANE;\n\t\t}\n\t}\n\tc += charidx&FONTBLOCKMASK;\n\tc->block = charidx/FONTBLOCKSIZE;\n\treturn c;\n}\nstatic struct charcache_s *Font_CopyChar(font_t *f, unsigned int oldcharidx, unsigned int newcharidx)\n{\n\tstruct charcache_s *new, *old = Font_GetCharIfLoaded(f, oldcharidx);\n\tif (!old)\n\t\treturn NULL;\n\tnew = Font_GetCharStore(f, newcharidx);\n\t*new = *old;\n\tnew->block = newcharidx/FONTBLOCKSIZE;\n\treturn new;\n}\n\n//loads a new image into a given character slot for the given font.\n//note: make sure it doesn't already exist or things will get cyclic\n//alphaonly says if its a greyscale image. false means rgba.\nstatic struct charcache_s *Font_LoadGlyphData(font_t *f, CHARIDXTYPE charidx, FT_Pixel_Mode pixelmode, void *data, unsigned int bmw, unsigned int bmh, unsigned int pitch){\n\tint x, y;\n\tunion byte_vec4_u *out;\n\tstruct charcache_s *c = Font_GetCharStore(f, charidx);\n\tint pad = 0;\n#define BORDERCOLOUR 0\n\n#define MAXOUTLINE 4\n\n\tint outline = min(f->outline, MAXOUTLINE);\n\n\tif (!bmw || !bmh)\n\t\toutline = 0;\n\n\tpad+=outline;\n\t\n\tif (fontplanes.texnum[0]->flags & IF_LINEAR)\n\t\tpad += 1;\t//pad the image data to avoid sampling outside\n\n\tif (fontplanes.planerowx + (int)bmw+pad*2 >= PLANEWIDTH)\n\t{\n\t\tfontplanes.planerowx = 0;\n\t\tfontplanes.planerowy += fontplanes.planerowh;\n\t\tfontplanes.planerowh = 0;\n\t}\n\n\tif (fontplanes.planerowy+(int)bmh+pad*2 >= PLANEHEIGHT)\n\t\tFont_FlushPlane();\n\n\tif (fontplanes.newestchar)\n\t\tfontplanes.newestchar->nextchar = c;\n\telse\n\t\tfontplanes.oldestchar = c;\n\tfontplanes.newestchar = c;\n\tc->nextchar = NULL;\n\tc->flags = 0;\n\n\tc->texplane = fontplanes.activeplane;\n\tc->bmx = fontplanes.planerowx+pad;\n\tc->bmy = fontplanes.planerowy+pad;\n\tc->bmw = bmw;\n\tc->bmh = bmh;\n\n\tif (fontplanes.planerowh < (int)bmh+pad*2)\n\t\tfontplanes.planerowh = bmh+pad*2;\n\tfontplanes.planerowx += bmw+pad*2;\n\n\tout = &fontplanes.plane[c->bmx+((int)c->bmy-pad)*PLANEHEIGHT];\n\tif (pixelmode == FT_PIXEL_MODE_GRAY)\n\t{\t//8bit font\n\t\tfor (y = -pad; y < 0; y++)\n\t\t{\n\t\t\tfor (x = -pad; x < (int)bmw+pad; x++)\n\t\t\t\tout[x].c = BORDERCOLOUR;\n\t\t\tout += PLANEWIDTH;\n\t\t}\n\t\tfor (; y < bmh; y++)\n\t\t{\n\t\t\tfor (x = -pad; x < 0; x++)\n\t\t\t\tout[x].c = BORDERCOLOUR;\n\t\t\tfor (; x < bmw; x++)\n\t\t\t\tout[x].c = 0x01010101 * ((unsigned char*)data)[x];\n\t\t\tfor (; x < bmw+pad; x++)\n\t\t\t\tout[x].c = BORDERCOLOUR;\n\t\t\tdata = (char*)data + pitch;\n\t\t\tout += PLANEWIDTH;\n\t\t}\n\t\tfor (; y < bmh+pad; y++)\n\t\t{\n\t\t\tfor (x = -pad; x < (int)bmw+pad; x++)\n\t\t\t\tout[x].c = BORDERCOLOUR;\n\t\t\tout += PLANEWIDTH;\n\t\t}\n\t}\n\telse if (pixelmode == FT_PIXEL_MODE_MONO)\n\t{\t//1bit font (\n\t\tfor (y = -pad; y < 0; y++)\n\t\t{\n\t\t\tfor (x = -pad; x < (int)bmw+pad; x++)\n\t\t\t\tout[x].c = BORDERCOLOUR;\n\t\t\tout += PLANEWIDTH;\n\t\t}\n\t\tfor (; y < bmh; y++)\n\t\t{\n\t\t\tfor (x = -pad; x < 0; x++)\n\t\t\t\tout[x].c = BORDERCOLOUR;\n\t\t\tfor (; x < bmw; x++)\n\t\t\t\tout[x].c = (((unsigned char*)data)[x>>3]&(1<<(7-(x&7))))?0xffffffff:0;\n\t\t\tfor (; x < bmw+pad; x++)\n\t\t\t\tout[x].c = BORDERCOLOUR;\n\t\t\tdata = (char*)data + pitch;\n\t\t\tout += PLANEWIDTH;\n\t\t}\n\t\tfor (; y < bmh+pad; y++)\n\t\t{\n\t\t\tfor (x = -pad; x < (int)bmw+pad; x++)\n\t\t\t\tout[x].c = BORDERCOLOUR;\n\t\t\tout += PLANEWIDTH;\n\t\t}\n\t}\n\telse if ((unsigned int)pixelmode == FT_PIXEL_MODE_RGBA_SA)\n\t{\t//rgba source using standard alpha.\n\t\t//(we'll multiply out the alpha for the gpu)\n\t\tfor (y = -pad; y < 0; y++)\n\t\t{\n\t\t\tfor (x = -pad; x < (int)bmw+pad; x++)\n\t\t\t\tout[x].c = BORDERCOLOUR;\n\t\t\tout += PLANEWIDTH;\n\t\t}\n\t\tfor (; y < bmh; y++)\n\t\t{\n\t\t\tfor (x = -pad; x < 0; x++)\n\t\t\t\tout[x].c = BORDERCOLOUR;\n\t\t\tfor (; x < bmw; x++)\n\t\t\t{\n\t\t\t\tif (((unsigned char*)data)[x*4+3] == 255)\n\t\t\t\t\tout[x] = ((union byte_vec4_u*)data)[x];\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tout[x].rgba[0] = (((unsigned char*)data)[x*4+3]*((unsigned char*)data)[x*4+0])<<8;\n\t\t\t\t\tout[x].rgba[1] = (((unsigned char*)data)[x*4+3]*((unsigned char*)data)[x*4+1])<<8;\n\t\t\t\t\tout[x].rgba[2] = (((unsigned char*)data)[x*4+3]*((unsigned char*)data)[x*4+2])<<8;\n\t\t\t\t\tout[x].rgba[3] = ((unsigned char*)data)[x*4+3];\n\t\t\t\t}\n\t\t\t}\n\t\t\tfor (; x < bmw+pad; x++)\n\t\t\t\tout[x].c = BORDERCOLOUR;\n\t\t\tdata = (char*)data + pitch;\n\t\t\tout += PLANEWIDTH;\n\t\t}\n\t\tfor (; y < (int)bmh+pad; y++)\n\t\t{\n\t\t\tfor (x = -pad; x < (int)bmw+pad; x++)\n\t\t\t\tout[x].c = BORDERCOLOUR;\n\t\t\tout += PLANEWIDTH;\n\t\t}\n\t}\n\telse if (pixelmode == FT_PIXEL_MODE_BGRA)\n\t{\t//bgra srgb font, already premultiplied\n\t\tfor (y = -pad; y < 0; y++)\n\t\t{\n\t\t\tfor (x = -pad; x < (int)bmw+pad; x++)\n\t\t\t\tout[x].c = BORDERCOLOUR;\n\t\t\tout += PLANEWIDTH;\n\t\t}\n\t\tfor (; y < bmh; y++)\n\t\t{\n\t\t\tfor (x = -pad; x < 0; x++)\n\t\t\t\tout[x].c = BORDERCOLOUR;\n\t\t\tfor (; x < bmw; x++)\n\t\t\t{\n\t\t\t\tout[x].rgba[0] = ((unsigned char*)data)[x*4+2];\n\t\t\t\tout[x].rgba[1] = ((unsigned char*)data)[x*4+1];\n\t\t\t\tout[x].rgba[2] = ((unsigned char*)data)[x*4+0];\n\t\t\t\tout[x].rgba[3] = ((unsigned char*)data)[x*4+3];\n\t\t\t}\n\t\t\tfor (; x < bmw+pad; x++)\n\t\t\t\tout[x].c = BORDERCOLOUR;\n\t\t\tdata = (char*)data + pitch;\n\t\t\tout += PLANEWIDTH;\n\t\t}\n\t\tfor (; y < bmh+pad; y++)\n\t\t{\n\t\t\tfor (x = -pad; x < (int)bmw+pad; x++)\n\t\t\t\tout[x].c = BORDERCOLOUR;\n\t\t\tout += PLANEWIDTH;\n\t\t}\n\n\t\tc->flags = CHARF_FORCEWHITE;\t//private glyph colours\n\t}\n\telse if ((unsigned int)pixelmode == FT_PIXEL_MODE_RGBA)\n\t{\t//bgra srgb font, already premultiplied\n\t\tfor (y = -pad; y < 0; y++)\n\t\t{\n\t\t\tfor (x = -pad; x < (int)bmw+pad; x++)\n\t\t\t\tout[x].c = BORDERCOLOUR;\n\t\t\tout += PLANEWIDTH;\n\t\t}\n\t\tfor (; y < bmh; y++)\n\t\t{\n\t\t\tfor (x = -pad; x < 0; x++)\n\t\t\t\tout[x].c = BORDERCOLOUR;\n\t\t\tfor (; x < bmw; x++)\n\t\t\t\tout[x].c = ((unsigned int*)data)[x];\n\t\t\tfor (; x < bmw+pad; x++)\n\t\t\t\tout[x].c = BORDERCOLOUR;\n\t\t\tdata = (char*)data + pitch;\n\t\t\tout += PLANEWIDTH;\n\t\t}\n\t\tfor (; y < bmh+pad; y++)\n\t\t{\n\t\t\tfor (x = -pad; x < (int)bmw+pad; x++)\n\t\t\t\tout[x].c = BORDERCOLOUR;\n\t\t\tout += PLANEWIDTH;\n\t\t}\n\n\t\tc->flags = CHARF_FORCEWHITE;\t//private glyph colours\n\t}\n\n\tif (outline)\n\t{\n\t\tstatic int filter_outline;\n\t\tstatic unsigned char filter_highest[MAXOUTLINE*2+1][MAXOUTLINE*2+1];\n\t\tif (outline != filter_outline)\n\t\t{\n\t\t\tfilter_outline = outline;\n\t\t\tfor (y = -outline; y <= outline; y++)\n\t\t\t\tfor (x = -outline; x <= outline; x++)\n\t\t\t\t\tfilter_highest[outline+y][outline+x] = bound(0, (outline + 1 - sqrt(x*x + y*y))*255+.5, 255);\n\t\t}\n\n\t\t//expand it to out full(ish) size\n\n\t\tif (pixelmode == FT_PIXEL_MODE_MONO)\n\t\t{\n\t\t\tqbyte *alpha = (char*)data - pitch*bmh;\n\t\t\tqbyte a;\n\t\t\tint bit;\n\n\t\t\talpha -= pitch*outline;\n\t\t\tout = &fontplanes.plane[c->bmx+((int)c->bmy-outline)*PLANEHEIGHT];\n\t\t\tfor (y = -outline; y < (int)bmh+outline; y++, out += PLANEWIDTH)\n\t\t\t\tfor (x = -outline; x < (int)bmw+outline; x++)\n\t\t\t\t{\n\t\t\t\t\tint xn, x1 = max(outline-x, 0), x2 = min(2*outline, (int)bmw-1-x+outline);\n\t\t\t\t\tint yn, y1 = max(outline-y, 0), y2 = min(2*outline, (int)bmh-1-y+outline);\n\t\t\t\t\tint v, m = out[x].rgba[3]*255;\n\t\t\t\t\tfor (yn = y1; yn <= y2; yn++)\n\t\t\t\t\t\tfor (xn = x1; xn <= x2; xn++)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tbit = (xn+x)-outline;\n\t\t\t\t\t\t\ta = alpha[(bit>>3)+(yn+y)*pitch];\n\t\t\t\t\t\t\ta = (a&(1<<(7-(bit&7))))?0xff:0;\n\n\t\t\t\t\t\t\tv = filter_highest[yn][xn] * a;\n\t\t\t\t\t\t\tm = max(m, v);\n\t\t\t\t\t\t}\n\t\t\t\t\t//out[x].c = 0;\n\t\t\t\t\tout[x].rgba[3] = m/255;\n\t\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tint bytes = (pixelmode == FT_PIXEL_MODE_GRAY)?1:4;\n\t\t\tqbyte *alpha = (char*)data + bytes-1 - pitch*bmh;\n\n\t\t\talpha -= pitch*outline + bytes*outline;\n\t\t\tout = &fontplanes.plane[c->bmx+((int)c->bmy-outline)*PLANEHEIGHT];\n\t\t\tfor (y = -outline; y < (int)bmh+outline; y++, out += PLANEWIDTH)\n\t\t\t\tfor (x = -outline; x < (int)bmw+outline; x++)\n\t\t\t\t{\n\t\t\t\t\tint xn, x1 = max(outline-x, 0), x2 = min(2*outline, (int)bmw-1-x+outline);\n\t\t\t\t\tint yn, y1 = max(outline-y, 0), y2 = min(2*outline, (int)bmh-1-y+outline);\n\t\t\t\t\tint v, m = out[x].rgba[3]*255;\n\t\t\t\t\tfor (yn = y1; yn <= y2; yn++)\n\t\t\t\t\t\tfor (xn = x1; xn <= x2; xn++)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tv = filter_highest[yn][xn] * alpha[(xn+x)*bytes+(yn+y)*pitch];\n\t\t\t\t\t\t\tm = max(m, v);\n\t\t\t\t\t\t}\n\t\t\t\t\t//out[x].c = 0;\n\t\t\t\t\tout[x].rgba[3] = m/255;\n\t\t\t\t}\n\t\t}\n\n\n\t\tc->bmx -= outline;\n\t\tc->bmy -= outline;\n\t\tc->bmw += outline*2;\n\t\tc->bmh += outline*2;\n\t}\n\n\tfontplanes.planechanged = true;\n\treturn c;\n}\n\nunsigned short hex[16] = {\n/*0*/\t(7<<0)|(5<<3)|(5<<6)|(5<<9)|(7<<12),\n/*1*/\t(2<<0)|(2<<3)|(2<<6)|(2<<9)|(2<<12),\n/*2*/\t(7<<0)|(1<<3)|(7<<6)|(4<<9)|(7<<12),\n/*3*/\t(7<<0)|(1<<3)|(3<<6)|(1<<9)|(7<<12),\n/*4*/\t(5<<0)|(5<<3)|(7<<6)|(1<<9)|(1<<12),\n/*5*/\t(7<<0)|(4<<3)|(7<<6)|(1<<9)|(7<<12),\n/*6*/\t(4<<0)|(4<<3)|(7<<6)|(5<<9)|(7<<12),\n/*7*/\t(7<<0)|(1<<3)|(1<<6)|(1<<9)|(1<<12),\n/*8*/\t(7<<0)|(5<<3)|(7<<6)|(5<<9)|(7<<12),\n/*9*/\t(7<<0)|(5<<3)|(7<<6)|(1<<9)|(1<<12),\n/*A*/\t(7<<0)|(5<<3)|(7<<6)|(5<<9)|(5<<12),\n/*B*/\t(6<<0)|(5<<3)|(7<<6)|(5<<9)|(6<<12),\n/*C*/\t(7<<0)|(5<<3)|(4<<6)|(5<<9)|(7<<12),\n/*D*/\t(6<<0)|(5<<3)|(5<<6)|(5<<9)|(6<<12),\n/*E*/\t(7<<0)|(4<<3)|(6<<6)|(4<<9)|(7<<12),\n/*F*/\t(7<<0)|(4<<3)|(6<<6)|(4<<9)|(4<<12)\n};\nstatic struct charcache_s *Font_LoadPlaceholderGlyph(font_t *f, CHARIDXTYPE charidx)\n{\n\tstruct charcache_s *c;\n\tunsigned int out[169*4], i, o, g, b, w, h, d, n;\n\tif (charidx == 0xe080 || charidx == 0xe081 || charidx == 0xe082 || charidx == 0xe083)\n\t{\t//scrollbar glyps\n\t\tw = min(16,f->charheight);\n\t\th = max(1,w/8);\n\t\td = 0;\n\n\t\tif (charidx == 0xe083)\t//centre\n\t\t{\n\t\t\th *= 3;\n\t\t\tmemset(out+w*h*0, 0xff, w*4*h);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tmemset(out+w*h*0, 0xff, w*4*h);\n\t\t\tmemset(out+w*h*1, 0x00, w*4*h);\n\t\t\tmemset(out+w*h*2, 0xff, w*4*h);\n\t\t\tfor (i = 0; i < h; i++)\n\t\t\t{\n\t\t\t\tif (charidx == 0xe080)\t//left\n\t\t\t\t\tmemset(out+w*(i+h*1), 0xff, h*4);\n\t\t\t\tif (charidx == 0xe082)\t//right\n\t\t\t\t\tmemset(out+w*(i+h*1)+w-h, 0xff, h*4);\n\t\t\t}\n\t\t\th *= 3;\n\t\t}\n\t}\n\telse if (charidx == 0xe01d || charidx == 0xe01e || charidx == 0xe01f)\n\t{\t//horizontal separator\n\t\tw = min(16,f->charheight);\n\t\th = max(1,w/8);\n\t\td = 0;\n\t\tmemset(out, 0xff, w*4*h);\n\t}\n\telse if (charidx == 0xe00b)\n\t{\t//console input cursor\n\t\th = min(16,f->charheight);\n\t\tw = max(1,h/8);\n\t\td = 1;\n\t\tmemset(out, 0xff, w*4*h);\n\t}\n\telse if (charidx == 0xe00d)\n\t{\t//filled > arrow indicator (used by the menus)\n\t\th = min(16,f->charheight);\n\t\tw = (h+1)/2;\n\t\td = 1;\n\t\tmemset(out, 0xff, w*4*h);\n\t\tfor (i = 0; i < w; i++)\n\t\t{\n\t\t\tmemset(out + w*i + (i+1), 0x0, (w-i-1)*4);\n\t\t\tmemset(out + w*(h-i-1) + (i+1), 0x0, (w-i-1)*4);\n\t\t}\n\t}\n\telse\n\t{\n\t\td = (f->charheight >= 11);\n\t\tif (d)\n\t\t{\t//two rows\n\t\t\th = 11;\n\t\t\tif (charidx > 0xffff)\n\t\t\t\tn = 3;\n\t\t\telse if (charidx > 0xff)\n\t\t\t\tn = 2;\n\t\t\telse\n\t\t\t\tn = 1;\n\t\t\tw = n*4-1;\n\t\t\tn*=2;\n\t\t}\n\t\telse\n\t\t{\t//single row. bye bye fixed-width.\n\t\t\tif (charidx > 0xfffff)\n\t\t\t\tn = 6;\n\t\t\telse if (charidx > 0xffff)\n\t\t\t\tn = 5;\n\t\t\telse if (charidx > 0xfff)\n\t\t\t\tn = 4;\n\t\t\telse if (charidx > 0xff)\n\t\t\t\tn = 3;\n\t\t\telse\n\t\t\t\tn = 2;\n\t\t\tw = n*4-1;\n\t\t\th = 5;\n\t\t}\n\n\t\t//figure out if we can get away with giving it a little more border to boost readability\n\t\tb = (h+2 < f->charheight);\n\t\tw += b*2;\n\t\th += b*2;\n\n\t\tmemset(out, 0xff, sizeof(out));\n\n\t\tfor (i = 0; i < n; i++)\n\t\t{\n\t\t\tg = hex[0xf & (charidx>>(i<<2))];\n\t\t\to = b + w*b;\n\t\t\tif (d)\n\t\t\t{\t//stradle them over the two rows.\n\t\t\t\tif (i >= (n>>1))\n\t\t\t\t\to += 4*(n-i-1);\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\to += 4*((n>>1)-i-1);\n\t\t\t\t\to += w * 6;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t\to += 4*(n-i-1);\t//just arrange them in order\n\t\t\tfor (; g; g>>=3, o+=w)\n\t\t\t{\n\t\t\t\tif (g & 4)\tout[o+0] = 0xff0000ff;\n\t\t\t\tif (g & 2)\tout[o+1] = 0xff0000ff;\n\t\t\t\tif (g & 1)\tout[o+2] = 0xff0000ff;\n\t\t\t}\n\t\t}\n\n\t\td = 1;\n\t}\n\tc = Font_LoadGlyphData(f, charidx, FT_PIXEL_MODE_RGBA, out, w, h, w*4);\n\tif (c)\n\t{\n\t\tc->advance = w+d;\n\t\tc->left = 0;\n\t\tc->top = (f->charheight-h-1)/2;\n\t}\n\treturn c;\n}\n\nstatic struct charcache_s *Font_TryLoadGlyphRaster(font_t *f, fontface_t *qface, CHARIDXTYPE charidx)\n{\n\tsize_t maxchar = 256;\n\tconst unsigned short *c1tab;\n\tsize_t c1tabsize;\n\tsize_t glyph = charidx;\n\n\tsafeswitch(qface->horiz.codepage)\n\t{\n\tsafedefault:\n\tcase FMT_AUTO:\t\t//shouldn't happen.\n\tcase FMT_ISO88591:\t//all identity.\n\tcase FMT_HORIZONTAL: //erk...\n\t\tc1tab = NULL;\n\t\tc1tabsize = 0;\n\t\tbreak;\n\tcase FMT_WINDOWS1252:\n\t\t{\n\t\t\tstatic const unsigned short win1252[] = {\n\t\t\t\t0x20ac,  0x81,0x201a,0x0192, 0x201e,0x2026,0x2020,0x2021, 0x02c6,0x2030,0x0160,0x2039, 0x0152,  0x8d,0x017d,  0x8f,\n\t\t\t\t  0x90,0x2018,0x2019,0x101c, 0x201d,0x2022,0x2013,0x2014, 0x02dc,0x2122,0x0161,0x203a, 0x0153,  0x9d,0x017e,0x0178};\n\t\t\tc1tab = win1252;\n\t\t\tc1tabsize = sizeof(win1252);\n\t\t}\n\t\tbreak;\n\tcase FMT_KOI8U:\n\t\t{\n\t\t\tstatic const unsigned short koi8u[] = {\n\t\t\t\t0x2500,0x2502,0x250C,0x2510, 0x2514,0x2518,0x251C,0x2524, 0x252C,0x2534,0x253C,0x2580, 0x2584,0x2588,0x258C,0x2590,\n\t\t\t\t0x2591,0x2592,0x2593,0x2320, 0x25A0,0x2219,0x221A,0x2248, 0x2264,0x2265,0x00A0,0x2321, 0x00B0,0x00B2,0x00B7,0x00F7,\n\t\t\t\t0x2550,0x2551,0x2552,0x0451, 0x0454,0x2554,0x0456,0x0457, 0x2557,0x2558,0x2559,0x255A, 0x255B,0x0491,0x255D,0x255E,\n\t\t\t\t0x255F,0x2560,0x2561,0x0401, 0x0404,0x2563,0x0406,0x0407, 0x2566,0x2567,0x2568,0x2569, 0x256A,0x0490,0x256C,0x00A9,\n\t\t\t\t0x044E,0x0430,0x0431,0x0446, 0x0434,0x0435,0x0444,0x0433, 0x0445,0x0438,0x0439,0x043A, 0x043B,0x043C,0x043D,0x043E,\n\t\t\t\t0x043F,0x044F,0x0440,0x0441, 0x0442,0x0443,0x0436,0x0432, 0x044C,0x044B,0x0437,0x0448, 0x044D,0x0449,0x0447,0x044A,\n\t\t\t\t0x042E,0x0410,0x0411,0x0426, 0x0414,0x0415,0x0424,0x0413, 0x0425,0x0418,0x0419,0x041A, 0x041B,0x041C,0x041D,0x041E,\n\t\t\t\t0x041F,0x042F,0x0420,0x0421, 0x0422,0x0423,0x0416,0x0412, 0x042C,0x042B,0x0417,0x0428, 0x042D,0x0429,0x0427,0x042A};\n\t\t\tc1tab = koi8u;\n\t\t\tc1tabsize = sizeof(koi8u);\n\t\t}\n\t\tbreak;\n\tcase FMT_QUAKE:\n\t\t{\n\t\t\tc1tab = NULL;\n\t\t\tc1tabsize = 0;\n\t\t\tif (glyph >= 0xe000 && glyph < 0xe100)\n\t\t\t\tglyph -= 0xe000;\n\t\t\telse if (glyph < 32 || glyph >= 0x80)\n\t\t\t\treturn NULL;\n\t\t}\n\t\tbreak;\n\t}\n\tif (glyph >= maxchar)\n\t\treturn NULL;\n\tif (glyph >= 0x80 && glyph < 0x80+c1tabsize)\n\t{\n\t\tint i;\n\t\tfor (i = 0; ; i++)\n\t\t{\n\t\t\tif (i == c1tabsize)\n\t\t\t\treturn NULL;\n\t\t\tif (glyph == c1tab[i])\n\t\t\t{\n\t\t\t\tglyph = 0x80+i;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\t{\n\t\tint gw = (qface->horiz.stride)/(maxchar/qface->horiz.rows);\n\t\tint gr = glyph / (maxchar/qface->horiz.rows);\n\t\tint gc = glyph - (gr*(maxchar/qface->horiz.rows));\n\t\tint gs = qface->horiz.stride;\n\t\tint gh = qface->horiz.charheight;\n\t\tstruct charcache_s *c;\n\t\tif (qface->horiz.paletted)\n\t\t{\n\t\t\tqbyte *in = (qbyte*)qface->horiz.data + gc*gw + gr*gs*gh;\n\t\t\t/*while (gw >= 1)\n\t\t\t{\n\t\t\t\tint y;\n\t\t\t\tgw--;\t//see if we can strip this column\n\t\t\t\tfor (y = 0; y < gh; y++)\n\t\t\t\t\tif (in[gw+y*gs])\n\t\t\t\t\t\tbreak;\n\t\t\t\tif (y < gh)\n\t\t\t\t{\n\t\t\t\t\tgw++;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}*/\n\t\t\t{\n\t\t\t\tint ngw = (gw * f->charheight) / gh;\n\t\t\t\tint ngh = f->charheight;\n\t\t\t\tint x, y;\n\t\t\t\tunsigned int *out2 = alloca(ngw*ngh*4);\n\t\t\t\tif (ngw&&ngh)\n\t\t\t\t{\n\t\t\t\t\tunsigned int *out1 = alloca(gw*gh*4);\n\t\t\t\t\tfor (y = 0; y < gh; y++)\n\t\t\t\t\t\tfor (x = 0; x < gw; x++)\n\t\t\t\t\t\t\tout1[x+y*gw] = in[x+y*gs]?d_8to24rgbtable[in[x+y*gs]]:0;\n\t\t\t\t\tImage_ResampleTexture(PTI_RGBA8, out1, gw, gh, out2, ngw, ngh);\n\t\t\t\t}\n\t\t\t\tc = Font_LoadGlyphData(f, charidx, FT_PIXEL_MODE_RGBA, out2, ngw, ngh, ngw*4);\n\t\t\t\tgw = ngw;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tunsigned int *in = (unsigned int*)qface->horiz.data + gc*gw + gr*gs*gh;\n\t\t\twhile (gw >= 1)\n\t\t\t{\n\t\t\t\tint y;\n\t\t\t\tgw--;\t//see if we can strip this column\n\t\t\t\tfor (y = 0; y < gh; y++)\n\t\t\t\t\tif (in[gw+y*gs] & 0x00ffffff)\n\t\t\t\t\t\tbreak;\n\t\t\t\tif (y < gh)\n\t\t\t\t{\n\t\t\t\t\tgw++;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (f->charheight != gh)\n\t\t\t{\n\t\t\t\tint ngw = (gw * f->charheight) / gh;\n\t\t\t\tint ngh = f->charheight;\n\t\t\t\tint x, y;\n\t\t\t\tunsigned int *out2 = alloca(ngw*ngh*4);\n\t\t\t\tif (ngw&&ngh)\n\t\t\t\t{\t//we need to repack the input, because Image_ResampleTexture can't handle strides\n\t\t\t\t\tunsigned int *out1 = alloca(gw*gh*4);\n\t\t\t\t\tfor (y = 0; y < gh; y++)\n\t\t\t\t\t\tfor (x = 0; x < gw; x++)\n\t\t\t\t\t\t\tout1[x+y*gw] = in[x+y*gs];\n\t\t\t\t\tImage_ResampleTexture(PTI_RGBA8, out1, gw, gh, out2, ngw, ngh);\n\t\t\t\t}\n\t\t\t\tc = Font_LoadGlyphData(f, charidx, FT_PIXEL_MODE_RGBA, out2, ngw, ngh, ngw*4);\n\t\t\t\tgw = ngw;\n\t\t\t}\n\t\t\telse\n\t\t\t\tc = Font_LoadGlyphData(f, charidx, FT_PIXEL_MODE_RGBA, in, gw, gh, gs*4);\n\t\t}\n\t\tif (!gw)\t//for invisble glyphs (eg: space), we attempt to ensure that there's some substance there. missing spaces is weird.\n\t\t\tgw = gh/3;\n\t\tif (c)\n\t\t{\n\t\t\tc->advance = gw;\n\t\t\tc->left = 0;\n\t\t\tc->top = 0;\n\t\t\tc->flags &= ~CHARF_FORCEWHITE;\n\t\t\treturn c;\n\t\t}\n\t}\n\treturn NULL;\n}\n\n//loads the given charidx for the given font, importing from elsewhere if needed.\nstatic struct charcache_s *Font_TryLoadGlyph(font_t *f, CHARIDXTYPE charidx)\n{\n\tstruct charcache_s *c;\n\n#if GEN_CONCHAR_GLYPHS != 0\n\tif (charidx >= 0xe000 && charidx <= 0xe0ff)\n\t{\n\t\tint cpos = charidx & 0xff;\n\t\tunsigned int img[64*64], *d;\n\t\tunsigned char *s;\n\t\tint scale;\n\t\tint x,y, ys;\n\t\tqbyte *draw_chars = W_GetLumpName(\"conchars\");\n\t\tif (draw_chars)\n\t\t{\n\t\t\td = img;\n\t\t\ts = draw_chars + 8*(cpos&15)+128*8*(cpos/16);\n\n\t\t\tscale = f->charheight/8;\n\t\t\tif (scale < 1)\n\t\t\t\tscale = 1;\n\t\t\tif (scale > 64/8)\n\t\t\t\tscale = 64/8;\n\t\t\t\n\t\t\tfor (y = 0; y < 8; y++)\n\t\t\t{\n\t\t\t\tfor (ys = 0; ys < scale; ys++)\n\t\t\t\t{\n\t\t\t\t\tfor (x = 0; x < 8*scale; x++)\n\t\t\t\t\t\td[x] = d_8to24rgbtable[s[x/scale]];\n\t\t\t\t\td+=8*scale;\n\t\t\t\t}\n\t\t\t\ts+=128;\n\t\t\t}\n\t\t\tc = Font_LoadGlyphData(f, charidx, FT_PIXEL_MODE_RGBA_SA, img, 8*scale, 8*scale, 8*scale*4);\n\t\t\tif (c)\n\t\t\t{\n\t\t\t\tc->advance = 8*scale;\n\t\t\t\tc->left = 0;\n\t\t\t\tc->top = 7*scale;\n\t\t\t}\n\t\t\treturn c;\n\t\t}\n\t\tif ((charidx&0x7f) > 0x20)\n\t\t\tcharidx &= 0x7f;\n\t}\n#endif\n\n#ifdef QUAKEHUD\n\tif (charidx >= 0xe100 && charidx <= 0xe1ff)\n\t{\n\t\tqpic_t *wadimg;\n\t\tqbyte lumptype = 0;\n\t\tunsigned char *src;\n\t\tunsigned int img[64*64];\n\t\tint nw, nh;\n\t\tint x, y;\n\t\tunsigned int stepx, stepy;\n\t\tunsigned int srcx, srcy;\n\t\tsize_t lumpsize = 0;\n\n\t\tif (charidx-0xe100 >= sizeof(imgs)/sizeof(imgs[0]))\n\t\t\twadimg = NULL;\n\t\telse\n\t\t\twadimg = W_GetLumpName(imgs[charidx-0xe100], &lumpsize, &lumptype);\n\t\tif (wadimg && lumptype == TYP_QPIC && lumpsize == 8+wadimg->height*wadimg->width)\n\t\t{\n\t\t\tnh = wadimg->height;\n\t\t\tnw = wadimg->width;\n\t\t\twhile (nh < f->charheight)\n\t\t\t{\n\t\t\t\tnh *= 2;\n\t\t\t\tnw *= 2;\n\t\t\t}\n\t\t\tif (nh > f->charheight)\n\t\t\t{\n\t\t\t\tnw = (nw * f->charheight)/nh;\n\t\t\t\tnh = f->charheight;\n\t\t\t}\n\t\t\tstepy = 0x10000*((float)wadimg->height/nh);\n\t\t\tstepx = 0x10000*((float)wadimg->width/nw);\n\t\t\tif (nh > 64)\n\t\t\t\tnh = 64;\n\t\t\tif (nw > 64)\n\t\t\t\tnw = 64;\n\t\t\tsrcy = 0;\n\t\t\tfor (y = 0; y < nh; y++)\n\t\t\t{\n\t\t\t\tsrc = (unsigned char *)(wadimg->data);\n\t\t\t\tsrc += wadimg->width * (srcy>>16);\n\t\t\t\tsrcy += stepy;\n\t\t\t\tsrcx = 0;\n\t\t\t\tfor (x = 0; x < nw; x++)\n\t\t\t\t{\n\t\t\t\t\timg[x+y*64] = d_8to24rgbtable[src[srcx>>16]];\n\t\t\t\t\tsrcx += stepx;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tc = Font_LoadGlyphData(f, charidx, FT_PIXEL_MODE_RGBA_SA, img, nw, nh, 64*4);\n\t\t\tif (c)\n\t\t\t{\n\t\t\t\tc->flags = CHARF_FORCEWHITE;\t//private glyph colours\n\t\t\t\tc->left = 0;\n\t\t\t\tc->top = f->charheight - nh;\n\t\t\t\tc->advance = nw;\n\t\t\t\treturn c;\n\t\t\t}\n\t\t}\n\t}\n#endif\n\t/*make tab invisible*/\n\tif (charidx == '\\t' || charidx == '\\n')\n\t{\n\t\tc = &f->chars[charidx/FONTBLOCKSIZE][charidx&FONTBLOCKMASK];\n\t\tc->left = 0;\n\t\tc->advance = f->charheight;\n\t\tc->top = 0;\n\n\t\tc->texplane = 0;\n\t\tc->bmx = 0;\n\t\tc->bmy = 0;\n\t\tc->bmw = 0;\n\t\tc->bmh = 0;\n\t\treturn c;\n\t}\n\n\tif (f->faces)\n\t{\n\t\tint file;\n\t\tfor (file = 0; file < f->faces; file++)\n\t\t{\n\t\t\tfontface_t *qface = f->face[file];\n#ifdef AVAIL_FREETYPE\n\t\t\tif (qface->ft.face)\n\t\t\t{\n\t\t\t\tFT_Face face = qface->ft.face;\n\n\t\t\t\tif (qface->ft.activeheight != f->charheight)\n\t\t\t\t{\n\t\t\t\t\tif (!Font_ChangeFTSize(qface, f->charheight))\n\t\t\t\t\t\treturn NULL;\t//some sort of error.\n\t\t\t\t}\n\t\t\t\tif (charidx == 0xfffe || pFT_Get_Char_Index(face, charidx))\t//ignore glyph 0 (undefined)\n\t\t\t\t\tif (pFT_Load_Char(face, charidx, FT_LOAD_RENDER|(((f->flags&FONT_MONO)&&qface->ft.activeheight==qface->ft.actualsize/*FIXME*/)?FT_LOAD_TARGET_MONO:FT_LOAD_TARGET_NORMAL)|FT_LOAD_COLOR) == 0)\n\t\t\t\t\t{\n\t\t\t\t\t\tFT_GlyphSlot slot;\n\t\t\t\t\t\tFT_Bitmap *bm;\n\n\t\t\t\t\t\tslot = face->glyph;\n\t\t\t\t\t\tbm = &slot->bitmap;\n\t\t\t\t\t\tif (qface->ft.activeheight!=qface->ft.actualsize)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t//I'm just going to assume full-height raster glyphs here. I'm sure I'll be proven wrong some time but w/e.\n\t\t\t\t\t\t\tint nw, nh;\n\t\t\t\t\t\t\tif (bm->rows)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tnh = f->charheight;\n\t\t\t\t\t\t\t\tnw = (bm->width*nh)/bm->rows;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\tnw = f->charheight, nh = 0;\n\t\t\t\t\t\t\tif (!nw || !nh)\n\t\t\t\t\t\t\t\tc = Font_LoadGlyphData(f, charidx, FT_PIXEL_MODE_GRAY, NULL, nw, nh, 0);\n\t\t\t\t\t\t\telse if (bm->pixel_mode == FT_PIXEL_MODE_BGRA)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tunsigned int *out = alloca(nw*nh*sizeof(*out));\n\t\t\t\t\t\t\t\tImage_ResampleTexture(PTI_BGRA8, (void*)bm->buffer, bm->width, bm->rows, out, nw, nh);\n\t\t\t\t\t\t\t\tc = Font_LoadGlyphData(f, charidx, bm->pixel_mode, out, nw, nh, nw*sizeof(*out));\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse if (bm->pixel_mode == FT_PIXEL_MODE_GRAY)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tunsigned char *out = alloca(nw*nh*sizeof(*out));\n\t\t\t\t\t\t\t\tImage_ResampleTexture(PTI_L8, (void*)bm->buffer, bm->width, bm->rows, out, nw, nh);\n\t\t\t\t\t\t\t\tc = Font_LoadGlyphData(f, charidx, bm->pixel_mode, out, nw, nh, nw*sizeof(*out));\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t/*else if (bm->pixel_mode == FT_PIXEL_MODE_MONO)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tunsigned char *out = alloca(nw*nh*sizeof(*out));\n\t\t\t\t\t\t\t\tImage_ResampleTexture(PTI_L1, (void*)bm->buffer, bm->width, bm->rows, out, nw, nh);\n\t\t\t\t\t\t\t\tc = Font_LoadGlyphData(f, charidx, bm->pixel_mode, out, nw, nh, nw*sizeof(*out));\n\t\t\t\t\t\t\t}*/\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\tc = NULL;\n\t\t\t\t\t\t\tif (c)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tc->advance = nw;\n\t\t\t\t\t\t\t\tc->left = 0;\n\t\t\t\t\t\t\t\tc->top = 0;\n\t\t\t\t\t\t\t\treturn c;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tc = Font_LoadGlyphData(f, charidx, bm->pixel_mode, bm->buffer, bm->width, bm->rows, bm->pitch);\n\n\t\t\t\t\t\t\tif (c)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tc->advance = slot->advance.x >> 6;\n\t\t\t\t\t\t\t\tc->left = slot->bitmap_left;\n\t\t\t\t\t\t\t\t/*if(1)\n\t\t\t\t\t\t\t\t\tc->top = f->truecharheight - slot->bitmap_top;\n\t\t\t\t\t\t\t\telse*/\n\t\t\t\t\t\t\t\t\tc->top = f->charheight*3/4 - slot->bitmap_top;\n\t\t\t\t\t\t\t\treturn c;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t}\n\t\t\telse\n#endif\n#ifdef HALFLIFEMODELS\n\t\t\tif (qface->halflife)\n\t\t\t{\n\t\t\t\tsize_t glyph = charidx;\n\t\t\t\tif (glyph > 0xe000)\n\t\t\t\t\tglyph -= 0xe000;\n\t\t\t\tif (glyph < 0x100)\n\t\t\t\t{\n\t\t\t\t\tint gw = qface->halflife->chartab[glyph].width;\n\t\t\t\t\tint gh = qface->halflife->fontheight1;\n\t\t\t\t\tqbyte *in = qface->halflife->data + 256 * qface->halflife->chartab[glyph].y + qface->halflife->chartab[glyph].x;\n\t\t\t\t\tqbyte *pal = qface->halflife->data + 256 * qface->halflife->imgheight+2;\n\t\t\t\t\tqbyte *out = alloca(gw*gh*4);\n\t\t\t\t\tint x, y;\n\t\t\t\t\tfor (y = 0; y < gh; y++, in += 256-gw)\n\t\t\t\t\t\tfor (x = 0; x < gw; x++, in++)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (*in==0xff)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tout[(x+y*gw)*4+0] = 0;\n\t\t\t\t\t\t\t\tout[(x+y*gw)*4+1] = 0;\n\t\t\t\t\t\t\t\tout[(x+y*gw)*4+2] = 0;\n\t\t\t\t\t\t\t\tout[(x+y*gw)*4+3] = 0;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tqbyte val;\n\t\t\t\t\t\t\t\tval = max(pal[*in*3+0], pal[*in*3+1]);\n\t\t\t\t\t\t\t\tval = max(val, pal[*in*3+2]);\n\t\t\t\t\t\t\t\tout[(x+y*gw)*4+0] =\n\t\t\t\t\t\t\t\tout[(x+y*gw)*4+1] =\n\t\t\t\t\t\t\t\tout[(x+y*gw)*4+2] = val;\n\t\t\t\t\t\t\t\tout[(x+y*gw)*4+3] = 0xff;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\tif (f->charheight != gh)\n\t\t\t\t\t{\n\t\t\t\t\t\tint ngw = (gw * f->charheight) / gh;\n\t\t\t\t\t\tint ngh = f->charheight;\n\t\t\t\t\t\tqbyte *out2 = alloca(ngw*ngh*4);\n\t\t\t\t\t\tif (ngw&&ngh)\n\t\t\t\t\t\t\tImage_ResampleTexture(PTI_RGBA8, out, gw, gh, out2, ngw, ngh);\n\t\t\t\t\t\tc = Font_LoadGlyphData(f, charidx, FT_PIXEL_MODE_RGBA, out2, ngw, ngh, ngw*4); \n\t\t\t\t\t\tgw = ngw;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\tc = Font_LoadGlyphData(f, charidx, FT_PIXEL_MODE_RGBA, out, gw, gh, gw*4); \n\t\t\t\t\tif (c)\n\t\t\t\t\t{\n\t\t\t\t\t\tc->flags = 0;\t//private glyph colours\n\t\t\t\t\t\tc->advance = gw;\n\t\t\t\t\t\tc->left = 0;\n\t\t\t\t\t\tc->top = 0;\n\t\t\t\t\t\treturn c;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n#endif\n\t\t\tif (qface->horiz.data)\n\t\t\t{\n\t\t\t\tc = Font_TryLoadGlyphRaster(f, qface, charidx);\n\t\t\t\tif (c)\n\t\t\t\t\treturn c;\n\t\t\t}\n\t\t}\n\t}\n\n\tif (charidx == '\\r')\n\t\treturn Font_CopyChar(f, charidx|0xe000, charidx);\n\n\treturn NULL;\n}\n\n//obtains a cached char, null if not cached\nstatic struct charcache_s *Font_GetChar(font_t *f, unsigned int codepoint)\n{\n\tCHARIDXTYPE charidx;\n\tstruct charcache_s *c;\n\tif (codepoint > FONT_MAXCHARS)\n\t\tcharidx = 0xfffd;\n\telse\n\t\tcharidx = codepoint;\n\tc = Font_GetCharIfLoaded(f, charidx);\n\tif (!c)\n\t{\n\t\tif (charidx >= TRACKERFIRST && charidx < TRACKERFIRST+TRACKERCOUNT)\n\t\t{\n\t\t\tstatic struct charcache_s tc;\n\t\t\ttc.texplane = TRACKERIMAGE;\n\t\t\tfontplanes.trackerimage = Font_GetTrackerImage(charidx-TRACKERFIRST);\n\t\t\tif (!fontplanes.trackerimage)\n\t\t\t\treturn Font_GetChar(f, '?');\n\t\t\ttc.advance = fontplanes.trackerimage->width * ((float)f->charheight / fontplanes.trackerimage->height);\n\t\t\treturn &tc;\n\t\t}\n\n\t\tif (charidx == 0x00a0)\t//nbsp\n\t\t{\n\t\t\tc = Font_GetCharIfLoaded(f, ' ');\n\t\t\tif (c)\n\t\t\t\treturn c;\n\t\t}\n\n\t\t//not cached, can't get.\n\t\tc = Font_TryLoadGlyph(f, charidx);\n\n\t\tif (!c && charidx >= 0x400 && charidx <= 0x45f)\n\t\t{\t//apparently there's a lot of russian players out there.\n\t\t\t//if we just replace all their chars with a '?', they're gonna get pissed.\n\t\t\t//so lets at least attempt to provide some default mapping that makes sense even if they don't have a full font.\n\t\t\t//koi8-u is a mapping useful with 7-bit email because the message is still vaugely readable in latin if the high bits get truncated.\n\t\t\t//not being a language specialist, I'm just going to use that mapping, with the high bit truncated to ascii (which mostly exists in the quake charset).\n\t\t\t//this exact table is from ezquake. because I'm too lazy to figure out the proper mapping. (beware of triglyphs)\n\t\t\tstatic char *wc2koi_table =\n\t\t\t\t\"?3??4?67??\" \"??\" \"??\" \">?\"\t//400\n\t\t\t\t\"abwgdevzijklmnop\"\t//410\n\t\t\t\t\"rstufhc~{}/yx|`q\"\t//420\n\t\t\t\t\"ABWGDEVZIJKLMNOP\"\t//430\n\t\t\t\t\"RSTUFHC^[]_YX\\\\@Q\"\t//440\n\t\t\t\t\"?#??$?&'??\" \"??\" \"??.?\";\t//450\n\t\t\tcharidx = wc2koi_table[charidx - 0x400];\n\t\t\tif (charidx != '?')\n\t\t\t{\n\t\t\t\tc = Font_GetCharIfLoaded(f, charidx);\n\t\t\t\tif (!c)\n\t\t\t\t\tc = Font_TryLoadGlyph(f, charidx);\n\t\t\t}\n\t\t}\n\n\t\tif (!c && charidx >= 0xA0 && charidx <= 0x17F)\n\t\t{\t//try to make sense of iso8859-1\n\t\t\t//(mostly for zerstorer's o-umlout...)\n\t\t\tstatic char *latin_table =\n\t\t\t\t\" ?c###|S?c?<??R?\"\t//A0\n\t\t\t\t\"??\"\"??\"\"'u?.,??\"\">??\"\"??\"\t//B0\n\t\t\t\t\"AAAAAAECEEEEIIII\"\t//C0\n\t\t\t\t\"DNOOOOO*OUUUUYYs\"\t//D0\n\t\t\t\t\"aaaaaaeceeeeiiii\"\t//E0\n\t\t\t\t\"onooooo/ouuuuyyy\"\t//F0\n\n\t\t\t\t\"AaAaAaCcCcCcCcDd\"\t//100\n\t\t\t\t\"DdEeEeEeEeEeGgGg\"\t//110\n\t\t\t\t\"GgGgHhHhIiIiIiIi\"\t//120\n\t\t\t\t\"IiIiJjKkkLlLlLlL\"\t//130\n\t\t\t\t\"lllNnNnNnnNnOoOo\"\t//140\n\t\t\t\t\"OoEeRrRrRrSsSsSs\"\t//150\n\t\t\t\t\"SsTtTtTtUuUuUuUu\"\t//160\n\t\t\t\t\"UuUuWwYyYZzZzZzf\";\t//170\n\t\t\tcharidx = latin_table[charidx - 0xA0];\n\t\t\tif (charidx != '?')\n\t\t\t{\n\t\t\t\tc = Font_GetCharIfLoaded(f, charidx);\n\t\t\t\tif (!c)\n\t\t\t\t\tc = Font_TryLoadGlyph(f, charidx);\n\t\t\t}\n\t\t}\n\n\t\tif (!c)\n\t\t\tc = Font_LoadPlaceholderGlyph(f, charidx);\n\t\tif (!c)\n\t\t{\n\t\t\tcharidx = 0xfffd;\t//unicode's replacement char\n\t\t\tc = Font_GetCharIfLoaded(f, charidx);\n\t\t\tif (!c)\n\t\t\t\tc = Font_TryLoadGlyph(f, charidx);\n\t\t}\n\t\tif (!c)\n\t\t{\n\t\t\tcharidx = '?';\t//meh\n\t\t\tc = Font_GetCharIfLoaded(f, charidx);\n\t\t\tif (!c)\n\t\t\t\tc = Font_TryLoadGlyph(f, charidx);\n\t\t}\n\t}\n\treturn c;\n}\n\nqboolean Font_LoadHorizontalFont(struct font_s *f, int fheight, const char *fontfilename)\n{\t//halflife-style.\n\tfontface_t *qface;\n\tvoid *rawdata;\n\tqofs_t rawsize;\n\tqbyte *rgbadata = NULL;\n\tint width=0,height=0;\n\tuploadfmt_t format;\n\n\tif (fheight < 1)\n\t\tfheight = 1;\n\n\t//ran out of font slots.\n\tif (f->faces == MAX_FACES)\n\t\treturn false;\n\n\tfor (qface = faces; qface; qface = qface->fnext)\n\t{\n\t\tif (!strcmp(qface->name, fontfilename) && qface->horiz.data)\n\t\t{\n\t\t\tqface->refs++;\n\t\t\tf->face[f->faces++] = qface;\n\t\t\treturn true;\n\t\t}\n\t}\n\n\trawdata = FS_MallocFile(fontfilename, FS_GAME, &rawsize);\n\tif (rawdata)\n\t\trgbadata = ReadRawImageFile(rawdata, rawsize, &width, &height, &format, true, fontfilename);\n\tFS_FreeFile(rawdata);\n\n\tif (rgbadata)\n\t{\n\t\t/*success!*/\n\t\tqface = Z_Malloc(sizeof(*qface));\n\t\tqface->flink = &faces;\n\t\tqface->fnext = *qface->flink;\n\t\t*qface->flink = qface;\n\t\tif (qface->fnext)\n\t\t\tqface->fnext->flink = &qface->fnext;\n\t\tqface->horiz.data = rgbadata;\n\t\tqface->horiz.stride = width;\n\t\tqface->horiz.charheight = height;\n\t\tqface->horiz.rows = 1;\n\t\tqface->horiz.paletted = false;\n\t\tqface->horiz.codepage = FMT_ISO88591;\n\t\tqface->refs++;\n\t\tQ_strncpyz(qface->name, fontfilename, sizeof(qface->name));\n\n\t\tf->face[f->faces++] = qface;\n\t\treturn true;\n\t}\n\treturn false;\n}\n\nqboolean Font_LoadKexFont(struct font_s *f, int fheight, const char *fontfilename)\n{\n\tvfsfile_t *kfont = FS_OpenVFS(va(\"fonts/%s.kfont\", fontfilename), \"rb\", FS_GAME);\n\tchar line[256];\n\tchar val[256], *s;\n\tstruct charcache_s *c;\n\tfontface_t *qface;\n\tif (!kfont)\n\t\treturn false;\n\twhile(VFS_GETS(kfont, line, sizeof(line)))\n\t{\n\t\ts = COM_ParseOut(line, val, sizeof(val));\n\t\tif (!s)\n\t\t\tcontinue;\n\t\tif (!strcmp(val, \"texture\") && (s=COM_ParseOut(s, val, sizeof(val))))\n\t\t{\n\t\t\tTEXDOWAIT(f->singletexture);\n\t\t\tif (!TEXLOADED(f->singletexture))\n\t\t\t{\t//noworker is needed because we need to know the size to make sense of char positions\n\t\t\t\t//exactextension is needed to work around quakeex fuckups\n\t\t\t\tf->singletexture = Image_GetTexture(val, NULL, IF_NOWORKER|IF_EXACTEXTENSION|IF_UIPIC|(r_font_linear.ival?IF_LINEAR:IF_NEAREST)|IF_NOPICMIP|IF_NOMIPMAP|IF_NOGAMMA|IF_NOPURGE, NULL, NULL, 0, 0, PTI_INVALID);\n\t\t\t}\n\t\t}\n\t\telse if (*val >= '0' && *val <='9')\n\t\t{\n\t\t\tunsigned int codepoint = atoi(val);\n\t\t\tunsigned int x, y, w, h;//, u;\n\t\t\tif (!TEXLOADED(f->singletexture))\n\t\t\t\tbreak;\n\n\t\t\ts=COM_ParseOut(s, val, sizeof(val));\n\t\t\tx = atoi(val);\n\t\t\ts=COM_ParseOut(s, val, sizeof(val));\n\t\t\ty = atoi(val);\n\t\t\ts=COM_ParseOut(s, val, sizeof(val));\n\t\t\tw = atoi(val);\n\t\t\ts=COM_ParseOut(s, val, sizeof(val));\n\t\t\th = atoi(val);\n\t\t\ts=COM_ParseOut(s, val, sizeof(val));\n\t\t\t//u = atoi(val);\n\t\t\tif (!s)\n\t\t\t\tcontinue;\t//something truncated.\n\t\t\tif (codepoint >= FONT_MAXCHARS || h<=0)\n\t\t\t\tcontinue;\t//out of range.\n\t\t\tc = Font_GetCharStore(f, codepoint);\n\t\t\tif (codepoint != ' ')\n\t\t\t{\n\t\t\t\ty+=2;\n\t\t\t\th-=4;\n\t\t\t}\n\t\t\tc->advance = max(1,(f->charheight * w)/h);\n\n\t\t\tc->bmw = (w * PLANEWIDTH) / f->singletexture->width;\n\t\t\tc->bmh = (h * PLANEHEIGHT) / f->singletexture->height;\n\t\t\tc->bmx = (x * PLANEWIDTH) / f->singletexture->width;\n\t\t\tc->bmy = (y * PLANEHEIGHT) / f->singletexture->height;\n\t\t\tc->left = 0;\n\t\t\tc->nextchar = 0;\t//these chars are not linked in\n\t\t\tc->texplane = BITMAPPLANE;\n\t\t}\n\t}\n\n\n\tVFS_CLOSE(kfont);\n\tif (!TEXLOADED(f->singletexture))\n\t\treturn false;\n\n\t/*success!*/\n\tqface = Z_Malloc(sizeof(*qface));\n\tqface->flink = &faces;\n\tqface->fnext = *qface->flink;\n\t*qface->flink = qface;\n\tif (qface->fnext)\n\t\tqface->fnext->flink = &qface->fnext;\n\tqface->refs++;\n\tQ_snprintfz(qface->name, sizeof(qface->name), \"fonts/%s.kfont\", fontfilename);\n\n\tf->face[f->faces++] = qface;\n\n\treturn true;\n}\n\n#ifdef AVAIL_FREETYPE\n#if defined(LIBFONTCONFIG_STATIC)\n#include <fontconfig/fontconfig.h>\n#endif\nextern cvar_t dpcompat_smallerfonts;\nint Font_ChangeFTSize(fontface_t *qface, int pixelheight)\n{\n\tFT_Face face = qface->ft.face;\n\tqface->ft.activeheight = pixelheight;\n#ifdef HAVE_LEGACY\n\tif (dpcompat_smallerfonts.ival)\n\t{\t//sizes specified include extra spacing in dp, giving extra padding somewhere.\n\t\tint s = pixelheight;\n\t\tfor(s = pixelheight; s; s--)\n\t\t{\n\t\t\tif (0==pFT_Set_Pixel_Sizes(face, 0, s))\n\t\t\t\tif (face->size->metrics.height>>6 <= pixelheight)\n\t\t\t\t\tbreak;\n\t\t}\n\n\t\tif (!s)\n\t\t{\n\t\t\tqface->ft.activeheight = 0; //something invalid\n\t\t\tqface->ft.actualsize = qface->ft.activeheight;\n\t\t\treturn 0;\n\t\t}\n\t\tqface->ft.actualsize = qface->ft.activeheight;\n\t\treturn s;\n\t}\n\telse\n#endif\n\t\tif (FT_HAS_FIXED_SIZES(face) && !FT_IS_SCALABLE(face))\n\t{\t//freetype doesn't like scaling these for us, so we have to pick a usable size ourselves.\n\t\tFT_Int best = 0, s;\n\t\tint bestheight = 0, h;\n\t\tfor (s = 0; s < qface->ft.face->num_fixed_sizes; s++)\n\t\t{\n\t\t\th = qface->ft.face->available_sizes[s].height;\n\t\t\t//always try to pick the smallest size that is also >= our target size\n\t\t\tif ((h > bestheight && bestheight < pixelheight) || (h >= pixelheight && h < bestheight))\n\t\t\t{\n\t\t\t\tbestheight = h;\n\t\t\t\tbest = s;\n\t\t\t}\n\t\t}\n\t\tqface->ft.actualsize = qface->ft.face->available_sizes[best].height;\n\t\tpFT_Select_Size(face, best);\n\t}\n\telse\n\t{\n\t\tpFT_Set_Pixel_Sizes(face, 0, pixelheight);\n\t\tqface->ft.actualsize = pixelheight;\n\t}\n\treturn pixelheight;\n}\nqboolean Font_LoadFreeTypeFont(struct font_s *f, int height, const char *fontfilename, const char *styles)\n{\n\tfontface_t *qface;\n\tFT_Face face = NULL;\n\tFT_Error error;\n\tflocation_t loc;\n\tvoid *fbase = NULL;\n\tif (!*fontfilename)\n\t\treturn false;\n\n\tif (height < 1)\n\t\theight = 1;\n\n\t//ran out of font slots.\n\tif (f->faces == MAX_FACES)\n\t\treturn false;\n\n\tfor (qface = faces; qface; qface = qface->fnext)\n\t{\n\t\tif (!strcmp(qface->name, fontfilename) && qface->ft.face)\n\t\t{\n\t\t\tqface->refs++;\n\t\t\tif (!f->faces)\n\t\t\t\tf->truecharheight = Font_ChangeFTSize(qface, f->charheight);\n\t\t\tf->face[f->faces++] = qface;\n\t\t\treturn true;\n\t\t}\n\t}\n\n\tif (!fontlib)\n\t{\n#ifndef FREETYPE_STATIC\n\t\tdllfunction_t ft2funcs[] =\n\t\t{\n\t\t\t{(void**)&pFT_Init_FreeType, \"FT_Init_FreeType\"},\n\t\t\t{(void**)&pFT_Load_Char, \"FT_Load_Char\"},\n\t\t\t{(void**)&pFT_Get_Char_Index, \"FT_Get_Char_Index\"},\n\t\t\t{(void**)&pFT_Set_Pixel_Sizes, \"FT_Set_Pixel_Sizes\"},\n\t\t\t{(void**)&pFT_Select_Size, \"FT_Select_Size\"},\n\t\t\t{(void**)&pFT_New_Face, \"FT_New_Face\"},\n\t\t\t{(void**)&pFT_New_Memory_Face, \"FT_New_Memory_Face\"},\n\t\t\t{(void**)&pFT_Init_FreeType, \"FT_Init_FreeType\"},\n\t\t\t{(void**)&pFT_Done_Face, \"FT_Done_Face\"},\n\t\t\t{(void**)&pFT_Error_String, \"FT_Error_String\"},\n\t\t\t{NULL, NULL}\n\t\t};\n\t\tif (triedtoloadfreetype)\n\t\t\treturn false;\n\t\ttriedtoloadfreetype = true;\n\n#ifdef _WIN32\n\t\tfontmodule = Sys_LoadLibrary(\"libfreetype-6\", ft2funcs);\n\t\tif (!fontmodule)\n\t\t\tfontmodule = Sys_LoadLibrary(\"freetype6\", ft2funcs);\n#else\n\t\tfontmodule = Sys_LoadLibrary(\"libfreetype.so.6\", ft2funcs);\n#endif\n\t\tif (!fontmodule)\n\t\t{\n\t\t\tCon_DPrintf(\"Couldn't load freetype library.\\n\");\n\t\t\treturn false;\n\t\t}\n#endif\n\t\terror = pFT_Init_FreeType(&fontlib);\n\t\tif (error)\n\t\t{\n\t\t\tCon_Printf(\"FT_Init_FreeType failed.\\n\");\n#ifndef FREETYPE_STATIC\n\t\t\tSys_CloseLibrary(fontmodule);\n#endif\n\t\t\treturn false;\n\t\t}\n\t\t/*any other errors leave freetype open*/\n\t}\n\n\terror = FT_Err_Cannot_Open_Resource;\n\tif (FS_FLocateFile(fontfilename, FSLF_IFFOUND|FSLF_QUIET, &loc) || FS_FLocateFile(va(\"%s.ttf\", fontfilename), FSLF_IFFOUND|FSLF_QUIET, &loc) || FS_FLocateFile(va(\"%s.otf\", fontfilename), FSLF_IFFOUND|FSLF_QUIET, &loc))\n\t{\n\t\tif (*loc.rawname && !loc.offset)\n\t\t{\n\t\t\tfbase = NULL;\n\t\t\t/*File is directly fopenable with no bias (not in a pk3/pak). Use the system-path form, so we don't have to eat the memory cost*/\n\t\t\terror = pFT_New_Face(fontlib, loc.rawname, 0, &face);\n\t\t}\n\t\telse\n\t\t{\n\t\t\t/*File is inside an archive, we need to read it and pass it as memory (and keep it available)*/\n\t\t\tvfsfile_t *f;\n\t\t\tf = FS_OpenReadLocation(loc.rawname, &loc);\n\t\t\tif (f && loc.len > 0)\n\t\t\t{\n\t\t\t\tfbase = BZ_Malloc(loc.len);\n\t\t\t\tVFS_READ(f, fbase, loc.len);\n\t\t\t\tVFS_CLOSE(f);\n\n\t\t\t\terror = pFT_New_Memory_Face(fontlib, fbase, loc.len, 0, &face);\n\t\t\t}\n\t\t}\n\t}\n\n#if defined(LIBFONTCONFIG_STATIC)\n\tif (error && !strchr(fontfilename, '/'))\n\t{\n\t\tFcConfig *config = FcInitLoadConfigAndFonts();\n\t\tFcResult res;\n\t\tFcPattern *font;\n\t\tFcPattern *pat;\n\n\t\t//FcNameParse takes something of the form: \"family:style1:style2\". we already swapped spaces for : in styles.\n\t\tif (styles)\n\t\t\tpat = FcNameParse((const FcChar8*)va(\"%s:%s\", fontfilename, styles));\n\t\telse\n\t\t\tpat = FcNameParse((const FcChar8*)fontfilename);\n\t\tFcConfigSubstitute(config, pat, FcMatchPattern);\n\t\tFcDefaultSubstitute(pat);\n\n\t\t// find the font\n\t\tfont = FcFontMatch(config, pat, &res);\n\t\tif (font)\n\t\t{\n\t\t\tFcChar8 *file = NULL;\n\t\t\tif (FcPatternGetString(font, FC_FILE, 0, &file) == FcResultMatch)\n\t\t\t\terror = pFT_New_Face(fontlib, file, 0, &face);\t//'file' should be a system path\n\t\t\tFcPatternDestroy(font);\n\t\t}\n\t\tFcPatternDestroy(pat);\n\t}\n#elif defined(_WIN32)\n\tif (error)\n\t{\n\t\tstatic qboolean firsttime = true;\n\t\tstatic char fontdir[MAX_OSPATH];\n\n\t\tif (firsttime)\n\t\t{\n\t\t\tHRESULT (WINAPI *dSHGetFolderPath) (HWND hwndOwner, int nFolder, HANDLE hToken, DWORD dwFlags, LPTSTR pszPath);\n\t\t\tdllfunction_t shfolderfuncs[] =\n\t\t\t{\n\t\t\t\t{(void**)&dSHGetFolderPath, \"SHGetFolderPathA\"},\n\t\t\t\t{NULL, NULL}\n\t\t\t};\n\t\t\tdllhandle_t *shfolder = Sys_LoadLibrary(\"shfolder.dll\", shfolderfuncs);\n\n\t\t\tfirsttime = false;\n\t\t\tif (shfolder)\n\t\t\t{\n\t\t\t\t// 0x14 == CSIDL_FONTS\n\t\t\t\tif (dSHGetFolderPath(NULL, 0x14, NULL, 0, fontdir) != S_OK)\n\t\t\t\t\t*fontdir = 0;\n\t\t\t\tSys_CloseLibrary(shfolder);\n\t\t\t}\n\t\t}\n\t\tif (*fontdir)\n\t\t{\n\t\t\terror = pFT_New_Face(fontlib, va(\"%s/%s\", fontdir, fontfilename), 0, &face);\n\t\t\tif (error)\n\t\t\t\terror = pFT_New_Face(fontlib, va(\"%s/%s.ttf\", fontdir, fontfilename), 0, &face);\n\t\t}\n\t}\n#else\n\tif (error)\n\t{\t//eg: /usr/share/fonts/truetype/droid/DroidSansFallbackFull.ttf\n\t\terror = pFT_New_Face(fontlib, va(\"/usr/share/fonts/%s\", fontfilename), 0, &face);\n\t\tif (error)\n\t\t\terror = pFT_New_Face(fontlib, va(\"/usr/share/fonts/truetype/%s.ttf\", fontfilename), 0, &face);\n\t\tif (error)\t//just to give a chance of the same names working on more than one os, with the right package installed.\n\t\t\terror = pFT_New_Face(fontlib, va(\"/usr/share/fonts/truetype/msttcorefonts/%s.ttf\", fontfilename), 0, &face);\n\t}\n#endif\n\tif (!error)\n\t{\n\t\tint trueheight;\n\t\t/*success!*/\n\t\tqface = Z_Malloc(sizeof(*qface));\n\t\tqface->ft.face = face;\n\t\tqface->ft.activeheight = qface->ft.actualsize = 0;//height;\n\t\tqface->ft.membuf = fbase;\n\t\tqface->refs++;\n\t\tQ_strncpyz(qface->name, fontfilename, sizeof(qface->name));\n\n\t\ttrueheight = Font_ChangeFTSize(qface, f->charheight);\n\t\tif (trueheight)\n\t\t{\t//okay, we can use it, link it in.\n\t\t\tqface->flink = &faces;\n\t\t\tqface->fnext = *qface->flink;\n\t\t\t*qface->flink = qface;\n\t\t\tif (qface->fnext)\n\t\t\t\tqface->fnext->flink = &qface->fnext;\n\n\t\t\tif (!f->faces)\n\t\t\t\tf->truecharheight = trueheight;\n\t\t\tf->face[f->faces++] = qface;\n\t\t\treturn true;\n\t\t}\n\t\tZ_Free(qface);\n\t}\n\tif (error && error != FT_Err_Cannot_Open_Resource)\n\t\tCon_Printf(\"Freetype(%s): error %i - %s\\n\", fontfilename, error, pFT_Error_String(error));\n\tif (fbase)\n\t\tBZ_Free(fbase);\n\n\treturn false;\n}\n#endif\n\nstatic texid_t Font_LoadReplacementConchars(void)\n{\n\ttexid_t tex;\n\t//q1 replacement\n\ttex = R_LoadHiResTexture(\"gfx/conchars.lmp\", NULL, (r_font_linear.ival?IF_LINEAR:IF_NEAREST)|IF_PREMULTIPLYALPHA|IF_LOADNOW|IF_UIPIC|IF_NOMIPMAP|IF_NOGAMMA|IF_NOPURGE);\n\tTEXDOWAIT(tex);\n\tif (TEXLOADED(tex))\n\t\treturn tex;\n\t//q2\n\ttex = R_LoadHiResTexture(\"pics/conchars.pcx\", NULL, (r_font_linear.ival?IF_LINEAR:IF_NEAREST)|IF_PREMULTIPLYALPHA|IF_LOADNOW|IF_UIPIC|IF_NOMIPMAP|IF_NOGAMMA|IF_NOPURGE);\n\tTEXDOWAIT(tex);\n\tif (TEXLOADED(tex))\n\t\treturn tex;\n\t//q3\n\ttex = R_LoadHiResTexture(\"gfx/2d/bigchars.tga\", NULL, (r_font_linear.ival?IF_LINEAR:IF_NEAREST)|IF_PREMULTIPLYALPHA|IF_LOADNOW|IF_UIPIC|IF_NOMIPMAP|IF_NOGAMMA|IF_NOPURGE);\n\tTEXDOWAIT(tex);\n\tif (TEXLOADED(tex))\n\t\treturn tex;\n\treturn r_nulltex;\n}\n\n#ifdef HEXEN2\nstatic texid_t Font_LoadHexen2Conchars(qboolean iso88591)\n{\n\t//gulp... so it's come to this has it? rework the hexen2 conchars into the q1 system.\n\ttexid_t tex;\n\tunsigned int i, x;\n\tunsigned char *tempchars;\n\tunsigned char *in, *out, *outbuf;\n\tFS_LoadFile(\"gfx/menu/conchars.lmp\", (void**)&tempchars);\n\n\t/*hexen2's conchars are arranged 32-wide, 16 high.\n\tthe upper 8 rows are 256 8859-1 chars\n\tthe lower 8 rows are a separate set of recoloured 8859-1 chars.\n\n\tif we're loading for the fallback then we're loading this data for quake compatibility, \n\tso we grab only the first 4 rows of each set of chars (128 low chars, 128 high chars).\n\n\tif we're loading a proper charset, then we load only the first set of chars, we can recolour the rest anyway (com_parseutf8 will do so anyway).\n\tas a final note, parsing iso8859-1 french/german/etc as utf8 will generally result in decoding errors which can gracefully revert to 8859-1 safely. If this premise fails too much, we can always change the parser for different charsets - the engine always uses unicode and thus 8859-1 internally.\n\t*/\n\tif (tempchars)\n\t{\n\t\toutbuf = BZ_Malloc(8*8*256*8);\n\n\t\tout = outbuf;\n\t\ti = 0;\n\t\t/*read the low chars*/\n\t\tfor (; i < 8*8*1; i+=1)\n\t\t{\n\t\t\tif (i&(1<<3))\n\t\t\t\tin = tempchars + (i>>3)*16*8*8+(i&7)*32*8 - 256*4+128;\n\t\t\telse\n\t\t\t\tin = tempchars + (i>>3)*16*8*8+(i&7)*32*8;\n\t\t\tfor (x = 0; x < 16*8; x++)\n\t\t\t\t*out++ = *in++;\n\t\t}\n\t\tif (iso88591)\n\t\t{\n\t\t\t/*read the non 8859-1 quake-compat control chars*/\n\t\t\tfor (; i < 8*8*1 + 16; i+=1)\n\t\t\t{\n\t\t\t\tif (i&(1<<3))\n\t\t\t\t\tin = tempchars+128*128 + ((i>>3)&7)*16*8*8+(i&7)*32*8 - 256*4+128;\n\t\t\t\telse\n\t\t\t\t\tin = tempchars+128*128 + ((i>>3)&7)*16*8*8+(i&7)*32*8;\n\t\t\t\tfor (x = 0; x < 16*8; x++)\n\t\t\t\t\t*out++ = *in++;\n\t\t\t}\n\n\t\t\t/*read the final low chars (final if 8859-1 anyway)*/\n\t\t\tfor (; i < 8*8*2; i+=1)\n\t\t\t{\n\t\t\t\tif (i&(1<<3))\n\t\t\t\t\tin = tempchars + (i>>3)*16*8*8+(i&7)*32*8 - 256*4+128;\n\t\t\t\telse\n\t\t\t\t\tin = tempchars + (i>>3)*16*8*8+(i&7)*32*8;\n\t\t\t\tfor (x = 0; x < 16*8; x++)\n\t\t\t\t\t*out++ = *in++;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\t/*read the high chars*/\n\t\t\tfor (; i < 8*8*2; i+=1)\n\t\t\t{\n\t\t\t\tif (i&(1<<3))\n\t\t\t\t\tin = tempchars+128*128 + ((i>>3)&15)*16*8*8+(i&7)*32*8 - 256*4+128;\n\t\t\t\telse\n\t\t\t\t\tin = tempchars+128*128 + ((i>>3)&15)*16*8*8+(i&7)*32*8;\n\t\t\t\tfor (x = 0; x < 16*8; x++)\n\t\t\t\t\t*out++ = *in++;\n\t\t\t}\n\t\t}\n\t\tFS_FreeFile(tempchars);\n\n\t\t// add ocrana leds\n\t\tif (!iso88591 && con_ocranaleds.value && con_ocranaleds.value != 2)\n\t\t\tAddOcranaLEDsIndexed (outbuf, 128, 128);\n\n\t\tfor (i=0 ; i<128*128 ; i++)\n\t\t\tif (outbuf[i] == 0)\n\t\t\t\toutbuf[i] = 255;\t// proper transparent color\n\t\ttex = R_LoadTexture8 (iso88591?\"gfx/menu/8859-1.lmp\":\"charset\", 128, 128, outbuf, IF_PREMULTIPLYALPHA|IF_LOADNOW|IF_UIPIC|IF_NOMIPMAP|IF_NOGAMMA, 1);\n\t\tZ_Free(outbuf);\n\t\treturn tex;\n\t}\n\treturn r_nulltex;\n}\n#endif\n\nFTE_ALIGN(4) qbyte default_conchar[/*11356*/] =\n{\n#include \"lhfont.h\"\n};\n\nstatic void Font_CopyGlyph(int src, int dst, void *data)\n{\n\tint glyphsize = 16;\n\tint y;\n\tint x;\n\tchar *srcptr = (char*)data + (src&15)*glyphsize*4 + (src>>4)*glyphsize*256*4;\n\tchar *dstptr = (char*)data + (dst&15)*glyphsize*4 + (dst>>4)*glyphsize*256*4;\n\tfor (y = 0; y < glyphsize; y++)\n\t{\n\t\tfor (x = 0; x < glyphsize; x++)\n\t\t{\n\t\t\tdstptr[x*4+0] = srcptr[x*4+0];\n\t\t\tdstptr[x*4+1] = srcptr[x*4+1];\n\t\t\tdstptr[x*4+2] = srcptr[x*4+2];\n\t\t\tdstptr[x*4+3] = srcptr[x*4+3];\n\t\t}\n\t\tdstptr += 256*4;\n\t\tsrcptr += 256*4;\n\t}\n}\nstatic texid_t Font_LoadFallbackConchars(void)\n{\n\ttexid_t tex;\n\tint width, height;\n\tunsigned int i;\n\tuploadfmt_t format;\n\tqbyte *lump = ReadRawImageFile(default_conchar, sizeof(default_conchar), &width, &height, &format, false, \"conchars\");\n\tif (!lump || (format != PTI_RGBX8 && format != PTI_RGBA8 && format != PTI_LLLX8))\n\t\treturn r_nulltex;\n\t/*convert greyscale to alpha*/\n\tfor (i = 0; i < width*height; i++)\n\t{\n\t\tlump[i*4+3] = lump[i*4];\n\t\tlump[i*4+0] = 255;\n\t\tlump[i*4+1] = 255;\n\t\tlump[i*4+2] = 255;\n\t}\n\tif (width == 256 && height == 256)\n\t{\t//make up some scroll-bar/download-progress-bar chars, so that webgl doesn't look so buggy with the initial pak file(s).\n\t\tFont_CopyGlyph('[', 128, lump);\n\t\tFont_CopyGlyph('-', 129, lump);\n\t\tFont_CopyGlyph(']', 130, lump);\n\t\tFont_CopyGlyph('|', 131, lump);\n\t\tFont_CopyGlyph('>', 13, lump);\n\t}\n\ttex = Image_GetTexture(\"charset\", NULL, IF_PREMULTIPLYALPHA|IF_LOADNOW|IF_UIPIC|IF_NOMIPMAP|IF_NOGAMMA|IF_NOPURGE, (void*)lump, NULL, width, height, PTI_RGBA8);\n\tBZ_Free(lump);\n\treturn tex;\n}\n\n/*loads a fallback image. not allowed to fail (use syserror if needed)*/\nstatic texid_t Font_LoadDefaultConchars(enum fontfmt_e *fmt)\n{\n\ttexid_t tex;\n\ttex = Font_LoadReplacementConchars();\n\tif (TEXLOADED(tex))\n\t\treturn tex;\n#ifdef HEXEN2\n\ttex = Font_LoadHexen2Conchars(true);\n\tif (tex && tex->status == TEX_LOADING)\n\t\tCOM_WorkerPartialSync(tex, &tex->status, TEX_LOADING);\n\tif (TEXLOADED(tex))\n\t{\n\t\t*fmt = FMT_ISO88591;\n\t\treturn tex;\n\t}\n#endif\n\ttex = Font_LoadFallbackConchars();\n\tif (tex && tex->status == TEX_LOADING)\n\t\tCOM_WorkerPartialSync(tex, &tex->status, TEX_LOADING);\n\tif (TEXLOADED(tex))\n\t{\n\t\t*fmt = FMT_QUAKE;\n\t\treturn tex;\n\t}\n\tSys_Error(\"Unable to load any conchars\\n\");\n}\n\ntypedef struct \n{ \n    short\t\twidth;\n    short\t\theight; \n    short\t\tleftoffset;\t// pixels to the left of origin \n    short\t\ttopoffset;\t// pixels below the origin \n    int\t\t\tcolumnofs[1];\n} doompatch_t;\ntypedef struct\n{\n    unsigned char\t\ttopdelta;\t// -1 is the last post in a column\n    unsigned char\t\tlength; \t// length data bytes follows\n} doomcolumn_t;\nvoid Doom_ExpandPatch(doompatch_t *p, unsigned char *b, int stride)\n{\n\tdoomcolumn_t *col;\n\tunsigned char *src, *dst;\n\tint x, y;\n\tfor (x = 0; x < p->width; x++)\n\t{\n\t\tcol = (doomcolumn_t *)((unsigned char *)p + p->columnofs[x]);\n\t\twhile(col->topdelta != 0xff)\n\t\t{\n\t\t\t//exploit protection\n\t\t\tif (col->length + col->topdelta > p->height)\n\t\t\t\tbreak;\n\n\t\t\tsrc = (unsigned char *)col + 2; /*why 3? why not, I suppose*/\n\t\t\tdst = b + stride*col->topdelta;\n\t\t\tfor (y = 0; y < col->length; y++)\n\t\t\t{\n\t\t\t\t*dst = *src++;\n\t\t\t\tdst += stride;\n\t\t\t}\n\t\t\tsrc++;\n\t\t\tcol = (doomcolumn_t *)((unsigned char*)col + col->length + 4);\n\t\t}\n\t\tb++;\n\t}\n}\n\nstatic qboolean Font_LoadFontLump(font_t *f, const char *facename)\n{\n\tsize_t lumpsize = 0;\n\tqbyte lumptype = 0;\n\tvoid *lumpdata;\n\n\tif (f->faces == MAX_FACES)\n\t\treturn false;\t//can't store it...\n\tlumpdata = W_GetLumpName(facename, &lumpsize, &lumptype);\n\tif (!lumpdata)\n\t\treturn false;\n\tif ((lumptype == TYP_MIPTEX && lumpsize==(8*8)*(16*16)) ||\t//proper format\n\t\t(lumptype == TYP_QPIC   && lumpsize==(8*8)*(16*16)+8))\t//fucked up buggy format used by some people\n\t{\n\t\tfontface_t *fa = Z_Malloc(sizeof(*fa));\n\t\tfa->horiz.data = lumpdata;\n\t\tfa->horiz.stride = 8*16;\n\t\tfa->horiz.charheight = 8;\n\t\tfa->horiz.codepage = FMT_QUAKE;\n\t\tfa->horiz.paletted = true;\n\t\tfa->horiz.rows = 16;\n\n\t\tif (con_ocranaleds.ival)\n\t\t{\n\t\t\tif (con_ocranaleds.ival != 2 || CalcHashInt(&hash_crc16, lumpdata, 128*128) == 798)\n\t\t\t\tAddOcranaLEDsIndexed (lumpdata, 128, 128);\n\t\t}\n\n\t\tfa->flink = &fa->fnext;\n\t\tfa->refs = 1;\n\t\tf->face[f->faces++] = fa;\n\t\treturn true;\n\t}\n#ifdef HALFLIFEMODELS\n\telse if (lumptype == TYP_HLFONT)\n\t{\n\t\tfontface_t *fa = Z_Malloc(sizeof(*fa));\n\t\tfa->halflife = lumpdata;\n\t\tfa->flink = &fa->fnext;\n\t\tfa->refs = 1;\n\t\tf->face[f->faces++] = fa;\n//\t\tf->charheight = fa->halflife->fontheight1;\t//force the font to a specific size.\n\t\treturn true;\n\t}\n#endif\n\treturn false;\t//doesn't match a valid known format\n}\n\n//creates a new font object from the given file, with each text row with the given height.\n//width is implicit and scales with height and choice of font.\nstruct font_s *Font_LoadFont(const char *fontfilename, float vheight, float scale, int outline, unsigned int flags)\n{\n\tstruct font_s *f;\n\tint i = 0;\n\tint defaultplane;\n\tchar *aname;\n\tchar *parms;\n\tint height = ((vheight * vid.rotpixelheight)/vid.height) + 0.5;\n\tchar facename[MAX_QPATH*12];\n#ifdef AVAIL_FREETYPE\n\tchar *styles = NULL;\n#endif\n\tstruct charcache_s *c;\n\tfloat aspect = 1;\n\tenum fontfmt_e fmt = FMT_AUTO;\n\tqboolean explicit;\n\n\tQ_strncpyz(facename, fontfilename, sizeof(facename));\n\n\taname = strstr(facename, \":\");\n\tif (aname)\n\t\t*aname++ = 0;\n\tparms = strstr(facename, \"?\");\n\tif (parms)\n\t\t*parms++ = 0;\n\n\tf = Z_Malloc(sizeof(*f));\n\tf->outline = outline;\n\tf->scale = scale;\n\tif (height < 1)\t//doesn't make sense. especially negatives...\n\t\theight = 1;\n\tif (height > 128)\n\t\theight = 128;\t//limit possible damage... we use alloca a bit so don't let the stack get abused too much.\n\tf->charheight = height;\n\tf->truecharheight = height;\n\tf->flags = flags;\n\tQ_strncpyz(f->name, fontfilename, sizeof(f->name));\n\n\tswitch(M_GameType())\n\t{\n\tcase MGT_QUAKE2:\n\t\tVectorSet(f->alttint, 0.44, 1.0, 0.2);\n\t\tbreak;\n\tdefault:\n\t\tVectorSet(f->alttint, 1.16, 0.54, 0.41);\n\t\tbreak;\n\t}\n\tVectorSet(f->tint, 1, 1, 1);\n\tfontfilename = facename;\n\n\tif (parms)\n\t{\n\t\twhile (*parms)\n\t\t{\n\t\t\tif (!strncmp(parms, \"col=\", 4))\n\t\t\t{\n\t\t\t\tchar *t = parms+4;\n\t\t\t\tf->tint[0] = strtod(t, &t);\n\t\t\t\tif (*t == ',') t++;\n\t\t\t\tif (*t == ' ') t++;\n\t\t\t\tf->tint[1] = strtod(t, &t);\n\t\t\t\tif (*t == ',') t++;\n\t\t\t\tif (*t == ' ') t++;\n\t\t\t\tf->tint[2] = strtod(t, &t);\n\t\t\t\tparms = t;\n\t\t\t}\n\t\t\tif (!strncmp(parms, \"fmt=\", 4))\n\t\t\t{\n\t\t\t\tchar *t = parms+4;\n\t\t\t\tfmt = 0;\n\t\t\t\tif (*t == 'q')\n\t\t\t\t\tfmt = FMT_QUAKE;\n\t\t\t\telse if (*t == 'l')\n\t\t\t\t\tfmt = FMT_ISO88591;\n\t\t\t\telse if (*t == 'w')\n\t\t\t\t\tfmt = FMT_WINDOWS1252;\n\t\t\t\telse if (*t == 'k')\n\t\t\t\t\tfmt = FMT_KOI8U;\n\t\t\t\telse if (*t == 'h')\n\t\t\t\t\tfmt = FMT_HORIZONTAL;\n\t\t\t}\n\t\t\tif (!strncmp(parms, \"aspect=\", 7))\n\t\t\t{\n\t\t\t\tchar *t = parms+7;\n\t\t\t\taspect = strtod(t, &t);\n\t\t\t\tparms = t;\n\t\t\t}\n#ifdef AVAIL_FREETYPE\n\t\t\tif (!strncmp(parms, \"style=\", 6))\n\t\t\t{\n\t\t\t\tchar *t = parms+6;\n\t\t\t\tstyles = t;\n\t\t\t\twhile (*t && *t != '&')\n\t\t\t\t{\n\t\t\t\t\tif (*t == ' ')\n\t\t\t\t\t\t*t = ':';\n\t\t\t\t\tt++;\n\t\t\t\t}\n\t\t\t\tparms = t;\n\t\t\t}\n#endif\n\n\t\t\twhile(*parms && *parms != '&')\n\t\t\t\tparms++;\n\t\t\tif (*parms == '&')\n\t\t\t{\n\t\t\t\t*parms++ = 0;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\t}\n\n\tif (vid.flags&VID_SRGBAWARE)\n\t{\n\t\tf->tint[0] = M_SRGBToLinear(f->tint[0], 1);\n\t\tf->tint[1] = M_SRGBToLinear(f->tint[1], 1);\n\t\tf->tint[2] = M_SRGBToLinear(f->tint[2], 1);\n\n\t\tf->alttint[0] = M_SRGBToLinear(f->alttint[0], 1);\n\t\tf->alttint[1] = M_SRGBToLinear(f->alttint[1], 1);\n\t\tf->alttint[2] = M_SRGBToLinear(f->alttint[2], 1);\n\t}\n\n#ifdef PACKAGE_DOOMWAD\n\tif (!*fontfilename)\n\t{\n\t\tunsigned char buf[PLANEWIDTH*PLANEHEIGHT];\n\t\tint i;\n\t\tint x=0,y=0,h=0;\n\t\tdoompatch_t *dp;\n\n\t\tmemset(buf, 0, sizeof(buf));\n\n\t\tfor (i = '!'; i <= '_'; i++)\n\t\t{\n\t\t\tdp = NULL;\n\t\t\tFS_LoadFile(va(\"wad/stcfn%.3d\", i), (void**)&dp);\n\t\t\tif (!dp)\n\t\t\t\tbreak;\n\n\t\t\t/*make sure it can fit*/\n\t\t\tif (x + dp->width > PLANEWIDTH)\n\t\t\t{\n\t\t\t\tx = 0;\n\t\t\t\ty += h;\n\t\t\t\th = 0;\n\t\t\t}\n\n\t\t\tc = Font_GetCharStore(f, i);\n\t\t\tc->advance = dp->width;\t/*this is how much line space the char takes*/\n\t\t\tc->left = -dp->leftoffset;\n\t\t\tc->top = -dp->topoffset;\n\t\t\tc->nextchar = 0;\n\t\t\tc->texplane = SINGLEPLANE;\n\n\t\t\tc->bmx = x;\n\t\t\tc->bmy = y;\n\t\t\tc->bmh = dp->height;\n\t\t\tc->bmw = dp->width;\n\n\t\t\tDoom_ExpandPatch(dp, &buf[y*PLANEWIDTH + x], PLANEWIDTH);\n\n\t\t\tx += dp->width;\n\t\t\tif (dp->height > h)\n\t\t\t{\n\t\t\t\th = dp->height;\n\t\t\t\tif (h > f->charheight)\n\t\t\t\t\tf->charheight = h;\n\t\t\t}\n\n\t\t\tFS_FreeFile(dp);\n\t\t}\n\n\t\t/*if all loaded okay, replicate the chars to the quake-compat range (both white+red chars)*/\n\t\tif (i == '_'+1)\n\t\t{\n\t\t\t//doom doesn't have many chars, so make sure the lower case chars exist.\n\t\t\tfor (i = 'a'; i <= 'z'; i++)\n\t\t\t\tFont_CopyChar(f, i-'a'+'A', i);\n\n\t\t\t//no space char either\n\t\t\tc = Font_GetCharStore(f, ' ');\n\t\t\tc->advance = 8;\n\n\t\t\tf->singletexture = R_LoadTexture8(\"doomfont\", PLANEWIDTH, PLANEHEIGHT, buf, 0, true);\n\t\t\tfor (i = 0xe000; i <= 0xe0ff; i++)\n\t\t\t\tFont_CopyChar(f, toupper(i&0x7f), i);\n\t\t\treturn f;\n\t\t}\n\t}\n#endif\n#ifdef HEXEN2\n\tif (!strcmp(fontfilename, \"gfx/tinyfont\"))\n\t{\n\t\tunsigned int *img;\n\t\tint x, y;\n\t\tsize_t lumpsize;\n\t\tqbyte lumptype;\n\t\tunsigned char *w = W_GetLumpName(fontfilename+4, &lumpsize, &lumptype);\n\t\tif (!w || lumpsize != 32*128 || lumptype != 'D')\n\t\t{\n\t\t\tZ_Free(f);\n\t\t\treturn NULL;\n\t\t}\n\t\timg = Z_Malloc(PLANEWIDTH*PLANEWIDTH*4);\n\t\tfor (y = 0; y < 32; y++)\n\t\t\tfor (x = 0; x < 128; x++)\n\t\t\t\timg[x + y*PLANEWIDTH] = w[x + y*128]?d_8to24rgbtable[w[x + y*128]]:0;\n\n\t\tf->singletexture = R_LoadTexture(\"tinyfont\",PLANEWIDTH,PLANEWIDTH,TF_RGBA32,img,IF_PREMULTIPLYALPHA|IF_UIPIC|IF_NOPICMIP|IF_NOMIPMAP|IF_NOPURGE);\n\t\tif (f->singletexture->status == TEX_LOADING)\n\t\t\tCOM_WorkerPartialSync(f->singletexture, &f->singletexture->status, TEX_LOADING);\n\t\tZ_Free(img);\n\n\t\tfor (i = 0x00; i <= 0xff; i++)\n\t\t{\n\t\t\tc = Font_GetCharStore(f, i);\n\t\t\tc->advance = (height*3)/4;\n\t\t\tc->left = 0;\n\t\t\tc->top = 0;\n\t\t\tc->nextchar = 0;\t//these chars are not linked in\n\t\t\tc->texplane = BITMAPPLANE;\t/*if its a 'raster' font, don't use the default chars, always use the raster images*/\n\n\n\t\t\tif (i >= 'a' && i <= 'z')\n\t\t\t{\n\t\t\t\tc->bmx = ((i - 64)&15)*8;\n\t\t\t\tc->bmy = ((i - 64)/16)*8;\n\t\t\t\tc->bmh = 8;\n\t\t\t\tc->bmw = 8;\n\t\t\t}\n\t\t\telse if (i >= 32 && i < 96)\n\t\t\t{\n\t\t\t\tc->bmx = ((i - 32)&15)*8;\n\t\t\t\tc->bmy = ((i - 32)/16)*8;\n\t\t\t\tc->bmh = 8;\n\t\t\t\tc->bmw = 8;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tc->bmh = 0;\n\t\t\t\tc->bmw = 0;\n\t\t\t\tc->bmx = 0;\n\t\t\t\tc->bmy = 0;\n\t\t\t}\n\n\t\t\tFont_CopyChar(f, i, i|0xe0ff);\n\t\t}\n\t\treturn f;\n\t}\n#endif\n\n\tif (aname)\n\t{\n\t\tif (!strncmp(aname, \"?col=\", 5))\n\t\t{\n\t\t\tchar *t = aname+5;\n\t\t\tf->alttint[0] = strtod(t, &t);\n\t\t\tif (*t == ',') t++;\n\t\t\tif (*t == ' ') t++;\n\t\t\tf->alttint[1] = strtod(t, &t);\n\t\t\tif (*t == ',') t++;\n\t\t\tif (*t == ' ') t++;\n\t\t\tf->alttint[2] = strtod(t, &t);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tf->alt = Font_LoadFont(aname, vheight, scale, outline, flags);\n\t\t\tif (f->alt)\n\t\t\t{\n\t\t\t\tVectorCopy(f->alt->tint, f->alttint);\n\t\t\t\tVectorCopy(f->alt->tint, f->alt->alttint);\n\t\t\t}\n\t\t}\n\t}\n\n\texplicit = false;\t//singletexture is some weird custom layout and not to be trusted.\n\t{\n\t\tconst char *start;\n\t\tqboolean success;\n\t\tstart = fontfilename;\n\t\tfor(;;)\n\t\t{\n\t\t\tchar *end = strchr(start, ',');\n\t\t\tif (end)\n\t\t\t\t*end = 0;\n\n\t\t\tif (fmt == FMT_AUTO && *start && Font_LoadKexFont(f, height, start))\n\t\t\t\tsuccess = explicit = true;\n\t\t\telse if (fmt == FMT_HORIZONTAL)\n\t\t\t\tsuccess = Font_LoadHorizontalFont(f, height, start);\n#ifdef AVAIL_FREETYPE\n\t\t\telse if (fmt == FMT_AUTO && Font_LoadFreeTypeFont(f, height, start, styles))\n\t\t\t\tsuccess = true;\n#endif\n\t\t\telse\n\t\t\t\tsuccess = false;\n\n\t\t\tif (!success && !TEXLOADED(f->singletexture) && *start)\n\t\t\t{\n\t\t\t\tconst char *ext = COM_GetFileExtension(start, NULL);\n\t\t\t\tif (!Q_strcasecmp(ext, \".ttf\") || !Q_strcasecmp(ext, \".otf\"))\n\t\t\t\t\t;\t//no, don't try loading it as an image-based font. just let it fail.\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tf->singletexture = R_LoadHiResTexture(start, \"fonts:charsets\", IF_PREMULTIPLYALPHA|(r_font_linear.ival?IF_LINEAR:IF_NEAREST)|IF_UIPIC|IF_NOPICMIP|IF_NOMIPMAP|IF_NOPURGE|IF_LOADNOW);\n\t\t\t\t\tif (f->singletexture->status == TEX_LOADING)\n\t\t\t\t\t\tCOM_WorkerPartialSync(f->singletexture, &f->singletexture->status, TEX_LOADING);\n\n\t\t\t\t\tif (!TEXLOADED(f->singletexture) && f->faces < MAX_FACES)\n\t\t\t\t\t\tFont_LoadFontLump(f, start);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (end)\n\t\t\t{\n\t\t\t\t*end = ',';\n\t\t\t\tstart = end+1;\n\t\t\t}\n\t\t\telse\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\tif (!f->faces && !TEXLOADED(f->singletexture) && r_font_linear.ival)\n\t\tFont_LoadFontLump(f, \"conchars\");\n\n\tdefaultplane = INVALIDPLANE;/*assume the bitmap plane - don't use the fallback as people don't think to use com_parseutf8*/\n\tif (!explicit && TEXLOADED(f->singletexture))\n\t\tdefaultplane = BITMAPPLANE;\n\telse if (TEXLOADED(fontplanes.defaultfont))\n\t\tdefaultplane = DEFAULTPLANE;\n\n\tif (defaultplane == INVALIDPLANE)\n\t{\n\t\tif (!TEXLOADED(fontplanes.defaultfont))\n\t\t{\n\t\t\tfontplanes.defaultfont = Font_LoadDefaultConchars(&fmt);\n\t\t}\n\n#ifdef HEXEN2\n\t\tif (!strcmp(fontfilename, \"gfx/hexen2\"))\n\t\t{\n\t\t\tf->singletexture = Font_LoadHexen2Conchars(false);\n\t\t\tdefaultplane = DEFAULTPLANE;\n\t\t}\n#endif\n\t\tif (!TEXLOADED(f->singletexture))\n\t\t\tf->singletexture = fontplanes.defaultfont;\n\n\t\tif (TEXLOADED(f->singletexture))\n\t\t\tdefaultplane = BITMAPPLANE;\n\t\telse if (TEXLOADED(fontplanes.defaultfont))\n\t\t\tdefaultplane = DEFAULTPLANE;\n\t}\n\n\tif (defaultplane != INVALIDPLANE)\n\t{\n\t\tif (fmt==FMT_AUTO)\n\t\t\tfmt=FMT_QUAKE;\n\t\tif (!f->faces)\n\t\t{\n\t\t\tstatic const unsigned short iso88591[] = {\n\t\t\t\t0x80,0x81,0x82,0x83, 0x84,0x85,0x86,0x87, 0x88,0x89,0x8a,0x8b, 0x8c,0x8d,0x8e,0x8f,\n\t\t\t\t0x90,0x91,0x92,0x93, 0x94,0x95,0x96,0x97, 0x98,0x99,0x9a,0x9b, 0x9c,0x9d,0x9e,0x9f};\n\t\t\tstatic const unsigned short win1252[] = {\n\t\t\t\t0x20ac,  0x81,0x201a,0x0192, 0x201e,0x2026,0x2020,0x2021, 0x02c6,0x2030,0x0160,0x2039, 0x0152,  0x8d,0x017d,  0x8f,\n\t\t\t\t  0x90,0x2018,0x2019,0x101c, 0x201d,0x2022,0x2013,0x2014, 0x02dc,0x2122,0x0161,0x203a, 0x0153,  0x9d,0x017e,0x0178};\n\t\t\tstatic const unsigned short koi8u[] = {\n\t\t\t\t0x2500,0x2502,0x250C,0x2510, 0x2514,0x2518,0x251C,0x2524, 0x252C,0x2534,0x253C,0x2580, 0x2584,0x2588,0x258C,0x2590,\n\t\t\t\t0x2591,0x2592,0x2593,0x2320, 0x25A0,0x2219,0x221A,0x2248, 0x2264,0x2265,0x00A0,0x2321, 0x00B0,0x00B2,0x00B7,0x00F7,\n\t\t\t\t0x2550,0x2551,0x2552,0x0451, 0x0454,0x2554,0x0456,0x0457, 0x2557,0x2558,0x2559,0x255A, 0x255B,0x0491,0x255D,0x255E,\n\t\t\t\t0x255F,0x2560,0x2561,0x0401, 0x0404,0x2563,0x0406,0x0407, 0x2566,0x2567,0x2568,0x2569, 0x256A,0x0490,0x256C,0x00A9,\n\t\t\t\t0x044E,0x0430,0x0431,0x0446, 0x0434,0x0435,0x0444,0x0433, 0x0445,0x0438,0x0439,0x043A, 0x043B,0x043C,0x043D,0x043E,\n\t\t\t\t0x043F,0x044F,0x0440,0x0441, 0x0442,0x0443,0x0436,0x0432, 0x044C,0x044B,0x0437,0x0448, 0x044D,0x0449,0x0447,0x044A,\n\t\t\t\t0x042E,0x0410,0x0411,0x0426, 0x0414,0x0415,0x0424,0x0413, 0x0425,0x0418,0x0419,0x041A, 0x041B,0x041C,0x041D,0x041E,\n\t\t\t\t0x041F,0x042F,0x0420,0x0421, 0x0422,0x0423,0x0416,0x0412, 0x042C,0x042B,0x0417,0x0428, 0x042D,0x0429,0x0427,0x042A};\n\t\t\tconst unsigned short *c1;\n\t\t\tunsigned int c1size;\n\n\t\t\tif (fmt == FMT_WINDOWS1252)\n\t\t\t{\t//some tools use these extra ones (latin-1 has no visible c1 entries)\n\t\t\t\tc1 = win1252;\n\t\t\t\tc1size = countof(win1252);\n\t\t\t}\n\t\t\telse if (fmt == FMT_KOI8U)\n\t\t\t{\t//lots of russians in the quake scene\n\t\t\t\tc1 = koi8u;\n\t\t\t\tc1size = countof(koi8u);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tc1 = iso88591;\n\t\t\t\tc1size = countof(iso88591);\n\t\t\t}\n\t\t\tc1size += 128;\n\n\t\t\t/*force it to load, even if there's nothing there*/\n\t\t\tfor (i = ((fmt==FMT_QUAKE)?32:0); i < ((fmt==FMT_QUAKE)?128:256); i++)\n\t\t\t{\n\t\t\t\tif (i >= 128 && i < c1size)\n\t\t\t\t\tc = Font_GetCharStore(f, c1[i-128]);\n\t\t\t\telse\n\t\t\t\t\tc = Font_GetCharStore(f, i);\n\n\t\t\t\tc->advance = f->charheight * aspect;\n\t\t\t\tc->bmh = PLANEWIDTH/16;\n\t\t\t\tc->bmw = PLANEWIDTH/16;\n\t\t\t\tc->bmx = (i&15)*(PLANEWIDTH/16);\n\t\t\t\tc->bmy = (i/16)*(PLANEWIDTH/16);\n\t\t\t\tc->left = 0;\n\t\t\t\tc->top = 0;\n\t\t\t\tc->nextchar = 0;\t//these chars are not linked in\n\t\t\t\tc->texplane = defaultplane;\n\t\t\t}\n\t\t}\n\n\t\tif (fmt == FMT_QUAKE)\n\t\t{\n\t\t\t/*pack the default chars into it*/\n\t\t\tfor (i = 0xe000; i <= 0xe0ff; i++)\n\t\t\t{\n\t\t\t\tc = Font_GetCharStore(f, i);\n\t\t\t\tc->advance = f->charheight * aspect;\n\t\t\t\tc->bmh = PLANEWIDTH/16;\n\t\t\t\tc->bmw = PLANEWIDTH/16;\n\t\t\t\tc->bmx = ((i&15))*(PLANEWIDTH/16);\n\t\t\t\tc->bmy = ((i&0xf0)/16)*(PLANEWIDTH/16);\n\t\t\t\tc->left = 0;\n\t\t\t\tc->top = 0;\n\t\t\t\tc->nextchar = 0;\t//these chars are not linked in\n\t\t\t\tc->texplane = defaultplane;\n\t\t\t}\n\t\t}\n\t}\n\treturn f;\n}\n\n//removes a font from memory.\nvoid Font_Free(struct font_s *f)\n{\n\tsize_t i;\n\tstruct charcache_s **link, *c, *valid;\n\n\tif (!f)\n\t\treturn;\n\n\t//kill the alt font first.\n\tif (f->alt)\n\t{\n\t\tFont_Free(f->alt);\n\t\tf->alt = NULL;\n\t}\n\tvalid = NULL;\n\t//walk all chars, unlinking any that appear to be within this font's char cache\n\tfor (link = &fontplanes.oldestchar; *link; )\n\t{\n\t\tc = *link;\n\t\tif (f->chars[c->block] && c >= f->chars[c->block] && c <= f->chars[c->block] + FONTBLOCKSIZE)\n\t\t{\n\t\t\tc = c->nextchar;\n\t\t\tif (!c)\n\t\t\t\tfontplanes.newestchar = valid;\n\t\t\t*link = c;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tvalid = c;\n\t\t\tlink = &c->nextchar;\n\t\t}\n\t}\n\n\twhile(f->faces --> 0)\n\t{\n\t\tfontface_t *qface = f->face[f->faces];\n\t\tqface->refs--;\n\t\tif (!qface->refs)\n\t\t{\n#ifdef AVAIL_FREETYPE\n\t\t\tif (qface->ft.face)\n\t\t\t\tpFT_Done_Face(qface->ft.face);\n\t\t\tif (qface->ft.membuf)\n\t\t\t\tBZ_Free(qface->ft.membuf);\n#endif\n\t\t\t*qface->flink = qface->fnext;\n\t\t\tif (qface->fnext)\n\t\t\t\tqface->fnext->flink = qface->flink;\n\t\t\tZ_Free(qface);\n\t\t}\n\t}\n\n\tfor (i = 0; i < FONTBLOCKS; i++)\n\t\tif (f->chars[i])\n\t\t\tZ_Free(f->chars[i]);\n\tZ_Free(f);\n}\n\n//maps a given virtual screen coord to a pixel coord, which matches the font's height/width values\nvoid Font_BeginString(struct font_s *font, float vx, float vy, int *px, int *py)\n{\n\tif (R2D_Flush && (R2D_Flush != Font_Flush || curfont != font || font_be_flags != r2d_be_flags))\n\t\tR2D_Flush();\n\tR2D_Flush = Font_Flush;\n\tfont_be_flags = r2d_be_flags;\n\tcurfont = font;\n\t*px = (vx*(int)vid.rotpixelwidth) / (float)vid.width;\n\t*py = (vy*(int)vid.rotpixelheight) / (float)vid.height;\n\n\tcurfont_scale[0] = curfont->charheight;\n\tcurfont_scale[1] = curfont->charheight;\n//\tcurfont_scaled = false;\n}\nvoid Font_Transform(float vx, float vy, int *px, int *py)\n{\n\tif (px)\n\t\t*px = (vx*(int)vid.rotpixelwidth) / (float)vid.width;\n\tif (py)\n\t\t*py = (vy*(int)vid.rotpixelheight) / (float)vid.height;\n}\nvoid Font_BeginScaledString(struct font_s *font, float vx, float vy, float szx, float szy, float *px, float *py)\n{\n\tif (R2D_Flush && (R2D_Flush != Font_Flush || curfont != font || font_be_flags != r2d_be_flags))\n\t\tR2D_Flush();\n\tR2D_Flush = Font_Flush;\n\tfont_be_flags = r2d_be_flags;\n\tcurfont = font;\n\t*px = (vx*(float)vid.rotpixelwidth) / (float)vid.width;\n\t*py = (vy*(float)vid.rotpixelheight) / (float)vid.height;\n\n\t//now that its in pixels, clamp it so the text is at least consistant with its position.\n\t//an individual char may end straddling a pixel boundary, but at least the pixels won't jiggle around as the text moves.\n\t*px = (int)*px;\n\t*py = (int)*py;\n\n/*\tif ((int)(szx * vid.rotpixelheight/vid.height) == curfont->charheight && (int)(szy * vid.rotpixelheight/vid.height) == curfont->charheight)\n\t\tcurfont_scaled = false;\n\telse\n\t\tcurfont_scaled = true;\n*/\n\tcurfont_scale[0] = (szx * (float)vid.rotpixelheight) / (curfont->charheight * (float)vid.height);\n\tcurfont_scale[1] = (szy * (float)vid.rotpixelheight) / (curfont->charheight * (float)vid.height);\n\tcurfont_scale[0] *= curfont->scale;\n\tcurfont_scale[1] *= curfont->scale;\n}\n\nvoid Font_EndString(struct font_s *font)\n{\n//\tFont_Flush();\n//\tcurfont = NULL;\n\n\tR2D_Flush = font_foremesh.numindexes?Font_Flush:NULL;\n}\n\n//obtains the font's row height (each row of chars should be drawn using this increment)\nint Font_CharHeight(void)\n{\n\treturn curfont->charheight;\n}\nint Font_CharPHeight(struct font_s *font)\n{\n\treturn font->charheight;\n}\nint Font_GetTrueHeight(struct font_s *font)\t//Char[P]Height lies for compat with DP.\n{\n\treturn font->truecharheight;\n}\nfloat Font_CharVHeight(struct font_s *font)\n{\n\treturn ((float)font->charheight * vid.height)/vid.rotpixelheight;\n}\n\n//obtains the font's row height (each row of chars should be drawn using this increment)\nfloat Font_CharScaleHeight(void)\n{\n\treturn curfont->charheight * curfont_scale[1];\n}\n\nint Font_TabWidth(int x)\n{\n\tint tabwidth = Font_CharWidth(CON_WHITEMASK, ' ');\n\tif (!tabwidth)\n\t\ttabwidth = curfont->charheight;\n\ttabwidth *= 8;\n\n\tx++;\n\tx = x + ((tabwidth - (x % tabwidth)) % tabwidth);\n\treturn x;\n}\n\n/*\nThis is where the character ends.\nNote: this function supports tabs - x must always be based off 0, with Font_LineDraw actually used to draw the line.\n*/\nint Font_CharEndCoord(struct font_s *font, int x, unsigned int charflags, unsigned int codepoint)\n{\n\tstruct charcache_s *c;\n\n\tif (charflags&CON_HIDDEN)\n\t\treturn x;\n\tif (codepoint == '\\t')\n\t\treturn Font_TabWidth(x);\n\n\tif ((charflags & CON_2NDCHARSETTEXT) && font->alt)\n\t\tfont = font->alt;\n\n\tc = Font_GetChar(font, codepoint);\n\tif (!c)\n\t{\n\t\treturn x+0;\n\t}\n\n\treturn x+c->advance;\n}\n\n//obtains the width of a character from a given font. This is how wide it is. The next char should be drawn at x + result.\n//FIXME: this function cannot cope with tab and should not be used.\nint Font_CharWidth(unsigned int charflags, unsigned int codepoint)\n{\n\tstruct charcache_s *c;\n\tstruct font_s *font = curfont;\n\n\tif (charflags&CON_HIDDEN)\n\t\treturn 0;\n\n\tif ((charflags & CON_2NDCHARSETTEXT) && font->alt)\n\t\tfont = font->alt;\n\n\tc = Font_GetChar(curfont, codepoint);\n\tif (!c)\n\t{\n\t\treturn 0;\n\t}\n\n\treturn c->advance;\n}\n\n//obtains the width of a character from a given font. This is how wide it is. The next char should be drawn at x + result.\n//FIXME: this function cannot cope with tab and should not be used.\nfloat Font_CharScaleWidth(unsigned int charflags, unsigned int codepoint)\n{\n\tstruct charcache_s *c;\n\tstruct font_s *font = curfont;\n\n\tif (charflags&CON_HIDDEN)\n\t\treturn 0;\n\tif ((charflags & CON_2NDCHARSETTEXT) && font->alt)\n\t\tfont = font->alt;\n\n\tc = Font_GetChar(curfont, codepoint);\n\tif (!c)\n\t{\n\t\treturn 0;\n\t}\n\n\treturn c->advance * curfont_scale[0];\n}\n\nconchar_t *Font_DecodeReverse(conchar_t *start, conchar_t *stop, unsigned int *codeflags, unsigned int *codepoint)\n{\n\tif (start <= stop)\n\t{\n\t\t*codeflags = 0;\n\t\t*codepoint = 0;\n\t\treturn stop;\n\t}\n\n\tstart--;\n\tif (start > stop && start[-1] & CON_LONGCHAR)\n\t\tif (!(start[-1] & CON_RICHFORECOLOUR))\n\t\t{\n\t\t\tstart--;\n\t\t\t*codeflags = start[1];\n\t\t\t*codepoint = ((start[0] & CON_CHARMASK)<<16) | (start[1] & CON_CHARMASK);\n\t\t\treturn start;\n\t\t}\n\t*codeflags = start[0];\n\t*codepoint = start[0] & CON_CHARMASK;\n\treturn start;\n}\n\n//for a given font, calculate the line breaks and word wrapping for a block of text\n//start+end are the input string\n//starts+ends are an array of line start and end points, which have maxlines elements.\n//(end is the terminator, null or otherwise)\n//maxpixelwidth is the width of the display area in pixels\nint Font_LineBreaks(conchar_t *start, conchar_t *end, int maxpixelwidth, int maxlines, conchar_t **starts, conchar_t **ends)\n{\n\tconchar_t *l, *bt, *n;\n\tint px;\n\tint foundlines = 0;\n\tstruct font_s *font = curfont;\n\tunsigned int codeflags, codepoint;\n\n\twhile (start < end)\n\t{\n\t// scan the width of the line\n\t\tfor (px=0, l=start ; px <= maxpixelwidth; )\n\t\t{\n\t\t\tif (l >= end)\n\t\t\t\tbreak;\n\t\t\tn = Font_Decode(l, &codeflags, &codepoint);\n\t\t\tif (!(codeflags & CON_HIDDEN) && (codepoint == '\\n' || codepoint == '\\v'))\n\t\t\t\tbreak;\n\t\t\tpx = Font_CharEndCoord(font, px, codeflags, codepoint);\n\t\t\tl = n;\n\t\t}\n\t\t//if we did get to the end\n\t\tif (px > maxpixelwidth)\n\t\t{\n\t\t\tbt = l;\n\t\t\t//backtrack until we find a space\n\t\t\tfor(;;)\n\t\t\t{\n\t\t\t\tn = Font_DecodeReverse(l, start, &codeflags, &codepoint);\n\t\t\t\tif (codepoint > ' ')\n\t\t\t\t\tl = n;\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tl = n;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (l == start && bt>start)\n\t\t\t\tl = Font_DecodeReverse(bt, start, &codeflags, &codepoint);\n\t\t}\n\n\t\tstarts[foundlines] = start;\n\t\tends[foundlines] = l;\n\t\tfoundlines++;\n\t\tif (foundlines == maxlines)\n\t\t\tbreak;\n\n\t\tfor (;;)\n\t\t{\n\t\t\tif (l >= end)\n\t\t\t\tbreak;\n\t\t\tn = Font_Decode(l, &codeflags, &codepoint);\n\t\t\tif (!(codeflags & CON_HIDDEN) && (codepoint != ' '))\n\t\t\t\tbreak;\n\t\t\tl = n;\n\t\t}\n\n\t\tstart=l;\n\t\tif (start == end)\n\t\t\tbreak;\n\n\t\tif ((*start&(CON_CHARMASK|CON_HIDDEN)) == '\\n' || (*start&(CON_CHARMASK|CON_HIDDEN)) == '\\v')\n\t\t\tstart++;                // skip the \\n\n\t}\n\n\treturn foundlines;\n}\n\nint Font_LineWidth(conchar_t *start, conchar_t *end)\n{\n\t//fixme: does this do the right thing with tabs?\n\tint x = 0;\n\tstruct font_s *font = curfont;\n\tunsigned int codeflags, codepoint;\n\tfor (; start < end; )\n\t{\n\t\tstart = Font_Decode(start, &codeflags, &codepoint);\n\t\tx = Font_CharEndCoord(font, x, codeflags, codepoint);\n\t}\n\treturn x;\n}\nfloat Font_LineScaleWidth(conchar_t *start, conchar_t *end)\n{\n\tint x = 0;\n\tstruct font_s *font = curfont;\n\tunsigned int codeflags, codepoint;\n\twhile(start < end)\n\t{\n\t\tstart = Font_Decode(start, &codeflags, &codepoint);\n\t\tx = Font_CharEndCoord(font, x, codeflags, codepoint);\n\t}\n\treturn x * curfont_scale[0];\n}\nvoid Font_LineDraw(int x, int y, conchar_t *start, conchar_t *end)\n{\n\tint lx = 0;\n\tstruct font_s *font = curfont;\n\tunsigned int codeflags, codepoint;\n\tfor (; start < end; )\n\t{\n\t\tstart = Font_Decode(start, &codeflags, &codepoint);\n\t\tFont_DrawChar(x+lx, y, codeflags, codepoint);\n\t\tlx = Font_CharEndCoord(font, lx, codeflags, codepoint);\n\t}\n}\n\nconchar_t *Font_CharAt(int x, conchar_t *start, conchar_t *end)\n{\n\tint lx = 0, nx;\n\tstruct font_s *font = curfont;\n\tunsigned int codeflags, codepoint;\n\tconchar_t *nc;\n\tfor (; start < end; lx = nx, start = nc)\n\t{\n\t\tnc = Font_Decode(start, &codeflags, &codepoint);\n\t\tnx = Font_CharEndCoord(font, lx, codeflags, codepoint);\n\t\tif (x >= lx && x < nx)\n\t\t\treturn start;\n\t}\n\treturn NULL;\n}\n\n/*Note: *all* strings after the current one will inherit the same colour, until one changes it explicitly\ncorrect usage of this function thus requires calling this with 1111 before Font_EndString*/\nvoid Font_InvalidateColour(vec4_t newcolour)\n{\n\tif (font_foretint[0] == newcolour[0] && font_foretint[1] == newcolour[1] && font_foretint[2] == newcolour[2] && font_foretint[3] == newcolour[3])\n\t\treturn;\n\n\tif ((font_colourmask & CON_NONCLEARBG) && font_foremesh.numindexes)\n\t{\n\t\tif (R2D_Flush)\n\t\t\tR2D_Flush();\n\t\tR2D_Flush = Font_Flush;\n\t}\n\tfont_colourmask = CON_WHITEMASK;\n\n\tVectorScale(newcolour, newcolour[3], font_foretint);\n\tfont_foretint[3] = newcolour[3];\n\tVector4Scale(font_foretint, 255, font_forecolour.rgba);\n\n\tfont_backcolour.rgba[3] = 0;\n\n\t/*Any drawchars that are now drawn will get the forced colour*/\n}\n\n//draw a character from the current font at a pixel location.\nint Font_DrawChar(int px, int py, unsigned int charflags, unsigned int codepoint)\n{\n\tstruct charcache_s *c;\n\tfloat s0, s1;\n\tfloat t0, t1;\n\tfloat nextx;\n\tfloat sx, sy, sw, sh;\n\tint col;\n\tint v;\n\tstruct font_s *font = curfont;\n#ifdef D3D11QUAKE\n\tfloat dxbias = 0;//(qrenderer == QR_DIRECT3D11)?0.5:0;\n#else\n#define dxbias 0\n#endif\n\tif (charflags & CON_HIDDEN)\n\t\treturn px;\n\n\tif (charflags & CON_2NDCHARSETTEXT)\n\t{\n\t\tif (font->alt)\n\t\t{\n\t\t\tfont = font->alt;\n//\t\t\tcharflags &= ~CON_2NDCHARSETTEXT;\n\t\t}\n\t\telse if ((codepoint) >= 0xe000 && (codepoint) <= 0xe0ff)\n\t\t\tcharflags &= ~CON_2NDCHARSETTEXT;\t//don't double-dip\n\t}\n\n\t//crash if there is no current font.\n\tc = Font_GetChar(font, codepoint);\n\tif (!c)\n\t\treturn px;\n\n\tnextx = px + c->advance;\n\n\tif (codepoint == '\\t')\n\t\treturn Font_TabWidth(px);\n\tif (codepoint == ' ' && (charflags & (CON_RICHFORECOLOUR|CON_NONCLEARBG)) != CON_NONCLEARBG)\n\t\treturn nextx;\n\n/*\tif (charcode & CON_BLINKTEXT)\n\t{\n\t\tif (!cl_noblink.ival)\n\t\t\tif ((int)(realtime*3) & 1)\n\t\t\t\treturn nextx;\n\t}\n*/\n\tif (charflags & CON_RICHFORECOLOUR)\n\t{\n\t\tcol = charflags & (CON_2NDCHARSETTEXT|CON_BLINKTEXT|CON_RICHFORECOLOUR|(0xfff<<CON_RICHBSHIFT));\n\t\tif (col != font_colourmask)\n\t\t{\n\t\t\tvec4_t rgba;\n\t\t\tif (font_colourmask & CON_NONCLEARBG)\n\t\t\t{\n\t\t\t\tFont_Flush();\n\t\t\t\tR2D_Flush = Font_Flush;\n\t\t\t}\n\t\t\tfont_colourmask = col;\n\n\t\t\trgba[0] = ((col>>CON_RICHRSHIFT)&0xf)*0x11;\n\t\t\trgba[1] = ((col>>CON_RICHGSHIFT)&0xf)*0x11;\n\t\t\trgba[2] = ((col>>CON_RICHBSHIFT)&0xf)*0x11;\n\t\t\trgba[3] = 255;\n\n\t\t\tfont_backcolour.c = 0;\n\t\t\tif (charflags & CON_2NDCHARSETTEXT)\n\t\t\t{\n\t\t\t\trgba[0] *= font->alttint[0];\n\t\t\t\trgba[1] *= font->alttint[1];\n\t\t\t\trgba[2] *= font->alttint[2];\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\trgba[0] *= font->tint[0];\n\t\t\t\trgba[1] *= font->tint[1];\n\t\t\t\trgba[2] *= font->tint[2];\n\t\t\t}\n\t\t\trgba[0] *= font_foretint[0];\n\t\t\trgba[1] *= font_foretint[1];\n\t\t\trgba[2] *= font_foretint[2];\n\t\t\trgba[3] *= font_foretint[3];\n\t\t\tif (charflags & CON_BLINKTEXT)\n\t\t\t{\n\t\t\t\tfloat a = (sin(realtime*3)+1)*0.4 + 0.2;\n\t\t\t\tVector4Scale(rgba, a, rgba);\n\t\t\t}\n\t\t\tfont_forecolour.rgba[0] = min(rgba[0], 255);\n\t\t\tfont_forecolour.rgba[1] = min(rgba[1], 255);\n\t\t\tfont_forecolour.rgba[2] = min(rgba[2], 255);\n\t\t\tfont_forecolour.rgba[3] = min(rgba[3], 255);\n\t\t}\n\t}\n\telse\n\t{\n\t\tcol = charflags & (CON_2NDCHARSETTEXT|CON_NONCLEARBG|CON_BGMASK|CON_FGMASK|CON_HALFALPHA|CON_BLINKTEXT);\n\t\tif (col != font_colourmask)\n\t\t{\n\t\t\tvec4_t rgba;\n\t\t\tif ((col ^ font_colourmask) & CON_NONCLEARBG)\n\t\t\t{\n\t\t\t\tFont_Flush();\n\t\t\t\tR2D_Flush = Font_Flush;\n\t\t\t}\n\t\t\tfont_colourmask = col;\n\n\t\t\tcol = (charflags&CON_FGMASK)>>CON_FGSHIFT;\n\t\t\tif(charflags & CON_HALFALPHA)\n\t\t\t{\n\t\t\t\trgba[0] = consolecolours[col].fr*0x7f;\n\t\t\t\trgba[1] = consolecolours[col].fg*0x7f;\n\t\t\t\trgba[2] = consolecolours[col].fb*0x7f;\n\t\t\t\trgba[3] = 0x7f;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\trgba[0] = consolecolours[col].fr*255;\n\t\t\t\trgba[1] = consolecolours[col].fg*255;\n\t\t\t\trgba[2] = consolecolours[col].fb*255;\n\t\t\t\trgba[3] = 255;\n\t\t\t}\n\n\t\t\tif (vid.flags&VID_SRGBAWARE)\n\t\t\t{\n\t\t\t\trgba[0] = M_SRGBToLinear(rgba[0], 255);\n\t\t\t\trgba[1] = M_SRGBToLinear(rgba[1], 255);\n\t\t\t\trgba[2] = M_SRGBToLinear(rgba[2], 255);\n\t\t\t}\n\n\t\t\tcol = (charflags&CON_BGMASK)>>CON_BGSHIFT;\n\t\t\tif (charflags & CON_NONCLEARBG)\n\t\t\t{\n\t\t\t\tfont_backcolour.rgba[0] = consolecolours[col].fr*255;\n\t\t\t\tfont_backcolour.rgba[1] = consolecolours[col].fg*255;\n\t\t\t\tfont_backcolour.rgba[2] = consolecolours[col].fb*255;\n\t\t\t\tfont_backcolour.rgba[3] = (charflags & CON_NONCLEARBG)?0xc0:0;\n\t\t\t}\n\t\t\telse\n\t\t\t\tfont_backcolour.c = 0;\n\n\t\t\tif (charflags & CON_2NDCHARSETTEXT)\n\t\t\t{\n\t\t\t\trgba[0] *= font->alttint[0];\n\t\t\t\trgba[1] *= font->alttint[1];\n\t\t\t\trgba[2] *= font->alttint[2];\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\trgba[0] *= font->tint[0];\n\t\t\t\trgba[1] *= font->tint[1];\n\t\t\t\trgba[2] *= font->tint[2];\n\t\t\t}\n\t\t\trgba[0] *= font_foretint[0];\n\t\t\trgba[1] *= font_foretint[1];\n\t\t\trgba[2] *= font_foretint[2];\n\t\t\trgba[3] *= font_foretint[3];\n\t\t\tif (charflags & CON_BLINKTEXT)\n\t\t\t{\n\t\t\t\tfloat a = (sin(realtime*3)+1)*0.4 + 0.2;\n\t\t\t\tVector4Scale(rgba, a, rgba);\n\t\t\t}\n\t\t\tfont_forecolour.rgba[0] = min(rgba[0], 255);\n\t\t\tfont_forecolour.rgba[1] = min(rgba[1], 255);\n\t\t\tfont_forecolour.rgba[2] = min(rgba[2], 255);\n\t\t\tfont_forecolour.rgba[3] = min(rgba[3], 255);\n\t\t}\n\t}\n\n\ts0 = (float)c->bmx/PLANEWIDTH;\n\tt0 = (float)c->bmy/PLANEWIDTH;\n\ts1 = (float)(c->bmx+c->bmw)/PLANEWIDTH;\n\tt1 = (float)(c->bmy+c->bmh)/PLANEWIDTH;\n\n\tswitch(c->texplane)\n\t{\n\tcase TRACKERIMAGE:\n\t\ts0 = t0 = 0;\n\t\ts1 = t1 = 1;\n\t\tsx = ((px+c->left + dxbias)*(int)vid.width) / (float)vid.rotpixelwidth;\n\t\tsy = ((py+c->top + dxbias)*(int)vid.height) / (float)vid.rotpixelheight;\n\t\tsw = (c->advance*vid.width) / (float)vid.rotpixelwidth;\n\t\tsh = (font->charheight*vid.height) / (float)vid.rotpixelheight;\n\t\tv = Font_BeginChar(fontplanes.trackerimage);\n\t\tbreak;\n\tcase DEFAULTPLANE:\n\t\tsx = ((px+c->left + dxbias)*(int)vid.width) / (float)vid.rotpixelwidth;\n\t\tsy = ((py+c->top + dxbias)*(int)vid.height) / (float)vid.rotpixelheight;\n\t\tsw = ((c->advance)*vid.width) / (float)vid.rotpixelwidth;\n\t\tsh = ((font->charheight)*vid.height) / (float)vid.rotpixelheight;\n\t\tv = Font_BeginChar(fontplanes.defaultfont);\n\t\tbreak;\n\tcase BITMAPPLANE:\n\t\tsx = ((px+c->left + dxbias)*(int)vid.width) / (float)vid.rotpixelwidth;\n\t\tsy = ((py+c->top + dxbias)*(int)vid.height) / (float)vid.rotpixelheight;\n\t\tsw = ((c->advance)*vid.width) / (float)vid.rotpixelwidth;\n\t\tsh = ((font->charheight)*vid.height) / (float)vid.rotpixelheight;\n\t\tv = Font_BeginChar(font->singletexture);\n\t\tbreak;\n\tcase SINGLEPLANE:\n\t\tsx = ((px+c->left + dxbias)*(int)vid.width) / (float)vid.rotpixelwidth;\n\t\tsy = ((py+c->top + dxbias)*(int)vid.height) / (float)vid.rotpixelheight;\n\t\tsw = ((c->bmw)*vid.width) / (float)vid.rotpixelwidth;\n\t\tsh = ((c->bmh)*vid.height) / (float)vid.rotpixelheight;\n\t\tv = Font_BeginChar(font->singletexture);\n\t\tbreak;\n\tdefault:\n\t\tsx = ((px+c->left + dxbias)*(int)vid.width) / (float)vid.rotpixelwidth;\n\t\tsy = ((py+c->top + dxbias)*(int)vid.height) / (float)vid.rotpixelheight;\n\t\tsw = ((c->bmw)*vid.width) / (float)vid.rotpixelwidth;\n\t\tsh = ((c->bmh)*vid.height) / (float)vid.rotpixelheight;\n\t\tv = Font_BeginChar(fontplanes.texnum[c->texplane]);\n\t\tbreak;\n\t}\n\n\tfont_texcoord[v+0][0] = s0;\n\tfont_texcoord[v+0][1] = t0;\n\tfont_texcoord[v+1][0] = s1;\n\tfont_texcoord[v+1][1] = t0;\n\tfont_texcoord[v+2][0] = s1;\n\tfont_texcoord[v+2][1] = t1;\n\tfont_texcoord[v+3][0] = s0;\n\tfont_texcoord[v+3][1] = t1;\n\n\tfont_coord[v+0][0] = sx;\n\tfont_coord[v+0][1] = sy;\n\tfont_coord[v+1][0] = sx+sw;\n\tfont_coord[v+1][1] = sy;\n\tfont_coord[v+2][0] = sx+sw;\n\tfont_coord[v+2][1] = sy+sh;\n\tfont_coord[v+3][0] = sx;\n\tfont_coord[v+3][1] = sy+sh;\n\n\tif (c->flags&CHARF_FORCEWHITE)\n\t{\n\t\tfont_forecoloura[v+0].c =\n\t\tfont_forecoloura[v+1].c =\n\t\tfont_forecoloura[v+2].c =\n\t\tfont_forecoloura[v+3].c = 0xffffffff;\n\t}\n\telse\n\t{\n\t\tfont_forecoloura[v+0] =\n\t\tfont_forecoloura[v+1] =\n\t\tfont_forecoloura[v+2] =\n\t\tfont_forecoloura[v+3] = font_forecolour;\n\t}\n\n\tif (font_colourmask & CON_NONCLEARBG)\n\t{\n\t\tsx = ((px+dxbias)*(int)vid.width) / (float)vid.rotpixelwidth;\n\t\tsy = ((py+dxbias)*(int)vid.height) / (float)vid.rotpixelheight;\n\t\tsw = sx + ((c->advance)*vid.width) / (float)vid.rotpixelwidth;\n\t\tsh = sy + ((font->charheight)*vid.height) / (float)vid.rotpixelheight;\n\n\t\t//don't care about texcoords\n\t\tfont_backcoord[v+0][0] = sx;\n\t\tfont_backcoord[v+0][1] = sy;\n\t\tfont_backcoord[v+1][0] = sw;\n\t\tfont_backcoord[v+1][1] = sy;\n\t\tfont_backcoord[v+2][0] = sw;\n\t\tfont_backcoord[v+2][1] = sh;\n\t\tfont_backcoord[v+3][0] = sx;\n\t\tfont_backcoord[v+3][1] = sh;\n\n\t\tfont_backcoloura[v+0] = font_backcolour;\n\t\tfont_backcoloura[v+1] = font_backcolour;\n\t\tfont_backcoloura[v+2] = font_backcolour;\n\t\tfont_backcoloura[v+3] = font_backcolour;\n\t}\n\n\treturn nextx;\n}\n\n/*there is no sane way to make this pixel-correct*/\nfloat Font_DrawScaleChar(float px, float py, unsigned int charflags, unsigned int codepoint)\n{\n\tstruct charcache_s *c;\n\tfloat s0, s1;\n\tfloat t0, t1;\n\tfloat nextx;\n\tfloat sx, sy, sw, sh;\n\tint col;\n\tint v;\n\tstruct font_s *font = curfont;\n\tfloat cw, ch;\n#ifdef D3D11QUAKE\n\tfloat dxbias = 0;//(qrenderer == QR_DIRECT3D11)?0.5:0;\n#else\n#define dxbias 0\n#endif\n\n//\tif (!curfont_scaled)\n//\t\treturn Font_DrawChar(px, py, charcode);\n\n\tif (charflags & CON_2NDCHARSETTEXT)\n\t{\n\t\tif (font->alt)\n\t\t{\n\t\t\tfont = font->alt;\n\t\t\tcharflags &= ~CON_2NDCHARSETTEXT;\n\t\t}\n\t\telse if (codepoint >= 0xe000 && codepoint <= 0xe0ff)\n\t\t\tcharflags &= ~CON_2NDCHARSETTEXT;\t//don't double-dip\n\t}\n\n\tcw = curfont_scale[0];\n\tch = curfont_scale[1];\n\n\t//crash if there is no current font.\n\tc = Font_GetChar(font, codepoint);\n\tif (!c)\n\t\treturn px;\n\n\tnextx = px + c->advance*cw;\n\n\tif (codepoint == ' ' && (charflags & (CON_RICHFORECOLOUR|CON_NONCLEARBG)) != CON_NONCLEARBG)\n\t\treturn nextx;\n\n\tif (charflags & CON_BLINKTEXT)\n\t{\n\t\tif (!cl_noblink.ival)\n\t\t\tif ((int)(realtime*3) & 1)\n\t\t\t\treturn nextx;\n\t}\n\n\tif (charflags & CON_RICHFORECOLOUR)\n\t{\n\t\tcol = charflags & (CON_2NDCHARSETTEXT|CON_RICHFORECOLOUR|(0xfff<<CON_RICHBSHIFT));\n\t\tif (col != font_colourmask)\n\t\t{\n\t\t\tvec4_t rgba;\n\t\t\tif (font_backcolour.rgba[3])\n\t\t\t{\n\t\t\t\tFont_Flush();\n\t\t\t\tR2D_Flush = Font_Flush;\n\t\t\t}\n\t\t\tfont_colourmask = col;\n\n\t\t\trgba[0] = ((col>>CON_RICHRSHIFT)&0xf)*0x11;\n\t\t\trgba[1] = ((col>>CON_RICHGSHIFT)&0xf)*0x11;\n\t\t\trgba[2] = ((col>>CON_RICHBSHIFT)&0xf)*0x11;\n\t\t\trgba[3] = 255;\n\n\t\t\tfont_backcolour.c = 0;\n\n\t\t\tif (charflags & CON_2NDCHARSETTEXT)\n\t\t\t{\n\t\t\t\trgba[0] *= font->alttint[0];\n\t\t\t\trgba[1] *= font->alttint[1];\n\t\t\t\trgba[2] *= font->alttint[2];\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\trgba[0] *= font->tint[0];\n\t\t\t\trgba[1] *= font->tint[1];\n\t\t\t\trgba[2] *= font->tint[2];\n\t\t\t}\n\t\t\trgba[0] *= font_foretint[0];\n\t\t\trgba[1] *= font_foretint[1];\n\t\t\trgba[2] *= font_foretint[2];\n\t\t\trgba[3] *= font_foretint[3];\n\t\t\tfont_forecolour.rgba[0] = min(rgba[0], 255);\n\t\t\tfont_forecolour.rgba[1] = min(rgba[1], 255);\n\t\t\tfont_forecolour.rgba[2] = min(rgba[2], 255);\n\t\t\tfont_forecolour.rgba[3] = min(rgba[3], 255);\n\t\t}\n\t}\n\telse\n\t{\n\t\tcol = charflags & (CON_2NDCHARSETTEXT|CON_NONCLEARBG|CON_BGMASK|CON_FGMASK|CON_HALFALPHA);\n\t\tif (col != font_colourmask)\n\t\t{\n\t\t\tvec4_t rgba;\n\t\t\tif (font_backcolour.rgba[3] != ((charflags & CON_NONCLEARBG)?127:0))\n\t\t\t{\n\t\t\t\tFont_Flush();\n\t\t\t\tR2D_Flush = Font_Flush;\n\t\t\t}\n\t\t\tfont_colourmask = col;\n\n\t\t\tcol = (charflags&CON_FGMASK)>>CON_FGSHIFT;\n\t\t\tif (charflags & CON_HALFALPHA)\n\t\t\t{\n\t\t\t\trgba[0] = consolecolours[col].fr*0x7f;\n\t\t\t\trgba[1] = consolecolours[col].fg*0x7f;\n\t\t\t\trgba[2] = consolecolours[col].fb*0x7f;\n\t\t\t\trgba[3] = 0x7f;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\trgba[0] = consolecolours[col].fr*255;\n\t\t\t\trgba[1] = consolecolours[col].fg*255;\n\t\t\t\trgba[2] = consolecolours[col].fb*255;\n\t\t\t\trgba[3] = 255;\n\t\t\t}\n\n\t\t\tcol = (charflags&CON_BGMASK)>>CON_BGSHIFT;\n\t\t\tif (charflags & CON_NONCLEARBG)\n\t\t\t{\n\t\t\t\tfont_backcolour.rgba[0] = consolecolours[col].fr*0xc0;\n\t\t\t\tfont_backcolour.rgba[1] = consolecolours[col].fg*0xc0;\n\t\t\t\tfont_backcolour.rgba[2] = consolecolours[col].fb*0xc0;\n\t\t\t\tfont_backcolour.rgba[3] = 0xc0;\n\t\t\t}\n\t\t\telse\n\t\t\t\tfont_backcolour.c = 0;\n\n\t\t\tif (charflags & CON_2NDCHARSETTEXT)\n\t\t\t{\n\t\t\t\trgba[0] *= font->alttint[0];\n\t\t\t\trgba[1] *= font->alttint[1];\n\t\t\t\trgba[2] *= font->alttint[2];\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\trgba[0] *= font->tint[0];\n\t\t\t\trgba[1] *= font->tint[1];\n\t\t\t\trgba[2] *= font->tint[2];\n\t\t\t}\n\t\t\trgba[0] *= font_foretint[0];\n\t\t\trgba[1] *= font_foretint[1];\n\t\t\trgba[2] *= font_foretint[2];\n\t\t\trgba[3] *= font_foretint[3];\n\t\t\tfont_forecolour.rgba[0] = min(rgba[0], 255);\n\t\t\tfont_forecolour.rgba[1] = min(rgba[1], 255);\n\t\t\tfont_forecolour.rgba[2] = min(rgba[2], 255);\n\t\t\tfont_forecolour.rgba[3] = min(rgba[3], 255);\n\t\t}\n\t}\n\n\ts0 = (float)c->bmx/PLANEWIDTH;\n\tt0 = (float)c->bmy/PLANEWIDTH;\n\ts1 = (float)(c->bmx+c->bmw)/PLANEWIDTH;\n\tt1 = (float)(c->bmy+c->bmh)/PLANEWIDTH;\n\n\tif (c->texplane >= DEFAULTPLANE)\n\t{\n\t\tsx = ((px+c->left*cw));\n\t\tsy = ((py+c->top*ch));\n\t\tsw = ((font->charheight*cw));\n\t\tsh = ((font->charheight*ch));\n\n\t\tif (c->texplane == DEFAULTPLANE)\n\t\t\tv = Font_BeginChar(fontplanes.defaultfont);\n\t\telse\n\t\t\tv = Font_BeginChar(font->singletexture);\n\t}\n\telse\n\t{\n\t\tsx = (px+c->left*cw);\n\t\tsy = (py+c->top*ch);\n\t\tsw = ((c->bmw*cw));\n\t\tsh = ((c->bmh*ch));\n\t\tv = Font_BeginChar(fontplanes.texnum[c->texplane]);\n\t}\n\n\tsx += dxbias;\n\tsy += dxbias;\n\n\tsx *= (int)vid.width / (float)vid.rotpixelwidth;\n\tsy *= (int)vid.height / (float)vid.rotpixelheight;\n\tsw *= (int)vid.width / (float)vid.rotpixelwidth;\n\tsh *= (int)vid.height / (float)vid.rotpixelheight;\n\n\tfont_texcoord[v+0][0] = s0;\n\tfont_texcoord[v+0][1] = t0;\n\tfont_texcoord[v+1][0] = s1;\n\tfont_texcoord[v+1][1] = t0;\n\tfont_texcoord[v+2][0] = s1;\n\tfont_texcoord[v+2][1] = t1;\n\tfont_texcoord[v+3][0] = s0;\n\tfont_texcoord[v+3][1] = t1;\n\n\tfont_coord[v+0][0] = sx;\n\tfont_coord[v+0][1] = sy;\n\tfont_coord[v+1][0] = sx+sw;\n\tfont_coord[v+1][1] = sy;\n\tfont_coord[v+2][0] = sx+sw;\n\tfont_coord[v+2][1] = sy+sh;\n\tfont_coord[v+3][0] = sx;\n\tfont_coord[v+3][1] = sy+sh;\n\n\tfont_forecoloura[v+0] = font_forecolour;\n\tfont_forecoloura[v+1] = font_forecolour;\n\tfont_forecoloura[v+2] = font_forecolour;\n\tfont_forecoloura[v+3] = font_forecolour;\n\n\tif (font_colourmask & CON_NONCLEARBG)\n\t{\n\t\tsx = px + dxbias;\n\t\tsy = py + dxbias;\n\t\tsw = sx + c->advance;\n\t\tsh = sy + font->charheight;\n\n\t\tsx *= (int)vid.width / (float)vid.rotpixelwidth;\n\t\tsy *= (int)vid.height / (float)vid.rotpixelheight;\n\t\tsw *= (int)vid.width / (float)vid.rotpixelwidth;\n\t\tsh *= (int)vid.height / (float)vid.rotpixelheight;\n\n\t\t//don't care about texcoords\n\t\tfont_backcoord[v+0][0] = sx;\n\t\tfont_backcoord[v+0][1] = sy;\n\t\tfont_backcoord[v+1][0] = sw;\n\t\tfont_backcoord[v+1][1] = sy;\n\t\tfont_backcoord[v+2][0] = sw;\n\t\tfont_backcoord[v+2][1] = sh;\n\t\tfont_backcoord[v+3][0] = sx;\n\t\tfont_backcoord[v+3][1] = sh;\n\n\t\tfont_backcoloura[v+0] = font_backcolour;\n\t\tfont_backcoloura[v+1] = font_backcolour;\n\t\tfont_backcoloura[v+2] = font_backcolour;\n\t\tfont_backcoloura[v+3] = font_backcolour;\n\t}\n\n\treturn nextx;\n}\n\n#endif\t//!SERVERONLY\n"
  },
  {
    "path": "engine/gl/gl_heightmap.c",
    "content": "/*\nSee gl_terrain.h for terminology, networking notes, etc.\n*/\n\n//FIXME: render in lightmap batches. generate vbos accordingly.\n//FIXME: assign to lightmaps by matching textures. should be able to get up to 65536/(17*17)=226 per section before index limits hit, 16*16=256 allows for 1024*1024 lightmaps.\n//FIXME: sort texture blend names to reduce combinations\n\n#include \"quakedef.h\"\n\n#ifdef TERRAIN\n#include \"glquake.h\"\n#include \"shader.h\"\n#include \"com_mesh.h\"\n\n#include \"pr_common.h\"\n\n#include \"gl_terrain.h\"\n\nstatic plugterrainfuncs_t terrainfuncs;\n\n\ncvar_t mod_terrain_networked = CVARD(\"mod_terrain_networked\", \"0\", \"Terrain edits are networked. Clients will download sections on demand, and servers will notify clients of changes.\");\ncvar_t mod_terrain_defaulttexture = CVARD(\"mod_terrain_defaulttexture\", \"\", \"Newly created terrain tiles will use this texture. This should generally be updated by the terrain editor.\");\ncvar_t mod_terrain_savever = CVARD(\"mod_terrain_savever\", \"\", \"Which terrain section version to write if terrain was edited.\");\ncvar_t mod_terrain_sundir = CVARD(\"mod_terrain_sundir\", \"0.4 0.7 2\", \"The direction of the sun (vector will be normalised).\");\ncvar_t mod_terrain_ambient = CVARD(\"mod_terrain_ambient\", \"0.5\", \"Proportion of ambient light.\");\ncvar_t mod_terrain_shadows = CVARD(\"mod_terrain_shadows\", \"0\", \"Cast rays to determine whether parts of the terrain should be in shadow.\");\ncvar_t mod_terrain_shadow_dist = CVARD(\"mod_terrain_shadow_dist\", \"2048\", \"How far rays should be cast in order to look for occlusing geometry.\");\ncvar_t mod_terrain_brushlights = CVARD(\"mod_map_lights\", \"0\", \"Calculates lighting on brushes/patches.\");\ncvar_t mod_terrain_brushtexscale = CVARD(\"mod_map_texscale\", \"1\", \"Defines the scale of texture texels. Use 1 for quake+quake2 maps, and 0.5 for quake3 maps.\");\n\nenum\n{\n\thmcmd_brush_delete,\t\t//brush OR patch destruction\n\thmcmd_brush_insert,\t\t//brush creation\n\thmcmd_prespawning,\t\t//sent before initial inserts\n\thmcmd_prespawned,\t\t//sent just after initial inserts\n\thmcmd_patch_insert,\t\t//patch creation\n\n\thmcmd_ent_edit = 0x40,\n\thmcmd_ent_remove\n};\n\n\nvoid validatelinks(link_t *firstnode)\n{\n/*\tlink_t *node;\n\n\tCOM_AssertMainThread(\"foo\");\n\n\tfor (node = firstnode->next; node; node = node->next)\n\t\tif (firstnode == node)\n\t\t\tbreak;\n\n\tfor (node = firstnode->prev; node; node = node->prev)\n\t\tif (firstnode == node)\n\t\t\tbreak;\n\n\treturn;*/\n}\n\nvoid validatelinks2(link_t *firstnode, link_t *panic)\n{\n/*\tlink_t *node;\n\n\tCOM_AssertMainThread(\"foo\");\n\n\tfor (node = firstnode->next; node; node = node->next)\n\t{\n\t\tif (node == panic)\n\t\t\tSys_Error(\"Panic\\n\");\n\t\tif (firstnode == node)\n\t\t\tbreak;\n\t}\n\n\tfor (node = firstnode->prev; node; node = node->prev)\n\t{\n\t\tif (node == panic)\n\t\t\tSys_Error(\"Panic\\n\");\n\t\tif (firstnode == node)\n\t\t\tbreak;\n\t}\n\n\treturn;*/\n}\n\n\nstatic hmsection_t *QDECL Terr_GetSection(heightmap_t *hm, int x, int y, unsigned int flags);\nstatic void Terr_LoadSectionWorker(void *ctx, void *data, size_t a, size_t b);\nstatic void Terr_WorkerLoadedSection(void *ctx, void *data, size_t a, size_t b);\nstatic void Terr_WorkerFailedSection(void *ctx, void *data, size_t a, size_t b);\n\nstatic void Terr_Brush_DeleteIdx(heightmap_t *hm, size_t idx);\n#ifdef HAVE_CLIENT\nstatic void ted_dorelight(model_t *m, heightmap_t *hm);\nstatic void Terr_WorkerLoadedSectionLightmap(void *ctx, void *data, size_t a, size_t b);\nstatic qboolean Terr_Collect(heightmap_t *hm);\nstatic void Terr_Brush_Draw(heightmap_t *hm, batch_t **batches, entity_t *e);\n\nstatic texid_t Terr_LoadTexture(char *name)\n{\n\textern texid_t missing_texture;\n\ttexid_t id;\n\tif (*name)\n\t{\n\t\tid = R_LoadHiResTexture(name, NULL, 0);\n\t\tif (!TEXVALID(id))\n\t\t{\n\t\t\tid = missing_texture;\n\t\t\tCon_Printf(\"Unable to load texture %s\\n\", name);\n\t\t}\n\t}\n\telse\n\t\tid = missing_texture;\n\treturn id;\n}\n#endif\n\nstatic void Terr_LoadSectionTextures(hmsection_t *s)\n{\n#ifdef HAVE_CLIENT\n\textern texid_t missing_texture;\n\tstruct hmwater_s *w;\n\tif (isDedicated)\n\t\treturn;\n\t//CL_CheckOrEnqueDownloadFile(s->texname[0], NULL, 0);\n\t//CL_CheckOrEnqueDownloadFile(s->texname[1], NULL, 0);\n\t//CL_CheckOrEnqueDownloadFile(s->texname[2], NULL, 0);\n\t//CL_CheckOrEnqueDownloadFile(s->texname[3], NULL, 0);\n\tswitch(s->hmmod->mode)\n\t{\n\tcase HMM_BLOCKS:\n\t\ts->textures.base\t\t\t= Terr_LoadTexture(va(\"maps/%s/atlas.tga\", s->hmmod->path));\n\t\ts->textures.fullbright\t\t= Terr_LoadTexture(va(\"maps/%s/atlas_luma.tga\", s->hmmod->path));\n\t\ts->textures.bump\t\t\t= Terr_LoadTexture(va(\"maps/%s/atlas_norm.tga\", s->hmmod->path));\n\t\ts->textures.specular\t\t= Terr_LoadTexture(va(\"maps/%s/atlas_spec.tga\", s->hmmod->path));\n\t\ts->textures.upperoverlay\t= missing_texture;\n\t\ts->textures.loweroverlay\t= missing_texture;\n\t\tbreak;\n\tcase HMM_TERRAIN:\n\t\ts->textures.base\t\t\t= Terr_LoadTexture(s->texname[0]);\n\t\ts->textures.upperoverlay\t= Terr_LoadTexture(s->texname[1]);\n\t\ts->textures.loweroverlay\t= Terr_LoadTexture(s->texname[2]);\n\t\ts->textures.fullbright\t\t= Terr_LoadTexture(s->texname[3]);\n\t\ts->textures.bump\t\t\t= *s->texname[0]?R_LoadHiResTexture(va(\"%s_norm\", s->texname[0]), NULL, 0):r_nulltex;\n\t\ts->textures.specular\t\t= *s->texname[0]?R_LoadHiResTexture(va(\"%s_spec\", s->texname[0]), NULL, 0):r_nulltex;\n\t\tbreak;\n\t}\n\tfor (w = s->water; w; w = w->next)\n\t{\n\t\tw->shader = R_RegisterCustom (NULL, w->shadername, SUF_NONE, Shader_DefaultWaterShader, NULL);\n\t\tR_BuildDefaultTexnums(NULL, w->shader, IF_WORLDTEX);\t//this might get expensive. hideously so.\n\t}\n#endif\n}\n\nstatic qboolean QDECL Terr_InitLightmap(hmsection_t *s, qboolean initialise)\n{\n#ifndef HAVE_CLIENT\n\treturn false;\n#else\n\theightmap_t *hm = s->hmmod;\n\n\tif (s->lightmap < 0)\n\t{\n\t\tstruct lmsect_s *lms;\n\t\tSys_LockMutex(com_resourcemutex);\n\t\twhile (!hm->unusedlmsects)\n\t\t{\n\t\t\tint lm;\n\t\t\tint i;\n\t\t\tSys_UnlockMutex(com_resourcemutex);\n\t\t\tlm = Surf_NewLightmaps(1, SECTTEXSIZE*LMCHUNKS, SECTTEXSIZE*LMCHUNKS, PTI_BGRA8, false);\n\t\t\tSys_LockMutex(com_resourcemutex);\n\t\t\tfor (i = 0; i < LMCHUNKS*LMCHUNKS; i++)\n\t\t\t{\n\t\t\t\tlms = BZ_Malloc(sizeof(*lms));\n\t\t\t\tlms->lm = lm;\n\t\t\t\tlms->x = (i & (LMCHUNKS-1))*SECTTEXSIZE;\n\t\t\t\tlms->y = (i / LMCHUNKS)*SECTTEXSIZE;\n\t\t\t\tlms->next = hm->unusedlmsects;\n\t\t\t\thm->unusedlmsects = lms;\n\t\t\t\thm->numunusedlmsects++;\n\t\t\t}\n\t\t}\n\n\t\tlms = hm->unusedlmsects;\n\t\thm->unusedlmsects = lms->next;\n\t\t\n\t\ts->lightmap = lms->lm;\n\t\ts->lmx = lms->x;\n\t\ts->lmy = lms->y;\n\n\t\thm->numunusedlmsects--;\n\t\thm->numusedlmsects++;\n\t\tSys_UnlockMutex(com_resourcemutex);\n\n\t\tZ_Free(lms);\n\t\tinitialise = true;\n\t}\n\n\tif (initialise && s->lightmap >= 0)\n\t{\n\t\tint x, y;\n\t\tunsigned char *lm = lightmap[s->lightmap]->lightmaps;\n\t\tunsigned int pixbytes = lightmap[s->lightmap]->pixbytes;\n\t\tlm += (s->lmy * HMLMSTRIDE + s->lmx) * pixbytes;\n\t\tfor (y = 0; y < SECTTEXSIZE; y++)\n\t\t{\n\t\t\tfor (x = 0; x < SECTTEXSIZE; x++)\n\t\t\t{\n\t\t\t\tlm[x*4+0] = 0;\n\t\t\t\tlm[x*4+1] = 0;\n\t\t\t\tlm[x*4+2] = 0;\n\t\t\t\tlm[x*4+3] = 255;\n\t\t\t}\n\t\t\tlm += (HMLMSTRIDE)*pixbytes;\n\t\t}\n\t}\n\n\tif (s->lightmap >= 0)\n\t{\n\t\tlightmap[s->lightmap]->modified = true;\n\t\tlightmap[s->lightmap]->rectchange.l = 0;\n\t\tlightmap[s->lightmap]->rectchange.t = 0;\n\t\tlightmap[s->lightmap]->rectchange.r = HMLMSTRIDE;\n\t\tlightmap[s->lightmap]->rectchange.b = HMLMSTRIDE;\n\t}\n\n\treturn s->lightmap>=0;\n#endif\n}\n\nstatic char *genextendedhex(int n, char *buf)\n{\n\tchar *ret;\n\tstatic char nibble[16] = \"0123456789abcdef\";\n\tunsigned int m;\n\tint i;\n\tfor (i = 7; i >= 1; i--)\t//>=1 ensures at least two nibbles appear.\n\t{\n\t\tm = 0xfffffff8<<(i*4);\n\t\tif ((n & m) != m && (n & m) != 0)\n\t\t\tbreak;\n\t}\n\tret = buf;\n\tfor(i++; i >= 0; i--)\n\t\t*buf++ = nibble[(n>>i*4) & 0xf];\n\t*buf++ = 0;\n\treturn ret;\n}\nstatic char *Terr_DiskBlockName(heightmap_t *hm, int sx, int sy, char *out, size_t outsize)\n{\n\tchar xpart[9];\n\tchar ypart[9];\n\t//using a naming scheme centered around 0 means we can gracefully expand the map away from 0,0\n\tsx -= CHUNKBIAS;\n\tsy -= CHUNKBIAS;\n\t//wrap cleanly\n\tsx &= CHUNKLIMIT-1;\n\tsy &= CHUNKLIMIT-1;\n\tsx /= SECTIONSPERBLOCK;\n\tsy /= SECTIONSPERBLOCK;\n\tif (sx >= CHUNKBIAS/SECTIONSPERBLOCK)\n\t\tsx |= 0xffffff00;\n\tif (sy >= CHUNKBIAS/SECTIONSPERBLOCK)\n\t\tsy |= 0xffffff00;\n\tQ_snprintfz(out, outsize, \"maps/%s/block_%s_%s.hms\", hm->path, genextendedhex(sx, xpart), genextendedhex(sy, ypart));\n\treturn out;\n}\nstatic char *Terr_DiskSectionName(heightmap_t *hm, int sx, int sy, char *out, size_t outsize)\n{\n\tsx -= CHUNKBIAS;\n\tsy -= CHUNKBIAS;\n\t//wrap cleanly\n\tsx &= CHUNKLIMIT-1;\n\tsy &= CHUNKLIMIT-1;\n\tQ_snprintfz(out, outsize, \"maps/%s/sect_%03x_%03x.hms\", hm->path, sx, sy);\n\treturn out;\n}\n#ifdef HAVE_CLIENT\nstatic char *Terr_TempDiskSectionName(heightmap_t *hm, int sx, int sy)\n{\n\tsx -= CHUNKBIAS;\n\tsy -= CHUNKBIAS;\n\t//wrap cleanly\n\tsx &= CHUNKLIMIT-1;\n\tsy &= CHUNKLIMIT-1;\n\treturn va(\"temp/%s/sect_%03x_%03x.hms\", hm->path, sx, sy);\n}\n#endif\n\n#ifdef HAVE_SERVER\nstatic int dehex_e(int i, qboolean *error)\n{\n\tif      (i >= '0' && i <= '9')\n\t\treturn (i-'0');\n\telse if (i >= 'A' && i <= 'F')\n\t\treturn (i-'A'+10);\n\telse if (i >= 'a' && i <= 'f')\n\t\treturn (i-'a'+10);\n\telse\n\t\t*error = true;\n\treturn 0;\n}\nstatic qboolean Terr_IsSectionFName(heightmap_t *hm, const char *fname, int *sx, int *sy)\n{\n\tint l;\n\tqboolean error = false;\n\t*sx = 0xdeafbeef;\t//something clearly invalid\n\t*sy = 0xdeafbeef;\n\n\t//not this model...\n\tif (!hm)\n\t\treturn false;\n\n\t//expect the first 5 chars to be maps/ or temp/\n\tfname += 5;\n\n\tl = strlen(hm->path);\n\tif (strncmp(fname, hm->path, l) || fname[l] != '/')\n\t\treturn false;\n\tfname += l+1;\n\n\t//fname now has a fixed length.\n\tif (strlen(fname) != 16)\n\t\treturn false;\n\tif (strncmp(fname, \"sect_\", 5) || fname[8] != '_' || (strcmp(fname+12, \".hms\") && strcmp(fname+12, \".tmp\")))\n\t\treturn false;\n\n\t*sx = 0;\n\t*sx += dehex_e(fname[5], &error)<<8;\n\t*sx += dehex_e(fname[6], &error)<<4;\n\t*sx += dehex_e(fname[7], &error)<<0;\n\n\t*sy = 0;\n\t*sy += dehex_e(fname[9], &error)<<8;\n\t*sy += dehex_e(fname[10], &error)<<4;\n\t*sy += dehex_e(fname[11], &error)<<0;\n\n\t*sx += CHUNKBIAS;\n\t*sy += CHUNKBIAS;\n\n\tif ((unsigned)*sx >= CHUNKLIMIT)\n\t\t*sx -= CHUNKLIMIT;\n\tif ((unsigned)*sy >= CHUNKLIMIT)\n\t\t*sy -= CHUNKLIMIT;\n\n\t//make sure its a valid section index.\n\tif ((unsigned)*sx >= CHUNKLIMIT)\n\t\treturn false;\n\tif ((unsigned)*sy >= CHUNKLIMIT)\n\t\treturn false;\n\treturn true;\n}\n#endif\n\nstatic int QDECL Terr_GenerateSections(heightmap_t *hm, int sx, int sy, int count, hmsection_t **sects)\n{\n\t//a worker is trying to load multiple sections at once.\n\t//lock ALL of them atomically, so that we don't end up with too many workers all doing stuff at once.\n\tint x, y;\n\t\n\thmsection_t *s;\n\thmcluster_t *cluster;\n\tint numgen = 0;\n\n\tSys_LockMutex(com_resourcemutex);\n\tfor (y = 0; y < count; y++)\n\t\tfor (x = 0; x < count; x++)\n\t\t{\n\t\t\tint clusternum = ((sx+x)/MAXSECTIONS) + ((sy+y)/MAXSECTIONS)*MAXCLUSTERS;\n\t\t\tcluster = hm->cluster[clusternum];\n\t\t\tif (!cluster)\n\t\t\t\tcluster = hm->cluster[clusternum] = Z_Malloc(sizeof(*cluster));\n\t\t\ts = cluster->section[((sx+x)%MAXSECTIONS) + ((sy+y)%MAXSECTIONS)*MAXSECTIONS];\n\t\t\tif (!s)\n\t\t\t{\n\t\t\t\ts = Z_Malloc(sizeof(*s));\n\t\t\t\ts->loadstate = TSLS_LOADING0;\n#ifdef HAVE_CLIENT\n\t\t\t\ts->lightmap = -1;\n#endif\n\t\t\t\ts->numents = 0;\n\t\t\t\ts->sx = sx + x;\n\t\t\t\ts->sy = sy + y;\n\t\t\t\tcluster->section[(s->sx%MAXSECTIONS) + (s->sy%MAXSECTIONS)*MAXSECTIONS] = s;\n\t\t\t\thm->activesections++;\n\t\t\t\ts->hmmod = hm;\n\n\t\t\t\ts->flags = TSF_DIRTY;\n\n\t\t\t\thm->loadingsections+=1;\n\t\t\t}\n#ifdef HAVE_CLIENT\n\t\t\telse if (s->loadstate == TSLS_LOADED && s->lightmap < 0)\n\t\t\t\t;\t//it lost its lightmap. the main thread won't be drawing with it, nor do any loaders.\n\t\t\t\t\t//FIXME: might be used by tracelines on a worker (eg lightmap generation)\n#endif\n\t\t\telse if (s->loadstate != TSLS_LOADING0)\n\t\t\t{\n\t\t\t\t//this one is already active.\n\t\t\t\tsects[x + y*count] = NULL;\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\ts->loadstate = TSLS_LOADING1;\n\n\t\t\tsects[x + y*count] = s;\n\t\t\tnumgen++;\n\t\t}\n\tSys_UnlockMutex(com_resourcemutex);\n\treturn numgen;\n}\nstatic hmsection_t *QDECL Terr_GenerateSection(heightmap_t *hm, int sx, int sy, qboolean scheduleload)\n{\n\thmsection_t *s;\n\thmcluster_t *cluster;\n\tint clusternum = (sx/MAXSECTIONS) + (sy/MAXSECTIONS)*MAXCLUSTERS;\n\n#ifdef LOADERTHREAD\n\tSys_LockMutex(com_resourcemutex);\n#endif\n\tcluster = hm->cluster[clusternum];\n\tif (!cluster)\n\t\tcluster = hm->cluster[clusternum] = Z_Malloc(sizeof(*cluster));\n\ts = cluster->section[(sx%MAXSECTIONS) + (sy%MAXSECTIONS)*MAXSECTIONS];\n\tif (!s)\n\t{\n\t\ts = Z_Malloc(sizeof(*s));\n\t\tif (!s)\n\t\t{\n#ifdef LOADERTHREAD\n\t\t\tSys_UnlockMutex(com_resourcemutex);\n#endif\n\t\t\treturn NULL;\n\t\t}\n#ifdef HAVE_CLIENT\n\t\ts->lightmap = -1;\n\t\ts->numents = 0;\n#endif\n\n\t\ts->sx = sx;\n\t\ts->sy = sy;\n\t\tcluster->section[(sx%MAXSECTIONS) + (sy%MAXSECTIONS)*MAXSECTIONS] = s;\n\t\thm->activesections++;\n\t\ts->hmmod = hm;\n\n\t\ts->flags = TSF_DIRTY;\n\n\t\thm->loadingsections+=1;\n\n\t\tif (!scheduleload)\n\t\t{\t//no scheduling means that we're loading it NOW, on this thread.\n\t\t\ts->loadstate = TSLS_LOADING1;\n#ifdef LOADERTHREAD\n\t\t\tSys_UnlockMutex(com_resourcemutex);\n#endif\n\t\t\treturn s;\n\t\t}\n\t\ts->loadstate = TSLS_LOADING0;\n\n#ifdef LOADERTHREAD\n\t\tSys_UnlockMutex(com_resourcemutex);\n#endif\n\t\tCOM_AddWork(WG_LOADER, Terr_LoadSectionWorker, s, hm, sx, sy);\n\t\treturn s;\n\t}\n\tif (!scheduleload)\n\t{\n\t\tif (s->loadstate == TSLS_LOADING0)\n\t\t\ts->loadstate = TSLS_LOADING1;\n\t\telse\n\t\t\ts = NULL;\n\t}\n#ifdef LOADERTHREAD\n\tSys_UnlockMutex(com_resourcemutex);\n#endif\n\treturn s;\n}\n\n//generates some water\nstatic void *QDECL Terr_GenerateWater(hmsection_t *s, float maxheight)\n{\n\tint i;\n\tstruct hmwater_s *w;\n\tw = Z_Malloc(sizeof(*s->water));\n\tw->next = s->water;\n\ts->water = w;\n\tQ_strncpyz(w->shadername, s->hmmod->defaultwatershader, sizeof(w->shadername));\n\tw->simple = true;\n\tw->contentmask = FTECONTENTS_WATER;\n\tmemset(w->holes, 0, sizeof(w->holes));\n\tfor (i = 0; i < 9*9; i++)\n\t\tw->heights[i] = maxheight;\n\tw->maxheight = w->minheight = maxheight;\n\tif (s->maxh_cull < w->maxheight)\n\t\ts->maxh_cull = w->maxheight;\n\treturn w;\n}\n\n//embeds a mesh\nstatic void QDECL Terr_AddMesh(heightmap_t *hm, int loadflags, model_t *mod, const char *modelname, vec3_t epos, vec3_t axis[3], float scale)\n{\n#ifdef HAVE_CLIENT\n\tstruct hmentity_s *e, *f = NULL;\n\thmsection_t *s;\n\tint min[2], max[2], coord[2];\n\tint i;\n\n\tif (!mod)\n\t{\n\t\tif (modelname)\n\t\t\tmod = Mod_ForName(modelname, MLV_WARN);\n\t\tif (!mod)\n\t\t\treturn;\n\t}\n\n\tif (!scale)\n\t\tscale = 1;\n\n\tif (mod->loadstate != MLS_LOADED)\n\t\tCon_DPrintf(\"Terr_AddMesh: model is not loaded yet\\n\");\n\n\t//I do NOT like that this depends on the size of the model.\n\tif (axis[0][0] != 1 || axis[0][1] != 0 || axis[0][2] != 0 ||\n\t\taxis[1][0] != 0 || axis[1][1] != 1 || axis[1][2] != 0 ||\n\t\taxis[2][0] != 0 || axis[2][1] != 0 || axis[2][2] != 1)\n\t{\n\t\tmin[0] = floor((epos[0]-mod->radius*scale) / hm->sectionsize) + CHUNKBIAS;\n\t\tmin[1] = floor((epos[1]-mod->radius*scale) / hm->sectionsize) + CHUNKBIAS;\n\t\tmin[0] = bound(hm->firstsegx, min[0], hm->maxsegx-1);\n\t\tmin[1] = bound(hm->firstsegy, min[1], hm->maxsegy-1);\n\t\t\n\t\tmax[0] = floor((epos[0]+mod->radius*scale) / hm->sectionsize) + CHUNKBIAS;\n\t\tmax[1] = floor((epos[1]+mod->radius*scale) / hm->sectionsize) + CHUNKBIAS;\n\t\tmax[0] = bound(hm->firstsegx, max[0], hm->maxsegx-1);\n\t\tmax[1] = bound(hm->firstsegy, max[1], hm->maxsegy-1);\n\t}\n\telse\n\t{\n\t\tmin[0] = floor((epos[0]+mod->mins[0]*scale) / hm->sectionsize) + CHUNKBIAS;\n\t\tmin[1] = floor((epos[1]+mod->mins[1]*scale) / hm->sectionsize) + CHUNKBIAS;\n\t\tmin[0] = bound(hm->firstsegx, min[0], hm->maxsegx-1);\n\t\tmin[1] = bound(hm->firstsegy, min[1], hm->maxsegy-1);\n\t\t\n\t\tmax[0] = floor((epos[0]+mod->maxs[0]*scale) / hm->sectionsize) + CHUNKBIAS;\n\t\tmax[1] = floor((epos[1]+mod->maxs[1]*scale) / hm->sectionsize) + CHUNKBIAS;\n\t\tmax[0] = bound(hm->firstsegx, max[0], hm->maxsegx-1);\n\t\tmax[1] = bound(hm->firstsegy, max[1], hm->maxsegy-1);\n\t}\n\n\tSys_LockMutex(hm->entitylock);\n\t//try to find the ent if it already exists (don't do dupes)\n\tfor (e = hm->entities; e; e = e->next)\n\t{\n\t\tif (!e->refs)\n\t\t\tf = e;\n\t\telse\n\t\t{\n\t\t\tif (e->ent.origin[0] != epos[0] || e->ent.origin[1] != epos[1] || e->ent.origin[2] != epos[2])\n\t\t\t\tcontinue;\n\t\t\tif (e->ent.model != mod || e->ent.scale != scale)\n\t\t\t\tcontinue;\n\t\t\tif (memcmp(axis, e->ent.axis, sizeof(e->ent.axis)))\n\t\t\t\tcontinue;\n\t\t\tbreak;\t//looks like a match.\n\t\t}\n\t}\n\t//allocate it if needed\n\tif (!e)\n\t{\n\t\tif (f)\n\t\t\te = f;\t//can reuse a released one\n\t\telse\n\t\t{\t//allocate one\n\t\t\te = Z_Malloc(sizeof(*e));\n\t\t\te->next = hm->entities;\n\t\t\thm->entities = e;\n\t\t}\n\n#ifdef HEXEN2\n\t\te->ent.drawflags = SCALE_ORIGIN_ORIGIN;\n#endif\n\t\te->ent.scale = scale;\n\t\te->ent.playerindex = -1;\n\t\te->ent.framestate.g[FS_REG].lerpweight[0] = 1;\n\t\te->ent.topcolour = TOP_DEFAULT;\n\t\te->ent.bottomcolour = BOTTOM_DEFAULT;\n\t\te->ent.shaderRGBAf[0] = 1;\n\t\te->ent.shaderRGBAf[1] = 1;\n\t\te->ent.shaderRGBAf[2] = 1;\n\t\te->ent.shaderRGBAf[3] = 1;\n\t\tVectorCopy(epos, e->ent.origin);\n\t\tmemcpy(e->ent.axis, axis, sizeof(e->ent.axis));\n\t\te->ent.model = mod;\n\t}\n\n\tfor (coord[0] = min[0]; coord[0] <= max[0]; coord[0]++)\n\t{\n\t\tfor (coord[1] = min[1]; coord[1] <= max[1]; coord[1]++)\n\t\t{\n\t\t\ts = Terr_GetSection(hm, coord[0], coord[1], loadflags|TGS_ANYSTATE);\n\t\t\tif (!s)\n\t\t\t\tcontinue;\n\n\t\t\t//don't add pointless dupes\n\t\t\tfor (i = 0; i < s->numents; i++)\n\t\t\t{\n\t\t\t\tif (s->ents[i] == e)\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (i < s->numents)\n\t\t\t\tcontinue;\n\n\t\t\t//FIXME: while technically correct, this causes issues with the v1 format.\n\t\t\ts->flags |= TSF_EDITED;\n\n\t\t\t//FIXME: race condition - main thread might be walking the entity list.\n\t\t\t//FIXME: even worse: the editor might be running through this routine adding/removing entities at the same time as the loader.\n\t\t\tif (s->maxents == s->numents)\n\t\t\t{\n\t\t\t\ts->maxents++;\n\t\t\t\ts->ents = realloc(s->ents, sizeof(*s->ents)*(s->maxents));\n\t\t\t}\n\t\t\ts->ents[s->numents++] = e;\n\t\t\te->refs++;\n\t\t}\n\t}\n\tSys_UnlockMutex(hm->entitylock);\n#endif\n}\n\nstatic void *Terr_ReadV1(heightmap_t *hm, hmsection_t *s, void *ptr, int len)\n{\n#ifdef HAVE_CLIENT\n\tdsmesh_v1_t *dm;\n\tfloat *colours;\n\tqbyte *lmstart;\n#endif\n\tdsection_v1_t *ds = ptr;\n\tint i;\n\n\tunsigned int flags = LittleLong(ds->flags);\n\ts->flags |= flags & ~(TSF_INTERNAL|TSF_HASWATER_V0);\n\tfor (i = 0; i < SECTHEIGHTSIZE*SECTHEIGHTSIZE; i++)\n\t{\n\t\ts->heights[i] = LittleFloat(ds->heights[i]);\n\t}\n\ts->minh = ds->minh;\n\ts->maxh = ds->maxh;\n\tif (flags & TSF_HASWATER_V0)\n\t\tTerr_GenerateWater(s, ds->waterheight);\n\n\tmemset(s->holes, 0, sizeof(s->holes));\n\tfor (i = 0; i < 8*8; i++)\n\t{\n\t\tint x = (i & 7);\n\t\tint y = (i>>3);\n\t\tint b = (1u<<(x>>1)) << ((y>>1)<<2);\n\t\tif (ds->holes & b)\n\t\t\ts->holes[y] |= 1u<<x;\n\t}\n\n\tptr = ds+1;\n\n#ifdef HAVE_CLIENT\n\t/*deal with textures*/\n\tQ_strncpyz(s->texname[0], ds->texname[0], sizeof(s->texname[0]));\n\tQ_strncpyz(s->texname[1], ds->texname[1], sizeof(s->texname[1]));\n\tQ_strncpyz(s->texname[2], ds->texname[2], sizeof(s->texname[2]));\n\tQ_strncpyz(s->texname[3], ds->texname[3], sizeof(s->texname[3]));\n\n\t/*load in the mixture/lighting*/\n\tlmstart = BZ_Malloc(SECTTEXSIZE*SECTTEXSIZE*4);\n\tmemcpy(lmstart, ds->texmap, SECTTEXSIZE*SECTTEXSIZE*4);\n\tCOM_AddWork(WG_MAIN, Terr_WorkerLoadedSectionLightmap, hm, lmstart, s->sx, s->sy);\n\n\ts->mesh.colors4f_array[0] = s->colours;\n\tif (flags & TSF_HASCOLOURS)\n\t{\n\t\tfor (i = 0, colours = (float*)ptr; i < SECTHEIGHTSIZE*SECTHEIGHTSIZE; i++, colours+=4)\n\t\t{\n\t\t\ts->colours[i][0] = LittleFloat(colours[0]);\n\t\t\ts->colours[i][1] = LittleFloat(colours[1]);\n\t\t\ts->colours[i][2] = LittleFloat(colours[2]);\n\t\t\ts->colours[i][3] = LittleFloat(colours[3]);\n\t\t}\n\t\tptr = colours;\n\t}\n\telse\n\t{\n\t\tfor (i = 0; i < SECTHEIGHTSIZE*SECTHEIGHTSIZE; i++)\n\t\t{\n\t\t\ts->colours[i][0] = 1;\n\t\t\ts->colours[i][1] = 1;\n\t\t\ts->colours[i][2] = 1;\n\t\t\ts->colours[i][3] = 1;\n\t\t}\n\t}\n\n\t/*load any static ents*/\n\tfor (i = 0, dm = (dsmesh_v1_t*)ptr; i < ds->ents_num; i++, dm = (dsmesh_v1_t*)((qbyte*)dm + dm->size))\n\t{\n\t\tvec3_t org;\n\t\torg[0] = dm->axisorg[3][0] + (s->sx-CHUNKBIAS)*hm->sectionsize;\n\t\torg[1] = dm->axisorg[3][1] + (s->sy-CHUNKBIAS)*hm->sectionsize;\n\t\torg[2] = dm->axisorg[3][2];\n\t\tTerr_AddMesh(hm, TGS_NOLOAD, NULL, (char*)(dm + 1), org, dm->axisorg, dm->scale); \n\t}\n#endif\n\treturn ptr;\n}\n\n\n\n\nstruct terrstream_s\n{\n\tqbyte *buffer;\n\tint maxsize;\n\tint pos;\n};\n//I really hope these get inlined properly.\nstatic int Terr_Read_SInt(struct terrstream_s *strm)\n{\n\tint val;\n\tstrm->pos = (strm->pos + sizeof(val)-1) & ~(sizeof(val)-1);\n\tval = *(int*)(strm->buffer+strm->pos);\n\tstrm->pos += sizeof(val);\n\treturn LittleLong(val);\n}\nstatic qbyte Terr_Read_Byte(struct terrstream_s *strm)\n{\n\tqbyte val;\n\tval = *(qbyte*)(strm->buffer+strm->pos);\n\tstrm->pos += sizeof(val);\n\treturn val;\n}\nstatic float Terr_Read_Float(struct terrstream_s *strm)\n{\n\tfloat val;\n\tstrm->pos = (strm->pos + sizeof(val)-1) & ~(sizeof(val)-1);\n\tval = *(float*)(strm->buffer+strm->pos);\n\tstrm->pos += sizeof(val);\n\treturn LittleFloat(val);\n}\nstatic char *Terr_Read_String(struct terrstream_s *strm, char *val, int maxlen)\n{\n\tint len = strlen(strm->buffer + strm->pos);\n\tmaxlen = min(len, maxlen-1);\t//truncate\n\tmemcpy(val, strm->buffer + strm->pos, maxlen);\n\tval[maxlen] = 0;\n\tstrm->pos += len+1;\n\treturn val;\n}\n#ifdef HAVE_CLIENT\nstatic void Terr_Write_SInt(struct terrstream_s *strm, int val)\n{\n\tval = LittleLong(val);\n\tstrm->pos = (strm->pos + sizeof(val)-1) & ~(sizeof(val)-1);\n\t*(int*)(strm->buffer+strm->pos) = val;\n\tstrm->pos += sizeof(val);\n}\nstatic void Terr_Write_Byte(struct terrstream_s *strm, qbyte val)\n{\n\t*(qbyte*)(strm->buffer+strm->pos) = val;\n\tstrm->pos += sizeof(val);\n}\nstatic void Terr_Write_Float(struct terrstream_s *strm, float val)\n{\n\tval = LittleFloat(val);\n\tstrm->pos = (strm->pos + sizeof(val)-1) & ~(sizeof(val)-1);\n\t*(float*)(strm->buffer+strm->pos) = val;\n\tstrm->pos += sizeof(val);\n}\nstatic void Terr_Write_String(struct terrstream_s *strm, char *val)\n{\n\tint len = strlen(val)+1;\n\tmemcpy(strm->buffer + strm->pos, val, len);\n\tstrm->pos += len;\n}\nstatic void Terr_TrimWater(hmsection_t *s)\n{\n\tint i;\n\tstruct hmwater_s *w, **link;\n\n\tfor (link = &s->water; (w = *link); )\n\t{\n\t\t//one has a height above the terrain?\n\t\tfor (i = 0; i < 9*9; i++)\n\t\t\tif (w->heights[i] > s->minh)\n\t\t\t\tbreak;\n\t\tif (i == 9*9)\n\t\t{\n\t\t\t*link = w->next;\n\t\t\tZ_Free(w);\n\t\t\tcontinue;\n\t\t}\n\t\telse\n\t\t\tlink = &(*link)->next;\n\t}\n}\nstatic void Terr_SaveV2(heightmap_t *hm, hmsection_t *s, vfsfile_t *f, int sx, int sy)\n{\n\tqbyte buffer[65536], last, delta, *lm;\n\tstruct terrstream_s strm = {buffer, sizeof(buffer), 0};\n\tunsigned int flags = s->flags;\n\tint i, j, x, y;\n\tstruct hmwater_s *w;\n\tunsigned int pixbytes;\n\n\tflags &= ~(TSF_INTERNAL);\n\tflags &= ~(TSF_HASCOLOURS|TSF_HASHEIGHTS|TSF_HASSHADOW);\n\n\tfor (i = 0; i < SECTHEIGHTSIZE*SECTHEIGHTSIZE; i++)\n\t{\n\t\tif (s->colours[i][0] != 1 || s->colours[i][1] != 1 || s->colours[i][2] != 1 || s->colours[i][3] != 1)\n\t\t{\n\t\t\tflags |= TSF_HASCOLOURS;\n\t\t\tbreak;\n\t\t}\n\t}\n\tfor (i = 0; i < SECTHEIGHTSIZE*SECTHEIGHTSIZE; i++)\n\t{\n\t\tif (s->heights[i] != s->heights[0])\n\t\t{\n\t\t\tflags |= TSF_HASHEIGHTS;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tpixbytes = lightmap[s->lightmap]->pixbytes;\n\tlm = lightmap[s->lightmap]->lightmaps;\n\tlm += (s->lmy * HMLMSTRIDE + s->lmx) * pixbytes;\n\tfor (y = 0; y < SECTTEXSIZE; y++)\n\t{\n\t\tfor (x = 0; x < SECTTEXSIZE; x++)\n\t\t{\n\t\t\tif (lm[x*4+3] != 255)\n\t\t\t{\n\t\t\t\tflags |= TSF_HASSHADOW;\n\t\t\t\ty = SECTTEXSIZE;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tlm += (HMLMSTRIDE)*pixbytes;\n\t}\n\n\t//write the flags so the loader knows what to load\n\tTerr_Write_SInt(&strm, flags);\n\n\t//if heights are compressed, only the first is present.\n\tif (!(flags & TSF_HASHEIGHTS))\n\t\tTerr_Write_Float(&strm, s->heights[0]);\n\telse\n\t{\n\t\tfor (i = 0; i < SECTHEIGHTSIZE*SECTHEIGHTSIZE; i++)\n\t\t\tTerr_Write_Float(&strm, s->heights[i]);\n\t}\n\n\tfor (i = 0; i < sizeof(s->holes); i++)\n\t\tTerr_Write_Byte(&strm, s->holes[i]);\n\n\tTerr_TrimWater(s);\n\tfor (j = 0, w = s->water; w; j++)\n\t\tw = w->next;\n\tTerr_Write_SInt(&strm, j);\n\tfor (i = 0, w = s->water; i < j; i++, w = w->next)\n\t{\n\t\tchar *shadername = w->shader->name;\n\t\tint fl = 0;\n\n\t\tif (strcmp(shadername, hm->defaultwatershader))\n\t\t\tfl |= 1;\n\t\tfor (x = 0; x < 8; x++)\n\t\t\tif (w->holes[x])\n\t\t\t\tbreak;\n\t\tfl |= ((x==8)?0:2);\n\t\tfor (x = 0; x < 9*9; x++)\n\t\t\tif (w->heights[x] != w->heights[0])\n\t\t\t\tbreak;\n\t\tfl |= ((x==9*9)?0:4);\n\n\t\t\n\t\tTerr_Write_SInt(&strm, fl);\n\t\tTerr_Write_SInt(&strm, w->contentmask);\n\t\tif (fl & 1)\n\t\t\tTerr_Write_String(&strm, shadername);\n\t\tif (fl & 2)\n\t\t{\n\t\t\tfor (x = 0; x < 8; x++)\n\t\t\t\tTerr_Write_Byte(&strm, w->holes[x]);\n\t\t}\n\t\tif (fl & 4)\n\t\t{\n\t\t\tfor (x = 0; x < 9*9; x++)\n\t\t\t\tTerr_Write_Float(&strm, w->heights[x]);\n\t\t}\n\t\telse\n\t\t\tTerr_Write_Float(&strm, w->heights[0]);\n\t}\n\n\tif (flags & TSF_HASCOLOURS)\n\t{\n\t\t//FIXME: bytes? channels?\n\t\tfor (i = 0; i < SECTHEIGHTSIZE*SECTHEIGHTSIZE; i++)\n\t\t{\n\t\t\tTerr_Write_Float(&strm, s->colours[i][0]);\n\t\t\tTerr_Write_Float(&strm, s->colours[i][1]);\n\t\t\tTerr_Write_Float(&strm, s->colours[i][2]);\n\t\t\tTerr_Write_Float(&strm, s->colours[i][3]);\n\t\t}\n\t}\n\n\tfor (j = 0; j < 4; j++)\n\t\tTerr_Write_String(&strm, s->texname[j]);\n\tfor (j = 0; j < 4; j++)\n\t{\n\t\tif (j == 3)\n\t\t{\n\t\t\t//only write the channel if it has actual data\n\t\t\tif (!(flags & TSF_HASSHADOW))\n\t\t\t\tcontinue;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t//only write the data if there's actually a texture.\n\t\t\t//its not meant to be possible to delete a texture without deleting its data too.\n\t\t\t//\n\t\t\tif (!*s->texname[2-j])\n\t\t\t\tcontinue;\n\t\t}\n\n\t\t//write the channel\n\t\tlast = 0;\n\t\tpixbytes = lightmap[s->lightmap]->pixbytes;\n\t\tlm = lightmap[s->lightmap]->lightmaps;\n\t\tlm += (s->lmy * HMLMSTRIDE + s->lmx) * pixbytes;\n\t\tfor (y = 0; y < SECTTEXSIZE; y++)\n\t\t{\n\t\t\tfor (x = 0; x < SECTTEXSIZE; x++)\n\t\t\t{\n\t\t\t\tdelta = lm[x*4+j] - last;\n\t\t\t\tlast = lm[x*4+j];\n\t\t\t\tTerr_Write_Byte(&strm, delta);\n\t\t\t}\n\t\t\tlm += (HMLMSTRIDE)*pixbytes;\n\t\t}\n\t}\n\n\tSys_LockMutex(hm->entitylock);\n\tTerr_Write_SInt(&strm, s->numents);\n\tfor (i = 0; i < s->numents; i++)\n\t{\n\t\tunsigned int mf;\n\n\t\t//make sure we don't overflow. we should always be aligned at this point.\n\t\tif (strm.pos > strm.maxsize/2)\n\t\t{\n\t\t\tVFS_WRITE(f, strm.buffer, strm.pos);\n\t\t\tstrm.pos = 0;\n\t\t}\n\n\t\tmf = 0;\n\t\tif (s->ents[i]->ent.scale != 1)\n\t\t\tmf |= TMF_SCALE;\n\t\tTerr_Write_SInt(&strm, mf);\n\t\tif (s->ents[i]->ent.model)\n\t\t\tTerr_Write_String(&strm, s->ents[i]->ent.model->name);\n\t\telse\n\t\t\tTerr_Write_String(&strm, \"*invalid\");\n\t\tTerr_Write_Float(&strm, s->ents[i]->ent.origin[0]+(CHUNKBIAS-sx)*hm->sectionsize);\n\t\tTerr_Write_Float(&strm, s->ents[i]->ent.origin[1]+(CHUNKBIAS-sy)*hm->sectionsize);\n\t\tTerr_Write_Float(&strm, s->ents[i]->ent.origin[2]);\n\t\tTerr_Write_Float(&strm, s->ents[i]->ent.axis[0][0]);\n\t\tTerr_Write_Float(&strm, s->ents[i]->ent.axis[0][1]);\n\t\tTerr_Write_Float(&strm, s->ents[i]->ent.axis[0][2]);\n\t\tTerr_Write_Float(&strm, s->ents[i]->ent.axis[1][0]);\n\t\tTerr_Write_Float(&strm, s->ents[i]->ent.axis[1][1]);\n\t\tTerr_Write_Float(&strm, s->ents[i]->ent.axis[1][2]);\n\t\tTerr_Write_Float(&strm, s->ents[i]->ent.axis[2][0]);\n\t\tTerr_Write_Float(&strm, s->ents[i]->ent.axis[2][1]);\n\t\tTerr_Write_Float(&strm, s->ents[i]->ent.axis[2][2]);\n\t\tif (mf & TMF_SCALE)\n\t\t\tTerr_Write_Float(&strm, s->ents[i]->ent.scale);\n\t}\n\tSys_UnlockMutex(hm->entitylock);\n\n\t//reset it in case the buffer is getting a little full\n\tstrm.pos = (strm.pos + sizeof(int)-1) & ~(sizeof(int)-1);\n\tVFS_WRITE(f, strm.buffer, strm.pos);\n\tstrm.pos = 0;\n}\n#ifdef HAVE_CLIENT\nstatic void Terr_WorkerLoadedSectionLightmap(void *ctx, void *data, size_t a, size_t b)\n{\n\theightmap_t *hm = ctx;\n\thmsection_t *s = Terr_GetSection(hm, a, b, TGS_NOLOAD|TGS_ANYSTATE);\n\tqbyte *inlm = data;\n\tqbyte *outlm;\n\tint y;\n\n\tif (s)\n\tif (Terr_InitLightmap(s, false))\n\t{\n\t\tint pixbytes = lightmap[s->lightmap]->pixbytes;\n\t\toutlm = lightmap[s->lightmap]->lightmaps;\n\t\toutlm += (s->lmy * HMLMSTRIDE + s->lmx) * pixbytes;\n\t\tfor (y = 0; y < SECTTEXSIZE; y++)\n\t\t{\n\t\t\tmemcpy(outlm, inlm, SECTTEXSIZE*4);\n\t\t\tinlm += SECTTEXSIZE*4;\n\t\t\toutlm += (HMLMSTRIDE)*pixbytes;\n\t\t}\n\t}\n\n\tBZ_Free(data);\n}\n#endif\n#endif\nstatic void *Terr_ReadV2(heightmap_t *hm, hmsection_t *s, void *ptr, int len)\n{\n#ifdef HAVE_CLIENT\n\tchar modelname[MAX_QPATH];\n\tqbyte last;\n\tint y;\n\tqboolean present;\n\tqbyte *lmstart = NULL, *lm, delta;\n#endif\n\tstruct terrstream_s strm = {ptr, len, 0};\n\tfloat f;\n\tint i, j, x;\n\tunsigned int flags = Terr_Read_SInt(&strm);\n\n\ts->flags |= flags & ~TSF_INTERNAL;\n\tif (flags & TSF_HASHEIGHTS)\n\t{\n\t\ts->minh = s->maxh = s->heights[0] = Terr_Read_Float(&strm);\n\t\tfor (i = 1; i < SECTHEIGHTSIZE*SECTHEIGHTSIZE; i++)\n\t\t{\n\t\t\tf = Terr_Read_Float(&strm);\n\t\t\tif (s->minh > f)\n\t\t\t\ts->minh = f;\n\t\t\tif (s->maxh < f)\n\t\t\t\ts->maxh = f;\n\t\t\ts->heights[i] = f;\n\t\t}\n\t}\n\telse\n\t{\n\t\ts->minh = s->maxh = f = Terr_Read_Float(&strm);\n\t\tfor (i = 0; i < SECTHEIGHTSIZE*SECTHEIGHTSIZE; i++)\n\t\t\ts->heights[i] = f;\n\t}\n\n\tfor (i = 0; i < sizeof(s->holes); i++)\n\t\ts->holes[i] = Terr_Read_Byte(&strm);\n\n\tj = Terr_Read_SInt(&strm);\n\tfor (i = 0; i < j; i++)\n\t{\n\t\tstruct hmwater_s *w = Z_Malloc(sizeof(*w));\n\t\tint fl = Terr_Read_SInt(&strm);\n\t\tw->next = s->water;\n\t\ts->water = w;\n\t\tw->simple = true;\n\t\tw->contentmask = Terr_Read_SInt(&strm);\n\t\tif (fl & 1)\n\t\t\tTerr_Read_String(&strm, w->shadername, sizeof(w->shadername));\n\t\telse\n\t\t\tQ_strncpyz(w->shadername, hm->defaultwatershader, sizeof(w->shadername));\n\t\tif (fl & 2)\n\t\t{\n\t\t\tfor (x = 0; x < 8; x++)\n\t\t\t\tw->holes[i] = Terr_Read_Byte(&strm);\n\t\t\tw->simple = false;\n\t\t}\n\t\tif (fl & 4)\n\t\t{\n\t\t\tfor (x = 0; x < 9*9; x++)\n\t\t\t{\n\t\t\t\tw->heights[x] = Terr_Read_Float(&strm);\n\t\t\t}\n\t\t\tw->simple = false;\n\t\t}\n\t\telse\n\t\t{\t//all heights the same can be used as a way to compress the data\n\t\t\tw->minheight = w->maxheight = Terr_Read_Float(&strm);\n\t\t\tfor (x = 0; x < 9*9; x++)\n\t\t\t\tw->heights[x] = w->minheight = w->maxheight;\n\t\t}\n\t}\n\n\t//dedicated server can stop reading here.\n\n#ifdef HAVE_CLIENT\n\tif (flags & TSF_HASCOLOURS)\n\t{\n\t\tfor (i = 0; i < SECTHEIGHTSIZE*SECTHEIGHTSIZE; i++)\n\t\t{\n\t\t\ts->colours[i][0] = Terr_Read_Float(&strm);\n\t\t\ts->colours[i][1] = Terr_Read_Float(&strm);\n\t\t\ts->colours[i][2] = Terr_Read_Float(&strm);\n\t\t\ts->colours[i][3] = Terr_Read_Float(&strm);\n\t\t}\n\t}\n\telse\n\t{\n\t\tfor (i = 0; i < SECTHEIGHTSIZE*SECTHEIGHTSIZE; i++)\n\t\t{\n\t\t\ts->colours[i][0] = 1;\n\t\t\ts->colours[i][1] = 1;\n\t\t\ts->colours[i][2] = 1;\n\t\t\ts->colours[i][3] = 1;\n\t\t}\n\t}\n\n\tfor (j = 0; j < 4; j++)\n\t\tTerr_Read_String(&strm, s->texname[j], sizeof(s->texname[j]));\n\tfor (j = 0; j < 4; j++)\n\t{\n\t\tif (j == 3)\n\t\t\tpresent = !!(flags & TSF_HASSHADOW);\n\t\telse\n\t\t\tpresent = !!(*s->texname[2-j]);\n\n\t\t//should be able to skip this if no shadows or textures\n\t\tif (!lmstart)\n\t\t\tlmstart = BZ_Malloc(SECTTEXSIZE*SECTTEXSIZE*4);\n\n\t\tif (present)\n\t\t{\n\t\t\t//read the channel\n\t\t\tlast = 0;\n\t\t\tlm = lmstart;\n\t\t\tfor (y = 0; y < SECTTEXSIZE; y++)\n\t\t\t{\n\t\t\t\tfor (x = 0; x < SECTTEXSIZE; x++)\n\t\t\t\t{\n\t\t\t\t\tdelta = Terr_Read_Byte(&strm);\n\t\t\t\t\tlast = (last+delta)&0xff;\n\t\t\t\t\tlm[x*4+j] = last;\n\t\t\t\t}\n\t\t\t\tlm += x*4;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tlast = ((j==3)?255:0);\n\t\t\tlm = lmstart;\n\t\t\tfor (y = 0; y < SECTTEXSIZE; y++)\n\t\t\t{\n\t\t\t\tfor (x = 0; x < SECTTEXSIZE; x++)\n\t\t\t\t\tlm[x*4+j] = last;\n\t\t\t\tlm += x*4;\n\t\t\t}\n\t\t}\n\t}\n\n\tif (lmstart)\n\t\tCOM_AddWork(WG_MAIN, Terr_WorkerLoadedSectionLightmap, hm, lmstart, s->sx, s->sy);\n\n\t/*load any static ents*/\n\tj = Terr_Read_SInt(&strm);\n\tfor (i = 0; i < j; i++)\n\t{\n\t\tvec3_t axis[3];\n\t\tvec3_t org;\n\t\tunsigned int mf;\n\t\tmodel_t *mod;\n\t\tfloat scale;\n\t\tmf = Terr_Read_SInt(&strm);\n\n\t\tmod = Mod_FindName(Terr_Read_String(&strm, modelname, sizeof(modelname)));\n\t\torg[0] = Terr_Read_Float(&strm);\n\t\torg[1] = Terr_Read_Float(&strm);\n\t\torg[2] = Terr_Read_Float(&strm);\n\t\taxis[0][0] = Terr_Read_Float(&strm);\n\t\taxis[0][1] = Terr_Read_Float(&strm);\n\t\taxis[0][2] = Terr_Read_Float(&strm);\n\t\taxis[1][0] = Terr_Read_Float(&strm);\n\t\taxis[1][1] = Terr_Read_Float(&strm);\n\t\taxis[1][2] = Terr_Read_Float(&strm);\n\t\taxis[2][0] = Terr_Read_Float(&strm);\n\t\taxis[2][1] = Terr_Read_Float(&strm);\n\t\taxis[2][2] = Terr_Read_Float(&strm);\n\t\tscale = (mf&TMF_SCALE)?Terr_Read_Float(&strm):1;\n\n\t\torg[0] += (s->sx-CHUNKBIAS)*hm->sectionsize;\n\t\torg[1] += (s->sy-CHUNKBIAS)*hm->sectionsize;\n\n\t\tTerr_AddMesh(hm, TGS_NOLOAD, mod, NULL, org, axis, scale);\n\t}\n#endif\n\treturn ptr;\n}\n\nstatic void Terr_ClearSection(hmsection_t *s)\n{\n\tstruct hmwater_s *w;\n\tint i;\n\tSys_LockMutex(s->hmmod->entitylock);\n\tfor (i = 0; i < s->numents; i++)\n\t\ts->ents[i]->refs-=1;\n\ts->numents = 0;\n\tSys_UnlockMutex(s->hmmod->entitylock);\n\n\twhile(s->water)\n\t{\n\t\tw = s->water;\n\t\ts->water = w->next;\n\t\tZ_Free(w);\n\t}\n}\n\nstatic void Terr_GenerateDefault(heightmap_t *hm, hmsection_t *s)\n{\n\tint i;\n\n\tmemset(s->holes, 0, sizeof(s->holes));\n\n#ifdef HAVE_CLIENT\n\tQ_strncpyz(s->texname[0], \"\", sizeof(s->texname[0]));\n\tQ_strncpyz(s->texname[1], \"\", sizeof(s->texname[1]));\n\tQ_strncpyz(s->texname[2], \"\", sizeof(s->texname[2]));\n\tQ_strncpyz(s->texname[3], hm->defaultgroundtexture, sizeof(s->texname[3]));\n\n\tif (s->lightmap >= 0)\n\t{\n\t\tint j;\n\t\tqbyte *lm = lightmap[s->lightmap]->lightmaps;\n\t\tint pixbytes = lightmap[s->lightmap]->pixbytes;\n\t\tlm += (s->lmy * HMLMSTRIDE + s->lmx) * pixbytes;\n\t\tfor (i = 0; i < SECTTEXSIZE; i++)\n\t\t{\n\t\t\tfor (j = 0; j < SECTTEXSIZE; j++)\n\t\t\t{\n\t\t\t\tlm[j*4+0] = 0;\n\t\t\t\tlm[j*4+0] = 0;\n\t\t\t\tlm[j*4+0] = 0;\n\t\t\t\tlm[j*4+3] = 255;\n\t\t\t}\n\t\t\tlm += (HMLMSTRIDE)*pixbytes;\n\t\t}\n\t\tlightmap[s->lightmap]->modified = true;\n\t\tlightmap[s->lightmap]->rectchange.l = 0;\n\t\tlightmap[s->lightmap]->rectchange.t = 0;\n\t\tlightmap[s->lightmap]->rectchange.r = HMLMSTRIDE;\n\t\tlightmap[s->lightmap]->rectchange.b = HMLMSTRIDE;\n\t}\n\tfor (i = 0; i < SECTHEIGHTSIZE*SECTHEIGHTSIZE; i++)\n\t{\n\t\ts->colours[i][0] = 1;\n\t\ts->colours[i][1] = 1;\n\t\ts->colours[i][2] = 1;\n\t\ts->colours[i][3] = 1;\n\t}\n\ts->mesh.colors4f_array[0] = s->colours;\n#endif\n\n\tfor (i = 0; i < SECTHEIGHTSIZE*SECTHEIGHTSIZE; i++)\n\t\ts->heights[i] = hm->defaultgroundheight;\n\n\tif (hm->defaultwaterheight > hm->defaultgroundheight)\n\t\tTerr_GenerateWater(s, hm->defaultwaterheight);\n\n#if 0//def DEBUG\n\tvoid *f;\n\tif (lightmap_bytes == 4 && lightmap_bgra && FS_LoadFile(va(\"maps/%s/splatt.png\", hm->path), &f) != (qofs_t)-1)\n\t{\n\t\t//temp\n\t\tint vx, vy;\n\t\tint x, y;\n\t\textern qbyte *Read32BitImageFile(qbyte *buf, int len, int *width, int *height, qboolean *hasalpha, const char *fname);\n\t\tint sw, sh;\n\t\tqboolean hasalpha;\n\t\tunsigned char *splatter = Read32BitImageFile(f, com_filesize, &sw, &sh, &hasalpha, \"splattermap\");\n\t\tif (splatter)\n\t\t{\n\t\t\tlm = lightmap[s->lightmap]->lightmaps;\n\t\t\tlm += (s->lmy * HMLMSTRIDE + s->lmx) * lightmap_bytes;\n\n\t\t\tfor (vx = 0; vx < SECTTEXSIZE; vx++)\n\t\t\t{\n\t\t\t\tx = sw * (((float)sy) + ((float)vx / (SECTTEXSIZE-1))) / hm->numsegsx;\n\t\t\t\tif (x > sw-1)\n\t\t\t\t\tx = sw-1;\n\t\t\t\tfor (vy = 0; vy < SECTTEXSIZE; vy++)\n\t\t\t\t{\n\t\t\t\t\ty = sh * (((float)sx) + ((float)vy / (SECTTEXSIZE-1))) / hm->numsegsy;\n\t\t\t\t\tif (y > sh-1)\n\t\t\t\t\t\ty = sh-1;\n\n\t\t\t\t\tlm[2] = splatter[(y + x*sh)*4+0];\n\t\t\t\t\tlm[1] = splatter[(y + x*sh)*4+1];\n\t\t\t\t\tlm[0] = splatter[(y + x*sh)*4+2];\n\t\t\t\t\tlm[3] = splatter[(y + x*sh)*4+3];\n\t\t\t\t\tlm += 4;\n\t\t\t\t}\n\t\t\t\tlm += (HMLMSTRIDE - SECTTEXSIZE)*lightmap_bytes;\n\t\t\t}\n\t\t\tBZ_Free(splatter);\n\n\t\t\tlightmap[s->lightmap]->modified = true;\n\t\t\tlightmap[s->lightmap]->rectchange.l = 0;\n\t\t\tlightmap[s->lightmap]->rectchange.t = 0;\n\t\t\tlightmap[s->lightmap]->rectchange.w = HMLMSTRIDE;\n\t\t\tlightmap[s->lightmap]->rectchange.h = HMLMSTRIDE;\n\t\t}\n\t\tFS_FreeFile(f);\n\t}\n\n\tif (lightmap_bytes == 4 && lightmap_bgra && !qofs_Error(FS_LoadFile(va(\"maps/%s/heightmap.png\", hm->path), &f)))\n\t{\n\t\t//temp\n\t\tint vx, vy;\n\t\tint x, y;\n\t\textern qbyte *Read32BitImageFile(qbyte *buf, int len, int *width, int *height, qboolean *hasalpha, const char *fname);\n\t\tint sw, sh;\n\t\tfloat *h;\n\t\tqboolean hasalpha;\n\t\tunsigned char *hmimage = Read32BitImageFile(f, com_filesize, &sw, &sh, &hasalpha, \"heightmap\");\n\t\tif (hmimage)\n\t\t{\n\t\t\th = s->heights;\n\n\t\t\tfor (vx = 0; vx < SECTHEIGHTSIZE; vx++)\n\t\t\t{\n\t\t\t\tx = sw * (((float)sy) + ((float)vx / (SECTHEIGHTSIZE-1))) / hm->numsegsx;\n\t\t\t\tif (x > sw-1)\n\t\t\t\t\tx = sw-1;\n\t\t\t\tfor (vy = 0; vy < SECTHEIGHTSIZE; vy++)\n\t\t\t\t{\n\t\t\t\t\ty = sh * (((float)sx) + ((float)vy / (SECTHEIGHTSIZE-1))) / hm->numsegsy;\n\t\t\t\t\tif (y > sh-1)\n\t\t\t\t\t\ty = sh-1;\n\n\t\t\t\t\t*h = 0;\n\t\t\t\t\t*h += hmimage[(y + x*sh)*4+0];\n\t\t\t\t\t*h += hmimage[(y + x*sh)*4+1]<<8;\n\t\t\t\t\t*h += hmimage[(y + x*sh)*4+2]<<16;\n\t\t\t\t\t*h *= 4.0f/(1<<16);\n\t\t\t\t\th++;\n\t\t\t\t}\n\t\t\t}\n\t\t\tBZ_Free(hmimage);\n\t\t}\n\t\tFS_FreeFile(f);\n\t}\n#endif\n}\n\nstatic void Terr_WorkerLoadedSection(void *ctx, void *data, size_t a, size_t b)\n{\n\thmsection_t *s = ctx;\n\tvalidatelinks(&s->hmmod->recycle);\n\n\tTerr_LoadSectionTextures(s);\n\tvalidatelinks2(&s->hmmod->recycle, &s->recycle);\n\tInsertLinkBefore(&s->recycle, &s->hmmod->recycle);\n\tvalidatelinks(&s->hmmod->recycle);\n\ts->hmmod->loadingsections-=1;\n\ts->flags &= ~TSF_EDITED;\n\ts->loadstate = TSLS_LOADED;\n\ts->timestamp = realtime;\n\n\tvalidatelinks(&s->hmmod->recycle);\n}\nstatic void Terr_WorkerFailedSection(void *ctx, void *data, size_t a, size_t b)\n{\n\thmsection_t *s = ctx;\n\tTerr_WorkerLoadedSection(ctx, data, a, b);\n\ts->flags &= ~TSF_EDITED;\n\ts->loadstate = TSLS_FAILED;\n\n\tvalidatelinks(&s->hmmod->recycle);\n}\n\nvoid QDECL Terr_FinishedSection(hmsection_t *s, qboolean success)\n{\n\ts->flags &= ~TSF_EDITED;\t//its just been loaded (and was probably edited by the loader), make sure it doesn't get saved or whatever\n\n\ts->loadstate = TSLS_LOADING2;\n\tif (!success)\n\t\tCOM_AddWork(WG_MAIN, Terr_WorkerFailedSection, s, NULL, s->sx, s->sy);\n\telse\n\t\tCOM_AddWork(WG_MAIN, Terr_WorkerLoadedSection, s, NULL, s->sx, s->sy);\n}\n\nstatic hmsection_t *Terr_ReadSection(heightmap_t *hm, hmsection_t *s, int ver, void *filebase, unsigned int filelen)\n{\n\tqboolean failed = false;\n\tvoid *ptr = filebase;\n\n\tif (ptr && ver == 1)\n\t\tTerr_ReadV1(hm, s, ptr, filelen);\n\telse if (ptr && ver == 2)\n\t\tTerr_ReadV2(hm, s, ptr, filelen);\n\telse\n\t{\n//\t\ts->flags |= TSF_RELIGHT;\n\t\tTerr_GenerateDefault(hm, s);\n\n\t\tfailed = true;\n\t}\n\n\tTerr_FinishedSection(s, !failed);\n\n\treturn s;\n}\n\n#ifdef HAVE_CLIENT\nqboolean Terr_DownloadedSection(char *fname)\n{\n/*\n\tqofs_t len;\n\tdsection_t *fileptr;\n\tint x, y;\n\theightmap_t *hm;\n\tint ver = 0;\n\n\tif (!cl.worldmodel)\n\t\treturn false;\n\n\thm = cl.worldmodel->terrain;\n\n\tif (Terr_IsSectionFName(hm, fname, &x, &y))\n\t{\n\t\tfileptr = NULL;\n\t\tlen = FS_LoadFile(fname, (void**)&fileptr);\n\n\t\tif (!qofs_Error(len) && len >= sizeof(*fileptr) && fileptr->magic == SECTION_MAGIC)\n\t\t\tTerr_ReadSection(hm, ver, x, y, fileptr+1, len - sizeof(*fileptr));\n\t\telse\n\t\t\tTerr_ReadSection(hm, ver, x, y, NULL, 0);\n\n\t\tif (fileptr)\n\t\t\tFS_FreeFile(fileptr);\n\t\treturn true;\n\t}\n*/\n\treturn false;\n}\n#endif\n\n#ifdef HAVE_CLIENT\nstatic void Terr_LoadSection(heightmap_t *hm, hmsection_t *s, int sx, int sy, unsigned int flags)\n{\n\t//when using networked terrain, the client will never load a section from disk, but will only load it from the server\n\t//one section at a time.\n\tif (mod_terrain_networked.ival && !sv_state)\n\t{\n\t\tchar fname[MAX_QPATH];\n\t\tif (flags & TGS_NODOWNLOAD)\n\t\t\treturn;\n\t\t//try to download it now...\n\t\tif (!cl.downloadlist)\n\t\t\tCL_CheckOrEnqueDownloadFile(Terr_DiskSectionName(hm, sx, sy, fname, sizeof(fname)), Terr_TempDiskSectionName(hm, sx, sy), DLLF_OVERWRITE|DLLF_TEMPORARY);\n\t\treturn;\n\t}\n\n\tif (!s)\n\t{\n\t\tTerr_GenerateSection(hm, sx, sy, true);\n\t}\n}\n#endif\nstatic void Terr_LoadSectionWorker(void *ctx, void *data, size_t a, size_t b)\n{\n\theightmap_t *hm = data;\n\thmsection_t *s = ctx;\n\tint sx = a;\n\tint sy = b;\n\tvoid *diskimage;\n\tqofs_t len;\n\tchar fname[MAX_QPATH];\n\n\t//already processed, or not otherwise valid\n\tif (s->loadstate != TSLS_LOADING0)\n\t\treturn;\n\n#if SECTIONSPERBLOCK > 1\n\tlen = FS_LoadFile(Terr_DiskBlockName(hm, sx, sy, fname, sizeof(fname)), (void**)&diskimage);\n\tif (!qofs_Error(len))\n\t{\n\t\tint offset;\n\t\tint x, y;\n\t\tint ver;\n\t\tdblock_t *block = diskimage;\n\t\tif (block->magic != SECTION_MAGIC || !(block->ver & 0x80000000))\n\t\t{\n\t\t\ts = Terr_GenerateSection(hm, sx, sy, false);\n\n\t\t\t//give it a dummy so we don't constantly hit the disk\n\t\t\tTerr_ReadSection(hm, s, 0, NULL, 0);\n\t\t}\n\t\telse\n\t\t{\n\t\t\thmsection_t *sects[SECTIONSPERBLOCK*SECTIONSPERBLOCK];\n\n\t\t\tsx&=~(SECTIONSPERBLOCK-1);\n\t\t\tsy&=~(SECTIONSPERBLOCK-1);\n\n\t\t\tver = block->ver & ~0x80000000;\n\t\t\tif (Terr_GenerateSections(hm, sx, sy, SECTIONSPERBLOCK, sects))\n\t\t\t{\n\t\t\t\tfor (y = 0; y < SECTIONSPERBLOCK; y++)\n\t\t\t\t\tfor (x = 0; x < SECTIONSPERBLOCK; x++)\n\t\t\t\t\t{\n\t\t\t\t\t\t//noload avoids recursion.\n\t\t\t\t\t\ts = sects[x+y*SECTIONSPERBLOCK];\n\t\t\t\t\t\tif (s)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\toffset = block->offset[x + y*SECTIONSPERBLOCK];\n\t\t\t\t\t\t\tif (!offset)\n\t\t\t\t\t\t\t\tTerr_ReadSection(hm, s, ver, NULL, 0);\t//no data in the file for this section\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\tTerr_ReadSection(hm, s, ver, (char*)diskimage + offset, len - offset);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tFS_FreeFile(diskimage);\n\t\treturn;\n\t}\n#endif\n\n\t//legacy one-section-per-file format.\n\tlen = FS_LoadFile(Terr_DiskSectionName(hm, sx, sy, fname, sizeof(fname)), (void**)&diskimage);\n\tif (!qofs_Error(len))\n\t{\n\t\tdsection_t *h = diskimage;\n\t\tif (len >= sizeof(*h) && h->magic == SECTION_MAGIC)\n\t\t{\n\t\t\ts = Terr_GenerateSection(hm, sx, sy, false);\n\t\t\tif (!s)\n\t\t\t\treturn;\n\t\t\tTerr_ReadSection(hm, s, h->ver, h+1, len-sizeof(*h));\n\t\t\tFS_FreeFile(diskimage);\n\t\t\treturn;\n\t\t}\n\t\tif (diskimage)\n\t\t\tFS_FreeFile(diskimage);\n\t}\n\n\tif (terrainfuncs.AutogenerateSection && terrainfuncs.AutogenerateSection(hm, sx, sy, 0))\n\t\treturn;\n\n\ts = Terr_GenerateSection(hm, sx, sy, false);\n\tif (!s)\n\t\treturn;\n\n\t//generate a dummy one\n\tTerr_ReadSection(hm, s, 0, NULL, 0);\n}\n\n#ifdef HAVE_CLIENT\nstatic void Terr_SaveV1(heightmap_t *hm, hmsection_t *s, vfsfile_t *f, int sx, int sy)\n{\n\tint i;\n\tdsmesh_v1_t dm;\n\tqbyte *lm;\n\tdsection_v1_t ds;\n\tvec4_t dcolours[SECTHEIGHTSIZE*SECTHEIGHTSIZE];\n\tint nothing = 0;\n\tstruct hmwater_s *w = s->water;\n\tint pixbytes;\n\n\tmemset(&ds, 0, sizeof(ds));\n\tmemset(&dm, 0, sizeof(dm));\n\n\t//mask off the flags which are only valid in memory\n\tds.flags = s->flags & ~(TSF_INTERNAL|TSF_HASWATER_V0);\n\n\t//kill the haswater flag if its entirely above any possible water anyway.\n\tif (w)\n\t\tds.flags |= TSF_HASWATER_V0;\n\tds.flags &= ~TSF_HASCOLOURS;\t//recalculated\n\n\tQ_strncpyz(ds.texname[0], s->texname[0], sizeof(ds.texname[0]));\n\tQ_strncpyz(ds.texname[1], s->texname[1], sizeof(ds.texname[1]));\n\tQ_strncpyz(ds.texname[2], s->texname[2], sizeof(ds.texname[2]));\n\tQ_strncpyz(ds.texname[3], s->texname[3], sizeof(ds.texname[3]));\n\n\tfor (i = 0; i < 8*8; i++)\n\t{\n\t\tint x = (i & 7);\n\t\tint y = (i>>3);\n\t\tint b = (1u<<(x>>1)) << ((y>>1)<<2);\n\t\tif (s->holes[y] & (1u<<x))\n\t\t\tds.holes |= b;\n\t}\n\n\t//make sure the user can see the holes they just saved.\n\tmemset(s->holes, 0, sizeof(s->holes));\n\tfor (i = 0; i < 8*8; i++)\n\t{\n\t\tint x = (i & 7);\n\t\tint y = (i>>3);\n\t\tint b = (1u<<(x>>1)) << ((y>>1)<<2);\n\t\tif (ds.holes & b)\n\t\t\ts->holes[y] |= 1u<<x;\n\t}\n\ts->flags |= TSF_DIRTY;\n\n\tpixbytes = lightmap[s->lightmap]->pixbytes;\n\tlm = lightmap[s->lightmap]->lightmaps;\n\tlm += (s->lmy * HMLMSTRIDE + s->lmx) * pixbytes;\n\tfor (i = 0; i < SECTTEXSIZE; i++)\n\t{\n\t\tmemcpy(ds.texmap + i, lm, sizeof(ds.texmap[0]));\n\t\tlm += (HMLMSTRIDE)*pixbytes;\n\t}\n\n\tfor (i = 0; i < SECTHEIGHTSIZE*SECTHEIGHTSIZE; i++)\n\t{\n\t\tds.heights[i] = LittleFloat(s->heights[i]);\n\n\t\tif (s->colours[i][0] != 1 || s->colours[i][1] != 1 || s->colours[i][2] != 1 || s->colours[i][3] != 1)\n\t\t{\n\t\t\tds.flags |= TSF_HASCOLOURS;\n\t\t\tdcolours[i][0] = LittleFloat(s->colours[i][0]);\n\t\t\tdcolours[i][1] = LittleFloat(s->colours[i][1]);\n\t\t\tdcolours[i][2] = LittleFloat(s->colours[i][2]);\n\t\t\tdcolours[i][3] = LittleFloat(s->colours[i][3]);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tdcolours[i][0] = dcolours[i][1] = dcolours[i][2] = dcolours[i][3] = LittleFloat(1);\n\t\t}\n\t}\n\tds.waterheight = w?w->heights[4*8+4]:s->minh;\n\tds.minh = s->minh;\n\tds.maxh = s->maxh;\n\tSys_LockMutex(hm->entitylock);\n\tds.ents_num = s->numents;\n\n\tVFS_WRITE(f, &ds, sizeof(ds));\n\tif (ds.flags & TSF_HASCOLOURS)\n\t\tVFS_WRITE(f, dcolours, sizeof(dcolours));\n\tfor (i = 0; i < s->numents; i++)\n\t{\n\t\tint pad;\n\t\tdm.scale = s->ents[i]->ent.scale;\n\t\tVectorCopy(s->ents[i]->ent.axis[0], dm.axisorg[0]);\n\t\tVectorCopy(s->ents[i]->ent.axis[1], dm.axisorg[1]);\n\t\tVectorCopy(s->ents[i]->ent.axis[2], dm.axisorg[2]);\n\t\tVectorCopy(s->ents[i]->ent.origin, dm.axisorg[3]);\n\t\tdm.axisorg[3][0] += (CHUNKBIAS-sx)*hm->sectionsize;\n\t\tdm.axisorg[3][1] += (CHUNKBIAS-sy)*hm->sectionsize;\n\t\tdm.size = sizeof(dm) + strlen(s->ents[i]->ent.model->name) + 1;\n\t\tif (dm.size & 3)\n\t\t\tpad = 4 - (dm.size&3);\n\t\telse\n\t\t\tpad = 0;\n\t\tdm.size += pad;\n\t\tVFS_WRITE(f, &dm, sizeof(dm));\n\t\tVFS_WRITE(f, s->ents[i]->ent.model->name, strlen(s->ents[i]->ent.model->name)+1);\n\t\tif (pad)\n\t\t\tVFS_WRITE(f, &nothing, pad);\n\t}\n\tSys_UnlockMutex(hm->entitylock);\n}\n\nstatic void Terr_Save(heightmap_t *hm, hmsection_t *s, vfsfile_t *f, int sx, int sy, int ver)\n{\n\tif (ver == 1)\n\t\tTerr_SaveV1(hm, s, f, sx, sy);\n\telse if (ver == 2)\n\t\tTerr_SaveV2(hm, s, f, sx, sy);\n}\n#endif\n\n//doesn't clear edited/dirty flags or anything\nstatic qboolean Terr_SaveSection(heightmap_t *hm, hmsection_t *s, int sx, int sy, qboolean blocksave)\n{\n#ifndef HAVE_CLIENT\n\treturn true;\n#else\n\tvfsfile_t *f;\n\tchar fname[MAX_QPATH];\n\tint x, y;\n\tint writever = mod_terrain_savever.ival;\n\tif (!writever)\n\t\twritever = SECTION_VER_DEFAULT;\n\t//if its invalid or doesn't contain all the data...\n\tif (!s || s->lightmap < 0)\n\t\treturn true;\n\n#if SECTIONSPERBLOCK > 1\n\tif (blocksave)\n\t{\n\t\tdblock_t dbh;\n\t\tsx = sx & ~(SECTIONSPERBLOCK-1);\n\t\tsy = sy & ~(SECTIONSPERBLOCK-1);\n\n\t\t//make sure its loaded before we replace the file\n\t\tfor (y = 0; y < SECTIONSPERBLOCK; y++)\n\t\t{\n\t\t\tfor (x = 0; x < SECTIONSPERBLOCK; x++)\n\t\t\t{\n\t\t\t\ts = Terr_GetSection(hm, sx+x, sy+y, TGS_WAITLOAD|TGS_NODOWNLOAD);\n\t\t\t\tif (s)\n\t\t\t\t\ts->flags |= TSF_EDITED;\t//stop them from getting reused for something else.\n\t\t\t}\n\t\t}\n\n\t\t//make sure all lightmap info was loaded.\n\t\tCOM_WorkerFullSync();\n\n\t\tTerr_DiskBlockName(hm, sx, sy, fname, sizeof(fname));\n\t\tFS_CreatePath(fname, FS_GAMEONLY);\n\t\tf = FS_OpenVFS(fname, \"wb\", FS_GAMEONLY);\n\t\tif (!f)\n\t\t{\n\t\t\tCon_Printf(\"Failed to open %s\\n\", fname);\n\t\t\treturn false;\n\t\t}\n\n\t\tmemset(&dbh, 0, sizeof(dbh));\n\t\tdbh.magic = LittleLong(SECTION_MAGIC);\n\t\tdbh.ver = LittleLong(writever | 0x80000000);\n\t\tVFS_WRITE(f, &dbh, sizeof(dbh));\n\t\tfor (y = 0; y < SECTIONSPERBLOCK; y++)\n\t\t{\n\t\t\tfor (x = 0; x < SECTIONSPERBLOCK; x++)\n\t\t\t{\n\t\t\t\ts = Terr_GetSection(hm, sx+x, sy+y, TGS_WAITLOAD|TGS_NODOWNLOAD);\n\t\t\t\tif (s && s->loadstate == TSLS_LOADED && Terr_InitLightmap(s, false))\n\t\t\t\t{\n\t\t\t\t\tdbh.offset[y*SECTIONSPERBLOCK + x] = VFS_TELL(f);\n\t\t\t\t\tTerr_Save(hm, s, f, sx+x, sy+y, writever);\n\t\t\t\t\ts->flags &= ~TSF_EDITED;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tdbh.offset[y*SECTIONSPERBLOCK + x] = 0;\n\t\t\t}\n\t\t}\n\n\t\tVFS_SEEK(f, 0);\n\t\tVFS_WRITE(f, &dbh, sizeof(dbh));\n\t\tVFS_CLOSE(f);\n\t\tFS_FlushFSHashWritten(fname);\n\t}\n\telse\n#endif\n\t{\n\t\tdsection_t dsh;\n\t\tTerr_DiskSectionName(hm, sx, sy, fname, sizeof(fname));\n\n//\t\tif (s && (s->flags & (TSF_EDITED|TSF_FAILEDLOAD)) != TSF_FAILEDLOAD)\n//\t\t\treturn FS_Remove(fname, FS_GAMEONLY);\t//delete the file if the section got reverted to default, and wasn't later modified.\n\n\t\t//make sure all lightmap info was loaded.\n\t\tCOM_WorkerFullSync();\n\n\t\tFS_CreatePath(fname, FS_GAMEONLY);\n\t\tf = FS_OpenVFS(fname, \"wb\", FS_GAMEONLY);\n\t\tif (!f)\n\t\t{\n\t\t\tCon_Printf(\"Failed to open %s\\n\", fname);\n\t\t\treturn false;\n\t\t}\n\n\t\tmemset(&dsh, 0, sizeof(dsh));\n\t\tdsh.magic = SECTION_MAGIC;\n\t\tdsh.ver = writever;\n\t\tVFS_WRITE(f, &dsh, sizeof(dsh));\n\t\tTerr_Save(hm, s, f, sx, sy, writever);\n\t\tVFS_CLOSE(f);\n\t\tFS_FlushFSHashWritten(fname);\n\t}\n\treturn true;\n#endif\n}\n\n/*convienience function*/\nstatic hmsection_t *QDECL Terr_GetSection(heightmap_t *hm, int x, int y, unsigned int flags)\n{\n\thmcluster_t *cluster;\n\thmsection_t *section;\n\tint cx = x / MAXSECTIONS;\n\tint cy = y / MAXSECTIONS;\n\tint sx = x & (MAXSECTIONS-1);\n\tint sy = y & (MAXSECTIONS-1);\n\tcluster = hm->cluster[cx + cy*MAXCLUSTERS];\n\tif (!cluster)\n\t\tsection = NULL;\n\telse\n\t\tsection = cluster->section[sx + sy*MAXSECTIONS];\n\tif (!section)\n\t{\n\t\tif (flags & (TGS_LAZYLOAD|TGS_TRYLOAD|TGS_WAITLOAD))\n\t\t{\n\t\t\tif ((flags & TGS_LAZYLOAD) && hm->loadingsections)\n\t\t\t\treturn NULL;\n\t\t\tsection = Terr_GenerateSection(hm, x, y, true);\n\t\t}\n\t}\n#ifdef HAVE_CLIENT\n\t//when using networked terrain, the client will never load a section from disk, but only loading it from the server\n\t//this means we need to send a new request to download the section if it was flagged as modified.\n\tif (!(flags & TGS_NODOWNLOAD))\n\tif (section && (section->flags & TSF_NOTIFY) && mod_terrain_networked.ival && !sv_state)\n\t{\n\t\t//try to download it now...\n\t\tif (!cl.downloadlist)\n\t\t{\n\t\t\tchar fname[MAX_QPATH];\n\t\t\tCL_CheckOrEnqueDownloadFile(Terr_DiskSectionName(hm, x, y, fname, sizeof(fname)), Terr_TempDiskSectionName(hm, x, y), DLLF_OVERWRITE|DLLF_TEMPORARY);\n\n\t\t\tsection->flags &= ~TSF_NOTIFY;\n\t\t}\n\t}\n#endif\n\n\tif (section && section->loadstate != TSLS_LOADED)\n\t{\n\t\t//wait for it to load if we're meant to be doing that.\n\t\tif (flags & TGS_WAITLOAD)\n\t\t{\n\t\t\t//wait for it to load if we're meant to be doing that.\n\t\t\tif (section->loadstate == TSLS_LOADING0)\n\t\t\t\tCOM_WorkerPartialSync(section, &section->loadstate, TSLS_LOADING0);\n\t\t\tif (section->loadstate == TSLS_LOADING1)\n\t\t\t\tCOM_WorkerPartialSync(section, &section->loadstate, TSLS_LOADING1);\n\t\t\tif (section->loadstate == TSLS_LOADING2)\n\t\t\t\tCOM_MainThreadFlush();\t//make sure any associated lightmaps also got read+handled\n\t\t}\n\n\t\t//if it failed, generate a default (for editing)\n\t\tif (section->loadstate == TSLS_FAILED && ((flags & TGS_DEFAULTONFAIL) || hm->forcedefault))\n\t\t{\n\t\t\tsection->flags = (section->flags & ~TSF_EDITED);\n\t\t\tsection->loadstate = TSLS_LOADED;\n\t\t\tTerr_ClearSection(section);\n\t\t\tTerr_GenerateDefault(hm, section);\n\t\t}\n\n\n\t\tif ((section->loadstate != TSLS_LOADED) && !(flags & TGS_ANYSTATE))\n\t\t\tsection = NULL;\n\t}\n\tif (section)\n\t\tsection->timestamp = realtime;\n\n\treturn section;\n}\n\n/*save all currently loaded sections*/\nint Heightmap_Save(heightmap_t *hm)\n{\n\thmsection_t *s;\n\tint x, y;\n\tint sectionssaved = 0;\n\tfor (x = hm->firstsegx; x < hm->maxsegx; x++)\n\t{\n\t\tfor (y = hm->firstsegy; y < hm->maxsegy; y++)\n\t\t{\n\t\t\ts = Terr_GetSection(hm, x, y, TGS_NOLOAD);\n\t\t\tif (!s)\n\t\t\t\tcontinue;\n\t\t\tif (s->flags & TSF_EDITED)\n\t\t\t{\n/*\t\t\t\t//make sure all the parts are loaded before trying to write them, so we don't try reading partial files, which would be bad, mmkay?\n\t\t\t\tfor (sy = y&~(SECTIONSPERBLOCK-1); sy < y+SECTIONSPERBLOCK && sy < hm->maxsegy; sy++)\n\t\t\t\t{\n\t\t\t\t\tfor (sx = x&~(SECTIONSPERBLOCK-1); sx < x+SECTIONSPERBLOCK && sx < hm->maxsegx; sx++)\n\t\t\t\t\t{\n\t\t\t\t\t\tos = Terr_GetSection(hm, sx, sy, TGS_WAITLOAD|TGS_NODOWNLOAD|TGS_NORENDER);\n\t\t\t\t\t\tif (os)\n\t\t\t\t\t\t\tos->flags |= TSF_EDITED;\n\t\t\t\t\t}\n\t\t\t\t}\n*/\n\n\t\t\t\tif (Terr_SaveSection(hm, s, x, y, true))\n\t\t\t\t{\n\t\t\t\t\ts->flags &= ~TSF_EDITED;\n\t\t\t\t\tsectionssaved++;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn sectionssaved;\n}\n\n#ifdef HAVE_SERVER\n//on servers, we can get requests to download current map sections. if so, give them it.\nqboolean Terrain_LocateSection(const char *name, flocation_t *loc)\n{\n\theightmap_t *hm;\n\thmsection_t *s;\n\tint x, y;\n\tchar fname[MAX_QPATH];\n\n\t//reject if its not in maps\n\tif (Q_strncasecmp(name, \"maps/\", 5))\n\t\treturn false;\n\n\tif (!sv.world.worldmodel)\n\t\treturn false;\n\thm = sv.world.worldmodel->terrain;\n\tif (!Terr_IsSectionFName(hm, name, &x, &y))\n\t\treturn false;\n\n\t//verify that its valid\n\tif (strcmp(name, Terr_DiskSectionName(hm, x, y, fname, sizeof(fname))))\n\t\treturn false;\n\n\ts = Terr_GetSection(hm, x, y, TGS_NOLOAD);\n\tif (!s || !(s->flags & TSF_EDITED))\n\t\treturn false;\t//its not been edited, might as well just use the regular file\n\n\tif (!Terr_SaveSection(hm, s, x, y, false))\n\t\treturn false;\n\n\treturn FS_FLocateFile(name, FSLF_IFFOUND, loc);\n}\n#endif\n\nvoid Terr_DestroySection(heightmap_t *hm, hmsection_t *s, qboolean lightmapreusable)\n{\n\tif (s && s->loadstate == TSLS_LOADING0)\n\t\tCOM_WorkerPartialSync(s, &s->loadstate, TSLS_LOADING0);\n\tif (s && s->loadstate == TSLS_LOADING1)\n\t\tCOM_WorkerPartialSync(s, &s->loadstate, TSLS_LOADING1);\n\tif (s && s->loadstate == TSLS_LOADING2)\n\t\tCOM_MainThreadFlush();\t//make sure any associated lightmaps also got read+handled\n\n\tif (!s || s->loadstate < TSLS_LOADING2)\n\t\treturn;\n\n\t{\n\t\tint cx = s->sx/MAXSECTIONS;\n\t\tint cy = s->sy/MAXSECTIONS;\n\t\thmcluster_t *c = hm->cluster[cx + cy*MAXCLUSTERS];\n\t\tint sx = s->sx & (MAXSECTIONS-1);\n\t\tint sy = s->sy & (MAXSECTIONS-1);\n\n\t\tif (c->section[sx+sy*MAXSECTIONS] != s)\n\t\t\tSys_Error(\"Section %i,%i already destroyed...\\n\", s->sx, s->sy);\n\t\tc->section[sx+sy*MAXSECTIONS] = NULL;\n\t}\n\n\tvalidatelinks(&hm->recycle);\n\n\tRemoveLink(&s->recycle);\n\tvalidatelinks(&s->hmmod->recycle);\n\n\tTerr_ClearSection(s);\n\n#ifdef HAVE_CLIENT\n\tif (s->lightmap >= 0)\n\t{\n\t\tstruct lmsect_s *lms;\n\n\t\tif (lightmapreusable)\n\t\t{\n\t\t\tlms = BZ_Malloc(sizeof(*lms));\n\t\t\tlms->lm = s->lightmap;\n\t\t\tlms->x = s->lmx;\n\t\t\tlms->y = s->lmy;\n\t\t\tlms->next = hm->unusedlmsects;\n\t\t\thm->unusedlmsects = lms;\n\t\t\thm->numunusedlmsects++;\n\t\t}\n\t\thm->numusedlmsects--;\n\t}\n\n\tif (hm->relight == s)\n\t\thm->relight = NULL;\n\n#ifdef GLQUAKE\n\tif (qrenderer == QR_OPENGL)\n\t{\n\t\tif (qglDeleteBuffersARB)\n\t\t{\n\t\t\tif (s->vbo.coord.gl.vbo)\n\t\t\t{\n\t\t\t\tqglDeleteBuffersARB(1, &s->vbo.coord.gl.vbo);\n\t\t\t\ts->vbo.coord.gl.vbo = 0;\n\t\t\t}\n\t\t\tif (s->vbo.indicies.gl.vbo)\n\t\t\t{\n\t\t\t\tqglDeleteBuffersARB(1, &s->vbo.indicies.gl.vbo);\n\t\t\t\ts->vbo.indicies.gl.vbo = 0;\n\t\t\t}\n\t\t}\n\t}\n\telse\n#endif\n\t{\n\t\tBE_ClearVBO(&s->vbo, true);\n\t}\n\n\tZ_Free(s->ents);\n\tZ_Free(s->mesh.xyz_array);\n\tZ_Free(s->mesh.indexes);\n#endif\n\n\tZ_Free(s);\n\n\thm->activesections--;\n\n\tvalidatelinks(&hm->recycle);\n}\n\n#ifdef HAVE_CLIENT\n//dedicated servers do not support editing. no lightmap info causes problems.\n\n//when a terrain section has the notify flag set on the server, the server needs to go through and set out notifications to replicate it to the various clients\n//so the clients know to re-download the section.\nstatic void Terr_DoEditNotify(heightmap_t *hm)\n{\n#ifdef HAVE_SERVER\n\tint i;\n\tchar *cmd;\n\thmsection_t *s;\n\tlink_t *ln = &hm->recycle;\n\n\tif (!sv_state)\n\t\treturn;\n\n\tfor (i = 0; i < sv.allocated_client_slots; i++)\n\t{\n\t\tif (svs.clients[i].state >= cs_connected && svs.clients[i].netchan.remote_address.type != NA_LOOPBACK)\n\t\t{\n\t\t\tif (svs.clients[i].backbuf.cursize)\n\t\t\t\treturn;\n\t\t}\n\t}\n\n\tfor (ln = &hm->recycle; ln->next != &hm->recycle; ln = &s->recycle)\n\t{\n\t\ts = (hmsection_t*)ln->next;\n\t\tif (s->flags & TSF_NOTIFY)\n\t\t{\n\t\t\ts->flags &= ~TSF_NOTIFY;\n\t\t\tcmd = va(\"mod_terrain_reload %s %i %i\\n\", hm->path, s->sx - CHUNKBIAS, s->sy - CHUNKBIAS);\n\t\t\tfor (i = 0; i < sv.allocated_client_slots; i++)\n\t\t\t{\n\t\t\t\tif (svs.clients[i].state >= cs_connected && svs.clients[i].netchan.remote_address.type != NA_LOOPBACK)\n\t\t\t\t{\n\t\t\t\t\tSV_StuffcmdToClient(&svs.clients[i], cmd);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t}\n#endif\n}\n\n//garbage collect the oldest section, to make space for another\nstatic qboolean Terr_Collect(heightmap_t *hm)\n{\n\thmcluster_t *c;\n\thmsection_t *s;\n\tint cx, cy;\n\tint sx, sy;\n\tfloat timeout = realtime-2;\t//must used no later than 2 seconds in the past\n\n\tlink_t *ln = &hm->recycle;\n\tvalidatelinks(&hm->recycle);\n\tfor (ln = &hm->recycle; ln->next != &hm->recycle; )\n\t{\n\t\ts = (hmsection_t*)ln->next;\n\t\tif ((s->flags & TSF_EDITED) || s->loadstate <= TSLS_LOADING2 || s->timestamp > timeout)\n\t\t\tln = &s->recycle;\n\t\telse\n\t\t{\n\t\t\tcx = s->sx/MAXSECTIONS;\n\t\t\tcy = s->sy/MAXSECTIONS;\n\t\t\tc = hm->cluster[cx + cy*MAXCLUSTERS];\n\t\t\tsx = s->sx & (MAXSECTIONS-1);\n\t\t\tsy = s->sy & (MAXSECTIONS-1);\n\t\t\tif (c->section[sx+sy*MAXSECTIONS] != s)\n\t\t\t\tSys_Error(\"invalid section collection\");\n\t\t\tc->section[sx+sy*MAXSECTIONS] = NULL;\n\n#if 0\n\t\t\tif (hm->relight == s)\n\t\t\t\thm->relight = NULL;\n\t\t\tRemoveLink(&s->recycle);\n\t\t\tInsertLinkAfter(&s->recycle, &hm->collected);\n\t\t\thm->activesections--;\n#else\n\t\t\tTerr_DestroySection(hm, s, true);\n#endif\n\t\t\tvalidatelinks(&hm->recycle);\n\t\t\treturn true;\n\t\t}\n\t}\n\treturn false;\n}\n#endif\n\n/*purge all sections, but not root\nlightmaps only are purged whenever the client rudely kills lightmaps (purges all lightmaps on map changes, to cope with models/maps potentially being unloaded)\nwe'll reload those when its next seen.\n(lightmaps will already have been destroyed, so no poking them)\n*/\nvoid Terr_PurgeTerrainModel(model_t *mod, qboolean lightmapsonly, qboolean lightmapreusable)\n{\n\theightmap_t *hm = mod->terrain;\n\thmcluster_t *c;\n\thmsection_t *s;\n\tint cx, cy;\n\tint sx, sy;\n\n\tCOM_WorkerFullSync();\t//should probably be inside the caller or something. make sure there's no loaders still loading lightmaps when lightmaps are going to be nuked.\n\n\nvalidatelinks(&hm->recycle);\n\n//\tCon_Printf(\"PrePurge: %i lm chunks used, %i unused\\n\", hm->numusedlmsects, hm->numunusedlmsects);\n\n\tfor (cy = 0; cy < MAXCLUSTERS; cy++)\n\tfor (cx = 0; cx < MAXCLUSTERS; cx++)\n\t{\n\t\tint numremaining = 0;\n\t\tc = hm->cluster[cx + cy*MAXCLUSTERS];\n\t\tif (!c)\n\t\t\tcontinue;\n\n\t\tfor (sy = 0; sy < MAXSECTIONS; sy++)\n\t\tfor (sx = 0; sx < MAXSECTIONS; sx++)\n\t\t{\n\t\t\ts = c->section[sx + sy*MAXSECTIONS];\n\t\t\tif (!s)\n\t\t\t{\n\t\t\t}\n\t\t\telse if (lightmapsonly)\n\t\t\t{\n\t\t\t\tnumremaining++;\n#ifdef HAVE_CLIENT\n\t\t\t\ts->lightmap = -1;\n#endif\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tvalidatelinks(&hm->recycle);\n\t\t\t\tTerr_DestroySection(hm, s, lightmapreusable);\n\t\t\t\tvalidatelinks(&hm->recycle);\n\t\t\t}\n\t\t}\n\t\tif (!numremaining)\n\t\t{\n\t\t\thm->cluster[cx + cy*MAXSECTIONS] = NULL;\n\t\t\tBZ_Free(c);\n\t\t\tvalidatelinks(&hm->recycle);\n\t\t}\n\t}\n\tvalidatelinks(&hm->recycle);\n#ifdef HAVE_CLIENT\n\tif (!lightmapreusable)\n\t{\n\t\twhile (hm->unusedlmsects)\n\t\t{\n\t\t\tstruct lmsect_s *lms;\n\t\t\tlms = hm->unusedlmsects;\n\t\t\thm->unusedlmsects = lms->next;\n\t\t\tBZ_Free(lms);\n\n\t\t\thm->numunusedlmsects--;\n\t\t}\n\n\n\t\thm->recalculatebrushlighting = true;\n\t\tBZ_Free(hm->brushlmremaps);\n\t\thm->brushlmremaps = NULL;\n\t\thm->brushmaxlms = 0;\n\t}\n#endif\n\tvalidatelinks(&hm->recycle);\n\n//\tCon_Printf(\"PostPurge: %i lm chunks used, %i unused\\n\", hm->numusedlmsects, hm->numunusedlmsects);\n}\n\nvoid Terr_FreeModel(model_t *mod)\n{\n\theightmap_t *hm = mod->terrain;\n\tif (hm)\n\t{\n\t\tvalidatelinks(&hm->recycle);\n\t\twhile(hm->numbrushes)\n\t\t\tTerr_Brush_DeleteIdx(hm, hm->numbrushes-1);\n\t\twhile(hm->brushtextures)\n\t\t{\n\t\t\tbrushtex_t *bt = hm->brushtextures;\n#ifdef HAVE_CLIENT\n\t\t\tbrushbatch_t *bb;\n\t\t\twhile((bb = bt->batches))\n\t\t\t{\n\t\t\t\tbt->batches = bb->next;\n\t\t\t\tBE_VBO_Destroy(&bb->vbo.coord, bb->vbo.vbomem);\n\t\t\t\tBE_VBO_Destroy(&bb->vbo.indicies, bb->vbo.ebomem);\n\t\t\t\tBZ_Free(bb);\n\t\t\t}\n#endif\n\t\t\thm->brushtextures = bt->next;\n\t\t\tBZ_Free(bt);\n\t\t}\n#ifdef RUNTIMELIGHTING\n\t\tif (hm->relightcontext)\n\t\t\tLightShutdown(hm->relightcontext, mod);\n\t\tif (hm->lightthreadmem && !hm->inheritedlightthreadmem)\n\t\t\tBZ_Free(hm->lightthreadmem);\n#endif\n\t\tBZ_Free(hm->wbrushes);\n\t\tTerr_PurgeTerrainModel(mod, false, false);\n\t\twhile(hm->entities)\n\t\t{\n\t\t\tstruct hmentity_s *n = hm->entities->next;\n\t\t\tZ_Free(hm->entities);\n\t\t\thm->entities = n;\n\t\t}\n\t\tSys_DestroyMutex(hm->entitylock);\n\t\tZ_Free(hm->seed);\n\t\tZ_Free(hm);\n\t\tmod->terrain = NULL;\n\t}\n}\n\n#ifdef HAVE_CLIENT\nvoid Terr_DrawTerrainWater(heightmap_t *hm, float *mins, float *maxs, struct hmwater_s *w)\n{\n\tscenetris_t *t;\n\tint flags = BEF_NOSHADOWS;\n\tint firstv;\n\tint y, x;\n\t\n\t//need to filter by height too, or reflections won't work properly.\n\tif (cl_numstris && cl_stris[cl_numstris-1].shader == w->shader && cl_stris[cl_numstris-1].flags == flags && cl_strisvertv[cl_stris[cl_numstris-1].firstvert][2] == w->maxheight)\n\t{\n\t\tt = &cl_stris[cl_numstris-1];\n\t}\n\telse\n\t{\n\t\tif (cl_numstris == cl_maxstris)\n\t\t{\n\t\t\tcl_maxstris+=8;\n\t\t\tcl_stris = BZ_Realloc(cl_stris, sizeof(*cl_stris)*cl_maxstris);\n\t\t}\n\t\tt = &cl_stris[cl_numstris++];\n\t\tt->shader = w->shader;\n\t\tt->flags = flags;\n\t\tt->firstidx = cl_numstrisidx;\n\t\tt->firstvert = cl_numstrisvert;\n\t\tt->numvert = 0;\n\t\tt->numidx = 0;\n\t}\n\n\tif (!w->simple)\n\t{\n\t\tfloat step = (maxs[0] - mins[0]) / 8;\n\t\tif (cl_numstrisidx+9*9*6 > cl_maxstrisidx)\n\t\t{\n\t\t\tcl_maxstrisidx=cl_numstrisidx+12 + 9*9*6*4;\n\t\t\tcl_strisidx = BZ_Realloc(cl_strisidx, sizeof(*cl_strisidx)*cl_maxstrisidx);\n\t\t}\n\t\tif (cl_numstrisvert+9*9 > cl_maxstrisvert)\n\t\t\tcl_stris_ExpandVerts(cl_numstrisvert+9*9+64);\n\n\t\tfirstv = t->numvert;\n\t\tfor (y = 0; y < 9; y++)\n\t\t{\n\t\t\tfor (x = 0; x < 9; x++)\n\t\t\t{\n\t\t\t\tcl_strisvertv[cl_numstrisvert][0] = mins[0] + step*x;\n\t\t\t\tcl_strisvertv[cl_numstrisvert][1] = mins[1] + step*y;\n\t\t\t\tcl_strisvertv[cl_numstrisvert][2] = w->heights[x + y*9];\n\t\t\t\tcl_strisvertt[cl_numstrisvert][0] = cl_strisvertv[cl_numstrisvert][0]/64;\n\t\t\t\tcl_strisvertt[cl_numstrisvert][1] = cl_strisvertv[cl_numstrisvert][1]/64;\n\t\t\t\tVector4Set(cl_strisvertc[cl_numstrisvert], 1,1,1,1);\n\t\t\t\tcl_numstrisvert++;\n\t\t\t}\n\t\t}\n\t\tfor (y = 0; y < 8; y++)\n\t\t{\n\t\t\tfor (x = 0; x < 8; x++)\n\t\t\t{\n\t\t\t\tif (w->holes[y] & (1u<<x))\n\t\t\t\t\tcontinue;\n\t\t\t\tcl_strisidx[cl_numstrisidx++] = firstv+(x+0)+(y+0)*9;\n\t\t\t\tcl_strisidx[cl_numstrisidx++] = firstv+(x+0)+(y+1)*9;\n\t\t\t\tcl_strisidx[cl_numstrisidx++] = firstv+(x+1)+(y+0)*9;\n\n\t\t\t\tcl_strisidx[cl_numstrisidx++] = firstv+(x+1)+(y+0)*9;\n\t\t\t\tcl_strisidx[cl_numstrisidx++] = firstv+(x+0)+(y+1)*9;\n\t\t\t\tcl_strisidx[cl_numstrisidx++] = firstv+(x+1)+(y+1)*9;\n\t\t\t}\n\t\t}\n\t\tt->numidx = cl_numstrisidx - t->firstidx;\n\t\tt->numvert = cl_numstrisvert - t->firstvert;\n\t}\n\telse\n\t{\n\t\tif (cl_numstrisidx+12 > cl_maxstrisidx)\n\t\t{\n\t\t\tcl_maxstrisidx=cl_numstrisidx+12 + 64;\n\t\t\tcl_strisidx = BZ_Realloc(cl_strisidx, sizeof(*cl_strisidx)*cl_maxstrisidx);\n\t\t}\n\t\tif (cl_numstrisvert+4 > cl_maxstrisvert)\n\t\t\tcl_stris_ExpandVerts(cl_numstrisvert+64);\n\n\t\t{\n\t\t\tVectorSet(cl_strisvertv[cl_numstrisvert], mins[0], mins[1], w->maxheight);\n\t\t\tVector4Set(cl_strisvertc[cl_numstrisvert], 1,1,1,1);\n\t\t\tVector2Set(cl_strisvertt[cl_numstrisvert], mins[0]/64, mins[1]/64);\n\t\t\tcl_numstrisvert++;\n\n\t\t\tVectorSet(cl_strisvertv[cl_numstrisvert], mins[0], maxs[1], w->maxheight);\n\t\t\tVector4Set(cl_strisvertc[cl_numstrisvert], 1,1,1,1);\n\t\t\tVector2Set(cl_strisvertt[cl_numstrisvert], mins[0]/64, maxs[1]/64);\n\t\t\tcl_numstrisvert++;\n\n\t\t\tVectorSet(cl_strisvertv[cl_numstrisvert], maxs[0], maxs[1], w->maxheight);\n\t\t\tVector4Set(cl_strisvertc[cl_numstrisvert], 1,1,1,1);\n\t\t\tVector2Set(cl_strisvertt[cl_numstrisvert], maxs[0]/64, maxs[1]/64);\n\t\t\tcl_numstrisvert++;\n\n\t\t\tVectorSet(cl_strisvertv[cl_numstrisvert], maxs[0], mins[1], w->maxheight);\n\t\t\tVector4Set(cl_strisvertc[cl_numstrisvert], 1,1,1,1);\n\t\t\tVector2Set(cl_strisvertt[cl_numstrisvert], maxs[0]/64, mins[1]/64);\n\t\t\tcl_numstrisvert++;\n\t\t}\n\n\n\t\tfirstv = t->numvert;\n\n\t\t/*build the triangles*/\n\t\tcl_strisidx[cl_numstrisidx++] = firstv + 0;\n\t\tcl_strisidx[cl_numstrisidx++] = firstv + 1;\n\t\tcl_strisidx[cl_numstrisidx++] = firstv + 2;\n\n\t\tcl_strisidx[cl_numstrisidx++] = firstv + 0;\n\t\tcl_strisidx[cl_numstrisidx++] = firstv + 2;\n\t\tcl_strisidx[cl_numstrisidx++] = firstv + 3;\n\n\t\tcl_strisidx[cl_numstrisidx++] = firstv + 3;\n\t\tcl_strisidx[cl_numstrisidx++] = firstv + 2;\n\t\tcl_strisidx[cl_numstrisidx++] = firstv + 1;\n\n\t\tcl_strisidx[cl_numstrisidx++] = firstv + 3;\n\t\tcl_strisidx[cl_numstrisidx++] = firstv + 1;\n\t\tcl_strisidx[cl_numstrisidx++] = firstv + 0;\n\n\n\t\tt->numidx = cl_numstrisidx - t->firstidx;\n\t\tt->numvert = cl_numstrisvert - t->firstvert;\n\t}\n}\n\nstatic void Terr_RebuildMesh(model_t *model, hmsection_t *s, int x, int y)\n{\n\tint vx, vy;\n\tint v;\n\tmesh_t *mesh = &s->mesh;\n\theightmap_t *hm = s->hmmod;\n\t\n\tTerr_InitLightmap(s, false);\n\n\ts->minh = 9999999999999999.f;\n\ts->maxh = -9999999999999999.f;\n\n\tswitch(hm->mode)\n\t{\n\tcase HMM_BLOCKS:\n\t\t//tiles, like dungeon keeper\n\t\tif (mesh->xyz_array)\n\t\t\tBZ_Free(mesh->xyz_array);\n\t\t{\n\t\t\tmesh->xyz_array = BZ_Malloc((sizeof(vecV_t)+sizeof(vec2_t)+sizeof(vec2_t)) * (SECTHEIGHTSIZE-1)*(SECTHEIGHTSIZE-1)*4*3);\n\t\t\tmesh->st_array = (void*) (mesh->xyz_array + (SECTHEIGHTSIZE-1)*(SECTHEIGHTSIZE-1)*4*3);\n\t\t\tmesh->lmst_array[0] = (void*) (mesh->st_array + (SECTHEIGHTSIZE-1)*(SECTHEIGHTSIZE-1)*4*3);\n\t\t}\n\t\tmesh->numvertexes = 0;\n\n\t\tif (mesh->indexes)\n\t\t\tBZ_Free(mesh->indexes);\n\t\tmesh->indexes = BZ_Malloc(sizeof(index_t) * SECTHEIGHTSIZE*SECTHEIGHTSIZE*6*3);\n\t\tmesh->numindexes = 0;\n\t\tmesh->colors4f_array[0] = NULL;\n\n\t\tfor (vy = 0; vy < SECTHEIGHTSIZE-1; vy++)\n\t\t{\n\t\t\tfor (vx = 0; vx < SECTHEIGHTSIZE-1; vx++)\n\t\t\t{\n\t\t\t\tfloat st[2], inst[2];\n#if SECTHEIGHTSIZE == 17\n\t\t\t\tint holebit;\n\t\t\t\tint holerow;\n\n\t\t\t\t//skip generation of the mesh above holes\n\t\t\t\tholerow = ((vy<<3)/(SECTHEIGHTSIZE-1));\n\t\t\t\tholebit = 1u<<((vx<<3)/(SECTHEIGHTSIZE-1));\n\t\t\t\tif (s->holes[holerow] & holebit)\n\t\t\t\t\tcontinue;\n#endif\n\n\t\t\t\t//top face\n\t\t\t\tv = mesh->numvertexes;\n\t\t\t\tmesh->numvertexes += 4;\n\t\t\t\tmesh->xyz_array[v+0][0] = (x-CHUNKBIAS + (vx+0)/(SECTHEIGHTSIZE-1.0f)) * hm->sectionsize;\n\t\t\t\tmesh->xyz_array[v+0][1] = (y-CHUNKBIAS + (vy+0)/(SECTHEIGHTSIZE-1.0f)) * hm->sectionsize;\n\t\t\t\tmesh->xyz_array[v+0][2] = s->heights[vx + vy*SECTHEIGHTSIZE];\n\n\t\t\t\tmesh->xyz_array[v+1][0] = (x-CHUNKBIAS + (vx+1)/(SECTHEIGHTSIZE-1.0f)) * hm->sectionsize;\n\t\t\t\tmesh->xyz_array[v+1][1] = (y-CHUNKBIAS + (vy+0)/(SECTHEIGHTSIZE-1.0f)) * hm->sectionsize;\n\t\t\t\tmesh->xyz_array[v+1][2] = s->heights[vx + vy*SECTHEIGHTSIZE];\n\n\t\t\t\tmesh->xyz_array[v+2][0] = (x-CHUNKBIAS + (vx+0)/(SECTHEIGHTSIZE-1.0f)) * hm->sectionsize;\n\t\t\t\tmesh->xyz_array[v+2][1] = (y-CHUNKBIAS + (vy+1)/(SECTHEIGHTSIZE-1.0f)) * hm->sectionsize;\n\t\t\t\tmesh->xyz_array[v+2][2] = s->heights[vx + vy*SECTHEIGHTSIZE];\n\n\t\t\t\tmesh->xyz_array[v+3][0] = (x-CHUNKBIAS + (vx+1)/(SECTHEIGHTSIZE-1.0f)) * hm->sectionsize;\n\t\t\t\tmesh->xyz_array[v+3][1] = (y-CHUNKBIAS + (vy+1)/(SECTHEIGHTSIZE-1.0f)) * hm->sectionsize;\n\t\t\t\tmesh->xyz_array[v+3][2] = s->heights[vx + vy*SECTHEIGHTSIZE];\n\n\t\t\t\tif (s->maxh < mesh->xyz_array[v][2])\n\t\t\t\t\ts->maxh = mesh->xyz_array[v][2];\n\t\t\t\tif (s->minh > mesh->xyz_array[v][2])\n\t\t\t\t\ts->minh = mesh->xyz_array[v][2];\n\n\t\t\t\tst[0] = 1.0f/hm->tilecount[0] * vx;\n\t\t\t\tst[1] = 1.0f/hm->tilecount[1] * vy;\n\t\t\t\tinst[0] = 0.5f/(hm->tilecount[0]*hm->tilepixcount[0]);\n\t\t\t\tinst[1] = 0.5f/(hm->tilecount[1]*hm->tilepixcount[1]);\n\t\t\t\tmesh->st_array[v+0][0] = st[0]+inst[0];\n\t\t\t\tmesh->st_array[v+0][1] = st[1]+inst[1];\n\t\t\t\tmesh->st_array[v+1][0] = st[0]-inst[0]+1.0f/hm->tilecount[0];\n\t\t\t\tmesh->st_array[v+1][1] = st[1]+inst[1];\n\t\t\t\tmesh->st_array[v+2][0] = st[0]+inst[0];\n\t\t\t\tmesh->st_array[v+2][1] = st[1]-inst[1]+1.0f/hm->tilecount[1];\n\t\t\t\tmesh->st_array[v+3][0] = st[0]-inst[0]+1.0f/hm->tilecount[0];\n\t\t\t\tmesh->st_array[v+3][1] = st[1]-inst[1]+1.0f/hm->tilecount[1];\n\n\t\t\t\t//calc the position in the range -0.5 to 0.5\n\t\t\t\tmesh->lmst_array[0][v][0] = (((float)vx / (SECTHEIGHTSIZE-1))-0.5);\n\t\t\t\tmesh->lmst_array[0][v][1] = (((float)vy / (SECTHEIGHTSIZE-1))-0.5);\n\t\t\t\t//scale down to a half-texel\n\t\t\t\tmesh->lmst_array[0][v][0] *= (SECTTEXSIZE-1.0f)/HMLMSTRIDE;\n\t\t\t\tmesh->lmst_array[0][v][1] *= (SECTTEXSIZE-1.0f)/HMLMSTRIDE;\n\t\t\t\t//bias it\n\t\t\t\tmesh->lmst_array[0][v][0] += ((float)SECTTEXSIZE/(HMLMSTRIDE*2)) + ((float)(s->lmx) / HMLMSTRIDE);\n\t\t\t\tmesh->lmst_array[0][v][1] += ((float)SECTTEXSIZE/(HMLMSTRIDE*2)) + ((float)(s->lmy) / HMLMSTRIDE);\n\n\t\t\t\tmesh->indexes[mesh->numindexes++] = v+0;\n\t\t\t\tmesh->indexes[mesh->numindexes++] = v+2;\n\t\t\t\tmesh->indexes[mesh->numindexes++] = v+1;\n\t\t\t\tmesh->indexes[mesh->numindexes++] = v+1;\n\t\t\t\tmesh->indexes[mesh->numindexes++] = v+2;\n\t\t\t\tmesh->indexes[mesh->numindexes++] = v+1+2;\n\n\n\t\t\t\t//x boundary\n\t\t\t\tv = mesh->numvertexes;\n\t\t\t\tmesh->numvertexes += 4;\n\t\t\t\tmesh->xyz_array[v+0][0] = (x-CHUNKBIAS + (vx+1)/(SECTHEIGHTSIZE-1.0f)) * hm->sectionsize;\n\t\t\t\tmesh->xyz_array[v+0][1] = (y-CHUNKBIAS + (vy+0)/(SECTHEIGHTSIZE-1.0f)) * hm->sectionsize;\n\t\t\t\tmesh->xyz_array[v+0][2] = s->heights[vx+0 + vy*SECTHEIGHTSIZE];\n\n\t\t\t\tmesh->xyz_array[v+1][0] = (x-CHUNKBIAS + (vx+1)/(SECTHEIGHTSIZE-1.0f)) * hm->sectionsize;\n\t\t\t\tmesh->xyz_array[v+1][1] = (y-CHUNKBIAS + (vy+0)/(SECTHEIGHTSIZE-1.0f)) * hm->sectionsize;\n\t\t\t\tmesh->xyz_array[v+1][2] = s->heights[(vx+1) + vy*SECTHEIGHTSIZE];\n\n\t\t\t\tmesh->xyz_array[v+2][0] = (x-CHUNKBIAS + (vx+1)/(SECTHEIGHTSIZE-1.0f)) * hm->sectionsize;\n\t\t\t\tmesh->xyz_array[v+2][1] = (y-CHUNKBIAS + (vy+1)/(SECTHEIGHTSIZE-1.0f)) * hm->sectionsize;\n\t\t\t\tmesh->xyz_array[v+2][2] = s->heights[(vx+0) + vy*SECTHEIGHTSIZE];\n\n\t\t\t\tmesh->xyz_array[v+3][0] = (x-CHUNKBIAS + (vx+1)/(SECTHEIGHTSIZE-1.0f)) * hm->sectionsize;\n\t\t\t\tmesh->xyz_array[v+3][1] = (y-CHUNKBIAS + (vy+1)/(SECTHEIGHTSIZE-1.0f)) * hm->sectionsize;\n\t\t\t\tmesh->xyz_array[v+3][2] = s->heights[(vx+1) + vy*SECTHEIGHTSIZE];\n\n\t\t\t\tif (s->maxh < mesh->xyz_array[v][2])\n\t\t\t\t\ts->maxh = mesh->xyz_array[v][2];\n\t\t\t\tif (s->minh > mesh->xyz_array[v][2])\n\t\t\t\t\ts->minh = mesh->xyz_array[v][2];\n\n\t\t\t\tst[0] = 1.0f/hm->tilecount[0] * vx;\n\t\t\t\tst[1] = 1.0f/hm->tilecount[1] * vy;\n\t\t\t\tinst[0] = 0.5f/(hm->tilecount[0]*hm->tilepixcount[0]);\n\t\t\t\tinst[1] = 0.5f/(hm->tilecount[1]*hm->tilepixcount[1]);\n\t\t\t\tmesh->st_array[v+0][0] = st[0]+inst[0];\n\t\t\t\tmesh->st_array[v+0][1] = st[1]+inst[1];\n\t\t\t\tmesh->st_array[v+1][0] = st[0]+inst[0];\n\t\t\t\tmesh->st_array[v+1][1] = st[1]-inst[1]+1.0f/hm->tilecount[1];\n\t\t\t\tmesh->st_array[v+2][0] = st[0]-inst[0]+1.0f/hm->tilecount[0];\n\t\t\t\tmesh->st_array[v+2][1] = st[1]+inst[1];\n\t\t\t\tmesh->st_array[v+3][0] = st[0]-inst[0]+1.0f/hm->tilecount[0];\n\t\t\t\tmesh->st_array[v+3][1] = st[1]-inst[1]+1.0f/hm->tilecount[1];\n\n\t\t\t\t//calc the position in the range -0.5 to 0.5\n\t\t\t\tmesh->lmst_array[0][v][0] = (((float)vx / (SECTHEIGHTSIZE-1))-0.5);\n\t\t\t\tmesh->lmst_array[0][v][1] = (((float)vy / (SECTHEIGHTSIZE-1))-0.5);\n\t\t\t\t//scale down to a half-texel\n\t\t\t\tmesh->lmst_array[0][v][0] *= (SECTTEXSIZE-1.0f)/HMLMSTRIDE;\n\t\t\t\tmesh->lmst_array[0][v][1] *= (SECTTEXSIZE-1.0f)/HMLMSTRIDE;\n\t\t\t\t//bias it\n\t\t\t\tmesh->lmst_array[0][v][0] += ((float)SECTTEXSIZE/(HMLMSTRIDE*2)) + ((float)(s->lmx) / HMLMSTRIDE);\n\t\t\t\tmesh->lmst_array[0][v][1] += ((float)SECTTEXSIZE/(HMLMSTRIDE*2)) + ((float)(s->lmy) / HMLMSTRIDE);\n\n\n\t\t\t\tmesh->indexes[mesh->numindexes++] = v+0;\n\t\t\t\tmesh->indexes[mesh->numindexes++] = v+2;\n\t\t\t\tmesh->indexes[mesh->numindexes++] = v+1;\n\t\t\t\tmesh->indexes[mesh->numindexes++] = v+1;\n\t\t\t\tmesh->indexes[mesh->numindexes++] = v+2;\n\t\t\t\tmesh->indexes[mesh->numindexes++] = v+1+2;\n\n\t\t\t\t//y boundary\n\t\t\t\tv = mesh->numvertexes;\n\t\t\t\tmesh->numvertexes += 4;\n\t\t\t\tmesh->xyz_array[v+0][0] = (x-CHUNKBIAS + (vx+0)/(SECTHEIGHTSIZE-1.0f)) * hm->sectionsize;\n\t\t\t\tmesh->xyz_array[v+0][1] = (y-CHUNKBIAS + (vy+1)/(SECTHEIGHTSIZE-1.0f)) * hm->sectionsize;\n\t\t\t\tmesh->xyz_array[v+0][2] = s->heights[vx + (vy+0)*SECTHEIGHTSIZE];\n\n\t\t\t\tmesh->xyz_array[v+1][0] = (x-CHUNKBIAS + (vx+1)/(SECTHEIGHTSIZE-1.0f)) * hm->sectionsize;\n\t\t\t\tmesh->xyz_array[v+1][1] = (y-CHUNKBIAS + (vy+1)/(SECTHEIGHTSIZE-1.0f)) * hm->sectionsize;\n\t\t\t\tmesh->xyz_array[v+1][2] = s->heights[vx + (vy+0)*SECTHEIGHTSIZE];\n\n\t\t\t\tmesh->xyz_array[v+2][0] = (x-CHUNKBIAS + (vx+0)/(SECTHEIGHTSIZE-1.0f)) * hm->sectionsize;\n\t\t\t\tmesh->xyz_array[v+2][1] = (y-CHUNKBIAS + (vy+1)/(SECTHEIGHTSIZE-1.0f)) * hm->sectionsize;\n\t\t\t\tmesh->xyz_array[v+2][2] = s->heights[vx + (vy+1)*SECTHEIGHTSIZE];\n\n\t\t\t\tmesh->xyz_array[v+3][0] = (x-CHUNKBIAS + (vx+1)/(SECTHEIGHTSIZE-1.0f)) * hm->sectionsize;\n\t\t\t\tmesh->xyz_array[v+3][1] = (y-CHUNKBIAS + (vy+1)/(SECTHEIGHTSIZE-1.0f)) * hm->sectionsize;\n\t\t\t\tmesh->xyz_array[v+3][2] = s->heights[vx + (vy+1)*SECTHEIGHTSIZE];\n\n\t\t\t\tif (s->maxh < mesh->xyz_array[v][2])\n\t\t\t\t\ts->maxh = mesh->xyz_array[v][2];\n\t\t\t\tif (s->minh > mesh->xyz_array[v][2])\n\t\t\t\t\ts->minh = mesh->xyz_array[v][2];\n\n\t\t\t\tst[0] = 1.0f/hm->tilecount[0] * vx;\n\t\t\t\tst[1] = 1.0f/hm->tilecount[1] * vy;\n\t\t\t\tinst[0] = 0.5f/(hm->tilecount[0]*hm->tilepixcount[0]);\n\t\t\t\tinst[1] = 0.5f/(hm->tilecount[1]*hm->tilepixcount[1]);\n\t\t\t\tmesh->st_array[v+0][0] = st[0]+inst[0];\n\t\t\t\tmesh->st_array[v+0][1] = st[1]+inst[1];\n\t\t\t\tmesh->st_array[v+1][0] = st[0]-inst[0]+1.0f/hm->tilecount[0];\n\t\t\t\tmesh->st_array[v+1][1] = st[1]+inst[1];\n\t\t\t\tmesh->st_array[v+2][0] = st[0]+inst[0];\n\t\t\t\tmesh->st_array[v+2][1] = st[1]-inst[1]+1.0f/hm->tilecount[1];\n\t\t\t\tmesh->st_array[v+3][0] = st[0]-inst[0]+1.0f/hm->tilecount[0];\n\t\t\t\tmesh->st_array[v+3][1] = st[1]-inst[1]+1.0f/hm->tilecount[1];\n\n\t\t\t\t//calc the position in the range -0.5 to 0.5\n\t\t\t\tmesh->lmst_array[0][v][0] = (((float)vx / (SECTHEIGHTSIZE-1))-0.5);\n\t\t\t\tmesh->lmst_array[0][v][1] = (((float)vy / (SECTHEIGHTSIZE-1))-0.5);\n\t\t\t\t//scale down to a half-texel\n\t\t\t\tmesh->lmst_array[0][v][0] *= (SECTTEXSIZE-1.0f)/HMLMSTRIDE;\n\t\t\t\tmesh->lmst_array[0][v][1] *= (SECTTEXSIZE-1.0f)/HMLMSTRIDE;\n\t\t\t\t//bias it\n\t\t\t\tmesh->lmst_array[0][v][0] += ((float)SECTTEXSIZE/(HMLMSTRIDE*2)) + ((float)(s->lmx) / HMLMSTRIDE);\n\t\t\t\tmesh->lmst_array[0][v][1] += ((float)SECTTEXSIZE/(HMLMSTRIDE*2)) + ((float)(s->lmy) / HMLMSTRIDE);\n\n\t\t\t\tmesh->indexes[mesh->numindexes++] = v+0;\n\t\t\t\tmesh->indexes[mesh->numindexes++] = v+2;\n\t\t\t\tmesh->indexes[mesh->numindexes++] = v+1;\n\t\t\t\tmesh->indexes[mesh->numindexes++] = v+1;\n\t\t\t\tmesh->indexes[mesh->numindexes++] = v+2;\n\t\t\t\tmesh->indexes[mesh->numindexes++] = v+1+2;\n\t\t\t}\n\t\t}\n\t\tbreak;\n\tcase HMM_TERRAIN:\n\t\t//smooth terrain\n\t\tif (!mesh->xyz_array)\n\t\t{\n\t\t\tmesh->xyz_array = BZ_Malloc((sizeof(vecV_t)+sizeof(vec2_t)+sizeof(vec2_t)) * (SECTHEIGHTSIZE)*(SECTHEIGHTSIZE));\n\t\t\tmesh->st_array = (void*) (mesh->xyz_array + (SECTHEIGHTSIZE)*(SECTHEIGHTSIZE));\n\t\t\tmesh->lmst_array[0] = (void*) (mesh->st_array + (SECTHEIGHTSIZE)*(SECTHEIGHTSIZE));\n\t\t}\n\t\tmesh->colors4f_array[0] = s->colours;\n\t\tmesh->numvertexes = 0;\n\t\t/*64 quads across requires 65 verticies*/\n\t\tfor (vy = 0; vy < SECTHEIGHTSIZE; vy++)\n\t\t{\n\t\t\tfor (vx = 0; vx < SECTHEIGHTSIZE; vx++)\n\t\t\t{\n\t\t\t\tv = mesh->numvertexes++;\n\t\t\t\tmesh->xyz_array[v][0] = (x-CHUNKBIAS + vx/(SECTHEIGHTSIZE-1.0f)) * hm->sectionsize;\n\t\t\t\tmesh->xyz_array[v][1] = (y-CHUNKBIAS + vy/(SECTHEIGHTSIZE-1.0f)) * hm->sectionsize;\n\t\t\t\tmesh->xyz_array[v][2] = s->heights[vx + vy*SECTHEIGHTSIZE];\n\n\t\t\t\tif (s->maxh < mesh->xyz_array[v][2])\n\t\t\t\t\ts->maxh = mesh->xyz_array[v][2];\n\t\t\t\tif (s->minh > mesh->xyz_array[v][2])\n\t\t\t\t\ts->minh = mesh->xyz_array[v][2];\n\n\t\t\t\tmesh->st_array[v][0] = mesh->xyz_array[v][0] / 128;\n\t\t\t\tmesh->st_array[v][1] = mesh->xyz_array[v][1] / 128;\n\n\t\t\t\t//calc the position in the range -0.5 to 0.5\n\t\t\t\tmesh->lmst_array[0][v][0] = (((float)vx / (SECTHEIGHTSIZE-1))-0.5);\n\t\t\t\tmesh->lmst_array[0][v][1] = (((float)vy / (SECTHEIGHTSIZE-1))-0.5);\n\t\t\t\t//scale down to a half-texel\n\t\t\t\tmesh->lmst_array[0][v][0] *= (SECTTEXSIZE-1.0f)/HMLMSTRIDE;\n\t\t\t\tmesh->lmst_array[0][v][1] *= (SECTTEXSIZE-1.0f)/HMLMSTRIDE;\n\t\t\t\t//bias it\n\t\t\t\tmesh->lmst_array[0][v][0] += ((float)SECTTEXSIZE/(HMLMSTRIDE*2)) + ((float)(s->lmx) / HMLMSTRIDE);\n\t\t\t\tmesh->lmst_array[0][v][1] += ((float)SECTTEXSIZE/(HMLMSTRIDE*2)) + ((float)(s->lmy) / HMLMSTRIDE);\n\t\t\t}\n\t\t}\n\n\t\tif (!mesh->indexes)\n\t\t\tmesh->indexes = BZ_Malloc(sizeof(index_t) * SECTHEIGHTSIZE*SECTHEIGHTSIZE*6);\n\n\t\tmesh->numindexes = 0;\n\t\tfor (vy = 0; vy < SECTHEIGHTSIZE-1; vy++)\n\t\t{\n\t\t\tfor (vx = 0; vx < SECTHEIGHTSIZE-1; vx++)\n\t\t\t{\n\t#ifndef STRICTEDGES\n\t\t\t\tfloat d1,d2;\n\t#endif\n\n\t#if SECTHEIGHTSIZE == 17\n\t\t\t\tint holerow;\n\t\t\t\tint holebit;\n\n\t\t\t\t//skip generation of the mesh above holes\n\t\t\t\tholerow = ((vy<<3)/(SECTHEIGHTSIZE-1));\n\t\t\t\tholebit = 1u<<((vx<<3)/(SECTHEIGHTSIZE-1));\n\t\t\t\tif (s->holes[holerow] & holebit)\n\t\t\t\t\tcontinue;\n\t#endif\n\t\t\t\tv = vx + vy*(SECTHEIGHTSIZE);\n\n\t#ifndef STRICTEDGES\n\t\t\t\td1 = fabs(mesh->xyz_array[v][2] - mesh->xyz_array[v+1+SECTHEIGHTSIZE][2]);\n\t\t\t\td2 = fabs(mesh->xyz_array[v+1][2] - mesh->xyz_array[v+SECTHEIGHTSIZE][2]);\n\t\t\t\tif (d1 < d2)\n\t\t\t\t{\n\t\t\t\t\tmesh->indexes[mesh->numindexes++] = v+0;\n\t\t\t\t\tmesh->indexes[mesh->numindexes++] = v+1+SECTHEIGHTSIZE;\n\t\t\t\t\tmesh->indexes[mesh->numindexes++] = v+1;\n\t\t\t\t\tmesh->indexes[mesh->numindexes++] = v+0;\n\t\t\t\t\tmesh->indexes[mesh->numindexes++] = v+SECTHEIGHTSIZE;\n\t\t\t\t\tmesh->indexes[mesh->numindexes++] = v+1+SECTHEIGHTSIZE;\n\t\t\t\t}\n\t\t\t\telse\n\t#endif\n\t\t\t\t{\n\t\t\t\t\tmesh->indexes[mesh->numindexes++] = v+0;\n\t\t\t\t\tmesh->indexes[mesh->numindexes++] = v+SECTHEIGHTSIZE;\n\t\t\t\t\tmesh->indexes[mesh->numindexes++] = v+1;\n\t\t\t\t\tmesh->indexes[mesh->numindexes++] = v+1;\n\t\t\t\t\tmesh->indexes[mesh->numindexes++] = v+SECTHEIGHTSIZE;\n\t\t\t\t\tmesh->indexes[mesh->numindexes++] = v+1+SECTHEIGHTSIZE;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tbreak;\n\t}\n\n\t//pure holes\n\tif (!mesh->numindexes)\n\t{\n\t\tmemset(&s->pvscache, 0, sizeof(s->pvscache));\n\t\treturn;\n\t}\n\n\tif (s->maxh_cull < s->maxh)\n\t\ts->maxh_cull = s->maxh;\n\t{\n\t\tvec3_t mins, maxs;\n\t\tmins[0] = (x-CHUNKBIAS) * hm->sectionsize;\n\t\tmins[1] = (y-CHUNKBIAS) * hm->sectionsize;\n\t\tmins[2] = s->minh;\n\t\tmaxs[0] = (x+1-CHUNKBIAS) * hm->sectionsize;\n\t\tmaxs[1] = (y+1-CHUNKBIAS) * hm->sectionsize;\n\t\tmaxs[2] = s->maxh_cull;\n\t\tmodel->funcs.FindTouchedLeafs(model, &s->pvscache, mins, maxs);\n\t}\n\n#ifdef GLQUAKE\n\t#if 0\n\tif (qrenderer == QR_OPENGL && qglGenBuffersARB)\n\t{\n\t\tvbobctx_t ctx;\n\t\tsize_t vertsize = sizeof(*mesh->xyz_array)+sizeof(*mesh->st_array)+sizeof(*mesh->lmst_array)+(mesh->colors4f_array?sizeof(*mesh->colors4f_array):0);\n\t\tBE_VBO_Begin(&ctx, vertsize * mesh->numvertexes);\n\t\tBE_VBO_Data(&ctx, mesh->xyz_array, sizeof(*mesh->xyz_array) * mesh->numvertexes, &s->vbo.coord);\n\t\tBE_VBO_Data(&ctx, mesh->st_array, sizeof(*mesh->st_array) * mesh->numvertexes, &s->vbo.texcoord);\n\t\tBE_VBO_Data(&ctx, mesh->lmst_array, sizeof(*mesh->lmst_array) * mesh->numvertexes, &s->vbo.lmcoord[0]);\n\t\tif (mesh->colors4f_array)\n\t\t\tBE_VBO_Data(&ctx, mesh->colors4f_array, sizeof(*mesh->colors4f_array) * mesh->numvertexes, &s->vbo.colours[0]);\n\t\tBE_VBO_Finish(&ctx, mesh->indexes, sizeof(*mesh->indexes)*mesh->numindexes, &s->vbo.indicies, NULL, NULL);\n\t}\n\t#else\n\tif (qrenderer == QR_OPENGL && qglGenBuffersARB)\n\t{\n\t\tif (!s->vbo.coord.gl.vbo)\n\t\t{\n\t\t\tqglGenBuffersARB(1, &s->vbo.coord.gl.vbo);\n\t\t\tGL_SelectVBO(s->vbo.coord.gl.vbo);\n\t\t}\n\t\telse\n\t\t\tGL_SelectVBO(s->vbo.coord.gl.vbo);\n\n\t\tqglBufferDataARB(GL_ARRAY_BUFFER_ARB, (sizeof(vecV_t)+sizeof(vec2_t)+sizeof(vec2_t)+sizeof(vec4_t)) * (mesh->numvertexes), NULL, GL_STATIC_DRAW_ARB);\n\n\t\tqglBufferSubDataARB(GL_ARRAY_BUFFER_ARB, 0, (sizeof(vecV_t)+sizeof(vec2_t)+sizeof(vec2_t)) * mesh->numvertexes, mesh->xyz_array);\n\t\tif (mesh->colors4f_array[0])\n\t\t\tqglBufferSubDataARB(GL_ARRAY_BUFFER_ARB, (sizeof(vecV_t)+sizeof(vec2_t)+sizeof(vec2_t)) * mesh->numvertexes, sizeof(vec4_t)*mesh->numvertexes,  mesh->colors4f_array[0]);\n\t\tGL_SelectVBO(0);\n\t\ts->vbo.coord.gl.addr = 0;\n\t\ts->vbo.texcoord.gl.addr = (void*)((char*)mesh->st_array - (char*)mesh->xyz_array);\n\t\ts->vbo.texcoord.gl.vbo = s->vbo.coord.gl.vbo;\n\t\ts->vbo.lmcoord[0].gl.addr = (void*)((char*)mesh->lmst_array[0] - (char*)mesh->xyz_array);\n\t\ts->vbo.lmcoord[0].gl.vbo = s->vbo.coord.gl.vbo;\n\t\ts->vbo.colours[0].gl.addr = (void*)((sizeof(vecV_t)+sizeof(vec2_t)+sizeof(vec2_t)) * mesh->numvertexes);\n\t\ts->vbo.colours[0].gl.vbo = s->vbo.coord.gl.vbo;\n\n\t\tif (!s->vbo.indicies.gl.vbo)\n\t\t\tqglGenBuffersARB(1, &s->vbo.indicies.gl.vbo);\n\t\ts->vbo.indicies.gl.addr = 0;\n\t\tGL_SelectEBO(s->vbo.indicies.gl.vbo);\n\t\tqglBufferDataARB(GL_ELEMENT_ARRAY_BUFFER_ARB, sizeof(index_t) * mesh->numindexes, mesh->indexes, GL_STATIC_DRAW_ARB);\n\t\tGL_SelectEBO(0);\n\n#if 1\n\t\tZ_Free(mesh->xyz_array);\n\t\tmesh->xyz_array = NULL;\n\t\tmesh->st_array = NULL;\n\t\tmesh->lmst_array[0] = NULL;\n\n\t\tZ_Free(mesh->indexes);\n\t\tmesh->indexes = NULL;\n#endif\n\t}\n\t#endif\n#endif\n#ifdef VKQUAKE\n\tif (qrenderer == QR_VULKAN)\n\t{\n\t\tvoid VKBE_GenBatchVBOs(vbo_t **vbochain, batch_t *firstbatch, batch_t *stopbatch);\n\t\tbatch_t batch = {0};\n\t\tmesh_t *meshes = &s->mesh;\n\t\tvbo_t *vbo = NULL;\n\t\tbatch.maxmeshes = 1;\n\t\tbatch.mesh = &meshes;\n\n\t\tVKBE_GenBatchVBOs(&vbo, &batch, NULL);\n\t\ts->vbo = *vbo;\n\t}\n#endif\n#ifdef D3D9QUAKE\n\tif (qrenderer == QR_DIRECT3D9)\n\t{\n\t\tvoid D3D9BE_GenBatchVBOs(vbo_t **vbochain, batch_t *firstbatch, batch_t *stopbatch);\n\t\tbatch_t batch = {0};\n\t\tmesh_t *meshes = &s->mesh;\n\t\tvbo_t *vbo = NULL;\n\t\tbatch.maxmeshes = 1;\n\t\tbatch.mesh = &meshes;\n\n\t\t//BE_ClearVBO(&s->vbo);\n\t\tD3D9BE_GenBatchVBOs(&vbo, &batch, NULL);\n\t\ts->vbo = *vbo;\n\t}\n#endif\n#ifdef D3D11QUAKE\n\tif (qrenderer == QR_DIRECT3D11)\n\t{\n\t\tvoid D3D11BE_GenBatchVBOs(vbo_t **vbochain, batch_t *firstbatch, batch_t *stopbatch);\n\t\tbatch_t batch = {0};\n\t\tmesh_t *meshes = &s->mesh;\n\t\tvbo_t *vbo = NULL;\n\t\tbatch.maxmeshes = 1;\n\t\tbatch.mesh = &meshes;\n\n\t\t//BE_ClearVBO(&s->vbo);\n\t\tD3D11BE_GenBatchVBOs(&vbo, &batch, NULL);\n\t\ts->vbo = *vbo;\n\t}\n#endif\n}\n\nstruct tdibctx\n{\n\theightmap_t *hm;\n\tint vx;\n\tint vy;\n\tentity_t *ent;\n\tbatch_t **batches;\n\tqbyte *pvs;\n\tmodel_t *wmodel;\n};\nvoid Terr_DrawInBounds(struct tdibctx *ctx, int x, int y, int w, int h)\n{\n\tvec3_t mins, maxs;\n\thmsection_t *s;\n\tstruct hmwater_s *wa;\n\tint i, j;\n\tbatch_t *b;\n\theightmap_t *hm = ctx->hm;\n\n\tmins[0] = (x+0 - CHUNKBIAS)*hm->sectionsize;\n\tmaxs[0] = (x+w - CHUNKBIAS)*hm->sectionsize;\n\n\tmins[1] = (y+0 - CHUNKBIAS)*hm->sectionsize;\n\tmaxs[1] = (y+h - CHUNKBIAS)*hm->sectionsize;\n\n\tmins[2] = r_origin[2]-999999;\n\tmaxs[2] = r_origin[2]+999999;\n\n\tif (w == 1 && h == 1)\n\t{\n//\t\tif (R_CullBox(mins, maxs))\n//\t\t\treturn;\n\n\t\ts = Terr_GetSection(hm, x, y, TGS_LAZYLOAD);\n\t\tif (!s)\n\t\t\treturn;\n\n\t\t/*move to head*/\n\t\tvalidatelinks(&hm->recycle);\n\t\tRemoveLink(&s->recycle);\n\t\tvalidatelinks(&hm->recycle);\n\t\tInsertLinkBefore(&s->recycle, &hm->recycle);\n\t\tvalidatelinks(&hm->recycle);\n\n\t\tif (s->lightmap < 0)\n\t\t\tTerr_LoadSection(hm, s, x, y, TGS_NODOWNLOAD);\n\n\t\tif (s->flags & TSF_RELIGHT)\n\t\t{\n\t\t\tif (!hm->relight)\n\t\t\t{\n\t\t\t\thm->relight = s;\n\t\t\t\thm->relightidx = 0;\n\t\t\t\thm->relightmin[0] = mins[0];\n\t\t\t\thm->relightmin[1] = mins[1];\n\t\t\t}\n\t\t}\n\n\t\tif (s->flags & TSF_DIRTY)\n\t\t{\n\t\t\ts->flags &= ~TSF_DIRTY;\n\n\t\t\tTerr_RebuildMesh(ctx->wmodel, s, x, y);\n\t\t}\n\n\t\tif (ctx->pvs && !ctx->wmodel->funcs.EdictInFatPVS(ctx->wmodel, &s->pvscache, ctx->pvs, NULL))\n\t\t\treturn;\t//this section isn't in any visible bsp leafs\n\n\t\tif (s->numents)\n\t\t{\n\t\t\tSys_LockMutex(hm->entitylock);\n\t\t\t//chuck out any batches for models in this section\n\t\t\tfor (i = 0; i < s->numents; i++)\n\t\t\t{\n\t\t\t\tstruct hmentity_s *e = s->ents[i];\n\t\t\t\tvec3_t dist;\n\t\t\t\tfloat a, dmin, dmax;\n\t\t\t\tmodel_t *model;\n\t\t\t\t//skip the entity if its already been added to some batch this frame.\n\t\t\t\tif (e->drawnframe == hm->drawnframe)\n\t\t\t\t\tcontinue;\n\t\t\t\te->drawnframe = hm->drawnframe;\n\n\t\t\t\tmodel = e->ent.model;\n\t\t\t\tif (!model)\n\t\t\t\t\tcontinue;\n\n\t\t\t\tif (model->loadstate == MLS_NOTLOADED)\n\t\t\t\t{\n\t//\t\t\t\tif (hm->beinglazy)\n\t//\t\t\t\t\tcontinue;\n\t//\t\t\t\thm->beinglazy = true;\n\t\t\t\t\tMod_LoadModel(model, MLV_WARN);\n\t\t\t\t}\n\t\t\t\tif (model->loadstate != MLS_LOADED)\n\t\t\t\t\tcontinue;\n\n\t\t\t\tVectorSubtract(e->ent.origin, r_origin, dist);\n\t\t\t\ta = VectorLength(dist);\n\t\t\t\tdmin = 1024 + model->radius*160;\n\t\t\t\tdmax = dmin + 1024;\n\t\t\t\ta = (a - dmin) / (dmax - dmin);\n\t\t\t\ta = 1-a;\n\t\t\t\tif (a < 0)\n\t\t\t\t\tcontinue;\n\n\t\t\t\tif (R_CullSphere(e->ent.origin, model->radius))\n\t\t\t\t\tcontinue;\n\n\t\t\t\tif (a >= 1)\n\t\t\t\t{\n\t\t\t\t\ta = 1;\n\t\t\t\t\te->ent.flags &= ~RF_TRANSLUCENT;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\te->ent.flags |= RF_TRANSLUCENT;\n\t\t\t\te->ent.shaderRGBAf[3] = a;\n\t\t\t\tswitch(model->type)\n\t\t\t\t{\n\t\t\t\tcase mod_alias:\n\t\t\t\t\tR_GAlias_GenerateBatches(&e->ent, ctx->batches);\n\t\t\t\t\tbreak;\n\t\t\t\tcase mod_brush:\n\t\t\t\t\tSurf_GenBrushBatches(ctx->batches, &e->ent);\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\t//FIXME: no sprites! oh noes!\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tSys_UnlockMutex(hm->entitylock);\n\t\t}\n\n\t\tfor (wa = s->water; wa; wa = wa->next)\n\t\t{\n\t\t\tmins[2] = wa->minheight;\n\t\t\tmaxs[2] = wa->maxheight;\n\t\t\tif (!R_CullBox(mins, maxs))\n\t\t\t{\n\t\t\t\tTerr_DrawTerrainWater(hm, mins, maxs, wa);\n\t\t\t}\n\t\t}\n\n\t\tmins[2] = s->minh;\n\t\tmaxs[2] = s->maxh;\n\n//\t\tif (!BoundsIntersect(mins, maxs, r_refdef.vieworg, r_refdef.vieworg))\n\t\t\tif (R_CullBox(mins, maxs))\n\t\t\t\treturn;\n\n\n\t\tif (hm->texmask)\n\t\t{\n\t\t\tfor (i = 0; i < 4; i++)\n\t\t\t{\n\t\t\t\tif (!*s->texname[i])\n\t\t\t\t\tbreak;\n\t\t\t\tif (!strcmp(s->texname[i], hm->texmask))\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (i == 4)\n\t\t\t{\t//flicker if the surface cannot accept the named texture\n\t\t\t\tint xor = (x&1)^(y&1);\n\t\t\t\tif (((int)(realtime*10) & 1) ^ xor)\n\t\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\tb = BE_GetTempBatch();\n\t\tif (!b)\n\t\t\treturn;\n\t\tb->ent = ctx->ent;\n\t\tb->shader = hm->shader;\n\t\tb->flags = 0;\n\t\tb->mesh = &s->amesh;\n\t\tb->mesh[0] = &s->mesh;\n\t\tb->meshes = 1;\n\t\tb->buildmeshes = NULL;\n\t\tb->skin = &s->textures;\n\t\tb->texture = NULL;\n\t\tb->vbo = &s->vbo;\n\t\tb->lightmap[0] = s->lightmap;\n\t\tfor (j = 1; j < MAXRLIGHTMAPS; j++)\n\t\t\tb->lightmap[j] = -1;\n\n\t\tb->next = ctx->batches[b->shader->sort];\n\t\tctx->batches[b->shader->sort] = b;\n\t}\n\telse if (w && h)\n\t{\n\t\t//divide and conquer, radiating outwards from the view.\n\t\tif (w > h)\n\t\t{\n\t\t\ti = x + w;\n\t\t\tw = x + w/2;\n\t\t\tif (ctx->vx >= w)\n\t\t\t{\n\t\t\t\tTerr_DrawInBounds(ctx, w, y, i-w, h);\n\t\t\t\tTerr_DrawInBounds(ctx, x, y, w-x, h);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tTerr_DrawInBounds(ctx, x, y, w-x, h);\n\t\t\t\tTerr_DrawInBounds(ctx, w, y, i-w, h);\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\ti = y + h;\n\t\t\th = y + h/2;\n\t\t\tif (ctx->vy >= h)\n\t\t\t{\n\t\t\t\tTerr_DrawInBounds(ctx, x, h, w, i-h);\n\t\t\t\tTerr_DrawInBounds(ctx, x, y, w, h-y);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tTerr_DrawInBounds(ctx, x, y, w, h-y);\n\t\t\t\tTerr_DrawInBounds(ctx, x, h, w, i-h);\n\t\t\t}\n\t\t}\n\t}\n}\n\nvoid Terr_DrawTerrainModel (batch_t **batches, entity_t *e)\n{\n\tmodel_t *m = e->model;\n\theightmap_t *hm = m->terrain;\n\tbatch_t *b;\n\tint bounds[4], j;\n\tstruct tdibctx tdibctx;\n\n\tif (!r_refdef.recurse)\n\t{\n\t\tTerr_DoEditNotify(hm);\n//\t\twhile (hm->activesections > 0)\n//\t\t\tif (!Terr_Collect(hm))\n//\t\t\t\tbreak;\n\t\twhile (hm->activesections > TERRAINACTIVESECTIONS)\n\t\t{\n\t\t\tif (!Terr_Collect(hm))\n\t\t\t\tbreak;\n\t\t\tbreak;\n\t\t}\n\t}\n\t\n//\thm->beinglazy = false;\n\tif (hm->relight)\n\t\tted_dorelight(m, hm);\n\n\tif (e->model == cl.worldmodel && hm->skyshader)\n\t{\n\t\tb = BE_GetTempBatch();\n\t\tif (b)\n\t\t{\n\t\t\tfor (j = 0; j < MAXRLIGHTMAPS; j++)\n\t\t\t\tb->lightmap[j] = -1;\n\t\t\tb->ent = e;\n\t\t\tb->shader = hm->skyshader;\n\t\t\tb->flags = 0;\n\t\t\tb->mesh = &hm->askymesh;\n\t\t\tb->mesh[0] = &hm->skymesh;\n\t\t\tb->meshes = 1;\n\t\t\tb->buildmeshes = NULL;\n\t\t\tb->skin = NULL;\n\t\t\tb->texture = NULL;\n\t//\t\tvbo = b->vbo = hm->vbo[x+y*MAXSECTIONS];\n\t\t\tb->vbo = NULL;\n\n\t\t\tb->next = batches[b->shader->sort];\n\t\t\tbatches[b->shader->sort] = b;\n\t\t}\n\t}\n\n\tTerr_Brush_Draw(hm, batches, e);\n\n\tif ((r_refdef.globalfog.density&&r_refdef.globalfog.alpha>=1) || r_refdef.maxdist>0)\n\t{\n\t\tfloat culldist;\n\t\textern cvar_t r_fog_exp2;\n\n\t\tif (r_refdef.globalfog.density&&r_refdef.globalfog.alpha>=1)\n\t\t{\t//fogalpha<1 means you can always see through it, so don't cull when its invisible.\n\t\t\t//figure out the eyespace distance required to reach that fog value\n\t\t\tculldist = log(0.5/255.0f);\n\t\t\tif (r_fog_exp2.ival)\n\t\t\t\tculldist = sqrt(culldist / (-r_refdef.globalfog.density * r_refdef.globalfog.density));\n\t\t\telse\n\t\t\t\tculldist = culldist / (-r_refdef.globalfog.density);\n\t\t\t//anything drawn beyond this point is fully obscured by fog\n\t\t\tculldist += 4096;\n\t\t}\n\t\telse\n\t\t\tculldist = 999999999999999.f;\n\n\t\tif (culldist < hm->maxdrawdist)\n\t\t\tculldist = hm->maxdrawdist;\n\t\tif (culldist > r_refdef.maxdist && r_refdef.maxdist>0)\n\t\t\tculldist = r_refdef.maxdist;\n\n\t\tbounds[0] = bound(hm->firstsegx, (r_refdef.vieworg[0] + (CHUNKBIAS + 0)*hm->sectionsize - culldist) / hm->sectionsize,  hm->maxsegx);\n\t\tbounds[1] = bound(hm->firstsegx, (r_refdef.vieworg[0] + (CHUNKBIAS + 1)*hm->sectionsize + culldist) / hm->sectionsize,  hm->maxsegx);\n\t\tbounds[2] = bound(hm->firstsegy, (r_refdef.vieworg[1] + (CHUNKBIAS + 0)*hm->sectionsize - culldist) / hm->sectionsize,  hm->maxsegy);\n\t\tbounds[3] = bound(hm->firstsegy, (r_refdef.vieworg[1] + (CHUNKBIAS + 1)*hm->sectionsize + culldist) / hm->sectionsize,  hm->maxsegy);\n\t}\n\telse\n\t{\n\t\tbounds[0] = hm->firstsegx;\n\t\tbounds[1] = hm->maxsegx;\n\t\tbounds[2] = hm->firstsegy;\n\t\tbounds[3] = hm->maxsegy;\n\t}\n\t//FIXME: project the near+far clip planes onto the screen, generate bounds from those, instead of the above overkill code.\n\n\thm->drawnframe+=1;\n\ttdibctx.hm = hm;\n\ttdibctx.batches = batches;\n\ttdibctx.ent = e;\n\ttdibctx.vx = (r_refdef.vieworg[0] + CHUNKBIAS*hm->sectionsize) / hm->sectionsize;\n\ttdibctx.vy = (r_refdef.vieworg[1] + CHUNKBIAS*hm->sectionsize) / hm->sectionsize;\n\ttdibctx.wmodel = e->model;\n\ttdibctx.pvs = (e->model == cl.worldmodel)?r_refdef.scenevis:NULL;\nvalidatelinks(&hm->recycle);\n\tTerr_DrawInBounds(&tdibctx, bounds[0], bounds[2], bounds[1]-bounds[0], bounds[3]-bounds[2]);\n\nvalidatelinks(&hm->recycle);\n\t/*{\n\ttrace_t trace;\n\tvec3_t player_mins = {-16, -16, -24};\n\tvec3_t player_maxs = {16, 16, 32};\n\tvec3_t start, end;\n\tVectorCopy(cl.playerview[0].simorg, start);\n\tVectorCopy(start, end);\n\tstart[0] += 5;\n\tend[2] -= 100;\n\tHeightmap_Trace(cl.worldmodel, 0, 0, NULL, start, end, player_mins, player_maxs, false, ~0, &trace);\n\t}*/\n}\n\nvoid Terrain_ClipDecal(fragmentdecal_t *dec, float *center, float radius, model_t *model)\n{\n\tint min[2], max[2], mint[2], maxt[2];\n\tint x, y, tx, ty;\n\tvecV_t vert[6];\n\thmsection_t *s;\n\theightmap_t *hm = model->terrain; \n\tmin[0] = floor((center[0] - radius)/(hm->sectionsize)) + CHUNKBIAS;\n\tmin[1] = floor((center[1] - radius)/(hm->sectionsize)) + CHUNKBIAS;\n\tmax[0] = ceil((center[0] + radius)/(hm->sectionsize)) + CHUNKBIAS;\n\tmax[1] = ceil((center[1] + radius)/(hm->sectionsize)) + CHUNKBIAS;\n\n\tmin[0] = bound(hm->firstsegx, min[0], hm->maxsegx);\n\tmin[1] = bound(hm->firstsegy, min[1], hm->maxsegy);\n\tmax[0] = bound(hm->firstsegx, max[0], hm->maxsegx);\n\tmax[1] = bound(hm->firstsegy, max[1], hm->maxsegy);\n\n\tfor (y = min[1]; y < max[1]; y++)\n\t{\n\t\tfor (x = min[0]; x < max[0]; x++)\n\t\t{\n\t\t\ts = Terr_GetSection(hm, x, y, TGS_WAITLOAD);\n\t\t\tif (!s)\n\t\t\t\tcontinue;\n\n\t\t\tmint[0] = floor((center[0] - radius)*(SECTHEIGHTSIZE-1)/(hm->sectionsize) + (CHUNKBIAS - x)*(SECTHEIGHTSIZE-1));\n\t\t\tmint[1] = floor((center[1] - radius)*(SECTHEIGHTSIZE-1)/(hm->sectionsize) + (CHUNKBIAS - y)*(SECTHEIGHTSIZE-1));\n\t\t\tmaxt[0] =  ceil((center[0] + radius)*(SECTHEIGHTSIZE-1)/(hm->sectionsize) + (CHUNKBIAS - x)*(SECTHEIGHTSIZE-1));\n\t\t\tmaxt[1] =  ceil((center[1] + radius)*(SECTHEIGHTSIZE-1)/(hm->sectionsize) + (CHUNKBIAS - y)*(SECTHEIGHTSIZE-1));\n\n\t\t\tmint[0] = bound(0, mint[0], (SECTHEIGHTSIZE-1));\n\t\t\tmint[1] = bound(0, mint[1], (SECTHEIGHTSIZE-1));\n\t\t\tmaxt[0] = bound(0, maxt[0], (SECTHEIGHTSIZE-1));\n\t\t\tmaxt[1] = bound(0, maxt[1], (SECTHEIGHTSIZE-1));\n\n\t\t\tfor (ty = mint[1]; ty < maxt[1]; ty++)\n\t\t\t{\n\t\t\t\tfor (tx = mint[0]; tx < maxt[0]; tx++)\n\t\t\t\t{\n#ifndef STRICTEDGES\n\t\t\t\t\tfloat d1, d2;\n\t\t\t\t\td1 = fabs(s->heights[(tx+0) + (ty+0)*SECTHEIGHTSIZE] - s->heights[(tx+1) + (ty+1)*SECTHEIGHTSIZE]);\n\t\t\t\t\td2 = fabs(s->heights[(tx+1) + (ty+0)*SECTHEIGHTSIZE] - s->heights[(tx+0) + (ty+1)*SECTHEIGHTSIZE]);\n\t\t\t\t\tif (d1 < d2)\n\t\t\t\t\t{\n\t\t\t\t\t\tvert[0][0] = (x-CHUNKBIAS)*hm->sectionsize + (tx+0)/(float)(SECTHEIGHTSIZE-1)*hm->sectionsize;vert[0][1] = (y-CHUNKBIAS)*hm->sectionsize + (ty+0)/(float)(SECTHEIGHTSIZE-1)*hm->sectionsize;\n\t\t\t\t\t\tvert[1][0] = (x-CHUNKBIAS)*hm->sectionsize + (tx+1)/(float)(SECTHEIGHTSIZE-1)*hm->sectionsize;vert[1][1] = (y-CHUNKBIAS)*hm->sectionsize + (ty+1)/(float)(SECTHEIGHTSIZE-1)*hm->sectionsize;\n\t\t\t\t\t\tvert[2][0] = (x-CHUNKBIAS)*hm->sectionsize + (tx+1)/(float)(SECTHEIGHTSIZE-1)*hm->sectionsize;vert[2][1] = (y-CHUNKBIAS)*hm->sectionsize + (ty+0)/(float)(SECTHEIGHTSIZE-1)*hm->sectionsize;\n\n\t\t\t\t\t\tvert[3][0] = (x-CHUNKBIAS)*hm->sectionsize + (tx+0)/(float)(SECTHEIGHTSIZE-1)*hm->sectionsize;vert[3][1] = (y-CHUNKBIAS)*hm->sectionsize + (ty+0)/(float)(SECTHEIGHTSIZE-1)*hm->sectionsize;\n\t\t\t\t\t\tvert[4][0] = (x-CHUNKBIAS)*hm->sectionsize + (tx+0)/(float)(SECTHEIGHTSIZE-1)*hm->sectionsize;vert[4][1] = (y-CHUNKBIAS)*hm->sectionsize + (ty+1)/(float)(SECTHEIGHTSIZE-1)*hm->sectionsize;\n\t\t\t\t\t\tvert[5][0] = (x-CHUNKBIAS)*hm->sectionsize + (tx+1)/(float)(SECTHEIGHTSIZE-1)*hm->sectionsize;vert[5][1] = (y-CHUNKBIAS)*hm->sectionsize + (ty+1)/(float)(SECTHEIGHTSIZE-1)*hm->sectionsize;\n\n\t\t\t\t\t\tvert[0][2] = s->heights[(tx+0) + (ty+0)*SECTHEIGHTSIZE];\n\t\t\t\t\t\tvert[1][2] = s->heights[(tx+1) + (ty+1)*SECTHEIGHTSIZE];\n\t\t\t\t\t\tvert[2][2] = s->heights[(tx+1) + (ty+0)*SECTHEIGHTSIZE];\n\t\t\t\t\t\tvert[3][2] = s->heights[(tx+0) + (ty+0)*SECTHEIGHTSIZE];\n\t\t\t\t\t\tvert[4][2] = s->heights[(tx+0) + (ty+1)*SECTHEIGHTSIZE];\n\t\t\t\t\t\tvert[5][2] = s->heights[(tx+1) + (ty+1)*SECTHEIGHTSIZE];\n\t\t\t\t\t}\n\t\t\t\t\telse\n#endif\n\t\t\t\t\t{\n\t\t\t\t\t\tvert[0][0] = (x-CHUNKBIAS)*hm->sectionsize + (tx+0)/(float)(SECTHEIGHTSIZE-1)*hm->sectionsize;vert[0][1] = (y-CHUNKBIAS)*hm->sectionsize + (ty+0)/(float)(SECTHEIGHTSIZE-1)*hm->sectionsize;\n\t\t\t\t\t\tvert[1][0] = (x-CHUNKBIAS)*hm->sectionsize + (tx+0)/(float)(SECTHEIGHTSIZE-1)*hm->sectionsize;vert[1][1] = (y-CHUNKBIAS)*hm->sectionsize + (ty+1)/(float)(SECTHEIGHTSIZE-1)*hm->sectionsize;\n\t\t\t\t\t\tvert[2][0] = (x-CHUNKBIAS)*hm->sectionsize + (tx+1)/(float)(SECTHEIGHTSIZE-1)*hm->sectionsize;vert[2][1] = (y-CHUNKBIAS)*hm->sectionsize + (ty+0)/(float)(SECTHEIGHTSIZE-1)*hm->sectionsize;\n\n\t\t\t\t\t\tvert[3][0] = (x-CHUNKBIAS)*hm->sectionsize + (tx+1)/(float)(SECTHEIGHTSIZE-1)*hm->sectionsize;vert[3][1] = (y-CHUNKBIAS)*hm->sectionsize + (ty+0)/(float)(SECTHEIGHTSIZE-1)*hm->sectionsize;\n\t\t\t\t\t\tvert[4][0] = (x-CHUNKBIAS)*hm->sectionsize + (tx+0)/(float)(SECTHEIGHTSIZE-1)*hm->sectionsize;vert[4][1] = (y-CHUNKBIAS)*hm->sectionsize + (ty+1)/(float)(SECTHEIGHTSIZE-1)*hm->sectionsize;\n\t\t\t\t\t\tvert[5][0] = (x-CHUNKBIAS)*hm->sectionsize + (tx+1)/(float)(SECTHEIGHTSIZE-1)*hm->sectionsize;vert[5][1] = (y-CHUNKBIAS)*hm->sectionsize + (ty+1)/(float)(SECTHEIGHTSIZE-1)*hm->sectionsize;\n\n\t\t\t\t\t\tvert[0][2] = s->heights[(tx+0) + (ty+0)*SECTHEIGHTSIZE];\n\t\t\t\t\t\tvert[1][2] = s->heights[(tx+0) + (ty+1)*SECTHEIGHTSIZE];\n\t\t\t\t\t\tvert[2][2] = s->heights[(tx+1) + (ty+0)*SECTHEIGHTSIZE];\n\t\t\t\t\t\tvert[3][2] = s->heights[(tx+1) + (ty+0)*SECTHEIGHTSIZE];\n\t\t\t\t\t\tvert[4][2] = s->heights[(tx+0) + (ty+1)*SECTHEIGHTSIZE];\n\t\t\t\t\t\tvert[5][2] = s->heights[(tx+1) + (ty+1)*SECTHEIGHTSIZE];\n\t\t\t\t\t}\n\n\t\t\t\t\t//fixme: per-section shaders for clutter info. this kinda sucks.\n\t\t\t\t\tFragment_ClipPoly(dec, 3, &vert[0][0], hm->shader);\n\t\t\t\t\tFragment_ClipPoly(dec, 3, &vert[3][0], hm->shader);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n#endif\n\nunsigned int Heightmap_PointContentsHM(heightmap_t *hm, float clipmipsz, const vec3_t org)\n{\n\tfloat x, y;\n\tfloat z, tz;\n\tint sx, sy;\n\tunsigned int holerow;\n\tunsigned int holebit;\n\thmsection_t *s;\n\tstruct hmwater_s *w;\n\tunsigned int contents;\n\tconst float wbias = CHUNKBIAS * hm->sectionsize;\n\n\tsx = (org[0]+wbias)/hm->sectionsize;\n\tsy = (org[1]+wbias)/hm->sectionsize;\n\tif (sx < hm->firstsegx || sy < hm->firstsegy)\n\t\treturn hm->exteriorcontents;\n\tif (sx >= hm->maxsegx || sy >= hm->maxsegy)\n\t\treturn hm->exteriorcontents;\n\ts = Terr_GetSection(hm, sx, sy, TGS_TRYLOAD | TGS_ANYSTATE);\n\tif (!s || s->loadstate != TSLS_LOADED)\n\t{\n\t\tif (s && s->loadstate == TSLS_FAILED)\n\t\t\treturn hm->exteriorcontents;\n\t\treturn FTECONTENTS_SOLID;\n\t}\n\n\tx = (org[0]+wbias - (sx*hm->sectionsize))*(SECTHEIGHTSIZE-1)/hm->sectionsize;\n\ty = (org[1]+wbias - (sy*hm->sectionsize))*(SECTHEIGHTSIZE-1)/hm->sectionsize;\n\tz = (org[2]+clipmipsz);\n\n\tif (z < s->minh-16)\n\t\treturn hm->exteriorcontents;\n\n\tsx = x; x-=sx;\n\tsy = y; y-=sy;\n\n\tholerow = ((sy<<3)/(SECTHEIGHTSIZE-1));\n\tholebit = 1u<<((sx<<3)/(SECTHEIGHTSIZE-1));\n\tif (s->holes[holerow] & (1u<<holebit))\n\t\treturn FTECONTENTS_EMPTY;\n\n\t//made of two triangles:\n\tif (x+y>1)\t//the 1, 1 triangle\n\t{\n\t\tfloat v1, v2, v3;\n\t\tv3 = 1-y;\n\t\tv2 = x+y-1;\n\t\tv1 = 1-x;\n\t\t//0, 1\n\t\t//1, 1\n\t\t//1, 0\n\t\ttz = (s->heights[(sx+0)+(sy+1)*SECTHEIGHTSIZE]*v1 +\n\t\t\t  s->heights[(sx+1)+(sy+1)*SECTHEIGHTSIZE]*v2 +\n\t\t\t  s->heights[(sx+1)+(sy+0)*SECTHEIGHTSIZE]*v3);\n\t}\n\telse\n\t{\n\t\tfloat v1, v2, v3;\n\t\tv1 = y;\n\t\tv2 = x;\n\t\tv3 = 1-y-x;\n\n\t\t//0, 1\n\t\t//1, 0\n\t\t//0, 0\n\t\ttz = (s->heights[(sx+0)+(sy+1)*SECTHEIGHTSIZE]*v1 +\n\t\t\t  s->heights[(sx+1)+(sy+0)*SECTHEIGHTSIZE]*v2 +\n\t\t\t  s->heights[(sx+0)+(sy+0)*SECTHEIGHTSIZE]*v3);\n\t}\n\tif (z <= tz)\n\t\treturn FTECONTENTS_SOLID;\t//contained within\n\n\tcontents = FTECONTENTS_EMPTY;\n\tfor (w = s->water; w; w = w->next)\n\t{\n\t\tif (w->holes[holerow] & (1u<<holebit))\n\t\t\tcontinue;\n\t\tif (z < w->maxheight)\t//FIXME\n\t\t\tcontents |= w->contentmask;\n\t}\n\treturn contents;\n}\n\nunsigned int Heightmap_PointContents(model_t *model, const vec3_t axis[3], const vec3_t org)\n{\n\theightmap_t *hm = model->terrain;\n\tunsigned int cont;\n\tbrushes_t *br;\n\tunsigned int i, j;\n\tfloat dist;\n\n\tcont = Heightmap_PointContentsHM(hm, 0, org);\n\tif (cont & FTECONTENTS_SOLID)\n\t\treturn cont;\n\n\n\tfor (i = 0; i < hm->numbrushes; i++)\n\t{\n\t\tbr = &hm->wbrushes[i];\n\t\tif (br->patch)\n\t\t\tcontinue;\t//infinitely thin...\n\n\t\tfor (j = 0; j < br->numplanes; j++)\n\t\t{\n\t\t\t/*\n\t\t\tfor (k=0 ; k<3 ; k++)\n\t\t\t{\n\t\t\t\tif (in_normals[j][k] < 0)\n\t\t\t\t\tbest[k] = br->maxs[k];\n\t\t\t\telse\n\t\t\t\t\tbest[k] = br->mins[k];\n\t\t\t}\n\t\t\t*/\n\t\t\tdist = DotProduct (org/*best*/, br->planes[j]);\n\t\t\tdist = br->planes[j][3] - dist;\n\t\t\tif (dist < 0)\n\t\t\t\tbreak;\n\t\t}\n\t\tif (j == br->numplanes)\n\t\t{\n\t\t\tcont |= br->contents;\n\t\t}\n\t}\n\n\treturn cont;\n}\nunsigned int Heightmap_NativeBoxContents(model_t *model, int hulloverride, const framestate_t *framestate, const vec3_t axis[3], const vec3_t org, const vec3_t mins, const vec3_t maxs)\n{\n\theightmap_t *hm = model->terrain;\n\treturn Heightmap_PointContentsHM(hm, mins[2], org);\n}\n\nfloat Heightmap_Normal(heightmap_t *hm, vec2_t org, vec3_t norm)\t//returns the z\n{\n#if 0\n\tfloat z = 0;\n\tnorm[0] = 0;\n\tnorm[1] = 0;\n\tnorm[2] = 1;\n#else\n\tfloat x, y;\n\tint sx, sy;\n\tvec3_t d1, d2;\n\tconst float wbias = CHUNKBIAS * hm->sectionsize;\n\thmsection_t *s;\n\tfloat z;\n\n\tnorm[0] = 0;\n\tnorm[1] = 0;\n\tnorm[2] = 1;\n\n\tsx = (org[0]+wbias)/hm->sectionsize;\n\tsy = (org[1]+wbias)/hm->sectionsize;\n\tif (sx < hm->firstsegx || sy < hm->firstsegy)\n\t\treturn hm->defaultgroundheight;\n\tif (sx >= hm->maxsegx || sy >= hm->maxsegy)\n\t\treturn hm->defaultgroundheight;\n\ts = Terr_GetSection(hm, sx, sy, TGS_TRYLOAD);\n\tif (!s)\n\t\treturn hm->defaultgroundheight;\n\n\tx = (org[0]+wbias - (sx*hm->sectionsize))*(SECTHEIGHTSIZE-1)/hm->sectionsize;\n\ty = (org[1]+wbias - (sy*hm->sectionsize))*(SECTHEIGHTSIZE-1)/hm->sectionsize;\n\n\tsx = x; x-=sx;\n\tsy = y; y-=sy;\n\n\tif (x+y>1)\t//the 1, 1 triangle\n\t{\n\t\t//0, 1\n\t\t//1, 1\n\t\t//1, 0\n\t\td1[0] = (hm->sectionsize / SECTHEIGHTSIZE);\n\t\td1[1] = 0;\n\t\td1[2] = (s->heights[(sx+1)+(sy+1)*SECTHEIGHTSIZE] - s->heights[(sx+0)+(sy+1)*SECTHEIGHTSIZE]);\n\t\td2[0] = 0;\n\t\td2[1] = (hm->sectionsize / SECTHEIGHTSIZE);\n\t\td2[2] = (s->heights[(sx+1)+(sy+1)*SECTHEIGHTSIZE] - s->heights[(sx+1)+(sy+0)*SECTHEIGHTSIZE]);\n\n\t\tz = (s->heights[(sx+0)+(sy+1)*SECTHEIGHTSIZE]*(1-y) +\n\t\t\t s->heights[(sx+1)+(sy+1)*SECTHEIGHTSIZE]*(x+y-1) +\n\t\t\t s->heights[(sx+1)+(sy+0)*SECTHEIGHTSIZE]*(1-x));\n\t}\n\telse\n\t{\t//the 0,0 triangle\n\t\t//0, 1\n\t\t//1, 0\n\t\t//0, 0\n\t\td1[0] = (hm->sectionsize / SECTHEIGHTSIZE);\n\t\td1[1] = 0;\n\t\td1[2] = (s->heights[(sx+1)+(sy+0)*SECTHEIGHTSIZE] - s->heights[(sx+0)+(sy+0)*SECTHEIGHTSIZE]);\n\t\td2[0] = 0;\n\t\td2[1] = (hm->sectionsize / SECTHEIGHTSIZE);\n\t\td2[2] = (s->heights[(sx+0)+(sy+1)*SECTHEIGHTSIZE] - s->heights[(sx+0)+(sy+0)*SECTHEIGHTSIZE]);\n\n\t\tz = (s->heights[(sx+0)+(sy+1)*SECTHEIGHTSIZE]*(y) +\n\t\t\t s->heights[(sx+1)+(sy+0)*SECTHEIGHTSIZE]*(x) +\n\t\t\t s->heights[(sx+0)+(sy+0)*SECTHEIGHTSIZE]*(1-y-x));\n\t}\n\n\n\tVectorNormalize(d1);\n\tVectorNormalize(d2);\n\tCrossProduct(d1, d2, norm);\n\tVectorNormalize(norm);\n#endif\n\treturn z;\n}\n\ntypedef struct {\n\tvec3_t start;\n\tvec3_t end;\n\tvec3_t impact;\n\tvec4_t plane;\n\tvec3_t mins;\n\tvec3_t maxs;\n\tvec3_t absmins;\n\tvec3_t absmaxs;\n\tvec3_t up;\n\tvec3_t capsulesize;\n\tenum {ispoint, iscapsule, isbox} shape;\n\tqboolean startsolid;\n\tdouble nearfrac;\n\tfloat truefrac;\n\tfloat htilesize;\n\theightmap_t *hm;\n\tint contents;\n\tint hitcontentsmask;\n\ttrace_t *result;\n\n#ifdef _DEBUG\n\tqboolean debug;\n#endif\n} hmtrace_t;\n\n#ifdef HAVE_CLIENT\nshader_t *Terr_GetShader(model_t *mod, trace_t *trace)\n{\n\n\theightmap_t\t\t*hm\t\t\t\t= mod?mod->terrain:NULL;\n\tunsigned int\tbrushid\t\t\t= trace->brush_id;\n\tunsigned int\tfa, i;\n\tbrushes_t\t\t*br;\n\n\tif (!brushid)\n\t\treturn NULL;\n\n\tif (!hm)\n\t\treturn NULL;\n\n\tfor (i = 0; i < hm->numbrushes; i++)\n\t{\n\t\tbr = &hm->wbrushes[i];\n\t\tif (br->id == trace->brush_id)\n\t\t{\n\t\t\tif (br->patch)\n\t\t\t\treturn br->patch->tex->shader;\n\t\t\tfa = trace->brush_face-1;\n\t\t\tif (fa >= br->numplanes)\n\t\t\t\treturn NULL;\n\t\t\treturn br->faces[fa].tex->shader;\n\t\t}\n\t}\n\treturn NULL;\n}\n#endif\n\nstatic int Heightmap_Trace_Brush(hmtrace_t *tr, vec4_t *planes, int numplanes, brushes_t *brushinfo)\n{\n\tqboolean startout;\n\tfloat *enterplane;\n\tdouble enterfrac, exitfrac, nearfrac=0;\n\tdouble enterdist=0;\n\tdouble dist, d1, d2, f;\n\tunsigned int i, j;\n\tvec3_t ofs;\n\n\tstartout = false;\n\tenterplane= NULL;\n\tenterfrac = -1;\n\texitfrac = 10;\n\tfor (i = 0; i < numplanes; i++)\n\t{\n\t\t/*calculate the distance based upon the shape of the object we're tracing for*/\n\t\tswitch(tr->shape)\n\t\t{\n\t\tdefault:\n\t\tcase isbox: // general box case\n\t\t\t// push the plane out apropriately for mins/maxs\n\n\t\t\t// FIXME: use signbits into 8 way lookup for each mins/maxs\n\t\t\tfor (j=0 ; j<3 ; j++)\n\t\t\t{\n\t\t\t\tif (planes[i][j] < 0)\n\t\t\t\t\tofs[j] = tr->maxs[j];\n\t\t\t\telse\n\t\t\t\t\tofs[j] = tr->mins[j];\n\t\t\t}\n\t\t\tdist = DotProduct (ofs, planes[i]);\n\t\t\tdist = planes[i][3] - dist;\n\t\t\tbreak;\n\t\tcase iscapsule:\n\t\t\tdist = DotProduct(tr->up, planes[i]);\n\t\t\tdist = dist*(tr->capsulesize[(dist<0)?1:2]) - tr->capsulesize[0];\n\t\t\tdist = planes[i][3] - dist;\n\t\t\tbreak;\n\t\tcase ispoint: // special point case\n\t\t\tdist = planes[i][3];\n\t\t\tbreak;\n\t\t}\n\n\n\t\td1 = DotProduct (tr->start, planes[i]) - dist;\n\t\td2 = DotProduct (tr->end, planes[i]) - dist;\n\n\t\t//if we're fully outside any plane, then we cannot possibly enter the brush, skip to the next one\n\t\tif (d1 > 0 && d2 >= d1)\n\t\t\treturn false;\n\n\t\tif (d1 > 0)\n\t\t\tstartout = true;\n\n\t\t//if we're fully inside the plane, then whatever is happening is not relevent for this plane\n\t\tif (d1 < 0 && d2 <= 0)\n\t\t\tcontinue;\n\n\t\tf = (d1) / (d1-d2);\n\t\tif (d1 > d2)\n\t\t{\n\t\t\t//entered the brush. favour the furthest fraction to avoid extended edges (yay for convex shapes)\n\t\t\tif (enterfrac < f)\n\t\t\t{\n\t\t\t\tenterfrac = f;\n\t\t\t\tnearfrac = (d1 - (0.03125)) / (d1-d2);\n\t\t\t\tenterplane = planes[i];\n\t\t\t\tenterdist = dist;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\t//left the brush, favour the nearest plane (smallest frac)\n\t\t\tif (exitfrac > f)\n\t\t\t{\n\t\t\t\texitfrac = f;\n\t\t\t}\n\t\t}\n\t}\n\n\t//non-point traces need to clip against the brush's edges\n\tif (brushinfo && tr->shape != ispoint && brushinfo->axialplanes != 0x3f)\n\t{\n\t\tstatic vec3_t axis[] = {{1,0,0},{0,1,0},{0,0,1},{-1,0,0},{0,-1,0},{0,0,-1}};\n\t\tfor (i = 0; i < 6; i++)\n\t\t{\n//\t\t\tif (brushinfo->axialplanes & (1u<<i))\n//\t\t\t\tcontinue;\t//should have already checked this plane.\n\t\t\tif (i >= 3)\n\t\t\t{\n\t\t\t\t/*calculate the distance based upon the shape of the object we're tracing for*/\n\t\t\t\tswitch(tr->shape)\n\t\t\t\t{\n\t\t\t\tdefault:\n\t\t\t\tcase isbox:\n\t\t\t\t\tdist = -tr->maxs[i-3];\n\t\t\t\t\tdist = -brushinfo->mins[i-3] - dist;\n\t\t\t\t\tbreak;\n\t\t\t\tcase iscapsule:\n\t\t\t\t\tdist = -tr->up[i-3];\n\t\t\t\t\tdist = dist*(tr->capsulesize[(dist<0)?1:2]) - tr->capsulesize[0];\n\t\t\t\t\tdist = -brushinfo->mins[i-3] - dist;\n\t\t\t\t\tbreak;\n\t\t\t\tcase ispoint:\n\t\t\t\t\tdist = -brushinfo->mins[i-3];\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\td1 = -tr->start[i-3] - dist;\n\t\t\t\td2 = -tr->end[i-3] - dist;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tswitch(tr->shape)\n\t\t\t\t{\n\t\t\t\tdefault:\n\t\t\t\tcase isbox:\n\t\t\t\t\tdist = brushinfo->maxs[i] - tr->mins[i];\n\t\t\t\t\tbreak;\n\t\t\t\tcase iscapsule:\n\t\t\t\t\tdist = tr->up[i];\n\t\t\t\t\tdist = dist*(tr->capsulesize[(dist<0)?1:2]) - tr->capsulesize[0];\n\t\t\t\t\tdist = brushinfo->maxs[i] - dist;\n\t\t\t\t\tbreak;\n\t\t\t\tcase ispoint:\n\t\t\t\t\tdist = brushinfo->maxs[i];\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\td1 = (tr->start[i]) - dist;\n\t\t\t\td2 = (tr->end[i]) - dist;\n\t\t\t}\n\n\t\t\t//if we're fully outside any plane, then we cannot possibly enter the brush, skip to the next one\n\t\t\tif (d1 > 0 && d2 >= d1)\n\t\t\t\treturn false;\n\n\t\t\tif (d1 > 0)\n\t\t\t\tstartout = true;\n\n\t\t\t//if we're fully inside the plane, then whatever is happening is not relevent for this plane\n\t\t\tif (d1 <= 0 && d2 <= 0)\n\t\t\t\tcontinue;\n\n\t\t\tf = (d1) / (d1-d2);\n\t\t\tif (d1 > d2)\n\t\t\t{\n\t\t\t\t//entered the brush. favour the furthest fraction to avoid extended edges (yay for convex shapes)\n\t\t\t\tif (enterfrac < f)\n\t\t\t\t{\n\t\t\t\t\tenterfrac = f;\n\t\t\t\t\tnearfrac = (d1 - (0.03125)) / (d1-d2);\n\t\t\t\t\tenterplane = axis[i];\n\t\t\t\t\tenterdist = dist;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t//left the brush, favour the nearest plane (smallest frac)\n\t\t\t\tif (exitfrac > f)\n\t\t\t\t{\n\t\t\t\t\texitfrac = f;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tif (!startout)\n\t{\n\n#if 0//def _DEBUG\n\tif (tr->debug)\n\t{\n\t\tvecV_t\t\t\tfacepoints[256];\n\t\tunsigned int\tnumpoints;\n\n\t\tfor (i = 0; i < numplanes; i++)\n\t\t{\n\t\t\tscenetris_t *t;\n\t\t\textern shader_t *shader_draw_fill;\n\t\t\t//generate points now (so we know the correct mins+maxs for the brush, and whether the plane is relevent)\n\t\t\tnumpoints = Terr_GenerateBrushFace(facepoints, countof(facepoints), planes, numplanes, planes[i]);\n\n\n\t\t\tif (cl_numstrisvert+numpoints > cl_maxstrisvert)\n\t\t\t\tbreak;\n\t\t\tif (cl_numstrisidx+(numpoints-2)*3 > cl_maxstrisidx)\n\t\t\t\tbreak;\n\n\t\t\tif (cl_numstris == cl_maxstris)\n\t\t\t{\n\t\t\t\tcl_maxstris+=8;\n\t\t\t\tcl_stris = BZ_Realloc(cl_stris, sizeof(*cl_stris)*cl_maxstris);\n\t\t\t}\n\t\t\tt = &cl_stris[cl_numstris++];\n\t\t\tt->shader = shader_draw_fill;\n\t\t\tt->flags = 0;\n\t\t\tt->firstidx = cl_numstrisidx;\n\t\t\tt->firstvert = cl_numstrisvert;\n\t\t\tfor (j = 2; j < numpoints; j++)\n\t\t\t{\n\t\t\t\tcl_strisidx[cl_numstrisidx++] = 0;\n\t\t\t\tcl_strisidx[cl_numstrisidx++] = j-1;\n\t\t\t\tcl_strisidx[cl_numstrisidx++] = j;\n\t\t\t}\n\t\t\tfor (j = 0; j < numpoints; j++)\n\t\t\t{\n\t\t\t\tVectorCopy(facepoints[j], cl_strisvertv[cl_numstrisvert]);\n\t\t\t\tcl_strisvertv[cl_numstrisvert][2] += 1;\n\t\t\t\tVector4Set(cl_strisvertc[cl_numstrisvert], 1, 0, 0, 0.2);\n\t\t\t\tVector2Set(cl_strisvertt[cl_numstrisvert], 0, 0);\n\t\t\t\tcl_numstrisvert++;\n\t\t\t}\n\t\t\tt->numidx = cl_numstrisidx - t->firstidx;\n\t\t\tt->numvert = cl_numstrisvert-t->firstvert;\n\t\t}\n\t}\n#endif\n\n\n\t\ttr->startsolid = true;\n\t\treturn false;\n\t}\n\tif (enterfrac != -1 && enterfrac < exitfrac)\n\t{\n\t\t//impact!\n\t\tif (enterfrac < tr->truefrac)\n\t\t{\n\t\t\tif (nearfrac < 0)\n\t\t\t\tnearfrac = 0;\n\t\t\ttr->nearfrac = nearfrac;\n\t\t\ttr->truefrac = enterfrac;\n\t\t\ttr->plane[3] = enterdist;\n\t\t\tVectorCopy(enterplane, tr->plane);\n\n\n#if 0//def _DEBUG\n\tif (tr->debug)\n\t{\n\t\tvecV_t\t\t\tfacepoints[256];\n\t\tunsigned int\tnumpoints;\n\n\t\tfor (i = 0; i < numplanes; i++)\n\t\t{\n\t\t\tscenetris_t *t;\n\t\t\textern shader_t *shader_draw_fill;\n\t\t\t//generate points now (so we know the correct mins+maxs for the brush, and whether the plane is relevent)\n\t\t\tnumpoints = Terr_GenerateBrushFace(facepoints, countof(facepoints), planes, numplanes, planes[i]);\n\n\n\t\t\tif (cl_numstrisvert+numpoints > cl_maxstrisvert)\n\t\t\t\tbreak;\n\t\t\tif (cl_numstrisidx+(numpoints-2)*3 > cl_maxstrisidx)\n\t\t\t\tbreak;\n\n\t\t\tif (cl_numstris == cl_maxstris)\n\t\t\t{\n\t\t\t\tcl_maxstris+=8;\n\t\t\t\tcl_stris = BZ_Realloc(cl_stris, sizeof(*cl_stris)*cl_maxstris);\n\t\t\t}\n\t\t\tt = &cl_stris[cl_numstris++];\n\t\t\tt->shader = shader_draw_fill;\n\t\t\tt->flags = 0;\n\t\t\tt->firstidx = cl_numstrisidx;\n\t\t\tt->firstvert = cl_numstrisvert;\n\t\t\tfor (j = 2; j < numpoints; j++)\n\t\t\t{\n\t\t\t\tcl_strisidx[cl_numstrisidx++] = 0;\n\t\t\t\tcl_strisidx[cl_numstrisidx++] = j-1;\n\t\t\t\tcl_strisidx[cl_numstrisidx++] = j;\n\t\t\t}\n\t\t\tfor (j = 0; j < numpoints; j++)\n\t\t\t{\n\t\t\t\tVectorCopy(facepoints[j], cl_strisvertv[cl_numstrisvert]);\n\t\t\t\tcl_strisvertv[cl_numstrisvert][2] += 1;\n\t\t\t\tVector4Set(cl_strisvertc[cl_numstrisvert], 0, 1, 0, 0.2);\n\t\t\t\tVector2Set(cl_strisvertt[cl_numstrisvert], 0, 0);\n\t\t\t\tcl_numstrisvert++;\n\t\t\t}\n\t\t\tt->numidx = cl_numstrisidx - t->firstidx;\n\t\t\tt->numvert = cl_numstrisvert-t->firstvert;\n\t\t}\n\t}\n#endif\n\n\n\t\t\treturn ((vec4_t*)enterplane - planes)+1;\n\t\t}\n\t}\n\treturn false;\n}\n\nstatic qboolean Heightmap_Trace_Quad(hmtrace_t *tr, const float *v0, const float *v1, const float *v2, const float *v3)\n{\n\t//super lame shite. be lazy and just use a bbox\n\tstatic vec4_t n[6] = {\n\t\t{-1, 0, 0, 0},\n\t\t{ 0,-1, 0, 0},\n\t\t{ 0, 0,-1, 0},\n\t\t{ 1, 0, 0, 0},\n\t\t{ 0, 1, 0, 0},\n\t\t{ 0, 0, 1, 0},\n\t};\n\tvec3_t d[2];\n\tconst float epsilon = 1.0/64;\n\tVectorCopy(v0, d[0]);\n\tVectorCopy(v0, d[1]);\n\tAddPointToBounds(v1, d[0], d[1]);\n\tAddPointToBounds(v2, d[0], d[1]);\n\tAddPointToBounds(v3, d[0], d[1]);\n\n//I'm implementing this primarily for selecting patches.\n//decals are often infinitely thin things.\n//so expand them by a tiny amount in the hopes that traces will hit patches before the wall they're coplanar with.\n\tn[0][3] =-d[0][0]+epsilon;\n\tn[1][3] =-d[0][1]+epsilon;\n\tn[2][3] =-d[0][2]+epsilon;\n\tn[3][3] = d[1][0]+epsilon;\n\tn[4][3] = d[1][1]+epsilon;\n\tn[5][3] = d[1][2]+epsilon;\n\n\treturn Heightmap_Trace_Brush(tr, n, 6, NULL) != 0;\n}\nstatic qboolean Heightmap_Trace_Patch(hmtrace_t *tr, brushes_t *brushinfo)\n{\n\tconst struct patchdata_s *patch = brushinfo->patch;\n\tunsigned int w, h, x, y;\n\tqboolean ret = false;\n\n\tif (!patch->tessvert)\n\t{\n\t\tconst struct qcpatchvert_s *r1 = patch->cp, *r2;\n\t\tw = patch->numcp[0];\n\t\th = patch->numcp[1];\n\n\t\tfor (y = 0, r2 = r1 + w; y < h-1; y++)\n\t\t{\n\t\t\tfor (x = 0; x < w-1; x++, r1++, r2++)\n\t\t\t\tret |= Heightmap_Trace_Quad(tr, r1[0].v, r1[1].v, r2[0].v, r1[1].v);\n\t\t\tr1++; r2++;\n\t\t}\n\t}\n\telse\n\t{\n\t\tconst struct patchtessvert_s *r1 = patch->tessvert, *r2;\n\t\tw = patch->tesssize[0];\n\t\th = patch->tesssize[1];\n\n\t\tfor (y = 0, r2 = r1 + w; y < h-1; y++)\n\t\t{\n\t\t\tfor (x = 0; x < w-1; x++, r1++, r2++)\n\t\t\t\tret |= Heightmap_Trace_Quad(tr, r1[0].v, r1[1].v, r2[0].v, r1[1].v);\n\t\t\tr1++; r2++;\n\t\t}\n\t}\n\treturn ret;\n}\n\n//sx,sy are the tile coord\n//note that tile SECTHEIGHTSIZE-1 does not exist, as the last sample overlaps the first sample of the next section\nstatic void Heightmap_Trace_Square(hmtrace_t *tr, int tx, int ty)\n{\n\tvec3_t d[2];\n\tvec3_t p[4];\n\tvec4_t n[6];\n\tint i;\n\n#ifndef STRICTEDGES\n\tfloat d1, d2;\n#endif\n\tint sx, sy;\n\thmsection_t *s;\n\tunsigned int holerow;\n\tunsigned int holebit;\n\n\tsx = tx/(SECTHEIGHTSIZE-1);\n\tsy = ty/(SECTHEIGHTSIZE-1);\n\tif (sx < tr->hm->firstsegx || sx >= tr->hm->maxsegx ||\n\t\tsy < tr->hm->firstsegy || sy >= tr->hm->maxsegy)\n\t\ts = NULL;\n\telse\n\t\ts = Terr_GetSection(tr->hm, sx, sy, TGS_TRYLOAD|TGS_WAITLOAD|TGS_ANYSTATE);\n\n\tif (!s || s->loadstate != TSLS_LOADED)\n\t{\n\t\tif ((tr->hitcontentsmask & tr->hm->exteriorcontents) || (s && s->loadstate != TSLS_FAILED))\n\t\t{\n\t\t\t//you're not allowed to walk into sections that have not loaded.\n\t\t\t//might as well check the entire section instead of just one tile\n\t\t\tVector4Set(n[0],  1, 0, 0, (tx/(SECTHEIGHTSIZE-1) + 1 - CHUNKBIAS)*tr->hm->sectionsize);\n\t\t\tVector4Set(n[1], -1, 0, 0, -(tx/(SECTHEIGHTSIZE-1) + 0 - CHUNKBIAS)*tr->hm->sectionsize);\n\t\t\tVector4Set(n[2], 0,  1, 0, (ty/(SECTHEIGHTSIZE-1) + 1 - CHUNKBIAS)*tr->hm->sectionsize);\n\t\t\tVector4Set(n[3], 0, -1, 0, -(ty/(SECTHEIGHTSIZE-1) + 0 - CHUNKBIAS)*tr->hm->sectionsize);\n\t\t\tHeightmap_Trace_Brush(tr, n, 4, NULL);\n\t\t}\n\t\treturn;\n\t}\n\n\tif (s->traceseq != tr->hm->traceseq && s->numents)\n\t{\n\t\ts->traceseq = tr->hm->traceseq;\n\t\tSys_LockMutex(tr->hm->entitylock);\n\t\tfor (i = 0; i < s->numents; i++)\n\t\t{\n\t\t\tvec3_t start_l, end_l;\n\t\t\ttrace_t etr;\n\t\t\tmodel_t *model;\n\t\t\tif (s->ents[i]->traceseq == tr->hm->traceseq)\n\t\t\t\tcontinue;\n\t\t\ts->ents[i]->traceseq = tr->hm->traceseq;\n\t\t\tmodel = s->ents[i]->ent.model;\n\t\t\t//FIXME: IGNORE the entity if it isn't loaded yet? surely that's bad?\n\t\t\tif (!model || model->loadstate != MLS_LOADED || !model->funcs.NativeTrace)\n\t\t\t\tcontinue;\n\t\t\t//figure out where on the submodel the trace is.\n\t\t\tVectorSubtract (tr->start, s->ents[i]->ent.origin, start_l);\n\t\t\tVectorSubtract (tr->end, s->ents[i]->ent.origin, end_l);\n//\t\t\tstart_l[2] -= tr->mins[2];\n//\t\t\tend_l[2] -= tr->mins[2];\n\t\t\tVectorScale(start_l, s->ents[i]->ent.scale, start_l);\n\t\t\tVectorScale(end_l, s->ents[i]->ent.scale, end_l);\n\n\t\t\t//skip if the local trace points are outside the model's bounds\n/*\t\t\tfor (j = 0; j < 3; j++)\n\t\t\t{\n\t\t\t\tif (start_l[j]+tr->mins[j] > model->maxs[j] && end_l[j]+tr->mins[j] > model->maxs[j])\n\t\t\t\t\tcontinue;\n\t\t\t\tif (start_l[j]+tr->maxs[j] < model->mins[j] && end_l[j]+tr->maxs[j] < model->mins[j])\n\t\t\t\t\tcontinue;\n\t\t\t}\n*/\n\t\t\t//do the trace\n\t\t\tmemset(&etr, 0, sizeof(etr));\n\t\t\tetr.fraction = 1;\n\t\t\tmodel->funcs.NativeTrace (model, 0, &s->ents[i]->ent.framestate, s->ents[i]->ent.axis, start_l, end_l, tr->mins, tr->maxs, tr->shape == iscapsule, tr->hitcontentsmask, &etr);\n\n\t\t\tif (etr.startsolid)\n\t\t\t{\t//many many bsp objects are not enclosed 'properly' (qbsp strips any surfaces outside the world).\n\t\t\t\t//this means that such bsps extend to infinity, resulting in sudden glitchy stuck issues when you enter a section containing such a bsp\n\t\t\t\t//so if we started solid, constrain that solidity to the volume of the submodel\n\t\t\t\tVectorCopy  (s->ents[i]->ent.axis[0], n[0]);\n\t\t\t\tVectorNegate(s->ents[i]->ent.axis[0], n[1]);\n\t\t\t\tVectorCopy  (s->ents[i]->ent.axis[1], n[2]);\n\t\t\t\tVectorNegate(s->ents[i]->ent.axis[1], n[3]);\n\t\t\t\tVectorCopy  (s->ents[i]->ent.axis[2], n[4]);\n\t\t\t\tVectorNegate(s->ents[i]->ent.axis[2], n[5]);\n\t\t\t\tn[0][3] = DotProduct(n[0], s->ents[i]->ent.origin) + model->maxs[0];\n\t\t\t\tn[1][3] = DotProduct(n[1], s->ents[i]->ent.origin) + -model->mins[0];\n\t\t\t\tn[2][3] = DotProduct(n[2], s->ents[i]->ent.origin) + model->maxs[1];\n\t\t\t\tn[3][3] = DotProduct(n[3], s->ents[i]->ent.origin) + -model->mins[1];\n\t\t\t\tn[4][3] = DotProduct(n[4], s->ents[i]->ent.origin) + model->maxs[2];\n\t\t\t\tn[5][3] = DotProduct(n[5], s->ents[i]->ent.origin) + -model->mins[2];\n\t\t\t\tHeightmap_Trace_Brush(tr, n, 6, NULL);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\ttr->result->startsolid |= etr.startsolid;\n\t\t\t\ttr->result->allsolid |= etr.allsolid;\n\t\t\t\tif (etr.fraction < tr->nearfrac)\n\t\t\t\t{\n\t\t\t\t\ttr->contents = etr.contents;\n\t\t\t\t\ttr->truefrac = etr.truefraction;\n\t\t\t\t\ttr->nearfrac = etr.fraction;\n\t\t\t\t\ttr->plane[3] = etr.plane.dist;\n\t\t\t\t\ttr->plane[0] = etr.plane.normal[0];\n\t\t\t\t\ttr->plane[1] = etr.plane.normal[1];\n\t\t\t\t\ttr->plane[2] = etr.plane.normal[2];\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tSys_UnlockMutex(tr->hm->entitylock);\n\t}\n\n\tsx = tx - CHUNKBIAS*(SECTHEIGHTSIZE-1);\n\tsy = ty - CHUNKBIAS*(SECTHEIGHTSIZE-1);\n\n\ttx = tx % (SECTHEIGHTSIZE-1);\n\tty = ty % (SECTHEIGHTSIZE-1);\n\n\tholerow = ((ty<<3)/(SECTHEIGHTSIZE-1));\n\tholebit = 1u<<((tx<<3)/(SECTHEIGHTSIZE-1));\n\tif (s->holes[holerow] & holebit)\n\t\treturn;\t//no collision with holes\n\n\tswitch(tr->hm->mode)\n\t{\n\tcase HMM_BLOCKS:\n\t\t//left-most\n\t\tVector4Set(n[0], -1, 0, 0, -tr->htilesize*(sx+0));\n\t\t//bottom-most\n\t\tVector4Set(n[1], 0, 1, 0, tr->htilesize*(sy+1));\n\t\t//right-most\n\t\tVector4Set(n[2], 1, 0, 0, tr->htilesize*(sx+1));\n\t\t//top-most\n\t\tVector4Set(n[3], 0, -1, 0, -tr->htilesize*(sy+0));\n\t\t//top\n\t\tVector4Set(n[4], 0, 0, 1, s->heights[(tx+0)+(ty+0)*SECTHEIGHTSIZE]);\n\n\t\tHeightmap_Trace_Brush(tr, n, 5, NULL);\n\t\treturn;\n\tcase HMM_TERRAIN:\n\t\tVectorSet(p[0], tr->htilesize*(sx+0), tr->htilesize*(sy+0), s->heights[(tx+0)+(ty+0)*SECTHEIGHTSIZE]);\n\t\tVectorSet(p[1], tr->htilesize*(sx+1), tr->htilesize*(sy+0), s->heights[(tx+1)+(ty+0)*SECTHEIGHTSIZE]);\n\t\tVectorSet(p[2], tr->htilesize*(sx+0), tr->htilesize*(sy+1), s->heights[(tx+0)+(ty+1)*SECTHEIGHTSIZE]);\n\t\tVectorSet(p[3], tr->htilesize*(sx+1), tr->htilesize*(sy+1), s->heights[(tx+1)+(ty+1)*SECTHEIGHTSIZE]);\n\n\t\tVectorSet(n[5], 0, 0, 1);\n#ifndef STRICTEDGES\n\t\td1 = fabs(p[0][2] - p[3][2]);\n\t\td2 = fabs(p[1][2] - p[2][2]);\n\t\tif (d1 < d2)\n\t\t{\n\t\t\t/*generate the brush (in world space*/\n\t\t\t{\n\t\t\t\tVectorSubtract(p[3], p[0], d[0]);\n\t\t\t\tVectorSubtract(p[2], p[0], d[1]);\n\t\t\t\t//left-most\n\t\t\t\tVector4Set(n[0], -1, 0, 0, -tr->htilesize*(sx+0));\n\t\t\t\t//bottom-most\n\t\t\t\tVector4Set(n[1], 0, 1, 0, tr->htilesize*(sy+1));\n\t\t\t\t//top-right\n\t\t\t\tVectorSet(n[2], 0.70710678118654752440084436210485, -0.70710678118654752440084436210485, 0);\n\t\t\t\tn[2][3] = DotProduct(n[2], p[0]);\n\t\t\t\t//top\n\t\t\t\tVectorNormalize(d[0]);\n\t\t\t\tVectorNormalize(d[1]);\n\t\t\t\tCrossProduct(d[0], d[1], n[3]);\n\t\t\t\tVectorNormalize(n[3]);\n\t\t\t\tn[3][3] = DotProduct(n[3], p[0]);\n\t\t\t\t//down\n\t\t\t\tVectorNegate(n[3], n[4]);\n\t\t\t\tn[4][3] = DotProduct(n[4], p[0]) - n[4][2]*TERRAINTHICKNESS;\n\n\t\t\t\tn[5][3] = max(p[0][2], p[2][2]);\n\t\t\t\tn[5][3] = max(n[5][3], p[3][2]);\n\t\t\t\tHeightmap_Trace_Brush(tr, n, 6, NULL);\n\t\t\t}\n\n\t\t\t{\n\t\t\t\tVectorSubtract(p[3], p[0], d[0]);\n\t\t\t\tVectorSubtract(p[3], p[1], d[1]);\n\n\t\t\t\t//right-most\n\t\t\t\tVector4Set(n[0], 1, 0, 0, tr->htilesize*(sx+1));\n\t\t\t\t//top-most\n\t\t\t\tVector4Set(n[1], 0, -1, 0, -tr->htilesize*(sy+0));\n\t\t\t\t//bottom-left\n\t\t\t\tVectorSet(n[2], -0.70710678118654752440084436210485, 0.70710678118654752440084436210485, 0);\n\t\t\t\tn[2][3] = DotProduct(n[2], p[0]);\n\t\t\t\t//top\n\t\t\t\tVectorNormalize(d[0]);\n\t\t\t\tVectorNormalize(d[1]);\n\t\t\t\tCrossProduct(d[0], d[1], n[3]);\n\t\t\t\tVectorNormalize(n[3]);\n\t\t\t\tn[3][3] = DotProduct(n[3], p[0]);\n\t\t\t\t//down\n\t\t\t\tVectorNegate(n[3], n[4]);\n\t\t\t\tn[4][3] = DotProduct(n[4], p[0]) - n[4][2]*TERRAINTHICKNESS;\n\n\t\t\t\tn[5][3] = max(p[0][2], p[1][2]);\n\t\t\t\tn[5][3] = max(n[5][3], p[3][2]);\n\t\t\t\tHeightmap_Trace_Brush(tr, n, 6, NULL);\n\t\t\t}\n\t\t}\n\t\telse\n#endif\n\t\t{\n\t\t\t/*generate the brush (in world space*/\n\t\t\t{\n\t\t\t\tVectorSubtract(p[1], p[0], d[0]);\n\t\t\t\tVectorSubtract(p[2], p[0], d[1]);\n\t\t\t\t//left-most\n\t\t\t\tVector4Set(n[0], -1, 0, 0, -tr->htilesize*(sx+0));\n\t\t\t\t//top-most\n\t\t\t\tVector4Set(n[1], 0, -1, 0, -tr->htilesize*(sy+0));\n\t\t\t\t//bottom-right\n\t\t\t\tVectorSet(n[2], 0.70710678118654752440084436210485, 0.70710678118654752440084436210485, 0);\n\t\t\t\tn[2][3] = DotProduct(n[2], p[1]);\n\t\t\t\t//top\n\t\t\t\tVectorNormalize(d[0]);\n\t\t\t\tVectorNormalize(d[1]);\n\t\t\t\tCrossProduct(d[0], d[1], n[3]);\n\t\t\t\tVectorNormalize(n[3]);\n\t\t\t\tn[3][3] = DotProduct(n[3], p[1]);\n\t\t\t\t//down\n\t\t\t\tVectorNegate(n[3], n[4]);\n\t\t\t\tn[4][3] = DotProduct(n[4], p[1]) - n[4][2]*TERRAINTHICKNESS;\n\n\t\t\t\tn[5][3] = max(p[0][2], p[1][2]);\n\t\t\t\tn[5][3] = max(n[5][3], p[2][2]);\n\t\t\t\tHeightmap_Trace_Brush(tr, n, 6, NULL);\n\t\t\t}\n\t\t\t{\n\t\t\t\tVectorSubtract(p[3], p[2], d[0]);\n\t\t\t\tVectorSubtract(p[3], p[1], d[1]);\n\n\t\t\t\t//right-most\n\t\t\t\tVector4Set(n[0], 1, 0, 0, tr->htilesize*(sx+1));\n\t\t\t\t//bottom-most\n\t\t\t\tVector4Set(n[1], 0, 1, 0, tr->htilesize*(sy+1));\n\t\t\t\t//top-left\n\t\t\t\tVectorSet(n[2], -0.70710678118654752440084436210485, -0.70710678118654752440084436210485, 0);\n\t\t\t\tn[2][3] = DotProduct(n[2], p[1]);\n\t\t\t\t//top\n\t\t\t\tVectorNormalize(d[0]);\n\t\t\t\tVectorNormalize(d[1]);\n\t\t\t\tCrossProduct(d[0], d[1], n[3]);\n\t\t\t\tVectorNormalize(n[3]);\n\t\t\t\tn[3][3] = DotProduct(n[3], p[1]);\n\t\t\t\t//down\n\t\t\t\tVectorNegate(n[3], n[4]);\n\t\t\t\tn[4][3] = DotProduct(n[4], p[1]) - n[4][2]*TERRAINTHICKNESS;\n\n\t\t\t\tn[5][3] = max(p[1][2], p[2][2]);\n\t\t\t\tn[5][3] = max(n[5][3], p[3][2]);\n\t\t\t\tHeightmap_Trace_Brush(tr, n, 6, NULL);\n\t\t\t}\n\t\t}\n\t\tbreak;\n\t}\n}\n\n#define DIST_EPSILON 0\n/*\nHeightmap_TraceRecurse\nTraces an arbitary box through a heightmap. (interface with outside)\n\nWhy is recursion good?\n1: it is consistant with bsp models. :)\n2: it allows us to use any size model we want\n3: we don't have to work out the height of the terrain every X units, but can be more precise.\n\nObviously, we don't care all that much about 1\n*/\nqboolean Heightmap_Trace(struct model_s *model, int hulloverride, const framestate_t *framestate, const vec3_t mataxis[3], const vec3_t start, const vec3_t end, const vec3_t mins, const vec3_t maxs, qboolean capsule, unsigned int against, struct trace_s *trace)\n{\n\tvec2_t pos;\n\tvec2_t frac;\n\tvec2_t emins;\n\tvec2_t emaxs;\n\tvec3_t tmp;\n\tint ipos[2], npos[2];\n\tint x, y, e;\n\tint axis;\n\tint breaklimit = 1000;\n\tfloat zbias;\n\thmtrace_t hmtrace;\n\thmtrace.hm = model->terrain;\n\thmtrace.hm->traceseq++;\n\thmtrace.htilesize = hmtrace.hm->sectionsize / (SECTHEIGHTSIZE-1);\n\thmtrace.nearfrac = hmtrace.truefrac = 1;\n\thmtrace.contents = 0;\n\thmtrace.hitcontentsmask = against;\n\n\thmtrace.plane[0] = 0;\n\thmtrace.plane[1] = 0;\n\thmtrace.plane[2] = 0;\n\thmtrace.plane[3] = 0;\n\tif (capsule)\n\t{\n\t\thmtrace.shape = iscapsule;\n\t\tzbias = 0;\n\n\t\tif (mataxis)\n\t\t\tVectorSet(hmtrace.up, mataxis[0][2], -mataxis[1][2], mataxis[2][2]);\n\t\telse\n\t\t\tVectorSet(hmtrace.up, 0, 0, 1);\n\n\t\t//determine the capsule sizes\n\t\thmtrace.capsulesize[0] = ((maxs[0]-mins[0]) + (maxs[1]-mins[1]))/4.0;\n\t\thmtrace.capsulesize[1] = maxs[2];\n\t\thmtrace.capsulesize[2] = mins[2];\n//\t\tzbias = (trace_capsulesize[1] > -hmtrace.capsulesize[2])?hmtrace.capsulesize[1]:-hmtrace.capsulesize[2];\n\t\thmtrace.capsulesize[1] -= hmtrace.capsulesize[0];\n\t\thmtrace.capsulesize[2] += hmtrace.capsulesize[0];\n\n\t\tzbias = 0;\n\t}\n\telse if (mins[0] || mins[1] || mins[2] || maxs[0] || maxs[1] || maxs[2])\n\t{\n\t\thmtrace.shape = isbox;\n\t\tzbias = 0;\n\t}\n\telse\n\t{\n\t\thmtrace.shape = ispoint;\n\t\tzbias = mins[2];\n\t}\n\n\tmemset(trace, 0, sizeof(*trace));\n\thmtrace.result = trace;\n\thmtrace.startsolid = false;\n\n\t//to tile space\n\thmtrace.start[0] = (start[0]);\n\thmtrace.start[1] = (start[1]);\n\thmtrace.start[2] = (start[2] + zbias);\n\thmtrace.end[0] = (end[0]);\n\thmtrace.end[1] = (end[1]);\n\thmtrace.end[2] = (end[2] + zbias);\n\n//\tmins = vec3_origin;\n//\tmaxs = vec3_origin;\n\n\tVectorCopy(mins, hmtrace.mins);\n\tVectorCopy(maxs, hmtrace.maxs);\n\n\t//determine extents\n\tVectorAdd(hmtrace.start, hmtrace.mins, hmtrace.absmins);\n\tVectorCopy(hmtrace.absmins, hmtrace.absmaxs);\n\tVectorAdd(hmtrace.start, hmtrace.maxs, tmp);\n\tAddPointToBounds (tmp, hmtrace.absmins, hmtrace.absmaxs);\n\tVectorAdd(hmtrace.end, hmtrace.mins, tmp);\n\tAddPointToBounds (tmp, hmtrace.absmins, hmtrace.absmaxs);\n\tVectorAdd(hmtrace.end, hmtrace.maxs, tmp);\n\tAddPointToBounds (tmp, hmtrace.absmins, hmtrace.absmaxs);\n\thmtrace.absmaxs[0] += 1;\n\thmtrace.absmaxs[1] += 1;\n\thmtrace.absmaxs[2] += 1;\n\thmtrace.absmins[0] -= 1;\n\thmtrace.absmins[1] -= 1;\n\thmtrace.absmins[2] -= 1;\n\n\t//figure out where we are in terms of tiles\n\tpos[0] = (hmtrace.start[0]+CHUNKBIAS*hmtrace.hm->sectionsize)/hmtrace.htilesize;\n\tpos[1] = (hmtrace.start[1]+CHUNKBIAS*hmtrace.hm->sectionsize)/hmtrace.htilesize;\n\n\temins[0] = (mins[0]-1.5)/hmtrace.htilesize;\n\temins[1] = (mins[1]-1.5)/hmtrace.htilesize;\n\temaxs[0] = (maxs[0]+1.5)/hmtrace.htilesize;\n\temaxs[1] = (maxs[1]+1.5)/hmtrace.htilesize;\n\n\t//Test code\n\tif (0)\n\t{\n\t\tvec2_t minb, maxb;\n\t\tVector2Copy(pos, minb);\n\t\tVector2Copy(pos, maxb);\n\t\tnpos[0] = (hmtrace.end[0]+CHUNKBIAS*hmtrace.hm->sectionsize)/hmtrace.htilesize;\n\t\tnpos[1] = (hmtrace.end[1]+CHUNKBIAS*hmtrace.hm->sectionsize)/hmtrace.htilesize;\n\t\tif (npos[0] > pos[0])\n\t\t\tmaxb[0] = pos[0];\n\t\telse\n\t\t\tminb[0] = pos[0];\n\t\tif (npos[1] > pos[1])\n\t\t\tmaxb[1] = pos[1];\n\t\telse\n\t\t\tminb[1] = pos[1];\n\n\t\tminb[0] += emins[0];\n\t\tminb[1] += emins[1];\n\t\tmaxb[0] += emaxs[0];\n\t\tmaxb[1] += emaxs[1];\n\n\t\tfor (y = floor(minb[1]); y <= ceil(maxb[1]); y++)\n\t\t\tfor (x = floor(minb[0]); x <= ceil(maxb[0]); x++)\n\t\t\t\tHeightmap_Trace_Square(&hmtrace, x, y);\n\t}\n\n\t//trace against the heightmap, if it exists.\n\tif (hmtrace.hm->maxsegx != hmtrace.hm->firstsegx)\n\t{\n\t\t//make sure the start tile is valid\n\t\tfor (y = floor(pos[1] + emins[1]); y <= ceil(pos[1] + emaxs[1]); y++)\n\t\t\tfor (x = floor(pos[0] + emins[0]); x <= ceil(pos[0] + emaxs[0]); x++)\n\t\t\t\tHeightmap_Trace_Square(&hmtrace, x, y);\n\n\t\t//now walk over the terrain\n\t\tif (hmtrace.end[0] != hmtrace.start[0] || hmtrace.end[1] != hmtrace.start[1])\n\t\t{\n\t\t\tvec2_t dir, trstart, trdist;\n\n\t\t\t//figure out the leading point\n\t\t\tfor (axis = 0; axis < 2; axis++)\n\t\t\t{\n\t\t\t\ttrdist[axis] = hmtrace.end[axis]-hmtrace.start[axis];\n\t\t\t\tdir[axis] = (hmtrace.end[axis] - hmtrace.start[axis])/hmtrace.htilesize;\n\n\t\t\t\tif (dir[axis] > 0)\n\t\t\t\t{\n\t\t\t\t\tipos[axis] = pos[axis] + emins[axis];\n\t\t\t\t\ttrstart[axis] = CHUNKBIAS*hmtrace.hm->sectionsize + (maxs[axis]) + hmtrace.start[axis];\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tipos[axis] = pos[axis] + emaxs[axis];\n\t\t\t\t\ttrstart[axis] = CHUNKBIAS*hmtrace.hm->sectionsize + (mins[axis]) + hmtrace.start[axis];\n\t\t\t\t}\n\t\t\t\ttrstart[axis] /= hmtrace.htilesize;\n\t\t\t\ttrdist[axis] /= hmtrace.htilesize;\n\t\t\t}\n\t\t\tfor(;;)\n\t\t\t{\n\t\t\t\tif (breaklimit--< 0)\n\t\t\t\t\tbreak;\n\t\t\t\tfor (axis = 0; axis < 2; axis++)\n\t\t\t\t{\n\t\t\t\t\tif (dir[axis] > 0)\n\t\t\t\t\t{\n\t\t\t\t\t\tnpos[axis] = ipos[axis]+1;\n\t\t\t\t\t\tfrac[axis] = (npos[axis]-trstart[axis])/trdist[axis];\n\t\t\t\t\t}\n\t\t\t\t\telse if (dir[axis] < 0)\n\t\t\t\t\t{\n\t\t\t\t\t\tnpos[axis] = ipos[axis];\n\t\t\t\t\t\tfrac[axis] = (ipos[axis]-trstart[axis])/trdist[axis];\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\tfrac[axis] = 1000000000000000.0;\n\t\t\t\t}\n\n\t\t\t\t//which side are we going down?\n\t\t\t\tif (frac[0] < frac[1])\n\t\t\t\t\taxis = 0;\n\t\t\t\telse\n\t\t\t\t\taxis = 1;\n\n\t\t\t\tif (frac[axis] >= 1)\n\t\t\t\t\tbreak;\n\n\t\t\t\t//progress to the crossed boundary\n\t\t\t\tif (dir[axis] < 0)\n\t\t\t\t\tipos[axis] = ipos[axis]-1;\n\t\t\t\telse\n\t\t\t\t\tipos[axis] = ipos[axis]+1;\n\n\t\t\t\taxis = !axis;\n\t\t\t\tif (dir[axis] > 0)\n\t\t\t\t{\t//leading edge is on the right, so start on the left and keep going until we hit the leading edge\n\t\t\t\t\tnpos[0] = ipos[0];\n\t\t\t\t\tnpos[1] = ipos[1];\n\n\t\t\t\t\tnpos[axis] -= ceil(emins[axis]-emaxs[axis]);\n\t\t\t\t\te = ipos[axis];\n\n\t\t\t\t\tnpos[axis] -= 1;\n\t\t\t\t\te++;\n\n\t\t\t\t\tfor (; npos[axis] <= e; npos[axis]++)\n\t\t\t\t\t\tHeightmap_Trace_Square(&hmtrace, npos[0], npos[1]);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t//leading edge is on the left\n\t\t\t\t\tnpos[0] = ipos[0];\n\t\t\t\t\tnpos[1] = ipos[1];\n\t\t\t\t\te = ipos[axis] + ceil(emaxs[axis]-emins[axis]);\n\n\t\t\t\t\tnpos[axis] -= 1;\n\t\t\t\t\te++;\n\n\t\t\t\t\tfor (; npos[axis] <= e; npos[axis]++)\n\t\t\t\t\t\tHeightmap_Trace_Square(&hmtrace, npos[0], npos[1]);\n\t\t\t\t}\n\n//\t\t\t\taxis = !axis;\n\t\t\t\t//and make sure our position on the other axis is correct, for the next time around the loop\n//\t\t\t\tif (frac[axis] > hmtrace.truefrac)\n//\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\t//now trace against the brushes.\n\t//FIXME: optimise into the section grid\n\t{\n\t\tbrushes_t *brushes = hmtrace.hm->wbrushes;\n\t\tint count = hmtrace.hm->numbrushes;\n\t\tfor (count = hmtrace.hm->numbrushes; count-->0; brushes++)\n\t\t{\n\t\t\tif (brushes->contents & against)\n\t\t\t{\n\t\t\t\tint face;\n\t\t\t\tif (hmtrace.absmaxs[0] < brushes->mins[0] ||\n\t\t\t\t\thmtrace.absmaxs[1] < brushes->mins[1] ||\n\t\t\t\t\thmtrace.absmaxs[2] < brushes->mins[2])\n\t\t\t\t\tcontinue;\n\t\t\t\tif (hmtrace.absmins[0] > brushes->maxs[0] ||\n\t\t\t\t\thmtrace.absmins[1] > brushes->maxs[1] ||\n\t\t\t\t\thmtrace.absmins[2] > brushes->maxs[2])\n\t\t\t\t\tcontinue;\n\t\t\t\tif (brushes->patch)\n\t\t\t\t{\n\t\t\t\t\tif (Heightmap_Trace_Patch(&hmtrace, brushes))\n\t\t\t\t\t\tface = -1;\n\t\t\t\t\telse\n\t\t\t\t\t\tface = 0;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tface = Heightmap_Trace_Brush(&hmtrace, brushes->planes, brushes->numplanes, brushes);\n\t\t\t\tif (face)\n\t\t\t\t{\n\t\t\t\t\ttrace->brush_id = brushes->id;\n\t\t\t\t\ttrace->brush_face = face;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\ttrace->plane.dist = hmtrace.plane[3];\n\ttrace->plane.normal[0] = hmtrace.plane[0];\n\ttrace->plane.normal[1] = hmtrace.plane[1];\n\ttrace->plane.normal[2] = hmtrace.plane[2];\n\n\ttrace->startsolid = trace->allsolid = hmtrace.startsolid;\n\tif (hmtrace.nearfrac < 0)\n\t\thmtrace.nearfrac = 0;\n\ttrace->fraction = hmtrace.nearfrac;\n\ttrace->truefraction = hmtrace.truefrac;\n\tVectorInterpolate(start, hmtrace.nearfrac, end, trace->endpos);\n\treturn trace->fraction < 1;\n}\n\nqboolean Heightmap_Trace_Test(struct model_s *model, int hulloverride, const framestate_t *framestate, const vec3_t mataxis[3], const vec3_t start, const vec3_t end, const vec3_t mins, const vec3_t maxs, qboolean capsule, unsigned int against, struct trace_s *trace)\n{\n\tqboolean ret = Heightmap_Trace(model, hulloverride, framestate, mataxis, start, end, mins, maxs, capsule, against, trace);\n\t\n\tif (!trace->startsolid)\n\t{\t//FIXME: this code should not be needed.\n\t\ttrace_t testtrace;\n\t\tHeightmap_Trace(model, hulloverride, framestate, mataxis, trace->endpos, trace->endpos, mins, maxs, capsule, against, &testtrace);\n\t\tif (testtrace.startsolid)\n\t\t{\t//yup, we're bugged.\n\t\t\tCon_DPrintf(\"Trace became solid\\n\");\n\t\t\ttrace->fraction = 0;\n\t\t\tVectorCopy(start, trace->endpos);\n\t\t\ttrace->startsolid = trace->allsolid = true;\n\t\t}\n\t}\n\n\treturn ret;\n}\n\ntypedef struct\n{\n\tint id;\n\tint pos[3];\n} hmpvs_t;\ntypedef struct\n{\n\tint id;\n\tint min[3], max[3];\n} hmpvsent_t;\nunsigned int Heightmap_FatPVS\t\t(model_t *mod, const vec3_t org, pvsbuffer_t *fte_restrict pvsbuffer, qboolean add)\n{\n\t//embed the org onto the pvs\n\thmpvs_t *hmpvs;\n\tif (pvsbuffer->buffersize < sizeof(*hmpvs))\n\t\tpvsbuffer->buffer = BZ_Realloc(pvsbuffer->buffer, pvsbuffer->buffersize=sizeof(*hmpvs));\n\thmpvs = (hmpvs_t*)pvsbuffer->buffer;\n\thmpvs->id = 0xdeadbeef;\n\tVectorCopy(org, hmpvs->pos);\n\treturn sizeof(*hmpvs);\n}\n\n#ifdef HAVE_SERVER\nqboolean Heightmap_EdictInFatPVS\t(model_t *mod, const struct pvscache_s *edict, const qbyte *pvsdata, const int *areas)\n{\n\theightmap_t *hm = mod->terrain;\n\tint o[3], i;\n\tconst hmpvs_t *hmpvs = (const hmpvs_t*)pvsdata;\n\tconst hmpvsent_t *hmed = (const hmpvsent_t*)edict;\n\n\tif (!hm->culldistance || !hmpvs)\n\t\treturn true;\n\n\t//check distance\n\tfor (i = 0; i < 3; i++)\n\t{\n\t\tif (hmpvs->pos[i] < hmed->min[i])\n\t\t\to[i] = hmed->min[i] - hmpvs->pos[i];\n\t\telse if (hmpvs->pos[i] > hmed->max[i])\n\t\t\to[i] = hmed->max[i] - hmpvs->pos[i];\n\t\telse\n\t\t\to[i] = 0;\n\t}\n\n\treturn DotProduct(o,o) < hm->culldistance;\n}\n\nvoid Heightmap_FindTouchedLeafs\t(model_t *mod, pvscache_t *ent, const float *mins, const float *maxs)\n{\n\thmpvsent_t *hmed = (hmpvsent_t*)ent;\n\n\tVectorCopy(mins, hmed->min);\n\tVectorCopy(maxs, hmed->max);\n}\n#endif\n\nvoid Heightmap_LightPointValues\t(model_t *mod, const vec3_t point, vec3_t res_diffuse, vec3_t res_ambient, vec3_t res_dir)\n{\n\tres_diffuse[0] = 128;\n\tres_diffuse[1] = 128;\n\tres_diffuse[2] = 128;\n\tres_ambient[0] = 64;\n\tres_ambient[1] = 64;\n\tres_ambient[2] = 64;\n\tres_dir[0] = 1;//sin(time);\n\tres_dir[1] = 0;//cos(time);\n\tres_dir[2] = 0;//sin(time);\n\tVectorNormalize(res_dir);\n}\nvoid Heightmap_StainNode\t\t\t(model_t *mod, float *parms)\n{\n}\nvoid Heightmap_MarkLights\t\t\t(dlight_t *light, dlightbitmask_t bit, mnode_t *node)\n{\n}\n\nqbyte *Heightmap_ClusterPVS\t(model_t *model, int num, pvsbuffer_t *buffer, pvsmerge_t merge)\n{\n\treturn NULL;\n//\tstatic qbyte heightmappvs = 255;\n//\treturn &heightmappvs;\n}\nint\tHeightmap_ClusterForPoint\t(model_t *model, const vec3_t point, int *area)\n{\n\tif (area)\n\t\t*area = 0;\n\treturn -1;\n}\n\n#ifdef HAVE_CLIENT\nstatic unsigned char *QDECL Terr_GetLightmap(hmsection_t *s, int idx, qboolean edit)\n{\n\tint x = idx % SECTTEXSIZE, y = idx / SECTTEXSIZE;\n\tif (s->lightmap < 0)\n\t{\n\t\tTerr_LoadSection(s->hmmod, s, s->sx, s->sy, true);\n\t\tTerr_InitLightmap(s, true);\n\t}\n\tif (s->lightmap < 0)\n\t\treturn NULL;\n\n\tif (edit)\n\t{\n\t\ts->flags |= TSF_EDITED;\n\n\t\tlightmap[s->lightmap]->modified = true;\n\t\tlightmap[s->lightmap]->rectchange.l = 0;\n\t\tlightmap[s->lightmap]->rectchange.t = 0;\n\t\tlightmap[s->lightmap]->rectchange.r = HMLMSTRIDE;\n\t\tlightmap[s->lightmap]->rectchange.b = HMLMSTRIDE;\n\t}\n\treturn lightmap[s->lightmap]->lightmaps + ((s->lmy+y) * HMLMSTRIDE + (s->lmx+x)) * lightmap[s->lightmap]->pixbytes;\n}\nstatic void ted_dorelight(model_t *m, heightmap_t *hm)\n{\n\tunsigned char *lm = Terr_GetLightmap(hm->relight, 0, true);\n\tint x, y, k;\n#define EXPAND 2\n\tvec3_t surfnorms[(SECTTEXSIZE+EXPAND*2)*(SECTTEXSIZE+EXPAND*2)];\n\tvec3_t surfpoint[(SECTTEXSIZE+EXPAND*2)*(SECTTEXSIZE+EXPAND*2)];\n//\tfloat scaletab[EXPAND*2*EXPAND*2];\n\tvec3_t ldir;\n\thmsection_t *s = hm->relight;\n\tfloat ambient, diffuse;\n\ttrace_t trace;\n\ts->flags &= ~TSF_RELIGHT;\n\thm->relight = NULL;\n\n\tif (s->lightmap < 0)\n\t\treturn;\n\n\tambient = 255*mod_terrain_ambient.value;\n\tdiffuse = 255-ambient;\n\n\tfor (y = -EXPAND; y < SECTTEXSIZE+EXPAND; y++)\n\tfor (x = -EXPAND; x < SECTTEXSIZE+EXPAND; x++)\n\t{\n\t\tk = x+EXPAND + (y+EXPAND)*(SECTTEXSIZE+EXPAND*2);\n\t\tsurfpoint[k][0] = hm->relightmin[0] + (x*hm->sectionsize/(SECTTEXSIZE-1));\n\t\tsurfpoint[k][1] = hm->relightmin[1] + (y*hm->sectionsize/(SECTTEXSIZE-1));\n\t\tsurfpoint[k][2] = Heightmap_Normal(s->hmmod, surfpoint[k], surfnorms[k])+0.1;\n\t}\n\n\tVectorNormalize2(mod_terrain_sundir.vec4, ldir);\n\n\tfor (y = 0; y < SECTTEXSIZE; y++, lm += (HMLMSTRIDE-SECTTEXSIZE)*4)\n\tfor (x = 0; x < SECTTEXSIZE; x++, lm += 4)\n\t{\n\t\tvec3_t norm;\n\t\tfloat d;\n\t\tint sx,sy;\n\t\tVectorClear(norm);\n\t\tfor (sy = -EXPAND; sy <= EXPAND; sy++)\n\t\tfor (sx = -EXPAND; sx <= EXPAND; sx++)\n\t\t{\n\t\t\td = sqrt((EXPAND*2+1)*(EXPAND*2+1) - sx*sx+sy*sy);\n\t\t\tVectorMA(norm, d, surfnorms[x+sx+EXPAND + (y+sy+EXPAND)*(SECTTEXSIZE+EXPAND*2)], norm);\n\t\t}\n\n\t\tVectorNormalize(norm);\n\t\td = DotProduct(ldir, norm);\n\t\tif (d < 0)\n\t\t\td = 0;\n\t\telse if (mod_terrain_shadows.ival)\n\t\t{\n\t\t\tfloat *point = surfpoint[x+EXPAND + (y+EXPAND)*(SECTTEXSIZE+EXPAND*2)];\n\t\t\tvec3_t sun;\n\t\t\tVectorMA(point, mod_terrain_shadow_dist.value, ldir, sun);\n\t\t\tif (m->funcs.NativeTrace(m, 0, NULL, NULL, point, sun, vec3_origin, vec3_origin, false, FTECONTENTS_SOLID|FTECONTENTS_BODY, &trace))\n\t\t\t\td = 0;\n\t\t}\n//\t\tlm[0] = norm[0]*127 + 128;\n//\t\tlm[1] = norm[1]*127 + 128;\n//\t\tlm[2] = norm[2]*127 + 128;\n\t\tlm[3] = ambient + d*diffuse;\n\t}\n\n\tlightmap[s->lightmap]->modified = true;\n\tlightmap[s->lightmap]->rectchange.l = 0;\n\tlightmap[s->lightmap]->rectchange.t = 0;\n\tlightmap[s->lightmap]->rectchange.r = HMLMSTRIDE;\n\tlightmap[s->lightmap]->rectchange.b = HMLMSTRIDE;\n}\nstatic void ted_sethole(void *ctx, hmsection_t *s, int idx, float wx, float wy, float w)\n{\n\tunsigned int row = idx/9;\n\tunsigned int col = idx%9;\n\tunsigned int bit;\n\tunsigned int mask;\n\tif (row == 8 || col == 8)\n\t\treturn;\t//meh, our painting function is written with an overlap of 1\n\tif (w <= 0)\n\t\treturn;\n\tmask = 1u<<(col);\n\tif (*(float*)ctx > 0)\n\t\tbit = mask;\n\telse\n\t\tbit = 0;\n\ts->flags |= TSF_NOTIFY|TSF_DIRTY|TSF_EDITED;\n\ts->holes[row] = (s->holes[row] & ~mask) | bit;\n}\nstatic void ted_heighttally(void *ctx, hmsection_t *s, int idx, float wx, float wy, float w)\n{\n\t/*raise the terrain*/\n\t((float*)ctx)[0] += s->heights[idx]*w;\n\t((float*)ctx)[1] += w;\n}\nstatic void ted_heightsmooth(void *ctx, hmsection_t *s, int idx, float wx, float wy, float w)\n{\n\ts->flags |= TSF_NOTIFY|TSF_DIRTY|TSF_EDITED|TSF_RELIGHT;\n\t/*interpolate the terrain towards a certain value*/\n\n\tif (IS_NAN(s->heights[idx]))\n\t\ts->heights[idx] = *(float*)ctx;\n\telse\n\t\ts->heights[idx] = s->heights[idx]*(1-w) + w**(float*)ctx;\n}\nstatic void ted_heightdebug(void *ctx, hmsection_t *s, int idx, float wx, float wy, float w)\n{\n\tint tx = idx/SECTHEIGHTSIZE, ty = idx % SECTHEIGHTSIZE;\n\ts->flags |= TSF_NOTIFY|TSF_DIRTY|TSF_EDITED|TSF_RELIGHT;\n\t/*interpolate the terrain towards a certain value*/\n\n\tif (tx == 16)\n\t\ttx = 0;\n\tif (ty == 16)\n\t\tty = 0;\n\n//\tif (ty < tx)\n//\t\ttx = ty;\n\ts->heights[idx] = (tx>>1) * 32 + (ty>>1) * 32;\n}\nstatic void ted_heightraise(void *ctx, hmsection_t *s, int idx, float wx, float wy, float strength)\n{\n\ts->flags |= TSF_NOTIFY|TSF_DIRTY|TSF_EDITED|TSF_RELIGHT;\n\t/*raise the terrain*/\n\ts->heights[idx] += strength;\n}\nstatic void ted_heightset(void *ctx, hmsection_t *s, int idx, float wx, float wy, float strength)\n{\n\ts->flags |= TSF_NOTIFY|TSF_DIRTY|TSF_EDITED|TSF_RELIGHT;\n\t/*set the terrain to a specific value*/\n\ts->heights[idx] = *(float*)ctx;\n}\n\nstatic void ted_waterset(void *ctx, hmsection_t *s, int idx, float wx, float wy, float strength)\n{\n\tstruct hmwater_s *w = s->water;\n\tif (!w)\n\t\tw = Terr_GenerateWater(s, *(float*)ctx);\n\ts->flags |= TSF_NOTIFY|TSF_DIRTY|TSF_EDITED;\n\n\t//FIXME: water doesn't render properly. don't let people make dodgy water regions because they can't see it.\n\t//this is temp code.\n\t//for (idx = 0; idx < 9*9; idx++)\n\t\t//w->heights[idx] = *(float*)ctx;\n\t//end fixme\n\n\tw->heights[idx] = *(float*)ctx;\n\tif (w->minheight > w->heights[idx])\n\t\tw->minheight = w->heights[idx];\n\tif (w->maxheight < w->heights[idx])\n\t\tw->maxheight = w->heights[idx];\n\n\t//FIXME: what about holes?\n}\n\nstatic void ted_texconcentrate(void *ctx, hmsection_t *s, int idx, float wx, float wy, float w)\n{\n\tunsigned char *lm = Terr_GetLightmap(s, idx, true);\n\ts->flags |= TSF_NOTIFY|TSF_EDITED;\n\n\t/*concentrate the lightmap values to a single channel*/\n\tif (lm[0] > lm[1] && lm[0] > lm[2] && lm[0] > (255-(lm[0]+lm[1]+lm[2])))\n\t{\n\t\tlm[0] = lm[0]*(1-w) + 255*(w);\n\t\tlm[1] = lm[1]*(1-w) + 0*(w);\n\t\tlm[2] = lm[2]*(1-w) + 0*(w);\n\t}\n\telse if (lm[1] > lm[2] && lm[1] > (255-(lm[0]+lm[1]+lm[2])))\n\t{\n\t\tlm[0] = lm[0]*(1-w) + 0*(w);\n\t\tlm[1] = lm[1]*(1-w) + 255*(w);\n\t\tlm[2] = lm[2]*(1-w) + 0*(w);\n\t}\n\telse if (lm[2] > (255-(lm[0]+lm[1]+lm[2])))\n\t{\n\t\tlm[0] = lm[0]*(1-w) + 0*(w);\n\t\tlm[1] = lm[1]*(1-w) + 0*(w);\n\t\tlm[2] = lm[2]*(1-w) + 255*(w);\n\t}\n\telse\n\t{\n\t\tlm[0] = lm[0]*(1-w) + 0*(w);\n\t\tlm[1] = lm[1]*(1-w) + 0*(w);\n\t\tlm[2] = lm[2]*(1-w) + 0*(w);\n\t}\n}\n\nstatic void ted_texnoise(void *ctx, hmsection_t *s, int idx, float wx, float wy, float w)\n{\n\tunsigned char *lm = Terr_GetLightmap(s, idx, true);\n\tvec4_t v;\n\tfloat sc;\n\n\ts->flags |= TSF_NOTIFY|TSF_EDITED;\n\n\t/*randomize the lightmap somewhat (you'll probably want to concentrate it a bit after)*/\n\tv[0] = (rand()&255);\n\tv[1] = (rand()&255);\n\tv[2] = (rand()&255);\n\tv[3] = (rand()&255);\n\tsc = v[0] + v[1] + v[2] + v[3];\n\tVector4Scale(v, 255/sc, v);\n\n\tlm[0] = lm[0]*(1-w) + (v[0]*(w));\n\tlm[1] = lm[1]*(1-w) + (v[1]*(w));\n\tlm[2] = lm[2]*(1-w) + (v[2]*(w));\n}\n\nstatic void ted_texpaint(void *ctx, hmsection_t *s, int idx, float wx, float wy, float w)\n{\n\tunsigned char *lm = Terr_GetLightmap(s, idx, true);\n\tconst char *texname = ctx;\n\tint t;\n\tvec4_t newval;\n\tif (w > 1)\n\t\tw = 1;\n\n\ts->flags |= TSF_NOTIFY|TSF_EDITED;\n\n\tfor (t = 0; t < 4; t++)\n\t{\n\t\tif (!strncmp(s->texname[t], texname, sizeof(s->texname[t])-1))\n\t\t{\n\t\t\tint extra;\n\t\t\tnewval[0] = (t == 0);\n\t\t\tnewval[1] = (t == 1);\n\t\t\tnewval[2] = (t == 2);\n\t\t\tnewval[3] = (t == 3);\n\t\t\textra = 255 - (lm[0]+lm[1]+lm[2]);\n\t\t\tlm[2] = lm[2]*(1-w) + (255*newval[0]*(w));\n\t\t\tlm[1] = lm[1]*(1-w) + (255*newval[1]*(w));\n\t\t\tlm[0] = lm[0]*(1-w) + (255*newval[2]*(w));\n\t\t\textra = extra*(1-w) + (255*newval[3]*(w));\n\n\t\t\t//the extra stuff is to cope with numerical precision. add any lost values to the new texture instead of the implicit one\n\t\t\textra = 255 - (extra+lm[0]+lm[1]+lm[2]);\n\t\t\tif (t != 3)\n\t\t\t\tlm[2-t] += extra;\n\t\t\treturn;\n\t\t}\n\t}\n\n\t/*special handling to make a section accept the first texture painted on it as a base texture. no more chessboard*/\n\tif (!*s->texname[0] && !*s->texname[1] && !*s->texname[2] && !*s->texname[3])\n\t{\n\t\tQ_strncpyz(s->texname[3], texname, sizeof(s->texname[3]));\n\t\tTerr_LoadSectionTextures(s);\n\n\t\tfor (idx = 0; idx < SECTTEXSIZE*SECTTEXSIZE; idx++)\n\t\t{\n\t\t\tlm = Terr_GetLightmap(s, idx, true);\n\t\t\tlm[2] = 0;\n\t\t\tlm[1] = 0;\n\t\t\tlm[0] = 0;\n\t\t}\n\t\treturn;\n\t}\n\tfor (t = 0; t < 4; t++)\n\t{\n\t\tif (!*s->texname[t])\n\t\t{\n\t\t\tQ_strncpyz(s->texname[t], texname, sizeof(s->texname[t]));\n\n\t\t\tnewval[0] = (t == 0);\n\t\t\tnewval[1] = (t == 1);\n\t\t\tnewval[2] = (t == 2);\n\t\t\tlm[2] = lm[2]*(1-w) + (255*newval[0]*(w));\n\t\t\tlm[1] = lm[1]*(1-w) + (255*newval[1]*(w));\n\t\t\tlm[0] = lm[0]*(1-w) + (255*newval[2]*(w));\n\n\t\t\tTerr_LoadSectionTextures(s);\n\t\t\treturn;\n\t\t}\n\t}\n}\n\nstatic void ted_texreplace(void *ctx, hmsection_t *s, int idx, float wx, float wy, float w)\n{\n\tif (w > 0)\n\t\tted_texpaint(ctx, s, idx, wx, wy, 1);\n}\n\n/*\nstatic void ted_texlight(void *ctx, hmsection_t *s, int idx, float wx, float wy, float w)\n{\n\tunsigned char *lm = ted_getlightmap(s, idx);\n\tvec3_t pos, pos2;\n\tvec3_t norm, tnorm;\n\tvec3_t ldir = {0.4, 0.7, 2};\n\tfloat d;\n\tint x,y;\n\ttrace_t tr;\n\tVectorClear(norm);\n\tfor (y = -4; y < 4; y++)\n\tfor (x = -4; x < 4; x++)\n\t{\n\t\tpos[0] = wx - (CHUNKBIAS + x/64.0) * s->hmmod->sectionsize;\n\t\tpos[1] = wy - (CHUNKBIAS + y/64.0) * s->hmmod->sectionsize;\n#if 0\n\t\tpos[2] = 10000;\n\t\tpos2[0] = wx - (CHUNKBIAS + x/64.0) * s->hmmod->sectionsize;\n\t\tpos2[1] = wy - (CHUNKBIAS + y/64.0) * s->hmmod->sectionsize;\n\t\tpos2[2] = -10000;\n\t\tHeightmap_Trace(cl.worldmodel, 0, 0, NULL, pos, pos2, vec3_origin, vec3_origin, FTECONTENTS_SOLID, &tr);\n\t\tVectorCopy(tr.plane.normal, tnorm);\n#else\n\t\tHeightmap_Normal(s->hmmod, pos, tnorm);\n#endif\n\t\td = sqrt(32 - x*x+y*y);\n\t\tVectorMA(norm, d, tnorm, norm);\n\t}\n\n\tVectorNormalize(ldir);\n\tVectorNormalize(norm);\n\td = DotProduct(ldir, norm);\n\tif (d < 0)\n\t\td = 0;\n\tlm[3] = d*255;\n}\n*/\nstatic void ted_texset(void *ctx, hmsection_t *s, int idx, float wx, float wy, float w)\n{\n\tunsigned char *lm = Terr_GetLightmap(s, idx, true);\n\tif (w > 1)\n\t\tw = 1;\n\ts->flags |= TSF_NOTIFY|TSF_EDITED;\n\n\tlm[2] = lm[2]*(1-w) + (255*((float*)ctx)[0]*(w));\n\tlm[1] = lm[1]*(1-w) + (255*((float*)ctx)[1]*(w));\n\tlm[0] = lm[0]*(1-w) + (255*((float*)ctx)[2]*(w));\n}\n\nstatic void ted_textally(void *ctx, hmsection_t *s, int idx, float wx, float wy, float w)\n{\n\tunsigned char *lm = Terr_GetLightmap(s, idx, false);\n\t((float*)ctx)[0] += lm[0]*w;\n\t((float*)ctx)[1] += lm[1]*w;\n\t((float*)ctx)[2] += lm[2]*w;\n\t((float*)ctx)[3] += w;\n}\n\nstatic void ted_tint(void *ctx, hmsection_t *s, int idx, float wx, float wy, float w)\n{\n\tfloat *col = s->colours[idx];\n\tfloat *newval = ctx;\n\tif (w > 1)\n\t\tw = 1;\n\ts->flags |= TSF_NOTIFY|TSF_DIRTY|TSF_EDITED|TSF_HASCOLOURS;\t/*dirty because of the vbo*/\n\tcol[0] = col[0]*(1-w) + (newval[0]*(w));\n\tcol[1] = col[1]*(1-w) + (newval[1]*(w));\n\tcol[2] = col[2]*(1-w) + (newval[2]*(w));\n\tcol[3] = col[3]*(1-w) + (newval[3]*(w));\n}\n\nenum\n{\n\ttid_linear,\n\ttid_exponential,\n\ttid_square_linear,\n\ttid_square_exponential,\n\ttid_flat\n};\n//calls 'func' for each tile upon the terrain. the 'tile' can be either height or texel\nstatic void ted_itterate(heightmap_t *hm, int distribution, float *pos, float radius, float strength, int steps, void(*func)(void *ctx, hmsection_t *s, int idx, float wx, float wy, float strength), void *ctx)\n{\n\tint tx, ty;\n\tfloat wx, wy;\n\tfloat sc[2];\n\tint min[2], max[2];\n\tint sx,sy;\n\thmsection_t *s;\n\tfloat w, xd, yd;\n\n\tif (radius < 0)\n\t{\n\t\tradius *= -1;\n\t\tdistribution |= 2;\n\t}\n\n\tmin[0] = floor((pos[0] - radius)/(hm->sectionsize) - 1.5);\n\tmin[1] = floor((pos[1] - radius)/(hm->sectionsize) - 1.5);\n\tmax[0] = ceil((pos[0] + radius)/(hm->sectionsize) + 1.5);\n\tmax[1] = ceil((pos[1] + radius)/(hm->sectionsize) + 1.5);\n\n\tmin[0] = bound(hm->firstsegx, min[0], hm->maxsegx);\n\tmin[1] = bound(hm->firstsegy, min[1], hm->maxsegy);\n\tmax[0] = bound(hm->firstsegx, max[0], hm->maxsegx);\n\tmax[1] = bound(hm->firstsegy, max[1], hm->maxsegy);\n\n\tsc[0] = hm->sectionsize/(steps-1);\n\tsc[1] = hm->sectionsize/(steps-1);\n\n\tfor (sy = min[1]; sy < max[1]; sy++)\n\t{\n\t\tfor (sx = min[0]; sx < max[0]; sx++)\n\t\t{\n\t\t\ts = Terr_GetSection(hm, sx, sy, TGS_WAITLOAD|TGS_DEFAULTONFAIL);\n\t\t\tif (!s)\n\t\t\t\tcontinue;\n\n\t\t\tfor (ty = 0; ty < steps; ty++)\n\t\t\t{\n\t\t\t\twy = (sy*(steps-1.0) + ty)*sc[1];\n\t\t\t\tyd = wy - pos[1];// - sc[1]/4;\n\t\t\t\tfor (tx = 0; tx < steps; tx++)\n\t\t\t\t{\n\t\t\t\t\t/*both heights and textures have an overlapping/matching sample at the edge, there's no need for any half-pixels or anything here*/\n\t\t\t\t\twx = (sx*(steps-1.0) + tx)*sc[0];\n\t\t\t\t\txd = wx - pos[0];// - sc[0]/4;\n\n\t\t\t\t\tswitch(distribution)\n\t\t\t\t\t{\n\t\t\t\t\tcase tid_exponential:\n\t\t\t\t\t\tw = radius*radius - (xd*xd+yd*yd);\n\t\t\t\t\t\tif (w > 0)\n\t\t\t\t\t\t\tfunc(ctx, s, tx+ty*steps, wx, wy, sqrt(w)*strength/(radius));\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase tid_linear:\n\t\t\t\t\t\tw = radius - sqrt(xd*xd+yd*yd);\n\t\t\t\t\t\tif (w > 0)\n\t\t\t\t\t\t\tfunc(ctx, s, tx+ty*steps, wx, wy, w*strength/(radius));\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase tid_square_exponential:\n\t\t\t\t\t\tw = max(fabs(xd), fabs(yd));\n\t\t\t\t\t\tw = radius*radius - w*w;\n\t\t\t\t\t\tif (w > 0)\n\t\t\t\t\t\t\tfunc(ctx, s, tx+ty*steps, wx, wy, sqrt(w)*strength/(radius));\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase tid_square_linear:\n\t\t\t\t\t\tw = max(fabs(xd), fabs(yd));\n\t\t\t\t\t\tw = radius - w;\n\t\t\t\t\t\tif (w > 0)\n\t\t\t\t\t\t\tfunc(ctx, s, tx+ty*steps, wx, wy, w*strength/(radius));\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase tid_flat:\n\t\t\t\t\t\tw = max(fabs(xd), fabs(yd));\n\t\t\t\t\t\tw = radius - w;\n\t\t\t\t\t\tif (w > 0)\n\t\t\t\t\t\t\tfunc(ctx, s, tx+ty*steps, wx, wy, strength);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nvoid ted_texkill(hmsection_t *s, const char *killtex)\n{\n\tint x, y, t, to;\n\tif (!s)\n\t\treturn;\n\tfor (t = 0; t < 4; t++)\n\t{\n\t\tif (!strcmp(s->texname[t], killtex))\n\t\t{\n\t\t\tunsigned char *lm = Terr_GetLightmap(s, 0, true);\n\t\t\ts->flags |= TSF_EDITED;\n\t\t\ts->texname[t][0] = 0;\n\t\t\tfor (to = 0; to < 4; to++)\n\t\t\t\tif (*s->texname[to])\n\t\t\t\t\tbreak;\n\t\t\tif (to == 4)\n\t\t\t\tto = 0;\n\n\t\t\tif (to == 0 || to == 2)\n\t\t\t\tto = 2 - to;\n\t\t\tif (t == 0 || t == 2)\n\t\t\t\tt = 2 - t;\n\n\t\t\tfor (y = 0; y < SECTTEXSIZE; y++)\n\t\t\t{\n\t\t\t\tfor (x = 0; x < SECTTEXSIZE; x++, lm+=4)\n\t\t\t\t{\n\t\t\t\t\tif (t == 3)\n\t\t\t\t\t{\n\t\t\t\t\t\t//to won't be 3\n\t\t\t\t\t\tlm[to] = lm[to] + (255 - (lm[0] + lm[1] + lm[2]));\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tif (to != 3)\n\t\t\t\t\t\t\tlm[to] = (lm[to]+lm[t])&0xff;\n\t\t\t\t\t\tlm[t] = 0;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tlm += SECTTEXSIZE*4*(LMCHUNKS-1);\n\t\t\t}\n\t\t\tif (t == 0 || t == 2)\n\t\t\t\tt = 2 - t;\n\t\t\tTerr_LoadSectionTextures(s);\n\t\t}\n\t}\n}\n\nvoid QCBUILTIN PF_terrain_edit(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tworld_t *vmw = prinst->parms->user;\n\tint action = G_FLOAT(OFS_PARM0);\n\tvec3_t pos;// G_VECTOR(OFS_PARM1);\n\tfloat radius = G_FLOAT(OFS_PARM2);\n\tfloat quant = G_FLOAT(OFS_PARM3);\n\tint modelindex = ((wedict_t*)PROG_TO_EDICT(prinst, *vmw->g.self))->v->modelindex;\n//\tG_FLOAT(OFS_RETURN) = Heightmap_Edit(w->worldmodel, action, pos, radius, quant);\n\tmodel_t *mod = vmw->Get_CModel(vmw, modelindex);\n\theightmap_t *hm;\n\tvec4_t tally;\n\n\tG_FLOAT(OFS_RETURN) = 0;\n\n\tif (!mod)\n\t\treturn;\n\tif (mod->loadstate == MLS_LOADING)\n\t\tCOM_WorkerPartialSync(mod, &mod->loadstate, MLS_LOADING);\n\tif (mod->loadstate != MLS_LOADED)\n\t\treturn;\n\n\tswitch(action)\n\t{\n\tcase ter_ent_get:\n\t\t{\n\t\t\tunsigned int idx = G_INT(OFS_PARM1);\n\t\t\tif (!mod->numentityinfo)\n\t\t\t\tMod_ParseEntities(mod);\n\t\t\tif (idx >= mod->numentityinfo || !mod->entityinfo[idx].keyvals)\n\t\t\t\tG_INT(OFS_RETURN) = 0;\n\t\t\telse\n\t\t\t\tG_INT(OFS_RETURN) = PR_TempString(prinst, mod->entityinfo[idx].keyvals);\n\t\t}\n\t\treturn;\n\tcase ter_ent_set:\n\t\t{\n\t\t\tunsigned int idx = G_INT(OFS_PARM1);\n\t\t\tint id;\n\t\t\tconst char *newvals;\n\t\t\tif (idx >= MAX_EDICTS)\t//we need some sanity limit... many ents will get removed like lights so this one isn't quite correct, but it'll be in the right sort of ballpark.\n\t\t\t{\n\t\t\t\tG_INT(OFS_RETURN) = 0;\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t//if there's no ents, then that's a problem. make sure that there's at least a worldspawn.\n\t\t\tif (!mod->numentityinfo)\n\t\t\t\tMod_ParseEntities(mod);\n\t\t\t//make sure we don't have any cached entities string, by wiping it all.\n\t\t\tZ_Free((char*)mod->entities_raw);\n\t\t\tmod->entities_raw = NULL;\n\n\t\t\tG_INT(OFS_RETURN) = 0;\n\t\t\tif (idx < mod->numentityinfo)\n\t\t\t{\n\t\t\t\tif (!G_INT(OFS_PARM2) && !mod->entityinfo[idx].keyvals)\n\t\t\t\t\treturn; //no-op\n\t\t\t\tZ_Free(mod->entityinfo[idx].keyvals);\n\t\t\t\tmod->entityinfo[idx].keyvals = NULL;\n\t\t\t\tid = mod->entityinfo[idx].id;\n\t\t\t}\n\t\t\telse\n\t\t\t\tid = 0;\n\t\t\tif (G_INT(OFS_PARM2))\n\t\t\t{\n\t\t\t\tnewvals = PR_GetStringOfs(prinst, OFS_PARM2);\n\t\t\t\tif (idx >= mod->numentityinfo)\n\t\t\t\t\tZ_ReallocElements((void**)&mod->entityinfo, &mod->numentityinfo, idx+64, sizeof(*mod->entityinfo));\n\t\t\t\tmod->entityinfo[idx].keyvals = Z_StrDup(newvals);\n\t\t\t\tif (!id)\n\t\t\t\t\tid = (idx+1) | ((cl.playerview[0].playernum+1)<<24);\n\t\t\t\tmod->entityinfo[idx].id = id;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tnewvals = NULL;\n\t\t\t\tif (idx < mod->numentityinfo)\n\t\t\t\t\tmod->entityinfo[idx].id = 0;\n\t\t\t}\n\n#ifdef HAVE_SERVER\n\t\t\tif (sv_state && modelindex > 0)\n\t\t\t{\n\t\t\t\tMSG_WriteByte(&sv.multicast, svcfte_brushedit);\n\t\t\t\tMSG_WriteShort(&sv.multicast, modelindex);\n\t\t\t\tMSG_WriteByte(&sv.multicast, newvals?hmcmd_ent_edit:hmcmd_ent_remove);\n\t\t\t\tMSG_WriteLong(&sv.multicast, id);\n\t\t\t\tif (newvals)\n\t\t\t\t\tMSG_WriteString(&sv.multicast, newvals);\n\t\t\t\tSV_MulticastProtExt(vec3_origin, MULTICAST_ALL_R, ~0, 0, 0);\n\t\t\t\t//tell ssqc, csqc will be told by the server\n\n\t\t\t\tSSQC_MapEntityEdited(modelindex, idx, newvals);\n\t\t\t}\n\t\t\telse\n#endif\n#ifdef HAVE_CLIENT\n\t\t\tif (cls.state && modelindex > 0)\n\t\t\t{\n\t\t\t\tMSG_WriteByte(&cls.netchan.message, clcfte_brushedit);\n\t\t\t\tMSG_WriteShort(&cls.netchan.message, modelindex);\n\t\t\t\tMSG_WriteByte(&cls.netchan.message, newvals?hmcmd_ent_edit:hmcmd_ent_remove);\n\t\t\t\tMSG_WriteLong(&cls.netchan.message, id);\n\t\t\t\tif (newvals)\n\t\t\t\t\tMSG_WriteString(&cls.netchan.message, newvals);\n\n\t\t\t\t#ifdef CSQC_DAT\n\t\t\t\t\tCSQC_MapEntityEdited(modelindex, idx, newvals);\n\t\t\t\t#endif\n\t\t\t}\n\t\t\telse\n#endif\n\t\t\t{\n\t\t\t\t#ifdef CSQC_DAT\n\t\t\t\t\tCSQC_MapEntityEdited(modelindex, idx, newvals);\n\t\t\t\t#endif\n\t\t\t}\n\t\t}\n\t\treturn;\n\tcase ter_ent_add:\n\t\t{\n//\t\t\tint idx = G_INT(OFS_PARM1);\n//\t\t\tconst char *news = PR_GetStringOfs(prinst, OFS_PARM2);\n\t\t\tG_INT(OFS_RETURN) = mod->numentityinfo;\n\t\t}\n\t\treturn;\n\tcase ter_ent_count:\n\t\tif (!mod->numentityinfo)\n\t\t\tMod_ParseEntities(mod);\n\t\tG_INT(OFS_RETURN) = mod->numentityinfo;\n\t\treturn;\n\tcase ter_ents_wipe_deprecated:\n\t\tG_INT(OFS_RETURN) = PR_TempString(prinst, Mod_GetEntitiesString(mod));\n\t\tMod_SetEntitiesString(mod, \"\", true);\n\t\treturn;\n\tcase ter_ents_concat_deprecated:\n\t\t{\n\t\t\tchar *newv;\n\t\t\tconst char *olds = Mod_GetEntitiesString(mod);\n\t\t\tconst char *news = PR_GetStringOfs(prinst, OFS_PARM1);\n\t\t\tsize_t oldlen = strlen(olds);\n\t\t\tsize_t newlen = strlen(news);\n\t\t\tnewv = Z_Malloc(oldlen + newlen + 1);\n\t\t\tmemcpy(newv, olds, oldlen);\n\t\t\tmemcpy(newv+oldlen, news, newlen);\n\t\t\tnewv[oldlen + newlen] = 0;\n\t\t\tZ_Free((char*)olds);\n\t\t\tG_FLOAT(OFS_RETURN) = oldlen + newlen;\n\t\t\t\n\t\t\tMod_SetEntitiesString(mod, newv, false);\n\t\t\tif (mod->terrain)\n\t\t\t{\n\t\t\t\thm = mod->terrain;\n\t\t\t\thm->entsdirty = true;\n\t\t\t}\n\t\t}\n\t\treturn;\n\tcase ter_ents_get:\n\t\tG_INT(OFS_RETURN) = PR_TempString(prinst, Mod_GetEntitiesString(mod));\n\t\treturn;\n\tcase ter_save:\n\t\tif (mod->terrain)\n\t\t{\n\t\t\tquant = Heightmap_Save(mod->terrain);\n\t\t\tCon_DPrintf(\"ter_save: %g sections saved\\n\", quant);\n\t\t}\n\t\tG_FLOAT(OFS_RETURN) = quant;\n\t\t/*\n\t\tif (mod->type == mod_brush)\n\t\t{\n\t\t\tCon_TPrintf(\"that model isn't a suitable worldmodel\\n\");\n\t\t\treturn;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tFS_CreatePath(fname, FS_GAMEONLY);\n\t\t\tfile = FS_OpenVFS(fname, \"wb\", FS_GAMEONLY);\n\t\t\tif (!file)\n\t\t\t\tCon_TPrintf(\"unable to open %s\\n\", fname);\n\t\t\telse\n\t\t\t{\n\t\t\t\tTerr_WriteMapFile(file, mod);\n\t\t\t\tVFS_CLOSE(file);\n\t\t\t}\n\t\t}*/\n\t\treturn;\n\t}\n\n\tif (!mod->terrain)\n\t{\n\t\tchar basename[MAX_QPATH];\n\t\tCOM_FileBase(mod->name, basename, sizeof(basename));\n\t\tmod->terrain = Mod_LoadTerrainInfo(mod, basename, true);\n\t\thm = mod->terrain;\n\t\tif (!hm)\n\t\t\treturn;\n\t\tTerr_FinishTerrain(mod);\n\t}\n\thm = mod->terrain;\n\n\tpos[0] = G_FLOAT(OFS_PARM1+0) + hm->sectionsize * CHUNKBIAS;\n\tpos[1] = G_FLOAT(OFS_PARM1+1) + hm->sectionsize * CHUNKBIAS;\n\tpos[2] = G_FLOAT(OFS_PARM1+2);\n\n\tswitch(action)\n\t{\n\tcase ter_reload:\n\t\tG_FLOAT(OFS_RETURN) = 1;\n\t\tTerr_PurgeTerrainModel(mod, false, true);\n\t\tbreak;\n\tcase ter_sethole:\n\t/*\t{\n\t\t\tint x, y;\n\t\t\thmsection_t *s;\n\t\t\tx = pos[0]*4 / hm->sectionsize;\n\t\t\ty = pos[1]*4 / hm->sectionsize;\n\t\t\tx = bound(hm->firstsegx*4, x, hm->maxsegx*4-1);\n\t\t\ty = bound(hm->firstsegy*4, y, hm->maxsegy*4-1);\n\t\t\n\t\t\ts = Terr_GetSection(hm, x/4, y/4, TGS_FORCELOAD);\n\t\t\tif (!s)\n\t\t\t\treturn;\n\t\t\tted_sethole(&quant, s, (x&3) + (y&3)*4, x/4, y/4, 0);\n\t\t}\n\t*/\t\n\t\tpos[0] -= 0.5 * hm->sectionsize / 8;\n\t\tpos[1] -= 0.5 * hm->sectionsize / 8;\n\t\tted_itterate(hm, tid_linear, pos, radius, 1, 9, ted_sethole, &quant);\n\t\tbreak;\n\tcase ter_height_set:\n\t\tted_itterate(hm, tid_linear, pos, radius, 1, SECTHEIGHTSIZE, ted_heightset, &quant);\n\t\tbreak;\n\tcase ter_height_flatten:\n\t\ttally[0] = 0;\n\t\ttally[1] = 0;\n\t\tted_itterate(hm, tid_exponential, pos, radius, 1, SECTHEIGHTSIZE, ted_heighttally, &tally);\n\t\ttally[0] /= tally[1];\n\t\tif (IS_NAN(tally[0]))\n\t\t\ttally[0] = 0;\n\t\tted_itterate(hm, tid_exponential, pos, radius, quant, SECTHEIGHTSIZE, ted_heightsmooth, &tally);\n\n\t\tted_itterate(hm, tid_exponential, pos, radius, quant, SECTHEIGHTSIZE, ted_heightdebug, &tally);\n\t\tbreak;\n\tcase ter_height_smooth:\n\t\ttally[0] = 0;\n\t\ttally[1] = 0;\n\t\tted_itterate(hm, tid_linear, pos, radius, 1, SECTHEIGHTSIZE, ted_heighttally, &tally);\n\t\ttally[0] /= tally[1];\n\t\tif (IS_NAN(tally[0]))\n\t\t\ttally[0] = 0;\n\t\tted_itterate(hm, tid_linear, pos, radius, quant, SECTHEIGHTSIZE, ted_heightsmooth, &tally);\n\t\tbreak;\n\tcase ter_height_spread:\n\t\ttally[0] = 0;\n\t\ttally[1] = 0;\n\t\tted_itterate(hm, tid_exponential, pos, radius/2, 1, SECTHEIGHTSIZE, ted_heighttally, &tally);\n\t\ttally[0] /= tally[1];\n\t\tif (IS_NAN(tally[0]))\n\t\t\ttally[0] = 0;\n\t\tted_itterate(hm, tid_exponential, pos, radius, 1, SECTHEIGHTSIZE, ted_heightsmooth, &tally);\n\t\tbreak;\n\tcase ter_water_set:\n\t\tted_itterate(hm, tid_linear, pos, radius, 1, 9, ted_waterset, &quant);\n\t\tbreak;\n\tcase ter_lower:\n\t\tquant *= -1;\n\tcase ter_raise:\n\t\tted_itterate(hm, tid_exponential, pos, radius, quant, SECTHEIGHTSIZE, ted_heightraise, &quant);\n\t\tbreak;\n\tcase ter_tint:\n\t\tted_itterate(hm, tid_exponential, pos, radius, quant, SECTHEIGHTSIZE, ted_tint, G_VECTOR(OFS_PARM4));\t//and parm5 too\n\t\tbreak;\n//\tcase ter_mixset:\n//\t\tted_itterate(hm, tid_exponential, pos, radius, 1, SECTTEXSIZE, ted_mixset, G_VECTOR(OFS_PARM4));\n//\t\tbreak;\n\tcase ter_tex_blend:\n\t\tted_itterate(hm, tid_exponential, pos, radius, quant/10, SECTTEXSIZE, ted_texpaint, (void*)PR_GetStringOfs(prinst, OFS_PARM4));\n\t\tbreak;\n\tcase ter_tex_replace:\n\t\tted_itterate(hm, tid_exponential, pos, radius, 1, SECTTEXSIZE, ted_texreplace, (void*)PR_GetStringOfs(prinst, OFS_PARM3));\n\t\tbreak;\n\tcase ter_tex_concentrate:\n\t\tted_itterate(hm, tid_exponential, pos, radius, 1, SECTTEXSIZE, ted_texconcentrate, NULL);\n\t\tbreak;\n\tcase ter_tex_noise:\n\t\tted_itterate(hm, tid_exponential, pos, radius, 1, SECTTEXSIZE, ted_texnoise, NULL);\n\t\tbreak;\n\tcase ter_tex_blur:\n\t\tVector4Set(tally, 0, 0, 0, 0);\n\t\tted_itterate(hm, tid_exponential, pos, radius, 1, SECTTEXSIZE, ted_textally, &tally);\n\t\tVectorScale(tally, 1/(tally[3]*255), tally);\n\t\tted_itterate(hm, tid_exponential, pos, radius, quant, SECTTEXSIZE, ted_texset, &tally);\n\t\tbreak;\n\tcase ter_tex_get:\n\t\t{\n\t\t\tint x, y;\n\t\t\thmsection_t *s;\n\t\t\tx = pos[0] / hm->sectionsize;\n\t\t\ty = pos[1] / hm->sectionsize;\n\t\t\tx = bound(hm->firstsegx, x, hm->maxsegx-1);\n\t\t\ty = bound(hm->firstsegy, y, hm->maxsegy-1);\n\t\t\n\t\t\ts = Terr_GetSection(hm, x, y, TGS_WAITLOAD|TGS_DEFAULTONFAIL);\n\t\t\tif (!s)\n\t\t\t\treturn;\n\t\t\tx = bound(0, quant, 3);\n\t\t\tG_INT(OFS_RETURN) = PR_TempString(prinst, s->texname[x]);\n\t\t}\n\t\tbreak;\n\tcase ter_tex_mask:\n\t\tZ_Free(hm->texmask);\n\t\thm->texmask = NULL;\n\n\t\tif (G_INT(OFS_PARM1) == 0)\n\t\t\thm->texmask = NULL;\n\t\telse\n\t\t\thm->texmask = Z_StrDup(PR_GetStringOfs(prinst, OFS_PARM1));\n\t\tbreak;\n\tcase ter_tex_kill:\n\t\t{\n\t\t\tint x, y;\n\t\t\tx = pos[0] / hm->sectionsize;\n\t\t\ty = pos[1] / hm->sectionsize;\n\t\t\tx = bound(hm->firstsegx, x, hm->maxsegx-1);\n\t\t\ty = bound(hm->firstsegy, y, hm->maxsegy-1);\n\n\t\t\tted_texkill(Terr_GetSection(hm, x, y, TGS_WAITLOAD|TGS_DEFAULTONFAIL), PR_GetStringOfs(prinst, OFS_PARM4));\n\t\t}\n\t\tbreak;\n\tcase ter_reset:\n\t\t{\n\t\t\tint x, y;\n\t\t\thmsection_t *s;\n\t\t\tx = pos[0] / hm->sectionsize;\n\t\t\ty = pos[1] / hm->sectionsize;\n\t\t\tx = bound(hm->firstsegx, x, hm->maxsegx-1);\n\t\t\ty = bound(hm->firstsegy, y, hm->maxsegy-1);\n\t\t\ts = Terr_GetSection(hm, x, y, TGS_WAITLOAD|TGS_DEFAULTONFAIL);\n\t\t\tif (s)\n\t\t\t{\n\t\t\t\ts->flags = (s->flags & ~TSF_EDITED);\n\t\t\t\tTerr_ClearSection(s);\n\t\t\t\tTerr_GenerateDefault(hm, s);\n\t\t\t}\n\t\t}\n\t\tbreak;\n\tcase ter_mesh_add:\n\t\t{\n\t\t\tvec3_t axis[3];\n\t\t\twedict_t *ed = G_WEDICT(prinst, OFS_PARM1);\n\t\t\t//FIXME: modeltype pitch inversion\n\t\t\tAngleVectorsFLU(ed->v->angles, axis[0], axis[1], axis[2]);\n\t\t\tTerr_AddMesh(hm, TGS_WAITLOAD|TGS_DEFAULTONFAIL, vmw->Get_CModel(vmw, ed->v->modelindex), NULL, ed->v->origin, axis, ed->xv->scale);\n\t\t}\n\t\tbreak;\n\tcase ter_mesh_kill:\n\t\t{\n\t\t\tint i;\n//\t\t\tentity_t *e;\n\t\t\tint x, y;\n//\t\t\tfloat r;\n\t\t\thmsection_t *s;\n\t\t\tx = pos[0] / hm->sectionsize;\n\t\t\ty = pos[1] / hm->sectionsize;\n\t\t\tx = bound(hm->firstsegx, x, hm->maxsegx-1);\n\t\t\ty = bound(hm->firstsegy, y, hm->maxsegy-1);\n\t\t\n\t\t\ts = Terr_GetSection(hm, x, y, TGS_WAITLOAD|TGS_DEFAULTONFAIL);\n\t\t\tif (!s)\n\t\t\t\treturn;\n\n\t\t\tSys_LockMutex(hm->entitylock);\n\t\t\t//FIXME: this doesn't work properly.\n\t\t\tif (s->numents)\n\t\t\t{\n\t\t\t\tfor (i = 0; i < s->numents; i++)\n\t\t\t\t\ts->ents[i]->refs -= 1;\n\t\t\t\ts->flags |= TSF_EDITED;\n\t\t\t\ts->numents = 0;\n\t\t\t}\n\t\t\tSys_UnlockMutex(hm->entitylock);\n\t\t}\n\t\tbreak;\n\t}\n}\n#else\nstatic unsigned char *QDECL Terr_GetLightmap(hmsection_t *s, int idx, qboolean edit)\n{\n\treturn NULL;\n}\nvoid QCBUILTIN PF_terrain_edit(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tG_FLOAT(OFS_RETURN) = 0;\n}\n#endif\n\nvoid Terr_ParseEntityLump(model_t *mod, heightmap_t *heightmap)\n{\n\tchar key[128];\n\tchar value[2048];\n\tconst char *data = Mod_GetEntitiesString(mod);\n\n\theightmap->sectionsize = 1024;\n\theightmap->mode = HMM_TERRAIN;\n\theightmap->culldistance = 4096*4096;\n\theightmap->forcedefault = false;\n\n\theightmap->defaultgroundheight = 0;\n\theightmap->defaultwaterheight = 0;\n\tQ_snprintfz(heightmap->defaultwatershader, sizeof(heightmap->defaultwatershader), \"water/%s\", heightmap->path);\n\tQ_strncpyz(heightmap->defaultgroundtexture, \"\", sizeof(heightmap->defaultgroundtexture));\n\n\tif (data)\n\tif ((data=COM_ParseOut(data, key, sizeof(key))))\t//read the map info.\n\tif (key[0] == '{')\n\twhile (1)\n\t{\n\t\tif (!(data=COM_ParseOut(data, key, sizeof(key))))\n\t\t\tbreak; // error\n\t\tif (key[0] == '}')\n\t\t\tbreak; // end of worldspawn\n\t\tif (key[0] == '_')\n\t\t\tmemmove(key, key+1, strlen(key)); //_ vars are for comments/utility stuff that arn't visible to progs and for compat. We want to support these stealth things.\n\t\tif (!((data=COM_ParseOut(data, value, sizeof(value)))))\n\t\t\tbreak; // error\t\t\n\t\tif (!strcmp(\"segmentsize\", key))\n\t\t\theightmap->sectionsize = atof(value);\n\t\telse if (!strcmp(\"minxsegment\", key))\n\t\t\theightmap->firstsegx = atoi(value);\n\t\telse if (!strcmp(\"minysegment\", key))\n\t\t\theightmap->firstsegy = atoi(value);\n\t\telse if (!strcmp(\"maxxsegment\", key))\n\t\t\theightmap->maxsegx = atoi(value);\n\t\telse if (!strcmp(\"maxysegment\", key))\n\t\t\theightmap->maxsegy = atoi(value);\n\t\telse if (!strcmp(\"forcedefault\", key))\n\t\t\theightmap->forcedefault = !!atoi(value);\n\t\telse if (!strcmp(\"defaultwaterheight\", key))\n\t\t\theightmap->defaultwaterheight = atof(value);\n\t\telse if (!strcmp(\"defaultgroundheight\", key))\n\t\t\theightmap->defaultgroundheight = atof(value);\n\t\telse if (!strcmp(\"defaultgroundtexture\", key))\n\t\t\tQ_strncpyz(heightmap->defaultgroundtexture, value, sizeof(heightmap->defaultgroundtexture));\n\t\telse if (!strcmp(\"defaultwatertexture\", key))\n\t\t\tQ_strncpyz(heightmap->defaultwatershader, value, sizeof(heightmap->defaultwatershader));\n\t\telse if (!strcmp(\"culldistance\", key))\n\t\t{\n\t\t\theightmap->culldistance = atof(value);\n\t\t\theightmap->culldistance *= heightmap->culldistance;\n\t\t}\n\t\telse if (!strcmp(\"drawdist\", key))\n\t\t\theightmap->maxdrawdist = atof(value);\n\t\telse if (!strcmp(\"seed\", key))\n\t\t{\n\t\t\tZ_Free(heightmap->seed);\n\t\t\theightmap->seed = Z_StrDup(value);\n\t\t}\n\t\telse if (!strcmp(\"exterior\", key))\n\t\t{\n\t\t\theightmap->legacyterrain = false;\n\t\t\tif (!strcmp(value, \"empty\") || !strcmp(value, \"\"))\n\t\t\t\theightmap->exteriorcontents = FTECONTENTS_EMPTY;\n\t\t\telse if (!strcmp(value, \"sky\"))\n\t\t\t\theightmap->exteriorcontents = FTECONTENTS_SKY;\n\t\t\telse if (!strcmp(value, \"lava\"))\n\t\t\t\theightmap->exteriorcontents = FTECONTENTS_LAVA;\n\t\t\telse //if (!strcmp(value, \"solid\"))\n\t\t\t\theightmap->exteriorcontents = FTECONTENTS_SOLID;\n\t\t}\n\t\telse if (!strcmp(\"skybox\", key))\n\t\t\tQ_strncpyz(heightmap->skyname, value, sizeof(heightmap->skyname));\n\t\telse if (!strcmp(\"tiles\", key))\n\t\t{\n\t\t\tchar *d;\n\t\t\theightmap->mode = HMM_BLOCKS;\n\t\t\td = value;\n\t\t\td = COM_ParseOut(d, key, sizeof(key));\n\t\t\theightmap->tilepixcount[0] = atoi(key);\n\t\t\td = COM_ParseOut(d, key, sizeof(key));\n\t\t\theightmap->tilepixcount[1] = atoi(key);\n\t\t\td = COM_ParseOut(d, key, sizeof(key));\n\t\t\theightmap->tilecount[0] = atoi(key);\n\t\t\td = COM_ParseOut(d, key, sizeof(key));\n\t\t\theightmap->tilecount[1] = atoi(key);\n\t\t}\n\t}\n\n\t/*bias and bound it*/\n\theightmap->firstsegx += CHUNKBIAS;\n\theightmap->firstsegy += CHUNKBIAS;\n\theightmap->maxsegx += CHUNKBIAS;\n\theightmap->maxsegy += CHUNKBIAS;\n\tif (heightmap->firstsegx < 0)\n\t\theightmap->firstsegx = 0;\n\tif (heightmap->firstsegy < 0)\n\t\theightmap->firstsegy = 0;\n\tif (heightmap->maxsegx > CHUNKLIMIT)\n\t\theightmap->maxsegx = CHUNKLIMIT;\n\tif (heightmap->maxsegy > CHUNKLIMIT)\n\t\theightmap->maxsegy = CHUNKLIMIT;\n}\n\nvoid Terr_FinishTerrain(model_t *mod)\n{\n#ifdef HAVE_CLIENT\n\theightmap_t *hm = mod->terrain;\n\tif (qrenderer != QR_NONE)\n\t{\n\t\tif (*hm->skyname)\n\t\t{\n\t\t\thm->skyshader = R_RegisterCustom(mod, va(\"skybox_%s\", hm->skyname), SUF_NONE, Shader_DefaultSkybox, NULL);\n\t\t\tif (!hm->skyshader->skydome)\n\t\t\t\thm->skyshader = NULL;\n\t\t}\n\t\telse\n\t\t\thm->skyshader = NULL;\n\n\t\tswitch (hm->mode)\n\t\t{\n\t\tcase HMM_BLOCKS:\n\t\t\thm->shader = R_RegisterShader(\"terraintileshader\", SUF_NONE,\n\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\t\"map $diffuse\\n\"\t\n\t\t\t\t\t\t\"}\\n\"\n\t\t\t\t\t\"}\\n\"\n\t\t\t\t);\n\t\t\tbreak;\n\t\tcase HMM_TERRAIN:\n\t\t\thm->shader = R_RegisterShader(hm->groundshadername, SUF_LIGHTMAP,\n\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\"bemode rtlight\\n\"\n\t\t\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\t\t\t\"map $diffuse\\n\"\n\t\t\t\t\t\t\t\t\t\"blendfunc add\\n\"\n\t\t\t\t\t\t\t\t\"}\\n\"\n\t\t\t\t\t\t\t\t//FIXME: these maps are a legacy thing, and could be removed if third-party glsl properly contains s_diffuse\n\t\t\t\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\t\t\t\"map $upperoverlay\\n\"\n\t\t\t\t\t\t\t\t\"}\\n\"\n\t\t\t\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\t\t\t\"map $loweroverlay\\n\"\n\t\t\t\t\t\t\t\t\"}\\n\"\n\t\t\t\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\t\t\t\"map $fullbright\\n\"\n\t\t\t\t\t\t\t\t\"}\\n\"\n\t\t\t\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\t\t\t\"map $lightmap\\n\"\n\t\t\t\t\t\t\t\t\"}\\n\"\n\t\t\t\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\t\t\t\"map $shadowmap\\n\"\n\t\t\t\t\t\t\t\t\"}\\n\"\n\t\t\t\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\t\t\t\"map $lightcubemap\\n\"\n\t\t\t\t\t\t\t\t\"}\\n\"\n\t\t\t\t\t\t\t\t//woo, one glsl to rule them all\n\t\t\t\t\t\t\t\t\"program terrain#RTLIGHT\\n\"\n\t\t\t\t\t\t\t\"}\\n\"\n\t\t\t\t\t\t\"bemode depthdark\\n\"\n\t\t\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\t\t\"program depthonly\\n\"\n\t\t\t\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\t\t\t\"depthwrite\\n\"\n\t\t\t\t\t\t\t\t\"}\\n\"\n\t\t\t\t\t\t\t\"}\\n\"\n\t\t\t\t\t\t\"bemode depthonly\\n\"\n\t\t\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\t\t\"program depthonly\\n\"\n\t\t\t\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\t\t\t\"depthwrite\\n\"\n\t\t\t\t\t\t\t\t\t\"maskcolor\\n\"\n\t\t\t\t\t\t\t\t\"}\\n\"\n\t\t\t\t\t\t\t\"}\\n\"\n\n\t\t\t\t\t\t//FIXME: these maps are a legacy thing, and could be removed if third-party glsl properly contains s_diffuse\n\t\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\t\"map $diffuse\\n\"\n\t\t\t\t\t\t\t\"rgbgen vertex\\n\"\n\t\t\t\t\t\t\t\"alphagen vertex\\n\"\n\t\t\t\t\t\t\"}\\n\"\n\t\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\t\"map $upperoverlay\\n\"\n\t\t\t\t\t\t\"}\\n\"\n\t\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\t\"map $loweroverlay\\n\"\n\t\t\t\t\t\t\"}\\n\"\n\t\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\t\"map $fullbright\\n\"\n\t\t\t\t\t\t\"}\\n\"\n\t\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\t\"map $lightmap\\n\"\n\t\t\t\t\t\t\"}\\n\"\n\t\t\t\t\t\t\"program terrain\\n\"\n\t\t\t\t\t\t\"if r_terraindebug\\n\"\n\t\t\t\t\t\t\"program terraindebug\\n\"\n\t\t\t\t\t\t\"endif\\n\"\n\t\t\t\t\t\"}\\n\"\n\t\t\t\t);\n\t\t\tbreak;\n\t\t}\n\t}\n#endif\n}\n\n#ifdef HAVE_CLIENT\nvoid Terr_Brush_Draw(heightmap_t *hm, batch_t **batches, entity_t *e)\n{\n\tbatch_t *b;\n\tsize_t i, j;\n\tvbobctx_t ctx;\n\n\tbrushbatch_t *bb;\n\tbrushtex_t *bt;\n\tbrushes_t *br;\n\n\tstruct {\n\t\tvecV_t coord[65536];\n\t\tvec2_t texcoord[65536];\n\t\tvec2_t lmcoord[65536];\n\t\tvec3_t normal[65536];\n\t\tvec3_t svector[65536];\n\t\tvec3_t tvector[65536];\n\t\tvec4_t rgba[65536];\n\t\tindex_t index[65535];\n\t} *arrays = NULL;\n\tsize_t numverts = 0;\n\tsize_t numindicies = 0;\n\tint w, h, lmnum;\n\tfloat scale[2];\n#ifdef RUNTIMELIGHTING\n\tlightmapinfo_t *lm;\n\tqboolean dorelight = true;\n\n\t//FIXME: lightmaps\n\t//if we're enabling lightmaps, make sure all surfaces have known sizes first.\n\t//allocate lightmap space for all surfaces, and then rebuild all textures.\n\t//if a surface is modified, clear its lightmap to -1 and when its batches are rebuilt, it'll unlight naturally.\n\n\tif (hm->entsdirty)\n\t{\n\t\tmodel_t *mod = e->model;\n\t\tif (mod->submodelof)\n\t\t\tmod = mod->submodelof;\n\t\thm->entsdirty = false;\n\t\tif (hm->relightcontext)\n\t\t\tLightReloadEntities(hm->relightcontext, Mod_GetEntitiesString(mod), true);\n\n\t\t//FIXME: figure out some way to hint this without having to relight the entire frigging world.\n\t\tfor (bt = hm->brushtextures; bt; bt = bt->next)\n\t\t\tfor (i = 0, br = hm->wbrushes; i < hm->numbrushes; i++, br++)\n\t\t\t\tfor (j = 0; j < br->numplanes; j++)\n\t\t\t\t\tbr->faces[j].relight = true;\n\t}\n\n\tif (hm->recalculatebrushlighting && !r_fullbright.ival)\n\t{\n\t\tunsigned int lmcount;\n\t\tunsigned int lmblocksize = 512;//LMBLOCK_SIZE_MAX\n\t\thm->recalculatebrushlighting = false;\n\n\t\tif (!hm->relightcontext)\n\t\t{\n\t\t\tfor (numverts = 0, numindicies = 0, i = 0, br = hm->wbrushes; i < hm->numbrushes; i++, br++)\n\t\t\t{\n\t\t\t\tfor (j = 0; j < br->numplanes; j++)\n\t\t\t\t{\n\t\t\t\t\tbr->faces[j].lightmap = -1;\n\t\t\t\t\tbr->faces[j].lmbase[0] = 0;\n\t\t\t\t\tbr->faces[j].lmbase[1] = 0;\n\t\t\t\t}\n\t\t\t}\n\t\t\tfor (bt = hm->brushtextures; bt; bt = bt->next)\n\t\t\t{\n\t\t\t\tbt->rebuild = true;\n\t\t\t\tbt->firstlm = 0;\n\t\t\t\tbt->lmcount = 0;\n\t\t\t}\n\n\t\t\tBZ_Free(hm->brushlmremaps);\n\t\t\thm->brushlmremaps = NULL;\n\t\t\thm->brushmaxlms = 0;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tMod_LightmapAllocInit(&hm->brushlmalloc, false, lmblocksize, lmblocksize, 0);\n\t\t\thm->brushlmscale = 1.0/lmblocksize;\n\n\t\t\t//textures is to try to ensure that they are allocated consecutively.\n\t\t\tfor (bt = hm->brushtextures; bt; bt = bt->next)\n\t\t\t{\n\t\t\t\tbt->firstlm = hm->brushlmalloc.lmnum;\n\t\t\t\tfor (numverts = 0, numindicies = 0, i = 0, br = hm->wbrushes; i < hm->numbrushes; i++, br++)\n\t\t\t\t{\n\t\t\t\t\tfor (j = 0; j < br->numplanes; j++)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (br->faces[j].tex == bt)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (br->faces[j].lightdata)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tMod_LightmapAllocBlock(&hm->brushlmalloc, br->faces[j].lmextents[0], br->faces[j].lmextents[1], &br->faces[j].lmbase[0], &br->faces[j].lmbase[1], &br->faces[j].lightmap);\n\t\t\t\t\t\t\t\tbr->faces[j].relit = true;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t{\t//this surface has no lightmap info or something.\n\t\t\t\t\t\t\t\tbr->faces[j].lightmap = -1;\n\t\t\t\t\t\t\t\tbr->faces[j].lmbase[0] = 0;\n\t\t\t\t\t\t\t\tbr->faces[j].lmbase[1] = 0;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbt->rebuild = true;\n\t\t\t\tbt->lmcount = hm->brushlmalloc.lmnum - bt->firstlm;\n\t\t\t\tif (hm->brushlmalloc.allocated[0])\n\t\t\t\t\tbt->lmcount++;\n\t\t\t\tif (hm->brushlmalloc.deluxe)\n\t\t\t\t{\n\t\t\t\t\tbt->firstlm *= 2;\n\t\t\t\t\tbt->lmcount *= 2;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tlmcount = hm->brushlmalloc.lmnum;\n\t\t\tif (hm->brushlmalloc.allocated[0])\n\t\t\t\tlmcount++;\n\t\t\tif (hm->brushlmalloc.deluxe)\n\t\t\t\tlmcount *= 2;\n\n\t\t\tif (lmcount > hm->brushmaxlms)\n\t\t\t{\n\t\t\t\tint first;\n\t\t\t\thm->brushlmremaps = BZ_Realloc(hm->brushlmremaps, sizeof(*hm->brushlmremaps) * lmcount);\n\t\t\t\tfirst = Surf_NewLightmaps(lmcount - hm->brushmaxlms, hm->brushlmalloc.width, hm->brushlmalloc.height, PTI_BGRA8, hm->brushlmalloc.deluxe);\n\n\t\t\t\twhile(hm->brushmaxlms < lmcount)\n\t\t\t\t\thm->brushlmremaps[hm->brushmaxlms++] = first++;\n\t\t\t}\n\t\t}\n\t}\n\n\tif (hm->relightcontext && !r_fullbright.ival)\n\tfor (i = 0, br = hm->wbrushes; i < hm->numbrushes; i++, br++)\n\t{\n\t\tfor (j = 0; j < br->numplanes; j++)\n\t\t{\n\t\t\tif (br->faces[j].relight && dorelight)\n\t\t\t{\n\t\t\t\tlightstyleindex_t styles[max(2,MAXCPULIGHTMAPS)] = {0,INVALID_LIGHTSTYLE};\n\t\t\t\tint texsize[2] = {br->faces[j].lmextents[0]-1, br->faces[j].lmextents[1]-1};\n\t\t\t\tvec2_t exactmins, exactmaxs;\n\t\t\t\tint m, k;\n\t\t\t\tvec2_t lm;\n\t\t\t\tfor (m = 0; m < br->faces[j].numpoints; m++)\n\t\t\t\t{\n\t\t\t\t\tfor (k = 0; k < 2; k++)\n\t\t\t\t\t{\n\t\t\t\t\t\tlm[k] = DotProduct(br->faces[j].points[m], br->faces[j].stdir[k]) + br->faces[j].stdir[k][3];\n\t\t\t\t\t\tif (m == 0)\n\t\t\t\t\t\texactmins[k] = exactmaxs[k] = lm[k];\n\t\t\t\t\t\telse if (lm[k] > exactmaxs[k])\n\t\t\t\t\t\texactmaxs[k] = lm[k];\n\t\t\t\t\t\telse if (lm[k] < exactmins[k])\n\t\t\t\t\t\texactmins[k] = lm[k];\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tdorelight = false;\n\t\t\t\tbr->faces[j].relight = false;\n\t\t\t\tLightPlane (hm->relightcontext, hm->lightthreadmem, styles, NULL, br->faces[j].lightdata, NULL, br->planes[j], br->faces[j].stdir, exactmins, exactmaxs, br->faces[j].lmbias, texsize, br->faces[j].lmscale);\t//special version that doesn't know what a face is or anything.\n\t\t\t\tbr->faces[j].relit = true;\n\t\t\t}\n\t\t\tif (br->faces[j].relit && br->faces[j].lightmap >= 0)\n\t\t\t{\n\t\t\t\tint s,t;\n\t\t\t\tqbyte *out, *in;\n\t\t\t\tlm = lightmap[hm->brushlmremaps[br->faces[j].lightmap]];\n\n\t\t\t\tbr->faces[j].relit = false;\n\n\t\t\t\tlm->modified = true;\n\t\t\t\tlm->rectchange.l = 0;\n\t\t\t\tlm->rectchange.t = 0;\n\t\t\t\tlm->rectchange.r = lm->width;\n\t\t\t\tlm->rectchange.b = lm->height;\n\n\t\t\t\tin = br->faces[j].lightdata;\n\t\t\t\tout = lm->lightmaps + (br->faces[j].lmbase[1] * lm->width + br->faces[j].lmbase[0]) * lm->pixbytes;\n\t\t\t\tswitch(lm->fmt)\n\t\t\t\t{\n\t\t\t\tdefault:\n\t\t\t\t\tSys_Error(\"Bad terrain lightmap format %i\\n\", lm->fmt);\n\t\t\t\t\tbreak;\n\t\t\t\tcase PTI_BGRA8:\n\t\t\t\tcase PTI_BGRX8:\n\t\t\t\t\tfor (t = 0; t < br->faces[j].lmextents[1]; t++)\n\t\t\t\t\t{\n\t\t\t\t\t\tfor (s = 0; s < br->faces[j].lmextents[0]; s++)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t*out++ = in[2];\n\t\t\t\t\t\t\t*out++ = in[1];\n\t\t\t\t\t\t\t*out++ = in[0];\n\t\t\t\t\t\t\t*out++ = 0xff;\n\t\t\t\t\t\t\tin+=3;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tout += (lm->width - br->faces[j].lmextents[0]) * 4;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase PTI_RGBA8:\n\t\t\t\tcase PTI_RGBX8:\n\t\t\t\t\tfor (t = 0; t < br->faces[j].lmextents[1]; t++)\n\t\t\t\t\t{\n\t\t\t\t\t\tfor (s = 0; s < br->faces[j].lmextents[0]; s++)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t*out++ = in[0];\n\t\t\t\t\t\t\t*out++ = in[1];\n\t\t\t\t\t\t\t*out++ = in[2];\n\t\t\t\t\t\t\t*out++ = 0xff;\n\t\t\t\t\t\t\tin+=3;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tout += (lm->width - br->faces[j].lmextents[0]) * 4;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase PTI_BGR8:\n\t\t\t\t\tfor (t = 0; t < br->faces[j].lmextents[1]; t++)\n\t\t\t\t\t{\n\t\t\t\t\t\tfor (s = 0; s < br->faces[j].lmextents[0]; s++)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t*out++ = in[2];\n\t\t\t\t\t\t\t*out++ = in[1];\n\t\t\t\t\t\t\t*out++ = in[0];\n\t\t\t\t\t\t\tin+=3;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tout += (lm->width - br->faces[j].lmextents[0]) * 3;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase PTI_RGB8:\n\t\t\t\t\tfor (t = 0; t < br->faces[j].lmextents[1]; t++)\n\t\t\t\t\t{\n\t\t\t\t\t\tfor (s = 0; s < br->faces[j].lmextents[0]; s++)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t*out++ = in[0];\n\t\t\t\t\t\t\t*out++ = in[1];\n\t\t\t\t\t\t\t*out++ = in[2];\n\t\t\t\t\t\t\tin+=3;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tout += (lm->width - br->faces[j].lmextents[0]) * 3;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\n\n\t\t\t\tcase PTI_A2BGR10:\n\t\t\t\t\tfor (t = 0; t < br->faces[j].lmextents[1]; t++)\n\t\t\t\t\t{\n\t\t\t\t\t\tfor (s = 0; s < br->faces[j].lmextents[0]; s++)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t*(unsigned int*)out = (0x3u<<30) | (in[2]<<22) | (in[1]<<12) | (in[0]<<2);\n\t\t\t\t\t\t\tout+=4;\n\t\t\t\t\t\t\tin+=3;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tout += (lm->width - br->faces[j].lmextents[0]) * 4;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t/*case PTI_E5BGR9:\n\t\t\t\t\tfor (t = 0; t < br->faces[j].lmextents[1]; t++)\n\t\t\t\t\t{\n\t\t\t\t\t\tfor (s = 0; s < br->faces[j].lmextents[0]; s++)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t*(unsigned int*)out = Surf_PackE5BRG9(in[0], in[1], in[2], 8);\n\t\t\t\t\t\t\tout+=4;\n\t\t\t\t\t\t\tin+=3;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tout += (lm->width - br->faces[j].lmextents[0]) * 4;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;*/\n\t\t\t\tcase PTI_L8:\n\t\t\t\t\tfor (t = 0; t < br->faces[j].lmextents[1]; t++)\n\t\t\t\t\t{\n\t\t\t\t\t\tfor (s = 0; s < br->faces[j].lmextents[0]; s++)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t*out++ = max(max(in[0], in[1]), in[2]);\n\t\t\t\t\t\t\tin+=3;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tout += (lm->width - br->faces[j].lmextents[0]);\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase PTI_RGBA32F:\n\t\t\t\t\tfor (t = 0; t < br->faces[j].lmextents[1]; t++)\n\t\t\t\t\t{\n\t\t\t\t\t\tfor (s = 0; s < br->faces[j].lmextents[0]; s++)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t((float*)out)[0] = in[0]/255.0;\n\t\t\t\t\t\t\t((float*)out)[1] = in[1]/255.0;\n\t\t\t\t\t\t\t((float*)out)[2] = in[2]/255.0;\n\t\t\t\t\t\t\t((float*)out)[3] = 1.0;\n\t\t\t\t\t\t\tout+=16;\n\t\t\t\t\t\t\tin+=3;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tout += (lm->width - br->faces[j].lmextents[0]) * 16;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t/*case PTI_RGBA16F:\n\t\t\t\t\tfor (t = 0; t < br->faces[j].lmextents[1]; t++)\n\t\t\t\t\t{\n\t\t\t\t\t\tfor (s = 0; s < br->faces[j].lmextents[0]; s++)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tSurf_PackRGB16F(in[0], in[1], in[2], 255);\n\t\t\t\t\t\t\tout+=8;\n\t\t\t\t\t\t\tin+=3;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tout += (lm->width - br->faces[j].lmextents[0]) * 8;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;*/\n\t\t\t\tcase PTI_RGB565:\n\t\t\t\tcase PTI_RGBA4444:\n\t\t\t\tcase PTI_RGBA5551:\n\t\t\t\tcase PTI_ARGB4444:\n\t\t\t\tcase PTI_ARGB1555:\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n#endif\n\n\tfor (bt = hm->brushtextures; bt; bt = bt->next)\n\t{\n\t\tif (!bt->shader)\n\t\t{\n#ifdef PACKAGE_TEXWAD\n\t\t\tmiptex_t *tx = W_GetMipTex(bt->shadername);\n#else\n\t\t\tconst miptex_t *tx = NULL;\n#endif\n\n\t\t\tbt->shader = R_RegisterCustom (NULL, va(\"textures/%s\", bt->shadername), SUF_LIGHTMAP, NULL, NULL);\n\t\t\tif (!bt->shader)\n\t\t\t{\n\t\t\t\tif (!Q_strcasecmp(bt->shadername, \"clip\") || !Q_strcasecmp(bt->shadername, \"hint\") || !Q_strcasecmp(bt->shadername, \"skip\"))\n\t\t\t\t\tbt->shader = R_RegisterShader(bt->shadername, SUF_LIGHTMAP, \"{\\nsurfaceparm nodraw\\n}\");\n\t\t\t\telse\n\t\t\t\t\tbt->shader = R_RegisterCustom (NULL, bt->shadername, SUF_LIGHTMAP, Shader_DefaultBSPQ1, NULL);\n//\t\t\t\t\tbt->shader = R_RegisterShader_Lightmap(bt->shadername);\n\t\t\t}\n\n\t\t\tif (!Q_strncasecmp(bt->shadername, \"sky\", 3) && tx)\n\t\t\t\tR_InitSky (bt->shader, bt->shadername, TF_SOLID8, (qbyte*)tx + tx->offsets[0], tx->width, tx->height);\n\t\t\telse if (tx)\n\t\t\t{\n\t\t\t\tunsigned int mapflags = SHADER_HASPALETTED | SHADER_HASDIFFUSE | SHADER_HASFULLBRIGHT | SHADER_HASNORMALMAP | SHADER_HASGLOSS;\n\t\t\t\tR_BuildLegacyTexnums(bt->shader, tx->name, NULL, mapflags, 0, TF_SOLID8, tx->width, tx->height, (qbyte*)tx + tx->offsets[0], NULL);\n\t\t\t}\n\t\t\telse\n\t\t\t\tR_BuildDefaultTexnums(NULL, bt->shader, IF_WORLDTEX);\n\n\t\t\tif (tx)\n\t\t\t{\n\t\t\t\tif (!bt->shader->width)\n\t\t\t\t\tbt->shader->width = tx->width;\n\t\t\t\tif (!bt->shader->height)\n\t\t\t\t\tbt->shader->height = tx->height;\n\t\t\t\tBZ_Free(tx);\n\t\t\t}\n\t\t}\n\n\t\tif (bt->rebuild)\n\t\t{\n\t\t\t//FIXME: don't block.\n\t\t\tif (R_GetShaderSizes(bt->shader, &w, &h, false) < 0)\n\t\t\t\tcontinue;\n\t\t\tbt->rebuild = false;\n\n\t\t\tif (w<1) w = 64;\n\t\t\tif (h<1) h = 64;\n\t\t\tscale[0] = mod_terrain_brushtexscale.value/w;\t//I hate needing this.\n\t\t\tscale[1] = mod_terrain_brushtexscale.value/h;\n\n\t\t\twhile(bt->batches)\n\t\t\t{\n\t\t\t\tbb = bt->batches;\n\t\t\t\tbt->batches = bb->next;\n\n\t\t\t\tBE_VBO_Destroy(&bb->vbo.coord, bb->vbo.vbomem);\n\t\t\t\tBE_VBO_Destroy(&bb->vbo.indicies, bb->vbo.ebomem);\n\t\t\t\tBZ_Free(bb);\n\t\t\t}\n\n\t\t\tif (!arrays)\n\t\t\t\tarrays = BZ_Malloc(sizeof(*arrays));\n\n\t\t\tfor (lmnum = -1; lmnum < bt->firstlm+bt->lmcount; ((lmnum==-1)?(lmnum=bt->firstlm):(lmnum=lmnum+1)))\n\t\t\t{\n\t\t\t\ti = 0;\n\t\t\t\tbr = hm->wbrushes;\n\t\t\t\tfor (;i < hm->numbrushes;)\n\t\t\t\t{\n\t\t\t\t\tfor (numverts = 0, numindicies = 0; i < hm->numbrushes; i++, br++)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (br->selected)\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\tif (br->patch)\n\t\t\t\t\t\t{\t//this one's a patch\n\t\t\t\t\t\t\tif (br->patch->tex == bt && lmnum == -1)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tint x, y;\n\t\t\t\t\t\t\t\tindex_t r1, r2;\n\n\t\t\t\t\t\t\t\tif (br->patch->tessvert && !br->selected)\n\t\t\t\t\t\t\t\t{\t//tessellated version of the patch.\n\n\t\t\t\t\t\t\t\t\t//make sure we don't overflow anything.\n\t\t\t\t\t\t\t\t\tsize_t newverts = br->patch->tesssize[0]*br->patch->tesssize[1], newindexes = (br->patch->tesssize[0]-1)*(br->patch->tesssize[1]-1)*6;\n\t\t\t\t\t\t\t\t\tif (numverts+newverts >= 0xffff || numindicies+newindexes >= 0xffff)\n\t\t\t\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\t\t\t\tfor (y = 0, r1 = numverts, r2 = 0; y < br->patch->tesssize[1]; y++)\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tfor (x = 0; x < br->patch->tesssize[0]; x++, r1++, r2++)\n\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\tVectorCopy(br->patch->tessvert[r2].v, arrays->coord[r1]);\n\t\t\t\t\t\t\t\t\t\t\tVector2Copy(br->patch->tessvert[r2].tc, arrays->texcoord[r1]);\n\t\t\t\t\t\t\t\t\t\t\tVector4Copy(br->patch->tessvert[r2].rgba, arrays->rgba[r1]);\n\n\t\t\t\t\t\t\t\t\t\t\t//lame\n\t\t\t\t\t\t\t\t\t\t\tVector2Copy(br->patch->tessvert[r2].tc, arrays->lmcoord[r1]);\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tfor (y = 0, r1 = numverts, r2 = r1 + br->patch->tesssize[0]; y < br->patch->tesssize[1]-1; y++)\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tfor (x = 0; x < br->patch->tesssize[0]-1; x++, r1++, r2++)\n\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\tarrays->index[numindicies++] = r1;\n\t\t\t\t\t\t\t\t\t\t\tarrays->index[numindicies++] = r1+1;\n\t\t\t\t\t\t\t\t\t\t\tarrays->index[numindicies++] = r2;\n\n\t\t\t\t\t\t\t\t\t\t\tarrays->index[numindicies++] = r1+1;\n\t\t\t\t\t\t\t\t\t\t\tarrays->index[numindicies++] = r2+1;\n\t\t\t\t\t\t\t\t\t\t\tarrays->index[numindicies++] = r2;\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\tr1++; r2++;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tMod_AccumulateTextureVectors(arrays->coord, arrays->texcoord, arrays->normal, arrays->svector, arrays->tvector, arrays->index+numindicies-newindexes, newindexes, true);\n\t\t\t\t\t\t\t\t\tMod_NormaliseTextureVectors(arrays->normal+numverts, arrays->svector+numverts, arrays->tvector+numverts, newverts, true);\n\t\t\t\t\t\t\t\t\tnumverts += newverts;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t{\t//control-point representation.\n\n\t\t\t\t\t\t\t\t\t//make sure we don't overflow anything.\n\t\t\t\t\t\t\t\t\tsize_t newverts = br->patch->numcp[0]*br->patch->numcp[1], newindexes = (br->patch->numcp[0]-1)*(br->patch->numcp[1]-1)*6;\n\t\t\t\t\t\t\t\t\tif (numverts+newverts >= 0xffff || numindicies+newindexes >= 0xffff)\n\t\t\t\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\t\t\t\tfor (y = 0, r1 = numverts, r2 = 0; y < br->patch->numcp[1]; y++)\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tfor (x = 0; x < br->patch->numcp[0]; x++, r1++, r2++)\n\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\tVectorCopy(br->patch->cp[r2].v, arrays->coord[r1]);\n\t\t\t\t\t\t\t\t\t\t\tVector2Copy(br->patch->cp[r2].tc, arrays->texcoord[r1]);\n\t\t\t\t\t\t\t\t\t\t\tVector4Copy(br->patch->cp[r2].rgba, arrays->rgba[r1]);\n\n\t\t\t\t\t\t\t\t\t\t\t//lame\n\t\t\t\t\t\t\t\t\t\t\tVector2Copy(br->patch->cp[r2].tc, arrays->lmcoord[r1]);\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tfor (y = 0, r1 = numverts, r2 = r1 + br->patch->numcp[0]; y < br->patch->numcp[1]-1; y++)\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tfor (x = 0; x < br->patch->numcp[0]-1; x++, r1++, r2++)\n\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\tarrays->index[numindicies++] = r1;\n\t\t\t\t\t\t\t\t\t\t\tarrays->index[numindicies++] = r1+1;\n\t\t\t\t\t\t\t\t\t\t\tarrays->index[numindicies++] = r2;\n\n\t\t\t\t\t\t\t\t\t\t\tarrays->index[numindicies++] = r1+1;\n\t\t\t\t\t\t\t\t\t\t\tarrays->index[numindicies++] = r2+1;\n\t\t\t\t\t\t\t\t\t\t\tarrays->index[numindicies++] = r2;\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\tr1++; r2++;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tMod_AccumulateTextureVectors(arrays->coord, arrays->texcoord, arrays->normal, arrays->svector, arrays->tvector, arrays->index+numindicies-newindexes, newindexes, true);\n\t\t\t\t\t\t\t\t\tMod_NormaliseTextureVectors(arrays->normal+numverts, arrays->svector+numverts, arrays->tvector+numverts, newverts, true);\n\t\t\t\t\t\t\t\t\tnumverts += newverts;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\t//regular brush\n\n\t\t\t\t\t\t\t//make sure we don't overflow anything.\n\t\t\t\t\t\t\tsize_t newverts = 0, newindexes = 0;\n\t\t\t\t\t\t\tfor (j = 0; j < br->numplanes; j++)\n\t\t\t\t\t\t\t\tif (br->faces[j].tex == bt && !br->selected && br->faces[j].lightmap == lmnum)\n\t\t\t\t\t\t\t\t\tnewverts += br->faces[j].numpoints, newindexes += (br->faces[j].numpoints-2)*3;\n\t\t\t\t\t\t\tif (numverts+newverts >= 0xffff || numindicies+newindexes >= 0xffff)\n\t\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\t\tfor (j = 0; j < br->numplanes; j++)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tif (br->faces[j].tex == bt && !br->selected && br->faces[j].lightmap == lmnum)\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tsize_t k, o;\n\t\t\t\t\t\t\t\t\tfloat s,t;\n\n\t\t\t\t\t\t\t\t\tfor (k = 0, o = numverts; k < br->faces[j].numpoints; k++, o++)\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tVectorCopy(br->faces[j].points[k], arrays->coord[o]);\n\t\t\t\t\t\t\t\t\t\tVectorCopy(br->planes[j], arrays->normal[o]);\n\t\t\t\t\t\t\t\t\t\tVectorCopy(br->faces[j].stdir[0], arrays->svector[o]);\n\t\t\t\t\t\t\t\t\t\tVectorCopy(br->faces[j].stdir[1], arrays->tvector[o]);\n\t\t\t\t\t\t\t\t\t\tVector4Set(arrays->rgba[o], 1.0, 1.0, 1.0, 1.0);\n\n\t\t\t\t\t\t\t\t\t\t//compute the texcoord planes\n\t\t\t\t\t\t\t\t\t\ts = (DotProduct(arrays->svector[o], arrays->coord[o]) + br->faces[j].stdir[0][3]);\n\t\t\t\t\t\t\t\t\t\tt = (DotProduct(arrays->tvector[o], arrays->coord[o]) + br->faces[j].stdir[1][3]);\n\t\t\t\t\t\t\t\t\t\tarrays->texcoord[o][0] = s * scale[0];\n\t\t\t\t\t\t\t\t\t\tarrays->texcoord[o][1] = t * scale[1];\n\n\t\t\t\t\t\t\t\t\t\t//maths, maths, and more maths.\n\t\t\t\t\t\t\t\t\t\tarrays->lmcoord[o][0] = (br->faces[j].lmbase[0]+0.5 + s/br->faces[j].lmscale-br->faces[j].lmbias[0]) * hm->brushlmscale;\n\t\t\t\t\t\t\t\t\t\tarrays->lmcoord[o][1] = (br->faces[j].lmbase[1]+0.5 + t/br->faces[j].lmscale-br->faces[j].lmbias[1]) * hm->brushlmscale;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tfor (k = 2; k < br->faces[j].numpoints; k++)\n\t\t\t\t\t\t\t\t\t{\t//triangle fans\n\t\t\t\t\t\t\t\t\t\tarrays->index[numindicies++] = numverts + 0;\n\t\t\t\t\t\t\t\t\t\tarrays->index[numindicies++] = numverts + k-1;\n\t\t\t\t\t\t\t\t\t\tarrays->index[numindicies++] = numverts + k-0;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tnumverts += br->faces[j].numpoints;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif (numverts || numindicies)\n\t\t\t\t\t{\n\t\t\t\t\t\tbb = Z_Malloc(sizeof(*bb) + (sizeof(bb->mesh.xyz_array[0])+sizeof(arrays->texcoord[0])+sizeof(arrays->lmcoord[0])+sizeof(arrays->normal[0])+sizeof(arrays->svector[0])+sizeof(arrays->tvector[0])+sizeof(arrays->rgba[0])) * numverts);\n\t\t\t\t\t\tbb->next = bt->batches;\n\t\t\t\t\t\tbt->batches = bb;\n\t\t\t\t\t\tbb->lightmap = lmnum;\n\t\t\t\t\t\tBE_VBO_Begin(&ctx, (sizeof(arrays->coord[0])+sizeof(arrays->texcoord[0])+sizeof(arrays->lmcoord[0])+sizeof(arrays->normal[0])+sizeof(arrays->svector[0])+sizeof(arrays->tvector[0])+sizeof(arrays->rgba[0])) * numverts);\n\t\t\t\t\t\tBE_VBO_Data(&ctx, arrays->coord,\tsizeof(arrays->coord\t[0])*numverts,\t\t&bb->vbo.coord);\n\t\t\t\t\t\tBE_VBO_Data(&ctx, arrays->texcoord, sizeof(arrays->texcoord\t[0])*numverts,\t\t&bb->vbo.texcoord);\n\t\t\t\t\t\tBE_VBO_Data(&ctx, arrays->lmcoord,\tsizeof(arrays->lmcoord\t[0])*numverts,\t\t&bb->vbo.lmcoord[0]);\n\t\t\t\t\t\tBE_VBO_Data(&ctx, arrays->normal,\tsizeof(arrays->normal\t[0])*numverts,\t\t&bb->vbo.normals);\n\t\t\t\t\t\tBE_VBO_Data(&ctx, arrays->svector,\tsizeof(arrays->svector\t[0])*numverts,\t\t&bb->vbo.svector);\n\t\t\t\t\t\tBE_VBO_Data(&ctx, arrays->tvector,\tsizeof(arrays->tvector\t[0])*numverts,\t\t&bb->vbo.tvector);\n\t\t\t\t\t\tBE_VBO_Data(&ctx, arrays->rgba,\t\tsizeof(arrays->rgba\t\t[0])*numverts,\t\t&bb->vbo.colours[0]);\n\t\t\t\t\t\tBE_VBO_Finish(&ctx, arrays->index,\tsizeof(arrays->index\t[0])*numindicies,\t&bb->vbo.indicies, &bb->vbo.vbomem, &bb->vbo.ebomem);\n\n\t\t\t\t\t\tbb->mesh.xyz_array = (vecV_t*)(bb+1);\n\t\t\t\t\t\tmemcpy(bb->mesh.xyz_array, arrays->coord, sizeof(*bb->mesh.xyz_array) * numverts);\n\t\t\t\t\t\tbb->mesh.st_array = (vec2_t*)(bb->mesh.xyz_array+numverts);\n\t\t\t\t\t\tmemcpy(bb->mesh.st_array, arrays->texcoord, sizeof(*bb->mesh.st_array) * numverts);\n\t\t\t\t\t\tbb->mesh.lmst_array[0] = (vec2_t*)(bb->mesh.st_array+numverts);\n\t\t\t\t\t\tmemcpy(bb->mesh.lmst_array[0], arrays->lmcoord, sizeof(*bb->mesh.lmst_array) * numverts);\n\t\t\t\t\t\tbb->mesh.normals_array = (vec3_t*)(bb->mesh.lmst_array[0]+numverts);\n\t\t\t\t\t\tmemcpy(bb->mesh.normals_array, arrays->normal, sizeof(*bb->mesh.normals_array) * numverts);\n\t\t\t\t\t\tbb->mesh.snormals_array = (vec3_t*)(bb->mesh.normals_array+numverts);\n\t\t\t\t\t\tmemcpy(bb->mesh.snormals_array, arrays->svector, sizeof(*bb->mesh.snormals_array) * numverts);\n\t\t\t\t\t\tbb->mesh.tnormals_array = (vec3_t*)(bb->mesh.snormals_array+numverts);\n\t\t\t\t\t\tmemcpy(bb->mesh.tnormals_array, arrays->tvector, sizeof(*bb->mesh.tnormals_array) * numverts);\n\t\t\t\t\t\tbb->mesh.colors4f_array[0] = (vec4_t*)(bb->mesh.tnormals_array+numverts);\n\t\t\t\t\t\tmemcpy(bb->mesh.colors4f_array[0], arrays->rgba, sizeof(*bb->mesh.colors4f_array[0]) * numverts);\n\n\t\t\t\t\t\tbb->pmesh = &bb->mesh;\n\t\t\t\t\t\tbb->mesh.numindexes = numindicies;\n\t\t\t\t\t\tbb->mesh.numvertexes = numverts;\n\n\t\t\t\t\t\tnumverts = 0;\n\t\t\t\t\t\tnumindicies = 0;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tfor(bb = bt->batches; bb; bb = bb->next)\n\t\t{\n\t\t\tb = BE_GetTempBatch();\n\t\t\tif (b)\n\t\t\t{\n\t\t\t\tj = 0;\n\t\t\t\tif (bb->lightmap >= 0)\n\t\t\t\t\tb->lightmap[j++] = r_fullbright.ival?-1:hm->brushlmremaps[bb->lightmap];\n\t\t\t\tfor (; j < MAXRLIGHTMAPS; j++)\n\t\t\t\t\tb->lightmap[j] = -1;\n\t\t\t\tb->ent = e;\n\t\t\t\tb->shader = bt->shader;\n\t\t\t\tb->flags = 0;\n\t\t\t\tb->mesh = &bb->pmesh;\n\t\t\t\tb->meshes = 1;\n\t\t\t\tb->buildmeshes = NULL;\n\t\t\t\tb->skin = NULL;\n\t\t\t\tb->texture = NULL;\n\t\t\t\tb->vbo = &bb->vbo;\n\n\t\t\t\tb->next = batches[b->shader->sort];\n\t\t\t\tbatches[b->shader->sort] = b;\n\t\t\t}\n\t\t}\n\t}\n\tif (arrays)\n\t\tBZ_Free(arrays);\n}\n#endif\n\nstatic brushtex_t *Terr_Brush_FindTexture(heightmap_t *hm, const char *texname)\n{\n\tbrushtex_t *bt;\n\tif (!hm)\n\t\treturn NULL;\n\n\tfor (bt = hm->brushtextures; bt; bt = bt->next)\n\t{\n\t\tif (!strcmp(bt->shadername, texname))\n\t\t\treturn bt;\n\t}\n\tbt = Z_Malloc(sizeof(*bt));\n\tbt->next = hm->brushtextures;\n\thm->brushtextures = bt;\n\tQ_strncpyz(bt->shadername, texname, sizeof(bt->shadername));\n\n\treturn bt;\n}\n\nstatic brushes_t *Terr_Brush_Insert(model_t *model, heightmap_t *hm, brushes_t *brush)\n{\n\tvecV_t facepoints[64];\n\tunsigned int iface, oface, j, k;\n\tunsigned int numpoints;\n\tbrushes_t *out;\n\tvec2_t mins, maxs;\n\tvec2_t lm;\n\n\tif (!hm)\n\t{\n\t\tif (model && model->loadstate == MLS_LOADING)\n\t\t\tCOM_WorkerPartialSync(model, &model->loadstate, MLS_LOADING);\n\t\tif (model && model->loadstate == MLS_LOADED)\n\t\t{\n\t\t\tchar basename[MAX_QPATH];\n\t\t\tCOM_FileBase(model->name, basename, sizeof(basename));\n\t\t\tmodel->terrain = Mod_LoadTerrainInfo(model, basename, true);\n\t\t\thm = model->terrain;\n\t\t\tif (!hm)\n\t\t\t\treturn NULL;\n\t\t\tTerr_FinishTerrain(model);\n\t\t}\n\t\telse\n\t\t\treturn NULL;\n\t}\n\n\thm->wbrushes = BZ_Realloc(hm->wbrushes, sizeof(*hm->wbrushes) * (hm->numbrushes+1));\n\tout = &hm->wbrushes[hm->numbrushes];\n\tout->selected = false;\n\tout->contents = brush->contents;\n\tout->axialplanes = 0;\n\tout->patch = NULL;\n\n\tout->planes = NULL;\n\tout->faces = NULL;\n\tout->numplanes = 0;\n\tout->ispatch = !!brush->patch;\n\tout->selected = false;\n\tClearBounds(out->mins, out->maxs);\n\tif (brush->numplanes)\n\t{\n\t\tout->planes = BZ_Malloc((sizeof(*out->planes)+sizeof(*out->faces)) * brush->numplanes);\n\t\tout->faces = (void*)(out->planes+brush->numplanes);\n\t\tfor (iface = 0, oface = 0; iface < brush->numplanes; iface++)\n\t\t{\n\t\t\tfor (j = 0; j < oface; j++)\n\t\t\t{\n\t\t\t\tif (out->planes[j][0] == brush->planes[iface][0] &&\n\t\t\t\t\tout->planes[j][1] == brush->planes[iface][1] &&\n\t\t\t\t\tout->planes[j][2] == brush->planes[iface][2] &&\n\t\t\t\t\tout->planes[j][3] == brush->planes[iface][3])\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (j < oface)\n\t\t\t{\n\t\t\t\tCon_DPrintf(\"duplicate plane\\n\");\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t//generate points now (so we know the correct mins+maxs for the brush, and whether the plane is relevent)\n\t\t\tnumpoints = Fragment_ClipPlaneToBrush(facepoints, countof(facepoints), brush->planes, sizeof(*brush->planes), brush->numplanes, brush->planes[iface]);\n\t\t\tif (!numpoints)\n\t\t\t{\n\t\t\t\tCon_DPrintf(\"redundant face\\n\");\n\t\t\t\tcontinue;\t//this surface was chopped away entirely, and isn't relevant.\n\t\t\t}\n\n\t\t\t//copy the basic face info out so we can save/restore/query/edit it later.\n\t\t\tVector4Copy(brush->planes[iface], out->planes[oface]);\n\t\t\tout->faces[oface].tex = brush->faces[iface].tex;\n\t\t\tVector4Copy(brush->faces[iface].stdir[0], out->faces[oface].stdir[0]);\n\t\t\tVector4Copy(brush->faces[iface].stdir[1], out->faces[oface].stdir[1]);\n\n\t\t\tif (out->planes[oface][0] == 1)\n\t\t\t\tout->axialplanes |= 1u<<0;\n\t\t\telse if (out->planes[oface][1] == 1)\n\t\t\t\tout->axialplanes |= 1u<<1;\n\t\t\telse if (out->planes[oface][2] == 1)\n\t\t\t\tout->axialplanes |= 1u<<2;\n\t\t\telse if (out->planes[oface][0] == -1)\n\t\t\t\tout->axialplanes |= 1u<<3;\n\t\t\telse if (out->planes[oface][1] == -1)\n\t\t\t\tout->axialplanes |= 1u<<4;\n\t\t\telse if (out->planes[oface][2] == -1)\n\t\t\t\tout->axialplanes |= 1u<<5;\n\n\t\t\t//make sure this stuff is rebuilt properly.\n\t\t\tout->faces[oface].tex->rebuild = true;\n\n\t\t\t//keep this stuff cached+reused, so everything is consistant. also work out min/max lightmap texture coords\n\t\t\tout->faces[oface].points = BZ_Malloc(numpoints * sizeof(*out->faces[oface].points));\n\t\t\tVector2Set(mins, 0, 0);\n\t\t\tVector2Set(maxs, 0, 0);\n\t\t\tfor (j = 0; j < numpoints; j++)\n\t\t\t{\n\t\t\t\tAddPointToBounds(facepoints[j], out->mins, out->maxs);\n\t\t\t\tVectorCopy(facepoints[j], out->faces[oface].points[j]);\n\t\t\t\tfor (k = 0; k < 2; k++)\n\t\t\t\t{\n\t\t\t\t\tlm[k] = DotProduct(out->faces[oface].points[j], out->faces[oface].stdir[k]) + out->faces[oface].stdir[k][3];\n\t\t\t\t\tif (j == 0)\n\t\t\t\t\t\tmins[k] = maxs[k] = lm[k];\n\t\t\t\t\telse if (lm[k] > maxs[k])\n\t\t\t\t\t\tmaxs[k] = lm[k];\n\t\t\t\t\telse if (lm[k] < mins[k])\n\t\t\t\t\t\tmins[k] = lm[k];\n\t\t\t\t}\n\t\t\t}\n\t\t\tout->faces[oface].numpoints = numpoints;\n\n\t\t\t//determine lightmap scale, and extents. rescale the lightmap if it ought to have been subdivided.\n\t\t\tout->faces[oface].relight = true;\n\t\t\tout->faces[oface].lmscale = 16;\n\t\t\tfor (k = 0; k < 2; )\n\t\t\t{\n\t\t\t\tout->faces[oface].lmbias[k] = floor(mins[k]/out->faces[oface].lmscale);\n\t\t\t\tout->faces[oface].lmextents[k] = ceil((maxs[k])/out->faces[oface].lmscale)-out->faces[oface].lmbias[k]+1;\n\t\t\t\tif (out->faces[oface].lmextents[k] > 128)\n\t\t\t\t{\t//surface is too large for lightmap data. just drop its resolution, because splitting the face in plane-defined geometry is a bad idea.\n\t\t\t\t\tif (out->faces[oface].lmscale > 256)\n\t\t\t\t\t{\n\t\t\t\t\t\tout->faces[oface].relight = false;\n\t\t\t\t\t\tk++;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tout->faces[oface].lmscale *= 2;\n\t\t\t\t\t\tk = 0;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tk++;\n\t\t\t}\n\t\t\tout->faces[oface].lightmap = -1;\n\t\t\tout->faces[oface].lmbase[0] = 0;\n\t\t\tout->faces[oface].lmbase[1] = 0;\n\t\t\tif (out->faces[oface].relight)\n\t\t\t{\n\t\t\t\tout->faces[oface].lightdata = BZ_Malloc(out->faces[oface].lmextents[0] * out->faces[oface].lmextents[1] * 3);\n\t\t\t\tmemset(out->faces[oface].lightdata, 0x3f, out->faces[oface].lmextents[0]*out->faces[oface].lmextents[1]*3);\n\t\t\t}\n\t\t\telse\n\t\t\t\tout->faces[oface].lightdata = NULL;\n\n\t//\t\tCon_Printf(\"lm extents: %u %u (%i points)\\n\", out->faces[oface].lmextents[0], out->faces[oface].lmextents[1], numpoints);\n\t\t\toface++;\n\t\t}\n\t\tout->numplanes = oface;\n\t}\n\n\tif (brush->patch)\n\t{\n\t\tout->patch = BZ_Malloc(sizeof(*out->patch)-sizeof(out->patch->cp) + sizeof(*out->patch->cp)*brush->patch->numcp[0]*brush->patch->numcp[1]);\n\t\tmemcpy(out->patch, brush->patch, sizeof(*out->patch)-sizeof(out->patch->cp) + sizeof(*out->patch->cp)*brush->patch->numcp[0]*brush->patch->numcp[1]);\n\n\t\tnumpoints = out->patch->numcp[0]*out->patch->numcp[1];\n\t\t//FIXME: lightmap...\n\t\tfor (j = 0; j < numpoints; j++)\n\t\t\tAddPointToBounds(out->patch->cp[j].v, out->mins, out->maxs);\n\n\n\t\tout->patch->tex->rebuild = true;\n\t}\n\n\tif ((out->numplanes < 4 && out->numplanes) || (out->numplanes && out->patch) || (!out->numplanes && !out->patch))\n\t{\t//a brush with less than 4 planes cannot be a valid convex area (but can happen when certain redundant planes are chopped out). don't accept creation\n\t\t//(we often get 2-plane brushes if the sides are sucked in)\n\t\tfor (j = 0; j < out->numplanes; j++)\n\t\t{\n\t\t\tBZ_Free(out->faces[j].lightdata);\n\t\t\tBZ_Free(out->faces[j].points);\n\t\t}\n\t\tBZ_Free(out->planes);\n\t\tBZ_Free(out->patch);\n\t\treturn NULL;\n\t}\n\n\tif (brush->id)\n\t\tout->id = brush->id;\n\telse\n\t{\n\t\tunsigned int i;\n\t\t//loop to avoid creating two brushes with the same id\n\t\tdo\n\t\t{\n\t\t\tout->id = (++hm->brushidseq)&0x00ffffff;\n#ifdef HAVE_CLIENT\n\t\t\tif (cls.state)\t//avoid networking conflicts by having each node generating its own private ids\n\t\t\t\tout->id |= (cl.playerview[0].playernum+1)<<24;\n#endif\n\n\t\t\tfor (i = 0; i < hm->numbrushes; i++)\n\t\t\t{\n\t\t\t\tif (hm->wbrushes[i].id == out->id)\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t} while (i != hm->numbrushes);\n\t}\n//\tCon_Printf(\"brush %u (%i faces)\\n\", out->id, oface);\n\n\thm->numbrushes+=1;\n\thm->brushesedited = true;\n\n\thm->recalculatebrushlighting = true;\t//lightmaps need to be reallocated\n\n\t//make sure the brush's bounds are added to the containing model.\n\tAddPointToBounds(out->mins, model->mins, model->maxs);\n\tAddPointToBounds(out->maxs, model->mins, model->maxs);\n\n\tif (out->patch && (out->patch->subdiv[0] || out->patch->subdiv[1]))\n\t\tout->patch->tessvert = PatchInfo_Evaluate(out->patch->cp, out->patch->numcp, out->patch->subdiv, out->patch->tesssize);\n\n\treturn out;\n}\n\n\nstatic brushes_t *Terr_Patch_Insert(model_t *model, heightmap_t *hm, brushtex_t *patch_tex, unsigned patch_w, unsigned patch_h, unsigned subdiv_w, unsigned subdiv_h, qcpatchvert_t *patch_v, int stride)\n{\n\tint x, y;\n\tbrushes_t brush;\n\t//finish the brush\n\tbrush.contents = FTECONTENTS_SOLID;\n\tbrush.numplanes = 0;\n\tbrush.planes = NULL;\n\tbrush.faces = NULL;\n\tbrush.id = 0;\n\tbrush.patch = alloca(sizeof(*brush.patch)-sizeof(brush.patch->cp) + sizeof(*brush.patch->cp)*patch_w*patch_h);\n\n\tbrush.patch->tex = patch_tex;\n\tbrush.patch->numcp[0] = patch_w;\n\tbrush.patch->numcp[1] = patch_h;\n\tbrush.patch->subdiv[0] = subdiv_w;\n\tbrush.patch->subdiv[1] = subdiv_h;\n\tbrush.patch->tessvert = NULL;\n\n\tfor (y = 0; y < patch_h; y++)\n\t{\n\t\tfor (x = 0; x < patch_w; x++)\n\t\t{\n\t\t\tVectorCopy(patch_v[x].v, brush.patch->cp[x + y*patch_w].v);\n\t\t\tVector2Copy(patch_v[x].tc, brush.patch->cp[x + y*patch_w].tc);\n\t\t\tVector4Copy(patch_v[x].rgba, brush.patch->cp[x + y*patch_w].rgba);\n\t\t\t//brush.patch->verts[x + y*patch_w].norm\n\t\t\t//brush.patch->verts[x + y*patch_w].sdir\n\t\t\t//brush.patch->verts[x + y*patch_w].tdir\n\t\t}\n\t\tpatch_v += stride;\n\t}\n\n\treturn Terr_Brush_Insert(model, hm, &brush);\n}\n\nstatic void Terr_Brush_DeleteIdx(heightmap_t *hm, size_t idx)\n{\n\tint i;\n\tbrushes_t *br = &hm->wbrushes[idx];\n\tif (!hm)\n\t\treturn;\n\n\tfor (i = 0; i < br->numplanes; i++)\n\t{\n\t\tBZ_Free(br->faces[i].lightdata);\n\t\tBZ_Free(br->faces[i].points);\n\t\tbr->faces[i].tex->rebuild = true;\n\t}\n\n\tBZ_Free(br->planes);\n\tif (br->patch)\n\t{\n\t\tBZ_Free(br->patch->tessvert);\n\t\tBZ_Free(br->patch);\n\t}\n\thm->numbrushes--;\n\thm->brushesedited = true;\n\t//plug the hole with some other brush.\n\tif (idx < hm->numbrushes)\n\t\thm->wbrushes[idx] = hm->wbrushes[hm->numbrushes];\n}\nstatic qboolean Terr_Brush_DeleteId(heightmap_t *hm, unsigned int brushid)\n{\n\tsize_t i;\n\tbrushes_t *br;\n\tif (!hm)\n\t\treturn false;\n\n\tfor (i = 0; i < hm->numbrushes; i++)\n\t{\n\t\tbr = &hm->wbrushes[i];\n\t\tif (br->id == brushid)\n\t\t{\n\t\t\tTerr_Brush_DeleteIdx(hm, i);\n\t\t\treturn true;\n\t\t}\n\t}\n\treturn false;\n}\n\n\nstatic void Patch_Serialise(sizebuf_t *sb, brushes_t *br)\n{\n\tqbyte flags = 0;\n\tunsigned int i, m = br->patch->numcp[0]*br->patch->numcp[1];\n\n\tfor (i = 0; i < m; i++)\n\t{\n\t\tif (br->patch->cp[i].rgba[0] != 1)\n\t\t\tflags |= 1;\n\t\tif (br->patch->cp[i].rgba[1] != 1)\n\t\t\tflags |= 2;\n\t\tif (br->patch->cp[i].rgba[2] != 1)\n\t\t\tflags |= 4;\n\t\tif (br->patch->cp[i].rgba[3] != 1)\n\t\t\tflags |= 8;\n\t}\n\n\tMSG_WriteLong(sb, br->id);\n\tMSG_WriteLong(sb, br->contents);\n\tMSG_WriteShort(sb, br->patch->numcp[0]);\n\tMSG_WriteShort(sb, br->patch->numcp[1]);\n\n\tMSG_WriteByte(sb, flags);\n\n\tMSG_WriteString(sb, br->patch->tex->shadername);\n\tMSG_WriteShort(sb, br->patch->subdiv[0]);\n\tMSG_WriteShort(sb, br->patch->subdiv[1]);\n\n\tfor (i = 0; i < m; i++)\n\t{\n\t\tMSG_WriteFloat(sb, br->patch->cp[i].v[0]);\n\t\tMSG_WriteFloat(sb, br->patch->cp[i].v[1]);\n\t\tMSG_WriteFloat(sb, br->patch->cp[i].v[2]);\n\t\tMSG_WriteFloat(sb, br->patch->cp[i].tc[0]);\n\t\tMSG_WriteFloat(sb, br->patch->cp[i].tc[1]);\n\n\t\tif (flags&1)\n\t\t\tMSG_WriteFloat(sb, br->patch->cp[i].rgba[0]);\n\t\tif (flags&2)\n\t\t\tMSG_WriteFloat(sb, br->patch->cp[i].rgba[1]);\n\t\tif (flags&4)\n\t\t\tMSG_WriteFloat(sb, br->patch->cp[i].rgba[2]);\n\t\tif (flags&8)\n\t\t\tMSG_WriteFloat(sb, br->patch->cp[i].rgba[3]);\n\t}\n}\nstatic size_t Patch_DeserialiseHeader(brushes_t *br)\n{\n\tunsigned int numcp[2];\n\tbr->id = MSG_ReadLong();\n\tbr->contents = MSG_ReadLong();\n\n\tbr->numplanes   = numcp[0] = (unsigned short)MSG_ReadShort();\n\tbr->axialplanes = numcp[1] = (unsigned short)MSG_ReadShort();\n\n\tif (numcp[0]*numcp[1] > 8192)\n\t\treturn 0; //too many. reject it as bad.\n\treturn sizeof(*br->patch) + sizeof(*br->patch->cp)*(numcp[0]*numcp[1]-countof(br->patch->cp));\n}\nstatic qboolean Patch_Deserialise(heightmap_t *hm, brushes_t *br, void *mem)\n{\n\tstruct qcpatchvert_s vert;\n\tqboolean flags;\n\tunsigned int i, m;\n\tflags = MSG_ReadByte();\n\n\tbr->patch = mem;\n\tbr->patch->numcp[0] = br->numplanes;\n\tbr->patch->numcp[1] = br->axialplanes;\n\tbr->numplanes = br->axialplanes = 0;\n\n\tm = br->patch->numcp[0]*br->patch->numcp[1];\n\n\t//FIXME: as a server, we probably want to reject the brush if we exceed some texnum/memory limitation, so clients can't just spam new textures endlessly.\n\tbr->patch->tex = Terr_Brush_FindTexture(hm, MSG_ReadString());\n\n\tbr->patch->subdiv[0] = MSG_ReadShort();\n\tbr->patch->subdiv[1] = MSG_ReadShort();\n\n\tfor (i = 0; i < m; i++)\n\t{\n\t\tvert.v[0] = MSG_ReadFloat();\n\t\tvert.v[1] = MSG_ReadFloat();\n\t\tvert.v[2] = MSG_ReadFloat();\n\t\tvert.tc[0] = MSG_ReadFloat();\n\t\tvert.tc[1] = MSG_ReadFloat();\n\n\t\tvert.rgba[0] = (flags&1)?MSG_ReadFloat():1;\n\t\tvert.rgba[1] = (flags&2)?MSG_ReadFloat():1;\n\t\tvert.rgba[2] = (flags&4)?MSG_ReadFloat():1;\n\t\tvert.rgba[3] = (flags&8)?MSG_ReadFloat():1;\n\n\t\tbr->patch->cp[i] = vert;\n\t}\n\treturn true;\n}\n\nstatic void Brush_Serialise(sizebuf_t *sb, brushes_t *br)\n{\n\tunsigned int i;\n\tMSG_WriteLong(sb, br->id);\n\tMSG_WriteLong(sb, br->contents);\n\tMSG_WriteLong(sb, br->numplanes);\n\n\tfor (i = 0; i < br->numplanes; i++)\n\t{\n\t\tMSG_WriteString(sb, br->faces[i].tex->shadername);\n\n\t\tMSG_WriteFloat(sb, br->planes[i][0]);\n\t\tMSG_WriteFloat(sb, br->planes[i][1]);\n\t\tMSG_WriteFloat(sb, br->planes[i][2]);\n\t\tMSG_WriteFloat(sb, br->planes[i][3]);\n\n\t\tMSG_WriteFloat(sb, br->faces[i].stdir[0][0]);\n\t\tMSG_WriteFloat(sb, br->faces[i].stdir[0][1]);\n\t\tMSG_WriteFloat(sb, br->faces[i].stdir[0][2]);\n\t\tMSG_WriteFloat(sb, br->faces[i].stdir[0][3]);\n\n\t\tMSG_WriteFloat(sb, br->faces[i].stdir[1][0]);\n\t\tMSG_WriteFloat(sb, br->faces[i].stdir[1][1]);\n\t\tMSG_WriteFloat(sb, br->faces[i].stdir[1][2]);\n\t\tMSG_WriteFloat(sb, br->faces[i].stdir[1][3]);\n\t}\n}\nstatic size_t Brush_DeserialiseHeader(brushes_t *br, qboolean ispatch)\n{\n\tbr->ispatch = ispatch;\n\tif (br->ispatch)\n\t\treturn Patch_DeserialiseHeader(br);\n\n\tbr->id = MSG_ReadLong();\n\tbr->contents = MSG_ReadLong();\n\tbr->numplanes = MSG_ReadLong();\n\n\tif (br->numplanes > 8192)\n\t\treturn 0;\t//abusive\n\n\treturn sizeof(*br->faces)  * br->numplanes\n\t\t + sizeof(*br->planes) * br->numplanes;\n}\nstatic qboolean Brush_Deserialise(heightmap_t *hm, brushes_t *br, void *mem)\n{\n\tunsigned int i;\n\tif (br->ispatch)\n\t\treturn Patch_Deserialise(hm, br, mem);\n\n\tbr->faces = mem;\n\tbr->planes = (vec4_t*)(br->faces + br->numplanes);\n\n\tfor (i = 0; i < br->numplanes; i++)\n\t{\n\t\t//FIXME: as a server, we probably want to reject the brush if we exceed some texnum/memory limitation, so clients can't just spam new textures endlessly.\n\t\tbr->faces[i].tex = Terr_Brush_FindTexture(hm, MSG_ReadString());\n\n\t\tbr->planes[i][0] = MSG_ReadFloat();\n\t\tbr->planes[i][1] = MSG_ReadFloat();\n\t\tbr->planes[i][2] = MSG_ReadFloat();\n\t\tbr->planes[i][3] = MSG_ReadFloat();\n\n\t\t//FIXME: can we optimise this part? a flag to say whether its needed?\n\t\tbr->faces[i].stdir[0][0] = MSG_ReadFloat();\n\t\tbr->faces[i].stdir[0][1] = MSG_ReadFloat();\n\t\tbr->faces[i].stdir[0][2] = MSG_ReadFloat();\n\t\tbr->faces[i].stdir[0][3] = MSG_ReadFloat();\n\n\t\tbr->faces[i].stdir[1][0] = MSG_ReadFloat();\n\t\tbr->faces[i].stdir[1][1] = MSG_ReadFloat();\n\t\tbr->faces[i].stdir[1][2] = MSG_ReadFloat();\n\t\tbr->faces[i].stdir[1][3] = MSG_ReadFloat();\n\n\t\tbr->faces[i].surfaceflags = 0;\t//used by q2\n\t\tbr->faces[i].surfacevalue = 0;\t//used by q2 (generally light levels)\n\t}\n\treturn true;\n}\n\n\n#ifdef HAVE_CLIENT\nheightmap_t\t*CL_BrushEdit_ForceContext(model_t *mod)\n{\n\theightmap_t *hm = mod?mod->terrain:NULL;\n\tif (!hm)\n\t{\n\t\tif (mod && mod->loadstate == MLS_LOADING)\n\t\t\tCOM_WorkerPartialSync(mod, &mod->loadstate, MLS_LOADING);\n\t\tif (mod && mod->loadstate == MLS_LOADED)\n\t\t{\n\t\t\tchar basename[MAX_QPATH];\n\t\t\tCOM_FileBase(mod->name, basename, sizeof(basename));\n\t\t\tmod->terrain = Mod_LoadTerrainInfo(mod, basename, true);\n\t\t\thm = mod->terrain;\n\t\t\tif (!hm)\n\t\t\t\treturn NULL;\n\t\t\tTerr_FinishTerrain(mod);\n\t\t}\n\t\telse\n\t\t\treturn NULL;\n\t}\n\treturn hm;\n}\n\nvoid CL_Parse_BrushEdit(void)\n{\n\tunsigned int\tmodelindex\t\t= MSG_ReadShort();\n\tint\t\t\t\tcmd\t\t\t\t= MSG_ReadByte();\n\n\tmodel_t\t\t\t*mod\t\t\t= (modelindex<countof(cl.model_precache))?cl.model_precache[modelindex]:NULL;\n\theightmap_t\t\t*hm\t\t\t\t= mod?mod->terrain:NULL;\n\n#ifdef HAVE_SERVER\n\tconst qboolean\t\tignore = (sv_state>=ss_loading);\t//if we're the server then we already have this info. don't break anything (this info is present for demos).\n#else\n\tconst qboolean\t\tignore = false;\n#endif\n\n\tif (cmd == hmcmd_brush_delete)\n\t{\n\t\tint id = MSG_ReadLong();\n\t\tif (ignore)\n\t\t\treturn;\t//ignore if we're the server, we should already have it anyway.\n\t\tTerr_Brush_DeleteId(hm, id);\n\t}\n\telse if (cmd == hmcmd_brush_insert || cmd == hmcmd_patch_insert)\t//1=create/replace\n\t{\n\t\tbrushes_t brush;\n\t\tsize_t tempmemsize;\n\n\t\thm = CL_BrushEdit_ForceContext(mod);\t//do this early, to ensure that the textures are correct\n\n\t\tmemset(&brush, 0, sizeof(brush));\n\t\ttempmemsize = Brush_DeserialiseHeader(&brush, (cmd == hmcmd_patch_insert));\n\t\tif (!tempmemsize)\n\t\t\tHost_EndGame(\"CL_Parse_BrushEdit: unparsable %s\\n\", brush.ispatch?\"patch\":\"brush\");\n\t\tif (!Brush_Deserialise(hm, &brush, alloca(tempmemsize)))\n\t\t\tHost_EndGame(\"CL_Parse_BrushEdit: unparsable %s\\n\", brush.ispatch?\"patch\":\"brush\");\n\n\t\tif (!ignore)\t//ignore if we're the server, we should already have it anyway (but might need it for demos, hence why its still sent).\n\t\t{\n\t\t\tif (brush.id)\n\t\t\t{\n\t\t\t\tint i;\n\t\t\t\tif (cls.demoplayback)\n\t\t\t\t\tTerr_Brush_DeleteId(hm, brush.id);\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tfor (i = 0; i < hm->numbrushes; i++)\n\t\t\t\t\t{\n\t\t\t\t\t\tbrushes_t *br = &hm->wbrushes[i];\n\t\t\t\t\t\tif (br->id == brush.id)\n\t\t\t\t\t\t\treturn;\t//we already have it. assume we just edited it.\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tTerr_Brush_Insert(mod, hm, &brush);\n\t\t}\n\t}\n\telse if (cmd == hmcmd_prespawning)\n\t{\t//delete all\n\t\tif (ignore)\n\t\t\treturn;\t//ignore if we're the server, we should already have it anyway.\n\n\t\thm = CL_BrushEdit_ForceContext(mod);\t//make sure we don't end up with any loaded brushes.\n\t\tif (hm)\n\t\t{\n\t\t\twhile(hm->numbrushes)\n\t\t\t\tTerr_Brush_DeleteIdx(hm, hm->numbrushes-1);\n\t\t}\n\t}\n\telse if (cmd == hmcmd_prespawned)\n\t{\n\t}\n\telse if (cmd == hmcmd_ent_edit || cmd == hmcmd_ent_remove)\n\t{\t//ent edit\n\t\tint id = MSG_ReadLong();\n\t\tconst char *data;\n\t\tint idx = mod->numentityinfo, i;\n\t\tif (cmd == hmcmd_ent_edit)\n\t\t\tdata = MSG_ReadString();\n\t\telse\n\t\t\tdata = NULL;\n\n\t\t//convert id to idx\n\t\tfor (i = 0; i < mod->numentityinfo; i++)\n\t\t{\n\t\t\tif (mod->entityinfo[i].id == id)\n\t\t\t{\n\t\t\t\tidx = i;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (!mod->entityinfo[i].keyvals)\n\t\t\t\tidx = i;\n\t\t}\n\n\t\t//FIXME: cap the maximum data sizes (both count and storage, to prevent DOS attacks).\n\n\t\tif (idx == mod->numentityinfo && data)\n\t\t\tZ_ReallocElements((void**)&mod->entityinfo, &mod->numentityinfo, mod->numentityinfo+64, sizeof(*mod->entityinfo));\n\t\tif (idx < mod->numentityinfo)\n\t\t{\n\t\t\tif (!ignore)\n\t\t\t{\n\t\t\t\tmod->entityinfo[idx].id = id;\n\t\t\t\tZ_Free(mod->entityinfo[idx].keyvals);\n\t\t\t\tif (data)\n\t\t\t\t\tmod->entityinfo[idx].keyvals = Z_StrDup(data);\n\t\t\t\telse\n\t\t\t\t\tmod->entityinfo[idx].keyvals = NULL;\n\n#ifdef CSQC_DAT\n\t\t\t\tCSQC_MapEntityEdited(modelindex, idx, data);\n#endif\n\t\t\t}\n\t\t}\n\t}\n\telse\n\t\tHost_EndGame(\"CL_Parse_BrushEdit: unknown command %i\\n\", cmd);\n}\n#endif\n#ifdef HAVE_SERVER\nqboolean SV_Prespawn_Brushes(sizebuf_t *msg, unsigned int *modelindex, unsigned int *lastid)\n{\n\t//lastid starts at 0\n\tunsigned int bestid, i;\n\tbrushes_t *best;\n\tmodel_t *mod;\n\theightmap_t *hm;\n\twhile(1)\n\t{\n\t\tif (*modelindex < MAX_PRECACHE_MODELS)\n\t\t\tmod = sv.models[*modelindex];\n\t\telse\n\t\t\tmod = NULL;\n\t\tif (!mod)\n\t\t{\n\t\t\tif (!(*modelindex)++)\n\t\t\t\tcontinue;\n\t\t\treturn false;\n\t\t}\n\t\thm = mod->terrain;\n\t\tif (!hm || !hm->brushesedited)\n\t\t{\n\t\t\t*modelindex+=1;\n\t\t\t*lastid = 0;\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (!*lastid)\n\t\t{\t//make sure the client starts with a clean slate.\n\t\t\tMSG_WriteByte(msg, svcfte_brushedit);\n\t\t\tMSG_WriteShort(msg, *modelindex);\n\t\t\tMSG_WriteByte(msg, hmcmd_prespawning);\n\t\t}\n\n\t\t//weird loop to try to ensure we never miss any brushes.\n\t\t//get the lowest index that is 1 higher than our previous.\n\t\tfor (best = NULL, bestid = ~0u, i = 0; i < hm->numbrushes; i++)\n\t\t{\n\t\t\tunsigned int bid = hm->wbrushes[i].id;\n\t\t\tif (bid > *lastid && bid <= bestid)\n\t\t\t{\n\t\t\t\tbest = &hm->wbrushes[i];\n\t\t\t\tbestid = best->id;\n\t\t\t\tif (bestid == *lastid+1)\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tif (best)\n\t\t{\n\t\t\tMSG_WriteByte(msg, svcfte_brushedit);\n\t\t\tMSG_WriteShort(msg, *modelindex);\n\t\t\tif (best->patch)\n\t\t\t{\n\t\t\t\tMSG_WriteByte(msg, hmcmd_patch_insert);\n\t\t\t\tPatch_Serialise(msg, best);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tMSG_WriteByte(msg, hmcmd_brush_insert);\n\t\t\t\tBrush_Serialise(msg, best);\n\t\t\t}\n\t\t\t*lastid = bestid;\n\t\t\treturn true;\n\t\t}\n\t\t\n\t\t*modelindex+=1;\n\t\t*lastid = 0;\n\t}\n}\nqboolean SV_Parse_BrushEdit(void)\n{\n\tqboolean authorise = (host_client->penalties & BAN_MAPPER) || (host_client->netchan.remote_address.type == NA_LOOPBACK);\n\tunsigned int\tmodelindex\t\t= MSG_ReadShort();\n\tint\t\t\t\tcmd\t\t\t\t= MSG_ReadByte();\n\tmodel_t\t\t\t*mod\t\t\t= (modelindex<countof(sv.models))?sv.models[modelindex]:NULL;\n\theightmap_t\t\t*hm\t\t\t\t= mod?mod->terrain:NULL;\n\tif (cmd == hmcmd_brush_delete)\n\t{\t//delete\n\t\tunsigned int brushid = MSG_ReadLong();\n\t\tif (!authorise)\n\t\t{\n\t\t\tSV_PrintToClient(host_client, PRINT_MEDIUM, \"Brush editing ignored: you are not a mapper\\n\");\n\t\t\treturn true;\n\t\t}\n\t\tTerr_Brush_DeleteId(hm, brushid);\n\n\t\tMSG_WriteByte(&sv.multicast, svcfte_brushedit);\n\t\tMSG_WriteShort(&sv.multicast, modelindex);\n\t\tMSG_WriteByte(&sv.multicast, hmcmd_brush_delete);\n\t\tMSG_WriteLong(&sv.multicast, brushid);\n\t\tSV_MulticastProtExt(vec3_origin, MULTICAST_ALL_R, ~0, 0, 0);\n\t\treturn true;\n\t}\n\telse if (cmd == hmcmd_brush_insert || cmd == hmcmd_patch_insert)\n\t{\n\t\tbrushes_t brush;\n\t\tsize_t tempmemsize;\n\t\tmemset(&brush, 0, sizeof(brush));\n\t\tbrush.ispatch = (cmd == hmcmd_patch_insert);\n\n\t\ttempmemsize = Brush_DeserialiseHeader(&brush, cmd == hmcmd_patch_insert);\n\t\tif (!tempmemsize)\n\t\t{\n\t\t\tCon_Printf(\"SV_Parse_BrushEdit: %s sent an abusive %s\\n\", host_client->name, brush.ispatch?\"patch\":\"brush\");\n\t\t\treturn false;\n\t\t}\n\t\tif (!Brush_Deserialise(hm, &brush, alloca(tempmemsize)))\n\t\t{\n\t\t\tCon_Printf(\"SV_Parse_BrushEdit: %s sent an unparsable brush\\n\", host_client->name);\n\t\t\treturn false;\n\t\t}\n\n\t\tif (!authorise)\n\t\t{\n\t\t\tSV_PrintToClient(host_client, PRINT_MEDIUM, \"Brush editing ignored: you are not a mapper\\n\");\n\t\t\treturn true;\n\t\t}\n\n\t\tTerr_Brush_DeleteId(hm, brush.id);\n\t\tif (!Terr_Brush_Insert(mod, hm, &brush))\n\t\t\treturn true;\t//looks mostly valid, but something was degenerate. fpu precision...\n\n\t\t//FIXME: expand the world entity's sizes if needed?\n\n\t\tMSG_WriteByte(&sv.multicast, svcfte_brushedit);\n\t\tMSG_WriteShort(&sv.multicast, modelindex);\n\t\tMSG_WriteByte(&sv.multicast, cmd);\n\t\tif (cmd == hmcmd_patch_insert)\n\t\t\tPatch_Serialise(&sv.multicast, &brush);\n\t\telse\n\t\t\tBrush_Serialise(&sv.multicast, &brush);\n\t\tSV_MulticastProtExt(vec3_origin, MULTICAST_ALL_R, ~0, 0, 0);\n\t\treturn true;\n\t}\n\telse if (cmd == hmcmd_ent_edit || cmd == hmcmd_ent_remove)\n\t{\n\t\tunsigned int entid = MSG_ReadLong();\n\t\tchar *keyvals = (cmd == hmcmd_ent_edit)?MSG_ReadString():NULL;\n\t\tif (mod->submodelof != mod)\n\t\t\treturn true;\n\t\tif (!authorise)\n\t\t{\n\t\t\tSV_PrintToClient(host_client, PRINT_MEDIUM, \"Entity editing ignored: you are not a mapper\\n\");\n\t\t\t//FIXME: undo the client's edit? or is that rude?\n\t\t\treturn true;\n\t\t}\n\n\t\t//FIXME: need to update the server's entity list\n\t\t//SSQC_MapEntityEdited(idx, newvals);\n\n\t\tMSG_WriteByte(&sv.multicast, svcfte_brushedit);\n\t\tMSG_WriteShort(&sv.multicast, modelindex);\n\t\tMSG_WriteByte(&sv.multicast, keyvals?hmcmd_ent_edit:hmcmd_ent_remove);\n\t\tMSG_WriteLong(&sv.multicast, entid);\n\t\tif (keyvals)\n\t\t\tMSG_WriteString(&sv.multicast, keyvals);\n\t\tSV_MulticastProtExt(vec3_origin, MULTICAST_ALL_R, ~0, 0, 0);\n\t}\n\telse\n\t{\n\t\tCon_Printf(\"SV_Parse_BrushEdit: %s sent an unknown command: %i\\n\", host_client->name, cmd);\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n#endif\n\nstatic void *validateqcpointer(pubprogfuncs_t *prinst, size_t qcptr, size_t elementsize, size_t elementcount, qboolean allownull)\n{\n\t//make sure that the sizes can't overflow\n\tif (elementcount > 0x10000)\n\t{\n\t\tPR_BIError(prinst, \"brush: elementcount %u is too large\\n\", (unsigned int)elementcount);\n\t\treturn NULL;\n\t}\n\tif (qcptr+(elementsize*elementcount) > (size_t)prinst->stringtablesize)\n\t{\n\t\tPR_BIError(prinst, \"brush: invalid qc pointer\\n\");\n\t\treturn NULL;\n\t}\n\tif (!qcptr)\n\t{\n\t\tif (!allownull)\n\t\t\tPR_BIError(prinst, \"brush: null qc pointer\\n\");\n\t\treturn NULL;\n\t}\n\treturn prinst->stringtable + qcptr;\n}\n\n//\t{\"patch_getcp\",\t\tPF_patch_getcp,\t\t0,\t\t0,\t\t0,\t\t0,\t\tD(qcpatchvert \"int(float modelidx, int patchid, patchvert_t *out_controlverts, int maxcp, __out patchinfo_t out_info)\", \"Queries a patch's information. You must pre-allocate the face array for the builtin to write to. Return value is the total number of control verts that were retrieved, 0 on error.\")},\nvoid QCBUILTIN PF_patch_getcp(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tworld_t\t\t\t*vmw\t\t\t= prinst->parms->user;\n\tmodel_t\t\t\t*mod\t\t\t= vmw->Get_CModel(vmw, G_FLOAT(OFS_PARM0));\n\theightmap_t\t\t*hm\t\t\t\t= mod?mod->terrain:NULL;\n\tunsigned int\tpatchid\t\t\t= G_INT(OFS_PARM1);\n\tunsigned int\tmaxverts\t\t= G_INT(OFS_PARM3);\n\tqcpatchvert_t\t*out_verts\t\t= validateqcpointer(prinst, G_INT(OFS_PARM2), sizeof(*out_verts), maxverts, true);\n\tqcpatchinfo_t\t*out_info\t\t= validateqcpointer(prinst, G_INT(OFS_PARM4), sizeof(*out_info), 1, true);\n\tunsigned int\ti, j;\n\tbrushes_t\t\t*br;\n\n\t//assume the worst.\n\tG_INT(OFS_RETURN) = 0;\n\tif (out_info)\n\t\tmemset(out_info, 0, sizeof(*out_info));\n\n\tif (!hm)\n\t\treturn;\n\n\tfor (i = 0; i < hm->numbrushes; i++)\n\t{\n\t\tbr = &hm->wbrushes[i];\n\t\tif (br->id == patchid)\n\t\t{\n\t\t\tif (!br->patch)\n\t\t\t\treturn;\n\t\t\tif (out_info)\n\t\t\t{\n\t\t\t\tout_info->contents = br->contents;\n\t\t\t\tout_info->cp_width = br->patch->numcp[0];\n\t\t\t\tout_info->cp_height = br->patch->numcp[1];\n\t\t\t\tout_info->subdiv_x = br->patch->subdiv[0];\n\t\t\t\tout_info->subdiv_y = br->patch->subdiv[1];\n\t\t\t\tout_info->shadername = PR_TempString(prinst, br->patch->tex->shadername);\n\t\t\t}\n\n\t\t\tif (!out_verts)\n\t\t\t\tG_INT(OFS_RETURN) = br->patch->numcp[0]*br->patch->numcp[1];\n\t\t\telse\n\t\t\t{\n\t\t\t\tmaxverts = min(br->patch->numcp[0]*br->patch->numcp[1], maxverts);\n\n\t\t\t\tfor (j = 0; j < maxverts; j++)\n\t\t\t\t{\n\t\t\t\t\tVectorCopy(br->patch->cp[j].v, out_verts->v);\n\t\t\t\t\tVector2Copy(br->patch->cp[j].tc, out_verts->tc);\n\t\t\t\t\tVector4Copy(br->patch->cp[j].rgba, out_verts->rgba);\n\n\t\t\t\t\tout_verts++;\n\t\t\t\t}\n\t\t\t\tG_INT(OFS_RETURN) = j;\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t}\n}\n//  {\"patch_evaluate\",\tPF_patch_evaluate,\t0,\t\t0,\t\t0,\t\t0,\t\tD(\"int(patchvert_t *in_controlverts, patchvert_t *out_renderverts, int maxout, patchinfo_t *inout_info)\", \"Calculates the geometry of a hyperthetical patch.\")},\nvoid QCBUILTIN PF_patch_evaluate(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tqcpatchinfo_t\t*inout_info\t\t= validateqcpointer(prinst, G_INT(OFS_PARM3), sizeof(*inout_info), 1, false);\n\tunsigned int\tmaxverts\t\t= G_INT(OFS_PARM2);\n\tqcpatchvert_t\t*out_verts\t\t= validateqcpointer(prinst, G_INT(OFS_PARM1), sizeof(*out_verts), maxverts, true);\n\tqcpatchvert_t\t*in_cp\t\t\t= validateqcpointer(prinst, G_INT(OFS_PARM0), sizeof(*in_cp), inout_info->cp_width*inout_info->cp_height, false);\n\n\tunsigned short numcp[] = {inout_info->cp_width, inout_info->cp_height}, size[2];\n\tshort subdiv[] = {inout_info->subdiv_x, inout_info->subdiv_y};\n\tpatchtessvert_t\t*working_verts\t= PatchInfo_Evaluate(in_cp, numcp, subdiv, size);\n\n\tunsigned int i;\n\tif (working_verts)\n\t{\n\t\tif (out_verts)\n\t\t{\n\t\t\tmaxverts = min(maxverts, size[0]*size[1]);\n\t\t\tfor (i = 0; i < maxverts; i++)\n\t\t\t{\n\t\t\t\tVectorCopy(working_verts[i].v, out_verts[i].v);\n\t\t\t\tVector4Copy(working_verts[i].rgba, out_verts[i].rgba);\n\t\t\t\tVector2Copy(working_verts[i].tc, out_verts[i].tc);\n\t\t\t}\n\t\t}\n\t\tBZ_Free(working_verts);\n\n\t\tinout_info->cp_width = size[0];\t//not really controlpoints, but the data works the same.\n\t\tinout_info->cp_height = size[1];\n\t}\n\telse\n\t\tinout_info->cp_width = inout_info->cp_height = 0; //erk...\n\tinout_info->subdiv_x = inout_info->subdiv_y = 0;\t//make it as explicit tessellation, so we can maybe skip this.\n\n\tG_INT(OFS_RETURN) = maxverts;\n}\n//\t{\"patch_getmesh\",\tPF_patch_getmesh,\t0,\t\t0,\t\t0,\t\t0,\t\tD(\"int(float modelidx, int patchid, patchvert_t *out_verts, int maxverts, patchinfo_t *out_info)\", \"Queries a patch's information. You must pre-allocate the face array for the builtin to write to. Return value is the total number of control verts that were retrieved, 0 on error.\")},\nvoid QCBUILTIN PF_patch_getmesh(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tworld_t\t\t\t*vmw\t\t\t= prinst->parms->user;\n\tmodel_t\t\t\t*mod\t\t\t= vmw->Get_CModel(vmw, G_FLOAT(OFS_PARM0));\n\theightmap_t\t\t*hm\t\t\t\t= mod?mod->terrain:NULL;\n\tunsigned int\tpatchid\t\t\t= G_INT(OFS_PARM1);\n\tunsigned int\tmaxverts\t\t= G_INT(OFS_PARM3);\n\tqcpatchvert_t\t*out_verts\t\t= validateqcpointer(prinst, G_INT(OFS_PARM2), sizeof(*out_verts), maxverts, true);\n\tqcpatchinfo_t\t*out_info\t\t= validateqcpointer(prinst, G_INT(OFS_PARM4), sizeof(*out_info), 1, true);\n\tunsigned int\ti, j;\n\tbrushes_t\t\t*br;\n\n\t//assume the worst.\n\tG_INT(OFS_RETURN) = 0;\n\tif (out_info)\n\t\tmemset(out_info, 0, sizeof(*out_info));\n\n\tif (!hm)\n\t\treturn;\n\n\tfor (i = 0; i < hm->numbrushes; i++)\n\t{\n\t\tbr = &hm->wbrushes[i];\n\t\tif (br->id == patchid)\n\t\t{\n\t\t\tif (!br->patch)\n\t\t\t\treturn;\n\t\t\tif (out_info)\n\t\t\t{\n\t\t\t\tout_info->contents = br->contents;\n\t\t\t\tout_info->cp_width = br->patch->tesssize[0];\n\t\t\t\tout_info->cp_height = br->patch->tesssize[1];\n\t\t\t\tout_info->subdiv_x = 0;\n\t\t\t\tout_info->subdiv_y = 0;\n\t\t\t\tout_info->shadername = PR_TempString(prinst, br->patch->tex->shadername);\n\t\t\t}\n\n\t\t\tif (!out_verts)\n\t\t\t\tG_INT(OFS_RETURN) = br->patch->tesssize[0]*br->patch->tesssize[1];\n\t\t\telse\n\t\t\t{\n\t\t\t\tmaxverts = min(br->patch->tesssize[0]*br->patch->tesssize[1], maxverts);\n\n\t\t\t\tfor (j = 0; j < maxverts; j++)\n\t\t\t\t{\n\t\t\t\t\tVectorCopy(br->patch->tessvert[j].v, out_verts->v);\n\t\t\t\t\tVector2Copy(br->patch->tessvert[j].tc, out_verts->tc);\n\t\t\t\t\tVector4Copy(br->patch->tessvert[j].rgba, out_verts->rgba);\n\n\t\t\t\t\tout_verts++;\n\t\t\t\t}\n\t\t\t\tG_INT(OFS_RETURN) = j;\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t}\n}\n\n//\t{\"brush_get\",\t\tPF_brush_get,\t\t0,\t\t0,\t\t0,\t\t0,\t\tD(qcbrushface \"int(float modelidx, int brushid, brushface_t *out_faces, int maxfaces, int *out_contents)\", \"Queries a brush's information. You must pre-allocate the face array for the builtin to write to. Return value is the number of faces retrieved, 0 on error.\")},\nvoid QCBUILTIN PF_brush_get(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tworld_t\t\t\t*vmw\t\t\t= prinst->parms->user;\n\tmodel_t\t\t\t*mod\t\t\t= vmw->Get_CModel(vmw, G_FLOAT(OFS_PARM0));\n\theightmap_t\t\t*hm\t\t\t\t= mod?mod->terrain:NULL;\n\tunsigned int\tbrushid\t\t\t= G_INT(OFS_PARM1);\n\tunsigned int\tmaxfaces\t\t= G_INT(OFS_PARM3);\n\tqcbrushface_t\t*out_faces\t\t= validateqcpointer(prinst, G_INT(OFS_PARM2), sizeof(*out_faces), maxfaces, true);\n\tunsigned int\t*out_contents\t= validateqcpointer(prinst, G_INT(OFS_PARM4), sizeof(*out_contents), 1, true);\n\tunsigned int\tfa, i;\n\tbrushes_t\t\t*br;\n\t\n\t//assume the worst.\n\tG_INT(OFS_RETURN) = 0;\n\tif (out_contents)\n\t\t*out_contents = 0;\n\n\tif (!hm)\n\t\treturn;\n\n\tfor (i = 0; i < hm->numbrushes; i++)\n\t{\n\t\tbr = &hm->wbrushes[i];\n\t\tif (br->id == brushid)\n\t\t{\n\t\t\tif (br->patch)\n\t\t\t\treturn;\n\t\t\tif (out_contents)\n\t\t\t\t*out_contents = br->contents;\n\t\t\tif (!out_faces)\n\t\t\t\tG_INT(OFS_RETURN) = br->numplanes;\n\t\t\telse\n\t\t\t{\n\t\t\t\tmaxfaces = min(br->numplanes, maxfaces);\n\n\t\t\t\tfor (fa = 0; fa < maxfaces; fa++)\n\t\t\t\t{\n\t\t\t\t\tout_faces->shadername = PR_TempString(prinst, br->faces[fa].tex->shadername);\n\t\t\t\t\tVectorCopy(br->planes[fa], out_faces->planenormal);\n\t\t\t\t\tout_faces->planedist = br->planes[fa][3];\n\n\t\t\t\t\tVectorCopy(br->faces[fa].stdir[0], out_faces->sdir);\n\t\t\t\t\tout_faces->sbias = br->faces[fa].stdir[0][3];\n\t\t\t\t\tVectorCopy(br->faces[fa].stdir[1], out_faces->tdir);\n\t\t\t\t\tout_faces->tbias = br->faces[fa].stdir[1][3];\n\n\t\t\t\t\tout_faces++;\n\t\t\t\t}\n\t\t\t\tG_INT(OFS_RETURN) = fa;\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t}\n}\n//\t{\"brush_create\",\tPF_brush_create,\t0,\t\t0,\t\t0,\t\t0,\t\tD(\"int(float modelidx, brushface_t *in_faces, int numfaces, int contents, optional int prevbrushid=0)\", \"Inserts a new brush into the model. Return value is the new brush's id.\")},\nvoid QCBUILTIN PF_brush_create(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tworld_t *vmw = prinst->parms->user;\n\tint\t\t\t\tmodelindex\t\t= G_FLOAT(OFS_PARM0);\n\tmodel_t\t\t\t*mod\t\t\t= vmw->Get_CModel(vmw, modelindex);\n\theightmap_t\t\t*hm\t\t\t\t= mod?mod->terrain:NULL;\n\tunsigned int\tnumfaces\t\t= G_INT(OFS_PARM2);\n\tqcbrushface_t\t*in_faces\t\t= validateqcpointer(prinst, G_INT(OFS_PARM1), sizeof(*in_faces), numfaces, numfaces==0);\n\tunsigned int\tcontents\t\t= G_INT(OFS_PARM3);\n\tunsigned int\tbrushid\t\t\t= (prinst->callargc > 4)?G_INT(OFS_PARM4):0;\t//to simplify edits\n\n\tunsigned int\t\t\ti;\n\tbrushes_t\t\t\t\tbrush, *nb;\n\tvec4_t\t\t\t\t\t*planes;\n\tstruct brushface_s\t\t*faces;\n\n\tG_INT(OFS_RETURN) = 0;\n\n\tif (!hm)\n\t{\n\t\tif (mod && mod->loadstate == MLS_LOADING)\n\t\t\tCOM_WorkerPartialSync(mod, &mod->loadstate, MLS_LOADING);\n\t\tif (mod && mod->loadstate == MLS_LOADED)\n\t\t{\n\t\t\tchar basename[MAX_QPATH];\n\t\t\tCOM_FileBase(mod->name, basename, sizeof(basename));\n\t\t\tmod->terrain = Mod_LoadTerrainInfo(mod, basename, true);\n\t\t\thm = mod->terrain;\n\t\t\tif (!hm)\n\t\t\t\treturn;\n\t\t\tTerr_FinishTerrain(mod);\n\t\t}\n\t\telse\n\t\t\treturn;\n\t}\n\n\t//if we're creating one that already exists, then assume that its a move.\n\tif (brushid && Terr_Brush_DeleteId(hm, brushid))\n\t{\n#ifdef HAVE_SERVER\n\t\tif (sv.state && modelindex > 0)\n\t\t{\n\t\t\tMSG_WriteByte(&sv.multicast, svcfte_brushedit);\n\t\t\tMSG_WriteShort(&sv.multicast, modelindex);\n\t\t\tMSG_WriteByte(&sv.multicast, hmcmd_brush_delete);\n\t\t\tMSG_WriteLong(&sv.multicast, brushid);\n\t\t\tSV_MulticastProtExt(vec3_origin, MULTICAST_ALL_R, ~0, 0, 0);\n\t\t}\n\t\telse\n#endif\n#ifdef HAVE_CLIENT\n\t\tif (cls.state && modelindex > 0)\n\t\t{\n\t\t\tMSG_WriteByte(&cls.netchan.message, clcfte_brushedit);\n\t\t\tMSG_WriteShort(&cls.netchan.message, modelindex);\n\t\t\tMSG_WriteByte(&cls.netchan.message, hmcmd_brush_delete);\n\t\t\tMSG_WriteLong(&cls.netchan.message, brushid);\n\t\t}\n#else\n\t\t{\n\t\t}\n#endif\n\t}\n\n\tplanes = alloca(sizeof(*planes) * numfaces);\n\tfaces = alloca(sizeof(*faces) * numfaces);\n\tfor (i = 0; i < numfaces; i++)\n\t{\n\t\tVectorCopy(in_faces[i].planenormal, planes[i]);\n\t\tplanes[i][3] = in_faces[i].planedist;\n\n\t\tfaces[i].tex = Terr_Brush_FindTexture(hm, PR_GetString(prinst, in_faces[i].shadername));\n\n\t\tVectorCopy(in_faces[i].sdir, faces[i].stdir[0]);\n\t\tfaces[i].stdir[0][3] = in_faces[i].sbias;\n\t\tVectorCopy(in_faces[i].tdir, faces[i].stdir[1]);\n\t\tfaces[i].stdir[1][3] = in_faces[i].tbias;\n\n\t\t//these are for compat with (q2/)q3 so as to not be lossy even if they're not really used.\n\t\tfaces[i].surfaceflags = 0;\n\t\tfaces[i].surfacevalue = 0;\n\t}\n\n\t//now emit it\n\tbrush.id = 0;\n\tbrush.contents = contents;\n\tbrush.numplanes = numfaces;\n\tbrush.planes = planes;\n\tbrush.faces = faces;\n\tbrush.patch = NULL;\n\tif (numfaces)\n\t{\n\t\tnb = Terr_Brush_Insert(mod, hm, &brush);\n\t\tif (nb)\n\t\t{\n\t\t\tG_INT(OFS_RETURN) = nb->id;\n#ifdef HAVE_SERVER\n\t\t\tif (sv.state && modelindex > 0)\n\t\t\t{\n\t\t\t\tMSG_WriteByte(&sv.multicast, svcfte_brushedit);\n\t\t\t\tMSG_WriteShort(&sv.multicast, modelindex);\n\t\t\t\tMSG_WriteByte(&sv.multicast, hmcmd_brush_insert);\n\t\t\t\tBrush_Serialise(&sv.multicast, nb);\n\t\t\t\tSV_MulticastProtExt(vec3_origin, MULTICAST_ALL_R, ~0, 0, 0);\n\t\t\t\treturn;\n\t\t\t}\n#endif\n#ifdef HAVE_CLIENT\n\t\t\tif (cls.state && modelindex > 0)\n\t\t\t{\n\t\t\t\tMSG_WriteByte(&cls.netchan.message, clcfte_brushedit);\n\t\t\t\tMSG_WriteShort(&cls.netchan.message, modelindex);\n\t\t\t\tMSG_WriteByte(&cls.netchan.message, hmcmd_brush_insert);\n\t\t\t\tBrush_Serialise(&cls.netchan.message, nb);\n\t\t\t\treturn;\n\t\t\t}\n#endif\n\t\t}\n\t}\n}\n//{\"patch_create\",\tPF_patch_create,\t0,\t\t0,\t\t0,\t\t0,\t\tD(\"int(float modelidx, int oldpatchid, patchvert_t *in_controlverts, patchinfo_t in_info)\", \"Inserts a new patch into the model. Return value is the new patch's id.\")},\nvoid QCBUILTIN PF_patch_create(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tworld_t *vmw = prinst->parms->user;\n\tint\t\t\t\tmodelindex\t\t= G_FLOAT(OFS_PARM0);\n\tmodel_t\t\t\t*mod\t\t\t= vmw->Get_CModel(vmw, modelindex);\n\theightmap_t\t\t*hm\t\t\t\t= mod?mod->terrain:NULL;\n\tunsigned int\tbrushid\t\t\t= G_INT(OFS_PARM1);\t//to simplify edits\n\tqcpatchinfo_t\t*info\t\t\t= (qcpatchinfo_t*)&G_INT(OFS_PARM3);\n\tunsigned int\ttotalcp\t\t\t= info->cp_width*info->cp_width;\n\tqcpatchvert_t\t*in_cverts\t\t= validateqcpointer(prinst, G_INT(OFS_PARM2), sizeof(*in_cverts), totalcp, false);\n\n\tunsigned int\t\t\ti;\n\tbrushes_t\t\t\t\tbrush, *nb;\n\n\tG_INT(OFS_RETURN) = 0;\n\n\tif (!hm)\n\t{\n\t\tif (mod && mod->loadstate == MLS_LOADING)\n\t\t\tCOM_WorkerPartialSync(mod, &mod->loadstate, MLS_LOADING);\n\t\tif (mod && mod->loadstate == MLS_LOADED)\n\t\t{\n\t\t\tchar basename[MAX_QPATH];\n\t\t\tCOM_FileBase(mod->name, basename, sizeof(basename));\n\t\t\tmod->terrain = Mod_LoadTerrainInfo(mod, basename, true);\n\t\t\thm = mod->terrain;\n\t\t\tif (!hm)\n\t\t\t\treturn;\n\t\t\tTerr_FinishTerrain(mod);\n\t\t}\n\t\telse\n\t\t\treturn;\n\t}\n\n\t//if we're creating one that already exists, then assume that its a move.\n\tif (brushid && Terr_Brush_DeleteId(hm, brushid))\n\t{\n#ifdef HAVE_SERVER\n\t\tif (sv.state && modelindex > 0)\n\t\t{\n\t\t\tMSG_WriteByte(&sv.multicast, svcfte_brushedit);\n\t\t\tMSG_WriteShort(&sv.multicast, modelindex);\n\t\t\tMSG_WriteByte(&sv.multicast, hmcmd_brush_delete);\n\t\t\tMSG_WriteLong(&sv.multicast, brushid);\n\t\t\tSV_MulticastProtExt(vec3_origin, MULTICAST_ALL_R, ~0, 0, 0);\n\t\t}\n\t\telse\n#endif\n#ifdef HAVE_CLIENT\n\t\tif (cls.state && modelindex > 0)\n\t\t{\n\t\t\tMSG_WriteByte(&cls.netchan.message, clcfte_brushedit);\n\t\t\tMSG_WriteShort(&cls.netchan.message, modelindex);\n\t\t\tMSG_WriteByte(&cls.netchan.message, hmcmd_brush_delete);\n\t\t\tMSG_WriteLong(&cls.netchan.message, brushid);\n\t\t}\n#else\n\t\t{\n\t\t}\n#endif\n\t}\n\n\tbrush.patch = alloca(sizeof(*brush.patch) + sizeof(brush.patch->cp[0])*(totalcp-countof(brush.patch->cp)));\n\tmemset (brush.patch, 0, sizeof(*brush.patch) - sizeof(brush.patch->cp));\n\tbrush.patch->numcp[0] = info->cp_width;\n\tbrush.patch->numcp[1] = info->cp_height;\n\tbrush.patch->subdiv[0] = info->subdiv_x;\n\tbrush.patch->subdiv[1] = info->subdiv_y;\n\n\tbrush.patch->tex = Terr_Brush_FindTexture(hm, PR_GetString(prinst, info->shadername));\n\n\tfor (i = 0; i < totalcp; i++)\n\t{\n\t\tVectorCopy(in_cverts[i].v, brush.patch->cp[i].v);\n\t\tVector2Copy(in_cverts[i].tc, brush.patch->cp[i].tc);\n\t\tVector4Copy(in_cverts[i].rgba, brush.patch->cp[i].rgba);\n\t}\n\n\t//now emit it\n\tbrush.id = 0;\n\tbrush.contents = info->contents;\n\tbrush.numplanes = 0;\n\tbrush.planes = NULL;\n\tif (info->cp_width > 1 && info->cp_width > 1)\n\t{\n\t\tnb = Terr_Brush_Insert(mod, hm, &brush);\n\t\tif (nb)\n\t\t{\n\t\t\tG_INT(OFS_RETURN) = nb->id;\n#ifdef HAVE_SERVER\n\t\t\tif (sv.state && modelindex > 0)\n\t\t\t{\n\t\t\t\tMSG_WriteByte(&sv.multicast, svcfte_brushedit);\n\t\t\t\tMSG_WriteShort(&sv.multicast, modelindex);\n\t\t\t\tMSG_WriteByte(&sv.multicast, hmcmd_patch_insert);\n\t\t\t\tPatch_Serialise(&sv.multicast, nb);\n\t\t\t\tSV_MulticastProtExt(vec3_origin, MULTICAST_ALL_R, ~0, 0, 0);\n\t\t\t\treturn;\n\t\t\t}\n#endif\n#ifdef HAVE_CLIENT\n\t\t\tif (cls.state && modelindex > 0)\n\t\t\t{\n\t\t\t\tMSG_WriteByte(&cls.netchan.message, clcfte_brushedit);\n\t\t\t\tMSG_WriteShort(&cls.netchan.message, modelindex);\n\t\t\t\tMSG_WriteByte(&cls.netchan.message, hmcmd_patch_insert);\n\t\t\t\tPatch_Serialise(&cls.netchan.message, nb);\n\t\t\t\treturn;\n\t\t\t}\n#endif\n\t\t}\n\t}\n}\n//\t{\"brush_delete\",\tPF_brush_delete,\t0,\t\t0,\t\t0,\t\t0,\t\tD(\"void(float modelidx, int brushid)\", \"Destroys the specified brush.\")},\nvoid QCBUILTIN PF_brush_delete(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tworld_t *vmw = prinst->parms->user;\n\tint\t\t\t\tmodelindex\t\t= G_FLOAT(OFS_PARM0);\n\tmodel_t\t\t\t*mod\t\t\t= vmw->Get_CModel(vmw, modelindex);\n\theightmap_t\t\t*hm\t\t\t\t= mod?mod->terrain:NULL;\n\tunsigned int\tbrushid\t\t\t= G_INT(OFS_PARM1);\n\n\tif (!hm)\n\t\treturn;\n\n\tTerr_Brush_DeleteId(hm, brushid);\n\n#ifdef HAVE_SERVER\n\tif (sv.state && modelindex > 0)\n\t{\n\t\tMSG_WriteByte(&sv.multicast, svcfte_brushedit);\n\t\tMSG_WriteShort(&sv.multicast, modelindex);\n\t\tMSG_WriteByte(&sv.multicast, hmcmd_brush_delete);\n\t\tMSG_WriteLong(&sv.multicast, brushid);\n\t\tSV_MulticastProtExt(vec3_origin, MULTICAST_ALL_R, ~0, 0, 0);\n\t\treturn;\n\t}\n#endif\n#ifdef HAVE_CLIENT\n\tif (cls.state && modelindex > 0)\n\t{\n\t\tMSG_WriteByte(&cls.netchan.message, clcfte_brushedit);\n\t\tMSG_WriteShort(&cls.netchan.message, modelindex);\n\t\tMSG_WriteByte(&cls.netchan.message, hmcmd_brush_delete);\n\t\tMSG_WriteLong(&cls.netchan.message, brushid);\n\t\treturn;\n\t}\n#endif\n}\n//\t{\"brush_selected\",\tPF_brush_selected,\t0,\t\t0,\t\t0,\t\t0,\t\tD(\"float(float modelid, int brushid, int faceid, float selectedstate)\", \"Allows you to easily set transient visual properties of a brush. If brush/face is -1, applies to all. returns old value. selectedstate=-1 changes nothing (called for its return value).\")},\nvoid QCBUILTIN PF_brush_selected(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tworld_t *vmw = prinst->parms->user;\n\tmodel_t\t\t\t*mod\t\t\t= vmw->Get_CModel(vmw, G_FLOAT(OFS_PARM0));\n\theightmap_t\t\t*hm\t\t\t\t= mod?mod->terrain:NULL;\n\tunsigned int\tbrushid\t\t\t= G_INT(OFS_PARM1);\n//\tunsigned int\tfaceid\t\t\t= G_INT(OFS_PARM2);\n\tint\t\t\t\tstate\t\t\t= G_FLOAT(OFS_PARM3);\n\tunsigned int\ti;\n\tbrushes_t\t\t*br;\n\n\tG_FLOAT(OFS_RETURN) = 0;\n\tif (!hm)\n\t\treturn;\n\n//\thm->recalculatebrushlighting = true;\n\n\tfor (i = 0; i < hm->numbrushes; i++)\n\t{\n\t\tbr = &hm->wbrushes[i];\n\t\tif (br->id == brushid)\n\t\t{\n\t\t\tG_FLOAT(OFS_RETURN) = br->selected;\n\n\t\t\tif (state >= 0)\n\t\t\t{\n\t\t\t\tif (br->selected != state)\n\t\t\t\t{\n\t\t\t\t\tif (br->patch)\n\t\t\t\t\t{\n\t\t\t\t\t\tbr->patch->tex->rebuild = true;\n//\t\t\t\t\t\tbr->patch->relight = true;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tfor (i = 0; i < br->numplanes; i++)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tbr->faces[i].tex->rebuild = true;\n\t\t\t\t\t\t\tbr->faces[i].relight = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tbr->selected = state;\n\t\t\t\t}\n\t\t\t}\n//\t\t\treturn;\n\t\t}\n\t}\n}\n\n\n\n//\t{\"brush_calcfacepoints\",PF_brush_calcfacepoints,0,0,\t\t0,\t\t0,\t\tD(\"int(int faceid, brushface_t *in_faces, int numfaces, vector *points, int maxpoints)\", \"Determines the points of the specified face, if the specified brush were to actually be created.\")},\nvoid QCBUILTIN PF_brush_calcfacepoints(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tsize_t\tfaceid\t\t\t= G_INT(OFS_PARM0);\n\tsize_t\tnumfaces\t\t= G_INT(OFS_PARM2);\n\tqcbrushface_t\t*in_faces\t\t= validateqcpointer(prinst, G_INT(OFS_PARM1), sizeof(*in_faces), numfaces, false);\n\tsize_t\tmaxpoints\t\t= G_INT(OFS_PARM4);\n\tvec3_t\t\t\t*out_verts\t\t= validateqcpointer(prinst, G_INT(OFS_PARM3), sizeof(*out_verts), maxpoints, false);\n\tvecV_t\t\t\tfacepoints[256];\n\tvec4_t\t\t\tplanes[256];\n\tunsigned int\tj, numpoints;\n\n\tfaceid--;\n\tif ((size_t)faceid >= numfaces)\n\t{\n\t\tG_INT(OFS_RETURN) = 0;\n\t\treturn;\n\t}\n\n\t//make sure this isn't a dupe face\n\tfor (j = 0; j < faceid; j++)\n\t{\n\t\tif (in_faces[j].planenormal[0] == in_faces[faceid].planenormal[0] &&\n\t\t\tin_faces[j].planenormal[1] == in_faces[faceid].planenormal[1] &&\n\t\t\tin_faces[j].planenormal[2] == in_faces[faceid].planenormal[2] && \n\t\t\tin_faces[j].planedist == in_faces[faceid].planedist)\n\t\t{\n\t\t\tG_INT(OFS_RETURN) = 0;\n\t\t\treturn;\n\t\t}\n\t}\n\n\t//generate a list that Terr_GenerateBrushFace can actually use, silly, but lets hope this isn't needed to be nippy\n\tfor (j = 0; j < numfaces; j++)\n\t{\n\t\tVectorCopy(in_faces[j].planenormal, planes[j]);\n\t\tplanes[j][3] = in_faces[j].planedist;\n\t}\n\n\t//generate points now (so we know the correct mins+maxs for the brush, and whether the plane is relevent)\n\tnumpoints = Fragment_ClipPlaneToBrush(facepoints, countof(facepoints), planes, sizeof(*planes), numfaces, planes[faceid]);\n\tG_INT(OFS_RETURN) = numpoints;\n\tif (numpoints > maxpoints)\n\t\tnumpoints = maxpoints;\n\n\t//... and copy them out without padding. yeah, silly.\n\tfor (j = 0; j < numpoints; j++)\n\t{\n\t\tVectorCopy(facepoints[j], out_verts[j]);\n\t}\n}\n\n//\t{\"brush_getfacepoints\",PF_brush_getfacepoints,0,0,\t\t0,\t\t0,\t\tD(\"int(float modelid, int brushid, int faceid, vector *points, int maxpoints)\", \"Allows you to easily set transient visual properties of a brush. If brush/face is -1, applies to all. returns old value. selectedstate=-1 changes nothing (called for its return value).\")},\nvoid QCBUILTIN PF_brush_getfacepoints(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tworld_t *vmw = prinst->parms->user;\n\tmodel_t\t\t\t*mod\t\t\t= vmw->Get_CModel(vmw, G_FLOAT(OFS_PARM0));\n\theightmap_t\t\t*hm\t\t\t\t= mod?mod->terrain:NULL;\n\tunsigned int\tbrushid\t\t\t= G_INT(OFS_PARM1);\n\tunsigned int\tfaceid\t\t\t= G_INT(OFS_PARM2);\n\tunsigned int\tmaxpoints\t\t= G_INT(OFS_PARM4), p;\n\tvec3_t\t\t\t*out_verts\t\t= validateqcpointer(prinst, G_INT(OFS_PARM3), sizeof(*out_verts), maxpoints, false);\n\tsize_t i;\n\tbrushes_t *br;\n\n\tG_INT(OFS_RETURN) = 0;\n\n\tif (!hm)\n\t\treturn;\n\n\tfor (i = 0; i < hm->numbrushes; i++)\n\t{\n\t\tbr = &hm->wbrushes[i];\n\t\tif (br->id == brushid)\n\t\t{\n\t\t\tif (!faceid)\n\t\t\t{\n\t\t\t\tif (maxpoints >= 2)\n\t\t\t\t{\n\t\t\t\t\tVectorCopy(br->mins, out_verts[0]);\n\t\t\t\t\tVectorCopy(br->maxs, out_verts[1]);\n\t\t\t\t\tG_INT(OFS_RETURN) = 2;\n\t\t\t\t}\n\t\t\t\telse if (maxpoints == 1)\n\t\t\t\t{\n\t\t\t\t\tVectorInterpolate(br->mins, 0.5, br->maxs, out_verts[0]);\n\t\t\t\t\tG_INT(OFS_RETURN) = 1;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tfaceid--;\n\t\t\t\tif (br->patch)\n\t\t\t\t{\n\t\t\t\t\tint w = br->patch->numcp[0];\n\t\t\t\t\tint h = br->patch->numcp[1];\n\t\t\t\t\tint x = faceid % (w-1);\n\t\t\t\t\tint y = faceid / (w-1);\n\t\t\t\t\tif (x >= w-1 || y >= h-1)\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tif (maxpoints >= 1)\n\t\t\t\t\t\tVectorCopy(br->patch->cp[(x+0)+(y+0)*w].v, out_verts[0]);\n\t\t\t\t\tif (maxpoints >= 2)\n\t\t\t\t\t\tVectorCopy(br->patch->cp[(x+1)+(y+0)*w].v, out_verts[1]);\n\t\t\t\t\tif (maxpoints >= 3)\n\t\t\t\t\t\tVectorCopy(br->patch->cp[(x+1)+(y+1)*w].v, out_verts[2]);\n\t\t\t\t\tif (maxpoints >= 3)\n\t\t\t\t\t\tVectorCopy(br->patch->cp[(x+0)+(y+1)*w].v, out_verts[3]);\n\t\t\t\t\tp = min(4, maxpoints);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tif (faceid >= br->numplanes)\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tmaxpoints = min(maxpoints, br->faces[faceid].numpoints);\n\t\t\t\t\tfor (p = 0; p < maxpoints; p++)\n\t\t\t\t\t\tVectorCopy(br->faces[faceid].points[p], out_verts[p]);\n\t\t\t\t}\n\t\t\t\tG_INT(OFS_RETURN) = p;\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t}\n}\n//\t{\"brush_findinvolume\",PF_brush_findinvolume,0,0,\t\t0,\t\t0,\t\tD(\"int(float modelid, vector *planes, float *dists, int numplanes, int *out_brushes, int *out_faces, int maxresults)\", \"Allows you to easily obtain a list of brushes+faces within the given bounding region. If out_faces is not null, the same brush might be listed twice.\")},\nvoid QCBUILTIN PF_brush_findinvolume(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tworld_t *vmw = prinst->parms->user;\n\tmodel_t\t\t\t*mod\t\t\t= vmw->Get_CModel(vmw, G_FLOAT(OFS_PARM0));\n\theightmap_t\t\t*hm\t\t\t\t= mod?mod->terrain:NULL;\n\tint\t\t\t\tin_numplanes\t= G_INT(OFS_PARM3);\n\tvec3_t\t\t\t*in_normals\t\t= validateqcpointer(prinst, G_INT(OFS_PARM1), sizeof(*in_normals), in_numplanes, false);\n\tfloat\t\t\t*in_distances\t= validateqcpointer(prinst, G_INT(OFS_PARM2), sizeof(*in_distances), in_numplanes, false);\n\tunsigned int\tmaxresults\t\t= G_INT(OFS_PARM6);\n\tunsigned int\t*out_brushids\t= validateqcpointer(prinst, G_INT(OFS_PARM4), sizeof(*out_brushids), maxresults, false);\n\tunsigned int\t*out_faceids\t= G_INT(OFS_PARM5)?validateqcpointer(prinst, G_INT(OFS_PARM5), sizeof(*out_faceids), maxresults, false):NULL;\n\tunsigned int\ti, j, k, r = 0;\n\tbrushes_t *br;\n\tvec3_t best;\n\tfloat dist;\n\n\t//find all brushes/faces with a vetex within the region\n\t//the brush is inside if any every plane has at least one vertex on the inner side\n\n\tif (hm)\n\tfor (i = 0; i < hm->numbrushes; i++)\n\t{\n\t\tbr = &hm->wbrushes[i];\n\n\t\tfor (j = 0; j < in_numplanes; j++)\n\t\t{\n\t\t\tfor (k=0 ; k<3 ; k++)\n\t\t\t{\n\t\t\t\tif (in_normals[j][k] < 0)\n\t\t\t\t\tbest[k] = br->maxs[k];\n\t\t\t\telse\n\t\t\t\t\tbest[k] = br->mins[k];\n\t\t\t}\n\t\t\tdist = DotProduct (best, in_normals[j]);\n\t\t\tdist = in_distances[j] - dist;\n\t\t\tif (dist <= 0)\t//don't find coplanar brushes. add an epsilon if you need this.\n\t\t\t\tbreak;\n\t\t}\n\t\tif (j == in_numplanes)\n\t\t{\n\t\t\t//the box had some point on the near side of every single plane, and thus must contain at least part of the box\n\t\t\tif (r == maxresults)\n\t\t\t\tbreak;\t//ran out\n\t\t\tout_brushids[r] = br->id;\n\t\t\tif (out_faceids)\t//FIXME: handle this properly.\n\t\t\t\tout_faceids[r] = 0;\n\t\t\tr++;\n\t\t}\n\t}\n\tG_INT(OFS_RETURN) = r;\n}\n\nvoid Terr_WriteBrushInfo(vfsfile_t *file, brushes_t *br)\n{\n\tfloat *point[3];\n\tint i, x, y;\n\tconst qboolean valve220 = true;\n\n\tVFS_PRINTF(file, \"\\n{\");\n\tif (br->patch)\n\t{\n\t\tqboolean hasrgba = false;\n\t\tfor (y = 0; y < br->patch->numcp[1]*br->patch->numcp[0]; y++)\n\t\t{\n\t\t\tif (br->patch->cp[y].rgba[0] != 1.0 || br->patch->cp[y].rgba[1] != 1.0 || br->patch->cp[y].rgba[2] != 1.0 || br->patch->cp[y].rgba[3] != 1.0)\n\t\t\t\tbreak;\n\t\t}\n\t\thasrgba = (y < br->patch->numcp[1]*br->patch->numcp[0]);\n\n\t\tif (br->patch->subdiv[0]>=0 && br->patch->subdiv[1]>=0)\n\t\t{\n\t\t\tVFS_PRINTF(file, \"\\n\\tpatchDef3%s\\n\\t{\\n\\t\\t\\\"%s\\\"\\n\\t\\t( %u %u %u %u %.9g %.9g %.9g )\\n\\t\\t(\\n\",\n\t\t\t\t\thasrgba?\"WS\":\"\",\n\t\t\t\t\tbr->patch->tex?br->patch->tex->shadername:\"\",\n\t\t\t\t\tbr->patch->numcp[0]/*width*/,\n\t\t\t\t\tbr->patch->numcp[1]/*height*/,\n\t\t\t\t\tbr->patch->subdiv[0]/*width*/,\n\t\t\t\t\tbr->patch->subdiv[1]/*height*/,\n\t\t\t\t\t0.0/*rotation*/,\n\t\t\t\t\t1.0/*xscale*/,\n\t\t\t\t\t1.0/*yscale*/);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tVFS_PRINTF(file, \"\\n\\tpatchDef2%s\\n\\t{\\n\\t\\t\\\"%s\\\"\\n\\t\\t( %u %u %.9g %.9g %.9g )\\n\\t\\t(\\n\",\n\t\t\t\t\thasrgba?\"WS\":\"\",\n\t\t\t\t\tbr->patch->tex?br->patch->tex->shadername:\"\",\n\t\t\t\t\tbr->patch->numcp[0]/*width*/,\n\t\t\t\t\tbr->patch->numcp[1]/*height*/,\n\t\t\t\t\t0.0/*rotation*/,\n\t\t\t\t\t1.0/*xscale*/,\n\t\t\t\t\t1.0/*yscale*/);\n\t\t}\n\t\tfor (y = 0; y < br->patch->numcp[1]; y++)\n\t\t{\n\t\t\tVFS_PRINTF(file, \"\\t\\t\\t(\\n\");\n\t\t\tfor (x = 0; x < br->patch->numcp[0]; x++)\n\t\t\t{\n\t\t\t\tconst char *fmt;\n\t\t\t\tif (hasrgba)\n\t\t\t\t\tfmt = \"\\t\\t\\t\\t( %.9g %.9g %.9g %.9g %.9g %.9g %.9g %.9g %.9g )\\n\";\n\t\t\t\telse\n\t\t\t\t\tfmt = \"\\t\\t\\t\\t( %.9g %.9g %.9g %.9g %.9g )\\n\";\t//q3 compat.\n\t\t\t\tVFS_PRINTF(file, fmt,\tbr->patch->cp[x + y*br->patch->numcp[0]].v[0],\n\t\t\t\t\t\t\t\t\t\tbr->patch->cp[x + y*br->patch->numcp[0]].v[1],\n\t\t\t\t\t\t\t\t\t\tbr->patch->cp[x + y*br->patch->numcp[0]].v[2],\n\t\t\t\t\t\t\t\t\t\tbr->patch->cp[x + y*br->patch->numcp[0]].tc[0],\n\t\t\t\t\t\t\t\t\t\tbr->patch->cp[x + y*br->patch->numcp[0]].tc[1],\n\t\t\t\t\t\t\t\t\t\tbr->patch->cp[x + y*br->patch->numcp[0]].rgba[0],\n\t\t\t\t\t\t\t\t\t\tbr->patch->cp[x + y*br->patch->numcp[0]].rgba[1],\n\t\t\t\t\t\t\t\t\t\tbr->patch->cp[x + y*br->patch->numcp[0]].rgba[2],\n\t\t\t\t\t\t\t\t\t\tbr->patch->cp[x + y*br->patch->numcp[0]].rgba[3]);\n\t\t\t}\n\t\t\tVFS_PRINTF(file, \"\\t\\t\\t)\\n\");\n\t\t}\n\t\tVFS_PRINTF(file, \"\\t\\t)\\n\\t}\\n\");\n\t}\n\telse\n\t{\n\t\tfor (i = 0; i < br->numplanes; i++)\n\t\t{\n\t\t\tconst char *texname, *s;\n\t\t\tpoint[0] = br->faces[i].points[0];\n\t\t\tpoint[1] = br->faces[i].points[1];\n\t\t\tpoint[2] = br->faces[i].points[2];\n\n\t\t\t//valve 220 format:\n\t\t\t//(-0 -0 16) (-0 -0 32) (64 -0 16) texname [x y z d] [x y z d] rotation sscale tscale\n\t\t\t//don't treat whitespace as optional, even if it works with qbsp it'll screw up third party editors.\n\n\t\t\t//%.9g is 'meant' to be lossless for a standard ieee single-precision float. (%.17g for a double)\n\n\t\t\t//write the 3 points-on-plane. I really hope its not degenerate\n\t\t\tVFS_PRINTF(file, \"\\n( %.9g %.9g %.9g ) ( %.9g %.9g %.9g ) ( %.9g %.9g %.9g )\",\n\t\t\t\t\tpoint[0][0], point[0][1], point[0][2],\n\t\t\t\t\tpoint[1][0], point[1][1], point[1][2],\n\t\t\t\t\tpoint[2][0], point[2][1], point[2][2]\n\t\t\t\t);\n\n\t\t\t//write the name - if it contains markup or control chars, or other weird glyphs then be sure to quote it.\n\t\t\t//we could unconditionally quote it, but that can and will screw up some editor somewhere (like trenchbroom...)\n\t\t\tfor (s = texname = br->faces[i].tex?br->faces[i].tex->shadername:\"\"; *s; s++)\n\t\t\t{\n\t\t\t\tif (*s <= 32 || *s >= 127 || *s == '\\\\' || *s == '(' || *s == '[' || *s == '{' || *s == ')' || *s == ']' || *s == '}')\n\t\t\t\t\tbreak;\t//\n\t\t\t}\n\t\t\tVFS_PRINTF(file, (!*texname || *s)?\" \\\"%s\\\"\":\" %s\", texname);\n\n\t\t\tif (valve220)\n\t\t\t{\n\t\t\t\tVFS_PRINTF(file, \" [ %.9g %.9g %.9g %.9g ] [ %.9g %.9g %.9g %.9g ] 0 1 1\",\n\t\t\t\t\t\tbr->faces[i].stdir[0][0], br->faces[i].stdir[0][1], br->faces[i].stdir[0][2], br->faces[i].stdir[0][3],\n\t\t\t\t\t\tbr->faces[i].stdir[1][0], br->faces[i].stdir[1][1], br->faces[i].stdir[1][2], br->faces[i].stdir[1][3]\n\t\t\t\t\t);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tfloat soffset, toffset, rotation, sscale, tscale;\n\t\t\t\t//FIXME: project onto the axial plane, then figure out new values.\n\t\t\t\tsoffset = toffset = 0;\n\t\t\t\trotation = 0;\n\t\t\t\tsscale = tscale = 1;\n\t\t\t\tVFS_PRINTF(file, \" %.9g %.9g %.9g %.9g %.9g\", soffset, toffset, rotation, sscale, tscale);\n\t\t\t}\n\n\t\t\t//historical note: Q2 used contents|surfaceflags|value.\n\t\t\t//                 however, Q3 uses the contents value exclusively for a detail flag. everything else comes from shaders.\n\t\t\tif (br->contents != FTECONTENTS_SOLID || br->faces[i].surfaceflags || br->faces[i].surfacevalue)\n\t\t\t\tVFS_PRINTF(file, \" %i %i %i\", br->contents, br->faces[i].surfaceflags, br->faces[i].surfacevalue);\n//\t\t\telse if (hexen2)\n//\t\t\t\tVFS_PRINTF(file, \" -1\");\t//Light\n\t\t}\n\t}\n\n\tVFS_PRINTF(file, \"\\n}\");\n}\nvoid Terr_WriteMapFile(vfsfile_t *file, model_t *mod)\n{\n\tchar token[8192];\n\tint nest = 0;\n\tconst char *start, *entities = Mod_GetEntitiesString(mod);\n\tint i;\n\tunsigned int entnum = 0;\n\theightmap_t *hm;\n\t\n\thm = mod->terrain;\n\tif (hm && hm->legacyterrain)\n\t\tVFS_WRITE(file, \"terrain\\n\", 8);\n\n\tstart = entities;\n\twhile(entities)\n\t{\n\t\tentities = COM_ParseOut(entities, token, sizeof(token));\n\t\tif (token[0] == '}' && token[1] == 0)\n\t\t{\n\t\t\tnest--;\n\t\t\tif (!nest)\n\t\t\t{\n\t\t\t\tif (!entnum)\n\t\t\t\t{\n//\t\t\t\t\tVFS_PRINTF(file, \"\\n//Worldspawn brushes go here\");\n\n\t\t\t\t\thm = mod->terrain;\n\t\t\t\t\tif (hm)\n\t\t\t\t\t\tfor (i = 0; i < hm->numbrushes; i++)\n\t\t\t\t\t\t\tTerr_WriteBrushInfo(file, &hm->wbrushes[i]);\n\t\t\t\t}\n\t\t\t\tentnum++;\n\t\t\t}\n\t\t}\n\t\telse if (token[0] == '{' && token[1] == 0)\n\t\t{\n\t\t\tnest++;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (!strcmp(token, \"model\"))\n\t\t\t{\n\t\t\t\tint submodelnum;\n\t\t\t\tentities = COM_ParseOut(entities, token, sizeof(token));\n\n\t\t\t\tif (*token == '*')\n\t\t\t\t\tsubmodelnum = atoi(token+1);\n\t\t\t\telse\n\t\t\t\t\tsubmodelnum = 0;\n\n\t\t\t\tif (submodelnum)\n\t\t\t\t{\n\t\t\t\t\tmodel_t *submod;\n\n\t\t\t\t\tQ_snprintfz(token, sizeof(token), \"*%i:%s\", submodelnum, mod->name);\n\t\t\t\t\tsubmod = Mod_FindName (token);\n\n//\t\t\t\t\tVFS_PRINTF(file, \"\\nBrushes for %s go here\", token);\n\t\t\t\t\thm = submod->terrain;\n\t\t\t\t\tif (hm)\n\t\t\t\t\t{\n\t\t\t\t\t\tfor (i = 0; i < hm->numbrushes; i++)\n\t\t\t\t\t\t\tTerr_WriteBrushInfo(file, &hm->wbrushes[i]);\n\n\t\t\t\t\t\tstart = entities;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t\tentities = COM_ParseOut(entities, token, sizeof(token));\n\t\t}\n\t\tif (entities)\n\t\t\tVFS_WRITE(file, start, entities - start);\n\t\tstart = entities;\n\t}\n}\nvoid Mod_Terrain_Save_f(void)\n{\n\tvfsfile_t *file;\n\tmodel_t *mod;\n\tconst char *mapname = Cmd_Argv(1);\n\tchar fname[MAX_QPATH];\n\tif (Cmd_IsInsecure())\n\t{\n\t\tCon_Printf(\"Please use this command via the console\\n\");\n\t\treturn;\n\t}\n\tif (*mapname)\n\t\tmod = Mod_FindName(va(\"maps/%s\", mapname));\n#ifdef HAVE_CLIENT\n\telse if (cls.state)\n\t\tmod = cl.worldmodel;\n#endif\n\telse\n\t\tmod = NULL;\n\n\tif (!mod)\n\t{\n\t\tCon_Printf(\"no model loaded by that name\\n\");\n\t\treturn;\n\t}\n\tif (mod->loadstate != MLS_LOADED)\n\t{\n\t\tCon_Printf(\"that model isn't fully loaded\\n\");\n\t\treturn;\n\t}\n\tif (*Cmd_Argv(2))\n\t\tQ_snprintfz(fname, sizeof(fname), \"maps/%s.map\", Cmd_Argv(2));\n\telse\n\t\tQ_snprintfz(fname, sizeof(fname), \"%s\", mod->name);\n\n\tif (mod->type != mod_heightmap)\n\t{\n\t\t//warning: brushes are not saved unless its a .map\n\t\tCOM_StripExtension(mod->name, fname, sizeof(fname));\n\t\tQ_strncatz(fname, mod_modifier, sizeof(fname));\n\t\tQ_strncatz(fname, \".ent\", sizeof(fname));\n\n\t\tFS_CreatePath(fname, FS_GAMEONLY);\n\t\tfile = FS_OpenVFS(fname, \"wb\", FS_GAMEONLY);\n\t\tif (!file)\n\t\t\tCon_TPrintf(\"unable to open %s\\n\", fname);\n\t\telse\n\t\t{\n\t\t\tconst char *s = Mod_GetEntitiesString(mod);\n\t\t\tVFS_WRITE(file, s, strlen(s));\n\t\t\tVFS_CLOSE(file);\n\t\t\tFS_FlushFSHashWritten(fname);\n\t\t}\n\t}\n\telse\n\t{\n\t\tFS_CreatePath(fname, FS_GAMEONLY);\n\t\tfile = FS_OpenVFS(fname, \"wb\", FS_GAMEONLY);\n\t\tif (!file)\n\t\t\tCon_TPrintf(\"unable to open %s\\n\", fname);\n\t\telse\n\t\t{\n\t\t\tTerr_WriteMapFile(file, mod);\n\t\t\tVFS_CLOSE(file);\n\t\t\tFS_FlushFSHashWritten(fname);\n\t\t}\n\t}\n}\nqboolean Terr_ReformEntitiesLump(model_t *mod, heightmap_t *hm, char *entities)\n{\n\tchar token[8192];\n\tint nest = 0;\n\tint buflen = strlen(entities);\n\tchar *out, *outstart, *start;\n\tint i;\n\tint submodelnum = 0;\n\tqboolean foundsubmodel = false;\n\tqboolean inbrush = false;\n\tint brushcontents = FTECONTENTS_SOLID;\n\theightmap_t *subhm = NULL;\n\tmodel_t *submod = NULL;\n\tconst char *brushpunct = \"(){}[]\";\t//use an empty string for better compat with vanilla qbsp...\n\n\t//brush planes\n\tint numplanes = 0;\n\tvec4_t planes[256];\n\tstruct brushface_s faces[countof(planes)];\n\n\t//patch info\n\tbrushtex_t *patch_tex=NULL;\n\tint\tpatchsz[2]={0,0}, patchsubdiv[2]={-1,-1};\n\tqcpatchvert_t patch_v[64][64];\n\n#ifdef RUNTIMELIGHTING\n\thm->entsdirty = true;\n\thm->relightcontext = mod_terrain_brushlights.ival?LightStartup(NULL, mod, mod_terrain_brushlights.ival>1, false):NULL;\n\thm->lightthreadmem = BZ_Malloc(lightthreadctxsize);\n\thm->inheritedlightthreadmem = false;\n#endif\n\n\t/*FIXME: we need to re-form the entities lump to insert model fields as appropriate*/\n\toutstart = out = Z_Malloc(buflen+1);\n\n\twhile(entities)\n\t{\n\t\tstart = entities;\n\t\tentities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL);\n\t\tif (!entities)\n\t\t{\n\t\t\tif (inbrush || nest)\n\t\t\t{\n\t\t\t\tCon_Printf(CON_ERROR \"%s: File truncated?\\n\", mod->name);\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t\telse if (token[0] == '}' && token[1] == 0)\n\t\t{\n\t\t\tnest--;\n\t\t\tif (inbrush)\n\t\t\t{\n\t\t\t\tif (subhm)\n\t\t\t\t{\n\t\t\t\t\tqboolean oe = subhm->brushesedited;\n\t\t\t\t\tif (numplanes)\n\t\t\t\t\t{\n\t\t\t\t\t\tbrushes_t brush;\n\t\t\t\t\t\t//finish the brush\n\t\t\t\t\t\tbrush.contents = brushcontents;\n\t\t\t\t\t\tbrush.numplanes = numplanes;\n\t\t\t\t\t\tbrush.planes = planes;\n\t\t\t\t\t\tbrush.faces = faces;\n\t\t\t\t\t\tbrush.id = 0;\n\t\t\t\t\t\tbrush.patch = NULL;\n\t\t\t\t\t\tTerr_Brush_Insert(submod, subhm, &brush);\n\t\t\t\t\t}\n\t\t\t\t\telse if (patch_tex)\n\t\t\t\t\t\tTerr_Patch_Insert(submod, subhm, patch_tex, patchsz[0], patchsz[1], patchsubdiv[0], patchsubdiv[1], patch_v[0], countof(patch_v[0]));\n\t\t\t\t\tsubhm->brushesedited = oe;\n\t\t\t\t}\n\t\t\t\tnumplanes = 0;\n\t\t\t\tinbrush = false;\n\t\t\t\tpatch_tex = NULL;\n\t\t\t\tbrushcontents = FTECONTENTS_SOLID;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\t\telse if (token[0] == '{' && token[1] == 0)\n\t\t{\n\t\t\tnest++;\n\t\t\tif (nest == 1)\n\t\t\t{\t//entering a new entity\n\t\t\t\tfoundsubmodel = false;\n\t\t\t}\n\t\t\tif (nest == 2)\n\t\t\t{\n\t\t\t\tif (!foundsubmodel)\n\t\t\t\t{\n\t\t\t\t\tfoundsubmodel = true;\n\t\t\t\t\tif (submodelnum)\n\t\t\t\t\t{\n\t\t\t\t\t\tQ_snprintfz(token, sizeof(token), \"*%i\", submodelnum);\n\t\t\t\t\t\t*out++ = '\\n';\n\t\t\t\t\t\t*out++ = 'm';\n\t\t\t\t\t\t*out++ = 'o';\n\t\t\t\t\t\t*out++ = 'd';\n\t\t\t\t\t\t*out++ = 'e';\n\t\t\t\t\t\t*out++ = 'l';\n\t\t\t\t\t\t*out++ = ' ';\n\t\t\t\t\t\t*out++ = '\\\"';\n\t\t\t\t\t\tfor (i = 0; token[i]; i++)\n\t\t\t\t\t\t\t*out++ = token[i];\n\t\t\t\t\t\t*out++ = '\\\"';\n\t\t\t\t\t\t*out++ = ' ';\n\t\t\t\t\t\t\n\t\t\t\t\t\tQ_snprintfz(token, sizeof(token), \"*%i:%s\", submodelnum, mod->name);\n\t\t\t\t\t\tsubmod = Mod_FindName (token);\n\t\t\t\t\t\tif (submod->loadstate == MLS_NOTLOADED)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tsubmod->type = mod_heightmap;\n\t\t\t\t\t\t\tMod_SetEntitiesString(submod, \"\", true);\n\t\t\t\t\t\t\tsubhm = submod->terrain = Mod_LoadTerrainInfo(submod, submod->name, true);\n\n\t\t\t\t\t\t\tsubhm->exteriorcontents = FTECONTENTS_EMPTY;\n\n\t\t\t\t\t\t\tClearBounds(submod->mins, submod->maxs);\n\n\t\t\t\t\t\t\tsubmod->funcs.NativeTrace\t\t\t= Heightmap_Trace_Test;\n\t\t\t\t\t\t\tsubmod->funcs.PointContents\t\t\t= Heightmap_PointContents;\n\t\t\t\t\t\t\tsubmod->funcs.NativeContents\t\t= Heightmap_NativeBoxContents;\n\t\t\t\t\t\t\tsubmod->funcs.LightPointValues\t\t= Heightmap_LightPointValues;\n\t\t\t\t\t\t\tsubmod->funcs.StainNode\t\t\t\t= Heightmap_StainNode;\n\t\t\t\t\t\t\tsubmod->funcs.MarkLights\t\t\t= Heightmap_MarkLights;\n\t\t\t\t\t\t\tsubmod->funcs.ClusterForPoint\t\t= Heightmap_ClusterForPoint;\n\t\t\t\t\t\t\tsubmod->funcs.ClusterPVS\t\t\t= Heightmap_ClusterPVS;\n#ifdef HAVE_SERVER\n\t\t\t\t\t\t\tsubmod->funcs.FindTouchedLeafs\t\t= Heightmap_FindTouchedLeafs;\n\t\t\t\t\t\t\tsubmod->funcs.EdictInFatPVS\t\t\t= Heightmap_EdictInFatPVS;\n\t\t\t\t\t\t\tsubmod->funcs.FatPVS\t\t\t\t= Heightmap_FatPVS;\n#endif\n\t\t\t\t\t\t\tsubmod->loadstate = MLS_LOADED;\n\t\t\t\t\t\t\tsubmod->pvsbytes = sizeof(hmpvs_t);\n\n#ifdef RUNTIMELIGHTING\n\t\t\t\t\t\t\tif (hm->relightcontext)\n\t\t\t\t\t\t\t\tsubhm->relightcontext = LightStartup(hm->relightcontext, submod, false, false);\n\t\t\t\t\t\t\tsubhm->lightthreadmem = hm->lightthreadmem;\n\t\t\t\t\t\t\tsubhm->inheritedlightthreadmem = true;\n#endif\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tsubhm = NULL;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tsubmod = mod;\n\t\t\t\t\t\tsubhm = hm;\n\t\t\t\t\t}\n\t\t\t\t\tsubmodelnum++;\n\t\t\t\t}\n\t\t\t\tinbrush = true;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\t\telse if (!nest)\n\t\t{\n\t\t\tCon_Printf(CON_ERROR \"%s: junk found\\n\", mod->name);\n\t\t\treturn false;\n\t\t}\n\t\telse if (inbrush && (!strcmp(token, \"patchDef2\")   || !strcmp(token, \"patchDef3\") ||\n\t\t\t\t\t\t\t !strcmp(token, \"patchDef2WS\") || !strcmp(token, \"patchDef3WS\")))\n\t\t{\n\t\t\tint x, y;\n\t\t\tqboolean patchdef3 = !!strchr(token, '3');\t//explict tessellation info (doom3-like)\n\t\t\tqboolean parsergba = !!strstr(token, \"WS\");\t//fancy alternative with rgba colours per control point\n\t\t\tpatchsz[0] = patchsz[1] = 0;\n\t\t\tpatchsubdiv[0] = patchsubdiv[1] = -1;\n\t\t\tif (numplanes || patch_tex)\n\t\t\t{\n\t\t\t\tCon_Printf(CON_ERROR \"%s: mixed patch+planes\\n\", mod->name);\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tmemset(patch_v, 0, sizeof(patch_v));\n\t\t\tentities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL);\n\t\t\tif (strcmp(token, \"{\")) {Con_Printf(CON_ERROR \"%s: invalid patch\\n\", mod->name);return false;}\n\t\t\tentities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL);\n\t\t\t/*parse texture name*/\n\t\t\tpatch_tex = Terr_Brush_FindTexture(subhm, token);\n\t\t\tentities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL);\n\t\t\tif (strcmp(token, \"(\")) {Con_Printf(CON_ERROR \"%s: invalid patch\\n\", mod->name);return false;}\n\t\t\tentities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL);\n\t\t\t/*patch_w = atof(token);*/\n\t\t\tentities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL);\n\t\t\t/*patch_h = atof(token);*/\n\t\t\tif (patchdef3)\n\t\t\t{\n\t\t\t\tentities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL);\n\t\t\t\tpatchsubdiv[0] = atof(token);\n\t\t\t\tentities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL);\n\t\t\t\tpatchsubdiv[1] = atof(token);\n\t\t\t}\n\t\t\tentities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL);\n\t\t\t/*rotation = atof(token);*/\n\t\t\tentities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL);\n\t\t\t/*xscale = atof(token);*/\n\t\t\tentities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL);\n\t\t\t/*yscale = atof(token);*/\n\t\t\tentities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL);\n\t\t\tif (strcmp(token, \")\")) {Con_Printf(CON_ERROR \"%s: invalid patch\\n\", mod->name);return false;}\n\t\t\tentities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL);\n\t\t\tif (strcmp(token, \"(\")) {Con_Printf(CON_ERROR \"%s: invalid patch\\n\", mod->name);return false;}\n\t\t\tentities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL);\n\t\t\ty = 0;\n\t\t\twhile (!strcmp(token, \"(\"))\n\t\t\t{\n\t\t\t\tentities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL);\n\t\t\t\tx = 0;\n\t\t\t\twhile (!strcmp(token, \"(\"))\n\t\t\t\t{\n\t\t\t\t\tentities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL);\n\t\t\t\t\tpatch_v[y][x].v[0] = atof(token);\n\t\t\t\t\tentities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL);\n\t\t\t\t\tpatch_v[y][x].v[1] = atof(token);\n\t\t\t\t\tentities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL);\n\t\t\t\t\tpatch_v[y][x].v[2] = atof(token);\n\t\t\t\t\tentities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL);\n\t\t\t\t\tpatch_v[y][x].tc[0] = atof(token);\n\t\t\t\t\tentities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL);\n\t\t\t\t\tpatch_v[y][x].tc[1] = atof(token);\n\n\t\t\t\t\tif (parsergba)\n\t\t\t\t\t{\t//the following four lines are stupid.\n\t\t\t\t\t\tentities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL);\n\t\t\t\t\t\tif (strcmp(token, \")\")) {Con_Printf(CON_ERROR \"%s: invalid patch\\n\", mod->name);return false;}\n\t\t\t\t\t\tentities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL);\n\t\t\t\t\t\tif (strcmp(token, \"(\")) {Con_Printf(CON_ERROR \"%s: invalid patch\\n\", mod->name);return false;}\n\n\t\t\t\t\t\tentities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL);\n\t\t\t\t\t\tpatch_v[y][x].rgba[0] = atof(token);\n\t\t\t\t\t\tentities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL);\n\t\t\t\t\t\tpatch_v[y][x].rgba[1] = atof(token);\n\t\t\t\t\t\tentities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL);\n\t\t\t\t\t\tpatch_v[y][x].rgba[2] = atof(token);\n\t\t\t\t\t\tentities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL);\n\t\t\t\t\t\tpatch_v[y][x].rgba[3] = atof(token);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\t//no data provided, use default values.\n\t\t\t\t\t\tpatch_v[y][x].rgba[0] =\n\t\t\t\t\t\tpatch_v[y][x].rgba[1] =\n\t\t\t\t\t\tpatch_v[y][x].rgba[2] =\n\t\t\t\t\t\tpatch_v[y][x].rgba[3] = 1.0;\n\t\t\t\t\t}\n\n\t\t\t\t\tentities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL);\n\t\t\t\t\tif (strcmp(token, \")\")) {Con_Printf(CON_ERROR \"%s: invalid patch\\n\", mod->name);return false;}\n\n\t\t\t\t\tentities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL);\n\t\t\t\t\tif (x < countof(patch_v[y])-1)\n\t\t\t\t\t\tx++;\n\t\t\t\t}\n\t\t\t\tif (patchsz[0] < x)\n\t\t\t\t\tpatchsz[0] = x;\n\t\t\t\tif (strcmp(token, \")\")) {Con_Printf(CON_ERROR \"%s: invalid patch\\n\", mod->name);return false;}\n\t\t\t\tentities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL);\n\t\t\t\tif (y < countof(patch_v)-1)\n\t\t\t\t\ty++;\n\t\t\t}\n\t\t\tpatchsz[1] = y;\n\t\t\tif (strcmp(token, \")\")) {Con_Printf(CON_ERROR \"%s: invalid patch\\n\", mod->name);return false;}\n\t\t\tentities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL);\n\t\t\tif (strcmp(token, \"}\")) {Con_Printf(CON_ERROR \"%s: invalid patch\\n\", mod->name);return false;}\n\t\t\tcontinue;\n\t\t}\n\t\telse if (inbrush)\n\t\t{\n\t\t\t//parse a plane\n\t\t\t//Quake:             ( -0 -0 16 ) ( -0 -0 32 ) ( 64 -0 16 )                         texname soffset toffset     rotation sscale tscale\n\t\t\t//Hexen2:            ( -0 -0 16 ) ( -0 -0 32 ) ( 64 -0 16 )                         texname soffset toffset     rotation sscale tscale surfvalue\n\t\t\t//Valve:             ( -0 -0 16 ) ( -0 -0 32 ) ( 64 -0 16 )                         texname [x y z d] [x y z d] rotation sscale tscale\n\t\t\t//FTE  :             ( px py pz pd )                                                texname [x y z d] [x y z d] rotation sscale tscale contents surfflags surfvalue\n\t\t\t//Quake2:            ( -0 -0 16 ) ( -0 -0 32 ) ( 64 -0 16 )                         texname soffset toffset     rotation sscale tscale contents surfflags surfvalue\n\t\t\t//Quake3:            ( -0 -0 16 ) ( -0 -0 32 ) ( 64 -0 16 )                         texname soffset toffset     rotation sscale tscale detailfl surfflags surfvalue\n\t\t\t//Q3 BP: brushDef {  ( -0 -0 16 ) ( -0 -0 32 ) ( 64 -0 16 ) ( ( x y o ) ( x y o ) ) texname                                            detailfl surfflags surfvalue } //generate tangent+bitangent from the normal to generate base texcoords, then transform by the given 2*3 matrix. I prefer valve's way - it rotates more cleanly.\n\t\t\t//Doom3: brushDef3 { ( px py pz pd )                        ( ( x y o ) ( x y o ) ) texname                                            detailfl surfflags surfvalue }\n\t\t\t//hexen2's extra surfvalue is completely unused, and should normally be -1\n\t\t\t//q3 ignores all contents except detail, as well surfaceflags and surfacevalue\n\t\t\t//220 ignores rotation, provided only for UI info, scale is still used\n\n\t\t\t//we don't care whether the input is planes or points.\n\t\t\t//if we get a [ instead of an soffset then its\n\n\t\t\tbrushtex_t *bt;\n\t\t\tvec3_t d1,d2;\n\t\t\tvec3_t points[3];\n\t\t\tvec4_t texplane[2];\n\t\t\tfloat scale[2], rot;\n\t\t\tint p;\n\t\t\tenum\n\t\t\t{\n\t\t\t\tTEXTYPE_AXIAL,\t//urgh\n\t\t\t\tTEXTYPE_PLANES,\n\t\t\t\tTEXTYPE_BP,\t\t//weird 2d planes\n\t\t\t} textype = TEXTYPE_AXIAL;\n\t\t\tmemset(points, 0, sizeof(points));\n\t\t\tif (patch_tex)\n\t\t\t{\n\t\t\t\tCon_Printf(CON_ERROR \"%s: mixed patch+planes\\n\", mod->name);\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tfor (p = 0; p < 3; p++)\n\t\t\t{\n\t\t\t\tif (token[0] != '(' || token[1] != 0)\n\t\t\t\t\tbreak;\n\t\t\t\tentities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL);\n\t\t\t\tpoints[p][0] = atof(token);\n\t\t\t\tentities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL);\n\t\t\t\tpoints[p][1] = atof(token);\n\t\t\t\tentities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL);\n\t\t\t\tpoints[p][2] = atof(token);\n\t\t\t\tentities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL);\n\t\t\t\tif (token[0] != ')' || token[1] != 0)\n\t\t\t\t{\n//\t\t\t\t\tVectorClear(points[1]);\n//\t\t\t\t\tVectorClear(points[2]);\n\t\t\t\t\tpoints[1][0] = atof(token);\n\t\t\t\t\tentities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL);\n\t\t\t\t\tif (p == 0 && !strcmp(token, \")\"))\n\t\t\t\t\t\tp = 4;\t//we just managed to read an entire plane instead of 3 points.\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tentities = COM_ParseTokenOut(entities, \"()\", token, sizeof(token), NULL);\n\t\t\t}\n\t\t\tif (p < 3)\n\t\t\t{\n\t\t\t\tCon_Printf(CON_ERROR \"%s: malformed brush\\n\", mod->name);\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tif (numplanes == sizeof(planes)/sizeof(planes[0]))\n\t\t\t{\n\t\t\t\tCon_Printf(CON_ERROR \"%s: too many planes in brush\\n\", mod->name);\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tif (token[0] == '(')\n\t\t\t{\n\t\t\t\ttextype = TEXTYPE_BP;\n\t\t\t\tentities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL);\n\t\t\t\tif (token[0] == '(')\n\t\t\t\t{\n\t\t\t\t\tentities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL);\n\t\t\t\t\ttexplane[0][0] = atof(token);\n\t\t\t\t\tentities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL);\n\t\t\t\t\ttexplane[0][1] = atof(token);\n\t\t\t\t\tentities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL);\n\t\t\t\t\ttexplane[0][3] = atof(token);\n\t\t\t\t\tentities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL);\n\t\t\t\t\tif (token[0] != ')')\n\t\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\tentities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL);\n\t\t\t\tif (token[0] == '(')\n\t\t\t\t{\n\t\t\t\t\tentities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL);\n\t\t\t\t\ttexplane[1][0] = atof(token);\n\t\t\t\t\tentities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL);\n\t\t\t\t\ttexplane[1][1] = atof(token);\n\t\t\t\t\tentities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL);\n\t\t\t\t\ttexplane[1][3] = atof(token);\n\t\t\t\t\tentities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL);\n\t\t\t\t\tif (token[0] != ')')\n\t\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\tentities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL);\n\t\t\t\tif (token[0] != ')')\n\t\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tbt = Terr_Brush_FindTexture(subhm, token);\n\n\t\t\tif (textype != TEXTYPE_BP)\n\t\t\t{\n\t\t\t\t//halflife/valve220 format has the entire [x y z dist] plane specified.\n\t\t\t\tentities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL);\n\t\t\t\tif (*token == '[')\n\t\t\t\t{\n\t\t\t\t\ttextype = TEXTYPE_PLANES;\n\n\t\t\t\t\tentities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL);\n\t\t\t\t\ttexplane[0][0] = atof(token);\n\t\t\t\t\tentities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL);\n\t\t\t\t\ttexplane[0][1] = atof(token);\n\t\t\t\t\tentities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL);\n\t\t\t\t\ttexplane[0][2] = atof(token);\n\t\t\t\t\tentities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL);\n\t\t\t\t\ttexplane[0][3] = atof(token);\n\n\t\t\t\t\tentities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL);\n\t\t\t\t\t//]\n\t\t\t\t\tentities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL);\n\t\t\t\t\t//[\n\n\t\t\t\t\tentities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL);\n\t\t\t\t\ttexplane[1][0] = atof(token);\n\t\t\t\t\tentities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL);\n\t\t\t\t\ttexplane[1][1] = atof(token);\n\t\t\t\t\tentities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL);\n\t\t\t\t\ttexplane[1][2] = atof(token);\n\t\t\t\t\tentities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL);\n\t\t\t\t\ttexplane[1][3] = atof(token);\n\n\t\t\t\t\tentities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL);\n\t\t\t\t\t//]\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\t//vanilla quake\n\t\t\t\t\tVectorClear(texplane[0]);\n\t\t\t\t\tVectorClear(texplane[1]);\n\t\t\t\t\ttexplane[0][3] = atof(token);\t//aka soffset\n\t\t\t\t\tentities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL);\n\t\t\t\t\ttexplane[1][3] = atof(token);\t//aka toffset\n\t\t\t\t}\n\n\t\t\t\tentities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL);\n\t\t\t\trot = atof(token);\n\t\t\t\tentities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL);\n\t\t\t\tscale[0] = atof(token);\n\t\t\t\tentities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL);\n\t\t\t\tscale[1] = atof(token);\n\t\t\t}\n\t\t\telse rot = 0, scale[0] = 1, scale[1] = 1;\n\n\t\t\t//hexen2 has some extra junk that is useless - some 'light' value, but its never used and should normally be -1.\n\t\t\t//quake2/3 on the other hand has 3 different args. Contents SurfaceFlags SurfaceValue.\n\t\t\t//the SurfaceFlags and SurfaceVales are no longer used in q3 (shaders do it all), but contents is still partially used.\n\t\t\t//The contents conveys only CONTENTS_DETAIL. which is awkward as it varies somewhat by game, but we assume q2/q3.\n\t\t\tfaces[numplanes].surfaceflags = 0;\n\t\t\tfaces[numplanes].surfacevalue = 0;\n\n\t\t\twhile (*entities == ' ' || *entities == '\\t')\n\t\t\t\tentities++;\n\t\t\tif (*entities == '-' || (*entities >= '0' && *entities <= '9'))\n\t\t\t{\n\t\t\t\tint ex1, ex2 = 0, ex3 = 0;\n\t\t\t\tentities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL);\n\t\t\t\tex1 = atoi(token);\n\n\t\t\t\twhile (*entities == ' ' || *entities == '\\t')\n\t\t\t\t\tentities++;\n\t\t\t\tif (*entities == '-' || (*entities >= '0' && *entities <= '9'))\n\t\t\t\t{\n\t\t\t\t\tentities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL);\n\t\t\t\t\tex2 = atoi(token);\n\t\t\t\t}\n\n\t\t\t\twhile (*entities == ' ' || *entities == '\\t')\n\t\t\t\t\tentities++;\n\t\t\t\tif (*entities == '-' || (*entities >= '0' && *entities <= '9'))\n\t\t\t\t{\n\t\t\t\t\tentities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL);\n\t\t\t\t\tex3 = atoi(token);\n\t\t\t\t\t//if we got this far, then its q3 format.\n\t\t\t\t\t//q3 is weird. the first extra arg is contents. but only the detail contents is used.\n\t\t\t\t\t//ex1 &= Q3CONTENTS_DETAIL;\n\t\t\t\t\tbrushcontents |= ex1;\n\n\t\t\t\t\t//propagate these, in case someone tries editing a q2bsp.\n\t\t\t\t\tfaces[numplanes].surfaceflags = ex2;\n\t\t\t\t\tfaces[numplanes].surfacevalue = ex3;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t//okay, that's all the actual parsing, now try to make sense of this plane.\n\t\t\tif (p == 4)\n\t\t\t{\t//parsed an actual plane\n\t\t\t\tVectorCopy(points[0], planes[numplanes]);\n\t\t\t\tplanes[numplanes][3] = points[1][0];\n\t\t\t}\n\t\t\telse\n\t\t\t{\t//parsed 3 points.\n\t\t\t\tVectorSubtract(points[0], points[1], d1);\n\t\t\t\tVectorSubtract(points[2], points[1], d2);\n\t\t\t\tCrossProduct(d1, d2, planes[numplanes]);\n\t\t\t\tVectorNormalize(planes[numplanes]);\n\t\t\t\tplanes[numplanes][3] = DotProduct(points[1], planes[numplanes]);\n\t\t\t}\n\t\t\tfaces[numplanes].tex = bt;\n\n\t\t\t/*\n\t\t\tshader_t *shader = R_RegisterCustom(NULL, bt->shadername, SUF_LIGHTMAP, NULL, NULL);\n\t\t\tif (shader)\n\t\t\t{\n\t\t\t\tbrushcontents &= Q3CONTENTS_DETAIL;\n\t\t\t\tbrushcontents |= shader->contentbits&~Q3CONTENTS_DETAIL;\n\t\t\t\tfaces[numplanes].surfaceflags = shader->surfacebits;\n\t\t\t}\n\t\t\telse\n\t\t\t*/\n\t\t\tif (bt && !numplanes)\n\t\t\t{\n\t\t\t\tif (*bt->shadername == '*')\n\t\t\t\t{\n\t\t\t\t\tif (!Q_strncasecmp(bt->shadername, \"*lava\", 5))\n\t\t\t\t\t\tbrushcontents |= FTECONTENTS_LAVA;\n\t\t\t\t\telse if (!Q_strncasecmp(bt->shadername, \"*slime\", 5))\n\t\t\t\t\t\tbrushcontents |= FTECONTENTS_SLIME;\n\t\t\t\t\telse\n\t\t\t\t\t\tbrushcontents |= FTECONTENTS_WATER;\n\t\t\t\t}\n\t\t\t\telse if (!Q_strncasecmp(bt->shadername, \"*sky\", 4))\n\t\t\t\t\tbrushcontents |= FTECONTENTS_SKY;\n\t\t\t\telse if (!Q_strcasecmp(bt->shadername, \"clip\"))\n\t\t\t\t\tbrushcontents |= FTECONTENTS_PLAYERCLIP|FTECONTENTS_MONSTERCLIP;\n\t\t\t\telse if (!Q_strcasecmp(bt->shadername, \"hint\"))\n\t\t\t\t\tbrushcontents |= 0;\n\t\t\t\telse if (!Q_strcasecmp(bt->shadername, \"skip\"))\t//skip should not force content values if paired with lava etc.\n\t\t\t\t\t;//brushcontents = 0;\n\t\t\t\telse\n\t\t\t\t\tbrushcontents |= FTECONTENTS_SOLID;\n\t\t\t}\n\n\t\t\tif (textype == TEXTYPE_BP)\n\t\t\t{\n\t\t\t\tfloat *norm = planes[numplanes];\n\t\t\t\tfloat RotY = -atan2(norm[2], sqrt(norm[1]*norm[1] + norm[0]*norm[0]));\n\t\t\t\tfloat RotZ = atan2(norm[1], norm[0]);\n\t\t\t\tvec3_t tx = {-sin(RotZ), cos(RotZ), 0};\t\t//tangent\n\t\t\t\tvec3_t ty = {-sin(RotY)*cos(RotZ), -sin(RotY)*sin(RotZ), -cos(RotY)};\t//bitangent\n\t\t\t\tvec2_t tms = {texplane[0][0],texplane[0][1]}, tmt = {texplane[1][0],texplane[1][1]};\t//bah, locals reuse suck\n\t\t\t\ttexplane[0][0] = (tx[0] * tms[0]) + (ty[0] * tms[1]);\t//multiply out some matricies\n\t\t\t\ttexplane[0][1] = (tx[1] * tms[0]) + (ty[1] * tms[1]);\n\t\t\t\ttexplane[0][2] = (tx[2] * tms[0]) + (ty[2] * tms[1]);\n\t\t\t\ttexplane[1][0] = (tx[0] * tmt[0]) + (ty[0] * tmt[1]);\n\t\t\t\ttexplane[1][1] = (tx[1] * tmt[0]) + (ty[1] * tmt[1]);\n\t\t\t\ttexplane[1][2] = (tx[2] * tmt[0]) + (ty[2] * tmt[1]);\n\n\t\t\t\t//scale is part of the matrix.\n\t\t\t\tscale[0] = 1;\n\t\t\t\tscale[1] = 1;\n\n\t\t\t\t//FIXME: these faces should NOT be scaled by the texture's size!\n\t\t\t}\n\t\t\telse if (textype == TEXTYPE_PLANES)\n\t\t\t\t;//texture planes were properly loaded above (the scaling below is still needed though).\n\t\t\telse if (textype == TEXTYPE_AXIAL)\n\t\t\t{\t//quake's .maps use the normal to decide which texture directions to use in some lame axially-aligned way.\n\t\t\t\tfloat a=fabs(planes[numplanes][0]),b=fabs(planes[numplanes][1]),c=fabs(planes[numplanes][2]);\n\t\t\t\tif (a>=b&&a>=c)\n\t\t\t\t\ttexplane[0][1] = 1;\n\t\t\t\telse\n\t\t\t\t\ttexplane[0][0] = 1;\n\t\t\t\tif (c>a&&c>b)\n\t\t\t\t\ttexplane[1][1] = -1;\n\t\t\t\telse\n\t\t\t\t\ttexplane[1][2] = -1;\n\n\t\t\t\tif (rot)\n\t\t\t\t{\n\t\t\t\t\tint mas, mat;\n\t\t\t\t\tfloat s,t;\n\t\t\t\t\tfloat a = rot*(M_PI/180);\n\t\t\t\t\tfloat cosa = cos(a), sina=sin(a);\n\t\t\t\t\tfor (mas=0; mas<2&&!texplane[0][mas]; mas++);\n\t\t\t\t\tfor (mat=0; mat<2&&!texplane[1][mat]; mat++);\n\t\t\t\t\tfor (i = 0; i < 2; i++)\n\t\t\t\t\t{\n\t\t\t\t\t\ts = cosa*texplane[i][mas] - sina*texplane[i][mat];\n\t\t\t\t\t\tt = sina*texplane[i][mas] + cosa*texplane[i][mat];\n\t\t\t\t\t\ttexplane[i][mas] = s;\n\t\t\t\t\t\ttexplane[i][mat] = t;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (!scale[0]) scale[0] = 1;\n\t\t\tif (!scale[1]) scale[1] = 1;\n\t\t\tVectorScale(texplane[0], 1.0/scale[0], faces[numplanes].stdir[0]);\n\t\t\tVectorScale(texplane[1], 1.0/scale[1], faces[numplanes].stdir[1]);\n\t\t\tfaces[numplanes].stdir[0][3] = texplane[0][3];\n\t\t\tfaces[numplanes].stdir[1][3] = texplane[1][3];\n\n\t\t\tnumplanes++;\n\t\t\tcontinue;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t/*if (!strcmp(token, \"classname\"))\n\t\t\t\tentities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL);\n\t\t\telse*/\n\t\t\t\tentities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL);\n\t\t}\n\t\twhile(start < entities)\n\t\t\t*out++ = *start++;\n\t}\n\t*out = 0;\n\n\tMod_SetEntitiesString(mod, outstart, false);\n\n\tmod->numsubmodels = submodelnum;\n\n\treturn true;\n}\n\nqboolean QDECL Terr_LoadTerrainModel (model_t *mod, void *buffer, size_t bufsize)\n{\n\tint legacyterrain;\n\theightmap_t *hm;\n\n\tchar token[MAX_QPATH];\n\tint sectsize = 0;\n\tchar *src;\n\n\tsrc = COM_ParseOut(buffer, token, sizeof(token));\n\tif (!strcmp(token, \"terrain\"))\n\t{\n\t\tlegacyterrain = true;\n\t\tbuffer = src;\n\t}\n\telse if (!strcmp(token, \"{\"))\n\t\tlegacyterrain = false;\n\telse\n\t{\n\t\tCon_Printf(CON_ERROR \"%s wasn't terrain map\\n\", mod->name);\t//shouldn't happen\n\t\treturn false;\n\t}\n\n\tmod->type = mod_heightmap;\n\n\tClearBounds(mod->mins, mod->maxs);\n\n\thm = Z_Malloc(sizeof(*hm));\n\tClearLink(&hm->recycle);\n//\tClearLink(&hm->collected);\n\tCOM_FileBase(mod->name, hm->path, sizeof(hm->path));\n\n\tif (!Terr_ReformEntitiesLump(mod, hm, buffer))\n\t\treturn false;\n\n\tstrcpy(hm->groundshadername, \"terrainshader\");\n\tstrcpy(hm->skyname, \"sky1\");\n\n\thm->entitylock = Sys_CreateMutex();\n\thm->sectionsize = sectsize;\n\tif (legacyterrain)\n\t{\n\t\thm->firstsegx = -1;\n\t\thm->firstsegy = -1;\n\t\thm->maxsegx = +1;\n\t\thm->maxsegy = +1;\n\t}\n\telse\n\t{\n\t\thm->firstsegx = 0;\n\t\thm->firstsegy = 0;\n\t\thm->maxsegx = 0;\n\t\thm->maxsegy = 0;\n\t}\n\thm->legacyterrain = legacyterrain;\n\tif (legacyterrain)\n\t\thm->exteriorcontents = FTECONTENTS_SOLID;\t//sky outside the map\n\n\tTerr_ParseEntityLump(mod, hm);\n\n\tif (hm->firstsegx != hm->maxsegx)\n\t{\n\t\tvec3_t point;\n\t\tpoint[0] = (hm->firstsegx - CHUNKBIAS) * hm->sectionsize;\n\t\tpoint[1] = (hm->firstsegy - CHUNKBIAS) * hm->sectionsize;\n\t\tpoint[2] = -999999999999999999999999.f;\n\t\tAddPointToBounds(point, mod->mins, mod->maxs);\n\t\tpoint[0] = (hm->maxsegx - CHUNKBIAS) * hm->sectionsize;\n\t\tpoint[1] = (hm->maxsegy - CHUNKBIAS) * hm->sectionsize;\n\t\tpoint[2] = 999999999999999999999999.f;\n\t\tAddPointToBounds(point, mod->mins, mod->maxs);\n\t}\n\n\tmod->funcs.NativeTrace\t\t\t= Heightmap_Trace_Test;\n\tmod->funcs.PointContents\t\t= Heightmap_PointContents;\n\n\tmod->funcs.NativeContents\t\t= Heightmap_NativeBoxContents;\n\n\tmod->funcs.LightPointValues\t\t= Heightmap_LightPointValues;\n\tmod->funcs.StainNode\t\t\t= Heightmap_StainNode;\n\tmod->funcs.MarkLights\t\t\t= Heightmap_MarkLights;\n\n\tmod->funcs.ClusterForPoint\t\t= Heightmap_ClusterForPoint;\n\tmod->funcs.ClusterPVS\t\t\t= Heightmap_ClusterPVS;\n\n#ifdef HAVE_SERVER\n\tmod->funcs.FindTouchedLeafs\t\t= Heightmap_FindTouchedLeafs;\n\tmod->funcs.EdictInFatPVS\t\t= Heightmap_EdictInFatPVS;\n\tmod->funcs.FatPVS\t\t\t\t= Heightmap_FatPVS;\n#endif\n/*\tmod->hulls[0].funcs.HullPointContents = Heightmap_PointContents;\n\tmod->hulls[1].funcs.HullPointContents = Heightmap_PointContents;\n\tmod->hulls[2].funcs.HullPointContents = Heightmap_PointContents;\n\tmod->hulls[3].funcs.HullPointContents = Heightmap_PointContents;\n*/\n\tmod->pvsbytes = sizeof(hmpvs_t);\n\n\tmod->terrain = hm;\n\n#ifdef RUNTIMELIGHTING\n\tif (hm->relightcontext)\n\t{\n\t\tLightReloadEntities(hm->relightcontext, Mod_GetEntitiesString(mod), true);\n\t\thm->entsdirty = false;\n\t}\n#endif\n\n\tvalidatelinks(&hm->recycle);\n\treturn true;\n}\n\nvoid *Mod_LoadTerrainInfo(model_t *mod, char *loadname, qboolean force)\n{\n\theightmap_t *hm;\n\theightmap_t potential;\n\tif (!Mod_GetEntitiesString(mod))\n\t\treturn NULL;\n\n\tmemset(&potential, 0, sizeof(potential));\n\tTerr_ParseEntityLump(mod, &potential);\n\n\tif (potential.firstsegx >= potential.maxsegx || potential.firstsegy >= potential.maxsegy)\n\t{\n\t\t//figure out the size such that it encompases the entire bsp.\n\t\tpotential.firstsegx = floor(mod->mins[0] / potential.sectionsize) + CHUNKBIAS;\n\t\tpotential.firstsegy = floor(mod->mins[1] / potential.sectionsize) + CHUNKBIAS;\n\t\tpotential.maxsegx = ceil(mod->maxs[0] / potential.sectionsize) + CHUNKBIAS;\n\t\tpotential.maxsegy = ceil(mod->maxs[1] / potential.sectionsize) + CHUNKBIAS;\n\t\tif (*loadname=='*')\n\t\t{\n\t\t\tpotential.firstsegx = bound(0, potential.firstsegx, CHUNKLIMIT);\n\t\t\tpotential.firstsegy = bound(0, potential.firstsegy, CHUNKLIMIT);\n\t\t\tpotential.maxsegx = bound(potential.firstsegx, potential.maxsegx, CHUNKLIMIT);\n\t\t\tpotential.maxsegy = bound(potential.firstsegx, potential.maxsegy, CHUNKLIMIT);\n\t\t}\n\t\telse\n\t\t{//bound it, such that 0 0 will always be loaded.\n\t\t\tpotential.firstsegx = bound(0, potential.firstsegx, CHUNKBIAS);\n\t\t\tpotential.firstsegy = bound(0, potential.firstsegy, CHUNKBIAS);\n\t\t\tpotential.maxsegx = bound(CHUNKBIAS+1, potential.maxsegx, CHUNKLIMIT);\n\t\t\tpotential.maxsegy = bound(CHUNKBIAS+1, potential.maxsegy, CHUNKLIMIT);\n\t\t}\n\n\t\tif (!force)\n\t\t{\n\t\t\tchar sect[MAX_QPATH];\n\t\t\tQ_snprintfz(sect, sizeof(sect), \"maps/%s/sect_%03x_%03x.hms\", loadname, potential.firstsegx + (potential.maxsegx-potential.firstsegx)/2, potential.firstsegy + (potential.maxsegy-potential.firstsegy)/2);\n\t\t\tif (!COM_FCheckExists(sect))\n\t\t\t{\n\t\t\t\tQ_snprintfz(sect, sizeof(sect), \"maps/%s/block_00_00.hms\", loadname);\n\t\t\t\tif (!COM_FCheckExists(sect))\n\t\t\t\t\treturn NULL;\n\t\t\t}\n\t\t}\n\t}\n\n\thm = Z_Malloc(sizeof(*hm));\n\t*hm = potential;\n\thm->entitylock = Sys_CreateMutex();\n\tClearLink(&hm->recycle);\n\tQ_strncpyz(hm->path, loadname, sizeof(hm->path));\n\tQ_strncpyz(hm->groundshadername, \"terrainshader\", sizeof(hm->groundshadername));\n\n\thm->exteriorcontents = FTECONTENTS_EMPTY;\t//bsp geometry outside the heightmap\n\n\treturn hm;\n}\n\n#ifdef HAVE_CLIENT\n#if 0 //not yet ready\nstruct ted_import_s\n{\n\tsize_t x, y;\n\tsize_t width;\n\tsize_t height;\n\tunsigned short *data;\n};\n//static void ted_itterate(heightmap_t *hm, int distribution, float *pos, float radius, float strength, int steps,\nstatic void ted_import_heights_r16(void *vctx, hmsection_t *s, int idx, float wx, float wy, float strength)\n{\n\tstruct ted_import_s *ctx = vctx;\n\tunsigned int y = idx/SECTHEIGHTSIZE;\n\tunsigned int x = idx%SECTHEIGHTSIZE;\n\tx += s->sx*(SECTHEIGHTSIZE-1) - ctx->x;\n\ty += s->sy*(SECTHEIGHTSIZE-1) - ctx->y;\n\tif (x >= ctx->width || y >= ctx->height)\n\t\treturn;\n\ts->flags |= TSF_NOTIFY|TSF_EDITED|TSF_DIRTY|TSF_RELIGHT;\n\ts->heights[idx] = ctx->data[x + y*ctx->width] * (8192.0/(1<<16));\n}\nstatic void Mod_Terrain_Import_f(void)\n{\n\tmodel_t *mod;\n\tstruct ted_import_s ctx;\n\tconst char *mapname = Cmd_Argv(1);\n\tconst char *filename;\n\tsize_t fsize;\n\theightmap_t *hm;\n\tvec3_t pos = {0};\n\tif (Cmd_IsInsecure())\n\t{\n\t\tCon_Printf(\"Please use this command via the console\\n\");\n\t\treturn;\n\t}\n\tif (*mapname)\n\t\tmod = NULL;//Mod_FindName(va(\"maps/%s\", mapname));\n\telse\n\t\tmod = cl.worldmodel;\n\tif (!mod || mod->type == mod_dummy)\n\t\treturn;\n\thm = mod->terrain;\n\tif (!hm)\n\t\treturn;\n\n\tfsize = 0;\n\tfilename = va(\"maps/%s.r16\", mapname);\n\tctx.data = (void*)FS_LoadMallocFile(filename, &fsize);\n\tif (!ctx.data)\n\t{\n\t\tCon_Printf(\"Unable to read %s\\n\", filename);\n\t\treturn;\n\t}\n\tctx.width = ctx.height = sqrt(fsize/2);\n\tctx.x = 0;\n\tctx.y = 0;\n\tpos[0] += hm->sectionsize * CHUNKBIAS;\n\tpos[1] += hm->sectionsize * CHUNKBIAS;\n\tif (fsize == ctx.width*ctx.height*2)\n\t\tted_itterate(hm, tid_flat, pos, max(ctx.width, ctx.height), 1, SECTHEIGHTSIZE, ted_import_heights_r16, &ctx);\n\tFS_FreeFile(ctx.data);\n}\nstatic void Mod_Terrain_Export_f(void)\n{\n\tmodel_t *mod;\n\tstruct ted_import_s ctx;\n\tchar mapname[MAX_QPATH];\n\tconst char *filename;\n\theightmap_t *hm;\n\tsize_t w, h;\n\tsize_t tx, ty;\n\tsize_t sx, sy;\n\tunsigned int outtilex=0,outtiley=0;\n\tqboolean populated;\n\tif (Cmd_IsInsecure())\n\t{\n\t\tCon_Printf(\"Please use this command via the console\\n\");\n\t\treturn;\n\t}\n\tif (*Cmd_Argv(1))\n\t\tmod = NULL;//Mod_FindName(va(\"maps/%s\", mapname));\n\telse\n\t\tmod = cl.worldmodel;\n\tif (!mod || mod->type == mod_dummy)\n\t\treturn;\n\thm = mod->terrain;\n\tif (!hm)\n\t\treturn;\n\n\tCOM_StripExtension(mod->name, mapname, sizeof(mapname));\n\n\tctx.x = hm->firstsegx * (SECTHEIGHTSIZE-1);\n\tw = (hm->maxsegx-hm->firstsegx) * (SECTHEIGHTSIZE-1) + 1;\n\twhile(w)\n\t{\n\t\tctx.width = w;\n\t\tif (ctx.width > 2048+1)\n\t\t\tctx.width = 2048;\n\n\t\touttiley = 0;\n\t\tctx.y = hm->firstsegy * (SECTHEIGHTSIZE-1);\n\t\th = (hm->maxsegy-hm->firstsegy) * (SECTHEIGHTSIZE-1) + 1;\n\t\twhile(h)\n\t\t{\n\t\t\tctx.height = h;\n\t\t\tif (ctx.height > 2048+1)\n\t\t\t\tctx.height = 2048;\n\n\t\t\tpopulated = false;\n\t\t\tctx.data = Z_Malloc(ctx.width*ctx.height*2);\n\t\t\tfor (sy = ctx.y/(SECTHEIGHTSIZE-1); sy < (ctx.y+ctx.height + SECTHEIGHTSIZE-3)/(SECTHEIGHTSIZE-1); sy++)\n\t\t\tfor (sx = ctx.x/(SECTHEIGHTSIZE-1); sx < (ctx.x+ctx.width  + SECTHEIGHTSIZE-3)/(SECTHEIGHTSIZE-1); sx++)\n\t\t\t{\n\t\t\t\thmsection_t *s = Terr_GetSection(hm, sx, sy, TGS_WAITLOAD|TGS_ANYSTATE);\n\t\t\t\tif (s->loadstate == TSLS_FAILED)\n\t\t\t\t{\t//we're doing this weirdly so we can destroy sections as we go.\n\t\t\t\t\tTerr_DestroySection(hm, s, true);\n\t\t\t\t\ts = NULL;\n\t\t\t\t}\n\t\t\t\tif (s)\n\t\t\t\t{\n\t\t\t\t\tpopulated = true;\n\t\t\t\t\tfor (ty = 0; ty < SECTHEIGHTSIZE; ty++)\n\t\t\t\t\t{\n\t\t\t\t\t\tsize_t y = sy*(SECTHEIGHTSIZE-1)+ty - ctx.y;\n\t\t\t\t\t\tif (y >= ctx.height)\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\tfor (tx = 0; tx < SECTHEIGHTSIZE; tx++)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tsize_t x = sx*(SECTHEIGHTSIZE-1)+tx - ctx.x;\n\t\t\t\t\t\t\tif (x >= ctx.width)\n\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\tctx.data[x + y*ctx.width] = s->heights[tx+y*SECTHEIGHTSIZE] / (8192.0/(1<<16));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif (!(s->flags & TSF_EDITED))\n\t\t\t\t\t\tTerr_DestroySection(hm, s, true);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tfor (ty = 0; ty < SECTHEIGHTSIZE; ty++)\n\t\t\t\t\t{\n\t\t\t\t\t\tsize_t y = sy*(SECTHEIGHTSIZE-1)+ty - ctx.y;\n\t\t\t\t\t\tif (y >= ctx.height)\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\tfor (tx = 0; tx < SECTHEIGHTSIZE; tx++)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tsize_t x = sx*(SECTHEIGHTSIZE-1)+tx - ctx.x;\n\t\t\t\t\t\t\tif (x >= ctx.width)\n\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\tctx.data[x + y*ctx.width] = hm->defaultgroundheight / (8192.0/(1<<16));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tfilename = va(\"%s/x%u_y%u.r16\", mapname, outtilex, outtiley);\n\t\t\tif (populated)\n\t\t\t{\n\t\t\t\tif (FS_WriteFile(filename, ctx.data, ctx.width*ctx.height*2, FS_GAMEONLY))\n\t\t\t\t{\n\t\t\t\t\tchar sysname[1024];\n\t\t\t\t\tFS_NativePath(filename, FS_GAMEONLY, sysname, sizeof(sysname));\n\t\t\t\t\tCon_Printf(\"Wrote %s\\n\", sysname);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tCon_Printf(\"Unable to write %s\\n\", filename);\n\t\t\t}\n\t\t\telse\n\t\t\t\tCon_Printf(\"Skipping unpopulated %s\\n\", filename);\n\t\t\tZ_Free(ctx.data);\n\n\t\t\touttiley++;\n\t\t\tctx.y += ctx.height;\n\t\t\th -= ctx.height;\n\t\t}\n\t\touttilex++;\n\t\tctx.x += ctx.width;\n\t\tw -= ctx.width;\n\t}\n}\n#endif\n\nvoid Mod_Terrain_Create_f(void)\n{\n\tint x,y;\n\thmsection_t *s;\n\theightmap_t *hm;\n\tchar *mname;\n\tchar *mapdesc;\n\tchar *skyname;\n\tchar *groundname;\n\tchar *watername;\n\tchar *groundheight;\n\tchar *waterheight;\n\tchar *seed;\n\tvfsfile_t *file;\n\tmodel_t mod;\n\tmemset(&mod, 0, sizeof(mod));\n\tif (Cmd_Argc() < 2)\n\t{\n\t\tCon_Printf(\"%s: NAME \\\"DESCRIPTION\\\" SKYNAME DEFAULTGROUNDTEX DEFAULTHEIGHT DEFAULTWATER DEFAULTWATERHEIGHT seed\\nGenerates a fresh maps/foo.hmp file. You may wish to edit it with notepad later to customise it. You will need csaddon.dat in order to edit the actual terrain.\\n\", Cmd_Argv(0));\n\t\treturn;\n\t}\n\n\tmapdesc = Cmd_Argv(2); if (!*mapdesc) mapdesc = Cmd_Argv(1);\n\tskyname = Cmd_Argv(3);\n\tgroundname = Cmd_Argv(4);\n\tgroundheight = Cmd_Argv(5);\n\twatername = Cmd_Argv(6);\n\twaterheight = Cmd_Argv(7);\n\tseed = Cmd_Argv(7);\n\tMod_SetEntitiesString(&mod, va(\n\t\t\"{\\n\"\n\t\t\t\"classname \\\"worldspawn\\\"\\n\"\n\t\t\t\"message \\\"%s\\\"\\n\"\n\t\t\t\"_sky \\\"%s\\\"\\n\"\n\t\t\t\"_fog 0.02\\n\"\n\t\t\t\"_maxdrawdist 0 /*overrides fog distance (if greater)*/\\n\"\n\t\t\t\"_segmentsize 1024 /*how big each section is. this affects texturing and resolutions*/\\n\"\n\t\t\t\"_minxsegment -2048\\n\"\n\t\t\t\"_minysegment -2048\\n\"\n\t\t\t\"_maxxsegment 2048\\n\"\n\t\t\t\"_maxysegment 2048\\n\"\n\t\t\t\"_seed \\\"%s\\\" /*for auto-gen plugins*/\\n\"\n\t\t\t\"_exterior solid\\n\"\n\t\t\t\"_defaultgroundtexture \\\"%s\\\"\\n\"\n\t\t\t\"_defaultgroundheight \\\"%s\\\"\\n\"\n\t\t\t\"_defaultwatertexture \\\"%s\\\"\\n\"\n\t\t\t\"_defaultwaterheight \\\"%s\\\"\\n\"\t//hurrah, sea level.\n//\t\t\t\"_tiles 64 64 8 8\\n\"\n\t\t\"}\\n\"\n\t\t\"{\\n\"\n\t\t\t\"classname info_player_start\\n\"\n\t\t\t\"origin \\\"0 0 1024\\\" /*EDITME*/\\n\"\n\t\t\"}\\n\"\n\t\t\"/*ADD EXTRA ENTITIES!*/\\n\"\n\t\t, mapdesc\n\t\t,*skyname?skyname:\"terrsky1\", seed\n\t\t,*groundname?groundname:\"ground1_1\"\n\t\t,*groundheight?groundheight:\"-1024\"\n\t\t,*watername?watername:\"*water2\"\n\t\t,*waterheight?waterheight:\"0\"\n\t\t), true);\n\n\tmod.type = mod_heightmap;\n\tmod.terrain = hm = Z_Malloc(sizeof(*hm));\n\tTerr_ParseEntityLump(&mod, hm);\n\thm->entitylock = Sys_CreateMutex();\n\tClearLink(&hm->recycle);\n\tQ_strncpyz(hm->path, Cmd_Argv(1), sizeof(hm->path));\n\tQ_strncpyz(hm->groundshadername, \"terrainshader\", sizeof(hm->groundshadername));\n\thm->exteriorcontents = FTECONTENTS_SOLID;\n\n\n\tfor (x = CHUNKBIAS-1; x < CHUNKBIAS+1; x++)\n\t\tfor (y = CHUNKBIAS-1; y < CHUNKBIAS+1; y++)\n\t\t\tTerr_GetSection(hm, x, y, TGS_TRYLOAD|TGS_DEFAULTONFAIL);\n\n\tfor (x = CHUNKBIAS-1; x < CHUNKBIAS+1; x++)\n\t\tfor (y = CHUNKBIAS-1; y < CHUNKBIAS+1; y++)\n\t\t{\n\t\t\ts = Terr_GetSection(hm, x, y, TGS_WAITLOAD|TGS_DEFAULTONFAIL);\n\t\t\tif (s && (s->flags & (TSF_EDITED|TSF_DIRTY)))\n\t\t\t{\n\t\t\t\tTerr_InitLightmap(s, false);\n\t\t\t\tTerr_SaveSection(hm, s, x, y, true);\n\t\t\t}\n\t\t}\n\n\tmname = va(\"maps/%s.hmp\", Cmd_Argv(1));\n\tif (COM_FCheckExists(mname))\n\t{\n\t\tCon_Printf(\"%s: already exists, not overwriting.\\n\", mname);\n\t\treturn;\n\t}\n\tFS_CreatePath(mname, FS_GAMEONLY);\n\tfile = FS_OpenVFS(mname, \"wb\", FS_GAMEONLY);\n\tif (!file)\n\t\tCon_TPrintf(\"unable to open %s\\n\", mname);\n\telse\n\t{\n\t\tTerr_WriteMapFile(file, &mod);\n\t\tVFS_CLOSE(file);\n\t\tCon_TPrintf(\"Wrote %s\\n\", mname);\n\t\tFS_FlushFSHashWritten(mname);\n\t}\n\tMod_SetEntitiesString(&mod, NULL, false);\n\tTerr_FreeModel(&mod);\n}\n#endif\n//reads in the terrain a tile at a time, and writes it out again.\n//the new version will match our current format version.\n//this is mostly so I can strip out old format revisions...\n#ifdef HAVE_CLIENT\nvoid Mod_Terrain_Convert_f(void)\n{\n\tmodel_t *mod;\n\theightmap_t *hm;\n\tif (Cmd_FromGamecode())\n\t\treturn;\n\n\tif (Cmd_Argc() >= 2)\n\t\tmod = Mod_FindName(va(\"maps/%s.hmp\", Cmd_Argv(1)));\n\telse if (cls.state)\n\t\tmod = cl.worldmodel;\n\telse\n\t\tmod = NULL;\n\tif (!mod || mod->type == mod_dummy)\n\t\treturn;\n\thm = mod->terrain;\n\tif (!hm)\n\t\treturn;\n\n\t{\n\t\tchar *texkill = Cmd_Argv(2);\n\t\thmsection_t *s;\n\t\tint x, sx;\n\t\tint y, sy;\n\n\t\twhile(Terr_Collect(hm))\t//collect as many as we can now, so when we collect later, the one that's collected is fresh.\n\t\t\t;\n\t\tfor (y = hm->firstsegy; y < hm->maxsegy; y+=SECTIONSPERBLOCK)\n\t\t{\n\t\t\tSys_Printf(\"%g%% complete\\n\", 100 * (y-hm->firstsegy)/(float)(hm->maxsegy-hm->firstsegy));\n\t\t\tfor (x = hm->firstsegx; x < hm->maxsegx; x+=SECTIONSPERBLOCK)\n\t\t\t{\n\t\t\t\tfor (sy = y; sy < y+SECTIONSPERBLOCK && sy < hm->maxsegy; sy++)\n\t\t\t\t{\n\t\t\t\t\tfor (sx = x; sx < x+SECTIONSPERBLOCK && sx < hm->maxsegx; sx++)\n\t\t\t\t\t{\n\t\t\t\t\t\ts = Terr_GetSection(hm, sx, sy, TGS_WAITLOAD|TGS_NODOWNLOAD|TGS_NORENDER);\n\t\t\t\t\t\tif (s)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (*texkill)\n\t\t\t\t\t\t\t\tted_texkill(s, texkill);\n\t\t\t\t\t\t\ts->flags |= TSF_EDITED;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tfor (sy = y; sy < y+SECTIONSPERBLOCK && sy < hm->maxsegy; sy++)\n\t\t\t\t{\n\t\t\t\t\tfor (sx = x; sx < x+SECTIONSPERBLOCK && sx < hm->maxsegx; sx++)\n\t\t\t\t\t{\n\t\t\t\t\t\ts = Terr_GetSection(hm, sx, sy, TGS_WAITLOAD|TGS_NODOWNLOAD|TGS_NORENDER);\n\t\t\t\t\t\tif (s)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (s->flags & TSF_EDITED)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tif (Terr_SaveSection(hm, s, sx, sy, true))\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ts->flags &= ~TSF_EDITED;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\twhile(Terr_Collect(hm))\n\t\t\t\t\t;\n\t\t\t}\n\t\t}\n\t\tSys_Printf(\"%g%% complete\\n\", 100.0f);\n\t}\n}\n#endif\nvoid Mod_Terrain_Reload_f(void)\n{\n\tmodel_t *mod;\n\theightmap_t *hm;\n\tif (Cmd_Argc() >= 2)\n\t\tmod = Mod_FindName(va(\"maps/%s.hmp\", Cmd_Argv(1)));\n#ifdef HAVE_CLIENT\n\telse if (cls.state)\n\t\tmod = cl.worldmodel;\n#endif\n\telse\n\t\tmod = NULL;\n\tif (!mod || mod->type == mod_dummy)\n\t\treturn;\n\thm = mod->terrain;\n\tif (!hm)\n\t\treturn;\n\n\tif (Cmd_Argc() >= 4)\n\t{\n\t\thmsection_t *s;\n\t\tint sx = atoi(Cmd_Argv(2)) + CHUNKBIAS;\n\t\tint sy = atoi(Cmd_Argv(3)) + CHUNKBIAS;\n\t\tif (hm)\n\t\t{\n\t\t\ts = Terr_GetSection(hm, sx, sy, TGS_NOLOAD);\n\t\t\tif (s)\n\t\t\t{\n\t\t\t\ts->flags |= TSF_NOTIFY;\n\t\t\t}\n\t\t}\n\t}\n\telse\n\t\tTerr_PurgeTerrainModel(mod, false, true);\n}\n\nplugterrainfuncs_t *Terr_GetTerrainFuncs(size_t structsize)\n{\n\tif (structsize != sizeof(plugterrainfuncs_t))\n\t\treturn NULL;\n#ifdef HAVE_CLIENT\n\treturn &terrainfuncs;\n#else\n\treturn NULL;\t//dedicated server builds have all the visual stuff stripped, which makes APIs too inconsistent. Generate then save. Or fix up the API...\n#endif\n}\n\nvoid Terr_Init(void)\n{\n\tterrainfuncs.GenerateWater = Terr_GenerateWater;\n\tterrainfuncs.InitLightmap = Terr_InitLightmap;\n\tterrainfuncs.AddMesh = Terr_AddMesh;\n\tterrainfuncs.GetLightmap = Terr_GetLightmap;\n\tterrainfuncs.GetSection = Terr_GetSection;\n\tterrainfuncs.GenerateSections = Terr_GenerateSections;\n\tterrainfuncs.FinishedSection = Terr_FinishedSection;\n\n\tCvar_Register(&mod_terrain_networked, \"Terrain\");\n\tCvar_Register(&mod_terrain_defaulttexture, \"Terrain\");\n\tCvar_Register(&mod_terrain_savever, \"Terrain\");\n\tCmd_AddCommand(\"mod_terrain_save\", Mod_Terrain_Save_f);\n\tCmd_AddCommand(\"mod_terrain_reload\", Mod_Terrain_Reload_f);\n#ifdef HAVE_CLIENT\n//\tCmd_AddCommandD(\"mod_terrain_export\", Mod_Terrain_Export_f, \"Export a raw heightmap\");\n//\tCmd_AddCommandD(\"mod_terrain_import\", Mod_Terrain_Import_f, \"Import a raw heightmap\");\n\tCmd_AddCommand(\"mod_terrain_create\", Mod_Terrain_Create_f);\n\tCmd_AddCommandD(\"mod_terrain_convert\", Mod_Terrain_Convert_f, \"mod_terrain_convert [mapname] [texkill]\\nConvert a terrain to the current format. If texkill is specified, only tiles with the named texture will be converted, and tiles with that texture will be stripped. This is a slow operation.\");\n\n\tCvar_Register(&mod_terrain_sundir, \"Terrain\");\n\tCvar_Register(&mod_terrain_ambient, \"Terrain\");\n\tCvar_Register(&mod_terrain_shadows, \"Terrain\");\n\tCvar_Register(&mod_terrain_shadow_dist, \"Terrain\");\n\tCvar_Register(&mod_terrain_brushlights, \"Terrain\");\n\tCvar_Register(&mod_terrain_brushtexscale, \"Terrain\");\n#endif\n\n\tMod_RegisterModelFormatText(NULL, \"FTE Heightmap Map (hmp)\", \"terrain\", Terr_LoadTerrainModel);\n\tMod_RegisterModelFormatText(NULL, \"Quake Map Format (map)\", \"{\", Terr_LoadTerrainModel);\n}\n#endif\n"
  },
  {
    "path": "engine/gl/gl_hlmdl.c",
    "content": "#include \"quakedef.h\"\n\n#ifdef HALFLIFEMODELS\n\n#include \"shader.h\"\n#include \"com_mesh.h\"\n/*\n +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n\tHalf-Life Model Renderer (Experimental) Copyright (C) 2001 James 'Ender' Brown [ender@quakesrc.org] This program is\n\tfree software; you can redistribute it and/or modify it under the terms of the GNU General Public License as\n\tpublished by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.\n\tThis program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied\n\twarranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more\n\tdetails. You should have received a copy of the GNU General Public License along with this program; if not, write\n\tto the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. fromquake.h -\n\n\trender.c - apart from calculations (mostly range checking or value conversion code is a mix of standard Quake 1\n\tmeshing, and vertex deforms. The rendering loop uses standard Quake 1 drawing, after SetupBones deforms the vertex.\n +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n Note: this code has since been greatly modified to fix skin, submodels, hitboxes, attachments, etc.\n\n\n\n  Also, please note that it won't do all hl models....\n  Nor will it work 100%\n */\n\nqboolean HLMDL_Trace\t\t(struct model_s *model, int hulloverride, const framestate_t *framestate, const vec3_t axis[3], const vec3_t p1, const vec3_t p2, const vec3_t mins, const vec3_t maxs, qboolean capsule, unsigned int against, struct trace_s *trace);\nunsigned int HLMDL_Contents\t(struct model_s *model, int hulloverride, const framestate_t *framestate, const vec3_t axis[3], const vec3_t p, const vec3_t mins, const vec3_t maxs);\n\nvoid QuaternionGLMatrix(float x, float y, float z, float w, vec4_t *GLM)\n{\n\tGLM[0][0] = 1 - 2 * y * y - 2 * z * z;\n\tGLM[1][0] = 2 * x * y + 2 * w * z;\n\tGLM[2][0] = 2 * x * z - 2 * w * y;\n\tGLM[0][1] = 2 * x * y - 2 * w * z;\n\tGLM[1][1] = 1 - 2 * x * x - 2 * z * z;\n\tGLM[2][1] = 2 * y * z + 2 * w * x;\n\tGLM[0][2] = 2 * x * z + 2 * w * y;\n\tGLM[1][2] = 2 * y * z - 2 * w * x;\n\tGLM[2][2] = 1 - 2 * x * x - 2 * y * y;\n}\n\n/*\n =======================================================================================================================\n\tQuaternionGLAngle - Convert a GL angle to a quaternion matrix\n =======================================================================================================================\n */\nvoid QuaternionGLAngle(const vec3_t angles, vec4_t quaternion)\n{\n\t/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/\n\tfloat\tyaw = angles[2] * 0.5;\n\tfloat\tpitch = angles[1] * 0.5;\n\tfloat\troll = angles[0] * 0.5;\n\tfloat\tsiny = sin(yaw);\n\tfloat\tcosy = cos(yaw);\n\tfloat\tsinp = sin(pitch);\n\tfloat\tcosp = cos(pitch);\n\tfloat\tsinr = sin(roll);\n\tfloat\tcosr = cos(roll);\n\t/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/\n\n\tquaternion[0] = sinr * cosp * cosy - cosr * sinp * siny;\n\tquaternion[1] = cosr * sinp * cosy + sinr * cosp * siny;\n\tquaternion[2] = cosr * cosp * siny - sinr * sinp * cosy;\n\tquaternion[3] = cosr * cosp * cosy + sinr * sinp * siny;\n}\n\nmatrix3x4 transform_matrix[MAX_BONES];\t/* Vertex transformation matrix */\n\n#ifndef SERVERONLY\nvoid GL_Draw_HL_AliasFrame(short *order, vec3_t *transformed, float tex_w, float tex_h);\n\nstruct hlvremaps\n{\n\tunsigned short vertidx;\n\tunsigned short normalidx;\n\tunsigned short scoord;\n\tunsigned short tcoord;\n};\nstatic index_t HLMDL_DeDupe(unsigned short *order, struct hlvremaps *rem, size_t *count, size_t first, size_t max)\n{\n\tsize_t i;\n\tfor (i = *count; i-- > first;)\n\t{\n\t\tif (rem[i].vertidx == order[0] && rem[i].normalidx == order[1] && rem[i].scoord == order[2] && rem[i].tcoord == order[3])\n\t\t\treturn i;\n\t}\n\ti = *count;\n\tif (i < max)\n\t{\n\t\trem[i].vertidx = order[0];\n\t\trem[i].normalidx = order[1];\n\t\trem[i].scoord = order[2];\n\t\trem[i].tcoord = order[3];\n\t}\n\t*count += 1;\n\treturn i;\n}\n\n//parse the vertex info, pull out what we can\nstatic void HLMDL_PrepareVerticies (model_t *mod, hlmodel_t *model)\n{\n\tstruct hlvremaps *uvert;\n\tsize_t uvertcount=0, uvertstart;\n\tunsigned short count;\n\tint i;\n\tsize_t idx = 0, m, maxidx=65536*3;\n\tsize_t maxverts = 65536;\n\n\tindex_t *index;\n\tmesh_t *mesh, *submesh;\n\n\tint body;\n\n\tuvert = malloc(sizeof(*uvert)*maxverts);\n\tindex = malloc(sizeof(byte_vec4_t)*maxidx);\n\n\tmodel->numgeomsets = model->header->numbodyparts;\n\tmodel->geomset = ZG_Malloc(&mod->memgroup, sizeof(*model->geomset) * model->numgeomsets);\n\tfor (body = 0; body < model->numgeomsets; body++)\n\t{\n\t\thlmdl_bodypart_t\t*bodypart = (hlmdl_bodypart_t *) ((qbyte *) model->header + model->header->bodypartindex) + body;\n\t\tint\t\t\t\t\tbodyindex;\n\t\tmodel->geomset[body].numalternatives = bodypart->nummodels;\n\t\tmodel->geomset[body].alternatives = ZG_Malloc(&mod->memgroup, sizeof(*model->geomset[body].alternatives) * bodypart->nummodels);\n\t\tfor (bodyindex = 0; bodyindex < bodypart->nummodels; bodyindex++)\n\t\t{\n\t\t\thlmdl_submodel_t\t\t*amodel = (hlmdl_submodel_t *) ((qbyte *) model->header + bodypart->modelindex) + bodyindex;\n\t\t\tstruct hlalternative_s *submodel;\n\n\t\t\tmodel->geomset[body].alternatives[bodyindex].numsubmeshes = amodel->nummesh;\n\t\t\tmodel->geomset[body].alternatives[bodyindex].submesh = ZG_Malloc(&mod->memgroup, sizeof(*model->geomset[body].alternatives[bodyindex].submesh) * amodel->nummesh);\n\n\t\t\tsubmodel = &model->geomset[body].alternatives[bodyindex];\n\n\t\t\tfor(m = 0; m < amodel->nummesh; m++)\n\t\t\t{\n\t\t\t\thlmdl_mesh_t\t*inmesh = (hlmdl_mesh_t *) ((qbyte *) model->header + amodel->meshindex) + m;\n\t\t\t\tunsigned short *order = (unsigned short *) ((qbyte *) model->header + inmesh->index);\n\n\t\t\t\tuvertstart = uvertcount;\n\t\t\t\tsubmodel->submesh[m].vbofirstvert = uvertstart;\n\t\t\t\tsubmodel->submesh[m].vbofirstelement = idx;\n\t\t\t\tsubmodel->submesh[m].numvertexes = 0;\n\t\t\t\tsubmodel->submesh[m].numindexes = 0;\n\n\t\t\t\tfor(;;)\n\t\t\t\t{\n\t\t\t\t\tcount = *order++;\t/* get the vertex count and primitive type */\n\t\t\t\t\tif(!count) break;\t/* done */\n\n\t\t\t\t\tif(count & 0x8000)\n\t\t\t\t\t{\t//fan\n\t\t\t\t\t\tint first = HLMDL_DeDupe(order+0*4, uvert, &uvertcount, uvertstart, maxverts);\n\t\t\t\t\t\tint prev = HLMDL_DeDupe(order+1*4, uvert, &uvertcount, uvertstart, maxverts);\n\t\t\t\t\t\tcount = (unsigned short)-(short)count;\n\t\t\t\t\t\tif (idx + (count-2)*3 > maxidx)\n\t\t\t\t\t\t\tbreak;\t//would overflow. fixme: extend\n\t\t\t\t\t\tfor (i = min(2,count); i < count; i++)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tindex[idx++] = first;\n\t\t\t\t\t\t\tindex[idx++] = prev;\n\t\t\t\t\t\t\tindex[idx++] = prev = HLMDL_DeDupe(order+i*4, uvert, &uvertcount, uvertstart, maxverts);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tint v0 = HLMDL_DeDupe(order+0*4, uvert, &uvertcount, uvertstart, maxverts);\n\t\t\t\t\t\tint v1 = HLMDL_DeDupe(order+1*4, uvert, &uvertcount, uvertstart, maxverts);\n\t\t\t\t\t\t//emit (count-2)*3 indicies as a strip\n\t\t\t\t\t\t//012 213, etc\n\t\t\t\t\t\tif (idx + (count-2)*3 > maxidx)\n\t\t\t\t\t\t\tbreak;\t//would overflow. fixme: extend\n\t\t\t\t\t\tfor (i = min(2,count); i < count; i++)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (i & 1)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tindex[idx++] = v1;\n\t\t\t\t\t\t\t\tindex[idx++] = v0;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tindex[idx++] = v0;\n\t\t\t\t\t\t\t\tindex[idx++] = v1;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tv0 = v1;\n\t\t\t\t\t\t\tindex[idx++] = v1 = HLMDL_DeDupe(order+i*4, uvert, &uvertcount, uvertstart, maxverts);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\torder += i*4;\n\t\t\t\t}\n\n\t\t\t\tif (uvertcount >= maxverts)\n\t\t\t\t{\n\t\t\t\t\t//if we're overflowing our verts, rewind, as we cannot generate this mesh. we'll just end up with a 0-index mesh, with no extra verts either\n\t\t\t\t\tuvertcount = uvertstart;\n\t\t\t\t\tidx = submodel->submesh[m].vbofirstelement;\n\t\t\t\t}\n\n\t\t\t\tsubmodel->submesh[m].numindexes = idx - submodel->submesh[m].vbofirstelement;\n\t\t\t\tsubmodel->submesh[m].numvertexes = uvertcount - uvertstart;\n\t\t\t}\n\t\t}\n\t}\n\n\tmesh = &model->mesh;\n\tmesh->indexes = ZG_Malloc(model->memgroup, sizeof(*mesh->indexes)*idx);\n\tmemcpy(mesh->indexes, index, sizeof(*index)*idx);\n\n\tmesh->colors4b_array = ZG_Malloc(model->memgroup, sizeof(*mesh->colors4b_array)*uvertcount);\n\tmesh->st_array = ZG_Malloc(model->memgroup, sizeof(*mesh->st_array)*uvertcount);\n\tmesh->lmst_array[0] = ZG_Malloc(model->memgroup, sizeof(*mesh->lmst_array[0])*uvertcount);\n\tmesh->xyz_array = ZG_Malloc(model->memgroup, sizeof(*mesh->xyz_array)*uvertcount);\n\tmesh->normals_array = ZG_Malloc(model->memgroup, sizeof(*mesh->normals_array)*uvertcount);\n\tmesh->bonenums = ZG_Malloc(model->memgroup, sizeof(*mesh->bonenums)*uvertcount);\n\tmesh->boneweights = ZG_Malloc(model->memgroup, sizeof(*mesh->boneweights)*uvertcount);\n#if defined(RTLIGHTS)\n\tmesh->snormals_array = ZG_Malloc(model->memgroup, sizeof(*mesh->snormals_array)*uvertcount);\n\tmesh->tnormals_array = ZG_Malloc(model->memgroup, sizeof(*mesh->tnormals_array)*uvertcount);\n#endif\n\n\tmesh->numindexes = idx;\n\tmesh->numvertexes = uvertcount;\n\n\tfor (body = 0; body < model->numgeomsets; body++)\n\t{\n\t\thlmdl_bodypart_t\t*bodypart = (hlmdl_bodypart_t *) ((qbyte *) model->header + model->header->bodypartindex) + body;\n\t\tint\t\t\t\t\tbodyindex;\n\t\tfor (bodyindex = 0; bodyindex < bodypart->nummodels; bodyindex++)\n\t\t{\n\t\t\thlmdl_submodel_t\t\t*amodel = (hlmdl_submodel_t *) ((qbyte *) model->header + bodypart->modelindex) + bodyindex;\n\n\t\t\tvec3_t *verts = (vec3_t *) ((qbyte *) model->header + amodel->vertindex);\n\t\t\tqbyte *bone = ((qbyte *) model->header + amodel->vertinfoindex);\n\t\t\tvec3_t *norms = (vec3_t *) ((qbyte *) model->header + amodel->normindex);\n\t\t\tsize_t iv, ov;\n\n\t\t\tstruct hlalternative_s *submodel = &model->geomset[body].alternatives[bodyindex];\n\t\t\tfor(m = 0; m < amodel->nummesh; m++)\n\t\t\t{\n\t\t\t\tsubmesh = &submodel->submesh[m];\n\n\t\t\t\tsubmesh->indexes\t\t= mesh->indexes\t\t\t+ submesh->vbofirstelement;\n\t\t\t\tsubmesh->colors4b_array\t= mesh->colors4b_array\t+ submesh->vbofirstvert;\n\t\t\t\tsubmesh->st_array\t\t= mesh->st_array\t\t+ submesh->vbofirstvert;\n\t\t\t\tsubmesh->lmst_array[0]\t= mesh->lmst_array[0]\t+ submesh->vbofirstvert;\n\t\t\t\tsubmesh->xyz_array\t\t= mesh->xyz_array\t\t+ submesh->vbofirstvert;\n\t\t\t\tsubmesh->normals_array\t= mesh->normals_array\t+ submesh->vbofirstvert;\n\t\t\t\tsubmesh->bonenums\t\t= mesh->bonenums\t\t+ submesh->vbofirstvert;\n\t\t\t\tsubmesh->boneweights\t= mesh->boneweights\t\t+ submesh->vbofirstvert;\n\n\t\t\t\t//prepare the verticies now that we have the mappings\n\t\t\t\tfor(ov = 0, iv = submesh->vbofirstvert; ov < submesh->numvertexes; ov++, iv++)\n\t\t\t\t{\n\t\t\t\t\tsubmesh->bonenums[ov][0] = submesh->bonenums[ov][1] = submesh->bonenums[ov][2] = submesh->bonenums[ov][3] = bone[uvert[iv].vertidx];\n\t\t\t\t\tVector4Set(submesh->boneweights[ov], 1, 0, 0, 0);\n\t\t\t\t\tVector4Set(submesh->colors4b_array[ov], 255, 255, 255, 255);\t//why bytes? why not?\n\n\t\t\t\t\tsubmesh->lmst_array[0][ov][0] = uvert[iv].scoord;\n\t\t\t\t\tsubmesh->lmst_array[0][ov][1] = uvert[iv].tcoord;\n\t\t\t\t\tVectorCopy(verts[uvert[iv].vertidx], submesh->xyz_array[ov]);\n\n\t\t\t\t\t//Warning: these models use different tables for vertex and normals.\n\t\t\t\t\t//this means they might be transformed by different bones. we ignore that and just assume that the normals will want the same bone.\n\t\t\t\t\tVectorCopy(norms[uvert[iv].normalidx], submesh->normals_array[ov]);\n\t\t\t\t}\n\n#if defined(RTLIGHTS)\n\t\t\t\t//treat this as the base pose, and calculate the sdir+tdir for bumpmaps.\n\t\t\t\tsubmesh->snormals_array = mesh->snormals_array + submesh->vbofirstvert;\n\t\t\t\tsubmesh->tnormals_array = mesh->tnormals_array + submesh->vbofirstvert;\n//\t\t\t\tR_Generate_Mesh_ST_Vectors(submesh);\n#endif\n\t\t\t}\n\t\t}\n\t}\n\n\t//scratch space...\n\tmesh->indexes = ZG_Malloc(model->memgroup, sizeof(*mesh->indexes)*idx);\n\n\t//don't need that mapping any more\n\tfree(uvert);\n\tfree(index);\n}\n#endif\n\n/*\n =======================================================================================================================\n\tMod_LoadHLModel - read in the model's constituent parts\n =======================================================================================================================\n */\nqboolean QDECL Mod_LoadHLModel (model_t *mod, void *buffer, size_t fsize)\n{\n#ifndef SERVERONLY\n\tint i, j;\n\tstruct hlmodelshaders_s *shaders;\n\thlmdl_tex_t\t*tex;\n\n\tlmalloc_t atlas;\n\tchar texname[MAX_QPATH];\n#endif\n\n\thlmodel_t *model;\n\thlmdl_header_t *header;\n\thlmdl_header_t *texheader;\n\thlmdl_bone_t\t*bones;\n\thlmdl_bonecontroller_t\t*bonectls;\n\tvoid *texmem = NULL;\n\n\n\t//load the model into hunk\n\tmodel = ZG_Malloc(&mod->memgroup, sizeof(hlmodel_t));\n\tmodel->memgroup = &mod->memgroup;\n\n\theader = ZG_Malloc(&mod->memgroup, fsize);\n\tmemcpy(header, buffer, fsize);\n\n#if defined(HLSERVER) && (defined(__powerpc__) || defined(__ppc__))\n//this is to let anyone who tries porting it know that there is a serious issue. And I'm lazy.\n#ifdef warningmsg\n#pragma warningmsg(\"-----------------------------------------\")\n#pragma warningmsg(\"FIXME: No byteswapping on halflife models\")\t//hah, yeah, good luck with that, you'll need it.\n#pragma warningmsg(\"-----------------------------------------\")\n#endif\n#endif\n\n\tif (header->version != 10)\n\t{\n\t\tCon_Printf(CON_ERROR \"Cannot load halflife model %s - unknown version %i\\n\", mod->name, header->version);\n\t\treturn false;\n\t}\n\n\tif (header->numcontrollers > MAX_BONE_CONTROLLERS)\n\t{\n\t\tCon_Printf(CON_ERROR \"Cannot load model %s - too many controllers %i\\n\", mod->name, header->numcontrollers);\n\t\treturn false;\n\t}\n\tif (header->numbones > MAX_BONES)\n\t{\n\t\tCon_Printf(CON_ERROR \"Cannot load model %s - too many bones %i\\n\", mod->name, header->numbones);\n\t\treturn false;\n\t}\n\n\ttexheader = NULL;\n\tif (!header->numtextures)\n\t{\n\t\tsize_t fz;\n\t\tchar texmodelname[MAX_QPATH];\n\t\tCOM_StripExtension(mod->name, texmodelname, sizeof(texmodelname));\n\t\tQ_strncatz(texmodelname, \"t.mdl\", sizeof(texmodelname));\n\t\t//no textures? eesh. They must be stored externally.\n\t\ttexheader = texmem = (hlmdl_header_t*)FS_LoadMallocFile(texmodelname, &fz);\n\t\tif (texheader)\n\t\t{\n\t\t\tif (texheader->version != 10)\n\t\t\t\ttexheader = NULL;\n\t\t}\n\t}\n\n\tif (!texheader)\n\t\ttexheader = header;\n\telse\n\t\theader->numtextures = texheader->numtextures;\n\n\tbones = (hlmdl_bone_t *) ((qbyte *) header + header->boneindex);\n\tbonectls = (hlmdl_bonecontroller_t *) ((qbyte *) header + header->controllerindex);\n\n\tmodel->header = header;\n\tmodel->bones = bones;\n\tmodel->bonectls = bonectls;\n\n#ifndef SERVERONLY\n\tmodel->compatbones = ZG_Malloc(&mod->memgroup, header->numbones * sizeof(*model->compatbones));\n\tfor (i = 0; i < header->numbones; i++)\n\t{\n\t\tfloat matrix[12];\n\t\tQ_strncpyz(model->compatbones[i].name, model->bones[i].name, sizeof(model->bones[i].name));\n\t\tmodel->compatbones[i].parent = model->bones[i].parent;\n\t\tmodel->compatbones[i].ref.org[0] = model->bones[i].value[0];\n\t\tmodel->compatbones[i].ref.org[1] = model->bones[i].value[1];\n\t\tmodel->compatbones[i].ref.org[2] = model->bones[i].value[2];\n\t\tQuaternionGLAngle(model->bones[i].value+3, model->compatbones[i].ref.quat);\n\t\tmodel->compatbones[i].ref.scale[0] = 1.0f;\n\t\tmodel->compatbones[i].ref.scale[1] = 1.0f;\n\t\tmodel->compatbones[i].ref.scale[2] = 1.0f;\n\n\t\t//compute rel matrix\n\t\tGenMatrixPosQuat4Scale(model->compatbones[i].ref.org, model->compatbones[i].ref.quat, model->compatbones[i].ref.scale, matrix);\n\t\t//compute abs matrix.\n\t\tif(model->bones[i].parent>=0)\n\t\t\tR_ConcatTransforms((void*)transform_matrix[model->bones[i].parent], (void*)matrix, transform_matrix[i]);\n\t\telse\n\t\t\tmemcpy(transform_matrix[i], matrix, 12 * sizeof(float));\n\t\t//keep the ragdoll code happy with its insistance on using inverses.\n\t\tMatrix3x4_Invert_Simple((const float*)transform_matrix[i], model->compatbones[i].inverse);\n\t}\n\n\ttex = (hlmdl_tex_t *) ((qbyte *) texheader + texheader->textures);\n\n\tshaders = ZG_Malloc(&mod->memgroup, texheader->numtextures*sizeof(shader_t));\n\tmodel->shaders = shaders;\n\n\tfor(i = 0; i < texheader->numtextures; i++)\n\t{\n\t\tQ_snprintfz(shaders[i].name, sizeof(shaders[i].name), \"%s/%s\", mod->name, COM_SkipPath(tex[i].name));\n\n\t\t/* handle the special textures - eukara */\n\t\tif (tex[i].flags)\n\t\t{\n\t\t\tchar *shader;\n\t\t\tif (tex[i].flags & HLMDLFL_FULLBRIGHT)\n\t\t\t{\n\t\t\t\tif (tex[i].flags & HLMDLFL_CHROME)\n\t\t\t\t{\n\t\t\t\t\tshader = HLSHADER_FULLBRIGHTCHROME;\n\t\t\t\t\tQ_snprintfz(shaders[i].name, sizeof(shaders[i].name), \"common/hlmodel_fullbrightchrome\");\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tshader = HLSHADER_FULLBRIGHT;\n\t\t\t\t\tQ_snprintfz(shaders[i].name, sizeof(shaders[i].name), \"common/hlmodel_fullbright\");\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if ( (tex[i].flags & HLMDLFL_MASKED) || (tex[i].flags & (HLMDLFL_MASKED | HLMDLFL_ALPHASOLID)))\n\t\t\t{\n\t\t\t\tshader = HLSHADER_MASKED;\n\t\t\t\tQ_snprintfz(shaders[i].name, sizeof(shaders[i].name), \"common/hlmodel_masked\");\n\t\t\t}\n\t\t\telse if (tex[i].flags & HLMDLFL_CHROME)\n\t\t\t{\n\t\t\t\tshader = HLSHADER_CHROME;\n\t\t\t\tQ_snprintfz(shaders[i].name, sizeof(shaders[i].name), \"common/hlmodel_chrome\");\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tshader = \"\";\n\t\t\t\tQ_snprintfz(shaders[i].name, sizeof(shaders[i].name), \"common/hlmodel_other\");\n\t\t\t}\n\t\t\tshaders[i].defaultshadertext = shader;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tshaders[i].defaultshadertext = NULL;\n\t\t\tQ_snprintfz(shaders[i].name, sizeof(shaders[i].name), \"common/hlmodel\");\n\t\t}\n\t\tmemset(&shaders[i].defaulttex, 0, sizeof(shaders[i].defaulttex));\n\t}\n\n\n\t//figure out the preferred atlas size. hopefully it'll fit well enough...\n\tif (texheader->numtextures == 1)\n\t\tMod_LightmapAllocInit(&atlas, false, tex[0].w, tex[0].h, 0);\n\telse\n\t{\n\t\tint sz = 1;\n\t\tfor(i = 0; i < texheader->numtextures; i++)\n\t\t\twhile (sz < tex[i].w || sz < tex[i].h)\n\t\t\t\tsz <<= 1;\n\t\tfor (; sz < sh_config.texture2d_maxsize && sz <= LMBLOCK_SIZE_MAX; sz<<=1)\n\t\t{\n\t\t\tunsigned short x,y;\n\t\t\tint atlasid;\n\t\t\tMod_LightmapAllocInit(&atlas, false, sz, sz, 0);\n\t\t\tfor(i = 0; i < texheader->numtextures; i++)\n\t\t\t{\n\t\t\t\tif ((tex[i].flags & HLMDLFL_CHROME) || !Q_strncasecmp(tex[i].name, \"DM_Base\", 7))\n\t\t\t\t\tcontinue;\n\t\t\t\tMod_LightmapAllocBlock(&atlas, tex[i].w, tex[i].h, &x, &y, &atlasid);\n\t\t\t}\n\t\t\tif (i == texheader->numtextures && atlas.lmnum <= 0)\n\t\t\t\tbreak;\t//okay, just go with it.\n\t\t}\n\t\tMod_LightmapAllocInit(&atlas, false, sz, sz, 0);\n\t}\n\tfor(i = 0; i < texheader->numtextures; i++)\n\t{\n\t\tif ((tex[i].flags & HLMDLFL_CHROME) || !Q_strncasecmp(tex[i].name, \"DM_Base\", 7))\n\t\t{\n\t\t\tshaders[i].x =\n\t\t\tshaders[i].y = 0;\n\t\t\tshaders[i].w = tex[i].w;\n\t\t\tshaders[i].h = tex[i].h;\n\t\t\tshaders[i].atlasid = -1;\n\t\t\tcontinue;\n\t\t}\n\t\tshaders[i].w = tex[i].w;\n\t\tshaders[i].h = tex[i].h;\n\t\tMod_LightmapAllocBlock(&atlas, shaders[i].w, shaders[i].h, &shaders[i].x, &shaders[i].y, &shaders[i].atlasid);\n\t}\n\tif (atlas.allocated[0])\n\t\tatlas.lmnum++;\n\t//now we know where the various textures will be, generate the atlas images.\n\tfor (j = 0; j < atlas.lmnum; j++)\n\t{\n\t\ttexid_t basetex;\n\t\tint y, x;\n\t\tunsigned int *basepix = Z_Malloc(atlas.width * atlas.height * sizeof(*basepix));\n\t\tfor(i = 0; i < texheader->numtextures; i++)\n\t\t{\n\t\t\tif (shaders[i].atlasid == j)\n\t\t\t{\n\t\t\t\tunsigned *out = basepix + atlas.width*shaders[i].y + shaders[i].x;\n\t\t\t\tqbyte *in = (qbyte *) texheader + tex[i].offset;\n\t\t\t\tqbyte *pal = (qbyte *) texheader + tex[i].w * tex[i].h + tex[i].offset;\n\t\t\t\tqbyte *rgb;\n\t\t\t\tfor(y = 0; y < tex[i].h; y++, out += atlas.width-shaders[i].w)\n\t\t\t\t\tfor(x = 0; x < tex[i].w; x++, in++)\n\t\t\t\t\t{\n\t\t\t\t\t\trgb = pal + *in*3;\n\t\t\t\t\t\t*out++ = 0xff000000 | (rgb[0]<<0) | (rgb[1]<<8) | (rgb[2]<<16);\n\t\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tQ_snprintfz(texname, sizeof(texname), \"%s*%i\", mod->name, j);\n\t\tbasetex = Image_GetTexture(texname, \"\", IF_NOALPHA|IF_NOREPLACE, basepix, NULL, atlas.width, atlas.height, PTI_RGBX8);\n\t\tZ_Free(basepix);\n\t\tfor(i = 0; i < texheader->numtextures; i++)\n\t\t{\n\t\t\tif (shaders[i].atlasid == j)\n\t\t\t\tshaders[i].defaulttex.base = basetex;\n\t\t}\n\t}\n\t//and chrome textures need to preserve their texture coords to avoid weirdness.\n\tfor(i = 0; i < texheader->numtextures; i++)\n\t{\n\t\tif (!Q_strncasecmp(tex[i].name, \"DM_Base\", 7))\n\t\t{\n\t\t\tint y, x;\n\t\t\tunsigned int *basepix = Z_Malloc(tex[i].w*tex[i].h*sizeof(*basepix) + tex[i].w*tex[i].h*2);\n\t\t\tunsigned int *out = basepix;\n\t\t\tqbyte *upper = (qbyte*)(basepix + tex[i].w * tex[i].h);\t//we use an L8 texture, because we can.\n\t\t\tqbyte *lower = upper + tex[i].w * tex[i].h;\n\t\t\tqbyte *in = (qbyte *) texheader + tex[i].offset;\n\t\t\tqbyte *pal = (qbyte *) texheader + tex[i].w * tex[i].h + tex[i].offset;\n\t\t\tqbyte *rgb;\n\t\t\tfor(y = 0; y < tex[i].h; y++)\n\t\t\t\tfor(x = 0; x < tex[i].w; x++, in++)\n\t\t\t\t{\n\t\t\t\t\tif (*in >= 256-96 && *in < 256-64)\n\t\t\t\t\t{\t//rows 11 and 12 are the player's upper colour (in the lower range)\n\t\t\t\t\t\t*out++ = 0xff000000;\n\t\t\t\t\t\t*upper++ = 255-(7+(*in-(256-96))*(256/32));\n\t\t\t\t\t\t*lower++ = 0;\n\t\t\t\t\t}\n\t\t\t\t\telse if (*in >= 256-64 && *in < 256-32)\n\t\t\t\t\t{\t//rows 13 and 14 are the player's lower colour\n\t\t\t\t\t\t*out++ = 0xff000000;\n\t\t\t\t\t\t*upper++ = 0;\n\t\t\t\t\t\t*lower++ = 255-(7+(*in-(256-64))*(256/32));\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\t//regular and fullbright ranges... not that there is fullbrights on hlmdl\n\t\t\t\t\t\trgb = pal + *in*3;\n\t\t\t\t\t\t*out++ = 0xff000000 | (rgb[0]<<0) | (rgb[1]<<8) | (rgb[2]<<16);\n\t\t\t\t\t\t*upper++ = 0;\n\t\t\t\t\t\t*lower++ = 0;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\tout = basepix;\n\t\t\tupper = (qbyte*)(basepix + tex[i].w * tex[i].h);\t//we use an L8 texture, because we can.\n\t\t\tlower = upper + tex[i].w * tex[i].h;\n\n\t\t\tQ_snprintfz(texname, sizeof(texname), \"%s*%s\", mod->name, tex[i].name);\n\t\t\tshaders[i].defaulttex.base         = Image_GetTexture(texname, \"\", IF_NOALPHA|IF_NOREPLACE, out, NULL, tex[i].w, tex[i].h, PTI_RGBX8);\n\t\t\tQ_snprintfz(texname, sizeof(texname), \"%s*%s*upper\", mod->name, tex[i].name);\n\t\t\tshaders[i].defaulttex.upperoverlay = Image_GetTexture(texname, \"\", IF_NOALPHA|IF_NOREPLACE, upper, NULL, tex[i].w, tex[i].h, PTI_L8);\n\t\t\tQ_snprintfz(texname, sizeof(texname), \"%s*%s*lower\", mod->name, tex[i].name);\n\t\t\tshaders[i].defaulttex.loweroverlay = Image_GetTexture(texname, \"\", IF_NOALPHA|IF_NOREPLACE, lower, NULL, tex[i].w, tex[i].h, PTI_L8);\n\t\t\tZ_Free(basepix);\n\t\t}\n\t\telse if (tex[i].flags & HLMDLFL_CHROME)\n\t\t{\n\t\t\tqbyte *in = (qbyte *) texheader + tex[i].offset;\n\t\t\tqbyte *pal = (qbyte *) texheader + tex[i].w * tex[i].h + tex[i].offset;\n\n\t\t\tshaders[i].atlasid = j++;\n\t\t\tQ_snprintfz(texname, sizeof(texname), \"%s*%i\", mod->name, shaders[i].atlasid);\n\t\t\tshaders[i].defaulttex.base = Image_GetTexture(texname, \"\", IF_NOALPHA|IF_NOREPLACE, in, pal, tex[i].w, tex[i].h, TF_8PAL24);\n\t\t}\n\t\telse if (tex[i].flags & HLMDLFL_MASKED)\n\t\t{\n\t\t\tint k = 0;\n\t\t\tqbyte *in = (qbyte *) texheader + tex[i].offset;\n\t\t\tqbyte *pal = (qbyte *) texheader + tex[i].w * tex[i].h + tex[i].offset;\n\t\t\tqbyte alphaPal[1024]; /* 256 color 32-bit palette */\n\n\t\t\tfor (k = 0; k < 255; k+= 1) {\n\t\t\t\tint p = k * 4;\n\t\t\t\tint x = k * 3;\n\t\t\t\talphaPal[p + 0] = pal[x + 0];\n\t\t\t\talphaPal[p + 1] = pal[x + 1];\n\t\t\t\talphaPal[p + 2] = pal[x + 2];\n\t\t\t\talphaPal[p + 3] = 255;\n\t\t\t}\n\n\t\t\t/* pal index 255 = always transparent ~eukara */\n\t\t\talphaPal[255*4+0] = 0;\n\t\t\talphaPal[255*4+1] = 0;\n\t\t\talphaPal[255*4+2] = 0;\n\t\t\talphaPal[255*4+3] = 0;\n\n\t\t\tshaders[i].atlasid = j++;\n\t\t\tQ_snprintfz(texname, sizeof(texname), \"%s*%i\", mod->name, shaders[i].atlasid);\n\t\t\tshaders[i].defaulttex.base = Image_GetTexture(texname, \"\", IF_NOREPLACE, in, alphaPal, tex[i].w, tex[i].h, TF_8PAL32);\n\t\t}\n\t}\n\n\n\n\n\tmodel->numskinrefs = texheader->skinrefs;\n\tmodel->numskingroups = texheader->skingroups;\n\tmodel->skinref = ZG_Malloc(&mod->memgroup, model->numskinrefs*model->numskingroups*sizeof(*model->skinref));\n\tmemcpy(model->skinref, (short *) ((qbyte *) texheader + texheader->skins), model->numskinrefs*model->numskingroups*sizeof(*model->skinref));\n#endif\n\n\tif (texmem)\n\t\tZ_Free(texmem);\n\n\tmod->funcs.NativeContents = HLMDL_Contents;\n\tmod->funcs.NativeTrace = HLMDL_Trace;\n\tmod->type = mod_halflife;\n\tmod->numframes = model->header->numseq;\n\tmod->meshinfo = model;\n\n#ifndef SERVERONLY\n\tmodel->numgeomsets = model->header->numbodyparts;\n\tmodel->geomset = ZG_Malloc(&mod->memgroup, sizeof(*model->geomset) * model->numgeomsets);\n\tHLMDL_PrepareVerticies(mod, model);\n\t//FIXME: No VBOs used.\n#endif\n\treturn true;\n}\n\n#ifdef HLSERVER\nvoid *Mod_GetHalfLifeModelData(model_t *mod)\n{\n\thlmodelcache_t *mc;\n\tif (!mod || mod->type != mod_halflife)\n\t\treturn NULL;\t//halflife models only, please\n\n\tmc = Mod_Extradata(mod);\n\treturn (void*)mc->header;\n}\n#endif\n\nint HLMDL_FrameForName(model_t *mod, const char *name)\n{\n\tint i;\n\thlmdl_header_t *h;\n\thlmdl_sequencelist_t *seqs;\n\thlmodel_t *mc;\n\tif (!mod || mod->type != mod_halflife)\n\t\treturn -1;\t//halflife models only, please\n\n\tmc = Mod_Extradata(mod);\n\n\th = mc->header;\n\tseqs = (hlmdl_sequencelist_t*)((char*)h+h->seqindex);\n\n\tfor (i = 0; i < h->numseq; i++)\n\t{\n\t\tif (!strcmp(seqs[i].name, name))\n\t\t\treturn i;\n\t}\n\treturn -1;\n}\n\nint HLMDL_FrameForAction(model_t *mod, int actionid)\n{\n\tint i;\n\thlmdl_header_t *h;\n\thlmdl_sequencelist_t *seqs;\n\thlmodel_t *mc;\n\tint weight = 0;\n\tif (!mod || mod->type != mod_halflife)\n\t\treturn -1;\t//halflife models only, please\n\n\tmc = Mod_Extradata(mod);\n\n\th = mc->header;\n\tseqs = (hlmdl_sequencelist_t*)((char*)h+h->seqindex);\n\n\t//figure out the total weight.\n\tfor (i = 0; i < h->numseq; i++)\n\t\tif (seqs[i].action == actionid)\n\t\t\tweight += seqs[i].actionweight;\n\t//pick a random number between 0 and the total weight...\n\tweight *= frandom();\n\t//now figure out which sequence that gives us.\n\tfor (i = 0; i < h->numseq; i++)\n\t\tif (seqs[i].action == actionid)\n\t\t{\n\t\t\tif (weight <= seqs[i].actionweight)\n\t\t\t\treturn i;\n\t\t\tweight -= seqs[i].actionweight;\n\t\t}\n\treturn -1;\t//failed...\n}\n\nqboolean HLMDL_GetModelEvent(model_t *model, int animation, int eventidx, float *timestamp, int *eventcode, char **eventdata)\n{\n\thlmodel_t *mc = Mod_Extradata(model);\n\thlmdl_header_t *h = mc->header;\n\thlmdl_event_t *ev;\n\thlmdl_sequencelist_t *seq = animation + (hlmdl_sequencelist_t*)((char*)h+h->seqindex);\n\tif (animation < 0 || animation >= h->numseq || eventidx < 0 || eventidx >= seq->num_events)\n\t\treturn false;\n\tev = eventidx + (hlmdl_event_t*)((char*)h+seq->ofs_events);\n\t*timestamp = ev->pose / seq->timing;\n\t*eventcode = ev->code;\n\t*eventdata = ev->data;\n\treturn true;\n}\n\nint HLMDL_BoneForName(model_t *mod, const char *name)\n{\n\tint i;\n\thlmdl_header_t *h;\n\thlmdl_bone_t *bones;\n\thlmodel_t *mc;\n\tif (!mod || mod->type != mod_halflife)\n\t\treturn -1;\t//halflife models only, please\n\n\tmc = Mod_Extradata(mod);\n\n\th = mc->header;\n\tbones = (hlmdl_bone_t*)((char*)h+h->boneindex);\n\n\tfor (i = 0; i < h->numbones; i++)\n\t{\n\t\tif (!strcmp(bones[i].name, name))\n\t\t\treturn i+1;\n\t}\n\n\t//FIXME: hlmdl has tags as well as bones.\n\treturn 0;\n}\n\n/*\n =======================================================================================================================\n\tHL_CalculateBones - calculate bone positions - quaternion+vector in one function\n =======================================================================================================================\n */\nvoid HL_CalculateBones\n(\n\tint\t\t\t\tframe,\n\tvec4_t\t\t\tadjust,\n\thlmdl_bone_t\t*bone,\n\thlmdl_anim_t\t*animation,\n\tfloat\t\t\t*organg\n)\n{\n\tint\t\ti;\n\n\t/* For each vector */\n\tfor(i = 0; i < 6; i++)\n\t{\n\t\torgang[i] = bone->value[i];\t/* Take the bone value */\n\n\t\tif(animation->offset[i] != 0)\n\t\t{\n\t\t\t/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/\n\t\t\tint\t\t\t\t\ttempframe;\n\t\t\thlmdl_animvalue_t\t*animvalue = (hlmdl_animvalue_t *) ((qbyte *) animation + animation->offset[i]);\n\t\t\t/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/\n\n\t\t\t/* find values including the required frame */\n\t\t\ttempframe = frame;\n\t\t\twhile(animvalue->num.total <= tempframe)\n\t\t\t{\n\t\t\t\ttempframe -= animvalue->num.total;\n\t\t\t\tanimvalue += animvalue->num.valid + 1;\n\t\t\t}\n\t\t\tif (tempframe >= animvalue->num.valid)\n\t\t\t\ttempframe = animvalue->num.valid;\n\t\t\telse\n\t\t\t\ttempframe += 1;\n\n\t\t\torgang[i] += animvalue[tempframe].value * bone->scale[i];\n\t\t}\n\n\t\tif(bone->bonecontroller[i] != -1)\n\t\t{\t/* Add the programmable offset. */\n\t\t\torgang[i] += adjust[bone->bonecontroller[i]];\n\t\t}\n\t}\n}\n\n/*\n =======================================================================================================================\n\tHL_CalcBoneAdj - Calculate the adjustment values for the programmable controllers\n =======================================================================================================================\n */\nvoid HL_CalcBoneAdj(hlmodel_t *model)\n{\n\t/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/\n\tint\t\t\t\t\t\ti;\n\tfloat\t\t\t\t\tvalue;\n\thlmdl_bonecontroller_t\t*control = (hlmdl_bonecontroller_t *)\n\t\t\t\t\t\t\t\t\t  ((qbyte *) model->header + model->header->controllerindex);\n\t/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/\n\n\tfor(i = 0; i < model->header->numcontrollers; i++)\n\t{\n\t\t/*~~~~~~~~~~~~~~~~~~~~~*/\n\t\tint j = control[i].index;\n\t\t/*~~~~~~~~~~~~~~~~~~~~~*/\n\n\t\tif(control[i].type & 0x8000)\n\t\t{\t//wraps normally\n\t\t\tvalue = model->controller[j];// + control[i].start;\n\t\t}\n\t\telse\n\t\t{\n//\t\t\tvalue = (model->controller[j]+1)*0.5;\t//shifted to give a valid range between -1 and 1, with 0 being mid-range.\n//\t\t\tif(value < 0)\n//\t\t\t\tvalue = 0;\n//\t\t\telse if(value > 1.0)\n//\t\t\t\tvalue = 1.0;\n//\t\t\tvalue = (1.0 - value) * control[i].start + value * control[i].end;\n\n\t\t\tvalue = model->controller[j];\n\t\t\tif (value < control[i].start)\n\t\t\t\tvalue = control[i].start;\n\t\t\tif (value > control[i].end)\n\t\t\t\tvalue = control[i].end;\n\t\t}\n\n\t\t/* Rotational controllers need their values converted */\n\t\tif(control[i].type >= 0x0008 && control[i].type <= 0x0020)\n\t\t\tmodel->adjust[i] = M_PI * value / 180;\n\t\telse\n\t\t\tmodel->adjust[i] = value;\n\t}\n}\n\n/*\n =======================================================================================================================\n\tHL_SetupBones - determine where vertex should be using bone movements\n =======================================================================================================================\n */\nvoid QuaternionSlerp( const vec4_t p, vec4_t q, float t, vec4_t qt );\nvoid HL_SetupBones(hlmodel_t *model, int seqnum, int firstbone, int lastbone, float subblendfrac1, float subblendfrac2, float frametime, float *matrix)\n{\n\t/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/\n\tint\t\t\t\t\t\ti, j;\n\tvec3_t\t\t\t\t\torgang1[2];\n\tvec3_t\t\t\t\t\torgang2[2];\n\tvec3_t\t\t\t\t\torgangb[2];\n\tvec4_t\t\t\t\t\tquat1, quat2;\n\n\tint frame1, frame2;\n\n\thlmdl_sequencelist_t\t*sequence = (hlmdl_sequencelist_t *) ((qbyte *) model->header + model->header->seqindex) +\n\t\t\t\t\t\t\t\t\t\t ((unsigned int)seqnum>=model->header->numseq?0:seqnum);\n\thlmdl_sequencedata_t\t*sequencedata = (hlmdl_sequencedata_t *)\n\t\t\t\t\t\t\t\t\t\t ((qbyte *) model->header + model->header->seqgroups) +\n\t\t\t\t\t\t\t\t\t\t sequence->seqindex;\n\thlmdl_anim_t\t\t\t*animation;\n\t/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/\n\n\tmatrix += firstbone*12;\n\n\tif (sequencedata->name[32])\n\t{\n\t\tsize_t fz;\n\t\tif (sequence->seqindex >= MAX_ANIM_GROUPS)\n\t\t{\n\t\t\tSys_Error(\"Too many animation sequence cache groups\\n\");\n\t\t\treturn;\n\t\t}\n\t\tif (!model->animcache[sequence->seqindex])\n\t\t\tmodel->animcache[sequence->seqindex] = FS_LoadMallocGroupFile(model->memgroup, sequencedata->name+32, &fz, true);\n\t\tif (!model->animcache[sequence->seqindex] || model->animcache[sequence->seqindex]->magic != (('I'<<0)|('D'<<8)|('S'<<16)|('Q'<<24)) || model->animcache[sequence->seqindex]->version != 10)\n\t\t{\n\t\t\tSys_Error(\"Unable to load %s\\n\", sequencedata->name+32);\n\t\t\treturn;\n\t\t}\n\t\tanimation = (hlmdl_anim_t *)((qbyte*)model->animcache[sequence->seqindex] + sequence->index);\n\t}\n\telse\n\t\tanimation = (hlmdl_anim_t *) ((qbyte *) model->header + sequencedata->data + sequence->index);\n\n\tframetime *= sequence->timing;\n\tif (frametime < 0)\n\t\tframetime = 0;\n\n\tframe1 = (int)frametime;\n\tframetime -= frame1;\n\tframe2 = frame1+1;\n\n\tif (!sequence->numframes)\n\t\treturn;\n\t//halflife seems to dupe the last frame in looping animations, so don't use it.\n\tif(frame1 >= sequence->numframes)\n\t{\n\t\tif (sequence->loop)\n\t\t\tframe1 %= sequence->numframes;\n\t\telse\n\t\t\tframe1 = sequence->numframes-1;\n\t}\n\tif(frame2 >= sequence->numframes)\n\t{\n\t\tif (sequence->loop)\n\t\t\tframe2 %= sequence->numframes;\n\t\telse\n\t\t\tframe2 = sequence->numframes-1;\n\t}\n\n\tif (frame2 < frame1)\n\t{\n\t\ti = frame2;\n\t\tframe2 = frame1;\n\t\tframe1 = i;\n\t\tframetime = 1-frametime;\n\t}\n\n\tif (lastbone > model->header->numbones)\n\t\tlastbone = model->header->numbones;\n\n\n\n\tHL_CalcBoneAdj(model);\t/* Deal with programmable controllers */\n\n\t/*FIXME:this is useless*/\n\t/*\n\tif(sequence->motiontype & 0x0001)\n\t\tpositions[sequence->motionbone][0] = 0.0;\n\tif(sequence->motiontype & 0x0002)\n\t\tpositions[sequence->motionbone][1] = 0.0;\n\tif(sequence->motiontype & 0x0004)\n\t\tpositions[sequence->motionbone][2] = 0.0;\n\t\t*/\n\n\t/*\n\tthis is hellish.\n\ta hl model blends:\n\t\t4 controllers (on a player, it seems each one of them twists a separate bone in the chest)\n\t\ta mouth (not used on players)\n\t\tits a sequence (to be smooth we need to blend between two frames in the sequence)\n\t\tup to four source animations (ironically used to pitch up/down)\n\t\talternate sequence (walking+firing)\n\t\tframe2 (quake expectations.)\n\n\t\tthis is madness, quite frankly.\n\n\t\tluckily...\n\t\tcontrollers and mouth control the entire thing. they should be interpolated outside, and have no affect on blending here\n\t\talternate sequences replace. we can just call this function twice (so long as bone ranges are incremental).\n\t\tautoanimating sequence is handled inside HL_CalculateBones (sequences are weird and it has to be handled there anyway)\n\n\t\tthis means we only have sources and alternate frames left to cope with.\n\n\t\tFIXME: we don't handle frame2.\n\t*/\n\n\tif (sequence->hasblendseq>1)\n\t{\n\t\tint bf0, bf1;\n\t\tunsigned int bweights;\n\t\tstruct\n\t\t{\n\t\t\tint frame;\n\t\t\tfloat weight;\n\t\t\thlmdl_anim_t *anim;\n\t\t} blend[8];\n\t\t//right, so, this stuff is annoying.\n\t\t//we have two different blend factors.\n\t\t//we have up to 9 blend weights. figure out which frames are what\n\t\tswitch(sequence->hasblendseq)\n\t\t{\n\t\tcase 0:\t//erk?\n\t\t\treturn;\n\t\tcase 1: //no blending.\n\t\t\tbf0 = bf1 = 1;\n\t\t\tbreak;\n\t\tdefault:\n\t\tcase 2: //mix(0, 1, weight0)\n\t\tcase 3: //mix(0, 1, 2, weight0);\n\t\tcase 8: //weight0 only...\n\t\t\tbf0 = sequence->hasblendseq;\n\t\t\tbf1 = 1;\n\t\t\tbreak;\n\t\tcase 4: //mix(mix(0, 1, weight0), mix(2, 3, weight0), weight1)\n\t\t\tbf0 = bf1 = 2;\n\t\t\tbreak;\n\t\t//case 6: //???\n\t\t//\tbf[0] = 3; bf[1] = 2;\n\t\t//\tbreak;\n\t\tcase 9: //mix(mix(0, 1, 2, weight0), mix(2, 3, 4, weight0), mix(5, 6, 7, weight0), weight1)\n\t\t\tbf0 = bf1 = 3;\n\t\t\tbreak;\n\t\t}\n\n\t\tsubblendfrac1 = (subblendfrac1+1)/2;\n\t\tsubblendfrac2 = (subblendfrac2+1)/2;\n\t\tbweights = 0;\n\t\tif (bf0 > 1)\n\t\t{\n\t\t\tfloat frac = (bf0-1) * bound(0, subblendfrac1, 1);\n\t\t\tint f1 = bound(0, frac, bf0-1);\n\t\t\tint f2 = bound(0, f1+1, bf0-1);\n\t\t\tfrac = (frac-f1);\n\t\t\tfrac = bound(0, frac, 1);\n\t\t\tif (bf1 > 1)\n\t\t\t{\n\t\t\t\tfloat frac2 = (bf1-1) * bound(0, subblendfrac2, 1);\n\t\t\t\tint a1 = bound(0, frac2, bf1-1);\n\t\t\t\tint a2 = bound(0, a1+1, bf1-1);\n\t\t\t\tfrac2 = (frac2-a1);\n\t\t\t\tfrac2 = bound(0, frac2, 1);\n\n\t\t\t\tif (frac2)\n\t\t\t\t{\n\t\t\t\t\tif (frac)\n\t\t\t\t\t{\n\t\t\t\t\t\tblend[bweights].frame = frame1;\n\t\t\t\t\t\tblend[bweights].anim = animation + model->header->numbones * (f2 + a2*bf0);\n\t\t\t\t\t\tblend[bweights++].weight = frac*frac2;\n\t\t\t\t\t}\n\t\t\t\t\tif (1-frac)\n\t\t\t\t\t{\n\t\t\t\t\t\tblend[bweights].frame = frame1;\n\t\t\t\t\t\tblend[bweights].anim = animation + model->header->numbones * (f1 + a2*bf0);\n\t\t\t\t\t\tblend[bweights++].weight = (1-frac)*frac2;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (1-frac2)\n\t\t\t\t{\n\t\t\t\t\tif (frac)\n\t\t\t\t\t{\n\t\t\t\t\t\tblend[bweights].frame = frame1;\n\t\t\t\t\t\tblend[bweights].anim = animation + model->header->numbones * (f2 + a1*bf0);\n\t\t\t\t\t\tblend[bweights++].weight = frac*(1-frac2);\n\t\t\t\t\t}\n\t\t\t\t\tif (1-frac)\n\t\t\t\t\t{\n\t\t\t\t\t\tblend[bweights].frame = frame1;\n\t\t\t\t\t\tblend[bweights].anim = animation + model->header->numbones * (f1 + a1*bf0);\n\t\t\t\t\t\tblend[bweights++].weight = (1-frac)*(1-frac2);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (frac)\n\t\t\t\t{\n\t\t\t\t\tblend[bweights].frame = frame1;\n\t\t\t\t\tblend[bweights].anim = animation + model->header->numbones * f1;\n\t\t\t\t\tblend[bweights++].weight = frac;\n\t\t\t\t}\n\t\t\t\tif (1-frac)\n\t\t\t\t{\n\t\t\t\t\tblend[bweights].frame = frame1;\n\t\t\t\t\tblend[bweights].anim = animation + model->header->numbones * f2;\n\t\t\t\t\tblend[bweights++].weight = 1-frac;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tblend[bweights].frame = frame1;\n\t\t\tblend[bweights].anim = animation;\n\t\t\tblend[bweights++].weight = 1;\n\t\t}\n\t\tif (frame1 != frame2)\n\t\t{\n\t\t\t//bweights can be 0-4 here..\n\t\t\tfor (i = 0; i < bweights; i++)\n\t\t\t{\n\t\t\t\tblend[bweights+i].frame = frame2;\n\t\t\t\tblend[bweights+i].anim = blend[i].anim;\n\t\t\t\tblend[bweights+i].weight = blend[i].weight;\n\n\t\t\t\tblend[i].weight *= 1-frametime;\n\t\t\t\tblend[bweights+i].weight *= frametime;\n\t\t\t}\n\t\t\tbweights *= 2;\n\t\t}\n\n\t\tfor(i = firstbone; i < lastbone; i++, matrix+=12)\n\t\t{\n\t\t\tvec3_t t;\n\t\t\tfloat len;\n\n\t\t\tHL_CalculateBones(blend[0].frame, model->adjust, model->bones + i, blend[0].anim + i, organgb[0]);\n\t\t\tQuaternionGLAngle(organgb[1], quat2);\n\t\t\tVector4Scale(quat2, blend[0].weight, quat1);\n\t\t\tVectorScale(organgb[0], blend[0].weight, t);\n\n\t\t\tfor (j = 1; j < bweights; j++)\n\t\t\t{\n\t\t\t\tHL_CalculateBones(blend[j].frame, model->adjust, model->bones + i, blend[j].anim + i, organgb[0]);\n\t\t\t\tQuaternionGLAngle(organgb[1], quat2);\n\t\t\t\tif (DotProduct4(quat2, quat1) < 0)\t//negative quats are annoying\n\t\t\t\t\tVector4MA(quat1, -blend[j].weight, quat2, quat1);\n\t\t\t\telse\n\t\t\t\t\tVector4MA(quat1, blend[j].weight, quat2, quat1);\n\t\t\t\tVectorMA(t, blend[j].weight, organgb[0], t);\n\t\t\t}\n\n\t\t\t//we were lame and didn't use slerp. boo hiss. now we need to normalise the things and hope we didn't hit any singularities\n\t\t\tlen = sqrt(DotProduct4(quat1,quat1));\n\t\t\tif (len && len != 1)\n\t\t\t{\n\t\t\t\tlen = 1/len;\n\t\t\t\tquat1[0] *= len;\n\t\t\t\tquat1[1] *= len;\n\t\t\t\tquat1[2] *= len;\n\t\t\t\tquat1[3] *= len;\n\t\t\t}\n\n\t\t\tQuaternionGLMatrix(quat1[0], quat1[1], quat1[2], quat1[3], (vec4_t*)matrix);\n\t\t\tmatrix[0*4+3] = t[0];\n\t\t\tmatrix[1*4+3] = t[1];\n\t\t\tmatrix[2*4+3] = t[2];\n\t\t}\n\t}\n\telse\n\t{\n\t\tfor(i = firstbone; i < lastbone; i++, matrix+=12)\n\t\t{\n\t\t\tHL_CalculateBones(frame1, model->adjust, model->bones + i, animation + i, organg1[0]);\n\t\t\tQuaternionGLAngle(organg1[1], quat1);\t/* A quaternion */\n\t\t\tif (frame1 != frame2)\n\t\t\t{\n\t\t\t\tHL_CalculateBones(frame2, model->adjust, model->bones + i, animation + i, organg2[0]);\n\t\t\t\tQuaternionGLAngle(organg2[1], quat2);\t/* A quaternion */\n\n\t\t\t\t//lerp the quats properly rather than poorly lerping eular angles.\n\t\t\t\tQuaternionSlerp(quat1, quat2, frametime, quat1);\n\t\t\t\tVectorInterpolate(organg1[0], frametime, organg2[0], organg1[0]);\n\t\t\t}\n\n\t\t\t//figure out the relative bone matrix.\n\t\t\t//we probably ought to keep them as quats or something.\n\t\t\tQuaternionGLMatrix(quat1[0], quat1[1], quat1[2], quat1[3], (vec4_t*)matrix);\n\t\t\tmatrix[0*4+3] = organg1[0][0];\n\t\t\tmatrix[1*4+3] = organg1[0][1];\n\t\t\tmatrix[2*4+3] = organg1[0][2];\n\t\t}\n\t}\n}\n\nint HLMDL_GetNumBones(model_t *mod, qboolean tags)\n{\n\thlmodel_t *mc;\n\tif (!mod || mod->type != mod_halflife)\n\t\treturn -1;\t//halflife models only, please\n\n\tmc = Mod_Extradata(mod);\n\tif (tags)\n\t\treturn mc->header->numbones + mc->header->num_attachments;\n\treturn mc->header->numbones;\n}\n\nint HLMDL_GetBoneParent(model_t *mod, int bonenum)\n{\n\thlmodel_t *model = Mod_Extradata(mod);\n\n\tif (bonenum >= 0 && bonenum < model->header->numbones)\n\t\treturn model->bones[bonenum].parent;\n\tbonenum -= model->header->numbones;\n\tif (bonenum >= 0 && bonenum < model->header->num_attachments)\n\t{\n\t\thlmdl_attachment_t *attachments = bonenum+(hlmdl_attachment_t*)((char*)model->header + model->header->ofs_attachments);\n\t\treturn attachments->bone;\n\t}\n\treturn -1;\n}\n\nconst char *HLMDL_GetBoneName(model_t *mod, int bonenum)\n{\n\thlmodel_t *model = Mod_Extradata(mod);\n\n\tif (bonenum >= 0 && bonenum < model->header->numbones)\n\t\treturn model->bones[bonenum].name;\n\tbonenum -= model->header->numbones;\n\tif (bonenum >= 0 && bonenum < model->header->num_attachments)\n\t{\n\t\thlmdl_attachment_t *attachments = bonenum+(hlmdl_attachment_t*)((char*)model->header + model->header->ofs_attachments);\n\t\tif (*attachments->name)\n\t\t\treturn attachments->name;\n\t\treturn \"Unnamed Attachment\";\n\t}\n\treturn NULL;\n}\n\nint HLMDL_GetAttachment(model_t *mod, int tagnum, float *resultmatrix)\n{\n\thlmodel_t *model = Mod_Extradata(mod);\n\tif (tagnum >= 0 && tagnum < model->header->num_attachments)\n\t{\n\t\thlmdl_attachment_t *attachments = tagnum+(hlmdl_attachment_t*)((char*)model->header + model->header->ofs_attachments);\n\t\tresultmatrix[3] = attachments->org[0];\n\t\tresultmatrix[7] = attachments->org[1];\n\t\tresultmatrix[11] = attachments->org[2];\n\t\treturn attachments->bone;\n\t}\n\treturn -1;\n}\n\nstatic int HLMDL_GetBoneData_Internal(hlmodel_t *model, int firstbone, int lastbone, const framestate_t *fstate, float *result)\n{\n\tint b, cbone, bgroup;\n\n\tfor (b = 0; b < MAX_BONE_CONTROLLERS; b++)\n\t\tmodel->controller[b] = fstate->bonecontrols[b];\n\tfor (cbone = 0, bgroup = 0; bgroup < FS_COUNT; bgroup++)\n\t{\n\t\tlastbone = fstate->g[bgroup].endbone;\n\t\tif (bgroup == FS_COUNT-1)\n\t\t\tlastbone = model->header->numbones;\n\t\tif (cbone >= lastbone)\n\t\t\tcontinue;\n\t\tHL_SetupBones(model, fstate->g[bgroup].frame[0] & ~0x8000, cbone, lastbone, fstate->g[bgroup].subblendfrac, fstate->g[bgroup].subblend2frac, fstate->g[bgroup].frametime[0], result);\t/* Setup the bones */\n\t\tcbone = lastbone;\n\t}\n\treturn cbone;\n}\nint HLMDL_GetBoneData(model_t *mod, int firstbone, int lastbone, const framestate_t *fstate, float *result)\n{\n\treturn HLMDL_GetBoneData_Internal(Mod_Extradata(mod), firstbone, lastbone, fstate, result);\n}\n\nconst char *HLMDL_FrameNameForNum(model_t *mod, int surfaceidx, int seqnum)\n{\n\thlmodel_t *model = Mod_Extradata(mod);\n\thlmdl_sequencelist_t\t*sequence = (hlmdl_sequencelist_t *) ((qbyte *) model->header + model->header->seqindex) +\n\t\t\t\t\t\t\t\t\t\t ((unsigned int)seqnum>=model->header->numseq?0:seqnum);\n\treturn sequence->name;\n}\nqboolean HLMDL_FrameInfoForNum(model_t *mod, int surfaceidx, int seqnum, char **name, int *numframes, float *duration, qboolean *loop, int *act)\n{\n\thlmodel_t *model = Mod_Extradata(mod);\n\thlmdl_sequencelist_t\t*sequence = (hlmdl_sequencelist_t *) ((qbyte *) model->header + model->header->seqindex) +\n\t\t\t\t\t\t\t\t\t\t ((unsigned int)seqnum>=model->header->numseq?0:seqnum);\n\n\t*name = sequence->name;\n\t*numframes = sequence->numframes;\n\t*duration = (sequence->numframes-1)/sequence->timing;\n\t*loop = sequence->loop;\n\t*act = sequence->action;\n\treturn true;\n}\n\n\n\nqboolean HLMDL_Trace\t\t(model_t *model, int hulloverride, const framestate_t *framestate, const vec3_t axis[3], const vec3_t p1, const vec3_t p2, const vec3_t mins, const vec3_t maxs, qboolean capsule, unsigned int against, struct trace_s *trace)\n{\n\thlmodel_t *hm = Mod_Extradata(model);\n\tfloat *relbones;\n\tfloat calcrelbones[MAX_BONES*12];\n\tint bonecount;\n\tint b, i;\n\tvec3_t norm, p1l, p2l;\n\tfloat inverse[12];\n\thlmdl_hitbox_t *hitbox = (hlmdl_hitbox_t*)((char*)hm->header+hm->header->ofs_hitboxes);\n\tfloat dist, d1, d2, f, enterfrac, enterdist, exitfrac;\n\tqboolean startout, endout;\n\tint enterplane;\n\n\tmemset (trace, 0, sizeof(trace_t));\n\ttrace->fraction = trace->truefraction = 1;\n\tif (!(against & FTECONTENTS_BODY) || !framestate)\n\t\treturn false;\n\n\tif (framestate->bonestate && framestate->skeltype == SKEL_ABSOLUTE)\n\t{\n\t\trelbones = framestate->bonestate;\n\t\tbonecount = framestate->bonecount;\n\t\tif (axis)\n\t\t{\n\t\t\tfor (b = 0; b < bonecount; b++)\n\t\t\t\tR_ConcatTransformsAxis(axis, (void*)(relbones+b*12), transform_matrix[b]);\n\t\t}\n\t\telse\n\t\t\tmemcpy(transform_matrix, relbones, bonecount * 12 * sizeof(float));\n\t}\n\telse\n\t{\n\t\t//get relative bones from th emodel.\n\t\tif (framestate->bonestate)\n\t\t{\n\t\t\trelbones = framestate->bonestate;\n\t\t\tbonecount = framestate->bonecount;\n\t\t}\n\t\telse\n\t\t{\n\t\t\trelbones = calcrelbones;\n\t\t\tbonecount = HLMDL_GetBoneData(model, 0, MAX_BONES, framestate, calcrelbones);\n\t\t}\n\n\t\t//convert relative to absolutes\n\t\tfor (b = 0; b < bonecount; b++)\n\t\t{\n\t\t\t/* If we have a parent, take the addition. Otherwise just copy the values */\n\t\t\tif(hm->bones[b].parent>=0)\n\t\t\t\tR_ConcatTransforms((void*)transform_matrix[hm->bones[b].parent], (void*)(relbones+b*12), transform_matrix[b]);\n\t\t\telse if (axis)\n\t\t\t\tR_ConcatTransformsAxis(axis, (void*)(relbones+b*12), transform_matrix[b]);\n\t\t\telse\n\t\t\t\tmemcpy(transform_matrix[b], relbones+b*12, 12 * sizeof(float));\n\t\t}\n\t}\n\n\tfor (b = 0; b < hm->header->num_hitboxes; b++, hitbox++)\n\t{\n\t\tstartout = false;\n\t\tendout = false;\n\t\tenterplane = 0;\n\t\tenterfrac = -1;\n\t\texitfrac = 10;\n\t\tenterdist = 0;\n\n\t\t//fixme: would be nice to check if there's a possible collision a bit faster, without needing to do lots of excess maths.\n\n\t\t//transform start+end into the bbox, so everything is axial and simple.\n\t\tMatrix3x4_Invert_Simple((void*)transform_matrix[hitbox->bone], inverse);\n\t\tMatrix3x4_RM_Transform3(inverse, p1, p1l);\n\t\tMatrix3x4_RM_Transform3(inverse, p2, p2l);\n\t\t//fixme: would it be faster to just generate the plane and transform that, colliding non-axially? would probably be better for sized impactors.\n\n\t\t//clip against the 6 axial faces\n\t\tfor (i = 0; i < 6; i++)\n\t\t{\n\t\t\tif (i < 3)\n\t\t\t{\t//normal>0\n\t\t\t\tdist = hitbox->maxs[i] - mins[i];\n\t\t\t\td1 = p1l[i] - dist;\n\t\t\t\td2 = p2l[i] - dist;\n\t\t\t}\n\t\t\telse\n\t\t\t{//normal<0\n\t\t\t\tdist = maxs[i-3] - hitbox->mins[i-3];\n\t\t\t\td1 = -p1l[i-3] - dist;\n\t\t\t\td2 = -p2l[i-3] - dist;\n\t\t\t}\n\t\t\t//FIXME: if the trace has size, we should insert 6 extra planes for the shape of the impactor\n\t\t\t//FIXME: capsules\n\n\t\t\tif (d1 >= 0)\n\t\t\t\tstartout = true;\n\t\t\tif (d2 > 0)\n\t\t\t\tendout = true;\n\n\t\t\t//if we're fully outside any plane, then we cannot possibly enter the brush, skip to the next one\n\t\t\tif (d1 > 0 && d2 >= 0)\n\t\t\t\tgoto nextbrush;\n\n\t\t\t//if we're fully inside the plane, then whatever is happening is not relevent for this plane\n\t\t\tif (d1 < 0 && d2 <= 0)\n\t\t\t\tcontinue;\n\n\t\t\tf = d1 / (d1-d2);\n\t\t\tif (d1 > d2)\n\t\t\t{\n\t\t\t\t//entered the brush. favour the furthest fraction to avoid extended edges (yay for convex shapes)\n\t\t\t\tif (enterfrac < f)\n\t\t\t\t{\n\t\t\t\t\tenterfrac = f;\n\t\t\t\t\tenterplane = i;\n\t\t\t\t\tenterdist = dist;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t//left the brush, favour the nearest plane (smallest frac)\n\t\t\t\tif (exitfrac > f)\n\t\t\t\t{\n\t\t\t\t\texitfrac = f;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (!startout)\n\t\t{\n\t\t\ttrace->startsolid = true;\n\t\t\tif (!endout)\n\t\t\t\ttrace->allsolid = true;\n\t\t\ttrace->contents = FTECONTENTS_BODY;\n\n\t\t\ttrace->brush_face = 0;\n\t\t\ttrace->bone_id = hitbox->bone+1;\n\t\t\ttrace->brush_id = b+1;\n\t\t\ttrace->surface_id = hitbox->body;\n\t\t\tbreak;\n\t\t}\n\t\tif (enterfrac != -1 && enterfrac < exitfrac)\n\t\t{\n\t\t\t//impact!\n\t\t\tif (enterfrac < trace->fraction)\n\t\t\t{\n\t\t\t\ttrace->fraction = trace->truefraction = enterfrac;\n\t\t\t\ttrace->plane.dist = enterdist;\n\t\t\t\ttrace->contents = FTECONTENTS_BODY;\n\n\t\t\t\ttrace->brush_face = enterplane+1;\n\t\t\t\ttrace->bone_id = hitbox->bone+1;\n\t\t\t\ttrace->brush_id = b+1;\n\t\t\t\ttrace->surface_id = hitbox->body;\n\t\t\t}\n\t\t}\nnextbrush:\n\t\t;\n\t}\n\n\tif (trace->brush_face)\n\t{\n\t\tVectorClear(norm);\n\t\tif (trace->brush_face < 4)\n\t\t\tnorm[trace->brush_face-1] = 1;\n\t\telse\n\t\t\tnorm[trace->brush_face-4] = -1;\n\t\tMatrix3x4_RM_Transform3x3((void*)transform_matrix[trace->bone_id-1], norm, trace->plane.normal);\n\t}\n\telse\n\t\tVectorClear(trace->plane.normal);\n\tVectorInterpolate(p1, trace->fraction, p2, trace->endpos);\n\n\treturn trace->truefraction != 1;\n}\nunsigned int HLMDL_Contents\t(model_t *model, int hulloverride, const framestate_t *framestate, const vec3_t axis[3], const vec3_t p, const vec3_t mins, const vec3_t maxs)\n{\n\ttrace_t tr;\n\tHLMDL_Trace(model, hulloverride, framestate, axis, p, p, mins, maxs, false, ~0, &tr);\n\treturn tr.contents;\n}\n\n\n#ifndef SERVERONLY\nstatic void R_HL_BuildFrame(hlmodel_t *model, int bodypart, int bodyidx, int meshidx, struct hlmodelshaders_s *texinfo, mesh_t *outmesh, float fatness)\n{\n\tint v;\n\tint w = texinfo->defaulttex.base->width;\n\tint h = texinfo->defaulttex.base->height;\n\tvec2_t texbase = {texinfo->x/(float)w, texinfo->y/(float)h};\n\tvec2_t texscale = {1.0/w, 1.0/h};\n\n\tmesh_t *srcmesh = &model->geomset[bodypart].alternatives[bodyidx].submesh[meshidx];\n\n\t//copy out the indexes into the final mesh.\n\tmemcpy(outmesh->indexes+outmesh->numindexes, srcmesh->indexes, sizeof(index_t)*srcmesh->numindexes);\n\toutmesh->numindexes += srcmesh->numindexes;\n\n\tif (outmesh == &model->mesh)\n\t{\t//get the backend to do the skeletal stuff (read: glsl)\n\t\tfor(v = 0; v < srcmesh->numvertexes; v++)\n\t\t{\t//should really come up with a better way to deal with this, like rect textures.\n\t\t\tsrcmesh->st_array[v][0] = texbase[0] + srcmesh->lmst_array[0][v][0] * texscale[0];\n\t\t\tsrcmesh->st_array[v][1] = texbase[1] + srcmesh->lmst_array[0][v][1] * texscale[1];\n\t\t}\n\t}\n\telse\n\t{\t//backend can't handle it, apparently. do it in software.\n\t\tint fvert = srcmesh->vbofirstvert;\n\t\tvecV_t *nxyz = outmesh->xyz_array+fvert;\n\t\tvec3_t *nnorm = outmesh->normals_array+fvert;\n\n\t\tfor(v = 0; v < srcmesh->numvertexes; v++)\n\t\t{\t//should really come up with a better way to deal with this, like rect textures.\n\t\t\tsrcmesh->st_array[v][0] = texbase[0] + srcmesh->lmst_array[0][v][0] * texscale[0];\n\t\t\tsrcmesh->st_array[v][1] = texbase[1] + srcmesh->lmst_array[0][v][1] * texscale[1];\n\n\t\t\t//transform to nxyz (a separate buffer from the srcmesh data)\n\t\t\tVectorTransform(srcmesh->xyz_array[v], (void *)transform_matrix[srcmesh->bonenums[v][0]], nxyz[v]);\n\n\t\t\t//transform to nnorm (a separate buffer from the srcmesh data)\n\t\t\tnnorm[v][0] = DotProduct(srcmesh->normals_array[v], transform_matrix[srcmesh->bonenums[v][0]][0]);\n\t\t\tnnorm[v][1] = DotProduct(srcmesh->normals_array[v], transform_matrix[srcmesh->bonenums[v][0]][1]);\n\t\t\tnnorm[v][2] = DotProduct(srcmesh->normals_array[v], transform_matrix[srcmesh->bonenums[v][0]][2]);\n\n\t\t\t//FIXME: svector, tvector!\n\t\t}\n\n\t\tif (fatness)\n\t\t\tfor(v = 0; v < srcmesh->numvertexes; v++)\n\t\t\t\tVectorMA(nxyz[v], fatness, nnorm[v], nxyz[v]);\n\t}\n}\n\nstatic void R_HL_BuildMeshes(batch_t *b)\n{\n\tentity_t *rent = b->ent;\n\thlmodel_t *model = Mod_Extradata(rent->model);\n\tint\t\t\t\t\t\tbody, m;\n\tstatic mesh_t *mptr[1], softbonemesh;\n\tskinfile_t *sk = rent->customskin?Mod_LookupSkin(rent->customskin):NULL;\n\n\tconst unsigned int entity_body = 0/*rent->body*/;\n\tint surf;\n\n\tfloat *bones;\n\tint numbones;\n\n\tif (b->shader->prog && (b->shader->prog->supportedpermutations & PERMUTATION_SKELETAL) && model->header->numbones < sh_config.max_gpu_bones && !b->ent->fatness)\n\t{\t//okay, we can use gpu gones. yay.\n\t\tb->mesh = mptr;\n\t\t*b->mesh = &model->mesh;\n\t}\n\telse\n\t{\n\t\tstatic vecV_t nxyz_buffer[65536];\n\t\tstatic vec3_t nnorm_buffer[65536];\n\t\t//no gpu bone support. :(\n\t\tsoftbonemesh = model->mesh;\n\t\tb->mesh = mptr;\n\t\t*b->mesh = &softbonemesh;\n\n\t\t//this stuff will get recalculated\n\t\tsoftbonemesh.xyz_array = nxyz_buffer;\n\t\tsoftbonemesh.normals_array = nnorm_buffer;\n\n\t\t//don't get confused.\n\t\tsoftbonemesh.bonenums = NULL;\n\t\tsoftbonemesh.boneweights = NULL;\n\t\tsoftbonemesh.bones = NULL;\n\t\tsoftbonemesh.numbones = 0;\n\t}\n\t(*b->mesh)->numindexes = 0;\n\n\t//FIXME: cache this!\n\tif (rent->framestate.bonecount >= model->header->numbones)\n\t{\t//skeletal object...\n\t\tint b;\n\t\tif (rent->framestate.skeltype == SKEL_RELATIVE)\n\t\t{\n\t\t\tnumbones = model->header->numbones;\n\t\t\tfor (b = 0; b < numbones; b++)\n\t\t\t{\n\t\t\t\t/* If we have a parent, take the addition. Otherwise just copy the values */\n\t\t\t\tif(model->bones[b].parent>=0)\n\t\t\t\t{\n\t\t\t\t\tR_ConcatTransforms((void*)transform_matrix[model->bones[b].parent], (void*)(rent->framestate.bonestate+b*12), transform_matrix[b]);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tmemcpy(transform_matrix[b], rent->framestate.bonestate+b*12, 12 * sizeof(float));\n\t\t\t\t}\n\t\t\t}\n\t\t\tbones = transform_matrix[0][0];\n\t\t}\n\t\telse\n\t\t{\n\t\t\tbones = rent->framestate.bonestate;\n\t\t\tnumbones = rent->framestate.bonecount;\n\t\t}\n\t}\n\telse\n\t{\t//lerp the bone data ourselves.\n\t\tfloat relatives[12*MAX_BONES];\n\t\tint cbone, b;\n\t\tbones = transform_matrix[0][0];\n\t\tnumbones = model->header->numbones;\n\n\t\tcbone = HLMDL_GetBoneData_Internal(model, 0, model->header->numbones, &rent->framestate, relatives);\n\n\t\t//convert relative to absolutes\n\t\tfor (b = 0; b < cbone; b++)\n\t\t{\n\t\t\t/* If we have a parent, take the addition. Otherwise just copy the values */\n\t\t\tif(model->bones[b].parent>=0)\n\t\t\t{\n\t\t\t\tR_ConcatTransforms((void*)transform_matrix[model->bones[b].parent], (void*)(relatives+b*12), transform_matrix[b]);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tmemcpy(transform_matrix[b], relatives+b*12, 12 * sizeof(float));\n\t\t\t}\n\t\t}\n\t}\n\n\tmodel->mesh.bones = bones;\n\tmodel->mesh.numbones = numbones;\n\n\tfor (surf = 0; surf < b->meshes; surf++)\n\t{\n\t\tbody = b->user.alias.surfrefs[surf] >> 8;\n\t\t{\n\t\t\t/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/\n\t\t\thlmdl_bodypart_t\t*bodypart = (hlmdl_bodypart_t *) ((qbyte *) model->header + model->header->bodypartindex) + body;\n\t\t\tint\t\t\t\t\tbodyindex = ((sk && body < MAX_GEOMSETS && sk->geomset[body] >= 1)?sk->geomset[body]-1:(entity_body / bodypart->base)) % bodypart->nummodels;\n\t\t\thlmdl_submodel_t\t*amodel = (hlmdl_submodel_t *) ((qbyte *) model->header + bodypart->modelindex) + bodyindex;\n\t\t\t/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/\n\n\t\t\t/* Draw each mesh */\n\t\t\tm = b->user.alias.surfrefs[surf] & 0xff;\n\t\t\t{\n\t\t\t\t/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/\n\t\t\t\thlmdl_mesh_t\t*mesh = (hlmdl_mesh_t *) ((qbyte *) model->header + amodel->meshindex) + m;\n\t\t\t\tstruct hlmodelshaders_s *texinfo;\n\t\t\t\tint skinidx = mesh->skinindex;\n\t\t\t\t/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/\n\n\t\t\t\tif (rent->skinnum < model->numskingroups)\n\t\t\t\t\tskinidx += rent->skinnum * model->numskinrefs;\n\t\t\t\ttexinfo = &model->shaders[model->skinref[skinidx]];\n\n\t\t\t\tR_HL_BuildFrame(model, body, bodyindex, m, texinfo, *b->mesh, b->ent->fatness);\n\t\t\t}\n\t\t}\n\t}\n\tb->meshes = 1;\n}\nqboolean R_CalcModelLighting(entity_t *e, model_t *clmodel);\n\nvoid R_HalfLife_TouchTextures(model_t *mod)\n{\n\thlmodel_t *model = Mod_Extradata(mod);\n\tunsigned int t;\n\tfor (t = 0; t < model->header->numtextures; t++)\n\t\tShader_TouchTexnums(&model->shaders[t].defaulttex);\n}\nvoid R_HalfLife_GenerateBatches(entity_t *rent, batch_t **batches)\n{\n\thlmodel_t *model = Mod_Extradata(rent->model);\n\tint\t\t\t\t\t\tbody, m;\n\tskinfile_t *sk = rent->customskin?Mod_LookupSkin(rent->customskin):NULL;\n\n\tconst unsigned int entity_body = 0/*rent->body*/;\n\tbatch_t *b = NULL;\n\n\tunsigned int surfidx = 0;\n\n\tR_CalcModelLighting(rent, rent->model);\t//make sure the ent's lighting is right.\n\n\t/*if (!model->vbobuilt)\n\t{\n\t\tmesh_t *mesh = &model->mesh;\n\t\tvbo_t *vbo = &model->vbo;\n\t\tvbobctx_t ctx;\n\n\t\tmodel->vbobuilt = true;\n\n\t\tBE_VBO_Begin(&ctx, (sizeof(*mesh->xyz_array)+\n\t\t\t\t\t\t\tsizeof(*mesh->colors4b_array)+\n\t\t\t\t\t\t\tsizeof(*mesh->st_array)+\n\t\t\t\t\t\t\tsizeof(*mesh->lmst_array[0])+\n\t\t\t\t\t\t\tsizeof(*mesh->normals_array)+\n\t\t\t\t\t\t\tsizeof(*mesh->bonenums)+\n\t\t\t\t\t\t\tsizeof(*mesh->boneweights)+\n\t\t\t\t\t\t\tsizeof(*mesh->snormals_array)+\n\t\t\t\t\t\t\tsizeof(*mesh->tnormals_array))*mesh->numvertexes);\n\t\tBE_VBO_Data(&ctx, mesh->xyz_array, sizeof(*mesh->xyz_array)*mesh->numvertexes, &vbo->coord);\n\t\tBE_VBO_Data(&ctx, mesh->colors4b_array, sizeof(*mesh->colors4b_array)*mesh->numvertexes, &vbo->colours[0]);vbo->colours_bytes = true;\n\t\tBE_VBO_Data(&ctx, mesh->st_array, sizeof(*mesh->st_array)*mesh->numvertexes, &vbo->texcoord);\n\t\tBE_VBO_Data(&ctx, mesh->lmst_array[0], sizeof(*mesh->lmst_array[0])*mesh->numvertexes, &vbo->lmcoord[0]);\n\t\tBE_VBO_Data(&ctx, mesh->normals_array, sizeof(*mesh->normals_array)*mesh->numvertexes, &vbo->normals);\n\t\tBE_VBO_Data(&ctx, mesh->bonenums, sizeof(*mesh->bonenums)*mesh->numvertexes, &vbo->bonenums);\n\t\tBE_VBO_Data(&ctx, mesh->boneweights, sizeof(*mesh->boneweights)*mesh->numvertexes, &vbo->boneweights);\n#if defined(RTLIGHTS)\n\t\tBE_VBO_Data(&ctx, mesh->snormals_array, sizeof(*mesh->snormals_array)*mesh->numvertexes, &vbo->tvector);\n\t\tBE_VBO_Data(&ctx, mesh->tnormals_array, sizeof(*mesh->tnormals_array)*mesh->numvertexes, &vbo->svector);\n#endif\n\t\tBE_VBO_Finish(&ctx, mesh->indexes, mesh->numindexes, &vbo->indicies, &vbo->vbomem, &vbo->ebomem);\n\t}*/\n\n\tfor (body = 0; body < model->numgeomsets; body++)\n\t{\n\t\t/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/\n\t\thlmdl_bodypart_t\t*bodypart = (hlmdl_bodypart_t *) ((qbyte *) model->header + model->header->bodypartindex) + body;\n\t\tint\t\t\t\t\tbodyindex = ((sk && body < MAX_GEOMSETS && sk->geomset[body] >= 1)?sk->geomset[body]-1:(entity_body / bodypart->base)) % bodypart->nummodels;\n\t\thlmdl_submodel_t\t*amodel = (hlmdl_submodel_t *) ((qbyte *) model->header + bodypart->modelindex) + bodyindex;\n\t\t/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/\n\n\n\t\t/* Draw each mesh */\n\t\tfor(m = 0; m < amodel->nummesh; m++, surfidx++)\n\t\t{\n\t\t\t/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/\n\t\t\thlmdl_mesh_t\t*mesh = (hlmdl_mesh_t *) ((qbyte *) model->header + amodel->meshindex) + m;\n\t\t\tstruct hlmodelshaders_s *s;\n\t\t\tint skinidx = mesh->skinindex;\n\t\t\ttexnums_t *skin;\n\t\t\tshader_t *shader;\n\t\t\tint sort, j;\n\t\t\t/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/\n\n\t\t\tif (skinidx >= model->numskinrefs)\n\t\t\t\tcontinue;\t//can happen from bad mesh/skin mixing\n\t\t\tif (rent->skinnum < model->numskingroups)\n\t\t\t\tskinidx += rent->skinnum * model->numskinrefs;\n\t\t\ts = &model->shaders[model->skinref[skinidx]];\n\n\n\t\t\tif (!s->shader)\n\t\t\t{\n\t\t\t\tif (s->defaultshadertext)\n\t\t\t\t\ts->shader = R_RegisterShader(s->name, SUF_NONE, s->defaultshadertext);\n\t\t\t\telse\n\t\t\t\t\ts->shader = R_RegisterSkin(rent->model, s->name);\n//\t\t\t\tR_BuildDefaultTexnums(&s->defaulttex, s->shader, 0);\n\t\t\t}\n\n\t\t\tskin = &s->defaulttex;\n\t\t\tshader = s->shader;\n\t\t\tif (sk)\n\t\t\t{\n\t\t\t\tint i;\n\t\t\t\tfor (i = 0; i < sk->nummappings; i++)\n\t\t\t\t{\n\t\t\t\t\tif (!strcmp(sk->mappings[i].surface, s->name))\n\t\t\t\t\t{\n\t\t\t\t\t\tskin = &sk->mappings[i].texnums;\n\t\t\t\t\t\tshader = sk->mappings[i].shader;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif ( rent->forcedshader ) {\n\t\t\t\tshader = rent->forcedshader;\n\t\t\t}\n\n\t\t\tif (b && b->skin->base == skin->base && b->shader == shader && b->meshes < countof(b->user.alias.surfrefs))\n\t\t\t\t;\t//merging it.\n\t\t\telse\n\t\t\t{\n\t\t\t\tb = BE_GetTempBatch();\n\t\t\t\tif (!b)\n\t\t\t\t\treturn;\n\t\t\t\tb->skin = skin;\n\t\t\t\tb->shader = shader;\n\n\t\t\t\tb->buildmeshes = R_HL_BuildMeshes;\n\t\t\t\tb->ent = rent;\n\t\t\t\tb->mesh = NULL;\n\t\t\t\tb->firstmesh = 0;\n\t\t\t\tb->meshes = 0;\n\t\t\t\tb->texture = NULL;\n\t\t\t\tfor (j = 0; j < MAXRLIGHTMAPS; j++)\n\t\t\t\t{\n\t\t\t\t\tb->lightmap[j] = -1;\n\t\t\t\t\tb->lmlightstyle[j] = INVALID_LIGHTSTYLE;\n\t\t\t\t}\n\t\t\t\tb->flags = 0;\n\t\t\t\tsort = shader->sort;\n\t\t\t\t//fixme: we probably need to force some blend modes based on the surface flags.\n\t\t\t\tif (rent->flags & RF_FORCECOLOURMOD)\n\t\t\t\t\tb->flags |= BEF_FORCECOLOURMOD;\n\t\t\t\tif (rent->flags & RF_ADDITIVE)\n\t\t\t\t{\n\t\t\t\t\tb->flags |= BEF_FORCEADDITIVE;\n\t\t\t\t\tif (sort < SHADER_SORT_ADDITIVE)\n\t\t\t\t\t\tsort = SHADER_SORT_ADDITIVE;\n\t\t\t\t}\n\t\t\t\tif (rent->flags & RF_TRANSLUCENT)\n\t\t\t\t{\n\t\t\t\t\tb->flags |= BEF_FORCETRANSPARENT;\n\t\t\t\t\tif (SHADER_SORT_PORTAL < sort && sort < SHADER_SORT_BLEND)\n\t\t\t\t\t\tsort = SHADER_SORT_BLEND;\n\t\t\t\t}\n\t\t\t\tif (rent->flags & RF_NODEPTHTEST)\n\t\t\t\t{\n\t\t\t\t\tb->flags |= BEF_FORCENODEPTH;\n\t\t\t\t\tif (sort < SHADER_SORT_NEAREST)\n\t\t\t\t\t\tsort = SHADER_SORT_NEAREST;\n\t\t\t\t}\n\t\t\t\tif (rent->flags & RF_NOSHADOW)\n\t\t\t\t\tb->flags |= BEF_NOSHADOWS;\n\t\t\t\tb->vbo = NULL;//&model->vbo;\n\t\t\t\tb->next = batches[sort];\n\t\t\t\tbatches[sort] = b;\n\t\t\t}\n\n\t\t\tb->user.alias.surfrefs[b->meshes++] = (body<<8)|(m&0xff);\n\t\t}\n\t}\n}\n\nvoid HLMDL_DrawHitBoxes(entity_t *rent)\n{\n\thlmodel_t *model = Mod_Extradata(rent->model);\n\thlmdl_hitbox_t *hitbox = (hlmdl_hitbox_t*)((char*)model->header+model->header->ofs_hitboxes);\n\tmatrix3x4 entitymatrix;\n\n\tshader_t *shader = R_RegisterShader(\"hitbox_nodepth\", SUF_NONE,\n\t\t\t\"{\\n\"\n\t\t\t\t\"polygonoffset\\n\"\n\t\t\t\t\"{\\n\"\n\t\t\t\t\t\"map $whiteimage\\n\"\n\t\t\t\t\t\"blendfunc gl_src_alpha gl_one\\n\"\n\t\t\t\t\t\"rgbgen vertex\\n\"\n\t\t\t\t\t\"alphagen vertex\\n\"\n\t\t\t\t\t\"nodepthtest\\n\"\n\t\t\t\t\"}\\n\"\n\t\t\t\"}\\n\");\n\n\tfloat relbones[MAX_BONES*12];\n\tint bonecount = HLMDL_GetBoneData(rent->model, 0, MAX_BONES, &rent->framestate, relbones);\n\tint b;\n\n\tentitymatrix[0][0] = rent->axis[0][0];\n\tentitymatrix[0][1] = rent->axis[1][0];\n\tentitymatrix[0][2] = rent->axis[2][0];\n\tentitymatrix[1][0] = rent->axis[0][1];\n\tentitymatrix[1][1] = rent->axis[1][1];\n\tentitymatrix[1][2] = rent->axis[2][1];\n\tentitymatrix[2][0] = rent->axis[0][2];\n\tentitymatrix[2][1] = rent->axis[1][2];\n\tentitymatrix[2][2] = rent->axis[2][2];\n\tentitymatrix[0][3] = rent->origin[0];\n\tentitymatrix[1][3] = rent->origin[1];\n\tentitymatrix[2][3] = rent->origin[2];\n\n\t//convert relative to absolutes\n\tfor (b = 0; b < bonecount; b++)\n\t{\n\t\t//If we have a parent, take the addition. Otherwise just copy the values\n\t\tif(model->bones[b].parent>=0)\n\t\t\tR_ConcatTransforms((void*)transform_matrix[model->bones[b].parent], (void*)(relbones+b*12), transform_matrix[b]);\n\t\telse\n\t\t\tR_ConcatTransforms((void*)entitymatrix, (void*)(relbones+b*12), transform_matrix[b]);\n\t}\n\n\tfor (b = 0; b < model->header->num_hitboxes; b++, hitbox++)\n\t\tCLQ1_AddOrientedCube(shader, hitbox->mins, hitbox->maxs, transform_matrix[hitbox->bone][0], 1, 1, 1, 0.2);\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "engine/gl/gl_model.c",
    "content": "/*\nCopyright (C) 1996-1997 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  \n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n*/\n// models.c -- model loading and caching\n\n// models are the only shared resource between a client and server running\n// on the same machine.\n\n#include \"quakedef.h\"\n#include \"fs.h\"\n#include \"com_bih.h\"\n#if 1//ndef SERVERONLY\t//FIXME\n#include \"glquake.h\"\n#include \"com_mesh.h\"\n\nextern cvar_t r_shadow_bumpscale_basetexture;\nextern cvar_t r_replacemodels;\nextern cvar_t r_lightmap_average;\ncvar_t mod_loadentfiles\t\t\t\t\t\t= CVAR(\"sv_loadentfiles\", \"1\");\ncvar_t mod_loadentfiles_dir\t\t\t\t\t= CVAR(\"sv_loadentfiles_dir\", \"\");\ncvar_t mod_external_vis\t\t\t\t\t\t= CVARD(\"mod_external_vis\", \"1\", \"Attempt to load .vis patches for quake maps, allowing transparent water to work properly.\");\ncvar_t mod_warnmodels\t\t\t\t\t\t= CVARD(\"mod_warnmodels\", \"1\", \"Warn if any models failed to load. Set to 0 if your mod is likely to lack optional models (like its in development).\");\t//set to 0 for hexen2 and its otherwise-spammy-as-heck demo.\ncvar_t mod_litsprites_force\t\t\t\t\t= CVARFD(\"mod_litsprites_force\", \"0\", CVAR_RENDERERLATCH, \"If set to 1, sprites will be lit according to world lighting (including rtlights), like Tenebrae. Ideally use EF_ADDITIVE or EF_FULLBRIGHT to make emissive sprites instead.\");\ncvar_t mod_loadmappackages\t\t\t\t\t= CVARD (\"mod_loadmappackages\", \"1\", \"Load additional content embedded within bsp files.\");\ncvar_t mod_lightscale_broken\t\t\t\t= CVARFD(\"mod_lightscale_broken\", \"0\", CVAR_RENDERERLATCH, \"When active, replicates a bug from vanilla - the radius of r_dynamic lights is scaled by per-surface texture scale rather than using actual distance.\");\ncvar_t mod_lightpoint_distance\t\t\t\t= CVARD(\"mod_lightpoint_distance\", \"8192\", \"This is the maximum distance to trace when searching for a ground surface for lighting info on map formats without light more fancy lighting info. Use 2048 for full compat with Quake.\");\n#ifdef SPRMODELS\ncvar_t r_sprite_backfacing\t\t\t\t\t= CVARD\t(\"r_sprite_backfacing\", \"0\", \"Make oriented sprites face backwards relative to their orientation, for compat with q1.\");\n#endif\ncvar_t r_nolerp_list\t\t\t\t\t\t= CVARFD (\"r_nolerp_list\"/*qs*/, \"\", CVAR_RENDERERLATCH, \"Models in this list will not interpolate. Any models included here should be considered bad.\");\n#ifdef RTLIGHTS\ncvar_t r_noshadow_list\t\t\t\t\t\t= CVARAFD (\"r_noshadow_list\"/*qs*/, \"r_noEntityCastShadowList\", \"progs/missile.mdl,progs/flame.mdl,progs/flame2.mdl,progs/lavaball.mdl,progs/grenade.mdl,progs/spike.mdl,progs/s_spike.mdl,progs/laser.mdl,progs/lspike.mdl,progs/candle.mdl\", CVAR_RENDERERLATCH, \"Models in this list will not cast shadows.\");\n#endif\n#ifdef SERVERONLY\ncvar_t gl_overbright, gl_specular, gl_load24bit, r_replacemodels, gl_miptexLevel, r_fb_bmodels;\t//all of these can/should default to 0\ncvar_t r_noframegrouplerp\t\t\t\t\t= CVARF  (\"r_noframegrouplerp\", \"0\", CVAR_ARCHIVE);\ncvar_t dpcompat_psa_ungroup\t\t\t\t\t= CVAR  (\"dpcompat_psa_ungroup\", \"0\");\ntexture_t\tr_notexture_mip_real;\ntexture_t\t*r_notexture_mip = &r_notexture_mip_real;\n#endif\n\nvoid CM_Init(void);\n\nvoid Mod_LoadSpriteShaders(model_t *spr);\nqboolean QDECL Mod_LoadSpriteModel (model_t *mod, void *buffer, size_t fsize);\nqboolean QDECL Mod_LoadSprite2Model (model_t *mod, void *buffer, size_t fsize);\n#ifdef Q1BSPS\nstatic qboolean QDECL Mod_LoadBrushModel (model_t *mod, void *buffer, size_t fsize);\n#endif\n#if defined(Q2BSPS) || defined(Q3BSPS)\nqboolean QDECL Mod_LoadQ2BrushModel (model_t *mod, void *buffer, size_t fsize);\n#endif\nmodel_t *Mod_LoadModel (model_t *mod, enum mlverbosity_e verbose);\nstatic void Mod_PrintFormats_f(void);\nstatic void Mod_ShowEnt_f(void);\nstatic void Mod_SaveEntFile_f(void);\n\n#ifdef MAP_DOOM\nqboolean QDECL Mod_LoadDoomLevel(model_t *mod, void *buffer, size_t fsize);\n#endif\n\n#ifdef DSPMODELS\nvoid Mod_LoadDoomSprite (model_t *mod);\n#endif\n\n#define\tMAX_MOD_KNOWN\t8192\nmodel_t\t*mod_known;\nint\t\tmod_numknown;\nchar mod_modifier[MAX_QPATH];\t//postfix for ent files\n\nextern cvar_t r_loadlits;\n#ifdef SPECULAR\nextern cvar_t gl_specular;\n#endif\nextern cvar_t r_fb_bmodels;\nvoid Mod_SortShaders(model_t *mod);\nvoid Mod_LoadAliasShaders(model_t *mod);\n\n#ifdef RUNTIMELIGHTING\nextern model_t *lightmodel;\n#endif\n\nstatic void Mod_MemList_f(void)\n{\n\tint m;\n\tmodel_t *mod;\n\tint total = 0;\n\tfor (m=0 , mod=mod_known ; m<mod_numknown ; m++, mod++)\n\t{\n\t\tif (mod->memgroup.totalbytes)\n\t\t\tCon_Printf(\"%s: %i bytes\\n\", mod->name, mod->memgroup.totalbytes);\n\t\ttotal += mod->memgroup.totalbytes;\n\t}\n\tCon_Printf(\"Total: %i bytes\\n\", total);\n}\n#ifndef SERVERONLY\nstatic void Mod_BatchList_f(void)\n{\n\tint m, i;\n\tmodel_t *mod;\n\tbatch_t *batch;\n\tunsigned int count;\n\tfor (m=0 , mod=mod_known ; m<mod_numknown ; m++, mod++)\n\t{\n\t\tif (mod->type == mod_brush && mod->loadstate == MLS_LOADED)\n\t\t{\n\t\t\tCon_Printf(\"^1%s:\\n\", mod->name);\n\t\t\tcount = 0;\n\t\t\tfor (i = 0; i < SHADER_SORT_COUNT; i++)\n\t\t\t{\n\t\t\t\tfor (batch = mod->batches[i]; batch; batch = batch->next)\n\t\t\t\t{\n\t\t\t\t\tchar editname[MAX_QPATH];\n\t\t\t\t\tchar *body = Shader_GetShaderBody(batch->texture->shader, editname, sizeof(editname));\n\t\t\t\t\tif (!body)\n\t\t\t\t\t\tbody = \"SHADER NOT KNOWN\";\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tchar *cr;\n\t\t\t\t\t\twhile ((cr = strchr(body, '\\r')))\n\t\t\t\t\t\t\t*cr = ' ';\n\t\t\t\t\t}\n\t\t\t\t\tCon_Printf(\"  ^[%s\\\\tipimg\\\\%s\\\\tipimgtype\\\\%i\\\\tip\\\\{%s^]\", batch->texture->shader->name, batch->texture->shader->name, batch->texture->shader->usageflags, body);\n\n#if MAXRLIGHTMAPS > 1\n\t\t\t\t\tif (batch->lightmap[3] >= 0)\n\t\t\t\t\t\tCon_Printf(\"^2 lm=(%i:%i %i:%i %i:%i %i:%i)\", batch->lightmap[0], batch->lmlightstyle[0], batch->lightmap[1], batch->lmlightstyle[1], batch->lightmap[2], batch->lmlightstyle[2], batch->lightmap[3], batch->lmlightstyle[3]);\n\t\t\t\t\telse if (batch->lightmap[2] >= 0)\n\t\t\t\t\t\tCon_Printf(\"^2 lm=(%i:%i %i:%i %i:%i)\", batch->lightmap[0], batch->lmlightstyle[0], batch->lightmap[1], batch->lmlightstyle[1], batch->lightmap[2], batch->lmlightstyle[2]);\n\t\t\t\t\telse if (batch->lightmap[1] >= 0)\n\t\t\t\t\t\tCon_Printf(\"^2 lm=(%i:%i %i:%i)\", batch->lightmap[0], batch->lmlightstyle[0], batch->lightmap[1], batch->lmlightstyle[1]);\n\t\t\t\t\telse\n#endif\n\t\t\t\t\t\tif (batch->lmlightstyle[0] != INVALID_LIGHTSTYLE)\n\t\t\t\t\t\tCon_Printf(\"^2 lm=(%i:%i)\", batch->lightmap[0], batch->lmlightstyle[0]);\n\t\t\t\t\telse\n\t\t\t\t\t\tCon_Printf(\"^2 lm=%i\", batch->lightmap[0]);\n\t\t\t\t\tcount++;\n\n\t\t\t\t\tif (batch->envmap)\n\t\t\t\t\t\tCon_Printf(\"^3 envmap=%s\", batch->envmap->ident);\n\t\t\t\t\tCon_Printf(\" surfs=%u\\n\", batch->maxmeshes);\n\t\t\t\t}\n\t\t\t}\n\t\t\tCon_Printf(\"^h(%u batches, lm %i*%i, lux %s)\\n\", count, mod->lightmaps.width, mod->lightmaps.height, mod->lightmaps.deluxemapping?\"true\":\"false\");\n\t\t}\n\t}\n}\n\nstatic void Mod_TextureList_f(void)\n{\n\tint m, i;\n\ttexture_t *tx;\n\tmodel_t *mod;\n\tqboolean shownmodelname = false;\n\tint count = 0;\n\tchar *body;\n\tchar editname[MAX_OSPATH];\n\tint preview = (Cmd_Argc()==1)?8:atoi(Cmd_Argv(1));\n\n\tint s;\n\tbatch_t *batch;\n\tunsigned int batchcount;\n\n\tfor (m=0 , mod=mod_known ; m<mod_numknown ; m++, mod++)\n\t{\n\t\tif (shownmodelname)\n\t\t\tCon_Printf(\"(%u textures)\\n\", count);\n\t\tshownmodelname = false;\n\n\t\tif (mod->type == mod_brush && mod->loadstate == MLS_LOADED)\n\t\t{\n\t\t\tif (*mod->name == '*')\n\t\t\t\tcontinue;//\tinlines don't count\n\t\t\tcount = 0;\n\t\t\tfor (i = 0; i < mod->numtextures; i++)\n\t\t\t{\n\t\t\t\ttx = mod->textures[i];\n\t\t\t\tif (!tx)\n\t\t\t\t\tcontinue;\t//happens on e1m2\n\n\t\t\t\tbatchcount = 0;\n\t\t\t\tfor (s = 0; s < SHADER_SORT_COUNT; s++)\n\t\t\t\t{\n\t\t\t\t\tfor (batch = mod->batches[s]; batch; batch = batch->next)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (batch->texture == tx)\n\t\t\t\t\t\t\tbatchcount++;\n\t\t\t\t\t}\n\t\t\t\t}\n\t//\t\t\tif (!batchcount)\n//\t\t\t\t\tcontinue; //not actually used...\n\n\t\t\t\tif (!shownmodelname)\n\t\t\t\t{\n\t\t\t\t\tshownmodelname = true;\n\t\t\t\t\tCon_Printf(\"%s\\n\", mod->name);\n\t\t\t\t\tcount = 0;\n\t\t\t\t}\n\n\t\t\t\tbody = Shader_GetShaderBody(tx->shader, editname, sizeof(editname));\n\t\t\t\tif (!body)\n\t\t\t\t\tbody = \"SHADER NOT KNOWN\";\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tchar *cr;\n\t\t\t\t\twhile ((cr = strchr(body, '\\r')))\n\t\t\t\t\t\t*cr = ' ';\n\t\t\t\t\tif (strlen(body) > 3000)\n\t\t\t\t\t\tbody[3000] = 0;\t//arbitrary cut off, to avoid console glitches with big shaders.\n\t\t\t\t}\n\n\t\t\t\tif (preview)\n\t\t\t\t{\n\t\t\t\t\tif (*editname)\n\t\t\t\t\t\tCon_Printf(\"^[\\\\edit\\\\%s\\\\img\\\\%s\\\\imgtype\\\\%i\\\\s\\\\%i\\\\tip\\\\{%s^]\", editname, tx->name, tx->shader->usageflags, preview, body);\n\t\t\t\t\telse\n\t\t\t\t\t\tCon_Printf(\"^[\\\\img\\\\%s\\\\imgtype\\\\%i\\\\s\\\\%i\\\\tip\\\\{%s^]\", tx->shader->name, tx->shader->usageflags, preview, body);\n\t\t\t\t}\n\t\t\t\tif (*editname)\n\t\t\t\t\tCon_Printf(\"  ^[%s\\\\edit\\\\%s\\\\tipimg\\\\%s\\\\tipimgtype\\\\%i\\\\tip\\\\{%s^] (%u batches)\\n\", tx->name, editname, tx->name, tx->shader->usageflags, body, batchcount);\n\t\t\t\telse\n\t\t\t\t\tCon_Printf(\"  ^[%s\\\\tipimg\\\\%s\\\\tipimgtype\\\\%i\\\\tip\\\\{%s^] (%u batches)\\n\", tx->name, tx->shader->name, tx->shader->usageflags, body, batchcount);\n\t\t\t\tcount++;\n\t\t\t}\n\t\t}\n\t}\n\tif (shownmodelname)\n\t\tCon_Printf(\"(%u textures)\\n\", count);\n}\n\nstatic void Mod_BlockTextureColour_f (void)\n{\n\tchar texname[64];\n\tmodel_t *mod;\n\ttexture_t *tx;\n//\tshader_t *s;\n\tchar *match = Cmd_Argv(1);\n\n\tint i, m;\n//\tunsigned int colour[8*8];\n\tunsigned int rgba;\n\n\t((qbyte *)&rgba)[0] = atoi(Cmd_Argv(2));\n\t((qbyte *)&rgba)[1] = atoi(Cmd_Argv(3));\n\t((qbyte *)&rgba)[2] = atoi(Cmd_Argv(4));\n\t((qbyte *)&rgba)[3] = 255;\n\n\tsprintf(texname, \"purergb_%i_%i_%i\", (int)((char *)&rgba)[0], (int)((char *)&rgba)[1], (int)((char *)&rgba)[2]);\n/*\ts = R_RegisterCustom(Cmd_Argv(2), SUF_LIGHTMAP, NULL, NULL);\n\tif (!s)\n\t{\n\t\ts = R_RegisterCustom (texname, SUF_LIGHTMAP, Shader_DefaultBSPQ1, NULL);\n\n\t\tfor (i = 0; i < sizeof(colour)/sizeof(colour[0]); i++)\n\t\t\tcolour[i] = rgba;\n\t\ts->defaulttextures.base = GL_LoadTexture32(texname, 8, 8, colour, IF_NOMIPMAP);\n\t}\n*/\n\tfor (m=0 , mod=mod_known ; m<mod_numknown ; m++, mod++)\n\t{\n\t\tif (mod->type == mod_brush && mod->loadstate == MLS_LOADED)\n\t\t{\n\t\t\tfor (i = 0; i < mod->numtextures; i++)\n\t\t\t{\n\t\t\t\ttx = mod->textures[i];\n\t\t\t\tif (!tx)\n\t\t\t\t\tcontinue;\t//happens on e1m2\n\n\t\t\t\tif (!stricmp(tx->name, match))\n\t\t\t\t\ttx->shader->defaulttextures->base = Image_GetTexture(texname, NULL, IF_NOMIPMAP|IF_NEAREST, &rgba, NULL, 1, 1, TF_BGRA32);\n\t\t\t}\n\t\t}\n\t}\n}\n#endif\n\nvoid Mod_RebuildLightmaps (void)\n{\n\tint i, j;\n\tmsurface_t *surf;\n\tmodel_t\t*mod;\n\n\tfor (i=0 , mod=mod_known ; i<mod_numknown ; i++, mod++)\n\t{\n\t\tif (mod->loadstate != MLS_LOADED)\n\t\t\tcontinue;\n\n\t\tif (mod->type == mod_brush)\n\t\t{\n\t\t\tfor (j=0, surf = mod->surfaces; j<mod->numsurfaces ; j++, surf++)\n\t\t\t\tsurf->cached_dlight=-1;//force it\n\t\t}\n\t}\n}\n\n#ifdef HAVE_CLIENT\nvoid Mod_ResortShaders(void)\n{\n\t//called when some shader changed its sort key.\n\t//this means we have to hunt down all models and update their batches.\n\t//really its only bsps that need this.\n\tbatch_t *oldlists[SHADER_SORT_COUNT], *b;\n\tint i, j, bs;\n\tmodel_t\t*mod;\n\tfor (i=0, mod=mod_known ; i<mod_numknown ; i++, mod++)\n\t{\n\t\tif (mod->loadstate != MLS_LOADED)\n\t\t\tcontinue;\n\n\t\tmemcpy(oldlists, mod->batches, sizeof(oldlists));\n\t\tmemset(mod->batches, 0, sizeof(oldlists));\n\t\tmod->numbatches = 0;\t//this is a bit of a misnomer. clearing this will cause it to be recalculated, with everything renumbered as needed.\n\t\n\t\tfor (j = 0; j < SHADER_SORT_COUNT; j++)\n\t\t{\n\t\t\twhile((b=oldlists[j]))\n\t\t\t{\n\t\t\t\toldlists[j] = b->next;\n\t\t\t\tbs = b->shader?b->shader->sort:j;\n\n\t\t\t\tb->next = mod->batches[bs];\n\t\t\t\tmod->batches[bs] = b;\n\t\t\t}\n\t\t}\n\t}\n\n\tSurf_ClearSceneCache();\t//make sure their caches are updated.\n}\n#endif\n\nconst char *Mod_GetEntitiesString(model_t *mod)\n{\n\tsize_t vl;\n\tsize_t e;\n\tsize_t sz;\n\tchar *o;\n\tif (!mod)\n\t\treturn NULL;\n\tif (mod->entities_raw)\t//still cached/correct\n\t\treturn mod->entities_raw;\n\tif (!mod->numentityinfo)\n\t{\n\t\tif (mod->loadstate != MLS_LOADED)\n\t\t\treturn NULL;\n\t\tmod->entities_raw = FS_LoadMallocFile(va(\"%s.ent\", mod->name), NULL);\n\t\tif (!mod->entities_raw)\n\t\t\tmod->entities_raw = FS_LoadMallocFile(va(\"%s.ent\", mod->name), NULL);\n\t\treturn mod->entities_raw;\n\t}\n\n\t//reform the entities back into a full string now that we apparently need it\n\t//find needed buffer size\n\tfor (e = 0, sz = 0; e < mod->numentityinfo; e++)\n\t{\n\t\tif (!mod->entityinfo[e].keyvals)\n\t\t\tcontinue;\n\t\tsz += 2;\n\t\tsz += strlen(mod->entityinfo[e].keyvals);\n\t\tsz += 2;\n\t}\n\tsz+=1;\n\to = BZ_Malloc(sz);\n\n\t//splurge it out\n\tfor (e = 0, sz = 0; e < mod->numentityinfo; e++)\n\t{\n\t\tif (!mod->entityinfo[e].keyvals)\n\t\t\tcontinue;\n\t\to[sz+0] = '{';\n\t\to[sz+1] = '\\n';\n\t\tsz += 2;\n\t\tvl = strlen(mod->entityinfo[e].keyvals);\n\t\tmemcpy(&o[sz], mod->entityinfo[e].keyvals, vl);\n\t\tsz += vl;\n\t\to[sz+0] = '}';\n\t\to[sz+1] = '\\n';\n\t\tsz += 2;\n\t}\n\to[sz+0] = 0;\n\n\tmod->entities_raw = o;\n\treturn mod->entities_raw;\n}\nvoid Mod_SetEntitiesString(model_t *mod, const char *str, qboolean docopy)\n{\n\tsize_t j;\n\tfor (j = 0; j < mod->numentityinfo; j++)\n\t\tZ_Free(mod->entityinfo[j].keyvals);\n\tmod->numentityinfo = 0;\n\tZ_Free(mod->entityinfo);\n\tmod->entityinfo = NULL;\n\tZ_Free((char*)mod->entities_raw);\n\tmod->entities_raw = NULL;\n\n\tif (str)\n\t{\n\t\tif (docopy)\n\t\t\tstr = Z_StrDup(str);\n\t\tmod->entities_raw = str;\n\t}\n}\n\nvoid Mod_SetEntitiesStringLen(model_t *mod, const char *str, size_t strsize)\n{\n\tif (str)\n\t{\n\t\tchar *cpy = BZ_Malloc(strsize+1);\n\t\tmemcpy(cpy, str, strsize);\n\t\tcpy[strsize] = 0;\n\t\tMod_SetEntitiesString(mod, cpy, false);\n\t}\n\telse\n\t\tMod_SetEntitiesString(mod, str, false);\n}\n\nvoid Mod_ParseEntities(model_t *mod)\n{\n\tchar key[1024];\n\tchar value[4096];\n\tconst char *entstart;\n\tconst char *entend;\n\tconst char *entdata;\n\tsize_t c, m;\n\n\tc = 0; m = 0;\n\n\twhile (mod->numentityinfo > 0)\n\t\tZ_Free(mod->entityinfo[--mod->numentityinfo].keyvals);\n\tZ_Free(mod->entityinfo);\n\tmod->entityinfo = NULL;\n\n\tentdata = Mod_GetEntitiesString(mod);\n\twhile(1)\n\t{\n\t\tif (!(entdata=COM_ParseOut(entdata, key, sizeof(key))))\n\t\t\tbreak;\n\t\tif (strcmp(key, \"{\"))\n\t\t\tbreak;\n\n\t\t//skip whitespace to save space.\n\t\twhile (*entdata == ' ' || *entdata == '\\r' || *entdata == '\\n' || *entdata == '\\t')\n\t\t\tentdata++;\n\n\t\tentstart = entdata;\n\n\t\twhile(1)\n\t\t{\n\t\t\tentend = entdata;\n\t\t\tentdata=COM_ParseOut(entdata, key, sizeof(key));\n\t\t\tif (!strcmp(key, \"}\"))\n\t\t\t\tbreak;\n\t\t\tentdata=COM_ParseOut(entdata, value, sizeof(value));\n\t\t}\n\t\tif (!entdata)\n\t\t\tbreak;\t//erk. eof\n\n\t\tif (c == m)\n\t\t{\n\t\t\tif (!m)\n\t\t\t\tm = 64;\n\t\t\telse\n\t\t\t\tm *= 2;\n\t\t\tmod->entityinfo = BZ_Realloc(mod->entityinfo, sizeof(*mod->entityinfo) * m);\n\t\t}\n\t\tmod->entityinfo[c].id = c+1;\n\t\tmod->entityinfo[c].keyvals = BZ_Malloc(entend-entstart + 1);\n\t\tmemcpy(mod->entityinfo[c].keyvals, entstart, entend-entstart);\n\t\tmod->entityinfo[c].keyvals[entend-entstart] = 0;\n\t\tc++;\n\t}\n\tmod->numentityinfo = c;\n}\n\n\nvoid Mod_LoadMapArchive(model_t *mod, void *archivedata, size_t archivesize)\n{\n\tif (archivesize && mod && !mod->archive && mod_loadmappackages.ival)\n\t{\n\t\tvfsfile_t *f = VFSPIPE_Open(1,true);\n\t\tif (f)\n\t\t{\n\t\t\tVFS_WRITE(f, archivedata, archivesize);\n\t\t\tmod->archive = FSZIP_LoadArchive(f, NULL, mod->name, mod->name, NULL);\n\t\t\tif (mod->archive)\n\t\t\t\tFS_LoadMapPackFile(mod->name, mod->archive);\t//give it to the filesystem to use.\n\t\t\telse\n\t\t\t\tVFS_CLOSE(f);\t//give up.\n\t\t}\n\t}\n}\n\n/*\n===================\nMod_ClearAll\n===================\n\ncalled before new content is loaded.\n*/\nstatic int mod_datasequence;\nvoid Mod_ClearAll (void)\n{\n#ifdef RUNTIMELIGHTING\n\tRelightTerminate(NULL);\n#endif\n\n\tmod_datasequence++;\n}\n\nqboolean Mod_PurgeModel(model_t\t*mod, enum mod_purge_e ptype)\n{\n\tif (mod->loadstate == MLS_LOADING)\n\t{\n\t\tif (ptype == MP_MAPCHANGED && !mod->submodelof)\n\t\t\treturn false;\t//don't bother waiting for it on map changes.\n\t\tCOM_WorkerPartialSync(mod, &mod->loadstate, MLS_LOADING);\n\t}\n\n#ifdef RUNTIMELIGHTING\n\tRelightTerminate(mod);\n#endif\n\n#ifdef TERRAIN\n\t//we can safely flush all terrain sections at any time\n\tif (mod->terrain)\n\t{\n\t\tif (ptype == MP_MAPCHANGED)\n\t\t\treturn false;\t//don't destroy any data there that the user might want to save. FIXME: handle better.\n\t\tTerr_PurgeTerrainModel(mod, false, true);\n\t}\n#endif\n\n\t//purge any vbos\n\tif (mod->type == mod_brush)\n\t{\n\t\tif (ptype == MP_FLUSH)\n\t\t\treturn false;\n#ifndef SERVERONLY\n\t\tSurf_Clear(mod);\n#endif\n\t}\n\n#ifdef TERRAIN\n\tif (mod->type == mod_brush || mod->type == mod_heightmap)\n\t\tTerr_FreeModel(mod);\n#endif\n\tif (mod->type == mod_alias)\n\t{\n\t\tMod_DestroyMesh(mod->meshinfo);\n\t\tmod->meshinfo = NULL;\n\t}\n\n\tMod_SetEntitiesString(mod, NULL, false);\n\n#ifdef PSET_SCRIPT\n\tPScript_ClearSurfaceParticles(mod);\n#endif\n\n\t//and obliterate anything else remaining in memory.\n\tmod->meshinfo = NULL;\n\tif (mod->archive)\n\t{\n\t\tFS_CloseMapPackFile(mod->archive);\n\t\tmod->archive = NULL;\n\t}\n\tZG_FreeGroup(&mod->memgroup);\n\tmod->loadstate = MLS_NOTLOADED;\n\n\tmod->submodelof = NULL;\n\tmod->pvs = NULL;\n\tmod->phs = NULL;\n\n#ifndef CLIENTONLY\n\tsv.world.lastcheckpvs = NULL;\t//if the server has that cached, flush it just in case.\n#endif\n\n\treturn true;\n}\n\n//can be called in one of two ways.\n//force=true: explicit flush. everything goes, even if its still in use.\n//force=false: map change. lots of stuff is no longer in use and can be freely flushed.\n//certain models cannot be safely flushed while still in use. such models will not be flushed even if forced (they may still be partially flushed).\nvoid Mod_Purge(enum mod_purge_e ptype)\n{\n\tint\t\ti;\n\tmodel_t\t*mod;\n\tqboolean unused;\n\n\tfor (i=0 , mod=mod_known ; i<mod_numknown ; i++, mod++)\n\t{\n\t\tunused = mod->datasequence != mod_datasequence;\n\n\t\tif (mod->loadstate == MLS_NOTLOADED)\n\t\t\tcontinue;\n\n\t\t//this model isn't active any more.\n\t\tif (unused || ptype != MP_MAPCHANGED)\n\t\t{\n\t\t\tif (unused)\n\t\t\t\tCon_DLPrintf(2, \"model \\\"%s\\\" no longer needed\\n\", mod->name);\n\t\t\tMod_PurgeModel(mod, (ptype==MP_FLUSH && unused)?MP_RESET:ptype);\n\t\t}\n#if defined(HALFLIFEMODELS) && defined(HAVE_CLIENT)\n\t\telse if (mod->type == mod_halflife)\n\t\t\tR_HalfLife_TouchTextures(mod);\n#endif\n\t}\n}\n\nvoid Mod_SetModifier(const char *modifier)\n{\n\tif (!modifier || strlen(modifier) >= sizeof(mod_modifier)) modifier = \"\";\n\tif (strcmp(modifier, mod_modifier))\n\t{\t//if the modifier changed, force all models to reset.\n\t\tCOM_WorkerFullSync();\t//sync all the workers, just in case.\n\t\tstrcpy(mod_modifier, modifier);\n\t\tMod_Purge(MP_RESET);\t//nuke it now\n\t}\n}\n\n#ifndef SERVERONLY\nvoid Mod_BSPX_Init(void);\n#endif\n\n/*\n===============\nMod_Init\n===============\n*/\nvoid Mod_Init (qboolean initial)\n{\n\tif (!mod_known)\n\t\tmod_known = malloc(MAX_MOD_KNOWN * sizeof(*mod_known));\n\tif (!initial)\n\t{\n\t\tMod_ClearAll();\t//shouldn't be needed\n\t\tMod_Purge(MP_RESET);//shouldn't be needed\n\t\tmod_numknown = 0;\n#ifdef Q1BSPS\n\t\tQ1BSP_Init();\n#endif\n\n\t\tCmd_AddCommand(\"mod_memlist\", Mod_MemList_f);\n#ifndef SERVERONLY\n\t\tCmd_AddCommand(\"mod_batchlist\", Mod_BatchList_f);\n\t\tCmd_AddCommand(\"mod_texturelist\", Mod_TextureList_f);\n\t\tCmd_AddCommand(\"mod_usetexture\", Mod_BlockTextureColour_f);\n#endif\n\t}\n\telse\n\t{\n\t\tCvar_Register(&mod_external_vis, \"Graphical Nicaties\");\n\t\tCvar_Register(&mod_warnmodels, \"Graphical Nicaties\");\n\t\tCvar_Register(&mod_litsprites_force, \"Graphical Nicaties\");\n\t\tCvar_Register(&mod_loadentfiles, NULL);\n\t\tCvar_Register(&mod_loadentfiles_dir, NULL);\n\t\tCvar_Register(&mod_loadmappackages, NULL);\n\t\tCvar_Register(&mod_lightscale_broken, NULL);\n\t\tCvar_Register(&mod_lightpoint_distance, NULL);\n\t\tCvar_Register (&r_meshpitch, \"Gamecode\");\n\t\tCvar_Register (&r_meshroll, \"Gamecode\");\n\t\tCvar_Register(&r_nolerp_list, \"Graphical Nicaties\");\n#ifdef RTLIGHTS\n\t\tCvar_Register(&r_noshadow_list, \"Graphical Nicaties\");\n#endif\n\t\tCmd_AddCommandD(\"sv_saveentfile\", Mod_SaveEntFile_f, \"Dumps a copy of the map's entities to disk, so that it can be edited and used as a replacement for slightly customised maps.\");\n\t\tCmd_AddCommandD(\"mod_showent\", Mod_ShowEnt_f, \"Allows you to quickly search through a map's entities.\");\n\t\tCmd_AddCommand(\"version_modelformats\", Mod_PrintFormats_f);\n\n#ifdef SPRMODELS\n\t\tCvar_Register (&r_sprite_backfacing, NULL);\n#endif\n#ifndef SERVERONLY\n\t\tMod_BSPX_Init();\n#endif\n\t}\n\n\tif (initial)\n\t{\n\t\tAlias_Register();\n\n#ifdef SPRMODELS\n\t\tMod_RegisterModelFormatMagic(NULL, \"Quake1 Sprite (spr)\",\t\t\tIDSPRITEHEADER,\t\t\t\t\t\t\tMod_LoadSpriteModel);\n#endif\n#ifdef SP2MODELS\n\t\tMod_RegisterModelFormatMagic(NULL, \"Quake2 Sprite (sp2)\",\t\t\tIDSPRITE2HEADER,\t\t\t\t\t\tMod_LoadSprite2Model);\n#endif\n\n\t\t//q2/q3bsps\n#ifdef Q3BSPS\n\t\tMod_RegisterModelFormatMagic(NULL, \"RTCW Map (bsp)\",\t\t\t\t\"IBSP\\57\\0\\0\\0\",8,\t\t\t\t\t\tMod_LoadQ2BrushModel);\n\t\tMod_RegisterModelFormatMagic(NULL, \"Quake3 Map (bsp)\",\t\t\t\t\"IBSP\\56\\0\\0\\0\",8,\t\t\t\t\t\tMod_LoadQ2BrushModel);\n#endif\n#ifdef Q2BSPS\n\t\tMod_RegisterModelFormatMagic(NULL, \"Quake2 Map (bsp)\",\t\t\t\t\"IBSP\\46\\0\\0\\0\",8,\t\t\t\t\t\tMod_LoadQ2BrushModel);\n\t\tMod_RegisterModelFormatMagic(NULL, \"Quake2World Map (bsp)\",\t\t\t\"IBSP\\105\\0\\0\\0\",8,\t\t\t\t\t\tMod_LoadQ2BrushModel);\n\t\tMod_RegisterModelFormatMagic(NULL, \"Qbism (Quake2) Map (bsp)\",\t\t\"QBSP\\46\\0\\0\\0\",8,\t\t\t\t\t\tMod_LoadQ2BrushModel);\n#endif\n#ifdef RFBSPS\n\t\tMod_RegisterModelFormatMagic(NULL, \"Raven Map (bsp)\",\t\t\t\t\"RBSP\\1\\0\\0\\0\",8,\tMod_LoadQ2BrushModel);\n\t\tMod_RegisterModelFormatMagic(NULL, \"QFusion Map (bsp)\",\t\t\t\t\"FBSP\\1\\0\\0\\0\",8,\tMod_LoadQ2BrushModel);\n#endif\n\n\t\t//doom maps\n#ifdef MAP_DOOM\n\t\tMod_RegisterModelFormatMagic(NULL, \"Doom IWad Map\",\t\t\t\t\t\"IWAD\",4,\t\tMod_LoadDoomLevel);\n\t\tMod_RegisterModelFormatMagic(NULL, \"Doom PWad Map\",\t\t\t\t\t\"PWAD\",4,\t\tMod_LoadDoomLevel);\n#endif\n\n#ifdef MAP_PROC\n\t\tMod_RegisterModelFormatText(NULL, \"Doom3 (cm)\",\t\t\t\t\t\t\"CM\",\t\t\t\t\t\t\t\t\tD3_LoadMap_CollisionMap);\n#endif\n\n#ifdef Q1BSPS\n\t\t//q1-based formats\n\t\tMod_RegisterModelFormatMagic(NULL, \"Quake1 2PSB Map (bsp)\",\t\t\tBSPVERSION_LONG1,\t\t\t\t\t\tMod_LoadBrushModel);\n\t\tMod_RegisterModelFormatMagic(NULL, \"Quake1 BSP2 Map (bsp)\",\t\t\tBSPVERSION_LONG2,\t\t\t\t\t\tMod_LoadBrushModel);\n\t\tMod_RegisterModelFormatMagic(NULL, \"Half-Life Map (bsp)\",\t\t\tBSPVERSIONHL,\t\t\t\t\t\t\tMod_LoadBrushModel);\n\t\tMod_RegisterModelFormatMagic(NULL, \"Quake1 Map (bsp)\",\t\t\t\tBSPVERSION,\t\t\t\t\t\t\t\tMod_LoadBrushModel);\n\t\tMod_RegisterModelFormatMagic(NULL, \"Quake1 Prerelease Map (bsp)\",\tBSPVERSIONPREREL,\t\t\t\t\t\tMod_LoadBrushModel);\n\t\tMod_RegisterModelFormatMagic(NULL, \"Quake 64 Remastered Map (bsp)\", BSPVERSIONQ64,\t\t\t\t\t\t\tMod_LoadBrushModel);\n#endif\n\t}\n}\n\nvoid Mod_Shutdown (qboolean final)\n{\n\tif (final)\n\t{\n\t\tMod_ClearAll();\n\t\tMod_Purge(MP_RESET);\n\n\t\tMod_UnRegisterAllModelFormats(NULL);\n\t}\n\telse\n\t{\n\t\tMod_ClearAll();\n\t\tMod_Purge(MP_RESET);\n\n\t\tCmd_RemoveCommand(\"mod_memlist\");\n\t\tCmd_RemoveCommand(\"mod_batchlist\");\n\t\tCmd_RemoveCommand(\"mod_texturelist\");\n\t\tCmd_RemoveCommand(\"mod_usetexture\");\n\t}\n\tfree(mod_known);\n\tmod_known = NULL;\n\tmod_numknown = 0;\n\n#ifndef SERVERONLY\n\tr_worldentity.model = NULL;\t//just in case.\n\tcl_numvisedicts = 0;\t//make sure nothing gets cached.\n#endif\n}\n\n/*\n===============\nMod_Init\n\nCaches the data if needed\n===============\n*/\nvoid *Mod_Extradata (model_t *mod)\n{\n\tvoid\t*r;\n\t\n\tr = mod->meshinfo;\n\tif (r)\n\t\treturn r;\n\n\tMod_LoadModel (mod, MLV_ERROR);\n\t\n\tif (!mod->meshinfo)\n\t\tSys_Error (\"Mod_Extradata: caching failed\");\n\treturn mod->meshinfo;\n}\n\nconst char *Mod_FixName(const char *modname, const char *worldname)\n{\n\tif (*modname == '*' && worldname && *worldname)\n\t{\n\t\t//make sure that the value is an inline value with no existing extra postfix or anything.\n\t\tchar *e;\n\t\tif (strtoul(modname+1, &e, 10) != 0)\n\t\t\tif (!*e)\n\t\t\t\treturn va(\"%s:%s\", modname, worldname);\n\t}\n\treturn modname;\n}\n\n//Called when the given file was (re)written.\n//\nvoid Mod_FileWritten (const char *filename)\n{\n\tint\t\ti;\n\tmodel_t\t*mod;\n\tfor (i=0 , mod=mod_known ; i<mod_numknown ; i++, mod++)\n\t\tif (!strcmp (mod->publicname, filename) )\n\t\t{\n\t\t\tif (mod->loadstate != MLS_NOTLOADED)\n\t\t\t\tMod_PurgeModel(mod, MP_RESET);\n\t\t}\n}\n\n/*\n==================\nMod_FindName\n\n==================\n*/\nmodel_t *Mod_FindName (const char *name)\n{\n\tint\t\ti;\n\tmodel_t\t*mod;\n\t\n//\tif (!name[0])\n//\t\tSys_Error (\"Mod_ForName: NULL name\");\n\t\t\n//\n// search the currently loaded models\n//\n\tfor (i=0 , mod=mod_known ; i<mod_numknown ; i++, mod++)\n\t\tif (!strcmp (mod->publicname, name) )\n\t\t\tbreak;\n\t\t\t\n\tif (i == mod_numknown)\n\t{\n#ifdef LOADERTHREAD\n\t\tSys_LockMutex(com_resourcemutex);\n\t\tfor (i=0 , mod=mod_known ; i<mod_numknown ; i++, mod++)\n\t\t\tif (!strcmp (mod->publicname, name) )\n\t\t\t\tbreak;\n\t\tif (i == mod_numknown)\n\t\t{\n#endif\n\t\t\tif (mod_numknown == MAX_MOD_KNOWN)\n\t\t\t{\n#ifdef LOADERTHREAD\n\t\t\t\tSys_UnlockMutex(com_resourcemutex);\n#endif\n\t\t\t\tSys_Error (\"mod_numknown == MAX_MOD_KNOWN\");\n\t\t\t\treturn NULL;\n\t\t\t}\n\t\t\tif (strlen(name) >= sizeof(mod->publicname))\n\t\t\t{\n#ifdef LOADERTHREAD\n\t\t\t\tSys_UnlockMutex(com_resourcemutex);\n#endif\n\t\t\t\tSys_Error (\"model name is too long: %s\", name);\n\t\t\t\treturn NULL;\n\t\t\t}\n\t\t\tmemset(mod, 0, sizeof(model_t));\t//clear the old model as the renderers use the same globals\n\t\t\tQ_strncpyz (mod->publicname, name, sizeof(mod->publicname));\n\t\t\tQ_strncpyz (mod->name, name, sizeof(mod->name));\n\t\t\tmod->loadstate = MLS_NOTLOADED;\n\t\t\tmod_numknown++;\n\t\t\tmod->particleeffect = -1;\n\t\t\tmod->particletrail = -1;\n#ifdef LOADERTHREAD\n\t\t}\n\t\tSys_UnlockMutex(com_resourcemutex);\n#endif\n\t}\n\n//\tif (mod->loadstate == MLS_FAILED)\n//\t\tmod->loadstate = MLS_NOTLOADED;\n\n\t//mark it as active, so it doesn't get flushed prematurely\n\tmod->datasequence = mod_datasequence;\n\treturn mod;\n}\n\n/*\n==================\nMod_TouchModel\n\n==================\n*/\nvoid Mod_TouchModel (const char *name)\n{\n\t//findname does this anyway.\n\tMod_FindName (name);\n}\n\nstatic struct\n{\n\tvoid *module;\n\tchar *formatname;\n\tchar *ident;\n\tqbyte *magic;\n\tsize_t magicsize;\n\tqboolean (QDECL *load) (model_t *mod, void *buffer, size_t buffersize);\n} modelloaders[64];\n\nstatic void Mod_PrintFormats_f(void)\n{\n\tint i;\n\tfor (i = 0; i < sizeof(modelloaders)/sizeof(modelloaders[0]); i++)\n\t{\n\t\tif (modelloaders[i].load && modelloaders[i].formatname)\n\t\t\tCon_Printf(\"%s\\n\", modelloaders[i].formatname);\n\t}\n}\n\nint Mod_RegisterModelFormatText(void *module, const char *formatname, char *magictext, qboolean (QDECL *load) (model_t *mod, void *buffer, size_t fsize))\n{\n\tint i, free = -1;\n\tfor (i = 0; i < sizeof(modelloaders)/sizeof(modelloaders[0]); i++)\n\t{\n\t\tif (modelloaders[i].ident && !strcmp(modelloaders[i].ident, magictext))\n\t\t{\n\t\t\tfree = i;\n\t\t\tbreak;\t//extension match always replaces\n\t\t}\n\t\telse if (!modelloaders[i].load && free < 0)\n\t\t\tfree = i;\n\t}\n\tif (free < 0)\n\t\treturn 0;\n\n\tmodelloaders[free].module = module;\n\tmodelloaders[free].formatname = Z_StrDup(formatname);\n\tmodelloaders[free].magic = NULL;\n\tmodelloaders[free].magicsize = 0;\n\tmodelloaders[free].ident = Z_StrDup(magictext);\n\tmodelloaders[free].load = load;\n\n\treturn free+1;\n}\nint Mod_RegisterModelFormatMagic(void *module, const char *formatname, qbyte *magic, size_t magicsize, qboolean (QDECL *load) (model_t *mod, void *buffer, size_t fsize))\n{\n\tint i, free = -1;\n\tfor (i = 0; i < sizeof(modelloaders)/sizeof(modelloaders[0]); i++)\n\t{\n\t\tif (modelloaders[i].magic && modelloaders[i].magicsize == magicsize && !memcmp(modelloaders[i].magic, magic, magicsize))\n\t\t{\n\t\t\tfree = i;\n\t\t\tbreak;\t//extension match always replaces\n\t\t}\n\t\telse if (!modelloaders[i].load && free < 0)\n\t\t\tfree = i;\n\t}\n\tif (free < 0)\n\t\treturn 0;\n\n\tmodelloaders[free].module = module;\n\tif (modelloaders[free].formatname)\n\t\tZ_Free(modelloaders[free].formatname);\n\tmodelloaders[free].formatname = Z_StrDup(formatname);\n\tmodelloaders[free].magic = magic;\n\tmodelloaders[free].magicsize = magicsize;\n\tmodelloaders[free].ident = NULL;\n\tmodelloaders[free].load = load;\n\n\treturn free+1;\n}\n\nvoid Mod_UnRegisterModelFormat(void *module, int idx)\n{\n\t\n\tidx--;\n\tif ((unsigned int)(idx) >= sizeof(modelloaders)/sizeof(modelloaders[0]))\n\t\treturn;\n\tif (modelloaders[idx].module != module)\n\t\treturn;\n\n\tCOM_WorkerFullSync();\n\tZ_Free(modelloaders[idx].ident);\n\tmodelloaders[idx].ident = NULL;\n\tZ_Free(modelloaders[idx].formatname);\n\tmodelloaders[idx].formatname = NULL;\n\tmodelloaders[idx].magic = 0;\n\tmodelloaders[idx].load = NULL;\n\tmodelloaders[idx].module = NULL;\n\n\t//FS_Restart will be needed\n}\n\nvoid Mod_UnRegisterAllModelFormats(void *module)\n{\n\tint i;\n\tCOM_WorkerFullSync();\n\tfor (i = 0; i < sizeof(modelloaders)/sizeof(modelloaders[0]); i++)\n\t{\n\t\tif (modelloaders[i].module == module)\n\t\t\tMod_UnRegisterModelFormat(module, i+1);\n\t}\n}\n\n//main thread. :(\nvoid Mod_ModelLoaded(void *ctx, void *data, size_t a, size_t b)\n{\n\tqboolean previouslyfailed;\n\tmodel_t *mod = ctx;\n\tenum mlverbosity_e verbose = b;\n#ifndef SERVERONLY\n\tP_LoadedModel(mod);\n#endif\n\n\tpreviouslyfailed = mod->loadstate == MLS_FAILED;\n\tmod->loadstate = a;\n\n#ifdef TERRAIN\n\tif (mod->terrain)\n\t\tTerr_FinishTerrain(mod);\n#endif\n#ifndef SERVERONLY\n\tif (mod->type == mod_brush)\n\t\tSurf_BuildModelLightmaps(mod);\n\tif (mod->type == mod_sprite)\n\t{\n\t\tMod_LoadSpriteShaders(mod);\n\t}\n\tif (mod->type == mod_alias)\n\t{\n\t\tif (qrenderer != QR_NONE)\n\t\t\tMod_LoadAliasShaders(mod);\n\t}\n\n#ifdef RAGDOLL\n\tif (mod->type == mod_alias || mod->type == mod_halflife)\n\t{\n\t\tint numbones = Mod_GetNumBones(mod, false);\n\t\tif (numbones)\n\t\t{\n\t\t\tsize_t filesize;\n\t\t\tchar *buf;\n\t\t\tchar dollname[MAX_QPATH];\n\t\t\tQ_snprintfz(dollname, sizeof(dollname), \"%s.doll\", mod->name);\n\t\t\tbuf = FS_LoadMallocFile(dollname, &filesize);\n\t\t\tif (buf)\n\t\t\t{\n\t\t\t\tmod->dollinfo = rag_createdollfromstring(mod, dollname, numbones, buf);\n\t\t\t\tBZ_Free(buf);\n\t\t\t}\n\t\t}\n\t}\n#endif\n\n#endif\n\n\tswitch(verbose)\n\t{\n\tdefault:\n\tcase MLV_ERROR:\n\t\tHost_EndGame (\"Mod_NumForName: %s not found or couldn't load\", mod->name);\n\t\tbreak;\n\tcase MLV_WARNSYNC:\n\tcase MLV_WARN:\n\t\tif (*mod->name != '*' && strcmp(mod->name, \"null\") && mod_warnmodels.ival && !previouslyfailed)\n\t\t\tCon_Printf(CON_ERROR \"Unable to load %s\\n\", mod->name);\n\t\tbreak;\n\tcase MLV_SILENT:\n\tcase MLV_SILENTSYNC:\n\t\tbreak;\n\t}\n}\nvoid Mod_SubmodelLoaded(model_t *mod, int state)\n{\n\tCOM_AddWork(WG_MAIN, Mod_ModelLoaded, mod, NULL, MLS_LOADED, 0);\n}\n/*\n==================\nMod_LoadModel\n\nLoads a model into the cache\n==================\n*/\nstatic void Mod_LoadModelWorker (void *ctx, void *data, size_t a, size_t b)\n{\n\tmodel_t *mod = ctx;\n\tenum mlverbosity_e verbose = a;\n\tunsigned *buf = NULL;\n\tchar mdlbase[MAX_QPATH];\n\tchar *replstr;\n#ifdef DSPMODELS\n\tqboolean doomsprite = false;\n#endif\n\tunsigned int i;\n\tsize_t filesize;\n\tchar ext[8];\n\tint basedepth;\n\n\t//clear out any old state.\n\tmemset(&mod->loadstate+1, 0, sizeof(*mod) - (qintptr_t)(&((model_t*)NULL)->loadstate+1));\n\n\tif (!*mod->publicname)\n\t{\n\t\tmod->type = mod_dummy;\n\t\tmod->mins[0] = -16;\n\t\tmod->mins[1] = -16;\n\t\tmod->mins[2] = -16;\n\t\tmod->maxs[0] = 16;\n\t\tmod->maxs[1] = 16;\n\t\tmod->maxs[2] = 16;\n\t\tmod->engineflags = 0;\n\t\tCOM_AddWork(WG_MAIN, Mod_ModelLoaded, mod, NULL, MLS_LOADED, 0);\n\t\treturn;\n\t}\n\t\n#ifdef RAGDOLL\n\tif (mod->dollinfo)\n\t{\n\t\trag_freedoll(mod->dollinfo);\n\t\tmod->dollinfo = NULL;\n\t}\n#endif\n\n\tif (mod->loadstate == MLS_FAILED)\n\t\treturn;\n\n//\n// load the file\n//\n\t// set necessary engine flags for loading purposes\n\tif (!strcmp(mod->publicname, \"progs/player.mdl\"))\n\t\tmod->engineflags |= MDLF_PLAYER | MDLF_DOCRC;\n\telse if (!strcmp(mod->publicname, \"progs/flame.mdl\")\n\t\t|| !strcmp(mod->publicname, \"progs/flame2.mdl\")\n#ifdef HEXEN2\n\t\t|| !strcmp(mod->publicname, \"models/flame1.mdl\")\t//hexen2 small standing flame\n\t\t|| !strcmp(mod->publicname, \"models/flame2.mdl\")\t//hexen2 large standing flame\n\t\t|| !strcmp(mod->publicname, \"models/cflmtrch.mdl\")\t//hexen2 wall torch\n#endif\n\t\t\t)\n\t\tmod->engineflags |= MDLF_FLAME|MDLF_NOSHADOWS;\n\telse if (!strcmp(mod->publicname, \"progs/bolt.mdl\")\n\t\t|| !strcmp(mod->publicname, \"progs/bolt2.mdl\")\n\t\t|| !strcmp(mod->publicname, \"progs/bolt3.mdl\")\n\t\t|| !strcmp(mod->publicname, \"progs/beam.mdl\")\n#ifdef HEXEN2\n\t\t|| !strcmp(mod->publicname, \"models/stsunsf2.mdl\")\n\t\t|| !strcmp(mod->publicname, \"models/stsunsf1.mdl\")\n\t\t|| !strcmp(mod->publicname, \"models/stice.mdl\")\n#endif\n\t\t\t )\n\t\tmod->engineflags |= MDLF_NOSHADOWS;\n\telse if (!strcmp(mod->publicname, \"progs/backpack.mdl\"))\n\t\tmod->engineflags |= MDLF_NOTREPLACEMENTS;\n\telse if (!strcmp(mod->publicname, \"progs/eyes.mdl\"))\n\t\tmod->engineflags |= MDLF_NOTREPLACEMENTS|MDLF_DOCRC;\n\n\t/*handle ezquake-originated cheats that would feck over fte users if fte didn't support\n\tthese are the conditions required for r_fb_models on non-players*/\n\tmod->engineflags |= MDLF_EZQUAKEFBCHEAT;\n\tif ((mod->engineflags & MDLF_DOCRC) ||\n\t\t!strcmp(mod->publicname, \"progs/backpack.mdl\") ||\n\t\t!strcmp(mod->publicname, \"progs/gib1.mdl\") ||\n\t\t!strcmp(mod->publicname, \"progs/gib2.mdl\") ||\n\t\t!strcmp(mod->publicname, \"progs/gib3.mdl\") ||\n\t\t!strcmp(mod->publicname, \"progs/h_player.mdl\") ||\n\t\t!strncmp(mod->publicname, \"progs/v_\", 8))\n\t\tmod->engineflags &= ~MDLF_EZQUAKEFBCHEAT;\n\n\tmod->engineflags |= MDLF_RECALCULATERAIN;\n\n\t// get string used for replacement tokens\n\tCOM_FileExtension(mod->publicname, ext, sizeof(ext));\n\tif (!Q_strcasecmp(ext, \"spr\") || !Q_strcasecmp(ext, \"sp2\"))\n\t\treplstr = \"\"; // sprite\n#ifdef DSPMODELS\n\telse if (!Q_strcasecmp(ext, \"dsp\")) // doom sprite\n\t{\n\t\treplstr = \"\";\n\t\tdoomsprite = true;\n\t}\n#endif\n\telse // assume models\n\t\treplstr = r_replacemodels.string;\n\n\t// gl_load24bit 0 disables all replacements\n\tif (!gl_load24bit.value)\n\t{\n\t\treplstr = \"\";\n\t\tbasedepth = FDEPTH_MISSING;\n\t}\n\telse\n\t\tbasedepth = COM_FDepthFile(mod->publicname, true);\n\n\tCOM_StripExtension(mod->publicname, mdlbase, sizeof(mdlbase));\n\n\twhile (replstr)\n\t{\n\t\tchar token[256];\n\t\treplstr = COM_ParseStringSet(replstr, token, sizeof(token));\n\n\t\tif (replstr)\n\t\t{\n\t\t\tchar altname[MAX_QPATH], *sl;\n\t\t\tsl = strchr(token, '/');\n\t\t\tif (sl)\n\t\t\t{\t//models/name.mdl -> path/preslash/name.postslash\n\t\t\t\tchar *p = COM_SkipPath(mdlbase);\n\t\t\t\tsize_t ofs = (p-mdlbase) + (sl+1-token);\n\t\t\t\tmemcpy(altname, mdlbase, p-mdlbase);\n\t\t\t\tmemcpy(altname+(p-mdlbase), token, sl+1-token);\n\t\t\t\tif (ofs + strlen(p)+strlen(sl+1)+2 > sizeof(altname))\n\t\t\t\t\tcontinue;\t//erk\n\t\t\t\tQ_snprintfz(altname + ofs, sizeof(altname)-ofs, \"%s.%s\", p, sl+1);\n\t\t\t}\n\t\t\telse\n\t\t\t\tQ_snprintfz(altname, sizeof(altname), \"%s.%s\", mdlbase, token);\n\n\t\t\tif (COM_FDepthFile(altname, true) > basedepth)\n\t\t\t\tcontinue;\n\n\t\t\tTRACE((\"Mod_LoadModel: Trying to load (replacement) model \\\"%s\\\"\\n\", altname));\n\t\t\tbuf = (unsigned *)FS_LoadMallocGroupFile(NULL, altname, &filesize, true);\n\n\t\t\tif (buf)\n\t\t\t\tQ_strncpyz(mod->name, altname, sizeof(mod->name));\n\t\t}\n\t\telse\n\t\t{\n\t\t\tTRACE((\"Mod_LoadModel: Trying to load model \\\"%s\\\"\\n\", mod->publicname));\n\t\t\tbuf = (unsigned *)FS_LoadMallocGroupFile(NULL, mod->publicname, &filesize, true);\n\t\t\tif (buf)\n\t\t\t\tQ_strncpyz(mod->name, mod->publicname, sizeof(mod->name));\n\t\t\telse if (!buf)\n\t\t\t{\n#ifdef DSPMODELS\n\t\t\t\tif (doomsprite) // special case needed for doom sprites\n\t\t\t\t{\n\t\t\t\t\tTRACE((\"Mod_LoadModel: doomsprite: \\\"%s\\\"\\n\", mod->name));\n\t\t\t\t\tMod_LoadDoomSprite(mod);\n\t\t\t\t\tBZ_Free(buf);\n\t\t\t\t\tCOM_AddWork(WG_MAIN, Mod_ModelLoaded, mod, NULL, MLS_LOADED, 0);\n\t\t\t\t\treturn;\n\t\t\t\t}\n#endif\n#ifdef TERRAIN\n\t\t\t\tif (!Q_strcasecmp(ext, \"map\"))\n\t\t\t\t{\n\t\t\t\t\tconst char *dummymap =\n\t\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\t\"classname worldspawn\\n\"\n\t\t\t\t\t\t\t\"wad \\\"base.wad\\\"\\n\"\t//we ARE a quake engine after all, and default.wad is generally wrong\n\t\t\t\t\t\t\t\"message \\\"Unnamed map\\\"\\n\"\n\t\t\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\t\t\"(-128  128 0)\t( 128  128 0)\t( 128 -128 0)\t\\\"WBRICK1_5\\\" 0 0 0 1 1\\n\"\n\t\t\t\t\t\t\t\t\"( 128 -128 -16)( 128  128 -16)\t(-128  128 -16)\t\\\"WBRICK1_5\\\" 0 0 0 1 1\\n\"\n\t\t\t\t\t\t\t\t\"( 128  128 0)\t(-128  128 0)\t(-128  128 -16)\t\\\"WBRICK1_5\\\" 0 0 0 1 1\\n\"\n\t\t\t\t\t\t\t\t\"(-128 -128 0)\t( 128 -128 0)\t( 128 -128 -16)\t\\\"WBRICK1_5\\\" 0 0 0 1 1\\n\"\n\t\t\t\t\t\t\t\t\"(-128  128 0)\t(-128 -128 0)\t(-128 -128 -16)\t\\\"WBRICK1_5\\\" 0 0 0 1 1\\n\"\n\t\t\t\t\t\t\t\t\"( 128 -128 0)\t( 128  128 0)\t( 128  128 -16)\t\\\"WBRICK1_5\\\" 0 0 0 1 1\\n\"\n\t\t\t\t\t\t\t\"}\\n\"\n\t\t\t\t\t\t\"}\\n\"\n\t\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\t\"classname info_player_start\\n\"\n\t\t\t\t\t\t\t\"origin \\\"0 0 24\\\"\\n\"\n\t\t\t\t\t\t\"}\\n\"\n\t\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\t\"classname light\\n\"\n\t\t\t\t\t\t\t\"origin \\\"0 0 64\\\"\\n\"\n\t\t\t\t\t\t\"}\\n\";\n\t\t\t\t\tbuf = (unsigned*)Z_StrDup(dummymap);\n\t\t\t\t\tfilesize = strlen(dummymap);\n\t\t\t\t}\n\t\t\t\telse\n#endif\n\t\t\t\t\tbreak; // failed to load unreplaced file and nothing left\n\t\t\t}\n\t\t}\n\t\tif (!buf)\n\t\t\tcontinue;\n\t\tif (filesize < 4)\n\t\t{\n\t\t\tBZ_Free(buf);\n\t\t\tcontinue;\n\t\t}\n\n//\n// fill it in\n//\n\t\tif (!Mod_DoCRC(mod, (char*)buf, filesize))\n\t\t{\n\t\t\tBZ_Free(buf);\n\t\t\tcontinue;\n\t\t}\n\n\t\tmemset(&mod->funcs, 0, sizeof(mod->funcs));\t//just in case...\n\n\t\t//look for known extensions first, to try to avoid issues with specific formats\n\t\tfor(i = 0; i < countof(modelloaders); i++)\n\t\t{\n\t\t\tif (modelloaders[i].load && modelloaders[i].ident && *modelloaders[i].ident == '.' && !Q_strcasecmp(modelloaders[i].ident, COM_GetFileExtension(mod->name, NULL)))\n\t\t\t\tbreak;\n\t\t}\n\t\t//now look to see if we can find one with the right magic header\n\t\tif (i == countof(modelloaders))\n\t\t{\n\t\t\tfor(i = 0; i < countof(modelloaders); i++)\n\t\t\t{\n\t\t\t\tif (modelloaders[i].load && modelloaders[i].magic && filesize >= modelloaders[i].magicsize && !memcmp(buf, modelloaders[i].magic, modelloaders[i].magicsize) && !modelloaders[i].ident)\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (i < countof(modelloaders))\n\t\t{\n\t\t\tif (!modelloaders[i].load(mod, buf, filesize))\n\t\t\t{\n\t\t\t\tBZ_Free(buf);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tCOM_ParseOut((char*)buf, token, sizeof(token));\n\t\t\tfor(i = 0; i < sizeof(modelloaders) / sizeof(modelloaders[0]); i++)\n\t\t\t{\n\t\t\t\tif (modelloaders[i].load && modelloaders[i].ident && !strcmp(modelloaders[i].ident, token))\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (i < sizeof(modelloaders) / sizeof(modelloaders[0]))\n\t\t\t{\n\t\t\t\tif (!modelloaders[i].load(mod, buf, filesize))\n\t\t\t\t{\n\t\t\t\t\tBZ_Free(buf);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tCon_Printf(CON_WARNING \"Unrecognised model format %c%c%c%c\\n\", ((char*)buf)[0], ((char*)buf)[1], ((char*)buf)[2], ((char*)buf)[3]);\n\t\t\t\tBZ_Free(buf);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\n\t\tTRACE((\"Mod_LoadModel: Loaded\\n\"));\n\n\t\tBZ_Free(buf);\n\n\t\tCOM_AddWork(WG_MAIN, Mod_ModelLoaded, mod, NULL, MLS_LOADED, 0);\n\t\treturn;\n\t}\n\n\tmod->type = mod_dummy;\n\tmod->mins[0] = -16;\n\tmod->mins[1] = -16;\n\tmod->mins[2] = -16;\n\tmod->maxs[0] = 16;\n\tmod->maxs[1] = 16;\n\tmod->maxs[2] = 16;\n\tmod->engineflags = 0;\n\tCOM_AddWork(WG_MAIN, Mod_ModelLoaded, mod, NULL, MLS_FAILED, verbose);\n}\n\n\nmodel_t *Mod_LoadModel (model_t *mod, enum mlverbosity_e verbose)\n{\n\tif (mod->loadstate == MLS_NOTLOADED && *mod->name != '*')\n\t{\n\t\tconst char *s = strstr(r_nolerp_list.string, mod->publicname);\n\t\tCOM_AssertMainThread(\"Mod_LoadModel\");\n\t\tif (s)\n\t\t{\n\t\t\tsize_t l = strlen(mod->publicname);\n\t\t\tif ((s == r_nolerp_list.string || s[-1]==',') && (s[l] == 0 || s[l] == ','))\n\t\t\t\tmod->engineflags |= MDLF_NOLERP;\n\t\t}\n#ifdef RTLIGHTS\n\t\ts = strstr(r_noshadow_list.string, mod->publicname);\n\t\tif (s)\n\t\t{\n\t\t\tsize_t l = strlen(mod->publicname);\n\t\t\tif ((s == r_noshadow_list.string || s[-1]==',') && (s[l] == 0 || s[l] == ','))\n\t\t\t\tmod->engineflags |= MDLF_NOSHADOWS;\n\t\t}\n#endif\n\n\t\tmod->loadstate = MLS_LOADING;\n\t\tif (verbose == MLV_ERROR || verbose == MLV_WARNSYNC)\n\t\t\tCOM_InsertWork(WG_LOADER, Mod_LoadModelWorker, mod, NULL, verbose, 0);\n\t\telse\n\t\t\tCOM_AddWork(WG_LOADER, Mod_LoadModelWorker, mod, NULL, verbose, 0);\n\t}\n\n\t//block until its loaded, if we care.\n\tif (mod->loadstate == MLS_LOADING && (verbose == MLV_ERROR || verbose == MLV_WARNSYNC || verbose == MLV_SILENTSYNC))\n\t\tCOM_WorkerPartialSync(mod, &mod->loadstate, MLS_LOADING);\n\n\tif (verbose == MLV_ERROR)\n\t{\n\t\t//someone already tried to load it without caring if it failed or not. make sure its loaded.\n\t\t//fixme: this is a spinloop.\n\n\t\tif (mod->loadstate != MLS_LOADED)\n\t\t\tHost_EndGame (\"Mod_NumForName: %s not found or couldn't load\", mod->name);\n\t}\n\treturn mod;\n}\n\n/*\n==================\nMod_ForName\n\nLoads in a model for the given name\n==================\n*/\nmodel_t *Mod_ForName (const char *name, enum mlverbosity_e verbosity)\n{\n\tmodel_t\t*mod;\n\t\n\tmod = Mod_FindName (name);\n\t\n\treturn Mod_LoadModel (mod, verbosity);\n}\n\n\n/*\n===============================================================================\n\n\t\t\t\t\tBRUSHMODEL LOADING\n\n===============================================================================\n*/\n\n#if !defined(SERVERONLY)\nstatic const struct\n{\n\tconst char *oldname;\n\tunsigned int chksum;\t//xor-compacted md4\n\tconst char *newname;\n} buggytextures[] =\n{\n\t//FIXME: we should load this table from disk or something.\n\t//old\t\t\tsum\t\t\tnew\n\t{\"metal5_2\",\t0x45d110ec,\t\"metal5_2_arc\"},\n\t{\"metal5_2\",\t0x0d275f87,\t\"metal5_2_x\"},\n\t{\"metal5_4\",\t0xf8e27da8,\t\"metal5_4_arc\"},\n\t{\"metal5_4\",\t0xa301c52e,\t\"metal5_4_double\"},\n\t{\"metal5_8\",\t0xfaa8bf77,\t\"metal5_8_back\"},\n\t{\"metal5_8\",\t0x88792923,\t\"metal5_8_rune\"},\n\t{\"plat_top1\",\t0xfe4f9f5a,\t\"plat_top1_bolt\"},\n\t{\"plat_top1\",\t0x9ac3fccf,\t\"plat_top1_cable\"},\n\t{\"sky4\",\t\t0xde688b77,\t\"sky1\"},\n//\t{\"sky4\",\t\t0x8a010dc0,\t\"sky4\"},\n//\t{\"window03\",\t?,\t\t\t\"window03_?\"},\n//\t{\"window03\",\t?,\t\t\t\"window03_?\"},\n\n\n\t//FIXME: hexen2 has the same issue.\n};\nstatic const char *Mod_RemapBuggyTexture(const char *name, const qbyte *data, unsigned int datalen)\n{\n\tunsigned int i;\n\tif (!data)\n\t\treturn NULL;\n\tfor (i = 0; i < sizeof(buggytextures)/sizeof(buggytextures[0]); i++)\n\t{\n\t\tif (!strcmp(name, buggytextures[i].oldname))\n\t\t{\n\t\t\tunsigned int sum = CalcHashInt(&hash_md4, data, datalen);\n\t\t\tfor (; i < sizeof(buggytextures)/sizeof(buggytextures[0]); i++)\n\t\t\t{\n\t\t\t\tif (strcmp(name, buggytextures[i].oldname))\n\t\t\t\t\tbreak;\n\t\t\t\tif (sum == buggytextures[i].chksum)\n\t\t\t\t\treturn buggytextures[i].newname;\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t}\n\treturn NULL;\n}\n\nstatic void Mod_FinishTexture(model_t *mod, texture_t *tx, const char *loadname, qboolean safetoloadfromwads)\n{\n\textern cvar_t gl_shadeq1_name;\n\tchar altname[MAX_QPATH];\n\tchar *star;\n\tconst char *origname = NULL;\n\tconst char *shadername = tx->name;\n\n\tif (!safetoloadfromwads || !tx->shader)\n\t{\n\t\t//remap to avoid bugging out on textures with the same name and different images (vanilla content sucks)\n\t\tshadername = Mod_RemapBuggyTexture(shadername, tx->srcdata, tx->srcwidth*tx->srcheight);\n\t\tif (shadername)\n\t\t\torigname = tx->name;\n\t\telse\n\t\t\tshadername = tx->name;\n\n\t\t//find the *\n\t\tif (!*gl_shadeq1_name.string || !strcmp(gl_shadeq1_name.string, \"*\"))\n\t\t\t;\n\t\telse if (!(star = strchr(gl_shadeq1_name.string, '*')) || (strlen(gl_shadeq1_name.string)+strlen(tx->name)+1>=sizeof(altname)))\t//it's got to fit.\n\t\t\tshadername = gl_shadeq1_name.string;\n\t\telse\n\t\t{\n\t\t\tstrncpy(altname, gl_shadeq1_name.string, star-gl_shadeq1_name.string);\t//copy the left\n\t\t\taltname[star-gl_shadeq1_name.string] = '\\0';\n\t\t\tstrcat(altname, shadername);\t//insert the *\n\t\t\tstrcat(altname, star+1);\t//add any final text.\n\t\t\tshadername = altname;\n\t\t}\n\n\t\ttx->shader = R_RegisterCustom (mod, shadername, SUF_LIGHTMAP, Shader_DefaultBSPQ1, NULL);\n\n\t\tif (!tx->srcdata && !safetoloadfromwads)\n\t\t\treturn;\n\t}\n\telse\n\t{\t//already loaded. don't waste time / crash (this will be a dead pointer).\n\t\tif (tx->srcdata)\n\t\t\treturn;\n\t}\n\n\tif (!strncmp(tx->name, \"sky\", 3))\n\t\tR_InitSky (tx->shader, shadername, tx->srcfmt, tx->srcdata, tx->srcwidth, tx->srcheight);\n\telse\n\t{\n\t\tunsigned int maps = 0;\n\t\tmaps |= SHADER_HASPALETTED;\n\t\tmaps |= SHADER_HASDIFFUSE;\n\t\tif (r_fb_bmodels.ival)\n\t\t\tmaps |= SHADER_HASFULLBRIGHT;\n\t\tif (r_loadbumpmapping || ((r_waterstyle.ival > 1 || r_telestyle.ival > 1) && *tx->name == '*') || tx->shader->defaulttextures->reflectcube)\n\t\t\tmaps |= SHADER_HASNORMALMAP;\n\t\tif (gl_specular.ival)\n\t\t\tmaps |= SHADER_HASGLOSS;\n\n\t\tR_BuildLegacyTexnums(tx->shader, origname, loadname, maps, 0, tx->srcfmt, tx->srcwidth, tx->srcheight, tx->srcdata, tx->palette);\n\t}\n\tBZ_Free(tx->srcdata);\n\ttx->srcdata = NULL;\n}\n#endif\n\nvoid Mod_NowLoadExternal(model_t *loadmodel)\n{\n\t//for halflife bsps where wads are loaded after the map.\n#if !defined(SERVERONLY)\n\tint i;\n\ttexture_t\t*tx;\n\tchar loadname[32];\n\tCOM_FileBase (cl.worldmodel->name, loadname, sizeof(loadname));\n\t\n\tif (!strncmp(loadname, \"b_\", 2))\n\t\tQ_strncpyz(loadname, \"bmodels\", sizeof(loadname));\n\n\tfor (i=0 ; i<loadmodel->numtextures ; i++)\n\t{\n\t\ttx = loadmodel->textures[i];\n\t\tif (!tx)\t//e1m2, this happens\n\t\t\tcontinue;\n\n\t\tif (tx->srcdata)\n\t\t\tcontinue;\n\n\t\tMod_FinishTexture(loadmodel, tx, loadname, true);\n\t}\n#endif\n}\n\nqbyte lmgamma[256];\nvoid BuildLightMapGammaTable (float g, float c)\n{\n\tint i, inf;\n\n//\tg = bound (0.1, g, 3);\n//\tc = bound (1, c, 3);\n\n\tif (g == 1 && c == 1)\n\t{\n\t\tfor (i = 0; i < 256; i++)\n\t\t\tlmgamma[i] = i;\n\t\treturn;\n\t}\n\n\tfor (i = 0; i < 256; i++)\n\t{\n\t\tinf = 255 * pow ((i + 0.5) / 255.5 * c, g) + 0.5;\n\t\tif (inf < 0)\n\t\t\tinf = 0;\n\t\telse if (inf > 255)\n\t\t\tinf = 255;\t\t\n\t\tlmgamma[i] = inf;\n\t}\n}\n\ntypedef struct\n{\n\tunsigned int magic; //\"QLIT\"\n\tunsigned int version; //2\n\tunsigned int numsurfs;\n\tunsigned int lmsize;\t//samples, not bytes (same size as vanilla lighting lump in a q1 bsp).\n\n\t//uint\t\tlmoffsets[numsurfs];\t//completely overrides the bsp lightmap info\n\t//ushort\tlmextents[numsurfs*2];\t//only to avoid precision issues. width+height pairs, actual lightmap sizes on disk (so +1).\n\t//byte\t\tlmstyles[numsurfs*4];\t//completely overrides the bsp lightmap info\n\t//byte\t\tlmshifts[numsurfs];\t\t//default is 4 (1<<4=16), for 1/16th lightmap-to-texel ratio\n\t//byte\t\tlitdata[lmsize*3];\t\t//rgb data\n\t//byte\t\tluxdata[lmsize*3];\t\t//stn light dirs (unsigned bytes\n} qlit2_t;\n\n/*\n=================\nMod_LoadLighting\n=================\n*/\nvoid Mod_LoadLighting (model_t *loadmodel, bspx_header_t *bspx, qbyte *mod_base, lump_t *l, qboolean interleaveddeluxe, lightmapoverrides_t *overrides, subbsp_t subbsp)\n{\n\tqboolean luxtmp = true;\n\tqboolean exptmp = true;\n\tqboolean littmp = true;\n\tqbyte *luxdata = NULL; //rgb8\n\tqbyte *expdata = NULL; //e5bgr9 (hdr!)\n\tqbyte *litdata = NULL; //xyz8\n\tqbyte *lumdata = NULL; //l8\n\tqbyte *out;\n\tsize_t samples;\n#ifdef RUNTIMELIGHTING\n\tqboolean relighting = false;\n#endif\n\n\textern cvar_t gl_overbright;\n\n#ifdef HAVE_CLIENT\n\tBSPX_LightGridLoad(loadmodel, bspx, mod_base);\n#endif\n\n\tloadmodel->lightmaps.fmt = LM_L8;\n\n\t//q3 maps have built in 4-fold overbright.\n\t//if we're not rendering with that, we need to brighten the lightmaps in order to keep the darker parts the same brightness. we loose the 2 upper bits. those bright areas become uniform and indistinct.\n\tif (loadmodel->fromgame == fg_quake3)\n\t{\n\t\tgl_overbright.flags |= CVAR_RENDERERLATCH;\n\t\tBuildLightMapGammaTable(1, (1<<(2-gl_overbright.ival)));\n\t}\n\telse //lit file light intensity is made to match the world's light intensity.\n\t\tBuildLightMapGammaTable(1, 1);\n\n\tloadmodel->lightdata = NULL;\n\tloadmodel->deluxdata = NULL;\n\tif (loadmodel->fromgame == fg_halflife || loadmodel->fromgame == fg_quake2 || loadmodel->fromgame == fg_quake3)\n\t{\n\t\tlitdata = mod_base + l->fileofs;\n\t\tsamples = l->filelen/3;\n\t}\n\telse if (subbsp == sb_quake64)\n\t{\n\t\tqbyte *q64l = mod_base + l->fileofs;\n\t\tqbyte* newl;\n\t\tint i;\n\n\t\tsamples = l->filelen / 2;\n\t\tlitdata = ZG_Malloc(&loadmodel->memgroup, samples * 3);\n\t\tlittmp = false;\n\n\t\t// q64 lightmap format: byte 0 RRRR RGGG byte 1 GGBB BBBA\n\t\tfor (i = 0, newl = litdata; i < samples; ++i, q64l += 2, newl += 3)\n\t\t{\n\t\t\tnewl[0] = (q64l[0] & 0xF8) | ((q64l[0] & 0xF8) >> 5);\n\t\t\tnewl[1] = ((q64l[0] & 0x07) << 5) | ((q64l[1] & 0xC0) >> 3) | (q64l[0] & 0x07);\n\t\t\tnewl[2] = ((q64l[1] & 0x3E) << 2) | ((q64l[1] & 0x3E) >> 3);\n\t\t}\n\t} \n\telse\n\t{\n\t\tlumdata = mod_base + l->fileofs;\n\t\tsamples = l->filelen;\n\t}\n\tif (interleaveddeluxe)\n\t\tsamples >>= 1;\n\tif (!samples)\n\t{\n\t\texpdata = BSPX_FindLump(bspx, mod_base, \"LIGHTING_E5BGR9\", &samples);\t//expressed as a big-endian packed int - 0xEBGR type thing, except misaligned and 32bit.\n\t\tsamples /= 4;\n\t\tif (!samples)\n\t\t{\n\t\t\tlitdata = BSPX_FindLump(bspx, mod_base, \"RGBLIGHTING\", &samples); //RGB packed data\n\t\t\tsamples /= 3;\n\t\t\tif (!samples)\n\t\t\t\treturn;\n\t\t}\n\t}\n\n#ifndef SERVERONLY\n#if 0//def Q2BSPS //Q2XP's alternative to lit files, for higher res lightmaps (that seem to have light coming from the wrong directions...)\n\tif (loadmodel->fromgame == fg_quake2 && overrides && !interleaveddeluxe)\n\t{\n\t\tchar litname[MAX_QPATH];\n\t\tsize_t litsize;\n\t\tqbyte *xplm;\n\t\tCOM_StripExtension(loadmodel->name, litname, sizeof(litname));\n\t\tQ_strncatz(litname, \".xplm\", sizeof(litname));\n\t\txplm = FS_LoadMallocGroupFile(&loadmodel->memgroup, litname, &litsize);\n\n\t\tif (litdata)\n\t\t{\n\t\t\tint scale;\n\t\t\tsize_t numsurfs = LittleLong(*(int *)&xplm[0]);\n\t\t\tunsigned int *offsets = (unsigned int*)(xplm+4);\n\t\t\tscale = xplm[(numsurfs+1)*4];\n\n\t\t\tfor (overrides->defaultshift=0; scale && !(scale&1); scale>>=1)\n\t\t\t\toverrides->defaultshift++;\n\t\t\tif (scale == 1)\n\t\t\t{\t//its a supported shift\n\t\t\t\tlitdata = xplm+(numsurfs+1)*4+1;\n\t\t\t\tsamples = (litsize-(numsurfs+1)*4+1)/3;\n\t\t\t\toverrides->offsets = offsets;\n\t\t\t}\n\t\t}\n\t}\n#endif\n\tif (!expdata && !litdata && r_loadlits.value)\n\t{\n\t\tstruct\n\t\t{\n\t\t\tchar *pattern;\n\t\t} litnames[] = {\n\t\t\t{\"%s.hdr\"},\n\t\t\t{\"%s.lit\"},\n#ifdef HAVE_LEGACY\n\t\t\t{\"lits/%s.lit\"},\n#endif\n\t\t};\n\t\tchar litbasep[MAX_QPATH];\n\t\tchar litbase[MAX_QPATH];\n\t\tint depth;\n\t\tint bestdepth = 0x7fffffff;\n\t\tint best = -1;\n\t\tint i;\n\t\tchar litname[MAX_QPATH];\n\t\tsize_t litsize;\n\t\tqboolean inhibitvalidation = false;\n\n\t\tCOM_StripExtension(loadmodel->name, litbasep, sizeof(litbasep));\n\t\tCOM_FileBase(loadmodel->name, litbase, sizeof(litbase));\n\t\tfor (i = 0; i < countof(litnames); i++)\n\t\t{\n\t\t\tif (strchr(litnames[i].pattern, '/'))\n\t\t\t\tQ_snprintfz(litname, sizeof(litname), litnames[i].pattern, litbase);\n\t\t\telse\n\t\t\t\tQ_snprintfz(litname, sizeof(litname), litnames[i].pattern, litbasep);\n\t\t\tdepth = COM_FDepthFile(litname, false);\n\t\t\tif (depth < bestdepth)\n\t\t\t{\n\t\t\t\tbestdepth = depth;\n\t\t\t\tbest = i;\n\t\t\t}\n\t\t}\n\t\tif (best >= 0)\n\t\t{\n\t\t\tif (strchr(litnames[best].pattern, '/'))\n\t\t\t\tQ_snprintfz(litname, sizeof(litname), litnames[best].pattern, litbase);\n\t\t\telse\n\t\t\t\tQ_snprintfz(litname, sizeof(litname), litnames[best].pattern, litbasep);\n\t\t\tlitdata = FS_LoadMallocGroupFile(&loadmodel->memgroup, litname, &litsize, false);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tlitdata = NULL;\n\t\t\tlitsize = 0;\n\t\t}\n\n\t\tif (litdata)\n\t\t{\t//validate it, if we loaded one.\n\t\t\tint litver = LittleLong(*(int *)&litdata[4]);\n\t\t\tif (litsize < 8 || litdata[0] != 'Q' || litdata[1] != 'L' || litdata[2] != 'I' || litdata[3] != 'T')\n\t\t\t{\n\t\t\t\tlitdata = NULL;\n\t\t\t\tCon_Printf(\"lit \\\"%s\\\" isn't a lit\\n\", litname);\n\t\t\t}\n\t\t\telse if (litver == 1)\n\t\t\t{\n\t\t\t\tif (l->filelen && samples*3 != (litsize-8))\n\t\t\t\t{\n\t\t\t\t\tlitdata = NULL;\n\t\t\t\t\tCon_Printf(\"lit \\\"%s\\\" doesn't match level. Ignored.\\n\", litname);\n\t\t\t\t}\n\t\t\t\telse\t\n\t\t\t\t\tlitdata += 8;\t//header+version\n\t\t\t}\n\t\t\telse if (litver == 0x10001)\n\t\t\t{\t//hdr lighting, e5bgr9 format\n\t\t\t\tif (l->filelen && samples*4 != (litsize-8))\n\t\t\t\t\tCon_Printf(\"lit \\\"%s\\\" doesn't match level. Ignored.\\n\", litname);\n\t\t\t\telse\t\n\t\t\t\t\texpdata = litdata+8;\t//header+version\n\t\t\t\tlitdata = NULL;\n\t\t\t}\n\t\t\telse if (litver == 2 && overrides && litsize > sizeof(qlit2_t))\n\t\t\t{\n\t\t\t\tqlit2_t *ql2 = (qlit2_t*)litdata;\n\t\t\t\tunsigned int *offsets = (unsigned int*)(ql2+1);\n\t\t\t\tunsigned short *extents = (unsigned short*)(offsets+ql2->numsurfs);\n\t\t\t\tunsigned char *styles = (unsigned char*)(extents+ql2->numsurfs*2);\n\t\t\t\tunsigned char *shifts = (unsigned char*)(styles+ql2->numsurfs*4);\n\t\t\t\tif (loadmodel->numsurfaces != ql2->numsurfs)\n\t\t\t\t{\n\t\t\t\t\tlitdata = NULL;\n\t\t\t\t\tCon_Printf(\"lit \\\"%s\\\" doesn't match level. Ignored.\\n\", litname);\n\t\t\t\t}\n\t\t\t\telse if (litsize != sizeof(qlit2_t)+ql2->numsurfs*4+ql2->lmsize*6)\n\t\t\t\t{\n\t\t\t\t\tlitdata = NULL;\n\t\t\t\t\tCon_Printf(\"lit \\\"%s\\\" is truncated. Ignored.\\n\", litname);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tCon_Printf(\"%s: lit2 support is unstandardised and may change in future.\\n\", litname);\n\n\t\t\t\t\tinhibitvalidation = true;\n\n\t\t\t\t\t//surface code needs to know the overrides.\n\t\t\t\t\toverrides->offsets = offsets;\n\t\t\t\t\toverrides->extents = extents;\n\t\t\t\t\toverrides->styles8 = styles;\n\t\t\t\t\toverrides->stylesperface = 4;\n\t\t\t\t\toverrides->shifts = shifts;\n\n\t\t\t\t\t//we're now using this amount of data.\n\t\t\t\t\tsamples = ql2->lmsize;\n\n\t\t\t\t\tlitdata = shifts+ql2->numsurfs;\n\t\t\t\t\tif (r_deluxemapping)\n\t\t\t\t\t\tluxdata = litdata+samples*3;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tCon_Printf(\"lit \\\"%s\\\" isn't version 1 or 2.\\n\", litname);\n\t\t\t\tlitdata = NULL;\n\t\t\t}\n\t\t}\n\n\t\texptmp = littmp = false;\n\t\tif (!litdata && !expdata)\n\t\t{\n\t\t\tsize_t size;\n\t\t\t/*FIXME: bspx support for extents+lmscale, may require style+offset lumps too, not sure what to do here*/\n\t\t\texpdata = BSPX_FindLump(bspx, mod_base, \"LIGHTING_E5BGR9\", &size);\n\t\t\texptmp = true;\n\t\t\tif (size != samples*4)\n\t\t\t{\n\t\t\t\texpdata = NULL;\n\n\t\t\t\tlitdata = BSPX_FindLump(bspx, mod_base, \"RGBLIGHTING\", &size);\n\t\t\t\tlittmp = true;\n\t\t\t\tif (size != samples*3)\n\t\t\t\t\tlitdata = NULL;\n\t\t\t}\n\t\t}\n\t\telse if (!inhibitvalidation)\n\t\t{\n\t\t\tif (lumdata && litdata)\n\t\t\t{\n\t\t\t\tfloat prop;\n\t\t\t\tint i;\n\t\t\t\tqbyte *lum;\n\t\t\t\tqbyte *lit;\n\n\t\t\t\t//now some cheat protection.\n\t\t\t\tlum = lumdata;\n\t\t\t\tlit = litdata;\n\n\t\t\t\tfor (i = 0; i < samples; i++)\t//force it to the same intensity. (or less, depending on how you see it...)\n\t\t\t\t{\n#define m(a, b, c) (a>(b>c?b:c)?a:(b>c?b:c))\n\t\t\t\t\tprop = (float)m(lit[0],  lit[1], lit[2]);\n\n\t\t\t\t\tif (!prop)\n\t\t\t\t\t{\n\t\t\t\t\t\tlit[0] = *lum;\n\t\t\t\t\t\tlit[1] = *lum;\n\t\t\t\t\t\tlit[2] = *lum;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tprop = *lum / prop;\n\t\t\t\t\t\tlit[0] *= prop;\n\t\t\t\t\t\tlit[1] *= prop;\n\t\t\t\t\t\tlit[2] *= prop;\n\t\t\t\t\t}\n\n\t\t\t\t\tlum++;\n\t\t\t\t\tlit+=3;\n\t\t\t\t}\n\t\t\t\t//end anti-cheat\n\t\t\t}\n\t\t}\n\t}\n\n\n\tif (!luxdata && r_loadlits.ival && r_deluxemapping)\n\t{\t//the map util has a '-scalecos X' parameter. use 0 if you're going to use only just lux. without lux scalecos 0 is hideous.\n\t\tchar luxname[MAX_QPATH];\n\t\tsize_t luxsz = 0;\n\t\t*luxname = 0;\n\t\tif (!luxdata)\n\t\t{\t\t\t\t\t\t\t\n\t\t\tQ_strncpyz(luxname, loadmodel->name, sizeof(luxname));\n\t\t\tCOM_StripExtension(loadmodel->name, luxname, sizeof(luxname));\n\t\t\tCOM_DefaultExtension(luxname, \".lux\", sizeof(luxname));\n\t\t\tluxdata = FS_LoadMallocGroupFile(&loadmodel->memgroup, luxname, &luxsz, false);\n\t\t\tluxtmp = false;\n\t\t}\n\t\tif (!luxdata)\n\t\t{\n\t\t\tQ_strncpyz(luxname, \"luxs/\", sizeof(luxname));\n\t\t\tCOM_StripExtension(COM_SkipPath(loadmodel->name), luxname+5, sizeof(luxname)-5);\n\t\t\tQ_strncatz(luxname, \".lux\", sizeof(luxname));\n\n\t\t\tluxdata = FS_LoadMallocGroupFile(&loadmodel->memgroup, luxname, &luxsz, false);\n\t\t\tluxtmp = false;\n\t\t}\n\t\tif (!luxdata) //dp...\n\t\t{\n\t\t\tCOM_StripExtension(loadmodel->name, luxname, sizeof(luxname));\n\t\t\tCOM_DefaultExtension(luxname, \".dlit\", sizeof(luxname));\n\t\t\tluxdata = FS_LoadMallocGroupFile(&loadmodel->memgroup, luxname, &luxsz, false);\n\t\t\tluxtmp = false;\n\t\t}\n\t\t//make sure the .lux has the correct size\n\t\tif (luxdata && l->filelen && l->filelen != (luxsz-8)/3)\n\t\t{\n\t\t\tCon_Printf(\"deluxmap \\\"%s\\\" doesn't match level. Ignored.\\n\", luxname);\n\t\t\tluxdata=NULL;\n\t\t}\n\t\tif (!luxdata)\n\t\t{\n\t\t\tsize_t size;\n\t\t\tluxdata = BSPX_FindLump(bspx, mod_base, \"LIGHTINGDIR\", &size);\n\t\t\tif (size != samples*3)\n\t\t\t\tluxdata = NULL;\n\t\t\tluxtmp = true;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (luxsz < 8 || (luxdata[0] == 'Q' && luxdata[1] == 'L' && luxdata[2] == 'I' && luxdata[3] == 'T'))\n\t\t\t{\n\t\t\t\tif (LittleLong(*(int *)&luxdata[4]) == 1)\n\t\t\t\t\tluxdata+=8;\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tCon_Printf(\"\\\"%s\\\" isn't a version 1 deluxmap\\n\", luxname);\n\t\t\t\t\tluxdata=NULL;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tCon_Printf(\"lit \\\"%s\\\" isn't a deluxmap\\n\", luxname);\n\t\t\t\tluxdata=NULL;\n\t\t\t}\n\t\t}\t\n\t}\n#endif\n\n#ifdef RUNTIMELIGHTING\n\tif ((loadmodel->type == mod_brush && loadmodel->fromgame == fg_quake) || loadmodel->type == mod_heightmap)\n\t{\t//we only support a couple of formats. :(\n\t\tif (r_loadlits.value >= 2 && ((!litdata&&!expdata) || (!luxdata && r_deluxemapping)))\n\t\t{\n\t\t\trelighting = RelightSetup(loadmodel, l->filelen, !litdata&&!expdata);\n\t\t}\n\t\telse if (r_deluxemapping_cvar.value>1 && r_deluxemapping && !luxdata\n#ifdef RTLIGHTS\n\t\t\t&& !(r_shadow_realtime_world.ival && r_shadow_realtime_world_lightmaps.value<=0)\n#endif\n\t\t\t)\n\t\t{\t//if deluxemapping is on, generate missing lux files a little more often, but don't bother if we have rtlights on anyway.\n\n\t\t\trelighting = RelightSetup(loadmodel, l->filelen, false);\n\t\t}\n\t}\n\n\t/*if we're relighting, make sure there's the proper lit data to be updated*/\n\tif (relighting && !litdata && !expdata)\n\t{\n\t\tint i;\n\t\tunsigned int *ergb;\n\n\t\tif (r_loadlits.ival >= 3)\n\t\t{\n\t\t\tergb = ZG_Malloc(&loadmodel->memgroup, samples*4);\n\t\t\texpdata = (qbyte*)ergb;\n\t\t\tlittmp = false;\n\t\t\tif (lumdata)\n\t\t\t{\n\t\t\t\tfor (i = 0; i < samples; i++)\n\t\t\t\t\tergb[i] = (17u<<27) | (lumdata[i]<<18) | (lumdata[i]<<9) | (lumdata[i]<<0);\n\t\t\t\tlumdata = NULL;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tlitdata = ZG_Malloc(&loadmodel->memgroup, samples*3);\n\t\t\tlittmp = false;\n\t\t\tif (lumdata)\n\t\t\t{\n\t\t\t\tfor (i = 0; i < samples; i++)\n\t\t\t\t{\n\t\t\t\t\tlitdata[i*3+0] = lumdata[i];\n\t\t\t\t\tlitdata[i*3+1] = lumdata[i];\n\t\t\t\t\tlitdata[i*3+2] = lumdata[i];\n\t\t\t\t}\n\t\t\t\tlumdata = NULL;\n\t\t\t}\n\t\t}\n\t}\n\t/*if we're relighting, make sure there's the proper lux data to be updated*/\n\tif (relighting && r_deluxemapping && !luxdata)\n\t{\n\t\tint i;\n\t\tluxdata = ZG_Malloc(&loadmodel->memgroup, samples*3);\n\t\tfor (i = 0; i < samples; i++)\n\t\t{\n\t\t\tluxdata[i*3+0] = 0.5f*255;\n\t\t\tluxdata[i*3+1] = 0.5f*255;\n\t\t\tluxdata[i*3+2] = 255;\n\t\t}\n\t}\n#endif\n\n\tif (overrides && !overrides->shifts)\n\t{\n\t\tsize_t size;\n\t\t//if we have shifts, then we probably also have legacy data in the surfaces that we want to override\n\t\tif (!overrides->offsets)\n\t\t{\n\t\t\tsize_t size;\n\t\t\toverrides->offsets = BSPX_FindLump(bspx, mod_base, \"LMOFFSET\", &size);\n\t\t\tif (size != loadmodel->numsurfaces * sizeof(int))\n\t\t\t{\n\t\t\t\tif (size)\n\t\t\t\t\tCon_Printf(CON_ERROR\"BSPX LMOFFSET lump is wrong size, expected %u entries, found %\"PRIuSIZE\"\\n\", loadmodel->numsurfaces, size/(unsigned int)sizeof(int));\n\t\t\t\toverrides->offsets = NULL;\n\t\t\t}\n\t\t}\n\t\tif (!overrides->styles8 && !overrides->styles16)\n\t\t{\t//16bit per-face lightmap styles index\n\t\t\tsize_t size;\n\t\t\toverrides->styles16 = BSPX_FindLump(bspx, mod_base, \"LMSTYLE16\", &size);\n\t\t\toverrides->stylesperface = size / (sizeof(*overrides->styles16)*loadmodel->numsurfaces); //rounding issues will be caught on the next line...\n\t\t\tif (!overrides->stylesperface || size != loadmodel->numsurfaces * sizeof(*overrides->styles16)*overrides->stylesperface)\n\t\t\t{\n\t\t\t\tif (size)\n\t\t\t\t\tCon_Printf(CON_ERROR\"BSPX LMSTYLE16 lump is wrong size, expected %u*%u entries, found %\"PRIuSIZE\"\\n\", loadmodel->numsurfaces, overrides->stylesperface, size/(unsigned int)sizeof(*overrides->styles16));\n\t\t\t\toverrides->styles16 = NULL;\n\t\t\t}\n\t\t\telse if (overrides->stylesperface > MAXCPULIGHTMAPS)\n\t\t\t\tCon_Printf(CON_WARNING \"LMSTYLE16 lump provides %i styles, only the first %i will be used.\\n\", overrides->stylesperface, MAXCPULIGHTMAPS);\n\t\t}\n\t\tif (!overrides->styles8 && !overrides->styles16)\n\t\t{\t//16bit per-face lightmap styles index\n\t\t\tsize_t size;\n\t\t\toverrides->styles8 = BSPX_FindLump(bspx, mod_base, \"LMSTYLE\", &size);\n\t\t\toverrides->stylesperface = size / (sizeof(*overrides->styles8)*loadmodel->numsurfaces); //rounding issues will be caught on the next line...\n\t\t\tif (!overrides->stylesperface || size != loadmodel->numsurfaces * sizeof(*overrides->styles8)*overrides->stylesperface)\n\t\t\t{\n\t\t\t\tif (size)\n\t\t\t\t\tCon_Printf(CON_ERROR\"BSPX LMSTYLE16 lump is wrong size, expected %u*%u entries, found %\"PRIuSIZE\"\\n\", loadmodel->numsurfaces, overrides->stylesperface, size/(unsigned int)sizeof(*overrides->styles8));\n\t\t\t\toverrides->styles8 = NULL;\n\t\t\t}\n\t\t\telse if (overrides->stylesperface > MAXCPULIGHTMAPS)\n\t\t\t\tCon_Printf(CON_WARNING \"LMSTYLE lump provides %i styles, only the first %i will be used.\\n\", overrides->stylesperface, MAXCPULIGHTMAPS);\n\t\t}\n\n\t\toverrides->shifts = BSPX_FindLump(bspx, mod_base, \"LMSHIFT\", &size);\n\t\tif (size != loadmodel->numsurfaces)\n\t\t{\n\t\t\tif (size)\n\t\t\t{\t//ericw-tools is screwing up again. don't leave things screwed.\n\t\t\t\tCon_Printf(CON_ERROR\"BSPX LMSHIFT lump is wrong size, expected %u entries, found %\"PRIuSIZE\"\\n\", loadmodel->numsurfaces, size);\n\t\t\t\toverrides->styles16 = NULL;\n\t\t\t\toverrides->styles8 = NULL;\n\t\t\t\toverrides->offsets = NULL;\n\t\t\t}\n\t\t\toverrides->shifts = NULL;\n\t\t}\n\t}\n\n\tif (luxdata && luxtmp)\n\t{\n\t\tloadmodel->deluxdata = ZG_Malloc(&loadmodel->memgroup, samples*3);\n\t\tmemcpy(loadmodel->deluxdata, luxdata, samples*3);\n\t}\n\telse if (luxdata)\n\t\tloadmodel->deluxdata = luxdata;\n\telse if (interleaveddeluxe)\n\t\tloadmodel->deluxdata = ZG_Malloc(&loadmodel->memgroup, samples*3);\n\n\tif (expdata)\n\t{\n\t\tloadmodel->lightmaps.fmt = LM_E5BGR9;\n\t\tloadmodel->lightdatasize = samples*4;\n\t\tif (exptmp)\n\t\t{\n\t\t\tloadmodel->lightdata = ZG_Malloc(&loadmodel->memgroup, samples*4);\n\t\t\tmemcpy(loadmodel->lightdata, expdata, samples*4);\n\t\t}\n\t\telse\n\t\t\tloadmodel->lightdata = expdata;\n\n\t\t//FIXME: no desaturation/gamma logic.\n\t\treturn;\n\t}\n\telse if (litdata)\n\t{\n\t\tloadmodel->lightmaps.fmt = LM_RGB8;\n\t\tif (littmp)\n\t\t\tloadmodel->lightdata = ZG_Malloc(&loadmodel->memgroup, samples*3);\t/*the memcpy is below*/\n\t\telse\n\t\t\tloadmodel->lightdata = litdata;\n\t\tsamples*=3;\n\t}\n\telse if (lumdata)\n\t{\n\t\tloadmodel->lightmaps.fmt = LM_L8;\n\t\tloadmodel->lightdata = ZG_Malloc(&loadmodel->memgroup, samples);\n\t\tlitdata = lumdata;\n\t}\n\n\t/*apply lightmap gamma to the entire lightmap*/\n\tloadmodel->lightdatasize = samples;\n\tout = loadmodel->lightdata;\n\tif (interleaveddeluxe)\n\t{\n\t\tqbyte *luxout = loadmodel->deluxdata;\n\t\tsamples /= 3;\n\t\twhile(samples-- > 0)\n\t\t{\n\t\t\t*out++ = lmgamma[*litdata++];\n\t\t\t*out++ = lmgamma[*litdata++];\n\t\t\t*out++ = lmgamma[*litdata++];\n\t\t\t*luxout++ = *litdata++;\n\t\t\t*luxout++ = *litdata++;\n\t\t\t*luxout++ = *litdata++;\n\t\t}\n\t}\n\telse\n\t{\n\t\twhile(samples-- > 0)\n\t\t{\n\t\t\t*out++ = lmgamma[*litdata++];\n\t\t}\n\t}\n\n#ifndef SERVERONLY\n\tif ((loadmodel->lightmaps.fmt == LM_RGB8) && r_lightmap_saturation.value != 1.0f)\n\t\tSaturateR8G8B8(loadmodel->lightdata, l->filelen, r_lightmap_saturation.value);\n#endif\n}\n\n//scans through the worldspawn for a single specific key.\nconst char *Mod_ParseWorldspawnKey(model_t *mod, const char *key, char *buffer, size_t sizeofbuffer)\n{\n\tchar keyname[64];\n\tchar value[1024];\n\tconst char *ents = Mod_GetEntitiesString(mod);\n\twhile(ents && *ents)\n\t{\n\t\tents = COM_ParseOut(ents, keyname, sizeof(keyname));\n\t\tif (*keyname == '{')\t//an entity\n\t\t{\n\t\t\twhile (ents && *ents)\n\t\t\t{\n\t\t\t\tents = COM_ParseOut(ents, keyname, sizeof(keyname));\n\t\t\t\tif (*keyname == '}')\n\t\t\t\t\tbreak;\n\t\t\t\tents = COM_ParseOut(ents, value, sizeof(value));\n\t\t\t\tif (!strcmp(keyname, key) || (*keyname == '_' && !strcmp(keyname+1, key)))\n\t\t\t\t{\n\t\t\t\t\tQ_strncpyz(buffer, value, sizeofbuffer);\n\t\t\t\t\treturn buffer;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn \"\";\t//worldspawn only.\n\t\t}\n\t}\n\treturn \"\";\t//err...\n}\n\nstatic void Mod_ShowEnt_f(void)\n{\n\tmodel_t *mod = NULL;\n\tsize_t idx = atoi(Cmd_Argv(1));\n\tchar *n = Cmd_Argv(2);\n\n\tif (*n)\n\t\tmod = Mod_ForName(n, MLV_WARN);\n#ifndef CLIENTONLY\n\tif (sv.state && !mod)\n\t\tmod = sv.world.worldmodel;\n#endif\n#ifndef SERVERONLY\n\tif (cls.state && !mod)\n\t\tmod = cl.worldmodel;\n#endif\n\tif (mod && mod->loadstate == MLS_LOADING)\n\t\tCOM_WorkerPartialSync(mod, &mod->loadstate, MLS_LOADING);\n\tif (!mod || mod->loadstate != MLS_LOADED)\n\t{\n\t\tCon_Printf(\"Map not loaded\\n\");\n\t\treturn;\n\t}\n\n\tif (!mod->numentityinfo)\n\t\tMod_ParseEntities(mod);\n\tif (!idx && strcmp(Cmd_Argv(1), \"0\"))\n\t{\n\t\tchar *match = Cmd_Argv(1);\n\t\tunsigned count = 0;\n\t\tfor (idx = 0; idx < mod->numentityinfo; idx++)\n\t\t{\n\t\t\tif (strstr(mod->entityinfo[idx].keyvals, match))\n\t\t\t{\n\t\t\t\tCon_Printf(\"{\\t//%u\\n%s\\n}\\n\", (unsigned)idx, mod->entityinfo[idx].keyvals);\n\t\t\t\tcount++;\n\t\t\t}\n\t\t}\n\t\tCon_Printf(\"%u of %u ents match\\n\", (unsigned)count, (unsigned)mod->numentityinfo);\n\t}\n\telse if (idx >= mod->numentityinfo)\n\t\tCon_Printf(\"Invalid entity index (max %u).\\n\", (unsigned)mod->numentityinfo);\n\telse if (!mod->entityinfo[idx].keyvals)\n\t\tCon_Printf(\"Entity index was cleared...\\n\");\n\telse\n\t\tCon_Printf(\"{\\n%s\\n}\\n\", mod->entityinfo[idx].keyvals);\n}\n\nstatic void Mod_SaveEntFile_f(void)\n{\n\tchar fname[MAX_QPATH];\n\tchar nname[MAX_OSPATH];\n\tmodel_t *mod = NULL;\n\tchar *n = Cmd_Argv(1);\n\tconst char *ents;\n\tif (*n)\n\t\tmod = Mod_ForName(n, MLV_WARN);\n#ifndef CLIENTONLY\n\tif (sv.state && !mod)\n\t\tmod = sv.world.worldmodel;\n#endif\n#ifndef SERVERONLY\n\tif (cls.state && !mod)\n\t\tmod = cl.worldmodel;\n#endif\n\tif (mod && mod->loadstate == MLS_LOADING)\n\t\tCOM_WorkerPartialSync(mod, &mod->loadstate, MLS_LOADING);\n\tif (!mod || mod->loadstate != MLS_LOADED)\n\t{\n\t\tCon_Printf(\"Map not loaded\\n\");\n\t\treturn;\n\t}\n\tents = Mod_GetEntitiesString(mod);\n\tif (!ents)\n\t{\n\t\tCon_Printf(\"Map is not a map, and has no entities\\n\");\n\t\treturn;\n\t}\n\n\tif (*mod_loadentfiles_dir.string && !strncmp(mod->name, \"maps/\", 5))\n\t{\n\t\tQ_snprintfz(fname, sizeof(fname), \"maps/%s/%s\", mod_loadentfiles_dir.string, mod->name+5);\n\t\tCOM_StripExtension(fname, fname, sizeof(fname));\n\t\tQ_strncatz(fname, mod_modifier, sizeof(fname));\n\t\tQ_strncatz(fname, \".ent\", sizeof(fname));\n\t}\n\telse\n\t{\n\t\tCOM_StripExtension(mod->name, fname, sizeof(fname));\n\t\tQ_strncatz(fname, mod_modifier, sizeof(fname));\n\t\tQ_strncatz(fname, \".ent\", sizeof(fname));\n\t}\n\n\tif (COM_WriteFile(fname, FS_GAMEONLY, ents, strlen(ents)))\n\t{\n\t\tif (FS_DisplayPath(fname, FS_GAMEONLY, nname, sizeof(nname)))\n\t\t\tCon_Printf(\"Wrote %s\\n\", nname);\n\t}\n\telse\n\t\tCon_Printf(\"Write failed\\n\");\n}\n\n/*\n=================\nMod_LoadEntities\n=================\n*/\nqboolean Mod_LoadEntitiesBlob(struct model_s *mod, const char *entdata, size_t entdatasize)\n{\n\tchar fname[MAX_QPATH];\n\tsize_t sz;\n\tchar keyname[64];\n\tchar value[1024];\n\tchar *ents = NULL, *k;\n\tint t;\n\n\tMod_SetEntitiesString(mod, NULL, false);\n\tif (!entdatasize)\n\t\treturn false;\n\n\tif (mod_loadentfiles.value && !ents && *mod_loadentfiles_dir.string)\n\t{\n\t\tif (!strncmp(mod->name, \"maps/\", 5))\n\t\t{\n\t\t\tQ_snprintfz(fname, sizeof(fname), \"maps/%s/%s\", mod_loadentfiles_dir.string, mod->name+5);\n\t\t\tCOM_StripExtension(fname, fname, sizeof(fname));\n\t\t\tQ_strncatz(fname, mod_modifier, sizeof(fname));\n\t\t\tQ_strncatz(fname, \".ent\", sizeof(fname));\n\t\t\tents = FS_LoadMallocFile(fname, &sz);\n\t\t}\n\t}\n\tif (mod_loadentfiles.value && !ents)\n\t{\n\t\tCOM_StripExtension(mod->name, fname, sizeof(fname));\n\t\tQ_strncatz(fname, mod_modifier, sizeof(fname));\n\t\tQ_strncatz(fname, \".ent\", sizeof(fname));\n\t\tents = FS_LoadMallocFile(fname, &sz);\n\t}\n\tif (mod_loadentfiles.value && !ents)\n\t{\t//tenebrae compat\n\t\tCOM_StripExtension(mod->name, fname, sizeof(fname));\n\t\tQ_strncatz(fname, mod_modifier, sizeof(fname));\n\t\tQ_strncatz(fname, \".edo\", sizeof(fname));\n\t\tents = FS_LoadMallocFile(fname, &sz);\n\t}\n\tif (!ents)\n\t{\n\t\tents = Z_Malloc(entdatasize + 1);\n\t\tmemcpy (ents, entdata, entdatasize);\n\t\tents[entdatasize] = 0;\n\t\tmod->entitiescrc = 0;\n\t}\n\telse\n\t\tmod->entitiescrc = CalcHashInt(&hash_crc16, ents, strlen(ents));\n\n\tMod_SetEntitiesString(mod, ents, false);\n\n\twhile(ents && *ents)\n\t{\n\t\tents = COM_ParseOut(ents, keyname, sizeof(keyname));\n\t\tif (*keyname == '{')\t//an entity\n\t\t{\n\t\t\twhile (ents && *ents)\n\t\t\t{\n\t\t\t\tents = COM_ParseOut(ents, keyname, sizeof(keyname));\n\t\t\t\tif (*keyname == '}')\n\t\t\t\t\tbreak;\n\t\t\t\tents = COM_ParseOut(ents, value, sizeof(value));\n\t\t\t\tif (!strncmp(keyname, \"_texpart_\", 9) || !strncmp(keyname, \"texpart_\", 8))\n\t\t\t\t{\n\t\t\t\t\tk = keyname + ((*keyname=='_')?9:8);\n\t\t\t\t\tfor (t = 0; t < mod->numtextures; t++)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (!strcmp(k, mod->textures[t]->name))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tmod->textures[t]->partname = ZG_Malloc(&mod->memgroup, strlen(value)+1);\n\t\t\t\t\t\t\tstrcpy(mod->textures[t]->partname, value);\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif (t == mod->numtextures)\n\t\t\t\t\t\tCon_Printf(\"\\\"%s\\\" is not valid for %s\\n\", keyname, mod->name);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn true;\n}\nvoid Mod_LoadEntities (model_t *loadmodel, qbyte *mod_base, lump_t *l)\n{\n\tMod_LoadEntitiesBlob(loadmodel, mod_base+l->fileofs, l->filelen);\n}\n\n/*\n=================\nMod_LoadVertexes\n=================\n*/\nqboolean Mod_LoadVertexes (model_t *loadmodel, qbyte *mod_base, lump_t *l)\n{\n\tdvertex_t\t*in;\n\tmvertex_t\t*out;\n\tint\t\t\ti, count;\n\n\tin = (void *)(mod_base + l->fileofs);\n\tcount = l->filelen / sizeof(*in);\n\tif (l->filelen % sizeof(*in) || count > SANITY_LIMIT(*out))\n\t{\n\t\tCon_Printf (CON_ERROR \"MOD_LoadBmodel: funny lump size in %s\\n\", loadmodel->name);\n\t\treturn false;\n\t}\n\tout = ZG_Malloc(&loadmodel->memgroup, count*sizeof(*out));\t\n\n\tloadmodel->vertexes = out;\n\tloadmodel->numvertexes = count;\n\n\tfor ( i=0 ; i<count ; i++, in++, out++)\n\t{\n\t\tout->position[0] = LittleFloat (in->point[0]);\n\t\tout->position[1] = LittleFloat (in->point[1]);\n\t\tout->position[2] = LittleFloat (in->point[2]);\n\t}\n\n\treturn true;\n}\n\nqboolean Mod_LoadVertexNormals (model_t *loadmodel, bspx_header_t *bspx, qbyte *mod_base, lump_t *l)\n{\n\tfloat\t*in;\n\tfloat\t*out;\n\tsize_t\ti, count;\n\n\tif (l)\n\t{\n\t\tin = (void *)(mod_base + l->fileofs);\n\t\tcount = l->filelen / sizeof(vec3_t);\n\t\tif (l->filelen % sizeof(*in) || count > SANITY_LIMIT(vec3_t))\n\t\t{\n\t\t\tCon_Printf (CON_ERROR \"MOD_LoadBmodel: funny lump size in %s\\n\", loadmodel->name);\n\t\t\treturn false;\n\t\t}\n\n\t\tif (count != loadmodel->numvertexes)\n\t\t\treturn false;\t//invalid number of verts there, can't use this.\n\t}\n\telse\n\t{\t//ericw's thing\n\t\tsize_t size;\n\t\tquint32_t t;\n\t\tint *normcount;\n\t\tstruct surfedgenormals_s *sen;\n\t\tnormcount = BSPX_FindLump(bspx, mod_base, \"FACENORMALS\", &size);\n\t\tif (normcount && size >= sizeof(*normcount))\n\t\t{\n\t\t\tcount = LittleLong(*normcount);\n\t\t\tif (count < 1)\n\t\t\t\treturn false;\n\t\t\tin = (void*)(normcount+1);\t//now the normals table.\n\t\t\tsen = (void*)(in + count*3);\n\t\t\tif ((qbyte*)(sen + loadmodel->numsurfedges)-(qbyte*)normcount != size)\n\t\t\t\treturn false;\t//bad size.\n\n\t\t\tloadmodel->surfedgenormals = ZG_Malloc(&loadmodel->memgroup, loadmodel->numsurfedges*sizeof(*loadmodel->surfedgenormals));\n\t\t\tfor ( i=0 ; i<loadmodel->numsurfedges ; i++, sen++)\n\t\t\t{\n\t\t\t\tt = LittleLong(sen->n); loadmodel->surfedgenormals[i].n = bound(0, t, count-1);\n\t\t\t\tt = LittleLong(sen->s); loadmodel->surfedgenormals[i].s = bound(0, t, count-1);\n\t\t\t\tt = LittleLong(sen->t); loadmodel->surfedgenormals[i].t = bound(0, t, count-1);\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\t//quake2world's thing\n\t\t\tin = BSPX_FindLump(bspx, mod_base, \"VERTEXNORMALS\", &count);\n\t\t\tif (in)\n\t\t\t\tcount /= sizeof(vec3_t);\n\t\t\telse\n\t\t\t\tcount = 0;\n\t\t\tif (count != loadmodel->numvertexes)\n\t\t\t\treturn false;\t//invalid number of verts there, can't use this.\n\t\t}\n\t}\n\n\tout = ZG_Malloc(&loadmodel->memgroup, count*sizeof(vec3_t));\t\n\tloadmodel->normals = (vec3_t*)out;\n\tfor ( i=0 ; i<count ; i++, in+=3, out+=3)\n\t{\n\t\tout[0] = LittleFloat (in[0]);\n\t\tout[1] = LittleFloat (in[1]);\n\t\tout[2] = LittleFloat (in[2]);\n\t}\n\treturn true;\n}\n\n#if defined(Q1BSPS) || defined(Q2BSPS)\nvoid ModQ1_Batches_BuildQ1Q2Poly(model_t *mod, msurface_t *surf, builddata_t *cookie)\n{\n\tunsigned int vertidx;\n\tint i, lindex, edgevert;\n\tmesh_t *mesh = surf->mesh;\n\tfloat *vec;\n\tfloat s, t, d;\n\tint sty;\n//\tint w,h;\n\tstruct facelmvecs_s *flmv = mod->facelmvecs?mod->facelmvecs + (surf-mod->surfaces):NULL;\n\n\tif (!mesh)\n\t{\n\t\tmesh = surf->mesh = ZG_Malloc(&mod->memgroup, sizeof(mesh_t) + (sizeof(vecV_t)+sizeof(vec2_t)*(1+1)+sizeof(vec3_t)*3+sizeof(vec4_t)*1)* surf->numedges + sizeof(index_t)*(surf->numedges-2)*3);\n\t\tmesh->numvertexes = surf->numedges;\n\t\tmesh->numindexes = (mesh->numvertexes-2)*3;\n\t\tmesh->xyz_array = (vecV_t*)(mesh+1);\n\t\tmesh->st_array = (vec2_t*)(mesh->xyz_array+mesh->numvertexes);\n\t\tmesh->lmst_array[0] = (vec2_t*)(mesh->st_array+mesh->numvertexes);\n\t\tmesh->normals_array = (vec3_t*)(mesh->lmst_array[0]+mesh->numvertexes);\n\t\tmesh->snormals_array = (vec3_t*)(mesh->normals_array+mesh->numvertexes);\n\t\tmesh->tnormals_array = (vec3_t*)(mesh->snormals_array+mesh->numvertexes);\n\t\tmesh->colors4f_array[0] = (vec4_t*)(mesh->tnormals_array+mesh->numvertexes);\n\t\tmesh->indexes = (index_t*)(mesh->colors4f_array[0]+mesh->numvertexes);\n\t}\n\tmesh->istrifan = true;\n\n\t//output the mesh's indicies\n\tfor (i=0 ; i<mesh->numvertexes-2 ; i++)\n\t{\n\t\tmesh->indexes[i*3] = 0;\n\t\tmesh->indexes[i*3+1] = i+1;\n\t\tmesh->indexes[i*3+2] = i+2;\n\t}\n\t//output the renderable verticies\n\tfor (i=0 ; i<mesh->numvertexes ; i++)\n\t{\n\t\tlindex = mod->surfedges[surf->firstedge + i];\n\t\tedgevert = lindex <= 0;\n\t\tif (edgevert)\n\t\t\tlindex = -lindex;\n\t\tif (lindex < 0 || lindex >= mod->numedges)\n\t\t\tvertidx = 0;\n\t\telse\n\t\t\tvertidx = mod->edges[lindex].v[edgevert];\n\t\tvec = mod->vertexes[vertidx].position;\n\n\t\ts = DotProduct (vec, surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3];\n\t\tt = DotProduct (vec, surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3];\n\n\t\tVectorCopy (vec, mesh->xyz_array[i]);\n\n/*\t\tif (R_GetShaderSizes(surf->texinfo->texture->shader, &w, &h, false) > 0)\n\t\t{\n\t\t\tmesh->st_array[i][0] = s/w;\n\t\t\tmesh->st_array[i][1] = t/h;\n\t\t}\n\t\telse\n*/\n\t\t{\n\t\t\tmesh->st_array[i][0] = s;\n\t\t\tmesh->st_array[i][1] = t;\n\t\t\tif (surf->texinfo->texture->vwidth)\n\t\t\t\tmesh->st_array[i][0] /= surf->texinfo->texture->vwidth;\n\t\t\tif (surf->texinfo->texture->vheight)\n\t\t\t\tmesh->st_array[i][1] /= surf->texinfo->texture->vheight;\n\n\t\t\tif (surf->texinfo->flags & TI_N64_UV)\n\t\t\t{\n\t\t\t\tmesh->st_array[i][0] /= 2;\n\t\t\t\tmesh->st_array[i][1] /= 2;\n\t\t\t}\n\t\t}\n\n\t\tif (flmv)\n\t\t{\n\t\t\ts = DotProduct (vec, flmv->lmvecs[0]) + flmv->lmvecs[0][3];\n\t\t\tt = DotProduct (vec, flmv->lmvecs[1]) + flmv->lmvecs[1][3];\n#ifndef SERVERONLY\n\t\t\tif (r_lightmap_average.ival)\n\t\t\t\ts = surf->extents[0]*0.5, t = surf->extents[1]*0.5;\n#endif\n\t\t\t//s+t are now in luxels... need to convert those to normalised texcoords though.\n\t\t\tfor (sty = 0; sty < 1; sty++)\n\t\t\t{\n\t\t\t\tmesh->lmst_array[sty][i][0] = (surf->light_s[sty] + s) / mod->lightmaps.width;\n\t\t\t\tmesh->lmst_array[sty][i][1] = (surf->light_t[sty] + t) / mod->lightmaps.height;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n#ifndef SERVERONLY\n\t\t\tif (r_lightmap_average.ival)\n\t\t\t{\n\t\t\t\tfor (sty = 0; sty < 1; sty++)\n\t\t\t\t{\n\t\t\t\t\tmesh->lmst_array[sty][i][0] = (surf->extents[0]*0.5 + (surf->light_s[sty]<<surf->lmshift) + (1<<surf->lmshift)*0.5) / (mod->lightmaps.width<<surf->lmshift);\n\t\t\t\t\tmesh->lmst_array[sty][i][1] = (surf->extents[1]*0.5 + (surf->light_t[sty]<<surf->lmshift) + (1<<surf->lmshift)*0.5) / (mod->lightmaps.height<<surf->lmshift);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n#endif\n\t\t\t{\n\t\t\t\tfor (sty = 0; sty < 1; sty++)\n\t\t\t\t{\n\t\t\t\t\tmesh->lmst_array[sty][i][0] = (s - surf->texturemins[0] + (surf->light_s[sty]<<surf->lmshift) + (1<<surf->lmshift)*0.5) / (mod->lightmaps.width<<surf->lmshift);\n\t\t\t\t\tmesh->lmst_array[sty][i][1] = (t - surf->texturemins[1] + (surf->light_t[sty]<<surf->lmshift) + (1<<surf->lmshift)*0.5) / (mod->lightmaps.height<<surf->lmshift);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (mod->surfedgenormals)\n\t\t{\n\t\t\tstruct surfedgenormals_s *pv = mod->surfedgenormals + surf->firstedge + i;\n\t\t\tVectorCopy(mod->normals[pv->n], mesh->normals_array[i]);\n\t\t\tVectorCopy(mod->normals[pv->s], mesh->snormals_array[i]);\n\t\t\tVectorCopy(mod->normals[pv->t], mesh->tnormals_array[i]);\n\t\t}\n\t\telse\n\t\t{\n\t\t\t//figure out the texture directions, for bumpmapping and stuff\n\t\t\tif (mod->normals && (surf->texinfo->flags & 0x800) && (mod->normals[vertidx][0] || mod->normals[vertidx][1] || mod->normals[vertidx][2]))\n\t\t\t{\n\t\t\t\t//per-vertex normals - used for smoothing groups and stuff.\n\t\t\t\tVectorCopy(mod->normals[vertidx], mesh->normals_array[i]);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (surf->flags & SURF_PLANEBACK)\n\t\t\t\t\tVectorNegate(surf->plane->normal, mesh->normals_array[i]);\n\t\t\t\telse\n\t\t\t\t\tVectorCopy(surf->plane->normal, mesh->normals_array[i]);\n\t\t\t}\n\t\t\tVectorCopy(surf->texinfo->vecs[0], mesh->snormals_array[i]);\n\t\t\tVectorNegate(surf->texinfo->vecs[1], mesh->tnormals_array[i]);\n\t\t\t//for q1bsp the s+t vectors are usually axis-aligned, so fiddle them so they're normal aligned instead\n\t\t\td = -DotProduct(mesh->normals_array[i], mesh->snormals_array[i]);\n\t\t\tVectorMA(mesh->snormals_array[i], d, mesh->normals_array[i], mesh->snormals_array[i]);\n\t\t\td = -DotProduct(mesh->normals_array[i], mesh->tnormals_array[i]);\n\t\t\tVectorMA(mesh->tnormals_array[i], d, mesh->normals_array[i], mesh->tnormals_array[i]);\n\t\t\tVectorNormalize(mesh->snormals_array[i]);\n\t\t\tVectorNormalize(mesh->tnormals_array[i]);\n\t\t}\n\n\t\t//q1bsp has no colour information (fixme: sample from the lightmap?)\n\t\tfor (sty = 0; sty < 1; sty++)\n\t\t{\n\t\t\tmesh->colors4f_array[sty][i][0] = 1;\n\t\t\tmesh->colors4f_array[sty][i][1] = 1;\n\t\t\tmesh->colors4f_array[sty][i][2] = 1;\n\t\t\tmesh->colors4f_array[sty][i][3] = 1;\n\t\t}\n\t}\n}\n#endif\n\n#ifndef SERVERONLY\nstatic void Mod_Batches_BuildModelMeshes(model_t *mod, int maxverts, int maxindicies, void (*build)(model_t *mod, msurface_t *surf, builddata_t *bd), builddata_t *bd, int lmmerge)\n{\n\tbatch_t *batch;\n\tmsurface_t *surf;\n\tmesh_t *mesh;\n\tint numverts = 0;\n\tint numindicies = 0;\n\tint j, i;\n\tint sortid;\n\tint sty;\n\tvbo_t vbo;\n\tint styles = mod->lightmaps.surfstyles;\n\tchar *ptr;\n\n\tmemset(&vbo, 0, sizeof(vbo));\n\tvbo.indicies.sysptr = ZG_Malloc(&mod->memgroup, sizeof(index_t) * maxindicies);\n\tptr = ZG_Malloc(&mod->memgroup, (sizeof(vecV_t)+sizeof(vec2_t)*(1+styles)+sizeof(vec3_t)*3+sizeof(vec4_t)*styles)* maxverts);\n\n\tvbo.coord.sysptr = ptr;\n\tptr += sizeof(vecV_t)*maxverts;\n\tfor (sty = 0; sty < styles; sty++)\n\t{\n\t\tvbo.colours[sty].sysptr = ptr;\n\t\tptr += sizeof(vec4_t)*maxverts;\n\t}\n\tfor (; sty < MAXRLIGHTMAPS; sty++)\n\t\tvbo.colours[sty].sysptr = NULL;\n\tvbo.texcoord.sysptr = ptr;\n\tptr += sizeof(vec2_t)*maxverts;\n\tsty = 0;\n\tfor (; sty < styles; sty++)\n\t{\n\t\tvbo.lmcoord[sty].sysptr = ptr;\n\t\tptr += sizeof(vec2_t)*maxverts;\n\t}\n\tfor (; sty < MAXRLIGHTMAPS; sty++)\n\t\tvbo.lmcoord[sty].sysptr = NULL;\n\tvbo.normals.sysptr = ptr;\n\tptr += sizeof(vec3_t)*maxverts;\n\tvbo.svector.sysptr = ptr;\n\tptr += sizeof(vec3_t)*maxverts;\n\tvbo.tvector.sysptr = ptr;\n\tptr += sizeof(vec3_t)*maxverts;\n\n\tnumindicies = 0;\n\tnumverts = 0;\n\n\t//build each mesh\n\tfor (sortid=0; sortid<SHADER_SORT_COUNT; sortid++)\n\t{\n\t\tfor (batch = mod->batches[sortid]; batch; batch = batch->next)\n\t\t{\n\t\t\tfor (j = 0; j < batch->maxmeshes; j++)\n\t\t\t{\n\t\t\t\tsurf = (msurface_t*)batch->mesh[j];\n\t\t\t\tmesh = surf->mesh;\n\t\t\t\tbatch->mesh[j] = mesh;\n\n\t\t\t\tmesh->vbofirstvert = numverts;\n\t\t\t\tmesh->vbofirstelement = numindicies;\n\t\t\t\tnumverts += mesh->numvertexes;\n\t\t\t\tnumindicies += mesh->numindexes;\n\n\t\t\t\t//set up the arrays. the arrangement is required for the backend to optimise vbos\n\t\t\t\tmesh->xyz_array = (vecV_t*)vbo.coord.sysptr + mesh->vbofirstvert;\n\t\t\t\tmesh->st_array = (vec2_t*)vbo.texcoord.sysptr + mesh->vbofirstvert;\n\t\t\t\tfor (sty = 0; sty < MAXRLIGHTMAPS; sty++)\n\t\t\t\t{\n\t\t\t\t\tif (vbo.lmcoord[sty].sysptr)\n\t\t\t\t\t\tmesh->lmst_array[sty] = (vec2_t*)vbo.lmcoord[sty].sysptr + mesh->vbofirstvert;\n\t\t\t\t\telse\n\t\t\t\t\t\tmesh->lmst_array[sty] = NULL;\n\t\t\t\t\tif (vbo.colours[sty].sysptr)\n\t\t\t\t\t\tmesh->colors4f_array[sty] = (vec4_t*)vbo.colours[sty].sysptr + mesh->vbofirstvert;\n\t\t\t\t\telse\n\t\t\t\t\t\tmesh->colors4f_array[sty] = NULL;\n\t\t\t\t}\n\t\t\t\tmesh->normals_array = (vec3_t*)vbo.normals.sysptr + mesh->vbofirstvert;\n\t\t\t\tmesh->snormals_array = (vec3_t*)vbo.svector.sysptr + mesh->vbofirstvert;\n\t\t\t\tmesh->tnormals_array = (vec3_t*)vbo.tvector.sysptr + mesh->vbofirstvert;\n\t\t\t\tmesh->indexes = (index_t*)vbo.indicies.sysptr + mesh->vbofirstelement;\n\n\t\t\t\tmesh->vbofirstvert = 0;\n\t\t\t\tmesh->vbofirstelement = 0;\n\n\t\t\t\tbuild(mod, surf, bd);\n\n\t\t\t\tif (lmmerge != 1)\n\t\t\t\t{\n\t\t\t\t\tfor (sty = 0; sty < MAXRLIGHTMAPS; sty++)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (surf->lightmaptexturenums[sty] >= 0)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (mod->lightmaps.deluxemapping)\n\t\t\t\t\t\t\t\tsurf->lightmaptexturenums[sty] /= 2;\n\t\t\t\t\t\t\tif (mesh->lmst_array[sty])\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tint soffset = surf->lightmaptexturenums[sty] % mod->lightmaps.mergew;\n\t\t\t\t\t\t\t\tint toffset = surf->lightmaptexturenums[sty] / mod->lightmaps.mergew;\n\t\t\t\t\t\t\t\tfloat smul = 1.0/mod->lightmaps.mergew;\n\t\t\t\t\t\t\t\tfloat tmul = 1.0/mod->lightmaps.mergeh;\n\t\t\t\t\t\t\t\tfor (i = 0; i < mesh->numvertexes; i++)\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tmesh->lmst_array[sty][i][0] += soffset;\n\t\t\t\t\t\t\t\t\tmesh->lmst_array[sty][i][0] *= smul;\n\t\t\t\t\t\t\t\t\tmesh->lmst_array[sty][i][1] += toffset;\n\t\t\t\t\t\t\t\t\tmesh->lmst_array[sty][i][1] *= tmul;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tsurf->lightmaptexturenums[sty] /= lmmerge;\n\t\t\t\t\t\t\tif (mod->lightmaps.deluxemapping)\n\t\t\t\t\t\t\t\tsurf->lightmaptexturenums[sty] *= 2;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tbatch->meshes = 0;\n\t\t\tbatch->firstmesh = 0;\n\t\t}\n\t}\n}\n\n#ifdef Q1BSPS\n//q1 autoanimates. if the frame is set, it uses the alternate animation.\nstatic void Mod_UpdateBatchShader_Q1 (struct batch_s *batch)\n{\n\ttexture_t *base = batch->texture;\n\tunsigned int\trelative;\n\tint\t\t\t\tcount;\n\n\tif (batch->ent->framestate.g[FS_REG].frame[0])\n\t{\n\t\tif (base->alternate_anims)\n\t\t\tbase = base->alternate_anims;\n\t}\n\n\tif (base->anim_total)\n\t{\n\t\trelative = (unsigned int)(cl.time*10) % base->anim_total;\n\n\t\tcount = 0;\n\t\twhile (base->anim_min > relative || base->anim_max <= relative)\n\t\t{\n\t\t\tbase = base->anim_next;\n\t\t\tif (!base)\n\t\t\t\tSys_Error (\"R_TextureAnimation: broken cycle\");\n\t\t\tif (++count > 100)\n\t\t\t\tSys_Error (\"R_TextureAnimation: infinite cycle\");\n\t\t}\n\t}\n\n\tbatch->shader = base->shader;\n}\n\n// copy of Q1s, but with a different framerate\nstatic void Mod_UpdateBatchShader_HL (struct batch_s *batch)\n{\n\ttexture_t *base = batch->texture;\n\tunsigned int\trelative;\n\tint\t\t\t\tcount;\n\n\tif (batch->ent->framestate.g[FS_REG].frame[0])\n\t{\n\t\tif (base->alternate_anims)\n\t\t\tbase = base->alternate_anims;\n\t}\n\n\tif (base->anim_total)\n\t{\n\t\trelative = (unsigned int)(cl.time*20) % base->anim_total;\n\n\t\tcount = 0;\n\t\twhile (base->anim_min > relative || base->anim_max <= relative)\n\t\t{\n\t\t\tbase = base->anim_next;\n\t\t\tif (!base)\n\t\t\t\tSys_Error (\"R_TextureAnimation: broken cycle\");\n\t\t\tif (++count > 100)\n\t\t\t\tSys_Error (\"R_TextureAnimation: infinite cycle\");\n\t\t}\n\t}\n\n\tbatch->shader = base->shader;\n}\n#endif\n\n#ifdef Q2BSPS\n//q2 has direct control over the texture frames used, but typically has the client generate the frame (different flags autogenerate different ranges).\nstatic void Mod_UpdateBatchShader_Q2 (struct batch_s *batch)\n{\n\ttexture_t *base = batch->texture;\n\tint\t\treletive;\n\tint frame = batch->ent->framestate.g[FS_REG].frame[0];\n\tif (batch->ent == &r_worldentity)\n\t\tframe = cl.time*2;\n\n\tif (base->anim_total)\n\t{\n\t\treletive = frame % base->anim_total;\n\t\twhile (reletive --> 0)\n\t\t{\n\t\t\tbase = base->anim_next;\n\t\t\tif (!base)\n\t\t\t\tSys_Error (\"R_TextureAnimation: broken cycle\");\n\t\t}\n\t}\n\n\tbatch->shader = base->shader;\n}\n#endif\n\n#define lmmerge(i) ((i>=0)?i/merge:i)\n/*\nbatch->firstmesh is set only in and for this function, its cleared out elsewhere\n*/\nstatic int Mod_Batches_Generate(model_t *mod)\n{\n//#define NOBATCH\t//define this to force each surface into its own batch...\n\n\tint i;\n\tmsurface_t *surf;\n\tshader_t *shader;\n\tint sortid;\n\tbatch_t *batch, *lbatch = NULL;\n\tvec4_t plane;\n\timage_t *envmap;\n\n\tint merge = mod->lightmaps.mergew*mod->lightmaps.mergeh;\n\tif (!merge)\n\t\tmerge = mod->lightmaps.mergew = mod->lightmaps.mergeh = 1;\t//no division by 0 please...\n\tif (mod->lightmaps.deluxemapping)\n\t{\n\t\tmod->lightmaps.count = ((mod->lightmaps.count+1)/2+merge-1) & ~(merge-1);\n\t\tmod->lightmaps.count /= merge;\n\t\tmod->lightmaps.count *= 2;\n\t}\n\telse\n\t{\n\t\tmod->lightmaps.count = (mod->lightmaps.count+merge-1) & ~(merge-1);\n\t\tmod->lightmaps.count /= merge;\n\t}\n\tmod->lightmaps.width *= mod->lightmaps.mergew;\n\tmod->lightmaps.height *= mod->lightmaps.mergeh;\n\n\tmod->numbatches = 0;\n\n\t//for each surface, find a suitable batch to insert it into.\n\t//we use 'firstmesh' to avoid chucking out too many verts in a single vbo (gl2 hardware tends to have a 16bit limit)\n\tfor (i=0; i<mod->nummodelsurfaces; i++)\n\t{\n\t\tsurf = mod->surfaces + mod->firstmodelsurface + i;\n\t\tshader = surf->texinfo->texture->shader;\n\t\tenvmap = surf->envmap;\n\n\t\tif (surf->flags & SURF_NODRAW)\n\t\t{\n\t\t\tshader = R_RegisterShader(\"nodraw\", SUF_NONE, \"{\\nsurfaceparm nodraw\\n}\");\n\t\t\tsortid = shader->sort;\n\t\t\tVectorClear(plane);\n\t\t\tplane[3] = 0;\n\t\t\tenvmap = NULL;\n\t\t}\n\t\telse if (shader)\n\t\t{\n\t\t\tsortid = shader->sort;\n\n\t\t\t//shaders that are portals need to be split into separate batches to have the same surface planes\n\t\t\tif (sortid == SHADER_SORT_PORTAL || (shader->flags & (SHADER_HASREFLECT | SHADER_HASREFRACT)))\n\t\t\t{\n\t\t\t\tif (surf->flags & SURF_PLANEBACK)\n\t\t\t\t{\n\t\t\t\t\tVectorNegate(surf->plane->normal, plane);\n\t\t\t\t\tplane[3] = -surf->plane->dist;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tVectorCopy(surf->plane->normal, plane);\n\t\t\t\t\tplane[3] = surf->plane->dist;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tVectorClear(plane);\n\t\t\t\tplane[3] = 0;\n\t\t\t}\n\n\t\t\tif (!(shader->flags & SHADER_HASREFLECTCUBE))\n\t\t\t\tenvmap = NULL;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tsortid = SHADER_SORT_OPAQUE;\n\t\t\tVectorClear(plane);\n\t\t\tplane[3] = 0;\n\t\t}\n\n#ifdef NOBATCH\n\t\tbatch = NULL;\n\t\t(void)lbatch;\n#else\n\t\tif (lbatch && (\n\t\t\t\t\tlbatch->texture == surf->texinfo->texture &&\n\t\t\t\t\tlbatch->shader == shader &&\n\t\t\t\t\tlbatch->lightmap[0] == lmmerge(surf->lightmaptexturenums[0]) &&\n\t\t\t\t\tVector4Compare(plane, lbatch->user.bmodel.plane) &&\n\t\t\t\t\tlbatch->firstmesh + surf->mesh->numvertexes <= MAX_INDICIES &&\n\t#if MAXRLIGHTMAPS > 1\n\t\t\t\t\tlbatch->lightmap[1] == lmmerge(surf->lightmaptexturenums[1]) &&\n\t\t\t\t\tlbatch->lightmap[2] == lmmerge(surf->lightmaptexturenums[2]) &&\n\t\t\t\t\tlbatch->lightmap[3] == lmmerge(surf->lightmaptexturenums[3]) &&\n\t#endif\n\t\t\t\t\tlbatch->fog == surf->fog &&\n\t\t\t\t\tlbatch->envmap == envmap))\n\t\t\tbatch = lbatch;\n\t\telse\n\t\t{\n\t\t\tfor (batch = mod->batches[sortid]; batch; batch = batch->next)\n\t\t\t{\n\t\t\t\tif (\n\t\t\t\t\t\t\tbatch->texture == surf->texinfo->texture &&\n\t\t\t\t\t\t\tbatch->shader == shader &&\n\t\t\t\t\t\t\tbatch->lightmap[0] == lmmerge(surf->lightmaptexturenums[0]) &&\n\t\t\t\t\t\t\tVector4Compare(plane, batch->user.bmodel.plane) &&\n\t\t\t\t\t\t\tbatch->firstmesh + surf->mesh->numvertexes <= MAX_INDICIES &&\n\t#if MAXRLIGHTMAPS > 1\n\t\t\t\t\t\t\tbatch->lightmap[1] == lmmerge(surf->lightmaptexturenums[1]) &&\n\t\t\t\t\t\t\tbatch->lightmap[2] == lmmerge(surf->lightmaptexturenums[2]) &&\n\t\t\t\t\t\t\tbatch->lightmap[3] == lmmerge(surf->lightmaptexturenums[3]) &&\n\t#endif\n\t\t\t\t\t\t\tbatch->fog == surf->fog &&\n\t\t\t\t\t\t\tbatch->envmap == envmap)\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n#endif\n\t\tif (!batch)\n\t\t{\n\t\t\tbatch = ZG_Malloc(&mod->memgroup, sizeof(*batch));\n\t\t\tbatch->lightmap[0] = lmmerge(surf->lightmaptexturenums[0]);\n#if MAXRLIGHTMAPS > 1\n\t\t\tbatch->lightmap[1] = lmmerge(surf->lightmaptexturenums[1]);\n\t\t\tbatch->lightmap[2] = lmmerge(surf->lightmaptexturenums[2]);\n\t\t\tbatch->lightmap[3] = lmmerge(surf->lightmaptexturenums[3]);\n#endif\n\t\t\tbatch->texture = surf->texinfo->texture;\n\t\t\tbatch->shader = shader;\n\t\t\tif (surf->texinfo->texture->alternate_anims || surf->texinfo->texture->anim_total)\n\t\t\t{\n\t\t\t\tswitch (mod->fromgame)\n\t\t\t\t{\n#ifdef Q2BSPS\n\t\t\t\tcase fg_quake2:\n\t\t\t\t\tbatch->buildmeshes = Mod_UpdateBatchShader_Q2;\n\t\t\t\t\tbreak;\n#endif\n#ifdef Q1BSPS\n\t\t\t\tcase fg_quake:\n\t\t\t\t\tbatch->buildmeshes = Mod_UpdateBatchShader_Q1;\n\t\t\t\t\tbreak;\n\t\t\t\tcase fg_halflife:\n\t\t\t\t\tbatch->buildmeshes = Mod_UpdateBatchShader_HL;\n\t\t\t\t\tbreak;\n#endif\n\t\t\t\tdefault:\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tbatch->next = mod->batches[sortid];\n\t\t\tbatch->ent = &r_worldentity;\n\t\t\tbatch->fog = surf->fog;\n\t\t\tbatch->envmap = envmap;\n\t\t\tVector4Copy(plane, batch->user.bmodel.plane);\n\n\t\t\tmod->batches[sortid] = batch;\n\t\t}\n\t\tbatch->user.bmodel.ebobatch = -1;\n\n\t\tsurf->sbatch = batch;\t//let the surface know which batch its in\n\t\tbatch->maxmeshes++;\n\t\tbatch->firstmesh += surf->mesh->numvertexes;\n\n\t\tlbatch = batch;\n\t}\n\treturn merge;\n#undef lmmerge\n}\n\nvoid Mod_LightmapAllocInit(lmalloc_t *lmallocator, qboolean hasdeluxe, unsigned int width, unsigned int height, int firstlm)\n{\n\tmemset(lmallocator, 0, sizeof(*lmallocator));\n\tlmallocator->deluxe = hasdeluxe;\n\tlmallocator->lmnum = firstlm;\n\tlmallocator->firstlm = firstlm;\n\n\tlmallocator->width = min(LMBLOCK_SIZE_MAX, width);\n\tlmallocator->height = min(LMBLOCK_SIZE_MAX, height);\n}\nvoid Mod_LightmapAllocDone(lmalloc_t *lmallocator, model_t *mod)\n{\n\tmod->lightmaps.first = lmallocator->firstlm;\n\tmod->lightmaps.count = (lmallocator->lmnum - lmallocator->firstlm);\n\tif (lmallocator->allocated[0])\t//lmnum was only *COMPLETE* lightmaps that we allocated, and does not include the one we're currently building.\n\t\tmod->lightmaps.count++;\n\n\tif (lmallocator->deluxe)\n\t{\n\t\tmod->lightmaps.first*=2;\n\t\tmod->lightmaps.count*=2;\n\t\tmod->lightmaps.deluxemapping = true;\n\t}\n\telse\n\t\tmod->lightmaps.deluxemapping = false;\n}\nvoid Mod_LightmapAllocBlock(lmalloc_t *lmallocator, int w, int h, unsigned short *x, unsigned short *y, int *tnum)\n{\n\tint best, best2;\n\tint i, j;\n\n\tfor(;;)\n\t{\n\t\tbest = lmallocator->height;\n\n\t\tfor (i = 0; i <= lmallocator->width - w; i++)\n\t\t{\n\t\t\tbest2 = 0;\n\n\t\t\tfor (j=0; j < w; j++)\n\t\t\t{\n\t\t\t\tif (lmallocator->allocated[i+j] >= best)\n\t\t\t\t\tbreak;\n\t\t\t\tif (lmallocator->allocated[i+j] > best2)\n\t\t\t\t\tbest2 = lmallocator->allocated[i+j];\n\t\t\t}\n\t\t\tif (j == w)\n\t\t\t{\t// this is a valid spot\n\t\t\t\t*x = i;\n\t\t\t\t*y = best = best2;\n\t\t\t}\n\t\t}\n\n\t\tif (best + h > lmallocator->height)\n\t\t{\n\t\t\tmemset(lmallocator->allocated, 0, sizeof(lmallocator->allocated));\n\t\t\tlmallocator->lmnum++;\n\t\t\tcontinue;\n\t\t}\n\n\t\tfor (i=0; i < w; i++)\n\t\t\tlmallocator->allocated[*x + i] = best + h;\n\n\t\tif (lmallocator->deluxe)\n\t\t\t*tnum = lmallocator->lmnum*2;\n\t\telse\n\t\t\t*tnum = lmallocator->lmnum;\n\t\tbreak;\n\t}\n}\n\n#ifdef Q3BSPS\nstatic void Mod_Batches_SplitLightmaps(model_t *mod, int lmmerge)\n{\n\tbatch_t *batch;\n\tbatch_t *nb;\n\tint i, j, sortid;\n\tmsurface_t *surf;\n\tint sty;\n\tint lmscale = 1;\n\n\tif (mod->lightmaps.deluxemapping)\n\t{\n\t\tlmmerge *= 2;\n\t\tlmscale *= 2;\n\t}\n\n\tfor (sortid = 0; sortid < SHADER_SORT_COUNT; sortid++)\n\tfor (batch = mod->batches[sortid]; batch != NULL; batch = batch->next)\n\t{\n\t\tsurf = (msurface_t*)batch->mesh[0];\n\t\tfor (sty = 0; sty < MAXRLIGHTMAPS; sty++)\n\t\t{\n\t\t\tbatch->lightmap[sty] = (surf->lightmaptexturenums[sty]>=0)?lmscale*(surf->lightmaptexturenums[sty]/lmmerge):surf->lightmaptexturenums[sty];\n\t\t\tbatch->lmlightstyle[sty] = surf->styles[sty];\n\t\t\tbatch->vtlightstyle[sty] = surf->vlstyles[sty];\n\t\t}\n\n\t\tfor (j = 1; j < batch->maxmeshes; j++)\n\t\t{\n\t\t\tsurf = (msurface_t*)batch->mesh[j];\n\t\t\tfor (sty = 0; sty < MAXRLIGHTMAPS; sty++)\n\t\t\t{\n\t\t\t\tint lm = (surf->lightmaptexturenums[sty]>=0)?lmscale*(surf->lightmaptexturenums[sty]/lmmerge):surf->lightmaptexturenums[sty];\n\t\t\t\tif (lm != batch->lightmap[sty] ||\n\t\t\t\t\t//fixme: we should merge later (reverted matching) surfaces into the prior batch\n\t\t\t\t\tsurf->styles[sty] != batch->lmlightstyle[sty] ||\n\t\t\t\t\tsurf->vlstyles[sty] != batch->vtlightstyle[sty])\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (sty < MAXRLIGHTMAPS)\n\t\t\t{\n\t\t\t\tnb = ZG_Malloc(&mod->memgroup, sizeof(*batch));\n\t\t\t\t*nb = *batch;\n\t\t\t\tbatch->next = nb;\n\n\t\t\t\tnb->mesh = batch->mesh + j*2;\n\t\t\t\tnb->maxmeshes = batch->maxmeshes - j;\n\t\t\t\tbatch->maxmeshes = j;\n\t\t\t\tfor (sty = 0; sty < MAXRLIGHTMAPS; sty++)\n\t\t\t\t{\n\t\t\t\t\tint lm = (surf->lightmaptexturenums[sty]>=0)?lmscale*(surf->lightmaptexturenums[sty]/lmmerge):surf->lightmaptexturenums[sty];\n\t\t\t\t\tnb->lightmap[sty] = lm;\n\t\t\t\t\tnb->lmlightstyle[sty] = surf->styles[sty];\n\t\t\t\t\tnb->vtlightstyle[sty] = surf->vlstyles[sty];\n\t\t\t\t}\n\n\t\t\t\tmemmove(nb->mesh, batch->mesh+j, sizeof(msurface_t*)*nb->maxmeshes);\n\n\t\t\t\tfor (i = 0; i < nb->maxmeshes; i++)\n\t\t\t\t{\n\t\t\t\t\tsurf = (msurface_t*)nb->mesh[i];\n\t\t\t\t\tsurf->sbatch = nb;\n\t\t\t\t}\n\n\t\t\t\tbatch = nb;\n\t\t\t\tj = 1;\n\t\t\t}\n\t\t}\n\t}\n}\n#endif\n\n#if defined(Q1BSPS) || defined(Q2BSPS)\nstatic void Mod_LightmapAllocSurf(lmalloc_t *lmallocator, msurface_t *surf, int surfstyle)\n{\n\tint smax, tmax;\n\tsmax = (surf->extents[0]>>surf->lmshift)+1;\n\ttmax = (surf->extents[1]>>surf->lmshift)+1;\n\n\tif (isDedicated ||\n\t\t(surf->texinfo->texture->shader && !(surf->texinfo->texture->shader->flags & SHADER_HASLIGHTMAP)) || //fte\n\t\t(surf->flags & (SURF_DRAWSKY|SURF_DRAWTILED)) ||\t//q1\n\t\t(surf->texinfo->flags & TEX_SPECIAL) ||\t//the original 'no lightmap'\n\t\tsmax > lmallocator->width || tmax > lmallocator->height || smax < 0 || tmax < 0)\t//bugs/bounds/etc\n\t{\n\t\tsurf->lightmaptexturenums[surfstyle] = -1;\n\t\treturn;\n\t}\n\n\tMod_LightmapAllocBlock (lmallocator, smax, tmax, &surf->light_s[surfstyle], &surf->light_t[surfstyle], &surf->lightmaptexturenums[surfstyle]);\n}\n\n/*\nallocates lightmaps and splits batches upon lightmap boundaries\n*/\nstatic void Mod_Batches_AllocLightmaps(model_t *mod)\n{\n\tbatch_t *batch;\n\tbatch_t *nb;\n\tlmalloc_t lmallocator;\n\tint i, j, sortid;\n\tmsurface_t *surf;\n\tint sty;\n\n\tsize_t samps = 0;\n\n\t//small models don't have many surfaces, don't allocate a smegging huge lightmap that simply won't be used.\n\tfor (i=0, j=0; i<mod->nummodelsurfaces; i++)\n\t{\n\t\tsurf = mod->surfaces + mod->firstmodelsurface + i;\n\t\tif (surf->texinfo->flags & TEX_SPECIAL)\n\t\t\tcontinue;\t//surfaces with no lightmap should not count torwards anything.\n\t\tsamps += ((surf->extents[0]>>surf->lmshift)+1) * ((surf->extents[1]>>surf->lmshift)+1);\n\n\t\tif (j < (surf->extents[0]>>surf->lmshift)+1)\n\t\t\tj = (surf->extents[0]>>surf->lmshift)+1;\n\t\tif (j < (surf->extents[1]>>surf->lmshift)+1)\n\t\t\tj = (surf->extents[1]>>surf->lmshift)+1;\n\t}\n\tsamps /= 4;\n\tsamps = sqrt(samps);\n\tif (j > 128 || r_dynamic.ival <= 0)\n\t\tsamps *= 2;\n\tmod->lightmaps.width = bound(j, samps, LMBLOCK_SIZE_MAX);\n\tmod->lightmaps.height = bound(j, samps, LMBLOCK_SIZE_MAX);\n\tfor (i = 0; (1<<i) < mod->lightmaps.width; i++);\n\tmod->lightmaps.width = 1<<i;\n\tfor (i = 0; (1<<i) < mod->lightmaps.height; i++);\n\tmod->lightmaps.height = 1<<i;\n\tmod->lightmaps.width = bound(64, mod->lightmaps.width, sh_config.texture2d_maxsize);\n\tmod->lightmaps.height = bound(64, mod->lightmaps.height, sh_config.texture2d_maxsize);\n\n\tMod_LightmapAllocInit(&lmallocator, mod->deluxdata != NULL, mod->lightmaps.width, mod->lightmaps.height, 0x50);\n\n\tfor (sortid = 0; sortid < SHADER_SORT_COUNT; sortid++)\n\tfor (batch = mod->batches[sortid]; batch != NULL; batch = batch->next)\n\t{\n\t\tsurf = (msurface_t*)batch->mesh[0];\n\t\tMod_LightmapAllocSurf (&lmallocator, surf, 0);\n\t\tfor (sty = 1; sty < MAXRLIGHTMAPS; sty++)\n\t\t\tsurf->lightmaptexturenums[sty] = -1;\n\t\tfor (sty = 0; sty < MAXRLIGHTMAPS; sty++)\n\t\t{\n\t\t\tbatch->lightmap[sty] = surf->lightmaptexturenums[sty];\n\t\t\tbatch->lmlightstyle[sty] = INVALID_LIGHTSTYLE;//don't do special backend rendering of lightstyles.\n\t\t\tbatch->vtlightstyle[sty] = 255;//don't do special backend rendering of lightstyles.\n\t\t}\n\n\t\tfor (j = 1; j < batch->maxmeshes; j++)\n\t\t{\n\t\t\tsurf = (msurface_t*)batch->mesh[j];\n\t\t\tMod_LightmapAllocSurf (&lmallocator, surf, 0);\n\t\t\tfor (sty = 1; sty < MAXRLIGHTMAPS; sty++)\n\t\t\t\tsurf->lightmaptexturenums[sty] = -1;\n\t\t\tif (surf->lightmaptexturenums[0] != batch->lightmap[0])\n\t\t\t{\n\t\t\t\tnb = ZG_Malloc(&mod->memgroup, sizeof(*batch));\n\t\t\t\t*nb = *batch;\n\t\t\t\tbatch->next = nb;\n\n\t\t\t\tnb->mesh = batch->mesh + j*2;\n\t\t\t\tnb->maxmeshes = batch->maxmeshes - j;\n\t\t\t\tbatch->maxmeshes = j;\n\t\t\t\tfor (sty = 0; sty < MAXRLIGHTMAPS; sty++)\n\t\t\t\t\tnb->lightmap[sty] = surf->lightmaptexturenums[sty];\n\n\t\t\t\tmemmove(nb->mesh, batch->mesh+j, sizeof(msurface_t*)*nb->maxmeshes);\n\n\t\t\t\tfor (i = 0; i < nb->maxmeshes; i++)\n\t\t\t\t{\n\t\t\t\t\tsurf = (msurface_t*)nb->mesh[i];\n\t\t\t\t\tsurf->sbatch = nb;\n\t\t\t\t}\n\n\t\t\t\tbatch = nb;\n\t\t\t\tj = 0;\n\t\t\t}\n\t\t}\n\t}\n\n\tMod_LightmapAllocDone(&lmallocator, mod);\n}\n#endif\n\nextern void Surf_CreateSurfaceLightmap (msurface_t *surf, int shift);\n//if build is NULL, uses q1/q2 surf generation, and allocates lightmaps\nvoid Mod_Batches_Build(model_t *mod, builddata_t *bd)\n{\n\tint i;\n\tint numverts = 0, numindicies=0;\n\tmsurface_t *surf;\n\tmesh_t *mesh;\n\tmesh_t **bmeshes;\n\tint sortid;\n\tbatch_t *batch;\n\tmesh_t *meshlist;\n\tint merge = 1;\n\n\tif (!mod->textures)\n\t\treturn;\n\n\tif (mod->firstmodelsurface + mod->nummodelsurfaces > mod->numsurfaces)\n\t\tSys_Error(\"submodel %s surface range is out of bounds\\n\", mod->name);\n\n\tif (bd)\n\t\tmeshlist = NULL;\n\telse\n\t\tmeshlist = ZG_Malloc(&mod->memgroup, sizeof(mesh_t) * mod->nummodelsurfaces);\n\n\tfor (i=0; i<mod->nummodelsurfaces; i++)\n\t{\n\t\tsurf = mod->surfaces + i + mod->firstmodelsurface;\n\t\tif (meshlist)\n\t\t{\n\t\t\tmesh = surf->mesh = &meshlist[i];\n\t\t\tmesh->numvertexes = surf->numedges;\n\t\t\tmesh->numindexes = (surf->numedges-2)*3;\n\t\t}\n\t\telse\n\t\t\tmesh = surf->mesh;\n\n\t\tif (mesh->numindexes <= 0 || mesh->numvertexes < 1)\n\t\t{\n\t\t\tmesh->numindexes = 0;\n\t\t\tmesh->numvertexes = 0;\n\t\t}\n\n\t\tnumverts += mesh->numvertexes;\n\t\tnumindicies += mesh->numindexes;\n//\t\tsurf->lightmaptexturenum = -1;\n\t}\n\n\t/*assign each mesh to a batch, generating as needed*/\n\tmerge = Mod_Batches_Generate(mod);\n\n\tbmeshes = ZG_Malloc(&mod->memgroup, sizeof(*bmeshes)*mod->nummodelsurfaces*R_MAX_RECURSE);\n\n\t//we now know which batch each surface is in, and how many meshes there are in each batch.\n\t//allocate the mesh-pointer-lists for each batch. *2 for recursion.\n\tfor (i = 0, sortid = 0; sortid < SHADER_SORT_COUNT; sortid++)\n\tfor (batch = mod->batches[sortid]; batch != NULL; batch = batch->next)\n\t{\n\t\tbatch->mesh = bmeshes + i;\n\t\ti += batch->maxmeshes*R_MAX_RECURSE;\n\t}\n\t//store the *surface* into the batch's mesh list (yes, this is an evil cast hack, but at least both are pointers)\n\tfor (i=0; i<mod->nummodelsurfaces; i++)\n\t{\n\t\tsurf = mod->surfaces + mod->firstmodelsurface + i;\n\t\tsurf->sbatch->mesh[surf->sbatch->meshes++] = (mesh_t*)surf;\n\t}\n\n#if defined(Q1BSPS) || defined(Q2BSPS)\n\tif (!bd)\n\t{\n\t\tMod_Batches_AllocLightmaps(mod);\n\n\t\tmod->lightmaps.surfstyles = 1;\n\t\tMod_Batches_BuildModelMeshes(mod, numverts, numindicies, ModQ1_Batches_BuildQ1Q2Poly, bd, merge);\n\t}\n#endif\n\tif (bd)\n\t{\n\t\tif (bd->paintlightmaps)\n\t\t\tMod_Batches_AllocLightmaps(mod);\n\t\telse\n\t\t\tMod_Batches_SplitLightmaps(mod, merge);\n\t\tMod_Batches_BuildModelMeshes(mod, numverts, numindicies, bd->buildfunc, bd, merge);\n\t}\n\n\tif (BE_GenBrushModelVBO)\n\t\tBE_GenBrushModelVBO(mod);\n}\n#endif\n\n\n/*\n=================\nMod_SetParent\n=================\n*/\nvoid Mod_SetParent (mnode_t *node, mnode_t *parent)\n{\n\tif (!node)\n\t\treturn;\n\tnode->parent = parent;\n\tif (node->contents < 0)\n\t\treturn;\n\tMod_SetParent (node->children[0], node);\n\tMod_SetParent (node->children[1], node);\n}\n\n#if defined(Q1BSPS) || defined(Q2BSPS)\n/*\n=================\nMod_LoadEdges\n=================\n*/\nqboolean Mod_LoadEdges (model_t *loadmodel, qbyte *mod_base, lump_t *l, subbsp_t subbsp)\n{\n\tmedge_t *out;\n\tint \ti, count;\n\t\n\tif (subbsp == sb_long1 || subbsp == sb_long2)\n\t{\n\t\tdledge_t *in = (void *)(mod_base + l->fileofs);\n\t\tcount = l->filelen / sizeof(*in);\n\t\tif (l->filelen % sizeof(*in) || count > SANITY_LIMIT(*out))\n\t\t{\n\t\t\tCon_Printf (\"MOD_LoadBmodel: funny lump size in %s\\n\", loadmodel->name);\n\t\t\treturn false;\n\t\t}\n\t\tout = ZG_Malloc(&loadmodel->memgroup, (count + 1) * sizeof(*out));\t\n\n\t\tloadmodel->edges = out;\n\t\tloadmodel->numedges = count;\n\n\t\tfor ( i=0 ; i<count ; i++, in++, out++)\n\t\t{\n\t\t\tout->v[0] = LittleLong(in->v[0]);\n\t\t\tout->v[1] = LittleLong(in->v[1]);\n\t\t}\n\t}\n\telse\n\t{\n\t\tdsedge_t *in = (void *)(mod_base + l->fileofs);\n\t\tcount = l->filelen / sizeof(*in);\n\t\tif (l->filelen % sizeof(*in) || count > SANITY_LIMIT(*out))\n\t\t{\n\t\t\tCon_Printf (\"MOD_LoadBmodel: funny lump size in %s\\n\", loadmodel->name);\n\t\t\treturn false;\n\t\t}\n\t\tout = ZG_Malloc(&loadmodel->memgroup, (count + 1) * sizeof(*out));\n\n\t\tloadmodel->edges = out;\n\t\tloadmodel->numedges = count;\n\n\t\tfor ( i=0 ; i<count ; i++, in++, out++)\n\t\t{\n\t\t\tout->v[0] = (unsigned short)LittleShort(in->v[0]);\n\t\t\tout->v[1] = (unsigned short)LittleShort(in->v[1]);\n\t\t}\n\t}\n\n\treturn true;\n}\n\n/*\n=================\nMod_LoadMarksurfaces\n=================\n*/\nqboolean Mod_LoadMarksurfaces (model_t *loadmodel, qbyte *mod_base, lump_t *l, subbsp_t subbsp)\n{\t\n\tint\t\ti, j, count;\n\tmsurface_t **out;\n\n\tif (subbsp == sb_long1 || subbsp == sb_long2)\n\t{\n\t\tint\t\t*inl;\n\t\tinl = (void *)(mod_base + l->fileofs);\n\t\tcount = l->filelen / sizeof(*inl);\n\t\tif (l->filelen % sizeof(*inl) || count > SANITY_LIMIT(*out))\n\t\t{\n\t\t\tCon_Printf (CON_ERROR \"MOD_LoadBmodel: funny lump size in %s\\n\",loadmodel->name);\n\t\t\treturn false;\n\t\t}\n\t\tout = ZG_Malloc(&loadmodel->memgroup, count*sizeof(*out));\n\n\t\tloadmodel->marksurfaces = out;\n\t\tloadmodel->nummarksurfaces = count;\n\n\t\tfor ( i=0 ; i<count ; i++)\n\t\t{\n\t\t\tj = (unsigned int)LittleLong(inl[i]);\n\t\t\tif (j >= loadmodel->numsurfaces)\n\t\t\t{\n\t\t\t\tCon_Printf (CON_ERROR \"Mod_ParseMarksurfaces: bad surface number\\n\");\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tout[i] = loadmodel->surfaces + j;\n\t\t}\n\t}\n\telse\n\t{\n\t\tshort\t\t*ins;\n\t\tins = (void *)(mod_base + l->fileofs);\n\t\tcount = l->filelen / sizeof(*ins);\n\t\tif (l->filelen % sizeof(*ins) || count > SANITY_LIMIT(*out))\n\t\t{\n\t\t\tCon_Printf (CON_ERROR \"MOD_LoadBmodel: funny lump size in %s\\n\",loadmodel->name);\n\t\t\treturn false;\n\t\t}\n\t\tout = ZG_Malloc(&loadmodel->memgroup, count*sizeof(*out));\n\n\t\tloadmodel->marksurfaces = out;\n\t\tloadmodel->nummarksurfaces = count;\n\n\t\tfor ( i=0 ; i<count ; i++)\n\t\t{\n\t\t\tj = (unsigned short)LittleShort(ins[i]);\n\t\t\tif (j >= loadmodel->numsurfaces)\n\t\t\t{\n\t\t\t\tCon_Printf (CON_ERROR \"Mod_ParseMarksurfaces: bad surface number\\n\");\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tout[i] = loadmodel->surfaces + j;\n\t\t}\n\t}\n\n\treturn true;\n}\n\n/*\n=================\nMod_LoadSurfedges\n=================\n*/\nqboolean Mod_LoadSurfedges (model_t *loadmodel, qbyte *mod_base, lump_t *l)\n{\t\n\tint\t\ti, count;\n\tint\t\t*in, *out;\n\t\n\tin = (void *)(mod_base + l->fileofs);\n\tcount = l->filelen / sizeof(*in);\n\tif (l->filelen % sizeof(*in) || count > SANITY_LIMIT(*out))\n\t{\n\t\tCon_Printf (CON_ERROR \"MOD_LoadBmodel: funny lump size in %s\\n\",loadmodel->name);\n\t\treturn false;\n\t}\n\tout = ZG_Malloc(&loadmodel->memgroup, count*sizeof(*out));\n\n\tloadmodel->surfedges = out;\n\tloadmodel->numsurfedges = count;\n\n\tfor ( i=0 ; i<count ; i++)\n\t\tout[i] = LittleLong (in[i]);\n\n\treturn true;\n}\n#endif\n#ifdef Q1BSPS\n/*\n=================\nMod_LoadVisibility\n=================\n*/\nstatic void Mod_LoadVisibility (model_t *loadmodel, qbyte *mod_base, lump_t *l, qbyte *ptr, size_t len)\n{\n\tif (!ptr)\n\t{\n\t\tptr = mod_base + l->fileofs;\n\t\tlen = l->filelen;\n\t}\n\tif (!len)\n\t{\n\t\tloadmodel->visdata = NULL;\n\t\treturn;\n\t}\n\tloadmodel->visdata = ZG_Malloc(&loadmodel->memgroup, len);\t\n\tmemcpy (loadmodel->visdata, ptr, len);\n}\n\n#ifndef SERVERONLY\nstatic void Mod_LoadMiptex(model_t *loadmodel, texture_t *tx, miptex_t *mt, int mtsize, qbyte *ptr, size_t miptexsize)\n{\n\tunsigned int legacysize =\n\t\t(mt->width>>0)*(mt->height>>0) +\n\t\t(mt->width>>1)*(mt->height>>1) +\n\t\t(mt->width>>2)*(mt->height>>2) +\n\t\t(mt->width>>3)*(mt->height>>3);\n\n\tuploadfmt_t newfmt = PTI_INVALID;\n\tsize_t neww=0, newh=0;\n\tqbyte *newdata=NULL;\n\tqbyte *pal = NULL;\n\tint m;\n\n\t//bug: vanilla quake ignored offsets and just made assumptions.\n\t//this means we can't just play with offsets to hide stuff, we have to postfix it (which requires guessing lump sizes)\n\t//issue: halflife textures have (leshort)256,(byte)pal[256*3] stuck on the end\n\t//we signal the presence of our extended data using 0x00,0xfb,0x2b,0xaf (this should be uncommon as the next mip's name shouldn't normally be empty, nor a weird char (which should hopefully also not come from random stack junk in the wad tool)\n\t//each extended block of data then has a size value, followed by a block name.\n\t//compressed formats then contain a width+height value, and then a FULL (round-down) mip chain.\n\t//if the gpu doesn't support npot, or its too big, or can't use the pixelformat then the engine will simply have to fall back on the paletted data. lets hope it was present.\n\tsize_t extofs;\n\tif (!mt->offsets[0])\n\t\textofs = mtsize;\n\telse if (mt->offsets[0] == mtsize &&\n\t\t\t mt->offsets[1] == mt->offsets[0]+(mt->width>>0)*(mt->height>>0) &&\n\t\t\t mt->offsets[2] == mt->offsets[1]+(mt->width>>1)*(mt->height>>1) &&\n\t\t\t mt->offsets[3] == mt->offsets[2]+(mt->width>>2)*(mt->height>>2))\n\t{\n\t\textofs = mt->offsets[3]+(mt->width>>3)*(mt->height>>3);\n\t\tif (extofs + 2+256*3 <= miptexsize && *(short*)(ptr + extofs) == 256)\n\t\t{\t//space for a halflife paletted texture, with the right signature (note: usually padded).\n\t\t\tpal = ptr + extofs+2;\n\t\t\textofs += 2+256*3;\n\t\t}\n\t}\n\telse\n\t\textofs = miptexsize;\t//the numbers don't match what we expect... something weird is going on here... don't misinterpret it.\n\tif (extofs+4 <= miptexsize && ptr[extofs+0] == 0 && ptr[extofs+1]==0xfb && ptr[extofs+2]==0x2b && ptr[extofs+3]==0xaf)\n\t{\n\t\tunsigned int extsize;\n\t\textofs += 4;\n\t\tfor (; extofs < miptexsize; extofs += extsize)\n\t\t{\n\t\t\tsize_t sz, w, h;\n\t\t\tunsigned int bb,bw,bh,bd;\n\t\t\tint mip;\n\t\t\tqbyte *extdata = (void*)(ptr+extofs);\n\t\t\tchar *extfmt = (char*)(extdata+4);\n\t\t\textsize = (extdata[0]<<0)|(extdata[1]<<8)|(extdata[2]<<16)|(extdata[3]<<24);\n\t\t\tif (extsize<8 || extofs+extsize>miptexsize) break;\t//not a valid entry... something weird is happening here\n\t\t\telse if (!strncmp(extfmt, \"NAME\", 4))\n\t\t\t{\t//replacement name, for longer shader/external names\n\t\t\t\tsize_t sz = extsize-8;\n\t\t\t\tif (sz >= sizeof(tx->name))\n\t\t\t\t\tcontinue;\n\t\t\t\tmemcpy(tx->name, (qbyte*)extdata+8, sz);\n\t\t\t\ttx->name[sz] = 0;\n\t\t\t}\n\t\t\telse if (!strncmp(extfmt, \"LPAL\", 4) && extsize == 8+256*3)\n\t\t\t{\t//replacement palette for the 8bit data, for feature parity with halflife, but with extra markup so we know its actually meant to be a replacement palette.\n\t\t\t\tpal = extdata+8;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\telse if (extsize <= 16) continue;\t\t\t\t\t\t\t\t\t\t//too small for an altformat lump\n\t\t\telse if (newfmt != PTI_INVALID)\tcontinue;\t\t\t\t\t\t\t\t//only accept the first accepted format (allowing for eg astc+bc1 fallbacks)\n\t\t\telse if (!strncmp(extfmt, \"RGBA\", 4))\tnewfmt = PTI_RGBA8;\t\t\t\t//32bpp, we don't normally need this alpha precision (padding can be handy though, for the lazy).\n\t\t\telse if (!strncmp(extfmt, \"RGBX\", 4))\tnewfmt = PTI_RGBX8;\t\t\t\t//32bpp, we don't normally need this alpha precision (padding can be handy though, for the lazy).\n\t\t\telse if (!strncmp(extfmt, \"RGB\", 4))\tnewfmt = PTI_RGB8;\t\t\t\t//24bpp\n\t\t\telse if (!strncmp(extfmt, \"565\", 4))\tnewfmt = PTI_RGB565;\t\t\t//16bpp\n\t\t\telse if (!strncmp(extfmt, \"4444\", 4))\tnewfmt = PTI_RGBA4444;\t\t\t//16bpp\n\t\t\telse if (!strncmp(extfmt, \"5551\", 4))\tnewfmt = PTI_RGBA5551;\t\t\t//16bpp\n\t\t\telse if (!strncmp(extfmt, \"LUM8\", 4))\tnewfmt = PTI_L8;\t\t\t//8bpp\n\t\t\telse if (!strncmp(extfmt, \"EXP5\", 4))\tnewfmt = PTI_E5BGR9;\t\t\t//32bpp, we don't normally need this alpha precision...\n\t\t\telse if (!strncmp(extfmt, \"BC1\", 4))\tnewfmt = PTI_BC1_RGBA;\t\t\t//4bpp\n\t\t\telse if (!strncmp(extfmt, \"BC2\", 4))\tnewfmt = PTI_BC2_RGBA;\t\t\t//8bpp, we don't normally need this alpha precision...\n\t\t\telse if (!strncmp(extfmt, \"BC3\", 4))\tnewfmt = PTI_BC3_RGBA;\t\t\t//8bpp, we don't normally need this alpha precision...\n\t\t\telse if (!strncmp(extfmt, \"BC4\", 4))\tnewfmt = PTI_BC4_R;\t\t\t\t//4bpp, wtf\n\t\t\telse if (!strncmp(extfmt, \"BC5\", 4))\tnewfmt = PTI_BC5_RG;\t\t\t//8bpp, wtf\n\t\t\telse if (!strncmp(extfmt, \"BC6\", 4))\tnewfmt = PTI_BC6_RGB_UFLOAT;\t//8bpp, weird\n\t\t\telse if (!strncmp(extfmt, \"BC7\", 4))\tnewfmt = PTI_BC7_RGBA;\t\t\t//8bpp\n\t\t\telse if (!strncmp(extfmt, \"AST4\", 4))\tnewfmt = PTI_ASTC_4X4_LDR;\t\t//8 bpp\n\t\t\telse if (!strncmp(extfmt, \"AS54\", 4))\tnewfmt = PTI_ASTC_5X4_LDR;\t\t//6.40bpp\n\t\t\telse if (!strncmp(extfmt, \"AST5\", 4))\tnewfmt = PTI_ASTC_5X5_LDR;\t\t//5.12bpp\n\t\t\telse if (!strncmp(extfmt, \"AS65\", 4))\tnewfmt = PTI_ASTC_6X5_LDR;\t\t//4.17bpp\n\t\t\telse if (!strncmp(extfmt, \"AST6\", 4))\tnewfmt = PTI_ASTC_6X6_LDR;\t\t//3.56bpp\n\t\t\telse if (!strncmp(extfmt, \"AS85\", 4))\tnewfmt = PTI_ASTC_8X5_LDR;\t\t//3.20bpp\n\t\t\telse if (!strncmp(extfmt, \"AS86\", 4))\tnewfmt = PTI_ASTC_8X6_LDR;\t\t//2.67bpp\n\t\t\telse if (!strncmp(extfmt, \"AS05\", 4))\tnewfmt = PTI_ASTC_10X5_LDR;\t\t//2.56bpp\n\t\t\telse if (!strncmp(extfmt, \"AS06\", 4))\tnewfmt = PTI_ASTC_10X6_LDR;\t\t//2.13bpp\n\t\t\telse if (!strncmp(extfmt, \"AST8\", 4))\tnewfmt = PTI_ASTC_8X8_LDR;\t\t//2 bpp\n\t\t\telse if (!strncmp(extfmt, \"AS08\", 4))\tnewfmt = PTI_ASTC_10X8_LDR;\t\t//1.60bpp\n\t\t\telse if (!strncmp(extfmt, \"AS00\", 4))\tnewfmt = PTI_ASTC_10X10_LDR;\t//1.28bpp\n\t\t\telse if (!strncmp(extfmt, \"AS20\", 4))\tnewfmt = PTI_ASTC_12X10_LDR;\t//1.07bpp\n\t\t\telse if (!strncmp(extfmt, \"AST2\", 4))\tnewfmt = PTI_ASTC_12X12_LDR;\t//0.89bpp\n\t\t\telse if (!strncmp(extfmt, \"ETC1\", 4))\tnewfmt = PTI_ETC1_RGB8;\t\t\t//4bpp\n\t\t\telse if (!strncmp(extfmt, \"ETC2\", 4))\tnewfmt = PTI_ETC2_RGB8;\t\t\t//4bpp\n\t\t\telse if (!strncmp(extfmt, \"ETCP\", 4))\tnewfmt = PTI_ETC2_RGB8A1;\t\t//4bpp\n\t\t\telse if (!strncmp(extfmt, \"ETCA\", 4))\tnewfmt = PTI_ETC2_RGB8A8;\t\t//8bpp, we don't normally need this alpha precision...\n\t\t\telse continue;\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t//dunno what that is, ignore it\n\n\t\t\t//alternative textures are usually compressed\n\t\t\t//this means we insist on a FULL mip chain\n\t\t\t//npot mips are explicitly round-down (but don't drop to 0 with non-square).\n\t\t\tImage_BlockSizeForEncoding(newfmt, &bb, &bw, &bh, &bd);\n\t\t\tneww = (extdata[8]<<0)|(extdata[9]<<8)|(extdata[10]<<16)|(extdata[11]<<24);\n\t\t\tnewh = (extdata[12]<<0)|(extdata[13]<<8)|(extdata[14]<<16)|(extdata[15]<<24);\n\t\t\tfor (mip = 0, w=neww, h=newh, sz=0; w || h; mip++, w>>=1,h>>=1)\n\t\t\t{\n\t\t\t\tw = max(1, w);\n\t\t\t\th = max(1, h);\n\t\t\t\tsz +=\tbb *\n\t\t\t\t\t\t((w+bw-1)/bw) *\n\t\t\t\t\t\t((h+bh-1)/bh);\n\t\t\t\t//Support truncation to top-mip only? tempting...\n\t\t\t}\n\t\t\tif (extsize != 16+sz)\n\t\t\t{\n\t\t\t\tCon_Printf(CON_WARNING\"miptex %s (%s) has incomplete mipchain\\n\", tx->name, Image_FormatName(newfmt));\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t//make sure we're not going to need to rescale compressed formats.\n\t\t\t//gles<3 or gl<2 requires npot inputs for this to work. I guess that means dx9.3+ gpus, so all astc+bc7 but not necessarily all bc1+etc2. oh well.\n\t\t\tif (!sh_config.texture_non_power_of_two)\n\t\t\t{\n\t\t\t\tif (neww & (neww - 1))\n\t\t\t\t\tcontinue;\n\t\t\t\tif (newh & (newh - 1))\n\t\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t//make sure its within our limits\n\t\t\tif (!neww || !newh || neww > sh_config.texture2d_maxsize || newh > sh_config.texture2d_maxsize)\n\t\t\t\tcontinue;\n\t\t\t//that our hardware supports it... (Note: FTE can soft-decompress all of the above so this doesn't make too much sense if there's only one)\n\t\t\t//if (!sh_config.texfmt[newfmt])\n\t\t\t//\tcontinue;\n\t\t\t//that we can actually use non-paletted data...\n\t\t\tif (r_softwarebanding && mt->offsets[0])\n\t\t\t\tcontinue;\n\n\t\t\tnewdata = BZ_Malloc(sz);\n\t\t\tmemcpy(newdata, extdata+16, sz);\n\t\t}\n\t}\n\n\tif (newdata)\n\t{\n\t\ttx->srcfmt = newfmt|PTI_FULLMIPCHAIN;\n\t\ttx->srcwidth = neww;\n\t\ttx->srcheight = newh;\n\t\ttx->srcdata = newdata;\n\t\ttx->palette = NULL;\n\t\treturn;\n\t}\n\n\tif (!mt->offsets[0])\n\t{\n\t\ttx->srcfmt = PTI_INVALID;\n\t\ttx->srcwidth = mt->width;\n\t\ttx->srcheight = mt->height;\n\t\ttx->srcdata = NULL;\n\t\ttx->palette = NULL;\n\t\treturn;\n\t}\n\n\tif (pal)\n\t{\t//mostly identical, just a specific palette hidden at the end. handle fences elsewhere.\n\t\ttx->srcdata = BZ_Malloc(legacysize + 768);\n\t\ttx->palette = tx->srcdata + legacysize;\n\t\tmemcpy(tx->palette, pal, 768);\n\t}\n\telse\n\t{\n\t\ttx->srcdata = BZ_Malloc(legacysize);\n\t\ttx->palette = NULL;\n\t}\n\n\tif (tx->palette)\n\t{\t//halflife, probably...\n\t\tif (*tx->name == '{')\n\t\t\ttx->srcfmt = TF_MIP4_8PAL24_T255;\n\t\telse\n\t\t\ttx->srcfmt = TF_MIP4_8PAL24;\n\t}\n\telse\n\t{\n\t\tif (*tx->name == '{')\n\t\t\ttx->srcfmt = TF_TRANS8;\n\t\telse\n\t\t\ttx->srcfmt = TF_MIP4_SOLID8;\n\t}\n\ttx->srcwidth = mt->width;\n\ttx->srcheight = mt->height;\n\n\tlegacysize = 0;\n\tfor (m = 0; m < 4; m++)\n\t{\n\t\tif (mt->offsets[m] && (mt->offsets[m]+(mt->width>>m)*(mt->height>>m)<=miptexsize))\n\t\t\tmemcpy(tx->srcdata+legacysize, ptr + mt->offsets[m], (mt->width>>m)*(mt->height>>m));\n\t\telse\n\t\t\tmemset(tx->srcdata+legacysize, 0, (mt->width>>m)*(mt->height>>m));\n\t\tlegacysize += (mt->width>>m)*(mt->height>>m);\n\t}\n}\n#endif\n\n/*\n=================\nMod_LoadTextures\n=================\n*/\nstatic qboolean Mod_LoadTextures (model_t *loadmodel, qbyte *mod_base, lump_t *l, subbsp_t subbsp)\n{\n\tint\t\ti, j, num, max, altmax;\n\ttexture_t\t*tx, *tx2;\n\ttexture_t\t*anims[10];\n\ttexture_t\t*altanims[10];\n\tdmiptexlump_t *m;\n\tunsigned int *sizes;\n\tunsigned int e, o;\n\tint mtsize;\n\nTRACE((\"dbg: Mod_LoadTextures: inittexturedescs\\n\"));\n\n//\tMod_InitTextureDescs(loadname);\n\n\tif (!l->filelen)\n\t{\n\t\tCon_Printf(CON_WARNING \"warning: %s contains no texture data\\n\", loadmodel->name);\n\n\t\tloadmodel->numtextures = 1;\n\t\tloadmodel->textures = ZG_Malloc(&loadmodel->memgroup, 1 * sizeof(*loadmodel->textures));\n\n\t\ti = 0;\n\t\ttx = ZG_Malloc(&loadmodel->memgroup, sizeof(texture_t));\n\t\tmemcpy(tx, r_notexture_mip, sizeof(texture_t));\n\t\tsprintf(tx->name, \"unnamed%i\", i);\n\t\tloadmodel->textures[i] = tx;\n\n\t\treturn true;\n\t}\n\tm = (dmiptexlump_t *)(mod_base + l->fileofs);\n\n\tm->nummiptex = LittleLong (m->nummiptex);\n\n\tif ((1+m->nummiptex)*sizeof(int) > l->filelen)\n\t{\n\t\tCon_Printf(CON_WARNING \"warning: %s contains corrupt texture lump\\n\", loadmodel->name);\n\t\treturn false;\n\t}\n\n\tloadmodel->numtextures = m->nummiptex;\n\tloadmodel->textures = ZG_Malloc(&loadmodel->memgroup, m->nummiptex * sizeof(*loadmodel->textures));\n\tsizes = alloca(sizeof(*sizes)*m->nummiptex);\n\n\tmtsize = subbsp == sb_quake64 ? sizeof(q64miptex_t) : sizeof(miptex_t);\n\n\tfor (i=m->nummiptex, e = l->filelen; i-->0; )\n\t{\n\t\tqbyte* ptr;\n\t\tmiptex_t* mt;\n\t\tmiptex_t tmp;\n\t\tint scale;\n\n\t\to = LittleLong(m->dataofs[i]);\n\t\tif (o >= l->filelen)\t//e1m2, this happens\n\t\t{\nbadmip:\n\t\t\ttx = ZG_Malloc(&loadmodel->memgroup, sizeof(texture_t));\n\t\t\tmemcpy(tx, r_notexture_mip, sizeof(texture_t));\n\t\t\tsprintf(tx->name, \"unnamed%i\", i);\n\t\t\tloadmodel->textures[i] = tx;\n\t\t\tcontinue;\n\t\t}\n\t\tif (o >= e)\n\t\t\te = l->filelen; //something doesn't make sense. try to avoid making too many assumptions.\n\n\t\tptr = (qbyte*)m + o;\n\n\t\tif (subbsp == sb_quake64)\n\t\t{\n\t\t\tq64miptex_t *q64mt = (q64miptex_t*)ptr;\n\t\t\tmemcpy(tmp.name, q64mt->name, sizeof(tmp.name));\n\t\t\tmt = &tmp;\n\t\t\tmt->width = LittleLong(q64mt->width);\n\t\t\tmt->height = LittleLong(q64mt->height);\n\t\t\tfor (j = 0; j < MIPLEVELS; j++)\n\t\t\t\tmt->offsets[j] = LittleLong(q64mt->offsets[j]);\n\t\t\tscale = LittleLong (q64mt->scale);\n\t\t} \n\t\telse \n\t\t{\n\t\t\tmt = (miptex_t*)ptr;\n\t\t\tmt->width = LittleLong(mt->width);\n\t\t\tmt->height = LittleLong(mt->height);\n\t\t\tfor (j = 0; j < MIPLEVELS; j++)\n\t\t\t\tmt->offsets[j] = LittleLong(mt->offsets[j]);\n\t\t}\n\n\t\tTRACE((\"dbg: Mod_LoadTextures: texture %s\\n\", mt->name));\n\n\t\tif (mt->offsets[0] && (mt->width > 0xffff|| mt->height > 0xffff))\n\t\t{\n\t\t\tCon_Printf(CON_WARNING \"%s: miptex %i is excessively large. probably corrupt\\n\", loadmodel->name, i);\n\t\t\tgoto badmip;\n\t\t}\n\n\t\tif (!*mt->name)\t//I HATE MAPPERS!\n\t\t{\n\t\t\tQ_snprintfz(mt->name, sizeof(mt->name), \"unnamed%i\", i);\n\t\t\tCon_DPrintf(CON_WARNING \"warning: unnamed texture in %s, renaming to %s\\n\", loadmodel->name, mt->name);\n\t\t}\n\n\t\tif ( (mt->width & 15) || (mt->height & 15) )\n\t\t\tCon_DPrintf (CON_WARNING \"Warning: Texture %s is not 16 aligned\", mt->name);\n\t\tif (mt->width < 1 || mt->height < 1)\n\t\t\tCon_Printf (CON_WARNING \"Warning: Texture %s has no size\", mt->name);\n\t\ttx = ZG_Malloc(&loadmodel->memgroup, sizeof(texture_t));\n\t\tloadmodel->textures[i] = tx;\n\n\t\tQ_strncpyz(tx->name, mt->name, min(sizeof(mt->name)+1, sizeof(tx->name)));\n\t\ttx->vwidth = mt->width;\n\t\ttx->vheight = mt->height;\n\t\tif (subbsp == sb_quake64)\n\t\t{\n\t\t\ttx->vwidth <<= scale;\n\t\t\ttx->vheight <<= scale;\n\t\t}\n\n#ifndef SERVERONLY\n\t\tMod_LoadMiptex(loadmodel, tx, mt, mtsize, ptr, e-o);\n#else\n\t\t(void)e;\n\t\t(void)mtsize;\n#endif\n\n\t\te = o;\n\t}\n//\n// sequence the animations\n//\n\tfor (i=0 ; i<m->nummiptex ; i++)\n\t{\n\t\tqboolean animvalid = true;\n\n\t\ttx = loadmodel->textures[i];\n\t\tif (!tx || tx->name[0] != '+')\n\t\t\tcontinue;\n\t\tif (tx->anim_next)\n\t\t\tcontinue;\t// already sequenced\n\n\t// find the number of frames in the animation\n\t\tmemset (anims, 0, sizeof(anims));\n\t\tmemset (altanims, 0, sizeof(altanims));\n\n\t\tmax = tx->name[1];\n\t\taltmax = 0;\n\t\tif (max >= 'a' && max <= 'z')\n\t\t\tmax -= 'a' - 'A';\n\t\tif (max >= '0' && max <= '9')\n\t\t{\n\t\t\tmax -= '0';\n\t\t\taltmax = 0;\n\t\t\tanims[max] = tx;\n\t\t\tmax++;\n\t\t}\n\t\telse if (max >= 'A' && max <= 'J')\n\t\t{\n\t\t\taltmax = max - 'A';\n\t\t\tmax = 0;\n\t\t\taltanims[altmax] = tx;\n\t\t\taltmax++;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tCon_Printf (CON_WARNING \"Bad animating texture name %s\\n\", tx->name);\n\t\t\tcontinue;\n\t\t}\n\n\t\tfor (j=i+1 ; j<m->nummiptex ; j++)\n\t\t{\n\t\t\ttx2 = loadmodel->textures[j];\n\t\t\tif (!tx2 || tx2->name[0] != '+')\n\t\t\t\tcontinue;\n\t\t\tif (strcmp (tx2->name+2, tx->name+2))\n\t\t\t\tcontinue;\n\n\t\t\tnum = tx2->name[1];\n\t\t\tif (num >= 'a' && num <= 'z')\n\t\t\t\tnum -= 'a' - 'A';\n\t\t\tif (num >= '0' && num <= '9')\n\t\t\t{\n\t\t\t\tnum -= '0';\n\t\t\t\tanims[num] = tx2;\n\t\t\t\tif (num+1 > max)\n\t\t\t\t\tmax = num + 1;\n\t\t\t}\n\t\t\telse if (num >= 'A' && num <= 'J')\n\t\t\t{\n\t\t\t\tnum = num - 'A';\n\t\t\t\taltanims[num] = tx2;\n\t\t\t\tif (num+1 > altmax)\n\t\t\t\t\taltmax = num+1;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\n#define\tANIM_CYCLE\t2\n\t\t// validate\n\t\tfor (j = 0; j < max; j++)\n\t\t{\n\t\t\tif (!anims[j])\n\t\t\t{\n\t\t\t\tCon_Printf(CON_WARNING \"Missing frame %i of %s\\n\", j, tx->name);\n\t\t\t\tanimvalid = false;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tfor (j = 0; j < altmax && animvalid; j++)\n\t\t{\n\t\t\tif (!altanims[j])\n\t\t\t{\n\t\t\t\tCon_Printf(CON_WARNING \"Missing alt frame %i of %s\\n\", j, tx->name);\n\t\t\t\tanimvalid = false;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\t// link them all together\n\t\tif (animvalid)\n\t\t{\n\t\t\tfor (j = 0; j < max; j++)\n\t\t\t{\n\t\t\t\ttx2 = anims[j];\n\t\t\t\ttx2->anim_total = max * ANIM_CYCLE;\n\t\t\t\ttx2->anim_min = j * ANIM_CYCLE;\n\t\t\t\ttx2->anim_max = (j + 1) * ANIM_CYCLE;\n\t\t\t\ttx2->anim_next = anims[(j + 1) % max];\n\t\t\t\tif (altmax)\n\t\t\t\t\ttx2->alternate_anims = altanims[0];\n\t\t\t}\n\t\t\tfor (j = 0; j < altmax; j++)\n\t\t\t{\n\t\t\t\ttx2 = altanims[j];\n\t\t\t\ttx2->anim_total = altmax * ANIM_CYCLE;\n\t\t\t\ttx2->anim_min = j * ANIM_CYCLE;\n\t\t\t\ttx2->anim_max = (j + 1) * ANIM_CYCLE;\n\t\t\t\ttx2->anim_next = altanims[(j + 1) % altmax];\n\t\t\t\tif (max)\n\t\t\t\t\ttx2->alternate_anims = anims[0];\n\t\t\t}\n\t\t}\n\t}\n\n\treturn true;\n}\n\n/*\n=================\nMod_LoadSubmodels\n=================\n*/\nstatic qboolean Mod_LoadSubmodels (model_t *loadmodel, qbyte *mod_base, lump_t *l, qboolean *hexen2map)\n{\n\tdq1model_t\t*inq;\n\tdh2model_t\t*inh;\n\tmmodel_t\t*out;\n\tint\t\t\ti, j, count;\n\n\t//this is crazy!\n\n\tinq = (void *)(mod_base + l->fileofs);\n\tinh = (void *)(mod_base + l->fileofs);\n\tif (!inq->numfaces)\n\t{\n\t\t*hexen2map = true;\n\t\tcount = l->filelen / sizeof(*inh);\n\t\tif (l->filelen % sizeof(*inh) || count > SANITY_LIMIT(*out))\n\t\t{\n\t\t\tCon_Printf (CON_ERROR \"MOD_LoadBmodel: funny lump size in %s\\n\",loadmodel->name);\n\t\t\treturn false;\n\t\t}\n\t\tout = ZG_Malloc(&loadmodel->memgroup, count*sizeof(*out));\n\n\t\tloadmodel->submodels = out;\n\t\tloadmodel->numsubmodels = count;\n\n\t\tfor ( i=0 ; i<count ; i++, inh++, out++)\n\t\t{\n\t\t\tfor (j=0 ; j<3 ; j++)\n\t\t\t{\t// spread the mins / maxs by a pixel\n\t\t\t\tout->mins[j] = LittleFloat (inh->mins[j]) - 1;\n\t\t\t\tout->maxs[j] = LittleFloat (inh->maxs[j]) + 1;\n\t\t\t\tout->origin[j] = LittleFloat (inh->origin[j]);\n\t\t\t}\n\t\t\tfor (j=0 ; j<MAX_MAP_HULLSDH2 ; j++)\n\t\t\t{\n\t\t\t\tout->headnode[j] = LittleLong (inh->headnode[j]);\n\t\t\t}\n\t\t\tfor ( ; j<MAX_MAP_HULLSM ; j++)\n\t\t\t\tout->headnode[j] = 0;\n\t\t\tfor (j=0 ; j<MAX_MAP_HULLSDH2 ; j++)\n\t\t\t\tout->hullavailable[j] = true;\n\t\t\tfor ( ; j<MAX_MAP_HULLSM ; j++)\n\t\t\t\tout->hullavailable[j] = false;\n\t\t\tout->visleafs = LittleLong (inh->visleafs);\n\t\t\tout->firstface = LittleLong (inh->firstface);\n\t\t\tout->numfaces = LittleLong (inh->numfaces);\n\t\t}\n\n\t}\n\telse\n\t{\n\t\t*hexen2map = false;\n\t\tcount = l->filelen / sizeof(*inq);\n\t\tif (l->filelen % sizeof(*inq) || count > SANITY_LIMIT(*out))\n\t\t{\n\t\t\tCon_Printf (CON_ERROR \"MOD_LoadBmodel: funny lump size in %s\\n\",loadmodel->name);\n\t\t\treturn false;\n\t\t}\n\t\tout = ZG_Malloc(&loadmodel->memgroup, count*sizeof(*out));\t\n\n\t\tloadmodel->submodels = out;\n\t\tloadmodel->numsubmodels = count;\n\n\t\tfor ( i=0 ; i<count ; i++, inq++, out++)\n\t\t{\n\t\t\tfor (j=0 ; j<3 ; j++)\n\t\t\t{\t// spread the mins / maxs by a pixel\n\t\t\t\tout->mins[j] = LittleFloat (inq->mins[j]) - 1;\n\t\t\t\tout->maxs[j] = LittleFloat (inq->maxs[j]) + 1;\n\t\t\t\tout->origin[j] = LittleFloat (inq->origin[j]);\n\t\t\t}\n\t\t\tfor (j=0 ; j<MAX_MAP_HULLSDQ1 ; j++)\n\t\t\t{\n\t\t\t\tout->headnode[j] = LittleLong (inq->headnode[j]);\n\t\t\t}\n\t\t\tfor ( ; j<MAX_MAP_HULLSM ; j++)\n\t\t\t\tout->headnode[j] = 0;\n\t\t\tfor (j=0 ; j<4 ; j++)\n\t\t\t\tout->hullavailable[j] = true;\n\t\t\tfor ( ; j<MAX_MAP_HULLSM ; j++)\n\t\t\t\tout->hullavailable[j] = false;\n\t\t\tout->visleafs = LittleLong (inq->visleafs);\n\t\t\tout->firstface = LittleLong (inq->firstface);\n\t\t\tout->numfaces = LittleLong (inq->numfaces);\n\t\t}\n\t}\n\n\treturn true;\n}\n\n/*\n=================\nMod_LoadTexinfo\n=================\n*/\nstatic qboolean Mod_LoadTexinfo (model_t *loadmodel, qbyte *mod_base, lump_t *l)\n{\n\ttexinfo_t *in;\n\tmtexinfo_t *out;\n\tint \ti, j, count;\n\tint\t\tmiptex;\n\n\tin = (void *)(mod_base + l->fileofs);\n\tcount = l->filelen / sizeof(*in);\n\tif (l->filelen % sizeof(*in) || count > SANITY_LIMIT(*out))\n\t{\n\t\tCon_Printf (CON_ERROR \"MOD_LoadBmodel: funny lump size in %s\\n\",loadmodel->name);\n\t\treturn false;\n\t}\n\tout = ZG_Malloc(&loadmodel->memgroup, count*sizeof(*out));\n\n\tloadmodel->texinfo = out;\n\tloadmodel->numtexinfo = count;\n\n\tfor ( i=0 ; i<count ; i++, in++, out++)\n\t{\n\t\tfor (j=0 ; j<4 ; j++)\n\t\t{\n\t\t\tout->vecs[0][j] = LittleFloat (in->vecs[0][j]);\n\t\t\tout->vecs[1][j] = LittleFloat (in->vecs[1][j]);\n\t\t}\n\t\tif (mod_lightscale_broken.ival)\n\t\t{\n\t\t\tout->vecscale[0] = 1.0;\n\t\t\tout->vecscale[1] = 1.0;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tout->vecscale[0] = 1.0/Length (out->vecs[0]);\n\t\t\tout->vecscale[1] = 1.0/Length (out->vecs[1]);\n\t\t}\n\n\t\tmiptex = LittleLong (in->miptex);\n\t\tout->flags = LittleLong (in->flags);\n\t\n\t\tif (loadmodel->numtextures)\n\t\t\tout->texture = loadmodel->textures[miptex % loadmodel->numtextures];\n\t\telse\n\t\t\tout->texture = NULL;\n\t\tif (!out->texture)\n\t\t{\n\t\t\tout->texture = r_notexture_mip; // texture not found\n\t\t\tout->flags = 0;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (*out->texture->name == '*' || (*out->texture->name == '!' && loadmodel->fromgame == fg_halflife))\t\t// turbulent\n\t\t\t{\n\t\t\t\tif (!(out->flags & TEX_SPECIAL) && !strchr(out->texture->name, '#'))\n\t\t\t\t\tQ_strncatz(out->texture->name, \"#LIT\", sizeof(out->texture->name));\n\t\t\t}\n\n\t\t\tif (!strncmp(out->texture->name, \"scroll\", 6) || ((*out->texture->name == '*' || *out->texture->name == '{' || *out->texture->name == '!') && !strncmp(out->texture->name+1, \"scroll\", 6)))\n\t\t\t\tout->flags |= TI_FLOWING;\n\n\t\t}\n\t}\n\n\treturn true;\n}\n\n/*\n================\nCalcSurfaceExtents\n\nFills in s->texturemins[] and s->extents[]\n================\n*/\n\nvoid CalcSurfaceExtents (model_t *mod, msurface_t *s);\n/*\n{\n\tfloat\tmins[2], maxs[2], val;\n\tint\t\ti,j, e;\n\tmvertex_t\t*v;\n\tmtexinfo_t\t*tex;\n\tint\t\tbmins[2], bmaxs[2];\n\n\tmins[0] = mins[1] = 999999;\n\tmaxs[0] = maxs[1] = -99999;\n\n\ttex = s->texinfo;\n\t\n\tfor (i=0 ; i<s->numedges ; i++)\n\t{\n\t\te = loadmodel->surfedges[s->firstedge+i];\n\t\tif (e >= 0)\n\t\t\tv = &loadmodel->vertexes[loadmodel->edges[e].v[0]];\n\t\telse\n\t\t\tv = &loadmodel->vertexes[loadmodel->edges[-e].v[1]];\n\t\t\n\t\tfor (j=0 ; j<2 ; j++)\n\t\t{\n\t\t\tval = v->position[0] * tex->vecs[j][0] + \n\t\t\t\tv->position[1] * tex->vecs[j][1] +\n\t\t\t\tv->position[2] * tex->vecs[j][2] +\n\t\t\t\ttex->vecs[j][3];\n\t\t\tif (val < mins[j])\n\t\t\t\tmins[j] = val;\n\t\t\tif (val > maxs[j])\n\t\t\t\tmaxs[j] = val;\n\t\t}\n\t}\n\n\tfor (i=0 ; i<2 ; i++)\n\t{\t\n\t\tbmins[i] = floor(mins[i]/16);\n\t\tbmaxs[i] = ceil(maxs[i]/16);\n\n\t\ts->texturemins[i] = bmins[i];\n\t\ts->extents[i] = (bmaxs[i] - bmins[i]);\n\n//\t\tif ( !(tex->flags & TEX_SPECIAL) && s->extents[i] > 512 )\t//q2 uses 512.\n//\t\t\tSys_Error (\"Bad surface extents\");\n\t}\n}\n*/\n\n/*\n=================\nMod_LoadFaces\n=================\n*/\nstatic qboolean Mod_LoadFaces (model_t *loadmodel, bspx_header_t *bspx, qbyte *mod_base, lump_t *l, lump_t *lightlump, subbsp_t subbsp)\n{\n\tdsface_t\t\t*ins;\n\tdlface_t\t\t*inl;\n\tmsurface_t \t*out;\n\tint\t\t\tcount, surfnum;\n\tint\t\t\ti, planenum, side;\n\tint tn;\n\tunsigned int lofs, lend;\n\n\tunsigned short lmshift, lmscale;\n\tchar buf[64];\n\tlightmapoverrides_t overrides;\n\n\tint lofsscale = 1;\n\tqboolean lightmapusable = false;\n\n\tstruct decoupled_lm_info_s *decoupledlm;\n\tsize_t dcsize;\n\n\tmemset(&overrides, 0, sizeof(overrides));\n\n\tlmscale = atoi(Mod_ParseWorldspawnKey(loadmodel, \"lightmap_scale\", buf, sizeof(buf)));\n\tif (!lmscale)\n\t\tlmshift = LMSHIFT_DEFAULT;\n\telse\n\t{\n\t\tfor(lmshift = 0; lmscale > 1; lmshift++)\n\t\t\tlmscale >>= 1;\n\t}\n\n\tif (subbsp == sb_long1 || subbsp == sb_long2)\n\t{\n\t\tins = NULL;\n\t\tinl = (void *)(mod_base + l->fileofs);\n\t\tcount = l->filelen / sizeof(*inl);\n\t\tif (l->filelen % sizeof(*inl) || count > SANITY_LIMIT(*out))\n\t\t{\n\t\t\tCon_Printf (CON_ERROR \"MOD_LoadBmodel: funny lump size in %s\\n\",loadmodel->name);\n\t\t\treturn false;\n\t\t}\n\t}\n\telse\n\t{\n\t\tins = (void *)(mod_base + l->fileofs);\n\t\tinl = NULL;\n\t\tcount = l->filelen / sizeof(*ins);\n\t\tif (l->filelen % sizeof(*ins) || count > SANITY_LIMIT(*out))\n\t\t{\n\t\t\tCon_Printf (CON_ERROR \"MOD_LoadBmodel: funny lump size in %s\\n\",loadmodel->name);\n\t\t\treturn false;\n\t\t}\n\t}\n\tout = ZG_Malloc(&loadmodel->memgroup, count*sizeof(*out));\n\n//\t*meshlist = ZG_Malloc(&loadmodel->memgroup, count*sizeof(**meshlist));\n\tloadmodel->surfaces = out;\n\tloadmodel->numsurfaces = count;\n\n\t//dodgy guesses time...\n\tif (loadmodel->fromgame == fg_quake //some halflife maps are misidentified as quake...\n\t\t&& loadmodel->submodels[0].headnode[3] /*these do have crouch hulls. this'll save a LOT of modulo expense*/\n\t\t&& subbsp == sb_none/*don't bother with bsp2... maybe halflife will get a remaster that uses/supports it?*/\n\t\t&& ins && count /*yeah... just in case*/\n\t\t&& !overrides.shifts /*would break expectations. fix your maps.*/\n\t\t&& lightlump->filelen%3==0\t/*hlbsp has rgb lighting so MUST be a multiple of 3*/\n\t\t)\n\t{\n\t\tfor (surfnum=0; surfnum<count; surfnum++)\n\t\t{\n\t\t\tlofs = LittleLong(ins[surfnum].lightofs);\n\t\t\tif (lofs%3)\n\t\t\t\tbreak;\t//not a byte offset within rgb data\n\t\t\tif (lofs != (unsigned int)-1 && ins[surfnum].styles[0]!=255)\n\t\t\t{\n\t\t\t\t//count styles\n\t\t\t\tfor (i = 0; i < countof(ins[surfnum].styles); i++)\n\t\t\t\t\tif (ins[surfnum].styles[i] == 255)\n\t\t\t\t\t\tbreak;\n\t\t\t\tif (!i)\n\t\t\t\t\tcontinue;\t//no lightmap data here...\n\n\t\t\t\ttn = LittleShort (ins->texinfo);\n\t\t\t\tif (tn < 0 || tn >= loadmodel->numtexinfo)\n\t\t\t\t\tbreak;\n\t\t\t\tout->texinfo = loadmodel->texinfo + tn;\n\t\t\t\tout->firstedge = LittleLong(ins->firstedge);\n\t\t\t\tout->numedges = LittleShort(ins->numedges);\n\t\t\t\tout->lmshift = lmshift;\n\t\t\t\tCalcSurfaceExtents (loadmodel, out);\n\t\t\t\ti *= (out->extents[0]>>out->lmshift)+1;\t//width\n\t\t\t\ti *= (out->extents[1]>>out->lmshift)+1;\t//height\n\t\t\t\ti *= 3; //for rgb\n\t\t\t\t//'i' is now the size of our lightmap data, in bytes. phew.\n\t\t\t\tlend = lofs + i;\n\n\t\t\t\t//we now have a reference surface.\n\t\t\t\tfor (surfnum++; surfnum<count; surfnum++)\n\t\t\t\t{\n\t\t\t\t\tunsigned int checklofs = LittleLong(ins[surfnum].lightofs);\n\t\t\t\t\tif (checklofs%3)\n\t\t\t\t\t\tbreak;\t//can't be hl\n\t\t\t\t\tif (checklofs > lofs && checklofs < lend)\n\t\t\t\t\t\tbreak;\t//started before reference surf ended... reference surface can't have been using RGB lighting. so not a mislabled hlbsp.\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (surfnum==count)\n\t\t\tloadmodel->fromgame = fg_halflife;\n\t}\n\n\tMod_LoadVertexNormals(loadmodel, bspx, mod_base, NULL);\n\tMod_LoadLighting (loadmodel, bspx, mod_base, lightlump, false, &overrides, subbsp);\n\n\tdecoupledlm = BSPX_FindLump(bspx, mod_base, \"DECOUPLED_LM\", &dcsize); //RGB packed data\n\tif (dcsize == count*sizeof(*decoupledlm))\n\t\tloadmodel->facelmvecs = ZG_Malloc(&loadmodel->memgroup, count * sizeof(*loadmodel->facelmvecs));\t//seems good.\n\telse\n\t\tdecoupledlm\t= NULL;\t//wrong size somehow... discard it.\n\n\tswitch(loadmodel->lightmaps.fmt)\n\t{\n\tcase LM_E5BGR9:\n\t\tlofsscale = 4;\n\t\tbreak;\n\tcase LM_RGB8:\n\t\tlofsscale = 3;\n\t\tbreak;\n\tdefault:\n\tcase LM_L8:\n\t\tlofsscale = 1;\n\t\tbreak;\n\t}\n\tif (loadmodel->fromgame == fg_halflife)\n\t\tlofsscale /= 3;\t//halflife has rgb offsets already (this should drop to 1, preserving any misaligned offsets...\n\n\tfor ( surfnum=0 ; surfnum<count ; surfnum++, out++)\n\t{\n\t\tif (subbsp == sb_long1 || subbsp == sb_long2)\n\t\t{\n\t\t\tplanenum = LittleLong(inl->planenum);\n\t\t\tside = LittleLong(inl->side);\n\t\t\tout->firstedge = LittleLong(inl->firstedge);\n\t\t\tout->numedges = LittleLong(inl->numedges);\n\t\t\ttn = LittleLong (inl->texinfo);\n\t\t\tfor (i=0 ; i<countof(out->styles) ; i++)\n\t\t\t\tout->styles[i] = (i >= countof(inl->styles) || (lightstyleindex_t)inl->styles[i]>=INVALID_LIGHTSTYLE|| inl->styles[i]==255)?INVALID_LIGHTSTYLE:inl->styles[i];\n\t\t\tlofs = LittleLong(inl->lightofs);\n\t\t\tinl++;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tplanenum = LittleShort(ins->planenum);\n\t\t\tside = LittleShort(ins->side);\n\t\t\tout->firstedge = LittleLong(ins->firstedge);\n\t\t\tout->numedges = LittleShort(ins->numedges);\n\t\t\ttn = LittleShort (ins->texinfo);\n\t\t\tfor (i=0 ; i<countof(out->styles) ; i++)\n\t\t\t\tout->styles[i] = (i >= countof(ins->styles) || (lightstyleindex_t)ins->styles[i]>=INVALID_LIGHTSTYLE || ins->styles[i]==255)?INVALID_LIGHTSTYLE:ins->styles[i];\n\t\t\tlofs = LittleLong(ins->lightofs);\n\t\t\tif (subbsp == sb_quake64)\n\t\t\t{\n\t\t\t\tlofs >>= 1;\n\t\t\t}\n\t\t\tins++;\n\t\t}\n//\t\t(*meshlist)[surfnum].vbofirstvert = out->firstedge;\n//\t\t(*meshlist)[surfnum].numvertexes = out->numedges;\n\t\tout->flags = 0;\n\n\t\tif (side)\n\t\t\tout->flags |= SURF_PLANEBACK;\t\t\t\n\n\t\tout->plane = loadmodel->planes + planenum;\n\n\t\tif (tn < 0 || tn >= loadmodel->numtexinfo)\n\t\t{\n\t\t\tCon_Printf(\"texinfo 0 <= %i < %i\\n\", tn, loadmodel->numtexinfo);\n\t\t\treturn false;\n\t\t}\n\t\tout->texinfo = loadmodel->texinfo + tn;\n\n\t\tif (overrides.shifts)\n\t\t\tout->lmshift = overrides.shifts[surfnum];\n\t\telse\n\t\t\tout->lmshift = lmshift;\n\t\tif (overrides.offsets)\n\t\t\tlofs = overrides.offsets[surfnum];\n\t\tif (overrides.styles16)\n\t\t{\n\t\t\tfor (i=0 ; i<countof(out->styles) ; i++)\n\t\t\t\tout->styles[i] = (i>=overrides.stylesperface)?INVALID_LIGHTSTYLE:overrides.styles16[surfnum*overrides.stylesperface+i];\n\t\t}\n\t\telse if (overrides.styles8)\n\t\t{\n\t\t\tfor (i=0 ; i<countof(out->styles) ; i++)\n\t\t\t\tout->styles[i] = (i>=overrides.stylesperface)?INVALID_LIGHTSTYLE:((overrides.styles8[surfnum*overrides.stylesperface+i]==255)?INVALID_LIGHTSTYLE:overrides.styles8[surfnum*overrides.stylesperface+i]);\n\t\t}\n\t\tfor (i=0 ; i<countof(out->styles) && out->styles[i] != INVALID_LIGHTSTYLE; i++)\n\t\t\tif (loadmodel->lightmaps.maxstyle < out->styles[i])\n\t\t\t\tloadmodel->lightmaps.maxstyle = out->styles[i];\n\n\t\tif (decoupledlm)\n\t\t{\n\t\t\tlofs = LittleLong(decoupledlm->lmoffset);\n\t\t\tout->texturemins[0] = out->texturemins[1] = 0; // should be handled by the now-per-surface vecs[][3] value.\n\t\t\tout->lmshift = 0;\t//redundant.\n\t\t\tif (!decoupledlm->lmsize[0] || !decoupledlm->lmsize[1])\n\t\t\t{\n\t\t\t\tdecoupledlm->lmsize[0] = decoupledlm->lmsize[1] = 0;\n\t\t\t\tif (lofs != (unsigned int)-1)\n\t\t\t\t{\t//we'll silently allow these buggy surfaces for now... but only if they've got no lightmap data at all. unsafe if they're the last otherwise.\n\t\t\t\t\tlofs = -1;\n\t\t\t\t\tCon_Printf(CON_WARNING\"%s: Face %i has invalid extents\\n\", loadmodel->name, surfnum);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tout->extents[0] = (unsigned short)LittleShort(decoupledlm->lmsize[0]) - 1;\t//surfaces should NEVER have an extent of 0. even if the surface is omitted it should still have some padding...\n\t\t\t\tout->extents[1] = (unsigned short)LittleShort(decoupledlm->lmsize[1]) - 1;\n\t\t\t}\n\t\t\tloadmodel->facelmvecs[surfnum].lmvecs[0][0] = LittleFloat(decoupledlm->lmvecs[0][0]);\n\t\t\tloadmodel->facelmvecs[surfnum].lmvecs[0][1] = LittleFloat(decoupledlm->lmvecs[0][1]);\n\t\t\tloadmodel->facelmvecs[surfnum].lmvecs[0][2] = LittleFloat(decoupledlm->lmvecs[0][2]);\n\t\t\tloadmodel->facelmvecs[surfnum].lmvecs[0][3] = LittleFloat(decoupledlm->lmvecs[0][3]) + 0.5f; //sigh\n\t\t\tloadmodel->facelmvecs[surfnum].lmvecs[1][0] = LittleFloat(decoupledlm->lmvecs[1][0]);\n\t\t\tloadmodel->facelmvecs[surfnum].lmvecs[1][1] = LittleFloat(decoupledlm->lmvecs[1][1]);\n\t\t\tloadmodel->facelmvecs[surfnum].lmvecs[1][2] = LittleFloat(decoupledlm->lmvecs[1][2]);\n\t\t\tloadmodel->facelmvecs[surfnum].lmvecs[1][3] = LittleFloat(decoupledlm->lmvecs[1][3]) + 0.5f; //sigh\n\t\t\tloadmodel->facelmvecs[surfnum].lmvecscale[0] = 1.0f/Length(loadmodel->facelmvecs[surfnum].lmvecs[0]);\t//luxels->qu\n\t\t\tloadmodel->facelmvecs[surfnum].lmvecscale[1] = 1.0f/Length(loadmodel->facelmvecs[surfnum].lmvecs[1]);\n\t\t\tdecoupledlm++;\n\t\t}\n\t\telse\n\t\t\tCalcSurfaceExtents (loadmodel, out);\n\t\tif (lofs != (unsigned int)-1)\n\t\t\tlofs *= lofsscale;\n\t\tlend = lofs+(out->extents[0]+1)*(out->extents[1]+1) /*FIXME: mul by numstyles */;\n\t\tif (lofs > loadmodel->lightdatasize || lend < lofs)\n\t\t\tout->samples = NULL;\t//should includes -1\n\t\telse\n\t\t{\n\t\t\tout->samples = loadmodel->lightdata + lofs;\n\t\t\tlightmapusable = true;\t//something has a valid offset.\n\t\t}\n\n\t\tif (!out->texinfo->texture)\n\t\t\tcontinue;\n\n\t\tif (out->numedges < 3)\n\t\t\tCon_Printf(CON_WARNING\"%s: Face %i has only %i edge(s) - \\\"%s\\\".\\n\", loadmodel->name, surfnum, out->numedges, out->texinfo->texture->name);\n\n\t\t\n\t// set the drawing flags flag\t\t\n\t\tif (!Q_strncmp(out->texinfo->texture->name,\"sky\",3))\t// sky\n\t\t{\n\t\t\tout->flags |= (SURF_DRAWSKY | SURF_DRAWTILED);\n\t\t\tcontinue;\n\t\t}\n\t\tif (*out->texinfo->texture->name == '*' || (*out->texinfo->texture->name == '!' && loadmodel->fromgame == fg_halflife))\t\t// turbulent\n\t\t{\n\t\t\tout->flags |= SURF_DRAWTURB;\n\t\t\tif (out->texinfo->flags & TEX_SPECIAL)\n\t\t\t{\n\t\t\t\tout->flags |= SURF_DRAWTILED;\n\t\t\t\tfor (i=0 ; i<2 ; i++)\n\t\t\t\t{\n\t\t\t\t\tout->extents[i] = 16384;\n\t\t\t\t\tout->texturemins[i] = -8192;\n\t\t\t\t}\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\n\t\t/*if (*out->texinfo->texture->name == '~')\n\t\t{\n\t\t\tout->texinfo->flags |= SURF_BLENDED;\n\t\t\tcontinue;\n\t\t}*/\n\t\tif (!Q_strncmp(out->texinfo->texture->name,\"{\",1))\t\t// alpha\n\t\t{\n\t\t\tout->flags |= (SURF_DRAWALPHA);\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (out->flags & SURF_DRAWALPHA)\n\t\t\tout->flags &= ~SURF_DRAWALPHA;\n\t}\n\n\tif (!lightmapusable)\n\t{\n\t\tCon_Printf(\"no valid lightmap offsets in map\\n\");\n#ifdef RUNTIMELIGHTING\n\t\tRelightTerminate(loadmodel);\t//not gonna work...\n#endif\n\t\tloadmodel->lightdata = NULL;\n\t\tloadmodel->deluxdata = NULL;\n\t}\n\treturn true;\n}\n\n/*\n=================\nMod_LoadNodes\n=================\n*/\nstatic qboolean Mod_LoadNodes (model_t *loadmodel, qbyte *mod_base, lump_t *l, subbsp_t subbsp)\n{\n\tint\t\t\ti, j, count, p;\n\tmnode_t \t*out;\n\n\tif (subbsp == sb_long2)\n\t{\n\t\tdl2node_t\t\t*in;\n\t\tin = (void *)(mod_base + l->fileofs);\n\t\tcount = l->filelen / sizeof(*in);\n\t\tif (l->filelen % sizeof(*in) || count > SANITY_LIMIT(*out))\n\t\t{\n\t\t\tCon_Printf (CON_ERROR \"MOD_LoadBmodel: funny lump size in %s\\n\",loadmodel->name);\n\t\t\treturn false;\n\t\t}\n\t\tout = ZG_Malloc(&loadmodel->memgroup, count*sizeof(*out));\n\n\t\tloadmodel->nodes = out;\n\t\tloadmodel->numnodes = count;\n\n\t\tfor ( i=0 ; i<count ; i++, in++, out++)\n\t\t{\n\t\t\tfor (j=0 ; j<3 ; j++)\n\t\t\t{\n\t\t\t\tout->minmaxs[j] = LittleFloat (in->mins[j]);\n\t\t\t\tout->minmaxs[3+j] = LittleFloat (in->maxs[j]);\n\t\t\t}\n\t\t\n\t\t\tp = LittleLong(in->planenum);\n\t\t\tout->plane = loadmodel->planes + p;\n\n\t\t\tout->firstsurface = LittleLong (in->firstface);\n\t\t\tout->numsurfaces = LittleLong (in->numfaces);\n\t\t\t\n\t\t\tfor (j=0 ; j<2 ; j++)\n\t\t\t{\n\t\t\t\tp = LittleLong (in->children[j]);\n\t\t\t\tif (p >= 0)\n\t\t\t\t\tout->children[j] = loadmodel->nodes + p;\n\t\t\t\telse\n\t\t\t\t\tout->children[j] = (mnode_t *)(loadmodel->leafs + (-1 - p));\n\t\t\t}\n\t\t}\n\t}\n\telse if (subbsp == sb_long1)\n\t{\n\t\tdl1node_t\t\t*in;\n\t\tin = (void *)(mod_base + l->fileofs);\n\t\tcount = l->filelen / sizeof(*in);\n\t\tif (l->filelen % sizeof(*in) || count > SANITY_LIMIT(*out))\n\t\t{\n\t\t\tCon_Printf (CON_ERROR \"MOD_LoadBmodel: funny lump size in %s\\n\",loadmodel->name);\n\t\t\treturn false;\n\t\t}\n\t\tout = ZG_Malloc(&loadmodel->memgroup, count*sizeof(*out));\n\n\t\tloadmodel->nodes = out;\n\t\tloadmodel->numnodes = count;\n\n\t\tfor ( i=0 ; i<count ; i++, in++, out++)\n\t\t{\n\t\t\tfor (j=0 ; j<3 ; j++)\n\t\t\t{\n\t\t\t\tout->minmaxs[j] = LittleShort (in->mins[j]);\n\t\t\t\tout->minmaxs[3+j] = LittleShort (in->maxs[j]);\n\t\t\t}\n\t\t\n\t\t\tp = LittleLong(in->planenum);\n\t\t\tout->plane = loadmodel->planes + p;\n\n\t\t\tout->firstsurface = LittleLong (in->firstface);\n\t\t\tout->numsurfaces = LittleLong (in->numfaces);\n\n\t\t\tfor (j=0 ; j<2 ; j++)\n\t\t\t{\n\t\t\t\tp = LittleLong (in->children[j]);\n\t\t\t\tif (p >= 0)\n\t\t\t\t\tout->children[j] = loadmodel->nodes + p;\n\t\t\t\telse\n\t\t\t\t\tout->children[j] = (mnode_t *)(loadmodel->leafs + (-1 - p));\n\t\t\t}\n\t\t}\n\t}\n\telse\n\t{\n\t\tdsnode_t\t\t*in;\n\t\tin = (void *)(mod_base + l->fileofs);\n\t\tcount = l->filelen / sizeof(*in);\n\t\tif (l->filelen % sizeof(*in) || count > SANITY_LIMIT(*out))\n\t\t{\n\t\t\tCon_Printf (CON_ERROR \"MOD_LoadBmodel: funny lump size in %s\\n\",loadmodel->name);\n\t\t\treturn false;\n\t\t}\n\t\tout = ZG_Malloc(&loadmodel->memgroup, count*sizeof(*out));\n\n\t\tloadmodel->nodes = out;\n\t\tloadmodel->numnodes = count;\n\n\t\tfor ( i=0 ; i<count ; i++, in++, out++)\n\t\t{\n\t\t\tfor (j=0 ; j<3 ; j++)\n\t\t\t{\n\t\t\t\tout->minmaxs[j] = LittleShort (in->mins[j]);\n\t\t\t\tout->minmaxs[3+j] = LittleShort (in->maxs[j]);\n\t\t\t}\n\t\t\n\t\t\tp = LittleLong(in->planenum);\n\t\t\tout->plane = loadmodel->planes + p;\n\n\t\t\tout->firstsurface = (unsigned short)LittleShort (in->firstface);\n\t\t\tout->numsurfaces = (unsigned short)LittleShort (in->numfaces);\n\n\t\t\tfor (j=0 ; j<2 ; j++)\n\t\t\t{\n\t\t\t\tp = (unsigned short)LittleShort (in->children[j]);\n\n\t\t\t\tif (p >= 0 && p < loadmodel->numnodes)\n\t\t\t\t\tout->children[j] = loadmodel->nodes + p;\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tp = (-1 - (signed)(0xffff0000|p));\n\t\t\t\t\tif (p >= 0 && p < loadmodel->numleafs)\n\t\t\t\t\t\tout->children[j] = (mnode_t *)(loadmodel->leafs + p);\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tCon_Printf (CON_ERROR \"MOD_LoadBmodel: invalid node child %i in %s\\n\", LittleShort (in->children[j]), loadmodel->name);\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\t\n\tMod_SetParent (loadmodel->nodes, NULL);\t// sets nodes and leafs\n\treturn true;\n}\n\n/*\n=================\nMod_LoadLeafs\n=================\n*/\nstatic qboolean Mod_LoadLeafs (model_t *loadmodel, qbyte *mod_base, lump_t *l, subbsp_t subbsp, qboolean isnotmap, qbyte *ptr, size_t len)\n{\n\tmleaf_t \t*out;\n\tint\t\t\ti, j, count, p;\n\n\tif (!ptr)\n\t{\n\t\tptr = mod_base + l->fileofs;\n\t\tlen = l->filelen;\n\t}\n\n\tif (subbsp == sb_long2)\n\t{\n\t\tdl2leaf_t \t*in;\n\t\tin = (void *)ptr;\n\t\tcount = len / sizeof(*in);\n\t\tif (len % sizeof(*in) || count > SANITY_LIMIT(*out))\n\t\t{\n\t\t\tCon_Printf (CON_ERROR \"MOD_LoadBmodel: funny lump size in %s\\n\",loadmodel->name);\n\t\t\treturn false;\n\t\t}\n\t\tout = ZG_Malloc(&loadmodel->memgroup, count*sizeof(*out));\n\n\t\tloadmodel->leafs = out;\n\t\tloadmodel->numleafs = count;\n\t\tloadmodel->numclusters = count-1;\n\t\tloadmodel->pvsbytes = ((loadmodel->numclusters+31)>>3)&~3;\n\n\t\tfor ( i=0 ; i<count ; i++, in++, out++)\n\t\t{\n\t\t\tfor (j=0 ; j<3 ; j++)\n\t\t\t{\n\t\t\t\tout->minmaxs[j] = LittleFloat (in->mins[j]);\n\t\t\t\tout->minmaxs[3+j] = LittleFloat (in->maxs[j]);\n\t\t\t}\n\n\t\t\tp = LittleLong(in->contents);\n\t\t\tout->contents = p;\n\n\t\t\tout->firstmarksurface = loadmodel->marksurfaces +\n\t\t\t\tLittleLong(in->firstmarksurface);\n\t\t\tout->nummarksurfaces = LittleLong(in->nummarksurfaces);\n\t\t\t\n\t\t\tp = LittleLong(in->visofs);\n\t\t\tif (p == -1)\n\t\t\t\tout->compressed_vis = NULL;\n\t\t\telse\n\t\t\t\tout->compressed_vis = loadmodel->visdata + p;\n\t\t\t\n\t\t\tfor (j=0 ; j<4 ; j++)\n\t\t\t\tout->ambient_sound_level[j] = in->ambient_level[j];\n\n\t#ifndef CLIENTONLY\n\t\t\tif (!isDedicated)\n\t#endif\n\t\t\t{\n\t\t\t\t// gl underwater warp\n\t\t\t\tif (out->contents != Q1CONTENTS_EMPTY)\n\t\t\t\t{\n\t\t\t\t\tfor (j=0 ; j<out->nummarksurfaces ; j++)\n\t\t\t\t\t\tout->firstmarksurface[j]->flags |= SURF_UNDERWATER;\n\t\t\t\t}\n\t\t\t\tif (isnotmap)\n\t\t\t\t{\n\t\t\t\t\tfor (j=0 ; j<out->nummarksurfaces ; j++)\n\t\t\t\t\t\tout->firstmarksurface[j]->flags |= SURF_DONTWARP;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\telse if (subbsp == sb_long1)\n\t{\n\t\tdl1leaf_t \t*in;\n\t\tin = (void *)(ptr);\n\t\tcount = len / sizeof(*in);\n\t\tif (len % sizeof(*in) || count > SANITY_LIMIT(*out))\n\t\t{\n\t\t\tCon_Printf (CON_ERROR \"MOD_LoadBmodel: funny lump size in %s\\n\",loadmodel->name);\n\t\t\treturn false;\n\t\t}\n\t\tout = ZG_Malloc(&loadmodel->memgroup, count*sizeof(*out));\n\n\t\tloadmodel->leafs = out;\n\t\tloadmodel->numleafs = count;\n\t\tloadmodel->numclusters = count-1;\n\t\tloadmodel->pvsbytes = ((loadmodel->numclusters+31)>>3)&~3;\n\n\t\tfor ( i=0 ; i<count ; i++, in++, out++)\n\t\t{\n\t\t\tfor (j=0 ; j<3 ; j++)\n\t\t\t{\n\t\t\t\tout->minmaxs[j] = LittleShort (in->mins[j]);\n\t\t\t\tout->minmaxs[3+j] = LittleShort (in->maxs[j]);\n\t\t\t}\n\n\t\t\tp = LittleLong(in->contents);\n\t\t\tout->contents = p;\n\n\t\t\tout->firstmarksurface = loadmodel->marksurfaces +\n\t\t\t\tLittleLong(in->firstmarksurface);\n\t\t\tout->nummarksurfaces = LittleLong(in->nummarksurfaces);\n\t\t\t\n\t\t\tp = LittleLong(in->visofs);\n\t\t\tif (p == -1)\n\t\t\t\tout->compressed_vis = NULL;\n\t\t\telse\n\t\t\t\tout->compressed_vis = loadmodel->visdata + p;\n\t\t\t\n\t\t\tfor (j=0 ; j<4 ; j++)\n\t\t\t\tout->ambient_sound_level[j] = in->ambient_level[j];\n\n\t#ifndef CLIENTONLY\n\t\t\tif (!isDedicated)\n\t#endif\n\t\t\t{\n\t\t\t\t// gl underwater warp\n\t\t\t\tif (out->contents != Q1CONTENTS_EMPTY)\n\t\t\t\t{\n\t\t\t\t\tfor (j=0 ; j<out->nummarksurfaces ; j++)\n\t\t\t\t\t\tout->firstmarksurface[j]->flags |= SURF_UNDERWATER;\n\t\t\t\t}\n\t\t\t\tif (isnotmap)\n\t\t\t\t{\n\t\t\t\t\tfor (j=0 ; j<out->nummarksurfaces ; j++)\n\t\t\t\t\t\tout->firstmarksurface[j]->flags |= SURF_DONTWARP;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\telse\n\t{\n\t\tdsleaf_t \t*in;\n\t\tin = (void *)(ptr);\n\t\tcount = len / sizeof(*in);\n\t\tif (len % sizeof(*in) || count > SANITY_LIMIT(*out))\n\t\t{\n\t\t\tCon_Printf (CON_ERROR \"MOD_LoadBmodel: funny lump size in %s\\n\",loadmodel->name);\n\t\t\treturn false;\n\t\t}\n\t\tout = ZG_Malloc(&loadmodel->memgroup, count*sizeof(*out));\n\n\t\tloadmodel->leafs = out;\n\t\tloadmodel->numleafs = count;\n\t\tloadmodel->numclusters = count-1;\n\t\tloadmodel->pvsbytes = ((loadmodel->numclusters+31)>>3)&~3;\n\n\t\tfor ( i=0 ; i<count ; i++, in++, out++)\n\t\t{\n\t\t\tfor (j=0 ; j<3 ; j++)\n\t\t\t{\n\t\t\t\tout->minmaxs[j] = LittleShort (in->mins[j]);\n\t\t\t\tout->minmaxs[3+j] = LittleShort (in->maxs[j]);\n\t\t\t}\n\n\t\t\tp = LittleLong(in->contents);\n\t\t\tout->contents = p;\n\n\t\t\tout->firstmarksurface = loadmodel->marksurfaces + (unsigned short)LittleShort(in->firstmarksurface);\n\t\t\tout->nummarksurfaces = (unsigned short)LittleShort(in->nummarksurfaces);\n\t\t\t\n\t\t\tp = LittleLong(in->visofs);\n\t\t\tif (p == -1)\n\t\t\t\tout->compressed_vis = NULL;\n\t\t\telse\n\t\t\t\tout->compressed_vis = loadmodel->visdata + p;\n\t\t\t\n\t\t\tfor (j=0 ; j<4 ; j++)\n\t\t\t\tout->ambient_sound_level[j] = in->ambient_level[j];\n\n\t#ifndef CLIENTONLY\n\t\t\tif (!isDedicated)\n\t#endif\n\t\t\t{\n\t\t\t\t// gl underwater warp\n\t\t\t\tif (out->contents != Q1CONTENTS_EMPTY)\n\t\t\t\t{\n\t\t\t\t\tfor (j=0 ; j<out->nummarksurfaces ; j++)\n\t\t\t\t\t\tout->firstmarksurface[j]->flags |= SURF_UNDERWATER;\n\t\t\t\t}\n\t\t\t\tif (isnotmap)\n\t\t\t\t{\n\t\t\t\t\tfor (j=0 ; j<out->nummarksurfaces ; j++)\n\t\t\t\t\t\tout->firstmarksurface[j]->flags |= SURF_DONTWARP;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn true;\n}\n\n\n\n\n/*\n=================\nMod_LoadClipnodes\n=================\n*/\nstatic qboolean Mod_LoadClipnodes (model_t *loadmodel, qbyte *mod_base, lump_t *l, subbsp_t subbsp, qboolean hexen2map)\n{\n\tdsclipnode_t *ins;\n\tdlclipnode_t *inl;\n\tmclipnode_t *out;\n\tint\t\t\ti, count;\n\thull_t\t\t*hull;\n\n\tif (subbsp == sb_long1 || subbsp == sb_long2)\n\t{\n\t\tins = NULL;\n\t\tinl = (void *)(mod_base + l->fileofs);\n\t\tcount = l->filelen / sizeof(*inl);\n\t\tif (l->filelen % sizeof(*inl) || count > SANITY_LIMIT(*out))\n\t\t{\n\t\t\tCon_Printf (CON_ERROR \"MOD_LoadBmodel: funny lump size in %s\\n\",loadmodel->name);\n\t\t\treturn false;\n\t\t}\n\t}\n\telse\n\t{\n\t\tins = (void *)(mod_base + l->fileofs);\n\t\tinl = NULL;\n\t\tcount = l->filelen / sizeof(*ins);\n\t\tif (l->filelen % sizeof(*ins) || count > SANITY_LIMIT(*out))\n\t\t{\n\t\t\tCon_Printf (CON_ERROR \"MOD_LoadBmodel: funny lump size in %s\\n\",loadmodel->name);\n\t\t\treturn false;\n\t\t}\n\t\tif (count > (1u<<16))\n\t\t{\n\t\t\tCon_Printf (CON_ERROR \"%s: clipnode count exceeds 16bit limit (%u). Try bsp2.\\n\", loadmodel->name, count);\n\t\t\treturn false;\n\t\t}\n\t}\n\tout = ZG_Malloc(&loadmodel->memgroup, count*sizeof(*out));//space for both\n\n\tloadmodel->clipnodes = out;\n\tloadmodel->numclipnodes = count;\n\n\n\tif (hexen2map)\n\t{\t//hexen2.\n\t\t//compatible with Q1.\n\t\thull = &loadmodel->hulls[1];\n\t\thull->clipnodes = out;\n\t\thull->firstclipnode = 0;\n\t\thull->lastclipnode = count-1;\n\t\thull->planes = loadmodel->planes;\n\t\thull->clip_mins[0] = -16;\n\t\thull->clip_mins[1] = -16;\n\t\thull->clip_mins[2] = -24;\n\t\thull->clip_maxs[0] = 16;\n\t\thull->clip_maxs[1] = 16;\n\t\thull->clip_maxs[2] = 32;\n\t\thull->available = true;\n\n\t\t//NOT compatible with Q1\n\t\thull = &loadmodel->hulls[2];\n\t\thull->clipnodes = out;\n\t\thull->firstclipnode = 0;\n\t\thull->lastclipnode = count-1;\n\t\thull->planes = loadmodel->planes;\n\t\thull->clip_mins[0] = -24;\n\t\thull->clip_mins[1] = -24;\n\t\thull->clip_mins[2] = -20;\n\t\thull->clip_maxs[0] = 24;\n\t\thull->clip_maxs[1] = 24;\n\t\thull->clip_maxs[2] = 20;\n\t\thull->available = true;\n\n\t\thull = &loadmodel->hulls[3];\n\t\thull->clipnodes = out;\n\t\thull->firstclipnode = 0;\n\t\thull->lastclipnode = count-1;\n\t\thull->planes = loadmodel->planes;\n\t\thull->clip_mins[0] = -16;\n\t\thull->clip_mins[1] = -16;\n\t\thull->clip_mins[2] = -12;\n\t\thull->clip_maxs[0] = 16;\n\t\thull->clip_maxs[1] = 16;\n\t\thull->clip_maxs[2] = 16;\n\t\thull->available = true;\n\n\t\t/*\n\t\tThere is some mission-pack weirdness here\n\t\tin the missionpack, hull 4 is meant to be '-8 -8 -8' '8 8 8'\n\t\tin the original game, hull 4 is '-40 -40 -42' '40 40 42'\n\t\t*/\n\t\thull = &loadmodel->hulls[4];\n\t\thull->clipnodes = out;\n\t\thull->firstclipnode = 0;\n\t\thull->lastclipnode = count-1;\n\t\thull->planes = loadmodel->planes;\n\t\thull->clip_mins[0] = -8;\n\t\thull->clip_mins[1] = -8;\n\t\thull->clip_mins[2] = -8;\n\t\thull->clip_maxs[0] = 8;\n\t\thull->clip_maxs[1] = 8;\n\t\thull->clip_maxs[2] = 8;\n\t\thull->available = true;\n\n\t\thull = &loadmodel->hulls[5];\n\t\thull->clipnodes = out;\n\t\thull->firstclipnode = 0;\n\t\thull->lastclipnode = count-1;\n\t\thull->planes = loadmodel->planes;\n\t\thull->clip_mins[0] = -48;\n\t\thull->clip_mins[1] = -48;\n\t\thull->clip_mins[2] = -50;\n\t\thull->clip_maxs[0] = 48;\n\t\thull->clip_maxs[1] = 48;\n\t\thull->clip_maxs[2] = 50;\n\t\thull->available = true;\n\n\t\t//6 isn't used.\n\t\t//7 isn't used.\n\t}\n\telse if (loadmodel->fromgame == fg_halflife)\n\t{\n\t\thull = &loadmodel->hulls[1];\n\t\thull->clipnodes = out;\n\t\thull->firstclipnode = 0;\n\t\thull->lastclipnode = count-1;\n\t\thull->planes = loadmodel->planes;\n\t\thull->clip_mins[0] = -16;\n\t\thull->clip_mins[1] = -16;\n\t\thull->clip_mins[2] = -36;//-36 is correct here, but mvdsv uses -32 instead. This breaks prediction between the two\n\t\thull->clip_maxs[0] = 16;\n\t\thull->clip_maxs[1] = 16;\n\t\thull->clip_maxs[2] = hull->clip_mins[2]+72;\n\t\thull->available = true;\n\n\t\thull = &loadmodel->hulls[2];\n\t\thull->clipnodes = out;\n\t\thull->firstclipnode = 0;\n\t\thull->lastclipnode = count-1;\n\t\thull->planes = loadmodel->planes;\n\t\thull->clip_mins[0] = -32;\n\t\thull->clip_mins[1] = -32;\n\t\thull->clip_mins[2] = -32;\n\t\thull->clip_maxs[0] = 32;\n\t\thull->clip_maxs[1] = 32;\n\t\thull->clip_maxs[2] = hull->clip_mins[2]+64;\n\t\thull->available = true;\n\n\t\thull = &loadmodel->hulls[3];\n\t\thull->clipnodes = out;\n\t\thull->firstclipnode = 0;\n\t\thull->lastclipnode = count-1;\n\t\thull->planes = loadmodel->planes;\n\t\thull->clip_mins[0] = -16;\n\t\thull->clip_mins[1] = -16;\n\t\thull->clip_mins[2] = -18;\n\t\thull->clip_maxs[0] = 16;\n\t\thull->clip_maxs[1] = 16;\n\t\thull->clip_maxs[2] = hull->clip_mins[2]+36;\n\t\thull->available = true;\n\t}\n\telse\n\t{\n\t\thull = &loadmodel->hulls[1];\n\t\thull->clipnodes = out;\n\t\thull->firstclipnode = 0;\n\t\thull->lastclipnode = count-1;\n\t\thull->planes = loadmodel->planes;\n\t\thull->clip_mins[0] = -16;\n\t\thull->clip_mins[1] = -16;\n\t\thull->clip_mins[2] = -24;\n\t\thull->clip_maxs[0] = 16;\n\t\thull->clip_maxs[1] = 16;\n\t\thull->clip_maxs[2] = 32;\n\t\thull->available = true;\n\n\t\thull = &loadmodel->hulls[2];\n\t\thull->clipnodes = out;\n\t\thull->firstclipnode = 0;\n\t\thull->lastclipnode = count-1;\n\t\thull->planes = loadmodel->planes;\n\t\thull->clip_mins[0] = -32;\n\t\thull->clip_mins[1] = -32;\n\t\thull->clip_mins[2] = -24;\n\t\thull->clip_maxs[0] = 32;\n\t\thull->clip_maxs[1] = 32;\n\t\thull->clip_maxs[2] = 64;\n\t\thull->available = true;\n\n\t\thull = &loadmodel->hulls[3];\n\t\thull->clipnodes = out;\n\t\thull->firstclipnode = 0;\n\t\thull->lastclipnode = count-1;\n\t\thull->planes = loadmodel->planes;\n\t\thull->clip_mins[0] = -16;\n\t\thull->clip_mins[1] = -16;\n\t\thull->clip_mins[2] = -6;\n\t\thull->clip_maxs[0] = 16;\n\t\thull->clip_maxs[1] = 16;\n\t\thull->clip_maxs[2] = 30;\n\t\thull->available = false;\n\t}\n\n\tif (subbsp == sb_long1 || subbsp == sb_long2)\n\t{\n\t\tfor (i=0 ; i<count ; i++, out++, inl++)\n\t\t{\n\t\t\tout->planenum = LittleLong(inl->planenum);\n\t\t\tout->children[0] = LittleLong(inl->children[0]);\n\t\t\tout->children[1] = LittleLong(inl->children[1]);\n\t\t}\n\t}\n\telse\n\t{\n\t\tfor (i=0 ; i<count ; i++, out++, ins++)\n\t\t{\n\t\t\tout->planenum = LittleLong(ins->planenum);\n\t\t\tout->children[0] = (unsigned short)LittleShort(ins->children[0]);\n\t\t\tout->children[1] = (unsigned short)LittleShort(ins->children[1]);\n\n\t\t\t//if these 'overflow', then they're meant to refer to contents instead, and should be negative\n\t\t\tif (out->children[0] >= count)\n\t\t\t\tout->children[0] -= 0x10000;\n\t\t\tif (out->children[1] >= count)\n\t\t\t\tout->children[1] -= 0x10000;\n\t\t}\n\t}\n\n\treturn true;\n}\n\n/*\n=================\nMod_MakeHull0\n\nDeplicate the drawing hull structure as a clipping hull\n=================\n*/\nstatic void Mod_MakeHull0 (model_t *loadmodel)\n{\n\tmnode_t\t\t*in, *child;\n\tmclipnode_t *out;\n\tint\t\t\ti, j, count;\n\thull_t\t\t*hull;\n\n\thull = &loadmodel->hulls[0];\t\n\n\tin = loadmodel->nodes;\n\tcount = loadmodel->numnodes;\n\tout = ZG_Malloc(&loadmodel->memgroup, count*sizeof(*out));\n\n\thull->clipnodes = out;\n\thull->firstclipnode = 0;\n\thull->lastclipnode = count-1;\n\thull->planes = loadmodel->planes;\n\n\tfor (i=0 ; i<count ; i++, out++, in++)\n\t{\n\t\tout->planenum = in->plane - loadmodel->planes;\n\t\tfor (j=0 ; j<2 ; j++)\n\t\t{\n\t\t\tchild = in->children[j];\n\t\t\tif (child->contents < 0)\n\t\t\t\tout->children[j] = child->contents;\n\t\t\telse\n\t\t\t\tout->children[j] = child - loadmodel->nodes;\n\t\t}\n\t}\n}\n\n/*\n=================\nMod_LoadPlanes\n=================\n*/\nstatic qboolean Mod_LoadPlanes (model_t *loadmodel, qbyte *mod_base, lump_t *l)\n{\n\tint\t\t\ti, j;\n\tmplane_t\t*out;\n\tdplane_t \t*in;\n\tint\t\t\tcount;\n\tint\t\t\tbits;\n\t\n\tin = (void *)(mod_base + l->fileofs);\n\tcount = l->filelen / sizeof(*in);\n\tif (l->filelen % sizeof(*in) || count > SANITY_LIMIT(*out))\n\t{\n\t\tCon_Printf (CON_ERROR \"MOD_LoadBmodel: funny lump size in %s\\n\",loadmodel->name);\n\t\treturn false;\n\t}\n\tout = ZG_Malloc(&loadmodel->memgroup, count*2*sizeof(*out));\n\t\n\tloadmodel->planes = out;\n\tloadmodel->numplanes = count;\n\n\tfor ( i=0 ; i<count ; i++, in++, out++)\n\t{\n\t\tbits = 0;\n\t\tfor (j=0 ; j<3 ; j++)\n\t\t{\n\t\t\tout->normal[j] = LittleFloat (in->normal[j]);\n\t\t\tif (out->normal[j] < 0)\n\t\t\t\tbits |= 1<<j;\n\t\t}\n\n\t\tout->dist = LittleFloat (in->dist);\n\t\tout->type = LittleLong (in->type);\n\t\tout->signbits = bits;\n\t}\n\n\treturn true;\n}\n\nstatic void Mod_FixupNodeMinsMaxs (mnode_t *node, mnode_t *parent)\n{\n\tif (!node)\n\t\treturn;\n\n\tif (node->contents >= 0)\n\t{\n\t\tMod_FixupNodeMinsMaxs (node->children[0], node);\n\t\tMod_FixupNodeMinsMaxs (node->children[1], node);\n\t}\n\n\tif (parent)\n\t{\n\t\tif (parent->minmaxs[0] > node->minmaxs[0])\n\t\t\tparent->minmaxs[0] = node->minmaxs[0];\n\t\tif (parent->minmaxs[1] > node->minmaxs[1])\n\t\t\tparent->minmaxs[1] = node->minmaxs[1];\n\t\tif (parent->minmaxs[2] > node->minmaxs[2])\n\t\t\tparent->minmaxs[2] = node->minmaxs[2];\n\n\t\tif (parent->minmaxs[3] < node->minmaxs[3])\n\t\t\tparent->minmaxs[3] = node->minmaxs[3];\n\t\tif (parent->minmaxs[4] < node->minmaxs[4])\n\t\t\tparent->minmaxs[4] = node->minmaxs[4];\n\t\tif (parent->minmaxs[5] < node->minmaxs[5])\n\t\t\tparent->minmaxs[5] = node->minmaxs[5];\n\t}\n\n}\n\nstatic void Mod_FixupMinsMaxs(model_t *loadmodel)\n{\n\t//q1 bsps are capped to +/- 32767 by the nodes/leafs\n\t//verts arn't though\n\t//so if the map is too big, let's figure out what they should be\n\tfloat *v;\n\tmsurface_t **mark, *surf;\n\tmleaf_t *pleaf;\n\tmedge_t *e, *pedges;\n\tint en, lindex;\n\tint i, c, lnumverts;\n\tqboolean needsfixup = false;\n\n\tif (loadmodel->mins[0] < -32768)\n\t\tneedsfixup = true;\n\tif (loadmodel->mins[1] < -32768)\n\t\tneedsfixup = true;\n\tif (loadmodel->mins[2] < -32768)\n\t\tneedsfixup = true;\n\n\tif (loadmodel->maxs[0] > 32767)\n\t\tneedsfixup = true;\n\tif (loadmodel->maxs[1] > 32767)\n\t\tneedsfixup = true;\n\tif (loadmodel->maxs[2] > 32767)\n\t\tneedsfixup = true;\n\n\tif (!needsfixup)\n\t\treturn;\n\n\t//this is insane.\n\t//why am I writing this?\n\t//by the time the world actually gets this large, the floating point errors are going to be so immensly crazy that it's just not worth it.\n\n\tpedges = loadmodel->edges;\n\n\tfor (i = 0; i < loadmodel->numleafs; i++)\n\t{\n\t\tpleaf = &loadmodel->leafs[i];\n\n\t\tmark = pleaf->firstmarksurface;\n\t\tc = pleaf->nummarksurfaces;\n\n\t\tif (c)\n\t\t{\n\t\t\tdo\n\t\t\t{\n\t\t\t\tsurf = (*mark++);\n\n\t\t\t\tlnumverts = surf->numedges;\n\t\t\t\tfor (en=0 ; en<lnumverts ; en++)\n\t\t\t\t{\n\t\t\t\t\tlindex = loadmodel->surfedges[surf->firstedge + en];\n\n\t\t\t\t\tif (lindex > 0)\n\t\t\t\t\t{\n\t\t\t\t\t\te = &pedges[lindex];\n\t\t\t\t\t\tv = loadmodel->vertexes[e->v[0]].position;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\te = &pedges[-lindex];\n\t\t\t\t\t\tv = loadmodel->vertexes[e->v[1]].position;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (pleaf->minmaxs[0] > v[0])\n\t\t\t\t\t\tpleaf->minmaxs[0] = v[0];\n\t\t\t\t\tif (pleaf->minmaxs[1] > v[1])\n\t\t\t\t\t\tpleaf->minmaxs[1] = v[1];\n\t\t\t\t\tif (pleaf->minmaxs[2] > v[2])\n\t\t\t\t\t\tpleaf->minmaxs[2] = v[2];\n\n\t\t\t\t\tif (pleaf->minmaxs[3] < v[0])\n\t\t\t\t\t\tpleaf->minmaxs[3] = v[0];\n\t\t\t\t\tif (pleaf->minmaxs[4] < v[1])\n\t\t\t\t\t\tpleaf->minmaxs[4] = v[1];\n\t\t\t\t\tif (pleaf->minmaxs[5] < v[2])\n\t\t\t\t\t\tpleaf->minmaxs[5] = v[2];\n\n\t\t\t\t}\n\t\t\t} while (--c);\n\t\t}\n\t}\n\tMod_FixupNodeMinsMaxs (loadmodel->nodes, NULL);\t// sets nodes and leafs\n}\n\n#endif\n\nvoid ModBrush_LoadGLStuff(void *ctx, void *data, size_t a, size_t b)\n{\n#ifndef SERVERONLY\n\tmodel_t *mod = ctx;\n\tchar loadname[MAX_QPATH];\n\n\tif (!a)\n\t{\t//submodels share textures, so only do this if 'a' is 0 (inline index, 0 = world).\n\t\tfor (a = 0; a < mod->numfogs; a++)\n\t\t{\n\t\t\tmod->fogs[a].shader = R_RegisterShader_Lightmap(mod, mod->fogs[a].shadername);\n\t\t\tR_BuildDefaultTexnums(NULL, mod->fogs[a].shader, IF_WORLDTEX);\n\t\t\tif (!mod->fogs[a].shader->fog_dist)\n\t\t\t{\n\t\t\t\t//invalid fog shader, don't use.\n\t\t\t\tmod->fogs[a].shader = NULL;\n\t\t\t\tmod->fogs[a].numplanes = 0;\n\t\t\t}\n\t\t}\n\n#if defined(Q3BSPS) || defined(RFBSPS)\n\t\tif (mod->fromgame == fg_quake3)\n\t\t{\n\t\t\tif (mod->lightmaps.deluxemapping && mod->lightmaps.deluxemapping_modelspace)\n\t\t\t{\n\t\t\t\tfor(a = 0; a < mod->numtexinfo; a++)\n\t\t\t\t{\n\t\t\t\t\tmod->textures[a]->shader = R_RegisterShader_Lightmap(mod, va(\"%s#BUMPMODELSPACE\", mod->textures[a]->name));\n\t\t\t\t\tR_BuildDefaultTexnums(NULL, mod->textures[a]->shader, IF_WORLDTEX);\n\n\t\t\t\t\tmod->textures[a+mod->numtexinfo]->shader = R_RegisterShader_Vertex (mod, va(\"%s#VERTEXLIT\", mod->textures[a+mod->numtexinfo]->name));\n\t\t\t\t\tR_BuildDefaultTexnums(NULL, mod->textures[a+mod->numtexinfo]->shader, IF_WORLDTEX);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tfor(a = 0; a < mod->numtexinfo; a++)\n\t\t\t\t{\n\t\t\t\t\tmod->textures[a]->shader = R_RegisterShader_Lightmap(mod, mod->textures[a]->name);\n\t\t\t\t\tR_BuildDefaultTexnums(NULL, mod->textures[a]->shader, IF_WORLDTEX);\n\n\t\t\t\t\tmod->textures[a+mod->numtexinfo]->shader = R_RegisterShader_Vertex (mod, va(\"%s#VERTEXLIT\", mod->textures[a+mod->numtexinfo]->name));\n\t\t\t\t\tR_BuildDefaultTexnums(NULL, mod->textures[a+mod->numtexinfo]->shader, IF_WORLDTEX);\n\t\t\t\t}\n\t\t\t}\n\t\t\tmod->textures[2*mod->numtexinfo]->shader = R_RegisterShader_Flare(mod, \"noshader\");\n\t\t}\n\t\telse\n#endif\n#ifdef Q2BSPS\n\t\tif (mod->fromgame == fg_quake2)\n\t\t{\n\t\t\tCOM_FileBase (mod->name, loadname, sizeof(loadname));\n\t\t\tfor(a = 0; a < mod->numtextures; a++)\n\t\t\t{\n\t\t\t\tunsigned int maps = 0;\n\t\t\t\tmod->textures[a]->shader = R_RegisterCustom (mod, mod->textures[a]->name, SUF_LIGHTMAP, Shader_DefaultBSPQ2, NULL);\n\n\t\t\t\tmaps |= SHADER_HASPALETTED;\n\t\t\t\tmaps |= SHADER_HASDIFFUSE;\n\t\t\t\tif (r_fb_bmodels.ival)\n\t\t\t\t\tmaps |= SHADER_HASFULLBRIGHT;\n//\t\t\t\tif (r_loadbumpmapping || (r_waterstyle.ival > 1 && *tx->name == '*'))\n//\t\t\t\t\tmaps |= SHADER_HASNORMALMAP;\n\t\t\t\tif (gl_specular.ival)\n\t\t\t\t\tmaps |= SHADER_HASGLOSS;\n\t\t\t\tR_BuildLegacyTexnums(mod->textures[a]->shader, mod->textures[a]->name, loadname, maps, IF_WORLDTEX, mod->textures[a]->srcfmt, mod->textures[a]->srcwidth, mod->textures[a]->srcheight, mod->textures[a]->srcdata, mod->textures[a]->palette);\n\t\t\t\tBZ_Free(mod->textures[a]->srcdata);\n\t\t\t\tmod->textures[a]->srcdata = NULL;\n\t\t\t}\n\t\t}\n\t\telse\n#endif\n\t\t{\n\t\t\tCOM_FileBase (mod->name, loadname, sizeof(loadname));\n\t\t\tif (!strncmp(loadname, \"b_\", 2))\n\t\t\t\tQ_strncpyz(loadname, \"bmodels\", sizeof(loadname));\n\t\t\tfor(a = 0; a < mod->numtextures; a++)\n\t\t\t\tMod_FinishTexture(mod, mod->textures[a], loadname, false);\n\t\t}\n\t}\n\tMod_Batches_Build(mod, data);\n\tif (data)\n\t\tBZ_Free(data);\n#endif\n}\n\n#ifdef Q1BSPS\n\nstruct vispatch_s\n{\n\tvoid *fileptr;\n\tsize_t filelen;\n\n\tvoid *visptr;\n\tint vislen;\n\n\tvoid *leafptr;\n\tint leaflen;\n};\n\nstatic void Mod_FindVisPatch(struct vispatch_s *patch, model_t *mod, size_t leaflumpsize)\n{\n\tchar patchname[MAX_QPATH];\n\tint *lenptr, len;\n\tint ofs;\n\tqbyte *file;\n\tchar *mapname;\n\tmemset(patch, 0, sizeof(*patch));\n\n\tif (!mod_external_vis.ival)\n\t\treturn;\n\n\tmapname = COM_SkipPath(mod->name);\n\n\tCOM_StripExtension(mod->name, patchname, sizeof(patchname));\n\tQ_strncatz(patchname, \".vis\", sizeof(patchname));\n\n\t//ignore the patch file if its in a different gamedir.\n\t//this file format sucks too much for other verification.\n\tif (FS_FLocateFile(mod->name,FSLF_DEEPONFAILURE, NULL) != FS_FLocateFile(patchname,FSLF_DEEPONFAILURE, NULL))\n\t\treturn;\n\n\tpatch->filelen = FS_LoadFile(patchname, &patch->fileptr);\n\tif (!patch->fileptr)\n\t\treturn;\n\n\tofs = 0;\n\twhile (ofs+36 <= patch->filelen)\n\t{\n\t\tfile = patch->fileptr;\n\t\tfile += ofs;\n\t\tmemcpy(patchname, file, 32);\n\t\tpatchname[32] = 0;\n\t\tfile += 32;\n\t\tlenptr = (int*)file;\n\t\tfile += sizeof(int);\n\t\tlen = LittleLong(*lenptr);\n\t\tif (ofs+36+len > patch->filelen)\n\t\t\tbreak;\n\n\t\tif (!Q_strcasecmp(patchname, mapname))\n\t\t{\n\t\t\tlenptr = (int*)file;\n\t\t\tpatch->vislen = LittleLong(*lenptr);\n\t\t\tfile += sizeof(int);\n\t\t\tpatch->visptr = file;\n\t\t\tfile += patch->vislen;\n\n\t\t\tlenptr = (int*)file;\n\t\t\tpatch->leaflen = LittleLong(*lenptr);\n\t\t\tfile += sizeof(int);\n\t\t\tpatch->leafptr = file;\n\t\t\tfile += patch->leaflen;\n\n\t\t\tif (sizeof(int)*2 + patch->vislen + patch->leaflen != len || patch->leaflen != leaflumpsize)\n\t\t\t{\n\t\t\t\tpatch->visptr = NULL;\n\t\t\t\tpatch->leafptr = NULL;\n\t\t\t}\n\t\t\telse\n\t\t\t\tbreak;\n\t\t}\n\t\tofs += 36+len;\n\t}\n}\n\n/*\n=================\nMod_LoadBrushModel\n=================\n*/\nstatic qboolean QDECL Mod_LoadBrushModel (model_t *mod, void *buffer, size_t fsize)\n{\n\tstruct vispatch_s vispatch;\n\tint\t\t\ti, j;\n\tdheader_t\theader;\n\tmmodel_t \t*bm;\n\tmodel_t *submod;\n\tunsigned int chksum;\n\tqboolean noerrors;\n\tchar loadname[32];\n\tqbyte *mod_base = buffer;\n\tqboolean hexen2map = false;\n\tqboolean isnotmap;\n\tqboolean using_rbe = true;\n\tqboolean misaligned = false;\n\tbspx_header_t *bspx;\n\tsubbsp_t subbsp = sb_none;\n\n\tCOM_FileBase (mod->name, loadname, sizeof(loadname));\n\tmod->type = mod_brush;\n\t\n\tif (fsize < sizeof(header))\n\t\treturn false;\n\n\tmod_base = (qbyte *)buffer;\n\tmemcpy(&header, mod_base, sizeof(header));\n\tfor (i=0 ; i<countof(header.lumps)/4 ; i++)\n\t{\n\t\theader.lumps[i].filelen = LittleLong(header.lumps[i].filelen);\n\t\theader.lumps[i].fileofs = LittleLong(header.lumps[i].fileofs);\n\t}\n\n#ifdef SERVERONLY\n\tisnotmap = !!sv.world.worldmodel;\n#else\n\tif ((!cl.worldmodel && cls.state>=ca_connected)\n#ifndef CLIENTONLY\n\t\t|| (!sv.world.worldmodel && sv.state)\n#endif\n\t\t)\n\t\tisnotmap = false;\n\telse\n\t\tisnotmap = true;\n#endif\n\n\tmod->fromgame = fg_quake;\n\tif (!memcmp(&header.version,  BSPVERSION))\n\t\tmod->engineflags |= MDLF_NEEDOVERBRIGHT;\n\telse if (!memcmp(&header.version,  BSPVERSIONQ64))\n\t\tmod->engineflags |= MDLF_NEEDOVERBRIGHT, subbsp = sb_quake64;\n\telse if (!memcmp(&header.version,  BSPVERSIONPREREL))\n\t\tmod->engineflags |= MDLF_NEEDOVERBRIGHT;\n\telse if (!memcmp(&header.version,  BSPVERSION_LONG1))\n\t\tmod->engineflags |= MDLF_NEEDOVERBRIGHT, subbsp = sb_long1;\n\telse if (!memcmp(&header.version,  BSPVERSION_LONG2))\n\t\tmod->engineflags |= MDLF_NEEDOVERBRIGHT, subbsp = sb_long2;\n\telse if (!memcmp(&header.version,  BSPVERSIONHL))\n\t{\n\t\tchar tmp[64];\n\t\tmod->fromgame = fg_halflife;\n\n\t\t//special hack to work around blueshit bugs - we need to swap LUMP_ENTITIES and LUMP_PLANES over\n\t\tif (COM_ParseOut(mod_base + header.lumps[LUMP_PLANES].fileofs, tmp, sizeof(tmp)) && !strcmp(tmp, \"{\"))\n\t\t{\n\t\t\tCOM_ParseOut(mod_base + header.lumps[LUMP_ENTITIES].fileofs, tmp, sizeof(tmp));\n\t\t\tif (strcmp(tmp, \"{\"))\n\t\t\t{\n\t\t\t\tint i;\n\t\t\t\tfor (i = 0; i < header.lumps[LUMP_ENTITIES].filelen && i < sizeof(dplane_t); i++)\n\t\t\t\t\tif (mod_base[header.lumps[LUMP_ENTITIES].fileofs + i] == 0)\n\t\t\t\t\t{\t//yeah, looks screwy in the way we expect. swap em over.\n\t\t\t\t\t\tlump_t tmp = header.lumps[LUMP_ENTITIES];\n\t\t\t\t\t\theader.lumps[LUMP_ENTITIES] = header.lumps[LUMP_PLANES];\n\t\t\t\t\t\theader.lumps[LUMP_PLANES] = tmp;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\telse\n\t{\n\t\tCon_Printf (CON_ERROR \"Mod_LoadBrushModel: %s has wrong version number (%i)\\n\", mod->name, i);\n\t\treturn false;\n\t}\n\theader.version = LittleLong(header.version);\n\n\tmod->lightmaps.width = 128;//LMBLOCK_WIDTH;\n\tmod->lightmaps.height = 128;//LMBLOCK_HEIGHT; \n\n// checksum all of the map, except for entities\n\tmod->checksum = 0;\n\tmod->checksum2 = 0;\n\n\tfor (i = 0; i < HEADER_LUMPS; i++)\n\t{\n\t\tif ((header.lumps[i].fileofs & 3) && header.lumps[i].filelen)\n\t\t\tmisaligned = true;\n\n\t\tif ((unsigned)header.lumps[i].fileofs + (unsigned)header.lumps[i].filelen > fsize)\n\t\t{\n\t\t\tCon_Printf (CON_ERROR \"Mod_LoadBrushModel: %s appears truncated\\n\", mod->name);\n\t\t\treturn false;\n\t\t}\n\t\tif (i == LUMP_ENTITIES)\n\t\t\tcontinue;\n\t\tchksum = CalcHashInt(&hash_md4, mod_base + header.lumps[i].fileofs, header.lumps[i].filelen);\n\t\tmod->checksum ^= chksum;\n\n\t\tif (i == LUMP_VISIBILITY || i == LUMP_LEAFS || i == LUMP_NODES)\n\t\t\tcontinue;\n\t\tmod->checksum2 ^= chksum;\n\t}\n\n\tif (misaligned)\n\t{\t//pre-phong versions of tyrutils wrote misaligned lumps. These crash on arm/etc.\n\t\tchar *tmp;\n\t\tunsigned int ofs = 0;\n\t\tCon_DPrintf(CON_WARNING\"%s: Misaligned lumps detected\\n\", mod->name);\n\t\ttmp = BZ_Malloc(fsize);\n\t\tmemcpy(tmp, mod_base, fsize);\n\t\tfor (i = 0; i < HEADER_LUMPS; i++)\n\t\t{\n\t\t\tif (ofs + header.lumps[i].filelen > fsize)\n\t\t\t{\t//can happen if two lumps overlap... otherwise impossible.\n\t\t\t\tCon_Printf(CON_ERROR\"%s: Realignment failed\\n\", mod->name);\n\t\t\t\tBZ_Free(tmp);\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tmemcpy(mod_base + ofs, tmp+header.lumps[i].fileofs, header.lumps[i].filelen);\n\t\t\theader.lumps[i].fileofs = ofs;\n\t\t\tofs += header.lumps[i].filelen;\n\t\t\tofs = (ofs + 3) & ~3u;\n\t\t}\n\t\tBZ_Free(tmp);\n\t\tbspx = NULL;\n\t}\n\telse\n\t{\n\t\tbspx = BSPX_Setup(mod, mod_base, fsize, header.lumps, HEADER_LUMPS);\n\n\t\t/*if (1)//mod_ebfs.value)\n\t\t{\n\t\t\tchar *id;\n\t\t\tid = (char *)mod_base + sizeof(dheader_t);\n\t\t\tif (id[0]=='P' && id[1]=='A' && id[2]=='C' && id[3]=='K')\n\t\t\t{\t//EBFS detected.\n\t\t\t\tCOM_LoadMapPackFile(mod->name, sizeof(dheader_t));\n\t\t\t}\n\t\t}*/\n\t}\n\t\t\n\tnoerrors = true;\n\n\tMod_FindVisPatch(&vispatch, mod, header.lumps[LUMP_LEAFS].filelen);\n\n// load into heap\n\tif (!isDedicated || using_rbe)\n\t{\n\t\tTRACE((\"Loading verts\\n\"));\n\t\tnoerrors = noerrors && Mod_LoadVertexes (mod, mod_base, &header.lumps[LUMP_VERTEXES]);\n\t\tTRACE((\"Loading edges\\n\"));\n\t\tnoerrors = noerrors && Mod_LoadEdges (mod, mod_base, &header.lumps[LUMP_EDGES], subbsp);\n\t\tTRACE((\"Loading Surfedges\\n\"));\n\t\tnoerrors = noerrors && Mod_LoadSurfedges (mod, mod_base, &header.lumps[LUMP_SURFEDGES]);\n\t}\n\tif (!isDedicated)\n\t{\n\t\tTRACE((\"Loading Textures\\n\"));\n\t\tnoerrors = noerrors && Mod_LoadTextures (mod, mod_base, &header.lumps[LUMP_TEXTURES], subbsp);\n\t}\n\tTRACE((\"Loading Submodels\\n\"));\n\tnoerrors = noerrors && Mod_LoadSubmodels (mod, mod_base, &header.lumps[LUMP_MODELS], &hexen2map);\n\tTRACE((\"Loading Planes\\n\"));\n\tnoerrors = noerrors && Mod_LoadPlanes (mod, mod_base, &header.lumps[LUMP_PLANES]);\n\tTRACE((\"Loading Entities\\n\"));\n\tMod_LoadEntities (mod, mod_base, &header.lumps[LUMP_ENTITIES]);\n\tif (!isDedicated || using_rbe)\n\t{\n\t\tTRACE((\"Loading Texinfo\\n\"));\n\t\tnoerrors = noerrors && Mod_LoadTexinfo (mod, mod_base, &header.lumps[LUMP_TEXINFO]);\n\t\tTRACE((\"Loading Faces\\n\"));\n\t\tnoerrors = noerrors && Mod_LoadFaces (mod, bspx, mod_base, &header.lumps[LUMP_FACES], &header.lumps[LUMP_LIGHTING], subbsp);\n\t}\n\tif (!isDedicated)\n\t{\n\t\tTRACE((\"Loading MarkSurfaces\\n\"));\n\t\tnoerrors = noerrors && Mod_LoadMarksurfaces (mod, mod_base, &header.lumps[LUMP_MARKSURFACES], subbsp);\n\t}\n\tif (noerrors)\n\t{\n\t\tTRACE((\"Loading Vis\\n\"));\n\t\tMod_LoadVisibility (mod, mod_base, &header.lumps[LUMP_VISIBILITY], vispatch.visptr, vispatch.vislen);\n\t}\n\tnoerrors = noerrors && Mod_LoadLeafs (mod, mod_base, &header.lumps[LUMP_LEAFS], subbsp, isnotmap, vispatch.leafptr, vispatch.leaflen);\n\tTRACE((\"Loading Nodes\\n\"));\n\tnoerrors = noerrors && Mod_LoadNodes (mod, mod_base, &header.lumps[LUMP_NODES], subbsp);\n\tTRACE((\"Loading Clipnodes\\n\"));\n\tnoerrors = noerrors && Mod_LoadClipnodes (mod, mod_base, &header.lumps[LUMP_CLIPNODES], subbsp, hexen2map);\n\tif (noerrors)\n\t{\n\t\tTRACE((\"Loading hull 0\\n\"));\n\t\tMod_MakeHull0 (mod);\n\t}\n\n\tTRACE((\"sorting shaders\\n\"));\n\tif (!isDedicated && noerrors)\n\t\tMod_SortShaders(mod);\n\n\tBZ_Free(vispatch.fileptr);\n\n\tif (!noerrors)\n\t{\n\t\treturn false;\n\t}\n\n\tTRACE((\"LoadBrushModel %i\\n\", __LINE__));\n\tQ1BSP_LoadBrushes(mod, bspx, mod_base);\n\tTRACE((\"LoadBrushModel %i\\n\", __LINE__));\n\n\tmod->numframes = 2;\t\t// regular and alternate animation\n\t\n\n//\n// set up the submodels (FIXME: this is confusing)\n//\n\n\tfor (j=0 ; j<2 ; j++)\n\t\tQ1BSP_CheckHullNodes(&mod->hulls[j]);\n\n\tfor (i=0, submod = mod; i<mod->numsubmodels ; i++)\n\t{\n\t\tbm = &mod->submodels[i];\n\n\t\tsubmod->rootnode = submod->nodes + bm->headnode[0];\n\t\tsubmod->hulls[0].firstclipnode = bm->headnode[0];\n\t\tsubmod->hulls[0].available = true;\n//\t\tQ1BSP_CheckHullNodes(&submod->hulls[0]);\n\nTRACE((\"LoadBrushModel %i\\n\", __LINE__));\n\t\tfor (j=1 ; j<MAX_MAP_HULLSM ; j++)\n\t\t{\n\t\t\tsubmod->hulls[j].firstclipnode = bm->headnode[j];\n\t\t\tsubmod->hulls[j].lastclipnode = submod->numclipnodes-1;\n\n\t\t\tsubmod->hulls[j].available &= bm->hullavailable[j];\n\t\t\tif (submod->hulls[j].firstclipnode > submod->hulls[j].lastclipnode)\n\t\t\t\tsubmod->hulls[j].available = false;\n\n//\t\t\tif (submod->hulls[j].available)\n//\t\t\t\tQ1BSP_CheckHullNodes(&submod->hulls[j]);\n\t\t}\n\n\t\tif (mod->fromgame == fg_halflife && i)\n\t\t{\n\t\t\tfor (j=bm->firstface ; j<bm->firstface+bm->numfaces ; j++)\n\t\t\t{\n\t\t\t\tif (mod->surfaces[j].flags & SURF_DRAWTURB)\n\t\t\t\t{\n\t\t\t\t\tfloat mid = bm->mins[2] + (0.5 * (bm->maxs[2] - bm->mins[2]));\n\t\t\t\t\tif (mod->surfaces[j].plane->type == PLANE_Z && mod->surfaces[j].plane->dist >= mid) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tmod->surfaces[j].flags |= SURF_NODRAW;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\tsubmod->firstmodelsurface = bm->firstface;\n\t\tsubmod->nummodelsurfaces = bm->numfaces;\n\t\t\n\t\tVectorCopy (bm->maxs, submod->maxs);\n\t\tVectorCopy (bm->mins, submod->mins);\n\n\t\tsubmod->radius = RadiusFromBounds (submod->mins, submod->maxs);\n\n\t\tsubmod->numclusters = (i==0)?bm->visleafs:0;\n\t\tsubmod->pvsbytes = ((submod->numclusters+31)>>3)&~3;\n\n\t\tif (i)\n\t\t{\n\t\t\tsubmod->entities_raw = NULL;\n\t\t\tsubmod->archive = NULL;\n\t\t}\n\n\t\tmemset(&submod->batches, 0, sizeof(submod->batches));\n\t\tsubmod->vbos = NULL;\n\t\tTRACE((\"LoadBrushModel %i\\n\", __LINE__));\n\t\tif (!isDedicated || using_rbe)\n\t\t{\n\t\t\tCOM_AddWork(WG_MAIN, ModBrush_LoadGLStuff, submod, NULL, i, 0);\n\t\t}\n\t\tTRACE((\"LoadBrushModel %i\\n\", __LINE__));\n\n\t\tsubmod->cnodes = NULL;\n\t\tQ1BSP_SetModelFuncs(submod);\n#ifdef Q2BSPS\n\t\tif (bm->brushes)\n\t\t{\n\t\t\tstruct bihleaf_s *leafs, *l;\n\t\t\tsize_t i;\n\t\t\tleafs = l = BZ_Malloc(sizeof(*leafs)*bm->numbrushes);\n\t\t\tfor (i = 0; i < bm->numbrushes; i++)\n\t\t\t{\n\t\t\t\tstruct q2cbrush_s *b = &bm->brushes[i];\n\t\t\t\tl->type = BIH_BRUSH;\n\t\t\t\tl->data.brush = b;\n\t\t\t\tl->data.contents = b->contents;\n\t\t\t\tVectorCopy(b->absmins, l->mins);\n\t\t\t\tVectorCopy(b->absmaxs, l->maxs);\n\t\t\t\tl++;\n\t\t\t}\n\t\t\tBIH_Build(submod, leafs, l-leafs);\n\t\t\tBZ_Free(leafs);\n\t\t}\n#endif\n\n\t\tif (i)\n\t\t\tCOM_AddWork(WG_MAIN, Mod_ModelLoaded, submod, NULL, MLS_LOADED, 0);\n\t\tif (i < submod->numsubmodels-1)\n\t\t{\t// duplicate the basic information\n\t\t\tchar\tname[MAX_QPATH];\n\t\t\tmodel_t *nextmod;\n\n\t\t\tQ_snprintfz (name, sizeof(name), \"*%i:%s\", i+1, mod->publicname);\n\t\t\tnextmod = Mod_FindName (name);\n\t\t\t*nextmod = *submod;\n\t\t\tnextmod->submodelof = mod;\n\t\t\tQ_strncpyz(nextmod->publicname, name, sizeof(nextmod->publicname));\n\t\t\tQ_snprintfz (nextmod->name, sizeof(nextmod->publicname), \"*%i:%s\", i+1, mod->publicname);\n\t\t\tsubmod = nextmod;\n\t\t\tmemset(&submod->memgroup, 0, sizeof(submod->memgroup));\n\t\t}\n\t\tTRACE((\"LoadBrushModel %i\\n\", __LINE__));\n\t}\nTRACE((\"LoadBrushModel %i\\n\", __LINE__));\n\tif (!isDedicated)\n\t\tMod_FixupMinsMaxs(mod);\nTRACE((\"LoadBrushModel %i\\n\", __LINE__));\n\n#ifdef TERRAIN\n\tmod->terrain = Mod_LoadTerrainInfo(mod, loadname, false);\n#endif\n\treturn true;\n}\n#endif\n\n/*\n==============================================================================\n\nSPRITES\n\n==============================================================================\n*/\n\n//=========================================================\n\n#ifdef SERVERONLY\n//dedicated servers should not need to load sprites.\n//dedicated servers need *.bsp to be loaded for setmodel to get the correct size (or all model types with sv_gameplayfix_setmodelrealbox).\n//otherwise other model types(actually: names) only need to be loaded once reflection or hitmodel is used.\n//for sprites we don't really care ever.\nqboolean QDECL Mod_LoadSpriteModel (model_t *mod, void *buffer, size_t fsize)\n{\n\tmod->type = mod_dummy;\n\treturn true;\n}\nqboolean QDECL Mod_LoadSprite2Model (model_t *mod, void *buffer, size_t fsize)\n{\n\treturn Mod_LoadSpriteModel(mod, buffer, fsize);\n}\nvoid Mod_LoadDoomSprite (model_t *mod)\n{\n\tmod->type = mod_dummy;\n}\n#else\n\n//we need to override the rtlight shader for sprites so they get lit properly ignoring n+s+t dirs\n//so lets split the shader into parts to avoid too many dupes\n#define SPRITE_SHADER_MAIN(extra)\t\t\t\\\n\t\t\t\"{\\n\"\t\t\t\t\t\t\t\t\t\t\t\\\n\t\t\t\t\"if gl_blendsprites\\n\"\t\t\t\t\t\t\\\n\t\t\t\t\t\"program defaultsprite\\n\"\t\t\t\t\\\n\t\t\t\t\"else\\n\"\t\t\t\t\t\t\t\t\t\\\n\t\t\t\t\t\"program defaultsprite#MASK=0.666\\n\"\t\\\n\t\t\t\t\"endif\\n\"\t\t\t\t\t\t\t\t\t\\\n\t\t\t\t\"{\\n\"\t\t\t\t\t\t\t\t\t\t\\\n\t\t\t\t\t\"map $diffuse\\n\"\t\t\t\t\t\t\\\n\t\t\t\t\t\"if gl_blendsprites == 2\\n\"\t\t\t\t\\\n\t\t\t\t\t\t\"blendfunc GL_ONE GL_ONE\\n\"\t\t\t\\\n\t\t\t\t\t\"elif gl_blendsprites\\n\"\t\t\t\t\\\n\t\t\t\t\t\t\"blendfunc GL_ONE GL_ONE_MINUS_SRC_ALPHA\\n\"\t\\\n\t\t\t\t\t\"else\\n\"\t\t\t\t\t\t\t\t\\\n\t\t\t\t\t\t\"alphafunc ge128\\n\"\t\t\t\t\t\\\n\t\t\t\t\t\t\"depthwrite\\n\"\t\t\t\t\t\t\\\n\t\t\t\t\t\"endif\\n\"\t\t\t\t\t\t\t\t\\\n\t\t\t\t\t\"rgbgen vertex\\n\"\t\t\t\t\t\t\\\n\t\t\t\t\t\"alphagen vertex\\n\"\t\t\t\t\t\t\\\n\t\t\t\t\"}\\n\"\t\t\t\t\t\t\t\t\t\t\\\n\t\t\t\t\"surfaceparm noshadows\\n\"\t\t\t\t\t\\\n\t\t\t\textra\t\t\t\t\t\t\t\t\t\t\\\n\t\t\t\"}\\n\"\n#define SPRITE_SHADER_UNLIT\tSPRITE_SHADER_MAIN(\t\t\t\\\n\t\t\t\t\"surfaceparm nodlight\\n\")\n#define SPRITE_SHADER_LIT\tSPRITE_SHADER_MAIN(\t\t\t\\\n\t\t\t\t\"sort seethrough\\n\"\t\t\t\t\t\t\\\n\t\t\t\t\"bemode rtlight\\n\"\t\t\t\t\t\t\\\n\t\t\t\t\"{\\n\"\t\t\t\t\t\t\t\t\t\\\n\t\t\t\t\t\"program rtlight#NOBUMP\\n\"\t\t\t\\\n\t\t\t\t\t\"{\\n\"\t\t\t\t\t\t\t\t\\\n\t\t\t\t\t\t\"map $diffuse\\n\"\t\t\t\t\\\n\t\t\t\t\t\t\"blendfunc add\\n\"\t\t\t\t\\\n\t\t\t\t\t\"}\\n\"\t\t\t\t\t\t\t\t\\\n\t\t\t\t\"}\\n\")\n\nvoid Mod_LoadSpriteFrameShader(model_t *spr, int frame, int subframe, mspriteframe_t *frameinfo)\n{\n#ifndef SERVERONLY\n\tchar *shadertext;\n\tchar name[MAX_QPATH];\n\tqboolean litsprite = false;\n\n\tif (qrenderer == QR_NONE)\n\t\treturn;\n\n\tif (subframe == -1)\n\t\tQ_snprintfz(name, sizeof(name), \"%s_%i.tga\", spr->name, frame);\n\telse\n\t\tQ_snprintfz(name, sizeof(name), \"%s_%i_%i.tga\", spr->name, frame, subframe);\n\n\tif (mod_litsprites_force.ival || strchr(spr->publicname, '!'))\n\t\tlitsprite = true;\n#ifdef HAVE_LEGACY\n\telse\n\t{\n\t\tint i;\n\t\t/*\n\t\tA quick note on tenebrae and sprites: In tenebrae, sprites are always additive, unless the light_lev field is set (which makes it fullbright).\n\t\tWhile its generally preferable and more consistent to assume lit sprites, this is incompatible with vanilla quake and thus unacceptable to us, but you can set the mod_assumelitsprites cvar if you want it.\n\t\tSo for better compatibility, we have a whitelist of 'well-known' sprites that tenebrae uses in this way, which we do lighting on.\n\t\tYou should still be able to use EF_FULLBRIGHT on these, but light_lev is an imprecise setting and will result in issues. Just be specific about fullbright or additive.\n\t\tDP on the other hand, supports lit sprites only when the sprite contains a ! in its name. We support that too.\n\t\t*/\n\t\tstatic char *forcelitsprites[] =\n\t\t{\n\t\t\t\"progs/smokepuff.spr\",\n\t\t\tNULL\n\t\t};\n\n\t\tfor (i = 0; forcelitsprites[i]; i++)\n\t\t\tif (!strcmp(spr->publicname, forcelitsprites[i]))\n\t\t\t{\n\t\t\t\tlitsprite = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t}\n#endif\n\n\tif (litsprite)\t// a ! in the filename makes it non-fullbright (and can also be lit by rtlights too).\n\t\tshadertext = SPRITE_SHADER_LIT;\n\telse\n\t\tshadertext = SPRITE_SHADER_UNLIT;\n\tframeinfo->lit = litsprite;\n\tframeinfo->shader = R_RegisterShader(name, SUF_NONE, shadertext);\n\tframeinfo->shader->defaulttextures->base = frameinfo->image;\n\tframeinfo->shader->width = frameinfo->right-frameinfo->left;\n\tframeinfo->shader->height = frameinfo->up-frameinfo->down;\n#endif\n}\nvoid Mod_LoadSpriteShaders(model_t *spr)\n{\n\tmsprite_t *psprite = spr->meshinfo;\n\tint i, j;\n\tmspritegroup_t *group;\n\n\tfor (i = 0; i < psprite->numframes; i++)\n\t{\n\t\tswitch (psprite->frames[i].type)\n\t\t{\n\t\tcase SPR_SINGLE:\n\t\t\tMod_LoadSpriteFrameShader(spr, i, -1, psprite->frames[i].frameptr);\n\t\t\tbreak;\n\t\tcase SPR_ANGLED:\n\t\tcase SPR_GROUP:\n\t\t\tgroup = (mspritegroup_t *)psprite->frames[i].frameptr;\n\t\t\tfor (j = 0; j < group->numframes; j++)\n\t\t\t\tMod_LoadSpriteFrameShader(spr, i, j, group->frames[j]);\n\t\t\tbreak;\n\t\t}\n\t}\n}\n\n#ifdef SPRMODELS\n/*\n=================\nMod_LoadSpriteFrame\n=================\n*/\nstatic void * Mod_LoadSpriteFrame (model_t *mod, void *pin, void *pend, mspriteframe_t **ppframe, int framenum, int subframe, int version, unsigned char *palette)\n{\n\tdspriteframe_t\t\t*pinframe;\n\tmspriteframe_t\t\t*pspriteframe;\n\tint\t\t\t\t\twidth, height, size, origin[2];\n\tchar\t\t\t\tname[64];\n\tuploadfmt_t\t\t\tlowresfmt;\n\tvoid\t\t\t\t*dataptr;\n\n\tpinframe = (dspriteframe_t *)pin;\n\n\twidth = LittleLong (pinframe->width);\n\theight = LittleLong (pinframe->height);\n\tsize = width * height;\n\n\tpspriteframe = ZG_Malloc(&mod->memgroup, sizeof (mspriteframe_t));\n\n\tQ_memset (pspriteframe, 0, sizeof (mspriteframe_t));\n\n\t*ppframe = pspriteframe;\n\n\torigin[0] = LittleLong (pinframe->origin[0]);\n\torigin[1] = LittleLong (pinframe->origin[1]);\n\n\tpspriteframe->up = origin[1];\n\tpspriteframe->down = origin[1] - height;\n\tpspriteframe->left = origin[0];\n\tpspriteframe->right = width + origin[0];\n\tpspriteframe->xmirror = false;\n\n\tdataptr = (pinframe + 1);\n\n\tif (version == SPRITE32_VERSION)\n\t{\n\t\tsize *= 4;\n\t\tlowresfmt = TF_RGBA32;\n\t}\n\telse if (version == SPRITEHL_VERSION)\n\t\tlowresfmt = TF_8PAL32;\n\telse\n\t\tlowresfmt = TF_TRANS8;\n\n\tif ((qbyte*)dataptr + size > (qbyte*)pend)\n\t{\n\t\t//tenebrae has a couple of dodgy truncated sprites. yay for replacement textures.\n\t\tdataptr = NULL;\n\t\tlowresfmt = TF_INVALID;\n\t}\n\n\tif (subframe == -1)\n\t\tQ_snprintfz(name, sizeof(name), \"%s_%i.tga\", mod->name, framenum);\n\telse\n\t\tQ_snprintfz(name, sizeof(name), \"%s_%i_%i.tga\", mod->name, framenum, subframe);\n\tpspriteframe->image = Image_GetTexture(name, \"sprites\", IF_NOMIPMAP|IF_NOGAMMA|IF_CLAMP|IF_PREMULTIPLYALPHA, dataptr, palette, width, height, lowresfmt);\n\n\treturn (void *)((qbyte *)(pinframe+1) + size);\n}\n\n\n/*\n=================\nMod_LoadSpriteGroup\n=================\n*/\nstatic void * Mod_LoadSpriteGroup (model_t *mod, void * pin, void *pend, mspriteframe_t **ppframe, int framenum, int version, unsigned char *palette)\n{\n\tdspritegroup_t\t\t*pingroup;\n\tmspritegroup_t\t\t*pspritegroup;\n\tint\t\t\t\t\ti, numframes;\n\tdspriteinterval_t\t*pin_intervals;\n\tfloat\t\t\t\t*poutintervals;\n\tvoid\t\t\t\t*ptemp;\n\tfloat\t\t\t\tprevtime;\n\n\tpingroup = (dspritegroup_t *)pin;\n\n\tnumframes = LittleLong (pingroup->numframes);\n\tif (numframes <= 0)\n\t{\n\t\tCon_Printf (CON_ERROR \"Mod_LoadSpriteGroup: invalid frame count\\n\");\n\t\treturn NULL;\n\t}\n\n\tpspritegroup = ZG_Malloc(&mod->memgroup, sizeof (mspritegroup_t) + (numframes - 1) * sizeof (pspritegroup->frames[0]));\n\n\tpspritegroup->numframes = numframes;\n\n\t*ppframe = (mspriteframe_t *)pspritegroup;\n\n\tpin_intervals = (dspriteinterval_t *)(pingroup + 1);\n\n\tpoutintervals = ZG_Malloc(&mod->memgroup, numframes * sizeof (float));\n\n\tpspritegroup->intervals = poutintervals;\n\n\tfor (i=0, prevtime=0 ; i<numframes ; i++)\n\t{\n\t\t*poutintervals = LittleFloat (pin_intervals->interval);\n\t\tif (*poutintervals <= 0.0)\n\t\t{\n\t\t\tCon_Printf (CON_ERROR \"Mod_LoadSpriteGroup: interval<=0\\n\");\n\t\t\treturn NULL;\n\t\t}\n\t\tprevtime = *poutintervals = prevtime+*poutintervals;\n\n\t\tpoutintervals++;\n\t\tpin_intervals++;\n\t}\n\n\tptemp = (void *)pin_intervals;\n\n\tfor (i=0 ; i<numframes ; i++)\n\t{\n\t\tptemp = Mod_LoadSpriteFrame (mod, ptemp, pend, &pspritegroup->frames[i], framenum, i, version, palette);\n\t}\n\n\treturn ptemp;\n}\n\n/*\n=================\nMod_LoadSpriteModel\n=================\n*/\nqboolean QDECL Mod_LoadSpriteModel (model_t *mod, void *buffer, size_t fsize)\n{\n\tint\t\t\t\t\ti;\n\tint\t\t\t\t\tversion;\n\tdsprite_t\t\t\t*pin;\n\tmsprite_t\t\t\t*psprite;\n\tint\t\t\t\t\tnumframes;\n\tint\t\t\t\t\tsize;\n\tdspriteframetype_t\t*pframetype;\n\tint rendertype=SPRHL_ALPHATEST;\n\tunsigned char pal[256*4];\n\tint sptype;\n\t\n\tpin = (dsprite_t *)buffer;\n\n\tversion = LittleLong (pin->version);\n\tif (version != SPRITE_VERSION)\n\tif (version != SPRITE32_VERSION)\n\tif (version != SPRITEHL_VERSION)\n\t{\n\t\tCon_Printf (CON_ERROR \"%s has wrong version number \"\n\t\t\t\t \"(%i should be %i)\\n\", mod->name, version, SPRITE_VERSION);\n\t\treturn false;\n\t}\n\n\tsptype = LittleLong (pin->type);\n\n\tif (LittleLong(pin->version) == SPRITEHL_VERSION)\n\t{\n\t\tpin = (dsprite_t*)((char*)pin + 4);\n\t\trendertype = LittleLong (pin->type);\t//not sure what the values mean.\n\t}\n\n\tnumframes = LittleLong (pin->numframes);\n\n\tsize = sizeof (msprite_t) +\t(numframes - 1) * sizeof (psprite->frames);\n\n\tpsprite = ZG_Malloc(&mod->memgroup, size);\n\n\tmod->meshinfo = psprite;\n\tswitch(sptype)\n\t{\n\tcase SPR_ORIENTED:\n\t\tif (r_sprite_backfacing.ival)\n\t\t\tsptype = SPR_ORIENTED_BACKFACE;\n\t\tbreak;\n\tcase SPR_VP_PARALLEL_UPRIGHT:\n\tcase SPR_FACING_UPRIGHT:\n\tcase SPR_VP_PARALLEL:\n\tcase SPR_VP_PARALLEL_ORIENTED:\n//\tcase SPRDP_LABEL:\n//\tcase SPRDP_LABEL_SCALE:\n//\tcase SPRDP_OVERHEAD:\n\t\tbreak;\n\tdefault:\n\t\tCon_DPrintf(CON_ERROR \"%s has unsupported sprite type %i\\n\", mod->name, sptype);\n\t\tsptype = SPR_VP_PARALLEL;\n\t\tbreak;\n\t}\n\tpsprite->type = sptype;\n\n\tpsprite->maxwidth = LittleLong (pin->width);\n\tpsprite->maxheight = LittleLong (pin->height);\n\tpsprite->beamlength = LittleFloat (pin->beamlength);\n\tmod->synctype = LittleLong (pin->synctype);\n\tpsprite->numframes = numframes;\n\n\tmod->mins[0] = mod->mins[1] = -psprite->maxwidth/2;\n\tmod->maxs[0] = mod->maxs[1] = psprite->maxwidth/2;\n\tmod->mins[2] = -psprite->maxheight/2;\n\tmod->maxs[2] = psprite->maxheight/2;\n\tif (qrenderer == QR_NONE)\n\t{\n\t\tmod->type = mod_dummy;\n\t\treturn true;\n\t}\n\n\tif (version == SPRITEHL_VERSION)\n\t{\n\t\tint i;\n\t\tshort *numi = (short*)(pin+1);\n\t\tunsigned char *src = (unsigned char *)(numi+1);\n\t\tif (LittleShort(*numi) != 256)\n\t\t{\n\t\t\tCon_Printf(CON_ERROR \"%s has wrong number of palette indexes (we only support 256)\\n\", mod->name);\n\t\t\treturn false;\n\t\t}\n\n\t\tif (rendertype == SPRHL_INDEXALPHA)\n\t\t{\n\t\t\t/* alpha value is equivalent to palette index - eukara */\n\t\t\tfor (i = 0; i < 256; i++)\n\t\t\t{\n\t\t\t\tpal[i*4+0] = *src++;\n\t\t\t\tpal[i*4+1] = *src++;\n\t\t\t\tpal[i*4+2] = *src++;\n\t\t\t\tpal[i*4+3] = i;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tfor (i = 0; i < 256; i++)\n\t\t\t{//FIXME: bgr?\n\t\t\t\tpal[i*4+0] = *src++;\n\t\t\t\tpal[i*4+1] = *src++;\n\t\t\t\tpal[i*4+2] = *src++;\n\t\t\t\tpal[i*4+3] = 255;\n\t\t\t}\n\t\t\tif (rendertype == SPRHL_ALPHATEST)\n\t\t\t{\n\t\t\t\tpal[255*4+0] = 0;\n\t\t\t\tpal[255*4+1] = 0;\n\t\t\t\tpal[255*4+2] = 0;\n\t\t\t\tpal[255*4+3] = 0;\n\t\t\t}\n\t\t}\n\n\t\tpframetype = (dspriteframetype_t *)(src);\n\t}\n\telse\n\t\tpframetype = (dspriteframetype_t *)(pin + 1);\n\n//\n// load the frames\n//\n\tif (numframes < 1)\n\t{\n\t\tCon_Printf (CON_ERROR \"Mod_LoadSpriteModel: Invalid # of frames: %d\\n\", numframes);\n\t\treturn false;\n\t}\n\n\tmod->numframes = numframes;\n\n\tfor (i=0 ; i<numframes ; i++)\n\t{\n\t\tspriteframetype_t\tframetype;\n\n\t\tframetype = LittleLong (pframetype->type);\n\t\tpsprite->frames[i].type = frametype;\n\n\t\tif (frametype == SPR_SINGLE)\n\t\t{\n\t\t\tpframetype = (dspriteframetype_t *)\n\t\t\t\t\tMod_LoadSpriteFrame (mod, pframetype + 1, (qbyte*)buffer + fsize,\n\t\t\t\t\t\t\t\t\t\t &psprite->frames[i].frameptr, i, -1, version, pal);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tpframetype = (dspriteframetype_t *)\n\t\t\t\t\tMod_LoadSpriteGroup (mod, pframetype + 1, (qbyte*)buffer + fsize,\n\t\t\t\t\t\t\t\t\t\t &psprite->frames[i].frameptr, i, version, pal);\n\t\t}\n\t\tif (pframetype == NULL)\n\t\t{\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tmod->type = mod_sprite;\n\n\treturn true;\n}\n#endif\n\n#ifdef SP2MODELS\nqboolean QDECL Mod_LoadSprite2Model (model_t *mod, void *buffer, size_t fsize)\n{\n\tint\t\t\t\t\ti;\n\tint\t\t\t\t\tversion;\n\tdmd2sprite_t\t\t*pin;\n\tmsprite_t\t\t\t*psprite;\n\tint\t\t\t\t\tnumframes;\n\tint\t\t\t\t\tsize;\n\tdmd2sprframe_t\t\t*pframetype;\n\tmspriteframe_t\t\t*frame;\n\tint w, h;\n\tfloat origin[2];\n\n\t\n\tpin = (dmd2sprite_t *)buffer;\n\n\tversion = LittleLong (pin->version);\n\tif (version != SPRITE2_VERSION)\n\t{\n\t\tCon_Printf (CON_ERROR \"%s has wrong version number \"\n\t\t\t\t \"(%i should be %i)\", mod->name, version, SPRITE2_VERSION);\n\t\treturn false;\n\t}\n\n\tnumframes = LittleLong (pin->numframes);\n\n\tsize = sizeof (msprite_t) +\t(numframes - 1) * sizeof (psprite->frames);\n\n\tpsprite = ZG_Malloc(&mod->memgroup, size);\n\n\tmod->meshinfo = psprite;\n\n\tpsprite->type = SPR_VP_PARALLEL;\n\tpsprite->maxwidth = 1;\n\tpsprite->maxheight = 1;\n\tpsprite->beamlength = 1;\n\tmod->synctype = 0;\n\tpsprite->numframes = numframes;\n\n\tmod->mins[0] = mod->mins[1] = -psprite->maxwidth/2;\n\tmod->maxs[0] = mod->maxs[1] = psprite->maxwidth/2;\n\tmod->mins[2] = -psprite->maxheight/2;\n\tmod->maxs[2] = psprite->maxheight/2;\n\t\n//\n// load the frames\n//\n\tif (numframes < 1)\n\t{\n\t\tCon_Printf (CON_ERROR \"Mod_LoadSpriteModel: Invalid # of frames: %d\\n\", numframes);\n\t\treturn false;\n\t}\n\n\tmod->numframes = numframes;\n\n\tpframetype = pin->frames;\n\n\tfor (i=0 ; i<numframes ; i++)\n\t{\n\t\tspriteframetype_t\tframetype;\n\n\t\tframetype = SPR_SINGLE;\n\t\tpsprite->frames[i].type = frametype;\n\n\t\tframe = psprite->frames[i].frameptr = ZG_Malloc(&mod->memgroup, sizeof(mspriteframe_t));\n\n\t\tframe->image = Image_GetTexture(pframetype->name, NULL, IF_NOMIPMAP|IF_NOGAMMA|IF_CLAMP, NULL, NULL, 0, 0, TF_INVALID);\n\n\t\tw = LittleLong(pframetype->width);\n\t\th = LittleLong(pframetype->height);\n\t\torigin[0] = LittleLong (pframetype->origin_x);\n\t\torigin[1] = LittleLong (pframetype->origin_y);\n\n\t\tframe->down = -origin[1];\n\t\tframe->up = h - origin[1];\n\t\tframe->left = -origin[0];\n\t\tframe->right = w - origin[0];\n\t\tframe->xmirror = false;\n\n\t\tpframetype++;\n\t}\n\n\tmod->type = mod_sprite;\n\n\treturn true;\n}\n#endif\n\n#ifdef DSPMODELS\n\ntypedef struct {\n\tshort width;\n\tshort height;\n\tshort xpos;\n\tshort ypos;\n} doomimage_t;\nstatic int QDECL FindDoomSprites(const char *name, qofs_t size, time_t mtime, void *param, searchpathfuncs_t *spath)\n{\n\tif (*(int *)param + strlen(name)+1 > 16000)\n\t\tSys_Error(\"Too many doom sprites\\n\");\n\n\tstrcpy((char *)param + *(int *)param, name);\n\t*(int *)param += strlen(name)+1;\n\n\treturn true;\n}\n\n\nstatic void LoadDoomSpriteFrame(model_t *mod, mspriteframe_t frame, mspriteframedesc_t *pdesc, int anglenum)\n{\n\tmspriteframe_t *pframe;\n\n\tif (!anglenum)\n\t{\n\t\tpdesc->type = SPR_SINGLE;\n\t\tpdesc->frameptr = pframe = ZG_Malloc(&mod->memgroup, sizeof(*pframe));\n\t}\n\telse\n\t{\n\t\tmspritegroup_t *group;\n\n\t\tif (!pdesc->frameptr || pdesc->type != SPR_ANGLED)\n\t\t{\n\t\t\tpdesc->type = SPR_ANGLED;\n\t\t\tgroup = ZG_Malloc(&mod->memgroup, sizeof(*group)+sizeof(mspriteframe_t *)*(8-1));\n\t\t\tpdesc->frameptr = (mspriteframe_t *)group;\n\t\t\tgroup->numframes = 8;\n\t\t}\n\t\telse\n\t\t\tgroup = (mspritegroup_t *)pdesc->frameptr;\n\n\t\tpframe = ZG_Malloc(&mod->memgroup, sizeof(*pframe));\n\t\tgroup->frames[anglenum-1] = pframe;\n\t}\n\n\t*pframe = frame;\n}\n\n/*\n=================\nDoom Sprites\n=================\n*/\nvoid Mod_LoadDoomSprite (model_t *mod)\n{\n\tchar files[16384];\n\tchar basename[MAX_QPATH];\n\tint baselen;\n\tchar *name;\n\n\tint numframes=0;\n\tint ofs;\n\n\tint size;\n\n\tint elements=0;\n\n\tint framenum;\n\tint anglenum;\n\n\tmsprite_t *psprite;\n\tunsigned int image[256*256];\n\tsize_t fsize;\n\tqbyte palette[256*4];\n\tdoomimage_t *header;\n\tqbyte *coldata, fr, rc;\n\tmspriteframe_t frame;\n\tsize_t c;\n\tunsigned int *colpointers;\n\tvec3_t t;\n\n\tmod->type = mod_dummy;\n\n\n\tCOM_StripExtension(mod->name, basename, sizeof(basename));\n\tbaselen = strlen(basename);\n\tstrcat(basename, \"*\");\n\t*(int *)files=4;\n\tCOM_EnumerateFiles(basename, FindDoomSprites, files);\n\n\t//find maxframes and validate the rest.\n\tfor (ofs = 4; ofs < *(int*)files; ofs+=strlen(files+ofs)+1)\n\t{\n\t\tname = files+ofs+baselen;\n\n\t\tif (!*name)\n\t\t\tHost_Error(\"Doom sprite componant lacks frame name\");\n\t\tif (*name - 'a'+1 > numframes)\n\t\t\tnumframes = *name - 'a'+1;\n\t\tif (name[1] < '0' || name[1] > '8')\n\t\t\tHost_Error(\"Doom sprite componant has bad angle number\");\n\t\tif (name[1] == '0')\n\t\t\telements+=8;\n\t\telse\n\t\t\telements++;\n\t\tif (name[2])\t//is there a second element?\n\t\t{\n\t\t\tif (name[2] - 'a'+1 > numframes)\n\t\t\t\tnumframes = name[2] - 'a'+1;\n\t\t\tif (name[3] < '0' || name[3] > '8')\n\t\t\t\tHost_Error(\"Doom sprite componant has bad angle number\");\n\n\t\t\tif (name[3] == '0')\n\t\t\t\telements+=8;\n\t\t\telse\n\t\t\t\telements++;\n\t\t}\n\t}\n\tif (elements != numframes*8)\n\t\tCon_Printf(\"Doom sprite %s has wrong componant count\", mod->name);\n\telse if (numframes)\n\t{\n\t\tsize = sizeof (msprite_t) +\t(elements - 1) * sizeof (psprite->frames);\n\t\tpsprite = ZG_Malloc(&mod->memgroup, size);\n\n\t\tpsprite->numframes = numframes;\n\n\t\tmemset(&frame, 0, sizeof(frame));\n\t\tcoldata = FS_LoadMallocFile(\"wad/playpal\", &fsize);\n\t\tif (coldata && fsize >= 256*3)\n\t\t{\t//expand to 32bit.\n\t\t\tfor (ofs = 0; ofs < 256; ofs++)\n\t\t\t{\n\t\t\t\tpalette[ofs*4+0] = coldata[ofs*3+0];\n\t\t\t\tpalette[ofs*4+1] = coldata[ofs*3+1];\n\t\t\t\tpalette[ofs*4+2] = coldata[ofs*3+2];\n\t\t\t\tpalette[ofs*4+3] = 255;\n\t\t\t}\n\t\t}\n\t\tZ_Free(coldata);\n\n\t\tClearBounds(mod->mins, mod->maxs);\n\n\t\t//do the actual loading.\n\t\tfor (ofs = 4; ofs < *(int*)files; ofs+=strlen(files+ofs)+1)\n\t\t{\n\t\t\tname = files+ofs;\n\n\t\t\theader = (doomimage_t *)FS_LoadMallocFile(name, &fsize);\n\n\t\t\t//the 5 is because doom likes drawing sprites slightly downwards, in the floor.\n\t\t\tframe.up = header->ypos + 5;\n\t\t\tframe.down = header->ypos-header->height + 5;\n\t\t\tframe.left = -header->xpos;\n\t\t\tframe.right = header->width - header->xpos;\n\n\t\t\tt[0] = t[1] = max(fabs(frame.left),fabs(frame.right));\n\t\t\tt[2] = frame.up;\n\t\t\tAddPointToBounds(t, mod->mins, mod->maxs);\n\t\t\tt[0] *= -1;\n\t\t\tt[1] *= -1;\n\t\t\tt[2] = frame.down;\n\n\t\t\tif (header->width*header->height <= sizeof(image))\n\t\t\t{\n\t\t\t\t//anything not written will be transparent.\n\t\t\t\tmemset(image, 0, header->width*header->height*4);\n\t\t\t\tcolpointers = (unsigned int*)(header+1);\n\t\t\t\tfor (c = 0; c < header->width; c++)\n\t\t\t\t{\n\t\t\t\t\tcoldata = (qbyte *)header + colpointers[c];\n\t\t\t\t\twhile(1)\n\t\t\t\t\t{\n\t\t\t\t\t\tfr = *coldata++;\n\t\t\t\t\t\tif (fr == 255)\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\trc = *coldata++;\n\t\t\t\t\t\tcoldata++;\n\t\t\t\t\t\tif ((fr+rc) > header->height)\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\twhile(rc)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\timage[c + fr*header->width] = ((unsigned int*)palette)[*coldata++];\n\t\t\t\t\t\t\tfr++;\n\t\t\t\t\t\t\trc--;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcoldata++;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tframe.image = Image_GetTexture(name, NULL, IF_CLAMP|IF_NOREPLACE, image, palette, header->width, header->height, TF_RGBA32);\n\t\t\t\tZ_Free(header);\n\t\t\t}\n\n\n\t\t\tframenum = name[baselen+0] - 'a';\n\t\t\tanglenum = name[baselen+1] - '0';\n\t\t\tframe.xmirror = false;\n\t\t\tLoadDoomSpriteFrame(mod, frame, &psprite->frames[framenum], anglenum);\n\n\t\t\tif (name[baselen+2])\t//is there a second element?\n\t\t\t{\n\t\t\t\tframenum = name[baselen+2] - 'a';\n\t\t\t\tanglenum = name[baselen+3] - '0';\n\t\t\t\tframe.xmirror = true;\n\t\t\t\tLoadDoomSpriteFrame(mod, frame, &psprite->frames[framenum], anglenum);\n\t\t\t}\n\t\t}\n\n\n\t\tpsprite->type = SPR_FACING_UPRIGHT;\n\t\tmod->numframes = numframes;\n\t\tmod->type = mod_sprite;\n\n\t\tmod->meshinfo = psprite;\n\t}\n}\n#endif\n\n#endif\n\n//=============================================================================\n\n/*\n================\nMod_Print\n================\n*/\nvoid Mod_Print (void)\n{\n\tint\t\ti;\n\tmodel_t\t*mod;\n\n\tCon_Printf (\"Cached models:\\n\");\n\tfor (i=0, mod=mod_known ; i < mod_numknown ; i++, mod++)\n\t{\n\t\tCon_Printf (\"%8p : %s\\n\", mod->meshinfo, mod->name);\n\t}\n}\n\n\n#endif\n"
  },
  {
    "path": "engine/gl/gl_model.h",
    "content": "/*\nCopyright (C) 1996-1997 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n*/\n\n#ifndef __MODEL__\n#define __MODEL__\n\n#include \"../client/modelgen.h\"\n#include \"../client/spritegn.h\"\n\nstruct hull_s;\nstruct trace_s;\nstruct wedict_s;\nstruct model_s;\nstruct world_s;\nstruct dlight_s;\ntypedef struct builddata_s builddata_t;\ntypedef struct bspx_header_s bspx_header_t;\n\ntypedef enum {\n\tSHADER_SORT_NONE,\n\tSHADER_SORT_RIPPLE,\t\t\t//new\n\tSHADER_SORT_DEFERREDLIGHT,\t//new\n\tSHADER_SORT_PORTAL,\n\tSHADER_SORT_SKY,\t\t\t//aka environment\n\tSHADER_SORT_OPAQUE,\n\t//fixme: occlusion tests\n\tSHADER_SORT_DECAL,\n\tSHADER_SORT_SEETHROUGH,\n\t//then rtlights are drawn\n\tSHADER_SORT_UNLITDECAL,\t\t//new\n\tSHADER_SORT_BANNER,\n\t//fog\n\tSHADER_SORT_UNDERWATER,\n\tSHADER_SORT_BLEND,\n\tSHADER_SORT_ADDITIVE,\n\t//blend2,3,6\n\t//stencilshadow\n\t//almostnearest\n\tSHADER_SORT_NEAREST,\n\n\n\tSHADER_SORT_COUNT\n} shadersort_t;\n\n#ifdef FTE_TARGET_WEB\n#define MAX_BONES 256\n#elif defined(IQMTOOL)\n#define MAX_BONES 8192\n#else\n#define MAX_BONES 256\t//Note: there's lots of bone data allocated on the stack, so don't bump recklessly.\n#endif\n#if MAX_BONES>65536\n#define GL_BONE_INDEX_TYPE GL_UNSIGNED_INT\ntypedef unsigned int boneidx_t;\n#elif MAX_BONES>256\n#define GL_BONE_INDEX_TYPE GL_UNSIGNED_SHORT\ntypedef unsigned short boneidx_t;\n#else\n#define GL_BONE_INDEX_TYPE GL_UNSIGNED_BYTE\ntypedef unsigned char boneidx_t;\n#endif\ntypedef boneidx_t bone_vec4_t[4];\n\nstruct doll_s;\nvoid rag_uninstanciateall(void);\nvoid rag_flushdolls(qboolean force);\nvoid rag_freedoll(struct doll_s *doll);\nstruct doll_s *rag_createdollfromstring(struct model_s *mod, const char *fname, int numbones, const char *file);\nstruct world_s;\nvoid rag_doallanimations(struct world_s *world);\nvoid rag_removedeltaent(lerpents_t *le);\nvoid rag_updatedeltaent(struct world_s *w, entity_t *ent, lerpents_t *le);\nvoid rag_lerpdeltaent(lerpents_t *le, unsigned int bonecount, short *newstate, float frac, short *oldstate);\nvoid skel_reset(struct world_s *world);\n\ntypedef struct mesh_s\n{\n\tint\t\t\t\tnumvertexes;\n\tint\t\t\t\tnumindexes;\n\n\t/*position within its vbo*/\n\tunsigned int\tvbofirstvert;\n\tunsigned int\tvbofirstelement;\n\n\t/*\n\tFIXME: move most of this stuff out into a vbo struct\n\t*/\n\n\tfloat\t\t\txyz_blendw[2];\n\n\t/*arrays used for rendering*/\n\tvecV_t\t\t\t*xyz_array;\n\tvecV_t\t\t\t*xyz2_array;\n\tvec3_t\t\t\t*normals_array;\t/*required for lighting*/\n\tvec3_t\t\t\t*snormals_array;/*required for rtlighting*/\n\tvec3_t\t\t\t*tnormals_array;/*required for rtlighting*/\n\tvec2_t\t\t\t*st_array;\t\t/*texture coords*/\n\tvec2_t\t\t\t*lmst_array[MAXRLIGHTMAPS];\t/*second texturecoord set (merely dubbed lightmap, one for each potential lightstyle)*/\n\tavec4_t\t\t\t*colors4f_array[MAXRLIGHTMAPS];/*floating point colours array*/\n\tbyte_vec4_t\t\t*colors4b_array;/*byte colours array*/\n\n    index_t\t\t\t*indexes;\n\n\t//required for shadow volumes\n\tint\t\t\t\t*trneighbors;\n\tvec3_t\t\t\t*trnormals;\n\n\tqboolean\t\tistrifan;\t/*if its a fan/poly/single quad  (permits optimisations)*/\n\tconst float\t\t*bones;\n\tint\t\t\t\tnumbones;\n\tbone_vec4_t\t\t*bonenums;\n\tvec4_t\t\t\t*boneweights;\n} mesh_t;\n\n/*\nbatches are generated for each shader/ent as required.\nonce a batch is known to the backend for that frame, its shader, vbo, ent, lightmap, textures may not be changed until the frame has finished rendering. This is to potentially permit caching.\n*/\ntypedef struct batch_s\n{\n\tmesh_t **mesh; /*list must be long enough for all surfaces that will form part of this batch times two, for mirrors/portals*/\n\tstruct batch_s *next;\n\tunsigned int meshes;\n\tunsigned int firstmesh;\n\n\tshader_t *shader;\n\tstruct vbo_s *vbo;\n\tentity_t *ent;\t/*used for shader properties*/\n\tstruct mfog_s *fog;\n\timage_t\t\t*envmap;\n\n\tshort lightmap[MAXRLIGHTMAPS];\t/*used for shader lightmap textures*/\n\tlightstyleindex_t lmlightstyle[MAXRLIGHTMAPS];\n\tunsigned char vtlightstyle[MAXRLIGHTMAPS];\n\n\tunsigned int maxmeshes;\t/*not used by backend*/\n\tunsigned int flags;\t/*backend flags (force transparency etc)*/\n\tstruct texture_s *texture; /*is this used by the backend?*/\n\tstruct texnums_s *skin;\n\n\tvoid (*buildmeshes)(struct batch_s *b);\n#if R_MAX_RECURSE > 2\n\tunsigned int recursefirst[R_MAX_RECURSE-2];\t//fixme: should thih, firstmesh, and meshes be made ushorts?\n#endif\n\t/*caller-use, not interpreted by backend*/\n\tunion\n\t{\n\t\tstruct\n\t\t{\n\t\t\tunsigned int ebobatch;\t\t//temporal scene cache stuff, basically just a simple index so we don't have to deal with shader sort values when generating new index lists.\n\t\t\tunsigned int shadowbatch;\t//a unique index to accelerate shadowmesh generation (dlights, yay!)\n//\t\t} bmodel;\n//\t\tstruct\n//\t\t{\n\t\t\tvec4_t plane;\t/*used only at load (for portal surfaces, so multiple planes are not part of the same batch)*/\n\t\t} bmodel;\t//bmodel surfaces.\n\t\tstruct\n\t\t{\n\t\t\tunsigned int lightidx;\n\t\t\tunsigned int lightmode;\n\t\t} dlight;\t//deferred light batches\n\t\tstruct\n\t\t{\n\t\t\tunsigned short surfrefs[sizeof(mesh_t)/sizeof(unsigned short)];\t//for hlmdl batching...\n\t\t} alias;\n\t\tstruct\n\t\t{\n\t\t\tunsigned int surface;\n\t\t} poly;\n\t/*\tstruct\n\t\t{\n\t\t\tunsigned int first;\n\t\t\tunsigned int count;\n\t\t} surf;*/\n\t\tstruct\n\t\t{\n\t\t\tunsigned int ebobatch;\t\t//temporal scene cache stuff, basically just a simple index so we don't have to deal with shader sort values when generating new index lists.\n\t\t\tmesh_t meshbuf;\n\t\t\tmesh_t *meshptr;\n\t\t};\n\t} user;\n} batch_t;\n/*\n\nd*_t structures are on-disk representations\nm*_t structures are in-memory\n\n*/\n\n// entity effects\n\n#define\tEF_BRIGHTFIELD\t\t\t(1<<0)\n#define\tEF_MUZZLEFLASH \t\t\t(1<<1)\n#define\tEF_BRIGHTLIGHT \t\t\t(1<<2)\n#define\tEF_DIMLIGHT \t\t\t(1<<3)\n#define\tQWEF_FLAG1\t \t\t\t(1<<4)\t//only applies to qw player entities\n#define\tNQEF_NODRAW\t\t\t\t(1<<4)\t//so packet entities are free to get this instead\n#define REEF_QUADLIGHT\t\t\t(1<<4)\n#define TENEBRAEEF_FULLDYNAMIC\t(1<<4)\n#define\tQWEF_FLAG2\t \t\t\t(1<<5)\t//only applies to qw player entities\n#define\tNQEF_ADDITIVE\t\t\t(1<<5)\t//so packet entities are free to get this instead\n#define REEF_PENTLIGHT\t\t\t(1<<5)\n#define TENEBRAEEF_GREEN\t\t(1<<5)\n#define\tEF_BLUE\t\t\t\t\t(1<<6)\n#define REEF_CANDLELIGHT\t\t(1<<6)\n#define\tEF_RED\t\t\t\t\t(1<<7)\n#define\tH2EF_NODRAW\t\t\t\t(1<<7)\t//this is going to get complicated... emulated server side.\n#define\tDPEF_NOGUNBOB\t\t\t(1<<8)\t//viewmodel attachment does not bob. only applies to viewmodelforclient/RF_WEAPONMODEL\n#define\tEF_FULLBRIGHT\t\t\t(1<<9)\t//abslight=1\n#define\tDPEF_FLAME\t\t\t\t(1<<10)\t//'onfire'\n#define\tDPEF_STARDUST\t\t\t(1<<11)\t//'showering sparks'\n#define\tEF_NOSHADOW\t\t \t\t(1<<12)\t//doesn't cast a shadow\n#define\tEF_NODEPTHTEST\t\t\t(1<<13)\t//shows through walls.\n#define\t\tDPEF_SELECTABLE_\t\t(1<<14)\t//highlights when prydoncursored\n#define\t\tDPEF_DOUBLESIDED_\t\t(1<<15)\t//disables culling\n#define\t\tDPEF_NOSELFSHADOW_\t\t(1<<16)\t//doesn't cast shadows on any noselfshadow entities.\n#define\t\tDPEF_DYNAMICMODELLIGHT_\t(1<<17)\t//forces dynamic lights... I have no idea what this is actually needed for.\n#define\tEF_GREEN\t\t\t\t(1<<18)\n#define\tEF_UNUSED19\t\t\t\t(1<<19)\n#define\tEF_RESTARTANIM_BIT\t\t(1<<20)\t//restarts the anim when toggled between states\n#define\tEF_TELEPORT_BIT\t\t\t(1<<21)\t//disable lerping when toggled between states\n#define DPEF_LOWPRECISION\t\t(1<<22) //part of the protocol/server, not the client itself.\n#define EF_NOMODELFLAGS\t\t\t(1<<23)\n#define EF_MF_ROCKET\t\t\t(1<<24)\n#define EF_MF_GRENADE\t\t\t(1<<25)\n#define EF_MF_GIB\t\t\t\t(1<<26)\n#define EF_MF_ROTATE\t\t\t(1<<27)\n#define EF_MF_TRACER\t\t\t(1<<28)\n#define EF_MF_ZOMGIB\t\t\t(1u<<29)\n#define EF_MF_TRACER2\t\t\t(1u<<30)\n#define EF_MF_TRACER3\t\t\t(1u<<31)\n\n#define EF_HASPARTICLETRAIL\t\t(0xff800000 | EF_BRIGHTFIELD|DPEF_FLAME|DPEF_STARDUST)\n\n/*\n==============================================================================\n\nBRUSH MODELS\n\n==============================================================================\n*/\n\nstruct mnode_s;\n\ntypedef struct\n{\n\tqbyte *buffer;\t//reallocated if needed.\n\tsize_t buffersize;\n} pvsbuffer_t;\ntypedef enum\n{\n\tPVM_FAST,\n\tPVM_MERGE,\t//merge the pvs bits into the provided buffer\n\tPVM_REPLACE,//return value is guarenteed to be the provided buffer.\n} pvsmerge_t;\n\ntypedef struct {\n\t//model is being purged from memory.\n\tvoid (*PurgeModel) (struct model_s *mod);\n\n\tunsigned int (*PointContents)\t(struct model_s *model, const vec3_t axis[3], const vec3_t p);\n//\tunsigned int (*BoxContents)\t\t(struct model_s *model, int hulloverride, const framestate_t *framestate, const vec3_t axis[3], const vec3_t p, const vec3_t mins, const vec3_t maxs);\n\n\t//deals with whatever is native for the bsp (gamecode is expected to distinguish this).\n\tqboolean (*NativeTrace)\t\t(struct model_s *model, int hulloverride, const framestate_t *framestate, const vec3_t axis[3], const vec3_t p1, const vec3_t p2, const vec3_t mins, const vec3_t maxs, qboolean capsule, unsigned int against, struct trace_s *trace);\n\tunsigned int (*NativeContents)(struct model_s *model, int hulloverride, const framestate_t *framestate, const vec3_t axis[3], const vec3_t p, const vec3_t mins, const vec3_t maxs);\n\n\tunsigned int (*FatPVS)\t\t(struct model_s *model, const vec3_t org, pvsbuffer_t *pvsbuffer, qboolean merge);\n\tqboolean (*EdictInFatPVS)\t(struct model_s *model, const struct pvscache_s *edict, const qbyte *pvs, const int *areas);\t//areas[0] is the count of accepted areas, if valid.\n\tvoid (*FindTouchedLeafs)\t(struct model_s *model, struct pvscache_s *ent, const vec3_t cullmins, const vec3_t cullmaxs);\t//edict system as opposed to q2 game dll system.\n\n\tvoid (*LightPointValues)\t(struct model_s *model, const vec3_t point, vec3_t res_diffuse, vec3_t res_ambient, vec3_t res_dir);\n\tvoid (*StainNode)\t\t\t(struct model_s *model, float *parms);\n\tvoid (*MarkLights)\t\t\t(struct dlight_s *light, dlightbitmask_t bit, struct mnode_s *node);\n\n\tint\t(*ClusterForPoint)\t\t(struct model_s *model, const vec3_t point, int *areaout);\t//pvs index (leaf-1 for q1bsp). may be negative (ie: no pvs).\n\tqbyte *(*ClusterPVS)\t\t(struct model_s *model, int cluster, pvsbuffer_t *pvsbuffer, pvsmerge_t merge);\n\tqbyte *(*ClusterPHS)\t\t(struct model_s *model, int cluster, pvsbuffer_t *pvsbuffer);\n\tqbyte *(*ClustersInSphere)\t(struct model_s *model, const vec3_t point, float radius, pvsbuffer_t *pvsbuffer, const qbyte *fte_restrict unionwith);\n\n\tsize_t (*WriteAreaBits)\t\t(struct model_s *model, qbyte *buffer, size_t maxbytes, int area, qboolean merge);\t//writes a set of bits valid for a specific viewpoint's area.\n\tqboolean (*AreasConnected)\t(struct model_s *model, unsigned int area1, unsigned int area2);\t//fails if there's no open doors\n\tvoid (*SetAreaPortalState)\t(struct model_s *model, unsigned int portal, unsigned int area1, unsigned int area2, qboolean open);\t//a door moved...\n\tsize_t (*SaveAreaPortalBlob)(struct model_s *model, void **ptr);\t\t\t\t//for vid_reload to not break portals. dupe the ptrbefore freeing the model.\n\tsize_t (*LoadAreaPortalBlob)(struct model_s *model, void *ptr, size_t size);\t//for vid_reload to not break portals (has refcount info etc).\n\n\tvoid (*PrepareFrame)\t\t(struct model_s *model, refdef_t *refdef, int area, int clusters[2], pvsbuffer_t *vis, qbyte **entvis_out, qbyte **surfvis_out);\n\tvoid (*GenerateShadowMesh)\t(struct model_s *model, dlight_t *dl, const qbyte *lvis, qbyte *truevis, void(*callback)(struct msurface_s*));\n\tvoid (*InfoForPoint)\t\t(struct model_s *model, vec3_t pos, int *area, int *cluster, unsigned int *contentbits);\n} modelfuncs_t;\n\n\n\n\n//\n// in memory representation\n//\n// !!! if this is changed, it must be changed in asm_draw.h too !!!\ntypedef struct\n{\n\tvec3_t\t\tposition;\n} mvertex_t;\n\n#define\tSIDE_FRONT\t0\n#define\tSIDE_BACK\t1\n#define\tSIDE_ON\t\t2\n\ntypedef struct vbo_s\n{\n\tunsigned int numvisible;\n\tstruct msurface_s **vislist;\n\n\tunsigned int indexcount;\n\tunsigned int vertcount;\n\tunsigned int meshcount;\n\tstruct msurface_s **meshlist;\n\n\tvboarray_t\t\tindicies;\n\tvoid *vertdata; /*internal use*/\n\n\tint vao;\n\tunsigned int vaodynamic;\t/*mask of the attributes that are dynamic*/\n\tunsigned int vaoenabled;\t/*mask of the attributes *currently* enabled. renderer may change this */\n\tvboarray_t coord;\n\tvboarray_t coord2;\n\tvboarray_t texcoord;\n\tvboarray_t lmcoord[MAXRLIGHTMAPS];\n\n\tvboarray_t normals;\n\tvboarray_t svector;\n\tvboarray_t tvector;\n\n\tqboolean colours_bytes;\n\tvboarray_t colours[MAXRLIGHTMAPS];\n\n\tvboarray_t bonenums;\n\n\tvboarray_t boneweights;\n\n\tvoid *vbomem;\n\tvoid *ebomem;\n\n\tunsigned int vbobones;\n\tconst float *bones;\n\tunsigned  int numbones;\n\n\tstruct vbo_s *next;\n} vbo_t;\nvoid GL_SelectVBO(int vbo);\nvoid GL_SelectEBO(int vbo);\nvoid GL_DeselectVAO(void);\n\ntypedef struct texture_s\n{\n\tchar\t\tname[128];\n\tunsigned\tvwidth, vheight;\t//used for lightmap coord generation\n\n\tstruct shader_s\t*shader;\n\tchar\t\t*partname;\t\t\t\t//parsed from the worldspawn entity\n\n\tunsigned int\tanim_total;\t\t\t\t// total tenths in sequence ( 0 = no)\n\tunsigned int\tanim_min, anim_max;\t\t// time for this frame min <=time< max\n\tstruct texture_s *anim_next;\t\t// in the animation sequence\n\tstruct texture_s *alternate_anims;\t// bmodels in frmae 1 use these\n\n\tuploadfmt_t srcfmt;\n\tunsigned int srcwidth, srcheight;\t//actual size (updated miptex format)\n\tqbyte\t\t*srcdata;\t\t//the different mipmap levels.\n\tqbyte\t\t*palette;\t//host_basepal or halflife per-texture palette (or null)\n} texture_t;\n/*\ntypedef struct\n{\n\tfloat coord[3];\n\tfloat texcoord[2];\n\tfloat lmcoord[2];\n\n\tfloat normals[3];\n\tfloat svector[3];\n\tfloat tvector[3];\n} vbovertex_t;\n*/\n#define SURF_DRAWSKYBOX\t\t0x00001\n#define\tSURF_PLANEBACK\t\t0x00002\n#define\tSURF_DRAWSKY\t\t0x00004\n#define SURF_DRAWSPRITE\t\t0x00008\n#define SURF_DRAWTURB\t\t0x00010\t//water warp effect\n#define SURF_DRAWTILED\t\t0x00020\t//no need for a sw surface cache... (read: no lightmap)\n#define SURF_DRAWBACKGROUND\t0x00040\n#define SURF_UNDERWATER\t\t0x00080\n#define SURF_DONTWARP\t\t0x00100\n//#define SURF_BULLETEN\t\t0x00200\n#define SURF_NOFLAT\t\t\t0x08000\n#define SURF_DRAWALPHA\t\t0x10000\n#define SURF_NODRAW\t\t\t0x20000\t//set on non-vertical halflife water submodel surfaces\n\n// !!! if this is changed, it must be changed in asm_draw.h too !!!\ntypedef struct\n{\n\tunsigned int\tv[2];\n} medge_t;\n\ntypedef struct mtexinfo_s\n{\n\tvec4_t\t\tvecs[2];\n\tfloat\t\tvecscale[2];\n\ttexture_t\t*texture;\n\tint\t\t\tflags;\n\n\t//it's a q2 thing.\n\tint\t\t\tnumframes;\n\tstruct mtexinfo_s\t*next;\n} mtexinfo_t;\n\n#define SPECULAR\n#ifdef SPECULAR\n#define\tVERTEXSIZE\t10\n#else\n#define\tVERTEXSIZE\t7\n#endif\n\ntypedef struct mfog_s\n{\n\tchar\t\t\tshadername[MAX_QPATH];\n\tstruct shader_s\t\t*shader;\n\n\tmplane_t\t\t*visibleplane;\n\n\tint\t\t\t\tnumplanes;\n\tmplane_t\t\t**planes;\n} mfog_t;\n\ntypedef struct\n{\n\tvec3_t origin;\n\tint cubesize;\t//pixels\n} denvmap_t;\ntypedef struct\n{\n\tvec3_t origin;\n\tint cubesize;\t//pixels\n\n\ttexid_t image;\n} menvmap_t;\n\n#define LMSHIFT_DEFAULT 4\ntypedef struct msurface_s\n{\n\tmplane_t\t*plane;\n\tint\t\t\tflags;\n\n\tint\t\t\tfirstedge;\t// look up in model->surfedges[], negative numbers\n\tunsigned short\tnumedges;\t// are backwards edges\n\n\tunsigned short\t\tlmshift;\t//texels>>lmshift = lightmap samples.\n\tint\t\t\ttexturemins[2];\n\tshort\t\textents[2];\n\n\tunsigned short\tlight_s[MAXRLIGHTMAPS], light_t[MAXRLIGHTMAPS];\t// gl lightmap coordinates\n\n\timage_t\t\t*envmap;\n\tmfog_t\t\t*fog;\n\tmesh_t\t\t*mesh;\n\n\tbatch_t\t\t*sbatch;\n\tmtexinfo_t\t*texinfo;\n\tint\t\t\tvisframe;\t\t// should be drawn when node is crossed\n#ifdef RTLIGHTS\n\tint\t\t\tshadowframe;\n#endif\n//\tint\t\t\tclipcount;\n\n// legacy lighting info\n\tdlightbitmask_t\tdlightbits;\n\tint\t\t\tdlightframe;\n\tqboolean\tcached_dlight;\t\t\t\t// true if dynamic light in cache\n\n//static lighting\n\tint\t\t\tlightmaptexturenums[MAXRLIGHTMAPS];\t//rbsp+fbsp formats have multiple lightmaps\n\tlightstyleindex_t\tstyles[MAXCPULIGHTMAPS];\n\tqbyte\t\tvlstyles[MAXRLIGHTMAPS];\n\tint\t\t\tcached_light[MAXCPULIGHTMAPS];\t// values currently used in lightmap\n\tint\t\t\tcached_colour[MAXCPULIGHTMAPS];\t// values currently used in lightmap\n#ifndef NOSTAINS\n\tqboolean stained;\n#endif\n\tqbyte\t\t*samples;\t\t// [numstyles*surfsize]\n} msurface_t;\n\n/*typedef struct mbrush_s\n{\n\tstruct mbrush_s *next;\n\tunsigned int contents;\n\tint numplanes;\n\tstruct mbrushplane_s\n\t{\n\t\tvec3_t normal;\n\t\tfloat dist;\n\t} planes[1];\n} mbrush_t;*/\n\ntypedef struct mnode_s\n{\n// common with leaf\n\tint\t\t\tcontents;\t\t// 0, to differentiate from leafs\n\tint\t\t\tvisframe;\t\t// node needs to be traversed if current\n\tint\t\t\tshadowframe;\n\n\tfloat\t\tminmaxs[6];\t\t// for bounding box culling\n\n\tstruct mnode_s\t*parent;\n//\tstruct mbrush_s\t*brushes;\n\n// node specific\n\tmplane_t\t*plane;\n\tstruct mnode_s\t*children[2];\n#if defined(Q2BSPS) || defined(Q3BSPS) || defined(MAP_PROC)\n\tint childnum[2];\n#endif\n\n\tunsigned int\t\tfirstsurface;\n\tunsigned int\t\tnumsurfaces;\n} mnode_t;\n\n\n\ntypedef struct mleaf_s\n{\n// common with node\n\tint\t\t\tcontents;\t\t// wil be a negative contents number\n\tint\t\t\tvisframe;\t\t// node needs to be traversed if current\n\tint\t\t\tshadowframe;\n\n\tfloat\t\tminmaxs[6];\t\t// for bounding box culling\n\n\tstruct mnode_s\t*parent;\n\n// leaf specific\n\tqbyte\t\t*compressed_vis;\n\n\tmsurface_t\t**firstmarksurface;\n\tint\t\t\tnummarksurfaces;\n\tqbyte\t\tambient_sound_level[NUM_AMBIENTS];\n\n#if defined(Q2BSPS) || defined(Q3BSPS)\n\tint\t\t\tcluster;\n\tint\t\t\tarea;\n\tunsigned int\tfirstleafbrush;\n\tunsigned int\tnumleafbrushes;\n#endif\n#ifdef Q3BSPS\n\tunsigned int\tfirstleafcmesh;\n\tunsigned int\tnumleafcmeshes;\n\tunsigned int\tfirstleafpatch;\n\tunsigned int\tnumleafpatches;\n#endif\n} mleaf_t;\n\n\ntypedef struct\n{\n\tfloat\t\t\t\tmins[3], maxs[3];\n\tfloat\t\t\t\torigin[3];\n\tunsigned int\t\theadnode[MAX_MAP_HULLSM];\n\tunsigned int\t\tvisleafs;\t\t// not including the solid leaf 0\n\tunsigned int\t\tfirstface, numfaces;\n\tqboolean\t\t\thullavailable[MAX_MAP_HULLSM];\n\n\tstruct q2cbrush_s\t*brushes;\n\tunsigned int\t\tnumbrushes;\n} mmodel_t;\n\n\n// !!! if this is changed, it must be changed in asm_i386.h too !!!\ntypedef struct hull_s\n{\n\tmclipnode_t\t*clipnodes;\n\tmplane_t\t*planes;\n\tint\t\t\tfirstclipnode;\n\tint\t\t\tlastclipnode;\n\tvec3_t\t\tclip_mins;\n\tvec3_t\t\tclip_maxs;\n\tint\t\t\tavailable;\n} hull_t;\n\nvoid Q1BSP_CheckHullNodes(hull_t *hull);\nvoid Q1BSP_SetModelFuncs(struct model_s *mod);\nvoid Q1BSP_LoadBrushes(struct model_s *model, bspx_header_t *bspx, void *mod_base);\nvoid Q1BSP_Init(void);\nvoid Q1BSP_GenerateShadowMesh(struct model_s *model, struct dlight_s *dl, const qbyte *lightvis, qbyte *litvis, void (*callback)(msurface_t *surf));\n\nvoid BSPX_LightGridLoad(struct model_s *model, bspx_header_t *bspx, qbyte *mod_base);\t//for q1 or q2 models.\nvoid BSPX_LoadEnvmaps(struct model_s *mod, bspx_header_t *bspx, void *mod_base);\nvoid *BSPX_FindLump(bspx_header_t *bspxheader, void *mod_base, char *lumpname, size_t *lumpsize);\nbspx_header_t *BSPX_Setup(struct model_s *mod, char *filebase, size_t filelen, lump_t *lumps, size_t numlumps);\n\ntypedef struct fragmentdecal_s fragmentdecal_t;\nvoid Fragment_ClipPoly(fragmentdecal_t *dec, int numverts, float *inverts, shader_t *surfshader);\nsize_t Fragment_ClipPlaneToBrush(vecV_t *points, size_t maxpoints, void *planes, size_t planestride, size_t numplanes, vec4_t face);\nvoid Mod_ClipDecal(struct model_s *mod, vec3_t center, vec3_t normal, vec3_t tangent1, vec3_t tangent2, float size, unsigned int surfflagmask, unsigned int surflagmatch, void (*callback)(void *ctx, vec3_t *fte_restrict points, size_t numpoints, shader_t *shader), void *ctx);\n\nvoid Q1BSP_MarkLights (dlight_t *light, dlightbitmask_t bit, mnode_t *node);\nvoid GLQ1BSP_LightPointValues(struct model_s *model, const vec3_t point, vec3_t res_diffuse, vec3_t res_ambient, vec3_t res_dir);\nqboolean Q1BSP_RecursiveHullCheck (hull_t *hull, int num, const vec3_t p1, const vec3_t p2, unsigned int hitcontents, struct trace_s *trace);\n\n/*\n==============================================================================\n\nSPRITE MODELS\n\n==============================================================================\n*/\n\n\n// FIXME: shorten these?\ntypedef struct mspriteframe_s\n{\n\tfloat\tup, down, left, right;\n\tqboolean xmirror;\n\tqboolean lit;\n\tshader_t *shader;\n\timage_t *image;\n} mspriteframe_t;\n\nmspriteframe_t *R_GetSpriteFrame (entity_t *currententity);\n\n\ntypedef struct\n{\n\tint\t\t\t\tnumframes;\n\tfloat\t\t\t*intervals;\n\tmspriteframe_t\t*frames[1];\n} mspritegroup_t;\n\ntypedef struct\n{\n\tspriteframetype_t\ttype;\n\tmspriteframe_t\t\t*frameptr;\n} mspriteframedesc_t;\n\ntypedef struct\n{\n\tint\t\t\t\t\ttype;\n\tint\t\t\t\t\tmaxwidth;\n\tint\t\t\t\t\tmaxheight;\n\tint\t\t\t\t\tnumframes;\n\tfloat\t\t\t\tbeamlength;\t\t// remove?\n\tmspriteframedesc_t\tframes[1];\n} msprite_t;\n\n\n/*\n==============================================================================\n\nALIAS MODELS\n\nAlias models are position independent, so the cache manager can move them.\n==============================================================================\n*/\n#if 0\ntypedef struct {\n\tint\t\ts;\n\tint\t\tt;\n} mstvert_t;\n\ntypedef struct\n{\n\tint\t\t\t\t\tfirstpose;\n\tint\t\t\t\t\tnumposes;\n\tfloat\t\t\t\tinterval;\n\tdtrivertx_t\t\t\tbboxmin;\n\tdtrivertx_t\t\t\tbboxmax;\n\n\tvec3_t\t\tscale;\n\tvec3_t\t\tscale_origin;\n\n\tint\t\t\t\t\tframe;\n\tchar\t\t\t\tname[16];\n} maliasframedesc_t;\n\ntypedef struct\n{\n\tdtrivertx_t\t\t\tbboxmin;\n\tdtrivertx_t\t\t\tbboxmax;\n\tint\t\t\t\t\tframe;\n} maliasgroupframedesc_t;\n\ntypedef struct\n{\n\tint\t\t\t\t\t\tnumframes;\n\tint\t\t\t\t\t\tintervals;\n\tmaliasgroupframedesc_t\tframes[1];\n} maliasgroup_t;\n\n// !!! if this is changed, it must be changed in asm_draw.h too !!!\ntypedef struct mtriangle_s {\n\tint\t\t\t\t\txyz_index[3];\n\tint\t\t\t\t\tst_index[3];\n\n\tint\tpad[2];\n} mtriangle_t;\n\n\n#define\tMAX_SKINS\t32\ntypedef struct {\n\tint\t\t\tident;\n\tint\t\t\tversion;\n\tvec3_t\t\tscale;\n\tvec3_t\t\tscale_origin;\n\tfloat\t\tboundingradius;\n\tvec3_t\t\teyeposition;\n\tint\t\t\tnumskins;\n\tint\t\t\tskinwidth;\n\tint\t\t\tskinheight;\n\tint\t\t\tnumverts;\n\tint\t\t\tnumtris;\n\tint\t\t\tnumframes;\n\tsynctype_t\tsynctype;\n\tint\t\t\tflags;\n\tfloat\t\tsize;\n\tint\t\t\t\t\tnumposes;\n\tint\t\t\t\t\tposeverts;\n\tint\t\t\t\t\tposedata;\t// numposes*poseverts trivert_t\n\n\tint\t\t\t\t\tbaseposedata; //original verts for triangles to reference\n\tint\t\t\t\t\ttriangles; //we need tri data for shadow volumes\n\n\tint\t\t\t\t\tcommands;\t// gl command list with embedded s/t\n\tint\t\t\t\t\tgl_texturenum[MAX_SKINS][4];\n\tint\t\t\t\t\ttexels[MAX_SKINS];\n\tmaliasframedesc_t\tframes[1];\t// variable sized\n} aliashdr_t;\n\n#define\tMAXALIASVERTS\t2048\n#define ALIAS_Z_CLIP_PLANE\t5\n#define\tMAXALIASFRAMES\t256\n#define\tMAXALIASTRIS\t2048\nextern\taliashdr_t\t*pheader;\nextern\tmstvert_t\tstverts[MAXALIASVERTS*2];\nextern\tmtriangle_t\ttriangles[MAXALIASTRIS];\nextern\tdtrivertx_t\t*poseverts[MAXALIASFRAMES];\n\n#endif\n\n\n/*\n========================================================================\n\n.MD2 triangle model file format\n\n========================================================================\n*/\n\n// LordHavoc: grabbed this from the Q2 utility source,\n// renamed a things to avoid conflicts\n\n#define MD2IDALIASHEADER\t\"IDP2\",4\n#define MD2ALIAS_VERSION\t8\n#define\tMD2MAX_SKINNAME\t\t64\t//part of the format\n\n/*\n#define\tMD2MAX_TRIANGLES\t4096\n#define MD2MAX_FRAMES\t\t512\n#define MD2MAX_VERTS\t\t2048\n#define MD2MAX_SKINS\t\t32\n// sanity checking size\n#define MD2MAX_SIZE\t(1024*4200)\n*/\ntypedef struct\n{\n\tshort\ts;\n\tshort\tt;\n} md2stvert_t;\n\ntypedef struct\n{\n\tshort\tindex_xyz[3];\n\tshort\tindex_st[3];\n} md2triangle_t;\n\ntypedef struct\n{\n\tqbyte\tv[3];\t\t\t// scaled qbyte to fit in frame mins/maxs\n\tqbyte\tlightnormalindex;\n} md2trivertx_t;\n\n/*\n#define MD2TRIVERTX_V0   0\n#define MD2TRIVERTX_V1   1\n#define MD2TRIVERTX_V2   2\n#define MD2TRIVERTX_LNI  3\n#define MD2TRIVERTX_SIZE 4\n*/\n\ntypedef struct\n{\n\tfloat\t\tscale[3];\t// multiply qbyte verts by this\n\tfloat\t\ttranslate[3];\t// then add this\n\tchar\t\tname[16];\t// frame name from grabbing\n\tmd2trivertx_t\tverts[1];\t// variable sized\n} md2frame_t;\n\n\n// the glcmd format:\n// a positive integer starts a tristrip command, followed by that many\n// vertex structures.\n// a negative integer starts a trifan command, followed by -x vertexes\n// a zero indicates the end of the command list.\n// a vertex consists of a floating point s, a floating point t,\n// and an integer vertex index.\n\n\ntypedef struct\n{\n\tint\t\t\tident;\n\tint\t\t\tversion;\n\n\tint\t\t\tskinwidth;\n\tint\t\t\tskinheight;\n\tint\t\t\tframesize;\t\t// qbyte size of each frame\n\n\tint\t\t\tnum_skins;\n\tint\t\t\tnum_xyz;\n\tint\t\t\tnum_st;\t\t\t// greater than num_xyz for seams\n\tint\t\t\tnum_tris;\n\tint\t\t\tnum_glcmds;\t\t// dwords in strip/fan command list\n\tint\t\t\tnum_frames;\n\n\tint\t\t\tofs_skins;\t\t// each skin is a MAX_SKINNAME string\n\tint\t\t\tofs_st;\t\t\t// qbyte offset from start for stverts\n\tint\t\t\tofs_tris;\t\t// offset for dtriangles\n\tint\t\t\tofs_frames;\t\t// offset for first frame\n\tint\t\t\tofs_glcmds;\n\tint\t\t\tofs_end;\t\t// end of file\n} md2_t;\n\n//#define ALIASTYPE_MDL 1\n//#define ALIASTYPE_MD2 2\n\n\n\n\n\n//===================================================================\n\n\ntypedef struct\n{\n\tqbyte\t\t\tambient[3];\n\tqbyte\t\t\tdiffuse[3];\n\tqbyte\t\t\tdirection[2];\n} dq3gridlight_t;\n\ntypedef struct\n{\n\tunsigned char\tambient[4][3];\n\tunsigned char\tdiffuse[4][3];\n\tunsigned char\tstyles[4];\n\tunsigned char\tdirection[2];\n} rbspgridlight_t;\n\n//q3 based\ntypedef struct {\n\tint gridBounds[4];\t//3 = 0*1\n\tvec3_t gridMins;\n\tvec3_t gridSize;\n\tint numlightgridelems;\n//rbsp specific\n\trbspgridlight_t *rbspelements;\n\tunsigned short *rbspindexes;\n//non-rbsp specific\n\tdq3gridlight_t *lightgrid;\n\n\t//the reason rbsp is seperate from the non-rbsp is because it allows better memory compression.\n\t//I chose not to expand at loadtime because q3 would suffer from greater cache misses.\n} q3lightgridinfo_t;\n\n\n//\n// Whole model\n//\n\ntypedef enum {mod_brush, mod_sprite, mod_alias, mod_dummy, mod_halflife, mod_heightmap} modtype_t;\ntypedef enum {fg_quake, fg_quake2, fg_quake3, fg_halflife, fg_new, fg_doom} fromgame_t;\t//useful when we have very similar model types. (eg quake/halflife bsps)\ntypedef enum {sb_none, sb_quake64, sb_long1, sb_long2} subbsp_t; // used to denote bsp specifics for load processing only (no runtime changes)\n\n#define\tMF_ROCKET\t\t\t\t(1u<<0)\t\t\t// leave a trail\n#define\tMF_GRENADE\t\t\t\t(1u<<1)\t\t\t// leave a trail\n#define\tMF_GIB\t\t\t\t\t(1u<<2)\t\t\t// leave a trail\n#define\tMF_ROTATE\t\t\t\t(1u<<3)\t\t\t// rotate (bonus items)\n#define\tMF_TRACER\t\t\t\t(1u<<4)\t\t\t// green split trail\n#define\tMF_ZOMGIB\t\t\t\t(1u<<5)\t\t\t// small blood trail\n#define\tMF_TRACER2\t\t\t\t(1u<<6)\t\t\t// orange split trail + rotate\n#define\tMF_TRACER3\t\t\t\t(1u<<7)\t\t\t// purple trail\n\n//hexen2 support.\n#define  MFH2_FIREBALL\t\t\t(1u<<8)\t\t\t// Yellow transparent trail in all directions\n#define  MFH2_ICE\t\t\t\t(1u<<9)\t\t\t// Blue-white transparent trail, with gravity\n#define  MFH2_MIP_MAP\t\t\t(1u<<10)\t\t// This model has mip-maps\n#define  MFH2_SPIT\t\t\t\t(1u<<11)\t\t// Black transparent trail with negative light\n#define  MFH2_TRANSPARENT\t\t(1u<<12)\t\t// Transparent sprite\n#define  MFH2_SPELL\t\t\t\t(1u<<13)\t\t// Vertical spray of particles\n#define  MFH2_HOLEY\t\t\t\t(1u<<14)\t\t// Solid model with color 0 cut out\n#define  MFH2_SPECIAL_TRANS\t\t(1u<<15)\t\t// Translucency through the particle table\n#define  MFH2_FACE_VIEW\t\t\t(1u<<16)\t\t// Poly Model always faces you\n#define  MFH2_VORP_MISSILE\t\t(1u<<17)\t\t// leave a trail at top and bottom of model\n#define  MFH2_SET_STAFF\t\t\t(1u<<18)\t\t// slowly move up and left/right\n#define  MFH2_MAGICMISSILE\t\t(1u<<19)\t\t// a trickle of blue/white particles with gravity\n#define  MFH2_BONESHARD\t\t\t(1u<<20)\t\t// a trickle of brown particles with gravity\n#define  MFH2_SCARAB\t\t\t(1u<<21)\t\t// white transparent particles with little gravity\n#define  MFH2_ACIDBALL\t\t\t(1u<<22)\t\t// Green drippy acid shit\n#define  MFH2_BLOODSHOT\t\t\t(1u<<23)\t\t// Blood rain shot trail\n#define  MFH2_SPIDERBLOOD\t\t(1u<<31)\t\t// spider blood (remapped from MF_ROCKET, to avoid dlight issues)\n\ntypedef union {\n\tstruct {\n\t\tint numlinedefs;\n\t\tint numsidedefs;\n\t\tint numsectors;\n\t} doom;\n} specificmodeltype_t;\n\ntypedef struct\n{\n\tint walkno;\n\tint area[2];\n\tvec3_t plane;\n\tfloat dist;\n\tvec3_t min;\n\tvec3_t max;\n\tint numpoints;\n\tvec4_t *points;\n} portal_t;\n\nstruct decoupled_lm_info_s\n{\n\tquint16_t lmsize[2];\t//made explicit. beware MAX_\n    quint32_t lmoffset;\t\t//replacement offset for vanilla compat.\n    vec4_t lmvecs[2]; //lmcoord[] = dotproduct3(vertexcoord, lmvecs[])+lmvecs[][3]\n};\nstruct facelmvecs_s\n{\n\tvec4_t lmvecs[2];\t\t//lmcoord[] = dotproduct3(vertexcoord, lmvecs[])+lmvecs[][3]\n\tfloat lmvecscale[2];\t//just 1/Length(lmvecs). dlights work in luxels, but need to be able to work back to qu.\n};\n\nstruct surfedgenormals_s {\n\tquint32_t n;\n\tquint32_t s;\n\tquint32_t t;\n};\n\nenum\n{\n\tMLS_NOTLOADED,\n\tMLS_LOADING,\n\tMLS_LOADED,\n\tMLS_FAILED\n};\ntypedef struct model_s\n{\n\tchar\t\tname[MAX_QPATH];\t//actual name on disk\n\tchar\t\tpublicname[MAX_QPATH];\t//name that the gamecode etc sees\n\tint\t\t\tdatasequence;\t//if it gets old enough, we can purge it.\n\tint\t\t\tengineflags;\t//\n\tint\t\t\tloadstate;\t\t//MLS_\n\tqboolean\ttainted;\t\t// differs from the server's version. this model will be invisible as a result, to avoid spiked models.\n\tqboolean\tpushdepth;\t\t// bsp submodels have this flag set so you don't get z fighting on co-planar surfaces.\n\ttime_t\t\tmtime;\t\t\t// modification time. so we can flush models if they're changed on disk. or at least worldmodels.\n\n\tstruct model_s *submodelof;\t// shares memory allocations with this model.\n\n\tmodtype_t\ttype;\n\tfromgame_t\tfromgame;\n\n\tint\t\t\tnumframes;\n\tsynctype_t\tsynctype;\n\n\tint\t\t\tflags;\n\tint\t\t\tparticleeffect;\n\tint\t\t\tparticletrail;\n\tint\t\t\ttraildefaultindex;\n\n//\n// volume occupied by the model graphics\n//\n\tvec3_t\t\tmins, maxs;\n\tfloat\t\tradius;\n\tfloat\t\tclampscale;\n\tfloat\t\tmaxlod;\n\n//\n// solid volume for clipping\n//\n\tqboolean\tclipbox;\n\tvec3_t\t\tclipmins, clipmaxs;\n\n\tvoid\t\t*cnodes;\t//BIH tree\n//\n// brush model\n//\n\tint\t\t\tfirstmodelsurface, nummodelsurfaces;\n\n\tint\t\t\tnumsubmodels;\n\tmmodel_t\t*submodels;\n\n\tint\t\t\tnumplanes;\n\tmplane_t\t*planes;\n\n\tsize_t\t\tpvsbytes;\t//total bytes for the per-leaf pvs/phs data. rounded up to sizeof(int).\n\tint\t\t\tnumclusters;\t//number of bits in the pvs data.\n\tint\t\t\tnumleafs;\t\t// number of visible leafs, not counting 0\n\tmleaf_t\t\t*leafs;\n\n\tint\t\t\tnumvertexes;\n\tmvertex_t\t*vertexes;\n\tvec3_t\t\t*normals;\n\tstruct surfedgenormals_s\t*surfedgenormals; //for per-vertex normals\n\tstruct facelmvecs_s\t\t\t*facelmvecs;\n\n\tint\t\t\tnumedges;\n\tmedge_t\t\t*edges;\n\n\tint\t\t\tnumnodes;\n\tmnode_t\t\t*nodes;\n\tmnode_t\t\t*rootnode;\n\n\tint\t\t\tnumtexinfo;\n\tmtexinfo_t\t*texinfo;\n\n\tint\t\t\tnumsurfaces;\n\tmsurface_t\t*surfaces;\n\n\tint\t\t\tnumsurfedges;\n\tint\t\t\t*surfedges;\n\n\tint\t\t\tnumclipnodes;\n\tmclipnode_t\t*clipnodes;\n\n\tint\t\t\tnummarksurfaces;\n\tmsurface_t\t**marksurfaces;\n\n\thull_t\t\thulls[MAX_MAP_HULLSM];\n\n\tint\t\t\tnumtextures;\n\ttexture_t\t**textures;\n\n\tqbyte\t\t*pvs, *phs;\t\t\t// fully expanded and decompressed\n\tqbyte\t\t*visdata;\n\tvoid\t*vis;\n\tqbyte\t\t*lightdata;\n\tqbyte\t\t*deluxdata;\n\tunsigned\tlightdatasize;\n\tq3lightgridinfo_t *lightgrid;\n\tmfog_t\t\t*fogs;\n\tint\t\t\tnumfogs;\n\tmenvmap_t\t*envmaps;\n\tunsigned\tnumenvmaps;\n\n\tstruct skytris_s\t\t*skytris;\t//for surface emittance\n\tfloat\t\t\t\t\tskytime;\t//for surface emittance\n\tstruct skytriblock_s\t*skytrimem;\n\n\tstruct {unsigned int id; char *keyvals;} *entityinfo;\n\tsize_t\t\tnumentityinfo;\n\tconst char\t*entities_raw;\n\tint\t\t\tentitiescrc;\n\n\tstruct doll_s\t\t*dollinfo;\t//ragdoll info\n\tint camerabone;\t//the 1-based bone index that the camera should be attached to (for gltf rather than anything else)\n\n\tshader_t\t*simpleskin[4];\t\t//simpleitems cache\n\n\tstruct {\n\t\ttexture_t *tex;\n\t\tvbo_t *vbo;\n\t} *shadowbatches;\n\tint numshadowbatches;\n\tvbo_t *vbos;\n\tvoid *terrain;\n\tbatch_t *batches[SHADER_SORT_COUNT];\n\tunsigned int numbatches;\n\tstruct\n\t{\n\t\tint first;\t\t\t\t//once built...\n\t\tint count;\t\t\t\t//num lightmaps\n\t\tint\tmergew;\t\t\t\t//merge this many source lightmaps together. woo.\n\t\tint\tmergeh;\t\t\t\t//merge this many source lightmaps together. woo.\n\t\tint width;\t\t\t\t//x size of lightmaps\n\t\tint height;\t\t\t\t//y size of lightmaps\n\t\tint surfstyles;\t\t\t//numbers of style per surface.\n\t\tint maxstyle;\t\t\t//highest (valid) style used (cl_max_lightstyles must be 1+ higher).\n\t\tenum {\n\t\t\t//vanilla used byte values, with 255 being a value of about 2.\n\t\t\t//float/hdr formats use 1 to mean 1, however.\n\t\t\t//internally, we still use integers for lighting, with .7 bits of extra precision.\n\t\t\tLM_L8,\n\t\t\tLM_RGB8,\n\t\t\tLM_E5BGR9,\n\t\t} fmt;\n\t\tenum uploadfmt prebaked;\n\t\tqboolean deluxemapping;\t//lightmaps are interleaved with deluxemap data (lightmap indicies should only be even values)\n\t\tqboolean deluxemapping_modelspace; //deluxemaps are in modelspace - we need different glsl.\n\t} lightmaps;\n\n\tunsigned\tchecksum;\t//the legacy checksum (excludes ent lump)\n\tunsigned\tchecksum2;\t//the watervis checksum (excludes ent lump and vis stuff). this is the one that's sent over the network.\n\n\tportal_t *portal;\n\tunsigned int numportals;\n\n\tmodelfuncs_t\tfuncs;\n//\n// additional model data\n//\n\tvoid *meshinfo;\t//data allocated within the memgroup allocations, will be nulled out when the model is flushed\n\tsearchpathfuncs_t *archive;\t//some bsp formats have an embedded zip...\n\tzonegroup_t memgroup;\n} model_t;\n\n#define MDLF_EMITREPLACE     0x0001 // particle effect engulphs model (don't draw)\n#define MDLF_EMITFORWARDS    0x0002\n#define MDLF_NODEFAULTTRAIL  0x0004\n//#define MDLF_RGBLIGHTING     0x0008\n#define MDLF_PLAYER          0x0010 // players have specific lighting values\n#define MDLF_FLAME           0x0020 // can be excluded with r_drawflame, fullbright render hack\n#define MDLF_DOCRC           0x0040 // model needs CRC built\n#define MDLF_NEEDOVERBRIGHT  0x0080 // only overbright these models with gl_overbright_all set\n#define MDLF_NOSHADOWS       0x0100 // doesn't produce shadows for one reason or another\n#define\tMDLF_NOTREPLACEMENTS 0x0200 // can be considered a cheat, disable texture replacements\n#define MDLF_EZQUAKEFBCHEAT  0x0400 // this is a blatent cheat, one that can disadvantage us fairly significantly if we don't support it.\n#define MDLF_NOLERP\t\t     0x0800 // doesn't lerp, ever. for dodgy models that don't scale to nothingness before jumping.\n#define MDLF_RECALCULATERAIN 0x1000 // particles changed, recalculate any sky polys\n\n//============================================================================\n#endif\t// __MODEL__\n\n\n\ntypedef struct\n{\n\tunsigned int *offsets;\n\tunsigned short *extents;\n\tunsigned char *styles8;\n\tunsigned short *styles16;\n\tunsigned int stylesperface;\n\tunsigned char *shifts;\n\tunsigned char defaultshift;\n} lightmapoverrides_t;\nvoid Mod_LoadLighting (struct model_s *loadmodel, bspx_header_t *bspx, qbyte *mod_base, lump_t *l, qboolean interleaveddeluxe, lightmapoverrides_t *overrides, subbsp_t subbsp);\n\nfloat RadiusFromBounds (const vec3_t mins, const vec3_t maxs);\n\n\n//\n// gl_heightmap.c\n//\n#ifdef TERRAIN\nvoid Terr_Init(void);\nstruct plugterrainfuncs_s;\nstruct plugterrainfuncs_s *Terr_GetTerrainFuncs(size_t structsize);\nvoid Terr_DrawTerrainModel (batch_t **batch, entity_t *e);\nvoid Terr_FreeModel(model_t *mod);\nvoid Terr_FinishTerrain(model_t *model);\nvoid Terr_PurgeTerrainModel(model_t *hm, qboolean lightmapsonly, qboolean lightmapreusable);\nvoid *Mod_LoadTerrainInfo(model_t *mod, char *loadname, qboolean force);\t//call this after loading a bsp\nqboolean Terrain_LocateSection(const char *name, flocation_t *loc);\t//used on servers to generate sections for download.\nqboolean Heightmap_Trace(model_t *model, int forcehullnum, const framestate_t *framestate, const vec3_t axis[3], const vec3_t start, const vec3_t end, const vec3_t mins, const vec3_t maxs, qboolean capsule, unsigned int contentmask, struct trace_s *trace);\nunsigned int Heightmap_PointContents(model_t *model, const vec3_t axis[3], const vec3_t org);\nstruct fragmentdecal_s;\nvoid Terrain_ClipDecal(struct fragmentdecal_s *dec, float *center, float radius, model_t *model);\nqboolean Terr_DownloadedSection(char *fname);\nshader_t *Terr_GetShader(struct model_s *mod, struct trace_s *trace);\n\nvoid CL_Parse_BrushEdit(void);\nqboolean SV_Parse_BrushEdit(void);\nqboolean SV_Prespawn_Brushes(sizebuf_t *msg, unsigned int *modelindex, unsigned int *lastid);\n#endif\n\n\n\n\n\nqboolean Heightmap_Edit(model_t *mod, int action, float *pos, float radius, float quant);\n\n\n#if defined(Q2BSPS) || defined(Q3BSPS)\nvoid CM_Init(void);\nstruct model_s *CM_TempBoxModel(const vec3_t mins, const vec3_t maxs);\n#endif\n#if 0\n\n#ifdef __cplusplus\n//#pragma warningmsg (\"                  c++ stinks\")\n#else\n\nqboolean\tCM_SetAreaPortalState (struct model_s *mod, int portalnum, qboolean open);\nqboolean\tCM_HeadnodeVisible (struct model_s *mod, int nodenum, const qbyte *visbits);\nqboolean\tVARGS CM_AreasConnected (struct model_s *mod, unsigned int area1, unsigned int area2);\nint\t\tCM_ClusterBytes (struct model_s *mod);\nint\t\tCM_LeafContents (struct model_s *mod, int leafnum);\nint\t\tCM_LeafCluster (struct model_s *mod, int leafnum);\nint\t\tCM_LeafArea (struct model_s *mod, int leafnum);\nint\t\tCM_WriteAreaBits (struct model_s *mod, qbyte *buffer, int area, qboolean merge);\nint\t\tCM_PointLeafnum (struct model_s *mod, const vec3_t p);\nqbyte\t*CM_ClusterPVS (struct model_s *mod, int cluster, pvsbuffer_t *buffer, pvsmerge_t merge);\nqbyte\t*CM_ClusterPHS (struct model_s *mod, int cluster, pvsbuffer_t *buffer);\nint\t\tCM_BoxLeafnums (struct model_s *mod, const vec3_t mins, const vec3_t maxs, int *list, int listsize, int *topnode);\nint\t\tCM_PointContents (struct model_s *mod, const vec3_t p);\nint\t\tCM_TransformedPointContents (struct model_s *mod, const vec3_t p, int headnode, const vec3_t origin, const vec3_t angles);\nint\t\tCM_HeadnodeForBox (struct model_s *mod, const vec3_t mins, const vec3_t maxs);\n//struct trace_s\tCM_TransformedBoxTrace (struct model_s *mod, vec3_t start, vec3_t end, vec3_t mins, vec3_t maxs, int brushmask, vec3_t origin, vec3_t angles);\n\n//for gamecode to control portals/areas\nvoid\tCMQ2_SetAreaPortalState (model_t *mod, unsigned int portalnum, qboolean open);\nvoid\tCMQ3_SetAreaPortalState (model_t *mod, unsigned int area1, unsigned int area2, qboolean open);\n\n//for saved games to write the raw state.\nsize_t\tCM_WritePortalState (model_t *mod, void **data);\nqofs_t\tCM_ReadPortalState (model_t *mod, qbyte *ptr, qofs_t ptrsize);\n\n#endif\n\n\n\n#endif\t//Q2BSPS\n\nvoid CategorizePlane ( mplane_t *plane );\nvoid CalcSurfaceExtents (model_t *mod, msurface_t *s);\n"
  },
  {
    "path": "engine/gl/gl_ngraph.c",
    "content": "/*\nCopyright (C) 1996-1997 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  \n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n*/\n// gl_ngraph.c\n\n#include \"quakedef.h\"\n#include \"shader.h\"\n\nvoid Draw_ExpandedString(struct font_s *font, float x, float y, conchar_t *str);\nstatic float timehistory[NET_TIMINGS];\nstatic int findex;\n\n#define NET_GRAPHHEIGHT 32\n\n//#define GRAPHTEX\n#ifdef GRAPHTEX\nstatic texid_t\tnetgraphtexture;\t// netgraph texture\nstatic shader_t *netgraphshader;\nstatic\tunsigned int ngraph_texels[NET_GRAPHHEIGHT][NET_TIMINGS];\n#else\nstatic\tstruct\n{\n\tunsigned int col;\n\tfloat height;\n} ngraph[NET_TIMINGS];\n#endif\n\nstatic void R_LineGraph (int x, float h)\n{\n\tint\t\ts;\n\tunsigned\t\tcolor;\n\n\ts = NET_GRAPHHEIGHT;\n\n\tif (h == 10000 || h<0)\n\t{\n\t\tcolor = 0xff00ffff;\t// yellow\n\t\th=fabs(h);\n\t}\n\telse if (h == 9999)\n\t\tcolor = 0xff0000ff;\t// red\n\telse if (h == 9998)\n\t\tcolor = 0xffff0000;\t// blue\n\telse\n\t\tcolor = 0xffffffff;\t// white\n\n#ifdef GRAPHTEX\n\tif (h>s)\n\t\th = s;\n\t\n\tfor (i=0 ; i<h ; i++)\n\t\tif (i & 1)\n\t\t\tngraph_texels[NET_GRAPHHEIGHT - i - 1][x] = color&0xffefefef;\n\t\telse\n\t\t\tngraph_texels[NET_GRAPHHEIGHT - i - 1][x] = color;\n\n\tfor ( ; i<s ; i++)\n\t\tngraph_texels[NET_GRAPHHEIGHT - i - 1][x] = 0x00000000;\n#else\n\tngraph[x].col = color;\n\tif (h > s)\n\t\tngraph[x].height = 1;\n\telse\n\t\tngraph[x].height = h/(float)s;\n#endif\n}\n\n/*\n==============\nR_NetGraph\n==============\n*/\nvoid R_NetGraph (void)\n{\n\tint\t\ta, x, i;\n\tfloat y;\n\tfloat pi, po, bi, bo;\n\tint errorbar;\n\n\tvec2_t p[4];\n\tvec2_t tc[4];\n\tvec4_t rgba[4];\n\textern shader_t *shader_draw_fill;\n\tconchar_t line[2048];\n\tfloat textheight, graphtop;\n\n\tfloat pings, pings_min, pings_max, pingms_stddev, pingfr, dropped, choked, invalid;\n\tint pingfr_min, pingfr_max;\n\n\tx = 0;\n\tif (r_netgraph.value < 0)\n\t{\n\t\tif (!cl.paused)\n\t\t\ttimehistory[++findex&NET_TIMINGSMASK] = (cl.currentpackentities?(cl.currentpackentities->servertime - cl.servertime)*NET_GRAPHHEIGHT*5:0);\n\t\tfor (a=0 ; a<NET_TIMINGS ; a++)\n\t\t{\n\t\t\ti = (findex-a) & NET_TIMINGSMASK;\n\t\t\tR_LineGraph (NET_TIMINGS-1-a, timehistory[i]<0?10000:timehistory[i]);\n\t\t}\n\t}\n\telse\n\t{\n\t\tfloat last = 10000;\n\t\tCL_CalcNet(r_netgraph.value);\n\t\tfor (a=0 ; a<NET_TIMINGS ; a++)\n\t\t{\n\t\t\ti = (cl.movesequence-a) & NET_TIMINGSMASK;\n//\t\t\tif (packet_latency[i] != 10000)\n\t\t\t\tlast = packet_latency[i];\n//\t\t\telse if (last >= 0)\n//\t\t\t\tlast = -last;\n\t\t\tR_LineGraph (NET_TIMINGS-1-a, last);\n\t\t}\n\t}\n\n\ttextheight = 4;\n#ifdef HAVE_SERVER\n\tif (sv.state && sv.allocated_client_slots != 1)\n\t\ttextheight+=2;\n#endif\n\ttextheight = ceil(textheight*Font_CharVHeight(font_console)/8)*8;\t//might have a small gap underneath\n\n\tx =\t((vid.width - 320)>>1);\t//eww\n\tx=-x;\n\ty = vid.height - sb_lines - textheight - NET_GRAPHHEIGHT - 2*8/*box borders*/;\n\n\tM_DrawTextBox (x, y, NET_TIMINGS/8, (NET_GRAPHHEIGHT + textheight)/8);\n\tx = 8;\n\tif (r_netgraph.ival > 1)\n\t\tCL_ShowTrafficUsage(x + NET_TIMINGS + 8, y);\n\ty += 8; //top border\n\tgraphtop = y+textheight;\n\n\tCL_CalcNet2(&pings, &pings_min, &pings_max, &pingms_stddev, &pingfr, &pingfr_min, &pingfr_max, &dropped, &choked, &invalid);\n\t{\n\t\tCOM_ParseFunString(CON_WHITEMASK, va(\"%3.0f%% lost, %3.0f%% choked, %3.0f%% bad\", dropped*100, choked*100, invalid*100), line, sizeof(line), false);\n\t\tDraw_ExpandedString(font_console, x, y, line);\n\t\ty += Font_CharVHeight(font_console);\n\n\t\tCOM_ParseFunString(CON_WHITEMASK, va(\" ping: %4.1fms %6.2f (%.1f-%.1f)\\n\", pings*1000, pingms_stddev, pings_min*1000, pings_max*1000), line, sizeof(line), false);\n\t\tDraw_ExpandedString(font_console, x, y, line);\n\t\ty += Font_CharVHeight(font_console);\n\t}\n\n\tif (NET_GetRates(cls.sockets, &pi, &po, &bi, &bo))\n\t{\n\t\tCOM_ParseFunString(CON_WHITEMASK, va(\"   in: %.1f %.0fb\\n\", pi, bi), line, sizeof(line), false);\n\t\tDraw_ExpandedString(font_console, x, y, line);\n\t\ty += Font_CharVHeight(font_console);\n\t\tCOM_ParseFunString(CON_WHITEMASK, va(\"  out: %.1f %.0fb   mtu:%u\\n\", po, bo, cls.netchan.mtu_cur), line, sizeof(line), false);\n\t\tDraw_ExpandedString(font_console, x, y, line);\n\t\ty += Font_CharVHeight(font_console);\n\t}\n#ifdef HAVE_SERVER\n\tif (sv.state && sv.allocated_client_slots != 1 && NET_GetRates(svs.sockets, &pi, &po, &bi, &bo))\n\t{\n\t\tCOM_ParseFunString(CON_WHITEMASK, va(\"sv in: %.1f %.0fb\\n\", pi, bi), line, sizeof(line), false);\n\t\tDraw_ExpandedString(font_console, x, y, line);\n\t\ty += Font_CharVHeight(font_console);\n\t\tCOM_ParseFunString(CON_WHITEMASK, va(\"svout: %.1f %.0fb\\n\", po, bo), line, sizeof(line), false);\n\t\tDraw_ExpandedString(font_console, x, y, line);\n\t\ty += Font_CharVHeight(font_console);\n\t}\n#endif\n\n\ty = graphtop;\t//rounding makes it ugly.\n\n#ifdef GRAPHTEX\n\tImage_Upload(netgraphtexture, TF_RGBA32, ngraph_texels, NULL, NET_TIMINGS, NET_GRAPHHEIGHT, IF_UIPIC|IF_NOMIPMAP|IF_NOPICMIP);\n\tR2D_Image(x, y, NET_TIMINGS, NET_GRAPHHEIGHT, 0, 0, 1, 1, netgraphshader);\n#else\n\tVector2Set(p[2], 0,0);\n\tVector2Set(p[3], 0,0);\n\tVector4Set(rgba[2], 0,0,0,0);\n\tVector4Set(rgba[3], 0,0,0,0);\n\terrorbar = 1; //first is discontinuous\n\tfor (a=0 ; a<NET_TIMINGS ; a++)\n\t{\n\t\tVector2Copy(p[3], p[0]);\tVector4Copy(rgba[3], rgba[0]);\n\t\tVector2Copy(p[2], p[1]);\tVector4Copy(rgba[2], rgba[1]);\n\n\t\tVector2Set(p[2+0], x+a,\t\ty+(1-ngraph[a].height)*NET_GRAPHHEIGHT);\n\t\tVector2Set(p[2+1], x+a,\t\ty+NET_GRAPHHEIGHT);\n\n\t\tVector2Set(tc[2+0], a/(float)NET_TIMINGS,\t\t(1-ngraph[a].height));\n\t\tVector2Set(tc[2+1], a/(float)NET_TIMINGS,\t\t1);\n\t\tVector4Set(rgba[2+0], ((ngraph[a].col>>0)&0xff)/255.0, ((ngraph[a].col>>8)&0xff)/255.0, ((ngraph[a].col>>16)&0xff)/255.0, ((ngraph[a].col>>24)&0xff)/255.0);\n\t\tVector4Copy(rgba[2+0], rgba[2+1]);\n\n\t\tif (ngraph[a].height==1)\n\t\t\terrorbar = 2;\t//this one and the following should be discontiguous\n\t\tif (errorbar --> 0)\n\t\t{\t//if this is a full-height bar, break the smooth curve and just make it discontinuous\n\t\t\tp[0][1] = p[3][1];\n\t\t\tp[1][1] = p[2][1];\n\t\t\tVector4Copy(rgba[3], rgba[0]);\n\t\t\tVector4Copy(rgba[2], rgba[1]);\n\t\t}\n\n\t\tif (a)\n\t\t\tR2D_Image2dQuad((const vec2_t*)p, (const vec2_t*)tc, (const vec4_t*)rgba, shader_draw_fill);\n\t}\n#endif\n}\n\nvoid R_FrameTimeGraph (float frametime, float scale)\n{\n\tfloat bias = 0, h, lh;\n\tint\t\ta, b, x, i, y;\n\n\tstruct{\n\t\tvec2_t xy[4];\n\t\tvec2_t tc[4];\n\t\tvec4_t rgba[4];\n\t} g[3];\n\textern shader_t *shader_draw_fill;\n\n\tconchar_t line[128];\n\tint textheight;\n\tfloat minv=FLT_MAX, maxv=FLT_MIN, avg=0, dev=0;\n\n\tstatic struct\n\t{\n\t\tfloat time[countof(g)];\n\t} history[NET_TIMINGS];\n\tstatic unsigned int findex;\n\n#ifdef LOADERTHREAD\n\textern int com_hadwork[WG_COUNT];\n#endif\n\textern double r_loaderstalltime;\n#ifdef HAVE_SERVER\n\textern double server_frametime;\n#endif\n\n\thistory[findex&NET_TIMINGSMASK].time[0] = max(0,frametime);\t//server band\n#ifdef HAVE_SERVER\n\tframetime -= server_frametime; server_frametime = 0;\n#endif\n\n\thistory[findex&NET_TIMINGSMASK].time[1] = max(0,frametime);\t//stalls band\n\tframetime -= r_loaderstalltime; r_loaderstalltime = 0;\n\n\thistory[findex&NET_TIMINGSMASK].time[2] = max(0,frametime);\t//client band (max is needed because we might have been failing to clear the other timers)\n\n\tfindex++;\n\n#ifdef LOADERTHREAD\n\tif (com_hadwork[WG_MAIN])\n\t{\t//recolour the graph red if the main thread processed something from a worker.\n\t\t//show three, because its not so easy to see when its whizzing past.\n\t\tcom_hadwork[WG_MAIN] = 0;\n//\t\thistory[(findex-1)&NET_TIMINGSMASK].col = 0xff0000ff;\n//\t\thistory[(findex-2)&NET_TIMINGSMASK].col = 0xff0000ff;\n//\t\thistory[(findex-3)&NET_TIMINGSMASK].col = 0xff0000ff;\n\t}\n#endif\n\n\tx = 0;\n\tfor (a=0 ; a<NET_TIMINGS ; a++)\n\t{\n\t\tavg     += history[a].time[0];\n\t\tif (minv > history[a].time[0])\n\t\t\tminv = history[a].time[0];\n\t\tif (maxv < history[a].time[0])\n\t\t\tmaxv = history[a].time[0];\n\t}\n\tif (!scale)\n\t{\n\t\tbias = minv;\n\t\tscale = NET_GRAPHHEIGHT/(maxv-minv);\n\t}\n\telse\n\t\tscale *= 1000;\n\tavg/=a;\n\tfor (a = 0; a < NET_TIMINGS; a++)\n\t\tdev += 1000*1000*(history[a].time[0] - avg)*(history[a].time[0] - avg);\n\tdev /= a;\n\tdev = sqrt(dev);\n\n\n\tx =\t((vid.width - 320)>>1);\n\tx=-x;\n\n\ttextheight = 4;\n\ttextheight = ceil(textheight*Font_CharVHeight(font_console)/8)*8;\t//might have a small gap underneath\n\n\ty = vid.height - sb_lines - 16 - NET_GRAPHHEIGHT - textheight;\n\n\tM_DrawTextBox (x, y, NET_TIMINGS/8, (textheight + NET_GRAPHHEIGHT)/8);\n\tx=8;\n\ty += 8;\n\n\tCOM_ParseFunString(CON_WHITEMASK, va(\"mean: %.3ffps (%.3fms)\", 1/avg, 1000*avg), line, sizeof(line), false);\n\tDraw_ExpandedString(font_console, x, y, line);\n\ty += Font_CharVHeight(font_console);\n\tCOM_ParseFunString(CON_WHITEMASK, va(\"fastest: %.3ffps (%.3fms)\", 1/minv, 1000*minv), line, sizeof(line), false);\n\tDraw_ExpandedString(font_console, x, y, line);\n\ty += Font_CharVHeight(font_console);\n\tCOM_ParseFunString(CON_WHITEMASK, va(\"slowest: %.3ffps (%.3fms)\", 1/maxv, 1000*maxv), line, sizeof(line), false);\n\tDraw_ExpandedString(font_console, x, y, line);\n\ty += Font_CharVHeight(font_console);\n\tCOM_ParseFunString(CON_WHITEMASK, va(\"deviation: %.3fms (max %.3fms)\", dev, (maxv-minv)*1000/2), line, sizeof(line), false);\n\tDraw_ExpandedString(font_console, x, y, line);\n\ty += Font_CharVHeight(font_console);\n\n\tfor (b = 0; b < countof(g); b++)\n\t{\n\t\tVector2Set(g[b].xy[2], 0,0);\n\t\tVector2Set(g[b].xy[3], 0,0);\n\t}\n\tfor (a=0 ; a<4 ; a++)\n\t{\n\t\tVector4Set(g[0].rgba[a], 1.0,0.1,0.1,1.0);\t//server = red\n\t\tVector4Set(g[1].rgba[a], 0.1,1.0,0.1,1.0);\t//lightmap/stalls = green\n\t\tVector4Set(g[2].rgba[a], 1.0,1.0,1.0,1.0);\t//client/other = white.\n\t}\n\n\tfor (a=0 ; a<NET_TIMINGS ; a++)\n\t{\n\t\ti = (findex-NET_TIMINGS+a)&(NET_TIMINGS-1);\n\t\tlh = NET_GRAPHHEIGHT;\n\t\tfor (b = countof(g); b-- > 0; lh = h)\n\t\t{\n\t\t\th = (history[i].time[b]-bias) * scale;\n\n\t\t\tif (h > NET_GRAPHHEIGHT)\n\t\t\t\th = NET_GRAPHHEIGHT;\n\t\t\th = (NET_GRAPHHEIGHT-h);\n\n\t\t\tVector2Copy(g[b].xy[3], g[b].xy[0]);\tVector4Copy(g[b].rgba[3], g[b].rgba[0]);\n\t\t\tVector2Copy(g[b].xy[2], g[b].xy[1]);\tVector4Copy(g[b].rgba[2], g[b].rgba[1]);\n\n\t\t\tVector2Set(g[b].xy[2+0], x+a,\t\ty+h);\n\t\t\tVector2Set(g[b].xy[2+1], x+a,\t\ty+lh);\n\n\t\t\tVector2Set(g[b].tc[2+0], x/(float)NET_TIMINGS,\t\t(NET_GRAPHHEIGHT-h)/NET_GRAPHHEIGHT);\n\t\t\tVector2Set(g[b].tc[2+1], x/(float)NET_TIMINGS,\t\t1);\n\n\t\t\tif (a && (h!=lh || g[b].xy[0][1]!=g[b].xy[1][1]))\n\t\t\t\tR2D_Image2dQuad((const vec2_t*)g[b].xy, (const vec2_t*)g[b].tc, (const vec4_t*)g[b].rgba, shader_draw_fill);\n\t\t}\n\t}\n}\n\nvoid R_NetgraphInit(void)\n{\n#ifdef GRAPHTEX\n\tTEXASSIGN(netgraphtexture, Image_CreateTexture(\"***netgraph***\", NULL, IF_UIPIC|IF_NOMIPMAP|IF_CLAMP));\n\tnetgraphshader = R_RegisterShader(\"netgraph\", SUF_NONE,\n\t\t\"{\\n\"\n\t\t\t\"program default2d\\n\"\n\t\t\t\"{\\n\"\n\t\t\t\t\"map $diffuse\\n\"\n\t\t\t\t\"blendfunc blend\\n\"\n\t\t\t\"}\\n\"\n\t\t\"}\\n\"\n\t\t);\n\tnetgraphshader->defaulttextures->base = netgraphtexture;\n#endif\n}\n"
  },
  {
    "path": "engine/gl/gl_rlight.c",
    "content": "/*\nCopyright (C) 1996-1997 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  \n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n*/\n// r_light.c\n\n#include \"quakedef.h\"\n#ifndef SERVERONLY\n#include \"glquake.h\"\n#include \"shader.h\"\n\nextern cvar_t r_shadow_realtime_world, r_shadow_realtime_world_lightmaps;\nextern cvar_t r_hdr_irisadaptation, r_hdr_irisadaptation_multiplier, r_hdr_irisadaptation_minvalue, r_hdr_irisadaptation_maxvalue, r_hdr_irisadaptation_fade_down, r_hdr_irisadaptation_fade_up;\nextern cvar_t mod_lightpoint_distance;\n\nint\tr_dlightframecount;\nint\t\td_lightstylevalue[MAX_NET_LIGHTSTYLES];\t// 8.8 fraction of base light value\n\nvoid R_BumpLightstyles(unsigned int maxstyle)\n{\n\tint style = cl_max_lightstyles;\n\tif (maxstyle >= style)\n\t{\n\t\tZ_ReallocElements((void**)&cl_lightstyle, &cl_max_lightstyles, maxstyle+1, sizeof(*cl_lightstyle));\n\t\tfor (; style < cl_max_lightstyles; style++)\n\t\t\tVectorSet(cl_lightstyle[style].colours, 1,1,1);\n\t}\n}\nvoid R_UpdateLightStyle(unsigned int style, const char *stylestring, float r, float g, float b)\n{\n\tif (style >= MAX_NET_LIGHTSTYLES)\n\t\treturn;\n\n\tR_BumpLightstyles(style);\n\n\tif (!stylestring)\n\t\tstylestring = \"\";\n\n\tQ_strncpyz (cl_lightstyle[style].map,  stylestring, sizeof(cl_lightstyle[style].map));\n\tcl_lightstyle[style].length = Q_strlen(cl_lightstyle[style].map);\n\tif (!cl_lightstyle[style].length)\n\t{\n\t\td_lightstylevalue[style] = 256;\n\t\tVectorSet(cl_lightstyle[style].colours, 1,1,1);\n\t}\n\telse\n\t\tVectorSet(cl_lightstyle[style].colours, r,g,b);\n\tcl_lightstyle[style].colourkey = (int)(cl_lightstyle[style].colours[0]*0x400) ^ (int)(cl_lightstyle[style].colours[1]*0x100000) ^ (int)(cl_lightstyle[style].colours[2]*0x40000000);\n}\n\nvoid Sh_CalcPointLight(vec3_t point, vec3_t light);\nvoid R_UpdateHDR(vec3_t org)\n{\n\tif (r_hdr_irisadaptation.ival && cl.worldmodel && !(r_refdef.flags & RDF_NOWORLDMODEL))\n\t{\n\t\t//fake and lame, but whatever.\n\t\tvec3_t ambient, diffuse, dir;\n\t\tfloat lev = 0;\n\n#ifdef RTLIGHTS\n\t\tSh_CalcPointLight(org, ambient);\n\t\tlev += VectorLength(ambient);\n\n\n\t\tif (!r_shadow_realtime_world.ival || r_shadow_realtime_world_lightmaps.value)\n#endif\n\t\t{\n\t\t\tcl.worldmodel->funcs.LightPointValues(cl.worldmodel, org, ambient, diffuse, dir);\n\t\t\tlev += (VectorLength(ambient) + VectorLength(diffuse))/256;\n\t\t}\n\n\t\tlev += 0.001;\t//no division by 0!\n\t\tlev = r_hdr_irisadaptation_multiplier.value / lev;\n\t\tlev = bound(r_hdr_irisadaptation_minvalue.value, lev, r_hdr_irisadaptation_maxvalue.value);\n\t\tif (lev > r_refdef.playerview->hdr_last + r_hdr_irisadaptation_fade_up.value*host_frametime)\n\t\t\tlev = r_refdef.playerview->hdr_last + r_hdr_irisadaptation_fade_up.value*host_frametime;\n\t\telse if (lev < r_refdef.playerview->hdr_last - r_hdr_irisadaptation_fade_down.value*host_frametime)\n\t\t\tlev = r_refdef.playerview->hdr_last - r_hdr_irisadaptation_fade_down.value*host_frametime;\n\t\tlev = bound(r_hdr_irisadaptation_minvalue.value, lev, r_hdr_irisadaptation_maxvalue.value);\n\t\tr_refdef.playerview->hdr_last = lev;\n\t\tr_refdef.hdr_value = lev;\n\t}\n\telse\n\t\tr_refdef.hdr_value = 1;\n}\n\n/*\n==================\nR_AnimateLight\n==================\n*/\nvoid R_AnimateLight (void)\n{\n\tint\t\t\ti,j;\n\tfloat f;\n\tstatic int fbmodcount;\n\n\n\t//if (r_lightstylescale.value > 2)\n\t\t//r_lightstylescale.value = 2;\n\t\n//\n// light animations\n// 'm' is normal light, 'a' is no light, 'z' is double bright\n\tf = (cl.time*r_lightstylespeed.value);\n\tif (f < 0)\n\t\tf = 0;\n\ti = (int)f;\n\tf -= i;\t//this can require updates at 1000 times a second.. Depends on your framerate of course\n\n\tif (r_fullbright.value)\n\t{\n\t\tfor (j=0 ; j<cl_max_lightstyles ; j++)\n\t\t\td_lightstylevalue[j] = r_fullbright.value*255;\n\t}\n\telse for (j=0 ; j<cl_max_lightstyles ; j++)\n\t{\n\t\tint v1, v2, vd;\n\t\tif (!cl_lightstyle[j].length)\n\t\t{\n\t\t\td_lightstylevalue[j] = ('m'-'a')*22 * r_lightstylescale.value;\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (cl_lightstyle[j].map[0] == '=')\n\t\t{\n\t\t\td_lightstylevalue[j] = atof(cl_lightstyle[j].map+1)*256*r_lightstylescale.value;\n\t\t\tcontinue;\n\t\t}\n\n\t\tv1 = i % cl_lightstyle[j].length;\n\t\tv1 = cl_lightstyle[j].map[v1] - 'a';\n\n\t\tv2 = (i+1) % cl_lightstyle[j].length;\n\t\tv2 = cl_lightstyle[j].map[v2] - 'a';\n\n\t\tvd = v1 - v2;\n\t\tif (!r_lightstylesmooth.ival || vd < -r_lightstylesmooth_limit.ival || vd > r_lightstylesmooth_limit.ival)\n\t\t\td_lightstylevalue[j] = v1*22*r_lightstylescale.value;\n\t\telse\n\t\t\td_lightstylevalue[j] = (v1*(1-f) + v2*(f))*22*r_lightstylescale.value;\n\t}\n\n\tif (r_fullbright.modified != fbmodcount)\n\t{\n\t\tfbmodcount = r_fullbright.modified;\n\t\tfor (j=0 ; j<cl_max_lightstyles ; j++)\n\t\t{\n\t\t\tif (r_fullbright.value)\n\t\t\t\tcl_lightstyle[j].colourkey = 0xff;\n\t\t\telse\n\t\t\t\tcl_lightstyle[j].colourkey = (int)(cl_lightstyle[j].colours[0]*0x400) ^ (int)(cl_lightstyle[j].colours[1]*0x100000) ^ (int)(cl_lightstyle[j].colours[2]*0x40000000);\n\t\t}\n\t}\n}\n\n/*\n=============================================================================\n\nDYNAMIC LIGHTS BLEND RENDERING\n\n=============================================================================\n*/\n\nvoid AddLightBlend (float r, float g, float b, float a2)\n{\n\tfloat\ta;\n\tfloat *sw_blend = r_refdef.playerview->screentint;\n\n\tr = bound(0, r, 1);\n\tg = bound(0, g, 1);\n\tb = bound(0, b, 1);\n\n\tsw_blend[3] = a = sw_blend[3] + a2*(1-sw_blend[3]);\n\n\ta2 = a2/a;\n\n\tsw_blend[0] = sw_blend[0]*(1-a2) + r*a2;\n\tsw_blend[1] = sw_blend[1]*(1-a2) + g*a2;\n\tsw_blend[2] = sw_blend[2]*(1-a2) + b*a2;\n//Con_Printf(\"AddLightBlend(): %4.2f %4.2f %4.2f %4.6f\\n\", v_blend[0], v_blend[1], v_blend[2], v_blend[3]);\n}\n\n#define FLASHBLEND_VERTS 16\nstatic float bubble_sintable[FLASHBLEND_VERTS+1], bubble_costable[FLASHBLEND_VERTS+1];\n\nstatic void R_InitBubble(void)\n{\n\tfloat a;\n\tint i;\n\tfloat *bub_sin, *bub_cos;\n\n\tbub_sin = bubble_sintable;\n\tbub_cos = bubble_costable;\n\n\tfor (i=FLASHBLEND_VERTS ; i>=0 ; i--)\n\t{\n\t\ta = i/(float)FLASHBLEND_VERTS * M_PI*2;\n\t\t*bub_sin++ = sin(a);\n\t\t*bub_cos++ = cos(a);\n\t}\n}\n\navec4_t flashblend_colours[FLASHBLEND_VERTS+1]; \nvecV_t flashblend_vcoords[FLASHBLEND_VERTS+1];\nvec2_t flashblend_tccoords[FLASHBLEND_VERTS+1];\nindex_t flashblend_indexes[FLASHBLEND_VERTS*3];\nindex_t flashblend_fsindexes[6] = {0, 1, 2, 0, 2, 3};\nmesh_t flashblend_mesh;\nmesh_t flashblend_fsmesh;\nshader_t *occluded_shader;\nshader_t *flashblend_shader;\nshader_t *deferredlight_shader[LSHADER_MODES];\n\nvoid R_GenerateFlashblendTexture(void)\n{\n\tfloat dx, dy;\n\tint x, y, a;\n\tunsigned char pixels[32][32][4];\n\tfor (y = 0;y < 32;y++)\n\t{\n\t\tdy = (y - 15.5f) * (1.0f / 16.0f);\n\t\tfor (x = 0;x < 32;x++)\n\t\t{\n\t\t\tdx = (x - 15.5f) * (1.0f / 16.0f);\n\t\t\ta = (int)(((1.0f / (dx * dx + dy * dy + 0.2f)) - (1.0f / (1.0f + 0.2))) * 32.0f / (1.0f / (1.0f + 0.2)));\n\t\t\ta = bound(0, a, 255);\n\t\t\tpixels[y][x][0] = a;\n\t\t\tpixels[y][x][1] = a;\n\t\t\tpixels[y][x][2] = a;\n\t\t\tpixels[y][x][3] = 255;\n\t\t}\n\t}\n\tR_LoadReplacementTexture(\"***flashblend***\", NULL, IF_LINEAR, pixels, 32, 32, TF_RGBA32);\n}\nvoid R_InitFlashblends(void)\n{\n\tint i;\n\tR_InitBubble();\n\tfor (i = 0; i < FLASHBLEND_VERTS; i++)\n\t{\n\t\tflashblend_indexes[i*3+0] = 0;\n\t\tif (i+1 == FLASHBLEND_VERTS)\n\t\t\tflashblend_indexes[i*3+1] = 1;\n\t\telse\n\t\t\tflashblend_indexes[i*3+1] = i+2;\n\t\tflashblend_indexes[i*3+2] = i+1;\n\n\t\tflashblend_tccoords[i+1][0] = 0.5 + bubble_sintable[i]*0.5;\n\t\tflashblend_tccoords[i+1][1] = 0.5 + bubble_costable[i]*0.5;\n\t}\n\tflashblend_tccoords[0][0] = 0.5;\n\tflashblend_tccoords[0][1] = 0.5;\n\tflashblend_mesh.numvertexes = FLASHBLEND_VERTS+1;\n\tflashblend_mesh.xyz_array = flashblend_vcoords;\n\tflashblend_mesh.st_array = flashblend_tccoords;\n\tflashblend_mesh.colors4f_array[0] = flashblend_colours;\n\tflashblend_mesh.indexes = flashblend_indexes;\n\tflashblend_mesh.numindexes = FLASHBLEND_VERTS*3;\n\tflashblend_mesh.istrifan = true;\n\n\tflashblend_fsmesh.numvertexes = 4;\n\tflashblend_fsmesh.xyz_array = flashblend_vcoords;\n\tflashblend_fsmesh.st_array = flashblend_tccoords;\n\tflashblend_fsmesh.colors4f_array[0] = flashblend_colours;\n\tflashblend_fsmesh.indexes = flashblend_fsindexes;\n\tflashblend_fsmesh.numindexes = 6;\n\tflashblend_fsmesh.istrifan = true;\n\n\tR_GenerateFlashblendTexture();\n\n\tflashblend_shader = R_RegisterShader(\"flashblend\", SUF_NONE,\n\t\t\"{\\n\"\n\t\t\t\"program defaultadditivesprite\\n\"\n\t\t\t\"{\\n\"\n\t\t\t\t\"map ***flashblend***\\n\"\t\n\t\t\t\t\"blendfunc gl_one gl_one\\n\"\n\t\t\t\t\"rgbgen vertex\\n\"\n\t\t\t\t\"alphagen vertex\\n\"\n\t\t\t\t\"nodepth\\n\"\n\t\t\t\"}\\n\"\n\t\t\"}\\n\"\n\t\t);\n\toccluded_shader = R_RegisterShader(\"flashblend_occlusiontest\", SUF_NONE,\n\t\t\"{\\n\"\n\t\t\t\"program defaultadditivesprite\\n\"\n\t\t\t\"{\\n\"\n\t\t\t\t\"maskcolor\\n\"\n\t\t\t\t\"maskalpha\\n\"\n\t\t\t\"}\\n\"\n\t\t\"}\\n\"\n\t\t);\n\tmemset(deferredlight_shader, 0, sizeof(deferredlight_shader));\n}\n\nstatic qboolean R_BuildDlightMesh(dlight_t *light, float colscale, float radscale, int dtype)\n{\n\tint\t\ti, j;\n//\tfloat\ta;\n\tvec3_t\tv;\n\tfloat\trad;\n\tfloat\t*bub_sin, *bub_cos;\n\tvec3_t colour;\n\n\tbub_sin = bubble_sintable;\n\tbub_cos = bubble_costable;\n\trad = light->radius * radscale;\n\n\tVectorCopy(light->color, colour);\n\n\tif (light->fov)\n\t{\n\t\tfloat a = -DotProduct(light->axis[0], vpn);\n\t\tcolour[0] *= a;\n\t\tcolour[1] *= a;\n\t\tcolour[2] *= a;\n\t\trad *= a;\n\t\trad *= 0.33;\n\t}\n\tif (light->style>=0 && light->style < countof(d_lightstylevalue))\n\t{\n\t\tcolscale *= d_lightstylevalue[light->style]/255.0f;\n\t}\n\n\tVectorSubtract (light->origin, r_origin, v);\n\tif (dtype != 1 && Length (v) < rad + r_refdef.mindist*2)\n\t{\t// view is inside the dlight\n\t\treturn false;\n\t}\n\n\tflashblend_colours[0][0] = colour[0]*colscale;\n\tflashblend_colours[0][1] = colour[1]*colscale;\n\tflashblend_colours[0][2] = colour[2]*colscale;\n\tflashblend_colours[0][3] = 1;\n\n\tVectorCopy(light->origin, flashblend_vcoords[0]);\n\tfor (i=FLASHBLEND_VERTS ; i>0 ; i--)\n\t{\n\t\tfor (j=0 ; j<3 ; j++)\n\t\t\tflashblend_vcoords[i][j] = light->origin[j] + (vright[j]*(*bub_cos) +\n\t\t\t\t+ vup[j]*(*bub_sin)) * rad;\n\t\tbub_sin++; \n\t\tbub_cos++;\n\t}\n\tif (dtype == 0)\n\t{\n\t\t//flashblend 3d-ish\n\t\tVectorMA(flashblend_vcoords[0], -rad/1.5, vpn, flashblend_vcoords[0]);\n\t}\n\telse if (dtype != 1)\n\t{\n\t\t//prepass lights needs to be fully infront of the light. the glsl is a fullscreen-style effect, but we can benefit from early-z and scissoring\n\t\tvec3_t diff;\n\t\tVectorSubtract(r_origin, light->origin, diff);\n\t\tVectorNormalize(diff);\n\t\tfor (i=0 ; i<=FLASHBLEND_VERTS ; i++)\n\t\t\tVectorMA(flashblend_vcoords[i], rad, diff, flashblend_vcoords[i]);\n\t}\n\treturn true;\n}\n\n/*\n=============\nR_RenderDlights\n=============\n*/\nvoid R_RenderDlights (void)\n{\n\tint\t\ti;\n\tdlight_t\t*l;\n\tvec3_t waste1, waste2;\n\tunsigned int beflags = 0;\n\tfloat intensity, cscale;\n\tqboolean coronastyle;\n\tqboolean flashstyle;\n\tfloat dist;\n\n\tif (!r_coronas.value && !r_flashblend.value)\n\t\treturn;\n\n\tl = cl_dlights+rtlights_first;\n\tfor (i=rtlights_first; i<rtlights_max; i++, l++)\n\t{\n\t\tif (!l->radius)\n\t\t\tcontinue;\n\n\t\tif (l->corona <= 0)\n\t\t\tcontinue;\n\n\t\t//dlights emitting from the local player are not visible as flashblends\n\t\tif (l->key == r_refdef.playerview->viewentity)\n\t\t\tcontinue;\t//was a glow\n\t\tif (l->key == -(r_refdef.playerview->viewentity))\n\t\t\tcontinue;\t//was a muzzleflash\n\n\t\tcoronastyle = (l->flags & (LFLAG_NORMALMODE|LFLAG_REALTIMEMODE)) && r_coronas.value;\n\t\tflashstyle = ((l->flags & LFLAG_FLASHBLEND) && r_flashblend.value);\n\n\t\tif (!coronastyle && !flashstyle)\n\t\t\tcontinue;\n\t\tif (coronastyle && flashstyle)\n\t\t\tflashstyle = false;\n\n\t\tcscale = l->coronascale;\n\t\tintensity = l->corona;// * 0.25;\n\t\tif (coronastyle)\n\t\t\tintensity *= r_coronas.value * r_coronas_intensity.value;\n\t\telse\n\t\t\tintensity *= r_flashblend.value;\n\t\tif (intensity <= 0 || cscale <= 0)\n\t\t\tcontinue;\n\n\t\t//prevent the corona from intersecting with the near clip plane by just fading it away if its too close\n\t\tVectorSubtract(l->origin, r_refdef.vieworg, waste1);\n\t\tdist = VectorLength(waste1);\n\t\tif (dist < r_coronas_mindist.value+r_coronas_fadedist.value)\n\t\t{\n\t\t\tif (dist <= r_coronas_mindist.value)\n\t\t\t\tcontinue;\n\t\t\tintensity *= (dist-r_coronas_mindist.value) / r_coronas_fadedist.value;\n\t\t}\n\n\t\t/*coronas use depth testing to compute visibility*/\n\t\tif (coronastyle)\n\t\t{\n\t\t\tint method;\n\t\t\tif (r_refdef.recurse)\n\t\t\t\tmethod = 1;\t//don't confuse queries... FIXME: splitscreen/PIP will still have issues.\n\t\t\telse if (!*r_coronas_occlusion.string)\n\t\t\t\tmethod = 4;\t//default to using hardware queries where possible.\n\t\t\telse\n\t\t\t\tmethod = r_coronas_occlusion.ival;\n\n\t\t\tswitch(method)\n\t\t\t{\n\t\t\tcase 0:\n\t\t\t\tbreak;\n\t\t\tcase 3:\n#ifdef GLQUAKE\n\t\t\t\tif (qrenderer == QR_OPENGL)\n\t\t\t\t{\n\t\t\t\t\tfloat depth;\n\t\t\t\t\tvec3_t out;\n\t\t\t\t\tfloat v[4], tempv[4];\n\t\t\t\t\tfloat mvp[16];\n\n\t\t\t\t\tv[0] = l->origin[0];\n\t\t\t\t\tv[1] = l->origin[1];\n\t\t\t\t\tv[2] = l->origin[2];\n\t\t\t\t\tv[3] = 1;\n\n\t\t\t\t\tMatrix4_Multiply(r_refdef.m_projection_std, r_refdef.m_view, mvp);\n\t\t\t\t\tMatrix4x4_CM_Transform4(mvp, v, tempv);\n\n\t\t\t\t\ttempv[0] /= tempv[3];\n\t\t\t\t\ttempv[1] /= tempv[3];\n\t\t\t\t\ttempv[2] /= tempv[3];\n\n\t\t\t\t\tout[0] = (1+tempv[0])/2;\n\t\t\t\t\tout[1] = (1+tempv[1])/2;\n\t\t\t\t\tout[2] = (1+tempv[2])/2;\n\n\t\t\t\t\tout[0] = out[0]*r_refdef.pxrect.width + r_refdef.pxrect.x;\n\t\t\t\t\tout[1] = out[1]*r_refdef.pxrect.height + r_refdef.pxrect.y;\n\t\t\t\t\tif (tempv[3] < 0)\n\t\t\t\t\t\tout[2] *= -1;\n\n\t\t\t\t\tif (out[2] < 0)\n\t\t\t\t\t\tcontinue;\n\n\t\t\t\t\t//FIXME: in terms of performance, mixing reads+draws is BAD BAD BAD. SERIOUSLY BAD\n\t\t\t\t\t//it would be an improvement to calculate all of these at once.\n\t\t\t\t\tqglReadPixels(out[0], out[1], 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &depth);\n\t\t\t\t\tif (depth < out[2])\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n#endif\n\t\t\t\t//other renderers fall through\n\t\t\tcase 4:\n#ifdef GLQUAKE\n\t\t\t\tif (qrenderer == QR_OPENGL && qglGenQueriesARB)\n\t\t\t\t{\n\t\t\t\t\tGLuint res;\n\t\t\t\t\tqboolean requery = true;\n\t\t\t\t\tif (r_refdef.recurse)\n\t\t\t\t\t\trequery = false;\n\t\t\t\t\telse if (l->coronaocclusionquery)\n\t\t\t\t\t{\n\t\t\t\t\t\tqglGetQueryObjectuivARB(l->coronaocclusionquery, GL_QUERY_RESULT_AVAILABLE_ARB, &res);\n\t\t\t\t\t\tif (res)\n\t\t\t\t\t\t\tqglGetQueryObjectuivARB(l->coronaocclusionquery, GL_QUERY_RESULT_ARB, &l->coronaocclusionresult);\n\t\t\t\t\t\telse if (!l->coronaocclusionresult)\n\t\t\t\t\t\t\tcontinue;\t//query still running, nor currently visible.\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\trequery = false;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tqglGenQueriesARB(1, &l->coronaocclusionquery);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (requery)\n\t\t\t\t\t{\n\t\t\t\t\t\tqglBeginQueryARB(GL_SAMPLES_PASSED_ARB, l->coronaocclusionquery);\n\t\t\t\t\t\tR_BuildDlightMesh (l, intensity*10, cscale*.1, coronastyle);\n\t\t\t\t\t\tBE_DrawMesh_Single(occluded_shader, &flashblend_mesh, NULL, beflags);\n\t\t\t\t\t\tqglEndQueryARB(GL_SAMPLES_PASSED_ARB);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (!l->coronaocclusionresult)\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n#endif\n\t\t\t\t//other renderers fall through\n\t\t\tdefault:\n\t\t\tcase 1:\t//bsp-only\n\t\t\tcase 2:\t//non-bsp too\n\t\t\t\tif (TraceLineR(r_refdef.vieworg, l->origin, waste1, waste2, method!=2))\n\t\t\t\t\tcontinue;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tif (!R_BuildDlightMesh (l, intensity, cscale, coronastyle) && !coronastyle)\n\t\t\tAddLightBlend (l->color[0], l->color[1], l->color[2], l->radius * 0.0003);\n\t\telse\n\t\t\tBE_DrawMesh_Single(flashblend_shader, &flashblend_mesh, NULL, (coronastyle?BEF_FORCENODEPTH|BEF_FORCEADDITIVE:0)|beflags);\n\t}\n}\n\n\nqboolean Sh_GenerateShadowMap(dlight_t *l, int lightflags);\nqboolean Sh_CullLight(dlight_t *dl, qbyte *vvis);\nvoid R_GenDlightMesh(struct batch_s *batch)\n{\n\tstatic mesh_t *meshptr;\n\tdlight_t\t*l = cl_dlights + batch->user.dlight.lightidx;\n\tvec3_t colour;\n\n\tint lightflags = batch->user.dlight.lightmode;\n\n\tVectorCopy(l->color, colour);\n\tif (l->style>=0 && l->style < cl_max_lightstyles)\n\t{\n\t\tcolour[0] *= cl_lightstyle[l->style].colours[0] * d_lightstylevalue[l->style]/255.0f;\n\t\tcolour[1] *= cl_lightstyle[l->style].colours[1] * d_lightstylevalue[l->style]/255.0f;\n\t\tcolour[2] *= cl_lightstyle[l->style].colours[2] * d_lightstylevalue[l->style]/255.0f;\n\t}\n\telse\n\t{\n\t\tcolour[0] *= r_lightstylescale.value;\n\t\tcolour[1] *= r_lightstylescale.value;\n\t\tcolour[2] *= r_lightstylescale.value;\n\t}\n\n\tif (colour[0] < 0.001 && colour[1] < 0.001 && colour[2] < 0.001)\n\t{\t//just switch these off.\n\t\tbatch->meshes = 0;\n\t\treturn;\n\t}\n\n\tBE_SelectDLight(l, colour, l->axis, lightflags);\n#ifdef RTLIGHTS\n\tif (lightflags & LSHADER_SMAP)\n\t{\n\t\tif (!Sh_GenerateShadowMap(l, lightflags))\n\t\t{\n\t\t\tbatch->meshes = 0;\n\t\t\treturn;\n\t\t}\n\t\tBE_SelectEntity(&r_worldentity);\n\t\tBE_SelectMode(BEM_STANDARD);\n\t}\n\telse if (Sh_CullLight(l, r_refdef.scenevis))\n\t{\n\t\tbatch->meshes = 0;\n\t\treturn;\n\t}\n#endif\n\n\tif (!R_BuildDlightMesh (l, 2, 1, 2))\n\t{\n\t\tint i;\n\t\tstatic vec2_t s[4] = {{1, -1}, {-1, -1}, {-1, 1}, {1, 1}};\n\t\tfor (i = 0; i < 4; i++)\n\t\t{\n\t\t\tVectorMA(r_origin, 32, vpn, flashblend_vcoords[i]);\n\t\t\tVectorMA(flashblend_vcoords[i], s[i][0]*320, vright, flashblend_vcoords[i]);\n\t\t\tVectorMA(flashblend_vcoords[i], s[i][1]*320, vup, flashblend_vcoords[i]);\n\t\t}\n\n\t\tmeshptr = &flashblend_fsmesh;\n\t}\n\telse\n\t{\n\t\tmeshptr = &flashblend_mesh;\n\t}\n\tbatch->mesh = &meshptr;\n\n\tRQuantAdd(RQUANT_RTLIGHT_DRAWN, 1);\n}\nvoid R_GenDlightBatches(batch_t *batches[])\n{\n#ifdef RTLIGHTS\n\tint i, j, sort;\n\tdlight_t\t*l;\n\tbatch_t\t\t*b;\n\tint lmode;\n\tunsigned modes;\n\textern cvar_t r_shadow_realtime_dlight;\n\textern cvar_t r_shadow_realtime_world;\n\tif (!r_lightprepass)\n\t\treturn;\n\n\tmodes = 0;\n\tif (r_shadow_realtime_dlight.ival)\n\t\tmodes |= LFLAG_NORMALMODE;\n\tif (r_shadow_realtime_world.ival)\n\t\tmodes |= LFLAG_REALTIMEMODE;\n\tif (!modes)\n\t\treturn;\n\n\n\tif (!deferredlight_shader[0])\n\t{\n\t\tconst char *deferredlight_shader_code = \n\t\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\t\"deferredlight\\n\"\n\t\t\t\t\t\t\t\"surfaceparm nodlight\\n\"\n\t\t\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\t\t\"program lpp_light#USE_ARB_SHADOW\\n\"\n\t\t\t\t\t\t\t\t\"blendfunc gl_one gl_one\\n\"\n\t\t\t\t\t\t\t\t\"nodepthtest\\n\"\n\t\t\t\t\t\t\t\t\"map $gbuffer0\\n\"\t//depth\n\t\t\t\t\t\t\t\t\"map $gbuffer1\\n\"\t//normals.rgb specexp.a\n\t\t\t\t\t\t\t\"}\\n\"\n\t\t\t\t\t\t\"}\\n\"\n\t\t\t;\n\t\tdeferredlight_shader[0] = R_RegisterShader(\"deferredlight\", SUF_NONE, deferredlight_shader_code);\n#ifdef RTLIGHTS\n\t\tdeferredlight_shader[LSHADER_SMAP] = R_RegisterShader(\"deferredlight#PCF\", SUF_NONE, deferredlight_shader_code);\n#endif\n\t}\n\n\tl = cl_dlights+rtlights_first;\n\tfor (i=rtlights_first; i<rtlights_max; i++, l++)\n\t{\n\t\tif (!l->radius)\n\t\t\tcontinue;\n\n\t\tif (!(modes & l->flags))\n\t\t\tcontinue;\n\n\t\tif (R_CullSphere(l->origin, l->radius))\n\t\t{\n\t\t\tRQuantAdd(RQUANT_RTLIGHT_CULL_FRUSTUM, 1);\n\t\t\tcontinue;\n\t\t}\n\n\t\tlmode = 0;\n#ifdef RTLIGHTS\n\t\tif (!(((i >= RTL_FIRST)?!r_shadow_realtime_world_shadows.ival:!r_shadow_realtime_dlight_shadows.ival) || l->flags & LFLAG_NOSHADOWS))\n\t\t\tlmode |= LSHADER_SMAP;\n#endif\n//\t\tif (TEXLOADED(l->cubetexture))\n//\t\t\tlmode |= LSHADER_CUBE;\n\n\t\tb = BE_GetTempBatch();\n\t\tif (!b)\n\t\t\treturn;\n\n\t\tb->flags = 0;\n\t\tb->shader = deferredlight_shader[lmode];\n\t\tsort = b->shader->sort;\n\t\tb->buildmeshes = R_GenDlightMesh;\n\t\tb->ent = &r_worldentity;\n\t\tb->mesh = NULL;\n\t\tb->firstmesh = 0;\n\t\tb->meshes = 1;\n\t\tb->skin = NULL;\n\t\tb->texture = NULL;\n\t\tfor (j = 0; j < MAXRLIGHTMAPS; j++)\n\t\t\tb->lightmap[j] = -1;\n\t\tb->user.dlight.lightidx = i;\n\t\tb->user.dlight.lightmode = lmode;\n\t\tb->flags |= BEF_NOSHADOWS|BEF_NODLIGHT;\t//that would be weeird\n\t\tb->vbo = NULL;\n\t\tb->next = batches[sort];\n\t\tbatches[sort] = b;\n\t}\n#endif\n}\n\n/*\n=============================================================================\n\nDYNAMIC LIGHTS\n\n=============================================================================\n*/\n\n/*\n=============\nR_PushDlights\n=============\n*/\nvoid R_PushDlights (void)\n{\n\tint\t\ti;\n\tdlight_t\t*l;\n\n\tr_dlightframecount++;\t// because the count hasn't\n\t\t\t\t\t\t\t\t\t\t\t//  advanced yet for this frame\n\n#ifdef RTLIGHTS\n\t/*if we're doing full rtlighting only, then don't bother calculating old-style dlights as they won't be visible anyway*/\n\tif (r_shadow_realtime_world.ival && r_shadow_realtime_world_lightmaps.value < 0.1)\n\t\treturn;\n#endif\n\n\tif (!r_dlightlightmaps || !r_worldentity.model)\n\t\treturn;\n\n\tif (r_worldentity.model->loadstate != MLS_LOADED)\n\t\treturn;\n\n\tif (!r_worldentity.model->nodes)\n\t\treturn;\n\n\tcurrentmodel = r_worldentity.model;\n\tif (!currentmodel->funcs.MarkLights)\n\t\treturn;\n\t\n\tl = cl_dlights+rtlights_first;\n\tfor (i=rtlights_first ; i <= DL_LAST ; i++, l++)\n\t{\n\t\tif (!l->radius || !(l->flags & LFLAG_LIGHTMAP))\n\t\t\tcontinue;\n\t\tif ((l->flags & LFLAG_NORMALMODE) && r_shadow_realtime_dlight.ival)\n\t\t\tcontinue;\t//don't draw both. its redundant and a waste of cpu.\n\t\tif ((l->flags & LFLAG_REALTIMEMODE) && r_shadow_realtime_world.ival)\n\t\t\tcontinue;\t//also don't draw both.\n\t\tcurrentmodel->funcs.MarkLights( l, (dlightbitmask_t)1u<<i, currentmodel->nodes );\n\t}\n}\n\n\n\n/////////////////////////////////////////////////////////////\n//rtlight loading\n\n#ifdef RTLIGHTS\n//These affect importing\nstatic cvar_t r_editlights_import_radius\t\t\t= CVARAD (\"r_editlights_import_radius\", \"1\", \"r_editlights_quakelightsizescale\", \"changes size of light entities loaded from a map\");\nstatic cvar_t r_editlights_import_ambient\t\t\t= CVARD (\"r_editlights_import_ambient\", \"0\", \"ambient light scaler for imported lights\");\nstatic cvar_t r_editlights_import_diffuse\t\t\t= CVARD (\"r_editlights_import_diffuse\", \"1\", \"diffuse light scaler for imported lights\");\nstatic cvar_t r_editlights_import_specular\t\t\t= CVARD (\"r_editlights_import_specular\", \"1\", \"specular light scaler for imported lights\");\t//excessive, but noticable. its called stylized, okay? shiesh, some people\n\n//these are just for the crappy editor\nstatic cvar_t r_editlights\t\t\t\t\t\t\t= CVARD (\"r_editlights\", \"0\", \"enables .rtlights file editing mode. Consider using csaddon/equivelent instead.\");\nstatic cvar_t r_editlights_cursordistance\t\t\t= CVARD (\"r_editlights_cursordistance\", \"1024\", \"maximum distance of cursor from eye\");\nstatic cvar_t r_editlights_cursorpushoff\t\t\t= CVARD (\"r_editlights_cursorpushoff\", \"4\", \"how far to push the cursor off the impacted surface\");\nstatic cvar_t r_editlights_cursorpushback\t\t\t= CVARD (\"r_editlights_cursorpushback\", \"0\", \"how far to pull the cursor back toward the eye, for some reason\");\nstatic cvar_t r_editlights_cursorgrid\t\t\t\t= CVARD (\"r_editlights_cursorgrid\", \"1\", \"snaps cursor to this grid size\");\n\n//internal settings\nstatic qboolean r_editlights_locked = false;\t\t//don't change the selected light\nstatic int r_editlights_selected = -1;\t\t\t\t//the light closest to the cursor\nstatic vec3_t r_editlights_cursor;\t\t\t\t\t//the position of the crosshair/cursor (new lights will be spawned here)\nstatic dlight_t r_editlights_copybuffer;\t\t\t//written by r_editlights_copyinfo, read by r_editlights_pasteinfo. FIXME: use system clipboard?\n\nqboolean R_ImportRTLights(const char *entlump, int importmode)\n{\n\ttypedef enum lighttype_e {LIGHTTYPE_MINUSX, LIGHTTYPE_RECIPX, LIGHTTYPE_RECIPXX, LIGHTTYPE_INFINITE, LIGHTTYPE_LOCALMIN, LIGHTTYPE_RECIPXX2, LIGHTTYPE_SUN} lighttype_t;\n\n\t/*I'm using the DP code so I know I'll get the DP results*/\n\tint entnum, style, islight, skin, pflags, n;\n\tlighttype_t type;\n\tfloat origin[3], angles[3], mangle[3], radius, color[3], light[4], fadescale, lightscale, originhack[3], overridecolor[3], colourscales[3], vec[4];\n\tchar key[256], value[8192];\n\tchar targetname[256], target[256];\n\tint nest;\n\tqboolean okay = false;\n\tinfobuf_t targets;\n\tconst char *lmp;\n\tqboolean rerelease = false;\n\tfloat fade[2];\n\tmemset(&targets, 0, sizeof(targets));\n\n\t//a quick note about tenebrae:\n\t//by default, tenebrae's rtlights come from the server via static entities, which is all fancy and posh and actually fairly nice... if all servers actually did it.\n\t//(the tenebrae gamecode uses spawnflag 2048 for static lights. note the pflags_fulldynamic fte/dp vs tenebrae difference)\n\t//failing that, it will insert lights with some crappy fixed radius around only all 'classname light' entities, without any colours or anything, vanilla only.\n\t//such lights are ONLY created if they're not near some other existing light (like a static entity one).\n\t//this can result in FTE having noticably more and bigger lights than tenebrae. shadowmapping doesn't help performance either.\n\n\t//handle doom3's header\n\tCOM_Parse(entlump);\n\tif (!strcmp(com_token, \"Version\"))\n\t{\n\t\tentlump = COM_Parse(entlump);\n\t\tentlump = COM_Parse(entlump);\n\t}\n\n\t//find targetnames, and store their origins so that we can deal with spotlights.\n\tfor (lmp = entlump; ;)\n\t{\n\t\tlmp = COM_Parse(lmp);\n\t\tif (com_token[0] != '{')\n\t\t\tbreak;\n\n\t\t*targetname = 0;\n\t\tVectorClear(origin);\n\n\t\tnest = 1;\n\t\twhile (1)\n\t\t{\n\t\t\tlmp = COM_ParseOut(lmp, key, sizeof(key));\n\t\t\tif (!lmp)\n\t\t\t\tbreak; // error\n\t\t\tif (key[0] == '{')\n\t\t\t{\n\t\t\t\tnest++;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (key[0] == '}')\n\t\t\t{\n\t\t\t\tnest--;\n\t\t\t\tif (!nest)\n\t\t\t\t\tbreak; // end of entity\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (nest!=1)\n\t\t\t\tcontinue;\n\t\t\tif (key[0] == '_')\n\t\t\t{\n\t\t\t\tif (!strcmp(key+1, \"shadowlight\"))\n\t\t\t\t\trerelease = true;\n\t\t\t\tmemmove(key, key+1, strlen(key));\n\t\t\t}\n\t\t\twhile (key[strlen(key)-1] == ' ') // remove trailing spaces\n\t\t\t\tkey[strlen(key)-1] = 0;\n\t\t\tlmp = COM_ParseOut(lmp, value, sizeof(value));\n\t\t\tif (!lmp)\n\t\t\t\tbreak; // error\n\n\t\t\t// now that we have the key pair worked out...\n\t\t\tif (!strcmp(\"targetname\", key))\n\t\t\t\tQ_strncpyz(targetname, value, sizeof(targetname));\n\t\t\telse if (!strcmp(\"origin\", key))\n\t\t\t\tsscanf(value, \"%f %f %f\", &origin[0], &origin[1], &origin[2]);\n\t\t}\n\t\t//if we found an ent with a targetname and an origin, then record where it was.\n\t\tif (*targetname && (origin[0] || origin[1] || origin[2]))\n\t\t\tInfoBuf_SetStarKey(&targets, targetname, va(\"%f %f %f\", origin[0], origin[1], origin[2]));\n\t}\n\n\tif (!importmode && !rerelease)\n\t{\n\t\tInfoBuf_Clear(&targets, true);\n\t\treturn false;\t//don't make it up from legacy ents.\n\t}\n\n\tfor (entnum = 0; ;entnum++)\n\t{\n\t\tentlump = COM_Parse(entlump);\n\t\tif (com_token[0] != '{')\n\t\t\tbreak;\n\n\t\ttype = LIGHTTYPE_MINUSX;\n\t\torigin[0] = origin[1] = origin[2] = 0;\n\t\toriginhack[0] = originhack[1] = originhack[2] = 0;\n\t\tangles[0] = angles[1] = angles[2] = 0;\n\t\tmangle[0] = mangle[1] = mangle[2] = 0;\n\t\tcolor[0] = color[1] = color[2] = 1;\n\t\tlight[0] = light[1] = light[2] = 1;light[3] = 300;\n\t\toverridecolor[0] = overridecolor[1] = overridecolor[2] = 1;\n\t\tfadescale = 1;\n\t\tlightscale = 1;\n\t\t*target = 0;\n\t\tstyle = 0;\n\t\tskin = 0;\n\t\tpflags = 0;\n\t\tfade[0] = fade[1] = 0;\n\t\tVectorSet(colourscales, r_editlights_import_ambient.value, r_editlights_import_diffuse.value, r_editlights_import_specular.value);\n\t\t//effects = 0;\n\t\tislight = false;\n\t\tnest = 1;\n\t\twhile (1)\n\t\t{\n\t\t\tentlump = COM_Parse(entlump);\n\t\t\tif (!entlump)\n\t\t\t\tbreak; // error\n\t\t\tif (com_token[0] == '{')\n\t\t\t{\n\t\t\t\tnest++;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (com_token[0] == '}')\n\t\t\t{\n\t\t\t\tnest--;\n\t\t\t\tif (!nest)\n\t\t\t\t\tbreak; // end of entity\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (nest!=1)\n\t\t\t\tcontinue;\n\t\t\tif (com_token[0] == '_')\n\t\t\t\tQ_strncpyz(key, com_token + 1, sizeof(key));\n\t\t\telse\n\t\t\t\tQ_strncpyz(key, com_token, sizeof(key));\n\t\t\twhile (key[strlen(key)-1] == ' ') // remove trailing spaces\n\t\t\t\tkey[strlen(key)-1] = 0;\n\t\t\tentlump = COM_Parse(entlump);\n\t\t\tif (!entlump)\n\t\t\t\tbreak; // error\n\t\t\tQ_strncpyz(value, com_token, sizeof(value));\n\n\t\t\tif (rerelease)\n\t\t\t{\n\t\t\t\tif (!strcmp(\"color\", key))\n\t\t\t\t\tsscanf(value, \"%f %f %f\", &light[0], &light[1], &light[2]);\n\t\t\t\telse if (!strcmp(\"shadowlightcull\", key))\n\t\t\t\t\t;//sscanf(value, \"%f %f %f\", &color[0], &color[1], &color[2]);\n\t\t\t\telse if (!strcmp(\"shadowlightresolution\", key))\n\t\t\t\t\t;//sscanf(value, \"%f %f %f\", &color[0], &color[1], &color[2]);\n\t\t\t\telse if (!strcmp(\"shadowlightradius\", key))\n\t\t\t\t\tlight[3] = atof(value);\n\t\t\t\telse if (!strcmp(\"shadowlightstartfadedistance\", key))\n\t\t\t\t\tfade[0] = atof(value);\n\t\t\t\telse if (!strcmp(\"shadowlightendfadedistance\", key))\n\t\t\t\t\tfade[1] = atof(value);\n\t\t\t\telse if (!strcmp(\"shadowlightintensity\", key))\n\t\t\t\t{\n\t\t\t\t\tcolourscales[0] *= atof(value);\n\t\t\t\t\tcolourscales[1] *= atof(value);\n\t\t\t\t\tcolourscales[2] *= atof(value);\n\t\t\t\t}\n\t\t\t\telse if (!strcmp(\"shadowlight\", key))\n\t\t\t\t\tislight = atoi(value);\n\t\t\t\telse if (!strcmp(\"shadowlightstyle\", key))\n\t\t\t\t\tstyle = atoi(value);\n\t\t\t\telse if (!strcmp(\"shadowlightconeangle\", key))\n\t\t\t\t\tangles[1] = atof(value)*2;\n\t\t\t\telse if (!strcmp(\"origin\", key))\n\t\t\t\t\tsscanf(value, \"%f %f %f\", &origin[0], &origin[1], &origin[2]);\n\t\t\t\telse if (!strcmp(\"target\", key))\n\t\t\t\t\tQ_strncpyz(target, value, sizeof(target));\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t// now that we have the key pair worked out...\n\t\t\t\tif (!strcmp(\"light\", key))\n\t\t\t\t{\n\t\t\t\t\tn = sscanf(value, \"%f %f %f %f\", &vec[0], &vec[1], &vec[2], &vec[3]);\n\t\t\t\t\tif (n == 1)\n\t\t\t\t\t{\n\t\t\t\t\t\t// quake\n\t\t\t\t\t\tlight[0] = vec[0] * (1.0f / 256.0f);\n\t\t\t\t\t\tlight[1] = vec[0] * (1.0f / 256.0f);\n\t\t\t\t\t\tlight[2] = vec[0] * (1.0f / 256.0f);\n\t\t\t\t\t\tlight[3] = vec[0];\n\t\t\t\t\t}\n\t\t\t\t\telse if (n == 4)\n\t\t\t\t\t{\n\t\t\t\t\t\t// halflife\n\t\t\t\t\t\tlight[0] = vec[0] * (1.0f / 255.0f);\n\t\t\t\t\t\tlight[1] = vec[1] * (1.0f / 255.0f);\n\t\t\t\t\t\tlight[2] = vec[2] * (1.0f / 255.0f);\n\t\t\t\t\t\tlight[3] = vec[3];\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse if (!strcmp(\"delay\", key))\n\t\t\t\t\ttype = atoi(value);\n\t\t\t\telse if (!strcmp(\"origin\", key))\n\t\t\t\t\tsscanf(value, \"%f %f %f\", &origin[0], &origin[1], &origin[2]);\n\t\t\t\telse if (!strcmp(\"angle\", key))\t//orientation for cubemaps (or angle of spot lights)\n\t\t\t\t\tangles[0] = 0, angles[1] = atof(value), angles[2] = 0;\n\t\t\t\telse if (!strcmp(\"mangle\", key))\t//orientation for cubemaps (or angle of spot lights)\n\t\t\t\t{\n\t\t\t\t\tsscanf(value, \"%f %f %f\", &mangle[1], &mangle[0], &mangle[2]);\t//FIXME: order is fucked.\n\t\t\t\t\tmangle[0] = 360-mangle[0];\t//FIXME: pitch is fucked too.\n\t\t\t\t}\n\t\t\t\t//_softangle -- the inner cone angle of a spotlight.\n\t\t\t\telse if (!strcmp(\"angles\", key))\t//richer cubemap orientation.\n\t\t\t\t\tsscanf(value, \"%f %f %f\", &angles[0], &angles[1], &angles[2]);\n\t\t\t\telse if (!strcmp(\"color\", key))\n\t\t\t\t\tsscanf(value, \"%f %f %f\", &color[0], &color[1], &color[2]);\n\t\t\t\telse if (!strcmp(\"wait\", key))\n\t\t\t\t\tfadescale = atof(value);\n\t\t\t\telse if (!strcmp(\"target\", key))\n\t\t\t\t\tQ_strncpyz(target, value, sizeof(target));\n\t\t\t\telse if (!strcmp(\"classname\", key))\n\t\t\t\t{\n\t\t\t\t\tif (!strncmp(value, \"light\", 5))\n\t\t\t\t\t{\n\t\t\t\t\t\tislight = true;\n\t\t\t\t\t\tif (!strcmp(value, \"light_fluoro\"))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\toriginhack[0] = 0;\n\t\t\t\t\t\t\toriginhack[1] = 0;\n\t\t\t\t\t\t\toriginhack[2] = 0;\n\t\t\t\t\t\t\toverridecolor[0] = 1;\n\t\t\t\t\t\t\toverridecolor[1] = 1;\n\t\t\t\t\t\t\toverridecolor[2] = 1;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (!strcmp(value, \"light_fluorospark\"))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\toriginhack[0] = 0;\n\t\t\t\t\t\t\toriginhack[1] = 0;\n\t\t\t\t\t\t\toriginhack[2] = 0;\n\t\t\t\t\t\t\toverridecolor[0] = 1;\n\t\t\t\t\t\t\toverridecolor[1] = 1;\n\t\t\t\t\t\t\toverridecolor[2] = 1;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (!strcmp(value, \"light_globe\"))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\toriginhack[0] = 0;\n\t\t\t\t\t\t\toriginhack[1] = 0;\n\t\t\t\t\t\t\toriginhack[2] = 0;\n\t\t\t\t\t\t\toverridecolor[0] = 1;\n\t\t\t\t\t\t\toverridecolor[1] = 0.8;\n\t\t\t\t\t\t\toverridecolor[2] = 0.4;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (!strcmp(value, \"light_flame_large_yellow\"))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\toriginhack[0] = 0;\n\t\t\t\t\t\t\toriginhack[1] = 0;\n\t\t\t\t\t\t\toriginhack[2] = 0;\n\t\t\t\t\t\t\toverridecolor[0] = 1;\n\t\t\t\t\t\t\toverridecolor[1] = 0.5;\n\t\t\t\t\t\t\toverridecolor[2] = 0.1;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (!strcmp(value, \"light_flame_small_yellow\"))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\toriginhack[0] = 0;\n\t\t\t\t\t\t\toriginhack[1] = 0;\n\t\t\t\t\t\t\toriginhack[2] = 0;\n\t\t\t\t\t\t\toverridecolor[0] = 1;\n\t\t\t\t\t\t\toverridecolor[1] = 0.5;\n\t\t\t\t\t\t\toverridecolor[2] = 0.1;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (!strcmp(value, \"light_torch_small_white\"))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\toriginhack[0] = 0;\n\t\t\t\t\t\t\toriginhack[1] = 0;\n\t\t\t\t\t\t\toriginhack[2] = 0;\n\t\t\t\t\t\t\toverridecolor[0] = 1;\n\t\t\t\t\t\t\toverridecolor[1] = 0.5;\n\t\t\t\t\t\t\toverridecolor[2] = 0.1;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (!strcmp(value, \"light_torch_small_walltorch\"))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\toriginhack[0] = 0;\n\t\t\t\t\t\t\toriginhack[1] = 0;\n\t\t\t\t\t\t\toriginhack[2] = 0;\n\t\t\t\t\t\t\toverridecolor[0] = 1;\n\t\t\t\t\t\t\toverridecolor[1] = 0.5;\n\t\t\t\t\t\t\toverridecolor[2] = 0.1;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse if (!strcmp(\"style\", key))\n\t\t\t\t\tstyle = atoi(value);\n\t\t\t\telse if (!strcmp(\"skin\", key))\n\t\t\t\t\tskin = (int)atof(value);\n\t\t\t\telse if (!strcmp(\"pflags\", key))\n\t\t\t\t\tpflags = (int)atof(value);\n\t\t\t\t//else if (!strcmp(\"effects\", key))\n\t\t\t\t\t//effects = (int)atof(value);\n\n\t\t\t\telse if (!strcmp(\"scale\", key))\n\t\t\t\t\tlightscale = atof(value);\n\t\t\t\telse if (!strcmp(\"fade\", key))\n\t\t\t\t\tfadescale = atof(value);\n\n#ifdef MAP_PROC\n\t\t\t\telse if (!strcmp(\"nodynamicshadows\", key))\t//doom3\n\t\t\t\t\t;\n\t\t\t\telse if (!strcmp(\"noshadows\", key))\t//doom3\n\t\t\t\t{\n\t\t\t\t\tif (atof(value))\n\t\t\t\t\t\tpflags |= PFLAGS_NOSHADOW;\n\t\t\t\t}\n\t\t\t\telse if (!strcmp(\"nospecular\", key))//doom3\n\t\t\t\t{\n\t\t\t\t\tif (atof(value))\n\t\t\t\t\t\tcolourscales[2] = 0;\n\t\t\t\t}\n\t\t\t\telse if (!strcmp(\"nodiffuse\", key))\t//doom3\n\t\t\t\t{\n\t\t\t\t\tif (atof(value))\n\t\t\t\t\t\tcolourscales[1] = 0;\n\t\t\t\t}\n#endif\n\t\t\t\telse if (!strcmp(\"light_radius\", key))\n\t\t\t\t{\n\t\t\t\t\tlight[0] = 1;\n\t\t\t\t\tlight[1] = 1;\n\t\t\t\t\tlight[2] = 1;\n\t\t\t\t\tlight[3] = atof(value);\n\t\t\t\t}\n\t\t\t\telse if (entnum == 0 && !strcmp(\"noautolight\", key))\n\t\t\t\t{\n\t\t\t\t\t//tenebrae compat. don't generate rtlights automagically if the world entity specifies this.\n\t\t\t\t\tif (atoi(value))\n\t\t\t\t\t{\n\t\t\t\t\t\tokay = true;\n\t\t\t\t\t\tInfoBuf_Clear(&targets, true);\n\t\t\t\t\t\treturn okay;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse if (entnum == 0 && !strcmp(\"lightmapbright\", key))\n\t\t\t\t{\n\t\t\t\t\t//tenebrae compat. this overrides r_shadow_realtime_world_lightmap\n\t\t\t\t\tr_shadow_realtime_world_lightmaps_force = atof(value);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (!islight)\n\t\t\tcontinue;\n\t\tif (lightscale <= 0)\n\t\t\tlightscale = 1;\n\t\tif (fadescale <= 0)\n\t\t\tfadescale = 1;\n\t\tif (color[0] >= 16 || color[1] >= 16 || color[2] >= 16)\t//_color 255 255 255 should be identity, not super-oversaturated.\n\t\t\tVectorScale(color, 1/255.0, color);\t\t\t\t\t//if only there were standards for this sort of thing.\n\t\tif (color[0] == color[1] && color[0] == color[2])\n\t\t{\n\t\t\tcolor[0] *= overridecolor[0];\n\t\t\tcolor[1] *= overridecolor[1];\n\t\t\tcolor[2] *= overridecolor[2];\n\t\t}\n\t\tradius = light[3] * r_editlights_import_radius.value * lightscale / fadescale;\n\t\tcolor[0] = color[0] * light[0];\n\t\tcolor[1] = color[1] * light[1];\n\t\tcolor[2] = color[2] * light[2];\n#define CUTOFF (128.0/255)\n\t\tswitch (type)\n\t\t{\n\t\tcase LIGHTTYPE_MINUSX:\n\t\t\tbreak;\n\t\tcase LIGHTTYPE_RECIPX:\n#if 1\n\t\t\tradius *= 2;\n//\t\t\tVectorScale(color, (1.0f / 16.0f), color);\n#else\n\t\t\t//light util uses something like: cutoff == light/((scaledist*fadescale*radius)/128)\n\t\t\t//radius = light/(cutoff*128*scaledist*fadescale)\n\t\t\tradius = lightscale*r_editlights_import_radius.value*256/(1*fadescale);\n\t\t\tradius = min(radius, 300);\n\t\t\tVectorScale(color, 255/light[3], color);\n#endif\n\t\t\tbreak;\n\t\tcase LIGHTTYPE_RECIPXX:\n\t\tcase LIGHTTYPE_RECIPXX2:\n#if 1\n\t\t\tradius *= 2;\n//\t\t\tVectorScale(color, (1.0f / 16.0f), color);\n#else\n\t\t\t//light util uses something like: cutoff == light/((scaledist*scaledist*fadescale*fadescale*radius*radius)/(128*128))\n\t\t\tradius = lightscale*r_editlights_import_radius.value*sqrt(1/CUTOFF*128*128*1*1*fadescale*fadescale);\n\t\t\tradius = min(radius, 300);\n\t\t\tVectorScale(color, 255/light[3], color);\n#endif\n\t\t\tbreak;\n\t\tdefault:\n\t\tcase LIGHTTYPE_INFINITE:\n\t\t\tradius = FLT_MAX;\t//close enough\n\t\t\tbreak;\n\t\tcase LIGHTTYPE_LOCALMIN:\t//can't support, treat like LIGHTTYPE_MINUSX\n\t\t\tbreak;\n\t\tcase LIGHTTYPE_SUN:\n\t\t\tbreak;\n\t\t}\n\t\t\n\t\tif (rerelease)\n\t\t{\n\t\t\tif (r_shadow_realtime_world_lightmaps_force < 0)\n\t\t\t\tr_shadow_realtime_world_lightmaps_force = 1;\n\t\t}\n\t\telse if (radius < 50)\t//some mappers insist on many tiny lights. such lights can usually get away with no shadows..\n\t\t\tpflags |= PFLAGS_NOSHADOW;\n\n\t\tVectorAdd(origin, originhack, origin);\n\t\tif (radius >= 1 && !(cl.worldmodel->funcs.PointContents(cl.worldmodel, NULL, origin) & FTECONTENTS_SOLID))\n\t\t{\n\t\t\tdlight_t *dl = CL_AllocSlight();\n\t\t\tif (!dl)\n\t\t\t\tbreak;\n\n\t\t\tVectorCopy(origin, dl->origin);\n\t\t\tVectorCopy(angles, dl->angles);\n\t\t\tAngleVectors(dl->angles, dl->axis[0], dl->axis[1], dl->axis[2]);\n\t\t\tVectorInverse(dl->axis[1]);\n\t\t\tdl->radius = radius;\n\t\t\tVectorCopy(color, dl->color);\n\t\t\tdl->flags = 0;\n\t\t\tdl->flags |= rerelease?LFLAG_REALTIMEMODE|LFLAG_NORMALMODE:LFLAG_REALTIMEMODE;\n\t\t\tdl->flags |= (pflags & PFLAGS_CORONA)?LFLAG_FLASHBLEND:0;\n\t\t\tdl->flags |= (pflags & PFLAGS_NOSHADOW)?LFLAG_NOSHADOWS:0;\n\t\t\tdl->style = style;\n\t\t\tdl->fade[0] = fade[0];\n\t\t\tdl->fade[1] = fade[1];\n\t\t\tVectorCopy(colourscales, dl->lightcolourscales);\n\n\t\t\t//handle spotlights.\n\t\t\tif (mangle[0] || mangle[1] || mangle[2])\n\t\t\t{\n\t\t\t\tdl->fov = angles[1];\n\t\t\t\tif (!dl->fov)\t//default is 40, supposedly\n\t\t\t\t\tdl->fov = 40;\n\n\t\t\t\tVectorCopy(mangle, dl->angles);\n\t\t\t\tAngleVectors(dl->angles, dl->axis[0], dl->axis[1], dl->axis[2]);\n\t\t\t\tVectorInverse(dl->axis[1]);\n\t\t\t}\n\t\t\telse if (*target)\n\t\t\t{\n\t\t\t\tlmp = InfoBuf_ValueForKey(&targets, target);\n\t\t\t\tif (*lmp)\n\t\t\t\t{\n\t\t\t\t\tdl->fov = angles[1];\n\t\t\t\t\tif (!dl->fov)\t//default is 40, supposedly\n\t\t\t\t\t\tdl->fov = 40;\n\t\t\t\t\tsscanf(lmp, \"%f %f %f\", &angles[0], &angles[1], &angles[2]);\n\t\t\t\t\tVectorSubtract(angles, origin, dl->axis[0]);\n\t\t\t\t\tVectorNormalize(dl->axis[0]);\n\t\t\t\t\tVectorVectors(dl->axis[0], dl->axis[1], dl->axis[2]);\n\t\t\t\t\tVectorInverse(dl->axis[1]);\n\t\t\t\t\t//we don't have any control over the inner cone.\n\n\t\t\t\t\t//so queries work properly\n\t\t\t\t\tVectorAngles(dl->axis[0], dl->axis[2], dl->angles, false);\n\t\t\t\t\tdl->angles[0] = anglemod(dl->angles[0]);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (skin >= 16)\n\t\t\t\tR_LoadNumberedLightTexture(dl, skin);\n\n\t\t\tokay = true;\n\t\t}\n\t}\n\n\tInfoBuf_Clear(&targets, true);\n\n\treturn okay;\n}\n\nqboolean R_LoadRTLights(void)\n{\n\tdlight_t *dl;\n\tchar fname[MAX_QPATH];\n\tchar cubename[MAX_QPATH];\n\tchar customstyle[1024];\n\tchar *file;\n\tchar *end;\n\tint style;\n\n\tvec3_t org;\n\tfloat radius;\n\tvec3_t rgb;\n\tvec3_t avel;\n\tfloat fov, nearclip;\n\tunsigned int flags;\n\n\tfloat coronascale;\n\tfloat corona;\n\tfloat ambientscale, diffusescale, specularscale;\n\tvec3_t angles;\n\tfloat fade[2];\n\n\t//delete all old lights, even dynamic ones\n\trtlights_first = RTL_FIRST;\n\trtlights_max = RTL_FIRST;\n\n\tCOM_StripExtension(cl.worldmodel->name, fname, sizeof(fname));\n\tstrncat(fname, \".rtlights\", MAX_QPATH-1);\n\n\tfile = COM_LoadTempFile(fname, 0, NULL);\n\tif (file)\n\twhile(1)\n\t{\n\t\tend = strchr(file, '\\n');\n\t\tif (!end)\n\t\t\tend = file + strlen(file);\n\t\tif (end == file)\n\t\t\tbreak;\n\t\t*end = '\\0';\n\n\t\twhile(*file == ' ' || *file == '\\t')\n\t\t\tfile++;\n\t\tif (*file == '#')\n\t\t{\n\t\t\tfile++;\n\t\t\twhile(*file == ' ' || *file == '\\t')\n\t\t\t\tfile++;\n\t\t\tfile = COM_Parse(file);\n\t\t\tif (!Q_strcasecmp(com_token, \"lightmaps\"))\n\t\t\t{\n\t\t\t\tfile = COM_Parse(file);\n\t\t\t\t//foo = atoi(com_token);\n\t\t\t}\n\t\t\telse\n\t\t\t\tCon_DPrintf(\"Unknown directive: %s\\n\", com_token);\n\t\t\tfile = end+1;\n\t\t\tcontinue;\n\t\t}\n\t\telse if (*file == '!')\n\t\t{\n\t\t\tflags = LFLAG_NOSHADOWS;\n\t\t\tfile++;\n\t\t}\n\t\telse\n\t\t\tflags = 0;\n\n\t\tfile = COM_Parse(file);\n\t\torg[0] = atof(com_token);\n\t\tfile = COM_Parse(file);\n\t\torg[1] = atof(com_token);\n\t\tfile = COM_Parse(file);\n\t\torg[2] = atof(com_token);\n\n\t\tfile = COM_Parse(file);\n\t\tradius = atof(com_token);\n\n\t\tfile = COM_Parse(file);\n\t\trgb[0] = file?atof(com_token):1;\n\t\tfile = COM_Parse(file);\n\t\trgb[1] = file?atof(com_token):1;\n\t\tfile = COM_Parse(file);\n\t\trgb[2] = file?atof(com_token):1;\n\n\t\tfile = COM_Parse(file);\n\t\tstyle = file?atof(com_token):0;\n\n\t\tfile = COM_Parse(file);\n\t\t//cubemap\n\t\tQ_strncpyz(cubename, com_token, sizeof(cubename));\n\n\t\tfile = COM_Parse(file);\n\t\t//corona\n\t\tcorona = file?atof(com_token):0;\n\n\t\tfile = COM_Parse(file);\n\t\tangles[0] = file?atof(com_token):0;\n\t\tfile = COM_Parse(file);\n\t\tangles[1] = file?atof(com_token):0;\n\t\tfile = COM_Parse(file);\n\t\tangles[2] = file?atof(com_token):0;\n\n\t\tfile = COM_Parse(file);\n\t\t//corona scale\n\t\tcoronascale = file?atof(com_token):0.25;\n\n\t\tfile = COM_Parse(file);\n\t\t//ambient\n\t\tambientscale = file?atof(com_token):0;\n\n\t\tfile = COM_Parse(file);\n\t\t//diffuse\n\t\tdiffusescale = file?atof(com_token):1;\n\n\t\tfile = COM_Parse(file);\n\t\t//specular\n\t\tspecularscale = file?atof(com_token):1;\n\n\t\tfile = COM_Parse(file);\n\t\tflags |= file?atoi(com_token):LFLAG_REALTIMEMODE;\n\n\t\tnearclip = fov = avel[0] = avel[1] = avel[2] = fade[0] = fade[1] = 0;\n\t\t*customstyle = 0;\n\t\twhile(file)\n\t\t{\n\t\t\tfile = COM_Parse(file);\n\t\t\tif (!strncmp(com_token, \"rotx=\", 5))\n\t\t\t\tavel[0] = file?atof(com_token+5):0;\n\t\t\telse if (!strncmp(com_token, \"roty=\", 5))\n\t\t\t\tavel[1] = file?atof(com_token+5):0;\n\t\t\telse if (!strncmp(com_token, \"rotz=\", 5))\n\t\t\t\tavel[2] = file?atof(com_token+5):0;\n\t\t\telse if (!strncmp(com_token, \"fov=\", 4))\n\t\t\t\tfov = file?atof(com_token+4):0;\n\t\t\telse if (!strncmp(com_token, \"fademin=\", 8))\n\t\t\t\tfade[0] = file?atof(com_token+8):0;\n\t\t\telse if (!strncmp(com_token, \"fademax=\", 8))\n\t\t\t\tfade[1] = file?atof(com_token+4):0;\n\t\t\telse if (!strncmp(com_token, \"nearclip=\", 9))\n\t\t\t\tnearclip = file?atof(com_token+9):0;\n\t\t\telse if (!strncmp(com_token, \"nostencil=\", 10))\n\t\t\t\tflags |= atoi(com_token+10)?LFLAG_SHADOWMAP:0;\n\t\t\telse if (!strncmp(com_token, \"crepuscular=\", 12))\n\t\t\t\tflags |= atoi(com_token+12)?LFLAG_CREPUSCULAR:0;\n\t\t\telse if (!strncmp(com_token, \"ortho=\", 6))\n\t\t\t\tflags |= atoi(com_token+6)?LFLAG_ORTHO:0;\n\t\t\telse if (!strncmp(com_token, \"stylestring=\", 12))\n\t\t\t\tQ_strncpyz(customstyle, com_token+12, sizeof(customstyle));\n\t\t\telse if (file)\n\t\t\t\tCon_DPrintf(\"Unknown .rtlights arg \\\"%s\\\"\\n\", com_token);\n\t\t}\n\n\t\tif (radius)\n\t\t{\n\t\t\tdl = CL_AllocSlight();\n\t\t\tif (!dl)\n\t\t\t\tbreak;\n\n\t\t\tVectorCopy(org, dl->origin);\n\t\t\tdl->radius = radius;\n\t\t\tVectorCopy(rgb, dl->color);\n\t\t\tdl->corona = corona;\n\t\t\tdl->coronascale = coronascale;\n\t\t\tdl->die = 0;\n\t\t\tdl->flags = flags;\n\t\t\tdl->fov = fov;\n\t\t\tdl->nearclip = nearclip;\n\t\t\tdl->lightcolourscales[0] = ambientscale;\n\t\t\tdl->lightcolourscales[1] = diffusescale;\n\t\t\tdl->lightcolourscales[2] = specularscale;\n\t\t\tdl->fade[0] = fade[0];\n\t\t\tdl->fade[1] = fade[1];\n\t\t\tVectorCopy(angles, dl->angles);\n\t\t\tAngleVectorsFLU(angles, dl->axis[0], dl->axis[1], dl->axis[2]);\n\t\t\tVectorCopy(avel, dl->rotation);\n\n\t\t\tQ_strncpyz(dl->cubemapname, cubename, sizeof(dl->cubemapname));\n\t\t\tif (*dl->cubemapname)\n\t\t\t\tdl->cubetexture = R_LoadReplacementTexture(dl->cubemapname, \"\", IF_TEXTYPE_CUBE, NULL, 0, 0, TF_INVALID);\n\t\t\telse\n\t\t\t\tdl->cubetexture = r_nulltex;\n\n\t\t\tdl->style = style;\n\t\t\tdl->customstyle = (*customstyle)?Z_StrDup(customstyle):NULL;\n\t\t}\n\t\tfile = end+1;\n\t}\n\treturn !!file;\n}\n\nstatic void R_SaveRTLights_f(void)\n{\n\tdlight_t *light;\n\tvfsfile_t *f;\n\tunsigned int i;\n\tchar fname[MAX_QPATH];\n\tchar displayname[MAX_OSPATH];\n\tvec3_t ang;\n\tint ver = 0;\n\tCOM_StripExtension(cl.worldmodel->name, fname, sizeof(fname));\n\tstrncat(fname, \".rtlights\", MAX_QPATH-1);\n\n\tFS_CreatePath(fname, FS_GAMEONLY);\n\tf = FS_OpenVFS(fname, \"wb\", FS_GAMEONLY);\n\tif (!f)\n\t{\n\t\tCon_Printf(\"couldn't open %s\\n\", fname);\n\t\treturn;\n\t}\n\n//\tVFS_PUTS(f, va(\"#lightmap %f\\n\", foo));\n\n\tfor (light = cl_dlights+rtlights_first, i=rtlights_first; i<rtlights_max; i++, light++)\n\t{\n\t\tif (light->die)\n\t\t\tcontinue;\n\t\tif (!light->radius)\n\t\t\tcontinue;\n\t\tVectorAngles(light->axis[0], light->axis[2], ang, false);\n\n\t\t//the .rtlights format is defined by DP, the first few parts cannot be changed without breaking wider compat.\n\t\t//it got extended a few times. only write what we need for greater compat, just in case.\n\t\tif ((light->flags & (LFLAG_SHADOWMAP|LFLAG_CREPUSCULAR|LFLAG_ORTHO)) || light->rotation[0] || light->rotation[1] || light->rotation[2] || light->fov || light->customstyle)\n\t\t\tver = 2;\t//one of our own flags. always spew the full DP stuff to try to avoid confusion\n\t\telse if (light->coronascale!=0.25 || light->lightcolourscales[0]!=0 || light->lightcolourscales[1]!=1 || light->lightcolourscales[2]!=1 || (light->flags&~LFLAG_NOSHADOWS) != LFLAG_REALTIMEMODE)\n\t\t\tver = 2;\n\t\telse if (*light->cubemapname || light->corona || ang[0] || ang[1] || ang[2])\n\t\t\tver = 1;\n\t\telse\n\t\t\tver = 0;\n\t\tVFS_PRINTF(f,\n\t\t\t\"%s%f %f %f \"\n\t\t\t\"%f %f %f %f \"\n\t\t\t\"%i\",\n\t\t\t(light->flags & LFLAG_NOSHADOWS)?\"!\":\"\", light->origin[0], light->origin[1], light->origin[2],\n\t\t\tlight->radius, light->color[0], light->color[1], light->color[2],\n\t\t\tlight->style);\n\t\tif (ver > 0)\n\t\t\tVFS_PRINTF(f, \" \\\"%s\\\" %f %f %f %f\", light->cubemapname, light->corona, ang[0], ang[1], ang[2]);\n\t\tif (ver > 1)\n\t\t\tVFS_PRINTF(f, \" %f %f %f %f %i\", light->coronascale, light->lightcolourscales[0], light->lightcolourscales[1], light->lightcolourscales[2], light->flags&(LFLAG_NORMALMODE|LFLAG_REALTIMEMODE));\n\n\t\t//our weird flags\n\t\tif (light->flags&LFLAG_SHADOWMAP)\n\t\t\tVFS_PRINTF(f, \" nostencil=1\");\n\t\tif (light->flags&LFLAG_CREPUSCULAR)\n\t\t\tVFS_PRINTF(f, \" crepuscular=1\");\n\t\tif (light->flags&LFLAG_ORTHO)\n\t\t\tVFS_PRINTF(f, \" ortho=1\");\n\t\t//spinning lights (for cubemaps)\n\t\tif (light->rotation[0] || light->rotation[1] || light->rotation[2])\n\t\t\tVFS_PRINTF(f, \" rotx=%g roty=%g rotz=%g\", light->rotation[0],light->rotation[1],light->rotation[2]);\n\t\t//spotlights\n\t\tif (light->fov)\n\t\t\tVFS_PRINTF(f, \" fov=%g\", light->fov); //aka: outer cone\n\t\tif (light->nearclip)\n\t\t\tVFS_PRINTF(f, \" nearclip=%g\", light->nearclip); //aka: distance into a wall, for lights that are meant to appear to come from a texture\n\t\tif (light->customstyle)\n\t\t\tVFS_PRINTF(f, \" \\\"stylestring=%s\\\"\", light->customstyle); //aka: outer cone\n\t\tif (light->fade[1]>0)\n\t\t\tVFS_PRINTF(f, \" \\\"fademin=%g\\\" \\\"fademax=%g\\\"\", light->fade[0], light->fade[1]);\n\n\t\tVFS_PUTS(f, \"\\n\");\n\t}\n\tVFS_CLOSE(f);\n\n\tFS_DisplayPath(fname, FS_GAMEONLY, displayname, sizeof(displayname));\n\tCon_Printf(\"rtlights saved to %s\\n\", displayname);\n}\n\nvoid R_StaticEntityToRTLight(int i)\n{\n\tentity_state_t *state = &cl_static_entities[i].state;\n\tdlight_t *dl;\n\tif (!(state->lightpflags&(PFLAGS_FULLDYNAMIC|PFLAGS_CORONA)))\n\t\treturn;\n\tdl = CL_AllocSlight();\n\tif (!dl)\n\t\treturn;\n\tVectorCopy(state->origin, dl->origin);\n\tAngleVectors(state->angles, dl->axis[0], dl->axis[1], dl->axis[2]);\n\tVectorInverse(dl->axis[1]);\n\tdl->radius = state->light[3];\n\tif (!dl->radius)\n\t\tdl->radius = 350;\n\tVectorScale(state->light, 1.0/1024, dl->color);\n\tif (!state->light[0] && !state->light[1] && !state->light[2])\n\t\tVectorSet(dl->color, 1, 1, 1);\n\tdl->flags = 0;\n\tdl->flags |= LFLAG_NORMALMODE|LFLAG_REALTIMEMODE;\n\tdl->flags |= (state->lightpflags & PFLAGS_NOSHADOW)?LFLAG_NOSHADOWS:0;\n\tif (state->lightpflags & PFLAGS_CORONA)\n\t\tdl->corona = 1;\n\tdl->style = state->lightstyle;\n\tif (state->lightpflags & PFLAGS_FULLDYNAMIC)\n\t{\n\t\tdl->lightcolourscales[0] = r_editlights_import_ambient.value;\n\t\tdl->lightcolourscales[1] = r_editlights_import_diffuse.value;\n\t\tdl->lightcolourscales[2] = r_editlights_import_specular.value;\n\t}\n\telse\n\t{\t//corona-only light\n\t\tdl->lightcolourscales[0] = 0;\n\t\tdl->lightcolourscales[1] = 0;\n\t\tdl->lightcolourscales[2] = 0;\n\t}\n\tif (state->skinnum >= 16)\n\t\tR_LoadNumberedLightTexture(dl, state->skinnum);\n}\n\nstatic void R_ReloadRTLights_f(void)\n{\n\tint i;\n\n\tif (!cl.worldmodel)\n\t{\n\t\tCon_Printf(\"Cannot reload lights at this time\\n\");\n\t\treturn;\n\t}\n\trtlights_first = RTL_FIRST;\n\trtlights_max = RTL_FIRST;\n\tr_shadow_realtime_world_lightmaps_force = -1;\n\tif (!strcmp(Cmd_Argv(1), \"bsp\"))\n\t\tR_ImportRTLights(Mod_GetEntitiesString(cl.worldmodel), 1);\n\telse if (!strcmp(Cmd_Argv(1), \"rtlights\"))\n\t\tR_LoadRTLights();\n\telse if (!strcmp(Cmd_Argv(1), \"statics\"))\n\t{\n\t\tfor (i = 0; i < cl.num_statics; i++)\n\t\t\tR_StaticEntityToRTLight(i);\n\t}\n\telse if (!strcmp(Cmd_Argv(1), \"none\"))\n\t\t;\n\telse\n\t{\n\t\t//try to load .rtlights file\n\t\tif (rtlights_first == rtlights_max)\n\t\t\tR_LoadRTLights();\n\t\t//if there's a static entity with rtlights set, then assume the mod is taking care of it for us.\n\t\tif (rtlights_first == rtlights_max)\n\t\t\tfor (i = 0; i < cl.num_statics; i++)\n\t\t\t\tR_StaticEntityToRTLight(i);\n\t\t//otherwise try to import.\n\t\tif (rtlights_first == rtlights_max)\n\t\t\tR_ImportRTLights(Mod_GetEntitiesString(cl.worldmodel), r_shadow_realtime_world_importlightentitiesfrommap.ival);\n\t}\n\n\tif (r_shadow_realtime_world_lightmaps_force >= 0)\n\t\tr_shadow_realtime_world_lightmaps.value = r_shadow_realtime_world_lightmaps_force;\n\telse\n\t\tr_shadow_realtime_world_lightmaps.value = atof(r_shadow_realtime_world_lightmaps.string);\n}\n\n//-1 for arg error\nstatic int R_EditLight(dlight_t *dl, const char *cmd, int argc, const char *x, const char *y, const char *z)\n{\n\tif (argc == 1)\n\t{\n\t\ty = x;\n\t\tz = x;\n\t}\n\tif (!strcmp(cmd, \"origin\"))\n\t{\n\t\tdl->origin[0] = atof(x);\n\t\tdl->origin[1] = atof(y);\n\t\tdl->origin[2] = atof(z);\n\t}\n\telse if (!strcmp(cmd, \"originscale\"))\n\t{\n\t\tdl->origin[0] *= atof(x);\n\t\tdl->origin[1] *= atof(y);\n\t\tdl->origin[2] *= atof(z);\n\t}\n\telse if (!strcmp(cmd, \"originx\"))\n\t\tdl->origin[0] = atof(x);\n\telse if (!strcmp(cmd, \"originy\"))\n\t\tdl->origin[1] = atof(x);\n\telse if (!strcmp(cmd, \"originz\"))\n\t\tdl->origin[2] = atof(x);\n\telse if (!strcmp(cmd, \"move\"))\n\t{\n\t\tdl->origin[0] += atof(x);\n\t\tdl->origin[1] += atof(y);\n\t\tdl->origin[2] += atof(z);\n\t}\n\telse if (!strcmp(cmd, \"movex\"))\n\t\tdl->origin[0] += atof(x);\n\telse if (!strcmp(cmd, \"movey\"))\n\t\tdl->origin[1] += atof(x);\n\telse if (!strcmp(cmd, \"movez\"))\n\t\tdl->origin[2] += atof(x);\n\n\telse if (!strcmp(cmd, \"angles\"))\n\t{\n\t\tdl->angles[0] = atof(x);\n\t\tdl->angles[1] = atof(y);\n\t\tdl->angles[2] = atof(z);\n\n\t\tAngleVectors(dl->angles, dl->axis[0], dl->axis[1], dl->axis[2]);\n\t\tVectorInverse(dl->axis[1]);\n\t}\n\telse if (!strcmp(cmd, \"anglesx\"))\n\t{\n\t\tdl->angles[0] = atof(x);\n\t\tAngleVectors(dl->angles, dl->axis[0], dl->axis[1], dl->axis[2]);\n\t\tVectorInverse(dl->axis[1]);\n\t}\n\telse if (!strcmp(cmd, \"anglesy\"))\n\t{\n\t\tdl->angles[1] = atof(x);\n\t\tAngleVectors(dl->angles, dl->axis[0], dl->axis[1], dl->axis[2]);\n\t\tVectorInverse(dl->axis[1]);\n\t}\n\telse if (!strcmp(cmd, \"anglesz\"))\n\t{\n\t\tdl->angles[2] = atof(x);\n\t\tAngleVectors(dl->angles, dl->axis[0], dl->axis[1], dl->axis[2]);\n\t\tVectorInverse(dl->axis[1]);\n\t}\n\n\telse if (!strcmp(cmd, \"avel\") || !strcmp(cmd, \"spin\"))\n\t{\n\t\tdl->rotation[0] = atof(x);\n\t\tdl->rotation[1] = atof(y);\n\t\tdl->rotation[2] = atof(z);\n\t}\n\telse if (!strcmp(cmd, \"avelx\"))\n\t\tdl->rotation[0] = atof(x);\n\telse if (!strcmp(cmd, \"avey\"))\n\t\tdl->rotation[1] = atof(x);\n\telse if (!strcmp(cmd, \"avelz\"))\n\t\tdl->rotation[2] = atof(x);\n\n\telse if (!strcmp(cmd, \"outercone\") || !strcmp(cmd, \"fov\") || !strcmp(cmd, \"cone\"))\n\t\tdl->fov = atof(x);\n\telse if (!strcmp(cmd, \"nearclip\"))\n\t\tdl->nearclip = atof(x);\n\telse if (!strcmp(cmd, \"color\") || !strcmp(cmd, \"colour\"))\n\t{\n\t\tdl->color[0] = atof(x);\n\t\tdl->color[1] = atof(y);\n\t\tdl->color[2] = atof(z);\n\t}\n\telse if (!strcmp(cmd, \"colorscale\") || !strcmp(cmd, \"colourscale\"))\n\t{\n\t\tdl->color[0] *= atof(x);\n\t\tdl->color[1] *= atof(y);\n\t\tdl->color[2] *= atof(z);\n\t}\n\telse if (!strcmp(cmd, \"radius\"))\n\t\tdl->radius = atof(x);\n\telse if (!strcmp(cmd, \"radiusscale\") || !strcmp(cmd, \"sizescale\"))\n\t\tdl->radius *= atof(x);\n\telse if (!strcmp(cmd, \"style\"))\n\t\tdl->style = atoi(x);\n\telse if (!strcmp(cmd, \"stylestring\"))\n\t{\n\t\tZ_Free(dl->customstyle);\n\t\tdl->customstyle = x?Z_StrDup(x):NULL;\n\t}\n\telse if (!strcmp(cmd, \"cubemap\"))\n\t{\n\t\tQ_strncpyz(dl->cubemapname, x, sizeof(dl->cubemapname));\n\t\tif (*dl->cubemapname)\n\t\t\tdl->cubetexture = R_LoadReplacementTexture(dl->cubemapname, \"\", IF_TEXTYPE_CUBE, NULL, 0, 0, TF_INVALID);\n\t\telse\n\t\t\tdl->cubetexture = r_nulltex;\n\t}\n\telse if (!strcmp(cmd, \"shadows\"))\n\t\tdl->flags = (dl->flags&~LFLAG_NOSHADOWS) | ((*x=='y'||*x=='Y'||*x=='t'||atoi(x))?0:LFLAG_NOSHADOWS);\n\telse if (!strcmp(cmd, \"nostencil\"))\n\t\tdl->flags = (dl->flags&~LFLAG_SHADOWMAP) | ((*x=='y'||*x=='Y'||*x=='t'||atoi(x))?0:LFLAG_SHADOWMAP);\n\telse if (!strcmp(cmd, \"crepuscular\"))\n\t\tdl->flags = (dl->flags&~LFLAG_CREPUSCULAR) | ((*x=='y'||*x=='Y'||*x=='t'||atoi(x))?LFLAG_CREPUSCULAR:0);\n\telse if (!strcmp(cmd, \"ortho\"))\n\t\tdl->flags = (dl->flags&~LFLAG_ORTHO) | ((*x=='y'||*x=='Y'||*x=='t'||atoi(x))?LFLAG_ORTHO:0);\n\telse if (!strcmp(cmd, \"corona\"))\n\t\tdl->corona = atof(x);\n\telse if (!strcmp(cmd, \"coronasize\"))\n\t\tdl->coronascale = atof(x);\n\telse if (!strcmp(cmd, \"ambient\"))\n\t\tdl->lightcolourscales[0] = atof(x);\n\telse if (!strcmp(cmd, \"diffuse\"))\n\t\tdl->lightcolourscales[1] = atof(x);\n\telse if (!strcmp(cmd, \"specular\"))\n\t\tdl->lightcolourscales[2] = atof(x);\n\telse if (!strcmp(cmd, \"normalmode\"))\n\t\tdl->flags = (dl->flags&~LFLAG_NORMALMODE) | ((*x=='y'||*x=='Y'||*x=='t'||atoi(x))?LFLAG_NORMALMODE:0);\n\telse if (!strcmp(cmd, \"realtimemode\"))\n\t\tdl->flags = (dl->flags&~LFLAG_REALTIMEMODE) | ((*x=='y'||*x=='Y'||*x=='t'||atoi(x))?LFLAG_REALTIMEMODE:0);\n\telse\n\t\treturn -2;\n\tdl->rebuildcache = true;\t//mneh, lets just flag it for everything.\n\treturn 1;\n}\n\nvoid R_EditLights_DrawInfo(void)\n{\n\tfloat fontscale[2] = {8,8};\n\tfloat x = vid.width - 320;\n\tfloat y = 0;\n\tconst char *s;\n\tif (!r_editlights.ival)\n\t\treturn;\n\n\tif (r_editlights_selected >= RTL_FIRST && r_editlights_selected < rtlights_max)\n\t{\n\t\tdlight_t *dl = &cl_dlights[r_editlights_selected];\n\t\ts = va(\t\"      Origin : %.0f %.0f %.0f\\n\"\n\t\t\t\t\"      Angles : %.0f %.0f %.0f\\n\"\n\t\t\t\t\"      Colour : %.2f %.2f %.2f\\n\"\n\t\t\t\t\"      Radius : %.0f\\n\"\n\t\t\t\t\"      Corona : %.0f\\n\"\n\t\t\t\t\"       Style : %i\\n\"\n\t\t\t\t\"Style String : %s\\n\"\n\t\t\t\t\"     Shadows : %s\\n\"\n\t\t\t\t\"     Cubemap : \\\"%s\\\"\\n\"\n\t\t\t\t\"  CoronaSize : %.2f\\n\"\n\t\t\t\t\"     Ambient : %.2f\\n\"\n\t\t\t\t\"     Diffuse : %.2f\\n\"\n\t\t\t\t\"    Specular : %.2f\\n\"\n\t\t\t\t\"  NormalMode : %s\\n\"\n\t\t\t\t\"RealTimeMode : %s\\n\"\n\t\t\t\t\"    FadeDist : %.0f-%.0f\\n\"\n\t\t\t\t\"        Spin : %.0f %.0f %.0f\\n\"\n\t\t\t\t\"        Cone : %.0f\\n\"\n\t\t\t\t\"    Nearclip : %.0f\\n\"\n\t\t\t\t//\"NoStencil    : %s\\n\"\n\t\t\t\t//\"Crepuscular  : %s\\n\"\n\t\t\t\t//\"Ortho        : %s\\n\"\n\t\t\t\t,dl->origin[0],dl->origin[1],dl->origin[2]\n\t\t\t\t,dl->angles[0],dl->angles[1],dl->angles[2]\n\t\t\t\t,dl->color[0],dl->color[1],dl->color[2]\n\t\t\t\t,dl->radius, dl->corona, dl->style, dl->customstyle?dl->customstyle:\"---\"\n\t\t\t\t,((dl->flags&LFLAG_NOSHADOWS)?\"no\":\"yes\"), dl->cubemapname, dl->coronascale\n\t\t\t\t,dl->lightcolourscales[0], dl->lightcolourscales[1], dl->lightcolourscales[2]\n\t\t\t\t,((dl->flags&LFLAG_NORMALMODE)?\"yes\":\"no\"), ((dl->flags&LFLAG_REALTIMEMODE)?\"yes\":\"no\")\n\t\t\t\t,dl->fade[0], dl->fade[1]\n\t\t\t\t,dl->rotation[0],dl->rotation[1],dl->rotation[2], dl->fov, dl->nearclip\n\t\t\t\t//,((dl->flags&LFLAG_SHADOWMAP)?\"no\":\"yes\"),((dl->flags&LFLAG_CREPUSCULAR)?\"yes\":\"no\"),((dl->flags&LFLAG_ORTHO)?\"yes\":\"no\")\n\t\t\t\t);\n\t}\n\telse\n\t\ts = \"No light selected\";\n\tR2D_ImageColours(0,0,0,.35);\n\tR2D_FillBlock(x-4, y, 320+4, 16*8+4);\n\tR2D_ImageColours(1,1,1,1);\n\tR_DrawTextField(x, y, 320, 19*8, s, CON_WHITEMASK, CPRINT_LALIGN|CPRINT_TALIGN|CPRINT_NOWRAP, font_default, fontscale);\n}\nvoid R_EditLights_DrawLights(void)\n{\n\tconst float SPRITE_SIZE = 8;\n\tint\t\ti;\n\tdlight_t\t*l;\n\tenum\n\t{\n//\t\tELS_CURSOR,\n\t\tELS_SELECTED,\n\t\tELS_LIGHT,\n\t\tELS_NOSHADOW,\n\t\tELS_MAX\n\t};\n\tchar *lightshaderinfo[] =\n\t{\n/*\t\t\"gfx/editlights/cursor\",\n\t\t\t\".59..95.\"\n\t\t\t\"59....95\"\n\t\t\t\"9.9..9.9\"\n\t\t\t\"...99...\"\n\t\t\t\"...99...\"\n\t\t\t\"9.9..9.9\"\n\t\t\t\"59....95\"\n\t\t\t\".59..95.\",\n*/\n\t\t\"gfx/editlights/selected\",\n\t\t\t\"999..999\"\n\t\t\t\"99....99\"\n\t\t\t\"9......9\"\n\t\t\t\"........\"\n\t\t\t\"........\"\n\t\t\t\"9......9\"\n\t\t\t\"99....99\"\n\t\t\t\"999..999\",\n\n\t\t\"gfx/editlights/light\",\n\t\t\t\"..1221..\"\n\t\t\t\".245542.\"\n\t\t\t\"14677641\"\n\t\t\t\"25799752\"\n\t\t\t\"25799752\"\n\t\t\t\"14677641\"\n\t\t\t\".245542.\"\n\t\t\t\"..1221..\",\n\n\t\t\"gfx/editlights/noshadow\",\n\t\t\t\"..1221..\"\n\t\t\t\".245542.\"\n\t\t\t\"14644641\"\n\t\t\t\"274..472\"\t//mmm, donuts.\n\t\t\t\"274..472\"\n\t\t\t\"14644641\"\n\t\t\t\".247742.\"\n\t\t\t\"..1221..\",\n\t};\n\tshader_t *shaders[ELS_MAX], *s;\n\tunsigned int asciipalette[256];\n\tasciipalette['.'] = 0;\n\tfor (i = 0; i < 10; i++)\n\t\tasciipalette['0'+i] = 0xff000000 | ((int)(255/9.0*i)*0x010101);\n\n\tif (!r_editlights.ival)\n\t\treturn;\n\n\tfor (i = 0; i < ELS_MAX; i++)\n\t{\n\t\tshaders[i] = R_RegisterShader(lightshaderinfo[i*2+0], SUF_NONE, va(\n\t\t\t\t\"{\\n\"\n\t\t\t\t\t\"program defaultadditivesprite\\n\"\n\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\"map $diffuse\\n\"\n\t\t\t\t\t\t\"blendfunc gl_one gl_one\\n\"\n\t\t\t\t\t\t\"rgbgen vertex\\n\"\n\t\t\t\t\t\t\"alphagen vertex\\n\"\n\t\t\t\t\t\t\"%s\"\n\t\t\t\t\t\"}\\n\"\n\t\t\t\t\"}\\n\"\n\t\t\t\t,(i==ELS_SELECTED)?\"nodepth\\n\":\"\")\n\t\t\t);\n\t\tif (!shaders[i]->defaulttextures->base)\n\t\t\tshaders[i]->defaulttextures->base = Image_GetTexture(shaders[i]->name, NULL, IF_LINEAR|IF_NOMIPMAP|IF_NOPICMIP|IF_CLAMP, lightshaderinfo[i*2+1], asciipalette, 8, 8, TF_8PAL32);\n\t}\n\n\tif (!r_editlights_locked)\n\t{\n\t\tvec3_t targ, norm;\n\t\tint ent;\n\t\tint best = -1;\n\t\tfloat bestscore = 0, score;\n\n\t\tVectorMA(r_refdef.vieworg, r_editlights_cursordistance.value, vpn, targ);\t//try to aim about 1024qu infront of the camera\n\t\tCL_TraceLine(r_refdef.vieworg, targ, r_editlights_cursor, norm, &ent);\t\t//figure out where the cursor ends up\n\t\tVectorMA(r_editlights_cursor, r_editlights_cursorpushoff.value, norm, r_editlights_cursor);\t//push off from the surface by 4qu.\n\t\tVectorMA(r_editlights_cursor, -r_editlights_cursorpushback.value, vpn, r_editlights_cursor);//move it back towards the camera, for no apparent reason\n\t\tif (r_editlights_cursorgrid.value)\n\t\t{\t//snap to a grid, if set\n\t\t\tfor (i =0; i < 3; i++)\n\t\t\t\tr_editlights_cursor[i] = floor(r_editlights_cursor[i] / r_editlights_cursorgrid.value + 0.5) * r_editlights_cursorgrid.value;\n\t\t}\n\n//\t\tCLQ1_AddSpriteQuad(shaders[ELS_CURSOR], r_editlights_cursor, SPRITE_SIZE);\n\n\t\tfor (i=RTL_FIRST; i<rtlights_max; i++)\n\t\t{\n\t\t\tl = &cl_dlights[i];\n\t\t\tif (!l->radius)\t//dead light is dead.\n\t\t\t\tcontinue;\n\n\t\t\tVectorSubtract(l->origin, r_refdef.vieworg, targ);\n\t\t\tscore = DotProduct(vpn, targ) / sqrt(DotProduct(targ,targ));\n\t\t\tif (score >= .95)\t//there's a threshhold required for a light to be selectable.\n\t\t\t{\n\t\t\t\t//trace from the light to the view (so startsolid doesn't cause so many problems)\n\t\t\t\tif (score > bestscore && CL_TraceLine(l->origin, r_refdef.vieworg, r_editlights_cursor, norm, &ent) == 1.0)\n\t\t\t\t{\n\t\t\t\t\tbestscore = score;\n\t\t\t\t\tbest = i;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tr_editlights_selected = best;\n\t}\n\n\tfor (i=RTL_FIRST; i<rtlights_max; i++)\n\t{\n\t\tl = &cl_dlights[i];\n\t\tif (!l->radius)\t//dead light is dead.\n\t\t\tcontinue;\n\n\t\t//we should probably show spotlights with a special icon or something\n\t\t//dp has alternate icons for cubemaps.\n\t\tif (l->flags & LFLAG_NOSHADOWS)\n\t\t\ts = shaders[ELS_NOSHADOW];\n\t\telse\n\t\t\ts = shaders[ELS_LIGHT];\n\t\tCLQ1_AddSpriteQuad(s, l->origin, SPRITE_SIZE);\n\t}\n\n\tif (r_editlights_selected >= RTL_FIRST && r_editlights_selected < rtlights_max)\n\t{\n\t\tl = &cl_dlights[r_editlights_selected];\n\t\tCLQ1_AddSpriteQuad(shaders[ELS_SELECTED], l->origin, SPRITE_SIZE);\n\t}\n}\n\nstatic void R_EditLights_Edit_f(void)\n{\n\tint i = r_editlights_selected;\n\tconst char *cmd = Cmd_Argv(1);\n\tconst char *x = Cmd_Argv(2);\n\tconst char *y = Cmd_Argv(3);\n\tconst char *z = Cmd_Argv(4);\n\tint argc = Cmd_Argc()-2;\n\tdlight_t *dl;\n\tif (!r_editlights.ival)\n\t{\n\t\tCon_Printf(\"Toggle r_editlights first\\n\");\n\t\treturn;\n\t}\n\tif (i < RTL_FIRST || i >= rtlights_max)\n\t{\n\t\tCon_Printf(\"No light selected\\n\");\n\t\treturn;\n\t}\n\tdl = &cl_dlights[i];\n\tif (!*cmd)\n\t{\n\t\tCon_Print(\"Selected light's properties:\\n\");\n\t\tCon_Printf(\"Origin       : ^[%f %f %f\\\\type\\\\r_editlights_edit origin %g %g %g^]\\n\", dl->origin[0],dl->origin[1],dl->origin[2], dl->origin[0],dl->origin[1],dl->origin[2]);\n\t\tCon_Printf(\"Angles       : ^[%f %f %f\\\\type\\\\r_editlights_edit angles %g %g %g^]\\n\", dl->angles[0],dl->angles[1],dl->angles[2], dl->angles[0],dl->angles[1],dl->angles[2]);\n\t\tCon_Printf(\"Colour       : ^[%f %f %f\\\\type\\\\r_editlights_edit avel %g %g %g^]\\n\", dl->color[0],dl->color[1],dl->color[2], dl->color[0],dl->color[1],dl->color[2]);\n\t\tCon_Printf(\"Radius       : ^[%f\\\\type\\\\r_editlights_edit radius %g^]\\n\", dl->radius, dl->radius);\n\t\tCon_Printf(\"Corona       : ^[%f\\\\type\\\\r_editlights_edit corona %g^]\\n\", dl->corona, dl->corona);\n\t\tCon_Printf(\"Style        : ^[%i\\\\type\\\\r_editlights_edit style %i^]\\n\", dl->style, dl->style);\n\t\tCon_Printf(\"Style String : ^[%s\\\\type\\\\r_editlights_edit stylestring %s^]\\n\", dl->customstyle?dl->customstyle:\"---\", dl->customstyle?dl->customstyle:\"\");\n\t\tCon_Printf(\"Shadows      : ^[%s\\\\type\\\\r_editlights_edit shadows %s^]\\n\", ((dl->flags&LFLAG_NOSHADOWS)?\"no\":\"yes\"), ((dl->flags&LFLAG_NOSHADOWS)?\"no\":\"yes\"));\n\t\tCon_Printf(\"Cubemap      : ^[\\\"%s\\\"\\\\type\\\\r_editlights_edit cubemap \\\"%s\\\"^]\\n\", dl->cubemapname, dl->cubemapname);\n\t\tCon_Printf(\"CoronaSize   : ^[%f\\\\type\\\\r_editlights_edit coronasize %g^]\\n\", dl->coronascale, dl->coronascale);\n\t\tCon_Printf(\"Ambient      : ^[%f\\\\type\\\\r_editlights_edit ambient %g^]\\n\", dl->lightcolourscales[0], dl->lightcolourscales[0]);\n\t\tCon_Printf(\"Diffuse      : ^[%f\\\\type\\\\r_editlights_edit diffuse %g^]\\n\", dl->lightcolourscales[1], dl->lightcolourscales[1]);\n\t\tCon_Printf(\"Specular     : ^[%f\\\\type\\\\r_editlights_edit specular %g^]\\n\", dl->lightcolourscales[2], dl->lightcolourscales[2]);\n\t\tCon_Printf(\"NormalMode   : ^[%s\\\\type\\\\r_editlights_edit normalmode %s^]\\n\", ((dl->flags&LFLAG_NORMALMODE)?\"yes\":\"no\"), ((dl->flags&LFLAG_NORMALMODE)?\"yes\":\"no\"));\n\t\tCon_Printf(\"RealTimeMode : ^[%s\\\\type\\\\r_editlights_edit realtimemode %s^]\\n\", ((dl->flags&LFLAG_REALTIMEMODE)?\"yes\":\"no\"), ((dl->flags&LFLAG_REALTIMEMODE)?\"yes\":\"no\"));\n\t\tCon_Printf(\"Spin         : ^[%f %f %f\\\\type\\\\r_editlights_edit avel %g %g %g^]\\n\", dl->rotation[0],dl->rotation[1],dl->rotation[2], dl->origin[0],dl->origin[1],dl->origin[2]);\n\t\tCon_Printf(\"Cone         : ^[%f\\\\type\\\\r_editlights_edit outercone %g^]\\n\", dl->fov, dl->fov);\n\t\tCon_Printf(\"NearClip     : ^[%f\\\\type\\\\r_editlights_edit nearclip %g^]\\n\", dl->nearclip, dl->nearclip);\n//\t\tCon_Printf(\"NoStencil    : ^[%s\\\\type\\\\r_editlights_edit nostencil %s^]\\n\", ((dl->flags&LFLAG_SHADOWMAP)?\"no\":\"yes\"), ((dl->flags&LFLAG_SHADOWMAP)?\"no\":\"yes\"));\n//\t\tCon_Printf(\"Crepuscular  : ^[%s\\\\type\\\\r_editlights_edit crepuscular %s^]\\n\", ((dl->flags&LFLAG_CREPUSCULAR)?\"yes\":\"no\"), ((dl->flags&LFLAG_CREPUSCULAR)?\"yes\":\"no\"));\n//\t\tCon_Printf(\"Ortho        : ^[%s\\\\type\\\\r_editlights_edit ortho %s^]\\n\", ((dl->flags&LFLAG_ORTHO)?\"yes\":\"no\"), ((dl->flags&LFLAG_ORTHO)?\"yes\":\"no\"));\n\t\treturn;\n\t}\n\tswitch(R_EditLight(dl, cmd, argc, x,y,z))\n\t{\n\tcase -1:\n\t\tCon_Printf(\"Not enough args for %s\\n\", cmd);\n\t\treturn;\n\tcase -2:\n\t\tCon_Printf(\"Argument not known: %s\\n\", cmd);\n\t\treturn;\n\t}\n}\nstatic void R_EditLights_Remove_f(void)\n{\n\tint i = r_editlights_selected;\n\tdlight_t *dl;\n\tif (!r_editlights.ival)\n\t{\n\t\tCon_Printf(\"Toggle r_editlights first\\n\");\n\t\treturn;\n\t}\n\tif (i < RTL_FIRST || i >= rtlights_max)\n\t{\n\t\tCon_Printf(\"No light selected\\n\");\n\t\treturn;\n\t}\n\tdl = &cl_dlights[i];\n\tdl->radius = 0;\n\tr_editlights_selected = -1;\n}\nstatic void R_EditLights_EditAll_f(void)\n{\n\tint i = 0;\n\tconst char *cmd = Cmd_Argv(1);\n\tconst char *x = Cmd_Argv(2);\n\tconst char *y = Cmd_Argv(3);\n\tconst char *z = Cmd_Argv(4);\n\tint argc = Cmd_Argc()-2;\n\tdlight_t *dl;\n\tif (!r_editlights.ival)\n\t{\n\t\tCon_Printf(\"No light selected\\n\");\n\t\treturn;\n\t}\n\tfor (i = RTL_FIRST; i < rtlights_max; i++)\n\t{\n\t\tdl = &cl_dlights[i];\n\t\tif (dl->radius <= 0)\n\t\t\tcontinue;\t//don't edit dead lights back to life\n\t\tswitch(R_EditLight(dl, cmd, argc, x,y,z))\n\t\t{\n\t\tcase -1:\n\t\t\tCon_Printf(\"Not enough args for %s\\n\", cmd);\n\t\t\treturn;\n\t\tcase -2:\n\t\t\tCon_Printf(\"Argument not known: %s\\n\", cmd);\n\t\t\treturn;\n\t\t}\n\t}\n}\nstatic void R_EditLights_Spawn_f(void)\n{\n\tdlight_t *dl;\n\tif (!r_editlights.ival)\n\t{\n\t\tCon_Printf(\"Toggle r_editlights first\\n\");\n\t\treturn;\n\t}\n\tdl = CL_AllocSlight();\n\tr_editlights_selected = dl - cl_dlights;\n\n\tVectorCopy(r_editlights_cursor, dl->origin);\n\tdl->radius = 200;\n\n\tdl->style = 0;\t//styled, but mostly static (we could use -1, but mneh).\n\tdl->lightcolourscales[0] = 0;\n\tdl->lightcolourscales[1] = 1;\n\tdl->lightcolourscales[2] = 1;\n}\nstatic void R_EditLights_Clone_f(void)\n{\n\tint i = r_editlights_selected;\n\tdlight_t *dl;\n\tdlight_t *src;\n\tif (!r_editlights.ival)\n\t{\n\t\tCon_Printf(\"Toggle r_editlights first\\n\");\n\t\treturn;\n\t}\n\tif (i < RTL_FIRST || i >= rtlights_max)\n\t{\n\t\tCon_Printf(\"No light selected\\n\");\n\t\treturn;\n\t}\n\tsrc = &cl_dlights[i];\n\tdl = CL_AllocSlight();\n\tr_editlights_selected = dl - cl_dlights;\n\tCL_CloneDlight(dl, src);\n\n\tVectorCopy(r_editlights_cursor, dl->origin);\n}\nstatic void R_EditLights_ToggleShadow_f(void)\n{\n\tint i = r_editlights_selected;\n\tdlight_t *dl;\n\tif (!r_editlights.ival)\n\t{\n\t\tCon_Printf(\"Toggle r_editlights first\\n\");\n\t\treturn;\n\t}\n\tif (i < RTL_FIRST || i >= rtlights_max)\n\t{\n\t\tCon_Printf(\"No light selected\\n\");\n\t\treturn;\n\t}\n\tdl = &cl_dlights[i];\n\tdl->flags ^= LFLAG_NOSHADOWS;\n}\nstatic void R_EditLights_ToggleCorona_f(void)\n{\n\tint i = r_editlights_selected;\n\tdlight_t *dl;\n\tif (!r_editlights.ival)\n\t{\n\t\tCon_Printf(\"Toggle r_editlights first\\n\");\n\t\treturn;\n\t}\n\tif (i < RTL_FIRST || i >= rtlights_max)\n\t{\n\t\tCon_Printf(\"No light selected\\n\");\n\t\treturn;\n\t}\n\tdl = &cl_dlights[i];\n\tdl->corona = !dl->corona;\n}\nstatic void R_EditLights_CopyInfo_f(void)\n{\n\tint i = r_editlights_selected;\n\tdlight_t *dl;\n\tif (!r_editlights.ival)\n\t{\n\t\tCon_Printf(\"Toggle r_editlights first\\n\");\n\t\treturn;\n\t}\n\tif (i < RTL_FIRST || i >= rtlights_max)\n\t\treturn;\n\tdl = &cl_dlights[i];\n\tCL_CloneDlight(&r_editlights_copybuffer, dl);\n}\nstatic void R_EditLights_PasteInfo_f(void)\n{\n\tint i = r_editlights_selected;\n\tdlight_t *dl;\n\tvec3_t org;\n\tif (!r_editlights.ival)\n\t{\n\t\tCon_Printf(\"Toggle r_editlights first\\n\");\n\t\treturn;\n\t}\n\tif (i < RTL_FIRST || i >= rtlights_max)\n\t{\n\t\tCon_Printf(\"No light selected\\n\");\n\t\treturn;\n\t}\n\tdl = &cl_dlights[i];\n\tVectorCopy(dl->origin, org);\n\tCL_CloneDlight(dl, &r_editlights_copybuffer);\n\tVectorCopy(org, dl->origin);\t//undo the origin's copy.\n\n\t//just in case its from a different map...\n\tif (*dl->cubemapname)\n\t\tdl->cubetexture = R_LoadReplacementTexture(dl->cubemapname, \"\", IF_TEXTYPE_CUBE, NULL, 0, 0, TF_INVALID);\n\telse\n\t\tdl->cubetexture = r_nulltex;\n}\n\nstatic void R_EditLights_Lock_f(void)\n{\n\tif (!r_editlights.ival)\n\t{\n\t\tCon_Printf(\"Toggle r_editlights first\\n\");\n\t\treturn;\n\t}\n\n\tif ((r_editlights_selected < RTL_FIRST || r_editlights_selected >= rtlights_max) && !r_editlights_locked)\n\t{\n\t\tCon_Printf(\"No light selected\\n\");\n\t\treturn;\n\t}\n\tr_editlights_locked = !r_editlights_locked;\n}\n\nstatic char\tmacro_buf[256] = \"\";\nstatic char *r_editlights_current_origin(void)\n{\n\tint i = r_editlights_selected;\n\tdlight_t *dl;\n\tif (i < RTL_FIRST || i >= rtlights_max)\n\t\treturn \"\";\n\tdl = &cl_dlights[i];\n\n\tQ_snprintfz (macro_buf, sizeof(macro_buf), \"%g %g %g\", dl->origin[0], dl->origin[1], dl->origin[2]);\n\treturn macro_buf;\n}\nstatic char *r_editlights_current_angles(void)\n{\n\tint i = r_editlights_selected;\n\tdlight_t *dl;\n\tif (i < RTL_FIRST || i >= rtlights_max)\n\t\treturn \"\";\n\tdl = &cl_dlights[i];\n\n\tQ_snprintfz (macro_buf, sizeof(macro_buf), \"%g %g %g\", dl->angles[0], dl->angles[1], dl->angles[2]);\n\treturn macro_buf;\n}\nstatic char *r_editlights_current_color(void)\n{\n\tint i = r_editlights_selected;\n\tdlight_t *dl;\n\tif (i < RTL_FIRST || i >= rtlights_max)\n\t\treturn \"\";\n\tdl = &cl_dlights[i];\n\n\tQ_snprintfz (macro_buf, sizeof(macro_buf), \"%g %g %g\", dl->color[0], dl->color[1], dl->color[2]);\n\treturn macro_buf;\n}\nstatic char *r_editlights_current_radius(void)\n{\n\tint i = r_editlights_selected;\n\tdlight_t *dl;\n\tif (i < RTL_FIRST || i >= rtlights_max)\n\t\treturn \"\";\n\tdl = &cl_dlights[i];\n\n\tQ_snprintfz (macro_buf, sizeof(macro_buf), \"%g\", dl->radius);\n\treturn macro_buf;\n}\nstatic char *r_editlights_current_corona(void)\n{\n\tint i = r_editlights_selected;\n\tdlight_t *dl;\n\tif (i < RTL_FIRST || i >= rtlights_max)\n\t\treturn \"\";\n\tdl = &cl_dlights[i];\n\n\tQ_snprintfz (macro_buf, sizeof(macro_buf), \"%g\", dl->corona);\n\treturn macro_buf;\n}\nstatic char *r_editlights_current_coronasize(void)\n{\n\tint i = r_editlights_selected;\n\tdlight_t *dl;\n\tif (i < RTL_FIRST || i >= rtlights_max)\n\t\treturn \"\";\n\tdl = &cl_dlights[i];\n\n\tQ_snprintfz (macro_buf, sizeof(macro_buf), \"%g\", dl->coronascale);\n\treturn macro_buf;\n}\nstatic char *r_editlights_current_style(void)\n{\n\tint i = r_editlights_selected;\n\tdlight_t *dl;\n\tif (i < RTL_FIRST || i >= rtlights_max)\n\t\treturn \"\";\n\tdl = &cl_dlights[i];\n\n\tQ_snprintfz (macro_buf, sizeof(macro_buf), \"%i\", dl->style);\n\treturn macro_buf;\n}\nstatic char *r_editlights_current_shadows(void)\n{\n\tint i = r_editlights_selected;\n\tdlight_t *dl;\n\tif (i < RTL_FIRST || i >= rtlights_max)\n\t\treturn \"\";\n\tdl = &cl_dlights[i];\n\n\tif (dl->flags & LFLAG_NOSHADOWS)\n\t\treturn \"0\";\n\treturn \"1\";\n}\nstatic char *r_editlights_current_cubemap(void)\n{\n\tint i = r_editlights_selected;\n\tdlight_t *dl;\n\tif (i < RTL_FIRST || i >= rtlights_max)\n\t\treturn \"\";\n\tdl = &cl_dlights[i];\n\n\tQ_snprintfz (macro_buf, sizeof(macro_buf), \"\\\"%s\\\"\", dl->cubemapname);\n\treturn macro_buf;\n}\nstatic char *r_editlights_current_ambient(void)\n{\n\tint i = r_editlights_selected;\n\tdlight_t *dl;\n\tif (i < RTL_FIRST || i >= rtlights_max)\n\t\treturn \"\";\n\tdl = &cl_dlights[i];\n\n\tQ_snprintfz (macro_buf, sizeof(macro_buf), \"%g\", dl->lightcolourscales[0]);\n\treturn macro_buf;\n}\nstatic char *r_editlights_current_diffuse(void)\n{\n\tint i = r_editlights_selected;\n\tdlight_t *dl;\n\tif (i < RTL_FIRST || i >= rtlights_max)\n\t\treturn \"\";\n\tdl = &cl_dlights[i];\n\n\tQ_snprintfz (macro_buf, sizeof(macro_buf), \"%g\", dl->lightcolourscales[1]);\n\treturn macro_buf;\n}\nstatic char *r_editlights_current_specular(void)\n{\n\tint i = r_editlights_selected;\n\tdlight_t *dl;\n\tif (i < RTL_FIRST || i >= rtlights_max)\n\t\treturn \"\";\n\tdl = &cl_dlights[i];\n\n\tQ_snprintfz (macro_buf, sizeof(macro_buf), \"%g\", dl->lightcolourscales[2]);\n\treturn macro_buf;\n}\nstatic char *r_editlights_current_normalmode(void)\n{\n\tint i = r_editlights_selected;\n\tdlight_t *dl;\n\tif (i < RTL_FIRST || i >= rtlights_max)\n\t\treturn \"\";\n\tdl = &cl_dlights[i];\n\n\tif (dl->flags & LFLAG_NORMALMODE)\n\t\treturn \"1\";\n\treturn \"0\";\n}\nstatic char *r_editlights_current_realtimemode(void)\n{\n\tint i = r_editlights_selected;\n\tdlight_t *dl;\n\tif (i < RTL_FIRST || i >= rtlights_max)\n\t\treturn \"\";\n\tdl = &cl_dlights[i];\n\n\tif (dl->flags & LFLAG_REALTIMEMODE)\n\t\treturn \"1\";\n\treturn \"0\";\n}\n\nvoid R_EditLights_RegisterCommands(void)\n{\n\tCmd_AddCommandD (\"r_editlights_reload\", R_ReloadRTLights_f, \"Reload static rtlights. Argument can be rtlights|statics|bsp|none to override the source.\");\n\tCmd_AddCommandD (\"r_editlights_save\", R_SaveRTLights_f, \"Saves rtlights to maps/FOO.rtlights\");\n\tCvar_Register (&r_editlights_import_radius,\t\t\"Realtime Light editing/importing\");\n\tCvar_Register (&r_editlights_import_ambient,\t\"Realtime Light editing/importing\");\n\tCvar_Register (&r_editlights_import_diffuse,\t\"Realtime Light editing/importing\");\n\tCvar_Register (&r_editlights_import_specular,\t\"Realtime Light editing/importing\");\n\tCvar_Register (&r_shadow_realtime_world_importlightentitiesfrommap,\t\"Realtime Light editing/importing\");\n\n\tCvar_Register (&r_editlights,\t\t\t\t\t\"Realtime Light editing/importing\");\n\tCvar_Register (&r_editlights_cursordistance,\t\"Realtime Light editing/importing\");\n\tCvar_Register (&r_editlights_cursorpushoff,\t\t\"Realtime Light editing/importing\");\n\tCvar_Register (&r_editlights_cursorpushback,\t\"Realtime Light editing/importing\");\n\tCvar_Register (&r_editlights_cursorgrid,\t\t\"Realtime Light editing/importing\");\n\n\t//the rest is optional stuff that should normally be handled via csqc instead, but hurrah for dp compat...\n\tCmd_AddCommandD(\"r_editlights_spawn\", R_EditLights_Spawn_f, \"Spawn a new light with default properties\");\n\tCmd_AddCommandD(\"r_editlights_clone\", R_EditLights_Clone_f, \"Duplicate the current light (with a new origin)\");\n\tCmd_AddCommandD(\"r_editlights_remove\", R_EditLights_Remove_f, \"Removes the current light.\");\n\tCmd_AddCommandD(\"r_editlights_edit\", R_EditLights_Edit_f, \"Changes named properties on the current light.\");\n\tCmd_AddCommandD(\"r_editlights_editall\", R_EditLights_EditAll_f, \"Like r_editlights_edit, but affects all lights instead of just the selected one.\");\n\tCmd_AddCommandD(\"r_editlights_toggleshadow\", R_EditLights_ToggleShadow_f, \"Toggles the shadow flag on the current light.\");\n\tCmd_AddCommandD(\"r_editlights_togglecorona\", R_EditLights_ToggleCorona_f, \"Toggles the current light's corona field.\");\n\tCmd_AddCommandD(\"r_editlights_copyinfo\", R_EditLights_CopyInfo_f, \"store a copy of all properties (except origin) of the selected light\");\n\tCmd_AddCommandD(\"r_editlights_pasteinfo\", R_EditLights_PasteInfo_f, \"apply the stored properties onto the selected light (making it exactly identical except for origin)\");\n\tCmd_AddCommandD(\"r_editlights_lock\", R_EditLights_Lock_f, \"Blocks changing the current light according the crosshair.\");\n\n\t//DP has these as cvars. mneh.\n\tCmd_AddMacroD(\"r_editlights_current_origin\",\tr_editlights_current_origin,\tfalse, \"origin of selected light\");\n\tCmd_AddMacroD(\"r_editlights_current_angles\",\tr_editlights_current_angles,\tfalse, \"angles of selected light\");\n\tCmd_AddMacroD(\"r_editlights_current_color\",\t\tr_editlights_current_color,\t\tfalse, \"color of selected light\");\n\tCmd_AddMacroD(\"r_editlights_current_radius\",\tr_editlights_current_radius,\tfalse, \"radius of selected light\");\n\tCmd_AddMacroD(\"r_editlights_current_corona\",\tr_editlights_current_corona,\tfalse, \"corona intensity of selected light\");\n\tCmd_AddMacroD(\"r_editlights_current_coronasize\",r_editlights_current_coronasize,false, \"corona size of selected light\");\n\tCmd_AddMacroD(\"r_editlights_current_style\",\t\tr_editlights_current_style,\t\tfalse, \"style of selected light\");\n\tCmd_AddMacroD(\"r_editlights_current_shadows\",\tr_editlights_current_shadows,\tfalse, \"shadows flag of selected light\");\n\tCmd_AddMacroD(\"r_editlights_current_cubemap\",\tr_editlights_current_cubemap,\tfalse, \"cubemap of selected light\");\n\tCmd_AddMacroD(\"r_editlights_current_ambient\",\tr_editlights_current_ambient,\tfalse, \"ambient intensity of selected light\");\n\tCmd_AddMacroD(\"r_editlights_current_diffuse\",\tr_editlights_current_diffuse,\tfalse, \"diffuse intensity of selected light\");\n\tCmd_AddMacroD(\"r_editlights_current_specular\",\tr_editlights_current_specular,\tfalse, \"specular intensity of selected light\");\n\tCmd_AddMacroD(\"r_editlights_current_normalmode\",r_editlights_current_normalmode,false, \"normalmode flag of selected light\");\n\tCmd_AddMacroD(\"r_editlights_current_realtimemode\",\tr_editlights_current_realtimemode,\tfalse, \"realtimemode flag of selected light\");\n}\n#endif\n\n/*\n=============================================================================\n\nLIGHT SAMPLING\n\n=============================================================================\n*/\n\nmplane_t\t\t*lightplane;\nvec3_t\t\t\tlightspot;\n\nstatic void GLQ3_AddLatLong(const qbyte latlong[2], vec3_t dir, float mag)\n{\n\tfloat lat = (float)latlong[0] * (2 * M_PI)*(1.0 / 255.0);\n\tfloat lng = (float)latlong[1] * (2 * M_PI)*(1.0 / 255.0);\n\tdir[0] += mag * cos ( lng ) * sin ( lat );\n\tdir[1] += mag * sin ( lng ) * sin ( lat );\n\tdir[2] += mag * cos ( lat );\n}\n\nvoid GLQ3_LightGrid(model_t *mod, const vec3_t point, vec3_t res_diffuse, vec3_t res_ambient, vec3_t res_dir)\n{\n\tq3lightgridinfo_t *lg = (q3lightgridinfo_t *)mod->lightgrid;\n\tint index[8];\n\tint vi[3];\n\tint i, j;\n\tfloat t[8];\n\tvec3_t vf, vf2;\n\tvec3_t ambient, diffuse, direction;\n\n\tif (!lg || (!lg->lightgrid && !lg->rbspelements) || lg->numlightgridelems < 1)\n\t{\n\t\tif(res_ambient)\n\t\t{\n\t\t\tres_ambient[0] = 64;\n\t\t\tres_ambient[1] = 64;\n\t\t\tres_ambient[2] = 64;\n\t\t}\n\n\t\tif (res_diffuse)\n\t\t{\n\t\t\tres_diffuse[0] = 192;\n\t\t\tres_diffuse[1] = 192;\n\t\t\tres_diffuse[2] = 192;\n\t\t}\n\n\t\tif (res_dir)\n\t\t{\n\t\t\tres_dir[0] = 1;\n\t\t\tres_dir[1] = 1;\n\t\t\tres_dir[2] = 0.1;\n\t\t}\n\t\treturn;\n\t}\n\n\t//If in doubt, steal someone else's code...\n\t//Thanks QFusion.\n\n\tfor ( i = 0; i < 3; i++ )\n\t{\n\t\tvf[i] = (point[i] - lg->gridMins[i]) / lg->gridSize[i];\n\t\tvi[i] = (int)(vf[i]);\n\t\tvf[i] = vf[i] - floor(vf[i]);\n\t\tvf2[i] = 1.0f - vf[i];\n\t}\n\n\tfor ( i = 0; i < 8; i++ )\n\t{\n\t\t//bound it properly\n\t\tindex[i] =\tbound(0, vi[0]+((i&1)?1:0), lg->gridBounds[0]-1) * 1                 +\n\t\t\t\t\tbound(0, vi[1]+((i&2)?1:0), lg->gridBounds[1]-1) * lg->gridBounds[0] +\n\t\t\t\t\tbound(0, vi[2]+((i&4)?1:0), lg->gridBounds[2]-1) * lg->gridBounds[3] ;\n\t\tt[i] =\t((i&1)?vf[0]:vf2[0]) *\n\t\t\t\t((i&2)?vf[1]:vf2[1]) *\n\t\t\t\t((i&4)?vf[2]:vf2[2]) ;\n\t}\n\n\t//rbsp has a separate grid->index lookup for compression.\n\tif (lg->rbspindexes)\n\t{\n\t\tfor (i = 0; i < 8; i++)\n\t\t\tindex[i] = lg->rbspindexes[index[i]];\n\t}\n\n\tVectorClear(ambient);\n\tVectorClear(diffuse);\n\tVectorClear(direction);\n\tif (lg->rbspelements)\n\t{\n\t\tfor (i = 0; i < 8; i++)\n\t\t{\t//rbsp has up to 4 styles per grid element, which needs to be scaled by that style's current value\n\t\t\tfloat tot = 0;\n\t\t\tfor (j = 0; j < countof(lg->rbspelements[index[i]].styles); j++)\n\t\t\t{\n\t\t\t\tqbyte st = lg->rbspelements[index[i]].styles[j];\n\t\t\t\tif (st != 255)\n\t\t\t\t{\n\t\t\t\t\tfloat mag = d_lightstylevalue[st] * 1.0/255 * t[i];\n\t\t\t\t\t//FIXME: cl_lightstyle[st].colours[rgb]\n\t\t\t\t\tVectorMA (ambient,      mag, lg->rbspelements[index[i]].ambient[j],   ambient);\n\t\t\t\t\tVectorMA (diffuse,      mag, lg->rbspelements[index[i]].diffuse[j],   diffuse);\n\t\t\t\t\ttot += mag;\n\t\t\t\t}\n\t\t\t}\n\t\t\tGLQ3_AddLatLong(lg->rbspelements[index[i]].direction, direction, tot);\n\t\t}\n\t}\n\telse\n\t{\n\t\tfor (i = 0; i < 8; i++)\n\t\t{\n\t\t\tVectorMA (ambient,      t[i], lg->lightgrid[index[i]].ambient,   ambient);\n\t\t\tVectorMA (diffuse,      t[i], lg->lightgrid[index[i]].diffuse,   diffuse);\n\t\t\tGLQ3_AddLatLong(lg->lightgrid[index[i]].direction, direction, t[i]);\n\t\t}\n\n\t\tVectorScale(ambient, d_lightstylevalue[0]/255.0, ambient);\n\t\tVectorScale(diffuse, d_lightstylevalue[0]/255.0, diffuse);\n\t\t//FIXME: cl_lightstyle[0].colours[rgb]\n\t}\n\n\t//q3bsp has *4 overbrighting.\n//\tVectorScale(ambient, 4, ambient);\n//\tVectorScale(diffuse, 4, diffuse);\n\n\t/*ambient is the min level*/\n\t/*diffuse is the max level*/\n\tVectorCopy(ambient, res_ambient);\n\tif (res_diffuse)\n\t\tVectorAdd(diffuse, ambient, res_diffuse);\n\tif (res_dir)\n\t\tVectorCopy(direction, res_dir);\n}\n\nstatic int GLRecursiveLightPoint (mnode_t *node, vec3_t start, vec3_t end)\n{\n\tint\t\t\tr;\n\tfloat\t\tfront, back, frac;\n\tint\t\t\tside;\n\tmplane_t\t*plane;\n\tvec3_t\t\tmid;\n\tmsurface_t\t*surf;\n\tint\t\t\ts, t, ds, dt;\n\tint\t\t\ti;\n\tmtexinfo_t\t*tex;\n\tqbyte\t\t*lightmap;\n\tunsigned\tscale;\n\tint\t\t\tmaps;\n\n\tif (cl.worldmodel->fromgame == fg_quake2)\n\t{\n\t\tif (node->contents != -1)\n\t\t\treturn -1;\t\t// solid\n\t}\n\telse if (node->contents < 0)\n\t\treturn -1;\t\t// didn't hit anything\n\t\n// calculate mid point\n\n// FIXME: optimize for axial\n\tplane = node->plane;\n\tfront = DotProduct (start, plane->normal) - plane->dist;\n\tback = DotProduct (end, plane->normal) - plane->dist;\n\tside = front < 0;\n\t\n\tif ( (back < 0) == side)\n\t\treturn GLRecursiveLightPoint (node->children[side], start, end);\n\t\n\tfrac = front / (front-back);\n\tmid[0] = start[0] + (end[0] - start[0])*frac;\n\tmid[1] = start[1] + (end[1] - start[1])*frac;\n\tmid[2] = start[2] + (end[2] - start[2])*frac;\n\t\n// go down front side\t\n\tr = GLRecursiveLightPoint (node->children[side], start, mid);\n\tif (r >= 0)\n\t\treturn r;\t\t// hit something\n\t\t\n\tif ( (back < 0) == side )\n\t\treturn -1;\t\t// didn't hit anuthing\n\t\t\n// check for impact on this node\n\tVectorCopy (mid, lightspot);\n\tlightplane = plane;\n\n\tsurf = cl.worldmodel->surfaces + node->firstsurface;\n\tfor (i=0 ; i<node->numsurfaces ; i++, surf++)\n\t{\n\t\tif (surf->flags & SURF_DRAWTILED)\n\t\t\tcontinue;\t// no lightmaps\n\n\t\ttex = surf->texinfo;\n\t\t\n\t\ts = DotProduct (mid, tex->vecs[0]) + tex->vecs[0][3];\n\t\tt = DotProduct (mid, tex->vecs[1]) + tex->vecs[1][3];;\n\n\t\tif (s < surf->texturemins[0] || t < surf->texturemins[1])\n\t\t\tcontinue;\n\t\t\n\t\tds = s - surf->texturemins[0];\n\t\tdt = t - surf->texturemins[1];\n\t\t\n\t\tif ( ds > surf->extents[0] || dt > surf->extents[1] )\n\t\t\tcontinue;\n\n\t\tif (!surf->samples)\n\t\t\treturn 0;\n\n\t\tds >>= surf->lmshift;\n\t\tdt >>= surf->lmshift;\n\n\t\tlightmap = surf->samples;\n\t\tr = 0;\n\t\tif (lightmap)\n\t\t{\n\t\t\tswitch(cl.worldmodel->lightmaps.fmt)\n\t\t\t{\n\t\t\tcase LM_E5BGR9:\n\t\t\t\tlightmap += (dt * ((surf->extents[0]>>surf->lmshift)+1) + ds)<<2;\n\t\t\t\tfor (maps = 0 ; maps < MAXCPULIGHTMAPS && surf->styles[maps] != INVALID_LIGHTSTYLE ; maps++)\n\t\t\t\t{\n\t\t\t\t\tunsigned int l = *(unsigned int*)lightmap;\n\t\t\t\t\tscale = d_lightstylevalue[surf->styles[maps]];\n\t\t\t\t\tscale *= pow(2, (int)(l>>27)-15-9+7);\n\n\t\t\t\t\tr += max3(((l>> 0)&0x1ff), ((l>> 9)&0x1ff), ((l>>18)&0x1ff)) * scale;\n\n\t\t\t\t\tlightmap += ((surf->extents[0]>>surf->lmshift)+1) * ((surf->extents[1]>>surf->lmshift)+1)<<2;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase LM_RGB8:\n\t\t\t\tlightmap += (dt * ((surf->extents[0]>>surf->lmshift)+1) + ds)*3;\n\t\t\t\tfor (maps = 0 ; maps < MAXCPULIGHTMAPS && surf->styles[maps] != INVALID_LIGHTSTYLE ; maps++)\n\t\t\t\t{\n\t\t\t\t\tscale = d_lightstylevalue[surf->styles[maps]];\n\t\t\t\t\tr += max3(lightmap[0],lightmap[1],lightmap[2]) * scale;\n\t\t\t\t\tlightmap += ((surf->extents[0]>>surf->lmshift)+1) * ((surf->extents[1]>>surf->lmshift)+1)*3;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase LM_L8:\n\t\t\t\tlightmap += dt * ((surf->extents[0]>>surf->lmshift)+1) + ds;\n\t\t\t\tfor (maps = 0 ; maps < MAXCPULIGHTMAPS && surf->styles[maps] != INVALID_LIGHTSTYLE ; maps++)\n\t\t\t\t{\n\t\t\t\t\tscale = d_lightstylevalue[surf->styles[maps]];\n\t\t\t\t\tr += *lightmap * scale;\n\t\t\t\t\tlightmap += ((surf->extents[0]>>surf->lmshift)+1) * ((surf->extents[1]>>surf->lmshift)+1);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\t\n\t\t\tr >>= 8;\n\t\t}\n\t\t\n\t\treturn r;\n\t}\n\n// go down back side\n\treturn GLRecursiveLightPoint (node->children[!side], mid, end);\n}\n\n\n\nint R_LightPoint (vec3_t p)\n{\n\tvec3_t\t\tend;\n\tint\t\t\tr;\n\n\tif (r_refdef.flags & 1)\n\t\treturn 255;\n\n\tif (!cl.worldmodel || cl.worldmodel->loadstate != MLS_LOADED || !cl.worldmodel->lightdata)\n\t\treturn 255;\n\n\tif (cl.worldmodel->fromgame == fg_quake3)\n\t{\n\t\tGLQ3_LightGrid(cl.worldmodel, p, NULL, end, NULL);\n\t\treturn (end[0] + end[1] + end[2])/3;\n\t}\n\n\tend[0] = p[0];\n\tend[1] = p[1];\n\tend[2] = p[2] - mod_lightpoint_distance.value;\n\n\tr = GLRecursiveLightPoint (cl.worldmodel->rootnode, p, end);\n\t\n\tif (r == -1)\n\t\tr = 0;\n\n\treturn r;\n}\n\n\n\n#ifdef PEXT_LIGHTSTYLECOL\n\nstatic float *GLRecursiveLightPoint3C (model_t *mod, mnode_t *node, const vec3_t start, const vec3_t end)\n{\n\tstatic float l[6];\n\tfloat *r;\n\tfloat\t\tfront, back, frac;\n\tint\t\t\tside;\n\tmplane_t\t*plane;\n\tvec3_t\t\tmid;\n\tmsurface_t\t*surf;\n\tint\t\t\ts, t, ds, dt;\n\tint\t\t\ti;\n\tvec4_t\t*lmvecs;\n\tqbyte\t\t*lightmap, *deluxmap;\n\tfloat\tscale, overbright;\n\tint\t\t\tmaps;\n\n\tif (mod->fromgame == fg_quake2)\n\t{\n\t\tif (node->contents != -1)\n\t\t\treturn NULL;\t\t// solid\n\t}\n\telse if (node->contents < 0)\n\t\treturn NULL;\t\t// didn't hit anything\n\t\n// calculate mid point\n\n// FIXME: optimize for axial\n\tplane = node->plane;\n\tfront = DotProduct (start, plane->normal) - plane->dist;\n\tback = DotProduct (end, plane->normal) - plane->dist;\n\tside = front < 0;\n\n\tif ( (back < 0) == side)\n\t\treturn GLRecursiveLightPoint3C (mod, node->children[side], start, end);\n\n\tfrac = front / (front-back);\n\tmid[0] = start[0] + (end[0] - start[0])*frac;\n\tmid[1] = start[1] + (end[1] - start[1])*frac;\n\tmid[2] = start[2] + (end[2] - start[2])*frac;\n\t\n// go down front side\t\n\tr = GLRecursiveLightPoint3C (mod, node->children[side], start, mid);\n\tif (r && r[0]+r[1]+r[2] >= 0)\n\t\treturn r;\t\t// hit something\n\n\tif ( (back < 0) == side )\n\t\treturn NULL;\t\t// didn't hit anuthing\n\n// check for impact on this node\n\tVectorCopy (mid, lightspot);\n\tlightplane = plane;\n\n\tsurf = mod->surfaces + node->firstsurface;\n\tfor (i=0 ; i<node->numsurfaces ; i++, surf++)\n\t{\n\t\tif (surf->flags & SURF_DRAWTILED)\n\t\t\tcontinue;\t// no lightmaps\n\n\t\tif (mod->facelmvecs)\n\t\t\tlmvecs = mod->facelmvecs[surf-mod->surfaces].lmvecs;\n\t\telse\n\t\t\tlmvecs = surf->texinfo->vecs;\n\t\t\n\t\ts = DotProduct (mid, lmvecs[0]) + lmvecs[0][3];\n\t\tt = DotProduct (mid, lmvecs[1]) + lmvecs[1][3];\n\n\t\tif (s < surf->texturemins[0] ||\n\t\t\tt < surf->texturemins[1])\n\t\t\tcontinue;\n\n\t\tds = s - surf->texturemins[0];\n\t\tdt = t - surf->texturemins[1];\n\t\t\n\t\tif ( ds > surf->extents[0] || dt > surf->extents[1] )\n\t\t\tcontinue;\n\n\t\tif (!surf->samples)\n\t\t{\n\t\t\tl[0]=0;l[1]=0;l[2]=0;\n\t\t\tl[3]=0;l[4]=1;l[5]=1;\n\t\t\treturn l;\n\t\t}\n\n\t\tds >>= surf->lmshift;\n\t\tdt >>= surf->lmshift;\n\n\t\tlightmap = surf->samples;\n\t\tl[0]=0;l[1]=0;l[2]=0;\n\t\tl[3]=0;l[4]=0;l[5]=0;\n\t\tif (lightmap)\n\t\t{\n\t\t\toverbright = 1/255.0f;\n\t\t\tif (mod->deluxdata)\n\t\t\t{\n\t\t\t\tswitch(mod->lightmaps.fmt)\n\t\t\t\t{\n\t\t\t\tcase LM_E5BGR9:\n\t\t\t\t\tdeluxmap = ((surf->samples - mod->lightdata)>>2)*3 + mod->deluxdata;\n\n\t\t\t\t\tlightmap += (dt * ((surf->extents[0]>>surf->lmshift)+1) + ds)<<2;\n\t\t\t\t\tdeluxmap += (dt * ((surf->extents[0]>>surf->lmshift)+1) + ds)*3;\n\t\t\t\t\tfor (maps = 0 ; maps < MAXCPULIGHTMAPS && surf->styles[maps] != INVALID_LIGHTSTYLE ; maps++)\n\t\t\t\t\t{\n\t\t\t\t\t\tunsigned int lm = *(unsigned int*)lightmap;\n\t\t\t\t\t\tscale = d_lightstylevalue[surf->styles[maps]]*overbright;\n\t\t\t\t\t\tscale *= pow(2, (int)(lm>>27)-15-9+8);\n\n\t\t\t\t\t\tl[0] += ((lm>> 0)&0x1ff) * scale * cl_lightstyle[surf->styles[maps]].colours[0];\n\t\t\t\t\t\tl[1] += ((lm>> 9)&0x1ff) * scale * cl_lightstyle[surf->styles[maps]].colours[1];\n\t\t\t\t\t\tl[2] += ((lm>>18)&0x1ff) * scale * cl_lightstyle[surf->styles[maps]].colours[2];\n\n\t\t\t\t\t\tl[3] += (deluxmap[0]-127)*scale;\n\t\t\t\t\t\tl[4] += (deluxmap[1]-127)*scale;\n\t\t\t\t\t\tl[5] += (deluxmap[2]-127)*scale;\n\n\t\t\t\t\t\tlightmap += ((surf->extents[0]>>surf->lmshift)+1) *\n\t\t\t\t\t\t\t\t((surf->extents[1]>>surf->lmshift)+1)<<2;\n\t\t\t\t\t\tdeluxmap += ((surf->extents[0]>>surf->lmshift)+1) *\n\t\t\t\t\t\t\t\t((surf->extents[1]>>surf->lmshift)+1) * 3;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase LM_RGB8:\n\t\t\t\t\tdeluxmap = surf->samples - mod->lightdata + mod->deluxdata;\n\n\t\t\t\t\tlightmap += (dt * ((surf->extents[0]>>surf->lmshift)+1) + ds)*3;\n\t\t\t\t\tdeluxmap += (dt * ((surf->extents[0]>>surf->lmshift)+1) + ds)*3;\n\t\t\t\t\tfor (maps = 0 ; maps < MAXCPULIGHTMAPS && surf->styles[maps] != INVALID_LIGHTSTYLE ; maps++)\n\t\t\t\t\t{\n\t\t\t\t\t\tscale = d_lightstylevalue[surf->styles[maps]]*overbright;\n\n\t\t\t\t\t\tl[0] += lightmap[0] * scale * cl_lightstyle[surf->styles[maps]].colours[0];\n\t\t\t\t\t\tl[1] += lightmap[1] * scale * cl_lightstyle[surf->styles[maps]].colours[1];\n\t\t\t\t\t\tl[2] += lightmap[2] * scale * cl_lightstyle[surf->styles[maps]].colours[2];\n\n\t\t\t\t\t\tl[3] += (deluxmap[0]-127)*scale;\n\t\t\t\t\t\tl[4] += (deluxmap[1]-127)*scale;\n\t\t\t\t\t\tl[5] += (deluxmap[2]-127)*scale;\n\n\t\t\t\t\t\tlightmap += ((surf->extents[0]>>surf->lmshift)+1) *\n\t\t\t\t\t\t\t\t((surf->extents[1]>>surf->lmshift)+1) * 3;\n\t\t\t\t\t\tdeluxmap += ((surf->extents[0]>>surf->lmshift)+1) *\n\t\t\t\t\t\t\t\t((surf->extents[1]>>surf->lmshift)+1) * 3;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase LM_L8:\n\t\t\t\t\tdeluxmap = (surf->samples - mod->lightdata)*3 + mod->deluxdata;\n\n\t\t\t\t\tlightmap += (dt * ((surf->extents[0]>>surf->lmshift)+1) + ds);\n\t\t\t\t\tdeluxmap += (dt * ((surf->extents[0]>>surf->lmshift)+1) + ds)*3;\n\t\t\t\t\tfor (maps = 0 ; maps < MAXCPULIGHTMAPS && surf->styles[maps] != INVALID_LIGHTSTYLE ; maps++)\n\t\t\t\t\t{\n\t\t\t\t\t\tscale = d_lightstylevalue[surf->styles[maps]]*overbright;\n\n\t\t\t\t\t\tl[0] += *lightmap * scale * cl_lightstyle[surf->styles[maps]].colours[0];\n\t\t\t\t\t\tl[1] += *lightmap * scale * cl_lightstyle[surf->styles[maps]].colours[1];\n\t\t\t\t\t\tl[2] += *lightmap * scale * cl_lightstyle[surf->styles[maps]].colours[2];\n\n\t\t\t\t\t\tl[3] += deluxmap[0]*scale;\n\t\t\t\t\t\tl[4] += deluxmap[1]*scale;\n\t\t\t\t\t\tl[5] += deluxmap[2]*scale;\n\n\t\t\t\t\t\tlightmap += ((surf->extents[0]>>surf->lmshift)+1) *\n\t\t\t\t\t\t\t\t((surf->extents[1]>>surf->lmshift)+1);\n\t\t\t\t\t\tdeluxmap += ((surf->extents[0]>>surf->lmshift)+1) *\n\t\t\t\t\t\t\t\t((surf->extents[1]>>surf->lmshift)+1) * 3;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tswitch(mod->lightmaps.fmt)\n\t\t\t\t{\n\t\t\t\tcase LM_E5BGR9:\n\t\t\t\t\tlightmap += (dt * ((surf->extents[0]>>surf->lmshift)+1) + ds)<<2;\n\t\t\t\t\tfor (maps = 0 ; maps < MAXCPULIGHTMAPS && surf->styles[maps] != INVALID_LIGHTSTYLE ; maps++)\n\t\t\t\t\t{\n\t\t\t\t\t\tunsigned int lm = *(unsigned int*)lightmap;\n\t\t\t\t\t\tscale = d_lightstylevalue[surf->styles[maps]]*overbright;\n\t\t\t\t\t\tscale *= pow(2, (int)(lm>>27)-15-9+8);\n\n\t\t\t\t\t\tl[0] += ((lm>> 0)&0x1ff) * scale * cl_lightstyle[surf->styles[maps]].colours[0];\n\t\t\t\t\t\tl[1] += ((lm>> 9)&0x1ff) * scale * cl_lightstyle[surf->styles[maps]].colours[1];\n\t\t\t\t\t\tl[2] += ((lm>>18)&0x1ff) * scale * cl_lightstyle[surf->styles[maps]].colours[2];\n\n\t\t\t\t\t\tlightmap += ((surf->extents[0]>>surf->lmshift)+1) *\n\t\t\t\t\t\t\t\t((surf->extents[1]>>surf->lmshift)+1)<<2;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase LM_RGB8:\n\t\t\t\t\tlightmap += (dt * ((surf->extents[0]>>surf->lmshift)+1) + ds)*3;\n\t\t\t\t\tfor (maps = 0 ; maps < MAXCPULIGHTMAPS && surf->styles[maps] != INVALID_LIGHTSTYLE ; maps++)\n\t\t\t\t\t{\n\t\t\t\t\t\tscale = d_lightstylevalue[surf->styles[maps]]*overbright;\n\n\t\t\t\t\t\tl[0] += lightmap[0] * scale * cl_lightstyle[surf->styles[maps]].colours[0];\n\t\t\t\t\t\tl[1] += lightmap[1] * scale * cl_lightstyle[surf->styles[maps]].colours[1];\n\t\t\t\t\t\tl[2] += lightmap[2] * scale * cl_lightstyle[surf->styles[maps]].colours[2];\n\n\t\t\t\t\t\tlightmap += ((surf->extents[0]>>surf->lmshift)+1) *\n\t\t\t\t\t\t\t\t((surf->extents[1]>>surf->lmshift)+1) * 3;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase LM_L8:\n\t\t\t\t\tlightmap += (dt * ((surf->extents[0]>>surf->lmshift)+1) + ds);\n\t\t\t\t\tfor (maps = 0 ; maps < MAXCPULIGHTMAPS && surf->styles[maps] != INVALID_LIGHTSTYLE ; maps++)\n\t\t\t\t\t{\n\t\t\t\t\t\tscale = d_lightstylevalue[surf->styles[maps]]*overbright;\n\n\t\t\t\t\t\tl[0] += *lightmap * scale * cl_lightstyle[surf->styles[maps]].colours[0];\n\t\t\t\t\t\tl[1] += *lightmap * scale * cl_lightstyle[surf->styles[maps]].colours[1];\n\t\t\t\t\t\tl[2] += *lightmap * scale * cl_lightstyle[surf->styles[maps]].colours[2];\n\n\t\t\t\t\t\tlightmap += ((surf->extents[0]>>surf->lmshift)+1) *\n\t\t\t\t\t\t\t\t((surf->extents[1]>>surf->lmshift)+1);\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\treturn l;\n\t}\n\n// go down back side\n\treturn GLRecursiveLightPoint3C (mod, node->children[!side], mid, end);\n}\n\n#endif\n\ntypedef struct\n{\n\tvec3_t gridscale;\n\tunsigned int count[3];\n\tvec3_t mins;\n\tunsigned int styles;\n\n\tunsigned int rootnode;\n\n\tunsigned int numnodes;\n\tstruct bspxlgnode_s\n\t{\t//this uses an octtree to trim samples.\n\t\tint mid[3];\n\t\tunsigned int child[8];\n#define LGNODE_LEAF\t\t(1u<<31)\n#define LGNODE_MISSING\t(1u<<30)\n\t} *nodes;\n\tunsigned int numleafs;\n\tstruct bspxlgleaf_s\n\t{\n\t\tint mins[3];\n\t\tint size[3];\n\t\tunsigned char numstyles;\n\t\tstruct bspxlgsamp_s\n\t\t{\n\t\t\tqbyte style;\n\t\t\tqbyte rgb[3];\n\t\t} *rgbvalues;\t//size[x*y*z]*numstyles\n\t} *leafs;\n} bspxlightgrid_t;\nstruct rctx_s {qbyte *data; size_t ofs, size;};\nstatic qbyte ReadByte(struct rctx_s *ctx)\n{\n\tif (ctx->ofs >= ctx->size)\n\t{\n\t\tctx->ofs++;\n\t\treturn 0;\n\t}\n\treturn ctx->data[ctx->ofs++];\n}\nstatic int ReadInt(struct rctx_s *ctx)\n{\n\tint r = (int)ReadByte(ctx)<<0;\n\t\tr|= (int)ReadByte(ctx)<<8;\n\t\tr|= (int)ReadByte(ctx)<<16;\n\t\tr|= (int)ReadByte(ctx)<<24;\n\treturn r;\n}\nstatic float ReadFloat(struct rctx_s *ctx)\n{\n\tunion {float f; int i;} u;\n\tu.i = ReadInt(ctx);\n\treturn u.f;\n}\nvoid BSPX_LightGridLoad(model_t *model, bspx_header_t *bspx, qbyte *mod_base)\n{\n\tvec3_t step, mins;\n\tint size[3];\n\tbspxlightgrid_t *grid;\n\tunsigned int numstyles, numnodes, numleafs, rootnode;\n\tunsigned int nodestart, leafsamps = 0, i, j, k, s;\n\tstruct bspxlgsamp_s *samp;\n\tstruct rctx_s ctx = {0};\n\tctx.data = BSPX_FindLump(bspx, mod_base, \"LIGHTGRID_OCTREE\", &ctx.size);\n\tmodel->lightgrid = NULL;\n\tif (!ctx.data)\n\t\treturn;\n\n\tfor (j = 0; j < 3; j++)\n\t\tstep[j] = ReadFloat(&ctx);\n\tfor (j = 0; j < 3; j++)\n\t\tsize[j] = ReadInt(&ctx);\n\tfor (j = 0; j < 3; j++)\n\t\tmins[j] = ReadFloat(&ctx);\n\n\tnumstyles = ReadByte(&ctx);\t//urgh, misaligned the entire thing\n\trootnode = ReadInt(&ctx);\n\tnumnodes = ReadInt(&ctx);\n\tnodestart = ctx.ofs;\n\tctx.ofs += (3+8)*4*numnodes;\n\tnumleafs = ReadInt(&ctx);\n\tfor (i = 0; i < numleafs; i++)\n\t{\n\t\tunsigned int ms = 1;\n\t\tunsigned int lsz[3];\n\t\tctx.ofs += 3*4;\n\t\tfor (j = 0; j < 3; j++)\n\t\t\tlsz[j] = ReadInt(&ctx);\n\t\tj = lsz[0]*lsz[1]*lsz[2];\n\t\twhile (j --> 0)\n\t\t{\t//this loop is annonying, memcpy dreams...\n\t\t\ts = ReadByte(&ctx);\n\t\t\tif (s == 255)\n\t\t\t\tcontinue;\n\t\t\tif (ms < s)\n\t\t\t\tms = s;\n\t\t\tctx.ofs += s*4;\n\t\t}\n\t\tj = lsz[0]*lsz[1]*lsz[2];\n\t\tleafsamps += j*ms;\n\t}\n\n\tgrid = ZG_Malloc(&model->memgroup, sizeof(*grid) + sizeof(*grid->leafs)*numleafs + sizeof(*grid->nodes)*numnodes + sizeof(struct bspxlgsamp_s)*leafsamps);\n//\tmemset(grid, 0xcc, sizeof(*grid) + sizeof(*grid->leafs)*numleafs + sizeof(*grid->nodes)*numnodes + sizeof(struct bspxlgsamp_s)*leafsamps);\n\tgrid->leafs = (void*)(grid+1);\n\tgrid->nodes = (void*)(grid->leafs + numleafs);\n\tsamp = (void*)(grid->nodes+numnodes);\n\n\tfor (j = 0; j < 3; j++)\n\t\tgrid->gridscale[j] = 1/step[j];\t//prefer it as a multiply\n\tVectorCopy(mins, grid->mins);\n\tVectorCopy(size, grid->count);\n\tgrid->numnodes = numnodes;\n\tgrid->numleafs = numleafs;\n\tgrid->rootnode = rootnode;\n\t(void)numstyles;\n\n\t//rewind to the nodes. *sigh*\n\tctx.ofs = nodestart;\n\tfor (i = 0; i < numnodes; i++)\n\t{\n\t\tfor (j = 0; j < 3; j++)\n\t\t\tgrid->nodes[i].mid[j] = ReadInt(&ctx);\n\t\tfor (j = 0; j < 8; j++)\n\t\t\tgrid->nodes[i].child[j] = ReadInt(&ctx);\n\t}\n\tctx.ofs += 4;\n\tfor (i = 0; i < numleafs; i++)\n\t{\n\t\tunsigned int leafdataofs;\n\t\tunsigned int ms = 1;\n\t\tfor (j = 0; j < 3; j++)\n\t\t\tgrid->leafs[i].mins[j] = ReadInt(&ctx);\n\t\tfor (j = 0; j < 3; j++)\n\t\t\tgrid->leafs[i].size[j] = ReadInt(&ctx);\n\n\t\tgrid->leafs[i].rgbvalues = samp;\n\n\t\tj = grid->leafs[i].size[0]*grid->leafs[i].size[1]*grid->leafs[i].size[2];\n\n\t\t//Count the maximum styles needed for this leaf.\n\t\tleafdataofs = ctx.ofs;\n\t\tfor (k = 0; k < j; k++)\n\t\t{\n\t\t\ts = ReadByte(&ctx);\n\t\t\tif (s == 255)\n\t\t\t\tcontinue;\n\t\t\tif (ms < s)\n\t\t\t\tms = s;\n\t\t\tctx.ofs += s*4;\n\t\t}\n\t\tctx.ofs = leafdataofs;\n\t\tgrid->leafs[i].numstyles = ms;\n\n\t\twhile (j --> 0)\n\t\t{\n\t\t\ts = ReadByte(&ctx);\n\t\t\tif (s == 0xff)\n\t\t\t\tmemset(samp, 0xff, sizeof(*samp));\n\t\t\telse\n\t\t\t{\n\t\t\t\tfor (k = 0; k < s; k++)\n\t\t\t\t{\n\t\t\t\t\tif (k >= ms)\n\t\t\t\t\t\tReadInt(&ctx);\t//shouldn't be possible...\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tsamp[k].style = ReadByte(&ctx);\n\t\t\t\t\t\tsamp[k].rgb[0] = ReadByte(&ctx);\n\t\t\t\t\t\tsamp[k].rgb[1] = ReadByte(&ctx);\n\t\t\t\t\t\tsamp[k].rgb[2] = ReadByte(&ctx);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tfor (; k < ms; k++)\n\t\t\t\t{\n\t\t\t\t\tsamp[k].style = k?(qbyte)~0u:0;\n\t\t\t\t\tsamp[k].rgb[0] =\n\t\t\t\t\tsamp[k].rgb[1] =\n\t\t\t\t\tsamp[k].rgb[2] = 0;\n\t\t\t\t}\n\t\t\t}\n\t\t\tsamp+=ms;\n\t\t}\n\t}\n\n\tif (ctx.ofs != ctx.size)\n\t\tgrid = NULL;\n\n\tmodel->lightgrid = (void*)grid;\n}\nstatic float BSPX_LightGridSingleValue(bspxlightgrid_t *grid, int x, int y, int z, float w, vec3_t res_diffuse)\n{\n\tint i;\n\tunsigned int node;\n\tstruct bspxlgsamp_s *samp;\n\tfloat lev;\n\n\tnode = grid->rootnode;\n\twhile (!(node & LGNODE_LEAF))\n\t{\n\t\tstruct bspxlgnode_s *n;\n\t\tif (node >= grid->numnodes) // also node&LGNODE_MISSING\n\t\t\treturn 0;\t//failure\n\t\tn = grid->nodes + node;\n\t\tnode = n->child[\n\t\t\t\t((x>=n->mid[0])<<2)|\n\t\t\t\t((y>=n->mid[1])<<1)|\n\t\t\t\t((z>=n->mid[2])<<0)];\n\t}\n\n\t{\n\t\tstruct bspxlgleaf_s *leaf = &grid->leafs[node & ~LGNODE_LEAF];\n\t\tx -= leaf->mins[0];\n\t\ty -= leaf->mins[1];\n\t\tz -= leaf->mins[2];\n\t\tif (x >= leaf->size[0] ||\n\t\t\ty >= leaf->size[1] ||\n\t\t\tz >= leaf->size[2])\n\t\t\treturn 0;\t//sample we're after is out of bounds...\n\n\t\ti = x + leaf->size[0]*(y + leaf->size[1]*z);\n\t\tsamp = leaf->rgbvalues + i*leaf->numstyles;\n\n\t\t//no hdr support\n\t\tfor (i = 0; i < leaf->numstyles; i++)\n\t\t{\n\t\t\tif (samp[i].style == ((qbyte)(~0u)))\n\t\t\t\tbreak;\t//no more\n\t\t\tif (samp[i].style < cl_max_lightstyles)\n\t\t\t{\n\t\t\t\tlev = d_lightstylevalue[samp[i].style]*w;\n\t\t\t\tres_diffuse[0] += samp[i].rgb[0] * lev * cl_lightstyle[samp[i].style].colours[0];\n\t\t\t\tres_diffuse[1] += samp[i].rgb[1] * lev * cl_lightstyle[samp[i].style].colours[1];\n\t\t\t\tres_diffuse[2] += samp[i].rgb[2] * lev * cl_lightstyle[samp[i].style].colours[2];\n\t\t\t}\n\t\t}\n\t\tif (i == 0)\n\t\t\tw = 0;\n\t}\n\treturn w;\n}\nstatic void BSPX_LightGridValue(void *lightgridinfo, const vec3_t point, vec3_t res_diffuse, vec3_t res_ambient, vec3_t res_dir)\n{\n\tbspxlightgrid_t *grid = lightgridinfo;\n\tint i, tile[3];\n\tfloat w, s, frac[3];\n\n\tVectorSet(res_diffuse, 0, 0, 0);\t//assume worst\n\tVectorSet(res_ambient, 0, 0, 0);\t//assume worst\n\tVectorSet(res_dir, 1, 0, 1);\t\t//super lame\n\n\tfor (i = 0; i < 3; i++)\n\t{\n\t\ttile[i] = floor((point[i] - grid->mins[i]) * grid->gridscale[i]);\n\t\tfrac[i] = (point[i] - grid->mins[i]) * grid->gridscale[i] - tile[i];\n\t}\n\n\tfor (i = 0, s = 0; i < 8; i++)\n\t{\n\t\tw =\t((i&1)?frac[0]:1-frac[0])\t//keep the lerping vaugely smooth.\n\t\t  * ((i&2)?frac[1]:1-frac[1])\n\t\t  * ((i&4)?frac[2]:1-frac[2]);\n\t\ts += w*BSPX_LightGridSingleValue(grid,\ttile[0]+!!(i&1),\n\t\t\t\t\t\t\t\t\t\t\t\ttile[1]+!!(i&2),\n\t\t\t\t\t\t\t\t\t\t\t\ttile[2]+!!(i&4), w, res_diffuse);\n\t}\n\n\tif (s)\n\t\tVectorScale(res_diffuse, 1.0/(s*255), res_diffuse);\t//average the successful ones\n\tVectorScale(res_diffuse, 0.5, res_ambient);\t//and fix up ambients.\n}\n\nvoid GLQ1BSP_LightPointValues(model_t *model, const vec3_t point, vec3_t res_diffuse, vec3_t res_ambient, vec3_t res_dir)\n{\n\tvec3_t\t\tend;\n\tfloat *r;\n#ifdef RTLIGHTS\n\textern cvar_t r_shadow_realtime_world, r_shadow_realtime_world_lightmaps;\n#endif\n\n\tif (!model->lightdata || r_fullbright.ival || model->loadstate != MLS_LOADED)\n\t{\n\t\tif (model->loadstate != MLS_LOADED)\n\t\t\tSys_Error(\"GLQ1BSP_LightPointValues: model not loaded...\\n\");\n\t\tres_diffuse[0] = 0;\n\t\tres_diffuse[1] = 0;\n\t\tres_diffuse[2] = 0;\n\t\n\t\tres_ambient[0] = 255;\n\t\tres_ambient[1] = 255;\n\t\tres_ambient[2] = 255;\n\n\t\tres_dir[0] = 1;\n\t\tres_dir[1] = 1;\n\t\tres_dir[2] = 0.1;\n\t\tVectorNormalize(res_dir);\n\t\treturn;\n\t}\n\n\tif (model->lightgrid)\n\t\treturn BSPX_LightGridValue(model->lightgrid, point, res_diffuse, res_ambient, res_dir);\n\n\tend[0] = point[0];\n\tend[1] = point[1];\n\tend[2] = point[2] - mod_lightpoint_distance.value;\n\n\tr = GLRecursiveLightPoint3C(model, model->rootnode, point, end);\n\tif (r == NULL)\n\t{\n\t\tres_diffuse[0] = 0;\n\t\tres_diffuse[1] = 0;\n\t\tres_diffuse[2] = 0;\n\t\n\t\tres_ambient[0] = 0;\n\t\tres_ambient[1] = 0;\n\t\tres_ambient[2] = 0;\n\n\t\tres_dir[0] = 0;\n\t\tres_dir[1] = 1;\n\t\tres_dir[2] = 1;\n\t}\n\telse\n\t{\n\t\tres_diffuse[0] = r[0];\n\t\tres_diffuse[1] = r[1];\n\t\tres_diffuse[2] = r[2];\n\n\t\t/*bright on one side, dark on the other, but not too dark*/\n\t\tres_ambient[0] = r[0];\n\t\tres_ambient[1] = r[1];\n\t\tres_ambient[2] = r[2];\n\n\t\tres_dir[0] = r[3];\n\t\tres_dir[1] = r[4];\n\t\tres_dir[2] = -r[5];\n\t\tif (!res_dir[0] && !res_dir[1] && !res_dir[2])\n\t\t\tres_dir[0] = res_dir[2] = 1;\n\t\tVectorNormalize(res_dir);\n\t}\n\n#ifdef RTLIGHTS\n\tif (r_shadow_realtime_world.ival)\n\t{\n\t\tfloat lm = r_shadow_realtime_world_lightmaps.value;\n\t\tif (lm < 0) lm = 0;\n\t\tif (lm > 1) lm = 1;\n\t\tVectorScale(res_diffuse, lm, res_diffuse);\n\t\tVectorScale(res_ambient, lm, res_ambient);\n\t}\n#endif\n}\n\n#endif\n"
  },
  {
    "path": "engine/gl/gl_rmain.c",
    "content": "/*\nCopyright (C) 1996-1997 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n*/\n// r_main.c\n\n#include \"quakedef.h\"\n\n#ifdef GLQUAKE\n#include \"glquake.h\"\n#include \"renderque.h\"\n#include \"shader.h\"\n#include \"vr.h\"\n\nvoid R_RenderBrushPoly (msurface_t *fa);\n\n#define PROJECTION_DISTANCE\t\t\t200\n#define MAX_STENCIL_ENTS\t\t\t128\n\nextern int\t\tgl_stencilbits;\n\n//mplane_t\tfrustum[4];\n\n//\n// view origin\n//\n//vec3_t\tvup;\n//vec3_t\tvpn;\n//vec3_t\tvright;\n//vec3_t\tr_origin;\n\nextern cvar_t\tgl_part_flame;\nextern cvar_t\tr_bloom;\nextern cvar_t\tr_wireframe, r_wireframe_smooth;\nextern cvar_t\tr_outline;\n\ncvar_t\tgl_affinemodels = CVARFD(\"gl_affinemodels\",\"0\", CVAR_ARCHIVE, \"Use affine texture sampling for models. This replicates software rendering's distortions.\");\ncvar_t\tgl_finish = CVAR(\"gl_finish\",\"0\");\ncvar_t\tgl_dither = CVAR(\"gl_dither\", \"1\");\nextern cvar_t\tr_stereo_separation;\nextern cvar_t\tr_stereo_convergence;\nextern cvar_t\tr_stereo_method;\nextern cvar_t\tr_postprocshader, r_fxaa, r_graphics;\nextern cvar_t\tr_hdr_framebuffer;\n\nextern cvar_t\tgl_screenangle;\n\nextern cvar_t\tgl_mindist;\nextern cvar_t\tvid_srgb;\n\nextern cvar_t\tffov;\n\nextern cvar_t\tgl_motionblur;\nextern cvar_t\tgl_motionblurscale;\n\nextern cvar_t r_tessellation;\nextern cvar_t gl_ati_truform_type;\nextern cvar_t r_tessellation_level;\n\nextern cvar_t r_portaldrawplanes;\nextern cvar_t r_portalonly;\n\nextern\tcvar_t\tscr_fov;\n\nstatic shader_t *scenepp_rescaled;\nstatic shader_t *scenepp_antialias;\nstatic shader_t *scenepp_waterwarp;\nstatic shader_t *scenepp_gamma;\n\n// post processing stuff\nstatic texid_t sceneblur_texture;\nstatic texid_t scenepp_texture_warp;\nstatic texid_t scenepp_texture_edge;\n\ntexid_t scenepp_postproc_cube;\nstatic int scenepp_postproc_cube_size;\n\nstatic fbostate_t fbo_vr;\nstatic fbostate_t fbo_gameview;\nstatic fbostate_t fbo_postproc;\nstatic fbostate_t fbo_postproc_cube;\n\n// KrimZon - init post processing - called in GL_CheckExtensions, when they're called\n// I put it here so that only this file need be changed when messing with the post\n// processing shaders\nstatic void GL_InitSceneProcessingShaders_WaterWarp (void)\n{\n\tscenepp_waterwarp = NULL;\n\tif (gl_config.arb_shader_objects)\n\t{\n\t\tscenepp_waterwarp = R_RegisterShader(\"waterwarp\", SUF_NONE,\n\t\t\t\"{\\n\"\n\t\t\t\t\"program underwaterwarp\\n\"\n\t\t\t\t\"{\\n\"\n\t\t\t\t\t\"map $sourcecolour\\n\"\n\t\t\t\t\"}\\n\"\n\t\t\t\t\"{\\n\"\n\t\t\t\t\t\"map $upperoverlay\\n\"\n\t\t\t\t\"}\\n\"\n\t\t\t\t\"{\\n\"\n\t\t\t\t\t\"map $loweroverlay\\n\"\n\t\t\t\t\"}\\n\"\n\t\t\t\"}\\n\"\n\t\t\t);\n\t\tscenepp_waterwarp->defaulttextures->upperoverlay = scenepp_texture_warp;\n\t\tscenepp_waterwarp->defaulttextures->loweroverlay = scenepp_texture_edge;\n\t}\n}\n\nvoid GL_ShutdownPostProcessing(void)\n{\n\tGLBE_FBO_Destroy(&fbo_vr);\n\tGLBE_FBO_Destroy(&fbo_gameview);\n\tGLBE_FBO_Destroy(&fbo_postproc);\n\tGLBE_FBO_Destroy(&fbo_postproc_cube);\n\tR_BloomShutdown();\n}\n\nvoid GL_InitSceneProcessingShaders (void)\n{\n\tif (gl_config.arb_shader_objects)\n\t{\n\t\tGL_InitSceneProcessingShaders_WaterWarp();\n\t}\n\n\tscenepp_gamma = R_RegisterShader(\"fte_scenegamma\", 0, \n\t\t\"{\\n\"\n\t\t\t\"program defaultgammacb\\n\"\n\t\t\t\"affine\\n\"\n\t\t\t\"{\\n\"\n\t\t\t\t\"map $sourcecolour\\n\"\n\t\t\t\t\"nodepthtest\\n\"\n\t\t\t\"}\\n\"\n\t\t\"}\\n\"\n\t\t);\n\n\tscenepp_rescaled = R_RegisterShader(\"fte_rescaler\", 0, \n\t\t\"{\\n\"\n\t\t\t\"program default2d\\n\"\n\t\t\t\"affine\\n\"\n\t\t\t\"{\\n\"\n\t\t\t\t\"map $sourcecolour\\n\"\n\t\t\t\t\"nodepthtest\\n\"\n\t\t\t\"}\\n\"\n\t\t\"}\\n\"\n\t\t);\n\tscenepp_antialias = R_RegisterShader(\"fte_ppantialias\", 0, \n\t\t\"{\\n\"\n\t\t\t\"program fxaa\\n\"\n\t\t\t\"affine\\n\"\n\t\t\t\"{\\n\"\n\t\t\t\t\"map $sourcecolour\\n\"\n\t\t\t\t\"nodepthtest\\n\"\n\t\t\t\"}\\n\"\n\t\t\"}\\n\"\n\t\t);\n\n\tr_wireframe_smooth.modified = true;\n\tgl_dither.modified = true;\t//fixme: bad place for this, but hey\n\tvid_srgb.modified = true;\n}\n\n#define PP_WARP_TEX_SIZE 64\n#define PP_AMP_TEX_SIZE 64\n#define PP_AMP_TEX_BORDER 4\nvoid GL_SetupSceneProcessingTextures (void)\n{\n\tint i, x, y;\n\tunsigned char pp_warp_tex[PP_WARP_TEX_SIZE*PP_WARP_TEX_SIZE*4];\n\tunsigned char pp_edge_tex[PP_AMP_TEX_SIZE*PP_AMP_TEX_SIZE*4];\n\n\tscenepp_postproc_cube = r_nulltex;\n\n\tTEXASSIGN(sceneblur_texture, Image_CreateTexture(\"***postprocess_blur***\", NULL, 0));\n\n\tif (!gl_config.arb_shader_objects)\n\t\treturn;\n\n\tTEXASSIGN(scenepp_texture_warp, Image_CreateTexture(\"***postprocess_warp***\", NULL, IF_NOMIPMAP|IF_NOGAMMA|IF_LINEAR));\n\tTEXASSIGN(scenepp_texture_edge, Image_CreateTexture(\"***postprocess_edge***\", NULL, IF_NOMIPMAP|IF_NOGAMMA|IF_LINEAR));\n\n\t// init warp texture - this specifies offset in\n\tfor (y=0; y<PP_WARP_TEX_SIZE; y++)\n\t{\n\t\tfor (x=0; x<PP_WARP_TEX_SIZE; x++)\n\t\t{\n\t\t\tfloat fx, fy;\n\n\t\t\ti = (x + y*PP_WARP_TEX_SIZE) * 4;\n\n\t\t\tfx = sin(((double)y / PP_WARP_TEX_SIZE) * M_PI * 2);\n\t\t\tfy = cos(((double)x / PP_WARP_TEX_SIZE) * M_PI * 2);\n\n\t\t\tpp_warp_tex[i  ] = (fx+1.0f)*127.0f;\n\t\t\tpp_warp_tex[i+1] = (fy+1.0f)*127.0f;\n\t\t\tpp_warp_tex[i+2] = 0;\n\t\t\tpp_warp_tex[i+3] = 0xff;\n\t\t}\n\t}\n\n\tImage_Upload(scenepp_texture_warp, TF_RGBX32, pp_warp_tex, NULL, PP_WARP_TEX_SIZE, PP_WARP_TEX_SIZE, 1, IF_LINEAR|IF_NOMIPMAP|IF_NOGAMMA);\n\n\t// TODO: init edge texture - this is ampscale * 2, with ampscale calculated\n\t// init warp texture - this specifies offset in\n\tfor (y=0; y<PP_AMP_TEX_SIZE; y++)\n\t{\n\t\tfor (x=0; x<PP_AMP_TEX_SIZE; x++)\n\t\t{\n\t\t\tfloat fx = 1, fy = 1;\n\n\t\t\ti = (x + y*PP_AMP_TEX_SIZE) * 4;\n\n\t\t\tif (x < PP_AMP_TEX_BORDER)\n\t\t\t{\n\t\t\t\tfx = (float)x / PP_AMP_TEX_BORDER;\n\t\t\t}\n\t\t\tif (x > PP_AMP_TEX_SIZE - PP_AMP_TEX_BORDER)\n\t\t\t{\n\t\t\t\tfx = (PP_AMP_TEX_SIZE - (float)x) / PP_AMP_TEX_BORDER;\n\t\t\t}\n\t\t\t\n\t\t\tif (y < PP_AMP_TEX_BORDER)\n\t\t\t{\n\t\t\t\tfy = (float)y / PP_AMP_TEX_BORDER;\n\t\t\t}\n\t\t\tif (y > PP_AMP_TEX_SIZE - PP_AMP_TEX_BORDER)\n\t\t\t{\n\t\t\t\tfy = (PP_AMP_TEX_SIZE - (float)y) / PP_AMP_TEX_BORDER;\n\t\t\t}\n\n\t\t\t//avoid any sudden changes.\n\t\t\tfx=sin(fx*M_PI*0.5);\n\t\t\tfy=sin(fy*M_PI*0.5);\n\n\t\t\t//lame\n\t\t\tfx = fy = min(fx, fy);\n\n\t\t\tpp_edge_tex[i  ] = fx * 255;\n\t\t\tpp_edge_tex[i+1] = fy * 255;\n\t\t\tpp_edge_tex[i+2] = 0;\n\t\t\tpp_edge_tex[i+3] = 0xff;\n\t\t}\n\t}\n\n\tImage_Upload(scenepp_texture_edge, TF_RGBX32, pp_edge_tex, NULL, PP_AMP_TEX_SIZE, PP_AMP_TEX_SIZE, 1, IF_LINEAR|IF_NOMIPMAP|IF_NOGAMMA);\n}\n\nvoid R_RotateForEntity (float *m, float *modelview, const entity_t *e, const model_t *mod)\n{\n\tif (e->flags & RF_WEAPONMODEL)\n\t{\n\t\tfloat em[16];\n\t\tfloat vm[16];\n\n\t\tif (e->flags & RF_WEAPONMODELNOBOB)\n\t\t{\n\t\t\tvm[0] = r_refdef.weaponmatrix[0][0];\n\t\t\tvm[1] = r_refdef.weaponmatrix[0][1];\n\t\t\tvm[2] = r_refdef.weaponmatrix[0][2];\n\t\t\tvm[3] = 0;\n\n\t\t\tvm[4] = r_refdef.weaponmatrix[1][0];\n\t\t\tvm[5] = r_refdef.weaponmatrix[1][1];\n\t\t\tvm[6] = r_refdef.weaponmatrix[1][2];\n\t\t\tvm[7] = 0;\n\n\t\t\tvm[8] = r_refdef.weaponmatrix[2][0];\n\t\t\tvm[9] = r_refdef.weaponmatrix[2][1];\n\t\t\tvm[10] = r_refdef.weaponmatrix[2][2];\n\t\t\tvm[11] = 0;\n\n\t\t\tvm[12] = r_refdef.weaponmatrix[3][0];\n\t\t\tvm[13] = r_refdef.weaponmatrix[3][1];\n\t\t\tvm[14] = r_refdef.weaponmatrix[3][2];\n\t\t\tvm[15] = 1;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tvm[0] = r_refdef.weaponmatrix_bob[0][0];\n\t\t\tvm[1] = r_refdef.weaponmatrix_bob[0][1];\n\t\t\tvm[2] = r_refdef.weaponmatrix_bob[0][2];\n\t\t\tvm[3] = 0;\n\n\t\t\tvm[4] = r_refdef.weaponmatrix_bob[1][0];\n\t\t\tvm[5] = r_refdef.weaponmatrix_bob[1][1];\n\t\t\tvm[6] = r_refdef.weaponmatrix_bob[1][2];\n\t\t\tvm[7] = 0;\n\n\t\t\tvm[8] = r_refdef.weaponmatrix_bob[2][0];\n\t\t\tvm[9] = r_refdef.weaponmatrix_bob[2][1];\n\t\t\tvm[10] = r_refdef.weaponmatrix_bob[2][2];\n\t\t\tvm[11] = 0;\n\n\t\t\tvm[12] = r_refdef.weaponmatrix_bob[3][0];\n\t\t\tvm[13] = r_refdef.weaponmatrix_bob[3][1];\n\t\t\tvm[14] = r_refdef.weaponmatrix_bob[3][2];\n\t\t\tvm[15] = 1;\n\t\t}\n\n\t\tem[0] = e->axis[0][0];\n\t\tem[1] = e->axis[0][1];\n\t\tem[2] = e->axis[0][2];\n\t\tem[3] = 0;\n\n\t\tem[4] = e->axis[1][0];\n\t\tem[5] = e->axis[1][1];\n\t\tem[6] = e->axis[1][2];\n\t\tem[7] = 0;\n\n\t\tem[8] = e->axis[2][0];\n\t\tem[9] = e->axis[2][1];\n\t\tem[10] = e->axis[2][2];\n\t\tem[11] = 0;\n\n\t\tem[12] = e->origin[0];\n\t\tem[13] = e->origin[1];\n\t\tem[14] = e->origin[2];\n\t\tem[15] = 1;\n\n\t\tMatrix4_Multiply(vm, em, m);\n\t}\n\telse\n\t{\n\t\tm[0] = e->axis[0][0];\n\t\tm[1] = e->axis[0][1];\n\t\tm[2] = e->axis[0][2];\n\t\tm[3] = 0;\n\n\t\tm[4] = e->axis[1][0];\n\t\tm[5] = e->axis[1][1];\n\t\tm[6] = e->axis[1][2];\n\t\tm[7] = 0;\n\n\t\tm[8] = e->axis[2][0];\n\t\tm[9] = e->axis[2][1];\n\t\tm[10] = e->axis[2][2];\n\t\tm[11] = 0;\n\n\t\tm[12] = e->origin[0];\n\t\tm[13] = e->origin[1];\n\t\tm[14] = e->origin[2];\n\t\tm[15] = 1;\n\t}\n\n\tif (e->scale != 1 && e->scale != 0)\t//hexen 2 stuff\n\t{\n#ifdef HEXEN2\n\t\tfloat z;\n\t\tfloat escale;\n\t\tescale = e->scale;\n\t\tswitch(e->drawflags&SCALE_TYPE_MASK)\n\t\t{\n\t\tdefault:\n\t\tcase SCALE_TYPE_UNIFORM:\n\t\t\tVectorScale((m+0), escale, (m+0));\n\t\t\tVectorScale((m+4), escale, (m+4));\n\t\t\tVectorScale((m+8), escale, (m+8));\n\t\t\tbreak;\n\t\tcase SCALE_TYPE_XYONLY:\n\t\t\tVectorScale((m+0), escale, (m+0));\n\t\t\tVectorScale((m+4), escale, (m+4));\n\t\t\tbreak;\n\t\tcase SCALE_TYPE_ZONLY:\n\t\t\tVectorScale((m+8), escale, (m+8));\n\t\t\tbreak;\n\t\t}\n\t\tif (mod && (e->drawflags&SCALE_TYPE_MASK) != SCALE_TYPE_XYONLY)\n\t\t{\n\t\t\tswitch(e->drawflags&SCALE_ORIGIN_MASK)\n\t\t\t{\n\t\t\tcase SCALE_ORIGIN_CENTER:\n\t\t\t\tz = ((mod->maxs[2] + mod->mins[2]) * (1-escale))/2;\n\t\t\t\tVectorMA((m+12), z, e->axis[2], (m+12));\n\t\t\t\tbreak;\n\t\t\tcase SCALE_ORIGIN_BOTTOM:\n\t\t\t\tVectorMA((m+12), mod->mins[2]*(1-escale), e->axis[2], (m+12));\n\t\t\t\tbreak;\n\t\t\tcase SCALE_ORIGIN_TOP:\n\t\t\t\tVectorMA((m+12), -mod->maxs[2], e->axis[2], (m+12));\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n#else\n\t\tVectorScale((m+0), e->scale, (m+0));\n\t\tVectorScale((m+4), e->scale, (m+4));\n\t\tVectorScale((m+8), e->scale, (m+8));\n#endif\n\t}\n\telse if (mod && !strcmp(mod->name, \"progs/eyes.mdl\"))\n\t{\n\t\t/*resize eyes, to make them easier to see*/\n\t\tm[14] -= (22 + 8);\n\t\tVectorScale((m+0), 2, (m+0));\n\t\tVectorScale((m+4), 2, (m+4));\n\t\tVectorScale((m+8), 2, (m+8));\n\t}\n\tif (mod && !ruleset_allow_larger_models.ival && mod->clampscale != 1 && mod->type == mod_alias)\n\t{\t//possibly this should be on a per-frame basis, but that's a real pain to do\n\t\tCon_DPrintf(\"Rescaling %s by %f\\n\", mod->name, mod->clampscale);\n\t\tVectorScale((m+0), mod->clampscale, (m+0));\n\t\tVectorScale((m+4), mod->clampscale, (m+4));\n\t\tVectorScale((m+8), mod->clampscale, (m+8));\n\t}\n\n\tMatrix4_Multiply(r_refdef.m_view, m, modelview);\n}\n\n//==================================================================================\n\n/*\n=============\nR_SetupGL\n=============\n*/\nstatic void R_SetupGL (const float eyematrix[12]/*can be null*/, const vec4_t fovoverrides/*can be null*/, const float projmatrix[16]/*can be null*/, const pxrect_t *viewport/*can be null*/, texid_t fbo/*can be null*/)\n{\n\tint\t\tx, x2, y2, y, w, h;\n\tvec3_t newa;\n\n\tfloat fov_x, fov_y, fov_l, fov_r, fov_d, fov_u;\n\tfloat fovv_x, fovv_y;\n\n\tTRACE((\"dbg: calling R_SetupGL\\n\"));\n\n\tif (!r_refdef.recurse)\n\t{\n\t\tnewa[0] = r_refdef.viewangles[0];\n\t\tnewa[1] = r_refdef.viewangles[1];\n\t\tnewa[2] = r_refdef.viewangles[2] + gl_screenangle.value;\n\t\tif (eyematrix)\n\t\t{\n\t\t\textern cvar_t in_vraim;\n\t\t\tmatrix3x4 basematrix;\n\t\t\tmatrix3x4 viewmatrix;\n\n\t\t\tif (r_refdef.base_known)\n\t\t\t{\t//mod is specifying its own base ang+org.\n\t\t\t\tMatrix3x4_RM_FromAngles(r_refdef.base_angles, r_refdef.base_origin, basematrix[0]);\n\t\t\t\tMatrix3x4_Multiply(eyematrix, basematrix[0], viewmatrix[0]);\n\t\t\t\tMatrix3x4_RM_ToVectors(viewmatrix[0], vpn, vright, vup, r_origin);\n\t\t\t\tVectorNegate(vright, vright);\n\t\t\t}\n\t\t\telse\n\t\t\t{\t//mod provides no info.\n\t\t\t\t//client will fiddle with input_angles\n\t\t\t\tnewa[0] = newa[2] = 0;\t//ignore player pitch+roll. sorry. apply the eye's transform on top.\n\t\t\t\tif (in_vraim.ival)\n\t\t\t\t\tnewa[1] -= SHORT2ANGLE(r_refdef.playerview->vrdev[VRDEV_HEAD].angles[YAW]);\n\t\t\t\tMatrix3x4_RM_FromAngles(newa, r_refdef.vieworg, basematrix[0]);\n\t\t\t\tMatrix3x4_Multiply(eyematrix, basematrix[0], viewmatrix[0]);\n\t\t\t\tMatrix3x4_RM_ToVectors(viewmatrix[0], vpn, vright, vup, r_origin);\n\t\t\t\tVectorNegate(vright, vright);\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tAngleVectors (newa, vpn, vright, vup);\n\t\t\tVectorCopy(r_refdef.vieworg, r_origin);\n\t\t}\n\n\t\tVectorAdd(r_origin, r_refdef.eyeoffset, r_origin);\t//used for vr screenshots\n\n\t\t//\n\t\t// set up viewpoint\n\t\t//\n\t\tif (viewport)\n\t\t{\n\t\t\tr_refdef.pxrect = *viewport;\n\t\t}\n\t\telse if (fbo)\n\t\t{\n\t\t\t//with VR fbo postprocessing, we disable all viewport.\n\t\t\tr_refdef.pxrect.x = 0;\n\t\t\tr_refdef.pxrect.y = 0;\n\t\t\tr_refdef.pxrect.width = fbo->width;\n\t\t\tr_refdef.pxrect.height = fbo->height;\n\t\t\tr_refdef.pxrect.maxheight = fbo->height;\n\t\t}\n\t\telse if (r_refdef.flags & (RDF_ALLPOSTPROC|RDF_RENDERSCALE))\n\t\t{\n\t\t\t//with fbo postprocessing, we disable all viewport.\n\t\t\tr_refdef.pxrect.x = 0;\n\t\t\tr_refdef.pxrect.y = 0;\n\t\t\tr_refdef.pxrect.width = vid.fbpwidth;\n\t\t\tr_refdef.pxrect.height = vid.fbpheight;\n\t\t\tr_refdef.pxrect.maxheight = vid.fbpheight;\n\t\t}\n\t\telse if (*r_refdef.rt_destcolour[0].texname)\n\t\t{\n\t\t\t//with fbo rendering, we disable all virtual scaling.\n\t\t\tx = r_refdef.vrect.x;\n\t\t\tx2 = r_refdef.vrect.x + r_refdef.vrect.width;\n\t\t\ty = r_refdef.vrect.y;\n\t\t\ty2 = r_refdef.vrect.y + r_refdef.vrect.height;\n\n\t\t\tw = x2 - x;\n\t\t\th = y2 - y;\n\n\t\t\tr_refdef.pxrect.x = x;\n\t\t\tr_refdef.pxrect.y = y;\n\t\t\tr_refdef.pxrect.width = w;\n\t\t\tr_refdef.pxrect.height = h;\n\t\t\tr_refdef.pxrect.maxheight = vid.fbpheight;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tx = floor(r_refdef.vrect.x * (float)vid.fbpwidth/(float)vid.width);\n\t\t\tx2 = ceil((r_refdef.vrect.x + r_refdef.vrect.width) * (float)vid.fbpwidth/(float)vid.width);\n\t\t\ty = floor(r_refdef.vrect.y * (float)vid.fbpheight/(float)vid.height);\n\t\t\ty2 = ceil((r_refdef.vrect.y + r_refdef.vrect.height) * (float)vid.fbpheight/(float)vid.height);\n\n\n\t\t\t// fudge around because of frac screen scale\n/*\t\t\tif (x > 0)\n\t\t\t\tx--;\n\t\t\tif (x2 < vid.fbpwidth)\n\t\t\t\tx2++;\n\t\t\tif (y2 < vid.fbpheight)\n\t\t\t\ty2++;\n\t\t\tif (y > 0)\n\t\t\t\ty--;\n*/\n\t\t\tw = x2 - x;\n\t\t\th = y2 - y;\n\n/*\t\t\tif (r_refdef.stereomethod == STEREO_CROSSEYED)\n\t\t\t{\n\t\t\t\tw /= 2;\n\t\t\t\tif (i)\n\t\t\t\t\tx += vid.fbpwidth/2;\n\t\t\t}\n*/\n\t\t\tr_refdef.pxrect.x = x;\n\t\t\tr_refdef.pxrect.y = y;\n\t\t\tr_refdef.pxrect.width = w;\n\t\t\tr_refdef.pxrect.height = h;\n\t\t\tr_refdef.pxrect.maxheight = vid.fbpheight;\n\t\t}\n\n\t\tif (projmatrix)\n\t\t{\n\t\t\tmemcpy(r_refdef.m_projection_std, projmatrix, sizeof(r_refdef.m_projection_std));\n\t\t\tmemcpy(r_refdef.m_projection_view, projmatrix, sizeof(r_refdef.m_projection_view));\n\t\t\tr_refdef.flipcull = 0;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (fovoverrides)\n\t\t\t{\n\t\t\t\tfov_l = fovoverrides[0];\n\t\t\t\tfov_r = fovoverrides[1];\n\t\t\t\tfov_d = fovoverrides[2];\n\t\t\t\tfov_u = fovoverrides[3];\n\n\t\t\t\tfov_x = fov_r-fov_l;\n\t\t\t\tfov_y = fov_u-fov_d;\n\n\t\t\t\tfovv_x = fov_x;\n\t\t\t\tfovv_y = fov_y;\n\t\t\t\tr_refdef.flipcull = ((fov_u < fov_d)^(fov_r < fov_l))?SHADER_CULL_FLIP:0;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tfov_x = r_refdef.fov_x;\n\t\t\t\tfov_y = r_refdef.fov_y;\n\t\t\t\tfovv_x = r_refdef.fovv_x;\n\t\t\t\tfovv_y = r_refdef.fovv_y;\n\n\t\t\t\tif ((*r_refdef.rt_destcolour[0].texname || *r_refdef.rt_depth.texname) && strcmp(r_refdef.rt_destcolour[0].texname, \"megascreeny\"))\n\t\t\t\t{\n\t\t\t\t\tr_refdef.pxrect.y = r_refdef.pxrect.maxheight - (r_refdef.pxrect.height+r_refdef.pxrect.y);\n\t\t\t\t\tfov_y *= -1;\n\t\t\t\t\tfovv_y *= -1;\n\t\t\t\t\tr_refdef.flipcull ^= SHADER_CULL_FLIP;\n\t\t\t\t}\n\t\t\t\telse if ((r_refdef.flags & RDF_UNDERWATER) && !(r_refdef.flags & RDF_WATERWARP))\n\t\t\t\t{\n\t\t\t\t\tfov_x *= 1 + (((sin(cl.time * 4.7) + 1) * 0.015) * r_waterwarp.value);\n\t\t\t\t\tfov_y *= 1 + (((sin(cl.time * 3.0) + 1) * 0.015) * r_waterwarp.value);\n\n\t\t\t\t\tfovv_x *= 1 + (((sin(cl.time * 4.7) + 1) * 0.015) * r_waterwarp.value);\n\t\t\t\t\tfovv_y *= 1 + (((sin(cl.time * 3.0) + 1) * 0.015) * r_waterwarp.value);\n\t\t\t\t}\n\t\t\t\tfov_l = -fov_x / 2;\n\t\t\t\tfov_r = fov_x / 2;\n\t\t\t\tfov_d = -fov_y / 2;\n\t\t\t\tfov_u = fov_y / 2;\n\t\t\t}\n\n\t\t\tif (r_xflip.ival)\n\t\t\t{\n\t\t\t\tfloat t = fov_l;\n\t\t\t\tfov_l = fov_r;\n\t\t\t\tfov_r = t;\n\t\t\t\tr_refdef.flipcull ^= SHADER_CULL_FLIP;\n\t\t\t\tfovv_x *= -1;\n\t\t\t}\n\n\t\t\tif (r_refdef.useperspective)\n\t\t\t{\n\t\t\t\tfloat maxdist = r_refdef.maxdist;\n\t\t\t\tif (sh_config.stencilbits && Sh_StencilShadowsActive())\n\t\t\t\t\tmaxdist = 0;\t//if we're using stencil shadows then force the maxdist to infinite to ensure the shadow volume is sealed.\n\t\t\t\tMatrix4x4_CM_Projection_Offset(r_refdef.m_projection_std, fov_l, fov_r, fov_d, fov_u, r_refdef.mindist, maxdist, false);\n\t\t\t\tMatrix4x4_CM_Projection_Offset(r_refdef.m_projection_view, -fovv_x/2, fovv_x/2, -fovv_y/2, fovv_y/2, r_refdef.mindist, maxdist, false);\n\n\t\t\t\tr_refdef.m_projection_std[8] += r_refdef.projectionoffset[0];\n\t\t\t\tr_refdef.m_projection_std[9] += r_refdef.projectionoffset[1];\n\t\t\t\tr_refdef.m_projection_view[8] += r_refdef.projectionoffset[0];\n\t\t\t\tr_refdef.m_projection_view[9] += r_refdef.projectionoffset[1];\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tMatrix4x4_CM_Orthographic(r_refdef.m_projection_std, -fov_x/2, fov_x/2, -fov_y/2, fov_y/2, r_refdef.mindist, r_refdef.maxdist?r_refdef.maxdist:9999);\n\t\t\t\tmemcpy(r_refdef.m_projection_view, r_refdef.m_projection_std, sizeof(r_refdef.m_projection_view));\n\t\t\t}\n\t\t}\n\t\tMatrix4x4_CM_ModelViewMatrixFromAxis(r_refdef.m_view, vpn, vright, vup, r_origin);\n\n\t\t//bias the viewmodel depth range to a third: -1 through -0.333 (instead of -1 to 1)\n\t\tr_refdef.m_projection_view[2+4*0] *= 0.333;\n\t\tr_refdef.m_projection_view[2+4*1] *= 0.333;\n\t\tr_refdef.m_projection_view[2+4*2] *= 0.333;\n\t\tr_refdef.m_projection_view[2+4*3] *= 0.333;\n\t\tr_refdef.m_projection_view[14] -= 0.666;\n\n\t\tGL_ViewportUpdate();\n\t}\n\n\tif (qglLoadMatrixf)\n\t{\n\t\tqglMatrixMode(GL_PROJECTION);\n\t\tqglLoadMatrixf(r_refdef.m_projection_std);\n\n\t\tqglMatrixMode(GL_MODELVIEW);\n\t\tqglLoadMatrixf(r_refdef.m_view);\n\t}\n\n#ifdef GL_LINE_SMOOTH\n\tif (!gl_config.gles && r_wireframe_smooth.modified)\n\t{\n\t\tr_wireframe_smooth.modified = false;\n\t\tif (r_wireframe_smooth.ival || (r_outline.ival && !r_wireframe.ival))\n\t\t{\n\t\t\tqglEnable(GL_LINE_SMOOTH);\n\t\t\tif (qglHint)\n\t\t\t\tqglHint(GL_LINE_SMOOTH_HINT, GL_NICEST);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tqglDisable(GL_LINE_SMOOTH);\n\t\t\tif (qglHint)\n\t\t\t\tqglHint(GL_LINE_SMOOTH_HINT, GL_FASTEST);\n\t\t}\n\t}\n#endif\n\tif (!gl_config.gles && gl_dither.modified)\n\t{\n\t\tgl_dither.modified = false;\n\t\tif (gl_dither.ival)\n\t\t{\n\t\t\tqglEnable(GL_DITHER);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tqglDisable(GL_DITHER);\n\t\t}\n\t}\n}\n\nvoid Surf_SetupFrame(void);\n\n/*\n================\nR_RenderScene\n\nr_refdef must be set before the first call\n================\n*/\nstatic void R_RenderScene_Internal(void)\n{\n\textern qboolean depthcleared;\n\tint tmpvisents = cl_numvisedicts;\n\tTRACE((\"dbg: calling R_SetFrustrum\\n\"));\n\tif (!r_refdef.recurse)\n\t\tR_SetFrustum (r_refdef.m_projection_std, r_refdef.m_view);\n\n\tRQ_BeginFrame();\n\n\tTRACE((\"dbg: calling Surf_DrawWorld\\n\"));\n\tSurf_DrawWorld ();\t\t// adds static entities to the list\n\n\tS_ExtraUpdate ();\t// don't let sound get messed up if going slow\n\n//\tR_DrawDecals();\n\n\tTRACE((\"dbg: calling R_RenderDlights\\n\"));\n\tR_RenderDlights ();\n\n\tif (r_refdef.recurse)\n\t\tRQ_RenderBatch();\n\telse\n\t\tRQ_RenderBatchClear();\n\n\tcl_numvisedicts = tmpvisents;\n\n\tdepthcleared = false;\t//whatever is in the depth buffer is no longer useful.\n\n\tif (vrui.enabled)\n\t{\n\t\tvec3_t uifwd, uiright, uiup;\n\t\tvec3_t diff;\n\t\tfloat d;\n\t\tvec3_t ctrlang, ctrlorg, aimdir;\n\t\textern cvar_t cl_vrui_lock;\n\n\t\tif (cl_vrui_lock.ival)\n\t\t\tVRUI_SnapAngle();\n\n//\t\textern usercmd_t cl_pendingcmd[MAX_SPLITS];\n\t\tAngleVectors(vrui.angles, uifwd, uiright, uiup);\n\n\t\tVectorAngles(uiright, uifwd, r_worldentity.angles, false);\n\t\tAngleVectors(r_worldentity.angles, r_worldentity.axis[0], r_worldentity.axis[1], r_worldentity.axis[2]);\n\t\tVectorNegate(r_worldentity.axis[1], r_worldentity.axis[1]);\n\t\tr_worldentity.scale = 1;//0.2;\n\t\tVectorMA(r_refdef.vieworg, Cvar_Get(\"2dz\", \"256\", 0, \"\")->value*r_worldentity.scale, r_worldentity.axis[2], r_worldentity.origin);\n\t\tVectorMA(r_worldentity.origin, -(int)(vid.width/2)*r_worldentity.scale, r_worldentity.axis[0], r_worldentity.origin);\n\t\tVectorMA(r_worldentity.origin, -(int)(vid.height/2)*r_worldentity.scale, r_worldentity.axis[1], r_worldentity.origin);\n\t\tGL_SetShaderState2D(true);\n\n\t\tVectorCopy(r_refdef.viewangles, ctrlang);\n\t\tif (r_refdef.playerview->vrdev[VRDEV_RIGHT].status&VRSTATUS_ANG)\n\t\t{\n\t\t\tctrlang[0] = SHORT2ANGLE(r_refdef.playerview->vrdev[VRDEV_RIGHT].angles[0]);\n\t\t\tctrlang[1] = SHORT2ANGLE(r_refdef.playerview->vrdev[VRDEV_RIGHT].angles[1]);\n\t\t\tctrlang[2] = SHORT2ANGLE(r_refdef.playerview->vrdev[VRDEV_RIGHT].angles[2]);\n\t\t}\n\t\telse\n\t\t\tVectorCopy(r_refdef.viewangles, ctrlang);\n\t\tif (r_refdef.playerview->vrdev[VRDEV_RIGHT].status&VRSTATUS_ORG)\n\t\t\tVectorCopy(r_refdef.playerview->vrdev[VRDEV_RIGHT].origin, ctrlorg);\n\t\telse\n\t\t\tVectorCopy(r_refdef.vieworg, ctrlorg);\n\n\t\tAngleVectors(ctrlang, aimdir, NULL, NULL);\n\t\tVectorSubtract(r_worldentity.origin, ctrlorg, diff);\n\t\t//d = DotProduct(diff, vpn); //figure out how far we need to move it to get an impact.\n\t\td = DotProduct(diff, r_worldentity.axis[2]);\n\t\td /= DotProduct(aimdir, r_worldentity.axis[2]);\t//compensate for the length.\n\t\tVectorMA(ctrlorg, d, aimdir, diff);\t//calc the impact point...\n\t\tVectorSubtract(diff, r_worldentity.origin, diff);\n\t\tmousecursor_x = DotProduct(diff, r_worldentity.axis[0]);\n\t\tmousecursor_y = DotProduct(diff, r_worldentity.axis[1]);\n\t\tmousecursor_x = bound(0, mousecursor_x, vid.width-1);\n\t\tmousecursor_y = bound(0, mousecursor_y, vid.height-1);\n\n#ifdef PLUGINS\n\t\tPlug_SBar (r_refdef.playerview);\n#else\n\t\tif (Sbar_ShouldDraw(r_refdef.playerview))\n\t\t{\n\t\t\tSCR_TileClear (sb_lines);\n\t\t\tSbar_Draw (r_refdef.playerview);\n\t\t\tSbar_DrawScoreboard (r_refdef.playerview);\n\t\t}\n\t\telse\n\t\t\tSCR_TileClear (0);\n#endif\n\n\t\tSCR_DrawTwoDimensional(true);\n\n\t\tVectorClear(r_worldentity.origin);\n\t\tVectorClear(r_worldentity.angles);\n\t\tVectorSet(r_worldentity.axis[0], 1,0,0);\n\t\tVectorSet(r_worldentity.axis[1], 0,1,0);\n\t\tVectorSet(r_worldentity.axis[2], 0,0,1);\n\t\tr_worldentity.scale = 1;\n\t\tGL_SetShaderState2D(false);\n\t}\n}\nstatic void R_RenderEyeScene (texid_t rendertarget, const pxrect_t *viewport, const vec4_t fovoverride, const float projmatrix[16], const float eyematrix[12])\n{\n\textern qboolean depthcleared;\n\trefdef_t refdef = r_refdef;\n\tint pw = vid.fbpwidth;\n\tint ph = vid.fbpheight;\n\tint r = 0;\n\n\textern void CL_ClampPitch (int pnum, float frametime);\n/*the vr code tends to be somewhat laggy with its head angles, leaving it to the last minute, so redo this to reduce latency*/\n\tif ((size_t)(refdef.playerview-cl.playerview) < MAX_SPLITS)\n\t\tCL_ClampPitch (refdef.playerview-cl.playerview, 0);\n\n\tvrui.enabled = true;\n\tif (rendertarget)\n\t{\n\t\tr = GLBE_FBO_Update(&fbo_vr, FBO_RB_DEPTH, &rendertarget, 1, r_nulltex,  rendertarget->width, rendertarget->height, 0);\n\t\tGL_ForceDepthWritable();\n\t\tqglClear (GL_DEPTH_BUFFER_BIT|GL_COLOR_BUFFER_BIT);\n\t\tdepthcleared = true;\n\t\tvid.fbpwidth = rendertarget->width;\n\t\tvid.fbpheight = rendertarget->height;\n\t}\n\n\tR_SetupGL (eyematrix, fovoverride, projmatrix, viewport, rendertarget);\n\tR_RenderScene_Internal();\n\n\tif (rendertarget)\n\t{\n\t\tGLBE_FBO_Pop(r);\n\t\tif (gl_finish.ival)\n\t\t\tqglFinish();\n\t}\n\n\tr_refdef = refdef;\n\tvid.fbpwidth = pw;\n\tvid.fbpheight = ph;\n}\nstatic void R_RenderScene (void)\n{\n\tfloat stereooffset[2];\n\tint stereoframes = 1;\n\tint stereomode;\n\tint i;\n\tint cull = r_refdef.flipcull;\n\tunsigned int colourmask = r_refdef.colourmask;\n\tvec3_t eyeangorg[2];\n\tfloat eyematrix[12];\n\textern qboolean\t\tdepthcleared;\n\n\tr_refdef.colourmask = 0u;\n\tstereomode = r_refdef.stereomethod;\n\tif (stereomode == STEREO_QUAD)\n\t{\n#ifdef GL_STEREO\n\t\tGLint glb;\n\t\tqglGetIntegerv(GL_STEREO, &glb);\n\t\tif (!glb || !qglDrawBuffer)\n#endif\n\t\t\tstereomode = STEREO_OFF;\t//we are not a stereo context, so no stereoscopic rendering (this encourages it to otherwise be left enabled, which means the user is more likely to spot that they asked it to give a slower context.\n\t}\n\n\n\tif (r_refdef.recurse || !stereomode)// || !(r_stereo_separation.value||r_stereo_convergence.value))\n\t{\n\t\tstereooffset[0] = 0;\n\t\tstereoframes = 1;\n\t\tstereomode = STEREO_OFF;\n\t}\n\telse\t\n\t{\n\t\tstereooffset[0] = -0.5*r_stereo_separation.value;\n\t\tstereooffset[1] = +0.5*r_stereo_separation.value;\n\t\tstereoframes = 2;\n\t}\n\n\tif (vid.vr && !r_refdef.recurse && vid.vr->Render(R_RenderEyeScene))\n\t\t;\t//we drew something VR-ey\n\telse if (stereomode == STEREO_OFF)\n\t{\n\t\tGL_ForceDepthWritable();\n\t\tqglClear (GL_DEPTH_BUFFER_BIT);\n\n\t\tR_SetupGL (NULL, NULL, NULL, NULL, NULL);\n\t\tR_RenderScene_Internal();\n\t}\n\telse for (i = 0; i < stereoframes; i++)\n\t{\n\t\tr_refdef.colourmask = 0u;\n\t\tswitch (stereomode)\n\t\t{\n\t\tdefault:\n\t\tcase STEREO_OFF:\t//off\n\t\t\tif (i)\n\t\t\t\treturn;\n\t\t\tbreak;\n#ifdef GL_STEREO\n\t\tcase STEREO_QUAD:\t//proper gl stereo rendering\n\t\t\tif (stereooffset[i] < 0)\n\t\t\t\tqglDrawBuffer(GL_BACK_LEFT);\n\t\t\telse\n\t\t\t\tqglDrawBuffer(GL_BACK_RIGHT);\n\t\t\tbreak;\n#endif\n\t\tcase STEREO_RED_CYAN:\t//red/cyan(green+blue)\n\t\t\tif (stereooffset[i] < 0)\n\t\t\t\tr_refdef.colourmask = (SBITS_MASK_GREEN|SBITS_MASK_BLUE);\n\t\t\telse\n\t\t\t\tr_refdef.colourmask = SBITS_MASK_RED;\n\t\t\tbreak;\n\t\tcase STEREO_RED_BLUE: //red/blue\n\t\t\tif (stereooffset[i] < 0)\n\t\t\t\tr_refdef.colourmask = (SBITS_MASK_GREEN|SBITS_MASK_BLUE);\n\t\t\telse\n\t\t\t\tr_refdef.colourmask = (SBITS_MASK_RED|SBITS_MASK_GREEN);\n\t\t\tbreak;\n\t\tcase STEREO_RED_GREEN:\t//red/green\n\t\t\tif (stereooffset[i] < 0)\n\t\t\t\tr_refdef.colourmask = (SBITS_MASK_GREEN|SBITS_MASK_BLUE);\n\t\t\telse\n\t\t\t\tr_refdef.colourmask = (SBITS_MASK_RED|SBITS_MASK_BLUE);\n\t\t\tbreak;\n\t\tcase STEREO_CROSSEYED:\t//eyestrain\n\t\t\tbreak;\n\t\tcase STEREO_LEFTONLY:\n\t\t\tif (i != 0)\n\t\t\t\tcontinue;\n\t\t\tbreak;\n\t\tcase STEREO_RIGHTONLY:\n\t\t\tif (i != 1)\n\t\t\t\tcontinue;\n\t\t\t//fixme: depth buffer doesn't need clearing\n\t\t\tbreak;\n\t\t}\n\n\t\tif (!depthcleared)\n\t\t{\n\t\t\tGL_ForceDepthWritable();\n\t\t\tqglClear (GL_DEPTH_BUFFER_BIT);\n\t\t\tdepthcleared = true;\n\t\t}\n\n\t\teyeangorg[0][0] = 0;\n\t\teyeangorg[0][1] = r_stereo_convergence.value * (i?0.5:-0.5);\n\t\teyeangorg[0][2] = 0;\n\t\tVectorSet(eyeangorg[1], 0, stereooffset[i], 0);\n\t\tMatrix3x4_RM_FromAngles(eyeangorg[0], eyeangorg[1], eyematrix);\n\t\tR_SetupGL (eyematrix, NULL, NULL, NULL, NULL);\n\t\tR_RenderScene_Internal ();\n\t}\n\n\tswitch (stereomode)\n\t{\n\tdefault:\n\tcase STEREO_OFF:\n\tcase STEREO_LEFTONLY:\n\tcase STEREO_RIGHTONLY:\n\t\tbreak;\n\tcase STEREO_QUAD:\n\t\tqglDrawBuffer(GL_BACK);\n\t\tbreak;\n\tcase STEREO_RED_BLUE:\t//green should have already been cleared.\n\tcase STEREO_RED_GREEN:\t//blue should have already been cleared.\n\tcase STEREO_RED_CYAN:\n\t\tbreak;\n\tcase 5:\n\t\tbreak;\n\t}\n\n\tr_refdef.flipcull = cull;\n\tr_refdef.colourmask = colourmask;\n}\n/*generates a new modelview matrix, as well as vpn vectors*/\nstatic void R_MirrorMatrix(plane_t *plane)\n{\n\tfloat mirror[16];\n\tfloat view[16];\n\tfloat result[16];\n\n\tvec3_t pnorm;\n\tVectorNegate(plane->normal, pnorm);\n\n\tmirror[0] = 1-2*pnorm[0]*pnorm[0];\n\tmirror[1] = -2*pnorm[0]*pnorm[1];\n\tmirror[2] = -2*pnorm[0]*pnorm[2];\n\tmirror[3] = 0;\n\n\tmirror[4] = -2*pnorm[1]*pnorm[0];\n\tmirror[5] = 1-2*pnorm[1]*pnorm[1];\n\tmirror[6] = -2*pnorm[1]*pnorm[2] ;\n\tmirror[7] = 0;\n\n\tmirror[8]  = -2*pnorm[2]*pnorm[0];\n\tmirror[9]  = -2*pnorm[2]*pnorm[1];\n\tmirror[10] = 1-2*pnorm[2]*pnorm[2];\n\tmirror[11] = 0;\n\n\tmirror[12] = -2*pnorm[0]*plane->dist;\n\tmirror[13] = -2*pnorm[1]*plane->dist;\n\tmirror[14] = -2*pnorm[2]*plane->dist;\n\tmirror[15] = 1;\n\n\tview[0] = vpn[0];\n\tview[1] = vpn[1];\n\tview[2] = vpn[2];\n\tview[3] = 0;\n\n\tview[4] = -vright[0];\n\tview[5] = -vright[1];\n\tview[6] = -vright[2];\n\tview[7] = 0;\n\n\tview[8]  = vup[0];\n\tview[9]  = vup[1];\n\tview[10] = vup[2];\n\tview[11] = 0;\n\n\tview[12] = r_refdef.vieworg[0];\n\tview[13] = r_refdef.vieworg[1];\n\tview[14] = r_refdef.vieworg[2];\n\tview[15] = 1;\n\n\tVectorMA(r_refdef.vieworg, 0.25, plane->normal, r_refdef.pvsorigin);\n\n\tMatrix4_Multiply(mirror, view, result);\n\n\tvpn[0] = result[0];\n\tvpn[1] = result[1];\n\tvpn[2] = result[2];\n\n\tvright[0] = -result[4];\n\tvright[1] = -result[5];\n\tvright[2] = -result[6];\n\n\tvup[0] = result[8];\n\tvup[1] = result[9];\n\tvup[2] = result[10];\n\n\tr_refdef.vieworg[0] = result[12];\n\tr_refdef.vieworg[1] = result[13];\n\tr_refdef.vieworg[2] = result[14];\n}\nstatic entity_t *R_FindPortalCamera(entity_t *rent)\n{\n\tint i;\n\tfor (i = 0; i < cl_numvisedicts; i++)\n\t{\n\t\tif (cl_visedicts[i].rtype == RT_PORTALCAMERA)\n\t\t{\n\t\t\tif (cl_visedicts[i].keynum == rent->keynum )\n\t\t\t\treturn &cl_visedicts[i];\n\t\t}\n\t}\n\treturn NULL;\n}\nstatic entity_t *R_NearestPortal(plane_t *plane)\n{\n\tint i;\n\tentity_t *best = NULL;\n\tfloat dist, bestd = 0;\n\n\t//for q3-compat, portals on world scan for a visedict to use for their view.\n\tfor (i = 0; i < cl_numvisedicts; i++)\n\t{\n\t\tif (cl_visedicts[i].rtype == RT_PORTALSURFACE)\n\t\t{\n\t\t\tdist = DotProduct(cl_visedicts[i].origin, plane->normal)-plane->dist;\n\t\t\tdist = fabs(dist);\n\t\t\tif (dist < 64 && (!best || dist < bestd))\n\t\t\t\tbest = &cl_visedicts[i];\n\t\t}\n\t}\n\treturn best;\n}\n\nstatic void TransformCoord(vec3_t in, vec3_t planea[3], vec3_t planeo, vec3_t viewa[3], vec3_t viewo, vec3_t result)\n{\n\tint\t\ti;\n\tvec3_t\tlocal;\n\tvec3_t\ttransformed;\n\tfloat\td;\n\n\tlocal[0] = in[0] - planeo[0];\n\tlocal[1] = in[1] - planeo[1];\n\tlocal[2] = in[2] - planeo[2];\n\n\tVectorClear(transformed);\n\tfor ( i = 0 ; i < 3 ; i++ )\n\t{\n\t\td = DotProduct(local, planea[i]);\n\t\tVectorMA(transformed, d, viewa[i], transformed);\n\t}\n\n\tresult[0] = transformed[0] + viewo[0];\n\tresult[1] = transformed[1] + viewo[1];\n\tresult[2] = transformed[2] + viewo[2];\n}\nstatic void TransformDir(vec3_t in, vec3_t planea[3], vec3_t viewa[3], vec3_t result)\n{\n\tint\t\ti;\n\tfloat\td;\n\tvec3_t tmp;\n\n\tVectorCopy(in, tmp);\n\n\tVectorClear(result);\n\tfor ( i = 0 ; i < 3 ; i++ )\n\t{\n\t\td = DotProduct(tmp, planea[i]);\n\t\tVectorMA(result, d, viewa[i], result);\n\t}\n}\n\nvoid R_ObliqueNearClip(float *viewmat, mplane_t *wplane);\nvoid CL_DrawDebugPlane(float *normal, float dist, float r, float g, float b, qboolean enqueue);\nvoid GLR_DrawPortal(batch_t *batch, batch_t **blist, batch_t *depthmasklist[2], int portaltype)\n{\n\tentity_t *view, *surfent;\n//\tGLdouble glplane[4];\n\tplane_t plane, oplane;\n\tfloat vmat[16];\n\trefdef_t oldrefdef;\n\tvec3_t r;\n\tint i;\n\tmesh_t *mesh = batch->mesh[batch->firstmesh];\n\tpvsbuffer_t newvis;\n\tfloat ivmat[16], trmat[16];\n\n\tif (mesh->xyz_array)\n\t{\n\t\tif (!mesh->normals_array)\n\t\t{\n\t\t\tVectorSet(plane.normal, 0, 0, 1);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tVectorCopy(mesh->normals_array[0], plane.normal);\n\t\t}\n\n\t\tif (batch->ent == &r_worldentity)\n\t\t{\n\t\t\tplane.dist = DotProduct(mesh->xyz_array[0], plane.normal);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tvec3_t point;\n\t\t\tVectorCopy(plane.normal, oplane.normal);\n\t\t\t//rotate the surface normal around its entity's matrix\n\t\t\tplane.normal[0] = oplane.normal[0]*batch->ent->axis[0][0] + oplane.normal[1]*batch->ent->axis[1][0] + oplane.normal[2]*batch->ent->axis[2][0];\n\t\t\tplane.normal[1] = oplane.normal[0]*batch->ent->axis[0][1] + oplane.normal[1]*batch->ent->axis[1][1] + oplane.normal[2]*batch->ent->axis[2][1];\n\t\t\tplane.normal[2] = oplane.normal[0]*batch->ent->axis[0][2] + oplane.normal[1]*batch->ent->axis[1][2] + oplane.normal[2]*batch->ent->axis[2][2];\n\n\t\t\t//rotate some point on the mesh around its entity's matrix\n\t\t\tpoint[0] = mesh->xyz_array[0][0]*batch->ent->axis[0][0] + mesh->xyz_array[0][1]*batch->ent->axis[1][0] + mesh->xyz_array[0][2]*batch->ent->axis[2][0] + batch->ent->origin[0];\n\t\t\tpoint[1] = mesh->xyz_array[0][0]*batch->ent->axis[0][1] + mesh->xyz_array[0][1]*batch->ent->axis[1][1] + mesh->xyz_array[0][2]*batch->ent->axis[2][1] + batch->ent->origin[1];\n\t\t\tpoint[2] = mesh->xyz_array[0][0]*batch->ent->axis[0][2] + mesh->xyz_array[0][1]*batch->ent->axis[1][2] + mesh->xyz_array[0][2]*batch->ent->axis[2][2] + batch->ent->origin[2];\n\n\t\t\t//now we can figure out the plane dist\n\t\t\tplane.dist = DotProduct(point, plane.normal);\n\t\t}\n\t}\n\telse\n\t\treturn;\n\n\t//if we're too far away from the surface, don't draw anything\n\tif (batch->shader->flags & SHADER_AGEN_PORTAL)\n\t{\n\t\t/*there's a portal alpha blend on that surface, that fades out after this distance*/\n\t\tif (DotProduct(r_refdef.vieworg, plane.normal)-plane.dist > batch->shader->portaldist)\n\t\t\treturn;\n\t}\n\t//if we're behind it, then also don't draw anything. for our purposes, behind is when the entire near clipplane is behind.\n\tif (DotProduct(r_refdef.vieworg, plane.normal)-plane.dist < -r_refdef.mindist)\n\t\treturn;\n\n\tif (r_refdef.recurse >= R_MAX_RECURSE-1)\n\t{\n\t\tGLBE_SelectMode(BEM_DEPTHDARK);\n\t\tGLBE_SubmitBatch(batch);\n\t\tGLBE_SelectMode(BEM_STANDARD);\n\t\treturn;\n\t}\n\n\n\tTRACE((\"GLR_DrawPortal: portal type %i\\n\", portaltype));\n\n\toldrefdef = r_refdef;\n\tr_refdef.recurse+=1;\n\n\tr_refdef.externalview = true;\n\n\tswitch(portaltype)\n\t{\n\tcase 1: /*fbo explicit mirror (fucked depth, working clip plane)*/\n\t\t//fixme: pvs is surely wrong?\n//\t\tr_refdef.flipcull ^= SHADER_CULL_FLIP;\n\t\tR_MirrorMatrix(&plane);\n\t\tMatrix4x4_CM_ModelViewMatrixFromAxis(vmat, vpn, vright, vup, r_refdef.vieworg);\n\n\t\tVectorCopy(mesh->xyz_array[0], r_refdef.pvsorigin);\n\t\tfor (i = 1; i < mesh->numvertexes; i++)\n\t\t\tVectorAdd(r_refdef.pvsorigin, mesh->xyz_array[i], r_refdef.pvsorigin);\n\t\tVectorScale(r_refdef.pvsorigin, 1.0/mesh->numvertexes, r_refdef.pvsorigin);\n\t\tbreak;\n\t\n\tcase 2:\t/*fbo refraction (fucked depth, working clip plane)*/\n\tcase 3:\t/*screen copy refraction (screen depth, fucked clip planes)*/\n\t\t/*refraction image (same view, just with things culled*/\n\t\tr_refdef.externalview = oldrefdef.externalview;\n\t\tVectorNegate(plane.normal, plane.normal);\n\t\tplane.dist = -plane.dist;\n\n\t\t//use the player's origin for r_viewleaf, because there's not much we can do anyway*/\n\t\tVectorCopy(r_origin, r_refdef.pvsorigin);\n\n\t\tif (cl.worldmodel && cl.worldmodel->funcs.ClusterPVS && !r_novis.ival)\n\t\t{\n\t\t\tint clust, i, j;\n\t\t\tfloat d;\n\t\t\tvec3_t point;\n\t\t\tr_refdef.forcevis = false;\n\t\t\tr_refdef.forcedvis = NULL;\n\t\t\tnewvis.buffer = alloca(newvis.buffersize=cl.worldmodel->pvsbytes);\n\t\t\tfor (i = batch->firstmesh; i < batch->meshes; i++)\n\t\t\t{\n\t\t\t\tmesh = batch->mesh[i];\n\t\t\t\tif (!mesh->xyz_array)\n\t\t\t\t\tcontinue;\n\t\t\t\tr_refdef.forcevis = true;\n\t\t\t\tVectorClear(point);\n\t\t\t\tfor (j = 0; j < mesh->numvertexes; j++)\n\t\t\t\t\tVectorAdd(point, mesh->xyz_array[j], point);\n\t\t\t\tVectorScale(point, 1.0f/mesh->numvertexes, point);\n\t\t\t\td = DotProduct(point, plane.normal) - plane.dist;\n\t\t\t\td += 0.1;\t//an epsilon on the far side\n\t\t\t\tVectorMA(point, d, plane.normal, point);\n\n\t\t\t\tclust = cl.worldmodel->funcs.ClusterForPoint(cl.worldmodel, point, NULL);\n\t\t\t\tif (i == batch->firstmesh)\n\t\t\t\t\tr_refdef.forcedvis = cl.worldmodel->funcs.ClusterPVS(cl.worldmodel, clust, &newvis, PVM_REPLACE);\n\t\t\t\telse\n\t\t\t\t\tr_refdef.forcedvis = cl.worldmodel->funcs.ClusterPVS(cl.worldmodel, clust, &newvis, PVM_MERGE);\n\t\t\t}\n//\t\t\tmemset(newvis, 0xff, pvsbytes);\n\t\t}\n\t\tMatrix4x4_CM_ModelViewMatrixFromAxis(vmat, vpn, vright, vup, r_refdef.vieworg);\n\t\tbreak;\n\n\tcase 0:\t\t/*q3 portal*/\n\tdefault:\n#ifdef CSQC_DAT\n\t\tif (CSQC_SetupToRenderPortal(batch->ent->keynum))\n\t\t{\n\t\t\toplane = plane;\n\n\t\t\t//transform the old surface plane into the new view matrix\n\t\t\tMatrix4_Invert(r_refdef.m_view, ivmat);\n\t\t\tMatrix4x4_CM_ModelViewMatrixFromAxis(vmat, vpn, vright, vup, r_refdef.vieworg);\n\t\t\tMatrix4_Multiply(ivmat, vmat, trmat);\n\t\t\tplane.normal[0] = -(oplane.normal[0] * trmat[0] + oplane.normal[1] * trmat[1] + oplane.normal[2] * trmat[2]);\n\t\t\tplane.normal[1] = -(oplane.normal[0] * trmat[4] + oplane.normal[1] * trmat[5] + oplane.normal[2] * trmat[6]);\n\t\t\tplane.normal[2] = -(oplane.normal[0] * trmat[8] + oplane.normal[1] * trmat[9] + oplane.normal[2] * trmat[10]);\n\t\t\tplane.dist = -oplane.dist + trmat[12]*oplane.normal[0] + trmat[13]*oplane.normal[1] + trmat[14]*oplane.normal[2];\n\n\t\t\tif (Cvar_Get(\"temp_useplaneclip\", \"1\", 0, \"temp\")->ival)\n\t\t\t\tportaltype = 1;\t//make sure the near clipplane is used.\n\t\t\tbreak;\n\t\t}\n#endif\n\t\tsurfent = batch->ent;\n\t\tif (batch->ent->keynum)\n\t\t\tview = R_FindPortalCamera(batch->ent);\n\t\telse\n\t\t{\n\t\t\tview = R_NearestPortal(&plane);\n\t\t\tif (view)\n\t\t\t{\t//for q3bsps where the portal surface is embedded in the bsp itself, we need an extra leyer of indirection.\n\t\t\t\tentity_t *oc = R_FindPortalCamera(view);\n\t\t\t\tif(oc)\n\t\t\t\t{\n\t\t\t\t\tsurfent = view;\n\t\t\t\t\tview = oc;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (view && view->rtype == RT_PORTALCAMERA)\n\t\t{\t//q1-style portal, where the portal is defined via attachments\n\t\t\t//the portal plane itself is assumed to be facing directly forwards from the entity that we're drawing, and with the same origin.\n\t\t\toplane = plane;\n\n\t\t\tTransformCoord(r_refdef.vieworg, surfent->axis, surfent->origin, view->axis, view->origin, r_refdef.vieworg);\n\t\t\tTransformDir(vpn, surfent->axis, view->axis, vpn);\n\t\t\tTransformDir(vright, surfent->axis, view->axis, vright);\n\t\t\tTransformDir(vup, surfent->axis, view->axis, vup);\n\n\t\t\t//transform the old surface plane into the new view matrix\n\t\t\tMatrix4_Invert(r_refdef.m_view, ivmat);\n\t\t\tMatrix4x4_CM_ModelViewMatrixFromAxis(vmat, vpn, vright, vup, r_refdef.vieworg);\n\t\t\tMatrix4_Multiply(ivmat, vmat, trmat);\n\t\t\tplane.normal[0] = -(oplane.normal[0] * trmat[0] + oplane.normal[1] * trmat[1] + oplane.normal[2] * trmat[2]);\n\t\t\tplane.normal[1] = -(oplane.normal[0] * trmat[4] + oplane.normal[1] * trmat[5] + oplane.normal[2] * trmat[6]);\n\t\t\tplane.normal[2] = -(oplane.normal[0] * trmat[8] + oplane.normal[1] * trmat[9] + oplane.normal[2] * trmat[10]);\n\t\t\tplane.dist = -oplane.dist + trmat[12]*oplane.normal[0] + trmat[13]*oplane.normal[1] + trmat[14]*oplane.normal[2];\n\n\t\t\tportaltype = 1;\t//make sure the near clipplane is used.\n\t\t\tbreak;\n\t\t}\n\n\t\t//portal surfaces with the same origin+oldorigin are explicit mirrors, and skipped in this case.\n\t\tif (view && view->rtype == RT_PORTALSURFACE && !VectorCompare(view->origin, view->oldorigin))\n\t\t{\t//q3-style portal, where a single entity provides orientation+two origins\n\t\t\tfloat d;\n\t\t\tvec3_t paxis[3], porigin, vaxis[3], vorg;\n\n\t\t\toplane = plane;\n\n\t\t\t/*calculate where the surface is meant to be*/\n\t\t\tVectorCopy(mesh->normals_array[0], paxis[0]);\n\t\t\tPerpendicularVector(paxis[1], paxis[0]);\n\t\t\tCrossProduct(paxis[0], paxis[1], paxis[2]);\n\t\t\td = DotProduct(view->origin, plane.normal) - plane.dist;\n\t\t\tVectorMA(view->origin, -d, paxis[0], porigin);\n\n\t\t\t/*grab the camera origin*/\n\t\t\tVectorNegate(view->axis[0], vaxis[0]);\n\t\t\tVectorNegate(view->axis[1], vaxis[1]);\n\t\t\tVectorCopy(view->axis[2], vaxis[2]);\n\t\t\tVectorCopy(view->oldorigin, vorg);\n\n\t\t\tVectorCopy(vorg, r_refdef.pvsorigin);\n\n\t\t\t/*rotate it a bit*/\n\t\t\tif (view->framestate.g[FS_REG].frame[1])\t//oldframe\n\t\t\t{\n\t\t\t\tif (view->framestate.g[FS_REG].frame[0])\t//newframe\n\t\t\t\t\td = realtime * view->framestate.g[FS_REG].frame[0];\t//newframe\n\t\t\t\telse\n\t\t\t\t\td = view->skinnum + sin(realtime)*4;\n\t\t\t}\n\t\t\telse\n\t\t\t\td = view->skinnum;\n\n\t\t\tif (d)\n\t\t\t{\n\t\t\t\tvec3_t rdir;\n\t\t\t\tVectorCopy(vaxis[1], rdir);\n\t\t\t\tRotatePointAroundVector(vaxis[1], vaxis[0], rdir, d);\n\t\t\t\tCrossProduct(vaxis[0], vaxis[1], vaxis[2]);\n\t\t\t}\n\n\t\t\tTransformCoord(oldrefdef.vieworg, paxis, porigin, vaxis, vorg, r_refdef.vieworg);\n\t\t\tTransformDir(vpn, paxis, vaxis, vpn);\n\t\t\tTransformDir(vright, paxis, vaxis, vright);\n\t\t\tTransformDir(vup, paxis, vaxis, vup);\n\t\t\tMatrix4x4_CM_ModelViewMatrixFromAxis(vmat, vpn, vright, vup, r_refdef.vieworg);\n\n\t\t\t//transform the old surface plane into the new view matrix\n\t\t\tif (Matrix4_Invert(r_refdef.m_view, ivmat))\n\t\t\t{\n\t\t\t\tMatrix4_Multiply(ivmat, vmat, trmat);\n\t\t\t\tplane.normal[0] = -(oplane.normal[0] * trmat[0] + oplane.normal[1] * trmat[1] + oplane.normal[2] * trmat[2]);\n\t\t\t\tplane.normal[1] = -(oplane.normal[0] * trmat[4] + oplane.normal[1] * trmat[5] + oplane.normal[2] * trmat[6]);\n\t\t\t\tplane.normal[2] = -(oplane.normal[0] * trmat[8] + oplane.normal[1] * trmat[9] + oplane.normal[2] * trmat[10]);\n\t\t\t\tplane.dist = -oplane.dist + trmat[12]*oplane.normal[0] + trmat[13]*oplane.normal[1] + trmat[14]*oplane.normal[2];\n\t\t\t\tportaltype = 1;\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t\t//fixme: q3 gamecode has explicit mirrors. we 'should' just ignore the surface if we've not seen it yet.\n\n\t\t//a portal with no portal entity, or a portal rentity with an origin equal to its oldorigin, is a mirror.\n\t\tR_MirrorMatrix(&plane);\n\t\tMatrix4x4_CM_ModelViewMatrixFromAxis(vmat, vpn, vright, vup, r_refdef.vieworg);\n\n\t\tVectorCopy(mesh->xyz_array[0], r_refdef.pvsorigin);\n\t\tfor (i = 1; i < mesh->numvertexes; i++)\n\t\t\tVectorAdd(r_refdef.pvsorigin, mesh->xyz_array[i], r_refdef.pvsorigin);\n\t\tVectorScale(r_refdef.pvsorigin, 1.0/mesh->numvertexes, r_refdef.pvsorigin);\n\n\t\tportaltype = 1;\n\t\tbreak;\n\t}\n\n\t/*FIXME: can we get away with stenciling the screen?*/\n\t/*Add to frustum culling instead of clip planes?*/\n/*\tif (qglClipPlane && portaltype)\n\t{\n\t\tGLdouble glplane[4];\n\t\tglplane[0] = plane.normal[0];\n\t\tglplane[1] = plane.normal[1];\n\t\tglplane[2] = plane.normal[2];\n\t\tglplane[3] = plane.dist;\n\t\tqglClipPlane(GL_CLIP_PLANE0, glplane);\n\t\tqglEnable(GL_CLIP_PLANE0);\n\t}\n*/\t//fixme: we can probably scissor a smaller frusum\n\tR_SetFrustum (r_refdef.m_projection_std, vmat);\n\tif (r_refdef.frustum_numplanes < MAXFRUSTUMPLANES)\n\t{\n\t\textern int SignbitsForPlane (mplane_t *out);\n\t\tmplane_t fp;\n\t\tVectorCopy(plane.normal, fp.normal);\n\t\tfp.dist = plane.dist;\n\n//\t\tif (DotProduct(fp.normal, vpn) < 0)\n//\t\t{\n//\t\t\tVectorNegate(fp.normal, fp.normal);\n//\t\t\tfp.dist *= -1;\n//\t\t}\n\n\t\tfp.type = PLANE_ANYZ;\n\t\tfp.signbits = SignbitsForPlane (&fp);\n\n\t\tif (portaltype == 1 || portaltype == 2)\n\t\t\tR_ObliqueNearClip(vmat, &fp);\n\n\t\t//our own culling should be an epsilon forwards so we don't still draw things behind the line due to precision issues.\n\t\tfp.dist += 0.01;\n\t\tr_refdef.frustum[r_refdef.frustum_numplanes++] = fp;\n\t}\n#if 1\n\tif (depthmasklist)\n\t{\n\t\t/*draw already-drawn portals as depth-only, to ensure that their contents are not harmed*/\n\t\t/*we can only do this AFTER the oblique perspective matrix is calculated, to avoid depth inconsistancies, while we still have the old view matrix*/\n\t\tint i;\n\t\tbatch_t *dmask = NULL;\n\t\tif (qglLoadMatrixf)\n\t\t{\n\t\t\tqglMatrixMode(GL_PROJECTION);\n\t\t\tqglLoadMatrixf(r_refdef.m_projection_std);\n\t\t\tqglMatrixMode(GL_MODELVIEW);\n\t\t}\n\t\t//portals to mask are relative to the old view still.\n\t\tcurrententity = NULL;\n\t\tif (gl_config.arb_depth_clamp)\n\t\t\tqglEnable(GL_DEPTH_CLAMP_ARB);\t//ignore the near clip plane(ish), this means nearer portals can still mask further ones.\n\t\tGL_ForceDepthWritable();\n\t\tGLBE_SelectMode(BEM_DEPTHONLY);\n\t\tfor (i = 0; i < 2; i++)\n\t\t{\n\t\t\tfor (dmask = depthmasklist[i]; dmask; dmask = dmask->next)\n\t\t\t{\n\t\t\t\tif (dmask == batch)\n\t\t\t\t\tcontinue;\n//\t\t\t\tif (dmask->meshes == dmask->firstmesh)\n//\t\t\t\t\tcontinue;\n\t\t\t\tGLBE_SubmitBatch(dmask);\n\t\t\t}\n\t\t}\n\t\tGLBE_SelectMode(BEM_STANDARD);\n\t\tif (gl_config.arb_depth_clamp)\n\t\t\tqglDisable(GL_DEPTH_CLAMP_ARB);\n\n\t\tcurrententity = NULL;\n\t}\n#endif\n//\tr_refdef = oldrefdef;\n//\treturn;\n\n\t//now determine the stuff the backend will use.\n\tmemcpy(r_refdef.m_view, vmat, sizeof(float)*16);\n\tVectorAngles(vpn, vup, r_refdef.viewangles, false);\n\tVectorCopy(r_refdef.vieworg, r_origin);\n\n\t//determine r_refdef.flipcull & SHADER_CULL_FLIP based upon whether right is right or not.\n\tCrossProduct(vpn, vup, r);\n\tif (DotProduct(r, vright) < 0)\n\t\tr_refdef.flipcull |= SHADER_CULL_FLIP;\n\telse\n\t\tr_refdef.flipcull &= ~SHADER_CULL_FLIP;\n\tif (r_refdef.m_projection_std[5]<0)\n\t\tr_refdef.flipcull ^= SHADER_CULL_FLIP;\n\n\tSurf_SetupFrame();\n\t//FIXME: just call Surf_DrawWorld instead?\n\tR_RenderScene();\n//\tif (qglClipPlane)\n//\t\tqglDisable(GL_CLIP_PLANE0);\n\n\tif (r_portaldrawplanes.ival)\n\t{\n\t\t//the front of the plane should generally point away from the camera, and will be drawn in bright green. woo\n\t\tCL_DrawDebugPlane(plane.normal, plane.dist+0.01, 0.0, 0.5, 0.0, false);\n\t\tCL_DrawDebugPlane(plane.normal, plane.dist-0.01, 0.0, 0.5, 0.0, false);\n\t\t//the back of the plane points towards the camera, and will be drawn in blue, for the luls\n\t\tVectorNegate(plane.normal, plane.normal);\n\t\tplane.dist *= -1;\n\t\tCL_DrawDebugPlane(plane.normal, plane.dist+0.01, 0.0, 0.0, 0.2, false);\n\t\tCL_DrawDebugPlane(plane.normal, plane.dist-0.01, 0.0, 0.0, 0.2, false);\n\t}\n\n\n\tr_refdef = oldrefdef;\n\n\t/*broken stuff*/\n\tAngleVectors (r_refdef.viewangles, vpn, vright, vup);\n\tVectorCopy (r_refdef.vieworg, r_origin);\n\n\tGLBE_SelectEntity(&r_worldentity);\n\n\tGL_CullFace(0);//make sure flipcull reversion takes effect\n\n\tTRACE((\"GLR_DrawPortal: portal drawn\\n\"));\n\n#ifdef warningmsg\n#pragma warningmsg(\"warning: there's a bug with rtlights in portals, culling is broken or something. May also be loading the wrong matrix\")\n#endif\n\tcurrententity = NULL;\n}\n\n\n/*\n=============\nR_Clear\n=============\n*/\nint gldepthfunc = GL_LEQUAL;\nqboolean depthcleared;\nvoid R_Clear (qboolean fbo)\n{\n\t/*tbh, this entire function should be in the backend*/\n\t{\n\t\tif (!depthcleared || fbo)\n\t\t{\n\t\t\tGL_ForceDepthWritable();\n\t\t\t//we no longer clear colour here. we only ever (need to) do that at the start of the frame, and this point can be called multiple times per frame.\n\t\t\t//for performance, we clear the depth at the same time we clear colour, so we can skip clearing depth here the first time around each frame.\n\t\t\t//but for multiple scenes, we do need to clear depth still.\n\t\t\t//fbos always get cleared depth, just in case (colour fbos may contain junk, but hey).\n\t\t\tif ((fbo && r_clear.ival) || r_refdef.stereomethod==STEREO_RED_BLUE||r_refdef.stereomethod==STEREO_RED_GREEN)\n\t\t\t{\n\t\t\t\tqglClearColor(0, 0, 0, 1);\n\t\t\t\tqglClear (GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);\n\t\t\t}\n\t\t\telse\n\t\t\t\tqglClear (GL_DEPTH_BUFFER_BIT);\n\t\t}\n\t\tif (!fbo)\n\t\t\tdepthcleared = false;\n\t\tgldepthmin = 0;\n\t\tgldepthmax = 1;\n\t\tgldepthfunc=GL_LEQUAL;\n\t}\n}\n\n#if 0\nvoid GLR_SetupFog (void)\n{\n\tif (r_viewleaf)// && r_viewcontents != FTECONTENTS_EMPTY)\n\t{\n\t\t//\tstatic fogcolour;\n\t\tfloat fogcol[4]={0};\n\t\tfloat fogperc;\n\t\tfloat fogdist;\n\n\t\tfogperc=0;\n\t\tfogdist=512;\n\t\tswitch(r_viewcontents)\n\t\t{\n\t\tcase FTECONTENTS_WATER:\n\t\t\tfogcol[0] = 64/255.0;\n\t\t\tfogcol[1] = 128/255.0;\n\t\t\tfogcol[2] = 192/255.0;\n\t\t\tfogperc=0.2;\n\t\t\tfogdist=512;\n\t\t\tbreak;\n\t\tcase FTECONTENTS_SLIME:\n\t\t\tfogcol[0] = 32/255.0;\n\t\t\tfogcol[1] = 192/255.0;\n\t\t\tfogcol[2] = 92/255.0;\n\t\t\tfogperc=1;\n\t\t\tfogdist=256;\n\t\t\tbreak;\n\t\tcase FTECONTENTS_LAVA:\n\t\t\tfogcol[0] = 192/255.0;\n\t\t\tfogcol[1] = 32/255.0;\n\t\t\tfogcol[2] = 64/255.0;\n\t\t\tfogperc=1;\n\t\t\tfogdist=128;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tfogcol[0] = 192/255.0;\n\t\t\tfogcol[1] = 192/255.0;\n\t\t\tfogcol[2] = 192/255.0;\n\t\t\tfogperc=1;\n\t\t\tfogdist=1024;\n\t\t\tbreak;\n\t\t}\n\t\tif (fogperc)\n\t\t{\n\t\t\tqglFogi(GL_FOG_MODE, GL_LINEAR);\n\t\t\tqglFogfv(GL_FOG_COLOR, fogcol);\n\t\t\tqglFogf(GL_FOG_DENSITY, fogperc);\n\t\t\tqglFogf(GL_FOG_START, 1);\n\t\t\tqglFogf(GL_FOG_END, fogdist);\n\t\t\tqglEnable(GL_FOG);\n\t\t}\n\t}\n}\n#endif\n\nstatic void R_RenderMotionBlur(void)\n{\n#if !defined(ANDROID)\n\tint vwidth = 1, vheight = 1;\n\tfloat vs, vt, cs, ct;\n\tshader_t *shader;\n\n\t//figure out the size of our texture.\n\tif (sh_config.texture_non_power_of_two_pic)\n\t{\t//we can use any size, supposedly\n\t\tvwidth = vid.pixelwidth;\n\t\tvheight = vid.pixelheight;\n\t}\n\telse\n\t{\t//limit the texture size to square and use padding.\n\t\twhile (vwidth < vid.pixelwidth)\n\t\t\tvwidth *= 2;\n\t\twhile (vheight < vid.pixelheight)\n\t\t\tvheight *= 2;\n\t}\n\n\t//blend the last frame onto the scene\n\t//the maths is because our texture is over-sized (must be power of two)\n\tcs = vs = (float)vid.pixelwidth / vwidth * 0.5;\n\tct = vt = (float)vid.pixelheight / vheight * 0.5;\n\tvs *= gl_motionblurscale.value;\n\tvt *= gl_motionblurscale.value;\n\n\t//render using our texture\n\tshader = R_RegisterShader(\"postproc_motionblur\", SUF_NONE,\n\t\t\"{\\n\"\n\t\t\t\"program default2d\\n\"\n\t\t\t\"{\\n\"\n\t\t\t\t\"map $sourcecolour\\n\"\n\t\t\t\t\"blendfunc blend\\n\"\n\t\t\t\"}\\n\"\n\t\t\"}\\n\"\n\t\t);\n\tGLBE_FBO_Sources(sceneblur_texture, r_nulltex);\n\tR2D_ImageColours(1, 1, 1, gl_motionblur.value);\n\tR2D_Image(0, 0, vid.width, vid.height, cs-vs, ct+vt, cs+vs, ct-vt, shader);\n\tGLBE_RenderToTextureUpdate2d(false);\n\n\t//grab the current image so we can feed that back into the next frame.\n\tGL_MTBind(0, GL_TEXTURE_2D, sceneblur_texture);\n\t//copy the image into the texture so that we can play with it next frame too!\n\tqglCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 0, 0, vwidth, vheight, 0);\n\tqglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);\n\tqglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);\n#endif\n}\n\n#if 1\n#include \"shader.h\"\n/*FIXME: we could use geometry shaders to draw to all 6 faces at once*/\nqboolean R_RenderScene_Cubemap(void)\n{\n\tint cmapsize = 512;\n\tint i;\n\tstatic vec3_t ang[6] =\n\t\t\t\t{\t{0, -90, 0}, {0, 90, 0},\n\t\t\t\t\t{90, 0, 0}, {-90, 0, 0},\n\t\t\t\t\t{0, 0, 0}, {0, -180, 0}\t};\n\tvec3_t saveang;\n\tvec3_t saveorg;\n\n\tvrect_t vrect;\n\tpxrect_t prect;\n\n\tshader_t *shader;\n\tint facemask;\n\textern cvar_t r_projection;\n\tint oldfbo = -1;\n\tqboolean usefbo = true;\t\t//this appears to be a 20% speedup in my tests.\n\tqboolean fboreset = false;\n\tint osm = r_refdef.stereomethod;\n\n\t/*needs glsl*/\n\tif (!gl_config.arb_shader_objects)\n\t\treturn false;\n\n\tif (!*ffov.string || !strcmp(ffov.string, \"0\"))\n\t\tffov.value = scr_fov.value;\n\n\tfacemask = 0;\n\tswitch(r_projection.ival)\n\t{\n\tdefault:\t//invalid.\n\t\treturn false;\n\tcase PROJ_STEREOGRAPHIC:\n\t\tshader = R_RegisterShader(\"postproc_stereographic\", SUF_NONE,\n\t\t\t\t\"{\\n\"\n\t\t\t\t\t\"program postproc_stereographic\\n\"\n\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\"map $sourcecube\\n\"\n\t\t\t\t\t\"}\\n\"\n\t\t\t\t\"}\\n\"\n\t\t\t\t);\n\n\t\tfacemask |= 1<<4; /*front view*/\n\t\tif (ffov.value > 70)\n\t\t{\n\t\t\tfacemask |= (1<<0) | (1<<1); /*side/top*/\n\t\t\tif (ffov.value > 85)\n\t\t\t\tfacemask |= (1<<2) | (1<<3); /*bottom views*/\n\t\t\tif (ffov.value > 300)\n\t\t\t\tfacemask |= 1<<5; /*back view*/\n\t\t}\n\t\tbreak;\n\tcase PROJ_FISHEYE:\n\t\tshader = R_RegisterShader(\"postproc_fisheye\", SUF_NONE,\n\t\t\t\t\"{\\n\"\n\t\t\t\t\t\"program postproc_fisheye\\n\"\n\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\"map $sourcecube\\n\"\n\t\t\t\t\t\"}\\n\"\n\t\t\t\t\"}\\n\"\n\t\t\t\t);\n\n\t\t//fisheye view sees up to a full sphere\n\t\tfacemask |= 1<<4; /*front view*/\n\t\tif (ffov.value > 77)\n\t\t\tfacemask |= (1<<0) | (1<<1) | (1<<2) | (1<<3); /*side/top/bottom views*/\n\t\tif (ffov.value > 270)\n\t\t\tfacemask |= 1<<5; /*back view*/\n\t\tbreak;\n\tcase PROJ_PANORAMA:\n\t\tshader = R_RegisterShader(\"postproc_panorama\", SUF_NONE,\n\t\t\t\t\"{\\n\"\n\t\t\t\t\t\"program postproc_panorama\\n\"\n\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\"map $sourcecube\\n\"\n\t\t\t\t\t\"}\\n\"\n\t\t\t\t\"}\\n\"\n\t\t\t\t);\n\n\t\t//panoramic view needs at most the four sides\n\t\tfacemask |= 1<<4; /*front view*/\n\t\tif (ffov.value > 90)\n\t\t{\n\t\t\tfacemask |= (1<<0) | (1<<1); /*side views*/\n\t\t\tif (ffov.value > 270)\n\t\t\t\tfacemask |= 1<<5; /*back view*/\n\t\t}\n\t\tfacemask = 0x3f;\n\t\tbreak;\n\tcase PROJ_LAEA:\n\t\tshader = R_RegisterShader(\"postproc_laea\", SUF_NONE,\n\t\t\t\t\"{\\n\"\n\t\t\t\t\t\"program postproc_laea\\n\"\n\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\"map $sourcecube\\n\"\n\t\t\t\t\t\"}\\n\"\n\t\t\t\t\"}\\n\"\n\t\t\t\t);\n\n\t\tfacemask |= 1<<4; /*front view*/\n\t\tif (ffov.value > 90)\n\t\t{\n\t\t\tfacemask |= (1<<0) | (1<<1) | (1<<2) | (1<<3); /*side/top/bottom views*/\n\t\t\tif (ffov.value > 270)\n\t\t\t\tfacemask |= 1<<5; /*back view*/\n\t\t}\n\t\tbreak;\n\n\tcase PROJ_EQUIRECTANGULAR:\n\t\tshader = R_RegisterShader(\"postproc_equirectangular\", SUF_NONE,\n\t\t\t\t\"{\\n\"\n\t\t\t\t\t\"program postproc_equirectangular\\n\"\n\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\"map $sourcecube\\n\"\n\t\t\t\t\t\"}\\n\"\n\t\t\t\t\"}\\n\"\n\t\t\t\t);\n\n\t\tfacemask = 0x3f;\n#if 0\n\t\tfacemask |= 1<<4; /*front view*/\n\t\tif (ffov.value > 90)\n\t\t{\n\t\t\tfacemask |= (1<<0) | (1<<1) | (1<<2) | (1<<3); /*side/top/bottom views*/\n\t\t\tif (ffov.value > 270)\n\t\t\t\tfacemask |= 1<<5; /*back view*/\n\t\t}\n#endif\n\t\tbreak;\n\tcase PROJ_PANINI:\n\t\tshader = R_RegisterShader(\"postproc_panini\", SUF_NONE,\n\t\t\t\t\"{\\n\"\n\t\t\t\t\t\"program postproc_panini\\n\"\n\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\"map $sourcecube\\n\"\n\t\t\t\t\t\"}\\n\"\n\t\t\t\t\"}\\n\"\n\t\t\t\t);\n\n\t\tfacemask |= 1<<4; /*front view*/\n\t\tif (ffov.value > 70)\n\t\t{\n\t\t\tfacemask |= (1<<0) | (1<<1); /*side/top*/\n\t\t\tif (ffov.value > 85)\n\t\t\t\tfacemask |= (1<<2) | (1<<3); /*bottom views*/\n\t\t\tif (ffov.value > 300)\n\t\t\t\tfacemask |= 1<<5; /*back view*/\n\t\t}\n\t\tbreak;\n\t}\n\n\t//FIXME: we should be able to rotate the view\n\n\tvrect = r_refdef.vrect;\n\tprect = r_refdef.pxrect;\n//\tprect.x = (vrect.x * vid.pixelwidth)/vid.width;\n//\tprect.width = (vrect.width * vid.pixelwidth)/vid.width;\n//\tprect.y = (vrect.y * vid.pixelheight)/vid.height;\n//\tprect.height = (vrect.height * vid.pixelheight)/vid.height;\n\n\tif (sh_config.texture_non_power_of_two_pic)\n\t{\n\t\tif (usefbo)\n\t\t{\n\t\t\tcmapsize = prect.width > prect.height?prect.width:prect.height;\n\t\t\tif (cmapsize > 4096)//sh_config.texture_maxsize)\n\t\t\t\tcmapsize = 4096;//sh_config.texture_maxsize;\n\t\t}\n\t\telse\n\t\t\tcmapsize = prect.width < prect.height?prect.width:prect.height;\n\t}\n\telse if (!usefbo)\n\t{\n\t\twhile (cmapsize > prect.width || cmapsize > prect.height)\n\t\t{\n\t\t\tcmapsize /= 2;\n\t\t}\n\t}\n\n\tif (usefbo)\n\t{\n\t\tr_refdef.flags |= RDF_FISHEYE;\n\t\tvid.fbpwidth = vid.fbpheight = cmapsize;\n\t}\n\n\t//FIXME: gl_max_size\n\n\tVectorCopy(r_refdef.vieworg, saveorg);\n\tVectorCopy(r_refdef.viewangles, saveang);\n\tsaveang[2] = 0;\n\n\tr_refdef.stereomethod = STEREO_OFF;\n\n\tif (!TEXVALID(scenepp_postproc_cube) || cmapsize != scenepp_postproc_cube_size)\n\t{\n\t\tif (!TEXVALID(scenepp_postproc_cube))\n\t\t{\n\t\t\tscenepp_postproc_cube = Image_CreateTexture(\"***fish***\", NULL, IF_TEXTYPE_CUBE|IF_RENDERTARGET|IF_CLAMP|IF_LINEAR);\n\t\t\tqglGenTextures(1, &scenepp_postproc_cube->num);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tqglDeleteTextures(1, &scenepp_postproc_cube->num);\n\t\t\tscenepp_postproc_cube->num = 0;\n\t\t\tGL_MTBind(0, GL_TEXTURE_CUBE_MAP_ARB, scenepp_postproc_cube);\n\t\t\tqglGenTextures(1, &scenepp_postproc_cube->num);\n\t\t}\n\n\t\tGL_MTBind(0, GL_TEXTURE_CUBE_MAP_ARB, scenepp_postproc_cube);\n\t\tfor (i = 0; i < 6; i++)\n\t\t\tqglCopyTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB + i, 0, GL_RGB, 0, 0, cmapsize, cmapsize, 0);\n\t\tqglTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);\n\t\tqglTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);\n\t\tqglTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_MAG_FILTER, GL_LINEAR);\n\t\tqglTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_MIN_FILTER, GL_LINEAR);\n\n\t\tscenepp_postproc_cube_size = cmapsize;\n\n\t\tfboreset = true;\n\t}\n\n\tvrect = r_refdef.vrect;\t//save off the old vrect\n\n\tr_refdef.vrect.width = (cmapsize * vid.fbvwidth) / vid.fbpwidth;\n\tr_refdef.vrect.height = (cmapsize * vid.fbvheight) / vid.fbpheight;\n\tr_refdef.vrect.x = 0;\n\tr_refdef.vrect.y = prect.y;\n\n\tang[0][0] = -saveang[0];\n\tang[0][1] = -90;\n\tang[0][2] = -saveang[0];\n\n\tang[1][0] = -saveang[0];\n\tang[1][1] = 90;\n\tang[1][2] = saveang[0];\n\tang[5][0] = -saveang[0]*2;\n\n\t//in theory, we could use a geometry shader to duplicate the polygons to each face.\n\t//that would of course require that every bit of glsl had such a geometry shader.\n\t//it would at least reduce cpu load quite a bit.\n\tfor (i = 0; i < 6; i++)\n\t{\n\t\tif (!(facemask & (1<<i)))\n\t\t\tcontinue;\n\n\t\tif (usefbo)\n\t\t{\n\t\t\tint r = GLBE_FBO_Update(&fbo_postproc_cube, FBO_RB_DEPTH|(fboreset?FBO_RESET:0), &scenepp_postproc_cube, 1, r_nulltex,  cmapsize, cmapsize, i);\n\t\t\tfboreset = false;\n\t\t\tif (oldfbo < 0)\n\t\t\t\toldfbo = r;\n\t\t}\n\n\t\tr_refdef.fov_x = 90;\n\t\tr_refdef.fov_y = 90;\n\t\tr_refdef.viewangles[0] = saveang[0]+ang[i][0];\n\t\tr_refdef.viewangles[1] = saveang[1]+ang[i][1];\n\t\tr_refdef.viewangles[2] = saveang[2]+ang[i][2];\n\n\t\tR_Clear (usefbo);\n\n\t\tGL_SetShaderState2D(false);\n\n\t\t// render normal view\n\t\tR_RenderScene ();\n\n\t\tif (!usefbo)\n\t\t{\n\t\t\tGL_MTBind(0, GL_TEXTURE_CUBE_MAP_ARB, scenepp_postproc_cube);\n\t\t\tqglCopyTexSubImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB + i, 0, 0, 0, 0, vid.fbpheight - (prect.y + cmapsize), cmapsize, cmapsize);\n\t\t}\n\t}\n\n\tif (usefbo)\n\t\tGLBE_FBO_Pop(oldfbo);\n\n\tr_refdef.vrect = vrect;\n\tr_refdef.pxrect = prect;\n\tVectorCopy(saveorg, r_refdef.vieworg);\n\tr_refdef.stereomethod = osm;\n\n\t//GL_ViewportUpdate();\n\tGL_Set2D(false);\n\t// go 2d\n/*\tqglMatrixMode(GL_PROJECTION);\n\tqglPushMatrix();\n\tqglLoadIdentity ();\n\tqglOrtho  (0, vid.width, vid.height, 0, -99999, 99999);\n\tqglMatrixMode(GL_MODELVIEW);\n\tqglPushMatrix();\n\tqglLoadIdentity ();\n*/\n\t// draw it through the shader\n\tif (r_projection.ival == PROJ_EQUIRECTANGULAR)\n\t{\n\t\t//note vr screenshots have requirements here\n\t\tR2D_Image(vrect.x, vrect.y, vrect.width, vrect.height, 0, 1, 1, 0, shader);\n\t}\n\telse if (r_projection.ival == PROJ_PANORAMA)\n\t{\n\t\tfloat saspect = .5;\n\t\tfloat taspect = vrect.height / vrect.width * ffov.value / 90;//(0.5 * vrect.width) / vrect.height;\n\t\tR2D_Image(vrect.x, vrect.y, vrect.width, vrect.height, -saspect, taspect, saspect, -taspect, shader);\n\t}\n\telse if (vrect.width > vrect.height)\n\t{\n\t\tfloat aspect = (0.5 * vrect.height) / vrect.width;\n\t\tR2D_Image(vrect.x, vrect.y, vrect.width, vrect.height, -0.5, aspect, 0.5, -aspect, shader);\n\t}\n\telse\n\t{\n\t\tfloat aspect = (0.5 * vrect.width) / vrect.height;\n\t\tR2D_Image(vrect.x, vrect.y, vrect.width, vrect.height, -aspect, 0.5, aspect, -0.5, shader);\n\t}\n\n\tif (R2D_Flush)\n\t\tR2D_Flush();\n\n\t//revert the matricies\n/*\tqglMatrixMode(GL_PROJECTION);\n\tqglPopMatrix();\n\tqglMatrixMode(GL_MODELVIEW);\n\tqglPopMatrix();\n*/\n\treturn true;\n}\n#endif\n\ntexid_t R_RenderPostProcess (texid_t sourcetex, texid_t sourcedepth, int type, shader_t *shader, char *restexname)\n{\n\tif (r_refdef.flags & type)\n\t{\n\t\tr_refdef.flags &= ~type;\n\n\t\tif (r_refdef.flags & RDF_ALLPOSTPROC)\n\t\t{\t//there's other post-processing passes that still need to be applied.\n\t\t\t//thus we need to write this output to a texture.\n\t\t\tint w = (r_refdef.vrect.width * vid.pixelwidth) / vid.width;\n\t\t\tint h = (r_refdef.vrect.height * vid.pixelheight) / vid.height;\n\t\t\tif (R2D_Flush)\n\t\t\t\tR2D_Flush();\n\t\t\tGLBE_FBO_Sources(sourcetex, sourcedepth);\n\t\t\tsourcetex = R2D_RT_Configure(restexname, w, h, TF_RGBA32, RT_IMAGEFLAGS);\n\t\t\tGLBE_FBO_Update(&fbo_postproc, 0, &sourcetex, 1, r_nulltex, w, h, 0);\n\t\t\tqglViewport(0,0,w,h);\n\t\t\tR2D_ScalePic(0, 0, vid.fbvwidth, vid.fbvheight, shader);\n\t\t\tif (R2D_Flush)\n\t\t\t\tR2D_Flush();\n\t\t\tGLBE_RenderToTextureUpdate2d(true);\n\t\t}\n\t\telse\n\t\t{\t//yay, dump it to the screen\n\t\t\t//update stuff now that we're not rendering the 3d scene\n\t\t\tif (R2D_Flush)\n\t\t\t\tR2D_Flush();\n\t\t\tGLBE_FBO_Sources(sourcetex, sourcedepth);\n\t\t\tR2D_ScalePic(r_refdef.vrect.x, r_refdef.vrect.y, r_refdef.vrect.width, r_refdef.vrect.height, shader);\n\t\t\tif (R2D_Flush)\n\t\t\t\tR2D_Flush();\n\t\t}\n\t}\n\n\treturn sourcetex;\n}\n\n/*\n================\nR_RenderView\n\nr_refdef must be set before the first call\n================\n*/\nvoid GLR_RenderView (void)\n{\n\tint dofbo = *r_refdef.rt_destcolour[0].texname || *r_refdef.rt_depth.texname;\n\tdouble\ttime1 = 0, time2;\n\ttexid_t sourcetex = r_nulltex;\n\ttexid_t sourcedepth = r_nulltex;\n\tshader_t *custompostproc = NULL;\n\tfloat renderscale;\t//extreme, but whatever\n\tint oldfbo = 0;\n\tqboolean forcedfb = false;\n\tqboolean fbdepth = false;\n\n\tcheckglerror();\n\n\tif (r_norefresh.value || !vid.fbpwidth || !vid.fbpwidth)\n\t\treturn;\n\n\t//when loading/bugged, its possible that the world is still loading.\n\t//in this case, don't act as a wallhack (unless the world is meant to be hidden anyway)\n\tif (!(r_refdef.flags & RDF_NOWORLDMODEL))\n\t{\n\t\t//FIXME: fbo stuff\n\t\tif (!r_worldentity.model || !cl.worldmodel)\n\t\t\tr_refdef.flags |= RDF_NOWORLDMODEL;\n\t\telse if (r_worldentity.model->loadstate != MLS_LOADED || !cl.worldmodel)\n\t\t{\n\t\t\tGL_Set2D (false);\n\t\t\tR2D_ImageColours(0, 0, 0, 1);\n\t\t\tR2D_FillBlock(r_refdef.vrect.x, r_refdef.vrect.y, r_refdef.vrect.width, r_refdef.vrect.height);\n\t\t\tR2D_ImageColours(1, 1, 1, 1);\n\t\t\treturn;\n\t\t}\n//\t\tSys_Error (\"R_RenderView: NULL worldmodel\");\n\t}\n\n\t//check if we're underwater (this also limits damage from stereo wallhacks).\n\tSurf_SetupFrame();\n\tr_refdef.flags &= ~(RDF_ALLPOSTPROC|RDF_RENDERSCALE);\n\n\tif (dofbo || (r_refdef.flags & RDF_NOWORLDMODEL))\n\t\trenderscale = 1;\n\telse\n\t{\n\t\trenderscale = r_renderscale.value;\n\t\tif (R_CanBloom())\n\t\t\tr_refdef.flags |= RDF_BLOOM;\n\t}\n\n\t//check if we can do underwater warp\n\tif (cls.protocol != CP_QUAKE2)\t//quake2 tells us directly\n\t{\n\t\tif (r_viewcontents & FTECONTENTS_FLUID)\n\t\t\tr_refdef.flags |= RDF_UNDERWATER;\n\t\telse\n\t\t\tr_refdef.flags &= ~RDF_UNDERWATER;\n\t}\n\tif (r_refdef.flags & RDF_UNDERWATER)\n\t{\n\t\textern cvar_t r_projection;\n\t\tif (!r_waterwarp.value || r_projection.ival)\n\t\t\tr_refdef.flags &= ~RDF_UNDERWATER;\t//no warp at all\n\t\telse if (r_waterwarp.value > 0 && scenepp_waterwarp)\n\t\t\tr_refdef.flags |= RDF_WATERWARP;\t//try fullscreen warp instead if we can\n\t}\n\n\tif (!r_refdef.globalfog.density)\n\t{\n\t\textern cvar_t r_fog_linear;\n\n\t\tint fogtype = ((r_refdef.flags & RDF_UNDERWATER) && cl.fog[FOGTYPE_WATER].density)?FOGTYPE_WATER:FOGTYPE_AIR;\n\t\tCL_BlendFog(&r_refdef.globalfog, &cl.oldfog[fogtype], realtime, &cl.fog[fogtype]);\n\n\t\tif (!r_fog_linear.ival)\n\t\t\tr_refdef.globalfog.density /= 64;\t//FIXME\n\t}\n\n\tif (!(r_refdef.flags & RDF_NOWORLDMODEL))\n\t{\n\t\tif (r_fxaa.ival)\n\t\t\tr_refdef.flags |= RDF_ANTIALIAS;\n\t\tif (*r_postprocshader.string)\n\t\t\tcustompostproc = R_RegisterCustom(NULL, r_postprocshader.string, SUF_NONE, NULL, NULL);\n\t\telse if (!r_graphics.ival)\n\t\t\tcustompostproc = R_RegisterShader(\"postproc_ascii\", 0, \n\t\t\t\t\"{\\n\"\n\t\t\t\t\t\"program postproc_ascii\\n\"\n\t\t\t\t\t\"affine\\n\"\n\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\"map $sourcecolour\\n\"\n\t\t\t\t\t\t\"nodepthtest\\n\"\n\t\t\t\t\t\"}\\n\"\n\t\t\t\t\"}\\n\"\n\t\t\t\t);\n\t\tif (custompostproc)\n\t\t\tr_refdef.flags |= RDF_CUSTOMPOSTPROC;\n\n\t\tif (r_hdr_framebuffer.ival && !(vid.flags & VID_FP16))\t//primary use of this cvar is to fix q3shader overbrights (so bright lightmaps can oversaturate then drop below 1 by modulation with the lightmap\n\t\t\tforcedfb = true;\n\t\tif (custompostproc)\n\t\t{\n\t\t\tint i;\n\t\t\tfor (i = 0; i < custompostproc->numpasses; i++)\n\t\t\t\tif (custompostproc->passes[i].texgen == T_GEN_SOURCEDEPTH)\n\t\t\t\t{\n\t\t\t\t\tfbdepth = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t}\n\t\tif (vid_hardwaregamma.ival == 4 && (v_gamma.value != 1 || v_contrast.value != 1 || v_contrastboost.value != 1|| v_brightness.value != 0))\n\t\t\tr_refdef.flags |= RDF_SCENEGAMMA;\n\t}\n\n\t//disable stuff if its simply not supported.\n\tif (dofbo || !gl_config.arb_shader_objects || !gl_config.ext_framebuffer_objects || !sh_config.texture_non_power_of_two_pic)\n\t{\n\t\tforcedfb &= !dofbo && gl_config.ext_framebuffer_objects && sh_config.texture_non_power_of_two_pic;\n\t\tr_refdef.flags &= ~(RDF_ALLPOSTPROC);\t//block all of this stuff\n\t}\n\tif (dofbo)\n\t\tforcedfb = false;\n\telse if (renderscale != 1)\n\t\tforcedfb = gl_config.ext_framebuffer_objects && sh_config.texture_non_power_of_two_pic;\n\n\tBE_Scissor(NULL);\n\tif (dofbo)\n\t{\n\t\tunsigned int flags = 0;\n\t\ttexid_t col[R_MAX_RENDERTARGETS], depth = r_nulltex;\n\t\tunsigned int cw=0, ch=0, dw=0, dh=0;\n\t\tint mrt;\n\n\t\tif (!gl_config.ext_framebuffer_objects && sh_config.texture_non_power_of_two_pic)\n\t\t{\n\t\t\tCon_DPrintf(CON_WARNING\"Render targets are not supported on this gpu.\\n\");\n\t\t\treturn;\t//not supported on this gpu. you'll just get black textures or something.\n\t\t}\n\n\t\t//3d views generally ignore source colour+depth.\n\t\t//FIXME: support depth with no colour\n\t\tfor (mrt = 0; mrt < R_MAX_RENDERTARGETS; mrt++)\n\t\t{\n\t\t\tif (*r_refdef.rt_destcolour[mrt].texname)\n\t\t\t{\n\t\t\t\tcol[mrt] = R2D_RT_GetTexture(r_refdef.rt_destcolour[mrt].texname, &cw, &ch);\n\t\t\t\tif (!TEXVALID(col[mrt]))\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tcol[mrt] = r_nulltex;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (*r_refdef.rt_depth.texname)\n\t\t\tdepth = R2D_RT_GetTexture(r_refdef.rt_depth.texname, &dw, &dh);\n\n\t\tif (mrt)\n\t\t{ \t//colour (with or without depth)\n\t\t\tif (*r_refdef.rt_depth.texname && (dw != cw || dh != ch))\n\t\t\t{\n\t\t\t\tCon_Printf(\"RT: destcolour and depth render targets are of different sizes\\n\");\t//should check rgb/depth modes too I guess.\n\t\t\t\tdepth = r_nulltex;\n\t\t\t}\n\t\t\tvid.fbvwidth = vid.fbpwidth = cw;\n\t\t\tvid.fbvheight = vid.fbpheight = ch;\n\t\t}\n\t\telse\n\t\t{\t//depth, with no colour\n\t\t\tvid.fbvwidth = vid.fbpwidth = dw;\n\t\t\tvid.fbvheight = vid.fbpheight = dh;\n\t\t}\n\t\tif (TEXVALID(depth))\n\t\t\tflags |= FBO_TEX_DEPTH;\n\t\telse\n\t\t\tflags |= FBO_RB_DEPTH;\n\t\toldfbo = GLBE_FBO_Update(&fbo_gameview, flags, col, mrt, depth, vid.fbpwidth, vid.fbpheight, 0);\n\t}\n\telse if ((r_refdef.flags & (RDF_ALLPOSTPROC)) || forcedfb)\n\t{\n\t\tunsigned int rtflags = IF_NOMIPMAP|IF_CLAMP|IF_RENDERTARGET|IF_NOSRGB;\n\t\tenum uploadfmt fmt;\n\t\tunsigned int fboflags = 0;\n\n\t\tr_refdef.flags |= RDF_RENDERSCALE;\n\n\t\t//the game needs to be drawn to a texture for post processing\n\t\tif (1)//vid.framebuffer)\n\t\t{\n\t\t\tvid.fbpwidth = (r_refdef.vrect.width * r_refdef.pxrect.width) / vid.width;\n\t\t\tvid.fbpheight = (r_refdef.vrect.height * r_refdef.pxrect.height) / vid.height;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tvid.fbpwidth = (r_refdef.vrect.width * vid.pixelwidth) / vid.width;\n\t\t\tvid.fbpheight = (r_refdef.vrect.height * vid.pixelheight) / vid.height;\n\t\t}\n\n\t\tif (renderscale < 0)\n\t\t{\n\t\t\trenderscale = -renderscale;\n\t\t\trtflags |= IF_NEAREST;\n\t\t\tvid.fbpwidth *= renderscale;\n\t\t\tvid.fbpheight *= renderscale;\n\t\t}\n\t\telse\n\t\t{\n\t\t\trtflags |= IF_LINEAR;\n\t\t\tvid.fbpwidth *= renderscale;\n\t\t\tvid.fbpheight *= renderscale;\n\t\t}\n\n\t\t//well... err... meh.\n\t\tvid.fbpwidth = bound(1, vid.fbpwidth, sh_config.texture2d_maxsize);\n\t\tvid.fbpheight = bound(1, vid.fbpheight, sh_config.texture2d_maxsize);\n\n\t\tvid.fbvwidth = vid.fbpwidth;\n\t\tvid.fbvheight = vid.fbpheight;\n\n\t\tfmt = PTI_RGBA8;\n\t\tif (r_hdr_framebuffer.ival < 0)\n\t\t{\t//cvar change handler will set ival negative if it matches a known format name, doesn't mean its supported.\n\t\t\tfmt = -r_hdr_framebuffer.ival;\n\t\t\tif (fmt >= PTI_FIRSTCOMPRESSED || !sh_config.texfmt[fmt])\n\t\t\t\tfmt = PTI_RGB565;\n\t\t}\n\t\telse if ((r_refdef.flags&RDF_SCENEGAMMA)||(vid.flags&(VID_SRGBAWARE|VID_FP16))||r_hdr_framebuffer.ival)\n\t\t{\t//gamma ramps really need higher colour precision, otherwise the entire thing looks terrible.\n\t\t\tif (sh_config.texfmt[PTI_B10G11R11F])\n\t\t\t\tfmt = PTI_B10G11R11F;\n\t\t\telse if (sh_config.texfmt[PTI_RGBA16F])\n\t\t\t\tfmt = PTI_RGBA16F;\n\t\t\telse if (sh_config.texfmt[PTI_A2BGR10])\n\t\t\t\tfmt = PTI_A2BGR10;\n\t\t}\n\n\t\tsourcetex = R2D_RT_Configure(\"rt/$lastgameview\", vid.fbpwidth, vid.fbpheight, fmt, rtflags);\n\t\tif (fbdepth)\n\t\t{\n\t\t\tif (sh_config.texfmt[PTI_DEPTH24_8] && !r_shadow_shadowmapping.ival)\n\t\t\t\tfmt = PTI_DEPTH24_8;\n\t\t\telse if (sh_config.texfmt[PTI_DEPTH32])\n\t\t\t\tfmt = PTI_DEPTH32;\n\t\t\telse\n\t\t\t\tfmt = PTI_DEPTH16;\n\t\t}\n\t\telse\n\t\t\tfmt = PTI_INVALID;\n\t\tsourcedepth = (fmt != PTI_INVALID)?R2D_RT_Configure(\"rt/$lastgameviewdepth\", vid.fbpwidth, vid.fbpheight, fmt, rtflags):r_nulltex;\n\t\tif (sourcedepth)\n\t\t\tfboflags = FBO_TEX_DEPTH;\n\t\telse\n\t\t\tfboflags = FBO_RB_DEPTH;\n\n\t\toldfbo = GLBE_FBO_Update(&fbo_gameview, fboflags, &sourcetex, 1, sourcedepth, vid.fbpwidth, vid.fbpheight, 0);\n\t\tdofbo = true;\n\t}\n\telse if (vid.framebuffer)\n\t{\n\t\tvid.fbvwidth = vid.width;\n\t\tvid.fbvheight = vid.height;\n\t\tvid.fbpwidth = vid.framebuffer->width;\n\t\tvid.fbpheight = vid.framebuffer->height;\n\t}\n\telse\n\t{\n\t\tvid.fbvwidth = vid.width;\n\t\tvid.fbvheight = vid.height;\n\t\tvid.fbpwidth = vid.pixelwidth;\n\t\tvid.fbpheight = vid.pixelheight;\n\t}\n\tr_refdef.flipcull = 0;\n\n\tif (qglPNTrianglesiATI)\n\t{\n\t\tif (gl_ati_truform_type.ival)\n\t\t{\t//linear\n\t\t\tqglPNTrianglesiATI(GL_PN_TRIANGLES_NORMAL_MODE_ATI, GL_PN_TRIANGLES_NORMAL_MODE_LINEAR_ATI);\n\t\t\tqglPNTrianglesiATI(GL_PN_TRIANGLES_POINT_MODE_ATI, GL_PN_TRIANGLES_POINT_MODE_CUBIC_ATI);\n\t\t}\n\t\telse\n\t\t{\t//quadric\n\t\t\tqglPNTrianglesiATI(GL_PN_TRIANGLES_NORMAL_MODE_ATI, GL_PN_TRIANGLES_NORMAL_MODE_QUADRATIC_ATI);\n\t\t\tqglPNTrianglesiATI(GL_PN_TRIANGLES_POINT_MODE_ATI, GL_PN_TRIANGLES_POINT_MODE_CUBIC_ATI);\n\t\t}\n\t\tqglPNTrianglesfATI(GL_PN_TRIANGLES_TESSELATION_LEVEL_ATI, r_tessellation_level.value);\n\t}\n\n\tif (gl_finish.ival)\n\t{\n\t\tRSpeedMark();\n\t\tqglFinish ();\n\t\tRSpeedEnd(RSPEED_SUBMIT);\n\t}\n\n\tif (r_speeds.ival)\n\t{\n\t\ttime1 = Sys_DoubleTime ();\n\t}\n\n\tif (!(r_refdef.flags & RDF_NOWORLDMODEL) && R_RenderScene_Cubemap())\n\t{\n\n\t}\n\telse\n\t{\n\t\tGL_SetShaderState2D(false);\n\n\t\tR_Clear (dofbo);\n\n\t//\tGLR_SetupFog ();\n\n\t\t// render normal view\n\t\tR_RenderScene ();\n\t}\n\n//\tqglDisable(GL_FOG);\n\n\tif (r_speeds.ival)\n\t{\n//\t\tglFinish ();\n\t\ttime2 = Sys_DoubleTime ();\n\n\t\tRQuantAdd(RQUANT_MSECS, (int)((time2-time1)*1000000));\n\n\t//\tCon_Printf (\"%3i ms  %4i wpoly %4i epoly\\n\", (int)((time2-time1)*1000), c_brush_polys, c_alias_polys);\n\t}\n\n\tcheckglerror();\n\n\t//update stuff now that we're not rendering the 3d scene\n\tif (dofbo)\n\t\tGLBE_FBO_Pop(oldfbo);\n\n\tGLBE_RenderToTextureUpdate2d(false);\n\tGL_Set2D (2);\n\n\t// SCENE POST PROCESSING\n\n\tif (forcedfb && !(r_refdef.flags & RDF_ALLPOSTPROC))\n\t{\n\t\tGLBE_FBO_Sources(sourcetex, sourcedepth);\n\t\tR2D_Image(r_refdef.vrect.x, r_refdef.vrect.y, r_refdef.vrect.width, r_refdef.vrect.height, 0, 1, 1, 0, scenepp_rescaled);\n\t}\n\telse\n\t{\n\t\tif (r_refdef.flags & RDF_SCENEGAMMA)\n\t\t{\n\t\t\tR2D_ImageColours (v_gammainverted.ival?v_gamma.value:(1/v_gamma.value), v_contrast.value, v_brightness.value, v_contrastboost.value);\n\t\t\tsourcetex = R_RenderPostProcess (sourcetex, sourcedepth, RDF_SCENEGAMMA, scenepp_gamma, \"rt/$gammaed\");\n\t\t\tR2D_ImageColours (1, 1, 1, 1);\n\t\t}\n\t\tsourcetex = R_RenderPostProcess (sourcetex, sourcedepth, RDF_WATERWARP, scenepp_waterwarp, \"rt/$waterwarped\");\n\t\tsourcetex = R_RenderPostProcess (sourcetex, sourcedepth, RDF_CUSTOMPOSTPROC, custompostproc, \"rt/$postproced\");\n\t\tsourcetex = R_RenderPostProcess (sourcetex, sourcedepth, RDF_ANTIALIAS, scenepp_antialias, \"rt/$antialiased\");\n\t\tif (r_refdef.flags & RDF_BLOOM)\n\t\t\tR_BloomBlend(sourcetex, r_refdef.vrect.x, r_refdef.vrect.y, r_refdef.vrect.width, r_refdef.vrect.height);\n\t}\n\n\tif (R2D_Flush)\n\t\tR2D_Flush();\n\n\tGLBE_FBO_Sources(r_nulltex, r_nulltex);\n\n\tif (gl_motionblur.value>0 && gl_motionblur.value < 1 && qglCopyTexImage2D)\n\t\tR_RenderMotionBlur();\n\n\tif (gl_screenangle.value)\n\t\tGL_Set2D (false);\t//make sure any hud stuff is rotated properly.\n\n\tcheckglerror();\n}\n\n#endif\n"
  },
  {
    "path": "engine/gl/gl_rmisc.c",
    "content": "/*\nCopyright (C) 1996-1997 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  \n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n*/\n// r_misc.c\n\n#include \"quakedef.h\"\n#ifdef GLQUAKE\n#include \"glquake.h\"\n#include \"gl_draw.h\"\n\n/*\n==================\nR_InitTextures\n==================\n*\nvoid\tGLR_InitTextures (void)\n{\n\tint\t\tx,y, m;\n\tqbyte\t*dest;\n\n// create a simple checkerboard texture for the default\n\tr_notexture_mip = Hunk_AllocName (sizeof(texture_t) + 16*16+8*8+4*4+2*2, \"notexture\");\n\t\n\tr_notexture_mip->width = r_notexture_mip->height = 16;\n\tr_notexture_mip->offsets[0] = sizeof(texture_t);\n\tr_notexture_mip->offsets[1] = r_notexture_mip->offsets[0] + 16*16;\n\tr_notexture_mip->offsets[2] = r_notexture_mip->offsets[1] + 8*8;\n\tr_notexture_mip->offsets[3] = r_notexture_mip->offsets[2] + 4*4;\n\t\n\tfor (m=0 ; m<4 ; m++)\n\t{\n\t\tdest = (qbyte *)r_notexture_mip + r_notexture_mip->offsets[m];\n\t\tfor (y=0 ; y< (16>>m) ; y++)\n\t\t\tfor (x=0 ; x< (16>>m) ; x++)\n\t\t\t{\n\t\t\t\tif (  (y< (8>>m) ) ^ (x< (8>>m) ) )\n\t\t\t\t\t*dest++ = 0;\n\t\t\t\telse\n\t\t\t\t\t*dest++ = 0xff;\n\t\t\t}\n\t}\t\n}*/\n\n\n\n\n\n\n\n\n\n\n#ifdef RTLIGHTS\ntexid_t GenerateNormalisationCubeMap(void)\n{\n\ttexid_t normalisationCubeMap;\n\tunsigned char data[32*32*3];\n\n\t//some useful variables\n\tint size=32;\n\tfloat offset=0.5f;\n\tfloat halfSize=16.0f;\n\tvec3_t tempVector;\n\tunsigned char * bytePtr;\n\n\tint i, j;\n\t\n\tnormalisationCubeMap = Image_CreateTexture(\"normalisationcubemap\", NULL, IF_TEXTYPE_CUBE);\n\tqglGenTextures(1, &normalisationCubeMap->num);\n\tGL_MTBind(0, GL_TEXTURE_CUBE_MAP_ARB, normalisationCubeMap);\n\n\t//positive x\n\tbytePtr=data;\n\n\tfor(j=0; j<size; j++)\n\t{\n\t\tfor(i=0; i<size; i++)\n\t\t{\n\t\t\ttempVector[0] = halfSize;\t\t\t\n\t\t\ttempVector[1] = -(j+offset-halfSize);\n\t\t\ttempVector[2] = -(i+offset-halfSize);\n\n\t\t\tVectorNormalize(tempVector);\n\n\t\t\tbytePtr[0]=(unsigned char)((tempVector[0]/2 + 0.5)*255);\n\t\t\tbytePtr[1]=(unsigned char)((tempVector[1]/2 + 0.5)*255);\n\t\t\tbytePtr[2]=(unsigned char)((tempVector[2]/2 + 0.5)*255);\n\n\t\t\tbytePtr+=3;\n\t\t}\n\t}\n\tqglTexImage2D(\tGL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB,\n\t\t\t\t\t0, GL_RGBA, 32, 32, 0, GL_RGB, GL_UNSIGNED_BYTE, data);\n\n\t//negative x\n\tbytePtr=data;\n\n\tfor(j=0; j<size; j++)\n\t{\n\t\tfor(i=0; i<size; i++)\n\t\t{\n\t\t\ttempVector[0] = (-halfSize);\n\t\t\ttempVector[1] = (-(j+offset-halfSize));\n\t\t\ttempVector[2] = ((i+offset-halfSize));\n\n\t\t\tVectorNormalize(tempVector);\n\n\t\t\tbytePtr[0]=(unsigned char)((tempVector[0]/2 + 0.5)*255);\n\t\t\tbytePtr[1]=(unsigned char)((tempVector[1]/2 + 0.5)*255);\n\t\t\tbytePtr[2]=(unsigned char)((tempVector[2]/2 + 0.5)*255);\n\n\t\t\tbytePtr+=3;\n\t\t}\n\t}\n\tqglTexImage2D(\tGL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB,\n\t\t\t\t\t0, GL_RGBA, 32, 32, 0, GL_RGB, GL_UNSIGNED_BYTE, data);\n\n\t//positive y\n\tbytePtr=data;\n\n\tfor(j=0; j<size; j++)\n\t{\n\t\tfor(i=0; i<size; i++)\n\t\t{\n\t\t\ttempVector[0] = (i+offset-halfSize);\n\t\t\ttempVector[1] = (halfSize);\n\t\t\ttempVector[2] = ((j+offset-halfSize));\n\n\t\t\tVectorNormalize(tempVector);\n\n\t\t\tbytePtr[0]=(unsigned char)((tempVector[0]/2 + 0.5)*255);\n\t\t\tbytePtr[1]=(unsigned char)((tempVector[1]/2 + 0.5)*255);\n\t\t\tbytePtr[2]=(unsigned char)((tempVector[2]/2 + 0.5)*255);\n\n\t\t\tbytePtr+=3;\n\t\t}\n\t}\n\tqglTexImage2D(\tGL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB,\n\t\t\t\t\t0, GL_RGBA, 32, 32, 0, GL_RGB, GL_UNSIGNED_BYTE, data);\n\n\t//negative y\n\tbytePtr=data;\n\n\tfor(j=0; j<size; j++)\n\t{\n\t\tfor(i=0; i<size; i++)\n\t\t{\n\t\t\ttempVector[0] = (i+offset-halfSize);\n\t\t\ttempVector[1] = (-halfSize);\n\t\t\ttempVector[2] = (-(j+offset-halfSize));\n\n\t\t\tVectorNormalize(tempVector);\n\n\t\t\tbytePtr[0]=(unsigned char)((tempVector[0]/2 + 0.5)*255);\n\t\t\tbytePtr[1]=(unsigned char)((tempVector[1]/2 + 0.5)*255);\n\t\t\tbytePtr[2]=(unsigned char)((tempVector[2]/2 + 0.5)*255);\n\n\t\t\tbytePtr+=3;\n\t\t}\n\t}\n\tqglTexImage2D(\tGL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB,\n\t\t\t\t\t0, GL_RGBA, 32, 32, 0, GL_RGB, GL_UNSIGNED_BYTE, data);\n\n\t//positive z\n\tbytePtr=data;\n\n\tfor(j=0; j<size; j++)\n\t{\n\t\tfor(i=0; i<size; i++)\n\t\t{\n\t\t\ttempVector[0] = (i+offset-halfSize);\n\t\t\ttempVector[1] = (-(j+offset-halfSize));\n\t\t\ttempVector[2] = (halfSize);\n\n\t\t\tVectorNormalize(tempVector);\n\n\t\t\tbytePtr[0]=(unsigned char)((tempVector[0]/2 + 0.5)*255);\n\t\t\tbytePtr[1]=(unsigned char)((tempVector[1]/2 + 0.5)*255);\n\t\t\tbytePtr[2]=(unsigned char)((tempVector[2]/2 + 0.5)*255);\n\n\t\t\tbytePtr+=3;\n\t\t}\n\t}\n\tqglTexImage2D(\tGL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB,\n\t\t\t\t\t0, GL_RGBA, 32, 32, 0, GL_RGB, GL_UNSIGNED_BYTE, data);\n\n\t//negative z\n\tbytePtr=data;\n\n\tfor(j=0; j<size; j++)\n\t{\n\t\tfor(i=0; i<size; i++)\n\t\t{\n\t\t\ttempVector[0] = (-(i+offset-halfSize));\n\t\t\ttempVector[1] = (-(j+offset-halfSize));\n\t\t\ttempVector[2] = (-halfSize);\n\n\t\t\tVectorNormalize(tempVector);\n\n\t\t\tbytePtr[0]=(unsigned char)((tempVector[0]/2 + 0.5)*255);\n\t\t\tbytePtr[1]=(unsigned char)((tempVector[1]/2 + 0.5)*255);\n\t\t\tbytePtr[2]=(unsigned char)((tempVector[2]/2 + 0.5)*255);\n\n\t\t\tbytePtr+=3;\n\t\t}\n\t}\n\tqglTexImage2D(\tGL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB,\n\t\t\t\t\t0, GL_RGBA, 32, 32, 0, GL_RGB, GL_UNSIGNED_BYTE, data);\t\n\t\t\n\n\tqglTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_MAG_FILTER, GL_LINEAR);\n\tqglTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_MIN_FILTER, GL_LINEAR);\n\tqglTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);\n\tqglTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);\n\tqglTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);\n\n\treturn normalisationCubeMap;\n}\n\ntexid_t normalisationCubeMap;\n#endif\n\n#if 0\ntypedef struct\n{\n   long offset;                 \t// Position of the entry in WAD\n   long dsize;                  \t// Size of the entry in WAD file\n   long size;                   \t// Size of the entry in memory\n   char type;                   \t// type of entry\n   char cmprs;                  \t// Compression. 0 if none.\n   short dummy;                 \t// Not used\n   char name[16];               \t// we use only first 8\n} wad2entry_t;\ntypedef struct\n{\n   char magic[4]; \t\t\t//should be WAD2\n   long num;\t\t\t\t//number of entries\n   long offset;\t\t\t\t//location of directory\n} wad2_t;\nvoid R_MakeTexWad_f(void)\n{\n\t//this function is written as little endian. nothing will fix that.\n\tmiptex_t dummymip = {\"\", 0, 0, {0, 0, 0, 0}};\n\twad2_t wad2 = {\"WAD2\",0,0};\n\twad2entry_t entry[2048];\n\tint entries = 0, i;\n\tvfsfile_t *f;\n\tchar base[128];\n//\tqbyte b;\n\tqboolean hasalpha;\n\tint width, height;\n\n\tqbyte *buf, *outmip;\n\tqbyte *mip, *stack;\n\n\tchar *wadname = Cmd_Argv(1);\n\tchar *imagename = Cmd_Argv(2);\n\tfloat scale = atof(Cmd_Argv(3));\n\n\tif (!scale)\n\t\tscale = 2;\n\n\tif (!*wadname || !*imagename)\n\t\treturn;\n\tf=FS_OpenVFS(wadname, \"w+b\", FS_GAMEONLY);\n\tif (!f)\n\t\treturn;\n\n\tmip = BZ_Malloc(1024*1024);\n//\tinitbuf = BZ_Malloc(1024*1024*4);\n\tstack = BZ_Malloc(1024*1024*4+1024);\n\n\tVFS_SEEK(f, 0);\n\tVFS_READ(f, &wad2, sizeof(wad2_t));\n\n\tVFS_SEEK(f, wad2.offset);\n\tVFS_READ(f, entry, sizeof(entry[0]) * wad2.num);\n\n\t//find the end of the data.\n\twad2.offset = sizeof(wad2_t);\n\tfor (entries = 0; entries < wad2.num; entries++)\n\t\tif (wad2.offset < entry[entries].offset + entry[entries].dsize)\n\t\t\twad2.offset = entry[entries].offset + entry[entries].dsize;\n\tVFS_SEEK(f, wad2.offset);\n\n\t{\n\t\tCOM_StripExtension(imagename, base, sizeof(base));\n\t\tbase[15]=0;\n\t\tfor (i =0; i < entries; i++)\n\t\t\tif (!stricmp(entry[i].name, base))\n\t\t\t\tbreak;\n\t\tif (i != entries)\n\t\t\tCon_Printf(\"Replacing %s, you'll want to compact your wad at some point.\\n\", base);\t//this will leave a gap. we don't support compacting.\n\t\telse\n\t\t\tentries++;\n\t\tentry[i].offset = VFS_TELL(f);\n\t\tentry[i].dsize = entry[i].size = 0;\n\t\tentry[i].type = TYP_MIPTEX;\n\t\tentry[i].cmprs = 0;\n\t\tentry[i].dummy = 0;\n\t\tstrcpy(entry[i].name, base);\n\n\t\tstrcpy(dummymip.name, base);\n\n\t\t{\n\t\n\t\t\tqbyte *data;\n\t\t\tint h;\n\t\t\tfloat x, xi;\n\t\t\tfloat y, yi;\t\t\t\n\n\t\t\tchar *path[] ={\n\t\t\"%s\",\n\t\t\"override/%s.tga\",\n\n\t\t\"textures/%s.png\",\n\t\t\"textures/%s.tga\",\n\n\t\t\"%s.png\",\n\t\t\"%s.tga\",\n\t\t\"progs/%s\"};\n\t\t\tfor (h = 0, buf=NULL; h < sizeof(path)/sizeof(char *); h++)\n\t\t\t{\t\t\t\n\t\t\t\tbuf = COM_LoadStackFile(va(path[h], imagename), stack, 1024*1024*4+1024);\n\t\t\t\tif (buf)\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\twidth = 16;\n\t\t\theight = 16;\n\t\t\tif (buf)\n\t\t\t\tdata = Read32BitImageFile(buf, com_filesize, &width, &height, &hasalpha, imagename);\n\t\t\telse\n\t\t\t\tdata = NULL;\n\t\t\tif (!data)\n\t\t\t\tdata = Z_Malloc(width*height*4);\n\n\t\t\tdummymip.width = (int)(width/scale) & ~0xf;\n\t\t\tdummymip.height = (int)(height/scale) & ~0xf;\n\t\t\tif (dummymip.width<=0)\n\t\t\t\tdummymip.width=16;\n\t\t\tif (dummymip.height<=0)\n\t\t\t\tdummymip.height=16;\n\t\t\tif (dummymip.width > 1024)\n\t\t\t\tdummymip.width = 1024;\n\t\t\tif (dummymip.height > 1024)\n\t\t\t\tdummymip.height = 1024;\n\n\t\t\tdummymip.offsets[0] = sizeof(dummymip);\n\t\t\tdummymip.offsets[1] = dummymip.offsets[0]+dummymip.width*dummymip.height;\n\t\t\tdummymip.offsets[2] = dummymip.offsets[1]+dummymip.width/2*dummymip.height/2;\n\t\t\tdummymip.offsets[3] = dummymip.offsets[2]+dummymip.width/4*dummymip.height/4;\n\t\t\tentry[entries].dsize = entry[entries].size = dummymip.offsets[3]+dummymip.width/8*dummymip.height/8;\n\n\t\t\txi = (float)width/dummymip.width;\n\t\t\tyi = (float)height/dummymip.height;\n\n\n\t\t\tVFS_WRITE(f, &dummymip, sizeof(dummymip));\n\t\t\toutmip=mip;\n\t\t\tfor (outmip=mip, y = 0; y < height; y+=yi)\n\t\t\tfor (x = 0; x < width; x+=xi)\n\t\t\t{\n\t\t\t\t*outmip++ = GetPaletteIndex(\tdata[(int)(x+y*width)*4+0],\n\t\t\t\t\t\t\t\tdata[(int)(x+y*width)*4+1],\n\t\t\t\t\t\t\t\tdata[(int)(x+y*width)*4+2]);\n\t\t\t}\n\t\t\tVFS_WRITE(f, mip, dummymip.width * dummymip.height);\n\t\t\tfor (outmip=mip, y = 0; y < height; y+=yi*2)\n\t\t\tfor (x = 0; x < width; x+=xi*2)\n\t\t\t{\n\t\t\t\t*outmip++ = GetPaletteIndex(\tdata[(int)(x+y*width)*4+0],\n\t\t\t\t\t\t\t\tdata[(int)(x+y*width)*4+1],\n\t\t\t\t\t\t\t\tdata[(int)(x+y*width)*4+2]);\t\t\t\t\n\t\t\t}\n\t\t\tVFS_WRITE(f, mip, (dummymip.width/2) * (dummymip.height/2));\n\t\t\tfor (outmip=mip, y = 0; y < height; y+=yi*4)\n\t\t\tfor (x = 0; x < width; x+=xi*4)\n\t\t\t{\n\t\t\t\t*outmip++ = GetPaletteIndex(\tdata[(int)(x+y*width)*4+0],\n\t\t\t\t\t\t\t\tdata[(int)(x+y*width)*4+1],\n\t\t\t\t\t\t\t\tdata[(int)(x+y*width)*4+2]);\t\t\t\t\n\t\t\t}\n\t\t\tVFS_WRITE(f, mip, (dummymip.width/4) * (dummymip.height/4));\n\t\t\tfor (outmip=mip, y = 0; y < height; y+=yi*8)\n\t\t\tfor (x = 0; x < width; x+=xi*8)\n\t\t\t{\n\t\t\t\t*outmip++ = GetPaletteIndex(\tdata[(int)(x+y*width)*4+0],\n\t\t\t\t\t\t\t\tdata[(int)(x+y*width)*4+1],\n\t\t\t\t\t\t\t\tdata[(int)(x+y*width)*4+2]);\n\t\t\t}\n\t\t\tVFS_WRITE(f, mip, (dummymip.width/8) * (dummymip.height/8));\n\n\t\t\tBZ_Free(data);\n\t\t}\n\t\tentry[i].dsize = VFS_TELL(f) - entry[i].offset;\n\t\tCon_Printf(\"Added %s\\n\", base);\n\t}\n\n\twad2.offset = VFS_TELL(f);\n\twad2.num = entries;\n\tVFS_WRITE(f, entry, entries*sizeof(wad2entry_t));\n\tVFS_SEEK(f, 0);\n\tVFS_WRITE(f, &wad2, sizeof(wad2_t));\n\tVFS_CLOSE(f);\n\n\n\tBZ_Free(mip);\n//\tBZ_Free(initbuf);\n\tBZ_Free(stack);\n\n\tCon_Printf(\"%s now has %i entries\\n\", wadname, entries);\n}\n#endif\nvoid GLR_TimeRefresh_f (void);\nvoid GLV_Gamma_Callback(struct cvar_s *var, char *oldvalue);\n\nvoid GLR_DeInit (void)\n{\n\tCmd_RemoveCommand (\"timerefresh\");\n\n//\tCmd_RemoveCommand (\"makewad\");\n\n//\tCvar_Unhook(&v_gamma);\n//\tCvar_Unhook(&v_contrast);\n//\tCvar_Unhook(&v_brightness);\n\n\tSurf_DeInit();\n\n\tGLDraw_DeInit();\n}\n\nvoid GLR_Init (void)\n{\t\n\tCmd_AddCommand (\"timerefresh\", GLR_TimeRefresh_f);\n\n//\tCmd_AddCommand (\"makewad\", R_MakeTexWad_f);\n}\n\n/*\n====================\nR_TimeRefresh_f\n\nFor program optimization\n====================\n*/\nvoid GLR_TimeRefresh_f (void)\n{\n\tint\t\t\ti;\n\tfloat\t\tstart, stop, time;\n\tint\t\t\tfinish;\n\tint\t\t\tframes = 128;\n\n\tfinish = atoi(Cmd_Argv(1));\n\tframes = atoi(Cmd_Argv(2));\n\tif (frames < 1)\n\t\tframes = 128;\n\n\tif (!r_refdef.playerview)\n\t\tr_refdef.playerview = &cl.playerview[0];\n\n\tif (finish == 2)\n\t{\n\t\tqglFinish ();\n\t\tstart = Sys_DoubleTime ();\n\t\tfor (i=0 ; i<frames ; i++)\n\t\t{\n\t\t\tr_refdef.viewangles[1] = i/(float)frames*360.0;\n\t\t\tR_RenderView ();\n\t\t\tVID_SwapBuffers();\n\t\t}\n\t}\n\telse\n\t{\n\t\tif (qglDrawBuffer)\n\t\t\tqglDrawBuffer  (GL_FRONT);\n\t\tqglFinish ();\n\n\t\tstart = Sys_DoubleTime ();\n\t\tfor (i=0 ; i<frames ; i++)\n\t\t{\n\t\t\tr_refdef.viewangles[1] = i/(float)frames*360.0;\n\t\t\tR_RenderView ();\n\t\t\tif (finish)\n\t\t\t\tqglFinish ();\n\t\t}\n\t}\n\tqglFinish ();\n\tstop = Sys_DoubleTime ();\n\ttime = stop-start;\n\tCon_Printf (\"%f seconds (%f fps)\\n\", time, frames/time);\n\n\tif (R2D_Flush)\n\t\tR2D_Flush();\n\tif (qglDrawBuffer)\n\t\tqglDrawBuffer  (GL_BACK);\n\tVID_SwapBuffers();\n}\n\n#endif\n"
  },
  {
    "path": "engine/gl/gl_rsurf.c",
    "content": "/*\nCopyright (C) 1996-1997 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n*/\n// r_surf.c: surface-related refresh code\n\n#include \"quakedef.h\"\n#if defined(GLQUAKE)\n#include \"glquake.h\"\n#include \"shader.h\"\n#include \"renderque.h\"\n#include <math.h>\n\nextern cvar_t r_lightmap_nearest;\n\nvoid GLBE_ClearVBO(vbo_t *vbo, qboolean dataonly)\n{\n\tint vboh[6 + MAXRLIGHTMAPS];\n\tint i, j;\n\tvboh[0] = vbo->indicies.gl.vbo;\n\tvboh[1] = vbo->coord.gl.vbo;\n\tvboh[2] = vbo->texcoord.gl.vbo;\n\tvboh[3] = vbo->normals.gl.vbo;\n\tvboh[4] = vbo->svector.gl.vbo;\n\tvboh[5] = vbo->tvector.gl.vbo;\n\tfor (i = 0; i < MAXRLIGHTMAPS; i++)\n\t\tvboh[6+i] = vbo->lmcoord[i].gl.vbo;\n\n\tmemset (&vbo->indicies, 0, sizeof(vbo->indicies));\n\tmemset (&vbo->coord, 0, sizeof(vbo->coord));\n\tmemset (&vbo->texcoord, 0, sizeof(vbo->texcoord));\n\tmemset (&vbo->normals, 0, sizeof(vbo->normals));\n\tmemset (&vbo->svector, 0, sizeof(vbo->svector));\n\tmemset (&vbo->tvector, 0, sizeof(vbo->tvector));\n\tmemset (&vbo->lmcoord, 0, sizeof(vbo->lmcoord));\n\n\tfor (i = 0; i < 7; i++)\n\t{\n\t\tif (!vboh[i])\n\t\t\tcontinue;\n\t\tfor (j = 0; j < i; j++)\n\t\t{\n\t\t\tif (vboh[j] == vboh[i])\n\t\t\t\tbreak;\t//already freed by one of the other ones\n\t\t}\n\t\tif (j == i)\n\t\t\tqglDeleteBuffersARB(1, &vboh[i]);\n\t}\n\tif (vbo->vertdata)\n\t\tBZ_Free(vbo->vertdata);\n\tvbo->vertdata = NULL;\n\tBZ_Free(vbo->meshlist);\n\tvbo->meshlist = NULL;\n\tif (!dataonly)\n\t\tBZ_Free(vbo);\n}\n\nvoid GLBE_SetupVAO(vbo_t *vbo, unsigned int vaodynamic, unsigned int vaostatic);\n\nstatic qboolean GL_BuildVBO(vbo_t *vbo, void *vdata, int vsize, void *edata, int elementsize, unsigned int vaodynamic)\n{\n\tunsigned int vbos[2];\n\tint s;\n\tunsigned int vaostatic = 0;\n\n\tif (!qglGenBuffersARB || !vsize || !elementsize)\n\t\treturn false;\n\n\tqglGenBuffersARB(1+(elementsize>0), vbos);\n\n\t//opengl ate our data, fixup the vbo arrays to point to the vbo instead of the raw data\n\n\tif (vbo->indicies.gl.addr && elementsize)\n\t{\n\t\tvbo->indicies.gl.vbo = vbos[1];\n\t\tvbo->indicies.gl.addr = (index_t*)((char*)vbo->indicies.gl.addr - (char*)edata);\n\t\tvaostatic |= 1u<<VATTR_LEG_ELEMENTS;\n\t}\n\tif (vbo->coord.gl.addr)\n\t{\n\t\tvbo->coord.gl.vbo = vbos[0];\n\t\tvbo->coord.gl.addr = (vecV_t*)((char*)vbo->coord.gl.addr - (char*)vdata);\n\t\tvaostatic |= 1u<<VATTR_VERTEX1;\n\t}\n\tif (vbo->texcoord.gl.addr)\n\t{\n\t\tvbo->texcoord.gl.vbo = vbos[0];\n\t\tvbo->texcoord.gl.addr = (vec2_t*)((char*)vbo->texcoord.gl.addr - (char*)vdata);\n\t\tvaostatic |= 1u<<VATTR_TEXCOORD;\n\t}\n\tfor (s = 0; s < MAXRLIGHTMAPS; s++)\n\t{\n\t\tif (vbo->colours[s].gl.addr)\n\t\t{\n\t\t\tvbo->colours[s].gl.vbo = vbos[0];\n\t\t\tvbo->colours[s].gl.addr = (vec4_t*)((char*)vbo->colours[s].gl.addr - (char*)vdata);\n\t\t\tswitch(s)\n\t\t\t{\n\t\t\tdefault: vaostatic |= 1u<<VATTR_COLOUR; break;\n#if MAXRLIGHTMAPS > 1\n\t\t\tcase 1: vaostatic |= 1u<<VATTR_COLOUR2; break;\n\t\t\tcase 2: vaostatic |= 1u<<VATTR_COLOUR3; break;\n\t\t\tcase 3: vaostatic |= 1u<<VATTR_COLOUR4; break;\n#endif\n\t\t\t}\n\t\t}\n\t\tif (vbo->lmcoord[s].gl.addr)\n\t\t{\n\t\t\tvbo->lmcoord[s].gl.vbo = vbos[0];\n\t\t\tvbo->lmcoord[s].gl.addr = (vec2_t*)((char*)vbo->lmcoord[s].gl.addr - (char*)vdata);\n\t\t\tswitch(s)\n\t\t\t{\n\t\t\tdefault: vaostatic |= 1u<<VATTR_LMCOORD; break;\n#if MAXRLIGHTMAPS > 1\n\t\t\tcase 1: vaostatic |= 1u<<VATTR_LMCOORD2; break;\n\t\t\tcase 2: vaostatic |= 1u<<VATTR_LMCOORD3; break;\n\t\t\tcase 3: vaostatic |= 1u<<VATTR_LMCOORD4; break;\n#endif\n\t\t\t}\n\t\t}\n\t}\n\tif (vbo->normals.gl.addr)\n\t{\n\t\tvbo->normals.gl.vbo = vbos[0];\n\t\tvbo->normals.gl.addr = (vec3_t*)((char*)vbo->normals.gl.addr - (char*)vdata);\n\t\tvaostatic |= 1u<<VATTR_NORMALS;\n\t}\n\tif (vbo->svector.gl.addr)\n\t{\n\t\tvbo->svector.gl.vbo = vbos[0];\n\t\tvbo->svector.gl.addr = (vec3_t*)((char*)vbo->svector.gl.addr - (char*)vdata);\n\t\tvaostatic |= 1u<<VATTR_SNORMALS;\n\t}\n\tif (vbo->tvector.gl.addr)\n\t{\n\t\tvbo->tvector.gl.vbo = vbos[0];\n\t\tvbo->tvector.gl.addr = (vec3_t*)((char*)vbo->tvector.gl.addr - (char*)vdata);\n\t\tvaostatic |= 1u<<VATTR_TNORMALS;\n\t}\n\n\n\tGLBE_SetupVAO(vbo, vaodynamic, vaostatic);\n\t\n\tif (qglBufferStorage)\n\t{\t//gl4.4 allows us to explicitly create device-only vbos\n\t\tqglBufferStorage(GL_ARRAY_BUFFER_ARB, vsize, vdata, 0);\n\t\tif (elementsize>0)\n\t\t\tqglBufferStorage(GL_ELEMENT_ARRAY_BUFFER_ARB, elementsize, edata, 0);\n\t}\n\telse\n\t{\n\t\tqglBufferDataARB(GL_ARRAY_BUFFER_ARB, vsize, vdata, GL_STATIC_DRAW_ARB);\n\t\tif (elementsize>0)\n\t\t{\n\t\t\tqglBufferDataARB(GL_ELEMENT_ARRAY_BUFFER_ARB, elementsize, edata, GL_STATIC_DRAW_ARB);\n\t\t}\n\t}\n\n\treturn true;\n}\n\n//allocates an aligned buffer. caller needs to make sure there's enough space.\nvoid *allocbuf(char **p, int elements, int elementsize)\n{\n\tvoid *ret;\n\t*p += elementsize - 1;\n\t*p -= (size_t)*p & (elementsize-1);\n\tret = *p;\n\t*p += elements*elementsize;\n\treturn ret;\n}\n\nvoid GLBE_GenBatchVBOs(vbo_t **vbochain, batch_t *firstbatch, batch_t *stopbatch, int lightmaps)\n{\n\tunsigned int maxvboverts;\n\tunsigned int maxvboelements;\n\n\tunsigned int i, s;\n\tunsigned int v;\n\tunsigned int vcount, ecount;\n\tunsigned int pervertsize;\t//erm, that name wasn't intentional\n\tunsigned int meshes;\n\n\tvbo_t *vbo;\n\tmesh_t *m;\n\tchar *p;\n\n\tvecV_t *coord;\n\tvec2_t *texcoord;\n\tvec2_t *lmcoord[MAXRLIGHTMAPS];\n\tvec3_t *normals;\n\tvec3_t *svector;\n\tvec3_t *tvector;\n\tvec4_t *colours[MAXRLIGHTMAPS];\n\tindex_t *indicies;\n\tbatch_t *batch;\n\tint vbosize;\n\n\n\tvbo = Z_Malloc(sizeof(*vbo));\n\n\tmaxvboverts = 0;\n\tmaxvboelements = 0;\n\tmeshes = 0;\n\tfor(batch = firstbatch; batch != stopbatch; batch = batch->next)\n\t{\n\t\tfor (i=0 ; i<batch->maxmeshes ; i++)\n\t\t{\n\t\t\tm = batch->mesh[i];\n\t\t\tmeshes++;\n\t\t\tmaxvboelements += m->numindexes;\n\t\t\tmaxvboverts += m->numvertexes;\n\t\t}\n\t}\n\tif (maxvboverts > MAX_INDICIES)\n\t\tSys_Error(\"Building a vbo with too many verticies\\n\");\n\n\n\tvcount = 0;\n\tecount = 0;\n\n\tpervertsize =\tsizeof(vecV_t)+\t//coord\n\t\t\t\tsizeof(vec2_t)+\t//tex\n\t\t\t\tsizeof(vec2_t)*lightmaps+\t//lm\n\t\t\t\tsizeof(vec3_t)+\t//normal\n\t\t\t\tsizeof(vec3_t)+\t//sdir\n\t\t\t\tsizeof(vec3_t)+\t//tdir\n\t\t\t\tsizeof(vec4_t)*lightmaps;\t//colours\n\n\tvbo->vertdata = BZ_Malloc((maxvboverts+1)*pervertsize + (maxvboelements+1)*sizeof(index_t));\n\n\tp = vbo->vertdata;\n\n\tvbo->coord.gl.addr = allocbuf(&p, maxvboverts, sizeof(vecV_t));\n\tvbo->texcoord.gl.addr = allocbuf(&p, maxvboverts, sizeof(vec2_t));\n\tfor (s = 0; s < lightmaps; s++)\n\t\tvbo->lmcoord[s].gl.addr = allocbuf(&p, maxvboverts, sizeof(vec2_t));\n\tfor (; s < MAXRLIGHTMAPS; s++)\n\t\tvbo->lmcoord[s].gl.addr = NULL;\n\tvbo->normals.gl.addr = allocbuf(&p, maxvboverts, sizeof(vec3_t));\n\tvbo->svector.gl.addr = allocbuf(&p, maxvboverts, sizeof(vec3_t));\n\tvbo->tvector.gl.addr = allocbuf(&p, maxvboverts, sizeof(vec3_t));\n\tfor (s = 0; s < lightmaps; s++)\n\t\tvbo->colours[s].gl.addr = allocbuf(&p, maxvboverts, sizeof(vec4_t));\n\tfor (; s < MAXRLIGHTMAPS; s++)\n\t\tvbo->lmcoord[s].gl.addr = NULL;\n\tvbosize = (char*)p - (char*)vbo->coord.gl.addr;\n\tif ((char*)p - (char*)vbo->vertdata > (maxvboverts+1)*pervertsize)\n\t\tSys_Error(\"GLBE_GenBatchVBOs: aligned overflow\");\n\tvbo->indicies.gl.addr = allocbuf(&p, maxvboelements, sizeof(index_t));\n\n\tcoord = vbo->coord.gl.addr;\n\ttexcoord = vbo->texcoord.gl.addr;\n\tfor (s = 0; s < MAXRLIGHTMAPS; s++)\n\t{\n\t\tlmcoord[s] = vbo->lmcoord[s].gl.addr;\n\t\tcolours[s] = vbo->colours[s].gl.addr;\n\t}\n\tnormals = vbo->normals.gl.addr;\n\tsvector = vbo->svector.gl.addr;\n\ttvector = vbo->tvector.gl.addr;\n\tindicies = vbo->indicies.gl.addr;\n\n\t//vbo->meshcount = meshes;\n\t//vbo->meshlist = BZ_Malloc(meshes*sizeof(*vbo->meshlist));\n\n\tmeshes = 0;\n\n\n\tfor(batch = firstbatch; batch != stopbatch; batch = batch->next)\n\t{\n\t\tbatch->vbo = vbo;\n\t\tfor (i=0 ; i<batch->maxmeshes ; i++)\n\t\t{\n\t\t\tm = batch->mesh[i];\n\n//\t\t\tsurf->mark = &vbo->meshlist[meshes++];\n//\t\t\t*surf->mark = NULL;\n\n\t\t\tm->vbofirstvert = vcount;\n\t\t\tm->vbofirstelement = ecount;\n\t\t\tfor (v = 0; v < m->numindexes; v++)\n\t\t\t\tindicies[ecount++] = vcount + m->indexes[v];\n\t\t\tfor (v = 0; v < m->numvertexes; v++)\n\t\t\t{\n\t\t\t\tcoord[vcount+v][0] = m->xyz_array[v][0];\n\t\t\t\tcoord[vcount+v][1] = m->xyz_array[v][1];\n\t\t\t\tcoord[vcount+v][2] = m->xyz_array[v][2];\n\t\t\t\tif (m->st_array)\n\t\t\t\t{\n\t\t\t\t\ttexcoord[vcount+v][0] = m->st_array[v][0];\n\t\t\t\t\ttexcoord[vcount+v][1] = m->st_array[v][1];\n\t\t\t\t}\n\t\t\t\tfor (s = 0; s < lightmaps; s++)\n\t\t\t\t{\n\t\t\t\t\tif (m->lmst_array[s])\n\t\t\t\t\t{\n\t\t\t\t\t\tlmcoord[s][vcount+v][0] = m->lmst_array[s][v][0];\n\t\t\t\t\t\tlmcoord[s][vcount+v][1] = m->lmst_array[s][v][1];\n\t\t\t\t\t}\n\t\t\t\t\tif (m->colors4f_array[s])\n\t\t\t\t\t{\n\t\t\t\t\t\tcolours[s][vcount+v][0] = m->colors4f_array[s][v][0];\n\t\t\t\t\t\tcolours[s][vcount+v][1] = m->colors4f_array[s][v][1];\n\t\t\t\t\t\tcolours[s][vcount+v][2] = m->colors4f_array[s][v][2];\n\t\t\t\t\t\tcolours[s][vcount+v][3] = m->colors4f_array[s][v][3];\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (m->normals_array)\n\t\t\t\t{\n\t\t\t\t\tnormals[vcount+v][0] = m->normals_array[v][0];\n\t\t\t\t\tnormals[vcount+v][1] = m->normals_array[v][1];\n\t\t\t\t\tnormals[vcount+v][2] = m->normals_array[v][2];\n\t\t\t\t}\n\t\t\t\tif (m->snormals_array)\n\t\t\t\t{\n\t\t\t\t\tsvector[vcount+v][0] = m->snormals_array[v][0];\n\t\t\t\t\tsvector[vcount+v][1] = m->snormals_array[v][1];\n\t\t\t\t\tsvector[vcount+v][2] = m->snormals_array[v][2];\n\t\t\t\t}\n\t\t\t\tif (m->tnormals_array)\n\t\t\t\t{\n\t\t\t\t\ttvector[vcount+v][0] = m->tnormals_array[v][0];\n\t\t\t\t\ttvector[vcount+v][1] = m->tnormals_array[v][1];\n\t\t\t\t\ttvector[vcount+v][2] = m->tnormals_array[v][2];\n\t\t\t\t}\n\t\t\t}\n\t\t\tvcount += v;\n\t\t}\n\t}\n\n\tif (GL_BuildVBO(vbo, vbo->coord.gl.addr, vbosize/*vcount*pervertsize*/, indicies, ecount*sizeof(index_t), 0))\n\t{\n\t\tBZ_Free(vbo->vertdata);\n\t\tvbo->vertdata = NULL;\n\t}\n\tvbo->vertcount = vcount;\n\n\tvbo->next = *vbochain;\n\t*vbochain = vbo;\n}\n\nvoid GLBE_GenBrushModelVBO(model_t *mod)\n{\n\tunsigned int vcount;\n\tunsigned int cvcount;\n\n\n\tbatch_t *batch, *fbatch;\n\tint sortid;\n\tint i;\n\n\tfbatch = NULL;\n\tvcount = 0;\n\tfor (sortid = 0; sortid < SHADER_SORT_COUNT; sortid++)\n\t{\n\t\tif (!mod->batches[sortid])\n\t\t\tcontinue;\n\n\t\tfor (fbatch = batch = mod->batches[sortid]; batch != NULL; batch = batch->next)\n\t\t{\n\t\t\tfor (i = 0, cvcount = 0; i < batch->maxmeshes; i++)\n\t\t\t\tcvcount += batch->mesh[i]->numvertexes;\n\n\t\t\t//firstmesh got reused as the number of verticies in each batch\n\t\t\tif (vcount + cvcount > MAX_INDICIES)\n\t\t\t{\n\t\t\t\tGLBE_GenBatchVBOs(&mod->vbos, fbatch, batch, mod->lightmaps.surfstyles);\n\t\t\t\tfbatch = batch;\n\t\t\t\tvcount = 0;\n\t\t\t}\n\n\t\t\tvcount += cvcount;\n\t\t}\n\n\t\tGLBE_GenBatchVBOs(&mod->vbos, fbatch, batch, mod->lightmaps.surfstyles);\n\t}\n\n#if 0\n\tif (!mod->numsurfaces)\n\t\treturn;\n\n\tfor (t = 0; t < mod->numtextures; t++)\n\t{\n\t\tif (!mod->textures[t])\n\t\t\tcontinue;\n\t\tvbo = &mod->textures[t]->vbo;\n\t\tBE_ClearVBO(vbo);\n\n\t\tmaxvboverts = 0;\n\t\tmaxvboelements = 0;\n\t\tmeshes = 0;\n\t\tfor (i=0 ; i<mod->numsurfaces ; i++)\n\t\t{\n\t\t\tif (mod->surfaces[i].texinfo->texture != mod->textures[t])\n\t\t\t\tcontinue;\n\t\t\tm = mod->surfaces[i].mesh;\n\t\t\tif (!m)\n\t\t\t\tcontinue;\n\n\t\t\tmeshes++;\n\t\t\tmaxvboelements += m->numindexes;\n\t\t\tmaxvboverts += m->numvertexes;\n\t\t}\n#if sizeof_index_t == 2\n\t\tif (maxvboverts > (1<<(sizeof(index_t)*8))-1)\n\t\t\tcontinue;\n#endif\n\t\tif (!maxvboverts)\n\t\t\tcontinue;\n\n\t\t//fixme: stop this from leaking!\n\t\tvcount = 0;\n\t\tecount = 0;\n\n\t\tpervertsize =\tsizeof(vecV_t)+\t//coord\n\t\t\t\t\tsizeof(vec2_t)+\t//tex\n\t\t\t\t\tsizeof(vec2_t)+\t//lm\n\t\t\t\t\tsizeof(vec3_t)+\t//normal\n\t\t\t\t\tsizeof(vec3_t)+\t//sdir\n\t\t\t\t\tsizeof(vec3_t)+\t//tdir\n\t\t\t\t\tsizeof(vec4_t);\t//colours\n\n\t\tvbo->vertdata = BZ_Malloc((maxvboverts+1)*pervertsize + (maxvboelements+1)*sizeof(index_t));\n\n\t\tp = vbo->vertdata;\n\n\t\tvbo->coord.gl.addr = allocbuf(&p, maxvboverts, sizeof(vecV_t));\n\t\tvbo->texcoord.gl.addr = allocbuf(&p, maxvboverts, sizeof(vec2_t));\n\t\tvbo->lmcoord.gl.addr = allocbuf(&p, maxvboverts, sizeof(vec2_t));\n\t\tvbo->normals.gl.addr = allocbuf(&p, maxvboverts, sizeof(vec3_t));\n\t\tvbo->svector.gl.addr = allocbuf(&p, maxvboverts, sizeof(vec3_t));\n\t\tvbo->tvector.gl.addr = allocbuf(&p, maxvboverts, sizeof(vec3_t));\n\t\tvbo->colours.gl.addr = allocbuf(&p, maxvboverts, sizeof(vec4_t));\n\t\tvbo->indicies.gl.addr = allocbuf(&p, maxvboelements, sizeof(index_t));\n\n\t\tcoord = vbo->coord.gl.addr;\n\t\ttexcoord = vbo->texcoord.gl.addr;\n\t\tlmcoord = vbo->lmcoord.gl.addr;\n\t\tnormals = vbo->normals.gl.addr;\n\t\tsvector = vbo->svector.gl.addr;\n\t\ttvector = vbo->tvector.gl.addr;\n\t\tcolours = vbo->colours.gl.addr;\n\t\tindicies = vbo->indicies.gl.addr;\n\n\t\tvbo->meshcount = meshes;\n\t\tvbo->meshlist = BZ_Malloc(meshes*sizeof(*vbo->meshlist));\n\n\t\tmeshes = 0;\n\n\t\tfor (i=0 ; i<mod->numsurfaces ; i++)\n\t\t{\n\t\t\tif (mod->surfaces[i].texinfo->texture != mod->textures[t])\n\t\t\t\tcontinue;\n\t\t\tm = mod->surfaces[i].mesh;\n\t\t\tif (!m)\n\t\t\t\tcontinue;\n\n\t\t\tmod->surfaces[i].mark = &vbo->meshlist[meshes++];\n\t\t\t*mod->surfaces[i].mark = NULL;\n\n\t\t\tm->vbofirstvert = vcount;\n\t\t\tm->vbofirstelement = ecount;\n\t\t\tfor (v = 0; v < m->numindexes; v++)\n\t\t\t\tindicies[ecount++] = vcount + m->indexes[v];\n\t\t\tfor (v = 0; v < m->numvertexes; v++)\n\t\t\t{\n\t\t\t\tcoord[vcount+v][0] = m->xyz_array[v][0];\n\t\t\t\tcoord[vcount+v][1] = m->xyz_array[v][1];\n\t\t\t\tcoord[vcount+v][2] = m->xyz_array[v][2];\n\t\t\t\tif (m->st_array)\n\t\t\t\t{\n\t\t\t\t\ttexcoord[vcount+v][0] = m->st_array[v][0];\n\t\t\t\t\ttexcoord[vcount+v][1] = m->st_array[v][1];\n\t\t\t\t}\n\t\t\t\tif (m->lmst_array)\n\t\t\t\t{\n\t\t\t\t\tlmcoord[vcount+v][0] = m->lmst_array[v][0];\n\t\t\t\t\tlmcoord[vcount+v][1] = m->lmst_array[v][1];\n\t\t\t\t}\n\t\t\t\tif (m->normals_array)\n\t\t\t\t{\n\t\t\t\t\tnormals[vcount+v][0] = m->normals_array[v][0];\n\t\t\t\t\tnormals[vcount+v][1] = m->normals_array[v][1];\n\t\t\t\t\tnormals[vcount+v][2] = m->normals_array[v][2];\n\t\t\t\t}\n\t\t\t\tif (m->snormals_array)\n\t\t\t\t{\n\t\t\t\t\tsvector[vcount+v][0] = m->snormals_array[v][0];\n\t\t\t\t\tsvector[vcount+v][1] = m->snormals_array[v][1];\n\t\t\t\t\tsvector[vcount+v][2] = m->snormals_array[v][2];\n\t\t\t\t}\n\t\t\t\tif (m->tnormals_array)\n\t\t\t\t{\n\t\t\t\t\ttvector[vcount+v][0] = m->tnormals_array[v][0];\n\t\t\t\t\ttvector[vcount+v][1] = m->tnormals_array[v][1];\n\t\t\t\t\ttvector[vcount+v][2] = m->tnormals_array[v][2];\n\t\t\t\t}\n\t\t\t\tif (m->colors4f_array)\n\t\t\t\t{\n\t\t\t\t\tcolours[vcount+v][0] = m->colors4f_array[v][0];\n\t\t\t\t\tcolours[vcount+v][1] = m->colors4f_array[v][1];\n\t\t\t\t\tcolours[vcount+v][2] = m->colors4f_array[v][2];\n\t\t\t\t\tcolours[vcount+v][3] = m->colors4f_array[v][3];\n\t\t\t\t}\n\t\t\t}\n\t\t\tvcount += v;\n\t\t}\n\n\t\tif (GL_BuildVBO(vbo, vbo->vertdata, vcount*pervertsize, indicies, ecount*sizeof(index_t), 0))\n\t\t{\n\t\t\tBZ_Free(vbo->vertdata);\n\t\t\tvbo->vertdata = NULL;\n\t\t}\n\t\tvbo->vertcount = vcount;\n\t}\n#endif\n}\n\n#endif\n"
  },
  {
    "path": "engine/gl/gl_screen.c",
    "content": "/*\nCopyright (C) 1996-1997 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  \n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n*/\n\n// screen.c -- master for refresh, status bar, console, chat, notify, etc\n\n#include \"quakedef.h\"\nextern qboolean gammaworks;\n#ifdef GLQUAKE\n#include \"glquake.h\"\n#include \"shader.h\"\n#include \"gl_draw.h\"\n\n#include <time.h>\n\nqboolean GLSCR_UpdateScreen (void);\n\n\nextern qboolean\tscr_drawdialog;\n\nextern cvar_t vid_triplebuffer;\nextern cvar_t          scr_fov;\n\nextern qboolean        scr_initialized;\nextern float oldsbar;\nextern qboolean        scr_drawloading;\n\nextern cvar_t vid_conautoscale;\nextern qboolean\t\tscr_con_forcedraw;\nextern qboolean\t\tdepthcleared;\n\n/*\n==================\nSCR_UpdateScreen\n\nThis is called every frame, and can also be called explicitly to flush\ntext to the screen.\n\nWARNING: be very careful calling this from elsewhere, because the refresh\nneeds almost the entire 256k of stack space!\n==================\n*/\nvoid SCR_DrawCursor(void);\nqboolean GLSCR_UpdateScreen (void)\n{\n\tqboolean nohud;\n\tqboolean noworld;\n\textern cvar_t vid_srgb;\n\n\tr_refdef.pxrect.maxheight = vid.pixelheight;\n\n\tvid.numpages = 2 + vid_triplebuffer.value;\n\n\tif (!scr_initialized || !con_initialized)\n\t{\n\t\treturn false;                         // not initialized yet\n\t}\n\n\tR2D_Font_Changed();\n\n\tif (vid_srgb.modified)\n\t{\n\t\tvid_srgb.modified = false;\n\n\t\t//vid_srgb can be changed between 0 and 1, but other values need texture reloads. do that without too much extra weirdness.\n\t\tif ((vid.flags & VID_SRGB_CAPABLE) && gl_config.arb_framebuffer_srgb)\n\t\t{\t//srgb-capable\n\t\t\tif (vid_srgb.ival > 0 && (vid.flags & VID_SRGBAWARE))\n\t\t\t{\t//full srgb wanted (and textures are loaded)\n\t\t\t\tqglEnable(GL_FRAMEBUFFER_SRGB);\n\t\t\t\tvid.flags |= VID_SRGB_FB_LINEAR;\n\t\t\t}\n\t\t\telse if (vid_srgb.ival < 0 || (vid.flags & VID_SRGBAWARE))\n\t\t\t{\t//srgb wanted only for the framebuffer, for gamma tricks.\n\t\t\t\tqglEnable(GL_FRAMEBUFFER_SRGB);\n\t\t\t\tvid.flags |= VID_SRGB_FB_LINEAR;\n\t\t\t}\n\t\t\telse\n\t\t\t{\t//srgb not wanted...\n\t\t\t\tqglDisable(GL_FRAMEBUFFER_SRGB);\n\t\t\t\tvid.flags &= ~VID_SRGB_FB_LINEAR;\n\t\t\t}\n\t\t}\n\t}\n\n\tif (scr_disabled_for_loading)\n\t{\n\t\textern char levelshotname[];\n\t\textern float scr_disabled_time;\n\t\tfloat now = Sys_DoubleTime();\n\t\tif (now - scr_disabled_time > 60 || Key_Dest_Has(~kdm_game))\n\t\t{\n\t\t\t//FIXME: instead of reenabling the screen, we should just draw the relevent things skipping only the game (except that this requires a copy of the game beneath or otherwise results in flickering).\n\t\t\tscr_disabled_for_loading = false;\n\t\t}\n\t\telse if (!*levelshotname && !CSQC_UseGamecodeLoadingScreen() && !MP_UsingGamecodeLoadingScreen()\n#ifdef MENU_NATIVECODE\n\t\t\t\t&& !(mn_entry && mn_entry->DrawLoading)\n#endif\n\t\t\t\t)\n\t\t\treturn false;\t//don't refresh if we can't do so safely.\n\t\telse\n\t\t{\n\t\t\tSCR_DrawLoading (true);\n\t\t\tSCR_SetUpToDrawConsole();\n\t\t\tif (Key_Dest_Has(kdm_console))\n\t\t\t\tSCR_DrawConsole(false);\n\t\t\tif (R2D_Flush)\n\t\t\t\tR2D_Flush();\n\t\t\tVID_SwapBuffers();\n\t\t\treturn true;\n\t\t}\n\t}\n\n\n\tShader_DoReload();\n\n\tqglDisable(GL_SCISSOR_TEST);\n\n#ifdef TEXTEDITOR\n\tif (editormodal)\n\t{\n\t\tEditor_Draw();\n\t\tV_UpdatePalette (false);\n\t\tR2D_BrightenScreen();\n\t\tMedia_RecordFrame();\n\n\t\tif (key_dest_mask & kdm_console)\n\t\t\tCon_DrawConsole(vid.height/2, false);\n\t\telse\n\t\t\tCon_DrawConsole(0, false);\n\t\tSCR_DrawCursor();\n\t}\n\telse\n#endif\n\t{\n\t\t//\n\t\t// do 3D refresh drawing, and then update the screen\n\t\t//\n\t\tSCR_SetUpToDrawConsole ();\n\n\t\tnoworld = false;\n\t\tnohud = false;\n\n\t\tif (r_clear.ival)\n\t\t{\n\t\t\tGL_ForceDepthWritable();\n\t\t\tif (r_clearcolour.ival)\n\t\t\t{\n\t\t\t\tr_clearcolour.vec4[0] = host_basepal[(r_clearcolour.ival & 0xFF)*3+0]/255.0;\n\t\t\t\tr_clearcolour.vec4[1] = host_basepal[(r_clearcolour.ival & 0xFF)*3+1]/255.0;\n\t\t\t\tr_clearcolour.vec4[2] = host_basepal[(r_clearcolour.ival & 0xFF)*3+2]/255.0;\n\t\t\t}\n\t\t\tqglClearColor(r_clearcolour.vec4[0], r_clearcolour.vec4[1], r_clearcolour.vec4[2], 1);\n\t\t\tqglClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);\n\t\t\tdepthcleared = true;\n\t\t}\n\n\t\tif (topmenu && topmenu->isopaque)\n\t\t\tnohud = true;\n#ifdef VM_CG\n\t\telse if (q3 && q3->cg.Redraw(cl.time))\n\t\t\tnohud = true;\n#endif\n#ifdef CSQC_DAT\n\t\telse if (CSQC_DrawView())\n\t\t\tnohud = true;\n#endif\n\t\telse\n\t\t{\n\t\t\tif ((r_worldentity.model && cls.state == ca_active) || vrui.enabled)\n\t\t\t\tV_RenderView (nohud);\n\t\t\telse\n\t\t\t\tnoworld = true;\n\t\t}\n\n\t\tGL_Set2D (false);\n\n\t\tscr_con_forcedraw = false;\n\t\tif (noworld)\n\t\t{\n\t\t\t//draw the levelshot or the conback fullscreen\n\t\t\tif (R2D_DrawLevelshot())\n\t\t\t\t;\n\t\t\telse if (scr_con_current != vid.height)\n\t\t\t{\n#ifdef HAVE_LEGACY\n\t\t\t\textern cvar_t dpcompat_console;\n\t\t\t\tif (dpcompat_console.ival)\n\t\t\t\t{\n\t\t\t\t\tR2D_ImageColours(0,0,0,1);\n\t\t\t\t\tR2D_FillBlock(0, 0, vid.width, vid.height);\n\t\t\t\t\tR2D_ImageColours(1,1,1,1);\n\t\t\t\t}\n\t\t\t\telse\n#endif\n\t\t\t\t\tR2D_ConsoleBackground(0, vid.height, true);\n\t\t\t}\n\t\t\telse\n\t\t\t\tscr_con_forcedraw = true;\n\n\t\t\tnohud = true;\n\t\t}\n\n\t\tr_refdef.playerview = &cl.playerview[0];\n\t\tif (!vrui.enabled)\n\t\t\tSCR_DrawTwoDimensional(nohud);\n\n\t\tV_UpdatePalette (false);\n\t\tR2D_BrightenScreen();\n\t\tMedia_RecordFrame();\n\t}\n\n\tRSpeedShow();\n\tif (R2D_Flush)\n\t\tR2D_Flush();\n\n\t{\n\t\tRSpeedMark();\n\t\tVID_SwapBuffers();\n\t\tRSpeedEnd(RSPEED_PRESENT);\n\t}\n\n\t//gl 4.5 / GL_ARB_robustness / GL_KHR_robustness\n\tif (qglGetGraphicsResetStatus)\n\t{\n\t\tchar *reason;\n\t\tGLenum err = qglGetGraphicsResetStatus();\n\t\tswitch(err)\n\t\t{\n\t\tcase GL_NO_ERROR:\n\t\t\tbreak;\n\t\tcase GL_GUILTY_CONTEXT_RESET:\t//we did it\n\t\tcase GL_INNOCENT_CONTEXT_RESET:\t//something else broke the hardware and broke our ram\n\t\tcase GL_UNKNOWN_CONTEXT_RESET:\t//whodunit\n\t\tdefault:\n\t\t\tif (err == GL_GUILTY_CONTEXT_RESET)\n\t\t\t\treason = \"guilty\";\n\t\t\telse if (err == GL_INNOCENT_CONTEXT_RESET)\n\t\t\t\treason = \"innocent\";\n\t\t\telse\n\t\t\t\treason = \"unknown\";\n\t\t\tCon_Printf(\"OpenGL reset detected (%s)\\n\", reason);\n\t\t\tSys_Sleep(3.0);\n\t\t\tCmd_ExecuteString(\"vid_restart\", RESTRICT_LOCAL);\n\t\t\tbreak;\n\t\t}\n\t}\n\treturn true;\n}\n\n\nchar *GLVID_GetRGBInfo(int *bytestride, int *truewidth, int *trueheight, enum uploadfmt *fmt)\n{\t//returns a BZ_Malloced array\n\textern qboolean gammaworks;\n\tint i, c;\n\tqbyte *ret;\n\textern qboolean r2d_canhwgamma;\n\tqboolean hdr = false;\n\n\t*bytestride = 0;\n\t*truewidth = vid.fbpwidth;\n\t*trueheight = vid.fbpheight;\n\n\tif (*r_refdef.rt_destcolour[0].texname)\n\t{\n\t\tunsigned int w,h;\n\t\ttexid_t tid = R2D_RT_GetTexture(r_refdef.rt_destcolour[0].texname, &w, &h);\n\t\tif (tid)\n\t\t\thdr = (tid->format==PTI_RGBA16F)||(tid->format==PTI_RGBA32F)||(tid->format==PTI_B10G11R11F);\n\t}\n\tif (hdr)\n\t{\n\t\t*fmt = PTI_RGBA16F;\n\t\tret = BZ_Malloc((*truewidth)*(*trueheight)*8);\n\t\tqglReadPixels (0, 0, (*truewidth), (*trueheight), GL_RGBA, GL_HALF_FLOAT, ret);\n\t\t*bytestride = *truewidth*-8;\n\t}\n\t/*else if (1)\n\t{\n\t\tfloat *p;\n\n\t\tp = BZ_Malloc(vid.pixelwidth*vid.pixelheight*sizeof(float));\n\t\tqglReadPixels (0, 0, vid.pixelwidth, vid.pixelheight, GL_DEPTH_COMPONENT, GL_FLOAT, p); \n\n\t\tret = BZ_Malloc(vid.pixelwidth*vid.pixelheight*3);\n\n\t\tc = vid.pixelwidth*vid.pixelheight;\n\t\tfor (i = 1; i < c; i++)\n\t\t{\n\t\t\tret[i*3+0]=p[i]*p[i]*p[i]*255;\n\t\t\tret[i*3+1]=p[i]*p[i]*p[i]*255;\n\t\t\tret[i*3+2]=p[i]*p[i]*p[i]*255;\n\t\t}\n\t\tBZ_Free(p);\n\t}*/\n\telse if (gl_config.gles || (*truewidth&3))\n\t{\n\t\t//gles:\n\t\t//Only two format/type parameter pairs are accepted.\n\t\t//GL_RGBA/GL_UNSIGNED_BYTE is always accepted, and the other acceptable pair can be discovered by querying GL_IMPLEMENTATION_COLOR_READ_FORMAT and GL_IMPLEMENTATION_COLOR_READ_TYPE.\n\t\t//thus its simpler to only use GL_RGBA/GL_UNSIGNED_BYTE\n\t\t//desktopgl:\n\t\t//total line byte length must be aligned to GL_PACK_ALIGNMENT. by reading rgba instead of rgb, we can ensure the line is a multiple of 4 bytes.\n\n\t\t*fmt = PTI_RGBA8;\n\t\tret = BZ_Malloc((*truewidth)*(*trueheight)*4);\n\t\tqglReadPixels (0, 0, (*truewidth), (*trueheight), GL_RGBA, GL_UNSIGNED_BYTE, ret);\n\t\t*bytestride = *truewidth*-4;\n\t}\n#if 1//def _DEBUG\n\telse if (!gl_config.gles && sh_config.texfmt[PTI_BGRA8])\n\t{\n\t\t*fmt = PTI_BGRA8;\n\t\tret = BZ_Malloc((*truewidth)*(*trueheight)*4);\n\t\tqglReadPixels (0, 0, (*truewidth), (*trueheight), GL_BGRA_EXT, GL_UNSIGNED_INT_8_8_8_8_REV, ret); \n\t\t*bytestride = *truewidth*-4;\n\t}\n#endif\n\telse\n\t{\n\t\t*fmt = PTI_RGB8;\n\t\tret = BZ_Malloc((*truewidth)*(*trueheight)*3);\n\t\tqglReadPixels (0, 0, (*truewidth), (*trueheight), GL_RGB, GL_UNSIGNED_BYTE, ret); \n\t\t*bytestride = *truewidth*-3;\n\t}\n\n\tif (gammaworks && r2d_canhwgamma)\n\t{\n\t\textern qbyte\t\tgammatable[256];\n\t\tint pxsize = 4;\n\t\tc = (*truewidth)*(*trueheight);\n\t\tswitch(*fmt)\n\t\t{\n\t\tcase PTI_RGB8:\n\t\tcase PTI_BGR8:\n\t\t\tpxsize = 3;\n\t\t\t//fallthrough\n\t\tcase PTI_LLLA8:\n\t\tcase PTI_LLLX8:\n\t\tcase PTI_RGBA8:\n\t\tcase PTI_RGBX8:\n\t\tcase PTI_BGRA8:\n\t\tcase PTI_BGRX8:\n\t\t\t//pxsize is 4 (or 3 if we fell through above)\n\t\t\tc*=pxsize;\n\t\t\tfor (i=0 ; i<c ; i+=pxsize)\n\t\t\t{\n\t\t\t\tret[i+0] = gammatable[ret[i+0]];\n\t\t\t\tret[i+1] = gammatable[ret[i+1]];\n\t\t\t\tret[i+2] = gammatable[ret[i+2]];\n\t\t\t}\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tbreak;\t//some kind of bug.\n\t\t}\n\t}\n\t\n\treturn ret;\n}\n#endif\n"
  },
  {
    "path": "engine/gl/gl_shader.c",
    "content": "/*\nCopyright (C) 2002-2003 Victor Luchits\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n*/\n// r_shader.c - based on code by Stephen C. Taylor\n// Ported to FTE from qfusion, there are numerous changes since then.\n\n\n#include \"quakedef.h\"\n#ifndef SERVERONLY\n#include \"glquake.h\"\n#ifdef VKQUAKE\n#include \"../vk/vkrenderer.h\"\n#endif\n#include \"shader.h\"\n\n#include \"hash.h\"\n\n\n\n#include <ctype.h>\n\nextern texid_t missing_texture;\ntexid_t r_whiteimage, r_blackimage;\nqboolean shader_reload_needed;\nstatic qboolean shader_rescan_needed;\n\nsh_config_t sh_config;\n\n//cvars that affect shader generation\ncvar_t r_vertexlight = CVARFD(\"r_vertexlight\", \"0\", CVAR_SHADERSYSTEM, \"Hack loaded shaders to remove detail pass and lightmap sampling for faster rendering.\");\ncvar_t r_forceprogramify = CVARAFD(\"r_forceprogramify\", \"0\", \"dpcompat_makeshitup\", CVAR_SHADERSYSTEM, \"Reduce the shader to a single texture, and then make stuff up about its mother. The resulting fist fight results in more colour when you shine a light upon its face.\\nSet to 2 to ignore 'depthfunc equal' and 'tcmod scale' in order to tolerate bizzare shaders made for a bizzare engine.\\nBecause most shaders made for DP are by people who _clearly_ have no idea what the heck they're doing, you'll typically need the '2' setting.\");\n#ifdef HAVE_LEGACY\ncvar_t dpcompat_nopremulpics = CVARFD(\"dpcompat_nopremulpics\", \"0\", CVAR_SHADERSYSTEM, \"By default FTE uses premultiplied alpha for hud/2d images, while DP does not (which results in halos with low-res content). Unfortunately DDS files would need to be recompressed, resulting in visible issues.\");\n#endif\ncvar_t r_glsl_precache = CVARFD(\"r_glsl_precache\", \"0\", CVAR_SHADERSYSTEM, \"Force all relevant glsl permutations to load upfront.\");\ncvar_t r_halfrate = CVARFD(\"r_halfrate\", \"0\", CVAR_ARCHIVE|CVAR_SHADERSYSTEM, \"Use half-rate shading (where supported by gpu).\");\n\nextern cvar_t r_glsl_offsetmapping_reliefmapping;\nextern cvar_t r_drawflat;\nextern cvar_t r_shaderblobs;\nextern cvar_t r_tessellation;\nextern cvar_t gl_compress;\n\n//backend fills this in to say the max fixed-function pass count (often 1, where its emulated by us, because we're too lazy).\nint be_maxpasses;\n\n\n#define Q_stricmp stricmp\n#define Q_strnicmp strnicmp\n#define clamp(v,min, max) (v) = (((v)<(min))?(min):(((v)>(max))?(max):(v)));\n\ntypedef union {\n\tfloat\t\t\tf;\n\tunsigned int\ti;\n} float_int_t;\nqbyte FloatToByte( float x )\n{\n\tstatic float_int_t f2i;\n\n\t// shift float to have 8bit fraction at base of number\n\tf2i.f = x + 32768.0f;\n\n\t// then read as integer and kill float bits...\n\treturn (qbyte) min(f2i.i & 0x7FFFFF, 255);\n}\n\n\n\ncvar_t r_detailtextures;\n\n#define MAX_TOKEN_CHARS sizeof(com_token)\n\nchar *COM_ParseExt (const char **data_p, qboolean nl, qboolean comma)\n{\n\tint\t\tc;\n\tint\t\tlen;\n\tconst char\t*data;\n\n\tCOM_AssertMainThread(\"COM_ParseExt\");\n\n\tdata = *data_p;\n\tlen = 0;\n\tcom_token[0] = 0;\n\n\tif (!data)\n\t{\n\t\t*data_p = NULL;\n\t\treturn \"\";\n\t}\n\n// skip whitespace\nskipwhite:\n\twhile ((c = (unsigned char)*data) <= ' ')\n\t{\n\t\tif (c == 0)\n\t\t{\n\t\t\t*data_p = NULL;\n\t\t\treturn \"\";\n\t\t}\n\t\tif (c == '\\n' && !nl)\n\t\t{\n\t\t\t*data_p = data;\n\t\t\treturn com_token;\n\t\t}\n\t\tdata++;\n\t}\n\n// skip // comments\n\tif (c == '/' && data[1] == '/')\n\t{\n\t\twhile (*data && *data != '\\n')\n\t\t\tdata++;\n\t\tgoto skipwhite;\n\t}\n// skip /* comments\n\tif (c == '/' && data[1] == '*')\n\t{\n\t\tconst char *start = data;\n\t\tdata+=2;\n\t\tfor(;data[0];)\n\t\t{\n\t\t\tif (data[0] == '*' && data[1] == '/')\n\t\t\t{\n\t\t\t\tdata+=2;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (*data == '\\n' && !nl)\n\t\t\t{\n\t\t\t\t*data_p = start;\n\t\t\t\treturn com_token;\n\t\t\t}\n\t\t\tdata++;\n\t\t}\n\t\tgoto skipwhite;\n\t}\n\n// handle quoted strings specially\n\tif (c == '\\\"')\n\t{\n\t\tdata++;\n\t\twhile (1)\n\t\t{\n\t\t\tc = *data++;\n\t\t\tif (c=='\\\"' || !c)\n\t\t\t{\n\t\t\t\tcom_token[len] = 0;\n\t\t\t\t*data_p = data;\n\t\t\t\treturn com_token;\n\t\t\t}\n\t\t\tif (len < MAX_TOKEN_CHARS)\n\t\t\t{\n\t\t\t\tcom_token[len] = c;\n\t\t\t\tlen++;\n\t\t\t}\n\t\t}\n\t}\n\n// parse a regular word\n\tdo\n\t{\n\t\tif (len < MAX_TOKEN_CHARS)\n\t\t{\n\t\t\tcom_token[len] = c;\n\t\t\tlen++;\n\t\t}\n\t\tdata++;\n\t\tc = *data;\n\t\tif (c == ',' && len && comma)\n\t\t\tbreak;\n\t} while (c>32);\n\n\tif (len == MAX_TOKEN_CHARS)\n\t{\n\t\tCon_DPrintf (\"Token exceeded %i chars, discarded.\\n\", (int)MAX_TOKEN_CHARS);\n\t\tlen = 0;\n\t}\n\tcom_token[len] = 0;\n\n\t*data_p = data;\n\treturn com_token;\n}\n\nstatic float Com_FloatArgument(const char *shadername, char *arg, size_t arglen, float def)\n{\n\tconst char *var;\n\n\t//grab an argument instead, otherwise 0\n\tvar = shadername+1;\n\tif (*shadername)\n\twhile((var = strchr(var, '#')))\n\t{\n\t\tvar++;\n\t\tif (!strnicmp(var, arg, arglen))\n\t\t{\n\t\t\tif (var[arglen] == '=')\n\t\t\t\treturn strtod(var+arglen+1, NULL);\n\t\t\tif (var[arglen] == '#' || !var[arglen])\n\t\t\t\treturn 1;\t//present, but no value\n\t\t}\n\t\tvar++;\n\t}\n\treturn def;\t//not present.\n}\n#define Shader_FloatArgument(s,k) (Com_FloatArgument(s->name,k,strlen(k),0))\n\n\n\n\n\n#define HASH_SIZE\t128\n\n#define SPF_DEFAULT\t\t0u\t/*quake3/fte internal*/\n#define SPF_PROGRAMIFY\t(1u<<0)\t/*automatically replace known glsl, pulling in additional textures+effects from a single primary pass*/\n#define SPF_DOOM3\t\t(1u<<1)\t/*any commands, args, etc, should be interpretted according to doom3's norms*/\n\ntypedef struct shaderparsestate_s\n{\n\tshader_t *s;\t\t//the shader we're parsing\n\tshaderpass_t *pass;\t//the pass we're currently parsing\n\tconst char *ptr;\t//the src file pointer we're at\n\tchar *sourcename;\t//the name of the shader file being read (or '<code>')\n\n\tconst char *forcedshader;\n\tunsigned int parseflags;\t//SPF_*\n\tqboolean droppass;\n\tunsigned int oldflags;\t//shader flags to revert to if the pass is dropped.\n\n\t//for dpwater compat, used to generate a program\n\tint dpwatertype;\n\tfloat reflectmin;\n\tfloat reflectmax;\n\tfloat reflectfactor;\n\tfloat refractfactor;\n\tvec3_t refractcolour;\n\tvec3_t reflectcolour;\n\tfloat wateralpha;\n\n\tchar **saveshaderbody;\n\n\t//FIXME: rtlights can't respond to these\n\tint offsetmappingmode;\n\tfloat offsetmappingscale;\n\tfloat offsetmappingbias;\n\tfloat specularexpscale; //*32 ish\n\tfloat specularvalscale; //*1 ish\n} parsestate_t;\n\ntypedef struct shaderkey_s\n{\n\tchar\t\t\t*keyword;\n\tvoid\t\t\t(*func)( parsestate_t *ps, const char **ptr );\n\tchar\t\t\t*prefix;\n} shaderkey_t;\ntypedef struct shadercachefile_s {\n\tchar *data;\n\tsize_t length;\n\tunsigned int parseflags;\n\tchar forcedshadername[64];\n\tstruct shadercachefile_s *next;\n\tchar name[1];\n} shadercachefile_t;\ntypedef struct shadercache_s {\n\tshadercachefile_t *source;\n\tsize_t offset;\n\tstruct shadercache_s *hash_next;\n\tchar name[1];\n} shadercache_t;\n\nstatic shadercachefile_t *shaderfiles;\t//contents of a .shader file\nstatic shadercache_t **shader_hash;\t\t//locations of known inactive shaders.\n\nunsigned int r_numshaders;\t//number of active slots in r_shaders array.\nunsigned int r_maxshaders;\t//max length of r_shaders array. resized if exceeded.\nshader_t\t**r_shaders;\t//list of active shaders for a id->shader lookup\nstatic hashtable_t shader_active_hash;\t//list of active shaders for a name->shader lookup\nvoid *shader_active_hash_mem;\n\n//static char\t\tr_skyboxname[MAX_QPATH];\n//static float\tr_skyheight;\n\nstatic const char *Shader_Skip(const char *file, const char *shadername, const char *ptr);\nstatic qboolean Shader_Parsetok(parsestate_t *ps, shaderkey_t *keys, const char *token);\nstatic void Shader_ParseFunc(parsestate_t *ps, const char *functype, const char **args, shaderfunc_t *func);\nstatic void Shader_MakeCache(const char *path, unsigned int parseflags);\nstatic qboolean Shader_LocateSource(const char *name, const char **buf, size_t *bufsize, size_t *offset, shadercachefile_t **sourcefile);\nstatic void Shader_ReadShader(parsestate_t *ps, const char *shadersource, shadercachefile_t *sourcefile);\nstatic qboolean Shader_ParseShader(parsestate_t *ps, const char *parsename);\n\n\nstatic struct\n{\n\tvoid *module;\n\tplugmaterialloaderfuncs_t *funcs;\n} *materialloader;\nstatic size_t\t\tmaterialloader_count;\nqboolean Material_RegisterLoader(void *module, plugmaterialloaderfuncs_t *driver)\n{\n\tint i;\n\tif (!driver)\n\t{\n\t\tfor (i = 0; i < materialloader_count; )\n\t\t{\n\t\t\tif (materialloader[i].module == module)\n\t\t\t{\n\t\t\t\tmemmove(&materialloader[i], &materialloader[i+1], materialloader_count-(i+1));\n\t\t\t\tmaterialloader_count--;\n\t\t\t}\n\t\t\telse\n\t\t\t\ti++;\n\t\t}\n\t\treturn true;\n\t}\n\telse\n\t{\n\t\tvoid *n = BZ_Malloc(sizeof(*materialloader)*(materialloader_count+1));\n\t\tmemcpy(n, materialloader, sizeof(*materialloader)*materialloader_count);\n\t\tZ_Free(materialloader);\n\t\tmaterialloader = n;\n\t\tmaterialloader[materialloader_count].module = module;\n\t\tmaterialloader[materialloader_count].funcs = driver;\n\t\tmaterialloader_count++;\n\t\treturn true;\n\t}\n}\n\n//===========================================================================\n\nstatic qboolean Shader_EvaluateCondition(shader_t *shader, const char **ptr)\n{\n\tchar *token;\n\tcvar_t *cv;\n\tfloat lhs;\n\tqboolean conditiontrue = true;\n\ttoken = COM_ParseExt(ptr, false, false);\n\tif (*token == '!')\n\t{\n\t\tconditiontrue = false;\n\t\ttoken++;\n\t\tif (!*token)\n\t\t\ttoken = COM_ParseExt(ptr, false, false);\n\t}\n\n\tif (*token >= '0' && *token <= '9')\n\t\tlhs = strtod(token, NULL);\n\telse\n\t{\n\t\tif (*token == '$')\n\t\t\ttoken++;\n\n\t\tif (*token == '#')\n\t\t\tlhs = !!Shader_FloatArgument(shader, token+1);\n\t\telse if (!Q_stricmp(token, \"lpp\"))\n\t\t\tlhs = r_lightprepass;\n\t\telse if (!Q_stricmp(token, \"lightmap\"))\n\t\t\tlhs = !r_fullbright.value;\n\t\telse if (!Q_stricmp(token, \"deluxmap\") || !Q_stricmp(token, \"deluxe\"))\n\t\t\tlhs = r_deluxemapping;\n\t\telse if (!Q_stricmp(token, \"softwarebanding\"))\n\t\t\tlhs = r_softwarebanding;\n\t\telse if (!Q_stricmp(token, \"unmaskedsky\"))\n\t\t\tlhs = cls.allow_unmaskedskyboxes;\t//can/should skip writing depth values for sky surfaces.\n\n\t\t//normalmaps are generated if they're not already known.\n\t\telse if (!Q_stricmp(token, \"normalmap\"))\n\t\t\tlhs = r_loadbumpmapping;\n\n\t\telse if (!Q_stricmp(token, \"vulkan\"))\n\t\t\tlhs = (qrenderer == QR_VULKAN);\n\t\telse if (!Q_stricmp(token, \"opengl\"))\n\t\t\tlhs = (qrenderer == QR_OPENGL);\n\t\telse if (!Q_stricmp(token, \"d3d8\"))\n\t\t\tlhs = (qrenderer == QR_DIRECT3D8);\n\t\telse if (!Q_stricmp(token, \"d3d9\"))\n\t\t\tlhs = (qrenderer == QR_DIRECT3D9);\n\t\telse if (!Q_stricmp(token, \"d3d11\"))\n\t\t\tlhs = (qrenderer == QR_DIRECT3D11);\n\t\telse if (!Q_stricmp(token, \"gles\"))\n\t\t{\n#ifdef GLQUAKE\n\t\t\tlhs = ((qrenderer == QR_OPENGL) && gl_config.gles);\n#else\n\t\t\tlhs = false;\n#endif\n\t\t}\n\t\telse if (!Q_stricmp(token, \"nofixed\"))\n\t\t\tlhs = sh_config.progs_required;\n\t\telse if (!Q_stricmp(token, \"glsl\"))\n\t\t\tlhs = ((qrenderer == QR_OPENGL) && sh_config.progs_supported);\n\t\telse if (!Q_stricmp(token, \"hlsl\"))\n\t\t\tlhs = ((qrenderer == QR_DIRECT3D9 || qrenderer == QR_DIRECT3D11) && sh_config.progs_supported);\t\n\t\telse if (!Q_stricmp(token, \"haveprogram\"))\n\t\t\tlhs = !!shader->prog;\n\t\telse if (!Q_stricmp(token, \"programs\"))\n\t\t\tlhs = sh_config.progs_supported;\n\t\telse if (!Q_stricmp(token, \"diffuse\"))\n\t\t\tlhs = true;\n\t\telse if (!Q_stricmp(token, \"specular\"))\n\t\t\tlhs = false;\n\t\telse if (!Q_stricmp(token, \"fullbright\"))\n\t\t\tlhs = false;\n\t\telse if (!Q_stricmp(token, \"topoverlay\"))\n\t\t\tlhs = false;\n\t\telse if (!Q_stricmp(token, \"loweroverlay\"))\n\t\t\tlhs = false;\n\n\t\t//these are for compat/documentation purposes with qfusion/warsow\n\t\telse if (!Q_stricmp(token, \"maxTextureSize\"))\n\t\t\tlhs = sh_config.texture2d_maxsize;\n\t\telse if (!Q_stricmp(token, \"maxTextureCubemapSize\"))\n\t\t\tlhs = sh_config.texturecube_maxsize;\n\t\telse if (!Q_stricmp(token, \"maxTextureUnits\"))\n\t\t\tlhs = 0;\n\t\telse if (!Q_stricmp(token, \"textureCubeMap\"))\n\t\t\tlhs = sh_config.havecubemaps;\n//\t\telse if (!Q_stricmp(token, \"GLSL\"))\n//\t\t\tlhs = 1;\n\t\telse if (!Q_stricmp(token, \"deluxeMaps\") || !Q_stricmp(token, \"deluxe\"))\n\t\t\tlhs = r_deluxemapping;\n\t\telse if (!Q_stricmp(token, \"portalMaps\"))\n\t\t\tlhs = false;\n\t\t//end qfusion\n\n\t\telse\n\t\t{\n\t\t\tcv = Cvar_Get(token, \"\", 0, \"Shader Conditions\");\n\t\t\tif (cv)\n\t\t\t{\n\t\t\t\tcv->flags |= CVAR_SHADERSYSTEM;\n\t\t\t\tlhs = cv->value;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tCon_Printf(\"Shader_EvaluateCondition: '%s' is not a cvar\\n\", token);\n\t\t\t\tlhs = 0;\n\t\t\t}\n\t\t}\n\t}\n\tif (*token)\n\t\ttoken = COM_ParseExt(ptr, false, false);\n\tif (*token)\n\t{\n\t\tfloat rhs;\n\t\tchar cmp[4];\n\t\tmemcpy(cmp, token, 4);\n\t\ttoken = COM_ParseExt(ptr, false, false);\n\t\trhs = atof(token);\n\t\tif (!strcmp(cmp, \"!=\"))\n\t\t\tconditiontrue = lhs != rhs;\n\t\telse if (!strcmp(cmp, \"==\"))\n\t\t\tconditiontrue = lhs == rhs;\n\t\telse if (!strcmp(cmp, \"<\"))\n\t\t\tconditiontrue = lhs < rhs;\n\t\telse if (!strcmp(cmp, \"<=\"))\n\t\t\tconditiontrue = lhs <= rhs;\n\t\telse if (!strcmp(cmp, \">\"))\n\t\t\tconditiontrue = lhs > rhs;\n\t\telse if (!strcmp(cmp, \">=\"))\n\t\t\tconditiontrue = lhs >= rhs;\n\t\telse\n\t\t\tconditiontrue = false;\n\t}\n\telse\n\t{\n\t\tconditiontrue = conditiontrue == !!lhs;\n\t}\n\tif (*token)\n\t\ttoken = COM_ParseExt(ptr, false, false);\n\tif (!strcmp(token, \"&&\"))\n\t\treturn Shader_EvaluateCondition(shader, ptr) && conditiontrue;\n\tif (!strcmp(token, \"||\"))\n\t\treturn Shader_EvaluateCondition(shader, ptr) || conditiontrue;\n\n\treturn conditiontrue;\n}\n\nstatic char *Shader_ParseExactString(const char **ptr)\n{\n\tchar *token;\n\n\tif (!ptr || !(*ptr))\n\t\treturn \"\";\n\tif (!**ptr || **ptr == '}')\n\t\treturn \"\";\n\n\ttoken = COM_ParseExt(ptr, false, false);\n\treturn token;\n}\n\nstatic char *Shader_ParseString(const char **ptr)\n{\n\tchar *token;\n\n\tif (!ptr || !(*ptr))\n\t\treturn \"\";\n\twhile(**ptr == ' ' || **ptr == '\\t')\n\t\t*ptr+=1;\n\tif (!**ptr || **ptr == '}')\n\t\treturn \"\";\n\n\ttoken = COM_ParseExt(ptr, false, true);\n\tQ_strlwr ( token );\n\n\treturn token;\n}\n\nstatic char *Shader_ParseSensString(const char **ptr)\n{\n\tchar *token;\n\n\tif (!ptr || !(*ptr))\n\t\treturn \"\";\n\tif (!**ptr || **ptr == '}')\n\t\treturn \"\";\n\n\ttoken = COM_ParseExt(ptr, false, true);\n\n\treturn token;\n}\n\nstatic float Shader_ParseFloat(shader_t *shader, const char **ptr, float defaultval)\n{\n\tchar *token;\n\tif (!ptr || !(*ptr))\n\t\treturn defaultval;\n\tif (!**ptr || **ptr == '}')\n\t\treturn defaultval;\n\n\ttoken = COM_ParseExt(ptr, false, true);\n\tif (*token == '$')\n\t{\n\t\tif (token[1] == '#')\n\t\t{\n\t\t\treturn Shader_FloatArgument(shader, token+2);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tcvar_t *var;\n\t\t\tvar = Cvar_FindVar(token+1);\n\t\t\tif (var)\n\t\t\t{\n\t\t\t\tif (*var->string)\n\t\t\t\t\treturn var->value;\n\t\t\t\telse\n\t\t\t\t\treturn defaultval;\n\t\t\t}\n\t\t}\n\t}\n\tif (!*token)\n\t\treturn defaultval;\n\treturn atof(token);\n}\n\nstatic void Shader_ParseVector(shader_t *shader, const char **ptr, vec3_t v)\n{\n\tconst char *scratch;\n\tchar *token;\n\tqboolean bracket;\n\tqboolean fromcvar = false;\n\n\ttoken = Shader_ParseString(ptr);\n\tif (*token == '$')\n\t{\n\t\tcvar_t *var;\n\t\tvar = Cvar_FindVar(token+1);\n\t\tif (!var)\n\t\t{\n\t\t\tv[0] = 1;\n\t\t\tv[1] = 1;\n\t\t\tv[2] = 1;\n\t\t\treturn;\n\t\t}\n\t\tvar->flags |= CVAR_SHADERSYSTEM;\n\t\tptr = &scratch;\n\t\tscratch = var->string;\n\n\t\ttoken = Shader_ParseString(ptr);\n\t\tfromcvar = true;\n\t}\n\tif (!Q_stricmp (token, \"(\"))\n\t{\n\t\tbracket = true;\n\t\ttoken = Shader_ParseString(ptr);\n\t}\n\telse if (token[0] == '(')\n\t{\n\t\tbracket = true;\n\t\ttoken = &token[1];\n\t}\n\telse\n\t\tbracket = false;\n\n\tif (!strncmp(token, \"0x\", 2))\n\t{\t//0xRRGGBB\n\t\tunsigned int hex = strtoul(token, NULL, 0);\n\t\tv[0] = ((hex>>16)&255)/255.;\n\t\tv[1] = ((hex>> 8)&255)/255.;\n\t\tv[2] = ((hex>> 0)&255)/255.;\n\t\treturn;\n\t}\n\n\tv[0] = atof ( token );\n\t\n\ttoken = Shader_ParseString ( ptr );\n\tif ( !token[0] ) {\n\t\tv[1] = fromcvar?v[0]:0;\n\t} else if (bracket &&  token[strlen(token)-1] == ')' ) {\n\t\tbracket = false;\n\t\ttoken[strlen(token)-1] = 0;\n\t\tv[1] = atof ( token );\n\t} else {\n\t\tv[1] = atof ( token );\n\t}\n\n\ttoken = Shader_ParseString ( ptr );\n\tif ( !token[0] ) {\n\t\tv[2] = fromcvar?v[1]:0;\n\t} else if (bracket && token[strlen(token)-1] == ')' ) {\n\t\ttoken[strlen(token)-1] = 0;\n\t\tv[2] = atof ( token );\n\t} else {\n\t\tv[2] = atof ( token );\n\t\tif ( bracket ) {\n\t\t\tShader_ParseString ( ptr );\n\t\t}\n\t}\n\n\t/*\n\tif (v[0] > 5 || v[1] > 5 || v[2] > 5)\n\t{\n\t\tVectorScale(v, 1.0f/255, v);\n\t}\n\t*/\n}\n\nqboolean Shader_ParseSkySides (char *shadername, char *texturename, texid_t *images)\n{\t//FIXME: use Image_LoadCubemapTextureData to load the faces\n\t//if possible directly use a 7th/cubemap texture instead\n\t//this requires fixing the sky code to not do the random transforms thing though.\n\tqboolean allokay = true;\n\tint i, ss, sp;\n\tchar path[MAX_QPATH];\n\n\tstatic char\t*skyname_suffix[][6] = {\n\t\t{\"rt\", \"bk\", \"lf\", \"ft\", \"up\", \"dn\"},\n//\t\t{\"px\", \"py\", \"nx\", \"ny\", \"pz\", \"nz\"},\n//\t\t{\"posx\", \"posy\", \"negx\", \"negy\", \"posz\", \"negz\"},\n//\t\t{\"_px\", \"_py\", \"_nx\", \"_ny\", \"_pz\", \"_nz\"},\n//\t\t{\"_posx\", \"_posy\", \"_negx\", \"_negy\", \"_posz\", \"_negz\"},\n\t\t{\"_rt\", \"_bk\", \"_lf\", \"_ft\", \"_up\", \"_dn\"}\n\t};\n\n\tstatic char *skyname_pattern[] = {\n\t\t\"%s_%s\",\n\t\t\"%s%s\",\n\t\t\"env/%s%s\",\n\t\t\"gfx/env/%s%s\"\n\t};\n\n\tif (*texturename == '$')\n\t{\n\t\tcvar_t *v;\n\t\tv = Cvar_FindVar(texturename+1);\n\t\tif (v)\n\t\t\ttexturename = v->string;\n\t}\n\tif (!*texturename)\n\t\ttexturename = \"-\";\n\n\tfor ( i = 0; i < 6; i++ )\n\t{\n\t\tif ( texturename[0] == '-' )\n\t\t{\n\t\t\timages[i] = r_nulltex;\n\t\t\tallokay = true;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tfor (sp = 0; sp < sizeof(skyname_pattern)/sizeof(skyname_pattern[0]); sp++)\n\t\t\t{\n\t\t\t\tfor (ss = 0; ss < sizeof(skyname_suffix)/sizeof(skyname_suffix[0]); ss++)\n\t\t\t\t{\n\t\t\t\t\tQ_snprintfz ( path, sizeof(path), skyname_pattern[sp], texturename, skyname_suffix[ss][i] );\n\t\t\t\t\timages[i] = R_LoadHiResTexture ( path, NULL, IF_NOALPHA|IF_CLAMP|IF_LOADNOW);\n\t\t\t\t\tif (images[i]->width)\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tif (images[i]->width)\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (!images[i]->width)\n\t\t\t{\n\t\t\t\tCon_DPrintf(\"Sky \\\"%s\\\" missing texture: %s\\n\", shadername, path);\n\t\t\t\timages[i] = r_blackimage;\n\t\t\t\tallokay = false;\n\t\t\t}\n\t\t}\n\t}\n\treturn allokay;\n}\n\nstatic void Shader_ParseFunc (parsestate_t *ps, const char *functype, const char **ptr, shaderfunc_t *func)\n{\n\tshader_t *shader = ps->s;\n\tchar *token;\n\n\ttoken = Shader_ParseString (ptr);\n\tif (!Q_stricmp (token, \"sin\"))\n\t\tfunc->type = SHADER_FUNC_SIN;\n\telse if (!Q_stricmp (token, \"triangle\"))\n\t\tfunc->type = SHADER_FUNC_TRIANGLE;\n\telse if (!Q_stricmp (token, \"square\"))\n\t\tfunc->type = SHADER_FUNC_SQUARE;\n\telse if (!Q_stricmp (token, \"sawtooth\"))\n\t\tfunc->type = SHADER_FUNC_SAWTOOTH;\n\telse if (!Q_stricmp (token, \"inversesawtooth\"))\n\t\tfunc->type = SHADER_FUNC_INVERSESAWTOOTH;\n\telse if (!Q_stricmp (token, \"noise\"))\n\t\tfunc->type = SHADER_FUNC_NOISE;\n\telse\n\t{\n\t\tif (!Q_stricmp (token, \"distanceramp\"))\t//QFusion\n\t\t\t;\n\t\telse\n\t\t\tCon_Printf(\"%s: %s: unknown %s \\\"%s\\\"\\n\", ps->sourcename, shader->name, functype, token);\n\t\tfunc->type = SHADER_FUNC_CONSTANT;\t//not supported...\n\t\tShader_ParseFloat (shader, ptr, 0);\n\t\tShader_ParseFloat (shader, ptr, 0);\n\t\tShader_ParseFloat (shader, ptr, 0);\n\t\tShader_ParseFloat (shader, ptr, 0);\n\t\tVector4Set(func->args, 255, 255, 255, 255);\n\t\treturn;\n\t}\n\n\tfunc->args[0] = Shader_ParseFloat (shader, ptr, 0);\n\tfunc->args[1] = Shader_ParseFloat (shader, ptr, 0);\n\tfunc->args[2] = Shader_ParseFloat (shader, ptr, 0);\n\tfunc->args[3] = Shader_ParseFloat (shader, ptr, 0);\n}\n\n//===========================================================================\n\nstatic int Shader_SetImageFlags(parsestate_t *parsestate, shaderpass_t *pass, char **name, int flags)\n{\n\t//fixme: pass flags should be handled elsewhere.\n\tfor(;name;)\n\t{\n\t\tif (!Q_strnicmp(*name, \"$rt:\", 4))\n\t\t{\n\t\t\t*name += 4;\n\t\t\tflags |= IF_NOMIPMAP|IF_CLAMP|IF_RENDERTARGET;\n\t\t\tif (!(flags & (IF_NEAREST|IF_LINEAR)))\n\t\t\t\tflags |= IF_LINEAR;\n\t\t}\n\t\telse if (!Q_strnicmp(*name, \"$clamp:\", 7))\n\t\t{\n\t\t\t*name += 7;\n\t\t\tflags |= IF_CLAMP;\n\t\t}\n\t\telse if (!Q_strnicmp(*name, \"$premul:\", 8))\n\t\t{\t//have the engine premultiply textures for you, instead of needing to do it in an editor.\n\t\t\t*name += 8;\n\t\t\tflags |= IF_PREMULTIPLYALPHA;\n\t\t}\n\t\telse if (!Q_strnicmp(*name, \"$2d:\", 4))\n\t\t{\n\t\t\t*name+=4;\n\t\t\tflags = (flags&~IF_TEXTYPEMASK) | IF_TEXTYPE_2D;\n\t\t}\n\t\telse if (!Q_strnicmp(*name, \"$2darray:\", 9))\n\t\t{\n\t\t\t*name+=9;\n\t\t\tflags = (flags&~IF_TEXTYPEMASK) | IF_TEXTYPE_2D_ARRAY;\n\t\t}\n\t\telse if (!Q_strnicmp(*name, \"$3d:\", 4))\n\t\t{\n\t\t\t*name+=4;\n\t\t\tflags = (flags&~IF_TEXTYPEMASK) | IF_TEXTYPE_3D;\n\t\t}\n\t\telse if (!Q_strnicmp(*name, \"$cube:\", 6))\n\t\t{\n\t\t\t*name+=6;\n\t\t\tflags = (flags&~IF_TEXTYPEMASK) | IF_TEXTYPE_CUBE;\n\t\t}\n\t\telse if (!Q_strnicmp(*name, \"$cubearray:\", 11))\n\t\t{\n\t\t\t*name+=11;\n\t\t\tflags = (flags&~IF_TEXTYPEMASK) | IF_TEXTYPE_CUBE_ARRAY;\n\t\t}\n\t\telse if (!Q_strnicmp(*name, \"$srgb:\", 6))\n\t\t{\n\t\t\t*name+=6;\n\t\t\tflags &= ~IF_NOSRGB;\n\t\t\tflags |= IF_SRGB;\n\t\t}\n\t\telse if (!Q_strnicmp(*name, \"$nosrgb:\", 8))\n\t\t{\n\t\t\t*name+=8;\n\t\t\tflags &= ~IF_SRGB;\n\t\t\tflags |= IF_NOSRGB;\n\t\t}\n\t\telse if (!Q_strnicmp(*name, \"$nearest:\", 9))\n\t\t{\n\t\t\t*name+=9;\n\t\t\tflags &= ~IF_LINEAR;\n\t\t\tflags |= IF_NEAREST;\n\t\t}\n\t\telse if (!Q_strnicmp(*name, \"$linear:\", 8))\n\t\t{\n\t\t\t*name+=8;\n\t\t\tflags &= ~IF_NEAREST;\n\t\t\tflags |= IF_LINEAR;\n\t\t}\n\t\telse if (!Q_strnicmp(*name, \"$palettize:\", 11))\n\t\t{\n\t\t\t*name+=11;\n\t\t\tflags |= IF_PALETTIZE;\n\t\t}\n\t\telse\n\t\t\tbreak;\n\t}\n\n//\tif (shader->flags & SHADER_SKY)\n//\t\tflags |= IF_SKY;\n\tif (parsestate->s->flags & SHADER_NOMIPMAPS)\n\t\tflags |= IF_NOMIPMAP;\n\tif (parsestate->s->flags & SHADER_NOPICMIP)\n\t\tflags |= IF_NOPICMIP;\n\tflags |= IF_MIPCAP;\n\n\tif (pass)\n\t{\n\t\tif (flags & IF_CLAMP)\n\t\t\tpass->flags |= SHADER_PASS_CLAMP;\n\t\tif (flags & IF_LINEAR)\n\t\t{\n\t\t\tpass->flags &= ~SHADER_PASS_NEAREST;\n\t\t\tpass->flags |= SHADER_PASS_LINEAR;\n\t\t}\n\t\telse if (flags & IF_NEAREST)\n\t\t{\n\t\t\tpass->flags &= ~SHADER_PASS_LINEAR;\n\t\t\tpass->flags |= SHADER_PASS_NEAREST;\n\t\t}\n\t}\n\n\treturn flags;\n}\n\ntexid_t R_LoadColourmapImage(void)\n{\n\t//FIXME: cache the result, because this is abusive\n\tunsigned int w = 256, h = VID_GRADES;\n\tunsigned int x;\n\tunsigned int data[256*(VID_GRADES)];\n\tqbyte *colourmappal = (qbyte *)FS_LoadMallocFile (\"gfx/colormap.lmp\", NULL);\n#if defined(Q2CLIENT) && defined(IMAGEFMT_PCX)\n\tif (!colourmappal)\n\t{\n\t\tsize_t sz;\n\t\tqbyte *pcx = FS_LoadMallocFile(\"pics/colormap.pcx\", &sz);\n\t\tif (pcx)\n\t\t{\n\t\t\tcolourmappal = Z_Malloc(256*VID_GRADES);\n\t\t\tReadPCXData(pcx, sz, 256, VID_GRADES, colourmappal);\n\t\t\tBZ_Free(pcx);\n\t\t}\n\t}\n#endif\n\tif (colourmappal)\n\t{\n\t\tfor (x = 0; x < sizeof(data)/sizeof(data[0]); x++)\n\t\t\tdata[x] = d_8to24rgbtable[colourmappal[x]];\n\t}\n\telse\n\t{\t//erk\n\t\t//fixme: generate a proper colourmap\n\t\tfor (x = 0; x < sizeof(data)/sizeof(data[0]); x++)\n\t\t{\n\t\t\tint r, g, b;\n\t\t\tfloat l = 1.0-((x/256)/(float)VID_GRADES);\n\t\t\tr = d_8to24rgbtable[x & 0xff];\n\t\t\tg = (r>>16)&0xff;\n\t\t\tb = (r>>8)&0xff;\n\t\t\tr = (r>>0)&0xff;\n\t\t\tdata[x] = d_8to24rgbtable[GetPaletteIndex(r*l,g*l,b*l)];\n\t\t}\n\t}\n\tBZ_Free(colourmappal);\n\treturn R_LoadTexture(\"$colourmap\", w, h, TF_RGBA32, data, IF_NOMIPMAP|IF_NOPICMIP|IF_NEAREST|IF_NOGAMMA|IF_CLAMP);\n}\n\nstatic texid_t Shader_FindImage (parsestate_t *parsestate, char *name, int flags )\n{\n\textern texid_t missing_texture_normal;\n\tif (parsestate->parseflags & SPF_DOOM3)\n\t{\n\t\tif (!Q_stricmp (name, \"_default\"))\n\t\t\treturn r_whiteimage; /*fixme*/\n\t\tif (!Q_stricmp (name, \"_white\"))\n\t\t\treturn r_whiteimage;\n\t\tif (!Q_stricmp (name, \"_black\"))\n\t\t{\n\t\t\tint wibuf[16] = {0};\n\t\t\treturn R_LoadTexture(\"$blackimage\", 4, 4, TF_RGBA32, wibuf, IF_NOMIPMAP|IF_NOPICMIP|IF_NEAREST|IF_NOGAMMA);\n\t\t}\n\t}\n\telse\n\t{\n\t\tif (!Q_stricmp (name, \"$whiteimage\"))\n\t\t\treturn r_whiteimage;\n\t\tif (!Q_stricmp (name, \"$blackimage\"))\n\t\t{\n\t\t\tint wibuf[16] = {0};\n\t\t\treturn R_LoadTexture(\"$blackimage\", 4, 4, TF_RGBA32, wibuf, IF_NOMIPMAP|IF_NOPICMIP|IF_NEAREST|IF_NOGAMMA);\n\t\t}\n\t\tif (!Q_stricmp (name, \"$identitynormal\"))\n\t\t\treturn missing_texture_normal;\n\t\tif (!Q_stricmp (name, \"$colourmap\"))\n\t\t\treturn R_LoadColourmapImage();\n\t}\n\tif (flags & IF_RENDERTARGET)\n\t\treturn R2D_RT_Configure(name, 0, 0, TF_INVALID, flags);\n\treturn R_LoadHiResTexture(name, NULL, flags);\n}\n\n\n/****************** shader keyword functions ************************/\n\nstatic void Shader_Cull (parsestate_t *ps, const char **ptr)\n{\n\tshader_t *shader = ps->s;\n\tchar *token;\n\n\tshader->flags &= ~(SHADER_CULL_FRONT|SHADER_CULL_BACK);\n\n\ttoken = Shader_ParseString ( ptr );\n\tif ( !Q_stricmp (token, \"disable\") || !Q_stricmp (token, \"none\") || !Q_stricmp (token, \"twosided\") ) {\n\t} else if ( !Q_stricmp (token, \"front\") ) {\n\t\tshader->flags |= SHADER_CULL_FRONT;\n\t} else if ( !Q_stricmp (token, \"back\") || !Q_stricmp (token, \"backside\") || !Q_stricmp (token, \"backsided\") ) {\n\t\tshader->flags |= SHADER_CULL_BACK;\n\t} else {\n\t\tshader->flags |= SHADER_CULL_FRONT;\n\t}\n}\n\nstatic void Shader_NoMipMaps (parsestate_t *ps, const char **ptr)\n{\n\tshader_t *shader = ps->s;\n\tshader->flags |= (SHADER_NOMIPMAPS|SHADER_NOPICMIP);\n}\n\nstatic void Shader_Affine (parsestate_t *ps, const char **ptr)\n{\n\tshader_t *shader = ps->s;\n\tint i;\n\tfor (i = 0; i < countof(shader->passes); i++)\n\t\tshader->passes[i].shaderbits |= SBITS_AFFINE;\n}\nstatic void Shader_FullRate (parsestate_t *ps, const char **ptr)\n{\n\tshader_t *shader = ps->s;\n\tint i;\n\tfor (i = 0; i < countof(shader->passes); i++)\n\t\tshader->passes[i].shaderbits |= SBITS_MISC_FULLRATE;\n}\n\nstatic void Shader_NoPicMip (parsestate_t *ps, const char **ptr)\n{\n\tshader_t *shader = ps->s;\n\tshader->flags |= SHADER_NOPICMIP;\n}\n\nstatic void Shader_DeformVertexes (parsestate_t *ps, const char **ptr)\n{\n\tshader_t *shader = ps->s;\n\tchar *token;\n\tdeformv_t *deformv;\n\n\tif ( shader->numdeforms >= SHADER_DEFORM_MAX )\n\t\treturn;\n\n\tdeformv = &shader->deforms[shader->numdeforms];\n\n\tshader->flags |= SHADER_NOMARKS;\t//just in case...\n\n\ttoken = Shader_ParseString ( ptr );\n\tif ( !Q_stricmp (token, \"wave\") )\n\t{\n\t\tdeformv->type = DEFORMV_WAVE;\n\t\tdeformv->args[0] = Shader_ParseFloat (shader, ptr, 0);\n\t\tif (deformv->args[0])\n\t\t\tdeformv->args[0] = 1.0f / deformv->args[0];\n\t\tShader_ParseFunc (ps, \"deformvertexes wave\", ptr, &deformv->func );\n\t}\n\telse if ( !Q_stricmp (token, \"normal\") )\n\t{\n\t\tdeformv->type = DEFORMV_NORMAL;\n\t\tdeformv->args[0] = Shader_ParseFloat (shader, ptr, 0);\n\t\tdeformv->args[1] = Shader_ParseFloat (shader, ptr, 0);\n\t}\n\telse if ( !Q_stricmp (token, \"bulge\") )\n\t{\n\t\tdeformv->type = DEFORMV_BULGE;\n\t\tShader_ParseVector (shader, ptr, deformv->args);\n\t}\n\telse if ( !Q_stricmp (token, \"move\") )\n\t{\n\t\tdeformv->type = DEFORMV_MOVE;\n\t\tShader_ParseVector (shader, ptr, deformv->args );\n\t\tShader_ParseFunc (ps, \"deformvertexes move\", ptr, &deformv->func );\n\t}\n\telse if ( !Q_stricmp (token, \"autosprite\") )\n\t{\n\t\tdeformv->type = DEFORMV_AUTOSPRITE;\n\t}\n\telse if ( !Q_stricmp (token, \"autosprite2\") )\n\t{\n\t\tdeformv->type = DEFORMV_AUTOSPRITE2;\n\t}\n\telse if ( !Q_stricmp (token, \"projectionShadow\") )\n\t\tdeformv->type = DEFORMV_PROJECTION_SHADOW;\n\telse if ( !Q_strnicmp (token, \"text\", 4) )\n\t{\n\t\tdeformv->type = DEFORMV_TEXT;\n\t\tdeformv->args[0] = atoi(token+4);\n\t}\n\telse\n\t\treturn;\n\n\tshader->numdeforms++;\n}\n\nstatic void Shader_ClutterParms(parsestate_t *ps, const char **ptr)\n{\n\tshader_t *shader = ps->s;\n\tstruct shader_clutter_s *clut;\n\tchar *modelname;\n\n\tmodelname = Shader_ParseString(ptr);\n\tclut = Z_Malloc(sizeof(*clut) + strlen(modelname));\n\tstrcpy(clut->modelname, modelname);\n\tclut->spacing\t= Shader_ParseFloat(shader, ptr, 1000);\n\tclut->scalemin\t= Shader_ParseFloat(shader, ptr, 1);\n\tclut->scalemax\t= Shader_ParseFloat(shader, ptr, 1);\n\tclut->zofs\t\t= Shader_ParseFloat(shader, ptr, 0);\n\tclut->anglemin\t= Shader_ParseFloat(shader, ptr, 0) * M_PI * 2 / 360.;\n\tclut->anglemax\t= Shader_ParseFloat(shader, ptr, 360) * M_PI * 2 / 360.;\n\n\tclut->next = shader->clutter;\n\tshader->clutter = clut;\n}\n\nstatic void Shader_SkyParms(parsestate_t *ps, const char **ptr)\n{\n\tshader_t *shader = ps->s;\n\tskydome_t *skydome;\n//\tfloat skyheight;\n\tchar *boxname;\n\n\tif (shader->skydome)\n\t{\n\t\tZ_Free(shader->skydome);\n\t}\n\n\tskydome = (skydome_t *)Z_Malloc(sizeof(skydome_t));\n\tshader->skydome = skydome;\n\n\tboxname = Shader_ParseString(ptr);\n\tShader_ParseSkySides(shader->name, boxname, skydome->farbox_textures);\n\n\t/*skyheight =*/ Shader_ParseFloat(shader, ptr, 512);\n\n\tboxname = Shader_ParseString(ptr);\n//\tShader_ParseSkySides(shader->name, boxname, skydome->nearbox_textures);\n\n\tshader->flags |= SHADER_SKY;\n\tshader->sort = SHADER_SORT_SKY;\n}\n\nstatic void Shader_FogParms (parsestate_t *ps, const char **ptr)\n{\n\tshader_t *shader = ps->s;\n\tfloat div;\n\tvec3_t color, fcolor;\n\n//\tif ( !r_ignorehwgamma->value )\n//\t\tdiv = 1.0f / pow(2, max(0, floor(r_overbrightbits->value)));\n//\telse\n\t\tdiv = 1.0f;\n\n\tShader_ParseVector (shader, ptr, color );\n\tVectorScale ( color, div, color );\n\tColorNormalize ( color, fcolor );\n\n\tshader->fog_color[0] = FloatToByte ( fcolor[0] );\n\tshader->fog_color[1] = FloatToByte ( fcolor[1] );\n\tshader->fog_color[2] = FloatToByte ( fcolor[2] );\n\tshader->fog_color[3] = 255;\n\tshader->fog_dist = Shader_ParseFloat (shader, ptr, 128);\n\n\tif ( shader->fog_dist <= 0.0f ) {\n\t\tshader->fog_dist = 128.0f;\n\t}\n\tshader->fog_dist = 1.0f / shader->fog_dist;\n\n\tshader->flags |= SHADER_NODLIGHT|SHADER_NOSHADOWS;\n}\n\nstatic void Shader_SurfaceParm (parsestate_t *ps, const char **ptr)\n{\n\tshader_t *shader = ps->s;\n\tchar *token;\n\n\ttoken = Shader_ParseString ( ptr );\n\tif ( !Q_stricmp( token, \"nodraw\" ) )\n\t\tshader->flags |= SHADER_NODRAW;\n\telse if ( !Q_stricmp( token, \"nodraw2\" ) )\n\t\tshader->flags |= SHADER_NODRAW;\t//an alternative so that q3map2 won't see+strip it.\n\telse if ( !Q_stricmp( token, \"nodlight\" ) )\n\t\tshader->flags |= SHADER_NODLIGHT;\n\telse if ( !Q_stricmp( token, \"noshadows\" ) )\n\t\tshader->flags |= SHADER_NOSHADOWS;\n\n\telse if ( !Q_stricmp( token, \"sky\" ) )\n\t\tshader->flags |= SHADER_SKY;\n\n\telse if ( !Q_stricmp( token, \"noimpact\" ) )\n\t\tshader->flags |= SHADER_NOMARKS;\t//wrong, but whatever.\n\telse if ( !Q_stricmp( token, \"nomarks\" ) )\n\t\tshader->flags |= SHADER_NOMARKS;\n\n\t//forceshader type things inherit certain textures from the original material\n\t//however, that original material might not need those textures and thus won't have them loaded, which breaks replacement.\n\t//these provide a way to override that.\n\telse if (!Q_stricmp( token, \"hasdiffuse\"))\n\t\tshader->flags |= SHADER_HASDIFFUSE;\n\telse if (!Q_stricmp( token, \"hasnormalmap\"))\n\t\tshader->flags |= SHADER_HASNORMALMAP;\n\telse if (!Q_stricmp( token, \"hasgloss\"))\n\t\tshader->flags |= SHADER_HASGLOSS;\n\telse if (!Q_stricmp( token, \"hasfullbright\"))\n\t\tshader->flags |= SHADER_HASFULLBRIGHT;\n\telse if (!Q_stricmp( token, \"haspaletted\"))\n\t\tshader->flags |= SHADER_HASPALETTED;\n\telse if (!Q_stricmp(token, \"hastop\") || !Q_stricmp(token, \"hasbottom\") || !Q_stricmp(token, \"hastopbottom\"))\n\t\tshader->flags |= SHADER_HASTOPBOTTOM;\n\telse\n\t\tCon_DLPrintf(2, \"Shader %s, Unknown surface parm \\\"%s\\\"\\n\", ps->s->name, token);\t//note that there are game-specific names used to override mod surfaceflags+contents\n}\n\nstatic void Shader_DP_Sort (parsestate_t *ps, const char **ptr)\n{\n\tshader_t *shader = ps->s;\n\tchar *token;\n\n\ttoken = Shader_ParseString ( ptr );\n\n\tif (!Q_stricmp(token, \"sky\"))\n\t\tshader->sort = SHADER_SORT_SKY;\n\telse if (!Q_stricmp(token, \"hud\"))\n\t\tshader->sort = SHADER_SORT_NEAREST;\n//\telse if (!Q_stricmp(token, \"distance\"))\n//\t\tshader->sort = SHADER_SORT_NONE;\t//not really immplemented, could maybe force v_depthsortentities. just let q3 rules take over.\n}\n\nstatic void Shader_Sort (parsestate_t *ps, const char **ptr)\n{\n\tshader_t *shader = ps->s;\n\tchar *token;\n\n\ttoken = Shader_ParseString ( ptr );\n\tif (r_forceprogramify.ival >= 2)\n\t{\n\t\tCon_DPrintf(\"Shader %s, ignoring 'sort %s'\\n\", ps->s->name, token);\n\t\treturn;\t//dp ignores 'sort' entirely.\n\t}\n//\telse if ( !Q_stricmp( token, \"none\" ) )\n//\t\tshader->sort = SHADER_SORT_NONE;\t//default, overwritten with an automatic choice.\n\telse if ( !Q_stricmp( token, \"ripple\" ) )\t//fte, weird. drawn only to the ripplemap.\n\t\tshader->sort = SHADER_SORT_RIPPLE;\n\telse if ( !Q_stricmp( token, \"deferredlight\" ) )\t//fte, weird. drawn only to prelight buffer.\n\t\tshader->sort = SHADER_SORT_DEFERREDLIGHT;\n\telse if ( !Q_stricmp( token, \"portal\" ) )\n\t\tshader->sort = SHADER_SORT_PORTAL;\n\telse if( !Q_stricmp( token, \"sky\" ) )\n\t\tshader->sort = SHADER_SORT_SKY;\n\telse if( !Q_stricmp( token, \"opaque\" ) )\n\t\tshader->sort = SHADER_SORT_OPAQUE;\n\telse if( !Q_stricmp( token, \"decal\" ) || !Q_stricmp( token, \"litdecal\" ) )\n\t\tshader->sort = SHADER_SORT_DECAL;\n\telse if( !Q_stricmp( token, \"seethrough\" ) )\n\t\tshader->sort = SHADER_SORT_SEETHROUGH;\n\telse if( !Q_stricmp( token, \"unlitdecal\" ) )\n\t\tshader->sort = SHADER_SORT_UNLITDECAL;\n\telse if( !Q_stricmp( token, \"banner\" ) )\n\t\tshader->sort = SHADER_SORT_BANNER;\n\telse if( !Q_stricmp( token, \"underwater\" ) )\n\t\tshader->sort = SHADER_SORT_UNDERWATER;\n\telse if( !Q_stricmp( token, \"blend\" ))\n\t\tshader->sort = SHADER_SORT_BLEND;\n\telse if( !Q_stricmp( token, \"additive\" ) )\n\t\tshader->sort = SHADER_SORT_ADDITIVE;\n\telse if( !Q_stricmp( token, \"nearest\" ) )\n\t\tshader->sort = SHADER_SORT_NEAREST;\n\telse\n\t{\n\t\tint q3 = atoi ( token );\n\t\tshadersort_t q3sorttofte[] =\n\t\t{\n\t\t\t/* 0*/SHADER_SORT_NONE,\n\t\t\t/* 1*/SHADER_SORT_PORTAL,\n\t\t\t/* 2*/SHADER_SORT_SKY,\t\t//aka environment in q3\n\t\t\t/* 3*/SHADER_SORT_OPAQUE,\n\t\t\t/* 4*/SHADER_SORT_DECAL,\n\t\t\t/* 5*/SHADER_SORT_SEETHROUGH,\n\t\t\t/* 6*/SHADER_SORT_BANNER,\n\t\t\t/* 7*/SHADER_SORT_UNDERWATER/*SHADER_SORT_FOG*/,\n\t\t\t/* 8*/SHADER_SORT_UNDERWATER,\n\t\t\t/* 9*/SHADER_SORT_BLEND,\t\t//blend0 in q3\n\t\t\t/*10*/SHADER_SORT_ADDITIVE,\t//blend1 in q3\n\t\t\t/*11*/SHADER_SORT_ADDITIVE/*SHADER_SORT_BLEND2*/,\n\t\t\t/*12*/SHADER_SORT_ADDITIVE/*SHADER_SORT_BLEND3*/,\n\t\t\t/*13*/SHADER_SORT_ADDITIVE/*SHADER_SORT_BLEND6*/, //yes, 4+5 missing in q3...\n\t\t\t/*14*/SHADER_SORT_ADDITIVE/*SHADER_SORT_STENCIL*/,\n\t\t\t/*15*/SHADER_SORT_NEAREST/*SHADER_SORT_ALMOSTNEAREST*/,\n\t\t\t/*16*/SHADER_SORT_NEAREST\n\t\t};\n\t\tif (q3 >= 0 && q3 < countof(q3sorttofte))\n\t\t\tshader->sort = q3sorttofte[q3];\n\t\telse\n\t\t\tshader->sort = SHADER_SORT_NONE;\t// :(\n\t\tclamp ( shader->sort, SHADER_SORT_NONE, SHADER_SORT_NEAREST );\n\t}\n}\n\nstatic void Shader_Deferredlight (parsestate_t *ps, const char **ptr)\n{\n\tshader_t *shader = ps->s;\n\tshader->sort = SHADER_SORT_DEFERREDLIGHT;\n}\n\nstatic void Shader_Portal (parsestate_t *ps, const char **ptr)\n{\n\tshader_t *shader = ps->s;\n\tshader->sort = SHADER_SORT_PORTAL;\n}\n\nstatic void Shader_PolygonOffset (parsestate_t *ps, const char **ptr)\n{\n\tshader_t *shader = ps->s;\n\tfloat m = Shader_ParseFloat(shader, ptr, 1);\n\n\tshader->polyoffset.unit = -25 * m;\n\tshader->polyoffset.factor = -0.05;\n\tshader->flags |= SHADER_POLYGONOFFSET;\t//some backends might be lazy and only allow simple values.\n}\n\nstatic void Shader_EntityMergable (parsestate_t *ps, const char **ptr)\n{\n\tshader_t *shader = ps->s;\n\tshader->flags |= SHADER_ENTITY_MERGABLE;\n}\n\n#if defined(GLQUAKE) || defined(D3DQUAKE)\nstatic qboolean Shader_ParseProgramCvar(char *script, char **cvarnames, int *cvartypes, int cvartype)\n{\n\tcvar_t *ref;\n\tchar body[MAX_QPATH];\n\tchar *out;\n\tchar *namestart;\n\twhile (*script == ' ' || *script == '\\t')\n\t\tscript++;\n\tnamestart = script;\n\twhile ((*script >= 'A' && *script <= 'Z') || (*script >= 'a' && *script <= 'z') || (*script >= '0' && *script <= '9') || *script == '_')\n\t\tscript++;\n\n\tcvartypes[0] = cvartype;\n\tcvarnames[0] = Z_Malloc(script - namestart + 1);\n\tmemcpy(cvarnames[0], namestart, script - namestart);\n\tcvarnames[0][script - namestart] = 0;\n\n\twhile (*script == ' ' || *script == '\\t')\n\t\tscript++;\n\tif (*script == '=')\n\t{\n\t\tscript++;\n\t\twhile (*script == ' ' || *script == '\\t')\n\t\t\tscript++;\n\n\t\tout = body;\n\t\twhile (out < com_token+countof(body)-1 && *script != '\\n' && !(script[0] == '/' && script[1] == '/')) \n\t\t\t*out++ = *script++;\n\t\t*out++ = 0;\n\t\tref = Cvar_Get(cvarnames[0], body, 0, \"GLSL Variables\");\n\t}\n\telse\n\t\tref = Cvar_Get(cvarnames[0], \"\", 0, \"GLSL Variables\");\n\tif (!ref)\n\t{\n\t\tZ_Free(cvarnames[0]);\n\t\treturn false;\n\t}\n\treturn true;\n}\nstatic qboolean Shader_ParseSemantic(const char *script, const char *shadername, char **cvarnames, int *cvartypes)\n{\n\tint s;\n\tconst char *namestart, *nameend;\n\twhile (*script == ' ' || *script == '\\t')\n\t\tscript++;\n\tnamestart = script;\n\twhile ((*script >= 'A' && *script <= 'Z') || (*script >= 'a' && *script <= 'z') || (*script >= '0' && *script <= '9') || *script == '_')\n\t\tscript++;\n\tnameend = script;\n\tif (*script == ' ' || *script == '\\t')\n\t{\n\t\twhile (*script == ' ' || *script == '\\t')\n\t\t\tscript++;\n\t\twhile ((*script >= 'A' && *script <= 'Z') || (*script >= 'a' && *script <= 'z') || (*script >= '0' && *script <= '9') || *script == '_')\n\t\t\tscript++;\n\t}\n\telse\n\t\treturn false;\n\n\tcvarnames[0] = Z_Malloc(script - namestart + 1);\n\tmemcpy(cvarnames[0], namestart, script - namestart);\n\tcvarnames[0][script - namestart] = 0;\n\n\tcvarnames[0][nameend-namestart] = 0;\n\tnameend = &cvarnames[0][nameend-namestart]+1;\n\n\tfor (s = 0; shader_unif_names[s].name; s++)\n\t{\n\t\tif (!strcmp(shader_unif_names[s].name, nameend))\n\t\t{\n\t\t\tcvartypes[0] = shader_unif_names[s].ptype;\n\t\t\treturn true;\n\t\t}\n\t}\n\tCon_Printf(\"%s: semantic %s not found\\n\", shadername, nameend);\n\tZ_Free(cvarnames[0]);\n\treturn false;\n}\nstatic qboolean Shader_ParseProgramConst(char *script, char **cvarnames, int *cvartypes, int cvartype, unsigned short *numsamplers)\n{\n\tchar *namestart;\n\tchar *nameend;\n\twhile (*script == ' ' || *script == '\\t')\n\t\tscript++;\n\tnamestart = script;\n\twhile ((*script >= 'A' && *script <= 'Z') || (*script >= 'a' && *script <= 'z') || (*script >= '0' && *script <= '9') || *script == '_')\n\t\tscript++;\n\tnameend = script;\n\tif (*script == ' ' || *script == '\\t')\n\t{\n\t\twhile (*script != '\\n')\n\t\t\tscript++;\n\t}\n\n\tcvartypes[0] = cvartype;\n\tcvarnames[0] = Z_Malloc(script - namestart + 1);\n\tmemcpy(cvarnames[0], namestart, script - namestart);\n\tcvarnames[0][script - namestart] = 0;\n\n\t//not a cvar. data is baked weirdly into the name.\n\tif (nameend < script)\n\t{\n\t\tcvarnames[0][nameend-namestart] = '=';\n\n\t\tif (numsamplers)\n\t\t{\t//this is a !!constt\n\t\t\t//make sure we know the max sampler id needed...\n\t\t\tunsigned short s;\n\t\t\tnameend = &cvarnames[0][nameend-namestart]+1;\n\t\t\twhile (*nameend == ' ' || *nameend == '\\t')\n\t\t\t\tnameend++;\n\t\t\ts = atoi(nameend)+1;\n\t\t\tif (*numsamplers < s)\n\t\t\t\t*numsamplers = s;\n\t\t}\n\t}\n\treturn true;\n}\n#endif\n\nconst struct sh_defaultsamplers_s sh_defaultsamplers[] =\n{\n\t{\"s_shadowmap\",\t\t1u<<S_SHADOWMAP},\n\t{\"s_projectionmap\",\t1u<<S_PROJECTIONMAP},\n\t{\"s_diffuse\",\t\t1u<<S_DIFFUSE},\n\t{\"s_normalmap\",\t\t1u<<S_NORMALMAP},\n\t{\"s_specular\",\t\t1u<<S_SPECULAR},\n\t{\"s_upper\",\t\t\t1u<<S_UPPERMAP},\n\t{\"s_lower\",\t\t\t1u<<S_LOWERMAP},\n\t{\"s_fullbright\",\t1u<<S_FULLBRIGHT},\n\t{\"s_paletted\",\t\t1u<<S_PALETTED},\n\t{\"s_reflectcube\",\t1u<<S_REFLECTCUBE},\n\t{\"s_reflectmask\",\t1u<<S_REFLECTMASK},\n\t{\"s_displacement\",\t1u<<S_DISPLACEMENT},\n\t{\"s_occlusion\",\t\t1u<<S_OCCLUSION},\n\t{\"s_transmission\",\t1u<<S_TRANSMISSION},\n\t{\"s_thickness\",\t\t1u<<S_THICKNESS},\n\t{\"s_lightmap\",\t\t1u<<S_LIGHTMAP0},\n\t{\"s_deluxemap\",\t\t1u<<S_DELUXEMAP0},\n#if MAXRLIGHTMAPS > 1\n\t{\"s_lightmap1\",\t\t1u<<S_LIGHTMAP1},\n\t{\"s_lightmap2\",\t\t1u<<S_LIGHTMAP2},\n\t{\"s_lightmap3\",\t\t1u<<S_LIGHTMAP3},\n\t{\"s_deluxemap1\",\t1u<<S_DELUXEMAP1},\n\t{\"s_deluxemap2\",\t1u<<S_DELUXEMAP2},\n\t{\"s_deluxemap3\",\t1u<<S_DELUXEMAP3},\n#else\n\t{\"s_lightmap1\",\t\t0},\n\t{\"s_lightmap2\",\t\t0},\n\t{\"s_lightmap3\",\t\t0},\n\t{\"s_deluxemap1\",\t0},\n\t{\"s_deluxemap2\",\t0},\n\t{\"s_deluxemap3\",\t0},\n#endif\n\t{NULL}\n};\n\nstatic struct\n{\n\tchar *name;\n\tunsigned int bitmask;\n} permutations[] =\n{\n\t{\"BUMP\", PERMUTATION_BUMPMAP},\n\t{\"FULLBRIGHT\", PERMUTATION_FULLBRIGHT},\n\t{\"UPPERLOWER\", PERMUTATION_UPPERLOWER},\n\t{\"REFLECTCUBEMASK\", PERMUTATION_REFLECTCUBEMASK},\n\t{\"SKELETAL\", PERMUTATION_SKELETAL},\n\t{\"FOG\", PERMUTATION_FOG},\n\t{\"FRAMEBLEND\", PERMUTATION_FRAMEBLEND},\n\t{\"LIGHTSTYLED\", PERMUTATION_LIGHTSTYLES}\n};\n#define MAXMODIFIERS 64\n\nvoid VARGS Q_strlcatfz (char *dest, size_t *offset, size_t size, const char *fmt, ...) LIKEPRINTF(4);\nvoid VARGS Q_strlcatfz (char *dest, size_t *offset, size_t size, const char *fmt, ...)\n{\n\tva_list\t\targptr;\n\n\tdest += *offset;\n\tsize -= *offset;\n\n\tva_start (argptr, fmt);\n\tQ_vsnprintfz(dest, size, fmt, argptr);\n\tva_end (argptr);\n\t*offset += strlen(dest);\n}\nstruct programpermu_s *Shader_LoadPermutation(program_t *prog, unsigned int p)\n{\n\tconst char *permutationdefines[3];\n\tstruct programpermu_s *pp;\n\tsize_t n, pn = 0;\n\tchar defines[8192];\n\tsize_t offset;\n\tqboolean fail = false;\n\n\textern cvar_t r_glsl_pbr, gl_specular, gl_specular_power;\n\n\tif (~prog->supportedpermutations & p)\n\t\treturn NULL;\t//o.O\n\tpp = Z_Malloc(sizeof(*pp));\n\tpp->permutation = p;\n\t*defines = 0;\n\toffset = 0;\n\tif (p & PERMUTATION_SKELETAL)\n\t\tQ_strlcatfz(defines, &offset, sizeof(defines), \"#define MAX_GPU_BONES %i\\n\", sh_config.max_gpu_bones);\n\tif (gl_specular.value)\n\t\tQ_strlcatfz(defines, &offset, sizeof(defines), \"#define SPECULAR\\n#define SPECULAR_BASE_MUL %f\\n#define SPECULAR_BASE_POW %f\\n\", 1.0*gl_specular.value, max(1,gl_specular_power.value));\n\tif (r_glsl_pbr.ival)\n\t\tQ_strlcatfz(defines, &offset, sizeof(defines), \"#define PBR\\n\");\n#ifdef RTLIGHTS\n\tif (r_fakeshadows)\n\t\tQ_strlcatfz(defines, &offset, sizeof(defines), \"#define FAKESHADOWS\\n%s\",\n#ifdef GLQUAKE\n\t\t\t\tgl_config.arb_shadow?\"#define USE_ARB_SHADOW\\n\":\n#endif\n\t\t\t\t\"\");\n#endif\n\n\tfor (n = 0; n < countof(permutations); n++)\n\t{\n\t\tif (p & permutations[n].bitmask)\n\t\t\tQ_strlcatfz(defines, &offset, sizeof(defines), \"#define %s\\n\", permutations[n].name);\n\t}\n\tif (p & PERMUTATION_UPPERLOWER)\n\t\tQ_strlcatfz(defines, &offset, sizeof(defines), \"#define UPPER\\n#define LOWER\\n\");\n\tif (p & PERMUTATION_BUMPMAP)\n\t{\n\t\tif (r_glsl_offsetmapping.ival)\n\t\t{\n\t\t\tQ_strlcatfz(defines, &offset, sizeof(defines), \"#define OFFSETMAPPING\\n\");\n\t\t\tif (r_glsl_offsetmapping_reliefmapping.ival && (p & PERMUTATION_BUMPMAP))\n\t\t\t\tQ_strlcatfz(defines, &offset, sizeof(defines), \"#define RELIEFMAPPING\\n\");\n\t\t}\n\n\t\tif (r_deluxemapping)\t//fixme: should be per-model really\n\t\t\tQ_strlcatfz(defines, &offset, sizeof(defines), \"#define DELUXE\\n\");\n\t}\n\tpermutationdefines[pn++] = defines;\n\tpermutationdefines[pn++] = prog->preshade;\n\tpermutationdefines[pn++] = NULL;\n\n\tif (!sh_config.pCreateProgram(prog, pp, prog->shaderver, permutationdefines, prog->shadertext, prog->tess?prog->shadertext:NULL, prog->tess?prog->shadertext:NULL, prog->geom?prog->shadertext:NULL, prog->shadertext, prog->warned, NULL))\n\t\tprog->warned = fail = true;\n\n\t//extra loop to validate the programs actually linked properly.\n\t//delaying it like this gives certain threaded drivers a chance to compile them all while we're messing around with other junk\n\tif (!fail && sh_config.pValidateProgram && !sh_config.pValidateProgram(prog, pp, prog->warned, NULL))\n\t\tprog->warned = fail = true;\n\n\tif (!fail && sh_config.pProgAutoFields)\n\t{\n\t\tchar *cvarnames[64+1];\n\t\tint cvartypes[64];\n\n\t\tunsigned char *cvardata = prog->cvardata;\n\t\tsize_t size = prog->cvardatasize, i;\n\t\tfor (i = 0; i < countof(cvartypes) && size; i++)\n\t\t{\n\t\t\tmemcpy(&cvartypes[i], cvardata, sizeof(int));\n\t\t\tcvarnames[i] = cvardata+sizeof(int);\n\t\t\tsize -= sizeof(int)+strlen(cvarnames[i])+1;\n\t\t\tcvardata += sizeof(int)+strlen(cvarnames[i])+1;\n\t\t}\n\t\tcvarnames[i] = NULL; //no more\n\t\tsh_config.pProgAutoFields(prog, pp, cvarnames, cvartypes);\n\t}\n\tif (fail)\n\t{\n\t\tZ_Free(pp);\n\t\treturn NULL;\n\t}\n\treturn pp;\n}\n\nqboolean Shader_PermutationEnabled(unsigned int bit)\n{\n\tif (bit == PERMUTATION_REFLECTCUBEMASK)\n\t\treturn gl_load24bit.ival;\n\tif (bit == PERMUTATION_BUMPMAP)\n\t\treturn r_loadbumpmapping;\n\treturn true;\n}\nqboolean Com_PermuOrFloatArgument(const char *shadername, char *arg, size_t arglen, float def)\n{\n\textern cvar_t gl_specular;\n\tsize_t p;\n\t//load-time-only permutations...\n\tif (arglen == 8 && !strncmp(\"SPECULAR\", arg, arglen) && gl_specular.value)\n\t\treturn true;\n#ifdef RTLIGHTS\n\tif (arglen == 11 && !strncmp(\"FAKESHADOWS\", arg, arglen) && r_fakeshadows)\n\t\treturn true;\n#endif\n\tif ((arglen==5||arglen==6) && !strncmp(\"DELUXE\", arg, arglen) && r_deluxemapping && Shader_PermutationEnabled(PERMUTATION_BUMPMAP))\n\t\treturn true;\n\tif (arglen == 13 && !strncmp(\"OFFSETMAPPING\", arg, arglen) && r_glsl_offsetmapping.ival)\n\t\treturn true;\n\tif (arglen == 13 && !strncmp(\"RELIEFMAPPING\", arg, arglen) && r_glsl_offsetmapping.ival && r_glsl_offsetmapping_reliefmapping.ival)\n\t\treturn true;\n\n\t//real permutations\n\tif (arglen == 5 && (!strncmp(\"UPPER\", arg, arglen)||!strncmp(\"LOWER\", arg, arglen)) && Shader_PermutationEnabled(PERMUTATION_BIT_UPPERLOWER))\n\t\treturn true;\n\tfor (p = 0; p < countof(permutations); p++)\n\t{\n\t\tif (arglen == strlen(permutations[p].name) && !strncmp(permutations[p].name, arg, arglen))\n\t\t{\n\t\t\tif (Shader_PermutationEnabled(permutations[p].bitmask))\n\t\t\t\treturn true;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\treturn Com_FloatArgument(shadername, arg, arglen, def) != 0;\n}\n\nstatic qboolean Shader_LoadPermutations(char *name, program_t *prog, char *script, int qrtype, int ver, char *blobfilename)\n{\n#if defined(GLQUAKE) || defined(D3DQUAKE)\n//\tconst char *permutationdefines[countof(permutations) + MAXMODIFIERS + 1];\n\tunsigned int nopermutation = PERMUTATIONS-1;\n//\tint nummodifiers = 0;\n\tint p;\n\tchar *end;\n\n\tchar *cvarnames[64];\n\tint cvartypes[64];\n\tsize_t cvarcount = 0, i;\n\textern cvar_t gl_specular, gl_specular_power;\n\tqboolean cantess = false;\t//not forced.\n\tchar prescript[8192];\n\tsize_t offset = 0;\n#endif\n\n\tif (qrenderer != qrtype)\n\t{\n\t\treturn false;\n\t}\n\n#ifdef VKQUAKE\n\tif (qrenderer == QR_VULKAN && qrtype == QR_VULKAN)\n\t{\t//vulkan 'scripts' are just blobs. could maybe base64 the spirv but eww.\n\t\treturn VK_LoadBlob(prog, script, name);\n\t}\n#endif\n\n#if defined(GLQUAKE) || defined(D3DQUAKE)\n\tver = 0;\n\n\tif (!sh_config.pCreateProgram && !sh_config.pLoadBlob)\n\t\treturn false;\n\n\tif (prog->name)\n\t\treturn false;\t//o.O\n\n\t*prescript = 0;\n\toffset = 0;\n\tmemset(prog->permu, 0, sizeof(prog->permu));\n\tprog->name = Z_StrDup(name);\n\tprog->geom = false;\n\tprog->tess = false;\n\tprog->rayquery = false;\n\tprog->calcgens = false;\n\tprog->numsamplers = 0;\n\tprog->defaulttextures = 0;\n\tfor(;;)\n\t{\n\t\twhile (*script == ' ' || *script == '\\r' || *script == '\\n' || *script == '\\t')\n\t\t\tscript++;\n\t\tif (!strncmp(script, \"!!fixed\", 7))\n\t\t{\n\t\t\tprog->calcgens = true;\n\t\t\tscript += 7;\n\t\t}\n\t\telse if (!strncmp(script, \"!!explicit\", 10))\n\t\t{\n\t\t\tprog->explicitsyms = true;\n\t\t\tscript += 10;\n\t\t}\n\t\telse if (!strncmp(script, \"!!geom\", 6))\n\t\t{\n\t\t\tprog->geom = true;\n\t\t\tscript += 6;\n\t\t}\n\t\telse if (!strncmp(script, \"!!tess\", 6))\n\t\t{\n\t\t\tprog->tess = true;\n\t\t\tscript += 6;\n\t\t}\n\t\telse if (!strncmp(script, \"!!samps\", 7))\n\t\t{\n\t\t\tcom_tokentype_t tt;\n\t\t\tqboolean ignore = false;\n\t\t\tscript += 7;\n\t\t\tfor(;;)\n\t\t\t{\n\t\t\t\tsize_t len;\n\t\t\t\tint i;\n\t\t\t\tchar *type, *idx, *next;\n\t\t\t\tchar *token = com_token;\n\n\t\t\t\tnext = COM_ParseTokenOut(script, \"\", com_token, sizeof(com_token), &tt);\n\t\t\t\tif (tt == TTP_LINEENDING || tt == TTP_EOF)\n\t\t\t\t\tbreak;\n\t\t\t\tscript = next;\n\n\t\t\t\tif (*token == '=' || *token == '!')\n\t\t\t\t{\n\t\t\t\t\tlen = strlen(token);\n\t\t\t\t\tif (*token == (Com_PermuOrFloatArgument(name, token+1, len-1, 0)?'!':'='))\n\t\t\t\t\t\tignore = true;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\telse if (ignore)\n\t\t\t\t\tcontinue;\n#if 1//def HAVE_LEGACY\n\t\t\t\telse if (!strncmp(token, \"deluxmap\", 8))\n\t\t\t\t{\t//FIXME: remove this some time.\n\t\t\t\t\tCon_DPrintf(\"Outdated texture name \\\"%s\\\" in program \\\"%s\\\"\\n\", token, name);\n\t\t\t\t\ttoken = va(\"deluxemap%s\",token+8);\n\t\t\t\t}\n#endif\n\t\t\t\ttype = strchr(token, ':');\n\t\t\t\tidx = strchr(token, '=');\n\t\t\t\tif (type || idx)\n\t\t\t\t{\t//name:type=idx\n\t\t\t\t\tif (type)\n\t\t\t\t\t\t*type++ = 0;\n\t\t\t\t\telse\n\t\t\t\t\t\ttype = \"2D\";\n\t\t\t\t\tif (idx)\n\t\t\t\t\t{\n\t\t\t\t\t\t*idx++ = 0;\n\t\t\t\t\t\ti = atoi(idx);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\ti = prog->numsamplers;\n\t\t\t\t\tif (prog->numsamplers < i+1)\n\t\t\t\t\t\tprog->numsamplers = i+1;\n\n\t\t\t\t\t/*for (j = 0; sh_defaultsamplers[j].name; j++)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (!strcmp(token, sh_defaultsamplers[j].name+2))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tCon_Printf(\"%s: %s is an internal texture name\\n\", name, token);\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}*/\n\n\t\t\t\t\t//I really want to use layout(binding = %i) here, but its specific to the glsl version (which we don't really know yet)\n\t\t\t\t\tQ_strlcatfz(prescript, &offset, sizeof(prescript), \"#define s_%s s_t%u\\nuniform %s%s s_%s;\\n\", token, i, strncmp(type, \"sampler\", 7)?\"sampler\":\"\", type, token);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tlen = strlen(token);\n\t\t\t\t\tfor (i = 0; sh_defaultsamplers[i].name; i++)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (!strcmp(token, sh_defaultsamplers[i].name+2))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tprog->defaulttextures |= sh_defaultsamplers[i].defaulttexbits;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif (!sh_defaultsamplers[i].name)\n\t\t\t\t\t{\t//this path is deprecated.\n\t\t\t\t\t\ti = atoi(token);\n\t\t\t\t\t\tif (i)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (qrenderer == QR_OPENGL)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\twhile (prog->numsamplers < i)\n\t\t\t\t\t\t\t\t\tQ_strlcatfz(prescript, &offset, sizeof(prescript), \"uniform sampler2D s_t%u;\\n\", prog->numsamplers++);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse if (prog->numsamplers < i)\n\t\t\t\t\t\t\t\tprog->numsamplers = i;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tCon_Printf(\"Unknown texture name \\\"%s\\\" in program \\\"%s\\\"\\n\", token, name);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse if (!strncmp(script, \"!!cvardf\", 8) || !strncmp(script, \"!!cvard3\", 8) || !strncmp(script, \"!!cvard4\", 8) || !strncmp(script, \"!!cvard_srgb\", 11))\n\t\t{\n\t\t\tqboolean srgb = false;\n\t\t\tfloat div = 1;\n\t\t\tchar type = script[7];\n\t\t\tscript+=8;\n\t\t\tif (type == '_')\n\t\t\t{\n\t\t\t\tif (*script == 's')\n\t\t\t\t{\n\t\t\t\t\tsrgb = true;\n\t\t\t\t\tscript+=1;\n\t\t\t\t}\n\n\t\t\t\tif (!strncmp(script, \"rgba\", 4))\n\t\t\t\t{\n\t\t\t\t\ttype = '4';\n\t\t\t\t\tscript+=4;\n\t\t\t\t}\n\t\t\t\telse if (!strncmp(script, \"rgb\", 3))\n\t\t\t\t{\n\t\t\t\t\ttype = '3';\n\t\t\t\t\tscript+=3;\n\t\t\t\t}\n\t\t\t\telse if (!strncmp(script, \"rg\", 2))\n\t\t\t\t{\n\t\t\t\t\ttype = '2';\n\t\t\t\t\tscript+=2;\n\t\t\t\t}\n\t\t\t\telse if (!strncmp(script, \"r\", 1) || !strncmp(script, \"f\", 1))\n\t\t\t\t{\n\t\t\t\t\ttype = 'f';\n\t\t\t\t\tscript+=1;\n\t\t\t\t}\n\n\t\t\t\tif (!strncmp(script, \"_b\", 2))\n\t\t\t\t{\n\t\t\t\t\tdiv = 255;\n\t\t\t\t\tscript+=2;\n\t\t\t\t}\n\t\t\t\telse if (!strncmp(script, \"_\", 1))\n\t\t\t\t\tdiv = strtod(script, &script);\n\t\t\t}\n\t\t\twhile (*script == ' ' || *script == '\\t')\n\t\t\t\tscript++;\n\t\t\tend = script;\n\t\t\twhile ((*end >= 'A' && *end <= 'Z') || (*end >= 'a' && *end <= 'z') || (*end >= '0' && *end <= '9') || *end == '_')\n\t\t\t\tend++;\n\t\t\tif (end - script < 64)\n\t\t\t{\n\t\t\t\tcvar_t *var;\n\t\t\t\tchar namebuf[64];\n\t\t\t\tchar valuebuf[64];\n\t\t\t\tmemcpy(namebuf, script, end - script);\n\t\t\t\tnamebuf[end - script] = 0;\n\t\t\t\twhile (*end == ' ' || *end == '\\t')\n\t\t\t\t\tend++;\n\t\t\t\tif (*end == '=')\n\t\t\t\t{\n\t\t\t\t\tscript = ++end;\n\t\t\t\t\twhile (*end && *end != '\\n' && *end != '\\r' && end < script+sizeof(namebuf)-1)\n\t\t\t\t\t\tend++;\n\t\t\t\t\tmemcpy(valuebuf, script, end - script);\n\t\t\t\t\tvaluebuf[end - script] = 0;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tstrcpy(valuebuf, \"0\");\n\t\t\t\tvar = Cvar_Get(namebuf, valuebuf, CVAR_SHADERSYSTEM, \"GLSL Variables\");\n\t\t\t\tif (var)\n\t\t\t\t{\n\t\t\t\t\tvar->flags |= CVAR_SHADERSYSTEM;\n\t\t\t\t\tif (srgb)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (type == '4')\n\t\t\t\t\t\t\tQ_strlcatfz(prescript, &offset, sizeof(prescript), \"#define %s %s(%g,%g,%g,%g)\\n\", namebuf, ((qrenderer == QR_OPENGL)?\"vec4\":\"float4\"), SRGBf(var->vec4[0]/div), SRGBf(var->vec4[1]/div), SRGBf(var->vec4[2]/div), var->vec4[3]/div);\n\t\t\t\t\t\telse if (type == '3')\n\t\t\t\t\t\t\tQ_strlcatfz(prescript, &offset, sizeof(prescript), \"#define %s %s(%g,%g,%g)\\n\", namebuf, ((qrenderer == QR_OPENGL)?\"vec3\":\"float3\"), SRGBf(var->vec4[0]/div), SRGBf(var->vec4[1]/div), SRGBf(var->vec4[2]/div));\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tQ_strlcatfz(prescript, &offset, sizeof(prescript), \"#define %s %g\\n\", namebuf, SRGBf(var->value/div));\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tif (type == '4')\n\t\t\t\t\t\t\tQ_strlcatfz(prescript, &offset, sizeof(prescript), \"#define %s %s(%g,%g,%g,%g)\\n\", namebuf, ((qrenderer == QR_OPENGL)?\"vec4\":\"float4\"), var->vec4[0]/div, var->vec4[1]/div, var->vec4[2]/div, var->vec4[3]/div);\n\t\t\t\t\t\telse if (type == '3')\n\t\t\t\t\t\t\tQ_strlcatfz(prescript, &offset, sizeof(prescript), \"#define %s %s(%g,%g,%g)\\n\", namebuf, ((qrenderer == QR_OPENGL)?\"vec3\":\"float3\"), var->vec4[0]/div, var->vec4[1]/div, var->vec4[2]/div);\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tQ_strlcatfz(prescript, &offset, sizeof(prescript), \"#define %s %g\\n\", namebuf, var->value/div);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tscript = end;\n\t\t}\n\t\telse if (!strncmp(script, \"!!cvarf\", 7) || !strncmp(script, \"!!cvari\", 7) || !strncmp(script, \"!!cvarv\", 7) || !strncmp(script, \"!!cvar3f\", 8) || !strncmp(script, \"!!cvar4f\", 8))\n\t\t{\n\t\t\tint type;\n\t\t\t     if (script[6]=='f') type = SP_CVARF;\n\t\t\telse if (script[6]=='i') type = SP_CVARI;\n\t\t\telse if (script[6]=='v') type = SP_CVAR3F;\n\t\t\telse if (script[6]=='3') type = SP_CVAR3F;\n\t\t\telse if (script[6]=='4') type = SP_CVAR4F;\n\t\t\telse break;\n\t\t\tif (cvarcount != sizeof(cvarnames)/sizeof(cvarnames[0]))\n\t\t\t\tcvarcount += Shader_ParseProgramCvar(script+8, &cvarnames[cvarcount], &cvartypes[cvarcount], type);\n\t\t}\n\t\telse if (\t!strncmp(script, \"!!semantic\", 10))\n\t\t{\n\t\t\tif (cvarcount != sizeof(cvarnames)/sizeof(cvarnames[0]))\n\t\t\t\tcvarcount += Shader_ParseSemantic(script+10, name, &cvarnames[cvarcount], &cvartypes[cvarcount]);\n\t\t}\n\t\telse if (\t!strncmp(script, \"!!const1f\", 9) ||\n\t\t\t\t\t!strncmp(script, \"!!const2f\", 9) ||\n\t\t\t\t\t!strncmp(script, \"!!const3f\", 9) ||\n\t\t\t\t\t!strncmp(script, \"!!const4f\", 9) ||\n\t\t\t\t\t!strncmp(script, \"!!constt\", 8))\n\t\t{\n\t\t\tint type;\n\t\t\tif (script[8] == 'f')\n\t\t\t\ttype = SP_CONST1F + (script[7]-'1');\n\t\t\telse if (script[8] == 'i')\n\t\t\t\ttype = SP_CONST1I + (script[7]-'1');\n\t\t\telse if (script[7] == 't')\n\t\t\t\ttype = SP_TEXTURE;\n\t\t\telse\n\t\t\t\tbreak;\n\n\t\t\tif (cvarcount != sizeof(cvarnames)/sizeof(cvarnames[0]))\n\t\t\t\tcvarcount += Shader_ParseProgramConst(script+9, &cvarnames[cvarcount], &cvartypes[cvarcount], type, (type==SP_TEXTURE)?&prog->numsamplers:NULL);\n\t\t}\n\t\telse if (!strncmp(script, \"!!arg\", 5))\n\t\t{\t//compat with our vulkan glsl, generate (specialisation) constants from #args\n\t\t\tchar namebuf[MAX_QPATH];\n\t\t\tchar valuebuf[MAX_QPATH];\n\t\t\tchar *out;\n\t\t\tchar *namestart;\n\t\t\tchar *atype;\n\t\t\tscript+=5;\n\t\t\tif (*script == 'b')\n\t\t\t{\n\t\t\t\tatype = \"bool\";\n\t\t\t\tstrcpy(valuebuf, \"false\");\n\t\t\t}\n\t\t\telse if (*script == 'f')\n\t\t\t{\n\t\t\t\tatype = \"float\";\n\t\t\t\tstrcpy(valuebuf, \"0.0\");\n\t\t\t}\n\t\t\telse if (*script == 'd')\n\t\t\t{\n\t\t\t\tatype = \"double\";\n\t\t\t\tstrcpy(valuebuf, \"0.0\");\n\t\t\t}\n\t\t\telse if (*script == 'i')\n\t\t\t{\n\t\t\t\tatype = \"int\";\n\t\t\t\tstrcpy(valuebuf, \"0\");\n\t\t\t}\n\t\t\telse if (*script == 'u')\n\t\t\t{\n\t\t\t\tatype = \"uint\";\n\t\t\t\tstrcpy(valuebuf, \"0\");\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tatype = \"float\";\t//I guess\n\t\t\t\tstrcpy(valuebuf, \"0.0\");\n\t\t\t}\n\t\t\twhile (*script >= 'a' && *script <= 'z')\n\t\t\t\tscript++;\n\t\t\twhile (*script == ' ' || *script == '\\t')\n\t\t\t\tscript++;\n\t\t\tnamestart = script;\n\t\t\twhile ((*script >= 'A' && *script <= 'Z') || (*script >= 'a' && *script <= 'z') || (*script >= '0' && *script <= '9') || *script == '_')\n\t\t\t\tscript++;\n\n\t\t\tif (script-namestart < countof(namebuf))\n\t\t\t{\n\t\t\t\tfloat def = 0;\n\t\t\t\tmemcpy(namebuf, namestart, script - namestart);\n\t\t\t\tnamebuf[script - namestart] = 0;\n\n\t\t\t\twhile (*script == ' ' || *script == '\\t')\n\t\t\t\t\tscript++;\n\t\t\t\tif (*script == '=')\n\t\t\t\t{\n\t\t\t\t\tscript++;\n\t\t\t\t\twhile (*script == ' ' || *script == '\\t')\n\t\t\t\t\t\tscript++;\n\n\t\t\t\t\tout = valuebuf;\n\t\t\t\t\twhile (out < com_token+countof(valuebuf)-1 && *script != '\\n' && !(script[0] == '/' && script[1] == '/'))\n\t\t\t\t\t\t*out++ = *script++;\n\t\t\t\t\t*out++ = 0;\n\t\t\t\t\tif (!strcmp(valuebuf, \"true\"))\n\t\t\t\t\t\tdef = 1;\n\t\t\t\t\telse\n\t\t\t\t\t\tdef = atof(valuebuf);\n\t\t\t\t}\n\t\t\t\tCom_FloatArgument(name, valuebuf, sizeof(valuebuf), def);\n\t\t\t\tQ_strlcatfz(prescript, &offset, sizeof(prescript), \"const %s arg_%s = %s(%s);\\n\", atype, namebuf, atype, valuebuf);\n\t\t\t}\n\t\t}\n\t\telse if (!strncmp(script, \"!!permu\", 7))\n\t\t{\n\t\t\tscript += 7;\n\t\t\twhile (*script == ' ' || *script == '\\t')\n\t\t\t\tscript++;\n\t\t\tend = script;\n\t\t\twhile ((*end >= 'A' && *end <= 'Z') || (*end >= 'a' && *end <= 'z') || (*end >= '0' && *end <= '9') || *end == '_')\n\t\t\t\tend++;\n\t\t\tfor (p = 0; p < countof(permutations); p++)\n\t\t\t{\n\t\t\t\tif (!strncmp(permutations[p].name, script, end - script) && permutations[p].name[end-script] == '\\0')\n\t\t\t\t{\n\t\t\t\t\tif (Shader_PermutationEnabled(permutations[p].bitmask))\n\t\t\t\t\t\tnopermutation &= ~permutations[p].bitmask;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (p == countof(permutations))\n\t\t\t{\n\t\t\t\t//we 'recognise' ones that are force-defined, despite not being actual permutations.\n\t\t\t\tif (end - script == 4 && !strncmp(\"TESS\", script, 4))\n\t\t\t\t\tcantess = true;\n\t\t\t\telse if (strncmp(\"SPECULAR\", script, end - script))\n\t\t\t\tif (strncmp(\"DELUXE\", script, end - script))\n\t\t\t\tif (strncmp(\"DELUX\", script, end - script))\n\t\t\t\tif (strncmp(\"OFFSETMAPPING\", script, end - script))\n\t\t\t\tif (strncmp(\"RELIEFMAPPING\", script, end - script))\n\t\t\t\tif (strncmp(\"FAKESHADOWS\", script, end - script))\n\t\t\t\t\tCon_DPrintf(\"Unknown pemutation in glsl program %s\\n\", name);\n\t\t\t}\n\t\t\tscript = end;\n\t\t}\n\t\telse if (!strncmp(script, \"!!ver\", 5))\n\t\t{\n\t\t\tint minver, maxver;\n\t\t\tscript += 5;\n\t\t\twhile (*script == ' ' || *script == '\\t')\n\t\t\t\tscript++;\n\t\t\tend = script;\n\t\t\twhile ((*end >= 'A' && *end <= 'Z') || (*end >= 'a' && *end <= 'z') || (*end >= '0' && *end <= '9') || *end == '_')\n\t\t\t\tend++;\n\t\t\tminver = strtol(script, &script, 0);\n\t\t\twhile (*script == ' ' || *script == '\\t')\n\t\t\t\tscript++;\n\t\t\tmaxver = strtol(script, NULL, 0);\n\t\t\tif (!maxver)\n\t\t\t\tmaxver = minver;\n\n\t\t\tver = maxver;\n\t\t\tif (ver > sh_config.maxver)\n\t\t\t\tver = sh_config.maxver;\n\t\t\tif (ver < minver)\n\t\t\t\tver = minver;\t//some kind of error.\n\n\t\t\tscript = end;\n\t\t}\n\t\telse if (!strncmp(script, \"!!\", 2))\n\t\t{\n\t\t\tCon_DPrintf(\"Unknown directinve in glsl program %s\\n\", name);\n\t\t\tscript += 2;\n\t\t\twhile (*script == ' ' || *script == '\\t')\n\t\t\t\tscript++;\n\t\t}\n\t\telse if (!strncmp(script, \"//\", 2))\n\t\t{\n\t\t\tscript += 2;\n\t\t\twhile (*script == ' ' || *script == '\\t')\n\t\t\t\tscript++;\n\t\t}\n\t\telse\n\t\t\tbreak;\n\t\twhile (*script && *script != '\\n')\n\t\t\tscript++;\n\t}\n\tprog->shadertext = Z_StrDup(script);\n\n\tif (qrenderer == qrtype && ver < 150)\n\t\tprog->tess = cantess = false;\t//GL_ARB_tessellation_shader requires glsl 150(gl3.2) (or glessl 3.1). nvidia complains about layouts if you try anyway\n\n\tif (!r_fog_permutation.ival)\n\t\tnopermutation |= PERMUTATION_BIT_FOG;\n\tif (!sh_config.max_gpu_bones)\n\t\tnopermutation |= PERMUTATION_SKELETAL;\n\n\t//multiple lightmaps is kinda hacky. if any are set, all must be.\n#if MAXRLIGHTMAPS > 1\n\tif (prog->defaulttextures & ((1u<<S_LIGHTMAP1 ) | (1u<<S_LIGHTMAP2 ) | (1u<<S_LIGHTMAP3 )))\n\t\tprog->defaulttextures |=((1u<<S_LIGHTMAP1 ) | (1u<<S_LIGHTMAP2 ) | (1u<<S_LIGHTMAP3 ));\n\tif (prog->defaulttextures & ((1u<<S_DELUXEMAP1) | (1u<<S_DELUXEMAP2) | (1u<<S_DELUXEMAP3)))\n\t\tprog->defaulttextures |=((1u<<S_DELUXEMAP1) | (1u<<S_DELUXEMAP2) | (1u<<S_DELUXEMAP3));\n#endif\n\n\tfor (end = *name?strchr(name+1, '#'):NULL; end && *end; )\n\t{\n\t\tsize_t startoffset=offset;\n\t\tchar *start = end+1;\n\t\tend = strchr(start, '#');\n\t\tif (!end)\n\t\t\tend = start + strlen(start);\n\t\tif (end-start == 7 && !Q_strncasecmp(start, \"usemods\", 7))\n\t\t\tprog->calcgens = true;\n\n\t\tif (end-start == 4 && !Q_strncasecmp(start, \"tess\", 4))\n\t\t\tprog->tess |= cantess;\n\n\t\tQ_strlcatfz(prescript, &offset, sizeof(prescript), \"#define \");\n\t\twhile (offset < sizeof(prescript) && start < end)\n\t\t{\n\t\t\tif (*start == '=')\n\t\t\t{\n\t\t\t\tif (offset == startoffset+8)\n\t\t\t\t\tbreak;\n\t\t\t\tstart++;\n\t\t\t\tprescript[offset++] = ' ';\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif ((*start >='a'&&*start<='z')||(*start >='A'&&*start<='Z')||*start=='_'||(*start >='0'&&*start<='9'&&offset>startoffset+8))\n\t\t\t\tprescript[offset++] = toupper(*start++);\n\t\t\telse\n\t\t\t{\t///invalid symbol name...\n\t\t\t\toffset = startoffset+8;\n\t\t\t\tprescript[offset] = 0;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (offset == startoffset+8)\n\t\t{\t///invalid symbol name...\n\t\t\toffset = startoffset;\n\t\t\tprescript[offset] = 0;\n\t\t\tbreak;\n\t\t}\n\t\twhile (offset < sizeof(prescript) && start < end)\n\t\t\tprescript[offset++] = toupper(*start++);\n\t\tQ_strlcatfz(prescript, &offset, sizeof(prescript), \"\\n\");\n\t}\n\n\tprog->preshade = Z_StrDup(prescript);\n\tprog->supportedpermutations = (~nopermutation) & (PERMUTATIONS-1);\n\tprog->shaderver = ver;\n\n\tif (cvarcount)\n\t{\n\t\t*prescript = 0;\n\t\toffset = 0;\n\t\tfor (i = 0; i < cvarcount && offset < sizeof(prescript); i++)\n\t\t{\n\t\t\tmemcpy(prescript+offset, &cvartypes[i], sizeof(int));\n\t\t\toffset+=4;\n\t\t\tQ_strlcatfz(prescript, &offset, sizeof(prescript), \"%s\", cvarnames[i]);\n\t\t\toffset++;\n\t\t}\n\t\tprog->cvardata = Z_Malloc(offset);\n\t\tprog->cvardatasize = offset;\n\t\tmemcpy(prog->cvardata, prescript, prog->cvardatasize);\n\t}\n\n\twhile(cvarcount)\n\t\tZ_Free((char*)cvarnames[--cvarcount]);\n\n\t//ensure that permutation 0 works correctly as a fallback.\n\t//FIXME: add debug mode to compile all.\n\tprog->permu[0] = Shader_LoadPermutation(prog, 0);\n\tif (!prog->permu[0])\n\t\treturn false;\n\n\tif (r_glsl_precache.ival)\n\t{\n\t\tfor (p = prog->supportedpermutations; p > 0; )\n\t\t{\n\t\t\tif (p != (p&prog->supportedpermutations))\n\t\t\t\tp &= prog->supportedpermutations;\t//this permutation isn't active, skip to the next one\n\t\t\telse\n\t\t\t{\n\t\t\t\tprog->permu[p] = Shader_LoadPermutation(prog, p);\n\t\t\t\tp--;\n\t\t\t}\n\t\t}\n\t}\n\treturn true;\n#else\n\treturn false;\n#endif\n}\n\ntypedef struct sgeneric_s\n{\n\tprogram_t prog;\n\tstruct sgeneric_s *next;\n\tchar *name;\n\tqboolean failed;\n} sgeneric_t;\nstatic sgeneric_t *sgenerics;\nstatic struct sbuiltin_s sbuiltins[] =\n{\n#include \"r_bishaders.h\"\n\t{QR_NONE}\n};\nvoid Shader_UnloadProg(program_t *prog)\n{\n\tif (sh_config.pDeleteProg)\n\t\tsh_config.pDeleteProg(prog);\n\n\tZ_Free(prog->name);\n\tZ_Free(prog->preshade);\n\tZ_Free(prog->shadertext);\n\tZ_Free(prog->cvardata);\n\n\tZ_Free(prog);\n}\nstatic void Shader_FlushGenerics(void)\n{\n\tsgeneric_t *g;\n\twhile (sgenerics)\n\t{\n\t\tg = sgenerics;\n\t\tsgenerics = g->next;\n\n\t\tif (g->prog.refs == 1)\n\t\t{\n\t\t\tg->prog.refs--;\n\t\t\tShader_UnloadProg(&g->prog);\n\t\t}\n\t\telse\n\t\t\tCon_Printf(\"generic shader still used\\n\"); \n\t}\n}\nstatic void Shader_LoadGeneric(sgeneric_t *g, int qrtype)\n{\n\tunsigned int i;\n\tvoid *file;\n\tchar basicname[MAX_QPATH];\n\tchar blobname[MAX_QPATH];\n\tchar *h;\n\n\tg->failed = true;\n\n\tTRACE((\"Loading program %s...\\n\", g->name));\n\n\tbasicname[1] = 0;\n\tQ_strncpyz(basicname, g->name, sizeof(basicname));\n\th = strchr(basicname+1, '#');\n\tif (h)\n\t\t*h = '\\0';\n\n\tif (strchr(basicname, '/') || strchr(basicname, '.'))\n\t{\t//explicit path\n\t\tFS_LoadFile(basicname, &file);\n\t\tif (!file)\n\t\t{\t//well that failed. try fixing up the extension in case they omitted that.\n\t\t\tQ_snprintfz(blobname, sizeof(blobname), COM_SkipPath(sh_config.progpath), basicname);\n\t\t\tFS_LoadFile(blobname, &file);\n\t\t}\n\t\t*blobname = 0;\n\t}\n\telse if (ruleset_allow_shaders.ival)\n\t{\t//renderer-specific files\n\t\tif (sh_config.progpath)\n\t\t{\n\t\t\tQ_snprintfz(blobname, sizeof(blobname), sh_config.progpath, basicname);\n\t\t\tFS_LoadFile(blobname, &file);\n\t\t}\n\t\telse\n\t\t\tfile = NULL;\n\t\tif (sh_config.blobpath && r_shaderblobs.ival)\n\t\t\tQ_snprintfz(blobname, sizeof(blobname), sh_config.blobpath, basicname);\n\t\telse\n\t\t\t*blobname = 0;\n\t}\n\telse\n\t{\n\t\tfile = NULL;\n\t\t*blobname = 0;\n\t}\n\n\tif (sh_config.pDeleteProg)\n\t{\n\t\tsh_config.pDeleteProg(&g->prog);\n\t}\n\tZ_Free(g->prog.name);\n\tg->prog.name = NULL;\n\tZ_Free(g->prog.preshade);\n\tg->prog.preshade = NULL;\n\tZ_Free(g->prog.shadertext);\n\tg->prog.shadertext = NULL;\n\tZ_Free(g->prog.cvardata);\n\tg->prog.cvardata = NULL;\n\n\tif (file)\n\t{\n\t\tTRACE((\"Loading from disk (%s)\\n\", g->name));\n//\t\tCon_DPrintf(\"Loaded %s from disk\\n\", sh_config.progpath?va(sh_config.progpath, basicname):basicname);\n\t\tg->failed = !Shader_LoadPermutations(g->name, &g->prog, file, qrtype, 0, blobname);\n\t\tFS_FreeFile(file);\n\t\treturn;\n\t}\n\telse\n\t{\n\t\tint ver;\n\t\tconst struct sbuiltin_s *progs;\n\t\tunsigned int l;\n\t\tfor (l = 0; l <= materialloader_count; l++)\n\t\t{\n\t\t\tif (l == materialloader_count)\n\t\t\t\tprogs = sbuiltins;\n\t\t\telse if (materialloader[l].funcs && materialloader[l].funcs->builtinshaders)\n\t\t\t\tprogs = materialloader[l].funcs->builtinshaders;\n\t\t\telse\n\t\t\t\tcontinue;\n\n\t\t\tfor (i = 0; *progs[i].name; i++)\n\t\t\t{\n\t\t\t\tif (progs[i].qrtype == qrtype && !strcmp(progs[i].name, basicname))\n\t\t\t\t{\n\t\t\t\t\tver = progs[i].apiver;\n\n\t\t\t\t\tif (ver < sh_config.minver || ver > sh_config.maxver)\n\t\t\t\t\t\tif (!(qrenderer==QR_OPENGL&&ver==110))\n\t\t\t\t\t\t\tcontinue;\n\n\t\t\t\t\tTRACE((\"Loading Embedded %s\\n\", g->name));\n\t\t\t\t\tg->failed = !Shader_LoadPermutations(g->name, &g->prog, progs[i].body, qrtype, ver, blobname);\n\n\t\t\t\t\tif (g->failed)\n\t\t\t\t\t\tcontinue;\n\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tTRACE((\"Program unloadable %s\\n\", g->name));\n\t}\n}\n\nprogram_t *Shader_FindGeneric(char *name, int qrtype)\n{\n\tsgeneric_t *g;\n\n\tfor (g = sgenerics; g; g = g->next)\n\t{\n\t\tif (!strcmp(name, g->name))\n\t\t{\n\t\t\tif (g->failed)\n\t\t\t\treturn NULL;\n\t\t\tg->prog.refs++;\n\t\t\treturn &g->prog;\n\t\t}\n\t}\n\n\t//don't even try if we know it won't work.\n\tif (!sh_config.progs_supported)\n\t\treturn NULL;\n\n\tg = BZ_Malloc(sizeof(*g) + strlen(name)+1);\n\tmemset(g, 0, sizeof(*g));\n\tg->name = (char*)(g+1);\n\tstrcpy(g->name, name);\n\tg->next = sgenerics;\n\tsgenerics = g;\n\n\tg->prog.refs = 1;\n\n\tShader_LoadGeneric(g, qrtype);\n\tif (g->failed)\n\t\treturn NULL;\n\tg->prog.refs++;\n\treturn &g->prog;\n}\nstatic void Shader_ReloadGenerics(void)\n{\n\tsgeneric_t *g;\n\tfor (g = sgenerics; g; g = g->next)\n\t{\n\t\t//this happens if some cvar changed that affects the glsl itself. supposedly.\n\t\tShader_LoadGeneric(g, qrenderer);\n\t}\n\n\t//this shader can take a while to load due to its number of permutations.\n\t//because this all happens on the main thread, try to avoid random stalls by pre-loading it.\n\tif (sh_config.progs_supported)\n\t{\n\t\tprogram_t *p = Shader_FindGeneric(\"defaultskin\", qrenderer);\n\t\tif (p)\t//generics get held on to in order to avoid so much churn. so we can just release the reference we just created and it'll be held until shutdown anyway.\n\t\t\tp->refs--;\n\t}\n}\nvoid Shader_WriteOutGenerics_f(void)\n{\n\tint i;\n\tchar *name;\n\tfor (i = 0; *sbuiltins[i].name; i++)\n\t{\n\t\tname = NULL;\n\t\tif (sbuiltins[i].qrtype == QR_OPENGL)\n\t\t{\n\t\t\tif (sbuiltins[i].apiver == 100)\n\t\t\t\tname = va(\"gles/eg_%s.glsl\", sbuiltins[i].name);\n\t\t\telse\n\t\t\t\tname = va(\"glsl/eg_%s.glsl\", sbuiltins[i].name);\n\t\t}\n\t\telse if (sbuiltins[i].qrtype == QR_DIRECT3D9)\n\t\t\tname = va(\"hlsl/eg_%s.hlsl\", sbuiltins[i].name);\n\t\telse if (sbuiltins[i].qrtype == QR_DIRECT3D11)\n\t\t\tname = va(\"hlsl11/eg_%s.hlsl\", sbuiltins[i].name);\n\n\t\tif (name)\n\t\t{\n\t\t\tvfsfile_t *f = FS_OpenVFS(name, \"rb\", FS_GAMEONLY);\n\t\t\tif (f)\n\t\t\t{\n\t\t\t\tint len = VFS_GETLEN(f);\n\t\t\t\tchar *buf = Hunk_TempAlloc(len);\n\t\t\t\tVFS_READ(f, buf, len);\n\t\t\t\tif (len != strlen(sbuiltins[i].body) || memcmp(buf, sbuiltins[i].body, len))\n\t\t\t\t\tCon_Printf(\"Not writing %s - modified version in the way\\n\", name);\n\t\t\t\telse\n\t\t\t\t\tCon_Printf(\"%s is unmodified\\n\", name);\n\t\t\t\tVFS_CLOSE(f);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tCon_Printf(\"Writing %s\\n\", name);\n\t\t\t\tFS_WriteFile(name, sbuiltins[i].body, strlen(sbuiltins[i].body), FS_GAMEONLY);\n\t\t\t}\n\t\t}\n\t}\n}\n\nstruct shader_field_names_s shader_attr_names[] =\n{\n\t/*vertex attributes*/\n\t{\"v_position1\",\t\t\t\tVATTR_VERTEX1},\n\t{\"v_position2\",\t\t\t\tVATTR_VERTEX2},\n\t{\"v_colour\",\t\t\t\tVATTR_COLOUR},\n\t{\"v_texcoord\",\t\t\t\tVATTR_TEXCOORD},\n\t{\"v_lmcoord\",\t\t\t\tVATTR_LMCOORD},\n\t{\"v_normal\",\t\t\t\tVATTR_NORMALS},\n\t{\"v_svector\",\t\t\t\tVATTR_SNORMALS},\n\t{\"v_tvector\",\t\t\t\tVATTR_TNORMALS},\n\t{\"v_bone\",\t\t\t\t\tVATTR_BONENUMS},\n\t{\"v_weight\",\t\t\t\tVATTR_BONEWEIGHTS},\n#if MAXRLIGHTMAPS > 1\n\t{\"v_lmcoord1\",\t\t\t\tVATTR_LMCOORD},\n\t{\"v_lmcoord2\",\t\t\t\tVATTR_LMCOORD2},\n\t{\"v_lmcoord3\",\t\t\t\tVATTR_LMCOORD3},\n\t{\"v_lmcoord4\",\t\t\t\tVATTR_LMCOORD4},\n\t{\"v_colour1\",\t\t\t\tVATTR_COLOUR},\n\t{\"v_colour2\",\t\t\t\tVATTR_COLOUR2},\n\t{\"v_colour3\",\t\t\t\tVATTR_COLOUR3},\n\t{\"v_colour4\",\t\t\t\tVATTR_COLOUR4},\n#endif\n\t{NULL}\n};\n\nstruct shader_field_names_s shader_unif_names[] =\n{\n\t/**///tagged names are available to vulkan\n\n\t/*matricies*/\n/**/{\"m_model\",\t\t\t\t\tSP_M_MODEL},\t//the model matrix\n\t{\"m_view\",\t\t\t\t\tSP_M_VIEW},\t\t//the view matrix\n\t{\"m_modelview\",\t\t\t\tSP_M_MODELVIEW},//the combined modelview matrix\n\t{\"m_projection\",\t\t\tSP_M_PROJECTION},//projection matrix\n/**/{\"m_modelviewprojection\",\tSP_M_MODELVIEWPROJECTION},//fancy mvp matrix. probably has degraded precision.\n\t{\"m_bones_packed\",\t\t\tSP_M_ENTBONES_PACKED},\t//bone matrix array. should normally be read via sys/skeletal.h\n\t{\"m_bones_mat3x4\",\t\t\tSP_M_ENTBONES_MAT3X4},\t//bone matrix array. should normally be read via sys/skeletal.h\n\t{\"m_bones_mat4\",\t\t\tSP_M_ENTBONES_MAT4},\t//bone matrix array. should normally be read via sys/skeletal.h\n\t{\"m_invviewprojection\",\t\tSP_M_INVVIEWPROJECTION},//inverted vp matrix\n\t{\"m_invmodelviewprojection\",SP_M_INVMODELVIEWPROJECTION},//inverted mvp matrix.\n\t{\"m_invmodelview\",\t\t\tSP_M_INVMODELVIEW},//inverted mv matrix.\n/**///m_modelinv\n\n\t/*viewer properties*/\n\t{\"v_eyepos\",\t\t\t\tSP_V_EYEPOS},\t//eye pos in worldspace\n/**/{\"w_fog\",\t\t\t\t\tSP_W_FOG},\t\t//read by sys/fog.h\n\t{\"w_user\",\t\t\t\t\tSP_W_USER},\t\t//set via VF_USERDATA\n\n\t/*ent properties*/\n\t{\"e_vblend\",\t\t\t\tSP_E_VBLEND},\t//v_position1+v_position2 scalers\n/**/{\"e_lmscale\",\t\t\t\tSP_E_LMSCALE},\t/*overbright shifting*/\n\t{\"e_vlscale\",\t\t\t\tSP_E_VLSCALE},\t/*no lightmaps, no overbrights*/\n\t{\"e_origin\",\t\t\t\tSP_E_ORIGIN},\t\t//the ents origin in worldspace\n/**/{\"e_time\",\t\t\t\t\tSP_E_TIME},\t\t\t//r_refdef.time - entity->time\n/**/{\"e_eyepos\",\t\t\t\tSP_E_EYEPOS},\t\t//eye pos in modelspace\n\t{\"e_colour\",\t\t\t\tSP_E_COLOURS},\t\t//colormod/alpha, even if colormod isn't set\n/**/{\"e_colourident\",\t\t\tSP_E_COLOURSIDENT},\t//colormod,alpha or 1,1,1,alpha if colormod isn't set\n/**/{\"e_glowmod\",\t\t\t\tSP_E_GLOWMOD},\t\t//fullbright scalers (for hdr mostly)\n/**/{\"e_uppercolour\",\t\t\tSP_E_TOPCOLOURS},\t//q1 player colours\n/**/{\"e_lowercolour\",\t\t\tSP_E_BOTTOMCOLOURS},//q1 player colours\n/**/{\"e_light_dir\",\t\t\t\tSP_E_L_DIR},\t\t//lightgrid light dir. dotproducts should be clamped to 0-1.\n/**/{\"e_light_mul\",\t\t\t\tSP_E_L_MUL},\t\t//lightgrid light scaler.\n/**/{\"e_light_ambient\",\t\t\tSP_E_L_AMBIENT},\t//lightgrid light value for the unlit side.\n\n\t{\"s_colour\",\t\t\t\tSP_S_COLOUR},\t//the rgbgen/alphagen stuff. obviously doesn't work with per-vertex ones.\n\n\t/*rtlight properties, use with caution*/\n\t{\"l_lightscreen\",\t\t\tSP_LIGHTSCREEN},\t//screenspace position of the current rtlight\n/**/{\"l_lightradius\",\t\t\tSP_LIGHTRADIUS},\t//radius of the current rtlight\n/**/{\"l_lightcolour\",\t\t\tSP_LIGHTCOLOUR},\t//rgb values of the current rtlight\n/**/{\"l_lightposition\",\t\t\tSP_LIGHTPOSITION},\t//light position in modelspace\n\t{\"l_lightdirection\",\t\tSP_LIGHTDIRECTION},\t//light direction in modelspace (ortho lights only, instead of position)\n/**/{\"l_lightcolourscale\",\t\tSP_LIGHTCOLOURSCALE},//ambient/diffuse/specular scalers\n/**/{\"l_cubematrix\",\t\t\tSP_LIGHTCUBEMATRIX},//matrix used to control the rtlight's cubemap projection\n/**/{\"l_shadowmapproj\",\t\t\tSP_LIGHTSHADOWMAPPROJ},\t//compacted projection matrix for shadowmaps\n/**/{\"l_shadowmapscale\",\t\tSP_LIGHTSHADOWMAPSCALE},//should probably use a samplerRect instead...\n\n\t{\"e_sourcesize\",\t\t\tSP_SOURCESIZE},\t\t\t//size of VF_SOURCECOLOUR image\n\t{\"e_rendertexturescale\",\tSP_RENDERTEXTURESCALE},\t//scaler for $currentrender, when npot isn't supported.\n\n\t{NULL}\n};\n\nstatic char *Shader_ParseBody(char *debugname, const char **ptr)\n{\n\tchar *body;\n\tconst char *start, *end;\n\n\tend = *ptr;\n\twhile (*end == ' ' || *end == '\\t' || *end == '\\r')\n\t\tend++;\n\tif (*end == '\\n')\n\t{\n\t\tint count;\n\t\tend++;\n\t\twhile (*end == ' ' || *end == '\\t')\n\t\t\tend++;\n\t\tif (*end != '{')\n\t\t{\n\t\t\tCon_Printf(\"shader \\\"%s\\\" missing program string\\n\", debugname);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tend++;\n\t\t\tstart = end;\n\t\t\tfor (count = 1; *end; end++)\n\t\t\t{\n\t\t\t\tif (*end == '}')\n\t\t\t\t{\n\t\t\t\t\tcount--;\n\t\t\t\t\tif (!count)\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\telse if (*end == '{')\n\t\t\t\t\tcount++;\n\t\t\t}\n\t\t\tbody = BZ_Malloc(end - start + 1);\n\t\t\tmemcpy(body, start, end-start);\n\t\t\tbody[end-start] = 0;\n\t\t\t*ptr = end+1;/*skip over it all*/\n\n\t\t\treturn body;\n\t\t}\n\t}\n\treturn NULL;\n}\n\nstatic void Shader_SLProgramName (shader_t *shader, shaderpass_t *pass, const char **ptr, int qrtype)\n{\n\t/*accepts:\n\tprogram\n\t{\n\t\tBLAH\n\t}\n\twhere BLAH is both vertex+frag with #ifdefs\n\tor\n\tprogram fname\n\ton one line.\n\t*/\n\tchar *programbody;\n\tchar *hash;\n\tprogram_t *newprog;\n\n\tprogrambody = Shader_ParseBody(shader->name, ptr);\n\tif (programbody)\n\t{\n\t\tnewprog = BZ_Malloc(sizeof(*newprog));\n\t\tmemset(newprog, 0, sizeof(*newprog));\n\t\tnewprog->refs = 1;\n\t\tif (!Shader_LoadPermutations(shader->name, newprog, programbody, qrtype, 0, NULL))\n\t\t{\n\t\t\tBZ_Free(newprog);\n\t\t\tnewprog = NULL;\n\t\t}\n\n\t\tBZ_Free(programbody);\n\t}\n\telse\n\t{\n\t\thash = strchr(shader->name, '#');\n\t\tif (hash)\n\t\t{\n\t\t\t//pass the # postfixes from the shader name onto the generic glsl to use\n\t\t\tchar newname[512];\n\t\t\tQ_snprintfz(newname, sizeof(newname), \"%s%s\", Shader_ParseExactString(ptr), hash);\n\t\t\tnewprog = Shader_FindGeneric(newname, qrtype);\n\t\t}\n\t\telse\n\t\t\tnewprog = Shader_FindGeneric(Shader_ParseExactString(ptr), qrtype);\n\t}\n\n\tif (pass)\n\t{\n\t\tif (pass->numMergedPasses)\n\t\t{\n\t\t\tShader_ReleaseGeneric(newprog);\n\t\t\tCon_DPrintf(\"shader %s: program defined after first texture map\\n\", shader->name);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tShader_ReleaseGeneric(pass->prog);\n\t\t\tpass->prog = newprog;\n\t\t}\n\t}\n\telse\n\t{\n\t\tShader_ReleaseGeneric(shader->prog);\n\t\tshader->prog = newprog;\n\t}\n}\n\nstatic void Shader_GLSLProgramName (parsestate_t *ps, const char **ptr)\n{\n\tshader_t *shader = ps->s;\n\tshaderpass_t *pass = ps->pass;\n\tShader_SLProgramName(shader,pass,ptr,QR_OPENGL);\n}\nstatic void Shader_ProgramName (parsestate_t *ps, const char **ptr)\n{\n\tshader_t *shader = ps->s;\n\tshaderpass_t *pass = ps->pass;\n\tShader_SLProgramName(shader,pass,ptr,qrenderer);\n}\nstatic void Shader_HLSL9ProgramName (parsestate_t *ps, const char **ptr)\n{\n\tshader_t *shader = ps->s;\n\tshaderpass_t *pass = ps->pass;\n\tShader_SLProgramName(shader,pass,ptr,QR_DIRECT3D9);\n}\nstatic void Shader_HLSL11ProgramName (parsestate_t *ps, const char **ptr)\n{\n\tshader_t *shader = ps->s;\n\tshaderpass_t *pass = ps->pass;\n\tShader_SLProgramName(shader,pass,ptr,QR_DIRECT3D11);\n}\n\nstatic void Shaderpass_BlendFunc (parsestate_t *ps, const char **ptr);\nstatic void Shader_ProgBlendFunc (parsestate_t *ps, const char **ptr)\n{\n\tif (ps->s->prog)\n\t{\n\t\tps->pass = ps->s->passes;\n\t\tShaderpass_BlendFunc(ps, ptr);\n\t\tps->pass = NULL;\n\t}\n}\n\nstatic void Shader_ReflectCube(parsestate_t *ps, const char **ptr)\n{\n\tchar *token = Shader_ParseSensString(ptr);\n\tunsigned int flags = Shader_SetImageFlags (ps, ps->pass, &token, IF_TEXTYPE_CUBE);\n\tps->s->defaulttextures->reflectcube = Shader_FindImage(ps, token, flags);\n}\nstatic void Shader_ReflectMask(parsestate_t *ps, const char **ptr)\n{\n\tchar *token = Shader_ParseSensString(ptr);\n\tunsigned int flags = Shader_SetImageFlags (ps, ps->pass, &token, IF_NOSRGB);\n\tps->s->defaulttextures->reflectmask = Shader_FindImage(ps, token, flags);\n}\n\nstatic void Shader_DiffuseMap(parsestate_t *ps, const char **ptr)\n{\n\tchar *token = Shader_ParseSensString(ptr);\n\tunsigned int flags = Shader_SetImageFlags (ps, ps->pass, &token, 0);\n\tps->s->defaulttextures->base = Shader_FindImage(ps, token, flags);\n\n\tQ_strncpyz(ps->s->defaulttextures->mapname, token, sizeof(ps->s->defaulttextures->mapname));\n}\nstatic void Shader_SpecularMap(parsestate_t *ps, const char **ptr)\n{\n\tchar *token = Shader_ParseSensString(ptr);\n\tunsigned int flags = Shader_SetImageFlags (ps, ps->pass, &token, 0);\n\tps->s->defaulttextures->specular = Shader_FindImage(ps, token, flags);\n}\nstatic void Shader_NormalMap(parsestate_t *ps, const char **ptr)\n{\n\tchar *token = Shader_ParseSensString(ptr);\n\tunsigned int flags = Shader_SetImageFlags (ps, ps->pass, &token, IF_TRYBUMP|IF_NOSRGB);\n\tps->s->defaulttextures->bump = Shader_FindImage(ps, token, flags);\n}\nstatic void Shader_FullbrightMap(parsestate_t *ps, const char **ptr)\n{\n\tchar *token = Shader_ParseSensString(ptr);\n\tunsigned int flags = Shader_SetImageFlags (ps, ps->pass, &token, 0);\n\tps->s->defaulttextures->fullbright = Shader_FindImage(ps, token, flags);\n}\nstatic void Shader_UpperMap(parsestate_t *ps, const char **ptr)\n{\n\tchar *token = Shader_ParseSensString(ptr);\n\tunsigned int flags = Shader_SetImageFlags (ps, ps->pass, &token, 0);\n\tps->s->defaulttextures->upperoverlay = Shader_FindImage(ps, token, flags);\n}\nstatic void Shader_LowerMap(parsestate_t *ps, const char **ptr)\n{\n\tchar *token = Shader_ParseSensString(ptr);\n\tunsigned int flags = Shader_SetImageFlags (ps, ps->pass, &token, 0);\n\tps->s->defaulttextures->loweroverlay = Shader_FindImage(ps, token, flags);\n}\nstatic void Shader_DisplacementMap(parsestate_t *ps, const char **ptr)\n{\n\tchar *token = Shader_ParseSensString(ptr);\n\tunsigned int flags = Shader_SetImageFlags (ps, ps->pass, &token, IF_NOSRGB);\n\tps->s->defaulttextures->displacement = Shader_FindImage(ps, token, flags);\n}\nstatic void Shader_TransmissionMap(parsestate_t *ps, const char **ptr)\n{\n\tchar *token = Shader_ParseSensString(ptr);\n\tunsigned int flags = Shader_SetImageFlags (ps, ps->pass, &token, IF_NOSRGB);\n\tps->s->defaulttextures->transmission = Shader_FindImage(ps, token, flags);\n}\nstatic void Shader_ThicknessMap(parsestate_t *ps, const char **ptr)\n{\n\tchar *token = Shader_ParseSensString(ptr);\n\tunsigned int flags = Shader_SetImageFlags (ps, ps->pass, &token, IF_NOSRGB);\n\tps->s->defaulttextures->thickness = Shader_FindImage(ps, token, flags);\n}\n\nstatic void Shaderpass_QF_Material(parsestate_t *ps, const char **ptr)\n{\t//qf_material BASETEXTURE NORMALMAP SPECULARMAP\n\tunsigned int flags;\n\tchar *progname = \"defaultwall\";\n\tchar *token;\n\tchar *hash = strchr(ps->s->name, '#');\n\tif (hash)\n\t{\n\t\t//pass the # postfixes from the shader name onto the generic glsl to use\n\t\tchar newname[512];\n\t\tQ_snprintfz(newname, sizeof(newname), \"%s%s\", progname, hash);\n\t\tps->pass->prog = Shader_FindGeneric(newname, qrenderer);\n\t}\n\telse\n\t\tps->pass->prog = Shader_FindGeneric(progname, qrenderer);\n\n\ttoken = Shader_ParseSensString(ptr);\n\tif (*token && strcmp(token, \"-\"))\n\t{\n\t\tflags = Shader_SetImageFlags (ps, ps->pass, &token, 0);\n\t\tif (*token)\n\t\t\tps->s->defaulttextures->base = Shader_FindImage(ps, token, flags);\n\t\telse\n\t\t{\n\t\t\ttoken = ps->s->name;\n\t\t\tif (hash)\n\t\t\t\t*hash = 0;\n\t\t\tps->s->defaulttextures->base = Shader_FindImage(ps, token, flags);\n\t\t\tif (hash)\n\t\t\t\t*hash = '#';\n\t\t}\n\t}\n\n\tif (*token)\n\t\ttoken = Shader_ParseSensString(ptr);\n\tif (*token && strcmp(token, \"-\"))\n\t{\n\t\tflags = Shader_SetImageFlags (ps, NULL, &token, IF_TRYBUMP|IF_NOSRGB);\n\t\tps->s->defaulttextures->bump = Shader_FindImage(ps, token, flags);\n\t}\n\n\tif (*token)\n\t\ttoken = Shader_ParseSensString(ptr);\n\tif (*token && strcmp(token, \"-\"))\n\t{\n\t\tflags = Shader_SetImageFlags (ps, NULL, &token, 0);\n\t\tps->s->defaulttextures->specular = Shader_FindImage(ps, token, flags);\n\t}\n}\n\nstatic qboolean Shaderpass_MapGen (parsestate_t *ps, shaderpass_t *pass, char *tname);\n\nstatic void Shader_Translucent(parsestate_t *ps, const char **ptr)\n{\n\tshader_t *shader = ps->s;\n\tshader->flags |= SHADER_BLEND;\n}\n\nstatic void Shader_PortalFBOScale(parsestate_t *ps, const char **ptr)\n{\n\tshader_t *shader = ps->s;\n\tshader->portalfboscale = Shader_ParseFloat(shader, ptr, 0);\n\tshader->portalfboscale = max(shader->portalfboscale, 0);\n}\n\nstatic void Shader_DP_Camera(parsestate_t *ps, const char **ptr)\n{\n\tps->s->sort = SHADER_SORT_PORTAL;\n\tps->dpwatertype |= 4;\n}\nstatic void Shader_DP_Water(parsestate_t *ps, const char **ptr)\n{\n\tshader_t *shader = ps->s;\n\tps->parseflags |= SPF_PROGRAMIFY;\n\n\tps->dpwatertype |= 3;\n\tps->reflectmin = Shader_ParseFloat(shader, ptr, 0);\n\tps->reflectmax = Shader_ParseFloat(shader, ptr, 0);\n\tps->refractfactor = Shader_ParseFloat(shader, ptr, 0);\n\tps->reflectfactor = Shader_ParseFloat(shader, ptr, 0);\n\tShader_ParseVector(shader, ptr, ps->refractcolour);\n\tShader_ParseVector(shader, ptr, ps->reflectcolour);\n\tps->wateralpha = Shader_ParseFloat(shader, ptr, 0);\n}\nstatic void Shader_DP_Reflect(parsestate_t *ps, const char **ptr)\n{\n\tps->parseflags |= SPF_PROGRAMIFY;\n\n\tps->dpwatertype |= 1;\n\tps->reflectmin = 1;\n\tps->reflectmax = 1;\n\tps->reflectfactor = Shader_ParseFloat(ps->s, ptr, 0);\n\tShader_ParseVector(ps->s, ptr, ps->reflectcolour);\n}\nstatic void Shader_DP_Refract(parsestate_t *ps, const char **ptr)\n{\n\tps->parseflags |= SPF_PROGRAMIFY;\n\n\tps->dpwatertype |= 2;\n\tps->refractfactor = Shader_ParseFloat(ps->s, ptr, 0);\n\tShader_ParseVector(ps->s, ptr, ps->refractcolour);\n}\n\nstatic void Shader_DP_OffsetMapping(parsestate_t *ps, const char **ptr)\n{\n\tshader_t *shader = ps->s;\n\tchar *token = Shader_ParseString(ptr);\n\tif (!strcmp(token, \"disable\") || !strcmp(token, \"none\") || !strcmp(token, \"off\"))\n\t\tps->offsetmappingmode = 0;\n\telse if (!strcmp(token, \"default\") || !strcmp(token, \"normal\"))\n\t\tps->offsetmappingmode = -1;\n\telse if (!strcmp(token, \"linear\"))\n\t\tps->offsetmappingmode = 1;\n\telse if (!strcmp(token, \"relief\"))\n\t\tps->offsetmappingmode = 2;\n\tps->offsetmappingscale = Shader_ParseFloat(shader, ptr, 1);\n\ttoken = Shader_ParseString(ptr);\n\tif (!strcmp(token, \"bias\"))\n\t\tps->offsetmappingbias = Shader_ParseFloat(shader, ptr, 0.5);\n\telse if (!strcmp(token, \"match\"))\n\t\tps->offsetmappingbias = 1.0 - Shader_ParseFloat(shader, ptr, 0.5);\n\telse if (!strcmp(token, \"match8\"))\n\t\tps->offsetmappingbias = 1.0 - (1.0/255) * Shader_ParseFloat(shader, ptr, 128);\n\telse if (!strcmp(token, \"match16\"))\n\t\tps->offsetmappingbias = 1.0 - (1.0/65535) * Shader_ParseFloat(shader, ptr, 32768);\n}\nstatic void Shader_DP_GlossScale(parsestate_t *ps, const char **ptr)\n{\n\tps->specularvalscale = Shader_ParseFloat(ps->s, ptr, 0);\n}\nstatic void Shader_DP_GlossExponent(parsestate_t *ps, const char **ptr)\n{\n\tps->specularexpscale = Shader_ParseFloat(ps->s, ptr, 0);\n}\n\nstatic void Shader_FactorBase(parsestate_t *ps, const char **ptr)\n{\n\tshader_t *shader = ps->s;\n\tshader->factors[MATERIAL_FACTOR_BASE][0] = Shader_ParseFloat(shader, ptr, 1);\n\tshader->factors[MATERIAL_FACTOR_BASE][1] = Shader_ParseFloat(shader, ptr, 1);\n\tshader->factors[MATERIAL_FACTOR_BASE][2] = Shader_ParseFloat(shader, ptr, 1);\n\tshader->factors[MATERIAL_FACTOR_BASE][3] = Shader_ParseFloat(shader, ptr, 1);\n}\nstatic void Shader_FactorSpec(parsestate_t *ps, const char **ptr)\n{\n\tshader_t *shader = ps->s;\n\tshader->factors[MATERIAL_FACTOR_SPEC][0] = Shader_ParseFloat(shader, ptr, 1);\n\tshader->factors[MATERIAL_FACTOR_SPEC][1] = Shader_ParseFloat(shader, ptr, 1);\n\tshader->factors[MATERIAL_FACTOR_SPEC][2] = Shader_ParseFloat(shader, ptr, 1);\n\tshader->factors[MATERIAL_FACTOR_SPEC][3] = Shader_ParseFloat(shader, ptr, 1);\n}\nstatic void Shader_FactorEmit(parsestate_t *ps, const char **ptr)\n{\n\tshader_t *shader = ps->s;\n\tshader->factors[MATERIAL_FACTOR_EMIT][0] = Shader_ParseFloat(shader, ptr, 1);\n\tshader->factors[MATERIAL_FACTOR_EMIT][1] = Shader_ParseFloat(shader, ptr, 1);\n\tshader->factors[MATERIAL_FACTOR_EMIT][2] = Shader_ParseFloat(shader, ptr, 1);\n\tshader->factors[MATERIAL_FACTOR_EMIT][3] = Shader_ParseFloat(shader, ptr, 1);\n}\nstatic void Shader_FactorTransmission(parsestate_t *ps, const char **ptr)\n{\n\tshader_t *shader = ps->s;\n\tshader->factors[MATERIAL_FACTOR_TRANSMISSION][0] = Shader_ParseFloat(shader, ptr, 1);\n//\tshader->factors[MATERIAL_FACTOR_TRANSMISSION][1] = the volume distance;\n\tshader->factors[MATERIAL_FACTOR_TRANSMISSION][2] = 0;\n\tshader->factors[MATERIAL_FACTOR_TRANSMISSION][3] = 0;\n}\nstatic void Shader_FactorVolume(parsestate_t *ps, const char **ptr)\n{\n\tshader_t *shader = ps->s;\n\tshader->factors[MATERIAL_FACTOR_VOLUME][0] = Shader_ParseFloat(shader, ptr, 1);\t//r\n\tshader->factors[MATERIAL_FACTOR_VOLUME][1] = Shader_ParseFloat(shader, ptr, 1);\t//g\n\tshader->factors[MATERIAL_FACTOR_VOLUME][2] = Shader_ParseFloat(shader, ptr, 1);\t//b\n\tshader->factors[MATERIAL_FACTOR_VOLUME][3] = Shader_ParseFloat(shader, ptr, 1);\t//factor\n\tshader->factors[MATERIAL_FACTOR_TRANSMISSION][1] = Shader_ParseFloat(shader, ptr, 1);\t//distance\n}\n\nstatic void Shader_BEMode(parsestate_t *ps, const char **ptr)\n{\n\tshader_t *shader = ps->s;\n\tchar subname[1024];\n\tint mode;\n\tchar tokencopy[1024];\n\tchar *token;\n\tchar *embed = NULL;\n\ttoken = Shader_ParseString(ptr);\n\tif (!Q_stricmp(token, \"rtlight\"))\n\t\tmode = -1;\t//all light types\n\telse if (!Q_stricmp(token, \"rtlight_only\"))\n\t\tmode = LSHADER_STANDARD;\n\telse if (!Q_stricmp(token, \"rtlight_smap\"))\n\t\tmode = LSHADER_SMAP;\n\telse if (!Q_stricmp(token, \"rtlight_spot\"))\n\t\tmode = LSHADER_SPOT;\n\telse if (!Q_stricmp(token, \"rtlight_cube\"))\n\t\tmode = LSHADER_CUBE;\n\telse if (!Q_stricmp(token, \"rtlight_cube_smap\"))\n\t\tmode = LSHADER_CUBE|LSHADER_SMAP;\n\telse if (!Q_stricmp(token, \"rtlight_cube_spot\"))\t\t//doesn't make sense.\n\t\tmode = LSHADER_CUBE|LSHADER_SPOT;\n\telse if (!Q_stricmp(token, \"rtlight_spot_smap\"))\n\t\tmode = LSHADER_SMAP|LSHADER_SPOT;\n\telse if (!Q_stricmp(token, \"rtlight_cube_spot_smap\"))\t//doesn't make sense.\n\t\tmode = LSHADER_CUBE|LSHADER_SPOT|LSHADER_SMAP;\n\telse if (!Q_stricmp(token, \"crepuscular\"))\n\t\tmode = bemoverride_crepuscular;\n\telse if (!Q_stricmp(token, \"depthonly\"))\n\t\tmode = bemoverride_depthonly;\n\telse if (!Q_stricmp(token, \"depthdark\"))\n\t\tmode = bemoverride_depthdark;\n\telse if (!Q_stricmp(token, \"gbuffer\") || !Q_stricmp(token, \"prelight\"))\n\t\tmode = bemoverride_gbuffer;\n\telse if (!Q_stricmp(token, \"fog\"))\n\t\tmode = bemoverride_fog;\n\telse\n\t{\n\t\tCon_DPrintf(CON_WARNING \"Shader %s specifies unknown bemode %s.\\n\", shader->name, token);\n\t\treturn;\t//not supported.\n\t}\n\n\tembed = Shader_ParseBody(shader->name, ptr);\n\tif (embed)\n\t{\n\t\tint l = strlen(embed) + 6;\n\t\tchar *b = BZ_Malloc(l);\n\t\tQ_snprintfz(b, l, \"{\\n%s\\n}\\n\", embed);\n\t\tBZ_Free(embed);\n\t\tembed = b;\n\t\t//generate a unique name\n\t\tQ_snprintfz(tokencopy, sizeof(tokencopy), \"%s_mode%i\", shader->name, mode);\n\t}\n\telse\n\t{\n\t\ttoken = Shader_ParseString(ptr);\n\t\tQ_strncpyz(tokencopy, token, sizeof(tokencopy));\t//make sure things don't go squiff.\n\t}\n\n\tif (mode == -1)\n\t{\n\t\t//shorthand for rtlights\n\t\tfor (mode = 0; mode < LSHADER_MODES; mode++)\n\t\t{\n\t\t\tif ((mode & LSHADER_RAYQUERY) && !r_shadow_raytrace.ival)\n\t\t\t\tcontinue;\t//no. just no.\n\t\t\tif ((mode & LSHADER_SMAP) && r_shadow_raytrace.ival)\n\t\t\t\tcontinue;\t//don't waste time.\n\t\t\tif ((mode & LSHADER_CUBE) && (mode & (LSHADER_SPOT|LSHADER_ORTHO)))\n\t\t\t\tcontinue;\t//cube projections don't make sense when the light isn't projecting a cube\n\t\t\tif ((mode & LSHADER_ORTHO) && (mode & LSHADER_SPOT))\n\t\t\t\tcontinue;\t//ortho+spot are mutually exclusive.\n\t\t\tQ_snprintfz(subname, sizeof(subname), \"%s%s%s%s%s%s%s\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(mode & LSHADER_RAYQUERY)?\"rq_\":\"\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttokencopy,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(mode & LSHADER_SMAP)?\"#PCF\":\"\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(mode & LSHADER_SPOT)?\"#SPOT\":\"\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(mode & LSHADER_CUBE)?\"#CUBE\":\"\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(mode & LSHADER_ORTHO)?\"#ORTHO\":\"\",\n#ifdef GLQUAKE\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(qrenderer == QR_OPENGL && gl_config.arb_shadow && (mode & LSHADER_SMAP))?\"#USE_ARB_SHADOW\":\"\"\n#else\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\"\n#endif\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t);\n\t\t\tshader->bemoverrides[mode] = R_RegisterCustom(shader->model, subname, shader->usageflags|(embed?SUR_FORCEFALLBACK:0), embed?Shader_DefaultScript:NULL, embed);\n\t\t}\n\t}\n\telse\n\t{\n\t\tshader->bemoverrides[mode] = R_RegisterCustom(shader->model, tokencopy, shader->usageflags|(embed?SUR_FORCEFALLBACK:0), embed?Shader_DefaultScript:NULL, embed);\n\t}\n\tif (embed)\n\t\tBZ_Free(embed);\n}\n\nstatic shaderkey_t shaderkeys[] =\n{\n#define Q3 NULL\n\t{\"cull\",\t\t\t\tShader_Cull,\t\t\t\tQ3},\n\t{\"skyparms\",\t\t\tShader_SkyParms,\t\t\tQ3},\n\t{\"fogparms\",\t\t\tShader_FogParms,\t\t\tQ3},\n\t{\"surfaceparm\",\t\t\tShader_SurfaceParm,\t\t\tQ3},\n\t{\"nomipmaps\",\t\t\tShader_NoMipMaps,\t\t\tQ3},\n\t{\"nopicmip\",\t\t\tShader_NoPicMip,\t\t\tQ3},\n\t{\"polygonoffset\",\t\tShader_PolygonOffset,\t\tQ3},\n\t{\"sort\",\t\t\t\tShader_Sort,\t\t\t\tQ3},\n\t{\"deformvertexes\",\t\tShader_DeformVertexes,\t\tQ3},\n\t{\"portal\",\t\t\t\tShader_Portal,\t\t\t\tQ3},\n\t{\"entitymergable\",\t\tShader_EntityMergable,\t\tQ3},\n\n\t//fte extensions\n\t{\"clutter\",\t\t\t\tShader_ClutterParms,\t\t\"fte\"},\n\t{\"deferredlight\",\t\tShader_Deferredlight,\t\t\"fte\"},\t//(sort = prelight)\n//\t{\"lpp_light\",\t\t\tShader_Deferredlight,\t\t\"fte\"},\t//(sort = prelight)\n\t{\"affine\",\t\t\t\tShader_Affine,\t\t\t\t\"fte\"},\t//some hardware is horribly slow, and can benefit from certain hints.\n\t{\"fullrate\",\t\t\tShader_FullRate,\t\t\t\"fte\"},\t//blocks half-rate shading on this surface.\n\n\t{\"bemode\",\t\t\t\tShader_BEMode,\t\t\t\t\"fte\"},\n\n\t{\"diffusemap\",\t\t\tShader_DiffuseMap,\t\t\t\"fte\"},\n\t{\"normalmap\",\t\t\tShader_NormalMap,\t\t\t\"fte\"},\n\t{\"specularmap\",\t\t\tShader_SpecularMap,\t\t\t\"fte\"},\n\t{\"fullbrightmap\",\t\tShader_FullbrightMap,\t\t\"fte\"},\n\t{\"uppermap\",\t\t\tShader_UpperMap,\t\t\t\"fte\"},\n\t{\"lowermap\",\t\t\tShader_LowerMap,\t\t\t\"fte\"},\n\t{\"reflectmask\",\t\t\tShader_ReflectMask,\t\t\t\"fte\"},\n\t{\"displacementmap\",\t\tShader_DisplacementMap,\t\t\"fte\"},\n\t{\"transmissionmap\",\t\tShader_TransmissionMap,\t\t\"fte\"},\n\t{\"thicknessmap\",\t\tShader_ThicknessMap,\t\t\"fte\"},\n\n\t{\"portalfboscale\",\t\tShader_PortalFBOScale,\t\t\"fte\"},\t//portal/mirror/refraction/reflection FBOs are resized by this scale\n\t{\"basefactor\",\t\t\tShader_FactorBase,\t\t\t\"fte\"},\t//material scalers for glsl\n\t{\"specularfactor\",\t\tShader_FactorSpec,\t\t\t\"fte\"},\t//material scalers for glsl\n\t{\"fullbrightfactor\",\tShader_FactorEmit,\t\t\t\"fte\"},\t//material scalers for glsl\n\t{\"fte_transmissionfactor\",Shader_FactorTransmission,\"fte\"},\t//material scalers for glsl\n\t{\"fte_volumefactor\",\tShader_FactorVolume,\t\t\"fte\"},\t//material scalers for glsl\n\n\t//TODO: PBR textures...\n//\t{\"albedomap\",\t\t\tShader_DiffuseMap,\t\t\t\"fte\"},\t//rgb(a)\n//\t{\"loweruppermap\",\t\tShader_LowerUpperMap,\t\t\"fte\"}, //r=lower, g=upper (team being more important than personal colours, this allows the texture to gracefully revert to red-only)\n\t//{\"normalmap\",\t\t\tShader_NormalMap,\t\t\t\"fte\"},\t//xy-h\n//\t{\"ormmap\",\t\t\t\tShader_SpecularMap,\t\t\t\"fte\"},\t//r=occlusion, g=metalness, b=roughness.\n\t//{\"glowmap\",\t\t\tShader_FullbrightMap,\t\t\"fte\"}, //rgb\n\n\t/*program stuff at the material level is an outdated practise.*/\n\t{\"program\",\t\t\t\tShader_ProgramName,\t\t\t\"fte\"},\t//usable with any renderer that has a usable shader language...\n\t{\"glslprogram\",\t\t\tShader_GLSLProgramName,\t\t\"fte\"},\t//for renderers that accept embedded glsl\n\t{\"hlslprogram\",\t\t\tShader_HLSL9ProgramName,\t\"fte\"},\t//for d3d with embedded hlsl\n\t{\"hlsl11program\",\t\tShader_HLSL11ProgramName,\t\"fte\"},\t//for d3d with embedded hlsl\n\t{\"progblendfunc\",\t\tShader_ProgBlendFunc,\t\t\"fte\"},\t//specifies the blend mode (actually just overrides the first subpasses' blendmode.\n//\t{\"progmap\",\t\t\t\tShader_ProgMap,\t\t\t\t\"fte\"},\t//avoids needing extra subpasses (actually just inserts an extra pass).\n\n\t//dp compat\n\t{\"reflectcube\",\t\t\tShader_ReflectCube,\t\t\t\"dp\"},\n\t{\"camera\",\t\t\t\tShader_DP_Camera,\t\t\t\"dp\"},\n\t{\"water\",\t\t\t\tShader_DP_Water,\t\t\t\"dp\"},\n\t{\"reflect\",\t\t\t\tShader_DP_Reflect,\t\t\t\"dp\"},\n\t{\"refract\",\t\t\t\tShader_DP_Refract,\t\t\t\"dp\"},\n\t{\"offsetmapping\",\t\tShader_DP_OffsetMapping,\t\"dp\"},\n\t{\"shadow\",\t\t\t\tNULL,\t\t\t\t\t\t\"dp\"},\n\t{\"noshadow\",\t\t\tNULL,\t\t\t\t\t\t\"dp\"},\n\t{\"polygonoffset\",\t\tNULL,\t\t\t\t\t\t\"dp\"},\n\t{\"glossintensitymod\",\tShader_DP_GlossScale,\t\t\"dp\"},\t//scales r_shadow_glossintensity(=1), aka: gl_specular\n\t{\"glossexponentmod\",\tShader_DP_GlossExponent,\t\"dp\"},\t//scales r_shadow_glossexponent(=32)\n\t{\"transparentsort\",\t\tShader_DP_Sort,\t\t\t\t\"dp\"},\t//urgh...\n\n\t/*doom3 compat*/\n\t{\"diffusemap\",\t\t\tShader_DiffuseMap,\t\t\t\"doom3\"},\t//macro for \"{\\nstage diffusemap\\nmap <map>\\n}\"\n\t{\"bumpmap\",\t\t\t\tShader_NormalMap,\t\t\t\"doom3\"},\t//macro for \"{\\nstage bumpmap\\nmap <map>\\n}\"\n\t{\"discrete\",\t\t\tNULL,\t\t\t\t\t\t\"doom3\"},\n\t{\"nonsolid\",\t\t\tNULL,\t\t\t\t\t\t\"doom3\"},\n\t{\"noimpact\",\t\t\tNULL,\t\t\t\t\t\t\"doom3\"},\n\t{\"translucent\",\t\t\tShader_Translucent,\t\t\t\"doom3\"},\n\t{\"noshadows\",\t\t\tNULL,\t\t\t\t\t\t\"doom3\"},\n\t{\"nooverlays\",\t\t\tNULL,\t\t\t\t\t\t\"doom3\"},\n\t{\"nofragment\",\t\t\tNULL,\t\t\t\t\t\t\"doom3\"},\n\n\t/*RTCW compat*/\n\t{\"nocompress\",\t\t\tNULL,\t\t\t\t\t\t\"rtcw\"},\n\t{\"allowcompress\",\t\tNULL,\t\t\t\t\t\t\"rtcw\"},\n\t{\"nofog\",\t\t\t\tNULL,\t\t\t\t\t\t\"rtcw\"},\n\t{\"skyfogvars\",\t\t\tNULL,\t\t\t\t\t\t\"rtcw\"},\n\t{\"sunshader\",\t\t\tNULL,\t\t\t\t\t\t\"rtcw\"},\n\t{\"sun\",\t\t\t\t\tNULL,\t\t\t\t\t\t\"q3map2\"},\t//provides rgb and dir\n\t{\"sunExt\",\t\t\t\tNULL,\t\t\t\t\t\t\"q3map2\"},\t//treated as an alias\n\t{\"fogParms\",\t\t\tNULL,\t\t\t\t\t\t\"rtcw\"},\t//sets a cvar. *shudder*\n\t{\"fogvars\",\t\t\t\tNULL,\t\t\t\t\t\t\"rtcw\"},\t//sets a cvar. *shudder*\n\t{\"waterfogvars\",\t\tNULL,\t\t\t\t\t\t\"rtcw\"},\t//sets a cvar. *shudder*\n\t{\"light\",\t\t\t\tNULL,\t\t\t\t\t\t\"rtcw\"},\t//for q3map2, not us\n\t{\"lightgridmulamb\",\t\tNULL,\t\t\t\t\t\t\"rtcw\"},\t//urm\n\t{\"lightgridmuldir\",\t\tNULL,\t\t\t\t\t\t\"rtcw\"},\t//not really sure how this is useful to us\n\n\t/*qfusion / warsow compat*/\n//\t{\"skyparms2\",\t\t\tNULL,\t\t\t\t\t\t\"qf\"},\t//skyparms without the underscore.\n//\t{\"skyparmssides\",\t\tNULL,\t\t\t\t\t\t\"qf\"},\t//skyparms with explicitly-named faces\n//\t{\"nocompress\",\t\t\tNULL,\t\t\t\t\t\t\"qf\"},\t//disables opportunistic compression (doesn't affect compressed source images, apparently)\n//\t{\"nofiltering\",\t\t\tNULL,\t\t\t\t\t\t\"qf\"},\t//misnomer. there is always 'filtering'. this means to use nearest filtering for min and mag, as well as no mipmaps.\n//\t{\"smallestmipmapsize\",\tNULL,\t\t\t\t\t\t\"qf\"},\t//mips with a size less than the specified value are dropped.\n//\t{\"stenciltest\",\t\t\tNULL,\t\t\t\t\t\t\"qf\"},\t//enables GL_STENCIL_TEST, which is special-case stuff that I see no reason to support\n//\t{\"offsetmappingscale\",\tNULL,\t\t\t\t\t\t\"qf\"},\n//\t{\"glossexponent\",\t\tNULL,\t\t\t\t\t\t\"qf\"},\n//\t{\"glossintensity\",\t\tNULL,\t\t\t\t\t\t\"qf\"},\n//\t{\"template\",\t\t\tNULL,\t\t\t\t\t\t\"qf\"},\t//parses some other shader, with $3 etc arg expansion\n\t{\"skip\",\t\t\t\tNULL,\t\t\t\t\t\t\"qf\"},\t//just skips the line. acts like a comment. no idea why they can't just use a comment.\n//\t{\"softparticle\",\t\tNULL,\t\t\t\t\t\t\"qf\"},\t//uses screen depth, if possible. \n//\t{\"forceworldoutlines\",\tNULL,\t\t\t\t\t\t\"qf\"},\t//looks like an ugly hack to me.\n\n\t{NULL,\t\t\t\tNULL}\n};\n\nstatic struct\n{\n\tchar *name;\n\tchar *body;\n} shadermacros[] =\n{\n\t{\"decal_macro\", \t\"polygonOffset 1\\ndiscrete\\nsort decal\\nnoShadows\"},\n//\t{\"diffusemap\", \t\t\"{\\nblend diffusemap\\nmap %1\\n}\"},\n//\t{\"bumpmap\", \t\t\"{\\nblend bumpmap\\nmap %1\\n}\"},\n//\t{\"specularmap\", \t\"{\\nblend specularmap\\nmap %1\\n}\"},\n\t{NULL}\n};\n\n// ===============================================================\n\nstatic qboolean Shaderpass_MapGen (parsestate_t *ps, shaderpass_t *pass, char *tname)\n{\n\tshader_t *shader = ps->s;\n\tint tcgen = TC_GEN_BASE;\n\tif (!Q_stricmp (tname, \"$lightmap\"))\n\t{\n\t\ttcgen = TC_GEN_LIGHTMAP;\n\t\tpass->flags |= SHADER_PASS_LIGHTMAP | SHADER_PASS_NOMIPMAP;\n\t\tpass->texgen = T_GEN_LIGHTMAP;\n\t\tshader->flags |= SHADER_HASLIGHTMAP;\n\t}\n\telse if (!Q_stricmp (tname, \"$deluxmap\"))\n\t{\n\t\ttcgen = TC_GEN_LIGHTMAP;\n\t\tpass->flags |= SHADER_PASS_DELUXMAP | SHADER_PASS_NOMIPMAP;\n\t\tpass->texgen = T_GEN_DELUXMAP;\n\t}\n\telse if (!Q_stricmp (tname, \"$diffuse\"))\n\t{\n\t\tpass->texgen = T_GEN_DIFFUSE;\n\t\tshader->flags |= SHADER_HASDIFFUSE;\n\t}\n\telse if (!Q_stricmp (tname, \"$paletted\"))\n\t{\n\t\tpass->texgen = T_GEN_PALETTED;\n\t\tshader->flags |= SHADER_HASPALETTED;\n\t}\n\telse if (!Q_stricmp (tname, \"$normalmap\"))\n\t{\n\t\tpass->texgen = T_GEN_NORMALMAP;\n\t\tshader->flags |= SHADER_HASNORMALMAP;\n\t}\n\telse if (!Q_stricmp (tname, \"$specular\"))\n\t{\n\t\tpass->texgen = T_GEN_SPECULAR;\n\t\tshader->flags |= SHADER_HASGLOSS;\n\t}\n\telse if (!Q_stricmp (tname, \"$fullbright\"))\n\t{\n\t\tpass->texgen = T_GEN_FULLBRIGHT;\n\t\tshader->flags |= SHADER_HASFULLBRIGHT;\n\t}\n\telse if (!Q_stricmp (tname, \"$upperoverlay\"))\n\t{\n\t\tshader->flags |= SHADER_HASTOPBOTTOM;\n\t\tpass->texgen = T_GEN_UPPEROVERLAY;\n\t}\n\telse if (!Q_stricmp (tname, \"$loweroverlay\"))\n\t{\n\t\tshader->flags |= SHADER_HASTOPBOTTOM;\n\t\tpass->texgen = T_GEN_LOWEROVERLAY;\n\t}\n\telse if (!Q_stricmp (tname, \"$reflectcube\"))\n\t{\n\t\tshader->flags |= SHADER_HASREFLECTCUBE;\n\t\tpass->texgen = T_GEN_REFLECTCUBE;\n\t}\n\telse if (!Q_stricmp (tname, \"$reflectmask\"))\n\t{\n\t\tpass->texgen = T_GEN_REFLECTMASK;\n\t}\n\telse if (!Q_stricmp (tname, \"$displacement\"))\n\t{\n\t\tshader->flags |= SHADER_HASDISPLACEMENT;\n\t\tpass->texgen = T_GEN_DISPLACEMENT;\n\t}\n\telse if (!Q_stricmp (tname, \"$shadowmap\"))\n\t{\n\t\tpass->texgen = T_GEN_SHADOWMAP;\n\t\tpass->flags |= SHADER_PASS_DEPTHCMP;\n\t}\n\telse if (!Q_stricmp (tname, \"$lightcubemap\"))\n\t{\n\t\tpass->texgen = T_GEN_LIGHTCUBEMAP;\n\t}\n\telse if (!Q_stricmp (tname, \"$currentrender\"))\n\t{\n\t\tpass->texgen = T_GEN_CURRENTRENDER;\n\t\tshader->flags |= SHADER_HASCURRENTRENDER;\n\t}\n\telse if (!Q_stricmp (tname, \"$sourcecolour\"))\n\t{\n\t\tpass->texgen = T_GEN_SOURCECOLOUR;\n\t}\n\telse if (!Q_stricmp (tname, \"$sourcecube\"))\n\t{\n\t\tpass->texgen = T_GEN_SOURCECUBE;\n\t}\n\telse if (!Q_stricmp (tname, \"$sourcedepth\"))\n\t{\n\t\tpass->texgen = T_GEN_SOURCEDEPTH;\n\t}\n\telse if (!Q_strnicmp (tname, \"$gbuffer\", 8))\n\t{\n\t\tunsigned idx = strtoul(tname+8, &tname, 10);\n\t\tif (*tname || idx >= GBUFFER_COUNT)\n\t\t\treturn false;\n\t\tpass->texgen = T_GEN_GBUFFER0 + idx;\n\t}\n\telse if (!Q_stricmp (tname, \"$reflection\"))\n\t{\n\t\tshader->flags |= SHADER_HASREFLECT;\n\t\tpass->texgen = T_GEN_REFLECTION;\n\t}\n\telse if (!Q_stricmp (tname, \"$refraction\"))\n\t{\n\t\tshader->flags |= SHADER_HASREFRACT;\n\t\tpass->texgen = T_GEN_REFRACTION;\n\t}\n\telse if (!Q_stricmp (tname, \"$refractiondepth\"))\n\t{\n\t\tshader->flags |= SHADER_HASREFRACT;\n\t\tpass->texgen = T_GEN_REFRACTIONDEPTH;\n\t}\n\telse if (!Q_stricmp (tname, \"$ripplemap\"))\n\t{\n\t\tshader->flags |= SHADER_HASRIPPLEMAP;\n\t\tpass->texgen = T_GEN_RIPPLEMAP;\n\t}\n\telse if (!Q_stricmp (tname, \"$null\"))\n\t{\n\t\tpass->flags |= SHADER_PASS_NOMIPMAP|SHADER_PASS_DETAIL;\n\t\tpass->texgen = T_GEN_SINGLEMAP;\n\t}\n\telse\n\t\treturn false;\n\n\tif (pass->tcgen == TC_GEN_UNSPECIFIED)\n\t\tpass->tcgen = tcgen;\n\treturn true;\n}\n\nshaderpass_t *Shaderpass_DefineMap(parsestate_t *ps, shaderpass_t *pass)\n{\n\t//'map foo' works a bit differently when there's a program in the same pass.\n\t//instead of corrupting the previous one, it collects multiple maps so that {prog foo;map t0;map t1; map t2; blendfunc add} can work as expected\n\tif (pass->prog)\n\t{\n\t\tif (pass->numMergedPasses==0)\n\t\t\tpass->numMergedPasses++;\n\t\telse\n\t\t{\t//FIXME: bounds check!\n\t\t\tif (ps->s->numpasses == SHADER_PASS_MAX || ps->s->numpasses == SHADER_TMU_MAX)\n\t\t\t{\n\t\t\t\tCon_DPrintf (CON_WARNING \"Shader %s has too many texture passes.\\n\", ps->s->name);\n\t\t\t\tps->droppass = true;\n\t\t\t}\n//\t\t\telse if (shader->numpasses == be_maxpasses)\n//\t\t\t\tps->droppass = true;\n\t\t\telse\n\t\t\t{\n\t\t\t\tpass->numMergedPasses++;\n\t\t\t\tps->s->numpasses++;\n\t\t\t}\n\t\t\tpass = ps->s->passes+ps->s->numpasses-1;\n\t\t\tmemset(pass, 0, sizeof(*pass));\n\t\t}\n\t}\n\telse if (pass->numMergedPasses>1)\n\t\tpass = ps->s->passes+ps->s->numpasses-1;\t//nextbundle stuff.\n\telse\n\t\tpass->numMergedPasses = 1;\n\treturn pass;\n}\n\nstatic void Shaderpass_Map (parsestate_t *ps, const char **ptr)\n{\n\tshader_t *shader = ps->s;\n\tshaderpass_t *pass = ps->pass;\n\tint flags;\n\tchar *token;\n\n\tpass = Shaderpass_DefineMap(ps, pass);\n\n\tpass->anim_frames[0] = r_nulltex;\n\n\ttoken = Shader_ParseSensString (ptr);\n\n\t/*cod compat*/\n\tif (!stricmp(token, \"clamp\"))\n\t\ttoken = Shader_ParseSensString (ptr);\n\telse if (!stricmp(token, \"clampx\"))\n\t\ttoken = Shader_ParseSensString (ptr);\n\telse if (!stricmp(token, \"clampy\"))\n\t\ttoken = Shader_ParseSensString (ptr);\n\n\tflags = Shader_SetImageFlags (ps, pass, &token, 0);\n\tif (!Shaderpass_MapGen(ps, pass, token))\n\t{\n\t\tpass->texgen = T_GEN_SINGLEMAP;\n\t\tif (pass->tcgen == TC_GEN_UNSPECIFIED)\n\t\t\tpass->tcgen = TC_GEN_BASE;\n\t\tif (!*shader->defaulttextures->mapname && *token != '$' && pass->tcgen == TC_GEN_BASE)\n\t\t\tQ_strncpyz(shader->defaulttextures->mapname, token, sizeof(shader->defaulttextures->mapname));\n\t\tpass->anim_frames[0] = Shader_FindImage (ps, token, flags);\n\t}\n}\n\nstatic void Shaderpass_AnimMap_Flag (parsestate_t *ps, const char **ptr, unsigned int flags)\n{\n\tshader_t *shader = ps->s;\n\tshaderpass_t *pass = ps->pass;\n\tchar *token;\n\ttexid_t image;\n\tqboolean isdiffuse = false;\n\n\tflags |= Shader_SetImageFlags (ps, ps->pass, NULL, 0);\n\n\tif (pass->tcgen == TC_GEN_UNSPECIFIED)\n\t\tpass->tcgen = TC_GEN_BASE;\n\tpass->flags |= SHADER_PASS_ANIMMAP;\n\tpass->texgen = T_GEN_ANIMMAP;\n\tpass->anim_fps = Shader_ParseFloat (shader, ptr, 0);\n\tpass->anim_numframes = 0;\n\n\tfor ( ; ; )\n\t{\n\t\ttoken = Shader_ParseString(ptr);\n\t\tif (!token[0])\n\t\t{\n\t\t\tbreak;\n\t\t}\n\n\t\tif (!pass->anim_numframes && !*shader->defaulttextures->mapname && *token != '$' && pass->tcgen == TC_GEN_BASE)\n\t\t{\n\t\t\tisdiffuse = true;\n\t\t\tshader->defaulttextures_fps = pass->anim_fps;\n\t\t}\n\n\t\tif (pass->anim_numframes < SHADER_MAX_ANIMFRAMES)\n\t\t{\n\t\t\timage = Shader_FindImage (ps, token, flags);\n\n\t\t\tif (isdiffuse)\n\t\t\t{\n\t\t\t\tif (shader->numdefaulttextures < pass->anim_numframes+1)\n\t\t\t\t{\n\t\t\t\t\tint newmax = pass->anim_numframes+1;\n\t\t\t\t\tshader->defaulttextures = BZ_Realloc(shader->defaulttextures, sizeof(*shader->defaulttextures) * (newmax));\n\t\t\t\t\tmemset(shader->defaulttextures+shader->numdefaulttextures, 0, sizeof(*shader->defaulttextures) * (newmax-shader->numdefaulttextures));\n\t\t\t\t\tshader->numdefaulttextures = newmax;\n\t\t\t\t}\n\t\t\t\tQ_strncpyz(shader->defaulttextures[pass->anim_numframes].mapname, token, sizeof(shader->defaulttextures[pass->anim_numframes].mapname));\n\t\t\t}\n\t\t\tif (!TEXVALID(image))\n\t\t\t{\n\t\t\t\tpass->anim_frames[pass->anim_numframes++] = missing_texture;\n\t\t\t\tCon_DPrintf (CON_WARNING \"Shader %s has an animmap with no image: %s.\\n\", shader->name, token );\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tpass->anim_frames[pass->anim_numframes++] = image;\n\t\t\t}\n\t\t}\n\t}\n}\nstatic void Shaderpass_AnimMap (parsestate_t *ps, const char **ptr)\n{\n\tShaderpass_AnimMap_Flag(ps, ptr, 0);\n}\nstatic void Shaderpass_QF_AnimClampMap (parsestate_t *ps, const char **ptr)\n{\n\tShaderpass_AnimMap_Flag(ps, ptr, IF_CLAMP);\n}\n\nstatic void Shaderpass_ClampMap (parsestate_t *ps, const char **ptr)\n{\n\tshaderpass_t *pass = ps->pass;\n\tint flags;\n\tchar *token;\n\n\ttoken = Shader_ParseSensString (ptr);\n\n\tflags = Shader_SetImageFlags (ps, pass, &token, IF_CLAMP);\n\tif (!Shaderpass_MapGen(ps, pass, token))\n\t{\n\t\tif (pass->tcgen == TC_GEN_UNSPECIFIED)\n\t\t\tpass->tcgen = TC_GEN_BASE;\n\t\tpass->anim_frames[0] = Shader_FindImage (ps, token, flags);\n\t\tpass->texgen = T_GEN_SINGLEMAP;\n\n\t\tif (!TEXVALID(pass->anim_frames[0]))\n\t\t{\n\t\t\tif ((flags & IF_TEXTYPEMASK)!=IF_TEXTYPE_2D)\n\t\t\t\tpass->anim_frames[0] = r_nulltex;\n\t\t\telse\n\t\t\t\tpass->anim_frames[0] = missing_texture;\n\t\t\tCon_DPrintf (CON_WARNING \"Shader %s has a stage with no image: %s.\\n\", ps->s->name, token);\n\t\t}\n\t}\n}\n\nstatic void Shaderpass_VideoMap (parsestate_t *ps, const char **ptr)\n{\n\tchar\t\t*token = Shader_ParseSensString (ptr);\n\n#ifndef HAVE_MEDIA_DECODER\n\t(void)token;\n#else\n\tshader_t *shader = ps->s;\n\tshaderpass_t *pass = ps->pass;\n\n\tif (pass->cin)\n\t\tZ_Free (pass->cin);\n\n\tpass->cin = Media_StartCin(token);\n\tif (!pass->cin)\n\t{\n\t\tCon_DPrintf (CON_WARNING \"(shader %s) Couldn't load video %s\\n\", shader->name, token);\n\t}\n\n\tif (pass->cin)\n\t{\n\t\tpass->flags |= SHADER_PASS_VIDEOMAP;\n\t\tshader->flags |= SHADER_VIDEOMAP;\n\t\tpass->texgen = T_GEN_VIDEOMAP;\n\t}\n\telse\n\t{\n\t\tpass->texgen = T_GEN_DIFFUSE;\n\t\tpass->rgbgen = RGB_GEN_CONST;\n\t\tpass->rgbgen_func.type = SHADER_FUNC_CONSTANT;\n\t\tpass->rgbgen_func.args[0] = pass->rgbgen_func.args[1] = pass->rgbgen_func.args[2] = 0;\n\t}\n#endif\n}\n\nstatic void Shaderpass_RTCW_Map_16bit (parsestate_t *ps, const char **ptr)\n{\n\tif (!gl_load24bit.ival)\t//urm, not sure if suitable choice of cvar\n\t\tShaderpass_Map(ps, ptr);\n}\nstatic void Shaderpass_RTCW_Map_32bit (parsestate_t *ps, const char **ptr)\n{\n\tif (gl_load24bit.ival)\n\t\tShaderpass_Map(ps, ptr);\n}\nstatic void Shaderpass_RTCW_Map_s3tc (parsestate_t *ps, const char **ptr)\n{\n\tif (sh_config.texfmt[PTI_BC3_RGBA] && gl_compress.ival)\n\t\tShaderpass_Map(ps, ptr);\n}\nstatic void Shaderpass_RTCW_Map_nos3tc (parsestate_t *ps, const char **ptr)\n{\n\tif (!(sh_config.texfmt[PTI_BC3_RGBA] && gl_compress.ival))\n\t\tShaderpass_Map(ps, ptr);\n}\nstatic void Shaderpass_RTCW_AnimMap_s3tc (parsestate_t *ps, const char **ptr)\n{\n\tif ((sh_config.texfmt[PTI_BC3_RGBA] && gl_compress.ival))\n\t\tShaderpass_AnimMap(ps, ptr);\n}\nstatic void Shaderpass_RTCW_AnimMap_nos3tc (parsestate_t *ps, const char **ptr)\n{\n\tif (!(sh_config.texfmt[PTI_BC3_RGBA] && gl_compress.ival))\n\t\tShaderpass_AnimMap(ps, ptr);\n}\n\nstatic void Shader_BeginPass(parsestate_t *ps);\nstatic void Shader_EndPass(parsestate_t *ps);\nstatic void Shaderpass_CoD_NextBundle (parsestate_t *ps, const char **ptr)\n{\t//in a pass... end it and start the next. cos annoying.\n\tshaderpass_t *basepass = ps->pass;\n\tshaderpass_t *newpass;\n\tif (!basepass->numMergedPasses)\n\t\tbasepass->numMergedPasses = 1;\t//its explicit...\n\tif (ps->s->numpasses == SHADER_PASS_MAX || ps->s->numpasses == SHADER_TMU_MAX)\n\t\tps->droppass = true;\n\telse\n\t{\n\t\tbasepass->numMergedPasses++;\n\t\tps->s->numpasses++;\n\t}\n\tnewpass = ps->s->passes+ps->s->numpasses-1;\n\tmemset(newpass, 0, sizeof(*newpass));\n\tnewpass->numMergedPasses++;\n\n\tnewpass->tcgen = TC_GEN_UNSPECIFIED;\n\tnewpass->shaderbits |= SBITS_SRCBLEND_DST_COLOR | SBITS_DSTBLEND_ZERO;\n\tnewpass->shaderbits |= SBITS_DEPTHFUNC_EQUAL;\n/*\n\tqboolean depthwrite = !!(ps->pass->shaderbits & SBITS_MISC_DEPTHWRITE);\n\tqboolean dropping = ps->droppass;\n\tShader_EndPass(ps);\n\n\tShader_BeginPass(ps);\n\tps->droppass = dropping;\n\t//make it modulate.\n\tps->pass->shaderbits |= SBITS_SRCBLEND_DST_COLOR | SBITS_DSTBLEND_ZERO;\n\tif (depthwrite)\t//and if the last one is doing weird alphatest crap, copy its depthfunc status.\n\t\tps->pass->shaderbits |= SBITS_DEPTHFUNC_EQUAL;\n*/\n}\nstatic void Shaderpass_CoD_Requires (parsestate_t *ps, const char **ptr)\n{\n\tif (!Shader_EvaluateCondition(ps->s, ptr))\n\t\tps->droppass = true;\n}\nstatic void Shaderpass_CoD_texEnvCombine (parsestate_t *ps, const char **ptr)\n{\n\tint depth = 0;\n\tchar *token;\n\twhile (*(token = COM_ParseExt (&ps->ptr, true, true)))\n\t{\t//extra parsing to even out this unexpected brace without extra warnings.\n\t\tif (token[0] == '}')\n\t\t\tdepth--;\n\t\telse if (token[0] == '{')\n\t\t\tdepth++;\t//crap.\n\t\tif (!depth)\n\t\t\tbreak;\n\t}\n\tps->droppass = true;\n}\nstatic void Shaderpass_CoD_nvRegCombiners (parsestate_t *ps, const char **ptr)\n{\n\tint depth = 0;\n\tchar *token;\n\twhile (*(token = COM_ParseExt (&ps->ptr, true, true)))\n\t{\t//extra parsing to even out this unexpected brace without extra warnings.\n\t\tif (token[0] == '}')\n\t\t\tdepth--;\n\t\telse if (token[0] == '{')\n\t\t\tdepth++;\t//crap.\n\t\tif (!depth)\n\t\t\tbreak;\n\t}\n\tps->droppass = true;\n}\nstatic void Shaderpass_CoD_atiFragmentShader (parsestate_t *ps, const char **ptr)\n{\n\tint depth = 0;\n\tchar *token;\n\twhile (*(token = COM_ParseExt (&ps->ptr, true, true)))\n\t{\t//extra parsing to even out this unexpected brace without extra warnings.\n\t\tif (token[0] == '}')\n\t\t\tdepth--;\n\t\telse if (token[0] == '{')\n\t\t\tdepth++;\t//crap.\n\t\tif (!depth)\n\t\t\tbreak;\n\t}\n\tps->droppass = true;\n}\n\nstatic void Shaderpass_SLProgramName (shader_t *shader, shaderpass_t *pass, const char **ptr, int qrtype)\n{\n\t/*accepts:\n\tprogram\n\t{\n\t\tBLAH\n\t}\n\twhere BLAH is both vertex+frag with #ifdefs\n\tor\n\tprogram fname\n\ton one line.\n\t*/\n\t//char *programbody;\n\tchar *hash;\n\n\t/*programbody = Shader_ParseBody(shader->name, ptr);\n\tif (programbody)\n\t{\n\t\tshader->prog = BZ_Malloc(sizeof(*shader->prog));\n\t\tmemset(shader->prog, 0, sizeof(*shader->prog));\n\t\tshader->prog->refs = 1;\n\t\tif (!Shader_LoadPermutations(shader->name, shader->prog, programbody, qrtype, 0, NULL))\n\t\t{\n\t\t\tBZ_Free(shader->prog);\n\t\t\tshader->prog = NULL;\n\t\t}\n\n\t\tBZ_Free(programbody);\n\t\treturn;\n\t}*/\n\n\thash = strchr(shader->name, '#');\n\tif (hash)\n\t{\n\t\t//pass the # postfixes from the shader name onto the generic glsl to use\n\t\tchar newname[512];\n\t\tQ_snprintfz(newname, sizeof(newname), \"%s%s\", Shader_ParseExactString(ptr), hash);\n\t\tpass->prog = Shader_FindGeneric(newname, qrtype);\n\t}\n\telse\n\t\tpass->prog = Shader_FindGeneric(Shader_ParseExactString(ptr), qrtype);\n}\nstatic void Shaderpass_ProgramName (parsestate_t *ps, const char **ptr)\n{\n\tshader_t *shader = ps->s;\n\tshaderpass_t *pass = ps->pass;\n\tShaderpass_SLProgramName(shader,pass,ptr,qrenderer);\n}\n\nstatic void Shaderpass_RGBGen (parsestate_t *ps, const char **ptr)\n{\n\tshader_t *shader = ps->s;\n\tshaderpass_t *pass = ps->pass;\n\tchar\t\t*token;\n\n\ttoken = Shader_ParseString (ptr);\n\tif (!Q_stricmp (token, \"identitylighting\"))\n\t\tpass->rgbgen = RGB_GEN_IDENTITY_LIGHTING;\n\telse if (!Q_stricmp (token, \"identity\"))\n\t\tpass->rgbgen = RGB_GEN_IDENTITY;\n\telse if (!Q_stricmp (token, \"wave\"))\n\t{\n\t\tpass->rgbgen = RGB_GEN_WAVE;\n\t\tShader_ParseFunc (ps, \"rgbGen wave\", ptr, &pass->rgbgen_func);\n\t}\n\telse if (!Q_stricmp(token, \"entity\"))\n\t\tpass->rgbgen = RGB_GEN_ENTITY;\n\telse if (!Q_stricmp (token, \"oneMinusEntity\"))\n\t\tpass->rgbgen = RGB_GEN_ONE_MINUS_ENTITY;\n\telse if (!Q_stricmp (token, \"vertex\"))\n\t{\n\t\tpass->rgbgen = RGB_GEN_VERTEX_LIGHTING;\n\t\tif (pass->alphagen == ALPHA_GEN_UNDEFINED)\t//matches Q3, and is a perf gain, even if its inconsistent.\n\t\t\tpass->alphagen = ALPHA_GEN_VERTEX;\n\t}\n\telse if (!Q_stricmp (token, \"oneMinusVertex\"))\n\t\tpass->rgbgen = RGB_GEN_ONE_MINUS_VERTEX;\n\telse if (!Q_stricmp (token, \"lightingDiffuse\"))\n\t\tpass->rgbgen = RGB_GEN_LIGHTING_DIFFUSE;\n\telse if (!Q_stricmp (token, \"entitylighting\"))\n\t\tpass->rgbgen = RGB_GEN_ENTITY_LIGHTING_DIFFUSE;\n\telse if (!Q_stricmp (token, \"exactvertex\"))\n\t\tpass->rgbgen = RGB_GEN_VERTEX_EXACT;\n\telse if (!Q_stricmp (token, \"const\") || !Q_stricmp (token, \"constant\")\n\t\t|| !Q_stricmp (token, \"constLighting\"))\n\t{\n\t\tpass->rgbgen = RGB_GEN_CONST;\n\t\tpass->rgbgen_func.type = SHADER_FUNC_CONSTANT;\n\n\t\tShader_ParseVector (shader, ptr, pass->rgbgen_func.args);\n\t}\n\telse if (!Q_stricmp (token, \"srgb\") || !Q_stricmp (token, \"srgbconst\"))\n\t{\n\t\tpass->rgbgen = RGB_GEN_CONST;\n\t\tpass->rgbgen_func.type = SHADER_FUNC_CONSTANT;\n\n\t\tShader_ParseVector (shader, ptr, pass->rgbgen_func.args);\n\n\t\tpass->rgbgen_func.args[0] = SRGBf(pass->rgbgen_func.args[0]);\n\t\tpass->rgbgen_func.args[1] = SRGBf(pass->rgbgen_func.args[1]);\n\t\tpass->rgbgen_func.args[2] = SRGBf(pass->rgbgen_func.args[2]);\n\t}\n\telse if (!Q_stricmp (token, \"topcolor\"))\n\t\tpass->rgbgen = RGB_GEN_TOPCOLOR;\n\telse if (!Q_stricmp (token, \"bottomcolor\"))\n\t\tpass->rgbgen = RGB_GEN_BOTTOMCOLOR;\n}\n\nstatic void Shaderpass_AlphaGen (parsestate_t *ps, const char **ptr)\n{\n\tshader_t *shader = ps->s;\n\tshaderpass_t *pass = ps->pass;\n\tchar\t\t*token;\n\n\ttoken = Shader_ParseString(ptr);\n\tif (!Q_stricmp (token, \"portal\"))\n\t{\n\t\tpass->alphagen = ALPHA_GEN_PORTAL;\n\t\tshader->portaldist = Shader_ParseFloat(shader, ptr, 256);\n\t\tif (!shader->portaldist)\n\t\t\tshader->portaldist = 256;\n\t\tshader->flags |= SHADER_AGEN_PORTAL;\n\t}\n\telse if (!Q_stricmp (token, \"vertex\"))\n\t{\n\t\tpass->alphagen = ALPHA_GEN_VERTEX;\n\t}\n\telse if (!Q_stricmp (token, \"entity\"))\n\t{\n\t\tpass->alphagen = ALPHA_GEN_ENTITY;\n\t}\n\telse if (!Q_stricmp (token, \"wave\"))\n\t{\n\t\tpass->alphagen = ALPHA_GEN_WAVE;\n\n\t\tShader_ParseFunc (ps, \"alphaGen wave\", ptr, &pass->alphagen_func);\n\t}\n\telse if ( !Q_stricmp (token, \"lightingspecular\"))\n\t{\n\t\tpass->alphagen = ALPHA_GEN_SPECULAR;\n\t}\n\telse if ( !Q_stricmp (token, \"const\") || !Q_stricmp (token, \"constant\"))\n\t{\n\t\tpass->alphagen = ALPHA_GEN_CONST;\n\t\tpass->alphagen_func.type = SHADER_FUNC_CONSTANT;\n\t\tpass->alphagen_func.args[0] = fabs(Shader_ParseFloat(shader, ptr, 0));\n\t}\n}\nstatic void Shaderpass_AlphaShift (parsestate_t *ps, const char **ptr)\n{\n\tshader_t *shader = ps->s;\n\tshaderpass_t *pass = ps->pass;\n\tfloat speed;\n\tfloat min, max;\n\tpass->alphagen = ALPHA_GEN_WAVE;\n\n\tpass->alphagen_func.type = SHADER_FUNC_SIN;\n\n\n\t//arg0 = add\n\t//arg1 = scale\n\t//arg2 = timeshift\n\t//arg3 = timescale\n\n\tspeed = Shader_ParseFloat(shader, ptr, 0);\n\tmin = Shader_ParseFloat(shader, ptr, 0);\n\tmax = Shader_ParseFloat(shader, ptr, 0);\n\n\tpass->alphagen_func.args[0] = min + (max - min)/2;\n\tpass->alphagen_func.args[1] = (max - min)/2;\n\tpass->alphagen_func.args[2] = 0;\n\tpass->alphagen_func.args[3] = 1/speed;\n}\n\nstatic int Shader_BlendFactor(char *name, qboolean dstnotsrc)\n{\n\tint factor;\n\tif (!strnicmp(name, \"gl_\", 3))\n\t\tname += 3;\n\n\tif (!Q_stricmp(name, \"zero\"))\n\t\tfactor = SBITS_SRCBLEND_ZERO;\n\telse if ( !Q_stricmp(name, \"one\"))\n\t\tfactor = SBITS_SRCBLEND_ONE;\n\telse if (!Q_stricmp(name, \"dst_color\"))\n\t\tfactor = SBITS_SRCBLEND_DST_COLOR;\n\telse if (!Q_stricmp(name, \"one_minus_src_alpha\"))\n\t\tfactor = SBITS_SRCBLEND_ONE_MINUS_SRC_ALPHA;\n\telse if (!Q_stricmp(name, \"src_alpha\"))\n\t\tfactor = SBITS_SRCBLEND_SRC_ALPHA;\n\telse if (!Q_stricmp(name, \"src_color\"))\n\t\tfactor = SBITS_SRCBLEND_SRC_COLOR_INVALID;\n\telse if (!Q_stricmp(name, \"one_minus_dst_color\"))\n\t\tfactor = SBITS_SRCBLEND_ONE_MINUS_DST_COLOR;\n\telse if (!Q_stricmp(name, \"one_minus_src_color\"))\n\t\tfactor = SBITS_SRCBLEND_ONE_MINUS_SRC_COLOR_INVALID;\n\telse if (!Q_stricmp(name, \"dst_alpha\") )\n\t\tfactor = SBITS_SRCBLEND_DST_ALPHA;\n\telse if (!Q_stricmp(name, \"one_minus_dst_alpha\"))\n\t\tfactor = SBITS_SRCBLEND_ONE_MINUS_DST_ALPHA;\n\telse\n\t\tfactor = SBITS_SRCBLEND_NONE;\n\n\tif (dstnotsrc)\n\t{\n\t\t//dest factors are shifted\n\t\tfactor <<= 4;\n\n\t\t/*gl doesn't accept dst_color for destinations*/\n\t\tif (factor == SBITS_DSTBLEND_NONE ||\n\t\t\tfactor == SBITS_DSTBLEND_DST_COLOR_INVALID ||\n\t\t\tfactor == SBITS_DSTBLEND_ONE_MINUS_DST_COLOR_INVALID ||\n\t\t\tfactor == SBITS_DSTBLEND_ALPHA_SATURATE_INVALID)\n\t\t{\n\t\t\tCon_DPrintf(\"Invalid shader dst blend \\\"%s\\\"\\n\", name);\n\t\t\tfactor = SBITS_DSTBLEND_ONE;\n\t\t}\n\t}\n\telse\n\t{\n\t\t/*gl doesn't accept src_color for sources*/\n\t\tif (factor == SBITS_SRCBLEND_NONE ||\n\t\t\tfactor == SBITS_SRCBLEND_SRC_COLOR_INVALID ||\n\t\t\tfactor == SBITS_SRCBLEND_ONE_MINUS_SRC_COLOR_INVALID)\n\t\t{\n\t\t\tCon_DPrintf(\"Unrecognised shader src blend \\\"%s\\\"\\n\", name);\n\t\t\tfactor = SBITS_SRCBLEND_ONE;\n\t\t}\n\t}\n\n\treturn factor;\n}\n\nstatic void Shaderpass_BlendFunc (parsestate_t *ps, const char **ptr)\n{\n\tshaderpass_t *pass = ps->pass;\n\tchar\t\t*token;\n\n\tif (pass->numMergedPasses>1)\n\t\tpass = ps->s->passes+ps->s->numpasses-1;\t//nextbundle stuff.\n\n\t//reset to defaults\n\tpass->shaderbits &= ~(SBITS_BLEND_BITS);\n\tpass->stagetype = ST_AMBIENT;\n\n\ttoken = Shader_ParseString (ptr);\n\tif ( !Q_stricmp (token, \"bumpmap\"))\t\t\t\t//doom3 is awkward...\n\t\tpass->stagetype = ST_BUMPMAP;\n\telse if ( !Q_stricmp (token, \"specularmap\"))\t//doom3 is awkward...\n\t\tpass->stagetype = ST_SPECULARMAP;\n\telse if ( !Q_stricmp (token, \"diffusemap\"))\t\t//doom3 is awkward...\n\t\tpass->stagetype = ST_DIFFUSEMAP;\n\telse if ( !Q_stricmp (token, \"blend\"))\n\t\tpass->shaderbits |= SBITS_SRCBLEND_SRC_ALPHA | SBITS_DSTBLEND_ONE_MINUS_SRC_ALPHA;\n\telse if ( !Q_stricmp (token, \"premul\"))\t\t\t//gets rid of feathering.\n\t\tpass->shaderbits |= SBITS_SRCBLEND_ONE | SBITS_DSTBLEND_ONE_MINUS_SRC_ALPHA;\n\telse if (!Q_stricmp (token, \"filter\"))\n\t\tpass->shaderbits |= SBITS_SRCBLEND_DST_COLOR | SBITS_DSTBLEND_ZERO;\n\telse if (!Q_stricmp (token, \"add\"))\n\t\tpass->shaderbits |= SBITS_SRCBLEND_ONE | SBITS_DSTBLEND_ONE;\n\telse if (!Q_stricmp (token, \"replace\"))\n\t\tpass->shaderbits |= SBITS_SRCBLEND_NONE | SBITS_DSTBLEND_NONE;\n\telse\n\t{\n\t\tpass->shaderbits |= Shader_BlendFactor(token, false);\n\n\t\ttoken = Shader_ParseString (ptr);\n\t\tif (*token == ',')\n\t\t\ttoken = Shader_ParseString (ptr);\n\t\tpass->shaderbits |= Shader_BlendFactor(token, true);\n\t}\n}\n\nstatic void Shaderpass_AlphaFunc (parsestate_t *ps, const char **ptr)\n{\n\tshaderpass_t *pass = ps->pass;\n\tchar *token;\n\n\tpass->shaderbits &= ~SBITS_ATEST_BITS;\n\n\ttoken = Shader_ParseString (ptr);\n\tif (!Q_stricmp (token, \"gt0\"))\n\t{\n\t\tpass->shaderbits |= SBITS_ATEST_GT0;\n\t}\n\telse if (!Q_stricmp (token, \"lt128\"))\n\t{\n\t\tpass->shaderbits |= SBITS_ATEST_LT128;\n\t}\n\telse if (!Q_stricmp (token, \"ge128\"))\n\t{\n\t\tpass->shaderbits |= SBITS_ATEST_GE128;\n\t}\n}\n\nstatic void Shaderpass_DepthFunc (parsestate_t *ps, const char **ptr)\n{\n\tshaderpass_t *pass = ps->pass;\n\tchar *token;\n\n\tpass->shaderbits &= ~(SBITS_DEPTHFUNC_BITS);\n\n\ttoken = Shader_ParseString (ptr);\n\tif (!Q_stricmp (token, \"equal\"))\n\t\tpass->shaderbits |= SBITS_DEPTHFUNC_EQUAL;\n\telse if (!Q_stricmp (token, \"lequal\"))\n\t\tpass->shaderbits |= SBITS_DEPTHFUNC_CLOSEREQUAL;\t//default\n\telse if (!Q_stricmp (token, \"less\"))\n\t\tpass->shaderbits |= SBITS_DEPTHFUNC_CLOSER;\n\telse if (!Q_stricmp (token, \"greater\"))\n\t\tpass->shaderbits |= SBITS_DEPTHFUNC_FURTHER;\n//\telse if (!Q_stricmp (token, \"gequal\"))\n//\t\tpass->shaderbits |= SBITS_DEPTHFUNC_FURTHEREQUAL;\n//\telse if (!Q_stricmp (token, \"nequal\"))\n//\t\tpass->shaderbits |= SBITS_DEPTHFUNC_NOTEQUAL;\n//\telse if (!Q_stricmp (token, \"never\"))\n//\t\tpass->shaderbits |= SBITS_DEPTHFUNC_NEVER;\n//\telse if (!Q_stricmp (token, \"always\"))\n//\t\tpass->shaderbits |= SBITS_DEPTHFUNC_ALWAYS;\n\telse\n\t\tCon_DPrintf(\"Invalid depth func %s\\n\", token);\n}\n\nstatic void Shaderpass_DepthWrite (parsestate_t *ps, const char **ptr)\n{\n\tshader_t *shader = ps->s;\n\tshaderpass_t *pass = ps->pass;\n\tshader->flags |= SHADER_DEPTHWRITE;\n\tpass->shaderbits |= SBITS_MISC_DEPTHWRITE;\n}\n\nstatic void Shaderpass_NoDepthTest (parsestate_t *ps, const char **ptr)\n{\n\tshader_t *shader = ps->s;\n\tshaderpass_t *pass = ps->pass;\n\tshader->flags |= SHADER_DEPTHWRITE;\n\tpass->shaderbits |= SBITS_MISC_NODEPTHTEST;\n}\n\nstatic void Shaderpass_NoDepth (parsestate_t *ps, const char **ptr)\n{\n\tshader_t *shader = ps->s;\n\tshader->flags |= SHADER_DEPTHWRITE;\n}\n\nstatic void Shaderpass_TcMod (parsestate_t *ps, const char **ptr)\n{\n\tshader_t *shader = ps->s;\n\tshaderpass_t *pass = ps->pass;\n\tint i;\n\ttcmod_t *tcmod;\n\tchar *token;\n\n\tif (pass->numMergedPasses>1)\n\t\tpass = ps->s->passes+ps->s->numpasses-1;\t//nextbundle stuff.\n\n\tif (pass->numtcmods >= SHADER_MAX_TC_MODS)\n\t{\n\t\treturn;\n\t}\n\n\ttcmod = &pass->tcmods[pass->numtcmods];\n\n\ttoken = Shader_ParseString (ptr);\n\tif (!Q_stricmp (token, \"rotate\"))\n\t{\n\t\ttcmod->args[0] = -Shader_ParseFloat(shader, ptr, 0) / 360.0f;\n\t\tif (!tcmod->args[0])\n\t\t{\n\t\t\treturn;\n\t\t}\n\n\t\ttcmod->type = SHADER_TCMOD_ROTATE;\n\t}\n\telse if ( !Q_stricmp (token, \"scale\") )\n\t{\n\t\ttcmod->args[0] = Shader_ParseFloat (shader, ptr, 0);\n\t\ttcmod->args[1] = Shader_ParseFloat (shader, ptr, 0);\n\t\ttcmod->type = SHADER_TCMOD_SCALE;\n\t}\n\telse if ( !Q_stricmp (token, \"scroll\") )\n\t{\n\t\ttcmod->args[0] = Shader_ParseFloat (shader, ptr, 0);\n\t\ttcmod->args[1] = Shader_ParseFloat (shader, ptr, 0);\n\t\ttcmod->type = SHADER_TCMOD_SCROLL;\n\t}\n\telse if (!Q_stricmp(token, \"stretch\"))\n\t{\n\t\tshaderfunc_t func;\n\n\t\tShader_ParseFunc(ps, \"tcmod stretch\", ptr, &func);\n\n\t\ttcmod->args[0] = func.type;\n\t\tfor (i = 1; i < 5; ++i)\n\t\t\ttcmod->args[i] = func.args[i-1];\n\t\ttcmod->type = SHADER_TCMOD_STRETCH;\n\t}\n\telse if (!Q_stricmp (token, \"transform\"))\n\t{\n\t\tfor (i = 0; i < 6; ++i)\n\t\t\ttcmod->args[i] = Shader_ParseFloat (shader, ptr, 0);\n\t\ttcmod->type = SHADER_TCMOD_TRANSFORM;\n\t}\n\telse if (!Q_stricmp (token, \"turb\"))\n\t{\n\t\tfor (i = 0; i < 4; i++)\n\t\t\ttcmod->args[i] = Shader_ParseFloat (shader, ptr, 0);\n\t\ttcmod->type = SHADER_TCMOD_TURB;\n\t}\n\telse if (!Q_stricmp (token, \"page\"))\n\t{\n\t\tfor (i = 0; i < 3; i++)\n\t\t\ttcmod->args[i] = Shader_ParseFloat (shader, ptr, 0);\n\t\ttcmod->type = SHADER_TCMOD_PAGE;\n\t}\n//\telse if (!Q_stricmp (token, \"entityTranslate\"))\t//RTCW\n//\telse if (!Q_stricmp (token, \"swap\"))\t\t\t//RTCW\n\telse\n\t{\n\t\tCon_DPrintf(\"Unknown tcmod %s in %s\\n\", token, shader->name);\n\t\treturn;\n\t}\n\n\tpass->numtcmods++;\n}\n\nstatic void Shaderpass_Scale (parsestate_t *ps, const char **ptr)\n{\n\tshader_t *shader = ps->s;\n\tshaderpass_t *pass = ps->pass;\n\t//seperate x and y\n\tchar *token;\n\ttcmod_t *tcmod;\n\n\ttcmod = &pass->tcmods[pass->numtcmods];\n\n\ttcmod->type = SHADER_TCMOD_SCALE;\n\n\ttoken = Shader_ParseString (ptr);\n\tif (!strcmp(token, \"static\"))\n\t{\n\t\ttcmod->args[0] = Shader_ParseFloat (shader, ptr, 0);\n\t}\n\telse\n\t{\n\t\ttcmod->args[0] = atof(token);\n\t}\n\n\twhile (**ptr == ' ' || **ptr == '\\t')\n\t\t*ptr+=1;\n\tif (**ptr == ',')\n\t\t*ptr+=1;\n\n\ttoken = Shader_ParseString (ptr);\n\tif (!strcmp(token, \"static\"))\n\t{\n\t\ttcmod->args[1] = Shader_ParseFloat (shader, ptr, 0);\n\t}\n\telse\n\t{\n\t\ttcmod->args[1] = atof(token);\n\t}\n\n\tpass->numtcmods++;\n}\n\nstatic void Shaderpass_Scroll (parsestate_t *ps, const char **ptr)\n{\n\tshader_t *shader = ps->s;\n\tshaderpass_t *pass = ps->pass;\n\t//seperate x and y\n\tchar *token;\n\ttcmod_t *tcmod;\n\n\ttcmod = &pass->tcmods[pass->numtcmods];\n\n\ttoken = Shader_ParseString ( ptr );\n\tif (!strcmp(token, \"static\"))\n\t{\n\t\ttcmod->type = SHADER_TCMOD_SCROLL;\n\t\ttcmod->args[0] = Shader_ParseFloat (shader, ptr, 0);\n\t}\n\telse\n\t{\n\t\tCon_DPrintf(\"Bad shader scroll value\\n\");\n\t\treturn;\n\t}\n\n\ttoken = Shader_ParseString ( ptr );\n\tif (!strcmp(token, \"static\"))\n\t{\n\t\ttcmod->type = SHADER_TCMOD_SCROLL;\n\t\ttcmod->args[1] = Shader_ParseFloat (shader, ptr, 0);\n\t}\n\telse\n\t{\n\t\tCon_DPrintf(\"Bad shader scroll value\\n\");\n\t\treturn;\n\t}\n\n\tpass->numtcmods++;\n}\n\n\nstatic void Shaderpass_TcGen (parsestate_t *ps, const char **ptr)\n{\n\tshader_t *shader = ps->s;\n\tshaderpass_t *pass = ps->pass;\n\tchar *token;\n\n\tif (pass->numMergedPasses>1)\n\t\tpass = ps->s->passes+ps->s->numpasses-1;\t//nextbundle stuff.\n\n\ttoken = Shader_ParseString ( ptr );\n\tif ( !Q_stricmp (token, \"base\") ) {\n\t\tpass->tcgen = TC_GEN_BASE;\n\t} else if ( !Q_stricmp (token, \"lightmap\") ) {\n\t\tpass->tcgen = TC_GEN_LIGHTMAP;\n\t} else if ( !Q_stricmp (token, \"environment\") ) {\n\t\tpass->tcgen = TC_GEN_ENVIRONMENT;\n\t} else if ( !Q_stricmp (token, \"fireriseenv\") ) {\t//from RTCW\n\t\tpass->tcgen = TC_GEN_ENVIRONMENT;\t//FIXME: not supported\n\t} else if ( !Q_stricmp (token, \"vector\") )\n\t{\n\t\tpass->tcgen = TC_GEN_VECTOR;\n\t\tShader_ParseVector (shader, ptr, pass->tcgenvec[0]);\n\t\tShader_ParseVector (shader, ptr, pass->tcgenvec[1]);\n\t} else if ( !Q_stricmp (token, \"normal\") ) {\n\t\tpass->tcgen = TC_GEN_NORMAL;\n\t} else if ( !Q_stricmp (token, \"svector\") ) {\n\t\tpass->tcgen = TC_GEN_SVECTOR;\n\t} else if ( !Q_stricmp (token, \"tvector\") ) {\n\t\tpass->tcgen = TC_GEN_TVECTOR;\n\t} else if ( !Q_stricmp (token, \"skybox\") ) {\n\t\tpass->tcgen = TC_GEN_SKYBOX;\n\t}\n}\nstatic void Shaderpass_EnvMap (parsestate_t *ps, const char **ptr)\n{\n\tshaderpass_t *pass = ps->pass;\n\tpass->tcgen = TC_GEN_ENVIRONMENT;\n}\n\nstatic void Shaderpass_Detail (parsestate_t *ps, const char **ptr)\n{\n\tshaderpass_t *pass = ps->pass;\n\tpass->flags |= SHADER_PASS_DETAIL;\n}\n\nstatic void Shaderpass_AlphaMask (parsestate_t *ps, const char **ptr)\n{\n\tshaderpass_t *pass = ps->pass;\n\tpass->shaderbits &= ~SBITS_ATEST_BITS;\n\tpass->shaderbits |= SBITS_ATEST_GE128;\n}\n\nstatic void Shaderpass_NoLightMap (parsestate_t *ps, const char **ptr)\n{\n\tshaderpass_t *pass = ps->pass;\n\tpass->rgbgen = RGB_GEN_IDENTITY;\n}\n\nstatic void Shaderpass_Red(parsestate_t *ps, const char **ptr)\n{\n\tshader_t *shader = ps->s;\n\tshaderpass_t *pass = ps->pass;\n\tpass->rgbgen = RGB_GEN_CONST;\n\tpass->rgbgen_func.args[0] = Shader_ParseFloat(shader, ptr, 0);\n}\nstatic void Shaderpass_Green(parsestate_t *ps, const char **ptr)\n{\n\tshader_t *shader = ps->s;\n\tshaderpass_t *pass = ps->pass;\n\tpass->rgbgen = RGB_GEN_CONST;\n\tpass->rgbgen_func.args[1] = Shader_ParseFloat(shader, ptr, 0);\n}\nstatic void Shaderpass_Blue(parsestate_t *ps, const char **ptr)\n{\n\tshader_t *shader = ps->s;\n\tshaderpass_t *pass = ps->pass;\n\tpass->rgbgen = RGB_GEN_CONST;\n\tpass->rgbgen_func.args[2] = Shader_ParseFloat(shader, ptr, 0);\n}\nstatic void Shaderpass_Alpha(parsestate_t *ps, const char **ptr)\n{\n\tshader_t *shader = ps->s;\n\tshaderpass_t *pass = ps->pass;\n\tpass->alphagen = ALPHA_GEN_CONST;\n\tpass->alphagen_func.args[0] = Shader_ParseFloat(shader, ptr, 0);\n}\nstatic void Shaderpass_MaskColor(parsestate_t *ps, const char **ptr)\n{\n\tshaderpass_t *pass = ps->pass;\n\tpass->shaderbits |= SBITS_MASK_RED|SBITS_MASK_GREEN|SBITS_MASK_BLUE;\n}\nstatic void Shaderpass_MaskRed(parsestate_t *ps, const char **ptr)\n{\n\tshaderpass_t *pass = ps->pass;\n\tpass->shaderbits |= SBITS_MASK_RED;\n}\nstatic void Shaderpass_MaskGreen(parsestate_t *ps, const char **ptr)\n{\n\tshaderpass_t *pass = ps->pass;\n\tpass->shaderbits |= SBITS_MASK_GREEN;\n}\nstatic void Shaderpass_MaskBlue(parsestate_t *ps, const char **ptr)\n{\n\tshaderpass_t *pass = ps->pass;\n\tpass->shaderbits |= SBITS_MASK_BLUE;\n}\nstatic void Shaderpass_MaskAlpha(parsestate_t *ps, const char **ptr)\n{\n\tshaderpass_t *pass = ps->pass;\n\tpass->shaderbits |= SBITS_MASK_ALPHA;\n}\nstatic void Shaderpass_AlphaTest(parsestate_t *ps, const char **ptr)\n{\n\tshader_t *shader = ps->s;\n\tshaderpass_t *pass = ps->pass;\n\tif (Shader_ParseFloat(shader, ptr, 0) == 0.5)\n\t\tpass->shaderbits |= SBITS_ATEST_GE128;\n\telse\n\t\tCon_Printf(\"unsupported alphatest value\\n\");\n}\nstatic void Shaderpass_TexGen(parsestate_t *ps, const char **ptr)\n{\n\tshaderpass_t *pass = ps->pass;\n\tchar *token = Shader_ParseString(ptr);\n\tif (!strcmp(token, \"normal\"))\n\t\tpass->tcgen = TC_GEN_NORMAL;\n\telse if (!strcmp(token, \"skybox\"))\n\t\tpass->tcgen = TC_GEN_SKYBOX;\n\telse if (!strcmp(token, \"wobblesky\"))\n\t{\n\t\tpass->tcgen = TC_GEN_WOBBLESKY;\n\t\ttoken = Shader_ParseString(ptr);\n\t\ttoken = Shader_ParseString(ptr);\n\t\ttoken = Shader_ParseString(ptr);\n\t}\n\telse if (!strcmp(token, \"reflect\"))\n\t\tpass->tcgen = TC_GEN_REFLECT;\n\telse\n\t{\n\t\tCon_Printf(\"texgen token not understood\\n\");\n\t}\n}\nstatic void Shaderpass_CubeMap(parsestate_t *ps, const char **ptr)\n{\n\tshaderpass_t *pass = ps->pass;\n\tchar *token = Shader_ParseString(ptr);\n\n\tif (pass->tcgen == TC_GEN_UNSPECIFIED)\n\t\tpass->tcgen = TC_GEN_SKYBOX;\n\tpass->anim_frames[0] = Shader_FindImage(ps, token, IF_TEXTYPE_CUBE);\n\tpass->texgen = T_GEN_SINGLEMAP;\n\n\tif (!TEXVALID(pass->anim_frames[0]))\n\t{\n\t\tpass->texgen = T_GEN_SINGLEMAP;\n\t\tpass->anim_frames[0] = missing_texture;\n\t}\n}\n\nstatic shaderkey_t shaderpasskeys[] =\n{\n#define Q3 NULL\n\t{\"rgbgen\",\t\tShaderpass_RGBGen,\t\t\tQ3},\n\t{\"alphagen\",\tShaderpass_AlphaGen,\t\tQ3},\n\t{\"blendfunc\",\tShaderpass_BlendFunc,\t\tQ3},\n\t{\"depthfunc\",\tShaderpass_DepthFunc,\t\tQ3},\n\t{\"depthwrite\",\tShaderpass_DepthWrite,\t\tQ3},\n\t{\"alphafunc\",\tShaderpass_AlphaFunc,\t\tQ3},\n\t{\"tcmod\",\t\tShaderpass_TcMod,\t\t\tQ3},\n\t{\"map\",\t\t\tShaderpass_Map,\t\t\t\tQ3},\n\t{\"animmap\",\t\tShaderpass_AnimMap,\t\t\tQ3},\n\t{\"clampmap\",\tShaderpass_ClampMap,\t\tQ3},\n\t{\"videomap\",\tShaderpass_VideoMap,\t\tQ3},\n\t{\"tcgen\",\t\tShaderpass_TcGen,\t\t\tQ3},\n\t{\"texgen\",\t\tShaderpass_TcGen,\t\t\tQ3},\n\t{\"detail\",\t\tShaderpass_Detail,\t\t\tQ3},\n\n\t{\"nodepthtest\",\tShaderpass_NoDepthTest,\t\tNULL},\n\t{\"nodepth\",\t\tShaderpass_NoDepth,\t\t\tNULL},\n\n\t{\"envmap\",\t\tShaderpass_EnvMap,\t\t\t\"rscript\"},//for alienarena\n\t{\"nolightmap\",\tShaderpass_NoLightMap,\t\t\"rscript\"},//for alienarena\n\t{\"scale\",\t\tShaderpass_Scale,\t\t\t\"rscript\"},//for alienarena\n\t{\"scroll\",\t\tShaderpass_Scroll,\t\t\t\"rscript\"},//for alienarena\n\t{\"alphashift\",\tShaderpass_AlphaShift,\t\t\"rscript\"},//for alienarena\n\t{\"alphamask\",\tShaderpass_AlphaMask,\t\t\"rscript\"},//for alienarena\n\n\t{\"program\",\t\tShaderpass_ProgramName,\t\t\"fte\"},\n\t\n\t/*doom3 compat*/\n\t{\"blend\",\t\tShaderpass_BlendFunc,\t\t\"doom3\"},\n\t{\"maskcolor\",\tShaderpass_MaskColor,\t\t\"doom3\"},\n\t{\"maskred\",\t\tShaderpass_MaskRed,\t\t\t\"doom3\"},\n\t{\"maskgreen\",\tShaderpass_MaskGreen,\t\t\"doom3\"},\n\t{\"maskblue\",\tShaderpass_MaskBlue,\t\t\"doom3\"},\n\t{\"maskalpha\",\tShaderpass_MaskAlpha,\t\t\"doom3\"},\n\t{\"alphatest\",\tShaderpass_AlphaTest,\t\t\"doom3\"},\n\t{\"texgen\",\t\tShaderpass_TexGen,\t\t\t\"doom3\"},\n\t{\"cubemap\",\t\tShaderpass_CubeMap,\t\t\t\"doom3\"},\t//one of these is wrong\n\t{\"cameracubemap\",Shaderpass_CubeMap,\t\t\"doom3\"},\t//one of these is wrong\n\t{\"red\",\t\t\tShaderpass_Red,\t\t\t\t\"doom3\"},\n\t{\"green\",\t\tShaderpass_Green,\t\t\t\"doom3\"},\n\t{\"blue\",\t\tShaderpass_Blue,\t\t\t\"doom3\"},\n\t{\"alpha\",\t\tShaderpass_Alpha,\t\t\t\"doom3\"},\n\n\n\t//RTCW\n\t//fancy map lines use the map if that mode is active.\n\t//FIXME: actually check these to ensure there's no issues with any shaders overriding the pass's previously specified map\n\t//      (hopefully no shaders would actually do that due to the engine loading both textures, which would be wasteful)\n\t{\"map16\",\t\tShaderpass_RTCW_Map_16bit,\t\t\"rtcw\"},\n\t{\"map32\",\t\tShaderpass_RTCW_Map_32bit,\t\t\"rtcw\"},\n\t{\"mapcomp\",\t\tShaderpass_RTCW_Map_s3tc,\t\t\"rtcw\"},\n\t{\"mapnocomp\",\tShaderpass_RTCW_Map_nos3tc,\t\t\"rtcw\"},\n\t{\"animcompmap\",\tShaderpass_RTCW_AnimMap_s3tc,\t\"rtcw\"},\n\t{\"animnocompmap\",Shaderpass_RTCW_AnimMap_nos3tc,\"rtcw\"},\n\n\t//qfusion/warsow compat\n\t{\"material\",\tShaderpass_QF_Material,\t\t\"qf\"},\n\t{\"animclampmap\",Shaderpass_QF_AnimClampMap,\t\"qf\"},\n//\t{\"cubemap\",\t\tShaderpass_QF_CubeMap,\t\t\"qf\"},\n//\t{\"shadecubemap\",Shaderpass_QF_ShadeCubeMap,\t\"qf\"},\n\t{\"surroundmap\",\tShaderpass_CubeMap,\t\t\t\"qf\"},\n//\t{\"distortion\",\tShaderpass_QF_Distortion,\t\"qf\"},\n//\t{\"celshade\",\tShaderpass_QF_Celshade,\t\t\"qf\"},\n//\t{\"grayscale\",\tShaderpass_QF_Greyscale,\t\"qf\"},\n//\t{\"greyscale\",\tShaderpass_QF_Greyscale,\t\"qf\"},\n//\t{\"skip\",\t\tShaderpass_QF_Skip,\t\t\t\"qf\"},\n\n\t{\"nextbundle\",\t\t\tShaderpass_CoD_NextBundle,\t\t\t\"cod\"},\n\t{\"requires\",\t\t\tShaderpass_CoD_Requires,\t\t\t\"cod\"},\n\t{\"texEnvCombine\",\t\tShaderpass_CoD_texEnvCombine,\t\t\"cod\"},\n\t{\"nvRegCombiners\",\t\tShaderpass_CoD_nvRegCombiners,\t\t\"cod\"},\n\t{\"atiFragmentShader\",\tShaderpass_CoD_atiFragmentShader,\t\"cod\"},\n\n\t{NULL,\t\t\tNULL}\n};\n\n// ===============================================================\n\n\nvoid Shader_FreePass (shaderpass_t *pass)\n{\n#ifdef HAVE_MEDIA_DECODER\n\tif ( pass->flags & SHADER_PASS_VIDEOMAP )\n\t{\n\t\tMedia_ShutdownCin(pass->cin);\n\t\tpass->cin = NULL;\n\t}\n#endif\n\n\tif (pass->prog)\n\t{\n\t\tShader_ReleaseGeneric(pass->prog);\n\t\tpass->prog = NULL;\n\t}\n}\n\nvoid Shader_ReleaseGeneric(program_t *prog)\n{\n\tif (prog)\n\t\tif (prog->refs-- == 1)\n\t\t\tShader_UnloadProg(prog);\n}\nvoid Shader_Free (shader_t *shader)\n{\n\tint i;\n\tshaderpass_t *pass;\n\n\tif (shader->bucket.data == shader)\n\t\tHash_RemoveData(&shader_active_hash, shader->name, shader);\n\tshader->bucket.data = NULL;\n\n\tShader_ReleaseGeneric(shader->prog);\n\tshader->prog = NULL;\n\n\tif (shader->skydome)\n\t\tZ_Free (shader->skydome);\n\tshader->skydome = NULL;\n\twhile (shader->clutter)\n\t{\n\t\tvoid *t = shader->clutter;\n\t\tshader->clutter = shader->clutter->next;\n\t\tZ_Free(t);\n\t}\n\n\tpass = shader->passes;\n\tfor (i = 0; i < shader->numpasses; i++, pass++)\n\t{\n\t\tShader_FreePass (pass);\n\t}\n\tshader->numpasses = 0;\n\n\tif (shader->genargs)\n\t{\n\t\tfree(shader->genargs);\n\t\tshader->genargs = NULL;\n\t}\n\tshader->uses = 0;\n\n\tZ_Free(shader->defaulttextures);\n\tshader->defaulttextures = NULL;\n}\n\n\n\n\n\nint QDECL Shader_InitCallback (const char *name, qofs_t size, time_t mtime, void *param, searchpathfuncs_t *spath)\n{\n\tShader_MakeCache(name, SPF_DEFAULT);\n\treturn true;\n}\nint QDECL Shader_InitCallback_Doom3 (const char *name, qofs_t size, time_t mtime, void *param, searchpathfuncs_t *spath)\n{\n\tShader_MakeCache(name, SPF_DOOM3);\n\treturn true;\n}\n\nqboolean Shader_Init (void)\n{\n\tint wibuf[16];\n\n\tif (!r_shaders)\n\t{\n\t\tr_numshaders = 0;\n\t\tr_maxshaders = 256;\n\t\tr_shaders = calloc(r_maxshaders, sizeof(*r_shaders));\n\n\t\tshader_hash = calloc (HASH_SIZE, sizeof(*shader_hash));\n\n\t\tshader_active_hash_mem = malloc(Hash_BytesForBuckets(1024));\n\t\tmemset(shader_active_hash_mem, 0, Hash_BytesForBuckets(1024));\n\t\tHash_InitTable(&shader_active_hash, 1024, shader_active_hash_mem);\n\n\t\tShader_FlushGenerics();\n\n\t\tif (!sh_config.progs_supported)\n\t\t\tsh_config.max_gpu_bones = 0;\n\t\telse\n\t\t{\n\t\t\textern cvar_t r_max_gpu_bones;\n\t\t\tif (!*r_max_gpu_bones.string)\n\t\t\t{\n#ifdef FTE_TARGET_WEB\n\t\t\t\tsh_config.max_gpu_bones = 0;\t//webgl tends to crap out if this is too high, so 32 is a good enough value to play safe. some browsers have really shitty uniform performance too, so lets just default to pure-cpu transforms. in javascript. yes, its that bad.\n#else\n\t\t\t\t//some of our APIs will set their own guesses from queries. don't stomp on that.\n\t\t\t\tif (!sh_config.max_gpu_bones)\n\t\t\t\t\tsh_config.max_gpu_bones = 64;\t//ATI drivers bug out and start to crash if you put this at 128.\n#endif\n\t\t\t}\n\t\t\telse\n\t\t\t\tsh_config.max_gpu_bones = bound(0, r_max_gpu_bones.ival, MAX_BONES);\n\t\t}\n\t}\n\t\n\tif (!qrenderer)\n\t\tr_whiteimage = r_nulltex;\n\telse\n\t{\n\t\tmemset(wibuf, 0xff, sizeof(wibuf));\n\t\tr_whiteimage = R_LoadTexture(\"$whiteimage\", 4, 4, TF_RGBA32, wibuf, IF_NOMIPMAP|IF_NOPICMIP|IF_NEAREST|IF_NOGAMMA|IF_NOPURGE);\n\t\tmemset(wibuf, 0, sizeof(wibuf));\n\t\tr_blackimage = R_LoadTexture(\"$blackimage\", 4, 4, TF_RGBA32, wibuf, IF_NOMIPMAP|IF_NOPICMIP|IF_NEAREST|IF_NOGAMMA|IF_NOPURGE);\n\t}\n\n\tShader_NeedReload(true);\n\tShader_DoReload();\n\treturn true;\n}\n\nvoid Shader_FlushCache(void)\n{\n\tshadercachefile_t *sf;\n\tshadercache_t *cache, *cache_next;\n\tint i;\n\n\tfor (i = 0; i < HASH_SIZE; i++)\n\t{\n\t\tcache = shader_hash[i];\n\t\tshader_hash[i] = NULL;\n\n\t\tfor (; cache; cache = cache_next)\n\t\t{\n\t\t\tcache_next = cache->hash_next;\n\t\t\tcache->hash_next = NULL;\n\t\t\tZ_Free(cache);\n\t\t}\n\t}\n\n\twhile(shaderfiles)\n\t{\n\t\tsf = shaderfiles;\n\t\tshaderfiles = sf->next;\n\t\tif (sf->data)\n\t\t\tFS_FreeFile(sf->data);\n\t\tZ_Free(sf);\n\t}\n}\n\nstatic void Shader_MakeCache(const char *path, unsigned int parseflags)\n{\n\tunsigned int key;\n\tconst char *buf, *ptr;\n\tchar *token;\n\tshadercache_t *cache;\n\tshadercachefile_t *cachefile, *filelink = NULL;\n\tqofs_t size;\n\n\tfor (cachefile = shaderfiles; cachefile; cachefile = cachefile->next)\n\t{\n\t\tif (!Q_stricmp(cachefile->name, path))\n\t\t\treturn;\t//already loaded. there's no source package or anything.\n\t\tfilelink = cachefile;\n\t}\n\n\t\n\tCon_DPrintf (\"...loading '%s'\\n\", path);\n\n\tcachefile = Z_Malloc(sizeof(*cachefile) + strlen(path));\n\tstrcpy(cachefile->name, path);\n\tsize = FS_LoadFile(path, (void **)&cachefile->data);\n\tcachefile->length = size;\n\tcachefile->parseflags = parseflags;\n\tif (filelink)\n\t\tfilelink->next = cachefile;\n\telse\n\t\tshaderfiles = cachefile;\n\n\tif (qofs_Error(size))\n\t{\n\t\tCon_Printf(\"Unable to read %s\\n\", path);\n\t\tcachefile->length = 0;\n\t\treturn;\n\t}\n\tif (size > 1024*1024*64)\t//sanity limit\n\t{\n\t\tCon_Printf(\"Refusing to parse %s due to size\\n\", path);\n\t\tcachefile->length = 0;\n\t\tFS_FreeFile(cachefile->data);\n\t\tcachefile->data = NULL;\n\t\treturn;\n\t}\n\n\tptr = buf = cachefile->data;\n\tsize = cachefile->length;\n\n\t//look for meta comments.\n\twhile (1)\n\t{\n\t\t//parse metas\n\t\twhile (*ptr == ' ' || *ptr == '\\t')\n\t\t\tptr++;\n\t\tif (ptr[0] == '\\r' && ptr[1] == '\\n')\n\t\t\tptr+=2;\t//blank line with dos ending\n\t\telse if (ptr[0] == '\\r' || ptr[0] == '\\n')\n\t\t\tptr+=1;\t//blank line with mac or unix ending\n\t\telse if (ptr[0] == '/' && ptr[1] == '/')\n\t\t{\n\t\t\tconst char *e = strchr(ptr, '\\n');\n\t\t\tif (e)\n\t\t\t\te++;\n\t\t\telse\n\t\t\t\te = ptr + strlen(ptr);\n\n\t\t\tptr += 2;\n\t\t\twhile (*ptr == ' ' || *ptr == '\\t')\n\t\t\t\tptr++;\n\t\t\tif (!strncmp(ptr, \"meta:\", 5))\n\t\t\t{\n\t\t\t\tptr+=5;\n\n\t\t\t\ttoken = COM_ParseExt (&ptr, false, true);\n\t\t\t\tif (!strcmp(token, \"forceprogramify\"))\n\t\t\t\t{\n\t\t\t\t\tcachefile->parseflags |= SPF_PROGRAMIFY;\n\t\t\t\t\ttoken = COM_ParseExt (&ptr, false, true);\n\t\t\t\t\tif (*token)\n\t\t\t\t\t\tQ_strncpyz(cachefile->forcedshadername, token, sizeof(cachefile->forcedshadername));\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tCon_DPrintf(\"unknown shader meta term \\\"%s\\\" in %s\\n\", token, path);\n\n\t\t\t\twhile (*ptr == ' ' || *ptr == '\\t')\n\t\t\t\t\tptr++;\n\t\t\t\tif (*ptr != '\\r' && *ptr != '\\n')\n\t\t\t\t{\n\t\t\t\t\twhile (*ptr && (*ptr != '\\r' && *ptr != '\\n'))\n\t\t\t\t\t\tptr++;\n\t\t\t\t\tCon_DPrintf(\"junk after shader meta in %s\\n\", path);\n\t\t\t\t}\n\t\t\t}\n\t\t\tptr = e;\n\t\t}\n\t\telse\n\t\t\tbreak;\t//the actual shader started.\n\t}\n\n\t//now scan the file looking for each individual shader.\n\tdo\n\t{\n\t\tif ( ptr - buf >= size )\n\t\t\tbreak;\n\n\t\ttoken = COM_ParseExt (&ptr, true, true);\n\t\tif ( !token[0] || ptr - buf >= size )\n\t\t\tbreak;\n\n\t\tCOM_CleanUpPath(token);\n\n\t\tif (Shader_LocateSource(token, NULL, NULL, NULL, NULL))\n\t\t{\n\t\t\tptr = Shader_Skip (path, token, ptr);\n\t\t\tcontinue;\n\t\t}\n\n\t\tkey = Hash_Key ( token, HASH_SIZE );\n\n\t\tcache = ( shadercache_t * )Z_Malloc(sizeof(shadercache_t) + strlen(token));\n\t\tstrcpy(cache->name, token);\n\t\tcache->hash_next = shader_hash[key];\n\t\tcache->source = cachefile;\n\t\tcache->offset = ptr - cachefile->data;\n\n\t\tshader_hash[key] = cache;\n\n\t\tptr = Shader_Skip (path, cache->name, ptr);\n\t} while ( ptr );\n}\n\nstatic qboolean Shader_LocateSource(const char *name, const char **buf, size_t *bufsize, size_t *offset, shadercachefile_t **sourcefile)\n{\n\tunsigned int key;\n\tshadercache_t *cache;\n\n\tkey = Hash_Key ( name, HASH_SIZE );\n\tcache = shader_hash[key];\n\n\tfor ( ; cache; cache = cache->hash_next )\n\t{\n\t\tif ( !Q_stricmp (cache->name, name) )\n\t\t{\n\t\t\tif (buf)\n\t\t\t{\n\t\t\t\t*buf = cache->source->data;\n\t\t\t\t*bufsize = cache->source->length;\n\t\t\t\t*offset = cache->offset;\n\t\t\t\t*sourcefile = cache->source;\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\t}\n\treturn false;\n}\n\nstatic const char *Shader_Skip(const char *file, const char *shadername, const char *ptr)\n{\n\tchar *tok;\n\tint brace_count;\n\n    // Opening brace\n\ttok = COM_ParseExt(&ptr, true, true);\n\n\tif (!ptr)\n\t\treturn NULL;\n\n\tif ( tok[0] != '{' )\n\t{\n\t\ttok = COM_ParseExt (&ptr, true, true);\n\t}\n\n\tfor (brace_count = 1; brace_count > 0; )\n\t{\n\t\ttok = COM_ParseExt (&ptr, true, true);\n\n\t\tif ( !tok[0] )\n\t\t{\n\t\t\tCon_Printf(CON_WARNING\"%s: unexpected EOF parsing %s\\n\", file, shadername);\n\t\t\treturn NULL;\n\t\t}\n\n\t\tif (tok[0] == '{')\n\t\t{\n\t\t\tif (r_forceprogramify.ival > 1 && brace_count == 2)\n\t\t\t\tCon_Printf(CON_WARNING\"%s: excess indentation depth while parsing shader \\\"%s\\\" (%s==%i)\\n\", file, shadername, r_forceprogramify.name, r_forceprogramify.ival);\n\t\t\telse\n\t\t\t\tbrace_count++;\n\t\t} else if (tok[0] == '}')\n\t\t{\n\t\t\tbrace_count--;\n\t\t}\n\t}\n\n\treturn ptr;\n}\n\nstatic void Shader_Reset(parsestate_t *ps)\n{\n\tshader_t *s = ps->s;\n\textern cvar_t r_refractreflect_scale;\n\tchar name[MAX_QPATH];\n\tint id = s->id;\n\tint uses = s->uses;\n\tshader_gen_t *defaultgen = s->generator;\n\tchar *genargs = s->genargs;\n\ttexnums_t *dt = s->defaulttextures;\n\tint dtcount = s->numdefaulttextures;\n\tfloat dtrate = s->defaulttextures_fps;\t//FIXME!\n\tint w = s->width;\n\tint h = s->height;\n\tmodel_t *mod = s->model;\n\tunsigned int uf = s->usageflags;\n\tQ_strncpyz(name, s->name, sizeof(name));\n\ts->genargs = NULL;\n\ts->defaulttextures = NULL;\n\tShader_Free(s);\n\tmemset(s, 0, sizeof(*s));\n\ts->portalfboscale = r_refractreflect_scale.value;\t//unless otherwise specified, this cvar specifies the value.\n\n\ts->remapto = s;\n\ts->id = id;\n\ts->width = w;\n\ts->height = h;\n\ts->model = mod;\n\ts->defaulttextures = dt;\n\ts->numdefaulttextures = dtcount;\n\ts->defaulttextures_fps = dtrate;\n\ts->generator = defaultgen;\n\ts->genargs = genargs;\n\ts->usageflags = uf;\n\ts->uses = uses;\n\tQ_strncpyz(s->name, name, sizeof(s->name));\n\tHash_Add(&shader_active_hash, s->name, s, &s->bucket);\n}\n\nstatic void Shader_Regenerate(parsestate_t *ps, const char *shortname)\n{\n\tShader_Reset(ps);\n\n\tif (!strcmp(shortname, \"textures/common/clip\") || !strcmp(shortname, \"textures/common/nodraw\") || !strcmp(shortname, \"common/nodraw\"))\n\t\tShader_DefaultScript(ps, shortname,\n\t\t\t\"{\\n\"\n\t\t\t\t\"surfaceparm nodraw\\n\"\n\t\t\t\t\"surfaceparm nodlight\\n\"\n\t\t\t\"}\\n\");\n\telse\n\t\tps->s->generator(ps, shortname, ps->s->genargs);\n}\n\nvoid Shader_Shutdown (void)\n{\n\tint i;\n\tshader_t *shader;\n\n\tif (!r_shaders)\n\t\treturn;\t/*nothing needs freeing yet*/\n\tfor (i = 0; i < r_numshaders; i++)\n\t{\n\t\tshader = r_shaders[i];\n\t\tif (!shader)\n\t\t\tcontinue;\n\n\t\tShader_Free(shader);\n\t\tZ_Free(r_shaders[i]);\n\t\tr_shaders[i] = NULL;\n\t}\n\n\tShader_FlushCache();\n\tShader_FlushGenerics();\n\n\tR_SkyShutdown();\n\n\tr_maxshaders = 0;\n\tr_numshaders = 0;\n\n\tfree(r_shaders);\n\tr_shaders = NULL;\n\tfree(shader_hash);\n\tshader_hash = NULL;\n\tfree(shader_active_hash_mem);\n\tshader_active_hash_mem = NULL;\n\n\tshader_reload_needed = false;\n}\n\nstatic void Shader_SetBlendmode (shaderpass_t *pass, shaderpass_t *lastpass)\n{\n\tqboolean lightmapoverbright;\n\tif (pass->texgen == T_GEN_DELUXMAP)\n\t{\n\t\tpass->blendmode = PBM_DOTPRODUCT;\n\t\treturn;\n\t}\n\n\tif (pass->texgen < T_GEN_DIFFUSE && !TEXVALID(pass->anim_frames[0]) && !(pass->flags & SHADER_PASS_LIGHTMAP))\n\t{\n\t\tpass->blendmode = PBM_MODULATE;\n\t\treturn;\n\t}\n\n\tif (!(pass->shaderbits & SBITS_BLEND_BITS))\n\t{\n\t\tif (pass->texgen == T_GEN_LIGHTMAP && lastpass)\n\t\t\tpass->blendmode = PBM_OVERBRIGHT;\n\t\telse if ((pass->rgbgen == RGB_GEN_IDENTITY) && (pass->alphagen == ALPHA_GEN_IDENTITY))\n\t\t{\n\t\t\tpass->blendmode = PBM_REPLACE;\n\t\t\treturn;\n\t\t}\n\t\telse if ((pass->rgbgen == RGB_GEN_IDENTITY_LIGHTING) && (pass->alphagen == ALPHA_GEN_IDENTITY))\n\t\t{\n\t\t\tpass->shaderbits &= ~SBITS_BLEND_BITS;\n\t\t\tpass->shaderbits |= SBITS_SRCBLEND_ONE;\n\t\t\tpass->shaderbits |= SBITS_DSTBLEND_ZERO;\n\t\t\tpass->blendmode = PBM_REPLACELIGHT;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tpass->shaderbits &= ~SBITS_BLEND_BITS;\n\t\t\tpass->shaderbits |= SBITS_SRCBLEND_ONE;\n\t\t\tpass->shaderbits |= SBITS_DSTBLEND_ZERO;\n\t\t\tpass->blendmode = PBM_MODULATE;\n\t\t}\n\t\treturn;\n\t}\n\n\tlightmapoverbright = pass->texgen == T_GEN_LIGHTMAP || (lastpass && lastpass->texgen == T_GEN_LIGHTMAP && lastpass->blendmode != PBM_OVERBRIGHT);\n\n\tif (((pass->shaderbits&SBITS_BLEND_BITS) == (SBITS_SRCBLEND_ZERO|SBITS_DSTBLEND_SRC_COLOR)) ||\n\t\t((pass->shaderbits&SBITS_BLEND_BITS) == (SBITS_SRCBLEND_DST_COLOR|SBITS_DSTBLEND_ZERO)) ||\n\t\t((pass->shaderbits&SBITS_BLEND_BITS) == (SBITS_SRCBLEND_DST_COLOR|SBITS_DSTBLEND_ONE_MINUS_DST_ALPHA)))\n\t\tpass->blendmode = lightmapoverbright?PBM_OVERBRIGHT:PBM_MODULATE;\n\telse if ((pass->shaderbits&SBITS_BLEND_BITS) == (SBITS_SRCBLEND_ONE|SBITS_DSTBLEND_ONE))\n\t\tpass->blendmode = PBM_ADD;\n\telse if ((pass->shaderbits&SBITS_BLEND_BITS) == (SBITS_SRCBLEND_SRC_ALPHA|SBITS_DSTBLEND_ONE_MINUS_SRC_ALPHA))\n\t\tpass->blendmode = PBM_DECAL;\n\telse\n\t\tpass->blendmode = lightmapoverbright?PBM_OVERBRIGHT:PBM_MODULATE;\n}\n\nstatic void Shader_FixupProgPasses(parsestate_t *ps, shaderpass_t *pass)\n{\n\tshader_t *shader = ps->s;\n\tint i;\n\tint maxpasses = SHADER_PASS_MAX - (pass-shader->passes);\n\tstruct\n\t{\n\t\tint gen;\n\t\tunsigned int flags;\n\t} defaulttgen[] =\n\t{\n\t\t//light\n\t\t{T_GEN_SHADOWMAP,\t\t0},\t\t\t\t\t\t//0\n\t\t{T_GEN_LIGHTCUBEMAP,\t0},\t\t\t\t\t\t//1\n\n\t\t//material\n\t\t{T_GEN_DIFFUSE,\t\t\tSHADER_HASDIFFUSE},\t\t//2\n\t\t{T_GEN_NORMALMAP,\t\tSHADER_HASNORMALMAP},\t//3\n\t\t{T_GEN_SPECULAR,\t\tSHADER_HASGLOSS},\t\t//4\n\t\t{T_GEN_UPPEROVERLAY,\tSHADER_HASTOPBOTTOM},\t//5\n\t\t{T_GEN_LOWEROVERLAY,\tSHADER_HASTOPBOTTOM},\t//6\n\t\t{T_GEN_FULLBRIGHT,\t\tSHADER_HASFULLBRIGHT},\t//7\n\t\t{T_GEN_PALETTED,\t\tSHADER_HASPALETTED},\t//8\n\t\t{T_GEN_REFLECTCUBE,\t\tSHADER_HASREFLECTCUBE},\t//9\n\t\t{T_GEN_REFLECTMASK,\t\t0},\t\t\t\t\t\t//10\n\t\t{T_GEN_DISPLACEMENT,\tSHADER_HASDISPLACEMENT},//11\n\t\t{T_GEN_OCCLUSION,\t\t0},\t\t\t\t\t\t//12\n\t\t{T_GEN_TRANSMISSION,\t0},\t\t\t\t\t\t//13\n\t\t{T_GEN_THICKNESS,\t\t0},\t\t\t\t\t\t//14\n//\t\t\t{T_GEN_REFLECTION,\t\tSHADER_HASREFLECT},\t\t//\n//\t\t\t{T_GEN_REFRACTION,\t\tSHADER_HASREFRACT},\t\t//\n//\t\t\t{T_GEN_REFRACTIONDEPTH,\tSHADER_HASREFRACTDEPTH},//\n//\t\t\t{T_GEN_RIPPLEMAP,\t\tSHADER_HASRIPPLEMAP},\t//\n\n\t\t//batch\n\t\t{T_GEN_LIGHTMAP,\t\tSHADER_HASLIGHTMAP},\t//15\n\t\t{T_GEN_DELUXMAP,\t\t0},\t\t\t\t\t\t//16\n\t\t//more lightmaps\t\t\t\t\t\t\t\t//17,18,19\n\t\t//mode deluxemaps\t\t\t\t\t\t\t\t//20,21,22\n\t};\n\n#ifdef HAVE_MEDIA_DECODER\n\tcin_t *cin = R_ShaderGetCinematic(shader);\n#endif\n\n\t//if the glsl doesn't specify all samplers, just trim them.\n\tpass->numMergedPasses = pass->prog->numsamplers;\n\n#ifdef HAVE_MEDIA_DECODER\n\tif (cin && R_ShaderGetCinematic(shader) == cin)\n\t\tcin = NULL;\n#endif\n\n\t//if the glsl has specific textures listed, be sure to provide a pass for them.\n\tfor (i = 0; i < sizeof(defaulttgen)/sizeof(defaulttgen[0]); i++)\n\t{\n\t\tif (pass->prog->defaulttextures & (1u<<i))\n\t\t{\n\t\t\tif (pass->numMergedPasses >= maxpasses)\n\t\t\t{\t//panic...\n\t\t\t\tps->droppass = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tpass[pass->numMergedPasses].flags |= SHADER_PASS_NOCOLORARRAY;\n\t\t\tpass[pass->numMergedPasses].flags &= ~SHADER_PASS_DEPTHCMP;\n\t\t\tif (defaulttgen[i].gen == T_GEN_SHADOWMAP)\n\t\t\t\tpass[pass->numMergedPasses].flags |= SHADER_PASS_DEPTHCMP;\n#ifdef HAVE_MEDIA_DECODER\n\t\t\tif (!i && cin)\n\t\t\t{\n\t\t\t\tpass[pass->numMergedPasses].texgen = T_GEN_VIDEOMAP;\n\t\t\t\tpass[pass->numMergedPasses].cin = cin;\n\t\t\t\tcin = NULL;\n\t\t\t}\n\t\t\telse\n#endif\n\t\t\t{\n\t\t\t\tpass[pass->numMergedPasses].texgen = defaulttgen[i].gen;\n#ifdef HAVE_MEDIA_DECODER\n\t\t\t\tpass[pass->numMergedPasses].cin = NULL;\n#endif\n\t\t\t}\n\t\t\tpass->numMergedPasses++;\n\t\t\tshader->flags |= defaulttgen[i].flags;\n\t\t}\n\t}\n\n\t//must have at least one texture.\n\tif (!pass->numMergedPasses)\n\t{\n#ifdef HAVE_MEDIA_DECODER\n\t\tpass[0].texgen = cin?T_GEN_VIDEOMAP:T_GEN_DIFFUSE;\n\t\tpass[0].cin = cin;\n#else\n\t\tpass[0].texgen = T_GEN_DIFFUSE;\n#endif\n\t\tpass->numMergedPasses = 1;\n\t}\n#ifdef HAVE_MEDIA_DECODER\n\telse if (cin)\n\t\tMedia_ShutdownCin(cin);\n#endif\n\n\tshader->numpasses = (pass-shader->passes)+pass->numMergedPasses;\n}\n\nstruct scondinfo_s\n{\n\tint depth;\n\tint level[8];\n#define COND_IGNORE\t\t\t1\n#define COND_IGNOREPARENT\t2\n#define COND_ALLOWELSE\t\t4\n#define COND_TAKEN\t\t\t8\n};\nstatic qboolean Shader_Conditional_Read(parsestate_t *ps, struct scondinfo_s *cond, const char *token, const char **ptr)\n{\n\tshader_t *shader = ps->s;\n\tif (ps->parseflags & SPF_DOOM3)\n\t\treturn false;\t//doom materials have conditionals that remove passes, without endifs. don't misparse here.\n\telse if (!Q_stricmp(token, \"if\"))\n\t{\n\t\tif (cond->depth+1 == countof(cond->level))\n\t\t{\n\t\t\tCon_Printf(\"if statements nest too deeply in shader %s\\n\", shader->name);\n\t\t\t*ptr += strlen(*ptr);\n\t\t\treturn true;\n\t\t}\n\t\tcond->depth++;\n\t\tcond->level[cond->depth] = (Shader_EvaluateCondition(shader, ptr)?0:COND_IGNORE);\n\t\tcond->level[cond->depth] |= COND_ALLOWELSE;\n\t\tif (cond->level[cond->depth-1] & (COND_IGNORE|COND_IGNOREPARENT))\n\t\t\tcond->level[cond->depth] |= COND_IGNOREPARENT;\t//if ignoring the parent, ignore this one too, even if valid\n\t\tif (!(cond->level[cond->depth] & (COND_IGNORE|COND_IGNOREPARENT)))\n\t\t\tcond->level[cond->depth] |= COND_TAKEN;\t\t//if we're not ignoring the contained commands then flag it so we don't take any elifs/elses\n\t}\n\telse if (!Q_stricmp(token, \"elif\"))\n\t{\n\t\tif (cond->level[cond->depth] & COND_ALLOWELSE)\n\t\t{\n\t\t\tif (cond->level[cond->depth] & COND_TAKEN)\n\t\t\t{\t//if we took the if/elif then don't take this elif either\n\t\t\t\tShader_EvaluateCondition(shader, ptr);\n\t\t\t\tcond->level[cond->depth] = COND_ALLOWELSE|COND_TAKEN|COND_IGNORE;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tcond->level[cond->depth] = (Shader_EvaluateCondition(shader, ptr)?0:COND_IGNORE);\n\t\t\t\tcond->level[cond->depth] |= COND_ALLOWELSE;\n\t\t\t}\n\t\t\tif (cond->level[cond->depth-1] & (COND_IGNORE|COND_IGNOREPARENT))\n\t\t\t\tcond->level[cond->depth] |= COND_IGNOREPARENT;\n\t\t\tif (!(cond->level[cond->depth] & (COND_IGNORE|COND_IGNOREPARENT)))\n\t\t\t\tcond->level[cond->depth] |= COND_TAKEN;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tCon_Printf(CON_WARNING\"unexpected elif statement in shader %s\\n\", shader->name);\n\t\t\t*ptr += strlen(*ptr);\n\t\t}\n\t}\n\telse if (!Q_stricmp(token, \"endif\"))\n\t{\n\t\tif (!cond->depth)\n\t\t{\n\t\t\tCon_Printf(\"endif without if in shader %s\\n\", shader->name);\n\t\t\t*ptr += strlen(*ptr);\n\t\t\treturn true;\n\t\t}\n\t\tcond->depth--;\n\t}\n\telse if (!Q_stricmp(token, \"else\"))\n\t{\n\t\tif (cond->level[cond->depth] & COND_ALLOWELSE)\n\t\t{\n\t\t\tif (cond->level[cond->depth] & COND_TAKEN)\n\t\t\t\tcond->level[cond->depth] |= COND_IGNORE;\n\t\t\telse\n\t\t\t\tcond->level[cond->depth] ^= COND_IGNORE;\n\t\t\tcond->level[cond->depth] &= ~COND_ALLOWELSE;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tCon_Printf(CON_WARNING\"unexpected else statement in shader %s\\n\", shader->name);\n\t\t\t*ptr += strlen(*ptr);\n\t\t}\n\t}\n\telse if (cond->level[cond->depth] & (COND_IGNORE|COND_IGNOREPARENT))\n\t{\n\t\t//eat it\n\t\twhile (ptr)\n\t\t{\n\t\t\ttoken = COM_ParseExt(ptr, false, true);\n\t\t\tif ( !token[0] )\n\t\t\t\tbreak;\n\t\t}\n\t}\n\telse\n\t\treturn false;\n\treturn true;\n}\n\nstatic void Shader_BeginPass(parsestate_t *ps)\n{\n\tshader_t *shader = ps->s;\n\tshaderpass_t *pass;\n\tstatic shader_t dummy;\n\tps->oldflags = shader->flags;\n\n\tif ( shader->numpasses >= SHADER_PASS_MAX )\n\t{\n\t\tps->droppass = true;\n\t\tshader = &dummy;\n\t\tshader->numpasses = 1;\n\t\tpass = shader->passes;\n\t}\n\telse\n\t{\n\t\tps->droppass = false;\n\t\tpass = &shader->passes[shader->numpasses++];\n\t}\n\n\t// Set defaults\n\tpass->flags = 0;\n\tpass->anim_frames[0] = r_nulltex;\n\tpass->anim_numframes = 0;\n\tpass->rgbgen = RGB_GEN_UNKNOWN;\n\tpass->alphagen = ALPHA_GEN_UNDEFINED;\n\tpass->tcgen = TC_GEN_UNSPECIFIED;\n\tpass->numtcmods = 0;\n\tpass->stagetype = ST_AMBIENT;\n\tpass->numMergedPasses = 0;\n\n\tif (shader->flags & SHADER_NOMIPMAPS)\n\t\tpass->flags |= SHADER_PASS_NOMIPMAP;\n\n\tps->pass = pass;\n}\nstatic void Shader_EndPass(parsestate_t *ps)\n{\n\tshader_t *shader = ps->s;\n\tshaderpass_t *pass = ps->pass;\n\n\tif (pass->alphagen == ALPHA_GEN_UNDEFINED)\n\t\tpass->alphagen = ALPHA_GEN_IDENTITY;\n\n\t//if there was no texgen, then its too late now.\n\tif (!pass->numMergedPasses)\n\t\tpass->numMergedPasses = 1;\n\n\tif (pass->tcgen == TC_GEN_UNSPECIFIED)\n\t\tpass->tcgen = TC_GEN_BASE;\n\n\tif (!ps->droppass)\n\t{\n\t\tswitch(pass->stagetype)\n\t\t{\n\t\tcase ST_DIFFUSEMAP:\n\t\t\tif (pass->texgen == T_GEN_SINGLEMAP)\n\t\t\t\tshader->defaulttextures->base = pass->anim_frames[0];\n\t\t\tbreak;\n\t\tcase ST_AMBIENT:\n\t\t\tbreak;\n\t\tcase ST_BUMPMAP:\n\t\t\tif (pass->texgen == T_GEN_SINGLEMAP)\n\t\t\t\tshader->defaulttextures->bump = pass->anim_frames[0];\n\t\t\tps->droppass = true;\t//fixme: scrolling etc may be important. but we're not doom3.\n\t\t\tbreak;\n\t\tcase ST_SPECULARMAP:\n\t\t\tif (pass->texgen == T_GEN_SINGLEMAP)\n\t\t\t\tshader->defaulttextures->specular = pass->anim_frames[0];\n\t\t\tps->droppass = true;\t//fixme: scrolling etc may be important. but we're not doom3.\n\t\t\tbreak;\n\t\t}\n\t}\n\n\t// check some things\n\n\tif (!ps->droppass)\n\t\tif ((pass->shaderbits&SBITS_BLEND_BITS) == (SBITS_SRCBLEND_ONE|SBITS_DSTBLEND_ZERO))\n\t\t{\n\t\t\tpass->shaderbits |= SBITS_MISC_DEPTHWRITE;\n\t\t\tshader->flags |= SHADER_DEPTHWRITE;\n\t\t}\n\n\tswitch (pass->rgbgen)\n\t{\n\t\tcase RGB_GEN_IDENTITY_LIGHTING:\n\t\tcase RGB_GEN_IDENTITY:\n\t\tcase RGB_GEN_CONST:\n\t\tcase RGB_GEN_WAVE:\n\t\tcase RGB_GEN_ENTITY:\n\t\tcase RGB_GEN_ONE_MINUS_ENTITY:\n\t\tcase RGB_GEN_UNKNOWN:\t// assume RGB_GEN_IDENTITY or RGB_GEN_IDENTITY_LIGHTING\n\n\t\t\tswitch (pass->alphagen)\n\t\t\t{\n\t\t\t\tcase ALPHA_GEN_IDENTITY:\n\t\t\t\tcase ALPHA_GEN_CONST:\n\t\t\t\tcase ALPHA_GEN_WAVE:\n\t\t\t\tcase ALPHA_GEN_ENTITY:\n\t\t\t\t\tpass->flags |= SHADER_PASS_NOCOLORARRAY;\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tbreak;\n\t}\n\n\t/*if ((shader->flags & SHADER_SKY) && (shader->flags & SHADER_DEPTHWRITE))\n\t{\n#ifdef warningmsg\n#pragma warningmsg(\"is this valid?\")\n#endif\n\t\tpass->shaderbits &= ~SBITS_MISC_DEPTHWRITE;\n\t}\n\t*/\n\n\t//if this pass specified a program, make sure it has all the textures that the program requires\n\tif (!ps->droppass && pass->prog)\n\t\tShader_FixupProgPasses(ps, pass);\n\n\tif (ps->droppass)\n\t{\n\t\twhile (pass->numMergedPasses > 0)\n\t\t{\n\t\t\tShader_FreePass (pass+--pass->numMergedPasses);\n\t\t\tshader->numpasses--;\n\t\t}\n\t\tshader->flags = ps->oldflags;\n\t}\n\tps->pass = NULL;\n}\nstatic void Shader_Readpass (parsestate_t *ps)\n{\n\tshader_t *shader = ps->s;\n\tconst char *token;\n\tstruct scondinfo_s cond = {0};\n\n\tShader_BeginPass(ps);\n\n\twhile ( ps->ptr )\n\t{\n\t\ttoken = COM_ParseExt (&ps->ptr, true, true);\n\n\t\tif ( !token[0] )\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\t\telse if (!Shader_Conditional_Read(ps, &cond, token, &ps->ptr))\n\t\t{\n\t\t\tif ( token[0] == '}' )\n\t\t\t\tbreak;\n\t\t\telse if (token[0] == '{')\n\t\t\t{\n\t\t\t\tint depth = 1;\n\t\t\t\tCon_Printf(CON_WARNING\"%s: unexpected indentation in %s\\n\", ps->sourcename, shader->name);\n\t\t\t\twhile (*(token = COM_ParseExt (&ps->ptr, true, true)))\n\t\t\t\t{\t//extra parsing to even out this unexpected brace without extra warnings.\n\t\t\t\t\tif (token[0] == '}')\n\t\t\t\t\t{\n\t\t\t\t\t\tif (depth--==0)\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\telse if (token[0] == '{')\n\t\t\t\t\t\tdepth++;\t//crap.\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if ( Shader_Parsetok (ps, shaderpasskeys, token) )\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\tif (cond.depth)\n\t{\n\t\tCon_Printf(\"if statements without endif in shader %s\\n\", shader->name);\n\t}\n\n\tShader_EndPass(ps);\n}\n\n//we've read the first token, now make sense of it and any args\nstatic qboolean Shader_Parsetok(parsestate_t *ps, shaderkey_t *keys, const char *token)\n{\n\tshaderkey_t *key;\n\tconst char *prefix;\n\tqboolean toolchainprefix = false;\n\n\tif (*token == '_')\n\t{\t//forward compat: make sure there's a way to shut stuff up if you're using future extensions in an outdated engine.\n\t\ttoken++;\n\t\ttoolchainprefix = true;\n\t}\n\n\t//handle known prefixes.\n\tif\t\t(!Q_strncasecmp(token, \"fte\", 3))\t\t{prefix = token; token += 3; }\n\telse if\t(!Q_strncasecmp(token, \"dp\", 2))\t\t{prefix = token; token += 2; }\n\telse if (!Q_strncasecmp(token, \"doom3\", 5))\t\t{prefix = token; token += 5; }\n\telse if (!Q_strncasecmp(token, \"rscript\", 7))\t{prefix = token; token += 7; }\n\telse if (!Q_strncasecmp(token, \"qer_\", 4))\t\t{prefix = token; token += 3; toolchainprefix = true; }\n\telse if (!Q_strncasecmp(token, \"q3map_\", 6))\t{prefix = token; token += 5; toolchainprefix = true; }\n\telse if (!Q_strncasecmp(token, \"vmap_\", 6))\t\t{prefix = token; token += 4; toolchainprefix = true; }\n\telse\tprefix = NULL;\n\tif (prefix && *token == '_')\n\t\ttoken++;\n\n\tfor (key = keys; key->keyword != NULL; key++)\n\t{\n\t\tif (!Q_stricmp (token, key->keyword))\n\t\t{\n\t\t\tif (!prefix || (prefix && key->prefix && !Q_strncasecmp(prefix, key->prefix, strlen(key->prefix))))\n\t\t\t{\n\t\t\t\tif (key->func)\n\t\t\t\t\tkey->func ( ps, &ps->ptr );\n\n\t\t\t\treturn (ps->ptr && *ps->ptr == '}' );\n\t\t\t}\n\t\t}\n\t}\n\n\tif (!toolchainprefix)\t//we don't really give a damn about prefixes owned by various toolchains - they shouldn't affect us.\n\t{\n\t\tif (prefix)\n\t\t\tCon_DPrintf(\"Unknown shader directive parsing %s: \\\"%s\\\"\\n\", ps->s->name, prefix);\n\t\telse\n\t\t\tCon_DPrintf(\"Unknown shader directive parsing %s: \\\"%s\\\"\\n\", ps->s->name, token);\n\t}\n\n\t// Next Line\n\twhile (ps->ptr)\n\t{\n\t\ttoken = COM_ParseExt(&ps->ptr, false, true);\n\t\tif ( !token[0] )\n\t\t{\n\t\t\tbreak;\n\t\t}\n\t}\n\n\treturn false;\n}\n\nstatic void Shader_SetPassFlush (shaderpass_t *pass, shaderpass_t *pass2)\n{\n\tif (((pass->flags & SHADER_PASS_DETAIL) && !r_detailtextures.value) ||\n\t\t((pass2->flags & SHADER_PASS_DETAIL) && !r_detailtextures.value) ||\n\t\t (pass->flags & SHADER_PASS_VIDEOMAP) || (pass2->flags & SHADER_PASS_VIDEOMAP))\n\t{\n\t\treturn;\n\t}\n\n\t//don't merge passes if they're got their own programs.\n\tif (pass->prog || pass2->prog)\n\t\treturn;\n\n\t/*identity alpha is required for merging*/\n\tif (pass->alphagen != ALPHA_GEN_IDENTITY || pass2->alphagen != ALPHA_GEN_IDENTITY)\n\t\treturn;\n\n\t/*don't merge passes if the hardware cannot support it*/\n\tif (pass->numMergedPasses >= be_maxpasses)\n\t\treturn;\n\n\t/*rgbgen must be identity too except if the later pass is identity_ligting, in which case all is well and we can switch the first pass to identity_lighting instead*/\n\tif (pass2->rgbgen == RGB_GEN_IDENTITY_LIGHTING && (pass2->blendmode == PBM_OVERBRIGHT || pass2->blendmode == PBM_MODULATE) && (pass->rgbgen == RGB_GEN_IDENTITY||pass->rgbgen == RGB_GEN_VERTEX_EXACT))\n\t{\n\t\tif (pass->blendmode == PBM_REPLACE)\n\t\t\tpass->blendmode = PBM_REPLACELIGHT;\n\t\tpass->rgbgen = RGB_GEN_IDENTITY_LIGHTING;\n\t\tpass2->rgbgen = RGB_GEN_IDENTITY;\n\t}\n\t/*rgbgen must be identity (or the first is identity_lighting)*/\n\telse if (pass2->rgbgen != RGB_GEN_IDENTITY || (pass->rgbgen != RGB_GEN_IDENTITY && pass->rgbgen != RGB_GEN_IDENTITY_LIGHTING))\n\t\treturn;\n\n\t/*if its alphatest, don't merge with anything other than lightmap (although equal stuff can be merged)*/\n\tif ((pass->shaderbits & SBITS_ATEST_BITS) && (((pass2->shaderbits & SBITS_DEPTHFUNC_BITS) != SBITS_DEPTHFUNC_EQUAL) || pass2->texgen != T_GEN_LIGHTMAP))\n\t\treturn;\n\n\tif ((pass->shaderbits & SBITS_MASK_BITS) != (pass2->shaderbits & SBITS_MASK_BITS))\n\t\treturn;\n\n\t// check if we can use multiple passes\n\tif (pass2->blendmode == PBM_DOTPRODUCT)\n\t{\n\t\tpass->numMergedPasses++;\n\t}\n\telse if (pass->numMergedPasses < be_maxpasses)\n\t{\n\t\tif (pass->blendmode == PBM_REPLACE || pass->blendmode == PBM_REPLACELIGHT)\n\t\t{\n\t\t\tif ((pass2->blendmode == PBM_DECAL && sh_config.tex_env_combine) ||\n\t\t\t\t(pass2->blendmode == PBM_ADD && sh_config.env_add) ||\n\t\t\t\t(pass2->blendmode && pass2->blendmode != PBM_ADD) ||\tsh_config.nv_tex_env_combine4)\n\t\t\t{\n\t\t\t\tpass->numMergedPasses++;\n\t\t\t}\n\t\t}\n\t\telse if (pass->blendmode == PBM_ADD &&\n\t\t\tpass2->blendmode == PBM_ADD && sh_config.env_add)\n\t\t{\n\t\t\tpass->numMergedPasses++;\n\t\t}\n\t\telse if ((pass->blendmode == PBM_MODULATE || pass->blendmode == PBM_OVERBRIGHT) && (pass2->blendmode == PBM_MODULATE || pass2->blendmode == PBM_OVERBRIGHT))\n\t\t{\n\t\t\tpass->numMergedPasses++;\n\t\t}\n\t\telse if (pass->blendmode == PBM_DECAL && pass2->blendmode == PBM_OVERBRIGHT)\n\t\t\tpass->numMergedPasses++;\t//HACK: allow modulating overbright passes with decal passes. overbright passes need to blend with something for their lighting to be correct, so this is a tradeoff.\n\t\telse\n\t\t\treturn;\n\t}\n\telse return;\n\n\t/*if (pass->texgen == T_GEN_LIGHTMAP && (pass->blendmode == PBM_REPLACE || pass->blendmode == PBM_REPLACELIGHT) && pass2->blendmode == PBM_MODULATE && sh_config.tex_env_combine)\n\t{\n//\t\tif (pass->rgbgen == RGB_GEN_IDENTITY)\n//\t\t\tpass->rgbgen = RGB_GEN_IDENTITY_OVERBRIGHT;\t//get the light levels right\n//\t\tpass2->blendmode = PBM_OVERBRIGHT;\n\t}\n\tif (pass2->texgen == T_GEN_LIGHTMAP && pass2->blendmode == PBM_OVERBRIGHT && sh_config.tex_env_combine)\n\t{\n//\t\tif (pass->rgbgen == RGB_GEN_IDENTITY)\n//\t\t\tpass->rgbgen = RGB_GEN_IDENTITY_OVERBRIGHT;\t//get the light levels right\n//\t\tpass->blendmode = PBM_REPLACELIGHT;\n//\t\tpass2->blendmode = PBM_OVERBRIGHT;\n\t\tpass->blendmode = PBM_OVERBRIGHT;\n\t}\n\t*/\n}\n\nstatic const char *Shader_AlphaMaskProgArgs(shader_t *s)\n{\n\tif (s->numpasses)\n\t{\n\t\t//alpha mask values ALWAYS come from the first pass.\n\t\tshaderpass_t *pass = &s->passes[0];\n\t\tswitch(pass->shaderbits & SBITS_ATEST_BITS)\n\t\t{\n\t\tdefault:\n\t\t\tbreak;\n\t\t//cases inverted. the atest is to retain the pixel, glsl is to discard (alpha OP MASK).\n\t\tcase SBITS_ATEST_GT0:\n\t\t\treturn \"#MASK=0.0#MASKLT=1\";\n\t\tcase SBITS_ATEST_LT128:\n\t\t\treturn \"#MASK=0.5\";\n\t\tcase SBITS_ATEST_GE128:\n\t\t\treturn \"#MASK=0.5#MASKLT=1\";\t//ignore the eq part.\n\t\t}\n\t}\n\treturn \"\";\n}\n\nstatic void Shader_Programify (parsestate_t *ps)\n{\n\tshader_t *s = ps->s;\n\tunsigned int reflectrefract = 0;\n\tconst char *prog = NULL;\n\tconst char *mask;\n\tchar args[1024];\n\tqboolean eightbit = false;\n/*\tenum\n\t{\n\t\tT_UNKNOWN,\n\t\tT_WALL,\n\t\tT_MODEL\n\t} type = 0;*/\n\tint i;\n\tshaderpass_t *pass, *lightmap = NULL, *modellighting = NULL, *vertexlighting = NULL;\n\tfor (i = 0; i < s->numpasses; i++)\n\t{\n\t\tpass = &s->passes[i];\n\t\tif (pass->rgbgen == RGB_GEN_LIGHTING_DIFFUSE || pass->rgbgen == RGB_GEN_ENTITY_LIGHTING_DIFFUSE)\n\t\t{\n\t\t\tif (s->usageflags & SUF_LIGHTMAP)\n\t\t\t\tlightmap = pass;\n\t\t\telse\n\t\t\t\tmodellighting = pass;\n\t\t}\n\t\telse if (pass->rgbgen == RGB_GEN_ENTITY)\n\t\t\tmodellighting = pass;\n\t\telse if (pass->rgbgen == RGB_GEN_VERTEX_LIGHTING || pass->rgbgen == RGB_GEN_VERTEX_EXACT)\n\t\t{\n\t\t\tif (s->usageflags & (SUF_LIGHTMAP|SUF_2D))\n\t\t\t\tvertexlighting = pass;\n\t\t\telse\n\t\t\t\tmodellighting = pass;\t//fucking DP morons who don't know what lightmaps are.\n\t\t}\n\t\telse if (pass->texgen == T_GEN_LIGHTMAP && pass->tcgen == TC_GEN_LIGHTMAP)\n\t\t{\n\t\t\tif (s->usageflags & SUF_LIGHTMAP)\n\t\t\t\tlightmap = pass;\n\t\t\telse\n\t\t\t\tmodellighting = pass;\t//fucking DP morons who don't know what lightmaps are.\n\t\t}\n\n\t\t/*if (pass->numtcmods || (pass->shaderbits & SBITS_ATEST_BITS))\n\t\t\treturn;\n\t\tif (pass->texgen == T_GEN_LIGHTMAP && pass->tcgen == TC_GEN_LIGHTMAP)\n\t\t\t;\n\t\telse if (pass->texgen != T_GEN_LIGHTMAP && pass->tcgen == TC_GEN_BASE)\n\t\t\t;\n\t\telse\n\t\t\treturn;*/\n\t}\n\n\tif (ps->forcedshader)\n\t\tprog = ps->forcedshader;\n\telse if (ps->dpwatertype)\n\t{\n\t\tprog = va(\"altwater%s#USEMODS#FRESNEL_EXP=2.0\"\n\t\t\t\t//variable parts\n\t\t\t\t\"#STRENGTH_REFR=%g#STRENGTH_REFL=%g\"\n\t\t\t\t\"#TINT_REFR=%g,%g,%g\"\n\t\t\t\t\"#TINT_REFL=%g,%g,%g\"\n\t\t\t\t\"#FRESNEL_MIN=%g#FRESNEL_RANGE=%g\"\n\t\t\t\t\"#ALPHA=%g\",\n\t\t\t\t//those args\n\t\t\t\t(ps->dpwatertype&1)?\"#REFLECT\":\"\",\n\t\t\t\tps->refractfactor*0.01, ps->reflectfactor*0.01,\n\t\t\t\tps->refractcolour[0],ps->refractcolour[1],ps->refractcolour[2],\n\t\t\t\tps->reflectcolour[0],ps->reflectcolour[1],ps->reflectcolour[2],\n\t\t\t\tps->reflectmin, ps->reflectmax-ps->reflectmin,\n\t\t\t\tps->wateralpha\n\t\t\t);\n\t\t//clear out blending and force regular depth.\n\t\ts->passes[0].shaderbits &= ~(SBITS_BLEND_BITS|SBITS_MISC_NODEPTHTEST|SBITS_DEPTHFUNC_BITS);\n\t\ts->passes[0].shaderbits |= SBITS_MISC_DEPTHWRITE;\n\n\t\tif (ps->dpwatertype & 1)\n\t\t\treflectrefract |= SHADER_HASREFLECT;\n\t\tif (ps->dpwatertype & 2)\n\t\t\treflectrefract |= SHADER_HASREFRACT;\n\t\tif (ps->dpwatertype & 4)\n\t\t{\n\t\t\treflectrefract |= SHADER_HASREFRACT|SHADER_HASPORTAL;\t//doubles up as a 'camera'\n\t\t\tif (s->sort == SHADER_SORT_PORTAL)\n\t\t\t\ts->sort = SHADER_SORT_OPAQUE;\t//don't do it twice.\n\t\t}\n\t}\n\telse if (modellighting)\n\t{\n\t\teightbit = r_softwarebanding && (qrenderer == QR_OPENGL) && sh_config.progs_supported;\n\t\tif (eightbit)\n\t\t\tprog = \"defaultskin#EIGHTBIT\";\n\t\telse\n\t\t\tprog = \"defaultskin\";\n\t}\n\telse if (lightmap)\n\t{\n\t\teightbit = r_softwarebanding && (qrenderer == QR_OPENGL || qrenderer == QR_VULKAN) && sh_config.progs_supported;\n\t\tif (eightbit)\n\t\t\tprog = \"defaultwall#EIGHTBIT\";\n\t\telse\n\t\t\tprog = \"defaultwall\";\n\t}\n\telse if (vertexlighting)\n\t{\n\t\tif (r_forceprogramify.ival < 0)\n\t\t\tprog = \"defaultfill\";\n\t\telse\n\t\t{\n\t\t\tpass = vertexlighting;\n\t\t\tprog = \"default2d\";\n\t\t}\n\t}\n\telse\n\t{\n\t\tif (r_forceprogramify.ival < 0)\n\t\t\tprog = \"defaultfill\";\n\t\telse\n\t\t\treturn;\n\t}\n\n\targs[0] = 0;\n\tif (ps->specularvalscale != 1)\n\t\tQ_strncatz(args, va(\"#specmul=%g\", ps->specularvalscale), sizeof(args));\n\tif (ps->specularexpscale != 1)\n\t\tQ_strncatz(args, va(\"#specexp=%g\", ps->specularexpscale), sizeof(args));\n/*\tswitch(ps->offsetmappingmode)\n\t{\n\tcase 0:\t//force off.\n\t\tQ_strncatz(args, va(\"#NOOFFSETMAPPING\", ps->specularexpscale), sizeof(args));\n\t\tbreak;\n\tcase 1:\t//force linear\n\t\tQ_strncatz(args, va(\"#NORELIEFMAPPING\", ps->specularexpscale), sizeof(args));\n\t\tbreak;\n\tcase 2:\t//force relief\n\t\tQ_strncatz(args, va(\"#RELIEFMAPPING\", ps->specularexpscale), sizeof(args));\n\t\tbreak;\n\t}\n\tif (ps->offsetmappingscale != 1)\n\t\tQ_strncatz(args, va(\"#OFFSETMAPPING_SCALE=%g\", ps->offsetmappingscale), sizeof(args));\n\tif (ps->offsetmappingbias != 0)\n\t\tQ_strncatz(args, va(\"#OFFSETMAPPING_BIAS=%g\", ps->offsetmappingbias), sizeof(args));\n*/\n\tmask = strchr(s->name, '#');\n\tif (mask)\n\t\tQ_strncatz(args, mask, sizeof(args));\n\n\tmask = Shader_AlphaMaskProgArgs(s);\n\n\ts->prog = Shader_FindGeneric(va(\"%s%s%s\", prog, mask, args), qrenderer);\n\tif (s->prog)\n\t{\n\t\ts->numpasses = 0;\n\t\tif (reflectrefract)\n\t\t{\n\t\t\tif (s->passes[0].numtcmods > 0 && s->passes[0].tcmods[0].type == SHADER_TCMOD_SCALE)\n\t\t\t{\t//crappy workaround for DP bug.\n\t\t\t\ts->passes[0].tcmods[0].args[0] *= 4;\n\t\t\t\ts->passes[0].tcmods[0].args[1] *= 4;\n\t\t\t}\n\t\t\ts->passes[s->numpasses++].texgen = T_GEN_REFRACTION;\n\t\t\ts->passes[s->numpasses++].texgen = T_GEN_REFLECTION;\n//\t\t\ts->passes[s->numpasses++].texgen = T_GEN_RIPPLEMAP;\n//\t\t\ts->passes[s->numpasses++].texgen = T_GEN_REFRACTIONDEPTH;\n\t\t\ts->flags |= reflectrefract;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (eightbit)\n\t\t\t{\n\t\t\t\ts->passes[s->numpasses].anim_frames[0] = R_LoadColourmapImage();\n\t\t\t\ts->passes[s->numpasses++].texgen = T_GEN_SINGLEMAP;\n\t\t\t}\n\t\t\telse\t\t\t\n\t\t\t\ts->passes[s->numpasses++].texgen = T_GEN_DIFFUSE;\n\t\t\ts->flags |= SHADER_HASDIFFUSE;\n\t\t}\n\t}\n}\n\nstatic void Shader_Finish (parsestate_t *ps)\n{\n\tshader_t *s = ps->s;\n\tint i;\n\tshaderpass_t *pass;\n\t\n\t//FIXME: reorder doom3 stages.\n\t//put diffuse first. give it a lightmap pass also, if we found a diffuse one with no lightmap.\n\t//then the ambient stages.\n\t//and forget about the bump/specular stages as we don't support them and already stripped them.\n\n#if 0\n\tif (s->flags & SHADER_SKY)\n\t{\n\t\t/*skies go all black if fastsky is set*/\n\t\tif (r_fastsky.ival)\n\t\t\ts->flags = 0;\n\t\t/*or if its purely a skybox and has missing textures*/\n//\t\tif (!s->numpasses)\n//\t\t\tfor (i = 0; i < 6; i++)\n//\t\t\t\tif (missing_texture.ref == s->skydome->farbox_textures[i].ref)\n//\t\t\t\t\ts->flags = 0;\n\t\tif (!(s->flags & SHADER_SKY))\n\t\t{\n\t\t\tShader_Reset(s);\n\n\t\t\tShader_DefaultScript(s->name, s,\n\t\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\t\"sort sky\\n\"\n\t\t\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\t\t\"map $whiteimage\\n\"\n\t\t\t\t\t\t\t\t\"rgbgen srgb $r_fastskycolour\\n\"\n\t\t\t\t\t\t\t\"}\\n\"\n\t\t\t\t\t\t\t\"surfaceparm nodlight\\n\"\n\t\t\t\t\t\t\"}\\n\"\n\t\t\t\t\t);\n\t\t\treturn;\n\t\t}\n\t}\n#endif\n\n\tif (s->prog && !s->numpasses)\n\t{\n\t\tpass = &s->passes[s->numpasses++];\n\t\tpass->tcgen = TC_GEN_BASE;\n\t\tpass->texgen = T_GEN_DIFFUSE;\n\t\tpass->shaderbits |= SBITS_MISC_DEPTHWRITE;\n\t\tpass->rgbgen = RGB_GEN_IDENTITY;\n\t\tpass->alphagen = ALPHA_GEN_IDENTITY;\n\t\tpass->numMergedPasses = 1;\n\t\tShader_SetBlendmode(pass, NULL);\n\t}\n\n\tif (!s->numpasses && s->sort != SHADER_SORT_PORTAL && !(s->flags & (SHADER_NODRAW|SHADER_SKY)) && !s->fog_dist)\n\t{\n\t\tif (r_forceprogramify.ival >= 2)\n\t\t{\n\t\t\tCon_DPrintf(\"Shader %s with no passes, forcing nodraw\\n\", s->name);\n\t\t\ts->flags |= SHADER_NODRAW;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tpass = &s->passes[s->numpasses++];\n\t\t\tpass = &s->passes[0];\n\t\t\tpass->tcgen = TC_GEN_BASE;\n\t\t\tif (TEXVALID(s->defaulttextures->base))\n\t\t\t\tpass->texgen = T_GEN_DIFFUSE;\n\t\t\telse\n\t\t\t{\n\t\t\t\tpass->texgen = T_GEN_SINGLEMAP;\n\t\t\t\tTEXASSIGN(pass->anim_frames[0], R_LoadHiResTexture(s->name, NULL, IF_NOALPHA));\n\t\t\t\tif (!TEXVALID(pass->anim_frames[0]))\n\t\t\t\t{\n\t\t\t\t\tCon_Printf(\"Shader %s failed to load default texture\\n\", s->name);\n\t\t\t\t\tpass->anim_frames[0] = missing_texture;\n\t\t\t\t}\n\t\t\t\tCon_Printf(\"Shader %s with no passes and no surfaceparm nodraw, inserting pass\\n\", s->name);\n\t\t\t}\n\t\t\tpass->shaderbits |= SBITS_MISC_DEPTHWRITE;\n\t\t\tpass->rgbgen = RGB_GEN_VERTEX_LIGHTING;\n\t\t\tpass->alphagen = ALPHA_GEN_IDENTITY;\n\t\t\tpass->numMergedPasses = 1;\n\t\t\tShader_SetBlendmode(pass, NULL);\n\t\t}\n\t}\n\n\tif (!Q_stricmp (s->name, \"flareShader\"))\n\t{\n\t\ts->flags |= SHADER_FLARE;\n\t\ts->flags |= SHADER_NODRAW;\n\t}\n\n\tif (!s->numpasses && !s->sort)\n\t{\n\t\ts->sort = SHADER_SORT_ADDITIVE;\n\t\treturn;\n\t}\n\n\tif (!s->sort && s->passes->texgen == T_GEN_CURRENTRENDER)\n\t\ts->sort = SHADER_SORT_NEAREST;\n\n\n\tif ((s->polyoffset.unit < 0) && !s->sort)\n\t{\n\t\ts->sort = SHADER_SORT_DECAL;\n\t}\n\n\tif ((r_vertexlight.value || !(s->usageflags & SUF_LIGHTMAP)) && !s->prog)\n\t{\n\t\t// do we have a lightmap pass?\n\t\tpass = s->passes;\n\t\tfor (i = 0; i < s->numpasses; i++, pass++)\n\t\t{\n\t\t\tif (pass->flags & SHADER_PASS_LIGHTMAP)\n\t\t\t\tbreak;\n\t\t}\n\n\t\tif (i == s->numpasses)\n\t\t{\n\t\t\tgoto done;\n\t\t}\n\n\t\tif (!r_vertexlight.value && pass->rgbgen == RGB_GEN_IDENTITY)\n\t\t{\t//we found the lightmap pass. if we need a vertex-lit shader then just switch over the rgbgen+texture and hope other things work out\n\t\t\tpass->rgbgen = RGB_GEN_VERTEX_LIGHTING;\n\t\t\tpass->flags &= ~SHADER_PASS_LIGHTMAP;\n\t\t\tpass->tcgen = T_GEN_SINGLEMAP;\n\t\t\tgoto done;\n\t\t}\n\n\t\t// try to find pass with rgbgen set to RGB_GEN_VERTEX\n\t\tpass = s->passes;\n\t\tfor (i = 0; i < s->numpasses; i++, pass++)\n\t\t{\n\t\t\tif (pass->rgbgen == RGB_GEN_VERTEX_LIGHTING)\n\t\t\t\tbreak;\n\t\t}\n\n\t\tif (i < s->numpasses)\n\t\t{\t\t// we found it\n\t\t\tpass->flags |= SHADER_CULL_FRONT;\n\t\t\tpass->flags &= ~SHADER_PASS_ANIMMAP;\n\t\t\tpass->shaderbits &= ~SBITS_BLEND_BITS;\n\t\t\tpass->blendmode = 0;\n\t\t\tpass->shaderbits |= SBITS_MISC_DEPTHWRITE;\n\t\t\tpass->alphagen = ALPHA_GEN_IDENTITY;\n\t\t\tpass->numMergedPasses = 1;\n\t\t\ts->flags |= SHADER_DEPTHWRITE;\n\t\t\ts->sort = SHADER_SORT_OPAQUE;\n\t\t\ts->numpasses = 1;\n\t\t\tmemcpy(&s->passes[0], pass, sizeof(shaderpass_t));\n\t\t}\n\t\telse\n\t\t{\t// we didn't find it - simply remove all lightmap passes\n\t\t\tpass = s->passes;\n\t\t\tfor(i = 0; i < s->numpasses; i++, pass++)\n\t\t\t{\n\t\t\t\tif (pass->flags & SHADER_PASS_LIGHTMAP)\n\t\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tif ( i == s->numpasses -1 )\n\t\t\t{\n\t\t\t\ts->numpasses--;\n\t\t\t}\n\t\t\telse if ( i < s->numpasses - 1 )\n\t\t\t{\n\t\t\t\tfor ( ; i < s->numpasses - 1; i++, pass++ )\n\t\t\t\t{\n\t\t\t\t\tmemcpy ( pass, &s->passes[i+1], sizeof(shaderpass_t) );\n\t\t\t\t}\n\t\t\t\ts->numpasses--;\n\t\t\t}\n\n\t\t\tif ( s->passes[0].numtcmods )\n\t\t\t{\n\t\t\t\tpass = s->passes;\n\t\t\t\tfor ( i = 0; i < s->numpasses; i++, pass++ )\n\t\t\t\t{\n\t\t\t\t\tif ( !pass->numtcmods )\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tmemcpy ( &s->passes[0], pass, sizeof(shaderpass_t) );\n\t\t\t}\n\n\t\t\ts->passes[0].rgbgen = RGB_GEN_VERTEX_LIGHTING;\n\t\t\ts->passes[0].alphagen = ALPHA_GEN_IDENTITY;\n\t\t\ts->passes[0].blendmode = 0;\n\t\t\ts->passes[0].flags &= ~(SHADER_PASS_ANIMMAP|SHADER_PASS_NOCOLORARRAY);\n\t\t\ts->passes[0].shaderbits &= ~SBITS_BLEND_BITS;\n\t\t\ts->passes[0].shaderbits |= SBITS_MISC_DEPTHWRITE;\n\t\t\ts->passes[0].numMergedPasses = 1;\n\t\t\ts->numpasses = 1;\n\t\t\ts->flags |= SHADER_DEPTHWRITE;\n\t\t}\n\t}\ndone:;\n\n\t//if we've no specular map, try and find whatever the q3 syntax said. hopefully it'll be compatible...\n \tif (!TEXVALID(s->defaulttextures->specular) && !(s->flags & SHADER_HASGLOSS))\n\t{\n\t\tfor (pass = s->passes, i = 0; i < s->numpasses; i++, pass++)\n\t\t{\n\t\t\tif (pass->alphagen == ALPHA_GEN_SPECULAR)\n\t\t\t\tif (pass->texgen == T_GEN_ANIMMAP || pass->texgen == T_GEN_SINGLEMAP)\n\t\t\t\t\ts->defaulttextures->specular = pass->anim_frames[0];\n\t\t}\n\t}\n\n\tif (!TEXVALID(s->defaulttextures->base) && !(s->flags & SHADER_HASDIFFUSE) && !s->prog)\n\t{\t//if one of the other passes specifies $diffuse, don't try and guess one, because that means that other pass's texture gets used for BOTH passes, which isn't good.\n\t\t//also, don't guess one if a program was specified.\n\t\tshaderpass_t *best = NULL;\n\t\tint bestweight = 9999999;\n\t\tint weight;\n\n\t\tfor (pass = s->passes, i = 0; i < s->numpasses; i++, pass++)\n\t\t{\n\t\t\tweight = 0;\n\t\t\tif (pass->flags & SHADER_PASS_DETAIL)\n\t\t\t\tweight += 500;\t//prefer not to use a detail pass. these are generally useless.\n\t\t\tif (pass->numtcmods || pass->tcgen != TC_GEN_BASE)\n\t\t\t\tweight += 200;\n\t\t\tif (pass->rgbgen != RGB_GEN_IDENTITY && pass->rgbgen != RGB_GEN_IDENTITY_OVERBRIGHT && pass->rgbgen != RGB_GEN_IDENTITY_LIGHTING)\n\t\t\t\tweight += 100;\n\n\t\t\tif (pass->texgen != T_GEN_ANIMMAP && pass->texgen != T_GEN_SINGLEMAP\n#ifdef HAVE_MEDIA_DECODER\n\t\t\t\t\t&& pass->texgen != T_GEN_VIDEOMAP\n#endif\n\t\t\t\t\t)\n\t\t\t\tweight += 1000;\n\n\t\t\tif ((pass->texgen == T_GEN_ANIMMAP || pass->texgen == T_GEN_SINGLEMAP) && pass->anim_frames[0] && *pass->anim_frames[0]->ident == '$')\n\t\t\t\tweight += 1500;\n\t\t\t\n\t\t\tif (weight < bestweight)\n\t\t\t{\n\t\t\t\tbestweight = weight;\n\t\t\t\tbest = pass;\n\t\t\t}\n\t\t}\n\n\t\tif (best)\n\t\t{\n\t\t\tif (best->texgen == T_GEN_ANIMMAP || best->texgen == T_GEN_SINGLEMAP)\n\t\t\t{\n\t\t\t\tif (best->anim_frames[0] && *best->anim_frames[0]->ident != '$')\n\t\t\t\t\ts->defaulttextures->base = best->anim_frames[0];\n\t\t\t}\n#ifdef HAVE_MEDIA_DECODER\n\t\t\telse if (pass->texgen == T_GEN_VIDEOMAP && pass->cin)\n\t\t\t\ts->defaulttextures->base = Media_UpdateForShader(best->cin);\n#endif\n\t\t}\n\t}\n\n\tfor (i = 0; i < s->numpasses; i += (pass->prog?pass->numMergedPasses:1))\n\t{\n\t\tpass = s->passes+i;\n\t\tif (!(pass->shaderbits & (SBITS_BLEND_BITS|SBITS_MASK_BITS)))\n\t\t{\n\t\t\tif (pass->texgen == T_GEN_LIGHTMAP && r_forceprogramify.ival==2)\n\t\t\t\tcontinue;\t//pretend its blended.\n\t\t\tbreak;\n\t\t}\n\t}\n\n\t// all passes have blendfuncs\n\tif (i == s->numpasses)\n\t{\n\t\tint maskpass;\n\t\tqboolean isopaque = false;\n\n\t\tmaskpass = -1;\n\t\tpass = s->passes;\n\t\tfor (i = 0; i < s->numpasses; i++, pass++ )\n\t\t{\n\t\t\tif (pass->shaderbits & SBITS_ATEST_BITS)\n\t\t\t{\n\t\t\t\tmaskpass = i;\n\t\t\t}\n\t\t\telse if ((pass->shaderbits & SBITS_MASK_BITS) == 0)\n\t\t\t{\t//a few shaders use blendfunc one zero so that they're ignored when using r_vertexlight (while later alpha-masked surfs are not).\n\t\t\t\tif (/*(pass->shaderbits & (SBITS_SRCBLEND_BITS|SBITS_DSTBLEND_BITS)) == 0 ||*/\n\t\t\t\t\t(pass->shaderbits & (SBITS_SRCBLEND_BITS|SBITS_DSTBLEND_BITS)) == (SBITS_SRCBLEND_ONE|SBITS_DSTBLEND_ZERO))\n\t\t\t\t\tisopaque = true;\n\t\t\t}\n\n\t\t\tif (pass->rgbgen == RGB_GEN_UNKNOWN)\n\t\t\t{\n\t\t\t\tif (   (pass->shaderbits & SBITS_SRCBLEND_BITS) == 0\n\t\t\t\t\t|| (pass->shaderbits & SBITS_SRCBLEND_BITS) == SBITS_SRCBLEND_ONE\n\t\t\t\t\t|| (pass->shaderbits & SBITS_SRCBLEND_BITS) == SBITS_SRCBLEND_SRC_ALPHA)\n\t\t\t\t\tpass->rgbgen = RGB_GEN_IDENTITY_LIGHTING;\n\t\t\t\telse\n\t\t\t\t\tpass->rgbgen = RGB_GEN_IDENTITY;\n\t\t\t}\n\n\t\t\tShader_SetBlendmode (pass, i?pass-1:NULL);\n\n\t\t\tif (pass->blendmode == PBM_ADD && !s->defaulttextures->fullbright)\n\t\t\t\ts->defaulttextures->fullbright = pass->anim_frames[0];\n\t\t}\n\n\t\tif (!(s->flags & SHADER_SKY ) && !s->sort)\n\t\t{\n\t\t\tif (isopaque)\n\t\t\t\ts->sort = SHADER_SORT_OPAQUE;\n\t\t\telse if (maskpass == -1)\n\t\t\t{\n\t\t\t\tif (s->numpasses && s->passes[0].blendmode == PBM_ADD)\n\t\t\t\t\ts->sort = SHADER_SORT_ADDITIVE;\n\t\t\t\telse\n\t\t\t\t\ts->sort = SHADER_SORT_BLEND;\n\t\t\t}\n\t\t\telse\n\t\t\t\ts->sort = SHADER_SORT_SEETHROUGH;\n\t\t}\n\t}\n\telse\n\t{\n\t\tint\tj;\n\t\tshaderpass_t *sp;\n\n\t\tsp = s->passes;\n\t\tfor (j = 0; j < s->numpasses; j++, sp++)\n\t\t{\n\t\t\tif (sp->rgbgen == RGB_GEN_UNKNOWN)\n\t\t\t{\n\t\t\t\tif (sp->flags & SHADER_PASS_LIGHTMAP)\n\t\t\t\t\tsp->rgbgen = RGB_GEN_IDENTITY_LIGHTING;\n\t\t\t\telse\n\t\t\t\t\tsp->rgbgen = RGB_GEN_IDENTITY;\n\t\t\t}\n\n\t\t\tShader_SetBlendmode (sp, j?sp-1:NULL);\n\t\t}\n\n\t\tif (!s->sort)\n\t\t{\n\t\t\tif (i < s->numpasses && (s->passes[i].shaderbits & SBITS_ATEST_BITS))\n\t\t\t\ts->sort = SHADER_SORT_SEETHROUGH;\n\t\t}\n\n\t\tif (!( s->flags & SHADER_DEPTHWRITE) &&\n\t\t\t!(s->flags & SHADER_SKY))\n\t\t{\n\t\t\ts->passes->shaderbits |= SBITS_MISC_DEPTHWRITE;\n\t\t\ts->flags |= SHADER_DEPTHWRITE;\n\t\t}\n\t}\n\n\tif (s->numpasses >= 2)\n\t{\n\t\tint j;\n\n\t\tpass = s->passes;\n\t\tfor (i = 0; i < s->numpasses;)\n\t\t{\n\t\t\tif (i == s->numpasses - 1)\n\t\t\t\tbreak;\n\n\t\t\tpass = s->passes + i;\n\t\t\tfor (j = 1; j < s->numpasses-i && j == pass->numMergedPasses && j+1 < be_maxpasses; j++)\n\t\t\t\tShader_SetPassFlush (pass, pass + j);\n\n\t\t\ti += pass->numMergedPasses;\n\t\t}\n\t}\n\n\tif (!s->sort)\n\t{\n\t\textern cvar_t r_refract_fbo;\n\t\tif ((s->flags & SHADER_HASREFRACT) && !r_refract_fbo.ival)\n\t\t\ts->sort = SHADER_SORT_UNDERWATER;\n\t\telse\n\t\t\ts->sort = SHADER_SORT_OPAQUE;\n\t}\n\n\tif ((s->flags & SHADER_SKY) && (s->flags & SHADER_DEPTHWRITE))\n\t{\n\t\ts->flags &= ~SHADER_DEPTHWRITE;\n\t}\n\n\tif (!s->bemoverrides[bemoverride_depthonly])\n\t{\n\t\tconst char *mask = Shader_AlphaMaskProgArgs(s);\n\t\tif (*mask || (s->prog&&s->prog->tess))\n\t\t\ts->bemoverrides[bemoverride_depthonly] = R_RegisterShader(va(\"depthonly%s%s\", mask, (s->prog&&s->prog->tess)?\"#TESS\":\"\"), SUF_NONE, \n\t\t\t\t\"{\\n\"\n\t\t\t\t\t\"program depthonly\\n\"\n\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\"map $diffuse\\n\"\n\t\t\t\t\t\t\"depthwrite\\n\"\n\t\t\t\t\t\t\"maskcolor\\n\"\n\t\t\t\t\t\"}\\n\"\n\t\t\t\t\"}\\n\");\n\t}\n\tif (!s->bemoverrides[LSHADER_STANDARD] && (s->prog&&s->prog->tess))\n\t{\n\t\tint mode;\n\t\tfor (mode = 0; mode < LSHADER_MODES; mode++)\n\t\t{\n\t\t\tif ((mode & LSHADER_CUBE) && (mode & LSHADER_SPOT))\n\t\t\t\tcontinue;\n\t\t\tif (s->bemoverrides[mode])\n\t\t\t\tcontinue;\n\t\t\ts->bemoverrides[mode] = R_RegisterShader(va(\"rtlight%s%s%s%s#TESS\", \n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(mode & LSHADER_SMAP)?\"#PCF\":\"\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(mode & LSHADER_SPOT)?\"#SPOT\":\"\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(mode & LSHADER_CUBE)?\"#CUBE\":\"\",\n#ifdef GLQUAKE\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(qrenderer == QR_OPENGL && gl_config.arb_shadow && (mode & (LSHADER_SMAP|LSHADER_SPOT)))?\"#USE_ARB_SHADOW\":\"\"\n#else\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\"\n#endif\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t, s->usageflags, \n\t\t\t\t\"{\\n\"\n\t\t\t\t\t\"program rtlight\\n\"\n\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\"map $diffuse\\n\"\n\t\t\t\t\t\t\"blendfunc add\\n\"\n\t\t\t\t\t\t\"nodepth\\n\"\n\t\t\t\t\t\"}\\n\"\n\t\t\t\t\"}\\n\");\n\t\t}\n\t}\n\n\tif (!s->prog && sh_config.progs_supported && (r_forceprogramify.ival || (ps->parseflags & SPF_PROGRAMIFY)))\n\t{\n\t\tif (r_forceprogramify.ival >= 2)\n\t\t{\n\t\t\tif (s->passes[0].numtcmods == 1 && s->passes[0].tcmods[0].type == SHADER_TCMOD_SCALE)\n\t\t\t\ts->passes[0].numtcmods = 0;\t//DP sucks and doesn't use normalized texture coords *if* there's a shader specified. so lets ignore any extra scaling that this imposes.\n\t\t\tif (s->passes[0].shaderbits & SBITS_ATEST_BITS)\t//mimic DP's limited alphafunc support\n\t\t\t\ts->passes[0].shaderbits = (s->passes[0].shaderbits & ~SBITS_ATEST_BITS) | SBITS_ATEST_GE128;\n\t\t\ts->passes[0].shaderbits &= ~SBITS_DEPTHFUNC_BITS;\t//DP ignores this too.\n\t\t\tif (s->flags & SHADER_CULL_BACK)\t//DP has no back-face culling\n\t\t\t\ts->flags = (s->flags&~SHADER_CULL_BACK)|SHADER_CULL_FRONT;\n\n\t\t\t//disable rtlight stuff when blended.\n\t\t\tif ((s->passes[0].shaderbits & SBITS_SRCBLEND_BITS)==SBITS_SRCBLEND_ONE && (s->passes[0].shaderbits & SBITS_DSTBLEND_BITS)==SBITS_DSTBLEND_ZERO)\n\t\t\t\t;\t//replace\n\t\t\telse if ((s->passes[0].shaderbits & SBITS_SRCBLEND_BITS)==SBITS_SRCBLEND_ONE && (s->passes[0].shaderbits & SBITS_DSTBLEND_BITS)==SBITS_DSTBLEND_ONE)\n\t\t\t\ts->flags |= SHADER_NOSHADOWS;\t//add-ignore-alpha\n\t\t\telse if ((s->passes[0].shaderbits & SBITS_SRCBLEND_BITS)==SBITS_SRCBLEND_SRC_ALPHA && (s->passes[0].shaderbits & SBITS_DSTBLEND_BITS)==SBITS_DSTBLEND_ONE)\n\t\t\t\ts->flags |= SHADER_NOSHADOWS;\t//add-with-alpha\n\t\t\telse if ((s->passes[0].shaderbits & SBITS_SRCBLEND_BITS)==SBITS_SRCBLEND_SRC_ALPHA && (s->passes[0].shaderbits & SBITS_DSTBLEND_BITS)==SBITS_DSTBLEND_ONE_MINUS_SRC_ALPHA)\n\t\t\t\ts->flags |= SHADER_NOSHADOWS;\t//blended\n\t\t\telse\n\t\t\t\ts->flags |= SHADER_NOSHADOWS|SHADER_NODLIGHT;\t//erk...\n\n\t\t\tif (s->passes[0].numtcmods>1)\n\t\t\t{\t//reverse the order of tcmods (dp uses a texture matrix which it concats in reverse order)\n\t\t\t\ttcmod_t\t\ttmp[SHADER_MAX_TC_MODS];\n\t\t\t\tint j;\n\t\t\t\tmemcpy(tmp, s->passes[0].tcmods, sizeof(*tmp)*s->passes[0].numtcmods);\n\t\t\t\tfor (j = 0; j < s->passes[0].numtcmods; j++)\n\t\t\t\t\ts->passes[0].tcmods[j] = tmp[s->passes[0].numtcmods-1-j];\n\t\t\t}\n\t\t}\n\t\tShader_Programify(ps);\n\t}\n\n\tif(\t\ts->numdeforms ||\n\t\t\ts->sort == SHADER_SORT_PORTAL ||\t//q3-style portals (needed for pvs info)\n\t\t\ts->flags & (SHADER_HASREFLECT|SHADER_HASREFRACT)) //water effects (needed for pvs info)\n\t\ts->flags |= SHADER_NEEDSARRAYS;\n\tif (s->prog)\n\t{\n\t\tstruct\n\t\t{\n\t\t\tint gen;\n\t\t\tunsigned int flags;\n\t\t} defaulttgen[] =\n\t\t{\n\t\t\t//light\n\t\t\t{T_GEN_SHADOWMAP,\t\t0},\t\t\t\t\t\t//0\n\t\t\t{T_GEN_LIGHTCUBEMAP,\t0},\t\t\t\t\t\t//1\n\n\t\t\t//material\n\t\t\t{T_GEN_DIFFUSE,\t\t\tSHADER_HASDIFFUSE},\t\t//2\n\t\t\t{T_GEN_NORMALMAP,\t\tSHADER_HASNORMALMAP},\t//3\n\t\t\t{T_GEN_SPECULAR,\t\tSHADER_HASGLOSS},\t\t//4\n\t\t\t{T_GEN_UPPEROVERLAY,\tSHADER_HASTOPBOTTOM},\t//5\n\t\t\t{T_GEN_LOWEROVERLAY,\tSHADER_HASTOPBOTTOM},\t//6\n\t\t\t{T_GEN_FULLBRIGHT,\t\tSHADER_HASFULLBRIGHT},\t//7\n\t\t\t{T_GEN_PALETTED,\t\tSHADER_HASPALETTED},\t//8\n\t\t\t{T_GEN_REFLECTCUBE,\t\tSHADER_HASREFLECTCUBE},\t//9\n\t\t\t{T_GEN_REFLECTMASK,\t\t0},\t\t\t\t\t\t//10\n\t\t\t{T_GEN_DISPLACEMENT,\tSHADER_HASDISPLACEMENT},//11\n\t\t\t{T_GEN_OCCLUSION,\t\t0},\t\t\t\t\t\t//12\n\t\t\t{T_GEN_TRANSMISSION,\t0},\t\t\t\t\t\t//13\n\t\t\t{T_GEN_THICKNESS,\t\t0},\t\t\t\t\t\t//14\n//\t\t\t{T_GEN_REFLECTION,\t\tSHADER_HASREFLECT},\t\t//\n//\t\t\t{T_GEN_REFRACTION,\t\tSHADER_HASREFRACT},\t\t//\n//\t\t\t{T_GEN_REFRACTIONDEPTH,\tSHADER_HASREFRACTDEPTH},//\n//\t\t\t{T_GEN_RIPPLEMAP,\t\tSHADER_HASRIPPLEMAP},\t//\n\n\t\t\t//batch\n\t\t\t{T_GEN_LIGHTMAP,\t\tSHADER_HASLIGHTMAP},\t//15\n\t\t\t{T_GEN_DELUXMAP,\t\t0},\t\t\t\t\t\t//16\n\t\t\t//more lightmaps\t\t\t\t\t\t\t\t//17,18,19\n\t\t\t//mode deluxemaps\t\t\t\t\t\t\t\t//20,21,22\n\t\t};\n\n#ifdef HAVE_MEDIA_DECODER\n\t\tcin_t *cin = R_ShaderGetCinematic(s);\n#endif\n\n\t\t//if the glsl doesn't specify all samplers, just trim them.\n\t\ts->numpasses = s->prog->numsamplers;\n\n#ifdef HAVE_MEDIA_DECODER\n\t\tif (cin && R_ShaderGetCinematic(s) == cin)\n\t\t\tcin = NULL;\n#endif\n\n\t\t//if the glsl has specific textures listed, be sure to provide a pass for them.\n\t\tfor (i = 0; i < sizeof(defaulttgen)/sizeof(defaulttgen[0]); i++)\n\t\t{\n\t\t\tif (s->prog->defaulttextures & (1u<<i))\n\t\t\t{\n\t\t\t\tif (s->numpasses >= SHADER_PASS_MAX)\n\t\t\t\t\tbreak;\t//panic...\n\t\t\t\ts->passes[s->numpasses].flags &= ~SHADER_PASS_DEPTHCMP;\n\t\t\t\tif (defaulttgen[i].gen == T_GEN_SHADOWMAP)\n\t\t\t\t\ts->passes[s->numpasses].flags |= SHADER_PASS_DEPTHCMP;\n#ifdef HAVE_MEDIA_DECODER\n\t\t\t\tif (!i && cin)\n\t\t\t\t{\n\t\t\t\t\ts->passes[s->numpasses].texgen = T_GEN_VIDEOMAP;\n\t\t\t\t\ts->passes[s->numpasses].cin = cin;\n\t\t\t\t\tcin = NULL;\n\t\t\t\t}\n\t\t\t\telse\n#endif\n\t\t\t\t{\n\t\t\t\t\ts->passes[s->numpasses].texgen = defaulttgen[i].gen;\n#ifdef HAVE_MEDIA_DECODER\n\t\t\t\t\ts->passes[s->numpasses].cin = NULL;\n#endif\n\t\t\t\t}\n\t\t\t\ts->numpasses++;\n\t\t\t\ts->flags |= defaulttgen[i].flags;\n\t\t\t}\n\t\t}\n\n\t\t//must have at least one texture.\n\t\tif (!s->numpasses)\n\t\t{\n#ifdef HAVE_MEDIA_DECODER\n\t\t\ts->passes[0].texgen = cin?T_GEN_VIDEOMAP:T_GEN_DIFFUSE;\n\t\t\ts->passes[0].cin = cin;\n#else\n\t\t\ts->passes[0].texgen = T_GEN_DIFFUSE;\n#endif\n\t\t\ts->numpasses = 1;\n\t\t}\n#ifdef HAVE_MEDIA_DECODER\n\t\telse if (cin)\n\t\t\tMedia_ShutdownCin(cin);\n#endif\n\t\ts->passes->numMergedPasses = s->numpasses;\n\t}\n\telse\n\t{\n\t\tfor (i = 0; i < s->numpasses; i++)\n\t\t{\n\t\t\tpass = &s->passes[i];\n\t\t\tif (pass->prog)\n\t\t\t\tcontinue;\n\t\t\tif (pass->numtcmods || (s->passes[i].tcgen != TC_GEN_BASE && s->passes[i].tcgen != TC_GEN_LIGHTMAP) || !(s->passes[i].flags & SHADER_PASS_NOCOLORARRAY))\n\t\t\t{\n\t\t\t\ts->flags |= SHADER_NEEDSARRAYS;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (pass->alphagen == ALPHA_GEN_PORTAL ||\t//needs xyz\n\t\t\t\tpass->alphagen == ALPHA_GEN_SPECULAR)\t//needs xyz+norm\n\t\t\t{\n\t\t\t\ts->flags |= SHADER_NEEDSARRAYS;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (!(pass->flags & SHADER_PASS_NOCOLORARRAY))\n\t\t\t{\n\t\t\t\tif (!(((pass->rgbgen == RGB_GEN_VERTEX_LIGHTING) ||\n\t\t\t\t\t(pass->rgbgen == RGB_GEN_VERTEX_EXACT) ||\n\t\t\t\t\t(pass->alphagen == ALPHA_GEN_VERTEX) ||\n\t\t\t\t\t(pass->rgbgen == RGB_GEN_ONE_MINUS_VERTEX)) &&\n\t\t\t\t\t(pass->alphagen == ALPHA_GEN_VERTEX)))\n\t\t\t\t{\n\t\t\t\t\ts->flags |= SHADER_NEEDSARRAYS;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tif (!r_halfrate.ival)\n\t{\n\t\tfor (i = 0; i < s->numpasses; i++)\n\t\t{\n\t\t\ts->passes[i].shaderbits |= SBITS_MISC_FULLRATE;\n\t\t}\n\t}\n}\n/*\nstatic void Shader_UpdateRegistration (void)\n{\n\tint i, j, l;\n\tshader_t *shader;\n\tshaderpass_t *pass;\n\n\tshader = r_shaders;\n\tfor (i = 0; i < MAX_SHADERS; i++, shader++)\n\t{\n\t\tif (!shader->registration_sequence)\n\t\t\tcontinue;\n\t\tif (shader->registration_sequence != registration_sequence)\n\t\t{\n\t\t\tShader_Free ( shader );\n\t\t\tshader->registration_sequence = 0;\n\t\t\tcontinue;\n\t\t}\n\n\t\tpass = shader->passes;\n\t\tfor (j = 0; j < shader->numpasses; j++, pass++)\n\t\t{\n\t\t\tif (pass->flags & SHADER_PASS_ANIMMAP)\n\t\t\t{\n\t\t\t\tfor (l = 0; l < pass->anim_numframes; l++)\n\t\t\t\t{\n\t\t\t\t\tif (pass->anim_frames[l])\n\t\t\t\t\t\tpass->anim_frames[l]->registration_sequence = registration_sequence;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if ( pass->flags & SHADER_PASS_VIDEOMAP )\n\t\t\t{\n\t\t\t\t// Shader_RunCinematic will do the job\n//\t\t\t\tpass->cin->frame = -1;\n\t\t\t}\n\t\t\telse if ( !(pass->flags & SHADER_PASS_LIGHTMAP) )\n\t\t\t{\n\t\t\t\tif ( pass->anim_frames[0] )\n\t\t\t\t\tpass->anim_frames[0]->registration_sequence = registration_sequence;\n\t\t\t}\n\t\t}\n\t}\n}\n*/\n\n/*\n\tif (*shader_diffusemapname)\n\t{\n\t\tif (!s->defaulttextures.base)\n\t\t\ts->defaulttextures.base = Shader_FindImage (va(\"%s.tga\", shader_diffusemapname), 0);\n\t\tif (!s->defaulttextures.bump)\n\t\t\ts->defaulttextures.bump = Shader_FindImage (va(\"%s_norm.tga\", shader_diffusemapname), 0);\n\t\tif (!s->defaulttextures.fullbright)\n\t\t\ts->defaulttextures.fullbright = Shader_FindImage (va(\"%s_glow.tga\", shader_diffusemapname), 0);\n\t\tif (!s->defaulttextures.specular)\n\t\t\ts->defaulttextures.specular = Shader_FindImage (va(\"%s_gloss.tga\", shader_diffusemapname), 0);\n\t\tif (!s->defaulttextures.upperoverlay)\n\t\t\ts->defaulttextures.upperoverlay = Shader_FindImage (va(\"%s_shirt.tga\", shader_diffusemapname), 0);\n\t\tif (!s->defaulttextures.loweroverlay)\n\t\t\ts->defaulttextures.loweroverlay = Shader_FindImage (va(\"%s_pants.tga\", shader_diffusemapname), 0);\t//stupid yanks...\n\t}\n*/\nvoid QDECL R_BuildDefaultTexnums(texnums_t *src, shader_t *shader, unsigned int imageflags)\n{\n\tchar *h;\n\tchar imagename[MAX_QPATH];\n\tchar mapname[MAX_QPATH];\n\tchar *subpath = NULL;\n\ttexnums_t *tex;\n\tunsigned int a, aframes;\n\tstrcpy(imagename, shader->name);\n\th = strchr(imagename, '#');\n\tif (h && !strchr(imagename, '@'))\n\t\t*h = 0;\n\tif (*imagename == '/' || strchr(imagename, ':'))\n\t{\t//this is not security. this is anti-spam for the verbose security in the filesystem code.\n\t\tCon_Printf(\"Warning: shader has absolute path: %s\\n\", shader->name);\n\t\t*imagename = 0;\n\t}\n\n\t//skins can use an alternative path in certain cases, to work around dodgy models.\n\tif (shader->generator == Shader_DefaultSkin)\n\t\tsubpath = shader->genargs;\n\n\ttex = shader->defaulttextures;\n\taframes = max(1, shader->numdefaulttextures);\n\t//if any were specified explicitly, replicate that into all.\n\t//this means animmap can be used, with any explicit textures overriding all.\n\tif (!shader->numdefaulttextures && src)\n\t{\n\t\t//only do this if there wasn't an animmap thing to break everything.\n\t\tif (!TEXVALID(tex->base))\n\t\t\ttex->base\t\t\t= src->base;\n\t\tif (!TEXVALID(tex->bump))\n\t\t\ttex->bump\t\t\t= src->bump;\n\t\tif (!TEXVALID(tex->fullbright))\n\t\t\ttex->fullbright\t\t= src->fullbright;\n\t\tif (!TEXVALID(tex->specular))\n\t\t\ttex->specular\t\t= src->specular;\n\t\tif (!TEXVALID(tex->loweroverlay))\n\t\t\ttex->loweroverlay\t= src->loweroverlay;\n\t\tif (!TEXVALID(tex->upperoverlay))\n\t\t\ttex->upperoverlay\t= src->upperoverlay;\n\t\tif (!TEXVALID(tex->reflectmask))\n\t\t\ttex->reflectmask\t= src->reflectmask;\n\t\tif (!TEXVALID(tex->reflectcube))\n\t\t\ttex->reflectcube\t= src->reflectcube;\n\t\tif (!TEXVALID(tex->displacement))\n\t\t\ttex->displacement\t= src->displacement;\n\t}\n\tfor (a = 1; a < aframes; a++)\n\t{\n\t\tif (!TEXVALID(tex[a].base))\n\t\t\ttex[a].base\t\t\t= tex[0].base;\n\t\tif (!TEXVALID(tex[a].bump))\n\t\t\ttex[a].bump\t\t\t= tex[0].bump;\n\t\tif (!TEXVALID(tex[a].fullbright))\n\t\t\ttex[a].fullbright\t= tex[0].fullbright;\n\t\tif (!TEXVALID(tex[a].specular))\n\t\t\ttex[a].specular\t\t= tex[0].specular;\n\t\tif (!TEXVALID(tex[a].loweroverlay))\n\t\t\ttex[a].loweroverlay\t= tex[0].loweroverlay;\n\t\tif (!TEXVALID(tex[a].upperoverlay))\n\t\t\ttex[a].upperoverlay\t= tex[0].upperoverlay;\n\t\tif (!TEXVALID(tex[a].reflectmask))\n\t\t\ttex[a].reflectmask\t= tex[0].reflectmask;\n\t\tif (!TEXVALID(tex[a].reflectcube))\n\t\t\ttex[a].reflectcube\t= tex[0].reflectcube;\n\t\tif (!TEXVALID(tex[a].displacement))\n\t\t\ttex[a].displacement\t= tex[0].displacement;\n\t}\n\tfor (a = 0; a < aframes; a++, tex++)\n\t{\n\t\tCOM_StripExtension(tex->mapname, mapname, sizeof(mapname));\n\n\t\tif (!TEXVALID(tex->base))\n\t\t{\n\t\t\t/*dlights/realtime lighting needs some stuff*/\n\t\t\tif (!TEXVALID(tex->base) && *tex->mapname)// && (shader->flags & SHADER_HASDIFFUSE))\n\t\t\t\ttex->base = R_LoadHiResTexture(tex->mapname, NULL, imageflags);\n\n\t\t\tif (!TEXVALID(tex->base))\n\t\t\t\ttex->base = R_LoadHiResTexture(imagename, subpath, (*imagename=='{')?0:IF_NOALPHA|imageflags);\n\t\t}\n\n\t\tif ((shader->flags & SHADER_HASPALETTED) && !TEXVALID(tex->paletted))\n\t\t{\n\t\t\tif (!TEXVALID(tex->paletted) && *tex->mapname)\n\t\t\t\ttex->paletted = R_LoadHiResTexture(va(\"%s\", tex->mapname), NULL, 0|IF_NEAREST|IF_PALETTIZE|imageflags);\n\t\t\tif (!TEXVALID(tex->paletted))\n\t\t\t\ttex->paletted = R_LoadHiResTexture(va(\"%s\", imagename), subpath, ((*imagename=='{')?0:IF_NOALPHA)|IF_NEAREST|IF_PALETTIZE|imageflags);\n\t\t}\n\n\t\timageflags |= IF_LOWPRIORITY;\n\n\t\tCOM_StripExtension(imagename, imagename, sizeof(imagename));\n\n\t\tif (!TEXVALID(tex->bump))\n\t\t{\n\t\t\tif (r_loadbumpmapping || (shader->flags & SHADER_HASNORMALMAP))\n\t\t\t{\n\t\t\t\tif (!TEXVALID(tex->bump) && *mapname && (shader->flags & SHADER_HASNORMALMAP))\n\t\t\t\t\ttex->bump = R_LoadHiResTexture(va(\"%s_norm\", mapname), NULL, imageflags|IF_TRYBUMP|IF_NOSRGB|imageflags);\n\t\t\t\tif (!TEXVALID(tex->bump))\n\t\t\t\t\ttex->bump = R_LoadHiResTexture(va(\"%s_norm\", imagename), subpath, imageflags|IF_TRYBUMP|IF_NOSRGB|imageflags);\n\t\t\t}\n\t\t}\n\n\t\tif (!TEXVALID(tex->loweroverlay))\n\t\t{\n\t\t\tif (shader->flags & SHADER_HASTOPBOTTOM)\n\t\t\t{\n\t\t\t\tif (!TEXVALID(tex->loweroverlay) && *mapname)\n\t\t\t\t\ttex->loweroverlay = R_LoadHiResTexture(va(\"%s_pants\", mapname), NULL, imageflags);\n\t\t\t\tif (!TEXVALID(tex->loweroverlay))\n\t\t\t\t\ttex->loweroverlay = R_LoadHiResTexture(va(\"%s_pants\", imagename), subpath, imageflags);\t/*how rude*/\n\t\t\t}\n\t\t}\n\n\t\tif (!TEXVALID(tex->upperoverlay))\n\t\t{\n\t\t\tif (shader->flags & SHADER_HASTOPBOTTOM)\n\t\t\t{\n\t\t\t\tif (!TEXVALID(tex->upperoverlay) && *mapname)\n\t\t\t\t\ttex->upperoverlay = R_LoadHiResTexture(va(\"%s_shirt\", mapname), NULL, imageflags);\n\t\t\t\tif (!TEXVALID(tex->upperoverlay))\n\t\t\t\t\ttex->upperoverlay = R_LoadHiResTexture(va(\"%s_shirt\", imagename), subpath, imageflags);\n\t\t\t}\n\t\t}\n\n\t\tif (!TEXVALID(tex->specular))\n\t\t{\n\t\t\textern cvar_t gl_specular;\n\t\t\tif ((shader->flags & SHADER_HASGLOSS) && gl_specular.value && gl_load24bit.value)\n\t\t\t{\n\t\t\t\tif (!TEXVALID(tex->specular) && *mapname)\n\t\t\t\t\ttex->specular = R_LoadHiResTexture(va(\"%s_gloss\", mapname), NULL, imageflags);\n\t\t\t\tif (!TEXVALID(tex->specular))\n\t\t\t\t\ttex->specular = R_LoadHiResTexture(va(\"%s_gloss\", imagename), subpath, imageflags);\n\t\t\t}\n\t\t}\n\n\t\tif (!TEXVALID(tex->fullbright))\n\t\t{\n\t\t\textern cvar_t r_fb_bmodels;\n\t\t\tif ((shader->flags & SHADER_HASFULLBRIGHT) && r_fb_bmodels.value && gl_load24bit.value)\n\t\t\t{\n\t\t\t\tif (!TEXVALID(tex->fullbright) && *mapname)\n\t\t\t\t\ttex->fullbright = R_LoadHiResTexture(va(\"%s_luma:%s_glow\", mapname, mapname), NULL, imageflags);\n\t\t\t\tif (!TEXVALID(tex->fullbright))\n\t\t\t\t\ttex->fullbright = R_LoadHiResTexture(va(\"%s_luma:%s_glow\", imagename, imagename), subpath, imageflags);\n\t\t\t}\n\t\t}\n\n\t\t//if there's a reflectcube texture specified by the shader, make sure we have a reflectmask to go with it.\n\t\tif (tex->reflectcube)\n\t\t{\n\t\t\tif (!TEXVALID(tex->reflectmask) && *mapname)\n\t\t\t\ttex->reflectmask = R_LoadHiResTexture(va(\"%s_reflect\", mapname), NULL, imageflags);\n\t\t\tif (!TEXVALID(tex->reflectmask))\n\t\t\t\ttex->reflectmask = R_LoadHiResTexture(va(\"%s_reflect\", imagename), subpath, imageflags);\n\t\t}\n\t}\n}\n\n#if 0//def Q2BSPS\nstatic qbyte *ReadRGBA8ImageFile(const char *fname, const char *subpath, int *width, int *height)\n{\n\tqboolean hasalpha;\n\tqofs_t filesize;\n\tqbyte *img, *filedata = NULL;\n\tchar *patterns[] = \n\t{\n\t\t\"*overrides/%s.tga\",\n\t\t\"*overrides/%s.jpg\",\n\t\t\"textures/%s.tga\",\n\t\t\"textures/%s.jpg\",\n\t};\n\tchar nname[MAX_QPATH];\n\tsize_t p;\n\tfor (p = 0; p < countof(patterns) && !filedata; p++)\n\t{\n\t\tif (*patterns[p] == '*')\n\t\t\tQ_snprintfz(nname, sizeof(nname), patterns[p]+1, COM_SkipPath(fname));\n\t\telse\n\t\t\tQ_snprintfz(nname, sizeof(nname), patterns[p], fname);\n\t\tfiledata = FS_MallocFile(nname, FS_GAME, &filesize);\n\t}\n\timg = filedata?Read32BitImageFile(filedata, filesize, width, height, &hasalpha, fname):NULL;\n\tBZ_Free(filedata);\n\treturn img;\n}\n#endif\n\n//call this with some fallback textures to directly load some textures\nvoid QDECL R_BuildLegacyTexnums(shader_t *shader, const char *fallbackname, const char *subpath, unsigned int loadflags, unsigned int imageflags, uploadfmt_t basefmt, size_t width, size_t height, qbyte *srcdata, qbyte *palette)\n{\n\tchar *h;\n\tchar imagename[MAX_QPATH];\n\tchar mapname[MAX_QPATH];\t//as specified by the shader.\n\t//extern cvar_t gl_miptexLevel;\n\ttexnums_t *tex = shader->defaulttextures;\n\tint a, aframes;\n\t/*else if (gl_miptexLevel.ival)\n\t{\n\t\tunsigned int miplevel = 0, i;\n\t\tfor (i = 0; i < 3 && i < gl_miptexLevel.ival && mipdata[i]; i++)\n\t\t\tmiplevel = i;\n\t\tfor (i = 0; i < 3; i++)\n\t\t\tdontcrashme[i] = (miplevel+i)>3?NULL:mipdata[miplevel+i];\n\t\twidth >>= miplevel;\n\t\theight >>= miplevel;\n\t\tmipdata = dontcrashme;\n\t}\n\t*/\n\tstrcpy(imagename, shader->name);\n\th = *imagename?strchr(imagename+1, '#'):NULL;\n\tif (h)\n\t\t*h = 0;\n\tif (*imagename == '/' || strchr(imagename, ':'))\n\t{\t//this is not security. this is anti-spam for the verbose security in the filesystem code.\n\t\tCon_Printf(\"Warning: shader has absolute path: %s\\n\", shader->name);\n\t\t*imagename = 0;\n\t}\n\n\t//for water texture replacements\n\twhile((h = strchr(imagename, '*')))\n\t\t*h = '#';\n\n\tloadflags &= shader->flags;\n\n\t//skins can use an alternative path in certain cases, to work around dodgy models.\n\tif (shader->generator == Shader_DefaultSkin)\n\t\tsubpath = shader->genargs;\n\n\t//optimise away any palette info if we can...\n\tif (!palette || palette == host_basepal)\n\t{\n\t\tif (basefmt == TF_MIP4_8PAL24)\n\t\t\tbasefmt = TF_MIP4_SOLID8;\n//\t\tif (basefmt == TF_MIP4_8PAL24_T255)\n//\t\t\tbasefmt = TF_MIP4_TRANS8;\n\t}\n\n\t//make sure the noalpha thing is set properly.\n\tswitch(basefmt)\n\t{\n\tcase TF_MIP4_P8:\n\tcase TF_MIP4_8PAL24:\n\tcase TF_MIP4_SOLID8:\n\tcase TF_SOLID8:\n\t\timageflags |= IF_NOALPHA;\n\t\t//fallthrough\n\tcase TF_MIP4_8PAL24_T255:\n\t\tif (!srcdata)\n\t\t\tbasefmt = TF_SOLID8;\n\t\tbreak;\n\tdefault:\n\t\tbreak;\n\t}\n\timageflags |= IF_MIPCAP;\n\n\tCOM_StripExtension(imagename, imagename, sizeof(imagename));\n\n\taframes = max(1, shader->numdefaulttextures);\n\t//if any were specified explicitly, replicate that into all.\n\t//this means animmap can be used, with any explicit textures overriding all.\n\tfor (a = 1; a < aframes; a++)\n\t{\n\t\tif (!TEXVALID(tex[a].base))\n\t\t\ttex[a].base\t\t\t= tex[0].base;\n\t\tif (!TEXVALID(tex[a].bump))\n\t\t\ttex[a].bump\t\t\t= tex[0].bump;\n\t\tif (!TEXVALID(tex[a].fullbright))\n\t\t\ttex[a].fullbright\t= tex[0].fullbright;\n\t\tif (!TEXVALID(tex[a].specular))\n\t\t\ttex[a].specular\t\t= tex[0].specular;\n\t\tif (!TEXVALID(tex[a].loweroverlay))\n\t\t\ttex[a].loweroverlay\t= tex[0].loweroverlay;\n\t\tif (!TEXVALID(tex[a].upperoverlay))\n\t\t\ttex[a].upperoverlay\t= tex[0].upperoverlay;\n\t\tif (!TEXVALID(tex[a].reflectmask))\n\t\t\ttex[a].reflectmask\t= tex[0].reflectmask;\n\t\tif (!TEXVALID(tex[a].reflectcube))\n\t\t\ttex[a].reflectcube\t= tex[0].reflectcube;\n\t\tif (!TEXVALID(tex[a].displacement))\n\t\t\ttex[a].displacement\t= tex[0].displacement;\n\t}\n\tfor (a = 0; a < aframes; a++, tex++)\n\t{\n\t\tCOM_StripExtension(tex->mapname, mapname, sizeof(mapname));\n\n#if 0//def Q2BSPS\n\t\tif (gl_load24bit.ival == 2)\n\t\t{\n\t\t\tqbyte *base;\n\t\t\tint basewidth, baseheight;\n\t\t\tqbyte *norm;\n\t\t\tint normwidth, normheight;\n\t\t\tqbyte tmp;\n\t\t\tint x, y;\n\t\t\tbase = ReadRGBA8ImageFile(imagename, subpath, &basewidth, &baseheight);\n\t\t\t//base contains diffuse RGB, and height\n\t\t\tif (base)\n\t\t\t{\n\t\t\t\ttex->base = Image_GetTexture(va(\"%s_diff\", imagename), subpath, imageflags|IF_NOREPLACE, base, NULL, basewidth, baseheight, TF_RGBX32);\t//queues the texture creation.\n\t\t\t\tnorm = ReadRGBA8ImageFile(va(\"%s_bump\", imagename), subpath, &normwidth, &normheight);\n\t\t\t\tif (norm)\n\t\t\t\t{\n\t\t\t\t\tif (normwidth != basewidth || normheight != baseheight)\n\t\t\t\t\t{\n\t\t\t\t\t\t//sucks...\n\t\t\t\t\t\ttex->bump = Image_GetTexture(va(\"%s_norm\", imagename), subpath, imageflags|IF_NOREPLACE, norm, NULL, normwidth, normheight, TF_RGBX32);\t//queues the texture creation.\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\t//so we have a base texture, and normalmap\n\t\t\t\t\t\t//we already uploaded the diffuse, so now we can just pretend that the base is the specularmap.\n\t\t\t\t\t\t//we just need to swap the two alpha channels.\n\t\t\t\t\t\tfor (y = 0; y < baseheight; y++)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tfor (x = 0; x < basewidth; x++)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\ttmp = base[(x+y*basewidth)*4+3];\n\t\t\t\t\t\t\t\tbase[(x+y*basewidth)*4+3] = norm[(x+y*basewidth)*4+3];\n\t\t\t\t\t\t\t\tnorm[(x+y*basewidth)*4+3] = ((x+y)&1)*255;//tmp;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\ttex->bump = Image_GetTexture(va(\"%s_norm\", imagename), subpath, imageflags|IF_NOREPLACE, norm, NULL, normwidth, normheight, TF_RGBA32);\t//queues the texture creation.\n\t\t\t\t\t\ttex->specular = Image_GetTexture(va(\"%s_spec\", imagename), subpath, imageflags|IF_NOREPLACE, base, NULL, basewidth, baseheight, TF_RGBA32);\t//queues the texture creation.\n\t\t\t\t\t}\n\t\t\t\t\tBZ_Free(norm);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t//generate a height8 image from the alpha channel. we  can do it in place\n\t\t\t\t\tfor (y = 0; y < baseheight; y++)\n\t\t\t\t\t{\n\t\t\t\t\t\tfor (x = 0; x < basewidth; x++)\n\t\t\t\t\t\t\tbase[(x+y*basewidth)] = base[(x+y*basewidth)*4+3];\n\t\t\t\t\t}\n\t\t\t\t\ttex->bump = Image_GetTexture(va(\"%s_norm\", imagename), subpath, imageflags|IF_NOREPLACE, base, NULL, basewidth, baseheight, TF_HEIGHT8);\n\t\t\t\t}\n\t\t\t\tBZ_Free(base);\n\t\t\t}\n\t\t}\n#endif\n\n\t\t/*dlights/realtime lighting needs some stuff*/\n\t\tif (loadflags & SHADER_HASDIFFUSE)\n\t\t{\n\t\t\tif (!TEXVALID(tex->base) && *mapname)\n\t\t\t\ttex->base = R_LoadHiResTexture(mapname, NULL, imageflags);\n\t\t\tif (!TEXVALID(tex->base) && fallbackname)\n\t\t\t{\n\t\t\t\tif (gl_load24bit.ival)\n\t\t\t\t{\n\t\t\t\t\ttex->base = Image_GetTexture(imagename, subpath, imageflags|IF_NOWORKER, NULL, NULL, width, height, basefmt);\n\t\t\t\t\tif (!TEXLOADED(tex->base))\n\t\t\t\t\t{\n\t\t\t\t\t\ttex->base = Image_GetTexture(fallbackname, subpath, imageflags|IF_NOWORKER, NULL, NULL, width, height, basefmt);\n\t\t\t\t\t\tif (TEXLOADED(tex->base))\n\t\t\t\t\t\t\tQ_strncpyz(imagename, fallbackname, sizeof(imagename));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (!TEXLOADED(tex->base))\n\t\t\t\t\ttex->base = Image_GetTexture(imagename, subpath, imageflags, srcdata, palette, width, height, basefmt);\n\t\t\t}\n\t\t\telse if (!TEXVALID(tex->base))\n\t\t\t\ttex->base = Image_GetTexture(imagename, subpath, imageflags, srcdata, palette, width, height, basefmt);\n\t\t}\n\n\t\tif (loadflags & SHADER_HASPALETTED)\n\t\t{\n\t\t\tif (!TEXVALID(tex->paletted) && *mapname)\n\t\t\t\ttex->paletted = R_LoadHiResTexture(va(\"%s_pal\", mapname), NULL, imageflags|IF_NEAREST|IF_PALETTIZE);\n\t\t\tif (!TEXVALID(tex->paletted))\n\t\t\t\ttex->paletted = Image_GetTexture(va(\"%s_pal\", imagename), subpath, imageflags|IF_NEAREST|IF_NOSRGB|IF_PALETTIZE, srcdata, palette, width, height, (basefmt==TF_MIP4_SOLID8)?TF_MIP4_P8:PTI_P8);\n\t\t}\n\n\t\timageflags |= IF_LOWPRIORITY;\n\t\t//all the rest need/want an alpha channel in some form.\n\t\timageflags &= ~IF_NOALPHA;\n\t\timageflags |= IF_NOGAMMA;\n\n\t\tif (loadflags & SHADER_HASNORMALMAP||*imagename=='#')\n\t\t{\n\t\t\textern cvar_t r_shadow_bumpscale_basetexture;\n\t\t\tif (!TEXVALID(tex->bump) && *mapname)\n\t\t\t\ttex->bump = R_LoadHiResTexture(va(\"%s_norm\", mapname), NULL, imageflags|IF_TRYBUMP|IF_NOSRGB);\n\t\t\tif (!TEXVALID(tex->bump) && (r_shadow_bumpscale_basetexture.ival||*imagename=='#'||gl_load24bit.ival))\n\t\t\t{\n\t\t\t\tqbyte *fallbackheight;\n\t\t\t\tif ((r_shadow_bumpscale_basetexture.ival||*imagename=='#') && !(basefmt&PTI_FULLMIPCHAIN))\n\t\t\t\t\tfallbackheight = srcdata;\t//generate normalmap from assumed heights.\n\t\t\t\telse\n\t\t\t\t\tfallbackheight = NULL;\t\t//disabled\n\t\t\t\ttex->bump = Image_GetTexture(va(\"%s_norm\", imagename), subpath, imageflags|IF_TRYBUMP|IF_NOSRGB|(*imagename=='#'?IF_LINEAR:0), fallbackheight, palette, width, height, TF_HEIGHT8PAL);\n\t\t\t}\n\t\t}\n\n\t\tif (loadflags & SHADER_HASTOPBOTTOM)\n\t\t{\n\t\t\tif (!TEXVALID(tex->loweroverlay) && *mapname)\n\t\t\t\ttex->loweroverlay = R_LoadHiResTexture(va(\"%s_pants\", mapname), NULL, imageflags);\n\t\t\tif (!TEXVALID(tex->loweroverlay))\n\t\t\t\ttex->loweroverlay = Image_GetTexture(va(\"%s_pants\", imagename), subpath, imageflags, NULL, palette, width, height, 0);\n\t\t}\n\t\tif (loadflags & SHADER_HASTOPBOTTOM)\n\t\t{\n\t\t\tif (!TEXVALID(tex->upperoverlay) && *mapname)\n\t\t\t\ttex->upperoverlay = R_LoadHiResTexture(va(\"%s_shirt\", mapname), NULL, imageflags);\n\t\t\tif (!TEXVALID(tex->upperoverlay))\n\t\t\t\ttex->upperoverlay = Image_GetTexture(va(\"%s_shirt\", imagename), subpath, imageflags, NULL, palette, width, height, 0);\n\t\t}\n\n\t\tif (loadflags & SHADER_HASGLOSS)\n\t\t{\n\t\t\tif (!TEXVALID(tex->specular) && *mapname)\n\t\t\t\ttex->specular = R_LoadHiResTexture(va(\"%s_gloss\", mapname), NULL, imageflags);\n\t\t\tif (!TEXVALID(tex->specular))\n\t\t\t\ttex->specular = Image_GetTexture(va(\"%s_gloss\", imagename), subpath, imageflags, NULL, palette, width, height, 0);\n\t\t}\n\t\t\n\t\tif (tex->reflectcube)\n\t\t{\n\t\t\tif (!TEXVALID(tex->reflectmask) && *mapname)\n\t\t\t\ttex->reflectmask = R_LoadHiResTexture(va(\"%s_reflect\", mapname), NULL, imageflags);\n\t\t\tif (!TEXVALID(tex->reflectmask))\n\t\t\t\ttex->reflectmask = Image_GetTexture(va(\"%s_reflect\", imagename), subpath, imageflags, NULL, NULL, width, height, TF_INVALID);\n\t\t}\n\n\t\tif (loadflags & SHADER_HASFULLBRIGHT)\n\t\t{\n\t\t\tif (!TEXVALID(tex->fullbright) && *mapname)\n\t\t\t\ttex->fullbright = R_LoadHiResTexture(va(\"%s_luma\", mapname), NULL, imageflags);\n\t\t\tif (!TEXVALID(tex->fullbright))\n\t\t\t{\n\t\t\t\tint s=-1;\n\t\t\t\tif (srcdata && !(basefmt&PTI_FULLMIPCHAIN) && (!palette || palette == host_basepal))\n\t\t\t\tfor(s = width*height-1; s>=0; s--)\n\t\t\t\t{\n\t\t\t\t\tif (srcdata[s] >= 256-vid.fullbright)\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\ttex->fullbright = Image_GetTexture(va(\"%s_luma:%s_glow\", imagename,imagename), subpath, imageflags, (s>=0)?srcdata:NULL, palette, width, height, TF_TRANS8_FULLBRIGHT);\n\t\t\t}\n\t\t}\n\t}\n}\n\nshader_t *Mod_RegisterBasicShader(struct model_s *mod, const char *texname, unsigned int usageflags, const char *shadertext, uploadfmt_t pixelfmt, unsigned int width, unsigned int height, void *pixeldata, void *palettedata)\n{\n\textern cvar_t r_fb_bmodels, r_fb_models;\n\textern cvar_t gl_specular;\n\tshader_t *s;\n\tunsigned int maps;\n\tchar mapbase[64];\n\tif (shadertext)\n\t\ts = R_RegisterShader(texname, usageflags, shadertext);\n\telse if (mod->type != mod_brush)\n\t\ts = R_RegisterCustom(mod, texname, usageflags, Shader_DefaultSkin, NULL);\n\telse\n\t\ts = R_RegisterCustom(mod, texname, usageflags, Shader_DefaultBSPLM, NULL);\n\n\tmaps = 0;\n\tmaps |= SHADER_HASPALETTED;\n\tmaps |= SHADER_HASDIFFUSE;\n\tif (mod->type == mod_alias)\n\t\tmaps |= SHADER_HASTOPBOTTOM;\n\tif ((mod->type == mod_brush)?r_fb_bmodels.ival:r_fb_models.ival)\n\t\tmaps |= SHADER_HASFULLBRIGHT;\n\tif (r_loadbumpmapping || s->defaulttextures->reflectcube)\n\t\tmaps |= SHADER_HASNORMALMAP;\n\tif (gl_specular.ival)\n\t\tmaps |= SHADER_HASGLOSS;\n\tCOM_FileBase(mod->name, mapbase, sizeof(mapbase));\n\tR_BuildLegacyTexnums(s, texname, mapbase, maps, 0, pixelfmt, width, height, pixeldata, palettedata);\n\treturn s;\n}\n\nvoid Shader_DefaultScript(parsestate_t *ps, const char *shortname, const void *args)\n{\n\tconst char *f = args;\n\tif (!args)\n\t\treturn;\n\twhile (*f == ' ' || *f == '\\t' || *f == '\\n' || *f == '\\r')\n\t\tf++;\n\tif (*f == '{')\n\t{\n\t\tf++;\n\t\tShader_ReadShader(ps, (void*)f, NULL);\n\t}\n}\n\nvoid Shader_DefaultBSPLM(parsestate_t *ps, const char *shortname, const void *args)\n{\n\tshader_t *s = ps->s;\n\tchar *builtin = NULL;\n\tif (Shader_ParseShader(ps, \"defaultwall\"))\n\t\treturn;\n\n\tif (!builtin && r_softwarebanding && (qrenderer == QR_OPENGL || qrenderer == QR_VULKAN) && sh_config.progs_supported)\n\t\tbuiltin = (\n\t\t\t\t\"{\\n\"\n\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\"program defaultwall#EIGHTBIT\\n\"\n\t\t\t\t\t\t\"map $colourmap\\n\"\n\t\t\t\t\t\"}\\n\"\n\t\t\t\t\"}\\n\"\n\t\t\t);\n\n\tif (!builtin && r_lightmap.ival)\n\t\tbuiltin = (\n\t\t\t\t\"{\\n\"\n\t\t\t\t\"fte_program drawflat_wall#LM\\n\"\n\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\"map $lightmap\\n\"\n\t\t\t\t\t\t\"tcgen lightmap\\n\"\n\t\t\t\t\t\"}\\n\"\n\t\t\t\t\"}\\n\"\n\t\t\t);\n\n\tif (!builtin && r_drawflat.ival)\n\t\tbuiltin = (\n\t\t\t\t\"{\\n\"\n\t\t\t\t\t\"fte_program drawflat_wall\\n\"\n\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\"map $lightmap\\n\"\n\t\t\t\t\t\t\"tcgen lightmap\\n\"\n\t\t\t\t\t\t\"rgbgen srgb $r_floorcolour\\n\"\n\t\t\t\t\t\"}\\n\"\n\t\t\t\t\"}\\n\"\n\t\t\t);\n\n\n\tif (!builtin && r_lightprepass)\n\t{\n\t\tbuiltin = (\n\t\t\t\"{\\n\"\n\t\t\t\t\"{\\n\"\n\t\t\t\t\t\"fte_program lpp_wall\\n\"\n\t\t\t\t\t\"map $gbuffer2\\n\"\t//diffuse lighting info\n\t\t\t\t\t\"map $gbuffer3\\n\"\t//specular lighting info\n\t\t\t\t\"}\\n\"\n\n\t\t\t\t//this is drawn during the initial gbuffer pass to prepare it\n\t\t\t\t\"fte_bemode gbuffer\\n\"\n\t\t\t\t\"{\\n\"\n\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\"fte_program lpp_depthnorm\\n\"\n//\t\t\t\t\t\t\"map $normalmap\\n\"\n\t\t\t\t\t\t\"tcgen base\\n\"\n\t\t\t\t\t\"}\\n\"\n\t\t\t\t\"}\\n\"\n\t\t\t\"}\\n\"\n\t\t);\n\t}\n\t//d3d has no position-invariant. this results in all sorts of glitches, so try not to use it.\n\tif (!builtin && ((sh_config.progs_supported && (qrenderer == QR_OPENGL/*||qrenderer == QR_DIRECT3D9*/)) || sh_config.progs_required))\n\t{\n\t\t\tbuiltin = (\n\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\"fte_program defaultwall\\n\"\n\t\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\t\"map $diffuse\\n\"\n\t\t\t\t\t\t\"}\\n\"\n\t\t\t\t\t\"}\\n\"\n\t\t\t\t);\n\t}\n\tif (!builtin)\n\t\tbuiltin = (\n\t\t\t\t\"{\\n\"\n/*\t\t\t\t\t\"if $deluxmap\\n\"\n\t\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\t\"map $normalmap\\n\"\n\t\t\t\t\t\t\t\"tcgen base\\n\"\n\t\t\t\t\t\t\t\"depthwrite\\n\"\n\t\t\t\t\t\t\"}\\n\"\n\t\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\t\"map $deluxmap\\n\"\n\t\t\t\t\t\t\t\"tcgen lightmap\\n\"\n\t\t\t\t\t\t\"}\\n\"\n\t\t\t\t\t\"endif\\n\"\n*///\t\t\t\t\"if !r_fullbright\\n\"\n\t\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\t\"map $lightmap\\n\"\n//\t\t\t\t\t\t\t\"if $deluxmap\\n\"\n//\t\t\t\t\t\t\t\t\"blendfunc gl_dst_color gl_zero\\n\"\n//\t\t\t\t\t\t\t\"endif\\n\"\n\t\t\t\t\t\t\"}\\n\"\n//\t\t\t\t\t\"endif\\n\"\n\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\"map $diffuse\\n\"\n\t\t\t\t\t\t\"tcgen base\\n\"\n//\t\t\t\t\t\t\"if $deluxmap || !r_fullbright\\n\"\n//\t\t\t\t\t\t\t\"blendfunc gl_dst_color gl_zero\\n\"\n\t\t\t\t\t\t\t\"blendfunc filter\\n\"\n//\t\t\t\t\t\t\"endif\\n\"\n\t\t\t\t\t\"}\\n\"\n\t\t\t\t\t\"if gl_fb_bmodels\\n\"\n\t\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\t\"map $fullbright\\n\"\n\t\t\t\t\t\t\t\"blendfunc add\\n\"\n\t\t\t\t\t\t\t\"depthfunc equal\\n\"\n\t\t\t\t\t\t\"}\\n\"\n\t\t\t\t\t\"endif\\n\"\n\t\t\t\t\"}\\n\"\n\t\t\t);\n\n\tShader_DefaultScript(ps, shortname, builtin);\n\n\tif (r_lightprepass)\n\t\ts->flags |= SHADER_HASNORMALMAP;\n}\n\nvoid Shader_DefaultCinematic(parsestate_t *ps, const char *shortname, const void *args)\n{\n\tShader_DefaultScript(ps, shortname,\n\t\tva(\n\t\t\t\"{\\n\"\n\t\t\t\t\"program default2d\\n\"\n\t\t\t\t\"{\\n\"\n\t\t\t\t\t\"videomap \\\"%s\\\"\\n\"\n\t\t\t\t\t\"blendfunc gl_one gl_one_minus_src_alpha\\n\"\n\t\t\t\t\"}\\n\"\n\t\t\t\"}\\n\"\n\t\t, (const char*)args)\n\t);\n}\n\n/*shortname should begin with 'skybox_'*/\nvoid Shader_DefaultSkybox(parsestate_t *ps, const char *shortname, const void *args)\n{\n\tshader_t *s = ps->s;\n\tint i;\n\tShader_DefaultScript(ps, shortname,\n\t\tva(\n\t\t\t\"{\\n\"\n\t\t\t\t\"sort sky\\n\"\n\t\t\t\t\"surfaceparm nodlight\\n\"\n\t\t\t\t\"skyparms %s - -\\n\"\n\t\t\t\"}\\n\"\n\t\t, shortname+7)\n\t);\n\n\tfor (i = 0; i < 6; i++)\n\t{\n\t\tif (s->skydome->farbox_textures[i] == missing_texture)\n\t\t{\n\t\t\tif (s->skydome)\n\t\t\t\tZ_Free(s->skydome);\n\t\t\ts->skydome = NULL;\n\t\t\treturn;\n\t\t}\n\t}\n}\n\nchar *Shader_DefaultBSPWater(parsestate_t *ps, const char *shortname, char *buffer, size_t buffersize)\n{\n\tshader_t *s = ps->s;\n\tint wstyle;\n\tint type;\n\tfloat alpha;\n\tqboolean explicitalpha = false;\n\tcvar_t *alphavars[] = {\t&r_wateralpha, &r_lavaalpha, &r_slimealpha, &r_telealpha};\n\tcvar_t *stylevars[] = {\t&r_waterstyle, &r_lavastyle, &r_slimestyle, &r_telestyle};\n\n\tif (!strncmp(shortname, \"*portal\", 7))\n\t{\n\t\treturn\t\"{\\n\"\n\t\t\t\t\t\"portal\\n\"\n\t\t\t\t\"}\\n\";\n\t}\n\telse if (!strncmp(shortname, \"*lava\", 5))\n\t\ttype = 1;\n\telse if (!strncmp(shortname, \"*slime\", 6))\n\t\ttype = 2;\n\telse if (!strncmp(shortname, \"*tele\", 5))\n\t\ttype = 3;\n\telse\n\t\ttype = 0;\n\talpha = Shader_FloatArgument(s, \"ALPHA\");\n\tif (alpha)\n\t\texplicitalpha = true;\n\telse\n\t{\n\t\tif (cls.allow_watervis)\n\t\t\talpha = *alphavars[type]->string?alphavars[type]->value:alphavars[0]->value;\n\t\telse\n\t\t\talpha = 1;\n\t}\n\n\tif (alpha <= 0)\n\t\twstyle = -1;\n\telse if (r_fastturb.ival)\n\t\twstyle = 0;\n\telse if (*stylevars[type]->string)\n\t\twstyle = stylevars[type]->ival;\n\telse if (stylevars[0]->ival > 0)\n\t\twstyle = stylevars[0]->ival;\n\telse\n\t\twstyle = 1;\n\n\tif ((wstyle < 0 || wstyle > 1) && !cls.allow_watervis)\n\t\twstyle = 1;\n\n\tif (wstyle > 1 && !sh_config.progs_supported)\n\t\twstyle = 1;\n\n\t//extra silly limitations\n\tswitch(qrenderer)\n\t{\n#ifdef GLQUAKE\n\tcase QR_OPENGL:\n\t\tif (wstyle > 2 && !gl_config.ext_framebuffer_objects)\n\t\t\twstyle = 2;\n\t\tbreak;\n#endif\n#ifdef VKQUAKE\n\tcase QR_VULKAN:\n\t\tif (wstyle > 3)\n\t\t\twstyle = 3;\n\t\tbreak;\n#endif\n\tdefault:\t//altwater not supported with other renderers\n\t\tif (wstyle > 1)\n\t\t\twstyle = 1;\n\t}\n\n\tswitch(wstyle)\n\t{\n\tcase -1:\t//invisible\n\t\treturn (\n\t\t\t\"{\\n\"\n\t\t\t\t\"surfaceparm nodraw\\n\"\n\t\t\t\t\"surfaceparm nodlight\\n\"\n\t\t\t\t\"surfaceparm nomarks\\n\"\n\t\t\t\t\"surfaceparm hasdiffuse\\n\"\n\t\t\t\"}\\n\"\n\t\t);\n\tcase -2:\t//regular with r_wateralpha forced off.\n\t\treturn (\n\t\t\t\"{\\n\"\n\t\t\t\t\"fte_program defaultwarp\\n\"\n\t\t\t\t\"{\\n\"\n\t\t\t\t\t\"map $diffuse\\n\"\n\t\t\t\t\t\"tcmod turb 0.02 0.1 0.5 0.1\\n\"\n\t\t\t\t\"}\\n\"\n\t\t\t\t\"surfaceparm nodlight\\n\"\n\t\t\t\t\"surfaceparm nomarks\\n\"\n\t\t\t\t\"surfaceparm hasdiffuse\\n\"\n\t\t\t\"}\\n\"\n\t\t);\n\tcase 0:\t//fastturb\n\t\treturn (\n\t\t\t\"{\\n\"\n\t\t\t\t\"{\\n\"\n\t\t\t\t\t//\"program defaultfill\\n\"\n\t\t\t\t\t\"map $whiteimage\\n\"\n\t\t\t\t\t\"rgbgen srgb $r_fastturbcolour\\n\"\n\t\t\t\t\"}\\n\"\n\t\t\t\t\"surfaceparm nodlight\\n\"\n\t\t\t\t\"surfaceparm nomarks\\n\"\n\t\t\t\t\"surfaceparm hasdiffuse\\n\"\n\t\t\t\"}\\n\"\n\t\t);\n\tdefault:\n\tcase 1:\t//vanilla style\n\t\tQ_snprintfz(buffer, buffersize, \n\t\t\t\t\"{\\n\"\n\t\t\t\t\t\"surfaceparm nodlight\\n\"\n\t\t\t\t\t\"surfaceparm nomarks\\n\"\n\t\t\t\t\t\"if %g < 1\\n\"\n\t\t\t\t\t\t\"sort underwater\\n\"\n\t\t\t\t\t\"endif\\n\"\n\t\t\t\t\t\"fte_program defaultwarp%s\\n\"\n\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\"map $diffuse\\n\"\n\t\t\t\t\t\t\"tcmod turb 0.02 0.1 0.5 0.1\\n\"\n\t\t\t\t\t\t\"if %g < 1\\n\"\n\t\t\t\t\t\t\t\"alphagen const %g\\n\"\n\t\t\t\t\t\t\t\"blendfunc gl_src_alpha gl_one_minus_src_alpha\\n\"\n\t\t\t\t\t\t\"endif\\n\"\n\t\t\t\t\t\"}\\n\"\n\t\t\t\t\t\"surfaceparm hasdiffuse\\n\"\n\t\t\t\t\"}\\n\"\n\t\t\t\t, alpha, (explicitalpha||alpha==1)?\"\":va(\"#ALPHA=%g\",alpha), alpha, alpha);\n\t\treturn buffer;\n\tcase 2:\t//refraction of the underwater surface, with a fresnel\n\t\treturn (\n\t\t\t\"{\\n\"\n\t\t\t\t\"surfaceparm nodlight\\n\"\n\t\t\t\t\"surfaceparm nomarks\\n\"\n\t\t\t\t\"{\\n\"\n\t\t\t\t\t\"program altwater#FRESNEL=4\\n\"\n\t\t\t\t\t\"map $refraction\\n\"\n\t\t\t\t\t\"map $null\\n\"//$reflection\n\t\t\t\t\t\"map $null\\n\"//$ripplemap\n\t\t\t\t\t\"map $null\\n\"//$refractiondepth\n\t\t\t\t\"}\\n\"\n\t\t\t\t\"surfaceparm hasdiffuse\\n\"\n\t\t\t\"}\\n\"\n\t\t);\n\tcase 3:\t//reflections\n\t\treturn (\n\t\t\t\"{\\n\"\n\t\t\t\t\"surfaceparm nodlight\\n\"\n\t\t\t\t\"surfaceparm nomarks\\n\"\n\t\t\t\t\"{\\n\"\n\t\t\t\t\t\"program altwater#REFLECT#FRESNEL=4\\n\"\n\t\t\t\t\t\"map $refraction\\n\"\n\t\t\t\t\t\"map $reflection\\n\"\n\t\t\t\t\t\"map $null\\n\"//$ripplemap\n\t\t\t\t\t\"map $null\\n\"//$refractiondepth\n\t\t\t\t\"}\\n\"\n\t\t\t\t\"surfaceparm hasdiffuse\\n\"\n\t\t\t\"}\\n\"\n\t\t);\n\tcase 4:\t//ripples\n\t\treturn (\n\t\t\t\"{\\n\"\n\t\t\t\t\"surfaceparm nodlight\\n\"\n\t\t\t\t\"surfaceparm nomarks\\n\"\n\t\t\t\t\"{\\n\"\n\t\t\t\t\t\"program altwater#RIPPLEMAP#FRESNEL=4\\n\"\n\t\t\t\t\t\"map $refraction\\n\"\n\t\t\t\t\t\"map $null\\n\"//$reflection\n\t\t\t\t\t\"map $ripplemap\\n\"\n\t\t\t\t\t\"map $null\\n\"//$refractiondepth\n\t\t\t\t\"}\\n\"\n\t\t\t\t\"surfaceparm hasdiffuse\\n\"\n\t\t\t\"}\\n\"\n\t\t);\n\tcase 5:\t//ripples+reflections\n\t\treturn (\n\t\t\t\"{\\n\"\n\t\t\t\t\"surfaceparm nodlight\\n\"\n\t\t\t\t\"surfaceparm nomarks\\n\"\n\t\t\t\t\"{\\n\"\n\t\t\t\t\t\"program altwater#REFLECT#RIPPLEMAP#FRESNEL=4\\n\"\n\t\t\t\t\t\"map $refraction\\n\"\n\t\t\t\t\t\"map $reflection\\n\"\n\t\t\t\t\t\"map $ripplemap\\n\"\n\t\t\t\t\t\"map $null\\n\"//$refractiondepth\n\t\t\t\t\"}\\n\"\n\t\t\t\t\"surfaceparm hasdiffuse\\n\"\n\t\t\t\"}\\n\"\n\t\t);\n\t}\n}\n\nvoid Shader_DefaultWaterShader(parsestate_t *ps, const char *shortname, const void *args)\n{\n\tchar tmpbuffer[2048];\n\tShader_DefaultScript(ps, shortname, Shader_DefaultBSPWater(ps, shortname, tmpbuffer, sizeof(tmpbuffer)));\n}\nvoid Shader_DefaultBSPQ2(parsestate_t *ps, const char *shortname, const void *args)\n{\n\tif (!strncmp(shortname, \"sky/\", 4))\n\t{\n\t\tShader_DefaultScript(ps, shortname,\n\t\t\t\t\"{\\n\"\n\t\t\t\t\t\"sort sky\\n\"\n\t\t\t\t\t\"surfaceparm nodlight\\n\"\n\t\t\t\t\t\"skyparms - - -\\n\"\n\t\t\t\t\"}\\n\"\n\t\t\t);\n\t}\n\telse if (Shader_FloatArgument(ps->s, \"WARP\"))//!strncmp(shortname, \"warp/\", 5) || !strncmp(shortname, \"warp33/\", 7) || !strncmp(shortname, \"warp66/\", 7))\n\t{\n\t\tchar tmpbuffer[2048];\n\t\tShader_DefaultScript(ps, shortname, Shader_DefaultBSPWater(ps, shortname, tmpbuffer, sizeof(tmpbuffer)));\n\t}\n\telse if (Shader_FloatArgument(ps->s, \"FLOW\"))\n\t{\n\t\tif (Shader_FloatArgument(ps->s, \"ALPHA\")) {\n\t\t\tShader_DefaultScript(ps, shortname,\n\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\t\"map $diffuse\\n\"\n\t\t\t\t\t\t\t\"alphagen const $#ALPHA\\n\"\n\t\t\t\t\t\t\t\"tcmod scroll -1 0\\n\"\n\t\t\t\t\t\t\t\"blendfunc blend\\n\"\n\t\t\t\t\t\t\"}\\n\"\n\t\t\t\t\t\"}\\n\"\n\t\t\t\t);\n\t\t} else {\n\t\t\tShader_DefaultScript(ps, shortname,\n\t\t\t\t\"{\\n\"\n\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\"map $diffuse\\n\"\n\t\t\t\t\t\t\"tcmod scroll -1 0\\n\"\n\t\t\t\t\t\"}\\n\"\n\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\"map $lightmap\\n\"\n\t\t\t\t\t\t\"if gl_overbright > 1\\n\"\n\t\t\t\t\t\t\"blendfunc gl_dst_color gl_src_color\\n\"\n\t\t\t\t\t\t\"else\\n\"\n\t\t\t\t\t\t\"blendfunc gl_dst_color gl_zero\\n\"\n\t\t\t\t\t\t\"endif\\n\"\n\t\t\t\t\t\t\"depthfunc equal\\n\"\n\t\t\t\t\t\"}\\n\"\n\t\t\t\t\"}\\n\"\n\t\t\t\t);\n\t\t}\n\t}\n\telse if (Shader_FloatArgument(ps->s, \"ALPHA\"))//   !strncmp(shortname, \"trans/\", 6))\n\t{\n\t\tShader_DefaultScript(ps, shortname,\n\t\t\t\t\"{\\n\"\n\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\"map $diffuse\\n\"\n\t\t\t\t\t\t\"alphagen const $#ALPHA\\n\"\n\t\t\t\t\t\t\"blendfunc blend\\n\"\n\t\t\t\t\t\"}\\n\"\n\t\t\t\t\"}\\n\"\n\t\t\t);\n\t}\n\telse\n\t\tShader_DefaultBSPLM(ps, shortname, args);\n}\n\nvoid Shader_DefaultBSPQ1(parsestate_t *ps, const char *shortname, const void *args)\n{\n\tchar *builtin = NULL;\n\tchar tmpbuffer[2048];\n\n\tif (!strcmp(shortname, \"mirror_portal\"))\n\t{\n\t\tbuiltin =\t\"{\\n\"\n\t\t\t\t\t\t\"portal\\n\"\n\t\t\t\t\t\"}\\n\";\n\t}\n\telse if (r_mirroralpha.value < 1 && (!strcmp(shortname, \"window02_1\") || !strncmp(shortname, \"mirror\", 6)))\n\t{\n\t\tif (r_mirroralpha.value < 0)\n\t\t{\n\t\t\tbuiltin =\t\"{\\n\"\n\t\t\t\t\t\t\t\"portal\\n\"\n\t\t\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\t\t\"map $diffuse\\n\"\n\t\t\t\t\t\t\t\t\"blendfunc blend\\n\"\n\t\t\t\t\t\t\t\t\"alphagen portal 512\\n\"\n\t\t\t\t\t\t\t\t\"depthwrite\\n\"\n\t\t\t\t\t\t\t\"}\\n\"\n\t\t\t\t\t\t\"}\\n\";\n\t\t}\n\t\telse\n\t\t{\n\t\t\tbuiltin =\t\"{\\n\"\n\t\t\t\t\t\t\t\"portal\\n\"\n\t\t\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\t\t\"map $diffuse\\n\"\n\t\t\t\t\t\t\t\t\"blendfunc blend\\n\"\n\t\t\t\t\t\t\t\t\"alphagen const $r_mirroralpha\\n\"\n\t\t\t\t\t\t\t\t\"depthwrite\\n\"\n\t\t\t\t\t\t\t\"}\\n\"\n\t\t\t\t\t\t\t\"surfaceparm nodlight\\n\"\n\t\t\t\t\t\t\"}\\n\";\n\t\t}\n\n\t}\n\n\tif (!builtin && (*shortname == '*' || *shortname == '!'))\n\t{\n\t\tbuiltin = Shader_DefaultBSPWater(ps, shortname, tmpbuffer, sizeof(tmpbuffer));\n\t}\n\tif (!builtin && !strncmp(shortname, \"sky\", 3))\n\t{\n\t\t//q1 sky\n\t\t/*if (r_fastsky.ival)\n\t\t{\n\t\t\tbuiltin = (\n\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\"sort sky\\n\"\n\t\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\t\"map $whiteimage\\n\"\n\t\t\t\t\t\t\t\"rgbgen srgb $r_fastskycolour\\n\"\n\t\t\t\t\t\t\"}\\n\"\n\t\t\t\t\t\t\"surfaceparm nodlight\\n\"\n\t\t\t\t\t\"}\\n\"\n\t\t\t\t);\n\t\t}*/\n\t\t/*else if (*r_skyboxname.string || *cl.skyname)\n\t\t{\n\t\t\tqboolean okay;\n\t\t\tZ_Free(s->skydome);\n\t\t\ts->skydome = (skydome_t *)Z_Malloc(sizeof(skydome_t));\n\n\t\t\tokay = Shader_ParseSkySides(shortname, \"\", s->skydome->farbox_textures);\n\t\t\ts->flags |= SHADER_SKY|SHADER_NODLIGHT;\n\t\t\ts->sort = SHADER_SORT_SKY;\n\n\t\t\tif (okay)\n\t\t\t\treturn;\n\t\t\tbuiltin = NULL;\n\t\t\t//if the r_skybox failed to load or whatever, reset and fall through and just use the regular sky\n\t\t\tShader_Reset(s);\n\t\t}*/\n\t\tif (!builtin)\n\t\t\tbuiltin = (\n\t\t\t\t\"{\\n\"\n\t\t\t\t\t\"sort sky\\n\"\n\t\t\t\t\t\"program defaultsky\\n\"\n\t\t\t\t\t\"skyparms - 512 -\\n\"\n\t\t\t\t\t/*WARNING: these values are not authentic quake, only close aproximations*/\n\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\"map $diffuse\\n\"\n\t\t\t\t\t\t\"tcmod scale 10 10\\n\"\n\t\t\t\t\t\t\"tcmod scroll 0.04 0.04\\n\"\n\t\t\t\t\t\t\"depthwrite\\n\"\n\t\t\t\t\t\"}\\n\"\n\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\"map $fullbright\\n\"\n\t\t\t\t\t\t\"blendfunc blend\\n\"\n\t\t\t\t\t\t\"tcmod scale 10 10\\n\"\n\t\t\t\t\t\t\"tcmod scroll 0.02 0.02\\n\"\n\t\t\t\t\t\"}\\n\"\n\t\t\t\t\"}\\n\"\n\t\t\t);\n\t}\n\tif (!builtin && *shortname == '{')\n\t{\n\t\t/*alpha test*/\n\t\tif (sh_config.progs_supported)\n\t\t\tbuiltin = (\n\t\t\t\t\"{\\n\"\n\t\t\t\t\t\"fte_program defaultwall#MASK=0.666#MASKLT\\n\"\n\t\t\t\t\"}\\n\"\n\t\t\t);\n\t\telse\n\t\t\tbuiltin = (\n\t\t\t\t\"{\\n\"\n\t\t\t/*\t\t\"if $deluxmap\\n\"\n\t\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\t\"map $normalmap\\n\"\n\t\t\t\t\t\t\t\"tcgen base\\n\"\n\t\t\t\t\t\t\"}\\n\"\n\t\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\t\"map $deluxmap\\n\"\n\t\t\t\t\t\t\t\"tcgen lightmap\\n\"\n\t\t\t\t\t\t\"}\\n\"\n\t\t\t\t\t\"endif\\n\"*/\n\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\"map $diffuse\\n\"\n\t\t\t\t\t\t\"tcgen base\\n\"\n\t\t\t\t\t\t\"alphafunc ge128\\n\"\n\t\t\t\t\t\"}\\n\"\n\t//\t\t\t\t\"if $lightmap\\n\"\n\t\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\t\"map $lightmap\\n\"\n\t\t\t\t\t\t\t\"if gl_overbright > 1\\n\"\n\t\t\t\t\t\t\t\"blendfunc gl_dst_color gl_src_color\\n\"\t//scale it up twice. will probably still get clamped, but what can you do\n\t\t\t\t\t\t\t\"else\\n\"\n\t\t\t\t\t\t\t\"blendfunc gl_dst_color gl_zero\\n\"\n\t\t\t\t\t\t\t\"endif\\n\"\n\t\t\t\t\t\t\t\"depthfunc equal\\n\"\n\t\t\t\t\t\t\"}\\n\"\n\t//\t\t\t\t\"endif\\n\"\n\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\"map $fullbright\\n\"\n\t\t\t\t\t\t\"blendfunc add\\n\"\n\t\t\t\t\t\t\"depthfunc equal\\n\"\n\t\t\t\t\t\"}\\n\"\n\t\t\t\t\"}\\n\"\n\t\t\t);\n\t}\n\n\t/* Half-Life requirement ~eukara */\n\tif (!builtin && !strncmp(shortname, \"scroll\", 6))\n\t{\n\t\tbuiltin = (\n\t\t\t\"{\\n\"\n\t\t\t\t\"fte_program defaultwall#SCROLL\\n\"\n\t\t\t\"}\\n\"\n\t\t);\n\t}\n\n\tif (builtin)\n\t\tShader_DefaultScript(ps, shortname, builtin);\n\telse\n\t\tShader_DefaultBSPLM(ps, shortname, args);\n}\n\nvoid Shader_DefaultBSPVertex(parsestate_t *ps, const char *shortname, const void *args)\n{\n\tchar *builtin = NULL;\n\n\tif (Shader_ParseShader(ps, \"defaultvertexlit\"))\n\t\treturn;\n\n\tif (!builtin)\n\t{\n\t\tbuiltin = (\n\t\t\t\"{\\n\"\n\t\t\t\t\"program defaultwall#VERTEXLIT\\n\"\n\t\t\t\t\"{\\n\"\n\t\t\t\t\t\"map $diffuse\\n\"\n\t\t\t\t\t\"rgbgen vertex\\n\"\n\t\t\t\t\t\"alphagen vertex\\n\"\n\t\t\t\t\"}\\n\"\n\t\t\t\"}\\n\"\n\t\t);\n\t}\n\n\tShader_DefaultScript(ps, shortname, builtin);\n}\nvoid Shader_DefaultBSPFlare(parsestate_t *ps, const char *shortname, const void *args)\n{\n\tshader_t *s = ps->s;\n\tshaderpass_t *pass;\n\tif (Shader_ParseShader(ps, \"defaultflare\"))\n\t\treturn;\n\n\tpass = &s->passes[0];\n\tpass->flags = SHADER_PASS_NOCOLORARRAY;\n\tpass->shaderbits |= SBITS_SRCBLEND_ONE|SBITS_DSTBLEND_ONE;\n\tpass->anim_frames[0] = R_LoadHiResTexture(shortname, NULL, 0);\n\tpass->rgbgen = RGB_GEN_VERTEX_LIGHTING;\n\tpass->alphagen = ALPHA_GEN_IDENTITY;\n\tpass->numtcmods = 0;\n\tpass->tcgen = TC_GEN_BASE;\n\tpass->numMergedPasses = 1;\n\tShader_SetBlendmode(pass, NULL);\n\n\tif (!TEXVALID(pass->anim_frames[0]))\n\t{\n\t\tCon_DPrintf (CON_WARNING \"Shader %s has a stage with no image: %s.\\n\", s->name, shortname );\n\t\tpass->anim_frames[0] = missing_texture;\n\t}\n\n\ts->numpasses = 1;\n\ts->numdeforms = 0;\n\ts->flags = SHADER_FLARE;\n\ts->sort = SHADER_SORT_ADDITIVE;\n\n\ts->flags |= SHADER_NODRAW;\n}\nvoid Shader_DefaultSkin(parsestate_t *ps, const char *shortname, const void *args)\n{\n\tif (Shader_ParseShader(ps, \"defaultskin\"))\n\t\treturn;\n\n\tif (r_softwarebanding && qrenderer == QR_OPENGL && sh_config.progs_supported)\n\t{\n\t\tShader_DefaultScript(ps, shortname,\n\t\t\t\"{\\n\"\n\t\t\t\t\"program defaultskin#EIGHTBIT\\n\"\n\t\t\t\t\"affine\\n\"\n\t\t\t\t\"{\\n\"\n\t\t\t\t\t\"map $colourmap\\n\"\n\t\t\t\t\"}\\n\"\n\t\t\t\"}\\n\"\n\t\t\t);\n\t\treturn;\n\t}\n\tif (r_lightprepass)\n\t{\n\t\tShader_DefaultScript(ps, shortname,\n\t\t\t\"{\\n\"\n\t\t\t\t\"{\\n\"\n\t\t\t\t\t\"fte_program lpp_wall\\n\"\n\t\t\t\t\t\"map $gbuffer2\\n\"\t//diffuse lighting info\n\t\t\t\t\t\"map $gbuffer3\\n\"\t//specular lighting info\n\t\t\t\t\"}\\n\"\n\n\t\t\t\t//this is drawn during the initial gbuffer pass to prepare it\n\t\t\t\t\"fte_bemode gbuffer\\n\"\n\t\t\t\t\"{\\n\"\n\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\"fte_program lpp_depthnorm\\n\"\n\t\t\t\t\t\t\"tcgen base\\n\"\n\t\t\t\t\t\"}\\n\"\n\t\t\t\t\"}\\n\"\n\t\t\t\"}\\n\"\n\t\t);\n\t\treturn;\n\t}\n\n\tif (r_tessellation.ival && sh_config.progs_supported)\n\t{\n\t\tShader_DefaultScript(ps, shortname,\n\t\t\t\"{\\n\"\n\t\t\t\t\"program defaultskin#TESS\\n\"\n\t\t\t\"}\\n\"\n\t\t\t);\n\t\treturn;\n\t}\n\n\tShader_DefaultScript(ps, shortname,\n\t\t\"{\\n\"\n\t\t\t\"if $lpp\\n\"\n\t\t\t\t\"program lpp_skin\\n\"\n\t\t\t\"else\\n\"\n\t\t\t\t\"program defaultskin\\n\"\n\t\t\t\"endif\\n\"\n\t\t\t\"if gl_affinemodels\\n\"\n\t\t\t\t\"affine\\n\"\n\t\t\t\"endif\\n\"\n\t\t\t\"{\\n\"\n\t\t\t\t\"map $diffuse\\n\"\n\t\t\t\t\"rgbgen lightingDiffuse\\n\"\n\t\t\t\t\"if #BLEND\\n\"\n\t\t\t\t\t\"blendfunc blend\\n\"\t//straight blend\n\t\t\t\t\"endif\\n\"\n\t\t\t\"}\\n\"\n\t\t\t\"{\\n\"\n\t\t\t\t\"map $loweroverlay\\n\"\n\t\t\t\t\"rgbgen bottomcolor\\n\"\n\t\t\t\t\"blendfunc gl_src_alpha gl_one\\n\"\n\t\t\t\"}\\n\"\n\t\t\t\"{\\n\"\n\t\t\t\t\"map $upperoverlay\\n\"\n\t\t\t\t\"rgbgen topcolor\\n\"\n\t\t\t\t\"blendfunc gl_src_alpha gl_one\\n\"\n\t\t\t\"}\\n\"\n\t\t\t\"{\\n\"\n\t\t\t\t\"map $fullbright\\n\"\n\t\t\t\t\"blendfunc add\\n\"\n\t\t\t\"}\\n\"\n\t\t\"}\\n\"\n\t\t);\n}\nvoid Shader_DefaultSkinShell(parsestate_t *ps, const char *shortname, const void *args)\n{\n\tif (Shader_ParseShader(ps, \"defaultskinshell\"))\n\t\treturn;\n\n\tShader_DefaultScript(ps, shortname,\n\t\t\"{\\n\"\n\t\t\t\"sort seethrough\\n\"\t//before blend, but after other stuff. should fix most issues with shotgun etc effects obscuring it.\n//\t\t\t\"deformvertexes normal 1 1\\n\"\n\t\t\t//draw it with depth but no colours at all\n\t\t\t\"{\\n\"\n\t\t\t\t\"map $whiteimage\\n\"\n\t\t\t\t\"maskcolor\\n\"\n\t\t\t\t\"depthwrite\\n\"\n\t\t\t\"}\\n\"\n\t\t\t//now draw it again, depthfunc = equal should fill only the near-side, avoiding any excess-brightness issues with overlapping triangles\n\t\t\t\"{\\n\"\n\t\t\t\t\"map $whiteimage\\n\"\n\t\t\t\t\"rgbgen entity\\n\"\n\t\t\t\t\"alphagen entity\\n\"\n\t\t\t\t\"blendfunc blend\\n\"\n\t\t\t\"}\\n\"\n\t\t\"}\\n\"\n\t\t);\n}\nvoid Shader_Default2D(parsestate_t *ps, const char *shortname, const void *genargs)\n{\n\tshader_t *s = ps->s;\n\tif (Shader_ParseShader(ps, \"default2d\"))\n\t\treturn;\n\tif (sh_config.progs_supported && qrenderer != QR_DIRECT3D9\n#ifdef HAVE_LEGACY\n\t\t\t&& !dpcompat_nopremulpics.ival\n#endif\n\t)\n\t{\n\t\t//hexen2 needs premultiplied alpha to avoid looking ugly\n\t\t//but that results in problems where things are drawn with alpha not 0, so scale vertex colour by alpha in the fragment program\n\t\tShader_DefaultScript(ps, shortname,\n\t\t\t\"{\\n\"\n\t\t\t\t\"affine\\n\"\n\t\t\t\t\"nomipmaps\\n\"\n\t\t\t\t\"program default2d#PREMUL\\n\"\n\t\t\t\t\"{\\n\"\n\t\t\t\t\"clampmap $diffuse\\n\"\n\t\t\t\t\"blendfunc gl_one gl_one_minus_src_alpha\\n\"\n\t\t\t\t\"}\\n\"\n\t\t\t\t\"sort additive\\n\"\n\t\t\t\"}\\n\"\n\t\t\t);\n\t\tTEXASSIGN(s->defaulttextures->base, R_LoadHiResTexture(s->name, genargs, IF_PREMULTIPLYALPHA|IF_UIPIC|IF_NOPICMIP|IF_NOMIPMAP|IF_CLAMP|IF_HIGHPRIORITY));\n\t}\n\telse\n\t{\n\t\tShader_DefaultScript(ps, shortname,\n\t\t\t\"{\\n\"\n\t\t\t\t\"affine\\n\"\n\t\t\t\t\"nomipmaps\\n\"\n\t\t\t\t\"{\\n\"\n\t\t\t\t\t\"clampmap $diffuse\\n\"\n\t\t\t\t\t\"rgbgen vertex\\n\"\n\t\t\t\t\t\"alphagen vertex\\n\"\n\t\t\t\t\t\"blendfunc gl_src_alpha gl_one_minus_src_alpha\\n\"\n\t\t\t\t\"}\\n\"\n\t\t\t\t\"sort additive\\n\"\n\t\t\t\"}\\n\"\n\t\t\t);\n\t\tTEXASSIGN(s->defaulttextures->base, R_LoadHiResTexture(s->name, genargs, IF_UIPIC|IF_NOPICMIP|IF_NOMIPMAP|IF_CLAMP|IF_HIGHPRIORITY));\n\t}\n}\nvoid Shader_PolygonShader(struct shaderparsestate_s *ps, const char *shortname, const void *args)\n{\n\tshader_t *s = ps->s;\n\tShader_DefaultScript(ps, shortname,\n\t\t\"{\\n\"\n\t\t\t\"if $lpp\\n\"\n\t\t\t\t\"program lpp_skin\\n\"\n\t\t\t\"else\\n\"\n\t\t\t\t\"program defaultskin#NONORMALS\\n\"\n\t\t\t\"endif\\n\"\n\t\t\t\"{\\n\"\n\t\t\t\t\"map $diffuse\\n\"\n\t\t\t\t\"rgbgen vertex\\n\"\n\t\t\t\t\"alphagen vertex\\n\"\n\t\t\t\"}\\n\"\n\t\t\t\"{\\n\"\n\t\t\t\t\"map $fullbright\\n\"\n\t\t\t\t\"blendfunc add\\n\"\n\t\t\t\"}\\n\"\n\t\t\"}\\n\"\n\t\t);\n\n\tif (!s->defaulttextures->base && (s->flags & SHADER_HASDIFFUSE))\n\t\tR_BuildDefaultTexnums(NULL, s, 0);\n}\n\nstatic qboolean Shader_ReadShaderTerms(parsestate_t *ps, struct scondinfo_s *cond)\n{\n\tchar *token;\n\n\tif (!ps->ptr)\n\t\treturn false;\n\n\ttoken = COM_ParseExt (&ps->ptr, true, true);\n\n\tif ( !token[0] )\n\t\treturn true;\n\telse if (!Shader_Conditional_Read(ps, cond, token, &ps->ptr))\n\t{\n\t\tint i;\n\t\tfor (i = 0; shadermacros[i].name; i++)\n\t\t{\n\t\t\tif (!Q_stricmp (token, shadermacros[i].name))\n\t\t\t{\n#define SHADER_MACRO_ARGS 8\n\t\t\t\tint argn = 0;\n\t\t\t\tconst char *oldptr;\n\t\t\t\tchar arg[SHADER_MACRO_ARGS][256];\n\t\t\t\tchar tmp[4096], *out, *in;\n\t\t\t\t//parse args until the end of the line\n\t\t\t\twhile (ps->ptr)\n\t\t\t\t{\n\t\t\t\t\ttoken = COM_ParseExt(&ps->ptr, false, true);\n\t\t\t\t\tif ( !token[0] )\n\t\t\t\t\t{\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tif (argn <= SHADER_MACRO_ARGS)\n\t\t\t\t\t{\n\t\t\t\t\t\tQ_strncpyz(arg[argn], token, sizeof(arg[argn]));\n\t\t\t\t\t\targn++;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tfor(out = tmp, in = shadermacros[i].body; *in; )\n\t\t\t\t{\n\t\t\t\t\tif (out == tmp+countof(tmp)-1)\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tif (*in == '%' && in[1] == '%')\n\t\t\t\t\t\tin++;\t//skip doubled up percents\n\t\t\t\t\telse if (*in == '%')\n\t\t\t\t\t{\t//expand an arg\n\t\t\t\t\t\tchar *e;\n\t\t\t\t\t\tint i = strtol(in+1, &e, 0);\n\t\t\t\t\t\tif (e != in+1)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ti--;\n\t\t\t\t\t\t\tif (i >= 0 && i < countof(arg))\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tfor (in = arg[i]; *in; )\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tif (out == tmp+countof(tmp)-1)\n\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t\t*out++ = *in++;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tin = e;\n\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t*out++ = *in++;\n\t\t\t\t}\n\t\t\t\t*out = 0;\n\n\t\t\t\toldptr = ps->ptr;\n\t\t\t\tps->ptr = tmp;\n\t\t\t\tShader_ReadShaderTerms(ps, cond);\n\t\t\t\tps->ptr = oldptr;\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\tif (token[0] == '}')\n\t\t\treturn false;\n\t\telse if (token[0] == '{')\n\t\t\tShader_Readpass(ps);\n\t\telse if (Shader_Parsetok(ps, shaderkeys, token))\n\t\t\treturn false;\n\t}\n\treturn true;\n}\n\n//loads a shader string into an existing shader object, and finalises it and stuff\nstatic void Shader_ReadShader(parsestate_t *ps, const char *shadersource, shadercachefile_t *sourcefile)\n{\n\tstruct scondinfo_s cond = {0};\n\tchar **savebody = ps->saveshaderbody;\n\tshader_t *s = ps->s;\n\n\tmemset(ps, 0, sizeof(*ps));\n\tif (sourcefile)\n\t{\n\t\tps->forcedshader = *sourcefile->forcedshadername?sourcefile->forcedshadername:NULL;\n\t\tps->parseflags = sourcefile->parseflags;\n\t\tps->sourcename = sourcefile->name;\n\t}\n\telse\n\t{\n\t\tps->parseflags = 0;\n\t\tps->sourcename = \"<code>\";\n\t}\n\tps->specularexpscale = 1;\n\tps->specularvalscale = 1;\n\tps->ptr = shadersource;\n\tps->s = s;\n\n\tif (!s->defaulttextures)\n\t{\n\t\ts->defaulttextures = Z_Malloc(sizeof(*s->defaulttextures));\n\t\ts->numdefaulttextures = 0;\n\t}\n\n// set defaults\n\ts->flags = SHADER_CULL_FRONT;\n\n\twhile (Shader_ReadShaderTerms(ps, &cond))\n\t{\n\t}\n\n\tif (cond.depth)\n\t{\n\t\tCon_Printf(\"if statements without endif in shader %s\\n\", s->name);\n\t}\n\n\tShader_Finish (ps);\n\n\t//querying the shader body often requires generating the shader, which then gets parsed.\n\tif (savebody)\n\t{\n\t\tsize_t l = ps->ptr?ps->ptr - shadersource:0;\n\t\tZ_Free(*savebody);\n\t\t*savebody = BZ_Malloc(l+1);\n\t\t(*savebody)[l] = 0;\n\t\tmemcpy(*savebody, shadersource, l);\n\t}\n}\n\nstatic void Shader_LoadMaterialString(parsestate_t *ps, const char *shadertext)\n{\t//callback for our external material loaders.\n\tShader_Reset(ps);\n\tShader_ReadShader(ps, shadertext, NULL);\n}\n\nstatic qboolean Shader_ParseShader(parsestate_t *ps, const char *parsename)\n{\n\tsize_t offset = 0, length;\n\tconst char *buf = NULL;\n\tshadercachefile_t *sourcefile = NULL;\n\tconst char *file;\n\tconst char *token=\".\";\n\tsize_t i;\n\n\tif (!strchr(parsename, ':'))\n\t{\n\t\t//if the named shader is a .shader file then just directly load it.\n\t\ttoken = COM_GetFileExtension(parsename, NULL);\n\t\tif (!strcmp(token, \".mat\") || !*token)\n\t\t{\n\t\t\tchar shaderfile[MAX_QPATH];\n\t\t\tif (!*token)\n\t\t\t{\n\t\t\t\tfor (i = 0; i < materialloader_count; i++)\n\t\t\t\t{\n\t\t\t\t\tif (materialloader[i].funcs->ReadMaterial(ps, parsename, Shader_LoadMaterialString))\n\t\t\t\t\t\treturn true;\n\t\t\t\t}\n\n\t\t\t\tQ_snprintfz(shaderfile, sizeof(shaderfile), \"%s.mat\", parsename);\n\t\t\t\tfile = COM_LoadTempMoreFile(shaderfile, &length);\n\t\t\t}\n\t\t\telse\n\t\t\t\tfile = COM_LoadTempMoreFile(parsename, &length);\n\t\t\tif (file)\n\t\t\t{\n\t\t\t\tShader_Reset(ps);\n\t\t\t\ttoken = COM_ParseExt (&file, true, false);\t//we need to skip over the leading {.\n\t\t\t\tif (*token != '{')\n\t\t\t\t\ttoken = COM_ParseExt (&file, true, false);\t//try again, in case we found some legacy name.\n\t\t\t\tif (*token == '{')\n\t\t\t\t{\n\t\t\t\t\tShader_ReadShader(ps, file, NULL);\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tCon_Printf(\"file %s.shader does not appear to contain a shader\\n\", shaderfile);\n\t\t\t}\n\t\t}\n\t}\n\n\tif (Shader_LocateSource(parsename, &buf, &length, &offset, &sourcefile))\n\t{\n\t\t// the shader is in the shader scripts\n\t\tif (buf && offset < length )\n\t\t{\n\t\t\tfile = buf + offset;\n\t\t\ttoken = COM_ParseExt (&file, true, true);\n\t\t\tif ( !file || token[0] != '{' )\n\t\t\t{\n\t\t\t\tFS_FreeFile((char*)buf);\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tShader_Reset(ps);\n\n\t\t\tShader_ReadShader(ps, file, sourcefile);\n\n\t\t\treturn true;\n\t\t}\n\t}\n\n\tif (*token)\n\t{\n\t\tfor (i = 0; i < materialloader_count; i++)\n\t\t{\n\t\t\tif (materialloader[i].funcs->ReadMaterial(ps, parsename, Shader_LoadMaterialString))\n\t\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\nvoid R_UnloadShader(shader_t *shader)\n{\n\tif (!shader)\n\t\treturn;\n\tif (shader->uses <= 0)\n\t{\n\t\tCon_Printf(\"Shader double free (%p %s %i)\\n\", shader, shader->name, shader->usageflags);\n\t\treturn;\n\t}\n\tif (--shader->uses == 0)\n\t\tShader_Free(shader);\n}\nstatic shader_t *R_LoadShader (model_t *mod, const char *name, unsigned int usageflags, shader_gen_t *defaultgen, const char *genargs)\n{\n\tint i, f = -1;\n\tchar cleanname[MAX_QPATH];\n\tshader_t *s;\n\n\tif (!*name)\n\t\tname = \"gfx/unspecified\";\n\n\tCOM_AssertMainThread(\"R_LoadShader\");\n\n\tQ_strncpyz(cleanname, name, sizeof(cleanname));\n\tCOM_CleanUpPath(cleanname);\n\n\t// check the hash first\n\ts = Hash_Get(&shader_active_hash, cleanname);\n\twhile (s)\n\t{\n\t\tif (!mod || s->model == mod)\n\t\t//make sure the same texture can be used as either a lightmap or vertexlit shader\n\t\t//if it has an explicit shader overriding it then that still takes precidence. we might just have multiple copies of it.\n\t\t//q3 has a separate (internal) shader for every lightmap.\n\t\tif (!((s->usageflags ^ usageflags) & SUF_LIGHTMAP))\n\t\t{\n\t\t\tif (!s->uses)\n\t\t\t\tbreak;\n\t\t\ts->uses++;\n\t\t\treturn s;\n\t\t}\n\t\tif (s->generator == Shader_DefaultScript)\n\t\t{\t//if someone shaderfornamed and then needed a different usageflag later, just borrow from the existing one.\n\t\t\tdefaultgen = s->generator;\n\t\t\tgenargs = s->genargs;\n\t\t}\n\t\ts = Hash_GetNext(&shader_active_hash, cleanname, s);\n\t}\n\n\t// not loaded, find a free slot\n\tfor (i = 0; i < r_numshaders; i++)\n\t{\n\t\tif (!r_shaders[i] || !r_shaders[i]->uses)\n\t\t{\n\t\t\tif ( f == -1 )\t// free shader\n\t\t\t{\n\t\t\t\tf = i;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\tif (f == -1)\n\t{\n\t\tshader_t **n;\n\t\tint nm;\t\n\t\tif (strlen(cleanname) >= sizeof(s->name))\n\t\t{\n\t\t\tSys_Error( \"R_LoadShader: Shader name too long.\");\n\t\t\treturn NULL;\n\t\t}\n\t\tf = r_numshaders;\n\t\tif (f == r_maxshaders)\n\t\t{\n\t\t\tif (!r_maxshaders)\n\t\t\t\tSys_Error( \"R_LoadShader: shader system not inited.\");\n\n\t\t\tnm = r_maxshaders * 2;\n\t\t\tn = realloc(r_shaders, nm*sizeof(*n));\n\t\t\tif (!n)\n\t\t\t{\n\t\t\t\tSys_Error( \"R_LoadShader: Shader limit exceeded.\");\n\t\t\t\treturn NULL;\n\t\t\t}\n\t\t\tmemset(n+r_maxshaders, 0, (nm - r_maxshaders)*sizeof(*n));\n\t\t\tr_shaders = n;\n\t\t\tr_maxshaders = nm;\n\t\t}\n\t}\n\n\t{\n\t\tparsestate_t ps;\n\t\tchar shortname[MAX_QPATH];\n\t\tchar *argsstart;\n\t\ts = r_shaders[f];\n\t\tif (!s)\n\t\t{\n\t\t\ts = r_shaders[f] = Z_Malloc(sizeof(*s));\n\t\t}\n\t\tps.saveshaderbody = NULL;\n\t\tps.s = s;\n\t\ts->id = f;\n\t\tif (r_numshaders < f+1)\n\t\t\tr_numshaders = f+1;\n\n\t\tif (!s->defaulttextures)\n\t\t\ts->defaulttextures = Z_Malloc(sizeof(*s->defaulttextures));\n\t\telse\n\t\t\tmemset(s->defaulttextures, 0, sizeof(*s->defaulttextures));\n\t\ts->numdefaulttextures = 0;\n\t\tQ_strncpyz(s->name, cleanname, sizeof(s->name));\n\t\ts->model = mod;\n\t\ts->usageflags = usageflags;\n\t\ts->generator = defaultgen;\n\t\ts->width = 0;\n\t\ts->height = 0;\n\t\ts->uses = 1;\n\t\tif (genargs)\n\t\t\ts->genargs = strdup(genargs);\n\t\telse\n\t\t\ts->genargs = NULL;\n\n\t\t//now determine the 'short name'. ie: the shader that is loaded off disk (no args, no extension)\n\t\targsstart = *cleanname?strchr(cleanname+1, '#'):NULL;\n\t\tif (argsstart)\n\t\t\t*argsstart = 0;\n\t\tCOM_StripExtension (cleanname, shortname, sizeof(shortname));\n\n\t\tif (ruleset_allow_shaders.ival && !(usageflags & SUR_FORCEFALLBACK))\n\t\t{\n\t\t\tif (sh_config.shadernamefmt)\n\t\t\t{\n\t\t\t\tchar drivername[MAX_QPATH];\n\t\t\t\tQ_snprintfz(drivername, sizeof(drivername), sh_config.shadernamefmt, cleanname);\n\t\t\t\tif (Shader_ParseShader(&ps, drivername))\n\t\t\t\t\treturn s;\n\t\t\t}\n\t\t\tif (Shader_ParseShader(&ps, cleanname))\n\t\t\t\treturn s;\n\t\t\tif (strcmp(cleanname, shortname))\n\t\t\t\tif (Shader_ParseShader(&ps, shortname))\n\t\t\t\t\treturn s;\n\t\t}\n\n\t\t// make a default shader\n\n\t\tif (s->generator)\n\t\t{\n\t\t\tShader_Regenerate(&ps, shortname);\n\t\t\treturn s;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tShader_Free(s);\n\t\t}\n\t}\n\treturn NULL;\n}\n\n#ifdef _DEBUG\nstatic char *Shader_DecomposePass(char *o, shaderpass_t *p, qboolean simple)\n{\n\tif (!simple)\n\t{\n\t\tswitch(p->rgbgen)\n\t\t{\n\t\tdefault: sprintf(o, \"RGB_GEN_? \"); break;\n\t\tcase RGB_GEN_ENTITY: sprintf(o, \"RGB_GEN_ENTITY \"); break;\n\t\tcase RGB_GEN_ONE_MINUS_ENTITY: sprintf(o, \"RGB_GEN_ONE_MINUS_ENTITY \"); break;\n\t\tcase RGB_GEN_VERTEX_LIGHTING: sprintf(o, \"RGB_GEN_VERTEX_LIGHTING \"); break;\n\t\tcase RGB_GEN_VERTEX_EXACT: sprintf(o, \"RGB_GEN_VERTEX_EXACT \"); break;\n\t\tcase RGB_GEN_ONE_MINUS_VERTEX: sprintf(o, \"RGB_GEN_ONE_MINUS_VERTEX \"); break;\n\t\tcase RGB_GEN_IDENTITY_LIGHTING: sprintf(o, \"RGB_GEN_IDENTITY_LIGHTING \"); break;\n\t\tcase RGB_GEN_IDENTITY_OVERBRIGHT: sprintf(o, \"RGB_GEN_IDENTITY_OVERBRIGHT \"); break;\n\t\tcase RGB_GEN_IDENTITY: sprintf(o, \"RGB_GEN_IDENTITY \"); break;\n\t\tcase RGB_GEN_CONST: sprintf(o, \"RGB_GEN_CONST \"); break;\n\t\tcase RGB_GEN_ENTITY_LIGHTING_DIFFUSE: sprintf(o, \"RGB_GEN_ENTITY_LIGHTING_DIFFUSE \"); break;\n\t\tcase RGB_GEN_LIGHTING_DIFFUSE: sprintf(o, \"RGB_GEN_LIGHTING_DIFFUSE \"); break;\n\t\tcase RGB_GEN_WAVE: sprintf(o, \"RGB_GEN_WAVE \"); break;\n\t\tcase RGB_GEN_TOPCOLOR: sprintf(o, \"RGB_GEN_TOPCOLOR \"); break;\n\t\tcase RGB_GEN_BOTTOMCOLOR: sprintf(o, \"RGB_GEN_BOTTOMCOLOR \"); break;\n\t\tcase RGB_GEN_UNKNOWN: sprintf(o, \"RGB_GEN_UNKNOWN \"); break;\n\t\t}\n\t\to+=strlen(o);\n\t\tsprintf(o, \"\\n\"); o+=strlen(o);\n\n\t\tswitch(p->alphagen)\n\t\t{\n\t\tdefault: sprintf(o, \"ALPHA_GEN_? \"); break;\n\t\tcase ALPHA_GEN_ENTITY: sprintf(o, \"ALPHA_GEN_ENTITY \"); break;\n\t\tcase ALPHA_GEN_WAVE: sprintf(o, \"ALPHA_GEN_WAVE \"); break;\n\t\tcase ALPHA_GEN_PORTAL: sprintf(o, \"ALPHA_GEN_PORTAL \"); break;\n\t\tcase ALPHA_GEN_SPECULAR: sprintf(o, \"ALPHA_GEN_SPECULAR \"); break;\n\t\tcase ALPHA_GEN_IDENTITY: sprintf(o, \"ALPHA_GEN_IDENTITY \"); break;\n\t\tcase ALPHA_GEN_VERTEX: sprintf(o, \"ALPHA_GEN_VERTEX \"); break;\n\t\tcase ALPHA_GEN_CONST: sprintf(o, \"ALPHA_GEN_CONST \"); break;\n\t\t}\n\t\to+=strlen(o);\n\t\tsprintf(o, \"\\n\"); o+=strlen(o);\n\t}\n\n\tif (p->prog)\n\t{\n\t\tsprintf(o, \"program %s\\n\", p->prog->name);\n\t\to+=strlen(o);\n\t}\n\n\tif (p->shaderbits & SBITS_MISC_DEPTHWRITE)\t\t{\tsprintf(o, \"SBITS_MISC_DEPTHWRITE\\n\"); o+=strlen(o); }\n\tif (p->shaderbits & SBITS_MISC_NODEPTHTEST)\t\t{\tsprintf(o, \"SBITS_MISC_NODEPTHTEST\\n\"); o+=strlen(o); }\n\telse switch (p->shaderbits & SBITS_DEPTHFUNC_BITS)\n\t{\n\tcase SBITS_DEPTHFUNC_EQUAL:\t\t\tsprintf(o, \"depthfunc equal\\n\");\tbreak;\n\tcase SBITS_DEPTHFUNC_CLOSER:\t\tsprintf(o, \"depthfunc less\\n\");\t\tbreak;\n\tcase SBITS_DEPTHFUNC_CLOSEREQUAL:\tsprintf(o, \"depthfunc lequal\\n\");\tbreak;\n\tcase SBITS_DEPTHFUNC_FURTHER:\t\tsprintf(o, \"depthfunc greater\\n\");\tbreak;\n\t}\n\tif (p->shaderbits & SBITS_TESSELLATION)\t\t\t{\tsprintf(o, \"SBITS_TESSELLATION\\n\"); o+=strlen(o); }\n\tif (p->shaderbits & SBITS_AFFINE)\t\t\t\t{\tsprintf(o, \"SBITS_AFFINE\\n\"); o+=strlen(o); }\n\tif (p->shaderbits & SBITS_MASK_BITS)\t\t\t{\tsprintf(o, \"SBITS_MASK_BITS\\n\"); o+=strlen(o); }\n\n\tif (p->shaderbits & SBITS_BLEND_BITS)\n\t{\n\t\tsprintf(o, \"blendfunc\"); \n\t\to+=strlen(o);\n\t\tswitch(p->shaderbits & SBITS_SRCBLEND_BITS)\n\t\t{\n\t\tcase SBITS_SRCBLEND_NONE:\t\t\t\t\t\t\tsprintf(o, \" SBITS_SRCBLEND_NONE\"); break;\n\t\tcase SBITS_SRCBLEND_ZERO:\t\t\t\t\t\t\tsprintf(o, \" SBITS_SRCBLEND_ZERO\"); break;\n\t\tcase SBITS_SRCBLEND_ONE:\t\t\t\t\t\t\tsprintf(o, \" SBITS_SRCBLEND_ONE\"); break;\n\t\tcase SBITS_SRCBLEND_DST_COLOR:\t\t\t\t\t\tsprintf(o, \" SBITS_SRCBLEND_DST_COLOR\"); break;\n\t\tcase SBITS_SRCBLEND_ONE_MINUS_DST_COLOR:\t\t\tsprintf(o, \" SBITS_SRCBLEND_ONE_MINUS_DST_COLOR\"); break;\n\t\tcase SBITS_SRCBLEND_SRC_ALPHA:\t\t\t\t\t\tsprintf(o, \" SBITS_SRCBLEND_SRC_ALPHA\"); break;\n\t\tcase SBITS_SRCBLEND_ONE_MINUS_SRC_ALPHA:\t\t\tsprintf(o, \" SBITS_SRCBLEND_ONE_MINUS_SRC_ALPHA\"); break;\n\t\tcase SBITS_SRCBLEND_DST_ALPHA:\t\t\t\t\t\tsprintf(o, \" SBITS_SRCBLEND_DST_ALPHA\"); break;\n\t\tcase SBITS_SRCBLEND_ONE_MINUS_DST_ALPHA:\t\t\tsprintf(o, \" SBITS_SRCBLEND_ONE_MINUS_DST_ALPHA\"); break;\n\t\tcase SBITS_SRCBLEND_SRC_COLOR_INVALID:\t\t\t\tsprintf(o, \" SBITS_SRCBLEND_SRC_COLOR_INVALID\"); break;\n\t\tcase SBITS_SRCBLEND_ONE_MINUS_SRC_COLOR_INVALID:\tsprintf(o, \" SBITS_SRCBLEND_ONE_MINUS_SRC_COLOR_INVALID\"); break;\n\t\tcase SBITS_SRCBLEND_ALPHA_SATURATE:\t\t\t\t\tsprintf(o, \" SBITS_SRCBLEND_ALPHA_SATURATE\"); break;\n\t\tdefault:\t\t\t\t\t\t\t\t\t\t\tsprintf(o, \" SBITS_SRCBLEND_INVALID\"); break;\n\t\t}\n\t\to+=strlen(o);\n\t\tswitch(p->shaderbits & SBITS_DSTBLEND_BITS)\n\t\t{\n\t\tcase SBITS_DSTBLEND_NONE:\t\t\t\t\t\t\tsprintf(o, \" SBITS_DSTBLEND_NONE\"); break;\n\t\tcase SBITS_DSTBLEND_ZERO:\t\t\t\t\t\t\tsprintf(o, \" SBITS_DSTBLEND_ZERO\"); break;\n\t\tcase SBITS_DSTBLEND_ONE:\t\t\t\t\t\t\tsprintf(o, \" SBITS_DSTBLEND_ONE\"); break;\n\t\tcase SBITS_DSTBLEND_DST_COLOR_INVALID:\t\t\t\tsprintf(o, \" SBITS_DSTBLEND_DST_COLOR_INVALID\"); break;\n\t\tcase SBITS_DSTBLEND_ONE_MINUS_DST_COLOR_INVALID:\tsprintf(o, \" SBITS_DSTBLEND_ONE_MINUS_DST_COLOR_INVALID\"); break;\n\t\tcase SBITS_DSTBLEND_SRC_ALPHA:\t\t\t\t\t\tsprintf(o, \" SBITS_DSTBLEND_SRC_ALPHA\"); break;\n\t\tcase SBITS_DSTBLEND_ONE_MINUS_SRC_ALPHA:\t\t\tsprintf(o, \" SBITS_DSTBLEND_ONE_MINUS_SRC_ALPHA\"); break;\n\t\tcase SBITS_DSTBLEND_DST_ALPHA:\t\t\t\t\t\tsprintf(o, \" SBITS_DSTBLEND_DST_ALPHA\"); break;\n\t\tcase SBITS_DSTBLEND_ONE_MINUS_DST_ALPHA:\t\t\tsprintf(o, \" SBITS_DSTBLEND_ONE_MINUS_DST_ALPHA\"); break;\n\t\tcase SBITS_DSTBLEND_SRC_COLOR:\t\t\t\t\t\tsprintf(o, \" SBITS_DSTBLEND_SRC_COLOR\"); break;\n\t\tcase SBITS_DSTBLEND_ONE_MINUS_SRC_COLOR:\t\t\tsprintf(o, \" SBITS_DSTBLEND_ONE_MINUS_SRC_COLOR\"); break;\n\t\tcase SBITS_DSTBLEND_ALPHA_SATURATE_INVALID:\t\t\tsprintf(o, \" SBITS_DSTBLEND_ALPHA_SATURATE_INVALID\"); break;\n\t\tdefault:\t\t\t\t\t\t\t\t\t\t\tsprintf(o, \" SBITS_DSTBLEND_INVALID\"); break;\n\t\t}\n\t\to+=strlen(o);\n\n\t\tsprintf(o, \"\\n\"); \n\t\to+=strlen(o);\n\t}\n\n\n\tswitch(p->shaderbits & SBITS_ATEST_BITS)\n\t{\n\tcase SBITS_ATEST_NONE:\tbreak;\n\tcase SBITS_ATEST_GE128: sprintf(o, \"SBITS_ATEST_GE128\\n\"); break;\n\tcase SBITS_ATEST_LT128: sprintf(o, \"SBITS_ATEST_LT128\\n\"); break;\n\tcase SBITS_ATEST_GT0: sprintf(o, \"SBITS_ATEST_GT0\\n\"); break;\n\t}\n\to+=strlen(o);\n\n\treturn o;\n}\nstatic void Shader_DecomposeSubPassMap(char *o, shader_t *s, char *name, texid_t tex)\n{\n\tif (tex)\n\t{\n\t\tunsigned int flags = tex->flags;\n\t\tsprintf(o, \"%s \\\"%s\\\" %ix%i%s %s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\", name, tex->ident, tex->width, tex->height,\n\t\t\t(tex->status == TEX_FAILED)?\" FAILED\":\"\",\n\t\t\tImage_FormatName(tex->format),\n\t\t\t(flags&IF_CLAMP)?\" clamp\":\"\",\n\t\t\t(flags&IF_NOMIPMAP)?\" nomipmap\":\"\",\n\t\t\t(flags&IF_NEAREST)?\" nearest\":\"\",\n\t\t\t(flags&IF_LINEAR)?\" linear\":\"\",\n\t\t\t(flags&IF_UIPIC)?\" uipic\":\"\",\n\t\t\t(flags&IF_SRGB)?\" srgb\":\"\",\n\n\t\t\t(flags&IF_NOPICMIP)?\" nopicmip\":\"\",\n\t\t\t(flags&IF_NOALPHA)?\" noalpha\":\"\",\n\t\t\t(flags&IF_NOGAMMA)?\" noalpha\":\"\",\n\t\t\t(flags&IF_TEXTYPEMASK)?\" non-2d\":\"\",\n\t\t\t(flags&IF_MIPCAP)?\"\":\" nomipcap\",\n\t\t\t(flags&IF_PREMULTIPLYALPHA)?\" premultiply\":\"\",\n\n\t\t\t(flags&IF_NOSRGB)?\" nosrgb\":\"\",\n\n\t\t\t(flags&IF_PALETTIZE)?\" palettize\":\"\",\n\t\t\t(flags&IF_NOPURGE)?\" nopurge\":\"\",\n\t\t\t(flags&IF_HIGHPRIORITY)?\" highpri\":\"\",\n\t\t\t(flags&IF_LOWPRIORITY)?\" lowpri\":\"\",\n\t\t\t(flags&IF_LOADNOW)?\" loadnow\":\"\",\n\t\t\t(flags&IF_TRYBUMP)?\" trybump\":\"\",\n\t\t\t(flags&IF_RENDERTARGET)?\" rendertarget\":\"\",\n\t\t\t(flags&IF_EXACTEXTENSION)?\" exactext\":\"\",\n\t\t\t(flags&IF_NOREPLACE)?\" noreplace\":\"\",\n\t\t\t(flags&IF_NOWORKER)?\" noworker\":\"\"\n\t\t\t);\n\t}\n\telse\n\t\tsprintf(o, \"%s (%s)\", name, \"UNDEFINED\");\n}\nstatic char *Shader_DecomposeSubPass(char *o, shader_t *s, shaderpass_t *p, qboolean simple)\n{\n\tint i;\n\tif (!simple)\n\t{\n\t\tswitch(p->tcgen)\n\t\t{\n\t\tdefault: sprintf(o, \"TC_GEN_? \"); break;\n\t\tcase TC_GEN_BASE: sprintf(o, \"TC_GEN_BASE \"); break;\n\t\tcase TC_GEN_LIGHTMAP: sprintf(o, \"TC_GEN_LIGHTMAP \"); break;\n\t\tcase TC_GEN_ENVIRONMENT: sprintf(o, \"TC_GEN_ENVIRONMENT \"); break;\n\t\tcase TC_GEN_DOTPRODUCT: sprintf(o, \"TC_GEN_DOTPRODUCT \"); break;\n\t\tcase TC_GEN_VECTOR: sprintf(o, \"TC_GEN_VECTOR \"); break;\n\t\tcase TC_GEN_NORMAL: sprintf(o, \"TC_GEN_NORMAL \"); break;\n\t\tcase TC_GEN_SVECTOR: sprintf(o, \"TC_GEN_SVECTOR \"); break;\n\t\tcase TC_GEN_TVECTOR: sprintf(o, \"TC_GEN_TVECTOR \"); break;\n\t\tcase TC_GEN_SKYBOX: sprintf(o, \"TC_GEN_SKYBOX \"); break;\n\t\tcase TC_GEN_WOBBLESKY: sprintf(o, \"TC_GEN_WOBBLESKY \"); break;\n\t\tcase TC_GEN_REFLECT: sprintf(o, \"TC_GEN_REFLECT \"); break;\n\t\tcase TC_GEN_UNSPECIFIED: sprintf(o, \"TC_GEN_UNSPECIFIED \"); break;\n\t\t}\n\t\to+=strlen(o);\n\t\tsprintf(o, \"\\n\"); o+=strlen(o);\n\n\t\tfor (i = 0; i < p->numtcmods; i++)\n\t\t{\n\t\t\tswitch(p->tcmods[i].type)\n\t\t\t{\n\t\t\t\tdefault: sprintf(o, \"TCMOD_GEN_? \"); break;\n\t\t\t\tcase SHADER_TCMOD_NONE: sprintf(o, \"SHADER_TCMOD_NONE \"); break;\n\t\t\t\tcase SHADER_TCMOD_SCALE: sprintf(o, \"SHADER_TCMOD_SCALE \"); break;\n\t\t\t\tcase SHADER_TCMOD_SCROLL: sprintf(o, \"SHADER_TCMOD_SCROLL \"); break;\n\t\t\t\tcase SHADER_TCMOD_STRETCH: sprintf(o, \"SHADER_TCMOD_STRETCH \"); break;\n\t\t\t\tcase SHADER_TCMOD_ROTATE: sprintf(o, \"SHADER_TCMOD_ROTATE \"); break;\n\t\t\t\tcase SHADER_TCMOD_TRANSFORM: sprintf(o, \"SHADER_TCMOD_TRANSFORM \"); break;\n\t\t\t\tcase SHADER_TCMOD_TURB: sprintf(o, \"SHADER_TCMOD_TURB \"); break;\n\t\t\t\tcase SHADER_TCMOD_PAGE: sprintf(o, \"SHADER_TCMOD_PAGE \"); break;\n\t\t\t}\n\t\t\to+=strlen(o);\n\t\t\tsprintf(o, \"\\n\"); o+=strlen(o);\n\t\t}\n\n\n\t\tswitch(p->blendmode)\n\t\t{\n\t\tdefault: sprintf(o, \"PBM_? \"); break;\n\t\tcase PBM_MODULATE: sprintf(o, \"PBM_MODULATE \"); break;\n\t\tcase PBM_OVERBRIGHT: sprintf(o, \"PBM_OVERBRIGHT \"); break;\n\t\tcase PBM_DECAL: sprintf(o, \"PBM_DECAL \"); break;\n\t\tcase PBM_ADD: sprintf(o, \"PBM_ADD \"); break;\n\t\tcase PBM_DOTPRODUCT: sprintf(o, \"PBM_DOTPRODUCT \"); break;\n\t\tcase PBM_REPLACE: sprintf(o, \"PBM_REPLACE \"); break;\n\t\tcase PBM_REPLACELIGHT: sprintf(o, \"PBM_REPLACELIGHT \"); break;\n\t\tcase PBM_MODULATE_PREV_COLOUR: sprintf(o, \"PBM_MODULATE_PREV_COLOUR \"); break;\n\t\t}\n\t\to+=strlen(o);\n\t}\n\n\tswitch(p->texgen)\n\t{\n\tdefault: sprintf(o, \"T_GEN_? \"); break;\n\tcase T_GEN_SINGLEMAP:\t\tShader_DecomposeSubPassMap(o, s, \"map\", p->anim_frames[0]);\tbreak;\n\tcase T_GEN_ANIMMAP:\t\t\tShader_DecomposeSubPassMap(o, s, \"animmap\", p->anim_frames[0]);\tbreak;\n#ifdef HAVE_MEDIA_DECODER\n\tcase T_GEN_VIDEOMAP:\t\tShader_DecomposeSubPassMap(o, s, \"videomap\", Media_UpdateForShader(p->cin)); break;\n#endif\n\tcase T_GEN_LIGHTMAP:\t\tsprintf(o, \"map $lightmap \"); break;\n\tcase T_GEN_DELUXMAP:\t\tsprintf(o, \"map $deluxemap \"); break;\n\tcase T_GEN_SHADOWMAP:\t\tsprintf(o, \"map $shadowmap \"); break;\n\tcase T_GEN_LIGHTCUBEMAP: \tsprintf(o, \"map $lightcubemap \"); break;\n\tcase T_GEN_DIFFUSE:\t\t\tShader_DecomposeSubPassMap(o, s, \"map $diffuse\", s->defaulttextures[0].base); break;\n\tcase T_GEN_NORMALMAP:\t\tShader_DecomposeSubPassMap(o, s, \"map $normalmap\", s->defaulttextures[0].bump); break;\n\tcase T_GEN_SPECULAR:\t\tShader_DecomposeSubPassMap(o, s, \"map $specular\", s->defaulttextures[0].specular); break;\n\tcase T_GEN_UPPEROVERLAY:\tShader_DecomposeSubPassMap(o, s, \"map $upper\", s->defaulttextures[0].upperoverlay); break;\n\tcase T_GEN_LOWEROVERLAY:\tShader_DecomposeSubPassMap(o, s, \"map $lower\", s->defaulttextures[0].loweroverlay); break;\n\tcase T_GEN_FULLBRIGHT:\t\tShader_DecomposeSubPassMap(o, s, \"map $fullbright\", s->defaulttextures[0].fullbright); break;\n\tcase T_GEN_PALETTED:\t\tShader_DecomposeSubPassMap(o, s, \"map $paletted\", s->defaulttextures[0].paletted); break;\n\tcase T_GEN_REFLECTCUBE:\t\tShader_DecomposeSubPassMap(o, s, \"map $reflectcube\", s->defaulttextures[0].reflectcube); break;\n\tcase T_GEN_REFLECTMASK:\t\tShader_DecomposeSubPassMap(o, s, \"map $reflectmask\", s->defaulttextures[0].reflectmask); break;\n\tcase T_GEN_DISPLACEMENT:\tShader_DecomposeSubPassMap(o, s, \"map $displacement\", s->defaulttextures[0].displacement); break;\n\tcase T_GEN_OCCLUSION:\t\tShader_DecomposeSubPassMap(o, s, \"map $occlusion\", s->defaulttextures[0].occlusion); break;\n\tcase T_GEN_TRANSMISSION:\tShader_DecomposeSubPassMap(o, s, \"map $transmission\", s->defaulttextures[0].transmission); break;\n\tcase T_GEN_THICKNESS:\t\tShader_DecomposeSubPassMap(o, s, \"map $thickness\", s->defaulttextures[0].thickness); break;\n\tcase T_GEN_CURRENTRENDER:\tsprintf(o, \"map $currentrender \"); break;\n\tcase T_GEN_SOURCECOLOUR:\tsprintf(o, \"map $sourcecolour\"); break;\n\tcase T_GEN_SOURCEDEPTH:\t\tsprintf(o, \"map $sourcedepth\"); break;\n\tcase T_GEN_REFLECTION:\t\tsprintf(o, \"map $reflection\"); break;\n\tcase T_GEN_REFRACTION:\t\tsprintf(o, \"map $refraction\"); break;\n\tcase T_GEN_REFRACTIONDEPTH:\tsprintf(o, \"map $refractiondepth\"); break;\n\tcase T_GEN_RIPPLEMAP:\t\tsprintf(o, \"map $ripplemap\"); break;\n\tcase T_GEN_SOURCECUBE:\t\tsprintf(o, \"map $sourcecube\"); break;\n\tcase T_GEN_GBUFFERCASE:\t\tsprintf(o, \"map $gbuffer%i \",p->texgen-T_GEN_GBUFFER0); break;\n\t}\n\to+=strlen(o);\n\n\tsprintf(o, \"\\n\"); o+=strlen(o);\n\treturn o;\n}\nchar *Shader_Decompose(shader_t *s)\n{\n\tstatic char decomposebuf[32768];\n\tchar *o = decomposebuf;\n\tshaderpass_t *p;\n\tunsigned int i, j;\n\n\tsprintf(o, \"\\n<---\\n\"); o+=strlen(o);\n\n\tswitch (s->sort)\n\t{\n\tdefault:\t\t\t\t\t\tsprintf(o, \"sort %i\\n\", s->sort); break;\n\tcase SHADER_SORT_NONE:\t\t\tsprintf(o, \"sort %i (SHADER_SORT_NONE)\\n\", s->sort); break;\n\tcase SHADER_SORT_RIPPLE:\t\tsprintf(o, \"sort %i (SHADER_SORT_RIPPLE)\\n\", s->sort); break;\n\tcase SHADER_SORT_DEFERREDLIGHT:\tsprintf(o, \"sort %i (SHADER_SORT_DEFERREDLIGHT)\\n\", s->sort); break;\n\tcase SHADER_SORT_PORTAL:\t\tsprintf(o, \"sort %i (SHADER_SORT_PORTAL)\\n\", s->sort); break;\n\tcase SHADER_SORT_SKY:\t\t\tsprintf(o, \"sort %i (SHADER_SORT_SKY)\\n\", s->sort); break;\n\tcase SHADER_SORT_OPAQUE:\t\tsprintf(o, \"sort %i (SHADER_SORT_OPAQUE)\\n\", s->sort); break;\n\tcase SHADER_SORT_DECAL:\t\t\tsprintf(o, \"sort %i (SHADER_SORT_DECAL)\\n\", s->sort); break;\n\tcase SHADER_SORT_SEETHROUGH:\tsprintf(o, \"sort %i (SHADER_SORT_SEETHROUGH)\\n\", s->sort); break;\n\tcase SHADER_SORT_BANNER:\t\tsprintf(o, \"sort %i (SHADER_SORT_BANNER)\\n\", s->sort); break;\n\tcase SHADER_SORT_UNDERWATER:\tsprintf(o, \"sort %i (SHADER_SORT_UNDERWATER)\\n\", s->sort); break;\n\tcase SHADER_SORT_BLEND:\t\t\tsprintf(o, \"sort %i (SHADER_SORT_BLEND)\\n\", s->sort); break;\n\tcase SHADER_SORT_ADDITIVE:\t\tsprintf(o, \"sort %i (SHADER_SORT_ADDITIVE)\\n\", s->sort); break;\n\tcase SHADER_SORT_NEAREST:\t\tsprintf(o, \"sort %i (SHADER_SORT_NEAREST)\\n\", s->sort); break;\n\t}\n\to+=strlen(o);\n\n\tif (s->prog)\n\t{\n\t\tsprintf(o, \"program %s\\n\", s->prog->name);\n\t\to+=strlen(o);\n\n\t\tp = s->passes;\n\t\to = Shader_DecomposePass(o, p, true);\n\t\tfor (j = 0; j < s->numpasses; j++)\n\t\t\to = Shader_DecomposeSubPass(o, s, p+j, true);\n\t}\n\telse\n\t{\n\t\tfor (i = 0; i < s->numpasses; i+= p->numMergedPasses)\n\t\t{\n\t\t\tp = &s->passes[i];\n\t\t\tsprintf(o, \"{\\n\"); o+=strlen(o);\n\n\t\t\to = Shader_DecomposePass(o, p, false);\n\t\t\tfor (j = 0; j < p->numMergedPasses; j++)\n\t\t\t\to = Shader_DecomposeSubPass(o, s, p+j, !!p->prog);\n\t\t\tsprintf(o, \"}\\n\"); o+=strlen(o);\n\t\t}\n\t}\n\tsprintf(o, \"--->\\n\"); o+=strlen(o);\n\treturn decomposebuf;\n}\n#endif\n\nchar *Shader_GetShaderBody(shader_t *s, char *fname, size_t fnamesize)\n{\n\tparsestate_t ps;\n\tchar *adr, *parsename=NULL, *argsstart;\n\tchar cleanname[MAX_QPATH];\n\tchar shortname[MAX_QPATH];\n\tchar drivername[MAX_QPATH];\n\tint oldsort;\n\tqboolean resort = false;\n\n\tif (!s || !s->uses)\n\t\treturn NULL;\n\tps.s = s;\n\n\tadr = Z_StrDup(\"UNKNOWN BODY\");\n\tps.saveshaderbody = &adr;\n\n\tstrcpy(cleanname, s->name);\n\targsstart = strchr(cleanname, '#');\n\tif (argsstart)\n\t\t*argsstart = 0;\n\tCOM_StripExtension (cleanname, shortname, sizeof(shortname));\n\tif (ruleset_allow_shaders.ival && !(s->usageflags & SUR_FORCEFALLBACK))\n\t{\n\t\tif (sh_config.shadernamefmt)\n\t\t{\n\t\t\tQ_snprintfz(drivername, sizeof(drivername), sh_config.shadernamefmt, cleanname);\n\t\t\tif (!parsename && Shader_ParseShader(&ps, drivername))\n\t\t\t\tparsename = drivername;\n\t\t}\n\t\tif (!parsename && Shader_ParseShader(&ps, cleanname))\n\t\t\tparsename = cleanname;\n\t\tif (!parsename && Shader_ParseShader(&ps, shortname))\n\t\t\tparsename = shortname;\n\t}\n\tif (!parsename && s->generator)\n\t{\n\t\toldsort = s->sort;\n\t\tShader_Regenerate(&ps, shortname);\n\n\t\tif (s->sort != oldsort)\n\t\t\tresort = true;\n\t}\n\n\tif (resort)\n\t{\n\t\tMod_ResortShaders();\n\t}\n\n\tif (fnamesize)\n\t{\n\t\t*fname = 0;\n\n\t\tif (parsename)\n\t\t{\n\t\t\tunsigned int key;\n\t\t\tshadercache_t *cache;\n\t\t\tkey = Hash_Key (parsename, HASH_SIZE);\n\t\t\tcache = shader_hash[key];\n\t\t\tfor ( ; cache; cache = cache->hash_next)\n\t\t\t{\n\t\t\t\tif (!Q_stricmp (cache->name, parsename))\n\t\t\t\t{\n\t\t\t\t\tchar *c, *stop;\n\t\t\t\t\tint line = 1;\n\t\t\t\t\t//okay, this is the shader we're looking for, we know where it came from too, so there's handy.\n\t\t\t\t\t//figure out the line index now, by just counting the \\ns up to the offset\n\t\t\t\t\tfor (c = cache->source->data, stop = c+cache->offset; c < stop; c++)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (*c == '\\n')\n\t\t\t\t\t\t\tline++;\n\t\t\t\t\t}\n\t\t\t\t\tQ_snprintfz(fname, fnamesize, \"%s:%i\", cache->source->name, line);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (!strchr(parsename, ':'))\n\t\t\t{\n\t\t\t\t//if the named shader is a .shader file then just directly load it.\n\t\t\t\tconst char *token = COM_GetFileExtension(parsename, NULL);\n\t\t\t\tif (!strcmp(token, \".shader\") || !*token)\n\t\t\t\t{\n\t\t\t\t\tchar shaderfile[MAX_QPATH];\n\t\t\t\t\tif (!*token)\n\t\t\t\t\t{\n\t\t\t\t\t\tQ_snprintfz(shaderfile, sizeof(shaderfile), \"%s.shader\", parsename);\n\t\t\t\t\t\tif (COM_FCheckExists(shaderfile))\n\t\t\t\t\t\t\tQ_snprintfz(fname, fnamesize, \"%s:%i\", shaderfile, 1);\n\t\t\t\t\t}\n\t\t\t\t\telse if (COM_FCheckExists(parsename))\n\t\t\t\t\t\tQ_snprintfz(fname, fnamesize, \"%s:%i\", parsename, 1);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n#ifdef _DEBUG\n\t{\n\t\tchar *add, *ret;\n\t\tadd = Shader_Decompose(s);\n\t\tif (*add)\n\t\t{\n\t\t\tret = Z_Malloc(strlen(add) + strlen(adr) + 1);\n\t\t\tstrcpy(ret, adr);\n\t\t\tstrcpy(ret + strlen(ret), add);\n\t\t\tZ_Free(adr);\n\t\t\tadr = ret;\n\t\t}\n\t}\n#endif\n\n\treturn adr;\n}\n\nvoid Shader_ShowShader_f(void)\n{\n\tchar *sourcename = Cmd_Argv(1);\n\tshader_t *o = R_LoadShader(NULL, sourcename, SUF_NONE, NULL, NULL);\n\tif (!o)\n\t\to = R_LoadShader(NULL, sourcename, SUF_LIGHTMAP, NULL, NULL);\n\tif (!o)\n\t\to = R_LoadShader(NULL, sourcename, SUF_2D, NULL, NULL);\n\tif (o)\n\t{\n\t\tchar fname[256];\n\t\tchar *body = Shader_GetShaderBody(o, fname, sizeof(fname));\n\t\tif (body)\n\t\t{\n\t\t\tCon_Printf(\"^h(%s)^h\\n%s\\n{%s\\n\", fname, o->name, body);\n\t\t\tZ_Free(body);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tCon_Printf(\"Shader \\\"%s\\\" is not in use\\n\", o->name);\n\t\t}\n\t}\n\telse\n\t\tCon_Printf(\"Shader \\\"%s\\\" is not loaded\\n\", sourcename);\n}\n\nvoid Shader_ShaderList_f(void)\n{\n\tunsigned int i;\n\t// not loaded, find a free slot\n\tfor (i = 0; i < r_numshaders; i++)\n\t{\n\t\tif (!r_shaders[i])\n\t\t\tcontinue;\t//gap?\n\t\tCon_Printf(\"^[\\\\img\\\\%s\\\\imgtype\\\\%i\\\\s\\\\64^] ^2%s^7 [%i]\", r_shaders[i]->name, r_shaders[i]->usageflags, r_shaders[i]->name, r_shaders[i]->usageflags);\n\t\tif (r_shaders[i]->width || r_shaders[i]->height)\n\t\t\tCon_Printf(\" Size:%ix%i\", r_shaders[i]->width, r_shaders[i]->height);\n\t\tif (r_shaders[i]->model)\n\t\t\tCon_Printf(\" ^[%s\\\\modelviewer\\\\%s^]\", r_shaders[i]->model->name, r_shaders[i]->model->name);\n\t\tCon_Printf(\"\\n\");\n\t}\n}\n\nvoid Shader_TouchTexnums(texnums_t *t)\n{\n\tif (t->base)\n\t\tt->base->regsequence = r_regsequence;\n\tif (t->bump)\n\t\tt->bump->regsequence = r_regsequence;\n\tif (t->specular)\n\t\tt->specular->regsequence = r_regsequence;\n\tif (t->upperoverlay)\n\t\tt->upperoverlay->regsequence = r_regsequence;\n\tif (t->loweroverlay)\n\t\tt->loweroverlay->regsequence = r_regsequence;\n\tif (t->paletted)\n\t\tt->paletted->regsequence = r_regsequence;\n\tif (t->fullbright)\n\t\tt->fullbright->regsequence = r_regsequence;\n\tif (t->reflectcube)\n\t\tt->reflectcube->regsequence = r_regsequence;\n\tif (t->reflectmask)\n\t\tt->reflectmask->regsequence = r_regsequence;\n\tif (t->displacement)\n\t\tt->displacement->regsequence = r_regsequence;\n\tif (t->occlusion)\n\t\tt->occlusion->regsequence = r_regsequence;\n}\nvoid Shader_TouchTextures(void)\n{\n\tint i, j, k;\n\tshader_t *s;\n\tshaderpass_t *p;\n\tfor (i = 0; i < r_numshaders; i++)\n\t{\n\t\ts = r_shaders[i];\n\t\tif (!s || !s->uses)\n\t\t\tcontinue;\n\n\t\tfor (j = 0; j < s->numpasses; j++)\n\t\t{\n\t\t\tp = &s->passes[j];\n\t\t\tfor (k = 0; k < countof(p->anim_frames); k++)\n\t\t\t\tif (p->anim_frames[k])\n\t\t\t\t\tp->anim_frames[k]->regsequence = r_regsequence;\n\t\t}\n\t\tfor (j = 0; j < max(1,s->numdefaulttextures); j++)\n\t\t\tShader_TouchTexnums(&s->defaulttextures[j]);\n\t}\n}\n\nvoid Shader_DoReload(void)\n{\n\tshader_t *s;\n\tunsigned int i;\n\tchar shortname[MAX_QPATH];\n\tchar cleanname[MAX_QPATH], *argsstart;\n\tint oldsort;\n\tqboolean resort = false;\n\tparsestate_t ps;\n\n\t//don't spam shader reloads while we're connecting, as that's just wasteful.\n\tif (cls.state && cls.state < ca_active)\n\t\treturn;\n\tif (!r_shaders)\n\t\treturn;\t//err, not ready yet\n\n\tif (shader_rescan_needed)\n\t{\n\t\tShader_FlushCache();\n\n\t\tif (ruleset_allow_shaders.ival)\n\t\t{\n\t\t\tCOM_EnumerateFiles(\"materials/*.mtr\", Shader_InitCallback_Doom3, NULL);\n\t\t\tCOM_EnumerateFiles(\"shaders/*.shader\", Shader_InitCallback, NULL);\n\t\t\tCOM_EnumerateFiles(\"scripts/*.shader\", Shader_InitCallback, NULL);\n\t\t\tCOM_EnumerateFiles(\"scripts/*.rscript\", Shader_InitCallback, NULL);\n\t\t}\n\n\t\tshader_reload_needed = true;\n\t\tshader_rescan_needed = false;\n\n\t\tCon_DPrintf(\"Rescanning shaders\\n\");\n\t}\n\telse\n\t{\n\t\tif (!shader_reload_needed)\n\t\t\treturn;\n\t\tCon_DPrintf(\"Reloading shaders\\n\");\n\t}\n\tshader_reload_needed = false;\n\tR2D_ImageColours(1,1,1,1);\n\tTRACE((\"Reloading generics\\n\"));\n\tShader_ReloadGenerics();\n\n\tfor (i = 0; i < r_numshaders; i++)\n\t{\n\t\ts = r_shaders[i];\n\t\tif (!s || !s->uses)\n\t\t\tcontinue;\n\t\tps.s = s;\n\t\tps.saveshaderbody = NULL;\n\n\t\tstrcpy(cleanname, s->name);\n\t\targsstart = *cleanname?strchr(cleanname+1, '#'):NULL;\n\t\tif (argsstart)\n\t\t\t*argsstart = 0;\n\t\tCOM_StripExtension (cleanname, shortname, sizeof(shortname));\n\t\tTRACE((\"reparsing %s\\n\", s->name));\n\t\tif (ruleset_allow_shaders.ival && !(s->usageflags & SUR_FORCEFALLBACK))\n\t\t{\n\t\t\tif (sh_config.shadernamefmt)\n\t\t\t{\n\t\t\t\tchar drivername[MAX_QPATH];\n\t\t\t\tQ_snprintfz(drivername, sizeof(drivername), sh_config.shadernamefmt, cleanname);\n\t\t\t\tif (Shader_ParseShader(&ps, drivername))\n\t\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (Shader_ParseShader(&ps, cleanname))\n\t\t\t\tcontinue;\n\t\t\tif (strcmp(cleanname, shortname))\n\t\t\t\tif (Shader_ParseShader(&ps, shortname))\n\t\t\t\t\tcontinue;\n\t\t}\n\t\tif (s->generator)\n\t\t{\n\t\t\toldsort = s->sort;\n\t\t\tShader_Regenerate(&ps, shortname);\n\t\t\tif (s->sort != oldsort)\n\t\t\t\tresort = true;\n\t\t}\n\t}\n\n\tTRACE((\"Resorting shaders\\n\"));\n\n\tif (resort)\n\t{\n\t\tMod_ResortShaders();\n\t}\n}\n\nvoid Shader_NeedReload(qboolean rescanfs)\n{\n\tif (rescanfs)\n\t\tshader_rescan_needed = true;\n\tshader_reload_needed = true;\n}\n\ncin_t *QDECL R_ShaderGetCinematic(shader_t *s)\n{\n#ifdef HAVE_MEDIA_DECODER\n\tint j;\n\tif (!s)\n\t\treturn NULL;\n\tfor (j = 0; j < s->numpasses; j++)\n\t\tif (s->passes[j].cin)\n\t\t\treturn s->passes[j].cin;\n#endif\n\t/*no cinematic in this shader!*/\n\treturn NULL;\n}\n\nshader_t *R_ShaderFind(const char *name)\n{\n\tint i;\n\tchar shortname[MAX_QPATH];\n\tshader_t *s;\n\n\tif (!r_shaders)\n\t\treturn NULL;\n\n\tCOM_StripExtension ( name, shortname, sizeof(shortname));\n\n\tCOM_CleanUpPath(shortname);\n\n\t//try and find it\n\tfor (i = 0; i < r_numshaders; i++)\n\t{\n\t\ts = r_shaders[i];\n\t\tif (!s || !s->uses)\n\t\t\tcontinue;\n\n\t\tif (!Q_stricmp (shortname, s->name) )\n\t\t\treturn s;\n\t}\n\treturn NULL;\n}\n\ncin_t *R_ShaderFindCinematic(const char *name)\n{\n#ifdef HAVE_MEDIA_DECODER\n\treturn R_ShaderGetCinematic(R_ShaderFind(name));\n#else\n\treturn NULL;\n#endif\n}\n\nvoid Shader_ResetRemaps(void)\n{\n\tshader_t *s;\n\tint i;\n\tfor (i = 0; i < r_numshaders; i++)\n\t{\n\t\ts = r_shaders[i];\n\t\tif (!s)\n\t\t\tcontinue;\n\t\ts->remapto = s;\n\t\ts->remaptime = 0;\n\t}\n}\n\nvoid R_RemapShader(const char *sourcename, const char *destname, float timeoffset)\n{\n\tshader_t *o;\n\tshader_t *n;\n\tint i;\n\tsize_t l;\n\n\tchar cleansrcname[MAX_QPATH];\n\tQ_strncpyz(cleansrcname, sourcename, sizeof(cleansrcname));\n\tCOM_CleanUpPath(cleansrcname);\n\tl = strlen(cleansrcname);\n\n\tfor (i = 0; i < r_numshaders; i++)\n\t{\n\t\to = r_shaders[i];\n\t\tif (o && o->uses)\n\t\t{\n\t\t\tif (!strncmp(o->name, cleansrcname, l) && (!o->name[l] || o->name[l]=='#'))\n\t\t\t{\n\t\t\t\tn = R_LoadShader (o->model, va(\"%s%s\", destname, o->name+l), o->usageflags, NULL, NULL);\n\t\t\t\tif (!n)\n\t\t\t\t{\t//if it isn't actually available on disk then don't care about usageflags, just find ANY that's already loaded.\n\t\t\t\t\t// check the hash first\n\t\t\t\t\tchar cleandstname[MAX_QPATH];\n\t\t\t\t\tQ_strncpyz(cleandstname, destname, sizeof(cleandstname));\n\t\t\t\t\tCOM_CleanUpPath(cleandstname);\n\t\t\t\t\tn = Hash_Get(&shader_active_hash, cleandstname);\n\n\t\t\t\t\t// if one of our shaders is made for lightmaps, check through the rest until we find one more suitable\n\t\t\t\t\tif ((n->usageflags ^ o->usageflags) & SUF_LIGHTMAP)\n\t\t\t\t\t{\n\t\t\t\t\t\tshader_t *n_f = n;\n\t\t\t\t\t\twhile (n)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (!((n->usageflags ^ o->usageflags) & SUF_LIGHTMAP))\n\t\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\t\tn = Hash_GetNext(&shader_active_hash, cleandstname, n);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (!n)\n\t\t\t\t\t\t\tn = n_f;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (!n || !n->uses)\n\t\t\t\t\t\tn = o;\n\t\t\t\t}\n\t\t\t\to->remapto = n;\n\t\t\t\to->remaptime = timeoffset;\t//this just feels wrong.\n\t\t\t}\n\t\t}\n\t}\n}\n\nvoid Shader_RemapShader_f(void)\n{\n\tchar *sourcename = Cmd_Argv(1);\n\tchar *destname = Cmd_Argv(2);\n\tfloat timeoffset = atof(Cmd_Argv(3));\n\t\n\tif (!Cmd_FromGamecode() && strcmp(InfoBuf_ValueForKey(&cl.serverinfo, \"*cheats\"), \"ON\"))\n\t{\n\t\tCon_Printf(\"%s may only be used from gamecode, or when cheats are enabled\\n\", Cmd_Argv(0));\n\t\treturn;\n\t}\n\tif (!*sourcename)\n\t{\n\t\tCon_Printf(\"%s originalshader remappedshader starttime\\n\", Cmd_Argv(0));\n\t\treturn;\n\t}\n\tR_RemapShader(sourcename, destname, timeoffset);\n}\n\n//blocks\nint R_GetShaderSizes(shader_t *shader, int *width, int *height, qboolean blocktillloaded)\n{\n\tif (!shader)\n\t\treturn false;\n\tif (!shader->width && !shader->height)\n\t{\n\t\tint i;\n\t\tif (width)\n\t\t\t*width = 0;\n\t\tif (height)\n\t\t\t*height = 0;\n\t\tif ((shader->flags & SHADER_HASDIFFUSE) && shader->defaulttextures->base)\n\t\t{\n\t\t\tif (shader->defaulttextures->base->status == TEX_LOADING)\n\t\t\t{\n\t\t\t\tif (!blocktillloaded)\n\t\t\t\t\treturn -1;\n\t\t\t\tCOM_WorkerPartialSync(shader->defaulttextures->base, &shader->defaulttextures->base->status, TEX_LOADING);\n\t\t\t}\n\t\t\tif (shader->defaulttextures->base->status == TEX_LOADED)\n\t\t\t{\n\t\t\t\tshader->width = shader->defaulttextures->base->width;\n\t\t\t\tshader->height = shader->defaulttextures->base->height;\n\t\t\t}\n\t\t}\n\t\telse if ((shader->flags & SHADER_HASPALETTED) && shader->defaulttextures->paletted)\n\t\t{\n\t\t\tif (shader->defaulttextures->paletted->status == TEX_LOADING)\n\t\t\t{\n\t\t\t\tif (!blocktillloaded)\n\t\t\t\t\treturn -1;\n\t\t\t\tCOM_WorkerPartialSync(shader->defaulttextures->paletted, &shader->defaulttextures->paletted->status, TEX_LOADING);\n\t\t\t}\n\t\t\tif (shader->defaulttextures->paletted->status == TEX_LOADED)\n\t\t\t{\n\t\t\t\tshader->width = shader->defaulttextures->paletted->width;\n\t\t\t\tshader->height = shader->defaulttextures->paletted->height;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tfor (i = 0; i < shader->numpasses; i++)\n\t\t\t{\n\t\t\t\tif (shader->passes[i].texgen == T_GEN_SINGLEMAP && shader->passes[i].anim_frames[0] && shader->passes[i].anim_frames[0]->status == TEX_LOADING)\n\t\t\t\t{\n\t\t\t\t\tif (!blocktillloaded)\n\t\t\t\t\t\treturn -1;\n\t\t\t\t\tCOM_WorkerPartialSync(shader->passes[i].anim_frames[0], &shader->passes[i].anim_frames[0]->status, TEX_LOADING);\n\t\t\t\t}\n\t\t\t\tif (shader->passes[i].texgen == T_GEN_DIFFUSE && (shader->defaulttextures->base && shader->defaulttextures->base->status == TEX_LOADING))\n\t\t\t\t{\n\t\t\t\t\tif (!blocktillloaded)\n\t\t\t\t\t\treturn -1;\n\t\t\t\t\tCOM_WorkerPartialSync(shader->defaulttextures->base, &shader->defaulttextures->base->status, TEX_LOADING);\n\t\t\t\t}\n\t\t\t\tif (shader->passes[i].texgen == T_GEN_PALETTED && (shader->defaulttextures->paletted && shader->defaulttextures->paletted->status == TEX_LOADING))\n\t\t\t\t{\n\t\t\t\t\tif (!blocktillloaded)\n\t\t\t\t\t\treturn -1;\n\t\t\t\t\tCOM_WorkerPartialSync(shader->defaulttextures->paletted, &shader->defaulttextures->paletted->status, TEX_LOADING);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tfor (i = 0; i < shader->numpasses; i++)\n\t\t\t{\n\t\t\t\tif (shader->passes[i].texgen == T_GEN_SINGLEMAP)\n\t\t\t\t{\n\t\t\t\t\tif (shader->passes[i].anim_frames[0] && shader->passes[i].anim_frames[0]->status == TEX_LOADED)\n\t\t\t\t\t{\n\t\t\t\t\t\tshader->width = shader->passes[i].anim_frames[0]->width;\n\t\t\t\t\t\tshader->height = shader->passes[i].anim_frames[0]->height;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tif (shader->passes[i].texgen == T_GEN_DIFFUSE)\n\t\t\t\t{\n\t\t\t\t\tif (shader->defaulttextures->base && shader->defaulttextures->base->status == TEX_LOADED)\n\t\t\t\t\t{\n\t\t\t\t\t\tshader->width = shader->defaulttextures->base->width;\n\t\t\t\t\t\tshader->height = shader->defaulttextures->base->height;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tif (shader->passes[i].texgen == T_GEN_PALETTED)\n\t\t\t\t{\n\t\t\t\t\tif (shader->defaulttextures->paletted && shader->defaulttextures->paletted->status == TEX_LOADED)\n\t\t\t\t\t{\n\t\t\t\t\t\tshader->width = shader->defaulttextures->paletted->width;\n\t\t\t\t\t\tshader->height = shader->defaulttextures->paletted->height;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (i == shader->numpasses)\n\t\t\t{\t//this shader has no textures from which to source a width and height\n\t\t\t\tif (!shader->width)\n\t\t\t\t\tshader->width = 64;\n\t\t\t\tif (!shader->height)\n\t\t\t\t\tshader->height = 64;\n\t\t\t}\n\t\t}\n\t}\n\tif (shader->width && shader->height)\n\t{\n\t\tif (width)\n\t\t\t*width = shader->width;\n\t\tif (height)\n\t\t\t*height = shader->height;\n\t\treturn true;\t//final size\n\t}\n\telse\n\t{\n\t\t//fill with dummy values\n\t\tif (width)\n\t\t\t*width = 64;\n\t\tif (height)\n\t\t\t*height = 64;\n\t\treturn false;\n\t}\n}\nshader_t *R_RegisterPic (const char *name, const char *subdirs)\n{\n\tshader_t *shader;\n\tshader = R_LoadShader (NULL, name, SUF_2D, Shader_Default2D, subdirs);\n\treturn shader;\n}\n\nshader_t *QDECL R_RegisterShader (const char *name, unsigned int usageflags, const char *shaderscript)\n{\n\treturn R_LoadShader (NULL, name, usageflags, Shader_DefaultScript, shaderscript);\n}\n\nshader_t *R_RegisterShader_Lightmap (model_t *mod, const char *name)\n{\n\treturn R_LoadShader (mod, name, SUF_LIGHTMAP, Shader_DefaultBSPLM, NULL);\n}\n\nshader_t *R_RegisterShader_Vertex (model_t *mod, const char *name)\n{\n\treturn R_LoadShader (mod, name, 0, Shader_DefaultBSPVertex, NULL);\n}\n\nshader_t *R_RegisterShader_Flare (model_t *mod, const char *name)\n{\n\treturn R_LoadShader (mod, name, 0, Shader_DefaultBSPFlare, NULL);\n}\n\nshader_t *QDECL R_RegisterSkin (model_t *mod, const char *shadername)\n{\n\tchar newsname[MAX_QPATH];\n\tshader_t *shader;\n#ifdef _DEBUG\n\tif (shadername == com_token)\n\t\tCon_Printf(\"R_RegisterSkin was passed com_token. that will bug out.\\n\");\n#endif\n\n\tnewsname[0] = 0;\n\tif (mod && !strchr(shadername, '/') && *shadername)\n\t{\n\t\tchar *modname = mod->name;\n\t\tchar *b = COM_SkipPath(modname);\n\t\tif (b != modname && b-modname + strlen(shadername)+1 < sizeof(newsname))\n\t\t{\n\t\t\tb--;\t//no trailing /\n\t\t\tmemcpy(newsname, modname, b - modname);\n\t\t\tnewsname[b-modname] = 0;\n\t\t}\n\t}\n\tif (*newsname)\n\t{\n\t\tint l = strlen(newsname);\n\t\tQ_strncpyz(newsname+l, \":models\", sizeof(newsname)-l);\n\t}\n\telse\n\t\tQ_strncpyz(newsname, \"models\", sizeof(newsname));\n\tshader = R_LoadShader (mod, shadername, 0, Shader_DefaultSkin, newsname);\n\treturn shader;\n}\nshader_t *R_RegisterCustom (model_t *mod, const char *name, unsigned int usageflags, shader_gen_t *defaultgen, const void *args)\n{\n\treturn R_LoadShader (mod, name, usageflags, defaultgen, args);\n}\n#endif //SERVERONLY\n"
  },
  {
    "path": "engine/gl/gl_shadow.c",
    "content": "#include \"quakedef.h\"\n\n/*\nroom for improvement:\nThere is no screen-space culling of lit surfaces.\nmodel meshes are interpolated multiple times per frame\n*/\n\n\n#if defined(RTLIGHTS) && !defined(SERVERONLY)\n\n#ifdef VKQUAKE\n#include \"../vk/vkrenderer.h\"\n#endif\n#include \"glquake.h\"\n#include \"shader.h\"\n\n#ifdef D3D9QUAKE\n#include \"shader.h\"\n#if !defined(HMONITOR_DECLARED) && (WINVER < 0x0500)\n    #define HMONITOR_DECLARED\n    DECLARE_HANDLE(HMONITOR);\n#endif\n#include <d3d9.h>\nextern LPDIRECT3DDEVICE9 pD3DDev9;\nvoid D3D9BE_Cull(unsigned int sflags);\nvoid D3D9BE_RenderShadowBuffer(unsigned int numverts, IDirect3DVertexBuffer9 *vbuf, unsigned int numindicies, IDirect3DIndexBuffer9 *ibuf);\n#endif\n#ifdef D3D11QUAKE\nvoid D3D11BE_GenerateShadowBuffer(void **vbuf, vecV_t *verts, int numverts, void **ibuf, index_t *indicies, int numindicies);\nvoid D3D11BE_RenderShadowBuffer(unsigned int numverts, void *vbuf, unsigned int numindicies, void *ibuf);\nvoid D3D11_DestroyShadowBuffer(void *vbuf, void *ibuf);\nvoid D3D11BE_DoneShadows(void);\n#endif\n#ifdef VKQUAKE\n#endif\nvoid GLBE_RenderShadowBuffer(unsigned int numverts, int vbo, vecV_t *verts, unsigned numindicies, int ibo, index_t *indicies);\n\nstatic void SHM_Shutdown(void);\n\n#define SHADOWMAP_SIZE 512\n\n#define PROJECTION_DISTANCE (float)(sh_shmesh->radius*2)//0x7fffffff\n\n#ifdef BEF_PUSHDEPTH\nextern qboolean r_pushdepth;\n#endif\n\ntexid_t crepuscular_texture_id;\nfbostate_t crepuscular_fbo;\nshader_t *crepuscular_shader;\n\ncvar_t r_shadow_shadowmapping_nearclip = CVAR(\"r_shadow_shadowmapping_nearclip\", \"1\");\ncvar_t r_shadow_shadowmapping_bias = CVAR(\"r_shadow_shadowmapping_bias\", \"0.03\");\ncvar_t r_shadow_scissor = CVARD(\"r_shadow_scissor\", \"1\", \"constrains stencil shadows to the onscreen box that contains the maxmium extents of the light. This avoids unnecessary work.\");\n\ncvar_t r_shadow_realtime_world\t\t\t\t= CVARFD (\"r_shadow_realtime_world\", \"0\", CVAR_ARCHIVE, \"Enables the use of static/world realtime lights.\");\ncvar_t r_shadow_realtime_world_shadows\t\t= CVARF (\"r_shadow_realtime_world_shadows\", \"1\", CVAR_ARCHIVE);\ncvar_t r_shadow_realtime_world_lightmaps\t= CVARFD (\"r_shadow_realtime_world_lightmaps\", \"0\", 0, \"Specifies how much of the map's normal lightmap to retain when using world realtime lights. 0 completely replaces lighting.\");\nfloat r_shadow_realtime_world_lightmaps_force;\ncvar_t r_shadow_realtime_world_importlightentitiesfrommap = CVARFD (\"r_shadow_realtime_world_importlightentitiesfrommap\", \"0\", CVAR_ARCHIVE, \"Controls default loading of map-based realtime lights.\\n0: Load explicit .rtlight files only.\\n1: Load explicit lights then try fallback to parsing the entities lump.\\n2: Load only the entities lump.\");\ncvar_t r_shadow_realtime_dlight\t\t\t\t= CVARAFD (\"r_shadow_realtime_dlight\", \"1\", \"r_shadow_realtime_dynamic\", CVAR_ARCHIVE, \"Enables the use of dynamic realtime lights, allowing explosions to use bumpmaps etc properly.\");\ncvar_t r_shadow_realtime_dlight_shadows\t\t= CVARFD (\"r_shadow_realtime_dlight_shadows\", \"1\", CVAR_ARCHIVE, \"Allows dynamic realtime lights to cast shadows as they move.\");\ncvar_t r_shadow_realtime_dlight_ambient\t\t= CVAR (\"r_shadow_realtime_dlight_ambient\", \"0\");\ncvar_t r_shadow_realtime_dlight_diffuse\t\t= CVAR (\"r_shadow_realtime_dlight_diffuse\", \"1\");\ncvar_t r_shadow_realtime_dlight_specular\t= CVAR (\"r_shadow_realtime_dlight_specular\", \"4\");\t//excessive, but noticable. its called stylized, okay? shiesh, some people\ncvar_t r_shadow_playershadows\t\t\t\t= CVARD (\"r_shadow_playershadows\", \"1\", \"Controls the presence of shadows on the local player.\");\ncvar_t r_shadow_raytrace\t\t\t\t\t= CVARFD (\"r_shadow_raytrace\", \"0\", CVAR_ARCHIVE, \"Enables use of hardware raytracing for shadows. Consider also using with r_halfrate.\");\ncvar_t r_shadow_shadowmapping\t\t\t\t= CVARFD (\"r_shadow_shadowmapping\", \"1\", CVAR_ARCHIVE, \"Enables soft shadows instead of stencil shadows.\");\ncvar_t r_shadow_shadowmapping_precision\t\t= CVARD (\"r_shadow_shadowmapping_precision\", \"1\", \"Scales the shadowmap detail level up or down.\");\nstatic cvar_t r_shadow_shadowmapping_depthbits\t\t= CVARD (\"r_shadow_shadowmapping_depthbits\", \"16\", \"Shadowmap depth bits. 16, 24, or 32.\");\ncvar_t r_sun_dir\t\t\t\t\t\t\t= CVARD (\"r_sun_dir\", \"0.2 0.5 0.8\", \"Specifies the direction that crepusular rays appear along\");\ncvar_t r_sun_colour\t\t\t\t\t\t\t= CVARFD (\"r_sun_colour\", \"0 0 0\", CVAR_ARCHIVE, \"Specifies the colour of sunlight that appears in the form of crepuscular rays.\");\n\nstatic cvar_t r_shadows_fakedistance\t\t= CVARD(\"r_shadows_fakedistance\", \"1024\", \"The radius to use for fake shadows.\");\nstatic cvar_t r_shadows_throwdirection\t\t= CVARD(\"r_shadows_throwdirection\", \"0 0 -1\", \"The direction to throw the fake shadows in. Should ideally be opposite to r_sun_dir, but that just shows how fake these things actually are.\");\nstatic cvar_t r_shadows_focus\t\t\t\t= CVARD(\"r_shadows_focus\", \"0 0 0\", \"Offset for the center of the fake-shadows volume.\");\n\nstatic void Sh_DrawEntLighting(dlight_t *light, vec3_t colour, qbyte *pvs);\n\nstatic pvsbuffer_t\tlvisb, lvisb2;\n\n/*\ncalled on framebuffer resize.\nflushes textures so they can be regenerated at the real size\n*/\nvoid Sh_Reset(void)\n{\n#ifdef GLQUAKE\n\tif (crepuscular_texture_id)\n\t{\n\t\tImage_DestroyTexture(crepuscular_texture_id);\n\t\tcrepuscular_texture_id = r_nulltex;\n\t}\n\tGLBE_FBO_Destroy(&crepuscular_fbo);\n#endif\n}\nvoid Sh_Shutdown(void)\n{\n\tSh_Reset();\n\tSHM_Shutdown();\n}\n\n\n\ntypedef struct {\n\tunsigned int count;\n\tunsigned int faceidxcount;\n\tunsigned int faceidxfirst;\n\tunsigned int max;\n\ttexture_t *tex;\n\tvbo_t *vbo;\n\tmesh_t **s;\n} shadowmeshbatch_t;\ntypedef struct shadowmesh_s\n{\n\tvec3_t\torigin;\n\tfloat\tradius;\n\tenum\n\t{\n\t\tSMT_STENCILVOLUME,\t//build edges mesh (and surface list)\n\t\tSMT_SHADOWMAP,\t\t//build front faces mesh (and surface list)\n\t\tSMT_ORTHO,\t\t\t//bounded by a box and with a single direction rather than an origin.\n\t\tSMT_SHADOWLESS,\t\t//build vis+surface list only\n\t\tSMT_DEFERRED\t\t//build vis without caring about any surfaces at all.\n\t} type;\n\tunsigned int numindicies;\n\tunsigned int maxindicies;\n\tindex_t *indicies;\n\n\tunsigned int numverts;\n\tunsigned int maxverts;\n\tvecV_t *verts;\n\n\t//we also have a list of all the surfaces that this light lights.\n\tunsigned int numbatches;\n\tshadowmeshbatch_t *batches;\n\n\tunsigned int leafbytes;\n\tunsigned char *litleaves;\n\n#ifdef VKQUAKE\n\tstruct vk_shadowbuffer *vkbuffer;\n#endif\n#ifdef GLQUAKE\n\tGLuint vefbo[3];\n\tqboolean havefaceebo;\n#endif\n#ifdef D3D9QUAKE\n\tIDirect3DVertexBuffer9\t*d3d9_vbuffer;\n\tIDirect3DIndexBuffer9\t*d3d9_ibuffer;\n#endif\n#ifdef D3D11QUAKE\n\tvoid\t*d3d11_vbuffer;\n\tvoid\t*d3d11_ibuffer;\n#endif\n} shadowmesh_t;\n\n/*state of the current shadow mesh*/\n#define inc 128\nint sh_shadowframe;\nstatic int sh_firstindex;\nstatic int sh_vertnum;\t\t//vertex number (set to 0 at SH_Begin)\nstatic shadowmesh_t *sh_shmesh, sh_tempshmesh;\n\n/* functions to add geometry to the shadow mesh */\nstatic void SHM_BeginQuads (void)\n{\n\tsh_firstindex = sh_shmesh->numverts;\n}\nstatic void SHM_End (void)\n{\n\tint i;\n\ti = (sh_shmesh->numindicies+(sh_vertnum/4)*6+inc+5)&~(inc-1);\t//and a bit of padding\n\tif (sh_shmesh->maxindicies != i)\n\t{\n\t\tsh_shmesh->maxindicies = i;\n\t\tsh_shmesh->indicies = BZ_Realloc(sh_shmesh->indicies, i * sizeof(*sh_shmesh->indicies));\n\t}\n\t//add the extra triangles\n\tfor (i = 0; i < sh_vertnum; i+=4)\n\t{\n\t\tsh_shmesh->indicies[sh_shmesh->numindicies++] = sh_firstindex + i+0;\n\t\tsh_shmesh->indicies[sh_shmesh->numindicies++] = sh_firstindex + i+1;\n\t\tsh_shmesh->indicies[sh_shmesh->numindicies++] = sh_firstindex + i+2;\n\n\t\tsh_shmesh->indicies[sh_shmesh->numindicies++] = sh_firstindex + i+0;\n\t\tsh_shmesh->indicies[sh_shmesh->numindicies++] = sh_firstindex + i+2;\n\t\tsh_shmesh->indicies[sh_shmesh->numindicies++] = sh_firstindex + i+3;\n\t}\n\tsh_vertnum = 0;\n}\nstatic void SHM_Vertex3fv (const float *v)\n{\n\tint i;\n\n//add the verts as we go\n\ti = (sh_shmesh->numverts+inc+5)&~(inc-1);\t//and a bit of padding\n\tif (sh_shmesh->maxverts < i)\n\t{\n\t\tsh_shmesh->maxverts = i;\n\t\tsh_shmesh->verts = BZ_Realloc(sh_shmesh->verts, i * sizeof(*sh_shmesh->verts));\n\t}\n\n\tsh_shmesh->verts[sh_shmesh->numverts][0] = v[0];\n\tsh_shmesh->verts[sh_shmesh->numverts][1] = v[1];\n\tsh_shmesh->verts[sh_shmesh->numverts][2] = v[2];\n\n\tsh_vertnum++;\n\tsh_shmesh->numverts++;\n\n\n\tif (sh_vertnum == 4)\n\t{\n\t\tSHM_End();\n\t\tsh_firstindex = sh_shmesh->numverts;\n\t}\n}\n\nstatic void SHM_MeshFrontOnly(int numverts, vecV_t *verts, int numidx, index_t *idx)\n{\n\tint first = sh_shmesh->numverts;\n\tint v, i;\n\tvecV_t *outv;\n\tindex_t *outi;\n\n\t/*make sure there's space*/\n\tv = (sh_shmesh->numverts+numverts + inc)&~(inc-1);\t//and a bit of padding\n\tif (sh_shmesh->maxverts < v)\n\t{\n\t\tv *= 2;\n\t\tv += 1024;\n\t\tsh_shmesh->maxverts = v;\n\t\tsh_shmesh->verts = BZ_Realloc(sh_shmesh->verts, v * sizeof(*sh_shmesh->verts));\n\t}\n\n\toutv = sh_shmesh->verts + sh_shmesh->numverts;\n\tfor (v = 0; v < numverts; v++)\n\t{\n\t\tVectorCopy(verts[v], outv[v]);\n\t}\n\n\tv = (sh_shmesh->numindicies+numidx + inc)&~(inc-1);\t//and a bit of padding\n\tif (sh_shmesh->maxindicies < v)\n\t{\n\t\tv *= 2;\n\t\tv += 1024;\n\t\tsh_shmesh->maxindicies = v;\n\t\tsh_shmesh->indicies = BZ_Realloc(sh_shmesh->indicies, v * sizeof(*sh_shmesh->indicies));\n\t}\n\touti = sh_shmesh->indicies + sh_shmesh->numindicies;\n\tfor (i = 0; i < numidx; i++)\n\t{\n\t\touti[i] = first + idx[i];\n\t}\n\n\tsh_shmesh->numverts += numverts;\n\tsh_shmesh->numindicies += numidx;\n}\n#if 0\nstatic void SHM_MeshBackOnly(int numverts, vecV_t *verts, int numidx, index_t *idx)\n{\n\tint first = sh_shmesh->numverts;\n\tint v, i;\n\tvecV_t *outv;\n\tindex_t *outi;\n\n\t/*make sure there's space*/\n\tv = (sh_shmesh->numverts+numverts + inc)&~(inc-1);\t//and a bit of padding\n\tif (sh_shmesh->maxverts < v)\n\t{\n\t\tv += 1024;\n\t\tsh_shmesh->maxverts = v;\n\t\tsh_shmesh->verts = BZ_Realloc(sh_shmesh->verts, v * sizeof(*sh_shmesh->verts));\n\t}\n\n\toutv = sh_shmesh->verts + sh_shmesh->numverts;\n\tfor (v = 0; v < numverts; v++)\n\t{\n\t\tVectorCopy(verts[v], outv[v]);\n\t}\n\n\tv = (sh_shmesh->numindicies+numidx + inc)&~(inc-1);\t//and a bit of padding\n\tif (sh_shmesh->maxindicies < v)\n\t{\n\t\tv += 1024;\n\t\tsh_shmesh->maxindicies = v;\n\t\tsh_shmesh->indicies = BZ_Realloc(sh_shmesh->indicies, v * sizeof(*sh_shmesh->indicies));\n\t}\n\touti = sh_shmesh->indicies + sh_shmesh->numindicies;\n\tfor (i = 0; i < numidx; i+=3)\n\t{\n\t\touti[i+0] = first + idx[i+2];\n\t\touti[i+1] = first + idx[i+1];\n\t\touti[i+2] = first + idx[i+0];\n\t}\n\n\tsh_shmesh->numverts += numverts;\n\tsh_shmesh->numindicies += numidx;\n}\n#endif\nstatic void SHM_TriangleFan(int numverts, vecV_t *verts, vec3_t lightorg, float pd)\n{\n\tint v, i, idxs;\n\tfloat *v1;\n\tvec3_t v3;\n\tvecV_t *outv;\n\tindex_t *outi;\n\n\t/*make sure there's space*/\n\tv = (sh_shmesh->numverts+numverts*2 + inc)&~(inc-1);\t//and a bit of padding\n\tif (sh_shmesh->maxverts < v)\n\t{\n\t\tv += 1024;\n\t\tsh_shmesh->maxverts = v;\n\t\tsh_shmesh->verts = BZ_Realloc(sh_shmesh->verts, v * sizeof(*sh_shmesh->verts));\n\t}\n\toutv = sh_shmesh->verts + sh_shmesh->numverts;\n\n\tfor (v = 0; v < numverts; v++)\n\t{\n\t\tv1 = verts[v];\n\t\tVectorCopy(v1, outv[v]);\n\n\t\tv3[0] = ( v1[0]-lightorg[0] )*pd;\n\t\tv3[1] = ( v1[1]-lightorg[1] )*pd;\n\t\tv3[2] = ( v1[2]-lightorg[2] )*pd;\n\n\t\toutv[v+numverts][0] = v1[0]+v3[0];\n\t\toutv[v+numverts][1] = v1[1]+v3[1];\n\t\toutv[v+numverts][2] = v1[2]+v3[2];\n\t}\n\n\tidxs = (numverts-2)*3;\n\t/*now add the verts in a fan*/\n\tv = (sh_shmesh->numindicies+idxs*2+inc)&~(inc-1);\t//and a bit of padding\n\tif (sh_shmesh->maxindicies < v)\n\t{\n\t\tv += 1024;\n\t\tsh_shmesh->maxindicies = v;\n\t\tsh_shmesh->indicies = BZ_Realloc(sh_shmesh->indicies, v * sizeof(*sh_shmesh->indicies));\n\t}\n\touti = sh_shmesh->indicies + sh_shmesh->numindicies;\n\n\tfor (v = 2, i = 0; v < numverts; v++, i+=3)\n\t{\n\t\touti[i+0] = sh_shmesh->numverts;\n\t\touti[i+1] = sh_shmesh->numverts+v-1;\n\t\touti[i+2] = sh_shmesh->numverts+v;\n\n\t\touti[i+0+idxs] = sh_shmesh->numverts+numverts+v;\n\t\touti[i+1+idxs] = sh_shmesh->numverts+numverts+v-1;\n\t\touti[i+2+idxs] = sh_shmesh->numverts+numverts;\n\t}\n\n\t/*we added this many*/\n\tsh_shmesh->numverts += numverts*2;\n\tsh_shmesh->numindicies += i*2;\n}\n\nstatic void SHM_Shadow_Cache_Surface(msurface_t *surf)\n{\n\tint i;\n\n\ti = surf->sbatch->user.bmodel.shadowbatch;\n\tif (i < 0)\n\t\treturn;\n\n\tif (sh_shmesh->batches[i].count == sh_shmesh->batches[i].max)\n\t{\n\t\tsh_shmesh->batches[i].max += 64;\n\t\tsh_shmesh->batches[i].s = BZ_Realloc(sh_shmesh->batches[i].s, sizeof(void*)*(sh_shmesh->batches[i].max));\n\t}\n\tsh_shmesh->batches[i].s[sh_shmesh->batches[i].count] = surf->mesh;\n\tsh_shmesh->batches[i].count++;\n\tsh_shmesh->batches[i].faceidxcount += surf->mesh->numindexes;\n}\n\nstatic void SHM_Shadow_Cache_Leaf(mleaf_t *leaf)\n{\n\tint i;\n\n\ti = (leaf - cl.worldmodel->leafs)-1;\n\tsh_shmesh->litleaves[i>>3] |= 1<<(i&7);\n}\n\nstatic void SH_FreeShadowMesh_(shadowmesh_t *sm)\n{\n\tunsigned int i;\n\tfor (i = 0; i < sm->numbatches; i++)\n\t\tZ_Free(sm->batches[i].s);\n\tsm->numbatches = 0;\n\tZ_Free(sm->batches);\n\tsm->batches = NULL;\n\tZ_Free(sm->indicies);\n\tsm->indicies = NULL;\n\tZ_Free(sm->verts);\n\tsm->verts = NULL;\n\tsm->numindicies = 0;\n\tsm->numverts = 0;\n\n\tswitch (qrenderer)\n\t{\n\tcase QR_NONE:\n\tcase QR_SOFTWARE:\n\tdefault:\n\t\tbreak;\n\n#ifdef GLQUAKE\n\tcase QR_OPENGL:\n\t\tif (qglDeleteBuffersARB)\n\t\t\tqglDeleteBuffersARB(3, sm->vefbo);\n\t\tsm->vefbo[0] = 0;\n\t\tsm->vefbo[1] = 0;\n\t\tsm->vefbo[2] = 0;\n\t\tsm->havefaceebo = false;\n\t\tbreak;\n#endif\n#ifdef VKQUAKE\n\tcase QR_VULKAN:\n\t\tVKBE_DestroyShadowBuffer(sm->vkbuffer);\n\t\tsm->vkbuffer = NULL;\n\t\tbreak;\n#endif\n#ifdef D3D9QUAKE\n\tcase QR_DIRECT3D9:\n\t\tif (sm->d3d9_ibuffer)\n\t\t\tIDirect3DIndexBuffer9_Release(sm->d3d9_ibuffer);\n\t\tsm->d3d9_ibuffer = NULL;\n\t\tif (sm->d3d9_vbuffer)\n\t\t\tIDirect3DVertexBuffer9_Release(sm->d3d9_vbuffer);\n\t\tsm->d3d9_vbuffer = NULL;\n\t\tbreak;\n#endif\n#ifdef D3D11QUAKE\n\tcase QR_DIRECT3D11:\n\t\tD3D11_DestroyShadowBuffer(sm->d3d11_vbuffer, sm->d3d11_ibuffer);\n\t\tsm->d3d11_vbuffer = NULL;\n\t\tsm->d3d11_ibuffer = NULL;\n\t\tbreak;\n#endif\n\t}\n}\nvoid SH_FreeShadowMesh(shadowmesh_t *sm)\n{\n\tSH_FreeShadowMesh_(sm);\n\tZ_Free(sm);\n}\n\nstatic void SH_CalcShadowBatches(model_t *mod)\n{\n\tint s;\n\tbatch_t *b;\n\tbatch_t *l = NULL;\n\tint sb;\n\n\tl = NULL;\n\tfor (s = 0; s < SHADER_SORT_COUNT; s++)\n\t{\n\t\tfor (b = mod->batches[s]; b; b = b->next)\n\t\t{\n\t\t\tif (!l || l->vbo != b->vbo || l->texture != b->texture)\n\t\t\t{\n\t\t\t\tb->user.bmodel.shadowbatch = mod->numshadowbatches++;\n\t\t\t\tl = b;\n\t\t\t}\n\t\t\telse\n\t\t\t\tb->user.bmodel.shadowbatch = l->user.bmodel.shadowbatch;\n\t\t}\n\t}\n\n\tif (!mod->numshadowbatches)\n\t\tmod->shadowbatches = NULL;\n\telse\n\t{\n\t\tl = NULL;\n\t\tsb = 0;\n\t\tmod->shadowbatches = BZ_Malloc(sizeof(*mod->shadowbatches)*mod->numshadowbatches);\n\t\tfor (s = 0; s < SHADER_SORT_COUNT; s++)\n\t\t{\n\t\t\tfor (b = mod->batches[s]; b; b = b->next)\n\t\t\t{\n\t\t\t\tif (!l || l->vbo != b->vbo || l->texture != b->texture)\n\t\t\t\t{\n\t\t\t\t\tmod->shadowbatches[sb].tex = b->texture;\n\t\t\t\t\tmod->shadowbatches[sb].vbo = b->vbo;\n\t\t\t\t\tsb++;\n\t\t\t\t\tl = b;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nstatic void SHM_BeginShadowMesh(dlight_t *dl, int type)\n{\n\tunsigned int i;\n\tunsigned int lb;\n\tsh_vertnum = 0;\n\n\tlb = (cl.worldmodel->numclusters+7)/8;\n\tif (!dl->die || !dl->key || (dl->flags&LFLAG_FORCECACHE))\n\t{\n\t\tsh_shmesh = dl->worldshadowmesh;\n\t\tif (!sh_shmesh || sh_shmesh->leafbytes != lb)\n\t\t{\n\t\t\t/*this shouldn't happen too often*/\n\t\t\tif (sh_shmesh)\n\t\t\t{\t//FIXME: if the light is the same light, reuse the memory allocations where possible...\n\t\t\t\tSH_FreeShadowMesh(sh_shmesh);\n\t\t\t}\n\n\t\t\t/*Create a new shadowmesh for this light*/\n\t\t\tsh_shmesh = Z_Malloc(sizeof(*sh_shmesh) + lb);\n\t\t\tsh_shmesh->leafbytes = lb;\n\t\t\tsh_shmesh->litleaves = (unsigned char*)(sh_shmesh+1);\n\n\t\t\tdl->worldshadowmesh = sh_shmesh;\n\t\t}\n\t\tmemset(sh_shmesh->litleaves, 0, sh_shmesh->leafbytes);\n\t\tdl->rebuildcache = false;\n\t}\n\telse\n\t{\n\t\tsh_shmesh = &sh_tempshmesh;\n\t\tif (sh_shmesh->leafbytes != lb)\n\t\t{\n\t\t\t/*this happens on map changes*/\n\t\t\tsh_shmesh->leafbytes = lb;\n\t\t\tZ_Free(sh_shmesh->litleaves);\n\t\t\tsh_shmesh->litleaves = Z_Malloc(lb);\n\t\t}\n\t}\n#ifdef GLQUAKE\n\tsh_shmesh->havefaceebo = false;\n#endif\n\tsh_shmesh->maxverts = 0;\n\tsh_shmesh->numverts = 0;\n\tsh_shmesh->maxindicies = 0;\n\tsh_shmesh->numindicies = 0;\n\tsh_shmesh->type = type;\n\tVectorCopy(dl->origin, sh_shmesh->origin);\n\tsh_shmesh->radius = dl->radius;\n\n\tif (!cl.worldmodel->numshadowbatches)\n\t{\n\t\tSH_CalcShadowBatches(cl.worldmodel);\n\t}\n\n\tif (sh_shmesh->numbatches != cl.worldmodel->numshadowbatches)\n\t{\n\t\tif (sh_shmesh->batches)\n\t\t{\n\t\t\tfor (i = 0; i < sh_shmesh->numbatches; i++)\n\t\t\t\tZ_Free(sh_shmesh->batches[i].s);\n\t\t\tZ_Free(sh_shmesh->batches);\n\t\t}\n\t\tsh_shmesh->batches = Z_Malloc(sizeof(shadowmeshbatch_t)*cl.worldmodel->numshadowbatches);\n\t\tsh_shmesh->numbatches=cl.worldmodel->numshadowbatches;\n\t}\n\n\tfor (i = 0; i < sh_shmesh->numbatches; i++)\n\t{\n\t\tsh_shmesh->batches[i].count = 0;\n\t\tsh_shmesh->batches[i].faceidxcount = 0;\n\t}\n}\n#ifdef GLQUAKE\nstatic size_t SHM_GenWorldFaceIndexes(index_t **outindexes)\n{\n\tsize_t count = 0, b, m, i;\n\tindex_t *out = *outindexes = NULL;\n\tmesh_t *surf;\n\tshadowmeshbatch_t *batch;\n\tsize_t tmp;\n\n\tif (sh_shmesh == &sh_tempshmesh)\n\t{\n\t\tfor (b = 0; b < sh_shmesh->numbatches; b++)\n\t\t\tsh_shmesh->batches[b].faceidxcount = 0;\n\n\t\t*outindexes = 0;\n\t\treturn 0;\n\t}\n\n\tfor (b = 0; b < sh_shmesh->numbatches; b++)\n\t\tcount+= sh_shmesh->batches[b].faceidxcount;\n\tout = *outindexes = (void*fte_restrict)BZ_Malloc(count * sizeof(*out));\n\n\tfor (b = 0, batch = sh_shmesh->batches; b < sh_shmesh->numbatches; b++, batch++)\n\t{\n\t\tbatch->faceidxfirst = out-*outindexes;\n\t\tfor (m = 0; m < batch->count; m++)\n\t\t{\n\t\t\tsurf = batch->s[m];\n\t\t\tfor (i = 0; i < surf->numindexes; i++)\n\t\t\t{\n\t\t\t\ttmp = surf->vbofirstvert + surf->indexes[i];\n\t\t\t\tif (tmp > MAX_INDICIES)\n\t\t\t\t\tSys_Error(\"Too many indexes\\n\");\n\t\t\t\t*out++ = surf->vbofirstvert + surf->indexes[i];\n\t\t\t}\n\t\t}\n\t}\n\treturn count;\n}\n#endif\nstatic struct shadowmesh_s *SHM_FinishShadowMesh(dlight_t *dl)\n{\n\tif (sh_shmesh != &sh_tempshmesh || 1)\n\t{\n\t\tswitch (qrenderer)\n\t\t{\n\t\tcase QR_NONE:\n\t\tcase QR_SOFTWARE:\n\t\tdefault:\n\t\t\tbreak;\n\n#ifdef GLQUAKE\n\t\tcase QR_OPENGL:\n\t\t\tif (!qglGenBuffersARB)\n\t\t\t\treturn sh_shmesh;\n\t\t\t{\t//generate a per-face buffer.\n\t\t\t\tindex_t *faceindexes;\n\t\t\t\tsize_t faceindexcount = SHM_GenWorldFaceIndexes(&faceindexes);\n\n\t\t\t\tif (!sh_shmesh->vefbo[0])\n\t\t\t\t\tqglGenBuffersARB(3, sh_shmesh->vefbo);\n\n\t\t\t\tGL_DeselectVAO();\n\t\t\t\tGL_SelectVBO(sh_shmesh->vefbo[0]);\n\t\t\t\tqglBufferDataARB(GL_ARRAY_BUFFER_ARB, sizeof(*sh_shmesh->verts) * sh_shmesh->numverts, sh_shmesh->verts, GL_STATIC_DRAW_ARB);\n\n\t\t\t\tif (faceindexes)\n\t\t\t\t{\n\t\t\t\t\tGL_SelectEBO(sh_shmesh->vefbo[2]);\n\t\t\t\t\tqglBufferDataARB(GL_ELEMENT_ARRAY_BUFFER_ARB, sizeof(*faceindexes) * faceindexcount, faceindexes, GL_STATIC_DRAW_ARB);\n\t\t\t\t\tBZ_Free(faceindexes);\n\t\t\t\t\tsh_shmesh->havefaceebo = true;\n\t\t\t\t}\n\n\t\t\t\tGL_SelectEBO(sh_shmesh->vefbo[1]);\n\t\t\t\tqglBufferDataARB(GL_ELEMENT_ARRAY_BUFFER_ARB, sizeof(*sh_shmesh->indicies) * sh_shmesh->numindicies, sh_shmesh->indicies, GL_STATIC_DRAW_ARB);\n\t\t\t}\n\t\t\tbreak;\n#endif\n#ifdef VKQUAKE\n\t\tcase QR_VULKAN:\n\t\t\tVKBE_DestroyShadowBuffer(sh_shmesh->vkbuffer);\n\t\t\tsh_shmesh->vkbuffer = VKBE_GenerateShadowBuffer(sh_shmesh->verts, sh_shmesh->numverts, sh_shmesh->indicies, sh_shmesh->numindicies, sh_shmesh == &sh_tempshmesh);\n\t\t\tbreak;\n#endif\n#ifdef D3D9QUAKE\n\t\tcase QR_DIRECT3D9:\n\t\t\tif (sh_shmesh->numindicies && sh_shmesh->numverts)\n\t\t\t{\n\t\t\t\tvoid *map;\n\t\t\t\tIDirect3DDevice9_CreateIndexBuffer(pD3DDev9, sizeof(index_t) * sh_shmesh->numindicies, 0, D3DFMT_QINDEX, D3DPOOL_MANAGED, &sh_shmesh->d3d9_ibuffer, NULL);\n\t\t\t\tIDirect3DIndexBuffer9_Lock(sh_shmesh->d3d9_ibuffer, 0, sizeof(index_t) * sh_shmesh->numindicies, &map, D3DLOCK_DISCARD);\n\t\t\t\tmemcpy(map, sh_shmesh->indicies, sizeof(index_t) * sh_shmesh->numindicies);\n\t\t\t\tIDirect3DIndexBuffer9_Unlock(sh_shmesh->d3d9_ibuffer);\n\n\t\t\t\tIDirect3DDevice9_CreateVertexBuffer(pD3DDev9, sizeof(vecV_t) * sh_shmesh->numverts, D3DUSAGE_WRITEONLY, 0, D3DPOOL_MANAGED, &sh_shmesh->d3d9_vbuffer, NULL);\n\t\t\t\tIDirect3DVertexBuffer9_Lock(sh_shmesh->d3d9_vbuffer, 0, sizeof(vecV_t) * sh_shmesh->numverts, &map, D3DLOCK_DISCARD);\n\t\t\t\tmemcpy(map, sh_shmesh->verts, sizeof(vecV_t) * sh_shmesh->numverts);\n\t\t\t\tIDirect3DVertexBuffer9_Unlock(sh_shmesh->d3d9_vbuffer);\n\n\t\t\t}\n\t\t\tbreak;\n#endif\n#ifdef D3D11QUAKE\t\n\t\tcase QR_DIRECT3D11:\n\t\t\tD3D11BE_GenerateShadowBuffer(&sh_shmesh->d3d11_vbuffer, sh_shmesh->verts, sh_shmesh->numverts, &sh_shmesh->d3d11_ibuffer, sh_shmesh->indicies, sh_shmesh->numindicies);\n\t\t\tbreak;\n#endif\n\t\t}\n\n\t\tZ_Free(sh_shmesh->verts);\n\t\tsh_shmesh->verts = NULL;\n\n\t\tZ_Free(sh_shmesh->indicies);\n\t\tsh_shmesh->indicies = NULL;\n\t}\n\treturn sh_shmesh;\n}\n\n\n/*state of the world that is still to compile*/\nstatic struct {\n\tshort count;\n\tshort count2;\n\tint next;\n\tint prev;\n} *edge;\nstatic int firstedge;\nstatic int maxedge;\nstatic void (*genshadowmapcallback) (msurface_t *mesh);\n\nstatic void SHM_RecursiveWorldNodeQ1_r (dlight_t *dl, mnode_t *node)\n{\n\tint\t\t\tc, side;\n\tmplane_t\t*plane;\n\tmsurface_t\t*surf, **mark;\n\tmleaf_t\t\t*pleaf;\n\tdouble\t\tdot;\n\n\tfloat\t\tl, maxdist;\n\tint\t\t\tj, s, t;\n\tvec3_t\t\timpact;\n\n\tif (node->shadowframe != sh_shadowframe)\n\t\treturn;\n\n\tif (node->contents == Q1CONTENTS_SOLID)\n\t\treturn;\t\t// solid\n\n\n\t//if light areabox is outside node, ignore node + children\n\tfor (c = 0; c < 3; c++)\n\t{\n\t\tif (dl->origin[c] + dl->radius < node->minmaxs[c])\n\t\t\treturn;\n\t\tif (dl->origin[c] - dl->radius > node->minmaxs[3+c])\n\t\t\treturn;\n\t}\n\n// if a leaf node, draw stuff\n\tif (node->contents < 0)\n\t{\n\t\tpleaf = (mleaf_t *)node;\n\t\tSHM_Shadow_Cache_Leaf(pleaf);\n\n\t\tif (sh_shmesh->type == SMT_DEFERRED)\t//such rtlights don't need ANY surface info, just a tight pvs\n\t\t\treturn;\n\n\t\tmark = pleaf->firstmarksurface;\n\t\tc = pleaf->nummarksurfaces;\n\n\t\tif (c)\n\t\t{\n\t\t\tdo\n\t\t\t{\n\t\t\t\t(*mark++)->shadowframe = sh_shadowframe;\n\t\t\t} while (--c);\n\t\t}\n\t\treturn;\n\t}\n\n// node is just a decision point, so go down the apropriate sides\n\n// find which side of the node we are on\n\tplane = node->plane;\n\n\tswitch (plane->type)\n\t{\n\tcase PLANE_X:\n\t\tdot = dl->origin[0] - plane->dist;\n\t\tbreak;\n\tcase PLANE_Y:\n\t\tdot = dl->origin[1] - plane->dist;\n\t\tbreak;\n\tcase PLANE_Z:\n\t\tdot = dl->origin[2] - plane->dist;\n\t\tbreak;\n\tdefault:\n\t\tdot = DotProduct (dl->origin, plane->normal) - plane->dist;\n\t\tbreak;\n\t}\n\n\tif (dot >= 0)\n\t\tside = 0;\n\telse\n\t\tside = 1;\n\n// recurse down the children, front side first\n\tSHM_RecursiveWorldNodeQ1_r (dl, node->children[side]);\n\n// draw stuff\n  \tc = node->numsurfaces;\n\n\tif (c)\n\t{\n\t\tsurf = cl.worldmodel->surfaces + node->firstsurface;\n\n\t\t{\n\n\t\t\tmaxdist = dl->radius*dl->radius;\n\n\t\t\tfor ( ; c ; c--, surf++)\n\t\t\t{\n\t\t\t\tif (surf->shadowframe != sh_shadowframe)\n\t\t\t\t\tcontinue;\n\n//\t\t\t\tif ((dot < 0) ^ !!(surf->flags & SURF_PLANEBACK))\n//\t\t\t\t\tcontinue;\t\t// wrong side\n\n//\t\t\t\tif (surf->flags & SURF_PLANEBACK)\n//\t\t\t\t\tcontinue;\n\n\t\t\t\tif (surf->flags & (SURF_DRAWALPHA | SURF_DRAWTILED))\n\t\t\t\t{\t// no shadows\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t//is the light on the right side?\n\t\t\t\tif (surf->flags & SURF_PLANEBACK)\n\t\t\t\t{//inverted normal.\n\t\t\t\t\tif (-DotProduct(surf->plane->normal, dl->origin)+surf->plane->dist >= dl->radius)\n\t\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tif (DotProduct(surf->plane->normal, dl->origin)-surf->plane->dist >= dl->radius)\n\t\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t//Yeah, you can blame LordHavoc for this alternate code here.\n\t\t\t\tfor (j=0 ; j<3 ; j++)\n\t\t\t\t\timpact[j] = dl->origin[j] - surf->plane->normal[j]*dot;\n\n\t\t\t\t// clamp center of light to corner and check brightness\n\t\t\t\tl = DotProduct (impact, surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3] - surf->texturemins[0];\n\t\t\t\ts = l+0.5;if (s < 0) s = 0;else if (s > surf->extents[0]) s = surf->extents[0];\n\t\t\t\ts = (l - s)*surf->texinfo->vecscale[0];\n\t\t\t\tl = DotProduct (impact, surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3] - surf->texturemins[1];\n\t\t\t\tt = l+0.5;if (t < 0) t = 0;else if (t > surf->extents[1]) t = surf->extents[1];\n\t\t\t\tt = (l - t)*surf->texinfo->vecscale[1];\n\t\t\t\t// compare to minimum light\n\t\t\t\tif ((s*s+t*t+dot*dot) < maxdist)\n\t\t\t\t\tgenshadowmapcallback(surf);\n\t\t\t}\n\t\t}\n\t}\n\n// recurse down the back side\n\tSHM_RecursiveWorldNodeQ1_r (dl, node->children[!side]);\n}\n\n#ifdef Q1BSPS\nvoid CategorizePlane ( mplane_t *plane );\nstatic void SHM_OrthoWorldLeafsQ1 (dlight_t *dl)\n{\n\tint\t\t\tc, i;\n\tmsurface_t\t*surf, **mark;\n\tmleaf_t\t\t*pleaf, *plastleaf;\n\tfloat dot;\n\n\tmplane_t orthoplanes[5];\n\n\tsh_shadowframe++;\n\n\tVectorCopy(dl->axis[0], orthoplanes[0].normal);\n\tVectorNegate(dl->axis[0], orthoplanes[1].normal);\n\tVectorCopy(dl->axis[1], orthoplanes[2].normal);\n\tVectorNegate(dl->axis[1], orthoplanes[3].normal);\n\tVectorNegate(dl->axis[0], orthoplanes[4].normal);\n\n\tfor (i = 0; i < countof(orthoplanes); i++)\n\t{\n\t\torthoplanes[i].dist = DotProduct(dl->origin, orthoplanes[i].normal) - dl->radius;\n\t\tCategorizePlane(&orthoplanes[i]);\n\t}\n\n\tfor (pleaf = cl.worldmodel->leafs+1, plastleaf = cl.worldmodel->leafs+cl.worldmodel->submodels[0].visleafs; pleaf <= plastleaf; pleaf++)\n\t{\n\t\tfor (i = 0; i < countof(orthoplanes); i++)\n\t\t\tif (BOX_ON_PLANE_SIDE (pleaf->minmaxs, pleaf->minmaxs+3, &orthoplanes[i]) == 2)\n\t\t\t\tgoto next;\n\n\t\tSHM_Shadow_Cache_Leaf(pleaf);\n\n\t\tmark = pleaf->firstmarksurface;\n\t\tc = pleaf->nummarksurfaces;\n\n\t\twhile (c --> 0)\n\t\t{\n\t\t\tsurf = *mark++;\n\n\t\t\tif (surf->flags & (SURF_DRAWALPHA | SURF_DRAWTILED | SURF_DRAWSKY))\n\t\t\t\tcontinue;\n\n\t\t\tif (surf->shadowframe != sh_shadowframe)\n\t\t\t{\n\t\t\t\tsurf->shadowframe = sh_shadowframe;\n\n\t\t\t\tdot = DotProduct(surf->plane->normal, dl->axis[0]);\n\t\t\t\tif (surf->flags & SURF_PLANEBACK)\n\t\t\t\t\tdot = -dot;\n\t\t\t\n\t\t\t\tif (dot < 0)\n\t\t\t\t{\n\t\t\t\t\tSHM_Shadow_Cache_Surface(surf);\n\t\t\t\t}\n\t\t\t\tSHM_MeshFrontOnly(surf->mesh->numvertexes, surf->mesh->xyz_array, surf->mesh->numindexes, surf->mesh->indexes);\n\t\t\t}\n\t\t}\n\nnext:;\n\t}\n}\n\nstatic void SHM_MarkLeavesQ1(dlight_t *dl, const qbyte *lvis)\n{\n\tmnode_t *node;\n\tint i;\n\tsh_shadowframe++;\n\n\tif (!lvis)\n\t\treturn;\n\n\t//variation on mark leaves\n\tfor (i=0 ; i<cl.worldmodel->numclusters ; i++)\n\t{\n\t\tif (lvis[i>>3] & (1<<(i&7)))\n\t\t{\n\t\t\tnode = (mnode_t *)&cl.worldmodel->leafs[i+1];\n\t\t\tdo\n\t\t\t{\n\t\t\t\tif (node->shadowframe == sh_shadowframe)\n\t\t\t\t\tbreak;\n\t\t\t\tnode->shadowframe = sh_shadowframe;\n\t\t\t\tnode = node->parent;\n\t\t\t} while (node);\n\t\t}\n\t}\n}\n\nvoid Q1BSP_GenerateShadowMesh(model_t *model, dlight_t *dl, const qbyte *lightvis, qbyte *litvis, void (*callback)(msurface_t *surf))\n{\n\tgenshadowmapcallback = callback;\n\tif (sh_shmesh->type == SMT_ORTHO)\n\t\tSHM_OrthoWorldLeafsQ1(dl);\n\telse\n\t{\n\t\tSHM_MarkLeavesQ1(dl, lightvis);\n\t\tSHM_RecursiveWorldNodeQ1_r(dl, cl.worldmodel->nodes);\n\t}\n}\n#endif\n\n#ifdef Q3BSPS\nstatic void SHM_OrthoWorldLeafsQ3 (dlight_t *dl)\n{\n\tint\t\t\tc, i;\n\tmsurface_t\t*surf, **mark;\n\tmleaf_t\t\t*pleaf, *plastleaf;\n\n\tmplane_t orthoplanes[5];\n\n\tsh_shadowframe++;\n\n\tVectorCopy(dl->axis[0], orthoplanes[0].normal);\n\tVectorNegate(dl->axis[0], orthoplanes[1].normal);\n\tVectorCopy(dl->axis[1], orthoplanes[2].normal);\n\tVectorNegate(dl->axis[1], orthoplanes[3].normal);\n\tVectorNegate(dl->axis[0], orthoplanes[4].normal);\n\n\tfor (i = 0; i < countof(orthoplanes); i++)\n\t{\n\t\torthoplanes[i].dist = DotProduct(dl->origin, orthoplanes[i].normal) - dl->radius;\n\t\tCategorizePlane(&orthoplanes[i]);\n\t}\n\n\tfor (pleaf = cl.worldmodel->leafs+1, plastleaf = cl.worldmodel->leafs+cl.worldmodel->numleafs; pleaf <= plastleaf; pleaf++)\n\t{\n\t\tfor (i = 0; i < countof(orthoplanes); i++)\n\t\t\tif (BOX_ON_PLANE_SIDE (pleaf->minmaxs, pleaf->minmaxs+3, &orthoplanes[i]) == 2)\n\t\t\t\tgoto next;\n\n\t\tSHM_Shadow_Cache_Leaf(pleaf);\n\n\t\tmark = pleaf->firstmarksurface;\n\t\tc = pleaf->nummarksurfaces;\n\n\t\twhile (c --> 0)\n\t\t{\n\t\t\tsurf = *mark++;\n\n\t\t\tif (surf->flags & (SURF_DRAWALPHA | SURF_DRAWTILED | SURF_DRAWSKY))\n\t\t\t\tcontinue;\n\n\t\t\tif (surf->shadowframe != sh_shadowframe)\n\t\t\t{\n\t\t\t\tsurf->shadowframe = sh_shadowframe;\n\t\t\t\n//\t\t\t\tif (dot < 0)\n\t\t\t\t{\n\t\t\t\t\tSHM_Shadow_Cache_Surface(surf);\n\t\t\t\t}\n//\t\t\t\telse\n//\t\t\t\tSHM_MeshBackOnly(surf->mesh->numvertexes, surf->mesh->xyz_array, surf->mesh->numindexes, surf->mesh->indexes);\n\t\t\t\t\tSHM_MeshFrontOnly(surf->mesh->numvertexes, surf->mesh->xyz_array, surf->mesh->numindexes, surf->mesh->indexes);\n\t\t\t}\n\t\t}\n\nnext:;\n\t}\n}\n#endif\n\n#ifdef Q2BSPS\nvoid CategorizePlane ( mplane_t *plane );\nstatic void SHM_OrthoWorldLeafsQ2 (dlight_t *dl)\n{\n\tint\t\t\tc, i;\n\tmsurface_t\t*surf, **mark;\n\tmleaf_t\t\t*pleaf, *plastleaf;\n\n\tmplane_t orthoplanes[5];\n\n\tsh_shadowframe++;\n\n\tVectorCopy(dl->axis[0], orthoplanes[0].normal);\n\tVectorNegate(dl->axis[0], orthoplanes[1].normal);\n\tVectorCopy(dl->axis[1], orthoplanes[2].normal);\n\tVectorNegate(dl->axis[1], orthoplanes[3].normal);\n\tVectorNegate(dl->axis[0], orthoplanes[4].normal);\n\n\tfor (i = 0; i < countof(orthoplanes); i++)\n\t{\n\t\torthoplanes[i].dist = DotProduct(dl->origin, orthoplanes[i].normal) - dl->radius;\n\t\tCategorizePlane(&orthoplanes[i]);\n\t}\n\n\tfor (pleaf = cl.worldmodel->leafs+1, plastleaf = cl.worldmodel->leafs+cl.worldmodel->numleafs; pleaf <= plastleaf; pleaf++)\n\t{\n\t\tfor (i = 0; i < countof(orthoplanes); i++)\n\t\t\tif (BOX_ON_PLANE_SIDE (pleaf->minmaxs, pleaf->minmaxs+3, &orthoplanes[i]) == 2)\n\t\t\t\tgoto next;\n\n\t\tSHM_Shadow_Cache_Leaf(pleaf);\n\n\t\tmark = pleaf->firstmarksurface;\n\t\tc = pleaf->nummarksurfaces;\n\n\t\twhile (c --> 0)\n\t\t{\n\t\t\tsurf = *mark++;\n\n\t\t\tif (surf->flags & (SURF_DRAWALPHA | SURF_DRAWTILED | SURF_DRAWSKY))\n\t\t\t\tcontinue;\n\n\t\t\tif (surf->shadowframe != sh_shadowframe)\n\t\t\t{\n\t\t\t\tsurf->shadowframe = sh_shadowframe;\n\n//\t\t\t\tif (dot < 0)\n\t\t\t\t{\n\t\t\t\t\tSHM_Shadow_Cache_Surface(surf);\n\t\t\t\t}\n//\t\t\t\telse\n//\t\t\t\tSHM_MeshBackOnly(surf->mesh->numvertexes, surf->mesh->xyz_array, surf->mesh->numindexes, surf->mesh->indexes);\n\t\t\t\t\tSHM_MeshFrontOnly(surf->mesh->numvertexes, surf->mesh->xyz_array, surf->mesh->numindexes, surf->mesh->indexes);\n\t\t\t}\n\t\t}\n\nnext:;\n\t}\n}\n\nstatic void SHM_RecursiveWorldNodeQ2_r (dlight_t *dl, mnode_t *node)\n{\n\tint\t\t\tc, side;\n\tmplane_t\t*plane;\n\tmsurface_t\t*surf, **mark;\n\tmleaf_t\t\t*pleaf;\n\tdouble\t\tdot;\n\tint v;\n\n\tfloat\t\tl, maxdist;\n\tint\t\t\tj, s, t;\n\tvec3_t\t\timpact;\n\tvec4_t\t\t*lmvecs;\n\tfloat\t\t*lmvecscale;\n\n\tif (node->shadowframe != sh_shadowframe)\n\t\treturn;\n\n\tif (node->contents == Q2CONTENTS_SOLID)\n\t\treturn;\t\t// solid\n\n\n\t//if light areabox is outside node, ignore node + children\n\tfor (c = 0; c < 3; c++)\n\t{\n\t\tif (dl->origin[c] + dl->radius < node->minmaxs[c])\n\t\t\treturn;\n\t\tif (dl->origin[c] - dl->radius > node->minmaxs[3+c])\n\t\t\treturn;\n\t}\n\n// if a leaf node, draw stuff\n\tif (node->contents != -1)\n\t{\n\t\tpleaf = (mleaf_t *)node;\n\n\t\tif (pleaf->cluster >= 0)\n\t\t\tsh_shmesh->litleaves[pleaf->cluster>>3] |= 1<<(pleaf->cluster&7);\n\n\t\tmark = pleaf->firstmarksurface;\n\t\tc = pleaf->nummarksurfaces;\n\n\t\tif (c)\n\t\t{\n\t\t\tdo\n\t\t\t{\n\t\t\t\t(*mark++)->shadowframe = sh_shadowframe;\n\t\t\t} while (--c);\n\t\t}\n\t\treturn;\n\t}\n\n// node is just a decision point, so go down the apropriate sides\n\n// find which side of the node we are on\n\tplane = node->plane;\n\n\tswitch (plane->type)\n\t{\n\tcase PLANE_X:\n\t\tdot = dl->origin[0] - plane->dist;\n\t\tbreak;\n\tcase PLANE_Y:\n\t\tdot = dl->origin[1] - plane->dist;\n\t\tbreak;\n\tcase PLANE_Z:\n\t\tdot = dl->origin[2] - plane->dist;\n\t\tbreak;\n\tdefault:\n\t\tdot = DotProduct (dl->origin, plane->normal) - plane->dist;\n\t\tbreak;\n\t}\n\n\tif (dot >= 0)\n\t\tside = 0;\n\telse\n\t\tside = 1;\n\n// recurse down the children, front side first\n\tSHM_RecursiveWorldNodeQ2_r (dl, node->children[side]);\n\n// draw stuff\n  \tc = node->numsurfaces;\n\n\tif (c)\n\t{\n\t\tsurf = cl.worldmodel->surfaces + node->firstsurface;\n\n\t\t{\n\n\t\t\tmaxdist = dl->radius*dl->radius;\n\n\t\t\tfor ( ; c ; c--, surf++)\n\t\t\t{\n\t\t\t\tif (surf->shadowframe != sh_shadowframe)\n\t\t\t\t\tcontinue;\n\n//\t\t\t\tif ((dot < 0) ^ !!(surf->flags & SURF_PLANEBACK))\n//\t\t\t\t\tcontinue;\t\t// wrong side\n\n//\t\t\t\tif (surf->flags & SURF_PLANEBACK)\n//\t\t\t\t\tcontinue;\n\n\t\t\t\tif (surf->flags & (SURF_DRAWALPHA | SURF_DRAWTILED))\n\t\t\t\t{\t// no shadows\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t//is the light on the right side?\n\t\t\t\tif (surf->flags & SURF_PLANEBACK)\n\t\t\t\t{//inverted normal.\n\t\t\t\t\tif (-DotProduct(surf->plane->normal, dl->origin)+surf->plane->dist >= dl->radius)\n\t\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tif (DotProduct(surf->plane->normal, dl->origin)-surf->plane->dist >= dl->radius)\n\t\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t//Yeah, you can blame LordHavoc for this alternate code here.\n\t\t\t\tfor (j=0 ; j<3 ; j++)\n\t\t\t\t\timpact[j] = dl->origin[j] - surf->plane->normal[j]*dot;\n\n\t\t\t\tif (currentmodel->facelmvecs)\n\t\t\t\t\tlmvecs = currentmodel->facelmvecs[surf-currentmodel->surfaces].lmvecs, lmvecscale = currentmodel->facelmvecs[surf-currentmodel->surfaces].lmvecscale;\n\t\t\t\telse\n\t\t\t\t\tlmvecs = surf->texinfo->vecs, lmvecscale = surf->texinfo->vecscale;\n\t\t\t\t// clamp center of light to corner and check brightness\n\t\t\t\tl = DotProduct (impact, lmvecs[0]) + lmvecs[0][3] - surf->texturemins[0];\n\t\t\t\ts = l;if (s < 0) s = 0;else if (s > surf->extents[0]) s = surf->extents[0];\n\t\t\t\ts = (l - s)*lmvecscale[0];\n\t\t\t\tl = DotProduct (impact, lmvecs[1]) + lmvecs[1][3] - surf->texturemins[1];\n\t\t\t\tt = l;if (t < 0) t = 0;else if (t > surf->extents[1]) t = surf->extents[1];\n\t\t\t\tt = (l - t)*lmvecscale[1];\n\t\t\t\t// compare to minimum light\n\t\t\t\tif ((s*s+t*t+dot*dot) < maxdist)\n\t\t\t\t{\n\t\t\t\t\tSHM_Shadow_Cache_Surface(surf);\n\t\t\t\t\tif (sh_shmesh->type == SMT_SHADOWMAP)\n\t\t\t\t\t{\n\t\t\t\t\t\tSHM_MeshFrontOnly(surf->mesh->numvertexes, surf->mesh->xyz_array, surf->mesh->numindexes, surf->mesh->indexes);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tif (sh_shmesh->type != SMT_STENCILVOLUME)\n\t\t\t\t\t\tcontinue;\n\n\t\t\t\t\t//build a list of the edges that are to be drawn.\n\t\t\t\t\tfor (v = 0; v < surf->numedges; v++)\n\t\t\t\t\t{\n\t\t\t\t\t\tint e, delta;\n\t\t\t\t\t\te = cl.worldmodel->surfedges[surf->firstedge+v];\n\t\t\t\t\t\t//negative edge means backwards edge.\n\t\t\t\t\t\tif (e < 0)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\te=-e;\n\t\t\t\t\t\t\tdelta = -1;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tdelta = 1;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (!edge[e].count)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (firstedge)\n\t\t\t\t\t\t\t\tedge[firstedge].prev = e;\n\t\t\t\t\t\t\tedge[e].next = firstedge;\n\t\t\t\t\t\t\tedge[e].prev = 0;\n\t\t\t\t\t\t\tfirstedge = e;\n\t\t\t\t\t\t\tedge[e].count = delta;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tedge[e].count += delta;\n\n\t\t\t\t\t\t\tif (!edge[e].count)\t//unlink\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tif (edge[e].next)\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tedge[edge[e].next].prev = edge[e].prev;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tif (edge[e].prev)\n\t\t\t\t\t\t\t\t\tedge[edge[e].prev].next = edge[e].next;\n\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t\tfirstedge = edge[e].next;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tSHM_TriangleFan(surf->mesh->numvertexes, surf->mesh->xyz_array, dl->origin, PROJECTION_DISTANCE);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n// recurse down the back side\n\tSHM_RecursiveWorldNodeQ2_r (dl, node->children[!side]);\n}\n\nstatic void SHM_MarkLeavesQ2(dlight_t *dl, const unsigned char *lvis)\n{\n\tmnode_t *node;\n\tint i;\n\tmleaf_t *leaf;\n\tint cluster;\n\tsh_shadowframe++;\n\n\tif (!dl->die)\n\t{\n\t\t//static\n\t\t//variation on mark leaves\n\t\tfor (i=0,leaf=cl.worldmodel->leafs ; i<cl.worldmodel->numleafs ; i++, leaf++)\n\t\t{\n\t\t\tcluster = leaf->cluster;\n\t\t\tif (cluster == -1)\n\t\t\t\tcontinue;\n\t\t\tif (lvis[cluster>>3] & (1<<(cluster&7)))\n\t\t\t{\n\t\t\t\tnode = (mnode_t *)leaf;\n\t\t\t\tdo\n\t\t\t\t{\n\t\t\t\t\tif (node->shadowframe == sh_shadowframe)\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tnode->shadowframe = sh_shadowframe;\n\t\t\t\t\tnode = node->parent;\n\t\t\t\t} while (node);\n\t\t\t}\n\t\t}\n\t}\n\telse\n\t{\n\t\t//dynamic lights will be discarded after this frame anyway, so only include leafs that are visible\n\t\t//variation on mark leaves\n\t\tfor (i=0,leaf=cl.worldmodel->leafs ; i<cl.worldmodel->numleafs ; i++, leaf++)\n\t\t{\n\t\t\tcluster = leaf->cluster;\n\t\t\tif (cluster == -1)\n\t\t\t\tcontinue;\n\t\t\tif (lvis[cluster>>3] & (1<<(cluster&7)))\n\t\t\t{\n\t\t\t\tnode = (mnode_t *)leaf;\n\t\t\t\tdo\n\t\t\t\t{\n\t\t\t\t\tif (node->shadowframe == sh_shadowframe)\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tnode->shadowframe = sh_shadowframe;\n\t\t\t\t\tnode = node->parent;\n\t\t\t\t} while (node);\n\t\t\t}\n\t\t}\n\t}\n}\nvoid Q2BSP_GenerateShadowMesh(model_t *model, dlight_t *dl, const qbyte *lightvis, qbyte *litvis, void (*callback)(msurface_t *surf))\n{\n\tgenshadowmapcallback = callback;\n\tif (sh_shmesh->type == SMT_ORTHO)\n\t\tSHM_OrthoWorldLeafsQ2(dl);\n\telse\n\t{\n\t\tSHM_MarkLeavesQ2(dl, lightvis);\n\t\tSHM_RecursiveWorldNodeQ2_r(dl, model->nodes);\n\t}\n}\n#endif\n\n#ifdef Q3BSPS\nstatic void SHM_RecursiveWorldNodeQ3_r (dlight_t *dl, mnode_t *node)\n{\n\tmplane_t\t*splitplane;\n\tfloat\t\tdist;\n\tmsurface_t\t**msurf;\n\tmsurface_t\t*surf;\n\tmleaf_t\t\t*leaf;\n\tint\t\t\ti;\n\n\tif (node->contents != -1)\n\t{\n\t\tleaf = (mleaf_t *)node;\n\t\tif (leaf->cluster >= 0)\n\t\t\tsh_shmesh->litleaves[leaf->cluster>>3] |= 1<<(leaf->cluster&7);\n\n\t// mark the polygons\n\t\tmsurf = leaf->firstmarksurface;\n\t\tfor (i=0 ; i<leaf->nummarksurfaces ; i++, msurf++)\n\t\t{\n\t\t\tsurf = *msurf;\n\n\t\t\t//only check each surface once. it can appear in multiple leafs.\n\t\t\tif (surf->shadowframe == sh_shadowframe)\n\t\t\t\tcontinue;\n\t\t\tsurf->shadowframe = sh_shadowframe;\n\n\t\t\t//FIXME: radius check\n\t\t\tSHM_Shadow_Cache_Surface(surf);\n\t\t\tif (sh_shmesh->type == SMT_SHADOWMAP && !(surf->texinfo->texture->shader->flags & SHADER_NOSHADOWS))\n\t\t\t\tSHM_MeshFrontOnly(surf->mesh->numvertexes, surf->mesh->xyz_array, surf->mesh->numindexes, surf->mesh->indexes);\n\t\t}\n\t\treturn;\n\t}\n\n\tsplitplane = node->plane;\n\tdist = DotProduct (dl->origin, splitplane->normal) - splitplane->dist;\n\n\tif (dist > dl->radius)\n\t{\n\t\tSHM_RecursiveWorldNodeQ3_r (dl, node->children[0]);\n\t\treturn;\n\t}\n\tif (dist < -dl->radius)\n\t{\n\t\tSHM_RecursiveWorldNodeQ3_r (dl, node->children[1]);\n\t\treturn;\n\t}\n\tSHM_RecursiveWorldNodeQ3_r (dl, node->children[0]);\n\tSHM_RecursiveWorldNodeQ3_r (dl, node->children[1]);\n}\n#endif\n\nstatic struct {\n\tunsigned int numtris;\n\tunsigned int maxtris;\n\tstruct {\n\t\tsigned int edge[3];\n\t} *tris; /*negative for reverse edge*/\n\n\tunsigned int numedges;\n\tunsigned int maxedges;\n\tstruct {\n\t\tunsigned int vert[2];\n\t} *edges;\n\n\tunsigned int numpoints;\n\tunsigned int maxpoints;\n\tvec3_t *points;\n\n\tunsigned int maxedgeuses;\n\tint *edgeuses;\t/*negative for back sides, so 0 means unused or used equally on both sides*/\n} cv;\n\nstatic void SHM_Shutdown(void)\n{\n\tSH_FreeShadowMesh_(&sh_tempshmesh);\n\tBZ_Free(sh_tempshmesh.litleaves);\n\tsh_tempshmesh.litleaves = NULL;\n\tsh_tempshmesh.leafbytes = 0;\n\tfree(cv.tris);\n\tfree(cv.edges);\n\tfree(cv.points);\n\tmemset(&cv, 0, sizeof(cv));\n}\n\n#ifdef Q3BSPS\n#define VERT_POS_EPSILON (1.0f/32)\nstatic int SHM_ComposeVolume_FindVert(float *vert)\n{\n\tint i;\n\tfor (i = 0; i < cv.numpoints; i++)\n\t{\n#if 1\n\t\tif (cv.points[i][0] == vert[0] &&\n\t\t\tcv.points[i][1] == vert[1] &&\n\t\t\tcv.points[i][2] == vert[2])\n#else\n\t\tvec3_t d;\n\t\td[0] = cv.points[i][0]-vert[0];\n\t\td[1] = cv.points[i][1]-vert[1];\n\t\td[2] = cv.points[i][2]-vert[2];\n\t\tif (d[0]*d[0] < VERT_POS_EPSILON &&\n\t\t\td[1]*d[1] < VERT_POS_EPSILON &&\n\t\t\td[2]*d[2] < VERT_POS_EPSILON)\n#endif\n\t\t\treturn i;\n\t}\n\tVectorCopy(vert, cv.points[i]);\n\tcv.numpoints++;\n\treturn i;\n}\nstatic int SHM_ComposeVolume_FindEdge(int v1, int v2)\n{\n\tint i;\n\tfor (i = 0; i < cv.numedges; i++)\n\t{\n\t\tif (cv.edges[i].vert[0] == v1 && cv.edges[i].vert[1] == v2)\n\t\t\treturn i;\n\t\tif (cv.edges[i].vert[0] == v2 && cv.edges[i].vert[1] == v1)\n\t\t\treturn -(i+1);\n\t}\n\tcv.edges[i].vert[0] = v1;\n\tcv.edges[i].vert[1] = v2;\n\tcv.numedges++;\n\treturn i;\n}\n\n/*each triangle is coplanar, and all face the light, and its a triangle fan. this is a special case that provides a slight speedup*/\nstatic void SHM_ComposeVolume_Fan(vecV_t *points, int numpoints)\n{\n\tint newmax;\n\tint lastedge;\n\tint i;\n\n\t#define MAX_ARRAY_VERTS 65535\n\tstatic index_t pointidx[MAX_ARRAY_VERTS];\n\n\t/*make sure there's space*/\n\tnewmax = (cv.numpoints+numpoints + inc)&~(inc-1);\n\tif (cv.maxpoints < newmax)\n\t{\n\t\tcv.maxpoints = newmax;\n\t\tcv.points = BZ_Realloc(cv.points, newmax * sizeof(*cv.points));\n\t}\n\tnewmax = (cv.numedges+(numpoints-2)*3 + inc)&~(inc-1);\n\tif (cv.maxedges < newmax)\n\t{\n\t\tcv.maxedges = newmax;\n\t\tcv.edges = BZ_Realloc(cv.edges, newmax * sizeof(*cv.edges));\n\t}\n\tnewmax = (cv.numtris+(numpoints-2) + inc)&~(inc-1);\n\tif (cv.maxtris < newmax)\n\t{\n\t\tcv.maxtris = newmax;\n\t\tcv.tris = BZ_Realloc(cv.tris, newmax * sizeof(*cv.tris));\n\t}\n\n\tfor (i = 0; i < numpoints; i++)\n\t{\n\t\tpointidx[i] = SHM_ComposeVolume_FindVert(points[i]);\n\t}\n\tlastedge = SHM_ComposeVolume_FindEdge(pointidx[0], pointidx[1]);\n\tfor (i = 2; i < numpoints; i++)\n\t{\n\t\tcv.tris[cv.numtris].edge[0] = lastedge;\n\t\tcv.tris[cv.numtris].edge[1] = SHM_ComposeVolume_FindEdge(pointidx[i-1], pointidx[i]);\n\t\tlastedge = SHM_ComposeVolume_FindEdge(pointidx[i], pointidx[0]);\n\t\tcv.tris[cv.numtris].edge[2] = lastedge;\n\t\tlastedge = -(lastedge+1);\n\t\tcv.numtris++;\n\t}\n}\nstatic void SHM_ComposeVolume_Soup(vecV_t *points, int numpoints, index_t *idx, int numidx)\n{\n\tint newmax;\n\tint i;\n\n\t#define MAX_ARRAY_VERTS 65535\n\tstatic index_t pointidx[MAX_ARRAY_VERTS];\n\n\t/*make sure there's space*/\n\tnewmax = (cv.numpoints+numpoints + inc)&~(inc-1);\n\tif (cv.maxpoints < newmax)\n\t{\n\t\tcv.maxpoints = newmax;\n\t\tcv.points = BZ_Realloc(cv.points, newmax * sizeof(*cv.points));\n\t}\n\tnewmax = (cv.numedges+numidx + inc)&~(inc-1);\n\tif (cv.maxedges < newmax)\n\t{\n\t\tcv.maxedges = newmax;\n\t\tcv.edges = BZ_Realloc(cv.edges, newmax * sizeof(*cv.edges));\n\t}\n\tnewmax = (cv.numtris+numidx/3 + inc)&~(inc-1);\n\tif (cv.maxtris < newmax)\n\t{\n\t\tcv.maxtris = newmax;\n\t\tcv.tris = BZ_Realloc(cv.tris, newmax * sizeof(*cv.tris));\n\t}\n\n\tfor (i = 0; i < numpoints; i++)\n\t{\n\t\tpointidx[i] = SHM_ComposeVolume_FindVert(points[i]);\n\t}\n\n\tfor (i = 0; i < numidx; i+=3, idx+=3)\n\t{\n\t\tcv.tris[cv.numtris].edge[0] = SHM_ComposeVolume_FindEdge(pointidx[idx[0]], pointidx[idx[1]]);\n\t\tcv.tris[cv.numtris].edge[1] = SHM_ComposeVolume_FindEdge(pointidx[idx[1]], pointidx[idx[2]]);\n\t\tcv.tris[cv.numtris].edge[2] = SHM_ComposeVolume_FindEdge(pointidx[idx[2]], pointidx[idx[0]]);\n\t\tcv.numtris++;\n\t}\n}\n\n/*call this function after generating litsurfs meshes*/\nstatic void SHM_ComposeVolume_BruteForce(dlight_t *dl)\n{\n\tshadowmeshbatch_t *sms;\n\tunsigned int tno;\n\tunsigned int sno;\n\tint i, e;\n\tmesh_t *sm;\n\tvec3_t ext;\n\tfloat sc;\n\tcv.numedges = 0;\n\tcv.numpoints = 0;\n\tcv.numtris = 0;\n\n\tfor (tno = 0; tno < sh_shmesh->numbatches; tno++)\n\t{\n\t\tsms = &sh_shmesh->batches[tno];\n\t\tif (!sms->count)\n\t\t\tcontinue;\n\t\tif ((cl.worldmodel->shadowbatches[tno].tex->shader->flags & (SHADER_BLEND|SHADER_NODRAW|SHADER_NOSHADOWS)))\n\t\t\tcontinue;\n\n\t\tfor (sno = 0; sno < sms->count; sno++)\n\t\t{\n\t\t\tsm = sms->s[sno];\n\n\t\t\tif (sm->istrifan)\n\t\t\t\tSHM_ComposeVolume_Fan(sm->xyz_array, sm->numvertexes);\n\t\t\telse\n\t\t\t\tSHM_ComposeVolume_Soup(sm->xyz_array, sm->numvertexes, sm->indexes, sm->numindexes);\n\t\t}\n\t}\n\n\t/*FIXME: clip away overlapping triangles*/\n\n\tif (cv.maxedgeuses < cv.numedges)\n\t{\n\t\tBZ_Free(cv.edgeuses);\n\t\tcv.maxedgeuses = cv.numedges;\n\t\tcv.edgeuses = Z_Malloc(cv.maxedgeuses * sizeof(*cv.edgeuses));\n\t}\n\telse\n\t\tmemset(cv.edgeuses, 0, cv.numedges * sizeof(*cv.edgeuses));\n\t\n\ti = (sh_shmesh->numverts+cv.numpoints*6+inc+5)&~(inc-1);\t//and a bit of padding\n\tif (sh_shmesh->maxverts < i)\n\t{\n\t\tsh_shmesh->maxverts = i;\n\t\tsh_shmesh->verts = BZ_Realloc(sh_shmesh->verts, i * sizeof(*sh_shmesh->verts));\n\t}\n\n\tfor (i = 0; i < cv.numpoints; i++)\n\t{\n\t\t/*front face*/\n\t\tsh_shmesh->verts[(i * 2) + 0][0] = cv.points[i][0];\n\t\tsh_shmesh->verts[(i * 2) + 0][1] = cv.points[i][1];\n\t\tsh_shmesh->verts[(i * 2) + 0][2] = cv.points[i][2];\n\n\t\t/*shadow direction*/\n\t\text[0] = cv.points[i][0]-dl->origin[0];\n\t\text[1] = cv.points[i][1]-dl->origin[1];\n\t\text[2] = cv.points[i][2]-dl->origin[2];\n\t\t\n\t\tsc = dl->radius * VectorNormalize(ext);\n\n\t\t/*back face*/\n\t\tsh_shmesh->verts[(i * 2) + 1][0] = cv.points[i][0] + ext[0] * sc;\n\t\tsh_shmesh->verts[(i * 2) + 1][1] = cv.points[i][1] + ext[1] * sc;\n\t\tsh_shmesh->verts[(i * 2) + 1][2] = cv.points[i][2] + ext[2] * sc;\n\t}\n\tsh_shmesh->numverts = i*2;\n\n\ti = (sh_shmesh->numindicies+cv.numtris*6+cv.numedges*6+inc+5)&~(inc-1);\t//and a bit of padding\n\tif (sh_shmesh->maxindicies < i)\n\t{\n\t\tsh_shmesh->maxindicies = i;\n\t\tsh_shmesh->indicies = BZ_Realloc(sh_shmesh->indicies, i * sizeof(*sh_shmesh->indicies));\n\t}\n\n\tfor (tno = 0; tno < cv.numtris; tno++)\n\t{\n\t\tfor (i = 0; i < 3; i++)\n\t\t{\n\t\t\te = cv.tris[tno].edge[i];\n\t\t\tif (e < 0)\n\t\t\t{\n\t\t\t\te = -(e+1);\n\t\t\t\tcv.edgeuses[e]--;\n\t\t\t\te = cv.edges[e].vert[1];\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tcv.edgeuses[e]++;\n\t\t\t\te = cv.edges[e].vert[0];\n\t\t\t}\n\n\t\t\tsh_shmesh->indicies[sh_shmesh->numindicies+i] = e*2;\n\t\t\tsh_shmesh->indicies[sh_shmesh->numindicies+5-i] = e*2 + 1;\n\t\t}\n\t\tsh_shmesh->numindicies += 6;\n\t}\n\n\tfor (i = 0; i < cv.numedges; i++)\n\t{\n\t\tif (cv.edgeuses[i] > 0)\n\t\t{\n\t\t\tsh_shmesh->indicies[sh_shmesh->numindicies++] = cv.edges[i].vert[1]*2 + 0;\n\t\t\tsh_shmesh->indicies[sh_shmesh->numindicies++] = cv.edges[i].vert[0]*2 + 0;\n\t\t\tsh_shmesh->indicies[sh_shmesh->numindicies++] = cv.edges[i].vert[0]*2 + 1;\n\n\t\t\tsh_shmesh->indicies[sh_shmesh->numindicies++] = cv.edges[i].vert[0]*2 + 1;\n\t\t\tsh_shmesh->indicies[sh_shmesh->numindicies++] = cv.edges[i].vert[1]*2 + 1;\n\t\t\tsh_shmesh->indicies[sh_shmesh->numindicies++] = cv.edges[i].vert[1]*2 + 0;\n\t\t}\n\t\telse if (cv.edgeuses[i] < 0)\n\t\t{\n\t\t\t//generally should not happen...\n\t\t\tsh_shmesh->indicies[sh_shmesh->numindicies++] = cv.edges[i].vert[1]*2 + 0;\n\t\t\tsh_shmesh->indicies[sh_shmesh->numindicies++] = cv.edges[i].vert[0]*2 + 1;\n\t\t\tsh_shmesh->indicies[sh_shmesh->numindicies++] = cv.edges[i].vert[0]*2 + 0;\n\n\t\t\tsh_shmesh->indicies[sh_shmesh->numindicies++] = cv.edges[i].vert[0]*2 + 1;\n\t\t\tsh_shmesh->indicies[sh_shmesh->numindicies++] = cv.edges[i].vert[1]*2 + 0;\n\t\t\tsh_shmesh->indicies[sh_shmesh->numindicies++] = cv.edges[i].vert[1]*2 + 1;\n\t\t}\n\t}\n}\n\nvoid Q3BSP_GenerateShadowMesh(model_t *model, dlight_t *dl, const qbyte *lightvis, qbyte *litvis, void (*callback)(msurface_t *surf))\n{\n\t/*q3 doesn't have edge info*/\n\tif (sh_shmesh->type == SMT_ORTHO)\n\t\tSHM_OrthoWorldLeafsQ3(dl);\n\telse\n\t{\n\t\tsh_shadowframe++;\n\t\tSHM_RecursiveWorldNodeQ3_r(dl, model->nodes);\n\t}\n\tif (sh_shmesh->type == SMT_STENCILVOLUME)\n\t\tSHM_ComposeVolume_BruteForce(dl);\n}\n#endif\n\nstatic void SHM_Shadow_Surface_Shadowmap (msurface_t *surf)\n{\n\tSHM_Shadow_Cache_Surface(surf);\n\tif (surf->texinfo->texture->shader->flags & SHADER_NOSHADOWS)\n\t\treturn;\n\tSHM_MeshFrontOnly(surf->mesh->numvertexes, surf->mesh->xyz_array, surf->mesh->numindexes, surf->mesh->indexes);\n}\nstatic void SHM_Shadow_Surface_StencilVolume (msurface_t *surf)\n{\n\tint v;\n\tSHM_Shadow_Cache_Surface(surf);\n\tif (surf->texinfo->texture->shader->flags & SHADER_NOSHADOWS)\n\t\treturn;\n\tif (!surf->mesh->istrifan)\n\t\treturn;\n\n\t//build a list of the edges that are to be drawn.\n\tfor (v = 0; v < surf->numedges; v++)\n\t{\n\t\tint e, delta;\n\t\te = cl.worldmodel->surfedges[surf->firstedge+v];\n\t\t//negative edge means backwards edge.\n\t\tif (e < 0)\n\t\t{\n\t\t\te=-e;\n\t\t\tdelta = -1;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tdelta = 1;\n\t\t}\n\n\t\tif (!edge[e].count)\n\t\t{\n\t\t\tif (firstedge)\n\t\t\t\tedge[firstedge].prev = e;\n\t\t\tedge[e].next = firstedge;\n\t\t\tedge[e].prev = 0;\n\t\t\tfirstedge = e;\n\t\t\tedge[e].count = delta;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tedge[e].count += delta;\n\n\t\t\tif (!edge[e].count)\t//unlink\n\t\t\t{\n\t\t\t\tif (edge[e].next)\n\t\t\t\t{\n\t\t\t\t\tedge[edge[e].next].prev = edge[e].prev;\n\t\t\t\t}\n\t\t\t\tif (edge[e].prev)\n\t\t\t\t\tedge[edge[e].prev].next = edge[e].next;\n\t\t\t\telse\n\t\t\t\t\tfirstedge = edge[e].next;\n\t\t\t}\n\t\t}\n\t}\n\n\tSHM_TriangleFan(surf->mesh->numvertexes, surf->mesh->xyz_array, sh_shmesh->origin, PROJECTION_DISTANCE);\n}\n\nstatic struct shadowmesh_s *SHM_BuildShadowMesh(dlight_t *dl, unsigned char *lvis, int type)\n{\n\tfloat *v1, *v2;\n\tvec3_t v3, v4;\n\n\tif (dl->worldshadowmesh && !dl->rebuildcache && dl->worldshadowmesh->type == type)\n\t\treturn dl->worldshadowmesh;\n\n\tif (!lvis)\n\t{\n\t\tint clus;\n\t\tif (type == SMT_ORTHO)\n\t\t\t;\n\t\telse if ((type == SMT_SHADOWLESS || dl->lightcolourscales[0]) && cl.worldmodel->funcs.ClustersInSphere)\n\t\t\t//shadowless lights don't cast shadows, so they're seen through everything - their vis must reflect that.\n\t\t\tlvis = cl.worldmodel->funcs.ClustersInSphere(cl.worldmodel, dl->origin, dl->radius, &lvisb, NULL);\n\t\telse\n\t\t{\n\t\t\tclus = cl.worldmodel->funcs.ClusterForPoint(cl.worldmodel, dl->origin, NULL);\t//FIXME: track the lights area\n\t\t\tlvis = cl.worldmodel->funcs.ClusterPVS(cl.worldmodel, clus, &lvisb, PVM_FAST);\n\n\t\t\tif (cl.worldmodel->funcs.ClustersInSphere)\n\t\t\t\tlvis = cl.worldmodel->funcs.ClustersInSphere(cl.worldmodel, dl->origin, dl->radius, &lvisb2, lvis);\n\t\t}\n\t}\n\n\tfirstedge=0;\n\tif (maxedge < cl.worldmodel->numedges)\n\t{\n\t\tmaxedge = cl.worldmodel->numedges;\n\t\tZ_Free(edge);\n\t\tedge = Z_Malloc(sizeof(*edge) * maxedge);\n\t}\n\n\tSHM_BeginShadowMesh(dl, type);\n\tif (cl.worldmodel->funcs.GenerateShadowMesh)\n\t{\n\t\tswitch(type)\n\t\t{\n\t\tcase SMT_SHADOWMAP:\n\t\t\tcl.worldmodel->funcs.GenerateShadowMesh(cl.worldmodel, dl, lvis, sh_shmesh->litleaves, SHM_Shadow_Surface_Shadowmap);\n\t\t\tbreak;\n\t\tcase SMT_STENCILVOLUME:\n\t\t\tcl.worldmodel->funcs.GenerateShadowMesh(cl.worldmodel, dl, lvis, sh_shmesh->litleaves, SHM_Shadow_Surface_StencilVolume);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tcl.worldmodel->funcs.GenerateShadowMesh(cl.worldmodel, dl, lvis, sh_shmesh->litleaves, SHM_Shadow_Cache_Surface);\n\t\t\tbreak;\n\t\t}\n\t}\n\telse if (cl.worldmodel->type == mod_brush)\n\t{\n\t\tswitch(cl.worldmodel->fromgame)\n\t\t{\n#ifdef Q1BSPS\n\t\tcase fg_quake:\n\t\tcase fg_halflife:\n\t\t\tif (type == SMT_ORTHO)\n\t\t\t\tSHM_OrthoWorldLeafsQ1(dl);\n\t\t\telse\n\t\t\t{\n\t\t\t\tSHM_MarkLeavesQ1(dl, lvis);\n\t\t\t\tSHM_RecursiveWorldNodeQ1_r(dl, cl.worldmodel->nodes);\n\t\t\t}\n\t\t\tbreak;\n#endif\n\t\tdefault:\n\t\t\tsh_shadowframe++;\n\n\t\t\t{\n\t\t\t\tint cluster = cl.worldmodel->funcs.ClusterForPoint(cl.worldmodel, dl->origin, NULL);\n\t\t\t\tif (cluster >= 0)\n\t\t\t\t\tsh_shmesh->litleaves[cluster>>3] |= 1<<(cluster&7);\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t}\n\telse\n\t{\n\t\tSHM_BeginShadowMesh(dl, type);\n\t\tsh_shadowframe++;\n\t}\n\n\t/*generate edge polys for map types that need it (q1/q2)*/\n\tswitch (type)\n\t{\n\tcase SMT_STENCILVOLUME:\n\t\tSHM_BeginQuads();\n\t\twhile(firstedge)\n\t\t{\n\t\t\t//border\n\t\t\tv1 = cl.worldmodel->vertexes[cl.worldmodel->edges[firstedge].v[0]].position;\n\t\t\tv2 = cl.worldmodel->vertexes[cl.worldmodel->edges[firstedge].v[1]].position;\n\n\t\t\t//get positions of v3 and v4 based on the light position\n\t\t\tv3[0] = v1[0] + ( v1[0]-dl->origin[0] )*PROJECTION_DISTANCE;\n\t\t\tv3[1] = v1[1] + ( v1[1]-dl->origin[1] )*PROJECTION_DISTANCE;\n\t\t\tv3[2] = v1[2] + ( v1[2]-dl->origin[2] )*PROJECTION_DISTANCE;\n\n\t\t\tv4[0] = v2[0] + ( v2[0]-dl->origin[0] )*PROJECTION_DISTANCE;\n\t\t\tv4[1] = v2[1] + ( v2[1]-dl->origin[1] )*PROJECTION_DISTANCE;\n\t\t\tv4[2] = v2[2] + ( v2[2]-dl->origin[2] )*PROJECTION_DISTANCE;\n\n\t\t\tif (edge[firstedge].count > 0)\n\t\t\t{\n\t\t\t\tSHM_Vertex3fv(v3);\n\t\t\t\tSHM_Vertex3fv(v4);\n\t\t\t\tSHM_Vertex3fv(v2);\n\t\t\t\tSHM_Vertex3fv(v1);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tSHM_Vertex3fv(v1);\n\t\t\t\tSHM_Vertex3fv(v2);\n\t\t\t\tSHM_Vertex3fv(v4);\n\t\t\t\tSHM_Vertex3fv(v3);\n\t\t\t}\n\t\t\tedge[firstedge].count=0;\n\n\t\t\tfirstedge = edge[firstedge].next;\n\t\t}\n\t\tSHM_End();\n\t\tbreak;\n\t}\n\n\treturn SHM_FinishShadowMesh(dl);\n}\n\n\n\n\n\n\n\n\n\n\n\n\n\nstatic qboolean Sh_VisOverlaps(qbyte *v1, qbyte *v2)\n{\n\tint i, m;\n\tif (!v2 || !v1)\n\t\treturn true;\n\tm = (cl.worldmodel->numclusters+7)>>3;\n\n\tfor (i=(m&~3) ; i<m ; i++)\n\t{\n\t\tif (v1[i] & v2[i])\n\t\t\treturn true;\n\t}\n\tm>>=2;\n\tfor (i=0 ; i<m ; i++)\n\t{\n\t\tif (((unsigned int*)v1)[i] & ((unsigned int*)v2)[i])\n\t\t\treturn true;\n\t}\n\treturn false;\n}\n\n#define Sh_LeafInView Sh_VisOverlaps\n\n\n/*\nstatic void Sh_Scissor (srect_t *r)\n{\n\t//float xs = vid.pixelwidth / (float)vid.width, ys = vid.pixelheight / (float)vid.height;\n\tswitch(qrenderer)\n\t{\n\tcase QR_NONE:\n\tcase QR_SOFTWARE:\n\tcase QR_DIRECT3D11:\n\tdefault:\n\t\tbreak;\n\n\tcase QR_OPENGL:\n#ifdef GLQUAKE\n\t\tqglScissor(\n\t\t\tfloor(r_refdef.pxrect.x + r->x*r_refdef.pxrect.width),\n\t\t\tfloor((r_refdef.pxrect.y + r->y*r_refdef.pxrect.height) - r_refdef.pxrect.height),\n\t\t\tceil(r->width * r_refdef.pxrect.width),\n\t\t\tceil(r->height * r_refdef.pxrect.height));\n\t\tqglEnable(GL_SCISSOR_TEST);\n\n\t\tif (qglDepthBoundsEXT)\n\t\t{\n\t\t\tqglDepthBoundsEXT(r->dmin, r->dmax);\n\t\t\tqglEnable(GL_DEPTH_BOUNDS_TEST_EXT);\n\t\t}\n#endif\n\t\tbreak;\n\tcase QR_DIRECT3D9:\n#ifdef D3D9QUAKE\n\t\t{\n\t\t\tRECT rect;\n\t\t\trect.left = r->x;\n\t\t\trect.right = r->x + r->width;\n\t\t\trect.top = r->y;\n\t\t\trect.bottom = r->y + r->height;\n\t\t\tIDirect3DDevice9_SetScissorRect(pD3DDev9, &rect);\n\t\t}\n#endif\n\t\tbreak;\n\t}\n}\nstatic void Sh_ScissorOff (void)\n{\n\tswitch(qrenderer)\n\t{\n\tdefault:\n\t\tbreak;\n\tcase QR_OPENGL:\n#ifdef GLQUAKE\n\t\tqglDisable(GL_SCISSOR_TEST);\n\t\tif (qglDepthBoundsEXT)\n\t\t\tqglDisable(GL_DEPTH_BOUNDS_TEST_EXT);\n#endif\n\t\tbreak;\n\tcase QR_DIRECT3D9:\n#ifdef D3D9QUAKE\n#endif\n\t\tbreak;\n\t}\n}\n*/\n#if 0\nstatic qboolean Sh_ScissorForSphere(vec3_t center, float radius, vrect_t *rect)\n{\n\t/*return false to say that its fully offscreen*/\n\n\tfloat v[4], tempv[4];\n\tint i;\n\tvrect_t r;\n\n\trect->x = 0;\n\trect->y = 0;\n\trect->width = vid.pixelwidth;\n\trect->height = vid.pixelheight;\n\n\n/*\n\tfor (i = 0; i < 4; i++)\n\t{\n\t\tv[3] = 1;\n\t\tVectorMA(center, radius, frustum[i].normal, v);\n\n\t\ttempv[0] = r_refdef.m_view[0]*v[0] + r_refdef.m_view[4]*v[1] + r_refdef.m_view[8]*v[2] + r_refdef.m_view[12]*v[3];\n\t\ttempv[1] = r_refdef.m_view[1]*v[0] + r_refdef.m_view[5]*v[1] + r_refdef.m_view[9]*v[2] + r_refdef.m_view[13]*v[3];\n\t\ttempv[2] = r_refdef.m_view[2]*v[0] + r_refdef.m_view[6]*v[1] + r_refdef.m_view[10]*v[2] + r_refdef.m_view[14]*v[3];\n\t\ttempv[3] = r_refdef.m_view[3]*v[0] + r_refdef.m_view[7]*v[1] + r_refdef.m_view[11]*v[2] + r_refdef.m_view[15]*v[3];\n\n\t\tproduct[0] = r_refdef.m_projection[0]*tempv[0] + r_refdef.m_projection[4]*tempv[1] + r_refdef.m_projection[8]*tempv[2] + r_refdef.m_projection[12]*tempv[3];\n\t\tproduct[1] = r_refdef.m_projection[1]*tempv[0] + r_refdef.m_projection[5]*tempv[1] + r_refdef.m_projection[9]*tempv[2] + r_refdef.m_projection[13]*tempv[3];\n\t\tproduct[2] = r_refdef.m_projection[2]*tempv[0] + r_refdef.m_projection[6]*tempv[1] + r_refdef.m_projection[10]*tempv[2] + r_refdef.m_projection[14]*tempv[3];\n\t\tproduct[3] = r_refdef.m_projection[3]*tempv[0] + r_refdef.m_projection[7]*tempv[1] + r_refdef.m_projection[11]*tempv[2] + r_refdef.m_projection[15]*tempv[3];\n\n\t\tv[0] /= v[3];\n\t\tv[1] /= v[3];\n\t\tv[2] /= v[3];\n\n\t\tout[0] = (1+v[0])/2;\n\t\tout[1] = (1+v[1])/2;\n\t\tout[2] = (1+v[2])/2;\n\n\t\tr.x \n\t}\n*/\n\treturn false;\n}\n#endif\n\n#define BoxesOverlap(a,b,c,d) ((a)[0] <= (d)[0] && (b)[0] >= (c)[0] && (a)[1] <= (d)[1] && (b)[1] >= (c)[1] && (a)[2] <= (d)[2] && (b)[2] >= (c)[2])\nstatic qboolean Sh_ScissorForBox(vec3_t mins, vec3_t maxs, srect_t *r)\n{\n\tstatic const int edge[12][2] =\n\t{\n\t\t{0, 1}, {0, 2}, {1, 3}, {2, 3},\n\t\t{4, 5}, {4, 6}, {5, 7}, {6, 7},\n\t\t{0, 4}, {1, 5}, {2, 6}, {3, 7}\n\t};\n\t//the box is a simple cube.\n\t//clip each vert to the near clip plane\n\t//insert a replacement vertex for edges that cross the nearclip plane where it crosses\n\t//calc the scissor rect from projecting the verts that survived, plus the clipped edge ones.\n\tfloat ncpdist;\n\tfloat dist[8];\n\tint sign[8];\n\tvec4_t vert[20];\n\tvec3_t p[8];\n\tint numverts = 0, i, v1, v2;\n\tvec4_t v,tv;\n\tfloat frac;\n\tfloat x,x1,x2,y,y1,y2;\n\tdouble z, z1, z2;\n\n\tr->x = 0;\n\tr->y = 0;\n\tr->width = 1;\n\tr->height = 1;\n\tr->dmin = 0;\n\tr->dmax = 1;\n\tif (!r_shadow_scissor.ival)\n\t{\n\t\tr->x = 0;\n\t\tr->y = 0;\n\t\tr->width  = 1;\n\t\tr->height = 1;\n\t\treturn false;\n\t}\n\t/*if view is inside the box, then skip this maths*/\n//\tif (BoxesOverlap(r_refdef.vieworg, r_refdef.vieworg, mins, maxs))\n//\t{\n//\t\treturn false;\n//\t}\n\n\tncpdist = DotProduct(r_refdef.vieworg, vpn) + r_refdef.mindist;\n\n\tfor (i = 0; i < 8; i++)\n\t{\n\t\tp[i][0] = (i & 1) ? mins[0] : maxs[0];\n\t\tp[i][1] = (i & 2) ? mins[1] : maxs[1];\n\t\tp[i][2] = (i & 4) ? mins[2] : maxs[2];\n\t\tdist[i] = ncpdist - DotProduct(p[i], vpn);\n\t\tsign[i] = (dist[i] > 0);\n\t\tif (!sign[i])\n\t\t{\n\t\t\tVectorCopy(p[i], vert[numverts]);\n\t\t\tnumverts++;\n\t\t}\n\t}\n\n\t/*fully clipped by near plane*/\n\tif (!numverts)\n\t\treturn true;\n\n\tif (numverts != 8)\n\t{\n\t\t/*crosses near clip plane somewhere*/\n\t\tfor (i = 0; i < 12; i++)\n\t\t{\n\t\t\tv1 = edge[i][0];\n\t\t\tv2 = edge[i][1];\n\t\t\tif (sign[v1] != sign[v2])\n\t\t\t{\n\t\t\t\tfrac = dist[v1] / (dist[v1] - dist[v2]);\n\t\t\t\tVectorInterpolate(p[v1], frac, p[v2], vert[numverts]);\n\t\t\t\tnumverts++;\n\t\t\t}\n\t\t}\n\t}\n\tx1 = y1 = z1 = 1;\n\tx2 = y2 = z2 = -1;\n\t/*transform each vert to get the screen pos*/\n\tfor (i = 0; i < numverts; i++)\n\t{\n\t\tvert[i][3] = 1;\n\t\tMatrix4x4_CM_Transform4(r_refdef.m_view, vert[i], tv); \n\t\tMatrix4x4_CM_Transform4(r_refdef.m_projection_std, tv, v);\n\n\t\tx = v[0] / v[3];\n\t\ty = v[1] / v[3];\n\t\tz = (double)v[2] / v[3];\n\t\tif (x < x1) x1 = x;\n\t\tif (x > x2) x2 = x;\n\t\tif (y < y1) y1 = y;\n\t\tif (y > y2) y2 = y;\n\t\tif (z < z1) z1 = z;\n\t\tif (z > z2) z2 = z;\n\t}\n\tx1 = (1+x1) / 2;\n\tx2 = (1+x2) / 2;\n\ty1 = (1+y1) / 2;\n\ty2 = (1+y2) / 2;\n\tz1 = (1+z1) / 2;\n\tz2 = (1+z2) / 2;\n\n\tif (x1 < 0)\n\t\tx1 = 0;\n\tif (y1 < 0)\n\t\ty1 = 0;\n\tif (x2 < 0)\n\t\tx2 = 0;\n\tif (y2 < 0)\n\t\ty2 = 0;\n\tif (x1 > 1)\n\t\tx1 = 1;\n\tif (y1 > 1)\n\t\ty1 = 1;\n\tif (x2 > 1)\n\t\tx2 = 1;\n\tif (y2 > 1)\n\t\ty2 = 1;\n\tr->x = x1;\n\tr->y = y1;\n\tr->width  = x2 - r->x;\n\tr->height = y2 - r->y;\n\tif (r->width == 0 || r->height == 0)\n\t\treturn true;\t//meh\n\n\tr->dmin = z1;\n\tr->dmax = z2;\n\treturn false;\n}\n\n#if 0\nstatic qboolean Sh_ScissorForBox(vec3_t mins, vec3_t maxs, vrect_t *r)\n{\n\tint i, ix1, iy1, ix2, iy2;\n\tfloat x1, y1, x2, y2, x, y, f;\n\tvec3_t smins, smaxs;\n\tvec4_t v, v2;\n\n\tr->x = 0;\n\tr->y = 0;\n\tr->width = vid.pixelwidth;\n\tr->height = vid.pixelheight;\n\tif (0)//!r_shadow_scissor.integer)\n\t{\n\t\treturn false;\n\t}\n\t// if view is inside the box, just say yes it's fully visible\n\tif (BoxesOverlap(r_refdef.vieworg, r_refdef.vieworg, mins, maxs))\n\t{\n\t\treturn false;\n\t}\n\tfor (i = 0;i < 3;i++)\n\t{\n\t\tif (vpn[i] >= 0)\n\t\t{\n\t\t\tv[i] = mins[i];\n\t\t\tv2[i] = maxs[i];\n\t\t}\n\t\telse\n\t\t{\n\t\t\tv[i] = maxs[i];\n\t\t\tv2[i] = mins[i];\n\t\t}\n\t}\n\tf = DotProduct(vpn, r_refdef.vieworg);\n\tif (DotProduct(vpn, v2) <= f)\n\t{\n\t\t// entirely behind nearclip plane, entirely obscured\n\t\treturn true;\n\t}\n\tif (DotProduct(vpn, v) >= f)\n\t{\n\t\t// entirely infront of nearclip plane\n\t\tx1 = y1 = x2 = y2 = 0;\n\t\tfor (i = 0;i < 8;i++)\n\t\t{\n\t\t\tv[0] = (i & 1) ? mins[0] : maxs[0];\n\t\t\tv[1] = (i & 2) ? mins[1] : maxs[1];\n\t\t\tv[2] = (i & 4) ? mins[2] : maxs[2];\n\t\t\tv[3] = 1.0f;\n\t\t\tMatrix4x4_CM_Project(v, v2, r_refdef.viewangles, r_refdef.vieworg, r_refdef.fov_x, r_refdef.fov_y);\n\t\t\tv2[0]*=vid.pixelwidth;\n\t\t\tv2[1]*=vid.pixelheight;\n//\t\t\tGL_TransformToScreen(v, v2);\n\t\t\t//Con_Printf(\"%.3f %.3f %.3f %.3f transformed to %.3f %.3f %.3f %.3f\\n\", v[0], v[1], v[2], v[3], v2[0], v2[1], v2[2], v2[3]);\n\t\t\tx = v2[0];\n\t\t\ty = v2[1];\n\t\t\tif (i)\n\t\t\t{\n\t\t\t\tif (x1 > x) x1 = x;\n\t\t\t\tif (x2 < x) x2 = x;\n\t\t\t\tif (y1 > y) y1 = y;\n\t\t\t\tif (y2 < y) y2 = y;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tx1 = x2 = x;\n\t\t\t\ty1 = y2 = y;\n\t\t\t}\n\t\t}\n\t}\n\telse\n\t{\n\t\t// clipped by nearclip plane\n\t\t// this is nasty and crude...\n\t\t// create viewspace bbox\n\t\ti = 0;\n\t\t/*unrolled the first iteration to avoid warnings*/\n\t\tv[0] = ((i & 1) ? mins[0] : maxs[0]) - r_refdef.vieworg[0];\n\t\tv[1] = ((i & 2) ? mins[1] : maxs[1]) - r_refdef.vieworg[1];\n\t\tv[2] = ((i & 4) ? mins[2] : maxs[2]) - r_refdef.vieworg[2];\n\t\tv2[0] = DotProduct(v, vright);\n\t\tv2[1] = DotProduct(v, vup);\n\t\tv2[2] = DotProduct(v, vpn);\n\t\tsmins[0] = smaxs[0] = v2[0];\n\t\tsmins[1] = smaxs[1] = v2[1];\n\t\tsmins[2] = smaxs[2] = v2[2];\n\t\tfor (i = 1;i < 8;i++)\n\t\t{\n\t\t\tv[0] = ((i & 1) ? mins[0] : maxs[0]) - r_refdef.vieworg[0];\n\t\t\tv[1] = ((i & 2) ? mins[1] : maxs[1]) - r_refdef.vieworg[1];\n\t\t\tv[2] = ((i & 4) ? mins[2] : maxs[2]) - r_refdef.vieworg[2];\n\t\t\tv2[0] = DotProduct(v, vright);\n\t\t\tv2[1] = DotProduct(v, vup);\n\t\t\tv2[2] = DotProduct(v, vpn);\n\t\t\tif (smins[0] > v2[0]) smins[0] = v2[0];\n\t\t\tif (smaxs[0] < v2[0]) smaxs[0] = v2[0];\n\t\t\tif (smins[1] > v2[1]) smins[1] = v2[1];\n\t\t\tif (smaxs[1] < v2[1]) smaxs[1] = v2[1];\n\t\t\tif (smins[2] > v2[2]) smins[2] = v2[2];\n\t\t\tif (smaxs[2] < v2[2]) smaxs[2] = v2[2];\n\t\t}\n\t\t// now we have a bbox in viewspace\n\t\t// clip it to the view plane\n\t\tif (smins[2] < 1)\n\t\t\tsmins[2] = 1;\n\t\t// return true if that culled the box\n\t\tif (smins[2] >= smaxs[2])\n\t\t\treturn true;\n\t\t// ok some of it is infront of the view, transform each corner back to\n\t\t// worldspace and then to screenspace and make screen rect\n\t\t// initialize these variables just to avoid compiler warnings\n\t\tx1 = y1 = x2 = y2 = 0;\n\t\tfor (i = 0;i < 8;i++)\n\t\t{\n\t\t\tv2[0] = (i & 1) ? smins[0] : smaxs[0];\n\t\t\tv2[1] = (i & 2) ? smins[1] : smaxs[1];\n\t\t\tv2[2] = (i & 4) ? smins[2] : smaxs[2];\n\t\t\tv[0] = v2[0] * vright[0] + v2[1] * vup[0] + v2[2] * vpn[0] + r_refdef.vieworg[0];\n\t\t\tv[1] = v2[0] * vright[1] + v2[1] * vup[1] + v2[2] * vpn[1] + r_refdef.vieworg[1];\n\t\t\tv[2] = v2[0] * vright[2] + v2[1] * vup[2] + v2[2] * vpn[2] + r_refdef.vieworg[2];\n\t\t\tv[3] = 1.0f;\n\t\t\tMatrix4x4_CM_Project(v, v2, r_refdef.viewangles, r_refdef.vieworg, r_refdef.fov_x, r_refdef.fov_y);\n\t\t\tv2[0]*=vid.pixelwidth;\n\t\t\tv2[1]*=vid.pixelheight;\n\t\t\t//Con_Printf(\"%.3f %.3f %.3f %.3f transformed to %.3f %.3f %.3f %.3f\\n\", v[0], v[1], v[2], v[3], v2[0], v2[1], v2[2], v2[3]);\n\t\t\tx = v2[0];\n\t\t\ty = v2[1];\n\t\t\tif (i)\n\t\t\t{\n\t\t\t\tif (x1 > x) x1 = x;\n\t\t\t\tif (x2 < x) x2 = x;\n\t\t\t\tif (y1 > y) y1 = y;\n\t\t\t\tif (y2 < y) y2 = y;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tx1 = x2 = x;\n\t\t\t\ty1 = y2 = y;\n\t\t\t}\n\t\t}\n#if 1\n\t\t// this code doesn't handle boxes with any points behind view properly\n\t\tx1 = 1000;x2 = -1000;\n\t\ty1 = 1000;y2 = -1000;\n\t\tfor (i = 0;i < 8;i++)\n\t\t{\n\t\t\tv[0] = (i & 1) ? mins[0] : maxs[0];\n\t\t\tv[1] = (i & 2) ? mins[1] : maxs[1];\n\t\t\tv[2] = (i & 4) ? mins[2] : maxs[2];\n\t\t\tv[3] = 1.0f;\n\t\t\tMatrix4x4_CM_Project(v, v2, r_refdef.viewangles, r_refdef.vieworg, r_refdef.fov_x, r_refdef.fov_y);\n\t\t\tv2[0]*=vid.pixelwidth;\n\t\t\tv2[1]*=vid.pixelheight;\n\t\t\t//Con_Printf(\"%.3f %.3f %.3f %.3f transformed to %.3f %.3f %.3f %.3f\\n\", v[0], v[1], v[2], v[3], v2[0], v2[1], v2[2], v2[3]);\n\t\t\tif (v2[2] > 0)\n\t\t\t{\n\t\t\t\tx = v2[0];\n\t\t\t\ty = v2[1];\n\n\t\t\t\tif (x1 > x) x1 = x;\n\t\t\t\tif (x2 < x) x2 = x;\n\t\t\t\tif (y1 > y) y1 = y;\n\t\t\t\tif (y2 < y) y2 = y;\n\t\t\t}\n\t\t}\n#endif\n\t}\n\tix1 = x1 - 1.0f;\n\tiy1 = y1 - 1.0f;\n\tix2 = x2 + 1.0f;\n\tiy2 = y2 + 1.0f;\n\t//Con_Printf(\"%f %f %f %f\\n\", x1, y1, x2, y2);\n\tif (ix1 < r->x) ix1 = r->x;\n\tif (iy1 < r->y) iy1 = r->y;\n\tif (ix2 > r->x + r->width) ix2 = r->x + r->width;\n\tif (iy2 > r->y + r->height) iy2 = r->y + r->height;\n\tif (ix2 <= ix1 || iy2 <= iy1)\n\t\treturn true;\n\t// set up the scissor rectangle\n\t\n\tr->x = ix1;\n\tr->y = iy1;\n\tr->width = ix2 - ix1;\n\tr->height = iy2 - iy1;\n\treturn false;\n}\n#endif\n\nvoid D3D11BE_BeginShadowmapFace(void);\n\n//determine the 5 bounding points of a shadowmap light projection side\n//needs to match Sh_GenShadowFace\nstatic void Sh_LightFrustumPlanes(dlight_t *l, vec3_t axis[3], vec4_t *planes, int face)\n{\n\tvec3_t tmp;\n\tint axis0, axis1, axis2;\n\tint dir;\n\tint i;\n\t//+x,+y,+z,-x,-y,-z\n\taxis0 = (face+0)%3;\t//our major axis\n\taxis1 = (face+1)%3;\n\taxis2 = (face+2)%3;\n\tdir = (face >= 3)?-1:1;\n\n\t//center point is always the same\n\tVectorCopy(l->origin, planes[4]);\n\tVectorScale(axis[axis0], dir, planes[4]);\n\tVectorNormalize(planes[4]);\n\tplanes[4][3] = (l->nearclip?l->nearclip:r_shadow_shadowmapping_nearclip.value) + DotProduct(planes[4], l->origin);\n\n\tfor (i = 0; i < 4; i++)\n\t{\n\t\tVectorScale(axis[axis0], dir, tmp);\n\t\tVectorMA(tmp,\t\t((i&1)?1:-1), axis[axis1], tmp);\n\t\tVectorMA(tmp,\t\t((i&2)?1:-1), axis[axis2], planes[i]);\n\t\tVectorNormalize(planes[i]);\n\t\tplanes[i][3] = DotProduct(planes[i], l->origin);\n\t}\n}\n\n//culling for the face happens in the caller.\n//these faces should thus match Sh_LightFrustumPlanes\nstatic void Sh_GenShadowFace(dlight_t *l, vec3_t axis[3], int lighttype, shadowmesh_t *smesh, int face, int smsize, int txsize, float proj[16], const qbyte *lightpvs)\n{\n\tvec3_t t1,t2,t3;\n\ttexture_t *tex;\n\tint tno;\n\n/*\tif (face >= 3)\n\t\tface -= 3;\n\telse\n\t\tface += 3;\n*/\n\tswitch(face)\n\t{\n\tcase 0:\n\t\t//down\n\t\tVectorCopy(axis[0], t1);\n\t\tVectorCopy(axis[1], t2);\n\t\tVectorCopy(axis[2], t3);\n\t\tMatrix4x4_CM_LightMatrixFromAxis(r_refdef.m_view, t1, t2, t3, l->origin);\n\t\tr_refdef.flipcull = 0;\n\t\tbreak;\n\tcase 1:\n\t\t//back\n\t\tVectorCopy(axis[2], t1);\n\t\tVectorCopy(axis[1], t2);\n\t\tVectorCopy(axis[0], t3);\n\t\tMatrix4x4_CM_LightMatrixFromAxis(r_refdef.m_view, t1, t2, t3, l->origin);\n\t\tr_refdef.flipcull = SHADER_CULL_FLIP;\n\t\tbreak;\n\tcase 2:\n\t\t//right\n\t\tVectorCopy(axis[0], t1);\n\t\tVectorCopy(axis[2], t2);\n\t\tVectorCopy(axis[1], t3);\n\t\tMatrix4x4_CM_LightMatrixFromAxis(r_refdef.m_view, t1, t2, t3, l->origin);\n\t\tr_refdef.flipcull = SHADER_CULL_FLIP;\n\t\tbreak;\n\tcase 3:\n\t\t//up\n\t\tVectorCopy(axis[0], t1);\n\t\tVectorCopy(axis[1], t2);\n\t\tVectorCopy(axis[2], t3);\n\t\tVectorNegate(t3, t3);\n\t\tMatrix4x4_CM_LightMatrixFromAxis(r_refdef.m_view, t1, t2, t3, l->origin);\n\t\tr_refdef.flipcull = SHADER_CULL_FLIP;\n\t\tbreak;\n\tcase 4:\n\t\t//forward\n\t\tVectorCopy(axis[2], t1);\n\t\tVectorCopy(axis[1], t2);\n\t\tVectorCopy(axis[0], t3);\n\t\tVectorNegate(t3, t3);\n\t\tMatrix4x4_CM_LightMatrixFromAxis(r_refdef.m_view, t1, t2, t3, l->origin);\n\t\tr_refdef.flipcull = 0;\n\t\tbreak;\n\tcase 5:\n\t\t//left\n\t\tVectorCopy(axis[0], t1);\n\t\tVectorCopy(axis[2], t2);\n\t\tVectorCopy(axis[1], t3);\n\t\tVectorNegate(t3, t3);\n\t\tMatrix4x4_CM_LightMatrixFromAxis(r_refdef.m_view, t1, t2, t3, l->origin);\n\t\tr_refdef.flipcull = 0;\n\t\tbreak;\n\t}\n\n\tif (lighttype & (LSHADER_SPOT|LSHADER_ORTHO))\n\t{\n\t\tr_refdef.pxrect.x = (txsize-smsize)/2;\n\t\tr_refdef.pxrect.width = smsize;\n\t\tr_refdef.pxrect.height = smsize;\n\t\tr_refdef.pxrect.y = (txsize-smsize)/2;\n\t\tr_refdef.pxrect.maxheight = txsize;\n\t}\n\telse\n\t{\n\t\tr_refdef.pxrect.x = (face%3 * txsize) + (txsize-smsize)/2;\n\t\tr_refdef.pxrect.width = smsize;\n\t\tr_refdef.pxrect.height = smsize;\n\t\tr_refdef.pxrect.y = (((face<3)*txsize) + (txsize-smsize)/2);\n\t\tr_refdef.pxrect.maxheight = txsize*2;\n\t}\n\n\tR_SetFrustum(proj, r_refdef.m_view);\n\n\tif (lighttype & LSHADER_ORTHO)\n\t\tr_refdef.frustum_numplanes = 4;\t//kill the near clip plane - we allow ANYTHING nearer through.\n\n\tif (lighttype & LSHADER_FAKESHADOWS)\n\t\tr_refdef.flipcull ^= SHADER_CULL_FLIP;\n\tr_refdef.colourmask = 0u;\n\n#ifdef SHADOWDBG_COLOURNOTDEPTH\n\tBE_SelectMode(BEM_STANDARD);\n#else\n\tBE_SelectMode(BEM_DEPTHONLY);\n#endif\n\tBE_SelectEntity(&r_worldentity);\n\n\tswitch(qrenderer)\n\t{\n#ifdef GLQUAKE\n\tcase QR_OPENGL:\n\t\tGL_ViewportUpdate();\n\t\tif (lighttype & LSHADER_ORTHO)\n\t\t\tqglEnable(GL_DEPTH_CLAMP_ARB);\n\t\tGL_CullFace(SHADER_CULL_FRONT);\n\t\tif (smesh)\n\t\t\tGLBE_RenderShadowBuffer(smesh->numverts, smesh->vefbo[0], smesh->verts, smesh->numindicies, smesh->vefbo[1], smesh->indicies);\n\t\tbreak;\n#endif\n#ifdef VKQUAKE\n\tcase QR_VULKAN:\n\t\t//FIXME: generate a single commandbuffer (requires full separation of viewprojection matrix)\n\t\tVKBE_BeginShadowmapFace();\n\t\tif (smesh)\n\t\t\tVKBE_RenderShadowBuffer(smesh->vkbuffer);\n\t\tbreak;\n#endif\n#ifdef D3D11QUAKE\n\tcase QR_DIRECT3D11:\n\t\t//opengl render targets are upside down - our code kinda assumes gl\n\t\tr_refdef.pxrect.y = r_refdef.pxrect.maxheight -(r_refdef.pxrect.y+r_refdef.pxrect.height);\n\t\tD3D11BE_BeginShadowmapFace();\n\t\tif (smesh)\n\t\t\tD3D11BE_RenderShadowBuffer(smesh->numverts, smesh->d3d11_vbuffer, smesh->numindicies, smesh->d3d11_ibuffer);\n\t\tbreak;\n#endif\n\tdefault:\n\t\t//FIXME: should be able to merge batches between textures+lightmaps.\n\t\tif (smesh)\n\t\tfor (tno = 0; tno < smesh->numbatches; tno++)\n\t\t{\n\t\t\tif (!smesh->batches[tno].count)\n\t\t\t\tcontinue;\n\t\t\ttex = cl.worldmodel->shadowbatches[tno].tex;\n\t\t\tif (tex->shader->flags & (SHADER_NOSHADOWS|SHADER_NODRAW))\t//FIXME: shadows not lights\n\t\t\t\tcontinue;\n\t\t\tBE_DrawMesh_List(tex->shader, smesh->batches[tno].count, smesh->batches[tno].s, cl.worldmodel->shadowbatches[tno].vbo, NULL, 0);\n\t\t}\n\t\tbreak;\n\t}\n\n\t//fixme: this walks through the entity lists up to 6 times per frame per entity.\n\tswitch(qrenderer)\n\t{\n\tdefault:\n\t\tbreak;\n#ifdef GLQUAKE\n\tcase QR_OPENGL:\n\t\tGLBE_BaseEntTextures(lightpvs, NULL);\n\n\t\tif (lighttype & LSHADER_ORTHO)\n\t\t\tqglDisable(GL_DEPTH_CLAMP_ARB);\n\t\tbreak;\n#endif\n#ifdef D3D9QUAKE\n\tcase QR_DIRECT3D9:\n\t\tD3D9BE_BaseEntTextures(lightpvs, NULL);\n\t\tbreak;\n#endif\n#ifdef D3D11QUAKE\n\tcase QR_DIRECT3D11:\n\t\tD3D11BE_BaseEntTextures(lightpvs, NULL);\n\t\tbreak;\n#endif\n#ifdef VKQUAKE\n\tcase QR_VULKAN:\n\t\tVKBE_BaseEntTextures(lightpvs, NULL);\n\t\tbreak;\n#endif\n\t}\n\n/*\n\t{\n\t\tint i;\n\t\tstatic float depth[SHADOWMAP_SIZE*SHADOWMAP_SIZE];\n\t\tqglReadPixels(0, 0, smsize, smsize,\n\t\t\tGL_DEPTH_COMPONENT, GL_FLOAT, depth);\n\t\tfor (i = SHADOWMAP_SIZE*SHADOWMAP_SIZE; i --> 0; )\n\t\t{\n\t\t\tif (depth[i] == 1)\n\t\t\t\t*((unsigned int*)depth+i) = 0;\n\t\t\telse\n\t\t\t\t*((unsigned int*)depth+i) = 0xff000000|((((unsigned char)(int)(depth[i]*128)))*0x10101);\n\t\t}\n\n\t\tqglTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,\n\t\t\tsmsize, smsize, 0,\n\t\t\tGL_RGBA, GL_UNSIGNED_BYTE, depth);\n\n\t\tqglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);\n\t\tqglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);\n\t\tqglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);\n\t\tqglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);\n\t}\n*/\n}\n\nqboolean Sh_GenShadowMap (dlight_t *l, int lighttype, vec3_t axis[3], qbyte *lvis, int smsize, int txsize)\n{\n\tint restorefbo = 0;\n\tint f,lf;\n\tfloat oprojs[16], oprojv[16], oview[16];\n\tpxrect_t oprect;\n\tshadowmesh_t *smesh;\n\tint sidevisible;\n\tint oldflip = r_refdef.flipcull;\n\tunsigned int oldcolourmask = r_refdef.colourmask;\n\tint oldexternalview = r_refdef.externalview;\n\tint twidth;\n\tint theight;\n\tint smapidx;\n\tuploadfmt_t fmt;\n\n\tif (r_shadow_shadowmapping_depthbits.ival >= 32 && sh_config.texfmt[PTI_DEPTH32])\n\t\tfmt = PTI_DEPTH32;\n\telse if (r_shadow_shadowmapping_depthbits.ival >= 24 && sh_config.texfmt[PTI_DEPTH24])\n\t\tfmt = PTI_DEPTH24;\n\telse if (r_shadow_shadowmapping_depthbits.ival >= 24 && sh_config.texfmt[PTI_DEPTH24_8])\n\t\tfmt = PTI_DEPTH24_8;\n\telse\n\t\tfmt = PTI_DEPTH16;\n\t(void)fmt;\n\n\tif (lighttype & (LSHADER_SPOT|LSHADER_ORTHO))\n\t{\t//spotlights only face forwards. which is side 4. which is annoying.\n\t\tf = 4;\n\t\tlf = f+1;\n\t\tsidevisible = 1<<f;\n\t\ttwidth = theight = txsize;\n\n\t\tif (lighttype & LSHADER_FAKESHADOWS)\n\t\t\tsmapidx = 2;\n\t\telse\n\t\t\tsmapidx = 1;\n\t}\n\telse\n\t{\n\t\tf = 0;\n\t\tlf = 6;\n\t\tsidevisible = (1<<6)-1;\n\t\ttwidth = txsize*3;\n\t\ttheight = txsize*2;\n\t\tsmapidx = 0;\n\t}\n\tif (R_CullSphere(l->origin, 0))\n\t{\t//if the light's center isn't onscreen, cull individual faces\n\t\t//FIXME: if the fov is < 90, we need to clip by the near lightplane first\n\t\tfor (; f < lf; f++)\n\t\t{\n\t\t\tvec4_t planes[5];\n\t\t\tfloat dist;\n\t\t\tint fp,lp;\n\t\t\tSh_LightFrustumPlanes(l, axis, planes, f);\n\t\t\tfor (fp = 0; fp < r_refdef.frustum_numplanes; fp++)\n\t\t\t{\n\t\t\t\tvec3_t nearest;\n\t\t\t\t//make a guess based upon the frustum plane\n\t\t\t\tVectorMA(l->origin, l->radius, r_refdef.frustum[fp].normal, nearest);\n\t\t\t\t//clip that point to the various planes\n\n\t\t\t\tfor(lp = 0; lp < 5; lp++)\n\t\t\t\t{\n\t\t\t\t\tdist = DotProduct(nearest, planes[lp]) - planes[lp][3];\n\t\t\t\t\tif (dist < 0)\n\t\t\t\t\t\tVectorMA(nearest, dist, planes[lp], nearest);\n\t\t\t\t}\n\n//\t\t\t\tP_RunParticleEffect(nearest, vec3_origin, 15, 1);\n\t\t\t\t//give up if the best point for any frustum plane is offscreen\n\t\t\t\tdist = DotProduct(r_refdef.frustum[fp].normal, nearest) - r_refdef.frustum[fp].dist;\n\t\t\t\tif (dist <= 0)\n\t\t\t\t\tbreak;\t\t\n\t\t\t}\n\t\t\tif (fp != r_refdef.frustum_numplanes)\n\t\t\t\tsidevisible &= ~(1u<<f);\n\t\t}\n\t}\n\n\t//if nothing is visible, then there's no point generating any shadowmaps at all...\n\tif (!sidevisible)\n\t\treturn false;\n\n\tmemcpy(oprojs, r_refdef.m_projection_std, sizeof(oprojs));\n\tmemcpy(oprojv, r_refdef.m_projection_view, sizeof(oprojv));\n\tmemcpy(oview, r_refdef.m_view, sizeof(oview));\n\toprect = r_refdef.pxrect;\n\tif (lighttype & LSHADER_FAKESHADOWS)\n\t\tsmesh = NULL;\n\telse\n\t\tsmesh = SHM_BuildShadowMesh(l, lvis, (lighttype & LSHADER_ORTHO)?SMT_ORTHO:SMT_SHADOWMAP);\n\n\tif (lighttype & LSHADER_SPOT)\n\t\tMatrix4x4_CM_Projection_Far(r_refdef.m_projection_std, l->fov, l->fov, l->nearclip?l->nearclip:r_shadow_shadowmapping_nearclip.value, l->radius, false);\n\telse if (lighttype & LSHADER_ORTHO)\n\t{\n\t\tfloat xmin = -l->radius;\n\t\tfloat ymin = -l->radius;\n\t\tfloat znear = -l->radius;\n\t\tfloat xmax = l->radius;\n\t\tfloat ymax = l->radius;\n\t\tfloat zfar = l->radius;\n\t\tMatrix4x4_CM_Orthographic(r_refdef.m_projection_std, xmin, xmax, ymax, ymin, znear, zfar);\n\t}\n\telse\n\t\tMatrix4x4_CM_Projection_Far(r_refdef.m_projection_std, 90, 90, l->nearclip?l->nearclip:r_shadow_shadowmapping_nearclip.value, l->radius, false);\n\n\tmemcpy(r_refdef.m_projection_view, r_refdef.m_projection_std, sizeof(r_refdef.m_projection_view));\n\n\tswitch(qrenderer)\n\t{\n\tdefault:\n\t\treturn false;\n#ifdef GLQUAKE\n\tcase QR_OPENGL:\n\t\tif (!GLBE_BeginShadowMap(smapidx, twidth, theight, fmt, &restorefbo))\n\t\t\treturn false;\n\t\tbreak;\n#endif\n#ifdef D3D11QUAKE\n\tcase QR_DIRECT3D11:\n\t\tif (!D3D11_BeginShadowMap(smapidx, twidth, theight))\n\t\t\treturn false;\n\t\tbreak;\n#endif\n\n#ifdef VKQUAKE\n\tcase QR_VULKAN:\n\t\tif (!VKBE_BeginShadowmap(smapidx, twidth, theight))\n\t\t\treturn false;\n\t\tbreak;\n#endif\n\t}\n\n\tr_refdef.externalview = true;\t//never any viewmodels\n\n\t/*generate faces*/\n\tfor (f = 0; f < 6; f++)\n\t{\n\t\tif (sidevisible & (1u<<f))\n\t\t{\n\t\t\tRQuantAdd(RQUANT_SHADOWSIDES, 1);\n\t\t\tSh_GenShadowFace(l, axis, lighttype, smesh, f, smsize, txsize, r_refdef.m_projection_std, lvis);\n\t\t}\n\t}\n\n\tmemcpy(r_refdef.m_view, oview, sizeof(r_refdef.m_view));\n\tmemcpy(r_refdef.m_projection_std, oprojs, sizeof(r_refdef.m_projection_std));\n\tmemcpy(r_refdef.m_projection_view, oprojv, sizeof(r_refdef.m_projection_view));\n\n\tr_refdef.pxrect = oprect;\n\n\tr_refdef.flipcull = oldflip;\n\tr_refdef.colourmask = oldcolourmask;\n\tr_refdef.externalview = oldexternalview;\n\tR_SetFrustum(r_refdef.m_projection_std, r_refdef.m_view);\n\n\tswitch(qrenderer)\n\t{\n#ifdef GLQUAKE\n\tcase QR_OPENGL:\n\t\t/*end framebuffer*/\n\t\tGLBE_EndShadowMap(restorefbo);\n\t\tGL_ViewportUpdate();\n\t\tbreak;\n#endif\n#ifdef D3D11QUAKE\n\tcase QR_DIRECT3D11:\n\t\tD3D11_EndShadowMap();\n\t\tD3D11BE_DoneShadows();\n\t\tbreak;\n#endif\n#ifdef VKQUAKE\n\tcase QR_VULKAN:\n\t\tVKBE_DoneShadows();\n\t\tbreak;\n#endif\n\tdefault:\n\t\t(void)restorefbo;\n\t\tbreak;\n\t}\n\n\treturn true;\n}\n\nqboolean Sh_GenerateShadowMap(dlight_t *l, int lighttype)\n{\n\tint smsize;\n\tqbyte *vvis = r_refdef.scenevis;\n\tqbyte *lvis;\n\tint texwidth, texheight;\n\t\n/*\tif (Sh_ScissorForBox(mins, maxs, &rect))\n\t{\n\t\tRQuantAdd(RQUANT_RTLIGHT_CULL_SCISSOR, 1);\n\t\treturn;\n\t}*/\n\n\tif (vvis)\n\t{\n\t\tif (!l->rebuildcache && l->worldshadowmesh)\n\t\t{\n\t\t\tlvis = l->worldshadowmesh->litleaves;\n\t\t\t//fixme: check head node first?\n\t\t\tif (!Sh_LeafInView(l->worldshadowmesh->litleaves, vvis))\n\t\t\t{\n\t\t\t\tRQuantAdd(RQUANT_RTLIGHT_CULL_PVS, 1);\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tint clus;\n\t\t\tclus = cl.worldmodel->funcs.ClusterForPoint(cl.worldmodel, l->origin, NULL);\n\t\t\tlvis = cl.worldmodel->funcs.ClusterPVS(cl.worldmodel, clus, &lvisb, PVM_FAST);\n\t\t\t//FIXME: surely we can use the phs for this?\n\n\t\t\tif (!Sh_VisOverlaps(lvis, vvis))\t//The two viewing areas do not intersect.\n\t\t\t{\n\t\t\t\tRQuantAdd(RQUANT_RTLIGHT_CULL_PVS, 1);\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t}\n\telse\n\t\tlvis = NULL;\n\n\n\tif (lighttype & (LSHADER_SPOT | LSHADER_ORTHO))\n\t\ttexwidth = texheight = smsize = SHADOWMAP_SIZE;\n\telse\n\t{\n\t\t//Stolen from DP. Actually, LH pasted it to me in IRC.\n\t\tvec3_t nearestpoint;\n\t\tvec3_t d;\n\t\tfloat distance, lodlinear;\n\t\tnearestpoint[0] = bound(l->origin[0]-l->radius, r_origin[0], l->origin[0]+l->radius);\n\t\tnearestpoint[1] = bound(l->origin[1]-l->radius, r_origin[1], l->origin[1]+l->radius);\n\t\tnearestpoint[2] = bound(l->origin[2]-l->radius, r_origin[2], l->origin[2]+l->radius);\n\t\tVectorSubtract(nearestpoint, r_origin, d);\n\t\tdistance = VectorLength(d);\n\t\tlodlinear = (l->radius * r_shadow_shadowmapping_precision.value) / sqrt(max(1.0f, distance / l->radius));\n\t\tsmsize = bound(16, lodlinear, SHADOWMAP_SIZE);\n\t\ttexwidth = smsize*3;\n\t\ttexheight = smsize*2;\n\t}\n\n\tswitch(qrenderer)\n\t{\n#ifdef GLQUAKE\n\tcase QR_OPENGL:\n\t\tGLBE_SetupForShadowMap(l, texwidth, texheight, (smsize-4) / (float)SHADOWMAP_SIZE);\n\t\tbreak;\n#endif\n#ifdef D3D11QUAKE\n\tcase QR_DIRECT3D11:\n\t\tD3D11BE_SetupForShadowMap(l, texwidth, texheight, (smsize-4) / (float)SHADOWMAP_SIZE);\n\t\tbreak;\n#endif\n#ifdef VKQUAKE\n\tcase QR_VULKAN:\n\t\tVKBE_SetupForShadowMap(l, texwidth, texheight, (smsize-4) / (float)SHADOWMAP_SIZE);\n\t\tbreak;\n#endif\n\tdefault:\n\t\t(void)texwidth;\n\t\t(void)texheight;\n\t\tbreak;\n\t}\n\n\t//fixme: light rotation\n\tif (!Sh_GenShadowMap(l, lighttype, l->axis, lvis, smsize, SHADOWMAP_SIZE))\n\t\treturn false;\t//didn't need to do anything\n\treturn true;\n}\n\n#ifdef _MSC_VER\n#define round(a) floor((a)+0.5)\n#endif\n\nvoid Sh_OrthoAlignToFrustum(dlight_t *dl, int smsize)\n{\n\tvec3_t neworg;\n\tdouble dot;\n\tdouble scale;\n\tint i;\n\t//there's 1 sample every dl->radius/(smsize*2)\n\t//fixme: fit to frustum\n\tVectorMA(r_origin, dl->radius/3, vpn, neworg);\n\tVectorMA(neworg, -r_shadows_focus.vec4[2], vpn, neworg);\n\tVectorMA(neworg, -r_shadows_focus.vec4[0], vright, neworg);\n\tVectorMA(neworg, -r_shadows_focus.vec4[1], vup, neworg);\n\tscale = dl->radius/(smsize*2);\n\tfor (i = 0; i < 3; i++)\n\t{\n\t\tdot = DotProduct_Double(neworg, dl->axis[i]);\n\t\tdot /= scale;\n\t\tdot = round(dot)-dot;\n\t\tdot *= scale;\n\t\tVectorMA(neworg, dot, dl->axis[i], neworg); //realign it on this axis.\n\t}\n\tVectorCopy(neworg, dl->origin);\n}\nqboolean r_fakeshadows;\nstatic dlight_t r_fakelight;\nvoid Sh_GenerateFakeShadows(void)\t//generates shadowmaps and selects the dlight, but does not actually render any lighting. the lightmapped-wall etc glsl must filter by itself if it wants to accept shadows.\n{\n\tdlight_t *l = &r_fakelight;\n\tvec3_t mins, maxs;\n\tsrect_t rect;\n\tint smsize;\n\tint texwidth, texheight;\n\n\tif (*r_shadows_throwdirection.string)\n\t\tVectorCopy(r_shadows_throwdirection.vec4, l->axis[0]);\n\telse\n\t\tVectorNegate(r_sun_dir.vec4, l->axis[0]);\n\tVectorNormalize(l->axis[0]);\n\tVectorVectors(l->axis[0], l->axis[1], l->axis[2]);\n\tVectorNegate(l->axis[1], l->axis[1]);\n\n\tsmsize = SHADOWMAP_SIZE*4;\t//spot lights or ortho lights can just use the full thing.\n\tSh_OrthoAlignToFrustum(l, smsize);\n\tl->rebuildcache = true;\n\n\tl->radius = r_shadows_fakedistance.value;\n\tl->flags = LFLAG_SHADOWMAP|LFLAG_ORTHO;\n\n\tif (R_CullSphere(l->origin, l->radius))\n\t{\n\t\tRQuantAdd(RQUANT_RTLIGHT_CULL_FRUSTUM, 1);\n\t\treturn;\t//this should be the more common case\n\t}\n\n\tmins[0] = l->origin[0] - l->radius;\n\tmins[1] = l->origin[1] - l->radius;\n\tmins[2] = l->origin[2] - l->radius;\n\n\tmaxs[0] = l->origin[0] + l->radius;\n\tmaxs[1] = l->origin[1] + l->radius;\n\tmaxs[2] = l->origin[2] + l->radius;\n\n\tif (Sh_ScissorForBox(mins, maxs, &rect))\n\t{\n\t\tRQuantAdd(RQUANT_RTLIGHT_CULL_SCISSOR, 1);\n\t\treturn;\n\t}\n\n\ttexwidth = smsize;\n\ttexheight = smsize;\n\n\tswitch(qrenderer)\n\t{\n#ifdef GLQUAKE\n\tcase QR_OPENGL:\n\t\tGLBE_SetupForShadowMap(l, texwidth, texheight, (smsize) / (float)texwidth);\n\t\tbreak;\n#endif\n#ifdef D3D11QUAKE\n\tcase QR_DIRECT3D11:\n\t\tD3D11BE_SetupForShadowMap(l, texwidth, texheight, (smsize) / (float)texwidth);\n\t\tbreak;\n#endif\n#ifdef VKQUAKE\n\tcase QR_VULKAN:\n\t\tVKBE_SetupForShadowMap(l, texwidth, texheight, (smsize) / (float)texwidth);\n\t\tbreak;\n#endif\n\tdefault:\n\t\t(void)texwidth;\n\t\t(void)texheight;\n\t\tbreak;\n\t}\n\n\tif (!BE_SelectDLight(l, vec3_origin, l->axis, LSHADER_SMAP|LSHADER_ORTHO))\n\t\treturn;\n\tSh_GenShadowMap(l, LSHADER_SMAP|LSHADER_ORTHO|LSHADER_FAKESHADOWS, l->axis, NULL, smsize, texwidth);\n}\n\nstatic void Sh_DrawShadowMapLight(dlight_t *l, vec3_t colour, vec3_t axis[3], qbyte *vvis)\n{\n\tvec3_t mins, maxs;\n\tqbyte *lvis;\n\tsrect_t rect;\n\tint smsize;\n\tint lighttype;\n\tint texwidth, texheight;\n\n\tif (l->fov != 0)\n\t\tlighttype = LSHADER_SMAP|LSHADER_SPOT;\n#ifdef LFLAG_ORTHO\n\telse if (l->flags & LFLAG_ORTHO)\n\t\tlighttype = LSHADER_SMAP|LSHADER_ORTHO;\n#endif\n\telse\n\t\tlighttype = LSHADER_SMAP;\n\n\tif (R_CullSphere(l->origin, l->radius))\n\t{\n\t\tRQuantAdd(RQUANT_RTLIGHT_CULL_FRUSTUM, 1);\n\t\treturn;\t//this should be the more common case\n\t}\n\n\tmins[0] = l->origin[0] - l->radius;\n\tmins[1] = l->origin[1] - l->radius;\n\tmins[2] = l->origin[2] - l->radius;\n\n\tmaxs[0] = l->origin[0] + l->radius;\n\tmaxs[1] = l->origin[1] + l->radius;\n\tmaxs[2] = l->origin[2] + l->radius;\n\n\tif (Sh_ScissorForBox(mins, maxs, &rect))\n\t{\n\t\tRQuantAdd(RQUANT_RTLIGHT_CULL_SCISSOR, 1);\n\t\treturn;\n\t}\n\n\tif (vvis)\n\t{\n\t\tif (!l->rebuildcache && l->worldshadowmesh)\n\t\t{\n\t\t\tlvis = l->worldshadowmesh->litleaves;\n\t\t\t//fixme: check head node first?\n\t\t\tif (!Sh_LeafInView(l->worldshadowmesh->litleaves, vvis))\n\t\t\t{\n\t\t\t\tRQuantAdd(RQUANT_RTLIGHT_CULL_PVS, 1);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tint clus;\n\t\t\tclus = cl.worldmodel->funcs.ClusterForPoint(cl.worldmodel, l->origin, NULL);\n\t\t\tlvis = cl.worldmodel->funcs.ClusterPVS(cl.worldmodel, clus, &lvisb, PVM_FAST);\n\t\t\t//FIXME: surely we can use the phs for this?\n\t\t\tif (cl.worldmodel->funcs.ClustersInSphere)\n\t\t\t\tlvis = cl.worldmodel->funcs.ClustersInSphere(cl.worldmodel, l->origin, l->radius, &lvisb2, lvis);\n\t\t\t//FIXME: check areas\n\t\t\tif (!Sh_VisOverlaps(lvis, vvis))\t//The two viewing areas do not intersect.\n\t\t\t{\n\t\t\t\tRQuantAdd(RQUANT_RTLIGHT_CULL_PVS, 1);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n\telse\n\t\tlvis = NULL;\n\n\tif (lighttype & LSHADER_SPOT)\n\t{\n\t\tsmsize = SHADOWMAP_SIZE;\t//spot lights or ortho lights can just use the full thing.\n\t\ttexwidth = smsize;\n\t\ttexheight = smsize;\n\t}\n\telse if (lighttype & LSHADER_ORTHO)\n\t{\n\t\tsmsize = SHADOWMAP_SIZE;\t//spot lights or ortho lights can just use the full thing.\n\t\ttexwidth = smsize;\n\t\ttexheight = smsize;\n\t}\n\telse\n\t{\n\t\t//Stolen from DP. Actually, LH pasted it to me in IRC.\n\t\tvec3_t nearestpoint;\n\t\tvec3_t d;\n\t\tfloat distance, lodlinear;\n\t\tnearestpoint[0] = bound(l->origin[0]-l->radius, r_origin[0], l->origin[0]+l->radius);\n\t\tnearestpoint[1] = bound(l->origin[1]-l->radius, r_origin[1], l->origin[1]+l->radius);\n\t\tnearestpoint[2] = bound(l->origin[2]-l->radius, r_origin[2], l->origin[2]+l->radius);\n\t\tVectorSubtract(nearestpoint, r_origin, d);\n\t\tdistance = VectorLength(d);\n\t\tlodlinear = (l->radius * r_shadow_shadowmapping_precision.value) / sqrt(max(1.0f, distance / l->radius));\n\t\tsmsize = bound(16, lodlinear, SHADOWMAP_SIZE);\n\t\ttexwidth = smsize*3;\n\t\ttexheight = smsize*2;\n\t}\n\n\tswitch(qrenderer)\n\t{\n#ifdef GLQUAKE\n\tcase QR_OPENGL:\n\t\tGLBE_SetupForShadowMap(l, texwidth, texheight, (smsize-4) / (float)SHADOWMAP_SIZE);\n\t\tbreak;\n#endif\n#ifdef D3D11QUAKE\n\tcase QR_DIRECT3D11:\n\t\tD3D11BE_SetupForShadowMap(l, texwidth, texheight, (smsize-4) / (float)SHADOWMAP_SIZE);\n\t\tbreak;\n#endif\n#ifdef VKQUAKE\n\tcase QR_VULKAN:\n\t\tVKBE_SetupForShadowMap(l, texwidth, texheight, (smsize-4) / (float)SHADOWMAP_SIZE);\n\t\tbreak;\n#endif\n\tdefault:\n\t\t(void)texwidth;\n\t\t(void)texheight;\n\t\tbreak;\n\t}\n\n\tif (!BE_SelectDLight(l, colour, axis, lighttype))\n\t\treturn;\n\tif (!Sh_GenShadowMap(l, lighttype, axis, lvis, smsize, SHADOWMAP_SIZE))\n\t\treturn;\n\n\tRQuantAdd(RQUANT_RTLIGHT_DRAWN, 1);\n\n\t//may as well use scissors\n\tBE_Scissor(&rect);\n\n\tBE_SelectEntity(&r_worldentity);\n\n\tBE_SelectMode(BEM_LIGHT);\n\tSh_DrawEntLighting(l, colour, vvis);\n\n}\n\n\n/*\ndraws faces facing the light\nNote: Backend mode must have been selected in advance, as must the light to light from\n*/\nstatic void Sh_DrawEntLighting(dlight_t *light, vec3_t colour, qbyte *pvs)\n{\n\tint tno;\n\ttexture_t *tex;\n\tshader_t *shader;\n\tshadowmesh_t *sm;\n\n\tsm = light->worldshadowmesh;\n\tif (light->rebuildcache)\n\t\tsm = &sh_tempshmesh;\n\tif (sm)\n\t{\n\t\tfor (tno = 0; tno < sm->numbatches; tno++)\n\t\t{\n\t\t\tif (!sm->batches[tno].count)\n\t\t\t\tcontinue;\n\t\t\ttex = cl.worldmodel->shadowbatches[tno].tex;\n\t\t\tif (cl.worldmodel->fromgame == fg_quake2)\n\t\t\t\tshader = R_TextureAnimation_Q2(tex)->shader;\n\t\t\telse\n\t\t\t\tshader = R_TextureAnimation(false, tex)->shader;\n\t\t\tif (shader->flags & (SHADER_NODLIGHT|SHADER_NODRAW|SHADER_SKY))\n\t\t\t\tcontinue;\n\t\t\t//FIXME: it should be worth building a dedicated ebo, for static ones\n#ifdef GLQUAKE\n\t\t\tif (qrenderer == QR_OPENGL && sm->batches[tno].faceidxcount && !(shader->flags & SHADER_NEEDSARRAYS) && sm->havefaceebo)\n\t\t\t{\n\t\t\t\tmesh_t unimesh = {0};\n\t\t\t\tmesh_t *unimeshptr = &unimesh;\n\t\t\t\tvboarray_t oldidx = cl.worldmodel->shadowbatches[tno].vbo->indicies;\n\t\t\t\tunimesh.numindexes = sm->batches[tno].faceidxcount;\n\t\t\t\tunimesh.numvertexes = cl.worldmodel->shadowbatches[tno].vbo->vertcount;\n\t\t\t\tunimesh.vbofirstelement = sm->batches[tno].faceidxfirst;\n\t\t\t\tcl.worldmodel->shadowbatches[tno].vbo->indicies.gl.vbo = sm->vefbo[2];\n\t\t\t\tcl.worldmodel->shadowbatches[tno].vbo->indicies.gl.addr = NULL;\n\n\t\t\t\tBE_DrawMesh_List(shader, 1, &unimeshptr, cl.worldmodel->shadowbatches[tno].vbo, NULL, 0);\n\t\t\t\tcl.worldmodel->shadowbatches[tno].vbo->indicies = oldidx;\n\t\t\t}\n\t\t\telse\n#endif\n\t\t\t\tBE_DrawMesh_List(shader, sm->batches[tno].count, sm->batches[tno].s, cl.worldmodel->shadowbatches[tno].vbo, NULL, 0);\n\t\t\tRQuantAdd(RQUANT_LITFACES, sm->batches[tno].count);\n\t\t}\n\n\t\tswitch(qrenderer)\n\t\t{\n\t\tdefault:\n\t\t\tbreak;\n#ifdef GLQUAKE\n\t\tcase QR_OPENGL:\n\t\t\tGLBE_BaseEntTextures(pvs, NULL);\n\t\t\tbreak;\n#endif\n#ifdef VKQUAKE\n\t\tcase QR_VULKAN:\n\t\t\tVKBE_BaseEntTextures(pvs, NULL);\n\t\t\tbreak;\n#endif\n#ifdef D3D9QUAKE\n\t\tcase QR_DIRECT3D9:\n\t\t\tD3D9BE_BaseEntTextures(pvs, NULL);\n\t\t\tbreak;\n#endif\n#ifdef D3D11QUAKE\n\t\tcase QR_DIRECT3D11:\n\t\t\tD3D11BE_BaseEntTextures(pvs, NULL);\n\t\t\tbreak;\n#endif\n\t\t}\n\t}\n}\n\n#ifdef GLQUAKE\n/*Fixme: this is brute forced*/\n#ifdef warningmsg\n#pragma warningmsg(\"brush shadows are bruteforced\")\n#endif\nstatic void Sh_DrawBrushModelShadow(dlight_t *dl, entity_t *e)\n{\n\tint v;\n\tfloat *v1, *v2;\n\tvec3_t v3, v4;\n\tvec3_t lightorg;\n\n\tint i;\n\tmodel_t *model;\n\tmsurface_t *surf;\n\n\tif (qrenderer != QR_OPENGL)\n\t\treturn;\n\n\tif (BE_LightCullModel(e->origin, e->model))\n\t\treturn;\n\n\tRotateLightVector((void *)e->axis, e->origin, dl->origin, lightorg);\n\n\tBE_SelectEntity(e);\n\n\tGL_DeselectVAO();\n\tGL_SelectVBO(0);\n\tGL_SelectEBO(0);\n\tqglEnableClientState(GL_VERTEX_ARRAY);\n\n#ifdef BEF_PUSHDEPTH\n\tGLBE_PolyOffsetStencilShadow(r_pushdepth);\n#else\n\tGLBE_PolyOffsetStencilShadow();\n#endif\n\n\tmodel = e->model;\n\tsurf = model->surfaces+model->firstmodelsurface;\n\tfor (i = 0; i < model->nummodelsurfaces; i++, surf++)\n\t{\n\t\tif (surf->flags & SURF_PLANEBACK)\n\t\t{//inverted normal.\n\t\t\tif (DotProduct(surf->plane->normal, lightorg)-surf->plane->dist >= -0.1)\n\t\t\t\tcontinue;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (DotProduct(surf->plane->normal, lightorg)-surf->plane->dist <= 0.1)\n\t\t\t\tcontinue;\n\t\t}\n\n\t\tif (surf->flags & (SURF_DRAWALPHA | SURF_DRAWTILED))\n\t\t{\t// no shadows\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (!surf->mesh)\n\t\t\tcontinue;\n\n\t\t//front face\n\t\tqglVertexPointer(3, GL_FLOAT, sizeof(vecV_t), surf->mesh->xyz_array);\n\t\tqglDrawArrays(GL_POLYGON, 0, surf->mesh->numvertexes);\n//\t\tqglDrawRangeElements(GL_TRIANGLES, 0, surf->mesh->numvertexes-1, surf->mesh->numindexes, GL_INDEX_TYPE, surf->mesh->indexes);\n\t\tRQuantAdd(RQUANT_SHADOWINDICIES, surf->mesh->numvertexes);\n\n\t\tfor (v = 0; v < surf->mesh->numvertexes; v++)\n\t\t{\n\t\t//border\n\t\t\tv1 = surf->mesh->xyz_array[v];\n\t\t\tv2 = surf->mesh->xyz_array[( v+1 )%surf->mesh->numvertexes];\n\n\t\t\t//get positions of v3 and v4 based on the light position\n\t\t\tv3[0] = ( v1[0]-lightorg[0] );\n\t\t\tv3[1] = ( v1[1]-lightorg[1] );\n\t\t\tv3[2] = ( v1[2]-lightorg[2] );\n\t\t\tVectorNormalizeFast(v3);\n\t\t\tVectorScale(v3, PROJECTION_DISTANCE, v3);\n\n\t\t\tv4[0] = ( v2[0]-lightorg[0] );\n\t\t\tv4[1] = ( v2[1]-lightorg[1] );\n\t\t\tv4[2] = ( v2[2]-lightorg[2] );\n\t\t\tVectorNormalizeFast(v4);\n\t\t\tVectorScale(v4, PROJECTION_DISTANCE, v4);\n\n\t\t\t//Now draw the quad from the two verts to the projected light\n\t\t\t//verts\n\t\t\tqglBegin( GL_QUAD_STRIP );\n\t\t\t\tqglVertex3fv(v1);\n\t\t\t\tqglVertex3f\t(v1[0]+v3[0], v1[1]+v3[1], v1[2]+v3[2]);\n\t\t\t\tqglVertex3fv(v2);\n\t\t\t\tqglVertex3f (v2[0]+v4[0], v2[1]+v4[1], v2[2]+v4[2]);\n\t\t\tqglEnd();\n\t\t}\n\n//back\n\t\t\t//the same applies as earlier\n\t\tqglBegin(GL_POLYGON);\n\t\tfor (v = surf->mesh->numvertexes-1; v >=0; v--)\n\t\t{\n\t\t\tv1 = surf->mesh->xyz_array[v];\n\t\t\tv3[0] = (v1[0]-lightorg[0]);\n\t\t\tv3[1] = (v1[1]-lightorg[1]);\n\t\t\tv3[2] = (v1[2]-lightorg[2]);\n\t\t\tVectorNormalizeFast(v3);\n\t\t\tVectorScale(v3, PROJECTION_DISTANCE, v3);\n\n\t\t\tqglVertex3f(v1[0]+v3[0], v1[1]+v3[1], v1[2]+v3[2]);\n\t\t}\n\t\tqglEnd();\n\t}\n\n#ifdef BEF_PUSHDEPTH\n\tGLBE_PolyOffsetStencilShadow(false);\n#else\n\tGLBE_PolyOffsetStencilShadow();\n#endif\n}\n#endif\n\n#if defined(GLQUAKE) || defined(D3D9QUAKE)\n/*when this is called, the gl state has been set up to draw the stencil volumes using whatever extensions we have\nif secondside is set, then the gpu sucks and we're drawing stuff the slow 2-pass way, and this is the second pass.\n*/\nstatic void Sh_DrawStencilLightShadows(dlight_t *dl, qbyte *lvis, qbyte *vvis, qboolean secondside)\n{\n\tstruct shadowmesh_s *sm;\n#ifdef GLQUAKE\n\textern cvar_t gl_part_flame;\n\tint\t\ti;\n\tentity_t *ent;\n\tmodel_t *emodel;\n#endif\n\n\tsm = SHM_BuildShadowMesh(dl, lvis, SMT_STENCILVOLUME);\n\tif (!sm)\n\t{\n#ifdef GLQUAKE\n\t\tSh_DrawBrushModelShadow(dl, &r_worldentity);\n#endif\n\t}\n\telse\n\t{\n\t\tswitch (qrenderer)\n\t\t{\n\t\tcase QR_NONE:\n\t\tcase QR_SOFTWARE:\n\t\tdefault:\n\t\t\tbreak;\n\n#ifdef D3D11QUAKE\n//\t\tcase QR_DIRECT3D11:\n//\t\t\tD3D11BE_RenderShadowBuffer(sm->numverts, sm->d3d11_vbuffer, sm->numindicies, sm->d3d11_ibuffer);\n//\t\t\tbreak;\n#endif\n#ifdef D3D9QUAKE\n\t\tcase QR_DIRECT3D9:\n\t\t\tD3D9BE_RenderShadowBuffer(sm->numverts, sm->d3d9_vbuffer, sm->numindicies, sm->d3d9_ibuffer);\n\t\t\tbreak;\n#endif\n#ifdef GLQUAKE\n\t\tcase QR_OPENGL:\n\t\t\tGLBE_RenderShadowBuffer(sm->numverts, sm->vefbo[0], sm->verts, sm->numindicies, sm->vefbo[1], sm->indicies);\n\t\t\tbreak;\n#endif\n#ifdef VKQUAKE\n//\t\tcase QR_VULKAN:\n//\t\t\tVKBE_RenderShadowBuffer(sm->numverts, sm->vebo[0], sm->verts, sm->numindicies, sm->vebo[1], sm->indicies);\n//\t\t\tbreak;\n#endif\n\t\t}\n\t}\n\tif (!r_drawentities.value)\n\t\treturn;\n\n#ifdef GLQUAKE\n\tif (qrenderer != QR_OPENGL)\n\t\treturn;\t//FIXME: still uses glBegin specifics.\n\tif (gl_config_nofixedfunc)\n\t\treturn;\t/*too lazy to use shaders*/\n\tif (gl_config_gles)\n\t\treturn;\t//FIXME: uses glBegin\n\n\t// draw sprites seperately, because of alpha blending\n\tfor (i=r_refdef.firstvisedict ; i<cl_numvisedicts ; i++)\n\t{\n\t\tent = &cl_visedicts[i];\n\n\t\tif (ent->rtype != RT_MODEL)\n\t\t\tcontinue;\n\n\t\tif (ent->flags & (RF_NOSHADOW|Q2RF_BEAM))\n\t\t\tcontinue;\n\n\t\tif (ent->keynum == dl->key && ent->keynum)\n\t\t\tcontinue;\n\n\t\temodel = ent->model;\n\t\tif (!emodel)\n\t\t\tcontinue;\n\n\t\tif (cls.allow_anyparticles)\t//allowed or static\n\t\t{\n\t\t\tif (emodel->engineflags & MDLF_EMITREPLACE)\n\t\t\t{\n\t\t\t\tif (gl_part_flame.value)\n\t\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\n\t\tif (emodel->loadstate == MLS_NOTLOADED)\n\t\t{\n\t\t\tif (!Mod_LoadModel(emodel, MLV_WARN))\n\t\t\t\tcontinue;\n\t\t}\n\t\tif (emodel->loadstate != MLS_LOADED)\n\t\t\tcontinue;\n\n\t\tswitch (emodel->type)\n\t\t{\n\t\tcase mod_alias:\n\t\t\tif (r_drawentities.ival == 3)\n\t\t\t\tcontinue;\n\t\t\tR_DrawGAliasShadowVolume (ent, dl->origin, dl->radius);\n\t\t\tbreak;\n\n\t\tcase mod_brush:\n\t\t\tif (r_drawentities.ival == 2)\n\t\t\t\tcontinue;\n\t\t\tSh_DrawBrushModelShadow (dl, ent);\n\t\t\tbreak;\n\n\t\tcase mod_sprite:\t//never any shadows on sprites, it doesn't really make sense.\n\t\t\tbreak;\n\n\t\tdefault:\n\t\t\tbreak;\n\t\t}\n\t}\n\tBE_SelectEntity(&r_worldentity);\n#endif\n}\n\n//draws a light using stencil shadows.\n//redraws world geometry up to 3 times per light...\nstatic qboolean Sh_DrawStencilLight(dlight_t *dl, vec3_t colour, vec3_t axis[3], qbyte *vvis)\n{\n\tint sref;\n\tint clus;\n\tqbyte *lvis;\n\tsrect_t rect;\n\n\tvec3_t mins;\n\tvec3_t maxs;\n\n\tif (R_CullSphere(dl->origin, dl->radius))\n\t{\n\t\tRQuantAdd(RQUANT_RTLIGHT_CULL_FRUSTUM, 1);\n\t\treturn false;\t//this should be the more common case\n\t}\n\n\tmins[0] = dl->origin[0] - dl->radius;\n\tmins[1] = dl->origin[1] - dl->radius;\n\tmins[2] = dl->origin[2] - dl->radius;\n\n\tmaxs[0] = dl->origin[0] + dl->radius;\n\tmaxs[1] = dl->origin[1] + dl->radius;\n\tmaxs[2] = dl->origin[2] + dl->radius;\n\n\tif (!dl->rebuildcache)\n\t{\n\t\t//fixme: check head node first?\n\t\tif (!Sh_LeafInView(dl->worldshadowmesh->litleaves, vvis))\n\t\t{\n\t\t\tRQuantAdd(RQUANT_RTLIGHT_CULL_PVS, 1);\n\t\t\treturn false;\n\t\t}\n\t\tlvis = NULL;\n\t}\n\telse\n\t{\n\t\tclus = cl.worldmodel->funcs.ClusterForPoint(cl.worldmodel, dl->origin, NULL);\t//FIXME: check areas\n\t\tlvis = cl.worldmodel->funcs.ClusterPVS(cl.worldmodel, clus, &lvisb, PVM_FAST);\n//\t\tif (cl.worldmodel->funcs.ClustersInSphere)\n//\t\t\tlvis = cl.worldmodel->funcs.ClustersInSphere(cl.worldmodel, dl->origin, dl->radius, &lvisb2, lvis);\n\n\t\tif (!Sh_VisOverlaps(lvis, vvis))\t//The two viewing areas do not intersect.\n\t\t{\n\t\t\tRQuantAdd(RQUANT_RTLIGHT_CULL_PVS, 1);\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t//sets up the gl scissor (and culls to view)\n\tif (Sh_ScissorForBox(mins, maxs, &rect))\n\t{\n\t\tRQuantAdd(RQUANT_RTLIGHT_CULL_SCISSOR, 1);\n\t\treturn false;\t//this doesn't cull often.\n\t}\n\tRQuantAdd(RQUANT_RTLIGHT_DRAWN, 1);\n\n\tBE_SelectDLight(dl, colour, axis, LSHADER_STANDARD);\n\tBE_SelectMode(BEM_STENCIL);\n\n\t//The backend doesn't maintain scissor state.\n\t//The backend doesn't maintain stencil test state either - it needs to be active for more than just stencils, or disabled. its awkward.\n\tBE_Scissor(&rect);\n\n\n\tswitch(qrenderer)\n\t{\n\tdefault:\n\t\t(void)sref;\n\t\tbreak;\n#ifdef GLQUAKE\n\tcase QR_OPENGL:\n\t\t{\n\t\t\tint sfrontfail;\n\t\t\tint sbackfail;\n\t\t\tqglEnable(GL_STENCIL_TEST);\n\n\t\t\t//FIXME: is it practical to test to see if scissors allow not clearing the stencil buffer?\n\n\t\t\t/*we don't need all that much stencil buffer depth, and if we don't get enough or have dodgy volumes, wrap if we can*/\n\t\t#ifdef I_LIVE_IN_A_FREE_COUNTRY\n\t\t\tsref = 0;\n\t\t\tsbackfail = GL_INCR;\n\t\t\tsfrontfail = GL_DECR;\n\t\t\tif (gl_config.ext_stencil_wrap)\n\t\t\t{\t//minimise damage...\n\t\t\t\tsbackfail = GL_INCR_WRAP_EXT;\n\t\t\t\tsfrontfail = GL_DECR_WRAP_EXT;\n\t\t\t}\n\t\t#else\n\t\t\tsref = (1<<sh_config.stencilbits)-1; /*this is halved for two-sided stencil support, just in case there's no wrap support*/\n\t\t\tsbackfail = GL_DECR;\n\t\t\tsfrontfail = GL_INCR;\n\t\t\tif (gl_config.ext_stencil_wrap)\n\t\t\t{\t//minimise damage...\n\t\t\t\tsbackfail = GL_DECR_WRAP_EXT;\n\t\t\t\tsfrontfail = GL_INCR_WRAP_EXT;\n\t\t\t}\n\t\t#endif\n\t\t\t//our stencil writes.\n\t\t\tif (gl_config.arb_depth_clamp && r_refdef.maxdist != 0)\n\t\t\t\tqglEnable(GL_DEPTH_CLAMP_ARB);\n\n\t\t#if 0 //def _DEBUG\n\t\t//\tif (r_shadows.value == 666)\t//testing (visible shadow volumes)\n\t\t\t{\n\t\t\t\tqglColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);\n\t\t\t\tqglColor4f(dl->color[0], dl->color[1], dl->color[2], 1);\n\t\t\t\tqglDisable(GL_STENCIL_TEST);\n//\t\t\t\tqglEnable(GL_POLYGON_OFFSET_FILL);\n//\t\t\t\tqglPolygonOffset(-1, -1);\n\t\t\t//\tqglPolygonMode(GL_FRONT_AND_BACK, GL_LINE);\n\t\t\t\tSh_DrawStencilLightShadows(dl, lvis, vvis, false);\n//\t\t\t\tqglDisable(GL_POLYGON_OFFSET_FILL);\n//\t\t\t\tqglPolygonMode(GL_FRONT_AND_BACK, GL_FILL);\n\t\t\t}\n\t\t#endif\n\n\t\t\tif (qglStencilOpSeparate)\n\t\t\t{\n\t\t\t\t//ATI/GLES/ARB method\n\t\t\t\tsref/=2;\n\t\t\t\tqglClearStencil(sref);\n\t\t\t\tqglClear(GL_STENCIL_BUFFER_BIT);\n\t\t\t\tGL_CullFace(0);\n\n\t\t\t\tqglStencilFunc(GL_ALWAYS, 0, ~0);\n\n\t\t\t\tqglStencilOpSeparate(GL_BACK, GL_KEEP, sbackfail, GL_KEEP);\n\t\t\t\tqglStencilOpSeparate(GL_FRONT, GL_KEEP, sfrontfail, GL_KEEP);\n\n\t\t\t\tSh_DrawStencilLightShadows(dl, lvis, vvis, false);\n\t\t\t\tqglStencilOpSeparate(GL_FRONT_AND_BACK, GL_KEEP, GL_KEEP, GL_KEEP);\n\n\t\t\t\tGL_CullFace(SHADER_CULL_FRONT);\n\n\t\t\t\tqglStencilFunc(GL_EQUAL, sref, ~0);\n\t\t\t}\n\t\t\telse if (qglActiveStencilFaceEXT)\n\t\t\t{\n\t\t\t\t//Nvidia-specific method.\n\t\t\t\tsref/=2;\n\t\t\t\tqglClearStencil(sref);\n\t\t\t\tqglClear(GL_STENCIL_BUFFER_BIT);\n\t\t\t\tGL_CullFace(0);\n\n\t\t\t\tqglEnable(GL_STENCIL_TEST_TWO_SIDE_EXT);\n\n\t\t\t\tqglActiveStencilFaceEXT(GL_BACK);\t\n\t\t\t\tqglStencilOp(GL_KEEP, sbackfail, GL_KEEP);\n\t\t\t\tqglStencilFunc(GL_ALWAYS, 0, ~0 );\n\n\t\t\t\tqglActiveStencilFaceEXT(GL_FRONT);\n\t\t\t\tqglStencilOp(GL_KEEP, sfrontfail, GL_KEEP);\n\t\t\t\tqglStencilFunc(GL_ALWAYS, 0, ~0 );\n\n\t\t\t\tSh_DrawStencilLightShadows(dl, lvis, vvis, false);\n\n\t\t\t\tqglActiveStencilFaceEXT(GL_BACK);\n\t\t\t\tqglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);\n\t\t\t\tqglStencilFunc(GL_ALWAYS, 0, ~0 );\n\n\t\t\t\tqglActiveStencilFaceEXT(GL_FRONT);\n\t\t\t\tqglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);\n\t\t\t\tqglStencilFunc(GL_EQUAL, sref, ~0 );\n\n\t\t\t\tqglDisable(GL_STENCIL_TEST_TWO_SIDE_EXT);\n\t\t\t}\n\t\t\telse //your graphics card sucks and lacks efficient stencil shadow techniques.\n\t\t\t{\t//centered around 0. Will only be increased then decreased less.\n\t\t\t\tqglClearStencil(sref);\n\t\t\t\tqglClear(GL_STENCIL_BUFFER_BIT);\n\n\t\t\t\tqglStencilFunc(GL_ALWAYS, 0, ~0);\n\n\t\t\t\tGL_CullFace(SHADER_CULL_BACK);\n\t\t\t\tqglStencilOp(GL_KEEP, sbackfail, GL_KEEP);\n\t\t\t\tSh_DrawStencilLightShadows(dl, lvis, vvis, false);\n\n\t\t\t\tGL_CullFace(SHADER_CULL_FRONT);\n\t\t\t\tqglStencilOp(GL_KEEP, sfrontfail, GL_KEEP);\n\t\t\t\tSh_DrawStencilLightShadows(dl, lvis, vvis, true);\n\n\t\t\t\tqglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);\n\t\t\t\tqglStencilFunc(GL_EQUAL, sref, ~0);\n\t\t\t}\n\t\t\tif (gl_config.arb_depth_clamp)\n\t\t\t\tqglDisable(GL_DEPTH_CLAMP_ARB);\n\t\t\t//end stencil writing.\n\n\t\t\tBE_SelectMode(BEM_LIGHT);\n\t\t\tSh_DrawEntLighting(dl, colour, vvis);\n\t\t\tqglDisable(GL_STENCIL_TEST);\n\t\t\tqglStencilFunc( GL_ALWAYS, 0, ~0 );\n\t\t}\n\t\tbreak;\n#endif\n#ifdef D3D9QUAKE\n\tcase QR_DIRECT3D9:\n\t\tsref = (1<<8)-1;\n\t\tsref/=2;\n\n\t\t/*clear the stencil buffer*/\n\t\tIDirect3DDevice9_Clear(pD3DDev9, 0, NULL, D3DCLEAR_STENCIL, D3DCOLOR_XRGB(0, 0, 0), 1.0f, sref);\n\n\t\t/*set up 2-sided stenciling*/\n\t\tD3D9BE_Cull(0);\n\t\tIDirect3DDevice9_SetRenderState(pD3DDev9, D3DRS_STENCILENABLE, true);\n\t\tIDirect3DDevice9_SetRenderState(pD3DDev9, D3DRS_STENCILFUNC, D3DCMP_ALWAYS);\n\t\tIDirect3DDevice9_SetRenderState(pD3DDev9, D3DRS_TWOSIDEDSTENCILMODE, true);\n\t\tIDirect3DDevice9_SetRenderState(pD3DDev9, D3DRS_STENCILFAIL, D3DSTENCILOP_KEEP);\n\t\tIDirect3DDevice9_SetRenderState(pD3DDev9, D3DRS_STENCILZFAIL, D3DSTENCILOP_DECR);\n\t\tIDirect3DDevice9_SetRenderState(pD3DDev9, D3DRS_STENCILPASS, D3DSTENCILOP_KEEP);\n\t\tIDirect3DDevice9_SetRenderState(pD3DDev9, D3DRS_STENCILFUNC, D3DCMP_ALWAYS);\n\t\tIDirect3DDevice9_SetRenderState(pD3DDev9, D3DRS_CCW_STENCILFAIL, D3DSTENCILOP_KEEP);\n\t\tIDirect3DDevice9_SetRenderState(pD3DDev9, D3DRS_CCW_STENCILZFAIL, D3DSTENCILOP_INCR);\n\t\tIDirect3DDevice9_SetRenderState(pD3DDev9, D3DRS_CCW_STENCILPASS, D3DSTENCILOP_KEEP);\n\n\t\t/*draw the shadows*/\n\t\tSh_DrawStencilLightShadows(dl, lvis, vvis, false);\n\n\t\t//disable stencil writing\n\t\tIDirect3DDevice9_SetRenderState(pD3DDev9, D3DRS_STENCILZFAIL, D3DSTENCILOP_KEEP);\n\t\tIDirect3DDevice9_SetRenderState(pD3DDev9, D3DRS_TWOSIDEDSTENCILMODE, false);\n\t\tIDirect3DDevice9_SetRenderState(pD3DDev9, D3DRS_STENCILFUNC, D3DCMP_EQUAL);\n\t\tIDirect3DDevice9_SetRenderState(pD3DDev9, D3DRS_STENCILREF, sref);\n\t\tIDirect3DDevice9_SetRenderState(pD3DDev9, D3DRS_STENCILMASK, ~0);\n\n\t\t/*draw the light*/\n\t\tBE_SelectMode(BEM_LIGHT);\n\t\tSh_DrawEntLighting(dl, colour, vvis);\n\n\t\t/*okay, no more stencil stuff*/\n\t\tIDirect3DDevice9_SetRenderState(pD3DDev9, D3DRS_STENCILENABLE, false);\n\t\tbreak;\n#endif\n\t}\n\n\treturn true;\n}\n#else\n#define Sh_DrawStencilLight(dl,rgb,axis,vvis) Sh_DrawShadowlessLight(dl,rgb,axis,vvis,LSHADER_STANDARD)\n#endif\n\nqboolean Sh_CullLight(dlight_t *dl, qbyte *vvis)\n{\n\tif (R_CullSphere(dl->origin, dl->radius))\n\t{\n\t\tRQuantAdd(RQUANT_RTLIGHT_CULL_FRUSTUM, 1);\n\t\treturn true;\t//this should be the more common case\n\t}\n\n\tif (!dl->rebuildcache)\n\t{\n\t\t//fixme: check head node first?\n\t\tif (!Sh_LeafInView(dl->worldshadowmesh->litleaves, vvis))\n\t\t{\n\t\t\tRQuantAdd(RQUANT_RTLIGHT_CULL_PVS, 1);\n\t\t\treturn true;\n\t\t}\n\t}\n\telse\n\t{\n\t\tint clus;\n\t\tqbyte *lvis;\n\n\t\tclus = cl.worldmodel->funcs.ClusterForPoint(cl.worldmodel, dl->origin, NULL);\n\t\tlvis = cl.worldmodel->funcs.ClusterPVS(cl.worldmodel, clus, &lvisb, PVM_FAST);\n//\t\tif (cl.worldmodel->funcs.ClustersInSphere)\n//\t\t\tlvis = cl.worldmodel->funcs.ClustersInSphere(cl.worldmodel, dl->origin, dl->radius, &lvisb2, lvis);\n\n\t\tSHM_BuildShadowMesh(dl, lvis, SMT_DEFERRED);\n\n\t\tif (!Sh_VisOverlaps(lvis, vvis))\t//The two viewing areas do not intersect.\n\t\t{\n\t\t\tRQuantAdd(RQUANT_RTLIGHT_CULL_PVS, 1);\n\t\t\treturn true;\n\t\t}\n\t}\n\treturn false;\t//please draw this...\n}\n\nstatic void Sh_DrawShadowlessLight(dlight_t *dl, vec3_t colour, vec3_t axis[3], qbyte *vvis, unsigned int lshaderflags)\n{\n\tvec3_t mins, maxs;\n\tsrect_t rect;\n\n\tif (R_CullSphere(dl->origin, dl->radius))\n\t{\n\t\tRQuantAdd(RQUANT_RTLIGHT_CULL_FRUSTUM, 1);\n\t\treturn;\t//this should be the more common case\n\t}\n\n\tif (!dl->rebuildcache)\n\t{\n\t\t//fixme: check head node first?\n\t\tif (!Sh_LeafInView(dl->worldshadowmesh->litleaves, vvis))\n\t\t{\n\t\t\tRQuantAdd(RQUANT_RTLIGHT_CULL_PVS, 1);\n\t\t\treturn;\n\t\t}\n\t}\n\telse\n\t{\n\t\tint clus;\n\t\tqbyte *lvis;\n\n\t\tif (cl.worldmodel->funcs.ClustersInSphere)\n\t\t\tlvis = cl.worldmodel->funcs.ClustersInSphere(cl.worldmodel, dl->origin, dl->radius, &lvisb2, NULL);\n\t\telse\n\t\t{\n\t\t\tclus = cl.worldmodel->funcs.ClusterForPoint(cl.worldmodel, dl->origin, NULL);\n\t\t\tlvis = cl.worldmodel->funcs.ClusterPVS(cl.worldmodel, clus, &lvisb, PVM_FAST);\n\t\t}\n\n\t\tSHM_BuildShadowMesh(dl, lvis, SMT_SHADOWLESS);\n\t\t//FIXME: check areas\n\t\tif (!Sh_VisOverlaps(lvis, vvis))\t//The two viewing areas do not intersect.\n\t\t{\n\t\t\tRQuantAdd(RQUANT_RTLIGHT_CULL_PVS, 1);\n\t\t\treturn;\n\t\t}\n\t}\n\n\tmins[0] = dl->origin[0] - dl->radius;\n\tmins[1] = dl->origin[1] - dl->radius;\n\tmins[2] = dl->origin[2] - dl->radius;\n\n\tmaxs[0] = dl->origin[0] + dl->radius;\n\tmaxs[1] = dl->origin[1] + dl->radius;\n\tmaxs[2] = dl->origin[2] + dl->radius;\n\n\n//sets up the gl scissor (actually just culls to view)\n\tif (Sh_ScissorForBox(mins, maxs, &rect))\n\t{\n\t\tRQuantAdd(RQUANT_RTLIGHT_CULL_SCISSOR, 1);\n\t\treturn;\t//was culled.\n\t}\n\n\t//should we actually scissor here? there's not really much point I suppose.\n\tBE_Scissor(NULL);\n\n\tRQuantAdd(RQUANT_RTLIGHT_DRAWN, 1);\n\n\tif (dl->fov)\n\t\tlshaderflags |= LSHADER_SPOT;\n\tBE_SelectDLight(dl, colour, axis, lshaderflags);\n\tBE_SelectMode(BEM_LIGHT);\n\tSh_DrawEntLighting(dl, colour, vvis);\n}\n\nvoid Sh_DrawCrepuscularLight(dlight_t *dl, float *colours)\n{\n#ifdef GLQUAKE\n\tint oldfbo;\n\tstatic mesh_t mesh;\n\timage_t *oldsrccol;\n\tstatic vecV_t xyz[4] =\n\t{\n\t\t{-1,-1,-1},\n\t\t{-1,1,-1},\n\t\t{1,1,-1},\n\t\t{1,-1,-1}\n\t};\n\tstatic vec2_t tc[4] =\n\t{\n\t\t{0,0},\n\t\t{0,1},\n\t\t{1,1},\n\t\t{1,0}\n\t};\n\tstatic index_t idx[6] =\n\t{\n\t\t0,1,2,\n\t\t0,2,3\n\t};\n\tif (qrenderer != QR_OPENGL)\n\t\treturn;\n\n\tmesh.numindexes = 6;\n\tmesh.numvertexes = 4;\n\tmesh.xyz_array = xyz;\n\tmesh.st_array = tc;\n\tmesh.indexes = idx;\n\n\t/*\n\ta crepuscular light (seriously, that's the correct spelling) is one that gives 'god rays', rather than regular light.\n\tour implementation doesn't cast shadows. this allows it to actually be outside the map, and to shine through cloud layers in the sky.\n\twe could cast shadows if the light was actually inside, I suppose.\n\tAnyway, its done using an FBO, where everything but the sky is black (stuff that occludes the sky is black too).\n\twhich is then blitted onto the screen in 2d-space.\n\t*/\n\n\t/*requires an FBO, as stated above*/\n\tif (!gl_config.ext_framebuffer_objects)\n\t\treturn;\n\n\t//fixme: we should add an extra few pixels each side to the fbo, to avoid too much weirdness at screen edges.\n\n\tif (!crepuscular_texture_id)\n\t{\n\t\t/*FIXME: requires npot*/\n\t\tcrepuscular_shader = R_RegisterShader(\"crepuscular_screen\", SUF_NONE,\n\t\t\t\"{\\n\"\n\t\t\t\t\"program crepuscular_rays\\n\"\n\t\t\t\t\"{\\n\"\n\t\t\t\t\t\"map $sourcecolour\\n\"\n\t\t\t\t\t\"blend add\\n\"\n\t\t\t\t\"}\\n\"\n\t\t\t\"}\\n\"\n\t\t\t);\n\n\t\tcrepuscular_texture_id = Image_CreateTexture(\"***crepusculartexture***\", NULL, IF_LINEAR|IF_NOMIPMAP|IF_CLAMP|IF_NOGAMMA);\n\t\tImage_Upload(crepuscular_texture_id, TF_RGBA32, NULL, NULL, vid.pixelwidth, vid.pixelheight, 1, IF_LINEAR|IF_NOMIPMAP|IF_CLAMP|IF_NOGAMMA);\n\t}\n\n\tBE_Scissor(NULL);\n\n\toldfbo = GLBE_FBO_Update(&crepuscular_fbo, FBO_RB_DEPTH, &crepuscular_texture_id, 1, r_nulltex, vid.pixelwidth, vid.pixelheight, 0);\n\n\tGL_ForceDepthWritable();\n//\tqglClearColor(0, 0, 0, 1);\n\tqglClear(GL_DEPTH_BUFFER_BIT);\n\n\tBE_SelectMode(BEM_CREPUSCULAR);\n\tBE_SelectDLight(dl, colours, dl->axis, LSHADER_STANDARD);\n\tGLBE_SubmitMeshes(cl.worldmodel->batches, SHADER_SORT_PORTAL, SHADER_SORT_BLEND);\n\n\tGLBE_FBO_Pop(oldfbo);\n\n\toldsrccol = NULL;//shaderstate.tex_sourcecol;\n\tGLBE_FBO_Sources(crepuscular_texture_id, NULL);\n//\tcrepuscular_shader->defaulttextures.base = crepuscular_texture_id;\n\t//shaderstate.tex_sourcecol = oldsrccol;\n\n\tBE_SelectMode(BEM_STANDARD);\n\n\tBE_DrawMesh_Single(crepuscular_shader, &mesh, NULL, 0);\n\n\tGLBE_FBO_Sources(oldsrccol, NULL);\n#endif\n}\n\nvoid Sh_PurgeShadowMeshes(void)\n{\n\tdlight_t *dl;\n\tsize_t i;\n\tfor (dl = cl_dlights, i=0; i<cl_maxdlights; i++, dl++)\n\t{\n\t\tif (dl->worldshadowmesh)\n\t\t{\n\t\t\tSH_FreeShadowMesh(dl->worldshadowmesh);\n\t\t\tdl->worldshadowmesh = NULL;\n\t\t\tdl->rebuildcache = true;\n\t\t}\n\t}\n\tZ_Free(edge);\n\tedge = NULL;\n\tmaxedge = 0;\n}\n\nvoid R_StaticEntityToRTLight(int i);\nvoid Sh_PreGenerateLights(void)\n{\n\tunsigned int ignoreflags;\n\tdlight_t *dl;\n\tint shadowtype;\n\tint leaf;\n\tqbyte *lvis;\n\tint i;\n\n\tif (r_shadow_realtime_world_importlightentitiesfrommap.modified)\n\t{\n\t\trtlights_first = RTL_FIRST;\n\t\trtlights_max = RTL_FIRST;\n\t}\n\n\tr_shadow_realtime_world_importlightentitiesfrommap.modified = false;\n\tr_shadow_realtime_world_lightmaps.value = atof(r_shadow_realtime_world_lightmaps.string);\n\tif (!cl.worldmodel)\n\t\treturn;\n\tif ((r_shadow_realtime_dlight.ival || r_shadow_realtime_world.ival) && rtlights_max == RTL_FIRST)\n\t{\n\t\tqboolean okay = false;\n\t\tr_shadow_realtime_world_lightmaps_force = -1;\n\t\tif (!okay && r_shadow_realtime_world_importlightentitiesfrommap.ival <= 1)\n\t\t\tokay |= R_LoadRTLights();\n\t\tif (!okay)\n\t\t{\n\t\t\tfor (i = 0; i < cl.num_statics; i++)\n\t\t\t\tR_StaticEntityToRTLight(i);\n\t\t\tokay |= rtlights_max != RTL_FIRST;\n\t\t}\n\t\tif (!okay)\n\t\t\tokay |= R_ImportRTLights(Mod_GetEntitiesString(cl.worldmodel), r_shadow_realtime_world_importlightentitiesfrommap.ival);\n\t\tif (!okay && r_shadow_realtime_world.ival && r_shadow_realtime_world_lightmaps.value < 0.5)\n\t\t{\n\t\t\tr_shadow_realtime_world_lightmaps.value = 1;\n\t\t\tif (!r_shadow_realtime_world_importlightentitiesfrommap.ival)\n\t\t\t\tCon_Printf(CON_WARNING \"No lights detected in map, ^[[and importing is disabled]\\\\type\\\\r_shadow_realtime_world_importlightentitiesfrommap 1^].\\n\");\n\t\t\telse\n\t\t\t\tCon_DPrintf(\"No lights detected in map.\\n\");\n\t\t}\n\n\t\tfor (i = 0; i < cl.num_statics; i++)\n\t\t{\n\t\t\tR_StaticEntityToRTLight(i);\n\t\t}\n\t}\n\n\tif (r_shadow_realtime_world_lightmaps_force >= 0)\n\t\tr_shadow_realtime_world_lightmaps.value = r_shadow_realtime_world_lightmaps_force;\n\n\tignoreflags = (r_shadow_realtime_world.ival?LFLAG_REALTIMEMODE:0)\n\t\t\t| (r_shadow_realtime_dlight.ival?LFLAG_NORMALMODE:0);\n\n\tfor (dl = cl_dlights+rtlights_first, i=rtlights_first; i<rtlights_max; i++, dl++)\n\t{\n\t\tdl->rebuildcache = true;\n\n\t\tif (dl->radius)\n\t\t{\n\t\t\tif (dl->flags & ignoreflags)\n\t\t\t{\n\t\t\t\tif (dl->flags & LFLAG_CREPUSCULAR)\n\t\t\t\t\tcontinue;\n\n\t\t\t\tif (((!dl->die)?!r_shadow_realtime_world_shadows.ival:!r_shadow_realtime_dlight_shadows.ival) || (dl->flags & LFLAG_NOSHADOWS))\n\t\t\t\t\tshadowtype = SMT_SHADOWLESS;\n\t\t\t\telse if (r_shadow_raytrace.ival)\n\t\t\t\t\tshadowtype = SMT_SHADOWLESS;\t//shadows are done via acceleration structures set up by the backend. don't need to worry about them here, they're effectively shadowless.\n\t\t\t\telse if (dl->flags & LFLAG_SHADOWMAP || r_shadow_shadowmapping.ival)\n\t\t\t\t\tshadowtype = SMT_SHADOWMAP;\n\t\t\t\telse\n\t\t\t\t\tshadowtype = SMT_STENCILVOLUME;\n\n\t\t\t\t//shadowless and lights with an ambient term pass through walls, so need to affect EVERY leaf withing the sphere.\n\t\t\t\tif ((shadowtype == SMT_SHADOWLESS || dl->lightcolourscales[0]) && cl.worldmodel->funcs.ClustersInSphere)\n\t\t\t\t\tlvis = cl.worldmodel->funcs.ClustersInSphere(cl.worldmodel, dl->origin, dl->radius, &lvisb2, NULL);\n\t\t\t\telse\n\t\t\t\t{\t//other lights only want to use the source leaf's pvs (clamped by the sphere)\n\t\t\t\t\tleaf = cl.worldmodel->funcs.ClusterForPoint(cl.worldmodel, dl->origin, NULL);\n\t\t\t\t\tlvis = cl.worldmodel->funcs.ClusterPVS(cl.worldmodel, leaf, &lvisb, PVM_FAST);\n\t\t\t\t\tif (cl.worldmodel->funcs.ClustersInSphere)\n\t\t\t\t\t\tlvis = cl.worldmodel->funcs.ClustersInSphere(cl.worldmodel, dl->origin, dl->radius, &lvisb2, lvis);\n\t\t\t\t}\n\n\t\t\t\tSHM_BuildShadowMesh(dl, lvis, shadowtype);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\n\t\tif (dl->worldshadowmesh)\n\t\t{\n\t\t\tSH_FreeShadowMesh(dl->worldshadowmesh);\n\t\t\tdl->worldshadowmesh = NULL;\n\t\t\tdl->rebuildcache = true;\n\t\t}\n\t}\n}\n\nvoid Com_ParseVector(char *str, vec3_t out)\n{\n\tstr = COM_Parse(str);\n\tout[0] = atof(com_token);\n\tstr = COM_Parse(str);\n\tout[1] = atof(com_token);\n\tstr = COM_Parse(str);\n\tout[2] = atof(com_token);\n}\n\nvoid Sh_CheckSettings(void)\n{\n\textern cvar_t r_shadows;\n\tqboolean canstencil = false, cansmap = false, canshadowless = false, canraytrace = false;\n\tr_shadow_raytrace.ival = r_shadow_raytrace.value;\n\tr_shadow_shadowmapping.ival = r_shadow_shadowmapping.value;\n\tr_shadow_realtime_world.ival = r_shadow_realtime_world.value;\n\tr_shadow_realtime_dlight.ival = r_shadow_realtime_dlight.value;\n\tr_shadow_realtime_world_shadows.ival = r_shadow_realtime_world_shadows.value;\n\tr_shadow_realtime_dlight_shadows.ival = r_shadow_realtime_dlight_shadows.value;\n\n\tswitch(qrenderer)\n\t{\n#ifdef VKQUAKE\n\tcase QR_VULKAN:\n\t\tcanshadowless = true;\n\t\tcansmap = vk.multisamplebits==VK_SAMPLE_COUNT_1_BIT; //FIXME - we need to render shadowmaps without needing to restart the current scene.\n\t\tcanraytrace = vk.khr_ray_query;\n\t\tcanstencil = false;\n\t\tbreak;\n#endif\n#ifdef GLQUAKE\n\tcase QR_OPENGL:\n\t\tcanshadowless = gl_config.arb_shader_objects || !gl_config_nofixedfunc; //falls back to crappy texture env\n\t\tif (gl_config.arb_shader_objects && gl_config.ext_framebuffer_objects && gl_config.arb_depth_texture)// && gl_config.arb_shadow)\n\t\t\tcansmap = true;\n\t\telse if ((r_shadow_realtime_world_shadows.ival || r_shadow_realtime_dlight_shadows.ival) && r_shadow_shadowmapping.ival)\n\t\t{\n\t\t\tif (!gl_config.arb_shader_objects)\n\t\t\t\tCon_DPrintf(\"Shadowmapping unsupported: No arb_shader_objects\\n\");\n\t\t\telse if (!gl_config.ext_framebuffer_objects)\n\t\t\t\tCon_DPrintf(\"Shadowmapping unsupported: No ext_framebuffer_objects\\n\");\n\t\t\telse if (!gl_config.arb_depth_texture)\n\t\t\t\tCon_DPrintf(\"Shadowmapping unsupported: No arb_depth_texture\\n\");\n\t\t}\n\t\tif (sh_config.stencilbits)\n\t\t\tcanstencil = true;\n\t\tbreak;\n#endif\n#ifdef D3D9QUAKE\n\tcase QR_DIRECT3D9:\n//\t\tcanshadowless = true;\n\t\t//the code still has a lot of ifdefs, so will crash if you try it in a merged build.\n\t\t//its not really usable in d3d-only builds either, so no great loss.\n//\t\tcanstencil = true;\n\t\tbreak;\n#endif\n#ifdef D3D11QUAKE\n\tcase QR_DIRECT3D11:\n\t\tcanshadowless = true;\t//all feature levels\n/* shadows are buggy right now. tbh they've always been buggy... rendering seems fine, its just the shadowmaps that are bad\n\t\tif (D3D11_BeginShadowMap(0, SHADOWMAP_SIZE*3, SHADOWMAP_SIZE*2))\n\t\t{\n\t\t\tD3D11_EndShadowMap();\n\t\t\tcansmap = true;\t\t//tends to not work properly until feature level 10 for one error or another.\n\t\t}\n*/\n\t\tbreak;\n#endif\n\tdefault:\n\t\tbreak;\n\t}\n\n\tif (!canstencil && !cansmap && !canshadowless)\n\t{\n\t\t//can't even do lighting\n\t\tif (r_shadow_realtime_world.ival || r_shadow_realtime_dlight.ival)\n\t\t\tCon_Printf(\"Missing rendering features: realtime %s lighting is not possible.\\n\", r_shadow_realtime_world.ival?\"world\":\"dynamic\");\n\t\tr_shadow_realtime_world.ival = 0;\n\t\tr_shadow_realtime_dlight.ival = 0;\n\t\tr_shadow_raytrace.ival = 0;\n\t}\n\telse if (!canstencil && !cansmap)\n\t{\n\t\tif (canraytrace)\t//just silently force raytrace on if we're not allowed stencil nor shadowmaps, but can use rt...\n\t\t\tr_shadow_raytrace.ival = true;\n\t\telse\n\t\t{\n\t\t\t//no shadow methods available at all.\n\t\t\tif ((r_shadow_realtime_world.ival&&r_shadow_realtime_world_shadows.ival)||(r_shadow_realtime_dlight.ival&&r_shadow_realtime_dlight_shadows.ival))\n\t\t\t\tCon_Printf(\"Missing rendering features: realtime shadows are not possible.\\n\");\n\t\t\tr_shadow_realtime_world_shadows.ival = 0;\n\t\t\tr_shadow_realtime_dlight_shadows.ival = 0;\n\t\t\tr_shadow_raytrace.ival = 0;\n\t\t}\n\t}\n\telse\n\t{\n\t\tr_shadow_raytrace.ival = r_shadow_raytrace.ival && canraytrace;\n\t\tif (!canstencil || !cansmap)\n\t\t{\n\t\t\t//only one shadow method\n\t\t\tif (!!r_shadow_shadowmapping.ival != cansmap)\n\t\t\t{\n\t\t\t\tif (!r_shadow_raytrace.ival && r_shadow_shadowmapping.ival && ((r_shadow_realtime_world.ival&&r_shadow_realtime_world_shadows.ival)||(r_shadow_realtime_dlight.ival&&r_shadow_realtime_dlight_shadows.ival)))\n\t\t\t\t\tCon_Printf(\"Missing rendering features: forcing shadowmapping %s.\\n\", cansmap?\"on\":\"off\");\n\t\t\t\tr_shadow_shadowmapping.ival = cansmap;\n\t\t\t}\n\t\t}\n\t}\n\n\tcansmap = cansmap && (r_shadows.ival==2);\n\tif (r_fakeshadows != cansmap)\n\t{\n\t\tr_fakeshadows = cansmap;\n\t\tShader_NeedReload(false);\n\t}\n\tr_blobshadows = r_fakeshadows?0:r_shadows.value;\t//force it off the hacky way.\n}\n\nvoid Sh_CalcPointLight(vec3_t point, vec3_t light)\n{\n\tvec3_t colour;\n\tdlight_t *dl;\n\tvec3_t disp;\n\tfloat dist;\n\tfloat frac;\n\tint i;\n\tunsigned int ignoreflags;\n\n\tvec3_t norm, impact;\n\tignoreflags = (r_shadow_realtime_world.ival?LFLAG_REALTIMEMODE:0)\n\t\t\t| (r_shadow_realtime_dlight.ival?LFLAG_NORMALMODE:0);\n\n\tVectorClear(light);\n\tif (ignoreflags)\n\tfor (dl = cl_dlights+rtlights_first, i=rtlights_first; i<rtlights_max; i++, dl++)\n\t{\n\t\tif (!(dl->flags & ignoreflags))\n\t\t\tcontinue;\n\n\t\tif (dl->key == cl.playerview[0].viewentity)\t//ignore the light if its emitting from the player. generally the player can't *SEE* that light so it still counts.\n\t\t\tcontinue;\t\t\t\t\t\t\t\t//disable this check if this function gets used for anything other than iris adaptation\n\n\t\tcolour[0] = dl->color[0];\n\t\tcolour[1] = dl->color[1];\n\t\tcolour[2] = dl->color[2];\n\t\tif (dl->style>=0 && dl->style<cl_max_lightstyles)\n\t\t{\n\t\t\tcolour[0] *= cl_lightstyle[dl->style].colours[0] * d_lightstylevalue[dl->style]/255.0f;\n\t\t\tcolour[1] *= cl_lightstyle[dl->style].colours[1] * d_lightstylevalue[dl->style]/255.0f;\n\t\t\tcolour[2] *= cl_lightstyle[dl->style].colours[2] * d_lightstylevalue[dl->style]/255.0f;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tcolour[0] *= r_lightstylescale.value;\n\t\t\tcolour[1] *= r_lightstylescale.value;\n\t\t\tcolour[2] *= r_lightstylescale.value;\n\t\t}\n\n\t\tif (colour[0] < 0.001 && colour[1] < 0.001 && colour[2] < 0.001)\n\t\t\tcontinue;\t//just switch these off.\n\n\t\tVectorSubtract(dl->origin, point, disp);\n\t\tdist = VectorLength(disp);\n\t\tfrac = dist / dl->radius;\n\t\tif (frac >= 1)\n\t\t\tcontinue;\n\t\t//FIXME: this should be affected by the direction.\n\t\tif (CL_TraceLine(point, dl->origin, impact, norm, NULL)>=1)\n\t\t\tVectorMA(light, 1-frac, colour, light);\n\t}\n}\n\nvoid Sh_DrawLights(qbyte *vis)\n{\n\tvec3_t rotated[3];\n\tvec3_t *axis;\n\tvec3_t colour;\n\tdlight_t *dl;\n\tint i;\n\tunsigned int ignoreflags;\n\textern cvar_t r_shadows;\n\n\tif (r_shadow_realtime_world.modified ||\n\t\tr_shadow_realtime_world_shadows.modified ||\n\t\tr_shadow_realtime_world_importlightentitiesfrommap.modified ||\n\t\tr_shadow_realtime_dlight.modified ||\n\t\tr_shadow_realtime_dlight_shadows.modified ||\n\t\tr_shadow_shadowmapping.modified || r_shadows.modified ||\n\t\tr_shadow_raytrace.modified)\n\t{\n\t\tr_shadow_realtime_world.modified =\n\t\tr_shadow_realtime_world_shadows.modified =\n\t\tr_shadow_realtime_dlight.modified =\n\t\tr_shadow_realtime_dlight_shadows.modified =\n\t\tr_shadow_shadowmapping.modified =\n\t\tr_shadows.modified =\n\t\tr_shadow_raytrace.modified =\n\t\t\t\tfalse;\n\t\tSh_CheckSettings();\n\t\t//make sure the lighting is reloaded\n\t\tSh_PreGenerateLights();\n\t}\n\n\tif (r_lightprepass)\n\t\treturn;\n\n\tignoreflags = (r_shadow_realtime_world.ival?LFLAG_REALTIMEMODE:0)\n\t\t\t\t| (r_shadow_realtime_dlight.ival?LFLAG_NORMALMODE:0)\n\t\t\t\t| ((r_dynamic.ival&&!r_dlightlightmaps)?LFLAG_LIGHTMAP:0);\t//if we're using scenecache and won't be updating lightmaps then we should still respect r_dynamic.\n\tif (!ignoreflags)\n\t\treturn;\n\n//\tif (r_refdef.recurse)\n\tfor (dl = cl_dlights+rtlights_first, i=rtlights_first; i<rtlights_max; i++, dl++)\n\t{\n\t\tif (!dl->radius)\n\t\t\tcontinue;\t//dead\n\n\t\tif (!(dl->flags & ignoreflags))\n\t\t\tcontinue;\n\n\t\tcolour[0] = dl->color[0];\n\t\tcolour[1] = dl->color[1];\n\t\tcolour[2] = dl->color[2];\n\t\tif (dl->customstyle)\n\t\t{\n\t\t\tconst char *map = dl->customstyle;\n\t\t\tint maplen = strlen(map);\n\n\t\t\tint idx, v1, v2, vd;\n\t\t\tfloat frac, strength;\n\n\t\t\tif (!maplen)\n\t\t\t{\n\t\t\t\tstrength = ('m'-'a')*22 * r_lightstylescale.value/255.0;\n\t\t\t}\n\t\t\telse if (map[0] == '=')\n\t\t\t{\n\t\t\t\tstrength = atof(map+1)*r_lightstylescale.value;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tfrac = (cl.time*r_lightstylespeed.value);\n\t\t\t\tif (*map == '?' && maplen>1)\n\t\t\t\t{\n\t\t\t\t\tmap++;\n\t\t\t\t\tmaplen--;\n\t\t\t\t\tfrac += i*M_PI;\n\t\t\t\t}\n\t\t\t\tfrac += i*M_PI;\n\t\t\t\tif (frac < 0)\n\t\t\t\t\tfrac = 0;\n\t\t\t\tidx = (int)frac;\n\t\t\t\tfrac -= idx;\t//this can require updates at 1000 times a second.. Depends on your framerate of course\n\n\t\t\t\tv1 = idx % maplen;\n\t\t\t\tv1 = map[v1] - 'a';\n\n\t\t\t\tv2 = (idx+1) % maplen;\n\t\t\t\tv2 = map[v2] - 'a';\n\n\t\t\t\tvd = v1 - v2;\n\t\t\t\tif (/*!r_lightstylesmooth.ival ||*/ vd < -r_lightstylesmooth_limit.ival || vd > r_lightstylesmooth_limit.ival)\n\t\t\t\t\tstrength = v1*(22/255.0)*r_lightstylescale.value;\n\t\t\t\telse\n\t\t\t\t\tstrength = (v1*(1-frac) + v2*(frac))*(22/255.0)*r_lightstylescale.value;\n\t\t\t}\n\t\t\tstrength *= d_lightstylevalue[0]/256.0f;\t//a lot of QW mods use lightstyle 0 for a global darkening fade-in thing, so be sure to respect that.\n\t\t\tcolour[0] *= strength;\n\t\t\tcolour[1] *= strength;\n\t\t\tcolour[2] *= strength;\n\t\t}\n\t\tif (dl->style>=0 && dl->style < cl_max_lightstyles)\n\t\t{\n\t\t\tcolour[0] *= cl_lightstyle[dl->style].colours[0] * d_lightstylevalue[dl->style]/255.0f;\n\t\t\tcolour[1] *= cl_lightstyle[dl->style].colours[1] * d_lightstylevalue[dl->style]/255.0f;\n\t\t\tcolour[2] *= cl_lightstyle[dl->style].colours[2] * d_lightstylevalue[dl->style]/255.0f;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tcolour[0] *= r_lightstylescale.value;\n\t\t\tcolour[1] *= r_lightstylescale.value;\n\t\t\tcolour[2] *= r_lightstylescale.value;\n\t\t}\n\t\tif (dl->fade[1])\n\t\t{\n\t\t\tvec3_t dir;\n\t\t\tfloat dist;\n\t\t\tVectorSubtract(dl->origin, r_origin, dir);\n\t\t\tdist = VectorLength(dir);\n\t\t\tif (dist > dl->fade[1])\n\t\t\t\tcontinue;\n\t\t\tif (dist > dl->fade[0])\n\t\t\t{\n\t\t\t\tdist = 1-((dist-dl->fade[0]) / (dl->fade[1]-dl->fade[0]));\n\t\t\t\tVectorScale(colour, dist, colour);\n\t\t\t}\n\t\t}\n\t\tcolour[0] *= r_refdef.hdr_value;\n\t\tcolour[1] *= r_refdef.hdr_value;\n\t\tcolour[2] *= r_refdef.hdr_value;\n\n\t\tif (colour[0] < 0.001 && colour[1] < 0.001 && colour[2] < 0.001)\n\t\t\tcontinue;\t//just switch these off.\n\n\t\tif (!dl->lightcolourscales[0] && !dl->lightcolourscales[1] && !dl->lightcolourscales[2])\n\t\t\tcontinue;\t//these lights are just coronas.\n\n\t\tif (dl->rotation[0] || dl->rotation[1] || dl->rotation[2])\n\t\t{\t//auto-rotating (static) rtlights\n\t\t\tvec3_t rot;\n\t\t\tvec3_t rotationaxis[3];\n\t\t\tVectorScale(dl->rotation, cl.time, rot);\n\t\t\tAngleVectorsFLU(rot, rotationaxis[0], rotationaxis[1], rotationaxis[2]);\n\t\t\tMatrix3_Multiply(dl->axis, rotationaxis, rotated);\n\t\t\taxis = rotated;\n\t\t}\n\t\telse\n\t\t\taxis = dl->axis;\n\n\t\tif (dl->flags & LFLAG_ORTHO)\n\t\t{\n\t\t\tvec3_t saveorg = {dl->origin[0], dl->origin[1], dl->origin[2]};\n\t\t\tvec3_t saveaxis[3];\n\t\t\tmemcpy(saveaxis, dl->axis, sizeof(saveaxis));\n\t\t\tmemcpy(dl->axis, axis, sizeof(saveaxis));\n\t\t\tSh_OrthoAlignToFrustum(dl, SHADOWMAP_SIZE);\n\t\t\tdl->rebuildcache = true;\n\t\t\tSh_DrawShadowMapLight(dl, colour, axis, NULL);\n\t\t\tVectorCopy(saveorg, dl->origin);\n\t\t\tmemcpy(dl->axis, saveaxis, sizeof(saveaxis));\n\t\t}\n\t\telse if (dl->flags & LFLAG_CREPUSCULAR)\n\t\t\tSh_DrawCrepuscularLight(dl, colour);\n\t\telse if (dl->flags & LFLAG_NOSHADOWS ||\n\t\t\t((i >= RTL_FIRST)?!r_shadow_realtime_world_shadows.ival:!r_shadow_realtime_dlight_shadows.ival) || //force shadowless when configured that way...\n\t\t\tignoreflags==LFLAG_LIGHTMAP)\t//scenecache fallback...\n\t\t{\n\t\t\tSh_DrawShadowlessLight(dl, colour, axis, vis, LSHADER_STANDARD);\n\t\t}\n\t\telse if (r_shadow_raytrace.ival)\n\t\t\tSh_DrawShadowlessLight(dl, colour, axis, vis, LSHADER_RAYQUERY);\n\t\telse if ((dl->flags & LFLAG_SHADOWMAP) || r_shadow_shadowmapping.ival)\n\t\t{\n\t\t\tSh_DrawShadowMapLight(dl, colour, axis, vis);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tSh_DrawStencilLight(dl, colour, axis, vis);\n\t\t}\n\t}\n\n#ifdef GLQUAKE\n\tif (gl_config.arb_shader_objects)\n\t{\n\t\tdlight_t sun = {0};\n\t\tvec3_t sundir;\n\t\tfloat dot;\n\t\tCom_ParseVector(r_sun_dir.string, sundir);\n\t\tCom_ParseVector(r_sun_colour.string, colour);\n\n\t\t//fade it out if we're looking at an angle parallel to it (to avoid nasty visible graduations or backwards rays!)\n\t\tdot = DotProduct(vpn, sundir);\n\t\tdot = 1-dot;\n\t\tdot *= dot;\n\t\tdot = 1-dot;\n\t\tVectorScale(colour, dot, colour);\n\n\t\tif (colour[0] > 0.001 || colour[1] > 0.001 || colour[2] > 0.001)\n\t\t{\n\t\t\t//only do this if we can see some sky surfaces. pointless otherwise\n\t\t\tbatch_t *b;\n\t\t\tfor (b = cl.worldmodel->batches[SHADER_SORT_SKY]; b; b = b->next)\n\t\t\t{\n\t\t\t\tif (b->meshes)\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (b)\n\t\t\t{\n\t\t\t\tVectorNormalize(sundir);\n\t\t\t\tVectorMA(r_origin, 1000, sundir, sun.origin);\n\t\t\t\tSh_DrawCrepuscularLight(&sun, colour);\n\t\t\t}\n\t\t}\n\t}\n#endif\n\n\tBE_Scissor(NULL);\n\n\tBE_SelectMode(BEM_STANDARD);\n\n//\tif (developer.value)\n//\tCon_Printf(\"%i lights drawn, %i frustum culled, %i pvs culled, %i scissor culled\\n\", bench.numlights, bench.numfrustumculled, bench.numpvsculled, bench.numscissorculled);\n//\tmemset(&bench, 0, sizeof(bench));\n}\n#endif\n\n//stencil shadows generally require that the farclip distance is really really far away\n//so this little function is used to check if its needed or not.\nqboolean Sh_StencilShadowsActive(void)\n{\n#if defined(RTLIGHTS) && !defined(SERVERONLY)\n\t//if shadowmapping is forced on all lights then we don't need special depth stuff\n\tif (r_shadow_shadowmapping.ival)\n\t\treturn false;\n\tif (isDedicated)\n\t\treturn false;\n\treturn\t(r_shadow_realtime_dlight.ival && r_shadow_realtime_dlight_shadows.ival) ||\n\t\t\t(r_shadow_realtime_world.ival && r_shadow_realtime_world_shadows.ival);\n#else\n\treturn false;\n#endif\n}\n\nvoid Sh_RegisterCvars(void)\n{\n#if defined(RTLIGHTS) && !defined(SERVERONLY)\n#define REALTIMELIGHTING \"Realtime Lighting\"\n\tCvar_Register (&r_shadow_scissor,\t\t\t\t\tREALTIMELIGHTING);\n\tCvar_Register (&r_shadow_realtime_world,\t\t\tREALTIMELIGHTING);\n\tCvar_Register (&r_shadow_realtime_world_shadows,\tREALTIMELIGHTING);\n\tCvar_Register (&r_shadow_realtime_dlight,\t\t\tREALTIMELIGHTING);\n\tCvar_Register (&r_shadow_realtime_dlight_ambient,\tREALTIMELIGHTING);\n\tCvar_Register (&r_shadow_realtime_dlight_diffuse,\tREALTIMELIGHTING);\n\tCvar_Register (&r_shadow_realtime_dlight_specular,\tREALTIMELIGHTING);\n\tCvar_Register (&r_shadow_realtime_dlight_shadows,\tREALTIMELIGHTING);\n\tCvar_Register (&r_shadow_realtime_world_lightmaps,\tREALTIMELIGHTING);\n\tCvar_Register (&r_shadow_playershadows,\t\t\t\tREALTIMELIGHTING);\n\tCvar_Register (&r_shadow_raytrace,\t\t\t\t\tREALTIMELIGHTING);\n\tCvar_Register (&r_shadow_shadowmapping,\t\t\t\tREALTIMELIGHTING);\n\tCvar_Register (&r_shadow_shadowmapping_precision,\tREALTIMELIGHTING);\n\tCvar_Register (&r_shadow_shadowmapping_nearclip,\tREALTIMELIGHTING);\n\tCvar_Register (&r_shadow_shadowmapping_bias,\t\tREALTIMELIGHTING);\n\tCvar_Register (&r_sun_dir,\t\t\t\t\t\t\tREALTIMELIGHTING);\n\tCvar_Register (&r_sun_colour,\t\t\t\t\t\tREALTIMELIGHTING);\n\tCvar_Register (&r_shadows_fakedistance,\t\t\t\tREALTIMELIGHTING);\n\tCvar_Register (&r_shadows_throwdirection,\t\t\tREALTIMELIGHTING);\n\tCvar_Register (&r_shadows_focus,\t\t\t\t\tREALTIMELIGHTING);\n\tCvar_Register (&r_shadow_shadowmapping_depthbits,\tREALTIMELIGHTING);\n#endif\n}\n"
  },
  {
    "path": "engine/gl/gl_terrain.h",
    "content": "#ifdef TERRAIN\r\n\r\n//#define STRICTEDGES\t//strict (ugly) grid\r\n#define TERRAINTHICKNESS 16\r\n#define TERRAINACTIVESECTIONS 3000\r\n\r\n/*\r\na note on networking:\r\nBy default terrain is NOT networked. This means content is loaded without networking delays.\r\nIf you wish to edit the terrain collaboratively, you can enable the mod_terrain_networked cvar.\r\nWhen set, changes on the server will notify clients that a section has changed, and the client will reload it as needed.\r\nChanges on the client WILL NOT notify the server, and will get clobbered if the change is also made on the server.\r\nThis means for editing purposes, you MUST funnel it via ssqc with your own permission checks.\r\nIt also means for explosions and local stuff, the server will merely restate changes from impacts if you do them early. BUT DO NOT CALL THE EDIT FUNCTION IF THE SERVER HAS ALREADY APPLIED THE CHANGE.\r\n*/\r\n/*\r\nterminology:\r\ntile:\r\n\ta single grid tile of 2*2 height samples.\r\n\titerrated for collisions but otherwise unused.\r\nsection:\r\n\t16*16 tiles, with a single texture spread over them.\r\n\tsamples have an overlap with the neighbouring section (so 17*17 height samples). texture samples do not quite match height frequency (63*63 vs 16*16).\r\n\tsmallest unit for culling.\r\nblock:\r\n\t16*16 sections. forms a single disk file. used only to avoid 16777216 files in a single directory, instead getting 65536 files for a single fully populated map... much smaller...\r\n\teach block file is about 4mb each. larger can be detrimental to automatic downloads.\r\ncluster:\r\n\t64*64 sections\r\n\tinternal concept to avoid a single pointer array of 16 million entries per terrain.\r\n*/\r\n\r\nint Surf_NewLightmaps(int count, int width, int height, uploadfmt_t fmt, qboolean deluxe);\r\n\r\n#define MAXCLUSTERS 64\r\n#define MAXSECTIONS 64\t//this many sections within each cluster in each direction\r\n#define SECTHEIGHTSIZE 17 //this many height samples per section (one for overlap)\r\n#define SECTTEXSIZE 64\t//this many texture samples per section (one for overlap, yes, this is a little awkward)\r\n#define SECTIONSPERBLOCK 16\r\n\r\n//each section is this many sections higher in world space, to keep the middle centered at '0 0'\r\n#define CHUNKBIAS\t(MAXCLUSTERS*MAXSECTIONS/2)\r\n#define CHUNKLIMIT\t(MAXCLUSTERS*MAXSECTIONS)\r\n\r\n#define LMCHUNKS 8//(LMBLOCK_WIDTH/SECTTEXSIZE)\t//FIXME: make dynamic.\r\n#define HMLMSTRIDE (LMCHUNKS*SECTTEXSIZE)\r\n\r\n#define SECTION_MAGIC (*(int*)\"HMMS\")\r\n#define SECTION_VER_DEFAULT\t1\r\n/*simple version history:\r\nver=0\r\n\tSECTHEIGHTSIZE=16\r\nver=1\r\n\tSECTHEIGHTSIZE=17 (oops, makes holes more usable)\r\n\t(holes in this format are no longer supported)\r\nver=2\r\n\tuses deltas instead of absolute values\r\n\tvariable length image names\r\n*/\r\n\r\n#define TGS_NOLOAD\t\t\t0\r\n#define TGS_LAZYLOAD\t\t1\t//see if its available, if not, queue it. don't create too much work at once. peace man\r\n#define TGS_TRYLOAD\t\t\t2\t//try and get it, but don't stress if its not available yet\r\n#define TGS_WAITLOAD\t\t4\t//load it, wait for it if needed.\r\n#define TGS_ANYSTATE\t\t8\t//returns the section regardless of its current state, even if its loading.\r\n#define TGS_NODOWNLOAD\t\t16\t//don't queue it for download\r\n#define TGS_NORENDER\t\t32\t//don't upload any textures or whatever\r\n#define TGS_DEFAULTONFAIL\t64\t//if it failed to load, generate a default anyway\r\n\r\nenum\r\n{\r\n\t//these flags can be found on disk\r\n\tTSF_HASWATER_V0\t= 1u<<0,\t//no longer flagged.\r\n\tTSF_HASCOLOURS\t= 1u<<1,\r\n\tTSF_HASHEIGHTS\t= 1u<<2,\r\n\tTSF_HASSHADOW\t= 1u<<3,\r\n\r\n\t//these flags are found only on disk\r\n\tTSF_D_UNUSED1\t= 1u<<28,\r\n\tTSF_D_UNUSED2\t= 1u<<29,\r\n\tTSF_D_UNUSED3\t= 1u<<30,\r\n\tTSF_D_UNUSED4\t= 1u<<31,\r\n\r\n\t//these flags should not be found on disk\r\n\tTSF_NOTIFY\t\t= 1u<<28,\t//modified on server, waiting for clients to be told about the change.\r\n\tTSF_RELIGHT\t\t= 1u<<29,\t//height edited, needs relighting.\r\n\tTSF_DIRTY\t\t= 1u<<30,\t//its heightmap has changed, the mesh needs rebuilding\r\n\tTSF_EDITED\t\t= 1u<<31\t//says it needs to be written if saved\r\n\r\n#define TSF_INTERNAL\t(TSF_RELIGHT|TSF_DIRTY|TSF_EDITED|TSF_NOTIFY)\r\n};\r\nenum\r\n{\r\n\tTMF_SCALE\t= 1u<<0,\r\n\t//what else do we want? alpha? colormod perhaps?\r\n};\r\n\r\ntypedef struct\r\n{\r\n\tint size;\r\n\tvec3_t axisorg[4];\r\n\tfloat scale;\r\n\tint reserved3;\r\n\tint reserved2;\r\n\tint reserved1;\r\n\t//char modelname[1+];\r\n} dsmesh_v1_t;\r\n\r\ntypedef struct\r\n{\r\n\tunsigned int flags;\r\n\tchar texname[4][32];\r\n\tunsigned int texmap[SECTTEXSIZE][SECTTEXSIZE];\r\n\tfloat heights[SECTHEIGHTSIZE*SECTHEIGHTSIZE];\r\n\tunsigned short holes;\r\n\tunsigned short reserved0;\r\n\tfloat waterheight;\r\n\tfloat minh;\r\n\tfloat maxh;\r\n\tint ents_num;\r\n\tint reserved1;\r\n\tint reserved4;\r\n\tint reserved3;\r\n\tint reserved2;\r\n} dsection_v1_t;\r\n\r\n//file header for a single section\r\ntypedef struct\r\n{\r\n\tint magic;\r\n\tint ver;\r\n} dsection_t;\r\n\r\n//file header for a block of sections.\r\n//(because 16777216 files in a single directory is a bad plan. windows really doesn't like it.)\r\ntypedef struct\r\n{\r\n\t//a block is a X*Y group of sections\r\n\t//if offset==0, the section isn't present.\r\n\t//the data length of the section preceeds the actual data.\r\n\tint magic;\r\n\tint ver;\r\n\tunsigned int offset[SECTIONSPERBLOCK*SECTIONSPERBLOCK];\r\n} dblock_t;\r\n\r\ntypedef struct hmpolyset_s\r\n{\r\n\tstruct hmpolyset_s *next;\r\n\tshader_t *shader;\r\n\tmesh_t mesh;\r\n\tmesh_t *amesh;\r\n\tvbo_t vbo;\r\n} hmpolyset_t;\r\nstruct hmwater_s\r\n{\r\n\tstruct hmwater_s *next;\r\n\tunsigned int contentmask;\r\n\tqboolean simple;\t//no holes, one height\r\n\tfloat minheight;\r\n\tfloat maxheight;\r\n\tchar shadername[MAX_QPATH];\r\n\tshader_t *shader;\r\n\tqbyte holes[8];\r\n\tfloat heights[9*9];\r\n\r\n/*\r\n\tqboolean facesdown;\r\n\tunsigned int contentmask;\r\n\tfloat\t\theights[SECTHEIGHTSIZE*SECTHEIGHTSIZE];\r\n#ifndef SERVERONLY\r\n\tbyte_vec4_t\tcolours[SECTHEIGHTSIZE*SECTHEIGHTSIZE];\r\n\tchar texname[4][MAX_QPATH];\r\n\tint lightmap;\r\n\tint lmx, lmy;\r\n\r\n\ttexnums_t textures;\r\n\tvbo_t vbo;\r\n\tmesh_t mesh;\r\n\tmesh_t *amesh;\r\n#endif\r\n*/\r\n};\r\nenum\r\n{\r\n\tTSLS_NOTLOADED,\r\n\tTSLS_LOADING0,\t//posted to a worker, but not picked up yet. this allows a single worker to generate multiple sections without fighting.\r\n\tTSLS_LOADING1,\t//section is queued to the worker (and may be loaded as part of another section)\r\n\tTSLS_LOADING2,\t//waiting for main thread to finish, worker will ignore\r\n\tTSLS_LOADED,\r\n\tTSLS_FAILED\r\n};\r\ntypedef struct\r\n{\r\n\tlink_t recycle;\r\n\tint sx, sy;\r\n\r\n\tint loadstate;\r\n\tfloat timestamp;\t//don't recycle it if its still fairly recent.\r\n\r\n\tfloat heights[SECTHEIGHTSIZE*SECTHEIGHTSIZE];\r\n\tunsigned char holes[8];\r\n\tunsigned int flags;\r\n\tfloat maxh_cull;\t//includes water+mesh heights\r\n\tfloat minh, maxh;\r\n\tstruct heightmap_s *hmmod;\r\n\r\n\t//FIXME: make layers, each with their own holes+heights+contents+textures+shader+mixes. water will presumably have specific values set for each part.\r\n\tstruct hmwater_s *water;\r\n\r\n\tsize_t traceseq;\r\n\r\n#ifndef SERVERONLY\r\n\tpvscache_t pvscache;\r\n\tvec4_t colours[SECTHEIGHTSIZE*SECTHEIGHTSIZE];\t//FIXME: make bytes\r\n\tchar texname[4][MAX_QPATH];\t//fixme: make path length dynamic.\r\n\tint lightmap;\r\n\tint lmx, lmy;\r\n\r\n\ttexnums_t textures;\r\n\tvbo_t vbo;\r\n\tmesh_t mesh;\r\n\tmesh_t *amesh;\r\n\r\n\thmpolyset_t *polys;\r\n#endif\r\n\tint numents;\r\n\tint maxents;\r\n\tstruct hmentity_s **ents;\r\n} hmsection_t;\r\ntypedef struct\r\n{\r\n\thmsection_t *section[MAXSECTIONS*MAXSECTIONS];\r\n} hmcluster_t;\r\n\r\n#ifndef SERVERONLY\r\ntypedef struct brushbatch_s\r\n{\r\n\tvbo_t vbo;\r\n\tmesh_t mesh;\r\n\tmesh_t *pmesh;\r\n\tint lightmap;\r\n\tstruct brushbatch_s *next;\r\n\tavec4_t align;\t//meh, cos we can.\r\n} brushbatch_t;\r\n#endif\r\ntypedef struct brushtex_s\r\n{\r\n\tchar shadername[MAX_QPATH];\r\n#ifndef SERVERONLY\r\n\tshader_t *shader;\r\n\r\n\t//for rebuild performance\r\n\tint firstlm;\r\n\tint lmcount;\r\n\r\n\tstruct brushbatch_s *batches;\r\n#endif\r\n\tqboolean rebuild;\r\n\tstruct brushtex_s *next;\r\n} brushtex_t;\r\n\r\ntypedef struct patchtessvert_s\r\n{\r\n\tvec3_t v;\r\n\tvec4_t rgba;\r\n\tvec2_t tc;\r\n//\tvec3_t norm;\r\n//\tvec3_t sdir;\r\n//\tvec3_t tdir;\r\n} patchtessvert_t;\r\ntypedef struct qcpatchvert_s\r\n{\r\n\tvec3_t v;\r\n\tvec4_t rgba;\r\n\tvec2_t tc;\r\n} qcpatchvert_t;\r\npatchtessvert_t *PatchInfo_Evaluate(const qcpatchvert_t *cp, const unsigned short patch_cp[2], const short subdiv[2], unsigned short *size);\r\nunsigned int PatchInfo_EvaluateIndexes(const unsigned short *size, index_t *out_indexes);\r\n\r\ntypedef struct\r\n{\r\n\tunsigned int\tcontents;\t//bitmask\r\n\tunsigned int\tid;\t\t\t//networked/gamecode id.\r\n\tunsigned int\taxialplanes;\t//+x,+y,+z,-x,-y,-z. used for bevel stuff.\r\n\tunsigned int\tnumplanes;\r\n\tunsigned char\tselected:1;\t//different shader stuff\r\n\tunsigned char\tispatch:1;\t//just for parsing really\r\n\tvec4_t\t\t\t*planes;\r\n\tvec3_t\t\t\tmins, maxs;\t//for optimisation and stuff\r\n\tstruct patchdata_s\r\n\t{\t//unlit, always...\r\n\t\tbrushtex_t *tex;\r\n\t\tunsigned short numcp[2];\r\n\t\tshort subdiv[2];\t//<0=regular q3 patch, 0=cp-only, >0=fixed-tessellation.\r\n\r\n\t\tunsigned short tesssize[2];\r\n\t\tpatchtessvert_t *tessvert; //x+(y*tesssize[0])\r\n\r\n\t\t//control points\r\n\t\tqcpatchvert_t cp[1]; //x+(y*numcp[0]) extends past end of patchdata_s\r\n\t} *patch;\t//if this is NULL, then its a regular brush. otherwise its a patch.\r\n\tstruct brushface_s\r\n\t{\r\n\t\tbrushtex_t *tex;\r\n\t\tvec4_t stdir[2];\r\n\t\tvec3_t *points;\r\n\t\tunsigned short numpoints;\r\n\t\tunsigned short lmscale;\r\n\t\tint\tlightmap;\r\n\t\tunsigned short lmbase[2];\t//min st coord of the lightmap atlas, in texels.\r\n\t\tunsigned short relight:1;\r\n\t\tunsigned short relit:1;\r\n\t\tint lmbias[2];\r\n\t\tunsigned short lmextents[2];\r\n\t\tunsigned int surfaceflags;\t//used by q2\r\n\t\tunsigned int surfacevalue;\t//used by q2 (generally light levels)\r\n\t\tqbyte *lightdata;\r\n\t} *faces;\r\n} brushes_t;\r\n\r\ntypedef struct\r\n{\r\n\tstring_t\tshadername;\r\n\tvec3_t\t\tplanenormal;\r\n\tfloat\t\tplanedist;\r\n\tvec3_t\t\tsdir;\r\n\tfloat\t\tsbias;\r\n\tvec3_t\t\ttdir;\r\n\tfloat\t\ttbias;\r\n} qcbrushface_t;\r\ntypedef struct\r\n{\r\n\tstring_t\tshadername;\r\n\tunsigned int contents;\r\n\tunsigned int cp_width;\r\n\tunsigned int cp_height;\r\n\tunsigned int subdiv_x;\r\n\tunsigned int subdiv_y;\r\n\tvec3_t\t\ttexinfo;\r\n} qcpatchinfo_t;\r\n\r\ntypedef struct heightmap_s\r\n{\r\n\tchar path[MAX_QPATH];\r\n\tchar skyname[MAX_QPATH];\t\t\t//name of the skybox\r\n\tchar groundshadername[MAX_QPATH];\t//this is the shader we're using to draw the terrain itself. you could use other shaders here, for eg debugging or stylised weirdness.\r\n\tunsigned int culldistance;\t\t\t//entities will be culled if they're this far away (squared distance\r\n\tfloat\tmaxdrawdist;\t\t\t\t//maximum view distance. extends view if larger than fog implies.\r\n\r\n\tunsigned char *seed;\t\t\t\t//used by whatever terrain generator.\r\n\tqboolean forcedefault;\t\t\t\t//sections that cannot be loaded/generated will receive default values for stuff.\r\n\tchar defaultgroundtexture[MAX_QPATH];//texture used for defaulted sections\r\n\tchar defaultwatershader[MAX_QPATH];\t//shader used for defaulted sections that have heights beneath defaultwaterheight.\r\n\tfloat defaultwaterheight;\t\t\t//water height. if you want your islands to be surrounded by water.\r\n\tfloat defaultgroundheight;\t\t\t//defaulted sections will have a z plane this high\r\n\r\n\tint firstsegx, firstsegy; //min bounds of the terrain, in sections\r\n\tint maxsegx, maxsegy; //max bounds of the terrain, in sections\r\n\tfloat sectionsize;\t//each section is this big, in world coords\r\n\thmcluster_t *cluster[MAXCLUSTERS*MAXCLUSTERS];\r\n\tshader_t *skyshader;\r\n\tshader_t *shader;\r\n\tmesh_t skymesh;\r\n\tmesh_t *askymesh;\r\n\tqboolean\tlegacyterrain;\t//forced exterior=SOLID\r\n\tunsigned int exteriorcontents;\t//contents type outside of the terrain sections area (.map should be empty, while terrain will usually block).\r\n\tunsigned int loadingsections;\t//number of sections currently being loaded. avoid loading extras while non-zero.\r\n\tsize_t traceseq;\r\n\tsize_t drawnframe;\r\n\tenum\r\n\t{\r\n\t\tDGT_SOLID,\t//invalid/new areas should be completely solid until painted.\r\n\t\tDGT_HOLES,\t//invalid/new sections should be non-solid+invisible\r\n\t\tDGT_FLAT\t//invalid/new sections should be filled with ground by default\r\n\t} defaultgroundtype;\r\n\tenum\r\n\t{\r\n\t\tHMM_TERRAIN,\r\n\t\tHMM_BLOCKS\r\n\t} mode;\r\n\tint tilecount[2];\r\n\tint tilepixcount[2];\r\n\r\n\tint activesections;\r\n\tlink_t recycle;\t\t//section list in lru order\r\n//\tlink_t collected;\t//memory that may be reused, to avoid excess reallocs.\r\n\r\n\tstruct hmentity_s\r\n\t{\r\n\t\tsize_t drawnframe;\t//don't add it to the scene multiple times.\r\n\t\tsize_t traceseq;\t//don't trace through this entity multiple times if its in different sections.\r\n\t\tint refs;\t\t//entity is free/reusable when its no longer referenced by any sections\r\n\t\tentity_t ent;\t//note: only model+modelmatrix info is relevant. fixme: implement instancing.\r\n\r\n\t\tstruct hmentity_s *next;\t//used for freeing/allocating an entity\r\n\t} *entities;\r\n\tvoid *entitylock;\t//lock this if you're going to read/write entities of any kind.\r\n\r\n#ifndef SERVERONLY\r\n\tunsigned int numusedlmsects;\t//to track leaks and stats\r\n\tunsigned int numunusedlmsects;\r\n\tstruct lmsect_s\r\n\t{\r\n\t\tstruct lmsect_s *next;\r\n\t\tint lm, x, y;\r\n\t} *unusedlmsects;\r\n#endif\r\n\r\n#ifndef SERVERONLY\r\n\t//I'm putting this here because we might have some quite expensive lighting routines going on\r\n\t//and that'll make editing the terrain jerky as fook, so relighting it a few texels at a time will help maintain a framerate while editing\r\n\thmsection_t *relight;\r\n\tunsigned int relightidx;\r\n\tvec2_t relightmin;\r\n#endif\r\n\r\n\r\n\t\r\n\tchar *texmask;\t//for editing - visually masks off the areas which CANNOT accept this texture\r\n\tqboolean entsdirty;\t//ents were edited, so we need to reload all lighting...\r\n\tstruct relight_ctx_s *relightcontext;\r\n\tstruct llightinfo_s *lightthreadmem;\r\n\tqboolean inheritedlightthreadmem;\r\n\tqboolean recalculatebrushlighting;\r\n\tlmalloc_t brushlmalloc;\r\n\tfloat brushlmscale;\r\n\tunsigned int *brushlmremaps;\r\n\tunsigned int brushmaxlms;\r\n\tbrushtex_t *brushtextures;\r\n\tbrushes_t *wbrushes;\r\n\tunsigned int numbrushes;\r\n\tunsigned int brushidseq;\r\n\tqboolean brushesedited;\r\n} heightmap_t;\r\n\r\ntypedef struct plugterrainfuncs_s\r\n{\r\n\tvoid\t\t\t*(QDECL *GenerateWater)\t\t\t(hmsection_t *s, float maxheight);\r\n\tqboolean\t\t (QDECL *InitLightmap)\t\t\t(hmsection_t *s, qboolean initialise);\r\n\tunsigned char\t*(QDECL *GetLightmap)\t\t\t(hmsection_t *s, int idx, qboolean edit);\r\n\tvoid\t\t\t (QDECL *AddMesh)\t\t\t\t(heightmap_t *hm, int loadflags, model_t *mod, const char *modelname, vec3_t epos, vec3_t axis[3], float scale);\r\n\thmsection_t\t\t*(QDECL *GetSection)\t\t\t(heightmap_t *hm, int x, int y, unsigned int flags);\r\n\tint\t\t\t\t (QDECL *GenerateSections)\t\t(heightmap_t *hm, int sx, int sy, int count, hmsection_t **sects);\r\n\tvoid\t\t\t (QDECL *FinishedSection)\t\t(hmsection_t *s, qboolean success);\r\n\r\n\tqboolean\t\t (QDECL *AutogenerateSection)(heightmap_t *hm, int sx, int sy, unsigned int tgsflags);\t//replace this if you want to make a terrain generator.\r\n#define plugterrainfuncs_name \"Terrain\"\r\n} plugterrainfuncs_t;\r\n#endif\r\n"
  },
  {
    "path": "engine/gl/gl_vidcocoa.m",
    "content": "/*\r\n \r\n Copyright (C) 2001-2002       A Nourai\r\n Copyright (C) 2006            Jacek Piszczek (Mac OSX port)\r\n \r\n This program is free software; you can redistribute it and/or\r\n modify it under the terms of the GNU General Public License\r\n as published by the Free Software Foundation; either version 2\r\n of the License, or (at your option) any later version.\r\n \r\n This program is distributed in the hope that it will be useful,\r\n but WITHOUT ANY WARRANTY; without even the implied warranty of\r\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  \r\n \r\n See the included (GNU.txt) GNU General Public License for more details.\r\n \r\n You should have received a copy of the GNU General Public License\r\n along with this program; if not, write to the Free Software\r\n Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\r\n*/\r\n\r\n#import <Cocoa/Cocoa.h>\r\n#import <Quartz/Quartz.h>\r\n#include \"quakedef.h\"\r\n\r\nid _p;\r\nint evcnt = 2;\r\n\r\n// Jacek: some keys are bogus, my ibook kb lacks keys and apple's docs lack\r\n// keyCode documentation\r\n\r\nunsigned char keyconv[] =\r\n{\r\n\t'a', /* 0 */\r\n\t's',\r\n\t'd',\r\n\t'f',\r\n\t'h',\r\n\t'g',\r\n\t'z',\r\n\t'x',\r\n\t'c',\r\n\t'v',\r\n\t'0', /* 10 */\r\n\t'b',\r\n\t'q',\r\n\t'w',\r\n\t'e',\r\n\t'r',\r\n\t'y',\r\n\t't',\r\n\t'1',\r\n\t'2',\r\n\t'3', /* 20 */\r\n\t'4',\r\n\t'6',\r\n\t'5',\r\n\t'=',\r\n\t'9',\r\n\t'7',\r\n\t'-',\r\n\t'8',\r\n\t'0',\r\n\t']', /* 30 */\r\n\t'o',\r\n\t'u',\r\n\t'[',\r\n\t'i',\r\n\t'p',\r\n\tK_ENTER,\r\n\t'l',\r\n\t'j',\r\n\t'\\'',\r\n\t'k', /* 40 */\r\n\t';',\r\n\t'\\\\',\r\n\t',',\r\n\t'/',\r\n\t'n',\r\n\t'm',\r\n\t'.',\r\n\tK_TAB,\r\n\tK_SPACE,\r\n\t'`', /* 50 */\r\n\tK_BACKSPACE,\r\n\t'v',\r\n\tK_ESCAPE,\r\n\t'n',\r\n\t'm',\r\n\t',',\r\n\t'.',\r\n\t'/',\r\n\t0,\r\n\tK_KP_DEL, /* 60 */\r\n\tK_KP_HOME,\r\n\tK_KP_UPARROW,\r\n\tK_KP_PGUP,\r\n\t' ',\r\n\tK_KP_DEL,\r\n\tK_TAB,\r\n\tK_KP_STAR,\r\n\tK_ENTER,\r\n\tK_KP_PLUS,\r\n\tK_DEL, /* 70 */\r\n\tK_INS,\r\n\tK_PGUP,\r\n\tK_PGDN,\r\n\tK_KP_MINUS,\r\n\tK_KP_SLASH,\r\n\tK_KP_ENTER,\r\n\t0,\r\n\tK_KP_MINUS,\r\n\t0,\r\n\tK_F1, /* 80 */\r\n\tK_F2,\r\n\tK_KP_INS,\r\n\tK_KP_END,\r\n\tK_KP_DOWNARROW,\r\n\tK_KP_PGDN,\r\n\tK_KP_LEFTARROW,\r\n\tK_KP_5,\r\n\tK_KP_RIGHTARROW,\r\n\tK_KP_HOME,\r\n\t0, /* 90 */\r\n\tK_KP_UPARROW,\r\n\tK_KP_PGUP,\r\n\t0,\r\n\tK_KP_PLUS,\r\n\t0,\r\n\tK_LSHIFT,\r\n\tK_RSHIFT,\r\n\t0,\r\n\tK_RCTRL,\r\n\tK_ALT, /* 100 */\r\n\tK_ALT,\r\n\t0,\r\n\t0,\r\n\t0,\r\n\t0,\r\n\t0,\r\n\t0,\r\n\t0,\r\n\t0,\r\n\tK_PAUSE, /* 110 */\r\n\tK_F12,\r\n\t0,\r\n\t0,\r\n\t0,\r\n\tK_HOME,\r\n\tK_PGUP,\r\n\tK_DEL,\r\n\t0,\r\n\tK_END,\r\n\t0, /* 120 */\r\n\tK_PGDN,\r\n\t0,\r\n\tK_LEFTARROW,\r\n\tK_RIGHTARROW,\r\n\tK_DOWNARROW,\r\n\tK_UPARROW,\r\n\t0,\r\n\t0,\r\n\t0,\r\n\t0, /* 130 */\r\n\t0,\r\n\t0,\r\n\t0,\r\n\t0,\r\n\t0,\r\n\t0,\r\n\t0,\r\n\t0,\r\n\t0,\r\n\t0, /* 140 */\r\n\t0,\r\n\t0,\r\n\t0,\r\n\t0,\r\n\t0,\r\n\t0,\r\n\t0,\r\n\t0,\r\n\t0,\r\n\t0, /* 150 */\r\n\t0,\r\n\t0,\r\n\t0,\r\n\t0,\r\n\t0,\r\n\t0,\r\n\t0,\r\n\t0,\r\n\t0,\r\n\t0, /* 160 */\r\n\t0,\r\n\t0,\r\n\t0,\r\n\t0,\r\n\t0,\r\n\t0,\r\n\t0,\r\n\t0,\r\n\t0,\r\n\t0, /* 170 */\r\n\t0,\r\n\t0,\r\n\t0,\r\n\t0,\r\n\t0,\r\n\t0,\r\n\t0,\r\n\t0,\r\n\t0,\r\n\t0, /* 180 */\r\n\t0,\r\n\t0,\r\n\t0,\r\n\t0,\r\n\t0,\r\n\t0,\r\n\t0,\r\n\t0,\r\n\t0,\r\n\t0, /* 190 */\r\n\t0,\r\n\t0,\r\n\t0,\r\n\t0,\r\n\t0,\r\n\t0,\r\n\t0,\r\n\t0,\r\n\t0,\r\n\t0, /* 200 */\r\n\t0,\r\n\t0,\r\n\t0,\r\n\t0,\r\n\t0,\r\n\t0,\r\n\t0,\r\n\t0,\r\n\t0,\r\n\t0, /* 210 */\r\n\t0,\r\n\t0,\r\n\t0,\r\n\t0,\r\n\t0,\r\n\t0,\r\n\t0,\r\n\t0,\r\n\t0,\r\n\t0, /* 220 */\r\n\t0,\r\n\t0,\r\n\t0,\r\n\t0,\r\n\t0,\r\n\t0,\r\n\t0,\r\n\t0,\r\n\t0,\r\n\t0, /* 230 */\r\n\t0,\r\n\t0,\r\n\t0,\r\n\t0,\r\n\t0,\r\n\t0,\r\n\t0,\r\n\t0,\r\n\t0,\r\n\t0, /* 240 */\r\n\t0,\r\n\t0,\r\n\t0,\r\n\t0,\r\n\t0,\r\n\t0,\r\n\t0,\r\n\t0,\r\n\t0,\r\n\t0, /* 250 */\r\n\t0,\r\n\t0,\r\n\t0,\r\n\t0,\r\n\t0,\r\n\t0,\r\n\t0,\r\n\t0,\r\n\t0\r\n};\r\n\r\n// validate the depth\r\nint checkDepth(int d)\r\n{\r\n\tif (d == 24)\r\n\t\t d = 32;\r\n\tif (d != 15 && d != 16 && d != 32)\r\n\t\t d = 32;\r\n    \r\n\treturn d;\r\n}\r\n\r\n@interface FTEApplication : NSApplication\r\n{\r\n\tNSOpenGLContext    *_openGLContext;\r\n\tNSTimer            *_timer;\r\n\tdouble time, oldtime, newtime;\r\n\tunsigned int        oldmflags;\r\n\tCFDictionaryRef     olddmode;\r\n}\r\n- (void)initDisplayWidth:(int)width height:(int)height depth:(int)depth;\r\n- (void)flushBuffer;\r\n- (void)runLoop:(NSTimer *)timer;\r\n@end\r\n\r\n@implementation FTEApplication\r\n\r\n- (id)init\r\n{\r\n\tif((self = [super init]))\r\n\t\t[self setDelegate:self];\r\n\t\r\n\toldmflags = 0;\r\n    \r\n\treturn self;\r\n}\r\n\r\n- (void)initDisplayWidth:(int)width height:(int)height depth:(int)depth;\r\n{\r\n\tlong                            value = 1;\r\n\tNSOpenGLPixelFormat*            format;\r\n\tNSOpenGLPixelFormatAttribute    attributes[] = {\r\n\t\tNSOpenGLPFAFullScreen,\r\n\t\tNSOpenGLPFAScreenMask,\r\n\t\tCGDisplayIDToOpenGLDisplayMask(kCGDirectMainDisplay),\r\n\t\tNSOpenGLPFANoRecovery,\r\n\t\tNSOpenGLPFADoubleBuffer,\r\n\t\tNSOpenGLPFAAccelerated,\r\n\t\tNSOpenGLPFADepthSize, checkDepth(depth),\r\n\t\t0};\r\n\r\n\tolddmode = CGDisplayCurrentMode(kCGDirectMainDisplay);\r\n\r\n\t// zeros mean we use the default screen! (but with 32bit depth)\r\n\tif (!((width == 0) && (height == 0) && (depth == 0)))\r\n\t{\r\n\t\tdepth = checkDepth(depth);\r\n\t\tif (width == 0)\r\n\t\t\t width = CGDisplayPixelsWide(kCGDirectMainDisplay);\r\n\t\tif (height == 0)\r\n\t\t\t height = CGDisplayPixelsHigh(kCGDirectMainDisplay);\r\n\t\tCFDictionaryRef dmode = CGDisplayBestModeForParameters(\r\n\t\t\tkCGDirectMainDisplay,\r\n\t\t\tcheckDepth(depth),\r\n\t\t\twidth,\r\n\t\t\theight,\r\n\t\t\tFALSE);\r\n\t\tCGDisplaySwitchToMode(kCGDirectMainDisplay,dmode);\r\n\t}\r\n\r\n\t// get screen size\r\n\tvid.pixelwidth = CGDisplayPixelsWide(kCGDirectMainDisplay);\r\n\tvid.pixelheight = CGDisplayPixelsHigh(kCGDirectMainDisplay);\r\n\r\n\t// capture the display!\r\n\tCGDisplayCapture(kCGDirectMainDisplay);\r\n\tCGDisplayHideCursor(kCGDirectMainDisplay);\r\n\tCGAssociateMouseAndMouseCursorPosition(false);\r\n\r\n\tformat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attributes];\r\n\t_openGLContext = [[NSOpenGLContext alloc] \r\n\t\t\tinitWithFormat:format \r\n\t\t\tshareContext:nil];\r\n\t[format release];\r\n\tif(_openGLContext == nil)\r\n\t{\r\n\t\tNSLog(@\"Cannot create OpenGL context\");\r\n\t\t[NSApp terminate:nil];\r\n\t\treturn;\r\n\t}\r\n    \r\n\t[_openGLContext setFullScreen];\r\n\t[_openGLContext setValues:&value forParameter:kCGLCPSwapInterval];\r\n\r\n\t[_openGLContext makeCurrentContext];\r\n    \r\n\t_timer = [[NSTimer scheduledTimerWithTimeInterval:1.0/250.0\r\n\t\ttarget:self \r\n\t\tselector:@selector(runLoop:) \r\n\t\tuserInfo:nil \r\n\t\trepeats:YES] \r\n\t\tretain];\r\n        \r\n\t[[NSApp mainWindow] setAcceptsMouseMovedEvents:YES];\r\n}\r\n\r\n- (void)dealloc\r\n{\r\n\tCGAssociateMouseAndMouseCursorPosition(true);\r\n\tCGDisplayRelease(kCGDirectMainDisplay);\r\n\tCGDisplayRestoreColorSyncSettings();\r\n\tCGDisplaySwitchToMode(kCGDirectMainDisplay,olddmode);\r\n\tCGDisplayMoveCursorToPoint(kCGDirectMainDisplay,\r\n                               CGPointMake(vid.width/2,vid.height/2));\r\n\t[_openGLContext release];\r\n\t[_timer invalidate];\r\n\t[_timer release];\r\n    \r\n\t[super dealloc];\r\n}\r\n\r\n- (void) sendEvent:(NSEvent*)event\r\n{\r\n\tif ([event type] == NSKeyDown)\r\n\t{\r\n\t\tint code = keyconv[[event keyCode]];\r\n\t\tKey_Event(0, code, code>=128?0:code, TRUE);\r\n\t\t//printf(\"%d\\n\",[event keyCode]);\r\n\t\treturn;\r\n\t}\r\n\r\n\tif ([event type] == NSKeyUp)\r\n\t{\r\n\t\tint code = keyconv[[event keyCode]];\r\n\t\tKey_Event(0, code, 0, FALSE);\r\n\t\treturn;\r\n\t}\r\n\r\n\tif ([event type] == NSFlagsChanged)\r\n\t{\r\n\t\tunsigned int mflags = [event modifierFlags];\r\n\r\n\t\tif ((mflags & NSAlternateKeyMask) ^ (oldmflags & NSAlternateKeyMask))\r\n\t\t{\r\n\t\t\tKey_Event(0, K_ALT, 0, (mflags & NSAlternateKeyMask) ? TRUE : FALSE);\r\n\t\t}\r\n\r\n\t\tif ((mflags & NSControlKeyMask) ^ (oldmflags & NSControlKeyMask))\r\n\t\t{\r\n\t\t\tKey_Event(0, K_LCTRL, 0, (mflags & NSControlKeyMask) ? TRUE : FALSE);\r\n\t\t}\r\n        \r\n\t\tif ((mflags & NSShiftKeyMask) ^ (oldmflags & NSShiftKeyMask))\r\n\t\t{\r\n\t\t\tKey_Event(0, K_LSHIFT, 0, (mflags & NSShiftKeyMask) ? TRUE : FALSE);\r\n\t\t}\r\n\r\n\t\tif ((mflags & NSCommandKeyMask) ^ (oldmflags & NSCommandKeyMask))\r\n\t\t{\r\n\t\t\tKey_Event(0, K_LWIN, 0, (mflags & NSCommandKeyMask) ? TRUE : FALSE);\r\n\t\t}\r\n\r\n\t\tif ((mflags & NSAlphaShiftKeyMask) ^ (oldmflags & NSAlphaShiftKeyMask))\r\n\t\t{\r\n\t\t\tKey_Event(0, K_CAPSLOCK, 0, (mflags & NSAlphaShiftKeyMask) ? TRUE : FALSE);\r\n\t\t}\r\n\r\n\t\toldmflags = mflags;\r\n\t\treturn;\r\n\t}\r\n\r\n\tif ([event type] == NSMouseMoved)\r\n\t{\r\n\t\tIN_MouseMove(0, false, [event deltaX], [event deltaY], 0, 0);\r\n\r\n\t\t// lame hack to avoid mouse ptr moving to the top of the screen since\r\n\t\t// a click there causes the mouse to appear and lock the event stream\r\n\t\t// Apple sucks :(\r\n\t\t// NOTE: it seems this is still needed for 10.3.x!\r\n\t\tCGDisplayMoveCursorToPoint(kCGDirectMainDisplay,\r\n\t\t\tCGPointMake(vid.width - 1,vid.height - 1));\r\n\t\treturn;\r\n\t}\r\n\r\n\tif ([event type] == NSLeftMouseDragged)\r\n\t{\r\n\t\tIN_MouseMove(0, false, [event deltaX], [event deltaY], 0, 0);\r\n\t\tCGDisplayMoveCursorToPoint(kCGDirectMainDisplay,\r\n\t\t\tCGPointMake(vid.width - 1,vid.height - 1));\r\n\t\treturn;\r\n\t}\r\n\r\n\tif ([event type] == NSRightMouseDragged)\r\n\t{\r\n\t\tIN_MouseMove(0, false, [event deltaX], [event deltaY], 0, 0);\r\n\t\tCGDisplayMoveCursorToPoint(kCGDirectMainDisplay,\r\n\t\t\tCGPointMake(vid.width - 1,vid.height - 1));\r\n\t\treturn;\r\n\t}\r\n\r\n\tif ([event type] == NSOtherMouseDragged)\r\n\t{\r\n\t\tIN_MouseMove(0, false, [event deltaX], [event deltaY], 0, 0);\r\n\t\tCGDisplayMoveCursorToPoint(kCGDirectMainDisplay,\r\n\t\t\tCGPointMake(vid.width - 1,vid.height - 1));\r\n\t\treturn;\r\n\t}\r\n\r\n\tif ([event type] == NSLeftMouseDown)\r\n\t{\r\n\t\tKey_Event(0, K_MOUSE1, 0, TRUE);\r\n\t\treturn;\r\n\t}\r\n\r\n\tif ([event type] == NSLeftMouseUp)\r\n\t{\r\n\t\tKey_Event(0, K_MOUSE1, 0, FALSE);\r\n\t\treturn;\r\n\t}\r\n\r\n\tif ([event type] == NSRightMouseDown)\r\n\t{\r\n\t\tKey_Event(0, K_MOUSE2, 0, TRUE);\r\n\t\treturn;\r\n\t}\r\n\r\n\tif ([event type] == NSRightMouseUp)\r\n\t{\r\n\t\tKey_Event(0, K_MOUSE2, 0, FALSE);\r\n\t\treturn;\r\n\t}\r\n\r\n\tif ([event type] == NSOtherMouseDown)\r\n\t{\r\n\t\tKey_Event(0, K_MOUSE3, 0, TRUE);\r\n\t\treturn;\r\n\t}\r\n\r\n\tif ([event type] == NSOtherMouseUp)\r\n\t{\r\n\t\tKey_Event(0, K_MOUSE3, 0, FALSE);\r\n\t\treturn;\r\n\t}\r\n    \r\n\tif ([event type] == NSScrollWheel)\r\n\t{\r\n\t\tKey_Event(0, ([event deltaY] > 0.0) ? K_MWHEELUP : K_MWHEELDOWN, 0, TRUE);\r\n\t\treturn;\r\n\t}\r\n}    \r\n\r\n- (void)flushBuffer\r\n{\r\n\t// synchronise display\r\n\t[_openGLContext flushBuffer];\r\n}\r\n\r\n// called on a timer event\r\n- (void)runLoop:(NSTimer *)timer\r\n{\r\n\tnewtime = Sys_DoubleTime ();\r\n\ttime = newtime - oldtime;\r\n\toldtime = newtime;\r\n    \r\n\tHost_Frame(time);\r\n}\r\n\r\n- (void)run\r\n{\r\n\toldtime = Sys_DoubleTime ();\r\n\t[super run];\r\n}\r\n\r\n@end\r\n\r\nstatic FTEApplication *fteglapp;\r\n\r\nBOOL initCocoa(rendererstate_t *info)\r\n{\r\n\t// init the application the hard way since we don't want to run it\r\n\t// immediately\r\n\tNSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];\r\n\tfteglapp = [FTEApplication sharedApplication];\r\n\r\n\t// store the var for later disposal\r\n\t_p = pool;\r\n\r\n\t// init the display\r\n\t[fteglapp initDisplayWidth:info->width height:info->height depth:info->bpp];\r\n\r\n\treturn TRUE;\r\n}\r\n\r\nqboolean glcocoaRunLoop(void)\r\n{\r\n\tif (!fteglapp)\r\n\t\treturn false;\r\n\t// this will initialise the NSTimer and run the app\r\n\t[NSApp run];\r\n\r\n\treturn true;\r\n}\r\n\r\nvoid killCocoa(void)\r\n{\r\n\t// terminates FTEApplicaiton\r\n\t[NSApp terminate:nil];\r\n\t[_p release];\r\n\r\n\tfteglapp = NULL;\r\n}\r\n\r\nvoid flushCocoa(void)\r\n{\r\n\t// synchronises display\r\n\t[NSApp flushBuffer];\r\n}\r\n\r\nvoid cocoaGamma(unsigned short *r,unsigned short *g,unsigned short *b)\r\n{\r\n\tuint8_t gammatable[3*256];\r\n\tint i;\r\n\r\n\t// convert the gamma values\r\n\tfor(i=0;i<256;i++)\r\n\t{\r\n\t\tgammatable[i] = r[i] >> 8;\r\n\t\tgammatable[i+256] = g[i] >> 8;\r\n\t\tgammatable[i+512] = b[i] >> 8;\r\n\t}\r\n    \r\n\t//... and set them\r\n\tCGSetDisplayTransferByByteTable(kCGDirectMainDisplay,256,\r\n\t\tgammatable,\r\n\t\tgammatable + 256,\r\n\t\tgammatable + 512);\r\n}\r\n"
  },
  {
    "path": "engine/gl/gl_vidcommon.c",
    "content": "/*\n\tLingering issues:\n\n\tnvidia vsync:\n\t\t\twith vsync enabled and framerates fluctuating across the 1000fps boundary, there is serious stuttering, like its re-showing the previous frame again.\n\t\t\tthis only happens with windows gl, and not vulkan/d3d so I'm assuming this is a driver bug with it mispredicting timings.\n\t\t\tworkaround: enable bloom or something else that's wasteful in terms of gpu time, to keep it under 1000fps.\n\n\tnouveau vsync:\n\t\t\tvsync seems forced when running fullscreen, but not when running windowed.\n\t\t\tworkaround: run windowed.\n\n\tnouveau framerates:\n\t\t\tnouveau doesn't seem to have any pstate control enabled.\n\t\t\tworkaround: sudo echo AUTO>/sys/kernel/debug/dri/0/pstate\n\t\t\t(you could also use different ids for explicit pstates - eg to return to a low-power state)\n\t\t\t(the engine cannot do this, as it requires root, nor does it know WHICH dri device to control)\n\t\t\t(more recent gpus might not support this at all due to nvidia blocking them, but works for my 750ti)\n\t\t\t(note that nouveau's presentation engine isn't that good, so don't expect 5000fps, but it should make rtlights usable)\n\n\tcore vs compatibility:\n\t\t\tvid_gl_context_compatibility defaults to 1, because it still gives higher framerates (due to streaming vertex data from the cpu).\n*/\n\n#include \"quakedef.h\"\n#ifdef GLQUAKE\n#include \"glquake.h\"\n#include \"gl_draw.h\"\n#include \"shader.h\"\n\n\nextern cvar_t\tgl_immutable_textures;\nextern cvar_t\tgl_immutable_buffers;\n\n#ifndef GL_STATIC\n//standard gles2 opengl calls.\nvoid (APIENTRY *qglBlendFunc) (GLenum sfactor, GLenum dfactor);\nvoid (APIENTRY *qglClear) (GLbitfield mask);\nvoid (APIENTRY *qglClearColor) (GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha);\nvoid (APIENTRY *qglClearStencil) (GLint s);\nvoid (APIENTRY *qglColorMask) (GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha);\nvoid (APIENTRY *qglCopyTexImage2D) (GLenum target, GLint level, GLenum internalFormat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border);\nvoid (APIENTRY *qglCopyTexSubImage2D) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height);\nvoid (APIENTRY *qglCullFace) (GLenum mode);\nvoid (APIENTRY *qglDepthFunc) (GLenum func);\nvoid (APIENTRY *qglDepthMask) (GLboolean flag);\n//void (APIENTRY *qglDepthRangef) (GLclampf zNear, GLclampf zFar);\nvoid (APIENTRY *qglDisable) (GLenum cap);\nvoid (APIENTRY *qglEnable) (GLenum cap);\nvoid (APIENTRY *qglFinish) (void);\nvoid (APIENTRY *qglFlush) (void);\nvoid (APIENTRY *qglGenTextures) (GLsizei n, GLuint *textures);\nvoid (APIENTRY *qglGenerateMipmap)(GLenum target);\nvoid (APIENTRY *qglGetBooleanv) (GLenum pname, GLboolean *params);\nGLenum (APIENTRY *qglGetError) (void);\nvoid (APIENTRY *qglGetFloatv) (GLenum pname, GLfloat *params);\nvoid (APIENTRY *qglGetIntegerv) (GLenum pname, GLint *params);\nconst GLubyte * (APIENTRY *qglGetString) (GLenum name);\nvoid (APIENTRY *qglHint) (GLenum target, GLenum mode);\nGLboolean (APIENTRY *qglIsEnabled) (GLenum cap);\nvoid (APIENTRY *qglPixelStorei) (GLenum pname, GLint param);\nvoid (APIENTRY *qglPolygonOffset) (GLfloat factor, GLfloat units);\nvoid (APIENTRY *qglLineWidth) (GLfloat width);\nvoid (APIENTRY *qglReadPixels) (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid *pixels);\nvoid (APIENTRY *qglTexImage2D) (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid *pixels);\nvoid (APIENTRY *qglTexSubImage2D) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels);\nvoid (APIENTRY *qglTexParameteri) (GLenum target, GLenum pname, GLint param);\nvoid (APIENTRY *qglTexParameterf) (GLenum target, GLenum pname, GLfloat param);\nvoid (APIENTRY *qglTexParameteriv) (GLenum target, GLenum pname, const GLint *params);\nvoid (APIENTRY *qglTexParameterfv) (GLenum target, GLenum pname, const GLfloat *params);\nvoid (APIENTRY *qglViewport) (GLint x, GLint y, GLsizei width, GLsizei height);\nvoid (APIENTRY *qglDrawElements) (GLenum mode, GLsizei count, GLenum type, const GLvoid *indices);\nvoid (APIENTRY *qglDrawArrays) (GLenum mode, GLint first, GLsizei count);\nvoid (APIENTRY *qglScissor) (GLint x, GLint y, GLsizei width, GLsizei height);\nvoid (APIENTRY *qglStencilOp) (GLenum fail, GLenum zfail, GLenum zpass);\nvoid (APIENTRY *qglStencilFunc) (GLenum func, GLint ref, GLuint mask);\nvoid (APIENTRY *qglDeleteTextures) (GLsizei n, const GLuint *textures);\n\nFTEPFNGLCOMPRESSEDTEXIMAGE2DARBPROC qglCompressedTexImage2D;\nvoid (APIENTRY *qglCompressedTexSubImage2D) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *data);\t//gl1.3\n\nvoid (APIENTRY *qglGenFramebuffersEXT)(GLsizei n, GLuint* ids);\nvoid (APIENTRY *qglDeleteFramebuffersEXT)(GLsizei n, const GLuint* ids);\nvoid (APIENTRY *qglBindFramebufferEXT)(GLenum target, GLuint id);\nvoid (APIENTRY *qglDeleteRenderbuffersEXT)(GLsizei n, const GLuint* ids);\nvoid (APIENTRY *qglFramebufferTexture2DEXT)(GLenum target, GLenum attachmentPoint, GLenum textureTarget, GLuint textureId, GLint  level);\nFTEPFNGLVERTEXATTRIBPOINTERPROC\t\t\tqglVertexAttribPointer;\nFTEPFNGLVERTEXATTRIB4FPROC\t\t\t\tqglVertexAttrib4f;\nFTEPFNGLGETVERTEXATTRIBIVPROC\t\t\tqglGetVertexAttribiv;\nFTEPFNGLENABLEVERTEXATTRIBARRAYPROC\t\tqglEnableVertexAttribArray;\nFTEPFNGLDISABLEVERTEXATTRIBARRAYPROC\tqglDisableVertexAttribArray;\nvoid (APIENTRY *qglStencilOpSeparate) (GLenum face, GLenum fail, GLenum zfail, GLenum zpass);\nvoid (APIENTRY *qglGetFramebufferAttachmentParameteriv)(GLenum  target,  GLenum  attachment,  GLenum  pname,  GLint * params);\nvoid (APIENTRY *qglGetVertexAttribPointerv) (GLuint index, GLenum pname, GLvoid* *pointer);\n\n//quick hack that made quake work on both 1+ext and 1.1 gl implementations.\nBINDTEXFUNCPTR qglBindTexture;\n\n/*glslang - arb_shader_objects\ngl core uses different names/distinctions from the extension\n*/\nFTEPFNGLCREATEPROGRAMPROC\tqglCreateProgramObjectARB;\nFTEPFNGLDELETEPROGRAMPROC\t\t\tqglDeleteProgramObject_;\nFTEPFNGLDELETESHADERPROC\t\t\tqglDeleteShaderObject_;\nFTEPFNGLUSEPROGRAMPROC\t\tqglUseProgramObjectARB;\nFTEPFNGLCREATESHADERPROC\tqglCreateShaderObjectARB;\nFTEPFNGLSHADERSOURCEPROC\t\t\tqglShaderSourceARB;\nFTEPFNGLCOMPILESHADERPROC\t\tqglCompileShaderARB;\nFTEPFNGLGETSHADERIVPROC\tqglGetShaderParameteriv_;\nFTEPFNGLGETPROGRAMIVPROC\tqglGetProgramParameteriv_;\nFTEPFNGLATTACHSHADERPROC\t\t\tqglAttachObjectARB;\nFTEPFNGLGETSHADERINFOLOGPROC\t\t\tqglGetShaderInfoLog_;\nFTEPFNGLGETPROGRAMINFOLOGPROC\t\t\tqglGetProgramInfoLog_;\nFTEPFNGLLINKPROGRAMPROC\t\t\tqglLinkProgramARB;\nFTEPFNGLBINDATTRIBLOCATIONPROC\tqglBindAttribLocationARB;\nFTEPFNGLGETATTRIBLOCATIONPROC\tqglGetAttribLocationARB;\nFTEPFNGLGETUNIFORMLOCATIONPROC\tqglGetUniformLocationARB;\nFTEPFNGLUNIFORMMATRIX4FVPROC\t\t\tqglUniformMatrix4fvARB;\nFTEPFNGLUNIFORMMATRIX3FVPROC\t\t\tqglUniformMatrix3fvARB;\nFTEPFNGLUNIFORM4FPROC\t\t\tqglUniform4fARB;\nFTEPFNGLUNIFORM4FVPROC\t\t\tqglUniform4fvARB;\nFTEPFNGLUNIFORM3FPROC\t\t\tqglUniform3fARB;\nFTEPFNGLUNIFORM3FVPROC\t\t\tqglUniform3fvARB;\nFTEPFNGLUNIFORM4FVPROC\t\t\tqglUniform2fvARB;\nFTEPFNGLUNIFORM1IPROC\t\t\tqglUniform1iARB;\nFTEPFNGLUNIFORM1FPROC\t\t\tqglUniform1fARB;\nFTEPFNGLGETSHADERSOURCEPROC\t\tqglGetShaderSource;\n#endif\nFTEPFNGLUNIFORMMATRIX3X4FVPROC\t\t\tqglUniformMatrix3x4fv;\nFTEPFNGLUNIFORMMATRIX4X3FVPROC\t\t\tqglUniformMatrix4x3fv;\n\n\n//GL_ARB_occlusion_query\nvoid (APIENTRY *qglGenQueriesARB)(GLsizei n, GLuint *ids);\nvoid (APIENTRY *qglDeleteQueriesARB)(GLsizei n, const GLuint *ids);\n//extern GLboolean (APIENTRY *qglIsQueryARB)(GLuint id);\nvoid (APIENTRY *qglBeginQueryARB)(GLenum target, GLuint id);\nvoid (APIENTRY *qglEndQueryARB)(GLenum target);\n//extern void (APIENTRY *qglGetQueryivARB)(GLenum target, GLenum pname, GLint *params);\n//extern void (APIENTRY *qglGetQueryObjectivARB)(GLuint id, GLenum pname, GLint *params);\nvoid (APIENTRY *qglGetQueryObjectuivARB)(GLuint id, GLenum pname, GLuint *params);\n\n\n//GL_OES_get_program_binary\nvoid (APIENTRY *qglGetProgramBinary)(GLuint program, GLsizei bufSize, GLsizei *length, GLenum *binaryFormat, GLvoid *binary);\nvoid (APIENTRY *qglProgramBinary)(GLuint program, GLenum binaryFormat, const GLvoid *binary, GLint length);\n#define GL_PROGRAM_BINARY_LENGTH\t0x8741\n\n//standard 1.1 opengl calls\nvoid (APIENTRY *qglAlphaFunc) (GLenum func, GLclampf ref);\nvoid (APIENTRY *qglBegin) (GLenum mode);\nvoid (APIENTRY *qglCallList) (GLuint list);\nvoid (APIENTRY *qglClearDepth) (GLclampd depth);\nvoid (APIENTRY *qglClipPlane) (GLenum plane, const GLdouble *equation);\n//void (APIENTRY *qglColor3f) (GLfloat red, GLfloat green, GLfloat blue);\n//void (APIENTRY *qglColor3ub) (GLubyte red, GLubyte green, GLubyte blue);\nvoid (APIENTRY *qglColor4f) (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha);\nvoid (APIENTRY *qglColor4fv) (const GLfloat *v);\n//void (APIENTRY *qglColor4ub) (GLubyte red, GLubyte green, GLubyte blue, GLubyte alpha);\n//void (APIENTRY *qglColor4ubv) (const GLubyte *v);\n//void (APIENTRY *qglDepthRange) (GLclampd zNear, GLclampd zFar);\nvoid (APIENTRY *qglDrawBuffer) (GLenum mode);\nvoid (APIENTRY *qglDrawPixels) (GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels);\nvoid (APIENTRY *qglEnd) (void);\nvoid (APIENTRY *qglEndList) (void);\nvoid (APIENTRY *qglFrustum) (GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar);\nGLuint (APIENTRY *qglGenLists) (GLsizei range);\nvoid (APIENTRY *qglLoadIdentity) (void);\nvoid (APIENTRY *qglLoadMatrixf) (const GLfloat *m);\nvoid (APIENTRY *qglNormal3f) (GLfloat nx, GLfloat ny, GLfloat nz);\nvoid (APIENTRY *qglNormal3fv) (const GLfloat *v);\nvoid (APIENTRY *qglMatrixMode) (GLenum mode);\nvoid (APIENTRY *qglMultMatrixf) (const GLfloat *m);\nvoid (APIENTRY *qglNewList) (GLuint list, GLenum mode);\n//void (APIENTRY *qglOrtho) (GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar);\nvoid (APIENTRY *qglPolygonMode) (GLenum face, GLenum mode);\nvoid (APIENTRY *qglPopMatrix) (void);\nvoid (APIENTRY *qglPushMatrix) (void);\nvoid (APIENTRY *qglReadBuffer) (GLenum mode);\nvoid (APIENTRY *qglRotatef) (GLfloat angle, GLfloat x, GLfloat y, GLfloat z);\nvoid (APIENTRY *qglScalef) (GLfloat x, GLfloat y, GLfloat z);\nvoid (APIENTRY *qglShadeModel) (GLenum mode);\nvoid (APIENTRY *qglTexCoord1f) (GLfloat s);\nvoid (APIENTRY *qglTexCoord2f) (GLfloat s, GLfloat t);\nvoid (APIENTRY *qglTexCoord2fv) (const GLfloat *v);\nvoid (APIENTRY *qglTexEnvf) (GLenum target, GLenum pname, GLfloat param);\nvoid (APIENTRY *qglTexEnvfv) (GLenum target, GLenum pname, const GLfloat *param);\nvoid (APIENTRY *qglTexEnvi) (GLenum target, GLenum pname, GLint param);\nvoid (APIENTRY *qglTexGeni) (GLenum coord, GLenum pname, GLint param);\nvoid (APIENTRY *qglTexGenfv) (GLenum coord, GLenum pname, const GLfloat *param);\nvoid (APIENTRY *qglTexImage3D) (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const GLvoid *pixels);\nvoid (APIENTRY *qglTexSubImage3D) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels);\nvoid (APIENTRY *qglTranslatef) (GLfloat x, GLfloat y, GLfloat z);\nvoid (APIENTRY *qglVertex2f) (GLfloat x, GLfloat y);\nvoid (APIENTRY *qglVertex3f) (GLfloat x, GLfloat y, GLfloat z);\nvoid (APIENTRY *qglVertex3fv) (const GLfloat *v);\nvoid (APIENTRY *qglGetTexLevelParameteriv) (GLenum target, GLint level, GLenum pname, GLint *params);\nvoid (APIENTRY *qglGetTexEnviv) (GLenum target, GLenum pname, GLint *params);\n\nvoid (APIENTRY *qglDrawRangeElements) (GLenum, GLuint, GLuint, GLsizei, GLenum, const GLvoid *);\nvoid (APIENTRY *qglMultiDrawElements) (GLenum mode, const GLsizei * count, GLenum type, const GLvoid * const * indices, GLsizei drawcount);\nvoid (APIENTRY *qglArrayElement) (GLint i);\nvoid (APIENTRY *qglVertexPointer) (GLint size, GLenum type, GLsizei stride, const GLvoid *pointer);\nvoid (APIENTRY *qglNormalPointer) (GLenum type, GLsizei stride, const GLvoid *pointer);\nvoid (APIENTRY *qglTexCoordPointer) (GLint size, GLenum type, GLsizei stride, const GLvoid *pointer);\nvoid (APIENTRY *qglColorPointer) (GLint size, GLenum type, GLsizei stride, const GLvoid *pointer);\nvoid (APIENTRY *qglDisableClientState) (GLenum array);\nvoid (APIENTRY *qglEnableClientState) (GLenum array);\n\nvoid (APIENTRY *qglPushAttrib) (GLbitfield mask);\nvoid (APIENTRY *qglPopAttrib) (void);\n\nGLenum (APIENTRY *qglGetGraphicsResetStatus) (void);\n\nvoid (APIENTRY *qglFogf) (GLenum pname, GLfloat param);\nvoid (APIENTRY *qglFogi) (GLenum pname, GLint param);\nvoid (APIENTRY *qglFogfv) (GLenum pname, const GLfloat *params);\n\n#ifndef GL_STATIC\nvoid (APIENTRY *qglGenBuffersARB)(GLsizei n, GLuint* ids);\nvoid (APIENTRY *qglDeleteBuffersARB)(GLsizei n, GLuint* ids);\nvoid (APIENTRY *qglBindBufferARB)(GLenum target, GLuint id);\nvoid (APIENTRY *qglBufferDataARB)(GLenum target, GLsizei size, const void* data, GLenum usage);\nvoid (APIENTRY *qglBufferSubDataARB)(GLenum target, GLint offset, GLsizei size, void* data);\n#endif\n\nvoid *(APIENTRY *qglMapBufferARB)(GLenum target, GLenum access);\nGLboolean (APIENTRY *qglUnmapBufferARB)(GLenum target);\nvoid *(APIENTRY *qglMapBufferRange)(GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access);\nvoid (APIENTRY *qglBufferStorage)(GLenum target, GLsizeiptr size, const GLvoid *data, GLbitfield flags);\n\nvoid (APIENTRY *qglTexStorage2D)(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height);\t\t//gl4.2\nvoid (APIENTRY *qglTexStorage3D)(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth);\t//gl4.2\nFTEPFNGLGETCOMPRESSEDTEXIMAGEARBPROC qglGetCompressedTexImage;\nvoid (APIENTRY *qglCompressedTexSubImage3D) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *data);\t//gl1.3\nFTEPFNGLCOMPRESSEDTEXIMAGE3DARBPROC qglCompressedTexImage3D;\n\nvoid (APIENTRY *qglGenVertexArrays)(GLsizei n, GLuint *arrays);\nvoid (APIENTRY *qglBindVertexArray)(GLuint vaoarray);\n\nconst GLubyte * (APIENTRY * qglGetStringi) (GLenum name, GLuint index);\nvoid (APIENTRY * qglGetPointerv) (GLenum pname, GLvoid **parms);\n\nvoid (APIENTRY *qglDrawBuffers)(GLsizei n, GLsizei *ids);\t//gl2\n#ifndef GL_STATIC\nvoid (APIENTRY *qglGenRenderbuffersEXT)(GLsizei n, GLuint *ids);\nvoid (APIENTRY *qglBindRenderbufferEXT)(GLenum target, GLuint id);\nvoid (APIENTRY *qglRenderbufferStorageEXT)(GLenum target, GLenum internalFormat, GLsizei width, GLsizei height);\nvoid (APIENTRY *qglFramebufferRenderbufferEXT)(GLenum target, GLenum attachmentPoint, GLenum textureTarget, GLuint textureId);\nGLenum (APIENTRY *qglCheckFramebufferStatusEXT)(GLenum target);\n#endif\n\nvoid (APIENTRY *qglDepthBoundsEXT) (GLclampd zmin, GLclampd zmax);\n/*\nPFNGLPROGRAMSTRINGARBPROC qglProgramStringARB;\nPFNGLBINDPROGRAMARBPROC qglBindProgramARB;\nPFNGLGENPROGRAMSARBPROC qglGenProgramsARB;\n*/\nFTEPFNGLLOCKARRAYSEXTPROC qglLockArraysEXT;\nFTEPFNGLUNLOCKARRAYSEXTPROC qglUnlockArraysEXT;\n\n\n//extensions\n//arb multitexture\n#ifndef qglActiveTextureARB\nqlpSelTexFUNC qglActiveTextureARB;\n#endif\nqlpSelTexFUNC qglClientActiveTextureARB;\n\n//generic multitexture\nlpSelTexFUNC qglSelectTextureSGIS;\nint mtexid0;\n\n//ati_truform\nFTEPFNGLPNTRIANGLESIATIPROC qglPNTrianglesiATI;\nFTEPFNGLPNTRIANGLESFATIPROC qglPNTrianglesfATI;\n\nvoid (APIENTRY *qglPatchParameteriARB)(GLenum pname, GLint value);\t//core in gl4\n\n//stencil shadowing\nFTEPFNGLACTIVESTENCILFACEEXTPROC qglActiveStencilFaceEXT;\n\n\n#if defined(_DEBUG) && !defined(DEBUG)\n#define DEBUG\n#endif\n#if defined(DEBUG)\n//always defining this, my system headers use void instead of GLvoid which results in gcc warnings.\ntypedef void (APIENTRY *qGLDEBUGPROCARB)(GLenum source,\n\t\t\t\t\tGLenum type,\n\t\t\t\t\tGLuint id,\n\t\t\t\t\tGLenum severity,\n\t\t\t\t\tGLsizei length,\n\t\t\t\t\tconst GLchar* message,\n\t\t\t\t\tGLvoid* userParam);\nvoid (APIENTRY *qglDebugMessageControlARB)(GLenum source,\n\t\t\t\t\tGLenum type,\n\t\t\t\t\tGLenum severity,\n\t\t\t\t\tGLsizei count,\n\t\t\t\t\tconst GLuint* ids,\n\t\t\t\t\tGLboolean enabled);\nvoid (APIENTRY *qglDebugMessageInsertARB)(GLenum source,\n\t\t\t\t\tGLenum type,\n\t\t\t\t\tGLuint id,\n\t\t\t\t\tGLenum severity,\n\t\t\t\t\tGLsizei length, \n\t\t\t\t\tconst char* buf);\nvoid (APIENTRY *qglDebugMessageCallbackARB)(qGLDEBUGPROCARB callback,\n\t\t\t\t\tvoid* userParam);\nGLuint (APIENTRY *qglGetDebugMessageLogARB)(GLuint count,\n\t\t\t\t\tGLsizei bufsize,\n\t\t\t\t\tGLenum*\tsources,\n\t\t\t\t\tGLenum* types,\n\t\t\t\t\tGLuint* ids,\n\t\t\t\t\tGLuint* severities,\n\t\t\t\t\tGLsizei* lengths,\n\t\t\t\t\tchar* messageLog);\n\n#ifndef GL_ARB_debug_output\n#define GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB               0x8242\n#define GL_MAX_DEBUG_MESSAGE_LENGTH_ARB               0x9143\n#define GL_MAX_DEBUG_LOGGED_MESSAGES_ARB              0x9144\n#define GL_DEBUG_LOGGED_MESSAGES_ARB                  0x9145\n#define GL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH_ARB       0x8243\n#define GL_DEBUG_CALLBACK_FUNCTION_ARB                0x8244\n#define GL_DEBUG_CALLBACK_USER_PARAM_ARB              0x8245\n#define GL_DEBUG_SOURCE_API_ARB                       0x8246\n#define GL_DEBUG_SOURCE_WINDOW_SYSTEM_ARB             0x8247\n#define GL_DEBUG_SOURCE_SHADER_COMPILER_ARB           0x8248\n#define GL_DEBUG_SOURCE_THIRD_PARTY_ARB               0x8249\n#define GL_DEBUG_SOURCE_APPLICATION_ARB               0x824A\n#define GL_DEBUG_SOURCE_OTHER_ARB                     0x824B\n#define GL_DEBUG_TYPE_ERROR_ARB                       0x824C\n#define GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_ARB         0x824D\n#define GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_ARB          0x824E\n#define GL_DEBUG_TYPE_PORTABILITY_ARB                 0x824F\n#define GL_DEBUG_TYPE_PERFORMANCE_ARB                 0x8250\n#define GL_DEBUG_TYPE_OTHER_ARB                       0x8251\n#define GL_DEBUG_SEVERITY_HIGH_ARB                    0x9146\n#define GL_DEBUG_SEVERITY_MEDIUM_ARB                  0x9147\n#define GL_DEBUG_SEVERITY_LOW_ARB                     0x9148 \n#endif\n\n\nvoid (APIENTRY myGLDEBUGPROCAMD)(GLenum source,\n\t\t\t\t\tGLenum type,\n\t\t\t\t\tGLuint id,\n\t\t\t\t\tGLenum severity,\n\t\t\t\t\tGLsizei length,\n\t\t\t\t\tconst GLchar* message,\n\t\t\t\t\tGLvoid* userParam)\n{\n#ifndef _WIN32\n#define OutputDebugStringA(s) puts(s)\n#endif\n\tswitch(type)\n\t{\n\tcase GL_DEBUG_TYPE_ERROR_ARB:\n\t\tOutputDebugStringA(\"Error: \");\n\t\tbreak;\n\tcase GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_ARB:\n\t\tOutputDebugStringA(\"Depricated: \");\n\t\tbreak;\n\tcase GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_ARB:\n\t\tOutputDebugStringA(\"Undefined: \");\n\t\tbreak;\n\tcase GL_DEBUG_TYPE_PORTABILITY_ARB:\n\t\tOutputDebugStringA(\"Portability: \");\n\t\tbreak;\n\tcase GL_DEBUG_TYPE_PERFORMANCE_ARB:\n\t\tOutputDebugStringA(\"Performance: \");\n\t\tbreak;\n\tdefault:\n\tcase GL_DEBUG_TYPE_OTHER_ARB:\n\t\treturn;\n\t\tOutputDebugStringA(\"Other: \");\n\t\tbreak;\n\t}\n\tOutputDebugStringA(message);\n\tOutputDebugStringA(\"\\n\");\n}\n#endif\n\nint gl_mtexarbable=0;\t//max texture units\nqboolean gl_mtexable = false;\n\n\nextern qboolean gammaworks;\t//if the gl drivers can set proper gamma.\n\n\ngl_config_t gl_config;\n\nfloat\t\tgldepthmin, gldepthmax;\nconst char *gl_vendor;\nconst char *gl_renderer;\nconst char *gl_version;\nstatic const char *gl_extensions;\n\nstatic unsigned int gl_num_extensions;\n\nextern cvar_t gl_workaround_ati_shadersource;\n\n\nqboolean GL_CheckExtension(char *extname)\n{\n\tint i;\n\tint len;\n\tconst char *foo;\n\tcvar_t *v = Cvar_Get(va(\"gl_ext_%s\", extname), \"1\", 0, \"GL Extensions\");\n\tif (v && !v->ival)\n\t{\n\t\tCon_Printf(\"Cvar %s is 0\\n\", v->name);\n\t\treturn false;\n\t}\n\n\tif (gl_num_extensions && qglGetStringi)\n\t{\n\t\tfor (i = 0; i < gl_num_extensions; i++)\n\t\t\tif (!strcmp(qglGetStringi(GL_EXTENSIONS, i), extname))\n\t\t\t{\n\t\t\t\tCon_DPrintf(\"GL: Found %s\\n\", extname);\n\t\t\t\treturn true;\n\t\t\t}\n\t}\n\n\tif (!gl_extensions)\n\t\treturn false;\n\n\t//the list is space delimited. we cannot just strstr lest we find leading/trailing _FOO_.\n\tlen = strlen(extname);\n\tfor (foo = gl_extensions; *foo; )\n\t{\n\t\tif (!strncmp(foo, extname, len) && (foo[len] == ' ' || !foo[len]))\n\t\t\treturn true;\n\t\twhile(*foo && *foo != ' ')\n\t\t\tfoo++;\n\t\tif (*foo == ' ')\n\t\t\tfoo++;\n\t}\n\treturn false;\n}\n\nvoid APIENTRY GL_DrawRangeElementsEmul(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const GLvoid *indices)\n{\n\tqglDrawElements(mode, count, type, indices);\n}\n\nvoid APIENTRY GL_Color4fv_Emul(const GLfloat *v)\n{\n\tqglColor4f(v[0], v[1], v[2], v[3]);\n}\n\nvoid APIENTRY GL_BindBufferARBStub(GLenum target, GLuint id)\n{\n}\n\nvoid APIENTRY GL_ClientStateStub(GLenum array)\n{\n}\n\nvoid APIENTRY GL_ClientActiveTextureStub(GLenum texid)\n{\n}\n\nstatic qboolean GL_ParseVersionTupple(char *id, int offset, unsigned int ver[3])\n{\n\tif (id)\n\t{\n\t\tid += offset;\n\t\tver[0] = strtoul(id, &id, 10);\n\t\tif (*id == '.')\n\t\t{\n\t\t\tver[1] = strtoul(id+1, &id, 10);\n\t\t\tif (*id == '.')\n\t\t\t{\n\t\t\t\tver[2] = strtoul(id+1, &id, 10);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t}\n\tver[0] = ver[1] = ver[2] = 0;\n\treturn false;\n}\nstatic qboolean GL_IsVersionTuppleGreaterEqual(unsigned int ver[3], unsigned int max0, unsigned int max1, unsigned int max2)\n{\n\tif (ver[0] > max0)\n\t\treturn true;\n\tif (ver[0] == max0)\n\t{\n\t\tif (ver[1] > max1)\n\t\t\treturn true;\n\t\tif (ver[1] == max1)\n\t\t{\n\t\t\tif (ver[2] >= max2)\n\t\t\t\treturn true;\n\t\t}\n\t}\n\treturn false;\n}\nstatic qboolean GL_IsVersionTuppleWithin(unsigned int ver[3], unsigned int min0, unsigned int min1, unsigned int min2, unsigned int max0, unsigned int max1, unsigned int max2)\n{\n\tif (GL_IsVersionTuppleGreaterEqual(ver, max0, max1, max2))\n\t\treturn false;\t//more recent\n\treturn GL_IsVersionTuppleGreaterEqual(ver, min0, min1, min2);\n}\n\n#define getglcore getglfunction\n#define getglext(name) getglfunction(name)\nstatic qboolean GL_CheckExtensions (void *(*getglfunction) (char *name))\n{\n\tqboolean webgl = false;\n\tunsigned int gl_major_version = 0;\n\tunsigned int gl_minor_version = 0;\n\tunsigned int mesaver[3];\n\tmemset(&gl_config, 0, sizeof(gl_config));\n\n\tGL_ParseVersionTupple(strstr(gl_version, \" Mesa \"), 6, mesaver);\n\n\tif (!strncmp(gl_version, \"WebGL\", 5))\n\t{\n\t\tgl_config.gles = true;\n\t\twebgl = true;\n\n\t\tif (!strcmp(gl_renderer, \"Internet Explorer\"))\n\t\t\tgl_config.webgl_ie = true;\n\t}\n\telse if (!strncmp(gl_version, \"OpenGL ES\", 9))\n\t\tgl_config.gles = true;\n\telse\n\t\tgl_config.gles = false;\n\n\tif (!gl_config.gles)\n\t{\n\t\tif (qglGetError())\n\t\t\tCon_Printf(\"glGetError %s:%i\\n\", __FILE__, __LINE__);\n\t\tqglGetIntegerv(GL_MAJOR_VERSION, &gl_major_version);\n\t\tqglGetIntegerv(GL_MINOR_VERSION, &gl_minor_version);\n\t}\n\tif (!gl_major_version || qglGetError())\n\t{\n\t\t/*GL_MAJOR_VERSION not supported? try and parse (es-aware)*/\n\t\tconst char *s;\n\t\tfor (s = gl_version; *s && (*s < '0' || *s > '9'); s++)\n\t\t\t;\n\t\tgl_major_version = atoi(s);\n\t\twhile(*s >= '0' && *s <= '9')\n\t\t\ts++;\n\t\tif (*s == '.')\n\t\t\ts++;\n\t\tgl_minor_version = atoi(s);\n\t}\n#ifdef _DEBUG\n\t{ extern cvar_t vid_gl_context_es;\n\tif (vid_gl_context_es.ival == 3)\n\t{\n\t\tgl_config.gles = true;\n\t\tgl_major_version = 1;\n\t\tgl_minor_version = 0;\n\t} }\n#endif\n\tif (webgl)\t//webgl version 1 equates to gles 2.\n\t{\n\t\tif (gl_major_version < 1)\n\t\t{\t//ie reports a bollocks version. don't try using fixed function stuff.\n\t\t\tgl_major_version = 2;\n\t\t\tgl_minor_version = 0;\n\t\t}\n\t\telse if (gl_major_version == 1)\n\t\t\tgl_major_version += 1;\n\t\t//webgl2 is not defined yet. either 2 will be gles3 or they'll skip it or something I don't know.\n\t\t//so assume webgl2 is still equivelent to gles2, to avoid confusions.\n\t}\n\t//FIXME: verify gles3 works properly.\n\n\t//yes, I know, this can't cope with minor versions of 10+... I don't care yet.\n\tgl_config.glversion += gl_major_version + (gl_minor_version/10.f);\n\n\t/*gl3 adds glGetStringi instead, as core, with the old form require GL_ARB_compatibility*/\n\tif (gl_major_version >= 3 && qglGetStringi) /*warning: wine fails to export qglGetStringi*/\n\t{\n\t\tint i;\n\t\tqglGetIntegerv(GL_NUM_EXTENSIONS, &gl_num_extensions);\n\t\tif (developer.value>1)\n\t\t{\n\t\t\tCon_Printf (\"GL_EXTENSIONS:\\n\");\n\t\t\tfor (i = 0; i < gl_num_extensions; i++)\n\t\t\t{\n\t\t\t\tCon_Printf (\" %s\", qglGetStringi(GL_EXTENSIONS, i));\n\t\t\t\tCon_Printf(\"\\n\");\n\t\t\t}\n\t\t\tCon_Printf (\"end of list\\n\");\n\t\t}\n\t\telse\n\t\t\tCon_DPrintf (\"GL_EXTENSIONS: %i extensions\\n\", gl_num_extensions);\n\t\tgl_extensions = NULL;\n\t}\n\telse\n\t{\n\t\tgl_num_extensions = 0;\n\t\tgl_extensions = qglGetString (GL_EXTENSIONS);\n\t\tCon_DPrintf (\"GL_EXTENSIONS: %s\\n\", gl_extensions);\n\n\t\tif (!gl_extensions)\n\t\t\tSys_Error(\"no extensions\\n\");\n\t}\n\n#ifdef _DEBUG\n\t{ extern cvar_t vid_gl_context_es;\n\tif (vid_gl_context_es.ival == 3)\n\t{\n\t\tgl_extensions = \"\";\n\t} }\n#endif\n\n\tif (gl_config.gles)\n\t\tgl_config.nofixedfunc = gl_config.glversion >= 2;\n\telse\n\t{\n\t\t/*in gl3.0 things are depricated but not removed*/\n\t\t/*in gl3.1 depricated things are removed unless compatibility is present*/\n\t\t/*in gl3.2 there's a profile flag we can query*/\n\t\tif (gl_config.glversion >= 3.2)\n\t\t{\n\t\t\tGLint profile = 0;\n#define GL_CONTEXT_PROFILE_MASK\t\t\t\t\t0x9126\n#define GL_CONTEXT_CORE_PROFILE_BIT\t\t\t\t0x00000001\n#define GL_CONTEXT_COMPATIBILITY_PROFILE_BIT\t0x00000002\n#define GL_CONTEXT_FLAGS\t\t\t\t\t\t0x821E\n#ifndef GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT\n#define GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT 0x0001\n#endif\n\t\t\tqglGetIntegerv(GL_CONTEXT_PROFILE_MASK, &profile);\n\n\t\t\tif (!profile)\n\t\t\t{\n\t\t\t\tCon_DPrintf(\"Driver reports invalid profile, assuming compatibility support\\n\");\n\t\t\t\tgl_config.nofixedfunc = false;\n\t\t\t}\n\t\t\telse\n\t\t\t\tgl_config.nofixedfunc = !(profile & GL_CONTEXT_COMPATIBILITY_PROFILE_BIT);\n\t\t}\n\t\telse if (gl_config.glversion >= 3.0)\n\t\t{\n\t\t\tGLint flags = 0;\n\t\t\tqglGetIntegerv(GL_CONTEXT_FLAGS, &flags);\n\t\t\tgl_config.nofixedfunc = !!(flags & GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT);\n\t\t\tif (gl_config.glversion >= 3.0999)\n\t\t\t\tgl_config.nofixedfunc = !GL_CheckExtension(\"GL_ARB_compatibility\");\n\t\t}\n\t\telse\n\t\t\tgl_config.nofixedfunc = false;\n\n#ifdef GLSLONLY\n\t\tgl_config.nofixedfunc = true;\n#endif\n\t}\n\n\tgl_config.maxglslversion = 0;\n\tif (gl_config.gles && gl_config.glversion >= 2)\n\t{\n\t\tif (gl_config.glversion >= 3.2)\n\t\t\tgl_config.maxglslversion = 320;\n\t\telse if (gl_config.glversion >= 3.1)\n\t\t\tgl_config.maxglslversion = 310;\n\t\telse if (gl_config.glversion >= 3)\n\t\t\tgl_config.maxglslversion = 300;\n\t\telse\n\t\t\tgl_config.maxglslversion = 100;\n\t}\n\telse if (gl_config.glversion >= 2)\n\t{\n#define GL_SHADING_LANGUAGE_VERSION 0x8B8C\n\t\tconst char *s = qglGetString (GL_SHADING_LANGUAGE_VERSION);\n\n\t\tif (s)\n\t\t{\n\t\t\tgl_config.maxglslversion = atoi(s) * 100;\n\t\t\twhile(*s >= '0' && *s <= '9')\n\t\t\t\ts++;\n\t\t\tif (*s == '.')\n\t\t\t\ts++;\n\t\t\tgl_config.maxglslversion += atoi(s);\n\t\t}\n\t\telse\n\t\t\tgl_config.maxglslversion = 110;\n\t}\n\telse\n\t\tgl_config.maxglslversion = 110;\n\n\n#if GL_INDEX_TYPE == GL_UNSIGNED_INT\n\tif (gl_config_gles && gl_config.glversion < 3.0 && !GL_CheckExtension(\"GL_OES_element_index_uint\"))\n\t{\t//opengles 1 and 2 do NOT support 32bit indexes. desktop gl always does but es supports it starting with gles3.0\n\t\tCon_Printf (\"Support for OpenGL ES 3.0 is required.\\n\");\n\t\treturn false;\n\t}\n#endif\n\n\t//multitexture\n\tgl_mtexable = false;\n\tgl_mtexarbable = 0;\n#ifndef qglActiveTextureARB\n\tqglActiveTextureARB = NULL;\n#endif\n\tqglSelectTextureSGIS = NULL;\n\tmtexid0 = 0;\n\n#ifndef GL_STATIC\n\tqglGenFramebuffersEXT\t\t= NULL;\n\tqglDeleteFramebuffersEXT\t= NULL;\n\tqglBindFramebufferEXT\t\t= NULL;\n\tqglGenRenderbuffersEXT\t\t= NULL;\n\tqglDeleteRenderbuffersEXT\t= NULL;\n\tqglBindRenderbufferEXT\t\t= NULL;\n\tqglRenderbufferStorageEXT\t= NULL;\n\tqglFramebufferTexture2DEXT\t= NULL;\n\n#endif\n\n\t//no GL_EXT_stencil_two_side\n\tqglActiveStencilFaceEXT = NULL;\n\n\t//no truform. sorry.\n\tqglPNTrianglesfATI = NULL;\n\tqglPNTrianglesiATI = NULL;\n\n\t//fragment programs\n/*\tgl_config.arb_fragment_program = false;\n\tqglProgramStringARB = NULL;\n\tqglGetProgramivARB = NULL;\n\tqglBindProgramARB = NULL;\n\tqglGenProgramsARB = NULL;\n*/\n\n\tif (gl_config.glversion >= (gl_config.gles?3:1.2))\n\t\tqglDrawRangeElements = (void *)getglext(\"glDrawRangeElements\");\n\telse if (GL_CheckExtension(\"GL_EXT_draw_range_elements\"))\n\t\tqglDrawRangeElements = (void *)getglext(\"glDrawRangeElementsEXT\");\n\telse\n\t\tqglDrawRangeElements = NULL;\n\tif (qglDrawRangeElements == NULL)\n\t\tqglDrawRangeElements = GL_DrawRangeElementsEmul;\n\n\tgl_config.ext_packed_depth_stencil = GL_CheckExtension(\"GL_EXT_packed_depth_stencil\");\n\n\tif ((!gl_config.gles && gl_config.glversion >= 4.6) || GL_CheckExtension(\"GL_EXT_texture_filter_anisotropic\") || GL_CheckExtension(\"GL_ARB_texture_filter_anisotropic\"))\n\t{\n\t\tqglGetIntegerv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &gl_config.ext_texture_filter_anisotropic);\n\t\tCon_DPrintf(\"Anisotropic filtering supported (%dx max).\\n\",gl_config.ext_texture_filter_anisotropic);\n\t}\n\n\tif ((!gl_config.gles && gl_config.glversion >= 2) || GL_CheckExtension(\"GL_ARB_texture_non_power_of_two\"))\n\t\tsh_config.texture_allow_block_padding = true;\t//gl2/npot explicitly relaxes this restriction\n\telse\n\t\tsh_config.texture_allow_block_padding = false;\t//gles does not support padded sizes, even with gles3. This is especially true if we're running atop d3d-via-webgl.\n\n\tif (!gl_config.gles && gl_config.glversion >= 3)\n\t{\t//GL_ARB_texture_non_power_of_two is supposed to be mandatory in gl2+ and thus checking for it is redundant and not forwards-compatible\n\t\t//geforcefx apparently software emulates it, so only activate it unconditionally on gl3+ hardware.\n\t\t//gl2 drivers will likely still be explicitly advertising GL_ARB_texture_non_power_of_two so there's probably no great loss from requiring a higher version.\n\t\tsh_config.texture_non_power_of_two = true;\n\t\tsh_config.texture_non_power_of_two_pic = true;\n\t}\n\telse if (gl_config.gles && gl_config.glversion >= 3)\n\t{\t//gles3 is meant to have full npot support (like gl2+ was meant to).\n\t\tsh_config.texture_non_power_of_two = true;\n\t\tsh_config.texture_non_power_of_two_pic = true;\n\t}\n\telse if (GL_CheckExtension(\"GL_ARB_texture_non_power_of_two\"))\n\t{\t//gl1 devices might still support npot\n\t\tsh_config.texture_non_power_of_two = true;\n\t\tsh_config.texture_non_power_of_two_pic = true;\n\t}\n\telse if (gl_config.gles && GL_CheckExtension(\"GL_OES_texture_npot\"))\n\t{\t//gles devices might have full npot too, but with a different extension name. because consistancy is good...\n\t\tsh_config.texture_non_power_of_two = true;\n\t\tsh_config.texture_non_power_of_two_pic = true;\n\t}\n\telse if (gl_config.gles && gl_config.glversion >= 2)\n\t{\t//gles2 has npot (clamp + no mips) support as base.\n\t\tsh_config.texture_non_power_of_two = false;\n\t\tsh_config.texture_non_power_of_two_pic = true;\n\t}\n\telse if (gl_config.gles && GL_CheckExtension(\"GL_APPLE_texture_2D_limited_npot\"))\n\t{\t//gles1 MIGHT have SOME npot support.\n\t\tsh_config.texture_non_power_of_two = false;\n\t\tsh_config.texture_non_power_of_two_pic = true;\n\t}\n\telse\n\t{\t//really old hardware/drivers with no npot support at all.\n\t\tsh_config.texture_non_power_of_two = false;\n\t\tsh_config.texture_non_power_of_two_pic = false;\n\t}\n\n//\tif (GL_CheckExtension(\"GL_SGIS_generate_mipmap\"))\t//a suprising number of implementations have this broken.\n//\t\tgl_config.sgis_generate_mipmap = true;\n\n\tif (gl_config.gles || gl_config_nofixedfunc)\n\t{\n#ifndef qglActiveTextureARB\n\t\tqglActiveTextureARB = (void *) getglext(\"glActiveTexture\");\n#endif\n\t\tqglClientActiveTextureARB = (void *) getglext(\"glClientActiveTexture\");\t//compat contexts only...\n\t\tqglSelectTextureSGIS = qglActiveTextureARB;\n\t\tmtexid0 = GL_TEXTURE0_ARB;\n\t\tif (!gl_config.nofixedfunc)\n\t\t\tqglGetIntegerv(GL_MAX_TEXTURE_UNITS_ARB, &gl_mtexarbable);\n\t\telse\n\t\t\tgl_mtexarbable = 8;\n\t}\n\telse if (GL_CheckExtension(\"GL_ARB_multitexture\") && !COM_CheckParm(\"-noamtex\"))\n\t{\t//ARB multitexture is the popular choice.\n#ifndef qglActiveTextureARB\n\t\tqglActiveTextureARB = (void *) getglext(\"glActiveTextureARB\");\n#endif\n\t\tqglClientActiveTextureARB = (void *) getglext(\"glClientActiveTextureARB\");\n\n\t\tqglGetIntegerv(GL_MAX_TEXTURE_UNITS_ARB, &gl_mtexarbable);\n\t\tgl_mtexable = true;\n\n\t\tqglSelectTextureSGIS = qglActiveTextureARB;\n\n\t\tmtexid0 = GL_TEXTURE0_ARB;\n\n#ifndef qglActiveTextureARB\n\t\tif (!qglActiveTextureARB || !qglClientActiveTextureARB)\n\t\t\tgl_mtexable = false;\n\t\telse if (gl_mtexarbable == 1)\n\t\t{\n\t\t\tCon_Printf(\"OpenGL Driver Bug detected: 1 texture is NOT multitexture\\n\");\n\t\t\tgl_mtexable = false;\n\t\t}\n\t\tif (!gl_mtexable)\n\t\t{\n\t\t\tqglActiveTextureARB = NULL;\n\t\t\tqglClientActiveTextureARB = NULL;\n\t\t\tqglSelectTextureSGIS = NULL;\n\t\t\tgl_mtexable=false;\n\t\t\tgl_mtexarbable = false;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tCon_DPrintf(\"ARB Multitexture extensions found. Use -noamtex to disable.\\n\");\n\t\t}\n#endif\n\t}\n\t/*\n\telse if (GL_CheckExtension(\"GL_SGIS_multitexture\") && !COM_CheckParm(\"-nomtex\"))\n\t{\t//SGIS multitexture, limited in many ways but basic functionality is identical to ARB\n\t\tCon_SafePrintf(\"Multitexture extensions found.\\n\");\n\t\tqglMTexCoord2fSGIS = (void *) getglext(\"glMTexCoord2fSGIS\");\n\t\tqglSelectTextureSGIS = (void *) getglext(\"glSelectTextureSGIS\");\n\t\tgl_mtexable = true;\n\n\t\tmtexid0 = GL_TEXTURE0_SGIS;\n\t}\n\t*/\n\tif (!qglClientActiveTextureARB)\n\t{\n\t\tqglClientActiveTextureARB = GL_ClientActiveTextureStub;\n\t}\n\n\tif ((gl_config.gles && gl_config.glversion >= 2) || GL_CheckExtension(\"GL_EXT_stencil_wrap\"))\n\t\tgl_config.ext_stencil_wrap = true;\n\n#ifndef GL_STATIC\n\tqglStencilOpSeparate = NULL;\n\tif ((gl_config.gles && gl_config.glversion >= 2) || (!gl_config.gles && gl_config.glversion >= 3)) //theoretically that should be a 2 not 3.\n\t\tqglStencilOpSeparate = (void *) getglext(\"glStencilOpSeparate\");\n\telse if (GL_CheckExtension(\"GL_ATI_separate_stencil\"))\n\t\tqglStencilOpSeparate = (void *) getglext(\"glStencilOpSeparateATI\");\n#endif\n\tqglActiveStencilFaceEXT = NULL;\n\tif (GL_CheckExtension(\"GL_EXT_stencil_two_side\"))\n\t\tqglActiveStencilFaceEXT = (void *) getglext(\"glActiveStencilFaceEXT\");\n\n\t/*not enabled - its only useful for shadow volumes, but (on nvidia) it affects the depth values even when not clamped which results in shadow z-fighting. best rely upon infinite projection matricies instead*/\n\tif (GL_CheckExtension(\"GL_ARB_depth_clamp\") || GL_CheckExtension(\"GL_NV_depth_clamp\"))\n\t\tgl_config.arb_depth_clamp = true;\n\n#ifndef GL_STATIC\n\tif (gl_config.gles)\n\t{\t//GL_ARB_texture_compression is not quite supported in gles, but works for custom compressed formats (like etc2).\n\t\tqglCompressedTexImage2D = (void *)getglext(\"glCompressedTexImage2D\");\n\t\tqglCompressedTexImage3D = (void *)getglext(\"glCompressedTexImage3D\");\n\t\tqglCompressedTexSubImage2D = (void *)getglext(\"glCompressedTexSubImage2D\");\n\t\tqglCompressedTexSubImage3D = (void *)getglext(\"glCompressedTexSubImage3D\");\n\t\tqglGetCompressedTexImage = NULL;\n\t}\n\telse if (!gl_config.gles && gl_config.glversion > 1.3)\n\t{\t//GL_ARB_texture_compression is core in gl1.3\n\t\tqglCompressedTexImage2D = (void *)getglext(\"glCompressedTexImage2D\");\n\t\tqglCompressedTexImage3D = (void *)getglext(\"glCompressedTexImage3D\");\n\t\tqglCompressedTexSubImage2D = (void *)getglext(\"glCompressedTexSubImage2D\");\n\t\tqglCompressedTexSubImage3D = (void *)getglext(\"glCompressedTexSubImage3D\");\n\t\tqglGetCompressedTexImage = (void *)getglext(\"glGetCompressedTexImage\");\n\t\tgl_config.arb_texture_compression = true;\n\t}\n\telse if (GL_CheckExtension(\"GL_ARB_texture_compression\"))\n\t{\n\t\tqglCompressedTexImage2D = (void *)getglext(\"glCompressedTexImage2DARB\");\n\t\tqglCompressedTexImage3D = (void *)getglext(\"glCompressedTexImage3DARB\");\n\t\tqglCompressedTexSubImage2D = (void *)getglext(\"glCompressedTexSubImage2DARB\");\n\t\tqglCompressedTexSubImage3D = (void *)getglext(\"glCompressedTexSubImage3DARB\");\n\t\tqglGetCompressedTexImage = (void *)getglext(\"glGetCompressedTexImageARB\");\n\n\t\tif (!qglCompressedTexImage2D || !qglGetCompressedTexImage)\n\t\t{\n\t\t\tqglCompressedTexImage2D = NULL;\n\t\t\tqglGetCompressedTexImage = NULL;\n\t\t}\n\t\telse\n\t\t\tgl_config.arb_texture_compression = true;\n\t}\n#endif\n\n\tgl_config.astc_decodeprecision = GL_CheckExtension(\"GL_EXT_texture_compression_astc_decode_mode\");\n/*\n\tif (GL_CheckExtension(\"GL_EXT_depth_bounds_test\"))\n\t\tqglDepthBoundsEXT = (void *)getglext(\"glDepthBoundsEXT\");\n\telse if (GL_CheckExtension(\"GL_NV_depth_bounds_test\"))\n\t\tqglDepthBoundsEXT = (void *)getglext(\"glDepthBoundsNV\");\n\telse\n\t\tqglDepthBoundsEXT = NULL;\n*/\n\tif (GL_CheckExtension(\"GL_ATI_pn_triangles\"))\n\t{\n\t\tqglPNTrianglesfATI = (void *)getglext(\"glPNTrianglesfATI\");\n\t\tqglPNTrianglesiATI = (void *)getglext(\"glPNTrianglesiATI\");\n\t}\n\tif ((!gl_config.gles && gl_config.glversion >= 4.0) || (gl_config.gles && gl_config.glversion >= 3.2))\n\t{\n\t\tgl_config.arb_tessellation_shader = true;\n\t\tqglPatchParameteriARB = getglext(\"glPatchParameteri\");\n\t}\n\telse if (GL_CheckExtension(\"GL_ARB_tessellation_shader\"))\n\t{\n\t\tgl_config.arb_tessellation_shader = true;\n\t\tqglPatchParameteriARB = getglext(\"glPatchParameteriARB\");\n\t}\n\telse if (GL_CheckExtension(\"GL_OES_tessellation_shader\"))\n\t{\n\t\tgl_config.arb_tessellation_shader = true;\n\t\tqglPatchParameteriARB = getglext(\"glPatchParameteriOES\");\n\t}\n\telse\n\t\tqglPatchParameteriARB = NULL;\n\n\n#ifndef GL_STATIC\n\tif (GL_CheckExtension(\"GL_EXT_texture_object\"))\n\t{\n\t\tqglBindTexture\t\t\t= (void *)getglext(\"glBindTextureEXT\");\n\t\tif (!qglBindTexture)\t//grrr\n\t\t\tqglBindTexture\t\t\t= (void *)getglext(\"glBindTexture\");\n\t}\n#endif\n\n\tif (GL_CheckExtension(\"GL_EXT_compiled_vertex_array\"))\n\t{\n\t\tqglLockArraysEXT = (void *)getglext(\"glLockArraysEXT\");\n\t\tqglUnlockArraysEXT = (void *)getglext(\"glUnlockArraysEXT\");\n\t}\n\telse\n\t{\n\t\tqglLockArraysEXT = NULL;\n\t\tqglUnlockArraysEXT = NULL;\n\t}\n\n\t/*various combiner features*/\n\tgl_config.tex_env_combine = GL_CheckExtension(\"GL_EXT_texture_env_combine\");\n\tgl_config.env_add = GL_CheckExtension(\"GL_EXT_texture_env_add\");\n\tgl_config.nv_tex_env_combine4 = GL_CheckExtension(\"GL_NV_texture_env_combine4\");\n\tgl_config.arb_texture_env_combine = GL_CheckExtension(\"GL_ARB_texture_env_combine\");\n\tgl_config.arb_texture_env_dot3 = GL_CheckExtension(\"GL_ARB_texture_env_dot3\");\n\n\tqglBufferStorage = NULL;\n#if !defined(GL_STATIC)\n\t/*vbos, were made core in gl1.5 or gles2.0*/\n\tif ((gl_config.gles && gl_config.glversion >= 2) || (!gl_config.gles && (gl_major_version > 1 || (gl_major_version == 1 && gl_minor_version >= 5))))\n\t{\n\t\tqglGenBuffersARB = (void *)getglext(\"glGenBuffers\");\n\t\tqglDeleteBuffersARB = (void *)getglext(\"glDeleteBuffers\");\n\t\tqglBindBufferARB = (void *)getglext(\"glBindBuffer\");\n\t\tqglBufferDataARB = (void *)getglext(\"glBufferData\");\n\t\tqglBufferSubDataARB = (void *)getglext(\"glBufferSubData\");\n\t\tqglMapBufferARB = (void *)getglext(\"glMapBuffer\");\n\t\tqglUnmapBufferARB = (void *)getglext(\"glUnmapBuffer\");\n\t}\n\telse if (GL_CheckExtension(\"GL_ARB_vertex_buffer_object\"))\n\t{\n\t\tqglGenBuffersARB = (void *)getglext(\"glGenBuffersARB\");\n\t\tqglDeleteBuffersARB = (void *)getglext(\"glDeleteBuffersARB\");\n\t\tqglBindBufferARB = (void *)getglext(\"glBindBufferARB\");\n\t\tqglBufferDataARB = (void *)getglext(\"glBufferDataARB\");\n\t\tqglBufferSubDataARB = (void *)getglext(\"glBufferSubDataARB\");\n\t\tqglMapBufferARB = (void *)getglext(\"glMapBufferARB\");\n\t\tqglUnmapBufferARB = (void *)getglext(\"glUnmapBufferARB\");\n\t}\n\telse\n\t{\n\t\tqglGenBuffersARB = NULL;\n\t\tqglDeleteBuffersARB = NULL;\n\t\tqglBindBufferARB = NULL;\n\t\tqglBufferDataARB = NULL;\n\t\tqglBufferSubDataARB = NULL;\n\t\tqglMapBufferARB = NULL;\n\t\tqglUnmapBufferARB = NULL;\n\t}\n#endif\n\n\t//ARB_map_buffer_range: core in gl3.0/gles3.0, the extension is backported, and thus no ARB postfix on functions.\n\tqglMapBufferRange = qglUnmapBufferARB?(void *)getglext(\"glMapBufferRange\"):NULL;\n\n\tif (qglBufferSubDataARB && qglMapBufferRange)\n\t{\n\t\tif ((!gl_config.gles && gl_config.glversion >= 4.4) || GL_CheckExtension(\"GL_ARB_buffer_storage\"))\n\t\t\tif (gl_immutable_buffers.ival)\n\t\t\t\tqglBufferStorage = (void *)getglext(\"glBufferStorage\");\t//no arb postfix even with the extension form of it.\n\t}\n\n#ifdef GL_STATIC\n\tgl_config.arb_shader_objects = true;\n#else\n\tif (Cvar_Get(\"gl_blacklist_debug_glsl\", \"0\", CVAR_VIDEOLATCH, \"gl blacklists\")->ival && !gl_config.nofixedfunc)\n\t{\n\t\tCon_Printf(CON_NOTICE \"GLSL disabled\\n\");\n\t\tgl_config.arb_shader_objects = false;\n\t\tqglCreateProgramObjectARB\t= NULL;\n\t\tqglDeleteProgramObject_\t\t= NULL;\n\t\tqglDeleteShaderObject_\t\t= NULL;\n\t\tqglUseProgramObjectARB\t\t= NULL;\n\t\tqglCreateShaderObjectARB\t= NULL;\n\t\tqglGetProgramParameteriv_\t= NULL;\n\t\tqglGetShaderParameteriv_\t= NULL;\n\t\tqglAttachObjectARB\t\t\t= NULL;\n\t\tqglGetProgramInfoLog_\t\t= NULL;\n\t\tqglGetShaderInfoLog_\t\t= NULL;\n\t\tqglShaderSourceARB\t\t\t= NULL;\n\t\tqglCompileShaderARB\t\t\t= NULL;\n\t\tqglLinkProgramARB\t\t\t= NULL;\n\t\tqglBindAttribLocationARB\t= NULL;\n\t\tqglGetAttribLocationARB\t\t= NULL;\n\t\tqglVertexAttrib4f\t\t\t= NULL;\n\t\tqglVertexAttribPointer\t\t= NULL;\n\t\tqglGetVertexAttribiv\t\t= NULL;\n\t\tqglGetVertexAttribPointerv\t= NULL;\n\t\tqglEnableVertexAttribArray\t= NULL;\n\t\tqglDisableVertexAttribArray\t= NULL;\n\t\tqglGetUniformLocationARB\t= NULL;\n\t\tqglUniformMatrix4fvARB\t\t= NULL;\n\t\tqglUniformMatrix3x4fv\t\t= NULL;\n\t\tqglUniformMatrix4x3fv\t\t= NULL;\n\t\tqglUniform4fARB\t\t\t\t= NULL;\n\t\tqglUniform4fvARB\t\t\t= NULL;\n\t\tqglUniform3fARB\t\t\t\t= NULL;\n\t\tqglUniform3fvARB\t\t\t= NULL;\n\t\tqglUniform2fvARB\t\t\t= NULL;\n\t\tqglUniform1iARB\t\t\t\t= NULL;\n\t\tqglUniform1fARB\t\t\t\t= NULL;\n\t\tqglGetShaderSource\t\t\t= NULL;\n\t}\n\t// glslang\n\t//the gf2 to gf4 cards emulate vertex_shader and thus supports shader_objects.\n\t//but our code kinda requires both for clean workings.\n\telse if (strstr(gl_renderer, \" Mesa \") && (gl_config.glversion < 3 || gl_config.gles) && Cvar_Get(\"gl_blacklist_mesa_glsl\", \"1\", CVAR_VIDEOLATCH, \"gl blacklists\")->ival)\n\t{\n//(9:12:33 PM) bigfoot: Spike, can you please blacklist your menu shader on Mesa? My machine just hard locked up again because I forgot that pressing escape in FTE is verboten\n//(11:51:42 PM) bigfoot: OpenGL vendor string: Tungsten Graphics, Inc\n//(11:51:50 PM) bigfoot: OpenGL version string: 2.1 Mesa 7.7.1\n\n//blacklist all glsl, it can't handle #define macros properly either.\n//if the menu shader is hardlocking, I don't know what else will do it too.\n\t\tCon_Printf(CON_NOTICE \"Mesa detected, ignoring any GLSL support. Use '+set gl_blacklist_mesa_glsl 0' on the commandline to reenable it.\\n\");\n\t}\n\telse if (gl_config.glversion >= 2)// && (gl_config.gles || 0))\n\t{\n\t\t/*core names are different from extension names (more functions too)*/\n\t\tqglCreateProgramObjectARB\t= (void *)getglext( \"glCreateProgram\");\n\t\tqglDeleteProgramObject_\t\t= (void *)getglext( \"glDeleteProgram\");\n\t\tqglDeleteShaderObject_\t\t= (void *)getglext( \"glDeleteShader\");\n\t\tqglUseProgramObjectARB\t\t= (void *)getglext( \"glUseProgram\");\n\t\tqglCreateShaderObjectARB\t= (void *)getglext( \"glCreateShader\");\n\t\tqglGetProgramParameteriv_\t= (void *)getglext( \"glGetProgramiv\");\n\t\tqglGetShaderParameteriv_\t= (void *)getglext( \"glGetShaderiv\");\n\t\tqglAttachObjectARB\t\t\t= (void *)getglext( \"glAttachShader\");\n\t\tqglGetProgramInfoLog_\t\t= (void *)getglext( \"glGetProgramInfoLog\");\n\t\tqglGetShaderInfoLog_\t\t= (void *)getglext( \"glGetShaderInfoLog\");\n\t\tqglShaderSourceARB\t\t\t= (void *)getglext(\"glShaderSource\");\n\t\tqglCompileShaderARB\t\t\t= (void *)getglext(\"glCompileShader\");\n\t\tqglLinkProgramARB\t\t\t= (void *)getglext(\"glLinkProgram\");\n\t\tqglBindAttribLocationARB\t= (void *)getglext(\"glBindAttribLocation\");\n\t\tqglGetAttribLocationARB\t\t= (void *)getglext(\"glGetAttribLocation\");\n\t\tqglGetUniformLocationARB\t= (void *)getglext(\"glGetUniformLocation\");\n\t\tqglUniformMatrix4fvARB\t\t= (void *)getglext(\"glUniformMatrix4fv\");\n\t\tqglUniformMatrix3fvARB\t\t= (void *)getglext(\"glUniformMatrix3fv\");\n\t\tqglUniformMatrix3x4fv\t\t= (void *)getglext(\"glUniformMatrix3x4fv\");\n\t\tqglUniformMatrix4x3fv\t\t= (void *)getglext(\"glUniformMatrix4x3fv\");\n\t\tqglUniform4fARB\t\t\t\t= (void *)getglext(\"glUniform4f\");\n\t\tqglUniform4fvARB\t\t\t= (void *)getglext(\"glUniform4fv\");\n\t\tqglUniform3fARB\t\t\t\t= (void *)getglext(\"glUniform3f\");\n\t\tqglUniform3fvARB\t\t\t= (void *)getglext(\"glUniform3fv\");\n\t\tqglUniform2fvARB\t\t\t= (void *)getglext(\"glUniform2fv\");\n\t\tqglUniform1iARB\t\t\t\t= (void *)getglext(\"glUniform1i\");\n\t\tqglUniform1fARB\t\t\t\t= (void *)getglext(\"glUniform1f\");\n\t\tqglVertexAttrib4f\t\t\t= (void *)getglext(\"glVertexAttrib4f\");\n\t\tqglVertexAttribPointer\t\t= (void *)getglext(\"glVertexAttribPointer\");\n\t\tqglGetVertexAttribiv\t\t= (void *)getglext(\"glGetVertexAttribiv\");\n\t\tqglGetVertexAttribPointerv\t= (void *)getglext(\"glGetVertexAttribPointerv\");\n\t\tqglEnableVertexAttribArray\t= (void *)getglext(\"glEnableVertexAttribArray\");\n\t\tqglDisableVertexAttribArray\t= (void *)getglext(\"glDisableVertexAttribArray\");\n\t\tqglGetShaderSource\t\t\t= (void *)getglext(\"glGetShaderSource\");\n\n\t\tif (qglCreateProgramObjectARB && qglLinkProgramARB)\n\t\t{\n\t\t\tCon_DPrintf(\"GLSL available\\n\");\n\t\t\tgl_config.arb_shader_objects = true;\n\t\t}\n\t\telse\n\t\t\tCon_Printf(CON_ERROR\"GL version specifies GLSL support, but GLSL functions are not available\\n\");\n\t}\n#ifdef GL_ARB_shader_objects\n\telse if (sizeof(GLhandleARB) == sizeof(GLuint)\t//buggy when __APPLE__ is defined.\n\t\t&& GL_CheckExtension(\"GL_ARB_fragment_shader\")\n\t\t&& GL_CheckExtension(\"GL_ARB_vertex_shader\")\n\t\t&& GL_CheckExtension(\"GL_ARB_shader_objects\"))\n\t{\n\t\tgl_config.arb_shader_objects = true;\n\t\tqglCreateProgramObjectARB\t= (void *)getglext(\"glCreateProgramObjectARB\");\n\t\tqglDeleteProgramObject_\t\t= (void *)getglext(\"glDeleteObjectARB\");\n\t\tqglDeleteShaderObject_\t\t= (void *)getglext(\"glDeleteObjectARB\");\n\t\tqglUseProgramObjectARB\t\t= (void *)getglext(\"glUseProgramObjectARB\");\n\t\tqglCreateShaderObjectARB\t= (void *)getglext(\"glCreateShaderObjectARB\");\n\t\tqglShaderSourceARB\t\t\t= (void *)getglext(\"glShaderSourceARB\");\n\t\tqglCompileShaderARB\t\t\t= (void *)getglext(\"glCompileShaderARB\");\n\t\tqglGetProgramParameteriv_\t= (void *)getglext(\"glGetObjectParameterivARB\");\n\t\tqglGetShaderParameteriv_\t= (void *)getglext(\"glGetObjectParameterivARB\");\n\t\tqglAttachObjectARB\t\t\t= (void *)getglext(\"glAttachObjectARB\");\n\t\tqglGetProgramInfoLog_\t\t= (void *)getglext(\"glGetInfoLogARB\");\n\t\tqglGetShaderInfoLog_\t\t= (void *)getglext(\"glGetInfoLogARB\");\n\t\tqglLinkProgramARB\t\t\t= (void *)getglext(\"glLinkProgramARB\");\n\t\tqglBindAttribLocationARB\t= (void *)getglext(\"glBindAttribLocationARB\");\n\t\tqglGetAttribLocationARB\t\t= (void *)getglext(\"glGetAttribLocationARB\");\n\t\tqglVertexAttrib4f\t\t\t= (void *)getglext(\"glVertexAttrib4fARB\");\n\t\tqglVertexAttribPointer\t\t= (void *)getglext(\"glVertexAttribPointerARB\");\n\t\tqglGetVertexAttribiv\t\t= (void *)getglext(\"glGetVertexAttribivARB\");\n\t\tqglGetVertexAttribPointerv\t= (void *)getglext(\"glGetVertexAttribPointervARB\");\n\t\tqglEnableVertexAttribArray\t= (void *)getglext(\"glEnableVertexAttribArrayARB\");\n\t\tqglDisableVertexAttribArray\t= (void *)getglext(\"glDisableVertexAttribArrayARB\");\n\t\tqglGetUniformLocationARB\t= (void *)getglext(\"glGetUniformLocationARB\");\n\t\tqglUniformMatrix4fvARB\t\t= (void *)getglext(\"glUniformMatrix4fvARB\");\n\t\tqglUniformMatrix3fvARB\t\t= (void *)getglext(\"glUniformMatrix3fvARB\");\n\t\tqglUniformMatrix3x4fv\t\t= (void *)getglext(\"glUniformMatrix3x4fvARB\");\n\t\tqglUniformMatrix4x3fv\t\t= (void *)getglext(\"glUniformMatrix4x3fvARB\");\n\t\tqglUniform4fARB\t\t\t\t= (void *)getglext(\"glUniform4fARB\");\n\t\tqglUniform4fvARB\t\t\t= (void *)getglext(\"glUniform4fvARB\");\n\t\tqglUniform3fARB\t\t\t\t= (void *)getglext(\"glUniform3fARB\");\n\t\tqglUniform3fvARB\t\t\t= (void *)getglext(\"glUniform3fvARB\");\n\t\tqglUniform2fvARB\t\t\t= (void *)getglext(\"glUniform2fvARB\");\n\t\tqglUniform1iARB\t\t\t\t= (void *)getglext(\"glUniform1iARB\");\n\t\tqglUniform1fARB\t\t\t\t= (void *)getglext(\"glUniform1fARB\");\n\t\tqglGetShaderSource\t\t\t= (void *)getglext(\"glGetShaderSourceARB\");\n\n\t\tCon_DPrintf(\"GLSL available\\n\");\n\t}\n#endif\n#endif\n\n\tif (Cvar_Get(\"gl_blacklist_invariant\", \"0\", CVAR_VIDEOLATCH, \"gl blacklists\")->ival)\n\t\tgl_config.blacklist_invariant = true;\n\telse if (gl_config.arb_shader_objects && !gl_config_nofixedfunc &&\n\t\t(strstr(gl_renderer, \" Mesa \") || strstr(gl_version, \" Mesa \")) && Cvar_Get(\"gl_blacklist_mesa_invariant\", \"1\", CVAR_VIDEOLATCH, \"gl blacklists\")->ival)\n\t{\t//should be fixed with 19.1.0\n\t\tif (GL_IsVersionTuppleWithin(mesaver, 0,0,1/*fixme*/, 19,1,0))\n\t\t{\n\t\t\tgl_config.blacklist_invariant = true;\t//assume yes\n\t\t\tCon_Printf(CON_NOTICE \"Mesa detected, disabling the use of glsl's invariant keyword.\"CON_DEFAULT\" This will result in z-fighting. Use '+set gl_blacklist_mesa_invariant 0' on the commandline to reenable it (but you will probably get glsl compilation errors from your driver).\\n\");\n\t\t}\n\t}\n\n\tqglGetProgramBinary = NULL;\n\tqglProgramBinary = NULL;\n\tif (gl_config.arb_shader_objects)\n\t{\n\t\tif (gl_config.glversion >= 4.1 || GL_CheckExtension(\"GL_ARB_get_program_binary\"))\n\t\t{\n\t\t\tqglGetProgramBinary = (void *)getglext(\"glGetProgramBinary\");\n\t\t\tqglProgramBinary = (void *)getglext(\"glProgramBinary\");\n\t\t}\n\t\telse if (GL_CheckExtension(\"GL_OES_get_program_binary\"))\n\t\t{\n\t\t\t//no PROGRAM_BINARY_RETRIEVABLE_HINT\n\t\t\tqglGetProgramBinary = (void *)getglext(\"glGetProgramBinaryOES\");\n\t\t\tqglProgramBinary = (void *)getglext(\"glProgramBinaryOES\");\n\t\t}\n\t\tqglGetIntegerv(GL_MAX_VERTEX_ATTRIBS_ARB, &gl_config.maxattribs);\n\t}\n\n\tif (gl_config.glversion >= 4.5)\t//the core version\n\t\tqglGetGraphicsResetStatus = (void *)getglext(\"glGetGraphicsResetStatus\");\n\telse if (GL_CheckExtension(\"GL_ARB_robustness\"))\t//desktop extension\n\t\tqglGetGraphicsResetStatus = (void *)getglext(\"glGetGraphicsResetStatusARB\");\n\telse if (GL_CheckExtension(\"GL_KHR_robustness\"))\t//glorified gles extension\n\t\tqglGetGraphicsResetStatus = (void *)getglext(\"glGetGraphicsResetStatusKHR\");\n\telse\n\t\tqglGetGraphicsResetStatus = NULL;\t\t\t\t//its not allowed to crash us. probably will. grr. oh well.\n\n\t//we only use vao if we don't have a choice.\n\t//certain drivers (*cough* mesa *cough*) update vao0 state even when a different vao is bound.\n\t//they also don't support client arrays, so are unusable without glsl or vertex streaming (which is *really* hard to optimise for - especially with webgl etc)\n\t//so only use them with gl3+ core contexts where vbo is mandatory anyway.\n\tif (!gl_config_nofixedfunc)\n\t{\n\t\t//don't bother if we've no glsl\n\t\tqglGenVertexArrays\t= NULL;\n\t\tqglBindVertexArray\t= NULL;\n\t}\n\telse if (gl_config.glversion >= 3 && !gl_config.gles)\n\t{\n\t\t/*yay core!*/\n\t\tCon_Printf(\"Using vao (core)\\n\");\n\t\tqglGenVertexArrays\t= (void *)getglext(\"glGenVertexArrays\");\n\t\tqglBindVertexArray\t= (void *)getglext(\"glBindVertexArray\");\n\t}\n\telse if (GL_CheckExtension(\"GL_ARB_vertex_array_object\"))\n\t{\n\t\tCon_Printf(\"Using vao (extension)\\n\");\n\t\tqglGenVertexArrays\t= (void *)getglext(\"glGenVertexArraysARB\");\n\t\tqglBindVertexArray\t= (void *)getglext(\"glBindVertexArrayARB\");\n\t}\n\telse\n\t{\n\t\tqglGenVertexArrays\t= NULL;\n\t\tqglBindVertexArray\t= NULL;\n\t}\n\n\tif (gl_config.gles)\n\t{\t//gles has different TexImage2D arguments for specifying quality.\n\t\tgl_config.arb_depth_texture = gl_config.glversion >= 3.0\n\t\t\t\t\t\t\t\t\t|| GL_CheckExtension(\"GL_OES_depth_texture\")\t//gles2\n\t\t\t\t\t\t\t\t\t|| GL_CheckExtension(\"GL_WEBGL_depth_texture\")\t//webgl. duh.\n\t\t\t\t\t\t\t\t\t|| GL_CheckExtension(\"GL_ANGLE_depth_texture\");\t//gah. should just use wildcards huh (no uploads)\n\t\tgl_config.arb_shadow = gl_config.glversion>=3.0;//||GL_CheckExtension(\"GL_EXT_shadow_samplers\");\n\t}\n\telse\n\t{\n\t\tgl_config.arb_depth_texture = gl_config.glversion>=1.4 || GL_CheckExtension(\"GL_ARB_depth_texture\");\n\t\tgl_config.arb_shadow = gl_config.glversion>=1.4||GL_CheckExtension(\"GL_ARB_shadow\");\n\t}\n\t//gl_config.arb_shadow |= GL_CheckExtension(\"GL_EXT_shadow_samplers\");\t//gles2. nvidia fucks up. depend on brute-force. :s\n\n\tif (GL_CheckExtension(\"GL_ARB_seamless_cube_map\"))\n\t\tqglEnable(0x884F);\t//TEXTURE_CUBE_MAP_SEAMLESS                   0x884F\n\n\tif (gl_config.gles)\n\t\tgl_config.geometryshaders = (gl_config.glversion >= 3.2) || GL_CheckExtension(\"GL_OES_geometry_shader\");\n\telse\n\t\tgl_config.geometryshaders = (gl_config.glversion >= 3.2);\n\n\tqglTexStorage2D = NULL;\n\tqglTexStorage3D = NULL;\n\tif ((!gl_config.gles && gl_config.glversion >= 4.2) ||\n\t\t( gl_config.gles && gl_config.glversion >= 3.0))\n\t{\t//from gles3.0 or gl4.2 onwards\n\t\tif (gl_immutable_textures.ival)\n\t\t{\n\t\t\tqglTexStorage2D = getglext(\"glTexStorage2D\");\n\t\t\tqglTexStorage3D = getglext(\"glTexStorage3D\");\n\t\t}\n\t}\n\n#ifdef GL_STATIC\n\tgl_config.ext_framebuffer_objects = true;\t\t\t\t//exists as core in gles2\n#else\n\tif ((gl_config.gles && gl_config.glversion >= 2) ||\t\t//exists as core in gles2\n\t\t(!gl_config.gles && gl_config.glversion >= 3) ||\t//exists as core in gl3\n\t\tGL_CheckExtension(\"GL_ARB_framebuffer_object\"))\t\t//exists as an extension in gl2 (defined in terms of gl3 so no ARB postfix needed)\n\t{\n\t\tgl_config.ext_framebuffer_objects = true;\n\t\tqglGenFramebuffersEXT\t\t\t= (void *)getglext(\"glGenFramebuffers\");\n\t\tqglDeleteFramebuffersEXT\t\t= (void *)getglext(\"glDeleteFramebuffers\");\n\t\tqglBindFramebufferEXT\t\t\t= (void *)getglext(\"glBindFramebuffer\");\n\t\tqglGenRenderbuffersEXT\t\t\t= (void *)getglext(\"glGenRenderbuffers\");\n\t\tqglDeleteRenderbuffersEXT\t\t= (void *)getglext(\"glDeleteRenderbuffers\");\n\t\tqglBindRenderbufferEXT\t\t\t= (void *)getglext(\"glBindRenderbuffer\");\n\t\tqglRenderbufferStorageEXT\t\t= (void *)getglext(\"glRenderbufferStorage\");\n\t\tqglFramebufferTexture2DEXT\t\t= (void *)getglext(\"glFramebufferTexture2D\");\n\t\tqglFramebufferRenderbufferEXT\t= (void *)getglext(\"glFramebufferRenderbuffer\");\n\t\tqglCheckFramebufferStatusEXT\t= (void *)getglext(\"glCheckFramebufferStatus\");\n\t\tqglGetFramebufferAttachmentParameteriv\t= (void *)getglext(\"glGetFramebufferAttachmentParameteriv\");\n\n\t\tgl_config.arb_framebuffer_srgb = GL_CheckExtension(\"GL_ARB_framebuffer_sRGB\");\n\t}\n\telse if (GL_CheckExtension(\"GL_EXT_framebuffer_object\"))\n\t{\n\t\tgl_config.ext_framebuffer_objects = true;\n\t\tqglGenFramebuffersEXT\t\t\t= (void *)getglext(\"glGenFramebuffersEXT\");\n\t\tqglDeleteFramebuffersEXT\t\t= (void *)getglext(\"glDeleteFramebuffersEXT\");\n\t\tqglBindFramebufferEXT\t\t\t= (void *)getglext(\"glBindFramebufferEXT\");\n\t\tqglGenRenderbuffersEXT\t\t\t= (void *)getglext(\"glGenRenderbuffersEXT\");\n\t\tqglDeleteRenderbuffersEXT\t\t= (void *)getglext(\"glDeleteRenderbuffersEXT\");\n\t\tqglBindRenderbufferEXT\t\t\t= (void *)getglext(\"glBindRenderbufferEXT\");\n\t\tqglRenderbufferStorageEXT\t\t= (void *)getglext(\"glRenderbufferStorageEXT\");\n\t\tqglFramebufferTexture2DEXT\t\t= (void *)getglext(\"glFramebufferTexture2DEXT\");\n\t\tqglFramebufferRenderbufferEXT\t= (void *)getglext(\"glFramebufferRenderbufferEXT\");\n\t\tqglCheckFramebufferStatusEXT\t= (void *)getglext(\"glCheckFramebufferStatusEXT\");\n\t\tqglGetFramebufferAttachmentParameteriv\t= (void *)getglext(\"glGetFramebufferAttachmentParameterivEXT\");\n\t}\n/*\t//I don't think we care about the differences, so this code should be safe, but I have no way to test that theory right now\n\telse if (GL_CheckExtension(\"GL_OES_framebuffer_object\"))\n\t{\n\t\tgl_config.ext_framebuffer_objects = true;\n\t\tqglGenFramebuffersEXT\t\t\t= (void *)getglext(\"glGenFramebuffersOES\");\n\t\tqglDeleteFramebuffersEXT\t\t= (void *)getglext(\"glDeleteFramebuffersOES\");\n\t\tqglBindFramebufferEXT\t\t\t= (void *)getglext(\"glBindFramebufferOES\");\n\t\tqglGenRenderbuffersEXT\t\t\t= (void *)getglext(\"glGenRenderbuffersOES\");\n\t\tqglDeleteRenderbuffersEXT\t\t= (void *)getglext(\"glDeleteRenderbuffersOES\");\n\t\tqglBindRenderbufferEXT\t\t\t= (void *)getglext(\"glBindRenderbufferOES\");\n\t\tqglRenderbufferStorageEXT\t\t= (void *)getglext(\"glRenderbufferStorageOES\");\n\t\tqglFramebufferTexture2DEXT\t\t= (void *)getglext(\"glFramebufferTexture2DOES\");\n\t\tqglFramebufferRenderbufferEXT\t= (void *)getglext(\"glFramebufferRenderbufferOES\");\n\t\tqglCheckFramebufferStatusEXT\t= (void *)getglext(\"glCheckFramebufferStatusOES\");\n\t}\n*/\n#endif\n#ifdef DEBUG\n\tif (GL_CheckExtension(\"GL_ARB_debug_output\"))\n\t{\n\t\tqglDebugMessageControlARB\t= (void *)getglext(\"glDebugMessageControlARB\");\n\t\tqglDebugMessageInsertARB\t= (void *)getglext(\"glDebugMessageInsertARB\");\n\t\tqglDebugMessageCallbackARB\t= (void *)getglext(\"glDebugMessageCallbackARB\");\n\t\tqglGetDebugMessageLogARB\t= (void *)getglext(\"glGetDebugMessageLogARB\");\n\t}\n\telse if (GL_CheckExtension(\"GL_KHR_debug\"))\n\t{\n\t\tif (gl_config_gles)\n\t\t{\n\t\t\tqglDebugMessageControlARB\t= (void *)getglext(\"glDebugMessageControlKHR\");\n\t\t\tqglDebugMessageInsertARB\t= (void *)getglext(\"glDebugMessageInsertKHR\");\n\t\t\tqglDebugMessageCallbackARB\t= (void *)getglext(\"glDebugMessageCallbackKHR\");\n\t\t\tqglGetDebugMessageLogARB\t= (void *)getglext(\"glGetDebugMessageLogKHR\");\n\t\t}\n\t\telse\n\t\t{\n\t\t\tqglDebugMessageControlARB\t= (void *)getglext(\"glDebugMessageControl\");\n\t\t\tqglDebugMessageInsertARB\t= (void *)getglext(\"glDebugMessageInsert\");\n\t\t\tqglDebugMessageCallbackARB\t= (void *)getglext(\"glDebugMessageCallback\");\n\t\t\tqglGetDebugMessageLogARB\t= (void *)getglext(\"glGetDebugMessageLog\");\n\t\t}\n\t}\n\telse\n\t{\n\t\tqglDebugMessageControlARB = NULL;\n\t\tqglDebugMessageInsertARB = NULL;\n\t\tqglDebugMessageCallbackARB = NULL;\n\t\tqglGetDebugMessageLogARB = NULL;\n\t}\n#endif\n\n\tif (!gl_config.nofixedfunc)\n\t{\t//clamping breaks overbrights. so if this isn't deprecated, try to use it.\n\t\t//part of GL_ARB_color_buffer_float, made core with gl3, and then deprecated in gl3.1. *sigh*\n#define GL_CLAMP_VERTEX_COLOR\t\t0x891A\n#define GL_CLAMP_FRAGMENT_COLOR\t\t0x891B\n#define GL_CLAMP_READ_COLOR\t\t\t0x891C\n\t\tvoid (APIENTRY *qglClampColor)(GLenum target,GLenum clamp);\n\t\tqglClampColor\t= (void *)getglext(\"glClampColor\");\n\t\tif (!qglClampColor)\n\t\t\tqglClampColor\t= (void *)getglext(\"glClampColorARB\");\n\t\tif (qglClampColor)\n\t\t{\n\t\t\tqglClampColor(GL_CLAMP_VERTEX_COLOR,\tGL_FALSE);\n\t\t\tqglClampColor(GL_CLAMP_FRAGMENT_COLOR,\tGL_FALSE);\n\t\t\tqglClampColor(GL_CLAMP_READ_COLOR,\t\tGL_FALSE);\n\t\t}\n\t}\n\n\tif ((!gl_config.gles && gl_config.glversion >= 1.5) || (gl_config.gles && gl_config.glversion >= 3.0))\n\t{\n\t\tqglGenQueriesARB\t\t= (void *)getglext(\"glGenQueries\");\n\t\tqglDeleteQueriesARB\t\t= (void *)getglext(\"glDeleteQueries\");\n\t\tqglBeginQueryARB\t\t= (void *)getglext(\"glBeginQuery\");\n\t\tqglEndQueryARB\t\t\t= (void *)getglext(\"glEndQuery\");\n\t\tqglGetQueryObjectuivARB\t= (void *)getglext(\"glGetQueryObjectuiv\");\n\t}\n\telse if (GL_CheckExtension(\"GL_ARB_occlusion_query\"))\n\t{\n\t\tqglGenQueriesARB\t\t= (void *)getglext(\"glGenQueriesARB\");\n\t\tqglDeleteQueriesARB\t\t= (void *)getglext(\"glDeleteQueriesARB\");\n\t\tqglBeginQueryARB\t\t= (void *)getglext(\"glBeginQueryARB\");\n\t\tqglEndQueryARB\t\t\t= (void *)getglext(\"glEndQueryARB\");\n\t\tqglGetQueryObjectuivARB\t= (void *)getglext(\"glGetQueryObjectuivARB\");\n\t}\n\telse\n\t{\n\t\tqglGenQueriesARB\t\t= NULL;\n\t\tqglDeleteQueriesARB\t\t= NULL;\n\t\tqglBeginQueryARB\t\t= NULL;\n\t\tqglEndQueryARB\t\t\t= NULL;\n\t\tqglGetQueryObjectuivARB\t= NULL;\n\t}\n\n\tif (!gl_config.gles && gl_config_nofixedfunc)\n\t\tqglDisableClientState(GL_VERTEX_ARRAY);\n\n\tif (qglGenVertexArrays)\n\t{\n\t\tGLuint vao;\n\t\tqglGenVertexArrays(1, &vao);\n\t\tqglBindVertexArray(vao);\n\t\tqglGenVertexArrays = NULL;\n\t\tqglBindVertexArray = NULL;\n\t}\n\n\tCvar_LockUnsupportedRendererCvar(&r_halfrate, \"0\");\n\tCvar_LockUnsupportedRendererCvar(&r_shadow_raytrace, \"0\");\n\treturn true;\t//all okay.\n}\n\nstatic const char *glsl_hdrs[] =\n{\n\t\"sys/defs.h\",\n\t\t\t\"#define DEFS_DEFINED\\n\"\n\t\t\t\"#ifdef VERTEX_SHADER\\n\"\n//\t\t\t\t\"attribute vec3 v_position1;\" //defined elsewhere, depending on fixed function availability\n//\t\t\t\t\"attribute vec3 v_position2;\"\n\t\t\t\t\"attribute vec4 v_colour;\"\n\t\t\t\t\"attribute vec2 v_texcoord;\"\n\t\t\t\t\"attribute vec2 v_lmcoord;\"\n\t\t\t\t\"attribute vec3 v_normal;\"\n\t\t\t\t\"attribute vec3 v_svector;\"\n\t\t\t\t\"attribute vec3 v_tvector;\"\n\t\t\t\t\"attribute vec4 v_bone;\"\t//fixme: make ints\n\t\t\t\t\"attribute vec4 v_weight;\"\n#if MAXRLIGHTMAPS > 1\n\t\t\t\t\"\\n#define v_lmcoord1 v_lmcoord\\n\"\n\t\t\t\t\"attribute vec2 v_lmcoord2;\"\n\t\t\t\t\"attribute vec2 v_lmcoord3;\"\n\t\t\t\t\"attribute vec2 v_lmcoord4;\"\n\t\t\t\t\"\\n#define v_colour1 v_colour\\n\"\n\t\t\t\t\"attribute vec4 v_colour2;\"\n\t\t\t\t\"attribute vec4 v_colour3;\"\n\t\t\t\t\"attribute vec4 v_colour4;\"\n#endif\n\t\t\t\"\\n#endif\\n\"\n\n\t\t\t\"#ifndef SPECEXP\\n\"\n\t\t\t\t\"#define SPECEXP 1.0\\n\"\n\t\t\t\"#endif\\n\"\n\t\t\t\"#ifndef SPECULAR_BASE_POW\\n\"\n\t\t\t\t\"#define SPECULAR_BASE_POW 32.0\\n\"\n\t\t\t\t\"#define SPECULAR_BASE_MUL 1.0\\n\"\n\t\t\t\"#endif\\n\"\n\t\t\t\"#ifndef FTE_SPECULAR_EXPONENT\\n\"\n\t\t\t\t\"#define FTE_SPECULAR_EXPONENT (SPECULAR_BASE_POW*float(SPECEXP))\\n\"\n\t\t\t\"#endif\\n\"\n\t\t\t\"#ifndef SPECMUL\\n\"\n\t\t\t\t\"#define SPECMUL 1.0\\n\"\n\t\t\t\"#endif\\n\"\n\t\t\t\"#define FTE_SPECULAR_MULTIPLIER (SPECULAR_BASE_MUL*float(SPECMUL))\\n\"\n#if 0//def HAVE_LEGACY\n\t\t\t\"uniform sampler2DShadow s_shadowmap;\"\n\t\t\t\"uniform samplerCube s_projectionmap;\"\n\t\t\t\"uniform sampler2D s_diffuse;\"\n\t\t\t\"uniform sampler2D s_normalmap;\"\n\t\t\t\"uniform sampler2D s_specular;\"\n\t\t\t\"uniform sampler2D s_upper;\"\n\t\t\t\"uniform sampler2D s_lower;\"\n\t\t\t\"uniform sampler2D s_fullbright;\"\n\t\t\t\"uniform sampler2D s_paletted;\"\n\t\t\t\"uniform samplerCube s_reflectcube;\"\n\t\t\t\"uniform sampler2D s_reflectmask;\"\n\t\t\t\"uniform sampler2D s_lightmap;\"\n\t\t\t\"uniform sampler2D s_deluxemap;\"\n\t\t\t\"\\n#define s_lightmap0 s_lightmap\\n\"\n\t\t\t\"#define s_deluxemap0 s_deluxemap\\n\"\n#if MAXRLIGHTMAPS > 1\n\t\t\t\"uniform sampler2D s_lightmap1;\"\n\t\t\t\"uniform sampler2D s_lightmap2;\"\n\t\t\t\"uniform sampler2D s_lightmap3;\"\n\t\t\t\"uniform sampler2D s_deluxemap1;\"\n\t\t\t\"uniform sampler2D s_deluxemap2;\"\n\t\t\t\"uniform sampler2D s_deluxemap3;\\n\"\n\n\t\t\t//FIXME: remove these some time.\n//\t\t\t\"#define s_deluxmap s_deluxemap\\n\"\n//\t\t\t\"#define s_deluxmap0 s_deluxemap0\\n\"\n//\t\t\t\"#define s_deluxmap1 s_deluxemap1\\n\"\n//\t\t\t\"#define s_deluxmap2 s_deluxemap2\\n\"\n//\t\t\t\"#define s_deluxmap3 s_deluxemap3\\n\"\n#endif\n#endif\n\t\t\t\"#if defined(ORM) || defined(SG)\\n\"\n\t\t\t\t\"uniform vec4 factors[5];\\n\"\n\t\t\t\t\"#define factor_base factors[0]\\n\"\n\t\t\t\t\"#define factor_spec factors[1]\\n\"\n\t\t\t\t\"#define factor_emit factors[2]\\n\"\n\t\t\t\t\"#define factor_transmission factors[3].r\\n\"\n\t\t\t\t\"#define factor_volume_distance factors[3].g\\n\"\n//\t\t\t\t\"#define factor_? factors[3].b\\n\"\n//\t\t\t\t\"#define factor_? factors[3].a\\n\"\n\t\t\t\t\"#define factor_volume_rgb factors[4].rgb\\n\"\n\t\t\t\t\"#define factor_volume_thickness factors[4].a\\n\"\n\t\t\t\"#else\\n\"\n\t\t\t\t\"#define factor_base vec4(1.0)\\n\"\n\t\t\t\t\"#define factor_spec vec4(1.0)\\n\"\n\t\t\t\t\"#define factor_emit vec4(1.0)\\n\"\n\t\t\t\t/*\"#define factor_transmission 0.0\\n\"\n\t\t\t\t\"#define factor_volume_distance 0.0\\n\"\n\t\t\t\t\"#define factor_volume_rgb vec3(1.0)\\n\"\n\t\t\t\t\"#define factor_volume_thickness 0.0\\n\"*/\n\t\t\t\"#endif\\n\"\n\t\t\t\"#ifdef USEUBOS\\n\"\n\t\t\t\t\"layout(std140) uniform u_lightinfo\\n\"\n\t\t\t\t\"{\"\n\t\t\t\t\t\"vec3\t\tl_lightscreen;\"\n\t\t\t\t\t\"float\t\tl_lightradius;\"\n\t\t\t\t\t\"vec3\t\tl_lightcolour;\"\n\t\t\t\t\t\t\"float\t\tl_pad1;\\n\"\n\t\t\t\t\t\"vec3\t\tl_lightcolourscale;n\"\n\t\t\t\t\t\t\"float\t\tl_pad2;\"\n\t\t\t\t\t\"vec3\t\tl_lightposition;\"\n\t\t\t\t\t\t\"float\t\tl_pad3;\"\n\t\t\t\t\t\"mat4\t\tl_cubematrix;\"\n\t\t\t\t\t\"vec4\t\tl_shadowmapproj;\"\n\t\t\t\t\t\"vec2\t\tl_shadowmapscale;\"\n\t\t\t\t\t\t\"vec2\t\tl_pad4;\"\n\t\t\t\t\"};\\n\"\n\t\t\t\t\"layout(std140) uniform u_entityinfo\\n\"\n\t\t\t\t\"{\\n\"\n\t\t\t\t\t\"vec2\t\te_vblend;\"\n\t\t\t\t\t\t\"vec2\t\te_pad1;\"\n\t\t\t\t\t\"vec3\t\te_glowmod;\"\n\t\t\t\t\t\t\"float\t\te_pad2;\"\n\t\t\t\t\t\"vec3\t\te_origin;\"\n\t\t\t\t\t\t\"float\t\te_pad3;\"\n\t\t\t\t\t\"vec4\t\tcolormod;\"\n\t\t\t\t\t\"vec3\t\te_glowmod;\"\n\t\t\t\t\t\t\"float\t\te_pad4;\"\n\t\t\t\t\t\"vec3\t\te_uppercolour;\"\n\t\t\t\t\t\t\"float\t\te_pad5;\"\n\t\t\t\t\t\"vec3\t\te_lowercolour;\"\n\t\t\t\t\t\t\"float\t\te_pad6;\"\n\t\t\t\t\t\"vec3\t\tw_fogcolour;\"\n\t\t\t\t\t\"float\t\tw_fogalpha;\"\n\t\t\t\t\t\"vec3\t\te_light_dir;\"\n\t\t\t\t\t\"float\t\tw_fogdensity;\"\n\t\t\t\t\t\"vec3\t\te_light_mul;\"\n\t\t\t\t\t\"float\t\tw_fogdepthbias;\"\n\t\t\t\t\t\"vec3\t\te_light_ambient;\"\n\t\t\t\t\t\"float\t\te_time;\"\n\t\t\t\t\"};\\n\"\n\t\t\t\t\"#ifdef SKELETAL\\n\"\n\t\t\t\t\t\"layout(std140) uniform u_bones\\n\"\n\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\"#ifdef PACKEDBONES\\n\"\n\t\t\t\t\t\t\t\"vec4 m_bones_packed[3*MAX_GPU_BONES];\\n\"\n\t\t\t\t\t\t\"#else\\n\"\n\t\t\t\t\t\t\t\"mat3x4 m_bones_mat3x4[MAX_GPU_BONES]\\n\"\n\t\t\t\t\t\t\"#endif\\n\"\n\t\t\t\t\t\"};\\n\"\n\t\t\t\t\"#endif\\n\"\n\t\t\t\"#else\\n\"\n\t\t\t\t\"uniform mat4 m_model;\"\n\t\t\t\t\"uniform mat4 m_view;\"\n\t\t\t\t\"uniform mat4 m_modelview;\"\n\t\t\t\t\"uniform mat4 m_projection;\\n\"\n\t\t\t\t\"#ifndef VERTEX_SHADER\\n\"\n\t\t\t\t\t\"uniform mat4 m_modelviewprojection;\\n\"\n\t\t\t\t\"#endif\\n\"\n\t\t\t\t\"#ifdef SKELETAL\\n\"\t//skeletal permutation tends to require glsl 120\n\t\t\t\t\t\"#ifdef PACKEDBONES\\n\"\n\t\t\t\t\t\t\"uniform vec4 m_bones_packed[3*MAX_GPU_BONES];\\n\"\n\t\t\t\t\t\"#else\\n\"\n\t\t\t\t\t\t\"uniform mat3x4 m_bones_mat3x4[MAX_GPU_BONES];\\n\"\n\t\t\t\t\t\"#endif\\n\"\n\t\t\t\t\"#endif\\n\"\n\t\t\t\t\"uniform mat4 m_invviewprojection;\"\n\t\t\t\t\"uniform mat4 m_invmodelviewprojection;\"\n\t\t\t\t\"uniform mat3 m_invmodelview;\"\n\n\t\t\t\t/*viewer properties*/\n\t\t\t\t\"uniform vec3 v_eyepos;\"\n\t\t\t\t\"uniform vec4 w_fog[2];\\n\"\n\t\t\t\t\"#define w_fogcolour\tw_fog[0].rgb\\n\"\n\t\t\t\t\"#ifdef FOG\\n\"\n\t\t\t\t\"#define w_fogalpha\t\tw_fog[0].a\\n\"\n\t\t\t\t\"#else\\n\"\n\t\t\t\t\"#define w_fogalpha\t\t0.0\\n\"\n\t\t\t\t\"#endif\\n\"\n\t\t\t\t\"#define w_fogdensity\tw_fog[1].x\\n\"\n\t\t\t\t\"#define w_fogdepthbias\tw_fog[1].y\\n\"\n\t\t\t\t\"uniform vec4 w_user[16];\\n\"\n\n\t\t\t\t/*ent properties*/\n\t\t\t\t//\"uniform vec2 e_vblend;\\n\"\n\t\t\t\t\"#ifdef LIGHTSTYLED\\n\"\n\t\t\t\t\"uniform vec4 e_lmscale[4];\\n\"\n\t\t\t\t\"#else\\n\"\n\t\t\t\t\"uniform vec4 e_lmscale;\\n\"\n\t\t\t\t\"#endif\\n\"\n\t\t\t\t\"uniform vec3 e_origin;\"\n\t\t\t\t\"uniform float e_time;\"\n\t\t\t\t\"uniform vec3 e_eyepos;\"\n\t\t\t\t\"uniform vec4 e_colour;\"\n\t\t\t\t\"uniform vec4 e_colourident;\"\n\t\t\t\t\"uniform vec3 e_glowmod;\"\n\t\t\t\t\"uniform vec3 e_uppercolour;\"\n\t\t\t\t\"uniform vec3 e_lowercolour;\"\n\t\t\t\t\"uniform vec3 e_light_dir;\"\n\t\t\t\t\"uniform vec3 e_light_mul;\"\n\t\t\t\t\"uniform vec3 e_light_ambient;\"\n\n\t\t\t\t/*rtlight properties, use with caution*/\n\t\t\t\t\"uniform vec2\tl_lightscreen;\"\n\t\t\t\t\"uniform float\tl_lightradius;\"\n\t\t\t\t\"uniform vec3\tl_lightcolour;\"\n\t\t\t\t\"uniform vec3\tl_lightposition;\"\n\t\t\t\t\"uniform vec3\tl_lightdirection;\"\n\t\t\t\t\"uniform vec3\tl_lightcolourscale;\"\n\t\t\t\t\"uniform mat4\tl_cubematrix;\"\n\t\t\t\t\"uniform vec4\tl_shadowmapproj;\"\n\t\t\t\t\"uniform vec2\tl_shadowmapscale;\"\n\n\t\t\t\t\"uniform vec2 e_rendertexturescale;\\n\"\n\t\t\t\"#endif\\n\"\n\t\t,\n\t\"sys/skeletal.h\",\n\t\t\t\"#ifndef DEFS_DEFINED\\n\"\n\t\t\t\t\"attribute vec3 v_normal;\"\n\t\t\t\t\"attribute vec3 v_svector;\"\n\t\t\t\t\"attribute vec3 v_tvector;\"\n\t\t\t\"\\n#endif\\n\"\n\t\t\t\"#ifdef SKELETAL\\n\"\n\t\t\t\t\"#ifndef DEFS_DEFINED\\n\"\n\t\t\t\t\t\"attribute vec4 v_bone;\"\n\t\t\t\t\t\"attribute vec4 v_weight;\\n\"\n\t\t\t\t\t\"#ifdef PACKEDBONES\\n\"\n\t\t\t\t\t\t\"uniform vec4 m_bones_packed[3*MAX_GPU_BONES];\\n\"\n\t\t\t\t\t\"#else\\n\"\n\t\t\t\t\t\t\"uniform mat3x4 m_bones_mat3x4[MAX_GPU_BONES];\\n\"\n\t\t\t\t\t\"#endif\\n\"\n\t\t\t\t\"#endif\\n\"\n\t\t\t\t\n\t\t\t\t\"#ifdef PACKEDBONES\\n\"\n\t\t\t\t\t\"vec4 skeletaltransform()\"\n\t\t\t\t\t\"{\"\n\t\t\t\t\t\t\"mat4 wmat;\"\n\t\t\t\t\t\t\"wmat[0]  = m_bones_packed[0+3*int(v_bone.x)] * v_weight.x;\"\n\t\t\t\t\t\t\"wmat[0] += m_bones_packed[0+3*int(v_bone.y)] * v_weight.y;\"\n\t\t\t\t\t\t\"wmat[0] += m_bones_packed[0+3*int(v_bone.z)] * v_weight.z;\"\n\t\t\t\t\t\t\"wmat[0] += m_bones_packed[0+3*int(v_bone.w)] * v_weight.w;\"\n\t\t\t\t\t\t\"wmat[1]  = m_bones_packed[1+3*int(v_bone.x)] * v_weight.x;\"\n\t\t\t\t\t\t\"wmat[1] += m_bones_packed[1+3*int(v_bone.y)] * v_weight.y;\"\n\t\t\t\t\t\t\"wmat[1] += m_bones_packed[1+3*int(v_bone.z)] * v_weight.z;\"\n\t\t\t\t\t\t\"wmat[1] += m_bones_packed[1+3*int(v_bone.w)] * v_weight.w;\"\n\t\t\t\t\t\t\"wmat[2]  = m_bones_packed[2+3*int(v_bone.x)] * v_weight.x;\"\n\t\t\t\t\t\t\"wmat[2] += m_bones_packed[2+3*int(v_bone.y)] * v_weight.y;\"\n\t\t\t\t\t\t\"wmat[2] += m_bones_packed[2+3*int(v_bone.z)] * v_weight.z;\"\n\t\t\t\t\t\t\"wmat[2] += m_bones_packed[2+3*int(v_bone.w)] * v_weight.w;\"\n\t\t\t\t\t\t\"wmat[3] = vec4(0.0,0.0,0.0,1.0);\"\n\t\t\t\t\t\t\"return m_modelviewprojection * (vec4(v_position.xyz, 1.0) * wmat);\"\n\t\t\t\t\t\"}\\n\"\n\t\t\t\t\t\"vec4 skeletaltransform_nst(out vec3 n, out vec3 t, out vec3 b)\"\n\t\t\t\t\t\"{\"\n\t\t\t\t\t\t\"mat4 wmat;\"\n\t\t\t\t\t\t\"wmat[0]  = m_bones_packed[0+3*int(v_bone.x)] * v_weight.x;\"\n\t\t\t\t\t\t\"wmat[0] += m_bones_packed[0+3*int(v_bone.y)] * v_weight.y;\"\n\t\t\t\t\t\t\"wmat[0] += m_bones_packed[0+3*int(v_bone.z)] * v_weight.z;\"\n\t\t\t\t\t\t\"wmat[0] += m_bones_packed[0+3*int(v_bone.w)] * v_weight.w;\"\n\t\t\t\t\t\t\"wmat[1]  = m_bones_packed[1+3*int(v_bone.x)] * v_weight.x;\"\n\t\t\t\t\t\t\"wmat[1] += m_bones_packed[1+3*int(v_bone.y)] * v_weight.y;\"\n\t\t\t\t\t\t\"wmat[1] += m_bones_packed[1+3*int(v_bone.z)] * v_weight.z;\"\n\t\t\t\t\t\t\"wmat[1] += m_bones_packed[1+3*int(v_bone.w)] * v_weight.w;\"\n\t\t\t\t\t\t\"wmat[2]  = m_bones_packed[2+3*int(v_bone.x)] * v_weight.x;\"\n\t\t\t\t\t\t\"wmat[2] += m_bones_packed[2+3*int(v_bone.y)] * v_weight.y;\"\n\t\t\t\t\t\t\"wmat[2] += m_bones_packed[2+3*int(v_bone.z)] * v_weight.z;\"\n\t\t\t\t\t\t\"wmat[2] += m_bones_packed[2+3*int(v_bone.w)] * v_weight.w;\"\n\t\t\t\t\t\t\"wmat[3] = vec4(0.0,0.0,0.0,1.0);\"\n\t\t\t\t\t\t\"n = (vec4(v_normal.xyz, 0.0) * wmat).xyz;\"\n\t\t\t\t\t\t\"t = (vec4(v_svector.xyz, 0.0) * wmat).xyz;\"\n\t\t\t\t\t\t\"b = (vec4(v_tvector.xyz, 0.0) * wmat).xyz;\"\n\t\t\t\t\t\t\"return m_modelviewprojection * (vec4(v_position.xyz, 1.0) * wmat);\"\n\t\t\t\t\t\"}\\n\"\n\t\t\t\t\t\"vec4 skeletaltransform_wnst(out vec3 w, out vec3 n, out vec3 t, out vec3 b)\"\n\t\t\t\t\t\"{\"\n\t\t\t\t\t\t\"mat4 wmat;\"\n\t\t\t\t\t\t\"wmat[0]  = m_bones_packed[0+3*int(v_bone.x)] * v_weight.x;\"\n\t\t\t\t\t\t\"wmat[0] += m_bones_packed[0+3*int(v_bone.y)] * v_weight.y;\"\n\t\t\t\t\t\t\"wmat[0] += m_bones_packed[0+3*int(v_bone.z)] * v_weight.z;\"\n\t\t\t\t\t\t\"wmat[0] += m_bones_packed[0+3*int(v_bone.w)] * v_weight.w;\"\n\t\t\t\t\t\t\"wmat[1]  = m_bones_packed[1+3*int(v_bone.x)] * v_weight.x;\"\n\t\t\t\t\t\t\"wmat[1] += m_bones_packed[1+3*int(v_bone.y)] * v_weight.y;\"\n\t\t\t\t\t\t\"wmat[1] += m_bones_packed[1+3*int(v_bone.z)] * v_weight.z;\"\n\t\t\t\t\t\t\"wmat[1] += m_bones_packed[1+3*int(v_bone.w)] * v_weight.w;\"\n\t\t\t\t\t\t\"wmat[2]  = m_bones_packed[2+3*int(v_bone.x)] * v_weight.x;\"\n\t\t\t\t\t\t\"wmat[2] += m_bones_packed[2+3*int(v_bone.y)] * v_weight.y;\"\n\t\t\t\t\t\t\"wmat[2] += m_bones_packed[2+3*int(v_bone.z)] * v_weight.z;\"\n\t\t\t\t\t\t\"wmat[2] += m_bones_packed[2+3*int(v_bone.w)] * v_weight.w;\"\n\t\t\t\t\t\t\"wmat[3] = vec4(0.0,0.0,0.0,1.0);\"\n\t\t\t\t\t\t\"n = (vec4(v_normal.xyz, 0.0) * wmat).xyz;\"\n\t\t\t\t\t\t\"t = (vec4(v_svector.xyz, 0.0) * wmat).xyz;\"\n\t\t\t\t\t\t\"b = (vec4(v_tvector.xyz, 0.0) * wmat).xyz;\"\n\t\t\t\t\t\t\"w = (vec4(v_position.xyz, 1.0) * wmat).xyz;\"\n\t\t\t\t\t\t\"return m_modelviewprojection * (vec4(v_position.xyz, 1.0) * wmat);\"\n\t\t\t\t\t\"}\\n\"\n\t\t\t\t\t\"vec4 skeletaltransform_wnst(out vec3 w)\"\n\t\t\t\t\t\"{\"\n\t\t\t\t\t\t\"mat4 wmat;\"\n\t\t\t\t\t\t\"wmat[0]  = m_bones_packed[0+3*int(v_bone.x)] * v_weight.x;\"\n\t\t\t\t\t\t\"wmat[0] += m_bones_packed[0+3*int(v_bone.y)] * v_weight.y;\"\n\t\t\t\t\t\t\"wmat[0] += m_bones_packed[0+3*int(v_bone.z)] * v_weight.z;\"\n\t\t\t\t\t\t\"wmat[0] += m_bones_packed[0+3*int(v_bone.w)] * v_weight.w;\"\n\t\t\t\t\t\t\"wmat[1]  = m_bones_packed[1+3*int(v_bone.x)] * v_weight.x;\"\n\t\t\t\t\t\t\"wmat[1] += m_bones_packed[1+3*int(v_bone.y)] * v_weight.y;\"\n\t\t\t\t\t\t\"wmat[1] += m_bones_packed[1+3*int(v_bone.z)] * v_weight.z;\"\n\t\t\t\t\t\t\"wmat[1] += m_bones_packed[1+3*int(v_bone.w)] * v_weight.w;\"\n\t\t\t\t\t\t\"wmat[2]  = m_bones_packed[2+3*int(v_bone.x)] * v_weight.x;\"\n\t\t\t\t\t\t\"wmat[2] += m_bones_packed[2+3*int(v_bone.y)] * v_weight.y;\"\n\t\t\t\t\t\t\"wmat[2] += m_bones_packed[2+3*int(v_bone.z)] * v_weight.z;\"\n\t\t\t\t\t\t\"wmat[2] += m_bones_packed[2+3*int(v_bone.w)] * v_weight.w;\"\n\t\t\t\t\t\t\"wmat[3] = vec4(0.0,0.0,0.0,1.0);\"\n\t\t\t\t\t\t\"w = (vec4(v_position.xyz, 1.0) * wmat).xyz;\"\n\t\t\t\t\t\t\"return m_modelviewprojection * (vec4(v_position.xyz, 1.0) * wmat);\"\n\t\t\t\t\t\"}\\n\"\n\t\t\t\t\t\"vec4 skeletaltransform_n(out vec3 n)\"\n\t\t\t\t\t\"{\"\n\t\t\t\t\t\t\"mat4 wmat;\"\n\t\t\t\t\t\t\"wmat[0]  = m_bones_packed[0+3*int(v_bone.x)] * v_weight.x;\"\n\t\t\t\t\t\t\"wmat[0] += m_bones_packed[0+3*int(v_bone.y)] * v_weight.y;\"\n\t\t\t\t\t\t\"wmat[0] += m_bones_packed[0+3*int(v_bone.z)] * v_weight.z;\"\n\t\t\t\t\t\t\"wmat[0] += m_bones_packed[0+3*int(v_bone.w)] * v_weight.w;\"\n\t\t\t\t\t\t\"wmat[1]  = m_bones_packed[1+3*int(v_bone.x)] * v_weight.x;\"\n\t\t\t\t\t\t\"wmat[1] += m_bones_packed[1+3*int(v_bone.y)] * v_weight.y;\"\n\t\t\t\t\t\t\"wmat[1] += m_bones_packed[1+3*int(v_bone.z)] * v_weight.z;\"\n\t\t\t\t\t\t\"wmat[1] += m_bones_packed[1+3*int(v_bone.w)] * v_weight.w;\"\n\t\t\t\t\t\t\"wmat[2]  = m_bones_packed[2+3*int(v_bone.x)] * v_weight.x;\"\n\t\t\t\t\t\t\"wmat[2] += m_bones_packed[2+3*int(v_bone.y)] * v_weight.y;\"\n\t\t\t\t\t\t\"wmat[2] += m_bones_packed[2+3*int(v_bone.z)] * v_weight.z;\"\n\t\t\t\t\t\t\"wmat[2] += m_bones_packed[2+3*int(v_bone.w)] * v_weight.w;\"\n\t\t\t\t\t\t\"wmat[3] = vec4(0.0,0.0,0.0,1.0);\"\n\t\t\t\t\t\t\"n = (vec4(v_normal.xyz, 0.0) * wmat).xyz;\"\n\t\t\t\t\t\t\"return m_modelviewprojection * (vec4(v_position.xyz, 1.0) * wmat);\"\n\t\t\t\t\t\"}\\n\"\n\t\t\t\t\"#else\\n\"\n\t\t\t\t\t\"vec4 skeletaltransform()\"\n\t\t\t\t\t\"{\"\n\t\t\t\t\t\t\"mat3x4 wmat;\"\n\t\t\t\t\t\t\"wmat = m_bones_mat3x4[int(v_bone.x)] * v_weight.x;\"\n\t\t\t\t\t\t\"wmat += m_bones_mat3x4[int(v_bone.y)] * v_weight.y;\"\n\t\t\t\t\t\t\"wmat += m_bones_mat3x4[int(v_bone.z)] * v_weight.z;\"\n\t\t\t\t\t\t\"wmat += m_bones_mat3x4[int(v_bone.w)] * v_weight.w;\"\n\t\t\t\t\t\t\"return m_modelviewprojection * vec4(vec4(v_position.xyz, 1.0) * wmat, 1.0);\"\n\t\t\t\t\t\"}\\n\"\n\t\t\t\t\t\"vec4 skeletaltransform_nst(out vec3 n, out vec3 t, out vec3 b)\"\n\t\t\t\t\t\"{\"\n\t\t\t\t\t\t\"mat3x4 wmat;\"\n\t\t\t\t\t\t\"wmat = m_bones_mat3x4[int(v_bone.x)] * v_weight.x;\"\n\t\t\t\t\t\t\"wmat += m_bones_mat3x4[int(v_bone.y)] * v_weight.y;\"\n\t\t\t\t\t\t\"wmat += m_bones_mat3x4[int(v_bone.z)] * v_weight.z;\"\n\t\t\t\t\t\t\"wmat += m_bones_mat3x4[int(v_bone.w)] * v_weight.w;\"\n\t\t\t\t\t\t\"n = vec4(v_normal.xyz, 0.0) * wmat;\"\n\t\t\t\t\t\t\"t = vec4(v_svector.xyz, 0.0) * wmat;\"\n\t\t\t\t\t\t\"b = vec4(v_tvector.xyz, 0.0) * wmat;\"\n\t\t\t\t\t\t\"return m_modelviewprojection * vec4(vec4(v_position.xyz, 1.0) * wmat, 1.0);\"\n\t\t\t\t\t\"}\\n\"\n\t\t\t\t\t\"vec4 skeletaltransform_wnst(out vec3 w, out vec3 n, out vec3 t, out vec3 b)\"\n\t\t\t\t\t\"{\"\n\t\t\t\t\t\t\"mat3x4 wmat;\"\n\t\t\t\t\t\t\"wmat = m_bones_mat3x4[int(v_bone.x)] * v_weight.x;\"\n\t\t\t\t\t\t\"wmat += m_bones_mat3x4[int(v_bone.y)] * v_weight.y;\"\n\t\t\t\t\t\t\"wmat += m_bones_mat3x4[int(v_bone.z)] * v_weight.z;\"\n\t\t\t\t\t\t\"wmat += m_bones_mat3x4[int(v_bone.w)] * v_weight.w;\"\n\t\t\t\t\t\t\"n = vec4(v_normal.xyz, 0.0) * wmat;\"\n\t\t\t\t\t\t\"t = vec4(v_svector.xyz, 0.0) * wmat;\"\n\t\t\t\t\t\t\"b = vec4(v_tvector.xyz, 0.0) * wmat;\"\n\t\t\t\t\t\t\"w = vec4(v_position.xyz, 1.0) * wmat;\"\n\t\t\t\t\t\t\"return m_modelviewprojection * vec4(w, 1.0);\"\n\t\t\t\t\t\"}\\n\"\n\t\t\t\t\t\"vec4 skeletaltransform_w(out vec3 w)\"\n\t\t\t\t\t\"{\"\n\t\t\t\t\t\t\"mat3x4 wmat;\"\n\t\t\t\t\t\t\"wmat = m_bones_mat3x4[int(v_bone.x)] * v_weight.x;\"\n\t\t\t\t\t\t\"wmat += m_bones_mat3x4[int(v_bone.y)] * v_weight.y;\"\n\t\t\t\t\t\t\"wmat += m_bones_mat3x4[int(v_bone.z)] * v_weight.z;\"\n\t\t\t\t\t\t\"wmat += m_bones_mat3x4[int(v_bone.w)] * v_weight.w;\"\n\t\t\t\t\t\t\"w = vec4(v_position.xyz, 1.0) * wmat;\"\n\t\t\t\t\t\t\"return m_modelviewprojection * vec4(w, 1.0);\"\n\t\t\t\t\t\"}\\n\"\n\t\t\t\t\t\"vec4 skeletaltransform_n(out vec3 n)\"\n\t\t\t\t\t\"{\"\n\t\t\t\t\t\t\"mat3x4 wmat;\"\n\t\t\t\t\t\t\"wmat = m_bones_mat3x4[int(v_bone.x)] * v_weight.x;\"\n\t\t\t\t\t\t\"wmat += m_bones_mat3x4[int(v_bone.y)] * v_weight.y;\"\n\t\t\t\t\t\t\"wmat += m_bones_mat3x4[int(v_bone.z)] * v_weight.z;\"\n\t\t\t\t\t\t\"wmat += m_bones_mat3x4[int(v_bone.w)] * v_weight.w;\"\n\t\t\t\t\t\t\"n = vec4(v_normal.xyz, 0.0) * wmat;\"\n\t\t\t\t\t\t\"return m_modelviewprojection * vec4(vec4(v_position.xyz, 1.0) * wmat, 1.0);\"\n\t\t\t\t\t\"}\\n\"\n\t\t\t\t\"#endif\\n\"\n\t\t\t\"#else\\n\"\n\t\t\t\t\"#define skeletaltransform ftetransform\\n\"\n\t\t\t\t\"vec4 skeletaltransform_wnst(out vec3 w, out vec3 n, out vec3 t, out vec3 b)\"\n\t\t\t\t\"{\"\n\t\t\t\t\t\"n = v_normal;\"\n\t\t\t\t\t\"t = v_svector;\"\n\t\t\t\t\t\"b = v_tvector;\"\n\t\t\t\t\t\"w = v_position.xyz;\"\n\t\t\t\t\t\"return ftetransform();\"\n\t\t\t\t\"}\\n\"\n\t\t\t\t\"vec4 skeletaltransform_w(out vec3 w)\"\n\t\t\t\t\"{\"\n\t\t\t\t\t\"w = v_position.xyz;\"\n\t\t\t\t\t\"return ftetransform();\"\n\t\t\t\t\"}\\n\"\n\t\t\t\t\"vec4 skeletaltransform_nst(out vec3 n, out vec3 t, out vec3 b)\"\n\t\t\t\t\"{\"\n\t\t\t\t\t\"n = v_normal;\"\n\t\t\t\t\t\"t = v_svector;\"\n\t\t\t\t\t\"b = v_tvector;\"\n\t\t\t\t\t\"return ftetransform();\"\n\t\t\t\t\"}\\n\"\n\t\t\t\t\"vec4 skeletaltransform_n(out vec3 n)\"\n\t\t\t\t\"{\"\n\t\t\t\t\t\"n = v_normal;\"\n\t\t\t\t\t\"return ftetransform();\"\n\t\t\t\t\"}\\n\"\n\t\t\t\"#endif\\n\"\n\t\t,\n\t\"sys/fog.h\",\n\t\t\t\"#ifdef FRAGMENT_SHADER\\n\"\n\t\t\t\t\"#ifdef FOG\\n\"\n\t\t\t\t\t\"#ifndef DEFS_DEFINED\\n\"\n\t\t\t\t\t\"uniform vec4 w_fog[2];\\n\"\n\t\t\t\t\t\"#define w_fogcolour\tw_fog[0].rgb\\n\"\n\t\t\t\t\t\"#define w_fogalpha\t\tw_fog[0].a\\n\"\n\t\t\t\t\t\"#define w_fogdensity\tw_fog[1].x\\n\"\n\t\t\t\t\t\"#define w_fogdepthbias\tw_fog[1].y\\n\"\n\t\t\t\t\t\"#endif\\n\"\n\n\t\t\t\t\t\"vec3 fog3(in vec3 regularcolour)\"\n\t\t\t\t\t\"{\"\n\t\t\t\t\t\t\"float z, fac;\\n\"\n\t\t\t\t\t\t\"#if #include \\\"cvar/r_fog_linear\\\"\\n\"\n\t\t\t\t\t\t\t\"z = gl_FragCoord.z / gl_FragCoord.w;\\n\"\n\t\t\t\t\t\t\t\"fac = (w_fogdensity - z) / (w_fogdensity - w_fogdepthbias);\\n\"\n\t\t\t\t\t\t\"#else\\n\"\n\t\t\t\t\t\t\t\"z = w_fogdensity * gl_FragCoord.z / gl_FragCoord.w;\\n\"\n\t\t\t\t\t\t\t\"z = max(0.0,z-w_fogdepthbias);\\n\"\n\t\t\t\t\t\t\t\"#if #include \\\"cvar/r_fog_exp2\\\"\\n\"\n\t\t\t\t\t\t\t\t\"z *= z;\\n\"\n\t\t\t\t\t\t\t\"#endif\\n\"\n\t\t\t\t\t\t\t\"fac = exp2(-(z * 1.442695));\\n\"\n\t\t\t\t\t\t\"#endif\\n\"\n\t\t\t\t\t\t\"fac = (1.0-w_fogalpha) + (clamp(fac, 0.0, 1.0)*w_fogalpha);\\n\"\n\t\t\t\t\t\t\"return mix(w_fogcolour, regularcolour, fac);\\n\"\n\t\t\t\t\t\"}\\n\"\n\t\t\t\t\t\"vec3 fog3additive(in vec3 regularcolour)\"\n\t\t\t\t\t\"{\"\n\t\t\t\t\t\t\"float z, fac;\\n\"\n\t\t\t\t\t\t\"#if #include \\\"cvar/r_fog_linear\\\"\\n\"\n\t\t\t\t\t\t\t\"z = gl_FragCoord.z / gl_FragCoord.w;\\n\"\n\t\t\t\t\t\t\t\"fac = (w_fogdensity - z) / (w_fogdensity - w_fogdepthbias);\\n\"\n\t\t\t\t\t\t\"#else\\n\"\n\t\t\t\t\t\t\t\"z = w_fogdensity * gl_FragCoord.z / gl_FragCoord.w;\\n\"\n\t\t\t\t\t\t\t\"z = max(0.0,z-w_fogdepthbias);\\n\"\n\t\t\t\t\t\t\t\"#if #include \\\"cvar/r_fog_exp2\\\"\\n\"\n\t\t\t\t\t\t\t\"z *= z;\\n\"\n\t\t\t\t\t\t\t\"#endif\\n\"\n\t\t\t\t\t\t\t\"fac = exp2(-(z * 1.442695));\\n\"\n\t\t\t\t\t\t\"#endif\\n\"\n\t\t\t\t\t\t\"fac = (1.0-w_fogalpha) + (clamp(fac, 0.0, 1.0)*w_fogalpha);\\n\"\n\t\t\t\t\t\t\"return regularcolour * fac;\\n\"\n\t\t\t\t\t\"}\\n\"\n\t\t\t\t\t\"vec4 fog4(in vec4 regularcolour)\"\n\t\t\t\t\t\"{\"\n\t\t\t\t\t\t\"return vec4(fog3(regularcolour.rgb), 1.0) * regularcolour.a;\\n\"\n\t\t\t\t\t\"}\\n\"\n\t\t\t\t\t\"vec4 fog4additive(in vec4 regularcolour)\"\n\t\t\t\t\t\"{\"\t//fog function for additive blends\n\t\t\t\t\t\t\"float z, fac;\\n\"\n\t\t\t\t\t\t\"#if #include \\\"cvar/r_fog_linear\\\"\\n\"\n\t\t\t\t\t\t\t\"z = gl_FragCoord.z / gl_FragCoord.w;\\n\"\n\t\t\t\t\t\t\t\"fac = (w_fogdensity - z) / (w_fogdensity - w_fogdepthbias);\\n\"\n\t\t\t\t\t\t\"#else\\n\"\n\t\t\t\t\t\t\t\"z = w_fogdensity * gl_FragCoord.z / gl_FragCoord.w;\\n\"\n\t\t\t\t\t\t\t\"z = max(0.0,z-w_fogdepthbias);\\n\"\n\t\t\t\t\t\t\t\"#if #include \\\"cvar/r_fog_exp2\\\"\\n\"\n\t\t\t\t\t\t\t\"z *= z;\\n\"\n\t\t\t\t\t\t\t\"#endif\\n\"\n\t\t\t\t\t\t\t\"fac = exp2(-(z * 1.442695));\\n\"\n\t\t\t\t\t\t\"#endif\\n\"\n\t\t\t\t\t\t\"fac = (1.0-w_fogalpha) + (clamp(fac, 0.0, 1.0)*w_fogalpha);\\n\"\n\t\t\t\t\t\t\"return regularcolour * vec4(fac, fac, fac, 1.0);\\n\"\n\t\t\t\t\t\"}\\n\"\n\t\t\t\t\t\"vec4 fog4blend(in vec4 regularcolour)\"\n\t\t\t\t\t\"{\"\t//fog function for regular alpha blends (uses the blend for fading, to avoid fighting the surface behind)\n\t\t\t\t\t\t\"float z, fac;\\n\"\n\t\t\t\t\t\t\"#if #include \\\"cvar/r_fog_linear\\\"\\n\"\n\t\t\t\t\t\t\t\"z = gl_FragCoord.z / gl_FragCoord.w;\\n\"\n\t\t\t\t\t\t\t\"fac = (w_fogdensity - z) / (w_fogdensity - w_fogdepthbias);\\n\"\n\t\t\t\t\t\t\"#else\\n\"\n\t\t\t\t\t\t\t\"z = w_fogdensity * gl_FragCoord.z / gl_FragCoord.w;\\n\"\n\t\t\t\t\t\t\t\"z = max(0.0,z-w_fogdepthbias);\\n\"\n\t\t\t\t\t\t\t\"#if #include \\\"cvar/r_fog_exp2\\\"\\n\"\n\t\t\t\t\t\t\t\"z *= z;\\n\"\n\t\t\t\t\t\t\t\"#endif\\n\"\n\t\t\t\t\t\t\t\"fac = exp2(-(z * 1.442695));\\n\"\n\t\t\t\t\t\t\"#endif\\n\"\n\t\t\t\t\t\t\"fac = (1.0-w_fogalpha) + (clamp(fac, 0.0, 1.0)*w_fogalpha);\\n\"\n\t\t\t\t\t\t\"return regularcolour * vec4(1.0, 1.0, 1.0, fac);\\n\"\n\t\t\t\t\t\"}\\n\"\n\t\t\t\t\"#else\\n\"\n\t\t\t\t\t\"#define w_fogalpha\t\t0.0\\n\"\n\t\t\t\t\t/*don't use macros for this - mesa bugs out*/\n\t\t\t\t\t\"vec3 fog3(in vec3 regularcolour) { return regularcolour; }\\n\"\n\t\t\t\t\t\"vec3 fog3additive(in vec3 regularcolour) { return regularcolour; }\\n\"\n\t\t\t\t\t\"vec4 fog4(in vec4 regularcolour) { return regularcolour; }\\n\"\n\t\t\t\t\t\"vec4 fog4additive(in vec4 regularcolour) { return regularcolour; }\\n\"\n\t\t\t\t\t\"vec4 fog4blend(in vec4 regularcolour) { return regularcolour; }\\n\"\n\t\t\t\t\"#endif\\n\"\n\t\t\t\"#endif\\n\"\n\t\t,\n\t\"sys/offsetmapping.h\",\n\t\t\t\"uniform float cvar_r_glsl_offsetmapping_scale;\\n\"\n\t\t\t\"vec2 offsetmap(sampler2D normtex, vec2 base, vec3 eyevector)\\n\"\n\t\t\t\"{\\n\"\n\t\t\t\"#if !defined(OFFSETMAPPING_SCALE)\\n\"\n\t\t\t\t\"#define OFFSETMAPPING_SCALE 1.0\\n\"\n\t\t\t\"#endif\\n\"\n\t\t\t\"#if !defined(OFFSETMAPPING_BIAS)\\n\"\n\t\t\t\t\"#define OFFSETMAPPING_BIAS 0.0\\n\"\n\t\t\t\"#endif\\n\"\n\t\t\t\"#if defined(RELIEFMAPPING) && !defined(GL_ES)\\n\"\n\t\t\t\t\"float i, f;\\n\"\n\t\t\t\t\"vec3 OffsetVector = vec3(normalize(eyevector.xyz).xy * cvar_r_glsl_offsetmapping_scale * OFFSETMAPPING_SCALE * vec2(-1.0, 1.0), -1.0);\\n\"\n\t\t\t\t\"vec3 RT = vec3(vec2(base.xy\"/* - OffsetVector.xy*OffsetMapping_Bias*/\"), 1.0);\\n\"\n\t\t\t\t\"OffsetVector /= 10.0;\\n\"\n\t\t\t\t\"for(i = 1.0; i < 10.0; ++i)\\n\"\n\t\t\t\t\t\"RT += OffsetVector *  step(texture2D(normtex, RT.xy).a, RT.z);\\n\"\n\t\t\t\t\"for(i = 0.0, f = 1.0; i < 5.0; ++i, f *= 0.5)\\n\"\n\t\t\t\t\t\"RT += OffsetVector * (step(texture2D(normtex, RT.xy).a, RT.z) * f - 0.5 * f);\\n\"\n\t\t\t\t\"return RT.xy;\\n\"\n\t\t\t\"#elif defined(OFFSETMAPPING)\\n\"\n\t\t\t\t\"vec2 OffsetVector = normalize(eyevector).xy * cvar_r_glsl_offsetmapping_scale * OFFSETMAPPING_SCALE * vec2(-1.0, 1.0);\\n\"\n\t\t\t\t\"vec2 tc = base;\\n\"\n\t\t\t\t\"tc += OffsetVector;\\n\"\n\t\t\t\t\"OffsetVector *= 0.333;\\n\"\n\t\t\t\t\"tc -= OffsetVector * texture2D(normtex, tc).a;\\n\"\n\t\t\t\t\"tc -= OffsetVector * texture2D(normtex, tc).a;\\n\"\n\t\t\t\t\"tc -= OffsetVector * texture2D(normtex, tc).a;\\n\"\n\t\t\t\t\"return tc;\\n\"\n\t\t\t\"#else\\n\"\n\t\t\t\t\"return base;\\n\"\n\t\t\t\"#endif\\n\"\n\t\t\t\"}\\n\"\n\t\t,\n\n\t\"sys/pbr.h\",\n\t\t\t//ripped from the gltf webgl demo.\n\t\t\t//https://github.com/KhronosGroup/glTF-WebGL-PBR/blob/master/shaders/pbr-frag.glsl\n\t\t\t//because most of this maths is gibberish, especially the odd magic number.\n\t\t\t\"#ifdef PBR\\n\"\n\t\t\t\"const float PI = 3.141592653589793;\\n\"\n\t\t\t\"vec3 diffuse(vec3 diffuseColor)\\n\" //Basic Lambertian diffuse\n\t\t\t\"{\\n\"\n\t\t\t\t\"return diffuseColor / PI;\\n\"\n\t\t\t\"}\\n\"\n\t\t\t\"vec3 specularReflection(vec3 reflectance0, vec3 reflectance90, float VdotH)\\n\"\n\t\t\t\"{\\n\"\n\t\t\t\t\"return reflectance0 + (reflectance90 - reflectance0) * pow(clamp(1.0 - VdotH, 0.0, 1.0), 5.0);\\n\"\n\t\t\t\"}\\n\"\n\t\t\t\"float geometricOcclusion(float NdotL, float NdotV, float r)\\n\"\n\t\t\t\"{\\n\"\n\t\t\t\t\"float attenuationL = 2.0 * NdotL / (NdotL + sqrt(r * r + (1.0 - r * r) * (NdotL * NdotL)));\\n\"\n\t\t\t\t\"float attenuationV = 2.0 * NdotV / (NdotV + sqrt(r * r + (1.0 - r * r) * (NdotV * NdotV)));\\n\"\n\t\t\t\t\"return attenuationL * attenuationV;\\n\"\n\t\t\t\"}\\n\"\n\t\t\t\"float microfacetDistribution(float alphaRoughness, float NdotH)\\n\" //Trowbridge-Reitz\n\t\t\t\"{\\n\"\n\t\t\t\t\"float roughnessSq = alphaRoughness * alphaRoughness;\\n\"\n\t\t\t\t\"float f = (NdotH * roughnessSq - NdotH) * NdotH + 1.0;\\n\"\n\t\t\t\t\"return roughnessSq / (PI * f * f);\\n\"\n\t\t\t\"}\\n\"\n\t\t\t\"vec3 DoPBR(vec3 n, vec3 v, vec3 l, float perceptualRoughness, vec3 diffuseColor, vec3 specularColor, vec3 scales)\\n\"\n\t\t\t\"{\\n\"\n\t\t\t\t// Compute reflectance.\n\t\t\t\t\"float reflectance = max(max(specularColor.r, specularColor.g), specularColor.b);\\n\"\n\t\t\t\t\"float alphaRoughness = perceptualRoughness * perceptualRoughness;\\n\"\n\n\t\t\t\t// For typical incident reflectance range (between 4% to 100%) set the grazing reflectance to 100% for typical fresnel effect.\n\t\t\t\t// For very low reflectance range on highly diffuse objects (below 4%), incrementally reduce grazing reflecance to 0%.\n\t\t\t\t\"float reflectance90 = clamp(reflectance * 25.0, 0.0, 1.0);\\n\"\n\t\t\t\t\"vec3 specularEnvironmentR0 = specularColor.rgb;\\n\"\n\t\t\t\t\"vec3 specularEnvironmentR90 = vec3(1.0, 1.0, 1.0) * reflectance90;\\n\"\n\n\t\t\t\t\"vec3 h = normalize(l+v);\\n\"                          // Half vector between both l and v\n\t\t\t\t\"vec3 reflection = -normalize(reflect(v, n));\\n\"\n\n\t\t\t\t\"float NdotL = clamp(dot(n, l), 0.001, 1.0);\\n\"\n\t\t\t\t\"float NdotV = clamp(abs(dot(n, v)), 0.001, 1.0);\\n\"\n\t\t\t\t\"float NdotH = clamp(dot(n, h), 0.0, 1.0);\\n\"\n\t\t\t\t\"float LdotH = clamp(dot(l, h), 0.0, 1.0);\\n\"\n\t\t\t\t\"float VdotH = clamp(dot(v, h), 0.0, 1.0);\\n\"\n\n\t\t\t\t// Calculate the shading terms for the microfacet specular shading model\n\t\t\t\t\"vec3 F = specularReflection(specularEnvironmentR0, specularEnvironmentR90, VdotH);\\n\"\n\t\t\t\t\"float G = geometricOcclusion(NdotL, NdotV, alphaRoughness);\\n\"\n\t\t\t\t\"float D = microfacetDistribution(alphaRoughness, NdotH);\\n\"\n\n\t\t\t\t// Calculation of analytical lighting contribution\n\t\t\t\t\"vec3 diffuseContrib = (1.0 - F) * diffuse(diffuseColor) * scales.y;\\n\"\n\t\t\t\t\"vec3 specContrib = F * G * D / (4.0 * NdotL * NdotV) * scales.z;\\n\"\n\t\t\t\t// Obtain final intensity as reflectance (BRDF) scaled by the energy of the light (cosine law)\n\t\t\t\t\"return NdotL * (diffuseContrib + specContrib);\\n\"\n\t\t\t\"}\\n\"\n\t\t\t\"#endif\\n\"\n\t\t,\n\t\"sys/pcf.h\",\n\t\t\t//!!cvardf r_glsl_pcf\n\t\t\t\"#if !defined(PCF) && !defined(FAKESHADOWS)\\n\"\n\t\t\t\t\"#define ShadowmapFilter(smap,proj) 1.0\\n\"\t//s_shadowmap generally. returns a scaler to say how much light should be used for this pixel.\n\t\t\t\"#else\\n\"\n\t\t\t\t\"#ifndef r_glsl_pcf\\n\"\n\t\t\t\t\t\"#define r_glsl_pcf 9\\n\"\n\t\t\t\t\"#endif\\n\"\n\t\t\t\t\"#if r_glsl_pcf < 1\\n\"\n\t\t\t\t\t\"#undef r_glsl_pcf\\n\"\n\t\t\t\t\t\"#define r_glsl_pcf 9\\n\"\n\t\t\t\t\"#endif\\n\"\n\t\t\t\t\"#ifndef DEFS_DEFINED\\n\"\n\t\t\t\t\"uniform vec4 l_shadowmapproj;\\n\" //light projection matrix info\n\t\t\t\t\"uniform vec2 l_shadowmapscale;\\n\"\t//xy are the texture scale, z is 1, w is the scale.\n\t\t\t\t\"#endif\\n\"\n\t\t\t\t\"vec3 ShadowmapCoord(vec4 cubeproj)\\n\"\n\t\t\t\t\"{\\n\"\n\t\t\t\t\"#ifdef SPOT\\n\"\n\t\t\t\t\t//bias it. don't bother figuring out which side or anything, its not needed\n\t\t\t\t\t//l_projmatrix contains the light's projection matrix so no other magic needed\n\t\t\t\t\t\"return ((cubeproj.yxz-vec3(0.0,0.0,0.015))/cubeproj.w + vec3(1.0, 1.0, 1.0)) * vec3(0.5, 0.5, 0.5);\\n\"\n\t\t\t\t\"#elif defined(ORTHO) || defined(FAKESHADOWS)\\n\"\n\t\t\t\t\t//the light's origin is in the center of the 'cube', projecting from one side to the other, so don't bias the z.\n\t\t\t\t\t\"return ((cubeproj.xyz-vec3(0.0,0.0,0.015))/cubeproj.w + vec3(1.0, 1.0, 1.0)) * vec3(0.5, 0.5, 0.5);\\n\"\n\t\t\t\t//\"#elif defined(CUBESHADOW)\\n\"\n\t\t\t\t//\tvec3 shadowcoord = vshadowcoord.xyz / vshadowcoord.w;\n\t\t\t\t//\t#define dosamp(x,y) shadowCube(s_t4, shadowcoord + vec2(x,y)*texscale.xy).r\n\t\t\t\t\"#else\\n\"\n\t\t\t\t\t//figure out which axis to use\n\t\t\t\t\t//texture is arranged thusly:\n\t\t\t\t\t//forward left  up\n\t\t\t\t\t//back    right down\n\t\t\t\t\t\"vec3 dir = abs(cubeproj.xyz);\\n\"\n\t\t\t\t\t//assume z is the major axis (ie: forward from the light)\n\t\t\t\t\t\"vec3 t = cubeproj.xyz;\\n\"\n\t\t\t\t\t\"float ma = dir.z;\\n\"\n\t\t\t\t\t\"vec3 axis = vec3(0.5/3.0, 0.5/2.0, 0.5);\\n\"\n\t\t\t\t\t\"if (dir.x > ma)\\n\"\n\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\"ma = dir.x;\\n\"\n\t\t\t\t\t\t\"t = cubeproj.zyx;\\n\"\n\t\t\t\t\t\t\"axis.x = 0.5;\\n\"\n\t\t\t\t\t\"}\\n\"\n\t\t\t\t\t\"if (dir.y > ma)\\n\"\n\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\"ma = dir.y;\\n\"\n\t\t\t\t\t\t\"t = cubeproj.xzy;\\n\"\n\t\t\t\t\t\t\"axis.x = 2.5/3.0;\\n\"\n\t\t\t\t\t\"}\\n\"\n\t\t\t\t\t//if the axis is negative, flip it.\n\t\t\t\t\t\"if (t.z > 0.0)\\n\"\n\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\"axis.y = 1.5/2.0;\\n\"\n\t\t\t\t\t\t\"t.z = -t.z;\\n\"\n\t\t\t\t\t\"}\\n\"\n\n\t\t\t\t\t//we also need to pass the result through the light's projection matrix too\n\t\t\t\t\t//the 'matrix' we need only contains 5 actual values. and one of them is a -1. So we might as well just use a vec4.\n\t\t\t\t\t//note: the projection matrix also includes scalers to pinch the image inwards to avoid sampling over borders, as well as to cope with non-square source image\n\t\t\t\t\t//the resulting z is prescaled to result in a value between -0.5 and 0.5.\n\t\t\t\t\t//also make sure we're in the right quadrant type thing\n\t\t\t\t\t\"return axis + ((l_shadowmapproj.xyz*t.xyz + vec3(0.0, 0.0, l_shadowmapproj.w)) / -t.z);\\n\"\n\t\t\t\t\"#endif\\n\"\n\t\t\t\t\"}\\n\"\n\n\t\t\t\t\"float ShadowmapFilter(sampler2DShadow smap, vec4 cubeproj)\\n\"\n\t\t\t\t\"{\\n\"\n\t\t\t\t\t\"vec3 shadowcoord = ShadowmapCoord(cubeproj);\\n\"\n#ifdef SHADOWDBG_COLOURNOTDEPTH\n\t\t\t\t\t//just r, unfortunately. oh well.\n\t\t\t\t\t\"return texture2D(smap, shadowcoord.xy + (vec2(0.0,0.0)*l_shadowmapscale.xy)).r;\\n\"\n#else\n\t\t\t\t\t\"#if 0\\n\"//def GL_ARB_texture_gather\n\t\t\t\t\t\t\"vec2 ipart, fpart;\\n\"\n\t\t\t\t\t\t\"#define dosamp(x,y) textureGatherOffset(smap, ipart.xy, vec2(x,y)))\\n\"\n\t\t\t\t\t\t\"vec4 tl = step(shadowcoord.z, dosamp(-1.0, -1.0));\\n\"\n\t\t\t\t\t\t\"vec4 bl = step(shadowcoord.z, dosamp(-1.0, 1.0));\\n\"\n\t\t\t\t\t\t\"vec4 tr = step(shadowcoord.z, dosamp(1.0, -1.0));\\n\"\n\t\t\t\t\t\t\"vec4 br = step(shadowcoord.z, dosamp(1.0, 1.0));\\n\"\n\t\t\t\t\t\t//we now have 4*4 results, woo\n\t\t\t\t\t\t//we can just average them for 1/16th precision, but that's still limited graduations\n\t\t\t\t\t\t//the middle four pixels are 'full strength', but we interpolate the sides to effectively give 3*3\n\t\t\t\t\t\t\"vec4 col =     vec4(tl.ba, tr.ba) + vec4(bl.rg, br.rg) + \" //middle two rows are full strength\n\t\t\t\t\t\t\t\t\"mix(vec4(tl.rg, tr.rg), vec4(bl.ba, br.ba), fpart.y);\\n\" //top+bottom rows\n\t\t\t\t\t\t\"return dot(mix(col.rgb, col.agb, fpart.x), vec3(1.0/9.0));\\n\"\t//blend r+a, gb are mixed because its pretty much free and gives a nicer dot instruction instead of lots of adds.\n\t\t\t\t\t\"#else\\n\"\n\t\t\t\t\t\t\"#ifdef USE_ARB_SHADOW\\n\"\n\t\t\t\t\t\t\t//with arb_shadow, we can benefit from hardware acclerated pcf, for smoother shadows\n\t\t\t\t\t\t\t\"#define dosamp(x,y) float(shadow2D(smap, shadowcoord.xyz + (vec3(x,y,0.0)*l_shadowmapscale.xyx)))\\n\"\n\t\t\t\t\t\t\"#else\\n\"\n\t\t\t\t\t\t\t\"#define dosamp(x,y) float(texture2D(smap, shadowcoord.xy + (vec2(x,y)*l_shadowmapscale.xy)).r >= shadowcoord.z)\\n\"\n\t\t\t\t\t\t\"#endif\\n\"\n\t\t\t\t\t\t\"float s = 0.0;\\n\"\n\t\t\t\t\t\t\"#if r_glsl_pcf >= 1 && r_glsl_pcf < 5\\n\"\n\t\t\t\t\t\t\t\"s += dosamp(0.0, 0.0);\\n\"\n\t\t\t\t\t\t\"#elif r_glsl_pcf >= 5 && r_glsl_pcf < 9\\n\"\n\t\t\t\t\t\t\t\"s += dosamp(-1.0, 0.0);\\n\"\n\t\t\t\t\t\t\t\"s += dosamp(0.0, -1.0);\\n\"\n\t\t\t\t\t\t\t\"s += dosamp(0.0, 0.0);\\n\"\n\t\t\t\t\t\t\t\"s += dosamp(0.0, 1.0);\\n\"\n\t\t\t\t\t\t\t\"s += dosamp(1.0, 0.0);\\n\"\n\t\t\t\t\t\t\t\"s/=5.0;\\n\"\n\t\t\t\t\t\t\"#else\\n\"\n\t\t\t\t\t\t\t\"s += dosamp(-1.0, -1.0);\\n\"\n\t\t\t\t\t\t\t\"s += dosamp(-1.0, 0.0);\\n\"\n\t\t\t\t\t\t\t\"s += dosamp(-1.0, 1.0);\\n\"\n\t\t\t\t\t\t\t\"s += dosamp(0.0, -1.0);\\n\"\n\t\t\t\t\t\t\t\"s += dosamp(0.0, 0.0);\\n\"\n\t\t\t\t\t\t\t\"s += dosamp(0.0, 1.0);\\n\"\n\t\t\t\t\t\t\t\"s += dosamp(1.0, -1.0);\\n\"\n\t\t\t\t\t\t\t\"s += dosamp(1.0, 0.0);\\n\"\n\t\t\t\t\t\t\t\"s += dosamp(1.0, 1.0);\\n\"\n\t\t\t\t\t\t\t\"s/=9.0;\\n\"\n\t\t\t\t\t\t\"#endif\\n\"\n\t\t\t\t\t\t\"#if defined(FAKESHADOWS)||defined(ORTHO)\\n\"\t//no cascaded shadow maps, so just fade shadows to 1 near the various edges of the shadow maps\n\t\t\t\t\t\t\t\"float d = shadowcoord.z;\\n\"\n\t\t\t\t\t\t\t\"d = max(d, shadowcoord.x);\\n\"\n\t\t\t\t\t\t\t\"d = max(d, 1.0-shadowcoord.x);\\n\"\n\t\t\t\t\t\t\t\"d = max(d, shadowcoord.y);\\n\"\n\t\t\t\t\t\t\t\"d = max(d, 1.0-shadowcoord.y);\\n\"\n\t\t\t\t\t\t\t\"if (d > 0.7)\\n\"\n\t\t\t\t\t\t\t\t\"s = mix(s, 1.0, min(1.0,10.0*(d-0.7)));\\n\"\n\t\t\t\t\t\t\"#endif\\n\"\n\t\t\t\t\t\t\"#ifdef FAKESHADOWS\\n\"\n\t\t\t\t\t\t\t\"s = s*0.5+0.5;\\n\" //don't be completely black\n\t\t\t\t\t\t\"#endif\\n\"\n\t\t\t\t\t\t\"return s;\\n\"\n\t\t\t\t\t\"#endif\\n\"\n#endif\n\t\t\t\t\"}\\n\"\n\t\t\t\"#endif\\n\"\n\t\t,\n\tNULL\n};\n\n#define GLSLPARTS (64+16)\nstruct glslparts_s\n{\n\tconst GLchar *str[GLSLPARTS];\n\tGLint len[GLSLPARTS];\n\tconst GLchar *file[GLSLPARTS];\n\tint line[GLSLPARTS];\n\tint strings;\n\n\tconst char *error;\n};\n\nstatic void GLSlang_GenerateInternal(struct glslparts_s *glsl, const char *shadersource)\n{\n\tif (glsl->strings == GLSLPARTS)\n\t{\n\t\tglsl->error = \"Too many parts\";\n\t\treturn;\n\t}\n\tglsl->str[glsl->strings] = shadersource;\n\tglsl->len[glsl->strings] = strlen(shadersource);\n\tglsl->file[glsl->strings] = NULL;\n\tglsl->line[glsl->strings] = 0;\n\tglsl->strings += 1;\n}\n\nstatic void GLSlang_Generate(struct glslparts_s *glsl, const char *shadersource, GLint length, const char *filename, int linenumber)\n{\n\tif (glsl->strings == GLSLPARTS)\n\t{\n\t\tglsl->error = \"Too many parts\";\n\t\treturn;\n\t}\n\tglsl->str[glsl->strings] = shadersource;\n\tglsl->len[glsl->strings] = length;\n\tglsl->file[glsl->strings] = filename;\n\tglsl->line[glsl->strings] = linenumber;\n\tglsl->strings += 1;\n}\n\nstatic qboolean GLSlang_GenerateIncludes(struct glslparts_s *glsl, const char *shadersource, const char *filename, int linenumber)\n{\n\tint i;\n\tchar *incline, *inc;\n\tchar incname[256];\n\twhile((incline=strstr(shadersource, \"#include\")))\n\t{\n\t\t/*emit up to the include*/\n\t\tif (incline - shadersource)\n\t\t{\n\t\t\tchar *e = incline;\n\t\t\twhile(e > shadersource && (e[-1] == ' ' || e[-1] == '\\t'))\n\t\t\t\te--;\n\t\t\tif (e > shadersource && e[-1] == '\\n')\n\t\t\t\tGLSlang_Generate(glsl, shadersource, e-shadersource, filename, linenumber);\n\t\t\telse\n\t\t\t\tGLSlang_Generate(glsl, shadersource, incline-shadersource, filename, linenumber);\n\t\t}\n\n\t\tincline += 8;\n\t\tincline = COM_ParseOut (incline, incname, sizeof(incname));\n\t\tif (!incline)\n\t\t{\n\t\t\tglsl->error = \"missing include name\";\n\t\t\treturn false;\n\t\t}\n\t\twhile (*incline == ' ' || *incline == '\\t')\n\t\t\tincline++;\n\n\t\tif (!strncmp(incname, \"cvar/\", 5))\n\t\t{\n\t\t\tcvar_t *var = Cvar_Get(incname+5, \"0\", 0, \"shader cvars\");\n\t\t\tif (var)\n\t\t\t{\n\t\t\t\tvar->flags |= CVAR_SHADERSYSTEM;\n\t\t\t\tif (!GLSlang_GenerateIncludes(glsl, var->string, NULL, 0))\n\t\t\t\t\treturn false;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t/*dump something if the cvar doesn't exist*/\n\t\t\t\tGLSlang_Generate(glsl, \"0\", strlen(\"0\"), filename, linenumber);\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tfor (i = 0; glsl_hdrs[i]; i += 2)\n\t\t\t{\n\t\t\t\tif (!strcmp(incname, glsl_hdrs[i]))\n\t\t\t\t{\n\t\t\t\t\tif (!GLSlang_GenerateIncludes(glsl, glsl_hdrs[i+1], glsl_hdrs[i], 1))\n\t\t\t\t\t\treturn false;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!glsl_hdrs[i])\n\t\t\t{\n\t\t\t\tsize_t sz;\n\t\t\t\tinc = COM_LoadTempMoreFile(incname, &sz);\n\t\t\t\tif (inc)\n\t\t\t\t{\n\t\t\t\t\tif (!GLSlang_GenerateIncludes(glsl, inc, NULL, 1))\n\t\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tglsl->error = \"include file not found\";\n\t\t\t\t\treturn false;\t//FIXME: add a warning\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t/*move the pointer past the include*/\n\t\twhile (shadersource < incline)\n\t\t{\n\t\t\tif (*shadersource == '\\n')\n\t\t\t\tlinenumber++;\n\t\t\tshadersource++;\n\t\t}\n\t}\n\n\tif (*shadersource)\n\t\tGLSlang_Generate(glsl, shadersource, strlen(shadersource), filename, linenumber);\n\treturn true;\n}\n\n// glslang helper api function definitions\n// type should be GL_FRAGMENT_SHADER_ARB or GL_VERTEX_SHADER_ARB\n//doesn't check to see if it was okay. use FinishShader for that.\nstatic GLuint GLSlang_CreateShader (program_t *prog, const char *name, int ver, const char **precompilerconstants, const char *shadersource, GLenum shadertype, qboolean silent)\n{\n\tGLuint shader;\n\tint i;\n\tstruct glslparts_s glsl;\n\tchar verline[64];\n\n\tglsl.strings = 0;\n\tglsl.error = NULL;\n\n\tif (!shadersource)\n\t\treturn 0;\n\n\tif (ver)\n\t{\n\t\t/*required version not supported, don't even try*/\n\t\tif (ver > gl_config.maxglslversion)\n\t\t\treturn 0;\n#ifdef FTE_TARGET_WEB\n\t\t//emscripten prefixes our shader with a precision specifier, and then the browser bitches at the following (otherwise valid) #version, so don't say anything at all if its ver 100, and the browser won't complain\n\t\tif (ver != 100)\n#endif\n\t\t{\n\t\t\t//known versions:\n\t\t\t//<undefined> == gl\n\t\t\t//100 == gles2\n\t\t\t//110 == gl2.0\n\t\t\t//120 == gl2.1\n\t\t\t//130 == gl3.0\n\t\t\t//140 == gl3.1\n\t\t\t//150 [core|compatibility] == gl3.2\n\t\t\t//300 ES == gles3\n\t\t\t//310 ES == gles3.1\n\t\t\t//320 ES == gles3.2\n\t\t\t//330, 400, 410, 420, 430 [core|compatibility] == gl?.??\n\n\t\t\tif (gl_config_gles)\n\t\t\t{\n\t\t\t\tif (ver <= 110)\t\t//gles2 is rougly gl2 so 100(es)==110ish\n\t\t\t\t\tver = 100;\n\t\t\t\telse if (ver <= 330)\t//gles3 is rougly gl3.3 so 300es==330ish\n\t\t\t\t\tver = 300;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (ver == 100)\n\t\t\t\t\tver = 110;\t//gles2 is roughly equivelent to gl2\n\t\t\t\telse if (ver >= 300 && ver < 330)\n\t\t\t\t\tver = 330;\t//gles3 is roughly equivelent to gl3.3\n\t\t\t}\n\n\n\t\t\tif (gl_config_gles && ver != 100)\n\t\t\t\tQ_snprintfz(verline, sizeof(verline), \"#version %u es\\n\", ver);\n\t\t\telse if (!gl_config_gles && ver >= 150 && !gl_config_nofixedfunc)\n\t\t\t\t//favour compatibility profile, simply because we want ftransform to work properly\n\t\t\t\t//note that versions 130+140 are awkward due to deprecation stuff, both assume compatibility profiles where supported.\n\t\t\t\t//  however, 130 REMOVED ftransform in revision 2 and then re-added it as deprecated in revision 4 (of 10).\n\t\t\t\tQ_snprintfz(verline, sizeof(verline), \"#version %u compatibility\\n\", ver);\n\t\t\telse\n\t\t\t\tQ_snprintfz(verline, sizeof(verline), \"#version %u\\n\", ver);\t//core assumed, where defined\n\t\t\tGLSlang_GenerateInternal(&glsl, verline);\n\t\t}\n\t}\n\n\twhile(*precompilerconstants)\n\t\tGLSlang_GenerateInternal(&glsl, *precompilerconstants++);\n\n\tGLSlang_GenerateInternal(&glsl, \"#define ENGINE_\"DISTRIBUTION\"\\n\");\n\tif (ver < 120)\n\t\tGLSlang_GenerateInternal(&glsl, \"#define PACKEDBONES\\n\");\n\n\tswitch (shadertype)\n\t{\n\tcase GL_FRAGMENT_SHADER_ARB:\n\t\tGLSlang_GenerateInternal(&glsl, \"#define FRAGMENT_SHADER\\n\");\n\t\tif (gl_config.gles)\n\t\t{\n\t\t\tGLSlang_GenerateInternal(&glsl, \n\t\t\t\t\t\"#ifdef GL_FRAGMENT_PRECISION_HIGH\\n\"\n\t\t\t\t\t\"precision highp float;\\n\"\n\t\t\t\t\t\"#else\\n\"\n\t\t\t\t\t\"precision mediump float;\\n\"\n\t\t\t\t\t\"#endif\\n\"\n\t\t\t\t);\n\t\t}\n\t\tif (ver >= 130)\n\t\t{\n\t\t\tGLSlang_GenerateInternal(&glsl, \n\t\t\t\t//gl3+ deprecated some things. these are removed in forwards-compatible / core contexts.\n\t\t\t\t//varying became either in or out, which is important if you have geometry shaders...\n\t\t\t\t\"#define varying in\\n\"\n\t\t\t\t//now only the 'texture' function exists, with overloads for each sampler type.\n\t\t\t\t\"#define texture2D texture\\n\"\n\t\t\t\t\"#define textureCube texture\\n\"\n\t\t\t\t\"#define shadow2D texture\\n\"\n\t\t\t\t//gl_FragColor and gl_FragData got deprecated too, need to make manual outputs\n\t\t\t\t\"#if __VERSION__ >= 300\\n\"\t//gl3.3, gles3 (gles3 requires layout stuff)\n\t\t\t\t\t\"layout(location=0) out vec4 fte_fragdata0;\"\n\t\t\t\t\t\"layout(location=1) out vec4 fte_fragdata1;\"\n\t\t\t\t\t\"layout(location=2) out vec4 fte_fragdata2;\"\n\t\t\t\t\t\"layout(location=3) out vec4 fte_fragdata3;\"\n\t\t\t\t\"\\n#else\\n\"\n\t\t\t\t\t\"out vec4 fte_fragdata0;\"\n\t\t\t\t\t\"out vec4 fte_fragdata1;\"\n\t\t\t\t\t\"out vec4 fte_fragdata2;\"\n\t\t\t\t\t\"out vec4 fte_fragdata3;\"\n\t\t\t\t\"\\n#endif\\n\"\t//gles3 requires this\n\t\t\t\t\"#define gl_FragColor fte_fragdata0\\n\"\n\t\t\t);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tGLSlang_GenerateInternal(&glsl,\n\t\t\t\t\"#define fte_fragdata0 gl_FragData[0]\\n\"\n\t\t\t\t\"#define fte_fragdata1 gl_FragData[1]\\n\"\n\t\t\t\t\"#define fte_fragdata2 gl_FragData[2]\\n\"\n\t\t\t\t\"#define fte_fragdata3 gl_FragData[3]\\n\"\n\t\t\t);\n\t\t}\n\n\t\tif (prog && !prog->explicitsyms)\n\t\t{\t//for compat with our vulkan processor, which injects samplers in order to control layouts.\n\t\t\tconst char *defaultsamplernames[] =\n\t\t\t{\n\t\t\t\t#ifdef SHADOWDBG_COLOURNOTDEPTH\n\t\t\t\t\t\t\t\"#define sampler2DShadow sampler2D\\n\"\n\t\t\t\t#else\n\t\t\t\t\t\t\t\"#ifndef USE_ARB_SHADOW\\n\"\t//fall back on regular samplers if we must\n\t\t\t\t\t\t\t\t\"#define sampler2DShadow sampler2D\\n\"\n\t\t\t\t\t\t\t\"#elif defined(GL_ES)\\n\"\n\t\t\t\t\t\t\t\t\"#if __VERSION__ < 300\\n\"\n\t\t\t\t\t\t\t\t\t\"#extension GL_EXT_shadow_samplers : require\\n\"\n\t\t\t\t\t\t\t\t\"#endif\\n\"\n\t\t\t\t\t\t\t\t\"precision lowp sampler2DShadow;\\n\"\t//gah\n\t\t\t\t\t\t\t\"#endif\\n\"\n\t\t\t\t#endif\n\t\t\t\t\"uniform sampler2DShadow s_shadowmap;\\n\",\n\n\t\t\t\t\"uniform samplerCube s_projectionmap;\\n\",\n\t\t\t\t\"uniform sampler2D s_diffuse;\\n\",\n\t\t\t\t\"uniform sampler2D s_normalmap;\\n\",\n\t\t\t\t\"uniform sampler2D s_specular;\\n\",\n\t\t\t\t\"uniform sampler2D s_upper;\\n\",\n\t\t\t\t\"uniform sampler2D s_lower;\\n\",\n\t\t\t\t\"uniform sampler2D s_fullbright;\\n\",\n\t\t\t\t\"uniform sampler2D s_paletted;\\n\",\n\t\t\t\t\"uniform samplerCube s_reflectcube;\\n\",\n\t\t\t\t\"uniform sampler2D s_reflectmask;\\n\",\n\t\t\t\t\"uniform sampler2D s_displacement;\\n\",\n\t\t\t\t\"uniform sampler2D s_occlusion;\\n\",\n\t\t\t\t\"uniform sampler2D s_transmission;\\n\",\n\t\t\t\t\"uniform sampler2D s_thickness;\\n\",\n\t\t\t\t\"uniform sampler2D s_lightmap;\\n#define s_lightmap0 s_lightmap\\n\",\n\t\t\t\t\"uniform sampler2D s_deluxemap;\\n#define s_deluxemap0 s_deluxemap\\n\",\n\n\t\t\t\t\"uniform sampler2D s_lightmap1;\\n\",\n\t\t\t\t\"uniform sampler2D s_lightmap2;\\n\",\n\t\t\t\t\"uniform sampler2D s_lightmap3;\\n\",\n\t\t\t\t\"uniform sampler2D s_deluxemap1;\\n\",\n\t\t\t\t\"uniform sampler2D s_deluxemap2;\\n\",\n\t\t\t\t\"uniform sampler2D s_deluxemap3;\\n\",\n\t\t\t};\n\t\t\tfor (i = 0; i < countof(defaultsamplernames); i++)\n\t\t\t{\n\t\t\t\tif (prog->defaulttextures & (1u<<i))\n\t\t\t\t\tGLSlang_GenerateInternal(&glsl, defaultsamplernames[i]);\n\t\t\t}\n\t\t}\n\t\tbreak;\n\tcase GL_GEOMETRY_SHADER_ARB:\n\t\tGLSlang_GenerateInternal(&glsl, \"#define GEOMETRY_SHADER\\n\");\n\t\tbreak;\n\tcase GL_TESS_CONTROL_SHADER_ARB:\n\t\tGLSlang_GenerateInternal(&glsl,\n\t\t\t\"#define TESS_CONTROL_SHADER\\n\"\n\t\t\t\"#if __VERSION__ < 400\\n\"\n\t\t\t\t\"#extension GL_ARB_tessellation_shader : enable\\n\"\n\t\t\t\"#endif\\n\"\n\t\t\t//varyings are arrays, so don't bother defining that here.\n\t\t);\n\t\tbreak;\n\tcase GL_TESS_EVALUATION_SHADER_ARB:\n\t\tGLSlang_GenerateInternal(&glsl,\n\t\t\t\"#define TESS_EVALUATION_SHADER\\n\"\n\t\t\t\"#if __VERSION__ < 400\\n\"\n\t\t\t\t\"#extension GL_ARB_tessellation_shader : enable\\n\"\n\t\t\t\"#endif\\n\"\n\t\t\t\"#define varying out\\n\"\n\t\t);\n\t\tbreak;\n\tcase GL_VERTEX_SHADER_ARB:\n\t\tGLSlang_GenerateInternal(&glsl, \"#define VERTEX_SHADER\\n\");\n\t\tif ((ver >= 120 || gl_config_gles) && !gl_config.blacklist_invariant)\t//invariant appeared in glsl 120, or glessl 100. rtlights, stencil shadows, multipass materials, fog volumes, blend-depth-masking all need invariant depth.\n\t\t\tGLSlang_GenerateInternal(&glsl, \"invariant gl_Position;\\n\");\n\t\tif (gl_config.gles)\n\t\t{\n\t\t\tGLSlang_GenerateInternal(&glsl,\n\t\t\t\t\t\"#ifdef GL_FRAGMENT_PRECISION_HIGH\\n\"\n\t\t\t\t\t\"precision highp float;\\n\"\n\t\t\t\t\t\"#else\\n\"\n\t\t\t\t\t\"precision mediump float;\\n\"\n\t\t\t\t\t\"#endif\\n\"\n\t\t\t\t);\n\t\t}\n\t\tif (ver >= 130)\n\t\t{\n\t\t\tGLSlang_GenerateInternal(&glsl,\n\t\t\t\t\t\"#define attribute in\\n\"\n\t\t\t\t\t\"#define varying out\\n\"\n\t\t\t\t);\n\t\t}\n\n\t\tif (!prog || !prog->explicitsyms)\n\t\t{\n\t\t\tif (gl_config_nofixedfunc)\n\t\t\t{\n\t\t\t\tGLSlang_GenerateInternal(&glsl,\n\t\t\t\t\t\t\"attribute vec3 v_position1;\\n\"\n\t\t\t\t\t\t\"#ifdef FRAMEBLEND\\n\"\n\t\t\t\t\t\t\t\"attribute vec3 v_position2;\\n\"\n\t\t\t\t\t\t\t\"uniform vec2 e_vblend;\\n\"\n\t\t\t\t\t\t\t\"#define v_position ((v_position1*e_vblend.x)+(v_position2*e_vblend.y))\\n\"\n\t\t\t\t\t\t\"#else\\n\"\n\t\t\t\t\t\t\t\"#define v_position v_position1\\n\"\n\t\t\t\t\t\t\"#endif\\n\"\n\t\t\t\t\t\t\"uniform mat4 m_modelviewprojection;\\n\"\n#if 1//def FTE_TARGET_WEB\n\t\t\t\t\t\t//IE is buggy\n\t\t\t\t\t\t\"vec4 ftetransform() { return m_modelviewprojection * vec4(v_position, 1.0); }\\n\"\n#else\n\t\t\t\t\t\t\"#define ftetransform() (m_modelviewprojection * vec4(v_position, 1.0))\\n\"\n#endif\n\t\t\t\t\t);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tGLSlang_GenerateInternal(&glsl,\n\t\t\t\t\t\t\"#ifdef FRAMEBLEND\\n\"\n\t\t\t\t\t\t\t\"attribute vec3 v_position2;\\n\"\n\t\t\t\t\t\t\t\"uniform vec2 e_vblend;\\n\"\n\t\t\t\t\t\t\t\"#define v_position (gl_Vertex.xyz*e_vblend.x+v_position2*e_vblend.y)\\n\"\n\t\t\t\t\t\t\t\"uniform mat4 m_modelviewprojection;\\n\"\n\t\t\t\t\t\t\t\"#define ftetransform() (m_modelviewprojection * vec4(v_position, 1.0))\\n\"\n\t\t\t\t\t\t\"#else\\n\"\n\t\t\t\t\t\t\t\"#define v_position gl_Vertex.xyz\\n\"\n\t\t\t\t\t\t\t\"uniform mat4 m_modelviewprojection;\\n\"\n\t\t\t\t\t\t\t\"#define ftetransform ftransform\\n\"\n\t\t\t\t\t\t\"#endif\\n\"\n\t\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\tbreak;\n\tdefault:\n\t\tGLSlang_GenerateInternal(&glsl, \"#define UNKNOWN_SHADER\\n\");\n\t\tbreak;\n\t}\n\n\tGLSlang_GenerateIncludes(&glsl, shadersource, name, 1);\n\n\tshader = qglCreateShaderObjectARB(shadertype);\n\n\tif (developer.ival)\n\t{\n\t\tGLchar *combined;\n\t\tint totallen = 1;\n\t\tfor (i = 0; i < glsl.strings; i++)\n\t\t\ttotallen += glsl.len[i] + 64 + (glsl.file[i]?strlen(glsl.file[i]):0);\n\t\tcombined = malloc(totallen);\n\t\ttotallen = 0;\n\t\tcombined[totallen] = 0;\n\t\tfor (i = 0; i < glsl.strings; i++)\n\t\t{\n\t\t\tif (ver && !i)\n\t\t\t\t; //#version MUST be the first line, don't prefix it with a #line, it'll just break things.\n\t\t\telse if (!totallen || combined[totallen-1] == '\\n')\n\t\t\t{\t//last line was a newline, hurrah. safe to insert without breaking anything\n\t\t\t\tif (glsl.file[i])\n\t\t\t\t\tQ_snprintfz(combined+totallen, 64+strlen(glsl.file[i]), \"#line %i %i //%s\\n\", glsl.line[i], i, glsl.file[i]);\n\t\t\t\telse\n\t\t\t\t\tQ_snprintfz(combined+totallen, 64, \"#line %i %i\\n\", glsl.line[i], i);\n\t\t\t\ttotallen += strlen(combined+totallen);\n\t\t\t}\n\t\t\telse if (glsl.len[i] && *glsl.str[i] == '\\n')\n\t\t\t{\t//last line didn't end with a newline, but there is one after. that's okay too, but we need to play it safe.\n\t\t\t\tif (glsl.file[i])\n\t\t\t\t\tQ_snprintfz(combined+totallen, 64+strlen(glsl.file[i]), \"\\n#line %i %i //%s\\n\", glsl.line[i], i, glsl.file[i]);\n\t\t\t\telse\n\t\t\t\t\tQ_snprintfz(combined+totallen, 64, \"\\n#line %i %i\\n\", glsl.line[i], i);\n\t\t\t\ttotallen += strlen(combined+totallen);\n\t\t\t}\n\t\t\t//now shove stuff there.\n\t\t\tmemcpy(combined+totallen, glsl.str[i], glsl.len[i]);\n\t\t\ttotallen += glsl.len[i];\n\t\t\tcombined[totallen] = 0;\n\t\t}\n\t\tqglShaderSourceARB(shader, 1, (const GLchar*const*)&combined, NULL);\n\t\tfree(combined);\n\t}\n\telse if (gl_workaround_ati_shadersource.ival)\n\t{\n\t\t/*ATI Driver Bug: ATI drivers ignore the 'length' array.\n\t\tthis code does what the drivers fail to do.\n\t\tthis patch makes the submission more mainstream\n\t\tif ati can feck it up so much on a system with no real system memory issues, I wouldn't be surprised if embedded systems also mess it up.\n\t\t*/\n\t\tGLcharARB *combined;\n\t\tint totallen = 1;\n\t\tfor (i = 0; i < glsl.strings; i++)\n\t\t\ttotallen += glsl.len[i];\n\t\tcombined = malloc(totallen);\n\t\ttotallen = 0;\n\t\tcombined[totallen] = 0;\n\t\tfor (i = 0; i < glsl.strings; i++)\n\t\t{\n\t\t\tmemcpy(combined + totallen, glsl.str[i], glsl.len[i]);\n\t\t\ttotallen += glsl.len[i];\n\t\t\tcombined[totallen] = 0;\n\t\t}\n\t\tqglShaderSourceARB(shader, 1, (const GLcharARB*const*)&combined, NULL);\n\t\tfree(combined);\n\t}\n\telse\n\t\tqglShaderSourceARB(shader, glsl.strings, glsl.str, glsl.len);\n\tqglCompileShaderARB(shader);\n\n\treturn shader;\n}\n\n//called after CreateShader. Checks for success.\n//Splitting creation allows for both vertex+fragment shaders to be processed simultaneously if the driver threads glCompileShaderARB.\nstatic GLuint GLSlang_FinishShader(GLuint shader, const char *name, GLenum shadertype, qboolean *silent)\n{\n\tGLint\tcompiled;\n\tint loglen;\n\n\tif (!shader)\t//if there's no shader, then there was nothing to finish...\n\t\treturn shader;\n\n\tqglGetShaderParameteriv_(shader, GL_OBJECT_COMPILE_STATUS_ARB, &compiled);\n\tif(!compiled)\n\t{\n\t\tchar\t*typedesc;\n\t\tchar\tstr[65536];\n\n\t\t*str = 0;\n\t\tqglGetShaderInfoLog_(shader, sizeof(str), NULL, str);\n\t\tif (!*silent)\n\t\t{\n\t\t\t*silent = true;\n\t\t\tswitch (shadertype)\n\t\t\t{\n\t\t\tcase GL_FRAGMENT_SHADER_ARB:\n\t\t\t\ttypedesc = \"Fragment\";\n\t\t\t\tbreak;\n\t\t\tcase GL_VERTEX_SHADER_ARB:\n\t\t\t\ttypedesc = \"Vertex\";\n\t\t\t\tbreak;\n\t\t\tcase GL_TESS_CONTROL_SHADER_ARB:\n\t\t\t\ttypedesc = \"Tesselation Control\";\n\t\t\t\tbreak;\n\t\t\tcase GL_TESS_EVALUATION_SHADER_ARB:\n\t\t\t\ttypedesc = \"Tesselation Evaluation\";\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\ttypedesc = \"???\";\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tCon_Printf(\"%s shader (%s) compilation error:\\n\"CON_ERROR\"%s\"CON_DEFAULT\"----------\\n\", typedesc, name, str);\n\n\t\t\t//if there's no fixed function then failure to compile the default2d shader should be considered fatal. this should help avoid black screens on android.\n\t\t\tif (gl_config_nofixedfunc && !strcmp(name, \"default2d\"))\n\t\t\t\tSys_Error(\"%s shader (%s) compilation error:\\n----------\\n%s----------\\n\", typedesc, name, str);\n\n\t\t\tif (developer.ival>1)\n\t\t\t{\t//could use echo console-link I guess (with embedded line numbers). shaders can get quite big though.\n\t\t\t\tunsigned int rawline, line, filenum = 0;\n\t\t\t\tchar *eol, *start, *e;\n\t\t\t\tconst char *filename = name;\n\t\t\t\tqglGetShaderSource(shader, sizeof(str), NULL, str);\n\t\t\t\tCon_Printf(\"Shader \\\"%s\\\" source:\\n\", name);\n\t\t\t\tfor(start = str, line = 1, rawline = 1; ;)\n\t\t\t\t{\n\t\t\t\t\teol = strchr(start, '\\n');\n\t\t\t\t\tif (eol)\n\t\t\t\t\t\t*eol=0;\n\t\t\t\t\tif (filename)\n\t\t\t\t\t\tCon_Printf(\"%s:%u:%u: %s\\n\", filename, line, rawline, start);\n\t\t\t\t\telse\n\t\t\t\t\t\tCon_Printf(\"%u:%u:%u: %s\\n\", filenum, line, rawline, start);\n\t\t\t\t\tif (!strncmp(start, \"#line \", 6))\n\t\t\t\t\t{\n\t\t\t\t\t\tline = strtoul(start+6, &e, 0);\n\t\t\t\t\t\twhile(*e == ' ')\n\t\t\t\t\t\t\te++;\n\t\t\t\t\t\tif (*e)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tfilenum = strtoul(e, &e, 0);\n\t\t\t\t\t\t\twhile(*e == ' ')\n\t\t\t\t\t\t\t\te++;\n\t\t\t\t\t\t\tfilename = NULL;\n\t\t\t\t\t\t\tif (e[0]=='/'&&e[1]=='/')\n\t\t\t\t\t\t\t\tfilename = e+2;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\tline++;\n\t\t\t\t\tif (!eol)\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tstart = eol+1;\n\t\t\t\t\trawline++;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tqglDeleteShaderObject_(shader);\n\t\treturn 0;\n\t}\n\n\tif (developer.ival)\n\t{\n\t\tqglGetShaderParameteriv_(shader, GL_OBJECT_INFO_LOG_LENGTH_ARB, &loglen);\n\t\tif (loglen)\n\t\t{\n\t\t\tchar\tstr[8192];\n\n\t\t\tqglGetShaderInfoLog_(shader, sizeof(str), NULL, str);\n\t\t\tif (strstr(str, \"WARNING\"))\n\t\t\t{\n\t\t\t\tCon_Printf(\"Shader \\\"%s\\\" log:\\n%s\", name, str);\n\t\t\t\tqglGetShaderSource(shader, sizeof(str), NULL, str);\n\t\t\t\tCon_Printf(\"Shader \\\"%s\\\" source:\\n%s\", name, str);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn shader;\n}\n\nGLuint GLSlang_CreateProgramObject (program_t *prog, const char *name, GLuint vert, GLuint cont, GLuint eval, GLuint geom, GLuint frag)\n{\n\tint i;\n\tGLuint\tprogram;\n\n\tprogram = qglCreateProgramObjectARB();\n\tif (vert) qglAttachObjectARB(program, vert);\n\tif (geom) qglAttachObjectARB(program, geom);\n\tif (cont) qglAttachObjectARB(program, cont);\n\tif (eval) qglAttachObjectARB(program, eval);\n\tif (frag) qglAttachObjectARB(program, frag);\n\n\tfor (i = 0; shader_attr_names[i].name; i++)\n\t{\n\t\tif (gl_config.maxattribs > shader_attr_names[i].ptype)\n\t\t{\n\t\t\tif (prog && prog->explicitsyms)\n\t\t\t\tqglBindAttribLocationARB(program, shader_attr_names[i].ptype, va(\"fte_%s\", shader_attr_names[i].name));\n\t\t\telse\n\t\t\t\tqglBindAttribLocationARB(program, shader_attr_names[i].ptype, shader_attr_names[i].name);\n\t\t}\n\t}\n\n\tqglLinkProgramARB(program);\n\treturn program;\n}\n\nqboolean GLSlang_ValidateProgram(union programhandle_u *h, const char *name, qboolean silent, vfsfile_t *blobfile)\n{\n\tchar\t\tstr[2048];\n\tGLint linked;\n\n\tif (!h->glsl.handle)\n\t\treturn false;\n\tqglGetProgramParameteriv_(h->glsl.handle, GL_OBJECT_LINK_STATUS_ARB, &linked);\n\n\tif(!linked)\n\t{\n\t\tif (!silent)\n\t\t{\n\t\t\tqglGetProgramInfoLog_(h->glsl.handle, sizeof(str), NULL, str);\n\t\t\tCon_Printf(\"Program link error on glsl program %s:\\n%s\\n\", name, str);\n\t\t}\n\n\t\tqglDeleteProgramObject_(h->glsl.handle);\n\t\th->glsl.handle = 0;\n\n\t\treturn false;\n\t}\n\n\tif (h->glsl.handle && blobfile && qglGetProgramBinary)\n\t{\n\t\tGLuint ui;\n\t\tGLenum e;\n\t\tunsigned int len, fmt;\n\t\tvoid *blobdata;\n\n\t\tqglGetProgramParameteriv_(h->glsl.handle, GL_PROGRAM_BINARY_LENGTH, &ui);\n\t\tlen = ui;\n\n\t\tblobdata = BZ_Malloc(len);\n\t\tqglGetProgramBinary(h->glsl.handle, len, NULL, &e, blobdata);\n\t\tfmt = e;\n\n\t\tVFS_WRITE(blobfile, &fmt, sizeof(fmt));\n\t\tVFS_WRITE(blobfile, &len, sizeof(len));\n\t\tVFS_WRITE(blobfile, blobdata, len);\n\t\tVFS_WRITE(blobfile, &h->glsl.usetesselation, sizeof(h->glsl.usetesselation));\n\t\tBZ_Free(blobdata);\n\t}\n\n\treturn true;\n}\n\nunion programhandle_u GLSlang_CreateProgram(program_t *prog, const char *name, int ver, const char **precompilerconstants, const char *vert, const char *cont, const char *eval, const char *geom, const char *frag, qboolean silent, vfsfile_t *blobfile)\n{\n\tunion programhandle_u ret;\n\tGLuint vs;\n\tGLuint gs;\n\tGLuint fs;\n\tGLuint cs;\n\tGLuint es;\n\tconst char *nullconstants = NULL;\n\n\tmemset(&ret, 0, sizeof(ret));\n\n\tif (!gl_config.arb_shader_objects)\n\t\treturn ret;\n\tif ((cont || eval) && !gl_config.arb_tessellation_shader)\n\t{\n\t\tCon_Printf(\"GLSlang_CreateProgram: %s requires tesselation support, but your gl drivers do not appear to support this (gl4.0 feature)\\n\", name);\n\t\treturn ret;\n\t}\n\tif (geom && !gl_config.geometryshaders)\n\t{\n\t\tCon_Printf(\"GLSlang_CreateProgram: %s requires geometry shader support, but your gl drivers do not appear to support this (gl3.2 feature)\\n\", name);\n\t\treturn ret;\n\t}\n\n\tif (!precompilerconstants)\n\t\tprecompilerconstants = &nullconstants;\n\n\tfs = GLSlang_CreateShader(prog, name, ver, precompilerconstants, frag, GL_FRAGMENT_SHADER_ARB, silent);\n\tgs = GLSlang_CreateShader(prog, name, ver, precompilerconstants, geom, GL_GEOMETRY_SHADER_ARB, silent);\n\tvs = GLSlang_CreateShader(prog, name, ver, precompilerconstants, vert, GL_VERTEX_SHADER_ARB, silent);\n\tcs = GLSlang_CreateShader(prog, name, ver, precompilerconstants, cont, GL_TESS_CONTROL_SHADER_ARB, silent);\n\tes = GLSlang_CreateShader(prog, name, ver, precompilerconstants, eval, GL_TESS_EVALUATION_SHADER_ARB, silent);\n\n\tfs = GLSlang_FinishShader(fs, name, GL_FRAGMENT_SHADER_ARB, &silent);\n\tgs = GLSlang_FinishShader(gs, name, GL_GEOMETRY_SHADER_ARB, &silent);\n\tvs = GLSlang_FinishShader(vs, name, GL_VERTEX_SHADER_ARB, &silent);\n\tcs = GLSlang_FinishShader(cs, name, GL_TESS_CONTROL_SHADER_ARB, &silent);\n\tes = GLSlang_FinishShader(es, name, GL_TESS_EVALUATION_SHADER_ARB, &silent);\n\n\tif (!vs || !fs)\n\t\tret.glsl.handle = 0;\n\telse\n\t\tret.glsl.handle = GLSlang_CreateProgramObject(prog, name, vs, cs, es, gs, fs);\n\t//delete ignores 0s.\n\tif (vs) qglDeleteShaderObject_(vs);\n\tif (gs) qglDeleteShaderObject_(gs);\n\tif (fs) qglDeleteShaderObject_(fs);\n\tif (cs) qglDeleteShaderObject_(cs);\n\tif (es) qglDeleteShaderObject_(es);\n\n\tcheckglerror();\n\n\tret.glsl.usetesselation = (cont || eval);\n\tif (ret.glsl.handle && blobfile && qglGetProgramBinary)\n\t{\n\t\tGLuint ui;\n\t\tGLenum e;\n\t\tunsigned int len, fmt;\n\t\tvoid *blobdata;\n\n\t\tqglGetProgramParameteriv_(ret.glsl.handle, GL_PROGRAM_BINARY_LENGTH, &ui);\n\t\tlen = ui;\n\n\t\tblobdata = BZ_Malloc(len);\n\t\tqglGetProgramBinary(ret.glsl.handle, len, NULL, &e, blobdata);\n\t\tfmt = e;\n\n\t\tVFS_WRITE(blobfile, &fmt, sizeof(fmt));\n\t\tVFS_WRITE(blobfile, &len, sizeof(len));\n\t\tVFS_WRITE(blobfile, blobdata, len);\n\t\tVFS_WRITE(blobfile, &ret.glsl.usetesselation, sizeof(ret.glsl.usetesselation));\n\t\tBZ_Free(blobdata);\n\t}\n\n\treturn ret;\n}\n\nqboolean GLSlang_ValidateProgramPermu(program_t *prog, struct programpermu_s *permu, qboolean noerrors, vfsfile_t *blobfile)\n{\n\tif (!permu)\n\t\treturn false;\n\treturn GLSlang_ValidateProgram(&permu->h, prog->name, noerrors, blobfile);\n}\nqboolean GLSlang_CreateProgramPermu(program_t *prog, struct programpermu_s *permu, int ver, const char **precompilerconstants, const char *vert, const char *tcs, const char *tes, const char *geom, const char *frag, qboolean noerrors, vfsfile_t *blobfile)\n{\n\tif (!ver)\n\t{\n\t\tif (gl_config.gles)\n\t\t\tver = 100;\n\t\telse\n\t\t\tver = 110;\n\t}\n\tif ((permu->permutation & PERMUTATION_SKELETAL) && gl_config.maxattribs < 10)\n\t\treturn false;\t//can happen in gles2\n#if MAXRLIGHTMAPS > 1\n\tif ((permu->permutation & PERMUTATION_LIGHTSTYLES) && gl_config.maxattribs < 16)\n\t\treturn false;\t//can happen in gles2\n#endif\n\n\tpermu->h = GLSlang_CreateProgram(prog, prog->name, ver, precompilerconstants, vert, tcs, tes, geom, frag, noerrors, blobfile);\n\tif (permu->h.glsl.handle)\n\t\treturn true;\n\treturn false;\n}\n\nGLint GLSlang_GetUniformLocation (GLuint prog, char *name)\n{\n\tint i = qglGetUniformLocationARB(prog, name);\n\tif (i == -1)\n\t{\n\t\tCon_Printf(\"Failed to get location of uniform '%s'\\n\", name);\n\t}\n\treturn i;\n}\n\nstatic qboolean GLSlang_LoadBlob(program_t *prog, unsigned int permu, vfsfile_t *blobfile)\n{\n\treturn false;\n/*\n\tunsigned int fmt;\n\tunsigned int length;\n\tvoid *binary;\n\tGLint success;\n\tif (!qglProgramBinary)\n\t\treturn false;\n\tVFS_READ(blobfile, &fmt, sizeof(fmt));\n\tVFS_READ(blobfile, &length, sizeof(length));\n\tbinary = BZ_Malloc(length);\n\tVFS_READ(blobfile, binary, length);\n\tVFS_READ(blobfile, &prog->permu[permu].h.glsl.usetesselation, sizeof(prog->permu[permu].h.glsl.usetesselation));\n\n\tprog->permu[permu].h.glsl.handle = qglCreateProgramObjectARB();\n\tqglProgramBinary(prog->permu[permu].h.glsl.handle, fmt, binary, length);\n\tBZ_Free(binary);\n\tqglGetProgramParameteriv_(prog->permu[permu].h.glsl.handle, GL_OBJECT_LINK_STATUS_ARB, &success);\n\n\tif (!success)\n\t{\n\t\tqglDeleteProgramObject_(prog->permu[permu].h.glsl.handle);\n\t\tmemset(&prog->permu[permu].h, 0, sizeof(prog->permu[permu].h));\n\t}\n\treturn !!success;\n*/\n}\n\nstatic void GLSlang_DeleteProg(program_t *prog)\n{\n\tunsigned int permu;\n\tstruct programpermu_s *pp;\n\tfor (permu = countof(prog->permu); permu-- > 0; )\n\t{\n\t\tpp = prog->permu[permu];\n\t\tif (pp)\n\t\t{\n\t\t\tprog->permu[permu] = NULL;\n\t\t\tif (pp == prog->permu[0] && permu)\n\t\t\t\tcontinue;\t//entry 0 (only) can get copied to avoid constant recompile failures (0 is always precompiled)\n\n\t\t\tqglDeleteProgramObject_(pp->h.glsl.handle);\n\t\t\tpp->h.glsl.handle = 0;\n\n\t\t\tBZ_Free(pp->parm);\n\t\t\tpp->parm = NULL;\n\t\t\tpp->numparms = 0;\n\n\t\t\tZ_Free(pp);\n\t\t}\n\t}\n}\n\nstatic void GLSlang_ProgAutoFields(program_t *prog, struct programpermu_s *pp, char **cvarnames, int *cvartypes)\n{\n\tunsigned int i,j;\n\tint uniformloc;\n\tchar tmpname[128];\n\tint maxparms = 0;\n\n\t//figure out visible attributes\n\tGLSlang_UseProgram(pp->h.glsl.handle);\n\tfor (i = 0; shader_attr_names[i].name; i++)\n\t{\n\t\tif (prog && prog->explicitsyms)\n\t\t\tuniformloc = qglGetAttribLocationARB(pp->h.glsl.handle, va(\"fte_%s\", shader_attr_names[i].name));\n\t\telse\n\t\t\tuniformloc = qglGetAttribLocationARB(pp->h.glsl.handle, shader_attr_names[i].name);\n\t\tif (uniformloc != -1)\n\t\t{\n\t\t\tif (shader_attr_names[i].ptype != uniformloc)\n\t\t\t\tCon_Printf(\"Bad attribute \\\"%s\\\" in glslprogram \\\"%s\\\" (%i should be %i)\\n\", shader_attr_names[i].name, prog?prog->name:\"<NULL>\", uniformloc, shader_attr_names[i].ptype);\n\t\t\telse\n\t\t\t\tpp->attrmask |= 1u<<uniformloc;\n\t\t}\n\t}\n\n\tpp->numparms = 0;\n\tpp->parm = NULL;\n\n\tpp->factorsuniform = qglGetUniformLocationARB(pp->h.glsl.handle, \"factors\");\n\n\tfor (i = 0; shader_unif_names[i].name; i++)\n\t{\n\t\tuniformloc = qglGetUniformLocationARB(pp->h.glsl.handle, shader_unif_names[i].name);\n\t\tif (uniformloc >= 0)\n\t\t{\n\t\t\tif (pp->numparms >= maxparms)\n\t\t\t{\n\t\t\t\tmaxparms = pp->numparms?pp->numparms * 2:8;\n\t\t\t\tpp->parm = BZ_Realloc(pp->parm, sizeof(*pp->parm) * maxparms);\n\t\t\t}\n\t\t\tpp->parm[pp->numparms].type = shader_unif_names[i].ptype;\n\t\t\tpp->parm[pp->numparms].handle = uniformloc;\n\t\t\tpp->parm[pp->numparms].pval = NULL;\n\t\t\tpp->numparms++;\n\t\t}\n\t}\n\n\t/*set cvar uniforms*/\n\t/*FIXME: enumerate cvars automatically instead*/\n\tfor (i = 0; cvarnames[i]; i++)\n\t{\n\t\tif (cvartypes[i] <= SP_CONST4F)\n\t\t{\n\t\t\tchar *t;\n\t\t\tQ_snprintfz(tmpname, sizeof(tmpname), \"%s\", cvarnames[i]);\n\t\t\tt = strchr(tmpname, '=');\n\t\t\tif (t) *t++=0;\n\t\t\tuniformloc = qglGetUniformLocationARB(pp->h.glsl.handle, tmpname);\n\t\t\tif (uniformloc >= 0)\n\t\t\t{\n\t\t\t\tif (pp->numparms >= maxparms)\n\t\t\t\t{\n\t\t\t\t\tmaxparms = pp->numparms?pp->numparms * 2:8;\n\t\t\t\t\tpp->parm = BZ_Realloc(pp->parm, sizeof(*pp->parm) * maxparms);\n\t\t\t\t}\n\t\t\t\tpp->parm[pp->numparms].type = cvartypes[i];\n\t\t\t\tfor(j = 0; j < 4; j++)\n\t\t\t\t{\n\t\t\t\t\tt = COM_Parse(t);\n\t\t\t\t\tif (cvartypes[i] >= SP_CONST1F && cvartypes[i] <= SP_CONST4F)\n\t\t\t\t\t\tpp->parm[pp->numparms].fval[j] = atoi(com_token);\n\t\t\t\t\telse\n\t\t\t\t\t\tpp->parm[pp->numparms].ival[j] = atoi(com_token);\n\t\t\t\t}\n\t\t\t\tpp->parm[pp->numparms].handle = uniformloc;\n\t\t\t\tpp->numparms++;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tcvar_t *ref = Cvar_FindVar(cvarnames[i]);\n\t\t\tif (!ref)\n\t\t\t\tcontinue;\t//shouldn't happen...\n\n\t\t\tQ_snprintfz(tmpname, sizeof(tmpname), \"cvar_%s\", cvarnames[i]);\n\t\t\tuniformloc = qglGetUniformLocationARB(pp->h.glsl.handle, tmpname);\n\t\t\tif (uniformloc >= 0)\n\t\t\t{\n\t\t\t\tif (pp->numparms >= maxparms)\n\t\t\t\t{\n\t\t\t\t\tmaxparms = pp->numparms?pp->numparms * 2:8;\n\t\t\t\t\tpp->parm = BZ_Realloc(pp->parm, sizeof(*pp->parm) * maxparms);\n\t\t\t\t}\n\t\t\t\tpp->parm[pp->numparms].type = cvartypes[i];\n\t\t\t\tpp->parm[pp->numparms].pval = ref;\n\t\t\t\tpp->parm[pp->numparms].handle = uniformloc;\n\t\t\t\tpp->numparms++;\n\t\t\t}\n\t\t}\n\t}\n\n\t//now scan/set texture uniforms\n\tif (!(pp->attrmask & (1u<<VATTR_VERTEX1)))\t//a shader kinda has to use one of these...\n\t\tpp->attrmask |= (1u<<VATTR_LEG_VERTEX);\n\tfor (i = 0; i < prog->numsamplers; i++)\n\t{\n\t\tQ_snprintfz(tmpname, sizeof(tmpname), \"s_t%i\", i);\n\t\tuniformloc = qglGetUniformLocationARB(pp->h.glsl.handle, tmpname);\n\t\tif (uniformloc != -1)\n\t\t{\n\t\t\tqglUniform1iARB(uniformloc, i);\n//\t\t\tif (prog->numsamplers < i+1)\n//\t\t\t\tprog->numsamplers = i+1;\n\t\t}\n\t}\n\tif (developer.ival)\n\t\tfor (i = 0; sh_defaultsamplers[i].name; i++)\n\t\t{\n\t\t\t//figure out which ones are needed.\n\t\t\tif (prog->defaulttextures & sh_defaultsamplers[i].defaulttexbits)\n\t\t\t\tcontinue;\t//don't spam\n\t\t\tuniformloc = qglGetUniformLocationARB(pp->h.glsl.handle, sh_defaultsamplers[i].name);\n\t\t\tif (uniformloc != -1)\n\t\t\t\tCon_Printf(\"glsl \\\"%s\\\" needs %s but doesn't declare so\\n\", prog->name, sh_defaultsamplers[i].name);\n\t\t}\n\n\tif (prog->defaulttextures)\n\t{\n\t\tunsigned int sampnum = prog->numsamplers;\n\t\t/*set default texture uniforms now that we know the right sampler ids*/\n\n\t\tfor (i = 0; sh_defaultsamplers[i].name; i++)\n\t\t{\n\t\t\tif (prog->defaulttextures & sh_defaultsamplers[i].defaulttexbits)\n\t\t\t{\n\t\t\t\tuniformloc = qglGetUniformLocationARB(pp->h.glsl.handle, sh_defaultsamplers[i].name);\n\t\t\t\tif (uniformloc != -1)\n\t\t\t\t\tqglUniform1iARB(uniformloc, sampnum);\n\t\t\t\tsampnum++;\n\t\t\t}\n\t\t}\n\t}\n}\n\nvoid GL_ForgetPointers(void)\n{\t//its at times like this that I wish I had put all of these into a struct.\n\t//but GL_STATIC and webgl makes that sub-optimal. *sigh*\n#ifndef GL_STATIC\n\tqglBindTexture\t\t= NULL;\n\tqglBlendFunc\t\t= NULL;\n\tqglClear\t\t\t= NULL;\n\tqglClearColor\t\t= NULL;\n\tqglClearStencil\t\t= NULL;\n\tqglColorMask\t\t= NULL;\n\tqglCopyTexImage2D\t= NULL;\n\tqglCopyTexSubImage2D= NULL;\n\tqglCullFace\t\t\t= NULL;\n\tqglDepthFunc\t\t= NULL;\n\tqglDepthMask\t\t= NULL;\n//\tqglDepthRangef\t\t= NULL;\n\tqglDisable\t\t\t= NULL;\n\tqglEnable\t\t\t= NULL;\n\tqglFinish\t\t\t= NULL;\n\tqglFlush\t\t\t= NULL;\n\tqglGenTextures\t\t= NULL;\n\tqglGetFloatv\t\t= NULL;\n\tqglGetIntegerv\t\t= NULL;\n\tqglGetString\t\t= NULL;\n\tqglHint\t\t\t\t= NULL;\n\tqglIsEnabled\t\t= NULL;\n\tqglReadPixels\t\t= NULL;\n\tqglTexImage2D\t\t= NULL;\n\tqglTexSubImage2D\t= NULL;\n\tqglTexParameteri\t= NULL;\n\tqglTexParameterf\t= NULL;\n\tqglTexParameteriv\t= NULL;\n\tqglTexParameterfv\t= NULL;\n\tqglViewport\t\t\t= NULL;\n\tqglGetBooleanv\t\t= NULL;\n\tqglGetError\t\t\t= NULL;\n\tqglDeleteTextures\t= NULL;\n\tqglDrawElements\t\t= NULL;\n\tqglDrawArrays\t\t= NULL;\n\tqglStencilOp\t\t= NULL;\n\tqglStencilFunc\t\t= NULL;\n\tqglScissor\t\t\t= NULL;\n\tqglPolygonOffset\t= NULL;\n#endif\n#ifndef FTE_TARGET_WEB\n\tqglAlphaFunc\t\t= NULL;\n\tqglBegin\t\t\t= NULL;\n\tqglClearDepth\t\t= NULL;\n\tqglClipPlane \t\t= NULL;\n//\tqglColor3f\t\t\t= NULL;\n//\tqglColor3ub\t\t\t= NULL;\n\tqglColor4f\t\t\t= NULL;\n\tqglColor4fv\t\t\t= NULL;\n//\tqglColor4ub\t\t\t= NULL;\n//\tqglColor4ubv\t\t= NULL;\n//\tqglDepthRange\t\t= NULL;\n\tqglDrawBuffer\t\t= NULL;\n\tqglDrawPixels\t\t= NULL;\n\tqglEnd\t\t\t\t= NULL;\n\tqglFrustum\t\t\t= NULL;\n\tqglGetTexLevelParameteriv\t= NULL;\n\tqglLoadIdentity\t\t= NULL;\n\tqglLoadMatrixf\t\t= NULL;\n\tqglNormal3f\t\t\t= NULL;\n\tqglNormal3fv\t\t= NULL;\n\tqglMatrixMode\t\t= NULL;\n\tqglMultMatrixf\t\t= NULL;\n//\tqglOrtho\t\t\t= NULL;\n\tqglPolygonMode\t\t= NULL;\n\tqglPopMatrix\t\t= NULL;\n\tqglPushMatrix\t\t= NULL;\n\tqglReadBuffer\t\t= NULL;\n\tqglRotatef\t\t\t= NULL;\n\tqglScalef\t\t\t= NULL;\n\tqglShadeModel\t\t= NULL;\n\tqglTexCoord1f\t\t= NULL;\n\tqglTexCoord2f\t\t= NULL;\n\tqglTexCoord2fv\t\t= NULL;\n\tqglTexEnvf\t\t\t= NULL;\n\tqglTexEnvfv\t\t\t= NULL;\n\tqglTexEnvi\t\t\t= NULL;\n\tqglTexGeni\t\t\t= NULL;\n\tqglTexGenfv\t\t\t= NULL;\n\tqglTexImage3D\t\t= NULL;\n\tqglTexSubImage3D\t= NULL;\n\tqglTranslatef\t\t= NULL;\n\tqglVertex2f\t\t\t= NULL;\n\tqglVertex3f\t\t\t= NULL;\n\tqglVertex3fv\t\t= NULL;\n#endif\n\n\t//various vertex array stuff.\n\tqglArrayElement\t\t\t= NULL;\n\tqglVertexPointer\t\t= NULL;\n\tqglNormalPointer\t\t= NULL;\n\tqglTexCoordPointer\t\t= NULL;\n\tqglColorPointer\t\t\t= NULL;\n\tqglEnableClientState\t= NULL;\n\tqglDisableClientState\t= NULL;\n\n\tqglDrawRangeElements\t= NULL;\n\n\t//fixme: definatly make non-core\n\tqglPushAttrib\t\t= NULL;\n\tqglPopAttrib\t\t= NULL;\n\n\t//does this need to be non-core as well?\n\tqglFogi\t\t\t\t= NULL;\n\tqglFogf\t\t\t\t= NULL;\n\tqglFogfv\t\t\t= NULL;\n\n\n\tqglGetTexEnviv\t\t= NULL;\n\tqglGetPointerv\t\t= NULL;\n\n\tqglGetStringi\t\t= NULL;\n\n\t//used by heightmaps\n\tqglGenLists\t\t= NULL;\n\tqglNewList\t\t= NULL;\n\tqglEndList\t\t= NULL;\n\tqglCallList\t\t= NULL;\n\n#ifndef GL_STATIC\n\tqglBindBufferARB\t\t= NULL;\n#endif\n\n\tgl_vendor = NULL;\n\tgl_renderer = NULL;\n\tgl_version = NULL;\n\tgl_extensions = NULL;\n\tgl_num_extensions = 0;\n\n#ifndef qglActiveTextureARB\n\tqglActiveTextureARB = NULL;\n#endif\n\tqglClientActiveTextureARB = NULL;\n\tqglSelectTextureSGIS = NULL;\n\tmtexid0 = 0;\n\n#ifndef GL_STATIC\n\tqglGenFramebuffersEXT\t\t= NULL;\n\tqglDeleteFramebuffersEXT\t= NULL;\n\tqglBindFramebufferEXT\t\t= NULL;\n\tqglGenRenderbuffersEXT\t\t= NULL;\n\tqglDeleteRenderbuffersEXT\t= NULL;\n\tqglBindRenderbufferEXT\t\t= NULL;\n\tqglRenderbufferStorageEXT\t= NULL;\n\tqglFramebufferTexture2DEXT\t= NULL;\n#endif\n\n\t//no GL_EXT_stencil_two_side\n\tqglActiveStencilFaceEXT = NULL;\n\n\t//no truform. sorry.\n\tqglPNTrianglesfATI = NULL;\n\tqglPNTrianglesiATI = NULL;\n\n\t//fragment programs\n//\tqglProgramStringARB = NULL;\n//\tqglGetProgramivARB = NULL;\n//\tqglBindProgramARB = NULL;\n//\tqglGenProgramsARB = NULL;\n\n\n#ifndef GL_STATIC\n\tqglStencilOpSeparate = NULL;\n#endif\n\tqglActiveStencilFaceEXT = NULL;\n\n#ifndef GL_STATIC\n\tqglCompressedTexImage2D = NULL;\n\tqglCompressedTexImage3D = NULL;\n\tqglCompressedTexSubImage2D = NULL;\n\tqglCompressedTexSubImage3D = NULL;\n\tqglGetCompressedTexImage = NULL;\n#endif\n\tqglDepthBoundsEXT = NULL;\n\tqglPNTrianglesfATI = NULL;\n\tqglPNTrianglesiATI = NULL;\n\tqglPatchParameteriARB = NULL;\n#ifndef GL_STATIC\n\tqglBindTexture\t\t\t= NULL;\n#endif\n\tqglLockArraysEXT = NULL;\n\tqglUnlockArraysEXT = NULL;\n\tqglBufferStorage\t= NULL;\n#if !defined(GL_STATIC)\n\tqglGenBuffersARB\t= NULL;\n\tqglDeleteBuffersARB\t= NULL;\n\tqglBindBufferARB\t= NULL;\n\tqglBufferDataARB\t= NULL;\n\tqglBufferSubDataARB\t= NULL;\n\tqglMapBufferARB\t\t= NULL;\n\tqglUnmapBufferARB\t= NULL;\n\tqglMapBufferRange\t= NULL;\n\n\tqglCreateProgramObjectARB\t= NULL;\n\tqglDeleteProgramObject_\t\t= NULL;\n\tqglDeleteShaderObject_\t\t= NULL;\n\tqglUseProgramObjectARB\t\t= NULL;\n\tqglCreateShaderObjectARB\t= NULL;\n\tqglGetProgramParameteriv_\t= NULL;\n\tqglGetShaderParameteriv_\t= NULL;\n\tqglAttachObjectARB\t\t\t= NULL;\n\tqglGetProgramInfoLog_\t\t= NULL;\n\tqglGetShaderInfoLog_\t\t= NULL;\n\tqglShaderSourceARB\t\t\t= NULL;\n\tqglCompileShaderARB\t\t\t= NULL;\n\tqglLinkProgramARB\t\t\t= NULL;\n\tqglBindAttribLocationARB\t= NULL;\n\tqglGetAttribLocationARB\t\t= NULL;\n\tqglVertexAttrib4f\t\t\t= NULL;\n\tqglVertexAttribPointer\t\t= NULL;\n\tqglGetVertexAttribiv\t\t= NULL;\n\tqglGetVertexAttribPointerv\t= NULL;\n\tqglEnableVertexAttribArray\t= NULL;\n\tqglDisableVertexAttribArray\t= NULL;\n\tqglGetUniformLocationARB\t= NULL;\n\tqglUniformMatrix4fvARB\t\t= NULL;\n\tqglUniformMatrix3x4fv\t\t= NULL;\n\tqglUniformMatrix4x3fv\t\t= NULL;\n\tqglUniform4fARB\t\t\t\t= NULL;\n\tqglUniform4fvARB\t\t\t= NULL;\n\tqglUniform3fARB\t\t\t\t= NULL;\n\tqglUniform3fvARB\t\t\t= NULL;\n\tqglUniform2fvARB\t\t\t= NULL;\n\tqglUniform1iARB\t\t\t\t= NULL;\n\tqglUniform1fARB\t\t\t\t= NULL;\n\tqglGetShaderSource\t\t\t= NULL;\n#endif\n\n\tqglGetProgramBinary = NULL;\n\tqglProgramBinary = NULL;\n\n\tqglGetGraphicsResetStatus = NULL;\t\t\t\t//its not allowed to crash us. probably will. grr. oh well.\n\n\n\tqglGenVertexArrays\t= NULL;\n\tqglBindVertexArray\t= NULL;\n\tqglTexStorage2D = NULL;\n\tqglTexStorage3D = NULL;\n\n#ifndef GL_STATIC\n\tqglGenFramebuffersEXT\t\t\t= NULL;\n\tqglDeleteFramebuffersEXT\t\t= NULL;\n\tqglBindFramebufferEXT\t\t\t= NULL;\n\tqglGenRenderbuffersEXT\t\t\t= NULL;\n\tqglDeleteRenderbuffersEXT\t\t= NULL;\n\tqglBindRenderbufferEXT\t\t\t= NULL;\n\tqglRenderbufferStorageEXT\t\t= NULL;\n\tqglFramebufferTexture2DEXT\t\t= NULL;\n\tqglFramebufferRenderbufferEXT\t= NULL;\n\tqglCheckFramebufferStatusEXT\t= NULL;\n\tqglGetFramebufferAttachmentParameteriv\t= NULL;\n#endif\n#ifdef DEBUG\n\tqglDebugMessageControlARB = NULL;\n\tqglDebugMessageInsertARB = NULL;\n\tqglDebugMessageCallbackARB = NULL;\n\tqglGetDebugMessageLogARB = NULL;\n#endif\n\n\tqglGenQueriesARB\t\t= NULL;\n\tqglDeleteQueriesARB\t\t= NULL;\n\tqglBeginQueryARB\t\t= NULL;\n\tqglEndQueryARB\t\t\t= NULL;\n\tqglGetQueryObjectuivARB\t= NULL;\n\n\tqglGenVertexArrays = NULL;\n\tqglBindVertexArray = NULL;\n\n\tqglDrawBuffers = NULL;\n\n\tqglLoadMatrixf = NULL;\n\tqglPolygonMode = NULL;\n\tqglShadeModel = NULL;\n\tqglDrawBuffer = NULL;\n\n\tmemset(&sh_config, 0, sizeof(sh_config));\n\tmemset(&gl_config, 0, sizeof(gl_config));\n}\n\n//the vid routines have initialised a window, and now they are giving us a reference to some of of GetProcAddress to get pointers to the funcs.\nqboolean GL_Init(rendererstate_t *info, void *(*getglfunction) (char *name))\n{\n#ifndef GL_STATIC\n\tqglBindTexture\t\t= (void *)getglcore(\"glBindTexture\");\t//for compleateness. core in 1.1. needed by fte.\n\tqglBlendFunc\t\t= (void *)getglcore(\"glBlendFunc\");\n\tqglClear\t\t\t= (void *)getglcore(\"glClear\");\n\tqglClearColor\t\t= (void *)getglcore(\"glClearColor\");\n\tqglClearStencil\t\t= (void *)getglcore(\"glClearStencil\");\n\tqglColorMask\t\t= (void *)getglcore(\"glColorMask\");\n\tqglCopyTexImage2D\t= (void *)getglcore(\"glCopyTexImage2D\");\n\tqglCopyTexSubImage2D= (void *)getglcore(\"glCopyTexSubImage2D\");\n\tqglCullFace\t\t\t= (void *)getglcore(\"glCullFace\");\n\tqglDepthFunc\t\t= (void *)getglcore(\"glDepthFunc\");\n\tqglDepthMask\t\t= (void *)getglcore(\"glDepthMask\");\n//\tqglDepthRangef\t\t= (void *)getglcore(\"glDepthRangef\");\n\tqglDisable\t\t\t= (void *)getglcore(\"glDisable\");\n\tqglEnable\t\t\t= (void *)getglcore(\"glEnable\");\n\tqglFinish\t\t\t= (void *)getglcore(\"glFinish\");\n\tqglFlush\t\t\t= (void *)getglcore(\"glFlush\");\n\tqglGenTextures\t\t= (void *)getglcore(\"glGenTextures\");\n\tqglPixelStorei\t\t= (void *)getglcore(\"glPixelStorei\");\n\tqglGetFloatv\t\t= (void *)getglcore(\"glGetFloatv\");\n\tqglGetIntegerv\t\t= (void *)getglcore(\"glGetIntegerv\");\n\tqglGetString\t\t= (void *)getglcore(\"glGetString\");\n\tqglHint\t\t\t\t= (void *)getglcore(\"glHint\");\n\tqglIsEnabled\t\t= (void *)getglext(\"glIsEnabled\");\n\tqglReadPixels\t\t= (void *)getglcore(\"glReadPixels\");\n\tqglTexImage2D\t\t= (void *)getglcore(\"glTexImage2D\");\n\tqglTexSubImage2D\t= (void *)getglcore(\"glTexSubImage2D\");\n\tqglTexParameteri\t= (void *)getglcore(\"glTexParameteri\");\n\tqglTexParameterf\t= (void *)getglcore(\"glTexParameterf\");\n\tqglTexParameteriv\t= (void *)getglcore(\"glTexParameteriv\");\n\tqglTexParameterfv\t= (void *)getglcore(\"glTexParameterfv\");\n\tqglViewport\t\t\t= (void *)getglcore(\"glViewport\");\n\tqglGetBooleanv\t\t= (void *)getglcore(\"glGetBooleanv\");\n\tqglGetError\t\t\t= (void *)getglcore(\"glGetError\");\n\tqglDeleteTextures\t= (void *)getglcore(\"glDeleteTextures\");\n\tqglDrawElements\t\t\t= (void *)getglcore(\"glDrawElements\");\n\tqglDrawArrays\t\t\t= (void *)getglcore(\"glDrawArrays\");\n\tqglStencilOp\t\t= (void *)getglcore(\"glStencilOp\");\n\tqglStencilFunc\t\t= (void *)getglcore(\"glStencilFunc\");\n\tqglScissor\t\t\t= (void *)getglcore(\"glScissor\");\n\tqglPolygonOffset\t= (void *)getglext(\"glPolygonOffset\");\n\tqglLineWidth\t\t= (void *)getglcore(\"glLineWidth\");\n#endif\n\n#ifndef FTE_TARGET_WEB\n\tqglGetTexLevelParameteriv\t= (void *)getglcore(\"glGetTexLevelParameteriv\");\n\tqglReadBuffer\t\t= (void *)getglcore(\"glReadBuffer\");\n\tqglTexImage3D\t\t= (void *)getglext(\"glTexImage3D\");\n\tqglTexSubImage3D\t= (void *)getglext(\"glTexSubImage3D\");\n#endif\n\n\n\t//various vertex array stuff.\n\tqglArrayElement\t\t\t= (void *)getglcore(\"glArrayElement\");\n\tqglVertexPointer\t\t= (void *)getglcore(\"glVertexPointer\");\n\tqglNormalPointer\t\t= (void *)getglcore(\"glNormalPointer\");\n\tqglTexCoordPointer\t\t= (void *)getglcore(\"glTexCoordPointer\");\n\tqglColorPointer\t\t\t= (void *)getglcore(\"glColorPointer\");\n\tqglEnableClientState\t= (void *)getglcore(\"glEnableClientState\");\n\tqglDisableClientState\t= (void *)getglcore(\"glDisableClientState\");\n\n\tqglMultiDrawElements\t= (void *)getglext(\"glMultiDrawElements\");\t//since gl2\n\n\n\tqglGetTexEnviv\t\t= (void *)getglext(\"glGetTexEnviv\");\n\tqglGetPointerv\t\t= (void *)getglext(\"glGetPointerv\");\n\n\tqglGetStringi\t\t= (void *)getglext(\"glGetStringi\");\n\n#ifndef GL_STATIC\n\tqglBindBufferARB\t\t= (void *)getglext(\"glBindBufferARB\");\n\tif (!qglBindBufferARB)\n\t\tqglBindBufferARB\t= (void *)getglext(\"glBindBuffer\");\n\tif (!qglBindBufferARB)\n\t\tqglBindBufferARB\t= GL_BindBufferARBStub;\n\n\tif (gl_config.glversion >= 3.0)\n\t\tqglGenerateMipmap\t= (void *)getglext(\"glGenerateMipmap\");\n\telse\n\t\tqglGenerateMipmap = NULL;\n#endif\n\n\tif (!qglGetString)\n\t\tSys_Error(\"qglGetString not set. Serious gl library initialisation error\\n\");\n\n\tgl_vendor = qglGetString (GL_VENDOR);\n\tCon_SafePrintf (\"GL_VENDOR: %s\\n\", gl_vendor);\n\tgl_renderer = qglGetString (GL_RENDERER);\n\tCon_SafePrintf (\"GL_RENDERER: %s\\n\", gl_renderer);\n\n\tgl_version = qglGetString (GL_VERSION);\n\tCon_SafePrintf (\"GL_VERSION: %s\\n\", gl_version);\n\n\tmemset(&sh_config, 0, sizeof(sh_config));\n\n\tif (!GL_CheckExtensions (getglfunction))\n\t\treturn false;\n\n#ifndef FTE_TARGET_WEB\n\tif (!gl_config.gles)\n\t{\n\t\tqglAlphaFunc\t\t= (void *)getglcore(\"glAlphaFunc\");\n\t\tqglBegin\t\t\t= (void *)getglcore(\"glBegin\");\n\t\tqglClearDepth\t\t= (void *)getglcore(\"glClearDepth\");\n\t\tqglClipPlane \t\t= (void *)getglcore(\"glClipPlane\");\n//\t\tqglColor3f\t\t\t= (void *)getglcore(\"glColor3f\");\n//\t\tqglColor3ub\t\t\t= (void *)getglcore(\"glColor3ub\");\n\t\tqglColor4f\t\t\t= (void *)getglcore(\"glColor4f\");\n\t\tqglColor4fv\t\t\t= (void *)getglext(\"glColor4fv\");\n//\t\tqglColor4ub\t\t\t= (void *)getglcore(\"glColor4ub\");\n//\t\tqglColor4ubv\t\t= (void *)getglcore(\"glColor4ubv\");\n//\t\tqglDepthRange\t\t= (void *)getglcore(\"glDepthRange\");\n\t\tqglDrawBuffer\t\t= (void *)getglcore(\"glDrawBuffer\");\n\t\tqglDrawPixels\t\t= (void *)getglcore(\"glDrawPixels\");\n\t\tqglEnd\t\t\t\t= (void *)getglcore(\"glEnd\");\n\t\tqglFrustum\t\t\t= (void *)getglcore(\"glFrustum\");\n\t\tqglLoadIdentity\t\t= (void *)getglcore(\"glLoadIdentity\");\n\t\tqglLoadMatrixf\t\t= (void *)getglcore(\"glLoadMatrixf\");\n\t\tqglNormal3f\t\t\t= (void *)getglcore(\"glNormal3f\");\n\t\tqglNormal3fv\t\t= (void *)getglcore(\"glNormal3fv\");\n\t\tqglMatrixMode\t\t= (void *)getglcore(\"glMatrixMode\");\n\t\tqglMultMatrixf\t\t= (void *)getglcore(\"glMultMatrixf\");\n//\t\tqglOrtho\t\t\t= (void *)getglcore(\"glOrtho\");\n\t\tqglPolygonMode\t\t= (void *)getglcore(\"glPolygonMode\");\n\t\tqglPopMatrix\t\t= (void *)getglcore(\"glPopMatrix\");\n\t\tqglPushMatrix\t\t= (void *)getglcore(\"glPushMatrix\");\n\t\tqglRotatef\t\t\t= (void *)getglcore(\"glRotatef\");\n\t\tqglScalef\t\t\t= (void *)getglcore(\"glScalef\");\n\t\tqglShadeModel\t\t= (void *)getglcore(\"glShadeModel\");\n\t\tqglTexCoord1f\t\t= (void *)getglcore(\"glTexCoord1f\");\n\t\tqglTexCoord2f\t\t= (void *)getglcore(\"glTexCoord2f\");\n\t\tqglTexCoord2fv\t\t= (void *)getglcore(\"glTexCoord2fv\");\n\t\tqglTexGeni\t\t\t= (void *)getglcore(\"glTexGeni\");\n\t\tqglTexGenfv\t\t\t= (void *)getglcore(\"glTexGenfv\");\n\t\tqglTranslatef\t\t= (void *)getglcore(\"glTranslatef\");\n\t\tqglVertex2f\t\t\t= (void *)getglcore(\"glVertex2f\");\n\t\tqglVertex3f\t\t\t= (void *)getglcore(\"glVertex3f\");\n\t\tqglVertex3fv\t\t= (void *)getglcore(\"glVertex3fv\");\n\n\t\t//fixme: definatly make non-core\n\t\tqglPushAttrib\t\t= (void *)getglcore(\"glPushAttrib\");\n\t\tqglPopAttrib\t\t= (void *)getglcore(\"glPopAttrib\");\n\n\t\t//does this need to be non-core as well?\n\t\tqglFogi\t\t\t\t= (void *)getglcore(\"glFogi\");\n\t\tqglFogf\t\t\t\t= (void *)getglcore(\"glFogf\");\n\t\tqglFogfv\t\t\t= (void *)getglcore(\"glFogfv\");\n\n\t\t//used by heightmaps\n\t\tqglGenLists\t\t= (void*)getglcore(\"glGenLists\");\n\t\tqglNewList\t\t= (void*)getglcore(\"glNewList\");\n\t\tqglEndList\t\t= (void*)getglcore(\"glEndList\");\n\t\tqglCallList\t\t= (void*)getglcore(\"glCallList\");\n\t}\n\tif (!gl_config.gles || (gl_config.gles && gl_config.glversion<2))\n\t{\n\t\tqglTexEnvf\t\t\t= (void *)getglcore(\"glTexEnvf\");\n\t\tqglTexEnvfv\t\t\t= (void *)getglcore(\"glTexEnvfv\");\n\t\tqglTexEnvi\t\t\t= (void *)getglcore(\"glTexEnvi\");\n\t}\n\tif (!qglColor4fv)\n\t\tqglColor4fv = GL_Color4fv_Emul;\t//can be missing in gles1\n#endif\n\n\tif ((gl_config.gles && gl_config.glversion >= 3) || (!gl_config.gles && gl_config.glversion >= 2))\n\t\tqglDrawBuffers = (void *)getglext(\"glDrawBuffers\");\n\telse\n\t\tqglDrawBuffers = NULL;\n\n\tif (gl_config.gles && gl_config.glversion >= 2)\n\t{\n\t\t/*these functions do not exist in gles2, they only exist on some platforms because they were provided for gl1*/\n\t\tqglLoadMatrixf = NULL;\n\t\tqglPolygonMode = NULL;\n\t\tqglShadeModel = NULL;\n//\t\tqglDepthRange = NULL;\n\t\tqglDrawBuffer = NULL;\n\n\t\tqglEnableClientState = GL_ClientStateStub;\n\t\tqglDisableClientState = GL_ClientStateStub;\n\n\t\tqglDrawRangeElements = GL_DrawRangeElementsEmul;\n\t}\n\telse if (gl_config_nofixedfunc)\n\t{\n\t\tqglLoadMatrixf = NULL;\n\t\tqglPolygonMode = NULL;\n\t\tqglShadeModel = NULL;\n//\t\tqglDepthRange = NULL;\n\t\tqglDrawBuffer = NULL;\n\n\t\tqglEnableClientState = GL_ClientStateStub;\n\t\tqglDisableClientState = GL_ClientStateStub;\n\t}\n\n\tqglClearColor (0,0,0,1);\t//clear to black so that it looks a little nicer on start.\n\tqglClear(GL_COLOR_BUFFER_BIT);\n\n\tif (qglPolygonMode)\n\t\tqglPolygonMode (GL_FRONT_AND_BACK, GL_FILL);\n\tif (qglShadeModel)\n\t\tqglShadeModel (GL_FLAT);\n\n#ifdef DEBUG\n\tif (qglDebugMessageControlARB)\n\t\tqglDebugMessageControlARB(0, 0, 0, 0, NULL, true);\n\tif (qglDebugMessageCallbackARB)\n\t{\n\t\tqglDebugMessageCallbackARB(myGLDEBUGPROCAMD, NULL);\n\t\tqglEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB);\n\t}\n\tqglGetError();\t/*suck up the invalid operation error for non-debug contexts*/\n#endif\n\n\tsh_config.texture2d_maxsize = 256;\t//early minidrivers might not implement this, but anything else should.\n\tqglGetIntegerv(GL_MAX_TEXTURE_SIZE, &sh_config.texture2d_maxsize);\n\n\t/*note regarding GL_COMPRESSED_TEXTURE_FORMATS:\n\tnvidia lists bc1-3, oes paletted, etc2, astc, astc_srgb.\n\tso the only listed formats that the hardware actually supports are bc1-3.\n\tand bc4-7 are NOT listed, despite eg vulkan supporting them.\n\tor in other words, this info is utterly useless (its deprecated somewhere, so this shouldn't be a huge surprise).\n\t*/\n\n\tif (gl_config.gles)\n\t{\n\t\tsh_config.minver = 100;\n\t\tif (gl_config.glversion >= 3.1)\n\t\t\tsh_config.maxver = 310;\n\t\telse if (gl_config.glversion >= 3.0)\n\t\t\tsh_config.maxver = 300;\n\t\telse\n\t\t\tsh_config.maxver = 100;\n\t\tsh_config.blobpath = \"gles/%s.blob\";\n\t\tsh_config.progpath = \"glsl/%s.glsl\";\n\t\tsh_config.shadernamefmt = \"%s_gles\";\n\n\t\tsh_config.can_mipcap = gl_config.glversion >= 3.0;\n\t\tsh_config.can_mipbias = false;\n\n\t\tsh_config.havecubemaps = gl_config.glversion >= 2.0;\n\t}\n\telse\n\t{\n\t\tsh_config.can_mipcap = gl_config.glversion >= 1.2;\n\t\tsh_config.can_mipbias = gl_config.glversion >= 1.4;//||GL_CheckExtension(\"GL_EXT_texture_lod_bias\");\n\n\t\tsh_config.havecubemaps = gl_config.glversion >= 1.3||GL_CheckExtension(\"GL_ARB_texture_cube_map\");\t//cubemaps AND clamp-to-edge.\n\n\t\tif (gl_config.nofixedfunc)\n\t\t{\t//core contexts don't normally support glsl < 140 (such glsl versions have lots of compat syntax still, which will not function on core. drivers might accept it anyway, but yeah, lots of crap that shouldn't work)\n\t\t\t//FIXME: GL_NUM_SHADING_LANGUAGE_VERSIONS and GL_SHADING_LANGUAGE_VERSION might allow for earlier versions.\n\t\t\tsh_config.minver = 140;\n\t\t\tsh_config.maxver = gl_config.maxglslversion;\n\t\t}\n\t\telse if (gl_config.arb_shader_objects)\n\t\t{\n\t\t\t//FIXME: we could accept 100 here, but that gets messy when gles is considered, and old drivers suck anyway.\n\t\t\tsh_config.minver = 110;\n\t\t\tsh_config.maxver = gl_config.maxglslversion;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tsh_config.minver = 0;\n\t\t\tsh_config.maxver = 0;\n\t\t}\n\t\tsh_config.blobpath = \"glsl/%s.blob\";\n\t\tsh_config.progpath = \"glsl/%s.glsl\";\n\t\tsh_config.shadernamefmt = \"%s_glsl\";\n\t}\n\n\tsh_config.texturecube_maxsize = 0;\n\tif (sh_config.havecubemaps)\n\t\tqglGetIntegerv(GL_MAX_CUBE_MAP_TEXTURE_SIZE_ARB, &sh_config.texturecube_maxsize);\n\tif (!gl_config_gles || gl_config.glversion >= 3)\n\t\tqglGetIntegerv(GL_MAX_3D_TEXTURE_SIZE, &sh_config.texture3d_maxsize);\n\tif (gl_config.glversion >= 3)\n\t\tqglGetIntegerv(GL_MAX_ARRAY_TEXTURE_LAYERS, &sh_config.texture2darray_maxlayers);\n\n\tsh_config.progs_supported\t= gl_config.arb_shader_objects;\n\tsh_config.progs_required\t= gl_config_nofixedfunc;\n\n\tif (gl_config.glversion >= 4.1)\n\t{\n\t\tGLint maxuniformvecs = 256;\t//minimum value... or 128 on webgl1(grr)\n\t\tif (gl_config.glversion >= 4.1)\n\t\t\tqglGetIntegerv(GL_MAX_VERTEX_UNIFORM_VECTORS, &maxuniformvecs);\n\t\telse if (gl_config.glversion >= 2.0)\n\t\t{\n\t\t\tqglGetIntegerv(GL_MAX_VERTEX_UNIFORM_COMPONENTS, &maxuniformvecs);\t//at least 1024, supposedly.\n\t\t\tmaxuniformvecs /= 4;\n\t\t}\n\n\t\tsh_config.max_gpu_bones\t= (maxuniformvecs-64)/3;\t//we don't know how many we're actually going to use for any specific bit of glsl, so make a generous guess and throw in a bit more for drivers that lie. we need 3 per bone matrix.\n\t\tsh_config.max_gpu_bones = bound(0, sh_config.max_gpu_bones, MAX_BONES); //o.O\n\t}\n\n\tif (sh_config.progs_supported)\n\t{\n\t\tsh_config.pDeleteProg\t\t= GLSlang_DeleteProg;\n\t\tsh_config.pLoadBlob\t\t\t= qglProgramBinary?GLSlang_LoadBlob:NULL;\n\t\tsh_config.pCreateProgram\t= GLSlang_CreateProgramPermu;\n\t\tsh_config.pValidateProgram\t= GLSlang_ValidateProgramPermu;\n\t\tsh_config.pProgAutoFields\t= GLSlang_ProgAutoFields;\n\t}\n\n\tif (gl_config_nofixedfunc)\n\t{\n\t\tsh_config.tex_env_combine\t\t= 1;\n\t\tsh_config.nv_tex_env_combine4\t= 1;\n\t\tsh_config.env_add\t\t\t\t= 1;\n\t}\n\telse\n\t{\n\t\tsh_config.tex_env_combine\t\t= gl_config.tex_env_combine;\n\t\tsh_config.nv_tex_env_combine4\t= gl_config.nv_tex_env_combine4;\n\t\tsh_config.env_add\t\t\t\t= gl_config.env_add;\n\t}\n\n\n//\tCon_Printf(\"sh_config.progs supported: %i, required: %i, vers: %i - %i\\n\", sh_config.progs_supported, sh_config.progs_required, sh_config.minver, sh_config.maxver);\n\n\treturn true;\n}\n\n\n#ifdef DEBUG\n#define GL_VERTEX_ARRAY_BINDING\t\t\t\t\t0x85B5\n#define GL_ARRAY_BUFFER\t\t\t\t\t\t\t0x8892\n#define GL_ELEMENT_ARRAY_BUFFER\t\t\t\t\t0x8893\n#define GL_ARRAY_BUFFER_BINDING\t\t\t\t\t0x8894\n#define GL_ELEMENT_ARRAY_BUFFER_BINDING\t\t\t0x8895\n#define GL_VERTEX_ARRAY_BUFFER_BINDING\t\t\t0x8896\n#define GL_NORMAL_ARRAY_BUFFER_BINDING\t\t\t0x8897\n#define GL_COLOR_ARRAY_BUFFER_BINDING\t\t\t0x8898\n#define GL_TEXTURE_COORD_ARRAY_BUFFER_BINDING   0x889A\n#define GL_VERTEX_ATTRIB_ARRAY_ENABLED\t\t\t0x8622\n#define GL_VERTEX_ATTRIB_ARRAY_SIZE\t\t\t\t0x8623\n#define GL_VERTEX_ATTRIB_ARRAY_STRIDE\t\t\t0x8624\n#define GL_VERTEX_ATTRIB_ARRAY_TYPE\t\t\t\t0x8625\n#define GL_VERTEX_ATTRIB_ARRAY_NORMALIZED\t\t0x886A\n#define GL_VERTEX_ATTRIB_ARRAY_POINTER\t\t\t0x8645\n#define GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING\t0x889F\n#define GL_CURRENT_PROGRAM\t\t\t\t\t\t0x8B8D\n\nstatic char *DecodeGLEnum(GLenum num)\n{\n\tswitch(num)\n\t{\n\tcase GL_CW:\t\t\t\t\t\treturn \"GL_CW\";\n\tcase GL_CCW:\t\t\t\t\treturn \"GL_CCW\";\n\tcase GL_NEVER:\t\t\t\t\treturn \"GL_NEVER\";\n\tcase GL_LESS:\t\t\t\t\treturn \"GL_LESS\";\n\tcase GL_EQUAL:\t\t\t\t\treturn \"GL_EQUAL\";\n\tcase GL_LEQUAL:\t\t\t\t\treturn \"GL_LEQUAL\";\n\tcase GL_GREATER:\t\t\t\treturn \"GL_GREATER\";\n\tcase GL_NOTEQUAL:\t\t\t\treturn \"GL_NOTEQUAL\";\n\tcase GL_GEQUAL:\t\t\t\t\treturn \"GL_GEQUAL\";\n\tcase GL_ALWAYS:\t\t\t\t\treturn \"GL_ALWAYS\";\n\tcase GL_FRONT:\t\t\t\t\treturn \"GL_FRONT\";\n\tcase GL_BACK:\t\t\t\t\treturn \"GL_BACK\";\n\tcase GL_FRONT_AND_BACK:\t\t\treturn \"GL_FRONT_AND_BACK\";\n\tcase GL_COMBINE_ARB:\t\t\treturn \"GL_COMBINE\";\n\tcase GL_MODULATE:\t\t\t\treturn \"GL_MODULATE\";\n\tcase GL_REPLACE:\t\t\t\treturn \"GL_REPLACE\";\n\tcase GL_ZERO:\t\t\t\t\treturn \"GL_ZERO\";\n\tcase GL_ONE:\t\t\t\t\treturn \"GL_ONE\";\n\tcase GL_SRC_COLOR:\t\t\t\treturn \"GL_SRC_COLOR\";\n\tcase GL_ONE_MINUS_SRC_COLOR:\treturn \"GL_ONE_MINUS_SRC_COLOR\";\n\tcase GL_SRC_ALPHA:\t\t\t\treturn \"GL_SRC_ALPHA\";\n\tcase GL_ONE_MINUS_SRC_ALPHA:\treturn \"GL_ONE_MINUS_SRC_ALPHA\";\n\tcase GL_DST_ALPHA:\t\t\t\treturn \"GL_DST_ALPHA\";\n\tcase GL_ONE_MINUS_DST_ALPHA:\treturn \"GL_ONE_MINUS_DST_ALPHA\";\n\tcase GL_DST_COLOR:\t\t\t\treturn \"GL_DST_COLOR\";\n\tcase GL_ONE_MINUS_DST_COLOR:\treturn \"GL_ONE_MINUS_DST_COLOR\";\n\tcase GL_SRC_ALPHA_SATURATE:\t\treturn \"GL_SRC_ALPHA_SATURATE\";\n\tdefault:\t\t\t\t\t\treturn va(\"0x%x\", num);\n\t}\n}\nvoid DumpGLState(void)\n{\n\tint rval;\n\tvoid *ptr;\n\tint i;\n\tGLint glint;\n\tGLint glint4[4];\n\n//\tif (qglGetVertexAttribiv)\n\t{\n\t\tif (qglBindVertexArray)\n\t\t{\n\t\t\tqglGetIntegerv(GL_VERTEX_ARRAY_BINDING, &rval);\n\t\t\tSys_Printf(\"VERTEX_ARRAY_BINDING: %i\\n\", rval);\n\t\t}\n\t\tif (qglBindBufferARB)\n\t\t{\n\t\t\tqglGetIntegerv(GL_ARRAY_BUFFER_BINDING, &rval);\n\t\t\tSys_Printf(\"GL_ARRAY_BUFFER_BINDING: %i\\n\", rval);\n\t\t}\n\t\tif (qglIsEnabled(GL_COLOR_ARRAY))\n\t\t{\n\t\t\tif (qglBindBufferARB)\n\t\t\t\tqglGetIntegerv(GL_COLOR_ARRAY_BUFFER_BINDING, &rval);\n\t\t\telse\n\t\t\t\trval = 0;\n\t\t\tqglGetPointerv(GL_COLOR_ARRAY_POINTER, &ptr);\n\t\t\tSys_Printf(\"GL_COLOR_ARRAY: %s %i:%p\\n\", qglIsEnabled(GL_COLOR_ARRAY)?\"en\":\"dis\", rval, ptr);\n\t\t}\n//\t\tif (qglIsEnabled(GL_FOG_COORDINATE_ARRAY_EXT))\n//\t\t{\n//\t\t\tqglGetPointerv(GL_FOG_COORD_ARRAY_POINTER, &ptr);\n//\t\t\tSys_Printf(\"GL_FOG_COORDINATE_ARRAY_EXT: %i (%lx)\\n\", (int) qglIsEnabled(GL_FOG_COORDINATE_ARRAY_EXT), (int) ptr);\n//\t\t}\n\t\t{\n\t\t\tif (qglBindBufferARB)\n\t\t\t\tqglGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &rval);\n\t\t\telse\n\t\t\t\trval = 0;\n\t\t\tSys_Printf(\"GL_ELEMENT_ARRAY_BUFFER_BINDING: %i:%p\\n\", rval, (void*)0);\n\t\t}\n\t\tif (qglIsEnabled(GL_NORMAL_ARRAY))\n\t\t{\n\t\t\tif (qglBindBufferARB)\n\t\t\t\tqglGetIntegerv(GL_NORMAL_ARRAY_BUFFER_BINDING, &rval);\n\t\t\telse\n\t\t\t\trval = 0;\n\t\t\tqglGetPointerv(GL_NORMAL_ARRAY_POINTER, &ptr);\n\t\t\tSys_Printf(\"GL_NORMAL_ARRAY: %s %i:%p\\n\", qglIsEnabled(GL_NORMAL_ARRAY)?\"en\":\"dis\", rval, ptr);\n\t\t}\n\t//\tqglGetPointerv(GL_SECONDARY_COLOR_ARRAY_POINTER, &ptr);\n\t//\tSys_Printf(\"GL_SECONDARY_COLOR_ARRAY: %i (%lx)\\n\", (int) qglIsEnabled(GL_SECONDARY_COLOR_ARRAY), (int) ptr);\n\t\tfor (i = 0; i < 4; i++)\n\t\t{\n\t\t\tqglClientActiveTextureARB(mtexid0 + i);\n\t\t\tif (qglIsEnabled(GL_TEXTURE_COORD_ARRAY))\n\t\t\t{\n\t\t\t\tif (qglBindBufferARB)\n\t\t\t\t\tqglGetIntegerv(GL_TEXTURE_COORD_ARRAY_BUFFER_BINDING, &rval);\n\t\t\t\telse\n\t\t\t\t\trval = 0;\n\t\t\t\tqglGetPointerv(GL_TEXTURE_COORD_ARRAY_POINTER, &ptr);\n\t\t\t\tSys_Printf(\"GL_TEXTURE_COORD_ARRAY %i: %s %i:%p\\n\", i, qglIsEnabled(GL_TEXTURE_COORD_ARRAY)?\"en\":\"dis\", rval, ptr);\n\t\t\t}\n\t\t}\n\t\tif (qglIsEnabled(GL_VERTEX_ARRAY))\n\t\t{\n\t\t\tif (qglBindBufferARB)\n\t\t\t\tqglGetIntegerv(GL_VERTEX_ARRAY_BUFFER_BINDING, &rval);\n\t\t\telse\n\t\t\t\trval = 0;\n\t\t\tqglGetPointerv(GL_VERTEX_ARRAY_POINTER, &ptr);\n\t\t\tSys_Printf(\"GL_VERTEX_ARRAY: %s %i:%p\\n\", qglIsEnabled(GL_VERTEX_ARRAY)?\"en\":\"dis\", rval, ptr);\n\t\t}\n\n\t\tif (qglGetVertexAttribiv)\n\t\tfor (i = 0; i < 16; i++)\n\t\t{\n\t\t\tint en, bo, as, st, ty, no;\n\n\t\t\tqglGetVertexAttribiv(i, GL_VERTEX_ATTRIB_ARRAY_ENABLED, &en);\n\t\t\tif (!en)\n\t\t\t\tcontinue;\n\t\t\tif (qglBindBufferARB)\n\t\t\t\tqglGetVertexAttribiv(i, GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING, &bo);\n\t\t\telse\n\t\t\t\tbo = 0;\n\t\t\tqglGetVertexAttribiv(i, GL_VERTEX_ATTRIB_ARRAY_SIZE, &as);\n\t\t\tqglGetVertexAttribiv(i, GL_VERTEX_ATTRIB_ARRAY_STRIDE, &st);\n\t\t\tqglGetVertexAttribiv(i, GL_VERTEX_ATTRIB_ARRAY_TYPE, &ty);\n\t\t\tqglGetVertexAttribiv(i, GL_VERTEX_ATTRIB_ARRAY_NORMALIZED, &no);\n\t\t\tqglGetVertexAttribPointerv(i, GL_VERTEX_ATTRIB_ARRAY_POINTER, &ptr);\n\n\t\t\tSys_Printf(\"attrib%i: %s sz:%i st:%i ty:%0x %s%i:%p\\n\", i, en?\"en\":\"dis\", as, st,ty,no?\"norm \":\"\", bo, ptr);\n\t\t}\n\n\t\tif (qglUseProgramObjectARB)\n\t\t{\n\t\t\tqglGetIntegerv(GL_CURRENT_PROGRAM, &glint);\n\t\t\tSys_Printf(\"GL_CURRENT_PROGRAM: %i\\n\", glint);\n\t\t}\n\n\t\tqglGetIntegerv(GL_BLEND, &glint);\n\t\tSys_Printf(\"GL_BLEND: %i\\n\", glint);\n\t\tqglGetIntegerv(GL_BLEND_SRC, &glint);\n\t\tSys_Printf(\"GL_BLEND_SRC: %s\\n\", DecodeGLEnum(glint));\n\t\tqglGetIntegerv(GL_BLEND_DST, &glint);\n\t\tSys_Printf(\"GL_BLEND_DST: %s\\n\", DecodeGLEnum(glint));\n\n\t\tqglGetIntegerv(GL_DEPTH_WRITEMASK, &glint);\n\t\tSys_Printf(\"GL_DEPTH_WRITEMASK: %i\\n\", glint);\n\t\tqglGetIntegerv(GL_DEPTH_TEST, &glint);\n\t\tSys_Printf(\"GL_DEPTH_TEST: %i\\n\", glint);\n\t\tqglGetIntegerv(GL_DEPTH_FUNC, &glint);\n\t\tSys_Printf(\"GL_DEPTH_FUNC: %s\\n\", DecodeGLEnum(glint));\n\t\tqglGetIntegerv(GL_CULL_FACE, &glint);\n\t\tSys_Printf(\"GL_CULL_FACE: %i\\n\", glint);\n\t\tqglGetIntegerv(GL_CULL_FACE_MODE, &glint);\n\t\tSys_Printf(\"GL_CULL_FACE_MODE: %s\\n\", DecodeGLEnum(glint));\n\t\tqglGetIntegerv(GL_FRONT_FACE, &glint);\n\t\tSys_Printf(\"GL_FRONT_FACE: %s\\n\", DecodeGLEnum(glint));\n\t\tqglGetIntegerv(GL_SCISSOR_TEST, &glint);\n\t\tSys_Printf(\"GL_SCISSOR_TEST: %i\\n\", glint);\n\t\tqglGetIntegerv(GL_STENCIL_TEST, &glint);\n\t\tSys_Printf(\"GL_STENCIL_TEST: %i\\n\", glint);\n\t\tqglGetIntegerv(GL_COLOR_WRITEMASK, glint4);\n\t\tSys_Printf(\"GL_COLOR_WRITEMASK: %i %i %i %i\\n\", glint4[0], glint4[1], glint4[2], glint4[3]);\n\n\t\tGL_SelectTexture(0);\n\t\tqglGetIntegerv(GL_TEXTURE_2D, &glint);\n\t\tSys_Printf(\"0: GL_TEXTURE_2D: %i\\n\", glint);\n\t\tqglGetTexEnviv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, &glint);\n\t\tSys_Printf(\"0: GL_TEXTURE_ENV_MODE: %s\\n\", DecodeGLEnum(glint));\n\t\tGL_SelectTexture(1);\n\t\tqglGetIntegerv(GL_TEXTURE_2D, &glint);\n\t\tSys_Printf(\"1: GL_TEXTURE_2D: %i\\n\", glint);\n\t\tqglGetTexEnviv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, &glint);\n\t\tSys_Printf(\"1: GL_TEXTURE_ENV_MODE: %s\\n\", DecodeGLEnum(glint));\n\t\tGL_SelectTexture(2);\n\t\tqglGetIntegerv(GL_TEXTURE_2D, &glint);\n\t\tSys_Printf(\"2: GL_TEXTURE_2D: %i\\n\", glint);\n\t\tqglGetTexEnviv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, &glint);\n\t\tSys_Printf(\"2: GL_TEXTURE_ENV_MODE: %s\\n\", DecodeGLEnum(glint));\n\t}\n}\n#endif\n\n\n\nrendererinfo_t openglrendererinfo = {\n\t//customise the text printed depending on the actual type of opengl that we're locking ourselves to\n#ifdef FTE_TARGET_WEB\n\t\"WebGL\",\n#elif defined(GLESONLY)\n\t#ifdef GLSLONLY\n\t\t\"OpenGLES2+\",\n\t#else\n\t\t\"OpenGLES\",\n\t#endif\n#else\n\t\"OpenGL\",\n#endif\n\t{\n\t\t//reorder these too, if only so that 'setrenderer' lists gles-only builds as using gles instead of gl\n#if defined(GLESONLY)\n\t\t\"gles\",\n\t\t\"opengles\",\n\t\t\"gl\",\n\t\t\"opengl\",\n#else\n\t\t\"gl\",\n\t\t\"opengl\",\n\t\t\"gles\",\n\t\t\"opengles\",\n#endif\n\t},\n\tQR_OPENGL,\n\n\n\tGLDraw_Init,\n\tGLDraw_DeInit,\n\n\tGL_UpdateFiltering,\n\tGL_LoadTextureMips,\n\tGL_DestroyTexture,\n\n\tGLR_Init,\n\tGLR_DeInit,\n\tGLR_RenderView,\n\n\tGLVID_Init,\n\tGLVID_DeInit,\n\tGLVID_SwapBuffers,\n\tGLVID_ApplyGammaRamps,\n\tNULL,\n\tNULL,\n\tNULL,\n\tGLVID_SetCaption,\t//setcaption\n\tGLVID_GetRGBInfo,\n\n\n\tGLSCR_UpdateScreen,\n\n\tGLBE_SelectMode,\n\tGLBE_DrawMesh_List,\n\tGLBE_DrawMesh_Single,\n\tGLBE_SubmitBatch,\n\tGLBE_GetTempBatch,\n\tGLBE_DrawWorld,\n\tGLBE_Init,\n\tGLBE_GenBrushModelVBO,\n\tGLBE_ClearVBO,\n\tGLBE_UpdateLightmaps,\n\tGLBE_SelectEntity,\n\tGLBE_SelectDLight,\n\tGLBE_Scissor,\n\tGLBE_LightCullModel,\n\n\tGLBE_VBO_Begin,\n\tGLBE_VBO_Data,\n\tGLBE_VBO_Finish,\n\tGLBE_VBO_Destroy,\n\n\tGLBE_RenderToTextureUpdate2d,\n\n\t\"\"\n};\n\n#endif\n\n"
  },
  {
    "path": "engine/gl/gl_viddroid.c",
    "content": "/*\nCopyright (C) 2006-2007 Mark Olsen\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  \n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n*/\n\n#include \"quakedef.h\"\n#include \"glquake.h\"\n\nextern float sys_dpi_x;\nextern float sys_dpi_y;\nextern cvar_t vid_vsync;\n\nstatic dllhandle_t *sys_gl_module = NULL;\nstatic rendererinfo_t gles1rendererinfo;\n\nstatic void *GLES_GetSymbol(char *symname)\n{\n\t//Can't use android's eglGetProcAddress\n\t//1) it gives less efficient stubs\n\t//2) it has a limited number of such stubs, and the limit is too low for core functions too\n\tvoid *ret;\n\n\tret = Sys_GetAddressForName(sys_gl_module, symname);\n\n\tif (!ret)\n\t\tSys_Warn(\"GLES_GetSymbol: couldn't find %s\\n\", symname);\n\treturn ret;\n}\n\n\n#include <EGL/egl.h>\n#include <android/native_window.h>\n#include <jni.h>\n#include <android/native_window_jni.h>\n\n/*android is a real fucking pain*/\n\n\nstatic EGLDisplay sys_display;\nstatic EGLSurface sys_surface;\nstatic EGLContext sys_context;\nANativeWindow *sys_nativewindow;\n\nvoid GLVID_DeInit(void)\n{\n\tif (sys_display)\n\t{\n\t\teglMakeCurrent(sys_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);\n\t\tif (sys_context)\n\t\t\teglDestroyContext(sys_display, sys_context);\n\n\t\tif (sys_surface != EGL_NO_SURFACE)\n\t\t\teglDestroySurface(sys_display, sys_surface);\n\n\t\teglTerminate(sys_display);\n\t}\n\tsys_display = EGL_NO_DISPLAY;\n\tsys_context = EGL_NO_CONTEXT;\n\tsys_surface = EGL_NO_SURFACE;\n\n\tGL_ForgetPointers();\n\nSys_Printf(\"GLVID_DeInited\\n\");\n}\n/*static void EGL_ShowConfig(EGLDisplay egldpy, EGLConfig cfg)\n{\n\tstruct\n\t{\n\t\tEGLint attr;\n\t\tconst char *attrname;\n\t} eglattrs[] = \n\t{\n\t\t{EGL_ALPHA_SIZE, \"EGL_ALPHA_SIZE\"},\n\t\t{EGL_ALPHA_MASK_SIZE, \"EGL_ALPHA_MASK_SIZE\"},\n\t\t{EGL_BIND_TO_TEXTURE_RGB, \"EGL_BIND_TO_TEXTURE_RGB\"},\n\t\t{EGL_BIND_TO_TEXTURE_RGBA, \"EGL_BIND_TO_TEXTURE_RGBA\"},\n\t\t{EGL_BLUE_SIZE, \"EGL_BLUE_SIZE\"},\n\t\t{EGL_BUFFER_SIZE, \"EGL_BUFFER_SIZE\"},\n\t\t{EGL_COLOR_BUFFER_TYPE, \"EGL_COLOR_BUFFER_TYPE\"},\n\t\t{EGL_CONFIG_CAVEAT, \"EGL_CONFIG_CAVEAT\"},\n\t\t{EGL_CONFIG_ID, \"EGL_CONFIG_ID\"},\n\t\t{EGL_CONFORMANT, \"EGL_CONFORMANT\"},\n\t\t{EGL_DEPTH_SIZE, \"EGL_DEPTH_SIZE\"},\n\t\t{EGL_GREEN_SIZE, \"EGL_GREEN_SIZE\"},\n\t\t{EGL_LEVEL, \"EGL_LEVEL\"},\n\t\t{EGL_LUMINANCE_SIZE, \"EGL_LUMINANCE_SIZE\"},\n\t\t{EGL_MAX_PBUFFER_WIDTH, \"EGL_MAX_PBUFFER_WIDTH\"},\n\t\t{EGL_MAX_PBUFFER_HEIGHT, \"EGL_MAX_PBUFFER_HEIGHT\"},\n\t\t{EGL_MAX_PBUFFER_PIXELS, \"EGL_MAX_PBUFFER_PIXELS\"},\n\t\t{EGL_MAX_SWAP_INTERVAL, \"EGL_MAX_SWAP_INTERVAL\"},\n\t\t{EGL_MIN_SWAP_INTERVAL, \"EGL_MIN_SWAP_INTERVAL\"},\n\t\t{EGL_NATIVE_RENDERABLE, \"EGL_NATIVE_RENDERABLE\"},\n\t\t{EGL_NATIVE_VISUAL_ID, \"EGL_NATIVE_VISUAL_ID\"},\n\t\t{EGL_NATIVE_VISUAL_TYPE, \"EGL_NATIVE_VISUAL_TYPE\"},\n\t\t{EGL_RED_SIZE, \"EGL_RED_SIZE\"},\n\t\t{EGL_RENDERABLE_TYPE, \"EGL_RENDERABLE_TYPE\"},\n\t\t{EGL_SAMPLE_BUFFERS, \"EGL_SAMPLE_BUFFERS\"},\n\t\t{EGL_SAMPLES, \"EGL_SAMPLES\"},\n\t\t{EGL_STENCIL_SIZE, \"EGL_STENCIL_SIZE\"},\n\t\t{EGL_SURFACE_TYPE, \"EGL_SURFACE_TYPE\"},\n\t\t{EGL_TRANSPARENT_TYPE, \"EGL_TRANSPARENT_TYPE\"},\n\t\t{EGL_TRANSPARENT_RED_VALUE, \"EGL_TRANSPARENT_RED_VALUE\"},\n\t\t{EGL_TRANSPARENT_GREEN_VALUE, \"EGL_TRANSPARENT_GREEN_VALUE\"},\n\t\t{EGL_TRANSPARENT_BLUE_VALUE, \"EGL_TRANSPARENT_BLUE_VALUE\"},\n\t};\n\tsize_t i;\n\tEGLint val;\n\n\tfor (i = 0; i < countof(eglattrs); i++)\n\t{\n\t\tif (eglGetConfigAttrib(egldpy, cfg, eglattrs[i].attr, &val))\n\t\t\tSys_Printf(\"%i.%s: %i\\n\", (int)cfg, eglattrs[i].attrname, val);\n\t\telse\n\t\t\tSys_Printf(\"%i.%s: UNKNOWN\\n\", (int)cfg, eglattrs[i].attrname);\n\t}\n};\n*/\n\nqboolean GLVID_Init (rendererstate_t *info, unsigned char *palette)\n{\nSys_Printf(\"GLVID_Initing...\\n\");\n\tif (!sys_nativewindow)\n\t{\n\t\tSys_Printf(\"GLVID_Init failed: no window known yet\\n\");\n\t\treturn false;\t//not at this time...\n\t}\n\n//\tvid.pixelwidth = ANativeWindow_getWidth(sys_window);\n//\tvid.pixelheight = ANativeWindow_getHeight(sys_window);\n\n\tvid.numpages = 3;\n\n\tEGLint attribs[] = {\n\t\tEGL_RENDERABLE_TYPE, EGL_OPENGL_ES_BIT,\n\t\tEGL_SURFACE_TYPE, EGL_WINDOW_BIT,\n\t\tEGL_BLUE_SIZE, (info->bpp==16)?5:8,\n\t\tEGL_GREEN_SIZE, (info->bpp==16)?6:8,\n\t\tEGL_RED_SIZE, (info->bpp==16)?5:8,\n\t\tEGL_DEPTH_SIZE, 16,\n//\t\tEGL_STENCIL_SIZE, 8,\n\t\tEGL_NONE\n\t};\n\tEGLint w, h, format;\n\tEGLint numConfigs;\n\tEGLConfig config;\n\tint glesversion;\n\n\tsys_display = eglGetDisplay(EGL_DEFAULT_DISPLAY);\n\tif (sys_display == EGL_NO_DISPLAY)\n\t{\n\t\tGLVID_DeInit();\n\t\treturn false;\n\t}\n\tif (!eglInitialize(sys_display, NULL, NULL))\n\t{\n\t\tGLVID_DeInit();\n\t\treturn false;\n\t}\n#ifdef EGL_VERSION_1_5\n\tattribs[1] = EGL_OPENGL_ES3_BIT;\n\tif (info->renderer==&gles1rendererinfo || !eglChooseConfig(sys_display, attribs, &config, 1, &numConfigs))\n#endif\n\t{\n\t\tattribs[1] = EGL_OPENGL_ES2_BIT;\n\t\tif (info->renderer==&gles1rendererinfo || !eglChooseConfig(sys_display, attribs, &config, 1, &numConfigs))\n\t\t{\n\t\t\t//gles2 was added in egl1.3\n\t\t\tattribs[1] = EGL_OPENGL_ES_BIT;\n\t\t\tif (!eglChooseConfig(sys_display, attribs, &config, 1, &numConfigs))\n\t\t\t{\n\t\t\t\t//EGL_RENDERABLE_TYPE added in egl1.2\n\t\t\t\tif (!eglChooseConfig(sys_display, attribs+2, &config, 1, &numConfigs))\n\t\t\t\t{\n\t\t\t\t\tGLVID_DeInit();\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\n\teglGetConfigAttrib(sys_display, config, EGL_RENDERABLE_TYPE, &format);\n\tif (info->renderer==&gles1rendererinfo && (format & EGL_OPENGL_ES_BIT))\n\t\tglesversion = 1;\n#ifdef EGL_VERSION_1_5\n\telse if (format & EGL_OPENGL_ES3_BIT)\n\t\tglesversion = 3;\n#endif\n\telse if (format & EGL_OPENGL_ES2_BIT)\n\t\tglesversion = 2;\n\telse\n\t\tglesversion = 1;\n\n\tSys_Printf(\"Creating gles %i context\\n\", glesversion);\n\n//\tEGL_ShowConfig(sys_display, config);\n\t\n\tsys_surface = eglCreateWindowSurface(sys_display, config, sys_nativewindow, NULL);\n\tif (!sys_surface)\n\t\treturn false;\n\tEGLint ctxattribs[] = {EGL_CONTEXT_CLIENT_VERSION, glesversion, EGL_NONE};\n\tsys_context = eglCreateContext(sys_display, config, NULL, ctxattribs);\n\tif (!sys_context)\n\t\treturn false;\n\n\n\tif (eglMakeCurrent(sys_display, sys_surface, sys_surface, sys_context) == EGL_FALSE)\n\t\treturn false;\n\n\teglQuerySurface(sys_display, sys_surface, EGL_WIDTH, &w);\n\teglQuerySurface(sys_display, sys_surface, EGL_HEIGHT, &h);\n\tvid.pixelwidth = w;\n\tvid.pixelheight = h;\n\n\t/*now that the context is created, load the dll so that we don't have to crash from eglGetProcAddress issues*/\t\n\tunsigned int gl_major_version = 0;\n\tconst char *(*eglGetString)(GLenum) = (void*)eglGetProcAddress(\"glGetString\");\n\tconst char *s = eglGetString(GL_VERSION);\n\twhile (*s && (*s < '0' || *s > '9'))\n\t\ts++;\n\tgl_major_version = atoi(s);\n\tconst char *driver;\n\tif ((glesversion<=1) != (gl_major_version<=1))\n\t{\n\t\tCon_Printf(CON_ERROR \"Requested gles %i context, but got incompatible %i instead.\\n\", glesversion, gl_major_version);\n\t\tGLVID_DeInit();\n\t\treturn false;\n\t}\n\tif (gl_major_version>=3)\n\t\tdriver = \"libGLESv3.so\";\n\telse if (gl_major_version>=2)\n\t\tdriver = \"libGLESv2.so\";\n\telse\n\t\tdriver = \"libGLESv1_CM.so\";\n\tSys_Printf(\"Loading %s\\n\", driver);\n\tsys_gl_module = Sys_LoadLibrary(driver, NULL);\n\tif (!sys_gl_module)\n\t{\n\t\tGLVID_DeInit();\n\t\treturn false;\n\t}\n\n\tif (!GL_Init(info, GLES_GetSymbol))\n\t\treturn false;\n\tSys_Printf(\"GLVID_Inited...\\n\");\n\tvid_vsync.modified = true;\n\treturn true;\n}\n\nvoid GLVID_SwapBuffers(void)\n{\n\tif (vid_vsync.modified)\n\t{\n\t\tint interval;\n\t\tvid_vsync.modified = false;\n\t\tif (*vid_vsync.string)\n\t\t\tinterval = vid_vsync.ival;\n\t\telse\n\t\t\tinterval = 1;\t//default is to always vsync, according to EGL docs, so lets just do that.\n\t\teglSwapInterval(sys_display, interval);\n\t\tSys_Printf(\"Swap interval changed\\n\");\n\t}\n\n\teglSwapBuffers(sys_display, sys_surface);\n\tTRACE((\"Swap Buffers\\n\"));\n\n\tEGLint w, h;\n\teglQuerySurface(sys_display, sys_surface, EGL_WIDTH, &w);\n\teglQuerySurface(sys_display, sys_surface, EGL_HEIGHT, &h);\n\tif (w != vid.pixelwidth || h != vid.pixelheight)\n\t{\n\t\tvid.pixelwidth = w;\n\t\tvid.pixelheight = h;\n\t\textern cvar_t vid_conautoscale;\n\t\tCvar_ForceCallback(&vid_conautoscale);\n\t\tSys_Printf(\"Video Resized\\n\");\n\t}\n}\n\nqboolean GLVID_ApplyGammaRamps (unsigned int gammarampsize, unsigned short *ramps)\n{\n\treturn false;\n}\n\nvoid GLVID_SetCaption(const const char *caption)\n{\n\t// :(\n}\n\nstatic int GLES1VID_GetPriority(void)\n{\t//assumed 1 when not defined.\n\treturn 1;\t//gles1 sucks, and lacks lots of features.\n}\nstatic int GLES2VID_GetPriority(void)\n{   //assumed 1 when not defined.\n\treturn 2;\t//urgh, betterer than gles1 at least.\n}\nvoid VID_Register(void)\n{\n\t//many android devices have drivers for both gles1 AND gles2.\n\t//we default to gles2 because its more capable, but some people might want to try using gles1. so register a renderer for that.\n\t//the init code explicitly checks for our gles1rendererinfo and tries to create a gles1 context instead.\n\textern rendererinfo_t openglrendererinfo;\n\tgles1rendererinfo = openglrendererinfo;\n\tgles1rendererinfo.description = \"OpenGL ES 1\";\n\tmemset(&gles1rendererinfo.name, 0, sizeof(gles1rendererinfo.name));\t//make sure there's no 'gl' etc names.\n\tgles1rendererinfo.name[0] = \"gles1\";\n\tgles1rendererinfo.VID_GetPriority = GLES1VID_GetPriority;\n\topenglrendererinfo.VID_GetPriority = GLES2VID_GetPriority;\n\tR_RegisterRenderer(NULL, &gles1rendererinfo);\n}\n\n\n#ifdef VKQUAKE\n#include \"../vk/vkrenderer.h\"\nstatic qboolean VKVID_CreateSurface(void)\n{\n\tVkResult err;\n\tVkAndroidSurfaceCreateInfoKHR createInfo = {VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR};\n\tcreateInfo.flags = 0;\n\tcreateInfo.window = sys_nativewindow;\n\terr = vkCreateAndroidSurfaceKHR(vk.instance, &createInfo, NULL, &vk.surface);\n\tswitch(err)\n\t{\n\tdefault:\n\t\tCon_Printf(\"Unknown vulkan device creation error: %x\\n\", err);\n\t\treturn false;\n\tcase VK_SUCCESS:\n\t\tbreak;\n\t}\n\treturn true;\n}\n\nstatic qboolean VKVID_Init (rendererstate_t *info, unsigned char *palette)\n{\n\t//this is simpler than most platforms, as the window itself is handled by java code, and we can't create/destroy it here\n\t//(android surfaces can be resized/resampled separately from their window, and are always 'fullscreen' anyway, so this isn't actually an issue for once)\n\tconst char *extnames[] = {VK_KHR_ANDROID_SURFACE_EXTENSION_NAME, NULL};\n\tSys_Printf(\"initialising vulkan...\\n\");\n\tif (!sys_nativewindow)\n\t{\n\t\tSys_Printf(\"VKVID_Init failed: no window known yet\\n\");\n\t\treturn false;\n\t}\n#ifdef VK_NO_PROTOTYPES\n\tdllhandle_t *hInstVulkan = NULL;\n\tif (!hInstVulkan)\n\t\thInstVulkan = *info->subrenderer?Sys_LoadLibrary(info->subrenderer, NULL):NULL;\n\tif (!hInstVulkan)\n\t\thInstVulkan = Sys_LoadLibrary(\"libvulkan.so\", NULL);\n\tif (!hInstVulkan)\n\t{\n\t\tCon_Printf(\"Unable to load libvulkan.so\\nNo Vulkan drivers are installed\\n\");\n\t\treturn false;\n\t}\n\tvkGetInstanceProcAddr = (PFN_vkGetInstanceProcAddr) Sys_GetAddressForName(hInstVulkan, \"vkGetInstanceProcAddr\");\n#endif\n\n\treturn VK_Init(info, extnames, VKVID_CreateSurface, NULL);\n}\nvoid VKVID_DeInit(void)\n{\n\tVK_Shutdown();\n\t//that's all folks.\n}\nstatic void VKVID_SwapBuffers(void)\n{\n\t// :(\n}\n\nstatic int VKVID_GetPriority(void)\n{\t//assumed 1 when not defined.\n\treturn 3;\t//make it a higher priority than our opengles implementation, because of the infamous black-screens bug that has been plaguing us for years.\n}\nrendererinfo_t vkrendererinfo =\n{\n\t\"Vulkan\",\n\t{\n\t\t\"vk\",\n\t\t\"Vulkan\"\n\t},\n\tQR_VULKAN,\n\n\tVK_Draw_Init,\n\tVK_Draw_Shutdown,\n\n\tVK_UpdateFiltering,\n\tVK_LoadTextureMips,\n\tVK_DestroyTexture,\n\n\tVK_R_Init,\n\tVK_R_DeInit,\n\tVK_R_RenderView,\n\n\tVKVID_Init,\n\tVKVID_DeInit,\n\tVKVID_SwapBuffers,\n\tGLVID_ApplyGammaRamps,\n\tNULL,//_CreateCursor,\n\tNULL,//_SetCursor,\n\tNULL,//_DestroyCursor,\n\tGLVID_SetCaption,\n\tVKVID_GetRGBInfo,\n\n\tVK_SCR_UpdateScreen,\n\n\tVKBE_SelectMode,\n\tVKBE_DrawMesh_List,\n\tVKBE_DrawMesh_Single,\n\tVKBE_SubmitBatch,\n\tVKBE_GetTempBatch,\n\tVKBE_DrawWorld,\n\tVKBE_Init,\n\tVKBE_GenBrushModelVBO,\n\tVKBE_ClearVBO,\n\tVKBE_UploadAllLightmaps,\n\tVKBE_SelectEntity,\n\tVKBE_SelectDLight,\n\tVKBE_Scissor,\n\tVKBE_LightCullModel,\n\n\tVKBE_VBO_Begin,\n\tVKBE_VBO_Data,\n\tVKBE_VBO_Finish,\n\tVKBE_VBO_Destroy,\n\n\tVKBE_RenderToTextureUpdate2d,\n\n\t\"no more\",\n\tVKVID_GetPriority,\n};\n#endif\n"
  },
  {
    "path": "engine/gl/gl_videgl.c",
    "content": "#include \"quakedef.h\"\n#if defined(GLQUAKE) && defined(USE_EGL)\n#include \"gl_videgl.h\"\n#include \"vr.h\"\n\n//EGL_KHR_gl_colorspace\n#ifndef EGL_GL_COLORSPACE_KHR\n#define EGL_GL_COLORSPACE_KHR 0x309D\n#endif\n#ifndef EGL_GL_COLORSPACE_SRGB_KHR\n#define EGL_GL_COLORSPACE_SRGB_KHR 0x3089\n#endif\n#ifndef EGL_GL_COLORSPACE_LINEAR_KHR\n#define EGL_GL_COLORSPACE_LINEAR_KHR 0x308A\n#endif\n\nextern cvar_t vid_vsync;\n\nEGLContext eglctx = EGL_NO_CONTEXT;\nEGLDisplay egldpy = EGL_NO_DISPLAY;\nEGLSurface eglsurf = EGL_NO_SURFACE;\nstatic const char *eglexts;\n\nstatic dllhandle_t *egllibrary;\nstatic dllhandle_t *eslibrary;\n\nstatic EGLint\t\t(EGLAPIENTRY *qeglGetError)(void);\n\nstatic EGLDisplay\t(EGLAPIENTRY *qeglGetPlatformDisplay)(EGLenum platform, void *native_display, const EGLAttrib *attrib_list);\nstatic EGLDisplay\t(EGLAPIENTRY *qeglGetDisplay)(EGLNativeDisplayType display_id);\nstatic EGLBoolean\t(EGLAPIENTRY *qeglInitialize)(EGLDisplay dpy, EGLint *major, EGLint *minor);\nstatic const char *\t(EGLAPIENTRY *qeglQueryString)(EGLDisplay dpy, EGLint name);\nstatic EGLBoolean\t(EGLAPIENTRY *qeglTerminate)(EGLDisplay dpy);\n\nstatic EGLBoolean\t(EGLAPIENTRY *qeglGetConfigs)(EGLDisplay dpy, EGLConfig *configs, EGLint config_size, EGLint *num_config);\nstatic EGLBoolean\t(EGLAPIENTRY *qeglChooseConfig)(EGLDisplay dpy, const EGLint *attrib_list, EGLConfig *configs, EGLint config_size, EGLint *num_config);\nEGLBoolean\t(EGLAPIENTRY *qeglGetConfigAttrib)(EGLDisplay dpy, EGLConfig config, EGLint attribute, EGLint *value);\nstatic EGLBoolean\t(EGLAPIENTRY *qeglBindAPI) (EGLenum api);\n\nstatic EGLSurface\t(EGLAPIENTRY *qeglCreatePlatformWindowSurface)(EGLDisplay dpy, EGLConfig config, void *native_window, const EGLAttrib *attrib_list);\nstatic EGLSurface\t(EGLAPIENTRY *qeglCreateWindowSurface)(EGLDisplay dpy, EGLConfig config, EGLNativeWindowType win, const EGLint *attrib_list);\nstatic EGLSurface\t(EGLAPIENTRY *qeglCreatePbufferSurface) (EGLDisplay dpy, EGLConfig config, const EGLint *attrib_list);\nstatic EGLBoolean\t(EGLAPIENTRY *qeglDestroySurface)(EGLDisplay dpy, EGLSurface surface);\nstatic EGLBoolean\t(EGLAPIENTRY *qeglQuerySurface)(EGLDisplay dpy, EGLSurface surface, EGLint attribute, EGLint *value);\n\nstatic EGLBoolean\t(EGLAPIENTRY *qeglSwapBuffers)(EGLDisplay dpy, EGLSurface surface);\nstatic EGLBoolean\t(EGLAPIENTRY *qeglMakeCurrent)(EGLDisplay dpy, EGLSurface draw, EGLSurface read, EGLContext ctx);\nstatic EGLContext\t(EGLAPIENTRY *qeglCreateContext)(EGLDisplay dpy, EGLConfig config, EGLContext share_context, const EGLint *attrib_list);\nstatic EGLBoolean\t(EGLAPIENTRY *qeglDestroyContext)(EGLDisplay dpy, EGLContext ctx);\nstatic void *\t\t(EGLAPIENTRY *qeglGetProcAddress) (const char *name);\n\nstatic EGLBoolean \t(EGLAPIENTRY *qeglSwapInterval) (EGLDisplay display, EGLint interval);\n\nstatic dllfunction_t qeglfuncs[] =\n{\n\t{(void*)&qeglGetError, \"eglGetError\"},\n\t\n\t{(void*)&qeglGetDisplay, \"eglGetDisplay\"},\n\t{(void*)&qeglInitialize, \"eglInitialize\"},\n\t{(void*)&qeglQueryString, \"eglQueryString\"},\n\t{(void*)&qeglTerminate, \"eglTerminate\"},\n\n\t{(void*)&qeglGetConfigs, \"eglGetConfigs\"},\n\t{(void*)&qeglChooseConfig, \"eglChooseConfig\"},\n\t{(void*)&qeglGetConfigAttrib, \"eglGetConfigAttrib\"},\n\n\t{(void*)&qeglCreatePbufferSurface, \"eglCreatePbufferSurface\"},\n\t{(void*)&qeglCreateWindowSurface, \"eglCreateWindowSurface\"},\n\t{(void*)&qeglDestroySurface, \"eglDestroySurface\"},\n\t{(void*)&qeglQuerySurface, \"eglQuerySurface\"},\n\n\t{(void*)&qeglSwapBuffers, \"eglSwapBuffers\"},\n\t{(void*)&qeglMakeCurrent, \"eglMakeCurrent\"},\n\t{(void*)&qeglCreateContext, \"eglCreateContext\"},\n\t{(void*)&qeglDestroyContext, \"eglDestroyContext\"},\n\n\t{(void*)&qeglGetProcAddress, \"eglGetProcAddress\"},\n\n\t//EGL 1.1\n\t{(void*)&qeglSwapInterval,\t\"eglSwapInterval\"},\n\n\t{NULL}\n};\n\n\nvoid *EGL_Proc(char *f)\n{\n\tvoid *proc = NULL;\n\n\t/*\n\tchar fname[512];\n\t{\n\t\tsprintf(fname, \"wrap_%s\", f);\n\t\tf = fname;\n\t}\n\t*/\n\n\tif (!proc)\n\t\tproc = Sys_GetAddressForName(eslibrary, f);\n\tif (!proc)\n\t\tproc = Sys_GetAddressForName(egllibrary, f);\n\n\t//eglGetProcAddress functions must work regardless of context...\n\t//FIXME: this means lots of false-positives.\n\t//as well as lots of thunks, which will result in slowdowns.\n\tif (qeglGetProcAddress)\n\t\tproc = qeglGetProcAddress(f);\n\n\treturn proc;\n}\n\nstatic const char *EGL_GetErrorString(int error)\n{\n\tswitch(error)\n\t{\n\tcase EGL_BAD_ACCESS:\t\t\treturn \"BAD_ACCESS\";\n\tcase EGL_BAD_ALLOC:\t\t\t\treturn \"BAD_ALLOC\";\n\tcase EGL_BAD_ATTRIBUTE:\t\t\treturn \"BAD_ATTRIBUTE\";\n\tcase EGL_BAD_CONFIG:\t\t\treturn \"BAD_CONFIG\";\n\tcase EGL_BAD_CONTEXT:\t\t\treturn \"BAD_CONEXT\";\n\tcase EGL_BAD_CURRENT_SURFACE:\treturn \"BAD_CURRENT_SURFACE\";\n\tcase EGL_BAD_DISPLAY:\t\t\treturn \"BAD_DISPLAY\";\n\tcase EGL_BAD_MATCH:\t\t\t\treturn \"BAD_MATCH\";\n\tcase EGL_BAD_NATIVE_PIXMAP:\t\treturn \"BAD_NATIVE_PIXMAP\";\n\tcase EGL_BAD_NATIVE_WINDOW:\t\treturn \"BAD_NATIVE_WINDOW\";\n\tcase EGL_BAD_PARAMETER:\t\t\treturn \"BAD_PARAMETER\";\n\tcase EGL_BAD_SURFACE:\t\t\treturn \"BAD_SURFACE\";\n\tdefault:\t\t\t\t\t\treturn va(\"EGL:%#x\", error);\n\t}\n}\n\nstatic qboolean EGL_CheckExtension(const char *extname)\n{\n\tconst char *x = eglexts, *n;\n\tsize_t l;\n\tif (!x)\n\t\treturn false;\n\tl = strlen(extname);\n\tfor(;;)\n\t{\n\t\tn = strchr(x, ' ');\n\t\tif (!n)\n\t\t{\n\t\t\tif (!strcmp(x, extname))\n\t\t\t\treturn true;\n\t\t\treturn false;\n\t\t}\n\t\telse if (n-x==l && !strncmp(x, extname, l))\n\t\t\treturn true;\n\t\tx = n+1;\n\t}\n}\n\nvoid EGL_UnloadLibrary(void)\n{\n\tif (egllibrary)\n\t\tSys_CloseLibrary(egllibrary);\n\tif (egllibrary == eslibrary)\n\t\teslibrary = NULL;\n\tif (eslibrary)\n\t\tSys_CloseLibrary(eslibrary);\n\teslibrary = egllibrary = NULL;\n}\n\nqboolean EGL_LoadLibrary(char *driver)\n{\n\t/*\tlinux seem to load glesv2 first for dependency issues.\n\t\t(most things are expected to statically link to their libs)\n\t\tstrictly speaking, EGL says that functions should work regardless of context.\n\t\t(which of course makes portability a nightmare, especially on windows where static linking is basically impossible)\n\t\t(android's EGL bugs out if you use eglGetProcAddress for core functions too, note that EGL_KHR_get_all_proc_addresses fixes that.)\n\t*/\n\tSys_Printf(\"Attempting to dlopen libGLESv2... \");\n\teslibrary = Sys_LoadLibrary(\"libGLESv2\", NULL);\n\tif (!eslibrary)\n\t{\n\t\tSys_Printf(\"failed\\n\");\n//\t\treturn false;\n\t}\n\telse\n\t\tSys_Printf(\"success\\n\");\n#ifndef _WIN32\n\tif (!eslibrary)\n\t{\n\t\teslibrary = dlopen(\"libGL\"ARCH_DL_POSTFIX\".1.2\", RTLD_NOW|RTLD_GLOBAL);\n\t\tif (eslibrary) Sys_Printf(\"Loaded libGL.so.1.2\\n\");\n\t}\n\tif (!eslibrary)\n\t{\n\t\teslibrary = dlopen(\"libGL\"ARCH_DL_POSTFIX\".1\", RTLD_NOW|RTLD_GLOBAL);\n\t\tif (eslibrary) Sys_Printf(\"Loaded libGL.so.1\\n\");\n\t}\n\tif (!eslibrary)\n\t{\n\t\teslibrary = dlopen(\"libGL\", RTLD_NOW|RTLD_GLOBAL);\n\t\tif (eslibrary) Sys_Printf(\"Loaded libGL\\n\");\n\t}\n#endif\n\tif (!eslibrary)\n\t\tSys_Printf(\"unable to load some libGL\\n\");\n\n\tSys_Printf(\"Attempting to dlopen libEGL... \");\n\tegllibrary = Sys_LoadLibrary(\"libEGL\", qeglfuncs);\n\tif (!egllibrary)\n\t{\n\t\tSys_Printf(\"failed\\n\");\n\t\tCon_Printf(\"libEGL library not loadable\\n\");\n\t\t/* TODO: some implementations combine EGL/GLESv2 into single library... */\n\t\tSys_CloseLibrary(eslibrary);\n\t\treturn false;\n\t}\n\tSys_Printf(\"success\\n\");\n\n\n\t//egl1.2\n\tqeglBindAPI = EGL_Proc(\"eglBindAPI\");\n\n\t//these are from egl1.5\n\tqeglGetPlatformDisplay\t\t= EGL_Proc(\"eglGetPlatformDisplay\");\n\tqeglCreatePlatformWindowSurface\t= EGL_Proc(\"eglCreatePlatformWindowSurface\");\n\t//and in case they arn't defined...\n\tif (!qeglGetPlatformDisplay)\t\tqeglGetPlatformDisplay\t\t= EGL_Proc(\"eglGetPlatformDisplayEXT\");\n\tif (!qeglCreatePlatformWindowSurface)\tqeglCreatePlatformWindowSurface\t= EGL_Proc(\"eglCreatePlatformWindowSurfaceEXT\");\n\n\treturn true;\n}\n\nvoid EGL_Shutdown(void)\n{\n\tif (eglctx == EGL_NO_CONTEXT)\n\t\treturn;\n\n\tqeglMakeCurrent(EGL_NO_DISPLAY, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);\n\tqeglDestroyContext(egldpy, eglctx);\n\n\tif (eglsurf != EGL_NO_SURFACE)\n\t\tqeglDestroySurface(egldpy, eglsurf);\n\n\tqeglTerminate(egldpy);\n\n\teglctx = EGL_NO_CONTEXT;\n\tegldpy = EGL_NO_DISPLAY;\n\teglsurf = EGL_NO_SURFACE;\n\teglexts = NULL;\n}\n\n/*static void EGL_ShowConfig(EGLDisplay egldpy, EGLConfig cfg)\n{\n\tstruct\n\t{\n\t\tEGLint attr;\n\t\tconst char *attrname;\n\t} eglattrs[] = \n\t{\n\t\t{EGL_ALPHA_SIZE, \"EGL_ALPHA_SIZE\"},\n\t\t{EGL_ALPHA_MASK_SIZE, \"EGL_ALPHA_MASK_SIZE\"},\n\t\t{EGL_BIND_TO_TEXTURE_RGB, \"EGL_BIND_TO_TEXTURE_RGB\"},\n\t\t{EGL_BIND_TO_TEXTURE_RGBA, \"EGL_BIND_TO_TEXTURE_RGBA\"},\n\t\t{EGL_BLUE_SIZE, \"EGL_BLUE_SIZE\"},\n\t\t{EGL_BUFFER_SIZE, \"EGL_BUFFER_SIZE\"},\n\t\t{EGL_COLOR_BUFFER_TYPE, \"EGL_COLOR_BUFFER_TYPE\"},\n\t\t{EGL_CONFIG_CAVEAT, \"EGL_CONFIG_CAVEAT\"},\n\t\t{EGL_CONFIG_ID, \"EGL_CONFIG_ID\"},\n\t\t{EGL_CONFORMANT, \"EGL_CONFORMANT\"},\n\t\t{EGL_DEPTH_SIZE, \"EGL_DEPTH_SIZE\"},\n\t\t{EGL_GREEN_SIZE, \"EGL_GREEN_SIZE\"},\n\t\t{EGL_LEVEL, \"EGL_LEVEL\"},\n\t\t{EGL_LUMINANCE_SIZE, \"EGL_LUMINANCE_SIZE\"},\n\t\t{EGL_MAX_PBUFFER_WIDTH, \"EGL_MAX_PBUFFER_WIDTH\"},\n\t\t{EGL_MAX_PBUFFER_HEIGHT, \"EGL_MAX_PBUFFER_HEIGHT\"},\n\t\t{EGL_MAX_PBUFFER_PIXELS, \"EGL_MAX_PBUFFER_PIXELS\"},\n\t\t{EGL_MAX_SWAP_INTERVAL, \"EGL_MAX_SWAP_INTERVAL\"},\n\t\t{EGL_MIN_SWAP_INTERVAL, \"EGL_MIN_SWAP_INTERVAL\"},\n\t\t{EGL_NATIVE_RENDERABLE, \"EGL_NATIVE_RENDERABLE\"},\n\t\t{EGL_NATIVE_VISUAL_ID, \"EGL_NATIVE_VISUAL_ID\"},\n\t\t{EGL_NATIVE_VISUAL_TYPE, \"EGL_NATIVE_VISUAL_TYPE\"},\n\t\t{EGL_RED_SIZE, \"EGL_RED_SIZE\"},\n\t\t{EGL_RENDERABLE_TYPE, \"EGL_RENDERABLE_TYPE\"},\n\t\t{EGL_SAMPLE_BUFFERS, \"EGL_SAMPLE_BUFFERS\"},\n\t\t{EGL_SAMPLES, \"EGL_SAMPLES\"},\n\t\t{EGL_STENCIL_SIZE, \"EGL_STENCIL_SIZE\"},\n\t\t{EGL_SURFACE_TYPE, \"EGL_SURFACE_TYPE\"},\n\t\t{EGL_TRANSPARENT_TYPE, \"EGL_TRANSPARENT_TYPE\"},\n\t\t{EGL_TRANSPARENT_RED_VALUE, \"EGL_TRANSPARENT_RED_VALUE\"},\n\t\t{EGL_TRANSPARENT_GREEN_VALUE, \"EGL_TRANSPARENT_GREEN_VALUE\"},\n\t\t{EGL_TRANSPARENT_BLUE_VALUE, \"EGL_TRANSPARENT_BLUE_VALUE\"},\n\t};\n\tsize_t i;\n\tEGLint val;\n\n\tfor (i = 0; i < countof(eglattrs); i++)\n\t{\n\t\tif (qeglGetConfigAttrib(egldpy, cfg, eglattrs[i].attr, &val))\n\t\t\tCon_DPrintf(\"%p.%s: %i\\n\", cfg, eglattrs[i].attrname, val);\n\t\telse\n\t\t\tCon_DPrintf(\"%p.%s: UNKNOWN\\n\", cfg, eglattrs[i].attrname);\n\t}\n}*/\n\nstatic void EGL_UpdateSwapInterval(void)\n{\n\tint interval;\n\tvid_vsync.modified = false;\n\tif (*vid_vsync.string)\n\t\tinterval = vid_vsync.ival;\n\telse\n\t\tinterval = 1;\t//default is to always vsync, according to EGL docs, so lets just do that.\n\n\tif (qeglSwapInterval)\n\t\tqeglSwapInterval(egldpy, interval);\n}\n\nvoid EGL_SwapBuffers (void)\n{\n\tif (vid_vsync.modified)\n\t\tEGL_UpdateSwapInterval();\n\n\tTRACE((\"EGL_SwapBuffers\\n\"));\n\tTRACE((\"swapping buffers\\n\"));\n\tqeglSwapBuffers(egldpy, eglsurf);\n\t/* TODO: check result? */\n\tTRACE((\"EGL_SwapBuffers done\\n\"));\n}\n\n\n\nqboolean EGL_InitDisplay (rendererstate_t *info, int eglplat, void *ndpy, EGLNativeDisplayType dpyid, EGLConfig *outconfig)\n{\n\tEGLint numconfig=0;\n\tEGLConfig cfg=0;\n\tEGLint major=0, minor=0;\n\tEGLint attrib[] =\n\t{\n\t\tEGL_SURFACE_TYPE, (eglplat==EGL_PLATFORM_DEVICE_EXT)?EGL_PBUFFER_BIT:EGL_WINDOW_BIT,\n//\t\tEGL_BUFFER_SIZE, info->bpp,\n//\t\tEGL_SAMPLES, info->multisample,\n//\t\tEGL_STENCIL_SIZE, 8,\n//\t\tEGL_ALPHA_MASK_SIZE, 0,\n\t\tEGL_DEPTH_SIZE, info->depthbits?info->depthbits:16,\n\t\tEGL_RED_SIZE, 4,\n\t\tEGL_GREEN_SIZE, 4,\n\t\tEGL_BLUE_SIZE, 4,\n\t\tEGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,\n\t\tEGL_NONE\n\t};\n\n/*\tif (!EGL_LoadLibrary(\"\"))\n\t{\n\t\tCon_Printf(CON_ERROR \"EGL: unable to load library!\\n\");\n\t\treturn false;\n\t}\n*/\n\n\tif (qeglGetPlatformDisplay && eglplat)\n\t\tegldpy = qeglGetPlatformDisplay(eglplat, ndpy, NULL/*attribs*/);\n\telse\n\t{\n\t\tif (eglplat == EGL_PLATFORM_WAYLAND_KHR)\n\t\t\tCon_Printf(CON_ERROR \"EGL: eglGetPlatformDisplay[EXT] not supported. Your EGL implementation is probably too old.\\n\");\n\t\tegldpy = qeglGetDisplay(dpyid);\n\t}\n\tif (egldpy == EGL_NO_DISPLAY)\n\t{\n\t\tCon_Printf(CON_WARNING \"EGL: creating default display\\n\");\n\t\tegldpy = qeglGetDisplay(EGL_DEFAULT_DISPLAY);\n\t}\n\tif (egldpy == EGL_NO_DISPLAY)\n\t{\n\t\tCon_Printf(CON_ERROR \"EGL: can't get display!\\n\");\n\t\treturn false;\n\t}\n\n\t//NOTE: mesa's egl really loves to crash on this call, and I define crash as 'anything that fails to return to caller', which fucks everything up.\n\tif (!qeglInitialize(egldpy, &major, &minor))\n\t{\n\t\tCon_Printf(CON_ERROR \"EGL: can't initialize display!\\n\");\n\t\treturn false;\n\t}\n\n\teglexts = qeglQueryString(egldpy, EGL_EXTENSIONS);\n\n/*\n\tif (!qeglGetConfigs(egldpy, NULL, 0, &numconfigs) || !numconfigs)\n\t{\n\t\tCon_Printf(CON_ERROR \"EGL: can't get configs!\\n\");\n\t\treturn false;\n\t}\n*/\n\n\tif (!qeglChooseConfig(egldpy, attrib, &cfg, 1, &numconfig))\n\t{\n\t\tCon_Printf(CON_ERROR \"EGL: can't choose config!\\n\");\n\t\treturn false;\n\t}\n\tif (!numconfig)\n\t{\n\t\tCon_Printf(CON_ERROR \"EGL: no configs!\\n\");\n\t\treturn false;\n\t}\n\n//\tEGL_ShowConfig(egldpy, cfg);\n\t*outconfig = cfg;\n\treturn true;\n}\n\nqboolean EGL_InitWindow (rendererstate_t *info, int eglplat, void *nwindow, EGLNativeWindowType windowid, EGLConfig cfg)\n{\n\tEGLint renderabletype;\n\n\tif (eglplat == EGL_PLATFORM_DEVICE_EXT && qeglCreatePbufferSurface)\n\t{\n\t\tEGLint wndattrib[] =\n\t\t{\n\t\t\tEGL_WIDTH, vid.pixelwidth,\n\t\t\tEGL_HEIGHT, vid.pixelheight,\n//\t\t\tEGL_GL_COLORSPACE_KHR, EGL_GL_COLORSPACE_SRGB_KHR,\n\t\t\tEGL_NONE,EGL_NONE\n\t\t};\n\t\teglsurf = qeglCreatePbufferSurface(egldpy, cfg, wndattrib);\n\t}\n\telse if (qeglCreatePlatformWindowSurface)\n\t{\n\t\tEGLAttrib wndattrib[3*2];\n\t\tsize_t i = 0;\n\n\t\tif (info->srgb)\n\t\t{\n\t\t\twndattrib[i++] = EGL_GL_COLORSPACE_KHR;\n\t\t\twndattrib[i++] = EGL_GL_COLORSPACE_SRGB_KHR;\n\t\t}\n\t\tif (EGL_CheckExtension(\"EGL_EXT_present_opaque\"))\n\t\t{\t//try to avoid nasty surprises.\n\t\t\t#ifndef EGL_PRESENT_OPAQUE_EXT\n\t\t\t\t#define EGL_PRESENT_OPAQUE_EXT                  0x31DF\n\t\t\t#endif\n\t\t\twndattrib[i++] = EGL_PRESENT_OPAQUE_EXT;\n\t\t\twndattrib[i++] = EGL_TRUE;\n\t\t}\n\t\twndattrib[i++] = EGL_NONE;\n\t\twndattrib[i++] = EGL_NONE;\n\t\teglsurf = qeglCreatePlatformWindowSurface(egldpy, cfg, nwindow, wndattrib);\n\t}\n\telse\n\t{\n\t\tEGLint wndattrib[] =\n\t\t{\n//\t\t\tEGL_GL_COLORSPACE_KHR, EGL_GL_COLORSPACE_SRGB_KHR,\n\n\t\t\tEGL_NONE,EGL_NONE\n\t\t};\n\t\teglsurf = qeglCreateWindowSurface(egldpy, cfg, windowid, info->srgb?wndattrib:NULL);\n\t}\n\tif (eglsurf == EGL_NO_SURFACE)\n\t{\n\t\tint err = qeglGetError();\n\t\tif (eglplat == EGL_PLATFORM_WAYLAND_KHR && err == EGL_BAD_DISPLAY)\t//slightly more friendly error that slags off nvidia for their refusal to implement existing standards, as is apparently appropriate.\n\t\t\tCon_Printf(CON_ERROR \"EGL: eglCreateWindowSurface failed: Bad Display. Your wayland setup is probably not properly supported by your video drivers.\\n\");\n\t\telse\n\t\t\tCon_Printf(CON_ERROR \"EGL: eglCreateWindowSurface failed: %s\\n\", EGL_GetErrorString(err));\n\t\treturn false;\n\t}\n\n\tqeglGetConfigAttrib(egldpy, cfg, EGL_RENDERABLE_TYPE, &renderabletype);\n\tif ((renderabletype & EGL_OPENGL_BIT) && qeglBindAPI)\n\t{\n\t\tEGLint contextattr[] =\n\t\t{\n//\t\t\tEGL_CONTEXT_OPENGL_DEBUG, 1,\n//\t\t\tEGL_CONTEXT_OPENGL_PROFILE_MASK,  EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT,\n//\t\t\tEGL_CONTEXT_CLIENT_VERSION, 2,\t//requires EGL 1.3\n//\t\t\tEGL_TEXTURE_TARGET,EGL_NO_TEXTURE,\t//just a rendertarget, not a texture.\n\t\t\tEGL_NONE,EGL_NONE\n\t\t};\n\t\tqeglBindAPI(EGL_OPENGL_API);\n\n\t\teglctx = qeglCreateContext(egldpy, cfg, EGL_NO_SURFACE, contextattr);\n\t}\n\telse\n\t{\n\t\tEGLint contextattr[] =\n\t\t{\n\t\t\tEGL_CONTEXT_CLIENT_VERSION, 2,\t//requires EGL 1.3\n\t\t\tEGL_NONE, EGL_NONE\n\t\t};\n\t\teglctx = qeglCreateContext(egldpy, cfg, EGL_NO_SURFACE, contextattr);\n\t}\n\tif (eglctx == EGL_NO_CONTEXT)\n\t{\n\t\tCon_Printf(CON_ERROR \"EGL: no context!\\n\");\n\t\treturn false;\n\t}\n\n\n\tif (!qeglMakeCurrent(egldpy, eglsurf, eglsurf, eglctx))\n\t{\n\t\tCon_Printf(CON_ERROR \"EGL: can't make current!\\n\");\n\t\treturn false;\n\t}\n\n\tEGL_UpdateSwapInterval();\n\n\n\tif (info->vr)\n\t{\n\t\tvrsetup_t vrsetup = {sizeof(vrsetup)};\n\t\tvrsetup.vrplatform = VR_EGL;\n\t\tvrsetup.egl.getprocaddr = qeglGetProcAddress;\n\t\tvrsetup.egl.egldisplay = egldpy;\n\t\tvrsetup.egl.eglconfig = cfg;\n\t\tvrsetup.egl.eglcontext = eglctx;\n\t\tif (!info->vr->Prepare(&vrsetup) ||\n\t\t\t!info->vr->Init(&vrsetup, info))\n\t\t{\n\t\t\tinfo->vr->Shutdown();\n\t\t\tinfo->vr = NULL;\n\t\t}\n\t\telse\n\t\t\tvid.vr = info->vr;\n\t}\n\n\treturn true;\n}\n\n\n\n//code for headless/pbuffer rendering.\n//in terms of energy efficiency its better to use the true headless renderer which stubs out ALL rendering.\n//however that's not very useful for screenshots or demo captures, so we offer this route too.\n#include \"glquake.h\"\n#include \"gl_draw.h\"\n#include \"shader.h\"\n#include \"EGL/eglext.h\"\nstatic void EGLHeadless_SwapBuffers(void)\n{\n\tif (R2D_Flush)\n\t\tR2D_Flush();\n\n\tEGL_SwapBuffers();\n}\nstatic qboolean EGLHeadless_Init (rendererstate_t *info, unsigned char *palette)\n{\n\tEGLConfig cfg;\n\tvoid *dpy = NULL;\n\n\tif (!EGL_LoadLibrary(info->subrenderer))\n\t{\n\t\tCon_Printf(\"couldn't load EGL library\\n\");\n\t\treturn false;\n\t}\n\n#ifdef EGL_EXT_device_base\n\tif (*info->devicename)\n\t{\n\t\tEGLDeviceEXT devs[64];\n\t\tEGLint count;\n\t\tEGLint idx = atoi(info->devicename);\n\t\tEGLBoolean (EGLAPIENTRY *qeglQueryDevicesEXT) (EGLint max_devices, EGLDeviceEXT *devices, EGLint *num_devices) = EGL_Proc(\"eglQueryDevicesEXT\");\n\t\tif (qeglQueryDevicesEXT)\n\t\t{\n\t\t\tqeglQueryDevicesEXT(countof(devs), devs, &count);\n\t\t\tif (idx >= 0 && idx < count)\n\t\t\t\tdpy = devs[idx];\n\t\t}\n\t}\n#endif\n\n\tvid.pixelwidth = (info->width>0)?info->width:640;\n\tvid.pixelheight = (info->height>0)?info->height:480;\n\tvid.activeapp = true;\n\n\tif (!EGL_InitDisplay(info, EGL_PLATFORM_DEVICE_EXT, dpy, (EGLNativeDisplayType)EGL_NO_DISPLAY, &cfg))\n\t{\n\t\tCon_Printf(\"couldn't find suitable EGL config\\n\");\n\t\treturn false;\n\t}\n\n\tif (!EGL_InitWindow(info, EGL_PLATFORM_DEVICE_EXT, EGL_NO_SURFACE, (EGLNativeWindowType)EGL_NO_SURFACE, cfg))\n\t{\n\t\tCon_Printf(\"couldn't initialise EGL context\\n\");\n\t\treturn false;\n\t}\n\n\tif (GL_Init(info, &EGL_Proc))\n\t\treturn true;\n\tCon_Printf(CON_ERROR \"Unable to initialise egl_headless.\\n\");\n\treturn false;\n}\nstatic void EGLHeadless_DeInit(void)\n{\n\tEGL_Shutdown();\n\tEGL_UnloadLibrary();\n\tGL_ForgetPointers();\n}\nstatic qboolean EGLHeadless_ApplyGammaRamps(unsigned int gammarampsize, unsigned short *ramps)\n{\n\t//not supported\n\treturn false;\n}\nstatic void EGLHeadless_SetCaption(const char *text)\n{\n}\n\nstatic int EGLHeadless_GetPriority(void)\n{\n\treturn -1;\t//lowest priority, so its never auto-used..\n}\n\nrendererinfo_t rendererinfo_headless_egl =\n{\n\t\"Headless OpenGL (EGL)\",\n\t{\n\t\t\"egl_headless\",\n\t},\n\tQR_OPENGL,\n\n\tGLDraw_Init,\n\tGLDraw_DeInit,\n\n\tGL_UpdateFiltering,\n\tGL_LoadTextureMips,\n\tGL_DestroyTexture,\n\n\tGLR_Init,\n\tGLR_DeInit,\n\tGLR_RenderView,\n\n\tEGLHeadless_Init,\n\tEGLHeadless_DeInit,\n\tEGLHeadless_SwapBuffers,\n\tEGLHeadless_ApplyGammaRamps,\n\tNULL,\n\tNULL,\n\tNULL,\n\tEGLHeadless_SetCaption,       //setcaption\n\tGLVID_GetRGBInfo,\n\n\n\tGLSCR_UpdateScreen,\n\n\tGLBE_SelectMode,\n\tGLBE_DrawMesh_List,\n\tGLBE_DrawMesh_Single,\n\tGLBE_SubmitBatch,\n\tGLBE_GetTempBatch,\n\tGLBE_DrawWorld,\n\tGLBE_Init,\n\tGLBE_GenBrushModelVBO,\n\tGLBE_ClearVBO,\n\tGLBE_UpdateLightmaps,\n\tGLBE_SelectEntity,\n\tGLBE_SelectDLight,\n\tGLBE_Scissor,\n\tGLBE_LightCullModel,\n\n\tGLBE_VBO_Begin,\n\tGLBE_VBO_Data,\n\tGLBE_VBO_Finish,\n\tGLBE_VBO_Destroy,\n\n\tGLBE_RenderToTextureUpdate2d,\n\n\t\"\",\n\tEGLHeadless_GetPriority\n};\n\n#endif\n\n"
  },
  {
    "path": "engine/gl/gl_videgl.h",
    "content": "#ifndef __GL_VIDEGL_H__\n#define __GL_VIDEGL_H__\n\n#include \"quakedef.h\"\n#define NativeWindowType EGLNativeWindowType\t//for old egl versions\n#include <EGL/egl.h>\n#ifndef _WIN32\n\t#include <dlfcn.h>\n#endif\n\n#ifndef EGL_PLATFORM_X11_KHR\n#define EGL_PLATFORM_X11_KHR                    0x31D5\t//EGL_KHR_platform_x11\n#define EGL_PLATFORM_X11_SCREEN_KHR             0x31D6\t//an attrib\n#endif\n\n#ifndef EGL_PLATFORM_WAYLAND_KHR\n#define EGL_PLATFORM_WAYLAND_KHR                0x31D8\t//EGL_KHR_platform_wayland\n#endif\n\n#ifndef EGL_PLATFORM_WIN32\n#define EGL_PLATFORM_WIN32\t\t\t\t\t\t0\t\t//no meaningful value.\n#endif\n\n#ifndef EGL_PLATFORM_DEVICE_EXT\n#define EGL_PLATFORM_DEVICE_EXT\t\t\t\t\t0x313F\t//we're using it for headless/pbuffer. oh well.\n#endif\n\nvoid *EGL_Proc(char *f);\nvoid EGL_UnloadLibrary(void);\nqboolean EGL_LoadLibrary(char *driver);\nvoid EGL_Shutdown(void);\nvoid EGL_SwapBuffers (void);\n\nqboolean EGL_InitDisplay (rendererstate_t *info, int eglplat, void *ndpy, EGLNativeDisplayType dpyid, EGLConfig *outconfig);\nqboolean EGL_InitWindow (rendererstate_t *info, int eglplat, void *nwindow, EGLNativeWindowType windowid, EGLConfig cfg);\n//qboolean EGL_Init (rendererstate_t *info, unsigned char *palette, int eglplatform, void *nwindow, void *ndpy, EGLNativeWindowType owindow, EGLNativeDisplayType odpy);\n\n//once you've created an egl display and got an egl config, some windowing systems require querying said egl config to create the window properly.\nextern EGLDisplay egldpy;\nextern EGLBoolean\t(EGLAPIENTRY *qeglGetConfigAttrib)(EGLDisplay dpy, EGLConfig config, EGLint attribute, EGLint *value);\n\n#endif\n"
  },
  {
    "path": "engine/gl/gl_vidlinuxglx.c",
    "content": "/*\nCopyright (C) 1996-1997 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n*/\n\n/*\nSmall note: anything concerning EGL in here is specific to egl-with-x11.\nif you want egl-with-framebuffer, look elsewhere.\n*/\n\n/*\nX11 is a huge pile of shit. I don't mean just the old x11 protocol, but all the _current_ standards that don't even try to fix the issues too.\n\nIts fucking retarded the crap that you have to do to get something to work.\ntimeouts to ensure alt+tab between two apps doesn't fuck up gamma ramps is NOT a nice thing to have to do.\n_MOUSE_ grabs cause alt+tab to fuck up\nif I use xinput2 to get raw mouse events (so that I don't have to use some random hack to disable acceleration and risk failing to reset it on crashes), then the mouse still moves outside of our window, and trying to fire then looses focus...\nxf86vm extension results in scrolling around a larger viewport. dependant upon the mouse position. even if we constrain the mouse to our window, it'll still scroll.\nwarping the pointer still triggers 'raw' mouse move events. in what world does that make any sense?!?\nalt-tab task lists are a window in their own right. that's okay, but what's not okay is that they destroy that window without giving focus to the new window first, so the old one gets focus and that fucks everything up too. yay for timeouts.\nto allow alt-tabbing with window managers that do not respect requests to not shove stuff on us, we have to hide ourselves completely and create a separate window that can still accept focus from the window manager. its fecking vile.\nwindow managers reparent us too, in much the same way. which is a bad thing because we keep getting reparented and that makes a mess of focus events. its a nightmare.\n\nthe whole thing is bloody retarded.\n\nnone of these issues will be fixed by a compositing window manager, because there's still a window manager there.\n*/\n\n#include <termios.h>\n#include <sys/ioctl.h>\n#include <sys/stat.h>\n#ifdef __linux__\n#include <sys/vt.h>\n#endif\n#include <stdarg.h>\n#include <stdio.h>\n#include <signal.h>\n#include <unistd.h>\n\n#include <dlfcn.h>\n\n#include \"quakedef.h\"\n\n#ifdef NO_X11\n\t#ifdef VKQUAKE\n\t\trendererinfo_t vkrendererinfo;\n\t#endif\n#else\n\n#include <X11/Xlib.h>\n#include <X11/Xatom.h>\n#include <X11/Xutil.h>\n\n#ifdef VKQUAKE\n#include \"vk/vkrenderer.h\"\n#ifdef VK_USE_PLATFORM_XLIB_KHR\nstatic qboolean XVK_SetupSurface_XLib(void);\n#endif\n#ifdef VK_USE_PLATFORM_XCB_KHR\nstatic qboolean XVK_SetupSurface_XCB(void);\n#endif\n#endif\n#ifdef GLQUAKE\n#include <GL/glx.h>\n#ifdef USE_EGL\n#include \"gl_videgl.h\"\n#endif\n#include \"glquake.h\"\n#include \"vr.h\"\n#endif\n\n#define USE_VMODE\n#ifndef NO_X11_RANDR\n\t#define USE_XRANDR\n#endif\n#ifndef NO_X11_XSS\n\t#define USE_XSS\n#endif\n\n#include <X11/keysym.h>\n#include <X11/cursorfont.h>\n#include <wchar.h>\n\nstatic Display *vid_dpy = NULL;\nstatic Cursor vid_nullcursor;\t//'cursor' to use when none should be shown\nstatic Cursor vid_newcursor;\t//cursor that the game is trying to use\nstatic Cursor vid_activecursor;\t//cursor that is currently active\nstatic Window vid_window;\nstatic Window vid_decoywindow;\t//for legacy mode, this is a boring window that we can reparent into as needed\nstatic Window vid_root;\n#ifdef GLQUAKE\nstatic GLXContext ctx = NULL;\n#endif\nextern qboolean vid_isfullscreen;\nstatic int scrnum;\nstatic long vid_x_eventmask;\nstatic enum\n{\n\tPSL_NONE,\n#ifdef GLQUAKE\n#ifdef USE_EGL\n\tPSL_EGL,\n#endif\n\tPSL_GLX,\n#endif\n#ifdef VKQUAKE\n\tPSL_VULKAN,\n#endif\n} currentpsl;\n\nextern cvar_t vid_conautoscale, vid_vsync;\n\nextern int sys_parentleft;\nextern int sys_parenttop;\nextern int sys_parentwidth;\nextern int sys_parentheight;\nextern long    sys_parentwindow;\n\nstatic qboolean X11_CheckFeature(const char *featurename, qboolean defaultval)\n{\n\tcvar_t *var;\n\tif (COM_CheckParm(va(\"-no%s\", featurename)))\n\t\treturn false;\n\tif (COM_CheckParm(va(\"-force%s\", featurename)))\n\t\treturn true;\n\tvar = Cvar_Get(va(\"x11_allow_%s\", featurename), defaultval?\"1\":\"0\", 0, NULL);\n\tif (var)\n\t\treturn !!var->ival;\n\treturn defaultval;\n}\nstatic qboolean X11_Clipboard_Notify(XSelectionEvent *xselection);\n\n#define KEY_MASK (KeyPressMask | KeyReleaseMask)\n#define MOUSE_MASK (ButtonPressMask | ButtonReleaseMask | \\\n\t\t    PointerMotionMask)\n\n#define X_MASK (KEY_MASK | MOUSE_MASK | ResizeRequest | StructureNotifyMask | FocusChangeMask | VisibilityChangeMask)\n\nstruct _XrmHashBucketRec;\n\ntypedef int (*qXErrorHandler) (Display*, XErrorEvent*);\n\nstatic struct\n{\n\tvoid *lib;\n\tint\t (*pXChangeProperty)(Display *display, Window w, Atom property, Atom type, int format, int mode, unsigned char *data, int nelements);\n\tint\t (*pXCloseDisplay)(Display *display);\n\tint \t (*pXConvertSelection)(Display *display, Atom selection, Atom target, Atom property, Window requestor, Time time);\n\tColormap (*pXCreateColormap)(Display *display, Window w, Visual *visual, int alloc);\n\tGC\t (*pXCreateGC)(Display *display, Drawable d, unsigned long valuemask, XGCValues *values);\n\tPixmap\t (*pXCreatePixmap)(Display *display, Drawable d, unsigned int width, unsigned int height, unsigned int depth);\n\tCursor\t (*pXCreatePixmapCursor)(Display *display, Pixmap source, Pixmap mask, XColor *foreground_color, XColor *background_color, unsigned int x, unsigned int y);\n\tWindow\t (*pXCreateWindow)(Display *display, Window parent, int x, int y, unsigned int width, unsigned int height, unsigned int border_width, int depth, unsigned int class, Visual *visual, unsigned long valuemask, XSetWindowAttributes *attributes);\n\tint\t (*pXDefineCursor)(Display *display, Window w, Cursor cursor);\n\tint\t (*pXDeleteProperty)(Display *display, Window w, Atom property);\n\tint\t (*pXDestroyWindow)(Display *display, Window w);\n\tint\t (*pXFillRectangle)(Display *display, Drawable d, GC gc, int x, int y, unsigned int width, unsigned int height);\n\tint \t (*pXFlush)(Display *display);\n\tint \t (*pXFree)(void *data);\n\tint\t (*pXFreeCursor)(Display *display, Cursor cursor);\n\tvoid\t (*pXFreeEventData)(Display *display, XGenericEventCookie *cookie);\n\tint\t (*pXFreeGC)(Display *display, GC gc);\n\tint\t (*pXFreePixmap)(Display *display, Pixmap pixmap);\n\tchar\t*(*pXGetAtomName)(Display *display, Atom atom);\n\tBool\t (*pXGetEventData)(Display *display, XGenericEventCookie *cookie);\n\tWindow \t (*pXGetSelectionOwner)(Display *display, Atom selection);\n\tStatus\t (*pXGetWindowAttributes)(Display *display, Window w, XWindowAttributes *window_attributes_return);\n\tint\t (*pXGetWindowProperty)(Display *display, Window w, Atom property, long long_offset, long long_length, Bool delete, Atom req_type, Atom *actual_type_return, int *actual_format_return, unsigned long *nitems_return, unsigned long *bytes_after_return, unsigned char **prop_return);\n\tint\t (*pXGrabKeyboard)(Display *display, Window grab_window, Bool owner_events, int pointer_mode, int keyboard_mode, Time time);\n\tint\t (*pXGrabPointer)(Display *display, Window grab_window, Bool owner_events, unsigned int event_mask, int pointer_mode, int keyboard_mode, Window confine_to, Cursor cursor, Time time);\n\tStatus (*pXInitThreads)(void);\n\tAtom \t (*pXInternAtom)(Display *display, char *atom_name, Bool only_if_exists);\n\tKeySym\t (*pXLookupKeysym)(XKeyEvent *key_event, int index);\n\tint\t (*pXLookupString)(XKeyEvent *event_struct, char *buffer_return, int bytes_buffer, KeySym *keysym_return, XComposeStatus *status_in_out);\n\tint\t (*pXMapWindow)(Display *display, Window w);\n\tint\t (*pXMoveResizeWindow)(Display *display, Window w, int x, int y, unsigned width, unsigned height);\n\tint\t (*pXMoveWindow)(Display *display, Window w, int x, int y);\n\tint\t (*pXNextEvent)(Display *display, XEvent *event_return);\n\tint\t (*pXPeekEvent)(Display *display, XEvent *event_return);\n\tDisplay *(*pXOpenDisplay)(char *display_name);\n\tint \t (*pXPending)(Display *display);\n\tBool \t (*pXQueryExtension)(Display *display, const char *name, int *major_opcode_return, int *first_event_return, int *first_error_return);\n\tint \t (*pXRaiseWindow)(Display *display, Window w);\n\tint\t (*pXReparentWindow)(Display *display, Window w, Window parent, int x, int y);\n\tint\t (*pXResizeWindow)(Display *display, Window w, unsigned width, unsigned height);\n\tint\t (*pXSelectInput)(Display *display, Window w, long event_mask);\n\tStatus \t (*pXSendEvent)(Display *display, Window w, Bool propagate, long event_mask, XEvent *event_send);\n\tint\t (*pXSetIconName)(Display *display, Window w, char *icon_name);\n\tint\t (*pXSetInputFocus)(Display *display, Window focus, int revert_to, Time time);\n\tint \t (*pXSetSelectionOwner)(Display *display, Atom selection, Window owner, Time time);\n\tvoid\t (*pXSetWMNormalHints)(Display *display, Window w, XSizeHints *hints);\n\tStatus\t (*pXSetWMProtocols)(Display *display, Window w, Atom *protocols, int count);\n\tint\t (*pXStoreName)(Display *display, Window w, char *window_name);\n\tint \t (*pXSync)(Display *display, Bool discard);\n\tint\t (*pXUndefineCursor)(Display *display, Window w);\n\tint\t (*pXUngrabKeyboard)(Display *display, Time time);\n\tint\t (*pXUngrabPointer)(Display *display, Time time);\n\tint \t (*pXWarpPointer)(Display *display, Window src_w, Window dest_w, int src_x, int src_y, unsigned int src_width, unsigned int src_height, int dest_x, int dest_y);\n\tStatus (*pXMatchVisualInfo)(Display *display, int screen, int depth, int class, XVisualInfo *vinfo_return);\n\tXVisualInfo *(*pXGetVisualInfo)(Display *display, long vinfo_mask, XVisualInfo *vinfo_template, int *nitems_return);\n\n\n\tqXErrorHandler (*pXSetErrorHandler)(XErrorHandler);\n\tint (*pXGetErrorText)(Display *display, int code, char *buffer_return, int length);\n\n\tint (*pXGrabServer)(Display *display);\n\tint (*pXUngrabServer)(Display *display);\n\n\tchar *(*pXKeysymToString)(KeySym);\n\tKeySym *(*pXGetKeyboardMapping)(Display *display, KeyCode first_keycode, int keycode_count, int *keysyms_per_keycode_return);\n\n#define XI_RESOURCENAME \"FTEQW\"\n#define XI_RESOURCECLASS \"FTEQW\"\n\tchar *(*pXSetLocaleModifiers)(char *modifier_list);\n\tBool (*pXSupportsLocale)(void); \n\tXIM\t\t(*pXOpenIM)(Display *display, struct _XrmHashBucketRec *db, char *res_name, char *res_class);\n\tchar *\t(*pXGetIMValues)(XIM im, ...); \n\tXIC\t\t(*pXCreateIC)(XIM im, ...);\n\tvoid\t(*pXSetICFocus)(XIC ic); \n\tvoid\t(*pXUnsetICFocus)(XIC ic); \n\tchar *  (*pXGetICValues)(XIC ic, ...);\n\tchar *  (*pXSetICValues)(XIC ic, ...);\n\tBool\t(*pXFilterEvent)(XEvent *event, Window w);\n\tint\t\t(*pXutf8LookupString)(XIC ic, XKeyPressedEvent *event, char *buffer_return, int bytes_buffer, KeySym *keysym_return, Status *status_return);\n\tint\t\t(*pXwcLookupString)(XIC ic, XKeyPressedEvent *event, wchar_t *buffer_return, int bytes_buffer, KeySym *keysym_return, Status *status_return);\n\tvoid\t(*pXDestroyIC)(XIC ic);\n\tStatus\t(*pXCloseIM)(XIM im);\n\tqboolean\tdounicode;\n\tint\t\t\time_shown;\n\tXIC\t\t\tunicodecontext;\n\tXIM\t\t\tinputmethod;\n\tXPoint\t\time_pos;\n\n\tstruct\n\t{\n\t\tWindow\tsource;\t//the source window to send dndFinished to.\n\t\tAtom\ttype;\t//the type of the data. usually text/uri-list.\n\t\tAtom\tmyprop;\t//the property on our window that we're copying the data to.\n\t} dnd;\n} x11;\n\nstatic int X11_ErrorHandler(Display *dpy, XErrorEvent *e)\n{\n\tchar msg[80];\n\t*msg = 0;\n\tx11.pXGetErrorText(dpy, e->error_code, msg, sizeof(msg));\n\tCon_Printf(CON_ERROR \"XLib Error %d (%s): request %d.%d\\n\", e->error_code, msg, e->request_code, e->minor_code);\n\treturn 0;\t//ignored.\n}\n\nstatic qboolean x11_initlib(void)\n{\n\tstatic dllfunction_t x11_functable[] =\n\t{\n\t\t{(void**)&x11.pXChangeProperty,\t\t\"XChangeProperty\"},\n\t\t{(void**)&x11.pXCloseDisplay,\t\t\"XCloseDisplay\"},\n\t\t{(void**)&x11.pXConvertSelection,\t\"XConvertSelection\"},\n\t\t{(void**)&x11.pXCreateColormap,\t\t\"XCreateColormap\"},\n\t\t{(void**)&x11.pXCreateGC,\t\t\"XCreateGC\"},\n\t\t{(void**)&x11.pXCreatePixmap,\t\t\"XCreatePixmap\"},\n\t\t{(void**)&x11.pXCreatePixmapCursor,\t\"XCreatePixmapCursor\"},\n\t\t{(void**)&x11.pXCreateWindow,\t\t\"XCreateWindow\"},\n\t\t{(void**)&x11.pXDefineCursor,\t\t\"XDefineCursor\"},\n\t\t{(void**)&x11.pXDeleteProperty,\t\t\"XDeleteProperty\"},\n\t\t{(void**)&x11.pXDestroyWindow,\t\t\"XDestroyWindow\"},\n\t\t{(void**)&x11.pXFillRectangle,\t\t\"XFillRectangle\"},\n\t\t{(void**)&x11.pXFlush,\t\t\t\"XFlush\"},\n\t\t{(void**)&x11.pXFree,\t\t\t\"XFree\"},\n\t\t{(void**)&x11.pXFreeCursor,\t\t\"XFreeCursor\"},\n\t\t{(void**)&x11.pXFreeGC,\t\t\t\"XFreeGC\"},\n\t\t{(void**)&x11.pXFreePixmap,\t\t\"XFreePixmap\"},\n\t\t{(void**)&x11.pXGetAtomName,\t\t\"XGetAtomName\"},\n\t\t{(void**)&x11.pXGetSelectionOwner,\t\"XGetSelectionOwner\"},\n\t\t{(void**)&x11.pXGetWindowAttributes,\t\"XGetWindowAttributes\"},\n\t\t{(void**)&x11.pXGetWindowProperty,\t\"XGetWindowProperty\"},\n\t\t{(void**)&x11.pXGrabKeyboard,\t\t\"XGrabKeyboard\"},\n\t\t{(void**)&x11.pXGrabPointer,\t\t\"XGrabPointer\"},\n\t\t{(void**)&x11.pXInitThreads,\t\t\"XInitThreads\"},\n\t\t{(void**)&x11.pXInternAtom,\t\t\"XInternAtom\"},\n\t\t{(void**)&x11.pXLookupKeysym,\t\t\"XLookupKeysym\"},\n\t\t{(void**)&x11.pXLookupString,\t\t\"XLookupString\"},\n\t\t{(void**)&x11.pXMapWindow,\t\t\"XMapWindow\"},\n\t\t{(void**)&x11.pXMoveResizeWindow,\t\"XMoveResizeWindow\"},\n\t\t{(void**)&x11.pXMoveWindow,\t\t\"XMoveWindow\"},\n\t\t{(void**)&x11.pXNextEvent,\t\t\"XNextEvent\"},\n\t\t{(void**)&x11.pXPeekEvent,\t\t\"XPeekEvent\"},\n\t\t{(void**)&x11.pXOpenDisplay,\t\t\"XOpenDisplay\"},\n\t\t{(void**)&x11.pXPending,\t\t\"XPending\"},\n\t\t{(void**)&x11.pXQueryExtension,\t\t\"XQueryExtension\"},\n\t\t{(void**)&x11.pXRaiseWindow,\t\t\"XRaiseWindow\"},\n\t\t{(void**)&x11.pXReparentWindow,\t\t\"XReparentWindow\"},\n\t\t{(void**)&x11.pXResizeWindow,\t\t\"XResizeWindow\"},\n\t\t{(void**)&x11.pXSelectInput,\t\t\"XSelectInput\"},\n\t\t{(void**)&x11.pXSendEvent,\t\t\"XSendEvent\"},\n\t\t{(void**)&x11.pXSetIconName,\t\t\"XSetIconName\"},\n\t\t{(void**)&x11.pXSetInputFocus,\t\t\"XSetInputFocus\"},\n\t\t{(void**)&x11.pXSetSelectionOwner,\t\"XSetSelectionOwner\"},\n\t\t{(void**)&x11.pXSetWMNormalHints,\t\"XSetWMNormalHints\"},\n\t\t{(void**)&x11.pXSetWMProtocols,\t\t\"XSetWMProtocols\"},\n\t\t{(void**)&x11.pXStoreName,\t\t\t\"XStoreName\"},\n\t\t{(void**)&x11.pXSync,\t\t\t\t\"XSync\"},\n\t\t{(void**)&x11.pXUndefineCursor,\t\t\"XUndefineCursor\"},\n\t\t{(void**)&x11.pXUngrabKeyboard,\t\t\"XUngrabKeyboard\"},\n\t\t{(void**)&x11.pXUngrabPointer,\t\t\"XUngrabPointer\"},\n\t\t{(void**)&x11.pXWarpPointer,\t\t\"XWarpPointer\"},\n\t\t{(void**)&x11.pXMatchVisualInfo,\t\"XMatchVisualInfo\"},\n\t\t{(void**)&x11.pXGetVisualInfo,\t\t\"XGetVisualInfo\"},\n\n\t\t{(void**)&x11.pXKeysymToString,\t\t\"XKeysymToString\"},\n\t\t{(void**)&x11.pXGetKeyboardMapping,\t\"XGetKeyboardMapping\"},\n\n\t\t{(void**)&x11.pXGrabServer,\t\t\t\"XGrabServer\"},\n\t\t{(void**)&x11.pXUngrabServer,\t\t\"XUngrabServer\"},\n\n\t\t{NULL, NULL}\n\t};\n\n\tif (!x11.lib)\n\t{\n#ifdef __CYGWIN__\n\t\tx11.lib = Sys_LoadLibrary(\"cygX11-6.dll\", x11_functable);\n#else\n\t\tx11.lib = Sys_LoadLibrary(\"libX11.so.6\", x11_functable);\n#endif\n\t\tif (!x11.lib)\n\t\t\tx11.lib = Sys_LoadLibrary(\"libX11\", x11_functable);\n\n\t\t//these ones are extensions, and the reason we're doing this.\n\t\tif (x11.lib)\n\t\t{\n\t\t\tx11.pXSetErrorHandler\t= Sys_GetAddressForName(x11.lib, \"XSetErrorHandler\");\n\t\t\tx11.pXGetErrorText\t= Sys_GetAddressForName(x11.lib, \"XGetErrorText\");\n\n\t\t\tif (x11.pXSetErrorHandler && x11.pXGetErrorText)\n\t\t\t\tx11.pXSetErrorHandler(X11_ErrorHandler);\n\n\t\t\t//raw input (yay mouse deltas)\n\t\t\tx11.pXGetEventData\t\t= Sys_GetAddressForName(x11.lib, \"XGetEventData\");\n\t\t\tx11.pXFreeEventData\t\t= Sys_GetAddressForName(x11.lib, \"XFreeEventData\");\n\n\t\t\t//internationalisation\n\t\t\tx11.pXSetLocaleModifiers = Sys_GetAddressForName(x11.lib, \"XSetLocaleModifiers\");\n\t\t\tx11.pXSupportsLocale\t= Sys_GetAddressForName(x11.lib, \"XSupportsLocale\");\n\t\t\tx11.pXOpenIM\t\t\t= Sys_GetAddressForName(x11.lib, \"XOpenIM\");\n\t\t\tx11.pXGetIMValues\t\t= Sys_GetAddressForName(x11.lib, \"XGetIMValues\");\n\t\t\tx11.pXCreateIC\t\t\t= Sys_GetAddressForName(x11.lib, \"XCreateIC\");\n\t\t\tx11.pXSetICFocus\t\t= Sys_GetAddressForName(x11.lib, \"XSetICFocus\");\n\t\t\tx11.pXUnsetICFocus\t\t= Sys_GetAddressForName(x11.lib, \"XUnsetICFocus\");\n\t\t\tx11.pXGetICValues\t\t= Sys_GetAddressForName(x11.lib, \"XGetICValues\");\n\t\t\tx11.pXSetICValues\t\t= Sys_GetAddressForName(x11.lib, \"XSetICValues\");\n\t\t\tx11.pXFilterEvent\t\t= Sys_GetAddressForName(x11.lib, \"XFilterEvent\");\n\t\t\tx11.pXutf8LookupString\t= Sys_GetAddressForName(x11.lib, \"Xutf8LookupString\");\n\t\t\tx11.pXwcLookupString\t= Sys_GetAddressForName(x11.lib, \"XwcLookupString\");\n\t\t\tx11.pXDestroyIC\t\t\t= Sys_GetAddressForName(x11.lib, \"XDestroyIC\");\n\t\t\tx11.pXCloseIM\t\t\t= Sys_GetAddressForName(x11.lib, \"XCloseIM\");\n\t\t}\n\t\telse\n\t\t{\n\t\t\tCon_Printf(\"Unable to load libX11\\n\");\n\t\t}\n\t}\n\t\n\n\treturn !!x11.lib;\n}\n\n#ifdef USE_XSS\n#include <X11/extensions/scrnsaver.h>\nstatic struct {\n\tvoid *lib;\n\tint event, error;\n\tint major, minor;\n\n\tBool (*pXScreenSaverQueryExtension)(Display *dpy, int *event_base_return, int *error_base_return);\n\tStatus (*pXScreenSaverQueryVersion)(Display *dpy, int *major_version_return, int *minor_version_return);\n\n\tvoid (*pXScreenSaverSuspend)(Display *dpy, Bool suspend);\n\n\tqboolean cansuspend;\n\tqboolean suspending;\n} x11xss;\nstatic qboolean X11Xss_Init(void)\n{\n\tstatic dllfunction_t x11xss_functable[] =\n\t{\n\t\t{(void**)&x11xss.pXScreenSaverQueryExtension,\t\"XScreenSaverQueryExtension\"},\n\t\t{(void**)&x11xss.pXScreenSaverQueryVersion,\t\t\"XScreenSaverQueryVersion\"},\n\t\t{(void**)&x11xss.pXScreenSaverSuspend,\t\t\t\"XScreenSaverSuspend\"},\n\t};\n\tif (!x11xss.lib)\n\t\tx11xss.lib = Sys_LoadLibrary(\"libXss.so.1\", x11xss_functable);\n\tx11xss.cansuspend = false;\n\tx11xss.suspending = false;\t//must be recalled for each new dpy...\n\tif (x11xss.lib)\n\t{\n\t\tif (x11xss.pXScreenSaverQueryExtension(vid_dpy, &x11xss.event, &x11xss.error))\n\t\t\tif (x11xss.pXScreenSaverQueryVersion(vid_dpy, &x11xss.major, &x11xss.minor))\n\t\t\t\tif (x11xss.major>1 || (x11xss.major==1&&x11xss.minor>=1))\n\t\t\t\t\tx11xss.cansuspend = true;\n\t}\n\treturn x11xss.cansuspend;\n}\nstatic void X11Xss_SuspendSaver(qboolean suspend)\n{\n\tif (!x11xss.cansuspend)\n\t\treturn; //no can do, sorry\n\tif (x11xss.suspending != suspend)\n\t{\n\t\tx11xss.suspending = suspend;\n\t\tx11xss.pXScreenSaverSuspend(vid_dpy, suspend);\t//this is refcounted, but we're asserting.\n\t}\n}\n#else\nstatic qboolean X11Xss_Init(void)\n{\n\treturn false;\n}\nstatic void X11Xss_SuspendSaver(qboolean suspend)\n{\n}\n#endif\n\n\n#ifdef VK_USE_PLATFORM_XCB_KHR\nstatic struct\n{\n\tvoid *lib;\n\txcb_connection_t *(*pXGetXCBConnection)(Display *dpy);\n} x11xcb;\nstatic qboolean x11xcb_initlib(void)\n{\n\tstatic dllfunction_t x11xcb_functable[] =\n\t{\n\t\t{(void**)&x11xcb.pXGetXCBConnection,\t\t\"XGetXCBConnection\"},\n\t\t{NULL, NULL}\n\t};\n\n\tif (!x11xcb.lib)\n\t{\n\t\tx11xcb.lib = Sys_LoadLibrary(\"libX11-xcb.so.1\", x11xcb_functable);\n\t\tif (!x11xcb.lib)\n\t\t\tx11xcb.lib = Sys_LoadLibrary(\"libX11-xcb\", x11xcb_functable);\n\n\t\tif (!x11xcb.lib)\n\t\t\tCon_Printf(\"Unable to load libX11-xcb\\n\");\n\t}\n\t\n\treturn !!x11xcb.lib;\n}\n#endif\n\n\n#define FULLSCREEN_DESKTOP\t(1u<<0)\t//we didn't need to change video modes at all.\n#define FULLSCREEN_VMODE\t(1u<<1)\t//using xf86 vidmode (we can actually change modes)\n#define FULLSCREEN_VMODEACTIVE\t(1u<<2)\t//xf86 vidmode currently forced\n#define FULLSCREEN_XRANDR\t(1u<<3)\t//using xf86 vidmode (we can actually change modes)\n#define FULLSCREEN_XRANDRACTIVE\t(1u<<4)\t//xf86 vidmode currently forced\n#define FULLSCREEN_LEGACY\t(1u<<5)\t//override redirect used (window is hidden from other programs, with a dummy window for alt+tab detection)\n#define FULLSCREEN_WM\t\t(1u<<6)\t//fullscreen hint used (desktop environment is expected to 'do the right thing', but it won't change actual video modes)\n#define FULLSCREEN_ACTIVE\t(1u<<7)\t//currently considered fullscreen\n#define FULLSCREEN_ANYMODE\t(FULLSCREEN_DESKTOP|FULLSCREEN_VMODE|FULLSCREEN_XRANDR)\nstatic unsigned int fullscreenflags;\nstatic int fullscreenx;\nstatic int fullscreeny;\nstatic int fullscreenwidth;\nstatic int fullscreenheight;\n\nvoid X_GoFullscreen(void);\nvoid X_GoWindowed(void);\n/*when alt-tabbing or whatever, the window manager creates a window, then destroys it again, resulting in weird focus events that trigger mode switches and grabs. using a timer reduces the weirdness and allows alt-tab to work properly. or at least better than it was working. that's the theory anyway*/\nstatic unsigned int modeswitchtime;\nstatic int modeswitchpending;\n\n#ifdef USE_VMODE\ntypedef struct\n{\n\tunsigned int        dotclock;\n\tunsigned short      hdisplay;\n\tunsigned short      hsyncstart;\n\tunsigned short      hsyncend;\n\tunsigned short      htotal;\n\tunsigned short      hskew;\n\tunsigned short      vdisplay;\n\tunsigned short      vsyncstart;\n\tunsigned short      vsyncend;\n\tunsigned short      vtotal;\n\tunsigned int        flags;\n} XF86VidModeModeInfo;\t//we don't touch this struct\n\nstatic struct\n{\n\tint opcode, event, error;\n\tint vmajor, vminor;\n\tvoid *lib;\n\tBool (*pXF86VidModeQueryVersion)(Display *dpy, int *majorVersion, int *minorVersion);\n\tBool (*pXF86VidModeGetGammaRampSize)(Display *dpy, int screen, int *size);\n\tBool (*pXF86VidModeGetGammaRamp)(Display *dpy, int screen, int size, unsigned short *red, unsigned short *green, unsigned short *blue);\n\tBool (*pXF86VidModeSetGammaRamp)(Display *dpy, int screen, int size, unsigned short *red, unsigned short *green, unsigned short *blue);\n\tBool (*pXF86VidModeSetViewPort)(Display *dpy, int screen, int x, int y);\n\tBool (*pXF86VidModeSwitchToMode)(Display *dpy, int screen, XF86VidModeModeInfo *modeline);\n\tBool (*pXF86VidModeGetAllModeLines)(Display *dpy, int screen, int *modecount, XF86VidModeModeInfo ***modelinesPtr);\n\n\tXF86VidModeModeInfo **modes;\n\tint num_modes;\n\tint usemode;\n\tunsigned short originalramps[3][2048];\n\tqboolean originalapplied;\t//states that the origionalramps arrays are valid, and contain stuff that we should revert to on close\n\tint originalrampsize;\n} vm;\nstatic qboolean VMODE_Init(void)\n{\n\tstatic dllfunction_t vm_functable[] =\n\t{\n\t\t{(void**)&vm.pXF86VidModeQueryVersion, \"XF86VidModeQueryVersion\"},\n\t\t{(void**)&vm.pXF86VidModeGetGammaRampSize, \"XF86VidModeGetGammaRampSize\"},\n\t\t{(void**)&vm.pXF86VidModeGetGammaRamp, \"XF86VidModeGetGammaRamp\"},\n\t\t{(void**)&vm.pXF86VidModeSetGammaRamp, \"XF86VidModeSetGammaRamp\"},\n\t\t{(void**)&vm.pXF86VidModeSetViewPort, \"XF86VidModeSetViewPort\"},\n\t\t{(void**)&vm.pXF86VidModeSwitchToMode, \"XF86VidModeSwitchToMode\"},\n\t\t{(void**)&vm.pXF86VidModeGetAllModeLines, \"XF86VidModeGetAllModeLines\"},\n\t\t{NULL, NULL}\n\t};\n\tvm.vmajor = 0;\n\tvm.vminor = 0;\n\tvm.usemode = -1;\n\tvm.originalapplied = false;\n\n\tif (!X11_CheckFeature(\"vmode\", true))\n\t\treturn false;\n\n\tif (!x11.pXQueryExtension(vid_dpy, \"XFree86-VidModeExtension\", &vm.opcode, &vm.event, &vm.error))\n\t{\n\t\tCon_Printf(\"VidModeExtension extension not available.\\n\");\n\t\treturn false;\n\t}\n\t\n\tif (!vm.lib)\n\t\tvm.lib = Sys_LoadLibrary(\"libXxf86vm\", vm_functable);\n\n\tif (vm.lib)\n\t{\n\t\tif (vm.pXF86VidModeQueryVersion(vid_dpy, &vm.vmajor, &vm.vminor))\n\t\t\tCon_DPrintf(\"Using XF86-VidModeExtension Ver. %d.%d\\n\", vm.vmajor, vm.vminor);\n\t\telse\n\t\t{\n\t\t\tCon_Printf(\"No XF86-VidModeExtension support\\n\");\n\t\t\tvm.vmajor = 0;\n\t\t\tvm.vminor = 0;\n\t\t}\n\t}\n\n\treturn vm.vmajor;\n}\n\nstatic void VMODE_SelectMode(int *width, int *height, float rate)\n{\n\tif (COM_CheckParm(\"-current\"))\n\t\treturn;\n\tif (vm.vmajor)\n\t{\n\t\tint best_fit, best_dist, dist, x, y, z, r, i;\n\n\t\tvm.pXF86VidModeGetAllModeLines(vid_dpy, scrnum, &vm.num_modes, &vm.modes);\n\t\tbest_dist = 9999999;\n\t\tbest_fit = -1;\n\t\n\t\tif ((!*width || *width == DisplayWidth(vid_dpy, scrnum)) && (!*height || *height == DisplayHeight(vid_dpy, scrnum)) && !rate)\n\t\t{\n\t\t\tCon_DPrintf(\"XF86VM: mode change not needed\\n\");\n\t\t\tfullscreenflags |= FULLSCREEN_DESKTOP;\n\t\t\treturn;\n\t\t}\n\n\t\tfor (i = 0; i < vm.num_modes; i++)\n\t\t{\n\t\t\t//fixme: check this formula. should be the full refresh rate\n\t\t\tr = vm.modes[i]->dotclock * 1000 / (vm.modes[i]->htotal * vm.modes[i]->vtotal);\n\t\t\tif (*width > vm.modes[i]->hdisplay ||\n\t\t\t\t*height > vm.modes[i]->vdisplay ||\n\t\t\t\trate > r)\n\t\t\t\tcontinue;\n\n\t\t\tx = *width - vm.modes[i]->hdisplay;\n\t\t\ty = *height - vm.modes[i]->vdisplay;\n\t\t\tz = rate?(rate - r):0;\n\t\t\tdist = (x * x) + (y * y) + (z * z);\n\t\t\tif (dist < best_dist)\n\t\t\t{\n\t\t\t\tbest_dist = dist;\n\t\t\t\tbest_fit = i;\n\t\t\t}\n\t\t}\n\n\t\tif (best_fit != -1)\n\t\t{\n\t\t\t// change to the mode\n\t\t\tif (vm.pXF86VidModeSwitchToMode(vid_dpy, scrnum, vm.modes[vm.usemode=best_fit]))\n\t\t\t{\n\t\t\t\t*width = vm.modes[best_fit]->hdisplay;\n\t\t\t\t*height = vm.modes[best_fit]->vdisplay;\n\t\t\t\t// Move the viewport to top left\n\t\t\t\tvm.pXF86VidModeSetViewPort(vid_dpy, scrnum, 0, 0);\n\t\t\t\tx11.pXSync(vid_dpy, False);\n\n\t\t\t\tfullscreenflags |= FULLSCREEN_VMODE | FULLSCREEN_VMODEACTIVE;\n\t\t\t\tCon_Printf(\"XF86VM: changed mode\\n\");\n\t\t\t}\n\t\t\telse\n\t\t\t\tCon_Printf(\"Failed to apply mode %i*%i\\n\", vm.modes[best_fit]->hdisplay, vm.modes[best_fit]->vdisplay);\n\t\t}\n\t}\n}\n#endif\n\n#ifdef USE_XRANDR\n#if 1\n\t#include <X11/extensions/Xrandr.h>\n#else\n\t//stuff to avoid dependancies\n\ttypedef struct _XRRScreenConfiguration XRRScreenConfiguration;\n\ttypedef unsigned short Rotation;\n\ttypedef unsigned short SizeID;\n\ttypedef unsigned short SubpixelOrder;\n\ttypedef unsigned short Connection;\n\t#define RR_Connected 0\n\ttypedef XID RROutput;\n\ttypedef XID RRCrtc;\n\ttypedef XID RRMode;\n\ttypedef unsigned long XRRModeFlags;\n\n\ttypedef struct {\n\t\tint width, height;\n\t\tint mwidth, mheight;\n\t} XRRScreenSize;\n\ttypedef struct _XRROutputInfo {\n\t\tTime            timestamp;\n\t\tRRCrtc          crtc;\n\t\tchar            *name;\n\t\tint             nameLen;\n\t\tunsigned long   mm_width;\n\t\tunsigned long   mm_height;\n\t\tConnection      connection;\n\t\tSubpixelOrder   subpixel_order;\n\t\tint             ncrtc;\n\t\tRRCrtc          *crtcs;\n\t\tint             nclone;\n\t\tRROutput        *clones;\n\t\tint             nmode;\n\t\tint             npreferred;\n\t\tRRMode  \t        *modes;\n\t} XRROutputInfo;\n\ttypedef struct _XRRModeInfo {\n\t\tRRMode              id;\n\t\tunsigned int        width;\n\t\tunsigned int        height;\n\t\tunsigned long       dotClock;\n\t\tunsigned int        hSyncStart;\n\t\tunsigned int        hSyncEnd;\n\t\tunsigned int        hTotal;\n\t\tunsigned int        hSkew;\n\t\tunsigned int        vSyncStart;\n\t\tunsigned int        vSyncEnd;\n\t\tunsigned int        vTotal;\n\t\tchar                *name;\n\t\tunsigned int        nameLength;\n\t\tXRRModeFlags        modeFlags;\n\t} XRRModeInfo;\n\ttypedef struct _XRRScreenResources {\n    \t\tTime        timestamp;\n\t\tTime        configTimestamp;\n\t\tint         ncrtc;\n\t\tRRCrtc      *crtcs;\n\t\tint         noutput;\n\t\tRROutput    *outputs;\n\t\tint         nmode;\n\t\tXRRModeInfo *modes;\n\t} XRRScreenResources;\n\ttypedef struct _XRRCrtcInfo {\n\t\tTime            timestamp;\n\t\tint             x, y;\n\t\tunsigned int    width, height;\n\t\tRRMode          mode;\n\t\tRotation        rotation;\n\t\tint             noutput;\n\t\tRROutput        *outputs;\n\t\tRotation        rotations;\n\t\tint             npossible;\n\t\tRROutput        *possible;\n\t} XRRCrtcInfo;\n\ttypedef struct _XRRCrtcGamma {\n\t\tint             size;\n\t\tunsigned short  *red;\n\t\tunsigned short  *green;\n\t\tunsigned short  *blue;\n\t} XRRCrtcGamma;\n\n\ttypedef struct _XRRPanning {\n\t\tTime            timestamp;\n\t\tunsigned int left;\n\t\tunsigned int top;\n\t\tunsigned int width;\n\t\tunsigned int height;\n\t\tunsigned int track_left;\n\t\tunsigned int track_top;\n\t\tunsigned int track_width;\n\t\tunsigned int track_height;\n\t\tint          border_left;\n\t\tint          border_top;\n\t\tint          border_right;\n\t\tint          border_bottom;\n\t} XRRPanning;\n#endif\nstatic struct\n{\n\t//caps\n\tqboolean canmodechange11;\n\tqboolean canmodechange12;\n\tqboolean cangamma;\n//\tqboolean cangetmonitor;\n\n\t//general stuff\n\tvoid *lib;\n\tint opcode, event, error;\n\tint vmajor, vminor;\n\tBool (*pQueryExtension)\t\t\t\t(Display *dpy, int *event_base_return, int *error_base_return);\n\tStatus (*pQueryVersion)\t\t\t\t(Display *dpy, int *major_version_return, int *minor_version_return);\n\n\t//for v1.1\n\t//this is only aware of a single screen, so that's a bit poo really.\n\t//if we go fullscreen, we have no real way to restore multi-display things after\n\tXRRScreenConfiguration\t*screenconfig;\n\tTime origtime;\n\tint origmode;\n\tRotation origrot;\n\tint origrate;\n\t\n\tint targmode;\n\tint targrate;\n\n\t//stuff to query video modes\n\tXRRScreenConfiguration *(*pGetScreenInfo)\t(Display *dpy, Window window);\n\tXRRScreenSize *(*pConfigSizes)\t\t\t(XRRScreenConfiguration *config, int *nsizes);\n\tshort *(*pConfigRates)\t\t\t\t(XRRScreenConfiguration *config, int sizeID, int *nrates);\n\tSizeID (*pConfigCurrentConfiguration)\t\t(XRRScreenConfiguration *config, Rotation *rotation);\n\tshort (*pConfigCurrentRate)\t\t\t(XRRScreenConfiguration *config);\n\n\t//stuff to change modes\n\tTime (*pConfigTimes)\t\t\t\t(XRRScreenConfiguration *config, Time *config_timestamp);\n\tshort (*pSetScreenConfigAndRate)\t\t(Display *dpy, XRRScreenConfiguration *config, Drawable draw, int size_index, Rotation rotation, short rate, Time timestamp);\n\n\t//for v1.2\n\t//we gain gamma and multiple outputs+crts+etc\n\tXRRScreenResources\t*res;\t\t//all the resources on the system (including modes etc)\n\tXRROutputInfo \t\t**outputs;\t//list of info for all outputs\n\tXRROutputInfo\t\t*output;\t//the output device that we're using\n\tRRCrtc\t\t\t\tcrtc;\t\t//the output device's screen that we're focusing on (modes and gamma)\n\tXRRCrtcInfo\t\t\t*crtcinfo;\t//the info to restore\n\tXRRModeInfo \t\t*crtcmode;\t//the mode we want to use\n\tXRRCrtcGamma\t\t*origgamma;\n\n//\tStatus\t\t\t\t(*pGetScreenSizeRange)\t(Display *dpy, Window window, int *minwidth, int *minheight, int *maxwidth, int *maxheight);\n\tvoid\t\t\t\t(*pSetScreenSize)\t\t(Display *dpy, Window window, int width, int height, int mmwidth, int mmheight);\n\tXRRScreenResources\t*(*pGetScreenResources)\t(Display *dpy, Window window);\n\tvoid\t\t\t\t*(*pFreeScreenResources)(XRRScreenResources *);\n\tXRROutputInfo \t\t*(*pGetOutputInfo)\t\t(Display *dpy, XRRScreenResources *resources, RROutput output);\n\tvoid\t\t\t\t*(*pFreeOutputInfo)\t\t(XRROutputInfo *outputinfo);\n\tXRRCrtcInfo \t\t*(*pGetCrtcInfo)\t\t(Display *dpy, XRRScreenResources *resources, RRCrtc crtc);\n\tvoid\t\t\t\t*(*pFreeCrtcInfo)\t\t(XRRCrtcInfo *crtcinfo);\n\tStatus\t\t\t\t(*pSetCrtcConfig)\t\t(Display *dpy, XRRScreenResources *resources, RRCrtc crtc, Time timestamp, int x, int y, RRMode mode, Rotation rotation, RROutput *output, int noutputs);\n\tXRRCrtcGamma *\t\t(*pGetCrtcGamma)\t\t(Display *dpy, RRCrtc crtc);\n\tvoid\t\t\t\t(*pFreeGamma)\t\t\t(XRRCrtcGamma *gamma);\n\tvoid\t\t\t\t(*pSetCrtcGamma)\t\t(Display *dpy, RRCrtc crtc, XRRCrtcGamma *gamma);\n\n\t//v1.3 has non-0 primary monitors.\n\tRROutput\t\t\t(*pGetOutputPrimary)\t(Display *dpy, Window window);\n\tStatus\t\t\t\t(*pSetPanning)\t\t\t(Display *dpy, XRRScreenResources *resources, RRCrtc crtc, XRRPanning *panning);\t//we need this just in case.\n\tXRRPanning *\t\t(*pGetPanning)\t\t\t(Display *dpy, XRRScreenResources *resources, RRCrtc crtc);\n\tvoid\t\t\t\t(*pFreePanning)\t\t\t(XRRPanning *panning);\n\tint pan[4];\t\t\t//for restoring panning. pan region the screen may move to\n\tint pantrack[4];\t//screen region where the mouse may be for the mouse to be tracked in\n\tint panborder[4];\t//border region of crtc for panning to take place in. typically >=0...\n\n\tint nvidiabug;\t//nvidia completely ignores panning requests, which fucks over fullscreen gameplay. we have to work around it by shrinking the screen which doesn't work with multiple displays (and risks fucking over everything else)\n\tint origscreenwidth,  origscreenwidthmm;\n\tint origscreenheight, origscreenheightmm;\n} xrandr;\nstatic qboolean XRandR_Init(void)\n{\n\tstatic dllfunction_t xrandr_functable[] =\n\t{\n\t\t{(void**)&xrandr.pQueryExtension,\t\t\t\t\"XRRQueryExtension\"},\n\t\t{(void**)&xrandr.pQueryVersion,\t\t\t\t\t\"XRRQueryVersion\"},\n\n\t\t//1.0\n\t\t{(void**)&xrandr.pGetScreenInfo,\t\t\t\t\"XRRGetScreenInfo\"},\n\t\t{(void**)&xrandr.pConfigTimes,\t\t\t\t\t\"XRRConfigTimes\"},\n\t\t{(void**)&xrandr.pConfigSizes,\t\t\t\t\t\"XRRConfigSizes\"},\n\t\t{(void**)&xrandr.pConfigRates,\t\t\t\t\t\"XRRConfigRates\"},\n\t\t{(void**)&xrandr.pConfigCurrentConfiguration,\t\"XRRConfigCurrentConfiguration\"},\n\t\t{(void**)&xrandr.pConfigCurrentRate,\t\t\t\"XRRConfigCurrentRate\"},\n\t\t//1.1\n\t\t{(void**)&xrandr.pSetScreenConfigAndRate,\t\t\"XRRSetScreenConfigAndRate\"},\n\n\t\t{NULL, NULL}\n\t};\n\txrandr.vmajor = 0;\n\txrandr.vminor = 0;\n\txrandr.canmodechange11 = false;\n\txrandr.canmodechange12 = false;\n//\txrandr.cangamma = false;\n\txrandr.crtcinfo = NULL;\n\txrandr.res = NULL;\n\txrandr.outputs = NULL;\n\n\t//enable by default once this is properly tested, and supports hwgamma.\n\tif (!X11_CheckFeature(\"xrandr\", true))\n\t\treturn false;\n\n\tif (!xrandr.lib)\n\t\txrandr.lib = Sys_LoadLibrary(\"libXrandr\", xrandr_functable);\n\n\tif (xrandr.lib)\n\t{\n\t\tif (xrandr.pQueryExtension(vid_dpy, &xrandr.event, &xrandr.error))\n\t\t{\n\t\t\txrandr.pQueryVersion(vid_dpy, &xrandr.vmajor, &xrandr.vminor);\n\t\t\tif (xrandr.vmajor > 1 || (xrandr.vmajor == 1 && xrandr.vminor >= 1))\n\t\t\t\txrandr.canmodechange11 = true;\n\t\t\tif (xrandr.vmajor > 1 || (xrandr.vmajor == 1 && xrandr.vminor >= 2))\n\t\t\t{\t//1.2 functions\n\t\t\t\txrandr.pGetScreenResources\t= Sys_GetAddressForName(xrandr.lib, \"XRRGetScreenResources\");\n\t\t\t\txrandr.pFreeScreenResources\t= Sys_GetAddressForName(xrandr.lib, \"XRRFreeScreenResources\");\n\t\t\t\txrandr.pGetOutputInfo\t\t= Sys_GetAddressForName(xrandr.lib, \"XRRGetOutputInfo\");\n\t\t\t\txrandr.pFreeOutputInfo\t\t= Sys_GetAddressForName(xrandr.lib, \"XRRFreeOutputInfo\");\n\t\t\t\txrandr.pGetCrtcInfo\t\t= Sys_GetAddressForName(xrandr.lib, \"XRRGetCrtcInfo\");\n\t\t\t\txrandr.pFreeCrtcInfo\t\t= Sys_GetAddressForName(xrandr.lib, \"XRRFreeCrtcInfo\");\n\t\t\t\txrandr.pSetCrtcConfig\t\t= Sys_GetAddressForName(xrandr.lib, \"XRRSetCrtcConfig\");\n\t\t\t\txrandr.pGetCrtcGamma\t\t= Sys_GetAddressForName(xrandr.lib, \"XRRGetCrtcGamma\");\n\t\t\t\txrandr.pFreeGamma\t\t= Sys_GetAddressForName(xrandr.lib, \"XRRFreeGamma\");\n\t\t\t\txrandr.pSetCrtcGamma\t\t= Sys_GetAddressForName(xrandr.lib, \"XRRSetCrtcGamma\");\n\n\t\t\t\txrandr.pSetScreenSize\t\t= Sys_GetAddressForName(xrandr.lib, \"XRRSetScreenSize\");\n\n\t\t\t\tif (\txrandr.pGetScreenResources && xrandr.pFreeScreenResources && xrandr.pFreeOutputInfo\n\t\t\t\t    &&\txrandr.pGetCrtcInfo && xrandr.pFreeCrtcInfo && xrandr.pSetCrtcConfig\n\t\t\t\t    &&\txrandr.pGetCrtcGamma && xrandr.pFreeGamma && xrandr.pSetCrtcGamma\n\t\t\t\t    \t)\n\t\t\t\t\txrandr.canmodechange12 = true;\n\t\t\t}\n\t\t\tif (xrandr.vmajor > 1 || (xrandr.vmajor == 1 && xrandr.vminor >= 3))\n\t\t\t{\n\t\t\t\txrandr.pGetOutputPrimary\t\t= Sys_GetAddressForName(xrandr.lib, \"XRRGetOutputPrimary\");\n\t\t\t\txrandr.pSetPanning\t\t\t\t= Sys_GetAddressForName(xrandr.lib, \"XRRSetPanning\");\n\t\t\t\txrandr.pGetPanning\t\t\t\t= Sys_GetAddressForName(xrandr.lib, \"XRRGetPanning\");\n\t\t\t\txrandr.pFreePanning\t\t\t\t= Sys_GetAddressForName(xrandr.lib, \"XRRFreePanning\");\n\t\t\t}\n\n\t\t\txrandr.nvidiabug = false; //hopeful...\n\t\t\t{\n\t\t\t\tint op, firstev, firsterr;\n\t\t\t\tif (x11.pXQueryExtension(vid_dpy, \"NV-CONTROL\", &op, &firstev, &firsterr))\n\t\t\t\t\txrandr.nvidiabug = true; //our dreams are so cruely shattered.\n\t\t\t}\n\n\t\t\t//FIXME: query monitor sizes and calculate dpi for vid.dpy_[x|y]\n\t\t\treturn true;\n\t\t}\n\t}\n\telse\n\t\tCon_Printf(\"XRandR library not available.\\n\");\n\n\treturn false;\n}\n\nstatic float XRandR_CalcRate(XRRModeInfo *mode)\n{\n\tif (!mode->hTotal || !mode->vTotal)\n\t\treturn 0;\n\treturn mode->dotClock / ((float)mode->hTotal*mode->vTotal);\n}\nstatic XRRModeInfo *XRandR_FindMode(RRMode mode)\n{\t//just looks up the right mode info.\n\tint i;\n\tfor (i = 0; i < xrandr.res->nmode; i++)\n\t{\n\t\tif (xrandr.res->modes[i].id == mode)\n\t\t\treturn &xrandr.res->modes[i];\n\t}\n\treturn NULL;\n}\nstatic XRRModeInfo *XRandR_FindBestMode(int width, int height, int rate)\n{\n\tint best_dist, dist, x, y, z, r, i;\n\tXRRModeInfo *mode, *best_mode;\n\n\tbest_dist = 9999999;\n\tbest_mode = NULL;\n\tfor (i = 0; i < xrandr.output->nmode; i++)\n\t{\n\t\tmode = XRandR_FindMode(xrandr.output->modes[i]);\n\t\tif (!mode)\n\t\t\tcontinue;\n\t\tr = XRandR_CalcRate(mode);\n\t\tif (width > mode->width ||\n\t\t\theight > mode->height ||\n\t\t\trate > r)\n\t\t\tcontinue;\n\n\t\t//FIXME: do rates differently - match width+height then come back for rate\n\t\tx = width - mode->width;\n\t\ty = height - mode->height;\n\t\tz = rate - r;\n\t\tdist = (x * x) + (y * y) + (z * z);\n\t\tif (dist < best_dist)\n\t\t{\n\t\t\tbest_dist = dist;\n\t\t\tbest_mode = mode;\n\t\t}\n\t}\n\treturn best_mode;\n}\n\nstatic void XRandR_RevertMode(void)\n{\n\tTime config_timestamp;\n\tif (fullscreenflags & FULLSCREEN_XRANDRACTIVE)\n\t{\n\t\tXRRCrtcInfo *c = xrandr.crtcinfo;\n\t\tif (c)\n\t\t{\n\t\t\tx11.pXGrabServer(vid_dpy);\n\t\t\tif (xrandr.nvidiabug == 1)\n\t\t\t{\t//attempt to undo at least part of the damage we inflicted to work around nvidia's defects.\n\t\t\t\tif (Success == xrandr.pSetCrtcConfig(vid_dpy, xrandr.res, xrandr.crtc, CurrentTime, c->x, c->y, None, c->rotation, NULL, 0))\n\t\t\t\t\txrandr.pSetScreenSize(vid_dpy, DefaultRootWindow(vid_dpy), xrandr.origscreenwidth, xrandr.origscreenheight, xrandr.origscreenwidthmm, xrandr.origscreenheightmm);\n\t\t\t}\n\n\t\t\tif (Success == xrandr.pSetCrtcConfig(vid_dpy, xrandr.res, xrandr.crtc, CurrentTime, c->x, c->y, c->mode, c->rotation, c->outputs, c->noutput))\n\t\t\t{\n\t\t\t\tif (xrandr.pSetPanning)\n\t\t\t\t{\t//and try to reset panning back to its original values.\n\t\t\t\t\tXRRPanning panning;\n\t\t\t\t\tpanning.timestamp\t\t= c->timestamp;\n\t\t\t\t\tpanning.left\t\t\t= xrandr.pan[0];\n\t\t\t\t\tpanning.top\t\t\t\t= xrandr.pan[1];\n\t\t\t\t\tpanning.width\t\t\t= xrandr.pan[2];\n\t\t\t\t\tpanning.height\t\t\t= xrandr.pan[3];\n\t\t\t\t\tpanning.track_left\t\t= xrandr.pantrack[0];\n\t\t\t\t\tpanning.track_top\t\t= xrandr.pantrack[1];\n\t\t\t\t\tpanning.track_width\t\t= xrandr.pantrack[2];\n\t\t\t\t\tpanning.track_height\t= xrandr.pantrack[3];\n\t\t\t\t\tpanning.border_left\t\t= xrandr.panborder[0];\n\t\t\t\t\tpanning.border_top\t\t= xrandr.panborder[1];\n\t\t\t\t\tpanning.border_right\t= xrandr.panborder[2];\n\t\t\t\t\tpanning.border_bottom\t= xrandr.panborder[3];\n\t\t\t\t\tif (Success != xrandr.pSetPanning(vid_dpy, xrandr.res, xrandr.crtc, &panning))\n\t\t\t\t\t\tCon_Printf(\"Revert panning configuration failed\\n\");\n\t\t\t\t\telse\n\t\t\t\t\t\tCon_DPrintf(\"Panning configuration succeeded\\n\");\n\t\t\t\t}\n\t\t\t\tCon_DPrintf(\"Reverted mode\\n\");\n\t\t\t}\n\t\t\telse\n\t\t\t\tCon_Printf(\"Couldn't revert XRandR mode!\\n\");\n\t\t\tx11.pXUngrabServer(vid_dpy);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (!xrandr.pSetScreenConfigAndRate(vid_dpy, xrandr.screenconfig, DefaultRootWindow(vid_dpy), xrandr.origmode, xrandr.origrot, xrandr.origrate, xrandr.origtime))\n\t\t\t\txrandr.origtime = xrandr.pConfigTimes(xrandr.screenconfig, &config_timestamp);\n\t\t}\n\t\tfullscreenflags &= ~FULLSCREEN_XRANDRACTIVE;\n\t}\n}\nstatic qboolean XRandR_ApplyMode(void)\n{\n\tqboolean ret = false;\n\tTime config_timestamp;\n\tif (!(fullscreenflags & FULLSCREEN_XRANDRACTIVE))\n\t{\n\t\tXRRCrtcInfo *c = xrandr.crtcinfo;\n\t\tif (c)\n\t\t{\n\t\t\tif (xrandr.crtcmode)\n\t\t\t{\n\t\t\t\tx11.pXGrabServer(vid_dpy);\n\t\t\t\tif (xrandr.nvidiabug == 1)\n\t\t\t\t{\n\t\t\t\t\t/*\tnvidia's drivers are a bit shite and behave differently from every other driver\n\t\t\t\t\t\tunlike other drivers they force panning enabled (and refuse to disable it), which then bugs out in SDL and Wine too.\n\t\t\t\t\t\t(specifically, the panning width+height values are recalculated (completely ignoring any passed args), but left+top are not, the tracking+border areas are preserved but affect nothing)\n\t\t\t\t\t\tthe workaround is to:\n\t\t\t\t\t\t\tlock the server (so other programs don't bug out too much),\n\t\t\t\t\t\t\tdisable the screen (for the next step to work),\n\t\t\t\t\t\t\tchange the virtual size to one that won't cause a problem with panning,\n\t\t\t\t\t\t\tre-enable the screen with the desired video mode,\n\t\t\t\t\t\t\tand unlock the server again.\n\t\t\t\t\t\tThis will not help with multimonitor setups, xrandr video mode switches are disabled entirely there.\n\t\t\t\t\t*/\n\t\t\t\t\tint screen_width = c->x + xrandr.crtcmode->width;\n\t\t\t\t\tint screen_height = c->y + xrandr.crtcmode->height;\n\t\t\t\t\tif (Success == xrandr.pSetCrtcConfig(vid_dpy, xrandr.res, xrandr.crtc, CurrentTime, c->x, c->y, None, c->rotation, NULL, 0))\n\t\t\t\t\t\txrandr.pSetScreenSize(vid_dpy, DefaultRootWindow(vid_dpy), screen_width, screen_height, (screen_width*xrandr.origscreenwidthmm)/xrandr.origscreenwidth, (screen_height*xrandr.origscreenheightmm)/xrandr.origscreenheight);\n\t\t\t\t}\n\t\t\t\tif (Success == xrandr.pSetCrtcConfig(vid_dpy, xrandr.res, xrandr.crtc, CurrentTime, c->x, c->y, xrandr.crtcmode->id, c->rotation, c->outputs, c->noutput))\n\t\t\t\t{\n\t\t\t\t\tif (xrandr.pSetPanning)\n\t\t\t\t\t{\t//disable panning, in case panning was previously enabled via exterior means\n\t\t\t\t\t\tXRRPanning panning;\n\t\t\t\t\t\tpanning.timestamp = c->timestamp;\n\t\t\t\t\t\tpanning.left = c->x;\n\t\t\t\t\t\tpanning.top = c->y;\n\t\t\t\t\t\tpanning.width = panning.height = 0; //disables panning - \"but RRSetScreenSize will silently enable panning if the screen size is increased. This does not happen if set to 0.\"\n\n\t\t\t\t\t\t//set the tracking area inside of the border area, to make doubly sure\n\t\t\t\t\t\tpanning.track_left = c->x + 1;\n\t\t\t\t\t\tpanning.track_top = c->y + 1;\n\t\t\t\t\t\tpanning.track_width = xrandr.crtcmode->width-2;\n\t\t\t\t\t\tpanning.track_height = xrandr.crtcmode->height-2;\n\t\t\t\t\t\tpanning.border_left = panning.border_top = panning.border_right = panning.border_bottom = -16384; //border area is the region of this screen in which panning might be triggered.\n\t\t\t\t\t\tif (Success != xrandr.pSetPanning(vid_dpy, xrandr.res, xrandr.crtc, &panning))\n\t\t\t\t\t\t\tCon_Printf(\"Panning configuration failed\\n\");\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tCon_DPrintf(\"Panning configuration succeeded\\n\");\n\t\t\t\t\t}\n\n\t\t\t\t\tCon_DPrintf(\"Applied mode\\n\");\n\t\t\t\t\tret = true;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tCon_Printf(\"Couldn't apply mode\\n\");\n\n\t\t\t\tx11.pXUngrabServer(vid_dpy);\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (!xrandr.pSetScreenConfigAndRate(vid_dpy, xrandr.screenconfig, DefaultRootWindow(vid_dpy), xrandr.targmode, xrandr.origrot, xrandr.targrate, xrandr.origtime))\n\t\t\t\txrandr.origtime = xrandr.pConfigTimes(xrandr.screenconfig, &config_timestamp);\n\t\t}\n\t\tfullscreenflags |= FULLSCREEN_XRANDRACTIVE;\n\t}\n\treturn ret;\n}\n\nstatic void XRandR_Shutdown(void)\n{\n\tint i;\n\tif (xrandr.origgamma)\n\t{\n\t\txrandr.pSetCrtcGamma(vid_dpy, xrandr.crtc, xrandr.origgamma);\n\t\txrandr.pFreeGamma(xrandr.origgamma);\n\t\txrandr.origgamma = NULL;\n\t}\n\tif (vid_dpy)\n\t\tXRandR_RevertMode();\n\tif (xrandr.outputs)\n\t{\n\t\tfor (i = 0; i < xrandr.res->noutput; i++)\n\t\t\txrandr.pFreeOutputInfo(xrandr.outputs[i]);\n\t\tZ_Free(xrandr.outputs);\n\t\txrandr.outputs = NULL;\n\t}\n\tif (xrandr.crtcinfo)\n\t{\n\t\txrandr.pFreeCrtcInfo(xrandr.crtcinfo);\n\t\txrandr.crtcinfo = NULL;\n\t}\n\tif (xrandr.res)\n\t{\n\t\txrandr.pFreeScreenResources(xrandr.res);\n\t\txrandr.res = NULL;\n\t}\n}\nstatic qboolean XRandR_FindOutput(const char *name)\n{\n\tRROutput p;\n\tint i;\n\tif (!xrandr.canmodechange12)\n\t\treturn false;\n\tif (!xrandr.res)\n\t\txrandr.res = xrandr.pGetScreenResources(vid_dpy, DefaultRootWindow(vid_dpy));\n\tif (!xrandr.res)\n\t\treturn false;\n\tif (!xrandr.outputs)\n\t{\n\t\txrandr.outputs = Z_Malloc(sizeof(*xrandr.outputs) * xrandr.res->noutput);\n\t\tfor (i = 0; i < xrandr.res->noutput; i++)\n\t\t\txrandr.outputs[i] = xrandr.pGetOutputInfo(vid_dpy, xrandr.res, xrandr.res->outputs[i]);\n\t}\n\tif (xrandr.pGetOutputPrimary)\n\t\tp = xrandr.pGetOutputPrimary(vid_dpy, DefaultRootWindow(vid_dpy));\n\telse if (xrandr.res->noutput)\n\t\tp = xrandr.res->outputs[0];\n\telse\n\t\tp = 0;\n\txrandr.output = NULL;\n\txrandr.crtc = None;\n\tif (xrandr.origgamma)\n\t{\n\t\txrandr.pSetCrtcGamma(vid_dpy, xrandr.crtc, xrandr.origgamma);\n\t\txrandr.pFreeGamma(xrandr.origgamma);\n\t\txrandr.origgamma = NULL;\n\t}\n\tif (xrandr.crtcinfo)\n\t\txrandr.pFreeCrtcInfo(xrandr.crtcinfo);\n\txrandr.crtcinfo = NULL;\n\tfor (i = 0; i < xrandr.res->noutput; i++)\n\t{\n\t\tif (xrandr.outputs[i]->connection != RR_Connected || !xrandr.outputs[i]->ncrtc)\n\t\t\tcontinue;\t//not usable...\n\t\tif (!xrandr.output && xrandr.res->outputs[i] == p)\n\t\t\txrandr.output = xrandr.outputs[i];\n\t\tif (*name && !strncmp(xrandr.outputs[i]->name, name, xrandr.outputs[i]->nameLen))\n\t\t{\t//this is the one they asked for\n\t\t\txrandr.output = xrandr.outputs[i];\n\t\t\tbreak;\n\t\t}\n\t}\n\tif (xrandr.output)\n\t{\n\t\txrandr.crtc = xrandr.output->crtc;\n\t\txrandr.crtcinfo = xrandr.pGetCrtcInfo(vid_dpy, xrandr.res, xrandr.crtc);\n\t\tif (xrandr.crtcinfo)\n\t\t{\n\t\t\txrandr.origgamma = xrandr.pGetCrtcGamma(vid_dpy, xrandr.crtc);\n\t\t\treturn true;\n\t\t}\n\t}\n\treturn false;\n}\n\n//called if we're not using randr to change video modes.\n//(sets up crtc data \nstatic qboolean XRandr_PickScreen(const char *devicename, int *x, int *y, int *width, int *height)\n{\n\tif (xrandr.crtcinfo || XRandR_FindOutput(devicename))\n\t{\n\t\tXRRCrtcInfo *c = xrandr.crtcinfo;\n\t\t*x = c->x;\n\t\t*y = c->y;\n\t\t*width = c->width;\n\t\t*height = c->height;\n\t\tCon_Printf(\"Found monitor %s %ix%i %i,%i\\n\", xrandr.output->name, c->width, c->height, c->x, c->y);\n\t\treturn true;\n\t}\n\treturn false;\n}\n\n//called when first changing video mode\nstatic void XRandR_SelectMode(const char *devicename, int *x, int *y, int *width, int *height, int rate)\n{\n\tif (COM_CheckParm(\"-current\"))\n\t\treturn;\n\n\tif (xrandr.crtcinfo)\n\t{\n\t\tXRRCrtcInfo *c;\n\t\txrandr.crtcmode = XRandR_FindBestMode(*width, *height, rate);\n\t\tc = xrandr.crtcinfo;\n\t\tif (!*width || !*height || (xrandr.crtcmode&&c->mode == xrandr.crtcmode->id))\n\t\t{\n\t\t\tfullscreenflags |= FULLSCREEN_DESKTOP;\n\t\t\tCon_Printf(\"XRRSetCrtcConfig not needed\\n\");\n\t\t}\n\t\telse if (!xrandr.crtcmode)\n\t\t\tCon_Printf(\"XRRSetCrtcConfig failed\\n\");\n\t\telse\n\t\t{\n\t\t\txrandr.origscreenwidth = DisplayWidth(vid_dpy, scrnum);\n\t\t\txrandr.origscreenheight = DisplayHeight(vid_dpy, scrnum);\n\t\t\txrandr.origscreenwidthmm = DisplayWidthMM(vid_dpy, scrnum);\n\t\t\txrandr.origscreenheightmm = DisplayHeightMM(vid_dpy, scrnum);\n\n\t\t\tif (xrandr.pGetPanning)\n\t\t\t{\n\t\t\t\tXRRPanning *panning = xrandr.pGetPanning(vid_dpy, xrandr.res, xrandr.crtc);\n\t\t\t\tif (panning)\n\t\t\t\t{\n\t\t\t\t\txrandr.pan[0] = panning->left?panning->left:c->x;\t//apparently some drivers can be buggy and forget left+top when panning was previously disabled, snappnig them to the wrong place when re-enabled.\n\t\t\t\t\txrandr.pan[1] = panning->top?panning->top:c->y;\n\t\t\t\t\txrandr.pan[2] = panning->width;\n\t\t\t\t\txrandr.pan[3] = panning->height;\n\t\t\t\t\txrandr.pantrack[0] = panning->track_left;\n\t\t\t\t\txrandr.pantrack[1] = panning->track_top;\n\t\t\t\t\txrandr.pantrack[2] = panning->track_width;\n\t\t\t\t\txrandr.pantrack[3] = panning->track_height;\n\t\t\t\t\txrandr.panborder[0] = panning->border_left;\n\t\t\t\t\txrandr.panborder[1] = panning->border_top;\n\t\t\t\t\txrandr.panborder[2] = panning->border_right;\n\t\t\t\t\txrandr.panborder[3] = panning->border_bottom;\n\t\t\t\t\txrandr.pFreePanning(panning);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (xrandr.nvidiabug && (c->x != 0 || c->y != 0 || c->noutput>1))\n\t\t\t{\n\t\t\t\tCon_Printf(\"Nvidia and multimonitor detected. XRandR cannot be used safely in this situation.\\n\");\n\t\t\t\txrandr.crtcmode = NULL;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (xrandr.nvidiabug)\n\t\t\t\t\tCon_Printf(CON_ERROR \"Attempting NVIDIA panning workaround. Try 'xrandr --output foo --auto' to fix if this breaks things.\\n\");\n\n\t\t\t\tfullscreenflags |= FULLSCREEN_XRANDR;\n\t\t\t\tif (XRandR_ApplyMode())\n\t\t\t\t{\t//worked\n\t\t\t\t\t*x = c->x;\n\t\t\t\t\t*y = c->y;\n\t\t\t\t\t*width = xrandr.crtcmode->width;\n\t\t\t\t\t*height = xrandr.crtcmode->height;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tfullscreenflags &= ~FULLSCREEN_XRANDR;\n\t\t\t}\n\t\t}\n\t}\n\telse if (xrandr.canmodechange11)\n\t{\n\t\tint best_fit, best_dist, best_rate, dist, x, y;\n\t\tint i, nummodes;\n\t\tXRRScreenSize *modes;\n\t\tTime config_timestamp;\n\t\tDrawable draw = DefaultRootWindow(vid_dpy);\n\n\t\txrandr.screenconfig = xrandr.pGetScreenInfo(vid_dpy, DefaultRootWindow(vid_dpy));\n\t\txrandr.origtime = xrandr.pConfigTimes(xrandr.screenconfig, &config_timestamp);\n\t\txrandr.origmode = xrandr.pConfigCurrentConfiguration(xrandr.screenconfig, &xrandr.origrot);\n\t\txrandr.origrate = xrandr.pConfigCurrentRate(xrandr.screenconfig);\n\t\tmodes = xrandr.pConfigSizes(xrandr.screenconfig, &nummodes);\n\n\t\tbest_dist = 9999999;\n\t\tbest_fit = -1;\n\t\tbest_rate = -1;\n\n\t\tfor (i = 0; i < nummodes; i++)\n\t\t{\n\t\t\t//fixme: check this formula. should be the full refresh rate\n\t\t\tif (*width > modes[i].width || *height > modes[i].height)\n\t\t\t\tcontinue;\n\n\t\t\tx = *width - modes[i].width;\n\t\t\ty = *height - modes[i].height;\n\t\t\tdist = (x * x) + (y * y);\n\t\t\tif (dist < best_dist)\n\t\t\t{\n\t\t\t\tbest_dist = dist;\n\t\t\t\tbest_fit = i;\n\t\t\t}\n\t\t}\n\n\t\tif (best_fit != -1)\n\t\t{\n\t\t\t//okay, there's a usable mode, and now we need to figure out what rate to use...\n\t\t\t//pick the higest rate under the target rate\n\t\t\tshort *rates = xrandr.pConfigRates(xrandr.screenconfig, best_fit, &nummodes);\n\t\t\tfor (i = 0; i < nummodes; i++)\n\t\t\t{\n\t\t\t\tif (rate>0 && rates[i] > rate)\n\t\t\t\t\tcontinue;\n\t\t\t\tif (best_rate < rates[i])\n\t\t\t\t\tbest_rate = rates[i];\n\t\t\t}\n\n\t\t\t//change to the mode\n\t\t\tCon_DPrintf(\"Setting XRandR Mode %i: %i*%i@%i\\n\", best_fit, modes[best_fit].width, modes[best_fit].height, best_rate);\n\t\t\tif (!xrandr.pSetScreenConfigAndRate(vid_dpy, xrandr.screenconfig, draw, best_fit, xrandr.origrot, best_rate, xrandr.origtime))\n\t\t\t{\n\t\t\t\txrandr.targmode = best_fit;\n\t\t\t\txrandr.targrate = best_rate;\n\t\t\t\t*width = modes[best_fit].width;\n\t\t\t\t*height = modes[best_fit].height;\n\t\t\t\tx11.pXSync(vid_dpy, False);\n\n\t\t\t\tfullscreenflags |= FULLSCREEN_XRANDR | FULLSCREEN_XRANDRACTIVE;\n\t\t\t}\n\t\t\telse\n\t\t\t\tCon_Printf(\"Failed to apply mode %i*%i@%i\\n\", modes[best_fit].width, modes[best_fit].height, best_rate);\n\t\t}\n\t}\n}\n#endif\n\n\n\nstatic float mouse_grabbed = 0;\n\nstatic enum\n{\n\tXIM_ORIG,\n\tXIM_DGA,\n\tXIM_XI2,\n} x11_input_method;\nint x11_mouseqdev = 0;\n\n#define XF86DGADirectMouse\t\t0x0004\nstatic struct\n{\n\tint opcode, event, error;\n\tvoid *lib;\n\tStatus (*pXF86DGADirectVideo) (Display *dpy, int screen, int enable);\n} dgam;\nstatic qboolean DGAM_Init(void)\n{\n\tstatic dllfunction_t dgam_functable[] =\n\t{\n\t\t{(void**)&dgam.pXF86DGADirectVideo, \"XF86DGADirectVideo\"},\n\t\t{NULL, NULL}\n\t};\n\n\tif (!x11.pXQueryExtension(vid_dpy, \"XFree86-DGA\", &dgam.opcode, &dgam.event, &dgam.error))\n\t{\n\t\tCon_Printf(\"DGA extension not available.\\n\");\n\t\treturn false;\n\t}\n\t\n\tif (!dgam.lib)\n\t\tdgam.lib = Sys_LoadLibrary(\"libXxf86dga\", dgam_functable);\n\treturn !!dgam.lib;\n}\n\n#if 0\n#include <X11/extensions/XInput2.h>\n#else\n#define XISetMask(ptr, event)   (((unsigned char*)(ptr))[(event)>>3] |=  (1 << ((event) & 7)))\n#define XIMaskIsSet(ptr, event) (((unsigned char*)(ptr))[(event)>>3] &   (1 << ((event) & 7)))\n#define XIMaskLen(event)        (((event + 7) >> 3))\ntypedef struct {\n\tint\t\t\t\tmask_len;\n\tunsigned char\t*mask;\n\tdouble\t\t\t*values;\n} XIValuatorState;\ntypedef struct\n{\n\tint\t\t\t\t\tdeviceid;\n\tint\t\t\t\t\tmask_len;\n\tunsigned char*\t\tmask;\n} XIEventMask;\n#define XIMasterPointer\t\t1\n#define XIMasterKeyboard\t2\n#define XISlavePointer\t\t3\n#define XISlaveKeyboard\t\t4\n#define XIAllDevices\t\t0\n#define XIAllMasterDevices\t1\n#define XI_RawButtonPress\t15\n#define XI_RawButtonRelease\t16\n#define XI_RawMotion\t\t17\n#define XI_LASTEVENT XI_RawMotion\ntypedef struct {\n\tint\t\t\t\ttype;\t\t\t/* GenericEvent */\n\tunsigned long\tserial;\t\t\t/* # of last request processed by server */\n\tBool\t\t\tsend_event;\t\t/* true if this came from a SendEvent request */\n\tDisplay\t\t\t*display;\t\t/* Display the event was read from */\n\tint\t\t\t\textension;\t\t/* XI extension offset */\n\tint\t\t\t\tevtype;\t\t\t/* XI_RawKeyPress, XI_RawKeyRelease, etc. */\n\tTime\t\t\ttime;\n\tint\t\t\t\tdeviceid;\n\tint\t\t\t\tsourceid;\t\t/* Bug: Always 0. https://bugs.freedesktop.org//show_bug.cgi?id=34240 */\n\tint\t\t\t\tdetail;\n\tint\t\t\t\tflags;\n\tXIValuatorState\tvaluators;\n\tdouble\t\t\t*raw_values;\n} XIRawEvent;\ntypedef struct\n{\n\tint         type;\n\tint         sourceid;\n} XIAnyClassInfo;\ntypedef struct\n{\n\tint                 deviceid;\n\tchar                *name;\n\tint                 use;\n\tint                 attachment;\n\tBool                enabled;\n\tint                 num_classes;\n\tXIAnyClassInfo      **classes;\n} XIDeviceInfo;\n#define XIValuatorClass\t\t2\ntypedef struct\n{\n\tint         type;\n\tint         sourceid;\n\tint         number;\n\tAtom        label;\n\tdouble      min;\n\tdouble      max;\n\tdouble      value;\n\tint         resolution;\n\tint         mode;\n} XIValuatorClassInfo;\n#define XIModeAbsolute\t\t1\n#endif\nstatic struct\n{\n\tint opcode, event, error;\n\tint vmajor, vminor;\n\tvoid *libxi;\n\n\tint devicegroup;\n\tsize_t ndeviceinfos;\n\tstruct xidevinfo\n\t{\n\t\tint qdev;\n\t\tstruct\n\t\t{\n\t\t\tqboolean abs;\n\t\t\tdouble min, max;\n\t\t\tfloat old;\n\t\t} axis[2]; //the meaning of any other axis is unknown. beware that they DO happen.\n\t\tqboolean abs;\n\t} *deviceinfo;\n\tint nextqdev;\n\n\tStatus (*pXIQueryVersion)( Display *display, int *major_version_inout, int *minor_version_inout);\n\tint (*pXISelectEvents)(Display *dpy, Window win, XIEventMask *masks, int num_masks);\n\tXIDeviceInfo *(*pXIQueryDevice)(Display *dpy, int deviceid, int *ndevices_return);\n\tvoid (*pXIFreeDeviceInfo)(XIDeviceInfo *info);\n} xi2;\nstatic struct xidevinfo *XI2_GetDeviceInfo(int devid)\n{\n\tif (devid >= xi2.ndeviceinfos)\n\t{\n\t\tstruct xidevinfo *n = Z_Malloc((devid+1) * sizeof(*xi2.deviceinfo));\n\t\tmemcpy(n, xi2.deviceinfo, xi2.ndeviceinfos*sizeof(*xi2.deviceinfo));\n\t\tZ_Free(xi2.deviceinfo);\n\t\txi2.deviceinfo = n;\n\t\twhile (xi2.ndeviceinfos <= devid)\n\t\t{\n\t\t\txi2.deviceinfo[xi2.ndeviceinfos].qdev = DEVID_UNSET;\n\t\t\tif (devid >= 2)\n\t\t\t{\n\t\t\t\tint devs;\n\t\t\t\tXIDeviceInfo *dev = xi2.pXIQueryDevice(vid_dpy, xi2.ndeviceinfos, &devs);\n\t\t\t\tif (dev)\n\t\t\t\t{\n\t\t\t\t\tif (devs==1)\n\t\t\t\t\t{\n\t\t\t\t\t\tint j;\n\t\t\t\t\t\tfor (j = 0; j < dev->num_classes; j++)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (dev->classes[j]->sourceid == xi2.ndeviceinfos && dev->classes[j]->type == XIValuatorClass)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tXIValuatorClassInfo *v = (XIValuatorClassInfo*)dev->classes[j];\n\t\t\t\t\t\t\t\tif (v->mode == XIModeAbsolute && v->number >= 0 && v->number < countof(xi2.deviceinfo[xi2.ndeviceinfos].axis))\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\txi2.deviceinfo[xi2.ndeviceinfos].abs = xi2.deviceinfo[xi2.ndeviceinfos].axis[v->number].abs = true;\n\t\t\t\t\t\t\t\t\txi2.deviceinfo[xi2.ndeviceinfos].axis[v->number].min = v->min;\n\t\t\t\t\t\t\t\t\txi2.deviceinfo[xi2.ndeviceinfos].axis[v->number].max = v->max;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\txi2.pXIFreeDeviceInfo(dev);\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\txi2.ndeviceinfos++;\n\t\t}\n\t}\n\t\t\n\treturn &xi2.deviceinfo[devid];\n}\nstatic qboolean XI2_Init(void)\n{\n\tstatic dllfunction_t xi2_functable[] =\n\t{\n\t\t{(void**)&xi2.pXIQueryVersion, \"XIQueryVersion\"},\n\t\t{(void**)&xi2.pXISelectEvents, \"XISelectEvents\"},\n\t\t{(void**)&xi2.pXIQueryDevice, \"XIQueryDevice\"},\n\t\t{(void**)&xi2.pXIFreeDeviceInfo, \"XIFreeDeviceInfo\"},\n\t\t{NULL, NULL}\n\t};\n\tXIEventMask evm;\n\tunsigned char maskbuf[XIMaskLen(XI_LASTEVENT)];\n\n\tif (!x11.pXQueryExtension(vid_dpy, \"XInputExtension\", &xi2.opcode, &xi2.event, &xi2.error))\n\t{\n\t\tCon_Printf(\"XInput extension not available.\\n\");\n\t\treturn false;\n\t}\n\n\tif (!xi2.libxi)\n\t{\n#ifdef __CYGWIN__\n\t\tif (!xi2.libxi)\n\t\t\txi2.libxi = Sys_LoadLibrary(\"cygXi-6.dll\", xi2_functable);\n#endif\n\t\tif (!xi2.libxi)\n\t\t\txi2.libxi = Sys_LoadLibrary(\"libXi.so.6\", xi2_functable);\n\t\tif (!xi2.libxi)\n\t\t\txi2.libxi = Sys_LoadLibrary(\"libXi\", xi2_functable);\n\t\tif (!xi2.libxi)\n\t\t\tCon_Printf(\"XInput library not available or too old.\\n\");\n\t}\n\tif (xi2.libxi)\n\t{\n//\t\txi2.nextqdev = 0;\t//start with 0, for player0\n//\t\txi2.nqdevices = 0;\n\t\txi2.vmajor = 2;\n\t\txi2.vminor = 0;\n//\t\txi2.devicegroup = XIAllMasterDevices;\n\t\txi2.devicegroup = XIAllDevices;\n\t\tif (xi2.pXIQueryVersion(vid_dpy, &xi2.vmajor, &xi2.vminor))\n\t\t{\n\t\t\tCon_Printf(\"XInput library or server is too old\\n\");\n\t\t\treturn false;\n\t\t}\n\t\tevm.deviceid = xi2.devicegroup;\n\t\tevm.mask_len = sizeof(maskbuf);\n\t\tevm.mask = maskbuf;\n\t\tmemset(maskbuf, 0, sizeof(maskbuf));\n\t\tXISetMask(maskbuf, XI_RawMotion);\n\t\tXISetMask(maskbuf, XI_RawButtonPress);\n\t\tXISetMask(maskbuf, XI_RawButtonRelease);\n/*\t\tif (xi2.vmajor >= 2 && xi2.vminor >= 2)\n\t\t{\n\t\t\tXISetMask(maskbuf, XI_RawTouchBegin);\n\t\t\tXISetMask(maskbuf, XI_RawTouchUpdate);\n\t\t\tXISetMask(maskbuf, XI_RawTouchEnd);\n\t\t}\n*/\t\txi2.pXISelectEvents(vid_dpy, DefaultRootWindow(vid_dpy), &evm, 1);\n\t\treturn true;\n\t}\n\treturn false;\n}\n\n/*-----------------------------------------------------------------------*/\n\n//qboolean is8bit = false;\n//qboolean isPermedia = false;\nextern qboolean sys_gracefulexit;\n\nchar *clipboard_buffer[2];\n\n\n/*-----------------------------------------------------------------------*/\n\n#ifdef GLQUAKE\nstatic struct\n{\n\tdllhandle_t *gllibrary;\n\n\tconst char *glxextensions;\n\n\tXVisualInfo* (*ChooseVisual) (Display *dpy, int screen, int *attribList);\n\tvoid (*SwapBuffers) (Display *dpy, GLXDrawable drawable);\n\tBool (*MakeCurrent) (Display *dpy, GLXDrawable drawable, GLXContext ctx);\n\tGLXContext (*CreateContext) (Display *dpy, XVisualInfo *vis, GLXContext shareList, Bool direct);\n\tvoid (*DestroyContext) (Display *dpy, GLXContext ctx);\n\n\tconst char * (*QueryExtensionsString)(Display * dpy,  int screen);\n\tvoid *(*GetProcAddress) (char *name);\n\tvoid (*SwapIntervalSGI) (int interval);\t\t\t\t//FFS!\n\tvoid (*SwapIntervalMESA) (unsigned int interval);\t//FFS!\n\tvoid (*SwapIntervalEXT) (Display *dpy, GLXDrawable drawable, int interval);\n\tqboolean swaptear;\n\n\tGLXFBConfig *(*ChooseFBConfig)(Display *dpy, int screen, const int *attrib_list, int *nelements);\n\tint (*GetFBConfigAttrib)(Display *dpy, GLXFBConfig config, int attribute, int * value);\n\tXVisualInfo *(*GetVisualFromFBConfig)(Display *dpy, GLXFBConfig config);\n\tGLXContext (*CreateContextAttribs)(Display *dpy, GLXFBConfig config, GLXContext share_context, Bool direct, const int *attrib_list);\n\n\tint swapint;\n} glx;\n\n/*Note: closing the GLX library is unsafe as nvidia's drivers like to crash once its reloaded.\nstatic void GLX_CloseLibrary(void)\n{\n\tSys_CloseLibrary(glx.gllibrary);\n\tglx.gllibrary = NULL;\n}\n*/\n\n#if 0//def _DEBUG\n//this is a list of the functions that exist in opengles2, as well as wglCreateContextAttribsARB.\n//functions not in this list *should* be stubs that just return errors, but we can't always depend on drivers for that... they shouldn't get called.\n//this list is just to make it easier to test+debug android gles2 stuff using windows.\nstatic char *gles2funcs[] =\n{\n#define f(n) #n,\n\t\tf(glActiveTexture)\n\t\tf(glAttachShader)\n\t\tf(glBindAttribLocation)\n\t\tf(glBindBuffer)\n\t\tf(glBindFramebuffer)\n\t\tf(glBindRenderbuffer)\n\t\tf(glBindTexture)\n\t\tf(glBlendColor)\n\t\tf(glBlendEquation)\n\t\tf(glBlendEquationSeparate)\n\t\tf(glBlendFunc)\n\t\tf(glBlendFuncSeparate)\n\t\tf(glBufferData)\n\t\tf(glBufferSubData)\n\t\tf(glCheckFramebufferStatus)\n\t\tf(glClear)\n\t\tf(glClearColor)\n\t\tf(glClearDepthf)\n\t\tf(glClearStencil)\n\t\tf(glColorMask)\n\t\tf(glCompileShader)\n\t\tf(glCompressedTexImage2D)\n\t\tf(glCompressedTexSubImage2D)\n\t\tf(glCopyTexImage2D)\n\t\tf(glCopyTexSubImage2D)\n\t\tf(glCreateProgram)\n\t\tf(glCreateShader)\n\t\tf(glCullFace)\n\t\tf(glDeleteBuffers)\n\t\tf(glDeleteFramebuffers)\n\t\tf(glDeleteProgram)\n\t\tf(glDeleteRenderbuffers)\n\t\tf(glDeleteShader)\n\t\tf(glDeleteTextures)\n\t\tf(glDepthFunc)\n\t\tf(glDepthMask)\n\t\tf(glDepthRangef)\n\t\tf(glDetachShader)\n\t\tf(glDisable)\n\t\tf(glDisableVertexAttribArray)\n\t\tf(glDrawArrays)\n\t\tf(glDrawElements)\n\t\tf(glEnable)\n\t\tf(glEnableVertexAttribArray)\n\t\tf(glFinish)\n\t\tf(glFlush)\n\t\tf(glFramebufferRenderbuffer)\n\t\tf(glFramebufferTexture2D)\n\t\tf(glFrontFace)\n\t\tf(glGenBuffers)\n\t\tf(glGenerateMipmap)\n\t\tf(glGenFramebuffers)\n \t\tf(glGenRenderbuffers)\n\t\tf(glGenTextures)\n\t\tf(glGetActiveAttrib)\n\t\tf(glGetActiveUniform)\n\t\tf(glGetAttachedShaders)\n\t\tf(glGetAttribLocation)\n\t\tf(glGetBooleanv)\n\t\tf(glGetBufferParameteriv)\n\t\tf(glGetError)\n\t\tf(glGetFloatv)\n\t\tf(glGetFramebufferAttachmentParameteriv)\n\t\tf(glGetIntegerv)\n\t\tf(glGetProgramiv)\n\t\tf(glGetProgramInfoLog)\n\t\tf(glGetRenderbufferParameteriv)\n\t\tf(glGetShaderiv)\n\t\tf(glGetShaderInfoLog)\n\t\tf(glGetShaderPrecisionFormat)\n\t\tf(glGetShaderSource)\n\t\tf(glGetString)\n\t\tf(glGetTexParameterfv)\n\t\tf(glGetTexParameteriv)\n\t\tf(glGetUniformfv)\n\t\tf(glGetUniformiv)\n\t\tf(glGetUniformLocation)\n\t\tf(glGetVertexAttribfv)\n\t\tf(glGetVertexAttribiv)\n\t\tf(glGetVertexAttribPointerv)\n\t\tf(glHint)\n\t\tf(glIsBuffer)\n\t\tf(glIsEnabled)\n\t\tf(glIsFramebuffer)\n\t\tf(glIsProgram)\n\t\tf(glIsRenderbuffer)\n\t\tf(glIsShader)\n\t\tf(glIsTexture)\n\t\tf(glLineWidth)\n\t\tf(glLinkProgram)\n\t\tf(glPixelStorei)\n\t\tf(glPolygonOffset)\n\t\tf(glReadPixels)\n\t\tf(glReleaseShaderCompiler)\n\t\tf(glRenderbufferStorage)\n\t\tf(glSampleCoverage)\n\t\tf(glScissor)\n\t\tf(glShaderBinary)\n\t\tf(glShaderSource)\n\t\tf(glStencilFunc)\n\t\tf(glStencilFuncSeparate)\n\t\tf(glStencilMask)\n\t\tf(glStencilMaskSeparate)\n\t\tf(glStencilOp)\n\t\tf(glStencilOpSeparate)\n\t\tf(glTexImage2D)\n\t\tf(glTexParameterf)\n\t\tf(glTexParameterfv)\n\t\tf(glTexParameteri)\n\t\tf(glTexParameteriv)\n\t\tf(glTexSubImage2D)\n\t\tf(glUniform1f)\n\t\tf(glUniform1fv)\n\t\tf(glUniform1i)\n\t\tf(glUniform1iv)\n\t\tf(glUniform2f)\n\t\tf(glUniform2fv)\n\t\tf(glUniform2i)\n\t\tf(glUniform2iv)\n\t\tf(glUniform3f)\n\t\tf(glUniform3fv)\n\t\tf(glUniform3i)\n\t\tf(glUniform3iv)\n\t\tf(glUniform4f)\n\t\tf(glUniform4fv)\n\t\tf(glUniform4i)\n\t\tf(glUniform4iv)\n\t\tf(glUniformMatrix2fv)\n\t\tf(glUniformMatrix3fv)\n\t\tf(glUniformMatrix4fv)\n\t\tf(glUseProgram)\n\t\tf(glValidateProgram)\n\t\tf(glVertexAttrib1f)\n\t\tf(glVertexAttrib1fv)\n\t\tf(glVertexAttrib2f)\n\t\tf(glVertexAttrib2fv)\n\t\tf(glVertexAttrib3f)\n\t\tf(glVertexAttrib3fv)\n\t\tf(glVertexAttrib4f)\n\t\tf(glVertexAttrib4fv)\n\t\tf(glVertexAttribPointer)\n\t\tf(glViewport)\n\t\tf(wglCreateContextAttribsARB)\n\t\tNULL\n};\n\n//this is a list of the functions that exist in opengles2, as well as wglCreateContextAttribsARB.\n//functions not in this list *should* be stubs that just return errors, but we can't always depend on drivers for that... they shouldn't get called.\n//this list is just to make it easier to test+debug android gles2 stuff using windows.\nstatic char *gles1funcs[] =\n{\n#define f(n) #n,\n\n\t\t/* Available only in Common profile */\n\t\tf(glAlphaFunc)\n\t\tf(glClearColor)\n\t\tf(glClearDepthf)\n\t\tf(glClipPlanef)\n\t\tf(glColor4f)\n\t\tf(glDepthRangef)\n\t\tf(glFogf)\n\t\tf(glFogfv)\n\t\tf(glFrustumf)\n\t\tf(glGetClipPlanef)\n\t\tf(glGetFloatv)\n\t\tf(glGetLightfv)\n\t\tf(glGetMaterialfv)\n\t\tf(glGetTexEnvfv)\n\t\tf(glGetTexParameterfv)\n\t\tf(glLightModelf)\n\t\tf(glLightModelfv)\n\t\tf(glLightf)\n\t\tf(glLightfv)\n\t\tf(glLineWidth)\n\t\tf(glLoadMatrixf)\n\t\tf(glMaterialf)\n\t\tf(glMaterialfv)\n\t\tf(glMultMatrixf)\n\t\tf(glMultiTexCoord4f)\n\t\tf(glNormal3f)\n\t\tf(glOrthof)\n\t\tf(glPointParameterf)\n\t\tf(glPointParameterfv)\n\t\tf(glPointSize)\n\t\tf(glPolygonOffset)\n\t\tf(glRotatef)\n\t\tf(glScalef)\n\t\tf(glTexEnvf)\n\t\tf(glTexEnvfv)\n\t\tf(glTexParameterf)\n\t\tf(glTexParameterfv)\n\t\tf(glTranslatef)\n\n\t\t/* Available in both Common and Common-Lite profiles */\n\t\tf(glActiveTexture)\n\t\tf(glAlphaFuncx)\n\t\tf(glBindBuffer)\n\t\tf(glBindTexture)\n\t\tf(glBlendFunc)\n\t\tf(glBufferData)\n\t\tf(glBufferSubData)\n\t\tf(glClear)\n\t\tf(glClearColorx)\n\t\tf(glClearDepthx)\n\t\tf(glClearStencil)\n\t\tf(glClientActiveTexture)\n\t\tf(glClipPlanex)\n\t\tf(glColor4ub)\n\t\tf(glColor4x)\n\t\tf(glColorMask)\n\t\tf(glColorPointer)\n\t\tf(glCompressedTexImage2D)\n\t\tf(glCompressedTexSubImage2D)\n\t\tf(glCopyTexImage2D)\n\t\tf(glCopyTexSubImage2D)\n\t\tf(glCullFace)\n\t\tf(glDeleteBuffers)\n\t\tf(glDeleteTextures)\n\t\tf(glDepthFunc)\n\t\tf(glDepthMask)\n\t\tf(glDepthRangex)\n\t\tf(glDisable)\n\t\tf(glDisableClientState)\n\t\tf(glDrawArrays)\n\t\tf(glDrawElements)\n\t\tf(glEnable)\n\t\tf(glEnableClientState)\n\t\tf(glFinish)\n\t\tf(glFlush)\n\t\tf(glFogx)\n\t\tf(glFogxv)\n\t\tf(glFrontFace)\n\t\tf(glFrustumx)\n\t\tf(glGetBooleanv)\n\t\tf(glGetBufferParameteriv)\n\t\tf(glGetClipPlanex)\n\t\tf(glGenBuffers)\n\t\tf(glGenTextures)\n\t\tf(glGetError)\n\t\tf(glGetFixedv)\n\t\tf(glGetIntegerv)\n\t\tf(glGetLightxv)\n\t\tf(glGetMaterialxv)\n\t\tf(glGetPointerv)\n\t\tf(glGetString)\n\t\tf(glGetTexEnviv)\n\t\tf(glGetTexEnvxv)\n\t\tf(glGetTexParameteriv)\n\t\tf(glGetTexParameterxv)\n\t\tf(glHint)\n\t\tf(glIsBuffer)\n\t\tf(glIsEnabled)\n\t\tf(glIsTexture)\n\t\tf(glLightModelx)\n\t\tf(glLightModelxv)\n\t\tf(glLightx)\n\t\tf(glLightxv)\n\t\tf(glLineWidthx)\n\t\tf(glLoadIdentity)\n\t\tf(glLoadMatrixx)\n\t\tf(glLogicOp)\n\t\tf(glMaterialx)\n\t\tf(glMaterialxv)\n\t\tf(glMatrixMode)\n\t\tf(glMultMatrixx)\n\t\tf(glMultiTexCoord4x)\n\t\tf(glNormal3x)\n\t\tf(glNormalPointer)\n\t\tf(glOrthox)\n\t\tf(glPixelStorei)\n\t\tf(glPointParameterx)\n\t\tf(glPointParameterxv)\n\t\tf(glPointSizex)\n\t\tf(glPolygonOffsetx)\n\t\tf(glPopMatrix)\n\t\tf(glPushMatrix)\n\t\tf(glReadPixels)\n\t\tf(glRotatex)\n\t\tf(glSampleCoverage)\n\t\tf(glSampleCoveragex)\n\t\tf(glScalex)\n\t\tf(glScissor)\n\t\tf(glShadeModel)\n\t\tf(glStencilFunc)\n\t\tf(glStencilMask)\n\t\tf(glStencilOp)\n\t\tf(glTexCoordPointer)\n\t\tf(glTexEnvi)\n\t\tf(glTexEnvx)\n\t\tf(glTexEnviv)\n\t\tf(glTexEnvxv)\n\t\tf(glTexImage2D)\n\t\tf(glTexParameteri)\n\t\tf(glTexParameterx)\n\t\tf(glTexParameteriv)\n\t\tf(glTexParameterxv)\n\t\tf(glTexSubImage2D)\n\t\tf(glTranslatex)\n\t\tf(glVertexPointer)\n\t\tf(glViewport)\n\n\t\t/*required to switch stuff around*/\n\t\tf(wglCreateContextAttribsARB)\n\t\tf(glXGetProcAddress)\n\t\tf(glXQueryExtensionsString)\n\t\tf(glXChooseFBConfig)\n\t\tf(glXGetFBConfigAttrib)\n\t\tf(glXGetVisualFromFBConfig)\n\t\tf(glXCreateContextAttribsARB)\n\t\tNULL\n};\n#endif\n\nstatic void *GLX_GetSymbol(char *name)\n{\n\tvoid *symb;\n\n#if 0//def _DEBUG\n\tif (1)\n\t{\n\t\tint i;\n\t\tfor (i = 0; gles1funcs[i]; i++)\n\t\t{\n\t\t\tif (!strcmp(name, gles1funcs[i]))\n\t\t\t\tbreak;\n\t\t}\n\t\tif (!gles1funcs[i])\n\t\t\treturn NULL;\t//not in the list\n\t}\n#endif\n\n\tif (glx.GetProcAddress)\n\t\tsymb = glx.GetProcAddress(name);\n\telse\n\t\tsymb = NULL;\n\n\tif (!symb)\n\t\tsymb = Sys_GetAddressForName(glx.gllibrary, name);\n\treturn symb;\n}\n\nstatic qboolean GLX_CheckExtension(const char *ext)\n{\n\tconst char *e = glx.glxextensions, *n;\n\tsize_t el = strlen(ext);\n\twhile(e && *e)\n\t{\n\t\twhile (*e == ' ')\n\t\t\te++;\n\t\tn = strchr(e, ' ');\n\t\tif (!n)\n\t\t\tn = n+strlen(e);\n\n\t\tif (n-e == el && !strncmp(ext, e, el))\n\t\t{\n\t\t\tCon_DPrintf(\"GLX: Found %s\\n\", ext);\n\t\t\treturn true;\n\t\t}\n\t\te = n;\n\t}\n\tCon_DPrintf(\"GLX: Missing %s\\n\", ext);\n\treturn false;\n}\n\nstatic qboolean GLX_InitLibrary(char *driver)\n{\n\tdllfunction_t funcs[] =\n\t{\n\t\t{(void*)&glx.ChooseVisual,\t\t\"glXChooseVisual\"},\n\t\t{(void*)&glx.SwapBuffers,\t\t\"glXSwapBuffers\"},\n\t\t{(void*)&glx.MakeCurrent,\t\t\"glXMakeCurrent\"},\n\t\t{(void*)&glx.CreateContext,\t\t\"glXCreateContext\"},\n\t\t{(void*)&glx.DestroyContext,\t\"glXDestroyContext\"},\n\t\t{NULL,\t\t\t\t\t\t\tNULL}\n\t};\n\tmemset(&glx, 0, sizeof(glx));\n\n\tif (driver && *driver)\n\t\tglx.gllibrary = Sys_LoadLibrary(driver, funcs);\n\telse\n\t\tglx.gllibrary = NULL;\n#ifdef __CYGWIN__\n\tif (!glx.gllibrary)\n\t\tglx.gllibrary = Sys_LoadLibrary(\"cygGL-1.dll\", funcs);\n#endif\n\tif (!glx.gllibrary)\t//I hate this.\n\t\tglx.gllibrary = Sys_LoadLibrary(\"libGL.so.1\", funcs);\n\tif (!glx.gllibrary)\n\t\tglx.gllibrary = Sys_LoadLibrary(\"libGL\", funcs);\n\tif (!glx.gllibrary)\n\t\treturn false;\n\n\tglx.QueryExtensionsString = GLX_GetSymbol(\"glXQueryExtensionsString\");\n\tglx.GetProcAddress = GLX_GetSymbol(\"glXGetProcAddress\");\n\tif (!glx.GetProcAddress)\n\t\tglx.GetProcAddress = GLX_GetSymbol(\"glXGetProcAddressARB\");\n\n\tglx.ChooseFBConfig = GLX_GetSymbol(\"glXChooseFBConfig\");\n\tglx.GetFBConfigAttrib = GLX_GetSymbol(\"glXGetFBConfigAttrib\");\n\tglx.GetVisualFromFBConfig = GLX_GetSymbol(\"glXGetVisualFromFBConfig\");\n\tglx.CreateContextAttribs = GLX_GetSymbol(\"glXCreateContextAttribsARB\");\n\treturn true;\n}\n\n#ifndef GLX_RGBA_FLOAT_BIT\n#define GLX_RGBA_FLOAT_BIT\t0x00000004\n#endif\n#ifndef GLX_CONTEXT_OPENGL_NO_ERROR_ARB\n#define GLX_CONTEXT_OPENGL_NO_ERROR_ARB\t0x31B3\n#endif\n\n//Since GLX1.3 (equivelent to gl1.2)\nstatic GLXFBConfig GLX_GetFBConfig(rendererstate_t *info)\n{\n\tint attrib[32];\n\tint n, i;\n\tint numconfigs;\n\tGLXFBConfig *fbconfigs;\n\n\tqboolean hassrgb, hasmultisample;//, hasfloats;\n\n\tif (!glx.ChooseFBConfig || !glx.GetVisualFromFBConfig)\n\t{\n\t\tCon_Printf(\"Missing function pointer\\n\");\n\t\treturn NULL;\n\t}\n\tif (!glx.CreateContextAttribs)\n\t{\n\t\tCon_Printf(\"Missing CreateContextAttribs\\n\");\n\t\treturn NULL;\t//don't worry about it\n\t}\n\n\thassrgb = GLX_CheckExtension(\"GLX_ARB_framebuffer_sRGB\") || GLX_CheckExtension(\"GLX_EXT_framebuffer_sRGB\");\n\thasmultisample = GLX_CheckExtension(\"GLX_ARB_multisample\");\n//\thasfloats = GLX_CheckExtension(\"GLX_ARB_fbconfig_float\");\n\n\t//do it in a loop, mostly to disable extensions that are unlikely to be supported on various glx implementations.\n\tfor (i = 0; i < (16<<1); i++)\n\t{\n\t\tn = 0;\n\t\t//attrib[n++] = GLX_LEVEL;\t\t\tattrib[n++] = 0;\t//overlays\n\t\tattrib[n++] = GLX_DOUBLEBUFFER;\t\tattrib[n++] = True;\n\t\tif (!(i&1))\n\t\t{\n\t\t\tif (!info->stereo)\n\t\t\t\tcontinue;\n\t\t\tattrib[n++] = GLX_STEREO; attrib[n++] = True;\n\t\t}\n\t\tif (!(i&2))\n\t\t{\n\t\t\tif (!info->srgb || !hassrgb)\n\t\t\t\tcontinue;\n\t\t\tattrib[n++] = GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB; attrib[n++] = True;\n\t\t}\n\t\t//attrib[n++] = GLX_AUX_BUFFERS;\tattrib[n++] = 0;\n\n\t\t\n#if 0\n\t\tif (!(i&4))\n\t\t{\t//unlike on windows, this is explicitly blocked except for pbuffers. ffs.\n\t\t\tif (info->srgb < 2 || !hasfloats)\n\t\t\t\tcontinue;\t//skip fp16 framebuffers\n\t\t\t//unlike on windows, this is explicitly blocked except for pbuffers. ffs.\n\t\t\tattrib[n++] = GLX_RENDER_TYPE;\t\tattrib[n++] = GLX_RGBA_FLOAT_BIT;\n\t\t\tattrib[n++] = GLX_RED_SIZE;\t\t\tattrib[n++] = info->bpp?info->bpp/3:4;\n\t\t\tattrib[n++] = GLX_GREEN_SIZE;\t\tattrib[n++] = info->bpp?info->bpp/3:4;\n\t\t\tattrib[n++] = GLX_BLUE_SIZE;\t\tattrib[n++] = info->bpp?info->bpp/3:4;\n\t\t}\n\t\telse\n#endif\n\t\t{\n\t\t\tif (info->bpp == 32)\n\t\t\t{\t//bpp32 is an alias for 24 (we ignore the alpha channel)\n\t\t\t\tattrib[n++] = GLX_RED_SIZE;\t\t\tattrib[n++] = 8;\n\t\t\t\tattrib[n++] = GLX_GREEN_SIZE;\t\tattrib[n++] = 8;\n\t\t\t\tattrib[n++] = GLX_BLUE_SIZE;\t\tattrib[n++] = 8;\n\t\t\t}\n\t\t\telse\n\t\t\t{\t//clamp requested bitdepth to 8bits on the second pass, so that bpp30 doesn't fail\n\t\t\t\tattrib[n++] = GLX_RED_SIZE;\t\t\tattrib[n++] = bound(1,info->bpp?info->bpp/3:4, (i&4)?8:16);\n\t\t\t\tattrib[n++] = GLX_GREEN_SIZE;\t\tattrib[n++] = bound(1,info->bpp?info->bpp/3:4, (i&4)?8:16);\n\t\t\t\tattrib[n++] = GLX_BLUE_SIZE;\t\tattrib[n++] = bound(1,info->bpp?info->bpp/3:4, (i&4)?8:16);\n\t\t\t}\n\t\t}\n\t\t//attrib[n++] = GLX_ALPHA_SIZE;\t\tattrib[n++] = GLX_DONT_CARE;\n\t\tattrib[n++] = GLX_DEPTH_SIZE;\t\tattrib[n++] = info->depthbits?info->depthbits:16;\n\t\tattrib[n++] = GLX_STENCIL_SIZE;\t\tattrib[n++] = (i&16)?0:4;\n\n\t\tif (!(i&8))\n\t\t{\n\t\t\tif (!info->multisample || !hasmultisample)\n\t\t\t\tcontinue;\n\t\t\tattrib[n++] = GLX_SAMPLE_BUFFERS_ARB;\tattrib[n++] = True;\n\t\t\tattrib[n++] = GLX_SAMPLES_ARB;\t\t\tattrib[n++] = info->multisample;\n\t\t}\n\n\t\t//attrib[n++] = GLX_ACCUM_RED_SIZE;\tattrib[n++] = 0;\n\t\t//attrib[n++] = GLX_ACCUM_GREEN_SIZE;\tattrib[n++] = 0;\n\t\t//attrib[n++] = GLX_ACCUM_BLUE_SIZE;\tattrib[n++] = 0;\n\t\t//attrib[n++] = GLX_ACCUM_ALPHA_SIZE;\tattrib[n++] = 0;\n\n\t\tattrib[n++] = GLX_DRAWABLE_TYPE;\tattrib[n++] = GLX_WINDOW_BIT;\n\t\tattrib[n++] = GLX_X_RENDERABLE;\t\tattrib[n++] = True;\n\t\t//attrib[n++] = GLX_X_VISUAL_TYPE;\tattrib[n++] = info->srgb?GLX_TRUE_COLOR:GLX_DIRECT_COLOR;\n\t\t//attrib[n++] = GLX_CONFIG_CAVEAT;\tattrib[n++] = GLX_DONT_CARE; GLX_NONE||GLX_SLOW_CONFIG||GLX_NON_CONFORMANT_CONFIG\n\t\t//attrib[n++] = GLX_TRANSPARENT_TYPE;attrib[n++] = GLX_NONE;\n\t\t//attrib[n++] = GLX_TRANSPARENT_ALPHA_VALUE;attrib[n++] = 0;\n\t\tattrib[n++] = None;\n\n\t\tnumconfigs = 0;\n\t\tfbconfigs = glx.ChooseFBConfig(vid_dpy, scrnum, attrib, &numconfigs);\n\t\tif (fbconfigs)\n\t\t{\n\t\t\tif (numconfigs)\n\t\t\t{\n\t\t\t\tGLXFBConfig r = fbconfigs[0];\n\t\t\t\tx11.pXFree(fbconfigs);\n\t\t\t\treturn r;\n\t\t\t}\n\t\t\tx11.pXFree(fbconfigs);\n\t\t}\n\t}\n\treturn NULL;\n}\n//for GLX<1.3\nstatic XVisualInfo *GLX_GetVisual(rendererstate_t *info)\n{\n\tXVisualInfo *visinfo;\n\tint attrib[32];\n\tint numattrib, i;\n\n\t//do it in a loop, mostly to disable extensions that are unlikely to be supported on various glx implementations.\n\tfor (i = 0; i < (8<<1); i++)\n\t{\n\t\tnumattrib = 0;\n\t\tattrib[numattrib++] = GLX_RGBA;\n\t\tattrib[numattrib++] = GLX_DOUBLEBUFFER;\n\t\tif (!(i&1))\n\t\t{\n\t\t\tif (!info->stereo)\n\t\t\t\tcontinue;\n\t\t\tattrib[numattrib++] = GLX_STEREO;\n\t\t}\n\t\tif (!(i&2))\n\t\t{\n\t\t\tif (!info->srgb)\n\t\t\t\tcontinue;\n\t\t\tattrib[numattrib++] = GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB;\n\t\t}\n\t\tif (!(i&4))\n\t\t{\n\t\t\tif (!info->multisample)\n\t\t\t\tcontinue;\n\t\t\tattrib[numattrib++] = GLX_SAMPLE_BUFFERS_ARB; attrib[numattrib++] = info->multisample;\n\t\t}\n\t\tif (!(i&8))\n\t\t{\n\t\t\tattrib[numattrib++] = GLX_STENCIL_SIZE;\tattrib[numattrib++] = 4;\n\t\t}\n\t\tattrib[numattrib++] = GLX_RED_SIZE;\t\tattrib[numattrib++] = 4;\n\t\tattrib[numattrib++] = GLX_GREEN_SIZE;\tattrib[numattrib++] = 4;\n\t\tattrib[numattrib++] = GLX_BLUE_SIZE;\tattrib[numattrib++] = 4;\n\t\tattrib[numattrib++] = GLX_DEPTH_SIZE;\tattrib[numattrib++] = 16;\n\t\tattrib[numattrib++] = None;\n\n\t\tvisinfo = glx.ChooseVisual(vid_dpy, scrnum, attrib);\n\t\tif (visinfo)\n\t\t\treturn visinfo;\n\t}\n\treturn NULL;\n}\n\nstatic qboolean GLX_Init(rendererstate_t *info, GLXFBConfig fbconfig, XVisualInfo *visinfo)\n{\n\textern cvar_t\tvid_gl_context_version;\n\textern cvar_t\tvid_gl_context_forwardcompatible;\n\textern cvar_t\tvid_gl_context_compatibility;\n\textern cvar_t\tvid_gl_context_debug;\n\textern cvar_t\tvid_gl_context_es;\n\textern cvar_t\tvid_gl_context_robustness;\n//\textern cvar_t\tvid_gl_context_selfreset;\n\textern cvar_t\tvid_gl_context_noerror;\n\n\tvrsetup_t setup = {sizeof(setup)};\n\tsetup.vrplatform = VR_X11_GLX;\n\tsetup.x11_glx.display = vid_dpy;\n\tsetup.x11_glx.visualid = visinfo->visualid;\n\tsetup.x11_glx.glxfbconfig = fbconfig;\n\tsetup.x11_glx.drawable = vid_window;\n\n\tif (info->vr && !info->vr->Prepare(&setup))\n\t{\n\t\tinfo->vr->Shutdown();\n\t\tinfo->vr = NULL;\n\t}\n\n\tif (fbconfig && glx.CreateContextAttribs)\n\t{\n\t\tunsigned int majorver=1, minorver=1;\n\t\tunsigned profile = 0;\n\t\tunsigned contextflags = 0;\n\t\tint attrib[16*2+1];\n\t\tint n, val;\n\t\tchar *ver;\n\n\t\tver = vid_gl_context_version.string;\n\t\tif (!*ver && vid_gl_context_es.ival && GLX_CheckExtension(\"GLX_EXT_create_context_es_profile\"))\n\t\t\tver = vid_gl_context_es.string;\n\t\tif (!*ver && !vid_gl_context_compatibility.ival && *vid_gl_context_compatibility.string)\n\t\t\tver = \"3.2\";\t//if the user explicitly disabled compat, then they'll want a version that actually supports doing so.\n\n\t\tmajorver = strtoul(ver, &ver, 10);\n\t\tif (*ver == '.')\n\t\t\tminorver = strtoul(ver+1, &ver, 10);\n\t\telse\n\t\t\tminorver = 0;\n\n\t\tif (majorver < setup.minver.major || (majorver == setup.minver.major && minorver < setup.minver.minor))\n\t\t{\t//if vr stuff requires a minimum version then try and ask for that now\n\t\t\tmajorver = setup.minver.major;\n\t\t\tminorver = setup.minver.minor;\n\t\t}\n\n\t\t//some weirdness for you:\n\t\t//3.0 simply marked stuff as deprecated, without removing it.\n\t\t//3.1 removed it (moved to the optional GL_ARB_compatibility extension, which shouldn't be supported in a forward-compatible context).\n\t\t//3.2 added the concept of profiles.\n\n\t\tif (vid_gl_context_debug.ival)\n\t\t\tcontextflags |= GLX_CONTEXT_DEBUG_BIT_ARB;\n\t\tif (vid_gl_context_forwardcompatible.ival)\t//treat this as a dev/debug thing.\n\t\t\tcontextflags |= GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB;\n\t\tif (vid_gl_context_robustness.ival && GLX_CheckExtension(\"GLX_ARB_create_context_robustness\"))\n\t\t\tcontextflags |= GLX_CONTEXT_ROBUST_ACCESS_BIT_ARB;\n\n\t\tif (vid_gl_context_es.ival && GLX_CheckExtension(\"GLX_EXT_create_context_es_profile\"))\n\t\t\tprofile = GLX_CONTEXT_ES_PROFILE_BIT_EXT;\n\t\telse if (majorver>3 || (majorver==3&&minorver>=2))\n\t\t{\t//profiles only started with 3.2\n\t\t\tif (vid_gl_context_compatibility.ival)\n\t\t\t\tprofile = GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB;\n\t\t\telse\n\t\t\t\tprofile = GLX_CONTEXT_CORE_PROFILE_BIT_ARB;\n\t\t}\n\t\telse\n\t\t\tprofile = 0;\n\n\t\tn = 0;\n\t\tif (majorver || minorver)\n\t\t{\n\t\t\tattrib[n++] = GLX_CONTEXT_MAJOR_VERSION_ARB;\t\tattrib[n++] = majorver;\n\t\t\tattrib[n++] = GLX_CONTEXT_MINOR_VERSION_ARB;\t\tattrib[n++] = minorver;\n\t\t}\n\t\tif (contextflags)\n\t\t{\n\t\t\tattrib[n++] = GLX_CONTEXT_FLAGS_ARB;\t\t\t\tattrib[n++] = contextflags;\n\t\t}\n\t\tif (profile)\n\t\t{\n\t\t\tattrib[n++] = GLX_CONTEXT_PROFILE_MASK_ARB;\t\tattrib[n++] = profile;\n\t\t}\n\t\tif (vid_gl_context_noerror.ival &&  GLX_CheckExtension(\"GLX_ARB_create_context_no_error\"))\n\t\t{\n\t\t\tattrib[n++] = GLX_CONTEXT_OPENGL_NO_ERROR_ARB;\tattrib[n++] = !vid_gl_context_robustness.ival && !vid_gl_context_debug.ival;\n\t\t}\n\t\tattrib[n++] = None;\n\n\t\tif (glx.GetFBConfigAttrib)\n\t\t{\n\t\t\tif (!glx.GetFBConfigAttrib(vid_dpy, fbconfig, GLX_RENDER_TYPE, &val) && val == GLX_RGBA_FLOAT_BIT)\n\t\t\t\tvid.flags |= VID_FP16;\t\t\t//other things need to be 16bit too, to avoid loss of precision.\n\t\t\tif (!glx.GetFBConfigAttrib(vid_dpy, fbconfig, GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB, &val) && val)\n\t\t\t\tvid.flags |= VID_SRGB_CAPABLE;\t//can use srgb properly, without faking it etc.\n\t\t}\n\n\t\tctx = glx.CreateContextAttribs(vid_dpy, fbconfig, NULL, True, attrib);\n\t}\n\telse\n\t\tctx = glx.CreateContext(vid_dpy, visinfo, NULL, True);\n\tif (!ctx)\n\t{\n\t\tCon_Printf(\"Failed to create GLX context.\\n\");\n\t\treturn false;\n\t}\n\n\tif (!glx.MakeCurrent(vid_dpy, vid_window, ctx))\n\t{\n\t\tCon_Printf(\"glXMakeCurrent failed\\n\");\n\t\treturn false;\n\t}\n\n\n\tsetup.x11_glx.glxcontext = ctx;\n\tif (info->vr && !info->vr->Init(&setup, info))\n\t{\n\t\tinfo->vr->Shutdown();\n\t\treturn false;\n\t}\n\tvid.vr = info->vr;\n\n\t//okay, we have a context, now init OpenGL-proper.\n\treturn GL_Init(info, &GLX_GetSymbol);\n}\n#endif\n\nstatic void X_ShutdownUnicode(void)\n{\n\tif (x11.unicodecontext)\n\t\tx11.pXDestroyIC(x11.unicodecontext);\n\tx11.unicodecontext = NULL;\n\tif (x11.inputmethod)\n\t\tx11.pXCloseIM(x11.inputmethod);\n\tx11.inputmethod = NULL;\n\tx11.dounicode = false;\n}\nstatic int XIMPreEditStartCallback(XIC ic, XPointer client_data, XPointer call_data)\n{\n\tZ_Free(vid.ime_preview);\t//just in case\n\tvid.ime_preview = NULL;\n\tvid.ime_previewlen = 0;\n//\tCon_Printf(\"XIMPreEditStartCallback\\n\");\n\treturn -1;\t//length of string we can handle (negative for unlimited)\n}\nstatic void XIMPreEditDoneCallback(XIC ic, XPointer client_data, XPointer call_data)\n{\n//\tCon_Printf(\"XIMPreEditDoneCallback\\n\");\n\n\tZ_Free(vid.ime_preview);\n\tvid.ime_preview = NULL;\n\tvid.ime_previewlen = 0;\n}\nstatic void XIMPreEditDrawCallback(XIM ic, XPointer client_data, XIMPreeditDrawCallbackStruct *d)\n{\n\t//if chg_length, wipe chg_length chars @chg_first.\n\t//if text, insert at chg_first (with per-char feedback properties)\n\t//if feedback (without text) then change text char flags\n\t//caret should then be moved accordingly.\n//\tCon_Printf(\"XIMPreEditDrawCallback %i %i %i %i %s\\n\", d->caret, d->chg_first, d->chg_length, d->text?d->text->encoding_is_wchar:0, d->text?d->text->string.multi_byte:\"???\");\n\tconchar_t *part[3];\n\tsize_t clen[3], c;\n\tconchar_t *n;\n\tunsigned int wc;\n\tunsigned int defaultfl = CON_WHITEMASK, fl;\n\n\tif (d->chg_length || (d->text && d->text->length))\n\t{\n\t\t//so inputs are in terms of chars.\n\t\t//our conchar_t struct is variable-sized (*sigh*), so we always use our longchar encoding.\n\t\t//so we end up with two conchars per wchar.\n\t\tpart[0] = vid.ime_preview;\n\t\tclen[0] = bound(0, d->chg_first, vid.ime_previewlen/2)*2;\n\t\tpart[1] = NULL;\n\t\tclen[1] = 0;\n\t\tpart[2] = part[0]+clen[0] + bound(0, d->chg_length, vid.ime_previewlen/2)*2;\n\t\tclen[2] = 0;\n\t\tif (part[2])\n\t\t\twhile (part[2][clen[2]])\n\t\t\t\tclen[2]+=1;\n\t\tif (d->text && d->text->encoding_is_wchar && d->text->string.wide_char)\n\t\t{\n\t\t\tpart[1] = alloca(d->text->length * 2*sizeof(wchar_t));\n\t\t\tfor (c = 0; c < d->text->length; c++)\n\t\t\t{\n\t\t\t\twc = d->text->string.wide_char[c];\n\t\t\t\tif (!wc)\n\t\t\t\t\tbreak; //erk? nulls confuse things\n\t\t\t\tpart[1][c*2+0] = CON_LONGCHAR|(wc>>16);\n\t\t\t\tpart[1][c*2+1] = defaultfl|(wc&CON_CHARMASK);\n\t\t\t}\n\t\t\tclen[1] = c*2;\n\t\t}\n\t\telse if (d->text && !d->text->encoding_is_wchar && d->text->string.multi_byte)\n\t\t{\n\t\t\tconst char *in = d->text->string.multi_byte;\n\t\t\tint error;\n\t\t\tpart[1] = alloca(d->text->length * 2*sizeof(wchar_t));\n\t\t\t//FIXME: d->text->length is meant to be chars, but fcitx always reports 1.\n\t\t\tfor (c = 0; c < d->text->length; c++)\n\t\t\t{\n\t\t\t\t//FIXME: This should use mbcstowcs, but that would require switching locales all the frikkin time, which isn't thread-safe.\n\t\t\t\twc = utf8_decode(&error, in, &in);\n\t\t\t\tif (!wc)\n\t\t\t\t\tbreak;\t//abort if there's a null...\n\t\t\t\tpart[1][c*2+0] = CON_LONGCHAR|(wc>>16);\n\t\t\t\tpart[1][c*2+1] = defaultfl|(wc&CON_CHARMASK);\n\t\t\t}\n\t\t\tclen[1] = c*2;\n\t\t}\n\n\t\tn = Z_Malloc((clen[0]+clen[1]+clen[2]+1)*sizeof(*n));\n\t\tmemcpy(n,\t\t\t\t\tpart[0],\tclen[0]*sizeof(*n));\n\t\tmemcpy(n+clen[0],\t\t\tpart[1],\tclen[1]*sizeof(*n));\n\t\tmemcpy(n+clen[0]+clen[1],\tpart[2],\tclen[2]*sizeof(*n));\n\t\tn[clen[0]+clen[1]+clen[2]] = 0;\n\t\tZ_Free(vid.ime_preview);\n\t\tvid.ime_preview = n;\n\t\tvid.ime_previewlen = clen[0]+clen[1]+clen[2];\n\t}\n\n\tif (d->text && d->text->feedback && d->chg_first >= 0 && d->chg_first+d->text->length <= vid.ime_previewlen/2)\n\t{\n\t\tfor (c = 0; c < d->text->length; c++)\n\t\t{\n\t\t\tfl = defaultfl;\n\t\t\tif (d->text->feedback[c] & XIMPrimary)\n\t\t\t\tfl = COLOR_RED<<CON_FGSHIFT;\n\t\t\tif (d->text->feedback[c] & XIMSecondary)\n\t\t\t\tfl = COLOR_GREEN<<CON_FGSHIFT;\n\t\t\tif (d->text->feedback[c] & XIMTertiary)\n\t\t\t\tfl = COLOR_BLUE<<CON_FGSHIFT;\n\t\t\tif (d->text->feedback[c] & XIMUnderline)\n\t\t\t\tfl = COLOR_MAGENTA<<CON_FGSHIFT;\n\t\t\tif (d->text->feedback[c] & XIMReverse)\n\t\t\t\tfl = ((fl&CON_FGMASK) << (CON_BGSHIFT-CON_FGSHIFT)) | ((fl&CON_BGMASK) >> (CON_BGSHIFT-CON_FGSHIFT));\n\t\t\tif (d->text->feedback[c] & XIMHighlight)\n\t\t\t\tfl |= CON_2NDCHARSETTEXT;\n\t\t\tvid.ime_preview[(d->chg_first+c)*2+1] = ((vid.ime_preview[(d->chg_first+c)*2+1])&~CON_FLAGSMASK)|fl;\n\t\t}\n\t}\n\n\tvid.ime_caret = bound(0, d->caret, vid.ime_previewlen/2)*2;\n}\nstatic void XIMPreEditCaretCallback(XIC ic, XPointer client_data, XIMPreeditCaretCallbackStruct *d)\n{\n//\tCon_Printf(\"XIMPreEditCaretCallback %i %i %i\\n\", d->direction, d->position, d->style);\n}\nstatic qboolean XIMSupportedStyle(XIMStyle preedit, XIMStyle status)\n{\n\tif (!(\t//preedit==XIMPreeditCallbacks||\t//FIXME: this should actually work, but we still need an ime that actually supports it properly to be sure.\n\t\t\t//preedit==XIMPreeditPosition||\n\t\t\t//preedit==XIMPreeditArea||\t\t\t//FIXME: assume the bottom half of the screen\n\t\t\tpreedit==XIMPreeditNothing||\n\t\t\tpreedit==XIMPreeditNone))\n\t\treturn false;\n\tif (!(\t//status==XIMStatusCallbacks||\n\t\t\t//status==XIMStatusArea||\n\t\t\tstatus==XIMStatusNothing||\n\t\t\tstatus==XIMStatusNone))\n\t\treturn false;\n\n\treturn true;\n}\nstatic XIMStyle XIMPreferredStyle(XIMStyle old, XIMStyle new)\n{\t//favour the more complicated (supported) preedit styles, *THEN* choose the preferred status style.\n\tXIMStyle p1 = old&0x00ff;\n\tXIMStyle p2 = new&0x00ff;\n\tXIMStyle s1 = old&0xff00;\n\tXIMStyle s2 = new&0xff00;\n\tif (!XIMSupportedStyle(p2, s2))\n\t\treturn old;\n\tif (!XIMSupportedStyle(p1, s1))\n\t\treturn new;\n\tif (p1 != p2)\n\t{\t//choose based upon the preedit flags\n\t\tif ((p1^p2)&XIMPreeditCallbacks)\t\t\t//FIXME: support this one properly some time.\n\t\t\treturn (p1&XIMPreeditCallbacks)?old:new;\n\t\tif ((p1^p2)&XIMPreeditPosition)\n\t\t\treturn (p1&XIMPreeditPosition)?old:new;\n\t\tif ((p1^p2)&XIMPreeditArea)\n\t\t\treturn (p1&XIMPreeditArea)?old:s2;\n\t\tif ((p1^p2)&XIMPreeditNothing)\n\t\t\treturn (p1&XIMPreeditNothing)?old:new;\n\t\tif ((p1^p2)&XIMPreeditNone)\n\t\t\treturn (p1&XIMPreeditNone)?old:new;\n\t}\n\telse\n\t{\t//preedit flags are equal, now pick the better\n\t\tif ((s1^s2)&XIMStatusCallbacks)\n\t\t\treturn (s1&XIMStatusCallbacks)?old:new;\n\t\tif ((s1^s2)&XIMStatusArea)\n\t\t\treturn (s1&XIMStatusArea)?old:new;\n\t\tif ((s1^s2)&XIMStatusNothing)\n\t\t\treturn (s1&XIMStatusNothing)?old:new;\n\t\tif ((s1^s2)&XIMStatusNone)\n\t\t\treturn (s1&XIMStatusNone)?old:new;\n\t}\n\n\t//difference not known. stick with the first\n\treturn old;\n}\n#include <locale.h>\nstatic long X_InitUnicode(void)\n{\n\tlong requiredevents = 0;\n\tX_ShutdownUnicode();\n\n\t//FIXME: enable by default if ubuntu's issue can ever be resolved.\n\tif (X11_CheckFeature(\"xim\", true))\n\t{\n\t\tif (x11.pXSetLocaleModifiers && x11.pXSupportsLocale && x11.pXOpenIM && x11.pXGetIMValues && x11.pXCreateIC && x11.pXSetICFocus && x11.pXUnsetICFocus && x11.pXGetICValues && x11.pXSetICValues && x11.pXFilterEvent && (x11.pXutf8LookupString || x11.pXwcLookupString) && x11.pXDestroyIC && x11.pXCloseIM)\n\t\t{\n\t\t\tsetlocale(LC_ALL, \"\");\t//just in case.\n\t\t\tx11.pXSetLocaleModifiers(\"\");\n\n\t\t\tif (x11.pXSupportsLocale())\n\t\t\t{\n\t\t\t\tx11.inputmethod = x11.pXOpenIM(vid_dpy, NULL, XI_RESOURCENAME, XI_RESOURCECLASS);\n\t\t\t\tif (x11.inputmethod)\n\t\t\t\t{\n\t\t\t\t\tXIMStyles *sup = NULL;\n\t\t\t\t\tXIMStyle st = 0;\n\t\t\t\t\tint i;\n\t\t\t\t\tx11.pXGetIMValues(x11.inputmethod, XNQueryInputStyle, &sup, NULL);\n\t\t\t\t\tfor (i = 0; sup && i < sup->count_styles; i++)\n\t\t\t\t\t\tst = XIMPreferredStyle(st, sup->supported_styles[i]);\n\t\t\t\t\tx11.pXFree(sup);\n\t\t\t\t\tCon_DPrintf(\"Chosen XIM Input Style: %x\\n\", (unsigned)st);\n//\t\t\t\t\tst=XIMPreeditCallbacks|XIMStatusArea;\n//\t\t\t\t\tst=XIMPreeditCallbacks|XIMStatusNothing;\n\t\t\t\t\tif (st != 0)\n\t\t\t\t\t{\n\t\t\t\t\t\tXIMCallback pe_cb_start={NULL,(XIMProc)XIMPreEditStartCallback};\n\t\t\t\t\t\tXIMCallback pe_cb_done={NULL,(XIMProc)XIMPreEditDoneCallback};\n\t\t\t\t\t\tXIMCallback pe_cb_draw={NULL,(XIMProc)XIMPreEditDrawCallback};\n\t\t\t\t\t\tXIMCallback pe_cb_caret={NULL,(XIMProc)XIMPreEditCaretCallback};\n\t\t\t\t\t\tvoid *preedit[] = {\n\t\t\t\t\t\t\t\t//should probably add in fonts, but that's kinda messy if we don't know the language/charset very well\n\t\t\t\t\t\t\t\tXNPreeditStartCallback,\t&pe_cb_start,\n\t\t\t\t\t\t\t\tXNPreeditDoneCallback,\t&pe_cb_done,\n\t\t\t\t\t\t\t\tXNPreeditDrawCallback,\t&pe_cb_draw,\n\t\t\t\t\t\t\t\tXNPreeditCaretCallback,\t&pe_cb_caret,\n\t\t\t\t\t\t\t\tXNSpotLocation,\t\t\t&x11.ime_pos,\n\t\t\t\t\t\t\t\tNULL};\n\t\t\t\t\t\tvoid *status[] = {\n\t\t\t\t\t\t\t\tNULL};\n\n\t\t\t\t\t\tx11.unicodecontext = x11.pXCreateIC(x11.inputmethod,\n\t\t\t\t\t\t\tXNInputStyle, st,\n\t\t\t\t\t\t\tXNClientWindow, vid_window,\n//\t\t\t\t\t\t\tXNFocusWindow, vid_window,\n\t\t\t\t\t\t\tXNResourceName, XI_RESOURCENAME,\n\t\t\t\t\t\t\tXNResourceClass, XI_RESOURCECLASS,\n\t\t\t\t\t\t\tXNPreeditAttributes, preedit,\n\t\t\t\t\t\t\tXNStatusAttributes, status,\n\t\t\t\t\t\t\tNULL);\n\t\t\t\t\t\tif (x11.unicodecontext)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tx11.ime_shown = -1;\n\t\t\t\t\t\t\tx11.dounicode = true;\n\n\t\t\t\t\t\t\tx11.pXGetICValues(x11.unicodecontext, XNFilterEvents, &requiredevents, NULL);\n\t\t\t\t\t\t\trequiredevents |= KeyPressMask;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tsetlocale(LC_ALL, \"C\");\n\t\t}\n\t}\n\n\tCon_DPrintf(\"Unicode support: %s\\n\", x11.dounicode?\"available\":\"unavailable\");\n\n\treturn requiredevents;\n}\n\n#define XKEY_QUAKE_MAP()\t\t\t\\\n\tXKQM(XK_KP_Page_Up,\t\tK_KP_PGUP)\t\t\\\n\tXKQM(XK_Page_Up,\t\tK_PGUP)\t\t\t\\\n\tXKQM(XK_KP_Page_Down,\tK_KP_PGDN)\t\t\\\n\tXKQM(XK_Page_Down,\t\tK_PGDN)\t\t\t\\\n\tXKQM(XK_KP_Home,\t\tK_KP_HOME)\t\t\\\n\tXKQM(XK_Home,\t\t\tK_HOME)\t\t\t\\\n\tXKQM(XK_KP_End,\t\t\tK_KP_END)\t\t\\\n\tXKQM(XK_End,\t\t\tK_END)\t\t\t\\\n\tXKQM(XK_KP_Left,\t\tK_KP_LEFTARROW)\t\\\n\tXKQM(XK_Left,\t\t\tK_LEFTARROW)\t\\\n\tXKQM(XK_KP_Right,\t\tK_KP_RIGHTARROW)\\\n\tXKQM(XK_Right,\t\t\tK_RIGHTARROW)\t\\\n\tXKQM(XK_KP_Down,\t\tK_KP_DOWNARROW)\t\\\n\tXKQM(XK_Down,\t\t\tK_DOWNARROW)\t\\\n\tXKQM(XK_KP_Up,\t\t\tK_KP_UPARROW)\t\\\n\tXKQM(XK_Up,\t\t\t\tK_UPARROW)\t\t\\\n\tXKQM(XK_Escape,\t\t\tK_ESCAPE)\t\t\\\n\tXKQM(XK_KP_Enter,\t\tK_KP_ENTER)\t\t\\\n\tXKQM(XK_Return,\t\t\tK_ENTER)\t\t\\\n\tXKQM(XK_Num_Lock,\t\tK_KP_NUMLOCK)\t\\\n\tXKQM(XK_Caps_Lock,\t\tK_CAPSLOCK)\t\t\\\n\tXKQM(XK_Scroll_Lock,\tK_SCRLCK)\t\t\\\n\tXKQM(XK_Print,\t\t\tK_PRINTSCREEN)\t\\\n\tXKQM(XK_Super_L,\t\tK_LWIN)\t\t\t\\\n\tXKQM(XK_Super_R,\t\tK_RWIN)\t\t\t\\\n\tXKQM(XK_Tab,\t\t\tK_TAB)\t\t\t\\\n\tXKQM(XK_F1,\t\t\t\tK_F1)\t\t\t\\\n\tXKQM(XK_F2,\t\t\t\tK_F2)\t\t\t\\\n\tXKQM(XK_F3,\t\t\t\tK_F3)\t\t\t\\\n\tXKQM(XK_F4,\t\t\t\tK_F4)\t\t\t\\\n\tXKQM(XK_F5,\t\t\t\tK_F5)\t\t\t\\\n\tXKQM(XK_F6,\t\t\t\tK_F6)\t\t\t\\\n\tXKQM(XK_F7,\t\t\t\tK_F7)\t\t\t\\\n\tXKQM(XK_F8,\t\t\t\tK_F8)\t\t\t\\\n\tXKQM(XK_F9,\t\t\t\tK_F9)\t\t\t\\\n\tXKQM(XK_F10,\t\t\tK_F10)\t\t\t\\\n\tXKQM(XK_F11,\t\t\tK_F11)\t\t\t\\\n\tXKQM(XK_F12,\t\t\tK_F12)\t\t\t\\\n\tXKQM(XK_F13,\t\t\tK_F13)\t\t\t\\\n\tXKQM(XK_F14,\t\t\tK_F14)\t\t\t\\\n\tXKQM(XK_F15,\t\t\tK_F15)\t\t\t\\\n\tXKQM(XK_BackSpace,\t\tK_BACKSPACE)\t\\\n\tXKQM(XK_KP_Delete,\t\tK_KP_DEL)\t\t\\\n\tXKQM(XK_Delete,\t\t\tK_DEL)\t\t\t\\\n\tXKQM(XK_Pause,\t\t\tK_PAUSE)\t\t\\\n\tXKQM(XK_Shift_L,\t\tK_LSHIFT)\t\t\\\n\tXKQM(XK_Shift_R,\t\tK_RSHIFT)\t\t\\\n\t/*XKQM2(XK_Execute,\t\tK_LCTRL)*/\t\t\\\n\tXKQM(XK_Control_L,\t\tK_LCTRL)\t\t\\\n\tXKQM(XK_Control_R,\t\tK_RCTRL)\t\t\\\n\tXKQM(XK_Alt_L,\t\t\tK_LALT)\t\t\t\\\n\t/*XKQM2(XK_Meta_L,\t\tK_LWIN)*/\t\t\\\n\tXKQM(XK_Alt_R,\t\t\tK_RALT)\t\t\t\\\n\t/*XKQM2(XK_Meta_R,\t\tK_RWIN)*/\t\t\\\n\tXKQM(XK_Menu,\t\t\tK_APP)\t\t\t\\\n\tXKQM(XK_KP_Begin,\t\tK_KP_5)\t\t\t\\\n\tXKQM(XK_KP_Insert,\t\tK_KP_INS)\t\t\\\n\tXKQM(XK_Insert,\t\t\tK_INS)\t\t\t\\\n\tXKQM(XK_KP_Multiply,\tK_KP_STAR)\t\t\\\n\tXKQM(XK_KP_Add,\t\t\tK_KP_PLUS)\t\t\\\n\tXKQM(XK_KP_Subtract,\tK_KP_MINUS)\t\t\\\n\tXKQM(XK_KP_Divide,\t\tK_KP_SLASH)\n\n\nqboolean INS_KeyToLocalName(int qkey, char *buf, size_t bufsize)\n{\n\tchar *s;\n\tint xk;\n\t*buf = 0;\n\tif(vid_dpy)\n\t{\n\t\tswitch(qkey)\n\t\t{\n#define XKQM(x,q) case q: xk = x; break;\nXKEY_QUAKE_MAP()\n#undef XKQM\n\t\tdefault:\n\t\t\tif (qkey >= K_SPACE && qkey < 127)\n\t\t\t\txk = qkey;\t//printable ascii is identity-mapped in both\n\t\t\telse\n\t\t\t\treturn false;\t//don't know how to map this. might not be a keyboard key.\n\t\t\tbreak;\n\t\t}\n\t\ts = x11.pXKeysymToString(xk);\n\t\tif (s)\n\t\t\tQ_strncpyz(buf, s, bufsize);\n\t}\n\treturn !!*buf;\n}\n\nstatic void X_KeyEvent(XKeyEvent *ev, qboolean pressed, qboolean filtered)\n{\n\tint i;\n\tint key;\n\tKeySym keysym, shifted;\n\tunsigned int unichar[64];\n\tint unichars = 0;\n\tkey = 0;\n\n\tkeysym = x11.pXLookupKeysym(ev, 0);\n\tif (pressed && !filtered)\n\t{\n\t\tif (x11.dounicode && vid.ime_allow)\n\t\t{\n\t\t\tStatus status = XLookupNone;\n\t\t\tif (x11.pXutf8LookupString)\n\t\t\t{\n\t\t\t\tchar buf1[512] = {0};\n\t\t\t\tchar *buf = buf1;\n\t\t\t\tconst char *c;\n\t\t\t\tint count = x11.pXutf8LookupString(x11.unicodecontext, (XKeyPressedEvent*)ev, buf1, sizeof(buf1), NULL, &status);\n\t\t\t\tif (status == XBufferOverflow)\n\t\t\t\t{\n\t\t\t\t\tbuf = alloca(count+1);\n\t\t\t\t\tcount = x11.pXutf8LookupString(x11.unicodecontext, (XKeyPressedEvent*)ev, buf, count, NULL, &status);\n\t\t\t\t}\n\t\t\t\tfor (c = buf; c < &buf[count]; )\n\t\t\t\t{\n\t\t\t\t\tint error;\n\t\t\t\t\tunsigned int uc = utf8_decode(&error, c, &c);\n\t\t\t\t\tif (uc)\n\t\t\t\t\t\tunichar[unichars++] = uc;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t//is allowed some weird encodings...\n\t\t\t\twchar_t buf1[512] = {0};\n\t\t\t\twchar_t *buf = buf1;\n\t\t\t\tint count = x11.pXwcLookupString(x11.unicodecontext, (XKeyPressedEvent*)ev, buf, sizeof(buf1), &shifted, &status);\n\t\t\t\tif (status == XBufferOverflow)\n\t\t\t\t{\n\t\t\t\t\tbuf = alloca(sizeof(wchar_t)*(count+1));\n\t\t\t\t\tcount = x11.pXwcLookupString(x11.unicodecontext, (XKeyPressedEvent*)ev, buf, count, NULL, &status);\n\t\t\t\t}\n\t\t\t\t//if wchar_t is 16bit, then expect problems when we completely ignore surrogates. this is why we favour the utf8 route as it doesn't care whether wchar_t is defined as 16bit or 32bit.\n\t\t\t\tfor (i = 0; i < count; i++)\n\t\t\t\t\tif (buf[i])\n\t\t\t\t\t\tunichar[unichars++] = buf[i];\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tchar buf[64];\n\t\t\tif ((keysym & 0xff000000) == 0x01000000)\n\t\t\t\tunichar[unichars++] = keysym & 0x00ffffff;\n\t\t\telse\n\t\t\t{\n\t\t\t\tint count = x11.pXLookupString(ev, buf, sizeof(buf), &shifted, 0);\n\t\t\t\tfor (i = 0; i < count; i++)\n\t\t\t\t\tif (buf[i])\n\t\t\t\t\t\tunichar[unichars++] = (unsigned char)buf[i];\n\t\t\t}\n\t\t}\n\t}\n\n\tswitch(keysym)\n\t{\n#define XKQM(x,q) case x: key = q; break;\nXKEY_QUAKE_MAP()\n#undef XKQM\n\n\t\tdefault:\n\t\t\tkey = keysym;\n\t\t\tif (key < 32)\n\t\t\t\tkey = 0;\n\t\t\telse if (key > 127)\n\t\t\t\tkey = 0;\n\t\t\telse if (key >= 'A' && key <= 'Z')\n\t\t\t\tkey = key - 'A' + 'a';\n\t\t\tbreak;\n\t}\n\n\tif (unichars)\n\t{\n\t\t//we got some text, this is fun isn't it?\n\t\t//the key value itself is sent with the last text char. this avoids multiple presses, and dead keys were already sent.\n\t\tfor (i = 0; i < unichars-1; i++)\n\t\t{\n\t\t\tIN_KeyEvent(0, pressed, 0, unichar[i]);\n\t\t}\n\t\tIN_KeyEvent(0, pressed, key, unichar[i]);\n\t}\n\telse\n\t{\n\t\t//no text available, just do the keypress\n\t\tIN_KeyEvent(0, pressed, key, 0);\n\t}\n}\n\nstatic void install_grabs(void)\n{\n\tif (!mouse_grabbed)\n\t{\n\t\t//XGrabPointer can cause alt+tab type shortcuts to be skipped by the window manager. This means we don't want to use it unless we have no choice.\n\t\t//the grab is purely to constrain the pointer to the window\n\t\tif (GrabSuccess != x11.pXGrabPointer(vid_dpy, DefaultRootWindow(vid_dpy),\n\t\t\t\t\tTrue,\n\t\t\t\t\t0,\n\t\t\t\t\tGrabModeAsync, GrabModeAsync,\n\t\t\t\t\tvid_window,\n\t\t\t\t\tNone,\n\t\t\t\t\tCurrentTime))\n\t\t{\n\t\t\tCon_Printf(\"Pointer grab failed\\n\");\n\t\t\treturn;\n\t\t}\n\n\t\tCon_DLPrintf(2, \"Grabbed mouse\\n\");\n\t\tmouse_grabbed = true;\n\n\t\tif (x11_input_method == XIM_DGA)\n\t\t{\n\t\t\tdgam.pXF86DGADirectVideo(vid_dpy, DefaultScreen(vid_dpy), XF86DGADirectMouse);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tx11.pXWarpPointer(vid_dpy, None, vid_window,\n\t\t\t\t\t\t 0, 0, 0, 0,\n\t\t\t\t\t\t vid.pixelwidth / 2, vid.pixelheight / 2);\n\t\t}\n\n//\t\tx11.pXSync(vid_dpy, True);\n\t}\n}\n\nstatic void uninstall_grabs(void)\n{\n\tif (mouse_grabbed && vid_dpy)\n\t{\n\t\tCon_DLPrintf(2, \"Releasing mouse grab\\n\");\n\t\tmouse_grabbed = false;\n\t\tif (x11_input_method == XIM_DGA)\n\t\t{\n\t\t\tdgam.pXF86DGADirectVideo(vid_dpy, DefaultScreen(vid_dpy), 0);\n\t\t}\n\n\t\tx11.pXUngrabPointer(vid_dpy, CurrentTime);\n\n//\t\tx11.pXSync(vid_dpy, True);\n\n\t\tif (!vid.forcecursor)\n\t\t{\n\t\t\tvid.forcecursor = true;\n\t\t\tvid.forcecursorpos[0] = vid.pixelwidth/2;\n\t\t\tvid.forcecursorpos[1] = vid.pixelheight/2;\n\t\t}\n\t}\n\n\tif (vid.forcecursor)\n\t{\n\t\tvid.forcecursor = false;\n\t\tx11.pXWarpPointer(vid_dpy, vid_window, vid_window,\n\t\t\t\t\t\t 0, 0, vid.pixelwidth, vid.pixelheight,\n\t\t\t\t\t\t vid.forcecursorpos[0], vid.forcecursorpos[1]);\n\t}\n}\n\nstatic void UpdateGrabs(void)\n{\n\tqboolean wantmgrabs, allownullcursor;\n\tCursor wantcursor;\n\n\twantmgrabs = (fullscreenflags&FULLSCREEN_ACTIVE) || !!in_windowed_mouse.value;\n\tif (!vid.activeapp)\n\t\twantmgrabs = false;\n\tallownullcursor = wantmgrabs;\t//this says whether we can possibly want it. if false then we disallow the null cursor. Yes, this might break mods that do their own sw cursors. such mods are flawed in other ways too.\n\tif (!vrui.enabled && Key_MouseShouldBeFree())\n\t\twantmgrabs = false;\n//\tif (modeswitchpending)\n//\t\twantmgrabs = false;\n\n\tif (wantmgrabs)\n\t\tinstall_grabs();\n\telse\n\t\tuninstall_grabs();\n\n\tif (mouse_grabbed)\n\t\twantcursor = vid_nullcursor;\n\telse if (vid_newcursor)\n\t\twantcursor = vid_newcursor;\n\telse if (!allownullcursor)\n\t\twantcursor = None;\n\telse\n\t\twantcursor = vid_nullcursor;\n\tif (wantcursor != vid_activecursor)\n\t{\n\t\tvid_activecursor = wantcursor;\n\t\tif (vid_activecursor)\n\t\t\tx11.pXDefineCursor(vid_dpy, vid_window, vid_activecursor);\n\t\telse\n\t\t\tx11.pXUndefineCursor(vid_dpy, vid_window);\n\t}\n\n\tX11Xss_SuspendSaver(cls.demoplayback && vid.activeapp);\n}\n\nstatic void ClearAllStates (void)\n{\n\tint\t\ti;\n\n// send an up event for each key, to make sure the server clears them all\n\tfor (i=0 ; i<K_MAX ; i++)\n\t{\n\t\tKey_Event (0, i, 0, false);\n\t}\n\n\tKey_ClearStates ();\n//\tIN_ClearStates ();\n}\n\nstatic void GetEvent(void)\n{\n\tXEvent event, rep;\n\tint b;\n\tqboolean x11violations = true;\n\tWindow mw;\n\tqboolean filtered = false;\n\n\tx11.pXNextEvent(vid_dpy, &event);\n\n\tif (x11.dounicode)\n\t{\n\t\tif (x11.pXFilterEvent(&event, None))//vid_window))\n\t\t{\n\t\t\tif (vid.ime_allow)\n\t\t\t\treturn;\n\t\t\tfiltered = true;\n\t\t}\n\t}\n\n\tswitch (event.type)\n\t{\n\tcase GenericEvent:\n\t\tif (x11.pXGetEventData(vid_dpy, &event.xcookie))\n\t\t{\n\t\t\tif (event.xcookie.extension == xi2.opcode)\n\t\t\t{\n\t\t\t\tswitch(event.xcookie.evtype)\n\t\t\t\t{\n\t\t\t\tcase XI_RawButtonPress:\n\t\t\t\tcase XI_RawButtonRelease:\n\t\t\t\t\tif (mouse_grabbed)\n\t\t\t\t\t{\n\t\t\t\t\t\tXIRawEvent *raw = event.xcookie.data;\n\t\t\t\t\t\tint *qdev = &XI2_GetDeviceInfo(raw->sourceid)->qdev;\n\t\t\t\t\t\tint button = raw->detail;\t//1-based\n\t\t\t\t\t\tif (raw->sourceid != raw->deviceid)\n\t\t\t\t\t\t\treturn;\t//ignore master devices to avoid dupes.\n\t\t\t\t\t\tif (*qdev == DEVID_UNSET)\n\t\t\t\t\t\t\t*qdev = xi2.nextqdev++;\n\t\t\t\t\t\tswitch(button)\n\t\t\t\t\t\t{\n\t\t\t\t\t\tcase 1: button = K_MOUSE1; break;\n\t\t\t\t\t\tcase 2: button = K_MOUSE3; break;\n\t\t\t\t\t\tcase 3: button = K_MOUSE2; break;\n\t\t\t\t\t\tcase 4: button = K_MWHEELUP; break;\t//so much for 'raw'.\n\t\t\t\t\t\tcase 5: button = K_MWHEELDOWN; break;\n\t\t\t\t\t\tcase 6: button = K_MWHEELLEFT; break;\t//so much for 'raw'.\n\t\t\t\t\t\tcase 7: button = K_MWHEELRIGHT; break;\n\t\t\t\t\t\tcase 8: button = K_MOUSE4; break;\n\t\t\t\t\t\tcase 9: button = K_MOUSE5; break;\n\t\t\t\t\t\tcase 10: button = K_MOUSE6; break;\n\t\t\t\t\t\tcase 11: button = K_MOUSE7; break;\n\t\t\t\t\t\tcase 12: button = K_MOUSE8; break;\n\t\t\t\t\t\tcase 13: button = K_MOUSE9; break;\n\t\t\t\t\t\tcase 14: button = K_MOUSE10; break;\n\t\t\t\t\t\tdefault:button = 0; break;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (button)\n\t\t\t\t\t\t\tIN_KeyEvent(*qdev, (event.xcookie.evtype==XI_RawButtonPress), button, 0);\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase XI_RawMotion:\n\t\t\t\t\tif (mouse_grabbed)\n\t\t\t\t\t{\n\t\t\t\t\t\tXIRawEvent *raw = event.xcookie.data;\n\t\t\t\t\t\tstruct xidevinfo *dev = XI2_GetDeviceInfo(raw->sourceid);\n\t\t\t\t\t\tdouble *val, *raw_val;\n\t\t\t\t\t\tdouble axis[2] = {0, 0};\n\t\t\t\t\t\tint i;\n\t\t\t\t\t\tif (raw->sourceid != raw->deviceid)\n\t\t\t\t\t\t\treturn;\t//ignore master devices to avoid dupes (we have our own device remapping stuff which should be slightly more friendly).\n\t\t\t\t\t\tif (dev->qdev == DEVID_UNSET)\n\t\t\t\t\t\t\tdev->qdev = xi2.nextqdev++;\n\t\t\t\t\t\tval = raw->valuators.values;\n\t\t\t\t\t\traw_val = raw->raw_values;\n\t\t\t\t\t\tif (dev->abs)\n\t\t\t\t\t\t\taxis[0] = axis[1] = FLT_MIN;\n\t\t\t\t\t\tfor (i = 0; i < raw->valuators.mask_len * 8; i++)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (XIMaskIsSet(raw->valuators.mask, i))\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tif (i == 0) axis[0] = *raw_val;\n\t\t\t\t\t\t\t\tif (i == 1) axis[1] = *raw_val;\n\t\t\t\t\t\t\t\tval++;\n\t\t\t\t\t\t\t\traw_val++;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (dev->abs)\n\t\t\t\t\t\t{\t//tablets use weird 16bit coords or whatever. rescale to window coords (we have grabs, so offscreen stuff shouldn't matter).\n\t\t\t\t\t\t\tfor (i = 0; i < 2; i++)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tif (axis[i] == FLT_MIN)\n\t\t\t\t\t\t\t\t\taxis[i] = dev->axis[i].old;\n\t\t\t\t\t\t\t\tdev->axis[i].old = axis[i];\n\t\t\t\t\t\t\t\taxis[i] = (axis[i]-dev->axis[i].min) / (dev->axis[1].max+1-dev->axis[i].min);\n\t\t\t\t\t\t\t\taxis[i] *= i?vid.pixelheight:vid.pixelwidth;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tIN_MouseMove(dev->qdev, dev->abs, axis[0], axis[1], 0, 0);\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tCon_Printf(\"Unknown xinput event %u!\\n\", event.xcookie.evtype);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t\tCon_Printf(\"Unknown generic event!\\n\");\n\t\t}\n\t\tx11.pXFreeEventData(vid_dpy, &event.xcookie);\n\t\tbreak;\n\tcase ResizeRequest:\n#ifdef VKQUAKE\n\t\tvk.neednewswapchain = true;\n#endif\n\t\tvid.pixelwidth = event.xresizerequest.width;\n\t\tvid.pixelheight = event.xresizerequest.height;\n\t\tCvar_ForceCallback(&vid_conautoscale);\n//\t\tif (fullscreenflags & FULLSCREEN_ACTIVE)\n//\t\t\tx11.pXMoveWindow(vid_dpy, vid_window, 0, 0);\n\t\tbreak;\n\tcase ConfigureNotify:\n\t\tif (event.xconfigurerequest.window == vid_window)\n\t\t{\n#ifdef VKQUAKE\n\t\t\tvk.neednewswapchain = true;\n#endif\n\t\t\tvid.pixelwidth = event.xconfigurerequest.width;\n\t\t\tvid.pixelheight = event.xconfigurerequest.height;\n\t\t\tCvar_ForceCallback(&vid_conautoscale);\n\t\t}\n\t\telse if (event.xconfigurerequest.window == vid_decoywindow)\n\t\t{\n\t\t\tif (!(fullscreenflags & FULLSCREEN_ACTIVE))\n\t\t\t\tx11.pXResizeWindow(vid_dpy, vid_window, event.xconfigurerequest.width, event.xconfigurerequest.height);\n\t\t}\n//\t\tif (fullscreenflags & FULLSCREEN_ACTIVE)\n//\t\t\tx11.pXMoveWindow(vid_dpy, vid_window, 0, 0);\n\t\tbreak;\n\tcase KeyPress:\n\t\tX_KeyEvent(&event.xkey, true, filtered);\n\t\tbreak;\n\tcase KeyRelease:\n\t\tif (x11.pXPending(vid_dpy))\n\t\t{\t//autorepeat is messy - if the next event is a press event for the same key then ignore the release (we still get presses doing their autorepeat thing, just not the releases)\n\t\t\tXEvent nextev;\n\t\t\tx11.pXPeekEvent(vid_dpy, &nextev);\t//blocks, so needs XPending\n\t\t\tif (nextev.type == KeyPress && nextev.xkey.time == event.xkey.time && nextev.xkey.keycode == event.xkey.keycode)\n\t\t\t\tbreak;\n\t\t}\n\t\tX_KeyEvent(&event.xkey, false, filtered);\n\t\tbreak;\n\n\tcase MotionNotify:\n\t\tif (x11_input_method == XIM_DGA && mouse_grabbed)\n\t\t{\n\t\t\tIN_MouseMove(x11_mouseqdev, false, event.xmotion.x_root, event.xmotion.y_root, 0, 0);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (mouse_grabbed)\n\t\t\t{\n\t\t\t\tif (x11_input_method != XIM_XI2)\n\t\t\t\t{\n\t\t\t\t\tint cx = vid.pixelwidth/2, cy=vid.pixelheight/2;\n\n\t\t\t\t\tIN_MouseMove(x11_mouseqdev, false, event.xmotion.x - cx, event.xmotion.y - cy, 0, 0);\n\n\t\t\t\t\t/* move the mouse to the window center again (disabling warp first so we don't see it*/\n\t\t\t\t\tx11.pXSelectInput(vid_dpy, vid_window, vid_x_eventmask & ~PointerMotionMask);\n\t\t\t\t\tx11.pXWarpPointer(vid_dpy, None, vid_window, 0, 0, 0, 0,\n\t\t\t\t\t\tcx, cy);\n\t\t\t\t\tx11.pXSelectInput(vid_dpy, vid_window, vid_x_eventmask);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tIN_MouseMove(x11_mouseqdev, true, event.xmotion.x, event.xmotion.y, 0, 0);\n\t\t\t}\n\t\t}\n\t\tbreak;\n\n\tcase ButtonPress:\n\t\tif (x11_input_method == XIM_XI2 && mouse_grabbed)\n\t\t\tbreak;\t//no dupes!\n\t\tb=-1;\n\t\tif (event.xbutton.button == 1)\n\t\t\tb = K_MOUSE1;\n\t\telse if (event.xbutton.button == 2)\n\t\t\tb = K_MOUSE3;\n\t\telse if (event.xbutton.button == 3)\n\t\t\tb = K_MOUSE2;\n\t\t//note, the x11 protocol does not support a mousewheel\n\t\t//we only support it because we follow convention. the actual protocol specifies 4+5 as regular buttons\n\t\telse if (event.xbutton.button == 4)\n\t\t\tb = x11violations?K_MWHEELUP:K_MOUSE4;\n\t\telse if (event.xbutton.button == 5)\n\t\t\tb = x11violations?K_MWHEELDOWN:K_MOUSE5;\n\t\t//note, the x11 protocol does not support more than 5 mouse buttons\n\t\t//which is a bit of a shame, but hey.\n\t\telse if (event.xbutton.button == 6)\n\t\t\tb = x11violations?K_MWHEELLEFT:-1;\n\t\telse if (event.xbutton.button == 7)\n\t\t\tb = x11violations?K_MWHEELRIGHT:-1;\n\t\telse if (event.xbutton.button == 8)\n\t\t\tb = x11violations?K_MOUSE4:-1;\n\t\telse if (event.xbutton.button == 9)\n\t\t\tb = x11violations?K_MOUSE5:-1;\n\t\telse if (event.xbutton.button == 10)\n\t\t\tb = x11violations?K_MOUSE6:-1;\n\t\telse if (event.xbutton.button == 11)\n\t\t\tb = x11violations?K_MOUSE7:-1;\n\t\telse if (event.xbutton.button == 12)\n\t\t\tb = x11violations?K_MOUSE8:-1;\n\n\t\tif (b>=0)\n\t\t\tIN_KeyEvent(x11_mouseqdev, true, b, 0);\n\n/*\n\t\tif (fullscreenflags & FULLSCREEN_LEGACY)\n\t\tif (fullscreenflags & FULLSCREEN_VMODE)\n\t\tif (!vid.activeapp)\n\t\t{\t//KDE doesn't seem to like us, in that you can't alt-tab back or click to activate.\n\t\t\t//This allows us to steal input focus back from the window manager\n\t\t\tx11.pXSetInputFocus(vid_dpy, vid_window, RevertToParent, CurrentTime);\n\t\t}\n*/\n\t\tbreak;\n\n\tcase ButtonRelease:\n\t\tb=-1;\n\t\tif (event.xbutton.button == 1)\n\t\t\tb = K_MOUSE1;\n\t\telse if (event.xbutton.button == 2)\n\t\t\tb = K_MOUSE3;\n\t\telse if (event.xbutton.button == 3)\n\t\t\tb = K_MOUSE2;\n\t\t//note, the x11 protocol does not support a mousewheel\n\t\t//we only support it because we follow convention. the actual protocol specifies 4+5 as regular buttons\n\t\telse if (event.xbutton.button == 4)\n\t\t\tb = x11violations?K_MWHEELUP:K_MOUSE4;\n\t\telse if (event.xbutton.button == 5)\n\t\t\tb = x11violations?K_MWHEELDOWN:K_MOUSE5;\n\t\t//note, the x11 protocol does not support more than 5 mouse buttons\n\t\t//which is a bit of a shame, but hey.\n\t\telse if (event.xbutton.button == 6)\n\t\t\tb = x11violations?K_MWHEELLEFT:-1;\n\t\telse if (event.xbutton.button == 7)\n\t\t\tb = x11violations?K_MWHEELRIGHT:-1;\n\t\telse if (event.xbutton.button == 8)\n\t\t\tb = x11violations?K_MOUSE4:-1;\n\t\telse if (event.xbutton.button == 9)\n\t\t\tb = x11violations?K_MOUSE5:-1;\n\t\telse if (event.xbutton.button == 10)\n\t\t\tb = x11violations?K_MOUSE6:-1;\n\t\telse if (event.xbutton.button == 11)\n\t\t\tb = x11violations?K_MOUSE7:-1;\n\t\telse if (event.xbutton.button == 12)\n\t\t\tb = x11violations?K_MOUSE8:-1;\n\n\t\tif (b>=0)\n\t\t\tIN_KeyEvent(x11_mouseqdev, false, b, 0);\n\t\tbreak;\n\n\tcase FocusIn:\n\t\t//don't care about it if its just someone wiggling the mouse\n\t\tif (event.xfocus.detail == NotifyPointer)\n\t\t\tbreak;\n\t\t//activeapp is if the game window is focused\n\t\tvid.activeapp = true;\n\t\tClearAllStates();\t//just in case.\n\n\t\t//but change modes to track the desktop window\n//\t\tif (!(fullscreenflags & FULLSCREEN_ACTIVE) || event.xfocus.window != vid_decoywindow)\n\t\t{\n\t\t\tmodeswitchpending = 1;\n\t\t\tmodeswitchtime = Sys_Milliseconds() + 1500;\t/*fairly slow, to make sure*/\n\t\t\tUpdateGrabs();\n\t\t}\n\n\t\tif (event.xfocus.window == vid_window)\n\t\t\tx11.ime_shown = -1;\n\n\t\t//we we're focusing onto the game window and we're currently fullscreen, hide the other one so alt-tab won't select that instead of a real alternate app.\n//\t\tif ((fullscreenflags & FULLSCREEN_ACTIVE) && (fullscreenflags & FULLSCREEN_LEGACY) && event.xfocus.window == vid_window)\n//\t\t\tx11.pXUnmapWindow(vid_dpy, vid_decoywindow);\n\t\tbreak;\n\tcase FocusOut:\n\t\t//don't care about it if its just someone wiggling the mouse\n\t\tif (event.xfocus.detail == NotifyPointer)\n\t\t\tbreak;\n\t\t//if we're already active, the decoy window shouldn't be focused anyway.\n\t\tif (event.xfocus.window == vid_window)\n\t\t\tx11.ime_shown = -1;\n\n\t\tif ((fullscreenflags & FULLSCREEN_ACTIVE) && event.xfocus.window == vid_decoywindow)\n\t\t{\n\t\t\tbreak;\n\t\t}\n\n#ifdef USE_XRANDR\n\t\tif (xrandr.origgamma)\n\t\t\txrandr.pSetCrtcGamma(vid_dpy, xrandr.crtc, xrandr.origgamma);\n#endif\n#ifdef USE_VMODE\n\t\tif (vm.originalapplied)\n\t\t\tvm.pXF86VidModeSetGammaRamp(vid_dpy, scrnum, vm.originalrampsize, vm.originalramps[0], vm.originalramps[1], vm.originalramps[2]);\n#endif\n\n\t\tmw = vid_window;\n\t\tif ((fullscreenflags & FULLSCREEN_LEGACY) && (fullscreenflags & FULLSCREEN_ACTIVE))\n\t\t\tmw = vid_decoywindow;\n\n\t\tif (event.xfocus.window == mw || event.xfocus.window == vid_window)\n\t\t{\n\t\t\tvid.activeapp = false;\n\t\t\tUpdateGrabs();\n\t\t\tClearAllStates();\n\t\t}\n\t\tmodeswitchpending = -1;\n\t\tmodeswitchtime = Sys_Milliseconds() + 100;\t/*fairly fast, so we don't unapply stuff when switching to other progs with delays*/\n\t\tbreak;\n\tcase ClientMessage:\n\t\t{\n\t\t\tchar *name = x11.pXGetAtomName(vid_dpy, event.xclient.message_type);\n\t\t\tif (!strcmp(name, \"WM_PROTOCOLS\") && event.xclient.format == 32)\n\t\t\t{\n\t\t\t\tchar *protname = x11.pXGetAtomName(vid_dpy, event.xclient.data.l[0]);\n\t\t\t\tif (!strcmp(protname, \"WM_DELETE_WINDOW\"))\n\t\t\t\t{\n\t\t\t\t\tx11.pXSetInputFocus(vid_dpy, vid_window, RevertToParent, CurrentTime); //make it easier to pick an option. FIXME: bring to top is a separate thing.\n\t\t\t\t\tM_Window_ClosePrompt();\n\t\t\t\t}\n\t\t\t\telse if (!strcmp(protname, \"_NET_WM_PING\"))\n\t\t\t\t{\n\t\t\t\t\tevent.xclient.window = vid_root;\n\t\t\t\t\tx11.pXSendEvent(vid_dpy, vid_root, false, SubstructureNotifyMask|SubstructureRedirectMask, &event);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tCon_DPrintf(\"Got unknown x11wm message %s\\n\", protname);\n\t\t\t\tx11.pXFree(protname);\n\t\t\t}\n#if 1\n\t\t\telse if (!strcmp(name, \"XdndEnter\") && event.xclient.format == 32)\n\t\t\t{\n\t\t\t\t//check for text/uri-list\n\t\t\t\tint i;\n\t\t\t\tx11.dnd.type = None;\n\t\t\t\tfor (i = 2; i < 2+3; i++)\n\t\t\t\t{\n\t\t\t\t\tif (event.xclient.data.l[i])\n\t\t\t\t\t{\n\t\t\t\t\t\tchar *t = x11.pXGetAtomName(vid_dpy, event.xclient.data.l[i]);\n#ifdef XDS\n\t\t\t\t\t\t//direct-save has no way to deal with multiple files, other than lying about it.\n\t\t\t\t\t\t//which is unfortunately what other programs do. we have no real way to tell which file(s) actually got dragged/written.\n\t\t\t\t\t\t//we would need to report an empty directory, then scan for new files, then wipe the lot recursively.\n\t\t\t\t\t\tif (!strcmp(t, \"XdndDirectSave0\") && !x11.dnd.type)\t//single file\n\t\t\t\t\t\t\tx11.dnd.type = event.xclient.data.l[i];\n#endif\n\t\t\t\t\t\tif (!strcmp(t, \"text/uri-list\"))\t//file list\n\t\t\t\t\t\t\tx11.dnd.type = event.xclient.data.l[i];\n//\t\t\t\t\t\telse if (!strcmp(t, \"application/octet-stream\"))\t//raw file data without a name.\n//\t\t\t\t\t\t\tx11.dnd.type = event.xclient.data.l[i];\n\t\t\t\t\t\tx11.pXFree(t);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (!strcmp(name, \"XdndPosition\") && event.xclient.format == 32)\n\t\t\t{\n\t\t\t\t//Send XdndStatus\n\t\t\t\tXEvent xev;\n\t\t\t\tmemset(&xev, 0, sizeof(xev));\n\t\t\t\txev.type = ClientMessage;\n\t\t\t\txev.xclient.window = event.xclient.data.l[0];\n\t\t\t\txev.xclient.message_type = x11.pXInternAtom(vid_dpy, \"XdndStatus\", False);\n\t\t\t\txev.xclient.format = 32;\n\t\t\t\txev.xclient.data.l[0] = vid_window;\t//so source can ignore it if stale\n\t\t\t\txev.xclient.data.l[1] = x11.dnd.type?1:0;\n\t\t\t\txev.xclient.data.l[2] = 0;\t//(x<<16)|y (should be in root coords)\n\t\t\t\txev.xclient.data.l[3] = 0;\t//(w<<16)|h\n\t\t\t\txev.xclient.data.l[4] = x11.pXInternAtom (vid_dpy, \"XdndActionCopy\", False);\n\t\t\t\tx11.pXSendEvent(vid_dpy, xev.xclient.window, False, 0, &xev);\n\t\t\t}\n\t\t\telse if (!strcmp(name, \"XdndLeave\") && event.xclient.format == 32)\n\t\t\t{\n\t\t\t\tif (x11.dnd.source == event.xclient.data.l[0])\n\t\t\t\t{\n\t\t\t\t\tx11.dnd.source = None;\n\t\t\t\t\tx11.dnd.type = None;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (!strcmp(name, \"XdndDrop\") && event.xclient.format == 32)\n\t\t\t{\n\t\t\t\tAtom xa_XdndSelection = x11.pXInternAtom(vid_dpy, \"XdndSelection\", False);\n\t\t\t\tWindow source = event.xclient.data.l[0];\n\t\t\t\tTime t = CurrentTime;//event.xclient.data.l[2];\n\n#ifdef XDS\n\t\t\t\tchar *droptype = x11.dnd.type?x11.pXGetAtomName(vid_dpy, x11.dnd.type):NULL;\n\t\t\t\tif (droptype && !strcmp(droptype, \"XdndDirectSave0\"))\t//single file\n\t\t\t\t{\n\t\t\t\t\tunsigned char *data = NULL;\n\t\t\t\t\tAtom type;\n\t\t\t\t\tint fmt;\n\t\t\t\t\tunsigned long nitems;\n\t\t\t\t\tunsigned long bytesleft;\n\t\t\t\t\tif (x11.pXGetWindowProperty(vid_dpy, source, x11.dnd.type, 0, 65536, False, AnyPropertyType, &type, &fmt, &nitems, &bytesleft, &data) == Success && data)\n\t\t\t\t\t{\n\t\t\t\t\t\tchar hostname[1024];\n\t\t\t\t\t\tif (gethostname(hostname, sizeof(hostname)) < 0)\n\t\t\t\t\t\t\t*hostname = 0;\t//failed? o.O\n\t\t\t\t\t\thostname[sizeof(hostname)-1] = 0;\n\t\t\t\t\t\tchar *path = va(\"file://%s/tmp/%s\", hostname, data);\n\t\t\t\t\t\tAtom proptype = x11.pXInternAtom(vid_dpy, \"text/plain\", false);\n\t\t\t\t\t\tx11.pXChangeProperty(vid_dpy, source, x11.dnd.type, proptype, 8, PropModeReplace, (void*)path, strlen(path));\n\t\t\t\t\t\tCon_Printf(\"Dropping file %s\\n\", data);\n\t\t\t\t\t\tx11.pXFree(data);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tx11.pXFree(droptype);\n#endif\n\n\t\t\t\tx11.dnd.myprop = x11.pXInternAtom(vid_dpy, \"_FTE_dnd\", False);\n\t\t\t\tif (x11.pXGetSelectionOwner(vid_dpy, xa_XdndSelection) == source)\n\t\t\t\t{\n\t\t\t\t\tx11.pXDeleteProperty(vid_dpy, vid_window, x11.dnd.myprop);\n\t\t\t\t\tx11.pXConvertSelection(vid_dpy, xa_XdndSelection, x11.dnd.type, x11.dnd.myprop, vid_window, t);\n\t\t\t\t}\n\t\t\t}\n#endif\n\t\t\telse\n\t\t\t\tCon_DPrintf(\"Got unknown x11 message %s\\n\", name);\n\t\t\tx11.pXFree(name);\n\t\t}\n\t\tbreak;\n#if 1\n\tcase SelectionNotify:\n\t\t//for paste\n\t\tif (X11_Clipboard_Notify(&event.xselection))\n\t\t\t;\n\t\t//for drag-n-drop\n\t\telse if (event.xselection.selection == x11.pXInternAtom(vid_dpy, \"XdndSelection\", False) && x11.dnd.myprop != None)\n\t\t{\n\t\t\tqboolean okay = false;\n\t\t\tunsigned char *data;\n\t\t\tAtom type;\n\t\t\tint fmt;\n\t\t\tunsigned long nitems;\n\t\t\tunsigned long bytesleft;\n\t\t\tif (x11.pXGetWindowProperty(vid_dpy, vid_window, x11.dnd.myprop, 0, 65536, False, AnyPropertyType, &type, &fmt, &nitems, &bytesleft, &data) == Success && data)\n\t\t\t{\n\t\t\t\tchar *tname = x11.pXGetAtomName(vid_dpy, x11.dnd.type);\n\t\t\t\tif (type == x11.dnd.type && !strcmp(tname, \"text/uri-list\"))\n\t\t\t\t{\n\t\t\t\t\tchar *start, *end;\n\t\t\t\t\tfor (start = data; *start; )\n\t\t\t\t\t{\n\t\t\t\t\t\tfor (end = start; *end && *end != '\\r'; end++)\n\t\t\t\t\t\t\t;\n\t\t\t\t\t\tif (end != start)\n\t\t\t\t\t\t\tHost_RunFile(start, end-start, NULL);\n\t\t\t\t\t\tstart = end;\n\t\t\t\t\t\twhile (*start == '\\r' || *start == '\\n')\n\t\t\t\t\t\t\tstart++;\n\t\t\t\t\t}\n\t\t\t\t\tokay = true;\n\t\t\t\t}\n#ifdef XDS\n\t\t\t\telse if (type == x11.dnd.type && !strcmp(tname, \"XdndDirectSave0\") && nitems == 1)\n\t\t\t\t{\n\t\t\t\t\tswitch(data[0])\n\t\t\t\t\t{\n\t\t\t\t\tcase 'S':\t//sender wrote the file\n\t\t\t\t\tcase 'E':\t//sender failed to generate the data or something\n\t\t\t\t\tcase 'F':\t//sender failed to write the file. we should use application/octet-stream and write it ourself.\n\t\t\t\t\t}\n\t\t\t\t}\n#endif\n\t\t\t\tx11.pXFree(tname);\n\t\t\t\tx11.pXFree(data);\n\t\t\t}\n\t\t\tx11.pXDeleteProperty(vid_dpy, vid_window, x11.dnd.myprop);\t//might be large, so don't force it to hang around.\n\n\t\t\t//Send XdndFinished now\n\t\t\t{\n\t\t\t\tXEvent xev;\n\t\t\t\tmemset(&xev, 0, sizeof(xev));\n\t\t\t\txev.type = ClientMessage;\n\t\t\t\txev.xclient.window = x11.dnd.source;\n\t\t\t\txev.xclient.message_type = x11.pXInternAtom(vid_dpy, \"XdndFinished\", False);\n\t\t\t\txev.xclient.format = 32;\n\t\t\t\txev.xclient.data.l[0] = vid_window;\t//so source can ignore it if stale\n\t\t\t\txev.xclient.data.l[1] = (okay?1:0);\n\t\t\t\txev.xclient.data.l[2] = x11.pXInternAtom (vid_dpy, \"XdndActionCopy\", False);\n\t\t\t\tx11.pXSendEvent(vid_dpy, xev.xclient.window, False, 0, &xev);\n\t\t\t}\n\t\t}\n\t\tbreak;\n\n\tcase SelectionRequest:\t//needed for when another program tries pasting.\n\t\t{\n\t\t\tAtom xa_u8string = x11.pXInternAtom(vid_dpy, \"UTF8_STRING\", false);\t//explicitly UTF-8\n\t\t\tAtom xa_l1string = x11.pXInternAtom(vid_dpy, \"STRING\", false);\t//explicitly 8859-1\n\t\t\tAtom xa_text = x11.pXInternAtom(vid_dpy, \"TEXT\", false);\t//selection owner decides encoding (and we pick UTF-8)\n\t\t\tAtom xa_targets = x11.pXInternAtom(vid_dpy, \"TARGETS\", false);\n\t\t\tAtom xa_supportedtargets[] = {xa_u8string, xa_l1string, xa_text, xa_targets/*, xa_multiple, xa_timestamp*/};\n\t\t\tchar *cliptext = NULL;\n\t\t\tmemset(&rep, 0, sizeof(rep));\n\n\t\t\tif (event.xselectionrequest.selection == x11.pXInternAtom(vid_dpy, \"PRIMARY\", false))\n\t\t\t\tcliptext = clipboard_buffer[CBT_SELECTION];\n\t\t\telse if (event.xselectionrequest.selection == x11.pXInternAtom(vid_dpy, \"CLIPBOARD\", false))\n\t\t\t\tcliptext = clipboard_buffer[CBT_CLIPBOARD];\n\t\t\tif (!cliptext)\t//err, nothing in the clipboard buffer... that's not meant to happen.\n\t\t\t\tcliptext = \"\";\n\n\t\t\tif (event.xselectionrequest.property == None)\t//no property sets a property matching the target atom, as a fallback.\n\t\t\t\tevent.xselectionrequest.property = event.xselectionrequest.target;\n\t\t\tif (event.xselectionrequest.property == None)\n\t\t\t\tevent.xselectionrequest.property = x11.pXInternAtom(vid_dpy, \"foobar2000\", false);\n\t\t\tif (event.xselectionrequest.property != None && event.xselectionrequest.target == xa_targets)\n\t\t\t{\t//TARGETS results in a list of accepted target types (atoms)\n\t\t\t\tx11.pXChangeProperty(vid_dpy, event.xselectionrequest.requestor, event.xselectionrequest.property, event.xselectionrequest.target, 32, PropModeReplace, (void*)xa_supportedtargets, countof(xa_supportedtargets));\n\t\t\t\trep.xselection.property = event.xselectionrequest.property;\n\t\t\t}\n\t\t\telse if (event.xselectionrequest.property != None && (event.xselectionrequest.target == xa_u8string || event.xselectionrequest.target == xa_text))\n\t\t\t{\t//UTF8_STRING or TEXT (which we choose to use utf-8 as our charset)\n\t\t\t\tx11.pXChangeProperty(vid_dpy, event.xselectionrequest.requestor, event.xselectionrequest.property, event.xselectionrequest.target, 8, PropModeReplace, (void*)cliptext, strlen(cliptext));\n\t\t\t\trep.xselection.property = event.xselectionrequest.property;\n\t\t\t}\n\t\t\telse if (event.xselectionrequest.property != None && event.xselectionrequest.target == xa_l1string)\n\t\t\t{\t//STRING == latin1. convert as needed.\n\t\t\t\tchar *latin1 = alloca(strlen(cliptext)+1);\t//may shorten\n\t\t\t\tconst char *in = cliptext;\n\t\t\t\tint c = 0;\n\t\t\t\tint err;\n\t\t\t\twhile (*in && c < sizeof(latin1))\n\t\t\t\t{\n\t\t\t\t\tint uc = utf8_decode(&err, in, &in);\n\t\t\t\t\tif ((uc >= 0xe000 && uc <= 0xe100) && (uc&0x7f) >= 32)\n\t\t\t\t\t\tuc = uc&0x7f;\t//don't do c0/c1 glyphs. otherwise treat as ascii.\n\t\t\t\t\telse if (uc > 255 || err)\n\t\t\t\t\t\tuc = '?';\t//unsupported char\n\t\t\t\t\tlatin1[c++] = uc;\n\t\t\t\t}\n\t\t\t\tx11.pXChangeProperty(vid_dpy, event.xselectionrequest.requestor, event.xselectionrequest.property, event.xselectionrequest.target, 8, PropModeReplace, (void*)latin1, c);\n\t\t\t\trep.xselection.property = event.xselectionrequest.property;\n\t\t\t}\n\t\t\telse\n\t\t\t{\t//unsupported target. we need to let them know that we don't know what they're asking for.\n\t\t\t\trep.xselection.property = None;\n\t\t\t}\n\t\t\trep.xselection.type = SelectionNotify;\n\t\t\trep.xselection.serial = 0;\n\t\t\trep.xselection.send_event = true;\n\t\t\trep.xselection.display = rep.xselection.display;\n\t\t\trep.xselection.requestor = event.xselectionrequest.requestor;\n\t\t\trep.xselection.selection = event.xselectionrequest.selection;\n\t\t\trep.xselection.target = event.xselectionrequest.target;\n\t\t\trep.xselection.time = event.xselectionrequest.time;\n\t\t\tx11.pXSendEvent(vid_dpy, event.xselectionrequest.requestor, 0, 0, &rep);\n\t\t}\n\t\tbreak;\n#endif\n\n\tdefault:\n//\t\tCon_Printf(\"%x\\n\", event.type);\n\t\tbreak;\n\t}\n}\n\n\nstatic void GLVID_Shutdown(void)\n{\n\tif (!vid_dpy)\n\t\treturn;\n\tvid_activecursor = None;\n\tvid_newcursor = None;\n\tx11.pXUngrabKeyboard(vid_dpy, CurrentTime);\n\tuninstall_grabs();\n\n#ifdef USE_VMODE\n\tif (vm.originalapplied)\n\t\tvm.pXF86VidModeSetGammaRamp(vid_dpy, scrnum, vm.originalrampsize, vm.originalramps[0], vm.originalramps[1], vm.originalramps[2]);\n#endif\n\n\tX_ShutdownUnicode();\n\n\tswitch(currentpsl)\n\t{\n#ifdef GLQUAKE\n#ifdef USE_EGL\n\tcase PSL_EGL:\n\t\tEGL_Shutdown();\n\t\tGL_ForgetPointers();\n\t\tbreak;\n#endif\n\tcase PSL_GLX:\n\t\tif (ctx)\n\t\t{\n\t\t\tglx.DestroyContext(vid_dpy, ctx);\n\t\t\tctx = NULL;\n\t\t}\n\t\tGL_ForgetPointers();\n\t\tbreak;\n#endif\n#ifdef VKQUAKE\n\tcase PSL_VULKAN:\n\t\tVK_Shutdown();\n\t\tbreak;\n#endif\n\tcase PSL_NONE:\n\t\tbreak;\n\t}\n\n#ifdef USE_XRANDR\n\tXRandR_Shutdown();\n#endif\n\n\tif (vid_window)\n\t\tx11.pXDestroyWindow(vid_dpy, vid_window);\n\tif (vid_decoywindow)\n\t\tx11.pXDestroyWindow(vid_dpy, vid_decoywindow);\n\tif (vid_nullcursor)\n\t\tx11.pXFreeCursor(vid_dpy, vid_nullcursor);\n\tif (vid_dpy)\n\t{\n#ifdef USE_VMODE\n\t\tif (fullscreenflags & FULLSCREEN_VMODEACTIVE)\n\t\t\tvm.pXF86VidModeSwitchToMode(vid_dpy, scrnum, vm.modes[0]);\n\t\tfullscreenflags &= ~FULLSCREEN_VMODEACTIVE;\n\n\t\tif (vm.modes)\n\t\t\tx11.pXFree(vm.modes);\n\t\tvm.modes = NULL;\n\t\tvm.num_modes = 0;\n#endif\n\t\tx11.pXCloseDisplay(vid_dpy);\n\t}\n\tvid_dpy = NULL;\n\tvid_window = (Window)NULL;\n\n\tswitch(currentpsl)\n\t{\n#ifdef GLQUAKE\n#ifdef USE_EGL\n\tcase PSL_EGL:\n\t\tEGL_UnloadLibrary();\n\t\tbreak;\n#endif\n#endif\n\tdefault:\n\t\tbreak;\n\t}\n\n\tcurrentpsl = PSL_NONE;\n}\n\nvoid GLVID_DeInit(void)\t//FIXME:....\n{\n\tGLVID_Shutdown();\n}\n\nstatic Cursor CreateNullCursor(Display *display, Window root)\n{\n\tPixmap cursormask;\n\tXGCValues xgc;\n\tGC gc;\n\tXColor dummycolour = {0};\n\tCursor cursor;\n\n\tcursormask = x11.pXCreatePixmap(display, root, 1, 1, 1/*depth*/);\n\txgc.function = GXclear;\n\tgc =  x11.pXCreateGC(display, cursormask, GCFunction, &xgc);\n\tx11.pXFillRectangle(display, cursormask, gc, 0, 0, 1, 1);\n\tdummycolour.flags = 04;\n\tcursor = x11.pXCreatePixmapCursor(display, cursormask, cursormask,\n\t\t&dummycolour,&dummycolour, 0,0);\n\tx11.pXFreePixmap(display,cursormask);\n\tx11.pXFreeGC(display,gc);\n\treturn cursor;\n}\n\n#ifndef NO_X11_CURSOR\n#include <X11/Xcursor/Xcursor.h>\nstatic struct\n{\n\tvoid *lib;\n\n\tXcursorBool (*SupportsARGB) (Display *dpy);\n\tXcursorImage *(*ImageCreate) (int width, int height);\n\tCursor (*ImageLoadCursor) (Display *dpy, const XcursorImage *image);\n\tvoid (*ImageDestroy) (XcursorImage *image);\n} xcursor;\nstatic void *X11VID_CreateCursorRGBA(const qbyte *rgbacursor, uploadfmt_t format, size_t w, size_t h, float hotx, float hoty)\n{\n\tCursor *cursor;\n\tsize_t x, y;\n\tXcursorImage *img;\n\tXcursorPixel *dest;\n\n\timg = xcursor.ImageCreate(w, h);\n\timg->xhot = hotx;\n\timg->yhot = hoty;\n\tdest = img->pixels;\n\n\tswitch (format)\n\t{\n\tcase PTI_BGRA8:\n\tcase PTI_BGRX8:\n\t\tfor (y = 0; y < h; y++)\n\t\t\tfor (x = 0; x < w; x++, rgbacursor+=4)\n\t\t\t\t*dest++ = (rgbacursor[3]<<24)|(rgbacursor[2]<<16)|(rgbacursor[1]<<8)|(rgbacursor[0]<<0);\t//0xARGB\n\t\tbreak;\t//supported...\n\tcase PTI_RGBA8:\n\tcase PTI_RGBX8:\n\tcase PTI_LLLA8:\n\tcase PTI_LLLX8:\n\t\tfor (y = 0; y < h; y++)\n\t\t\tfor (x = 0; x < w; x++, rgbacursor+=4)\n\t\t\t\t*dest++ = (rgbacursor[3]<<24)|(rgbacursor[0]<<16)|(rgbacursor[1]<<8)|(rgbacursor[2]<<0);\t//0xARGB\n\t\tbreak;\n\tdefault:\n\t\t//panic... format wasn't supported. I hope we didn't spend ages resampling it...\n\t\txcursor.ImageDestroy(img);\n\t\treturn NULL;\n\t}\n\n\tcursor = Z_Malloc(sizeof(*cursor));\n\t*cursor = xcursor.ImageLoadCursor(vid_dpy, img);\n\txcursor.ImageDestroy(img);\n\tif (*cursor)\n\t\treturn (void*)cursor;\n\tZ_Free(cursor);\n\treturn NULL;\n}\nstatic void *X11VID_CreateCursor(const qbyte *imagedata, int width, int height, uploadfmt_t format, float hotx, float hoty, float scale)\n{\n\tvoid *r;\n\tif (!imagedata)\n\t\treturn NULL;\n\n\tif (scale != 1)\n\t{\n\t\tint nw,nh;\n\t\tqbyte *nd;\n\t\tfor(;;)\n\t\t{\n\t\t\tnw = width * scale;\n\t\t\tnh = height * scale;\n\t\t\tif (nw <= 0 || nh <= 0 || nw > 128 || nh > 128) //don't go crazy.\n\t\t\t\treturn NULL;\n\t\t\tif (nw < 8)\n\t\t\t\tscale = 8/width;\n\t\t\telse if (nh < 8)\n\t\t\t\tscale = 8/height;\n\t\t\telse\n\t\t\t\tbreak;\n\t\t}\n\t\tnd = Image_ResampleTexture(format, imagedata, width, height, NULL, nw, nh);\n\t\tif (!nd)\n\t\t\treturn NULL;\t//resampling of that format didn't work for some reason...\n\t\twidth = nw;\n\t\theight = nh;\n\t\tr = X11VID_CreateCursorRGBA(nd, format, width, height, hotx, hoty);\n\t\tBZ_Free(nd);\n\t}\n\telse\n\t\tr = X11VID_CreateCursorRGBA(imagedata, format, width, height, hotx, hoty);\n\n\treturn r;\n}\nstatic void X11VID_DestroyCursor(void *qcursor)\n{\n\tCursor c = *(Cursor*)qcursor;\n\tif (vid_newcursor == c)\n\t\tvid_newcursor = None;\n\tif (vid_dpy)\n\t\tx11.pXFreeCursor(vid_dpy, c);\n\tZ_Free(qcursor);\n}\nstatic qboolean X11VID_SetCursor(void *qcursor)\n{\n\tif (qcursor)\n\t\tvid_newcursor = *(Cursor*)qcursor;\n\telse\n\t\tvid_newcursor = None;\n\n\tif (vid_dpy)\n\t\tUpdateGrabs();\n\treturn true;\n}\nstatic qboolean XCursor_Init(void)\n{\n\tstatic dllfunction_t xcursor_functable[] =\n\t{\n\t\t{(void**)&xcursor.SupportsARGB,\t\t\"XcursorSupportsARGB\"},\n\t\t{(void**)&xcursor.ImageCreate,\t\t\"XcursorImageCreate\"},\n\t\t{(void**)&xcursor.ImageLoadCursor,\t\"XcursorImageLoadCursor\"},\n\t\t{(void**)&xcursor.ImageDestroy,\t\t\"XcursorImageDestroy\"},\n\t\t{NULL, NULL}\n\t};\n\tqboolean defaulthwcursor = true;\n\n#ifdef GLQUAKE\n\tif (qrenderer == QR_OPENGL && !strcmp(gl_vendor, \"Humper\") && !strcmp(gl_renderer, \"Chromium\"))\n\t{\t//I don't really understand the significance of these two values, but we get them when running inside VirtualBox\n\t\t//in such cases, the opengl window has a nasty habit of appearing top-most, even above mouse cursors...\n\t\t//so we NEED to disable hardware cursors by default in this case, because otherwise we won't see any\n\t\t//(the cursor should be visible with mouse integration enabled, but then XWarpPointer fails without error. unplayable both ways to some extent)\n\t\tCon_Printf(\"VirtualBox Detected: OpenGL obscures hardware cursors. seta x11_allow_xcursor 1 to ignore.\\n\");\n\t\tdefaulthwcursor = false;\n\t}\n#endif\n\n\t//in case they were previously set...\n\trf->VID_CreateCursor = NULL;\n\trf->VID_DestroyCursor = NULL;\n\trf->VID_SetCursor = NULL;\n\n\tif (!X11_CheckFeature(\"xcursor\", defaulthwcursor))\n\t{\n\t\tCon_Printf(\"Hardware cursors disabled.\\n\");\n\t\treturn false;\n\t}\n\n\tif (!xcursor.lib)\n\t{\n#ifdef __CYGWIN__\n\t\tif (!xcursor.lib)\n\t\t\txcursor.lib = Sys_LoadLibrary(\"cygXcursor.dll\", xcursor_functable);\n#endif\n\t\tif (!xcursor.lib)\n\t\t\txcursor.lib = Sys_LoadLibrary(\"libXcursor.so.1\", xcursor_functable);\n\t\tif (!xcursor.lib)\n\t\t\txcursor.lib = Sys_LoadLibrary(\"libXcursor.so\", xcursor_functable);\n\t\tif (!xcursor.lib)\n\t\t\tCon_Printf(\"Xcursor library not available or too old.\\n\");\n\t}\n\tif (xcursor.lib)\n\t{\n\t\tif (xcursor.SupportsARGB(vid_dpy))\n\t\t{\t//okay, we should be able to use argb hardware cursors, so set up our public function pointers\n\t\t\trf->VID_CreateCursor = X11VID_CreateCursor;\n\t\t\trf->VID_DestroyCursor = X11VID_DestroyCursor;\n\t\t\trf->VID_SetCursor = X11VID_SetCursor;\n\t\t\treturn true;\n\t\t}\n\t}\n\tCon_Printf(\"Hardware cursors unsupported.\\n\");\n\treturn false;\n}\n#else\nstatic qboolean XCursor_Init(void)\n{\n\treturn false;\n}\n#endif\n\nqboolean GLVID_ApplyGammaRamps(unsigned int rampcount, unsigned short *ramps)\n{\n\textern qboolean gammaworks;\n\n\tif (ramps)\n\t{\n\t\tswitch(vid_hardwaregamma.ival)\n\t\t{\n\t\tcase 0:\t//never use hardware/glsl gamma\n\t\tcase 2:\t//ALWAYS use glsl gamma\n\t\t\treturn false;\n\t\tdefault:\n\t\tcase 1:\t//no hardware gamma when windowed\n\t\t\tif (!(fullscreenflags & FULLSCREEN_ACTIVE))\n\t\t\t\treturn false;\n\t\t\tbreak;\n\t\tcase 3:\t//ALWAYS try to use hardware gamma, even when it fails...\n\t\t\tbreak;\n\t\t}\n\t}\n\n#ifdef USE_XRANDR\n\tif (xrandr.origgamma)\n\t{\t//we favour xrandr - xf86 gamma seems to cache old values which screws up gamma after having previously quit ezquake.\n\t\tif (ramps && rampcount == xrandr.origgamma->size)\n\t\t{\n\t\t\tXRRCrtcGamma g;\n\t\t\tg.size = rampcount;\n\t\t\tg.red = &ramps[0];\n\t\t\tg.green = &ramps[rampcount];\n\t\t\tg.blue = &ramps[rampcount*2];\n\t\t\tif (vid.activeapp)\n\t\t\t{\n\t\t\t\tif (gammaworks)\n\t\t\t\t\txrandr.pSetCrtcGamma(vid_dpy, xrandr.crtc, &g);\n\t\t\t\tgammaworks = true;\n\t\t\t\treturn gammaworks;\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\t\telse if (gammaworks)\n\t\t{\n\t\t\txrandr.pSetCrtcGamma(vid_dpy, xrandr.crtc, xrandr.origgamma);\n\t\t\treturn true;\n\t\t}\n\t}\n#endif\n\n#ifdef USE_VMODE\n\t//if we don't know the original ramps yet, don't allow changing them, because we're probably invalid anyway, and even if it worked, it'll break something later.\n\tif (vm.originalapplied)\n\t{\n\n\t\tif (ramps && rampcount == vm.originalrampsize)\n\t\t{\n\t\t\tif (vid.activeapp)\n\t\t\t{\n\t\t\t\tif (gammaworks)\n\t\t\t\t\tvm.pXF86VidModeSetGammaRamp (vid_dpy, scrnum, rampcount, &ramps[0], &ramps[rampcount], &ramps[rampcount*2]);\n\t\t\t\telse\n\t\t\t\t\tgammaworks = !!vm.pXF86VidModeSetGammaRamp (vid_dpy, scrnum, rampcount, &ramps[0], &ramps[rampcount], &ramps[rampcount*2]);\n\t\t\t\treturn gammaworks;\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\t\telse if (gammaworks)\n\t\t{\n\t\t\tvm.pXF86VidModeSetGammaRamp(vid_dpy, scrnum, vm.originalrampsize, vm.originalramps[0], vm.originalramps[1], vm.originalramps[2]);\n\t\t\treturn true;\n\t\t}\n\t}\n#endif\n\treturn false;\n}\n\nvoid GLVID_SwapBuffers (void)\n{\n\tswitch(currentpsl)\n\t{\n#ifdef GLQUAKE\n#ifdef USE_EGL\n\tcase PSL_EGL:\n\t\tEGL_SwapBuffers();\n\t\tbreak;\n#endif\n\tcase PSL_GLX:\n\t\t{\n\t\t\tint n = vid_vsync.ival;\n\t\t\tif (cls.timedemo && cls.demoplayback)\n\t\t\t\tn = 0;\n\t\t\tif (!glx.swaptear)\n\t\t\t\tn = abs(n);\n\t\t\tif (glx.swapint != n)\n\t\t\t{\n\t\t\t\tglx.swapint = n;\n\t\t\t\tif (glx.SwapIntervalEXT)\n\t\t\t\t{\n\t\t\t\t\tglx.SwapIntervalEXT(vid_dpy, vid_window, glx.swapint);\n\t\t\t\t\tCon_DPrintf(\"Swap interval changed to %i\\n\", glx.swapint);\n\t\t\t\t}\n\t\t\t\telse if (glx.SwapIntervalMESA && glx.swapint>=0)\n\t\t\t\t{\n\t\t\t\t\tglx.SwapIntervalMESA(glx.swapint);\n\t\t\t\t\tCon_DPrintf(\"Swap interval changed to %i\\n\", glx.swapint);\n\t\t\t\t}\n\t\t\t\telse if (glx.SwapIntervalSGI && glx.swapint>0)\n\t\t\t\t{\n\t\t\t\t\tglx.SwapIntervalSGI(glx.swapint);\n\t\t\t\t\tCon_DPrintf(\"Swap interval changed to %i\\n\", glx.swapint);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tCon_Printf(\"Unable to change swap interval to %i\\n\", glx.swapint);\n\t\t\t}\n\t\t}\n\n\t\t//we don't need to flush, XSawpBuffers does it for us.\n\t\t//chances are, it's version is more suitable anyway. At least there's the chance that it might be.\n\t\tglx.SwapBuffers(vid_dpy, vid_window);\n\t\tbreak;\n#endif\n#ifdef VKQUAKE\n\tcase PSL_VULKAN:\n\t\tbreak;\n#endif\n\tdefault:\n\tcase PSL_NONE:\n\t\tbreak;\n\t}\n}\n\n#include \"fte_eukara64.h\"\n//#include \"bymorphed.h\"\nstatic void X_StoreIcon(Window wnd)\n{\n\tint i;\n\tAtom propname = x11.pXInternAtom(vid_dpy, \"_NET_WM_ICON\", false);\n\tAtom proptype = x11.pXInternAtom(vid_dpy, \"CARDINAL\", false);\n\n\tsize_t filesize = 0;\n\tqbyte *filedata = NULL;\n#ifdef IMAGEFMT_PNG\n\tif (!filedata)\n\t\tfiledata = FS_LoadMallocFile(\"icon.png\", &filesize);\n#endif\n\tif (!filedata)\n\t\tfiledata = FS_LoadMallocFile(\"icon.tga\", &filesize);\n#ifdef IMAGEFMT_JPG\n\tif (!filedata)\n\t\tfiledata = FS_LoadMallocFile(\"icon.jpg\", &filesize);\n#endif\n#ifdef IMAGEFMT_BMP\n\tif (!filedata)\n\t\tfiledata = FS_LoadMallocFile(\"icon.ico\", &filesize);\n#endif\n#ifdef HAVE_LEGACY\n\tif (!filedata)\n\t\tfiledata = FS_LoadMallocFile(\"darkplaces-icon.tga\", &filesize);\n#endif\n\tif (filedata)\n\t{\n\t\tint imagewidth, imageheight;\n\t\tunsigned long *iconblob;\t//yes, long, even on 64bit machines. and even when we claim it to be 32bit. xlib legacy cruft that'll get stripped...\n\t\tuploadfmt_t format;\n\t\tqbyte *imagedata = ReadRawImageFile(filedata, filesize, &imagewidth, &imageheight, &format, true, \"icon.png\");\n\t\tZ_Free(filedata);\n\n\t\tif (imagedata)\n\t\t{\n\t\t\ticonblob = BZ_Malloc(sizeof(*iconblob)*(2+imagewidth*imageheight));\n\t\t\ticonblob[0] = imagewidth;\n\t\t\ticonblob[1] = imageheight;\n\t\t\t//needs to be 0xARGB, rather than RGBA bytes\n\t\t\tfor (i = 0; i < imagewidth*imageheight; i++)\n\t\t\t\ticonblob[i+2] = (imagedata[i*4+3]<<24) | (imagedata[i*4+0]<<16) | (imagedata[i*4+1]<<8) | (imagedata[i*4+2]<<0);\n\t\t\tZ_Free(imagedata);\n\n\t\t\tx11.pXChangeProperty(vid_dpy, wnd, propname, proptype, 32, PropModeReplace, (void*)iconblob, 2+imagewidth*imageheight);\n\t\t\tBZ_Free(iconblob);\n\t\t\treturn;\n\t\t}\n\t}\n\n\t{\n\t\t//fall back to the embedded icon.\n\t\tunsigned long data[64*64+2];\n\t\tdata[0] = icon.width;\n\t\tdata[1] = icon.height;\n\n\t\t/* GIMP exports dumps as RGBA only, so we have to convert them too - eukara */\n\t\tfor (i = 0; i < data[0]*data[1]; i++)\n\t\t\tdata[i+2] = (icon.pixel_data[i*4+3]<<24) | (icon.pixel_data[i*4+0]<<16) | (icon.pixel_data[i*4+1]<<8) | (icon.pixel_data[i*4+2]<<0);\n\n\t\tx11.pXChangeProperty(vid_dpy, wnd, propname, proptype, 32, PropModeReplace, (void*)data, data[0]*data[1]+2);\n\t}\n}\n\nstatic void X_StorePID(Window wnd)\n{\n\tAtom net_wm_pid  = x11.pXInternAtom(vid_dpy, \"_NET_WM_PID\", false);\n\tAtom wm_client_machine = x11.pXInternAtom(vid_dpy, \"WM_CLIENT_MACHINE\", false);\n\tAtom cardinal = x11.pXInternAtom(vid_dpy, \"CARDINAL\", false);\n\tAtom string = x11.pXInternAtom(vid_dpy, \"STRING\", false);\n\tlong pid = getpid();\n\t#ifndef HOST_NAME_MAX\n\t#define HOST_NAME_MAX 255\n\t#endif\n\tchar hostname[HOST_NAME_MAX+1];\n\t*hostname = 0;\n\tif (gethostname(hostname, sizeof(hostname)) < 0)\n\t\treturn;\t//just give up. if the hostname isn't valid then the pid won't be useful anyway.\n\thostname[countof(hostname)-1] = 0;\t//make sure its null terminated... *sigh*\n\n\tx11.pXChangeProperty(vid_dpy, wnd, wm_client_machine, string, 8, PropModeReplace, hostname, strlen(hostname));\n\tx11.pXChangeProperty(vid_dpy, wnd, net_wm_pid, cardinal, 32, PropModeReplace, (void*)&pid, 1);\n}\n\nvoid X_GoFullscreen(void)\n{\n\tXEvent xev;\n\t\n\t//for NETWM window managers\n\tmemset(&xev, 0, sizeof(xev));\n\txev.type = ClientMessage;\n\txev.xclient.window = vid_window;\n\txev.xclient.message_type = x11.pXInternAtom(vid_dpy, \"_NET_WM_STATE\", False);\n\txev.xclient.format = 32;\n\txev.xclient.data.l[0] = 1;\t//add\n\txev.xclient.data.l[1] = x11.pXInternAtom(vid_dpy, \"_NET_WM_STATE_FULLSCREEN\", False);\n\txev.xclient.data.l[2] = 0;\n\n\t//for any other window managers, and broken NETWM\n\t//Note that certain window managers ignore this entirely, and instead fullscreen us on whichever monitor has the mouse cursor at the time. Which is awkward.\n\tx11.pXMoveResizeWindow(vid_dpy, vid_window, fullscreenx, fullscreeny, fullscreenwidth, fullscreenheight);\n//\tx11.pXMoveResizeWindow(vid_dpy, vid_window, fullscreenx+fullscreenwidth/2, fullscreeny+fullscreenheight/2, 1, 1);\n\tx11.pXSync(vid_dpy, False);\n\tx11.pXSendEvent(vid_dpy, DefaultRootWindow(vid_dpy), False, SubstructureNotifyMask, &xev);\n\tx11.pXSync(vid_dpy, False);\n\tx11.pXMoveResizeWindow(vid_dpy, vid_window, fullscreenx, fullscreeny, fullscreenwidth, fullscreenheight);\n\tx11.pXSync(vid_dpy, False);\n\n//Con_Printf(\"Gone fullscreen\\n\");\n}\nvoid X_GoWindowed(void)\n{\n\tXEvent xev;\n\tx11.pXFlush(vid_dpy);\n\tx11.pXSync(vid_dpy, False);\n\n\tmemset(&xev, 0, sizeof(xev));\n\txev.type = ClientMessage;\n\txev.xclient.window = vid_window;\n\txev.xclient.message_type = x11.pXInternAtom(vid_dpy, \"_NET_WM_STATE\", False);\n\txev.xclient.format = 32;\n\txev.xclient.data.l[0] = 0;\t//remove\n\txev.xclient.data.l[1] = x11.pXInternAtom(vid_dpy, \"_NET_WM_STATE_FULLSCREEN\", False);\n\txev.xclient.data.l[2] = 0;\n\tx11.pXSendEvent(vid_dpy, DefaultRootWindow(vid_dpy), False, SubstructureNotifyMask, &xev);\n\tx11.pXSync(vid_dpy, False);\n\n\tx11.pXMoveResizeWindow(vid_dpy, vid_window, 0, 0, 640, 480);\n//Con_Printf(\"Gone windowed\\n\");\n}\nstatic qboolean X_CheckWMFullscreenAvailable(void)\n{\n\t//root window must have _NET_SUPPORTING_WM_CHECK which is a Window created by the WM\n\t//the WM's window must have _NET_WM_NAME set, which is the name of the window manager\n\t//if we can find those, then the window manager has not crashed.\n\t//if we can then find _NET_WM_STATE_FULLSCREEN in the _NET_SUPPORTED atom list on the root, then we can get fullscreen mode from the WM\n\t//and we'll have no alt-tab issues whatsoever.\n\n\tAtom xa_net_supporting_wm_check = x11.pXInternAtom(vid_dpy, \"_NET_SUPPORTING_WM_CHECK\", False);\n\tAtom xa_net_wm_name = x11.pXInternAtom(vid_dpy, \"_NET_WM_NAME\", False);\n\tAtom xa_net_supported = x11.pXInternAtom(vid_dpy, \"_NET_SUPPORTED\", False);\n\tAtom xa_net_wm_state_fullscreen = x11.pXInternAtom(vid_dpy, \"_NET_WM_STATE_FULLSCREEN\", False);\n\tWindow wmwindow;\n\tunsigned char *prop;\n\tunsigned long bytes_after, nitems;\n\tAtom type;\n\tint format;\n\tqboolean success = false;\n\tunsigned char *wmname;\n\tint i;\n\n\tif (!X11_CheckFeature(\"wmfullscreen\", true))\n\t{\n\t\tCon_Printf(\"Window manager fullscreen support disabled. Will attempt to hide from it instead.\\n\");\n\t\treturn success;\n\t}\n\t\n\n\tif (x11.pXGetWindowProperty(vid_dpy, vid_root, xa_net_supporting_wm_check, 0, 16384, False, AnyPropertyType, &type, &format, &nitems, &bytes_after, &prop) != Success || prop == NULL)\n\t{\n\t\tCon_Printf(\"Window manager not identified\\n\");\n\t\treturn success;\n\t}\n\twmwindow = *(Window *)prop;\n\tx11.pXFree(prop);\n\t\n\tif (x11.pXGetWindowProperty(vid_dpy, wmwindow, xa_net_wm_name, 0, 16384, False, AnyPropertyType, &type, &format, &nitems, &bytes_after, &wmname) != Success || wmname == NULL)\n\t{\n\t\tCon_Printf(\"Window manager crashed or something\\n\");\n\t\treturn success;\n\t}\n\telse\n\t{\n\t\tif (x11.pXGetWindowProperty(vid_dpy, vid_root, xa_net_supported, 0, 16384, False, AnyPropertyType, &type, &format, &nitems, &bytes_after, &prop) != Success || prop == NULL)\n\t\t{\n\t\t\tCon_Printf(\"Window manager \\\"%s\\\" supports nothing\\n\", wmname);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tfor (i = 0; i < nitems; i++)\n\t\t\t{\n//\t\t\t\tCon_Printf(\"supported: %s\\n\", x11.pXGetAtomName(vid_dpy, ((Atom*)prop)[i]));\n\t\t\t\tif (((Atom*)prop)[i] == xa_net_wm_state_fullscreen)\n\t\t\t\t{\n\t\t\t\t\tsuccess = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!success)\n\t\t\t\tCon_Printf(\"Window manager \\\"%s\\\" does not appear to support fullscreen\\n\", wmname);\n\t\t\telse if (!strcmp(wmname, \"Fluxbox\"))\n\t\t\t{\n\t\t\t\tCon_Printf(\"Window manager \\\"%s\\\" claims to support fullscreen, but is known to be buggy\\n\", wmname);\n\t\t\t\tsuccess = false;\n\t\t\t}\n\t\t\telse\n\t\t\t\tCon_DPrintf(\"Window manager \\\"%s\\\" supports fullscreen. Set x11_allow_wmfullscreen 0 if it is buggy.\\n\", wmname);\n\t\t\tx11.pXFree(prop);\n\t\t}\n\t\tx11.pXFree(wmname);\n\t}\n\treturn success;\n}\n\nstatic Window X_CreateWindow(rendererstate_t *info, qboolean override, XVisualInfo *visinfo, int x, int y, unsigned int width, unsigned int height, qboolean fullscreen)\n{\n\tWindow wnd, parent;\n\tXSetWindowAttributes attr;\n\tXSizeHints szhints;\n\tunsigned int mask;\n\tAtom prots[2];\n\textern cvar_t vid_minsize;\n\n\t/* window attributes */\n\tattr.background_pixel = 0;\n\tattr.border_pixel = 0;\n\tattr.colormap = x11.pXCreateColormap(vid_dpy, vid_root, visinfo->visual, AllocNone);\n\tattr.event_mask = vid_x_eventmask = X_MASK;\n\tattr.backing_store = NotUseful;\n\tattr.save_under = False;\n\tmask = CWBackPixel | CWBorderPixel | CWColormap | CWEventMask | CWBackingStore |CWSaveUnder;\n\n\t// override redirect prevents the windowmanager from finding out about us, and thus will not apply borders to our window.\n\tif (override)\n\t{\n\t\tmask |= CWOverrideRedirect;\n\t\tattr.override_redirect = True;\n\t}\n\n\tmemset(&szhints, 0, sizeof(szhints));\n\tszhints.flags = PMinSize|PPosition|PSize;\n\tszhints.min_width = max(vid_minsize.vec4[0], 320);\n\tszhints.min_height = max(vid_minsize.vec4[1], 200);\n\n\tif (!fullscreen)\n\t{\n#ifdef USE_XRANDR\n\t\tint dx, dy, dw, dh;\n\t\tif (*info->devicename && XRandr_PickScreen(info->devicename, &dx, &dy, &dw, &dh))\n\t\t{\n\t\t\tx += dx + (dw-width)/2;\n\t\t\ty += dy + (dh-height)/2;\n\t\t}\n\t\telse\n#endif\n\t\t\tszhints.flags &= ~PPosition;\n\t}\n\n\tif (sys_parentwindow && !fullscreen)\n\t{\n\t\tx = (sys_parentwidth - width) / 2;\n\t\ty = (sys_parentheight - height) / 2;\n\t\tparent = sys_parentwindow;\n\t}\n\telse\n\t\tparent = vid_root;\n\n\tszhints.x = x;\n\tszhints.y = y;\n\tszhints.width = width;\n\tszhints.height = height;\n\n\twnd = x11.pXCreateWindow(vid_dpy, parent, x, y, width, height,\n\t\t\t\t\t\t0, visinfo->depth, InputOutput,\n\t\t\t\t\t\tvisinfo->visual, mask, &attr);\n\t/*ask the window manager to stop triggering bugs in Xlib*/\n\tprots[0] = x11.pXInternAtom(vid_dpy, \"WM_DELETE_WINDOW\", False);\n\tprots[1] = x11.pXInternAtom(vid_dpy, \"_NET_WM_PING\", False);\n\tx11.pXSetWMProtocols(vid_dpy, wnd, prots, countof(prots));\n\tx11.pXSetWMNormalHints(vid_dpy, wnd, &szhints);\n\t/*set caption*/\n\tx11.pXStoreName(vid_dpy, wnd, \"FTE QuakeWorld\");\n\tx11.pXSetIconName(vid_dpy, wnd, \"FTEQW\");\n\tX_StoreIcon(wnd);\n\tX_StorePID(wnd);\n\t/*make it visible*/\n\tx11.pXMapWindow(vid_dpy, wnd);\n\n\t//advertise support as a drag+drop target\n\tprots[0] = 5;\t//version 5 is the most recent.\n\tx11.pXChangeProperty(vid_dpy, wnd, x11.pXInternAtom(vid_dpy, \"XdndAware\", False), XA_ATOM, 32, PropModeReplace, (void*)prots, 1);\n\n\treturn wnd;\n}\n\nstatic qboolean X11VID_Init (rendererstate_t *info, unsigned char *palette, int psl)\n{\n\tint x = 0;\n\tint y = 0;\n\tint width = info->width;\t//can override these if vmode isn't available\n\tint height = info->height;\n\tint rate = info->rate;\n#if defined(USE_EGL)\n\tEGLConfig eglcfg = 0;\n#endif\n#if defined(USE_EGL) || defined(VKQUAKE)\n\tXVisualInfo vinfodef;\n#endif\n\tXVisualInfo *visinfo;\n#ifdef GLQUAKE\n\tGLXFBConfig fbconfig = NULL;\n#endif\n\tqboolean fullscreen = false;\n\n\tif (!x11_initlib())\n\t\treturn false;\n\n\tif (info->fullscreen)\n\t\tfullscreen = true;\n\n\tcurrentpsl = psl;\n\n\tswitch(currentpsl)\n\t{\n#ifdef GLQUAKE\n#ifdef USE_EGL\n\tcase PSL_EGL:\n\t\tif (!EGL_LoadLibrary(info->subrenderer))\n\t\t{\n\t\t\tCon_Printf(\"couldn't load EGL library\\n\");\n\t\t\treturn false;\n\t\t}\n\t\tbreak;\n#endif\n\tcase PSL_GLX:\n\t\tif (!GLX_InitLibrary(info->subrenderer))\n\t\t{\n\t\t\tCon_Printf(\"Couldn't intialise GLX\\nEither your drivers are not installed or you need to specify the library name with the gl_driver cvar\\n\");\n\t\t\treturn false;\n\t\t}\n\t\tbreak;\n#endif\n#ifdef VKQUAKE\n\tcase PSL_VULKAN:\n\t\t{\n\t\t\tdllfunction_t func[] =\n\t\t\t{\n\t\t\t\t{(void*)&vkGetInstanceProcAddr,\t\t\"vkGetInstanceProcAddr\"},\n\t\t\t\t{NULL,\t\t\t\t\t\t\tNULL}\n\t\t\t};\n\n\t\t\tif (!Sys_LoadLibrary(\"libvulkan.so.1\", func))\n\t\t\t{\n\t\t\t\tif (!Sys_LoadLibrary(\"libvulkan.so\", func))\n\t\t\t\t{\n\t\t\t\t\tCon_Printf(CON_ERROR\"Couldn't load libvulkan.so\\nvulkan loader is not installed\\n\");\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t//\"Some implementations may require threads to implement some presentation modes so applications must call XInitThreads() before calling any other Xlib functions.\"\n\t\t\tx11.pXInitThreads();\n\t\t}\n\t\tbreak;\n#endif\n\tcase PSL_NONE:\n\t\treturn false;\n\t}\n\n\tif (!vid_dpy)\n\t\tvid_dpy = x11.pXOpenDisplay(NULL);\n\tif (!vid_dpy)\n\t{\n\t\tCon_Printf(CON_ERROR \"Error: couldn't open the X display\\n\");\n\t\treturn false;\n\t}\n\n\tscrnum = DefaultScreen(vid_dpy);\n\tvid_root = RootWindow(vid_dpy, scrnum);\n\n#define MILLIMETRESPERINCH 25.4\t//sigh, why did we go for dpi?\n\tvid.dpi_x = DisplayWidth(vid_dpy, scrnum) * (MILLIMETRESPERINCH / DisplayWidthMM(vid_dpy, scrnum));\n\tvid.dpi_y = DisplayHeight(vid_dpy, scrnum) * (MILLIMETRESPERINCH / DisplayHeightMM(vid_dpy, scrnum));\n\n\tfullscreenflags = 0;\n\n\tif (info->fullscreen == 2)\n\t\tfullscreenflags |= FULLSCREEN_DESKTOP;\n#ifdef USE_XRANDR\n\tXRandR_Init();\n\tXRandR_FindOutput(info->devicename);\n\tif (fullscreen && !(fullscreenflags & FULLSCREEN_ANYMODE))\n\t\tXRandR_SelectMode(info->devicename, &x, &y, &width, &height, rate);\n#endif\n\n#ifdef USE_VMODE\n\tVMODE_Init();\n\tif (fullscreen && !(fullscreenflags & FULLSCREEN_ANYMODE))\n\t\tVMODE_SelectMode(&width, &height, rate);\n#endif\n\n\tif (fullscreen)\n\t{\n\t\tif (!(fullscreenflags & (FULLSCREEN_ANYMODE&~FULLSCREEN_DESKTOP)))\n\t\t{\n\t\t\t//if we arn't using any mode switching extension then our fullscreen has to be the size of the root window\n\t\t\t//FIXME: with xrandr (even when not fullscreen), we should pick an arbitrary display to size the game appropriately.\n\t\t\tXWindowAttributes xwa;\n\t\t\tx11.pXGetWindowAttributes(vid_dpy, DefaultRootWindow(vid_dpy), &xwa);\n\t\t\twidth = xwa.width;\n\t\t\theight = xwa.height;\n\t\t\tx = 0;\n\t\t\ty = 0;\n#ifdef USE_XRANDR\n\t\t\tXRandr_PickScreen(info->devicename, &x, &y, &width, &height);\n#endif\n\t\t}\n\n\t\t//window managers fuck up too much if we change the video mode and request the windowmanager make us fullscreen.\n\t\t//we assume that window manages will understand xrandr, as that actually provides notifications that things have changed.\n\t\tif (!(fullscreenflags & (FULLSCREEN_VMODE|FULLSCREEN_XRANDR)) && X_CheckWMFullscreenAvailable())\n\t\t\tfullscreenflags |= FULLSCREEN_WM;\n\t\telse\n\t\t\tfullscreenflags |= FULLSCREEN_LEGACY;\n\t}\n\telse if (sys_parentwindow)\n\t{\n\t\tif (width < 64 || width > sys_parentwidth)\n\t\t\twidth = sys_parentwidth;\n\t\tif (height < 64 || height > sys_parentheight)\n\t\t\theight = sys_parentheight;\n\t}\n\n\tswitch(currentpsl)\n\t{\n#ifdef GLQUAKE\n#ifdef USE_EGL\n\tcase PSL_EGL:\n\t\tif (!EGL_InitDisplay(info, EGL_PLATFORM_X11_KHR, vid_dpy, (EGLNativeDisplayType)vid_dpy, &eglcfg))\n\t\t{\n\t\t\tCon_Printf(\"X11VID_Init: Unable to find suitable EGL config\\n\");\n\t\t\tGLVID_Shutdown();\n\t\t\treturn false;\n\t\t}\n\t\t{\n\t\t\tint num_visuals;\n\t\t\tEGLint id;\n\t\t\tif (!qeglGetConfigAttrib(egldpy, eglcfg, EGL_NATIVE_VISUAL_ID, &id))\n\t\t\t\tSys_Error(\"Couldn't choose visual for EGL\\n\");\n\t\t\tvinfodef.visualid = id;\n\t\t\tvisinfo = x11.pXGetVisualInfo(vid_dpy, VisualIDMask, &vinfodef, &num_visuals);\n\t\t\tif (!visinfo)\n\t\t\t\tSys_Error(\"Couldn't get visual info for EGL\\n\");\n\t\t}\n\t\tbreak;\n#endif\n\tcase PSL_GLX:\n\t\tif (glx.QueryExtensionsString)\n\t\t\tglx.glxextensions = glx.QueryExtensionsString(vid_dpy, scrnum);\n\t\tfbconfig = GLX_GetFBConfig(info);\n\t\tif (fbconfig)\n\t\t\tvisinfo = glx.GetVisualFromFBConfig(vid_dpy, fbconfig);\n\t\telse\n\t\t\tvisinfo = GLX_GetVisual(info);\n\t\tif (!visinfo)\n\t\t{\n\t\t\tCon_Printf(\"X11VID_Init: Error couldn't get an RGB, Double-buffered, Depth visual\\n\");\n\t\t\tGLVID_Shutdown();\n\t\t\treturn false;\n\t\t}\n\t\tbreak;\n#endif\n#ifdef VKQUAKE\n\tcase PSL_VULKAN:\n\t\tvisinfo = &vinfodef;\n\t\t//the idea of a x11 server supporting vulkan but greyscale/palette video is just comedic. there will be a truecolor visual option.\n\t\t//WARNING: nvidia's vulkan drivers might fall back to 8bpc/24.8bpp here, then only allow a 30bit swapchain for that 8bpc surface. this will cause colour screwups. I'm going to call that a driver screwup if its choosing surface formats based on padded depth of the surface's window instead of x11's visuals or unpadded depth.\n\t\tif (info->bpp>0 && !x11.pXMatchVisualInfo(vid_dpy, scrnum, info->bpp, TrueColor, visinfo))\n\t\t{\n\t\t\tint defdepth = DefaultDepth(vid_dpy, scrnum);\n\t\t\tif (info->bpp!=defdepth && !x11.pXMatchVisualInfo(vid_dpy, scrnum, defdepth, TrueColor, visinfo))\n\t\t\t{\n\t\t\t\tif (defdepth!=24 && info->bpp!=24 && !x11.pXMatchVisualInfo(vid_dpy, scrnum, 24, TrueColor, visinfo))\n\t\t\t\t{\n\t\t\t\t\tCon_Printf(\"Couldn't choose visual for vulkan (depth %i), try changing vid_bpp\\n\", info->bpp?info->bpp:DefaultDepth(vid_dpy, scrnum));\n\t\t\t\t\tGLVID_Shutdown();\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tbreak;\n#endif\n\tdefault:\n\tcase PSL_NONE:\n\t\tvisinfo = NULL;\n\t\tbreak;\t//erm\n\t}\n\n\tvid.activeapp = false;\n\tif (fullscreenflags & FULLSCREEN_LEGACY)\n\t{\n\t\tvid_decoywindow = X_CreateWindow(info, false, visinfo, x, y, 320, 200, false);\n\t\tvid_window = X_CreateWindow(info, true, visinfo, x, y, width, height, fullscreen);\n\t}\n\telse\n\t\tvid_window = X_CreateWindow(info, false, visinfo, x, y, width, height, fullscreen);\n\n\tvid_x_eventmask |= X_InitUnicode();\n\tx11.pXSelectInput(vid_dpy, vid_window, vid_x_eventmask);\n\n\tCL_UpdateWindowTitle();\n\t/*make it visible*/\n\n#ifdef USE_VMODE\n\tif (fullscreenflags & FULLSCREEN_VMODE)\n\t{\n\t\tx11.pXRaiseWindow(vid_dpy, vid_window);\n\t\tx11.pXWarpPointer(vid_dpy, None, vid_window, 0, 0, 0, 0, 0, 0);\n\t\tx11.pXFlush(vid_dpy);\n\t\t// Move the viewport to top left, in case its not already.\n\t\tvm.pXF86VidModeSetViewPort(vid_dpy, scrnum, 0, 0);\n\t}\n#endif\n\n\tvid_nullcursor = CreateNullCursor(vid_dpy, vid_window);\n\tvid_newcursor = vid_activecursor = None;\t//at this point, the cursor is undefined (aka: inherited from parent)\n\n\tx11.pXFlush(vid_dpy);\n\n#ifdef USE_XRANDR\n\tif (xrandr.origgamma)\n\t\tvid.gammarampsize = xrandr.origgamma->size;\n\telse\n#endif\n#ifdef USE_VMODE\n\tif (vm.pXF86VidModeGetGammaRampSize)\n\t{\n\t\tint rampsize = 256;\n\t\tvm.pXF86VidModeGetGammaRampSize(vid_dpy, scrnum, &rampsize);\n\t\tif (rampsize > countof(vm.originalramps[0]))\n\t\t{\n\t\t\tvm.originalapplied = false;\n\t\t\tCon_Printf(\"Gamma ramps have more than %zi entries (%i).\\n\", countof(vm.originalramps[0]), rampsize);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tvm.originalrampsize = vid.gammarampsize = rampsize;\n\t\t\tvm.originalapplied = vm.pXF86VidModeGetGammaRamp(vid_dpy, scrnum, vm.originalrampsize, vm.originalramps[0], vm.originalramps[1], vm.originalramps[2]);\n\t\t}\n\t}\n\telse\n#endif\n\t\tvid.gammarampsize = 256;\n\n\tswitch(currentpsl)\n\t{\n#ifdef GLQUAKE\n\tcase PSL_GLX:\n\t\tif (!GLX_Init(info, fbconfig, visinfo))\n\t\t{\n#if defined(USE_EGL) || defined(VKQUAKE)\n\t\t\tif (visinfo != &vinfodef)\n#endif\n\t\t\t\tx11.pXFree(visinfo);\n\t\t\tGLVID_Shutdown();\n\t\t\treturn false;\n\t\t}\n#if defined(USE_EGL) || defined(VKQUAKE)\n\t\tif (visinfo != &vinfodef)\n#endif\n\t\t\tx11.pXFree(visinfo);\n\n\t\tglx.SwapIntervalEXT = GLX_CheckExtension(\"GLX_EXT_swap_control\")?GLX_GetSymbol(\"glXSwapIntervalEXT\"):NULL;\n\t\tglx.swaptear = glx.SwapIntervalEXT&&GLX_CheckExtension(\"GLX_EXT_swap_control_tear\");\n//\t\tif (glx.swaptear)\n//\t\t\tglx.QueryDrawable(vid_dpy, vid_window, 0x20F3, &glx.swaptear);\n\t\tif (!glx.SwapIntervalEXT)\n\t\t\tglx.SwapIntervalMESA = GLX_CheckExtension(\"GLX_MESA_swap_control\")?GLX_GetSymbol(\"glXSwapIntervalMESA\"):NULL;\n\t\tif (!glx.SwapIntervalEXT && !glx.SwapIntervalMESA)\n\t\t\tglx.SwapIntervalSGI = GLX_CheckExtension(\"GLX_SGI_swap_control\")?GLX_GetSymbol(\"glXSwapIntervalSGI\"):NULL;\n\t\tglx.swapint = vid_vsync.ival;\n\t\tif (!glx.swaptear)\n\t\t\tglx.swapint\t= abs(glx.swapint);\n\t\tif (*vid_vsync.string)\n\t\t{\n\t\t\tif (glx.SwapIntervalEXT /*&& (glx.swapint>=0 || swap_tear)*/)\n\t\t\t{\n\t\t\t\tglx.SwapIntervalEXT(vid_dpy, vid_window, glx.swapint);\n\t\t\t\tCon_DPrintf(\"Swap interval %i\\n\", glx.swapint);\n\t\t\t}\n\t\t\telse if (glx.SwapIntervalMESA && glx.swapint>=0)\n\t\t\t{\n\t\t\t\tglx.SwapIntervalMESA(abs(glx.swapint));\n\t\t\t\tCon_DPrintf(\"Swap interval %i\\n\", glx.swapint);\n\t\t\t}\n\t\t\telse if (glx.SwapIntervalSGI && glx.swapint>0)\n\t\t\t{\n\t\t\t\tglx.SwapIntervalSGI(glx.swapint);\n\t\t\t\tCon_DPrintf(\"Swap interval %i\\n\", glx.swapint);\n\t\t\t}\n\t\t\telse\n\t\t\t\tCon_Printf(\"Unable to explicitly %s vsync\\n\", glx.swapint?\"configure\":\"disable\");\n\t\t}\n\t\tbreak;\n#ifdef USE_EGL\n\tcase PSL_EGL:\n\t\tif (!EGL_InitWindow(info, EGL_PLATFORM_X11_KHR, &vid_window, (EGLNativeWindowType)vid_window, eglcfg))\n\t\t{\n\t\t\tCon_Printf(\"Failed to create EGL context.\\n\");\n\t\t\tGLVID_Shutdown();\n\t\t\treturn false;\n\t\t}\n\t\tif (!GL_Init(info, &EGL_Proc))\n\t\t\treturn false;\n\t\tbreak;\n#endif\n#endif\n#ifdef VKQUAKE\n\tcase PSL_VULKAN:\n#ifdef VK_USE_PLATFORM_XLIB_KHR\n\t\t{\n\t\t\tconst char *extnames[] = {VK_KHR_XLIB_SURFACE_EXTENSION_NAME};\n\t\t\tif (VK_Init(info, extnames, countof(extnames), XVK_SetupSurface_XLib, NULL))\n\t\t\t\tbreak;\n\t\t\tVK_Shutdown();\n\t\t}\n#endif\n#ifdef VK_USE_PLATFORM_XCB_KHR\n\t\t{\n\t\t\tconst char *extnames[] = {VK_KHR_XCB_SURFACE_EXTENSION_NAME};\n\t\t\tif (x11xcb_initlib() && VK_Init(info, extnames, countof(extnames), XVK_SetupSurface_XCB, NULL))\n\t\t\t\tbreak;\n\t\t\tVK_Shutdown();\n\t\t}\n#endif\n\t\t//Con_Printf(CON_ERROR \"Failed to create a vulkan context.\\n\");\n\t\tGLVID_Shutdown();\n\t\treturn false;\n#endif\n\tcase PSL_NONE:\n\t\tbreak;\n\t}\n\n\t//probably going to be resized in the event handler\n\tvid.pixelwidth = fullscreenwidth = width;\n\tvid.pixelheight = fullscreenheight = height;\n\tfullscreenx = x;\n\tfullscreeny = y;\n\n\tvid.numpages = 2;\n\n//\tCon_SafePrintf (\"Video mode %dx%d+%d,%d initialized.\\n\", width, height, x, y);\n\tx11.pXRaiseWindow(vid_dpy, vid_window);\n\tif (fullscreenflags & FULLSCREEN_WM)\n\t\tX_GoFullscreen();\n\tif (fullscreenflags & FULLSCREEN_LEGACY)\n\t\tx11.pXMoveResizeWindow(vid_dpy, vid_window, fullscreenx, fullscreeny, fullscreenwidth, fullscreenheight);\n\tif (fullscreenflags)\n\t\tfullscreenflags |= FULLSCREEN_ACTIVE;\n\tvid_isfullscreen = !!(fullscreenflags &FULLSCREEN_ACTIVE);\n\n\tif (X11_CheckFeature(\"xi2\", true) && XI2_Init())\n\t{\n\t\tx11_input_method = XIM_XI2;\n\t\tCon_DPrintf(\"Using XInput2\\n\");\n\t}\n\telse if (X11_CheckFeature(\"dga\", true) && DGAM_Init())\n\t{\n\t\tx11_input_method = XIM_DGA;\n\t\tCon_DPrintf(\"Using DGA mouse\\n\");\n\t}\n\telse\n\t{\n\t\tx11_input_method = XIM_ORIG;\n\t\tCon_DPrintf(\"Using X11 mouse\\n\");\n\t}\n\tXCursor_Init();\n\tX11Xss_Init();\n\n\tif (fullscreenflags & FULLSCREEN_LEGACY)\n\t\tx11.pXMoveResizeWindow(vid_dpy, vid_window, fullscreenx, fullscreeny, fullscreenwidth, fullscreenheight);\n\tif (Cvar_Get(\"vidx_grabkeyboard\", \"0\", 0, \"Additional video options\")->value)\n\t\tx11.pXGrabKeyboard(vid_dpy, vid_window,\n\t\t\t\t  False,\n\t\t\t\t  GrabModeAsync, GrabModeAsync,\n\t\t\t\t  CurrentTime);\n\telse if (fullscreenflags & FULLSCREEN_LEGACY)\n\t\tx11.pXSetInputFocus(vid_dpy, vid_window, RevertToNone, CurrentTime);\n\n\treturn true;\n}\n#ifdef GLQUAKE\nqboolean GLVID_Init (rendererstate_t *info, unsigned char *palette)\n{\n\treturn X11VID_Init(info, palette, PSL_GLX);\n}\n#ifdef USE_EGL\nstatic qboolean EGLVID_Init (rendererstate_t *info, unsigned char *palette)\n{\n\treturn X11VID_Init(info, palette, PSL_EGL);\n}\n#endif\n#endif\n#ifdef VKQUAKE\nstatic qboolean VKVID_Init (rendererstate_t *info, unsigned char *palette)\n{\n\treturn X11VID_Init(info, palette, PSL_VULKAN);\n}\n#endif\n\n\nvoid GLVID_SetCaption(const char *text)\n{\n\tx11.pXStoreName(vid_dpy, vid_window, (char*)text);\n}\n\n#ifdef USE_EGL\n#include \"shader.h\"\n#include \"gl_draw.h\"\nrendererinfo_t eglrendererinfo =\n{\n\t\"OpenGL (X11 EGL)\",\n\t{\n\t\t\"egl\"\n\t},\n\tQR_OPENGL,\n\n\tGLDraw_Init,\n\tGLDraw_DeInit,\n\n\tGL_UpdateFiltering,\n\tGL_LoadTextureMips,\n\tGL_DestroyTexture,\n\n\tGLR_Init,\n\tGLR_DeInit,\n\tGLR_RenderView,\n\n\tEGLVID_Init,\n\tGLVID_DeInit,\n\tGLVID_SwapBuffers,\n\tGLVID_ApplyGammaRamps,\n\n\tNULL,\n\tNULL,\n\tNULL,\n\tGLVID_SetCaption,       //setcaption\n\tGLVID_GetRGBInfo,\n\n\n\tGLSCR_UpdateScreen,\n\n\tGLBE_SelectMode,\n\tGLBE_DrawMesh_List,\n\tGLBE_DrawMesh_Single,\n\tGLBE_SubmitBatch,\n\tGLBE_GetTempBatch,\n\tGLBE_DrawWorld,\n\tGLBE_Init,\n\tGLBE_GenBrushModelVBO,\n\tGLBE_ClearVBO,\n\tGLBE_UpdateLightmaps,\n\tGLBE_SelectEntity,\n\tGLBE_SelectDLight,\n\tGLBE_Scissor,\n\tGLBE_LightCullModel,\n\n\tGLBE_VBO_Begin,\n\tGLBE_VBO_Data,\n\tGLBE_VBO_Finish,\n\tGLBE_VBO_Destroy,\n\n\tGLBE_RenderToTextureUpdate2d,\n\n\t\"\"\n};\n#endif\n\n#ifdef VKQUAKE\n#ifdef VK_USE_PLATFORM_XLIB_KHR\nstatic qboolean XVK_SetupSurface_XLib(void)\n{\n\tVkXlibSurfaceCreateInfoKHR inf = {VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR};\n\tinf.flags = 0;\n\tinf.dpy = vid_dpy;\n\tinf.window = vid_window;\n\n\tif (VK_SUCCESS == vkCreateXlibSurfaceKHR(vk.instance, &inf, vkallocationcb, &vk.surface))\n\t\treturn true;\n\treturn false;\n}\n#endif\n#ifdef VK_USE_PLATFORM_XCB_KHR\nstatic qboolean XVK_SetupSurface_XCB(void)\n{\n\tVkXcbSurfaceCreateInfoKHR inf = {VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR};\n\tinf.flags = 0;\n\tinf.connection = x11xcb.pXGetXCBConnection(vid_dpy);\n\tinf.window = vid_window;\n\n\tif (VK_SUCCESS == vkCreateXcbSurfaceKHR(vk.instance, &inf, vkallocationcb, &vk.surface))\n\t\treturn true;\n\treturn false;\n}\n#endif\nstatic qboolean XVK_EnumerateDevices(void *usercontext, void(*callback)(void *context, const char *devicename, const char *outputname, const char *desc))\n{\n\tqboolean ret = false;\n#ifdef VK_NO_PROTOTYPES\n\tPFN_vkGetInstanceProcAddr vkGetInstanceProcAddr;\n\tvoid *lib = NULL;\n\tdllfunction_t func[] =\n\t{\n\t\t{(void*)&vkGetInstanceProcAddr,\t\t\"vkGetInstanceProcAddr\"},\n\t\t{NULL,\t\t\t\t\t\t\tNULL}\n\t};\n\n\tif (!lib)\n\t\tlib = Sys_LoadLibrary(\"libvulkan.so.1\", func);\n\tif (!lib)\n\t\tlib = Sys_LoadLibrary(\"libvulkan.so\", func);\n\tif (!lib)\n\t\treturn false;\n#endif\n\tret = VK_EnumerateDevices(usercontext, callback, \"Vulkan-X11-\", vkGetInstanceProcAddr);\n#ifdef VK_NO_PROTOTYPES\n\tSys_CloseLibrary(lib);\n#endif\n\treturn ret;\n}\nrendererinfo_t vkrendererinfo =\n{\n\t\"Vulkan (X11)\",\n\t{\n\t\t\"xvk\",\n\t\t\"vk\",\n\t\t\"vulkan\"\n\t},\n\tQR_VULKAN,\n\n\tVK_Draw_Init,\n\tVK_Draw_Shutdown,\n\n\tVK_UpdateFiltering,\n\tVK_LoadTextureMips,\n\tVK_DestroyTexture,\n\n\tVK_R_Init,\n\tVK_R_DeInit,\n\tVK_R_RenderView,\n\n\tVKVID_Init,\n\tGLVID_DeInit,\n\tGLVID_SwapBuffers,\n\tGLVID_ApplyGammaRamps,\n\n\tNULL,\n\tNULL,\n\tNULL,\n\tGLVID_SetCaption,       //setcaption\n\tVKVID_GetRGBInfo,\n\n\n\tVK_SCR_UpdateScreen,\n\n\tVKBE_SelectMode,\n\tVKBE_DrawMesh_List,\n\tVKBE_DrawMesh_Single,\n\tVKBE_SubmitBatch,\n\tVKBE_GetTempBatch,\n\tVKBE_DrawWorld,\n\tVKBE_Init,\n\tVKBE_GenBrushModelVBO,\n\tVKBE_ClearVBO,\n\tVKBE_UploadAllLightmaps,\n\tVKBE_SelectEntity,\n\tVKBE_SelectDLight,\n\tVKBE_Scissor,\n\tVKBE_LightCullModel,\n\n\tVKBE_VBO_Begin,\n\tVKBE_VBO_Data,\n\tVKBE_VBO_Finish,\n\tVKBE_VBO_Destroy,\n\n\tVKBE_RenderToTextureUpdate2d,\n\n\t\"\",\n\n\tNULL,//int\t(*VID_GetPriority)\t(void);\t//so that eg x11 or wayland can be prioritised depending on environment settings. assumed to be 1.\n\tNULL,//void\t(*VID_EnumerateVideoModes) (const char *driver, const char *output, void (*cb) (int w, int h));\n\tXVK_EnumerateDevices,//qboolean\t(*VID_EnumerateDevices) (void *usercontext, void(*callback)(void *context, const char *devicename, const char *outputname, const char *desc));\n};\n#endif\n\n#if 1\nstatic void (*paste_callback)(void *cb, const char *utf8);\nstatic void *pastectx;\nstatic struct {\n\tAtom clipboard;\n\tAtom prop;\n\tAtom owner;\n} x11paste;\nvoid Sys_Clipboard_PasteText(clipboardtype_t clipboardtype, void (*callback)(void *ctx, const char *utf8), void *ctx)\n{\n\t//if there's a paste already pending, cancel the callback to ensure it always gets called.\n\tif (paste_callback)\n\t\tpaste_callback(pastectx, NULL);\n\n\tpaste_callback = NULL;\n\tpastectx = NULL;\n\n\tif(vid_dpy)\n\t{\n\t\tWindow clipboardowner;\n\t\tAtom xa_string = x11.pXInternAtom(vid_dpy, \"UTF8_STRING\", false);\n\t\t//FIXME: we should query it using TARGETS first to see if UTF8_STRING etc is actually valid.\n\t\tif (clipboardtype == CBT_SELECTION)\n\t\t\tx11paste.clipboard = x11.pXInternAtom(vid_dpy, \"PRIMARY\", false);\n\t\telse\n\t\t\tx11paste.clipboard = x11.pXInternAtom(vid_dpy, \"CLIPBOARD\", false);\n\t\tx11paste.prop = x11.pXInternAtom(vid_dpy, \"_FTE_PASTE\", false);\n\t\tclipboardowner = x11.pXGetSelectionOwner(vid_dpy, x11paste.clipboard);\n\t\tif (clipboardowner == vid_window)\n\t\t\tcallback(ctx, clipboard_buffer[clipboardtype]);\t//we own it? no point doing round-robin stuff.\n\t\telse if (clipboardowner != None && clipboardowner != vid_window)\n\t\t{\n\t\t\tx11.pXConvertSelection(vid_dpy, x11paste.clipboard, xa_string, x11paste.prop, vid_window, CurrentTime);\n\n\t\t\tpaste_callback = callback;\n\t\t\tpastectx = ctx;\n\t\t}\n\t\telse\n\t\t\tcallback(ctx, NULL);\t//nothing to paste (the window that owned it sucks\n\t}\n\telse\n\t\tcallback(ctx, clipboard_buffer[clipboardtype]);\t//if we're not using x11 then just instantly call the callback\n}\nstatic qboolean X11_Clipboard_Notify(XSelectionEvent *xselection)\n{\t//called once XConvertSelection completes\n\tif (xselection->display == vid_dpy && xselection->selection == x11paste.clipboard && xselection->requestor == vid_window && xselection->property == x11paste.prop)\n\t{\n\t\tint fmt;\n\t\tAtom type;\n\t\tunsigned long nitems, bytesleft;\n\t\tunsigned char *data = NULL;\n\t\tx11.pXGetWindowProperty(vid_dpy, vid_window, x11paste.prop, 0, 65536, False, AnyPropertyType, &type, &fmt, &nitems, &bytesleft, &data);\n\t\tif (paste_callback)\n\t\t\tpaste_callback(pastectx, data);\n\t\tx11.pXFree(data);\n\t\tpaste_callback = NULL;\n\t\tpastectx = NULL;\n\t\treturn true;\n\t}\n\treturn false;\n}\n\n/*char *Sys_GetClipboard(void)\n{\n\tif(vid_dpy)\n\t{\n\t\t//FIXME: we should query it using TARGETS first to see if UTF8_STRING etc is actually valid.\n\t\tAtom xa_clipboard = x11.pXInternAtom(vid_dpy, \"PRIMARY\", false);\n\t\tAtom xa_string = x11.pXInternAtom(vid_dpy, \"UTF8_STRING\", false);\n\t\tWindow clipboardowner = x11.pXGetSelectionOwner(vid_dpy, xa_clipboard);\n\t\tif (clipboardowner != None && clipboardowner != vid_window)\n\t\t{\n\t\t\tint fmt;\n\t\t\tAtom type;\n\t\t\tunsigned long nitems, bytesleft;\n\t\t\tunsigned char *data;\n\t\t\tx11.pXConvertSelection(vid_dpy, xa_clipboard, xa_string, None, vid_window, CurrentTime);\n\n\t\t\t//FIXME: we should rewrite the clipboard pasting to invoke a callback once its available.\n\t\t\tx11.pXFlush(vid_dpy);\n\t\t\tx11.pXSync(vid_dpy, False);\n\t\t\tSys_Sleep(0.3);\n\t\t\tx11.pXSync(vid_dpy, False);\n\n\t\t\t//and now we can actually read the data.\n\t\t\tx11.pXGetWindowProperty(vid_dpy, vid_window, xa_string, 0, 65536, False, AnyPropertyType, &type, &fmt, &nitems, &bytesleft, &data);\n\t\t\t\n\t\t\treturn data;\n\t\t}\n\t}\n\treturn clipboard_buffer;\n}\n*/\n\nvoid Sys_SaveClipboard(clipboardtype_t clipboardtype, const char *text)\n{\n\tfree(clipboard_buffer[clipboardtype]);\n\tclipboard_buffer[clipboardtype] = strdup(text);\n\tif(vid_dpy)\n\t{\n\t\tAtom xa_clipboard;\n\t\tswitch(clipboardtype)\n\t\t{\n\t\tcase CBT_SELECTION:\n\t\t\txa_clipboard = x11.pXInternAtom(vid_dpy, \"PRIMARY\", false);\n\t\t\tbreak;\n\t\tcase CBT_CLIPBOARD:\n\t\t\txa_clipboard = x11.pXInternAtom(vid_dpy, \"CLIPBOARD\", false);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\treturn;\n\t\t}\n\t\tx11.pXSetSelectionOwner(vid_dpy, xa_clipboard, vid_window, CurrentTime);\n\t}\n}\n#endif\n\nqboolean X11_GetDesktopParameters(int *width, int *height, int *bpp, int *refreshrate)\n{\n\tDisplay *xtemp;\n\tint scr;\n\n\tif (!x11_initlib())\n\t\treturn false;\n\n\txtemp = x11.pXOpenDisplay(NULL);\n\n\tif (!xtemp)\n\t\treturn false;\n\n\tscr = DefaultScreen(xtemp);\n\n\t*width = 0;//DisplayWidth(xtemp, scr);\n\t*height = 0;//DisplayHeight(xtemp, scr);\n\t*bpp = DefaultDepth(xtemp, scr);\n\t*refreshrate = 0;\n\n\tx11.pXCloseDisplay(xtemp);\n\n\treturn true;\n}\n#endif\n\n\n\n\nvoid Sys_SendKeyEvents(void)\n{\n#ifndef CLIENTONLY\n\t//this is stupid\n\tSV_GetConsoleCommands();\n#endif\n\n#ifndef NO_X11\n\tif (sys_gracefulexit)\n\t{\n\t\tCbuf_AddText(\"\\nquit\\n\", RESTRICT_LOCAL);\n\t\tsys_gracefulexit = false;\n\t}\n\tif (vid_dpy && vid_window)\n\t{\n\t\tif (x11.unicodecontext)\n\t\t{\n\t\t\tqboolean want = vid.ime_allow && vid.activeapp;\n\t\t\tXPoint pos;\n\t\t\tif (want != x11.ime_shown)\n\t\t\t{\n\t\t\t\tx11.ime_shown = want;\n\t\t\t\tif (x11.ime_shown)\n\t\t\t\t\tx11.pXSetICFocus(x11.unicodecontext);\n\t\t\t\telse\n\t\t\t\t\tx11.pXUnsetICFocus(x11.unicodecontext);\n\t\t\t}\n\t\t\tpos.x = (vid.ime_position[0] * vid.pixelwidth)/vid.width;\n\t\t\tpos.y = (vid.ime_position[1] * vid.pixelheight)/vid.height;\n\t\t\tif (/*x11.ime_shown &&*/ (x11.ime_pos.x != pos.x || x11.ime_pos.y != pos.y))\n\t\t\t{\n\t\t\t\tvoid *attr[] = {XNSpotLocation, &x11.ime_pos, NULL};\n\t\t\t\tx11.ime_pos = pos;\n\t\t\t\tx11.pXSetICValues(x11.unicodecontext, XNPreeditAttributes, attr, NULL);\n\t\t\t}\n\t\t}\n\n\t\twhile (x11.pXPending(vid_dpy))\n\t\t\tGetEvent();\n\n\t\tif (modeswitchpending && modeswitchtime < Sys_Milliseconds())\n\t\t{\n\t\t\tUpdateGrabs();\n\t\t\tif (modeswitchpending > 0 && !(fullscreenflags & FULLSCREEN_ACTIVE))\n\t\t\t{\n\t\t\t\t//entering fullscreen mode\n#ifdef USE_VMODE\n\t\t\t\tif (fullscreenflags & FULLSCREEN_VMODE)\n\t\t\t\t{\n\t\t\t\t\tif (!(fullscreenflags & FULLSCREEN_VMODEACTIVE))\n\t\t\t\t\t{\n\t\t\t\t\t\t// change to the mode\n\t\t\t\t\t\tvm.pXF86VidModeSwitchToMode(vid_dpy, scrnum, vm.modes[vm.usemode]);\n\t\t\t\t\t\tfullscreenflags |= FULLSCREEN_VMODEACTIVE;\n\t\t\t\t\t\t// Move the viewport to top left\n\t\t\t\t\t}\n\t\t\t\t\tvm.pXF86VidModeSetViewPort(vid_dpy, scrnum, 0, 0);\n\t\t\t\t}\n#endif\n#ifdef USE_XRANDR\n\t\t\t\tif (fullscreenflags & FULLSCREEN_XRANDR)\n\t\t\t\t\tXRandR_ApplyMode();\n#endif\n\t\t\t\tCvar_ForceCallback(&v_gamma);\n\n\t\t\t\t/*release the mouse now, because we're paranoid about clip regions*/\n\t\t\t\tif (fullscreenflags & FULLSCREEN_WM)\n\t\t\t\t\tX_GoFullscreen();\n\t\t\t\tif (fullscreenflags & FULLSCREEN_LEGACY)\n\t\t\t\t{\n\t\t\t\t\tx11.pXReparentWindow(vid_dpy, vid_window, vid_root, fullscreenx, fullscreeny);\n\t\t\t\t//\tif (vid_decoywindow)\n\t\t\t\t//\t\tx11.pXMoveWindow(vid_dpy, vid_decoywindow, fullscreenx, fullscreeny);\n\t\t\t\t\t//x11.pXUnmapWindow(vid_dpy, vid_decoywindow);\n\t\t\t\t\t//make sure we have it\n\t\t\t\t\tx11.pXSetInputFocus(vid_dpy, vid_window, RevertToParent, CurrentTime);\n\t\t\t\t\tx11.pXRaiseWindow(vid_dpy, vid_window);\n\t\t\t\t\tx11.pXMoveResizeWindow(vid_dpy, vid_window, fullscreenx, fullscreeny, fullscreenwidth, fullscreenheight);\n\t\t\t\t}\n\t\t\t\tif (fullscreenflags)\n\t\t\t\t\tfullscreenflags |= FULLSCREEN_ACTIVE;\n\t\t\t\tvid_isfullscreen = !!(fullscreenflags &FULLSCREEN_ACTIVE);\n\t\t\t}\n\t\t\tif (modeswitchpending < 0)\n\t\t\t{\n\t\t\t\t//leave fullscreen mode\n\t\t \t\tif (!COM_CheckParm(\"-stayactive\"))\n \t\t\t\t{\t//a parameter that leaves the program fullscreen if you taskswitch.\n \t\t\t\t\t//sounds pointless, works great with two moniters. :D\n#ifdef USE_VMODE\n\t\t\t\t\tif (fullscreenflags & FULLSCREEN_VMODE)\n\t\t\t\t\t{\n\t \t\t\t\t\tif (vm.originalapplied)\n\t\t\t\t\t\t\tvm.pXF86VidModeSetGammaRamp(vid_dpy, scrnum, vm.originalrampsize, vm.originalramps[0], vm.originalramps[1], vm.originalramps[2]);\n\t\t\t\t\t\tif (fullscreenflags & FULLSCREEN_VMODEACTIVE)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tvm.pXF86VidModeSwitchToMode(vid_dpy, scrnum, vm.modes[0]);\n\t\t\t\t\t\t\tfullscreenflags &= ~FULLSCREEN_VMODEACTIVE;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n#endif\n#ifdef USE_XRANDR\n\t\t\t\t\tif (fullscreenflags & FULLSCREEN_XRANDR)\n\t\t\t\t\t\tXRandR_RevertMode();\n#endif\n\t\t\t\t\tif (fullscreenflags & FULLSCREEN_WM)\n\t\t\t\t\t\tX_GoWindowed();\n\t\t\t\t\tif (fullscreenflags & FULLSCREEN_LEGACY)\n\t\t\t\t\t{\n\t\t\t\t\t\tx11.pXReparentWindow(vid_dpy, vid_window, vid_decoywindow, 0, 0);\n//\t\t\t\t\t\tx11.pXMoveResizeWindow(vid_dpy, vid_decoywindow, fullscreenx + (fullscreenwidth-640)/2, fullscreeny + (fullscreenheight-480)/2, 640, 480);\n\t\t\t\t\t\tx11.pXMapWindow(vid_dpy, vid_decoywindow);\n\t\t\t\t\t}\n\t\t\t\t\tfullscreenflags &= ~FULLSCREEN_ACTIVE;\n\t\t\t\t\tvid_isfullscreen = !!(fullscreenflags &FULLSCREEN_ACTIVE);\n\t\t\t\t}\n\t\t\t}\n\t\t\tmodeswitchpending = 0;\n\t\t}\n\n\t\tif (modeswitchpending)\n\t\t\treturn;\n\n\t\tUpdateGrabs();\n\t}\n#endif\n}\n\n/*static void Force_CenterView_f (void)\n{\n\tcl.playerview[0].viewangles[PITCH] = 0;\n}*/\n\n\n//these are done from the x11 event handler. we don't support evdev.\nvoid INS_Move(void)\n{\n}\nvoid INS_Commands(void)\n{\n}\nvoid INS_Init(void)\n{\n}\nvoid INS_ReInit(void)\n{\n}\nvoid INS_Shutdown(void)\n{\n}\nvoid INS_EnumerateDevices(void *ctx, void(*callback)(void *ctx, const char *type, const char *devicename, unsigned int *qdevid))\n{\n#ifndef NO_X11\n\tcallback(ctx, \"keyboard\", \"x11\", NULL);\n\tswitch(x11_input_method)\n\t{\n\tcase XIM_ORIG:\n\t\tcallback(ctx, \"mouse\", \"x11\", &x11_mouseqdev);\n\t\tbreak;\n\tcase XIM_DGA:\n\t\tcallback(ctx, \"mouse\", \"dga\", &x11_mouseqdev);\n\t\tbreak;\n\tcase XIM_XI2:\n\t\t{\n\t\t\tint i, devs;\n\t\t\tXIDeviceInfo *dev = xi2.pXIQueryDevice(vid_dpy, xi2.devicegroup, &devs);\n\t\t\tif (dev)\n\t\t\t{\n\t\t\t\tfor (i = 0; i < devs; i++)\n\t\t\t\t{\n\t\t\t\t\tif (!dev[i].enabled)\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\tif (/*dev[i].use == XIMasterPointer ||*/ dev[i].use == XISlavePointer)\n\t\t\t\t\t{\n\t\t\t\t\t\tstruct xidevinfo *devi = XI2_GetDeviceInfo(dev[i].deviceid);\n\t\t\t\t\t\tcallback(ctx, devi->abs?\"tablet\":\"mouse\", dev[i].name, &devi->qdev);\n\t\t\t\t\t}\n//\t\t\t\t\telse if (dev[i].use == XIMasterKeyboard || dev[i].use == XISlaveKeyboard)\n//\t\t\t\t\t{\n//\t\t\t\t\t\tint qdev = dev[i].deviceid;\n//\t\t\t\t\t\tcallback(ctx, \"xi2kb\", dev[i].name, &qdev);\n//\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\txi2.pXIFreeDeviceInfo(dev);\n\t\t\t}\n\t\t}\n\t\tbreak;\n\t}\n#endif\n}\n\nenum controllertype_e INS_GetControllerType(int id)\n{\n\treturn CONTROLLER_NONE;\n}\n/* doubt this will ever happen to begin with */\nvoid INS_Rumble(int id, quint16_t amp_low, quint16_t amp_high, quint32_t duration)\n{\n\t//Con_DPrintf(CON_WARNING \"Rumble is unavailable on this platform\\n\");\n}\n\nvoid INS_RumbleTriggers(int id, quint16_t left, quint16_t right, quint32_t duration)\n{\n\t//Con_DPrintf(CON_WARNING \"Trigger rumble is unavailable on this platform\\n\");\n}\n\nvoid INS_SetLEDColor(int id, vec3_t color)\n{\n\t//Con_DPrintf(CON_WARNING \"Game-Pad LED colors are unavailable on this platform\\n\");\n}\n\nvoid INS_SetTriggerFX(int id, const void *data, size_t size)\n{\n\t//Con_DPrintf(CON_WARNING \"Trigger FX are unavailable on this platform\\n\");\n}\n"
  },
  {
    "path": "engine/gl/gl_vidmacos.c",
    "content": "/*\n \n Copyright (C) 2001-2002       A Nourai\n Copyright (C) 2006            Jacek Piszczek (Mac OSX port)\n \n This program is free software; you can redistribute it and/or\n modify it under the terms of the GNU General Public License\n as published by the Free Software Foundation; either version 2\n of the License, or (at your option) any later version.\n \n This program is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  \n \n See the included (GNU.txt) GNU General Public License for more details.\n \n You should have received a copy of the GNU General Public License\n along with this program; if not, write to the Free Software\n Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n*/\n\n#include \"quakedef.h\"\n#include \"glquake.h\"\n\n#include <dlfcn.h>\n\n//#include \"vid_macos.h\"\n\n// note: cocoa code is separated in vid_cocoa.m because of compilation pbs\n\nextern cvar_t vid_hardwaregamma;\n\nstatic void *agllibrary;\nstatic void *opengllibrary;\nvoid *AGL_GetProcAddress(char *functionname)\n{\n\tvoid *func;\n\tif (agllibrary)\n\t{\n\t\tfunc = dlsym(agllibrary, functionname);\n\t\tif (func)\n\t\t\treturn func;\n\t}\n\tif (opengllibrary)\n\t{\n\t\tfunc = dlsym(opengllibrary, functionname);\n\t\tif (func)\n\t\t\treturn func;\n\t}\n\treturn NULL;\n}\n\nqboolean GLVID_Init(rendererstate_t *info, unsigned char *palette)\n{\n\tint argnum;\n\tint i;\n\n\tagllibrary = dlopen(\"/System/Library/Frameworks/AGL.framework/AGL\", RTLD_LAZY);\n\tif (!agllibrary)\n\t{\n\t\tCon_Printf(\"Couldn't load AGL framework\\n\");\n\t\treturn false;\n\t}\n\topengllibrary = dlopen(\"/System/Library/Frameworks/OpenGL.framework/OpenGL\", RTLD_LAZY);\n\t//don't care if opengl failed.\n\n\tvid.numpages = 2;\n\n\t// initialise the NSApplication and the screen\n\tinitCocoa(info);\n\n\n\t// calculate the conwidth AFTER the screen has been opened\n\tif (vid.pixelwidth <= 640)\n\t{\n\t\tvid.width = vid.pixelwidth;\n\t\tvid.height = vid.pixelheight;\n\t}\n\telse\n\t{\n\t\tvid.width = vid.pixelwidth/2;\n\t\tvid.height = vid.pixelheight/2;\n\t}\n\n\tif ((i = COM_CheckParm(\"-conwidth\")) && i + 1 < com_argc)\n\t{\n\t\tvid.width = Q_atoi(com_argv[i + 1]);\n\n\t\t// pick a conheight that matches with correct aspect\n\t\tvid.height = vid.width * 3 / 4;\n\t}\n\n\tvid.width &= 0xfff8; // make it a multiple of eight\n\n\tif ((i = COM_CheckParm(\"-conheight\")) && i + 1 < com_argc)\n\t\tvid.height = Q_atoi(com_argv[i + 1]);\n\n\tif (vid.width < 320)\n\t\tvid.width = 320;\n\n\tif (vid.height < 200)\n\t\tvid.height = 200;\n\n\tGL_Init(info, AGL_GetProcAddress);\n\n\tGLVID_SetPalette(palette);\n\n\treturn true;\n}\n\nvoid GLVID_DeInit(void)\n{\n\tkillCocoa();\n\tGL_ForgetPointers();\n}\n\nvoid GLVID_SetPalette (unsigned char *palette)\n{\n\tqbyte *pal;\n\tunsigned int r,g,b;\n\tint i;\n\tunsigned *table1;\n\textern qbyte gammatable[256];\n\n\tCon_Printf(\"Converting 8to24\\n\");\n\n\tpal = palette;\n\ttable1 = d_8to24rgbtable;\n\n\tif (vid_hardwaregamma.value)\n\t{\n\t\tfor (i=0 ; i<256 ; i++)\n\t\t{\n\t\t\tr = pal[0];\n\t\t\tg = pal[1];\n\t\t\tb = pal[2];\n\t\t\tpal += 3;\n\n\t\t\t*table1++ = LittleLong((255<<24) + (r<<0) + (g<<8) + (b<<16));\n\t\t}\n\t}\n\telse\n\t{\n\t\tfor (i=0 ; i<256 ; i++)\n\t\t{\n\t\t\tr = gammatable[pal[0]];\n\t\t\tg = gammatable[pal[1]];\n\t\t\tb = gammatable[pal[2]];\n\t\t\tpal += 3;\n\n\t\t\t*table1++ = LittleLong((255<<24) + (r<<0) + (g<<8) + (b<<16));\n\t\t}\n\t}\n\td_8to24rgbtable[255] &= LittleLong(0xffffff);\t// 255 is transparent\n\tCon_Printf(\"Converted\\n\");\n}\n\nvoid Sys_SendKeyEvents(void)\n{\n}\n\nvoid GLVID_LockBuffer(void)\n{\n}\n\nvoid GLVID_UnlockBuffer(void)\n{\n}\n\nqboolean GLVID_IsLocked(void)\n{\n\treturn 0;\n}\n\nvoid GLVID_SetCaption(const char *text)\n{\n}\n\nvoid GLVID_SwapBuffers(void)\n{\n\tflushCocoa();\n}\n\nvoid GLVID_SetDeviceGammaRamp(unsigned short *ramps)\n{\n\tcocoaGamma(ramps,ramps+256,ramps+512);\n}\n\nvoid GLVID_ShiftPalette(unsigned char *p)\n{\n\textern\tunsigned short ramps[3][256];\n\tif (vid_hardwaregamma.value)\n\t\tGLVID_SetDeviceGammaRamp(ramps);\n}\n\n//I'm too lazy to put these stubs elsewhere.\nvoid INS_Init (void)\n{\n}\nvoid INS_ReInit(void)\n{\n}\nvoid INS_Shutdown (void)\n{\n}\nvoid INS_Commands (void)\n{\n}\nvoid INS_EnumerateDevices(void *ctx, void(*callback)(void *ctx, const char *type, const char *devicename, unsigned int *qdevid))\n{\n}\nvoid INS_Move (void)\n{\n}\n\n#define SYS_CLIPBOARD_SIZE  256\nstatic char clipboard_buffer[SYS_CLIPBOARD_SIZE] = {0};\nvoid Sys_Clipboard_PasteText(clipboardtype_t cbt, void (*callback)(void *cb, char *utf8), void *ctx)\n{\n\tcallback(ctx, clipboard_buffer);\n}\nvoid Sys_SaveClipboard(clipboardtype_t cbt, char *text)\n{\n \tQ_strncpyz(clipboard_buffer, text, SYS_CLIPBOARD_SIZE);\n}\n\n"
  },
  {
    "path": "engine/gl/gl_vidmorphos.c",
    "content": "/*\nCopyright (C) 2006-2007 Mark Olsen\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  \n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n*/\n\n#include <exec/exec.h>\n#define SYSTEM_PRIVATE\n#include <intuition/intuition.h>\n#include <intuition/intuitionbase.h>\n#include <intuition/extensions.h>\n#include <cybergraphx/cybergraphics.h>\n\n#include <tgl/gl.h>\n#include <tgl/gla.h>\n\n#include <proto/exec.h>\n#include <proto/intuition.h>\n#include <proto/cybergraphics.h>\n#include <proto/tinygl.h>\n\n#include \"quakedef.h\"\n#include \"glquake.h\"\n\n#include \"gl_vidtinyglstubs.c\"\n\n#ifndef SA_GammaControl\n#define SA_GammaControl (SA_Dummy + 123)\n#endif\n\n#ifndef SA_3DSupport\n#define SA_3DSupport (SA_Dummy + 127)\n#endif\n\n#ifndef SA_GammaRed\n#define SA_GammaRed (SA_Dummy + 124)\n#endif\n\n#ifndef SA_GammaBlue\n#define SA_GammaBlue (SA_Dummy + 125)\n#endif\n\n#ifndef SA_GammaGreen\n#define SA_GammaGreen (SA_Dummy + 126)\n#endif\n\n#define WARP_WIDTH              320\n#define WARP_HEIGHT             200\n\nextern qboolean gammaworks;\nextern unsigned short ramps[3][256];\n\nstruct Library *TinyGLBase = 0;\nGLContext *__tglContext;\nstatic int glctx = 0;\n\nunsigned char *mosgammatable;\n\nstruct Window *window;\nstruct Screen *screen;\n\nstatic void *pointermem;\n\nstatic void *TinyGL_GetSymbol(char *name)\n{\n\tvoid *ret = 0;\n\n\tif (strcmp(name, \"glAlphaFunc\") == 0)\n\t\tret = stub_glAlphaFunc;\n\n\telse if (strcmp(name, \"glBegin\") == 0)\n\t\tret = stub_glBegin;\n\n\telse if (strcmp(name, \"glBlendFunc\") == 0)\n\t\tret = stub_glBlendFunc;\n\n\telse if (strcmp(name, \"glBindTexture\") == 0)\n\t\tret = stub_glBindTexture;\n\n\telse if (strcmp(name, \"glClear\") == 0)\n\t\tret = stub_glClear;\n\n\telse if (strcmp(name, \"glClearColor\") == 0)\n\t\tret = stub_glClearColor;\n\n\telse if (strcmp(name, \"glClearDepth\") == 0)\n\t\tret = stub_glClearDepth;\n\n\telse if (strcmp(name, \"glClearStencil\") == 0)\n\t\tret = stub_glClearStencil;\n\n\telse if (strcmp(name, \"glColor3f\") == 0)\n\t\tret = stub_glColor3f;\n\n\telse if (strcmp(name, \"glColor3ub\") == 0)\n\t\tret = stub_glColor3ub;\n\n\telse if (strcmp(name, \"glColor4f\") == 0)\n\t\tret = stub_glColor4f;\n\n\telse if (strcmp(name, \"glColor4fv\") == 0)\n\t\tret = stub_glColor4fv;\n\n\telse if (strcmp(name, \"glColor4ub\") == 0)\n\t\tret = stub_glColor4ub;\n\n\telse if (strcmp(name, \"glColor4ubv\") == 0)\n\t\tret = stub_glColor4ubv;\n\n\telse if (strcmp(name, \"glColorMask\") == 0)\n\t\tret = stub_glColorMask;\n\n\telse if (strcmp(name, \"glCopyTexImage2D\") == 0)\n\t\tret = stub_glCopyTexImage2D;\n\n\telse if (strcmp(name, \"glCopyTexSubImage2D\") == 0)\n\t\tret = stub_glCopyTexSubImage2D;\n\n\telse if (strcmp(name, \"glCullFace\") == 0)\n\t\tret = stub_glCullFace;\n\n\telse if (strcmp(name, \"glDepthFunc\") == 0)\n\t\tret = stub_glDepthFunc;\n\n\telse if (strcmp(name, \"glDepthMask\") == 0)\n\t\tret = stub_glDepthMask;\n\n\telse if (strcmp(name, \"glDepthRange\") == 0)\n\t\tret = stub_glDepthRange;\n\n\telse if (strcmp(name, \"glDisable\") == 0)\n\t\tret = stub_glDisable;\n\n\telse if (strcmp(name, \"glDrawBuffer\") == 0)\n\t\tret = stub_glDrawBuffer;\n\n\telse if (strcmp(name, \"glDrawPixels\") == 0)\n\t\tret = stub_glDrawPixels;\n\n\telse if (strcmp(name, \"glEnable\") == 0)\n\t\tret = stub_glEnable;\n\n\telse if (strcmp(name, \"glEnd\") == 0)\n\t\tret = stub_glEnd;\n\n\telse if (strcmp(name, \"glFinish\") == 0)\n\t\tret = stub_glFinish;\n\n\telse if (strcmp(name, \"glFlush\") == 0)\n\t\tret = stub_glFlush;\n\n\telse if (strcmp(name, \"glFrustum\") == 0)\n\t\tret = stub_glFrustum;\n\n\telse if (strcmp(name, \"glGetFloatv\") == 0)\n\t\tret = stub_glGetFloatv;\n\n\telse if (strcmp(name, \"glGetIntegerv\") == 0)\n\t\tret = stub_glGetIntegerv;\n\n\telse if (strcmp(name, \"glGetString\") == 0)\n\t\tret = stub_glGetString;\n\n\telse if (strcmp(name, \"glGetTexLevelParameteriv\") == 0)\n\t\tret = stub_glGetTexLevelParameteriv;\n\n\telse if (strcmp(name, \"glHint\") == 0)\n\t\tret = stub_glHint;\n\n\telse if (strcmp(name, \"glLoadIdentity\") == 0)\n\t\tret = stub_glLoadIdentity;\n\n\telse if (strcmp(name, \"glLoadMatrixf\") == 0)\n\t\tret = stub_glLoadMatrixf;\n\n\telse if (strcmp(name, \"glNormal3f\") == 0)\n\t\tret = stub_glNormal3f;\n\n\telse if (strcmp(name, \"glNormal3fv\") == 0)\n\t\tret = stub_glNormal3fv;\n\n\telse if (strcmp(name, \"glMatrixMode\") == 0)\n\t\tret = stub_glMatrixMode;\n\n\telse if (strcmp(name, \"glMultMatrixf\") == 0)\n\t\tret = stub_glMultMatrixf;\n\n\telse if (strcmp(name, \"glOrtho\") == 0)\n\t\tret = stub_glOrtho;\n\n\telse if (strcmp(name, \"glPolygonMode\") == 0)\n\t\tret = stub_glPolygonMode;\n\n\telse if (strcmp(name, \"glPopMatrix\") == 0)\n\t\tret = stub_glPopMatrix;\n\n\telse if (strcmp(name, \"glPushMatrix\") == 0)\n\t\tret = stub_glPushMatrix;\n\n\telse if (strcmp(name, \"glReadBuffer\") == 0)\n\t\tret = stub_glReadBuffer;\n\n\telse if (strcmp(name, \"glReadPixels\") == 0)\n\t\tret = stub_glReadPixels;\n\n\telse if (strcmp(name, \"glRotatef\") == 0)\n\t\tret = stub_glRotatef;\n\n\telse if (strcmp(name, \"glScalef\") == 0)\n\t\tret = stub_glScalef;\n\n\telse if (strcmp(name, \"glShadeModel\") == 0)\n\t\tret = stub_glShadeModel;\n\n\telse if (strcmp(name, \"glTexCoord1f\") == 0)\n\t\tret = stub_glTexCoord1f;\n\n\telse if (strcmp(name, \"glTexCoord2f\") == 0)\n\t\tret = stub_glTexCoord2f;\n\n\telse if (strcmp(name, \"glTexCoord2fv\") == 0)\n\t\tret = stub_glTexCoord2fv;\n\n\telse if (strcmp(name, \"glTexEnvf\") == 0)\n\t\tret = stub_glTexEnvf;\n\n\telse if (strcmp(name, \"glTexEnvfv\") == 0)\n\t\tret = stub_glTexEnvfv;\n\n\telse if (strcmp(name, \"glTexEnvi\") == 0)\n\t\tret = stub_glTexEnvi;\n\n\telse if (strcmp(name, \"glTexGeni\") == 0)\n\t\tret = stub_glTexGeni;\n\n\telse if (strcmp(name, \"glTexImage2D\") == 0)\n\t\tret = stub_glTexImage2D;\n\n\telse if (strcmp(name, \"glTexParameteri\") == 0)\n\t\tret = stub_glTexParameteri;\n\n\telse if (strcmp(name, \"glTexParameterf\") == 0)\n\t\tret = stub_glTexParameterf;\n\n\telse if (strcmp(name, \"glTexSubImage2D\") == 0)\n\t\tret = stub_glTexSubImage2D;\n\n\telse if (strcmp(name, \"glTranslatef\") == 0)\n\t\tret = stub_glTranslatef;\n\n\telse if (strcmp(name, \"glVertex2f\") == 0)\n\t\tret = stub_glVertex2f;\n\n\telse if (strcmp(name, \"glVertex3f\") == 0)\n\t\tret = stub_glVertex3f;\n\n\telse if (strcmp(name, \"glVertex3fv\") == 0)\n\t\tret = stub_glVertex3fv;\n\n\telse if (strcmp(name, \"glViewport\") == 0)\n\t\tret = stub_glViewport;\n\n\telse if (strcmp(name, \"glGetError\") == 0)\n\t\tret = stub_glGetError;\n\n\telse if (strcmp(name, \"glDrawElements\") == 0)\n\t\tret = stub_glDrawElements;\n\n\telse if (strcmp(name, \"glArrayElement\") == 0)\n\t\tret = stub_glArrayElement;\n\n\telse if (strcmp(name, \"glVertexPointer\") == 0)\n\t\tret = stub_glVertexPointer;\n\n\telse if (strcmp(name, \"glNormalPointer\") == 0)\n\t\tret = stub_glNormalPointer;\n\n\telse if (strcmp(name, \"glTexCoordPointer\") == 0)\n\t\tret = stub_glTexCoordPointer;\n\n\telse if (strcmp(name, \"glColorPointer\") == 0)\n\t\tret = stub_glColorPointer;\n\n\telse if (strcmp(name, \"glDrawArrays\") == 0)\n\t\tret = stub_glDrawArrays;\n\n\telse if (strcmp(name, \"glEnableClientState\") == 0)\n\t\tret = stub_glEnableClientState;\n\n\telse if (strcmp(name, \"glDisableClientState\") == 0)\n\t\tret = stub_glDisableClientState;\n\n\telse if (strcmp(name, \"glStencilOp\") == 0)\n\t\tret = stub_glStencilOp;\n\n\telse if (strcmp(name, \"glStencilFunc\") == 0)\n\t\tret = stub_glStencilFunc;\n\n\telse if (strcmp(name, \"glPushAttrib\") == 0)\n\t\tret = stub_glPushAttrib;\n\n\telse if (strcmp(name, \"glPopAttrib\") == 0)\n\t\tret = stub_glPopAttrib;\n\n\telse if (strcmp(name, \"glScissor\") == 0)\n\t\tret = stub_glScissor;\n\n\telse if (strcmp(name, \"glMultiTexCoord2fARB\") == 0)\n\t\tret = stub_glMultiTexCoord2fARB;\n\n\telse if (strcmp(name, \"glMultiTexCoord3fARB\") == 0)\n\t\tret = stub_glMultiTexCoord3fARB;\n\n\telse if (strcmp(name, \"glActiveTextureARB\") == 0)\n\t\tret = stub_glActiveTextureARB;\n\n\telse if (strcmp(name, \"glClientActiveTextureARB\") == 0)\n\t\tret = stub_glClientActiveTextureARB;\n\n\telse\n\t\tprintf(\"Function \\\"%s\\\" not found\\n\", name);\n\n\treturn ret;\n}\n\nqboolean GLVID_Init (rendererstate_t *info, unsigned char *palette)\n{\n\tint argnum;\n\n\tint r;\n\n\tint i;\n\n\tint depth = 24;\n\n\tstruct TagItem tgltags[] =\n\t{\n\t\t{ 0, 0 },\n\t\t{ TGL_CONTEXT_STENCIL, TRUE },\n\t\t{ TAG_DONE }\n\t};\n\n\tif (IntuitionBase->LibNode.lib_Version > 50 || (IntuitionBase->LibNode.lib_Version == 50 && IntuitionBase->LibNode.lib_Revision >= 74))\n\t{\n\t\tmosgammatable = AllocVec(256*3, MEMF_ANY);\n\t\tif (mosgammatable)\n\t\t\tgammaworks = 1;\n\t}\n\n\tvid.pixelwidth = info->width;\n\tvid.pixelheight = info->height;\n\tvid.numpages = 3;\n\n\tif (vid.pixelwidth <= 640)\n\t{\n\t\tvid.width = vid.pixelwidth;\n\t\tvid.height = vid.pixelheight;\n\t}\n\telse\n\t{\n\t\tvid.width = vid.pixelwidth/2;\n\t\tvid.height = vid.pixelheight/2;\n\t}\n\n\tif ((i = COM_CheckParm(\"-conwidth\")) && i + 1 < com_argc)\n\t{\n\t\tvid.width = Q_atoi(com_argv[i + 1]);\n\n\t\t// pick a conheight that matches with correct aspect\n\t\tvid.height = vid.width * 3 / 4;\n\t}\n\n\tvid.width &= 0xfff8; // make it a multiple of eight\n\n\tif ((i = COM_CheckParm(\"-conheight\")) && i + 1 < com_argc)\n\t\tvid.height = Q_atoi(com_argv[i + 1]);\n\n\tif (vid.width < 320)\n\t\tvid.width = 320;\n\n\tif (vid.height < 200)\n\t\tvid.height = 200;\n\n\tTinyGLBase = OpenLibrary(\"tinygl.library\", 0);\n\tif (TinyGLBase)\n\t{\n\t\tif (TinyGLBase->lib_Version > 50 || (TinyGLBase->lib_Version == 50 && TinyGLBase->lib_Revision >= 9))\n\t\t{\n\t\t\tif (info->fullscreen)\n\t\t\t{\n\t\t\t\tscreen = OpenScreenTags(0,\n\t\t\t\t\tSA_Width, vid.width,\n\t\t\t\t\tSA_Height, vid.height,\n\t\t\t\t\tSA_Depth, depth,\n\t\t\t\t\tSA_Quiet, TRUE,\n\t\t\t\t\tSA_GammaControl, TRUE,\n\t\t\t\t\tSA_3DSupport, TRUE,\n\t\t\t\t\tTAG_DONE);\n\t\t\t}\n\n\t\t\twindow = OpenWindowTags(0,\n\t\t\t\tWA_InnerWidth, vid.width,\n\t\t\t\tWA_InnerHeight, vid.height,\n\t\t\t\tWA_Title, \"FTEQuake\",\n\t\t\t\tWA_DragBar, screen?FALSE:TRUE,\n\t\t\t\tWA_DepthGadget, screen?FALSE:TRUE,\n\t\t\t\tWA_Borderless, screen?TRUE:FALSE,\n\t\t\t\tWA_RMBTrap, TRUE,\n\t\t\t\tscreen?WA_PubScreen:TAG_IGNORE, (ULONG)screen,\n\t\t\t\tWA_Activate, TRUE,\n\t\t\t\tTAG_DONE);\n\n\t\t\tif (window)\n\t\t\t{\n\t\t\t\tif (screen == 0)\n\t\t\t\t\tgammaworks = 0;\n\n\t\t\t\t__tglContext = GLInit();\n\t\t\t\tif (__tglContext)\n\t\t\t\t{\n\t\t\t\t\tif (screen)\n\t\t\t\t\t{\n\t\t\t\t\t\ttgltags[0].ti_Tag = TGL_CONTEXT_SCREEN;\n\t\t\t\t\t\ttgltags[0].ti_Data = screen;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\ttgltags[0].ti_Tag = TGL_CONTEXT_WINDOW;\n\t\t\t\t\t\ttgltags[0].ti_Data = window;\n\t\t\t\t\t}\n\n\t\t\t\t\tr = GLAInitializeContext(__tglContext, tgltags);\n\n\t\t\t\t\tif (r)\n\t\t\t\t\t{\n\t\t\t\t\t\tglctx = 1;\n\n\t\t\t\t\t\tgl_stencilbits = 8;\n\n\t\t\t\t\t\tpointermem = AllocVec(256, MEMF_ANY|MEMF_CLEAR);\n\t\t\t\t\t\tif (pointermem)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tSetPointer(window, pointermem, 16, 16, 0, 0);\n\n#if 0\n\t\t\t\t\t\t\tlastwindowedmouse = 1;\n#endif\n\n\t\t\t\t\t\t\tif (vid.height > vid.pixelheight)\n\t\t\t\t\t\t\t\tvid.height = vid.pixelheight;\n\t\t\t\t\t\t\tif (vid.width > vid.pixelwidth)\n\t\t\t\t\t\t\t\tvid.width = vid.pixelwidth;\n\n\t\t\t\t\t\t\tGL_Init(&TinyGL_GetSymbol);\n\n\t\t\t\t\t\t\tVID_SetPalette(palette);\n\n\t\t\t\t\t\t\tvid.recalc_refdef = 1;\n\n\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tCon_Printf(\"Unable to allocate memory for mouse pointer\");\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (screen && !(TinyGLBase->lib_Version == 0 && TinyGLBase->lib_Revision < 4))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tglADestroyContextScreen();\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tglADestroyContextWindowed();\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tglctx = 0;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tCon_Printf(\"Unable to initialize GL context\");\n\t\t\t\t\t}\n\n\t\t\t\t\tGLClose(__tglContext);\n\t\t\t\t\t__tglContext = 0;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tCon_Printf(\"Unable to create GL context\");\n\t\t\t\t}\n\n\t\t\t\tif (screen)\n\t\t\t\t{\n\t\t\t\t\tCloseScreen(screen);\n\t\t\t\t\tscreen = 0;\n\t\t\t\t}\n\n\t\t\t\tCloseWindow(window);\n\t\t\t\twindow = 0;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tCon_Printf(\"Unable to open window\");\n\t\t\t}\n\n\t\t}\n\n\t\tCloseLibrary(TinyGLBase);\n\t\tTinyGLBase = 0;\n\t}\n\telse\n\t{\n\t\tCon_Printf(\"Couldn't open tinygl.library\");\n\t}\n\n\treturn false;\n}\n\nvoid GLVID_DeInit(void)\n{\n\tif (glctx)\n\t{\n\t\tif (screen && !(TinyGLBase->lib_Version == 0 && TinyGLBase->lib_Revision < 4))\n\t\t{\n\t\t\tglADestroyContextScreen();\n\t\t}\n\t\telse\n\t\t{\n\t\t\tglADestroyContextWindowed();\n\t\t}\n\n\t\tglctx = 0;\n\t}\n\n\tif (__tglContext)\n\t{\n\t\tGLClose(__tglContext);\n\t\t__tglContext = 0;\n\t}\n\n\tif (window)\n\t{\n\t\tCloseWindow(window);\n\t\twindow = 0;\n\t}\n\n\tif (pointermem)\n\t{\n\t\tFreeVec(pointermem);\n\t\tpointermem = 0;\n\t}\n\n\tif (screen)\n\t{\n\t\tCloseScreen(screen);\n\t\tscreen = 0;\n\t}\n\n\tif (TinyGLBase)\n\t{\n\t\tCloseLibrary(TinyGLBase);\n\t\tTinyGLBase = 0;\n\t}\n\n\tif (mosgammatable)\n\t{\n\t\tFreeVec(mosgammatable);\n\t\tmosgammatable = 0;\n\t}\n}\n\nvoid GLVID_SwapBuffers (void)\n{\n\tglASwapBuffers();\n}\n\nvoid GLVID_SetPalette (unsigned char *palette)\n{\n\tqbyte *pal;\n\tunsigned int r,g,b;\n\tint i;\n\tunsigned *table1;\n\textern qbyte gammatable[256];\n\n\tCon_Printf(\"Converting 8to24\\n\");\n\n\tpal = palette;\n\ttable1 = d_8to24rgbtable;\n\tfor (i=0 ; i<256 ; i++)\n\t{\n\t\tr = gammatable[pal[0]];\n\t\tg = gammatable[pal[1]];\n\t\tb = gammatable[pal[2]];\n\t\tpal += 3;\n\t\t\n\t\t*table1++ = LittleLong((255<<24) + (r<<0) + (g<<8) + (b<<16));\n\t}\n\td_8to24rgbtable[255] &= LittleLong(0xffffff);\t// 255 is transparent\n}\n\nvoid GLVID_ShiftPalette (unsigned char *palette)\n{\n\tint i;\n\n\tif (gammaworks)\n\t{\n\t\tfor(i=0;i<768;i++)\n\t\t{\n\t\t\tmosgammatable[i] = ramps[0][i]>>8;\n\t\t}\n\n\t\tSetAttrs(screen,\n\t\t\tSA_GammaRed, mosgammatable,\n\t\t\tSA_GammaGreen, mosgammatable+256,\n\t\t\tSA_GammaBlue, mosgammatable+512,\n\t\t\tTAG_DONE);\n\t}\n}\n\nvoid Sys_SendKeyEvents(void)\n{\n}\n\nvoid GLVID_SetCaption(char *caption)\n{\n\tSetWindowTitles(window, caption, (void *)-1);\n}\n\n"
  },
  {
    "path": "engine/gl/gl_vidnt.c",
    "content": "/*\nCopyright (C) 1996-1997 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n*/\n// gl_vidnt.c -- NT GL vid component\n// note that this file also handles win32 vulkan window things\n// and can also use either EGL or WGL.\n\n#include \"quakedef.h\"\n#if defined(_WIN32) && (defined(GLQUAKE) || defined(VKQUAKE))\n#include \"glquake.h\"\n#include \"winquake.h\"\n#include \"resource.h\"\n#include \"shader.h\"\n#include \"vr.h\"\n#include <commctrl.h>\n\n#ifdef USE_EGL\n\t#ifdef GLQUAKE\n\t\t#include \"gl_videgl.h\"\n\t#else\n\t\t#undef USE_EGL\n\t#endif\n#endif\n#ifdef GLQUAKE\n\t#define USE_WGL\n#endif\n#ifdef VKQUAKE\n\t#include \"../vk/vkrenderer.h\"\n#endif\n\nstatic enum\n{\n#ifdef USE_WGL\n\tMODE_WGL,\n#endif\n#ifdef USE_EGL\n\tMODE_EGL,\n#endif\n#ifdef VKQUAKE\n\tMODE_VULKAN,\t//proper vulkan\n\t#ifdef USE_WGL\n\t\tMODE_NVVULKAN,\t//vulkan accessed via nvidia's render-to-image-then-copy-to-gl-backbuffer-then-copy-that opengl extension.\n\t#endif\n#endif\n} platform_rendermode;\n\n\nvoid STT_Event(void);\n\n#ifndef SetWindowLongPtr\t//yes its a define, for unicode support\n#define SetWindowLongPtr SetWindowLong\n#endif\n\n#ifndef CDS_FULLSCREEN\n\t#define CDS_FULLSCREEN 4\n#endif\n\n#ifndef WM_XBUTTONDOWN\n   #define WM_XBUTTONDOWN      0x020B\n   #define WM_XBUTTONUP      0x020C\n#endif\n#ifndef MK_XBUTTON1\n   #define MK_XBUTTON1         0x0020\n#endif\n#ifndef MK_XBUTTON2\n   #define MK_XBUTTON2         0x0040\n#endif\n// copied from DarkPlaces in an attempt to grab more buttons\n#ifndef MK_XBUTTON3\n   #define MK_XBUTTON3         0x0080\n#endif\n#ifndef MK_XBUTTON4\n   #define MK_XBUTTON4         0x0100\n#endif\n#ifndef MK_XBUTTON5\n   #define MK_XBUTTON5         0x0200\n#endif\n#ifndef MK_XBUTTON6\n   #define MK_XBUTTON6         0x0400\n#endif\n#ifndef MK_XBUTTON7\n   #define MK_XBUTTON7         0x0800\n#endif\n\n#ifndef WM_INPUT\n\t#define WM_INPUT 255\n#endif\n\n#ifndef WS_EX_LAYERED\n\t#define WS_EX_LAYERED 0x00080000\n#endif\n#ifndef LWA_ALPHA\n\t#define LWA_ALPHA 0x00000002\n#endif\ntypedef BOOL (WINAPI *lpfnSetLayeredWindowAttributes)(HWND hwnd, COLORREF crKey, BYTE bAlpha, DWORD dwFlags);\n\nextern cvar_t vid_conwidth, vid_conautoscale;\n\n\n#define WINDOW_CLASS_NAME_W L\"FTEGLQuake\"\n#define WINDOW_CLASS_NAME_A \"FTEGLQuake\"\n\nextern cvar_t vid_width;\nextern cvar_t vid_height;\nextern cvar_t vid_wndalpha;\nextern cvar_t vid_winthread;\n\nstatic qboolean VID_SetWindowedMode (rendererstate_t *info);\t//-1 on bpp or hz for default.\nstatic qboolean VID_SetFullDIBMode (rendererstate_t *info);\t//-1 on bpp or hz for default.\n\n#ifdef MULTITHREAD\n#define WTHREAD\t//While the user is resizing a window, the entire thread that owns said window becomes frozen. in order to cope with window resizing, its easiest to just create a separate thread to be microsoft's plaything. our main game thread can then just keep rendering. hopefully that won't bug out on the present.\n#endif\n#ifdef WTHREAD\nstatic HANDLE\twindowthread;\n#endif\n\nstatic DEVMODE\tgdevmode;\nstatic qboolean\tvid_initialized = false;\nstatic qboolean vid_canalttab = false;\nstatic qboolean vid_wassuspended = false;\nextern qboolean\tmouseactive;  // from in_win.c\nstatic HICON\thIcon;\nextern qboolean vid_isfullscreen;\n\nstatic unsigned short originalgammaramps[3][256];\n\nstatic qboolean vid_initializing;\n\nstatic int\t\t\tDIBWidth, DIBHeight;\nstatic RECT\t\tWindowRect;\nstatic DWORD\t\tWindowStyle, ExWindowStyle;\n\nstatic HWND dibwindow;\n\nstatic HDC\t\tmaindc;\n\nHWND WINAPI InitializeWindow (HINSTANCE hInstance, int nCmdShow);\n\n//unsigned short\td_8to16rgbtable[256];\n//unsigned\td_8to24rgbtable[256];\n//unsigned short\td_8to16bgrtable[256];\n//unsigned\td_8to24bgrtable[256];\n\nstatic enum {MS_WINDOWED, MS_FULLDIB, MS_FULLWINDOW, MS_UNINIT}\tmodestate = MS_UNINIT;\n\nextern float gammapending;\n\n\n//====================================\n// Note that 0 is MODE_WINDOWED\nextern cvar_t\tvid_mode;\n// Note that 3 is MODE_FULLSCREEN_DEFAULT\nextern cvar_t\t\tvid_vsync;\nextern cvar_t\t\tvid_hardwaregamma;\nextern cvar_t\t\tvid_desktopgamma;\nextern cvar_t\t\tgl_lateswap;\nextern cvar_t\t\tvid_preservegamma;\n\nstatic int\t\t\twindow_x, window_y;\nstatic int\t\t\twindow_width, window_height;\n\n\nstatic LONG WINAPI GLMainWndProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);\nstatic qboolean GLAppActivate(BOOL fActive, BOOL minimize);\nstatic void ClearAllStates (void);\nstatic void VID_UpdateWindowStatus (HWND hWnd);\n\nstatic BOOL (WINAPI *qGetDeviceGammaRamp)(HDC hDC, void *ramp);\nstatic BOOL (WINAPI *qSetDeviceGammaRamp)(HDC hDC, void *ramp);\n\n//==========================================================\n#ifdef USE_WGL\nstatic HGLRC\tbaseRC;\nstatic const char *wgl_extensions;\n\nstatic qboolean VID_AttachGL (rendererstate_t *info);\nstatic BOOL bSetupPixelFormat(HDC hDC, rendererstate_t *info);\nstatic BOOL CheckForcePixelFormat(rendererstate_t *info);\n\nextern cvar_t\t\tvid_gl_context_version;\nextern cvar_t\t\tvid_gl_context_debug;\nextern cvar_t\t\tvid_gl_context_es;\nextern cvar_t\t\tvid_gl_context_forwardcompatible;\nextern cvar_t\t\tvid_gl_context_compatibility;\nextern cvar_t\t\tvid_gl_context_robustness;\nextern cvar_t\t\tvid_gl_context_selfreset;\nextern cvar_t\t\tvid_gl_context_noerror;\n\nstatic dllhandle_t *hInstGL = NULL;\nstatic dllhandle_t *hInstwgl = NULL;\nstatic qboolean usingminidriver;\nstatic char reqminidriver[MAX_OSPATH];\nstatic char opengldllname[MAX_OSPATH];\n\n\nstatic HGLRC (WINAPI *qwglCreateContext)(HDC);\nstatic BOOL  (WINAPI *qwglDeleteContext)(HGLRC);\nstatic HGLRC (WINAPI *qwglGetCurrentContext)(VOID);\nstatic HDC   (WINAPI *qwglGetCurrentDC)(VOID);\nstatic PROC  (WINAPI *qwglGetProcAddress)(LPCSTR);\nstatic BOOL  (WINAPI *qwglMakeCurrent)(HDC, HGLRC);\nstatic BOOL  (WINAPI *qSwapBuffers)(HDC);\nstatic BOOL  (WINAPI *qwglSwapLayerBuffers)(HDC, UINT);\nstatic int   (WINAPI *qChoosePixelFormat)(HDC, CONST PIXELFORMATDESCRIPTOR *);\nstatic BOOL  (WINAPI *qSetPixelFormat)(HDC, int, CONST PIXELFORMATDESCRIPTOR *);\nstatic int   (WINAPI *qDescribePixelFormat)(HDC, int, UINT, LPPIXELFORMATDESCRIPTOR);\n\nstatic BOOL (WINAPI *qwglSwapIntervalEXT) (int);\n\nstatic BOOL (APIENTRY *qwglChoosePixelFormatARB)(HDC hdc, const int *piAttribIList, const FLOAT *pfAttribFList, UINT nMaxFormats, int* piFormats, UINT* nNumFormats);\nstatic BOOL (APIENTRY *qwglGetPixelFormatAttribfvARB)(HDC hdc, int iPixelFormat, int iLayerPlane, UINT nAttributes, int *piAttributes, FLOAT *pfValues);\n\nstatic HGLRC (APIENTRY *qwglCreateContextAttribsARB)(HDC hDC, HGLRC hShareContext, const int *attribList);\n#define WGL_CONTEXT_MAJOR_VERSION_ARB\t\t\t\t\t0x2091\n#define WGL_CONTEXT_MINOR_VERSION_ARB\t\t\t\t\t0x2092\n#define WGL_CONTEXT_LAYER_PLANE_ARB\t\t\t\t\t\t0x2093\n#define WGL_CONTEXT_FLAGS_ARB\t\t\t\t\t\t\t0x2094\n#define\t\tWGL_CONTEXT_DEBUG_BIT_ARB\t\t\t\t\t\t0x0001\n#define\t\tWGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB\t\t\t0x0002\n#define\t\tWGL_CONTEXT_ROBUST_ACCESS_BIT_ARB\t\t\t\t0x0004 /*WGL_ARB_create_context_robustness*/\n#define WGL_CONTEXT_PROFILE_MASK_ARB\t\t\t\t\t0x9126\t\t/*WGL_ARB_create_context_profile*/\n#define\t\tWGL_CONTEXT_CORE_PROFILE_BIT_ARB\t\t\t\t0x00000001\n#define\t\tWGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB\t\t0x00000002\n#define\t\tWGL_CONTEXT_ES2_PROFILE_BIT_EXT\t\t\t\t\t0x00000004\t/*WGL_CONTEXT_ES2_PROFILE_BIT_EXT*/\n#define ERROR_INVALID_VERSION_ARB\t\t\t\t\t\t0x2095\n#define\tERROR_INVALID_PROFILE_ARB\t\t\t\t\t\t0x2096\n#define WGL_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB\t\t0x8256\t/*WGL_ARB_create_context_robustness*/\n#define\t\tWGL_NO_RESET_NOTIFICATION_ARB\t\t\t\t\t0x8261\n#define\t\tWGL_LOSE_CONTEXT_ON_RESET_ARB\t\t\t\t\t0x8252\n#define\tWGL_CONTEXT_OPENGL_NO_ERROR_ARB\t\t\t\t\t0x31B3 /*WGL_ARB_create_context_no_error*/\n\n\n//pixel format stuff\n#define \tWGL_DRAW_TO_WINDOW_ARB\t\t0x2001\n#define \tWGL_ACCELERATION_ARB\t\t0x2003\n#define\t\tWGL_SWAP_LAYER_BUFFERS_ARB\t0x2006\n#define \tWGL_SUPPORT_OPENGL_ARB\t\t0x2010\n#define \tWGL_DOUBLE_BUFFER_ARB\t\t0x2011\n#define\t\tWGL_STEREO_ARB\t\t\t\t0x2012\n#define \tWGL_COLOR_BITS_ARB\t\t\t0x2014\n#define \tWGL_ALPHA_BITS_ARB\t\t\t0x201B\n#define \tWGL_DEPTH_BITS_ARB\t\t\t0x2022\n#define \tWGL_STENCIL_BITS_ARB\t\t0x2023\n#define \tWGL_FULL_ACCELERATION_ARB\t0x2027\n#define\t\tWGL_PIXEL_TYPE_ARB\t\t\t0x2013\n#define\t\t\tWGL_TYPE_RGBA_ARB\t\t0x202B\n#define\t\t\tWGL_TYPE_RGBA_FLOAT_ARB\t0x21A0\n#define\t\tWGL_RED_BITS_ARB\t\t\t0x2015\n#define\t\tWGL_GREEN_BITS_ARB\t\t\t0x2017\n#define\t\tWGL_BLUE_BITS_ARB\t\t\t0x2019\n\n\n\n#ifdef _DEBUG\n//this is a list of the functions that exist in opengles2, as well as wglCreateContextAttribsARB.\n//functions not in this list *should* be stubs that just return errors, but we can't always depend on drivers for that... they shouldn't get called.\n//this list is just to make it easier to test+debug android gles2 stuff using windows.\nstatic char *gles2funcs[] =\n{\n#define f(n) #n,\n\t\tf(glActiveTexture)\n\t\tf(glAttachShader)\n\t\tf(glBindAttribLocation)\n\t\tf(glBindBuffer)\n\t\tf(glBindFramebuffer)\n\t\tf(glBindRenderbuffer)\n\t\tf(glBindTexture)\n\t\tf(glBlendColor)\n\t\tf(glBlendEquation)\n\t\tf(glBlendEquationSeparate)\n\t\tf(glBlendFunc)\n\t\tf(glBlendFuncSeparate)\n\t\tf(glBufferData)\n\t\tf(glBufferSubData)\n\t\tf(glCheckFramebufferStatus)\n\t\tf(glClear)\n\t\tf(glClearColor)\n\t\tf(glClearDepthf)\n\t\tf(glClearStencil)\n\t\tf(glColorMask)\n\t\tf(glCompileShader)\n\t\tf(glCompressedTexImage2D)\n\t\tf(glCompressedTexSubImage2D)\n\t\tf(glCopyTexImage2D)\n\t\tf(glCopyTexSubImage2D)\n\t\tf(glCreateProgram)\n\t\tf(glCreateShader)\n\t\tf(glCullFace)\n\t\tf(glDeleteBuffers)\n\t\tf(glDeleteFramebuffers)\n\t\tf(glDeleteProgram)\n\t\tf(glDeleteRenderbuffers)\n\t\tf(glDeleteShader)\n\t\tf(glDeleteTextures)\n\t\tf(glDepthFunc)\n\t\tf(glDepthMask)\n\t\tf(glDepthRangef)\n\t\tf(glDetachShader)\n\t\tf(glDisable)\n\t\tf(glDisableVertexAttribArray)\n\t\tf(glDrawArrays)\n\t\tf(glDrawElements)\n\t\tf(glEnable)\n\t\tf(glEnableVertexAttribArray)\n\t\tf(glFinish)\n\t\tf(glFlush)\n\t\tf(glFramebufferRenderbuffer)\n\t\tf(glFramebufferTexture2D)\n\t\tf(glFrontFace)\n\t\tf(glGenBuffers)\n\t\tf(glGenerateMipmap)\n\t\tf(glGenFramebuffers)\n \t\tf(glGenRenderbuffers)\n\t\tf(glGenTextures)\n\t\tf(glGetActiveAttrib)\n\t\tf(glGetActiveUniform)\n\t\tf(glGetAttachedShaders)\n\t\tf(glGetAttribLocation)\n\t\tf(glGetBooleanv)\n\t\tf(glGetBufferParameteriv)\n\t\tf(glGetError)\n\t\tf(glGetFloatv)\n\t\tf(glGetFramebufferAttachmentParameteriv)\n\t\tf(glGetIntegerv)\n\t\tf(glGetProgramiv)\n\t\tf(glGetProgramInfoLog)\n\t\tf(glGetRenderbufferParameteriv)\n\t\tf(glGetShaderiv)\n\t\tf(glGetShaderInfoLog)\n\t\tf(glGetShaderPrecisionFormat)\n\t\tf(glGetShaderSource)\n\t\tf(glGetString)\n\t\tf(glGetTexParameterfv)\n\t\tf(glGetTexParameteriv)\n\t\tf(glGetUniformfv)\n\t\tf(glGetUniformiv)\n\t\tf(glGetUniformLocation)\n\t\tf(glGetVertexAttribfv)\n\t\tf(glGetVertexAttribiv)\n\t\tf(glGetVertexAttribPointerv)\n\t\tf(glHint)\n\t\tf(glIsBuffer)\n\t\tf(glIsEnabled)\n\t\tf(glIsFramebuffer)\n\t\tf(glIsProgram)\n\t\tf(glIsRenderbuffer)\n\t\tf(glIsShader)\n\t\tf(glIsTexture)\n\t\tf(glLineWidth)\n\t\tf(glLinkProgram)\n\t\tf(glPixelStorei)\n\t\tf(glPolygonOffset)\n\t\tf(glReadPixels)\n\t\tf(glReleaseShaderCompiler)\n\t\tf(glRenderbufferStorage)\n\t\tf(glSampleCoverage)\n\t\tf(glScissor)\n\t\tf(glShaderBinary)\n\t\tf(glShaderSource)\n\t\tf(glStencilFunc)\n\t\tf(glStencilFuncSeparate)\n\t\tf(glStencilMask)\n\t\tf(glStencilMaskSeparate)\n\t\tf(glStencilOp)\n\t\tf(glStencilOpSeparate)\n\t\tf(glTexImage2D)\n\t\tf(glTexParameterf)\n\t\tf(glTexParameterfv)\n\t\tf(glTexParameteri)\n\t\tf(glTexParameteriv)\n\t\tf(glTexSubImage2D)\n\t\tf(glUniform1f)\n\t\tf(glUniform1fv)\n\t\tf(glUniform1i)\n\t\tf(glUniform1iv)\n\t\tf(glUniform2f)\n\t\tf(glUniform2fv)\n\t\tf(glUniform2i)\n\t\tf(glUniform2iv)\n\t\tf(glUniform3f)\n\t\tf(glUniform3fv)\n\t\tf(glUniform3i)\n\t\tf(glUniform3iv)\n\t\tf(glUniform4f)\n\t\tf(glUniform4fv)\n\t\tf(glUniform4i)\n\t\tf(glUniform4iv)\n\t\tf(glUniformMatrix2fv)\n\t\tf(glUniformMatrix3fv)\n\t\tf(glUniformMatrix4fv)\n\t\tf(glUseProgram)\n\t\tf(glValidateProgram)\n\t\tf(glVertexAttrib1f)\n\t\tf(glVertexAttrib1fv)\n\t\tf(glVertexAttrib2f)\n\t\tf(glVertexAttrib2fv)\n\t\tf(glVertexAttrib3f)\n\t\tf(glVertexAttrib3fv)\n\t\tf(glVertexAttrib4f)\n\t\tf(glVertexAttrib4fv)\n\t\tf(glVertexAttribPointer)\n\t\tf(glViewport)\n\t\tf(wglCreateContextAttribsARB)\n\t\tNULL\n};\n\n//this is a list of the functions that exist in opengles2, as well as wglCreateContextAttribsARB.\n//functions not in this list *should* be stubs that just return errors, but we can't always depend on drivers for that... they shouldn't get called.\n//this list is just to make it easier to test+debug android gles2 stuff using windows.\nstatic char *gles1funcs[] =\n{\n#define f(n) #n,\n\n\t\t/* Available only in Common profile */\n\t\tf(glAlphaFunc)\n\t\tf(glClearColor)\n\t\tf(glClearDepthf)\n\t\tf(glClipPlanef)\n\t\tf(glColor4f)\n\t\tf(glDepthRangef)\n\t\tf(glFogf)\n\t\tf(glFogfv)\n\t\tf(glFrustumf)\n\t\tf(glGetClipPlanef)\n\t\tf(glGetFloatv)\n\t\tf(glGetLightfv)\n\t\tf(glGetMaterialfv)\n\t\tf(glGetTexEnvfv)\n\t\tf(glGetTexParameterfv)\n\t\tf(glLightModelf)\n\t\tf(glLightModelfv)\n\t\tf(glLightf)\n\t\tf(glLightfv)\n\t\tf(glLineWidth)\n\t\tf(glLoadMatrixf)\n\t\tf(glMaterialf)\n\t\tf(glMaterialfv)\n\t\tf(glMultMatrixf)\n\t\tf(glMultiTexCoord4f)\n\t\tf(glNormal3f)\n\t\tf(glOrthof)\n\t\tf(glPointParameterf)\n\t\tf(glPointParameterfv)\n\t\tf(glPointSize)\n\t\tf(glPolygonOffset)\n\t\tf(glRotatef)\n\t\tf(glScalef)\n\t\tf(glTexEnvf)\n\t\tf(glTexEnvfv)\n\t\tf(glTexParameterf)\n\t\tf(glTexParameterfv)\n\t\tf(glTranslatef)\n\n\t\t/* Available in both Common and Common-Lite profiles */\n\t\tf(glActiveTexture)\n\t\tf(glAlphaFuncx)\n\t\tf(glBindBuffer)\n\t\tf(glBindTexture)\n\t\tf(glBlendFunc)\n\t\tf(glBufferData)\n\t\tf(glBufferSubData)\n\t\tf(glClear)\n\t\tf(glClearColorx)\n\t\tf(glClearDepthx)\n\t\tf(glClearStencil)\n\t\tf(glClientActiveTexture)\n\t\tf(glClipPlanex)\n\t\tf(glColor4ub)\n\t\tf(glColor4x)\n\t\tf(glColorMask)\n\t\tf(glColorPointer)\n\t\tf(glCompressedTexImage2D)\n\t\tf(glCompressedTexSubImage2D)\n\t\tf(glCopyTexImage2D)\n\t\tf(glCopyTexSubImage2D)\n\t\tf(glCullFace)\n\t\tf(glDeleteBuffers)\n\t\tf(glDeleteTextures)\n\t\tf(glDepthFunc)\n\t\tf(glDepthMask)\n\t\tf(glDepthRangex)\n\t\tf(glDisable)\n\t\tf(glDisableClientState)\n\t\tf(glDrawArrays)\n\t\tf(glDrawElements)\n\t\tf(glEnable)\n\t\tf(glEnableClientState)\n\t\tf(glFinish)\n\t\tf(glFlush)\n\t\tf(glFogx)\n\t\tf(glFogxv)\n\t\tf(glFrontFace)\n\t\tf(glFrustumx)\n\t\tf(glGetBooleanv)\n\t\tf(glGetBufferParameteriv)\n\t\tf(glGetClipPlanex)\n\t\tf(glGenBuffers)\n\t\tf(glGenTextures)\n\t\tf(glGetError)\n\t\tf(glGetFixedv)\n\t\tf(glGetIntegerv)\n\t\tf(glGetLightxv)\n\t\tf(glGetMaterialxv)\n\t\tf(glGetPointerv)\n\t\tf(glGetString)\n\t\tf(glGetTexEnviv)\n\t\tf(glGetTexEnvxv)\n\t\tf(glGetTexParameteriv)\n\t\tf(glGetTexParameterxv)\n\t\tf(glHint)\n\t\tf(glIsBuffer)\n\t\tf(glIsEnabled)\n\t\tf(glIsTexture)\n\t\tf(glLightModelx)\n\t\tf(glLightModelxv)\n\t\tf(glLightx)\n\t\tf(glLightxv)\n\t\tf(glLineWidthx)\n\t\tf(glLoadIdentity)\n\t\tf(glLoadMatrixx)\n\t\tf(glLogicOp)\n\t\tf(glMaterialx)\n\t\tf(glMaterialxv)\n\t\tf(glMatrixMode)\n\t\tf(glMultMatrixx)\n\t\tf(glMultiTexCoord4x)\n\t\tf(glNormal3x)\n\t\tf(glNormalPointer)\n\t\tf(glOrthox)\n\t\tf(glPixelStorei)\n\t\tf(glPointParameterx)\n\t\tf(glPointParameterxv)\n\t\tf(glPointSizex)\n\t\tf(glPolygonOffsetx)\n\t\tf(glPopMatrix)\n\t\tf(glPushMatrix)\n\t\tf(glReadPixels)\n\t\tf(glRotatex)\n\t\tf(glSampleCoverage)\n\t\tf(glSampleCoveragex)\n\t\tf(glScalex)\n\t\tf(glScissor)\n\t\tf(glShadeModel)\n\t\tf(glStencilFunc)\n\t\tf(glStencilMask)\n\t\tf(glStencilOp)\n\t\tf(glTexCoordPointer)\n\t\tf(glTexEnvi)\n\t\tf(glTexEnvx)\n\t\tf(glTexEnviv)\n\t\tf(glTexEnvxv)\n\t\tf(glTexImage2D)\n\t\tf(glTexParameteri)\n\t\tf(glTexParameterx)\n\t\tf(glTexParameteriv)\n\t\tf(glTexParameterxv)\n\t\tf(glTexSubImage2D)\n\t\tf(glTranslatex)\n\t\tf(glVertexPointer)\n\t\tf(glViewport)\n\n\t\t/*required to switch stuff around*/\n\t\tf(wglCreateContextAttribsARB)\n\t\tNULL\n};\n#endif\n\n//just GetProcAddress with a safty net.\nstatic void *getglfunc(char *name)\n{\n\tFARPROC proc;\n\tproc = qwglGetProcAddress?qwglGetProcAddress(name):NULL;\n\tif (!proc)\n\t{\n\t\tproc = GetProcAddress(hInstGL, name);\n\t\tTRACE((\"dbg: getglfunc: gpa %s: success %i\\n\", name, !!proc));\n\t}\n\telse\n\t{\n\t\tTRACE((\"dbg: getglfunc: glgpa %s: success %i\\n\", name, !!proc));\n\t}\n\n#ifdef _DEBUG\n\tif (vid_gl_context_es.ival == 3)\n\t{\n\t\tint i;\n\t\tfor (i = 0; gles1funcs[i]; i++)\n\t\t{\n\t\t\tif (!strcmp(name, gles1funcs[i]))\n\t\t\t\treturn proc;\n\t\t}\n\t\treturn NULL;\n\t}\n\tif (vid_gl_context_es.ival == 2)\n\t{\n\t\tint i;\n\t\tfor (i = 0; gles2funcs[i]; i++)\n\t\t{\n\t\t\tif (!strcmp(name, gles2funcs[i]))\n\t\t\t\treturn proc;\n\t\t}\n\t\treturn NULL;\n\t}\n#endif\n\treturn proc;\n}\nstatic void *getwglfunc(char *name)\n{\n\tFARPROC proc;\n\tTRACE((\"dbg: getwglfunc: %s: getting\\n\", name));\n\n\tproc = GetProcAddress(hInstGL, name);\n\tif (!proc)\n\t{\n\t\tif (!hInstwgl)\n\t\t{\n\t\t\tTRACE((\"dbg: getwglfunc: explicitly loading opengl32.dll\\n\", name));\n\t\t\thInstwgl = LoadLibraryA(\"opengl32.dll\");\n\t\t}\n\t\tTRACE((\"dbg: getwglfunc: %s: wglgetting\\n\", name));\n\t\tproc = GetProcAddress(hInstwgl, name);\n\t\tTRACE((\"dbg: getwglfunc: gpa %s: success %i\\n\", name, !!proc));\n\t\tif (!proc)\n\t\t\tSys_Error(\"GL function %s was not found in %s\\nPossibly you do not have a full enough gl implementation\", name, opengldllname);\n\t}\n\tTRACE((\"dbg: getwglfunc: glgpa %s: success %i\\n\", name, !!proc));\n\treturn proc;\n}\n\nstatic qboolean shouldforcepixelformat;\nstatic int forcepixelformat;\nstatic int currentpixelformat;\n\nstatic qboolean GLInitialise (char *renderer)\n{\n\tif (!hInstGL || strcmp(reqminidriver, renderer))\n\t{\n\t\tusingminidriver = false;\n\t\tif (hInstGL)\n\t\t\tSys_CloseLibrary(hInstGL);\n\t\thInstGL=NULL;\n\t\tif (hInstwgl)\n\t\t\tSys_CloseLibrary(hInstwgl);\n\t\thInstwgl=NULL;\n\n\t\tQ_strncpyz(reqminidriver, renderer, sizeof(reqminidriver));\n\t\tQ_strncpyz(opengldllname, renderer, sizeof(opengldllname));\n\n\t\tif (*renderer && stricmp(renderer, \"opengl32.dll\") && stricmp(renderer, \"opengl32\"))\n\t\t{\n\t\t\tunsigned int emode = SetErrorMode(SEM_FAILCRITICALERRORS); /*no annoying errors if they use glide*/\n\t\t\tCon_DPrintf (\"Loading renderer dll \\\"%s\\\"\", renderer);\n\t\t\thInstGL = Sys_LoadLibrary(opengldllname, NULL);\n\t\t\tSetErrorMode(emode);\n\t\t\tif (hInstGL)\n\t\t\t{\n\t\t\t\tusingminidriver = true;\n\t\t\t\tCon_DPrintf (\" Success\\n\");\n\t\t\t}\n\t\t\telse\n\t\t\t\tCon_DPrintf (\" Failed\\n\");\n\t\t}\n\t\telse\n\t\t\thInstGL = NULL;\n\n\t\tif (!hInstGL)\n\t\t{\t//gog has started shipping glquake using a 3dfxopengl->nglide->direct3d chain of wrappers.\n\t\t\t//this bypasses issues with (not that) recent gl drivers giving up on limiting extension string lengths and paletted textures\n\t\t\t//instead, we explicitly try to use the opengl32.dll from the windows system32 directory to try and avoid using the wrapper.\n\t\t\tunsigned int emode;\n\t\t\twchar_t wbuffer[MAX_OSPATH];\n\t\t\tGetSystemDirectoryW(wbuffer, countof(wbuffer));\n\t\t\tnarrowen(opengldllname, sizeof(opengldllname), wbuffer);\n\t\t\tQ_strncatz(opengldllname, \"\\\\opengl32.dll\", sizeof(opengldllname));\n\t\t\tCon_DPrintf (\"Loading renderer dll \\\"%s\\\"\", opengldllname);\n\t\t\temode = SetErrorMode(SEM_FAILCRITICALERRORS);\n\t\t\thInstGL = Sys_LoadLibrary(opengldllname, NULL);\n\t\t\tSetErrorMode(emode);\n\n\t\t\tif (hInstGL)\n\t\t\t\tCon_DPrintf (\" Success\\n\");\n\t\t\telse\n\t\t\t\tCon_DPrintf (\" Failed\\n\");\n\t\t}\n\n\t\tif (!hInstGL)\n\t\t{\n\t\t\tunsigned int emode;\n\t\t\tstrcpy(opengldllname, \"opengl32\");\n\t\t\tCon_DPrintf (\"Loading renderer dll \\\"%s\\\"\", opengldllname);\n\t\t\temode = SetErrorMode(SEM_FAILCRITICALERRORS); /*no annoying errors if they use glide*/\n\t\t\thInstGL = Sys_LoadLibrary(opengldllname, NULL);\n\t\t\tSetErrorMode(emode);\n\n\t\t\tif (hInstGL)\n\t\t\t\tCon_DPrintf (\" Success\\n\");\n\t\t\telse\n\t\t\t\tCon_DPrintf (\" Failed\\n\");\n\t\t}\n\t\tif (!hInstGL)\n\t\t{\n\t\t\tif (*renderer)\n\t\t\t\tCon_Printf (\"Couldn't load %s or %s\\n\", renderer, opengldllname);\n\t\t\telse\n\t\t\t\tCon_Printf (\"Couldn't load %s\\n\", opengldllname);\n\t\t\treturn false;\n\t\t}\n\t}\n\telse\n\t{\n\t\tCon_DPrintf (\"Reusing renderer dll %s\\n\", opengldllname);\n\t}\n\n\tCon_DPrintf (\"Loaded renderer dll %s\\n\", opengldllname);\n\n\t// windows dependant\n\tqwglCreateContext\t\t= (void *)getwglfunc(\"wglCreateContext\");\n\tqwglDeleteContext\t\t= (void *)getwglfunc(\"wglDeleteContext\");\n\tqwglGetCurrentContext\t= (void *)getwglfunc(\"wglGetCurrentContext\");\n\tqwglGetCurrentDC\t\t= (void *)getwglfunc(\"wglGetCurrentDC\");\n\tqwglGetProcAddress\t\t= (void *)getwglfunc(\"wglGetProcAddress\");\n\tqwglMakeCurrent\t\t\t= (void *)getwglfunc(\"wglMakeCurrent\");\n\n\tif (usingminidriver)\n\t{\n\t\tqwglSwapLayerBuffers\t= NULL;\n\t\tqSwapBuffers\t\t\t= (void *)getglfunc(\"wglSwapBuffers\");\n\t\tqChoosePixelFormat\t\t= (void *)getglfunc(\"wglChoosePixelFormat\");\n\t\tqSetPixelFormat\t\t\t= (void *)getglfunc(\"wglSetPixelFormat\");\n\t\tqDescribePixelFormat\t= (void *)getglfunc(\"wglDescribePixelFormat\");\n\t}\n\telse\n\t{\n\t\tqwglSwapLayerBuffers\t= NULL;//(void *)getwglfunc(\"wglSwapLayerBuffers\");\n\t\tqSwapBuffers\t\t\t= SwapBuffers;\n\t\tqChoosePixelFormat\t\t= ChoosePixelFormat;\n\t\tqSetPixelFormat\t\t\t= SetPixelFormat;\n\t\tqDescribePixelFormat\t= DescribePixelFormat;\n\t}\n\n\tqGetDeviceGammaRamp\t\t\t= (void *)getglfunc(\"wglGetDeviceGammaRamp3DFX\");\n\tqSetDeviceGammaRamp\t\t\t= (void *)getglfunc(\"wglSetDeviceGammaRamp3DFX\");\n\n\tTRACE((\"dbg: GLInitialise: got wgl funcs\\n\"));\n\n\treturn true;\n}\n\nstatic void ReleaseGL(void)\n{\n\tHGLRC\thRC;\n\tHDC\t\thDC = NULL;\n\n\tif (qwglGetCurrentContext)\n\t{\n\t\thRC = qwglGetCurrentContext();\n\t\thDC = qwglGetCurrentDC();\n\n\t\tqwglMakeCurrent(NULL, NULL);\n\n\t\tif (hRC)\n\t\t\tqwglDeleteContext(hRC);\n\t}\n\tqwglGetCurrentContext=NULL;\n\n\tif (hDC && dibwindow)\n\t\tReleaseDC(dibwindow, hDC);\n}\n#endif\t//USE_WGL\n\n#ifdef VKQUAKE\nstatic dllhandle_t *hInstVulkan = NULL;\nstatic qboolean Win32VK_CreateSurface(void)\n{\n\tVkResult err;\n\tVkWin32SurfaceCreateInfoKHR createInfo = {VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR};\n\tcreateInfo.flags = 0;\n\tcreateInfo.hinstance = GetModuleHandle(NULL);\n\tcreateInfo.hwnd = mainwindow;\n\n\terr = vkCreateWin32SurfaceKHR(vk.instance, &createInfo, NULL, &vk.surface);\n\tswitch(err)\n\t{\n\tdefault:\n\t\tCon_Printf(\"Unknown vulkan device creation error: %x\\n\", err);\n\t\treturn false;\n\tcase VK_SUCCESS:\n\t\tbreak;\n\t}\n\treturn true;\n}\n\n#ifdef WTHREAD\nstatic void Win32VK_Present(struct vkframe *theframe)\n{\n//\tif (theframe)\n//\t\tPostMessage(mainwindow, WM_USER_VKPRESENT, 0, (LPARAM)theframe);\n//\telse\n\t\tSendMessage(mainwindow, WM_USER_VKPRESENT, 0, (LPARAM)theframe);\n}\n#else\n#define Win32VK_Present NULL\n#endif\n\nstatic qboolean Win32VK_AttachVulkan (rendererstate_t *info)\n{\t//make sure we can get a valid renderer.\n\tconst char *extnames[] = {VK_KHR_WIN32_SURFACE_EXTENSION_NAME};\n#ifdef VK_NO_PROTOTYPES\n\thInstVulkan = NULL;\n\tif (!hInstVulkan)\n\t\thInstVulkan = *info->subrenderer?LoadLibrary(info->subrenderer):NULL;\n\tif (!hInstVulkan)\n\t\thInstVulkan = LoadLibrary(\"vulkan-1.dll\");\n\tif (!hInstVulkan)\n\t{\n\t\tCon_Printf(\"Unable to load vulkan-1.dll\\nNo Vulkan drivers are installed\\n\");\n\t\treturn false;\n\t}\n\tvkGetInstanceProcAddr = (PFN_vkGetInstanceProcAddr) GetProcAddress(hInstVulkan, \"vkGetInstanceProcAddr\");\n#endif\n\n\treturn VK_Init(info, extnames,countof(extnames), Win32VK_CreateSurface, Win32VK_Present);\n}\nstatic qboolean Win32VK_EnumerateDevices(void *usercontext, void(*callback)(void *context, const char *devicename, const char *outputname, const char *desc))\n{\n\tqboolean ret = false;\n#ifdef VK_NO_PROTOTYPES\n\tPFN_vkGetInstanceProcAddr vkGetInstanceProcAddr;\n\tdllfunction_t func[] =\n\t{\n\t\t{(void*)&vkGetInstanceProcAddr,\t\t\"vkGetInstanceProcAddr\"},\n\t\t{NULL,\t\t\t\t\t\t\tNULL}\n\t};\n\n\tif (!hInstVulkan)\n\t\thInstVulkan = Sys_LoadLibrary(\"vulkan-1.dll\", func);\n\tif (!hInstVulkan)\n\t\treturn false;\n#endif\n\tret = VK_EnumerateDevices(usercontext, callback, \"Vulkan-\", vkGetInstanceProcAddr);\n\treturn ret;\n}\n#endif\n\n\n#if defined(VKQUAKE) && defined(USE_WGL)\n#define GLuint64 quint64_t\n#define GLchar char\nstatic PFN_vkVoidFunction\t(WINAPI *qglGetVkProcAddrNV)\t\t(const GLchar *name);\nstatic void\t\t\t\t\t(WINAPI *qglDrawVkImageNV)\t\t\t(GLuint64 vkImage, GLuint sampler, GLfloat x0, GLfloat y0, GLfloat x1, GLfloat y1, GLfloat z, GLfloat s0, GLfloat t0, GLfloat s1, GLfloat t1);\nstatic void\t\t\t\t\t(WINAPI *qglWaitVkSemaphoreNV)\t\t(GLuint64 vkSemaphore);\nstatic void\t\t\t\t\t(WINAPI *qglSignalVkSemaphoreNV)\t(GLuint64 vkSemaphore);\nstatic void\t\t\t\t\t(WINAPI *qglSignalVkFenceNV)\t\t(GLuint64 vkFence);\nstatic PFN_vkVoidFunction VKAPI_CALL nvvkGetInstanceProcAddr(VkInstance instance, const char* pName)\n{\n\t//nvidia do not make this easy.\n\tPFN_vkVoidFunction fnc;\n\n//\tqwglMakeCurrent(maindc, baseRC);\n\tfnc = qglGetVkProcAddrNV(pName);\n//\tqwglMakeCurrent(maindc, NULL);\n\n\treturn fnc;\n}\nstatic qboolean Win32NVVK_CreateSurface(void)\n{\n\tvk.surface = VK_NULL_HANDLE;\n//\tvk.allowsubmissionthread = false;\t//must come on the main thread, because that's the one with the gl context.\n\t\t\t\t\t\t\t\t\t\t//I seem to be getting crashes on vulkan's fences if I try giving ownership of the gl context to a different thread (instead of main).\n\treturn true;\n}\nstatic void Win32NVVK_Present(struct vkframe *theframe)\n{\n\tSendMessage(mainwindow, WM_USER_NVVKPRESENT, 0, (LPARAM)theframe);\n}\nstatic void Win32NVVK_DoPresent(struct vkframe *theframe)\n{\n\tVkFence fence;\n\tRSpeedLocals();\n\tif (!theframe)\n\t\treturn;\t//this is used to ensure some presentation thread has woken up. we're not threading this, hopefully the gl server will do any of that that's needed.\n\n\tRSpeedRemark();\n\n\t//this might be a submission thread, so make sure we're talking to the right opengl context...\n//\tqwglMakeCurrent(maindc, baseRC);\n\n\t//get the gl driver to wait for the vk driver to finish rendering the frame\n\tqglWaitVkSemaphoreNV((GLuint64)theframe->backbuf->presentsemaphore);\n\n\t//tell the gl driver to copy it over now\n\tqglDrawVkImageNV((GLuint64)theframe->backbuf->colour.image, (GLuint64)theframe->backbuf->colour.sampler, \n\t\t\t0, 0, vid.pixelwidth, vid.pixelheight,\t//xywh (window coords)\n\t\t\t0,\t//z\n\t\t\t0, 1, 1, 0);\t//stst (remember that gl textures are meant to be upside down)\n\n\t//and tell our code to expect it.\n\tvk.acquirebufferidx[vk.acquirelast%ACQUIRELIMIT] = vk.acquirelast%vk.backbuf_count;\n\tfence = vk.acquirefences[vk.acquirelast%ACQUIRELIMIT];\n\tvk.acquirelast++;\n\t//and actually signal it, so our code can wake up.\n\tqglSignalVkFenceNV((GLuint64)fence);\n\n\n\t//and the gl driver has its final image and should do something with it now.\n\tqSwapBuffers(maindc);\n\n//\tqwglMakeCurrent(maindc, NULL);\n\n\tRSpeedEnd(RSPEED_PRESENT);\n}\nstatic qboolean WGL_CheckExtension(char *extname);\nstatic qboolean Win32NVVK_AttachVulkan (rendererstate_t *info)\n{\t//make sure we can get a valid renderer.\n\n\tif (!GL_CheckExtension(\"GL_NV_draw_vulkan_image\"))\n\t{\n\t\tCon_Printf(\"GL_NV_draw_vulkan_image is not supported. Try using real vulkan instead.\\n\");\n\t\treturn false;\n\t}\n\tqglGetVkProcAddrNV\t\t= getglfunc(\"glGetVkProcAddrNV\");\n\tqglDrawVkImageNV\t\t= getglfunc(\"glDrawVkImageNV\");\n\tqglWaitVkSemaphoreNV\t= getglfunc(\"glWaitVkSemaphoreNV\");\n\tqglSignalVkSemaphoreNV\t= getglfunc(\"glSignalVkSemaphoreNV\");\n\tqglSignalVkFenceNV\t\t= getglfunc(\"glSignalVkFenceNV\");\n\n\tvkGetInstanceProcAddr = (PFN_vkGetInstanceProcAddr)nvvkGetInstanceProcAddr(NULL, \"vkGetInstanceProcAddr\");\n\tif (!vkGetInstanceProcAddr)\n\t\tvkGetInstanceProcAddr = nvvkGetInstanceProcAddr;\n//\tqwglMakeCurrent(maindc, NULL);\n\treturn VK_Init(info, NULL,0, Win32NVVK_CreateSurface, Win32NVVK_Present);\n}\n#endif\n\n/*doesn't consider parent offsets*/\nstatic RECT centerrect(unsigned int parentleft, unsigned int parenttop, unsigned int parentwidth, unsigned int parentheight, unsigned int cwidth, unsigned int cheight)\n{\n\tRECT r;\n\tif (modestate!=MS_WINDOWED)\n\t{\n\t\tif (!vid_width.ival)\n\t\t\tcwidth = parentwidth;\n\t\tif (!vid_height.ival)\n\t\t\tcheight = parentwidth;\n\t}\n\n\tif (parentwidth < cwidth)\n\t{\n\t\tr.left = parentleft;\n\t\tr.right = r.left+parentwidth;\n\t}\n\telse\n\t{\n\t\tr.left = parentleft + (parentwidth - cwidth) / 2;\n\t\tr.right = r.left + cwidth;\n\t}\n\n\tif (parentheight < cheight)\n\t{\n\t\tr.top = parenttop;\n\t\tr.bottom = r.top + parentheight;\n\t}\n\telse\n\t{\n\t\tr.top = parenttop + (parentheight - cheight) / 2;\n\t\tr.bottom = r.top + cheight;\n\t}\n\n\treturn r;\n}\n\nstatic qboolean VID_SetWindowedMode (rendererstate_t *info)\n//qboolean VID_SetWindowedMode (int modenum)\n{\n\tint i;\n\tHDC\t\t\t\thdc;\n\tint\t\t\t\twwidth, wheight, pleft, ptop, pwidth, pheight;\n\n\tmodestate = MS_WINDOWED;\n\n\thdc = GetDC(NULL);\n\tif (GetDeviceCaps(hdc, RASTERCAPS) & RC_PALETTE)\n\t{\n\t\tReleaseDC(NULL, hdc);\n\t\tCon_Printf(\"Can't run GL in non-RGB mode\\n\");\n\t\treturn false;\n\t}\n\tvid.dpi_x = GetDeviceCaps(hdc, LOGPIXELSX);\n\tvid.dpi_y = GetDeviceCaps(hdc, LOGPIXELSY);\n\tReleaseDC(NULL, hdc);\n\n#ifndef FTE_SDL\n\tif (sys_parentwindow)\n\t{\n\t\tSetWindowLong(sys_parentwindow, GWL_STYLE, GetWindowLong(sys_parentwindow, GWL_STYLE)|WS_OVERLAPPED);\n\t\tWindowStyle = WS_CHILDWINDOW|WS_OVERLAPPED;\n\t\tExWindowStyle = 0;\n\n\t\tpleft = sys_parentleft;\n\t\tptop = sys_parenttop;\n\t\tpwidth = sys_parentwidth;\n\t\tpheight = sys_parentheight;\n\n\t\twwidth = sys_parentwidth;\n\t\twheight = sys_parentheight;\n\t}\n\telse\n#endif\n\t{\n\t\tWindowStyle = WS_OVERLAPPED | WS_BORDER | WS_CAPTION | WS_SYSMENU |\n\t\t\t\t\t  WS_MINIMIZEBOX;\n\t\tExWindowStyle = 0;\n\n\t\tWindowStyle |= WS_SIZEBOX | WS_MAXIMIZEBOX;\n\n\t\tpleft = 0;\n\t\tptop = 0;\n\t\tpwidth = GetSystemMetrics(SM_CXSCREEN);\n\t\tpheight = GetSystemMetrics(SM_CYSCREEN);\n\n\t\t/*Assume dual monitors, and chop the width to try to put it on only one screen\n\t\t  \"Because of app compatibility reasons these system metrics return the size of the primary monitor\"\n\t\t  so we shouldn't need this...\n\t\t*/\n//\t\tif (pwidth >= pheight*2)\n//\t\t\tpwidth /= 2;\n\n\t\twwidth = info->width;\n\t\twheight = info->height;\n\t\t/*win8 code:\n\t\tHMONITOR mod = MonitorFromRect(&rect, MONITOR_DEFAULTTONEAREST);\n\t\tif (mon != INVALID_HANDLE_VALUE)\n\t\t\tGetDpiForMonitor(mon, 0, &vid.dpi_x, &vid.dpi_y);\n\t\t*/\n//\t\twwidth = (wwidth*vid.dpi_x)/96;\n//\t\twheight = (wheight*vid.dpi_y)/96;\n\t}\n\n\tWindowRect = centerrect(pleft, ptop, pwidth, pheight, wwidth, wheight);\n\tif (!sys_parentwindow)\n\t\tAdjustWindowRectEx(&WindowRect, WindowStyle, FALSE, 0);\n\n\t// Create the DIB window\n\tif (WinNT)\n\t{\n\t\tdibwindow = CreateWindowExW (\n\t\t\t ExWindowStyle,\n\t\t\t WINDOW_CLASS_NAME_W,\n\t\t\t _L(FULLENGINENAME),\n\t\t\t WindowStyle,\n\t\t\t WindowRect.left, WindowRect.top,\n\t\t\t WindowRect.right - WindowRect.left,\n\t\t\t WindowRect.bottom - WindowRect.top,\n\t\t\t sys_parentwindow,\n\t\t\t NULL,\n\t\t\t global_hInstance,\n\t\t\t NULL);\n\t}\n\telse\n\t{\n\t\tdibwindow = CreateWindowExA (\n\t\t\t ExWindowStyle,\n\t\t\t WINDOW_CLASS_NAME_A,\n\t\t\t FULLENGINENAME,\n\t\t\t WindowStyle,\n\t\t\t WindowRect.left, WindowRect.top,\n\t\t\t WindowRect.right - WindowRect.left,\n\t\t\t WindowRect.bottom - WindowRect.top,\n\t\t\t sys_parentwindow,\n\t\t\t NULL,\n\t\t\t global_hInstance,\n\t\t\t NULL);\n\t}\n\n\tif (!dibwindow)\n\t{\n\t\tCon_Printf (\"Couldn't create DIB window\");\n\t\treturn false;\n\t}\n\n\tGetClientRect(dibwindow, &WindowRect);\n\tWindowRect.right -= WindowRect.left;\n\tWindowRect.bottom -= WindowRect.top;\n\tDIBWidth = WindowRect.right;\n\tDIBHeight = WindowRect.bottom;\n\n\tSendMessage (dibwindow, WM_SETICON, (WPARAM)TRUE, (LPARAM)hIcon);\n\tSendMessage (dibwindow, WM_SETICON, (WPARAM)FALSE, (LPARAM)hIcon);\n\n\tif (!sys_parentwindow)\n\t{\n#ifdef WS_EX_LAYERED\n\t\tint av;\n\t\tav = 255*vid_wndalpha.value;\n\t\tif (av < 70)\n\t\t\tav = 70;\n\t\tif (av < 255)\n\t\t{\n\t\t\tHMODULE hm = GetModuleHandleA(\"user32.dll\");\n\t\t\tlpfnSetLayeredWindowAttributes pSetLayeredWindowAttributes;\n\t\t\tpSetLayeredWindowAttributes = (void*)GetProcAddress(hm, \"SetLayeredWindowAttributes\");\n\n\t\t\tif (pSetLayeredWindowAttributes)\n\t\t\t{\n\t\t\t\t// Set WS_EX_LAYERED on this window\n\t\t\t\tSetWindowLong(dibwindow, GWL_EXSTYLE, GetWindowLong(dibwindow, GWL_EXSTYLE) | WS_EX_LAYERED);\n\n\t\t\t\t// Make this window 70% alpha\n\t\t\t\tpSetLayeredWindowAttributes(dibwindow, 0, (BYTE)av, LWA_ALPHA);\n\t\t\t}\n\t\t}\n#endif\n\t}\n\n\tShowWindow (dibwindow, SW_SHOWNORMAL);\n\tSetFocus(dibwindow);\n\n//\tShowWindow (dibwindow, SW_SHOWDEFAULT);\n//\tUpdateWindow (dibwindow);\n\n// because we have set the background brush for the window to NULL\n// (to avoid flickering when re-sizing the window on the desktop),\n// we clear the window to black when created, otherwise it will be\n// empty while Quake starts up.\n\thdc = GetDC(dibwindow);\n\tPatBlt(hdc,0,0,WindowRect.right,WindowRect.bottom,BLACKNESS);\n\tReleaseDC(dibwindow, hdc);\n\n\tif ((i = COM_CheckParm(\"-conwidth\")) != 0)\n\t\tvid.width = Q_atoi(com_argv[i+1]);\n\telse\n\t{\n\t\tvid.width = 640;\n\t}\n\n\tvid.width &= 0xfff8; // make it a multiple of eight\n\n\tif (vid.width < 320)\n\t\tvid.width = 320;\n\n\t// pick a conheight that matches with correct aspect\n\tvid.height = vid.width*3 / 4;\n\n\tif ((i = COM_CheckParm(\"-conheight\")) != 0)\n\t\tvid.height = Q_atoi(com_argv[i+1]);\n\tif (vid.height < 200)\n\t\tvid.height = 200;\n\n\tif (vid.height > info->height)\n\t\tvid.height = info->height;\n\tif (vid.width > info->width)\n\t\tvid.width = info->width;\n\n\tvid.numpages = 2;\n\n\tmainwindow = dibwindow;\n\tvid_isfullscreen=false;\n\n\tCL_UpdateWindowTitle();\n\n\treturn true;\n}\n\nvoid GLVID_SetCaption(const char *text)\n{\n\twchar_t wide[2048];\n\twiden(wide, sizeof(wide), text);\n\tSetWindowTextW(mainwindow, wide);\n}\n\nstatic qboolean VID_SetFullDIBMode (rendererstate_t *info)\n{\n\tint i;\n\tHDC\t\t\t\thdc;\n\tint\t\t\t\twwidth, wheight;\n\tRECT\t\t\trect;\n\n\tif (info->fullscreen != 2)\n\t{\t//make windows change res.\n\n\t\tmodestate = MS_FULLDIB;\n\n\t\tgdevmode.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT;\n\t\tif (info->bpp)\n\t\t\tgdevmode.dmFields |= DM_BITSPERPEL;\n\t\tif (info->rate)\n\t\t\tgdevmode.dmFields |= DM_DISPLAYFREQUENCY;\n\t\tif (info->bpp && (info->bpp < 15))\n\t\t{\t//low values get you a warning. otherwise only 16 and 32bit are allowed.\n\t\t\tCon_Printf(\"Forcing at least 15bpp\\n\");\n\t\t\tgdevmode.dmBitsPerPel = 16;\n\t\t}\n\t\telse if (info->bpp == 16)\n\t\t\tgdevmode.dmBitsPerPel = 16;\n\t\telse\n\t\t\tgdevmode.dmBitsPerPel = 32;\n\t\tgdevmode.dmDisplayFrequency = info->rate;\n\t\tgdevmode.dmPelsWidth = info->width;\n\t\tgdevmode.dmPelsHeight = info->height;\n\t\tgdevmode.dmSize = sizeof (gdevmode);\n\n\t\tif (ChangeDisplaySettings (&gdevmode, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL)\n\t\t{\n\t\t\tCon_SafePrintf((gdevmode.dmFields&DM_DISPLAYFREQUENCY)?\"Windows rejected mode %i*%i, %ibpp @%ihz\\n\":\"Windows rejected mode %i*%i, %ibpp\\n\", (int)gdevmode.dmPelsWidth, (int)gdevmode.dmPelsHeight, (int)gdevmode.dmBitsPerPel, (int)gdevmode.dmDisplayFrequency);\n\t\t\treturn false;\n\t\t}\n\t}\n\telse\n\t{\n\t\tmodestate = MS_FULLWINDOW;\n\t\t\n\t}\n\n\tWindowRect.top = WindowRect.left = 0;\n\n\tWindowRect.right = info->width;\n\tWindowRect.bottom = info->height;\n\n\tDIBWidth = info->width;\n\tDIBHeight = info->height;\n\n\tWindowStyle = WS_POPUP;\n\tExWindowStyle = 0;\n\n\trect = WindowRect;\n\tAdjustWindowRectEx(&rect, WindowStyle, FALSE, 0);\n\n\twwidth = rect.right - rect.left;\n\twheight = rect.bottom - rect.top;\n\n\t// Create the DIB window\n\tif(WinNT)\n\t{\n\t\tdibwindow = CreateWindowExW (\n\t\t\tExWindowStyle,\n\t\t\tWINDOW_CLASS_NAME_W,\n\t\t\t_L(FULLENGINENAME),\n\t\t\tWindowStyle,\n\t\t\trect.left, rect.top,\n\t\t\twwidth,\n\t\t\twheight,\n\t\t\tNULL,\n\t\t\tNULL,\n\t\t\tglobal_hInstance,\n\t\t\tNULL);\n\t}\n\telse\n\t{\n\t\tdibwindow = CreateWindowExA (\n\t\t\tExWindowStyle,\n\t\t\tWINDOW_CLASS_NAME_A,\n\t\t\tFULLENGINENAME,\n\t\t\tWindowStyle,\n\t\t\trect.left, rect.top,\n\t\t\twwidth,\n\t\t\twheight,\n\t\t\tNULL,\n\t\t\tNULL,\n\t\t\tglobal_hInstance,\n\t\t\tNULL);\n\t}\n\n\tif (!dibwindow)\n\t\tSys_Error (\"Couldn't create DIB window\");\n\n\t{\n\t\tBOOL fDisable = TRUE;\n\t\tDWORD qDWMWA_TRANSITIONS_FORCEDISABLED = 3;\n\t\tHRESULT (WINAPI *pDwmSetWindowAttribute)(HWND hWnd,DWORD dwAttribute,LPCVOID pvAttribute,DWORD cbAttribute);\n\t\tdllfunction_t dwm[] =\n\t\t{\n\t\t\t{(void*)&pDwmSetWindowAttribute, \"DwmSetWindowAttribute\"},\n\t\t\t{NULL,NULL}\n\t\t};\n\t\tif (Sys_LoadLibrary(\"dwmapi.dll\", dwm))\n\t\t{\n\t\t\tpDwmSetWindowAttribute(dibwindow, qDWMWA_TRANSITIONS_FORCEDISABLED, &fDisable, sizeof(fDisable));\n\t\t}\n\t}\n\n\tSendMessage (dibwindow, WM_SETICON, (WPARAM)TRUE, (LPARAM)hIcon);\n\tSendMessage (dibwindow, WM_SETICON, (WPARAM)FALSE, (LPARAM)hIcon);\n\n\tif (modestate == MS_FULLWINDOW)\n\t\tShowWindow (dibwindow, SW_HIDE);//SW_SHOWMAXIMIZED);\n\telse\n\t\tShowWindow (dibwindow, SW_HIDE);//SW_SHOWDEFAULT);\n\tUpdateWindow (dibwindow);\n\n\t// Because we have set the background brush for the window to NULL\n\t// (to avoid flickering when re-sizing the window on the desktop), we\n\t// clear the window to black when created, otherwise it will be\n\t// empty while Quake starts up.\n\thdc = GetDC(dibwindow);\n\tPatBlt(hdc,0,0,WindowRect.right,WindowRect.bottom,BLACKNESS);\n\tReleaseDC(dibwindow, hdc);\n\n\n\tif ((i = COM_CheckParm(\"-conwidth\")) != 0)\n\t\tvid.width = Q_atoi(com_argv[i+1]);\n\telse\n\t\tvid.width = 640;\n\n\tvid.width &= 0xfff8; // make it a multiple of eight\n\n\tif (vid.width < 320)\n\t\tvid.width = 320;\n\n\t// pick a conheight that matches with correct aspect\n\tvid.height = vid.width*3 / 4;\n\n\tif ((i = COM_CheckParm(\"-conheight\")) != 0)\n\t\tvid.height = Q_atoi(com_argv[i+1]);\n\tif (vid.height < 200)\n\t\tvid.height = 200;\n\n\tif (vid.height > info->height)\n\t\tvid.height = info->height;\n\tif (vid.width > info->width)\n\t\tvid.width = info->width;\n\n\tvid.numpages = 2;\n\n// needed because we're not getting WM_MOVE messages fullscreen on NT\n\twindow_x = 0;\n\twindow_y = 0;\n\tvid_isfullscreen=true;\n\n\tmainwindow = dibwindow;\n\n\treturn true;\n}\n\nextern qboolean gammaworks;\nstatic void Win_Touch_Init(HWND wnd);\n\n#ifdef WTHREAD\nstatic rendererstate_t *rs;\nstatic qboolean CreateMainWindow(rendererstate_t *info, qboolean withthread);\nstatic int GLVID_WindowThread(void *cond)\n{\n\textern qboolean mouseshowtoggle;\n\tint cursor = 1;\n\tMSG\t\tmsg;\n\tHWND wnd;\n\tCreateMainWindow(rs, false);\n\twnd = mainwindow;\n\tSys_ConditionSignal(cond);\n\n\twhile (GetMessageW(&msg, NULL, 0, 0))\n\t{\n//\t\tTranslateMessageW (&msg);\n\t\tDispatchMessageW (&msg);\n\n\t\t//ShowCursor is thread-local.\n\t\tif (cursor != mouseshowtoggle)\n\t\t{\n\t\t\tcursor = mouseshowtoggle;\n\t\t\tShowCursor(cursor);\n\t\t}\n\t}\n\tDestroyWindow(wnd);\n\treturn 0;\n}\n#endif\nstatic qboolean CreateMainWindow(rendererstate_t *info, qboolean withthread)\n{\n\tqboolean\t\tstat;\n\n#ifdef WTHREAD\n\tif (withthread && vid_winthread.ival)\n\t{\n\t\tvoid *cond = Sys_CreateConditional();\n\t\tSys_LockConditional(cond);\n\t\trs = info;\n\t\twindowthread = Sys_CreateThread(\"windowthread\", GLVID_WindowThread, cond, 0, 0);\n\t\tif (!Sys_ConditionWait(cond))\n\t\t\tCon_SafePrintf (\"Looks like the window thread isn't starting up\\n\");\n\t\tSys_UnlockConditional(cond);\n\t\tSys_DestroyConditional(cond);\n\n\t\treturn !!mainwindow;\n\t}\n#endif\n\n\tif (WinNT)\n\t{\n\t\tWNDCLASSW\t\twc;\n\t\t/* Register the frame class */\n\t\twc.style         = CS_OWNDC;\n\t\twc.lpfnWndProc   = (WNDPROC)GLMainWndProc;\n\t\twc.cbClsExtra    = 0;\n\t\twc.cbWndExtra    = 0;\n\t\twc.hInstance     = global_hInstance;\n\t\twc.hIcon         = hIcon;\n\t\twc.hCursor       = hArrowCursor;\n\t\twc.hbrBackground = NULL;\n\t\twc.lpszMenuName  = 0;\n\t\twc.lpszClassName = WINDOW_CLASS_NAME_W;\n\t\tif (!RegisterClassW (&wc))\t//this isn't really fatal, we'll let the CreateWindow fail instead.\n\t\t\tCon_DPrintf(\"RegisterClass failed\\n\");\n\t}\n\telse\n\t{\n\t\tWNDCLASSA\t\twc;\n\t\t/* Register the frame class */\n\t\twc.style         = CS_OWNDC;\n\t\twc.lpfnWndProc   = (WNDPROC)GLMainWndProc;\n\t\twc.cbClsExtra    = 0;\n\t\twc.cbWndExtra    = 0;\n\t\twc.hInstance     = global_hInstance;\n\t\twc.hIcon         = hIcon;\n\t\twc.hCursor       = hArrowCursor;\n\t\twc.hbrBackground = NULL;\n\t\twc.lpszMenuName  = 0;\n\t\twc.lpszClassName = WINDOW_CLASS_NAME_A;\n\t\tif (!RegisterClassA (&wc))\t//this isn't really fatal, we'll let the CreateWindow fail instead.\n\t\t\tCon_DPrintf(\"RegisterClass failed\\n\");\n\t}\n\n\tif (!info->fullscreen)\n\t{\n\t\tTRACE((\"dbg: GLVID_SetMode: VID_SetWindowedMode\\n\"));\n\t\tstat = VID_SetWindowedMode(info);\n\t}\n\telse\n\t{\n\t\tTRACE((\"dbg: GLVID_SetMode: VID_SetFullDIBMode\\n\"));\n\t\tstat = VID_SetFullDIBMode(info);\n\t}\n\tVID_UpdateWindowStatus(mainwindow);\n\n\tWin_Touch_Init(mainwindow);\n\n\tINS_UpdateGrabs(info->fullscreen, vid.activeapp);\n\n\treturn stat;\n}\n\nstatic void VID_UnSetMode (void);\nstatic int GLVID_SetMode (rendererstate_t *info, unsigned char *palette)\n{\n\tint\t\t\t\ttemp;\n\tqboolean\t\tstat;\n\tMSG\t\t\t\tmsg;\n//\tHDC\t\t\t\thdc;\n\n\tvrsetup_t setup = {sizeof(setup)};\n\n\tTRACE((\"dbg: GLVID_SetMode\\n\"));\n\n// so Con_Printfs don't mess us up by forcing vid and snd updates\n\ttemp = scr_disabled_for_loading;\n\tscr_disabled_for_loading = true;\n\n\tCDAudio_Pause ();\n\n\tqGetDeviceGammaRamp = (void*)GetDeviceGammaRamp;\n\tqSetDeviceGammaRamp = (void*)SetDeviceGammaRamp;\n\n\tswitch(platform_rendermode)\n\t{\n#ifdef USE_WGL\n\tcase MODE_WGL:\n#ifdef VKQUAKE\n\tcase MODE_NVVULKAN:\n#endif\n\t\tsetup.vrplatform = VR_WIN_WGL;\n\t\tif (info->vr && !info->vr->Prepare(&setup))\n\t\t{\n\t\t\tinfo->vr->Shutdown();\n\t\t\tinfo->vr = NULL;\n\t\t}\n\n\t\t// Set either the fullscreen or windowed mode\n\t\tqwglChoosePixelFormatARB = NULL;\n\t\tqwglGetPixelFormatAttribfvARB = NULL;\n\t\tqwglCreateContextAttribsARB = NULL;\n\t\tstat = CreateMainWindow(info, true);\n\n\t\tif (stat)\n\t\t{\n\t\t\tstat = VID_AttachGL(info);\n\t\t\tif (stat)\n\t\t\t{\n\t\t\t\tTRACE((\"dbg: GLVID_SetMode: attaching gl okay\\n\"));\n\t\t\t\tif (CheckForcePixelFormat(info))\n\t\t\t\t{\n\t\t\t\t\tHMODULE oldgl = hInstGL;\n\t\t\t\t\thInstGL = NULL;\t//don't close the gl library, just in case\n\t\t\t\t\tVID_UnSetMode();\t//SetPixelFormat may only be used once per window. which means we *MUST* destroy the window if we're using a different pixelformat.\n\t\t\t\t\thInstGL = oldgl;\n\n\t\t\t\t\tif (CreateMainWindow(info, true) && VID_AttachGL(info))\n\t\t\t\t\t{\n\t\t\t\t\t\t//we have our multisample window\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\t//multisample failed\n\t\t\t\t\t\t//try the origional way\n\t\t\t\t\t\tif (!CreateMainWindow(info, true) || !VID_AttachGL(info))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tCon_Printf(\"Failed to undo antialising. Giving up.\\n\");\n\t\t\t\t\t\t\treturn false;\t//eek\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tTRACE((\"dbg: GLVID_SetMode: attaching gl failed\\n\"));\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tTRACE((\"dbg: GLVID_SetMode: unable to create window\\n\"));\n\t\t\treturn false;\n\t\t}\n\n\t\tif (modestate == MS_FULLWINDOW)\n\t\t\tShowWindow (dibwindow, SW_SHOWMAXIMIZED);\n\t\telse\n\t\t\tShowWindow (dibwindow, SW_SHOWNORMAL);\t\n\n\t\tif (!GL_Init(info, getglfunc))\n\t\t\treturn false;\n\t\tsetup.wgl.hdc = maindc;\n\t\tsetup.wgl.hglrc = baseRC;\n\t\tif (info->vr && !info->vr->Init(&setup, info))\n\t\t{\n\t\t\tinfo->vr->Shutdown();\n\t\t\treturn false;\n\t\t}\n\t\tvid.vr = info->vr;\n\n\t\tif (qwglGetPixelFormatAttribfvARB)\t//just for debugging info.\n\t\t{\n\t\t\tint iAttributeNames[] = {WGL_RED_BITS_ARB, WGL_GREEN_BITS_ARB, WGL_BLUE_BITS_ARB, WGL_ALPHA_BITS_ARB, WGL_PIXEL_TYPE_ARB, WGL_DEPTH_BITS_ARB, WGL_STENCIL_BITS_ARB};\n\t\t\tfloat fAttributeValues[countof(iAttributeNames)] = {0};\n\t\t\tif (qwglGetPixelFormatAttribfvARB(maindc, currentpixelformat, 0, countof(iAttributeNames), iAttributeNames, fAttributeValues))\n\t\t\t{\n\t\t\t\tCon_DPrintf(\"Colour buffer: GL_R%gG%gB%gA%g%s\\n\", fAttributeValues[0], fAttributeValues[1], fAttributeValues[2], fAttributeValues[3], fAttributeValues[5]==WGL_TYPE_RGBA_FLOAT_ARB?\"F\":((vid.flags & VID_SRGBAWARE)?\"_SRGB\":\"\"));\n\t\t\t\tCon_DPrintf(\"Depth buffer: GL_DEPTH%g_STENCIL%g\\n\", fAttributeValues[5], fAttributeValues[6]);\n\t\t\t}\n\t\t}\n\n\n\t\tqSwapBuffers(maindc);\n\n#ifdef VKQUAKE\n\t\tif (platform_rendermode == MODE_NVVULKAN && stat)\n\t\t{\n\t\t\tstat = Win32NVVK_AttachVulkan(info);\n\t\t}\n#endif\n\t\tbreak;\n#endif\n#ifdef USE_EGL\n\tcase MODE_EGL:\n\t\tstat = CreateMainWindow(info, true);\n\t\tif (stat)\n\t\t{\n\t\t\tEGLConfig cfg;\n\t\t\tmaindc = GetDC(mainwindow);\n\n\t\t\tif (!EGL_InitDisplay(info, EGL_PLATFORM_WIN32, maindc, (EGLNativeDisplayType)maindc, &cfg))\n\t\t\t{\n\t\t\t\tCon_Printf(\"couldn't find suitable EGL config\\n\");\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tif (!EGL_InitWindow(info, EGL_PLATFORM_WIN32, mainwindow, (EGLNativeWindowType)mainwindow, cfg))\n\t\t\t{\n\t\t\t\tCon_Printf(\"couldn't initialise EGL context\\n\");\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tif (!GL_Init(info, &EGL_Proc))\n\t\t\t\treturn false;\n\t\t}\n\t\tbreak;\n#endif\n#ifdef VKQUAKE\n\tcase MODE_VULKAN:\n\t\tstat = CreateMainWindow(info, true);\n\n\t\tif (stat)\n\t\t{\n\t\t\tmaindc = GetDC(mainwindow);\n\t\t\tstat = Win32VK_AttachVulkan(info);\n\t\t}\n\t\tbreak;\n#endif\n\tdefault:\n\t\tstat = false;\n\t\tbreak;\n\t}\n\n\tif (!stat)\n\t{\n\t\tTRACE((\"dbg: GLVID_SetMode: VID_Set... failed\\n\"));\n\t\treturn false;\n\t}\n\n\tif (!qGetDeviceGammaRamp) qGetDeviceGammaRamp = (void*)GetDeviceGammaRamp;\n\tif (!qSetDeviceGammaRamp) qSetDeviceGammaRamp = (void*)SetDeviceGammaRamp;\n\n\n\twindow_width = DIBWidth;\n\twindow_height = DIBHeight;\n\tVID_UpdateWindowStatus (mainwindow);\n\tCvar_ForceCallback(&vid_conautoscale);\n\n\tCDAudio_Resume ();\n\tscr_disabled_for_loading = temp;\n\n// now we try to make sure we get the focus on the mode switch, because\n// sometimes in some systems we don't.  We grab the foreground, then\n// finish setting up, pump all our messages, and sleep for a little while\n// to let messages finish bouncing around the system, then we put\n// ourselves at the top of the z order, then grab the foreground again,\n// Who knows if it helps, but it probably doesn't hurt\n\tSetForegroundWindow (mainwindow);\n\n\t/*I don't like this, but if we */\n\tSleep (100);\n\twhile (PeekMessage (&msg, mainwindow, 0, 0, PM_REMOVE))\n\t{\n\t\tTranslateMessage (&msg);\n\t\tDispatchMessage (&msg);\n\t}\n\tSleep (100);\n\n\tSetWindowPos (mainwindow, HWND_TOP, 0, 0, 0, 0,\n\t\t\t\t  SWP_DRAWFRAME | SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW |\n\t\t\t\t  SWP_NOCOPYBITS);\n\n\tSetForegroundWindow (mainwindow);\n\n\tSleep (100);\n\twhile (PeekMessage (&msg, mainwindow, 0, 0, PM_REMOVE))\n\t{\n\t\tTranslateMessage (&msg);\n\t\tDispatchMessage (&msg);\n\t}\n\tSleep (100);\n\n// fix the leftover Alt from any Alt-Tab or the like that switched us away\n\tClearAllStates ();\n\n\tgammaworks = FALSE;\n\tif (vid_desktopgamma.value)\n\t{\n\t\tHDC hDC = GetDC(GetDesktopWindow());\n\t\tif (qGetDeviceGammaRamp(hDC, originalgammaramps))\n\t\t\tgammaworks = qSetDeviceGammaRamp(hDC, originalgammaramps);\n\t\tReleaseDC(GetDesktopWindow(), hDC);\n\t}\n\telse\n\t{\n\t\tif (qGetDeviceGammaRamp(maindc, originalgammaramps))\n\t\t\tgammaworks = qSetDeviceGammaRamp(maindc, originalgammaramps);\n\t}\n\tif (!gammaworks)\n\t\tCon_Printf(\"Hardware gamma is not supported\\n\");\n\telse\n\t\tCon_DPrintf(\"Hardware gamma appears to work\\n\");\n\n\treturn true;\n}\n\nvoid VID_UnSetMode (void)\n{\n\tif (mainwindow && vid_initialized)\n\t{\n\t\tGLAppActivate(false, false);\n\n\t\tvid_canalttab = false;\n\n\t\tswitch(platform_rendermode)\n\t\t{\n#if defined(VKQUAKE) && defined(USE_WGL)\n\t\tcase MODE_NVVULKAN:\n\t\t\tqwglMakeCurrent(maindc, baseRC);\n\t\t\tVK_Shutdown();\n\t\t\tReleaseGL();\n\t\t\tbreak;\n#endif\n#ifdef USE_WGL\n\t\tcase MODE_WGL:\n\t\t\tReleaseGL();\n\t\t\tbreak;\n#endif\n#ifdef USE_EGL\n\t\tcase MODE_EGL:\n\t\t\tEGL_Shutdown();\n\t\t\tbreak;\n#endif\n#ifdef VKQUAKE\n\t\tcase MODE_VULKAN:\n\t\t\tVK_Shutdown();\n\t\t\tbreak;\n#endif\n\t\t}\n\n\t\tif (modestate == MS_FULLDIB)\n\t\t\tChangeDisplaySettings (NULL, 0);\n\n\t\tif (maindc && dibwindow)\n\t\t\tReleaseDC (dibwindow, maindc);\n\t\tmaindc = NULL;\n\t}\n\n\tif (mainwindow)\n\t{\n\t\tdibwindow=NULL;\n\t//\tShowWindow(mainwindow, SW_HIDE);\n\t//\tSetWindowLongPtr(mainwindow, GWL_WNDPROC, DefWindowProc);\n\t//\tPostMessage(mainwindow, WM_CLOSE, 0, 0);\n#ifdef WTHREAD\n\t\tif (windowthread)\n\t\t{\n\t\t\tSendMessage(mainwindow, WM_USER+4, 0, 0);\n\t\t\tSys_WaitOnThread(windowthread);\n\t\t\twindowthread = NULL;\n\t\t}\n\t\telse\n#endif\n\t\t\tDestroyWindow(mainwindow);\n\t\tmainwindow = NULL;\n\t}\n\n\tif (WinNT)\n\t\tUnregisterClassW(WINDOW_CLASS_NAME_W, global_hInstance);\n\telse\n\t\tUnregisterClassA(WINDOW_CLASS_NAME_A, global_hInstance);\n\n#if 0\n\t//Logically this code should be active. However...\n\t//1: vid_restarts are slightly slower if we don't reuse the old dll\n\t//2: nvidia drivers crash if we shut it down+reload!\n\tif (hInstGL)\n\t{\n\t\tFreeLibrary(hInstGL);\n\t\thInstGL=NULL;\n\t}\n\tif (hInstwgl)\n\t{\n\t\tFreeLibrary(hInstwgl);\n\t\thInstwgl=NULL;\n\t}\n\t*opengldllname = 0;\n#endif\n\n#ifdef GLQUAKE\n\tGL_ForgetPointers();\n#endif\n}\n\n\n/*\n================\nVID_UpdateWindowStatus\n================\n*/\nstatic void VID_UpdateWindowStatus (HWND hWnd)\n{\n\tPOINT p;\n\tRECT nr;\n\tGetClientRect(hWnd, &nr);\n\n\t//if its bad then we're probably minimised\n\tif (nr.right <= nr.left)\n\t\treturn;\n\tif (nr.bottom <= nr.top)\n\t\treturn;\n\n\tWindowRect = nr;\n\tp.x = 0;\n\tp.y = 0;\n\tClientToScreen(hWnd, &p);\n\twindow_x = p.x;\n\twindow_y = p.y;\n\twindow_width = WindowRect.right - WindowRect.left;\n\twindow_height = WindowRect.bottom - WindowRect.top;\n\n\twindow_rect.left = window_x;\n\twindow_rect.top = window_y;\n\twindow_rect.right = window_x + window_width;\n\twindow_rect.bottom = window_y + window_height;\n\twindow_center_x = (window_rect.left + window_rect.right) / 2;\n\twindow_center_y = (window_rect.top + window_rect.bottom) / 2;\n\n\tINS_UpdateClipCursor ();\n\n\tswitch(platform_rendermode)\n\t{\n#ifdef VKQUAKE\n#ifdef USE_WGL\n\tcase MODE_NVVULKAN:\n#endif\n\tcase MODE_VULKAN:\n\t\tif (vid.pixelwidth != window_width || vid.pixelheight != window_height)\n\t\t\tvk.neednewswapchain = true;\n\t\tbreak;\n#endif\n\tdefault:\n\t\tvid.pixelwidth = window_width;\n\t\tvid.pixelheight = window_height;\n\t\tbreak;\n\t}\n}\n\n#ifdef USE_WGL\nstatic qboolean WGL_CheckExtension(char *extname)\n{\n//\tint i;\n\tint len;\n\tconst char *foo;\n\tcvar_t *v = Cvar_Get(va(\"gl_ext_%s\", extname), \"1\", 0, \"GL Extensions\");\n\tif (v && !v->ival)\n\t{\n\t\tCon_Printf(\"Cvar %s is 0\\n\", v->name);\n\t\treturn false;\n\t}\n\n/*\tif (gl_num_extensions && qglGetStringi)\n\t{\n\t\tfor (i = 0; i < gl_num_extensions; i++)\n\t\t\tif (!strcmp(qglGetStringi(GL_EXTENSIONS, i), extname))\n\t\t\t{\n\t\t\t\tCon_DPrintf(\"Detected GL extension %s\\n\", extname);\n\t\t\t\treturn true;\n\t\t\t}\n\t}\n*/\n\tif (!wgl_extensions)\n\t\treturn false;\n\n\t//the list is space delimited. we cannot just strstr lest we find leading/trailing _FOO_.\n\tlen = strlen(extname);\n\tfor (foo = wgl_extensions; *foo; )\n\t{\n\t\tif (!strncmp(foo, extname, len) && (foo[len] == ' ' || !foo[len]))\n\t\t\treturn true;\n\t\twhile(*foo && *foo != ' ')\n\t\t\tfoo++;\n\t\tif (*foo == ' ')\n\t\t\tfoo++;\n\t}\n\treturn false;\n}\n\n//====================================\n\nqboolean VID_AttachGL (rendererstate_t *info)\n{\t//make sure we can get a valid renderer.\n\tint iAttributeNames[2];\n\tFLOAT fAttributeValues[countof(iAttributeNames)];\n\n\tdo\n\t{\n\t\tTRACE((\"dbg: VID_AttachGL: GLInitialise\\n\"));\n\t\tif (GLInitialise(info->subrenderer))\n\t\t{\n\t\t\tmaindc = GetDC(mainwindow);\n\t\t\tTRACE((\"dbg: VID_AttachGL: bSetupPixelFormat\\n\"));\n\t\t\tif (bSetupPixelFormat(maindc, info))\n\t\t\t\tbreak;\n\t\t\tReleaseDC(mainwindow, maindc);\n\t\t}\n\n\t\tif (!*info->subrenderer || !stricmp(info->subrenderer, \"opengl32.dll\") || !stricmp(info->subrenderer, \"opengl32\"))\t//go for windows system dir if we failed with the default. Should help to avoid the 3dfx problem.\n\t\t{\n\t\t\twchar_t systemglw[MAX_OSPATH+1];\n\t\t\tchar systemgl[MAX_OSPATH+1];\n\t\t\tGetSystemDirectoryW(systemglw, countof(systemglw)-1);\n\t\t\tnarrowen(systemgl, sizeof(systemgl), systemglw);\n\t\t\tQ_strncatz(systemgl, \"\\\\\", sizeof(systemgl));\n\t\t\tif (*info->subrenderer)\n\t\t\t\tQ_strncatz(systemgl, info->subrenderer, sizeof(systemgl));\n\t\t\telse\n\t\t\t\tQ_strncatz(systemgl, \"opengl32.dll\", sizeof(systemgl));\n\t\t\tTRACE((\"dbg: VID_AttachGL: GLInitialise (system dir specific)\\n\"));\n\t\t\tif (GLInitialise(systemgl))\n\t\t\t{\n\t\t\t\tmaindc = GetDC(mainwindow);\n\t\t\t\tTRACE((\"dbg: VID_AttachGL: bSetupPixelFormat\\n\"));\n\t\t\t\tif (bSetupPixelFormat(maindc, info))\n\t\t\t\t\tbreak;\n\t\t\t\tReleaseDC(mainwindow, maindc);\n\t\t\t}\n\t\t}\n\n\t\tTRACE((\"dbg: VID_AttachGL: failed to find a valid dll\\n\"));\n\t\treturn false;\n\t} while(1);\n\n\tTRACE((\"dbg: VID_AttachGL: qwglCreateContext\\n\"));\n\n\tbaseRC = qwglCreateContext(maindc);\n\tif (!baseRC)\n\t{\n\t\tCon_SafePrintf(CON_ERROR \"Could not initialize GL (wglCreateContext failed).\\n\\nMake sure you in are 65535 color mode, and try running -window.\\n\");\t//green to make it show.\n\t\treturn false;\n\t}\n\tTRACE((\"dbg: VID_AttachGL: qwglMakeCurrent\\n\"));\n\tif (!qwglMakeCurrent(maindc, baseRC))\n\t{\n\t\tCon_SafePrintf(CON_ERROR \"wglMakeCurrent failed\\n\");\t//green to make it show.\n\t\treturn false;\n\t}\n\n\t{\n\t\tchar *(WINAPI *wglGetExtensionsString)(HDC hdc) = NULL;\n\t\tif (!wglGetExtensionsString)\n\t\t\twglGetExtensionsString = getglfunc(\"wglGetExtensionsString\");\n\t\tif (!wglGetExtensionsString)\n\t\t\twglGetExtensionsString = getglfunc(\"wglGetExtensionsStringARB\");\n\t\tif (!wglGetExtensionsString)\n\t\t\twglGetExtensionsString = getglfunc(\"wglGetExtensionsStringEXT\");\n\t\tif (wglGetExtensionsString)\n\t\t\twgl_extensions = wglGetExtensionsString(maindc);\n\t\telse\n\t\t\twgl_extensions = NULL;\n\t}\n\n\tif (developer.ival)\n\t\tCon_SafePrintf(\"WGL_EXTENSIONS: %s\\n\", wgl_extensions?wgl_extensions:\"NONE\");\n\n\tqwglCreateContextAttribsARB = getglfunc(\"wglCreateContextAttribsARB\");\n\n\t//attempt to promote that to opengl3.\n\tif (qwglCreateContextAttribsARB)\n\t{\n\t\tHGLRC opengl3;\n\t\tint attribs[11];\n\t\tchar *mv;\n\t\tint i = 0;\n\t\tchar *ver;\n\n\t\tver = vid_gl_context_version.string;\n\t\tif (!*ver && vid_gl_context_es.ival && WGL_CheckExtension(\"WGL_EXT_create_context_es2_profile\"))\n\t\t\tver = \"2.0\";\n\n\t\tmv = ver;\n\t\twhile (*mv)\n\t\t{\n\t\t\tif (*mv++ == '.')\n\t\t\t\tbreak;\n\t\t}\n\n\t\tif (*ver)\n\t\t{\n\t\t\tattribs[i++] = WGL_CONTEXT_MAJOR_VERSION_ARB;\n\t\t\tattribs[i++] = atoi(ver);\n\t\t}\n\t\tif (*mv)\n\t\t{\n\t\t\tattribs[i++] = WGL_CONTEXT_MINOR_VERSION_ARB;\n\t\t\tattribs[i++] = atoi(mv);\n\t\t}\n\n\t\t//flags\n\t\tattribs[i+1] = 0;\n\t\tif (vid_gl_context_debug.ival)\n\t\t\tattribs[i+1] |= WGL_CONTEXT_DEBUG_BIT_ARB;\n\t\tif (vid_gl_context_forwardcompatible.ival)\n\t\t\tattribs[i+1] |= WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB;\n\t\tif (vid_gl_context_robustness.ival && WGL_CheckExtension(\"WGL_ARB_create_context_robustness\"))\n\t\t\tattribs[i+1] |= WGL_CONTEXT_ROBUST_ACCESS_BIT_ARB;\n\n\t\tif (attribs[i+1])\n\t\t{\n\t\t\tattribs[i] = WGL_CONTEXT_FLAGS_ARB;\n\t\t\ti += 2;\n\t\t}\n\n\t\tif (vid_gl_context_selfreset.ival &&  WGL_CheckExtension(\"WGL_ARB_create_context_robustness\"))\n\t\t{\n\t\t\tattribs[i++] = WGL_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB;\n\t\t\tattribs[i++] = WGL_LOSE_CONTEXT_ON_RESET_ARB;\n\t\t}\n\n\t\tif (vid_gl_context_noerror.ival &&  WGL_CheckExtension(\"WGL_ARB_create_context_no_error\"))\n\t\t{\n\t\t\tattribs[i++] = WGL_CONTEXT_OPENGL_NO_ERROR_ARB;\n\t\t\tattribs[i++] = !vid_gl_context_robustness.ival && !vid_gl_context_debug.ival;\n\t\t}\n\n\t\t/*only switch contexts if there's actually a point*/\n\t\tif (i || !vid_gl_context_compatibility.ival || vid_gl_context_es.ival)\n\t\t{\n\t\t\tif (WGL_CheckExtension(\"WGL_ARB_create_context_profile\"))\n\t\t\t{\n\t\t\t\tattribs[i+1] = 0;\n\t\t\t\tif (vid_gl_context_es.ival && (WGL_CheckExtension(\"WGL_EXT_create_context_es_profile\") || WGL_CheckExtension(\"WGL_EXT_create_context_es2_profile\")))\n\t\t\t\t\tattribs[i+1] |= WGL_CONTEXT_ES2_PROFILE_BIT_EXT;\n\t\t\t\telse if (vid_gl_context_compatibility.ival)\n\t\t\t\t\tattribs[i+1] |= WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB;\n\t\t\t\telse\n\t\t\t\t\tattribs[i+1] |= WGL_CONTEXT_CORE_PROFILE_BIT_ARB;\n\t\t\t\tattribs[i] = WGL_CONTEXT_PROFILE_MASK_ARB;\n\t\t\t\t//WGL_CONTEXT_PROFILE_MASK_ARB is ignored if < 3.2 - however, nvidia do not agree and return errors\n\t\t\t\tif (atof(ver) >= 3.2 || vid_gl_context_es.ival)\n\t\t\t\t\ti+=2;\n\t\t\t}\n\n\t\t\tattribs[i] = 0;\n\n\t\t\tif ((opengl3 = qwglCreateContextAttribsARB(maindc, NULL, attribs)))\n\t\t\t{\n\t\t\t\tqwglMakeCurrent(maindc, NULL);\n\t\t\t\tqwglDeleteContext(baseRC);\n\n\t\t\t\tbaseRC = opengl3;\n\t\t\t\tif (!qwglMakeCurrent( maindc, baseRC ))\n\t\t\t\t{\n\t\t\t\t\tCon_SafePrintf(CON_ERROR \"wglMakeCurrent failed\\n\");\t//green to make it show.\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tDWORD error = GetLastError();\n\t\t\t\tif (error == (0xc0070000 | ERROR_INVALID_VERSION_ARB))\n\t\t\t\t\tCon_Printf(\"Unsupported OpenGL context version (%s).\\n\", vid_gl_context_version.string);\n\t\t\t\telse if (error == (0xc0070000 | ERROR_INVALID_PROFILE_ARB))\n\t\t\t\t\tCon_Printf(\"Unsupported OpenGL profile (%s).\\n\", vid_gl_context_es.ival?\"gles\":(vid_gl_context_compatibility.ival?\"compat\":\"core\"));\n\t\t\t\telse if (error == (0xc0070000 | ERROR_INVALID_OPERATION))\n\t\t\t\t\tCon_Printf(\"wglCreateContextAttribsARB returned invalid operation.\\n\");\n\t\t\t\telse if (error == (0xc0070000 | ERROR_DC_NOT_FOUND))\n\t\t\t\t\tCon_Printf(\"wglCreateContextAttribsARB returned dc not found.\\n\");\n\t\t\t\telse if (error == (0xc0070000 | ERROR_INVALID_PIXEL_FORMAT))\n\t\t\t\t\tCon_Printf(\"wglCreateContextAttribsARB returned dc not found.\\n\");\n\t\t\t\telse if (error == (0xc0070000 | ERROR_NO_SYSTEM_RESOURCES))\n\t\t\t\t\tCon_Printf(\"wglCreateContextAttribsARB ran out of system resources.\\n\");\n\t\t\t\telse if (error == (0xc0070000 | ERROR_INVALID_PARAMETER))\n\t\t\t\t\tCon_Printf(\"wglCreateContextAttribsARB reported invalid parameter.\\n\");\n\t\t\t\telse\n\t\t\t\t\tCon_Printf(\"Unknown error creating an OpenGL (%s) Context.\\n\", vid_gl_context_version.string);\n\t\t\t}\n\t\t}\n\t}\n\n\tqwglChoosePixelFormatARB\t= getglfunc(\"wglChoosePixelFormatARB\");\n\tqwglGetPixelFormatAttribfvARB = getglfunc(\"wglGetPixelFormatAttribfvARB\");\n\n\tif (info->stereo)\n\t{\n\t\tGLboolean ster = false;\n\t\tqglGetBooleanv(GL_STEREO, &ster);\n\t\tif (!ster)\n\t\t\tCon_Printf(\"Unable to create stereoscopic/quad-buffered OpenGL context. Please use a different stereoscopic method.\\n\");\n\t}\n\n\tqwglSwapIntervalEXT\t\t= getglfunc(\"wglSwapIntervalEXT\");\n\tif (qwglSwapIntervalEXT && *vid_vsync.string)\n\t{\n\t\tTRACE((\"dbg: VID_AttachGL: qwglSwapIntervalEXT\\n\"));\n\t\tqwglSwapIntervalEXT(vid_vsync.value);\n\t}\n\n\tvid.flags = 0;\n\tiAttributeNames[0] = WGL_PIXEL_TYPE_ARB;\n\tiAttributeNames[1] = WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB;\n\tif (qwglGetPixelFormatAttribfvARB && qwglGetPixelFormatAttribfvARB(maindc, currentpixelformat, 0, 1, iAttributeNames+0, fAttributeValues+0) && fAttributeValues[0] == WGL_TYPE_RGBA_FLOAT_ARB)\n\t\tvid.flags |= VID_FP16 | VID_SRGB_FB_LINEAR;\n\tif (qwglGetPixelFormatAttribfvARB && qwglGetPixelFormatAttribfvARB(maindc, currentpixelformat, 0, 1, iAttributeNames+1, fAttributeValues+1) && fAttributeValues[1] == TRUE)\n\t\tvid.flags |= VID_SRGB_CAPABLE;\n\treturn true;\n}\n#endif\n\nstatic void QDECL VID_Wait_Override_Callback(struct cvar_s *var, char *oldvalue)\n{\n\tswitch(platform_rendermode)\n\t{\n#ifdef USE_WGL\n\tcase MODE_WGL:\n\t\tif (qwglSwapIntervalEXT && *vid_vsync.string)\n\t\t\tqwglSwapIntervalEXT(vid_vsync.value);\n\t\tbreak;\n#endif\n\tdefault:\n\t\tbreak;\n\t}\n}\n\nstatic void GLVID_Recenter_f(void)\n{\n\t// 4 unused variables\n\t//int nw = vid_width.value;\n\t//int nh = vid_height.value;\n\t//int nx = 0;\n\t//int ny = 0;\n\n\tif (Cmd_Argc() > 1)\n\t\tsys_parentleft = atoi(Cmd_Argv(1));\n\tif (Cmd_Argc() > 2)\n\t\tsys_parenttop = atoi(Cmd_Argv(2));\n\tif (Cmd_Argc() > 3)\n\t\tsys_parentwidth = atoi(Cmd_Argv(3));\n\tif (Cmd_Argc() > 4)\n\t\tsys_parentheight = atoi(Cmd_Argv(4));\n\tif (Cmd_Argc() > 5)\n\t{\n\t\tHWND newparent = (HWND)(DWORD_PTR)strtoull(Cmd_Argv(5), NULL, 16);\n\t\tif (newparent != sys_parentwindow && mainwindow && modestate==MS_WINDOWED)\n\t\t\tSetParent(mainwindow, sys_parentwindow);\n\t\tsys_parentwindow = newparent;\n\t}\n\n\tif (sys_parentwindow && modestate==MS_WINDOWED)\n\t{\n\t\tWindowRect = centerrect(sys_parentleft, sys_parenttop, sys_parentwidth, sys_parentheight, sys_parentwidth, sys_parentheight);\n\t\tMoveWindow(mainwindow, WindowRect.left, WindowRect.top, WindowRect.right - WindowRect.left, WindowRect.bottom - WindowRect.top, FALSE);\n\n\t\tVID_UpdateWindowStatus (mainwindow);\n\t\tCvar_ForceCallback(&vid_conautoscale);\n\t}\n}\n\nstatic void QDECL VID_WndAlpha_Override_Callback(struct cvar_s *var, char *oldvalue)\n{\n\t//this code tells windows to use the alpha channel of the screen, but does really nasty things with the mouse such that its unplayable.\n\t//its not useful.\n/*\tif (modestate==MS_WINDOWED)\n\t{\n\t\tstruct qDWM_BLURBEHIND \n\t\t{\n\t\t\t  DWORD dwFlags;\n\t\t\t  BOOL  fEnable;\n\t\t\t  HRGN  hRgnBlur;\n\t\t\t  BOOL  fTransitionOnMaximized;\n\t\t} bb = {1, true, NULL, true};\n\t\tHRESULT (WINAPI *pDwmEnableBlurBehindWindow)(HWND hWnd,const struct qDWM_BLURBEHIND *pBlurBehind);\n\t\tdllfunction_t dwm[] =\n\t\t{\n\t\t\t{(void*)&pDwmEnableBlurBehindWindow, \"DwmEnableBlurBehindWindow\"},\n\t\t\t{NULL,NULL}\n\t\t};\n\t\tif (Sys_LoadLibrary(\"dwmapi.dll\", dwm))\n\t\t\tpDwmEnableBlurBehindWindow(mainwindow, &bb);\n\t}\n*/\n\n#ifdef WS_EX_LAYERED\n\t//enable whole-window fixed transparency. should work in win2k+\n\t//note that this can destroy framerates, and they won't reset when the setting is reverted to 1.\n\t//be prepared to do a vid_restart.\n\tif (modestate==MS_WINDOWED)\n\t{\n\t\tint av;\n\t\tHMODULE hm = GetModuleHandleA(\"user32.dll\");\n\t\tlpfnSetLayeredWindowAttributes pSetLayeredWindowAttributes;\n\t\tpSetLayeredWindowAttributes = (void*)GetProcAddress(hm, \"SetLayeredWindowAttributes\");\n\n\t\tav = 255 * var->value;\n\t\tif (av < 70)\n\t\t\tav = 70;\n\t\tif (av > 255)\n\t\t\tav = 255;\n\n\t\tif (pSetLayeredWindowAttributes)\n\t\t{\n\t\t\t// Set WS_EX_LAYERED on this window\n\n\t\t\tif (av < 255)\n\t\t\t{\n\t\t\t\tSetWindowLong(mainwindow, GWL_EXSTYLE, GetWindowLong(mainwindow, GWL_EXSTYLE) | WS_EX_LAYERED);\n\n\t\t\t\t// Make this window 70% alpha\n\t\t\t\tpSetLayeredWindowAttributes(mainwindow, 0, (BYTE)av, LWA_ALPHA);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tSetWindowLong(mainwindow, GWL_EXSTYLE, GetWindowLong(mainwindow, GWL_EXSTYLE) & ~WS_EX_LAYERED);\n\t\t\t\tpSetLayeredWindowAttributes(mainwindow, 0, (BYTE)255, LWA_ALPHA);\n\t\t\t}\n\t\t}\n\t}\n#endif\n}\n\nvoid GLVID_SwapBuffers (void)\n{\n\tswitch(platform_rendermode)\n\t{\n#ifdef USE_WGL\n\tcase MODE_WGL:\n\t\tif (qwglSwapLayerBuffers)\n\t\t{\n\t\t\tif (!qwglSwapLayerBuffers(maindc, WGL_SWAP_MAIN_PLANE))\n\t\t\t\tqwglSwapLayerBuffers = NULL;\n\t\t}\n\t\telse\n\t\t\tqSwapBuffers(maindc);\n\t\tbreak;\n#endif\n#ifdef USE_EGL\n\tcase MODE_EGL:\n\t\tEGL_SwapBuffers();\n\t\tbreak;\n#endif\n#ifdef VKQUAKE\n\tcase MODE_VULKAN:\n#ifdef USE_WGL\n\tcase MODE_NVVULKAN:\n#endif\n\t\t//FIXME: force a buffer swap now (might be called while loading (eg: q3), where we won't get a chance to redraw for a bit)\n\t\tbreak;\n#endif\n\t}\n\n// handle the mouse state when windowed if that's changed\n\n\tINS_UpdateGrabs(modestate != MS_WINDOWED, vid.activeapp);\n}\n\nstatic void OblitterateOldGamma(void)\n{\n\tint i;\n\tif (vid_preservegamma.value)\n\t\treturn;\n\n\tfor (i = 0; i < 256; i++)\n\t{\n\t\toriginalgammaramps[0][i] = (i<<8) + i;\n\t\toriginalgammaramps[1][i] = (i<<8) + i;\n\t\toriginalgammaramps[2][i] = (i<<8) + i;\n\t}\n}\n\nqboolean GLVID_ApplyGammaRamps (unsigned int gammarampsize, unsigned short *ramps)\n{\n\tif (ramps)\n\t{\n\t\tswitch(vid_hardwaregamma.ival)\n\t\t{\n\t\tcase 0:\t//never use hardware/glsl gamma\n\t\tcase 2:\t//ALWAYS use glsl gamma\n\t\tcase 4:\t//scene-only gamma\n\t\t\treturn false;\n\t\tdefault:\n\t\tcase 1:\t//no hardware gamma when windowed\n\t\t\tif (modestate == MS_WINDOWED)\n\t\t\t\treturn false;\n\t\t\tbreak;\n\t\tcase 3:\t//ALWAYS try to use hardware gamma, even when it fails...\n\t\t\tbreak;\n\t\t}\n\n\t\tif (vid.activeapp)\t//this is needed because ATI drivers don't work properly (or when task-switched out).\n\t\t{\t//we have hardware gamma applied - if we're doing a BF, we don't want to reset to the default gamma (yuck)\n\t\t\tif (vid_desktopgamma.value)\n\t\t\t{\n\t\t\t\tHDC hDC = GetDC(GetDesktopWindow());\n\t\t\t\tqSetDeviceGammaRamp (hDC, ramps);\n\t\t\t\tReleaseDC(GetDesktopWindow(), hDC);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tqSetDeviceGammaRamp (maindc, ramps);\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\telse if (gammaworks)\n\t{\n\t\t//revert to default\n\t\tif (qSetDeviceGammaRamp)\n\t\t{\n\t\t\tOblitterateOldGamma();\n\n\t\t\tif (vid_desktopgamma.value)\n\t\t\t{\n\t\t\t\tHDC hDC = GetDC(GetDesktopWindow());\n\t\t\t\tqSetDeviceGammaRamp (hDC, originalgammaramps);\n\t\t\t\tReleaseDC(GetDesktopWindow(), hDC);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tqSetDeviceGammaRamp(maindc, originalgammaramps);\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\treturn false;\n}\n\nvoid GLVID_Crashed(void)\n{\n\tif (qSetDeviceGammaRamp && gammaworks)\n\t{\n\t\tOblitterateOldGamma();\n\t\tqSetDeviceGammaRamp(maindc, originalgammaramps);\n\t}\n}\n\nvoid\tGLVID_Shutdown (void)\n{\n\tif (qSetDeviceGammaRamp)\n\t{\n\t\tOblitterateOldGamma();\n\n\t\tif (vid_desktopgamma.value)\n\t\t{\n\t\t\tHDC hDC = GetDC(GetDesktopWindow());\n\t\t\tqSetDeviceGammaRamp(hDC, originalgammaramps);\n\t\t\tReleaseDC(GetDesktopWindow(), hDC);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tqSetDeviceGammaRamp(maindc, originalgammaramps);\n\t\t}\n\t}\n\tqSetDeviceGammaRamp = NULL;\n\tqGetDeviceGammaRamp = NULL;\n\n\tgammaworks = false;\n\n//\tGLBE_Shutdown();\n//\tImage_Shutdown();\n\tVID_UnSetMode();\n}\n\n\n//==========================================================================\n\n#ifdef USE_WGL\nstatic BOOL CheckForcePixelFormat(rendererstate_t *info)\n{\n\tif (qwglChoosePixelFormatARB && (info->multisample || info->srgb || info->bpp==30))\n\t{\n\t\tHDC hDC;\n\t\tint valid;\n\t\tfloat fAttribute[] = {0,0};\n\t\tUINT numFormats;\n\t\tint pixelformats[1];\n\t\tint iAttributes = 0;\n\t\tint iAttribute[16*2];\n\n\t\tiAttribute[iAttributes++] = WGL_DRAW_TO_WINDOW_ARB;\t\t\t\tiAttribute[iAttributes++] = GL_TRUE;\n\t\tiAttribute[iAttributes++] = WGL_SUPPORT_OPENGL_ARB;\t\t\t\tiAttribute[iAttributes++] = GL_TRUE;\n\t\tiAttribute[iAttributes++] = WGL_ACCELERATION_ARB;\t\t\t\tiAttribute[iAttributes++] = WGL_FULL_ACCELERATION_ARB;\n\n\t\tif (info->srgb>=2 && modestate != MS_WINDOWED)\n\t\t{\t//half-float backbuffers!\n\n\t\t\t//'as has been the case since Windows Vista, fp16 swap chains are expected to have linear color data'\n\t\t\t//if we try using WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB, then we won't actually get a pixelformat.\n\t\t\t//we just have to assume that its a linear colour space.\n\n\t\t\t//we ONLY use this fullscreen, because its totally fucked on nvidia otherwise.\n\t\t\t//when windowed, its an scRGB image but with and srgb capable false, when fullscreen its always linear until something else takes focus...\n\t\t\t//so if windowed or unfocused or whatever, we would need to use custom glsl to fix the gamma settings.\n\n\t\t\t//additionally, a single program using floats will disable the desktop compositor, which can be a bit jarring.\n\n\t\t\tiAttribute[iAttributes++] = WGL_PIXEL_TYPE_ARB;\t\t\t\t\tiAttribute[iAttributes++] = WGL_TYPE_RGBA_FLOAT_ARB;\n\t\t\tiAttribute[iAttributes++] = WGL_RED_BITS_ARB;\t\t\t\t\tiAttribute[iAttributes++] = 16;\n\t\t\tiAttribute[iAttributes++] = WGL_GREEN_BITS_ARB;\t\t\t\t\tiAttribute[iAttributes++] = 16;\n\t\t\tiAttribute[iAttributes++] = WGL_BLUE_BITS_ARB;\t\t\t\t\tiAttribute[iAttributes++] = 16;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (info->srgb)\n\t\t\t{\n\t\t\t\tiAttribute[iAttributes++] = WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB;\tiAttribute[iAttributes++] = info->bpp<=32;\n\t\t\t}\n\t\t\tif (info->bpp==30)\n\t\t\t{\t//10-bit backbuffers!\n\t//\t\t\tiAttribute[iAttributes++] = WGL_PIXEL_TYPE_ARB;\t\t\t\t\tiAttribute[iAttributes++] = WGL_TYPE_RGBA_FLOAT_ARB;\n\t\t\t\tiAttribute[iAttributes++] = WGL_RED_BITS_ARB;\t\t\t\t\tiAttribute[iAttributes++] = 10;\n\t\t\t\tiAttribute[iAttributes++] = WGL_GREEN_BITS_ARB;\t\t\t\t\tiAttribute[iAttributes++] = 10;\n\t\t\t\tiAttribute[iAttributes++] = WGL_BLUE_BITS_ARB;\t\t\t\t\tiAttribute[iAttributes++] = 10;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tiAttribute[iAttributes++] = WGL_COLOR_BITS_ARB;\t\t\t\t\tiAttribute[iAttributes++] = (info->bpp>24)?24:info->bpp;\n\t\t\t}\n\t\t}\n//\t\tiAttribute[iAttributes++] = WGL_ALPHA_BITS_ARB;\t\t\t\t\tiAttribute[iAttributes++] = 2;\n\t\tiAttribute[iAttributes++] = WGL_DEPTH_BITS_ARB;\t\t\t\t\tiAttribute[iAttributes++] = info->depthbits?info->depthbits:24;\n\t\tiAttribute[iAttributes++] = WGL_STENCIL_BITS_ARB;\t\t\t\tiAttribute[iAttributes++] = 8;\n\t\tiAttribute[iAttributes++] = WGL_DOUBLE_BUFFER_ARB;\t\t\t\tiAttribute[iAttributes++] = GL_TRUE;\n\t\tiAttribute[iAttributes++] = WGL_STEREO_ARB;\t\t\t\t\t\tiAttribute[iAttributes++] = info->stereo;\n\t\tif (info->multisample)\n\t\t{\n\t\t\tiAttribute[iAttributes++] = WGL_SAMPLE_BUFFERS_ARB;\t\t\t\tiAttribute[iAttributes++] = GL_TRUE;\n\t\t\tiAttribute[iAttributes++] = WGL_SAMPLES_ARB,\t\t\t\t\tiAttribute[iAttributes++] = info->multisample;\t\t\t\t\t\t// Check For 4x Multisampling\n\t\t}\n\t\tiAttribute[iAttributes++] = 0;\t\t\t\t\t\t\t\t\tiAttribute[iAttributes++] = 0;\n\n\n\t\tTRACE((\"dbg: bSetupPixelFormat: attempting wglChoosePixelFormatARB (multisample 4)\\n\"));\n\t\thDC = GetDC(mainwindow);\n\n\t\tvalid = qwglChoosePixelFormatARB(hDC,iAttribute,fAttribute,countof(pixelformats),pixelformats,&numFormats);\n/*\t\twhile ((!valid || numFormats < 1) && iAttribute[19] > 1)\n\t\t{\t//failed, switch wgl_samples to 2\n\t\t\tiAttribute[19] /= 2;\n\t\t\tTRACE((\"dbg: bSetupPixelFormat: attempting wglChoosePixelFormatARB (smaller multisample)\\n\"));\n\t\t\tvalid = qwglChoosePixelFormatARB(hDC,iAttribute,fAttribute,1,&pixelformat,&numFormats);\n\t\t}\n*/\n\n#if 0\n\t\tfor (iAttributes = -1; iAttributes < (int)numFormats; iAttributes++)\n\t\t{\n\t\t\tint j;\n\t\t\tstruct\n\t\t\t{\n\t\t\t\tchar *name;\n\t\t\t\tint id;\n\t\t\t} iAttributeTable[] = {\n\t\t\t\t{\"WGL_DRAW_TO_WINDOW\",\t\t\t\tWGL_DRAW_TO_WINDOW_ARB},\n\t\t\t\t{\"WGL_DRAW_TO_BITMAP\",\t\t\t\t0x2002},\n\t\t\t\t{\"WGL_ACCELERATION\",\t\t\t\tWGL_ACCELERATION_ARB},\n\t\t\t\t{\"WGL_NEED_PALETTE\",\t\t\t\t0x2004},\n\t\t\t\t{\"WGL_NEED_SYSTEM_PALETTE\",\t\t\t0x2005},\n\t\t\t\t{\"WGL_SWAP_LAYER_BUFFERS\",\t\t\tWGL_SWAP_LAYER_BUFFERS_ARB},\n\t\t\t\t{\"WGL_SWAP_METHOD\",\t\t\t\t\t0x2007},\n\t\t\t\t{\"WGL_NUMBER_OVERLAYS\",\t\t\t\t0x2008},\n\t\t\t\t{\"WGL_NUMBER_UNDERLAYS\",\t\t\t0x2009},\n\t\t\t\t{\"WGL_TRANSPARENT\",\t\t\t\t\t0x200A},\n\t\t\t\t{\"WGL_TRANSPARENT_RED_VALUE\",\t\t0x2037},\n\t\t\t\t{\"WGL_TRANSPARENT_GREEN_VALUE\",\t\t0x2038},\n\t\t\t\t{\"WGL_TRANSPARENT_BLUE_VALUE\",\t\t0x2039},\n\t\t\t\t{\"WGL_TRANSPARENT_ALPHA_VALUE\",\t\t0x203a},\n\t\t\t\t{\"WGL_TRANSPARENT_INDEX_VALUE\",\t\t0x203B},\n\t\t\t\t{\"WGL_SHARE_DEPTH\",\t\t\t\t\t0x200C},\n\t\t\t\t{\"WGL_SHARE_STENCIL\",\t\t\t\t0x200D},\n\t\t\t\t{\"WGL_SHARE_ACCUM\",\t\t\t\t\t0x200E},\n\t\t\t\t{\"WGL_SUPPORT_GDI\",\t\t\t\t\t0x200F},\n\t\t\t\t{\"WGL_SUPPORT_OPENGL\",\t\t\t\tWGL_SUPPORT_OPENGL_ARB},\n\t\t\t\t{\"WGL_DOUBLE_BUFFER\",\t\t\t\tWGL_DOUBLE_BUFFER_ARB},\n\t\t\t\t{\"WGL_STEREO\",\t\t\t\t\t\tWGL_STEREO_ARB},\n\t\t\t\t{\"WGL_PIXEL_TYPE\",\t\t\t\t\tWGL_PIXEL_TYPE_ARB},\n\t\t\t\t{\"WGL_COLOR_BITS\",\t\t\t\t\tWGL_COLOR_BITS_ARB},\n\t\t\t\t{\"WGL_RED_BITS\",\t\t\t\t\t0x2015},\n\t\t\t\t{\"WGL_RED_SHIFT\",\t\t\t\t\t0x2016},\n\t\t\t\t{\"WGL_GREEN_BITS\",\t\t\t\t\t0x2017},\n\t\t\t\t{\"WGL_GREEN_SHIFT\",\t\t\t\t\t0x2018},\n\t\t\t\t{\"WGL_BLUE_BITS\",\t\t\t\t\t0x2019},\n\t\t\t\t{\"WGL_BLUE_SHIFT\",\t\t\t\t\t0x201A},\n\t\t\t\t{\"WGL_ALPHA_BITS\",\t\t\t\t\tWGL_ALPHA_BITS_ARB},\n\t\t\t\t{\"WGL_ALPHA_SHIFT\",\t\t\t\t\t0x201C},\n\t\t\t\t{\"WGL_ACCUM_BITS\",\t\t\t\t\t0x201D},\n\t\t\t\t{\"WGL_ACCUM_RED_BITS\",\t\t\t\t0x201E},\n\t\t\t\t{\"WGL_ACCUM_GREEN_BITS\",\t\t\t0x201F},\n\t\t\t\t{\"WGL_ACCUM_BLUE_BITS\",\t\t\t\t0x2020},\n\t\t\t\t{\"WGL_ACCUM_ALPHA_BITS\",\t\t\t0x2021},\n\t\t\t\t{\"WGL_DEPTH_BITS\",\t\t\t\t\tWGL_DEPTH_BITS_ARB},\n\t\t\t\t{\"WGL_STENCIL_BITS\",\t\t\t\tWGL_STENCIL_BITS_ARB},\n\t\t\t\t{\"WGL_AUX_BUFFERS\",\t\t\t\t\t0x2024},\n\t\t\t\t\n\t\t\t\t//extra extensions\n\t\t\t\t{\"WGL_SAMPLE_BUFFERS_ARB\",\t\t\tWGL_SAMPLE_BUFFERS_ARB},\t//multisample\n\t\t\t\t{\"WGL_SAMPLES_ARB\",\t\t\t\t\tWGL_SAMPLES_ARB},\t//multisample\n\n\t\t\t\t{\"WGL_DRAW_TO_PBUFFER_ARB\",\t\t\t0x202D},\t//pbuffers\n\t\t\t\t{\"WGL_MAX_PBUFFER_PIXELS_ARB\",\t\t0x202E},\t//pbuffers\n\t\t\t\t{\"WGL_MAX_PBUFFER_WIDTH_ARB\",\t\t0x202F},\t//pbuffers\n\t\t\t\t{\"WGL_MAX_PBUFFER_HEIGHT_ARB\",\t\t0x2030},\t//pbuffers\n\n\t\t\t\t{\"WGL_BIND_TO_TEXTURE_RGB_ARB\",\t\t0x2070},\n\t\t\t\t{\"WGL_BIND_TO_TEXTURE_RGBA_ARB\",\t0x2071},\n\t\t\t\t{\"WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB\",WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB},\n\t\t\t\t{\"WGL_COLORSPACE_EXT\",\t\t\t\t0x309D},\t//WGL_EXT_colorspace\n\n\t\t\t\t//stuff my drivers don't support\n//\t\t\t\t{\"WGL_DEPTH_FLOAT_EXT\",\t\t\t\t0x2040},\n\t\t\t};\n\t\t\tint iAttributeNames[countof(iAttributeTable)];\n\t\t\tfloat fAttributeValues[countof(iAttributeTable)];\n\t\t\tfloat basevalues[countof(iAttributeTable)];\n\n\t\t\tfor (j = 0; j < countof(iAttributeTable); j++)\n\t\t\t\tiAttributeNames[j] = iAttributeTable[j].id;\n\n\t\t\tSys_Printf(\"Pixel Format %i --------------------------\\n\", iAttributes<0?currentpixelformat:pixelformats[iAttributes]);\n\t\t\tif (qwglGetPixelFormatAttribfvARB(hDC, iAttributes<0?currentpixelformat:pixelformats[iAttributes], 0, countof(iAttributeTable), iAttributeNames, fAttributeValues))\n\t\t\t{\n\t\t\t\tif (iAttributes==-1)\n\t\t\t\t\tmemcpy(basevalues, fAttributeValues, sizeof(basevalues));\n\t\t\t\tfor (j = 0; j < countof(iAttributeTable); j++)\n\t\t\t\t{\n\t\t\t\t\tif (iAttributes==-1 || fAttributeValues[j] != basevalues[j])\n\t\t\t\t\t{\n\t\t\t\t\t\tif (iAttributeTable[j].id == 0x2007 && fAttributeValues[j] == 0x2028)\n\t\t\t\t\t\t\tSys_Printf(\"%s: exchange\\n\", iAttributeTable[j].name);\n\t\t\t\t\t\telse if (iAttributeTable[j].id == 0x2007 && fAttributeValues[j] == 0x2029)\n\t\t\t\t\t\t\tSys_Printf(\"%s: copy\\n\", iAttributeTable[j].name);\n\t\t\t\t\t\telse if (iAttributeTable[j].id == 0x309D && fAttributeValues[j] == 0x3089)\n\t\t\t\t\t\t\tSys_Printf(\"%s: WGL_COLORSPACE_SRGB\\n\", iAttributeTable[j].name);\n\t\t\t\t\t\telse if (iAttributeTable[j].id == 0x309D && fAttributeValues[j] == 0x308A)\n\t\t\t\t\t\t\tSys_Printf(\"%s: WGL_COLORSPACE_LINEAR\\n\", iAttributeTable[j].name);\n\t\t\t\t\t\telse if (iAttributeTable[j].id == WGL_PIXEL_TYPE_ARB && fAttributeValues[j] == WGL_TYPE_RGBA_FLOAT_ARB)\n\t\t\t\t\t\t\tSys_Printf(\"%s: WGL_TYPE_RGBA_FLOAT_ARB\\n\", iAttributeTable[j].name);\n\t\t\t\t\t\telse if (iAttributeTable[j].id == WGL_PIXEL_TYPE_ARB && fAttributeValues[j] == 0x20A8)\n\t\t\t\t\t\t\tSys_Printf(\"%s: WGL_TYPE_RGBA_UNSIGNED_FLOAT_EXT\\n\", iAttributeTable[j].name);\n\t\t\t\t\t\telse if (iAttributeTable[j].id == 0x202E || iAttributeTable[j].id == WGL_PIXEL_TYPE_ARB)\n\t\t\t\t\t\t\tSys_Printf(\"%s: %#x\\n\", iAttributeTable[j].name, (int)fAttributeValues[j]);\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tSys_Printf(\"%s: %g\\n\", iAttributeTable[j].name, fAttributeValues[j]);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n#endif\n\n\t\tReleaseDC(mainwindow, hDC);\n\t\tif (valid && numFormats > 0 && pixelformats[0] != currentpixelformat)\n\t\t{\n\t\t\tshouldforcepixelformat = true;\n\t\t\tforcepixelformat = pixelformats[0];\n\t\t\treturn true;\n\t\t}\n\t}\n\treturn false;\n}\n\nstatic BYTE IntensityFromShifted(unsigned int index, unsigned int shift, unsigned int bits)\n{\n\tunsigned int val;\n\n\tval = (index >> shift) & ((1 << bits) - 1);\n\n\tswitch (bits)\n\t{\n\tcase 1:\n\t\tval = val ? 0xFF : 0;\n\t\tbreak;\n\tcase 2:\n\t\tval |= val << 2;\n\t\tval |= val << 4;\n\t\tbreak;\n\tcase 3:\n\t\tval = val << (8 - bits);\n\t\tval |= val >> 3;\n\t\tbreak;\n\tcase 4:\n\tcase 5:\n\tcase 6:\n\tcase 7:\n\t\tval = val << (8 - bits);\n\t\tval |= val >> bits;\n\t\tbreak;\n\tcase 8:\n\t\tbreak;\n\tdefault:\n\t\treturn 0;\n\t}\n\n\treturn val;\n}\n\nstatic void FixPaletteInDescriptor(HDC hDC, PIXELFORMATDESCRIPTOR *pfd)\n{\n\tLOGPALETTE *ppal;\n\tHPALETTE hpal;\n\tint idx, clrs;\n\n\tif (pfd->dwFlags & PFD_NEED_PALETTE)\n\t{\n\t\tclrs = 1 << pfd->cColorBits;\n\n\t\tppal = Z_Malloc(sizeof(LOGPALETTE) + sizeof(PALETTEENTRY) * clrs);\n\n\t\tppal->palVersion = 0x300;\n\t\tppal->palNumEntries = clrs;\n\n\t\tfor (idx = 0; idx < clrs; idx++)\n\t\t{\n\t\t\tppal->palPalEntry[idx].peRed = IntensityFromShifted(idx, pfd->cRedShift, pfd->cRedBits);\n\t\t\tppal->palPalEntry[idx].peGreen = IntensityFromShifted(idx, pfd->cGreenShift, pfd->cGreenBits);\n\t\t\tppal->palPalEntry[idx].peBlue = IntensityFromShifted(idx, pfd->cBlueShift, pfd->cBlueBits);\n\t\t\tppal->palPalEntry[idx].peFlags = 0;\n\t\t}\n\n\t\thpal = CreatePalette(ppal);\n\t\tSelectPalette(hDC, hpal, FALSE);\n\t\tRealizePalette(hDC);\n\t\tZ_Free(ppal);\n\t}\n}\n\nstatic BOOL bSetupPixelFormat(HDC hDC, rendererstate_t *info)\n{\n\tPIXELFORMATDESCRIPTOR pfd = {\n\tsizeof(PIXELFORMATDESCRIPTOR),\t// size of this pfd\n\t1,\t\t\t\t// version number\n\tPFD_DRAW_TO_WINDOW \t\t// support window\n\t|  PFD_SUPPORT_OPENGL \t// support OpenGL\n\t|  PFD_DOUBLEBUFFER,\t\t// double buffered\n\tPFD_TYPE_RGBA,\t\t\t// RGBA type\n\t24,\t\t\t\t// 24-bit color depth\n\t0, 0, 0, 0, 0, 0,\t\t// color bits ignored\n\t0,\t\t\t\t// no alpha buffer\n\t0,\t\t\t\t// shift bit ignored\n\t0,\t\t\t\t// no accumulation buffer\n\t0, 0, 0, 0, \t\t\t// accum bits ignored\n#ifndef RTLIGHTS\n\t24,\t\t\t\t// 24-bit z-buffer\n\t0,\t\t\t\t// 0 stencil, don't need it unless we're using rtlights\n#else\n\t24,\t\t\t\t// 24-bit z-buffer\n\t8,\t\t\t\t// stencil buffer\n#endif\n\t0,\t\t\t\t// no auxiliary buffer\n\tPFD_MAIN_PLANE,\t\t\t// main layer\n\t0,\t\t\t\t// reserved\n\t0, 0, 0\t\t\t\t// layer masks ignored\n\t};\n\n\tTRACE((\"dbg: bSetupPixelFormat: ChoosePixelFormat\\n\"));\n\n\tif (info->stereo)\n\t\tpfd.dwFlags |= PFD_STEREO;\n\tif (info->bpp == 15 || info->bpp == 16)\n\t\tpfd.cColorBits = 16;\n\n\tif (shouldforcepixelformat && qwglChoosePixelFormatARB && \n\t\tqDescribePixelFormat(hDC, forcepixelformat, sizeof(pfd), &pfd) &&\n\t\tqSetPixelFormat(hDC, forcepixelformat, &pfd))\t//the extra && is paranoia\n\t{\n\t\tshouldforcepixelformat = false;\n\t\tcurrentpixelformat = forcepixelformat;\n\t}\n\telse if ((currentpixelformat = qChoosePixelFormat(hDC, &pfd)) && qDescribePixelFormat(hDC, currentpixelformat, sizeof(pfd), &pfd) && qSetPixelFormat(hDC, currentpixelformat, &pfd))\n\t\t;\t//we got our desired pixel format, or close enough\n\telse\n\t{\n\t\tpfd.cStencilBits = 0;\n\t\tif ((currentpixelformat = qChoosePixelFormat(hDC, &pfd)) && qDescribePixelFormat(hDC, currentpixelformat, sizeof(pfd), &pfd) && qSetPixelFormat(hDC, currentpixelformat, &pfd))\n\t\t\t;\n\t\telse\n\t\t{\n\t\t\tCon_Printf(\"Unable to find suitable pixel format: %i\\n\", (int)GetLastError());\n\t\t\treturn FALSE;\n\t\t}\n\t}\n\tshouldforcepixelformat = false;\n\n\tif ((pfd.dwFlags & PFD_GENERIC_FORMAT) && !(pfd.dwFlags & PFD_GENERIC_ACCELERATED))\n\t{\n\t\tCon_Printf(CON_WARNING \"WARNING: software-rendered opengl context\\nPlease install appropriate graphics drivers, or try d3d rendering instead\\n\");\n\t}\n\telse if (pfd.dwFlags & PFD_SWAP_COPY)\n\t\tCon_Printf(CON_WARNING \"WARNING: buffer swaps will use copy operations\\n\");\n\n\tFixPaletteInDescriptor(hDC, &pfd);\n\treturn TRUE;\n}\n#endif\n\n/*\n===================================================================\n\nMAIN WINDOW\n\n===================================================================\n*/\n\n/*\n================\nClearAllStates\n================\n*/\nstatic void ClearAllStates (void)\n{\n\tIN_KeyEvent(0, false, -1, 0);\t//-1 means to clear all keys.\n\tKey_ClearStates ();\n\tINS_ClearStates ();\n}\n\nstatic qboolean GLAppActivate(BOOL fActive, BOOL minimize)\n/****************************************************************************\n*\n* Function:     AppActivate\n* Parameters:   fActive - True if app is activating\n*\n* Description:  If the application is activating, then swap the system\n*               into SYSPAL_NOSTATIC mode so that our palettes will display\n*               correctly.\n*\n****************************************************************************/\n{\n\tstatic BOOL\tsound_active;\n\n//\tCon_Printf(\"GLAppActivate: %i %i\\n\", fActive, minimize);\n\n\tif (vid.activeapp == fActive && Minimized == minimize)\n\t\treturn false;\t//so windows doesn't crash us over and over again.\n\n\tvid.activeapp = fActive;// && (foregroundwindow==mainwindow);\n\tMinimized = minimize;\n\n// enable/disable sound on focus gain/loss\n\tif (!vid.activeapp && sound_active)\n\t{\n\t\tS_BlockSound ();\n\t\tsound_active = false;\n\t}\n\telse if (vid.activeapp && !sound_active)\n\t{\n\t\tS_UnblockSound ();\n\t\tsound_active = true;\n\t}\n\n\tINS_UpdateGrabs(modestate != MS_WINDOWED, vid.activeapp);\n\n\tif (fActive)\n\t{\n\t\tif (modestate == MS_FULLDIB)\n\t\t{\n\t\t\tif (vid_canalttab && vid_wassuspended)\n\t\t\t{\n\t\t\t\tvid_wassuspended = false;\n\t\t\t\tChangeDisplaySettings (&gdevmode, CDS_FULLSCREEN);\n\t\t\t\tShowWindow(mainwindow, SW_SHOWNORMAL);\n\n\t\t\t\t\t\t\t\t// Fix for alt-tab bug in NVidia drivers\n\t\t\t\tMoveWindow (mainwindow, 0, 0, gdevmode.dmPelsWidth, gdevmode.dmPelsHeight, false);\n\t\t\t}\n\t\t}\n\t\telse if (modestate == MS_FULLWINDOW)\n\t\t{\n\t\t\tShowWindow (mainwindow, SW_SHOWMAXIMIZED);\n\t\t\tUpdateWindow (mainwindow);\n\t\t}\n\n\t\tgammapending = 0.5;\t\t\t\t//delayed gamma force\n\t\tCvar_ForceCallback(&v_gamma);\t//so the delay isn't so blatent when you have decent graphics drivers that don't break things.\n\t}\n\n\tif (!fActive)\n\t{\n\t\tif (modestate == MS_FULLDIB)\n\t\t{\n\t\t\tif (vid_canalttab)\n\t\t\t{\n\t\t\t\tChangeDisplaySettings (NULL, 0);\n\t\t\t\tvid_wassuspended = true;\n\t\t\t}\n\t\t}\n\n\t\tCvar_ForceCallback(&v_gamma);\t//wham bam thanks.\n\t}\n\n\treturn true;\n}\n\n#ifndef TWF_WANTPALM\ntypedef struct _TOUCHINPUT {\n  LONG      x;\n  LONG      y;\n  HANDLE    hSource;\n  DWORD     dwID;\n  DWORD     dwFlags;\n  DWORD     dwMask;\n  DWORD     dwTime;\n  ULONG_PTR dwExtraInfo;\n  DWORD     cxContact;\n  DWORD     cyContact;\n} TOUCHINPUT, *PTOUCHINPUT;\nDECLARE_HANDLE(HTOUCHINPUT);\n\n#define WM_TOUCH\t\t\t\t\t0x0240 \n#define TOUCHINPUTMASKF_CONTACTAREA\t0x0004\n#define TOUCHEVENTF_DOWN\t\t\t0x0002\n#define TOUCHEVENTF_UP\t\t\t\t0x0004\n#define TWF_WANTPALM\t\t\t\t0x00000002\n#endif\n\nstatic BOOL (WINAPI *pRegisterTouchWindow)(HWND hWnd, ULONG ulFlags);\nstatic BOOL (WINAPI *pGetTouchInputInfo)(HTOUCHINPUT hTouchInput, UINT cInputs, PTOUCHINPUT pInputs, int cbSize);\nstatic BOOL (WINAPI *pCloseTouchInputHandle)(HTOUCHINPUT hTouchInput);\nstatic void Win_Touch_Init(HWND wnd)\n{\n\tHMODULE lib;\n\tlib = LoadLibraryA(\"user32.dll\");\n\tpRegisterTouchWindow = (void*)GetProcAddress(lib, \"RegisterTouchWindow\");\n\tpGetTouchInputInfo = (void*)GetProcAddress(lib, \"GetTouchInputInfo\");\n\tpCloseTouchInputHandle = (void*)GetProcAddress(lib, \"CloseTouchInputHandle\");\n\n\tif (pRegisterTouchWindow && pGetTouchInputInfo && pCloseTouchInputHandle)\n\t\tpRegisterTouchWindow(wnd, TWF_WANTPALM);\n}\nstatic void Win_Touch_Event(int points, HTOUCHINPUT ti)\n{\n\tfloat sz;\n\tint i;\n\tTOUCHINPUT *inputs = malloc(points * sizeof(*inputs)), *input;\n\tif (inputs)\n\t{\n\t\tif (pGetTouchInputInfo(ti, points, inputs, sizeof(*inputs)))\n\t\t{\n\t\t\tfor (i = 0, input = inputs; i < points; i++, input++)\n\t\t\t{\n\t\t\t\tint id = input->dwID+1;\t//googling implies the id is generally a low 0-based index. I can't test this. the +1 ensures that mouselook is not broken by someone trying to use a touchscreen at the same time.\n\t\t\t\tif (input->dwMask & TOUCHINPUTMASKF_CONTACTAREA)\n\t\t\t\t\tsz = sqrt((input->cxContact*input->cxContact + input->cyContact*input->cyContact) / 10000.0);\n\t\t\t\telse\n\t\t\t\t\tsz = 0;\n\n\t\t\t\t//the web seems to imply that the ids should be low values, <16 or so. hurrah.\n\n\t\t\t\t//movement *then* buttons. this should ensure that the cursor is positioned correctly.\n\t\t\t\tIN_MouseMove(id, true, input->x/100.0f, input->y/100.0f, 0, sz);\n\n\t\t\t\tif (input->dwFlags & TOUCHEVENTF_DOWN)\n\t\t\t\t\tIN_KeyEvent(id, true, K_MOUSE1, 0);\n\t\t\t\tif (input->dwFlags & TOUCHEVENTF_UP)\n\t\t\t\t\tIN_KeyEvent(id, false, K_MOUSE1, 0);\n\t\t\t}\n\t\t}\n\t\tfree(inputs);\n\t}\n\n\tpCloseTouchInputHandle(ti);\n}\n\n#ifdef WTHREAD\n//runs on the main/render thread, forwarded from the worker thread.\n//these events are the ones that would cause race conditions, but need to be able to cope with a little bit of a delay (and shouldn't need to trigger other window messages, as that would cause other races).\nvoid MainThreadWndProc(void *ctx, void *data, size_t msg, size_t ex)\n{\n\tswitch(msg)\n\t{\n\tcase WM_COPYDATA:\n\t\tHost_RunFile(data, ex, NULL);\n\t\tZ_Free(data);\n\t\tbreak;\n\tcase WM_CLOSE:\n\t\tM_Window_ClosePrompt();\n\t\tbreak;\n\tcase WM_SIZE:\n\tcase WM_MOVE:\n\t\tCvar_ForceCallback(&vid_conautoscale);\n\t\tbreak;\n\tcase WM_KILLFOCUS:\n\t\tGLAppActivate(FALSE, Minimized);\n\t\tClearAllStates ();\n\t\tbreak;\n\tcase WM_SETFOCUS:\n\t\tif (!GLAppActivate(TRUE, Minimized))\n\t\t\tbreak;\n\t\tClearAllStates ();\n\t\tbreak;\n\n#ifdef HAVE_CDPLAYER\n\tcase MM_MCINOTIFY:\n\t\tCDAudio_MessageHandler (mainwindow, msg, (WPARAM)ctx, (LPARAM)data);\n\t\tbreak;\n#endif\n\t}\n}\n#endif\n\n/* main window procedure\ndue to moving the main window over to a different thread, we gain access to input timestamps (as well as video refreshes when dragging etc)\nhowever, we have to tread carefully. the main/render thread will be running the whole time, and may trigger messages that we need to respond to _now_.\nthis means that the main and window thread cannot be allowed to contest any mutexes where anything but memory is touched before its unlocked.\n(or in other words, we can't have the main thread near-perma-lock any mutexes that can be locked-to-sync here)\n*/\nstatic LONG WINAPI GLMainWndProc (\n\tHWND\thWnd,\n\tUINT\tuMsg,\n\tWPARAM\twParam,\n\tLPARAM\tlParam)\n{\n\tLONG\tlRet = 1;\n//\tint\t\tfActive, fMinimized;\n\tint \ttemp;\n\textern unsigned int uiWheelMessage;\n\n\tif ( uMsg == uiWheelMessage )\n\t\tuMsg = WM_MOUSEWHEEL;\n\n\tswitch (uMsg)\n\t{\n\t\tcase WM_COPYDATA:\n\t\t\t{\n\t\t\t\tCOPYDATASTRUCT *cds = (COPYDATASTRUCT*)lParam;\n#ifdef WTHREAD\n\t\t\t\tCOM_AddWork(WG_MAIN, MainThreadWndProc, NULL, memcpy(Z_Malloc(cds->cbData), cds->lpData, cds->cbData), uMsg, cds->cbData);\n#else\n\t\t\t\tHost_RunFile(cds->lpData, cds->cbData, NULL);\n#endif\n\t\t\t\tlRet = 1;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase WM_KILLFOCUS:\n#ifdef WTHREAD\n\t\t\tCOM_AddWork(WG_MAIN, MainThreadWndProc, NULL, NULL, uMsg, 0);\n#else\n\t\t\tGLAppActivate(FALSE, Minimized);//FIXME: thread\n\t\t\tClearAllStates ();\t//FIXME: thread\n#endif\n\t\t\tif (modestate != MS_WINDOWED)\n\t\t\t\tShowWindow(mainwindow, SW_SHOWMINNOACTIVE);\n\t\t\tbreak;\n\t\tcase WM_SETFOCUS:\n#ifdef WTHREAD\n\t\t\tCOM_AddWork(WG_MAIN, MainThreadWndProc, NULL, NULL, uMsg, 0);\n#else\n\t\t\tif (!GLAppActivate(TRUE, Minimized))//FIXME: thread\n\t\t\t\tbreak;\n\t\t\tClearAllStates ();\t//FIXME: thread\n#endif\n\t\t\tbreak;\n\n\t\tcase WM_TOUCH:\n\t\t\tWin_Touch_Event(LOWORD(wParam), (HTOUCHINPUT)lParam);\n\t\t\treturn 0;\t//return 0 if we handled it.\n\n\t\tcase WM_CREATE:\n\t\t\tbreak;\n\n\t\tcase WM_MOVE:\n\t\t\tVID_UpdateWindowStatus (hWnd);\n#ifdef WTHREAD\n\t\t\tCOM_AddWork(WG_MAIN, MainThreadWndProc, NULL, NULL, uMsg, 0);\n#else\n\t\t\tCvar_ForceCallback(&vid_conautoscale);\n#endif\n\t\t\tbreak;\n\n\t\tcase WM_KEYDOWN:\n\t\tcase WM_SYSKEYDOWN:\n\t\t\tif (!vid_initializing)\n\t\t\t\tINS_TranslateKeyEvent(wParam, lParam, true, 0, false);\n\t\t\tbreak;\n\n//\t\tcase WM_UNICHAR:\n\t\tcase WM_DEADCHAR:\n\t\tcase WM_SYSDEADCHAR:\n\t\tcase WM_CHAR:\n\t\tcase WM_SYSCHAR:\n//\t\t\tif (!vid_initializing)\n//\t\t\t\tINS_TranslateKeyEvent(wParam, lParam, true);\n\t\t\tbreak;\n\n\t\tcase WM_KEYUP:\n\t\tcase WM_SYSKEYUP:\n\t\t\tif (!vid_initializing)\n\t\t\t\tINS_TranslateKeyEvent(wParam, lParam, false, 0, false);\n\t\t\tbreak;\n\n\t\tcase WM_APPCOMMAND:\n\t\t\tif (!INS_AppCommand(lParam))\n\t\t\t\tlRet = DefWindowProc(hWnd, uMsg, wParam, lParam);\t//otherwise it won't get handled by background apps, like media players.\n\t\t\tbreak;\n\n\t\tcase WM_MOUSEACTIVATE:\n\t\t\tlRet = MA_ACTIVATEANDEAT;\n\t\t\tbreak;\n\n\t// this is complicated because Win32 seems to pack multiple mouse events into\n\t// one update sometimes, so we always check all states and look for events\n\t\tcase WM_LBUTTONDOWN:\n\t\tcase WM_LBUTTONUP:\n\t\tcase WM_RBUTTONDOWN:\n\t\tcase WM_RBUTTONUP:\n\t\tcase WM_MBUTTONDOWN:\n\t\tcase WM_MBUTTONUP:\n\t\tcase WM_MOUSEMOVE:\n\t\tcase WM_XBUTTONDOWN:\n\t\tcase WM_XBUTTONUP:\n\t\t\ttemp = 0;\n\n\t\t\tif (wParam & MK_LBUTTON)\n\t\t\t{\n\t\t\t\ttemp |= 1;\n\t\t\t\tif (sys_parentwindow && modestate == MS_WINDOWED)\n\t\t\t\t\tSetFocus(hWnd);\n\t\t\t}\n\n\t\t\tif (wParam & MK_RBUTTON)\n\t\t\t\ttemp |= 2;\n\n\t\t\tif (wParam & MK_MBUTTON)\n\t\t\t\ttemp |= 4;\n\n\t\t\tif (wParam & MK_XBUTTON1)\n\t\t\t\ttemp |= 8;\n\n\t\t\tif (wParam & MK_XBUTTON2)\n\t\t\t\ttemp |= 16;\n\n\t\t\tif (wParam & MK_XBUTTON3)\n\t\t\t\ttemp |= 32;\n\n\t\t\tif (wParam & MK_XBUTTON4)\n\t\t\t\ttemp |= 64;\n\n\t\t\tif (wParam & MK_XBUTTON5)\n\t\t\t\ttemp |= 128;\n\n\t\t\tif (wParam & MK_XBUTTON6)\n\t\t\t\ttemp |= 256;\n\n\t\t\tif (wParam & MK_XBUTTON7)\n\t\t\t\ttemp |= 512;\n\n\t\t\tif (!vid_initializing)\n\t\t\t\tINS_MouseEvent (temp);\t//FIXME: thread (halflife)\n\n\t\t\tbreak;\n\n\t\t// JACK: This is the mouse wheel with the Intellimouse\n\t\t// Its delta is either positive or neg, and we generate the proper\n\t\t// Event.\n\t\tcase WM_MOUSEWHEEL:\n\t\t\tif (!vid_initializing)\n\t\t\t{\n\t\t\t\tif ((short) HIWORD(wParam&0xffffffff) > 0)\n\t\t\t\t{\n\t\t\t\t\tIN_KeyEvent(0, true, K_MWHEELUP, 0);\n\t\t\t\t\tIN_KeyEvent(0, false, K_MWHEELUP, 0);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tIN_KeyEvent(0, true, K_MWHEELDOWN, 0);\n\t\t\t\t\tIN_KeyEvent(0, false, K_MWHEELDOWN, 0);\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase WM_INPUT:\n\t\t\t// raw input handling\n\t\t\tif (!vid_initializing)\n\t\t\t{\n\t\t\t\tINS_RawInput_Read((HANDLE)lParam);\n\t\t\t\tlRet = 0;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase WM_DEVICECHANGE:\n\t\t\tCOM_AddWork(WG_MAIN, INS_DeviceChanged, NULL, NULL, uMsg, 0);\n\t\t\tlRet = TRUE;\n\t\t\tbreak;\n\n#ifdef VKQUAKE\n\t\tcase WM_USER_VKPRESENT:\n\t\t\tVK_DoPresent((struct vkframe*)lParam);\n\t\t\tbreak;\n#endif\n#if defined(VKQUAKE) && defined(USE_WGL)\n\t\tcase WM_USER_NVVKPRESENT:\n\t\t\tWin32NVVK_DoPresent((struct vkframe*)lParam);\n\t\t\tbreak;\n#endif\n\t\tcase WM_USER_VIDSHUTDOWN:\n\t\t\tPostQuitMessage(0);\n\t\t\tbreak;\n\t\tcase WM_USER_SPEECHTOTEXT:\n#ifdef HAVE_SPEECHTOTEXT\n\t\t\tSTT_Event();\n#endif\n\t\t\tbreak;\n\n\t\tcase WM_GETMINMAXINFO:\n\t\t\t{\n\t\t\t\tRECT windowrect;\n\t\t\t\tRECT clientrect;\n\t\t\t\tMINMAXINFO *mmi = (MINMAXINFO *) lParam;\n\n\t\t\t\tGetWindowRect (hWnd, &windowrect);\n\t\t\t\tGetClientRect (hWnd, &clientrect);\n\n\t\t\t\tmmi->ptMinTrackSize.x = 320 + ((windowrect.right - windowrect.left) - (clientrect.right - clientrect.left));\n\t\t\t\tmmi->ptMinTrackSize.y = 200 + ((windowrect.bottom - windowrect.top) - (clientrect.bottom - clientrect.top));\n\t\t\t}\n\t\t\treturn 0;\n\t\tcase WM_SIZE:\n\t\t\tvid.isminimized  = (wParam==SIZE_MINIMIZED);\n\t\t\tif (!vid_initializing)\n\t\t\t{\n\t\t\t\tVID_UpdateWindowStatus (hWnd);\n#ifdef WTHREAD\n\t\t\t\tCOM_AddWork(WG_MAIN, MainThreadWndProc, NULL, NULL, uMsg, 0);\n#else\n\t\t\t\tCvar_ForceCallback(&vid_conautoscale);\n#endif\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase WM_CLOSE:\n\t\t\tif (!vid_initializing)\n\t\t\t{\n#if 1\n\t#ifdef WTHREAD\n\t\t\t\tCOM_AddWork(WG_MAIN, MainThreadWndProc, NULL, NULL, uMsg, 0);\n\t#else\n\t\t\t\tM_Window_ClosePrompt();\n\t#endif\n\t\t\t\tSetFocus(hWnd);\n#else\n\t\t\t\tif (wantquit)\n\t\t\t\t{\n\t\t\t\t\t//urr, this would be the second time that they've told us to quit.\n\t\t\t\t\t//assume the main thread has deadlocked\n\t\t\t\t\tif (MessageBoxW (hWnd, L\"Terminate process?\", L\"Confirm Exit\",\n\t\t\t\t\t\t\tMB_YESNO | MB_SETFOREGROUND | MB_ICONEXCLAMATION | MB_DEFBUTTON2) == IDYES)\n\t\t\t\t\t{\n\t\t\t\t\t\t//abrupt process termination is never nice, but sometimes drivers suck.\n\t\t\t\t\t\t//or qc code runs away, or ...\n\t\t\t\t\t\texit(1);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\telse if (MessageBoxW (hWnd, L\"Are you sure you want to quit?\", L\"Confirm Exit\",\n\t\t\t\t\t\t\tMB_YESNO | MB_SETFOREGROUND | MB_ICONQUESTION|MB_DEFBUTTON2) == IDYES)\n\t\t\t\t{\n#ifdef WTHREAD\n\t\t\t\t\tCOM_AddWork(WG_MAIN, MainThreadWndProc, NULL, NULL, uMsg, 0);\n#else\n\t\t\t\t\tCbuf_AddText(\"\\nquit\\n\", RESTRICT_LOCAL);\n#endif\n\t\t\t\t\twantquit = true;\n\t\t\t\t}\n#endif\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase WM_ERASEBKGND:\n\t\t\tlRet = TRUE;\n\t\t\tbreak;\n/*\n\t\tcase WM_ACTIVATE:\n//\t\t\tfActive = LOWORD(wParam);\n//\t\t\tfMinimized = (BOOL) HIWORD(wParam);\n//\t\t\tif (!GLAppActivate(!(fActive == WA_INACTIVE), fMinimized))\n\t\t\t\tbreak;//so, urm, tell me microsoft, what changed?\n\t\t\tif (modestate == MS_FULLDIB)\n\t\t\t\tShowWindow(hWnd, SW_SHOWNORMAL);\n\n#ifdef WTHREAD\n#else\n\t\t// fix the leftover Alt from any Alt-Tab or the like that switched us away\n\t\t\tClearAllStates ();\t//FIXME: thread\n\n\t\t\tCvar_ForceCallback(&vid_conautoscale);\t//FIXME: thread\n#endif\n\t\t\tbreak;\n*/\n\t\tcase WM_DESTROY:\n\t\t\tbreak;\n\t\tcase WM_SETCURSOR:\n\t\t\t//only use a custom cursor if the cursor is inside the client area\n\t\t\tswitch(lParam&0xffff)\n\t\t\t{\n\t\t\tcase 0:\n\t\t\t\tbreak;\n\t\t\tcase HTCLIENT:\n\t\t\t\tif (hCustomCursor)\t//custom cursor enabled\n\t\t\t\t\tSetCursor(hCustomCursor);\n\t\t\t\telse\t\t\t\t//fallback on an arrow cursor, just so we have something visible at startup or so\n\t\t\t\t\tSetCursor(hArrowCursor);\n\t\t\t\tlRet = TRUE;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tlRet = DefWindowProcW (hWnd, uMsg, wParam, lParam);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase WM_DROPFILES:\n\t\t\t{\n\t\t\t\tHDROP p = (HDROP)wParam;\n\t\t\t\twchar_t fnamew[MAX_PATH];\n\t\t\t\tchar fname[MAX_PATH];\n\t\t\t\tvfsfile_t *f;\n\t\t\t\tint i, count = DragQueryFile(p, ~0, NULL, 0);\n\t\t\t\tfor(i = 0; i < count; i++)\n\t\t\t\t{\n\t\t\t\t\tif (WinNT)\n\t\t\t\t\t{\n\t\t\t\t\t\tDragQueryFileW(p, i, fnamew, countof(fnamew));\n\t\t\t\t\t\tnarrowen(fname, sizeof(fname), fnamew);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\tDragQueryFileA(p, i, fname, countof(fname));\n\t\t\t\t\tf = FS_OpenVFS(fname, \"rb\", FS_SYSTEM);\n\t\t\t\t\tif (f)\n\t\t\t\t\t\tHost_RunFile(fname, strlen(fname), f);\n\t\t\t\t}\n\t\t\t\tDragFinish(p);\n\t\t\t}\n\t\t\treturn 0;\t//An application should return zero if it processes this message.\n\n\t\tcase WM_SYSCOMMAND:\t//this only works when we're the active app. which is fine.\n\t\t\tif (GET_SC_WPARAM(wParam)==SC_SCREENSAVE)\n\t\t\t\tlRet = 0;\t//try to block sscreensavers, if enabled... will likely fail due to passwords and security stuff etc...\n\t\t\telse if (GET_SC_WPARAM(wParam)==SC_MONITORPOWER && (lParam == 1 || lParam == 2))\n\t\t\t\tlRet = 0;\t//try to block monitor power saving.\n\t\t\telse if (WinNT)\n\t\t\t\tlRet = DefWindowProcW (hWnd, uMsg, wParam, lParam);\n\t\t\telse\n\t\t\t\tlRet = DefWindowProcA (hWnd, uMsg, wParam, lParam);\n\t\t\tbreak;\n#ifdef HAVE_CDPLAYER\n\t\tcase MM_MCINOTIFY:\n#ifdef WTHREAD\n\t\t\tCOM_AddWork(WG_MAIN, MainThreadWndProc, (void*)wParam, (void*)lParam, uMsg, 0);\n\t\t\tlRet = 0;\n#else\n\t\t\tlRet = CDAudio_MessageHandler (hWnd, uMsg, wParam, lParam);\t//FIXME: thread\n#endif\n\t\t\tbreak;\n#endif\n\n\t\tdefault:\n\t\t\t/* pass all unhandled messages to DefWindowProc */\n\t\t\tif (WinNT)\n\t\t\t\tlRet = DefWindowProcW (hWnd, uMsg, wParam, lParam);\n\t\t\telse\n\t\t\t\tlRet = DefWindowProcA (hWnd, uMsg, wParam, lParam);\n\t\t\tbreak;\n\t}\n\n\t/* return 1 if handled message, 0 if not */\n\treturn lRet;\n}\n\n/*\nvoid VID_Init8bitPalette(void)\n{\n#ifdef GL_USE8BITTEX\n#ifdef GL_EXT_paletted_texture\n#define GL_SHARED_TEXTURE_PALETTE_EXT 0x81FB\n\n\t// Check for 8bit Extensions and initialize them.\n\tint i;\n\tchar thePalette[256*3];\n\tchar *oldPalette, *newPalette;\n\n\tqglColorTableEXT = (void *)qwglGetProcAddress(\"glColorTableEXT\");\n\tif (!qglColorTableEXT || !GL_CheckExtension(\"GL_EXT_shared_texture_palette\") || COM_CheckParm(\"-no8bit\"))\n\t\treturn;\n\n\tCon_SafePrintf(\"8-bit GL extensions enabled.\\n\");\n\tqglEnable(GL_SHARED_TEXTURE_PALETTE_EXT);\n\toldPalette = (char *) d_8to24rgbtable; //d_8to24table3dfx;\n\tnewPalette = thePalette;\n\tfor (i=0;i<256;i++)\n\t{\n\t\t*newPalette++ = *oldPalette++;\n\t\t*newPalette++ = *oldPalette++;\n\t\t*newPalette++ = *oldPalette++;\n\t\toldPalette++;\n\t}\n\tqglColorTableEXT(GL_SHARED_TEXTURE_PALETTE_EXT, GL_RGB, 256, GL_RGB, GL_UNSIGNED_BYTE,\n\t\t(void *) thePalette);\n\tis8bit = TRUE;\n\n#endif\n#endif\n}\n*/\n\nvoid GLVID_DeInit (void)\n{\n\tGLVID_Shutdown();\n\tvid.activeapp = false;\n\n\tCvar_Unhook(&vid_vsync);\n\tCvar_Unhook(&vid_wndalpha);\n\tCmd_RemoveCommand(\"vid_recenter\");\n}\n\n/*\n===================\nVID_Init\n===================\n*/\nqboolean Win32VID_Init (rendererstate_t *info, unsigned char *palette, int mode)\n{\n\textern int isPlugin;\n//\tqbyte\t*ptmp;\n\tDEVMODE\tdevmode;\n\n\tplatform_rendermode = mode;\n\n\tmemset(&devmode, 0, sizeof(devmode));\n\n\thIcon = LoadIcon (global_hInstance, MAKEINTRESOURCE (IDI_ICON1));\n\thArrowCursor = LoadCursor (NULL,IDC_ARROW);\n\n\trf->VID_CreateCursor = WIN_CreateCursor;\n\trf->VID_DestroyCursor = WIN_DestroyCursor;\n\trf->VID_SetCursor = WIN_SetCursor;\n\n\tvid_initialized = false;\n\tvid_initializing = true;\n\n\tif (!GLVID_SetMode (info, palette))\n\t{\n\t\tVID_UnSetMode();\n\t\treturn false;\n\t}\n\n\t// Check for 3DFX Extensions and initialize them.\n\t//VID_Init8bitPalette();\n\n\tvid_canalttab = true;\n\n\tCvar_Hook(&vid_vsync, VID_Wait_Override_Callback);\n\tCvar_Hook(&vid_wndalpha, VID_WndAlpha_Override_Callback);\n\n\tCmd_AddCommand(\"vid_recenter\", GLVID_Recenter_f);\n\n\tif (isPlugin >= 2)\n\t{\n\t\tfprintf(stdout, \"refocuswindow %\"PRIxPTR\"\\n\", (quintptr_t)mainwindow);\n\t\tfflush(stdout);\n\t}\n\n\tvid_initialized = true;\n\tvid_initializing = false;\n\n\tWIN_WindowCreated(mainwindow);\n\n\treturn true;\n}\n\n#ifdef USE_WGL\nqboolean GLVID_Init (rendererstate_t *info, unsigned char *palette)\n{\n\treturn Win32VID_Init(info, palette, MODE_WGL);\n}\n#endif\t//USE_WGL\n\n\n\n\n\n#ifdef USE_EGL\n\nstatic qboolean EGLVID_Init (rendererstate_t *info, unsigned char *palette)\n{\n\tif (!EGL_LoadLibrary(info->subrenderer))\n\t\treturn false;\n\treturn Win32VID_Init(info, palette, MODE_EGL);\n}\n\n\n\n#include \"shader.h\"\n#include \"gl_draw.h\"\nrendererinfo_t eglrendererinfo =\n{\n\t\"EGL(win32)\",\n\t{\n\t\t\"egl\"\n\t},\n\tQR_OPENGL,\n\n\tGLDraw_Init,\n\tGLDraw_DeInit,\n\n\tGL_UpdateFiltering,\n\tGL_LoadTextureMips,\n\tGL_DestroyTexture,\n\n\tGLR_Init,\n\tGLR_DeInit,\n\tGLR_RenderView,\n\n\tEGLVID_Init,\n\tGLVID_DeInit,\n\tGLVID_SwapBuffers,\n\tGLVID_ApplyGammaRamps,\n\n\tNULL,\n\tNULL,\n\tNULL,\n\tGLVID_SetCaption,       //setcaption\n\tGLVID_GetRGBInfo,\n\n\n\tGLSCR_UpdateScreen,\n\n\tGLBE_SelectMode,\n\tGLBE_DrawMesh_List,\n\tGLBE_DrawMesh_Single,\n\tGLBE_SubmitBatch,\n\tGLBE_GetTempBatch,\n\tGLBE_DrawWorld,\n\tGLBE_Init,\n\tGLBE_GenBrushModelVBO,\n\tGLBE_ClearVBO,\n\tGLBE_UpdateLightmaps,\n\tGLBE_SelectEntity,\n\tGLBE_SelectDLight,\n\tGLBE_Scissor,\n\tGLBE_LightCullModel,\n\n\tGLBE_VBO_Begin,\n\tGLBE_VBO_Data,\n\tGLBE_VBO_Finish,\n\tGLBE_VBO_Destroy,\n\n\tGLBE_RenderToTextureUpdate2d,\n\n\t\"\"\n};\n#endif\t//USE_EGL\n\n\n#ifdef VKQUAKE\nstatic qboolean VKVID_Init (rendererstate_t *info, unsigned char *palette)\n{\n\treturn Win32VID_Init(info, palette, MODE_VULKAN);\n}\n\nrendererinfo_t vkrendererinfo =\n{\n\t\"Vulkan\",\n\t{\n\t\t\"vk\",\n\t\t\"Vulkan\"\n\t},\n\tQR_VULKAN,\n\n\tVK_Draw_Init,\n\tVK_Draw_Shutdown,\n\n\tVK_UpdateFiltering,\n\tVK_LoadTextureMips,\n\tVK_DestroyTexture,\n\n\tVK_R_Init,\n\tVK_R_DeInit,\n\tVK_R_RenderView,\n\n\tVKVID_Init,\n\tGLVID_DeInit,\n\tGLVID_SwapBuffers,\n\tGLVID_ApplyGammaRamps,\n\tWIN_CreateCursor,\n\tWIN_SetCursor,\n\tWIN_DestroyCursor,\n\tGLVID_SetCaption,\n\tVKVID_GetRGBInfo,\n\n\tVK_SCR_UpdateScreen,\n\n\tVKBE_SelectMode,\n\tVKBE_DrawMesh_List,\n\tVKBE_DrawMesh_Single,\n\tVKBE_SubmitBatch,\n\tVKBE_GetTempBatch,\n\tVKBE_DrawWorld,\n\tVKBE_Init,\n\tVKBE_GenBrushModelVBO,\n\tVKBE_ClearVBO,\n\tVKBE_UploadAllLightmaps,\n\tVKBE_SelectEntity,\n\tVKBE_SelectDLight,\n\tVKBE_Scissor,\n\tVKBE_LightCullModel,\n\n\tVKBE_VBO_Begin,\n\tVKBE_VBO_Data,\n\tVKBE_VBO_Finish,\n\tVKBE_VBO_Destroy,\n\n\tVKBE_RenderToTextureUpdate2d,\n\n\t\"no more\",\n\tNULL,\t//getpriority\n\tNULL,\t//enummodes\n\tWin32VK_EnumerateDevices,\t//enumdevices\n};\n\n\n\n#ifdef USE_WGL\nstatic qboolean NVVKVID_Init (rendererstate_t *info, unsigned char *palette)\n{\n\treturn Win32VID_Init(info, palette, MODE_NVVULKAN);\n}\nrendererinfo_t nvvkrendererinfo =\n{\n\t\"Vulkan via OpenGL (GL_NV_draw_vulkan_image)\",\n\t{\n\t\t\"nvvk\",\n\t\t\"GL_NV_draw_vulkan_image\",\n\t},\n\tQR_VULKAN,\n\n\tVK_Draw_Init,\n\tVK_Draw_Shutdown,\n\n\tVK_UpdateFiltering,\n\tVK_LoadTextureMips,\n\tVK_DestroyTexture,\n\n\tVK_R_Init,\n\tVK_R_DeInit,\n\tVK_R_RenderView,\n\n\tNVVKVID_Init,\n\tGLVID_DeInit,\n\tGLVID_SwapBuffers,\n\tGLVID_ApplyGammaRamps,\n\tWIN_CreateCursor,\n\tWIN_SetCursor,\n\tWIN_DestroyCursor,\n\tGLVID_SetCaption,\n\tVKVID_GetRGBInfo,\n\n\tVK_SCR_UpdateScreen,\n\n\tVKBE_SelectMode,\n\tVKBE_DrawMesh_List,\n\tVKBE_DrawMesh_Single,\n\tVKBE_SubmitBatch,\n\tVKBE_GetTempBatch,\n\tVKBE_DrawWorld,\n\tVKBE_Init,\n\tVKBE_GenBrushModelVBO,\n\tVKBE_ClearVBO,\n\tVKBE_UploadAllLightmaps,\n\tVKBE_SelectEntity,\n\tVKBE_SelectDLight,\n\tVKBE_Scissor,\n\tVKBE_LightCullModel,\n\n\tVKBE_VBO_Begin,\n\tVKBE_VBO_Data,\n\tVKBE_VBO_Finish,\n\tVKBE_VBO_Destroy,\n\n\tVKBE_RenderToTextureUpdate2d,\n\n\t\"no more\",\n\tNULL,\n\tNULL,\n\tNULL,\t//reminder that this is the OPENGL device to init.\n};\n#endif\n#endif\n\n#endif\n"
  },
  {
    "path": "engine/gl/gl_vidnull.c",
    "content": "#include \"quakedef.h\"\n\nqboolean GLVID_Init(rendererstate_t *info, unsigned char *palette)\n{\n\treturn false;\n}\n\nvoid GLVID_DeInit()\n{\n}\n\nvoid GLVID_ShiftPalette(unsigned char *p)\n{\n}\n\nvoid GLVID_SetPalette(unsigned char *palette)\n{\n}\n\nvoid Sys_SendKeyEvents(void)\n{\n}\n\nvoid GLD_BeginDirectRect(int x, int y, qbyte *pbitmap, int width, int height)\n{\n}\n\nvoid GLD_EndDirectRect(int x, int y, int width, int height)\n{\n}\n\nvoid GLVID_SwapBuffers(void)\n{\n}\n\nvoid GLVID_SetCaption(char *text)\n{\n}\n\n/*** Input ***/\n\nvoid IN_ReInit(void)\n{\n}\n\nvoid IN_Init(void)\n{\n}\n\nvoid IN_Shutdown(void)\n{\n}\n\nvoid IN_Commands(void)\n{\n}\n\nvoid IN_Move(float *movements, int pnum)\n{\n}\n\n"
  },
  {
    "path": "engine/gl/gl_vidrpi.c",
    "content": "#include \"bothdefs.h\"\n#if defined(GLQUAKE) && defined(USE_EGL)\n#include \"gl_videgl.h\"\n\n#include <bcm_host.h>\n#include \"glquake.h\"\n#include \"shader.h\"\nqboolean RPI_Init (rendererstate_t *info, unsigned char *palette)\n{\n\tstatic EGL_DISPMANX_WINDOW_T nativewindow;\n\n\tDISPMANX_ELEMENT_HANDLE_T dispman_element;\n\tDISPMANX_DISPLAY_HANDLE_T dispman_display;\n\tDISPMANX_UPDATE_HANDLE_T dispman_update;\n\tVC_RECT_T dst_rect;\n\tVC_RECT_T src_rect;\n\tint rw, rh;\n\n\tif (!EGL_LoadLibrary(info->subrenderer))\n\t{\n\t\tCon_Printf(\"couldn't load EGL library\\n\");\n\t\treturn false;\n\t}\n\tbcm_host_init();\n\n\tgraphics_get_display_size(0 /* LCD */, &rw, &rh);\n\tCon_Printf(\"Screen size is actually %i*%i\\n\", rw, rh);\n\n\tif (info->width < 64 || info->height < 64)\n\t{\n\t\tinfo->width = rw;\n\t\tinfo->height = rh;\n\t}\n\tdispman_display = vc_dispmanx_display_open(0 /* LCD */);\n\tdispman_update = vc_dispmanx_update_start(0);\n\n\n\tdst_rect.x = 0;\n\tdst_rect.y = 0;\n\tdst_rect.width = info->width;\n\tdst_rect.height = info->height;\n\t  \n\tsrc_rect.x = 0;\n\tsrc_rect.y = 0;\n\tsrc_rect.width = info->width << 16;\n\tsrc_rect.height = info->height << 16; \n\n\tvid.pixelwidth = info->width;\n\tvid.pixelheight = info->height;\n\t \n\tdispman_element = vc_dispmanx_element_add(dispman_update, dispman_display, 0/*layer*/, &dst_rect, 0/*src*/, &src_rect, DISPMANX_PROTECTION_NONE, 0 /*alpha*/, 0/*clamp*/, 0/*transform*/);\n  \n\tnativewindow.element = dispman_element;\n\tnativewindow.width = info->width;\n\tnativewindow.height = info->height;\n\tvc_dispmanx_update_submit_sync(dispman_update);\n\n\n\tif (!EGL_Init(info, palette, &nativewindow, EGL_DEFAULT_DISPLAY))\n\t{\n\t\tCon_Printf(\"couldn't initialise EGL context\\n\");\n\t\treturn false;\n\t}\n\tGL_Init(&EGL_Proc);\n\treturn true;\n}\nvoid RPI_DeInit(void)\n{\n\tEGL_Shutdown();\n}\nqboolean RPI_ApplyGammaRamps(unsigned int gammarampsize, unsigned short *ramps)\n{\n\t//not supported\n\treturn false;\n}\nvoid RPI_SetCaption(char *text)\n{\n}\n\n#include \"gl_draw.h\"\nrendererinfo_t rpirendererinfo =\n{\n    \"EGL(VideoCore)\",\n    {\n        \"rpi\",\n\t\t\"videocore\",\n\t\t\"rpiegl\"\n    },\n    QR_OPENGL,\n\n    GLDraw_Init,\n    GLDraw_DeInit,\n\n\tGL_UpdateFiltering,\r\n\tGL_LoadTextureMips,\r\n\tGL_DestroyTexture,\n\n    GLR_Init,\n    GLR_DeInit,\n    GLR_RenderView,\n\n    RPI_Init,\n    RPI_DeInit,\n    RPI_ApplyGammaRamps,\n    GLVID_GetRGBInfo,\n\n    RPI_SetCaption,       //setcaption\n\n\n    GLSCR_UpdateScreen,\n\n    GLBE_SelectMode,\n    GLBE_DrawMesh_List,\n    GLBE_DrawMesh_Single,\n    GLBE_SubmitBatch,\n    GLBE_GetTempBatch,\n    GLBE_DrawWorld,\n    GLBE_Init,\n    GLBE_GenBrushModelVBO,\n    GLBE_ClearVBO,\n    GLBE_UploadAllLightmaps,\n    GLBE_SelectEntity,\n    GLBE_SelectDLight,\n    GLBE_Scissor,\n    GLBE_LightCullModel,\n\n    GLBE_VBO_Begin,\n    GLBE_VBO_Data,\n    GLBE_VBO_Finish,\n    GLBE_VBO_Destroy,\n\n    GLBE_RenderToTextureUpdate2d,\n\n    \"\"\n};\n\n#endif\n\n"
  },
  {
    "path": "engine/gl/gl_vidsdl.c",
    "content": "#include \"quakedef.h\"\n\n#ifdef FTE_SDL3\n\t#include <SDL3/SDL.h>\n#else\n\t#include <SDL.h>\n\t#include <SDL_syswm.h>\n#endif\n\n#ifdef GLQUAKE\n\t#include \"glquake.h\"\n\t#define OPENGL_SDL\n#endif\n\n#if SDL_VERSION_ATLEAST(2,0,0)\n\t#if SDL_VERSION_ATLEAST(2,0,6)\n\t\t#ifdef VKQUAKE\n\t\t\t#if SDL_VERSION_ATLEAST(3,0,0)\n\t\t\t\t#include <SDL3/SDL_vulkan.h>\n\t\t\t#else\n\t\t\t\t#include <SDL_vulkan.h>\n\t\t\t#endif\n\t\t\t#include \"../vk/vkrenderer.h\"\n\t\t\t#define VULKAN_SDL\n\t\t#endif\n\t#endif\n\tSDL_Window *sdlwindow;\n\t#ifdef OPENGL_SDL\n\t\t#if SDL_VERSION_ATLEAST(3,0,0)\n\t\t\tstatic SDL_GLContext sdlcontext;\n\t\t#else\n\t\t\tstatic SDL_GLContext *sdlcontext;\n\t\t#endif\n\t#endif\n#else\n\tSDL_Surface *sdlsurf;\n#endif\nvoid INS_SetOSK(int osk);\n\n#include \"vr.h\"\n\nextern cvar_t\t\tvid_vsync;\nextern cvar_t vid_hardwaregamma;\nextern cvar_t gl_lateswap;\nextern cvar_t vid_gl_context_version;\nextern cvar_t vid_gl_context_debug;\nextern cvar_t vid_gl_context_forwardcompatible;\nextern cvar_t vid_gl_context_es;\nextern cvar_t vid_gl_context_compatibility;\nextern qboolean gammaworks;\n\n#ifdef _WIN32\t//half the rest of the code uses windows apis to focus windows. Should be fixed, but it's not too important.\nHWND mainwindow;\n#endif\n\nextern qboolean vid_isfullscreen;\n\n#if !SDL_VERSION_ATLEAST(2,0,0)\nunsigned short intitialgammaramps[3][256];\n#endif\n\nextern qboolean mouseactive;\nextern qboolean mouseusedforgui;\n\n#ifdef OPENGL_SDL\nstatic void *GLVID_getsdlglfunction(char *functionname)\n{\n#ifdef GL_STATIC\n\t//this reduces dependancies in the webgl build (removing warnings about emulation being poo)\n\treturn NULL;\n#else\n\treturn SDL_GL_GetProcAddress(functionname);\n#endif\n}\n#endif\n\n#if SDL_VERSION_ATLEAST(2,0,0)\n#if SDL_VERSION_ATLEAST(3,0,0)\nstatic SDL_Surface *SDL_CreateRGBSurfaceFrom(void *pixels, int width, int height, int depth, int pitch, Uint32 Rmask, Uint32 Gmask, Uint32 Bmask, Uint32 Amask)\n{\n    return SDL_CreateSurfaceFrom(width, height,\n                                 SDL_GetPixelFormatForMasks(depth, Rmask, Gmask, Bmask, Amask),\n                                 pixels, pitch);\n}\n#undef SDL_FreeSurface\n#define SDL_FreeSurface SDL_DestroySurface\n#undef SDL_FreeCursor\n#define SDL_FreeCursor SDL_DestroyCursor\n#endif\n\nvoid *GLVID_CreateCursor\t\t\t(const qbyte *imagedata, int width, int height, uploadfmt_t format, float hotx, float hoty, float scale)\n{\n\tSDL_Cursor *curs;\n\tSDL_Surface *surf;\n\tUint32 r,g,b,a;\n\tif (!imagedata)\n\t\treturn NULL;\n\tswitch(format)\n\t{\n#if SDL_BYTEORDER == SDL_BIG_ENDIAN\n\tcase PTI_LLLX8:\n\tcase PTI_RGBX8:\n\t\tr = 0xff000000;\n\t\tg = 0x00ff0000;\n\t\tb = 0x0000ff00;\n\t\ta = 0x00000000;\n\t\tbreak;\n\tcase PTI_LLLA8:\n\tcase PTI_RGBA8:\n\t\tr = 0xff000000;\n\t\tg = 0x00ff0000;\n\t\tb = 0x0000ff00;\n\t\ta = 0x000000ff;\n\t\tbreak;\n\tcase PTI_BGRX8:\n\t\tb = 0xff000000;\n\t\tg = 0x00ff0000;\n\t\tr = 0x0000ff00;\n\t\ta = 0x00000000;\n\t\tbreak;\n\tcase PTI_BGRA8:\n\t\tb = 0xff000000;\n\t\tg = 0x00ff0000;\n\t\tr = 0x0000ff00;\n\t\ta = 0x000000ff;\n\t\tbreak;\n#else\n\tcase PTI_LLLX8:\n\tcase PTI_RGBX8:\n\t\tr = 0x000000ff;\n\t\tg = 0x0000ff00;\n\t\tb = 0x00ff0000;\n\t\ta = 0x00000000;\n\t\tbreak;\n\tcase PTI_LLLA8:\n\tcase PTI_RGBA8:\n\t\tr = 0x000000ff;\n\t\tg = 0x0000ff00;\n\t\tb = 0x00ff0000;\n\t\ta = 0xff000000;\n\t\tbreak;\n\tcase PTI_BGRX8:\n\t\tb = 0x000000ff;\n\t\tg = 0x0000ff00;\n\t\tr = 0x00ff0000;\n\t\ta = 0x00000000;\n\t\tbreak;\n\tcase PTI_BGRA8:\n\t\tb = 0x000000ff;\n\t\tg = 0x0000ff00;\n\t\tr = 0x00ff0000;\n\t\ta = 0xff000000;\n\t\tbreak;\n#endif\n\tcase PTI_A2BGR10:\n\t\tr = 0x000003ff;\n\t\tg = 0x000ffc00;\n\t\tb = 0x3ff00000;\n\t\ta = 0xc0000000;\n\t\tbreak;\n\n\tdefault:\n\t\treturn NULL;\n\t}\n\n\tif (scale != 1)\n\t{\n\t\tint nw,nh;\n\t\tqbyte *nd;\n\t\tnw = width * scale;\n\t\tnh = height * scale;\n\t\tif (nw <= 0 || nh <= 0 || nw > 128 || nh > 128)\t//don't go crazy.\n\t\t\treturn NULL;\n\n\t\tnd = Image_ResampleTexture(format, imagedata, width, height, NULL, nw, nh);\n\n\t\tsurf = SDL_CreateRGBSurfaceFrom(nd, nw, nh, 32, nw*4, r, g, b, a);\n\t\tcurs = SDL_CreateColorCursor(surf, hotx, hoty);\n\t\tSDL_FreeSurface(surf);\n\n\t\tBZ_Free(nd);\n\t}\n\telse\n\t{\n\t\tsurf = SDL_CreateRGBSurfaceFrom((void*)imagedata, width, height, 32, width*4, r, g, b, a);\n\t\tcurs = SDL_CreateColorCursor(surf, hotx, hoty);\n\t\tSDL_FreeSurface(surf);\n\t}\n\treturn curs;\n}\nqboolean GLVID_SetCursor\t\t\t(void *cursor)\n{\n\tSDL_SetCursor(cursor);\n\treturn !!cursor;\n}\nvoid GLVID_DestroyCursor\t\t\t(void *cursor)\n{\n\tSDL_FreeCursor(cursor);\n}\n\n\nstatic void GLVID_SetIcon\t\t\t\t(void)\n{\n\tSDL_Surface *iconsurf = NULL;\n\tsize_t filesize = 0;\n\tqbyte *filedata = NULL;\n\tqbyte *imagedata = NULL;\n\n\t#ifdef IMAGEFMT_PNG\n\tif (!filedata)\n\t\tfiledata = FS_LoadMallocFile(\"icon.png\", &filesize);\n\t#endif\n\tif (!filedata)\n\t\tfiledata = FS_LoadMallocFile(\"icon.tga\", &filesize);\n\t#ifdef IMAGEFMT_JPG\n\tif (!filedata)\n\t\tfiledata = FS_LoadMallocFile(\"icon.jpg\", &filesize);\n\t#endif\n\t#ifdef IMAGEFMT_BMP\n\tif (!filedata)\n\t\tfiledata = FS_LoadMallocFile(\"icon.ico\", &filesize);\n\t#endif\n\t#ifdef HAVE_LEGACY\n\tif (!filedata)\n\t\tfiledata = FS_LoadMallocFile(\"darkplaces-icon.tga\", &filesize);\n\t#endif\n\n\tif (filedata)\n\t{\n\t\tint imagewidth, imageheight;\n\t\tuploadfmt_t format = PTI_INVALID;\n\t\timagedata = ReadRawImageFile(filedata, filesize, &imagewidth, &imageheight, &format, true, \"icon.*\");\n\t\tZ_Free(filedata);\n\n\t\tif (imagedata)\n\t\t{\n\t\t\t/* hopefully SDL can resize as appropriate\n\t\t\tqbyte *resized = Image_ResampleTexture(format, imagedata, imagewidth, imageheight, NULL, 64, 64);\n\t\t\tif (resized)\n\t\t\t{\n\t\t\t\tZ_Free(imagedata);\n\t\t\t\timagedata = resized;\n\t\t\t\timagewidth = 64;\n\t\t\t\timageheight = 64;\n\t\t\t}*/\n\t\t\tswitch(format)\n\t\t\t{\n\t\t\tcase PTI_LLLA8:\t\t//fallthrough\n\t\t\tcase PTI_RGBA8:\t\ticonsurf = SDL_CreateRGBSurfaceFrom(imagedata, imagewidth, imageheight, 32, 4*imagewidth, 0x000000FF, 0x0000FF00, 0x00FF0000, 0xFF000000); break;\n\t\t\tcase PTI_LLLX8:\t\t//fallthrough\n\t\t\tcase PTI_RGBX8:\t\ticonsurf = SDL_CreateRGBSurfaceFrom(imagedata, imagewidth, imageheight, 32, 4*imagewidth, 0x000000FF, 0x0000FF00, 0x00FF0000, 0x00000000); break;\n\t\t\tcase PTI_BGRA8:\t\ticonsurf = SDL_CreateRGBSurfaceFrom(imagedata, imagewidth, imageheight, 32, 4*imagewidth, 0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000); break;\n\t\t\tcase PTI_BGRX8:\t\ticonsurf = SDL_CreateRGBSurfaceFrom(imagedata, imagewidth, imageheight, 32, 4*imagewidth, 0x00FF0000, 0x0000FF00, 0x000000FF, 0x00000000); break;\n\t\t\tcase PTI_A2BGR10:\ticonsurf = SDL_CreateRGBSurfaceFrom(imagedata, imagewidth, imageheight, 32, 4*imagewidth, 0x3FF00000, 0x000FFC00, 0x000003FF, 0xC0000000); break;\n\t\t\tdefault:\t//others shouldn't happen.\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\tif (!iconsurf)\n\t{\n\t\t#include \"fte_eukara64.h\"\n//\t\t#include \"bymorphed.h\"\n\t\ticonsurf = SDL_CreateRGBSurfaceFrom((void*)icon.pixel_data, icon.width, icon.height, 32, 4*icon.width, 0x000000FF, 0x0000FF00, 0x00FF0000, 0xFF000000);\t//RGBA byte order on a little endian machine, at least...\n\t}\n\tSDL_SetWindowIcon(sdlwindow, iconsurf);\n\tSDL_FreeSurface(iconsurf);\n\tZ_Free(imagedata);\n}\n\n#if SDL_VERSION_ATLEAST(3,0,0)\n//converts an output device name/number to an index.\n//So we can use eg VGA-0 on linux and get the proper device.\nstatic SDL_DisplayID SDLVID_GetVideoDevice(const char *devicename)\n{\n\tchar *end;\n\tint numdisps = 0;\n\tSDL_DisplayID *displays = SDL_GetDisplays(&numdisps);\n\tSDL_DisplayID ret = 0;\t//invalid\n\tint idx = strtol(devicename, &end, 0);\n\tif (*end)\n\t{\t//okay, so its not purely a number. scan by name\n\t\tfor (idx = 0; idx < numdisps; idx++)\n\t\t{\n\t\t\tconst char *dname = SDL_GetDisplayName(displays[idx]);\n\t\t\tif (dname && !Q_strcasecmp(dname, devicename))\n\t\t\t{\n\t\t\t\tret = displays[idx];\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\telse\n\t{\n\t\tif (idx < 0 || idx >= numdisps)\n\t\t\tret = 0;\t//invalid.\n\t\telse\n\t\t\tret = displays[idx];\n\t}\n\tif (!ret)\n\t\tret = SDL_GetPrimaryDisplay();\t//FIXME: this sucks. should get the device that currently holds the cursor or w/e, so we don't piss off the user by showing it on device instead of the one with their task bar/shortcut/etc. sdl seems to be catering to wayland and doesn't seem to have any GetCursorPos (other than post-window-creation events which makes it useless).\n\tSDL_free(displays);\n\treturn ret;\n}\nstatic qboolean SDLVID_GetVideoMode(rendererstate_t *info, SDL_DisplayID *display, SDL_DisplayMode *mode)\n{\n\t*display = SDLVID_GetVideoDevice(info->devicename);\n\tif (info->fullscreen != 1)\n\t\treturn false;\n\n\tif (SDL_GetClosestFullscreenDisplayMode(*display, info->width, info->height, info->rate, true, mode))\n\t{\n\t\tinfo->width = mode->w;\n\t\tinfo->height = mode->h;\n\t\treturn true;\t//yay\n\t}\n\treturn false;\t//fail\n}\nstatic void\tSDLVID_EnumerateVideoModes (const char *driver, const char *output, void (*cb) (int w, int h))\n{\n\tint nummodes, m;\n\tSDL_DisplayID display = SDLVID_GetVideoDevice(output);\n\tSDL_DisplayMode **modes = SDL_GetFullscreenDisplayModes(display, &nummodes);\n\tfor (m = 0; m < nummodes; m++)\n\t\tcb(modes[m]->w, modes[m]->h);\n\tSDL_free(modes);\n}\n#else\n//converts an output device name/number to an index.\n//So we can use eg VGA-0 on linux and get the proper device.\nstatic int SDLVID_GetVideoDevice(const char *devicename)\n{\n\tchar *end;\n\tint display = strtol(devicename, &end, 0);\n\tif (*end)\n\t{\t//okay, so its not purely a number. scan by name\n\t\tdisplay = SDL_GetNumVideoDisplays();\n\t\tif (display)\n\t\t{\n\t\t\twhile (display --> 0)\n\t\t\t{\n\t\t\t\tconst char *dname = SDL_GetDisplayName(display);\n\t\t\t\tif (dname && !Q_strcasecmp(dname, devicename))\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\telse\n\t{\n\t\tif (display < 0 || display >= SDL_GetNumVideoDisplays())\n\t\t\tdisplay = 0;\n\t}\n\treturn display;\n}\nstatic qboolean SDLVID_GetVideoMode(rendererstate_t *info, int *display, SDL_DisplayMode *mode)\n{\n\tSDL_DisplayMode targ;\n\t*display = SDLVID_GetVideoDevice(info->devicename);\n\tif (info->fullscreen != 1)\n\t\treturn false;\n\ttarg.w = info->width;\n\ttarg.h = info->height;\n\tif (info->bpp == 30)\n\t\ttarg.format = SDL_PIXELFORMAT_ARGB2101010;\n\telse\n\t\ttarg.format = SDL_PIXELFORMAT_UNKNOWN;\n\ttarg.driverdata = NULL;\n\ttarg.refresh_rate = info->rate;\n\n\tif (SDL_GetClosestDisplayMode(*display, &targ, mode))\n\t{\n\t\tinfo->width = targ.w;\n\t\tinfo->height = targ.h;\n\t\treturn true;\t//yay\n\t}\n\treturn false;\t//fail\n}\nstatic void\tSDLVID_EnumerateVideoModes (const char *driver, const char *output, void (*cb) (int w, int h))\n{\n\tSDL_DisplayMode modeinfo;\n\tint modes, m;\n\tint display = SDLVID_GetVideoDevice(output);\n\tmodes = SDL_GetNumDisplayModes(display);\n\tfor (m = 0; m < modes; m++)\n\t{\n\t\tif (0==SDL_GetDisplayMode(display, m, &modeinfo))\n\t\t\tcb(modeinfo.w, modeinfo.h);\n\t}\n}\n#endif\n#endif\n\n\nstatic qboolean SDLVID_Init (rendererstate_t *info, unsigned char *palette, r_qrenderer_t qrenderer)\n{\n\tint flags = 0;\n#if SDL_VERSION_ATLEAST(2,0,0)\n\tint display = -1;\n\tSDL_DisplayMode modeinfo, *usemode;\n\n\tSDL_SetHint(SDL_HINT_TOUCH_MOUSE_EVENTS, \"0\");\t\t//we understand touch events. we do NOT want to get confused with mouse motion constantly warping.\n#ifdef SDL_HINT_IME_INTERNAL_EDITING\n\tSDL_SetHint(SDL_HINT_IME_INTERNAL_EDITING, \"1\");\t//our code doesn't handle displaying non-committed text. ask to not be expected to show it, where possible.\n#endif\n#ifdef SDL_HINT_IME_SUPPORT_EXTENDED_TEXT\n//\tSDL_SetHint(SDL_HINT_IME_SUPPORT_EXTENDED_TEXT, \"1\");//says we don't have a limit. enable once we actually support this stuff.\n#endif\n#ifdef SDL_HINT_IME_SHOW_UI\n\tSDL_SetHint(SDL_HINT_IME_SHOW_UI, \"1\");\t\t\t\t//not much point having an IME if you can't see it...\n#endif\n#endif\n\n#if SDL_VERSION_ATLEAST(3,0,0)\n\tSDL_InitSubSystem(SDL_INIT_VIDEO);\n#else\n\tSDL_Init(SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE);\n#endif\n#if !defined(FTE_TARGET_WEB) && !SDL_VERSION_ATLEAST(2,0,0)\n\tSDL_SetVideoMode(0, 0, 0, 0);\t//to get around some SDL bugs\n#endif\n\n#if SDL_VERSION_ATLEAST(2,0,0)\n\tswitch(qrenderer)\n\t{\n\tdefault:\n\t\treturn false;\n#ifdef OPENGL_SDL\n\tcase QR_OPENGL:\n\t\tSDL_GL_LoadLibrary(NULL);\n\n\t\tif (info->bpp >= 32)\n\t\t{\n\t\t\tSDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);\n\t\t\tSDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);\n\t\t\tSDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);\n\t\t\tSDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 0);\n\t\t\tSDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);\t//technically we don't always need stencil support.\n\t\t}\n\t\telse\n\t\t{\n\t\t\tSDL_GL_SetAttribute(SDL_GL_RED_SIZE, 5);\n\t\t\tSDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 5);\n\t\t\tSDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 5);\n\t\t\tSDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 0);\n\t\t\tSDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 0);\n\t\t}\n\t\tSDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16);\n\t\tSDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);\n\n\t\tif (info->stereo)\n\t\t\tSDL_GL_SetAttribute(SDL_GL_STEREO, 1);\n\n\t\tif (info->srgb)\n\t\t\tSDL_GL_SetAttribute(SDL_GL_FRAMEBUFFER_SRGB_CAPABLE, 1);\n\n\t\t//FIXME: this stuff isn't part of info.\n\t\t//this means it shouldn't be exposed to the menu or widely advertised.\n\t\tif (*vid_gl_context_version.string)\n\t\t{\n\t\t\tint major, minor;\n\t\t\tchar *ver = vid_gl_context_version.string;\n\t\t\tmajor = strtoul(ver, &ver, 10);\n\t\t\tif (*ver == '.')\n\t\t\t{\n\t\t\t\tver++;\n\t\t\t\tminor = strtoul(ver, &ver, 10);\n\t\t\t}\n\t\t\telse\n\t\t\t\tminor = 0;\n\t\t\tSDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, major);\n\t\t\tSDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, minor);\n\t\t}\n\t\tSDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS,\n\t\t\t\t(vid_gl_context_debug.ival?SDL_GL_CONTEXT_DEBUG_FLAG:0) |\n\t\t\t\t(vid_gl_context_forwardcompatible.ival?SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG:0) |\n\t\t\t\t0);\n\n\t\tif (vid_gl_context_es.ival)\n\t\t\tSDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);\n\t\telse if (vid_gl_context_compatibility.ival)\n\t\t\tSDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_COMPATIBILITY);\n\t\telse\n\t\t\tSDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);\n\t\tif (info->multisample)\n\t\t{\n\t\t\tSDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, info->multisample);\n\t\t\tSDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1);\n\t\t}\n\n\t\tflags |= SDL_WINDOW_OPENGL;\n\t\tbreak;\n#endif\n#ifdef VULKAN_SDL\n\tcase QR_VULKAN:\t\t\n\t\tflags |= SDL_WINDOW_VULKAN;\n\t\tbreak;\n#endif\n\t}\n\tflags |= SDL_WINDOW_RESIZABLE;\n#if SDL_VERSION_ATLEAST(3,0,0)\n//\tflags |= SDL_WINDOW_MOUSE_GRABBED;\n\tflags |= SDL_WINDOW_HIGH_PIXEL_DENSITY;\n\tflags |= SDL_WINDOW_HIDDEN;\n#else\n\tflags |= SDL_WINDOW_INPUT_GRABBED;\n\t#if SDL_VERSION_ATLEAST(2,0,1)\n\t\tflags |= SDL_WINDOW_ALLOW_HIGHDPI;\n\t#endif\n#endif\n\n\tusemode = NULL;\n\tif (SDLVID_GetVideoMode(info, &display, &modeinfo))\n\t\tusemode = &modeinfo;\n\trf->VID_EnumerateVideoModes = SDLVID_EnumerateVideoModes;\n\n#if SDL_VERSION_ATLEAST(3,0,0)\n\tsdlwindow = SDL_CreateWindow(FULLENGINENAME, info->width, info->height, flags);\n#else\n\tsdlwindow = SDL_CreateWindow(FULLENGINENAME, SDL_WINDOWPOS_CENTERED_DISPLAY(display), SDL_WINDOWPOS_CENTERED_DISPLAY(display), info->width, info->height, flags);\n#endif\n\tif (!sdlwindow)\n\t{\n\t\tCon_Printf(\"SDL_CreateWindow failed: %s\\n\", SDL_GetError());\n\t\treturn false;\n\t}\n\n\tSDL_SetWindowMinimumSize(sdlwindow, 320, 200);\n\n\tif (usemode)\n\t{\n#if SDL_VERSION_ATLEAST(3,0,0)\n\t\tSDL_SetWindowFullscreenMode(sdlwindow, usemode);\n#else\n\t\tSDL_SetWindowDisplayMode(sdlwindow, usemode);\n#endif\n\t\tSDL_SetWindowFullscreen(sdlwindow, SDL_WINDOW_FULLSCREEN);\n\t}\n\telse if (info->fullscreen)\n\t{\n#if SDL_VERSION_ATLEAST(3,0,0)\n\t\tSDL_SetWindowFullscreen(sdlwindow, SDL_WINDOW_FULLSCREEN);\t//SDL decides itself now.\n#else\n\t\tSDL_SetWindowFullscreen(sdlwindow, SDL_WINDOW_FULLSCREEN_DESKTOP);\n#endif\n\t}\n\tSDL_ShowWindow(sdlwindow);\n\n#if defined(__linux__) && !SDL_VERSION_ATLEAST(3,0,0)\n\tif (usemode)\n\t{\t//try to work around an nvidia bug (panning being forced active, instead of disabled).\n\t\t//if the user pans then they might get stuck with x11 at the wrong resolution, so try to back out if it would fail.\n\t\tint w, h;\n\t\tSDL_GetWindowSize(sdlwindow, &w, &h);\n\t\tSys_SendKeyEvents();\n\t\tSDL_GetWindowSize(sdlwindow, &vid.pixelwidth, &vid.pixelheight);\n\t\tif (w != vid.pixelwidth || h != vid.pixelheight)\n\t\t{\n\t\t\tCon_Printf(CON_ERROR \"Video mode change didn't stick (Nvidia bug?). Resorting to fullscreen-desktop mode.\\n\");\n\t\t\tSDL_SetWindowFullscreen(sdlwindow, SDL_WINDOW_FULLSCREEN_DESKTOP);\n\t\t\tSDL_ShowWindow(sdlwindow);\n\t\t}\n\t}\n#endif\n\n\tCL_UpdateWindowTitle();\n\tGLVID_SetIcon();\n\n#if SDL_VERSION_ATLEAST(2,26,0)\n\tSDL_GetWindowSizeInPixels(sdlwindow, &vid.pixelwidth, &vid.pixelheight);\n#else\n\tswitch(qrenderer)\n\t{\n#ifdef OPENGL_SDL\n#if SDL_VERSION_ATLEAST(2,0,1)\n\tcase QR_OPENGL:\n\t\tSDL_GL_GetDrawableSize(sdlwindow, &vid.pixelwidth, &vid.pixelheight);\t//get the proper physical size.\n\t\tbreak;\n#endif\n#endif\n#ifdef VULKAN_SDL\n\tcase QR_VULKAN:\n\t\tSDL_Vulkan_GetDrawableSize(sdlwindow, &vid.pixelwidth, &vid.pixelheight);\n\t\tbreak;\n#endif\n\tdefault:\n\t\tSDL_GetWindowSize(sdlwindow, &vid.pixelwidth, &vid.pixelheight);\n\t\tbreak;\n\t}\n#endif\n\n#if SDL_VERSION_ATLEAST(3,0,0)\n\tvid.dpi_x = vid.dpi_y = SDL_GetWindowDisplayScale(sdlwindow)*96;\n//#elif SDL_VERSION_ATLEAST(2,0,4)\n//\tSDL_GetDisplayDPI(display, NULL, &vid.dpi_x, &vid.dpi_y);\t//unusable rubbish.\n#else\n\t{\t//SDL_GetDisplayDPI is unusable (depends on buggy drivers/hardware), so try and guess based on initial settings. this may cause issues with monitors using different scales though.\n\t\tint ww,wh;\n\t\tSDL_GetWindowSize(sdlwindow, &ww, &wh);\n\t\tvid.dpi_x = (96 * vid.pixelwidth)/ww;\n\t\tvid.dpi_y = (96 * vid.pixelheight)/wh;\n\t}\n#endif\n\n\n#ifdef OPENGL_SDL\n\tif (qrenderer == QR_OPENGL)\n\t{\n\t\tint srgb;\n#if\tSDL_VERSION_ATLEAST(3,0,0)\n\t\tvrsetup_t setup = {sizeof(setup)};\n\t\tconst char *driver = SDL_GetCurrentVideoDriver();\n\t\tif (SDL_strcmp(driver, \"x11\") == 0)\n\t\t\tsetup.vrplatform = VR_X11_GLX;\n\t\telse if (SDL_strcmp(driver, \"wayland\") == 0)\n\t\t\tsetup.vrplatform = VR_EGL;\n\t\telse if (SDL_strcmp(driver, \"windows\") == 0)\n\t\t\tsetup.vrplatform = VR_WIN_WGL;\n\t\telse\n\t\t\tsetup.vrplatform = VR_HEADLESS;\t//unsupported.\n#else\n\t\tvrsetup_t setup = {sizeof(setup)};\n\t\tSDL_SysWMinfo wminfo;\n\t\tSDL_VERSION(&wminfo.version);\n\t\tSDL_GetWindowWMInfo(sdlwindow, &wminfo);\n\t\tswitch(wminfo.subsystem)\n\t\t{\n#if defined(SDL_VIDEO_DRIVER_WINDOWS)\n\t\tcase SDL_SYSWM_WINDOWS:\n\t\t\tsetup.vrplatform = VR_WIN_WGL;\n\t\t\tbreak;\n#endif\n#if defined(SDL_VIDEO_DRIVER_X11)\n\t\tcase SDL_SYSWM_X11:\n\t\t\tsetup.vrplatform = VR_X11_GLX;\n\t\t\tbreak;\n#endif\n#if defined(SDL_VIDEO_DRIVER_WAYLAND) && SDL_VERSION_ATLEAST(3,0,0)\n\t\tcase SDL_SYSWM_WAYLAND:\n\t\t\tsetup.vrplatform = VR_EGL;\n\t\t\tbreak;\n#endif\n\t\tdefault:\n\t\t\tsetup.vrplatform = VR_HEADLESS; //unsupported platform...\n\t\t\tbreak;\n\t\t}\n#endif\n\t\tif (info->vr && !info->vr->Prepare(&setup))\n\t\t\tinfo->vr = NULL;\n\n\t\tsdlcontext = SDL_GL_CreateContext(sdlwindow);\n\t\tif (!sdlcontext)\n\t\t{\n\t\t\tSDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 1);\n\t\t\tSDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1);\n\t\t\tSDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, 0);\n\t\t\tSDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, 0);\n\t\t\tsdlcontext = SDL_GL_CreateContext(sdlwindow);\n\t\t\tif (!sdlcontext)\n\t\t\t{\n\t\t\t\tCon_Printf(\"Couldn't initialize GL context: %s\\n\", SDL_GetError());\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\tsrgb = 0;\n\t\tSDL_GL_GetAttribute(SDL_GL_FRAMEBUFFER_SRGB_CAPABLE, &srgb);\n\t\tif (srgb)\n\t\t\tvid.flags |= VID_SRGB_CAPABLE;\n\n\t\t//now fill it in properly, now that we have a context.\n#if\tSDL_VERSION_ATLEAST(3,0,0)\n\t\tswitch(setup.vrplatform)\n\t\t{\n\t\tcase VR_X11_GLX:\n\t\t\tsetup.x11_glx.display = SDL_GetPointerProperty(SDL_GetWindowProperties(sdlwindow), SDL_PROP_WINDOW_X11_DISPLAY_POINTER, NULL);\n\t\t\t//setup.x11_glx.screen = SDL_GetNumberProperty(SDL_GetWindowProperties(sdlwindow), SDL_PROP_WINDOW_X11_SCREEN_NUMBER, 0);\n\t\t\tsetup.x11_glx.drawable = SDL_GetNumberProperty(SDL_GetWindowProperties(sdlwindow), SDL_PROP_WINDOW_X11_WINDOW_NUMBER, 0);\n\t\t\tsetup.x11_glx.visualid = 0;//???\n\t\t\tsetup.x11_glx.glxfbconfig = 0;//???\n\t\t\tsetup.x11_glx.glxcontext = SDL_GL_GetCurrentContext();\n\t\t\tbreak;\n\t\tcase VR_EGL:\t//wayland\n\t\t\tsetup.egl.getprocaddr = (void *(*)(const char *)) SDL_EGL_GetProcAddress;\n\t\t\tsetup.egl.egldisplay = SDL_EGL_GetCurrentDisplay();\n\t\t\tsetup.egl.eglconfig = SDL_EGL_GetCurrentConfig();\n\t\t\tsetup.egl.eglcontext = SDL_GL_GetCurrentContext();\n\t\t\tbreak;\n\t\tcase VR_WIN_WGL:\n\t\t\tsetup.wgl.hdc = SDL_GetPointerProperty(SDL_GetWindowProperties(sdlwindow), SDL_PROP_WINDOW_WIN32_HDC_POINTER, NULL);\n\t\t\tsetup.wgl.hglrc = SDL_GL_GetCurrentContext();\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tbreak;\n\t\t}\n#else\n\t\tswitch(wminfo.subsystem)\n\t\t{\n#if defined(SDL_VIDEO_DRIVER_WINDOWS)\n\t\tcase SDL_SYSWM_WINDOWS:\n\t\t\tsetup.wgl.hdc = wminfo.info.win.hdc;\n\t\t\tsetup.wgl.hglrc = SDL_GL_GetCurrentContext();\n\t\t\tbreak;\n#endif\n#if defined(SDL_VIDEO_DRIVER_X11)\n\t\tcase SDL_SYSWM_X11:\n\t\t\tsetup.x11_glx.display = wminfo.info.x11.display;\n\t\t\tsetup.x11_glx.drawable = wminfo.info.x11.window;\n\t\t\tsetup.x11_glx.visualid = 0;//???\n\t\t\tsetup.x11_glx.glxfbconfig = 0;//???\n\t\t\tsetup.x11_glx.glxcontext = SDL_GL_GetCurrentContext();\n\t\t\tbreak;\n#endif\n#if defined(SDL_VIDEO_DRIVER_WAYLAND) && SDL_VERSION_ATLEAST(3,0,0)\n\t\tcase SDL_SYSWM_WAYLAND:\n\t\t\tsetup.vrplatform = VR_EGL;\n\t\t\tsetup.egl.getprocaddr = SDL_EGL_GetProcAddress;\n\t\t\tsetup.egl.egldisplay = SDL_EGL_GetCurrentEGLDisplay();\n\t\t\tsetup.egl.eglconfig = SDL_EGL_GetCurrentEGLConfig();\n\t\t\tsetup.egl.eglcontext = SDL_GL_GetCurrentContext();\n\t\t\tbreak;\n\n#endif\n\t\tdefault:\n\t\t\tsetup.vrplatform = VR_HEADLESS; //unsupported platform...\n\t\t\tbreak;\n\t\t}\n#endif\n\n\t\tif (info->vr && !info->vr->Init(&setup, info))\n\t\t{\n\t\t\tinfo->vr->Shutdown();\n\t\t\tvid.vr = NULL;\n\t\t}\n\t\telse\n\t\t\tvid.vr = info->vr;\n\t}\n#endif\n\n#else\n\tSDL_GetGammaRamp(intitialgammaramps[0], intitialgammaramps[1], intitialgammaramps[2]);\n\tif (info->fullscreen)\n\t{\n\t\tflags = SDL_FULLSCREEN;\n\t\tvid_isfullscreen = true;\n\t}\n\telse\n\t{\n\t\tflags = SDL_RESIZABLE;\n\t\tvid_isfullscreen = false;\n\t}\n\tsdlsurf = SDL_SetVideoMode(vid.pixelwidth=info->width, vid.pixelheight=info->height, info->bpp, flags | SDL_OPENGL);\n\tif (!sdlsurf)\n\t{\n\t\tCon_Printf(\"Couldn't set GL mode: %s\\n\", SDL_GetError());\n\t\treturn false;\n\t}\n#endif\n\tvid.activeapp = true;\n\n\tmouseactive = false;\n//\tif (vid_isfullscreen)\n//\t\tIN_ActivateMouse();\n\n#if SDL_VERSION_ATLEAST(2,0,0)\n\tSDL_DisableScreenSaver();\n#else\n\tSDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);\n#endif\n\tvid_vsync.modified = true;\n\n#ifdef _WIN32\n\t{\t//win32 apis are very insistant upon having a window context for things that have nothing to do with windowing system stuff.\n\t#if\tSDL_VERSION_ATLEAST(3,0,0)\n\t\tmainwindow = SDL_GetPointerProperty(SDL_GetWindowProperties(sdlwindow), SDL_PROP_WINDOW_WIN32_HWND_POINTER, NULL);\n\t#elif SDL_VERSION_ATLEAST(2,0,0)\n\t\tSDL_SysWMinfo info;\n\t\tSDL_GetWindowWMInfo(sdlwindow, &info);\n\t\tif (info.subsystem == SDL_SYSWM_WINDOWS)\n\t\t\tmainwindow = info.info.win.window;\n\t\telse\n\t\t\tmainwindow = NULL;\t//if we're using an x11 subsystem but running in windows then don't feck up... here, at least.\n\t#else\n\t\tSDL_SysWMinfo wmInfo;\n\t\tSDL_GetWMInfo(&wmInfo);\n\t\tmainwindow = wmInfo.window; //note that this is usually still null\n\t#endif\n\t}\n#endif\n\n#if SDL_VERSION_ATLEAST(2,0,0)\n\trf->VID_CreateCursor = GLVID_CreateCursor;\n\trf->VID_DestroyCursor = GLVID_DestroyCursor;\n\trf->VID_SetCursor = GLVID_SetCursor;\n#endif\n\n\treturn true;\n}\n\n#ifdef OPENGL_SDL\nqboolean GLVID_Init (rendererstate_t *info, unsigned char *palette)\n{\n\tif (SDLVID_Init(info, palette, QR_OPENGL))\n\t{\n\t\treturn GL_Init(info, GLVID_getsdlglfunction);\n\t}\n\treturn false;\n}\n#endif\n\nvoid GLVID_DeInit (void)\n{\n\tvid.activeapp = false;\n\n\tIN_DeactivateMouse();\n\tINS_SetOSK(false);\n\n#if SDL_VERSION_ATLEAST(2,0,0)\n#if !SDL_VERSION_ATLEAST(3,0,0)\n\tSDL_SetWindowGammaRamp(sdlwindow, NULL, NULL, NULL);\n#endif\n\n\tswitch(qrenderer)\n\t{\n#ifdef OPENGL_SDL\n\tcase QR_OPENGL:\n#if SDL_VERSION_ATLEAST(3,0,0)\n\t\tSDL_GL_DestroyContext(sdlcontext);\n#else\n\t\tSDL_GL_DeleteContext(sdlcontext);\n#endif\n\t\tbreak;\n#endif\n#ifdef VULKAN_SDL\n\tcase QR_VULKAN:\n\t\tVK_Shutdown();\n\t\tbreak;\n#endif\n\tdefault:\n\t\tbreak;\n\t}\n\tSDL_DestroyWindow(sdlwindow);\n\tsdlwindow = NULL;\n#else\n\tSDL_SetGammaRamp (intitialgammaramps[0], intitialgammaramps[1], intitialgammaramps[2]);\n#endif\n\n\tSDL_QuitSubSystem(SDL_INIT_VIDEO);\n#ifdef OPENGL_SDL\n\tGL_ForgetPointers();\n#endif\n}\n\n\nvoid GLVID_SwapBuffers (void)\n{\n\tswitch(qrenderer)\n\t{\n#ifdef OPENGL_SDL\n\tcase QR_OPENGL:\n#if SDL_VERSION_ATLEAST(2,0,0)\n\t\tif (vid_vsync.modified)\n\t\t{\n\t\t\tif (*vid_vsync.string)\n\t\t\t{\n\t\t\t\t//if swap_tear fails, try without.\n#if SDL_VERSION_ATLEAST(3,0,0)\n\t\t\t\tif (!SDL_GL_SetSwapInterval(vid_vsync.ival) && vid_vsync.ival < 0)\n#else\n\t\t\t\tif (SDL_GL_SetSwapInterval(vid_vsync.ival) == -1 && vid_vsync.ival < 0)\n#endif\n\t\t\t\t\tSDL_GL_SetSwapInterval(-vid_vsync.ival);\n\t\t\t}\n\t\t\tvid_vsync.modified = false;\n\t\t}\n\n\t\tSDL_GL_SwapWindow(sdlwindow);\n#else\n\t\tSDL_GL_SwapBuffers();\n#endif\n\t\tbreak;\n#endif\n\tdefault:\n\t\tbreak;\n\t}\n\n\n\tif (!vid_isfullscreen)\n\t{\n\t\tif (!in_windowed_mouse.value)\n\t\t{\n\t\t\tIN_DeactivateMouse ();\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (!Key_MouseShouldBeFree() && vid.activeapp)\n\t\t\t\tIN_ActivateMouse ();\n\t\t\telse\n\t\t\t\tIN_DeactivateMouse ();\n\t\t}\n\t}\n}\n\nqboolean GLVID_ApplyGammaRamps (unsigned int gammarampsize, unsigned short *ramps)\n{\n#if SDL_VERSION_ATLEAST(3,0,0)\n\treturn false;\n#elif SDL_VERSION_ATLEAST(2,0,0)\n\tif (ramps && gammarampsize == 256)\n\t{\n\t\tswitch(vid_hardwaregamma.ival)\n\t\t{\n\t\tcase 0:\t//never use hardware/glsl gamma\n\t\tcase 2:\t//ALWAYS use glsl gamma\n\t\t\treturn false;\n\t\tdefault:\n\t\tcase 1:\t//no hardware gamma when windowed\n\t\t\tif (!vid_isfullscreen)\n\t\t\t\treturn false;\n\t\t\tbreak;\n\t\tcase 3:\t//ALWAYS try to use hardware gamma, even when it fails...\n\t\t\tbreak;\n\t\t}\n\n\t\tgammaworks |= !SDL_SetWindowGammaRamp (sdlwindow, &ramps[0], &ramps[256], &ramps[512]);\n\t\treturn gammaworks;\n\t}\n\telse if (gammaworks)\n\t{\n\t\tSDL_SetWindowGammaRamp (sdlwindow, NULL, NULL, NULL);\n\t\treturn true;\n\t}\n#else\n\tif (ramps && gammarampsize == 256)\n\t{\n\t\tswitch(vid_hardwaregamma.ival)\n\t\t{\n\t\tcase 0:\t//never use hardware/glsl gamma\n\t\tcase 2:\t//ALWAYS use glsl gamma\n\t\t\treturn false;\n\t\tdefault:\n\t\tcase 1:\t//no hardware gamma when windowed\n\t\t\tif (!vid_isfullscreen)\n\t\t\t\treturn false;\n\t\t\tbreak;\n\t\tcase 3:\t//ALWAYS try to use hardware gamma, even when it fails...\n\t\t\tbreak;\n\t\t}\n\n\t\tgammaworks |= !SDL_SetGammaRamp (&ramps[0], &ramps[256], &ramps[512]);\n\t\treturn gammaworks;\n\t}\n\telse\n\t{\n\t\tSDL_SetGammaRamp (intitialgammaramps[0], intitialgammaramps[1], intitialgammaramps[2]);\n\t\treturn true;\n\t}\n#endif\n\treturn false;\n}\n\nvoid GLVID_SetCaption(const char *text)\n{\n#if SDL_VERSION_ATLEAST(2,0,0)\n\tSDL_SetWindowTitle(sdlwindow, text);\n#else\n\tSDL_WM_SetCaption(text, NULL);\n#endif\n}\n\n\n\n#ifdef VULKAN_SDL\nstatic qboolean VKSDL_CreateSurface(void)\n{\n#if SDL_VERSION_ATLEAST(3,0,0)\n\treturn SDL_Vulkan_CreateSurface(sdlwindow, vk.instance, vkallocationcb, &vk.surface);\n#else\n\treturn SDL_Vulkan_CreateSurface(sdlwindow, vk.instance, &vk.surface);\n#endif\n}\nstatic qboolean VKVID_Init (rendererstate_t *info, unsigned char *palette)\n{\n\tunsigned extcount;\n#if SDL_VERSION_ATLEAST(3,0,0)\n\tchar const* const*extnames;\n#else\n\tconst char **extnames;\n#endif\n\tif (!SDLVID_Init(info, palette, QR_VULKAN))\n\t\treturn false;\n#if SDL_VERSION_ATLEAST(3,0,0)\n\textnames = SDL_Vulkan_GetInstanceExtensions(&extcount);\n#else\n\tif (!SDL_Vulkan_GetInstanceExtensions(sdlwindow, &extcount, NULL))\n\t\treturn false;\n\textnames = alloca(sizeof(*extnames)*extcount);\n\tif (!SDL_Vulkan_GetInstanceExtensions(sdlwindow, &extcount, extnames))\n\t\treturn false;\n#endif\n\n\tvkGetInstanceProcAddr = (PFN_vkGetInstanceProcAddr)SDL_Vulkan_GetVkGetInstanceProcAddr();\n\tif (!VK_Init(info, extnames, extcount, VKSDL_CreateSurface, NULL))\n\t\treturn false;\n\treturn true;\n}\nstatic qboolean VKVID_EnumerateDevices(void *usercontext, void(*callback)(void *context, const char *devicename, const char *outputname, const char *desc))\n{\n    qboolean ret = false;\n#ifdef VK_NO_PROTOTYPES\n    PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr = (PFN_vkGetInstanceProcAddr)SDL_Vulkan_GetVkGetInstanceProcAddr();\n#endif\n\t//FIXME: enumerate monitor names too, not just gpus.\n    ret = VK_EnumerateDevices(usercontext, callback, \"Vulkan-SDL-\", vkGetInstanceProcAddr);\n    return ret;\n}\nrendererinfo_t vkrendererinfo =\n{\n\t\"Vulkan-SDL\",\n\t{\n\t\t\"vk\",\n\t\t\"Vulkan\"\n\t},\n\tQR_VULKAN,\n\n\tVK_Draw_Init,\n\tVK_Draw_Shutdown,\n\n\tVK_UpdateFiltering,\n\tVK_LoadTextureMips,\n\tVK_DestroyTexture,\n\n\tVK_R_Init,\n\tVK_R_DeInit,\n\tVK_R_RenderView,\n\n\tVKVID_Init,\n\tGLVID_DeInit,\n\tGLVID_SwapBuffers,\n\tGLVID_ApplyGammaRamps,\n\tNULL,\n\tNULL,\n\tNULL,\n\tGLVID_SetCaption,\n\tVKVID_GetRGBInfo,\n\n\tVK_SCR_UpdateScreen,\n\n\tVKBE_SelectMode,\n\tVKBE_DrawMesh_List,\n\tVKBE_DrawMesh_Single,\n\tVKBE_SubmitBatch,\n\tVKBE_GetTempBatch,\n\tVKBE_DrawWorld,\n\tVKBE_Init,\n\tVKBE_GenBrushModelVBO,\n\tVKBE_ClearVBO,\n\tVKBE_UploadAllLightmaps,\n\tVKBE_SelectEntity,\n\tVKBE_SelectDLight,\n\tVKBE_Scissor,\n\tVKBE_LightCullModel,\n\n\tVKBE_VBO_Begin,\n\tVKBE_VBO_Data,\n\tVKBE_VBO_Finish,\n\tVKBE_VBO_Destroy,\n\n\tVKBE_RenderToTextureUpdate2d,\n\n\t\"no more\",\n\n\tNULL,\t//GetPriority\n\tSDLVID_EnumerateVideoModes,\n\tVKVID_EnumerateDevices,\n\tNULL,\t//MayRefresh\n};\n#else\nrendererinfo_t vkrendererinfo;\n#endif\n"
  },
  {
    "path": "engine/gl/gl_vidtinyglstubs.c",
    "content": "/*\nCopyright (C) 2006-2007 Mark Olsen\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  \n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n*/\n\n/*\nThis file is currently used only by bigfoot's MorphOS port.\n\nI should explain this file to any Unix/Windows programmers...\nOver on amiga-like operating systems, each system function to dynamic libraries is defined as a macro to a jmp statement.\n\nalong the lines of ((myfunc_t)((char*)library + offset))(parameters);\nObviously, in an engine that likes function pointers to all the gl functions, this is somewhat problematic.\n\nYou can see the state of his opengl library be seeing which functions are actually implemented. :)\n*/\n\n/*\n * PS: It is Kiero's library and everything _IS_ implemented ;)\n *   - bigfoot\n *\n * Oh, and just FYI, the offsets are negative, that is, the jump table is below\n * the library base pointer.\n */\n\nvoid stub_glAlphaFunc(GLenum func, GLclampf ref)\n{\n\tglAlphaFunc(func, ref);\n}\n\nvoid stub_glBegin(GLenum mode)\n{\n\tglBegin(mode);\n}\n\nvoid stub_glBlendFunc(GLenum sfactor, GLenum dfactor)\n{\n\tglBlendFunc(sfactor, dfactor);\n}\n\nvoid stub_glBindTexture(GLenum target, GLuint texture)\n{\n\tglBindTexture(target, texture);\n}\n\nvoid stub_glClear(GLbitfield mask)\n{\n\tglClear(mask);\n}\n\nvoid stub_glClearColor(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha)\n{\n\tglClearColor(red, green, blue, alpha);\n}\n\nvoid stub_glClearDepth(GLclampd depth)\n{\n\tglClearDepth(depth);\n}\n\nvoid stub_glClearStencil(GLint s)\n{\n\tglClearStencil(s);\n}\n\nvoid stub_glColor3f(GLfloat red, GLfloat green, GLfloat blue)\n{\n\tglColor3f(red, green, blue);\n}\n\nvoid stub_glColor3ub(GLubyte red, GLubyte green, GLubyte blue)\n{\n\tglColor3ub(red, green, blue);\n}\n\nvoid stub_glColor4f(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha)\n{\n\tglColor4f(red, green, blue, alpha);\n}\n\nvoid stub_glColor4fv(const GLfloat *v)\n{\n\tglColor4fv(v);\n}\n\nvoid stub_glColor4ub(GLubyte red, GLubyte green, GLubyte blue, GLubyte alpha)\n{\n\tglColor4ub(red, green, blue, alpha);\n}\n\nvoid stub_glColor4ubv(const GLubyte *v)\n{\n\tglColor4ubv(v);\n}\n\nvoid stub_glColorMask(GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha)\n{\n\tglColorMask(red, green, blue, alpha);\n}\n\nvoid stub_glCopyTexImage2D(GLenum target, GLint level, GLenum internalFormat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border)\n{\n\tglCopyTexImage2D(target, level, internalFormat, x, y, width, height, border);\n}\n\nvoid stub_glCopyTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height)\n{\n\tglCopyTexSubImage2D(target, level, xoffset, yoffset, x, y, width, height);\n}\n\nvoid stub_glCullFace(GLenum mode)\n{\n\tglCullFace(mode);\n}\n\nvoid stub_glDepthFunc(GLenum func)\n{\n\tglDepthFunc(func);\n}\n\nvoid stub_glDepthMask(GLboolean flag)\n{\n\tglDepthMask(flag);\n}\n\nvoid stub_glDepthRange(GLclampd zNear, GLclampd zFar)\n{\n\tglDepthRange(zNear, zFar);\n}\n\nvoid stub_glDisable(GLenum cap)\n{\n\tglDisable(cap);\n}\n\nvoid stub_glDrawBuffer(GLenum mode)\n{\n\tglDrawBuffer(mode);\n}\n\nvoid stub_glDrawPixels(GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels)\n{\n\tglDrawPixels(width, height, format, type, pixels);\n}\n\nvoid stub_glEnable(GLenum cap)\n{\n\tglEnable(cap);\n}\n\nvoid stub_glEnd(void)\n{\n\tglEnd();\n}\n\nvoid stub_glFinish(void)\n{\n\tglFinish();\n}\n\nvoid stub_glFlush(void)\n{\n\tglFlush();\n}\n\nvoid stub_glFrustum(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar)\n{\n\tglFrustum(left, right, bottom, top, zNear, zFar);\n}\n\nvoid stub_glGetFloatv(GLenum pname, GLfloat *params)\n{\n\tglGetFloatv(pname, params);\n}\n\nvoid stub_glGetIntegerv(GLenum pname, GLint *params)\n{\n\tglGetIntegerv(pname, params);\n}\n\nconst GLubyte *stub_glGetString(GLenum name)\n{\n\treturn glGetString(name);\n}\n\nvoid stub_glGetTexLevelParameteriv(GLenum target, GLint level, GLenum pname, GLint *params)\n{\n\tglGetTexLevelParameteriv(target, level, pname, params);\n}\n\nvoid stub_glHint(GLenum target, GLenum mode)\n{\n\tglHint(target, mode);\n}\n\nvoid stub_glLoadIdentity(void)\n{\n\tglLoadIdentity();\n}\n\nvoid stub_glLoadMatrixf(const GLfloat *m)\n{\n\tglLoadMatrixf(m);\n}\n\nvoid stub_glNormal3f(GLfloat nx, GLfloat ny, GLfloat nz)\n{\n\tglNormal3f(nx, ny, nz);\n}\n\nvoid stub_glNormal3fv(const GLfloat *v)\n{\n\tglNormal3fv(v);\n}\n\nvoid stub_glMatrixMode(GLenum mode)\n{\n\tglMatrixMode(mode);\n}\n\nvoid stub_glMultMatrixf(const GLfloat *m)\n{\n\tglMultMatrixf(m);\n}\n\nvoid stub_glOrtho(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar)\n{\n\tglOrtho(left, right, bottom, top, zNear, zFar);\n}\n\nvoid stub_glPolygonMode(GLenum face, GLenum mode)\n{\n\tglPolygonMode(face, mode);\n}\n\nvoid stub_glPopMatrix(void)\n{\n\tglPopMatrix();\n}\n\nvoid stub_glPushMatrix(void)\n{\n\tglPushMatrix();\n}\n\nvoid stub_glReadBuffer(GLenum mode)\n{\n\tglReadBuffer(mode);\n}\n\nvoid stub_glReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid *pixels)\n{\n\tglReadPixels(x, y, width, height, format, type, pixels);\n}\n\nvoid stub_glRotatef(GLfloat angle, GLfloat x, GLfloat y, GLfloat z)\n{\n\tglRotatef(angle, x, y, z);\n}\n\nvoid stub_glScalef(GLfloat x, GLfloat y, GLfloat z)\n{\n\tglScalef(x, y, z);\n}\n\nvoid stub_glShadeModel(GLenum mode)\n{\n\tglShadeModel(mode);\n}\n\nvoid stub_glTexCoord1f(GLfloat s)\n{\n\tglTexCoord1f(s);\n}\n\nvoid stub_glTexCoord2f(GLfloat s, GLfloat t)\n{\n\tglTexCoord2f(s, t);\n}\n\nvoid stub_glTexCoord2fv(const GLfloat *v)\n{\n\tglTexCoord2fv(v);\n}\n\nvoid stub_glTexEnvf(GLenum target, GLenum pname, GLfloat param)\n{\n\tglTexEnvf(target, pname, param);\n}\n\nvoid stub_glTexEnvfv(GLenum target, GLenum pname, const GLfloat *params)\n{\n\tglTexEnvfv(target, pname, params);\n}\n\nvoid stub_glTexEnvi(GLenum target, GLenum pname, GLint param)\n{\n\tglTexEnvi(target, pname, param);\n}\n\nvoid stub_glTexGeni(GLenum coord, GLenum pname, GLint param)\n{\n\tglTexGeni(coord, pname, param);\n}\n\nvoid stub_glTexImage2D(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid *pixels)\n{\n\tglTexImage2D(target, level, internalformat, width, height, border, format, type, pixels);\n}\n\nvoid stub_glTexParameteri(GLenum target, GLenum pname, GLint param)\n{\n\tglTexParameteri(target, pname, param);\n}\n\nvoid stub_glTexParameterf(GLenum target, GLenum pname, GLfloat param)\n{\n\tglTexParameterf(target, pname, param);\n}\n\nvoid stub_glTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels)\n{\n\tglTexSubImage2D(target, level, xoffset, yoffset, width, height, format, type, pixels);\n}\n\nvoid stub_glTranslatef(GLfloat x, GLfloat y, GLfloat z)\n{\n\tglTranslatef(x, y, z);\n}\n\nvoid stub_glVertex2f(GLfloat x, GLfloat y)\n{\n\tglVertex2f(x, y);\n}\n\nvoid stub_glVertex3f(GLfloat x, GLfloat y, GLfloat z)\n{\n\tglVertex3f(x, y, z);\n}\n\nvoid stub_glVertex3fv(const GLfloat *v)\n{\n\tglVertex3fv(v);\n}\n\nvoid stub_glViewport(GLint x, GLint y, GLsizei width, GLsizei height)\n{\n\tglViewport(x, y, width, height);\n}\n\nGLenum stub_glGetError(void)\n{\n\treturn glGetError();\n}\n\nvoid stub_glDrawElements(GLenum mode, GLsizei count, GLenum type, const GLvoid *indices)\n{\n\tglDrawElements(mode, count, type, indices);\n}\n\nvoid stub_glArrayElement(GLint i)\n{\n\tglArrayElement(i);\n}\n\nvoid stub_glVertexPointer(GLint size, GLenum type, GLsizei stride, const GLvoid *pointer)\n{\n\tglVertexPointer(size, type, stride, pointer);\n}\n\nvoid stub_glNormalPointer(GLenum type, GLsizei stride, const GLvoid *pointer)\n{\n\tglNormalPointer(type, stride, pointer);\n}\n\nvoid stub_glTexCoordPointer(GLint size, GLenum type, GLsizei stride, const GLvoid *pointer)\n{\n\tglTexCoordPointer(size, type, stride, pointer);\n}\n\nvoid stub_glColorPointer(GLint size, GLenum type, GLsizei stride, const GLvoid *pointer)\n{\n\tglColorPointer(size, type, stride, pointer);\n}\n\nvoid stub_glDrawArrays(GLenum mode, GLint first, GLsizei count)\n{\n\tglDrawArrays(mode, first, count);\n}\n\nvoid stub_glEnableClientState(GLenum array)\n{\n\tglEnableClientState(array);\n}\n\nvoid stub_glDisableClientState(GLenum array)\n{\n\tglDisableClientState(array);\n}\n\nvoid stub_glStencilOp(GLenum fail, GLenum zfail, GLenum zpass)\n{\n\tglStencilOp(fail, zfail, zpass);\n}\n\nvoid stub_glStencilFunc(GLenum func, GLint ref, GLuint mask)\n{\n\tglStencilFunc(func, ref, mask);\n}\n\nvoid stub_glPushAttrib(GLbitfield mask)\n{\n\tglPushAttrib(mask);\n}\n\nvoid stub_glPopAttrib(void)\n{\n\tglPopAttrib();\n}\n\nvoid stub_glScissor(GLint x, GLint y, GLsizei width, GLsizei height)\n{\n\tglScissor(x, y, width, height);\n}\n\nvoid stub_glMultiTexCoord2fARB(GLenum unit, GLfloat s, GLfloat t)\n{\n\tglMultiTexCoord2fARB(unit, s, t);\n}\n\nvoid stub_glMultiTexCoord3fARB(GLenum unit, GLfloat s, GLfloat t, GLfloat r)\n{\n\tglMultiTexCoord3fARB(unit, s, t, r);\n}\n\nvoid stub_glActiveTextureARB(GLenum unit)\n{\n\tglActiveTextureARB(unit);\n}\n\nvoid stub_glClientActiveTextureARB(GLenum unit)\n{\n\tglClientActiveTextureARB(unit);\n}\n"
  },
  {
    "path": "engine/gl/gl_vidwayland.c",
    "content": "//This is my attempt at wayland support for both opengl and vulkan.\r\n//Note that this is sorely under-tested - I haven't tested vulkan-on-wayland at all as none of the drivers for nvidia support it.\r\n\r\n//in no particular order...\r\n//TODO: leaks on shutdown\r\n//TODO: proper window decorations - zxdg_decoration_manager_v1\r\n//TODO: kb autorepeat\r\n//TODO: system clipboard\r\n//TODO: drag+drop\r\n\r\n/*\r\nkwin unstable protocools:\r\n zwp_relative_pointer_manager_v1, since 5.28\r\n zwp_pointer_constraints_v1, since 5.29\r\n xdg_wm_base, since 5.48\r\n zxdg_decoration_manager_v1, since 5.54\r\n*/\r\n\r\n#include \"bothdefs.h\"\r\n#ifdef WAYLANDQUAKE\r\n#include \"gl_videgl.h\"\t//define this BEFORE the wayland stuff. This means the EGL types will have their (x11) defaults instead of getting mixed up with wayland. we expect to be able to use the void* verions instead for wayland anyway.\r\n#include <wayland-client.h>\r\n#include <wayland-egl.h>\r\n#include <linux/input.h>\t//this is shite.\r\n#include <fcntl.h>\r\n#include <unistd.h>\r\n#include <sys/mman.h>\r\n#include <xkbcommon/xkbcommon.h>\r\n\r\n#include \"quakedef.h\"\r\n#if defined(GLQUAKE) && defined(USE_EGL)\r\n#include \"gl_draw.h\"\r\n#endif\r\n#if defined(VKQUAKE)\r\n#include \"vk/vkrenderer.h\"\r\n#endif\r\n\r\n#if WAYLAND_VERSION_MAJOR < 1\r\n#error \"wayland headers are too old\"\r\n#endif\r\n\r\n#include \"glquake.h\"\r\n#include \"shader.h\"\r\n\r\nextern cvar_t vid_conautoscale;\r\n\r\n//protocol names...\r\n#define WP_POINTER_CONSTRAINTS_NAME \"zwp_pointer_constraints_v1\"\r\n#define WP_RELATIVE_POINTER_MANAGER_NAME \"zwp_relative_pointer_manager_v1\"\r\n#define ORG_KDE_KWIN_SERVER_DECORATION_MANAGER_NAME \"org_kde_kwin_server_decoration_manager\" //comment out to disable once the XDG version is tested.\r\n//FIXME: needs testing. #define XDG_DECORATION_MANAGER_NAME \"zxdg_decoration_manager_v1\"\r\n\r\n//#define XDG_SHELL_UNSTABLE\r\n#ifdef XDG_SHELL_UNSTABLE\r\n#define XDG_SHELL_NAME \"zxdg_shell_v6\"\r\n#else\r\n#define XDG_SHELL_NAME \"xdg_wm_base\"\r\n#endif\r\n#define WL_SHELL_NAME \"wl_shell\"\t//fall back on this if xdg_shell is missing\r\n\r\n#ifndef STATIC_WAYLAND\r\n\t#define DYNAMIC_WAYLAND\r\n#endif\r\n#ifdef DYNAMIC_WAYLAND\r\n\r\nstatic struct wl_display *(*pwl_display_connect)(const char *name);\r\nstatic void (*pwl_display_disconnect)(struct wl_display *display);\r\nstatic int (*pwl_display_dispatch)(struct wl_display *display);\r\nstatic int (*pwl_display_dispatch_pending)(struct wl_display *display);\r\nstatic int (*pwl_display_roundtrip)(struct wl_display *display);\r\n\r\nstatic struct wl_proxy *(*pwl_proxy_marshal_constructor)(struct wl_proxy *proxy, uint32_t opcode, const struct wl_interface *interface, ...);\r\nstatic struct wl_proxy *(*pwl_proxy_marshal_constructor_versioned)(struct wl_proxy *proxy, uint32_t opcode, const struct wl_interface *interface, uint32_t version, ...);\r\nstatic void (*pwl_proxy_destroy)(struct wl_proxy *proxy);\r\nstatic void (*pwl_proxy_marshal)(struct wl_proxy *p, uint32_t opcode, ...);\r\nstatic int (*pwl_proxy_add_listener)(struct wl_proxy *proxy, void (**implementation)(void), void *data);\r\n\r\nstatic const struct wl_interface\t\t*pwl_keyboard_interface;\r\nstatic const struct wl_interface\t\t*pwl_pointer_interface;\r\nstatic const struct wl_interface\t\t*pwl_compositor_interface;\r\nstatic const struct wl_interface\t\t*pwl_region_interface;\r\nstatic const struct wl_interface\t\t*pwl_surface_interface;\r\n#ifdef WL_SHELL_NAME\r\nstatic const struct wl_interface\t\t*pwl_shell_surface_interface;\r\nstatic const struct wl_interface\t\t*pwl_shell_interface;\r\n#endif\r\nstatic const struct wl_interface\t\t*pwl_seat_interface;\r\nstatic const struct wl_interface\t\t*pwl_registry_interface;\r\nstatic const struct wl_interface\t\t*pwl_output_interface;\r\nstatic const struct wl_interface\t\t*pwl_shm_interface;\r\nstatic const struct wl_interface\t\t*pwl_shm_pool_interface;\r\nstatic const struct wl_interface\t\t*pwl_buffer_interface;\r\nstatic const struct wl_interface\t\t*pwl_callback_interface;\r\n\r\nstatic dllfunction_t waylandexports_wl[] =\r\n{\r\n\t{(void**)&pwl_display_connect,\t\t\t\t\t\t\"wl_display_connect\"},\r\n\t{(void**)&pwl_display_disconnect,\t\t\t\t\t\"wl_display_disconnect\"},\r\n\t{(void**)&pwl_display_dispatch,\t\t\t\t\t\t\"wl_display_dispatch\"},\r\n\t{(void**)&pwl_display_dispatch_pending,\t\t\t\t\"wl_display_dispatch_pending\"},\r\n\t{(void**)&pwl_display_roundtrip,\t\t\t\t\t\"wl_display_roundtrip\"},\r\n\t{(void**)&pwl_proxy_marshal_constructor,\t\t\t\"wl_proxy_marshal_constructor\"},\r\n\t{(void**)&pwl_proxy_marshal_constructor_versioned,\t\"wl_proxy_marshal_constructor_versioned\"},\r\n\t{(void**)&pwl_proxy_destroy,\t\t\t\t\t\t\"wl_proxy_destroy\"},\r\n\t{(void**)&pwl_proxy_marshal,\t\t\t\t\t\t\"wl_proxy_marshal\"},\r\n\t{(void**)&pwl_proxy_add_listener,\t\t\t\t\t\"wl_proxy_add_listener\"},\r\n\t{(void**)&pwl_keyboard_interface,\t\t\t\t\t\"wl_keyboard_interface\"},\r\n\t{(void**)&pwl_pointer_interface,\t\t\t\t\t\"wl_pointer_interface\"},\r\n\t{(void**)&pwl_compositor_interface,\t\t\t\t\t\"wl_compositor_interface\"},\r\n\t{(void**)&pwl_region_interface,\t\t\t\t\t\t\"wl_region_interface\"},\r\n\t{(void**)&pwl_surface_interface,\t\t\t\t\t\"wl_surface_interface\"},\r\n#ifdef WL_SHELL_NAME\r\n\t{(void**)&pwl_shell_surface_interface,\t\t\t\t\"wl_shell_surface_interface\"},\r\n\t{(void**)&pwl_shell_interface,\t\t\t\t\t\t\"wl_shell_interface\"},\r\n#endif\r\n\t{(void**)&pwl_seat_interface,\t\t\t\t\t\t\"wl_seat_interface\"},\r\n\t{(void**)&pwl_registry_interface,\t\t\t\t\t\"wl_registry_interface\"},\r\n\t{(void**)&pwl_output_interface,\t\t\t\t\t\t\"wl_output_interface\"},\r\n\t{(void**)&pwl_shm_interface,\t\t\t\t\t\t\"wl_shm_interface\"},\r\n\t{(void**)&pwl_shm_pool_interface,\t\t\t\t\t\"wl_shm_pool_interface\"},\r\n\t{(void**)&pwl_buffer_interface,\t\t\t\t\t\t\"wl_buffer_interface\"},\r\n\t{(void**)&pwl_callback_interface,\t\t\t\t\t\"wl_callback_interface\"},\r\n\t{NULL, NULL}\r\n};\r\nstatic dllhandle_t *lib_wayland_wl;\r\nstatic qboolean WL_InitLibrary(void)\r\n{\r\n\tlib_wayland_wl = Sys_LoadLibrary(\"libwayland-client.so.0\", waylandexports_wl);\r\n\tif (!lib_wayland_wl)\r\n\t\treturn false;\r\n\treturn true;\r\n}\r\n\r\nstatic dllhandle_t *lib_xkb;\r\nstatic struct xkb_context *(*pxkb_context_new)(enum xkb_context_flags flags);\r\nstatic struct xkb_keymap *(*pxkb_keymap_new_from_string)(struct xkb_context *context, const char *string, enum xkb_keymap_format format, enum xkb_keymap_compile_flags flags);\r\nstatic struct xkb_state *(*pxkb_state_new)(struct xkb_keymap *keymap);\r\nstatic xkb_keysym_t (*pxkb_state_key_get_one_sym)(struct xkb_state *state, xkb_keycode_t key);\r\nstatic uint32_t (*pxkb_keysym_to_utf32)(xkb_keysym_t keysym);\r\nstatic void (*pxkb_state_unref)(struct xkb_state *state);\r\nstatic void (*pxkb_keymap_unref)(struct xkb_keymap *keymap);\r\nstatic enum xkb_state_component (*pxkb_state_update_mask)(struct xkb_state *state, xkb_mod_mask_t depressed_mods, xkb_mod_mask_t latched_mods, xkb_mod_mask_t locked_mods, xkb_layout_index_t depressed_layout, xkb_layout_index_t latched_layout, xkb_layout_index_t locked_layout);\r\nstatic qboolean WL_InitLibraryXKB(void)\r\n{\r\n\tstatic dllfunction_t tab[] =\r\n\t{\r\n\t\t{(void**)&pxkb_context_new,\t\t\t\t\t\t\"xkb_context_new\"},\r\n\t\t{(void**)&pxkb_keymap_new_from_string,\t\t\t\"xkb_keymap_new_from_string\"},\r\n\t\t{(void**)&pxkb_state_new,\t\t\t\t\t\t\"xkb_state_new\"},\r\n\t\t{(void**)&pxkb_state_update_mask,\t\t\t\t\"xkb_state_update_mask\"},\r\n\t\t{(void**)&pxkb_state_key_get_one_sym,\t\t\t\"xkb_state_key_get_one_sym\"},\r\n\t\t{(void**)&pxkb_keysym_to_utf32,\t\t\t\t\t\"xkb_keysym_to_utf32\"},\r\n\t\t{(void**)&pxkb_state_unref,\t\t\t\t\t\t\"xkb_state_unref\"},\r\n\t\t{(void**)&pxkb_keymap_unref,\t\t\t\t\t\"xkb_keymap_unref\"},\r\n\t\t{NULL, NULL}\r\n\t};\r\n\tlib_xkb = Sys_LoadLibrary(\"libxkbcommon.so.0\", tab);\r\n\treturn !!lib_xkb;\r\n}\r\n\r\n#if defined(GLQUAKE) && defined(USE_EGL)\r\nstatic struct wl_egl_window *(*pwl_egl_window_create)(struct wl_surface *surface, int width, int height);\r\nstatic void (*pwl_egl_window_destroy)(struct wl_egl_window *egl_window);\r\nstatic void (*pwl_egl_window_resize)(struct wl_egl_window *egl_window, int width, int height, int dx, int dy);\r\n//static void (*pwl_egl_window_get_attached_size(struct wl_egl_window *egl_window, int *width, int *height);\r\nstatic dllfunction_t waylandexports_egl[] =\r\n{\r\n\t{(void**)&pwl_egl_window_create,\t\t\"wl_egl_window_create\"},\r\n\t{(void**)&pwl_egl_window_destroy,\t\t\"wl_egl_window_destroy\"},\r\n\t{(void**)&pwl_egl_window_resize,\t\t\"wl_egl_window_resize\"},\r\n//\t{(void**)&pwl_egl_window_get_attached_size,\t\"wl_egl_window_get_attached_size\"},\r\n\t{NULL, NULL}\r\n};\r\nstatic dllhandle_t *lib_wayland_egl;\r\n#endif\r\n\r\n\r\n//I hate wayland.\r\nstatic inline struct wl_region *pwl_compositor_create_region(struct wl_compositor *wl_compositor)\t\t\t\t{return (struct wl_region*)(struct wl_proxy *) pwl_proxy_marshal_constructor((struct wl_proxy *) wl_compositor, WL_COMPOSITOR_CREATE_REGION, pwl_region_interface, NULL);}\r\nstatic inline struct wl_surface *pwl_compositor_create_surface(struct wl_compositor *wl_compositor)\t\t\t\t{return (struct wl_surface *)(struct wl_proxy *) pwl_proxy_marshal_constructor((struct wl_proxy *) wl_compositor, WL_COMPOSITOR_CREATE_SURFACE, pwl_surface_interface, NULL);}\r\nstatic inline void pwl_surface_set_opaque_region(struct wl_surface *wl_surface, struct wl_region *region)\t\t{pwl_proxy_marshal((struct wl_proxy *) wl_surface, WL_SURFACE_SET_OPAQUE_REGION, region);}\r\nstatic inline void pwl_region_add(struct wl_region *wl_region, int32_t x, int32_t y, int32_t width, int32_t height)\t{pwl_proxy_marshal((struct wl_proxy *) wl_region, WL_REGION_ADD, x, y, width, height);}\r\n#ifdef WL_SHELL_NAME\r\nstatic inline struct wl_shell_surface *pwl_shell_get_shell_surface(struct wl_shell *wl_shell, struct wl_surface *surface)\t{return (struct wl_shell_surface *)(struct wl_proxy *) pwl_proxy_marshal_constructor((struct wl_proxy *) wl_shell, WL_SHELL_GET_SHELL_SURFACE, pwl_shell_surface_interface, NULL, surface);}\r\nstatic inline void pwl_shell_surface_set_toplevel(struct wl_shell_surface *wl_shell_surface)\t\t\t\t\t{pwl_proxy_marshal((struct wl_proxy *) wl_shell_surface, WL_SHELL_SURFACE_SET_TOPLEVEL);}\r\nstatic inline void pwl_shell_surface_set_fullscreen(struct wl_shell_surface *wl_shell_surface, uint32_t method, uint32_t framerate, struct wl_output *output)\t{pwl_proxy_marshal((struct wl_proxy *) wl_shell_surface, WL_SHELL_SURFACE_SET_FULLSCREEN, method, framerate, output);}\r\nstatic inline int pwl_shell_surface_add_listener(struct wl_shell_surface *wl_shell_surface, const struct wl_shell_surface_listener *listener, void *data)\t\t{return pwl_proxy_add_listener((struct wl_proxy *) wl_shell_surface, (void (**)(void)) listener, data);}\r\nstatic inline void pwl_shell_surface_pong(struct wl_shell_surface *wl_shell_surface, uint32_t serial)\t\t\t{pwl_proxy_marshal((struct wl_proxy *) wl_shell_surface, WL_SHELL_SURFACE_PONG, serial);}\r\nstatic inline void pwl_shell_surface_set_title(struct wl_shell_surface *wl_shell_surface, const char *title)\t{pwl_proxy_marshal((struct wl_proxy *) wl_shell_surface, WL_SHELL_SURFACE_SET_TITLE, title);}\r\n#endif\r\nstatic inline struct wl_registry *pwl_display_get_registry(struct wl_display *wl_display)\t{return (struct wl_registry *)pwl_proxy_marshal_constructor((struct wl_proxy *) wl_display, WL_DISPLAY_GET_REGISTRY, pwl_registry_interface, NULL);}\r\nstatic inline void *pwl_registry_bind(struct wl_registry *wl_registry, uint32_t name, const struct wl_interface *interface, uint32_t version)\t{return (void*)pwl_proxy_marshal_constructor_versioned((struct wl_proxy *) wl_registry, WL_REGISTRY_BIND, interface, version, name, interface->name, version, NULL);}\r\nstatic inline int pwl_registry_add_listener(struct wl_registry *wl_registry, const struct wl_registry_listener *listener, void *data)\t\t\t{return pwl_proxy_add_listener((struct wl_proxy *) wl_registry, (void (**)(void)) listener, data);}\r\nstatic inline void pwl_keyboard_destroy(struct wl_keyboard *wl_keyboard)\t\t\t{pwl_proxy_destroy((struct wl_proxy *) wl_keyboard);}\r\nstatic inline int pwl_keyboard_add_listener(struct wl_keyboard *wl_keyboard, const struct wl_keyboard_listener *listener, void *data)\t\t\t{return pwl_proxy_add_listener((struct wl_proxy *) wl_keyboard, (void (**)(void)) listener, data);}\r\nstatic inline void pwl_pointer_destroy(struct wl_pointer *wl_pointer)\t\t\t\t{pwl_proxy_destroy((struct wl_proxy *) wl_pointer);}\r\nstatic inline int pwl_pointer_add_listener(struct wl_pointer *wl_pointer, const struct wl_pointer_listener *listener, void *data)\t\t\t\t{return pwl_proxy_add_listener((struct wl_proxy *) wl_pointer, (void (**)(void)) listener, data);}\r\nstatic inline struct wl_pointer *pwl_seat_get_pointer(struct wl_seat *wl_seat)\t\t{return (struct wl_pointer *)(struct wl_proxy *) pwl_proxy_marshal_constructor((struct wl_proxy *) wl_seat, WL_SEAT_GET_POINTER, pwl_pointer_interface, NULL);}\r\nstatic inline struct wl_keyboard *pwl_seat_get_keyboard(struct wl_seat *wl_seat)\t{return (struct wl_keyboard *)(struct wl_proxy *) pwl_proxy_marshal_constructor((struct wl_proxy *) wl_seat, WL_SEAT_GET_KEYBOARD, pwl_keyboard_interface, NULL);}\r\nstatic inline int pwl_seat_add_listener(struct wl_seat *wl_seat, const struct wl_seat_listener *listener, void *data)\t{return pwl_proxy_add_listener((struct wl_proxy *) wl_seat, (void (**)(void)) listener, data);}\r\n\r\nstatic inline struct wl_callback *pwl_surface_frame(struct wl_surface *wl_surface)\t{return (struct wl_callback*)(struct wl_proxy *) pwl_proxy_marshal_constructor((struct wl_proxy *) wl_surface, WL_SURFACE_FRAME, pwl_callback_interface, NULL);}\r\nstatic inline int pwl_callback_add_listener(struct wl_callback *wl_callback, const struct wl_callback_listener *listener, void *data) {return pwl_proxy_add_listener((struct wl_proxy *) wl_callback, (void (**)(void)) listener, data);}\r\n\r\nstatic inline void pwl_pointer_set_cursor(struct wl_pointer *wl_pointer, uint32_t serial, struct wl_surface *surface, int32_t hotspot_x, int32_t hotspot_y)\t\t{ pwl_proxy_marshal((struct wl_proxy *) wl_pointer, WL_POINTER_SET_CURSOR, serial, surface, hotspot_x, hotspot_y); }\r\nstatic inline struct wl_shm_pool *pwl_shm_create_pool(struct wl_shm *wl_shm, int32_t fd, int32_t size)\t\t{ return (struct wl_shm_pool *)pwl_proxy_marshal_constructor((struct wl_proxy *) wl_shm, WL_SHM_CREATE_POOL, pwl_shm_pool_interface, NULL, fd, size); }\r\nstatic inline struct wl_buffer *pwl_shm_pool_create_buffer(struct wl_shm_pool *wl_shm_pool, int32_t offset, int32_t width, int32_t height, int32_t stride, uint32_t format)\t\t{ return (struct wl_buffer *)pwl_proxy_marshal_constructor((struct wl_proxy *) wl_shm_pool, WL_SHM_POOL_CREATE_BUFFER, pwl_buffer_interface, NULL, offset, width, height, stride, format); }\r\nstatic inline void pwl_shm_pool_destroy(struct wl_shm_pool *wl_shm_pool)\t\t{ pwl_proxy_marshal((struct wl_proxy *) wl_shm_pool, WL_SHM_POOL_DESTROY); pwl_proxy_destroy((struct wl_proxy *) wl_shm_pool); }\r\nstatic inline void pwl_surface_commit(struct wl_surface *wl_surface)\t\t{ pwl_proxy_marshal((struct wl_proxy *) wl_surface, WL_SURFACE_COMMIT); }\r\nstatic inline void pwl_buffer_destroy(struct wl_buffer *wl_buffer)\t\t{ pwl_proxy_marshal((struct wl_proxy *) wl_buffer, WL_BUFFER_DESTROY); pwl_proxy_destroy((struct wl_proxy *) wl_buffer); }\r\nstatic inline void pwl_surface_attach(struct wl_surface *wl_surface, struct wl_buffer *buffer, int32_t x, int32_t y)\t\t{ pwl_proxy_marshal((struct wl_proxy *) wl_surface, WL_SURFACE_ATTACH, buffer, x, y); }\r\nstatic inline void pwl_surface_damage(struct wl_surface *wl_surface, int32_t x, int32_t y, int32_t width, int32_t height)\t\t{ pwl_proxy_marshal((struct wl_proxy *) wl_surface, WL_SURFACE_DAMAGE, x, y, width, height); }\r\nstatic inline void pwl_surface_destroy(struct wl_surface *wl_surface)\t\t{ pwl_proxy_marshal((struct wl_proxy *) wl_surface, WL_SURFACE_DESTROY); pwl_proxy_destroy((struct wl_proxy *) wl_surface); }\r\n\r\n#else\r\n#define pwl_keyboard_interface\t\t&wl_keyboard_interface\r\n#define pwl_pointer_interface\t\t&wl_pointer_interface\r\n#define pwl_compositor_interface\t&wl_compositor_interface\r\n#define pwl_registry_interface\t\t&wl_registry_interface\r\n#define pwl_region_interface\t\t&wl_region_interface\r\n#define pwl_surface_interface\t\t&wl_surface_interface\r\n#ifdef WL_SHELL_NAME\r\n#define pwl_shell_surface_interface\t&wl_shell_surface_interface\r\n#define pwl_shell_interface\t\t\t&wl_shell_interface\r\n#endif\r\n#define pwl_seat_interface\t\t\t&wl_seat_interface\r\n#define pwl_output_interface\t\t&wl_output_interface\r\n#define pwl_shm_interface\t\t\t&wl_shm_interface\r\n\r\n#define pwl_display_connect\t\t\t\t\twl_display_connect\r\n#define pwl_display_disconnect\t\t\t\twl_display_disconnect\r\n#define pwl_display_dispatch\t\t\t\twl_display_dispatch\r\n#define pwl_display_dispatch_pending\t\twl_display_dispatch_pending\r\n#define pwl_display_roundtrip\t\t\t\twl_display_roundtrip\r\n\r\n#define pwl_proxy_marshal\t\t\t\t\twl_proxy_marshal\r\n#define pwl_proxy_marshal_constructor\t\twl_proxy_marshal_constructor\r\n#define pwl_proxy_marshal_constructor_versioned\t\twl_proxy_marshal_constructor_versioned\r\n#define pwl_proxy_destroy\t\t\t\t\twl_proxy_destroy\r\n#define pwl_proxy_add_listener\t\t\t\twl_proxy_add_listener\r\n\r\n#define pwl_compositor_create_region\t\twl_compositor_create_region\r\n#define pwl_compositor_create_surface\t\twl_compositor_create_surface\r\n#define pwl_surface_set_opaque_region\t\twl_surface_set_opaque_region\r\n#define pwl_region_add\t\t\t\t\t\twl_region_add\r\n#define pwl_shell_get_shell_surface\t\t\twl_shell_get_shell_surface\r\n#define pwl_shell_surface_set_toplevel\t\twl_shell_surface_set_toplevel\r\n#define pwl_shell_surface_set_fullscreen\twl_shell_surface_set_fullscreen\r\n#define pwl_shell_surface_add_listener\t\twl_shell_surface_add_listener\r\n#define pwl_shell_surface_pong\t\t\t\twl_shell_surface_pong\r\n#define pwl_shell_surface_set_title\t\t\twl_shell_surface_set_title\r\n#define pwl_display_get_registry\t\t\twl_display_get_registry\r\n#define pwl_registry_bind\t\t\t\t\twl_registry_bind\r\n#define pwl_registry_add_listener\t\t\twl_registry_add_listener\r\n#define pwl_keyboard_destroy\t\t\t\twl_keyboard_destroy\r\n#define pwl_keyboard_add_listener\t\t\twl_keyboard_add_listener\r\n#define pwl_pointer_destroy\t\t\t\t\twl_pointer_destroy\r\n#define pwl_pointer_add_listener\t\t\twl_pointer_add_listener\r\n#define pwl_seat_get_pointer\t\t\t\twl_seat_get_pointer\r\n#define pwl_seat_get_keyboard\t\t\t\twl_seat_get_keyboard\r\n#define pwl_seat_add_listener\t\t\t\twl_seat_add_listener\r\n#define pwl_surface_frame\t\t\t\t\twl_surface_frame\r\n#define pwl_callback_add_listener\t\t\twl_callback_add_listener\r\n#define pwl_pointer_set_cursor\t\t\t\twl_pointer_set_cursor\r\n#define pwl_shm_create_pool\t\t\t\t\twl_shm_create_pool\r\n#define pwl_shm_pool_create_buffer\t\t\twl_shm_pool_create_buffer\r\n#define pwl_shm_pool_destroy\t\t\t\twl_shm_pool_destroy\r\n#define pwl_surface_commit\t\t\t\t\twl_surface_commit\r\n#define pwl_buffer_destroy\t\t\t\t\twl_buffer_destroy\r\n#define pwl_surface_attach\t\t\t\t\twl_surface_attach\r\n#define pwl_surface_damage\t\t\t\t\twl_surface_damage\r\n#define pwl_surface_destroy\t\t\t\t\twl_surface_destroy\r\n\r\n#define pwl_egl_window_create\t\t\t\twl_egl_window_create\r\n#define pwl_egl_window_destroy\t\t\t\twl_egl_window_destroy\r\n#define pwl_egl_window_resize\t\t\t\twl_egl_window_resize\r\n\r\n#define pxkb_context_new\t\t\t\t\txkb_context_new\r\n#define pxkb_keymap_new_from_string\t\t\txkb_keymap_new_from_string\r\n#define pxkb_state_new\t\t\t\t\t\txkb_state_new\r\n#define pxkb_state_key_get_one_sym\t\t\txkb_state_key_get_one_sym\r\n#define pxkb_keysym_to_utf32\t\t\t\txkb_keysym_to_utf32\r\n#define pxkb_state_unref\t\t\t\t\txkb_state_unref\r\n#define pxkb_keymap_unref\t\t\t\t\txkb_keymap_unref\r\n#define pxkb_state_update_mask\t\t\t\txkb_state_update_mask\r\n\r\n#endif\r\nstatic const struct wl_interface *pzwp_relative_pointer_v1_interface;\r\nstatic const struct wl_interface *pzwp_locked_pointer_v1_interface;\r\n\r\nstruct cursorinfo_s\r\n{\r\n\tstruct wl_surface *surf;\t//the surface used as a cursor.\r\n\tstruct wl_buffer *buf;\t\t//can't destroy this too early, or the server bugs out.\r\n\tint hot_x, hot_y;\t\t\t//hotspot stuff\r\n\tint width, height;\t\t\t//for invalidating the surface to avoid server bugs.\r\n};\r\n\r\nstatic struct wdisplay_s\r\n{\r\n\t//display stuff\r\n\tstruct wl_display *display;\t//manager\r\n\tstruct wl_registry *registry;\t//manager\r\n\tstruct wl_compositor *compositor;\t//manager\r\n\tstruct wl_shm *shm;\t//manager\r\n\r\n\t//seat stuff\r\n\tvoid *pointer;\r\n\tqboolean cursorfocus;\t//says that we 'own' the hardware cursor\r\n\tuint32_t cursorserial;\t//required for updating the cursor image\r\n\tstruct cursorinfo_s *cursor;\r\n\tstruct wl_keyboard *keyboard;\r\n\tstruct wl_seat *seat;\r\n\r\n\t//because wayland really doesn't help when it comes to various keymaps\r\n\tstruct xkb_context *xkb_context;\r\n\tstruct xkb_state *xkb_state;\r\n\t//to try to fix mouse stuff\r\n\tqboolean relative_mouse_active;\r\n#ifdef WP_POINTER_CONSTRAINTS_NAME\r\n\tstruct zwp_pointer_constraints_v1 *pointer_constraints;\r\n\tstruct zwp_locked_pointer_v1 *locked_pointer;\r\n#endif\r\n#ifdef WP_RELATIVE_POINTER_MANAGER_NAME\r\n\tstruct zwp_relative_pointer_manager_v1 *relative_pointer_manager;\r\n\tstruct zwp_relative_pointer_v1 *relative_pointer;\r\n#endif\r\n\r\n\t//stupid csd crap to work around shitty wayland servers\r\n\tint truewidth;\t//not really needed, but present for consistency\r\n\tint trueheight;\r\n\tint csdsize;\r\n\tchar *csdcaption;\r\n\tqboolean hasssd;\t//probably false on gnome.\r\n\tint mousex,mousey;\r\n\r\n\t//window stuff\r\n#if defined(GLQUAKE) && defined(USE_EGL)\r\n\tstruct wl_egl_window *enwindow;\r\n#endif\r\n\tstruct wl_surface *surface;\r\n\r\n#ifdef XDG_SHELL_NAME\r\n\tstruct xdg_toplevel *xdg_toplevel;\r\n\tstruct xdg_surface *xdg_surface;\r\n\tstruct xdg_wm_base *xdg_wm_base;\t//manager\r\n#endif\r\n#ifdef WL_SHELL_NAME\r\n\tstruct wl_shell_surface *ssurface;\r\n\tstruct wl_shell *shell;\t//manager\r\n#endif\r\n\tqboolean wait_for_configure;\t//WL is being slow and won't let us redraw yet.\r\n\tqboolean waylandisblocking;\r\n\r\n\t//belated sanity stuff\r\n#ifdef XDG_DECORATION_MANAGER_NAME\r\n\tstruct zxdg_toplevel_decoration_v1 *xdg_decoration;\r\n\tstruct zxdg_decoration_manager_v1 *xdg_decoration_manager;\r\n#endif\r\n#ifdef ORG_KDE_KWIN_SERVER_DECORATION_MANAGER_NAME\r\n\tstruct org_kde_kwin_server_decoration *kwin_decoration;\r\n\tstruct org_kde_kwin_server_decoration_manager *kwin_decoration_manager;\r\n#endif\r\n} w;\r\nstatic void WL_SetCaption(const char *text);\r\n\r\n\r\n\r\n#ifdef XDG_SHELL_NAME\r\nstatic const struct wl_interface *pxdg_surface_interface;\r\nstatic const struct wl_interface *pxdg_toplevel_interface;\r\nstatic const struct wl_interface *pxdg_wm_base_interface;\r\nstatic void WL_Setup_XDG_Shell(void)\r\n{\r\n\tstatic const struct wl_interface *types[24];\r\n\r\n\tstatic const struct wl_message xdg_wm_base_requests[] = {\r\n\t\t{ \"destroy\", \"\", types + 0 },\r\n\t\t{ \"create_positioner\", \"n\", types + 4 },\r\n\t\t{ \"get_xdg_surface\", \"no\", types + 5 },\r\n\t\t{ \"pong\", \"u\", types + 0 },\r\n\t};\r\n\tstatic const struct wl_message xdg_wm_base_events[] = {\r\n\t\t{ \"ping\", \"u\", types + 0 },\r\n\t};\r\n\r\n\tstatic const struct wl_interface xdg_wm_base_interface = {\r\n#ifdef XDG_SHELL_UNSTABLE\r\n\t\t\"zxdg_wm_base_v6\", 1,\r\n#else\r\n\t\t\"xdg_wm_base\", 2,\r\n#endif\r\n\t\t4, xdg_wm_base_requests,\r\n\t\t1, xdg_wm_base_events,\r\n\t};\r\n\r\n\tstatic const struct wl_message xdg_positioner_requests[] = {\r\n\t\t{ \"destroy\", \"\", types + 0 },\r\n\t\t{ \"set_size\", \"ii\", types + 0 },\r\n\t\t{ \"set_anchor_rect\", \"iiii\", types + 0 },\r\n\t\t{ \"set_anchor\", \"u\", types + 0 },\r\n\t\t{ \"set_gravity\", \"u\", types + 0 },\r\n\t\t{ \"set_constraint_adjustment\", \"u\", types + 0 },\r\n\t\t{ \"set_offset\", \"ii\", types + 0 },\r\n\t};\r\n\r\n\tstatic const struct wl_interface xdg_positioner_interface = {\r\n#ifdef XDG_SHELL_UNSTABLE\r\n\t\t\"zxdg_positioner_v6\", 1,\r\n#else\r\n\t\t\"xdg_positioner\", 2,\r\n#endif\r\n\t\t7, xdg_positioner_requests,\r\n\t\t0, NULL,\r\n\t};\r\n\r\n\tstatic const struct wl_message xdg_surface_requests[] = {\r\n\t\t{ \"destroy\", \"\", types + 0 },\r\n\t\t{ \"get_toplevel\", \"n\", types + 7 },\r\n#ifdef XDG_SHELL_UNSTABLE\r\n\t\t{ \"get_popup\", \"noo\", types + 8 },\r\n#else\r\n\t\t{ \"get_popup\", \"n?oo\", types + 8 },\r\n#endif\r\n\t\t{ \"set_window_geometry\", \"iiii\", types + 0 },\r\n\t\t{ \"ack_configure\", \"u\", types + 0 },\r\n\t};\r\n\r\n\tstatic const struct wl_message xdg_surface_events[] = {\r\n\t\t{ \"configure\", \"u\", types + 0 },\r\n\t};\r\n\r\n\tstatic const struct wl_interface xdg_surface_interface = {\r\n#ifdef XDG_SHELL_UNSTABLE\r\n\t\t\"zxdg_surface_v6\", 1,\r\n#else\r\n\t\t\"xdg_surface\", 2,\r\n#endif\r\n\t\t5, xdg_surface_requests,\r\n\t\t1, xdg_surface_events,\r\n\t};\r\n\r\n\tstatic const struct wl_message xdg_toplevel_requests[] = {\r\n\t\t{ \"destroy\", \"\", types + 0 },\r\n\t\t{ \"set_parent\", \"?o\", types + 11 },\r\n\t\t{ \"set_title\", \"s\", types + 0 },\r\n\t\t{ \"set_app_id\", \"s\", types + 0 },\r\n\t\t{ \"show_window_menu\", \"ouii\", types + 12 },\r\n\t\t{ \"move\", \"ou\", types + 16 },\r\n\t\t{ \"resize\", \"ouu\", types + 18 },\r\n\t\t{ \"set_max_size\", \"ii\", types + 0 },\r\n\t\t{ \"set_min_size\", \"ii\", types + 0 },\r\n\t\t{ \"set_maximized\", \"\", types + 0 },\r\n\t\t{ \"unset_maximized\", \"\", types + 0 },\r\n\t\t{ \"set_fullscreen\", \"?o\", types + 21 },\r\n\t\t{ \"unset_fullscreen\", \"\", types + 0 },\r\n\t\t{ \"set_minimized\", \"\", types + 0 },\r\n\t};\r\n\r\n\tstatic const struct wl_message xdg_toplevel_events[] = {\r\n\t\t{ \"configure\", \"iia\", types + 0 },\r\n\t\t{ \"close\", \"\", types + 0 },\r\n\t};\r\n\r\n\tstatic const struct wl_interface xdg_toplevel_interface = {\r\n#ifdef XDG_SHELL_UNSTABLE\r\n\t\t\"zxdg_toplevel_v6\", 1,\r\n#else\r\n\t\t\"xdg_toplevel\", 2,\r\n#endif\r\n\t\t14, xdg_toplevel_requests,\r\n\t\t2, xdg_toplevel_events,\r\n\t};\r\n\r\n\tstatic const struct wl_message xdg_popup_requests[] = {\r\n\t\t{ \"destroy\", \"\", types + 0 },\r\n\t\t{ \"grab\", \"ou\", types + 22 },\r\n\t};\r\n\tstatic const struct wl_message xdg_popup_events[] = {\r\n\t\t{ \"configure\", \"iiii\", types + 0 },\r\n\t\t{ \"popup_done\", \"\", types + 0 },\r\n\t};\r\n\r\n\tstatic const struct wl_interface xdg_popup_interface = {\r\n#ifdef UNSTABLE\r\n\t\t\"zxdg_popup_v6\", 1,\r\n#else\r\n\t\t\"xdg_popup\", 2,\r\n#endif\r\n\t\t2, xdg_popup_requests,\r\n\t\t2, xdg_popup_events,\r\n\t};\r\n\r\n\ttypes[4] = &xdg_positioner_interface;\r\n\ttypes[5] = &xdg_surface_interface;\r\n\ttypes[6] = pwl_surface_interface;\r\n\ttypes[7] = &xdg_toplevel_interface;\r\n\ttypes[8] = &xdg_popup_interface;\r\n\ttypes[9] = &xdg_surface_interface;\r\n\ttypes[10] = &xdg_positioner_interface;\r\n\ttypes[11] = &xdg_toplevel_interface;\r\n\ttypes[12] = pwl_seat_interface;\r\n\ttypes[16] = pwl_seat_interface;\r\n\ttypes[18] = pwl_seat_interface;\r\n\ttypes[21] = pwl_output_interface;\r\n\ttypes[22] = pwl_seat_interface;\r\n\r\n\tpxdg_surface_interface = &xdg_surface_interface;\r\n\tpxdg_toplevel_interface = &xdg_toplevel_interface;\r\n\tpxdg_wm_base_interface = &xdg_wm_base_interface;\r\n}\r\n#define XDG_WM_BASE_DESTROY 0\r\n#define XDG_WM_BASE_CREATE_POSITIONER 1\r\n#define XDG_WM_BASE_GET_XDG_SURFACE 2\r\n#define XDG_WM_BASE_PONG 3\r\nstruct xdg_wm_base_listener {\r\n    void (*ping)(void *data, struct xdg_wm_base *xdg_wm_base, uint32_t serial);\r\n};\r\nstatic inline int xdg_wm_base_add_listener(struct xdg_wm_base *xdg_wm_base, const struct xdg_wm_base_listener *listener, void *data)\t\t{ return pwl_proxy_add_listener((struct wl_proxy *) xdg_wm_base, (void (**)(void)) listener, data); }\r\nstatic inline struct xdg_surface *xdg_wm_base_get_xdg_surface(struct xdg_wm_base *xdg_wm_base, struct wl_surface *surface)\t\t{ return (struct xdg_surface *)pwl_proxy_marshal_constructor((struct wl_proxy *) xdg_wm_base, XDG_WM_BASE_GET_XDG_SURFACE, pxdg_surface_interface, NULL, surface); }\r\nstatic inline void xdg_wm_base_pong(struct xdg_wm_base *xdg_wm_base, uint32_t serial)\t\t{ pwl_proxy_marshal((struct wl_proxy *) xdg_wm_base, XDG_WM_BASE_PONG, serial); }\r\n\r\n#define XDG_SURFACE_DESTROY 0\r\n#define XDG_SURFACE_GET_TOPLEVEL 1\r\n#define XDG_SURFACE_GET_POPUP 2\r\n#define XDG_SURFACE_SET_WINDOW_GEOMETRY 3\r\n#define XDG_SURFACE_ACK_CONFIGURE 4\r\nstruct xdg_surface_listener {\r\n    void (*configure)(void *data, struct xdg_surface *xdg_surface, uint32_t serial);\r\n};\r\nstatic inline int xdg_surface_add_listener(struct xdg_surface *xdg_surface, const struct xdg_surface_listener *listener, void *data) { return pwl_proxy_add_listener((struct wl_proxy *) xdg_surface, (void (**)(void)) listener, data); }\r\nstatic inline struct xdg_toplevel *xdg_surface_get_toplevel(struct xdg_surface *xdg_surface)\t\t{ return (struct xdg_toplevel *)pwl_proxy_marshal_constructor((struct wl_proxy *) xdg_surface, XDG_SURFACE_GET_TOPLEVEL, pxdg_toplevel_interface, NULL); }\r\nstatic inline void xdg_surface_ack_configure(struct xdg_surface *xdg_surface, uint32_t serial)\t\t{ pwl_proxy_marshal((struct wl_proxy *) xdg_surface, XDG_SURFACE_ACK_CONFIGURE, serial); }\r\n\r\n#define XDG_TOPLEVEL_DESTROY 0\r\n#define XDG_TOPLEVEL_SET_PARENT 1\r\n#define XDG_TOPLEVEL_SET_TITLE 2\r\n#define XDG_TOPLEVEL_SET_APP_ID 3\r\n#define XDG_TOPLEVEL_SHOW_WINDOW_MENU 4\r\n#define XDG_TOPLEVEL_MOVE 5\r\n#define XDG_TOPLEVEL_RESIZE 6\r\n#define XDG_TOPLEVEL_SET_MAX_SIZE 7\r\n#define XDG_TOPLEVEL_SET_MIN_SIZE 8\r\n#define XDG_TOPLEVEL_SET_MAXIMIZED 9\r\n#define XDG_TOPLEVEL_UNSET_MAXIMIZED 10\r\n#define XDG_TOPLEVEL_SET_FULLSCREEN 11\r\n#define XDG_TOPLEVEL_UNSET_FULLSCREEN 12\r\n#define XDG_TOPLEVEL_SET_MINIMIZED 13\r\nstruct xdg_toplevel_listener;\r\nstatic inline void xdg_toplevel_set_title(struct xdg_toplevel *xdg_toplevel, const char *title)\t\t{ pwl_proxy_marshal((struct wl_proxy *) xdg_toplevel, XDG_TOPLEVEL_SET_TITLE, title); }\r\nstatic inline int xdg_toplevel_add_listener(struct xdg_toplevel *xdg_toplevel, const struct xdg_toplevel_listener *listener, void *data)\t\t{ return pwl_proxy_add_listener((struct wl_proxy *) xdg_toplevel, (void (**)(void)) listener, data); }\r\nstatic inline void xdg_toplevel_set_min_size(struct xdg_toplevel *xdg_toplevel, int32_t width, int32_t height)\t\t{ pwl_proxy_marshal((struct wl_proxy *) xdg_toplevel, XDG_TOPLEVEL_SET_MIN_SIZE, width, height); }\r\nstatic inline void xdg_toplevel_move(struct xdg_toplevel *xdg_toplevel, struct wl_seat *seat, uint32_t serial)\t\t{ pwl_proxy_marshal((struct wl_proxy *) xdg_toplevel, XDG_TOPLEVEL_MOVE, seat, serial); }\r\nstatic inline void xdg_toplevel_show_window_menu(struct xdg_toplevel *xdg_toplevel, struct wl_seat *seat, uint32_t serial, int32_t x, int32_t y)\t\t{ pwl_proxy_marshal((struct wl_proxy *) xdg_toplevel, XDG_TOPLEVEL_SHOW_WINDOW_MENU, seat, serial, x, y); }\r\nstatic inline void xdg_toplevel_set_fullscreen(struct xdg_toplevel *xdg_toplevel, struct wl_output *output)\t\t{ pwl_proxy_marshal((struct wl_proxy *) xdg_toplevel, XDG_TOPLEVEL_SET_FULLSCREEN, output); }\r\n\r\nstatic void xdg_wm_base_handle_ping(void *data, struct xdg_wm_base *xdg_wm_base, uint32_t serial)\r\n{\r\n\txdg_wm_base_pong(xdg_wm_base, serial);\r\n}\r\nstatic const struct xdg_wm_base_listener myxdg_wm_base_listener =\r\n{\r\n\txdg_wm_base_handle_ping,\r\n};\r\n\r\nstatic void xdg_surface_handle_configure(void *data, struct xdg_surface *xdg_surface, uint32_t serial)\r\n{\r\n\txdg_surface_ack_configure(xdg_surface, serial);\r\n\r\n\tw.wait_for_configure = false; //all good now\r\n}\r\nstatic const struct xdg_surface_listener myxdg_surface_listener =\r\n{\r\n\txdg_surface_handle_configure,\r\n};\r\n\r\nvoid xdg_toplevel_handle_configure(void *data, struct xdg_toplevel *xdg_toplevel, int32_t width, int32_t height, struct wl_array *states)\r\n{\r\n\tif (!width || !height)\r\n\t{\r\n\t\twidth = w.truewidth;\r\n\t\theight = w.trueheight;\r\n\t}\r\n\r\n#if defined(GLQUAKE) && defined(USE_EGL)\r\n\tif (w.enwindow)\r\n\t\tpwl_egl_window_resize(w.enwindow, width, height, 0, 0);\r\n#endif\r\n\r\n\tw.truewidth = width;\r\n\tw.trueheight = height;\r\n\tif (vid.pixelwidth != width || vid.pixelheight != height-w.csdsize)\r\n\t{\r\n\t\tvid.pixelwidth = width;\r\n\t\tvid.pixelheight = w.trueheight-w.csdsize;\r\n\t\tCvar_ForceCallback(&vid_conautoscale);\r\n\t}\r\n}\r\nvoid xdg_toplevel_handle_close(void *data, struct xdg_toplevel *xdg_toplevel)\r\n{\r\n\t//fixme: steal focus, bring to top, or just hope the compositor is doing that for us when the user right-clicked the task bar of our minimised app or whatever.\r\n\tM_Window_ClosePrompt();\r\n}\r\nstatic struct xdg_toplevel_listener {\r\n    void (*configure)(void *data, struct xdg_toplevel *xdg_toplevel, int32_t width, int32_t height, struct wl_array *states);\r\n    void (*close)(void *data, struct xdg_toplevel *xdg_toplevel);\r\n} myxdg_toplevel_listener =\r\n{\r\n\txdg_toplevel_handle_configure,\r\n\txdg_toplevel_handle_close\r\n};\r\n\r\n#endif\r\n\r\n#ifdef WL_SHELL_NAME\r\nstatic void WL_shell_handle_ping(void *data, struct wl_shell_surface *shell_surface, uint32_t serial)\r\n{\r\n\tpwl_shell_surface_pong(shell_surface, serial);\r\n}\r\nstatic void WL_shell_handle_configure(void *data, struct wl_shell_surface *shell_surface, uint32_t edges, int32_t width, int32_t height)\r\n{\r\n#if defined(GLQUAKE) && defined(USE_EGL)\r\n\tif (w.enwindow)\r\n\t\tpwl_egl_window_resize(w.enwindow, width, height, 0, 0);\r\n#endif\r\n\r\n\tw.truewidth = width;\r\n\tw.trueheight = height;\r\n\tif (vid.pixelwidth != width || vid.pixelheight != height-w.csdsize)\r\n\t{\r\n\t\tvid.pixelwidth = width;\r\n\t\tvid.pixelheight = height-w.csdsize;\r\n\t\tCvar_ForceCallback(&vid_conautoscale);\r\n\t}\r\n\r\n\tw.wait_for_configure = false; //all good now\r\n}\r\nstatic void WL_shell_handle_popup_done(void *data, struct wl_shell_surface *shell_surface)\r\n{\r\n}\r\n\r\nstatic const struct wl_shell_surface_listener shell_surface_listener =\r\n{\r\n\tWL_shell_handle_ping,\r\n\tWL_shell_handle_configure,\r\n\tWL_shell_handle_popup_done\r\n};\r\n#endif\r\n\r\n//qkeys are ascii-compatible for the most part.\r\nstatic unsigned short waylandinputsucksbighairydonkeyballs[] =\r\n{\r\n\t0, \tK_ESCAPE,'1','2','3','4','5','6',\t//0x\r\n\t'7','8','9','0','-','=',K_BACKSPACE,K_TAB,\r\n\t'q','w','e','r','t','y','u','i',\t\t//1x\r\n\t'o','p','[',']',K_ENTER,K_LCTRL,'a', 's',\r\n\t'd','f','g','h','j','k','l',';',\t\t//2x\r\n\t'\\'','`',K_LSHIFT,'#','z','x','c','v',\r\n\t'b','n','m',',','.','/',K_RSHIFT,K_KP_STAR,\t//3x\r\n\tK_LALT,' ',K_CAPSLOCK,K_F1,K_F2,K_F3,K_F4,K_F5,\r\n\tK_F6,K_F7,K_F8,K_F9,K_F10,K_KP_NUMLOCK,K_SCRLCK,K_KP_HOME,//4x\r\n\tK_KP_UPARROW,K_KP_PGUP,K_KP_MINUS,K_KP_LEFTARROW,K_KP_5,K_KP_RIGHTARROW,K_KP_PLUS,K_KP_END,\r\n\tK_KP_DOWNARROW,K_KP_PGDN,K_KP_INS,K_KP_DEL,0,0,'\\\\',K_F11,\t//5x\r\n\tK_F12,0,0,0,0,0,0,0,\r\n\tK_KP_ENTER,K_RCTRL,K_KP_SLASH,K_PRINTSCREEN,K_RALT,0,K_HOME,K_UPARROW,\t//6x\r\n\tK_PGUP,K_LEFTARROW,K_RIGHTARROW,K_END,K_DOWNARROW,K_PGDN,K_INS,K_DEL,\r\n\t0,0,0,0,0,0,0,K_PAUSE,\t//7x\r\n\t0,0,0,0,0,K_LWIN,K_RWIN,K_APP\r\n};\r\nstatic unsigned short waylandinputsucksbighairydonkeyballsshift[] =\r\n{\r\n\t0, \tK_ESCAPE,'!','\\\"','3','$','%','^',\t//0x\r\n\t'&','*','(',')','_','+',K_BACKSPACE,K_TAB,\r\n\t'Q','W','E','R','T','Y','U','I',\t\t//1x\r\n\t'O','P','{','}',K_ENTER,K_LCTRL,'A', 'S',\r\n\t'D','F','G','H','J','K','L',':',\t\t//2x\r\n\t'@','`',K_LSHIFT,'~','Z','X','C','V',\r\n\t'B','N','M','<','>','?',K_RSHIFT,K_KP_STAR,\t//3x\r\n\tK_LALT,' ',K_CAPSLOCK,K_F1,K_F2,K_F3,K_F4,K_F5,\r\n\tK_F6,K_F7,K_F8,K_F9,K_F10,K_KP_NUMLOCK,K_SCRLCK,K_KP_HOME,//4x\r\n\tK_KP_UPARROW,K_KP_PGUP,K_KP_MINUS,K_KP_LEFTARROW,K_KP_5,K_KP_RIGHTARROW,K_KP_PLUS,K_KP_END,\r\n\tK_KP_DOWNARROW,K_KP_PGDN,K_KP_INS,K_KP_DEL,0,0,'|',K_F11,\t//5x\r\n\tK_F12,0,0,0,0,0,0,0,\r\n\tK_KP_ENTER,K_RCTRL,K_KP_SLASH,K_PRINTSCREEN,K_RALT,0,K_HOME,K_UPARROW,\t//6x\r\n\tK_PGUP,K_LEFTARROW,K_RIGHTARROW,K_END,K_DOWNARROW,K_PGDN,K_INS,K_DEL,\r\n\t0,0,0,0,0,0,0,K_PAUSE,\t//7x\r\n\t0,0,0,0,0,K_LWIN,K_RWIN,K_APP\t\r\n};\r\n\r\nstatic void WL_SetHWCursor(void)\r\n{\t//called when the hardware cursor needs to be re-applied\r\n\r\n//\tstruct wl_buffer *buffer;\r\n//\tstruct wl_cursor *cursor = s->default_cursor;\r\n//\tstruct wl_cursor_image *image;\r\n\r\n\tif (!w.cursorfocus)\r\n\t\treturn;\t//nope, we don't own the cursor, don't try changing it.\r\n\r\n\t/*'If surface is NULL, the pointer image is hidden'*/\r\n\tif (!w.relative_mouse_active && !w.locked_pointer && w.cursor)\r\n\t{\r\n\t\tstruct cursorinfo_s *c = w.cursor;\r\n\t\t//weston bugs out if we don't redo this junk\r\n\t\tpwl_surface_attach(c->surf, c->buf, 0, 0);\r\n\t\tpwl_surface_damage(c->surf, 0, 0, c->width, c->height);\r\n\t\tpwl_surface_commit(c->surf);\r\n\r\n\t\t//now actually set it.\r\n\t\tpwl_pointer_set_cursor(w.pointer, w.cursorserial, c->surf, c->hot_x, c->hot_y);\r\n\t}\r\n\telse\r\n\t\tpwl_pointer_set_cursor(w.pointer, w.cursorserial, NULL, 0, 0);\r\n}\r\nvoid *WL_CreateCursor(const qbyte *imagedata, int width, int height, uploadfmt_t format, float hotx, float hoty, float scale)\r\n{\r\n\tstruct wl_surface *surf = NULL;\r\n\tenum wl_shm_format shmfmt;\r\n\tint pbytes;\r\n\tstruct wl_shm_pool *pool;\r\n\tstruct wl_buffer *buffer = NULL;\r\n\tqbyte *outdata;\r\n\tsize_t size;\r\n\tconst char *xdg_runtime_dir = getenv (\"XDG_RUNTIME_DIR\");\r\n\tint fd;\r\n\tstruct cursorinfo_s *c;\r\n\tstatic qboolean allowfmts[PTI_MAX] = {[PTI_BGRX8]=true, [PTI_BGRA8]=true};\t//FIXME: populate via wl_shm_add_listener instead of using formats that we THINK will work.\r\n\tstruct pendingtextureinfo mips = {};\r\n\r\n\tif (!w.shm)\r\n\t\treturn NULL;\t//can't shm the image to the server\r\n\r\n\tmips.mipcount = 1;\r\n\tmips.type = PTI_2D;\r\n\tmips.encoding = format;\r\n\tmips.extrafree = NULL;\r\n\r\n\tmips.mip[0].width = width*scale;\r\n\tmips.mip[0].height = height*scale;\r\n\tmips.mip[0].depth = 1;\r\n\tmips.mip[0].data = Image_ResampleTexture(format, imagedata, width, height, NULL, mips.mip[0].width, mips.mip[0].height);\r\n\tmips.mip[0].needfree = true;\r\n\tmips.mip[0].datasize = 0;\r\n\tif (!allowfmts[mips.encoding])\r\n\t\tImage_ChangeFormat(&mips, allowfmts, format, \"cursor\");\r\n\tif (!allowfmts[mips.encoding])\r\n\t\treturn NULL;\t//failure...\r\n\r\n\tswitch(mips.encoding)\r\n\t{\t//fte favours byte orders, while packed formats are ordered as hex numbers would be\r\n\t\t//wayland formats are as hex (explicitly little-endian, so byteswapped)\r\n\tcase PTI_RGBA8:\tpbytes = 4; shmfmt = WL_SHM_FORMAT_ABGR8888;\tbreak;\r\n\tcase PTI_RGBX8:\tpbytes = 4; shmfmt = WL_SHM_FORMAT_XBGR8888;\tbreak;\r\n\tcase PTI_LLLA8: //just fall through\r\n\tcase PTI_BGRA8:\tpbytes = 4; shmfmt = WL_SHM_FORMAT_ARGB8888;\tbreak;\r\n\tcase PTI_LLLX8: //just fall through\r\n\tcase PTI_BGRX8:\tpbytes = 4; shmfmt = WL_SHM_FORMAT_XRGB8888;\tbreak;\r\n\tcase PTI_RGB8:\tpbytes = 3; shmfmt = WL_SHM_FORMAT_BGR888;\t\tbreak;\r\n\tcase PTI_BGR8:\tpbytes = 3; shmfmt = WL_SHM_FORMAT_RGB888;\t\tbreak;\r\n\r\n\tcase PTI_RGB565:pbytes = 2; shmfmt = WL_SHM_FORMAT_RGB565;\t\tbreak;\r\n\tcase PTI_A2BGR10:pbytes= 4; shmfmt = WL_SHM_FORMAT_ABGR2101010;\tbreak;\r\n\tcase PTI_L8:\r\n\tdefault:\r\n\t\tSys_Error(\"WL_CreateCursor: converted to unsupported pixel format %i\", format);\r\n\t\treturn NULL;\t//failure. can't convert from that format.\r\n\t}\r\n\r\n\tImage_Premultiply(&mips);\r\n\r\n\tfd = open (xdg_runtime_dir, __O_TMPFILE|O_RDWR|O_EXCL, 0600);\r\n\tsize = mips.mip[0].width * mips.mip[0].height * pbytes;\r\n\tif (fd >= 0)\r\n\t{\r\n\t\tftruncate (fd, size);\r\n\t\toutdata = mmap (NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);\r\n\t\tif (outdata)\r\n\t\t{\r\n\t\t\tmemcpy(outdata, mips.mip[0].data, mips.mip[0].width*mips.mip[0].height*pbytes);\r\n\t\t\tpool = pwl_shm_create_pool (w.shm, fd, size);\r\n\t\t\tif (pool)\r\n\t\t\t{\r\n\t\t\t\tbuffer = pwl_shm_pool_create_buffer (pool, 0, mips.mip[0].width, mips.mip[0].height, mips.mip[0].width*pbytes, shmfmt);\r\n\t\t\t\tif (buffer)\r\n\t\t\t\t{\r\n\t\t\t\t\tsurf = pwl_compositor_create_surface(w.compositor);\r\n\t\t\t\t\tif (surf)\r\n\t\t\t\t\t{\t//yay! something worked for once!\r\n\t\t\t\t\t\tpwl_surface_attach(surf, buffer, 0, 0);\r\n\t\t\t\t\t\tpwl_surface_damage(surf, 0, 0, mips.mip[0].width, mips.mip[0].height);\r\n\t\t\t\t\t\tpwl_surface_commit(surf);\r\n\t\t\t\t\t}\r\n\t\t\t\t\t//can't destroy the buffer too early\r\n\t\t\t\t}\r\n\t\t\t\tpwl_shm_pool_destroy (pool);\t//nor that\r\n\t\t\t}\r\n\t\t\tmunmap(outdata, size);\t//should be serverside by now\r\n\t\t}\r\n\t\tclose (fd);\t//nor that...\r\n\t}\r\n\tif (surf)\r\n\t{\r\n\t\tc = Z_Malloc(sizeof(*c));\r\n\t\tc->surf = surf;\r\n\t\tc->buf = buffer;\r\n\t\tc->hot_x = hotx;\r\n\t\tc->hot_y = hoty;\r\n\t\tc->width = mips.mip[0].width;\r\n\t\tc->height = mips.mip[0].height;\r\n\t\treturn c;\r\n\t}\r\n\tif (buffer)\r\n\t\tpwl_buffer_destroy(buffer);\t//don't need to track that any more\r\n\r\n\t//free it conversions required it.\r\n\tif (mips.mip[0].needfree)\r\n\t\tZ_Free(mips.mip[0].data);\r\n\treturn NULL;\r\n}\r\nqboolean WL_SetCursor(void *cursor)\r\n{\r\n\tstruct cursorinfo_s *c = cursor;\r\n\tw.cursor = c;\r\n\r\n\tWL_SetHWCursor();\t//update it\r\n\treturn true;\r\n}\r\nvoid\t WL_DestroyCursor(void *cursor)\r\n{\r\n\tstruct cursorinfo_s *c = cursor;\r\n\r\n\tif (c == w.cursor)\r\n\t{\t//unset the cursor if its active\r\n\t\tw.cursor = NULL;\r\n\t\tWL_SetHWCursor();\r\n\t}\r\n\tpwl_surface_destroy(c->surf);\r\n\tpwl_buffer_destroy(c->buf);\r\n}\r\n\r\nstatic void WL_pointer_handle_enter(void *data, struct wl_pointer *pointer, uint32_t serial, struct wl_surface *surface, wl_fixed_t sx, wl_fixed_t sy)\r\n{\r\n\tstruct wdisplay_s *s = data;\r\n\r\n\ts->cursorfocus = true;\r\n\ts->cursorserial = serial;\r\n\tWL_SetHWCursor();\r\n}\r\nstatic void WL_pointer_handle_leave(void *data, struct wl_pointer *pointer, uint32_t serial, struct wl_surface *surface)\r\n{\r\n\tstruct wdisplay_s *s = data;\r\n\ts->cursorfocus = false;\r\n\tWL_SetHWCursor();\r\n}\r\n\r\nstatic void WL_pointer_handle_motion(void *data, struct wl_pointer *pointer, uint32_t time, wl_fixed_t sx, wl_fixed_t sy)\r\n{\r\n\tstruct wdisplay_s *s = data;\r\n\tdouble x = wl_fixed_to_double(sx);\r\n\tdouble y = wl_fixed_to_double(sy);\r\n\t//wayland is shite shite shite.\r\n\t//1.4 still has no relative mouse motion.\r\n\r\n\t//track this for csd\r\n\ts->mousex = x;\r\n\ts->mousey = y;\r\n\ty -= s->csdsize;\r\n\r\n\t//and let the game know.\r\n\tif (!s->relative_mouse_active)\r\n\t\tIN_MouseMove(0, true, x, y, 0, 0);\r\n}\r\n\r\nstatic void WL_pointer_handle_button(void *data, struct wl_pointer *wl_pointer, uint32_t serial, uint32_t time, uint32_t button, uint32_t state)\r\n{\r\n\tstruct wdisplay_s *s = data;\r\n\tint qkey;\r\n\r\n\tswitch(button)\r\n\t{\r\n\tdefault:\r\n\t\treturn;\t//blurgh.\r\n\tcase BTN_LEFT:\r\n\t\tqkey = K_MOUSE1;\r\n\t\tif ((!s->relative_mouse_active||!vid.activeapp) && s->mousey < s->csdsize)\r\n\t\t{\r\n#ifdef XDG_SHELL_NAME\r\n\t\t\tif (s->xdg_toplevel)\r\n\t\t\t\txdg_toplevel_move(s->xdg_toplevel, s->seat, serial);\r\n#endif\r\n#ifdef WL_SHELL_NAME\r\n\t\t\tif (s->ssurface)\r\n\t\t\t\tpwl_proxy_marshal((struct wl_proxy *) s->ssurface, WL_SHELL_SURFACE_MOVE, s->seat, serial);\r\n#endif\r\n\t\t\treturn;\r\n\t\t}\r\n\t\tbreak;\r\n\tcase BTN_RIGHT:\r\n\t\tqkey = K_MOUSE2;\r\n\t\tif ((!s->relative_mouse_active||!vid.activeapp) && s->mousey < s->csdsize)\r\n\t\t{\r\n#ifdef XDG_SHELL_NAME\r\n\t\t\tif (s->xdg_toplevel)\r\n\t\t\t\txdg_toplevel_show_window_menu(s->xdg_toplevel, s->seat, serial, s->mousex, s->mousey);\r\n#endif\r\n#ifdef WL_SHELL_NAME\r\n\t\t\tif (s->ssurface)\r\n\t\t\t\tpwl_proxy_marshal((struct wl_proxy *) s->ssurface, WL_SHELL_SURFACE_SET_MAXIMIZED, NULL);\r\n#endif\r\n\t\t\treturn;\r\n\t\t}\r\n\t\tbreak;\r\n\tcase BTN_MIDDLE:\r\n\t\tqkey = K_MOUSE3;\r\n\t\tbreak;\r\n\tcase BTN_SIDE:\r\n\t\tqkey = K_MOUSE4;\r\n\t\tbreak;\r\n\tcase BTN_EXTRA:\r\n\t\tqkey = K_MOUSE5;\r\n\t\tbreak;\r\n\tcase BTN_FORWARD:\r\n\t\tqkey = K_MOUSE6;\r\n\t\tbreak;\r\n\tcase BTN_BACK:\r\n\t\tqkey = K_MOUSE7;\r\n\t\tbreak;\r\n\tcase BTN_TASK:\r\n\t\tqkey = K_MOUSE8;\r\n\t\tbreak;\r\n\t}\r\n\tIN_KeyEvent(0, !!state, qkey, 0);\r\n//\t\tpwl_shell_surface_move(display->window->shell_surface, display->seat, serial);\r\n}\r\n\r\nstatic void WL_pointer_handle_axis(void *data, struct wl_pointer *wl_pointer, uint32_t time, uint32_t axis, wl_fixed_t value)\r\n{\r\n\tif (value < 0)\r\n\t{\r\n\t\tIN_KeyEvent(0, 1, K_MWHEELUP, 0);\r\n\t\tIN_KeyEvent(0, 0, K_MWHEELUP, 0);\r\n\t}\r\n\telse\r\n\t{\r\n\t\tIN_KeyEvent(0, 1, K_MWHEELDOWN, 0);\r\n\t\tIN_KeyEvent(0, 0, K_MWHEELDOWN, 0);\r\n\t}\r\n}\r\n\r\nstatic const struct wl_pointer_listener pointer_listener =\r\n{\r\n\tWL_pointer_handle_enter,\r\n\tWL_pointer_handle_leave,\r\n\tWL_pointer_handle_motion,\r\n\tWL_pointer_handle_button,\r\n\tWL_pointer_handle_axis,\r\n};\r\n\r\n#ifdef WP_RELATIVE_POINTER_MANAGER_NAME\r\nstruct zwp_relative_pointer_v1;\r\n#define ZWP_RELATIVE_POINTER_MANAGER_V1_DESTROY 0\r\n#define ZWP_RELATIVE_POINTER_MANAGER_V1_GET_RELATIVE_POINTER 1\r\n#define ZWP_RELATIVE_POINTER_V1_DESTROY 0\r\n\r\nstatic void WL_pointer_handle_delta(void *data, struct zwp_relative_pointer_v1 *pointer, uint32_t time_hi, uint32_t time_lo, wl_fixed_t dx_w, wl_fixed_t dy_w, wl_fixed_t dx_raw_w, wl_fixed_t dy_raw_w)\r\n{\r\n\tif (w.relative_mouse_active)\r\n\t{\r\n\t\tdouble xmove = wl_fixed_to_double(dx_raw_w);\r\n\t\tdouble ymove = wl_fixed_to_double(dy_raw_w);\r\n\t\tIN_MouseMove(0, false, xmove, ymove, 0, 0);\r\n\t}\r\n}\r\nstruct zwp_relative_pointer_v1_listener\r\n{\r\n\tvoid (*delta)(void *data, struct zwp_relative_pointer_v1 *pointer, uint32_t time_hi, uint32_t time_lo, wl_fixed_t dx_w, wl_fixed_t dy_w, wl_fixed_t dx_raw_w, wl_fixed_t dy_raw_w);\r\n};\r\nstatic const struct zwp_relative_pointer_v1_listener relative_pointer_listener =\r\n{\r\n\tWL_pointer_handle_delta,\r\n};\r\n\r\nstatic void WL_BindRelativePointerManager(struct wl_registry *registry, uint32_t id)\r\n{\t/*oh hey, I wrote lots of code! pay me more! fuck that shit.*/\r\n\r\n\tstatic const struct wl_interface *types[8];\r\n\tstatic const struct wl_message zwp_relative_pointer_manager_v1_requests[] = {\r\n\t\t{ \"destroy\", \"\", types + 0 },\r\n\t\t{ \"get_relative_pointer\", \"no\", types + 6 },\r\n\t};\r\n\tstatic const struct wl_interface zwp_relative_pointer_manager_v1_interface = {\r\n\t\t\"zwp_relative_pointer_manager_v1\", 1,\r\n\t\t2, zwp_relative_pointer_manager_v1_requests,\r\n\t\t0, NULL,\r\n\t};\r\n\tstatic const struct wl_message zwp_relative_pointer_v1_requests[] = {\r\n\t\t{ \"destroy\", \"\", types + 0 },\r\n\t};\r\n\tstatic const struct wl_message zwp_relative_pointer_v1_events[] = {\r\n\t\t{ \"relative_motion\", \"uuffff\", types + 0 },\r\n\t};\r\n\tstatic const struct wl_interface zwp_relative_pointer_v1_interface = {\r\n\t\t\"zwp_relative_pointer_v1\", 1,\r\n\t\t1, zwp_relative_pointer_v1_requests,\r\n\t\t1, zwp_relative_pointer_v1_events,\r\n\t};\r\n\r\n\t//fix up types...\r\n\ttypes[6] = &zwp_relative_pointer_v1_interface;\r\n\ttypes[7] = pwl_pointer_interface;\r\n\r\n\tpzwp_relative_pointer_v1_interface = &zwp_relative_pointer_v1_interface;\r\n\tw.relative_pointer_manager = pwl_registry_bind(registry, id, &zwp_relative_pointer_manager_v1_interface, 1);\r\n}\r\n#endif\r\n\r\n#ifdef WP_POINTER_CONSTRAINTS_NAME\r\n#define ZWP_POINTER_CONSTRAINTS_V1_DESTROY 0\r\n#define ZWP_POINTER_CONSTRAINTS_V1_LOCK_POINTER 1\r\n#define ZWP_POINTER_CONSTRAINTS_V1_CONFINE_POINTER 2\r\n#define ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_ONESHOT 1\r\n#define ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT 2\r\n#define ZWP_LOCKED_POINTER_V1_DESTROY 0\r\n#define ZWP_LOCKED_POINTER_V1_SET_CURSOR_POSITION_HINT 1\r\n#define ZWP_LOCKED_POINTER_V1_SET_REGION 2\r\nstatic void WL_pointer_handle_locked(void *data, struct zwp_locked_pointer_v1 *pointer)\r\n{\r\n\tstruct wdisplay_s *s = data;\r\n\ts->relative_mouse_active = true;\t//yay, everything works!...\r\n\tWL_SetHWCursor();\r\n}\r\nstatic void WL_pointer_handle_unlocked(void *data, struct zwp_locked_pointer_v1 *pointer)\r\n{\t//this is a one-shot. it gets destroyed automatically, but we still need to null it\r\n\tstruct wdisplay_s *s = data;\r\n\ts->relative_mouse_active = false;\t//probably the compositor killed it.\r\n\tWL_SetHWCursor();\r\n\r\n//\ts->locked_pointer = NULL;\r\n}\r\nstruct zwp_locked_pointer_v1_listener\r\n{\r\n\tvoid (*locked)(void *data, struct zwp_locked_pointer_v1 *pointer);\r\n\tvoid (*unlocked)(void *data, struct zwp_locked_pointer_v1 *pointer);\r\n};\r\nstatic const struct zwp_locked_pointer_v1_listener locked_pointer_listener =\r\n{\r\n\tWL_pointer_handle_locked,\r\n\tWL_pointer_handle_unlocked,\r\n};\r\nstatic void WL_BindPointerConstraintsManager(struct wl_registry *registry, uint32_t id)\r\n{\r\n\tstatic const struct wl_interface *types[14];\r\n\tstatic const struct wl_message zwp_pointer_constraints_v1_requests[] = {\r\n\t\t{\"destroy\", \"\", types+0},\r\n\t\t{\"lock_pointer\", \"noo?ou\", types+2},\r\n\t\t{\"confine_pointer\", \"noo?ou\", types+7},\r\n\t};\r\n\tstatic const struct wl_interface zwp_pointer_constraints_v1_interface = {\r\n\t\t\"zwp_pointer_constraints_v1\", 1,\r\n\t\t3, zwp_pointer_constraints_v1_requests,\r\n\t\t0, NULL,\r\n\t};\r\n\r\n\tstatic const struct wl_message zwp_locked_pointer_v1_requests[] = {\r\n\t\t{\"destroy\", \"\", types + 0},\r\n\t\t{\"set_cursor_position_hint\", \"ff\", types + 0},\r\n\t\t{\"set_region\", \"?o\", types + 12},\r\n\t};\r\n\r\n\tstatic const struct wl_message zwp_locked_pointer_v1_events[] = {\r\n\t\t{\"locked\", \"\", types + 0},\r\n\t\t{\"unlocked\", \"\", types + 0},\r\n\t};\r\n\r\n\tstatic const struct wl_interface zwp_locked_pointer_v1_interface = {\r\n\t\t\"zwp_locked_pointer_v1\", 1,\r\n\t\t3, zwp_locked_pointer_v1_requests,\r\n\t\t2, zwp_locked_pointer_v1_events,\r\n\t};\r\n\r\n\ttypes[2] = &zwp_locked_pointer_v1_interface;\r\n\ttypes[3] = pwl_surface_interface;\r\n\ttypes[4] = pwl_pointer_interface;\r\n\ttypes[5] = pwl_region_interface;\r\n//\ttypes[7] = &zwp_confined_pointer_v1_interfae;\r\n\ttypes[8] = pwl_surface_interface;\r\n\ttypes[9] = pwl_pointer_interface;\r\n\ttypes[10] = pwl_region_interface;\r\n\ttypes[12] = pwl_region_interface;\r\n\ttypes[13] = pwl_region_interface;\r\n\r\n\tpzwp_locked_pointer_v1_interface = &zwp_locked_pointer_v1_interface;\r\n\tw.pointer_constraints = pwl_registry_bind(registry, id, &zwp_pointer_constraints_v1_interface, 1);\r\n}\r\nstatic inline int zwp_locked_pointer_v1_add_listener(struct zwp_locked_pointer_v1 *zwp_locked_pointer_v1, const struct zwp_locked_pointer_v1_listener *listener, void *data)\t\t{return pwl_proxy_add_listener((struct wl_proxy *) zwp_locked_pointer_v1, (void (**)(void)) listener, data);}\r\nstatic inline struct zwp_locked_pointer_v1 *zwp_pointer_constraints_v1_lock_pointer(struct zwp_pointer_constraints_v1 *zwp_pointer_constraints_v1, struct wl_surface *surface, struct wl_pointer *pointer, struct wl_region *region, uint32_t lifetime)\t\t{return (struct zwp_locked_pointer_v1 *) pwl_proxy_marshal_constructor((struct wl_proxy *) zwp_pointer_constraints_v1, ZWP_POINTER_CONSTRAINTS_V1_LOCK_POINTER, pzwp_locked_pointer_v1_interface, NULL, surface, pointer, region, lifetime);}\r\n#endif\r\n\r\nstatic void WL_keyboard_handle_keymap(void *data, struct wl_keyboard *keyboard, uint32_t format, int fd, uint32_t size)\r\n{\r\n\tstruct wdisplay_s *d = data;\r\n\tif (d->xkb_context)\r\n\t{\r\n\t\tstruct xkb_keymap *keymap;\r\n\t\tchar *keymap_string = mmap (NULL, size, PROT_READ, MAP_SHARED, fd, 0);\r\n\t\tif (keymap_string != MAP_FAILED)\r\n\t\t{\r\n\t\t\tkeymap = pxkb_keymap_new_from_string (d->xkb_context, keymap_string, XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS);\r\n\t\t\tmunmap (keymap_string, size);\r\n\t\t\tpxkb_state_unref (d->xkb_state);\r\n\t\t\td->xkb_state = pxkb_state_new (keymap);\r\n\t\t\tpxkb_keymap_unref (keymap);\r\n\t\t}\r\n\t}\r\n\r\n\tclose(fd);\r\n}\r\n\r\nstatic void WL_keyboard_handle_enter(void *data, struct wl_keyboard *keyboard, uint32_t serial, struct wl_surface *surface, struct wl_array *keys)\r\n{\r\n\tvid.activeapp = true;\r\n}\r\n\r\nstatic void WL_keyboard_handle_leave(void *data, struct wl_keyboard *keyboard, uint32_t serial, struct wl_surface *surface)\r\n{\r\n\tvid.activeapp = false;\r\n\tIN_KeyEvent(0, false, -1, 0);\t//aka: release all, so we're not left with alt held, etc.\r\n}\r\n\r\nstatic void WL_keyboard_handle_key(void *data, struct wl_keyboard *keyboard, uint32_t serial, uint32_t time, uint32_t key, uint32_t state)\r\n{\r\n\textern int\t\tshift_down;\r\n\tstruct wdisplay_s *d = data;\r\n\tuint32_t qkey;\r\n\tuint32_t ukey;\r\n\r\n\tif (key < sizeof(waylandinputsucksbighairydonkeyballs)/sizeof(waylandinputsucksbighairydonkeyballs[0]))\r\n\t\tqkey = waylandinputsucksbighairydonkeyballs[key];\r\n\telse\r\n\t\tqkey = 0;\r\n\tif (!qkey)\r\n\t\tCon_DPrintf(\"WLScancode %#x has no mapping\\n\", key);\r\n\r\n\tif (d->xkb_context)\r\n\t{\r\n\t\tif (state == WL_KEYBOARD_KEY_STATE_PRESSED)\r\n\t\t{\r\n\t\t\txkb_keysym_t keysym = pxkb_state_key_get_one_sym (d->xkb_state, key+8);\r\n\t\t\tukey = pxkb_keysym_to_utf32 (keysym);\r\n\t\t}\r\n\t\telse\r\n\t\t\tukey = 0;\r\n\t}\r\n\telse\r\n\t{\r\n\t\t//FIXME: this stuff is fucked, especially the ukey stuff.\r\n\t\tif (key < sizeof(waylandinputsucksbighairydonkeyballs)/sizeof(waylandinputsucksbighairydonkeyballs[0]))\r\n\t\t{\r\n\t\t\tif (shift_down)\r\n\t\t\t\tukey = waylandinputsucksbighairydonkeyballsshift[key];\r\n\t\t\telse\r\n\t\t\t\tukey = waylandinputsucksbighairydonkeyballs[key];\r\n\t\t}\r\n\t\telse\r\n\t\t\tukey = 0;\r\n\t\tif (ukey < ' ' || ukey > 127)\r\n\t\t\tukey = 0;\r\n\t}\r\n\r\n\tIN_KeyEvent(0, (state==WL_KEYBOARD_KEY_STATE_PRESSED)?1:0, qkey, ukey);\r\n}\r\n\r\nstatic void WL_keyboard_handle_modifiers(void *data, struct wl_keyboard *keyboard, uint32_t serial, uint32_t mods_depressed, uint32_t mods_latched, uint32_t mods_locked, uint32_t group)\r\n{\r\n\tstruct wdisplay_s *s = data;\r\n\tpxkb_state_update_mask (s->xkb_state, mods_depressed, mods_latched, mods_locked, 0, 0, group);\r\n}\r\n\r\nstatic const struct wl_keyboard_listener keyboard_listener =\r\n{\r\n\tWL_keyboard_handle_keymap,\r\n\tWL_keyboard_handle_enter,\r\n\tWL_keyboard_handle_leave,\r\n\tWL_keyboard_handle_key,\r\n\tWL_keyboard_handle_modifiers\r\n};\r\nstatic void WL_seat_handle_capabilities(void *data, struct wl_seat *seat, enum wl_seat_capability caps)\r\n{\r\n\tstruct wdisplay_s *s = data;\r\n\tif ((caps & WL_SEAT_CAPABILITY_POINTER) && !s->pointer)\r\n\t{\r\n\t\ts->pointer = pwl_seat_get_pointer(seat);\r\n\t\tpwl_pointer_add_listener(s->pointer, &pointer_listener, s);\r\n\r\n\t\tif (w.relative_pointer_manager)\r\n\t\t{\t//and try and get relative pointer events too. so much fucking boilerplate.\r\n\t\t\tw.relative_pointer = (struct zwp_relative_pointer_v1 *)pwl_proxy_marshal_constructor((struct wl_proxy *) w.relative_pointer_manager, ZWP_RELATIVE_POINTER_MANAGER_V1_GET_RELATIVE_POINTER, pzwp_relative_pointer_v1_interface, NULL, w.pointer);\r\n\t\t\tpwl_proxy_add_listener((struct wl_proxy *) w.relative_pointer, (void (**)(void)) &relative_pointer_listener, &w);\r\n\t\t}\r\n\t}\r\n\telse if (!(caps & WL_SEAT_CAPABILITY_POINTER) && s->pointer)\r\n\t{\r\n\t\tpwl_pointer_destroy(s->pointer);\r\n\t\ts->pointer = NULL;\r\n\r\n\t\tif (w.relative_pointer)\r\n\t\t{\r\n\t\t\tpwl_proxy_marshal((struct wl_proxy *) w.relative_pointer, ZWP_RELATIVE_POINTER_V1_DESTROY);\r\n\t\t\tpwl_proxy_destroy((struct wl_proxy *) w.relative_pointer);\r\n\t\t}\r\n\t}\r\n\r\n\tif ((caps & WL_SEAT_CAPABILITY_KEYBOARD) && !s->keyboard)\r\n\t{\r\n\t\ts->keyboard = pwl_seat_get_keyboard(seat);\r\n\t\tpwl_keyboard_add_listener(s->keyboard, &keyboard_listener, s);\r\n\t}\r\n\telse if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && s->keyboard)\r\n\t{\r\n\t\tpwl_keyboard_destroy(s->keyboard);\r\n\t\ts->keyboard = NULL;\r\n\t}\r\n}\r\nstatic const struct wl_seat_listener seat_listener =\r\n{\r\n\tWL_seat_handle_capabilities\r\n};\r\n\r\n\r\n#ifdef XDG_DECORATION_MANAGER_NAME\r\nstatic const struct wl_interface *pzxdg_toplevel_decoration_v1_interface;\r\nstatic void WL_BindDecorationManager(struct wl_registry *registry, uint32_t id)\r\n{\t/*oh hey, I wrote lots of code! pay me more! fuck that shit.*/\r\n\tstatic const struct wl_interface *types[3];\r\n\tstatic const struct wl_message zxdg_decoration_manager_v1_requests[] = {\r\n\t\t{ \"destroy\", \"\", types + 0 },\r\n\t\t{ \"get_toplevel_decoration\", \"no\", types + 1 },\r\n\t};\r\n\tstatic const struct wl_interface zxdg_decoration_manager_v1_interface = {\r\n\t\t\"zxdg_decoration_manager_v1\", 1,\r\n\t\t2, zxdg_decoration_manager_v1_requests,\r\n\t\t0, NULL,\r\n\t};\r\n\tstatic const struct wl_message zxdg_toplevel_decoration_v1_requests[] = {\r\n\t\t{ \"destroy\", \"\", types + 0 },\r\n\t\t{ \"set_mode\", \"u\", types + 0 },\r\n\t\t{ \"unset_mode\", \"\", types + 0 },\r\n\t};\r\n\tstatic const struct wl_message zxdg_toplevel_decoration_v1_events[] = {\r\n\t\t{ \"configure\", \"u\", types + 0 },\r\n\t};\r\n\tstatic const struct wl_interface zxdg_toplevel_decoration_v1_interface = {\r\n\t\t\"zxdg_toplevel_decoration_v1\", 1,\r\n\t\t3, zxdg_toplevel_decoration_v1_requests,\r\n\t\t1, zxdg_toplevel_decoration_v1_events,\r\n\t};\r\n\r\n\t//fix up types...\r\n\ttypes[1] = &zxdg_toplevel_decoration_v1_interface;\r\n\ttypes[2] = pxdg_toplevel_interface;\r\n\r\n\tpzxdg_toplevel_decoration_v1_interface = &zxdg_toplevel_decoration_v1_interface;\r\n\tw.xdg_decoration_manager = pwl_registry_bind(registry, id, &zxdg_decoration_manager_v1_interface, 1);\r\n}\r\n\r\nenum xdg_decoration_manager_mode {\r\n    ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE = 1,\r\n    ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE = 2,\r\n};\r\n#define ZXDG_DECORATION_MANAGER_V1_GET_TOPLEVEL_DECORATION 1\r\nstatic inline struct zxdg_toplevel_decoration_v1 *zxdg_decoration_manager_v1_get_toplevel_decoration(struct zxdg_decoration_manager_v1 *zxdg_decoration_manager_v1, struct xdg_toplevel *toplevel)\t\t{ return (struct zxdg_toplevel_decoration_v1 *) pwl_proxy_marshal_constructor((struct wl_proxy *) zxdg_decoration_manager_v1, ZXDG_DECORATION_MANAGER_V1_GET_TOPLEVEL_DECORATION, pzxdg_toplevel_decoration_v1_interface, NULL, toplevel); }\r\n\r\n#define ZXDG_TOPLEVEL_DECORATION_V1_SET_MODE 1\r\nstruct zxdg_toplevel_decoration_v1_listener {\r\n    void (*configure)(void *data, struct zxdg_toplevel_decoration_v1 *zxdg_toplevel_decoration_v1, uint32_t mode);\r\n};\r\nstatic inline void zxdg_toplevel_decoration_v1_set_mode(struct zxdg_toplevel_decoration_v1 *xdg_decoration, uint32_t mode)\t\t{ pwl_proxy_marshal((struct wl_proxy *) xdg_decoration, ZXDG_TOPLEVEL_DECORATION_V1_SET_MODE, mode); }\r\nstatic inline int zxdg_toplevel_decoration_v1_add_listener(struct zxdg_toplevel_decoration_v1 *zxdg_toplevel_decoration_v1, const struct zxdg_toplevel_decoration_v1_listener *listener, void *data)\t{ return pwl_proxy_add_listener((struct wl_proxy *) zxdg_toplevel_decoration_v1, (void (**)(void)) listener, data); }\r\n\r\nstatic void xdg_decoration_mode(void *data, struct zxdg_toplevel_decoration_v1 *xdg_decoration, uint32_t mode)\r\n{\r\n\tstruct wdisplay_s *d = data;\r\n\td->hasssd = (mode != ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE);\r\n}\r\nstatic const struct zxdg_toplevel_decoration_v1_listener myxdg_decoration_listener =\r\n{\r\n\txdg_decoration_mode\r\n};\r\n\r\n#endif\r\n\r\n#ifdef ORG_KDE_KWIN_SERVER_DECORATION_MANAGER_NAME\r\nstatic const struct wl_interface *porg_kde_kwin_server_decoration_interface;\r\nstatic void WL_BindKWinDecorationManager(struct wl_registry *registry, uint32_t id)\r\n{\r\n\tstatic const struct wl_interface *types[3];\r\n\tstatic const struct wl_message org_kde_kwin_server_decoration_manager_requests[] = {\r\n\t\t{ \"create\", \"no\", types + 1 },\r\n\t};\r\n\tstatic const struct wl_message org_kde_kwin_server_decoration_manager_events[] = {\r\n\t\t{ \"default_mode\", \"u\", types + 0 },\r\n\t};\r\n\tstatic const struct wl_interface org_kde_kwin_server_decoration_manager_interface = {\r\n\t\t\"org_kde_kwin_server_decoration_manager\", 1,\r\n\t\t1, org_kde_kwin_server_decoration_manager_requests,\r\n\t\t1, org_kde_kwin_server_decoration_manager_events,\r\n\t};\r\n\tstatic const struct wl_message org_kde_kwin_server_decoration_requests[] = {\r\n\t\t{ \"release\", \"\", types + 0 },\r\n\t\t{ \"request_mode\", \"u\", types + 0 },\r\n\t};\r\n\tstatic const struct wl_message org_kde_kwin_server_decoration_events[] = {\r\n\t\t{ \"mode\", \"u\", types + 0 },\r\n\t};\r\n\tstatic const struct wl_interface org_kde_kwin_server_decoration_interface = {\r\n\t\t\"org_kde_kwin_server_decoration\", 1,\r\n\t\t2, org_kde_kwin_server_decoration_requests,\r\n\t\t1, org_kde_kwin_server_decoration_events,\r\n\t};\r\n\r\n\t//fix up types...\r\n\ttypes[1] = &org_kde_kwin_server_decoration_interface;\r\n\ttypes[2] = pwl_surface_interface;\r\n\r\n\tporg_kde_kwin_server_decoration_interface = &org_kde_kwin_server_decoration_interface;\r\n\tw.kwin_decoration_manager = pwl_registry_bind(registry, id, &org_kde_kwin_server_decoration_manager_interface, 1);\r\n}\r\nenum org_kde_kwin_server_decoration_manager_mode {\r\n    ORG_KDE_KWIN_SERVER_DECORATION_MANAGER_MODE_NONE = 0,\r\n    ORG_KDE_KWIN_SERVER_DECORATION_MANAGER_MODE_CLIENT = 1,\r\n    ORG_KDE_KWIN_SERVER_DECORATION_MANAGER_MODE_SERVER = 2,\r\n};\r\n#define ORG_KDE_KWIN_SERVER_DECORATION_MANAGER_CREATE 0\r\nstatic inline struct org_kde_kwin_server_decoration *org_kde_kwin_server_decoration_manager_create(struct org_kde_kwin_server_decoration_manager *org_kde_kwin_server_decoration_manager, struct wl_surface *surface)\t\t{ return (struct org_kde_kwin_server_decoration *) pwl_proxy_marshal_constructor((struct wl_proxy *) org_kde_kwin_server_decoration_manager, ORG_KDE_KWIN_SERVER_DECORATION_MANAGER_CREATE, porg_kde_kwin_server_decoration_interface, NULL, surface); }\r\n#define ORG_KDE_KWIN_SERVER_DECORATION_REQUEST_MODE 1\r\nstruct org_kde_kwin_server_decoration_listener {\r\n    void (*mode)(void *data, struct org_kde_kwin_server_decoration *org_kde_kwin_server_decoration, uint32_t mode);\r\n};\r\nstatic inline void org_kde_kwin_server_decoration_request_mode(struct org_kde_kwin_server_decoration *org_kde_kwin_server_decoration, uint32_t mode)\t\t{ pwl_proxy_marshal((struct wl_proxy *) org_kde_kwin_server_decoration, ORG_KDE_KWIN_SERVER_DECORATION_REQUEST_MODE, mode); }\r\nstatic inline int org_kde_kwin_server_decoration_add_listener(struct org_kde_kwin_server_decoration *org_kde_kwin_server_decoration, const struct org_kde_kwin_server_decoration_listener *listener, void *data)\t\t{ return pwl_proxy_add_listener((struct wl_proxy *) org_kde_kwin_server_decoration, (void (**)(void)) listener, data); }\r\n\r\nstatic void kwin_decoration_mode(void *data, struct org_kde_kwin_server_decoration *org_kde_kwin_server_decoration, uint32_t mode)\r\n{\r\n\tstruct wdisplay_s *d = data;\r\n\td->hasssd = (mode != ORG_KDE_KWIN_SERVER_DECORATION_MANAGER_MODE_CLIENT);\r\n}\r\nstatic struct org_kde_kwin_server_decoration_listener myorg_kde_kwin_server_decoration_listener =\r\n{\r\n\tkwin_decoration_mode\r\n};\r\n#endif\r\n\r\nstatic void WL_handle_global(void *data, struct wl_registry *registry, uint32_t id, const char *interface, uint32_t version)\r\n{\r\n\tstruct wdisplay_s *d = data;\r\nCon_DLPrintf(0, \"Wayland Interface %s id %u version %u\\n\", interface, id, version);\r\n\tif (strcmp(interface, \"wl_compositor\") == 0)\r\n\t\td->compositor = pwl_registry_bind(registry, id, pwl_compositor_interface, 1);\r\n\telse if (strcmp(interface, \"wl_shm\") == 0)\r\n\t\td->shm = pwl_registry_bind(registry, id, pwl_shm_interface, 1);\r\n#ifdef WL_SHELL_NAME\r\n\telse if (strcmp(interface, WL_SHELL_NAME) == 0)\r\n\t\td->shell = pwl_registry_bind(registry, id, pwl_shell_interface, 1);\r\n#endif\r\n#ifdef XDG_SHELL_NAME\r\n\telse if (strcmp(interface, XDG_SHELL_NAME) == 0 && version > 1)\t//old versions of kwin are buggy, sidestep it.\r\n\t\td->xdg_wm_base = pwl_registry_bind(registry, id, pxdg_wm_base_interface, 2);\r\n#endif\r\n\telse if (strcmp(interface, \"wl_seat\") == 0 && !d->seat)\r\n\t{\r\n\t\td->seat = pwl_registry_bind(registry, id, pwl_seat_interface, 1);\r\n\t\tpwl_seat_add_listener(d->seat, &seat_listener, d);\r\n\t}\r\n#ifdef XDG_DECORATION_MANAGER_NAME\r\n\telse if (strcmp(interface, XDG_DECORATION_MANAGER_NAME) == 0)\r\n\t\tWL_BindDecorationManager(registry, id);\r\n#endif\r\n#ifdef ORG_KDE_KWIN_SERVER_DECORATION_MANAGER_NAME\r\n\telse if (strcmp(interface, ORG_KDE_KWIN_SERVER_DECORATION_MANAGER_NAME) == 0)\r\n\t\tWL_BindKWinDecorationManager(registry, id);\r\n#endif\r\n//\telse if (strcmp(interface, \"zxdg_decoration_manager_v1\") == 0)\r\n//\t\tWL_BindDecorationManager(registry, id);\r\n#ifdef WP_RELATIVE_POINTER_MANAGER_NAME\r\n\telse if (strcmp(interface, WP_RELATIVE_POINTER_MANAGER_NAME) == 0)\r\n\t\tWL_BindRelativePointerManager(registry, id);\r\n#endif\r\n#ifdef WP_POINTER_CONSTRAINTS_NAME\r\n\telse if (strcmp(interface, WP_POINTER_CONSTRAINTS_NAME) == 0)\r\n\t\tWL_BindPointerConstraintsManager(registry, id);\r\n#endif\r\n/*\telse if (!strcmp(interface, \"input_device\"))\r\n\t\tdisplay_add_input(id);\r\n*/\r\n}\r\n\r\nstatic void WL_handle_global_remove(void *data, struct wl_registry *wl_registry, uint32_t id)\r\n{\r\n\tCon_Printf(\"WL_handle_global_remove: %u\\n\", id);\r\n}\r\n\r\nstatic const struct wl_registry_listener WL_registry_listener = {\r\n\tWL_handle_global,\r\n\tWL_handle_global_remove\r\n};\r\n\r\nqboolean WL_MayRefresh(void)\r\n{\r\n\tif (w.wait_for_configure || w.waylandisblocking)\r\n\t{\r\n\t\tif (pwl_display_dispatch(w.display) == -1)\r\n\t\t{\r\n\t\t\tSys_Sleep(2);\r\n\t\t\tCon_Printf(CON_ERROR \"wayland connection was lost. Restarting video\\n\");\r\n\t\t\tCbuf_InsertText(\"vid_restart\", RESTRICT_LOCAL, true);\r\n\t\t}\r\n\t\treturn false;\r\n\t}\r\n\treturn true;\r\n}\r\nstatic void frame_handle_done(void *data, struct wl_callback *callback, uint32_t time)\r\n{\r\n\tpwl_proxy_destroy((struct wl_proxy *)callback);\r\n\tw.waylandisblocking = false;\r\n}\r\nstatic const struct wl_callback_listener frame_listener = {\r\n\t.done = frame_handle_done,\r\n};\r\n\r\nstatic void WL_SwapBuffers(void)\r\n{\r\n\tqboolean wantabs;\r\n\tTRACE((\"WL_SwapBuffers\\n\"));\r\n\r\n\tswitch(qrenderer)\r\n\t{\r\n#if defined(GLQUAKE) && defined(USE_EGL)\r\n\tcase QR_OPENGL:\r\n\t\tif (w.csdsize && font_default)\r\n\t\t{\r\n\t\t\tunsigned int vw=vid.width;\r\n\t\t\tunsigned int vh=vid.height;\r\n\t\t\tunsigned int rw=vid.rotpixelwidth;\r\n\t\t\tunsigned int rh=vid.rotpixelheight;\r\n\t\t\tMatrix4x4_CM_Orthographic(r_refdef.m_projection_std, 0, vid.pixelwidth, w.csdsize, 0, -99999, 99999);\r\n\t\t\tMatrix4x4_Identity(r_refdef.m_view);\r\n\t\t\tqglViewport(0, vid.pixelheight, vid.pixelwidth, w.csdsize);\r\n\t\t\tGL_SetShaderState2D(true);\t//responsible for loading the new projection matrix\r\n\t\t\tvid.rotpixelwidth = vid.width = vid.pixelwidth;\r\n\t\t\tvid.rotpixelheight = vid.height = w.csdsize;\r\n\t\t\tR2D_ImageColours(0.05, 0.05, 0.1, 1);\r\n\t\t\tR2D_FillBlock(0, 0, vid.pixelwidth, w.csdsize);\r\n\t\t\tR2D_ImageColours(1, 1, 1, 1);\r\n\t\t\tDraw_FunStringWidth(0, 4, w.csdcaption, vid.pixelwidth, 2, vid.activeapp);\r\n\t\t\tvid.width=vw;\r\n\t\t\tvid.height=vh;\r\n\t\t\tvid.rotpixelwidth=rw;\r\n\t\t\tvid.rotpixelheight=rh;\r\n\t\t}\r\n\t\tif (R2D_Flush)\r\n\t\t\tR2D_Flush();\r\n\r\n\t\t{\r\n\t\t\t// Register a frame callback to know when we need to draw the next frame\r\n\t\t\tstruct wl_callback *callback = pwl_surface_frame(w.surface);\r\n\t\t\tw.waylandisblocking = true;\r\n\t\t\tpwl_callback_add_listener(callback, &frame_listener, NULL);\r\n\t\t}\r\n\r\n\t\tEGL_SwapBuffers();\r\n\t\t//wl_surface_damage(w.surface, 0, 0, vid.pixelwidth, vid.pixelheight);\r\n\t\tif (pwl_display_dispatch_pending(w.display) < 0)\r\n\t\t{\r\n\t\t\tCon_Printf(CON_ERROR \"wayland connection was lost. Restarting video\\n\");\r\n\t\t\tCbuf_InsertText(\"vid_restart\", RESTRICT_LOCAL, true);\r\n\t\t\treturn;\r\n\t\t}\r\n\t\tif (w.hasssd)\r\n\t\t\tw.csdsize = 0;\t//kde's implementation-specific/legacy extension doesn't need us to draw crappy lame CSD junk\r\n\t\telse if (vid.activeapp && w.relative_mouse_active)\r\n\t\t\tw.csdsize = 0;\t//kill the csd while using relative mouse coords.\r\n\t\telse\r\n\t\t\tw.csdsize = Font_CharPHeight(font_default)+8;\r\n\r\n\t\tif (vid.pixelheight != w.trueheight-w.csdsize)\r\n\t\t{\r\n\t\t\tvid.pixelheight = w.trueheight-w.csdsize;\r\n\t\t\tCvar_ForceCallback(&vid_conautoscale);\r\n\t\t}\r\n\t\tbreak;\r\n#endif\r\n\tcase QR_VULKAN:\r\n\t\tif (pwl_display_dispatch_pending(w.display) < 0)\r\n\t\t{\r\n\t\t\tCon_Printf(CON_ERROR \"wayland connection was lost. Restarting video\\n\");\r\n\t\t\tCbuf_InsertText(\"vid_restart\", RESTRICT_LOCAL, true);\r\n\t\t\treturn;\r\n\t\t}\r\n\t\tbreak;\r\n\tdefault:\r\n\t\tbreak;\r\n\t}\r\n\r\n\t//if the game wants absolute mouse positions...\r\n\twantabs = !vid.activeapp || (!vrui.enabled && Key_MouseShouldBeFree()) || !in_windowed_mouse.value;\r\n\t//and force it on if we're lacking one of the plethora of extensions that were needed to get the damn thing actually usable.\r\n\twantabs |= !w.relative_pointer || !w.pointer_constraints;\r\n\tif (!wantabs && w.cursorfocus && !w.locked_pointer && w.pointer_constraints)\r\n\t{\r\n\t\tw.locked_pointer = zwp_pointer_constraints_v1_lock_pointer(w.pointer_constraints, w.surface, w.pointer, NULL, ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT);\r\n\t\tzwp_locked_pointer_v1_add_listener(w.locked_pointer, &locked_pointer_listener, &w);\r\n\t}\r\n\tif (wantabs && w.locked_pointer)\r\n\t{\r\n\t\tpwl_proxy_marshal((struct wl_proxy *) w.locked_pointer, ZWP_LOCKED_POINTER_V1_DESTROY);\r\n\t\tpwl_proxy_destroy((struct wl_proxy *) w.locked_pointer);\r\n\t\tw.locked_pointer = NULL;\r\n\t\tif (!w.relative_mouse_active)\r\n\t\t{\r\n\t\t\tw.relative_mouse_active = false;\r\n\t\t\tWL_SetHWCursor();\r\n\t\t}\r\n\t}\r\n}\r\n\r\n#ifdef VKQUAKE\r\nstatic qboolean WLVK_SetupSurface(void)\r\n{\r\n\tVkWaylandSurfaceCreateInfoKHR inf = {VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR};\r\n    inf.flags = 0;\r\n    inf.display = w.display;\r\n    inf.surface = w.surface;\r\n\r\n    if (VK_SUCCESS == vkCreateWaylandSurfaceKHR(vk.instance, &inf, vkallocationcb, &vk.surface))\r\n        return true;\r\n    return false;\r\n}\r\n#endif\r\n\r\nstatic qboolean WL_NameAndShame(void)\r\n{\r\n\t//called after the renderer has been initialised, so these messages should be more prominant.\r\n\tif (!w.relative_pointer)\r\n\t\tCon_Printf(CON_WARNING \"WARNING: Wayland server does not support %s, \"CON_ERROR\"mouse grabs are not supported\\n\", WP_RELATIVE_POINTER_MANAGER_NAME);\r\n\telse if (!w.pointer_constraints)\r\n\t\tCon_Printf(CON_WARNING \"WARNING: Wayland server does not support %s, \"CON_ERROR\"mouse grabs are not supported\\n\", WP_POINTER_CONSTRAINTS_NAME);\r\n\r\n\tif (!w.hasssd)\r\n\t\tCon_Printf(CON_WARNING \"WARNING: Wayland server does not support window decorations\\n\");\r\n\treturn true;\r\n}\r\n\r\nstatic qboolean WL_Init (rendererstate_t *info, unsigned char *palette)\r\n{\r\n#ifdef DYNAMIC_WAYLAND\r\n\tif (!WL_InitLibrary())\r\n\t{\r\n\t\tCon_Printf(\"couldn't load wayland client libraries\\n\");\r\n\t\treturn false;\r\n\t}\r\n#endif\r\n\tWL_Setup_XDG_Shell();\r\n\r\n\tswitch(qrenderer)\r\n\t{\r\n#if defined(GLQUAKE) && defined(USE_EGL)\r\n\tcase QR_OPENGL:\r\n#ifdef DYNAMIC_WAYLAND\r\n\t\tlib_wayland_egl = Sys_LoadLibrary(\"libwayland-egl.so.1\", waylandexports_egl);\r\n\t\tif (!lib_wayland_egl)\r\n\t\t{\r\n\t\t\tCon_Printf(\"couldn't load libwayland-egl.so.1 library\\n\");\r\n\t\t\treturn false;\r\n\t\t}\r\n#endif\r\n\r\n//\t\tsetenv(\"EGL_PLATFORM\", \"wayland\", 1);\t//if this actually matters then we're kinda screwed\r\n\t\tif (!EGL_LoadLibrary(info->subrenderer))\r\n\t\t{\r\n\t\t\tCon_Printf(\"couldn't load EGL library\\n\");\r\n\t\t\treturn false;\r\n\t\t}\r\n\r\n\t\tbreak;\r\n#endif\r\n#ifdef VKQUAKE\r\n\tcase QR_VULKAN:\r\n\t\t#ifdef VK_NO_PROTOTYPES\r\n\t\t{\t//vulkan requires that vkGetInstanceProcAddr is set in advance.\r\n\t\t\tdllfunction_t func[] =\r\n\t\t\t{\r\n\t\t\t\t{(void*)&vkGetInstanceProcAddr,\t\"vkGetInstanceProcAddr\"},\r\n\t\t\t\t{NULL,\t\t\t\t\t\t\tNULL}\r\n\t\t\t};\r\n\r\n\t\t\tif (!Sys_LoadLibrary(\"libvulkan.so.1\", func))\r\n\t\t\t{\r\n\t\t\t\tif (!Sys_LoadLibrary(\"libvulkan.so\", func))\r\n\t\t\t\t{\r\n\t\t\t\t\tCon_Printf(\"Couldn't intialise libvulkan.so\\nvulkan loader is not installed\\n\");\r\n\t\t\t\t\treturn false;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t\t#endif\r\n\t\tbreak;\r\n#endif\r\n\tdefault:\r\n\t\treturn false;\t//not supported dude...\r\n\t}\r\n\r\n\tmemset(&w, 0, sizeof(w));\r\n\r\n#ifdef DYNAMIC_WAYLAND\r\n\tif (WL_InitLibraryXKB())\r\n#endif\r\n\t\tw.xkb_context = pxkb_context_new(XKB_CONTEXT_NO_FLAGS);\r\n\r\n\tw.csdsize = 0;\r\n\tw.display = pwl_display_connect(NULL);\r\n\tif (!w.display)\r\n\t{\r\n\t\tCon_Printf(\"couldn't connect to wayland server\\n\");\r\n\t\treturn false;\r\n\t}\r\n\tw.registry = pwl_display_get_registry(w.display);\r\n\tpwl_registry_add_listener(w.registry, &WL_registry_listener, &w);\r\n\tpwl_display_dispatch(w.display);\r\n\tpwl_display_roundtrip(w.display);\r\n\tif (!w.compositor)\r\n\t{\r\n\t\tCon_Printf(\"no compositor running, apparently\\n\");\r\n\t\treturn false;\r\n\t}\r\n\r\n\tw.surface = pwl_compositor_create_surface(w.compositor);\r\n\tif (!w.surface)\r\n\t{\r\n\t\tCon_Printf(\"no compositor running, apparently\\n\");\r\n\t\treturn false;\r\n\t}\r\n\r\n\tif (0)\r\n\t\t;\r\n#ifdef XDG_SHELL_NAME\r\n\telse if (w.xdg_wm_base)\r\n\t{\r\n\t\txdg_wm_base_add_listener(w.xdg_wm_base, &myxdg_wm_base_listener, &w);\r\n\t\tw.xdg_surface = xdg_wm_base_get_xdg_surface(w.xdg_wm_base, w.surface);\r\n\t\tif (!w.xdg_surface)\r\n\t\t\treturn false;\r\n\t\txdg_surface_add_listener(w.xdg_surface, &myxdg_surface_listener, &w);\r\n\t\tw.xdg_toplevel = xdg_surface_get_toplevel(w.xdg_surface);\r\n\t\tif (!w.xdg_toplevel)\r\n\t\t\treturn false;\r\n\t\txdg_toplevel_add_listener(w.xdg_toplevel, &myxdg_toplevel_listener, &w);\r\n\t\txdg_toplevel_set_min_size(w.xdg_toplevel, 320, 200);\r\n\t}\r\n#endif\r\n#ifdef WL_SHELL_NAME\r\n\telse if (w.shell)\r\n\t{\r\n\t\tw.ssurface = pwl_shell_get_shell_surface(w.shell, w.surface);\r\n\t\tif (w.ssurface)\r\n\t\t\tpwl_shell_surface_add_listener(w.ssurface, &shell_surface_listener, &w);\r\n\t}\r\n#endif\r\n\telse\r\n\t{\r\n\t\tCon_Printf(\"no compositor shell running, apparently\\n\");\r\n\t\treturn false;\t//no way to make it fullscreen/top-level means it'll probably stay hidden, so make this fatal.\r\n\t}\r\n\r\n\tWL_SetCaption(\"FTE Quake\");\r\n#ifdef XDG_DECORATION_MANAGER_NAME\r\n\tif (w.xdg_decoration_manager)\r\n\t{\t//decorate it!\r\n\t\tw.xdg_decoration = zxdg_decoration_manager_v1_get_toplevel_decoration(w.xdg_decoration_manager, w.xdg_toplevel);\r\n\t\tif (w.xdg_decoration)\r\n\t\t{\r\n\t\t\tzxdg_toplevel_decoration_v1_add_listener(w.xdg_decoration, &myxdg_decoration_listener, &w);\r\n\t\t\tzxdg_toplevel_decoration_v1_set_mode(w.xdg_decoration, ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE);\r\n\t\t}\r\n\t}\r\n#endif\r\n#if defined(XDG_DECORATION_MANAGER_NAME) && defined(ORG_KDE_KWIN_SERVER_DECORATION_MANAGER_NAME)\r\n\telse\r\n#endif\r\n#ifdef ORG_KDE_KWIN_SERVER_DECORATION_MANAGER_NAME\r\n\tif (w.kwin_decoration_manager)\r\n\t{\t//decorate it!\r\n\t\tw.kwin_decoration = org_kde_kwin_server_decoration_manager_create(w.kwin_decoration_manager, w.surface);\r\n\t\tif (w.kwin_decoration)\r\n\t\t{\r\n\t\t\torg_kde_kwin_server_decoration_add_listener(w.kwin_decoration, &myorg_kde_kwin_server_decoration_listener, &w);\r\n\t\t\torg_kde_kwin_server_decoration_request_mode(w.kwin_decoration, ORG_KDE_KWIN_SERVER_DECORATION_MANAGER_MODE_SERVER);\r\n\t\t}\r\n\t}\r\n#endif\r\n\t\r\n#ifdef XDG_SHELL_NAME\r\n\tif (w.xdg_wm_base)\r\n\t{\r\n\t\tif (info->fullscreen)\r\n\t\t\txdg_toplevel_set_fullscreen(w.xdg_toplevel, NULL);\r\n\t}\r\n#endif\r\n#ifdef WL_SHELL_NAME\r\n\tif (w.ssurface)\r\n\t{\r\n\t\tif (info->fullscreen)\r\n\t\t\tpwl_shell_surface_set_fullscreen(w.ssurface, WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT, 60, NULL);\r\n\t\telse\r\n\t\t\tpwl_shell_surface_set_toplevel(w.ssurface);\r\n\t}\r\n#endif\r\n\r\n\tw.truewidth = info->width?info->width:640;\r\n\tw.trueheight = info->height?info->height:480;\r\npwl_display_roundtrip(w.display);\r\n\r\n\t{\r\n\t\tstruct wl_region *region = pwl_compositor_create_region(w.compositor);\r\n\t\tpwl_region_add(region, 0, 0, w.truewidth, w.trueheight);\r\n\t\tpwl_surface_set_opaque_region(w.surface, region);\r\n\t\t//FIXME: leaks region...\r\n\t\t//pwl_region_destroy(region);\r\n\t}\r\npwl_display_roundtrip(w.display);\r\n\tvid.pixelwidth = w.truewidth;\r\n\tvid.pixelheight = w.trueheight-w.csdsize;\r\n\r\n\tvid.activeapp = true;\r\n\r\n\t//window_set_keyboard_focus_handler(window, WL_handler_keyfocus);\r\n\t//window_set_resize_handler(w.surface, WL_handler_resize);\r\n\r\n\tswitch(qrenderer)\r\n\t{\r\n\tdefault:\r\n\t\treturn false;\r\n#ifdef VKQUAKE\r\n\tcase QR_VULKAN:\r\n\t\t{\r\n\t\t\tconst char *extnames[] = {VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME};\r\n\t\t\tif (VK_Init(info, extnames, countof(extnames), WLVK_SetupSurface, NULL))\r\n\t\t\t\treturn WL_NameAndShame();\r\n\t\t\tCon_Printf(CON_ERROR \"Unable to initialise vulkan-on-wayland.\\n\");\r\n\t\t\treturn false;\r\n\t\t}\r\n\t\tbreak;\r\n#endif\r\n#if defined(GLQUAKE) && defined(USE_EGL)\r\n\tcase QR_OPENGL:\r\n\t\t{\r\n\t\t\tEGLConfig cfg;\r\n\t\t\tw.enwindow = pwl_egl_window_create(w.surface, w.truewidth, w.trueheight);\r\n\t\t\tif (!EGL_InitDisplay(info, EGL_PLATFORM_WAYLAND_KHR, w.display, (EGLNativeDisplayType)w.display, &cfg))\r\n\t\t\t{\r\n\t\t\t\tCon_Printf(\"couldn't find suitable EGL config\\n\");\r\n\t\t\t\treturn false;\r\n\t\t\t}\r\n\t\t\t#ifdef XDG_SHELL_NAME\r\n\t\t\t\tif (w.xdg_toplevel)\r\n\t\t\t\t{\r\n\t\t\t\t\tw.wait_for_configure = true;\r\n\t\t\t\t\tpwl_surface_commit(w.surface);\r\n\t\t\t\t}\r\n\t\t\t#endif\r\n\t\t\tif (!EGL_InitWindow(info, EGL_PLATFORM_WAYLAND_KHR, w.enwindow, (EGLNativeWindowType)w.enwindow, cfg))\r\n\t\t\t{\r\n\t\t\t\tCon_Printf(\"couldn't initialise EGL context\\n\");\r\n\t\t\t\treturn false;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tpwl_display_dispatch_pending(w.display);\r\n\r\n\t\tif (GL_Init(info, &EGL_Proc))\r\n\t\t\treturn WL_NameAndShame();\r\n\t\tCon_Printf(CON_ERROR \"Unable to initialise opengl-on-wayland.\\n\");\r\n\t\treturn false;\r\n#endif\r\n\t}\r\n\treturn true;\r\n}\r\nstatic void WL_DeInit(void)\r\n{\r\n#if defined(GLQUAKE) && defined(USE_EGL)\r\n\tEGL_Shutdown();\r\n\tif (w.enwindow)\r\n\t\tpwl_egl_window_destroy(w.enwindow);\r\n\tEGL_UnloadLibrary();\r\n\tGL_ForgetPointers();\r\n#endif\r\n#ifdef WL_SHELL_NAME\r\n\tif (w.ssurface)\t\tpwl_proxy_destroy((struct wl_proxy *) w.ssurface);\r\n#endif\r\n\tif (w.surface)\t\tpwl_proxy_marshal((struct wl_proxy *) w.surface, WL_SURFACE_DESTROY);\r\n\tif (w.surface)\t\tpwl_proxy_destroy((struct wl_proxy *) w.surface);\r\n\tif (w.compositor)\tpwl_proxy_destroy((struct wl_proxy *) w.compositor);\r\n\tif (w.registry)\t\tpwl_proxy_destroy((struct wl_proxy *) w.registry);\r\n\tif (w.display)\r\n\t{\t//let the server know everything before shutting it off\r\n\t\tpwl_display_dispatch(w.display);\r\n\t\tpwl_display_roundtrip(w.display);\r\n\t\tpwl_display_disconnect(w.display);\r\n\t}\r\n\tZ_Free(w.csdcaption);\r\n\tmemset(&w, 0, sizeof(w));\r\n}\r\nstatic qboolean WL_ApplyGammaRamps(unsigned int gammarampsize, unsigned short *ramps)\r\n{\r\n\t//not supported\r\n\treturn false;\r\n}\r\nstatic void WL_SetCaption(const char *text)\r\n{\r\n#ifdef XDG_SHELL_NAME\r\n\tif (w.xdg_toplevel)\r\n\t\txdg_toplevel_set_title(w.xdg_toplevel, text);\r\n#endif\r\n#ifdef WL_SHELL_NAME\r\n\tif (w.ssurface)\r\n\t\tpwl_shell_surface_set_title(w.ssurface, text);\r\n#endif\r\n\r\n\tZ_StrDupPtr(&w.csdcaption, text);\r\n}\r\n\r\nstatic int WL_GetPriority(void)\r\n{\r\n\t//2 = above x11, 0 = below x11.\r\n\tchar *stype = getenv(\"XDG_SESSION_TYPE\");\r\n\tchar *dpyname;\r\n\tif (stype)\r\n\t{\r\n\t\tif (!strcmp(stype, \"wayland\"))\r\n\t\t\treturn 2;\r\n\t\tif (!strcmp(stype, \"x11\"))\r\n\t\t\treturn 0;\r\n\t\tif (!strcmp(stype, \"tty\"))\t//FIXME: support this!\r\n\t\t\treturn 0;\r\n\t}\r\n\r\n\t//otherwise if both WAYLAND_DISPLAY and DISPLAY are defined, then we assume that we were started from xwayland wrapper thing, and that the native/preferred windowing system is wayland.\r\n\t//(lets just hope our wayland support is comparable)\r\n\r\n\tdpyname = getenv(\"WAYLAND_DISPLAY\");\r\n\tif (dpyname && *dpyname)\r\n\t\treturn 2;\t//something above X11.\r\n\treturn 0;\t//default.\r\n}\r\n\r\n#if defined(GLQUAKE) && defined(USE_EGL)\r\nrendererinfo_t rendererinfo_wayland_gl =\r\n{\r\n\t\"OpenGL (Wayland)\",\r\n\t{\r\n\t\t\"wlgl\",\r\n\t\t\"wayland\",\r\n\t},\r\n\tQR_OPENGL,\r\n\r\n\tGLDraw_Init,\r\n\tGLDraw_DeInit,\r\n\r\n\tGL_UpdateFiltering,\r\n\tGL_LoadTextureMips,\r\n\tGL_DestroyTexture,\r\n\r\n\tGLR_Init,\r\n\tGLR_DeInit,\r\n\tGLR_RenderView,\r\n\r\n\tWL_Init,\r\n\tWL_DeInit,\r\n\tWL_SwapBuffers,\r\n\tWL_ApplyGammaRamps,\r\n\tWL_CreateCursor,\r\n\tWL_SetCursor,\r\n\tWL_DestroyCursor,\r\n\tWL_SetCaption,       //setcaption\r\n\tGLVID_GetRGBInfo,\r\n\r\n\r\n\tGLSCR_UpdateScreen,\r\n\r\n\tGLBE_SelectMode,\r\n\tGLBE_DrawMesh_List,\r\n\tGLBE_DrawMesh_Single,\r\n\tGLBE_SubmitBatch,\r\n\tGLBE_GetTempBatch,\r\n\tGLBE_DrawWorld,\r\n\tGLBE_Init,\r\n\tGLBE_GenBrushModelVBO,\r\n\tGLBE_ClearVBO,\r\n\tGLBE_UpdateLightmaps,\r\n\tGLBE_SelectEntity,\r\n\tGLBE_SelectDLight,\r\n\tGLBE_Scissor,\r\n\tGLBE_LightCullModel,\r\n\r\n\tGLBE_VBO_Begin,\r\n\tGLBE_VBO_Data,\r\n\tGLBE_VBO_Finish,\r\n\tGLBE_VBO_Destroy,\r\n\r\n\tGLBE_RenderToTextureUpdate2d,\r\n\r\n\t\"\",\r\n\tWL_GetPriority,\r\n\tNULL,\r\n\tNULL,\r\n\tWL_MayRefresh\r\n};\r\n#endif\r\n\r\n#ifdef VKQUAKE\r\nstatic qboolean WLVK_EnumerateDevices(void *usercontext, void(*callback)(void *context, const char *devicename, const char *outputname, const char *desc))\r\n{\r\n\tqboolean ret = false;\r\n#ifdef VK_NO_PROTOTYPES\r\n\tPFN_vkGetInstanceProcAddr vkGetInstanceProcAddr;\r\n\tvoid *lib = NULL;\r\n\tdllfunction_t func[] =\r\n\t{\r\n\t\t{(void*)&vkGetInstanceProcAddr,\t\t\"vkGetInstanceProcAddr\"},\r\n\t\t{NULL,\t\t\t\t\t\t\tNULL}\r\n\t};\r\n\r\n\tif (!lib)\r\n\t\tlib = Sys_LoadLibrary(\"libvulkan.so.1\", func);\r\n\tif (!lib)\r\n\t\tlib = Sys_LoadLibrary(\"libvulkan.so\", func);\r\n\tif (!lib)\r\n\t\treturn false;\r\n#endif\r\n\tret = VK_EnumerateDevices(usercontext, callback, \"Vulkan-Wayland-\", vkGetInstanceProcAddr);\r\n#ifdef VK_NO_PROTOTYPES\r\n\tSys_CloseLibrary(lib);\r\n#endif\r\n\treturn ret;\r\n}\r\nrendererinfo_t rendererinfo_wayland_vk =\r\n{\r\n    \"Vulkan (Wayland)\",\r\n    {\r\n        \"wlvk\",\r\n        \"vk\",\r\n        \"vulkan\",\r\n        \"wayland\"\r\n    },\r\n    QR_VULKAN,\r\n\r\n    VK_Draw_Init,\r\n    VK_Draw_Shutdown,\r\n\r\n    VK_UpdateFiltering,\r\n    VK_LoadTextureMips,\r\n    VK_DestroyTexture,\r\n\r\n    VK_R_Init,\r\n    VK_R_DeInit,\r\n    VK_R_RenderView,\r\n\r\n    WL_Init,\r\n    WL_DeInit,\r\n    WL_SwapBuffers,\r\n    WL_ApplyGammaRamps,\r\n\r\n    WL_CreateCursor,\r\n    WL_SetCursor,\r\n    WL_DestroyCursor,\r\n    WL_SetCaption,       //setcaption\r\n    VKVID_GetRGBInfo,\r\n\r\n\r\n    VK_SCR_UpdateScreen,\r\n\r\n    VKBE_SelectMode,\r\n    VKBE_DrawMesh_List,\r\n    VKBE_DrawMesh_Single,\r\n    VKBE_SubmitBatch,\r\n    VKBE_GetTempBatch,\r\n    VKBE_DrawWorld,\r\n    VKBE_Init,\r\n    VKBE_GenBrushModelVBO,\r\n    VKBE_ClearVBO,\r\n    VKBE_UploadAllLightmaps,\r\n    VKBE_SelectEntity,\r\n    VKBE_SelectDLight,\r\n    VKBE_Scissor,\r\n    VKBE_LightCullModel,\r\n\r\n    VKBE_VBO_Begin,\r\n    VKBE_VBO_Data,\r\n    VKBE_VBO_Finish,\r\n    VKBE_VBO_Destroy,\r\n\r\n    VKBE_RenderToTextureUpdate2d,\r\n\r\n    \"\",\r\n\tWL_GetPriority,\r\n\tNULL,\r\n\tWLVK_EnumerateDevices\r\n};\r\n#endif\r\n\r\n#endif\r\n\r\n"
  },
  {
    "path": "engine/gl/gl_warp.c",
    "content": "/*\nCopyright (C) 1996-1997 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n*/\n// gl_warp.c -- sky and water polygons\n\n#include \"quakedef.h\"\n#ifdef HAVE_CLIENT\n#include \"glquake.h\"\n#include \"shader.h\"\n#include <ctype.h>\n\nstatic void R_CalcSkyChainBounds (batch_t *s);\nstatic void GL_DrawSkySphere (batch_t *fa, shader_t *shader);\nstatic void GL_SkyForceDepth(batch_t *fa);\nstatic void GL_DrawSkyBox (texid_t *texnums, batch_t *s);\n\nstatic void GL_DrawSkyGrid (texnums_t *tex);\n\nstatic void QDECL R_SkyBox_Changed (struct cvar_s *var, char *oldvalue);\n\ncvar_t r_fastsky\t\t\t\t\t\t\t= CVARF (\"r_fastsky\", \"0\",\tCVAR_ARCHIVE);\nstatic cvar_t r_fastskycolour\t\t\t\t\t\t= CVARF (\"r_fastskycolour\", \"0\",\tCVAR_RENDERERCALLBACK|CVAR_SHADERSYSTEM);\nstatic cvar_t gl_skyboxdist\t\t\t\t\t\t= CVARD  (\"gl_skyboxdist\", \"0\", \"The distance of the skybox. If 0, the engine will determine it based upon the far clip plane distance.\");\t//0 = guess.\nstatic cvar_t r_skycloudalpha\t\t\t\t\t\t= CVARFD (\"r_skycloudalpha\", \"1\", CVAR_RENDERERLATCH, \"Controls how opaque the front layer of legacy scrolling skies should be.\");\ncvar_t r_skyboxname\t\t\t\t\t\t\t= CVARFC (\"r_skybox\", \"\", CVAR_RENDERERCALLBACK | CVAR_SHADERSYSTEM, R_SkyBox_Changed);\ncvar_t r_skybox_orientation\t\t\t\t\t= CVARFD (\"r_glsl_skybox_orientation\", \"0 0 0 0\", CVAR_SHADERSYSTEM, \"Defines the axis around which skyboxes will rotate (the first three values). The fourth value defines the speed the skybox rotates at, in degrees per second.\");\ncvar_t r_skybox_autorotate\t\t\t\t\t= CVARFD (\"r_glsl_skybox_autorotate\", \"1\", CVAR_SHADERSYSTEM, \"Defines the axis around which skyboxes will rotate (the first three values). The fourth value defines the speed the skybox rotates at, in degrees per second.\");\ncvar_t r_skyfog\t\t\t\t\t\t\t\t= CVARD  (\"r_skyfog\", \"0.5\", \"This controls an alpha-blend value for fog on skyboxes, cumulative with regular fog alpha.\");\n\nstatic shader_t *forcedsky;\nstatic shader_t *skyboxface;\nstatic shader_t *skygridface;\n\n\n//=========================================================\n\n//called on video shutdown to reset internal state\nvoid R_SkyShutdown(void)\n{\n\tskyboxface = NULL;\n\tskygridface = NULL;\n\tforcedsky = NULL;\n}\n\n//lets the backend know which fallback envmap it can use.\ntexid_t R_GetDefaultEnvmap(void)\n{\n\tif (*r_refdef.nearenvmap.texname)\n\t\treturn Image_GetTexture(r_refdef.nearenvmap.texname, NULL, IF_TEXTYPE_CUBE, NULL, NULL, 0, 0, TF_INVALID);\n\n\tif (forcedsky && TEXLOADED(forcedsky->defaulttextures->reflectcube))\n\t\treturn forcedsky->defaulttextures->reflectcube;\n\n\treturn r_nulltex;\n}\n\nvoid R_SetSky(const char *sky)\n{\n\tint i;\n\tconst char *shadername;\n\textern cvar_t r_skyboxname;\n\tif (sky)\n\t\tQ_strncpyz(cl.skyname, sky, sizeof(cl.skyname));\n\telse\n\t\tsky = cl.skyname;\n\tif (qrenderer <= QR_NONE)\n\t\treturn;\t//not ready yet...\n\tif (*r_skyboxname.string)\t//override it with the user's preference\n\t\tsky = r_skyboxname.string;\n\n\tshadername = va(\"skybox_%s\", sky);\n\tif (!forcedsky || strcmp(shadername, forcedsky->name))\n\t{\n\t\ttexnums_t tex;\n\t\tforcedsky = NULL;\t//fall back to regular skies if forcing fails.\n\n\t\tif (!*sky)\n\t\t\treturn; //no need to do anything\n\n\t\tmemset(&tex, 0, sizeof(tex));\n\n\t\ttex.base = R_LoadHiResTexture(sky, \"env:gfx/env\", IF_LOADNOW|IF_NOMIPMAP);\n\t\tif (tex.base && tex.base->status == TEX_LOADING)\n\t\t\tCOM_WorkerPartialSync(tex.base, &tex.base->status, TEX_LOADING);\n\t\tif (tex.base->width && TEXLOADED(tex.base))\n\t\t{\n\t\t\tforcedsky = R_RegisterShader(shadername, 0,\n\t\t\t\t\"{\\n\"\n\t\t\t\t\t\"sort sky\\n\"\n\t\t\t\t\t\"program defaultsky#EQUI\\n\"\n\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\"if !$unmaskedsky\\n\"\t/* Q2/HL require the skybox to not draw over geometry, shouldn't we force it? --eukara */\n\t\t\t\t\t\t\t\"depthwrite\\n\"\n\t\t\t\t\t\t\"endif\\n\"\n\t\t\t\t\t\t\"map \\\"$diffuse\\\"\\n\"\n\t\t\t\t\t\t\"tcgen skybox\\n\"\n\t\t\t\t\t\"}\\n\"\n\t\t\t\t\t\"surfaceparm nodlight\\n\"\n\t\t\t\t\"surfaceparm sky\\n\"\n\t\t\t\t\"}\");\n\t\t\tR_BuildDefaultTexnums(&tex, forcedsky, IF_WORLDTEX);\n\t\t\treturn;\n\t\t}\n\n\t\t//if we have cubemaps then we can just go and use a cubemap for our skybox\n\t\tif (sh_config.havecubemaps)\n\t\t{\n\t\t\tmemset(&tex, 0, sizeof(tex));\n\t\t\ttex.reflectcube = R_LoadHiResTexture(sky, \"env:gfx/env\", IF_LOADNOW|IF_TEXTYPE_CUBE|IF_NOMIPMAP|IF_CLAMP);\n\t\t\tif (tex.reflectcube && tex.reflectcube->status == TEX_LOADING)\n\t\t\t\tCOM_WorkerPartialSync(tex.reflectcube, &tex.reflectcube->status, TEX_LOADING);\n\t\t\tif (tex.reflectcube->width && TEXLOADED(tex.reflectcube))\n\t\t\t{\n\t\t\t\tforcedsky = R_RegisterShader(shadername, 0,\n\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\"sort sky\\n\"\n\t\t\t\t\t\t\"program defaultskybox\\n\"\n\t\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\t\"if !$unmaskedsky\\n\"\t/* Q2/HL require the skybox to not draw over geometry, shouldn't we force it? --eukara */\n\t\t\t\t\t\t\t\t\"depthwrite\\n\"\n\t\t\t\t\t\t\t\"endif\\n\"\n\t\t\t\t\t\t\t\"map \\\"$cube:$reflectcube\\\"\\n\"\n\t\t\t\t\t\t\t\"tcgen skybox\\n\"\n\t\t\t\t\t\t\"}\\n\"\n\t\t\t\t\t\t\"surfaceparm nodlight\\n\"\n\t\t\t\t\t\"surfaceparm sky\\n\"\n\t\t\t\t\t\"}\");\n\t\t\t\tR_BuildDefaultTexnums(&tex, forcedsky, IF_WORLDTEX);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\t//crappy old path that I still need to fix up a bit\n\t\t//unlike cubemaps, this works on gl1.1/gles1, and also works with the different faces as different sizes.\n\t\tforcedsky = R_RegisterShader(shadername, 0, va(\"{\\nsort sky\\nskyparms \\\"%s\\\" 512 -\\nsurfaceparm nodlight\\n}\", sky));\n\t\t//check that we actually got some textures.\n\t\t//we accept the skybox if even 1 face is valid.\n\t\t//we ignore the replacement only request if all are invalid.\n\t\tfor (i = 0; i < 6; i++)\n\t\t{\n\t\t\textern texid_t missing_texture;\n\t\t\tif (forcedsky->skydome && forcedsky->skydome->farbox_textures[i] != missing_texture)\n\t\t\t\tbreak;\n\t\t}\n\t\tif (i == 6)\t//couldn't find ANY sky textures.\n\t\t\tforcedsky = NULL;\n\t}\n}\n\nstruct skylist_s\n{\n\tconst char *prefix;\n\tconst char *partial;\n\tsize_t partiallen;\n\tstruct xcommandargcompletioncb_s *ctx;\n};\nstatic int QDECL R_ForceSky_Enumerated (const char *name, qofs_t flags, time_t mtime, void *parm, searchpathfuncs_t *spath)\n{\n\tstruct skylist_s *ctx = parm;\n\tchar base[MAX_QPATH];\n\tconst char *ext;\n\tsize_t l = strlen(ctx->prefix), pl;\n\tsize_t p;\n\n\tstatic char *skypost[] =\n\t{\n\t\t\"rt\",\"lf\",\"bk\",\"ft\",\"up\",\"dn\",\n\t\t\"px\",\"nx\",\"py\",\"ny\",\"pz\",\"nz\",\n\t\t\"posx\",\"negx\",\"posy\",\"negy\",\"posz\",\"negz\",\n\t};\n\n\tif (!strncmp(name, ctx->prefix, l))\n\t{\n\t\text = COM_GetFileExtension(name+l, NULL);\n\t\tCOM_StripExtension(name+l, base, sizeof(base));\n\t\tl = strlen(base);\n\t\tfor (p = 0; p < countof(skypost); p++)\n\t\t{\n\t\t\tpl = strlen(skypost[p]);\n\t\t\tif (pl > l)\n\t\t\t\tcontinue;\n\t\t\tif (!strcmp(base+l-pl, skypost[p]))\n\t\t\t{\n\t\t\t\tif (p%6)\n\t\t\t\t\treturn true;\n\t\t\t\tbase[l-pl] = 0;\t//strip the postfix too.\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (p == countof(skypost) && strcmp(ext, \".tga\") && strcmp(ext, \".png\"))\t//give it its extension back if its not a regular skybox.\n\t\t\tQ_strncatz(base, ext, sizeof(base));\n\t\tif (!Q_strncasecmp(base, ctx->partial, ctx->partiallen))\n\t\t{\n\t\t\t//non-matches are dds/ktx cubemaps, or equirectangular or something\n\t\t\tif (ctx->ctx)\n\t\t\t\tctx->ctx->cb(base, NULL, NULL, ctx->ctx);\n\t\t\telse\n\t\t\t\tCon_Printf(\"\\t^[%s\\\\type\\\\%s %s^]\\n\", base, r_skyboxname.name, base);\n\t\t}\n\t}\n\treturn true;\n}\nstatic void R_ForceSky_c(int argn, const char *partial, struct xcommandargcompletioncb_s *ctx)\n{\n\tstatic char *skypath[] =\n\t{\n\t\t\"env/\",\n\t\t\"gfx/env/\",\n\t\t\"textures/env/\",\n\t\t\"textures/gfx/env/\",\n\t};\n\tstruct skylist_s l;\n\tsize_t pre;\n\tl.ctx = ctx;\n\tl.partial = partial;\n\tl.partiallen = strlen(partial);\n\tfor (pre = 0; pre < countof(skypath); pre++)\n\t{\n\t\tl.prefix = skypath[pre];\n\t\tCOM_EnumerateFiles(va(\"%s*.*\", l.prefix), R_ForceSky_Enumerated, &l);\n\t}\n\n\t//no skybox is also an option.\n\tif (ctx && !*partial)\n\t\tctx->cb(\"\", NULL, NULL, ctx);\n}\nstatic void R_ListSkyBoxes_f(void)\n{\n\tCon_Printf(\"Skybox Options:\\n\");\n\tR_ForceSky_c(0, \"\", NULL);\n}\nstatic void R_ForceSky_f(void)\n{\n\tif (Cmd_Argc() < 2)\n\t{\n\t\tif (*r_skyboxname.string)\n\t\t\tCon_Printf(\"Current user skybox is %s\\n\", r_skyboxname.string);\n\t\telse if (*cl.skyname)\n\t\t\tCon_Printf(\"Current per-map skybox is %s\\n\", cl.skyname);\n\t\telse\n\t\t\tCon_Printf(\"no skybox forced.\\n\");\n\t}\n\telse\n\t{\n\t\tR_SetSky(Cmd_Argv(1));\n\t}\n}\nvoid QDECL R_SkyBox_Changed (struct cvar_s *var, char *oldvalue)\n{\n\tR_SetSky(NULL);\n//\tShader_NeedReload(false);\n}\n\nvoid R_DrawFastSky(batch_t *batch)\n{\n\tbatch_t b = *batch;\n\tb.shader = R_RegisterShader(\"fastsky\", 0, \"{\\n\"\n\t\t\t\t\t\"sort sky\\n\"\n\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\"map $whiteimage\\n\"\n\t\t\t\t\t\t\"rgbgen const $r_fastskycolour\\n\"\n\t\t\t\t\t\"}\\n\"\n\t\t\t\t\t\"surfaceparm nodlight\\n\"\n\t\t\t\t\"}\\n\");\n\tb.skin = NULL;\n\tb.texture = NULL;\n\tBE_SubmitBatch(&b);\n}\n\n//annoyingly sky does not always write depth values.\n//this can allow entities to appear beyond it.\n//this can include (massive) entities outside of the skyroom.\n//so, we can only draw entities in skyrooms if:\n//1) either the ents have robust pvs info and we can draw ONLY the ones actually inside the skyroom\n//2) or if r_ignoreentpvs==1 we must mask depth and waste a whole load of batches drawing invisible ents in the sky\nextern cvar_t r_ignoreentpvs;\n\nqboolean R_DrawSkyroom(shader_t *skyshader)\n{\n\tfloat vmat[16];\n\trefdef_t oldrefdef;\n\tint oldarea = r_viewarea, oldcluster[2] = {r_viewcluster,r_viewcluster2};\n//\textern cvar_t r_ignoreentpvs; //legacy value is 1...\n\n\tif (r_viewcluster == -1)\n\t\treturn false;\t//don't draw the skyroom if the camera is outside.\n\n\tif (r_fastsky.value)\n\t\treturn false;\t//skyrooms can be expensive.\n\tif (!r_refdef.skyroom_enabled || r_refdef.recurse >= R_MAX_RECURSE-1)\n\t\treturn false;\n\n\toldrefdef = r_refdef;\n\tr_refdef.recurse+=1;\n\n//\tif (r_ignoreentpvs.ival)\t//if we're ignoring ent pvs then we're probably drawing lots of ents in the skybox that shouldn't be there\n//\t\tr_refdef.firstvisedict = cl_numvisedicts;\n\tr_refdef.externalview = true;\t//an out-of-body experience...\n\tr_refdef.skyroom_enabled = false;\n\tr_refdef.forcevis = false;\n\tr_refdef.flags |= RDF_DISABLEPARTICLES;\n\tr_refdef.flags &= ~RDF_SKIPSKY;\n\tr_refdef.forcedvis = NULL;\n\tr_refdef.areabitsknown = false;\t//recalculate areas clientside.\n\tr_refdef.sceneareas = NULL;\n\n\tif (cl.fog[FOGTYPE_SKYROOM].density)\n\t{\n\t\tCL_BlendFog(&r_refdef.globalfog, &cl.oldfog[FOGTYPE_SKYROOM], realtime, &cl.fog[FOGTYPE_SKYROOM]);\n\t\tr_refdef.globalfog.density/=64;\n\t}\n\n\t/*work out where the camera should be (use the same angles)*/\n\tVectorCopy(r_refdef.skyroom_pos, r_refdef.vieworg);\n\tVectorCopy(r_refdef.skyroom_pos, r_refdef.pvsorigin);\n\n\tif (developer.ival)\n\t\tif (r_worldentity.model->funcs.PointContents(r_worldentity.model, NULL, r_refdef.skyroom_pos) & FTECONTENTS_SOLID)\n\t\t\tCon_DPrintf(\"Skyroom position %.1f %.1f %.1f in solid\\n\", r_refdef.skyroom_pos[0], r_refdef.skyroom_pos[1], r_refdef.skyroom_pos[2]);\n\n\tif (r_refdef.skyroom_spin[3])\n\t{\n\t\tvec3_t axis[3];\n\t\tfloat ang = r_refdef.skyroom_spin[3];\n\t\tif (!r_refdef.skyroom_spin[0]&&!r_refdef.skyroom_spin[1]&&!r_refdef.skyroom_spin[2])\n\t\t\tVectorSet(r_refdef.skyroom_spin, 0,0,1);\n\t\tVectorNormalize(r_refdef.skyroom_spin);\n\t\tRotatePointAroundVector(axis[0], r_refdef.skyroom_spin, vpn, ang);\n\t\tRotatePointAroundVector(axis[1], r_refdef.skyroom_spin, vright, ang);\n\t\tRotatePointAroundVector(axis[2], r_refdef.skyroom_spin, vup, ang);\n\t\tMatrix4x4_CM_ModelViewMatrixFromAxis(vmat, axis[0], axis[1], axis[2], r_refdef.vieworg);\n\t}\n\telse\n\t\tMatrix4x4_CM_ModelViewMatrixFromAxis(vmat, vpn, vright, vup, r_refdef.vieworg);\n\tR_SetFrustum (r_refdef.m_projection_std, vmat);\n\n\t//now determine the stuff the backend will use.\n\tmemcpy(r_refdef.m_view, vmat, sizeof(float)*16);\n\tVectorAngles(vpn, vup, r_refdef.viewangles, false);\n\tVectorCopy(r_refdef.vieworg, r_origin);\n\n\tSurf_SetupFrame();\n\tSurf_DrawWorld ();\n\n\tr_viewarea = oldarea;\n\tr_viewcluster = oldcluster[0];\n\tr_viewcluster2 = oldcluster[1];\n\tr_refdef = oldrefdef;\n\n\t/*broken stuff*/\n\tAngleVectors (r_refdef.viewangles, vpn, vright, vup);\n\tVectorCopy (r_refdef.vieworg, r_origin);\n\n\treturn true;\n}\n\n//q3 mustn't mask sky (breaks q3map2's invisible skyportals), whereas q1 must (or its a cheat). halflife doesn't normally expect masking.\n//we also MUST mask any sky inside skyrooms, or you'll see all the entities outside of the skyroom through the room's own sky (q3map2 skyportals are hopefully irrelevant in this case).\n#define SKYMUSTBEMASKED (r_worldentity.model->fromgame != fg_quake3 || ((r_refdef.flags & RDF_DISABLEPARTICLES) && r_ignoreentpvs.ival) || !cls.allow_unmaskedskyboxes)\n\n/*\n=================\nGL_DrawSkyChain\n=================\n*/\nqboolean R_DrawSkyChain (batch_t *batch)\n{\n\tshader_t *skyshader;\n\ttexid_t *skyboxtex;\n\n\tif (r_fastsky.value)\n\t{\n\t\tR_DrawFastSky(batch);\n\t\treturn true;\t//depth will always be drawn with this pathway.\n\t}\n\n\tif (forcedsky)\n\t{\n\t\tskyshader = forcedsky;\n\n\t\tif (r_refdef.flags & RDF_SKIPSKY)\n\t\t{\n\t\t\tif (r_worldentity.model->fromgame != fg_quake3)\n\t\t\t\tGL_SkyForceDepth(batch);\n\t\t\treturn true;\n\t\t}\n\n\t\tif (forcedsky->numpasses && !forcedsky->skydome && batch->mesh[0]->xyz_array)\n\t\t{\t//cubemap skies!\n\t\t\t//this is just a simple pass. we use glsl/texgen for any actual work\n\t\t\tbatch_t b = *batch;\n\t\t\tb.shader = forcedsky;\n\t\t\tb.skin = NULL;\n\t\t\tb.texture = NULL;\n\t\t\tBE_SubmitBatch(&b);\n\t\t\treturn true;\n\t\t}\n\t}\n\telse\n\t{\n\t\tskyshader = batch->shader;\n\t\tif (skyshader->prog)\t//glsl is expected to do the whole skybox/warpsky thing itself, with no assistance from this legacy code.\n\t\t{\n\t\t\tif (r_refdef.flags & RDF_SKIPSKY)\n\t\t\t{\n\t\t\t\tif (SKYMUSTBEMASKED)\n\t\t\t\t\tGL_SkyForceDepth(batch);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\t//if the first pass is transparent in some form, then be prepared to give it a skyroom behind.\n\t\t\treturn false;\t//draw as normal...\n\t\t}\n\t}\n\n\tif (skyshader->skydome)\n\t\tskyboxtex = skyshader->skydome->farbox_textures;\n\telse\n\t\tskyboxtex = NULL;\n\n\tif (r_refdef.flags & RDF_SKIPSKY)\n\t{\t//don't obscure the skyroom if the sky shader is opaque.\n\t\tqboolean opaque = false;\n\t\tif (skyshader->numpasses)\n\t\t{\n\t\t\tshaderpass_t *pass = skyshader->passes;\n\t\t\tif (pass->shaderbits & SBITS_ATEST_BITS)\t//alphatests\n\t\t\t\t;\n\t\t\telse if (pass->shaderbits & SBITS_MASK_BITS)\t//colormasks\n\t\t\t\t;\n\t\t\telse if ((pass->shaderbits & SBITS_BLEND_BITS) != 0 && (pass->shaderbits & SBITS_BLEND_BITS) != (SBITS_SRCBLEND_ONE|SBITS_DSTBLEND_ZERO))\t//blendfunc\n\t\t\t\t;\n\t\t\telse\n\t\t\t\topaque = true;\t//that shader looks like its opaque.\n\t\t}\n\t\tif (!opaque)\n\t\t\tGL_DrawSkySphere(batch, skyshader);\n\t}\n\telse if (skyboxtex && TEXVALID(*skyboxtex))\n\t{\t//draw a skybox if we were given the textures\n\t\tR_CalcSkyChainBounds(batch);\n\t\tGL_DrawSkyBox (skyboxtex, batch);\n\n\t\tif (skyshader->numpasses)\n\t\t\tGL_DrawSkySphere(batch, skyshader);\n\t}\n\telse if (skyshader->numpasses)\n\t{\t//if we have passes, then they're normally projected.\n\t\tif (*r_fastsky.string && skyshader->numpasses == 2 && TEXVALID(batch->shader->defaulttextures->base) && TEXVALID(batch->shader->defaulttextures->fullbright))\n\t\t{\t//we have a small perf trick to accelerate q1 skies, also helps avoids distortions, but doesn't work too well for any other type of sky.\n\t\t\tR_CalcSkyChainBounds(batch);\n\t\t\tGL_DrawSkyGrid(skyshader->defaulttextures);\n\t\t}\n\t\telse\n\t\t\tGL_DrawSkySphere(batch, skyshader);\n\t}\n\telse if (batch->meshes)\n\t{\t//skys are weird.\n\t\t//they're the one type of surface with implicit nodraw when there's no passes.\n\t\tif ((skyboxtex&&*skyboxtex) || batch->shader->numpasses)\n\t\t\tR_DrawFastSky(batch);\n\t\treturn true;\t//depth will always be drawn with this pathway... or we were not drawing anything anyway...\n\t}\n\n\t//neither skydomes nor skyboxes nor skygrids will have been drawn with the correct depth values for the sky.\n\t//this can result in rooms behind the sky surfaces being visible.\n\t//so make sure they're correct where they're expected to be.\n\t//don't do it on q3 bsp, because q3map2 can't do skyrooms without being weird about it. or something. anyway, we get different (buggy) behaviour from q3 if we don't skip this.\n\t//See: The Edge Of Forever (motef, by sock) for an example of where this needs to be skipped.\n\t//See dm3 for an example of where the depth needs to be correct (OMG THERE'S PLAYERS IN MY SKYBOX! WALLHAXX!).\n\t//you can't please them all.\n\tif (SKYMUSTBEMASKED)\n\t\tGL_SkyForceDepth(batch);\n\n\treturn true;\n}\n\n/*\n=================================================================\n\n  Quake 2 environment sky\n\n=================================================================\n*/\n\nstatic vec3_t\tskyclip[6] = {\n\t{1,1,0},\n\t{1,-1,0},\n\t{0,-1,1},\n\t{0,1,1},\n\t{1,0,1},\n\t{-1,0,1}\n};\n\n// 1 = s, 2 = t, 3 = 2048\nstatic int\tst_to_vec[6][3] =\n{\n\t{3,-1,2},\n\t{-3,1,2},\n\n\t{1,3,2},\n\t{-1,-3,2},\n\n\t{-2,-1,3},\t\t// 0 degrees yaw, look straight up\n\t{2,-1,-3}\t\t// look straight down\n\n//\t{-1,2,3},\n//\t{1,2,-3}\n};\n\n// s = [0]/[2], t = [1]/[2]\nstatic int\tvec_to_st[6][3] =\n{\n\t{-2,3,1},\n\t{2,3,-1},\n\n\t{1,3,2},\n\t{-1,3,-2},\n\n\t{-2,-1,3},\n\t{-2,1,-3}\n\n//\t{-1,2,3},\n//\t{1,2,-3}\n};\n\nstatic float\tskymins[2][6], skymaxs[2][6];\n\nstatic void DrawSkyPolygon (int nump, vec3_t vecs)\n{\n\tint\t\ti,j;\n\tvec3_t\tv, av;\n\tfloat\ts, t, dv;\n\tint\t\taxis;\n\tfloat\t*vp;\n\n\t// decide which face it maps to\n\tVectorClear (v);\n\tfor (i=0, vp=vecs ; i<nump ; i++, vp+=3)\n\t{\n\t\tVectorAdd (vp, v, v);\n\t}\n\tav[0] = fabs(v[0]);\n\tav[1] = fabs(v[1]);\n\tav[2] = fabs(v[2]);\n\tif (av[0] > av[1] && av[0] > av[2])\n\t{\n\t\tif (v[0] < 0)\n\t\t\taxis = 1;\n\t\telse\n\t\t\taxis = 0;\n\t}\n\telse if (av[1] > av[2] && av[1] > av[0])\n\t{\n\t\tif (v[1] < 0)\n\t\t\taxis = 3;\n\t\telse\n\t\t\taxis = 2;\n\t}\n\telse\n\t{\n\t\tif (v[2] < 0)\n\t\t\taxis = 5;\n\t\telse\n\t\t\taxis = 4;\n\t}\n\n\t// project new texture coords\n\tfor (i=0 ; i<nump ; i++, vecs+=3)\n\t{\n\t\tj = vec_to_st[axis][2];\n\t\tif (j > 0)\n\t\t\tdv = vecs[j - 1];\n\t\telse\n\t\t\tdv = -vecs[-j - 1];\n\n\t\tif (dv < 0.001)\n\t\t\tcontinue;\t// don't divide by zero\n\n\t\tj = vec_to_st[axis][0];\n\t\tif (j < 0)\n\t\t\ts = -vecs[-j -1] / dv;\n\t\telse\n\t\t\ts = vecs[j-1] / dv;\n\t\tj = vec_to_st[axis][1];\n\t\tif (j < 0)\n\t\t\tt = -vecs[-j -1] / dv;\n\t\telse\n\t\t\tt = vecs[j-1] / dv;\n\n\t\tif (skymins[0][axis] > s)\n\t\t\tskymins[0][axis] = s;\n\t\tif (skymins[1][axis] > t)\n\t\t\tskymins[1][axis] = t;\n\t\tif (skymaxs[0][axis] < s)\n\t\t\tskymaxs[0][axis] = s;\n\t\tif (skymaxs[1][axis] < t)\n\t\t\tskymaxs[1][axis] = t;\n\t}\n}\n\n#define\tMAX_CLIP_VERTS\t64\nstatic void ClipSkyPolygon (int nump, vec3_t vecs, int stage)\n{\n\tfloat\t*norm;\n\tfloat\t*v;\n\tqboolean\tfront, back;\n\tfloat\td, e;\n\tfloat\tdists[MAX_CLIP_VERTS];\n\tint\t\tsides[MAX_CLIP_VERTS];\n\tvec3_t\tnewv[2][MAX_CLIP_VERTS];\n\tint\t\tnewc[2];\n\tint\t\ti, j;\n\n\tif (nump > MAX_CLIP_VERTS-2)\n\t\tSys_Error (\"ClipSkyPolygon: MAX_CLIP_VERTS\");\n\tif (stage == 6)\n\t{\t// fully clipped, so draw it\n\t\tDrawSkyPolygon (nump, vecs);\n\t\treturn;\n\t}\n\n\tfront = back = false;\n\tnorm = skyclip[stage];\n\tfor (i=0, v = vecs ; i<nump ; i++, v+=3)\n\t{\n\t\td = DotProduct (v, norm);\n\t\tif (d > ON_EPSILON)\n\t\t{\n\t\t\tfront = true;\n\t\t\tsides[i] = SIDE_FRONT;\n\t\t}\n\t\telse if (d < -ON_EPSILON)\n\t\t{\n\t\t\tback = true;\n\t\t\tsides[i] = SIDE_BACK;\n\t\t}\n\t\telse\n\t\t\tsides[i] = SIDE_ON;\n\t\tdists[i] = d;\n\t}\n\n\tif (!front || !back)\n\t{\t// not clipped\n\t\tClipSkyPolygon (nump, vecs, stage+1);\n\t\treturn;\n\t}\n\n\t// clip it\n\tsides[i] = sides[0];\n\tdists[i] = dists[0];\n\tVectorCopy (vecs, (vecs+(i*3)) );\n\tnewc[0] = newc[1] = 0;\n\n\tfor (i=0, v = vecs ; i<nump ; i++, v+=3)\n\t{\n\t\tswitch (sides[i])\n\t\t{\n\t\tcase SIDE_FRONT:\n\t\t\tVectorCopy (v, newv[0][newc[0]]);\n\t\t\tnewc[0]++;\n\t\t\tbreak;\n\t\tcase SIDE_BACK:\n\t\t\tVectorCopy (v, newv[1][newc[1]]);\n\t\t\tnewc[1]++;\n\t\t\tbreak;\n\t\tcase SIDE_ON:\n\t\t\tVectorCopy (v, newv[0][newc[0]]);\n\t\t\tnewc[0]++;\n\t\t\tVectorCopy (v, newv[1][newc[1]]);\n\t\t\tnewc[1]++;\n\t\t\tbreak;\n\t\t}\n\n\t\tif (sides[i] == SIDE_ON || sides[i+1] == SIDE_ON || sides[i+1] == sides[i])\n\t\t\tcontinue;\n\n\t\td = dists[i] / (dists[i] - dists[i+1]);\n\t\tfor (j=0 ; j<3 ; j++)\n\t\t{\n\t\t\te = v[j] + d*(v[j+3] - v[j]);\n\t\t\tnewv[0][newc[0]][j] = e;\n\t\t\tnewv[1][newc[1]][j] = e;\n\t\t}\n\t\tnewc[0]++;\n\t\tnewc[1]++;\n\t}\n\n\t// continue\n\tClipSkyPolygon (newc[0], newv[0][0], stage+1);\n\tClipSkyPolygon (newc[1], newv[1][0], stage+1);\n}\n\n/*\n=================\nR_DrawSkyBoxChain\n=================\n*/\nstatic void R_CalcSkyChainBounds (batch_t *batch)\n{\n\tmesh_t *mesh;\n\n\tint\t\ti, m;\n\tvec3_t\tverts[MAX_CLIP_VERTS];\n\n\tif (batch->meshes == 1 && !batch->mesh[batch->firstmesh]->numindexes)\n\t{\t//deal with geometryless skies, like terrain/raw maps\n\t\tfor (i=0 ; i<6 ; i++)\n\t\t{\n\t\t\tskymins[0][i] = skymins[1][i] = -1;\n\t\t\tskymaxs[0][i] = skymaxs[1][i] = 1;\n\t\t}\n\t\treturn;\n\t}\n\tfor (i=0 ; i<6 ; i++)\n\t{\n\t\tskymins[0][i] = skymins[1][i] = 1;//9999;\n\t\tskymaxs[0][i] = skymaxs[1][i] = -1;//-9999;\n\t}\n\n\t// calculate vertex values for sky box\n\tfor (m = batch->firstmesh; m < batch->meshes; m++)\n\t{\n\t\tmesh = batch->mesh[m];\n\t\tif (!mesh->xyz_array || !mesh->indexes)\n\t\t\tcontinue;\n\t\t//triangulate\n\t\tfor (i = 0; i < mesh->numindexes; i+=3)\n\t\t{\n\t\t\tVectorSubtract (mesh->xyz_array[mesh->indexes[i+0]], r_origin, verts[0]);\n\t\t\tVectorSubtract (mesh->xyz_array[mesh->indexes[i+1]], r_origin, verts[1]);\n\t\t\tVectorSubtract (mesh->xyz_array[mesh->indexes[i+2]], r_origin, verts[2]);\n\t\t\tClipSkyPolygon (3, verts[0], 0);\n\t\t}\n\t}\n}\n\n#define skygridx 16\n#define skygridx1 (skygridx + 1)\n#define skygridxrecip (1.0f / (skygridx))\n#define skygridy 16\n#define skygridy1 (skygridy + 1)\n#define skygridyrecip (1.0f / (skygridy))\n#define skysphere_numverts (skygridx1 * skygridy1)\n#define skysphere_numtriangles (skygridx * skygridy * 2)\n\nstatic int skymade;\nstatic index_t skysphere_element3i[skysphere_numtriangles * 3];\nstatic float skysphere_texcoord2f[skysphere_numverts * 2];\n\nstatic vecV_t skysphere_vertex3f[skysphere_numverts];\nstatic mesh_t skymesh;\n\n\nstatic void gl_skyspherecalc(int skytype)\n{\t//yes, this is basically stolen from DarkPlaces\n\tint i, j;\n\tindex_t *e;\n\tfloat a, b, x, ax, ay, v[3], length, *texcoord2f;\n\tvecV_t* vertex;\n\tfloat dx, dy, dz;\n\n\tfloat texscale;\n\n\tif (skymade == skytype)\n\t\treturn;\n\n\tskymade = skytype;\n\n\tif (skymade == 2)\n\t\ttexscale = 1/16.0f;\n\telse\n\t\ttexscale = 1/1.5f;\n\n\ttexscale*=3;\n\n\tskymesh.indexes = skysphere_element3i;\n\tskymesh.st_array = (void*)skysphere_texcoord2f;\n\tskymesh.lmst_array[0] = (void*)skysphere_texcoord2f;\n\tskymesh.xyz_array = (void*)skysphere_vertex3f;\n\n\tskymesh.numindexes = skysphere_numtriangles * 3;\n\tskymesh.numvertexes = skysphere_numverts;\n\n\tdx = 1;\n\tdy = 1;\n\tdz = 1 / 3.0;\n\tvertex = skysphere_vertex3f;\n\ttexcoord2f = skysphere_texcoord2f;\n\tfor (j = 0;j <= skygridy;j++)\n\t{\n\t\ta = j * skygridyrecip;\n\t\tax = cos(a * M_PI * 2);\n\t\tay = -sin(a * M_PI * 2);\n\t\tfor (i = 0;i <= skygridx;i++)\n\t\t{\n\t\t\tb = i * skygridxrecip;\n\t\t\tx = cos((b + 0.5) * M_PI);\n\t\t\tv[0] = ax*x * dx;\n\t\t\tv[1] = ay*x * dy;\n\t\t\tv[2] = -sin((b + 0.5) * M_PI) * dz;\n\t\t\tlength = texscale / sqrt(v[0]*v[0]+v[1]*v[1]+(v[2]*v[2]*9));\n\t\t\t*texcoord2f++ = v[0] * length;\n\t\t\t*texcoord2f++ = v[1] * length;\n\t\t\t(*vertex)[0] = v[0];\n\t\t\t(*vertex)[1] = v[1];\n\t\t\t(*vertex)[2] = v[2];\n\t\t\tvertex++;\n\t\t}\n\t}\n\te = skysphere_element3i;\n\tfor (j = 0;j < skygridy;j++)\n\t{\n\t\tfor (i = 0;i < skygridx;i++)\n\t\t{\n\t\t\t*e++ =  j      * skygridx1 + i;\n\t\t\t*e++ =  j      * skygridx1 + i + 1;\n\t\t\t*e++ = (j + 1) * skygridx1 + i;\n\n\t\t\t*e++ =  j      * skygridx1 + i + 1;\n\t\t\t*e++ = (j + 1) * skygridx1 + i + 1;\n\t\t\t*e++ = (j + 1) * skygridx1 + i;\n\t\t}\n\t}\n}\n\nstatic void GL_SkyForceDepth(batch_t *batch)\n{\n\tif (!cls.allow_unmaskedskyboxes && batch->texture)\t//allow a little extra fps.\n\t{\n\t\tBE_SelectMode(BEM_DEPTHONLY);\n\t\tBE_DrawMesh_List(batch->shader, batch->meshes-batch->firstmesh, batch->mesh+batch->firstmesh, batch->vbo, NULL, batch->flags);\n\t\tBE_SelectMode(BEM_STANDARD);\t/*skys only render in standard mode anyway, so this is safe*/\n\t}\n}\n\nstatic void R_DrawSkyMesh(batch_t *batch, mesh_t *m, shader_t *shader)\n{\n\tstatic entity_t skyent;\n\tbatch_t b;\n\n\tfloat skydist = gl_skyboxdist.value;\n\tif (skydist<1)\n\t\tskydist=r_refdef.maxdist * 0.577;\n\tif (skydist<1)\n\t\tskydist = 10000000;\n\n\tVectorCopy(r_refdef.vieworg, skyent.origin);\n\tskyent.axis[0][0] = skydist;\n\tskyent.axis[0][1] = 0;\n\tskyent.axis[0][2] = 0;\n\tskyent.axis[1][0] = 0;\n\tskyent.axis[1][1] = skydist;\n\tskyent.axis[1][2] = 0;\n\tskyent.axis[2][0] = 0;\n\tskyent.axis[2][1] = 0;\n\tskyent.axis[2][2] = skydist;\n\tskyent.scale = 1;\n\n//FIXME: We should use the skybox clipping code and split the sphere into 6 sides.\n\tb = *batch;\n\tb.meshes = 1;\n\tb.firstmesh = 0;\n\tb.mesh = &m;\n\tb.ent = &skyent;\n\tb.shader = shader;\n\tb.skin = NULL;\n\tb.texture = NULL;\n\tb.vbo = NULL;\n\tVector4Set(skyent.shaderRGBAf, 1, 1, 1, 1);\n\tBE_SubmitBatch(&b);\n}\n\nstatic void GL_DrawSkySphere (batch_t *batch, shader_t *shader)\n{\n\t//FIXME: We should use the skybox clipping code and split the sphere into 6 sides.\n\tgl_skyspherecalc(2);\n\tR_DrawSkyMesh(batch, &skymesh, shader);\n}\n\nstatic void GL_MakeSkyVec (float s, float t, int axis, float *vc, float *tc)\n{\n\tvec3_t\t\tb;\n\tint\t\t\tj, k;\n\n\tb[0] = s;\n\tb[1] = t;\n\tb[2] = 1;\n\n\tfor (j=0 ; j<3 ; j++)\n\t{\n\t\tk = st_to_vec[axis][j];\n\t\tif (k < 0)\n\t\t\tvc[j] = -b[-k - 1];\n\t\telse\n\t\t\tvc[j] = b[k - 1];\n\t}\n\n\t// avoid bilerp seam\n\ts = (s+1)*0.5;\n\tt = (t+1)*0.5;\n\n\tif (s < 1.0/512)\n\t\ts = 1.0/512;\n\telse if (s > 511.0/512)\n\t\ts = 511.0/512;\n\tif (t < 1.0/512)\n\t\tt = 1.0/512;\n\telse if (t > 511.0/512)\n\t\tt = 511.0/512;\n\n\ttc[0] = s;\n\ttc[1] = 1.0 - t;\n}\n\n\nstatic float\tspeedscale1;\t// for top sky\nstatic float\tspeedscale2;\t// for bottom sky\nstatic void EmitSkyGridVert (vec3_t v, vec2_t tc1, vec2_t tc2)\n{\n\tvec3_t dir;\n\tfloat\tlength;\n\n\tVectorSubtract (v, r_origin, dir);\n\tdir[2] *= 3;\t// flatten the sphere\n\n\tlength = VectorLength (dir);\n\tlength = 6*63/length;\n\n\tdir[0] *= length;\n\tdir[1] *= length;\n\n\ttc1[0] = (speedscale1 + dir[0]) * (1.0/128);\n\ttc1[1] = (speedscale1 + dir[1]) * (1.0/128);\n\n\ttc2[0] = (speedscale2 + dir[0]) * (1.0/128);\n\ttc2[1] = (speedscale2 + dir[1]) * (1.0/128);\n}\n\n// s and t range from -1 to 1\nstatic void MakeSkyGridVec2 (float s, float t, int axis, vec3_t v, vec2_t tc1, vec2_t tc2)\n{\n\tvec3_t\t\tb;\n\tint\t\t\tj, k;\n\n\tfloat skydist = gl_skyboxdist.value;\n\tif (skydist<1)\n\t\tskydist=r_refdef.maxdist * 0.577;\n\tif (skydist<1)\n\t\tskydist = 10000000;\n\n\tb[0] = s*skydist;\n\tb[1] = t*skydist;\n\tb[2] = skydist;\n\n\tfor (j=0 ; j<3 ; j++)\n\t{\n\t\tk = st_to_vec[axis][j];\n\t\tif (k < 0)\n\t\t\tv[j] = -b[-k - 1];\n\t\telse\n\t\t\tv[j] = b[k - 1];\n\t\tv[j] += r_origin[j];\n\t}\n\n\tEmitSkyGridVert(v, tc1, tc2);\n}\n\n#define SUBDIVISIONS\t10\n\nstatic void GL_DrawSkyGridFace (int axis, mesh_t *fte_restrict mesh)\n{\n\tint i, j;\n\tfloat s, t;\n\n\tfloat fstep = 2.0 / SUBDIVISIONS;\n\n\tfor (i = 0; i < SUBDIVISIONS; i++)\n\t{\n\t\ts = (float)(i*2 - SUBDIVISIONS) / SUBDIVISIONS;\n\n\t\tif (s + fstep < skymins[0][axis] || s > skymaxs[0][axis])\n\t\t\tcontinue;\n\n\t\tfor (j = 0; j < SUBDIVISIONS; j++)\n\t\t{\n\t\t\tt = (float)(j*2 - SUBDIVISIONS) / SUBDIVISIONS;\n\n\t\t\tif (t + fstep < skymins[1][axis] || t > skymaxs[1][axis])\n\t\t\t\tcontinue;\n\n\t\t\tmesh->indexes[mesh->numindexes++] = mesh->numvertexes+0;\n\t\t\tmesh->indexes[mesh->numindexes++] = mesh->numvertexes+1;\n\t\t\tmesh->indexes[mesh->numindexes++] = mesh->numvertexes+2;\n\t\t\tmesh->indexes[mesh->numindexes++] = mesh->numvertexes+0;\n\t\t\tmesh->indexes[mesh->numindexes++] = mesh->numvertexes+2;\n\t\t\tmesh->indexes[mesh->numindexes++] = mesh->numvertexes+3;\n\n\t\t\tMakeSkyGridVec2 (s,\t\t\tt,\t\t\taxis, mesh->xyz_array[mesh->numvertexes], mesh->st_array[mesh->numvertexes], mesh->lmst_array[0][mesh->numvertexes]); mesh->numvertexes++;\n\t\t\tMakeSkyGridVec2 (s,\t\t\tt + fstep,\taxis, mesh->xyz_array[mesh->numvertexes], mesh->st_array[mesh->numvertexes], mesh->lmst_array[0][mesh->numvertexes]); mesh->numvertexes++;\n\t\t\tMakeSkyGridVec2 (s + fstep, t + fstep,\taxis, mesh->xyz_array[mesh->numvertexes], mesh->st_array[mesh->numvertexes], mesh->lmst_array[0][mesh->numvertexes]); mesh->numvertexes++;\n\t\t\tMakeSkyGridVec2 (s + fstep, t,\t\t\taxis, mesh->xyz_array[mesh->numvertexes], mesh->st_array[mesh->numvertexes], mesh->lmst_array[0][mesh->numvertexes]); mesh->numvertexes++;\n\t\t}\n\t}\n}\n\nstatic void GL_DrawSkyGrid (texnums_t *tex)\n{\n\tstatic entity_t skyent;\n\tstatic batch_t b;\n\tstatic mesh_t skymesh, *meshptr=&skymesh;\n\n\tvecV_t coords[SUBDIVISIONS*SUBDIVISIONS*4*6];\n\tvec2_t texcoords1[SUBDIVISIONS*SUBDIVISIONS*4*6];\n\tvec2_t texcoords2[SUBDIVISIONS*SUBDIVISIONS*4*6];\n\tindex_t indexes[SUBDIVISIONS*SUBDIVISIONS*6*6];\n\n\tint i;\n\tfloat time = cl.gametime+realtime-cl.gametimemark;\n\n\tspeedscale1 = time*8;\n\tspeedscale1 -= (int)speedscale1 & ~127;\n\tspeedscale2 = time*16;\n\tspeedscale2 -= (int)speedscale2 & ~127;\n\n\tskymesh.indexes = indexes;\n\tskymesh.st_array = texcoords1;\n\tskymesh.lmst_array[0] = texcoords2;\n\tskymesh.xyz_array = coords;\n\tskymesh.numindexes = 0;\n\tskymesh.numvertexes = 0;\n\n\tfor (i = 0; i < 6; i++)\n\t{\n\t\tif ((skymins[0][i] >= skymaxs[0][i]\t|| skymins[1][i] >= skymaxs[1][i]))\n\t\t\tcontinue;\n\t\tGL_DrawSkyGridFace (i, &skymesh);\n\t}\n\n\tVectorCopy(r_refdef.vieworg, skyent.origin);\n\tskyent.axis[0][0] = 1;\n\tskyent.axis[0][1] = 0;\n\tskyent.axis[0][2] = 0;\n\tskyent.axis[1][0] = 0;\n\tskyent.axis[1][1] = 1;\n\tskyent.axis[1][2] = 0;\n\tskyent.axis[2][0] = 0;\n\tskyent.axis[2][1] = 0;\n\tskyent.axis[2][2] = 1;\n\tskyent.scale = 1;\n\n\tif (!skygridface)\n\t\tskygridface = R_RegisterShader(\"skygridface\", SUF_NONE,\n\t\t\t\t\"{\\n\"\n\t\t\t\t\t\"program default2d\\n\"\n\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\"map $diffuse\\n\"\n\t\t\t\t\t\t\"nodepth\\n\"\t//don't write depth. this stuff is meant to be an infiniteish distance away.\n\t\t\t\t\t\"}\\n\"\n\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\"map $fullbright\\n\"\n\t\t\t\t\t\t\"blendfunc blend\\n\"\n\t\t\t\t\t\t\"nodepth\\n\"\t//don't write depth. this stuff is meant to be an infiniteish distance away.\n\t\t\t\t\t\"}\\n\"\n\t\t\t\t\"}\\n\"\n\t\t\t);\n\n//FIXME: We should use the skybox clipping code and split the sphere into 6 sides.\n\tb.meshes = 1;\n\tb.firstmesh = 0;\n\tb.mesh = &meshptr;\n\tb.ent = &skyent;\n\tb.shader = skygridface;\n\tb.skin = tex;\n\tb.texture = NULL;\n\tb.vbo = NULL;\n\tBE_SubmitBatch(&b);\n}\n\n/*\n==============\nR_DrawSkyBox\n==============\n*/\nstatic int\tskytexorder[6] = {0,2,1,3,4,5};\nstatic void GL_DrawSkyBox (texid_t *texnums, batch_t *s)\n{\n\tint i;\n\n\tvecV_t skyface_vertex[4];\n\tvec2_t skyface_texcoord[4];\n\tindex_t skyface_index[6] = {0, 1, 2, 0, 2, 3};\n\tvec4_t skyface_colours[4] = {{1,1,1,1},{1,1,1,1},{1,1,1,1},{1,1,1,1}};\n\tmesh_t skyfacemesh = {0};\n\n\tif (cl.skyrotate)\n\t{\n\t\tfor (i=0 ; i<6 ; i++)\n\t\t{\n\t\t\tif (skymins[0][i] < skymaxs[0][i]\n\t\t\t\t&& skymins[1][i] < skymaxs[1][i])\n\t\t\t\t\tbreak;\n\n\t\t\tskymins[0][i] = -1;\t//fully visible\n\t\t\tskymins[1][i] = -1;\n\t\t\tskymaxs[0][i] = 1;\n\t\t\tskymaxs[1][i] = 1;\n\t\t}\n\t\tif (i == 6)\n\t\t\treturn;\t//can't see anything\n\t\tfor ( ; i<6 ; i++)\n\t\t{\n\t\t\tskymins[0][i] = -1;\n\t\t\tskymins[1][i] = -1;\n\t\t\tskymaxs[0][i] = 1;\n\t\t\tskymaxs[1][i] = 1;\n\t\t}\n\t}\n\n\tskyfacemesh.indexes = skyface_index;\n\tskyfacemesh.st_array = skyface_texcoord;\n\tskyfacemesh.xyz_array = skyface_vertex;\n\tskyfacemesh.colors4f_array[0] = skyface_colours;\n\tskyfacemesh.numindexes = 6;\n\tskyfacemesh.numvertexes = 4;\n\n\tif (!skyboxface)\n\t\tskyboxface = R_RegisterShader(\"skyboxface\", SUF_NONE,\n\t\t\t\t\"{\\n\"\n\t\t\t\t\t\"program default2d\\n\"\n\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\"map $diffuse\\n\"\n\t\t\t\t\t\t\"nodepth\\n\"\t//don't write depth. this stuff is meant to be an infiniteish distance away.\n\t\t\t\t\t\"}\\n\"\n\t\t\t\t\"}\\n\"\n\t\t\t);\n\n\tfor (i=0 ; i<6 ; i++)\n\t{\n\t\tif (skymins[0][i] >= skymaxs[0][i]\n\t\t|| skymins[1][i] >= skymaxs[1][i])\n\t\t\tcontinue;\n\n\t\tGL_MakeSkyVec (skymins[0][i], skymins[1][i], i, skyface_vertex[0], skyface_texcoord[0]);\n\t\tGL_MakeSkyVec (skymins[0][i], skymaxs[1][i], i, skyface_vertex[1], skyface_texcoord[1]);\n\t\tGL_MakeSkyVec (skymaxs[0][i], skymaxs[1][i], i, skyface_vertex[2], skyface_texcoord[2]);\n\t\tGL_MakeSkyVec (skymaxs[0][i], skymins[1][i], i, skyface_vertex[3], skyface_texcoord[3]);\n\n\t\tskyboxface->defaulttextures->base = texnums[skytexorder[i]];\n\t\tR_DrawSkyMesh(s, &skyfacemesh, skyboxface);\n\t}\n}\n\n//===============================================================\n\n/*\n=============\nR_InitSky\n\nA sky image is 256*128 and comprises two logical textures.\nthe left is the transparent/blended part. the right is the opaque/background part.\n==============\n*/\nvoid R_InitSky (shader_t *shader, const char *skyname, uploadfmt_t fmt, qbyte *src, unsigned int width, unsigned int height)\n{\n\tint\t\t\ti, j, p;\n\tunsigned\t*temp;\n\tunsigned\ttranspix, alphamask;\n\tint\t\t\tr, g, b;\n\tunsigned\t*rgba;\n\tchar name[MAX_QPATH*2];\n\n\tunsigned int stride = width;\n\twidth /= 2;\n\n\tif (width < 1 || height < 1 || stride != width*2 || !src)\n\t\treturn;\n\n\t//try to load dual-layer-single-image skies.\n\t//this is always going to be lame special case crap\n\tif (gl_load24bit.ival)\n\t{\n\t\tsize_t filesize = 0;\n\t\tqbyte *filedata = NULL;\n\t\tif (!filedata)\n\t\t{\n\t\t\tQ_snprintfz(name, sizeof(name), \"textures/%s.tga\", skyname);\n\t\t\tfiledata = FS_LoadMallocFile(name, &filesize);\n\t\t}\n\t\tif (!filedata)\n\t\t{\n\t\t\tQ_snprintfz(name, sizeof(name), \"textures/%s.png\", skyname);\n\t\t\tfiledata = FS_LoadMallocFile(name, &filesize);\n\t\t}\n\n\t\tif (filedata)\n\t\t{\n\t\t\tint imagewidth, imageheight;\n\t\t\tuploadfmt_t format;\t//fixme, if this has no alpha, is it worth all this code?\n\t\t\tunsigned int *imagedata = (unsigned int*)ReadRawImageFile(filedata, filesize, &imagewidth, &imageheight, &format, true, name);\n\t\t\tZ_Free(filedata);\n\n\t\t\tif (imagedata && !(imagewidth&1))\n\t\t\t{\n\t\t\t\timagewidth>>=1;\n\n\t\t\t\ttemp = BZF_Malloc(imagewidth*imageheight*sizeof(*temp));\n\t\t\t\tif (temp)\n\t\t\t\t{\n\t\t\t\t\tfor (i=0 ; i<imageheight ; i++)\n\t\t\t\t\t\tfor (j=0 ; j<imagewidth ; j++)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttemp[i*imagewidth+j] = imagedata[i*(imagewidth<<1)+j+imagewidth];\n\t\t\t\t\t\t}\n\t\t\t\t\tQ_snprintfz(name, sizeof(name), \"%s_solid\", skyname);\n\t\t\t\t\tQ_strlwr(name);\n\t\t\t\t\tshader->defaulttextures->base = R_LoadReplacementTexture(name, NULL, IF_NOALPHA, temp, imagewidth, imageheight, TF_RGBX32);\n\n\t\t\t\t\tfor (i=0 ; i<imageheight ; i++)\n\t\t\t\t\t\tfor (j=0 ; j<imagewidth ; j++)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttemp[i*imagewidth+j] = imagedata[i*(imagewidth<<1)+j];\n\t\t\t\t\t\t}\n\t\t\t\t\tBZ_Free(imagedata);\n\t\t\t\t\tQ_snprintfz(name, sizeof(name), \"%s_alpha:%s_trans\", skyname, skyname);\n\t\t\t\t\tQ_strlwr(name);\n\t\t\t\t\tshader->defaulttextures->fullbright = R_LoadReplacementTexture(name, NULL, 0, temp, imagewidth, imageheight, TF_RGBA32);\n\t\t\t\t\tBZ_Free(temp);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t\tBZ_Free(imagedata);\n\t\t}\n\t}\n\n\tif (fmt & PTI_FULLMIPCHAIN)\n\t{\t//input is expected to make sense...\n\t\tqbyte *front, *back;\n\t\tunsigned int bb, bw, bh, bd;\n\t\tunsigned int w, h, y;\n\t\tfmt = fmt&~PTI_FULLMIPCHAIN;\n\t\tImage_BlockSizeForEncoding(fmt, &bb, &bw, &bh, &bd);\n\n\t\tw = (width+bw-1)/bw;\n\t\th = (height+bh-1)/bh;\n\t\t//d = (depth+bd-1)/bd;\n\n\t\tback = BZ_Malloc(bb*w*2*h);\n\t\tfront = back + bb*w*h;\n\t\tfor (y = 0; y < h; y++)\n\t\t{\n\t\t\tmemcpy(back + bb*y*w, src + bb*(y*w*2+w), w*bb);\n\t\t\tmemcpy(front + bb*y*w, src + bb*(y*w*2), w*bb);\n\t\t}\n\t\tif (!shader->defaulttextures->base)\n\t\t{\n\t\t\tQ_snprintfz(name, sizeof(name), \"%s_solid\", skyname);\n\t\t\tQ_strlwr(name);\n\t\t\tshader->defaulttextures->base = R_LoadReplacementTexture(name, NULL, IF_NOALPHA, back, width, height, fmt);\n\t\t}\n\t\tif (!shader->defaulttextures->fullbright)\n\t\t{\t//FIXME: support _trans\n\t\t\tQ_snprintfz(name, sizeof(name), \"%s_alpha:%s_trans\", skyname, skyname);\n\t\t\tQ_strlwr(name);\n\t\t\tshader->defaulttextures->fullbright = R_LoadReplacementTexture(name, NULL, 0, front, width, height, fmt);\n\t\t}\n\t\tBZ_Free(back);\n\t}\n\telse\n\t{\n\t\ttemp = BZ_Malloc(width*height*sizeof(*temp));\n\n\t\t// make an average value for the back to avoid\n\t\t// a fringe on the top level\n\n\t\tr = g = b = 0;\n\t\tfor (i=0 ; i<height ; i++)\n\t\t\tfor (j=0 ; j<width ; j++)\n\t\t\t{\n\t\t\t\tp = src[i*stride + j + width];\n\t\t\t\trgba = &d_8to24rgbtable[p];\n\t\t\t\ttemp[(i*width) + j] = *rgba;\n\t\t\t\tr += ((qbyte *)rgba)[0];\n\t\t\t\tg += ((qbyte *)rgba)[1];\n\t\t\t\tb += ((qbyte *)rgba)[2];\n\t\t\t}\n\n\t\tif (!shader->defaulttextures->base)\n\t\t{\n\t\t\tQ_snprintfz(name, sizeof(name), \"%s_solid\", skyname);\n\t\t\tQ_strlwr(name);\n\t\t\tshader->defaulttextures->base = R_LoadReplacementTexture(name, NULL, IF_NOALPHA, temp, width, height, TF_RGBX32);\n\t\t}\n\n\t\tif (!shader->defaulttextures->fullbright)\n\t\t{\n\t\t\t//fixme: use premultiplied alpha here.\n\t\t\t((qbyte *)&transpix)[0] = r/(width*height);\n\t\t\t((qbyte *)&transpix)[1] = g/(width*height);\n\t\t\t((qbyte *)&transpix)[2] = b/(width*height);\n\t\t\t((qbyte *)&transpix)[3] = 0;\n\t\t\talphamask = r_skycloudalpha.value*255;\n\t\t\talphamask = ((bound(0, alphamask, 0xff)<<24) | 0x00ffffff);\n\t\t\talphamask = LittleLong(alphamask);\n\t\t\tfor (i=0 ; i<height ; i++)\n\t\t\t\tfor (j=0 ; j<width ; j++)\n\t\t\t\t{\n\t\t\t\t\tp = src[i*stride + j];\n\t\t\t\t\tif (p == 0)\n\t\t\t\t\t\ttemp[(i*width) + j] = transpix;\n\t\t\t\t\telse\n\t\t\t\t\t\ttemp[(i*width) + j] = d_8to24rgbtable[p] & alphamask;\n\t\t\t\t}\n\n\t\t\t//FIXME: support _trans\n\t\t\tQ_snprintfz(name, sizeof(name), \"%s_alpha:%s_trans\", skyname, skyname);\n\t\t\tQ_strlwr(name);\n\t\t\tshader->defaulttextures->fullbright = R_LoadReplacementTexture(name, NULL, 0, temp, width, height, TF_RGBA32);\n\t\t}\n\t\tBZ_Free(temp);\n\t}\n}\n\nvoid R_Sky_Register(void)\n{\n\tconst char *groupname = \"Skies\";\n\tCvar_Register (&r_skycloudalpha,\t\tgroupname);\n\tCvar_Register (&r_fastsky,\t\t\t\tgroupname);\n\tCvar_Register (&r_fastskycolour,\t\tgroupname);\n\tCvar_Register (&r_skyfog,\t\t\t\tgroupname);\n\tCvar_Register (&r_skyboxname,\t\t\tgroupname);\n\tCvar_Register (&r_skybox_orientation,\tgroupname);\n\tCvar_Register (&r_skybox_autorotate,\tgroupname);\n\tCvar_Register (&gl_skyboxdist,\t\t\tgroupname);\n\n\tCmd_AddCommandAD(\"sky\", R_ForceSky_f, R_ForceSky_c, \"For compat with Quakespasm, please use r_skybox.\");\t//QS compat\n\tCmd_AddCommandAD(\"loadsky\", R_ForceSky_f, R_ForceSky_c, \"For compat with DarkPlaces, please use r_skybox.\");\n\tCmd_AddCommandD (\"listskyboxes\", R_ListSkyBoxes_f, \"Displays a list of available custom skyboxes that can be set with r_skybox, type or click an entry.\");\n}\n#endif\n"
  },
  {
    "path": "engine/gl/gl_warp_sin.h",
    "content": "/*\nCopyright (C) 1996-1997 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  \n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n*/\n 0, 0.19633, 0.392541, 0.588517, 0.784137, 0.979285, 1.17384, 1.3677,\n 1.56072, 1.75281, 1.94384, 2.1337, 2.32228, 2.50945, 2.69512, 2.87916,\n 3.06147, 3.24193, 3.42044, 3.59689, 3.77117, 3.94319, 4.11282, 4.27998,\n 4.44456, 4.60647, 4.76559, 4.92185, 5.07515, 5.22538, 5.37247, 5.51632,\n 5.65685, 5.79398, 5.92761, 6.05767, 6.18408, 6.30677, 6.42566, 6.54068,\n 6.65176, 6.75883, 6.86183, 6.9607, 7.05537, 7.14579, 7.23191, 7.31368,\n 7.39104, 7.46394, 7.53235, 7.59623, 7.65552, 7.71021, 7.76025, 7.80562,\n 7.84628, 7.88222, 7.91341, 7.93984, 7.96148, 7.97832, 7.99036, 7.99759,\n 8, 7.99759, 7.99036, 7.97832, 7.96148, 7.93984, 7.91341, 7.88222,\n 7.84628, 7.80562, 7.76025, 7.71021, 7.65552, 7.59623, 7.53235, 7.46394,\n 7.39104, 7.31368, 7.23191, 7.14579, 7.05537, 6.9607, 6.86183, 6.75883,\n 6.65176, 6.54068, 6.42566, 6.30677, 6.18408, 6.05767, 5.92761, 5.79398,\n 5.65685, 5.51632, 5.37247, 5.22538, 5.07515, 4.92185, 4.76559, 4.60647,\n 4.44456, 4.27998, 4.11282, 3.94319, 3.77117, 3.59689, 3.42044, 3.24193,\n 3.06147, 2.87916, 2.69512, 2.50945, 2.32228, 2.1337, 1.94384, 1.75281,\n 1.56072, 1.3677, 1.17384, 0.979285, 0.784137, 0.588517, 0.392541, 0.19633,\n 9.79717e-16, -0.19633, -0.392541, -0.588517, -0.784137, -0.979285, -1.17384, -1.3677,\n -1.56072, -1.75281, -1.94384, -2.1337, -2.32228, -2.50945, -2.69512, -2.87916,\n -3.06147, -3.24193, -3.42044, -3.59689, -3.77117, -3.94319, -4.11282, -4.27998,\n -4.44456, -4.60647, -4.76559, -4.92185, -5.07515, -5.22538, -5.37247, -5.51632,\n -5.65685, -5.79398, -5.92761, -6.05767, -6.18408, -6.30677, -6.42566, -6.54068,\n -6.65176, -6.75883, -6.86183, -6.9607, -7.05537, -7.14579, -7.23191, -7.31368,\n -7.39104, -7.46394, -7.53235, -7.59623, -7.65552, -7.71021, -7.76025, -7.80562,\n -7.84628, -7.88222, -7.91341, -7.93984, -7.96148, -7.97832, -7.99036, -7.99759,\n -8, -7.99759, -7.99036, -7.97832, -7.96148, -7.93984, -7.91341, -7.88222,\n -7.84628, -7.80562, -7.76025, -7.71021, -7.65552, -7.59623, -7.53235, -7.46394,\n -7.39104, -7.31368, -7.23191, -7.14579, -7.05537, -6.9607, -6.86183, -6.75883,\n -6.65176, -6.54068, -6.42566, -6.30677, -6.18408, -6.05767, -5.92761, -5.79398,\n -5.65685, -5.51632, -5.37247, -5.22538, -5.07515, -4.92185, -4.76559, -4.60647,\n -4.44456, -4.27998, -4.11282, -3.94319, -3.77117, -3.59689, -3.42044, -3.24193,\n -3.06147, -2.87916, -2.69512, -2.50945, -2.32228, -2.1337, -1.94384, -1.75281,\n -1.56072, -1.3677, -1.17384, -0.979285, -0.784137, -0.588517, -0.392541, -0.19633,\n"
  },
  {
    "path": "engine/gl/glmod_doom.c",
    "content": "#include \"quakedef.h\"\n#ifdef MAP_DOOM\n#include \"glquake.h\"\n#include \"shader.h\"\n\n\n\nchar *va2(char *buffer, size_t buffersize, const char *format, ...)\n{\n\tva_list\t\targptr;\n\n\tva_start (argptr, format);\n\tbuffer[--buffersize] = 0;\n\tvsnprintf (buffer, buffersize, format, argptr);\n\tva_end (argptr);\n\n\treturn buffer;\n}\n\nint SignbitsForPlane (mplane_t *out);\nint\tPlaneTypeForNormal ( vec3_t normal );\n\n//coded from file specifications provided by:\n//Matthew S Fell (msfell@aol.com)\n//Unofficial Doom Specs\n\n//(aol suck)\n\n\n//assumptions:\n//1. That there is a node, and thus two ssectors.\n//2. That the user doesn't want textures...\n//3. That all segs ssectors for a single sector are all the same.\n//4. That ALL sectors are fully enclosed, and not made of two areas.\n//5. That no sectors are inside out.\n\n/*FIXME:\nwe need to do a bsp2prt type thing (walk nodes and determine actual leaf/ssector shapes based upon those).\nbuild sector geometry based upon this.\nthis is because flats in doom were implemented using a flood-fill algorithm and thus omits various unecessary inner edges, while 3d rendering apis all need tri-soup instead.\nattempting to generate sane volumes from most doom maps is doomed to failure because quite often the sector values on linedefs is just buggy, resulting in some really whacky polygons that cannot be souped in any meaningful way.\nthis may still result in a mess of floor polygons outside the world, so be sure to draw those last, for early-z.\n*/\n\nenum {\n\tTHING_PLAYER\t\t= 1,\n\tTHING_PLAYER2\t\t= 2,\n\tTHING_PLAYER3\t\t= 3,\n\tTHING_PLAYER4\t\t= 4,\n\tTHING_DMSPAWN\t\t= 11,\n\n//we need to balance weapons according to ammo types.\n\tTHING_WCHAINSAW\t\t= 2005,\t//-> quad\n\tTHING_WSHOTGUN1\t\t= 2001,\t//-> ng\n\tTHING_WSHOTGUN2\t\t= 82,\t//-> sng\n\tTHING_WCHAINGUN\t\t= 2002,\t//-> ssg\n\tTHING_WROCKETL\t\t= 2003,\t//-> lightning\n\tTHING_WPLASMA\t\t= 2004,\t//-> grenade\n\tTHING_WBFG\t\t\t= 2006\t//-> rocket\n} THING_TYPES;\n\n//thing flags\n//skill/dm is appears in rather than quake's excuded in.\n#define THING_EASY\t\t\t1\n#define THING_MEDIUM\t\t2\n#define THING_HARD\t\t\t4\n#define THING_DEAF\t\t\t8\n#define\tTHING_DEATHMATCH\t16\n//other bits are ignored\n\n\n\ntypedef struct {\n\tshort xpos;\n\tshort ypos;\n\tshort angle;\n\tunsigned short type;\n\tunsigned short flags;\n} dthing_t;\n\ntypedef struct {\n\tshort xpos;\n\tshort ypos;\n} ddoomvertex_t;\n\ntypedef struct {\n\tfloat xpos;\n\tfloat ypos;\n} mdoomvertex_t;\n\ntypedef struct {\n\tunsigned short vert[2];\n\tunsigned short flags;\n\tshort types;\n\tshort tag;\n\tunsigned short sidedef[2]; //(0xffff is none for sidedef[1])\n} dlinedef_t;\n#define LINEDEF_IMPASSABLE\t\t1\n#define\tLINEDEF_BLOCKMONSTERS\t2\n#define LINEDEF_TWOSIDED\t\t4\n#define LINEDEF_UPPERUNPEGGED\t8\n#define LINEDEF_LOWERUNPEGGED\t16\n#define LINEDEF_SECRET\t\t\t32\t//seen as singlesided on automap, does nothing else.\n#define LINEDEF_BLOCKSOUND\t\t64\n#define LINEDEF_NOTONMAP\t\t128\t//doesn't appear on automap.\n#define LINEDEF_STARTONMAP\t\t256\n//others are ignored.\n\ntypedef struct {\n\tshort texx;\n\tshort texy;\n\tchar uppertex[8];\n\tchar lowertex[8];\n\tchar middletex[8];\n\tunsigned short sector;\n} dsidedef_t;\ntypedef struct {\n\tfloat texx;\n\tfloat texy;\n\tint uppertex;\n\tint lowertex;\n\tint middletex;\n\tunsigned short sector;\n} msidedef_t;\n\ntypedef struct {\t//figure out which linedef to use and throw the rest away.\n\tunsigned short\tvert[2];\n\tshort\tangle;\n\tunsigned short\tlinedef;\n\tshort\tdirection;\n\tshort\toffset;\n} dseg_t;\n\ntypedef struct {\n\tunsigned short\tvert[2];\n\tunsigned short\tlinedef;\n\tshort\tdirection;\n\tunsigned short Partner;\t//the one on the other side of the owner's linedef\n} dgl_seg1_t;\n\ntypedef struct {\n\tunsigned int\tvert[2];\n\tunsigned short\tlinedef;\n\tshort\tdirection;\n\tunsigned int Partner;\t//the one on the other side of the owner's linedef\n} dgl_seg3_t;\n\ntypedef struct {\n\tunsigned short segcount;\n\tunsigned short first;\n} dssector_t;\n\ntypedef struct {\n\tstruct msector_s *sector;\n\tunsigned short segcount;\n\tunsigned short first;\n} mssector_t;\n\ntypedef struct {\n\tshort x;\n\tshort y;\n\tshort dx;\n\tshort dy;\n\tshort y1upper;\n\tshort y1lower;\n\tshort x1lower;\n\tshort x1upper;\n\tshort y2upper;\n\tshort y2lower;\n\tshort x2lower;\n\tshort x2upper;\n\tunsigned short node1;\n\tunsigned short node2;\n} ddoomnode_t;\n#define NODE_IS_SSECTOR\t0x8000\n\ntypedef struct {\n\tshort floorheight;\n\tshort ceilingheight;\n\tchar floortexture[8];\n\tchar ceilingtexture[8];\n\tshort lightlevel;\n\tshort specialtype;\n\tshort tag;\n} dsector_t;\n\ntypedef struct msector_s {\n\tint visframe;\n\tint floortex;\n\tint ceilingtex;\n\n\tshort floorheight;\n\tshort ceilingheight;\n\n\tqbyte lightlev;\n\tqbyte pad;\n\tint numflattris;\n\tshort tag;\n\tshort specialtype;\n\n\tunsigned short *flats;\n} msector_t;\n\n\ntypedef struct {\n\tshort xorg;\n\tshort yorg;\n\tshort columns;\n\tshort rows;\n} blockmapheader_t;\n\ntypedef struct\n{\n\tchar name[16];\n\tshader_t *shader;\n\tunsigned short width;\n\tunsigned short height;\n\tbatch_t batch;\n\tmesh_t *meshptr;\n\tmesh_t mesh;\n\tint maxverts;\n\tint maxindicies;\n} doomtexture_t;\n\ntypedef struct doommap_s\n{\n\tmodel_t\t\t\t*model;\n\n\tddoomnode_t\t\t*node;\n\tplane_t\t\t\t*nodeplane;\n\tunsigned int\tnumnodes;\n\n\tmssector_t\t\t*ssector;\t//aka: leafs\n\tunsigned int\tnumssectors;\n\n\tmsector_t\t\t*sector;\n\tunsigned int\tnumsectors;\n\n\tdthing_t\t\t*thing;\n\tunsigned int\tnumthings;\n\n\tmdoomvertex_t\t*vertexes;\n\tunsigned int\tnumvertexes;\n\n\tdgl_seg3_t\t\t*seg;\n\tunsigned int\tnumsegs;\n\n\tdlinedef_t\t\t*linedef;\n\tplane_t\t\t\t*lineplane;\n\tunsigned int\t numlinedefs;\n\n\tmsidedef_t\t\t*sidedef;\n\tunsigned int\tnumsidedefs;\n\n\tblockmapheader_t *blockmap;\n\tunsigned short\t*blockmapofs;\n\n\tunsigned int\tvertexsglbase;\n\n\tdoomtexture_t\t*textures;\n\tunsigned int\tnumtextures;\n} doommap_t;\n\nvoid Doom_SetModelFunc(model_t *mod);\n\n////////////////////////////////////////////////////////////////////////////////////////////\n//physics\n\n/*walk the bsp tree*/\nmsector_t *Doom_SectorNearPoint(doommap_t *dm, vec3_t p)\n{\n\tddoomnode_t *node;\n\tplane_t *plane;\n\tint num;\n\tfloat d;\n\tnum = dm->numnodes-1;\n\twhile (1)\n\t{\n\t\tif (num & NODE_IS_SSECTOR)\n\t\t{\n\t\t\tnum -= NODE_IS_SSECTOR;\n\t\t\treturn dm->ssector[num].sector;\n\t\t}\n\n\t\tnode = dm->node + num;\n\t\tplane = dm->nodeplane + num;\n\t\t\n//\t\tif (plane->type < 3)\n//\t\t\td = p[plane->type] - plane->dist;\n//\t\telse\n\t\t\td = DotProduct (plane->normal, p) - plane->dist;\n\t\tif (d < 0)\n\t\t\tnum = node->node2;\n\t\telse\n\t\t\tnum = node->node1;\n\t}\n\t\n\treturn NULL;\n}\n\nint Doom_PointContents(model_t *model, vec3_t axis[3], vec3_t p)\n{\n\tdoommap_t *dm = model->meshinfo;\n\tmsector_t *sec = Doom_SectorNearPoint(dm, p);\n\tif (p[2] < sec->floorheight)\n\t\treturn FTECONTENTS_SOLID;\n\tif (p[2] > sec->ceilingheight)\n\t\treturn FTECONTENTS_SOLID;\n\treturn FTECONTENTS_EMPTY;\n}\n\n/*\nfixme:\nuse q2-style bsp collision using the trisoup for flats collisions.\nuse blockmap for walls\n*/\nqboolean Doom_Trace(model_t *model, int hulloverride, framestate_t *framestate, vec3_t axis[3], vec3_t start, vec3_t end, vec3_t mins, vec3_t maxs, qboolean iscapsule, unsigned int contentstype, trace_t *trace)\n{\n\tdoommap_t *dm = model->meshinfo;\n#if 1\n#define TRACESTEP\t16\n\tunsigned short *linedefs;\n\tdlinedef_t *ld;\n\tint bmi, obmi;\n\tvec3_t delta;\n\tmsector_t *sec1 = Doom_SectorNearPoint(dm, start);\n\tvec3_t p1, pointonplane, ofs;\n\tfloat d1, d2, c1, c2, planedist;\n\tplane_t *lp;\n\tmdoomvertex_t *v1, *v2;\n\tint j;\n\tfloat p1f, p2f;\n\n\tfloat clipfrac;\n#define\tDIST_EPSILON\t(0.03125)\n\n//\tCon_Printf(\"%i\\n\", sec1);\n\n\tif (start[2] < sec1->floorheight-mins[2])\t//whoops, started outside... ?\n\t{\n\t\ttrace->fraction = 0;\n\t\ttrace->allsolid = trace->startsolid = true;\n\t\ttrace->endpos[0] = start[0];\n\t\ttrace->endpos[1] = start[1];\n\t\ttrace->endpos[2] = start[2];\t//yeah, we do mean this - startsolid\n//\t\tif (IS_NAN(trace->endpos[2]))\n//\t\t\tCon_Printf(\"Nanny\\n\");\n\t\ttrace->plane.normal[0] = 0;\n\t\ttrace->plane.normal[1] = 0;\n\t\ttrace->plane.normal[2] = 1;\n\t\ttrace->plane.dist = sec1->floorheight-mins[2];\n\n\t\treturn false;\n\t}\n\tif (start[2] > sec1->ceilingheight-maxs[2])\t//whoops, started outside... ?\n\t{\n\t\ttrace->fraction = 0;\n\t\ttrace->allsolid = trace->startsolid = true;\n\t\ttrace->endpos[0] = start[0];\n\t\ttrace->endpos[1] = start[1];\n\t\ttrace->endpos[2] = start[2];\n\t\ttrace->plane.normal[0] = 0;\n\t\ttrace->plane.normal[1] = 0;\n\t\ttrace->plane.normal[2] = -1;\n\t\ttrace->plane.dist = -(sec1->ceilingheight-maxs[2]);\n\t\treturn false;\n\t}\n\n\tobmi = -1;\n\tVectorSubtract(end, start, delta);\n\tp2f = Length(delta)+DIST_EPSILON;\n\tif (IS_NAN(p2f) || p2f > 100000)\n\t\tp2f = 100000;\n\tVectorNormalize(delta);\n\n\ttrace->endpos[0] = end[0];\n\ttrace->endpos[1] = end[1];\n\ttrace->endpos[2] = end[2];\n\n\tVectorCopy(start, p1);\n\tp1f = 0;\n\n\ttrace->fraction = 1;\n\twhile(1)\n\t{\n\t\tbmi = ((int)p1[0] - dm->blockmap->xorg)/128 + (((int)p1[1] - dm->blockmap->yorg)/128)*dm->blockmap->columns;\n//\t\tCon_Printf(\"%i of %i \", bmi, dm->blockmap->rows*dm->blockmap->columns);\n\t\tif (bmi >= 0 && bmi < dm->blockmap->rows*dm->blockmap->columns)\n\t\tif (bmi != obmi)\n\t\t{\n#if 0\n\t\t\tshort dummy;\n\t\t\tlinedefs = &dummy;\n\t\t\tfor (dummy = 0; dummy < dm->numlinedefs; dummy++)\n#else\n\t\t\tfor(linedefs = (short*)dm->blockmap + dm->blockmapofs[bmi]+1; *linedefs != 0xffff; linedefs++)\n#endif\n\t\t\t{\n\t\t\t\tld = dm->linedef + *linedefs;\n\t\t\t\tif (ld->sidedef[1] != 0xffff)\n\t\t\t\t{\n\t\t\t\t\tif (dm->sector[dm->sidedef[ld->sidedef[0]].sector].floorheight == dm->sector[dm->sidedef[ld->sidedef[1]].sector].floorheight &&\n\t\t\t\t\t\tdm->sector[dm->sidedef[ld->sidedef[0]].sector].ceilingheight == dm->sector[dm->sidedef[ld->sidedef[1]].sector].ceilingheight)\n\t\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tlp = dm->lineplane + *linedefs;\n\n\t\t\t\tif (1)\n\t\t\t\t{\t//figure out how far to move the plane out by\n\t\t\t\t\tfor (j=0 ; j<2 ; j++)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (lp->normal[j] < 0)\n\t\t\t\t\t\t\tofs[j] = maxs[j];\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tofs[j] = mins[j];\n\t\t\t\t\t}\n\t\t\t\t\tofs[2] = 0;\n\t\t\t\t\tplanedist = lp->dist - DotProduct (ofs, lp->normal);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tplanedist = lp->dist;\n\n\t\t\t\td1 = DotProduct(lp->normal, start) - (planedist);\n\t\t\t\td2 = DotProduct(lp->normal, end) - (planedist);\n\t\t\t\tif (d1 > 0 && d2 > 0)\n\t\t\t\t\tcontinue;\t//both points on the front side.\n\t\t\t\tif (d1 < 0)\t//start on back side\n\t\t\t\t{\n\t\t\t\t\tif (ld->sidedef[1] != 0xffff)\t//two sided (optimisation)\n\t\t\t\t\t{\n\t\t\t\t\t\tplanedist = -planedist+lp->dist;\n\t\t\t\t\t\tif (/*d1 < planedist*-1 &&*/ d1 > planedist*2)\n\t\t\t\t\t\t{\t//right, we managed to end up just on the other side of a wall's plane.\n\t\t\t\t\t\t\tv1 = &dm->vertexes[ld->vert[0]];\n\t\t\t\t\t\t\tv2 = &dm->vertexes[ld->vert[1]];\n\t\t\t\t\t\t\tif (!(d1 - d2))\n\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\tif (d1<0)\t//back to front.\n\t\t\t\t\t\t\t\tc1 = (d1+DIST_EPSILON) / (d1 - d2);\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\tc1 = (d1-DIST_EPSILON) / (d1 - d2);\n\t\t\t\t\t\t\tc2 = 1-c1;\n\t\t\t\t\t\t\tpointonplane[0] = start[0]*c2 + end[0]*c1;\n/*\t\t\t\t\t\t\tif (pointonplane[0] > v1->xpos+DIST_EPSILON*2+hull->clip_maxs[0] && pointonplane[0] > v2->xpos+DIST_EPSILON*2+hull->clip_maxs[0])\n\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\tif (pointonplane[0] < v1->xpos-DIST_EPSILON*2+hull->clip_mins[0] && pointonplane[0] < v2->xpos-DIST_EPSILON*2+hull->clip_mins[0])\n\t\t\t\t\t\t\t\tcontinue;\n*/\t\t\t\t\t\t\tpointonplane[1] = start[1]*c2 + end[1]*c1;\n/*\t\t\t\t\t\t\tif (pointonplane[1] > v1->ypos+DIST_EPSILON*2+hull->clip_maxs[1] && pointonplane[1] > v2->ypos+DIST_EPSILON*2+hull->clip_maxs[1])\n\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\tif (pointonplane[1] < v1->ypos-DIST_EPSILON*2+hull->clip_mins[1] && pointonplane[1] < v2->ypos-DIST_EPSILON*2+hull->clip_mins[1])\n\t\t\t\t\t\t\t\tcontinue;\n*/\n\t\t\t\t\t\t\tpointonplane[2] = start[2]*c2 + end[2]*c1;\n\n\t\t\t\t\t\t\tCon_Printf(\"Started in wall\\n\");\n\t\t\t\t\t\t\tj = dm->sidedef[ld->sidedef[d1 < planedist]].sector;\n\t\t\t\t\t\t\t//yup, we are in the thing\n\t\t\t\t\t\t\t//prevent ourselves from entering the back-sector's floor/ceiling at the point of impact\n\t\t\t\t\t\t\tif (pointonplane[2] < dm->sector[j].floorheight-mins[2])\t//whoops, started outside... ?\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tCon_Printf(\"Started in floor\\n\");\n\t\t\t\t\t\t\t\ttrace->allsolid = trace->startsolid = false;\n\t\t\t\t\t\t\t\ttrace->endpos[2] = dm->sector[j].floorheight-mins[2];\n\t\t\t\t\t\t\t\ttrace->fraction = fabs(trace->endpos[2] - start[2]) / fabs(end[2] - start[2]);\n\t\t\t\t\t\t\t\ttrace->endpos[0] = start[0]+delta[0]*trace->fraction*p2f;\n\t\t\t\t\t\t\t\ttrace->endpos[1] = start[1]+delta[1]*trace->fraction*p2f;\n\t\t\t\t\t\t//\t\tif (IS_NAN(trace->endpos[2]))\n\t\t\t\t\t\t//\t\t\tCon_Printf(\"Nanny\\n\");\n\t\t\t\t\t\t\t\ttrace->plane.normal[0] = 0;\n\t\t\t\t\t\t\t\ttrace->plane.normal[1] = 0;\n\t\t\t\t\t\t\t\ttrace->plane.normal[2] = 1;\n\t\t\t\t\t\t\t\ttrace->plane.dist = dm->sector[j].floorheight-mins[2];\n\n\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (pointonplane[2] > dm->sector[j].ceilingheight-maxs[2])\t//whoops, started outside... ?\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tCon_Printf(\"Started in ceiling\\n\");\n\t\t\t\t\t\t\t\ttrace->allsolid = trace->startsolid = false;\n\t\t\t\t\t\t\t\ttrace->endpos[0] = pointonplane[0];\n\t\t\t\t\t\t\t\ttrace->endpos[1] = pointonplane[1];\n\t\t\t\t\t\t\t\ttrace->endpos[2] = dm->sector[j].ceilingheight-maxs[2];\n\t\t\t\t\t\t\t\ttrace->fraction = fabs(trace->endpos[2] - start[2]) / fabs(end[2] - start[2]);\n\t\t\t\t\t\t\t\ttrace->plane.normal[0] = 0;\n\t\t\t\t\t\t\t\ttrace->plane.normal[1] = 0;\n\t\t\t\t\t\t\t\ttrace->plane.normal[2] = -1;\n\t\t\t\t\t\t\t\ttrace->plane.dist = -(dm->sector[j].ceilingheight-maxs[2]);\n\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif (d2 < 0)\n\t\t\t\t\t\tcontinue;\t//both points on the reverse side.\n\t\t\t\t}\n\n\t\t\t\t//line crosses plane.\n\n\t\t\t\tv1 = &dm->vertexes[ld->vert[0]];\n\t\t\t\tv2 = &dm->vertexes[ld->vert[1]];\n\n\t\t\t\tif (d1<0)\t//back to front.\n\t\t\t\t{\n\t\t\t\t\tif (ld->sidedef[1] == 0xffff)\n\t\t\t\t\t\tcontinue;\t//hack to allow them to pass\n\t\t\t\t\tc1 = (d1+DIST_EPSILON) / (d1 - d2);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tc1 = (d1-DIST_EPSILON) / (d1 - d2);\n\t\t\t\tc2 = 1-c1;\n\t\t\t\tpointonplane[0] = start[0]*c2 + end[0]*c1;\n\t\t\t\tif (pointonplane[0] > v1->xpos+DIST_EPSILON*2+maxs[0] && pointonplane[0] > v2->xpos+DIST_EPSILON*2+maxs[0])\n\t\t\t\t\tcontinue;\n\t\t\t\tif (pointonplane[0] < v1->xpos-DIST_EPSILON*2+mins[0] && pointonplane[0] < v2->xpos-DIST_EPSILON*2+mins[0])\n\t\t\t\t\tcontinue;\n\t\t\t\tpointonplane[1] = start[1]*c2 + end[1]*c1;\n\t\t\t\tif (pointonplane[1] > v1->ypos+DIST_EPSILON*2+maxs[1] && pointonplane[1] > v2->ypos+DIST_EPSILON*2+maxs[1])\n\t\t\t\t\tcontinue;\n\t\t\t\tif (pointonplane[1] < v1->ypos-DIST_EPSILON*2+mins[1] && pointonplane[1] < v2->ypos-DIST_EPSILON*2+mins[1])\n\t\t\t\t\tcontinue;\n\t\t\t\tpointonplane[2] = start[2]*c2 + end[2]*c1;\n\n\t\t\t\tif (ld->flags & LINEDEF_IMPASSABLE || ld->sidedef[1] == 0xffff)\t//unconditionally unpassable.\n\t\t\t\t{\t//unconditionally clipped.\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\t//ensure that the side we are passing on to passes the clip (no ceiling/floor clips happened first)\n\t\t\t\t\tmsector_t *sec2;\n\n\t\t\t\t\tif (d1<0)\n\t\t\t\t\t\tsec2 = &dm->sector[dm->sidedef[ld->sidedef[1]].sector];\n\t\t\t\t\telse\n\t\t\t\t\t\tsec2 = &dm->sector[dm->sidedef[ld->sidedef[0]].sector];\n\n\t\t\t\t\tif (pointonplane[2] < sec2->floorheight-mins[2])\n\t\t\t\t\t{\t//hit the floor first.\n\t\t\t\t\t\tc1 = fabs(sec1->floorheight-mins[2] - start[2]);\n\t\t\t\t\t\tc2 = fabs(end[2] - start[2]);\n\t\t\t\t\t\tif (!c2)\n\t\t\t\t\t\t\tc1 = 1;\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tc1 = (c1-DIST_EPSILON) / c2;\n\t\t\t\t\t\tif (trace->fraction > c1)\n\t\t\t\t\t\t{\n//\t\t\t\t\t\t\tCon_Printf(\"Hit floor\\n\");\n\t\t\t\t\t\t\ttrace->fraction = c1;\n\t\t\t\t\t\t\ttrace->allsolid = trace->startsolid = true;\n\t\t\t\t\t\t\ttrace->endpos[0] = start[0] + trace->fraction*(end[0]-start[0]);\n\t\t\t\t\t\t\ttrace->endpos[1] = start[1] + trace->fraction*(end[1]-start[1]);\n\t\t\t\t\t\t\ttrace->endpos[2] = start[2] + trace->fraction*(end[2]-start[2]);\n\t\t\t\t\t\t\ttrace->plane.normal[0] = 0;\n\t\t\t\t\t\t\ttrace->plane.normal[1] = 0;\n\t\t\t\t\t\t\ttrace->plane.normal[2] = 1;\n\t\t\t\t\t\t\ttrace->plane.dist = sec1->floorheight-mins[2];\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (pointonplane[2] > sec2->ceilingheight-maxs[2])\n\t\t\t\t\t{\t//hit the floor first.\n\t\t\t\t\t\tc1 = fabs((sec1->ceilingheight-maxs[2]) - start[2]);\n\t\t\t\t\t\tc2 = fabs(end[2] - start[2]);\n\t\t\t\t\t\tif (!c2)\n\t\t\t\t\t\t\tc1 = 1;\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tc1 = (c1-DIST_EPSILON) / c2;\n\n\n\t\t\t\t\t\tif (trace->fraction > c1)\n\t\t\t\t\t\t{\n//\t\t\t\t\t\t\tCon_Printf(\"Hit ceiling\\n\");\n\t\t\t\t\t\t\ttrace->fraction = c1;\n\t\t\t\t\t\t\ttrace->allsolid = trace->startsolid = true;\n\t\t\t\t\t\t\ttrace->endpos[0] = start[0] + trace->fraction*(end[0]-start[0]);\n\t\t\t\t\t\t\ttrace->endpos[1] = start[1] + trace->fraction*(end[1]-start[1]);\n\t\t\t\t\t\t\ttrace->endpos[2] = start[2] + trace->fraction*(end[2]-start[2]);\n\t\t\t\t\t\t\ttrace->plane.normal[0] = 0;\n\t\t\t\t\t\t\ttrace->plane.normal[1] = 0;\n\t\t\t\t\t\t\ttrace->plane.normal[2] = -1;\n\t\t\t\t\t\t\ttrace->plane.dist = -(sec1->ceilingheight-maxs[2]);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (d1<0)\n\t\t\t\t\t\tsec2 = &dm->sector[dm->sidedef[ld->sidedef[0]].sector];\n\t\t\t\t\telse\n\t\t\t\t\t\tsec2 = &dm->sector[dm->sidedef[ld->sidedef[1]].sector];\n\n\t\t\t\t\tif(sec2->ceilingheight == sec2->floorheight)\n\t\t\t\t\t\tsec2->ceilingheight += 64;\n\n\t\t\t\t\tif (pointonplane[2] > sec2->floorheight-mins[2] &&\n\t\t\t\t\t\tpointonplane[2] < sec2->ceilingheight-maxs[2])\n\t\t\t\t\t{\n//\t\t\t\t\t\tCon_Printf(\"Two sided passed\\n\");\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n//\t\t\t\t\tCon_Printf(\"blocked by two sided line\\n\");\n//\t\t\t\t\tsec2->floorheight--;\n\t\t\t\t}\n\n\t\t\t\tif (d1<0)\t//back to front.\n\t\t\t\t\tc1 = (d1+DIST_EPSILON) / (d1 - d2);\n\t\t\t\telse\n\t\t\t\t\tc1 = (d1-DIST_EPSILON) / (d1 - d2);\n\n\n\t\t\t\tclipfrac = c1;\n\n\t\t\t\tif (clipfrac < 0)\n\t\t\t\t\tclipfrac = 0;\n\t\t\t\tif (clipfrac > 1)\n\t\t\t\t\tclipfrac = 1;\n\n\t\t\t\tif (trace->fraction > clipfrac)\n\t\t\t\t{\n\t\t\t\t\ttrace->fraction = clipfrac;\n\t\t\t\t\tVectorMA(pointonplane, 0, lp->normal, trace->endpos);\n\t\t\t\t\tVectorMA(trace->endpos, -0.1, delta, trace->endpos);\n//\t\t\t\t\tif (IS_NAN(trace->endpos[2]))\n//\t\t\t\t\t\tCon_Printf(\"Buggy clipping\\n\");\n\t\t\t\t\tVectorCopy(lp->normal, trace->plane.normal);\n\t\t\t\t\ttrace->plane.dist = planedist;\n//\t\t\t\t\tif (IS_NAN(trace->plane.normal[2]))\n//\t\t\t\t\t\tCon_Printf(\"Buggy clipping\\n\");\n\n\t\t\t\t\tif (clipfrac)\n\t\t\t\t\tCon_Printf(\"Clip Wall %f\\n\", clipfrac);\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\tobmi = bmi;\n\t\t}\n\n\t\tp1f += TRACESTEP;\n\t\tif (p1f >= p2f)\n\t\t\tbreak;\n\n\t\tVectorMA(p1, TRACESTEP, delta, p1);\n\t}\n\n//\tVectorMA(start, p2f*trace->fraction, delta, p2);\n\n\tif (end[2] != start[2])\n\t{\n\t\tif (sec1 == Doom_SectorNearPoint(dm, trace->endpos))\t//special test.\n\t\t{\n\t\t\tif (end[2] <= sec1->floorheight-mins[2])\t//whoops, started outside... ?\n\t\t\t{\n\t\t\t\tp1f = fabs(sec1->floorheight-mins[2] - start[2]);\n\t\t\t\tp2f = fabs(end[2] - start[2]);\n\t\t\t\tif (!p2f)\n\t\t\t\t\tc1 = 1;\n\t\t\t\telse\n\t\t\t\t\tc1 = (p1f-DIST_EPSILON) / p2f;\n\t\t\t\tif (trace->fraction > c1)\n\t\t\t\t{\n\t\t\t\t\ttrace->fraction = c1;\n\t\t\t\t\ttrace->allsolid = trace->startsolid = false;\n\t\t\t\t\ttrace->endpos[0] = start[0] + trace->fraction*(end[0]-start[0]);\n\t\t\t\t\ttrace->endpos[1] = start[1] + trace->fraction*(end[1]-start[1]);\n\t\t\t\t\ttrace->endpos[2] = start[2] + trace->fraction*(end[2]-start[2]);\n\t\t\t\t\ttrace->plane.normal[0] = 0;\n\t\t\t\t\ttrace->plane.normal[1] = 0;\n\t\t\t\t\ttrace->plane.normal[2] = 1;\n\t\t\t\t\ttrace->plane.dist = sec1->floorheight-mins[2];\n\t\t\t\t}\n\n//\t\t\t\tif (IS_NAN(trace->endpos[2]))\n//\t\t\t\t\tCon_Printf(\"Nanny\\n\");\n\t\t\t}\n\t\t\tif (end[2] >= sec1->ceilingheight-maxs[2])\t//whoops, started outside... ?\n\t\t\t{\n\t\t\t\tp1f = fabs(sec1->ceilingheight-maxs[2] - start[2]);\n\t\t\t\tp2f = fabs(end[2] - start[2]);\n\t\t\t\tif (!p2f)\n\t\t\t\t\tc1 = 1;\n\t\t\t\telse\n\t\t\t\t\tc1 = (p1f-DIST_EPSILON) / p2f;\n\t\t\t\tif (trace->fraction > c1)\n\t\t\t\t{\n\t\t\t\t\ttrace->fraction = c1;\n\t\t\t\t\ttrace->allsolid = trace->startsolid = false;\n\t\t\t\t\ttrace->endpos[0] = start[0] + trace->fraction*(end[0]-start[0]);\n\t\t\t\t\ttrace->endpos[1] = start[1] + trace->fraction*(end[1]-start[1]);\n\t\t\t\t\ttrace->endpos[2] = start[2] + trace->fraction*(end[2]-start[2]);\n\t\t\t\t\ttrace->plane.normal[0] = 0;\n\t\t\t\t\ttrace->plane.normal[1] = 0;\n\t\t\t\t\ttrace->plane.normal[2] = -1;\n\t\t\t\t\ttrace->plane.dist = -(sec1->ceilingheight-maxs[2]);\n\t\t\t\t}\n\n//\t\t\t\tif (IS_NAN(trace->endpos[2]))\n//\t\t\t\t\tCon_Printf(\"Nanny\\n\");\n\t\t\t}\n\t\t}\n\t}\n\n\t//we made it all the way through. yay.\n\n\ttrace->allsolid = trace->startsolid = false;\n//Con_Printf(\"total = %f\\n\", trace->fraction);\n#endif\n\treturn trace->fraction==1;\n}\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n#ifndef SERVERONLY\nqbyte doompalette[768];\nstatic qboolean paletteloaded;\n\nvoid Doom_LoadPalette(void)\n{\n\tchar *file;\n\tint greyscale;\n\tif (!paletteloaded)\n\t{\n\t\tpaletteloaded = true;\n\t\tfile = FS_LoadMallocFile(\"wad/playpal\", NULL);\n\t\tif (file)\n\t\t{\n\t\t\tmemcpy(doompalette, file, 768);\n\t\t\tZ_Free(file);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tfor (greyscale = 0; greyscale < 256; greyscale++)\n\t\t\t{\n\t\t\t\tdoompalette[greyscale*3+0] = greyscale;\n\t\t\t\tdoompalette[greyscale*3+1] = greyscale;\n\t\t\t\tdoompalette[greyscale*3+2] = greyscale;\n\t\t\t}\n\t\t}\n\t}\n}\n#endif\nint Doom_LoadFlat(doommap_t *dm, char *flatname)\n{\n#ifndef SERVERONLY\n\tchar texname[64];\n\tint texnum;\n\n\tsprintf(texname, \"flats/%-.8s\", flatname);\n\n\tfor (texnum = 0; texnum < dm->numtextures; texnum++)\n\t{\n\t\tif (!strcmp(dm->textures[texnum].name, texname))\n\t\t\treturn texnum;\n\t}\n\t\n\tdm->textures = BZ_Realloc(dm->textures, sizeof(*dm->textures)*((dm->numtextures+16)&~15));\n\tmemset(dm->textures + dm->numtextures, 0, sizeof(dm->textures[dm->numtextures]));\n\tdm->numtextures++;\n\n\tQ_strncpyz(dm->textures[texnum].name, texname, sizeof(dm->textures[texnum].name));\n\n\tdm->textures[texnum].width = 64;\n\tdm->textures[texnum].height = 64;\n\n\treturn texnum;\n#else\n\treturn 0;\n#endif\n}\n\n#ifndef SERVERONLY\nstatic void R_DrawWall(doommap_t *dm, int texnum, int s, int t, float x1, float y1, float z1, float x2, float y2, float z2, qboolean unpegged, unsigned int colour4b)\n{\n\tdoomtexture_t *tex = dm->textures+texnum;\n\tmesh_t *mesh = &tex->mesh;\n\tfloat len = sqrt((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2));\n\tfloat s1, s2;\n\tfloat t1, t2;\n\tunsigned int col;\n\n\ts1 = s/tex->width;\n\ts2 = s1 + len/tex->width;\n\n\tif (unpegged)\n\t{\n\t\tt2 = t/tex->height;\n\t\tt1 = t2 - (z2-z1)/tex->height;\n\t}\n\telse\n\t{\n\t\tt1 = t/tex->height;\n\t\tt2 = t1 + (z2-z1)/tex->height;\n\t}\n\tt1 = 0;\n\tt2 = 1;\n\n\tif (mesh->numvertexes+4 > tex->maxverts)\n\t{\n\t\tif (mesh->numvertexes+4 > MAX_INDICIES)\n\t\t\tBE_DrawMesh_Single(tex->shader, mesh, NULL, 0);\n\t\telse\n\t\t{\n\t\t\ttex->maxverts = mesh->numvertexes+4;\n\t\t\tmesh->colors4b_array = BZ_Realloc(mesh->colors4b_array, sizeof(*mesh->colors4b_array) * tex->maxverts);\n\t\t\tmesh->xyz_array = BZ_Realloc(mesh->xyz_array, sizeof(*mesh->xyz_array) * tex->maxverts);\n\t\t\tmesh->st_array = BZ_Realloc(mesh->st_array, sizeof(*mesh->st_array) * tex->maxverts);\n\t\t}\n\t}\n\tif (mesh->numindexes+6 > tex->maxindicies)\n\t{\n\t\ttex->maxindicies = mesh->numindexes+6;\n\t\tmesh->indexes = BZ_Realloc(mesh->indexes, sizeof(*mesh->indexes) * tex->maxindicies);\n\t}\n\n\tcol = colour4b * 0x01010101;\n\t((unsigned char*)&col)[3] = 0xff;\n\t*(unsigned int*)mesh->colors4b_array[mesh->numvertexes+0] = col;\n\t*(unsigned int*)mesh->colors4b_array[mesh->numvertexes+1] = col;\n\t*(unsigned int*)mesh->colors4b_array[mesh->numvertexes+2] = col;\n\t*(unsigned int*)mesh->colors4b_array[mesh->numvertexes+3] = col;\n\tVectorSet(mesh->xyz_array[mesh->numvertexes+0], x1, y1, z1);\n\tVectorSet(mesh->xyz_array[mesh->numvertexes+1], x1, y1, z2);\n\tVectorSet(mesh->xyz_array[mesh->numvertexes+2], x2, y2, z2);\n\tVectorSet(mesh->xyz_array[mesh->numvertexes+3], x2, y2, z1);\n\tVector2Set(mesh->st_array[mesh->numvertexes+0], s1, t2);\n\tVector2Set(mesh->st_array[mesh->numvertexes+1], s1, t1);\n\tVector2Set(mesh->st_array[mesh->numvertexes+2], s2, t1);\n\tVector2Set(mesh->st_array[mesh->numvertexes+3], s2, t2);\n\n\tmesh->indexes[mesh->numindexes+0] = mesh->numvertexes+0;\n\tmesh->indexes[mesh->numindexes+1] = mesh->numvertexes+1;\n\tmesh->indexes[mesh->numindexes+2] = mesh->numvertexes+2;\n\n\tmesh->indexes[mesh->numindexes+3] = mesh->numvertexes+0;\n\tmesh->indexes[mesh->numindexes+4] = mesh->numvertexes+2;\n\tmesh->indexes[mesh->numindexes+5] = mesh->numvertexes+3;\n\n\tmesh->numvertexes += 4;\n\tmesh->numindexes += 6;\n}\n\nstatic void R_DrawFlats(doommap_t *dm, int floortexnum, int floorheight, int ceiltexnum, int ceilheight, int numverts, unsigned short *verts, unsigned int colour4b)\n{\n\tmesh_t *mesh;\n\tunsigned int col;\n\tunsigned int v, i;\n\n\t//floor\n\t{\n\t\tdoomtexture_t *floortex = dm->textures + floortexnum;\n\t\tmesh = &floortex->mesh;\n\t\tif (mesh->numvertexes+numverts > floortex->maxverts)\n\t\t{\n\t\t\tif (mesh->numvertexes+numverts > MAX_INDICIES)\n\t\t\t{\n\t\t\t\tBE_DrawMesh_Single(floortex->shader, mesh, NULL, 0);\n\t\t\t\tmesh->numvertexes = 0;\n\t\t\t\tmesh->numindexes = 0;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tfloortex->maxverts = mesh->numvertexes+numverts;\n\t\t\t\tmesh->colors4b_array = BZ_Realloc(mesh->colors4b_array, sizeof(*mesh->colors4b_array) * floortex->maxverts);\n\t\t\t\tmesh->xyz_array = BZ_Realloc(mesh->xyz_array, sizeof(*mesh->xyz_array) * floortex->maxverts);\n\t\t\t\tmesh->st_array = BZ_Realloc(mesh->st_array, sizeof(*mesh->st_array) * floortex->maxverts);\n\t\t\t}\n\t\t}\n\t\tif (mesh->numindexes+numverts > floortex->maxindicies)\n\t\t{\n\t\t\tfloortex->maxindicies = mesh->numindexes+numverts;\n\t\t\tmesh->indexes = BZ_Realloc(mesh->indexes, sizeof(*mesh->indexes) * floortex->maxindicies);\n\t\t}\n\n\t\tcol = colour4b * 0x01010101;\n\t\t((unsigned char*)&col)[3] = 0xff;\n\n\t\tfor (i = 0; i < numverts; i++)\n\t\t{\n\t\t\tv = verts[i];\n\t\t\tVectorSet(mesh->xyz_array[mesh->numvertexes+i], dm->vertexes[v].xpos, dm->vertexes[v].ypos, floorheight);\n\t\t\tVector2Set(mesh->st_array[mesh->numvertexes+i], dm->vertexes[v].xpos/64.0f, dm->vertexes[v].ypos/64.0f);\n\t\t\t*(unsigned int*)mesh->colors4b_array[mesh->numvertexes+i] = col;\n\t\t}\n\n\t\tfor (i = 0; i < numverts; i++)\n\t\t{\n\t\t\tmesh->indexes[mesh->numindexes+i] = mesh->numvertexes+i;\n\t\t}\n\n\t\tmesh->numvertexes += numverts;\n\t\tmesh->numindexes += numverts;\n\n//\t\tif (floortex->shader)\n//\t\t\tBE_DrawMesh_Single(floortex->shader, mesh, NULL, 0);\n\t}\n\n\t//ceiling\n\t{\n\t\tdoomtexture_t *ceiltex = dm->textures + ceiltexnum;\n\t\tmesh = &ceiltex->mesh;\n\t\tif (mesh->numvertexes+numverts > ceiltex->maxverts)\n\t\t{\n\t\t\tif (mesh->numvertexes+numverts > MAX_INDICIES)\n\t\t\t{\n\t\t\t\tBE_DrawMesh_Single(ceiltex->shader, mesh, NULL, 0);\n\t\t\t\tmesh->numvertexes = 0;\n\t\t\t\tmesh->numindexes = 0;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tceiltex->maxverts = mesh->numvertexes+numverts;\n\t\t\t\tmesh->colors4b_array = BZ_Realloc(mesh->colors4b_array, sizeof(*mesh->colors4b_array) * ceiltex->maxverts);\n\t\t\t\tmesh->xyz_array = BZ_Realloc(mesh->xyz_array, sizeof(*mesh->xyz_array) * ceiltex->maxverts);\n\t\t\t\tmesh->st_array = BZ_Realloc(mesh->st_array, sizeof(*mesh->st_array) * ceiltex->maxverts);\n\t\t\t}\n\t\t}\n\t\tif (mesh->numindexes+numverts > ceiltex->maxindicies)\n\t\t{\n\t\t\tceiltex->maxindicies = mesh->numindexes+numverts;\n\t\t\tmesh->indexes = BZ_Realloc(mesh->indexes, sizeof(*mesh->indexes) * ceiltex->maxindicies);\n\t\t}\n\n\t\tcol = colour4b * 0x01010101;\n\t\t((unsigned char*)&col)[3] = 0xff;\n\n\t\tfor (i = 0; i < numverts; i++)\n\t\t{\n\t\t\tv = verts[numverts-1-i];\n\t\t\tVectorSet(mesh->xyz_array[mesh->numvertexes+i], dm->vertexes[v].xpos, dm->vertexes[v].ypos, ceilheight);\n\t\t\tVector2Set(mesh->st_array[mesh->numvertexes+i], dm->vertexes[v].xpos/64.0f, dm->vertexes[v].ypos/64.0f);\n\t\t\t*(unsigned int*)mesh->colors4b_array[mesh->numvertexes+i] = col;\n\t\t}\n\n\t\tfor (i = 0; i < numverts; i++)\n\t\t{\n\t\t\tmesh->indexes[mesh->numindexes+i] = mesh->numvertexes+i;\n\t\t}\n\n\t\tmesh->numvertexes += numverts;\n\t\tmesh->numindexes += numverts;\n\n//\t\tif (ceiltex->shader)\n//\t\t\tBE_DrawMesh_Single(ceiltex->shader, mesh, NULL, 0);\n\t}\n}\n\nstatic void R_DrawSSector(doommap_t *dm, unsigned int ssec)\n{\n\tshort v0, v1;\n\tint sd;\n\tdlinedef_t *ld;\n\tint seg;\n\tmsector_t *sec, *sec2;\n\n\tfor (seg = dm->ssector[ssec].first + dm->ssector[ssec].segcount-1; seg >= dm->ssector[ssec].first; seg--)\n\t\tif (dm->seg[seg].linedef != 0xffff)\n\t\t\tbreak;\n\tsec = dm->sector + dm->sidedef[dm->linedef[dm->seg[seg].linedef].sidedef[dm->seg[seg].direction]].sector;\n\n\tif (sec->visframe != r_visframecount)\n\t{\n\t\tR_DrawFlats(dm, sec->floortex, sec->floorheight, sec->ceilingtex, sec->ceilingheight, sec->numflattris*3, sec->flats, sec->lightlev);\n\n\t\tsec->visframe = r_visframecount;\n\t}\n\tfor (seg = dm->ssector[ssec].first + dm->ssector[ssec].segcount-1; seg >= dm->ssector[ssec].first; seg--)\n\t{\n\t\tif (dm->seg[seg].linedef == 0xffff)\n\t\t\tcontinue;\n\n\t\tv0 = dm->seg[seg].vert[0];\n\t\tv1 = dm->seg[seg].vert[1];\n\t\tif (v0==v1)\n\t\t\tcontinue;\n\t\tld = dm->linedef + dm->seg[seg].linedef;\n\t\tsd = ld->sidedef[dm->seg[seg].direction];\n\n\t\tif (ld->sidedef[1] != 0xffff)\t//we can see through this linedef\n\t\t{\n\t\t\tsec2 = dm->sector + dm->sidedef[ld->sidedef[1-dm->seg[seg].direction]].sector;\n\n\t\t\tif (sec->floorheight < sec2->floorheight)\n\t\t\t{\n\t\t\t\tR_DrawWall(dm,\n\t\t\t\t\tdm->sidedef[sd].lowertex, \n\t\t\t\t\tdm->sidedef[ld->sidedef[1-dm->seg[seg].direction]].texx,\n\t\t\t\t\tdm->sidedef[ld->sidedef[1-dm->seg[seg].direction]].texy,\n\t\t\t\t\tdm->vertexes[v0].xpos, dm->vertexes[v0].ypos, sec->floorheight,\n\t\t\t\t\tdm->vertexes[v1].xpos, dm->vertexes[v1].ypos, sec2->floorheight, ld->flags & LINEDEF_LOWERUNPEGGED, sec->lightlev);\n\t\t\t}\n\n\t\t\tif (sec->ceilingheight > sec2->ceilingheight)\n\t\t\t{\n\t\t\t\tR_DrawWall(dm,\n\t\t\t\t\tdm->sidedef[sd].uppertex, \n\t\t\t\t\tdm->sidedef[ld->sidedef[1-dm->seg[seg].direction]].texx,\n\t\t\t\t\tdm->sidedef[ld->sidedef[1-dm->seg[seg].direction]].texy,\n\t\t\t\t\tdm->vertexes[v0].xpos, dm->vertexes[v0].ypos, sec2->ceilingheight,\n\t\t\t\t\tdm->vertexes[v1].xpos, dm->vertexes[v1].ypos, sec->ceilingheight, ld->flags & LINEDEF_UPPERUNPEGGED, sec->lightlev);\n\t\t\t}\n\n\t\t\tif (dm->sidedef[sd].middletex)\n\t\t\t{\n\t\t\t\tR_DrawWall(dm,\n\t\t\t\t\tdm->sidedef[sd].middletex, \n\t\t\t\t\tdm->sidedef[ld->sidedef[dm->seg[seg].direction]].texx,\n\t\t\t\t\tdm->sidedef[ld->sidedef[dm->seg[seg].direction]].texy,\n\t\t\t\t\tdm->vertexes[v1].xpos, dm->vertexes[v1].ypos, (sec2->ceilingheight < sec->ceilingheight)?sec2->ceilingheight:sec->ceilingheight,\n\t\t\t\t\tdm->vertexes[v0].xpos, dm->vertexes[v0].ypos, (sec2->floorheight > sec->floorheight)?sec2->floorheight:sec->floorheight, false, sec->lightlev);\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\t//solid wall, draw full wall.\n\n\t\t\tR_DrawWall(dm,\n\t\t\t\tdm->sidedef[sd].middletex, \n\t\t\t\tdm->sidedef[ld->sidedef[dm->seg[seg].direction]].texx,\n\t\t\t\tdm->sidedef[ld->sidedef[dm->seg[seg].direction]].texy,\n\t\t\t\tdm->vertexes[v0].xpos, dm->vertexes[v0].ypos, sec->floorheight,\n\t\t\t\tdm->vertexes[v1].xpos, dm->vertexes[v1].ypos, sec->ceilingheight, false, sec->lightlev);\n\t\t}\n\t}\n}\n\nmplane_t\tfrustum2d[2];\nstatic int Box2DOnPlaneSide (short emins[2], short emaxs[2], mplane_t *p)\n{\n\tfloat\tdist1, dist2;\n\tint\t\tsides;\n\n// general case\n\tswitch (p->signbits)\n\t{\n\tcase 0:\ndist1 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1];\ndist2 = p->normal[0]*emins[0] + p->normal[1]*emins[1];\n\t\tbreak;\n\tcase 1:\ndist1 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1];\ndist2 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1];\n\t\tbreak;\n\tcase 2:\ndist1 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1];\ndist2 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1];\n\t\tbreak;\n\tcase 3:\ndist1 = p->normal[0]*emins[0] + p->normal[1]*emins[1];\ndist2 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1];\n\t\tbreak;\n\tcase 4:\ndist1 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1];\ndist2 = p->normal[0]*emins[0] + p->normal[1]*emins[1];\n\t\tbreak;\n\tcase 5:\ndist1 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1];\ndist2 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1];\n\t\tbreak;\n\tcase 6:\ndist1 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1];\ndist2 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1];\n\t\tbreak;\n\tcase 7:\ndist1 = p->normal[0]*emins[0] + p->normal[1]*emins[1];\ndist2 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1];\n\t\tbreak;\n\tdefault:\n\t\tdist1 = dist2 = 0;\t\t// shut up compiler\n//\t\tBOPS_Error ();\n\t\tbreak;\n\t}\n\n\tsides = 0;\n\tif (dist1 >= p->dist)\n\t\tsides = 1;\n\tif (dist2 < p->dist)\n\t\tsides |= 2;\n\n#ifdef PARANOID\nif (sides == 0)\n\tSys_Error (\"Box2DOnPlaneSide: sides==0\");\n#endif\n\n\treturn sides;\n}\nstatic qboolean R_Cull2DBox (short mins_x, short mins_y, short maxs_x, short maxs_y)\n{\n\tshort mins[2], maxs[2];\n\tint\t\ti;\n//return false;\n\tmins[0] = mins_x;\n\tmins[1] = mins_y;\n\tmaxs[0] = maxs_x;\n\tmaxs[1] = maxs_y;\n\n\tfor (i=0 ; i<2 ; i++)\n\t\tif (Box2DOnPlaneSide (mins, maxs, &frustum2d[i]) == 2)\n\t\t\treturn true;\n\treturn false;\n}\n\nvoid R_Set2DFrustum (void)\n{\n\tint\t\ti;\n\tvec3_t vpn, vright, vup, viewang;\n\n\tif ((int)r_novis.value & 4)\n\t\treturn;\n\n\tviewang[0] = 0;\n\tviewang[1] = r_refdef.viewangles[1];\n\tviewang[2] = 0;\n\tAngleVectors (viewang, vpn, vright, vup);\n\n/*\tif (r_refdef.fov_x == 90) \n\t{\n\t\t// front side is visible\n\n\t\tVectorAdd (vpn, vright, frustum2d[0].normal);\n\t\tVectorSubtract (vpn, vright, frustum2d[1].normal);\n\t}\n\telse*/\n\t{\n\n\t\t// rotate VPN right by FOV_X/2 degrees\n\t\tRotatePointAroundVector( frustum2d[0].normal, vup, vpn, -(90-r_refdef.fov_x / 2 ) );\n\t\t// rotate VPN left by FOV_X/2 degrees\n\t\tRotatePointAroundVector( frustum2d[1].normal, vup, vpn, 90-r_refdef.fov_x / 2 );\n\t}\n\n\tfor (i=0 ; i<2 ; i++)\n\t{\n\t\tfrustum2d[i].type = PLANE_ANYZ;\n\t\tfrustum2d[i].dist = DotProduct (r_origin, frustum2d[i].normal);\n\t\tfrustum2d[i].signbits = SignbitsForPlane (&frustum2d[i]);\n\t}\n}\n\n\nstatic void R_RecursiveDoomNode(doommap_t *dm, unsigned int node)\n{\n\tif (node & NODE_IS_SSECTOR)\n\t{\n\t\tR_DrawSSector(dm, node & ~NODE_IS_SSECTOR);\t\t\n\n\t\treturn;\n\t}\n\n\tif (!R_Cull2DBox(dm->node[node].x1lower, dm->node[node].y1lower, dm->node[node].x1upper, dm->node[node].y1upper)||1)\n\t\tR_RecursiveDoomNode(dm, dm->node[node].node1);\n\tif (!R_Cull2DBox(dm->node[node].x2lower, dm->node[node].y2lower, dm->node[node].x2upper, dm->node[node].y2upper)||1)\n\t\tR_RecursiveDoomNode(dm, dm->node[node].node2);\n}\n\nvoid R_DoomWorld(void)\n{\n\tmodel_t *mod = cl.worldmodel;\n\tdoommap_t *dm = mod->meshinfo;\n\tint texnum;\n\tdoomtexture_t *t;\n\tif (!dm->node || !dm->numnodes)\n\t\treturn;\t//err... buggy\n\n\tfor (texnum = 0; texnum < dm->numtextures; texnum++)\t//a hash table might be a good plan.\n\t{\n\t\tt = &dm->textures[texnum];\n\t\tt->mesh.numindexes = 0;\n\t\tt->mesh.numvertexes = 0;\n\t}\n\tr_visframecount++;\n\tR_RecursiveDoomNode(dm, dm->numnodes-1);\n\n\tmemset(mod->batches, 0, sizeof(mod->batches));\n\tfor (texnum = 0; texnum < dm->numtextures; texnum++)\t//a hash table might be a good plan.\n\t{\n\t\tt = &dm->textures[texnum];\n\t\tif (t->mesh.numindexes && t->shader)\n\t\t{\n\t\t\tt->batch.next = mod->batches[t->shader->sort];\n\t\t\tmod->batches[t->shader->sort] = &t->batch;\n\n\t\t\tBE_DrawMesh_Single(t->shader, &t->mesh, NULL, 0);\n\t\t}\n\t}\n}\n#endif\n\n\n//find the first ssector, go through it's list/\n//grab the lines into multiple arrays.\n//make sure all arrays are looped fully. If not, error out.\n//if we have two arrays, we have a hole in the middle.\n//with multiple arrays, from the second onwards\n//\tgrab two adjacent verts and find the nearest point in any other array, that is also on the positive side.\n//\tOne of the two should be an extreeme, and the external point should be in the direction that the angle points at.\n//\t\tnone found = error\n//\tcreate a triangle from these points, fix array links.\n//\tmove on to next spare array.\n//we now have a concave polygon with no holes.\n//pick a point, follow along the walls making a triangle fan, until an angle of > 180, throw out fan, rebuild arrays.\n//at new point, start a new fan. Be prepared to not be able to generate one.\n\n#define MAX_REGIONS\t\t256\n#define MAX_POLYVERTS\t(MAX_FLATTRIS*3)\n#define MAX_FLATTRIS\t1024\n\n//buffer to hold tris\nstatic unsigned short indexes[MAX_POLYVERTS];\nstatic unsigned int numindexes;\n\ntypedef struct {\n\tint vertex[MAX_POLYVERTS];\n\tunsigned int numverts;\n\tfloat angle;\n} conectedregion_t;\nstatic conectedregion_t polyregions[MAX_REGIONS];\t//we need to be able to join them as we go.\nstatic unsigned int regions;\n\n//throw out duplicates.\nstatic void Triangulate_AddLine(int v1, int v2)\t//order makes a difference\n{\n\tint r, v;\n\tint beginingof = -1;\n\tint endof = -1;\n\tint freer = -1;\n\n\tfor (r = 0; r < regions; r++)\n\t{\n\t\tif (!polyregions[r].numverts)\n\t\t{\n\t\t\tfreer = r;\n\t\t\tcontinue;\n\t\t}\n\t\tif (polyregions[r].vertex[0] == v2)\n\t\t\tbeginingof = r;\n\t\tif (polyregions[r].vertex[polyregions[r].numverts-1] == v1)\n\t\t\tendof = r;\n\n\t\tfor (v = polyregions[r].numverts-2; v >= 0; v--)\n\t\t\tif (polyregions[r].vertex[v] == v1 && polyregions[r].vertex[v+1] == v2)\n\t\t\t\treturn;\t//whoops. Duplicate line.\n\t}\n\tif (beginingof >= 0 && endof >= 0)\n\t{\t//merge two regions. Copy one onto the end of the other.\n\t\tif (beginingof == endof)\n\t\t{\t//close up\n\t\t\tif (polyregions[endof].numverts+1 >= MAX_POLYVERTS)\n\t\t\t{\n\t\t\t\tCon_Printf(CON_WARNING \"WARNING: Map region is too large.\\n\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tpolyregions[endof].vertex[polyregions[endof].numverts] = v2;\n\t\t\tpolyregions[endof].numverts++;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (polyregions[endof].numverts+polyregions[beginingof].numverts >= MAX_POLYVERTS)\n\t\t\t{\n\t\t\t\tCon_Printf(CON_WARNING \"WARNING: Map region is too large.\\n\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tmemcpy(polyregions[endof].vertex + polyregions[endof].numverts,\n\t\t\t\tpolyregions[beginingof].vertex,\n\t\t\t\tsizeof(polyregions[beginingof].vertex[0])*polyregions[beginingof].numverts);\n\t\t\tpolyregions[endof].numverts += polyregions[beginingof].numverts;\n\t\t\tpolyregions[beginingof].numverts = 0;\n\t\t}\n\t}\n\telse if (beginingof >= 0)\n\t{\t//insert into\n\t\tif (polyregions[beginingof].numverts+1 >= MAX_POLYVERTS)\n\t\t{\n\t\t\tCon_Printf(CON_WARNING \"WARNING: Map region is too large.\\n\");\n\t\t\treturn;\n\t\t}\n\n\t\tmemmove(polyregions[beginingof].vertex + 1,\n\t\t\tpolyregions[beginingof].vertex,\n\t\t\tsizeof(polyregions[beginingof].vertex[0])*polyregions[beginingof].numverts);\n\t\tpolyregions[beginingof].vertex[0] = v1;\n\t\tpolyregions[beginingof].numverts++;\n\t}\n\telse if (endof >= 0)\n\t{\t//stick outselves on the end\n\t\tif (polyregions[endof].numverts+1 >= MAX_POLYVERTS)\n\t\t{\n\t\t\tCon_Printf(CON_WARNING \"WARNING: Map region is too large.\\n\");\n\t\t\treturn;\n\t\t}\n\t\tpolyregions[endof].vertex[polyregions[endof].numverts] = v2;\n\t\tpolyregions[endof].numverts++;\n\t}\n\telse\n\t{\t//new region.\n\t\tif (freer < 0)\n\t\t{\n\t\t\tfreer = regions++;\n\t\t\tif (regions > MAX_REGIONS)\n\t\t\t{\n\t\t\t\tCon_Printf(CON_WARNING \"WARNING: Too many regions. Sector is too chaotic/complicated.\\n\");\n\t\t\t\tfreer = 0;\n\t\t\t\tregions = 1;\n\t\t\t}\n\t\t}\n\n\t\tpolyregions[freer].numverts = 2;\n\t\tpolyregions[freer].vertex[0] = v1;\n\t\tpolyregions[freer].vertex[1] = v2;\n\t}\n}\n\nstatic unsigned short *Triangulate_Finish(doommap_t *dm, int *numtris, unsigned short *old, int oldindexcount)\n{\n\tunsigned short *out;\n\tunsigned int v1, v2, v3, v;\n\tunsigned int r, v2s, f;\n\tfloat a1;\n\tfloat a2;\n\tfor (r = 0; r < regions; r++)\n\t{\n\t\tif (!polyregions[r].numverts)\n\t\t\tcontinue;\n\n\t\tif (polyregions[r].vertex[0] != polyregions[r].vertex[polyregions[r].numverts-1])\n\t\t{\n\t\t\tCon_Printf(\"Sector is not enclosed\\n\");\n\t\t\tpolyregions[r].vertex[polyregions[r].numverts] = polyregions[r].vertex[0];\n\t\t\tpolyregions[r].numverts++;\n\n\t\t\t/*\n\t\t\t*numtris = 0;\n\t\t\tregions = 0;\n\n\n\t\t\treturn NULL;*/\n\t\t}\n\n\t\tpolyregions[r].angle = 0;\n\t\tpolyregions[r].numverts--;//start == end\n\t\tfor (v = 0; v < polyregions[r].numverts; v++)\n\t\t{\n\t\t\tv1 = polyregions[r].vertex[v];\n\t\t\tv2 = polyregions[r].vertex[(v+1)%(polyregions[r].numverts)];\n\t\t\tv3 = polyregions[r].vertex[(v+2)%(polyregions[r].numverts)];\n\t\t\ta1 = atan2(dm->vertexes[v3].ypos - dm->vertexes[v2].ypos, dm->vertexes[v3].xpos - dm->vertexes[v2].xpos);\n\t\t\ta2 = atan2(dm->vertexes[v1].ypos - dm->vertexes[v2].ypos, dm->vertexes[v1].xpos - dm->vertexes[v2].xpos);\n\t\t\tpolyregions[r].angle += fabs(a1 - a2);\n\t\t}\n\t}\n\n\t//FIXME: inner loops should find the nearest point in a forwards direction from one of the extreeme points.\n\n\t//angle should be either (numverts-2)*PI\t//inner loop\n\t//or PI*numverts+2*PI\t\t\t\t\t\t//outer loop\n\t//unfortuantly it's rarly either of them...\n\n\tfor (r = 0; r < regions; r++)\n\t{\n\t\tif (polyregions[r].numverts<3)\n\t\t\tcontinue;\n\t\tv1 = polyregions[r].vertex[0];\n\t\tv2 = polyregions[r].vertex[1];\n\t\tv2s = 1;\n\t\tf=0;\n\t\tfor (v = 2; polyregions[r].numverts>=3; )\n\t\t{\t//build a triangle fan.\n\t\t\tif (numindexes+3 > MAX_POLYVERTS)\n\t\t\t{\n\t\t\t\tCon_Printf(CON_WARNING \"WARNING: Sector is too big for triangulation\\n\");\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tv3 = polyregions[r].vertex[v];\n\n\t\t\ta1 = atan2(dm->vertexes[v3].ypos - dm->vertexes[v2].ypos, dm->vertexes[v3].xpos - dm->vertexes[v2].xpos);\n\t\t\ta2 = atan2(dm->vertexes[v1].ypos - dm->vertexes[v2].ypos, dm->vertexes[v1].xpos - dm->vertexes[v2].xpos);\n\t\t\tif (fabs(a1-a2) > M_PI+0.01)\t//this would be a reflex angle then.;.\n\t\t\t{\n/*\t\t\t\tindexes[numindexes++] = 0;\n\t\t\t\tindexes[numindexes++] = v2;\n\t\t\t\tindexes[numindexes++] = 1;\n*/\n\t\t\t\tv1 = v2;\n\t\t\t\tv2 = v3;\n\t\t\t\tv2s = v;\n\t\t\t\tv=(v+1)%polyregions[r].numverts;\n\t\t\t\tf++;\n\t\t\t\tif (f >= 1000)\n\t\t\t\t{\t//infinate loop - shouldn't happen. must have got the angle stuff wrong.\n\t\t\t\t\tCon_Printf(CON_WARNING \"WARNING: Failed to triangulate polygon\\n\");\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t//FIXME: make sure v1 -> v3 doesn't clip any same-region lines.\n\n\t\t\tindexes[numindexes++] = v1;\n\t\t\tindexes[numindexes++] = v2;\n\t\t\tindexes[numindexes++] = v3;\n\t\t\tmemmove(polyregions[r].vertex+v2s, polyregions[r].vertex+v2s+1, (polyregions[r].numverts-v2s)*sizeof(polyregions[r].vertex[0]));\n\t\t\tpolyregions[r].numverts--;\n\t\t\tv=(v)%polyregions[r].numverts;\n\t\t\tv2 = v3;\n\t\t\tv2s = v;\n\t\t\tpolyregions[r].vertex[polyregions[r].numverts] = 0;\n\t\t}\n\t}\n\n\tif (!numindexes)\n\t{\n\t\tCon_Printf(CON_WARNING \"Warning: Sector is empty\\n\");\n\n\t\t*numtris = 0;\n\t\tregions = 0;\n\n\t\treturn NULL;\n\t}\n\n\tout = BZ_Realloc(old, sizeof(*out)*(numindexes+oldindexcount*3));\n\tmemcpy(out+oldindexcount*3, indexes, sizeof(*out)*numindexes);\n\n\t*numtris = numindexes/3+oldindexcount;\n\tregions = 0;\n\tnumindexes = 0;\n\n\treturn out;\n}\n\nstatic void Triangulate_Sectors(doommap_t *dm, dsector_t *sectorl, qboolean glbspinuse)\n{\n\tint seg, nsec;\n\tint i, sec=-1;\n\n\tif (glbspinuse)\n\t{\n\t\tfor (i = 0; i < dm->numssectors; i++)\n\t\t{\t//only do linedefs.\n\t\t\tfor (seg = dm->ssector[i].first; seg < dm->ssector[i].first + dm->ssector[i].segcount; seg++)\n\t\t\t\tif (dm->seg[seg].linedef != 0xffff)\n\t\t\t\t\tbreak;\n\n\t\t\tif (seg == dm->ssector[i].first + dm->ssector[i].segcount)\t//throw a fit.\n\t\t\t{\n\t\t\t\tCon_Printf(\"SubSector %i has absolutly no walls\\n\", i);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t\t\n\t\t\tnsec = dm->sidedef[dm->linedef[dm->seg[seg].linedef].sidedef[dm->seg[seg].direction]].sector;\n\t\t\tif (sec != nsec)\n\t\t\t{\n\t\t\t\tif (sec>=0)\n\t\t\t\t\tdm->sector[sec].flats = Triangulate_Finish(dm, &dm->sector[sec].numflattris, dm->sector[sec].flats, dm->sector[sec].numflattris);\n\t\t\t\tsec = nsec;\n\t\t\t}\n\t\t\tfor (seg = dm->ssector[i].first; seg < dm->ssector[i].first + dm->ssector[i].segcount; seg++)\n\t\t\t{\t//ignore direction, it's do do with the intersection rather than the draw direction.\n\t\t\t\tTriangulate_AddLine(dm->seg[seg].vert[0], dm->seg[seg].vert[1]);\n\t\t\t}\n\t\t}\n\t\tif (sec>=0)\n\t\t\tdm->sector[sec].flats = Triangulate_Finish(dm, &dm->sector[sec].numflattris, dm->sector[sec].flats, dm->sector[sec].numflattris);\n\t}\n\telse\n\t{\n\t\tfor (sec = 0; sec < dm->numsectors; sec++)\n\t\t{\n\t\t\tfor (i = 0; i < dm->numlinedefs; i++)\n\t\t\t{\n\t\t\t\tif (dm->sidedef[dm->linedef[i].sidedef[0]].sector == sec)\n\t\t\t\t\tTriangulate_AddLine(dm->linedef[i].vert[0], dm->linedef[i].vert[1]);\n\t\t\t\tif (dm->linedef[i].sidedef[1] != 0xffff && dm->sidedef[dm->linedef[i].sidedef[1]].sector == sec)\n\t\t\t\t\tTriangulate_AddLine(dm->linedef[i].vert[1], dm->linedef[i].vert[0]);\n\t\t\t}\n\t\t\tdm->sector[sec].flats = Triangulate_Finish(dm, &dm->sector[sec].numflattris, dm->sector[sec].flats, dm->sector[sec].numflattris);\n\t\t}\n\t}\n\n\t/*\n\tfor (i = 0; i < ssectorsc; i++)\n\t{\t//only do linedefs.\n\t\tseg = dm->ssector[i].first;\n\t\tnsec = dm->sidedef[dm->linedef[dm->seg[seg].linedef].sidedef[dm->seg[seg].direction]].sector;\n\t\tif (sec != nsec)\n\t\t{\n\t\t\tif (sec>=0)\n\t\t\t\tdm->sector[sec].flats = Triangulate_Finish(&dm->sector[sec].numflattris);\n\t\t\tsec = nsec;\n\t\t}\n\t\tfor (seg = dm->ssector[i].first; seg < dm->ssector[i].first + dm->ssector[i].segcount; seg++)\n\t\t{\t//ignore direction, it's do do with the intersection rather than the draw direction.\n\t\t\tTriangulate_AddLine(dm->seg[seg].vert[0], dm->seg[seg].vert[1]);\n\t\t}\n\t}\n\tif (sec>=0)\n\t\tdm->sector[sec].flats = Triangulate_Finish(&dm->sector[sec].numflattris);\n\t*/\n\n\tfor (i = 0; i < dm->numsectors; i++)\n\t{\n\t\tdm->sector[i].ceilingtex = Doom_LoadFlat(dm, sectorl[i].ceilingtexture);\n\t\tdm->sector[i].floortex = Doom_LoadFlat(dm, sectorl[i].floortexture);\n\t\tdm->sector[i].lightlev = sectorl[i].lightlevel;\n\t\tdm->sector[i].specialtype = sectorl[i].specialtype;\n\t\tdm->sector[i].tag = sectorl[i].tag;\n\t\tdm->sector[i].ceilingheight = sectorl[i].ceilingheight;\n\t\tdm->sector[i].floorheight = sectorl[i].floorheight;\n\t}\n}\n\n#ifndef SERVERONLY\nstatic void *textures1;\nstatic void *textures2;\nstatic char *pnames;\nstatic void Doom_LoadTextureInfos(void)\n{\n\ttextures1 = FS_LoadMallocFile(\"wad/texture1\", NULL);\n\ttextures2 = FS_LoadMallocFile(\"wad/texture2\", NULL);\n\tpnames = FS_LoadMallocFile(\"wad/pnames\", NULL);\n}\n\ntypedef struct {\n\tchar name[8];\n\tshort always0_0;\n\tshort always0_1;\n\tshort width;\n\tshort height;\n\tshort always0_2;\n\tshort always0_3;\n\tshort componantcount;\n} ddoomtexture_t;\ntypedef struct {\n\tshort xoffset;\n\tshort yoffset;\n\tunsigned short patchnum;\n\tunsigned short always_1;\n\tunsigned short always_0;\n} ddoomtexturecomponant_t;\n\ntypedef struct {\n\tshort width;\n\tshort height;\n\tshort xpos;\n\tshort ypos;\n} doomimage_t;\n\nstatic void Doom_ExtractPName(unsigned int *out, doomimage_t *di, size_t imgsize, int outwidth, int outheight, int x, int y)\n{\n\tunsigned int *colpointers;\n\tint c, fr, rc, extra;\n\tunsigned char *data, *coldata;\n\textern qbyte\t\tgammatable[256];\n\n\tif (!di)\n\t\treturn;\n\n\tdata = (char *)di;\n\n\n//\tout += x/*+di->xpos*/;\n//\tout += (y/*+di->ypos*/)*outwidth;\n\n\tcolpointers = (unsigned int*)(data+sizeof(doomimage_t));\n\tfor (c = 0; c < di->width; c++)\n\t{\n\t\tif (c+x < 0)\n\t\t\tcontinue;\n\t\tif (c+x >= outwidth)\n\t\t\tbreak;\n\n\t\tif (colpointers[c] >= imgsize)\n\t\t\tbreak;\n\t\tcoldata = data + colpointers[c];\n\t\twhile(1)\n\t\t{\n\t\t\tfr = *coldata++;\n\t\t\tif (fr == 255)\n\t\t\t\tbreak;\n\n\t\t\trc = *coldata++;\n\n\t\t\tcoldata++;\t//one not drawn, on each side\n\n\t\t\tfr+=y;\n\n\t\t\tif (fr<0)\n\t\t\t{\n\t\t\t\tcoldata += -fr;\t//plus\n\t\t\t\trc -= -fr;\n\t\t\t\tfr = 0;\n\t\t\t}\n\n\t\t\tif ((fr+rc) > outheight)\n\t\t\t{\n\t\t\t\textra = rc - (outheight - fr) +1;\n\t\t\t\trc = outheight - fr;\n\t\t\t\tif (rc < 0)\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\telse\n\t\t\t\textra = 1;\n\n\t\t\twhile(rc)\n\t\t\t{\n\t\t\t\tout[c+x + fr*outwidth] = (gammatable[doompalette[*coldata*3]]) + (gammatable[doompalette[*coldata*3+1]]<<8) + (gammatable[doompalette[*coldata*3+2]]<<16) + (255<<24);\n\t\t\t\tcoldata++;\n\t\t\t\tfr++;\n\t\t\t\trc--;\n\t\t\t}\n\n\t\t\tcoldata+=extra; //one not drawn, on each side\n\t\t}\n\t}\n}\n\nstatic texid_t Doom_LoadPatchFromTexWad(char *name, void *texlump, unsigned short *width, unsigned short *height, qboolean *hasalpha)\n{\n\tchar patch[32] = \"patches/\";\n\tunsigned int *tex;\n\tddoomtexture_t *tx;\n\tddoomtexturecomponant_t *tc;\n\ttexid_t result;\n\tint i;\n\tint count;\n\n\tcount = *(int *)texlump;\n\ttex = (int *)texlump+1;\n\n\tfor (i = 0; i < count; i++)\n\t{\n\t\ttx = (ddoomtexture_t*)((unsigned char*)texlump + *tex);\n\t\tif (!strncmp(tx->name, name, 8))\n\t\t{\n\t\t\ttex = BZ_Malloc(tx->width*tx->height*4);\n\t\t\tmemset(tex, 0, tx->width*tx->height*4);\n\t\t\t*width = tx->width;\n\t\t\t*height = tx->height;\n\t\t\ttc = (ddoomtexturecomponant_t*)(tx+1);\n\t\t\tfor (i = 0; i < tx->componantcount; i++, tc++)\n\t\t\t{\n\t\t\t\tdoomimage_t *img;\n\t\t\t\tsize_t imgsize;\n\t\t\t\tstrncpy(patch+8, pnames+4+8*tc->patchnum, 8);\n\t\t\t\tQ_strlwr(patch+8);\n\t\t\t\tpatch[16] = '\\0';\n\t\t\t\tQ_strncatz(patch, \".pat\", sizeof(patch));\n\n\t\t\t\timg = (doomimage_t *)FS_LoadMallocFile(patch, &imgsize);\n\t\t\t\tDoom_ExtractPName(tex, img, imgsize, tx->width, tx->height, tc->xoffset, tc->yoffset);\n\t\t\t\tBZ_Free(img);\n\t\t\t}\n\n\t\t\t*hasalpha = false;\n\t\t\tfor (i = 0; i < tx->width * tx->height; i++)\n\t\t\t{\n\t\t\t\tif (!(tex[i] & 0xff000000))\n\t\t\t\t{\n\t\t\t\t\t*hasalpha = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tresult = R_LoadTexture32(name, tx->width, tx->height, tex, 0);\n\t\t\tBZ_Free(tex);\n\t\t\treturn result;\n\t\t}\n\n\t\ttex++;\n\t}\n\n\treturn r_nulltex;\n}\nstatic int Doom_LoadPatch(doommap_t *dm, char *name)\n{\n\tqboolean hasalpha = false;\n\tint texnum;\n\tsize_t nlen = strnlen(name, 8);\n\n\tfor (texnum = 0; texnum < dm->numtextures; texnum++)\t//a hash table might be a good plan.\n\t{\n\t\tif(!memcmp(name, dm->textures[texnum].name, nlen) && !dm->textures[texnum].name[nlen])\n\t\t{\n\t\t\treturn texnum;\n\t\t}\n\t}\n\t//couldn't find it.\n//\ttexnum = dm->numtextures;\n\n\tdm->textures = BZ_Realloc(dm->textures, sizeof(*dm->textures)*((dm->numtextures+16)&~15));\n\tmemset(dm->textures + dm->numtextures, 0, sizeof(dm->textures[dm->numtextures]));\n\tdm->numtextures++;\n\n\tmemcpy(dm->textures[texnum].name, name, nlen);\n\tdm->textures[texnum].name[nlen] = 0;\n\n\treturn texnum;\n}\n\nstatic void Doom_LoadShaders(void *ctx, void *data, size_t a, size_t b)\n{\n\tmodel_t *mod = ctx;\n\tdoommap_t *dm = mod->meshinfo;\n\ttexnums_t tn;\n\tqboolean hasalpha = false;\n\tqboolean isflat;\n\tint texnum;\n\tchar tmp[MAX_QPATH];\n\n\tfor (texnum = 0; texnum < dm->numtextures; texnum++)\t//a hash table might be a good plan.\n\t{\n\t\tisflat = !strncmp(dm->textures[texnum].name, \"flats/\", 6);\n\t\tmemset(&tn, 0, sizeof(tn));\n\t\tif (isflat)\n\t\t{\n\t\t\tvoid *file = FS_LoadMallocFile(va2(tmp, sizeof(tmp), \"%s.raw\", dm->textures[texnum].name), NULL);\n\t\t\tif (file)\n\t\t\t{\n\t\t\t\ttn.base = Image_GetTexture(dm->textures[texnum].name, NULL, 0, file, doompalette, 64, 64, TF_8PAL24);\n\t\t\t\tZ_Free(file);\n\t\t\t}\n\t\t\tdm->textures[texnum].width = 64;\n\t\t\tdm->textures[texnum].height = 64;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (textures1 && !TEXVALID(tn.base))\n\t\t\t\ttn.base = Doom_LoadPatchFromTexWad(dm->textures[texnum].name, textures1, &dm->textures[texnum].width, &dm->textures[texnum].height, &hasalpha);\n\t\t\tif (textures2 && !TEXVALID(tn.base))\n\t\t\t\ttn.base = Doom_LoadPatchFromTexWad(dm->textures[texnum].name, textures2, &dm->textures[texnum].width, &dm->textures[texnum].height, &hasalpha);\n\t\t}\n\t\tif (!TEXVALID(tn.base))\n\t\t{\n\t\t\tdm->textures[texnum].width = 64;\n\t\t\tdm->textures[texnum].height = 64;\n\t\t\thasalpha = false;\n\t\t}\n\n\t\tif (hasalpha)\n\t\t\tdm->textures[texnum].shader = R_RegisterShader(dm->textures[texnum].name, SUF_NONE, \"{\\n{\\nmap $diffuse\\nrgbgen vertex\\nalphagen vertex\\nalphafunc ge128\\n}\\n}\\n\");\n\t\telse\n\t\t\tdm->textures[texnum].shader = R_RegisterShader(dm->textures[texnum].name, SUF_NONE, \"{\\n{\\nmap $diffuse\\nrgbgen vertex\\nalphagen vertex\\n}\\n}\\n\");\n\n\t\tR_BuildDefaultTexnums(&tn, dm->textures[texnum].shader);\n\t}\n};\n\nstatic void Doom_Purge (struct model_s *mod)\n{\n\tint texnum;\n\tdoommap_t *dm = mod->meshinfo;\n\tfor (texnum = 0; texnum < dm->numtextures; texnum++)\n\t{\n\t\tBZ_Free(dm->textures[texnum].mesh.colors4b_array);\n\t\tBZ_Free(dm->textures[texnum].mesh.st_array);\n\t\tBZ_Free(dm->textures[texnum].mesh.xyz_array);\n\t\tBZ_Free(dm->textures[texnum].mesh.indexes);\n\t}\n\tBZ_Free(dm->textures);\n\tdm->textures = NULL;\n}\n#endif\nstatic void CleanWalls(doommap_t *dm, dsidedef_t *sidedefsl)\n{\n\tint i;\n\tchar texname[64];\n\tchar lastmiddle[9]=\"-\";\n\tchar lastlower[9]=\"-\";\n\tchar lastupper[9]=\"-\";\n\tint lastmidtex=0, lastuptex=0, lastlowtex=0;\n\tdm->sidedef = BZ_Malloc(dm->numsidedefs * sizeof(*dm->sidedef));\n\tfor (i = 0; i < dm->numsidedefs; i++)\n\t{\n#if 1//def GLQUAKE\n\t\tstrncpy(texname, sidedefsl[i].middletex, 8);\n\t\ttexname[8] = '\\0';\n\t\tif (!strcmp(texname, \"-\"))\n\t\t\tdm->sidedef[i].middletex = 0;\n\t\telse\n\t\t{\n\t\t\tif (!strncmp(texname, lastmiddle, 8))\n\t\t\t\tdm->sidedef[i].middletex = lastmidtex;\n\t\t\telse\n\t\t\t{\n\t\t\t\tstrncpy(lastmiddle, texname, 8);\n\t\t\t\tdm->sidedef[i].middletex = lastmidtex = Doom_LoadPatch(dm, texname);\n\t\t\t}\n\t\t}\n\n\t\tstrncpy(texname, sidedefsl[i].lowertex, 8);\n\t\ttexname[8] = '\\0';\n\t\tif (!strcmp(texname, \"-\"))\n\t\t\tdm->sidedef[i].lowertex = 0;\n\t\telse\n\t\t{\n\t\t\tif (!strncmp(texname, lastlower, 8))\n\t\t\t\tdm->sidedef[i].lowertex = lastlowtex;\n\t\t\telse\n\t\t\t{\n\t\t\t\tstrncpy(lastlower, texname, 8);\n\t\t\t\tdm->sidedef[i].lowertex = lastlowtex = Doom_LoadPatch(dm, texname);\n\t\t\t}\n\t\t}\n\n\t\tstrncpy(texname, sidedefsl[i].uppertex, 8);\n\t\ttexname[8] = '\\0';\n\t\tif (!strcmp(texname, \"-\"))\n\t\t\tdm->sidedef[i].uppertex = 0;\n\t\telse\n\t\t{\n\t\t\tif (!strncmp(texname, lastupper, 8))\n\t\t\t\tdm->sidedef[i].uppertex = lastuptex;\n\t\t\telse\n\t\t\t{\n\t\t\t\tstrncpy(lastupper, texname, 8);\n\t\t\t\tdm->sidedef[i].uppertex = lastuptex = Doom_LoadPatch(dm, texname);\n\t\t\t}\n\t\t}\n#endif\n\t\tdm->sidedef[i].sector = sidedefsl[i].sector;\n\t\tdm->sidedef[i].texx = sidedefsl[i].texx;\n\t\tdm->sidedef[i].texy = sidedefsl[i].texy;\n\t}\n}\n\nvoid QuakifyThings(doommap_t *dm)\n{\n\tmsector_t *sector;\n\tint spawnflags;\n\tchar *name;\n\tint i;\n\tint zpos;\n\tstatic char newlump[1024*1024];\t//FIXME\n\tchar thingname[MAX_QPATH];\n\t\n\tchar *ptr = newlump;\n\tvec3_t point;\n\n\tsprintf(ptr,\t\"{\\n\"\n\t\t\t\t\t\"\\\"classname\\\" \\\"worldspawn\\\"\\n\"\n\t\t\t\t\t\"}\\n\");\n\tptr += strlen(ptr);\n\n\tfor (i = 0; i < dm->numthings; i++)\n\t{\n\t\tfloat zbias = 24;\n\t\tswitch(dm->thing[i].type)\n\t\t{\n\t\tcase THING_PLAYER:\t//fixme: spit out a coop spawn too.\n\t\t\tname = \"info_player_start\";\n\t\t\tbreak;\n\t\tcase THING_PLAYER2:\n\t\tcase THING_PLAYER3:\n\t\tcase THING_PLAYER4:\n\t\t\tname = \"info_player_coop\";\n\t\t\tbreak;\n\t\tcase THING_DMSPAWN:\n\t\t\tname = \"info_player_deathmatch\";\n\t\t\tbreak;\n\n\n\t\tcase THING_WCHAINSAW:\n\t\t\tname = \"item_artifact_super_damage\";\n\t\t\tbreak;\n\t\tcase THING_WSHOTGUN1:\n\t\t\tname = \"weapon_nailgun\";\n\t\t\tbreak;\n\t\tcase THING_WSHOTGUN2:\n\t\t\tname = \"weapon_supernailgun\";\n\t\t\tbreak;\n\t\tcase THING_WCHAINGUN:\n\t\t\tname = \"weapon_supershotgun\";\n\t\t\tbreak;\n\t\tcase THING_WROCKETL:\n\t\t\tname = \"weapon_rocketlauncher\";\n\t\t\tbreak;\n\t\tcase THING_WPLASMA:\n\t\t\tname = \"weapon_grenadelauncher\";\n\t\t\tbreak;\n\t\tcase THING_WBFG:\n\t\t\tname = \"weapon_lightning\";\n\t\t\tbreak;\n\n\t\tdefault:\n\t\t\tname = va2(thingname, sizeof(thingname), \"thing_%i\", dm->thing[i].type);\n\t\t\tzbias = 0;\n\t\t\tbreak;\n\t\t}\n\n\t\tpoint[0] = dm->thing[i].xpos;\n\t\tpoint[1] = dm->thing[i].ypos;\n\t\tpoint[2] = 0;\n\t\tsector = Doom_SectorNearPoint(dm, point);\n\t\tzpos = sector->floorheight + zbias;\t//things have no z coord, so find the sector they're in\n\n\t\tspawnflags = SPAWNFLAG_NOT_EASY | SPAWNFLAG_NOT_MEDIUM | SPAWNFLAG_NOT_HARD | SPAWNFLAG_NOT_DEATHMATCH;\n\t\tif (dm->thing[i].flags & THING_EASY)\n\t\t\tspawnflags -= SPAWNFLAG_NOT_EASY;\n\t\tif (dm->thing[i].flags & THING_MEDIUM)\n\t\t\tspawnflags -= SPAWNFLAG_NOT_MEDIUM;\n\t\tif (dm->thing[i].flags & THING_HARD)\n\t\t\tspawnflags -= SPAWNFLAG_NOT_HARD;\n\t\tif (dm->thing[i].flags & THING_DEATHMATCH)\n\t\t\tspawnflags -= SPAWNFLAG_NOT_DEATHMATCH;\n\t\tif (dm->thing[i].flags & THING_DEAF)\n\t\t\tspawnflags |= 1;\n\n\t\tQ_snprintfz(ptr, newlump+sizeof(newlump)-ptr,\t\"{\\n\"\n\t\t\t\t\t\t\"\\\"classname\\\" \\\"%s\\\"\\n\"\n\t\t\t\t\t\t\"\\\"origin\\\" \\\"%i %i %i\\\"\\n\"\n\t\t\t\t\t\t\"\\\"spawnflags\\\" \\\"%i\\\"\\n\"\n\t\t\t\t\t\t\"\\\"angle\\\" \\\"%i\\\"\\n\"\n\t\t\t\t\t\t\"}\\n\",\n\t\t\t\t\t\t\tname,\n\t\t\t\t\t\t\tdm->thing[i].xpos, dm->thing[i].ypos, zpos,\n\t\t\t\t\t\t\tspawnflags,\n\t\t\t\t\t\t\tdm->thing[i].angle\n\t\t\t\t\t\t);\n\t\tptr += strlen(ptr);\n\t}\n\n\tMod_SetEntitiesStringLen(dm->model, newlump, ptr-newlump);\n}\n\nvoid Doom_GeneratePlanes(doommap_t *dm)\n{\n\tvec3_t point, up, line;\n\tint n;\n\tup[0] = 0;\n\tup[1] = 0;\n\tup[2] = 1;\n\tline[2] = 0;\n\tdm->nodeplane = BZ_Malloc(sizeof(*dm->nodeplane)*dm->numnodes);\n\tdm->lineplane = BZ_Malloc(sizeof(*dm->lineplane)*dm->numlinedefs);\n\tpoint[2] = 0;\n\tfor (n = 0; n < dm->numnodes; n++)\n\t{\n\t\tline[0] = dm->node[n].dx;\n\t\tline[1] = dm->node[n].dy;\n\t\tpoint[0] = dm->node[n].x;\n\t\tpoint[1] = dm->node[n].y;\n\t\tCrossProduct(line, up, dm->nodeplane[n].normal);\n\t\tVectorNormalize(dm->nodeplane[n].normal);\n\t\tdm->nodeplane[n].dist = DotProduct (point, dm->nodeplane[n].normal);\n\t}\n\n\tfor (n = 0; n < dm->numlinedefs; n++)\n\t{\n\t\tpoint[0] = dm->vertexes[dm->linedef[n].vert[0]].xpos;\n\t\tpoint[1] = dm->vertexes[dm->linedef[n].vert[0]].ypos;\n\t\tline[0] = dm->vertexes[dm->linedef[n].vert[1]].xpos-point[0];\n\t\tline[1] = dm->vertexes[dm->linedef[n].vert[1]].ypos-point[1];\n\t\tCrossProduct(line, up, dm->lineplane[n].normal);\n\t\tVectorNormalize(dm->lineplane[n].normal);\n\t\tdm->lineplane[n].dist = DotProduct (point, dm->lineplane[n].normal);\n\t}\n}\n\n/*\ndoom maps have no network limitations, but has +/-32767 map size limits (same as quake bsp)\nfte defaults to a +/- 4096 world\na lot of maps are off-centered and can be moved to get them to fit fte's constraints, so if we can, do so\n*/\nstatic void MoveWorld(doommap_t *dm)\n{\n\tint v;\n\tshort adj[2];\n\tshort min[2], max[2];\n\tmin[0] = 4096;\n\tmin[1] = 4096;\n\tmax[0] = -4096;\n\tmax[1] = -4096;\n\n\tfor (v = 0; v < dm->numvertexes; v++)\n\t{\n\t\tif (min[0] > dm->vertexes[v].xpos)\n\t\t\tmin[0] = dm->vertexes[v].xpos;\n\t\tif (min[1] > dm->vertexes[v].ypos)\n\t\t\tmin[1] = dm->vertexes[v].ypos;\n\n\t\tif (max[0] < dm->vertexes[v].xpos)\n\t\t\tmax[0] = dm->vertexes[v].xpos;\n\t\tif (max[1] < dm->vertexes[v].ypos)\n\t\t\tmax[1] = dm->vertexes[v].ypos;\n\t}\n\n\tif (min[0]>=-4096 && max[0]<=4096)\n\t\tif (min[1]>=-4096 && max[1]<=4096)\n\t\t\tadj[0] = adj[1] = 0;\t//doesn't need adjusting, live with it.\n\n\tif (max[0]-min[0]>=8192 || max[1]-min[1]>=8192)\n\t{\n\t\tCon_Printf(CON_WARNING \"Warning: Map is too large for the network protocol\\n\");\n\t\tadj[0] = adj[1] = 0;\n\t}\n\telse\n\t{\n\t\tadj[0] = (max[0]-4096)&~63;\t//don't harm the tiling.\n\t\tadj[1] = (max[1]-4096)&~63;\n\t}\n\n\tdm->model->mins[0] = min[0] - adj[0];\n\tdm->model->mins[1] = min[1] - adj[1];\n\tdm->model->mins[2] = -32768;\n\n\tdm->model->maxs[0] = max[0] - adj[0];\n\tdm->model->maxs[1] = max[1] - adj[1];\n\tdm->model->maxs[2] = 32767;\n\n\tif (!adj[0] && !adj[1])\n\t\treturn;\n\n\tCon_Printf(\"Adjusting map (%i %i)\\n\", -adj[0], -adj[1]);\n\n\tfor (v = 0; v < dm->numvertexes; v++)\n\t{\n\t\tdm->vertexes[v].xpos -= adj[0];\n\t\tdm->vertexes[v].ypos -= adj[1];\n\t}\n\n\tfor (v = 0; v < dm->numnodes; v++)\n\t{\n\t\tdm->node[v].x -= adj[0];\n\t\tdm->node[v].y -= adj[1];\n\n\t\tdm->node[v].x1lower -= adj[0];\n\t\tdm->node[v].x1upper -= adj[1];\n\t\tdm->node[v].y1lower -= adj[0];\n\t\tdm->node[v].y1upper -= adj[1];\n\n\t\tdm->node[v].x2lower -= adj[0];\n\t\tdm->node[v].x2upper -= adj[1];\n\t\tdm->node[v].y2lower -= adj[0];\n\t\tdm->node[v].y2upper -= adj[1];\n\t}\n\n\tfor (v = 0; v < dm->numthings; v++)\n\t{\n\t\tdm->thing[v].xpos -= adj[0];\n\t\tdm->thing[v].ypos -= adj[1];\n\t}\n\n\tdm->blockmap->xorg -= adj[0];\n\tdm->blockmap->yorg -= adj[1];\n}\n\n\nstatic void Doom_LoadVerticies(doommap_t *dm, char *name)\n{\n\tddoomvertex_t *std, *gl1;\n\tint stdc, glc;\n\tint *gl2;\n\tint i;\n\tsize_t fsize;\n\tchar tmp[MAX_QPATH];\n\n\tstd\t\t= (void *)FS_LoadMallocFile\t(va2(tmp,sizeof(tmp),\"%s.vertexes\",\tname), &fsize);\n\tstdc\t= fsize/sizeof(*std);\n\n\tgl2\t\t= (void *)FS_LoadMallocFile\t(va2(tmp,sizeof(tmp),\"%s.gl_vert\",\tname), &fsize);\n\tif (!gl2)\n\t{\n\t\tglc = 0;\n\t\tgl1 = NULL;\n\t}\n\telse if (gl2[0] == (('g'<<0)|('N'<<8)|('d'<<16)|('2'<<24)))\n\t{\n\t\tgl2++;\n\t\tglc = (fsize-4)/sizeof(int)/2;\n\t\tgl1 = NULL;\n\t}\n\telse\n\t{\n\t\tglc\t= fsize/sizeof(*gl1);\n\t\tgl1 = (ddoomvertex_t*)gl2;\n\t}\n\n\tif (stdc)\n\t{\n\t\tdm->numvertexes = stdc + glc;\n\t\tdm->vertexes = BZ_Malloc(dm->numvertexes*sizeof(*dm->vertexes));\n\n\t\tdm->vertexsglbase = stdc;\n\n\t\tfor (i = 0; i < stdc; i++)\n\t\t{\n\t\t\tdm->vertexes[i].xpos = std[i].xpos;\n\t\t\tdm->vertexes[i].ypos = std[i].ypos;\n\t\t}\n\t\tif (gl1)\n\t\t{\n\t\t\tfor (i = 0; i < glc; i++)\n\t\t\t{\n\t\t\t\tdm->vertexes[stdc+i].xpos = gl1[i].xpos;\n\t\t\t\tdm->vertexes[stdc+i].ypos = gl1[i].ypos;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tfor (i = 0; i < glc; i++)\n\t\t\t{\n\t\t\t\tdm->vertexes[stdc+i].xpos = (float)gl2[i*2] / 0x10000;\n\t\t\t\tdm->vertexes[stdc+i].ypos = (float)gl2[i*2+1] / 0x10000;\n\t\t\t}\n\t\t}\n\t}\n\tZ_Free(std);\n\tZ_Free(gl2);\n}\n\nstatic void Doom_LoadSSectors(doommap_t *dm, char *name)\n{\n\tdssector_t *in;\n\tsize_t fsize;\n\tunsigned int i;\n\tchar tmp[MAX_QPATH];\n\tin\t= (void *)FS_LoadMallocFile\t(va2(tmp, sizeof(tmp), \"%s.gl_ssect\",\tname), &fsize);\n\tif (!in)\n\t\tin\t= (void *)FS_LoadMallocFile\t(va2(tmp, sizeof(tmp), \"%s.ssectors\",\tname), &fsize);\n\t//FIXME: \"gNd3\" means that it's glbsp version 3.\n\tdm->numssectors\t= fsize/sizeof(*in);\n\n\tdm->ssector = Z_Malloc(dm->numssectors * sizeof(*dm->ssector));\n\tfor (i = 0; i < dm->numssectors; i++)\n\t{\n\t\tdm->ssector[i].segcount = in[i].segcount;\n\t\tdm->ssector[i].first = in[i].first;\n\t}\n\tZ_Free(in);\n}\nstatic void Doom_CalcSubsectorSectors(doommap_t *dm)\n{\t//kinda shitty\n\tunsigned int num, seg;\n\tfor (num = 0; num < dm->numssectors; num++)\n\t{\n\t\tdm->ssector[num].sector = &dm->sector[dm->sidedef[dm->linedef[dm->seg[dm->ssector[num].first].linedef].sidedef[dm->seg[dm->ssector[num].first].direction]].sector];\n\t\tfor (seg = dm->ssector[num].first+1; seg < dm->ssector[num].first + dm->ssector[num].segcount; seg++)\n\t\t\tif (dm->seg[seg].linedef != 0xffff)\n\t\t\t{\n\t\t\t\tdm->ssector[num].sector = &dm->sector[dm->sidedef[dm->linedef[dm->seg[seg].linedef].sidedef[dm->seg[seg].direction]].sector];\n\t\t\t\tbreak;\n\t\t\t}\n\t}\n}\n\nstatic void Doom_LoadSSegs(doommap_t *dm, char *name)\n{\t//these skirt the subsectors\n\n\tvoid *file;\n\tdgl_seg3_t\t*s3;\n\tdgl_seg1_t\t*s1;\n\tdseg_t\t\t*s0;\n\tint i;\n\tsize_t fsize;\n\tchar tmp[MAX_QPATH];\n\n\tfile\t= (void *)FS_LoadMallocFile\t(va2(tmp, sizeof(tmp), \"%s.gl_segs\",\tname), &fsize);\n\tif (!file)\n\t{\n\t\ts0 = (void *)FS_LoadMallocFile\t(va2(tmp, sizeof(tmp), \"%s.segs\",\tname), &fsize);\n\t\tdm->numsegs\t= fsize/sizeof(*s0);\n\n\t\tdm->seg = BZ_Malloc(dm->numsegs * sizeof(*dm->seg));\n\t\tfor (i = 0; i < dm->numsegs; i++)\n\t\t{\n\t\t\tdm->seg[i].vert[0] = s0[i].vert[0];\n\t\t\tdm->seg[i].vert[1] = s0[i].vert[1];\n\t\t\tdm->seg[i].linedef = s0[i].linedef;\n\t\t\tdm->seg[i].direction = s0[i].direction;\n\t\t\tdm->seg[i].Partner = 0xffff;\n\t\t}\n\t}\n\telse if (*(int *)file == *(int *)\"gNd3\")\n\t{\n\t\ts3 = file;\n\t\tdm->numsegs\t= fsize/sizeof(*s3);\n\n\t\tdm->seg = s3;\n\t}\n\telse if (!file)\n\t\treturn;\n\telse\n\t{\n\t\ts1 = file;\n\t\tdm->numsegs\t= fsize/sizeof(*s1);\n\n\t\tdm->seg = BZ_Malloc(dm->numsegs * sizeof(*dm->seg));\n\t\tfor (i = 0; i < dm->numsegs; i++)\n\t\t{\n\t\t\tif (s1[i].vert[0] & 0x8000)\n\t\t\t\tdm->seg[i].vert[0] = (s1[i].vert[0]&0x7fff)+dm->vertexsglbase;\n\t\t\telse\n\t\t\t\tdm->seg[i].vert[0] = s1[i].vert[0];\n\t\t\tif (s1[i].vert[1] & 0x8000)\n\t\t\t\tdm->seg[i].vert[1] = (s1[i].vert[1]&0x7fff)+dm->vertexsglbase;\n\t\t\telse\n\t\t\t\tdm->seg[i].vert[1] = s1[i].vert[1];\n\t\t\tdm->seg[i].linedef = s1[i].linedef;\n\t\t\tdm->seg[i].direction = s1[i].direction;\n\t\t\tif (s1[i].Partner == 0xffff)\n\t\t\t\tdm->seg[i].Partner = 0xffffffff;\n\t\t\telse\n\t\t\t\tdm->seg[i].Partner = s1[i].Partner;\n\t\t}\n\t}\n}\n\nqboolean QDECL Mod_LoadDoomLevel(model_t *mod, void *buffer, size_t fsize)\n{\n\tint h;\n\tdsector_t\t\t*sectorl;\n\tdsidedef_t\t\t*sidedefsl;\n\tchar name[MAX_QPATH];\n\tchar tmp[MAX_QPATH];\n\tdoommap_t *dm;\n\n\tint *gl_nodes;\n\n\tif (fsize != 4)\n\t{\n\t\tCon_Printf(\"Wad map %s does actually exist... weird.\\n\", mod->name);\n\t\treturn false;\n\t}\n\n\tdm = Z_Malloc(sizeof(*dm));\n\tdm->model = mod;\n\tmod->meshinfo = dm;\n\n\tCOM_StripExtension(mod->name, name, sizeof(name));\n\n\tgl_nodes\t= (void *)FS_LoadMallocFile\t(va2(tmp,sizeof(tmp),\"%s.gl_nodes\",\tname), &fsize);\n\tif (gl_nodes && fsize>0)\n\t{\n\t\tdm->node = (void *)gl_nodes;\n\t\tdm->numnodes = fsize/sizeof(*dm->node);\n\t}\n\telse\n\t{\n\t\tgl_nodes=NULL;\n\t\tdm->node\t\t= (void *)FS_LoadMallocFile\t(va2(tmp,sizeof(tmp),\"%s.nodes\",\t\tname), &fsize);\n\t\tdm->numnodes\t\t= fsize/sizeof(*dm->node);\n\t}\n\tsectorl\t\t= (void *)FS_LoadMallocFile\t(va2(tmp,sizeof(tmp),\"%s.sectors\",\tname), &fsize);\n\tdm->numsectors\t\t= fsize/sizeof(*sectorl);\n\tdm->sector = Z_Malloc(dm->numsectors * sizeof(*dm->sector));\n\n#ifndef SERVERONLY\n\tdm->numtextures=0;\n\tDoom_LoadPalette();\n#endif\n\n\n\tDoom_LoadVerticies(dm, name);\n\n\tDoom_LoadSSegs(dm, name);\n\tDoom_LoadSSectors(dm, name);\n\n\tdm->thing\t\t= (void *)FS_LoadMallocFile\t(va2(tmp,sizeof(tmp),\"%s.things\",\tname), &fsize);\n\tdm->numthings\t\t= fsize/sizeof(*dm->thing);\n\tdm->linedef\t= (void *)FS_LoadMallocFile\t(va2(tmp,sizeof(tmp),\"%s.linedefs\",\tname), &fsize);\n\tdm->numlinedefs\t= fsize/sizeof(*dm->linedef);\n\tsidedefsl\t= (void *)FS_LoadMallocFile\t(va2(tmp,sizeof(tmp),\"%s.sidedefs\",\tname), &fsize);\n\tdm->numsidedefs\t= fsize/sizeof(*sidedefsl);\n\tdm->blockmap\t= (void *)FS_LoadMallocFile\t(va2(tmp,sizeof(tmp),\"%s.blockmap\",\tname), &fsize);\n\n#ifndef SERVERONLY\n\tDoom_LoadTextureInfos();\n#endif\n\tdm->blockmapofs = (unsigned short*)(dm->blockmap+1);\n\n\tif (!dm->node || !sectorl || !dm->seg || !dm->ssector || !dm->thing || !dm->linedef || !sidedefsl || !dm->vertexes)\n\t{\n\t\tSys_Error(\"Wad map doesn't contain enough lumps\\n\");\n\t\tdm->node = NULL;\n\t\treturn false;\n\t}\n\n\tMoveWorld(dm);\n\n\tDoom_GeneratePlanes(dm);\n\n\tmod->hulls[0].clip_mins[0] = 0;\n\tmod->hulls[0].clip_mins[1] = 0;\n\tmod->hulls[0].clip_mins[2] = 0;\n\tmod->hulls[0].clip_maxs[0] = 0;\n\tmod->hulls[0].clip_maxs[1] = 0;\n\tmod->hulls[0].clip_maxs[2] = 0;\n\tmod->hulls[0].available = true;\n\n\tfor (h = 1; h < MAX_MAP_HULLSM; h++)\n\t\tmod->hulls[h].available = false;\n\n\tDoom_SetModelFunc(mod);\n\n\tmod->fromgame = fg_doom;\n\tmod->type = mod_brush;\n\tmod->nodes = (void*)0x1;\n\tmod->numclusters = dm->numsectors;\n\n\tCleanWalls(dm, sidedefsl);\n\n\tDoom_CalcSubsectorSectors(dm);\n\n\tTriangulate_Sectors(dm, sectorl, !!gl_nodes);\n\n\tQuakifyThings(dm);\n\n\tCOM_AddWork(WG_MAIN, Doom_LoadShaders, mod, NULL, 0, 0);\n\treturn true;\n}\n\nstatic void Doom_LightPointValues(model_t *model, vec3_t point, vec3_t res_diffuse, vec3_t res_ambient, vec3_t res_dir)\n{\n\tdoommap_t *dm = model->meshinfo;\n\tmsector_t *sec;\n\tsec = Doom_SectorNearPoint(dm, point);\n\n\tres_dir[0] = 0;\n\tres_dir[1] = 1;\n\tres_dir[2] = 1;\n\tres_diffuse[0] = sec->lightlev;\n\tres_diffuse[1] = sec->lightlev;\n\tres_diffuse[2] = sec->lightlev;\n\tres_ambient[0] = sec->lightlev;\n\tres_ambient[1] = sec->lightlev;\n\tres_ambient[2] = sec->lightlev;\n}\n\n//return pvs bits for point\nstatic unsigned int Doom_FatPVS(struct model_s *model, vec3_t org, pvsbuffer_t *pvsbuffer, qboolean merge)\n{\n\t//FIXME: use REJECT lump.\n\treturn 0;\n}\n\n//check if an ent is within the given pvs\nstatic qboolean Doom_EdictInFatPVS(struct model_s *model, struct pvscache_s *edict, qbyte *pvsbuffer)\n{\t//FIXME: use REJECT lump.\n\treturn true;\n}\n\nstatic int Doom_ClusterForPoint(struct model_s *model, vec3_t point)\n{\n\tdoommap_t *dm = model->meshinfo;\n\treturn Doom_SectorNearPoint(dm, point) - dm->sector;\n}\nstatic qbyte *Doom_ClusterPVS(struct model_s *model, int cluster, pvsbuffer_t *pvsbuffer, pvsmerge_t merge)\n{\t//FIXME: use REJECT lump.\n\treturn NULL;\n}\n\n//generate useful info for correct functioning of Doom_EdictInFatPVS.\nstatic void Doom_FindTouchedLeafs(struct model_s *model, struct pvscache_s *ent, vec3_t cullmins, vec3_t cullmaxs)\n{\n\t//work out the sectors this ent is in for easy pvs.\n}\n\n//requires lightmaps - not supported.\nstatic void Doom_StainNode(struct mnode_s *node, float *parms)\n{\n}\n\n//requires lightmaps - not supported.\nstatic void Doom_MarkLights(struct dlight_s *light, int bit, struct mnode_s *node)\n{\n}\n\nvoid Doom_SetModelFunc(model_t *mod)\n{\n#ifndef SERVERONLY\n\tmod->funcs.PurgeModel\t\t\t= Doom_Purge;\n#endif\n\tmod->funcs.FatPVS\t\t\t\t= Doom_FatPVS;\n\tmod->funcs.EdictInFatPVS\t\t= Doom_EdictInFatPVS;\n\tmod->funcs.FindTouchedLeafs\t\t= Doom_FindTouchedLeafs;\n\tmod->funcs.ClusterForPoint\t\t= Doom_ClusterForPoint;\n\tmod->funcs.ClusterPVS\t\t\t= Doom_ClusterPVS;\n\n\tmod->funcs.LightPointValues\t\t= Doom_LightPointValues;\n\tmod->funcs.StainNode\t\t\t= Doom_StainNode;\n\tmod->funcs.MarkLights\t\t\t= Doom_MarkLights;\n\n//\tmod->funcs.LeafPVS)\t\t\t(struct model_s *model, int num, qbyte *buffer, unsigned int buffersize);\n\n\tmod->funcs.NativeTrace\t\t\t= Doom_Trace;\n\tmod->funcs.PointContents\t\t= Doom_PointContents;\n\n\t//Doom_SetCollisionFuncs(mod);\n}\n\n#endif\n"
  },
  {
    "path": "engine/gl/glquake.h",
    "content": "/*\nCopyright (C) 1996-1997 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n*/\n\n#ifndef GLQUAKE_H\n#define GLQUAKE_H\n\n// disable data conversion warnings\n#ifdef MSVCDISABLEWARNINGS\n#pragma warning(disable : 4244)     // MIPS\n#pragma warning(disable : 4136)     // X86\n#pragma warning(disable : 4051)     // ALPHA\n#endif\n\nvoid AddPointToBounds (const vec3_t v, vec3_t mins, vec3_t maxs);\nqboolean BoundsIntersect (const vec3_t mins1, const vec3_t maxs1, const vec3_t mins2, const vec3_t maxs2);\nvoid ClearBounds (vec3_t mins, vec3_t maxs);\n\nvoid ModBrush_LoadGLStuff(void *ctx, void *data, size_t a, size_t b);\t//data === builddata_t\n\n#ifdef GLQUAKE\n\t#if defined(ANDROID) /*FIXME: actually just to use standard GLES headers instead of full GL*/\n\t\t#ifndef GLSLONLY\n\t\t\t#include <GLES/gl.h>\n\t\t\t#ifndef GL_CLIP_PLANE0\n\t\t\t#define GL_CLIP_PLANE0 0x3000\n\t\t\t#endif\n\t\t#else\n\t\t\t#include <GLES2/gl2.h>\n\t\t#endif\n\t\t/*gles has no doubles*/\n\t\t#define GLclampd GLclampf\n\t\t#define GLdouble GLfloat\n\t\t#define GL_NONE                           0\n\t#elif defined(__APPLE__) || defined(__MACOSX__)\n\t\t#include <OpenGL/OpenGLAvailability.h>\n\t\t#if defined(OPENGL_DEPRECATED) && !defined(GL_SILENCE_DEPRECATION)\n\t\t\t#undef OPENGL_DEPRECATED\n\t\t\t#define OPENGL_DEPRECATED(from, to) API_DEPRECATED(\"Apple is deprecated. (Define GL_SILENCE_DEPRECATION to silence these warnings)\", macos(from, to))\n\t\t#endif\n\t\t#include <OpenGL/gl.h>\t//tuna says use this.\n\t\t//apple really do suck.\n\t#elif defined(FTE_TARGET_WEB)\n\t\t#include <GLES2/gl2.h>\n\t\t#define GLclampd GLclampf\n\t\t#define GLdouble GLfloat\n\t#else\n\t\t#ifdef _WIN32\t//windows might use the standard header filename, but it still requires that we manually include windows.h first.\n\t\t\t#if !defined(WIN32_BLOATED) && !defined(WIN32_LEAN_AND_MEAN)\n\t\t\t\t#define WIN32_LEAN_AND_MEAN\n\t\t\t#endif\n\t\t\t#include <windows.h>\n\t\t#endif\n\n\t\t#include <GL/gl.h>\n\t\t#ifdef GL_STATIC\n\t\t\t#define GL_GLEXT_PROTOTYPES\n\t\t\t#include <GL/glext.h>\n\t\t#endif\n\t#endif\n//\t#include <GL/glu.h>\n\n\t#ifndef APIENTRY\n\t\t#define APIENTRY\t//our code decorates function pointers with this for windows, so make sure it exists on systems that don't need it.\n\t#endif\n\t#include \"glsupp.h\"\n\t\t\t\n\n\n\t/*gles2 has no fixed function*/\n#ifndef GL_ALPHA_TEST\n\t#define GL_ALPHA_TEST 0\n#endif\n#ifndef GL_FILL\n\t#define GL_FILL (Sys_Error(\"GL_FILL was used\"),0)\n#endif\n#ifndef GL_CLAMP\n\t#define GL_CLAMP GL_CLAMP_TO_EDGE\n#endif\n#ifndef GL_TEXTURE_ENV\n\t#define GL_TEXTURE_ENV (Con_Printf(\"GL_TEXTURE_ENV was used\"),0)\n\t#define GL_TEXTURE_ENV_MODE (Con_Printf(\"GL_TEXTURE_ENV_MODE was used\"),0)\n\t#define GL_VERTEX_ARRAY (Con_Printf(\"GL_VERTEX_ARRAY was used\"),0)\n\t#define GL_COLOR_ARRAY (Con_Printf(\"GL_COLOR_ARRAY was used\"),0)\n\t#define GL_TEXTURE_COORD_ARRAY (Con_Printf(\"GL_TEXTURE_COORD_ARRAY was used\"),0)\n\t#define GL_DECAL (Con_Printf(\"GL_DECAL was used\"),0)\n\t#define GL_ADD (Con_Printf(\"GL_ADD was used\"),0)\n\t#define GL_FLAT (Con_Printf(\"GL_FLAT was used\"),0)\n\t#define GL_SMOOTH (Con_Printf(\"GL_SMOOTH was used\"),0)\n\t#define GL_MODULATE 0x2100\n\t#define GL_PROJECTION (Con_Printf(\"GL_PROJECTION was used\"),0)\n\t#define GL_MODELVIEW (Con_Printf(\"GL_MODELVIEW was used\"),0)\n\t#define GL_CLIP_PLANE0 (Con_Printf(\"GL_CLIP_PLANE0 was used\"),0)\n#endif\n#ifndef GL_COLOR_ARRAY_POINTER\n\t#define GL_COLOR_ARRAY_POINTER 0\n\t#define GL_NORMAL_ARRAY 0\n\t#define GL_NORMAL_ARRAY_POINTER 0\n\t#define GL_TEXTURE_COORD_ARRAY_POINTER 0\n\t#define GL_VERTEX_ARRAY_POINTER 0\n\t#define GL_BLEND_SRC 0\n\t#define GL_BLEND_DST 0\n#endif\n#ifndef GL_POLYGON\n\t#define GL_POLYGON (Con_Printf(\"GL_POLYGON was used\"),0)\n\t#define GL_QUAD_STRIP (Con_Printf(\"GL_QUAD_STRIP was used\"),0)\n\t#define GL_QUADS (Con_Printf(\"GL_QUADS was used\"),0)\n#endif\n\nvoid GL_InitFogTexture(void);\n\n#ifndef GL_VERSION_2_0\n#define GLchar char\n#endif\n\n// Function prototypes for the Texture Object Extension routines\ntypedef GLboolean (APIENTRY *ARETEXRESFUNCPTR)(GLsizei, const GLuint *,\n                    const GLboolean *);\ntypedef void (APIENTRY *BINDTEXFUNCPTR)(GLenum, GLuint);\ntypedef void (APIENTRY *DELTEXFUNCPTR)(GLsizei, const GLuint *);\ntypedef void (APIENTRY *GENTEXFUNCPTR)(GLsizei, GLuint *);\ntypedef GLboolean (APIENTRY *ISTEXFUNCPTR)(GLuint);\ntypedef void (APIENTRY *PRIORTEXFUNCPTR)(GLsizei, const GLuint *,\n                    const GLclampf *);\ntypedef void (APIENTRY *TEXSUBIMAGEPTR)(int, int, int, int, int, int, int, int, void *);\ntypedef void (APIENTRY *FTEPFNGLCOMPRESSEDTEXIMAGE2DARBPROC)\t(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const GLvoid* data);\ntypedef void (APIENTRY *FTEPFNGLCOMPRESSEDTEXIMAGE3DARBPROC)\t(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void *data);\ntypedef void (APIENTRY *FTEPFNGLGETCOMPRESSEDTEXIMAGEARBPROC)\t(GLenum target, GLint lod, const GLvoid* img);\ntypedef void (APIENTRY *FTEPFNGLPNTRIANGLESIATIPROC)(GLenum pname, GLint param);\ntypedef void (APIENTRY *FTEPFNGLPNTRIANGLESFATIPROC)(GLenum pname, GLfloat param);\ntypedef void (APIENTRY *FTEPFNGLACTIVESTENCILFACEEXTPROC) (GLenum face);\n\ntypedef GLuint\t\t(APIENTRYP FTEPFNGLCREATEPROGRAMPROC)\t\t(void);\ntypedef void\t\t(APIENTRYP FTEPFNGLDELETEPROGRAMPROC)\t\t(GLuint obj);\ntypedef void\t\t(APIENTRYP FTEPFNGLDELETESHADERPROC)\t\t(GLuint obj);\ntypedef void\t\t(APIENTRYP FTEPFNGLUSEPROGRAMPROC)\t\t\t(GLuint programObj);\ntypedef GLuint\t\t(APIENTRYP FTEPFNGLCREATESHADERPROC)\t\t(GLenum shaderType);\ntypedef void\t\t(APIENTRYP FTEPFNGLSHADERSOURCEPROC)\t\t(GLuint shaderObj, GLsizei count, const GLchar* const*string, const GLint *length);\ntypedef void\t\t(APIENTRYP FTEPFNGLCOMPILESHADERPROC)\t\t(GLuint shaderObj);\ntypedef void        (APIENTRYP FTEPFNGLGETPROGRAMIVPROC)\t\t(GLuint obj, GLenum pname, GLint *params);\ntypedef void        (APIENTRYP FTEPFNGLGETSHADERIVPROC)\t\t\t(GLuint obj, GLenum pname, GLint *params);\ntypedef void\t\t(APIENTRYP FTEPFNGLATTACHSHADERPROC)\t\t(GLuint containerObj, GLuint obj);\ntypedef void\t\t(APIENTRYP FTEPFNGLGETPROGRAMINFOLOGPROC)\t(GLuint obj, GLsizei maxLength, GLsizei *length, GLchar *infoLog);\ntypedef void\t\t(APIENTRYP FTEPFNGLGETSHADERINFOLOGPROC)\t(GLuint obj, GLsizei maxLength, GLsizei *length, GLchar *infoLog);\ntypedef void\t\t(APIENTRYP FTEPFNGLLINKPROGRAMPROC)\t\t\t(GLuint programObj);\ntypedef void        (APIENTRYP FTEPFNGLBINDATTRIBLOCATIONPROC)\t(GLuint programObj, GLuint index, const GLchar *name);\ntypedef GLint\t\t(APIENTRYP FTEPFNGLGETATTRIBLOCATIONPROC)\t(GLuint programObj, const GLchar *name);\ntypedef void\t\t(APIENTRYP FTEPFNGLVERTEXATTRIBPOINTERPROC)\t(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid *pointer);\ntypedef void\t\t(APIENTRYP FTEPFNGLVERTEXATTRIB4FPROC)\t\t(GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w);\ntypedef void\t\t(APIENTRYP FTEPFNGLENABLEVERTEXATTRIBARRAYPROC)\t(GLuint index);\ntypedef void\t\t(APIENTRYP FTEPFNGLDISABLEVERTEXATTRIBARRAYPROC)(GLuint index);\ntypedef GLint\t\t(APIENTRYP FTEPFNGLGETUNIFORMLOCATIONPROC)\t(GLuint programObj, const GLchar *name);\ntypedef void\t\t(APIENTRYP FTEPFNGLGETVERTEXATTRIBIVPROC)\t(GLuint index, GLenum pname, GLint *params);\ntypedef void\t\t(APIENTRYP FTEPFNGLUNIFORM4FPROC)\t\t\t(GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3);\ntypedef void\t\t(APIENTRYP FTEPFNGLUNIFORMMATRIX4FVPROC)\t(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);\ntypedef void\t\t(APIENTRYP FTEPFNGLUNIFORMMATRIX3FVPROC)\t(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);\ntypedef void\t\t(APIENTRYP FTEPFNGLUNIFORMMATRIX4X3FVPROC)\t\t(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);\ntypedef void\t\t(APIENTRYP FTEPFNGLUNIFORMMATRIX3X4FVPROC)\t\t(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);\ntypedef void\t\t(APIENTRYP FTEPFNGLUNIFORM4FVPROC)\t\t\t(GLint location, GLsizei count, const GLfloat *value);\ntypedef void\t\t(APIENTRYP FTEPFNGLUNIFORM3FPROC)\t\t\t(GLint location, GLfloat v0, GLfloat v1, GLfloat v2);\ntypedef void\t\t(APIENTRYP FTEPFNGLUNIFORM3FVPROC)\t\t\t(GLint location, GLsizei count, const GLfloat *value);\ntypedef void\t\t(APIENTRYP FTEPFNGLUNIFORM2FVPROC)\t\t\t(GLint location, GLsizei count, const GLfloat *value);\ntypedef void\t\t(APIENTRYP FTEPFNGLUNIFORM1IPROC)\t\t\t(GLint location, GLint v0);\ntypedef void\t\t(APIENTRYP FTEPFNGLUNIFORM1FPROC)\t\t\t(GLint location, GLfloat v0);\ntypedef void\t\t(APIENTRYP FTEPFNGLGETSHADERSOURCEPROC)\t\t(GLuint obj, GLsizei maxLength, GLsizei *length, GLchar *source);\n\ntypedef void (APIENTRY * FTEPFNGLLOCKARRAYSEXTPROC) (GLint first, GLsizei count);\ntypedef void (APIENTRY * FTEPFNGLUNLOCKARRAYSEXTPROC) (void);\n\n#ifndef GL_STATIC\nextern\tBINDTEXFUNCPTR qglBindTexture;\nextern\tDELTEXFUNCPTR delTexFunc;\nextern\tTEXSUBIMAGEPTR TexSubImage2DFunc;\nextern void (APIENTRY *qglStencilOpSeparate) (GLenum face, GLenum fail, GLenum zfail, GLenum zpass);\n#endif\nextern\tFTEPFNGLPNTRIANGLESIATIPROC qglPNTrianglesiATI;\nextern\tFTEPFNGLPNTRIANGLESFATIPROC qglPNTrianglesfATI;\nextern void (APIENTRY *qglPatchParameteriARB)(GLenum pname, GLint value);\t//core in gl4\n\nqboolean GL_CheckExtension(char *extname);\n\nstruct glfmt_s\n{\n\tint sizedformat;\t//texstorage\n\tint cformat;\t\t//sized format used when gl_compress is set\n\tint internalformat;\t//used instead of internal format when gl_compress is set, or 0\n\tint format;\t\t\t//0 for compressed data\n\tint type;\t\t\t//0 for compressed data\n\tint swizzle_r;\n\tint swizzle_g;\n\tint swizzle_b;\n\tint swizzle_a;\n};\n\ntypedef struct {\n\tfloat glversion;\n\tint maxglslversion;\n\tint maxattribs;\t//max generic attributes. probably only 16 on nvidia.\n\tqboolean nofixedfunc;\n\tqboolean gles;\n\tqboolean webgl_ie;\t//workaround ie webgl bugs/omissions.\n\tqboolean blacklist_invariant; //mesa's invariant keyword is broken (for us when combined with fixed function)\n\tqboolean tex_env_combine;\n\tqboolean nv_tex_env_combine4;\n\tqboolean env_add;\n\n//\tqboolean sgis_generate_mipmap;\n\n\tqboolean arb_texture_env_combine;\n\tqboolean arb_texture_env_dot3;\n\n\tqboolean arb_texture_compression;\t//means we support dynamic compression, rather than any specific compressed texture formats\n\tqboolean astc_decodeprecision;\t\t//means we can tell the gpu that our astc textures actually are ldr.\n\n\tqboolean geometryshaders;\n\tqboolean arb_tessellation_shader;\n\n\tqboolean ext_framebuffer_objects;\n\tqboolean arb_framebuffer_srgb;\n//\tqboolean arb_fragment_program;\n\tqboolean arb_shader_objects;\n\tqboolean arb_shadow;\n\tqboolean arb_depth_texture;\n\tqboolean ext_stencil_wrap;\n\tqboolean ext_packed_depth_stencil;\n\tqboolean arb_depth_clamp;\n\tint ext_texture_filter_anisotropic;\n\n\tstruct glfmt_s formatinfo[PTI_MAX];\n\tint unpackalignment;\n} gl_config_t;\n\nextern gl_config_t gl_config;\n\nextern\tfloat\tgldepthmin, gldepthmax;\n\nvoid GL_UpdateFiltering(image_t *imagelist, int filtermip[3], int filterpic[3], int mipcap[2], float lodbias, float anis);\nqboolean GL_LoadTextureMips(texid_t tex, const struct pendingtextureinfo *mips);\nvoid GL_DestroyTexture(texid_t tex);\nvoid GL_SetupFormats(void);\n\n/*\ntypedef struct\n{\n\tfloat\tx, y, z;\n\tfloat\ts, t;\n\tfloat\tr, g, b;\n} glvert_t;\n\nFTE_DEPRECATED extern glvert_t glv;\n*/\n#endif\n\n// r_local.h -- private refresh defs\n\n//#define ALIAS_BASE_SIZE_RATIO\t\t(1.0 / 11.0)\n\t\t\t\t\t// normalizing factor so player model works out to about\n\t\t\t\t\t//  1 pixel per triangle\n//#define\tMAX_LBM_HEIGHT\t\t480\n\n//#define TILE_SIZE\t\t128\t\t// size of textures generated by R_GenTiledSurf\n\n//#define SKYSHIFT\t\t7\n//#define\tSKYSIZE\t\t\t(1 << SKYSHIFT)\n//#define SKYMASK\t\t\t(SKYSIZE - 1)\n\n\nvoid R_TimeRefresh_f (void);\n\n#include \"particles.h\"\n\n//====================================================\n\nextern\tentity_t\tr_worldentity;\nextern\tvec3_t\t\tr_entorigin;\nextern\tentity_t\t*currententity;\n\nextern qboolean\t\tr_loadbumpmapping;\n\n//\n// view origin\n//\nextern\tvec3_t\tvup;\nextern\tvec3_t\tvpn;\nextern\tvec3_t\tvright;\nextern\tvec3_t\tr_origin;\n\n//\n// screen size info\n//\nextern\trefdef_t\tr_refdef;\nextern\tunsigned int r_viewcontents;\nextern\tint r_viewarea;\nextern\tint\t\tr_viewcluster, r_viewcluster2;\nextern\ttexture_t\t*r_notexture_mip;\n\nextern\ttexid_t\tnetgraphtexture;\t// netgraph texture\nextern\tshader_t *netgraphshader;\n\nextern\tconst char *gl_vendor;\nextern\tconst char *gl_renderer;\nextern\tconst char *gl_version;\n\nqboolean R_CullBox (vec3_t mins, vec3_t maxs);\nqboolean R_CullEntityBox(entity_t *e, vec3_t modmins, vec3_t modmaxs);\nqboolean R_CullSphere (vec3_t origin, float radius);\nvoid Sh_PreGenerateLights(void);\nvoid Sh_PurgeShadowMeshes(void);\n\n#ifdef GLQUAKE\nvoid R_TranslatePlayerSkin (int playernum);\nvoid GL_MTBind(int tmu, int target, texid_t texnum); /*use this if you're going to change the texture object (ensures glActiveTexture(tmu))*/\nvoid GL_CullFace(unsigned int sflags);\nvoid GL_TexEnv(GLenum mode);\n\n// Multitexture\n#define    GL_TEXTURE0_SGIS\t\t\t\t0x835E\n#define    GL_TEXTURE1_SGIS\t\t\t\t0x835F\n\n\nextern\tint gl_stencilbits;\n\n\nextern int gl_mtexarbable;\t//max texture units\nextern qboolean gl_mtexable;\n\nextern int mtexid0;\n\nextern qboolean gl_mtexable;\n\nvoid GL_SelectTexture (int tmunum);\nvoid GL_SetShaderState2D(qboolean is2d);\nvoid GL_ForceDepthWritable(void);\n\n#endif\n\n//\n// gl_draw.c\n//\n#ifdef GLQUAKE\ntexid_tf GL_LoadPicTexture (qpic_t *pic);\nvoid GL_Set2D (unsigned int flags);\n#endif\n\n//\n// gl_rmain.c\n//\nqboolean R_ShouldDraw(entity_t *e);\n#ifdef GLQUAKE\nvoid R_RotateForEntity (float *modelmatrix, float *modelviewmatrix, const entity_t *e, const model_t *mod);\n\nvoid GL_ShutdownPostProcessing(void);\nvoid GL_InitSceneProcessingShaders (void);\nvoid GL_SetupSceneProcessingTextures (void);\n#endif\n\n//\n// gl_alias.c\n//\nvoid R_DrawGAliasShadowVolume(entity_t *e, vec3_t lightpos, float radius);\n\n#ifdef GLQUAKE\n//misc model formats\nvoid R_DrawHLModel(entity_t\t*curent);\n#endif\n\n//\n// gl_rlight.c\n//\nvoid R_GenDlightBatches(batch_t *batches[]);\nvoid R_InitFlashblends(void);\n#ifdef GLQUAKE\nvoid GLR_MarkQ2Lights (dlight_t *light, int bit, mnode_t *node);\n#endif\nvoid GLQ3_LightGrid(model_t *mod, const vec3_t point, vec3_t res_diffuse, vec3_t res_ambient, vec3_t res_dir);\nqboolean R_LoadRTLights(void);\nqboolean R_ImportRTLights(const char *entlump, int importmode);\n\n//doom\n#ifdef MAP_DOOM\nvoid R_DoomWorld();\n#endif\n#ifdef MAP_PROC\nqboolean QDECL D3_LoadMap_CollisionMap(model_t *mod, void *buf, size_t bufsize);\n#endif\n\n//gl_bloom.c\n#ifdef GLQUAKE\nvoid R_BloomRegister(void);\nqboolean R_CanBloom(void);\nvoid R_BloomBlend (texid_t source, int x, int y, int w, int h);\nvoid R_BloomShutdown(void);\n#endif\n\n//\n// gl_ngraph.c\n//\nvoid R_NetGraph (void);\n\n\n#if defined(GLQUAKE)\n\n//updates the viewport correctly.\n//pxrect.y is relative to the top.\n//gl requires viewports specified relative to the bottom.\n//so we need to do a little extra maths, which keeps confusing me, so one macro to ensure consistancy.\n#define GL_ViewportUpdate() qglViewport(r_refdef.pxrect.x, r_refdef.pxrect.maxheight-(r_refdef.pxrect.y+r_refdef.pxrect.height), r_refdef.pxrect.width, r_refdef.pxrect.height)\n\n#ifdef GL_STATIC\n//these are the functions that are valid in gles2.\n//other functions should never actually be used.\n#define qglActiveTextureARB glActiveTexture\n#define qglAttachShader glAttachShader\n#define qglBindAttribLocation glBindAttribLocation\n#define qglBindBuffer glBindBuffer\n#define qglBindFramebuffer glBindFramebuffer\n#define qglBindRenderbufferEXT glBindRenderbuffer\n#define qglBindTexture glBindTexture\n#define qglBlendColor glBlendColor\n#define qglBlendEquation glBlendEquation\n#define qglBlendEquationSeparate glBlendEquationSeparate\n#define qglBlendFunc glBlendFunc\n#define qglBlendFuncSeparate glBlendFuncSeparate\n#define qglBufferData glBufferData\n#define qglBufferSubData glBufferSubData\n#define qglCheckFramebufferStatusEXT glCheckFramebufferStatus\n#define qglClear glClear\n#define qglClearColor glClearColor\n#define qglClearDepthf glClearDepthf\n#define qglClearStencil glClearStencil\n#define qglColorMask glColorMask\n#define qglCompileShader glCompileShader\n#define qglCompressedTexImage2D glCompressedTexImage2D\n#define qglCompressedTexSubImage2D glCompressedTexSubImage2D\n#define qglCopyTexImage2D glCopyTexImage2D\n#define qglCopyTexSubImage2D glCopyTexSubImage2D\n#define qglCreateProgram glCreateProgram\n#define qglCreateShader glCreateShader\n#define qglCullFace glCullFace\n#define qglDeleteBuffers glDeleteBuffers\n#define qglDeleteFramebuffers glDeleteFramebuffers\n#define qglDeleteProgram glDeleteProgram\n#define qglDeleteRenderbuffers glDeleteRenderbuffers\n#define qglDeleteShader glDeleteShader\n#define qglDeleteTextures glDeleteTextures\n#define qglDepthFunc glDepthFunc\n#define qglDepthMask glDepthMask\n#define qglDepthRangef glDepthRangef\n#define qglDetachShader glDetachShader\n#define qglDisable glDisable\n#define qglDisableVertexAttribArray glDisableVertexAttribArray\n#define qglDrawArrays glDrawArrays\n#define qglDrawElements glDrawElements\n#define qglEnable glEnable\n#define qglEnableVertexAttribArray glEnableVertexAttribArray\n#define qglFinish glFinish\n#define qglFlush glFlush\n#define qglFramebufferRenderbufferEXT glFramebufferRenderbuffer\n#define qglFramebufferTexture2D glFramebufferTexture2D\n#define qglFrontFace glFrontFace\n#define qglGenBuffers glGenBuffers\n#define qglGenerateMipmap glGenerateMipmap\n#define qglGenFramebuffers glGenFramebuffers\n#define qglGenRenderbuffersEXT glGenRenderbuffers\n#define qglGenTextures glGenTextures\n#define qglGetActiveAttrib glGetActiveAttrib\n#define qglGetActiveUniform glGetActiveUniform\n#define qglGetAttachedShaders glGetAttachedShaders\n#define qglGetAttribLocation glGetAttribLocation\n#define qglGetBooleanv glGetBooleanv\n#define qglGetBufferParameteriv glGetBufferParameteriv\n#define qglGetError glGetError\n#define qglGetFloatv glGetFloatv\n#define qglGetFramebufferAttachmentParameteriv glGetFramebufferAttachmentParameteriv\n#define qglGetIntegerv glGetIntegerv\n#define qglGetProgramiv glGetProgramiv\n#define qglGetProgramInfoLog glGetProgramInfoLog\n#define qglGetRenderbufferParameteriv glGetRenderbufferParameteriv\n#define qglGetShaderiv glGetShaderiv\n#define qglGetShaderInfoLog glGetShaderInfoLog\n#define qglGetShaderPrecisionFormat glGetShaderPrecisionFormat\n#define qglGetShaderSource glGetShaderSource\n#define qglGetString glGetString\n#define qglGetTexParameterfv glGetTexParameterfv\n#define qglGetTexParameteriv glGetTexParameteriv\n#define qglGetUniformfv glGetUniformfv\n#define qglGetUniformiv glGetUniformiv\n#define qglGetUniformLocation glGetUniformLocation\n#define qglGetVertexAttribfv glGetVertexAttribfv\n#define qglGetVertexAttribiv glGetVertexAttribiv\n#define qglGetVertexAttribPointerv glGetVertexAttribPointerv\n#define qglHint glHint\n#define qglIsBuffer glIsBuffer\n#define qglIsEnabled glIsEnabled\n#define qglIsFramebuffer glIsFramebuffer\n#define qglIsProgram glIsProgram\n#define qglIsRenderbuffer glIsRenderbuffer\n#define qglIsShader glIsShader\n#define qglIsTexture glIsTexture\n#define qglLineWidth glLineWidth\n#define qglLinkProgram glLinkProgram\n#define qglPixelStorei glPixelStorei\n#define qglPolygonOffset glPolygonOffset\n#define qglReadPixels glReadPixels\n#define qglReleaseShaderCompiler glReleaseShaderCompiler\n#define qglRenderbufferStorageEXT glRenderbufferStorage\n#define qglSampleCoverage glSampleCoverage\n#define qglScissor glScissor\n#define qglShaderBinary glShaderBinary\n#define qglShaderSource glShaderSource\n#define qglStencilFunc glStencilFunc\n#define qglStencilFuncSeparate glStencilFuncSeparate\n#define qglStencilMask glStencilMask\n#define qglStencilMaskSeparate glStencilMaskSeparate\n#define qglStencilOp glStencilOp\n#define qglStencilOpSeparate glStencilOpSeparate\n#define qglTexImage2D glTexImage2D\n#define qglTexParameterf glTexParameterf\n#define qglTexParameterfv glTexParameterfv\n#define qglTexParameteri glTexParameteri\n#define qglTexParameteriv glTexParameteriv\n#define qglTexSubImage2D glTexSubImage2D\n#define qglUniform1f glUniform1f\n#define qglUniform1fv glUniform1fv\n#define qglUniform1i glUniform1i\n#define qglUniform1iv glUniform1iv\n#define qglUniform2f glUniform2f\n#define qglUniform2fv glUniform2fv\n#define qglUniform2i glUniform2i\n#define qglUniform2iv glUniform2iv\n#define qglUniform3f glUniform3f\n#define qglUniform3fv glUniform3fv\n#define qglUniform3i glUniform3i\n#define qglUniform3iv glUniform3iv\n#define qglUniform4f glUniform4f\n#define qglUniform4fv glUniform4fv\n#define qglUniform4i glUniform4i\n#define qglUniform4iv glUniform4iv\n#define qglUniformMatrix2fv glUniformMatrix2fv\n#define qglUniformMatrix3fv glUniformMatrix3fv\n#define qglUniformMatrix4fv glUniformMatrix4fv\n#define qglUseProgram glUseProgram\n#define qglValidateProgram glValidateProgram\n#define qglVertexAttrib1f glVertexAttrib1f\n#define qglVertexAttrib1fv glVertexAttrib1fv\n#define qglVertexAttrib2f glVertexAttrib2f\n#define qglVertexAttrib2fv glVertexAttrib2fv\n#define qglVertexAttrib3f glVertexAttrib3f\n#define qglVertexAttrib3fv glVertexAttrib3fv\n#define qglVertexAttrib4f glVertexAttrib4f\n#define qglVertexAttrib4fv glVertexAttrib4fv\n#define qglVertexAttribPointer glVertexAttribPointer\n#define qglViewport glViewport\n\n#define qglGenFramebuffersEXT qglGenFramebuffers\n#define qglDeleteFramebuffersEXT qglDeleteFramebuffers\n#define qglBindFramebufferEXT qglBindFramebuffer\n#define qglFramebufferTexture2DEXT qglFramebufferTexture2D\n#define qglDeleteRenderbuffersEXT qglDeleteRenderbuffers\n//#define qglCompressedTexImage2DARB qglCompressedTexImage2D\n\n#define qglCreateProgramObjectARB\tglCreateProgram\n#define qglDeleteProgramObject_\t\tglDeleteProgram\n#define qglDeleteShaderObject_\t\tglDeleteShader\n#define qglUseProgramObjectARB\t\tglUseProgram\n#define qglCreateShaderObjectARB\tglCreateShader\n#define qglShaderSourceARB\t\tglShaderSource\n#define qglCompileShaderARB\t\tglCompileShader\n#define qglGetProgramParameteriv_\tglGetProgramiv\n#define qglGetShaderParameteriv_\tglGetShaderiv\n#define qglAttachObjectARB\t\tglAttachShader\n#define qglGetProgramInfoLog_\t\tglGetProgramInfoLog\n#define qglGetShaderInfoLog_\t\tglGetShaderInfoLog\n#define qglLinkProgramARB\t\tglLinkProgram\n#define qglBindAttribLocationARB\tglBindAttribLocation\n#define qglGetAttribLocationARB\t\tglGetAttribLocation\n#define qglGetUniformLocationARB\tglGetUniformLocation\n#define qglUniformMatrix4fvARB\t\tglUniformMatrix4fv\n#define qglUniformMatrix3fvARB\t\tglUniformMatrix3fv\n#define qglUniform4fARB\t\t\tglUniform4f\n#define qglUniform4fvARB\t\tglUniform4fv\n#define qglUniform3fARB\t\t\tglUniform3f\n#define qglUniform3fvARB\t\tglUniform3fv\n#define qglUniform2fvARB\t\tglUniform2fv\n#define qglUniform1iARB\t\t\tglUniform1i\n#define qglUniform1fARB\t\t\tglUniform1f\n\n#define qglGenBuffersARB\t\tglGenBuffers\n#define qglDeleteBuffersARB\t\tglDeleteBuffers\n#define qglBindBufferARB\t\tglBindBuffer\n#define qglBufferDataARB\t\tglBufferData\n#define qglBufferSubDataARB\t\tglBufferSubData\n\n#else\nextern void (APIENTRY *qglBindTexture) (GLenum target, GLuint texture);\nextern void (APIENTRY *qglBlendFunc) (GLenum sfactor, GLenum dfactor);\nextern void (APIENTRY *qglClear) (GLbitfield mask);\nextern void (APIENTRY *qglClearColor) (GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha);\nextern void (APIENTRY *qglClearDepthf) (GLclampf depth);\nextern void (APIENTRY *qglClearStencil) (GLint s);\nextern void (APIENTRY *qglColorMask) (GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha);\nextern void (APIENTRY *qglCopyTexImage2D) (GLenum target, GLint level, GLenum internalFormat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border);\nextern void (APIENTRY *qglCopyTexSubImage2D) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height);\nextern void (APIENTRY *qglCullFace) (GLenum mode);\nextern void (APIENTRY *qglDeleteTextures) (GLsizei n, const GLuint *textures);\nextern void (APIENTRY *qglDepthFunc) (GLenum func);\nextern void (APIENTRY *qglDepthMask) (GLboolean flag);\nextern void (APIENTRY *qglDepthRangef) (GLclampf zNear, GLclampf zFar);\nextern void (APIENTRY *qglDisable) (GLenum cap);\nextern void (APIENTRY *qglDrawArrays) (GLenum mode, GLint first, GLsizei count);\nextern void (APIENTRY *qglDrawElements) (GLenum mode, GLsizei count, GLenum type, const GLvoid *indices);\nextern void (APIENTRY *qglEnable) (GLenum cap);\nextern void (APIENTRY *qglFinish) (void);\nextern void (APIENTRY *qglFlush) (void);\nextern void (APIENTRY *qglFrontFace) (GLenum mode);\nextern void (APIENTRY *qglGenTextures) (GLsizei n, GLuint *textures);\nextern void (APIENTRY *qglGenerateMipmap)(GLenum target);\nextern void (APIENTRY *qglGetBooleanv) (GLenum pname, GLboolean *params);\nextern GLenum (APIENTRY *qglGetError) (void);\nextern void (APIENTRY *qglGetFloatv) (GLenum pname, GLfloat *params);\nextern void (APIENTRY *qglGetIntegerv) (GLenum pname, GLint *params);\nextern const GLubyte * (APIENTRY *qglGetString) (GLenum name);\nextern void (APIENTRY *qglGetTexParameterfv) (GLenum target, GLenum pname, GLfloat *params);\nextern void (APIENTRY *qglGetTexParameteriv) (GLenum target, GLenum pname, GLint *params);\nextern void (APIENTRY *qglHint) (GLenum target, GLenum mode);\nextern GLboolean (APIENTRY *qglIsEnabled) (GLenum cap);\nextern GLboolean (APIENTRY *qglIsTexture) (GLuint texture);\nextern void (APIENTRY *qglLineWidth) (GLfloat width);\nextern void (APIENTRY *qglPixelStorei) (GLenum pname, GLint param);\nextern void (APIENTRY *qglPolygonOffset) (GLfloat factor, GLfloat units);\nextern void (APIENTRY *qglReadPixels) (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid *pixels);\nextern void (APIENTRY *qglScissor) (GLint x, GLint y, GLsizei width, GLsizei height);\nextern void (APIENTRY *qglStencilFunc) (GLenum func, GLint ref, GLuint mask);\nextern void (APIENTRY *qglStencilMask) (GLuint mask);\nextern void (APIENTRY *qglStencilOp) (GLenum fail, GLenum zfail, GLenum zpass);\nextern void (APIENTRY *qglTexImage2D) (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid *pixels);\nextern void (APIENTRY *qglTexParameterf) (GLenum target, GLenum pname, GLfloat param);\nextern void (APIENTRY *qglTexParameterfv) (GLenum target, GLenum pname, const GLfloat *params);\nextern void (APIENTRY *qglTexParameteri) (GLenum target, GLenum pname, GLint param);\nextern void (APIENTRY *qglTexParameteriv) (GLenum target, GLenum pname, const GLint *params);\nextern void (APIENTRY *qglTexSubImage2D) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels);\nextern void (APIENTRY *qglViewport) (GLint x, GLint y, GLsizei width, GLsizei height);\nextern FTEPFNGLCOMPRESSEDTEXIMAGE2DARBPROC qglCompressedTexImage2D;\nextern void (APIENTRY *qglCompressedTexSubImage2D) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *data);\t//gl1.3\n\nextern void (APIENTRY *qglGenFramebuffersEXT)(GLsizei n, GLuint* ids);\nextern void (APIENTRY *qglDeleteFramebuffersEXT)(GLsizei n, const GLuint* ids);\nextern void (APIENTRY *qglBindFramebufferEXT)(GLenum target, GLuint id);\nextern void (APIENTRY *qglGenRenderbuffersEXT)(GLsizei n, GLuint* ids);\nextern void (APIENTRY *qglDeleteRenderbuffersEXT)(GLsizei n, const GLuint* ids);\nextern void (APIENTRY *qglBindRenderbufferEXT)(GLenum target, GLuint id);\nextern void (APIENTRY *qglRenderbufferStorageEXT)(GLenum target, GLenum internalFormat, GLsizei width, GLsizei height);\nextern void (APIENTRY *qglFramebufferTexture2DEXT)(GLenum target, GLenum attachmentPoint, GLenum textureTarget, GLuint textureId, GLint  level);\nextern void (APIENTRY *qglFramebufferRenderbufferEXT)(GLenum target, GLenum attachmentPoint, GLenum textureTarget, GLuint textureId);\nextern GLenum (APIENTRY *qglCheckFramebufferStatusEXT)(GLenum target);\nextern void (APIENTRY *qglGetFramebufferAttachmentParameteriv)(GLenum  target,  GLenum  attachment,  GLenum  pname,  GLint * params);\n\n//glslang - arb_shader_objects\nextern FTEPFNGLCREATEPROGRAMPROC\t\t\tqglCreateProgramObjectARB;\nextern FTEPFNGLDELETEPROGRAMPROC\t\t\tqglDeleteProgramObject_;\nextern FTEPFNGLDELETESHADERPROC\t\t\t\tqglDeleteShaderObject_;\nextern FTEPFNGLUSEPROGRAMPROC\t\t\t\tqglUseProgramObjectARB;\nextern FTEPFNGLCREATESHADERPROC\t\t\t\tqglCreateShaderObjectARB;\nextern FTEPFNGLSHADERSOURCEPROC\t\t\t\tqglShaderSourceARB;\nextern FTEPFNGLCOMPILESHADERPROC\t\t\tqglCompileShaderARB;\nextern FTEPFNGLGETPROGRAMIVPROC\t\t\t\tqglGetProgramParameteriv_;\nextern FTEPFNGLGETSHADERIVPROC\t\t\t\tqglGetShaderParameteriv_;\nextern FTEPFNGLATTACHSHADERPROC\t\t\t\tqglAttachObjectARB;\nextern FTEPFNGLGETPROGRAMINFOLOGPROC\t\tqglGetProgramInfoLog_;\nextern FTEPFNGLGETSHADERINFOLOGPROC\t\t\tqglGetShaderInfoLog_;\nextern FTEPFNGLLINKPROGRAMPROC\t\t\t\tqglLinkProgramARB;\nextern FTEPFNGLBINDATTRIBLOCATIONPROC\t\tqglBindAttribLocationARB;\nextern FTEPFNGLGETATTRIBLOCATIONPROC\t\tqglGetAttribLocationARB;\nextern FTEPFNGLGETUNIFORMLOCATIONPROC\t\tqglGetUniformLocationARB;\nextern FTEPFNGLUNIFORMMATRIX4FVPROC\t\t\tqglUniformMatrix4fvARB;\nextern FTEPFNGLUNIFORMMATRIX3FVPROC\t\t\tqglUniformMatrix3fvARB;\nextern FTEPFNGLUNIFORMMATRIX4X3FVPROC\t\tqglUniformMatrix4x3fvARB;\t//gl2.1+\nextern FTEPFNGLUNIFORMMATRIX3X4FVPROC\t\tqglUniformMatrix3x4fvARB;\t//gl2.1+\nextern FTEPFNGLUNIFORM4FPROC\t\t\t\tqglUniform4fARB;\nextern FTEPFNGLUNIFORM4FVPROC\t\t\t\tqglUniform4fvARB;\nextern FTEPFNGLUNIFORM3FPROC\t\t\t\tqglUniform3fARB;\nextern FTEPFNGLUNIFORM3FVPROC\t\t\t\tqglUniform3fvARB;\nextern FTEPFNGLUNIFORM2FVPROC\t\t\t\tqglUniform2fvARB;\nextern FTEPFNGLUNIFORM1IPROC\t\t\t\tqglUniform1iARB;\nextern FTEPFNGLUNIFORM1FPROC\t\t\t\tqglUniform1fARB;\nextern FTEPFNGLVERTEXATTRIB4FPROC\t\t\tqglVertexAttrib4f;\nextern FTEPFNGLVERTEXATTRIBPOINTERPROC\t\tqglVertexAttribPointer;\nextern FTEPFNGLGETVERTEXATTRIBIVPROC\t\tqglGetVertexAttribiv;\nextern FTEPFNGLENABLEVERTEXATTRIBARRAYPROC\tqglEnableVertexAttribArray;\nextern FTEPFNGLDISABLEVERTEXATTRIBARRAYPROC\tqglDisableVertexAttribArray;\n\nextern void (APIENTRY *qglGenBuffersARB)(GLsizei n, GLuint* ids);\nextern void (APIENTRY *qglDeleteBuffersARB)(GLsizei n, GLuint* ids);\nextern void (APIENTRY *qglBindBufferARB)(GLenum target, GLuint id);\nextern void (APIENTRY *qglBufferDataARB)(GLenum target, GLsizei size, const void* data, GLenum usage);\nextern void (APIENTRY *qglBufferSubDataARB)(GLenum target, GLint offset, GLsizei size, void* data);\n#endif\n\n#define GLintptr qintptr_t\n#define GLsizeiptr quintptr_t\n#ifndef GL_MAP_READ_BIT\n#define GL_MAP_READ_BIT 0x0001\n#endif\n#ifndef GL_MAP_WRITE_BIT\n#define GL_MAP_WRITE_BIT 0x0002\n#endif\n#ifndef GL_MAP_PERSISTENT_BIT\n#define GL_MAP_PERSISTENT_BIT 0x0040\n#endif\n#ifndef GL_MAP_COHERENT_BIT\n#define GL_MAP_COHERENT_BIT 0x0080\n#endif\nextern void *(APIENTRY *qglMapBufferARB)(GLenum target, GLenum access);\nextern GLboolean (APIENTRY *qglUnmapBufferARB)(GLenum target);\nextern void *(APIENTRY *qglMapBufferRange)(GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access);\t//gl3.0\nextern void (APIENTRY *qglBufferStorage)(GLenum target, GLsizeiptr size, const GLvoid *data, GLbitfield flags);\t\t//gl4.4\n\nextern void (APIENTRY *qglGenQueriesARB)(GLsizei n, GLuint *ids);\nextern void (APIENTRY *qglDeleteQueriesARB)(GLsizei n, const GLuint *ids);\n//extern GLboolean (APIENTRY *qglIsQueryARB)(GLuint id);\nextern void (APIENTRY *qglBeginQueryARB)(GLenum target, GLuint id);\nextern void (APIENTRY *qglEndQueryARB)(GLenum target);\n//extern void (APIENTRY *qglGetQueryivARB)(GLenum target, GLenum pname, GLint *params);\n//extern void (APIENTRY *qglGetQueryObjectivARB)(GLuint id, GLenum pname, GLint *params);\nextern void (APIENTRY *qglGetQueryObjectuivARB)(GLuint id, GLenum pname, GLuint *params);\n\nextern void (APIENTRY *qglDrawBuffers)(GLsizei n, GLsizei *ids);\t//gl2\n\nextern GLenum (APIENTRY *qglGetGraphicsResetStatus) (void);\n\n//non-gles2 gl functions\nextern void (APIENTRY *qglAccum) (GLenum op, GLfloat value);\nextern void (APIENTRY *qglAlphaFunc) (GLenum func, GLclampf ref);\nextern GLboolean (APIENTRY *qglAreTexturesResident) (GLsizei n, const GLuint *textures, GLboolean *residences);\nextern void (APIENTRY *qglArrayElement) (GLint i);\nextern void (APIENTRY *qglBitmap) (GLsizei width, GLsizei height, GLfloat xorig, GLfloat yorig, GLfloat xmove, GLfloat ymove, const GLubyte *bitmap);\nextern void (APIENTRY *qglCallList) (GLuint list);\nextern void (APIENTRY *qglCallLists) (GLsizei n, GLenum type, const GLvoid *lists);\nextern void (APIENTRY *qglClearAccum) (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha);\nextern void (APIENTRY *qglClearDepth) (GLclampd depth);\nextern void (APIENTRY *qglClearIndex) (GLfloat c);\nextern void (APIENTRY *qglClipPlane) (GLenum plane, const GLdouble *equation);\nextern void (APIENTRY *qglColor3b) (GLbyte red, GLbyte green, GLbyte blue);\nextern void (APIENTRY *qglColor3bv) (const GLbyte *v);\nextern void (APIENTRY *qglColor3d) (GLdouble red, GLdouble green, GLdouble blue);\nextern void (APIENTRY *qglColor3dv) (const GLdouble *v);\nextern void (APIENTRY *qglColor3f) (GLfloat red, GLfloat green, GLfloat blue);\nextern void (APIENTRY *qglColor3fv) (const GLfloat *v);\nextern void (APIENTRY *qglColor3i) (GLint red, GLint green, GLint blue);\nextern void (APIENTRY *qglColor3iv) (const GLint *v);\nextern void (APIENTRY *qglColor3s) (GLshort red, GLshort green, GLshort blue);\nextern void (APIENTRY *qglColor3sv) (const GLshort *v);\nextern void (APIENTRY *qglColor3ub) (GLubyte red, GLubyte green, GLubyte blue);\nextern void (APIENTRY *qglColor3ubv) (const GLubyte *v);\nextern void (APIENTRY *qglColor3ui) (GLuint red, GLuint green, GLuint blue);\nextern void (APIENTRY *qglColor3uiv) (const GLuint *v);\nextern void (APIENTRY *qglColor3us) (GLushort red, GLushort green, GLushort blue);\nextern void (APIENTRY *qglColor3usv) (const GLushort *v);\nextern void (APIENTRY *qglColor4b) (GLbyte red, GLbyte green, GLbyte blue, GLbyte alpha);\nextern void (APIENTRY *qglColor4bv) (const GLbyte *v);\nextern void (APIENTRY *qglColor4d) (GLdouble red, GLdouble green, GLdouble blue, GLdouble alpha);\nextern void (APIENTRY *qglColor4dv) (const GLdouble *v);\nextern void (APIENTRY *qglColor4f) (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha);\nextern void (APIENTRY *qglColor4fv) (const GLfloat *v);\nextern void (APIENTRY *qglColor4i) (GLint red, GLint green, GLint blue, GLint alpha);\nextern void (APIENTRY *qglColor4iv) (const GLint *v);\nextern void (APIENTRY *qglColor4s) (GLshort red, GLshort green, GLshort blue, GLshort alpha);\nextern void (APIENTRY *qglColor4sv) (const GLshort *v);\nextern void (APIENTRY *qglColor4ub) (GLubyte red, GLubyte green, GLubyte blue, GLubyte alpha);\nextern void (APIENTRY *qglColor4ubv) (const GLubyte *v);\nextern void (APIENTRY *qglColor4ui) (GLuint red, GLuint green, GLuint blue, GLuint alpha);\nextern void (APIENTRY *qglColor4uiv) (const GLuint *v);\nextern void (APIENTRY *qglColor4us) (GLushort red, GLushort green, GLushort blue, GLushort alpha);\nextern void (APIENTRY *qglColor4usv) (const GLushort *v);\nextern void (APIENTRY *qglColorMaterial) (GLenum face, GLenum mode);\nextern void (APIENTRY *qglColorPointer) (GLint size, GLenum type, GLsizei stride, const GLvoid *pointer);\nextern void (APIENTRY *qglCopyPixels) (GLint x, GLint y, GLsizei width, GLsizei height, GLenum type);\nextern void (APIENTRY *qglCopyTexImage1D) (GLenum target, GLint level, GLenum internalFormat, GLint x, GLint y, GLsizei width, GLint border);\nextern void (APIENTRY *qglCopyTexSubImage1D) (GLenum target, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width);\nextern void (APIENTRY *qglDeleteLists) (GLuint list, GLsizei range);\nextern void (APIENTRY *qglDepthRange) (GLclampd zNear, GLclampd zFar);\nextern void (APIENTRY *qglDisableClientState) (GLenum array);\nextern void (APIENTRY *qglDrawBuffer) (GLenum mode);\nextern void (APIENTRY *qglDrawPixels) (GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels);\nextern void (APIENTRY *qglEdgeFlag) (GLboolean flag);\nextern void (APIENTRY *qglEdgeFlagPointer) (GLsizei stride, const GLvoid *pointer);\nextern void (APIENTRY *qglEdgeFlagv) (const GLboolean *flag);\nextern void (APIENTRY *qglEndList) (void);\nextern void (APIENTRY *qglEvalCoord1d) (GLdouble u);\nextern void (APIENTRY *qglEvalCoord1dv) (const GLdouble *u);\nextern void (APIENTRY *qglEvalCoord1f) (GLfloat u);\nextern void (APIENTRY *qglEvalCoord1fv) (const GLfloat *u);\nextern void (APIENTRY *qglEvalCoord2d) (GLdouble u, GLdouble v);\nextern void (APIENTRY *qglEvalCoord2dv) (const GLdouble *u);\nextern void (APIENTRY *qglEvalCoord2f) (GLfloat u, GLfloat v);\nextern void (APIENTRY *qglEvalCoord2fv) (const GLfloat *u);\nextern void (APIENTRY *qglEvalMesh1) (GLenum mode, GLint i1, GLint i2);\nextern void (APIENTRY *qglEvalMesh2) (GLenum mode, GLint i1, GLint i2, GLint j1, GLint j2);\nextern void (APIENTRY *qglEvalPoint1) (GLint i);\nextern void (APIENTRY *qglEvalPoint2) (GLint i, GLint j);\nextern void (APIENTRY *qglFeedbackBuffer) (GLsizei size, GLenum type, GLfloat *buffer);\nextern void (APIENTRY *qglFogf) (GLenum pname, GLfloat param);\nextern void (APIENTRY *qglFogfv) (GLenum pname, const GLfloat *params);\nextern void (APIENTRY *qglFogi) (GLenum pname, GLint param);\nextern void (APIENTRY *qglFogiv) (GLenum pname, const GLint *params);\nextern void (APIENTRY *qglFrustum) (GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar);\nextern GLuint (APIENTRY *qglGenLists) (GLsizei range);\nextern void (APIENTRY *qglGetClipPlane) (GLenum plane, GLdouble *equation);\nextern void (APIENTRY *qglGetDoublev) (GLenum pname, GLdouble *params);\nextern void (APIENTRY *qglGetLightfv) (GLenum light, GLenum pname, GLfloat *params);\nextern void (APIENTRY *qglGetLightiv) (GLenum light, GLenum pname, GLint *params);\nextern void (APIENTRY *qglGetMapdv) (GLenum target, GLenum query, GLdouble *v);\nextern void (APIENTRY *qglGetMapfv) (GLenum target, GLenum query, GLfloat *v);\nextern void (APIENTRY *qglGetMapiv) (GLenum target, GLenum query, GLint *v);\nextern void (APIENTRY *qglGetMaterialfv) (GLenum face, GLenum pname, GLfloat *params);\nextern void (APIENTRY *qglGetMaterialiv) (GLenum face, GLenum pname, GLint *params);\nextern void (APIENTRY *qglGetPixelMapfv) (GLenum map, GLfloat *values);\nextern void (APIENTRY *qglGetPixelMapuiv) (GLenum map, GLuint *values);\nextern void (APIENTRY *qglGetPixelMapusv) (GLenum map, GLushort *values);\nextern void (APIENTRY *qglGetPointerv) (GLenum pname, GLvoid* *params);\nextern void (APIENTRY *qglGetPolygonStipple) (GLubyte *mask);\nextern void (APIENTRY *qglGetTexEnvfv) (GLenum target, GLenum pname, GLfloat *params);\nextern void (APIENTRY *qglGetTexEnviv) (GLenum target, GLenum pname, GLint *params);\nextern void (APIENTRY *qglGetTexGendv) (GLenum coord, GLenum pname, GLdouble *params);\nextern void (APIENTRY *qglGetTexGenfv) (GLenum coord, GLenum pname, GLfloat *params);\nextern void (APIENTRY *qglGetTexGeniv) (GLenum coord, GLenum pname, GLint *params);\nextern void (APIENTRY *qglGetTexImage) (GLenum target, GLint level, GLenum format, GLenum type, GLvoid *pixels);\nextern void (APIENTRY *qglGetTexLevelParameterfv) (GLenum target, GLint level, GLenum pname, GLfloat *params);\nextern void (APIENTRY *qglGetTexLevelParameteriv) (GLenum target, GLint level, GLenum pname, GLint *params);\nextern void (APIENTRY *qglIndexMask) (GLuint mask);\nextern void (APIENTRY *qglIndexPointer) (GLenum type, GLsizei stride, const GLvoid *pointer);\nextern void (APIENTRY *qglIndexd) (GLdouble c);\nextern void (APIENTRY *qglIndexdv) (const GLdouble *c);\nextern void (APIENTRY *qglIndexf) (GLfloat c);\nextern void (APIENTRY *qglIndexfv) (const GLfloat *c);\nextern void (APIENTRY *qglIndexi) (GLint c);\nextern void (APIENTRY *qglIndexiv) (const GLint *c);\nextern void (APIENTRY *qglIndexs) (GLshort c);\nextern void (APIENTRY *qglIndexsv) (const GLshort *c);\nextern void (APIENTRY *qglIndexub) (GLubyte c);\nextern void (APIENTRY *qglIndexubv) (const GLubyte *c);\nextern void (APIENTRY *qglInitNames) (void);\nextern void (APIENTRY *qglInterleavedArrays) (GLenum format, GLsizei stride, const GLvoid *pointer);\nextern GLboolean (APIENTRY *qglIsList) (GLuint list);\nextern void (APIENTRY *qglLightModelf) (GLenum pname, GLfloat param);\nextern void (APIENTRY *qglLightModelfv) (GLenum pname, const GLfloat *params);\nextern void (APIENTRY *qglLightModeli) (GLenum pname, GLint param);\nextern void (APIENTRY *qglLightModeliv) (GLenum pname, const GLint *params);\nextern void (APIENTRY *qglLightf) (GLenum light, GLenum pname, GLfloat param);\nextern void (APIENTRY *qglLightfv) (GLenum light, GLenum pname, const GLfloat *params);\nextern void (APIENTRY *qglLighti) (GLenum light, GLenum pname, GLint param);\nextern void (APIENTRY *qglLightiv) (GLenum light, GLenum pname, const GLint *params);\nextern void (APIENTRY *qglLineStipple) (GLint factor, GLushort pattern);\nextern void (APIENTRY *qglListBase) (GLuint base);\nextern void (APIENTRY *qglLoadIdentity) (void);\nextern void (APIENTRY *qglLoadMatrixd) (const GLdouble *m);\nextern void (APIENTRY *qglLoadMatrixf) (const GLfloat *m);\nextern void (APIENTRY *qglLoadName) (GLuint name);\nextern void (APIENTRY *qglLogicOp) (GLenum opcode);\nextern void (APIENTRY *qglMap1d) (GLenum target, GLdouble u1, GLdouble u2, GLint stride, GLint order, const GLdouble *points);\nextern void (APIENTRY *qglMap1f) (GLenum target, GLfloat u1, GLfloat u2, GLint stride, GLint order, const GLfloat *points);\nextern void (APIENTRY *qglMap2d) (GLenum target, GLdouble u1, GLdouble u2, GLint ustride, GLint uorder, GLdouble v1, GLdouble v2, GLint vstride, GLint vorder, const GLdouble *points);\nextern void (APIENTRY *qglMap2f) (GLenum target, GLfloat u1, GLfloat u2, GLint ustride, GLint uorder, GLfloat v1, GLfloat v2, GLint vstride, GLint vorder, const GLfloat *points);\nextern void (APIENTRY *qglMapGrid1d) (GLint un, GLdouble u1, GLdouble u2);\nextern void (APIENTRY *qglMapGrid1f) (GLint un, GLfloat u1, GLfloat u2);\nextern void (APIENTRY *qglMapGrid2d) (GLint un, GLdouble u1, GLdouble u2, GLint vn, GLdouble v1, GLdouble v2);\nextern void (APIENTRY *qglMapGrid2f) (GLint un, GLfloat u1, GLfloat u2, GLint vn, GLfloat v1, GLfloat v2);\nextern void (APIENTRY *qglMaterialf) (GLenum face, GLenum pname, GLfloat param);\nextern void (APIENTRY *qglMaterialfv) (GLenum face, GLenum pname, const GLfloat *params);\nextern void (APIENTRY *qglMateriali) (GLenum face, GLenum pname, GLint param);\nextern void (APIENTRY *qglMaterialiv) (GLenum face, GLenum pname, const GLint *params);\nextern void (APIENTRY *qglMatrixMode) (GLenum mode);\nextern void (APIENTRY *qglMultMatrixd) (const GLdouble *m);\nextern void (APIENTRY *qglMultMatrixf) (const GLfloat *m);\nextern void (APIENTRY *qglNewList) (GLuint list, GLenum mode);\nextern void (APIENTRY *qglNormal3b) (GLbyte nx, GLbyte ny, GLbyte nz);\nextern void (APIENTRY *qglNormal3bv) (const GLbyte *v);\nextern void (APIENTRY *qglNormal3d) (GLdouble nx, GLdouble ny, GLdouble nz);\nextern void (APIENTRY *qglNormal3dv) (const GLdouble *v);\nextern void (APIENTRY *qglNormal3f) (GLfloat nx, GLfloat ny, GLfloat nz);\nextern void (APIENTRY *qglNormal3fv) (const GLfloat *v);\nextern void (APIENTRY *qglNormal3i) (GLint nx, GLint ny, GLint nz);\nextern void (APIENTRY *qglNormal3iv) (const GLint *v);\nextern void (APIENTRY *qglNormal3s) (GLshort nx, GLshort ny, GLshort nz);\nextern void (APIENTRY *qglNormal3sv) (const GLshort *v);\nextern void (APIENTRY *qglNormalPointer) (GLenum type, GLsizei stride, const GLvoid *pointer);\n//extern void (APIENTRY *qglOrtho) (GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar);\nextern void (APIENTRY *qglPassThrough) (GLfloat token);\nextern void (APIENTRY *qglPixelMapfv) (GLenum map, GLsizei mapsize, const GLfloat *values);\nextern void (APIENTRY *qglPixelMapuiv) (GLenum map, GLsizei mapsize, const GLuint *values);\nextern void (APIENTRY *qglPixelMapusv) (GLenum map, GLsizei mapsize, const GLushort *values);\nextern void (APIENTRY *qglPixelStoref) (GLenum pname, GLfloat param);\nextern void (APIENTRY *qglPixelTransferf) (GLenum pname, GLfloat param);\nextern void (APIENTRY *qglPixelTransferi) (GLenum pname, GLint param);\nextern void (APIENTRY *qglPixelZoom) (GLfloat xfactor, GLfloat yfactor);\nextern void (APIENTRY *qglPointSize) (GLfloat size);\nextern void (APIENTRY *qglPolygonMode) (GLenum face, GLenum mode);\nextern void (APIENTRY *qglPolygonStipple) (const GLubyte *mask);\nextern void (APIENTRY *qglPopAttrib) (void);\nextern void (APIENTRY *qglPopClientAttrib) (void);\nextern void (APIENTRY *qglPopMatrix) (void);\nextern void (APIENTRY *qglPopName) (void);\nextern void (APIENTRY *qglPrioritizeTextures) (GLsizei n, const GLuint *textures, const GLclampf *priorities);\nextern void (APIENTRY *qglPushAttrib) (GLbitfield mask);\nextern void (APIENTRY *qglPushClientAttrib) (GLbitfield mask);\nextern void (APIENTRY *qglPushMatrix) (void);\nextern void (APIENTRY *qglPushName) (GLuint name);\nextern void (APIENTRY *qglRasterPos2d) (GLdouble x, GLdouble y);\nextern void (APIENTRY *qglRasterPos2dv) (const GLdouble *v);\nextern void (APIENTRY *qglRasterPos2f) (GLfloat x, GLfloat y);\nextern void (APIENTRY *qglRasterPos2fv) (const GLfloat *v);\nextern void (APIENTRY *qglRasterPos2i) (GLint x, GLint y);\nextern void (APIENTRY *qglRasterPos2iv) (const GLint *v);\nextern void (APIENTRY *qglRasterPos2s) (GLshort x, GLshort y);\nextern void (APIENTRY *qglRasterPos2sv) (const GLshort *v);\nextern void (APIENTRY *qglRasterPos3d) (GLdouble x, GLdouble y, GLdouble z);\nextern void (APIENTRY *qglRasterPos3dv) (const GLdouble *v);\nextern void (APIENTRY *qglRasterPos3f) (GLfloat x, GLfloat y, GLfloat z);\nextern void (APIENTRY *qglRasterPos3fv) (const GLfloat *v);\nextern void (APIENTRY *qglRasterPos3i) (GLint x, GLint y, GLint z);\nextern void (APIENTRY *qglRasterPos3iv) (const GLint *v);\nextern void (APIENTRY *qglRasterPos3s) (GLshort x, GLshort y, GLshort z);\nextern void (APIENTRY *qglRasterPos3sv) (const GLshort *v);\nextern void (APIENTRY *qglRasterPos4d) (GLdouble x, GLdouble y, GLdouble z, GLdouble w);\nextern void (APIENTRY *qglRasterPos4dv) (const GLdouble *v);\nextern void (APIENTRY *qglRasterPos4f) (GLfloat x, GLfloat y, GLfloat z, GLfloat w);\nextern void (APIENTRY *qglRasterPos4fv) (const GLfloat *v);\nextern void (APIENTRY *qglRasterPos4i) (GLint x, GLint y, GLint z, GLint w);\nextern void (APIENTRY *qglRasterPos4iv) (const GLint *v);\nextern void (APIENTRY *qglRasterPos4s) (GLshort x, GLshort y, GLshort z, GLshort w);\nextern void (APIENTRY *qglRasterPos4sv) (const GLshort *v);\nextern void (APIENTRY *qglRectd) (GLdouble x1, GLdouble y1, GLdouble x2, GLdouble y2);\nextern void (APIENTRY *qglRectdv) (const GLdouble *v1, const GLdouble *v2);\nextern void (APIENTRY *qglRectf) (GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2);\nextern void (APIENTRY *qglRectfv) (const GLfloat *v1, const GLfloat *v2);\nextern void (APIENTRY *qglRecti) (GLint x1, GLint y1, GLint x2, GLint y2);\nextern void (APIENTRY *qglRectiv) (const GLint *v1, const GLint *v2);\nextern void (APIENTRY *qglRects) (GLshort x1, GLshort y1, GLshort x2, GLshort y2);\nextern void (APIENTRY *qglRectsv) (const GLshort *v1, const GLshort *v2);\nextern GLint (APIENTRY *qglRenderMode) (GLenum mode);\nextern void (APIENTRY *qglRotated) (GLdouble angle, GLdouble x, GLdouble y, GLdouble z);\nextern void (APIENTRY *qglRotatef) (GLfloat angle, GLfloat x, GLfloat y, GLfloat z);\nextern void (APIENTRY *qglScaled) (GLdouble x, GLdouble y, GLdouble z);\nextern void (APIENTRY *qglScalef) (GLfloat x, GLfloat y, GLfloat z);\nextern void (APIENTRY *qglSelectBuffer) (GLsizei size, GLuint *buffer);\nextern void (APIENTRY *qglShadeModel) (GLenum mode);\nextern void (APIENTRY *qglTexCoord1d) (GLdouble s);\nextern void (APIENTRY *qglTexCoord1dv) (const GLdouble *v);\nextern void (APIENTRY *qglTexCoord1f) (GLfloat s);\nextern void (APIENTRY *qglTexCoord1fv) (const GLfloat *v);\nextern void (APIENTRY *qglTexCoord1i) (GLint s);\nextern void (APIENTRY *qglTexCoord1iv) (const GLint *v);\nextern void (APIENTRY *qglTexCoord1s) (GLshort s);\nextern void (APIENTRY *qglTexCoord1sv) (const GLshort *v);\nextern void (APIENTRY *qglTexCoord2d) (GLdouble s, GLdouble t);\nextern void (APIENTRY *qglTexCoord2dv) (const GLdouble *v);\nextern void (APIENTRY *qglTexCoord2f) (GLfloat s, GLfloat t);\nextern void (APIENTRY *qglTexCoord2fv) (const GLfloat *v);\nextern void (APIENTRY *qglTexCoord2i) (GLint s, GLint t);\nextern void (APIENTRY *qglTexCoord2iv) (const GLint *v);\nextern void (APIENTRY *qglTexCoord2s) (GLshort s, GLshort t);\nextern void (APIENTRY *qglTexCoord2sv) (const GLshort *v);\nextern void (APIENTRY *qglTexCoord3d) (GLdouble s, GLdouble t, GLdouble r);\nextern void (APIENTRY *qglTexCoord3dv) (const GLdouble *v);\nextern void (APIENTRY *qglTexCoord3f) (GLfloat s, GLfloat t, GLfloat r);\nextern void (APIENTRY *qglTexCoord3fv) (const GLfloat *v);\nextern void (APIENTRY *qglTexCoord3i) (GLint s, GLint t, GLint r);\nextern void (APIENTRY *qglTexCoord3iv) (const GLint *v);\nextern void (APIENTRY *qglTexCoord3s) (GLshort s, GLshort t, GLshort r);\nextern void (APIENTRY *qglTexCoord3sv) (const GLshort *v);\nextern void (APIENTRY *qglTexCoord4d) (GLdouble s, GLdouble t, GLdouble r, GLdouble q);\nextern void (APIENTRY *qglTexCoord4dv) (const GLdouble *v);\nextern void (APIENTRY *qglTexCoord4f) (GLfloat s, GLfloat t, GLfloat r, GLfloat q);\nextern void (APIENTRY *qglTexCoord4fv) (const GLfloat *v);\nextern void (APIENTRY *qglTexCoord4i) (GLint s, GLint t, GLint r, GLint q);\nextern void (APIENTRY *qglTexCoord4iv) (const GLint *v);\nextern void (APIENTRY *qglTexCoord4s) (GLshort s, GLshort t, GLshort r, GLshort q);\nextern void (APIENTRY *qglTexCoord4sv) (const GLshort *v);\nextern void (APIENTRY *qglTexCoordPointer) (GLint size, GLenum type, GLsizei stride, const GLvoid *pointer);\nextern void (APIENTRY *qglTexEnvf) (GLenum target, GLenum pname, GLfloat param);\nextern void (APIENTRY *qglTexEnvfv) (GLenum target, GLenum pname, const GLfloat *params);\nextern void (APIENTRY *qglTexEnvi) (GLenum target, GLenum pname, GLint param);\nextern void (APIENTRY *qglTexEnviv) (GLenum target, GLenum pname, const GLint *params);\nextern void (APIENTRY *qglTexGend) (GLenum coord, GLenum pname, GLdouble param);\nextern void (APIENTRY *qglTexGendv) (GLenum coord, GLenum pname, const GLdouble *params);\nextern void (APIENTRY *qglTexGenf) (GLenum coord, GLenum pname, GLfloat param);\nextern void (APIENTRY *qglTexGenfv) (GLenum coord, GLenum pname, const GLfloat *params);\nextern void (APIENTRY *qglTexGeni) (GLenum coord, GLenum pname, GLint param);\nextern void (APIENTRY *qglTexGeniv) (GLenum coord, GLenum pname, const GLint *params);\nextern void (APIENTRY *qglTexImage1D) (GLenum target, GLint level, GLint internalformat, GLsizei width, GLint border, GLenum format, GLenum type, const GLvoid *pixels);\nextern void (APIENTRY *qglTexImage3D) (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const GLvoid *pixels);\nextern void (APIENTRY *qglTexSubImage1D) (GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const GLvoid *pixels);\nextern void (APIENTRY *qglTexSubImage3D) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels);\nextern void (APIENTRY *qglTranslated) (GLdouble x, GLdouble y, GLdouble z);\nextern void (APIENTRY *qglTranslatef) (GLfloat x, GLfloat y, GLfloat z);\n\nextern FTEPFNGLUNIFORMMATRIX4X3FVPROC\t\tqglUniformMatrix4x3fv;\nextern FTEPFNGLUNIFORMMATRIX3X4FVPROC\t\tqglUniformMatrix3x4fv;\n\nextern FTEPFNGLCOMPRESSEDTEXIMAGE3DARBPROC qglCompressedTexImage3D;\nextern void (APIENTRY *qglCompressedTexSubImage3D) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *data);\t//gl1.3\nextern FTEPFNGLGETCOMPRESSEDTEXIMAGEARBPROC qglGetCompressedTexImage;\n\nextern const GLubyte * (APIENTRY * qglGetStringi) (GLenum name, GLuint index);\n\n/*\nextern qboolean gl_arb_fragment_program;\nextern PFNGLPROGRAMSTRINGARBPROC qglProgramStringARB;\nextern PFNGLGETPROGRAMIVARBPROC qglGetProgramivARB;\nextern PFNGLBINDPROGRAMARBPROC qglBindProgramARB;\nextern PFNGLGENPROGRAMSARBPROC qglGenProgramsARB;\n*/\n\nextern FTEPFNGLLOCKARRAYSEXTPROC qglLockArraysEXT;\nextern FTEPFNGLUNLOCKARRAYSEXTPROC qglUnlockArraysEXT;\n\ntypedef void (APIENTRY *lpSelTexFUNC) (GLenum en);\nextern lpSelTexFUNC qglSelectTextureSGIS;\n\n//these functions are not available in gles2, for one reason or another\nextern void (APIENTRY *qglBegin) (GLenum mode);\nextern void (APIENTRY *qglVertex2d) (GLdouble x, GLdouble y);\nextern void (APIENTRY *qglVertex2dv) (const GLdouble *v);\nextern void (APIENTRY *qglVertex2f) (GLfloat x, GLfloat y);\nextern void (APIENTRY *qglVertex2fv) (const GLfloat *v);\nextern void (APIENTRY *qglVertex2i) (GLint x, GLint y);\nextern void (APIENTRY *qglVertex2iv) (const GLint *v);\nextern void (APIENTRY *qglVertex2s) (GLshort x, GLshort y);\nextern void (APIENTRY *qglVertex2sv) (const GLshort *v);\nextern void (APIENTRY *qglVertex3d) (GLdouble x, GLdouble y, GLdouble z);\nextern void (APIENTRY *qglVertex3dv) (const GLdouble *v);\nextern void (APIENTRY *qglVertex3f) (GLfloat x, GLfloat y, GLfloat z);\nextern void (APIENTRY *qglVertex3fv) (const GLfloat *v);\nextern void (APIENTRY *qglVertex3i) (GLint x, GLint y, GLint z);\nextern void (APIENTRY *qglVertex3iv) (const GLint *v);\nextern void (APIENTRY *qglVertex3s) (GLshort x, GLshort y, GLshort z);\nextern void (APIENTRY *qglVertex3sv) (const GLshort *v);\nextern void (APIENTRY *qglVertex4d) (GLdouble x, GLdouble y, GLdouble z, GLdouble w);\nextern void (APIENTRY *qglVertex4dv) (const GLdouble *v);\nextern void (APIENTRY *qglVertex4f) (GLfloat x, GLfloat y, GLfloat z, GLfloat w);\nextern void (APIENTRY *qglVertex4fv) (const GLfloat *v);\nextern void (APIENTRY *qglVertex4i) (GLint x, GLint y, GLint z, GLint w);\nextern void (APIENTRY *qglVertex4iv) (const GLint *v);\nextern void (APIENTRY *qglVertex4s) (GLshort x, GLshort y, GLshort z, GLshort w);\nextern void (APIENTRY *qglVertex4sv) (const GLshort *v);\nextern void (APIENTRY *qglEnd) (void);\nextern void (APIENTRY *qglReadBuffer) (GLenum mode);\n\n//misc extensions\nextern FTEPFNGLACTIVESTENCILFACEEXTPROC qglActiveStencilFaceEXT;\nextern void (APIENTRY *qglDepthBoundsEXT) (GLclampd zmin, GLclampd zmax);\n\nextern void (APIENTRY *qglDrawRangeElements) (GLenum, GLuint, GLuint, GLsizei, GLenum, const GLvoid *);\nextern void (APIENTRY *qglMultiDrawElements) (GLenum mode, const GLsizei * count, GLenum type, const GLvoid * const * indices, GLsizei drawcount);\nextern void (APIENTRY *qglEnableClientState) (GLenum array);\nextern void (APIENTRY *qglVertexPointer) (GLint size, GLenum type, GLsizei stride, const GLvoid *pointer);\n\nextern void (APIENTRY *qglGenVertexArrays)(GLsizei n, GLuint *arrays);\nextern void (APIENTRY *qglBindVertexArray)(GLuint vaoarray);\n\nextern void (APIENTRY *qglTexStorage2D)(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height);\t\t//gl4.2\nextern void (APIENTRY *qglTexStorage3D)(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth);\t//gl4.2\n\n\n//glslang helper api\nstruct programshared_s;\nunion programhandle_u GLSlang_CreateProgram(struct programshared_s *prog, const char *name, int ver, const char **precompilerconstants, const char *vert, const char *cont, const char *eval, const char *geom, const char *frag, qboolean silent, vfsfile_t *blobfile);\nGLint GLSlang_GetUniformLocation (GLuint prog, char *name);\nvoid GL_SelectProgram(GLuint program);\n#define GLSlang_UseProgram(prog) GL_SelectProgram(prog)\n#define GLSlang_SetUniform1i(uni, parm0) qglUniform1iARB(uni, parm0)\n#define GLSlang_SetUniform1f(uni, parm0) qglUniform1fARB(uni, parm0)\n\n\n#ifdef _DEBUG\n#if defined(__GNUC__)\n#define checkglerror() do {int i=qglGetError(); if (i) Sys_Printf(\"GL Error %i detected at line %s:%i (caller %p)\\n\", i, __FILE__, __LINE__, __builtin_return_address(0));}while(0)\n#else\n#define checkglerror() do {int i=qglGetError(); if (i) Con_Printf(\"GL Error %i detected at line %s:%i\\n\", i, __FILE__, __LINE__);}while(0)\n#endif\n#else\n#define checkglerror()\n#endif\n\n\n\n\nqboolean GL_Init(rendererstate_t *info, void *(*getglfunction) (char *name));\nvoid GL_ForgetPointers(void);\n\n#endif\n\nqbyte GetPaletteIndex(int red, int green, int blue);\nqbyte GetPaletteIndexNoFB(int red, int green, int blue);\nint Mod_ReadFlagsFromMD1(char *name, int md3version);\n\n/*\n//opengl 3 deprecation\n\n. Application-generated object names - the names of all object types, such as\nbuffer, query, and texture objects, must be generated using the corresponding\nGen* commands. Trying to bind an object name not returned by a Gen*\ncommand will result in an INVALID OPERATION error. This behavior is\nalready the case for framebuffer, renderbuffer, and vertex array objects. Object\ntypes which have default objects (objects named zero) , such as vertex\narray, framebuffer, and texture objects, may also bind the default object, even\nthough it is not returned by Gen*.\n\n. OpenGL Shading Language versions 1.10 and 1.20. These versions of the\nshading language depend on many API features that have also been deprecated.\n\n. Pixel transfer modes and operations - all pixel transfer modes, including\npixel maps, shift and bias, color table lookup, color matrix, and convolution\ncommands and state, and all associated state and commands defining\nthat state.\n\n. Legacy OpenGL 1.0 pixel formats - the values 1, 2, 3, and 4 are no longer\naccepted as internal formats by TexImage* or any other command taking\nan internal format argument. The initial internal format of a texel array is\nRGBA instead of 1.\n\n. Texture borders - the border value to TexImage* must always be zero, or\nan INVALID VALUE error is generated (section 3.8.1); all language in section\n3.8 referring to nonzero border widths during texture image specification\nand texture sampling; and all associated state.\n\nGL_COLOR_INDEX\n\nglBegin\nglEnd\nglEdgeFlag*; \nglColor*,\nglFogCoord*\nglIndex*\nglNormal3*\nglSecondaryColor3*\nglTexCoord*\nglVertex*\n\nglColorPointer\nglEdgeFlagPointer\nglFogCoordPointer\nglIndexPointer\nglNormalPointer\nglSecondary-\nglColorPointer, \nglTexCoordPointer\nglVertexPointer\nglEnableClientState\nglDisableClientState,\n\nglInterleavedArrays\nglClientActiveTexture\nglFrustum,\nglLoadIdentity\nglLoadMatrix\nglLoadTransposeMatrix\nglMatrixMode,\nglMultMatrix\nglMultTransposeMatrix\nglOrtho\nglPopMatrix\nglPushMatrix,\nglRotate\nglScale\nglTranslate\nGL_RESCALE_NORMAL\nGL_NORMALIZE\nglTexGen*\nGL_TEXTURE_GEN_*,\nMaterial*\nglLight*\nglLightModel*\nglColorMaterial\nglShadeModel\nGL_LIGHTING\nGL_VERTEX_PROGRAM_TWO_SIDE\nGL_LIGHTi,\nGL_COLOR_MATERIAL\nglClipPlane\nGL_CLAMP_VERTEX_COLOR\nGL_CLAMP_FRAGMENT_COLOR\nglRect*\n\nglRasterPos*\nglWindowPos*\n\nGL_POINT_SMOOTH\nGL_POINT_SPRITE\n\nglLineStipple\nGL_LINE_STIPPLE\nGL_POLYGON\nGL_QUADS\nGL_QUAD_STRIP\nglPolygonMode\nglPolygonStipple\nGL_POLYGON_STIPPLE\nglDrawPixels\nglPixelZoom\nglBitmap\nGL_BITMAP\n\nGL_TEXTURE_COMPONENTS\nGL_ALPHA\nGL_LUMINANCE\nGL_LUMINANCE_ALPHA\nGL_INTENSITY\n\nGL_DEPTH_TEXTURE_MODE\n\nGL_CLAMP\n\nGL_GENERATE_MIPMAP\n\nglAreTexturesResident\nglPrioritizeTextures,\nGL_TEXTURE_PRIORITY\nGL_TEXTURE_ENV\nGL_TEXTURE_FILTER_CONTROL\nGL_TEXTURE_LOD_BIAS\n\nGL_TEXTURE_1D\nGL_TEXTURE_2D,\nGL_TEXTURE_3D\nGL_TEXTURE_1D_ARRAY\nGL_TEXTURE_2D_ARRAY\nGL_TEXTURE_CUBE_MAP\nGL_COLOR_SUM\nGL_FOG\nglFog\nGL_MAX_TEXTURE_UNITS\nGL_MAX_TEXTURE_COORDS\nglAlphaFunc\nGL_ALPHA_TEST\n\nglClearAccum\nGL_ACCUM_BUFFER_BIT\n\nglCopyPixels\n\nGL_AUX0\nGL_RED_BITS\nGL_GREEN_BITS\nGL_BLUE_BITS\nGL_ALPHA_BITS\nGL_DEPTH_BITS\nSTENCIL BITS\nglMap*\nglEvalCoord*\nglMapGrid*\nglEvalMesh*\nglEvalPoint*\n\nglRenderMode\nglInitNames\nglPopName\nglPushName\nglLoadName\nglSelectBuffer\nglFeedbackBuffer\nglPassThrough\nglNewList\nglEndList\nglCallList\nglCallLists\nglListBase\nglGenLists,\nglIsList\nglDeleteLists\n\n\nGL_PERSPECTIVE_CORRECTION_HINT\nGL_POINT_SMOOTH_HINT,\nGL_FOG_HINT\nGL_GENERATE_MIPMAP_HINT\n\nglPushAttrib\nglPushClientAttrib\nglPopAttrib\nglPopClientAttrib,\nGL_MAX_ATTRIB_STACK_DEPTH,\nGL_MAX_CLIENT_ATTRIB_STACK_DEPTH\nGL_ATTRIB_STACK_DEPTH\nGL_CLIENT_ATTRIB_STACK_DEPTH\nGL_ALL_ATTRIB_BITS\nGL_CLIENT_ALL_ATTRIB_BITS.\nGL_EXTENSIONS\n*/\n\n#endif\n"
  },
  {
    "path": "engine/gl/glsupp.h",
    "content": "//gl suppliment for Quake\n\n#define APIENTRYP APIENTRY *\n\n//contains the extra things that would otherwise be found in glext.h\n\n//typedef void (APIENTRY *qlpMTex2FUNC) (GLenum, GLfloat, GLfloat);\n//typedef void (APIENTRY *qlpMTex3FUNC) (GLenum, GLfloat, GLfloat, GLfloat);\ntypedef void (APIENTRY *qlpSelTexFUNC) (GLenum);\n\nextern qlpSelTexFUNC\tqglActiveTextureARB;\nextern qlpSelTexFUNC\tqglClientActiveTextureARB;\n//extern qlpMTex3FUNC\t\tqglMultiTexCoord3fARB;\n//extern qlpMTex2FUNC\t\tqglMultiTexCoord2fARB;\n\n//This stuff is normally supplied in the <GL/glext.h> header file. I don't actually have one of them, so it's here instead.\n#if 0\t//change to 1 if you do actually have the file in question - and its up to date.\n#include <GL/glext.h>\t//would be ideal.\n#else\n\n#ifndef GL_TEXTURE_WIDTH\n#define GL_TEXTURE_WIDTH                  0x1000\n#endif\n#ifndef GL_TEXTURE_HEIGHT\n#define GL_TEXTURE_HEIGHT                 0x1001\n#endif\n#ifndef GL_TEXTURE_INTERNAL_FORMAT\n#define GL_TEXTURE_INTERNAL_FORMAT\t\t0x1003\n#endif\n\n#ifndef GL_DEPTH_COMPONENT\n#define GL_DEPTH_COMPONENT                0x1902\n#endif\n\n//#ifndef GL_VERSION_1_2\n#define GL_CLAMP_TO_EDGE                  0x812F\n//#endif\n\n\n#ifndef GL_MAX_ARRAY_TEXTURE_LAYERS\n#define GL_MAX_ARRAY_TEXTURE_LAYERS       0x88FF\t/*opengl 3.0*/\n#endif\n\n\n// Added to make morphos and mingw32 crosscompilers to work\n/*\n./gl/gl_draw.c: In function `GL_Upload32_BGRA':\n./gl/gl_draw.c:3251: error: `GL_BGRA_EXT' undeclared (first use in this function)\n./gl/gl_draw.c:3251: error: (Each undeclared identifier is reported only once\n./gl/gl_draw.c:3251: error: for each function it appears in.)\n*/\n#ifndef GL_EXT_bgra\n#define GL_BGR_EXT\t\t\t\t\t\t\t0x80E0\t/*core in opengl 1.2*/\n#define GL_BGRA_EXT\t\t\t\t\t\t\t0x80E1\n#endif\n\n#ifndef GL_UNSIGNED_INT_8_8_8_8_REV\n#define GL_UNSIGNED_INT_8_8_8_8_REV\t\t\t0x8367\t/*opengl 1.2*/\n#endif\n#ifndef GL_UNSIGNED_INT_2_10_10_10_REV\n#define GL_UNSIGNED_INT_2_10_10_10_REV\t\t0x8368\t/*opengl 1.2*/\n#endif\n#ifndef GL_UNSIGNED_INT_5_9_9_9_REV\n#define GL_UNSIGNED_INT_5_9_9_9_REV\t\t\t0x8C3E\t/*opengl 3.0*/\n#endif\n#ifndef GL_UNSIGNED_SHORT_4_4_4_4_REV\n#define GL_UNSIGNED_SHORT_4_4_4_4_REV\t\t0x8365\n#endif\n#ifndef GL_UNSIGNED_SHORT_1_5_5_5_REV\n#define GL_UNSIGNED_SHORT_1_5_5_5_REV\t\t0x8366\n#endif\n#ifndef GL_UNSIGNED_SHORT_4_4_4_4\n#define GL_UNSIGNED_SHORT_4_4_4_4\t\t\t0x8033\n#endif\n#ifndef GL_UNSIGNED_SHORT_5_5_5_1\n#define GL_UNSIGNED_SHORT_5_5_5_1\t\t\t0x8034\n#endif\n#ifndef GL_UNSIGNED_SHORT_5_6_5\n#define GL_UNSIGNED_SHORT_5_6_5\t\t\t\t0x8363\n#endif\n#ifndef GL_HALF_FLOAT\n#define GL_HALF_FLOAT\t\t\t\t\t\t0x140B\t\t/*GL_ARB_half_float_pixel*/\n#endif\n#ifndef GL_HALF_FLOAT_OES\n#define GL_HALF_FLOAT_OES\t\t\t\t\t0x8D61\t\t/*GL_OES_texture_half_float*/\n#endif\n#ifndef GL_UNSIGNED_INT_24_8\n#define GL_UNSIGNED_INT_24_8              0x84FA\n#endif\n\n#ifndef GL_ARB_multitexture\n#define GL_ARB_multitexture 1\n#define GL_TEXTURE0_ARB                   0x84C0\n#define GL_TEXTURE1_ARB                   0x84C1\n#define GL_TEXTURE2_ARB                   0x84C2\n#define GL_TEXTURE3_ARB                   0x84C3\n#define GL_TEXTURE4_ARB                   0x84C4\n#define GL_TEXTURE5_ARB                   0x84C5\n#define GL_TEXTURE6_ARB                   0x84C6\n#define GL_TEXTURE7_ARB                   0x84C7\n#define GL_TEXTURE8_ARB                   0x84C8\n#define GL_TEXTURE9_ARB                   0x84C9\n#define GL_TEXTURE10_ARB                  0x84CA\n#define GL_TEXTURE11_ARB                  0x84CB\n#define GL_TEXTURE12_ARB                  0x84CC\n#define GL_TEXTURE13_ARB                  0x84CD\n#define GL_TEXTURE14_ARB                  0x84CE\n#define GL_TEXTURE15_ARB                  0x84CF\n#define GL_TEXTURE16_ARB                  0x84D0\n#define GL_TEXTURE17_ARB                  0x84D1\n#define GL_TEXTURE18_ARB                  0x84D2\n#define GL_TEXTURE19_ARB                  0x84D3\n#define GL_TEXTURE20_ARB                  0x84D4\n#define GL_TEXTURE21_ARB                  0x84D5\n#define GL_TEXTURE22_ARB                  0x84D6\n#define GL_TEXTURE23_ARB                  0x84D7\n#define GL_TEXTURE24_ARB                  0x84D8\n#define GL_TEXTURE25_ARB                  0x84D9\n#define GL_TEXTURE26_ARB                  0x84DA\n#define GL_TEXTURE27_ARB                  0x84DB\n#define GL_TEXTURE28_ARB                  0x84DC\n#define GL_TEXTURE29_ARB                  0x84DD\n#define GL_TEXTURE30_ARB                  0x84DE\n#define GL_TEXTURE31_ARB                  0x84DF\n#define GL_ACTIVE_TEXTURE_ARB             0x84E0\n#define GL_CLIENT_ACTIVE_TEXTURE_ARB      0x84E1\n#define GL_MAX_TEXTURE_UNITS_ARB          0x84E2\n#endif\n\n#ifndef GL_ARB_texture_cube_map\n#define GL_ARB_texture_cube_map 1\n#define GL_NORMAL_MAP_ARB                 0x8511\n#define GL_REFLECTION_MAP_ARB             0x8512\n#define GL_TEXTURE_CUBE_MAP_ARB           0x8513\n#define GL_TEXTURE_BINDING_CUBE_MAP_ARB   0x8514\n#define GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB 0x8515\n#define GL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB 0x8516\n#define GL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB 0x8517\n#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB 0x8518\n#define GL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB 0x8519\n#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB 0x851A\n#define GL_PROXY_TEXTURE_CUBE_MAP_ARB     0x851B\n#define GL_MAX_CUBE_MAP_TEXTURE_SIZE_ARB  0x851C\n#endif\n\n#ifndef GL_ARB_texture_cube_map_array\n#define GL_ARB_texture_cube_map_array 1\n#define GL_TEXTURE_CUBE_MAP_ARRAY_ARB     0x9009\n#define GL_TEXTURE_BINDING_CUBE_MAP_ARRAY_ARB 0x900A\n#define GL_PROXY_TEXTURE_CUBE_MAP_ARRAY_ARB 0x900B\n#define GL_SAMPLER_CUBE_MAP_ARRAY_ARB     0x900C\n#define GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW_ARB 0x900D\n#define GL_INT_SAMPLER_CUBE_MAP_ARRAY_ARB 0x900E\n#define GL_UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY_ARB 0x900F\n#endif /* GL_ARB_texture_cube_map_array */\n\n#ifndef GL_TEXTURE_2D_ARRAY\t\t\t\t//gl 3.0 or GL_EXT_texture_array\n#define GL_TEXTURE_2D_ARRAY\t\t\t\t0x8C1A\n#endif\n\n\n#ifndef GL_ARB_depth_texture\n#define GL_ARB_depth_texture\n#define GL_DEPTH_COMPONENT16_ARB          0x81A5\n#define GL_DEPTH_COMPONENT24_ARB          0x81A6\n#define GL_DEPTH_COMPONENT32_ARB          0x81A7\n#define GL_TEXTURE_DEPTH_SIZE_ARB         0x884A\n#define GL_DEPTH_TEXTURE_MODE_ARB         0x884B\n#endif\n#ifndef GL_DEPTH_COMPONENT32F\n#define GL_DEPTH_COMPONENT32F\t0x8CAC\n#endif\n\n//GL_OES_depth_texture adds this because gles otherwise lacks it.\n#ifndef GL_UNSIGNED_INT\n#define GL_UNSIGNED_INT\t\t\t\t\t\t0x1405\n#endif\n\n#ifndef GL_EXT_packed_depth_stencil\n#define GL_DEPTH24_STENCIL8_EXT                           0x88F0\n#define GL_DEPTH_STENCIL_EXT                              0x84F9\n#define GL_UNSIGNED_INT_24_8_EXT                          0x84FA\n#endif\n\n#ifndef GL_ARB_shadow\n#define GL_ARB_shadow\n#define GL_TEXTURE_COMPARE_MODE_ARB       0x884C\n#define GL_TEXTURE_COMPARE_FUNC_ARB       0x884D\n#define GL_COMPARE_R_TO_TEXTURE_ARB       0x884E\n#endif\n\n#ifndef GL_EXT_texture3D\n#define GL_EXT_texture3D 1\n#define GL_PACK_SKIP_IMAGES               0x806B\n#define GL_PACK_SKIP_IMAGES_EXT           0x806B\n#define GL_PACK_IMAGE_HEIGHT              0x806C\n#define GL_PACK_IMAGE_HEIGHT_EXT          0x806C\n#define GL_UNPACK_SKIP_IMAGES             0x806D\n#define GL_UNPACK_SKIP_IMAGES_EXT         0x806D\n#define GL_UNPACK_IMAGE_HEIGHT            0x806E\n#define GL_UNPACK_IMAGE_HEIGHT_EXT        0x806E\n#define GL_TEXTURE_3D                     0x806F\n#define GL_TEXTURE_3D_EXT                 0x806F\n#define GL_PROXY_TEXTURE_3D               0x8070\n#define GL_PROXY_TEXTURE_3D_EXT           0x8070\n#define GL_TEXTURE_DEPTH                  0x8071\n#define GL_TEXTURE_DEPTH_EXT              0x8071\n#define GL_TEXTURE_WRAP_R                 0x8072\n#define GL_TEXTURE_WRAP_R_EXT             0x8072\n#define GL_MAX_3D_TEXTURE_SIZE            0x8073\n#define GL_MAX_3D_TEXTURE_SIZE_EXT        0x8073\n#endif\n\n\n\n\n\n\n\n\n//some of these were needed.\n//They were also not in the ones I could find on the web.\n//GL_ARB_texture_env_combine\n#define  GL_COMBINE_ARB\t\t\t\t\t0x8570\n#define  GL_COMBINE_RGB_ARB\t\t\t\t0x8571\n#define  GL_COMBINE_ALPHA_ARB\t\t\t0x8572\n#define  GL_SOURCE0_RGB_ARB\t\t\t\t0x8580\n#define  GL_SOURCE1_RGB_ARB\t\t\t\t0x8581\n#define  GL_SOURCE2_RGB_ARB\t\t\t\t0x8582\n#define  GL_SOURCE0_ALPHA_ARB\t\t\t0x8588\n#define  GL_SOURCE1_ALPHA_ARB\t\t\t0x8589\n#define  GL_SOURCE2_ALPHA_ARB\t\t\t0x858A\n#define  GL_OPERAND0_RGB_ARB\t\t\t0x8590\n#define  GL_OPERAND1_RGB_ARB\t\t\t0x8591\n#define  GL_OPERAND2_RGB_ARB\t\t\t0x8592\n#define  GL_OPERAND0_ALPHA_ARB\t\t\t0x8598\n#define  GL_OPERAND1_ALPHA_ARB\t\t\t0x8599\n#define  GL_OPERAND2_ALPHA_ARB\t\t\t0x859A\n#define  GL_RGB_SCALE_ARB\t\t\t\t0x8573\n#define  GL_ADD_SIGNED_ARB\t\t\t\t0x8574\n#define  GL_INTERPOLATE_ARB\t\t\t\t0x8575\n#define  GL_SUBTRACT_ARB\t\t\t\t0x84E7\n#define  GL_CONSTANT_ARB\t\t\t\t0x8576\n#define  GL_PRIMARY_COLOR_ARB\t\t\t0x8577\n#define  GL_PREVIOUS_ARB\t\t\t\t0x8578\n\n\n#define  GL_DOT3_RGB_ARB   0x86AE\n#define  GL_DOT3_RGBA_ARB   0x86AF\n\n//GL_EXT_texture_env_combine\n#define  GL_COMBINE_EXT\t\t\t\t\t0x8570\n#define  GL_COMBINE_RGB_EXT\t\t\t\t0x8571\n#define  GL_COMBINE_ALPHA_EXT\t\t\t0x8572\n#define  GL_SOURCE0_RGB_EXT\t\t\t\t0x8580\n#define  GL_SOURCE1_RGB_EXT\t\t\t\t0x8581\n#define  GL_SOURCE2_RGB_EXT\t\t\t\t0x8582\n#define  GL_SOURCE0_ALPHA_EXT\t\t\t0x8588\n#define  GL_SOURCE1_ALPHA_EXT\t\t\t0x8589\n#define  GL_SOURCE2_ALPHA_EXT\t\t\t0x858A\n#define  GL_OPERAND0_RGB_EXT\t\t\t0x8590\n#define  GL_OPERAND1_RGB_EXT\t\t\t0x8591\n#define  GL_OPERAND2_RGB_EXT\t\t\t0x8592\n#define  GL_OPERAND0_ALPHA_EXT\t\t\t0x8598\n#define  GL_OPERAND1_ALPHA_EXT\t\t\t0x8599\n#define  GL_OPERAND2_ALPHA_EXT\t\t\t0x859A\n#define  GL_RGB_SCALE_EXT\t\t\t\t0x8573\n#define  GL_ADD_SIGNED_EXT\t\t\t\t0x8574\n#define  GL_INTERPOLATE_EXT\t\t\t\t0x8575\n#define  GL_CONSTANT_EXT\t\t\t\t0x8576\n#define  GL_PRIMARY_COLOR_EXT\t\t\t0x8577\n#define  GL_PREVIOUS_EXT\t\t\t\t0x8578\n\n//GL_NV_texture_env_combine4\n#define  GL_COMBINE4_NV\t\t\t\t\t0x8503\n#define  GL_SOURCE3_RGB_NV\t\t\t\t0x8583\n#define  GL_SOURCE3_ALPHA_NV\t\t\t0x858B\n#define  GL_OPERAND3_RGB_NV\t\t\t\t0x8593\n#define  GL_OPERAND3_ALPHA_NV\t\t\t0x859B\n\n\n\n/* GL_ARB_texture_compression */\n#ifndef GL_ARB_texture_compression\n#define GL_ARB_texture_compression 1\n\n#define GL_COMPRESSED_ALPHA_ARB                                 0x84E9\n#define GL_COMPRESSED_LUMINANCE_ARB                             0x84EA\n#define GL_COMPRESSED_LUMINANCE_ALPHA_ARB                       0x84EB\n#define GL_COMPRESSED_INTENSITY_ARB                             0x84EC\n#define GL_COMPRESSED_RGB_ARB                                   0x84ED\n#define GL_COMPRESSED_RGBA_ARB                                  0x84EE\n#define GL_TEXTURE_COMPRESSION_HINT_ARB                         0x84EF\n#define GL_TEXTURE_COMPRESSED_IMAGE_SIZE_ARB                    0x86A0\n#define GL_TEXTURE_COMPRESSED_ARB                               0x86A1\n#define GL_NUM_COMPRESSED_TEXTURE_FORMATS_ARB                   0x86A2\n#define GL_COMPRESSED_TEXTURE_FORMATS_ARB                       0x86A3\n\ntypedef void (APIENTRY *PFNGLCOMPRESSEDTEXIMAGE3DARBPROC)\t(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const GLvoid* data);\ntypedef void (APIENTRY *PFNGLCOMPRESSEDTEXIMAGE2DARBPROC)\t(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const GLvoid* data);\ntypedef void (APIENTRY *PFNGLCOMPRESSEDTEXIMAGE1DARBPROC)\t(GLenum target, GLint level, GLint internalformat, GLsizei width, GLint border, GLsizei imageSize, const GLvoid* data);\ntypedef void (APIENTRY *PFNGLCOMPRESSEDTEXSUBIMAGE3DARBPROC)\t(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const GLvoid* data);\ntypedef void (APIENTRY *PFNGLCOMPRESSEDTEXSUBIMAGE2DARBPROC)\t(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const GLvoid* data);\ntypedef void (APIENTRY *PFNGLCOMPRESSEDTEXSUBIMAGE1DARBPROC)\t(GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const GLvoid* data);\ntypedef void (APIENTRY *PFNGLGETCOMPRESSEDTEXIMAGEARBPROC)\t(GLenum target, GLint lod, const GLvoid* img);\n\n#endif /* GL_ARB_texture_compression */\n\n\n#ifndef GL_ATI_pn_triangles\t//ati truform\n#define GL_PN_TRIANGLES_ATI\t\t\t\t\t\t\t0x87F0\n#define GL_MAX_PN_TRIANGLES_TESSELATION_LEVEL_ATI\t0x87F1\n#define GL_PN_TRIANGLES_POINT_MODE_ATI\t\t\t\t0x87F2\n#define GL_PN_TRIANGLES_NORMAL_MODE_ATI\t\t\t\t0x87F3\n#define GL_PN_TRIANGLES_TESSELATION_LEVEL_ATI\t\t0x87F4\n#define GL_PN_TRIANGLES_POINT_MODE_LINEAR_ATI\t\t0x87F5\n#define GL_PN_TRIANGLES_POINT_MODE_CUBIC_ATI\t\t0x87F6\n#define GL_PN_TRIANGLES_NORMAL_MODE_LINEAR_ATI\t\t0x87F7\n#define GL_PN_TRIANGLES_NORMAL_MODE_QUADRATIC_ATI\t0x87F8\n\ntypedef void (APIENTRY *PFNGLPNTRIANGLESIATIPROC)(GLenum pname, GLint param);\ntypedef void (APIENTRY *PFNGLPNTRIANGLESFATIPROC)(GLenum pname, GLfloat param);\n#endif\n\n#ifndef GL_EXT_depth_bounds_test\n#define GL_EXT_depth_bounds_test 1\n#define GL_DEPTH_BOUNDS_TEST_EXT\t\t\t\t\t0x8890\n#endif\n\n#ifndef GL_EXT_stencil_two_side\n#define GL_EXT_stencil_two_side 1\n\n#define GL_STENCIL_TEST_TWO_SIDE_EXT\t\t\t\t0x8910\n#define GL_ACTIVE_STENCIL_FACE_EXT\t\t\t\t\t0x8911\n\ntypedef void (APIENTRY * PFNGLACTIVESTENCILFACEEXTPROC) (GLenum face);\n#endif\n\n#ifndef GL_EXT_stencil_wrap\n#define GL_EXT_stencil_wrap 1\n#define GL_INCR_WRAP_EXT\t\t\t\t\t\t\t0x8507\n#define GL_DECR_WRAP_EXT\t\t\t\t\t\t\t0x8508\n#endif\n\n#ifndef GL_EXT_texture_filter_anisotropic\n#define GL_EXT_texture_filter_anisotropic 1\n#define GL_TEXTURE_MAX_ANISOTROPY_EXT\t\t\t\t0x84FE\n#define GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT\t\t\t0x84FF\n#endif\n\n\n#ifndef GL_VERSION_4_1\n#define GL_MAX_VERTEX_UNIFORM_VECTORS     0x8DFB\n#endif\n#ifndef GL_VERSION_2_0\n#define GL_MAX_VERTEX_UNIFORM_COMPONENTS  0x8B4A\n#endif\n\n\n#ifndef GL_ARB_vertex_program\n#define GL_COLOR_SUM_ARB                  0x8458\n#define GL_VERTEX_PROGRAM_ARB             0x8620\n#define GL_VERTEX_ATTRIB_ARRAY_ENABLED_ARB 0x8622\n#define GL_VERTEX_ATTRIB_ARRAY_SIZE_ARB   0x8623\n#define GL_VERTEX_ATTRIB_ARRAY_STRIDE_ARB 0x8624\n#define GL_VERTEX_ATTRIB_ARRAY_TYPE_ARB   0x8625\n#define GL_CURRENT_VERTEX_ATTRIB_ARB      0x8626\n#define GL_PROGRAM_LENGTH_ARB             0x8627\n#define GL_PROGRAM_STRING_ARB             0x8628\n#define GL_MAX_PROGRAM_MATRIX_STACK_DEPTH_ARB 0x862E\n#define GL_MAX_PROGRAM_MATRICES_ARB       0x862F\n#define GL_CURRENT_MATRIX_STACK_DEPTH_ARB 0x8640\n#define GL_CURRENT_MATRIX_ARB             0x8641\n#define GL_VERTEX_PROGRAM_POINT_SIZE_ARB  0x8642\n#define GL_VERTEX_PROGRAM_TWO_SIDE_ARB    0x8643\n#define GL_VERTEX_ATTRIB_ARRAY_POINTER_ARB 0x8645\n#define GL_PROGRAM_ERROR_POSITION_ARB     0x864B\n#define GL_PROGRAM_BINDING_ARB            0x8677\n#define GL_MAX_VERTEX_ATTRIBS_ARB         0x8869\n#define GL_VERTEX_ATTRIB_ARRAY_NORMALIZED_ARB 0x886A\n#define GL_PROGRAM_ERROR_STRING_ARB       0x8874\n#define GL_PROGRAM_FORMAT_ASCII_ARB       0x8875\n#define GL_PROGRAM_FORMAT_ARB             0x8876\n#define GL_PROGRAM_INSTRUCTIONS_ARB       0x88A0\n#define GL_MAX_PROGRAM_INSTRUCTIONS_ARB   0x88A1\n#define GL_PROGRAM_NATIVE_INSTRUCTIONS_ARB 0x88A2\n#define GL_MAX_PROGRAM_NATIVE_INSTRUCTIONS_ARB 0x88A3\n#define GL_PROGRAM_TEMPORARIES_ARB        0x88A4\n#define GL_MAX_PROGRAM_TEMPORARIES_ARB    0x88A5\n#define GL_PROGRAM_NATIVE_TEMPORARIES_ARB 0x88A6\n#define GL_MAX_PROGRAM_NATIVE_TEMPORARIES_ARB 0x88A7\n#define GL_PROGRAM_PARAMETERS_ARB         0x88A8\n#define GL_MAX_PROGRAM_PARAMETERS_ARB     0x88A9\n#define GL_PROGRAM_NATIVE_PARAMETERS_ARB  0x88AA\n#define GL_MAX_PROGRAM_NATIVE_PARAMETERS_ARB 0x88AB\n#define GL_PROGRAM_ATTRIBS_ARB            0x88AC\n#define GL_MAX_PROGRAM_ATTRIBS_ARB        0x88AD\n#define GL_PROGRAM_NATIVE_ATTRIBS_ARB     0x88AE\n#define GL_MAX_PROGRAM_NATIVE_ATTRIBS_ARB 0x88AF\n#define GL_PROGRAM_ADDRESS_REGISTERS_ARB  0x88B0\n#define GL_MAX_PROGRAM_ADDRESS_REGISTERS_ARB 0x88B1\n#define GL_PROGRAM_NATIVE_ADDRESS_REGISTERS_ARB 0x88B2\n#define GL_MAX_PROGRAM_NATIVE_ADDRESS_REGISTERS_ARB 0x88B3\n#define GL_MAX_PROGRAM_LOCAL_PARAMETERS_ARB 0x88B4\n#define GL_MAX_PROGRAM_ENV_PARAMETERS_ARB 0x88B5\n#define GL_PROGRAM_UNDER_NATIVE_LIMITS_ARB 0x88B6\n#define GL_TRANSPOSE_CURRENT_MATRIX_ARB   0x88B7\n#define GL_MATRIX0_ARB                    0x88C0\n#define GL_MATRIX1_ARB                    0x88C1\n#define GL_MATRIX2_ARB                    0x88C2\n#define GL_MATRIX3_ARB                    0x88C3\n#define GL_MATRIX4_ARB                    0x88C4\n#define GL_MATRIX5_ARB                    0x88C5\n#define GL_MATRIX6_ARB                    0x88C6\n#define GL_MATRIX7_ARB                    0x88C7\n#define GL_MATRIX8_ARB                    0x88C8\n#define GL_MATRIX9_ARB                    0x88C9\n#define GL_MATRIX10_ARB                   0x88CA\n#define GL_MATRIX11_ARB                   0x88CB\n#define GL_MATRIX12_ARB                   0x88CC\n#define GL_MATRIX13_ARB                   0x88CD\n#define GL_MATRIX14_ARB                   0x88CE\n#define GL_MATRIX15_ARB                   0x88CF\n#define GL_MATRIX16_ARB                   0x88D0\n#define GL_MATRIX17_ARB                   0x88D1\n#define GL_MATRIX18_ARB                   0x88D2\n#define GL_MATRIX19_ARB                   0x88D3\n#define GL_MATRIX20_ARB                   0x88D4\n#define GL_MATRIX21_ARB                   0x88D5\n#define GL_MATRIX22_ARB                   0x88D6\n#define GL_MATRIX23_ARB                   0x88D7\n#define GL_MATRIX24_ARB                   0x88D8\n#define GL_MATRIX25_ARB                   0x88D9\n#define GL_MATRIX26_ARB                   0x88DA\n#define GL_MATRIX27_ARB                   0x88DB\n#define GL_MATRIX28_ARB                   0x88DC\n#define GL_MATRIX29_ARB                   0x88DD\n#define GL_MATRIX30_ARB                   0x88DE\n#define GL_MATRIX31_ARB                   0x88DF\n#endif\n\n#ifndef GL_ARB_fragment_program\n#define GL_FRAGMENT_PROGRAM_ARB           0x8804\n#define GL_PROGRAM_ALU_INSTRUCTIONS_ARB   0x8805\n#define GL_PROGRAM_TEX_INSTRUCTIONS_ARB   0x8806\n#define GL_PROGRAM_TEX_INDIRECTIONS_ARB   0x8807\n#define GL_PROGRAM_NATIVE_ALU_INSTRUCTIONS_ARB 0x8808\n#define GL_PROGRAM_NATIVE_TEX_INSTRUCTIONS_ARB 0x8809\n#define GL_PROGRAM_NATIVE_TEX_INDIRECTIONS_ARB 0x880A\n#define GL_MAX_PROGRAM_ALU_INSTRUCTIONS_ARB 0x880B\n#define GL_MAX_PROGRAM_TEX_INSTRUCTIONS_ARB 0x880C\n#define GL_MAX_PROGRAM_TEX_INDIRECTIONS_ARB 0x880D\n#define GL_MAX_PROGRAM_NATIVE_ALU_INSTRUCTIONS_ARB 0x880E\n#define GL_MAX_PROGRAM_NATIVE_TEX_INSTRUCTIONS_ARB 0x880F\n#define GL_MAX_PROGRAM_NATIVE_TEX_INDIRECTIONS_ARB 0x8810\n#define GL_MAX_TEXTURE_COORDS_ARB         0x8871\n#define GL_MAX_TEXTURE_IMAGE_UNITS_ARB    0x8872\n#endif\n\n\n\n\n\n#ifndef GL_ARB_vertex_program\n#define GL_ARB_vertex_program 1\n//typedef void (APIENTRYP PFNGLVERTEXATTRIB1DARBPROC) (GLuint index, GLdouble x);\n//typedef void (APIENTRYP PFNGLVERTEXATTRIB1DVARBPROC) (GLuint index, const GLdouble *v);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB1FARBPROC) (GLuint index, GLfloat x);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB1FVARBPROC) (GLuint index, const GLfloat *v);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB1SARBPROC) (GLuint index, GLshort x);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB1SVARBPROC) (GLuint index, const GLshort *v);\n//typedef void (APIENTRYP PFNGLVERTEXATTRIB2DARBPROC) (GLuint index, GLdouble x, GLdouble y);\n//typedef void (APIENTRYP PFNGLVERTEXATTRIB2DVARBPROC) (GLuint index, const GLdouble *v);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB2FARBPROC) (GLuint index, GLfloat x, GLfloat y);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB2FVARBPROC) (GLuint index, const GLfloat *v);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB2SARBPROC) (GLuint index, GLshort x, GLshort y);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB2SVARBPROC) (GLuint index, const GLshort *v);\n//typedef void (APIENTRYP PFNGLVERTEXATTRIB3DARBPROC) (GLuint index, GLdouble x, GLdouble y, GLdouble z);\n//typedef void (APIENTRYP PFNGLVERTEXATTRIB3DVARBPROC) (GLuint index, const GLdouble *v);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB3FARBPROC) (GLuint index, GLfloat x, GLfloat y, GLfloat z);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB3FVARBPROC) (GLuint index, const GLfloat *v);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB3SARBPROC) (GLuint index, GLshort x, GLshort y, GLshort z);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB3SVARBPROC) (GLuint index, const GLshort *v);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB4NBVARBPROC) (GLuint index, const GLbyte *v);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB4NIVARBPROC) (GLuint index, const GLint *v);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB4NSVARBPROC) (GLuint index, const GLshort *v);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB4NUBARBPROC) (GLuint index, GLubyte x, GLubyte y, GLubyte z, GLubyte w);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB4NUBVARBPROC) (GLuint index, const GLubyte *v);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB4NUIVARBPROC) (GLuint index, const GLuint *v);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB4NUSVARBPROC) (GLuint index, const GLushort *v);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB4BVARBPROC) (GLuint index, const GLbyte *v);\n//typedef void (APIENTRYP PFNGLVERTEXATTRIB4DARBPROC) (GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w);\n//typedef void (APIENTRYP PFNGLVERTEXATTRIB4DVARBPROC) (GLuint index, const GLdouble *v);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB4FARBPROC) (GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB4FVARBPROC) (GLuint index, const GLfloat *v);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB4IVARBPROC) (GLuint index, const GLint *v);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB4SARBPROC) (GLuint index, GLshort x, GLshort y, GLshort z, GLshort w);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB4SVARBPROC) (GLuint index, const GLshort *v);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB4UBVARBPROC) (GLuint index, const GLubyte *v);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB4UIVARBPROC) (GLuint index, const GLuint *v);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB4USVARBPROC) (GLuint index, const GLushort *v);\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBPOINTERARBPROC) (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid *pointer);\ntypedef void (APIENTRYP PFNGLENABLEVERTEXATTRIBARRAYARBPROC) (GLuint index);\ntypedef void (APIENTRYP PFNGLDISABLEVERTEXATTRIBARRAYARBPROC) (GLuint index);\ntypedef void (APIENTRYP PFNGLPROGRAMSTRINGARBPROC) (GLenum target, GLenum format, GLsizei len, const GLvoid *string);\ntypedef void (APIENTRYP PFNGLBINDPROGRAMARBPROC) (GLenum target, GLuint program);\ntypedef void (APIENTRYP PFNGLDELETEPROGRAMSARBPROC) (GLsizei n, const GLuint *programs);\ntypedef void (APIENTRYP PFNGLGENPROGRAMSARBPROC) (GLsizei n, GLuint *programs);\n//typedef void (APIENTRYP PFNGLPROGRAMENVPARAMETER4DARBPROC) (GLenum target, GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w);\n//typedef void (APIENTRYP PFNGLPROGRAMENVPARAMETER4DVARBPROC) (GLenum target, GLuint index, const GLdouble *params);\ntypedef void (APIENTRYP PFNGLPROGRAMENVPARAMETER4FARBPROC) (GLenum target, GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w);\ntypedef void (APIENTRYP PFNGLPROGRAMENVPARAMETER4FVARBPROC) (GLenum target, GLuint index, const GLfloat *params);\n//typedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETER4DARBPROC) (GLenum target, GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w);\n//typedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETER4DVARBPROC) (GLenum target, GLuint index, const GLdouble *params);\ntypedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETER4FARBPROC) (GLenum target, GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w);\ntypedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETER4FVARBPROC) (GLenum target, GLuint index, const GLfloat *params);\n//typedef void (APIENTRYP PFNGLGETPROGRAMENVPARAMETERDVARBPROC) (GLenum target, GLuint index, GLdouble *params);\ntypedef void (APIENTRYP PFNGLGETPROGRAMENVPARAMETERFVARBPROC) (GLenum target, GLuint index, GLfloat *params);\n//typedef void (APIENTRYP PFNGLGETPROGRAMLOCALPARAMETERDVARBPROC) (GLenum target, GLuint index, GLdouble *params);\ntypedef void (APIENTRYP PFNGLGETPROGRAMLOCALPARAMETERFVARBPROC) (GLenum target, GLuint index, GLfloat *params);\ntypedef void (APIENTRYP PFNGLGETPROGRAMIVARBPROC) (GLenum target, GLenum pname, GLint *params);\ntypedef void (APIENTRYP PFNGLGETPROGRAMSTRINGARBPROC) (GLenum target, GLenum pname, GLvoid *string);\n//typedef void (APIENTRYP PFNGLGETVERTEXATTRIBDVARBPROC) (GLuint index, GLenum pname, GLdouble *params);\ntypedef void (APIENTRYP PFNGLGETVERTEXATTRIBFVARBPROC) (GLuint index, GLenum pname, GLfloat *params);\ntypedef void (APIENTRYP PFNGLGETVERTEXATTRIBIVARBPROC) (GLuint index, GLenum pname, GLint *params);\ntypedef void (APIENTRYP PFNGLGETVERTEXATTRIBPOINTERVARBPROC) (GLuint index, GLenum pname, GLvoid* *pointer);\ntypedef GLboolean (APIENTRYP PFNGLISPROGRAMARBPROC) (GLuint program);\n#endif\n\n#ifndef GL_ARB_fragment_program\n#define GL_ARB_fragment_program 1\n/* All ARB_fragment_program entry points are shared with ARB_vertex_program. */\n#endif\n\n\n\n\n#ifndef GL_ARB_shader_objects\n#define GL_ARB_shader_objects 1\n#define GL_PROGRAM_OBJECT_ARB\t\t\t0x8B40\n#define GL_OBJECT_TYPE_ARB\t\t\t\t0x8B4E\n#define GL_OBJECT_SUBTYPE_ARB\t\t\t0x8B4F\n#define GL_OBJECT_DELETE_STATUS_ARB\t\t0x8B80\n#define GL_OBJECT_COMPILE_STATUS_ARB\t0x8B81\n#define GL_OBJECT_LINK_STATUS_ARB\t\t0x8B82\n#define GL_OBJECT_VALIDATE_STATUS_ARB\t0x8B83\n#define GL_OBJECT_INFO_LOG_LENGTH_ARB\t0x8B84\n#define GL_OBJECT_ATTACHED_OBJECTS_ARB\t0x8B85\n#define GL_OBJECT_ACTIVE_UNIFORMS_ARB\t0x8B86\n#define GL_OBJECT_ACTIVE_UNIFORM_MAX_LENGTH_ARB\t0x8B87\n#define GL_OBJECT_SHADER_SOURCE_LENGTH_ARB\t\t0x8B88\n#define GL_SHADER_OBJECT_ARB\t\t\t0x8B48\n#define GL_FLOAT\t\t\t\t\t\t0x1406\n#define GL_FLOAT_VEC2_ARB\t\t\t\t0x8B50\n#define GL_FLOAT_VEC3_ARB\t\t\t\t0x8B51\n#define GL_FLOAT_VEC4_ARB\t\t\t\t0x8B52\n//#define GL_INT\t\t\t\t\t\t\t0x1404\n#define GL_INT_VEC2_ARB\t\t\t\t\t0x8B53\n#define GL_INT_VEC3_ARB\t\t\t\t\t0x8B54\n#define GL_INT_VEC4_ARB\t\t\t\t\t0x8B55\n#define GL_BOOL_ARB\t\t\t\t\t\t0x8B56\n#define GL_BOOL_VEC2_ARB\t\t\t\t0x8B57\n#define GL_BOOL_VEC3_ARB\t\t\t\t0x8B58\n#define GL_BOOL_VEC4_ARB\t\t\t\t0x8B59\n#define GL_FLOAT_MAT2_ARB\t\t\t\t0x8B5A\n#define GL_FLOAT_MAT3_ARB\t\t\t\t0x8B5B\n#define GL_FLOAT_MAT4_ARB\t\t\t\t0x8B5C\n#define GL_SAMPLER_1D_ARB\t\t\t\t0x8B5D\n#define GL_SAMPLER_2D_ARB\t\t\t\t0x8B5E\n#define GL_SAMPLER_3D_ARB\t\t\t\t0x8B5F\n#define GL_SAMPLER_CUBE_ARB\t\t\t\t0x8B60\n#define GL_SAMPLER_1D_SHADOW_ARB\t\t0x8B61\n#define GL_SAMPLER_2D_SHADOW_ARB\t\t0x8B62\n#define GL_SAMPLER_2D_RECT_ARB\t\t\t0x8B63\n#define GL_SAMPLER_2D_RECT_SHADOW_ARB\t0x8B64\n// dont know if these two should go somewhere better:\n#ifdef __APPLE__\ntypedef void *GLhandleARB; //Royally Fucked.\n#else\ntypedef unsigned int GLhandleARB;\n#endif\ntypedef char         GLcharARB;\ntypedef void\t\t(APIENTRYP PFNGLDELETEOBJECTARBPROC)\t\t(GLhandleARB obj);\ntypedef GLhandleARB\t(APIENTRYP PFNGLGETHANDLEARBPROC)\t\t\t(GLenum pname);\ntypedef void\t\t(APIENTRYP PFNGLDETACHOBJECTARBPROC)\t\t(GLhandleARB containerObj, GLhandleARB attachedObj);\ntypedef GLhandleARB\t(APIENTRYP PFNGLCREATESHADEROBJECTARBPROC)\t(GLenum shaderType);\ntypedef void\t\t(APIENTRYP PFNGLSHADERSOURCEARBPROC)\t\t(GLhandleARB shaderObj, GLsizei count, const GLcharARB* *string, const GLint *length);\ntypedef void\t\t(APIENTRYP PFNGLCOMPILESHADERARBPROC)\t\t(GLhandleARB shaderObj);\ntypedef GLhandleARB\t(APIENTRYP PFNGLCREATEPROGRAMOBJECTARBPROC)\t(void);\ntypedef void\t\t(APIENTRYP PFNGLATTACHOBJECTARBPROC)\t\t(GLhandleARB containerObj, GLhandleARB obj);\ntypedef void\t\t(APIENTRYP PFNGLLINKPROGRAMARBPROC)\t\t\t(GLhandleARB programObj);\ntypedef void\t\t(APIENTRYP PFNGLUSEPROGRAMOBJECTARBPROC)\t(GLhandleARB programObj);\ntypedef void\t\t(APIENTRYP PFNGLVALIDATEPROGRAMARBPROC)\t\t(GLhandleARB programObj);\ntypedef void\t\t(APIENTRYP PFNGLUNIFORM1FARBPROC)\t\t\t(GLint location, GLfloat v0);\ntypedef void\t\t(APIENTRYP PFNGLUNIFORM2FARBPROC)\t\t\t(GLint location, GLfloat v0, GLfloat v1);\ntypedef void\t\t(APIENTRYP PFNGLUNIFORM3FARBPROC)\t\t\t(GLint location, GLfloat v0, GLfloat v1, GLfloat v2);\ntypedef void\t\t(APIENTRYP PFNGLUNIFORM4FARBPROC)\t\t\t(GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3);\ntypedef void\t\t(APIENTRYP PFNGLUNIFORM1IARBPROC)\t\t\t(GLint location, GLint v0);\ntypedef void\t\t(APIENTRYP PFNGLUNIFORM2IARBPROC)\t\t\t(GLint location, GLint v0, GLint v1);\ntypedef void\t\t(APIENTRYP PFNGLUNIFORM3IARBPROC)\t\t\t(GLint location, GLint v0, GLint v1, GLint v2);\ntypedef void\t\t(APIENTRYP PFNGLUNIFORM4IARBPROC)\t\t\t(GLint location, GLint v0, GLint v1, GLint v2, GLint v3);\ntypedef void\t\t(APIENTRYP PFNGLUNIFORM1FVARBPROC)\t\t\t(GLint location, GLsizei count, GLfloat *value);\ntypedef void\t\t(APIENTRYP PFNGLUNIFORM2FVARBPROC)\t\t\t(GLint location, GLsizei count, GLfloat *value);\ntypedef void\t\t(APIENTRYP PFNGLUNIFORM3FVARBPROC)\t\t\t(GLint location, GLsizei count, GLfloat *value);\ntypedef void\t\t(APIENTRYP PFNGLUNIFORM4FVARBPROC)\t\t\t(GLint location, GLsizei count, GLfloat *value);\ntypedef void\t\t(APIENTRYP PFNGLUNIFORM1IVARBPROC)\t\t\t(GLint location, GLsizei count, GLint *value);\ntypedef void\t\t(APIENTRYP PFNGLUNIFORM2IVARBPROC)\t\t\t(GLint location, GLsizei count, GLint *value);\ntypedef void\t\t(APIENTRYP PFNGLUNIFORM3IVARBPROC)\t\t\t(GLint location, GLsizei count, GLint *value);\ntypedef void\t\t(APIENTRYP PFNGLUNIFORM4IVARBPROC)\t\t\t(GLint location, GLsizei count, GLint *value);\ntypedef void\t\t(APIENTRYP PFNGLUNIFORMMATRIX2FVARBPROC)\t(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);\ntypedef void\t\t(APIENTRYP PFNGLUNIFORMMATRIX3FVARBPROC)\t(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);\ntypedef void\t\t(APIENTRYP PFNGLUNIFORMMATRIX4FVARBPROC)\t(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);\ntypedef void        (APIENTRYP PFNGLGETOBJECTPARAMETERFVARBPROC) (GLhandleARB obj, GLenum pname, GLfloat *params);\ntypedef void        (APIENTRYP PFNGLGETOBJECTPARAMETERIVARBPROC) (GLhandleARB obj, GLenum pname, GLint *params);\ntypedef void\t\t(APIENTRYP PFNGLGETINFOLOGARBPROC)\t\t\t(GLhandleARB obj, GLsizei maxLength, GLsizei *length, GLcharARB *infoLog);\ntypedef void\t\t(APIENTRYP PFNGLGETATTACHEDOBJECTSARB)\t\t(GLhandleARB containerObj, GLsizei maxCount, GLsizei *count, GLhandleARB *obj);\ntypedef GLint\t\t(APIENTRYP PFNGLGETUNIFORMLOCATIONARBPROC)\t(GLhandleARB programObj, const GLcharARB *name);\ntypedef void\t\t(APIENTRYP PFNGLGETACTIVEUNIFORMARBPROC)\t(GLhandleARB programObj, GLuint index, GLsizei maxLength, GLsizei *length, GLsizei *size, GLenum *type, GLcharARB *name);\ntypedef void\t\t(APIENTRYP PFNGLGETUNIFORMFVARBPROC)\t\t(GLhandleARB programObj, GLint location, GLfloat *parms);\ntypedef void\t\t(APIENTRYP PFNGLGETUNIFORMIVARBPROC)\t\t(GLhandleARB programObj, GLint location, GLint *parms);\ntypedef void\t\t(APIENTRYP PFNGLGETSHADERSOURCEARBPROC)\t\t(GLhandleARB obj, GLsizei maxLength, GLsizei *length, GLcharARB *source);\n#endif // GL_ARB_shader_objects\n\n#ifndef GL_ARB_vertex_shader\n#define GL_VERTEX_SHADER_ARB\t\t\t\t\t\t0x8B31\n#define GL_MAX_VERTEX_UNIFORM_COMPONENTS_ARB\t\t0x8B4A\n#define GL_MAX_VARYING_FLOATS_ARB\t\t\t\t\t0x8B4B\n#define GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS_ARB\t\t0x8B4C\n#define GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS_ARB\t\t0x8B4D\n#define GL_OBJECT_ACTIVE_ATTRIBUTES_ARB\t\t\t\t0x8B89\n#define GL_OBJECT_ACTIVE_ATTRIBUTE_MAX_LENGTH_ARB\t0x8B8A\n#endif\n\n#ifndef GL_GEOMETRY_SHADER_ARB\n#define GL_GEOMETRY_SHADER_ARB 0x8DD9\n#endif\n\n#ifndef GL_PATCH_VERTICES_ARB //GL_ARB_tessellation_shader lacks _ARB postfix.\n#define GL_PATCHES_ARB\t\t\t\t\t\t\t\t0xE\n#define GL_PATCH_VERTICES_ARB\t\t\t\t\t\t0x8E72\n#define GL_TESS_EVALUATION_SHADER_ARB\t\t\t\t0x8E87\n#define GL_TESS_CONTROL_SHADER_ARB\t\t\t\t\t0x8E88\n#endif\n\n#ifndef GL_ARB_fragment_shader\n#define GL_FRAGMENT_SHADER_ARB\t\t\t\t\t\t0x8B30\n#define GL_MAX_FRAGMENT_UNIFORM_COMPONENTS_ARB\t\t0x8B49\n#endif\n\n#define WGL_SAMPLE_BUFFERS_ARB\t0x2041\n#define WGL_SAMPLES_ARB\t\t0x2042\n#define GL_MULTISAMPLE_ARB 0x809D\n\n#ifndef GL_FRAMEBUFFER_SRGB\n#define GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB\t\t\t0x20B2\n#define WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB\t\t\t0x20A9\n#define GL_FRAMEBUFFER_SRGB\t\t\t\t\t\t\t0x8DB9\n#endif\n#ifndef GL_FRAMEBUFFER_SRGB_CAPABLE_EXT\n#define GL_FRAMEBUFFER_SRGB_CAPABLE_EXT\t\t\t\t0x8DBA\n#endif\n\n\n#ifndef GL_COMPRESSED_RGB_S3TC_DXT1_EXT\n#define GL_COMPRESSED_RGB_S3TC_DXT1_EXT\t\t\t\t0x83F0\n#define GL_COMPRESSED_RGBA_S3TC_DXT1_EXT\t\t\t0x83F1\n#define GL_COMPRESSED_RGBA_S3TC_DXT3_EXT\t\t\t0x83F2\n#define GL_COMPRESSED_RGBA_S3TC_DXT5_EXT\t\t\t0x83F3\n#endif\n#ifndef GL_COMPRESSED_RED_RGTC1\n#define\tGL_COMPRESSED_RED_RGTC1\t\t\t\t\t\t0x8DBB\n#define\tGL_COMPRESSED_SIGNED_RED_RGTC1\t\t\t\t0x8DBC\n#define\tGL_COMPRESSED_RG_RGTC2\t\t\t\t\t\t0x8DBD\n#define\tGL_COMPRESSED_SIGNED_RG_RGTC2\t\t\t\t0x8DBE\n#endif\n#ifndef GL_COMPRESSED_RGBA_BPTC_UNORM_ARB\n#define GL_COMPRESSED_RGBA_BPTC_UNORM_ARB\t\t\t0x8E8C\n#define GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB\t\t0x8E8D\n#define GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB\t\t0x8E8E\n#define GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB\t0x8E8F\n#endif\n\n#ifndef GL_EXT_texture_sRGB\n#define GL_EXT_texture_sRGB 1\n#define GL_SRGB_EXT                       0x8C40\n#define GL_SRGB8_EXT                      0x8C41\n#define GL_SRGB_ALPHA_EXT                 0x8C42\n#define GL_SRGB8_ALPHA8_EXT               0x8C43\n#define GL_SLUMINANCE_ALPHA_EXT           0x8C44\n#define GL_SLUMINANCE8_ALPHA8_EXT         0x8C45\n#define GL_SLUMINANCE_EXT                 0x8C46\n#define GL_SLUMINANCE8_EXT                0x8C47\n#define GL_COMPRESSED_SRGB_EXT            0x8C48\n#define GL_COMPRESSED_SRGB_ALPHA_EXT      0x8C49\n#define GL_COMPRESSED_SLUMINANCE_EXT      0x8C4A\n#define GL_COMPRESSED_SLUMINANCE_ALPHA_EXT 0x8C4B\n#define GL_COMPRESSED_SRGB_S3TC_DXT1_EXT  0x8C4C\n#define GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT 0x8C4D\n#define GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT 0x8C4E\n#define GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT 0x8C4F\n#endif /* GL_EXT_texture_sRGB */\n\n#ifndef GL_EXT_texture_sRGB_R8\n#define GL_EXT_texture_sRGB_R8\n#define GL_SR8_EXT\t\t\t\t\t\t\t0x8FBD\n#endif\n#ifndef GL_EXT_texture_sRGB_RG8\n#define GL_EXT_texture_sRGB_RG8\n#define GL_SRG8_EXT\t\t\t\t\t\t\t0x8FBE\n#endif\n\n#ifndef GL_RGB9_E5\n#define GL_RGB9_E5                        0x8C3D\t/*opengl 3.0*/\n#endif\n#ifndef GL_RG\n#define GL_RG                             0x8227\n#define GL_R8                             0x8229\t/*opengl 3.0*/\n#define GL_R16                            0x822A\t/*opengl 3.0*/\n#define GL_RG8                            0x822B\t/*opengl 3.0*/\n#endif\n#ifndef GL_RG8_SNORM\n#define GL_R8_SNORM                       0x8F94\t/*opengl 3.1*/\n#define GL_RG8_SNORM                      0x8F95\t/*opengl 3.1*/\n#endif\n\n#ifndef GL_R11F_G11F_B10F\n#define GL_R11F_G11F_B10F\t\t\t\t  0x8C3A\n#define GL_UNSIGNED_INT_10F_11F_11F_REV\t  0x8C3B\n#endif\n\n#ifndef GL_TEXTURE_SWIZZLE_R\n#define GL_TEXTURE_SWIZZLE_R              0x8E42\n#define GL_TEXTURE_SWIZZLE_G              0x8E43\n#define GL_TEXTURE_SWIZZLE_B              0x8E44\n#define GL_TEXTURE_SWIZZLE_A              0x8E45\n#endif\n\n#ifndef GL_ETC1_RGB8_OES\n#define GL_ETC1_RGB8_OES 0x8D64\t//4*4 blocks of 8 bytes\n#endif\n#ifndef GL_COMPRESSED_RGB8_ETC2\n#define GL_COMPRESSED_R11_EAC\t\t\t\t\t\t\t0x9270\n#define GL_COMPRESSED_SIGNED_R11_EAC\t\t\t\t\t0x9271\n#define GL_COMPRESSED_RG11_EAC\t\t\t\t\t\t\t0x9272\n#define GL_COMPRESSED_SIGNED_RG11_EAC\t\t\t\t\t0x9273\n#define GL_COMPRESSED_RGB8_ETC2\t\t\t\t\t\t\t0x9274\t//4*4 blocks of 8 bytes. also accepts valid etc1 images\n#define GL_COMPRESSED_SRGB8_ETC2\t\t\t\t\t\t0x9275\n#define GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2\t\t0x9276\t//\n#define GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2\t0x9277\n#define GL_COMPRESSED_RGBA8_ETC2_EAC\t\t\t\t\t0x9278\t//4*4 blocks of 8+8 bytes.\n#define GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC\t\t\t\t0x9279\n#endif\n#ifndef GL_COMPRESSED_RGBA_ASTC_4x4_KHR\n#define GL_COMPRESSED_RGBA_ASTC_4x4_KHR\t\t\t\t\t0x93B0\n#define GL_COMPRESSED_RGBA_ASTC_5x4_KHR\t\t\t\t\t0x93B1\n#define GL_COMPRESSED_RGBA_ASTC_5x5_KHR\t\t\t\t\t0x93B2\n#define GL_COMPRESSED_RGBA_ASTC_6x5_KHR\t\t\t\t\t0x93B3\n#define GL_COMPRESSED_RGBA_ASTC_6x6_KHR\t\t\t\t\t0x93B4\n#define GL_COMPRESSED_RGBA_ASTC_8x5_KHR\t\t\t\t\t0x93B5\n#define GL_COMPRESSED_RGBA_ASTC_8x6_KHR\t\t\t\t\t0x93B6\n#define GL_COMPRESSED_RGBA_ASTC_8x8_KHR\t\t\t\t\t0x93B7\n#define GL_COMPRESSED_RGBA_ASTC_10x5_KHR\t\t\t\t0x93B8\n#define GL_COMPRESSED_RGBA_ASTC_10x6_KHR\t\t\t\t0x93B9\n#define GL_COMPRESSED_RGBA_ASTC_10x8_KHR\t\t\t\t0x93BA\n#define GL_COMPRESSED_RGBA_ASTC_10x10_KHR\t\t\t\t0x93BB\n#define GL_COMPRESSED_RGBA_ASTC_12x10_KHR\t\t\t\t0x93BC\n#define GL_COMPRESSED_RGBA_ASTC_12x12_KHR\t\t\t\t0x93BD\n#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR\t\t\t0x93D0\n#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR\t\t\t0x93D1\n#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR\t\t\t0x93D2\n#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR\t\t\t0x93D3\n#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR\t\t\t0x93D4\n#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR\t\t\t0x93D5\n#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR\t\t\t0x93D6\n#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR\t\t\t0x93D7\n#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR\t\t0x93D8\n#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR\t\t0x93D9\n#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR\t\t0x93DA\n#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR\t\t0x93DB\n#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR\t\t0x93DC\n#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR\t\t0x93DD\n#endif\n#ifndef GL_COMPRESSED_RGBA_ASTC_3x3x3_OES\n#define GL_COMPRESSED_RGBA_ASTC_3x3x3_OES          0x93C0\n#define GL_COMPRESSED_RGBA_ASTC_4x3x3_OES          0x93C1\n#define GL_COMPRESSED_RGBA_ASTC_4x4x3_OES          0x93C2\n#define GL_COMPRESSED_RGBA_ASTC_4x4x4_OES          0x93C3\n#define GL_COMPRESSED_RGBA_ASTC_5x4x4_OES          0x93C4\n#define GL_COMPRESSED_RGBA_ASTC_5x5x4_OES          0x93C5\n#define GL_COMPRESSED_RGBA_ASTC_5x5x5_OES          0x93C6\n#define GL_COMPRESSED_RGBA_ASTC_6x5x5_OES          0x93C7\n#define GL_COMPRESSED_RGBA_ASTC_6x6x5_OES          0x93C8\n#define GL_COMPRESSED_RGBA_ASTC_6x6x6_OES          0x93C9\n#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_3x3x3_OES  0x93E0\n#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x3x3_OES  0x93E1\n#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4x3_OES  0x93E2\n#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4x4_OES  0x93E3\n#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4x4_OES  0x93E4\n#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5x4_OES  0x93E5\n#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5x5_OES  0x93E6\n#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5x5_OES  0x93E7\n#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6x5_OES  0x93E8\n#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6x6_OES  0x93E9\n#endif\n\n#ifndef GL_ARB_pixel_buffer_object\n#define GL_PIXEL_PACK_BUFFER_ARB                        0x88EB\n#define GL_PIXEL_UNPACK_BUFFER_ARB                      0x88EC\n#endif\n\n#ifndef GL_ARB_vertex_buffer_object\n#define GL_BUFFER_SIZE_ARB                0x8764\n#define GL_BUFFER_USAGE_ARB               0x8765\n#define GL_ARRAY_BUFFER_ARB               0x8892\n#define GL_ELEMENT_ARRAY_BUFFER_ARB       0x8893\n#define GL_ARRAY_BUFFER_BINDING_ARB       0x8894\n#define GL_ELEMENT_ARRAY_BUFFER_BINDING_ARB 0x8895\n#define GL_VERTEX_ARRAY_BUFFER_BINDING_ARB 0x8896\n#define GL_NORMAL_ARRAY_BUFFER_BINDING_ARB 0x8897\n#define GL_COLOR_ARRAY_BUFFER_BINDING_ARB 0x8898\n#define GL_INDEX_ARRAY_BUFFER_BINDING_ARB 0x8899\n#define GL_TEXTURE_COORD_ARRAY_BUFFER_BINDING_ARB 0x889A\n#define GL_EDGE_FLAG_ARRAY_BUFFER_BINDING_ARB 0x889B\n#define GL_SECONDARY_COLOR_ARRAY_BUFFER_BINDING_ARB 0x889C\n#define GL_FOG_COORDINATE_ARRAY_BUFFER_BINDING_ARB 0x889D\n#define GL_WEIGHT_ARRAY_BUFFER_BINDING_ARB 0x889E\n#define GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING_ARB 0x889F\n#define GL_READ_ONLY_ARB                  0x88B8\n#define GL_WRITE_ONLY_ARB                 0x88B9\n#define GL_READ_WRITE_ARB                 0x88BA\n#define GL_BUFFER_ACCESS_ARB              0x88BB\n#define GL_BUFFER_MAPPED_ARB              0x88BC\n#define GL_BUFFER_MAP_POINTER_ARB         0x88BD\n#define GL_STREAM_DRAW_ARB                0x88E0\n#define GL_STREAM_READ_ARB                0x88E1\n#define GL_STREAM_COPY_ARB                0x88E2\n#define GL_STATIC_DRAW_ARB                0x88E4\n#define GL_STATIC_READ_ARB                0x88E5\n#define GL_STATIC_COPY_ARB                0x88E6\n#define GL_DYNAMIC_DRAW_ARB               0x88E8\n#define GL_DYNAMIC_READ_ARB               0x88E9\n#define GL_DYNAMIC_COPY_ARB               0x88EA\n#endif\n\n\n\n\n#ifndef GL_SGIS_generate_mipmap\n#define GL_SGIS_generate_mipmap 1\n\n#define GL_GENERATE_MIPMAP_SGIS           0x8191\n#define GL_GENERATE_MIPMAP_HINT_SGIS      0x8192\n#endif\n\n\n#ifndef GL_EXT_compiled_vertex_array\n#define GL_ARRAY_ELEMENT_LOCK_FIRST_EXT   0x81A8\n#define GL_ARRAY_ELEMENT_LOCK_COUNT_EXT   0x81A9\n\n#define GL_EXT_compiled_vertex_array 1\n#ifdef GL_GLEXT_PROTOTYPES\nextern void APIENTRY glLockArraysEXT (GLint, GLsizei);\nextern void APIENTRY glUnlockArraysEXT (void);\n#endif /* GL_GLEXT_PROTOTYPES */\ntypedef void (APIENTRY * PFNGLLOCKARRAYSEXTPROC) (GLint first, GLsizei count);\ntypedef void (APIENTRY * PFNGLUNLOCKARRAYSEXTPROC) (void);\n#endif\n\n\n#ifndef GL_EXT_texture_compression_astc_decode_mode\n#define GL_TEXTURE_ASTC_DECODE_PRECISION_EXT\t0x8F69\n#endif\n\n#ifndef GL_EXT_framebuffer_object\n#define GL_INVALID_FRAMEBUFFER_OPERATION_EXT 0x0506\n#define GL_MAX_RENDERBUFFER_SIZE_EXT      0x84E8\n#define GL_FRAMEBUFFER_BINDING_EXT        0x8CA6\n#define GL_RENDERBUFFER_BINDING_EXT       0x8CA7\n#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE_EXT 0x8CD0\n#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME_EXT 0x8CD1\n#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL_EXT 0x8CD2\n#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE_EXT 0x8CD3\n#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_3D_ZOFFSET_EXT 0x8CD4\n#define GL_FRAMEBUFFER_COMPLETE_EXT       0x8CD5\n#define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT 0x8CD6\n#define GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT 0x8CD7\n#define GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT 0x8CD9\n#define GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT 0x8CDA\n#define GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT 0x8CDB\n#define GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT 0x8CDC\n#define GL_FRAMEBUFFER_UNSUPPORTED_EXT    0x8CDD\n#define GL_MAX_COLOR_ATTACHMENTS_EXT      0x8CDF\n#define GL_COLOR_ATTACHMENT0_EXT          0x8CE0\n#define GL_COLOR_ATTACHMENT1_EXT          0x8CE1\n#define GL_COLOR_ATTACHMENT2_EXT          0x8CE2\n#define GL_COLOR_ATTACHMENT3_EXT          0x8CE3\n#define GL_COLOR_ATTACHMENT4_EXT          0x8CE4\n#define GL_COLOR_ATTACHMENT5_EXT          0x8CE5\n#define GL_COLOR_ATTACHMENT6_EXT          0x8CE6\n#define GL_COLOR_ATTACHMENT7_EXT          0x8CE7\n#define GL_COLOR_ATTACHMENT8_EXT          0x8CE8\n#define GL_COLOR_ATTACHMENT9_EXT          0x8CE9\n#define GL_COLOR_ATTACHMENT10_EXT         0x8CEA\n#define GL_COLOR_ATTACHMENT11_EXT         0x8CEB\n#define GL_COLOR_ATTACHMENT12_EXT         0x8CEC\n#define GL_COLOR_ATTACHMENT13_EXT         0x8CED\n#define GL_COLOR_ATTACHMENT14_EXT         0x8CEE\n#define GL_COLOR_ATTACHMENT15_EXT         0x8CEF\n#define GL_DEPTH_ATTACHMENT_EXT           0x8D00\n#define GL_STENCIL_ATTACHMENT_EXT         0x8D20\n#define GL_FRAMEBUFFER_EXT                0x8D40\n#define GL_RENDERBUFFER_EXT               0x8D41\n#define GL_RENDERBUFFER_WIDTH_EXT         0x8D42\n#define GL_RENDERBUFFER_HEIGHT_EXT        0x8D43\n#define GL_RENDERBUFFER_INTERNAL_FORMAT_EXT 0x8D44\n#define GL_STENCIL_INDEX1_EXT             0x8D46\n#define GL_STENCIL_INDEX4_EXT             0x8D47\n#define GL_STENCIL_INDEX8_EXT             0x8D48\n#define GL_STENCIL_INDEX16_EXT            0x8D49\n#define GL_RENDERBUFFER_RED_SIZE_EXT      0x8D50\n#define GL_RENDERBUFFER_GREEN_SIZE_EXT    0x8D51\n#define GL_RENDERBUFFER_BLUE_SIZE_EXT     0x8D52\n#define GL_RENDERBUFFER_ALPHA_SIZE_EXT    0x8D53\n#define GL_RENDERBUFFER_DEPTH_SIZE_EXT    0x8D54\n#define GL_RENDERBUFFER_STENCIL_SIZE_EXT  0x8D55\n#endif\n\n#ifndef GL_ARB_framebuffer_object\n#define GL_DRAW_FRAMEBUFFER_ARB           0x8CA9\n#define GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE 0x8217 \n#define GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING_ARB 0x8210\n#endif\n\n#ifndef GL_VERSION_3_0\n#define GL_MAJOR_VERSION                  0x821B\n#define GL_MINOR_VERSION                  0x821C\n#define GL_NUM_EXTENSIONS                 0x821D\n#endif\n\n//GL_ARB_robustness, core in 4.5\n#ifndef GL_GUILTY_CONTEXT_RESET\n//#define GL_NO_ERROR\t\t\t\t\t\t0x0000\n#define GL_GUILTY_CONTEXT_RESET\t\t\t\t0x8253\n#define GL_INNOCENT_CONTEXT_RESET\t\t\t0x8254\n#define GL_UNKNOWN_CONTEXT_RESET\t\t\t0x8255\n#endif\n\n#ifndef GL_DEPTH_CLAMP_ARB\n#define GL_DEPTH_CLAMP_ARB\t\t\t      0x864F\n#endif\n\n#ifndef GL_TEXTURE_BASE_LEVEL\t//GL_SGIS_texture_lod (core gl 1.2)\n#define GL_TEXTURE_BASE_LEVEL 0x813c \n#define GL_TEXTURE_MAX_LEVEL 0x813d \n#endif\n\n#ifndef GL_TEXTURE_LOD_BIAS\n#define GL_TEXTURE_LOD_BIAS\t\t\t\t0x8501\t//gl1.4\n#endif\n\n#ifndef GL_RGBA16\n#define GL_RGBA16\t\t\t\t\t\t0x805B\t//gl1.1, but not in gles.\n#endif\n#ifndef GL_RGBA16F\n#define GL_RGBA16F\t\t\t\t\t\t0x881A\n#define GL_RGBA32F\t\t\t\t\t\t0x8814\n#endif\n#ifndef GL_RGB32F\n#define GL_RGB32F\t\t\t\t\t\t0x8815\n#endif\n#ifndef GL_R16F\n#define GL_R16F\t\t\t\t\t\t\t0x822D\n#define GL_R32F\t\t\t\t\t\t\t0x822E\n#endif\n\n#ifndef GL_RED\n//gles2 does not support swizzles, but gles3 does\n#define GL_RED\t\t\t\t\t\t\t\t0x1903\n#define GL_GREEN\t\t\t\t\t\t\t0x1904\n#define GL_BLUE\t\t\t\t\t\t\t\t0x1905\n#endif\n#ifndef GL_RGBA8\n//gles2 does not support sized formats, but gl1.1 and gles3 do.\n#define GL_RGBA8\t\t\t\t\t\t\t0x8058\n#define GL_RGB8\t\t\t\t\t\t\t\t0x8051\n#define GL_RGB10_A2\t\t\t\t\t\t\t0x8059\n#define GL_RGB5\t\t\t\t\t\t\t\t0x8050\t//note: not in gles3. a poor-man's substitute for rgb565\n#define GL_RGBA4\t\t\t\t\t\t\t0x8056\n#define GL_RGB5_A1\t\t\t\t\t\t\t0x8057\n#endif\n#ifndef GL_LUMINANCE8\n#define GL_LUMINANCE8\t\t\t\t\t\t0x8040\t//not in gles2, nor gl3core (use gl_red+swizzles for gles3)\n#define GL_LUMINANCE8_ALPHA8\t\t\t\t0x8045\t//not in gles2, nor gl3core (use gl_red+swizzles for gles3)\n#endif\n\n#ifndef GL_LINE\n#define GL_LINE\t\t\t\t\t\t\t\t0x1B01\n#endif\n#ifndef GL_POLYGON_OFFSET_LINE\n#define GL_POLYGON_OFFSET_LINE\t\t\t\t0x2A02\n#endif\n\n#ifndef GL_SAMPLES_PASSED_ARB\n#define GL_SAMPLES_PASSED_ARB                             0x8914\n//#define GL_QUERY_COUNTER_BITS_ARB                         0x8864\n//#define GL_CURRENT_QUERY_ARB                              0x8865\n#define GL_QUERY_RESULT_ARB                               0x8866\n#define GL_QUERY_RESULT_AVAILABLE_ARB                     0x8867\n#endif\n\n\n#endif\n"
  },
  {
    "path": "engine/gl/ltface.c",
    "content": "#include \"quakedef.h\"\n#ifdef RUNTIMELIGHTING\n\ntypedef struct mentity_s {\n\tvec3_t origin;\n\tfloat light;\n\tfloat angle;\n\tfloat cone;\n\tint style;\n\tvec3_t colour;\n\tchar classname[64];\n\tchar target[64];\n\tchar targetname[64];\n\n\tint targetentnum;\n} mentity_t;\n\nstruct relight_ctx_s\n{\n\tunsigned int nummodels;\n\tmodel_t *models[2048];\t//0 is the worldmodel and must be valid\n\n\tqboolean parsed;\t//ents have been parsed okay.\n\tqboolean loaded;\t//needed models are all loaded.\n\n\tdouble starttime;\n\tfloat minlight;\n\tqboolean skiplit;\t//lux only\n\tqboolean shadows;\n\tmentity_t *entities;\n\tunsigned int num_entities;\n\tunsigned int max_entities;\n\tsize_t lightmapsamples;\n\tunsigned long nextface;\n};\n\n\n#define bsptexinfo(i) (*i)\n#define dsurfedges lightmodel->surfedges\n#define dvertexes lightmodel->vertexes\n#define dedges lightmodel->edges\n#define texinfo_t mtexinfo_t\n#define Q_PI M_PI\n\n#define dfaces lightmodel->surfaces\n#define dplanes lightmodel->planes\n#define dface_t msurface_t\n#define dvertex_t mvertex_t\n\n#define side flags & SURF_PLANEBACK\n\n#define scaledist 1\n#define rangescale 0.5\n#define extrasamples 1\n#define scalecos 0.5\n\n\n#define bsp_origin vec3_origin\n\n/*\n============\nCastRay\n\nReturns the distance between the points, or -1 if blocked\n=============\n*/\nstatic vec_t CastRay (struct relight_ctx_s *ctx, vec3_t p1, vec3_t p2)\n{\n\ttrace_t\ttrace;\n\tvec3_t move;\n\n\tif (ctx->shadows)\n\t{\n\t\tctx->models[0]->funcs.NativeTrace (ctx->models[0], 0, NULLFRAMESTATE, NULL, p1, p2, vec3_origin, vec3_origin, false, FTECONTENTS_SOLID, &trace);\n\t\tif (trace.fraction < 1)\n\t\t\treturn -1;\n\t}\n\n\tVectorSubtract(p1, p2, move);\n\treturn VectorLength(move);\n}\n\n\n\n\nstatic void ParseEpair (mentity_t *mapent, char *key, char *value)\n{\n\tdouble vec[3];\n\n\tif (!strcmp(key, \"classname\"))\n\t\tstrcpy(mapent->classname, value);\n\n\telse if (!strcmp(key, \"target\"))\n\t\tstrcpy(mapent->target, value);\n\n\telse if (!strcmp(key, \"targetname\"))\n\t\tstrcpy(mapent->targetname, value);\n\n\telse if (!strcmp(key, \"light\") || !strcmp(key, \"_light\"))\n\t\tmapent->light = atoi(value);\n\n\telse if (!strcmp(key, \"style\") || !strcmp(key, \"_style\"))\n\t\tmapent->style = atoi(value);\n\n\telse if (!strcmp(key, \"angle\") || !strcmp(key, \"_angle\"))\n\t\tmapent->angle = atof(value);\n\n\telse if (!strcmp(key, \"cone\") || !strcmp(key, \"_cone\"))\n\t\tmapent->cone = atof(value);\n\n\telse if (!strcmp(key, \"origin\"))\n\t{\n\t\tsscanf (value, \"%lf %lf %lf\", &vec[0], &vec[1], &vec[2]);\n\t\tmapent->origin[0]=vec[0];\n\t\tmapent->origin[1]=vec[1];\n\t\tmapent->origin[2]=vec[2];\n\t}\n\n\telse if (!strcmp(key, \"colour\") || !strcmp(key, \"color\") || !strcmp(key, \"_colour\") || !strcmp(key, \"_color\"))\n\t{\n\t\tsscanf (value, \"%lf %lf %lf\", &vec[0], &vec[1], &vec[2]);\n\t\tmapent->colour[0]=vec[0];\n\t\tmapent->colour[1]=vec[1];\n\t\tmapent->colour[2]=vec[2];\n\t}\n}\n\nvoid LightShutdown(struct relight_ctx_s *ctx, model_t *model)\n{\n\tunsigned int u;\n\tfor (u = 0; u < ctx->nummodels; u++)\n\t\tif (ctx->models[u] == model)\n\t\t{\n\t\t\tif (u < ctx->nummodels-1)\t//swap it out.\n\t\t\t\tctx->models[u] = ctx->models[ctx->nummodels-1];\n\t\t\tctx->nummodels -= 1;\n\n\t\t\tif (ctx->nummodels)\n\t\t\t\treturn;\t//other models still refer to it!\n\n\t\t\tZ_Free(ctx->entities);\n\t\t\tZ_Free(ctx);\n\t\t\tbreak;\n\t\t}\n}\nstruct relight_ctx_s *LightStartup(struct relight_ctx_s *ctx, model_t *model, qboolean shadows, qboolean skiplit)\n{\n\tunsigned int u;\n\tif (!ctx)\n\t{\n\t\tctx = Z_Malloc(sizeof(*ctx));\n\t\tctx->shadows = shadows;\n\t\tctx->skiplit = skiplit;\n\t}\n\tfor (u = 0; u < ctx->nummodels; u++)\n\t\tif (ctx->models[u] == model)\n\t\t\tbreak;\n\tif (u == ctx->nummodels)\n\t\tif (ctx->nummodels < countof(ctx->models))\n\t\t\tctx->models[ctx->nummodels++] = model;\n\tctx->starttime = Sys_DoubleTime();\n\treturn ctx;\n}\nvoid LightReloadEntities(struct relight_ctx_s *ctx, const char *entstring, qboolean ignorestyles)\n{\n#define DEFAULTLIGHTLEVEL 300\n\tmentity_t *mapent;\n\tchar key[1024];\n\tchar value[1024];\n\tint i;\n\tint switchedstyle=32;\n\tctx->num_entities = 0;\n\n\twhile(1)\n\t{\n\t\tentstring = COM_ParseOut(entstring, key, sizeof(key));\n\t\tif (!entstring || !*key)\n\t\t\tbreak;\n\t\tif (strcmp(key, \"{\"))\n\t\t{\t//someone messed up. Stop parsing.\n\t\t\tCon_Printf(\"token wasn't an open brace\\n\");\n\t\t\tbreak;\n\t\t}\n\n\t\tif (ctx->num_entities == ctx->max_entities)\n\t\t{\n\t\t\tctx->max_entities = ctx->max_entities + 128;\n\t\t\tctx->entities = BZ_Realloc(ctx->entities, sizeof(*ctx->entities) * ctx->max_entities);\n\t\t}\n\n\t\tmapent = &ctx->entities[ctx->num_entities];\n\t\tmemset(mapent, 0, sizeof(*mapent));\n\t\tmapent->colour[0] = 0;\n\t\tmapent->colour[1] = 0;\n\t\tmapent->colour[2] = 0;\n\t\tmapent->targetentnum = -1;\n\t\twhile(entstring)\n\t\t{\n\t\t\tentstring = COM_ParseOut(entstring, key, sizeof(key));\n\t\t\tif (!strcmp(key, \"}\"))\n\t\t\t\tbreak;\n\t\t\tentstring = COM_ParseOut(entstring, value, sizeof(value));\n\t\t\tParseEpair(mapent, key, value);\n\t\t}\n\t\tif (!mapent->colour[0] && !mapent->colour[1] && !mapent->colour[2])\n\t\t{\n\t\t\tint cont;\n\t\t\tvec3_t v;\n\t\t\tv[0] = mapent->origin[0];\n\t\t\tv[1] = mapent->origin[1];\n\t\t\tcont=0;\n\t\t\tfor (i = 0; i < 256; i+=16)\n\t\t\t{\n\t\t\t\tv[2] = mapent->origin[2]-i;\n\t\t\t\tcont = ctx->models[0]->funcs.PointContents (ctx->models[0], NULL, v);\n\t\t\t\tif (cont & (FTECONTENTS_LAVA | FTECONTENTS_SLIME | FTECONTENTS_SOLID))\n\t\t\t\t\tbreak;\n\t\t\t}\t\t\t\n\t\t\tif (cont & FTECONTENTS_LAVA)\n\t\t\t{\n\t\t\t\tmapent->colour[0] = 1;\n\t\t\t\tmapent->colour[1] = i/256.0;\n\t\t\t\tmapent->colour[2] = i/256.0;\n\t\t\t}\t\n\t\t\telse if (cont & FTECONTENTS_SLIME)\n\t\t\t{\n\t\t\t\tmapent->colour[0] = 0.5+0.5*i/256.0;\n\t\t\t\tmapent->colour[1] = 1;\t\t\t\t\n\t\t\t\tmapent->colour[2] = 0.5+0.5*i/256.0;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (mapent->style == 9)\t//hmm..\n\t\t\t\t{\n\t\t\t\t\tmapent->colour[1] = 1;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tif (!strncmp(mapent->classname, \"light_torch_small_walltorch\", 12))\n\t\t\t\t\t{\n\t\t\t\t\t\tmapent->colour[0] = 1;\n\t\t\t\t\t\tmapent->colour[1] = 0.7;\n\t\t\t\t\t\tmapent->colour[2] = 0.7;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tmapent->colour[0] = 1;\n\t\t\t\t\t\tmapent->colour[1] = 1;\n\t\t\t\t\t\tif (strncmp(mapent->classname, \"light_fluoro\", 12))\n\t\t\t\t\t\t\tmapent->colour[2] = 1;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (!mapent->light && !strncmp (mapent->classname, \"light\", 5))\n\t\t\tmapent->light = DEFAULTLIGHTLEVEL;\n\n\t\tif (*mapent->targetname && !mapent->style && !strcmp(mapent->classname, \"light\"))\n\t\t{\n\t\t\tfor (i = 0; i <= ctx->num_entities; i++)\n\t\t\t{\n\t\t\t\tif (ctx->entities[i].style >= 32 && !strcmp(ctx->entities[i].targetname, mapent->targetname))\n\t\t\t\t{\n\t\t\t\t\tmapent->style = ctx->entities[i].style;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (i == ctx->num_entities)\n\t\t\t\tmapent->style = switchedstyle++;\n\t\t}\n\n\t\tif (ignorestyles)\n\t\t\tmapent->style = 0;\n\n\t\tctx->num_entities++;\n\t}\n\n\tif (ctx->num_entities)\n\t\tif (ctx->entities[0].light)\n\t\t\tctx->minlight = ctx->entities[0].light;\n\n\tfor (mapent = ctx->entities; mapent < &ctx->entities[ctx->num_entities]; mapent++)\n\t{\n\t\tif (*mapent->target)\n\t\t{\n\t\t\tfor (i = 0; i < ctx->num_entities; i++)\n\t\t\t{\n\t\t\t\tif (mapent == &ctx->entities[i])\n\t\t\t\t\tcontinue;\n\n\t\t\t\tif (!strcmp(mapent->target, ctx->entities[i].targetname))\n\t\t\t\t{\n\t\t\t\t\tmapent->targetentnum = i;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n/*\n===============================================================================\n\nSAMPLE POINT DETERMINATION\n\nvoid SetupBlock (dface_t *f) Returns with surfpt[] set\n\nThis is a little tricky because the lightmap covers more area than the face.\nIf done in the straightforward fashion, some of the\nsample points will be inside walls or on the other side of walls, causing\nfalse shadows and light bleeds.\n\nTo solve this, I only consider a sample point valid if a line can be drawn\nbetween it and the exact midpoint of the face.  If invalid, it is adjusted\ntowards the center until it is valid.\n\n(this doesn't completely work)\n\n===============================================================================\n*/\n\n#define MAXIMUMEXTENT 128\n#define\tSINGLEMAP\t(MAXIMUMEXTENT*MAXIMUMEXTENT*4)\n\ntypedef struct llightinfo_s\n{\n\tstruct relight_ctx_s *ctx;\t//relight context, shared between threads.\n\n\tvec3_t\tlightmaps[MAXCPULIGHTMAPS][SINGLEMAP];\n\tvec3_t\tlightnorm[MAXCPULIGHTMAPS][SINGLEMAP];\n\tint\t\tnumlightstyles;\n\tvec_t\t*light;\n\tvec_t\tfacedist;\n\tvec3_t\tfacenormal;\n\n\tint\t\tnumsurfpt;\n\tvec3_t\tsurfpt[SINGLEMAP];\n\n\tvec3_t\ttexorg;\n\tvec3_t\tworldtotex[2];\t// s = (world - texorg) . worldtotex[0]\n\tvec3_t\ttextoworld[2];\t// world = texorg + s * textoworld[0]\n\n\tvec_t\texactmins[2], exactmaxs[2];\n\t\n\tint\t\ttexmins[2], texsize[2];\n\tint\t\tlightstyles[MAXCPULIGHTMAPS];\n} llightinfo_t;\n\nconst size_t lightthreadctxsize = sizeof(llightinfo_t);\n\n\n/*\n================\nCalcFaceVectors\n\nFills in texorg, worldtotex. and textoworld\n================\n*/\nstatic void LightCalcFaceVectors (llightinfo_t *l, vec4_t surf_texplanes[2])\n{\n\tint\t\t\ti, j;\n\tvec3_t\ttexnormal;\n\tfloat\tdistscale;\n\tvec_t\tdist, len;\n\t\n// convert from float to vec_t\n\tfor (i=0 ; i<2 ; i++)\n\t\tfor (j=0 ; j<3 ; j++)\n\t\t\tl->worldtotex[i][j] = surf_texplanes[i][j];\n\n// calculate a normal to the texture axis.  points can be moved along this\n// without changing their S/T\n\ttexnormal[0] =\tsurf_texplanes[1][1]*surf_texplanes[0][2]\n\t\t\t\t  - surf_texplanes[1][2]*surf_texplanes[0][1];\n\ttexnormal[1] =\tsurf_texplanes[1][2]*surf_texplanes[0][0]\n\t\t\t\t  - surf_texplanes[1][0]*surf_texplanes[0][2];\n\ttexnormal[2] =\tsurf_texplanes[1][0]*surf_texplanes[0][1]\n\t\t\t\t  - surf_texplanes[1][1]*surf_texplanes[0][0];\n\tVectorNormalize (texnormal);\n\n// flip it towards plane normal\n\tdistscale = DotProduct (texnormal, l->facenormal);\n\tif (!distscale)\n\t{\n\t\tVectorCopy(l->facenormal, texnormal);\n\t\tdistscale = 1;\n\t\tCon_Printf (\"Texture axis perpendicular to face\\n\");\n\t}\n\tif (distscale < 0)\n\t{\n\t\tdistscale = -distscale;\n\t\tVectorNegate (texnormal, texnormal);\n\t}\t\n\n// distscale is the ratio of the distance along the texture normal to\n// the distance along the plane normal\n\tdistscale = 1/distscale;\n\n\tfor (i=0 ; i<2 ; i++)\n\t{\n\t\tlen = VectorLength (l->worldtotex[i]);\n\t\tdist = DotProduct (l->worldtotex[i], l->facenormal);\n\t\tdist *= distscale;\n\t\tVectorMA (l->worldtotex[i], -dist, texnormal, l->textoworld[i]);\n\t\tVectorScale (l->textoworld[i], (1/len)*(1/len), l->textoworld[i]);\n\t}\n\n\n// calculate texorg on the texture plane\n\tfor (i=0 ; i<3 ; i++)\n\t\tl->texorg[i] = -surf_texplanes[0][3]* l->textoworld[0][i] - surf_texplanes[1][3] * l->textoworld[1][i];\n\n// project back to the face plane\n\tdist = DotProduct (l->texorg, l->facenormal) - l->facedist - 1;\n\tdist *= distscale;\n\tVectorMA (l->texorg, -dist, texnormal, l->texorg);\n\t\n}\n\n/*\n================\nCalcFaceExtents\n\nFills in s->texmins[] and s->texsize[]\nalso sets exactmins[] and exactmaxs[]\n================\n*/\nstatic void LightCalcFaceExtents (model_t *lightmodel, dface_t *s, vec2_t exactmins, vec2_t exactmaxs, int texmins[2], int texsize[2])\n{\n\tvec_t\tmins[2], maxs[2], val;\n\tint\t\ti,j, e;\n\tdvertex_t\t*v;\n\ttexinfo_t\t*tex;\n\n\tmins[0] = mins[1] = 999999;\n\tmaxs[0] = maxs[1] = -999999;\n\n\ttex = &bsptexinfo(s->texinfo);\n\t\n\tfor (i=0 ; i<s->numedges ; i++)\n\t{\n\t\te = dsurfedges[s->firstedge+i];\n\t\tif (e >= 0)\n\t\t\tv = dvertexes + dedges[e].v[0];\n\t\telse\n\t\t\tv = dvertexes + dedges[-e].v[1];\n\t\t\n\t\tfor (j=0 ; j<2 ; j++)\n\t\t{\n\t\t\tval = v->position[0] * tex->vecs[j][0] + \n\t\t\t\tv->position[1] * tex->vecs[j][1] +\n\t\t\t\tv->position[2] * tex->vecs[j][2] +\n\t\t\t\ttex->vecs[j][3];\n\t\t\tif (val < mins[j])\n\t\t\t\tmins[j] = val;\n\t\t\tif (val > maxs[j])\n\t\t\t\tmaxs[j] = val;\n\t\t}\n\t}\n\n\tfor (i=0 ; i<2 ; i++)\n\t{\t\n\t\texactmins[i] = mins[i];\n\t\texactmaxs[i] = maxs[i];\n\t\t\n\t\tmins[i] = floor(mins[i]/(1<<s->lmshift));\n\t\tmaxs[i] = ceil(maxs[i]/(1<<s->lmshift));\n\n\t\ttexmins[i] = mins[i];\n\t\ttexsize[i] = maxs[i] - mins[i];\n\t\tif (texsize[i] > MAXIMUMEXTENT-1)\n\t\t{\n\t\t\ttexsize[i] = MAXIMUMEXTENT-1;\n\t\t\tCon_Printf(\"Bad surface extents\");\n\t\t}\n\t}\n}\n\n/*\n=================\nCalcPoints\n\nFor each texture aligned grid point, back project onto the plane\nto get the world xyz value of the sample point\n=================\n*/\nstatic void LightCalcPoints (llightinfo_t *l, float lmscale)\n{\n\tint\t\ti;\n\tint\t\ts, t, j;\n\tint\t\tw, h;\n\tvec_t\tstep;\n\tvec_t\tstarts, startt, us, ut;\n\tvec_t\t*surf;\n\tvec_t\tmids, midt;\n\tvec3_t\tfacemid, move;\n\n//\n// fill in surforg\n// the points are biased towards the center of the surface\n// to help avoid edge cases just inside walls\n//\n\tsurf = l->surfpt[0];\n\tmids = (l->exactmaxs[0] + l->exactmins[0])/2;\n\tmidt = (l->exactmaxs[1] + l->exactmins[1])/2;\n\n\tfor (j=0 ; j<3 ; j++)\n\t\tfacemid[j] = l->texorg[j] + l->textoworld[0][j]*mids + l->textoworld[1][j]*midt;\n\n\tif (extrasamples)\n\t{\t// extra filtering\n\t\th = (l->texsize[1]+1)*2;\n\t\tw = (l->texsize[0]+1)*2;\n\t\tstarts = (l->texmins[0]-0.5)*lmscale;\n\t\tstartt = (l->texmins[1]-0.5)*lmscale;\n\t\tstep = 0.5 * lmscale;\n\t}\n\telse\n\t{\n\t\th = l->texsize[1]+1;\n\t\tw = l->texsize[0]+1;\n\t\tstarts = l->texmins[0]*lmscale;\n\t\tstartt = l->texmins[1]*lmscale;\n\t\tstep = lmscale;\n\t}\n\n\tl->numsurfpt = w * h;\n\tfor (t=0 ; t<h ; t++)\n\t{\n\t\tfor (s=0 ; s<w ; s++, surf+=3)\n\t\t{\n\t\t\tus = starts + s*step;\n\t\t\tut = startt + t*step;\n\n\t\t// if a line can be traced from surf to facemid, the point is good\n\t\t\tfor (i=0 ; i<6 ; i++)\n\t\t\t{\n\t\t\t// calculate texture point\n\t\t\t\tfor (j=0 ; j<3 ; j++)\n\t\t\t\t\tsurf[j] = l->texorg[j] + l->textoworld[0][j]*us\n\t\t\t\t\t+ l->textoworld[1][j]*ut;\n\n\t\t\t\tif (CastRay (l->ctx, facemid, surf) != -1)\n\t\t\t\t\tbreak;\t// got it\n\t\t\t\tif (i & 1)\n\t\t\t\t{\n\t\t\t\t\tif (us > mids)\n\t\t\t\t\t{\n\t\t\t\t\t\tus -= lmscale*0.5;\n\t\t\t\t\t\tif (us < mids)\n\t\t\t\t\t\t\tus = mids;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tus += lmscale*0.5;\n\t\t\t\t\t\tif (us > mids)\n\t\t\t\t\t\t\tus = mids;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tif (ut > midt)\n\t\t\t\t\t{\n\t\t\t\t\t\tut -= lmscale*0.5;\n\t\t\t\t\t\tif (ut < midt)\n\t\t\t\t\t\t\tut = midt;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tut += lmscale*0.5;\n\t\t\t\t\t\tif (ut > midt)\n\t\t\t\t\t\t\tut = midt;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\t// move surf 8 pixels towards the center\n\t\t\t\tVectorSubtract (facemid, surf, move);\n\t\t\t\tVectorNormalize (move);\n\t\t\t\tVectorMA (surf, 8, move, surf);\n\t\t\t}\n\t\t}\n\t}\n\t\n}\n\n\n/*\n===============================================================================\n\nFACE LIGHTING\n\n===============================================================================\n*/\n\n/*\n================\nSingleLightFace\n================\n*/\nstatic void SingleLightFace (mentity_t *light, llightinfo_t *l)\n{\n\tvec_t\tdist;\n\tvec3_t\tincoming;\n\tvec_t\tangle;\n\tvec_t\tadd;\n\tvec_t\t*surf;\n\tqboolean\thit;\n\tint\t\tmapnum;\n\tint\t\tc;\n\tvec3_t\trel;\n\tvec3_t\tspotvec;\n\tvec_t\tfalloff;\n\tvec3_t\t*lightsamp;\n\tvec3_t\t*norms;\n\t\n\tVectorSubtract (light->origin, bsp_origin, rel);\n\tdist = scaledist * (DotProduct (rel, l->facenormal) - l->facedist);\n\t\n// don't bother with lights behind the surface\n\tif (dist <= 0)\n\t\treturn;\n\t\t\n// don't bother with light too far away\n\tif (dist > light->light)\n\t\treturn;\n\n\tif (light->targetentnum>=0)\n\t{\n\t\tVectorSubtract (l->ctx->entities[light->targetentnum].origin, light->origin, spotvec);\n\t\tVectorNormalize (spotvec);\n\t\tif (!light->angle)\n\t\t\tfalloff = -cos(20*Q_PI/180);\t\n\t\telse\n\t\t\tfalloff = -cos(light->angle/2*Q_PI/180);\n\t}\n\telse\n\t\tfalloff = 0;\t// shut up compiler warnings\n\t\n\tmapnum = 0;\n\tfor (mapnum=0 ; mapnum<l->numlightstyles ; mapnum++)\n\t\tif (l->lightstyles[mapnum] == light->style)\n\t\t\tbreak;\n\t\n\tlightsamp = l->lightmaps[mapnum];\n\tnorms = l->lightnorm[mapnum];\n\tif (mapnum == l->numlightstyles)\n\t{\t// init a new light map\n#ifdef UTILITY\n\t\tif (mapnum == MAXCPULIGHTMAPS)\n\t\t{\n\t\t\tprintf (\"WARNING: Too many light styles on a face\\n\");\n\t\t\treturn;\n\t\t}\n\t\tsize = (l->texsize[1]+1)*(l->texsize[0]+1);\n\t\tfor (i=0 ; i<size ; i++)\n\t\t{\n\t\t\tlightsamp[i][0] = 0;\n\t\t\tlightsamp[i][1] = 0;\n\t\t\tlightsamp[i][2] = 0;\n\t\t\tnorms[i][0] = 0;\n\t\t\tnorms[i][1] = 0;\n\t\t\tnorms[i][2] = 0;\n\t\t}\n#else\n\t\treturn;\t//can't light a surface with a lightstyle that did not previously exist, due to lightmap space limits.\n#endif\n\t}\n\n//\n// check it for real\n//\n\thit = false;\n\t\n\tsurf = l->surfpt[0];\n\tfor (c=0 ; c<l->numsurfpt ; c++, surf+=3)\n\t{\n\t\tdist = CastRay(l->ctx, light->origin, surf)*scaledist;\n\t\tif (dist < 0)\n\t\t\tcontinue;\t// light doesn't reach\n\n\t\tVectorSubtract (light->origin, surf, incoming);\n\t\tVectorNormalize (incoming);\n\t\tif (light->targetentnum >= 0)\n\t\t{\t// spotlight cutoff\n\t\t\tif (DotProduct (spotvec, incoming) > falloff)\n\t\t\t\tcontinue;\n\t\t}\n\t\tangle = DotProduct (incoming, l->facenormal);\n\n\t\tangle = (1.0-scalecos) + scalecos*angle;\n\t\tadd = light->light - dist;\n\t\tadd *= angle;\n\t\tif (add < 0)\n\t\t\tcontinue;\n\n\t\tlightsamp[c][0] += add*light->colour[0];\n\t\tlightsamp[c][1] += add*light->colour[1];\n\t\tlightsamp[c][2] += add*light->colour[2];\n\n\t\tnorms[c][0] += add * incoming[0];\n\t\tnorms[c][1] += add * incoming[1];\n\t\tnorms[c][2] += add * incoming[2];\n\n\t\tif (add > 1)\t\t// ignore real tiny lights\n\t\t\thit = true;\n\t}\n\n\tif (mapnum == l->numlightstyles && hit)\n\t{\n\t\tl->lightstyles[mapnum] = light->style;\n\t\tl->numlightstyles++;\t// the style has some real data now\n\t}\n}\n\n/*\n============\nFixMinlight\n============\n*/\nstatic void FixMinlight (llightinfo_t *l)\n{\n\tint\t\ti, j;\n\tfloat\tminlight = l->ctx->minlight;\n\n// if minlight is set, there must be a style 0 light map\n\tif (!minlight)\n\t\treturn;\n\t\n\tfor (i=0 ; i< l->numlightstyles ; i++)\n\t{\n\t\tif (l->lightstyles[i] == 0)\n\t\t\tbreak;\n\t}\n\tif (i == l->numlightstyles)\n\t{\n\t\tif (l->numlightstyles == MAXCPULIGHTMAPS)\n\t\t\treturn;\t\t// oh well..\n\t\tfor (j=0 ; j<l->numsurfpt ; j++)\n\t\t{\n\t\t\tl->lightmaps[i][j][0] = minlight;\n\t\t\tl->lightmaps[i][j][1] = minlight;\n\t\t\tl->lightmaps[i][j][2] = minlight;\n\t\t}\n\t\tl->lightstyles[i] = 0;\n\t\tl->numlightstyles++;\n\t}\n\telse\n\t{\n\t\tfor (j=0 ; j<l->numsurfpt ; j++)\n\t\t{\n\t\t\tif ( l->lightmaps[i][j][0] < minlight)\n\t\t\t\tl->lightmaps[i][j][0] = minlight;\n\t\t\tif ( l->lightmaps[i][j][1] < minlight)\n\t\t\t\tl->lightmaps[i][j][1] = minlight;\n\t\t\tif ( l->lightmaps[i][j][2] < minlight)\n\t\t\t\tl->lightmaps[i][j][2] = minlight;\n\t\t}\n\t}\n}\n\nstatic unsigned int PackE5BRG9(vec3_t rgb)\n{\t//5 bits exponent, 3*9 bits of mantissa. no sign bit.\n\tint e = 0;\n\tfloat m = max(max(rgb[0], rgb[1]), rgb[2]);\n\tfloat scale;\n\tunsigned int hdr;\n\n\tif (m >= 0.5)\n\t{\t//positive exponent\n\t\twhile (m >= (1<<(e)) && e < 30-15)\t//don't do nans.\n\t\t\te++;\n\t}\n\telse\n\t{\t//negative exponent...\n\t\twhile (m < 1/(1<<-e) && e > -15)\t//don't do denormals.\n\t\t\te--;\n\t}\n\n\tscale = pow(2, e-9);\n\thdr = ((e+15)<<27);\n\thdr |= bound(0, (int)(rgb[0]/scale + 0.5), 0x1ff)<<0;\n\thdr |= bound(0, (int)(rgb[1]/scale + 0.5), 0x1ff)<<9;\n\thdr |= bound(0, (int)(rgb[2]/scale + 0.5), 0x1ff)<<18;\n\treturn hdr;\n}\n\n/*\n============\nLightFace\n============\n*/\nvoid LightPlane (struct relight_ctx_s *ctx, struct llightinfo_s *l, lightstyleindex_t surf_styles[MAXCPULIGHTMAPS], unsigned int *surf_expsamples, qbyte *surf_rgbsamples, qbyte *surf_deluxesamples, vec4_t surf_plane, vec4_t surf_texplanes[2], vec2_t exactmins, vec2_t exactmaxs, int texmins[2], int texsize[2], float lmscale)\n{\n\tint\t\ts, t;\n\tint\t\ti,c,ch;\n\tvec_t\ttotal, mean;\n\tint\t\tsize;\n\tint\t\tlightmapwidth;\n#ifdef UTILITY\n\tint\t\tlightmapsize;\n\tbyte\t*out;\n#endif\n\tunsigned int *expout;\n\tqbyte\t*rgbout;\n\tqbyte\t*dulout;\n\tvec3_t\t*light, *norm;\n\tvec3_t\twnorm, temp, svector, tvector;\n\tint\t\tw;\n\t\n\n//\n// some surfaces don't need lightmaps\n//\t\n\tif (!surf_rgbsamples && !surf_expsamples)\n\t\treturn;\n\n//\tmemset (l, 0, sizeof(*l));\n\tl->ctx = ctx;\n\n//\n// rotate plane\n//\n\tVectorCopy (surf_plane, l->facenormal);\n\tl->facedist = surf_plane[3];\n\n\tLightCalcFaceVectors (l, surf_texplanes);\n\tVector2Copy(exactmins, l->exactmins);\n\tVector2Copy(exactmaxs, l->exactmaxs);\n\tVector2Copy(texmins, l->texmins);\n\tVector2Copy(texsize, l->texsize);\n\tLightCalcPoints (l, lmscale);\n\n\tlightmapwidth = l->texsize[0]+1;\n\n\tsize = lightmapwidth*(l->texsize[1]+1);\n\tif (size > SINGLEMAP)\n\t\tHost_Error (\"Bad lightmap size\");\n\n\ti = 0;\n#ifndef UTILITY\n\tfor (; surf_styles[i] != INVALID_LIGHTSTYLE && i < MAXCPULIGHTMAPS; i++)\n\t{\n\t\tl->lightstyles[i] = surf_styles[i];\n\t\tmemset(&l->lightmaps[i], 0, sizeof(l->lightmaps[i][0])*l->numsurfpt);\n\t\tmemset(&l->lightnorm[i], 0, sizeof(l->lightnorm[i][0])*l->numsurfpt);\n\t}\n#endif\n\tl->numlightstyles = i;\n\tfor ( ; i<MAXCPULIGHTMAPS ; i++)\n\t\tl->lightstyles[i] = INVALID_LIGHTSTYLE;\n\t\n//\n// cast all lights\n//\t\n\tfor (i=0 ; i<ctx->num_entities ; i++)\n\t{\n\t\tif (ctx->entities[i].light)\n\t\t\tSingleLightFace (&ctx->entities[i], l);\n\t}\n\n\tFixMinlight (l);\n\t\t\n\tif (!l->numlightstyles)\n\t{\t// no light hitting it\n#ifdef UTILITY\n\t\tf->lightofs = -1;\n#endif\n\t\treturn;\n\t}\n\n//\n// save out the values\n//\n\tfor (i=0 ; i <MAXCPULIGHTMAPS ; i++)\n\t\tsurf_styles[i] = l->lightstyles[i];\n\n\n#ifdef UTILITY\n\tlightmapsize = size*l->numlightstyles;\n\tif (runningrgblightdatabase)\n\t{\n\t\tout = GetFakeFileSpace(&f->lightofs, lightmapsize);\n\t\texpout = NULL;\n\t\trgbout = runningrgblightdatabase + f->lightofs*3;\n\t\tdulout = runninglightnormbase + f->lightofs*3;\n\t}\n\telse\n\t{\n\t\tout = GetFileSpace (&f->lightofs, lightmapsize);\n\t\texpout = NULL;\n\t\trgbout = GetRGBFileSpace (f->lightofs, lightmapsize);\n\t\tdulout = GetNormFileSpace (f->lightofs, lightmapsize);\n\t}\n#else\n\tif (!ctx->skiplit)\n\t{\n\t\texpout = surf_expsamples;\n\t\trgbout = surf_rgbsamples;\n\t}\n\telse\n\t{\n\t\texpout = NULL;\n\t\trgbout = NULL;\n\t}\n\tif (l->ctx->models[0]->deluxdata)\n\t{\n\t\tdulout = surf_deluxesamples;\n\n\t\tVectorCopy(surf_texplanes[0], svector);\n\t\tVectorNegate(surf_texplanes[1], tvector);\n\t\tVectorNormalize(svector);\n\t\tVectorNormalize(tvector);\n\t}\n\telse\n\t\tdulout = NULL;\n#endif\n\n\n\t\n// extra filtering\n//\th = (l.texsize[1]+1)*2;\n\tw = l->texsize[0]+1;\n\tif (extrasamples)\n\t\tw *= 2;\n\n\tfor (i=0 ; i< l->numlightstyles ; i++)\n\t{\n\t\tif (l->lightstyles[i] == 0xff)\n\t\t\tHost_Error (\"Wrote empty lightmap\");\n\t\tlight = l->lightmaps[i];\n\t\tnorm = l->lightnorm[i];\n\t\tc = 0;\n\t\tfor (t=0 ; t<=l->texsize[1] ; t++)\n\t\t{\n\t\t\tfor (s=0 ; s<=l->texsize[0] ; s++, c++)\n\t\t\t{\n\t\t\t\tmean = 0;\n\n\t\t\t\tfor (ch = 0; ch < 3; ch++)\n\t\t\t\t{\n\t\t\t\t\tif (extrasamples)\n\t\t\t\t\t{\t// filtered sample\n\t\t\t\t\t\ttotal = light[t*2*w+s*2][ch] + light[t*2*w+s*2+1][ch]\n\t\t\t\t\t\t+ light[(t*2+1)*w+s*2][ch] + light[(t*2+1)*w+s*2+1][ch];\n\t\t\t\t\t\ttotal *= 0.25;\n\n\t\t\t\t\t\twnorm[ch] = norm[t*2*w+s*2][ch] + norm[t*2*w+s*2+1][ch]\n\t\t\t\t\t\t+ norm[(t*2+1)*w+s*2][ch] + norm[(t*2+1)*w+s*2+1][ch];\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\ttotal = light[c][ch];\n\t\t\t\t\t\twnorm[ch] = norm[c][ch];\n\t\t\t\t\t}\n\t\t\t\t\ttotal *= rangescale;\t// scale before clamping\n\t\t\t\t\ttemp[ch] = total/0x80;\t// quake bsps store logical light values between 0 and 2 for overbrights. normalise it appropriately.\n#ifndef UTILITY\n//\t\t\t\t\tif (total > *rgbout)\t//sorry - for qw\n//\t\t\t\t\t\ttotal = *rgbout;\n#endif\n\t\t\t\t\tif (total < 0)\n\t\t\t\t\t\ttotal = 0;\n\t\t\t\t\tif (total > 0xff)\n\t\t\t\t\t\ttotal = 0xff;\n\t\t\t\t\t\n\t\t\t\t\tif (rgbout)\n\t\t\t\t\t\t*rgbout++ = total;\n\t\t\t\t\tmean += total;\n\t\t\t\t}\n\t\t\t\tif (expout)\n\t\t\t\t\t*expout++ = PackE5BRG9(temp);\n#ifdef UTILITY\n\t\t\t\t*out++ = mean/3;\n#endif\n\n\t\t\t\tif (dulout)\n\t\t\t\t{\n\t\t\t\t\ttemp[0] = DotProduct(wnorm, svector);\n\t\t\t\t\ttemp[1] = DotProduct(wnorm, tvector);\n\t\t\t\t\ttemp[2] = DotProduct(wnorm, l->facenormal);\n\t\t\t\t\tif (!temp[0] && !temp[1] && !temp[2])\n\t\t\t\t\t\tVectorSet(temp, 0, 0, 1);\n\t\t\t\t\telse\n\t\t\t\t\t\tVectorNormalize(temp);\n\t\t\t\t\t*dulout++ = (temp[0]+1)*127;\n\t\t\t\t\t*dulout++ = (temp[1]+1)*127;\n\t\t\t\t\t*dulout++ = (temp[2]+1)*127;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\nstatic void LightFace (struct relight_ctx_s *ctx, struct llightinfo_s *threadctx, int facenum)\n{\n\tdface_t *f = ctx->models[0]->surfaces + facenum;\n\tvec4_t plane;\n\tvec2_t exactmins;\n\tvec2_t exactmaxs;\n\tint texmins[2], texsize[2];\n\n\tVectorCopy (f->plane->normal, plane);\n\tplane[3] = f->plane->dist;\n\tif (f->flags & SURF_PLANEBACK)\n\t{\n\t\tVectorNegate (plane, plane);\n\t\tplane[3] = -plane[3];\n\t}\n\n\t//no lighting on these.\n\tif (f->texinfo->flags & TEX_SPECIAL)\n\t\treturn;\n\n\tLightCalcFaceExtents(ctx->models[0], f, exactmins, exactmaxs, texmins, texsize);\n\tif (ctx->models[0]->lightmaps.fmt == LM_E5BGR9)\n\t\tLightPlane(ctx, threadctx, f->styles, (unsigned int*)f->samples, NULL, 3*(f->samples - ctx->models[0]->lightdata)/4 + ctx->models[0]->deluxdata, plane, f->texinfo->vecs, exactmins, exactmaxs, texmins, texsize, 1<<f->lmshift);\n\telse\n\t\tLightPlane(ctx, threadctx, f->styles, NULL, f->samples, f->samples - ctx->models[0]->lightdata + ctx->models[0]->deluxdata, plane, f->texinfo->vecs, exactmins, exactmaxs, texmins, texsize, 1<<f->lmshift);\n}\n\n\n\nstatic struct relight_ctx_s *lightcontext;\n#if defined(MULTITHREAD)\n#ifdef _WIN32\n#include <windows.h>\n#elif defined(__linux__)\n#include <unistd.h>\n#endif\nstatic void *relightthread[8];\nstatic unsigned int relightthreads;\nstatic volatile qboolean wantrelight;\n\nstatic int RelightThread(void *ctx)\n{\n\tint surf;\n\tstruct relight_ctx_s *lightcontext = ctx;\n\tmodel_t *lightmodel = lightcontext->models[0];\n\tvoid *threadctx = malloc(lightthreadctxsize);\n\twhile (wantrelight)\n\t{\n#ifdef _WIN32\n\t\tsurf = InterlockedIncrement(&lightcontext->nextface);\n#elif defined(__GNUC__)\n\t\tsurf = __sync_add_and_fetch(&lightcontext->nextface, 1);\n#else\n\t\tsurf = relitsurface++;\n#endif\n\t\tif (surf >= lightmodel->numsurfaces)\n\t\t\tbreak;\n\t\tLightFace(lightcontext, threadctx, surf);\n\t\tlightmodel->surfaces[surf].cached_dlight = -1;\t//invalidate it (slightly racey buy w/e\n\t}\n\tfree(threadctx);\n\treturn 0;\n}\n#else\nstatic void *lightmainthreadctx;\n#endif\n\nvoid RelightTerminate(model_t *mod)\n{\n\tmodel_t *lightmodel;\n\tsize_t u;\n\tif (!lightcontext)\n\t\treturn;\n\n\t//if one of the models we're using is being purged then we have to abort the relight to avoid caching partial results (especially if its the model we're actually relighting)\n\tif (mod)\n\t{\n\t\tfor (u = 0; u < lightcontext->nummodels; u++)\n\t\t\tif (lightcontext->models[u] == mod)\n\t\t\t\tbreak;\n\t}\n\telse\n\t\tu = 0;\n\n\tif (u < lightcontext->nummodels)\n\t{\n\t\tlightmodel = lightcontext->models[0];\n\n#ifdef MULTITHREAD\n\t\twantrelight = false;\n\t\tif (relightthreads)\n\t\t{\n\t\t\tint i;\n\t\t\twantrelight = false;\n\t\t\tfor (i = 0; i < relightthreads; i++)\n\t\t\t{\n\t\t\t\tSys_WaitOnThread(relightthread[i]);\n\t\t\t\trelightthread[i] = NULL;\n\t\t\t}\n\t\t\trelightthreads = 0;\n\t\t}\n#else\n\t\tfree(lightmainthreadctx);\n\t\tlightmainthreadctx = NULL;\n#endif\n\n\t\tif (lightcontext->nextface >= lightmodel->numsurfaces)\n\t\t{\n\t\t\tvfsfile_t *f;\n\t\t\tchar filename[MAX_QPATH];\n\n\t\t\tif (lightmodel->deluxdata)\n\t\t\t{\n\t\t\t\tCOM_StripExtension(lightmodel->name, filename, sizeof(filename));\n\t\t\t\tCOM_DefaultExtension(filename, \".lux\", sizeof(filename));\n\t\t\t\tf = FS_OpenVFS(filename, \"wb\", FS_GAME);\n\t\t\t\tif (f)\n\t\t\t\t{\n\t\t\t\t\tVFS_WRITE(f, \"QLIT\\1\\0\\0\\0\", 8);\n\t\t\t\t\tVFS_WRITE(f, lightmodel->deluxdata, lightcontext->lightmapsamples*3);\n\t\t\t\t\tVFS_CLOSE(f);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tCon_Printf(\"Unable to write \\\"%s\\\"\\n\", filename);\n\t\t\t}\n\n\t\t\tif (!lightcontext->skiplit)\t//the user might already have a lit file (don't overwrite it).\n\t\t\t{\n\t\t\t\tCOM_StripExtension(lightmodel->name, filename, sizeof(filename));\n\t\t\t\tCOM_DefaultExtension(filename, \".lit\", sizeof(filename));\n\n\t\t\t\tf = FS_OpenVFS(filename, \"wb\", FS_GAME);\n\t\t\t\tif (f)\n\t\t\t\t{\n\t\t\t\t\tif (lightmodel->lightmaps.fmt == LM_E5BGR9)\n\t\t\t\t\t{\n\t\t\t\t\t\tVFS_WRITE(f, \"QLIT\\x01\\0\\x01\\0\", 8);\n\t\t\t\t\t\tVFS_WRITE(f, lightmodel->lightdata, lightcontext->lightmapsamples*4);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tVFS_WRITE(f, \"QLIT\\1\\0\\0\\0\", 8);\n\t\t\t\t\t\tVFS_WRITE(f, lightmodel->lightdata, lightcontext->lightmapsamples*3);\n\t\t\t\t\t}\n\t\t\t\t\tVFS_CLOSE(f);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tCon_Printf(\"Unable to write \\\"%s\\\"\\n\", filename);\n\t\t\t}\n\t\t}\n\t\telse\n\t\t\tCon_Printf(\"Relighting aborted before completion\\n\");\n\n\t\tLightShutdown(lightcontext, mod);\n\t\tlightcontext = NULL;\n\t}\n}\n\nqboolean RelightSetup (model_t *model, size_t lightsamples, qboolean generatelit)\n{\n\tqboolean ret = false;\n\tSys_LockMutex(com_resourcemutex);\t//models can be loaded on different threads, so no race conditions please.\n\tif (!lightcontext)\n\t{\n\t\tlightcontext = LightStartup(NULL, model, true, !generatelit);\n\t\tlightcontext->lightmapsamples = lightsamples;\n\t\tret = true;\n\t}\n\tSys_UnlockMutex(com_resourcemutex);\n\treturn ret;\n}\n\nconst char *RelightGetProgress(float *progress)\n{\n\tchar filename[MAX_QPATH];\n\tif (!lightcontext)\n\t\treturn NULL;\n\t*progress = (lightcontext->nextface*100.0f) / lightcontext->models[0]->numsurfaces;\n\n\tCOM_StripExtension(lightcontext->models[0]->name, filename, sizeof(filename));\n\tCOM_DefaultExtension(filename, lightcontext->skiplit?\".lux\":\".lit\", sizeof(filename));\n\treturn va(\"%s\", filename);\n}\n\nvoid RelightThink (void)\n{\n\tif (lightcontext)\n\t{\n\t\tmodel_t *lightmodel = lightcontext->models[0];\n\n\t\tif (!lightcontext->loaded)\n\t\t{\t//make sure everything finished loading properly before we start poking things.\n\t\t\tsize_t u;\n\t\t\tif (!lightcontext->parsed)\n\t\t\t{\n\t\t\t\tif (lightcontext->models[0]->loadstate != MLS_LOADED)\n\t\t\t\t\treturn;\t//not ready yet...\n\n\t\t\t\tLightReloadEntities(lightcontext, Mod_GetEntitiesString(lightmodel), false);\n\t\t\t\tlightcontext->parsed = true;\n\t\t\t}\n\n\t\t\tfor (u = 0; u < lightcontext->nummodels; u++)\n\t\t\t\tif (lightcontext->models[u]->loadstate != MLS_LOADED)\n\t\t\t\t\treturn;\n\t\t\tlightcontext->loaded = true;\n\t\t}\n\n#ifdef MULTITHREAD\n\t\tif (!relightthreads)\n\t\t{\n\t\t\tint i;\n#if defined(_WIN32) && !defined(WINRT)\n\t\t\tHANDLE me = GetCurrentProcess();\n\t\t\tDWORD_PTR proc, sys;\n\t\t\t/*count cpus*/\n\t\t\tGetProcessAffinityMask(me, &proc, &sys);\n\t\t\trelightthreads = 0;\n\t\t\tfor (i = 0; i < sizeof(proc)*8; i++)\n\t\t\t\tif (proc & ((size_t)1u<<i))\n\t\t\t\t\trelightthreads++;\n\t\t\t/*subtract 1*/\n\t\t\tif (relightthreads <= 1)\n\t\t\t\trelightthreads = 1;\n\t\t\telse\n\t\t\t\trelightthreads--;\n#elif defined(__GNUC__)\n\t#ifdef __linux__\n\t\t\trelightthreads = sysconf(_SC_NPROCESSORS_ONLN)-1;\n\t\t\tif (relightthreads < 1)\n\t\t\t\trelightthreads = 1;\n\t#else\n\t\t\trelightthreads = 2;\t//erm, lets hope...\n\t#endif\n#else\n\t\t\t/*can't do atomics*/\n\t\t\trelightthreads = 1;\n#endif\n\t\t\tif (relightthreads > sizeof(relightthread)/sizeof(relightthread[0]))\n\t\t\t\trelightthreads = sizeof(relightthread)/sizeof(relightthread[0]);\n\t\t\twantrelight = true;\n\t\t\tfor (i = 0; i < relightthreads; i++)\n\t\t\t\trelightthread[i] = Sys_CreateThread(\"relight\", RelightThread, lightcontext, THREADP_NORMAL, 0);\n\t\t}\n\t\tif (lightcontext->nextface < lightmodel->numsurfaces)\n\t\t{\n\t\t\treturn;\n\t\t}\n#else\n\t\tif (!lightmainthreadctx)\n\t\t\tlightmainthreadctx = malloc(lightthreadctxsize);\n\t\tLightFace(lightcontext, lightmainthreadctx, lightcontext->nextface);\n\t\tlightmodel->surfaces[lightcontext->nextface].cached_dlight = -1;\n\n\t\tlightcontext->nextface++;\n#endif\n\t\tif (lightcontext->nextface >= lightmodel->numsurfaces)\n\t\t{\n\t\t\tdouble starttime = lightcontext->starttime;\n\t\t\tRelightTerminate(lightmodel);\n\t\t\tCon_Printf(\"Finished lighting %s, took %.1f seconds\\n\", lightmodel->name, Sys_DoubleTime()-starttime);\n\t\t}\n\t}\n}\n#endif\n"
  },
  {
    "path": "engine/gl/model_hl.h",
    "content": "/*\n +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n    Half-Life Model Renderer (Experimental) Copyright (C) 2001 James 'Ender' Brown [ender@quakesrc.org] This program is\n    free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as\n    published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.\n    This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied\n    warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more\n    details. You should have received a copy of the GNU General Public License along with this program; if not, write\n    to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. fromquake.h -\n    \n\tmodel_hl.h - halflife model structure\n +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n */\n#define HLPOLYHEADER\t(('T' << 24) + ('S' << 16) + ('D' << 8) + 'I')\t/* little-endian \"IDST\" */\n#define HLMDLHEADER\t\t\"IDST\"\n\n/* flags - eukara */\n#define HLMDLFL_FLAT\t\t0x0001\n#define HLMDLFL_CHROME\t\t0x0002\n#define HLMDLFL_FULLBRIGHT\t0x0004\n#define HLMDLFL_MASKED\t\t0x0040\n#define HLMDLFL_ALPHASOLID\t0x0800\n\n#define HLSHADER_FULLBRIGHT \\\n\t\t\"{\\n\" \\\n\t\t\t\"program defaultskin\\n\" \\\n\t\t\t\"{\\n\" \\\n\t\t\t\t\"map $diffuse\\n\" \\\n\t\t\t\"}\\n\" \\\n\t\t\"}\\n\"\n\n#define HLSHADER_CHROME \\\n\t\t\"{\\n\" \\\n\t\t\t\"program defaultskin#CHROME\\n\" \\\n\t\t\t\"{\\n\" \\\n\t\t\t\t\"map $diffuse\\n\" \\\n\t\t\t\t\"tcgen environment\\n\" \\\n\t\t\t\t\"rgbgen lightingdiffuse\\n\" \\\n\t\t\t\"}\\n\" \\\n\t\t\"}\\n\"\n\n#define HLSHADER_MASKED \\\n\t\t\"{\\n\" \\\n\t\t\t\"program defaultskin#MASK=0.5\\n\" \\\n\t\t\t\"{\\n\" \\\n\t\t\t\t\"map $diffuse\\n\" \\\n\t\t\t\t\"rgbgen lightingdiffuse\\n\" \\\n\t\t\t\t\"alphaFunc GE128\\n\" \\\n\t\t\t\"}\\n\" \\\n\t\t\"}\\n\"\n\n#define HLSHADER_FULLBRIGHTCHROME \\\n\t\t\"{\\n\" \\\n\t\t\t\"program defaultskin#CHROME\\n\" \\\n\t\t\t\"{\\n\" \\\n\t\t\t\t\"map $diffuse\\n\" \\\n\t\t\t\t\"tcgen environment\\n\" \\\n\t\t\t\"}\\n\" \\\n\t\t\"}\\n\"\n\n/*\n -----------------------------------------------------------------------------------------------------------------------\n    main model header\n -----------------------------------------------------------------------------------------------------------------------\n */\ntypedef struct\n{\n\tint\t\tfiletypeid;\t//IDSP\n\tint\t\tversion;\t//10\n\tchar\tname[64];\n\tint\t\tfilesize;\n\tvec3_t\tunknown3[5];\n\tint\t\tunknown4;\t//flags\n\tint\t\tnumbones;\n\tint\t\tboneindex;\n\tint\t\tnumcontrollers;\n\tint\t\tcontrollerindex;\n\tint\t\tnum_hitboxes;\n\tint\t\tofs_hitboxes;\n\tint\t\tnumseq;\n\tint\t\tseqindex;\n\tint\t\tunknown6;\t\t//external sequences\n\tint\t\tseqgroups;\n\tint\t\tnumtextures;\n\tint\t\ttextures;\n\tint\t\tunknown7;\t//something to do with external textures\n\tint\t\tskinrefs;\n\tint\t\tskingroups;\n\tint\t\tskins;\n\tint\t\tnumbodyparts;\n\tint\t\tbodypartindex;\n\tint\t\tnum_attachments;\n\tint\t\tofs_attachments;\n\tint\t\tunknown9[6];\t//sounds, transitions\n} hlmdl_header_t;\n\n/*\n -----------------------------------------------------------------------------------------------------------------------\n    skin info\n -----------------------------------------------------------------------------------------------------------------------\n */\ntypedef struct\n{\n\tchar\tname[64];\n\tint\t\tflags; /*flat, chrome, fullbright*/\n\tint\t\tw;\t/* width */\n\tint\t\th;\t/* height */\n\tint\t\toffset;\t/* index */\n} hlmdl_tex_t;\n\n/*\n -----------------------------------------------------------------------------------------------------------------------\n    body part index\n -----------------------------------------------------------------------------------------------------------------------\n */\ntypedef struct\n{\n\tchar\tname[64];\n\tint\t\tnummodels;\n\tint\t\tbase;\n\tint\t\tmodelindex;\n} hlmdl_bodypart_t;\n\n/*\n -----------------------------------------------------------------------------------------------------------------------\n    meshes\n -----------------------------------------------------------------------------------------------------------------------\n */\ntypedef struct\n{\n\tint numtris;\n\tint index;\n\tint skinindex;\n\tint unknown2;\n\tint unknown3;\n} hlmdl_mesh_t;\n\n/*\n -----------------------------------------------------------------------------------------------------------------------\n    bones\n -----------------------------------------------------------------------------------------------------------------------\n */\ntypedef struct\n{\n\tchar\tname[32];\n\tint\t\tparent;\n\tint\t\tunknown1;\n\tint\t\tbonecontroller[6];\n\tfloat\tvalue[6];\n\tfloat\tscale[6];\n} hlmdl_bone_t;\n\ntypedef struct\n{\n\tchar name[32];\t//I assume\n\tint unk;\n\tint bone;\n\tvec3_t org;\n\tvec3_t unk2[3];\n} hlmdl_attachment_t;\n\ntypedef struct\n{\n\tint bone;\n\tint body;\t//value reported to gamecode on impact\n\tvec3_t mins;\n\tvec3_t maxs;\n} hlmdl_hitbox_t;\n\n/*\n -----------------------------------------------------------------------------------------------------------------------\n    bone controllers\n -----------------------------------------------------------------------------------------------------------------------\n */\ntypedef struct\n{\n\tint\t\tname;\n\tint\t\ttype;\n\tfloat\tstart;\n\tfloat\tend;\n\tint\t\tunknown1;\n\tint\t\tindex;\n} hlmdl_bonecontroller_t;\n\n/*\n -----------------------------------------------------------------------------------------------------------------------\n    halflife submodel descriptor\n -----------------------------------------------------------------------------------------------------------------------\n */\ntypedef struct\n{\n\tchar\tname[64];\n\tint\t\tunknown1;\n\tfloat\tunknown2;\n\tint\t\tnummesh;\n\tint\t\tmeshindex;\n\tint\t\tnumverts;\n\tint\t\tvertinfoindex;\n\tint\t\tvertindex;\n\tint\t\tunknown3[2];\n\tint\t\tnormindex;\n\tint\t\tunknown4[2];\n} hlmdl_submodel_t;\n\n/*\n -----------------------------------------------------------------------------------------------------------------------\n    animation\n -----------------------------------------------------------------------------------------------------------------------\n */\ntypedef struct\n{\n\tunsigned short\toffset[6];\n} hlmdl_anim_t;\n\n/*\n -----------------------------------------------------------------------------------------------------------------------\n    animation frames\n -----------------------------------------------------------------------------------------------------------------------\n */\ntypedef union\n{\n\tstruct {\n\t\tqbyte\tvalid;\n\t\tqbyte\ttotal;\n\t} num;\n\tshort\tvalue;\n} hlmdl_animvalue_t;\n\n/*\n -----------------------------------------------------------------------------------------------------------------------\n    sequence descriptions\n -----------------------------------------------------------------------------------------------------------------------\n */\ntypedef struct\n{\n\tchar\tname[32];\n\tfloat\ttiming;\n\tint\t\tloop;\n\tint\t\taction;\n\tint\t\tactionweight;\n\tint\t\tnum_events;\n\tint\t\tofs_events;\n\tint\t\tnumframes;\n\tint\t\tunknown2[2];\n\tint\t\tmotiontype;\n\tint\t\tmotionbone;\n\tvec3_t\tunknown3;\n\tint\t\tunknown4[2];\n\tvec3_t\tbbox[2];\n\tint\t\thasblendseq;\n\tint\t\tindex;\n\tint\t\tunknown7[2];\n\tfloat\tunknown[4];\n\tint\t\tunknown8;\n\tunsigned int\t\tseqindex;\n\tint\t\tunknown9[4];\n} hlmdl_sequencelist_t;\n\ntypedef struct\n{\n\tint pose;\n\tint code;\n\tint unknown1;\n\tchar data[64];\n} hlmdl_event_t;\n\n/*\n -----------------------------------------------------------------------------------------------------------------------\n    sequence groups\n -----------------------------------------------------------------------------------------------------------------------\n */\ntypedef struct\n{\n\tchar\t\t\tname[96];\t/* should be split label[32] and name[64] */\n\tunsigned int\tcache;\n\tint\t\t\t\tdata;\n} hlmdl_sequencedata_t;\n\ntypedef struct\n{\n\tint magic;\t\t//IDSQ\n\tint version;\t//10\n\tchar name[64];\n\tint unk1;\n} hlmdl_sequencefile_t;\n/*\n -----------------------------------------------------------------------------------------------------------------------\n    halflife model internal structure\n -----------------------------------------------------------------------------------------------------------------------\n */\n\n#define MAX_ANIM_GROUPS\t16\t//submodel files containing anim data.\ntypedef struct\t//this is stored as the cache. an hlmodel_t is generated when drawing\n{\n\t//updated while rendering...\n\tfloat\tcontroller[5];\t\t\t\t/* Position of bone controllers */\n\tfloat\tadjust[5];\n\n\thlmdl_header_t\t\t\t*header;\n\thlmdl_bone_t\t\t\t*bones;\n\tstruct galiasbone_s\t\t*compatbones;\n\thlmdl_bonecontroller_t\t*bonectls;\n\thlmdl_sequencefile_t\t*animcache[MAX_ANIM_GROUPS];\n\tzonegroup_t\t\t\t\t*memgroup;\n\tstruct hlmodelshaders_s\n\t{\n\t\tchar name[MAX_QPATH];\n\t\tchar *defaultshadertext;\n\t\ttexnums_t defaulttex;\n\t\tshader_t *shader;\n\t\tint w, h;\n\t\tint atlasid;\n\t\tunsigned short x,y;\n\t} *shaders;\n\tshort *skinref;\n\tint numskinrefs;\n\tint numskingroups;\n\n\tint numgeomsets;\n\tstruct\n\t{\n\t\tint numalternatives;\n\t\tstruct hlalternative_s\n\t\t{\n\t\t\tint numsubmeshes;\n\t\t\tmesh_t *submesh;\n\t\t} *alternatives;\n\t} *geomset;\n\tmesh_t mesh;\n\tvbo_t vbo;\n\tqboolean vbobuilt;\n} hlmodel_t;\n\n/* HL mathlib prototypes: */\nvoid\tQuaternionGLAngle(const vec3_t angles, vec4_t quaternion);\nvoid\tQuaternionGLMatrix(float x, float y, float z, float w, vec4_t *GLM);\n//void\tUploadTexture(hlmdl_tex_t *ptexture, qbyte *data, qbyte *pal);\n\nqboolean QDECL Mod_LoadHLModel (model_t *mod, void *buffer, size_t fsize);\n\n/* physics stuff */\nvoid *Mod_GetHalfLifeModelData(model_t *mod);\n\n//reflectioney things, including bone data\nint HLMDL_BoneForName(model_t *mod, const char *name);\nint HLMDL_FrameForName(model_t *mod, const char *name);\nint HLMDL_FrameForAction(model_t *mod, int actionid);\nconst char *HLMDL_FrameNameForNum(model_t *model, int surfaceidx, int num);\nqboolean HLMDL_FrameInfoForNum(model_t *model, int surfaceidx, int num, char **name, int *numframes, float *duration, qboolean *loop, int *act);\nqboolean HLMDL_GetModelEvent(model_t *model, int animation, int eventidx, float *timestamp, int *eventcode, char **eventdata);\nint HLMDL_GetNumBones(model_t *mod, qboolean tagstoo);\nint HLMDL_GetBoneParent(model_t *mod, int bonenum);\nconst char *HLMDL_GetBoneName(model_t *mod, int bonenum);\nint HLMDL_GetBoneData(model_t *model, int firstbone, int lastbone, const framestate_t *fstate, float *result);\nint HLMDL_GetAttachment(model_t *model, int tagnum, float *resultmatrix);\n\n#ifdef HAVE_CLIENT\n//stuff only useful for clients that need to draw stuff\nvoid\tR_DrawHLModel(entity_t\t*curent);\nvoid HLMDL_DrawHitBoxes(entity_t *ent);\nvoid R_HalfLife_GenerateBatches(entity_t *rent, batch_t **batches);\nvoid R_HalfLife_TouchTextures(model_t *mod);\n#endif\n"
  },
  {
    "path": "engine/gl/r_bishaders.h",
    "content": "/*\nWARNING: THIS FILE IS GENERATED BY 'generatebuiltinsl.c'.\nYOU SHOULD NOT EDIT THIS FILE BY HAND\n*/\n\n#ifdef GLQUAKE\n{QR_OPENGL, 110, \"fixedemu\",\n\"!!ver 100-450\\n\"\n\"!!samps sourcetex=0\\n\"\n\n//this shader is present for support for gles/gl3core contexts\n//it is single-texture-with-vertex-colours, and doesn't do anything special.\n//beware that a few things use this, including apparently fonts and bloom rescaling.\n//its really not meant to do anything special.\n\n\"#ifdef VERTEX_SHADER\\n\"\n\"attribute vec2 v_texcoord;\\n\"\n\"varying vec2 tc;\\n\"\n\"#ifndef UC\\n\"\n\"attribute vec4 v_colour;\\n\"\n\"varying vec4 vc;\\n\"\n\"#endif\\n\"\n\"void main ()\\n\"\n\"{\\n\"\n\"tc = v_texcoord;\\n\"\n\"#ifndef UC\\n\"\n\"vc = v_colour;\\n\"\n\"#endif\\n\"\n\"gl_Position = ftetransform();\\n\"\n\"}\\n\"\n\"#endif\\n\"\n\"#ifdef FRAGMENT_SHADER\\n\"\n\"varying vec2 tc;\\n\"\n\"#ifndef UC\\n\"\n\"varying vec4 vc;\\n\"\n\"#else\\n\"\n\"uniform vec4 s_colour;\\n\"\n\"#define vc s_colour\\n\"\n\"#endif\\n\"\n\"float e_time;\\n\"\n\"void main ()\\n\"\n\"{\\n\"\n\"vec4 fc = texture2D(s_sourcetex, tc) * vc;\\n\"\n\"#ifdef ALPHATEST\\n\"\n\"if (!(fc.a ALPHATEST))\\n\"\n\"discard;\\n\"\n\"#endif\\n\"\n\"gl_FragColor = fc;\\n\"\n\"}\\n\"\n\"#endif\\n\"\n},\n#endif\n#ifdef VKQUAKE\n{QR_VULKAN, -1, \"fixedemu\",\n\"\\xFF\\x53\\x50\\x56\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x2C\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x2C\\x00\\x00\\x00\"\n\"\\x6C\\x0A\\x00\\x00\\x98\\x0A\\x00\\x00\\x5C\\x0A\\x00\\x00\\x03\\x02\\x23\\x07\\x00\\x00\\x01\\x00\\x0A\\x00\\x08\\x00\\x4F\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x11\\x00\\x02\\x00\\x01\\x00\\x00\\x00\\x0B\\x00\\x06\\x00\\x01\\x00\\x00\\x00\\x47\\x4C\\x53\\x4C\\x2E\\x73\\x74\\x64\\x2E\\x34\\x35\\x30\\x00\\x00\\x00\\x00\"\n\"\\x0E\\x00\\x03\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x0F\\x00\\x0F\\x00\\x00\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x6D\\x61\\x69\\x6E\\x00\\x00\\x00\\x00\"\n\"\\x1C\\x00\\x00\\x00\\x39\\x00\\x00\\x00\\x3B\\x00\\x00\\x00\\x3E\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x44\\x00\\x00\\x00\\x4B\\x00\\x00\\x00\\x4C\\x00\\x00\\x00\"\n\"\\x4D\\x00\\x00\\x00\\x4E\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x11\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x13\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x13\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x13\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x05\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x13\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xC0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x04\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\xCC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xD0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x13\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xDC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\xE0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xEC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\"\n\"\\x09\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xF0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x0A\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xFC\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x40\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x4C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x13\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x50\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x5C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x60\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\"\n\"\\x11\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x6C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x70\\x01\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x13\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x14\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x90\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x94\\x01\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x13\\x00\\x00\\x00\\x16\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x98\\x01\\x00\\x00\\x47\\x00\\x03\\x00\\x13\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\"\n\"\\x15\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x15\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\"\n\"\\x1C\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x39\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\"\n\"\\x3B\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x3E\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x47\\x00\\x04\\x00\"\n\"\\x40\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x42\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x47\\x00\\x03\\x00\\x42\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x47\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x48\\x00\\x04\\x00\"\n\"\\x48\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x48\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x48\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x48\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x48\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x4C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x48\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x50\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x48\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x5C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x48\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x60\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x48\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x6C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x48\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x70\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x48\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x48\\x00\\x00\\x00\\x09\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x88\\x00\\x00\\x00\\x47\\x00\\x03\\x00\\x48\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x4A\\x00\\x00\\x00\\x22\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x4A\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x4B\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\"\n\"\\x03\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x4C\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x4D\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\"\n\"\\x05\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x4E\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x13\\x00\\x02\\x00\\x02\\x00\\x00\\x00\\x21\\x00\\x03\\x00\"\n\"\\x03\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x16\\x00\\x03\\x00\\x06\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x17\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x04\\x00\\x00\\x00\\x21\\x00\\x03\\x00\\x08\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x18\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x17\\x00\\x04\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x03\\x00\\x00\\x00\"\n\"\\x15\\x00\\x04\\x00\\x0F\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x0F\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x04\\x00\\x00\\x00\"\n\"\\x1C\\x00\\x04\\x00\\x11\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x17\\x00\\x04\\x00\\x12\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x1E\\x00\\x19\\x00\\x13\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x11\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x12\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x14\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x13\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x14\\x00\\x00\\x00\\x15\\x00\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\x15\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x17\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x18\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x0E\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\\x1C\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\"\n\"\\x00\\x00\\x80\\x3F\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x24\\x00\\x00\\x00\\x00\\x00\\x80\\xBF\\x2B\\x00\\x04\\x00\\x0F\\x00\\x00\\x00\\x25\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x26\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x0F\\x00\\x00\\x00\\x2B\\x00\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x0F\\x00\\x00\\x00\\x2E\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x32\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x40\\x20\\x00\\x04\\x00\\x38\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x38\\x00\\x00\\x00\\x39\\x00\\x00\\x00\"\n\"\\x03\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x3A\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x3A\\x00\\x00\\x00\\x3B\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x3D\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x3D\\x00\\x00\\x00\\x3E\\x00\\x00\\x00\"\n\"\\x03\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x3F\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x3F\\x00\\x00\\x00\\x40\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x1E\\x00\\x03\\x00\\x42\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x43\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x42\\x00\\x00\\x00\"\n\"\\x3B\\x00\\x04\\x00\\x43\\x00\\x00\\x00\\x44\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x32\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x47\\x00\\x00\\x00\\x04\\x00\\x00\\x00\"\n\"\\x1E\\x00\\x0C\\x00\\x48\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x49\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x48\\x00\\x00\\x00\"\n\"\\x3B\\x00\\x04\\x00\\x49\\x00\\x00\\x00\\x4A\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x3A\\x00\\x00\\x00\\x4B\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x3B\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\\x4C\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\\x4D\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x3B\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\\x4E\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x36\\x00\\x05\\x00\\x02\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x03\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x05\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x12\\x00\\x00\\x00\\x3C\\x00\\x00\\x00\\x3B\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\"\n\"\\x39\\x00\\x00\\x00\\x3C\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x41\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x3E\\x00\\x00\\x00\"\n\"\\x41\\x00\\x00\\x00\\x39\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x45\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x3D\\x00\\x00\\x00\\x46\\x00\\x00\\x00\"\n\"\\x44\\x00\\x00\\x00\\x17\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x46\\x00\\x00\\x00\\x45\\x00\\x00\\x00\\xFD\\x00\\x01\\x00\\x38\\x00\\x01\\x00\\x36\\x00\\x05\\x00\"\n\"\\x07\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x0A\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\"\n\"\\x0C\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x18\\x00\\x00\\x00\\x19\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x17\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x0D\\x00\\x00\\x00\\x1A\\x00\\x00\\x00\\x19\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0E\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x1C\\x00\\x00\\x00\\x51\\x00\\x05\\x00\"\n\"\\x06\\x00\\x00\\x00\\x1F\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x50\\x00\\x07\\x00\\x07\\x00\\x00\\x00\"\n\"\\x22\\x00\\x00\\x00\\x1F\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x91\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x1A\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x0C\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x26\\x00\\x00\\x00\\x27\\x00\\x00\\x00\"\n\"\\x0C\\x00\\x00\\x00\\x25\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x28\\x00\\x00\\x00\\x27\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\"\n\"\\x29\\x00\\x00\\x00\\x28\\x00\\x00\\x00\\x24\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x26\\x00\\x00\\x00\\x2A\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x25\\x00\\x00\\x00\"\n\"\\x3E\\x00\\x03\\x00\\x2A\\x00\\x00\\x00\\x29\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x26\\x00\\x00\\x00\\x2C\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x2B\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\\x2C\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x26\\x00\\x00\\x00\\x2F\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\"\n\"\\x2E\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x30\\x00\\x00\\x00\\x2F\\x00\\x00\\x00\\x81\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x31\\x00\\x00\\x00\"\n\"\\x2D\\x00\\x00\\x00\\x30\\x00\\x00\\x00\\x88\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x33\\x00\\x00\\x00\\x31\\x00\\x00\\x00\\x32\\x00\\x00\\x00\\x41\\x00\\x05\\x00\"\n\"\\x26\\x00\\x00\\x00\\x34\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x2B\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x34\\x00\\x00\\x00\\x33\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x07\\x00\\x00\\x00\\x35\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\xFE\\x00\\x02\\x00\\x35\\x00\\x00\\x00\\x38\\x00\\x01\\x00\\x03\\x02\\x23\\x07\\x00\\x00\\x01\\x00\"\n\"\\x0A\\x00\\x08\\x00\\x52\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x11\\x00\\x02\\x00\\x01\\x00\\x00\\x00\\x0B\\x00\\x06\\x00\\x01\\x00\\x00\\x00\\x47\\x4C\\x53\\x4C\"\n\"\\x2E\\x73\\x74\\x64\\x2E\\x34\\x35\\x30\\x00\\x00\\x00\\x00\\x0E\\x00\\x03\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x0F\\x00\\x08\\x00\\x04\\x00\\x00\\x00\"\n\"\\x04\\x00\\x00\\x00\\x6D\\x61\\x69\\x6E\\x00\\x00\\x00\\x00\\x11\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x46\\x00\\x00\\x00\\x10\\x00\\x03\\x00\\x04\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\x21\\x00\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x11\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x15\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x19\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x46\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x4B\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x4C\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x4C\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x4C\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x4C\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x4C\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x4C\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x4C\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x4C\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x80\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x4C\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x4C\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xC0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x4C\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\xCC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x4C\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xD0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x4C\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xDC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x4C\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xE0\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x4C\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xEC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x4C\\x00\\x00\\x00\\x09\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\xF0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x4C\\x00\\x00\\x00\\x0A\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xFC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x4C\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x4C\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x40\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x4C\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x4C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x4C\\x00\\x00\\x00\"\n\"\\x0E\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x50\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x4C\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x5C\\x01\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x4C\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x60\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x4C\\x00\\x00\\x00\\x11\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x6C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x4C\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x70\\x01\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x4C\\x00\\x00\\x00\\x13\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x4C\\x00\\x00\\x00\\x14\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x90\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x4C\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x94\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x4C\\x00\\x00\\x00\"\n\"\\x16\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x98\\x01\\x00\\x00\\x47\\x00\\x03\\x00\\x4C\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x4E\\x00\\x00\\x00\"\n\"\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x4E\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x4F\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x4F\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x4F\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x4F\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x40\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x4F\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x4C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x4F\\x00\\x00\\x00\"\n\"\\x03\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x50\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x4F\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x5C\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x4F\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x60\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x4F\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x6C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x4F\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x70\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x4F\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x4F\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x88\\x00\\x00\\x00\\x47\\x00\\x03\\x00\\x4F\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x51\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x51\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x13\\x00\\x02\\x00\\x02\\x00\\x00\\x00\\x21\\x00\\x03\\x00\\x03\\x00\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\x16\\x00\\x03\\x00\\x06\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x17\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x04\\x00\\x00\\x00\"\n\"\\x20\\x00\\x04\\x00\\x08\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x19\\x00\\x09\\x00\\x0A\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x1B\\x00\\x03\\x00\\x0B\\x00\\x00\\x00\\x0A\\x00\\x00\\x00\"\n\"\\x20\\x00\\x04\\x00\\x0C\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x0C\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x17\\x00\\x04\\x00\\x0F\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x10\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\"\n\"\\x3B\\x00\\x04\\x00\\x10\\x00\\x00\\x00\\x11\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x14\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x3B\\x00\\x04\\x00\\x14\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x15\\x00\\x04\\x00\\x18\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x32\\x00\\x04\\x00\\x18\\x00\\x00\\x00\\x19\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x18\\x00\\x00\\x00\\x1A\\x00\\x00\\x00\\x04\\x00\\x00\\x00\"\n\"\\x14\\x00\\x02\\x00\\x1B\\x00\\x00\\x00\\x34\\x00\\x06\\x00\\x1B\\x00\\x00\\x00\\x1C\\x00\\x00\\x00\\xAA\\x00\\x00\\x00\\x19\\x00\\x00\\x00\\x1A\\x00\\x00\\x00\"\n\"\\x2B\\x00\\x04\\x00\\x18\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x34\\x00\\x06\\x00\\x1B\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\xAA\\x00\\x00\\x00\"\n\"\\x19\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x15\\x00\\x04\\x00\\x24\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x24\\x00\\x00\\x00\"\n\"\\x25\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x26\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\"\n\"\\x29\\x00\\x00\\x00\\x00\\x00\\x00\\x3F\\x2B\\x00\\x04\\x00\\x18\\x00\\x00\\x00\\x2F\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x34\\x00\\x06\\x00\\x1B\\x00\\x00\\x00\"\n\"\\x30\\x00\\x00\\x00\\xAA\\x00\\x00\\x00\\x19\\x00\\x00\\x00\\x2F\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x35\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x2B\\x00\\x04\\x00\\x18\\x00\\x00\\x00\\x3B\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x34\\x00\\x06\\x00\\x1B\\x00\\x00\\x00\\x3C\\x00\\x00\\x00\\xAA\\x00\\x00\\x00\"\n\"\\x19\\x00\\x00\\x00\\x3B\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x45\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x45\\x00\\x00\\x00\"\n\"\\x46\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x18\\x00\\x04\\x00\\x48\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x17\\x00\\x04\\x00\\x49\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x24\\x00\\x00\\x00\\x4A\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x1C\\x00\\x04\\x00\\x4B\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x4A\\x00\\x00\\x00\\x1E\\x00\\x19\\x00\\x4C\\x00\\x00\\x00\\x48\\x00\\x00\\x00\\x48\\x00\\x00\\x00\\x48\\x00\\x00\\x00\\x49\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x49\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x49\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x49\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x4B\\x00\\x00\\x00\"\n\"\\x49\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x49\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x49\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x4D\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x4C\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\"\n\"\\x4D\\x00\\x00\\x00\\x4E\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x1E\\x00\\x0C\\x00\\x4F\\x00\\x00\\x00\\x48\\x00\\x00\\x00\\x49\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x49\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x49\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\\x20\\x00\\x04\\x00\"\n\"\\x50\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x4F\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x50\\x00\\x00\\x00\\x51\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x36\\x00\\x05\\x00\"\n\"\\x02\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x05\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x08\\x00\\x00\\x00\"\n\"\\x09\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0F\\x00\\x00\\x00\"\n\"\\x12\\x00\\x00\\x00\\x11\\x00\\x00\\x00\\x57\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x13\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x07\\x00\\x00\\x00\\x16\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x17\\x00\\x00\\x00\\x13\\x00\\x00\\x00\\x16\\x00\\x00\\x00\"\n\"\\x3E\\x00\\x03\\x00\\x09\\x00\\x00\\x00\\x17\\x00\\x00\\x00\\xF7\\x00\\x03\\x00\\x1E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\\x1C\\x00\\x00\\x00\"\n\"\\x1D\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x1D\\x00\\x00\\x00\\xFC\\x00\\x01\\x00\\xF8\\x00\\x02\\x00\\x1E\\x00\\x00\\x00\\xF7\\x00\\x03\\x00\"\n\"\\x23\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\\x21\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x2E\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x22\\x00\\x00\\x00\"\n\"\\x41\\x00\\x05\\x00\\x26\\x00\\x00\\x00\\x27\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x25\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x28\\x00\\x00\\x00\"\n\"\\x27\\x00\\x00\\x00\\xB8\\x00\\x05\\x00\\x1B\\x00\\x00\\x00\\x2A\\x00\\x00\\x00\\x28\\x00\\x00\\x00\\x29\\x00\\x00\\x00\\xF7\\x00\\x03\\x00\\x2C\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\\x2A\\x00\\x00\\x00\\x2B\\x00\\x00\\x00\\x2C\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x2B\\x00\\x00\\x00\\xFC\\x00\\x01\\x00\"\n\"\\xF8\\x00\\x02\\x00\\x2C\\x00\\x00\\x00\\xF9\\x00\\x02\\x00\\x23\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x2E\\x00\\x00\\x00\\xF7\\x00\\x03\\x00\\x32\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\\x30\\x00\\x00\\x00\\x31\\x00\\x00\\x00\\x3A\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x31\\x00\\x00\\x00\\x41\\x00\\x05\\x00\"\n\"\\x26\\x00\\x00\\x00\\x33\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x25\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x34\\x00\\x00\\x00\\x33\\x00\\x00\\x00\"\n\"\\xBC\\x00\\x05\\x00\\x1B\\x00\\x00\\x00\\x36\\x00\\x00\\x00\\x34\\x00\\x00\\x00\\x35\\x00\\x00\\x00\\xF7\\x00\\x03\\x00\\x38\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\xFA\\x00\\x04\\x00\\x36\\x00\\x00\\x00\\x37\\x00\\x00\\x00\\x38\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x37\\x00\\x00\\x00\\xFC\\x00\\x01\\x00\\xF8\\x00\\x02\\x00\"\n\"\\x38\\x00\\x00\\x00\\xF9\\x00\\x02\\x00\\x32\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x3A\\x00\\x00\\x00\\xF7\\x00\\x03\\x00\\x3E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\xFA\\x00\\x04\\x00\\x3C\\x00\\x00\\x00\\x3D\\x00\\x00\\x00\\x3E\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x3D\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x26\\x00\\x00\\x00\"\n\"\\x3F\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x25\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x3F\\x00\\x00\\x00\\xBE\\x00\\x05\\x00\"\n\"\\x1B\\x00\\x00\\x00\\x41\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x29\\x00\\x00\\x00\\xF7\\x00\\x03\\x00\\x43\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\"\n\"\\x41\\x00\\x00\\x00\\x42\\x00\\x00\\x00\\x43\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x42\\x00\\x00\\x00\\xFC\\x00\\x01\\x00\\xF8\\x00\\x02\\x00\\x43\\x00\\x00\\x00\"\n\"\\xF9\\x00\\x02\\x00\\x3E\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x3E\\x00\\x00\\x00\\xF9\\x00\\x02\\x00\\x32\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x32\\x00\\x00\\x00\"\n\"\\xF9\\x00\\x02\\x00\\x23\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x23\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x47\\x00\\x00\\x00\\x09\\x00\\x00\\x00\"\n\"\\x3E\\x00\\x03\\x00\\x46\\x00\\x00\\x00\\x47\\x00\\x00\\x00\\xFD\\x00\\x01\\x00\\x38\\x00\\x01\\x00\"},\n#endif\n#ifdef D3D11QUAKE\n{QR_DIRECT3D11, 11, \"fixedemu\",\n\"!!samps 1\\n\"\n\"struct a2v\\n\"\n\"{\\n\"\n\"float4 pos: POSITION;\\n\"\n\"float2 tc: TEXCOORD0;\\n\"\n\"float4 vcol: COLOR0;\\n\"\n\"};\\n\"\n\"struct v2f\\n\"\n\"{\\n\"\n\"float4 pos: SV_POSITION;\\n\"\n\"float2 tc: TEXCOORD0;\\n\"\n\"float4 vcol: COLOR0;\\n\"\n\"};\\n\"\n\n\"#include <ftedefs.h>\\n\"\n\n\"#ifdef VERTEX_SHADER\\n\"\n\"v2f main (a2v inp)\\n\"\n\"{\\n\"\n\"v2f outp;\\n\"\n\"outp.pos = mul(m_model, inp.pos);\\n\"\n\"outp.pos = mul(m_view, outp.pos);\\n\"\n\"outp.pos = mul(m_projection, outp.pos);\\n\"\n\"outp.tc = inp.tc;\\n\"\n\"outp.vcol = inp.vcol;\\n\"\n\"return outp;\\n\"\n\"}\\n\"\n\"#endif\\n\"\n\n\"#ifdef FRAGMENT_SHADER\\n\"\n\"Texture2D t_t0 : register(t0);\\n\"\n\"SamplerState s_t0 : register(s0);\\n\"\n\"float4 main (v2f inp) : SV_TARGET\\n\"\n\"{\\n\"\n\"float4 tc = t_t0.Sample(s_t0, inp.tc).rgba;\\n\"\n\"tc.rgba *= inp.vcol.bgra;\\n\"\n\"#ifdef ALPHATEST\\n\"\n\"if (!(tc.a ALPHATEST))\\n\"\n\"discard;\\n\"\n\"#endif\\n\"\n\"return tc;\\n\"\n\"}\\n\"\n\"#endif\\n\"\n},\n#endif\n#ifdef VKQUAKE\n{QR_VULKAN, -1, \"fixedemu_flat\",\n\"\\xFF\\x53\\x50\\x56\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x2C\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x2C\\x00\\x00\\x00\"\n\"\\x2C\\x0A\\x00\\x00\\x58\\x0A\\x00\\x00\\xA8\\x0A\\x00\\x00\\x03\\x02\\x23\\x07\\x00\\x00\\x01\\x00\\x0A\\x00\\x08\\x00\\x4D\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x11\\x00\\x02\\x00\\x01\\x00\\x00\\x00\\x0B\\x00\\x06\\x00\\x01\\x00\\x00\\x00\\x47\\x4C\\x53\\x4C\\x2E\\x73\\x74\\x64\\x2E\\x34\\x35\\x30\\x00\\x00\\x00\\x00\"\n\"\\x0E\\x00\\x03\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x0F\\x00\\x0E\\x00\\x00\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x6D\\x61\\x69\\x6E\\x00\\x00\\x00\\x00\"\n\"\\x1C\\x00\\x00\\x00\\x39\\x00\\x00\\x00\\x3B\\x00\\x00\\x00\\x3F\\x00\\x00\\x00\\x48\\x00\\x00\\x00\\x49\\x00\\x00\\x00\\x4A\\x00\\x00\\x00\\x4B\\x00\\x00\\x00\"\n\"\\x4C\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x11\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x13\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x13\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x13\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x13\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x80\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x13\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xC0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\xCC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xD0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xDC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xE0\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xEC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x09\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\xF0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x0A\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xFC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x13\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x40\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x4C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\"\n\"\\x0E\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x50\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x5C\\x01\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x60\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x11\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x6C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x70\\x01\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x13\\x00\\x00\\x00\\x13\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x14\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x90\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x94\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\"\n\"\\x16\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x98\\x01\\x00\\x00\\x47\\x00\\x03\\x00\\x13\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x15\\x00\\x00\\x00\"\n\"\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x15\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x1C\\x00\\x00\\x00\"\n\"\\x1E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x39\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x3B\\x00\\x00\\x00\"\n\"\\x1E\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x3D\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x03\\x00\"\n\"\\x3D\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x43\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x44\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x44\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x44\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x44\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x40\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x44\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x4C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x44\\x00\\x00\\x00\"\n\"\\x03\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x50\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x44\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x5C\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x44\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x60\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x44\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x6C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x44\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x70\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x44\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x44\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x88\\x00\\x00\\x00\\x47\\x00\\x03\\x00\\x44\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x46\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x46\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x48\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x49\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x4A\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x04\\x00\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x4B\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x4C\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x13\\x00\\x02\\x00\\x02\\x00\\x00\\x00\\x21\\x00\\x03\\x00\\x03\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x16\\x00\\x03\\x00\\x06\\x00\\x00\\x00\\x20\\x00\\x00\\x00\"\n\"\\x17\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x21\\x00\\x03\\x00\\x08\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x20\\x00\\x04\\x00\"\n\"\\x0B\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x18\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x17\\x00\\x04\\x00\"\n\"\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x15\\x00\\x04\\x00\\x0F\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\"\n\"\\x0F\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x1C\\x00\\x04\\x00\\x11\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x17\\x00\\x04\\x00\"\n\"\\x12\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x1E\\x00\\x19\\x00\\x13\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\"\n\"\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x11\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x14\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x13\\x00\\x00\\x00\"\n\"\\x3B\\x00\\x04\\x00\\x14\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x15\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x2B\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x17\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x18\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\"\n\"\\x20\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\\x1C\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x00\\x00\\x80\\x3F\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x24\\x00\\x00\\x00\\x00\\x00\\x80\\xBF\"\n\"\\x2B\\x00\\x04\\x00\\x0F\\x00\\x00\\x00\\x25\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x26\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x2B\\x00\\x04\\x00\\x0F\\x00\\x00\\x00\\x2B\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x0F\\x00\\x00\\x00\\x2E\\x00\\x00\\x00\\x03\\x00\\x00\\x00\"\n\"\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x32\\x00\\x00\\x00\\x00\\x00\\x00\\x40\\x20\\x00\\x04\\x00\\x38\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x12\\x00\\x00\\x00\"\n\"\\x3B\\x00\\x04\\x00\\x38\\x00\\x00\\x00\\x39\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x3A\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x12\\x00\\x00\\x00\"\n\"\\x3B\\x00\\x04\\x00\\x3A\\x00\\x00\\x00\\x3B\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x1E\\x00\\x03\\x00\\x3D\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x20\\x00\\x04\\x00\"\n\"\\x3E\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x3D\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x3E\\x00\\x00\\x00\\x3F\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x20\\x00\\x04\\x00\"\n\"\\x41\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x32\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x43\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x1E\\x00\\x0C\\x00\"\n\"\\x44\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x45\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x44\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\"\n\"\\x45\\x00\\x00\\x00\\x46\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x47\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\"\n\"\\x47\\x00\\x00\\x00\\x48\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x3A\\x00\\x00\\x00\\x49\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\"\n\"\\x1B\\x00\\x00\\x00\\x4A\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\\x4B\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\"\n\"\\x1B\\x00\\x00\\x00\\x4C\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x36\\x00\\x05\\x00\\x02\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x03\\x00\\x00\\x00\"\n\"\\xF8\\x00\\x02\\x00\\x05\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x12\\x00\\x00\\x00\\x3C\\x00\\x00\\x00\\x3B\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x39\\x00\\x00\\x00\"\n\"\\x3C\\x00\\x00\\x00\\x39\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x41\\x00\\x00\\x00\\x42\\x00\\x00\\x00\"\n\"\\x3F\\x00\\x00\\x00\\x17\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x42\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\xFD\\x00\\x01\\x00\\x38\\x00\\x01\\x00\\x36\\x00\\x05\\x00\"\n\"\\x07\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x0A\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\"\n\"\\x0C\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x18\\x00\\x00\\x00\\x19\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x17\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x0D\\x00\\x00\\x00\\x1A\\x00\\x00\\x00\\x19\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0E\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x1C\\x00\\x00\\x00\\x51\\x00\\x05\\x00\"\n\"\\x06\\x00\\x00\\x00\\x1F\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x50\\x00\\x07\\x00\\x07\\x00\\x00\\x00\"\n\"\\x22\\x00\\x00\\x00\\x1F\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x91\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x1A\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x0C\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x26\\x00\\x00\\x00\\x27\\x00\\x00\\x00\"\n\"\\x0C\\x00\\x00\\x00\\x25\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x28\\x00\\x00\\x00\\x27\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\"\n\"\\x29\\x00\\x00\\x00\\x28\\x00\\x00\\x00\\x24\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x26\\x00\\x00\\x00\\x2A\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x25\\x00\\x00\\x00\"\n\"\\x3E\\x00\\x03\\x00\\x2A\\x00\\x00\\x00\\x29\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x26\\x00\\x00\\x00\\x2C\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x2B\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\\x2C\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x26\\x00\\x00\\x00\\x2F\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\"\n\"\\x2E\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x30\\x00\\x00\\x00\\x2F\\x00\\x00\\x00\\x81\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x31\\x00\\x00\\x00\"\n\"\\x2D\\x00\\x00\\x00\\x30\\x00\\x00\\x00\\x88\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x33\\x00\\x00\\x00\\x31\\x00\\x00\\x00\\x32\\x00\\x00\\x00\\x41\\x00\\x05\\x00\"\n\"\\x26\\x00\\x00\\x00\\x34\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x2B\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x34\\x00\\x00\\x00\\x33\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x07\\x00\\x00\\x00\\x35\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\xFE\\x00\\x02\\x00\\x35\\x00\\x00\\x00\\x38\\x00\\x01\\x00\\x03\\x02\\x23\\x07\\x00\\x00\\x01\\x00\"\n\"\\x0A\\x00\\x08\\x00\\x56\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x11\\x00\\x02\\x00\\x01\\x00\\x00\\x00\\x0B\\x00\\x06\\x00\\x01\\x00\\x00\\x00\\x47\\x4C\\x53\\x4C\"\n\"\\x2E\\x73\\x74\\x64\\x2E\\x34\\x35\\x30\\x00\\x00\\x00\\x00\\x0E\\x00\\x03\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x0F\\x00\\x07\\x00\\x04\\x00\\x00\\x00\"\n\"\\x04\\x00\\x00\\x00\\x6D\\x61\\x69\\x6E\\x00\\x00\\x00\\x00\\x11\\x00\\x00\\x00\\x4A\\x00\\x00\\x00\\x10\\x00\\x03\\x00\\x04\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x11\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x14\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x47\\x00\\x03\\x00\\x14\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x1D\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x4A\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x4F\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x10\\x00\\x00\\x00\"\n\"\\x48\\x00\\x04\\x00\\x50\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x50\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x50\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x50\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x50\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x50\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x50\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x05\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x50\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x50\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x50\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xC0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x50\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xCC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x50\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\xD0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x50\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xDC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x50\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xE0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x50\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xEC\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x50\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xF0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x50\\x00\\x00\\x00\\x0A\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\xFC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x50\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x50\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x50\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x4C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x50\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x50\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x50\\x00\\x00\\x00\"\n\"\\x0F\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x5C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x50\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x60\\x01\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x50\\x00\\x00\\x00\\x11\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x6C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x50\\x00\\x00\\x00\\x12\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x70\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x50\\x00\\x00\\x00\\x13\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x01\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x50\\x00\\x00\\x00\\x14\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x90\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x50\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x94\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x50\\x00\\x00\\x00\\x16\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x98\\x01\\x00\\x00\\x47\\x00\\x03\\x00\\x50\\x00\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x52\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x52\\x00\\x00\\x00\\x21\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x53\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x53\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x53\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x53\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x53\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x4C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x53\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x50\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x53\\x00\\x00\\x00\"\n\"\\x04\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x5C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x53\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x60\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x53\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x6C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x53\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x70\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x53\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x53\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x88\\x00\\x00\\x00\\x47\\x00\\x03\\x00\\x53\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\"\n\"\\x55\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x55\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x13\\x00\\x02\\x00\"\n\"\\x02\\x00\\x00\\x00\\x21\\x00\\x03\\x00\\x03\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x16\\x00\\x03\\x00\\x06\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x17\\x00\\x04\\x00\"\n\"\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x08\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x19\\x00\\x09\\x00\"\n\"\\x0A\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x1B\\x00\\x03\\x00\\x0B\\x00\\x00\\x00\\x0A\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x0C\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\"\n\"\\x0C\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x17\\x00\\x04\\x00\\x0F\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x20\\x00\\x04\\x00\"\n\"\\x10\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x10\\x00\\x00\\x00\\x11\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x1E\\x00\\x03\\x00\"\n\"\\x14\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x15\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x14\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x15\\x00\\x00\\x00\"\n\"\\x16\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x15\\x00\\x04\\x00\\x17\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x17\\x00\\x00\\x00\"\n\"\\x18\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x19\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x32\\x00\\x04\\x00\\x17\\x00\\x00\\x00\"\n\"\\x1D\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x17\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x14\\x00\\x02\\x00\\x1F\\x00\\x00\\x00\"\n\"\\x34\\x00\\x06\\x00\\x1F\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\xAA\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x17\\x00\\x00\\x00\"\n\"\\x24\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x34\\x00\\x06\\x00\\x1F\\x00\\x00\\x00\\x25\\x00\\x00\\x00\\xAA\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x24\\x00\\x00\\x00\"\n\"\\x15\\x00\\x04\\x00\\x28\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x28\\x00\\x00\\x00\\x29\\x00\\x00\\x00\\x03\\x00\\x00\\x00\"\n\"\\x20\\x00\\x04\\x00\\x2A\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\\x00\\x00\\x00\\x3F\"\n\"\\x2B\\x00\\x04\\x00\\x17\\x00\\x00\\x00\\x33\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x34\\x00\\x06\\x00\\x1F\\x00\\x00\\x00\\x34\\x00\\x00\\x00\\xAA\\x00\\x00\\x00\"\n\"\\x1D\\x00\\x00\\x00\\x33\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x39\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x17\\x00\\x00\\x00\"\n\"\\x3F\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x34\\x00\\x06\\x00\\x1F\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\xAA\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x3F\\x00\\x00\\x00\"\n\"\\x20\\x00\\x04\\x00\\x49\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x49\\x00\\x00\\x00\\x4A\\x00\\x00\\x00\\x03\\x00\\x00\\x00\"\n\"\\x18\\x00\\x04\\x00\\x4C\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x17\\x00\\x04\\x00\\x4D\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x03\\x00\\x00\\x00\"\n\"\\x2B\\x00\\x04\\x00\\x28\\x00\\x00\\x00\\x4E\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x1C\\x00\\x04\\x00\\x4F\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x4E\\x00\\x00\\x00\"\n\"\\x1E\\x00\\x19\\x00\\x50\\x00\\x00\\x00\\x4C\\x00\\x00\\x00\\x4C\\x00\\x00\\x00\\x4C\\x00\\x00\\x00\\x4D\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x4D\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x4D\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x4D\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x4F\\x00\\x00\\x00\\x4D\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x4D\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x4D\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x0F\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x51\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x50\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x51\\x00\\x00\\x00\\x52\\x00\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\x1E\\x00\\x0C\\x00\\x53\\x00\\x00\\x00\\x4C\\x00\\x00\\x00\\x4D\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x4D\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x4D\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x54\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x53\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x54\\x00\\x00\\x00\\x55\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x36\\x00\\x05\\x00\\x02\\x00\\x00\\x00\\x04\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x05\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x08\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0F\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x11\\x00\\x00\\x00\"\n\"\\x57\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x13\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x19\\x00\\x00\\x00\\x1A\\x00\\x00\\x00\"\n\"\\x16\\x00\\x00\\x00\\x18\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x1B\\x00\\x00\\x00\\x1A\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x07\\x00\\x00\\x00\"\n\"\\x1C\\x00\\x00\\x00\\x13\\x00\\x00\\x00\\x1B\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x09\\x00\\x00\\x00\\x1C\\x00\\x00\\x00\\xF7\\x00\\x03\\x00\\x22\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\\x20\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x21\\x00\\x00\\x00\\xFC\\x00\\x01\\x00\"\n\"\\xF8\\x00\\x02\\x00\\x22\\x00\\x00\\x00\\xF7\\x00\\x03\\x00\\x27\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\\x25\\x00\\x00\\x00\\x26\\x00\\x00\\x00\"\n\"\\x32\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x26\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x2A\\x00\\x00\\x00\\x2B\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x29\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x2C\\x00\\x00\\x00\\x2B\\x00\\x00\\x00\\xB8\\x00\\x05\\x00\\x1F\\x00\\x00\\x00\\x2E\\x00\\x00\\x00\\x2C\\x00\\x00\\x00\"\n\"\\x2D\\x00\\x00\\x00\\xF7\\x00\\x03\\x00\\x30\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\\x2E\\x00\\x00\\x00\\x2F\\x00\\x00\\x00\\x30\\x00\\x00\\x00\"\n\"\\xF8\\x00\\x02\\x00\\x2F\\x00\\x00\\x00\\xFC\\x00\\x01\\x00\\xF8\\x00\\x02\\x00\\x30\\x00\\x00\\x00\\xF9\\x00\\x02\\x00\\x27\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\"\n\"\\x32\\x00\\x00\\x00\\xF7\\x00\\x03\\x00\\x36\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\\x34\\x00\\x00\\x00\\x35\\x00\\x00\\x00\\x3E\\x00\\x00\\x00\"\n\"\\xF8\\x00\\x02\\x00\\x35\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x2A\\x00\\x00\\x00\\x37\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x29\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x06\\x00\\x00\\x00\\x38\\x00\\x00\\x00\\x37\\x00\\x00\\x00\\xBC\\x00\\x05\\x00\\x1F\\x00\\x00\\x00\\x3A\\x00\\x00\\x00\\x38\\x00\\x00\\x00\\x39\\x00\\x00\\x00\"\n\"\\xF7\\x00\\x03\\x00\\x3C\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\\x3A\\x00\\x00\\x00\\x3B\\x00\\x00\\x00\\x3C\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\"\n\"\\x3B\\x00\\x00\\x00\\xFC\\x00\\x01\\x00\\xF8\\x00\\x02\\x00\\x3C\\x00\\x00\\x00\\xF9\\x00\\x02\\x00\\x36\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x3E\\x00\\x00\\x00\"\n\"\\xF7\\x00\\x03\\x00\\x42\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\\x40\\x00\\x00\\x00\\x41\\x00\\x00\\x00\\x42\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\"\n\"\\x41\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x2A\\x00\\x00\\x00\\x43\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x29\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\"\n\"\\x44\\x00\\x00\\x00\\x43\\x00\\x00\\x00\\xBE\\x00\\x05\\x00\\x1F\\x00\\x00\\x00\\x45\\x00\\x00\\x00\\x44\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\\xF7\\x00\\x03\\x00\"\n\"\\x47\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\\x45\\x00\\x00\\x00\\x46\\x00\\x00\\x00\\x47\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x46\\x00\\x00\\x00\"\n\"\\xFC\\x00\\x01\\x00\\xF8\\x00\\x02\\x00\\x47\\x00\\x00\\x00\\xF9\\x00\\x02\\x00\\x42\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x42\\x00\\x00\\x00\\xF9\\x00\\x02\\x00\"\n\"\\x36\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x36\\x00\\x00\\x00\\xF9\\x00\\x02\\x00\\x27\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x27\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x07\\x00\\x00\\x00\\x4B\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x4A\\x00\\x00\\x00\\x4B\\x00\\x00\\x00\\xFD\\x00\\x01\\x00\\x38\\x00\\x01\\x00\"\n\"\"},\n#endif\n#ifdef GLQUAKE\n{QR_OPENGL, 110, \"altwater\",\n\"!!cvardf r_glsl_turbscale_reflect=1 //simpler scaler\\n\"\n\"!!cvardf r_glsl_turbscale_refract=1 //simpler scaler\\n\"\n\"!!samps diffuse normalmap\\n\"\n\"!!samps    refract=0 //always present\\n\"\n\"!!samps =REFLECT reflect=1\\n\"\n\"!!samps =RIPPLEMAP ripplemap=2\\n\"\n\"!!samps =DEPTH   refractdepth=3\\n\"\n\"!!permu FOG\\n\"\n\n\"#include \\\"sys/defs.h\\\"\\n\"\n\n//modifier: REFLECT\t\t(s_t2 is a reflection instead of diffusemap)\n//modifier: STRENGTH_REFL\t(distortion strength - 0.1 = fairly gentle, 0.2 = big waves)\n//modifier: STRENGTH_REFL\t(distortion strength - 0.1 = fairly gentle, 0.2 = big waves)\n//modifier: FRESNEL_EXP\t(5=water)\n//modifier: TXSCALE\t\t(wave size - 0.2)\n//modifier: RIPPLEMAP\t\t(s_t3 contains a ripplemap\n//modifier: TINT_REFR\t\t(some colour value)\n//modifier: TINT_REFL\t\t(some colour value)\n//modifier: ALPHA\t\t(mix in the normal water texture over the top)\n//modifier: USEMODS\t\t(use single-texture scrolling via tcmods - note, also forces the engine to actually use tcmod etc)\n\n//a few notes on DP compat:\n//'dpwater' makes numerous assumptions about DP internals\n//by default there is a single pass that uses the pass's normal tcmods\n//the fresnel has a user-supplied min+max rather than an exponent\n//both parts are tinted individually\n//if alpha is enabled, the regular water texture is blended over the top, again using the same crappy tcmods...\n\n//legacy crap\n\"#ifndef FRESNEL\\n\"\n\"#define FRESNEL 5.0\\n\"\n\"#endif\\n\"\n\"#ifndef TINT\\n\"\n\"#define TINT 0.7,0.8,0.7\\n\"\n\"#endif\\n\"\n\"#ifndef STRENGTH\\n\"\n\"#define STRENGTH 0.1\\n\"\n\"#endif\\n\"\n\"#ifndef TXSCALE\\n\"\n\"#define TXSCALE 0.2\\n\"\n\"#endif\\n\"\n\n//current values (referring to legacy defaults where needed)\n\"#ifndef FRESNEL_EXP\\n\"\n\"#define FRESNEL_EXP 5.0\\n\"\n\"#endif\\n\"\n\"#ifndef FRESNEL_MIN\\n\"\n\"#define FRESNEL_MIN 0.0\\n\"\n\"#endif\\n\"\n\"#ifndef FRESNEL_RANGE\\n\"\n\"#define FRESNEL_RANGE 1.0\\n\"\n\"#endif\\n\"\n\"#ifndef STRENGTH_REFL\\n\"\n\"#define STRENGTH_REFL STRENGTH\\n\"\n\"#endif\\n\"\n\"#ifndef STRENGTH_REFR\\n\"\n\"#define STRENGTH_REFR STRENGTH\\n\"\n\"#endif\\n\"\n\"#ifndef TXSCALE1\\n\"\n\"#define TXSCALE1 TXSCALE\\n\"\n\"#endif\\n\"\n\"#ifndef TXSCALE2\\n\"\n\"#define TXSCALE2 TXSCALE\\n\"\n\"#endif\\n\"\n\"#ifndef TINT_REFR\\n\"\n\"#define TINT_REFR TINT\\n\"\n\"#endif\\n\"\n\"#ifndef TINT_REFL\\n\"\n\"#define TINT_REFL 1.0,1.0,1.0\\n\"\n\"#endif\\n\"\n\"#ifndef FOGTINT\\n\"\n\"#define FOGTINT 0.2,0.3,0.2\\n\"\n\"#endif\\n\"\n\n\"varying vec2 tc;\\n\"\n\"varying vec4 tf;\\n\"\n\"varying vec3 norm;\\n\"\n\"varying vec3 eye;\\n\"\n\"#ifdef VERTEX_SHADER\\n\"\n\"void main (void)\\n\"\n\"{\\n\"\n\"tc = v_texcoord.st;\\n\"\n\"tf = ftetransform();\\n\"\n\"norm = v_normal;\\n\"\n\"eye = e_eyepos - v_position.xyz;\\n\"\n\"gl_Position = ftetransform();\\n\"\n\"}\\n\"\n\"#endif\\n\"\n\"#ifdef FRAGMENT_SHADER\\n\"\n\"#include \\\"sys/fog.h\\\"\\n\"\n\n\n\"void main (void)\\n\"\n\"{\\n\"\n\"vec2 stc; //screen tex coords\\n\"\n\"vec2 ntc; //normalmap/diffuse tex coords\\n\"\n\"vec3 n, refr, refl;\\n\"\n\"float fres;\\n\"\n\"float depth;\\n\"\n\"stc = (1.0 + (tf.xy / tf.w)) * 0.5;\\n\"\n//hack the texture coords slightly so that there are less obvious gaps\n\"stc.t -= 1.5*norm.z/1080.0;\\n\"\n\n\"#if 0//def USEMODS\\n\"\n\"ntc = tc;\\n\"\n\"n = texture2D(s_normalmap, ntc).xyz - 0.5;\\n\"\n\"#else\\n\"\n//apply q1-style warp, just for kicks\n\"ntc = tc + sin(tc.ts+e_time)*0.125;\\n\"\n\n//generate the two wave patterns from the normalmap\n\"n = (texture2D(s_normalmap, vec2(TXSCALE1)*tc + vec2(e_time*0.1, 0.0)).xyz);\\n\"\n\"n += (texture2D(s_normalmap, vec2(TXSCALE2)*tc - vec2(0, e_time*0.097)).xyz);\\n\"\n\"n -= 1.0 - 4.0/256.0;\\n\"\n\"#endif\\n\"\n\n\"#ifdef RIPPLEMAP\\n\"\n\"n += texture2D(s_ripplemap, stc).rgb*3.0;\\n\"\n\"#endif\\n\"\n\"n = normalize(n);\\n\"\n\n//the fresnel term decides how transparent the water should be\n\"fres = pow(1.0-abs(dot(n, normalize(eye))), float(FRESNEL_EXP)) * float(FRESNEL_RANGE) + float(FRESNEL_MIN);\\n\"\n\n\"#ifdef DEPTH\\n\"\n\"float far = #include \\\"cvar/gl_maxdist\\\";\\n\"\n\"float near = #include \\\"cvar/gl_mindist\\\";\\n\"\n//get depth value at the surface\n\"float sdepth = gl_FragCoord.z;\\n\"\n\"sdepth = (2.0*near) / (far + near - sdepth * (far - near));\\n\"\n\"sdepth = mix(near, far, sdepth);\\n\"\n\n//get depth value at the ground beyond the surface.\n\"float gdepth = texture2D(s_refractdepth, stc).x;\\n\"\n\"gdepth = (2.0*near) / (far + near - gdepth * (far - near));\\n\"\n\"if (gdepth >= 0.5)\\n\"\n\"{\\n\"\n\"gdepth = sdepth;\\n\"\n\"depth = 0.0;\\n\"\n\"}\\n\"\n\"else\\n\"\n\"{\\n\"\n\"gdepth = mix(near, far, gdepth);\\n\"\n\"depth = gdepth - sdepth;\\n\"\n\"}\\n\"\n\n//reduce the normals in shallow water (near walls, reduces the pain of linear sampling)\n\"if (depth < 100.0)\\n\"\n\"n *= depth/100.0;\\n\"\n\"#else\\n\"\n\"depth = 1.0;\\n\"\n\"#endif \\n\"\n\n\n//refraction image (and water fog, if possible)\n\"refr = texture2D(s_refract, stc + n.st*float(STRENGTH_REFR)*float(r_glsl_turbscale_refract)).rgb * vec3(TINT_REFR);\\n\"\n\"#ifdef DEPTH\\n\"\n\"refr = mix(refr, vec3(FOGTINT), min(depth/4096.0, 1.0));\\n\"\n\"#endif\\n\"\n\n//reflection/diffuse\n\"#ifdef REFLECT\\n\"\n\"refl = texture2D(s_reflect, stc - n.st*float(STRENGTH_REFL)*float(r_glsl_turbscale_reflect)).rgb * vec3(TINT_REFL);\\n\"\n\"#else\\n\"\n\"refl = texture2D(s_diffuse, ntc).xyz * vec3(TINT_REFL);\\n\"\n\"#endif\\n\"\n//interplate by fresnel\n\"refr = mix(refr, refl, fres);\\n\"\n\n\n\n\n\n\"#ifdef ALPHA\\n\"\n\"vec4 ts = texture2D(s_diffuse, ntc);\\n\"\n\"vec4 surf = fog4blend(vec4(ts.rgb, float(ALPHA)*ts.a));\\n\"\n\"refr = mix(refr, surf.rgb, surf.a);\\n\"\n\"#else\\n\"\n\"refr = fog3(refr); \\n\"\n\"#endif\\n\"\n\n//done\n\"gl_FragColor = vec4(refr, 1.0);\\n\"\n\"}\\n\"\n\"#endif\\n\"\n},\n#endif\n#ifdef VKQUAKE\n{QR_VULKAN, -1, \"altwater\",\n\"\\xFF\\x53\\x50\\x56\\x01\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x2C\\x00\\x00\\x00\\xA6\\x01\\x00\\x00\\xD4\\x01\\x00\\x00\"\n\"\\x40\\x0F\\x00\\x00\\x14\\x11\\x00\\x00\\xC8\\x26\\x00\\x00\\x01\\x00\\x62\\x31\\x72\\x5F\\x66\\x6F\\x67\\x5F\\x65\\x78\\x70\\x32\\x00\\x00\\x00\\x00\\x00\\x01\"\n\"\\x01\\x62\\x31\\x72\\x5F\\x66\\x6F\\x67\\x5F\\x6C\\x69\\x6E\\x65\\x61\\x72\\x00\\x00\\x00\\x00\\x00\\x01\\x02\\x66\\x31\\x72\\x5F\\x67\\x6C\\x73\\x6C\\x5F\\x74\"\n\"\\x75\\x72\\x62\\x73\\x63\\x61\\x6C\\x65\\x5F\\x72\\x65\\x66\\x6C\\x65\\x63\\x74\\x00\\x3F\\x80\\x00\\x00\\x01\\x03\\x66\\x31\\x72\\x5F\\x67\\x6C\\x73\\x6C\\x5F\"\n\"\\x74\\x75\\x72\\x62\\x73\\x63\\x61\\x6C\\x65\\x5F\\x72\\x65\\x66\\x72\\x61\\x63\\x74\\x00\\x3F\\x80\\x00\\x00\\x01\\x04\\x66\\x31\\x67\\x6C\\x5F\\x6D\\x61\\x78\"\n\"\\x64\\x69\\x73\\x74\\x00\\x46\\x00\\x00\\x00\\x01\\x05\\x66\\x31\\x67\\x6C\\x5F\\x6D\\x69\\x6E\\x64\\x69\\x73\\x74\\x00\\x40\\x80\\x00\\x00\\x01\\x06\\x42\\x31\"\n\"\\x72\\x65\\x66\\x6C\\x65\\x63\\x74\\x00\\x00\\x00\\x00\\x00\\x01\\x07\\x46\\x31\\x73\\x74\\x72\\x65\\x6E\\x67\\x74\\x68\\x5F\\x72\\x65\\x66\\x6C\\x00\\x3D\\xCC\"\n\"\\xCC\\xCD\\x01\\x08\\x46\\x31\\x73\\x74\\x72\\x65\\x6E\\x67\\x74\\x68\\x5F\\x72\\x65\\x66\\x72\\x00\\x3D\\xCC\\xCC\\xCD\\x01\\x09\\x46\\x31\\x66\\x72\\x65\\x73\"\n\"\\x6E\\x65\\x6C\\x5F\\x65\\x78\\x70\\x00\\x40\\xA0\\x00\\x00\\x01\\x0A\\x46\\x31\\x66\\x72\\x65\\x73\\x6E\\x65\\x6C\\x5F\\x72\\x61\\x6E\\x67\\x65\\x00\\x3F\\x80\"\n\"\\x00\\x00\\x01\\x0B\\x46\\x31\\x66\\x72\\x65\\x73\\x6E\\x65\\x6C\\x5F\\x6D\\x69\\x6E\\x00\\x3F\\x80\\x00\\x00\\x01\\x0C\\x46\\x31\\x74\\x78\\x73\\x63\\x61\\x6C\"\n\"\\x65\\x31\\x00\\x3E\\x4C\\xCC\\xCD\\x01\\x0D\\x46\\x31\\x74\\x78\\x73\\x63\\x61\\x6C\\x65\\x32\\x00\\x3E\\x4C\\xCC\\xCD\\x01\\x0E\\x42\\x31\\x72\\x69\\x70\\x70\"\n\"\\x6C\\x65\\x6D\\x61\\x70\\x00\\x00\\x00\\x00\\x00\\x01\\x0F\\x46\\x33\\x74\\x69\\x6E\\x74\\x5F\\x72\\x65\\x66\\x72\\x00\\x3F\\x33\\x33\\x33\\x3F\\x4C\\xCC\\xCD\"\n\"\\x3F\\x33\\x33\\x33\\x01\\x12\\x46\\x33\\x74\\x69\\x6E\\x74\\x5F\\x72\\x65\\x66\\x6C\\x00\\x3F\\x33\\x33\\x33\\x3F\\x4C\\xCC\\xCD\\x3F\\x33\\x33\\x33\\x01\\x15\"\n\"\\x42\\x31\\x64\\x65\\x70\\x74\\x68\\x00\\x00\\x00\\x00\\x00\\x01\\x16\\x46\\x31\\x61\\x6C\\x70\\x68\\x61\\x00\\x00\\x00\\x00\\x00\\x01\\x17\\x46\\x33\\x66\\x6F\"\n\"\\x67\\x74\\x69\\x6E\\x74\\x00\\x3E\\x4C\\xCC\\xCD\\x3E\\x99\\x99\\x9A\\x3E\\x4C\\xCC\\xCD\\x00\\x00\\x03\\x02\\x23\\x07\\x00\\x00\\x01\\x00\\x0A\\x00\\x08\\x00\"\n\"\\x7A\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x11\\x00\\x02\\x00\\x01\\x00\\x00\\x00\\x0B\\x00\\x06\\x00\\x01\\x00\\x00\\x00\\x47\\x4C\\x53\\x4C\\x2E\\x73\\x74\\x64\"\n\"\\x2E\\x34\\x35\\x30\\x00\\x00\\x00\\x00\\x0E\\x00\\x03\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x0F\\x00\\x11\\x00\\x00\\x00\\x00\\x00\\x04\\x00\\x00\\x00\"\n\"\\x6D\\x61\\x69\\x6E\\x00\\x00\\x00\\x00\\x2C\\x00\\x00\\x00\\x49\\x00\\x00\\x00\\x4B\\x00\\x00\\x00\\x4E\\x00\\x00\\x00\\x51\\x00\\x00\\x00\\x52\\x00\\x00\\x00\"\n\"\\x54\\x00\\x00\\x00\\x5D\\x00\\x00\\x00\\x76\\x00\\x00\\x00\\x77\\x00\\x00\\x00\\x78\\x00\\x00\\x00\\x79\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x0E\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x0F\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\x0F\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x10\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\x10\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x11\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\x13\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x12\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\x14\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x13\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\x15\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x14\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\x18\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x17\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\x19\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x18\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\x1A\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x19\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\x21\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x23\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x23\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x23\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x23\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x05\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x23\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x23\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x23\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x23\\x00\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x23\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x23\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xC0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x23\\x00\\x00\\x00\\x04\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\xCC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x23\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xD0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x23\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xDC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x23\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\xE0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x23\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xEC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x23\\x00\\x00\\x00\"\n\"\\x09\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xF0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x23\\x00\\x00\\x00\\x0A\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xFC\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x23\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x23\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x40\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x23\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x4C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x23\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x50\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x23\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x5C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x23\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x60\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x23\\x00\\x00\\x00\"\n\"\\x11\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x6C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x23\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x70\\x01\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x23\\x00\\x00\\x00\\x13\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x23\\x00\\x00\\x00\\x14\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x90\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x23\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x94\\x01\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x23\\x00\\x00\\x00\\x16\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x98\\x01\\x00\\x00\\x47\\x00\\x03\\x00\\x23\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\"\n\"\\x25\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x25\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\"\n\"\\x2C\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x49\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\"\n\"\\x4B\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x4E\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x47\\x00\\x04\\x00\"\n\"\\x51\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x52\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x47\\x00\\x04\\x00\"\n\"\\x54\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x5B\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x47\\x00\\x03\\x00\\x5B\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x60\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x47\\x00\\x04\\x00\"\n\"\\x61\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x01\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\x62\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x02\\x01\\x00\\x00\\x47\\x00\\x04\\x00\"\n\"\\x63\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x03\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\x64\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x04\\x01\\x00\\x00\\x47\\x00\\x04\\x00\"\n\"\\x65\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x05\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\x66\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x06\\x01\\x00\\x00\\x47\\x00\\x04\\x00\"\n\"\\x67\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x07\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\x68\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x08\\x01\\x00\\x00\\x47\\x00\\x04\\x00\"\n\"\\x69\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x09\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\x6A\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x0A\\x01\\x00\\x00\\x47\\x00\\x04\\x00\"\n\"\\x6B\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x0B\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\x6C\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x0C\\x01\\x00\\x00\\x47\\x00\\x04\\x00\"\n\"\\x6D\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x0D\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\x6E\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x0E\\x01\\x00\\x00\\x47\\x00\\x04\\x00\"\n\"\\x6F\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x15\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\x70\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x16\\x01\\x00\\x00\\x47\\x00\\x04\\x00\"\n\"\\x71\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x72\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x72\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x72\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x10\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x72\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x72\\x00\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x4C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x72\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x50\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x72\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x5C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x72\\x00\\x00\\x00\\x05\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x60\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x72\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x6C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x72\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x70\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x72\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x80\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x72\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x88\\x00\\x00\\x00\\x47\\x00\\x03\\x00\\x72\\x00\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x74\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x74\\x00\\x00\\x00\\x21\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x76\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x77\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\"\n\"\\x03\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x78\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x79\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x13\\x00\\x02\\x00\\x02\\x00\\x00\\x00\\x21\\x00\\x03\\x00\\x03\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x16\\x00\\x03\\x00\\x06\\x00\\x00\\x00\"\n\"\\x20\\x00\\x00\\x00\\x17\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x21\\x00\\x03\\x00\\x08\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x17\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x0C\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\"\n\"\\x3B\\x00\\x04\\x00\\x0C\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x32\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x00\\x80\\x87\\x43\"\n\"\\x32\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\\x00\\x00\\x88\\x43\\x32\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x00\\x80\\x88\\x43\"\n\"\\x3B\\x00\\x04\\x00\\x0C\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x32\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x13\\x00\\x00\\x00\\x00\\x00\\x89\\x43\"\n\"\\x32\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x14\\x00\\x00\\x00\\x00\\x80\\x89\\x43\\x32\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x00\\x00\\x8A\\x43\"\n\"\\x3B\\x00\\x04\\x00\\x0C\\x00\\x00\\x00\\x17\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x32\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x18\\x00\\x00\\x00\\x00\\x80\\x8B\\x43\"\n\"\\x32\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x19\\x00\\x00\\x00\\x00\\x00\\x8C\\x43\\x32\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x1A\\x00\\x00\\x00\\x00\\x80\\x8C\\x43\"\n\"\\x20\\x00\\x04\\x00\\x1C\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x18\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x04\\x00\\x00\\x00\"\n\"\\x15\\x00\\x04\\x00\\x1F\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x1F\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x04\\x00\\x00\\x00\"\n\"\\x1C\\x00\\x04\\x00\\x21\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x17\\x00\\x04\\x00\\x22\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x1E\\x00\\x19\\x00\\x23\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x0B\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x22\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x24\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x24\\x00\\x00\\x00\\x25\\x00\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\x15\\x00\\x04\\x00\\x26\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x26\\x00\\x00\\x00\\x27\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x28\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x2B\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x0B\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x2B\\x00\\x00\\x00\\x2C\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x2E\\x00\\x00\\x00\"\n\"\\x00\\x00\\x80\\x3F\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x34\\x00\\x00\\x00\\x00\\x00\\x80\\xBF\\x2B\\x00\\x04\\x00\\x1F\\x00\\x00\\x00\\x35\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x36\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x1F\\x00\\x00\\x00\\x3B\\x00\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x1F\\x00\\x00\\x00\\x3E\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x42\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x40\\x20\\x00\\x04\\x00\\x48\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x48\\x00\\x00\\x00\\x49\\x00\\x00\\x00\"\n\"\\x03\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x4A\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x4A\\x00\\x00\\x00\\x4B\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x4D\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x4D\\x00\\x00\\x00\\x4E\\x00\\x00\\x00\"\n\"\\x03\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x50\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x50\\x00\\x00\\x00\\x51\\x00\\x00\\x00\"\n\"\\x03\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x2B\\x00\\x00\\x00\\x52\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x50\\x00\\x00\\x00\\x54\\x00\\x00\\x00\"\n\"\\x03\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x26\\x00\\x00\\x00\\x55\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x56\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x0B\\x00\\x00\\x00\\x1E\\x00\\x03\\x00\\x5B\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x5C\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x5B\\x00\\x00\\x00\"\n\"\\x3B\\x00\\x04\\x00\\x5C\\x00\\x00\\x00\\x5D\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x32\\x00\\x04\\x00\\x26\\x00\\x00\\x00\\x60\\x00\\x00\\x00\\x00\\x01\\x00\\x00\"\n\"\\x32\\x00\\x04\\x00\\x26\\x00\\x00\\x00\\x61\\x00\\x00\\x00\\x01\\x01\\x00\\x00\\x32\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x62\\x00\\x00\\x00\\x00\\x00\\x81\\x43\"\n\"\\x32\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x63\\x00\\x00\\x00\\x00\\x80\\x81\\x43\\x32\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x64\\x00\\x00\\x00\\x00\\x00\\x82\\x43\"\n\"\\x32\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x65\\x00\\x00\\x00\\x00\\x80\\x82\\x43\\x32\\x00\\x04\\x00\\x26\\x00\\x00\\x00\\x66\\x00\\x00\\x00\\x06\\x01\\x00\\x00\"\n\"\\x32\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x67\\x00\\x00\\x00\\x00\\x80\\x83\\x43\\x32\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x68\\x00\\x00\\x00\\x00\\x00\\x84\\x43\"\n\"\\x32\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x69\\x00\\x00\\x00\\x00\\x80\\x84\\x43\\x32\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x6A\\x00\\x00\\x00\\x00\\x00\\x85\\x43\"\n\"\\x32\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x6B\\x00\\x00\\x00\\x00\\x80\\x85\\x43\\x32\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x6C\\x00\\x00\\x00\\x00\\x00\\x86\\x43\"\n\"\\x32\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x6D\\x00\\x00\\x00\\x00\\x80\\x86\\x43\\x32\\x00\\x04\\x00\\x26\\x00\\x00\\x00\\x6E\\x00\\x00\\x00\\x0E\\x01\\x00\\x00\"\n\"\\x32\\x00\\x04\\x00\\x26\\x00\\x00\\x00\\x6F\\x00\\x00\\x00\\x15\\x01\\x00\\x00\\x32\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x70\\x00\\x00\\x00\\x00\\x00\\x8B\\x43\"\n\"\\x32\\x00\\x04\\x00\\x26\\x00\\x00\\x00\\x71\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x1E\\x00\\x0C\\x00\\x72\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x22\\x00\\x00\\x00\"\n\"\\x20\\x00\\x04\\x00\\x73\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x72\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x73\\x00\\x00\\x00\\x74\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x20\\x00\\x04\\x00\\x75\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x75\\x00\\x00\\x00\\x76\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x3B\\x00\\x04\\x00\\x4A\\x00\\x00\\x00\\x77\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x2B\\x00\\x00\\x00\\x78\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x3B\\x00\\x04\\x00\\x2B\\x00\\x00\\x00\\x79\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x36\\x00\\x05\\x00\\x02\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x03\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x05\\x00\\x00\\x00\\x50\\x00\\x06\\x00\\x0B\\x00\\x00\\x00\\x11\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\"\n\"\\x10\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x0D\\x00\\x00\\x00\\x11\\x00\\x00\\x00\\x50\\x00\\x06\\x00\\x0B\\x00\\x00\\x00\\x16\\x00\\x00\\x00\\x13\\x00\\x00\\x00\"\n\"\\x14\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x12\\x00\\x00\\x00\\x16\\x00\\x00\\x00\\x50\\x00\\x06\\x00\\x0B\\x00\\x00\\x00\\x1B\\x00\\x00\\x00\"\n\"\\x18\\x00\\x00\\x00\\x19\\x00\\x00\\x00\\x1A\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x17\\x00\\x00\\x00\\x1B\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x22\\x00\\x00\\x00\"\n\"\\x4C\\x00\\x00\\x00\\x4B\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x49\\x00\\x00\\x00\\x4C\\x00\\x00\\x00\\x39\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x4F\\x00\\x00\\x00\"\n\"\\x09\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x4E\\x00\\x00\\x00\\x4F\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\x53\\x00\\x00\\x00\\x52\\x00\\x00\\x00\"\n\"\\x3E\\x00\\x03\\x00\\x51\\x00\\x00\\x00\\x53\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x56\\x00\\x00\\x00\\x57\\x00\\x00\\x00\\x25\\x00\\x00\\x00\\x55\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\x58\\x00\\x00\\x00\\x57\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\x59\\x00\\x00\\x00\\x2C\\x00\\x00\\x00\"\n\"\\x83\\x00\\x05\\x00\\x0B\\x00\\x00\\x00\\x5A\\x00\\x00\\x00\\x58\\x00\\x00\\x00\\x59\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x54\\x00\\x00\\x00\\x5A\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x5E\\x00\\x00\\x00\\x4E\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x4D\\x00\\x00\\x00\\x5F\\x00\\x00\\x00\\x5D\\x00\\x00\\x00\"\n\"\\x27\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x5F\\x00\\x00\\x00\\x5E\\x00\\x00\\x00\\xFD\\x00\\x01\\x00\\x38\\x00\\x01\\x00\\x36\\x00\\x05\\x00\\x07\\x00\\x00\\x00\"\n\"\\x09\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x0A\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x1C\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x28\\x00\\x00\\x00\\x29\\x00\\x00\\x00\\x25\\x00\\x00\\x00\\x27\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\"\n\"\\x2A\\x00\\x00\\x00\\x29\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\\x2C\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\"\n\"\\x2F\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x30\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x31\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x50\\x00\\x07\\x00\\x07\\x00\\x00\\x00\\x32\\x00\\x00\\x00\"\n\"\\x2F\\x00\\x00\\x00\\x30\\x00\\x00\\x00\\x31\\x00\\x00\\x00\\x2E\\x00\\x00\\x00\\x91\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x33\\x00\\x00\\x00\\x2A\\x00\\x00\\x00\"\n\"\\x32\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x1D\\x00\\x00\\x00\\x33\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x36\\x00\\x00\\x00\\x37\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\"\n\"\\x35\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x38\\x00\\x00\\x00\\x37\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x39\\x00\\x00\\x00\"\n\"\\x38\\x00\\x00\\x00\\x34\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x36\\x00\\x00\\x00\\x3A\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x35\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\"\n\"\\x3A\\x00\\x00\\x00\\x39\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x36\\x00\\x00\\x00\\x3C\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x3B\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x06\\x00\\x00\\x00\\x3D\\x00\\x00\\x00\\x3C\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x36\\x00\\x00\\x00\\x3F\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x3E\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x3F\\x00\\x00\\x00\\x81\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x41\\x00\\x00\\x00\\x3D\\x00\\x00\\x00\"\n\"\\x40\\x00\\x00\\x00\\x88\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x43\\x00\\x00\\x00\\x41\\x00\\x00\\x00\\x42\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x36\\x00\\x00\\x00\"\n\"\\x44\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x3B\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x44\\x00\\x00\\x00\\x43\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\"\n\"\\x45\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\xFE\\x00\\x02\\x00\\x45\\x00\\x00\\x00\\x38\\x00\\x01\\x00\\x03\\x02\\x23\\x07\\x00\\x00\\x01\\x00\\x0A\\x00\\x08\\x00\"\n\"\\x94\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x11\\x00\\x02\\x00\\x01\\x00\\x00\\x00\\x0B\\x00\\x06\\x00\\x01\\x00\\x00\\x00\\x47\\x4C\\x53\\x4C\\x2E\\x73\\x74\\x64\"\n\"\\x2E\\x34\\x35\\x30\\x00\\x00\\x00\\x00\\x0E\\x00\\x03\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x0F\\x00\\x0B\\x00\\x04\\x00\\x00\\x00\\x04\\x00\\x00\\x00\"\n\"\\x6D\\x61\\x69\\x6E\\x00\\x00\\x00\\x00\\x2F\\x00\\x00\\x00\\x7C\\x00\\x00\\x00\\x89\\x00\\x00\\x00\\x96\\x00\\x00\\x00\\xE5\\x00\\x00\\x00\\x8B\\x01\\x00\\x00\"\n\"\\x10\\x00\\x03\\x00\\x04\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x10\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x0F\\x01\\x00\\x00\\x47\\x00\\x04\\x00\"\n\"\\x11\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x10\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\x12\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x11\\x01\\x00\\x00\\x47\\x00\\x04\\x00\"\n\"\\x15\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x12\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x13\\x01\\x00\\x00\\x47\\x00\\x04\\x00\"\n\"\\x17\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x14\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\x1A\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x17\\x01\\x00\\x00\\x47\\x00\\x04\\x00\"\n\"\\x1B\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x18\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\x1C\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x19\\x01\\x00\\x00\\x47\\x00\\x04\\x00\"\n\"\\x1F\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x28\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x01\\x01\\x00\\x00\\x47\\x00\\x04\\x00\"\n\"\\x2F\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x3C\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\"\n\"\\x3E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x3E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x3E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x3E\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x3E\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x3E\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x3E\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x3E\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x3E\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x10\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x3E\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xC0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x3E\\x00\\x00\\x00\"\n\"\\x04\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xCC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x3E\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xD0\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x3E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xDC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x3E\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\xE0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x3E\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xEC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x3E\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xF0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x3E\\x00\\x00\\x00\\x0A\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\xFC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x3E\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x3E\\x00\\x00\\x00\"\n\"\\x0C\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x3E\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x4C\\x01\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x3E\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x50\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x3E\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x5C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x3E\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x60\\x01\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x3E\\x00\\x00\\x00\\x11\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x6C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x3E\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x70\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x3E\\x00\\x00\\x00\\x13\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x3E\\x00\\x00\\x00\"\n\"\\x14\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x90\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x3E\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x94\\x01\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x3E\\x00\\x00\\x00\\x16\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x98\\x01\\x00\\x00\\x47\\x00\\x03\\x00\\x3E\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x40\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x40\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x5D\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\x7C\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x89\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x96\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\xB5\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\xB5\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x03\\x00\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\xB7\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x0C\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\xC3\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x0D\\x01\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\xD4\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x0E\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\xD8\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\xD8\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\xE5\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x03\\x00\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\xEB\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x09\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\xED\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x0A\\x01\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\xEF\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x0B\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\xF1\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x15\\x01\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\xF6\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x04\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\xF8\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x05\\x01\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x0E\\x01\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x0E\\x01\\x00\\x00\\x21\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x38\\x01\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x38\\x01\\x00\\x00\\x21\\x00\\x00\\x00\\x04\\x00\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x3D\\x01\\x00\\x00\\x01\\x00\\x00\\x00\\x08\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\x3F\\x01\\x00\\x00\\x01\\x00\\x00\\x00\\x03\\x01\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x51\\x01\\x00\\x00\\x01\\x00\\x00\\x00\\x06\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\x56\\x01\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x56\\x01\\x00\\x00\\x21\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x5B\\x01\\x00\\x00\\x01\\x00\\x00\\x00\\x07\\x01\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x5D\\x01\\x00\\x00\\x01\\x00\\x00\\x00\\x02\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\x65\\x01\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x65\\x01\\x00\\x00\\x21\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x6F\\x01\\x00\\x00\\x01\\x00\\x00\\x00\\x16\\x01\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x8B\\x01\\x00\\x00\\x1E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x91\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x05\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x91\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x91\\x01\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x91\\x01\\x00\\x00\\x01\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x91\\x01\\x00\\x00\\x02\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x4C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x91\\x01\\x00\\x00\\x03\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x50\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x91\\x01\\x00\\x00\\x04\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x5C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x91\\x01\\x00\\x00\"\n\"\\x05\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x60\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x91\\x01\\x00\\x00\\x06\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x6C\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x91\\x01\\x00\\x00\\x07\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x70\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x91\\x01\\x00\\x00\\x08\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x80\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x91\\x01\\x00\\x00\\x09\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x88\\x00\\x00\\x00\\x47\\x00\\x03\\x00\"\n\"\\x91\\x01\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x93\\x01\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x93\\x01\\x00\\x00\"\n\"\\x21\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x13\\x00\\x02\\x00\\x02\\x00\\x00\\x00\\x21\\x00\\x03\\x00\\x03\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x16\\x00\\x03\\x00\"\n\"\\x06\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x17\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x08\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x21\\x00\\x04\\x00\\x09\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\x17\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x0E\\x00\\x00\\x00\"\n\"\\x0F\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x32\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x00\\x80\\x87\\x43\\x32\\x00\\x04\\x00\\x06\\x00\\x00\\x00\"\n\"\\x11\\x00\\x00\\x00\\x00\\x00\\x88\\x43\\x32\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x00\\x80\\x88\\x43\\x3B\\x00\\x04\\x00\\x0E\\x00\\x00\\x00\"\n\"\\x14\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x32\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x00\\x00\\x89\\x43\\x32\\x00\\x04\\x00\\x06\\x00\\x00\\x00\"\n\"\\x16\\x00\\x00\\x00\\x00\\x80\\x89\\x43\\x32\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x17\\x00\\x00\\x00\\x00\\x00\\x8A\\x43\\x3B\\x00\\x04\\x00\\x0E\\x00\\x00\\x00\"\n\"\\x19\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x32\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x1A\\x00\\x00\\x00\\x00\\x80\\x8B\\x43\\x32\\x00\\x04\\x00\\x06\\x00\\x00\\x00\"\n\"\\x1B\\x00\\x00\\x00\\x00\\x00\\x8C\\x43\\x32\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x1C\\x00\\x00\\x00\\x00\\x80\\x8C\\x43\\x15\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\"\n\"\\x20\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x32\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\\x1F\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\"\n\"\\x20\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x14\\x00\\x02\\x00\\x21\\x00\\x00\\x00\\x34\\x00\\x06\\x00\\x21\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\xAB\\x00\\x00\\x00\"\n\"\\x1F\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x34\\x00\\x05\\x00\\x21\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xA8\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x32\\x00\\x04\\x00\"\n\"\\x1E\\x00\\x00\\x00\\x28\\x00\\x00\\x00\\x01\\x01\\x00\\x00\\x34\\x00\\x06\\x00\\x21\\x00\\x00\\x00\\x29\\x00\\x00\\x00\\xAB\\x00\\x00\\x00\\x28\\x00\\x00\\x00\"\n\"\\x20\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x2C\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x2E\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x2E\\x00\\x00\\x00\\x2F\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x15\\x00\\x04\\x00\\x30\\x00\\x00\\x00\\x20\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x30\\x00\\x00\\x00\\x31\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x32\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x30\\x00\\x00\\x00\\x35\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x18\\x00\\x04\\x00\\x3A\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x04\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x30\\x00\\x00\\x00\\x3B\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x1C\\x00\\x04\\x00\\x3C\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x3B\\x00\\x00\\x00\\x17\\x00\\x04\\x00\\x3D\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x1E\\x00\\x19\\x00\\x3E\\x00\\x00\\x00\\x3A\\x00\\x00\\x00\"\n\"\\x3A\\x00\\x00\\x00\\x3A\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x0D\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x3C\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x3D\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x3F\\x00\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\x3E\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x3F\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\"\n\"\\x41\\x00\\x00\\x00\\x14\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x42\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\"\n\"\\x49\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x57\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x32\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\"\n\"\\x5D\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x34\\x00\\x06\\x00\\x21\\x00\\x00\\x00\\x5E\\x00\\x00\\x00\\xAB\\x00\\x00\\x00\\x5D\\x00\\x00\\x00\\x20\\x00\\x00\\x00\"\n\"\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x65\\x00\\x00\\x00\\x3B\\xAA\\xB8\\x3F\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x69\\x00\\x00\\x00\\x00\\x00\\x80\\x3F\"\n\"\\x2B\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\\x6A\\x00\\x00\\x00\\x13\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x7A\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3D\\x00\\x00\\x00\"\n\"\\x3B\\x00\\x04\\x00\\x2E\\x00\\x00\\x00\\x7C\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x85\\x00\\x00\\x00\\x00\\x00\\x00\\x3F\"\n\"\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x87\\x00\\x00\\x00\\x00\\x00\\xC0\\x3F\\x20\\x00\\x04\\x00\\x88\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\"\n\"\\x3B\\x00\\x04\\x00\\x88\\x00\\x00\\x00\\x89\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x8D\\x00\\x00\\x00\\x00\\x00\\x87\\x44\"\n\"\\x2B\\x00\\x04\\x00\\x30\\x00\\x00\\x00\\x8F\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x95\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x3D\\x00\\x00\\x00\"\n\"\\x3B\\x00\\x04\\x00\\x95\\x00\\x00\\x00\\x96\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x30\\x00\\x00\\x00\\x97\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x2B\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\\x9C\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\xA1\\x00\\x00\\x00\\x00\\x00\\x00\\x3E\"\n\"\\x20\\x00\\x04\\x00\\xB0\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x19\\x00\\x09\\x00\\xB2\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x1B\\x00\\x03\\x00\\xB3\\x00\\x00\\x00\\xB2\\x00\\x00\\x00\"\n\"\\x20\\x00\\x04\\x00\\xB4\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xB3\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\xB4\\x00\\x00\\x00\\xB5\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x32\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\xB7\\x00\\x00\\x00\\x00\\x00\\x86\\x43\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\xBC\\x00\\x00\\x00\\xCD\\xCC\\xCC\\x3D\"\n\"\\x32\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\xC3\\x00\\x00\\x00\\x00\\x80\\x86\\x43\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\xC8\\x00\\x00\\x00\\xF0\\xA7\\xC6\\x3D\"\n\"\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\xD0\\x00\\x00\\x00\\x00\\x00\\x7C\\x3F\\x32\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\\xD4\\x00\\x00\\x00\\x0E\\x01\\x00\\x00\"\n\"\\x34\\x00\\x06\\x00\\x21\\x00\\x00\\x00\\xD5\\x00\\x00\\x00\\xAB\\x00\\x00\\x00\\xD4\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\xB4\\x00\\x00\\x00\"\n\"\\xD8\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\xDD\\x00\\x00\\x00\\x00\\x00\\x40\\x40\\x3B\\x00\\x04\\x00\\x88\\x00\\x00\\x00\"\n\"\\xE5\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x32\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\xEB\\x00\\x00\\x00\\x00\\x80\\x84\\x43\\x32\\x00\\x04\\x00\\x06\\x00\\x00\\x00\"\n\"\\xED\\x00\\x00\\x00\\x00\\x00\\x85\\x43\\x32\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\xEF\\x00\\x00\\x00\\x00\\x80\\x85\\x43\\x32\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\"\n\"\\xF1\\x00\\x00\\x00\\x15\\x01\\x00\\x00\\x34\\x00\\x06\\x00\\x21\\x00\\x00\\x00\\xF2\\x00\\x00\\x00\\xAB\\x00\\x00\\x00\\xF1\\x00\\x00\\x00\\x20\\x00\\x00\\x00\"\n\"\\x32\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\xF6\\x00\\x00\\x00\\x00\\x00\\x82\\x43\\x32\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\xF8\\x00\\x00\\x00\\x00\\x80\\x82\\x43\"\n\"\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\xFC\\x00\\x00\\x00\\x00\\x00\\x00\\x40\\x3B\\x00\\x04\\x00\\xB4\\x00\\x00\\x00\\x0E\\x01\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x2E\\x01\\x00\\x00\\x00\\x00\\xC8\\x42\\x3B\\x00\\x04\\x00\\xB4\\x00\\x00\\x00\\x38\\x01\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x32\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x3D\\x01\\x00\\x00\\x00\\x00\\x84\\x43\\x32\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x3F\\x01\\x00\\x00\\x00\\x80\\x81\\x43\"\n\"\\x34\\x00\\x06\\x00\\x21\\x00\\x00\\x00\\x46\\x01\\x00\\x00\\xAB\\x00\\x00\\x00\\xF1\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\"\n\"\\x4C\\x01\\x00\\x00\\x00\\x00\\x80\\x45\\x32\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\\x51\\x01\\x00\\x00\\x06\\x01\\x00\\x00\\x34\\x00\\x06\\x00\\x21\\x00\\x00\\x00\"\n\"\\x52\\x01\\x00\\x00\\xAB\\x00\\x00\\x00\\x51\\x01\\x00\\x00\\x20\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\xB4\\x00\\x00\\x00\\x56\\x01\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x32\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x5B\\x01\\x00\\x00\\x00\\x80\\x83\\x43\\x32\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x5D\\x01\\x00\\x00\\x00\\x00\\x81\\x43\"\n\"\\x3B\\x00\\x04\\x00\\xB4\\x00\\x00\\x00\\x65\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x32\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x6F\\x01\\x00\\x00\\x00\\x00\\x8B\\x43\"\n\"\\x20\\x00\\x04\\x00\\x8A\\x01\\x00\\x00\\x03\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x8A\\x01\\x00\\x00\\x8B\\x01\\x00\\x00\\x03\\x00\\x00\\x00\"\n\"\\x1E\\x00\\x0C\\x00\\x91\\x01\\x00\\x00\\x3A\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3D\\x00\\x00\\x00\\x3D\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x92\\x01\\x00\\x00\\x02\\x00\\x00\\x00\\x91\\x01\\x00\\x00\"\n\"\\x3B\\x00\\x04\\x00\\x92\\x01\\x00\\x00\\x93\\x01\\x00\\x00\\x02\\x00\\x00\\x00\\x36\\x00\\x05\\x00\\x02\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x03\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x05\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x7A\\x00\\x00\\x00\\x7B\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\"\n\"\\x7A\\x00\\x00\\x00\\x94\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\xB0\\x00\\x00\\x00\\xB1\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\"\n\"\\x2C\\x00\\x00\\x00\\xE3\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x2C\\x00\\x00\\x00\\xF5\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\"\n\"\\x2C\\x00\\x00\\x00\\xF7\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x2C\\x00\\x00\\x00\\xF9\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\"\n\"\\x2C\\x00\\x00\\x00\\x0D\\x01\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x2C\\x00\\x00\\x00\\x24\\x01\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\"\n\"\\xB0\\x00\\x00\\x00\\x37\\x01\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\xB0\\x00\\x00\\x00\\x55\\x01\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\"\n\"\\x08\\x00\\x00\\x00\\x73\\x01\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x08\\x00\\x00\\x00\\x77\\x01\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\"\n\"\\x08\\x00\\x00\\x00\\x81\\x01\\x00\\x00\\x07\\x00\\x00\\x00\\x50\\x00\\x06\\x00\\x0D\\x00\\x00\\x00\\x13\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x11\\x00\\x00\\x00\"\n\"\\x12\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x0F\\x00\\x00\\x00\\x13\\x00\\x00\\x00\\x50\\x00\\x06\\x00\\x0D\\x00\\x00\\x00\\x18\\x00\\x00\\x00\\x15\\x00\\x00\\x00\"\n\"\\x16\\x00\\x00\\x00\\x17\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x14\\x00\\x00\\x00\\x18\\x00\\x00\\x00\\x50\\x00\\x06\\x00\\x0D\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\"\n\"\\x1A\\x00\\x00\\x00\\x1B\\x00\\x00\\x00\\x1C\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x19\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\"\n\"\\x7D\\x00\\x00\\x00\\x7C\\x00\\x00\\x00\\x4F\\x00\\x07\\x00\\x3D\\x00\\x00\\x00\\x7E\\x00\\x00\\x00\\x7D\\x00\\x00\\x00\\x7D\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x32\\x00\\x00\\x00\\x7F\\x00\\x00\\x00\\x7C\\x00\\x00\\x00\\x35\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\"\n\"\\x80\\x00\\x00\\x00\\x7F\\x00\\x00\\x00\\x50\\x00\\x05\\x00\\x3D\\x00\\x00\\x00\\x81\\x00\\x00\\x00\\x80\\x00\\x00\\x00\\x80\\x00\\x00\\x00\\x88\\x00\\x05\\x00\"\n\"\\x3D\\x00\\x00\\x00\\x82\\x00\\x00\\x00\\x7E\\x00\\x00\\x00\\x81\\x00\\x00\\x00\\x50\\x00\\x05\\x00\\x3D\\x00\\x00\\x00\\x83\\x00\\x00\\x00\\x69\\x00\\x00\\x00\"\n\"\\x69\\x00\\x00\\x00\\x81\\x00\\x05\\x00\\x3D\\x00\\x00\\x00\\x84\\x00\\x00\\x00\\x83\\x00\\x00\\x00\\x82\\x00\\x00\\x00\\x8E\\x00\\x05\\x00\\x3D\\x00\\x00\\x00\"\n\"\\x86\\x00\\x00\\x00\\x84\\x00\\x00\\x00\\x85\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x7B\\x00\\x00\\x00\\x86\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x32\\x00\\x00\\x00\"\n\"\\x8A\\x00\\x00\\x00\\x89\\x00\\x00\\x00\\x31\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x8B\\x00\\x00\\x00\\x8A\\x00\\x00\\x00\\x85\\x00\\x05\\x00\"\n\"\\x06\\x00\\x00\\x00\\x8C\\x00\\x00\\x00\\x87\\x00\\x00\\x00\\x8B\\x00\\x00\\x00\\x88\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x8E\\x00\\x00\\x00\\x8C\\x00\\x00\\x00\"\n\"\\x8D\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x2C\\x00\\x00\\x00\\x90\\x00\\x00\\x00\\x7B\\x00\\x00\\x00\\x8F\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\"\n\"\\x91\\x00\\x00\\x00\\x90\\x00\\x00\\x00\\x83\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x92\\x00\\x00\\x00\\x91\\x00\\x00\\x00\\x8E\\x00\\x00\\x00\\x41\\x00\\x05\\x00\"\n\"\\x2C\\x00\\x00\\x00\\x93\\x00\\x00\\x00\\x7B\\x00\\x00\\x00\\x8F\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x93\\x00\\x00\\x00\\x92\\x00\\x00\\x00\\x41\\x00\\x05\\x00\"\n\"\\x32\\x00\\x00\\x00\\x98\\x00\\x00\\x00\\x96\\x00\\x00\\x00\\x97\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x99\\x00\\x00\\x00\\x98\\x00\\x00\\x00\"\n\"\\x41\\x00\\x05\\x00\\x32\\x00\\x00\\x00\\x9A\\x00\\x00\\x00\\x96\\x00\\x00\\x00\\x8F\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x9B\\x00\\x00\\x00\"\n\"\\x9A\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x42\\x00\\x00\\x00\\x9D\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x9C\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\"\n\"\\x9E\\x00\\x00\\x00\\x9D\\x00\\x00\\x00\\x81\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x9F\\x00\\x00\\x00\\x9B\\x00\\x00\\x00\\x9E\\x00\\x00\\x00\\x0C\\x00\\x06\\x00\"\n\"\\x06\\x00\\x00\\x00\\xA0\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x9F\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\xA2\\x00\\x00\\x00\"\n\"\\xA0\\x00\\x00\\x00\\xA1\\x00\\x00\\x00\\x81\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\xA3\\x00\\x00\\x00\\x99\\x00\\x00\\x00\\xA2\\x00\\x00\\x00\\x41\\x00\\x05\\x00\"\n\"\\x2C\\x00\\x00\\x00\\xA4\\x00\\x00\\x00\\x94\\x00\\x00\\x00\\x97\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\xA4\\x00\\x00\\x00\\xA3\\x00\\x00\\x00\\x41\\x00\\x05\\x00\"\n\"\\x32\\x00\\x00\\x00\\xA5\\x00\\x00\\x00\\x96\\x00\\x00\\x00\\x8F\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\xA6\\x00\\x00\\x00\\xA5\\x00\\x00\\x00\"\n\"\\x41\\x00\\x05\\x00\\x32\\x00\\x00\\x00\\xA7\\x00\\x00\\x00\\x96\\x00\\x00\\x00\\x97\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\xA8\\x00\\x00\\x00\"\n\"\\xA7\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x42\\x00\\x00\\x00\\xA9\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x9C\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\"\n\"\\xAA\\x00\\x00\\x00\\xA9\\x00\\x00\\x00\\x81\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\xAB\\x00\\x00\\x00\\xA8\\x00\\x00\\x00\\xAA\\x00\\x00\\x00\\x0C\\x00\\x06\\x00\"\n\"\\x06\\x00\\x00\\x00\\xAC\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\xAB\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\xAD\\x00\\x00\\x00\"\n\"\\xAC\\x00\\x00\\x00\\xA1\\x00\\x00\\x00\\x81\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\xAE\\x00\\x00\\x00\\xA6\\x00\\x00\\x00\\xAD\\x00\\x00\\x00\\x41\\x00\\x05\\x00\"\n\"\\x2C\\x00\\x00\\x00\\xAF\\x00\\x00\\x00\\x94\\x00\\x00\\x00\\x8F\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\xAF\\x00\\x00\\x00\\xAE\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\xB3\\x00\\x00\\x00\\xB6\\x00\\x00\\x00\\xB5\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x3D\\x00\\x00\\x00\\xB8\\x00\\x00\\x00\\x96\\x00\\x00\\x00\\x8E\\x00\\x05\\x00\"\n\"\\x3D\\x00\\x00\\x00\\xB9\\x00\\x00\\x00\\xB8\\x00\\x00\\x00\\xB7\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x42\\x00\\x00\\x00\\xBA\\x00\\x00\\x00\\x40\\x00\\x00\\x00\"\n\"\\x9C\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\xBB\\x00\\x00\\x00\\xBA\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\xBD\\x00\\x00\\x00\"\n\"\\xBB\\x00\\x00\\x00\\xBC\\x00\\x00\\x00\\x50\\x00\\x05\\x00\\x3D\\x00\\x00\\x00\\xBE\\x00\\x00\\x00\\xBD\\x00\\x00\\x00\\x57\\x00\\x00\\x00\\x81\\x00\\x05\\x00\"\n\"\\x3D\\x00\\x00\\x00\\xBF\\x00\\x00\\x00\\xB9\\x00\\x00\\x00\\xBE\\x00\\x00\\x00\\x57\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\xC0\\x00\\x00\\x00\\xB6\\x00\\x00\\x00\"\n\"\\xBF\\x00\\x00\\x00\\x4F\\x00\\x08\\x00\\x0D\\x00\\x00\\x00\\xC1\\x00\\x00\\x00\\xC0\\x00\\x00\\x00\\xC0\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\xB1\\x00\\x00\\x00\\xC1\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\xB3\\x00\\x00\\x00\\xC2\\x00\\x00\\x00\\xB5\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x3D\\x00\\x00\\x00\\xC4\\x00\\x00\\x00\\x96\\x00\\x00\\x00\\x8E\\x00\\x05\\x00\\x3D\\x00\\x00\\x00\\xC5\\x00\\x00\\x00\\xC4\\x00\\x00\\x00\"\n\"\\xC3\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x42\\x00\\x00\\x00\\xC6\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x9C\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\"\n\"\\xC7\\x00\\x00\\x00\\xC6\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\xC9\\x00\\x00\\x00\\xC7\\x00\\x00\\x00\\xC8\\x00\\x00\\x00\\x50\\x00\\x05\\x00\"\n\"\\x3D\\x00\\x00\\x00\\xCA\\x00\\x00\\x00\\x57\\x00\\x00\\x00\\xC9\\x00\\x00\\x00\\x83\\x00\\x05\\x00\\x3D\\x00\\x00\\x00\\xCB\\x00\\x00\\x00\\xC5\\x00\\x00\\x00\"\n\"\\xCA\\x00\\x00\\x00\\x57\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\xCC\\x00\\x00\\x00\\xC2\\x00\\x00\\x00\\xCB\\x00\\x00\\x00\\x4F\\x00\\x08\\x00\\x0D\\x00\\x00\\x00\"\n\"\\xCD\\x00\\x00\\x00\\xCC\\x00\\x00\\x00\\xCC\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\"\n\"\\xCE\\x00\\x00\\x00\\xB1\\x00\\x00\\x00\\x81\\x00\\x05\\x00\\x0D\\x00\\x00\\x00\\xCF\\x00\\x00\\x00\\xCE\\x00\\x00\\x00\\xCD\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\"\n\"\\xB1\\x00\\x00\\x00\\xCF\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\xD1\\x00\\x00\\x00\\xB1\\x00\\x00\\x00\\x50\\x00\\x06\\x00\\x0D\\x00\\x00\\x00\"\n\"\\xD2\\x00\\x00\\x00\\xD0\\x00\\x00\\x00\\xD0\\x00\\x00\\x00\\xD0\\x00\\x00\\x00\\x83\\x00\\x05\\x00\\x0D\\x00\\x00\\x00\\xD3\\x00\\x00\\x00\\xD1\\x00\\x00\\x00\"\n\"\\xD2\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\xB1\\x00\\x00\\x00\\xD3\\x00\\x00\\x00\\xF7\\x00\\x03\\x00\\xD7\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\"\n\"\\xD5\\x00\\x00\\x00\\xD6\\x00\\x00\\x00\\xD7\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\xD6\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\xB3\\x00\\x00\\x00\\xD9\\x00\\x00\\x00\"\n\"\\xD8\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x3D\\x00\\x00\\x00\\xDA\\x00\\x00\\x00\\x7B\\x00\\x00\\x00\\x57\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\xDB\\x00\\x00\\x00\"\n\"\\xD9\\x00\\x00\\x00\\xDA\\x00\\x00\\x00\\x4F\\x00\\x08\\x00\\x0D\\x00\\x00\\x00\\xDC\\x00\\x00\\x00\\xDB\\x00\\x00\\x00\\xDB\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x8E\\x00\\x05\\x00\\x0D\\x00\\x00\\x00\\xDE\\x00\\x00\\x00\\xDC\\x00\\x00\\x00\\xDD\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x0D\\x00\\x00\\x00\\xDF\\x00\\x00\\x00\\xB1\\x00\\x00\\x00\\x81\\x00\\x05\\x00\\x0D\\x00\\x00\\x00\\xE0\\x00\\x00\\x00\\xDF\\x00\\x00\\x00\\xDE\\x00\\x00\\x00\"\n\"\\x3E\\x00\\x03\\x00\\xB1\\x00\\x00\\x00\\xE0\\x00\\x00\\x00\\xF9\\x00\\x02\\x00\\xD7\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\xD7\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x0D\\x00\\x00\\x00\\xE1\\x00\\x00\\x00\\xB1\\x00\\x00\\x00\\x0C\\x00\\x06\\x00\\x0D\\x00\\x00\\x00\\xE2\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x45\\x00\\x00\\x00\"\n\"\\xE1\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\xB1\\x00\\x00\\x00\\xE2\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\xE4\\x00\\x00\\x00\\xB1\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\xE6\\x00\\x00\\x00\\xE5\\x00\\x00\\x00\\x0C\\x00\\x06\\x00\\x0D\\x00\\x00\\x00\\xE7\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x45\\x00\\x00\\x00\\xE6\\x00\\x00\\x00\\x94\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\xE8\\x00\\x00\\x00\\xE4\\x00\\x00\\x00\\xE7\\x00\\x00\\x00\\x0C\\x00\\x06\\x00\"\n\"\\x06\\x00\\x00\\x00\\xE9\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\xE8\\x00\\x00\\x00\\x83\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\xEA\\x00\\x00\\x00\"\n\"\\x69\\x00\\x00\\x00\\xE9\\x00\\x00\\x00\\x0C\\x00\\x07\\x00\\x06\\x00\\x00\\x00\\xEC\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x1A\\x00\\x00\\x00\\xEA\\x00\\x00\\x00\"\n\"\\xEB\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\xEE\\x00\\x00\\x00\\xEC\\x00\\x00\\x00\\xED\\x00\\x00\\x00\\x81\\x00\\x05\\x00\\x06\\x00\\x00\\x00\"\n\"\\xF0\\x00\\x00\\x00\\xEE\\x00\\x00\\x00\\xEF\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\xE3\\x00\\x00\\x00\\xF0\\x00\\x00\\x00\\xF7\\x00\\x03\\x00\\xF4\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\\xF2\\x00\\x00\\x00\\xF3\\x00\\x00\\x00\\x36\\x01\\x00\\x00\\xF8\\x00\\x02\\x00\\xF3\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\"\n\"\\xF5\\x00\\x00\\x00\\xF6\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\xF7\\x00\\x00\\x00\\xF8\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x32\\x00\\x00\\x00\\xFA\\x00\\x00\\x00\"\n\"\\x2F\\x00\\x00\\x00\\x31\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\xFB\\x00\\x00\\x00\\xFA\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\xF9\\x00\\x00\\x00\"\n\"\\xFB\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\xFD\\x00\\x00\\x00\\xF7\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\xFE\\x00\\x00\\x00\"\n\"\\xFC\\x00\\x00\\x00\\xFD\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\xFF\\x00\\x00\\x00\\xF5\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\"\n\"\\x00\\x01\\x00\\x00\\xF7\\x00\\x00\\x00\\x81\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x01\\x01\\x00\\x00\\xFF\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x06\\x00\\x00\\x00\\x02\\x01\\x00\\x00\\xF9\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x03\\x01\\x00\\x00\\xF5\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x06\\x00\\x00\\x00\\x04\\x01\\x00\\x00\\xF7\\x00\\x00\\x00\\x83\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x05\\x01\\x00\\x00\\x03\\x01\\x00\\x00\\x04\\x01\\x00\\x00\"\n\"\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x06\\x01\\x00\\x00\\x02\\x01\\x00\\x00\\x05\\x01\\x00\\x00\\x83\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x07\\x01\\x00\\x00\"\n\"\\x01\\x01\\x00\\x00\\x06\\x01\\x00\\x00\\x88\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x08\\x01\\x00\\x00\\xFE\\x00\\x00\\x00\\x07\\x01\\x00\\x00\\x3E\\x00\\x03\\x00\"\n\"\\xF9\\x00\\x00\\x00\\x08\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x09\\x01\\x00\\x00\\xF7\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\"\n\"\\x0A\\x01\\x00\\x00\\xF5\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x0B\\x01\\x00\\x00\\xF9\\x00\\x00\\x00\\x0C\\x00\\x08\\x00\\x06\\x00\\x00\\x00\"\n\"\\x0C\\x01\\x00\\x00\\x01\\x00\\x00\\x00\\x2E\\x00\\x00\\x00\\x09\\x01\\x00\\x00\\x0A\\x01\\x00\\x00\\x0B\\x01\\x00\\x00\\x3E\\x00\\x03\\x00\\xF9\\x00\\x00\\x00\"\n\"\\x0C\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\xB3\\x00\\x00\\x00\\x0F\\x01\\x00\\x00\\x0E\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x3D\\x00\\x00\\x00\\x10\\x01\\x00\\x00\"\n\"\\x7B\\x00\\x00\\x00\\x57\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x11\\x01\\x00\\x00\\x0F\\x01\\x00\\x00\\x10\\x01\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\"\n\"\\x12\\x01\\x00\\x00\\x11\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x0D\\x01\\x00\\x00\\x12\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\"\n\"\\x13\\x01\\x00\\x00\\xF7\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x14\\x01\\x00\\x00\\xFC\\x00\\x00\\x00\\x13\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x06\\x00\\x00\\x00\\x15\\x01\\x00\\x00\\xF5\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x16\\x01\\x00\\x00\\xF7\\x00\\x00\\x00\\x81\\x00\\x05\\x00\"\n\"\\x06\\x00\\x00\\x00\\x17\\x01\\x00\\x00\\x15\\x01\\x00\\x00\\x16\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x18\\x01\\x00\\x00\\x0D\\x01\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x19\\x01\\x00\\x00\\xF5\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x1A\\x01\\x00\\x00\\xF7\\x00\\x00\\x00\"\n\"\\x83\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x1B\\x01\\x00\\x00\\x19\\x01\\x00\\x00\\x1A\\x01\\x00\\x00\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x1C\\x01\\x00\\x00\"\n\"\\x18\\x01\\x00\\x00\\x1B\\x01\\x00\\x00\\x83\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x1D\\x01\\x00\\x00\\x17\\x01\\x00\\x00\\x1C\\x01\\x00\\x00\\x88\\x00\\x05\\x00\"\n\"\\x06\\x00\\x00\\x00\\x1E\\x01\\x00\\x00\\x14\\x01\\x00\\x00\\x1D\\x01\\x00\\x00\\x3E\\x00\\x03\\x00\\x0D\\x01\\x00\\x00\\x1E\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x06\\x00\\x00\\x00\\x1F\\x01\\x00\\x00\\x0D\\x01\\x00\\x00\\xBE\\x00\\x05\\x00\\x21\\x00\\x00\\x00\\x20\\x01\\x00\\x00\\x1F\\x01\\x00\\x00\\x85\\x00\\x00\\x00\"\n\"\\xF7\\x00\\x03\\x00\\x22\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\\x20\\x01\\x00\\x00\\x21\\x01\\x00\\x00\\x25\\x01\\x00\\x00\\xF8\\x00\\x02\\x00\"\n\"\\x21\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x23\\x01\\x00\\x00\\xF9\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x0D\\x01\\x00\\x00\\x23\\x01\\x00\\x00\"\n\"\\x3E\\x00\\x03\\x00\\x24\\x01\\x00\\x00\\x57\\x00\\x00\\x00\\xF9\\x00\\x02\\x00\\x22\\x01\\x00\\x00\\xF8\\x00\\x02\\x00\\x25\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x06\\x00\\x00\\x00\\x26\\x01\\x00\\x00\\xF7\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x27\\x01\\x00\\x00\\xF5\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x06\\x00\\x00\\x00\\x28\\x01\\x00\\x00\\x0D\\x01\\x00\\x00\\x0C\\x00\\x08\\x00\\x06\\x00\\x00\\x00\\x29\\x01\\x00\\x00\\x01\\x00\\x00\\x00\\x2E\\x00\\x00\\x00\"\n\"\\x26\\x01\\x00\\x00\\x27\\x01\\x00\\x00\\x28\\x01\\x00\\x00\\x3E\\x00\\x03\\x00\\x0D\\x01\\x00\\x00\\x29\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\"\n\"\\x2A\\x01\\x00\\x00\\x0D\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x2B\\x01\\x00\\x00\\xF9\\x00\\x00\\x00\\x83\\x00\\x05\\x00\\x06\\x00\\x00\\x00\"\n\"\\x2C\\x01\\x00\\x00\\x2A\\x01\\x00\\x00\\x2B\\x01\\x00\\x00\\x3E\\x00\\x03\\x00\\x24\\x01\\x00\\x00\\x2C\\x01\\x00\\x00\\xF9\\x00\\x02\\x00\\x22\\x01\\x00\\x00\"\n\"\\xF8\\x00\\x02\\x00\\x22\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x2D\\x01\\x00\\x00\\x24\\x01\\x00\\x00\\xB8\\x00\\x05\\x00\\x21\\x00\\x00\\x00\"\n\"\\x2F\\x01\\x00\\x00\\x2D\\x01\\x00\\x00\\x2E\\x01\\x00\\x00\\xF7\\x00\\x03\\x00\\x31\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\\x2F\\x01\\x00\\x00\"\n\"\\x30\\x01\\x00\\x00\\x31\\x01\\x00\\x00\\xF8\\x00\\x02\\x00\\x30\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x32\\x01\\x00\\x00\\x24\\x01\\x00\\x00\"\n\"\\x88\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x33\\x01\\x00\\x00\\x32\\x01\\x00\\x00\\x2E\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\x34\\x01\\x00\\x00\"\n\"\\xB1\\x00\\x00\\x00\\x8E\\x00\\x05\\x00\\x0D\\x00\\x00\\x00\\x35\\x01\\x00\\x00\\x34\\x01\\x00\\x00\\x33\\x01\\x00\\x00\\x3E\\x00\\x03\\x00\\xB1\\x00\\x00\\x00\"\n\"\\x35\\x01\\x00\\x00\\xF9\\x00\\x02\\x00\\x31\\x01\\x00\\x00\\xF8\\x00\\x02\\x00\\x31\\x01\\x00\\x00\\xF9\\x00\\x02\\x00\\xF4\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\"\n\"\\x36\\x01\\x00\\x00\\x3E\\x00\\x03\\x00\\x24\\x01\\x00\\x00\\x69\\x00\\x00\\x00\\xF9\\x00\\x02\\x00\\xF4\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\xF4\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\xB3\\x00\\x00\\x00\\x39\\x01\\x00\\x00\\x38\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x3D\\x00\\x00\\x00\\x3A\\x01\\x00\\x00\\x7B\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\x3B\\x01\\x00\\x00\\xB1\\x00\\x00\\x00\\x4F\\x00\\x07\\x00\\x3D\\x00\\x00\\x00\\x3C\\x01\\x00\\x00\\x3B\\x01\\x00\\x00\"\n\"\\x3B\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x8E\\x00\\x05\\x00\\x3D\\x00\\x00\\x00\\x3E\\x01\\x00\\x00\\x3C\\x01\\x00\\x00\\x3D\\x01\\x00\\x00\"\n\"\\x8E\\x00\\x05\\x00\\x3D\\x00\\x00\\x00\\x40\\x01\\x00\\x00\\x3E\\x01\\x00\\x00\\x3F\\x01\\x00\\x00\\x81\\x00\\x05\\x00\\x3D\\x00\\x00\\x00\\x41\\x01\\x00\\x00\"\n\"\\x3A\\x01\\x00\\x00\\x40\\x01\\x00\\x00\\x57\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x42\\x01\\x00\\x00\\x39\\x01\\x00\\x00\\x41\\x01\\x00\\x00\\x4F\\x00\\x08\\x00\"\n\"\\x0D\\x00\\x00\\x00\\x43\\x01\\x00\\x00\\x42\\x01\\x00\\x00\\x42\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x0D\\x00\\x00\\x00\\x44\\x01\\x00\\x00\\x0F\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x0D\\x00\\x00\\x00\\x45\\x01\\x00\\x00\\x43\\x01\\x00\\x00\\x44\\x01\\x00\\x00\"\n\"\\x3E\\x00\\x03\\x00\\x37\\x01\\x00\\x00\\x45\\x01\\x00\\x00\\xF7\\x00\\x03\\x00\\x48\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\\x46\\x01\\x00\\x00\"\n\"\\x47\\x01\\x00\\x00\\x48\\x01\\x00\\x00\\xF8\\x00\\x02\\x00\\x47\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\x49\\x01\\x00\\x00\\x37\\x01\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\x4A\\x01\\x00\\x00\\x19\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x4B\\x01\\x00\\x00\\x24\\x01\\x00\\x00\"\n\"\\x88\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x4D\\x01\\x00\\x00\\x4B\\x01\\x00\\x00\\x4C\\x01\\x00\\x00\\x0C\\x00\\x07\\x00\\x06\\x00\\x00\\x00\\x4E\\x01\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x25\\x00\\x00\\x00\\x4D\\x01\\x00\\x00\\x69\\x00\\x00\\x00\\x50\\x00\\x06\\x00\\x0D\\x00\\x00\\x00\\x4F\\x01\\x00\\x00\\x4E\\x01\\x00\\x00\"\n\"\\x4E\\x01\\x00\\x00\\x4E\\x01\\x00\\x00\\x0C\\x00\\x08\\x00\\x0D\\x00\\x00\\x00\\x50\\x01\\x00\\x00\\x01\\x00\\x00\\x00\\x2E\\x00\\x00\\x00\\x49\\x01\\x00\\x00\"\n\"\\x4A\\x01\\x00\\x00\\x4F\\x01\\x00\\x00\\x3E\\x00\\x03\\x00\\x37\\x01\\x00\\x00\\x50\\x01\\x00\\x00\\xF9\\x00\\x02\\x00\\x48\\x01\\x00\\x00\\xF8\\x00\\x02\\x00\"\n\"\\x48\\x01\\x00\\x00\\xF7\\x00\\x03\\x00\\x54\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\\x52\\x01\\x00\\x00\\x53\\x01\\x00\\x00\\x64\\x01\\x00\\x00\"\n\"\\xF8\\x00\\x02\\x00\\x53\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\xB3\\x00\\x00\\x00\\x57\\x01\\x00\\x00\\x56\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x3D\\x00\\x00\\x00\"\n\"\\x58\\x01\\x00\\x00\\x7B\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\x59\\x01\\x00\\x00\\xB1\\x00\\x00\\x00\\x4F\\x00\\x07\\x00\\x3D\\x00\\x00\\x00\"\n\"\\x5A\\x01\\x00\\x00\\x59\\x01\\x00\\x00\\x59\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x8E\\x00\\x05\\x00\\x3D\\x00\\x00\\x00\\x5C\\x01\\x00\\x00\"\n\"\\x5A\\x01\\x00\\x00\\x5B\\x01\\x00\\x00\\x8E\\x00\\x05\\x00\\x3D\\x00\\x00\\x00\\x5E\\x01\\x00\\x00\\x5C\\x01\\x00\\x00\\x5D\\x01\\x00\\x00\\x83\\x00\\x05\\x00\"\n\"\\x3D\\x00\\x00\\x00\\x5F\\x01\\x00\\x00\\x58\\x01\\x00\\x00\\x5E\\x01\\x00\\x00\\x57\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x60\\x01\\x00\\x00\\x57\\x01\\x00\\x00\"\n\"\\x5F\\x01\\x00\\x00\\x4F\\x00\\x08\\x00\\x0D\\x00\\x00\\x00\\x61\\x01\\x00\\x00\\x60\\x01\\x00\\x00\\x60\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\x62\\x01\\x00\\x00\\x14\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x0D\\x00\\x00\\x00\\x63\\x01\\x00\\x00\"\n\"\\x61\\x01\\x00\\x00\\x62\\x01\\x00\\x00\\x3E\\x00\\x03\\x00\\x55\\x01\\x00\\x00\\x63\\x01\\x00\\x00\\xF9\\x00\\x02\\x00\\x54\\x01\\x00\\x00\\xF8\\x00\\x02\\x00\"\n\"\\x64\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\xB3\\x00\\x00\\x00\\x66\\x01\\x00\\x00\\x65\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x3D\\x00\\x00\\x00\\x67\\x01\\x00\\x00\"\n\"\\x94\\x00\\x00\\x00\\x57\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x68\\x01\\x00\\x00\\x66\\x01\\x00\\x00\\x67\\x01\\x00\\x00\\x4F\\x00\\x08\\x00\\x0D\\x00\\x00\\x00\"\n\"\\x69\\x01\\x00\\x00\\x68\\x01\\x00\\x00\\x68\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x55\\x01\\x00\\x00\"\n\"\\x69\\x01\\x00\\x00\\xF9\\x00\\x02\\x00\\x54\\x01\\x00\\x00\\xF8\\x00\\x02\\x00\\x54\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\x6A\\x01\\x00\\x00\"\n\"\\x37\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\x6B\\x01\\x00\\x00\\x55\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x6C\\x01\\x00\\x00\"\n\"\\xE3\\x00\\x00\\x00\\x50\\x00\\x06\\x00\\x0D\\x00\\x00\\x00\\x6D\\x01\\x00\\x00\\x6C\\x01\\x00\\x00\\x6C\\x01\\x00\\x00\\x6C\\x01\\x00\\x00\\x0C\\x00\\x08\\x00\"\n\"\\x0D\\x00\\x00\\x00\\x6E\\x01\\x00\\x00\\x01\\x00\\x00\\x00\\x2E\\x00\\x00\\x00\\x6A\\x01\\x00\\x00\\x6B\\x01\\x00\\x00\\x6D\\x01\\x00\\x00\\x3E\\x00\\x03\\x00\"\n\"\\x37\\x01\\x00\\x00\\x6E\\x01\\x00\\x00\\xB7\\x00\\x05\\x00\\x21\\x00\\x00\\x00\\x70\\x01\\x00\\x00\\x6F\\x01\\x00\\x00\\x57\\x00\\x00\\x00\\xF7\\x00\\x03\\x00\"\n\"\\x72\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\\x70\\x01\\x00\\x00\\x71\\x01\\x00\\x00\\x72\\x01\\x00\\x00\\xF8\\x00\\x02\\x00\\x71\\x01\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\xB3\\x00\\x00\\x00\\x74\\x01\\x00\\x00\\x65\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x3D\\x00\\x00\\x00\\x75\\x01\\x00\\x00\\x94\\x00\\x00\\x00\"\n\"\\x57\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x76\\x01\\x00\\x00\\x74\\x01\\x00\\x00\\x75\\x01\\x00\\x00\\x3E\\x00\\x03\\x00\\x73\\x01\\x00\\x00\\x76\\x01\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x78\\x01\\x00\\x00\\x73\\x01\\x00\\x00\\x4F\\x00\\x08\\x00\\x0D\\x00\\x00\\x00\\x79\\x01\\x00\\x00\\x78\\x01\\x00\\x00\"\n\"\\x78\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x2C\\x00\\x00\\x00\\x7A\\x01\\x00\\x00\\x73\\x01\\x00\\x00\"\n\"\\x35\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x7B\\x01\\x00\\x00\\x7A\\x01\\x00\\x00\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x7C\\x01\\x00\\x00\"\n\"\\x6F\\x01\\x00\\x00\\x7B\\x01\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x7D\\x01\\x00\\x00\\x79\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x51\\x00\\x05\\x00\"\n\"\\x06\\x00\\x00\\x00\\x7E\\x01\\x00\\x00\\x79\\x01\\x00\\x00\\x01\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x7F\\x01\\x00\\x00\\x79\\x01\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\x50\\x00\\x07\\x00\\x07\\x00\\x00\\x00\\x80\\x01\\x00\\x00\\x7D\\x01\\x00\\x00\\x7E\\x01\\x00\\x00\\x7F\\x01\\x00\\x00\\x7C\\x01\\x00\\x00\"\n\"\\x3E\\x00\\x03\\x00\\x81\\x01\\x00\\x00\\x80\\x01\\x00\\x00\\x39\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x82\\x01\\x00\\x00\\x0B\\x00\\x00\\x00\\x81\\x01\\x00\\x00\"\n\"\\x3E\\x00\\x03\\x00\\x77\\x01\\x00\\x00\\x82\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\x83\\x01\\x00\\x00\\x37\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x07\\x00\\x00\\x00\\x84\\x01\\x00\\x00\\x77\\x01\\x00\\x00\\x4F\\x00\\x08\\x00\\x0D\\x00\\x00\\x00\\x85\\x01\\x00\\x00\\x84\\x01\\x00\\x00\\x84\\x01\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x2C\\x00\\x00\\x00\\x86\\x01\\x00\\x00\\x77\\x01\\x00\\x00\\x35\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x87\\x01\\x00\\x00\\x86\\x01\\x00\\x00\\x50\\x00\\x06\\x00\\x0D\\x00\\x00\\x00\\x88\\x01\\x00\\x00\\x87\\x01\\x00\\x00\"\n\"\\x87\\x01\\x00\\x00\\x87\\x01\\x00\\x00\\x0C\\x00\\x08\\x00\\x0D\\x00\\x00\\x00\\x89\\x01\\x00\\x00\\x01\\x00\\x00\\x00\\x2E\\x00\\x00\\x00\\x83\\x01\\x00\\x00\"\n\"\\x85\\x01\\x00\\x00\\x88\\x01\\x00\\x00\\x3E\\x00\\x03\\x00\\x37\\x01\\x00\\x00\\x89\\x01\\x00\\x00\\xF9\\x00\\x02\\x00\\x72\\x01\\x00\\x00\\xF8\\x00\\x02\\x00\"\n\"\\x72\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\x8C\\x01\\x00\\x00\\x37\\x01\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x8D\\x01\\x00\\x00\"\n\"\\x8C\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x8E\\x01\\x00\\x00\\x8C\\x01\\x00\\x00\\x01\\x00\\x00\\x00\\x51\\x00\\x05\\x00\"\n\"\\x06\\x00\\x00\\x00\\x8F\\x01\\x00\\x00\\x8C\\x01\\x00\\x00\\x02\\x00\\x00\\x00\\x50\\x00\\x07\\x00\\x07\\x00\\x00\\x00\\x90\\x01\\x00\\x00\\x8D\\x01\\x00\\x00\"\n\"\\x8E\\x01\\x00\\x00\\x8F\\x01\\x00\\x00\\x69\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x8B\\x01\\x00\\x00\\x90\\x01\\x00\\x00\\xFD\\x00\\x01\\x00\\x38\\x00\\x01\\x00\"\n\"\\x36\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x37\\x00\\x03\\x00\\x08\\x00\\x00\\x00\\x0A\\x00\\x00\\x00\"\n\"\\xF8\\x00\\x02\\x00\\x0C\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x2C\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x2C\\x00\\x00\\x00\"\n\"\\x39\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\xF7\\x00\\x03\\x00\\x25\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\\x23\\x00\\x00\\x00\\x24\\x00\\x00\\x00\"\n\"\\x25\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x24\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x26\\x00\\x00\\x00\\x0A\\x00\\x00\\x00\\xFE\\x00\\x02\\x00\"\n\"\\x26\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x25\\x00\\x00\\x00\\xF7\\x00\\x03\\x00\\x2B\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\\x29\\x00\\x00\\x00\"\n\"\\x2A\\x00\\x00\\x00\\x4E\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x2A\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x32\\x00\\x00\\x00\\x33\\x00\\x00\\x00\\x2F\\x00\\x00\\x00\"\n\"\\x31\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x34\\x00\\x00\\x00\\x33\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x32\\x00\\x00\\x00\\x36\\x00\\x00\\x00\"\n\"\\x2F\\x00\\x00\\x00\\x35\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x37\\x00\\x00\\x00\\x36\\x00\\x00\\x00\\x88\\x00\\x05\\x00\\x06\\x00\\x00\\x00\"\n\"\\x38\\x00\\x00\\x00\\x34\\x00\\x00\\x00\\x37\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x2D\\x00\\x00\\x00\\x38\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x42\\x00\\x00\\x00\"\n\"\\x43\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x41\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x44\\x00\\x00\\x00\\x43\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x06\\x00\\x00\\x00\\x45\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\\x83\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x46\\x00\\x00\\x00\\x44\\x00\\x00\\x00\\x45\\x00\\x00\\x00\"\n\"\\x41\\x00\\x05\\x00\\x42\\x00\\x00\\x00\\x47\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x41\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x48\\x00\\x00\\x00\"\n\"\\x47\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x42\\x00\\x00\\x00\\x4A\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x49\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\"\n\"\\x4B\\x00\\x00\\x00\\x4A\\x00\\x00\\x00\\x83\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x4C\\x00\\x00\\x00\\x48\\x00\\x00\\x00\\x4B\\x00\\x00\\x00\\x88\\x00\\x05\\x00\"\n\"\\x06\\x00\\x00\\x00\\x4D\\x00\\x00\\x00\\x46\\x00\\x00\\x00\\x4C\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x39\\x00\\x00\\x00\\x4D\\x00\\x00\\x00\\xF9\\x00\\x02\\x00\"\n\"\\x2B\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x4E\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x42\\x00\\x00\\x00\\x4F\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x41\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x50\\x00\\x00\\x00\\x4F\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x32\\x00\\x00\\x00\\x51\\x00\\x00\\x00\\x2F\\x00\\x00\\x00\"\n\"\\x31\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x52\\x00\\x00\\x00\\x51\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x53\\x00\\x00\\x00\"\n\"\\x50\\x00\\x00\\x00\\x52\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x32\\x00\\x00\\x00\\x54\\x00\\x00\\x00\\x2F\\x00\\x00\\x00\\x35\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x06\\x00\\x00\\x00\\x55\\x00\\x00\\x00\\x54\\x00\\x00\\x00\\x88\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x56\\x00\\x00\\x00\\x53\\x00\\x00\\x00\\x55\\x00\\x00\\x00\"\n\"\\x3E\\x00\\x03\\x00\\x2D\\x00\\x00\\x00\\x56\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x58\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\\x41\\x00\\x05\\x00\"\n\"\\x42\\x00\\x00\\x00\\x59\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x49\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x5A\\x00\\x00\\x00\\x59\\x00\\x00\\x00\"\n\"\\x83\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x5B\\x00\\x00\\x00\\x58\\x00\\x00\\x00\\x5A\\x00\\x00\\x00\\x0C\\x00\\x07\\x00\\x06\\x00\\x00\\x00\\x5C\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x28\\x00\\x00\\x00\\x57\\x00\\x00\\x00\\x5B\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x2D\\x00\\x00\\x00\\x5C\\x00\\x00\\x00\\xF7\\x00\\x03\\x00\"\n\"\\x60\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\\x5E\\x00\\x00\\x00\\x5F\\x00\\x00\\x00\\x60\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x5F\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x61\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x62\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\"\n\"\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x63\\x00\\x00\\x00\\x62\\x00\\x00\\x00\\x61\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x2D\\x00\\x00\\x00\\x63\\x00\\x00\\x00\"\n\"\\xF9\\x00\\x02\\x00\\x60\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x60\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x64\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\"\n\"\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x66\\x00\\x00\\x00\\x64\\x00\\x00\\x00\\x65\\x00\\x00\\x00\\x7F\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x67\\x00\\x00\\x00\"\n\"\\x66\\x00\\x00\\x00\\x0C\\x00\\x06\\x00\\x06\\x00\\x00\\x00\\x68\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x67\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\"\n\"\\x39\\x00\\x00\\x00\\x68\\x00\\x00\\x00\\xF9\\x00\\x02\\x00\\x2B\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x2B\\x00\\x00\\x00\\x41\\x00\\x06\\x00\\x42\\x00\\x00\\x00\"\n\"\\x6B\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x6A\\x00\\x00\\x00\\x35\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x6C\\x00\\x00\\x00\\x6B\\x00\\x00\\x00\"\n\"\\x83\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x6D\\x00\\x00\\x00\\x69\\x00\\x00\\x00\\x6C\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x6E\\x00\\x00\\x00\"\n\"\\x39\\x00\\x00\\x00\\x0C\\x00\\x08\\x00\\x06\\x00\\x00\\x00\\x6F\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x2B\\x00\\x00\\x00\\x6E\\x00\\x00\\x00\\x57\\x00\\x00\\x00\"\n\"\\x69\\x00\\x00\\x00\\x41\\x00\\x06\\x00\\x42\\x00\\x00\\x00\\x70\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x6A\\x00\\x00\\x00\\x35\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x06\\x00\\x00\\x00\\x71\\x00\\x00\\x00\\x70\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x72\\x00\\x00\\x00\\x6F\\x00\\x00\\x00\\x71\\x00\\x00\\x00\"\n\"\\x81\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x73\\x00\\x00\\x00\\x6D\\x00\\x00\\x00\\x72\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x39\\x00\\x00\\x00\\x73\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x74\\x00\\x00\\x00\\x0A\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x75\\x00\\x00\\x00\\x39\\x00\\x00\\x00\"\n\"\\x50\\x00\\x07\\x00\\x07\\x00\\x00\\x00\\x76\\x00\\x00\\x00\\x69\\x00\\x00\\x00\\x69\\x00\\x00\\x00\\x69\\x00\\x00\\x00\\x75\\x00\\x00\\x00\\x85\\x00\\x05\\x00\"\n\"\\x07\\x00\\x00\\x00\\x77\\x00\\x00\\x00\\x74\\x00\\x00\\x00\\x76\\x00\\x00\\x00\\xFE\\x00\\x02\\x00\\x77\\x00\\x00\\x00\\x38\\x00\\x01\\x00\"},\n#endif\n#ifdef GLQUAKE\n{QR_OPENGL, 110, \"bloom_blur\",\n\"!!samps 1\\n\"\n//apply gaussian filter\n\n\"varying vec2 tc;\\n\"\n\n\"#ifdef VERTEX_SHADER\\n\"\n\"attribute vec2 v_texcoord;\\n\"\n\"void main ()\\n\"\n\"{\\n\"\n\"tc = v_texcoord;\\n\"\n\"gl_Position = ftetransform();\\n\"\n\"}\\n\"\n\"#endif\\n\"\n\"#ifdef FRAGMENT_SHADER\\n\"\n/*offset should be 1.2 pixels away from the center*/\n\"uniform vec3 e_glowmod;\\n\"\n\"void main ()\\n\"\n\"{\\n\"\n\"gl_FragColor =\\n\"\n\"0.3125 * texture2D(s_t0, tc - e_glowmod.st) +\\n\"\n\"0.375 * texture2D(s_t0, tc) +\\n\"\n\"0.3125 * texture2D(s_t0, tc + e_glowmod.st);\\n\"\n\"}\\n\"\n\"#endif\\n\"\n},\n#endif\n#ifdef VKQUAKE\n{QR_VULKAN, -1, \"bloom_blur\",\n\"\\xFF\\x53\\x50\\x56\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x2C\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x2C\\x00\\x00\\x00\"\n\"\\x0C\\x0A\\x00\\x00\\x38\\x0A\\x00\\x00\\x50\\x08\\x00\\x00\\x03\\x02\\x23\\x07\\x00\\x00\\x01\\x00\\x0A\\x00\\x08\\x00\\x4C\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x11\\x00\\x02\\x00\\x01\\x00\\x00\\x00\\x0B\\x00\\x06\\x00\\x01\\x00\\x00\\x00\\x47\\x4C\\x53\\x4C\\x2E\\x73\\x74\\x64\\x2E\\x34\\x35\\x30\\x00\\x00\\x00\\x00\"\n\"\\x0E\\x00\\x03\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x0F\\x00\\x0E\\x00\\x00\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x6D\\x61\\x69\\x6E\\x00\\x00\\x00\\x00\"\n\"\\x1C\\x00\\x00\\x00\\x39\\x00\\x00\\x00\\x3B\\x00\\x00\\x00\\x3F\\x00\\x00\\x00\\x47\\x00\\x00\\x00\\x48\\x00\\x00\\x00\\x49\\x00\\x00\\x00\\x4A\\x00\\x00\\x00\"\n\"\\x4B\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x11\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x13\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x13\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x13\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x13\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x80\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x13\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xC0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\xCC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xD0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xDC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xE0\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xEC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x09\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\xF0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x0A\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xFC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x13\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x40\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x4C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\"\n\"\\x0E\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x50\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x5C\\x01\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x60\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x11\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x6C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x70\\x01\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x13\\x00\\x00\\x00\\x13\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x14\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x90\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x94\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\"\n\"\\x16\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x98\\x01\\x00\\x00\\x47\\x00\\x03\\x00\\x13\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x15\\x00\\x00\\x00\"\n\"\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x15\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x1C\\x00\\x00\\x00\"\n\"\\x1E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x39\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x3B\\x00\\x00\\x00\"\n\"\\x1E\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x3D\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x03\\x00\"\n\"\\x3D\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x43\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x43\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x43\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x43\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x43\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x4C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x43\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x50\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x43\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x5C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x43\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x60\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x43\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x6C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x43\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x70\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x43\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x43\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x88\\x00\\x00\\x00\\x47\\x00\\x03\\x00\\x43\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x45\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x45\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x47\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x48\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x03\\x00\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x49\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x4A\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x05\\x00\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x4B\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x13\\x00\\x02\\x00\\x02\\x00\\x00\\x00\\x21\\x00\\x03\\x00\\x03\\x00\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\x16\\x00\\x03\\x00\\x06\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x17\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x04\\x00\\x00\\x00\"\n\"\\x21\\x00\\x03\\x00\\x08\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x18\\x00\\x04\\x00\"\n\"\\x0D\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x17\\x00\\x04\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x15\\x00\\x04\\x00\"\n\"\\x0F\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x0F\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x1C\\x00\\x04\\x00\"\n\"\\x11\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x17\\x00\\x04\\x00\\x12\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x1E\\x00\\x19\\x00\"\n\"\\x13\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x11\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x12\\x00\\x00\\x00\"\n\"\\x20\\x00\\x04\\x00\\x14\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x13\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x14\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x15\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x17\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x20\\x00\\x04\\x00\\x18\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\"\n\"\\x3B\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\\x1C\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x00\\x00\\x80\\x3F\"\n\"\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x24\\x00\\x00\\x00\\x00\\x00\\x80\\xBF\\x2B\\x00\\x04\\x00\\x0F\\x00\\x00\\x00\\x25\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x20\\x00\\x04\\x00\\x26\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x0F\\x00\\x00\\x00\\x2B\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x2B\\x00\\x04\\x00\\x0F\\x00\\x00\\x00\\x2E\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x32\\x00\\x00\\x00\\x00\\x00\\x00\\x40\"\n\"\\x20\\x00\\x04\\x00\\x38\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x38\\x00\\x00\\x00\\x39\\x00\\x00\\x00\\x03\\x00\\x00\\x00\"\n\"\\x20\\x00\\x04\\x00\\x3A\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x3A\\x00\\x00\\x00\\x3B\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x1E\\x00\\x03\\x00\\x3D\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x3E\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x3D\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\"\n\"\\x3E\\x00\\x00\\x00\\x3F\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x41\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x1E\\x00\\x0C\\x00\"\n\"\\x43\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x44\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x43\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\"\n\"\\x44\\x00\\x00\\x00\\x45\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x46\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\"\n\"\\x46\\x00\\x00\\x00\\x47\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x3A\\x00\\x00\\x00\\x48\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\"\n\"\\x1B\\x00\\x00\\x00\\x49\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\\x4A\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\"\n\"\\x1B\\x00\\x00\\x00\\x4B\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x36\\x00\\x05\\x00\\x02\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x03\\x00\\x00\\x00\"\n\"\\xF8\\x00\\x02\\x00\\x05\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x12\\x00\\x00\\x00\\x3C\\x00\\x00\\x00\\x3B\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x39\\x00\\x00\\x00\"\n\"\\x3C\\x00\\x00\\x00\\x39\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x41\\x00\\x00\\x00\\x42\\x00\\x00\\x00\"\n\"\\x3F\\x00\\x00\\x00\\x17\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x42\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\xFD\\x00\\x01\\x00\\x38\\x00\\x01\\x00\\x36\\x00\\x05\\x00\"\n\"\\x07\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x0A\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\"\n\"\\x0C\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x18\\x00\\x00\\x00\\x19\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x17\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x0D\\x00\\x00\\x00\\x1A\\x00\\x00\\x00\\x19\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0E\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x1C\\x00\\x00\\x00\\x51\\x00\\x05\\x00\"\n\"\\x06\\x00\\x00\\x00\\x1F\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x50\\x00\\x07\\x00\\x07\\x00\\x00\\x00\"\n\"\\x22\\x00\\x00\\x00\\x1F\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x91\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x1A\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x0C\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x26\\x00\\x00\\x00\\x27\\x00\\x00\\x00\"\n\"\\x0C\\x00\\x00\\x00\\x25\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x28\\x00\\x00\\x00\\x27\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\"\n\"\\x29\\x00\\x00\\x00\\x28\\x00\\x00\\x00\\x24\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x26\\x00\\x00\\x00\\x2A\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x25\\x00\\x00\\x00\"\n\"\\x3E\\x00\\x03\\x00\\x2A\\x00\\x00\\x00\\x29\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x26\\x00\\x00\\x00\\x2C\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x2B\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\\x2C\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x26\\x00\\x00\\x00\\x2F\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\"\n\"\\x2E\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x30\\x00\\x00\\x00\\x2F\\x00\\x00\\x00\\x81\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x31\\x00\\x00\\x00\"\n\"\\x2D\\x00\\x00\\x00\\x30\\x00\\x00\\x00\\x88\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x33\\x00\\x00\\x00\\x31\\x00\\x00\\x00\\x32\\x00\\x00\\x00\\x41\\x00\\x05\\x00\"\n\"\\x26\\x00\\x00\\x00\\x34\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x2B\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x34\\x00\\x00\\x00\\x33\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x07\\x00\\x00\\x00\\x35\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\xFE\\x00\\x02\\x00\\x35\\x00\\x00\\x00\\x38\\x00\\x01\\x00\\x03\\x02\\x23\\x07\\x00\\x00\\x01\\x00\"\n\"\\x0A\\x00\\x08\\x00\\x37\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x11\\x00\\x02\\x00\\x01\\x00\\x00\\x00\\x0B\\x00\\x06\\x00\\x01\\x00\\x00\\x00\\x47\\x4C\\x53\\x4C\"\n\"\\x2E\\x73\\x74\\x64\\x2E\\x34\\x35\\x30\\x00\\x00\\x00\\x00\\x0E\\x00\\x03\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x0F\\x00\\x07\\x00\\x04\\x00\\x00\\x00\"\n\"\\x04\\x00\\x00\\x00\\x6D\\x61\\x69\\x6E\\x00\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x10\\x00\\x03\\x00\\x04\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x09\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x0E\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x0E\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x12\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x18\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x19\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x05\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x19\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x19\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x19\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x19\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x19\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\"\n\"\\x48\\x00\\x04\\x00\\x19\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x19\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x80\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x19\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x19\\x00\\x00\\x00\"\n\"\\x03\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xC0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x19\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xCC\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x19\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xD0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x19\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\xDC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x19\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xE0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x19\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xEC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x19\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\xF0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x19\\x00\\x00\\x00\\x0A\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xFC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x19\\x00\\x00\\x00\"\n\"\\x0B\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x19\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x01\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x19\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x4C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x19\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x50\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x19\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x5C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x19\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x60\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x19\\x00\\x00\\x00\\x11\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x6C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x19\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x70\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x19\\x00\\x00\\x00\"\n\"\\x13\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x19\\x00\\x00\\x00\\x14\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x90\\x01\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x19\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x94\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x19\\x00\\x00\\x00\\x16\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x98\\x01\\x00\\x00\\x47\\x00\\x03\\x00\\x19\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\\x22\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x34\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x34\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x34\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x34\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x34\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x4C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x34\\x00\\x00\\x00\\x03\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x50\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x34\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x5C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x34\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x60\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x34\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x6C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x34\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x70\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x34\\x00\\x00\\x00\"\n\"\\x08\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x34\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x88\\x00\\x00\\x00\"\n\"\\x47\\x00\\x03\\x00\\x34\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x36\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\"\n\"\\x36\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x13\\x00\\x02\\x00\\x02\\x00\\x00\\x00\\x21\\x00\\x03\\x00\\x03\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x16\\x00\\x03\\x00\\x06\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x17\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x20\\x00\\x04\\x00\"\n\"\\x08\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x08\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\"\n\"\\x06\\x00\\x00\\x00\\x0A\\x00\\x00\\x00\\x00\\x00\\xA0\\x3E\\x19\\x00\\x09\\x00\\x0B\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x1B\\x00\\x03\\x00\\x0C\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x20\\x00\\x04\\x00\"\n\"\\x0D\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x17\\x00\\x04\\x00\"\n\"\\x10\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x11\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\"\n\"\\x11\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x18\\x00\\x04\\x00\\x14\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x17\\x00\\x04\\x00\"\n\"\\x15\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x15\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\"\n\"\\x16\\x00\\x00\\x00\\x17\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x1C\\x00\\x04\\x00\\x18\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x17\\x00\\x00\\x00\\x1E\\x00\\x19\\x00\"\n\"\\x19\\x00\\x00\\x00\\x14\\x00\\x00\\x00\\x14\\x00\\x00\\x00\\x14\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x15\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x18\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x15\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x10\\x00\\x00\\x00\"\n\"\\x20\\x00\\x04\\x00\\x1A\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x19\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x1A\\x00\\x00\\x00\\x1B\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x15\\x00\\x04\\x00\\x1C\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x1C\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x10\\x00\\x00\\x00\"\n\"\\x20\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x25\\x00\\x00\\x00\\x00\\x00\\xC0\\x3E\"\n\"\\x1E\\x00\\x0C\\x00\\x34\\x00\\x00\\x00\\x14\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x15\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x35\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x34\\x00\\x00\\x00\"\n\"\\x3B\\x00\\x04\\x00\\x35\\x00\\x00\\x00\\x36\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x36\\x00\\x05\\x00\\x02\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x03\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x05\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0C\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x10\\x00\\x00\\x00\\x13\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x1E\\x00\\x00\\x00\\x1F\\x00\\x00\\x00\\x1B\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x15\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x1F\\x00\\x00\\x00\\x4F\\x00\\x07\\x00\\x10\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x20\\x00\\x00\\x00\"\n\"\\x20\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x83\\x00\\x05\\x00\\x10\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x13\\x00\\x00\\x00\\x21\\x00\\x00\\x00\"\n\"\\x57\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x8E\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x24\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x0A\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0C\\x00\\x00\\x00\\x26\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x10\\x00\\x00\\x00\"\n\"\\x27\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x57\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x28\\x00\\x00\\x00\\x26\\x00\\x00\\x00\\x27\\x00\\x00\\x00\\x8E\\x00\\x05\\x00\"\n\"\\x07\\x00\\x00\\x00\\x29\\x00\\x00\\x00\\x28\\x00\\x00\\x00\\x25\\x00\\x00\\x00\\x81\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x2A\\x00\\x00\\x00\\x24\\x00\\x00\\x00\"\n\"\\x29\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0C\\x00\\x00\\x00\\x2B\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x10\\x00\\x00\\x00\\x2C\\x00\\x00\\x00\"\n\"\\x12\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x1E\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\\x1B\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x15\\x00\\x00\\x00\"\n\"\\x2E\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\\x4F\\x00\\x07\\x00\\x10\\x00\\x00\\x00\\x2F\\x00\\x00\\x00\\x2E\\x00\\x00\\x00\\x2E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x81\\x00\\x05\\x00\\x10\\x00\\x00\\x00\\x30\\x00\\x00\\x00\\x2C\\x00\\x00\\x00\\x2F\\x00\\x00\\x00\\x57\\x00\\x05\\x00\\x07\\x00\\x00\\x00\"\n\"\\x31\\x00\\x00\\x00\\x2B\\x00\\x00\\x00\\x30\\x00\\x00\\x00\\x8E\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x32\\x00\\x00\\x00\\x31\\x00\\x00\\x00\\x0A\\x00\\x00\\x00\"\n\"\\x81\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x33\\x00\\x00\\x00\\x2A\\x00\\x00\\x00\\x32\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x09\\x00\\x00\\x00\\x33\\x00\\x00\\x00\"\n\"\\xFD\\x00\\x01\\x00\\x38\\x00\\x01\\x00\"},\n#endif\n#ifdef GLQUAKE\n{QR_OPENGL, 110, \"bloom_filter\",\n\"!!cvarv r_bloom_filter\\n\"\n\"!!samps 1\\n\"\n//the bloom filter\n//filter out any texels which are not to bloom\n\n\"varying vec2 tc;\\n\"\n\n\"#ifdef VERTEX_SHADER\\n\"\n\"attribute vec2 v_texcoord;\\n\"\n\"void main ()\\n\"\n\"{\\n\"\n\"tc = v_texcoord;\\n\"\n\"gl_Position = ftetransform();\\n\"\n\"}\\n\"\n\"#endif\\n\"\n\"#ifdef FRAGMENT_SHADER\\n\"\n\"uniform vec3 cvar_r_bloom_filter;\\n\"\n\"void main ()\\n\"\n\"{\\n\"\n\"gl_FragColor.rgb = (texture2D(s_t0, tc).rgb - cvar_r_bloom_filter)/(1.0-cvar_r_bloom_filter);\\n\"\n\"}\\n\"\n\"#endif\\n\"\n},\n#endif\n#ifdef VKQUAKE\n{QR_VULKAN, -1, \"bloom_filter\",\n\"\\xFF\\x53\\x50\\x56\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x2C\\x00\\x00\\x00\\x1F\\x00\\x00\\x00\\x4C\\x00\\x00\\x00\"\n\"\\xB0\\x0A\\x00\\x00\\xFC\\x0A\\x00\\x00\\x08\\x08\\x00\\x00\\x01\\x00\\x66\\x33\\x72\\x5F\\x62\\x6C\\x6F\\x6F\\x6D\\x5F\\x66\\x69\\x6C\\x74\\x65\\x72\\x00\\x3F\"\n\"\\x33\\x33\\x33\\x3F\\x33\\x33\\x33\\x3F\\x33\\x33\\x33\\x00\\x03\\x02\\x23\\x07\\x00\\x00\\x01\\x00\\x0A\\x00\\x08\\x00\\x52\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x11\\x00\\x02\\x00\\x01\\x00\\x00\\x00\\x0B\\x00\\x06\\x00\\x01\\x00\\x00\\x00\\x47\\x4C\\x53\\x4C\\x2E\\x73\\x74\\x64\\x2E\\x34\\x35\\x30\\x00\\x00\\x00\\x00\"\n\"\\x0E\\x00\\x03\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x0F\\x00\\x0E\\x00\\x00\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x6D\\x61\\x69\\x6E\\x00\\x00\\x00\\x00\"\n\"\\x22\\x00\\x00\\x00\\x3F\\x00\\x00\\x00\\x41\\x00\\x00\\x00\\x45\\x00\\x00\\x00\\x4D\\x00\\x00\\x00\\x4E\\x00\\x00\\x00\\x4F\\x00\\x00\\x00\\x50\\x00\\x00\\x00\"\n\"\\x51\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x0E\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\x0F\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x01\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\x10\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x02\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\x17\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x19\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x19\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x19\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\"\n\"\\x19\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x19\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x19\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x19\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x19\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x19\\x00\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x19\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xC0\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x19\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xCC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x19\\x00\\x00\\x00\\x05\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\xD0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x19\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xDC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x19\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xE0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x19\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\xEC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x19\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xF0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x19\\x00\\x00\\x00\"\n\"\\x0A\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xFC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x19\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x01\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x19\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x19\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x4C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x19\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x50\\x01\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x19\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x5C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x19\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x60\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x19\\x00\\x00\\x00\\x11\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x6C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x19\\x00\\x00\\x00\"\n\"\\x12\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x70\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x19\\x00\\x00\\x00\\x13\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x01\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x19\\x00\\x00\\x00\\x14\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x90\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x19\\x00\\x00\\x00\\x15\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x94\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x19\\x00\\x00\\x00\\x16\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x98\\x01\\x00\\x00\\x47\\x00\\x03\\x00\"\n\"\\x19\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\"\n\"\\x21\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x22\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x3F\\x00\\x00\\x00\"\n\"\\x1E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x41\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x43\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x03\\x00\\x43\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x49\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x49\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x49\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x49\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x40\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x49\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x4C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x49\\x00\\x00\\x00\"\n\"\\x03\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x50\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x49\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x5C\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x49\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x60\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x49\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x6C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x49\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x70\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x49\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x49\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x88\\x00\\x00\\x00\\x47\\x00\\x03\\x00\\x49\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x4B\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x4B\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x4D\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x4E\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x4F\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x04\\x00\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x50\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x51\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x13\\x00\\x02\\x00\\x02\\x00\\x00\\x00\\x21\\x00\\x03\\x00\\x03\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x16\\x00\\x03\\x00\\x06\\x00\\x00\\x00\\x20\\x00\\x00\\x00\"\n\"\\x17\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x21\\x00\\x03\\x00\\x08\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x17\\x00\\x04\\x00\"\n\"\\x0B\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x0C\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\"\n\"\\x0C\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x32\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x00\\x00\\x80\\x43\\x32\\x00\\x04\\x00\"\n\"\\x06\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\\x00\\x80\\x80\\x43\\x32\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x00\\x00\\x81\\x43\\x20\\x00\\x04\\x00\"\n\"\\x12\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x18\\x00\\x04\\x00\\x14\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x15\\x00\\x04\\x00\"\n\"\\x15\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x15\\x00\\x00\\x00\\x16\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x1C\\x00\\x04\\x00\"\n\"\\x17\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x16\\x00\\x00\\x00\\x17\\x00\\x04\\x00\\x18\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x1E\\x00\\x19\\x00\"\n\"\\x19\\x00\\x00\\x00\\x14\\x00\\x00\\x00\\x14\\x00\\x00\\x00\\x14\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x0B\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x17\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x18\\x00\\x00\\x00\"\n\"\\x20\\x00\\x04\\x00\\x1A\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x19\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x1A\\x00\\x00\\x00\\x1B\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x15\\x00\\x04\\x00\\x1C\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x1C\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x20\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x14\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x21\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\"\n\"\\x3B\\x00\\x04\\x00\\x21\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x24\\x00\\x00\\x00\\x00\\x00\\x80\\x3F\"\n\"\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x2A\\x00\\x00\\x00\\x00\\x00\\x80\\xBF\\x2B\\x00\\x04\\x00\\x15\\x00\\x00\\x00\\x2B\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x20\\x00\\x04\\x00\\x2C\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x15\\x00\\x00\\x00\\x31\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x2B\\x00\\x04\\x00\\x15\\x00\\x00\\x00\\x34\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x38\\x00\\x00\\x00\\x00\\x00\\x00\\x40\"\n\"\\x20\\x00\\x04\\x00\\x3E\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x18\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x3E\\x00\\x00\\x00\\x3F\\x00\\x00\\x00\\x03\\x00\\x00\\x00\"\n\"\\x20\\x00\\x04\\x00\\x40\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x18\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x40\\x00\\x00\\x00\\x41\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x1E\\x00\\x03\\x00\\x43\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x44\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x43\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\"\n\"\\x44\\x00\\x00\\x00\\x45\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x47\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x1E\\x00\\x0C\\x00\"\n\"\\x49\\x00\\x00\\x00\\x14\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x18\\x00\\x00\\x00\\x18\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x4A\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x49\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\"\n\"\\x4A\\x00\\x00\\x00\\x4B\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x4C\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\"\n\"\\x4C\\x00\\x00\\x00\\x4D\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x40\\x00\\x00\\x00\\x4E\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\"\n\"\\x21\\x00\\x00\\x00\\x4F\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x21\\x00\\x00\\x00\\x50\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\"\n\"\\x21\\x00\\x00\\x00\\x51\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x36\\x00\\x05\\x00\\x02\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x03\\x00\\x00\\x00\"\n\"\\xF8\\x00\\x02\\x00\\x05\\x00\\x00\\x00\\x50\\x00\\x06\\x00\\x0B\\x00\\x00\\x00\\x11\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\\x10\\x00\\x00\\x00\"\n\"\\x3E\\x00\\x03\\x00\\x0D\\x00\\x00\\x00\\x11\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x18\\x00\\x00\\x00\\x42\\x00\\x00\\x00\\x41\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\"\n\"\\x3F\\x00\\x00\\x00\\x42\\x00\\x00\\x00\\x39\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x46\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x47\\x00\\x00\\x00\"\n\"\\x48\\x00\\x00\\x00\\x45\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x48\\x00\\x00\\x00\\x46\\x00\\x00\\x00\\xFD\\x00\\x01\\x00\\x38\\x00\\x01\\x00\"\n\"\\x36\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x0A\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\"\n\"\\x12\\x00\\x00\\x00\\x13\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x1E\\x00\\x00\\x00\\x1F\\x00\\x00\\x00\\x1B\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x14\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x1F\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x22\\x00\\x00\\x00\"\n\"\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x25\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x26\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x27\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x50\\x00\\x07\\x00\"\n\"\\x07\\x00\\x00\\x00\\x28\\x00\\x00\\x00\\x25\\x00\\x00\\x00\\x26\\x00\\x00\\x00\\x27\\x00\\x00\\x00\\x24\\x00\\x00\\x00\\x91\\x00\\x05\\x00\\x07\\x00\\x00\\x00\"\n\"\\x29\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x28\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x13\\x00\\x00\\x00\\x29\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x2C\\x00\\x00\\x00\"\n\"\\x2D\\x00\\x00\\x00\\x13\\x00\\x00\\x00\\x2B\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x2E\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\\x85\\x00\\x05\\x00\"\n\"\\x06\\x00\\x00\\x00\\x2F\\x00\\x00\\x00\\x2E\\x00\\x00\\x00\\x2A\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x2C\\x00\\x00\\x00\\x30\\x00\\x00\\x00\\x13\\x00\\x00\\x00\"\n\"\\x2B\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x30\\x00\\x00\\x00\\x2F\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x2C\\x00\\x00\\x00\\x32\\x00\\x00\\x00\\x13\\x00\\x00\\x00\"\n\"\\x31\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x33\\x00\\x00\\x00\\x32\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x2C\\x00\\x00\\x00\\x35\\x00\\x00\\x00\"\n\"\\x13\\x00\\x00\\x00\\x34\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x36\\x00\\x00\\x00\\x35\\x00\\x00\\x00\\x81\\x00\\x05\\x00\\x06\\x00\\x00\\x00\"\n\"\\x37\\x00\\x00\\x00\\x33\\x00\\x00\\x00\\x36\\x00\\x00\\x00\\x88\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x39\\x00\\x00\\x00\\x37\\x00\\x00\\x00\\x38\\x00\\x00\\x00\"\n\"\\x41\\x00\\x05\\x00\\x2C\\x00\\x00\\x00\\x3A\\x00\\x00\\x00\\x13\\x00\\x00\\x00\\x31\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x3A\\x00\\x00\\x00\\x39\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x00\\x00\\x13\\x00\\x00\\x00\\xFE\\x00\\x02\\x00\\x3B\\x00\\x00\\x00\\x38\\x00\\x01\\x00\\x03\\x02\\x23\\x07\"\n\"\\x00\\x00\\x01\\x00\\x0A\\x00\\x08\\x00\\x2F\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x11\\x00\\x02\\x00\\x01\\x00\\x00\\x00\\x0B\\x00\\x06\\x00\\x01\\x00\\x00\\x00\"\n\"\\x47\\x4C\\x53\\x4C\\x2E\\x73\\x74\\x64\\x2E\\x34\\x35\\x30\\x00\\x00\\x00\\x00\\x0E\\x00\\x03\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x0F\\x00\\x07\\x00\"\n\"\\x04\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x6D\\x61\\x69\\x6E\\x00\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x18\\x00\\x00\\x00\\x10\\x00\\x03\\x00\\x04\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x0A\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x01\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\x0C\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x02\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\x10\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x14\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x14\\x00\\x00\\x00\\x21\\x00\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x18\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x28\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x29\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x29\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x29\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\"\n\"\\x29\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x29\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x29\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x29\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x29\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x29\\x00\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x29\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xC0\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x29\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xCC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x29\\x00\\x00\\x00\\x05\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\xD0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x29\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xDC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x29\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xE0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x29\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\xEC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x29\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xF0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x29\\x00\\x00\\x00\"\n\"\\x0A\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xFC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x29\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x01\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x29\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x29\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x4C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x29\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x50\\x01\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x29\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x5C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x29\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x60\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x29\\x00\\x00\\x00\\x11\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x6C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x29\\x00\\x00\\x00\"\n\"\\x12\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x70\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x29\\x00\\x00\\x00\\x13\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x01\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x29\\x00\\x00\\x00\\x14\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x90\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x29\\x00\\x00\\x00\\x15\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x94\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x29\\x00\\x00\\x00\\x16\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x98\\x01\\x00\\x00\\x47\\x00\\x03\\x00\"\n\"\\x29\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x2B\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x2B\\x00\\x00\\x00\"\n\"\\x21\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x2C\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x2C\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x2C\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x2C\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x2C\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x4C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x2C\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x50\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x2C\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x5C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x2C\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x60\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x2C\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x6C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x2C\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x70\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x2C\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x2C\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x88\\x00\\x00\\x00\\x47\\x00\\x03\\x00\\x2C\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x2E\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x2E\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x13\\x00\\x02\\x00\\x02\\x00\\x00\\x00\\x21\\x00\\x03\\x00\\x03\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x16\\x00\\x03\\x00\\x06\\x00\\x00\\x00\\x20\\x00\\x00\\x00\"\n\"\\x17\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x08\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x3B\\x00\\x04\\x00\\x08\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x32\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x0A\\x00\\x00\\x00\\x00\\x00\\x80\\x43\"\n\"\\x32\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x00\\x80\\x80\\x43\\x32\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x00\\x00\\x81\\x43\"\n\"\\x17\\x00\\x04\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x0F\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\"\n\"\\x3B\\x00\\x04\\x00\\x0F\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x19\\x00\\x09\\x00\\x11\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x1B\\x00\\x03\\x00\\x12\\x00\\x00\\x00\\x11\\x00\\x00\\x00\"\n\"\\x20\\x00\\x04\\x00\\x13\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x13\\x00\\x00\\x00\\x14\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x17\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x17\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x16\\x00\\x00\\x00\"\n\"\\x3B\\x00\\x04\\x00\\x17\\x00\\x00\\x00\\x18\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x00\\x00\\x80\\x3F\"\n\"\\x18\\x00\\x04\\x00\\x25\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x15\\x00\\x04\\x00\\x26\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x2B\\x00\\x04\\x00\\x26\\x00\\x00\\x00\\x27\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x1C\\x00\\x04\\x00\\x28\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x27\\x00\\x00\\x00\"\n\"\\x1E\\x00\\x19\\x00\\x29\\x00\\x00\\x00\\x25\\x00\\x00\\x00\\x25\\x00\\x00\\x00\\x25\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x28\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x16\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x2A\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x29\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x2A\\x00\\x00\\x00\\x2B\\x00\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\x1E\\x00\\x0C\\x00\\x2C\\x00\\x00\\x00\\x25\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x16\\x00\\x00\\x00\\x16\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x2D\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x2C\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x2D\\x00\\x00\\x00\\x2E\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x36\\x00\\x05\\x00\\x02\\x00\\x00\\x00\\x04\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x05\\x00\\x00\\x00\\x50\\x00\\x06\\x00\\x07\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x0A\\x00\\x00\\x00\"\n\"\\x0B\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x09\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x12\\x00\\x00\\x00\\x15\\x00\\x00\\x00\"\n\"\\x14\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x19\\x00\\x00\\x00\\x18\\x00\\x00\\x00\\x57\\x00\\x05\\x00\\x0E\\x00\\x00\\x00\\x1A\\x00\\x00\\x00\"\n\"\\x15\\x00\\x00\\x00\\x19\\x00\\x00\\x00\\x4F\\x00\\x08\\x00\\x07\\x00\\x00\\x00\\x1B\\x00\\x00\\x00\\x1A\\x00\\x00\\x00\\x1A\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x1C\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x83\\x00\\x05\\x00\\x07\\x00\\x00\\x00\"\n\"\\x1D\\x00\\x00\\x00\\x1B\\x00\\x00\\x00\\x1C\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x1F\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x50\\x00\\x06\\x00\"\n\"\\x07\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x83\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x21\\x00\\x00\\x00\"\n\"\\x20\\x00\\x00\\x00\\x1F\\x00\\x00\\x00\\x88\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x0E\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x4F\\x00\\x09\\x00\\x0E\\x00\\x00\\x00\\x24\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x22\\x00\\x00\\x00\"\n\"\\x04\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x10\\x00\\x00\\x00\\x24\\x00\\x00\\x00\\xFD\\x00\\x01\\x00\"\n\"\\x38\\x00\\x01\\x00\"},\n#endif\n#ifdef GLQUAKE\n{QR_OPENGL, 110, \"bloom_final\",\n\"!!cvarf r_bloom\\n\"\n\"!!cvarf r_bloom_retain=1.0\\n\"\n\"!!samps 4\\n\"\n//add them together\n//optionally apply tonemapping\n\n\"varying vec2 tc;\\n\"\n\n\"#ifdef VERTEX_SHADER\\n\"\n\"attribute vec2 v_texcoord;\\n\"\n\"void main ()\\n\"\n\"{\\n\"\n\"tc = v_texcoord;\\n\"\n\"gl_Position = ftetransform();\\n\"\n\"}\\n\"\n\"#endif\\n\"\n\"#ifdef FRAGMENT_SHADER\\n\"\n\"uniform float cvar_r_bloom;\\n\"\n\"uniform float cvar_r_bloom_retain;\\n\"\n\"void main ()\\n\"\n\"{\\n\"\n\"gl_FragColor = \\n\"\n\"cvar_r_bloom_retain * texture2D(s_t0, tc) +\\n\"\n\"cvar_r_bloom*(\\n\"\n\"texture2D(s_t1, tc) +\\n\"\n\"texture2D(s_t2, tc) +\\n\"\n\"texture2D(s_t3, tc)\\n\"\n\") ;\\n\"\n\"}\\n\"\n\"#endif\\n\"\n},\n#endif\n#ifdef VKQUAKE\n{QR_VULKAN, -1, \"bloom_final\",\n\"\\xFF\\x53\\x50\\x56\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x2C\\x00\\x00\\x00\\x27\\x00\\x00\\x00\\x54\\x00\\x00\\x00\"\n\"\\x4C\\x0A\\x00\\x00\\xA0\\x0A\\x00\\x00\\x5C\\x08\\x00\\x00\\x01\\x00\\x66\\x31\\x72\\x5F\\x62\\x6C\\x6F\\x6F\\x6D\\x00\\x3F\\x80\\x00\\x00\\x01\\x01\\x66\\x31\"\n\"\\x72\\x5F\\x62\\x6C\\x6F\\x6F\\x6D\\x5F\\x72\\x65\\x74\\x61\\x69\\x6E\\x00\\x3F\\x80\\x00\\x00\\x00\\x03\\x02\\x23\\x07\\x00\\x00\\x01\\x00\\x0A\\x00\\x08\\x00\"\n\"\\x4E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x11\\x00\\x02\\x00\\x01\\x00\\x00\\x00\\x0B\\x00\\x06\\x00\\x01\\x00\\x00\\x00\\x47\\x4C\\x53\\x4C\\x2E\\x73\\x74\\x64\"\n\"\\x2E\\x34\\x35\\x30\\x00\\x00\\x00\\x00\\x0E\\x00\\x03\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x0F\\x00\\x0E\\x00\\x00\\x00\\x00\\x00\\x04\\x00\\x00\\x00\"\n\"\\x6D\\x61\\x69\\x6E\\x00\\x00\\x00\\x00\\x1C\\x00\\x00\\x00\\x39\\x00\\x00\\x00\\x3B\\x00\\x00\\x00\\x3F\\x00\\x00\\x00\\x49\\x00\\x00\\x00\\x4A\\x00\\x00\\x00\"\n\"\\x4B\\x00\\x00\\x00\\x4C\\x00\\x00\\x00\\x4D\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x11\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\"\n\"\\x13\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x13\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x13\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x13\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x10\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xC0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\"\n\"\\x04\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xCC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xD0\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xDC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\xE0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xEC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x13\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xF0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x0A\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\xFC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\"\n\"\\x0C\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x4C\\x01\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x50\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x5C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x60\\x01\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x13\\x00\\x00\\x00\\x11\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x6C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x70\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x13\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\"\n\"\\x14\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x90\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x94\\x01\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x16\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x98\\x01\\x00\\x00\\x47\\x00\\x03\\x00\\x13\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x15\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x15\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x1C\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x39\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x3B\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x3D\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x47\\x00\\x03\\x00\\x3D\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x43\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x01\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x44\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x01\\x01\\x00\\x00\\x48\\x00\\x04\\x00\\x45\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x05\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x45\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x45\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x45\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x45\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x4C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x45\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x50\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x45\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x5C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x45\\x00\\x00\\x00\"\n\"\\x05\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x60\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x45\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x6C\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x45\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x70\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x45\\x00\\x00\\x00\\x08\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x80\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x45\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x88\\x00\\x00\\x00\\x47\\x00\\x03\\x00\"\n\"\\x45\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x47\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x47\\x00\\x00\\x00\"\n\"\\x21\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x49\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x4A\\x00\\x00\\x00\"\n\"\\x1E\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x4B\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x4C\\x00\\x00\\x00\"\n\"\\x1E\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x4D\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x13\\x00\\x02\\x00\\x02\\x00\\x00\\x00\"\n\"\\x21\\x00\\x03\\x00\\x03\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x16\\x00\\x03\\x00\\x06\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x17\\x00\\x04\\x00\\x07\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x21\\x00\\x03\\x00\\x08\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x18\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x17\\x00\\x04\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x03\\x00\\x00\\x00\\x15\\x00\\x04\\x00\\x0F\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x0F\\x00\\x00\\x00\\x10\\x00\\x00\\x00\"\n\"\\x04\\x00\\x00\\x00\\x1C\\x00\\x04\\x00\\x11\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x17\\x00\\x04\\x00\\x12\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\x1E\\x00\\x19\\x00\\x13\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x11\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x14\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x13\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x14\\x00\\x00\\x00\"\n\"\\x15\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x15\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x16\\x00\\x00\\x00\"\n\"\\x17\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x18\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\\x1C\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\"\n\"\\x1E\\x00\\x00\\x00\\x00\\x00\\x80\\x3F\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x24\\x00\\x00\\x00\\x00\\x00\\x80\\xBF\\x2B\\x00\\x04\\x00\\x0F\\x00\\x00\\x00\"\n\"\\x25\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x26\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x0F\\x00\\x00\\x00\"\n\"\\x2B\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x0F\\x00\\x00\\x00\\x2E\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\"\n\"\\x32\\x00\\x00\\x00\\x00\\x00\\x00\\x40\\x20\\x00\\x04\\x00\\x38\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x38\\x00\\x00\\x00\"\n\"\\x39\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x3A\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x3A\\x00\\x00\\x00\"\n\"\\x3B\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x1E\\x00\\x03\\x00\\x3D\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x3E\\x00\\x00\\x00\\x03\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x3E\\x00\\x00\\x00\\x3F\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x41\\x00\\x00\\x00\\x03\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x32\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x43\\x00\\x00\\x00\\x00\\x00\\x80\\x43\\x32\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x44\\x00\\x00\\x00\"\n\"\\x00\\x80\\x80\\x43\\x1E\\x00\\x0C\\x00\\x45\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x46\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x45\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x46\\x00\\x00\\x00\\x47\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x48\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x48\\x00\\x00\\x00\\x49\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x3A\\x00\\x00\\x00\\x4A\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\\x4B\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\\x4C\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\\x4D\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x36\\x00\\x05\\x00\\x02\\x00\\x00\\x00\\x04\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x05\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x12\\x00\\x00\\x00\\x3C\\x00\\x00\\x00\\x3B\\x00\\x00\\x00\"\n\"\\x3E\\x00\\x03\\x00\\x39\\x00\\x00\\x00\\x3C\\x00\\x00\\x00\\x39\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x41\\x00\\x05\\x00\"\n\"\\x41\\x00\\x00\\x00\\x42\\x00\\x00\\x00\\x3F\\x00\\x00\\x00\\x17\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x42\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\xFD\\x00\\x01\\x00\"\n\"\\x38\\x00\\x01\\x00\\x36\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x0A\\x00\\x00\\x00\"\n\"\\x3B\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x18\\x00\\x00\\x00\\x19\\x00\\x00\\x00\\x15\\x00\\x00\\x00\"\n\"\\x17\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\x1A\\x00\\x00\\x00\\x19\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0E\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\"\n\"\\x1C\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x1F\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\"\n\"\\x20\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x50\\x00\\x07\\x00\\x07\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x1F\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x91\\x00\\x05\\x00\"\n\"\\x07\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x1A\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x0C\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x41\\x00\\x05\\x00\"\n\"\\x26\\x00\\x00\\x00\\x27\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x25\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x28\\x00\\x00\\x00\\x27\\x00\\x00\\x00\"\n\"\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x29\\x00\\x00\\x00\\x28\\x00\\x00\\x00\\x24\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x26\\x00\\x00\\x00\\x2A\\x00\\x00\\x00\"\n\"\\x0C\\x00\\x00\\x00\\x25\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x2A\\x00\\x00\\x00\\x29\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x26\\x00\\x00\\x00\\x2C\\x00\\x00\\x00\"\n\"\\x0C\\x00\\x00\\x00\\x2B\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\\x2C\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x26\\x00\\x00\\x00\"\n\"\\x2F\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x2E\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x30\\x00\\x00\\x00\\x2F\\x00\\x00\\x00\\x81\\x00\\x05\\x00\"\n\"\\x06\\x00\\x00\\x00\\x31\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\\x30\\x00\\x00\\x00\\x88\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x33\\x00\\x00\\x00\\x31\\x00\\x00\\x00\"\n\"\\x32\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x26\\x00\\x00\\x00\\x34\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x2B\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x34\\x00\\x00\\x00\"\n\"\\x33\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x35\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\xFE\\x00\\x02\\x00\\x35\\x00\\x00\\x00\\x38\\x00\\x01\\x00\"\n\"\\x03\\x02\\x23\\x07\\x00\\x00\\x01\\x00\\x0A\\x00\\x08\\x00\\x32\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x11\\x00\\x02\\x00\\x01\\x00\\x00\\x00\\x0B\\x00\\x06\\x00\"\n\"\\x01\\x00\\x00\\x00\\x47\\x4C\\x53\\x4C\\x2E\\x73\\x74\\x64\\x2E\\x34\\x35\\x30\\x00\\x00\\x00\\x00\\x0E\\x00\\x03\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x0F\\x00\\x07\\x00\\x04\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x6D\\x61\\x69\\x6E\\x00\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x10\\x00\\x03\\x00\"\n\"\\x04\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x09\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x0A\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x01\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\x0E\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x0E\\x00\\x00\\x00\"\n\"\\x21\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x12\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x16\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\x17\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x17\\x00\\x00\\x00\"\n\"\\x21\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\"\n\"\\x21\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x20\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x20\\x00\\x00\\x00\"\n\"\\x21\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x2B\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x2C\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x2C\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x2C\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x2C\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x05\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x2C\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x2C\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x2C\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x2C\\x00\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x2C\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x2C\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xC0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x2C\\x00\\x00\\x00\\x04\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\xCC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x2C\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xD0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x2C\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xDC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x2C\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\xE0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x2C\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xEC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x2C\\x00\\x00\\x00\"\n\"\\x09\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xF0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x2C\\x00\\x00\\x00\\x0A\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xFC\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x2C\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x2C\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x40\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x2C\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x4C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x2C\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x50\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x2C\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x5C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x2C\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x60\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x2C\\x00\\x00\\x00\"\n\"\\x11\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x6C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x2C\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x70\\x01\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x2C\\x00\\x00\\x00\\x13\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x2C\\x00\\x00\\x00\\x14\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x90\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x2C\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x94\\x01\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x2C\\x00\\x00\\x00\\x16\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x98\\x01\\x00\\x00\\x47\\x00\\x03\\x00\\x2C\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\"\n\"\\x2E\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x2E\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x48\\x00\\x04\\x00\"\n\"\\x2F\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x2F\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x2F\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x2F\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x2F\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x4C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x2F\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x50\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x2F\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x5C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x2F\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x60\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x2F\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x6C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x2F\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x70\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x2F\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x2F\\x00\\x00\\x00\\x09\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x88\\x00\\x00\\x00\\x47\\x00\\x03\\x00\\x2F\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x31\\x00\\x00\\x00\\x22\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x31\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x13\\x00\\x02\\x00\\x02\\x00\\x00\\x00\\x21\\x00\\x03\\x00\"\n\"\\x03\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x16\\x00\\x03\\x00\\x06\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x17\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x04\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x08\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x08\\x00\\x00\\x00\\x09\\x00\\x00\\x00\"\n\"\\x03\\x00\\x00\\x00\\x32\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x0A\\x00\\x00\\x00\\x00\\x80\\x80\\x43\\x19\\x00\\x09\\x00\\x0B\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x1B\\x00\\x03\\x00\\x0C\\x00\\x00\\x00\"\n\"\\x0B\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x17\\x00\\x04\\x00\\x10\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x11\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x10\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x11\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x32\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x16\\x00\\x00\\x00\"\n\"\\x00\\x00\\x80\\x43\\x3B\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\x17\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\x1B\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x18\\x00\\x04\\x00\\x27\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x04\\x00\\x00\\x00\\x17\\x00\\x04\\x00\\x28\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x15\\x00\\x04\\x00\\x29\\x00\\x00\\x00\\x20\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x29\\x00\\x00\\x00\\x2A\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x1C\\x00\\x04\\x00\\x2B\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x2A\\x00\\x00\\x00\\x1E\\x00\\x19\\x00\\x2C\\x00\\x00\\x00\\x27\\x00\\x00\\x00\\x27\\x00\\x00\\x00\\x27\\x00\\x00\\x00\\x28\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x28\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x28\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x28\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x2B\\x00\\x00\\x00\\x28\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x28\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x28\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x2D\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x2C\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x2D\\x00\\x00\\x00\"\n\"\\x2E\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x1E\\x00\\x0C\\x00\\x2F\\x00\\x00\\x00\\x27\\x00\\x00\\x00\\x28\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x28\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x28\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x30\\x00\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\x2F\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x30\\x00\\x00\\x00\\x31\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x36\\x00\\x05\\x00\\x02\\x00\\x00\\x00\"\n\"\\x04\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x05\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0C\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\"\n\"\\x0E\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x10\\x00\\x00\\x00\\x13\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x57\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x14\\x00\\x00\\x00\"\n\"\\x0F\\x00\\x00\\x00\\x13\\x00\\x00\\x00\\x8E\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x14\\x00\\x00\\x00\\x0A\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x0C\\x00\\x00\\x00\\x18\\x00\\x00\\x00\\x17\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x10\\x00\\x00\\x00\\x19\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x57\\x00\\x05\\x00\"\n\"\\x07\\x00\\x00\\x00\\x1A\\x00\\x00\\x00\\x18\\x00\\x00\\x00\\x19\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0C\\x00\\x00\\x00\\x1C\\x00\\x00\\x00\\x1B\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x10\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x57\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x1C\\x00\\x00\\x00\"\n\"\\x1D\\x00\\x00\\x00\\x81\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x1F\\x00\\x00\\x00\\x1A\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0C\\x00\\x00\\x00\"\n\"\\x21\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x10\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x57\\x00\\x05\\x00\\x07\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x81\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x24\\x00\\x00\\x00\\x1F\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x8E\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x25\\x00\\x00\\x00\\x24\\x00\\x00\\x00\\x16\\x00\\x00\\x00\\x81\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x26\\x00\\x00\\x00\"\n\"\\x15\\x00\\x00\\x00\\x25\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x09\\x00\\x00\\x00\\x26\\x00\\x00\\x00\\xFD\\x00\\x01\\x00\\x38\\x00\\x01\\x00\"},\n#endif\n#ifdef GLQUAKE\n{QR_OPENGL, 110, \"colourtint\",\n\"!!samps 2\\n\"\n//this glsl shader is useful for cubemapped post processing effects (see csaddon for an example)\n\"varying vec4 tf;\\n\"\n\"#ifdef VERTEX_SHADER\\n\"\n\"void main ()\\n\"\n\"{\\n\"\n\"gl_Position = tf = vec4(v_position.xy,-1.0, 1.0);\\n\"\n\"}\\n\"\n\"#endif\\n\"\n\"#ifdef FRAGMENT_SHADER\\n\"\n\"void main()\\n\"\n\"{\\n\"\n\"vec2 fc;\\n\"\n\"fc = tf.xy / tf.w;\\n\"\n\"vec3 raw = texture2D(s_t0, (1.0 + fc) / 2.0).rgb;\\n\"\n\"#define LUTSIZE 16.0\\n\"\n\"vec3 scale = vec3((LUTSIZE-1.0)/LUTSIZE);\\n\"\n\"vec3 bias = vec3(1.0/(2.0*LUTSIZE));\\n\"\n\"gl_FragColor = texture3D(s_t1, raw * scale + bias);\\n\"\n\"}\\n\"\n\"#endif\\n\"\n},\n#endif\n#ifdef GLQUAKE\n{QR_OPENGL, 110, \"crepuscular_opaque\",\n//opaque surfaces are drawn to the render target to mask out skies\n\"#ifdef VERTEX_SHADER\\n\"\n\"void main ()\\n\"\n\"{\\n\"\n\"gl_Position = ftetransform();\\n\"\n\"}\\n\"\n\"#endif\\n\"\n\"#ifdef FRAGMENT_SHADER\\n\"\n\"void main()\\n\"\n\"{\\n\"\n\"gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0);\\n\"\n\"}\\n\"\n\"#endif\\n\"\n},\n#endif\n#ifdef GLQUAKE\n{QR_OPENGL, 110, \"crepuscular_rays\",\n\"!!cvarf crep_decay\\n\"\n\"!!cvarf crep_density\\n\"\n\"!!cvarf crep_weight\\n\"\n\"!!samps 1\\n\"\n\n//this is a post-processing shader, drawn in 2d\n//there will be a render target containing sky surfaces drawn with crepuscular_sky, and everything else drawn with crepuscular_opaque (to mask out the sky)\n//this shader then just smudges the sky out a bit as though its coming from the sun or whatever through the clouds.\n//yoinked from http://fabiensanglard.net/lightScattering/index.php\n\n\"varying vec2 tc;\\n\"\n\"#ifdef VERTEX_SHADER\\n\"\n\"attribute vec2 v_texcoord;\\n\"\n\"void main ()\\n\"\n\"{\\n\"\n\"tc = v_texcoord;\\n\"\n\"gl_Position = vec4(v_position, 1.0);\\n\"\n\"}\\n\"\n\"#endif\\n\"\n\"#ifdef FRAGMENT_SHADER\\n\"\n\"const float crep_decay = 0.94;\\n\"\n\"const float crep_density = 0.5;\\n\"\n\"const float crep_weight = 0.2;\\n\"\n\"uniform vec3 l_lightcolour;\\n\"\n\"uniform vec3 l_lightscreen;\\n\"\n\"const int NUM_SAMPLES = 100;\\n\"\n\"void main()\\n\"\n\"{\\n\"\n\"vec2 deltaTextCoord = vec2(tc.st - l_lightscreen.xy);\\n\"\n\"vec2 textCoo = tc.st;\\n\"\n\"deltaTextCoord *= 1.0 / float(NUM_SAMPLES) * crep_density;\\n\"\n\"float illuminationDecay = 1.0;\\n\"\n\"gl_FragColor = vec4(0.0,0.0,0.0,0.0);\\n\"\n\"for(int i=0; i < NUM_SAMPLES ; i++)\\n\"\n\"{\\n\"\n\"textCoo -= deltaTextCoord;\\n\"\n\"vec4 sample = texture2D(s_t0, textCoo);\\n\"\n\"sample *= illuminationDecay * crep_weight;\\n\"\n\"gl_FragColor += sample;\\n\"\n\"illuminationDecay *= crep_decay;\\n\"\n\"}\\n\"\n\"gl_FragColor *= vec4(l_lightcolour, 1.0);\\n\"\n\"}\\n\"\n\"#endif\\n\"\n},\n#endif\n#ifdef GLQUAKE\n{QR_OPENGL, 110, \"crepuscular_sky\",\n\"!!samps 2\\n\"\n//pretty much a regular sky shader\n//though in reality we should render a sun circle in the middle.\n//still, its kinda cool to have scrolling clouds masking out parts of the sun.\n\n\"#ifdef VERTEX_SHADER\\n\"\n\"varying vec3 pos;\\n\"\n\"void main ()\\n\"\n\"{\\n\"\n\"pos = v_position.xyz;\\n\"\n\"gl_Position = ftetransform();\\n\"\n\"}\\n\"\n\"#endif\\n\"\n\"#ifdef FRAGMENT_SHADER\\n\"\n\"uniform float e_time;\\n\"\n\"uniform vec3 e_eyepos;\\n\"\n\"varying vec3 pos;\\n\"\n\"void main ()\\n\"\n\"{\\n\"\n\"vec2 tccoord;\\n\"\n\"vec3 dir = pos - e_eyepos;\\n\"\n\"dir.z *= 3.0;\\n\"\n\"dir.xy /= 0.5*length(dir);\\n\"\n\"tccoord = (dir.xy + e_time*0.03125);\\n\"\n\"vec3 solid = vec3(texture2D(s_t0, tccoord));\\n\"\n\"tccoord = (dir.xy + e_time*0.0625);\\n\"\n\"vec4 clouds = texture2D(s_t1, tccoord);\\n\"\n\"gl_FragColor.rgb = (solid.rgb*(1.0-clouds.a)) + (clouds.a*clouds.rgb);\\n\"\n\"}\\n\"\n\"#endif\\n\"\n},\n#endif\n#ifdef GLQUAKE\n{QR_OPENGL, 110, \"depthonly\",\n\"!!ver 100 150\\n\"\n\"!!permu TESS\\n\"\n\"!!permu FRAMEBLEND\\n\"\n\"!!permu SKELETAL\\n\"\n\"!!cvardf r_tessellation_level=5\\n\"\n\n\"#include \\\"sys/defs.h\\\"\\n\"\n\n//standard shader used for drawing shadowmap depth.\n//also used for masking off portals and other things that want depth and no colour.\n//must support skeletal and 2-way vertex blending or Bad Things Will Happen.\n//the vertex shader is responsible for calculating lighting values.\n\n\"#ifdef VERTEX_SHADER\\n\"\n\"#include \\\"sys/skeletal.h\\\"\\n\"\n\"#ifdef TESS\\n\"\n\"varying vec3 vertex;\\n\"\n\"varying vec3 normal;\\n\"\n\"#endif\\n\"\n\"void main ()\\n\"\n\"{\\n\"\n\"#ifdef TESS\\n\"\n\"skeletaltransform_n(normal);\\n\"\n\"vertex = v_position;\\n\"\n\"#else\\n\"\n\"gl_Position = skeletaltransform();\\n\"\n\"#endif\\n\"\n\"}\\n\"\n\"#endif\\n\"\n\n\n\n\"#if defined(TESS_CONTROL_SHADER)\\n\"\n\"layout(vertices = 3) out;\\n\"\n\n\"in vec3 vertex[];\\n\"\n\"out vec3 t_vertex[];\\n\"\n\"in vec3 normal[];\\n\"\n\"out vec3 t_normal[];\\n\"\n\n\"void main()\\n\"\n\"{\\n\"\n//the control shader needs to pass stuff through\n\"#define id gl_InvocationID\\n\"\n\"t_vertex[id] = vertex[id];\\n\"\n\"t_normal[id] = normal[id];\\n\"\n\"gl_TessLevelOuter[0] = float(r_tessellation_level);\\n\"\n\"gl_TessLevelOuter[1] = float(r_tessellation_level);\\n\"\n\"gl_TessLevelOuter[2] = float(r_tessellation_level);\\n\"\n\"gl_TessLevelInner[0] = float(r_tessellation_level);\\n\"\n\"}\\n\"\n\"#endif\\n\"\n\n\n\n\n\n\n\n\n\n\"#if defined(TESS_EVALUATION_SHADER)\\n\"\n\"layout(triangles) in;\\n\"\n\n\"in vec3 t_vertex[];\\n\"\n\"in vec3 t_normal[];\\n\"\n\n\"#define LERP(a) (gl_TessCoord.x*a[0] + gl_TessCoord.y*a[1] + gl_TessCoord.z*a[2])\\n\"\n\"void main()\\n\"\n\"{\\n\"\n\"#define factor 1.0\\n\"\n\"vec3 w = LERP(t_vertex);\\n\"\n\n\"vec3 t0 = w - dot(w-t_vertex[0],t_normal[0])*t_normal[0];\\n\"\n\"vec3 t1 = w - dot(w-t_vertex[1],t_normal[1])*t_normal[1];\\n\"\n\"vec3 t2 = w - dot(w-t_vertex[2],t_normal[2])*t_normal[2];\\n\"\n\"w = w*(1.0-factor) + factor*(gl_TessCoord.x*t0+gl_TessCoord.y*t1+gl_TessCoord.z*t2);\\n\"\n\n\"gl_Position = m_modelviewprojection * vec4(w,1.0);\\n\"\n\"}\\n\"\n\"#endif\\n\"\n\n\n\n\n\n\n\n\n\"#ifdef FRAGMENT_SHADER\\n\"\n\"void main ()\\n\"\n\"{\\n\"\n//must always draw something, supposedly. It might as well be black.\n\"gl_FragColor = vec4(0, 0, 0, 1);\\n\"\n\"}\\n\"\n\"#endif\\n\"\n\n},\n#endif\n#ifdef VKQUAKE\n{QR_VULKAN, -1, \"depthonly\",\n\"\\xFF\\x53\\x50\\x56\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x2C\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x2C\\x00\\x00\\x00\"\n\"\\xBC\\x09\\x00\\x00\\xE8\\x09\\x00\\x00\\xA8\\x05\\x00\\x00\\x03\\x02\\x23\\x07\\x00\\x00\\x01\\x00\\x0A\\x00\\x08\\x00\\x49\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x11\\x00\\x02\\x00\\x01\\x00\\x00\\x00\\x0B\\x00\\x06\\x00\\x01\\x00\\x00\\x00\\x47\\x4C\\x53\\x4C\\x2E\\x73\\x74\\x64\\x2E\\x34\\x35\\x30\\x00\\x00\\x00\\x00\"\n\"\\x0E\\x00\\x03\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x0F\\x00\\x0D\\x00\\x00\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x6D\\x61\\x69\\x6E\\x00\\x00\\x00\\x00\"\n\"\\x1C\\x00\\x00\\x00\\x3A\\x00\\x00\\x00\\x42\\x00\\x00\\x00\\x44\\x00\\x00\\x00\\x45\\x00\\x00\\x00\\x46\\x00\\x00\\x00\\x47\\x00\\x00\\x00\\x48\\x00\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x11\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x13\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x05\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x13\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\"\n\"\\x48\\x00\\x04\\x00\\x13\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x80\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\"\n\"\\x03\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xC0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xCC\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xD0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\xDC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xE0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x13\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xEC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\xF0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x0A\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xFC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\"\n\"\\x0B\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x01\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x4C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x50\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x5C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x13\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x60\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x11\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x6C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x70\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\"\n\"\\x13\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x14\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x90\\x01\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x94\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x16\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x98\\x01\\x00\\x00\\x47\\x00\\x03\\x00\\x13\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x15\\x00\\x00\\x00\\x22\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x15\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x1C\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x38\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x03\\x00\\x38\\x00\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x3E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x3E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x3E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x3E\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x3E\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x4C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x3E\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x50\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x3E\\x00\\x00\\x00\"\n\"\\x04\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x5C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x3E\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x60\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x3E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x6C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x3E\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x70\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x3E\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x3E\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x88\\x00\\x00\\x00\\x47\\x00\\x03\\x00\\x3E\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\"\n\"\\x40\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x40\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x47\\x00\\x04\\x00\"\n\"\\x42\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x44\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\"\n\"\\x45\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x46\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x47\\x00\\x04\\x00\"\n\"\\x47\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x48\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x13\\x00\\x02\\x00\"\n\"\\x02\\x00\\x00\\x00\\x21\\x00\\x03\\x00\\x03\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x16\\x00\\x03\\x00\\x06\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x17\\x00\\x04\\x00\"\n\"\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x21\\x00\\x03\\x00\\x08\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x18\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x17\\x00\\x04\\x00\\x0E\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x15\\x00\\x04\\x00\\x0F\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x0F\\x00\\x00\\x00\"\n\"\\x10\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x1C\\x00\\x04\\x00\\x11\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x17\\x00\\x04\\x00\\x12\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x1E\\x00\\x19\\x00\\x13\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x11\\x00\\x00\\x00\"\n\"\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x14\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x13\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\"\n\"\\x14\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x15\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\"\n\"\\x16\\x00\\x00\\x00\\x17\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x18\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x20\\x00\\x04\\x00\"\n\"\\x1B\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\\x1C\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\"\n\"\\x06\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x00\\x00\\x80\\x3F\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x24\\x00\\x00\\x00\\x00\\x00\\x80\\xBF\\x2B\\x00\\x04\\x00\"\n\"\\x0F\\x00\\x00\\x00\\x25\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x26\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\"\n\"\\x0F\\x00\\x00\\x00\\x2B\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x0F\\x00\\x00\\x00\\x2E\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\"\n\"\\x06\\x00\\x00\\x00\\x32\\x00\\x00\\x00\\x00\\x00\\x00\\x40\\x1E\\x00\\x03\\x00\\x38\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x39\\x00\\x00\\x00\"\n\"\\x03\\x00\\x00\\x00\\x38\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x39\\x00\\x00\\x00\\x3A\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x3C\\x00\\x00\\x00\"\n\"\\x03\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x1E\\x00\\x0C\\x00\\x3E\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x3F\\x00\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\x3E\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x3F\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x41\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x41\\x00\\x00\\x00\\x42\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x43\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x43\\x00\\x00\\x00\\x44\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x41\\x00\\x00\\x00\"\n\"\\x45\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\\x46\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\"\n\"\\x47\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\\x48\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x36\\x00\\x05\\x00\\x02\\x00\\x00\\x00\"\n\"\\x04\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x05\\x00\\x00\\x00\\x39\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x00\\x00\"\n\"\\x09\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x3C\\x00\\x00\\x00\\x3D\\x00\\x00\\x00\\x3A\\x00\\x00\\x00\\x17\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x3D\\x00\\x00\\x00\"\n\"\\x3B\\x00\\x00\\x00\\xFD\\x00\\x01\\x00\\x38\\x00\\x01\\x00\\x36\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x08\\x00\\x00\\x00\"\n\"\\xF8\\x00\\x02\\x00\\x0A\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x18\\x00\\x00\\x00\"\n\"\\x19\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x17\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\x1A\\x00\\x00\\x00\\x19\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x0E\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x1C\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x1F\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x21\\x00\\x00\\x00\"\n\"\\x1D\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x50\\x00\\x07\\x00\\x07\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x1F\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x21\\x00\\x00\\x00\"\n\"\\x1E\\x00\\x00\\x00\\x91\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x1A\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x0C\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x26\\x00\\x00\\x00\\x27\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x25\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\"\n\"\\x28\\x00\\x00\\x00\\x27\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x29\\x00\\x00\\x00\\x28\\x00\\x00\\x00\\x24\\x00\\x00\\x00\\x41\\x00\\x05\\x00\"\n\"\\x26\\x00\\x00\\x00\\x2A\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x25\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x2A\\x00\\x00\\x00\\x29\\x00\\x00\\x00\\x41\\x00\\x05\\x00\"\n\"\\x26\\x00\\x00\\x00\\x2C\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x2B\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\\x2C\\x00\\x00\\x00\"\n\"\\x41\\x00\\x05\\x00\\x26\\x00\\x00\\x00\\x2F\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x2E\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x30\\x00\\x00\\x00\"\n\"\\x2F\\x00\\x00\\x00\\x81\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x31\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\\x30\\x00\\x00\\x00\\x88\\x00\\x05\\x00\\x06\\x00\\x00\\x00\"\n\"\\x33\\x00\\x00\\x00\\x31\\x00\\x00\\x00\\x32\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x26\\x00\\x00\\x00\\x34\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x2B\\x00\\x00\\x00\"\n\"\\x3E\\x00\\x03\\x00\\x34\\x00\\x00\\x00\\x33\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x35\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\xFE\\x00\\x02\\x00\"\n\"\\x35\\x00\\x00\\x00\\x38\\x00\\x01\\x00\\x03\\x02\\x23\\x07\\x00\\x00\\x01\\x00\\x0A\\x00\\x08\\x00\\x16\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x11\\x00\\x02\\x00\"\n\"\\x01\\x00\\x00\\x00\\x0B\\x00\\x06\\x00\\x01\\x00\\x00\\x00\\x47\\x4C\\x53\\x4C\\x2E\\x73\\x74\\x64\\x2E\\x34\\x35\\x30\\x00\\x00\\x00\\x00\\x0E\\x00\\x03\\x00\"\n\"\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x0F\\x00\\x06\\x00\\x04\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x6D\\x61\\x69\\x6E\\x00\\x00\\x00\\x00\\x15\\x00\\x00\\x00\"\n\"\\x10\\x00\\x03\\x00\\x04\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x0C\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\"\n\"\\x0E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x0E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x0E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x0E\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x0E\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x0E\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x0E\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x0E\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x0E\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x10\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x0E\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xC0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x0E\\x00\\x00\\x00\"\n\"\\x04\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xCC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x0E\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xD0\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xDC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x0E\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\xE0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x0E\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xEC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x0E\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xF0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x0E\\x00\\x00\\x00\\x0A\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\xFC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x0E\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x0E\\x00\\x00\\x00\"\n\"\\x0C\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x0E\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x4C\\x01\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x0E\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x50\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x0E\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x5C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x0E\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x60\\x01\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x0E\\x00\\x00\\x00\\x11\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x6C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x0E\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x70\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x0E\\x00\\x00\\x00\\x13\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x0E\\x00\\x00\\x00\"\n\"\\x14\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x90\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x0E\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x94\\x01\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x0E\\x00\\x00\\x00\\x16\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x98\\x01\\x00\\x00\\x47\\x00\\x03\\x00\\x0E\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x10\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x10\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x48\\x00\\x04\\x00\\x11\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x11\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x11\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x11\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x11\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x4C\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x11\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x50\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x11\\x00\\x00\\x00\\x04\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x5C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x11\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x60\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x11\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x6C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x11\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x70\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x11\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x11\\x00\\x00\\x00\"\n\"\\x09\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x88\\x00\\x00\\x00\\x47\\x00\\x03\\x00\\x11\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x13\\x00\\x00\\x00\"\n\"\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x13\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x15\\x00\\x00\\x00\"\n\"\\x1E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x13\\x00\\x02\\x00\\x02\\x00\\x00\\x00\\x21\\x00\\x03\\x00\\x03\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x16\\x00\\x03\\x00\"\n\"\\x06\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x17\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x18\\x00\\x04\\x00\\x08\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x17\\x00\\x04\\x00\\x09\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x15\\x00\\x04\\x00\\x0A\\x00\\x00\\x00\"\n\"\\x20\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x0A\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x1C\\x00\\x04\\x00\\x0C\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x17\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x1E\\x00\\x19\\x00\\x0E\\x00\\x00\\x00\"\n\"\\x08\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x09\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x09\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x20\\x00\\x04\\x00\"\n\"\\x0F\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x0F\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x1E\\x00\\x0C\\x00\"\n\"\\x11\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x12\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x11\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\"\n\"\\x12\\x00\\x00\\x00\\x13\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x14\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\"\n\"\\x14\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x36\\x00\\x05\\x00\\x02\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x03\\x00\\x00\\x00\"\n\"\\xF8\\x00\\x02\\x00\\x05\\x00\\x00\\x00\\xFD\\x00\\x01\\x00\\x38\\x00\\x01\\x00\"},\n#endif\n#ifdef D3D11QUAKE\n{QR_DIRECT3D11, 11, \"depthonly\",\n//used for generating shadowmaps and stuff that draws nothing.\n\n\"struct a2v\\n\"\n\"{\\n\"\n\"float4 pos: POSITION;\\n\"\n\"};\\n\"\n\"struct v2f\\n\"\n\"{\\n\"\n\"float3 col: TEXCOORD;\\n\"\n\"float4 pos: SV_POSITION;\\n\"\n\"};\\n\"\n\n\"#include <ftedefs.h>\\n\"\n\n\"#ifdef VERTEX_SHADER\\n\"\n\"v2f main (a2v inp)\\n\"\n\"{\\n\"\n\"v2f outp;\\n\"\n\"outp.pos = mul(m_model, inp.pos);\\n\"\n\"outp.pos = mul(m_view, outp.pos);\\n\"\n\"outp.pos = mul(m_projection, outp.pos);\\n\"\n\"outp.col = inp.pos.xyz - l_lightposition;\\n\"\n\"return outp;\\n\"\n\"}\\n\"\n\"#endif\\n\"\n\n\"#ifdef FRAGMENT_SHADER\\n\"\n\"#if LEVEL < 0x10000\\n\"\n//pre dx10 requires that we ALWAYS write to a target.\n\"float4 main (v2f inp) : SV_TARGET\\n\"\n\"{\\n\"\n\"return float4(0, 0, 0, 1);\\n\"\n\"}\\n\"\n\"#else\\n\"\n//but on 10, it'll write depth automatically and we don't care about colour.\n\"void main (v2f inp)\\n\"\n\"{\\n\"\n\"}\\n\"\n\"#endif\\n\"\n\"#endif\\n\"\n},\n#endif\n#ifdef GLQUAKE\n{QR_OPENGL, 110, \"default2d\",\n\"!!ver 100-450\\n\"\n\"!!samps img=0\\n\"\n\n//this shader is present for support for gles/gl3core contexts\n//it is single-texture-with-vertex-colours, and doesn't do anything special.\n//beware that a few things use this, including apparently fonts and bloom rescaling.\n//its really not meant to do anything special.\n\n\"varying vec2 tc;\\n\"\n\"varying vec4 vc;\\n\"\n\n\"#ifdef VERTEX_SHADER\\n\"\n\"attribute vec2 v_texcoord;\\n\"\n\"attribute vec4 v_colour;\\n\"\n\"void main ()\\n\"\n\"{\\n\"\n\"tc = v_texcoord;\\n\"\n\"vc = v_colour;\\n\"\n\"gl_Position = ftetransform();\\n\"\n\"}\\n\"\n\"#endif\\n\"\n\"#ifdef FRAGMENT_SHADER\\n\"\n\"void main ()\\n\"\n\"{\\n\"\n\"vec4 f = vc;\\n\"\n\"#ifdef PREMUL\\n\"\n\"f.rgb *= f.a;\\n\"\n\"#endif\\n\"\n\"#ifdef DECLAMP\\n\"\n\"vec2 ntc = fract(tc);\\n\"\n\"#define tc ntc\\n\"\n\"#endif\\n\"\n\"f *= texture2D(s_img, tc);\\n\"\n\"gl_FragColor = f;\\n\"\n\"}\\n\"\n\"#endif\\n\"\n},\n#endif\n#ifdef VKQUAKE\n{QR_VULKAN, -1, \"default2d\",\n\"\\xFF\\x53\\x50\\x56\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x2C\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\\x3C\\x00\\x00\\x00\"\n\"\\x6C\\x0A\\x00\\x00\\xA8\\x0A\\x00\\x00\\x98\\x08\\x00\\x00\\x01\\x00\\x42\\x31\\x50\\x52\\x45\\x4D\\x55\\x4C\\x00\\x00\\x00\\x00\\x00\\x00\\x03\\x02\\x23\\x07\"\n\"\\x00\\x00\\x01\\x00\\x0A\\x00\\x08\\x00\\x4F\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x11\\x00\\x02\\x00\\x01\\x00\\x00\\x00\\x0B\\x00\\x06\\x00\\x01\\x00\\x00\\x00\"\n\"\\x47\\x4C\\x53\\x4C\\x2E\\x73\\x74\\x64\\x2E\\x34\\x35\\x30\\x00\\x00\\x00\\x00\\x0E\\x00\\x03\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x0F\\x00\\x0F\\x00\"\n\"\\x00\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x6D\\x61\\x69\\x6E\\x00\\x00\\x00\\x00\\x1C\\x00\\x00\\x00\\x39\\x00\\x00\\x00\\x3B\\x00\\x00\\x00\\x3E\\x00\\x00\\x00\"\n\"\\x40\\x00\\x00\\x00\\x44\\x00\\x00\\x00\\x4B\\x00\\x00\\x00\\x4C\\x00\\x00\\x00\\x4D\\x00\\x00\\x00\\x4E\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x11\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x13\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\"\n\"\\x48\\x00\\x04\\x00\\x13\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x40\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x13\\x00\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x13\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\xC0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xCC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\"\n\"\\x05\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xD0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xDC\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xE0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x08\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\xEC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xF0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x13\\x00\\x00\\x00\\x0A\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xFC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x00\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\"\n\"\\x0D\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x4C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x50\\x01\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x5C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x10\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x60\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x11\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x6C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x13\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x70\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x13\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x80\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x14\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x90\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\"\n\"\\x15\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x94\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x16\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x98\\x01\\x00\\x00\"\n\"\\x47\\x00\\x03\\x00\\x13\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x15\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\"\n\"\\x15\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x1C\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\"\n\"\\x39\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x3B\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x47\\x00\\x04\\x00\"\n\"\\x3E\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x40\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x42\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x03\\x00\\x42\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\"\n\"\\x47\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x48\\x00\\x04\\x00\\x48\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x48\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x48\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x10\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x48\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x48\\x00\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x4C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x48\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x50\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x48\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x5C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x48\\x00\\x00\\x00\\x05\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x60\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x48\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x6C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x48\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x70\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x48\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x80\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x48\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x88\\x00\\x00\\x00\\x47\\x00\\x03\\x00\\x48\\x00\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x4A\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x4A\\x00\\x00\\x00\\x21\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x4B\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x4C\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\"\n\"\\x04\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x4D\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x4E\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x13\\x00\\x02\\x00\\x02\\x00\\x00\\x00\\x21\\x00\\x03\\x00\\x03\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x16\\x00\\x03\\x00\\x06\\x00\\x00\\x00\"\n\"\\x20\\x00\\x00\\x00\\x17\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x21\\x00\\x03\\x00\\x08\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x20\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x18\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x04\\x00\\x00\\x00\"\n\"\\x17\\x00\\x04\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x15\\x00\\x04\\x00\\x0F\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x2B\\x00\\x04\\x00\\x0F\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x1C\\x00\\x04\\x00\\x11\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\"\n\"\\x17\\x00\\x04\\x00\\x12\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x1E\\x00\\x19\\x00\\x13\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\"\n\"\\x0D\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x11\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x14\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x13\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x14\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x15\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x20\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x17\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x18\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x0D\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\\x1C\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x00\\x00\\x80\\x3F\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x24\\x00\\x00\\x00\"\n\"\\x00\\x00\\x80\\xBF\\x2B\\x00\\x04\\x00\\x0F\\x00\\x00\\x00\\x25\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x26\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x0F\\x00\\x00\\x00\\x2B\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x0F\\x00\\x00\\x00\\x2E\\x00\\x00\\x00\"\n\"\\x03\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x32\\x00\\x00\\x00\\x00\\x00\\x00\\x40\\x20\\x00\\x04\\x00\\x38\\x00\\x00\\x00\\x03\\x00\\x00\\x00\"\n\"\\x12\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x38\\x00\\x00\\x00\\x39\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x3A\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x12\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x3A\\x00\\x00\\x00\\x3B\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x3D\\x00\\x00\\x00\\x03\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x3D\\x00\\x00\\x00\\x3E\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x3F\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x3F\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x1E\\x00\\x03\\x00\\x42\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x20\\x00\\x04\\x00\\x43\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x42\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x43\\x00\\x00\\x00\\x44\\x00\\x00\\x00\\x03\\x00\\x00\\x00\"\n\"\\x32\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x47\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x1E\\x00\\x0C\\x00\\x48\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x12\\x00\\x00\\x00\"\n\"\\x20\\x00\\x04\\x00\\x49\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x48\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x49\\x00\\x00\\x00\\x4A\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x3B\\x00\\x04\\x00\\x3A\\x00\\x00\\x00\\x4B\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\\x4C\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x3B\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\\x4D\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\\x4E\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x36\\x00\\x05\\x00\\x02\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x05\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x12\\x00\\x00\\x00\\x3C\\x00\\x00\\x00\\x3B\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x39\\x00\\x00\\x00\\x3C\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\"\n\"\\x41\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x3E\\x00\\x00\\x00\\x41\\x00\\x00\\x00\\x39\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x45\\x00\\x00\\x00\"\n\"\\x09\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x3D\\x00\\x00\\x00\\x46\\x00\\x00\\x00\\x44\\x00\\x00\\x00\\x17\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x46\\x00\\x00\\x00\"\n\"\\x45\\x00\\x00\\x00\\xFD\\x00\\x01\\x00\\x38\\x00\\x01\\x00\\x36\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x08\\x00\\x00\\x00\"\n\"\\xF8\\x00\\x02\\x00\\x0A\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x18\\x00\\x00\\x00\"\n\"\\x19\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x17\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\x1A\\x00\\x00\\x00\\x19\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x0E\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x1C\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x1F\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x21\\x00\\x00\\x00\"\n\"\\x1D\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x50\\x00\\x07\\x00\\x07\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x1F\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x21\\x00\\x00\\x00\"\n\"\\x1E\\x00\\x00\\x00\\x91\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x1A\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x0C\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x26\\x00\\x00\\x00\\x27\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x25\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\"\n\"\\x28\\x00\\x00\\x00\\x27\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x29\\x00\\x00\\x00\\x28\\x00\\x00\\x00\\x24\\x00\\x00\\x00\\x41\\x00\\x05\\x00\"\n\"\\x26\\x00\\x00\\x00\\x2A\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x25\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x2A\\x00\\x00\\x00\\x29\\x00\\x00\\x00\\x41\\x00\\x05\\x00\"\n\"\\x26\\x00\\x00\\x00\\x2C\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x2B\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\\x2C\\x00\\x00\\x00\"\n\"\\x41\\x00\\x05\\x00\\x26\\x00\\x00\\x00\\x2F\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x2E\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x30\\x00\\x00\\x00\"\n\"\\x2F\\x00\\x00\\x00\\x81\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x31\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\\x30\\x00\\x00\\x00\\x88\\x00\\x05\\x00\\x06\\x00\\x00\\x00\"\n\"\\x33\\x00\\x00\\x00\\x31\\x00\\x00\\x00\\x32\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x26\\x00\\x00\\x00\\x34\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x2B\\x00\\x00\\x00\"\n\"\\x3E\\x00\\x03\\x00\\x34\\x00\\x00\\x00\\x33\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x35\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\xFE\\x00\\x02\\x00\"\n\"\\x35\\x00\\x00\\x00\\x38\\x00\\x01\\x00\\x03\\x02\\x23\\x07\\x00\\x00\\x01\\x00\\x0A\\x00\\x08\\x00\\x37\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x11\\x00\\x02\\x00\"\n\"\\x01\\x00\\x00\\x00\\x0B\\x00\\x06\\x00\\x01\\x00\\x00\\x00\\x47\\x4C\\x53\\x4C\\x2E\\x73\\x74\\x64\\x2E\\x34\\x35\\x30\\x00\\x00\\x00\\x00\\x0E\\x00\\x03\\x00\"\n\"\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x0F\\x00\\x08\\x00\\x04\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x6D\\x61\\x69\\x6E\\x00\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\"\n\"\\x26\\x00\\x00\\x00\\x2C\\x00\\x00\\x00\\x10\\x00\\x03\\x00\\x04\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x0E\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\x22\\x00\\x00\\x00\\x22\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x22\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x26\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x2C\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x30\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x31\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x31\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x31\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\"\n\"\\x31\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x31\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x31\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x31\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x31\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x31\\x00\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x31\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xC0\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x31\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xCC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x31\\x00\\x00\\x00\\x05\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\xD0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x31\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xDC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x31\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xE0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x31\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\xEC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x31\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xF0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x31\\x00\\x00\\x00\"\n\"\\x0A\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xFC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x31\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x01\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x31\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x31\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x4C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x31\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x50\\x01\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x31\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x5C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x31\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x60\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x31\\x00\\x00\\x00\\x11\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x6C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x31\\x00\\x00\\x00\"\n\"\\x12\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x70\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x31\\x00\\x00\\x00\\x13\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x01\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x31\\x00\\x00\\x00\\x14\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x90\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x31\\x00\\x00\\x00\\x15\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x94\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x31\\x00\\x00\\x00\\x16\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x98\\x01\\x00\\x00\\x47\\x00\\x03\\x00\"\n\"\\x31\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x33\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x33\\x00\\x00\\x00\"\n\"\\x21\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x34\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x34\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x34\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x34\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x34\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x4C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x34\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x50\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x34\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x5C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x34\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x60\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x34\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x6C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x34\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x70\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x34\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x34\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x88\\x00\\x00\\x00\\x47\\x00\\x03\\x00\\x34\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x36\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x36\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x13\\x00\\x02\\x00\\x02\\x00\\x00\\x00\\x21\\x00\\x03\\x00\\x03\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x16\\x00\\x03\\x00\\x06\\x00\\x00\\x00\\x20\\x00\\x00\\x00\"\n\"\\x17\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x08\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x20\\x00\\x04\\x00\\x0A\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x0A\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x15\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x32\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x00\\x01\\x00\\x00\"\n\"\\x2B\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x14\\x00\\x02\\x00\\x10\\x00\\x00\\x00\\x34\\x00\\x06\\x00\\x10\\x00\\x00\\x00\"\n\"\\x11\\x00\\x00\\x00\\xAB\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\\x15\\x00\\x04\\x00\\x14\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x2B\\x00\\x04\\x00\\x14\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x17\\x00\\x04\\x00\\x19\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x19\\x00\\x09\\x00\\x1F\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x1B\\x00\\x03\\x00\\x20\\x00\\x00\\x00\\x1F\\x00\\x00\\x00\"\n\"\\x20\\x00\\x04\\x00\\x21\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x21\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x17\\x00\\x04\\x00\\x24\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x25\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x24\\x00\\x00\\x00\"\n\"\\x3B\\x00\\x04\\x00\\x25\\x00\\x00\\x00\\x26\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x2B\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x3B\\x00\\x04\\x00\\x2B\\x00\\x00\\x00\\x2C\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x18\\x00\\x04\\x00\\x2E\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x04\\x00\\x00\\x00\"\n\"\\x2B\\x00\\x04\\x00\\x14\\x00\\x00\\x00\\x2F\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x1C\\x00\\x04\\x00\\x30\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x2F\\x00\\x00\\x00\"\n\"\\x1E\\x00\\x19\\x00\\x31\\x00\\x00\\x00\\x2E\\x00\\x00\\x00\\x2E\\x00\\x00\\x00\\x2E\\x00\\x00\\x00\\x19\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x19\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x19\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x19\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x30\\x00\\x00\\x00\\x19\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x19\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x19\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x24\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x32\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x31\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x32\\x00\\x00\\x00\\x33\\x00\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\x1E\\x00\\x0C\\x00\\x34\\x00\\x00\\x00\\x2E\\x00\\x00\\x00\\x19\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x19\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x19\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x24\\x00\\x00\\x00\\x24\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x35\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x34\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x35\\x00\\x00\\x00\\x36\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x36\\x00\\x05\\x00\\x02\\x00\\x00\\x00\\x04\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x05\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x08\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x09\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\xF7\\x00\\x03\\x00\"\n\"\\x13\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\\x11\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x13\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x12\\x00\\x00\\x00\"\n\"\\x41\\x00\\x05\\x00\\x16\\x00\\x00\\x00\\x17\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x18\\x00\\x00\\x00\"\n\"\\x17\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x1A\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x4F\\x00\\x08\\x00\\x19\\x00\\x00\\x00\\x1B\\x00\\x00\\x00\"\n\"\\x1A\\x00\\x00\\x00\\x1A\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x8E\\x00\\x05\\x00\\x19\\x00\\x00\\x00\\x1C\\x00\\x00\\x00\"\n\"\\x1B\\x00\\x00\\x00\\x18\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x4F\\x00\\x09\\x00\\x07\\x00\\x00\\x00\"\n\"\\x1E\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x1C\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\"\n\"\\x09\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\xF9\\x00\\x02\\x00\\x13\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x13\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x20\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x24\\x00\\x00\\x00\\x27\\x00\\x00\\x00\\x26\\x00\\x00\\x00\\x57\\x00\\x05\\x00\\x07\\x00\\x00\\x00\"\n\"\\x28\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x27\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x29\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x85\\x00\\x05\\x00\"\n\"\\x07\\x00\\x00\\x00\\x2A\\x00\\x00\\x00\\x29\\x00\\x00\\x00\\x28\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x09\\x00\\x00\\x00\\x2A\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x07\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x2C\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\\xFD\\x00\\x01\\x00\\x38\\x00\\x01\\x00\"\n\"\"},\n#endif\n#ifdef D3D11QUAKE\n{QR_DIRECT3D11, 11, \"default2d\",\n\"!!samps 1\\n\"\n\n\"struct a2v\\n\"\n\"{\\n\"\n\"float4 pos: POSITION;\\n\"\n\"float2 tc: TEXCOORD0;\\n\"\n\"float4 vcol: COLOR0;\\n\"\n\"};\\n\"\n\"struct v2f\\n\"\n\"{\\n\"\n\"float4 pos: SV_POSITION;\\n\"\n\"float2 tc: TEXCOORD0;\\n\"\n\"float4 vcol: COLOR0;\\n\"\n\"};\\n\"\n\n\"#include <ftedefs.h>\\n\"\n\n\"#ifdef VERTEX_SHADER\\n\"\n\"v2f main (a2v inp)\\n\"\n\"{\\n\"\n\"v2f outp;\\n\"\n\"outp.pos = mul(m_projection, inp.pos);\\n\"\n\"outp.tc = inp.tc;\\n\"\n\"outp.vcol = inp.vcol;\\n\"\n\"return outp;\\n\"\n\"}\\n\"\n\"#endif\\n\"\n\n\"#ifdef FRAGMENT_SHADER\\n\"\n\"Texture2D t_t0;\\n\"\n\"SamplerState s_t0;\\n\"\n\"float4 main (v2f inp) : SV_TARGET\\n\"\n\"{\\n\"\n\"#ifdef PREMUL\\n\"\n\"inp.vcol.rgb *= inp.vcol.a;\\n\"\n\"#endif\\n\"\n\"return t_t0.Sample(s_t0, inp.tc) * inp.vcol;\\n\"\n\"}\\n\"\n\"#endif\\n\"\n\n},\n#endif\n#ifdef GLQUAKE\n{QR_OPENGL, 110, \"default2danim\",\n\"!!ver 300-450\\n\"\n\"!!samps anim:2DArray=0\\n\"\n\n\"#include \\\"sys/defs.h\\\"\\n\"\n\n//this shader is present for support for gles/gl3core contexts\n//it is single-texture-with-vertex-colours, and doesn't do anything special.\n//beware that a few things use this, including apparently fonts and bloom rescaling.\n//its really not meant to do anything special.\n\n\"varying vec2 tc;\\n\"\n\"varying vec4 vc;\\n\"\n\n\"#ifdef VERTEX_SHADER\\n\"\n\"void main ()\\n\"\n\"{\\n\"\n\"tc = v_texcoord;\\n\"\n\"vc = v_colour;\\n\"\n\"gl_Position = ftetransform();\\n\"\n\"}\\n\"\n\"#endif\\n\"\n\"#ifdef FRAGMENT_SHADER\\n\"\n\"void main ()\\n\"\n\"{\\n\"\n//figure out which frame to use.\n\"ivec3 sz = textureSize(s_anim, 0);\\n\"\n\"float layer = mod(e_time*10, sz.z-1);\\n\"\n\n\"vec4 f = vc;\\n\"\n\"#ifdef PREMUL\\n\"\n\"f.rgb *= f.a;\\n\"\n\"#endif\\n\"\n\"f *= texture(s_anim, vec3(tc, layer));\\n\"\n\"gl_FragColor = f;\\n\"\n\"}\\n\"\n\"#endif\\n\"\n},\n#endif\n#ifdef GLQUAKE\n{QR_OPENGL, 110, \"defaultadditivesprite\",\n\"!!permu FOG\\n\"\n\"!!samps 1\\n\"\n\n//meant to be used for additive stuff. presumably particles and sprites. though actually its only flashblend effects that use this at the time of writing.\n//includes fog, apparently.\n\n\"#include \\\"sys/fog.h\\\"\\n\"\n\"#ifdef VERTEX_SHADER\\n\"\n\"attribute vec2 v_texcoord;\\n\"\n\"attribute vec4 v_colour;\\n\"\n\"varying vec2 tc;\\n\"\n\"varying vec4 vc;\\n\"\n\"void main ()\\n\"\n\"{\\n\"\n\"tc = v_texcoord;\\n\"\n\"vc = v_colour;\\n\"\n\"gl_Position = ftetransform();\\n\"\n\"}\\n\"\n\"#endif\\n\"\n\"#ifdef FRAGMENT_SHADER\\n\"\n\"varying vec2 tc;\\n\"\n\"varying vec4 vc;\\n\"\n\"uniform vec4 e_colourident;\\n\"\n\"void main ()\\n\"\n\"{\\n\"\n\"gl_FragColor = fog4additive(texture2D(s_t0, tc) * vc * e_colourident);\\n\"\n\"}\\n\"\n\"#endif\\n\"\n},\n#endif\n#ifdef VKQUAKE\n{QR_VULKAN, -1, \"defaultadditivesprite\",\n\"\\xFF\\x53\\x50\\x56\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x2C\\x00\\x00\\x00\\x35\\x00\\x00\\x00\\x64\\x00\\x00\\x00\"\n\"\\xCC\\x0A\\x00\\x00\\x30\\x0B\\x00\\x00\\x78\\x0F\\x00\\x00\\x01\\x00\\x46\\x31\\x4D\\x41\\x53\\x4B\\x00\\x00\\x00\\x00\\x00\\x01\\x01\\x62\\x31\\x72\\x5F\\x66\"\n\"\\x6F\\x67\\x5F\\x65\\x78\\x70\\x32\\x00\\x00\\x00\\x00\\x00\\x01\\x02\\x62\\x31\\x72\\x5F\\x66\\x6F\\x67\\x5F\\x6C\\x69\\x6E\\x65\\x61\\x72\\x00\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x03\\x02\\x23\\x07\\x00\\x00\\x01\\x00\\x0A\\x00\\x08\\x00\\x52\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x11\\x00\\x02\\x00\\x01\\x00\\x00\\x00\"\n\"\\x0B\\x00\\x06\\x00\\x01\\x00\\x00\\x00\\x47\\x4C\\x53\\x4C\\x2E\\x73\\x74\\x64\\x2E\\x34\\x35\\x30\\x00\\x00\\x00\\x00\\x0E\\x00\\x03\\x00\\x00\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x0F\\x00\\x0F\\x00\\x00\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x6D\\x61\\x69\\x6E\\x00\\x00\\x00\\x00\\x1C\\x00\\x00\\x00\\x39\\x00\\x00\\x00\"\n\"\\x3B\\x00\\x00\\x00\\x3E\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x44\\x00\\x00\\x00\\x4E\\x00\\x00\\x00\\x4F\\x00\\x00\\x00\\x50\\x00\\x00\\x00\\x51\\x00\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x11\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x13\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x05\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x13\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\"\n\"\\x48\\x00\\x04\\x00\\x13\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x80\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\"\n\"\\x03\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xC0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xCC\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xD0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\xDC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xE0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x13\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xEC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\xF0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x0A\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xFC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\"\n\"\\x0B\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x01\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x4C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x50\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x5C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x13\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x60\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x11\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x6C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x70\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\"\n\"\\x13\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x14\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x90\\x01\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x94\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x16\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x98\\x01\\x00\\x00\\x47\\x00\\x03\\x00\\x13\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x15\\x00\\x00\\x00\\x22\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x15\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x1C\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x39\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x3B\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x3E\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x40\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x42\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x03\\x00\\x42\\x00\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x47\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\x48\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x01\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\x49\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x02\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\x4A\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x15\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x4B\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x4B\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x4B\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x4B\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x4B\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x4C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x4B\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x50\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x4B\\x00\\x00\\x00\"\n\"\\x04\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x5C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x4B\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x60\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x4B\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x6C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x4B\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x70\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x4B\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x4B\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x88\\x00\\x00\\x00\\x47\\x00\\x03\\x00\\x4B\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\"\n\"\\x4D\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x4D\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x47\\x00\\x04\\x00\"\n\"\\x4E\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x4F\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x47\\x00\\x04\\x00\"\n\"\\x50\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x51\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x13\\x00\\x02\\x00\"\n\"\\x02\\x00\\x00\\x00\\x21\\x00\\x03\\x00\\x03\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x16\\x00\\x03\\x00\\x06\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x17\\x00\\x04\\x00\"\n\"\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x21\\x00\\x03\\x00\\x08\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x18\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x17\\x00\\x04\\x00\\x0E\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x15\\x00\\x04\\x00\\x0F\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x0F\\x00\\x00\\x00\"\n\"\\x10\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x1C\\x00\\x04\\x00\\x11\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x17\\x00\\x04\\x00\\x12\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x1E\\x00\\x19\\x00\\x13\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x11\\x00\\x00\\x00\"\n\"\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x14\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x13\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\"\n\"\\x14\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x15\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\"\n\"\\x16\\x00\\x00\\x00\\x17\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x18\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x20\\x00\\x04\\x00\"\n\"\\x1B\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\\x1C\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\"\n\"\\x06\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x00\\x00\\x80\\x3F\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x24\\x00\\x00\\x00\\x00\\x00\\x80\\xBF\\x2B\\x00\\x04\\x00\"\n\"\\x0F\\x00\\x00\\x00\\x25\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x26\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\"\n\"\\x0F\\x00\\x00\\x00\\x2B\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x0F\\x00\\x00\\x00\\x2E\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\"\n\"\\x06\\x00\\x00\\x00\\x32\\x00\\x00\\x00\\x00\\x00\\x00\\x40\\x20\\x00\\x04\\x00\\x38\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\"\n\"\\x38\\x00\\x00\\x00\\x39\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x3A\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\"\n\"\\x3A\\x00\\x00\\x00\\x3B\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x3D\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\"\n\"\\x3D\\x00\\x00\\x00\\x3E\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x3F\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\"\n\"\\x3F\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x1E\\x00\\x03\\x00\\x42\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x43\\x00\\x00\\x00\"\n\"\\x03\\x00\\x00\\x00\\x42\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x43\\x00\\x00\\x00\\x44\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x32\\x00\\x04\\x00\\x06\\x00\\x00\\x00\"\n\"\\x47\\x00\\x00\\x00\\x00\\x00\\x80\\x43\\x32\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x48\\x00\\x00\\x00\\x01\\x01\\x00\\x00\\x32\\x00\\x04\\x00\\x16\\x00\\x00\\x00\"\n\"\\x49\\x00\\x00\\x00\\x02\\x01\\x00\\x00\\x32\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x4A\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x1E\\x00\\x0C\\x00\\x4B\\x00\\x00\\x00\"\n\"\\x0D\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x12\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x4C\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x4B\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x4C\\x00\\x00\\x00\"\n\"\\x4D\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x3A\\x00\\x00\\x00\\x4E\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\"\n\"\\x4F\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\\x50\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\"\n\"\\x51\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x36\\x00\\x05\\x00\\x02\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\"\n\"\\x05\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x12\\x00\\x00\\x00\\x3C\\x00\\x00\\x00\\x3B\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x39\\x00\\x00\\x00\\x3C\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x41\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x3E\\x00\\x00\\x00\\x41\\x00\\x00\\x00\\x39\\x00\\x04\\x00\"\n\"\\x07\\x00\\x00\\x00\\x45\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x3D\\x00\\x00\\x00\\x46\\x00\\x00\\x00\\x44\\x00\\x00\\x00\\x17\\x00\\x00\\x00\"\n\"\\x3E\\x00\\x03\\x00\\x46\\x00\\x00\\x00\\x45\\x00\\x00\\x00\\xFD\\x00\\x01\\x00\\x38\\x00\\x01\\x00\\x36\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x09\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x0A\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x41\\x00\\x05\\x00\\x18\\x00\\x00\\x00\\x19\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x17\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\x1A\\x00\\x00\\x00\"\n\"\\x19\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0E\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x1C\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x1F\\x00\\x00\\x00\"\n\"\\x1D\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x51\\x00\\x05\\x00\"\n\"\\x06\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x50\\x00\\x07\\x00\\x07\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x1F\\x00\\x00\\x00\"\n\"\\x20\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x91\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x1A\\x00\\x00\\x00\\x22\\x00\\x00\\x00\"\n\"\\x3E\\x00\\x03\\x00\\x0C\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x26\\x00\\x00\\x00\\x27\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x25\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x28\\x00\\x00\\x00\\x27\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x29\\x00\\x00\\x00\\x28\\x00\\x00\\x00\"\n\"\\x24\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x26\\x00\\x00\\x00\\x2A\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x25\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x2A\\x00\\x00\\x00\"\n\"\\x29\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x26\\x00\\x00\\x00\\x2C\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x2B\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\"\n\"\\x2D\\x00\\x00\\x00\\x2C\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x26\\x00\\x00\\x00\\x2F\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x2E\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x06\\x00\\x00\\x00\\x30\\x00\\x00\\x00\\x2F\\x00\\x00\\x00\\x81\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x31\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\\x30\\x00\\x00\\x00\"\n\"\\x88\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x33\\x00\\x00\\x00\\x31\\x00\\x00\\x00\\x32\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x26\\x00\\x00\\x00\\x34\\x00\\x00\\x00\"\n\"\\x0C\\x00\\x00\\x00\\x2B\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x34\\x00\\x00\\x00\\x33\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x35\\x00\\x00\\x00\"\n\"\\x0C\\x00\\x00\\x00\\xFE\\x00\\x02\\x00\\x35\\x00\\x00\\x00\\x38\\x00\\x01\\x00\\x03\\x02\\x23\\x07\\x00\\x00\\x01\\x00\\x0A\\x00\\x08\\x00\\x91\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x11\\x00\\x02\\x00\\x01\\x00\\x00\\x00\\x0B\\x00\\x06\\x00\\x01\\x00\\x00\\x00\\x47\\x4C\\x53\\x4C\\x2E\\x73\\x74\\x64\\x2E\\x34\\x35\\x30\"\n\"\\x00\\x00\\x00\\x00\\x0E\\x00\\x03\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x0F\\x00\\x09\\x00\\x04\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x6D\\x61\\x69\\x6E\"\n\"\\x00\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x73\\x00\\x00\\x00\\x82\\x00\\x00\\x00\\x84\\x00\\x00\\x00\\x10\\x00\\x03\\x00\\x04\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x0E\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x17\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x02\\x01\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x2C\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x10\\x00\\x00\\x00\"\n\"\\x48\\x00\\x04\\x00\\x2E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x2E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x2E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x2E\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x2E\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x2E\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x2E\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x05\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x2E\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x2E\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x2E\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xC0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x2E\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xCC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x2E\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\xD0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x2E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xDC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x2E\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xE0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x2E\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xEC\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x2E\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xF0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x2E\\x00\\x00\\x00\\x0A\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\xFC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x2E\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x2E\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x2E\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x4C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x2E\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x50\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x2E\\x00\\x00\\x00\"\n\"\\x0F\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x5C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x2E\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x60\\x01\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x2E\\x00\\x00\\x00\\x11\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x6C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x2E\\x00\\x00\\x00\\x12\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x70\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x2E\\x00\\x00\\x00\\x13\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x01\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x2E\\x00\\x00\\x00\\x14\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x90\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x2E\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x94\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x2E\\x00\\x00\\x00\\x16\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x98\\x01\\x00\\x00\\x47\\x00\\x03\\x00\\x2E\\x00\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x30\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x30\\x00\\x00\\x00\\x21\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x4D\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x01\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\x70\\x00\\x00\\x00\\x22\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x70\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x73\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x76\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\x82\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x84\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x8E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x8E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x8E\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x8E\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x8E\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x4C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x8E\\x00\\x00\\x00\\x03\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x50\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x8E\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x5C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x8E\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x60\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x8E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x6C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x8E\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x70\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x8E\\x00\\x00\\x00\"\n\"\\x08\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x8E\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x88\\x00\\x00\\x00\"\n\"\\x47\\x00\\x03\\x00\\x8E\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x90\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\"\n\"\\x90\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x13\\x00\\x02\\x00\\x02\\x00\\x00\\x00\\x21\\x00\\x03\\x00\\x03\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x16\\x00\\x03\\x00\\x06\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x17\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x20\\x00\\x04\\x00\"\n\"\\x08\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x21\\x00\\x04\\x00\\x09\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\x15\\x00\\x04\\x00\"\n\"\\x0D\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x32\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\"\n\"\\x0D\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x14\\x00\\x02\\x00\\x10\\x00\\x00\\x00\\x34\\x00\\x06\\x00\\x10\\x00\\x00\\x00\\x11\\x00\\x00\\x00\"\n\"\\xAB\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\\x34\\x00\\x05\\x00\\x10\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\xA8\\x00\\x00\\x00\\x11\\x00\\x00\\x00\"\n\"\\x32\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\x17\\x00\\x00\\x00\\x02\\x01\\x00\\x00\\x34\\x00\\x06\\x00\\x10\\x00\\x00\\x00\\x18\\x00\\x00\\x00\\xAB\\x00\\x00\\x00\"\n\"\\x17\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x1D\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x1D\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x15\\x00\\x04\\x00\\x1F\\x00\\x00\\x00\"\n\"\\x20\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x1F\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x21\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x1F\\x00\\x00\\x00\\x24\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x18\\x00\\x04\\x00\\x29\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x17\\x00\\x04\\x00\\x2A\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x1F\\x00\\x00\\x00\"\n\"\\x2B\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x1C\\x00\\x04\\x00\\x2C\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x2B\\x00\\x00\\x00\\x17\\x00\\x04\\x00\\x2D\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x1E\\x00\\x19\\x00\\x2E\\x00\\x00\\x00\\x29\\x00\\x00\\x00\\x29\\x00\\x00\\x00\\x29\\x00\\x00\\x00\\x2A\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x2A\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x2A\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x2A\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x2C\\x00\\x00\\x00\"\n\"\\x2A\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x2A\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x2A\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x2F\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x2E\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\"\n\"\\x2F\\x00\\x00\\x00\\x30\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\x31\\x00\\x00\\x00\\x14\\x00\\x00\\x00\\x20\\x00\\x04\\x00\"\n\"\\x32\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\x39\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\"\n\"\\x06\\x00\\x00\\x00\\x47\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x32\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\x4D\\x00\\x00\\x00\\x01\\x01\\x00\\x00\\x34\\x00\\x06\\x00\"\n\"\\x10\\x00\\x00\\x00\\x4E\\x00\\x00\\x00\\xAB\\x00\\x00\\x00\\x4D\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x55\\x00\\x00\\x00\"\n\"\\x3B\\xAA\\xB8\\x3F\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x59\\x00\\x00\\x00\\x00\\x00\\x80\\x3F\\x2B\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\x5A\\x00\\x00\\x00\"\n\"\\x13\\x00\\x00\\x00\\x19\\x00\\x09\\x00\\x6D\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x1B\\x00\\x03\\x00\\x6E\\x00\\x00\\x00\\x6D\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x6F\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x6E\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x6F\\x00\\x00\\x00\\x70\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x72\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x2D\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x72\\x00\\x00\\x00\\x73\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x32\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x76\\x00\\x00\\x00\"\n\"\\x00\\x00\\x80\\x43\\x20\\x00\\x04\\x00\\x81\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x81\\x00\\x00\\x00\\x82\\x00\\x00\\x00\"\n\"\\x03\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x1D\\x00\\x00\\x00\\x84\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\x87\\x00\\x00\\x00\"\n\"\\x12\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x88\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x1E\\x00\\x0C\\x00\\x8E\\x00\\x00\\x00\\x29\\x00\\x00\\x00\"\n\"\\x2A\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x2A\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x2A\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\"\n\"\\x2D\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x8F\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x8E\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x8F\\x00\\x00\\x00\\x90\\x00\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\x36\\x00\\x05\\x00\\x02\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x05\\x00\\x00\\x00\"\n\"\\x3B\\x00\\x04\\x00\\x08\\x00\\x00\\x00\\x6C\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x08\\x00\\x00\\x00\\x8C\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x6E\\x00\\x00\\x00\\x71\\x00\\x00\\x00\\x70\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x2D\\x00\\x00\\x00\\x74\\x00\\x00\\x00\\x73\\x00\\x00\\x00\"\n\"\\x57\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x75\\x00\\x00\\x00\\x71\\x00\\x00\\x00\\x74\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x6C\\x00\\x00\\x00\\x75\\x00\\x00\\x00\"\n\"\\xB7\\x00\\x05\\x00\\x10\\x00\\x00\\x00\\x77\\x00\\x00\\x00\\x76\\x00\\x00\\x00\\x47\\x00\\x00\\x00\\xF7\\x00\\x03\\x00\\x79\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\xFA\\x00\\x04\\x00\\x77\\x00\\x00\\x00\\x78\\x00\\x00\\x00\\x79\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x78\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x1B\\x00\\x00\\x00\"\n\"\\x7A\\x00\\x00\\x00\\x6C\\x00\\x00\\x00\\x24\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x7B\\x00\\x00\\x00\\x7A\\x00\\x00\\x00\\xB8\\x00\\x05\\x00\"\n\"\\x10\\x00\\x00\\x00\\x7C\\x00\\x00\\x00\\x7B\\x00\\x00\\x00\\x76\\x00\\x00\\x00\\xF9\\x00\\x02\\x00\\x79\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x79\\x00\\x00\\x00\"\n\"\\xF5\\x00\\x07\\x00\\x10\\x00\\x00\\x00\\x7D\\x00\\x00\\x00\\x77\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x7C\\x00\\x00\\x00\\x78\\x00\\x00\\x00\\xF7\\x00\\x03\\x00\"\n\"\\x7F\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\\x7D\\x00\\x00\\x00\\x7E\\x00\\x00\\x00\\x7F\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x7E\\x00\\x00\\x00\"\n\"\\xFC\\x00\\x01\\x00\\xF8\\x00\\x02\\x00\\x7F\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x83\\x00\\x00\\x00\\x6C\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x07\\x00\\x00\\x00\\x85\\x00\\x00\\x00\\x84\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x86\\x00\\x00\\x00\\x83\\x00\\x00\\x00\\x85\\x00\\x00\\x00\"\n\"\\x41\\x00\\x05\\x00\\x88\\x00\\x00\\x00\\x89\\x00\\x00\\x00\\x30\\x00\\x00\\x00\\x87\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x8A\\x00\\x00\\x00\"\n\"\\x89\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x8B\\x00\\x00\\x00\\x86\\x00\\x00\\x00\\x8A\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x8C\\x00\\x00\\x00\"\n\"\\x8B\\x00\\x00\\x00\\x39\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x8D\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x8C\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x82\\x00\\x00\\x00\"\n\"\\x8D\\x00\\x00\\x00\\xFD\\x00\\x01\\x00\\x38\\x00\\x01\\x00\\x36\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x09\\x00\\x00\\x00\"\n\"\\x37\\x00\\x03\\x00\\x08\\x00\\x00\\x00\\x0A\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x0C\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\\x1C\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\\x28\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\xF7\\x00\\x03\\x00\\x14\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\xFA\\x00\\x04\\x00\\x12\\x00\\x00\\x00\\x13\\x00\\x00\\x00\\x14\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x13\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\"\n\"\\x15\\x00\\x00\\x00\\x0A\\x00\\x00\\x00\\xFE\\x00\\x02\\x00\\x15\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x14\\x00\\x00\\x00\\xF7\\x00\\x03\\x00\\x1A\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\\x18\\x00\\x00\\x00\\x19\\x00\\x00\\x00\\x3E\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x19\\x00\\x00\\x00\\x41\\x00\\x05\\x00\"\n\"\\x21\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x22\\x00\\x00\\x00\"\n\"\\x41\\x00\\x05\\x00\\x21\\x00\\x00\\x00\\x25\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x24\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x26\\x00\\x00\\x00\"\n\"\\x25\\x00\\x00\\x00\\x88\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x27\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x26\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x1C\\x00\\x00\\x00\"\n\"\\x27\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x32\\x00\\x00\\x00\\x33\\x00\\x00\\x00\\x30\\x00\\x00\\x00\\x31\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\"\n\"\\x34\\x00\\x00\\x00\\x33\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x35\\x00\\x00\\x00\\x1C\\x00\\x00\\x00\\x83\\x00\\x05\\x00\\x06\\x00\\x00\\x00\"\n\"\\x36\\x00\\x00\\x00\\x34\\x00\\x00\\x00\\x35\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x32\\x00\\x00\\x00\\x37\\x00\\x00\\x00\\x30\\x00\\x00\\x00\\x31\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x38\\x00\\x00\\x00\\x37\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x32\\x00\\x00\\x00\\x3A\\x00\\x00\\x00\\x30\\x00\\x00\\x00\"\n\"\\x39\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x3B\\x00\\x00\\x00\\x3A\\x00\\x00\\x00\\x83\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x3C\\x00\\x00\\x00\"\n\"\\x38\\x00\\x00\\x00\\x3B\\x00\\x00\\x00\\x88\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x3D\\x00\\x00\\x00\\x36\\x00\\x00\\x00\\x3C\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\"\n\"\\x28\\x00\\x00\\x00\\x3D\\x00\\x00\\x00\\xF9\\x00\\x02\\x00\\x1A\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x3E\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x32\\x00\\x00\\x00\"\n\"\\x3F\\x00\\x00\\x00\\x30\\x00\\x00\\x00\\x31\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x3F\\x00\\x00\\x00\\x41\\x00\\x05\\x00\"\n\"\\x21\\x00\\x00\\x00\\x41\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x42\\x00\\x00\\x00\\x41\\x00\\x00\\x00\"\n\"\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x43\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x42\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x21\\x00\\x00\\x00\\x44\\x00\\x00\\x00\"\n\"\\x1E\\x00\\x00\\x00\\x24\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x45\\x00\\x00\\x00\\x44\\x00\\x00\\x00\\x88\\x00\\x05\\x00\\x06\\x00\\x00\\x00\"\n\"\\x46\\x00\\x00\\x00\\x43\\x00\\x00\\x00\\x45\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x1C\\x00\\x00\\x00\\x46\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\"\n\"\\x48\\x00\\x00\\x00\\x1C\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x32\\x00\\x00\\x00\\x49\\x00\\x00\\x00\\x30\\x00\\x00\\x00\\x39\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x06\\x00\\x00\\x00\\x4A\\x00\\x00\\x00\\x49\\x00\\x00\\x00\\x83\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x4B\\x00\\x00\\x00\\x48\\x00\\x00\\x00\\x4A\\x00\\x00\\x00\"\n\"\\x0C\\x00\\x07\\x00\\x06\\x00\\x00\\x00\\x4C\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x28\\x00\\x00\\x00\\x47\\x00\\x00\\x00\\x4B\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\"\n\"\\x1C\\x00\\x00\\x00\\x4C\\x00\\x00\\x00\\xF7\\x00\\x03\\x00\\x50\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\\x4E\\x00\\x00\\x00\\x4F\\x00\\x00\\x00\"\n\"\\x50\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x4F\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x51\\x00\\x00\\x00\\x1C\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x06\\x00\\x00\\x00\\x52\\x00\\x00\\x00\\x1C\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x53\\x00\\x00\\x00\\x52\\x00\\x00\\x00\\x51\\x00\\x00\\x00\"\n\"\\x3E\\x00\\x03\\x00\\x1C\\x00\\x00\\x00\\x53\\x00\\x00\\x00\\xF9\\x00\\x02\\x00\\x50\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x50\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x06\\x00\\x00\\x00\\x54\\x00\\x00\\x00\\x1C\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x56\\x00\\x00\\x00\\x54\\x00\\x00\\x00\\x55\\x00\\x00\\x00\"\n\"\\x7F\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x57\\x00\\x00\\x00\\x56\\x00\\x00\\x00\\x0C\\x00\\x06\\x00\\x06\\x00\\x00\\x00\\x58\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x1D\\x00\\x00\\x00\\x57\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x28\\x00\\x00\\x00\\x58\\x00\\x00\\x00\\xF9\\x00\\x02\\x00\\x1A\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\"\n\"\\x1A\\x00\\x00\\x00\\x41\\x00\\x06\\x00\\x32\\x00\\x00\\x00\\x5B\\x00\\x00\\x00\\x30\\x00\\x00\\x00\\x5A\\x00\\x00\\x00\\x24\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x06\\x00\\x00\\x00\\x5C\\x00\\x00\\x00\\x5B\\x00\\x00\\x00\\x83\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x5D\\x00\\x00\\x00\\x59\\x00\\x00\\x00\\x5C\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x5E\\x00\\x00\\x00\\x28\\x00\\x00\\x00\\x0C\\x00\\x08\\x00\\x06\\x00\\x00\\x00\\x5F\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x2B\\x00\\x00\\x00\\x5E\\x00\\x00\\x00\\x47\\x00\\x00\\x00\\x59\\x00\\x00\\x00\\x41\\x00\\x06\\x00\\x32\\x00\\x00\\x00\\x60\\x00\\x00\\x00\\x30\\x00\\x00\\x00\"\n\"\\x5A\\x00\\x00\\x00\\x24\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x61\\x00\\x00\\x00\\x60\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\"\n\"\\x62\\x00\\x00\\x00\\x5F\\x00\\x00\\x00\\x61\\x00\\x00\\x00\\x81\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x63\\x00\\x00\\x00\\x5D\\x00\\x00\\x00\\x62\\x00\\x00\\x00\"\n\"\\x3E\\x00\\x03\\x00\\x28\\x00\\x00\\x00\\x63\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x64\\x00\\x00\\x00\\x0A\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x06\\x00\\x00\\x00\\x65\\x00\\x00\\x00\\x28\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x66\\x00\\x00\\x00\\x28\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x06\\x00\\x00\\x00\\x67\\x00\\x00\\x00\\x28\\x00\\x00\\x00\\x50\\x00\\x07\\x00\\x07\\x00\\x00\\x00\\x68\\x00\\x00\\x00\\x65\\x00\\x00\\x00\\x66\\x00\\x00\\x00\"\n\"\\x67\\x00\\x00\\x00\\x59\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x69\\x00\\x00\\x00\\x64\\x00\\x00\\x00\\x68\\x00\\x00\\x00\\xFE\\x00\\x02\\x00\"\n\"\\x69\\x00\\x00\\x00\\x38\\x00\\x01\\x00\"},\n#endif\n#ifdef GLQUAKE\n{QR_OPENGL, 110, \"defaultskin\",\n\"!!ver 100 150\\n\"\n\"!!permu TESS\\n\"\n\"!!permu FULLBRIGHT\\n\"\n\"!!permu UPPERLOWER\\n\"\n\"!!permu FRAMEBLEND\\n\"\n\"!!permu SKELETAL\\n\"\n\"!!permu FOG\\n\"\n\"!!permu BUMP\\n\"\n\"!!permu REFLECTCUBEMASK\\n\"\n\"!!cvarf r_glsl_offsetmapping_scale\\n\"\n\"!!cvarf gl_specular\\n\"\n\"!!cvardf gl_affinemodels=0\\n\"\n\"!!cvardf r_tessellation_level=5\\n\"\n\"!!samps !EIGHTBIT diffuse normalmap specular fullbright upper lower reflectmask reflectcube\\n\"\n\"!!samps =EIGHTBIT paletted 1\\n\"\n\"!!samps =OCCLUDE occlusion\\n\"\n\"!!samps =USE_TRANSMISSION transmission //only .r valid, multiplier for factor_transmission\\n\"\n\"!!samps =USE_VOLUME thickness   //only .g valid, multiplier for factor_volume_thickness, combined with factor_volume_rgb+factor_volume_distance(average distance travelled in metres)\\n\"\n//!!permu VC\t\t\t// adds rgba vertex colour multipliers\n//!!permu SPECULAR\t\t// auto-added when gl_specular>0\n//!!permu OFFSETMAPPING\t// auto-added when r_glsl_offsetmapping is set\n//!!permu NONORMALS\t\t// states that there's no normals available, which affects lighting.\n//!!permu ORM\t\t\t// specularmap is r:Occlusion, g:Roughness, b:Metalness\n//!!permu SG\t\t\t// specularmap is rgb:F0, a:Roughness (instead of exponent)\n//!!permu PBR\t\t\t// an attempt at pbr logic (enabled from ORM or SG)\n//!!permu NOOCCLUDE\t\t// ignores the use of ORM's occlusion... yeah, stupid.\n//!!permu OCCLUDE\t\t// use an explicit occlusion texturemap (separate from roughness+metalness).\n//!!permu EIGHTBIT\t\t// uses software-style paletted colourmap lookups\n//!!permu ALPHATEST\t\t// if defined, this is the required alpha level (more versatile than doing it at the q3shader level)\n\n\"#include \\\"sys/defs.h\\\"\\n\"\n\n//standard shader used for models.\n//must support skeletal and 2-way vertex blending or Bad Things Will Happen.\n//the vertex shader is responsible for calculating lighting values.\n\n\"#if gl_affinemodels==1 && __VERSION__ >= 130 && !defined(GL_ES)\\n\"\n\"#define affine noperspective\\n\"\n\"#else\\n\"\n\"#define affine\\n\"\n\"#endif\\n\"\n\n\"#if defined(ORM) || defined(SG)\\n\"\n\"#define PBR\\n\"\n\"#endif\\n\"\n\n\"#ifdef NONORMALS //lots of things need normals to work properly. make sure nothing breaks simply because they added an extra texture.\\n\"\n\"#undef BUMP\\n\"\n\"#undef SPECULAR\\n\"\n\"#undef OFFSETMAPPING\\n\"\n\"#undef REFLECTCUBEMASK\\n\"\n\"#endif\\n\"\n\n\n\n\n\"#ifdef VERTEX_SHADER\\n\"\n\"#include \\\"sys/skeletal.h\\\"\\n\"\n\n\"affine varying vec2 tc;\\n\"\n\"varying vec4 light;\\n\"\n\"#if defined(SPECULAR) || defined(OFFSETMAPPING) || defined(REFLECTCUBEMASK) || defined(PBR)\\n\"\n\"varying vec3 eyevector;\\n\"\n\"#endif\\n\"\n\"#if defined(PBR)||defined(REFLECTCUBEMASK)\\n\"\n\"varying mat3 invsurface;\\n\"\n\"#endif\\n\"\n\"#ifdef TESS\\n\"\n\"varying vec3 vertex;\\n\"\n\"varying vec3 normal;\\n\"\n\"#endif\\n\"\n\n\"void main ()\\n\"\n\"{\\n\"\n\"light.rgba = vec4(e_light_ambient, 1.0);\\n\"\n\n\"#ifdef NONORMALS\\n\"\n\"vec3 n, w;\\n\"\n\"gl_Position = skeletaltransform_w(w);\\n\"\n\"n = vec3(0.0);\\n\"\n\"#else\\n\"\n\"vec3 n, s, t, w;\\n\"\n\"gl_Position = skeletaltransform_wnst(w,n,s,t);\\n\"\n\"n = normalize(n);\\n\"\n\"s = normalize(s);\\n\"\n\"t = normalize(t);\\n\"\n\"#ifndef PBR\\n\"\n\"#ifdef EIGHTBIT\\n\"\n//doesn't darken in the shade, only gets brighter in the light (overbrighting)\n\"light.rgb += max(0.0,dot(n,e_light_dir)) * e_light_mul;\\n\"\n\"#else\\n\"\n//_DOES_ get darker in the shade, despite the light not lighting it at all....\n\"float d = dot(n,e_light_dir);\\n\"\n\"if (d < 0.0)\\n\"\n\"d *= 13.0/44.0; //a wtfery factor to approximate glquake's anorm_dots.h\\n\"\n\"light.rgb += d * e_light_mul;\\n\"\n\"#endif\\n\"\n\"#else\\n\"\n\"light.rgb = vec3(1.0);\\n\"\n\"#endif\\n\"\n\"#endif\\n\"\n\n\"#if defined(SPECULAR)||defined(OFFSETMAPPING) || defined(REFLECTCUBEMASK) || defined(PBR)\\n\"\n\"vec3 eyeminusvertex = e_eyepos - w.xyz;\\n\"\n\"eyevector.x = dot(eyeminusvertex, s.xyz);\\n\"\n\"eyevector.y = dot(eyeminusvertex, t.xyz);\\n\"\n\"eyevector.z = dot(eyeminusvertex, n.xyz);\\n\"\n\"#endif\\n\"\n\"#if defined(PBR) || defined(REFLECTCUBEMASK)\\n\"\n\"invsurface = mat3(s, t, n);\\n\"\n\"#endif\\n\"\n\n\"tc = v_texcoord;\\n\"\n\n\"#ifdef VC\\n\"\n\"light *= v_colour;\\n\"\n\"#endif\\n\"\n\n//FIXME: Software rendering imitation should possibly push out normals by half a pixel or something to approximate software's over-estimation of distant model sizes (small models are drawn using JUST their verticies using the nearest pixel, which results in larger meshes)\n\n\"#ifdef TESS\\n\"\n\"normal = n;\\n\"\n\"vertex = w;\\n\"\n\"#endif\\n\"\n\"}\\n\"\n\"#endif\\n\"\n\n\n\n\n\n\n\n\n\n\n\"#if defined(TESS_CONTROL_SHADER)\\n\"\n\"layout(vertices = 3) out;\\n\"\n\n\"in vec3 vertex[];\\n\"\n\"out vec3 t_vertex[];\\n\"\n\"in vec3 normal[];\\n\"\n\"out vec3 t_normal[];\\n\"\n\"affine in vec2 tc[];\\n\"\n\"affine out vec2 t_tc[];\\n\"\n\"in vec4 light[];\\n\"\n\"out vec4 t_light[];\\n\"\n\"#if defined(SPECULAR) || defined(OFFSETMAPPING) || defined(REFLECTCUBEMASK) || defined(PBR)\\n\"\n\"in vec3 eyevector[];\\n\"\n\"out vec3 t_eyevector[];\\n\"\n\"#endif\\n\"\n\"#ifdef REFLECTCUBEMASK\\n\"\n\"in mat3 invsurface[];\\n\"\n\"out mat3 t_invsurface[];\\n\"\n\"#endif\\n\"\n\"void main()\\n\"\n\"{\\n\"\n//the control shader needs to pass stuff through\n\"#define id gl_InvocationID\\n\"\n\"t_vertex[id] = vertex[id];\\n\"\n\"t_normal[id] = normal[id];\\n\"\n\"t_tc[id] = tc[id];\\n\"\n\"t_light[id] = light[id];\\n\"\n\"#if defined(SPECULAR) || defined(OFFSETMAPPING) || defined(REFLECTCUBEMASK) || defined(PBR)\\n\"\n\"t_eyevector[id] = eyevector[id];\\n\"\n\"#endif\\n\"\n\"#ifdef REFLECTCUBEMASK\\n\"\n\"t_invsurface[id][0] = invsurface[id][0];\\n\"\n\"t_invsurface[id][1] = invsurface[id][1];\\n\"\n\"t_invsurface[id][2] = invsurface[id][2];\\n\"\n\"#endif\\n\"\n\n\"gl_TessLevelOuter[0] = float(r_tessellation_level);\\n\"\n\"gl_TessLevelOuter[1] = float(r_tessellation_level);\\n\"\n\"gl_TessLevelOuter[2] = float(r_tessellation_level);\\n\"\n\"gl_TessLevelInner[0] = float(r_tessellation_level);\\n\"\n\"}\\n\"\n\"#endif\\n\"\n\n\n\n\n\n\n\n\n\n\"#if defined(TESS_EVALUATION_SHADER)\\n\"\n\"layout(triangles) in;\\n\"\n\n\"in vec3 t_vertex[];\\n\"\n\"in vec3 t_normal[];\\n\"\n\"affine in vec2 t_tc[];\\n\"\n\"affine out vec2 tc;\\n\"\n\"in vec4 t_light[];\\n\"\n\"out vec4 light;\\n\"\n\"#if defined(SPECULAR) || defined(OFFSETMAPPING) || defined(REFLECTCUBEMASK) || defined(PBR)\\n\"\n\"in vec3 t_eyevector[];\\n\"\n\"out vec3 eyevector;\\n\"\n\"#endif\\n\"\n\"#ifdef REFLECTCUBEMASK\\n\"\n\"in mat3 t_invsurface[];\\n\"\n\"out mat3 invsurface;\\n\"\n\"#endif\\n\"\n\n\"#define LERP(a) (gl_TessCoord.x*a[0] + gl_TessCoord.y*a[1] + gl_TessCoord.z*a[2])\\n\"\n\"void main()\\n\"\n\"{\\n\"\n\"#define factor 1.0\\n\"\n\"tc = LERP(t_tc);\\n\"\n\"vec3 w = LERP(t_vertex);\\n\"\n\n\"vec3 t0 = w - dot(w-t_vertex[0],t_normal[0])*t_normal[0];\\n\"\n\"vec3 t1 = w - dot(w-t_vertex[1],t_normal[1])*t_normal[1];\\n\"\n\"vec3 t2 = w - dot(w-t_vertex[2],t_normal[2])*t_normal[2];\\n\"\n\"w = w*(1.0-factor) + factor*(gl_TessCoord.x*t0+gl_TessCoord.y*t1+gl_TessCoord.z*t2);\\n\"\n\n//FIXME: we should be recalcing these here, instead of just lerping them\n\"light = LERP(t_light);\\n\"\n\"#if defined(SPECULAR) || defined(OFFSETMAPPING) || defined(REFLECTCUBEMASK) || defined(PBR)\\n\"\n\"eyevector = LERP(t_eyevector);\\n\"\n\"#endif\\n\"\n\"#ifdef REFLECTCUBEMASK\\n\"\n\"invsurface[0] = LERP(t_invsurface[0]);\\n\"\n\"invsurface[1] = LERP(t_invsurface[1]);\\n\"\n\"invsurface[2] = LERP(t_invsurface[2]);\\n\"\n\"#endif\\n\"\n\n\"gl_Position = m_modelviewprojection * vec4(w,1.0);\\n\"\n\"}\\n\"\n\"#endif\\n\"\n\n\n\n\n\n\n\n\n\n\n\"#ifdef FRAGMENT_SHADER\\n\"\n\n\"#include \\\"sys/fog.h\\\"\\n\"\n\n\"#if defined(SPECULAR)\\n\"\n\"uniform float cvar_gl_specular;\\n\"\n\"#endif\\n\"\n\n\"#ifdef OFFSETMAPPING\\n\"\n\"#include \\\"sys/offsetmapping.h\\\"\\n\"\n\"#endif\\n\"\n\n\"#ifdef EIGHTBIT\\n\"\n\"#define s_colourmap s_t0\\n\"\n\"#endif\\n\"\n\n\"affine varying vec2 tc;\\n\"\n\"varying vec4 light;\\n\"\n\"#if defined(SPECULAR) || defined(OFFSETMAPPING) || defined(REFLECTCUBEMASK) || defined(PBR)\\n\"\n\"varying vec3 eyevector;\\n\"\n\"#endif\\n\"\n\"#if defined(PBR) || defined(REFLECTCUBEMASK)\\n\"\n\"varying mat3 invsurface;\\n\"\n\"#endif\\n\"\n\n\"#ifdef PBR\\n\"\n\"#include \\\"sys/pbr.h\\\"\\n\"\n\"#if 0\\n\"\n\"vec3 getIBLContribution(PBRInfo pbrInputs, vec3 n, vec3 reflection)\\n\"\n\"{\\n\"\n\"float mipCount = 9.0; // resolution of 512x512\\n\"\n\"float lod = (pbrInputs.perceptualRoughness * mipCount);\\n\"\n// retrieve a scale and bias to F0. See [1], Figure 3\n\"vec3 brdf = texture2D(u_brdfLUT, vec2(pbrInputs.NdotV, 1.0 - pbrInputs.perceptualRoughness)).rgb;\\n\"\n\"vec3 diffuseLight = textureCube(u_DiffuseEnvSampler, n).rgb;\\n\"\n\n\"#ifdef USE_TEX_LOD\\n\"\n\"vec3 specularLight = textureCubeLodEXT(u_SpecularEnvSampler, reflection, lod).rgb;\\n\"\n\"#else\\n\"\n\"vec3 specularLight = textureCube(u_SpecularEnvSampler, reflection).rgb;\\n\"\n\"#endif\\n\"\n\n\"vec3 diffuse = diffuseLight * pbrInputs.diffuseColor;\\n\"\n\"vec3 specular = specularLight * (pbrInputs.specularColor * brdf.x + brdf.y);\\n\"\n\n// For presentation, this allows us to disable IBL terms\n\"diffuse *= u_ScaleIBLAmbient.x;\\n\"\n\"specular *= u_ScaleIBLAmbient.y;\\n\"\n\n\"return diffuse + specular;\\n\"\n\"}\\n\"\n\"#endif\\n\"\n\"#endif\\n\"\n\n\n\"void main ()\\n\"\n\"{\\n\"\n\"vec4 col, sp;\\n\"\n\n\"#ifdef OFFSETMAPPING\\n\"\n\"vec2 tcoffsetmap = offsetmap(s_normalmap, tc, eyevector);\\n\"\n\"#define tc tcoffsetmap\\n\"\n\"#endif\\n\"\n\n\"#ifdef EIGHTBIT\\n\"\n\"vec3 lightlev = light.rgb;\\n\"\n//FIXME: with this extra flag, half the permutations are redundant.\n\"lightlev *= 0.5; //counter the fact that the colourmap contains overbright values and logically ranges from 0 to 2 intead of to 1.\\n\"\n\"float pal = texture2D(s_paletted, tc).r; //the palette index. hopefully not interpolated.\\n\"\n//\tlightlev -= 1.0 / 128.0;\t//software rendering appears to round down, so make sure we favour the lower values instead of rounding to the nearest\n\"col.r = texture2D(s_colourmap, vec2(pal, 1.0-lightlev.r)).r; //do 3 lookups. this is to cope with lit files, would be a waste to not support those.\\n\"\n\"col.g = texture2D(s_colourmap, vec2(pal, 1.0-lightlev.g)).g; //its not very softwarey, but re-palettizing is ugly.\\n\"\n\"col.b = texture2D(s_colourmap, vec2(pal, 1.0-lightlev.b)).b; //without lits, it should be identical.\\n\"\n\"col.a = (pal<1.0)?light.a:0.0;\\n\"\n\"#else\\n\"\n\"col = texture2D(s_diffuse, tc);\\n\"\n\"#ifdef UPPER\\n\"\n\"vec4 uc = texture2D(s_upper, tc);\\n\"\n\"col.rgb += uc.rgb*e_uppercolour*uc.a;\\n\"\n\"#endif\\n\"\n\"#ifdef LOWER\\n\"\n\"vec4 lc = texture2D(s_lower, tc);\\n\"\n\"col.rgb += lc.rgb*e_lowercolour*lc.a;\\n\"\n\"#endif\\n\"\n\n\"col *= factor_base;\\n\"\n\n\"#ifndef IOR\\n\"\n\"#define IOR 1.5 //Index Of Reflection.\\n\"\n\"#endif\\n\"\n\"#define dielectricSpecular pow(((IOR - 1.0)/(IOR + 1.0)),2.0)\\n\"\n\"#ifdef SPECULAR\\n\"\n\"vec4 specs = texture2D(s_specular, tc)*factor_spec;\\n\"\n\"#ifdef ORM\\n\"\n\"#define occlusion specs.r\\n\"\n\"#define roughness clamp(specs.g, 0.04, 1.0)\\n\"\n\"#define metalness specs.b\\n\"\n\"#define gloss 1.0 //sqrt(1.0-roughness)\\n\"\n\"#define ambientrgb (specrgb+col.rgb)\\n\"\n\"vec3 specrgb = mix(vec3(dielectricSpecular), col.rgb, metalness);\\n\"\n\"col.rgb = col.rgb * (1.0 - dielectricSpecular) * (1.0-metalness);\\n\"\n\"#elif defined(SG) //pbr-style specular+glossiness, without occlusion\\n\"\n//occlusion needs to be baked in. :(\n\"#define roughness (1.0-specs.a)\\n\"\n\"#define gloss (specs.a)\\n\"\n\"#define specrgb specs.rgb\\n\"\n\"#define ambientrgb (specrgb+col.rgb)\\n\"\n\"#else //blinn-phong\\n\"\n\"#define roughness (1.0-specs.a)\\n\"\n\"#define gloss specs.a\\n\"\n\"#define specrgb specs.rgb\\n\"\n\"#define ambientrgb col.rgb\\n\"\n\"#endif\\n\"\n\"#else\\n\"\n\"#define roughness 0.3\\n\"\n\"#define specrgb vec3(1.0) //vec3(dielectricSpecular)\\n\"\n\"#define ambientrgb col.rgb\\n\"\n\"#endif\\n\"\n\n\"#ifdef BUMP\\n\"\n\"#ifdef PBR //to modelspace\\n\"\n\"vec3 bumps = normalize(invsurface * (texture2D(s_normalmap, tc).rgb*2.0 - 1.0));\\n\"\n\"#else //stay in tangentspace\\n\"\n\"vec3 bumps = normalize(vec3(texture2D(s_normalmap, tc)) - 0.5);\\n\"\n\"#endif\\n\"\n\"#else\\n\"\n\"#ifdef PBR //to modelspace\\n\"\n\"#define bumps normalize(invsurface[2])\\n\"\n\"#else //tangent space\\n\"\n\"#define bumps vec3(0.0, 0.0, 1.0)\\n\"\n\"#endif\\n\"\n\"#endif\\n\"\n\n\"#ifdef PBR\\n\"\n//move everything to model space\n\"col.rgb = DoPBR(bumps, normalize(eyevector), -e_light_dir, roughness, col.rgb, specrgb, vec3(0.0,1.0,1.0))*e_light_mul + e_light_ambient*.25*ambientrgb;\\n\"\n\"#elif defined(gloss)\\n\"\n\"vec3 halfdir = normalize(normalize(eyevector) - e_light_dir);\\n\"\n\"float specmag = pow(max(dot(halfdir, bumps), 0.0), FTE_SPECULAR_EXPONENT * gloss);\\n\"\n\"col.rgb += FTE_SPECULAR_MULTIPLIER * specmag * specrgb;\\n\"\n\"#endif\\n\"\n\n\"#ifdef REFLECTCUBEMASK\\n\"\n\"vec3 rtc = reflect(-eyevector, bumps);\\n\"\n\"#ifndef PBR\\n\"\n\"rtc = rtc.x*invsurface[0] + rtc.y*invsurface[1] + rtc.z*invsurface[2];\\n\"\n\"#endif\\n\"\n\"rtc = (m_model * vec4(rtc.xyz,0.0)).xyz;\\n\"\n\"col.rgb += texture2D(s_reflectmask, tc).rgb * textureCube(s_reflectcube, rtc).rgb;\\n\"\n\"#endif\\n\"\n\n\"#ifdef OCCLUDE\\n\"\n\"col.rgb *= texture2D(s_occlusion, tc).r; \\n\"\n\"#elif defined(occlusion) && !defined(NOOCCLUDE)\\n\"\n\"col.rgb *= occlusion;\\n\"\n\"#endif\\n\"\n\"col *= light * e_colourident;\\n\"\n\n\"#ifdef FULLBRIGHT\\n\"\n\"vec4 fb = texture2D(s_fullbright, tc);\\n\"\n//\t\tcol.rgb = mix(col.rgb, fb.rgb, fb.a);\n\"col.rgb += fb.rgb * fb.a * e_glowmod.rgb * factor_emit.rgb;\\n\"\n\"#elif defined(PBR)\\n\"\n\"col.rgb += e_glowmod.rgb * factor_emit.rgb;\\n\"\n\"#endif\\n\"\n\"#endif\\n\"\n\n\"#ifdef ALPHATEST\\n\"\n\"if (!(col.a ALPHATEST))\\n\"\n\"discard;\\n\"\n\"#elif defined(MASK)\\n\"\n\"#if defined(MASKLT)\\n\"\n\"if (col.a < MASK)\\n\"\n\"discard;\\n\"\n\"#else\\n\"\n\"if (col.a >= MASK)\\n\"\n\"discard;\\n\"\n\"#endif\\n\"\n\"col.a = 1.0;    //alpha blending AND alpha testing usually looks stupid, plus it screws up our fog.\\n\"\n\"#endif\\n\"\n\n\"gl_FragColor = fog4(col);\\n\"\n\"}\\n\"\n\"#endif\\n\"\n\n},\n#endif\n#ifdef VKQUAKE\n{QR_VULKAN, -1, \"defaultskin\",\n\"\\xFF\\x53\\x50\\x56\\x01\\x00\\x00\\x00\\xFC\\x06\\x00\\x00\\x00\\x00\\x00\\x00\\x2F\\x00\\x00\\x00\\x2C\\x00\\x00\\x00\\x7C\\x00\\x00\\x00\\xA8\\x00\\x00\\x00\"\n\"\\xB8\\x13\\x00\\x00\\x60\\x14\\x00\\x00\\xD4\\x2C\\x00\\x00\\x01\\x00\\x66\\x31\\x72\\x5F\\x67\\x6C\\x73\\x6C\\x5F\\x6F\\x66\\x66\\x73\\x65\\x74\\x6D\\x61\\x70\"\n\"\\x70\\x69\\x6E\\x67\\x00\\x00\\x00\\x00\\x00\\x01\\x01\\x66\\x31\\x72\\x5F\\x67\\x6C\\x73\\x6C\\x5F\\x6F\\x66\\x66\\x73\\x65\\x74\\x6D\\x61\\x70\\x70\\x69\\x6E\"\n\"\\x67\\x5F\\x73\\x63\\x61\\x6C\\x65\\x00\\x3D\\x23\\xD7\\x0A\\x01\\x02\\x66\\x31\\x67\\x6C\\x5F\\x73\\x70\\x65\\x63\\x75\\x6C\\x61\\x72\\x00\\x00\\x00\\x00\\x00\"\n\"\\x01\\x03\\x62\\x31\\x72\\x5F\\x66\\x6F\\x67\\x5F\\x65\\x78\\x70\\x32\\x00\\x00\\x00\\x00\\x00\\x01\\x04\\x62\\x31\\x72\\x5F\\x66\\x6F\\x67\\x5F\\x6C\\x69\\x6E\"\n\"\\x65\\x61\\x72\\x00\\x00\\x00\\x00\\x00\\x03\\x02\\x23\\x07\\x00\\x00\\x01\\x00\\x0A\\x00\\x08\\x00\\xC1\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x11\\x00\\x02\\x00\"\n\"\\x01\\x00\\x00\\x00\\x0B\\x00\\x06\\x00\\x01\\x00\\x00\\x00\\x47\\x4C\\x53\\x4C\\x2E\\x73\\x74\\x64\\x2E\\x34\\x35\\x30\\x00\\x00\\x00\\x00\\x0E\\x00\\x03\\x00\"\n\"\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x0F\\x00\\x11\\x00\\x00\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x6D\\x61\\x69\\x6E\\x00\\x00\\x00\\x00\\x28\\x00\\x00\\x00\"\n\"\\x44\\x00\\x00\\x00\\x46\\x00\\x00\\x00\\x48\\x00\\x00\\x00\\x60\\x00\\x00\\x00\\x78\\x00\\x00\\x00\\x8C\\x00\\x00\\x00\\xA2\\x00\\x00\\x00\\xB0\\x00\\x00\\x00\"\n\"\\xB2\\x00\\x00\\x00\\xBF\\x00\\x00\\x00\\xC0\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x1D\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\"\n\"\\x1F\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x1F\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x1F\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x1F\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x1F\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x1F\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x1F\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x1F\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x1F\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x10\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x1F\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xC0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x1F\\x00\\x00\\x00\"\n\"\\x04\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xCC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x1F\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xD0\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x1F\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xDC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x1F\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\xE0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x1F\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xEC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x1F\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xF0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x1F\\x00\\x00\\x00\\x0A\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\xFC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x1F\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x1F\\x00\\x00\\x00\"\n\"\\x0C\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x1F\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x4C\\x01\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x1F\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x50\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x1F\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x5C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x1F\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x60\\x01\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x1F\\x00\\x00\\x00\\x11\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x6C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x1F\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x70\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x1F\\x00\\x00\\x00\\x13\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x1F\\x00\\x00\\x00\"\n\"\\x14\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x90\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x1F\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x94\\x01\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x1F\\x00\\x00\\x00\\x16\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x98\\x01\\x00\\x00\\x47\\x00\\x03\\x00\\x1F\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x21\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x21\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x28\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x44\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x04\\x00\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x46\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x48\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x52\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x02\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\x56\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x01\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x59\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x13\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x5E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x47\\x00\\x03\\x00\\x5E\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x78\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x8C\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\xA2\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\xB0\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\xB2\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\xB4\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x01\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\xB5\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x03\\x01\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\xB6\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x04\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\xB7\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x10\\x00\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\xB8\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x11\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\xB9\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x12\\x00\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\xBA\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\xBB\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x05\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\xBB\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\xBB\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\xBB\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\xBB\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x4C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\xBB\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x50\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\xBB\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x5C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\xBB\\x00\\x00\\x00\"\n\"\\x05\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x60\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\xBB\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x6C\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\xBB\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x70\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\xBB\\x00\\x00\\x00\\x08\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x80\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\xBB\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x88\\x00\\x00\\x00\\x47\\x00\\x03\\x00\"\n\"\\xBB\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\xBD\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\xBD\\x00\\x00\\x00\"\n\"\\x21\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\xBF\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\xC0\\x00\\x00\\x00\"\n\"\\x1E\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x13\\x00\\x02\\x00\\x02\\x00\\x00\\x00\\x21\\x00\\x03\\x00\\x03\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x16\\x00\\x03\\x00\"\n\"\\x06\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x17\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x21\\x00\\x03\\x00\\x08\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x17\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x0C\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x0B\\x00\\x00\\x00\\x21\\x00\\x07\\x00\\x0D\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\"\n\"\\x21\\x00\\x04\\x00\\x14\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x18\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x18\\x00\\x04\\x00\\x1A\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x15\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x2B\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\\x1C\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x1C\\x00\\x04\\x00\\x1D\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x1C\\x00\\x00\\x00\"\n\"\\x17\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x1E\\x00\\x19\\x00\\x1F\\x00\\x00\\x00\\x1A\\x00\\x00\\x00\\x1A\\x00\\x00\\x00\"\n\"\\x1A\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x20\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x1F\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x20\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x15\\x00\\x04\\x00\\x22\\x00\\x00\\x00\\x20\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x22\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x24\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x1A\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x27\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x27\\x00\\x00\\x00\\x28\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x2A\\x00\\x00\\x00\\x00\\x00\\x80\\x3F\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x30\\x00\\x00\\x00\"\n\"\\x00\\x00\\x80\\xBF\\x2B\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\\x31\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x32\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\\x37\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\\x3A\\x00\\x00\\x00\"\n\"\\x03\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x3E\\x00\\x00\\x00\\x00\\x00\\x00\\x40\\x3B\\x00\\x04\\x00\\x27\\x00\\x00\\x00\\x44\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x27\\x00\\x00\\x00\\x46\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x27\\x00\\x00\\x00\\x48\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x32\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x52\\x00\\x00\\x00\\x00\\x00\\x81\\x43\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x53\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x14\\x00\\x02\\x00\\x54\\x00\\x00\\x00\\x32\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x56\\x00\\x00\\x00\\x00\\x00\\x80\\x43\\x32\\x00\\x04\\x00\"\n\"\\x22\\x00\\x00\\x00\\x59\\x00\\x00\\x00\\x13\\x00\\x00\\x00\\x34\\x00\\x06\\x00\\x54\\x00\\x00\\x00\\x5A\\x00\\x00\\x00\\xAB\\x00\\x00\\x00\\x59\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x1E\\x00\\x03\\x00\\x5E\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x5F\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x5E\\x00\\x00\\x00\"\n\"\\x3B\\x00\\x04\\x00\\x5F\\x00\\x00\\x00\\x60\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x6E\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x2B\\x00\\x04\\x00\\x22\\x00\\x00\\x00\\x71\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x72\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\"\n\"\\x20\\x00\\x04\\x00\\x77\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x77\\x00\\x00\\x00\\x78\\x00\\x00\\x00\\x03\\x00\\x00\\x00\"\n\"\\x2B\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\\x7C\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x7D\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x34\\x00\\x06\\x00\\x54\\x00\\x00\\x00\\x87\\x00\\x00\\x00\\xAB\\x00\\x00\\x00\\x59\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x18\\x00\\x04\\x00\\x8A\\x00\\x00\\x00\"\n\"\\x0B\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x8B\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x8A\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x8B\\x00\\x00\\x00\"\n\"\\x8C\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x22\\x00\\x00\\x00\\x9A\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x77\\x00\\x00\\x00\"\n\"\\xA2\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x22\\x00\\x00\\x00\\xA3\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x22\\x00\\x00\\x00\"\n\"\\xAA\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\xAF\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\xAF\\x00\\x00\\x00\"\n\"\\xB0\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\xB1\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\xB1\\x00\\x00\\x00\"\n\"\\xB2\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x32\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\xB4\\x00\\x00\\x00\\x00\\x80\\x80\\x43\\x32\\x00\\x04\\x00\\x22\\x00\\x00\\x00\"\n\"\\xB5\\x00\\x00\\x00\\x03\\x01\\x00\\x00\\x32\\x00\\x04\\x00\\x22\\x00\\x00\\x00\\xB6\\x00\\x00\\x00\\x04\\x01\\x00\\x00\\x32\\x00\\x04\\x00\\x22\\x00\\x00\\x00\"\n\"\\xB7\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x32\\x00\\x04\\x00\\x22\\x00\\x00\\x00\\xB8\\x00\\x00\\x00\\x11\\x00\\x00\\x00\\x32\\x00\\x04\\x00\\x22\\x00\\x00\\x00\"\n\"\\xB9\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x32\\x00\\x04\\x00\\x22\\x00\\x00\\x00\\xBA\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x1E\\x00\\x0C\\x00\\xBB\\x00\\x00\\x00\"\n\"\\x1A\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x1E\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\xBC\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\xBB\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\xBC\\x00\\x00\\x00\"\n\"\\xBD\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\xBE\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\xBE\\x00\\x00\\x00\"\n\"\\xBF\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\xB1\\x00\\x00\\x00\\xC0\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x36\\x00\\x05\\x00\\x02\\x00\\x00\\x00\"\n\"\\x04\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x05\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x0C\\x00\\x00\\x00\\x61\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x0C\\x00\\x00\\x00\\x62\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x0C\\x00\\x00\\x00\\x63\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x0C\\x00\\x00\\x00\\x64\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x0C\\x00\\x00\\x00\\x65\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x0C\\x00\\x00\\x00\\x66\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x0C\\x00\\x00\\x00\\x67\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x0C\\x00\\x00\\x00\\x68\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x0C\\x00\\x00\\x00\\x70\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x0C\\x00\\x00\\x00\\x94\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x32\\x00\\x00\\x00\\x98\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\xBA\\x00\\x05\\x00\\x54\\x00\\x00\\x00\\x55\\x00\\x00\\x00\\x52\\x00\\x00\\x00\\x53\\x00\\x00\\x00\\xBA\\x00\\x05\\x00\\x54\\x00\\x00\\x00\"\n\"\\x57\\x00\\x00\\x00\\x56\\x00\\x00\\x00\\x53\\x00\\x00\\x00\\xA6\\x00\\x05\\x00\\x54\\x00\\x00\\x00\\x58\\x00\\x00\\x00\\x55\\x00\\x00\\x00\\x57\\x00\\x00\\x00\"\n\"\\xA6\\x00\\x05\\x00\\x54\\x00\\x00\\x00\\x5B\\x00\\x00\\x00\\x58\\x00\\x00\\x00\\x5A\\x00\\x00\\x00\\xF7\\x00\\x03\\x00\\x5D\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\xFA\\x00\\x04\\x00\\x5B\\x00\\x00\\x00\\x5C\\x00\\x00\\x00\\x93\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x5C\\x00\\x00\\x00\\x39\\x00\\x08\\x00\\x07\\x00\\x00\\x00\"\n\"\\x69\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x65\\x00\\x00\\x00\\x66\\x00\\x00\\x00\\x67\\x00\\x00\\x00\\x68\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\"\n\"\\x6A\\x00\\x00\\x00\\x65\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x61\\x00\\x00\\x00\\x6A\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\x6B\\x00\\x00\\x00\"\n\"\\x66\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x62\\x00\\x00\\x00\\x6B\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\x6C\\x00\\x00\\x00\\x67\\x00\\x00\\x00\"\n\"\\x3E\\x00\\x03\\x00\\x63\\x00\\x00\\x00\\x6C\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\x6D\\x00\\x00\\x00\\x68\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\"\n\"\\x64\\x00\\x00\\x00\\x6D\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x6E\\x00\\x00\\x00\\x6F\\x00\\x00\\x00\\x60\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\"\n\"\\x6F\\x00\\x00\\x00\\x69\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x72\\x00\\x00\\x00\\x73\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x71\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x0B\\x00\\x00\\x00\\x74\\x00\\x00\\x00\\x73\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\x75\\x00\\x00\\x00\\x61\\x00\\x00\\x00\\x83\\x00\\x05\\x00\"\n\"\\x0B\\x00\\x00\\x00\\x76\\x00\\x00\\x00\\x74\\x00\\x00\\x00\\x75\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x70\\x00\\x00\\x00\\x76\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x0B\\x00\\x00\\x00\\x79\\x00\\x00\\x00\\x70\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\x7A\\x00\\x00\\x00\\x63\\x00\\x00\\x00\\x94\\x00\\x05\\x00\"\n\"\\x06\\x00\\x00\\x00\\x7B\\x00\\x00\\x00\\x79\\x00\\x00\\x00\\x7A\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x7D\\x00\\x00\\x00\\x7E\\x00\\x00\\x00\\x78\\x00\\x00\\x00\"\n\"\\x7C\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x7E\\x00\\x00\\x00\\x7B\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\x7F\\x00\\x00\\x00\\x70\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\x80\\x00\\x00\\x00\\x64\\x00\\x00\\x00\\x94\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x81\\x00\\x00\\x00\\x7F\\x00\\x00\\x00\"\n\"\\x80\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x7D\\x00\\x00\\x00\\x82\\x00\\x00\\x00\\x78\\x00\\x00\\x00\\x31\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x82\\x00\\x00\\x00\"\n\"\\x81\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\x83\\x00\\x00\\x00\\x70\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\x84\\x00\\x00\\x00\"\n\"\\x62\\x00\\x00\\x00\\x94\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x85\\x00\\x00\\x00\\x83\\x00\\x00\\x00\\x84\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x7D\\x00\\x00\\x00\"\n\"\\x86\\x00\\x00\\x00\\x78\\x00\\x00\\x00\\x37\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x86\\x00\\x00\\x00\\x85\\x00\\x00\\x00\\xF7\\x00\\x03\\x00\\x89\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\\x87\\x00\\x00\\x00\\x88\\x00\\x00\\x00\\x89\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x88\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x0B\\x00\\x00\\x00\\x8D\\x00\\x00\\x00\\x63\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x77\\x00\\x00\\x00\\x8E\\x00\\x00\\x00\\x8C\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x3E\\x00\\x03\\x00\\x8E\\x00\\x00\\x00\\x8D\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\x8F\\x00\\x00\\x00\\x64\\x00\\x00\\x00\\x41\\x00\\x05\\x00\"\n\"\\x77\\x00\\x00\\x00\\x90\\x00\\x00\\x00\\x8C\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x90\\x00\\x00\\x00\\x8F\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x0B\\x00\\x00\\x00\\x91\\x00\\x00\\x00\\x62\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x77\\x00\\x00\\x00\\x92\\x00\\x00\\x00\\x8C\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x3E\\x00\\x03\\x00\\x92\\x00\\x00\\x00\\x91\\x00\\x00\\x00\\xF9\\x00\\x02\\x00\\x89\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x89\\x00\\x00\\x00\\xF9\\x00\\x02\\x00\"\n\"\\x5D\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x93\\x00\\x00\\x00\\x39\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x95\\x00\\x00\\x00\\x16\\x00\\x00\\x00\\x94\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\x96\\x00\\x00\\x00\\x94\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x62\\x00\\x00\\x00\\x96\\x00\\x00\\x00\\x41\\x00\\x05\\x00\"\n\"\\x6E\\x00\\x00\\x00\\x97\\x00\\x00\\x00\\x60\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x97\\x00\\x00\\x00\\x95\\x00\\x00\\x00\\xF9\\x00\\x02\\x00\"\n\"\\x5D\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x5D\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\x99\\x00\\x00\\x00\\x62\\x00\\x00\\x00\\x41\\x00\\x05\\x00\"\n\"\\x72\\x00\\x00\\x00\\x9B\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x9A\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\x9C\\x00\\x00\\x00\\x9B\\x00\\x00\\x00\"\n\"\\x94\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x9D\\x00\\x00\\x00\\x99\\x00\\x00\\x00\\x9C\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x98\\x00\\x00\\x00\\x9D\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x9E\\x00\\x00\\x00\\x98\\x00\\x00\\x00\\xB8\\x00\\x05\\x00\\x54\\x00\\x00\\x00\\x9F\\x00\\x00\\x00\\x9E\\x00\\x00\\x00\"\n\"\\x53\\x00\\x00\\x00\\xF7\\x00\\x03\\x00\\xA1\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\\x9F\\x00\\x00\\x00\\xA0\\x00\\x00\\x00\\xA1\\x00\\x00\\x00\"\n\"\\xF8\\x00\\x02\\x00\\xA0\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x98\\x00\\x00\\x00\\x53\\x00\\x00\\x00\\xF9\\x00\\x02\\x00\\xA1\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\"\n\"\\xA1\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x72\\x00\\x00\\x00\\xA4\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\xA3\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\"\n\"\\xA5\\x00\\x00\\x00\\xA4\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\xA6\\x00\\x00\\x00\\x62\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x72\\x00\\x00\\x00\"\n\"\\xA7\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x9A\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\xA8\\x00\\x00\\x00\\xA7\\x00\\x00\\x00\\x94\\x00\\x05\\x00\"\n\"\\x06\\x00\\x00\\x00\\xA9\\x00\\x00\\x00\\xA6\\x00\\x00\\x00\\xA8\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x72\\x00\\x00\\x00\\xAB\\x00\\x00\\x00\\x21\\x00\\x00\\x00\"\n\"\\xAA\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\xAC\\x00\\x00\\x00\\xAB\\x00\\x00\\x00\\x8E\\x00\\x05\\x00\\x0B\\x00\\x00\\x00\\xAD\\x00\\x00\\x00\"\n\"\\xAC\\x00\\x00\\x00\\xA9\\x00\\x00\\x00\\x81\\x00\\x05\\x00\\x0B\\x00\\x00\\x00\\xAE\\x00\\x00\\x00\\xA5\\x00\\x00\\x00\\xAD\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\"\n\"\\xA2\\x00\\x00\\x00\\xAE\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\\xB3\\x00\\x00\\x00\\xB2\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\xB0\\x00\\x00\\x00\"\n\"\\xB3\\x00\\x00\\x00\\xFD\\x00\\x01\\x00\\x38\\x00\\x01\\x00\\x36\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x08\\x00\\x00\\x00\"\n\"\\xF8\\x00\\x02\\x00\\x0A\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x18\\x00\\x00\\x00\\x19\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x24\\x00\\x00\\x00\"\n\"\\x25\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x1A\\x00\\x00\\x00\\x26\\x00\\x00\\x00\\x25\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x0B\\x00\\x00\\x00\\x29\\x00\\x00\\x00\\x28\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x2B\\x00\\x00\\x00\\x29\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x2C\\x00\\x00\\x00\\x29\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\"\n\"\\x29\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x50\\x00\\x07\\x00\\x07\\x00\\x00\\x00\\x2E\\x00\\x00\\x00\\x2B\\x00\\x00\\x00\\x2C\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\"\n\"\\x2A\\x00\\x00\\x00\\x91\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x2F\\x00\\x00\\x00\\x26\\x00\\x00\\x00\\x2E\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x19\\x00\\x00\\x00\"\n\"\\x2F\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x32\\x00\\x00\\x00\\x33\\x00\\x00\\x00\\x19\\x00\\x00\\x00\\x31\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\"\n\"\\x34\\x00\\x00\\x00\\x33\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x35\\x00\\x00\\x00\\x34\\x00\\x00\\x00\\x30\\x00\\x00\\x00\\x41\\x00\\x05\\x00\"\n\"\\x32\\x00\\x00\\x00\\x36\\x00\\x00\\x00\\x19\\x00\\x00\\x00\\x31\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x36\\x00\\x00\\x00\\x35\\x00\\x00\\x00\\x41\\x00\\x05\\x00\"\n\"\\x32\\x00\\x00\\x00\\x38\\x00\\x00\\x00\\x19\\x00\\x00\\x00\\x37\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x39\\x00\\x00\\x00\\x38\\x00\\x00\\x00\"\n\"\\x41\\x00\\x05\\x00\\x32\\x00\\x00\\x00\\x3B\\x00\\x00\\x00\\x19\\x00\\x00\\x00\\x3A\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x3C\\x00\\x00\\x00\"\n\"\\x3B\\x00\\x00\\x00\\x81\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x3D\\x00\\x00\\x00\\x39\\x00\\x00\\x00\\x3C\\x00\\x00\\x00\\x88\\x00\\x05\\x00\\x06\\x00\\x00\\x00\"\n\"\\x3F\\x00\\x00\\x00\\x3D\\x00\\x00\\x00\\x3E\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x32\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x19\\x00\\x00\\x00\\x37\\x00\\x00\\x00\"\n\"\\x3E\\x00\\x03\\x00\\x40\\x00\\x00\\x00\\x3F\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x41\\x00\\x00\\x00\\x19\\x00\\x00\\x00\\xFE\\x00\\x02\\x00\"\n\"\\x41\\x00\\x00\\x00\\x38\\x00\\x01\\x00\\x36\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x37\\x00\\x03\\x00\"\n\"\\x0C\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x37\\x00\\x03\\x00\\x0C\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\\x37\\x00\\x03\\x00\\x0C\\x00\\x00\\x00\\x10\\x00\\x00\\x00\"\n\"\\x37\\x00\\x03\\x00\\x0C\\x00\\x00\\x00\\x11\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x13\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\x45\\x00\\x00\\x00\"\n\"\\x44\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x0F\\x00\\x00\\x00\\x45\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\x47\\x00\\x00\\x00\\x46\\x00\\x00\\x00\"\n\"\\x3E\\x00\\x03\\x00\\x10\\x00\\x00\\x00\\x47\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\x49\\x00\\x00\\x00\\x48\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\"\n\"\\x11\\x00\\x00\\x00\\x49\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\x4A\\x00\\x00\\x00\\x28\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x0E\\x00\\x00\\x00\"\n\"\\x4A\\x00\\x00\\x00\\x39\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x4B\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\xFE\\x00\\x02\\x00\\x4B\\x00\\x00\\x00\\x38\\x00\\x01\\x00\"\n\"\\x36\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x16\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x14\\x00\\x00\\x00\\x37\\x00\\x03\\x00\\x0C\\x00\\x00\\x00\\x15\\x00\\x00\\x00\"\n\"\\xF8\\x00\\x02\\x00\\x17\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\x4E\\x00\\x00\\x00\\x44\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x15\\x00\\x00\\x00\"\n\"\\x4E\\x00\\x00\\x00\\x39\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x4F\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\xFE\\x00\\x02\\x00\\x4F\\x00\\x00\\x00\\x38\\x00\\x01\\x00\"\n\"\\x03\\x02\\x23\\x07\\x00\\x00\\x01\\x00\\x0A\\x00\\x08\\x00\\xEA\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x11\\x00\\x02\\x00\\x01\\x00\\x00\\x00\\x0B\\x00\\x06\\x00\"\n\"\\x01\\x00\\x00\\x00\\x47\\x4C\\x53\\x4C\\x2E\\x73\\x74\\x64\\x2E\\x34\\x35\\x30\\x00\\x00\\x00\\x00\\x0E\\x00\\x03\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x0F\\x00\\x0B\\x00\\x04\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x6D\\x61\\x69\\x6E\\x00\\x00\\x00\\x00\\x2F\\x00\\x00\\x00\\x1B\\x01\\x00\\x00\\x1D\\x01\\x00\\x00\"\n\"\\x94\\x01\\x00\\x00\\xC4\\x01\\x00\\x00\\xDF\\x01\\x00\\x00\\x10\\x00\\x03\\x00\\x04\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x1F\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x28\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x04\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\x2F\\x00\\x00\\x00\"\n\"\\x0B\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x3C\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x3D\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x3D\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x3D\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x3D\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x05\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x3D\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x3D\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x3D\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x3D\\x00\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x3D\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x3D\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xC0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x3D\\x00\\x00\\x00\\x04\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\xCC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x3D\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xD0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x3D\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xDC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x3D\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\xE0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x3D\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xEC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x3D\\x00\\x00\\x00\"\n\"\\x09\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xF0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x3D\\x00\\x00\\x00\\x0A\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xFC\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x3D\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x3D\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x40\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x3D\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x4C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x3D\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x50\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x3D\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x5C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x3D\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x60\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x3D\\x00\\x00\\x00\"\n\"\\x11\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x6C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x3D\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x70\\x01\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x3D\\x00\\x00\\x00\\x13\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x3D\\x00\\x00\\x00\\x14\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x90\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x3D\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x94\\x01\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x3D\\x00\\x00\\x00\\x16\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x98\\x01\\x00\\x00\\x47\\x00\\x03\\x00\\x3D\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\"\n\"\\x3F\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x3F\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\"\n\"\\x5C\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x03\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\x97\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x01\\x01\\x00\\x00\\x47\\x00\\x04\\x00\"\n\"\\xE5\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\x19\\x01\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\"\n\"\\x19\\x01\\x00\\x00\\x21\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x1B\\x01\\x00\\x00\\x1E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\"\n\"\\x1D\\x01\\x00\\x00\\x1E\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x26\\x01\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\"\n\"\\x26\\x01\\x00\\x00\\x21\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x2A\\x01\\x00\\x00\\x01\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x47\\x00\\x04\\x00\"\n\"\\x2F\\x01\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x2F\\x01\\x00\\x00\\x21\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x47\\x00\\x04\\x00\"\n\"\\x43\\x01\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x43\\x01\\x00\\x00\\x21\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x47\\x00\\x04\\x00\"\n\"\\x57\\x01\\x00\\x00\\x01\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x59\\x01\\x00\\x00\\x01\\x00\\x00\\x00\\x02\\x01\\x00\\x00\\x47\\x00\\x04\\x00\"\n\"\\x69\\x01\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x69\\x01\\x00\\x00\\x21\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x47\\x00\\x04\\x00\"\n\"\\x86\\x01\\x00\\x00\\x01\\x00\\x00\\x00\\x13\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x94\\x01\\x00\\x00\\x1E\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x47\\x00\\x04\\x00\"\n\"\\xB1\\x01\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\xB1\\x01\\x00\\x00\\x21\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x47\\x00\\x04\\x00\"\n\"\\xB9\\x01\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\xB9\\x01\\x00\\x00\\x21\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\x47\\x00\\x04\\x00\"\n\"\\xC4\\x01\\x00\\x00\\x1E\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\xCB\\x01\\x00\\x00\\x01\\x00\\x00\\x00\\x11\\x00\\x00\\x00\\x47\\x00\\x04\\x00\"\n\"\\xD0\\x01\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\xD0\\x01\\x00\\x00\\x21\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x47\\x00\\x04\\x00\"\n\"\\xDF\\x01\\x00\\x00\\x1E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\xE7\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\xE7\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\xE7\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x10\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\xE7\\x01\\x00\\x00\\x01\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\xE7\\x01\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x4C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\xE7\\x01\\x00\\x00\\x03\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x50\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\xE7\\x01\\x00\\x00\\x04\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x5C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\xE7\\x01\\x00\\x00\\x05\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x60\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\xE7\\x01\\x00\\x00\\x06\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x6C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\xE7\\x01\\x00\\x00\\x07\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x70\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\xE7\\x01\\x00\\x00\\x08\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x80\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\xE7\\x01\\x00\\x00\\x09\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x88\\x00\\x00\\x00\\x47\\x00\\x03\\x00\\xE7\\x01\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\xE9\\x01\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\xE9\\x01\\x00\\x00\\x21\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x13\\x00\\x02\\x00\\x02\\x00\\x00\\x00\\x21\\x00\\x03\\x00\\x03\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x16\\x00\\x03\\x00\\x06\\x00\\x00\\x00\"\n\"\\x20\\x00\\x00\\x00\\x17\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x08\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x21\\x00\\x04\\x00\\x09\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\x17\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x04\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x0E\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x21\\x00\\x04\\x00\\x0F\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\"\n\"\\x0E\\x00\\x00\\x00\\x19\\x00\\x09\\x00\\x13\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x1B\\x00\\x03\\x00\\x14\\x00\\x00\\x00\\x13\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x15\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x14\\x00\\x00\\x00\\x17\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x17\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x16\\x00\\x00\\x00\\x21\\x00\\x06\\x00\\x18\\x00\\x00\\x00\\x16\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x17\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\x15\\x00\\x04\\x00\"\n\"\\x1E\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x32\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\\x1F\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\"\n\"\\x1E\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x14\\x00\\x02\\x00\\x21\\x00\\x00\\x00\\x34\\x00\\x06\\x00\\x21\\x00\\x00\\x00\\x22\\x00\\x00\\x00\"\n\"\\xAB\\x00\\x00\\x00\\x1F\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x34\\x00\\x05\\x00\\x21\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xA8\\x00\\x00\\x00\\x22\\x00\\x00\\x00\"\n\"\\x32\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\\x28\\x00\\x00\\x00\\x04\\x01\\x00\\x00\\x34\\x00\\x06\\x00\\x21\\x00\\x00\\x00\\x29\\x00\\x00\\x00\\xAB\\x00\\x00\\x00\"\n\"\\x28\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x2C\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x2E\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x2E\\x00\\x00\\x00\\x2F\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x15\\x00\\x04\\x00\\x30\\x00\\x00\\x00\"\n\"\\x20\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x30\\x00\\x00\\x00\\x31\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x32\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x30\\x00\\x00\\x00\\x35\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x18\\x00\\x04\\x00\\x3A\\x00\\x00\\x00\"\n\"\\x0D\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x30\\x00\\x00\\x00\\x3B\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x1C\\x00\\x04\\x00\\x3C\\x00\\x00\\x00\"\n\"\\x0D\\x00\\x00\\x00\\x3B\\x00\\x00\\x00\\x1E\\x00\\x19\\x00\\x3D\\x00\\x00\\x00\\x3A\\x00\\x00\\x00\\x3A\\x00\\x00\\x00\\x3A\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x3C\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x16\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x3E\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x3D\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\"\n\"\\x3E\\x00\\x00\\x00\\x3F\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x14\\x00\\x00\\x00\\x20\\x00\\x04\\x00\"\n\"\\x41\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\\x48\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\"\n\"\\x06\\x00\\x00\\x00\\x56\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x32\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\\x5C\\x00\\x00\\x00\\x03\\x01\\x00\\x00\\x34\\x00\\x06\\x00\"\n\"\\x21\\x00\\x00\\x00\\x5D\\x00\\x00\\x00\\xAB\\x00\\x00\\x00\\x5C\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x64\\x00\\x00\\x00\"\n\"\\x3B\\xAA\\xB8\\x3F\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x68\\x00\\x00\\x00\\x00\\x00\\x80\\x3F\\x2B\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\\x69\\x00\\x00\\x00\"\n\"\\x13\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x73\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x34\\x00\\x06\\x00\\x21\\x00\\x00\\x00\\x7D\\x00\\x00\\x00\"\n\"\\xAB\\x00\\x00\\x00\\x1F\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x34\\x00\\x05\\x00\\x21\\x00\\x00\\x00\\x7E\\x00\\x00\\x00\\xA8\\x00\\x00\\x00\\x7D\\x00\\x00\\x00\"\n\"\\x2A\\x00\\x03\\x00\\x21\\x00\\x00\\x00\\x90\\x00\\x00\\x00\\x32\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x97\\x00\\x00\\x00\\x00\\x80\\x80\\x43\\x2B\\x00\\x04\\x00\"\n\"\\x06\\x00\\x00\\x00\\x9A\\x00\\x00\\x00\\x00\\x00\\x80\\xBF\\x2C\\x00\\x05\\x00\\x16\\x00\\x00\\x00\\x9B\\x00\\x00\\x00\\x9A\\x00\\x00\\x00\\x68\\x00\\x00\\x00\"\n\"\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\xA8\\x00\\x00\\x00\\x00\\x00\\x20\\x41\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\xC9\\x00\\x00\\x00\\x00\\x00\\xA0\\x40\"\n\"\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\xD6\\x00\\x00\\x00\\x00\\x00\\x00\\x3F\\x32\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\xE5\\x00\\x00\\x00\\x00\\x00\\x80\\x43\"\n\"\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\xF5\\x00\\x00\\x00\\xFA\\x7E\\xAA\\x3E\\x3B\\x00\\x04\\x00\\x15\\x00\\x00\\x00\\x19\\x01\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x20\\x00\\x04\\x00\\x1A\\x01\\x00\\x00\\x01\\x00\\x00\\x00\\x16\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x1A\\x01\\x00\\x00\\x1B\\x01\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x20\\x00\\x04\\x00\\x1C\\x01\\x00\\x00\\x01\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x1C\\x01\\x00\\x00\\x1D\\x01\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x3B\\x00\\x04\\x00\\x15\\x00\\x00\\x00\\x26\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x32\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\\x2A\\x01\\x00\\x00\\x12\\x00\\x00\\x00\"\n\"\\x34\\x00\\x06\\x00\\x21\\x00\\x00\\x00\\x2B\\x01\\x00\\x00\\xAB\\x00\\x00\\x00\\x2A\\x01\\x00\\x00\\x20\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x15\\x00\\x00\\x00\"\n\"\\x2F\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\\x35\\x01\\x00\\x00\\x0C\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x36\\x01\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x15\\x00\\x00\\x00\\x43\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\"\n\"\\x49\\x01\\x00\\x00\\x0E\\x00\\x00\\x00\\x2C\\x00\\x06\\x00\\x07\\x00\\x00\\x00\\x56\\x01\\x00\\x00\\x56\\x00\\x00\\x00\\x56\\x00\\x00\\x00\\x68\\x00\\x00\\x00\"\n\"\\x32\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\\x57\\x01\\x00\\x00\\x10\\x00\\x00\\x00\\x34\\x00\\x06\\x00\\x21\\x00\\x00\\x00\\x58\\x01\\x00\\x00\\xAB\\x00\\x00\\x00\"\n\"\\x57\\x01\\x00\\x00\\x20\\x00\\x00\\x00\\x32\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x59\\x01\\x00\\x00\\x00\\x00\\x81\\x43\\x3B\\x00\\x04\\x00\\x15\\x00\\x00\\x00\"\n\"\\x69\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x77\\x01\\x00\\x00\\x00\\x00\\x00\\x42\\x32\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\"\n\"\\x86\\x01\\x00\\x00\\x13\\x00\\x00\\x00\\x34\\x00\\x06\\x00\\x21\\x00\\x00\\x00\\x87\\x01\\x00\\x00\\xAB\\x00\\x00\\x00\\x86\\x01\\x00\\x00\\x20\\x00\\x00\\x00\"\n\"\\x2B\\x00\\x04\\x00\\x30\\x00\\x00\\x00\\x8F\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x18\\x00\\x04\\x00\\x92\\x01\\x00\\x00\\x07\\x00\\x00\\x00\\x03\\x00\\x00\\x00\"\n\"\\x20\\x00\\x04\\x00\\x93\\x01\\x00\\x00\\x01\\x00\\x00\\x00\\x92\\x01\\x00\\x00\\x3B\\x00\\x04\\x00\\x93\\x01\\x00\\x00\\x94\\x01\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x2B\\x00\\x04\\x00\\x30\\x00\\x00\\x00\\x98\\x01\\x00\\x00\\x01\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\\x9B\\x01\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x2B\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\\xA2\\x01\\x00\\x00\\x02\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\xA7\\x01\\x00\\x00\\x02\\x00\\x00\\x00\\x3A\\x00\\x00\\x00\"\n\"\\x3B\\x00\\x04\\x00\\x15\\x00\\x00\\x00\\xB1\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x19\\x00\\x09\\x00\\xB6\\x01\\x00\\x00\\x06\\x00\\x00\\x00\\x03\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x1B\\x00\\x03\\x00\\xB7\\x01\\x00\\x00\\xB6\\x01\\x00\\x00\"\n\"\\x20\\x00\\x04\\x00\\xB8\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\xB7\\x01\\x00\\x00\\x3B\\x00\\x04\\x00\\xB8\\x01\\x00\\x00\\xB9\\x01\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x3B\\x00\\x04\\x00\\x1C\\x01\\x00\\x00\\xC4\\x01\\x00\\x00\\x01\\x00\\x00\\x00\\x32\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\\xCB\\x01\\x00\\x00\\x11\\x00\\x00\\x00\"\n\"\\x34\\x00\\x06\\x00\\x21\\x00\\x00\\x00\\xCC\\x01\\x00\\x00\\xAB\\x00\\x00\\x00\\xCB\\x01\\x00\\x00\\x20\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x15\\x00\\x00\\x00\"\n\"\\xD0\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\xDE\\x01\\x00\\x00\\x03\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\xDE\\x01\\x00\\x00\"\n\"\\xDF\\x01\\x00\\x00\\x03\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\\xE1\\x01\\x00\\x00\\x12\\x00\\x00\\x00\\x1E\\x00\\x0C\\x00\\xE7\\x01\\x00\\x00\"\n\"\\x3A\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\"\n\"\\x16\\x00\\x00\\x00\\x16\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\xE8\\x01\\x00\\x00\\x02\\x00\\x00\\x00\\xE7\\x01\\x00\\x00\\x3B\\x00\\x04\\x00\\xE8\\x01\\x00\\x00\"\n\"\\xE9\\x01\\x00\\x00\\x02\\x00\\x00\\x00\\x36\\x00\\x05\\x00\\x02\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\"\n\"\\x05\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x17\\x00\\x00\\x00\\x18\\x01\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x17\\x00\\x00\\x00\\x1E\\x01\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x08\\x00\\x00\\x00\\x20\\x01\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x0E\\x00\\x00\\x00\\x25\\x01\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x0E\\x00\\x00\\x00\\x2E\\x01\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x0E\\x00\\x00\\x00\\x42\\x01\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x08\\x00\\x00\\x00\\x55\\x01\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x0E\\x00\\x00\\x00\\x68\\x01\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x08\\x00\\x00\\x00\\x6D\\x01\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x2C\\x00\\x00\\x00\\x72\\x01\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x08\\x00\\x00\\x00\\x8A\\x01\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x0E\\x00\\x00\\x00\\xCF\\x01\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x0E\\x00\\x00\\x00\\xE5\\x01\\x00\\x00\\x07\\x00\\x00\\x00\\xBA\\x00\\x05\\x00\\x21\\x00\\x00\\x00\\x15\\x01\\x00\\x00\"\n\"\\xE5\\x00\\x00\\x00\\x56\\x00\\x00\\x00\\xF7\\x00\\x03\\x00\\x17\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\\x15\\x01\\x00\\x00\\x16\\x01\\x00\\x00\"\n\"\\x23\\x01\\x00\\x00\\xF8\\x00\\x02\\x00\\x16\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x1F\\x01\\x00\\x00\\x1B\\x01\\x00\\x00\\x3E\\x00\\x03\\x00\"\n\"\\x1E\\x01\\x00\\x00\\x1F\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x21\\x01\\x00\\x00\\x1D\\x01\\x00\\x00\\x3E\\x00\\x03\\x00\\x20\\x01\\x00\\x00\"\n\"\\x21\\x01\\x00\\x00\\x39\\x00\\x07\\x00\\x16\\x00\\x00\\x00\\x22\\x01\\x00\\x00\\x1C\\x00\\x00\\x00\\x19\\x01\\x00\\x00\\x1E\\x01\\x00\\x00\\x20\\x01\\x00\\x00\"\n\"\\x3E\\x00\\x03\\x00\\x18\\x01\\x00\\x00\\x22\\x01\\x00\\x00\\xF9\\x00\\x02\\x00\\x17\\x01\\x00\\x00\\xF8\\x00\\x02\\x00\\x23\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x16\\x00\\x00\\x00\\x24\\x01\\x00\\x00\\x1B\\x01\\x00\\x00\\x3E\\x00\\x03\\x00\\x18\\x01\\x00\\x00\\x24\\x01\\x00\\x00\\xF9\\x00\\x02\\x00\\x17\\x01\\x00\\x00\"\n\"\\xF8\\x00\\x02\\x00\\x17\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x14\\x00\\x00\\x00\\x27\\x01\\x00\\x00\\x26\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x16\\x00\\x00\\x00\"\n\"\\x28\\x01\\x00\\x00\\x18\\x01\\x00\\x00\\x57\\x00\\x05\\x00\\x0D\\x00\\x00\\x00\\x29\\x01\\x00\\x00\\x27\\x01\\x00\\x00\\x28\\x01\\x00\\x00\\x3E\\x00\\x03\\x00\"\n\"\\x25\\x01\\x00\\x00\\x29\\x01\\x00\\x00\\xF7\\x00\\x03\\x00\\x2D\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\\x2B\\x01\\x00\\x00\\x2C\\x01\\x00\\x00\"\n\"\\x2D\\x01\\x00\\x00\\xF8\\x00\\x02\\x00\\x2C\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x14\\x00\\x00\\x00\\x30\\x01\\x00\\x00\\x2F\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x16\\x00\\x00\\x00\\x31\\x01\\x00\\x00\\x18\\x01\\x00\\x00\\x57\\x00\\x05\\x00\\x0D\\x00\\x00\\x00\\x32\\x01\\x00\\x00\\x30\\x01\\x00\\x00\\x31\\x01\\x00\\x00\"\n\"\\x3E\\x00\\x03\\x00\\x2E\\x01\\x00\\x00\\x32\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\x33\\x01\\x00\\x00\\x2E\\x01\\x00\\x00\\x4F\\x00\\x08\\x00\"\n\"\\x07\\x00\\x00\\x00\\x34\\x01\\x00\\x00\\x33\\x01\\x00\\x00\\x33\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x41\\x00\\x05\\x00\"\n\"\\x36\\x01\\x00\\x00\\x37\\x01\\x00\\x00\\x3F\\x00\\x00\\x00\\x35\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x38\\x01\\x00\\x00\\x37\\x01\\x00\\x00\"\n\"\\x85\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x39\\x01\\x00\\x00\\x34\\x01\\x00\\x00\\x38\\x01\\x00\\x00\\x41\\x00\\x05\\x00\\x2C\\x00\\x00\\x00\\x3A\\x01\\x00\\x00\"\n\"\\x2E\\x01\\x00\\x00\\x35\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x3B\\x01\\x00\\x00\\x3A\\x01\\x00\\x00\\x8E\\x00\\x05\\x00\\x07\\x00\\x00\\x00\"\n\"\\x3C\\x01\\x00\\x00\\x39\\x01\\x00\\x00\\x3B\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\x3D\\x01\\x00\\x00\\x25\\x01\\x00\\x00\\x4F\\x00\\x08\\x00\"\n\"\\x07\\x00\\x00\\x00\\x3E\\x01\\x00\\x00\\x3D\\x01\\x00\\x00\\x3D\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x81\\x00\\x05\\x00\"\n\"\\x07\\x00\\x00\\x00\\x3F\\x01\\x00\\x00\\x3E\\x01\\x00\\x00\\x3C\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\x40\\x01\\x00\\x00\\x25\\x01\\x00\\x00\"\n\"\\x4F\\x00\\x09\\x00\\x0D\\x00\\x00\\x00\\x41\\x01\\x00\\x00\\x40\\x01\\x00\\x00\\x3F\\x01\\x00\\x00\\x04\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x03\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x25\\x01\\x00\\x00\\x41\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x14\\x00\\x00\\x00\\x44\\x01\\x00\\x00\\x43\\x01\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x45\\x01\\x00\\x00\\x18\\x01\\x00\\x00\\x57\\x00\\x05\\x00\\x0D\\x00\\x00\\x00\\x46\\x01\\x00\\x00\\x44\\x01\\x00\\x00\"\n\"\\x45\\x01\\x00\\x00\\x3E\\x00\\x03\\x00\\x42\\x01\\x00\\x00\\x46\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\x47\\x01\\x00\\x00\\x42\\x01\\x00\\x00\"\n\"\\x4F\\x00\\x08\\x00\\x07\\x00\\x00\\x00\\x48\\x01\\x00\\x00\\x47\\x01\\x00\\x00\\x47\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x41\\x00\\x05\\x00\\x36\\x01\\x00\\x00\\x4A\\x01\\x00\\x00\\x3F\\x00\\x00\\x00\\x49\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x4B\\x01\\x00\\x00\"\n\"\\x4A\\x01\\x00\\x00\\x85\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x4C\\x01\\x00\\x00\\x48\\x01\\x00\\x00\\x4B\\x01\\x00\\x00\\x41\\x00\\x05\\x00\\x2C\\x00\\x00\\x00\"\n\"\\x4D\\x01\\x00\\x00\\x42\\x01\\x00\\x00\\x35\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x4E\\x01\\x00\\x00\\x4D\\x01\\x00\\x00\\x8E\\x00\\x05\\x00\"\n\"\\x07\\x00\\x00\\x00\\x4F\\x01\\x00\\x00\\x4C\\x01\\x00\\x00\\x4E\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\x50\\x01\\x00\\x00\\x25\\x01\\x00\\x00\"\n\"\\x4F\\x00\\x08\\x00\\x07\\x00\\x00\\x00\\x51\\x01\\x00\\x00\\x50\\x01\\x00\\x00\\x50\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x81\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x52\\x01\\x00\\x00\\x51\\x01\\x00\\x00\\x4F\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\x53\\x01\\x00\\x00\"\n\"\\x25\\x01\\x00\\x00\\x4F\\x00\\x09\\x00\\x0D\\x00\\x00\\x00\\x54\\x01\\x00\\x00\\x53\\x01\\x00\\x00\\x52\\x01\\x00\\x00\\x04\\x00\\x00\\x00\\x05\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x25\\x01\\x00\\x00\\x54\\x01\\x00\\x00\\xF9\\x00\\x02\\x00\\x2D\\x01\\x00\\x00\\xF8\\x00\\x02\\x00\"\n\"\\x2D\\x01\\x00\\x00\\x3E\\x00\\x03\\x00\\x55\\x01\\x00\\x00\\x56\\x01\\x00\\x00\\xBA\\x00\\x05\\x00\\x21\\x00\\x00\\x00\\x5A\\x01\\x00\\x00\\x59\\x01\\x00\\x00\"\n\"\\x56\\x00\\x00\\x00\\xA6\\x00\\x05\\x00\\x21\\x00\\x00\\x00\\x5B\\x01\\x00\\x00\\x58\\x01\\x00\\x00\\x5A\\x01\\x00\\x00\\xF7\\x00\\x03\\x00\\x5D\\x01\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\\x5B\\x01\\x00\\x00\\x5C\\x01\\x00\\x00\\x5D\\x01\\x00\\x00\\xF8\\x00\\x02\\x00\\x5C\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x14\\x00\\x00\\x00\\x5E\\x01\\x00\\x00\\x19\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x5F\\x01\\x00\\x00\\x18\\x01\\x00\\x00\\x57\\x00\\x05\\x00\"\n\"\\x0D\\x00\\x00\\x00\\x60\\x01\\x00\\x00\\x5E\\x01\\x00\\x00\\x5F\\x01\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x61\\x01\\x00\\x00\\x60\\x01\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x62\\x01\\x00\\x00\\x60\\x01\\x00\\x00\\x01\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\"\n\"\\x63\\x01\\x00\\x00\\x60\\x01\\x00\\x00\\x02\\x00\\x00\\x00\\x50\\x00\\x06\\x00\\x07\\x00\\x00\\x00\\x64\\x01\\x00\\x00\\x61\\x01\\x00\\x00\\x62\\x01\\x00\\x00\"\n\"\\x63\\x01\\x00\\x00\\x50\\x00\\x06\\x00\\x07\\x00\\x00\\x00\\x65\\x01\\x00\\x00\\xD6\\x00\\x00\\x00\\xD6\\x00\\x00\\x00\\xD6\\x00\\x00\\x00\\x83\\x00\\x05\\x00\"\n\"\\x07\\x00\\x00\\x00\\x66\\x01\\x00\\x00\\x64\\x01\\x00\\x00\\x65\\x01\\x00\\x00\\x0C\\x00\\x06\\x00\\x07\\x00\\x00\\x00\\x67\\x01\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x45\\x00\\x00\\x00\\x66\\x01\\x00\\x00\\x3E\\x00\\x03\\x00\\x55\\x01\\x00\\x00\\x67\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x14\\x00\\x00\\x00\\x6A\\x01\\x00\\x00\"\n\"\\x69\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x6B\\x01\\x00\\x00\\x18\\x01\\x00\\x00\\x57\\x00\\x05\\x00\\x0D\\x00\\x00\\x00\\x6C\\x01\\x00\\x00\"\n\"\\x6A\\x01\\x00\\x00\\x6B\\x01\\x00\\x00\\x3E\\x00\\x03\\x00\\x68\\x01\\x00\\x00\\x6C\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x6E\\x01\\x00\\x00\"\n\"\\x1D\\x01\\x00\\x00\\x0C\\x00\\x06\\x00\\x07\\x00\\x00\\x00\\x6F\\x01\\x00\\x00\\x01\\x00\\x00\\x00\\x45\\x00\\x00\\x00\\x6E\\x01\\x00\\x00\\x81\\x00\\x05\\x00\"\n\"\\x07\\x00\\x00\\x00\\x70\\x01\\x00\\x00\\x6F\\x01\\x00\\x00\\x56\\x01\\x00\\x00\\x0C\\x00\\x06\\x00\\x07\\x00\\x00\\x00\\x71\\x01\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x45\\x00\\x00\\x00\\x70\\x01\\x00\\x00\\x3E\\x00\\x03\\x00\\x6D\\x01\\x00\\x00\\x71\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x73\\x01\\x00\\x00\"\n\"\\x6D\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x74\\x01\\x00\\x00\\x55\\x01\\x00\\x00\\x94\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x75\\x01\\x00\\x00\"\n\"\\x73\\x01\\x00\\x00\\x74\\x01\\x00\\x00\\x0C\\x00\\x07\\x00\\x06\\x00\\x00\\x00\\x76\\x01\\x00\\x00\\x01\\x00\\x00\\x00\\x28\\x00\\x00\\x00\\x75\\x01\\x00\\x00\"\n\"\\x56\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x2C\\x00\\x00\\x00\\x78\\x01\\x00\\x00\\x68\\x01\\x00\\x00\\x35\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\"\n\"\\x79\\x01\\x00\\x00\\x78\\x01\\x00\\x00\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x7A\\x01\\x00\\x00\\x77\\x01\\x00\\x00\\x79\\x01\\x00\\x00\\x0C\\x00\\x07\\x00\"\n\"\\x06\\x00\\x00\\x00\\x7B\\x01\\x00\\x00\\x01\\x00\\x00\\x00\\x1A\\x00\\x00\\x00\\x76\\x01\\x00\\x00\\x7A\\x01\\x00\\x00\\x3E\\x00\\x03\\x00\\x72\\x01\\x00\\x00\"\n\"\\x7B\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x7C\\x01\\x00\\x00\\x72\\x01\\x00\\x00\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x7D\\x01\\x00\\x00\"\n\"\\x59\\x01\\x00\\x00\\x7C\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\x7E\\x01\\x00\\x00\\x68\\x01\\x00\\x00\\x4F\\x00\\x08\\x00\\x07\\x00\\x00\\x00\"\n\"\\x7F\\x01\\x00\\x00\\x7E\\x01\\x00\\x00\\x7E\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x8E\\x00\\x05\\x00\\x07\\x00\\x00\\x00\"\n\"\\x80\\x01\\x00\\x00\\x7F\\x01\\x00\\x00\\x7D\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\x81\\x01\\x00\\x00\\x25\\x01\\x00\\x00\\x4F\\x00\\x08\\x00\"\n\"\\x07\\x00\\x00\\x00\\x82\\x01\\x00\\x00\\x81\\x01\\x00\\x00\\x81\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x81\\x00\\x05\\x00\"\n\"\\x07\\x00\\x00\\x00\\x83\\x01\\x00\\x00\\x82\\x01\\x00\\x00\\x80\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\x84\\x01\\x00\\x00\\x25\\x01\\x00\\x00\"\n\"\\x4F\\x00\\x09\\x00\\x0D\\x00\\x00\\x00\\x85\\x01\\x00\\x00\\x84\\x01\\x00\\x00\\x83\\x01\\x00\\x00\\x04\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x03\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x25\\x01\\x00\\x00\\x85\\x01\\x00\\x00\\xF9\\x00\\x02\\x00\\x5D\\x01\\x00\\x00\\xF8\\x00\\x02\\x00\\x5D\\x01\\x00\\x00\"\n\"\\xF7\\x00\\x03\\x00\\x89\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\\x87\\x01\\x00\\x00\\x88\\x01\\x00\\x00\\x89\\x01\\x00\\x00\\xF8\\x00\\x02\\x00\"\n\"\\x88\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x8B\\x01\\x00\\x00\\x1D\\x01\\x00\\x00\\x7F\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x8C\\x01\\x00\\x00\"\n\"\\x8B\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x8D\\x01\\x00\\x00\\x55\\x01\\x00\\x00\\x0C\\x00\\x07\\x00\\x07\\x00\\x00\\x00\\x8E\\x01\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x47\\x00\\x00\\x00\\x8C\\x01\\x00\\x00\\x8D\\x01\\x00\\x00\\x3E\\x00\\x03\\x00\\x8A\\x01\\x00\\x00\\x8E\\x01\\x00\\x00\\x41\\x00\\x05\\x00\"\n\"\\x2C\\x00\\x00\\x00\\x90\\x01\\x00\\x00\\x8A\\x01\\x00\\x00\\x8F\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x91\\x01\\x00\\x00\\x90\\x01\\x00\\x00\"\n\"\\x41\\x00\\x05\\x00\\x1C\\x01\\x00\\x00\\x95\\x01\\x00\\x00\\x94\\x01\\x00\\x00\\x20\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x96\\x01\\x00\\x00\"\n\"\\x95\\x01\\x00\\x00\\x8E\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x97\\x01\\x00\\x00\\x96\\x01\\x00\\x00\\x91\\x01\\x00\\x00\\x41\\x00\\x05\\x00\\x2C\\x00\\x00\\x00\"\n\"\\x99\\x01\\x00\\x00\\x8A\\x01\\x00\\x00\\x98\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x9A\\x01\\x00\\x00\\x99\\x01\\x00\\x00\\x41\\x00\\x05\\x00\"\n\"\\x1C\\x01\\x00\\x00\\x9C\\x01\\x00\\x00\\x94\\x01\\x00\\x00\\x9B\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x9D\\x01\\x00\\x00\\x9C\\x01\\x00\\x00\"\n\"\\x8E\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x9E\\x01\\x00\\x00\\x9D\\x01\\x00\\x00\\x9A\\x01\\x00\\x00\\x81\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x9F\\x01\\x00\\x00\"\n\"\\x97\\x01\\x00\\x00\\x9E\\x01\\x00\\x00\\x41\\x00\\x05\\x00\\x2C\\x00\\x00\\x00\\xA0\\x01\\x00\\x00\\x8A\\x01\\x00\\x00\\x31\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x06\\x00\\x00\\x00\\xA1\\x01\\x00\\x00\\xA0\\x01\\x00\\x00\\x41\\x00\\x05\\x00\\x1C\\x01\\x00\\x00\\xA3\\x01\\x00\\x00\\x94\\x01\\x00\\x00\\xA2\\x01\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\xA4\\x01\\x00\\x00\\xA3\\x01\\x00\\x00\\x8E\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\xA5\\x01\\x00\\x00\\xA4\\x01\\x00\\x00\"\n\"\\xA1\\x01\\x00\\x00\\x81\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\xA6\\x01\\x00\\x00\\x9F\\x01\\x00\\x00\\xA5\\x01\\x00\\x00\\x3E\\x00\\x03\\x00\\x8A\\x01\\x00\\x00\"\n\"\\xA6\\x01\\x00\\x00\\x41\\x00\\x05\\x00\\xA7\\x01\\x00\\x00\\xA8\\x01\\x00\\x00\\x3F\\x00\\x00\\x00\\x9B\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x3A\\x00\\x00\\x00\"\n\"\\xA9\\x01\\x00\\x00\\xA8\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\xAA\\x01\\x00\\x00\\x8A\\x01\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\"\n\"\\xAB\\x01\\x00\\x00\\xAA\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\xAC\\x01\\x00\\x00\\xAA\\x01\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\xAD\\x01\\x00\\x00\\xAA\\x01\\x00\\x00\\x02\\x00\\x00\\x00\\x50\\x00\\x07\\x00\\x0D\\x00\\x00\\x00\\xAE\\x01\\x00\\x00\"\n\"\\xAB\\x01\\x00\\x00\\xAC\\x01\\x00\\x00\\xAD\\x01\\x00\\x00\\x56\\x00\\x00\\x00\\x91\\x00\\x05\\x00\\x0D\\x00\\x00\\x00\\xAF\\x01\\x00\\x00\\xA9\\x01\\x00\\x00\"\n\"\\xAE\\x01\\x00\\x00\\x4F\\x00\\x08\\x00\\x07\\x00\\x00\\x00\\xB0\\x01\\x00\\x00\\xAF\\x01\\x00\\x00\\xAF\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x8A\\x01\\x00\\x00\\xB0\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x14\\x00\\x00\\x00\\xB2\\x01\\x00\\x00\\xB1\\x01\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\xB3\\x01\\x00\\x00\\x18\\x01\\x00\\x00\\x57\\x00\\x05\\x00\\x0D\\x00\\x00\\x00\\xB4\\x01\\x00\\x00\\xB2\\x01\\x00\\x00\"\n\"\\xB3\\x01\\x00\\x00\\x4F\\x00\\x08\\x00\\x07\\x00\\x00\\x00\\xB5\\x01\\x00\\x00\\xB4\\x01\\x00\\x00\\xB4\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\xB7\\x01\\x00\\x00\\xBA\\x01\\x00\\x00\\xB9\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\xBB\\x01\\x00\\x00\"\n\"\\x8A\\x01\\x00\\x00\\x57\\x00\\x05\\x00\\x0D\\x00\\x00\\x00\\xBC\\x01\\x00\\x00\\xBA\\x01\\x00\\x00\\xBB\\x01\\x00\\x00\\x4F\\x00\\x08\\x00\\x07\\x00\\x00\\x00\"\n\"\\xBD\\x01\\x00\\x00\\xBC\\x01\\x00\\x00\\xBC\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x07\\x00\\x00\\x00\"\n\"\\xBE\\x01\\x00\\x00\\xB5\\x01\\x00\\x00\\xBD\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\xBF\\x01\\x00\\x00\\x25\\x01\\x00\\x00\\x4F\\x00\\x08\\x00\"\n\"\\x07\\x00\\x00\\x00\\xC0\\x01\\x00\\x00\\xBF\\x01\\x00\\x00\\xBF\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x81\\x00\\x05\\x00\"\n\"\\x07\\x00\\x00\\x00\\xC1\\x01\\x00\\x00\\xC0\\x01\\x00\\x00\\xBE\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\xC2\\x01\\x00\\x00\\x25\\x01\\x00\\x00\"\n\"\\x4F\\x00\\x09\\x00\\x0D\\x00\\x00\\x00\\xC3\\x01\\x00\\x00\\xC2\\x01\\x00\\x00\\xC1\\x01\\x00\\x00\\x04\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x03\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x25\\x01\\x00\\x00\\xC3\\x01\\x00\\x00\\xF9\\x00\\x02\\x00\\x89\\x01\\x00\\x00\\xF8\\x00\\x02\\x00\\x89\\x01\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\xC5\\x01\\x00\\x00\\xC4\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\xC6\\x01\\x00\\x00\\x25\\x01\\x00\\x00\"\n\"\\x4F\\x00\\x08\\x00\\x07\\x00\\x00\\x00\\xC7\\x01\\x00\\x00\\xC6\\x01\\x00\\x00\\xC6\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x85\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\xC8\\x01\\x00\\x00\\xC7\\x01\\x00\\x00\\xC5\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\xC9\\x01\\x00\\x00\"\n\"\\x25\\x01\\x00\\x00\\x4F\\x00\\x09\\x00\\x0D\\x00\\x00\\x00\\xCA\\x01\\x00\\x00\\xC9\\x01\\x00\\x00\\xC8\\x01\\x00\\x00\\x04\\x00\\x00\\x00\\x05\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x25\\x01\\x00\\x00\\xCA\\x01\\x00\\x00\\xF7\\x00\\x03\\x00\\xCE\\x01\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\xFA\\x00\\x04\\x00\\xCC\\x01\\x00\\x00\\xCD\\x01\\x00\\x00\\xCE\\x01\\x00\\x00\\xF8\\x00\\x02\\x00\\xCD\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x14\\x00\\x00\\x00\"\n\"\\xD1\\x01\\x00\\x00\\xD0\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\xD2\\x01\\x00\\x00\\x18\\x01\\x00\\x00\\x57\\x00\\x05\\x00\\x0D\\x00\\x00\\x00\"\n\"\\xD3\\x01\\x00\\x00\\xD1\\x01\\x00\\x00\\xD2\\x01\\x00\\x00\\x3E\\x00\\x03\\x00\\xCF\\x01\\x00\\x00\\xD3\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\"\n\"\\xD4\\x01\\x00\\x00\\xCF\\x01\\x00\\x00\\x4F\\x00\\x08\\x00\\x07\\x00\\x00\\x00\\xD5\\x01\\x00\\x00\\xD4\\x01\\x00\\x00\\xD4\\x01\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x2C\\x00\\x00\\x00\\xD6\\x01\\x00\\x00\\xCF\\x01\\x00\\x00\\x35\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x06\\x00\\x00\\x00\\xD7\\x01\\x00\\x00\\xD6\\x01\\x00\\x00\\x8E\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\xD8\\x01\\x00\\x00\\xD5\\x01\\x00\\x00\\xD7\\x01\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\xD9\\x01\\x00\\x00\\x25\\x01\\x00\\x00\\x4F\\x00\\x08\\x00\\x07\\x00\\x00\\x00\\xDA\\x01\\x00\\x00\\xD9\\x01\\x00\\x00\"\n\"\\xD9\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x81\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\xDB\\x01\\x00\\x00\\xDA\\x01\\x00\\x00\"\n\"\\xD8\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\xDC\\x01\\x00\\x00\\x25\\x01\\x00\\x00\\x4F\\x00\\x09\\x00\\x0D\\x00\\x00\\x00\\xDD\\x01\\x00\\x00\"\n\"\\xDC\\x01\\x00\\x00\\xDB\\x01\\x00\\x00\\x04\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x25\\x01\\x00\\x00\"\n\"\\xDD\\x01\\x00\\x00\\xF9\\x00\\x02\\x00\\xCE\\x01\\x00\\x00\\xF8\\x00\\x02\\x00\\xCE\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\xE0\\x01\\x00\\x00\"\n\"\\x25\\x01\\x00\\x00\\x41\\x00\\x05\\x00\\x73\\x00\\x00\\x00\\xE2\\x01\\x00\\x00\\x3F\\x00\\x00\\x00\\xE1\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\"\n\"\\xE3\\x01\\x00\\x00\\xE2\\x01\\x00\\x00\\x85\\x00\\x05\\x00\\x0D\\x00\\x00\\x00\\xE4\\x01\\x00\\x00\\xE0\\x01\\x00\\x00\\xE3\\x01\\x00\\x00\\x3E\\x00\\x03\\x00\"\n\"\\xE5\\x01\\x00\\x00\\xE4\\x01\\x00\\x00\\x39\\x00\\x05\\x00\\x0D\\x00\\x00\\x00\\xE6\\x01\\x00\\x00\\x11\\x00\\x00\\x00\\xE5\\x01\\x00\\x00\\x3E\\x00\\x03\\x00\"\n\"\\xDF\\x01\\x00\\x00\\xE6\\x01\\x00\\x00\\xFD\\x00\\x01\\x00\\x38\\x00\\x01\\x00\\x36\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x09\\x00\\x00\\x00\\x37\\x00\\x03\\x00\\x08\\x00\\x00\\x00\\x0A\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x0C\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x2C\\x00\\x00\\x00\"\n\"\\x2D\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x2C\\x00\\x00\\x00\\x39\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\xF7\\x00\\x03\\x00\\x25\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\\x23\\x00\\x00\\x00\\x24\\x00\\x00\\x00\\x25\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x24\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x07\\x00\\x00\\x00\\x26\\x00\\x00\\x00\\x0A\\x00\\x00\\x00\\xFE\\x00\\x02\\x00\\x26\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x25\\x00\\x00\\x00\\xF7\\x00\\x03\\x00\"\n\"\\x2B\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\\x29\\x00\\x00\\x00\\x2A\\x00\\x00\\x00\\x4D\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x2A\\x00\\x00\\x00\"\n\"\\x41\\x00\\x05\\x00\\x32\\x00\\x00\\x00\\x33\\x00\\x00\\x00\\x2F\\x00\\x00\\x00\\x31\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x34\\x00\\x00\\x00\"\n\"\\x33\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x32\\x00\\x00\\x00\\x36\\x00\\x00\\x00\\x2F\\x00\\x00\\x00\\x35\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\"\n\"\\x37\\x00\\x00\\x00\\x36\\x00\\x00\\x00\\x88\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x38\\x00\\x00\\x00\\x34\\x00\\x00\\x00\\x37\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\"\n\"\\x2D\\x00\\x00\\x00\\x38\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x41\\x00\\x00\\x00\\x42\\x00\\x00\\x00\\x3F\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x06\\x00\\x00\\x00\\x43\\x00\\x00\\x00\\x42\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x44\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\\x83\\x00\\x05\\x00\"\n\"\\x06\\x00\\x00\\x00\\x45\\x00\\x00\\x00\\x43\\x00\\x00\\x00\\x44\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x41\\x00\\x00\\x00\\x46\\x00\\x00\\x00\\x3F\\x00\\x00\\x00\"\n\"\\x40\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x47\\x00\\x00\\x00\\x46\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x41\\x00\\x00\\x00\\x49\\x00\\x00\\x00\"\n\"\\x3F\\x00\\x00\\x00\\x48\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x4A\\x00\\x00\\x00\\x49\\x00\\x00\\x00\\x83\\x00\\x05\\x00\\x06\\x00\\x00\\x00\"\n\"\\x4B\\x00\\x00\\x00\\x47\\x00\\x00\\x00\\x4A\\x00\\x00\\x00\\x88\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x4C\\x00\\x00\\x00\\x45\\x00\\x00\\x00\\x4B\\x00\\x00\\x00\"\n\"\\x3E\\x00\\x03\\x00\\x39\\x00\\x00\\x00\\x4C\\x00\\x00\\x00\\xF9\\x00\\x02\\x00\\x2B\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x4D\\x00\\x00\\x00\\x41\\x00\\x05\\x00\"\n\"\\x41\\x00\\x00\\x00\\x4E\\x00\\x00\\x00\\x3F\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x4F\\x00\\x00\\x00\\x4E\\x00\\x00\\x00\"\n\"\\x41\\x00\\x05\\x00\\x32\\x00\\x00\\x00\\x50\\x00\\x00\\x00\\x2F\\x00\\x00\\x00\\x31\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x51\\x00\\x00\\x00\"\n\"\\x50\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x52\\x00\\x00\\x00\\x4F\\x00\\x00\\x00\\x51\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x32\\x00\\x00\\x00\"\n\"\\x53\\x00\\x00\\x00\\x2F\\x00\\x00\\x00\\x35\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x54\\x00\\x00\\x00\\x53\\x00\\x00\\x00\\x88\\x00\\x05\\x00\"\n\"\\x06\\x00\\x00\\x00\\x55\\x00\\x00\\x00\\x52\\x00\\x00\\x00\\x54\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x2D\\x00\\x00\\x00\\x55\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x06\\x00\\x00\\x00\\x57\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x41\\x00\\x00\\x00\\x58\\x00\\x00\\x00\\x3F\\x00\\x00\\x00\\x48\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x59\\x00\\x00\\x00\\x58\\x00\\x00\\x00\\x83\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x5A\\x00\\x00\\x00\\x57\\x00\\x00\\x00\"\n\"\\x59\\x00\\x00\\x00\\x0C\\x00\\x07\\x00\\x06\\x00\\x00\\x00\\x5B\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x28\\x00\\x00\\x00\\x56\\x00\\x00\\x00\\x5A\\x00\\x00\\x00\"\n\"\\x3E\\x00\\x03\\x00\\x2D\\x00\\x00\\x00\\x5B\\x00\\x00\\x00\\xF7\\x00\\x03\\x00\\x5F\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\\x5D\\x00\\x00\\x00\"\n\"\\x5E\\x00\\x00\\x00\\x5F\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x5E\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x60\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x61\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x62\\x00\\x00\\x00\\x61\\x00\\x00\\x00\"\n\"\\x60\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x2D\\x00\\x00\\x00\\x62\\x00\\x00\\x00\\xF9\\x00\\x02\\x00\\x5F\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x5F\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x63\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x65\\x00\\x00\\x00\\x63\\x00\\x00\\x00\"\n\"\\x64\\x00\\x00\\x00\\x7F\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x66\\x00\\x00\\x00\\x65\\x00\\x00\\x00\\x0C\\x00\\x06\\x00\\x06\\x00\\x00\\x00\\x67\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x66\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x39\\x00\\x00\\x00\\x67\\x00\\x00\\x00\\xF9\\x00\\x02\\x00\\x2B\\x00\\x00\\x00\"\n\"\\xF8\\x00\\x02\\x00\\x2B\\x00\\x00\\x00\\x41\\x00\\x06\\x00\\x41\\x00\\x00\\x00\\x6A\\x00\\x00\\x00\\x3F\\x00\\x00\\x00\\x69\\x00\\x00\\x00\\x35\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x6B\\x00\\x00\\x00\\x6A\\x00\\x00\\x00\\x83\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x6C\\x00\\x00\\x00\\x68\\x00\\x00\\x00\"\n\"\\x6B\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x6D\\x00\\x00\\x00\\x39\\x00\\x00\\x00\\x0C\\x00\\x08\\x00\\x06\\x00\\x00\\x00\\x6E\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x2B\\x00\\x00\\x00\\x6D\\x00\\x00\\x00\\x56\\x00\\x00\\x00\\x68\\x00\\x00\\x00\\x41\\x00\\x06\\x00\\x41\\x00\\x00\\x00\\x6F\\x00\\x00\\x00\"\n\"\\x3F\\x00\\x00\\x00\\x69\\x00\\x00\\x00\\x35\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x70\\x00\\x00\\x00\\x6F\\x00\\x00\\x00\\x85\\x00\\x05\\x00\"\n\"\\x06\\x00\\x00\\x00\\x71\\x00\\x00\\x00\\x6E\\x00\\x00\\x00\\x70\\x00\\x00\\x00\\x81\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x72\\x00\\x00\\x00\\x6C\\x00\\x00\\x00\"\n\"\\x71\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x39\\x00\\x00\\x00\\x72\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x73\\x00\\x00\\x00\\x74\\x00\\x00\\x00\\x3F\\x00\\x00\\x00\"\n\"\\x69\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\x75\\x00\\x00\\x00\\x74\\x00\\x00\\x00\\x4F\\x00\\x08\\x00\\x07\\x00\\x00\\x00\\x76\\x00\\x00\\x00\"\n\"\\x75\\x00\\x00\\x00\\x75\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x77\\x00\\x00\\x00\"\n\"\\x0A\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x78\\x00\\x00\\x00\\x39\\x00\\x00\\x00\\x50\\x00\\x06\\x00\\x07\\x00\\x00\\x00\\x79\\x00\\x00\\x00\"\n\"\\x78\\x00\\x00\\x00\\x78\\x00\\x00\\x00\\x78\\x00\\x00\\x00\\x0C\\x00\\x08\\x00\\x07\\x00\\x00\\x00\\x7A\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x2E\\x00\\x00\\x00\"\n\"\\x76\\x00\\x00\\x00\\x77\\x00\\x00\\x00\\x79\\x00\\x00\\x00\\xFE\\x00\\x02\\x00\\x7A\\x00\\x00\\x00\\x38\\x00\\x01\\x00\\x36\\x00\\x05\\x00\\x0D\\x00\\x00\\x00\"\n\"\\x11\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\\x37\\x00\\x03\\x00\\x0E\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x12\\x00\\x00\\x00\"\n\"\\x3B\\x00\\x04\\x00\\x08\\x00\\x00\\x00\\x83\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\xF7\\x00\\x03\\x00\\x80\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\"\n\"\\x7E\\x00\\x00\\x00\\x7F\\x00\\x00\\x00\\x80\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x7F\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\x81\\x00\\x00\\x00\"\n\"\\x10\\x00\\x00\\x00\\xFE\\x00\\x02\\x00\\x81\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x80\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\x84\\x00\\x00\\x00\"\n\"\\x10\\x00\\x00\\x00\\x4F\\x00\\x08\\x00\\x07\\x00\\x00\\x00\\x85\\x00\\x00\\x00\\x84\\x00\\x00\\x00\\x84\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x83\\x00\\x00\\x00\\x85\\x00\\x00\\x00\\x39\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x86\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\"\n\"\\x83\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x87\\x00\\x00\\x00\\x86\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\"\n\"\\x88\\x00\\x00\\x00\\x86\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x89\\x00\\x00\\x00\\x86\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x50\\x00\\x07\\x00\\x0D\\x00\\x00\\x00\\x8A\\x00\\x00\\x00\\x87\\x00\\x00\\x00\\x88\\x00\\x00\\x00\\x89\\x00\\x00\\x00\\x68\\x00\\x00\\x00\\x41\\x00\\x05\\x00\"\n\"\\x2C\\x00\\x00\\x00\\x8B\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x35\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x8C\\x00\\x00\\x00\\x8B\\x00\\x00\\x00\"\n\"\\x8E\\x00\\x05\\x00\\x0D\\x00\\x00\\x00\\x8D\\x00\\x00\\x00\\x8A\\x00\\x00\\x00\\x8C\\x00\\x00\\x00\\xFE\\x00\\x02\\x00\\x8D\\x00\\x00\\x00\\x38\\x00\\x01\\x00\"\n\"\\x36\\x00\\x05\\x00\\x16\\x00\\x00\\x00\\x1C\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x18\\x00\\x00\\x00\\x37\\x00\\x03\\x00\\x15\\x00\\x00\\x00\\x19\\x00\\x00\\x00\"\n\"\\x37\\x00\\x03\\x00\\x17\\x00\\x00\\x00\\x1A\\x00\\x00\\x00\\x37\\x00\\x03\\x00\\x08\\x00\\x00\\x00\\x1B\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x1D\\x00\\x00\\x00\"\n\"\\x3B\\x00\\x04\\x00\\x08\\x00\\x00\\x00\\x93\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x08\\x00\\x00\\x00\\xA0\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x3B\\x00\\x04\\x00\\x2C\\x00\\x00\\x00\\xAC\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x2C\\x00\\x00\\x00\\xC2\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x3B\\x00\\x04\\x00\\x17\\x00\\x00\\x00\\xE9\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x17\\x00\\x00\\x00\\xF0\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\xF7\\x00\\x03\\x00\\x92\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\\x90\\x00\\x00\\x00\\x91\\x00\\x00\\x00\\xE4\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\"\n\"\\x91\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x94\\x00\\x00\\x00\\x1B\\x00\\x00\\x00\\x0C\\x00\\x06\\x00\\x07\\x00\\x00\\x00\\x95\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x45\\x00\\x00\\x00\\x94\\x00\\x00\\x00\\x4F\\x00\\x07\\x00\\x16\\x00\\x00\\x00\\x96\\x00\\x00\\x00\\x95\\x00\\x00\\x00\\x95\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x8E\\x00\\x05\\x00\\x16\\x00\\x00\\x00\\x98\\x00\\x00\\x00\\x96\\x00\\x00\\x00\\x97\\x00\\x00\\x00\\x8E\\x00\\x05\\x00\"\n\"\\x16\\x00\\x00\\x00\\x99\\x00\\x00\\x00\\x98\\x00\\x00\\x00\\x68\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x16\\x00\\x00\\x00\\x9C\\x00\\x00\\x00\\x99\\x00\\x00\\x00\"\n\"\\x9B\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x9D\\x00\\x00\\x00\\x9C\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\"\n\"\\x9E\\x00\\x00\\x00\\x9C\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x50\\x00\\x06\\x00\\x07\\x00\\x00\\x00\\x9F\\x00\\x00\\x00\\x9D\\x00\\x00\\x00\\x9E\\x00\\x00\\x00\"\n\"\\x9A\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x93\\x00\\x00\\x00\\x9F\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\xA1\\x00\\x00\\x00\\x1A\\x00\\x00\\x00\"\n\"\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\xA2\\x00\\x00\\x00\\xA1\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\xA3\\x00\\x00\\x00\"\n\"\\xA1\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x50\\x00\\x05\\x00\\x16\\x00\\x00\\x00\\xA4\\x00\\x00\\x00\\xA2\\x00\\x00\\x00\\xA3\\x00\\x00\\x00\\x51\\x00\\x05\\x00\"\n\"\\x06\\x00\\x00\\x00\\xA5\\x00\\x00\\x00\\xA4\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\xA6\\x00\\x00\\x00\\xA4\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x50\\x00\\x06\\x00\\x07\\x00\\x00\\x00\\xA7\\x00\\x00\\x00\\xA5\\x00\\x00\\x00\\xA6\\x00\\x00\\x00\\x68\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\"\n\"\\xA0\\x00\\x00\\x00\\xA7\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\xA9\\x00\\x00\\x00\\x93\\x00\\x00\\x00\\x50\\x00\\x06\\x00\\x07\\x00\\x00\\x00\"\n\"\\xAA\\x00\\x00\\x00\\xA8\\x00\\x00\\x00\\xA8\\x00\\x00\\x00\\xA8\\x00\\x00\\x00\\x88\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\xAB\\x00\\x00\\x00\\xA9\\x00\\x00\\x00\"\n\"\\xAA\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x93\\x00\\x00\\x00\\xAB\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\xAC\\x00\\x00\\x00\\x68\\x00\\x00\\x00\\xF9\\x00\\x02\\x00\"\n\"\\xAD\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\xAD\\x00\\x00\\x00\\xF6\\x00\\x04\\x00\\xAF\\x00\\x00\\x00\\xB0\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xF9\\x00\\x02\\x00\"\n\"\\xB1\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\xB1\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\xB2\\x00\\x00\\x00\\xAC\\x00\\x00\\x00\\xB8\\x00\\x05\\x00\"\n\"\\x21\\x00\\x00\\x00\\xB3\\x00\\x00\\x00\\xB2\\x00\\x00\\x00\\xA8\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\\xB3\\x00\\x00\\x00\\xAE\\x00\\x00\\x00\\xAF\\x00\\x00\\x00\"\n\"\\xF8\\x00\\x02\\x00\\xAE\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\xB4\\x00\\x00\\x00\\x93\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x14\\x00\\x00\\x00\"\n\"\\xB5\\x00\\x00\\x00\\x19\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\xB6\\x00\\x00\\x00\\xA0\\x00\\x00\\x00\\x4F\\x00\\x07\\x00\\x16\\x00\\x00\\x00\"\n\"\\xB7\\x00\\x00\\x00\\xB6\\x00\\x00\\x00\\xB6\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x57\\x00\\x05\\x00\\x0D\\x00\\x00\\x00\\xB8\\x00\\x00\\x00\"\n\"\\xB5\\x00\\x00\\x00\\xB7\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\xB9\\x00\\x00\\x00\\xB8\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x41\\x00\\x05\\x00\"\n\"\\x2C\\x00\\x00\\x00\\xBA\\x00\\x00\\x00\\xA0\\x00\\x00\\x00\\x31\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\xBB\\x00\\x00\\x00\\xBA\\x00\\x00\\x00\"\n\"\\x0C\\x00\\x07\\x00\\x06\\x00\\x00\\x00\\xBC\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x30\\x00\\x00\\x00\\xB9\\x00\\x00\\x00\\xBB\\x00\\x00\\x00\\x8E\\x00\\x05\\x00\"\n\"\\x07\\x00\\x00\\x00\\xBD\\x00\\x00\\x00\\xB4\\x00\\x00\\x00\\xBC\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\xBE\\x00\\x00\\x00\\xA0\\x00\\x00\\x00\"\n\"\\x81\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\xBF\\x00\\x00\\x00\\xBE\\x00\\x00\\x00\\xBD\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\xA0\\x00\\x00\\x00\\xBF\\x00\\x00\\x00\"\n\"\\xF9\\x00\\x02\\x00\\xB0\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\xB0\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\xC0\\x00\\x00\\x00\\xAC\\x00\\x00\\x00\"\n\"\\x81\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\xC1\\x00\\x00\\x00\\xC0\\x00\\x00\\x00\\x68\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\xAC\\x00\\x00\\x00\\xC1\\x00\\x00\\x00\"\n\"\\xF9\\x00\\x02\\x00\\xAD\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\xAF\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\xAC\\x00\\x00\\x00\\x56\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\"\n\"\\xC2\\x00\\x00\\x00\\x68\\x00\\x00\\x00\\xF9\\x00\\x02\\x00\\xC3\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\xC3\\x00\\x00\\x00\\xF6\\x00\\x04\\x00\\xC5\\x00\\x00\\x00\"\n\"\\xC6\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xF9\\x00\\x02\\x00\\xC7\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\xC7\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\"\n\"\\xC8\\x00\\x00\\x00\\xAC\\x00\\x00\\x00\\xB8\\x00\\x05\\x00\\x21\\x00\\x00\\x00\\xCA\\x00\\x00\\x00\\xC8\\x00\\x00\\x00\\xC9\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\"\n\"\\xCA\\x00\\x00\\x00\\xC4\\x00\\x00\\x00\\xC5\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\xC4\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\xCB\\x00\\x00\\x00\"\n\"\\x93\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x14\\x00\\x00\\x00\\xCC\\x00\\x00\\x00\\x19\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\xCD\\x00\\x00\\x00\"\n\"\\xA0\\x00\\x00\\x00\\x4F\\x00\\x07\\x00\\x16\\x00\\x00\\x00\\xCE\\x00\\x00\\x00\\xCD\\x00\\x00\\x00\\xCD\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x57\\x00\\x05\\x00\\x0D\\x00\\x00\\x00\\xCF\\x00\\x00\\x00\\xCC\\x00\\x00\\x00\\xCE\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\xD0\\x00\\x00\\x00\"\n\"\\xCF\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x2C\\x00\\x00\\x00\\xD1\\x00\\x00\\x00\\xA0\\x00\\x00\\x00\\x31\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x06\\x00\\x00\\x00\\xD2\\x00\\x00\\x00\\xD1\\x00\\x00\\x00\\x0C\\x00\\x07\\x00\\x06\\x00\\x00\\x00\\xD3\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x30\\x00\\x00\\x00\"\n\"\\xD0\\x00\\x00\\x00\\xD2\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\xD4\\x00\\x00\\x00\\xC2\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\"\n\"\\xD5\\x00\\x00\\x00\\xD3\\x00\\x00\\x00\\xD4\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\xD7\\x00\\x00\\x00\\xC2\\x00\\x00\\x00\\x85\\x00\\x05\\x00\"\n\"\\x06\\x00\\x00\\x00\\xD8\\x00\\x00\\x00\\xD6\\x00\\x00\\x00\\xD7\\x00\\x00\\x00\\x83\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\xD9\\x00\\x00\\x00\\xD5\\x00\\x00\\x00\"\n\"\\xD8\\x00\\x00\\x00\\x8E\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\xDA\\x00\\x00\\x00\\xCB\\x00\\x00\\x00\\xD9\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\"\n\"\\xDB\\x00\\x00\\x00\\xA0\\x00\\x00\\x00\\x81\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\xDC\\x00\\x00\\x00\\xDB\\x00\\x00\\x00\\xDA\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\"\n\"\\xA0\\x00\\x00\\x00\\xDC\\x00\\x00\\x00\\xF9\\x00\\x02\\x00\\xC6\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\xC6\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\"\n\"\\xDD\\x00\\x00\\x00\\xAC\\x00\\x00\\x00\\x81\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\xDE\\x00\\x00\\x00\\xDD\\x00\\x00\\x00\\x68\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\"\n\"\\xAC\\x00\\x00\\x00\\xDE\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\xDF\\x00\\x00\\x00\\xC2\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\"\n\"\\xE0\\x00\\x00\\x00\\xDF\\x00\\x00\\x00\\xD6\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\xC2\\x00\\x00\\x00\\xE0\\x00\\x00\\x00\\xF9\\x00\\x02\\x00\\xC3\\x00\\x00\\x00\"\n\"\\xF8\\x00\\x02\\x00\\xC5\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\xE1\\x00\\x00\\x00\\xA0\\x00\\x00\\x00\\x4F\\x00\\x07\\x00\\x16\\x00\\x00\\x00\"\n\"\\xE2\\x00\\x00\\x00\\xE1\\x00\\x00\\x00\\xE1\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\xFE\\x00\\x02\\x00\\xE2\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\"\n\"\\xE4\\x00\\x00\\x00\\xBA\\x00\\x05\\x00\\x21\\x00\\x00\\x00\\xE6\\x00\\x00\\x00\\xE5\\x00\\x00\\x00\\x56\\x00\\x00\\x00\\xF7\\x00\\x03\\x00\\xE8\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\\xE6\\x00\\x00\\x00\\xE7\\x00\\x00\\x00\\xE8\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\xE7\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x07\\x00\\x00\\x00\\xEA\\x00\\x00\\x00\\x1B\\x00\\x00\\x00\\x0C\\x00\\x06\\x00\\x07\\x00\\x00\\x00\\xEB\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x45\\x00\\x00\\x00\"\n\"\\xEA\\x00\\x00\\x00\\x4F\\x00\\x07\\x00\\x16\\x00\\x00\\x00\\xEC\\x00\\x00\\x00\\xEB\\x00\\x00\\x00\\xEB\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x8E\\x00\\x05\\x00\\x16\\x00\\x00\\x00\\xED\\x00\\x00\\x00\\xEC\\x00\\x00\\x00\\x97\\x00\\x00\\x00\\x8E\\x00\\x05\\x00\\x16\\x00\\x00\\x00\\xEE\\x00\\x00\\x00\"\n\"\\xED\\x00\\x00\\x00\\x68\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x16\\x00\\x00\\x00\\xEF\\x00\\x00\\x00\\xEE\\x00\\x00\\x00\\x9B\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\"\n\"\\xE9\\x00\\x00\\x00\\xEF\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\xF1\\x00\\x00\\x00\\x1A\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\xF0\\x00\\x00\\x00\"\n\"\\xF1\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\xF2\\x00\\x00\\x00\\xE9\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\xF3\\x00\\x00\\x00\"\n\"\\xF0\\x00\\x00\\x00\\x81\\x00\\x05\\x00\\x16\\x00\\x00\\x00\\xF4\\x00\\x00\\x00\\xF3\\x00\\x00\\x00\\xF2\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\xF0\\x00\\x00\\x00\"\n\"\\xF4\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\xF6\\x00\\x00\\x00\\xE9\\x00\\x00\\x00\\x8E\\x00\\x05\\x00\\x16\\x00\\x00\\x00\\xF7\\x00\\x00\\x00\"\n\"\\xF6\\x00\\x00\\x00\\xF5\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\xE9\\x00\\x00\\x00\\xF7\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\xF8\\x00\\x00\\x00\"\n\"\\xE9\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x14\\x00\\x00\\x00\\xF9\\x00\\x00\\x00\\x19\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\xFA\\x00\\x00\\x00\"\n\"\\xF0\\x00\\x00\\x00\\x57\\x00\\x05\\x00\\x0D\\x00\\x00\\x00\\xFB\\x00\\x00\\x00\\xF9\\x00\\x00\\x00\\xFA\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\"\n\"\\xFC\\x00\\x00\\x00\\xFB\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x8E\\x00\\x05\\x00\\x16\\x00\\x00\\x00\\xFD\\x00\\x00\\x00\\xF8\\x00\\x00\\x00\\xFC\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\xFE\\x00\\x00\\x00\\xF0\\x00\\x00\\x00\\x83\\x00\\x05\\x00\\x16\\x00\\x00\\x00\\xFF\\x00\\x00\\x00\\xFE\\x00\\x00\\x00\"\n\"\\xFD\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\xF0\\x00\\x00\\x00\\xFF\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\xE9\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x14\\x00\\x00\\x00\\x01\\x01\\x00\\x00\\x19\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x02\\x01\\x00\\x00\\xF0\\x00\\x00\\x00\"\n\"\\x57\\x00\\x05\\x00\\x0D\\x00\\x00\\x00\\x03\\x01\\x00\\x00\\x01\\x01\\x00\\x00\\x02\\x01\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x04\\x01\\x00\\x00\"\n\"\\x03\\x01\\x00\\x00\\x03\\x00\\x00\\x00\\x8E\\x00\\x05\\x00\\x16\\x00\\x00\\x00\\x05\\x01\\x00\\x00\\x00\\x01\\x00\\x00\\x04\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x16\\x00\\x00\\x00\\x06\\x01\\x00\\x00\\xF0\\x00\\x00\\x00\\x83\\x00\\x05\\x00\\x16\\x00\\x00\\x00\\x07\\x01\\x00\\x00\\x06\\x01\\x00\\x00\\x05\\x01\\x00\\x00\"\n\"\\x3E\\x00\\x03\\x00\\xF0\\x00\\x00\\x00\\x07\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x08\\x01\\x00\\x00\\xE9\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x14\\x00\\x00\\x00\\x09\\x01\\x00\\x00\\x19\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x0A\\x01\\x00\\x00\\xF0\\x00\\x00\\x00\\x57\\x00\\x05\\x00\"\n\"\\x0D\\x00\\x00\\x00\\x0B\\x01\\x00\\x00\\x09\\x01\\x00\\x00\\x0A\\x01\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x0C\\x01\\x00\\x00\\x0B\\x01\\x00\\x00\"\n\"\\x03\\x00\\x00\\x00\\x8E\\x00\\x05\\x00\\x16\\x00\\x00\\x00\\x0D\\x01\\x00\\x00\\x08\\x01\\x00\\x00\\x0C\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x16\\x00\\x00\\x00\"\n\"\\x0E\\x01\\x00\\x00\\xF0\\x00\\x00\\x00\\x83\\x00\\x05\\x00\\x16\\x00\\x00\\x00\\x0F\\x01\\x00\\x00\\x0E\\x01\\x00\\x00\\x0D\\x01\\x00\\x00\\x3E\\x00\\x03\\x00\"\n\"\\xF0\\x00\\x00\\x00\\x0F\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x10\\x01\\x00\\x00\\xF0\\x00\\x00\\x00\\xFE\\x00\\x02\\x00\\x10\\x01\\x00\\x00\"\n\"\\xF8\\x00\\x02\\x00\\xE8\\x00\\x00\\x00\\xF9\\x00\\x02\\x00\\x92\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x92\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x16\\x00\\x00\\x00\"\n\"\\x12\\x01\\x00\\x00\\x1A\\x00\\x00\\x00\\xFE\\x00\\x02\\x00\\x12\\x01\\x00\\x00\\x38\\x00\\x01\\x00\"},\n#endif\n#ifdef D3D9QUAKE\n{QR_DIRECT3D9, 9, \"defaultskin\",\n\"!!permu FRAMEBLEND\\n\"\n\"!!permu UPPERLOWER\\n\"\n//!!permu FULLBRIGHT\n\"!!samps diffuse upper lower\\n\"\n// fullbright\n\n\"struct a2v\\n\"\n\"{\\n\"\n\"float3 pos: POSITION0;\\n\"\n\"#ifdef FRAMEBLEND\\n\"\n\"float3 pos2: POSITION1;\\n\"\n\"#endif\\n\"\n\"float2 tc: TEXCOORD0;\\n\"\n\"float3 normal: NORMAL;\\n\"\n\"};\\n\"\n\"struct v2f\\n\"\n\"{\\n\"\n\"#ifndef FRAGMENT_SHADER\\n\"\n\"float4 pos: POSITION;\\n\"\n\"#endif\\n\"\n\"float2 tc: TEXCOORD0;\\n\"\n\"float3 light: TEXCOORD1;\\n\"\n\"};\\n\"\n\n//#include <ftedefs.h>\n\n\"#ifdef VERTEX_SHADER\\n\"\n\"float3 e_light_dir;\\n\"\n\"float3 e_light_mul;\\n\"\n\"float3 e_light_ambient;\\n\"\n\"float2 e_vblend;\\n\"\n\"float4x4  m_model;\\n\"\n\"float4x4  m_view;\\n\"\n\"float4x4  m_projection;\\n\"\n\"v2f main (a2v inp)\\n\"\n\"{\\n\"\n\"v2f outp;\\n\"\n\"float4 pos;\\n\"\n\"#ifdef FRAMEBLEND\\n\"\n\"pos = float4(e_vblend.x*inp.pos + e_vblend.y*inp.pos2, 1);\\n\"\n\"#else\\n\"\n\"pos = float4(inp.pos, 1);\\n\"\n\"#endif\\n\"\n\"outp.pos = mul(m_model, pos);\\n\"\n\"outp.pos = mul(m_view, outp.pos);\\n\"\n\"outp.pos = mul(m_projection, outp.pos);\\n\"\n\n\"float d = dot(inp.normal, e_light_dir);\\n\"\n\"outp.light = e_light_ambient + (d * e_light_mul);\\n\"\n\"outp.tc = inp.tc.xy;\\n\"\n\"return outp;\\n\"\n\"}\\n\"\n\"#endif\\n\"\n\"#ifdef FRAGMENT_SHADER\\n\"\n\"float4 e_colourident;\\n\"\n\"float3 e_uppercolour;\\n\"\n\"float3 e_lowercolour;\\n\"\n\"sampler s_diffuse; /*diffuse*/\\n\"\n\"sampler s_upper; /*upper*/\\n\"\n\"sampler s_lower; /*lower*/\\n\"\n\"sampler s_fullbright; /*fullbright*/\\n\"\n\"float4 main (v2f inp) : SV_TARGET\\n\"\n\"{\\n\"\n\"float4 col;\\n\"\n\"col = tex2D(s_diffuse, inp.tc);\\n\"\n\"#ifdef UPPER\\n\"\n\"float4 uc = tex2D(s_upper, inp.tc);\\n\"\n\"col.rgb += uc.rgb*e_uppercolour * uc.a;\\n\"\n\"#endif\\n\"\n\"#ifdef LOWER\\n\"\n\"float4 lc = tex2D(s_lower, inp.tc);\\n\"\n\"col.rgb += lc.rgb*e_lowercolour * lc.a;\\n\"\n\"#endif\\n\"\n\"col.rgb *= inp.light;\\n\"\n\"#ifdef FULLBRIGHT\\n\"\n\"float4 fb = tex2D(s_fullbright, inp.tc);\\n\"\n\"col.rgb = lerp(col.rgb, fb.rgb, fb.a);\\n\"\n\"#endif\\n\"\n\"return col * e_colourident;\\n\"\n//\t\treturn fog4(col * e_colourident);\n\"}\\n\"\n\"#endif\\n\"\n},\n#endif\n#ifdef D3D11QUAKE\n{QR_DIRECT3D11, 11, \"defaultskin\",\n\"!!permu UPPERLOWER\\n\"\n\"!!samps diffuse upper lower fullbright\\n\"\n\n\"struct a2v\\n\"\n\"{\\n\"\n\"float4 pos: POSITION;\\n\"\n\"float2 tc: TEXCOORD0;\\n\"\n\"float3 normal: NORMAL;\\n\"\n\"};\\n\"\n\"struct v2f\\n\"\n\"{\\n\"\n\"float4 pos: SV_POSITION;\\n\"\n\"float2 tc: TEXCOORD0;\\n\"\n\"float3 light: TEXCOORD1;\\n\"\n\"};\\n\"\n\n\"#include <ftedefs.h>\\n\"\n\n\"#ifdef VERTEX_SHADER\\n\"\n//attribute vec2 v_texcoord;\n//uniform vec3 e_light_dir;\n//uniform vec3 e_light_mul;\n//uniform vec3 e_light_ambient;\n\"v2f main (a2v inp)\\n\"\n\"{\\n\"\n\"v2f outp;\\n\"\n\"outp.pos = mul(m_model, inp.pos);\\n\"\n\"outp.pos = mul(m_view, outp.pos);\\n\"\n\"outp.pos = mul(m_projection, outp.pos);\\n\"\n\"outp.light = e_light_ambient + (dot(inp.normal,e_light_dir)*e_light_mul);\\n\"\n\"outp.tc = inp.tc.xy;\\n\"\n\"return outp;\\n\"\n\"}\\n\"\n\"#endif\\n\"\n\"#ifdef FRAGMENT_SHADER\\n\"\n\"Texture2D t_diffuse  : register(t0);\\n\"\n\"#ifdef UPPER\\n\"\n\"Texture2D t_upper  : register(t1);\\n\"\n\"Texture2D t_lower  : register(t2);\\n\"\n\"Texture2D t_fullbright : register(t3);\\n\"\n\"#else\\n\"\n\"Texture2D t_fullbright : register(t1);\\n\"\n\"#endif\\n\"\n\n\"SamplerState SampleType;\\n\"\n\n\"float4 main (v2f inp) : SV_TARGET\\n\"\n\"{\\n\"\n\"float4 col;\\n\"\n\"col = t_diffuse.Sample(SampleType, inp.tc);\\n\"\n\n\"#ifdef MASK\\n\"\n\"#ifndef MASKOP\\n\"\n\"#define MASKOP >= //drawn if (alpha OP ref) is true.\\n\"\n\"#endif\\n\"\n//support for alpha masking\n\"if (!(col.a MASKOP MASK))\\n\"\n\"discard;\\n\"\n\"#endif\\n\"\n\n\"#ifdef UPPER\\n\"\n\"float4 uc = t_upper.Sample(SampleType, inp.tc);\\n\"\n\"col.rgb += uc.rgb*e_uppercolour.rgb*uc.a;\\n\"\n\"#endif\\n\"\n\"#ifdef LOWER\\n\"\n\"float4 lc = t_lower.Sample(SampleType, inp.tc);\\n\"\n\"col.rgb += lc.rgb*e_lowercolour.rgb*lc.a;\\n\"\n\"#endif\\n\"\n\"col.rgb *= inp.light;\\n\"\n//#ifdef FULLBRIGHT\n\"float4 fb = t_fullbright.Sample(SampleType, inp.tc)*e_glowmod;\\n\"\n//\t\tcol.rgb = lerp(col.rgb, fb.rgb, fb.a);\t//matches vanilla quake...\n\"col.rgb += fb.rgb * fb.a;    //but nothing expects it to.\\n\"\n//#endif\n\"col *= e_colourmod;\\n\"\n//\t\tcol = fog4(col);\n\"return col;\\n\"\n\"}\\n\"\n\"#endif\\n\"\n},\n#endif\n#ifdef GLQUAKE\n{QR_OPENGL, 110, \"defaultsky\",\n\"!!permu FOG\\n\"\n\"!!samps base=0, cloud=1\\n\"\n\"!!cvardf r_skyfog=0.5\\n\"\n\"#include \\\"sys/fog.h\\\"\\n\"\n\n//regular sky shader for scrolling q1 skies\n//the sky surfaces are thrown through this as-is.\n\n\"#ifdef VERTEX_SHADER\\n\"\n\"varying vec3 pos;\\n\"\n\"void main ()\\n\"\n\"{\\n\"\n\"pos = v_position.xyz;\\n\"\n\"gl_Position = ftetransform();\\n\"\n\"}\\n\"\n\"#endif\\n\"\n\"#ifdef FRAGMENT_SHADER\\n\"\n\"uniform float e_time;\\n\"\n\"uniform vec3 e_eyepos;\\n\"\n\"varying vec3 pos;\\n\"\n\"void main ()\\n\"\n\"{\\n\"\n\"vec2 tccoord;\\n\"\n\"vec3 dir = pos - e_eyepos;\\n\"\n\n\"#ifdef EQUI\\n\"\n\"#define PI 3.1415926535897932384626433832795\\n\"\n\"dir = normalize(dir);\\n\"\n\"tccoord.x = atan(dir.y,-dir.x) / (PI*2.0);\\n\"\n\"tccoord.y = acos(dir.z) / PI;\\n\"\n\n\"vec3 sky = vec3(texture2D(s_base, tccoord));\\n\"\n\"#else\\n\"\n\n\"dir.z *= 3.0;\\n\"\n\"dir.xy /= 0.5*length(dir);\\n\"\n\"tccoord = (dir.xy + e_time*0.03125);\\n\"\n\"vec3 sky = vec3(texture2D(s_base, tccoord));\\n\"\n\"tccoord = (dir.xy + e_time*0.0625);\\n\"\n\"vec4 clouds = texture2D(s_cloud, tccoord);\\n\"\n\"sky = (sky.rgb*(1.0-clouds.a)) + (clouds.a*clouds.rgb);\\n\"\n\"#endif\\n\"\n\n\"#ifdef FOG\\n\"\n\"sky.rgb = mix(sky.rgb, w_fogcolour, float(r_skyfog)*w_fogalpha); //flat fog ignoring actual geometry\\n\"\n//sky = fog3(sky);\t\t\t\t\t\t\t\t\t\t\t\t\t//fog according to actual geometry\n\"#endif\\n\"\n\"gl_FragColor = vec4(sky, 1.0);\\n\"\n\"}\\n\"\n\"#endif\\n\"\n},\n#endif\n#ifdef VKQUAKE\n{QR_VULKAN, -1, \"defaultsky\",\n\"\\xFF\\x53\\x50\\x56\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x2C\\x00\\x00\\x00\\x28\\x00\\x00\\x00\\x54\\x00\\x00\\x00\"\n\"\\x6C\\x0A\\x00\\x00\\xC0\\x0A\\x00\\x00\\x80\\x13\\x00\\x00\\x01\\x00\\x62\\x31\\x72\\x5F\\x66\\x6F\\x67\\x5F\\x65\\x78\\x70\\x32\\x00\\x00\\x00\\x00\\x00\\x01\"\n\"\\x01\\x62\\x31\\x72\\x5F\\x66\\x6F\\x67\\x5F\\x6C\\x69\\x6E\\x65\\x61\\x72\\x00\\x00\\x00\\x00\\x00\\x03\\x02\\x23\\x07\\x00\\x00\\x01\\x00\\x0A\\x00\\x08\\x00\"\n\"\\x4F\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x11\\x00\\x02\\x00\\x01\\x00\\x00\\x00\\x0B\\x00\\x06\\x00\\x01\\x00\\x00\\x00\\x47\\x4C\\x53\\x4C\\x2E\\x73\\x74\\x64\"\n\"\\x2E\\x34\\x35\\x30\\x00\\x00\\x00\\x00\\x0E\\x00\\x03\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x0F\\x00\\x0E\\x00\\x00\\x00\\x00\\x00\\x04\\x00\\x00\\x00\"\n\"\\x6D\\x61\\x69\\x6E\\x00\\x00\\x00\\x00\\x1C\\x00\\x00\\x00\\x39\\x00\\x00\\x00\\x3D\\x00\\x00\\x00\\x48\\x00\\x00\\x00\\x4A\\x00\\x00\\x00\\x4B\\x00\\x00\\x00\"\n\"\\x4C\\x00\\x00\\x00\\x4D\\x00\\x00\\x00\\x4E\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x11\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\"\n\"\\x13\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x13\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x13\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x13\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x10\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xC0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\"\n\"\\x04\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xCC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xD0\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xDC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\xE0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xEC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x13\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xF0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x0A\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\xFC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\"\n\"\\x0C\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x4C\\x01\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x50\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x5C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x60\\x01\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x13\\x00\\x00\\x00\\x11\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x6C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x70\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x13\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\"\n\"\\x14\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x90\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x94\\x01\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x16\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x98\\x01\\x00\\x00\\x47\\x00\\x03\\x00\\x13\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x15\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x15\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x1C\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x39\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x3B\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x03\\x00\\x3B\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x41\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\x42\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x01\\x01\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x43\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x44\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x05\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x44\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x44\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x44\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x44\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x4C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x44\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x50\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x44\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x5C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x44\\x00\\x00\\x00\"\n\"\\x05\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x60\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x44\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x6C\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x44\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x70\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x44\\x00\\x00\\x00\\x08\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x80\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x44\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x88\\x00\\x00\\x00\\x47\\x00\\x03\\x00\"\n\"\\x44\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x46\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x46\\x00\\x00\\x00\"\n\"\\x21\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x48\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x4A\\x00\\x00\\x00\"\n\"\\x1E\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x4B\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x4C\\x00\\x00\\x00\"\n\"\\x1E\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x4D\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x4E\\x00\\x00\\x00\"\n\"\\x1E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x13\\x00\\x02\\x00\\x02\\x00\\x00\\x00\\x21\\x00\\x03\\x00\\x03\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x16\\x00\\x03\\x00\"\n\"\\x06\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x17\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x21\\x00\\x03\\x00\\x08\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x18\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x04\\x00\\x00\\x00\\x17\\x00\\x04\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x15\\x00\\x04\\x00\\x0F\\x00\\x00\\x00\\x20\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x0F\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x1C\\x00\\x04\\x00\\x11\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x10\\x00\\x00\\x00\\x17\\x00\\x04\\x00\\x12\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x1E\\x00\\x19\\x00\\x13\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\"\n\"\\x0D\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x11\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x14\\x00\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\x13\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x14\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x15\\x00\\x04\\x00\\x16\\x00\\x00\\x00\"\n\"\\x20\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x17\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x18\\x00\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\"\n\"\\x1C\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x00\\x00\\x80\\x3F\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\"\n\"\\x24\\x00\\x00\\x00\\x00\\x00\\x80\\xBF\\x2B\\x00\\x04\\x00\\x0F\\x00\\x00\\x00\\x25\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x26\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x0F\\x00\\x00\\x00\\x2B\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x0F\\x00\\x00\\x00\"\n\"\\x2E\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x32\\x00\\x00\\x00\\x00\\x00\\x00\\x40\\x20\\x00\\x04\\x00\\x38\\x00\\x00\\x00\"\n\"\\x03\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x38\\x00\\x00\\x00\\x39\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x1E\\x00\\x03\\x00\\x3B\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x3C\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x3B\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x3C\\x00\\x00\\x00\\x3D\\x00\\x00\\x00\"\n\"\\x03\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x3F\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x32\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x41\\x00\\x00\\x00\"\n\"\\x00\\x01\\x00\\x00\\x32\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x42\\x00\\x00\\x00\\x01\\x01\\x00\\x00\\x32\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x43\\x00\\x00\\x00\"\n\"\\x15\\x00\\x00\\x00\\x1E\\x00\\x0C\\x00\\x44\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x45\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x44\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x45\\x00\\x00\\x00\\x46\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x47\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x12\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x47\\x00\\x00\\x00\\x48\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x49\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x49\\x00\\x00\\x00\\x4A\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x47\\x00\\x00\\x00\\x4B\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\\x4C\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\\x4D\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\\x4E\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x36\\x00\\x05\\x00\\x02\\x00\\x00\\x00\\x04\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x05\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0E\\x00\\x00\\x00\\x3A\\x00\\x00\\x00\\x1C\\x00\\x00\\x00\"\n\"\\x3E\\x00\\x03\\x00\\x39\\x00\\x00\\x00\\x3A\\x00\\x00\\x00\\x39\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x3E\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x41\\x00\\x05\\x00\"\n\"\\x3F\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x3D\\x00\\x00\\x00\\x17\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x40\\x00\\x00\\x00\\x3E\\x00\\x00\\x00\\xFD\\x00\\x01\\x00\"\n\"\\x38\\x00\\x01\\x00\\x36\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x0A\\x00\\x00\\x00\"\n\"\\x3B\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x18\\x00\\x00\\x00\\x19\\x00\\x00\\x00\\x15\\x00\\x00\\x00\"\n\"\\x17\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\x1A\\x00\\x00\\x00\\x19\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0E\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\"\n\"\\x1C\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x1F\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\"\n\"\\x20\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x50\\x00\\x07\\x00\\x07\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x1F\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x91\\x00\\x05\\x00\"\n\"\\x07\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x1A\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x0C\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x41\\x00\\x05\\x00\"\n\"\\x26\\x00\\x00\\x00\\x27\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x25\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x28\\x00\\x00\\x00\\x27\\x00\\x00\\x00\"\n\"\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x29\\x00\\x00\\x00\\x28\\x00\\x00\\x00\\x24\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x26\\x00\\x00\\x00\\x2A\\x00\\x00\\x00\"\n\"\\x0C\\x00\\x00\\x00\\x25\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x2A\\x00\\x00\\x00\\x29\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x26\\x00\\x00\\x00\\x2C\\x00\\x00\\x00\"\n\"\\x0C\\x00\\x00\\x00\\x2B\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\\x2C\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x26\\x00\\x00\\x00\"\n\"\\x2F\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x2E\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x30\\x00\\x00\\x00\\x2F\\x00\\x00\\x00\\x81\\x00\\x05\\x00\"\n\"\\x06\\x00\\x00\\x00\\x31\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\\x30\\x00\\x00\\x00\\x88\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x33\\x00\\x00\\x00\\x31\\x00\\x00\\x00\"\n\"\\x32\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x26\\x00\\x00\\x00\\x34\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x2B\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x34\\x00\\x00\\x00\"\n\"\\x33\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x35\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\xFE\\x00\\x02\\x00\\x35\\x00\\x00\\x00\\x38\\x00\\x01\\x00\"\n\"\\x03\\x02\\x23\\x07\\x00\\x00\\x01\\x00\\x0A\\x00\\x08\\x00\\xC1\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x11\\x00\\x02\\x00\\x01\\x00\\x00\\x00\\x0B\\x00\\x06\\x00\"\n\"\\x01\\x00\\x00\\x00\\x47\\x4C\\x53\\x4C\\x2E\\x73\\x74\\x64\\x2E\\x34\\x35\\x30\\x00\\x00\\x00\\x00\\x0E\\x00\\x03\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x0F\\x00\\x08\\x00\\x04\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x6D\\x61\\x69\\x6E\\x00\\x00\\x00\\x00\\x1F\\x00\\x00\\x00\\x70\\x00\\x00\\x00\\xAC\\x00\\x00\\x00\"\n\"\\x10\\x00\\x03\\x00\\x04\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x0E\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x47\\x00\\x04\\x00\"\n\"\\x17\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x01\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\x1F\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\\x47\\x00\\x04\\x00\"\n\"\\x2C\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x2E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x2E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x2E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x2E\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x2E\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x2E\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\"\n\"\\x2E\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x2E\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x2E\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x2E\\x00\\x00\\x00\\x03\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\xC0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x2E\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xCC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x2E\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xD0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x2E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\xDC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x2E\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xE0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x2E\\x00\\x00\\x00\"\n\"\\x08\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xEC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x2E\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xF0\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x2E\\x00\\x00\\x00\\x0A\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xFC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x2E\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x2E\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x01\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x2E\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x4C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x2E\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x50\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x2E\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x5C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x2E\\x00\\x00\\x00\"\n\"\\x10\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x60\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x2E\\x00\\x00\\x00\\x11\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x6C\\x01\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x2E\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x70\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x2E\\x00\\x00\\x00\\x13\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x80\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x2E\\x00\\x00\\x00\\x14\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x90\\x01\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x2E\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x94\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x2E\\x00\\x00\\x00\\x16\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x98\\x01\\x00\\x00\\x47\\x00\\x03\\x00\\x2E\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x30\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x30\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x4D\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x01\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x70\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x95\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x95\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\xA7\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\xA7\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\xAC\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x48\\x00\\x04\\x00\\xBE\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\xBE\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\xBE\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\xBE\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\xBE\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x4C\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\xBE\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x50\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\xBE\\x00\\x00\\x00\\x04\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x5C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\xBE\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x60\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\xBE\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x6C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\xBE\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x70\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\xBE\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\xBE\\x00\\x00\\x00\"\n\"\\x09\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x88\\x00\\x00\\x00\\x47\\x00\\x03\\x00\\xBE\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\xC0\\x00\\x00\\x00\"\n\"\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\xC0\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x13\\x00\\x02\\x00\\x02\\x00\\x00\\x00\"\n\"\\x21\\x00\\x03\\x00\\x03\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x16\\x00\\x03\\x00\\x06\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x17\\x00\\x04\\x00\\x07\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x08\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x21\\x00\\x04\\x00\\x09\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\x15\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x32\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\"\n\"\\x0E\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x14\\x00\\x02\\x00\\x10\\x00\\x00\\x00\"\n\"\\x34\\x00\\x06\\x00\\x10\\x00\\x00\\x00\\x11\\x00\\x00\\x00\\xAB\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\\x34\\x00\\x05\\x00\\x10\\x00\\x00\\x00\"\n\"\\x12\\x00\\x00\\x00\\xA8\\x00\\x00\\x00\\x11\\x00\\x00\\x00\\x32\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\x17\\x00\\x00\\x00\\x01\\x01\\x00\\x00\\x34\\x00\\x06\\x00\"\n\"\\x10\\x00\\x00\\x00\\x18\\x00\\x00\\x00\\xAB\\x00\\x00\\x00\\x17\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x17\\x00\\x04\\x00\\x1D\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x1D\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\\x1F\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x15\\x00\\x04\\x00\\x20\\x00\\x00\\x00\\x20\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x20\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x22\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x20\\x00\\x00\\x00\\x25\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x18\\x00\\x04\\x00\\x2A\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\"\n\"\\x04\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x20\\x00\\x00\\x00\\x2B\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x1C\\x00\\x04\\x00\\x2C\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\"\n\"\\x2B\\x00\\x00\\x00\\x17\\x00\\x04\\x00\\x2D\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x1E\\x00\\x19\\x00\\x2E\\x00\\x00\\x00\\x2A\\x00\\x00\\x00\"\n\"\\x2A\\x00\\x00\\x00\\x2A\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x2C\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x2F\\x00\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\x2E\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x2F\\x00\\x00\\x00\\x30\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\"\n\"\\x31\\x00\\x00\\x00\\x14\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x32\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\"\n\"\\x39\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x47\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x32\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\"\n\"\\x4D\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x34\\x00\\x06\\x00\\x10\\x00\\x00\\x00\\x4E\\x00\\x00\\x00\\xAB\\x00\\x00\\x00\\x4D\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\"\n\"\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x55\\x00\\x00\\x00\\x3B\\xAA\\xB8\\x3F\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x59\\x00\\x00\\x00\\x00\\x00\\x80\\x3F\"\n\"\\x2B\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\x5A\\x00\\x00\\x00\\x13\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x64\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\"\n\"\\x20\\x00\\x04\\x00\\x6F\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x6F\\x00\\x00\\x00\\x70\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x2B\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\x72\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x73\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x77\\x00\\x00\\x00\\x00\\x00\\x40\\x40\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x7C\\x00\\x00\\x00\\x00\\x00\\x00\\x3F\"\n\"\\x20\\x00\\x04\\x00\\x86\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\x8A\\x00\\x00\\x00\\x04\\x00\\x00\\x00\"\n\"\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x8D\\x00\\x00\\x00\\x00\\x00\\x00\\x3D\\x19\\x00\\x09\\x00\\x92\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x1B\\x00\\x03\\x00\\x93\\x00\\x00\\x00\\x92\\x00\\x00\\x00\"\n\"\\x20\\x00\\x04\\x00\\x94\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x93\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x94\\x00\\x00\\x00\\x95\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\xA1\\x00\\x00\\x00\\x00\\x00\\x80\\x3D\\x20\\x00\\x04\\x00\\xA5\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\"\n\"\\x3B\\x00\\x04\\x00\\x94\\x00\\x00\\x00\\xA7\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\xAB\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\"\n\"\\x3B\\x00\\x04\\x00\\xAB\\x00\\x00\\x00\\xAC\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\xBC\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x1E\\x00\\x0C\\x00\\xBE\\x00\\x00\\x00\\x2A\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\xBF\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\xBE\\x00\\x00\\x00\"\n\"\\x3B\\x00\\x04\\x00\\xBF\\x00\\x00\\x00\\xC0\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x36\\x00\\x05\\x00\\x02\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x03\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x05\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x08\\x00\\x00\\x00\\x6E\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\"\n\"\\x86\\x00\\x00\\x00\\x87\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x08\\x00\\x00\\x00\\x91\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\"\n\"\\xA5\\x00\\x00\\x00\\xA6\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x08\\x00\\x00\\x00\\xB8\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x07\\x00\\x00\\x00\\x71\\x00\\x00\\x00\\x70\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x73\\x00\\x00\\x00\\x74\\x00\\x00\\x00\\x30\\x00\\x00\\x00\\x72\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x75\\x00\\x00\\x00\\x74\\x00\\x00\\x00\\x83\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x76\\x00\\x00\\x00\\x71\\x00\\x00\\x00\"\n\"\\x75\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x6E\\x00\\x00\\x00\\x76\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x1B\\x00\\x00\\x00\\x78\\x00\\x00\\x00\\x6E\\x00\\x00\\x00\"\n\"\\x21\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x79\\x00\\x00\\x00\\x78\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x7A\\x00\\x00\\x00\"\n\"\\x79\\x00\\x00\\x00\\x77\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x1B\\x00\\x00\\x00\\x7B\\x00\\x00\\x00\\x6E\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\"\n\"\\x7B\\x00\\x00\\x00\\x7A\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x7D\\x00\\x00\\x00\\x6E\\x00\\x00\\x00\\x0C\\x00\\x06\\x00\\x06\\x00\\x00\\x00\"\n\"\\x7E\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x42\\x00\\x00\\x00\\x7D\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x7F\\x00\\x00\\x00\\x7C\\x00\\x00\\x00\"\n\"\\x7E\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x80\\x00\\x00\\x00\\x6E\\x00\\x00\\x00\\x4F\\x00\\x07\\x00\\x2D\\x00\\x00\\x00\\x81\\x00\\x00\\x00\"\n\"\\x80\\x00\\x00\\x00\\x80\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x50\\x00\\x05\\x00\\x2D\\x00\\x00\\x00\\x82\\x00\\x00\\x00\\x7F\\x00\\x00\\x00\"\n\"\\x7F\\x00\\x00\\x00\\x88\\x00\\x05\\x00\\x2D\\x00\\x00\\x00\\x83\\x00\\x00\\x00\\x81\\x00\\x00\\x00\\x82\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\"\n\"\\x84\\x00\\x00\\x00\\x6E\\x00\\x00\\x00\\x4F\\x00\\x08\\x00\\x07\\x00\\x00\\x00\\x85\\x00\\x00\\x00\\x84\\x00\\x00\\x00\\x83\\x00\\x00\\x00\\x03\\x00\\x00\\x00\"\n\"\\x04\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x6E\\x00\\x00\\x00\\x85\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x88\\x00\\x00\\x00\"\n\"\\x6E\\x00\\x00\\x00\\x4F\\x00\\x07\\x00\\x2D\\x00\\x00\\x00\\x89\\x00\\x00\\x00\\x88\\x00\\x00\\x00\\x88\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x41\\x00\\x05\\x00\\x32\\x00\\x00\\x00\\x8B\\x00\\x00\\x00\\x30\\x00\\x00\\x00\\x8A\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x8C\\x00\\x00\\x00\"\n\"\\x8B\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x8E\\x00\\x00\\x00\\x8C\\x00\\x00\\x00\\x8D\\x00\\x00\\x00\\x50\\x00\\x05\\x00\\x2D\\x00\\x00\\x00\"\n\"\\x8F\\x00\\x00\\x00\\x8E\\x00\\x00\\x00\\x8E\\x00\\x00\\x00\\x81\\x00\\x05\\x00\\x2D\\x00\\x00\\x00\\x90\\x00\\x00\\x00\\x89\\x00\\x00\\x00\\x8F\\x00\\x00\\x00\"\n\"\\x3E\\x00\\x03\\x00\\x87\\x00\\x00\\x00\\x90\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x93\\x00\\x00\\x00\\x96\\x00\\x00\\x00\\x95\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x2D\\x00\\x00\\x00\\x97\\x00\\x00\\x00\\x87\\x00\\x00\\x00\\x57\\x00\\x05\\x00\\x1D\\x00\\x00\\x00\\x98\\x00\\x00\\x00\\x96\\x00\\x00\\x00\\x97\\x00\\x00\\x00\"\n\"\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x99\\x00\\x00\\x00\\x98\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x9A\\x00\\x00\\x00\"\n\"\\x98\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x9B\\x00\\x00\\x00\\x98\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x50\\x00\\x06\\x00\"\n\"\\x07\\x00\\x00\\x00\\x9C\\x00\\x00\\x00\\x99\\x00\\x00\\x00\\x9A\\x00\\x00\\x00\\x9B\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x91\\x00\\x00\\x00\\x9C\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x9D\\x00\\x00\\x00\\x6E\\x00\\x00\\x00\\x4F\\x00\\x07\\x00\\x2D\\x00\\x00\\x00\\x9E\\x00\\x00\\x00\\x9D\\x00\\x00\\x00\"\n\"\\x9D\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x32\\x00\\x00\\x00\\x9F\\x00\\x00\\x00\\x30\\x00\\x00\\x00\\x8A\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\xA0\\x00\\x00\\x00\\x9F\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\xA2\\x00\\x00\\x00\\xA0\\x00\\x00\\x00\"\n\"\\xA1\\x00\\x00\\x00\\x50\\x00\\x05\\x00\\x2D\\x00\\x00\\x00\\xA3\\x00\\x00\\x00\\xA2\\x00\\x00\\x00\\xA2\\x00\\x00\\x00\\x81\\x00\\x05\\x00\\x2D\\x00\\x00\\x00\"\n\"\\xA4\\x00\\x00\\x00\\x9E\\x00\\x00\\x00\\xA3\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x87\\x00\\x00\\x00\\xA4\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x93\\x00\\x00\\x00\"\n\"\\xA8\\x00\\x00\\x00\\xA7\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x2D\\x00\\x00\\x00\\xA9\\x00\\x00\\x00\\x87\\x00\\x00\\x00\\x57\\x00\\x05\\x00\\x1D\\x00\\x00\\x00\"\n\"\\xAA\\x00\\x00\\x00\\xA8\\x00\\x00\\x00\\xA9\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\xA6\\x00\\x00\\x00\\xAA\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\"\n\"\\xAD\\x00\\x00\\x00\\x91\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x1B\\x00\\x00\\x00\\xAE\\x00\\x00\\x00\\xA6\\x00\\x00\\x00\\x25\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x06\\x00\\x00\\x00\\xAF\\x00\\x00\\x00\\xAE\\x00\\x00\\x00\\x83\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\xB0\\x00\\x00\\x00\\x59\\x00\\x00\\x00\\xAF\\x00\\x00\\x00\"\n\"\\x8E\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\xB1\\x00\\x00\\x00\\xAD\\x00\\x00\\x00\\xB0\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x1B\\x00\\x00\\x00\\xB2\\x00\\x00\\x00\"\n\"\\xA6\\x00\\x00\\x00\\x25\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\xB3\\x00\\x00\\x00\\xB2\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x1D\\x00\\x00\\x00\"\n\"\\xB4\\x00\\x00\\x00\\xA6\\x00\\x00\\x00\\x4F\\x00\\x08\\x00\\x07\\x00\\x00\\x00\\xB5\\x00\\x00\\x00\\xB4\\x00\\x00\\x00\\xB4\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x8E\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\xB6\\x00\\x00\\x00\\xB5\\x00\\x00\\x00\\xB3\\x00\\x00\\x00\\x81\\x00\\x05\\x00\"\n\"\\x07\\x00\\x00\\x00\\xB7\\x00\\x00\\x00\\xB1\\x00\\x00\\x00\\xB6\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\xB8\\x00\\x00\\x00\\xB7\\x00\\x00\\x00\\x39\\x00\\x05\\x00\"\n\"\\x07\\x00\\x00\\x00\\xB9\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\xB8\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x1D\\x00\\x00\\x00\\xBA\\x00\\x00\\x00\\xAC\\x00\\x00\\x00\"\n\"\\x4F\\x00\\x09\\x00\\x1D\\x00\\x00\\x00\\xBB\\x00\\x00\\x00\\xBA\\x00\\x00\\x00\\xB9\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x03\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\xAC\\x00\\x00\\x00\\xBB\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\xBC\\x00\\x00\\x00\\xBD\\x00\\x00\\x00\\xAC\\x00\\x00\\x00\"\n\"\\x25\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\xBD\\x00\\x00\\x00\\x59\\x00\\x00\\x00\\xFD\\x00\\x01\\x00\\x38\\x00\\x01\\x00\\x36\\x00\\x05\\x00\\x07\\x00\\x00\\x00\"\n\"\\x0B\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x37\\x00\\x03\\x00\\x08\\x00\\x00\\x00\\x0A\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x0C\\x00\\x00\\x00\"\n\"\\x3B\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\\x1C\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\\x29\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\xF7\\x00\\x03\\x00\\x14\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\\x12\\x00\\x00\\x00\\x13\\x00\\x00\\x00\\x14\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\"\n\"\\x13\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x0A\\x00\\x00\\x00\\xFE\\x00\\x02\\x00\\x15\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\"\n\"\\x14\\x00\\x00\\x00\\xF7\\x00\\x03\\x00\\x1A\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\\x18\\x00\\x00\\x00\\x19\\x00\\x00\\x00\\x3E\\x00\\x00\\x00\"\n\"\\xF8\\x00\\x02\\x00\\x19\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x22\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x1F\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x06\\x00\\x00\\x00\\x24\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x22\\x00\\x00\\x00\\x26\\x00\\x00\\x00\\x1F\\x00\\x00\\x00\\x25\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x27\\x00\\x00\\x00\\x26\\x00\\x00\\x00\\x88\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x28\\x00\\x00\\x00\\x24\\x00\\x00\\x00\"\n\"\\x27\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x1C\\x00\\x00\\x00\\x28\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x32\\x00\\x00\\x00\\x33\\x00\\x00\\x00\\x30\\x00\\x00\\x00\"\n\"\\x31\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x34\\x00\\x00\\x00\\x33\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x35\\x00\\x00\\x00\"\n\"\\x1C\\x00\\x00\\x00\\x83\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x36\\x00\\x00\\x00\\x34\\x00\\x00\\x00\\x35\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x32\\x00\\x00\\x00\"\n\"\\x37\\x00\\x00\\x00\\x30\\x00\\x00\\x00\\x31\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x38\\x00\\x00\\x00\\x37\\x00\\x00\\x00\\x41\\x00\\x05\\x00\"\n\"\\x32\\x00\\x00\\x00\\x3A\\x00\\x00\\x00\\x30\\x00\\x00\\x00\\x39\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x3B\\x00\\x00\\x00\\x3A\\x00\\x00\\x00\"\n\"\\x83\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x3C\\x00\\x00\\x00\\x38\\x00\\x00\\x00\\x3B\\x00\\x00\\x00\\x88\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x3D\\x00\\x00\\x00\"\n\"\\x36\\x00\\x00\\x00\\x3C\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x29\\x00\\x00\\x00\\x3D\\x00\\x00\\x00\\xF9\\x00\\x02\\x00\\x1A\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\"\n\"\\x3E\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x32\\x00\\x00\\x00\\x3F\\x00\\x00\\x00\\x30\\x00\\x00\\x00\\x31\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\"\n\"\\x40\\x00\\x00\\x00\\x3F\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x22\\x00\\x00\\x00\\x41\\x00\\x00\\x00\\x1F\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x06\\x00\\x00\\x00\\x42\\x00\\x00\\x00\\x41\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x43\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x42\\x00\\x00\\x00\"\n\"\\x41\\x00\\x05\\x00\\x22\\x00\\x00\\x00\\x44\\x00\\x00\\x00\\x1F\\x00\\x00\\x00\\x25\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x45\\x00\\x00\\x00\"\n\"\\x44\\x00\\x00\\x00\\x88\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x46\\x00\\x00\\x00\\x43\\x00\\x00\\x00\\x45\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x1C\\x00\\x00\\x00\"\n\"\\x46\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x48\\x00\\x00\\x00\\x1C\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x32\\x00\\x00\\x00\\x49\\x00\\x00\\x00\"\n\"\\x30\\x00\\x00\\x00\\x39\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x4A\\x00\\x00\\x00\\x49\\x00\\x00\\x00\\x83\\x00\\x05\\x00\\x06\\x00\\x00\\x00\"\n\"\\x4B\\x00\\x00\\x00\\x48\\x00\\x00\\x00\\x4A\\x00\\x00\\x00\\x0C\\x00\\x07\\x00\\x06\\x00\\x00\\x00\\x4C\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x28\\x00\\x00\\x00\"\n\"\\x47\\x00\\x00\\x00\\x4B\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x1C\\x00\\x00\\x00\\x4C\\x00\\x00\\x00\\xF7\\x00\\x03\\x00\\x50\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\xFA\\x00\\x04\\x00\\x4E\\x00\\x00\\x00\\x4F\\x00\\x00\\x00\\x50\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x4F\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\"\n\"\\x51\\x00\\x00\\x00\\x1C\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x52\\x00\\x00\\x00\\x1C\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\"\n\"\\x53\\x00\\x00\\x00\\x52\\x00\\x00\\x00\\x51\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x1C\\x00\\x00\\x00\\x53\\x00\\x00\\x00\\xF9\\x00\\x02\\x00\\x50\\x00\\x00\\x00\"\n\"\\xF8\\x00\\x02\\x00\\x50\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x54\\x00\\x00\\x00\\x1C\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\"\n\"\\x56\\x00\\x00\\x00\\x54\\x00\\x00\\x00\\x55\\x00\\x00\\x00\\x7F\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x57\\x00\\x00\\x00\\x56\\x00\\x00\\x00\\x0C\\x00\\x06\\x00\"\n\"\\x06\\x00\\x00\\x00\\x58\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x57\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x29\\x00\\x00\\x00\\x58\\x00\\x00\\x00\"\n\"\\xF9\\x00\\x02\\x00\\x1A\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x1A\\x00\\x00\\x00\\x41\\x00\\x06\\x00\\x32\\x00\\x00\\x00\\x5B\\x00\\x00\\x00\\x30\\x00\\x00\\x00\"\n\"\\x5A\\x00\\x00\\x00\\x25\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x5C\\x00\\x00\\x00\\x5B\\x00\\x00\\x00\\x83\\x00\\x05\\x00\\x06\\x00\\x00\\x00\"\n\"\\x5D\\x00\\x00\\x00\\x59\\x00\\x00\\x00\\x5C\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x5E\\x00\\x00\\x00\\x29\\x00\\x00\\x00\\x0C\\x00\\x08\\x00\"\n\"\\x06\\x00\\x00\\x00\\x5F\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x2B\\x00\\x00\\x00\\x5E\\x00\\x00\\x00\\x47\\x00\\x00\\x00\\x59\\x00\\x00\\x00\\x41\\x00\\x06\\x00\"\n\"\\x32\\x00\\x00\\x00\\x60\\x00\\x00\\x00\\x30\\x00\\x00\\x00\\x5A\\x00\\x00\\x00\\x25\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x61\\x00\\x00\\x00\"\n\"\\x60\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x62\\x00\\x00\\x00\\x5F\\x00\\x00\\x00\\x61\\x00\\x00\\x00\\x81\\x00\\x05\\x00\\x06\\x00\\x00\\x00\"\n\"\\x63\\x00\\x00\\x00\\x5D\\x00\\x00\\x00\\x62\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x29\\x00\\x00\\x00\\x63\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x64\\x00\\x00\\x00\"\n\"\\x65\\x00\\x00\\x00\\x30\\x00\\x00\\x00\\x5A\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x1D\\x00\\x00\\x00\\x66\\x00\\x00\\x00\\x65\\x00\\x00\\x00\\x4F\\x00\\x08\\x00\"\n\"\\x07\\x00\\x00\\x00\\x67\\x00\\x00\\x00\\x66\\x00\\x00\\x00\\x66\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x07\\x00\\x00\\x00\\x68\\x00\\x00\\x00\\x0A\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x69\\x00\\x00\\x00\\x29\\x00\\x00\\x00\\x50\\x00\\x06\\x00\"\n\"\\x07\\x00\\x00\\x00\\x6A\\x00\\x00\\x00\\x69\\x00\\x00\\x00\\x69\\x00\\x00\\x00\\x69\\x00\\x00\\x00\\x0C\\x00\\x08\\x00\\x07\\x00\\x00\\x00\\x6B\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x2E\\x00\\x00\\x00\\x67\\x00\\x00\\x00\\x68\\x00\\x00\\x00\\x6A\\x00\\x00\\x00\\xFE\\x00\\x02\\x00\\x6B\\x00\\x00\\x00\\x38\\x00\\x01\\x00\"\n\"\"},\n#endif\n#ifdef D3D9QUAKE\n{QR_DIRECT3D9, 9, \"defaultsky\",\n\"!!samps 2\\n\"\n\n\"struct a2v\\n\"\n\"{\\n\"\n\"float4 pos: POSITION;\\n\"\n\"};\\n\"\n\"struct v2f\\n\"\n\"{\\n\"\n\"#ifndef FRAGMENT_SHADER\\n\"\n\"float4 pos: POSITION;\\n\"\n\"#endif\\n\"\n\"float3 vpos: TEXCOORD0;\\n\"\n\"};\\n\"\n\n\"#ifdef VERTEX_SHADER\\n\"\n\"float4x4  m_modelviewprojection;\\n\"\n\"v2f main (in a2v inp)\\n\"\n\"{\\n\"\n\"v2f outp;\\n\"\n\"outp.pos = mul(m_modelviewprojection, inp.pos);\\n\"\n\"outp.vpos = inp.pos.xyz;\\n\"\n\"return outp;\\n\"\n\"}\\n\"\n\"#endif\\n\"\n\n\"#ifdef FRAGMENT_SHADER\\n\"\n\"float e_time;\\n\"\n\"float3 e_eyepos;\\n\"\n\n\"sampler s_diffuse; /*diffuse*/\\n\"\n\"sampler s_fullbright; /*normal*/\\n\"\n\"float4 main (v2f inp) : COLOR0\\n\"\n\"{\\n\"\n\"float2 tccoord;\\n\"\n\n\"float3 dir = inp.vpos - e_eyepos;\\n\"\n\n\"dir.z *= 3.0;\\n\"\n\"dir.xy /= 0.5*length(dir);\\n\"\n\n\"tccoord = (dir.xy + e_time*0.03125);\\n\"\n\"float4 solid = tex2D(s_diffuse, tccoord);\\n\"\n\n\"tccoord = (dir.xy + e_time*0.0625);\\n\"\n\"float4 clouds = tex2D(s_fullbright, tccoord);\\n\"\n\"return float4((solid.rgb*(1.0-clouds.a)) + (clouds.a*clouds.rgb), 1);\\n\"\n\"}\\n\"\n\"#endif\\n\"\n},\n#endif\n#ifdef D3D11QUAKE\n{QR_DIRECT3D11, 11, \"defaultsky\",\n\"!!samps diffuse fullbright\\n\"\n\n//regular sky shader for scrolling q1 skies\n//the sky surfaces are thrown through this as-is.\n\n\"struct a2v\\n\"\n\"{\\n\"\n\"float4 pos: POSITION;\\n\"\n\"float2 tc: TEXCOORD0;\\n\"\n\"};\\n\"\n\"struct v2f\\n\"\n\"{\\n\"\n\"float4 pos: SV_POSITION;\\n\"\n\"float2 tc: TEXCOORD0;\\n\"\n\"float3 mpos: TEXCOORD1;\\n\"\n\"};\\n\"\n\n\"#include <ftedefs.h>\\n\"\n\n\"#ifdef VERTEX_SHADER\\n\"\n\"v2f main (a2v inp)\\n\"\n\"{\\n\"\n\"v2f outp;\\n\"\n\"outp.pos = mul(m_model, inp.pos);\\n\"\n\"outp.mpos = outp.pos.xyz;\\n\"\n\"outp.pos = mul(m_view, outp.pos);\\n\"\n\"outp.pos = mul(m_projection, outp.pos);\\n\"\n\"outp.tc = inp.tc;\\n\"\n\"return outp;\\n\"\n\"}\\n\"\n\"#endif\\n\"\n\n\"#ifdef FRAGMENT_SHADER\\n\"\n\"Texture2D t_diffuse   : register(t0);\\n\"\n\"Texture2D t_fullbright : register(t1);\\n\"\n\"SamplerState s_diffuse  : register(s0);\\n\"\n\"SamplerState s_fullbright : register(s1);\\n\"\n\n\"float4 main (v2f inp) : SV_TARGET\\n\"\n\"{\\n\"\n\"float2 tccoord;\\n\"\n\"float3 dir = inp.mpos - v_eyepos;\\n\"\n\"dir.z *= 3.0;\\n\"\n\"dir.xy /= 0.5*length(dir);\\n\"\n\"tccoord = (dir.xy + e_time*0.03125);\\n\"\n\"float4 solid = t_diffuse.Sample(s_diffuse, tccoord);\\n\"\n\"tccoord = (dir.xy + e_time*0.0625);\\n\"\n\"float4 clouds = t_fullbright.Sample(s_fullbright, tccoord);\\n\"\n\"return lerp(solid, clouds, clouds.a);\\n\"\n\"}\\n\"\n\"#endif\\n\"\n},\n#endif\n#ifdef GLQUAKE\n{QR_OPENGL, 110, \"defaultskybox\",\n\"!!permu FOG\\n\"\n\"!!samps reflectcube\\n\"\n\"!!cvardf r_skyfog=0.5\\n\"\n\"!!cvard4 r_glsl_skybox_orientation=0 0 0 0\\n\"\n\"!!cvardf r_glsl_skybox_autorotate=1\\n\"\n\"#include \\\"sys/defs.h\\\"\\n\"\n\"#include \\\"sys/fog.h\\\"\\n\"\n\n//simple shader for simple skyboxes.\n\n\"varying vec3 pos;\\n\"\n\"#ifdef VERTEX_SHADER\\n\"\n\"mat3 rotateAroundAxis(vec4 axis) //xyz axis, with angle in w\\n\"\n\"{\\n\"\n\"if (bool(r_glsl_skybox_autorotate))\\n\"\n\"axis.w *= e_time;\\n\"\n\"axis.w *= (3.14/180.0);\\n\"\n\"axis.xyz = normalize(axis.xyz);\\n\"\n\"float s = sin(axis.w);\\n\"\n\"float c = cos(axis.w);\\n\"\n\"float oc = 1.0 - c;\\n\"\n\n\"return mat3(oc * axis.x * axis.x + c, oc * axis.x * axis.y - axis.z * s, oc * axis.z * axis.x + axis.y * s,\\n\"\n\"oc * axis.x * axis.y + axis.z * s, oc * axis.y * axis.y + c, oc * axis.y * axis.z - axis.x * s,\\n\"\n\"oc * axis.z * axis.x - axis.y * s, oc * axis.y * axis.z + axis.x * s, oc * axis.z * axis.z + c);\\n\"\n\"}\\n\"\n\"void main ()\\n\"\n\"{\\n\"\n\"pos = v_position.xyz - e_eyepos;\\n\"\n\n\"if (r_glsl_skybox_orientation.xyz != vec3(0.0))\\n\"\n\"pos = pos*rotateAroundAxis(r_glsl_skybox_orientation);\\n\"\n\n\"gl_Position = ftetransform();\\n\"\n\"}\\n\"\n\"#endif\\n\"\n\"#ifdef FRAGMENT_SHADER\\n\"\n\"void main ()\\n\"\n\"{\\n\"\n\"vec4 skybox = textureCube(s_reflectcube, pos);\\n\"\n\n//Fun question: should sky be fogged as if infinite, or as if an actual surface?\n\"#ifdef FOG\\n\"\n\"#if 1\\n\"\n\"skybox.rgb = mix(skybox.rgb, w_fogcolour, float(r_skyfog)*w_fogalpha); //flat fog ignoring actual geometry\\n\"\n\"#else\\n\"\n\"skybox.rgb = mix(skybox.rgb, fog3(skybox.rgb), float(r_skyfog));  //fog in terms of actual geometry distance\\n\"\n\"#endif\\n\"\n\"#endif\\n\"\n\"gl_FragColor = skybox;\\n\"\n\"}\\n\"\n\"#endif\\n\"\n},\n#endif\n#ifdef VKQUAKE\n{QR_VULKAN, -1, \"defaultskybox\",\n\"\\xFF\\x53\\x50\\x56\\x01\\x00\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x2C\\x00\\x00\\x00\\x28\\x00\\x00\\x00\\x54\\x00\\x00\\x00\"\n\"\\xC4\\x0A\\x00\\x00\\x18\\x0B\\x00\\x00\\xB0\\x0E\\x00\\x00\\x01\\x00\\x62\\x31\\x72\\x5F\\x66\\x6F\\x67\\x5F\\x65\\x78\\x70\\x32\\x00\\x00\\x00\\x00\\x00\\x01\"\n\"\\x01\\x62\\x31\\x72\\x5F\\x66\\x6F\\x67\\x5F\\x6C\\x69\\x6E\\x65\\x61\\x72\\x00\\x00\\x00\\x00\\x00\\x03\\x02\\x23\\x07\\x00\\x00\\x01\\x00\\x0A\\x00\\x08\\x00\"\n\"\\x54\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x11\\x00\\x02\\x00\\x01\\x00\\x00\\x00\\x0B\\x00\\x06\\x00\\x01\\x00\\x00\\x00\\x47\\x4C\\x53\\x4C\\x2E\\x73\\x74\\x64\"\n\"\\x2E\\x34\\x35\\x30\\x00\\x00\\x00\\x00\\x0E\\x00\\x03\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x0F\\x00\\x0E\\x00\\x00\\x00\\x00\\x00\\x04\\x00\\x00\\x00\"\n\"\\x6D\\x61\\x69\\x6E\\x00\\x00\\x00\\x00\\x1C\\x00\\x00\\x00\\x39\\x00\\x00\\x00\\x42\\x00\\x00\\x00\\x4D\\x00\\x00\\x00\\x4F\\x00\\x00\\x00\\x50\\x00\\x00\\x00\"\n\"\\x51\\x00\\x00\\x00\\x52\\x00\\x00\\x00\\x53\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x11\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\"\n\"\\x13\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x13\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x13\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x13\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x10\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xC0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\"\n\"\\x04\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xCC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xD0\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xDC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\xE0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xEC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x13\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xF0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x0A\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\xFC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\"\n\"\\x0C\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x4C\\x01\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x50\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x5C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x60\\x01\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x13\\x00\\x00\\x00\\x11\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x6C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x70\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x13\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\"\n\"\\x14\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x90\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x94\\x01\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x16\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x98\\x01\\x00\\x00\\x47\\x00\\x03\\x00\\x13\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x15\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x15\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x1C\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x39\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x40\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x03\\x00\\x40\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x46\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\x47\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x01\\x01\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x48\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x49\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x05\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x49\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x49\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x49\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x49\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x4C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x49\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x50\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x49\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x5C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x49\\x00\\x00\\x00\"\n\"\\x05\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x60\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x49\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x6C\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x49\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x70\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x49\\x00\\x00\\x00\\x08\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x80\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x49\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x88\\x00\\x00\\x00\\x47\\x00\\x03\\x00\"\n\"\\x49\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x4B\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x4B\\x00\\x00\\x00\"\n\"\\x21\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x4D\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x4F\\x00\\x00\\x00\"\n\"\\x1E\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x50\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x51\\x00\\x00\\x00\"\n\"\\x1E\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x52\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x53\\x00\\x00\\x00\"\n\"\\x1E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x13\\x00\\x02\\x00\\x02\\x00\\x00\\x00\\x21\\x00\\x03\\x00\\x03\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x16\\x00\\x03\\x00\"\n\"\\x06\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x17\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x21\\x00\\x03\\x00\\x08\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x18\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x04\\x00\\x00\\x00\\x17\\x00\\x04\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x15\\x00\\x04\\x00\\x0F\\x00\\x00\\x00\\x20\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x0F\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x1C\\x00\\x04\\x00\\x11\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x10\\x00\\x00\\x00\\x17\\x00\\x04\\x00\\x12\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x1E\\x00\\x19\\x00\\x13\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\"\n\"\\x0D\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x11\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x14\\x00\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\x13\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x14\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x15\\x00\\x04\\x00\\x16\\x00\\x00\\x00\"\n\"\\x20\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x17\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x18\\x00\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\"\n\"\\x1C\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x00\\x00\\x80\\x3F\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\"\n\"\\x24\\x00\\x00\\x00\\x00\\x00\\x80\\xBF\\x2B\\x00\\x04\\x00\\x0F\\x00\\x00\\x00\\x25\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x26\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x0F\\x00\\x00\\x00\\x2B\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x0F\\x00\\x00\\x00\"\n\"\\x2E\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x32\\x00\\x00\\x00\\x00\\x00\\x00\\x40\\x20\\x00\\x04\\x00\\x38\\x00\\x00\\x00\"\n\"\\x03\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x38\\x00\\x00\\x00\\x39\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x16\\x00\\x00\\x00\"\n\"\\x3B\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x3C\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x1E\\x00\\x03\\x00\\x40\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x41\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x41\\x00\\x00\\x00\\x42\\x00\\x00\\x00\"\n\"\\x03\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x44\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x32\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x46\\x00\\x00\\x00\"\n\"\\x00\\x01\\x00\\x00\\x32\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x47\\x00\\x00\\x00\\x01\\x01\\x00\\x00\\x32\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x48\\x00\\x00\\x00\"\n\"\\x15\\x00\\x00\\x00\\x1E\\x00\\x0C\\x00\\x49\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x4A\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x49\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x4A\\x00\\x00\\x00\\x4B\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x4C\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x12\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x4C\\x00\\x00\\x00\\x4D\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x4E\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x4E\\x00\\x00\\x00\\x4F\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x4C\\x00\\x00\\x00\\x50\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\\x51\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\\x52\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\\x53\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x36\\x00\\x05\\x00\\x02\\x00\\x00\\x00\\x04\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x05\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0E\\x00\\x00\\x00\\x3A\\x00\\x00\\x00\\x1C\\x00\\x00\\x00\"\n\"\\x41\\x00\\x05\\x00\\x3C\\x00\\x00\\x00\\x3D\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x3B\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0E\\x00\\x00\\x00\\x3E\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x00\\x00\\x83\\x00\\x05\\x00\\x0E\\x00\\x00\\x00\\x3F\\x00\\x00\\x00\\x3A\\x00\\x00\\x00\\x3E\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x39\\x00\\x00\\x00\"\n\"\\x3F\\x00\\x00\\x00\\x39\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x43\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x44\\x00\\x00\\x00\\x45\\x00\\x00\\x00\"\n\"\\x42\\x00\\x00\\x00\\x17\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x45\\x00\\x00\\x00\\x43\\x00\\x00\\x00\\xFD\\x00\\x01\\x00\\x38\\x00\\x01\\x00\\x36\\x00\\x05\\x00\"\n\"\\x07\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x0A\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\"\n\"\\x0C\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x18\\x00\\x00\\x00\\x19\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x17\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x0D\\x00\\x00\\x00\\x1A\\x00\\x00\\x00\\x19\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0E\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x1C\\x00\\x00\\x00\\x51\\x00\\x05\\x00\"\n\"\\x06\\x00\\x00\\x00\\x1F\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x50\\x00\\x07\\x00\\x07\\x00\\x00\\x00\"\n\"\\x22\\x00\\x00\\x00\\x1F\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x91\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x1A\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x0C\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x26\\x00\\x00\\x00\\x27\\x00\\x00\\x00\"\n\"\\x0C\\x00\\x00\\x00\\x25\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x28\\x00\\x00\\x00\\x27\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\"\n\"\\x29\\x00\\x00\\x00\\x28\\x00\\x00\\x00\\x24\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x26\\x00\\x00\\x00\\x2A\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x25\\x00\\x00\\x00\"\n\"\\x3E\\x00\\x03\\x00\\x2A\\x00\\x00\\x00\\x29\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x26\\x00\\x00\\x00\\x2C\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x2B\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\\x2C\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x26\\x00\\x00\\x00\\x2F\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\"\n\"\\x2E\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x30\\x00\\x00\\x00\\x2F\\x00\\x00\\x00\\x81\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x31\\x00\\x00\\x00\"\n\"\\x2D\\x00\\x00\\x00\\x30\\x00\\x00\\x00\\x88\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x33\\x00\\x00\\x00\\x31\\x00\\x00\\x00\\x32\\x00\\x00\\x00\\x41\\x00\\x05\\x00\"\n\"\\x26\\x00\\x00\\x00\\x34\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x2B\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x34\\x00\\x00\\x00\\x33\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x07\\x00\\x00\\x00\\x35\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\xFE\\x00\\x02\\x00\\x35\\x00\\x00\\x00\\x38\\x00\\x01\\x00\\x03\\x02\\x23\\x07\\x00\\x00\\x01\\x00\"\n\"\\x0A\\x00\\x08\\x00\\x86\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x11\\x00\\x02\\x00\\x01\\x00\\x00\\x00\\x0B\\x00\\x06\\x00\\x01\\x00\\x00\\x00\\x47\\x4C\\x53\\x4C\"\n\"\\x2E\\x73\\x74\\x64\\x2E\\x34\\x35\\x30\\x00\\x00\\x00\\x00\\x0E\\x00\\x03\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x0F\\x00\\x08\\x00\\x04\\x00\\x00\\x00\"\n\"\\x04\\x00\\x00\\x00\\x6D\\x61\\x69\\x6E\\x00\\x00\\x00\\x00\\x1F\\x00\\x00\\x00\\x76\\x00\\x00\\x00\\x7A\\x00\\x00\\x00\\x10\\x00\\x03\\x00\\x04\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x0E\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x17\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x01\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\x1F\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x2C\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x2E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x2E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x2E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\"\n\"\\x2E\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x2E\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x2E\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x2E\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x2E\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x2E\\x00\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x2E\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xC0\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x2E\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xCC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x2E\\x00\\x00\\x00\\x05\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\xD0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x2E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xDC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x2E\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xE0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x2E\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\xEC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x2E\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xF0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x2E\\x00\\x00\\x00\"\n\"\\x0A\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xFC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x2E\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x01\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x2E\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x2E\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x4C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x2E\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x50\\x01\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x2E\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x5C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x2E\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x60\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x2E\\x00\\x00\\x00\\x11\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x6C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x2E\\x00\\x00\\x00\"\n\"\\x12\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x70\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x2E\\x00\\x00\\x00\\x13\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x01\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x2E\\x00\\x00\\x00\\x14\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x90\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x2E\\x00\\x00\\x00\\x15\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x94\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x2E\\x00\\x00\\x00\\x16\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x98\\x01\\x00\\x00\\x47\\x00\\x03\\x00\"\n\"\\x2E\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x30\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x30\\x00\\x00\\x00\"\n\"\\x21\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x4D\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\x73\\x00\\x00\\x00\"\n\"\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x73\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x76\\x00\\x00\\x00\"\n\"\\x1E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x7A\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x83\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x83\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x83\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x83\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x40\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x83\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x4C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x83\\x00\\x00\\x00\"\n\"\\x03\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x50\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x83\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x5C\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x83\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x60\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x83\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x6C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x83\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x70\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x83\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x83\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x88\\x00\\x00\\x00\\x47\\x00\\x03\\x00\\x83\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x85\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x85\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x13\\x00\\x02\\x00\\x02\\x00\\x00\\x00\\x21\\x00\\x03\\x00\\x03\\x00\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\x16\\x00\\x03\\x00\\x06\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x17\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x03\\x00\\x00\\x00\"\n\"\\x20\\x00\\x04\\x00\\x08\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x21\\x00\\x04\\x00\\x09\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x08\\x00\\x00\\x00\"\n\"\\x15\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x32\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x15\\x00\\x00\\x00\"\n\"\\x2B\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x14\\x00\\x02\\x00\\x10\\x00\\x00\\x00\\x34\\x00\\x06\\x00\\x10\\x00\\x00\\x00\"\n\"\\x11\\x00\\x00\\x00\\xAB\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\\x34\\x00\\x05\\x00\\x10\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\xA8\\x00\\x00\\x00\"\n\"\\x11\\x00\\x00\\x00\\x32\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\x17\\x00\\x00\\x00\\x01\\x01\\x00\\x00\\x34\\x00\\x06\\x00\\x10\\x00\\x00\\x00\\x18\\x00\\x00\\x00\"\n\"\\xAB\\x00\\x00\\x00\\x17\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x17\\x00\\x04\\x00\"\n\"\\x1D\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\"\n\"\\x1E\\x00\\x00\\x00\\x1F\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x15\\x00\\x04\\x00\\x20\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\"\n\"\\x20\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x22\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\"\n\"\\x20\\x00\\x00\\x00\\x25\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x18\\x00\\x04\\x00\\x2A\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\"\n\"\\x20\\x00\\x00\\x00\\x2B\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x1C\\x00\\x04\\x00\\x2C\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x2B\\x00\\x00\\x00\\x17\\x00\\x04\\x00\"\n\"\\x2D\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x1E\\x00\\x19\\x00\\x2E\\x00\\x00\\x00\\x2A\\x00\\x00\\x00\\x2A\\x00\\x00\\x00\\x2A\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x2C\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\"\n\"\\x1D\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x2F\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x2E\\x00\\x00\\x00\"\n\"\\x3B\\x00\\x04\\x00\\x2F\\x00\\x00\\x00\\x30\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\x31\\x00\\x00\\x00\\x14\\x00\\x00\\x00\"\n\"\\x20\\x00\\x04\\x00\\x32\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\x39\\x00\\x00\\x00\\x15\\x00\\x00\\x00\"\n\"\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x47\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x32\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\x4D\\x00\\x00\\x00\\x00\\x01\\x00\\x00\"\n\"\\x34\\x00\\x06\\x00\\x10\\x00\\x00\\x00\\x4E\\x00\\x00\\x00\\xAB\\x00\\x00\\x00\\x4D\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\"\n\"\\x55\\x00\\x00\\x00\\x3B\\xAA\\xB8\\x3F\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x59\\x00\\x00\\x00\\x00\\x00\\x80\\x3F\\x2B\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\"\n\"\\x5A\\x00\\x00\\x00\\x13\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x64\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x6E\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x19\\x00\\x09\\x00\\x70\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x1B\\x00\\x03\\x00\\x71\\x00\\x00\\x00\\x70\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x72\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x71\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x72\\x00\\x00\\x00\\x73\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x75\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x75\\x00\\x00\\x00\\x76\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x79\\x00\\x00\\x00\"\n\"\\x03\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x79\\x00\\x00\\x00\\x7A\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x1E\\x00\\x0C\\x00\\x83\\x00\\x00\\x00\"\n\"\\x2A\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\"\n\"\\x2D\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x84\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x83\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x84\\x00\\x00\\x00\"\n\"\\x85\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x36\\x00\\x05\\x00\\x02\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\"\n\"\\x05\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x6E\\x00\\x00\\x00\\x6F\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x08\\x00\\x00\\x00\\x7B\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x71\\x00\\x00\\x00\\x74\\x00\\x00\\x00\\x73\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x77\\x00\\x00\\x00\"\n\"\\x76\\x00\\x00\\x00\\x57\\x00\\x05\\x00\\x1D\\x00\\x00\\x00\\x78\\x00\\x00\\x00\\x74\\x00\\x00\\x00\\x77\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x6F\\x00\\x00\\x00\"\n\"\\x78\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x1D\\x00\\x00\\x00\\x7C\\x00\\x00\\x00\\x6F\\x00\\x00\\x00\\x4F\\x00\\x08\\x00\\x07\\x00\\x00\\x00\\x7D\\x00\\x00\\x00\"\n\"\\x7C\\x00\\x00\\x00\\x7C\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x7B\\x00\\x00\\x00\\x7D\\x00\\x00\\x00\"\n\"\\x39\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x7E\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x7B\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x7F\\x00\\x00\\x00\"\n\"\\x7E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x80\\x00\\x00\\x00\\x7E\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x51\\x00\\x05\\x00\"\n\"\\x06\\x00\\x00\\x00\\x81\\x00\\x00\\x00\\x7E\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x50\\x00\\x07\\x00\\x1D\\x00\\x00\\x00\\x82\\x00\\x00\\x00\\x7F\\x00\\x00\\x00\"\n\"\\x80\\x00\\x00\\x00\\x81\\x00\\x00\\x00\\x59\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x7A\\x00\\x00\\x00\\x82\\x00\\x00\\x00\\xFD\\x00\\x01\\x00\\x38\\x00\\x01\\x00\"\n\"\\x36\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x37\\x00\\x03\\x00\\x08\\x00\\x00\\x00\\x0A\\x00\\x00\\x00\"\n\"\\xF8\\x00\\x02\\x00\\x0C\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\\x1C\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\"\n\"\\x29\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\xF7\\x00\\x03\\x00\\x14\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\\x12\\x00\\x00\\x00\\x13\\x00\\x00\\x00\"\n\"\\x14\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x13\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x0A\\x00\\x00\\x00\\xFE\\x00\\x02\\x00\"\n\"\\x15\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x14\\x00\\x00\\x00\\xF7\\x00\\x03\\x00\\x1A\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\\x18\\x00\\x00\\x00\"\n\"\\x19\\x00\\x00\\x00\\x3E\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x19\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x22\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x1F\\x00\\x00\\x00\"\n\"\\x21\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x24\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x22\\x00\\x00\\x00\\x26\\x00\\x00\\x00\"\n\"\\x1F\\x00\\x00\\x00\\x25\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x27\\x00\\x00\\x00\\x26\\x00\\x00\\x00\\x88\\x00\\x05\\x00\\x06\\x00\\x00\\x00\"\n\"\\x28\\x00\\x00\\x00\\x24\\x00\\x00\\x00\\x27\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x1C\\x00\\x00\\x00\\x28\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x32\\x00\\x00\\x00\"\n\"\\x33\\x00\\x00\\x00\\x30\\x00\\x00\\x00\\x31\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x34\\x00\\x00\\x00\\x33\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x06\\x00\\x00\\x00\\x35\\x00\\x00\\x00\\x1C\\x00\\x00\\x00\\x83\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x36\\x00\\x00\\x00\\x34\\x00\\x00\\x00\\x35\\x00\\x00\\x00\"\n\"\\x41\\x00\\x05\\x00\\x32\\x00\\x00\\x00\\x37\\x00\\x00\\x00\\x30\\x00\\x00\\x00\\x31\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x38\\x00\\x00\\x00\"\n\"\\x37\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x32\\x00\\x00\\x00\\x3A\\x00\\x00\\x00\\x30\\x00\\x00\\x00\\x39\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\"\n\"\\x3B\\x00\\x00\\x00\\x3A\\x00\\x00\\x00\\x83\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x3C\\x00\\x00\\x00\\x38\\x00\\x00\\x00\\x3B\\x00\\x00\\x00\\x88\\x00\\x05\\x00\"\n\"\\x06\\x00\\x00\\x00\\x3D\\x00\\x00\\x00\\x36\\x00\\x00\\x00\\x3C\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x29\\x00\\x00\\x00\\x3D\\x00\\x00\\x00\\xF9\\x00\\x02\\x00\"\n\"\\x1A\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x3E\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x32\\x00\\x00\\x00\\x3F\\x00\\x00\\x00\\x30\\x00\\x00\\x00\\x31\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x3F\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x22\\x00\\x00\\x00\\x41\\x00\\x00\\x00\\x1F\\x00\\x00\\x00\"\n\"\\x21\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x42\\x00\\x00\\x00\\x41\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x43\\x00\\x00\\x00\"\n\"\\x40\\x00\\x00\\x00\\x42\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x22\\x00\\x00\\x00\\x44\\x00\\x00\\x00\\x1F\\x00\\x00\\x00\\x25\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x06\\x00\\x00\\x00\\x45\\x00\\x00\\x00\\x44\\x00\\x00\\x00\\x88\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x46\\x00\\x00\\x00\\x43\\x00\\x00\\x00\\x45\\x00\\x00\\x00\"\n\"\\x3E\\x00\\x03\\x00\\x1C\\x00\\x00\\x00\\x46\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x48\\x00\\x00\\x00\\x1C\\x00\\x00\\x00\\x41\\x00\\x05\\x00\"\n\"\\x32\\x00\\x00\\x00\\x49\\x00\\x00\\x00\\x30\\x00\\x00\\x00\\x39\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x4A\\x00\\x00\\x00\\x49\\x00\\x00\\x00\"\n\"\\x83\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x4B\\x00\\x00\\x00\\x48\\x00\\x00\\x00\\x4A\\x00\\x00\\x00\\x0C\\x00\\x07\\x00\\x06\\x00\\x00\\x00\\x4C\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x28\\x00\\x00\\x00\\x47\\x00\\x00\\x00\\x4B\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x1C\\x00\\x00\\x00\\x4C\\x00\\x00\\x00\\xF7\\x00\\x03\\x00\"\n\"\\x50\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\\x4E\\x00\\x00\\x00\\x4F\\x00\\x00\\x00\\x50\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x4F\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x51\\x00\\x00\\x00\\x1C\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x52\\x00\\x00\\x00\\x1C\\x00\\x00\\x00\"\n\"\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x53\\x00\\x00\\x00\\x52\\x00\\x00\\x00\\x51\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x1C\\x00\\x00\\x00\\x53\\x00\\x00\\x00\"\n\"\\xF9\\x00\\x02\\x00\\x50\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x50\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x54\\x00\\x00\\x00\\x1C\\x00\\x00\\x00\"\n\"\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x56\\x00\\x00\\x00\\x54\\x00\\x00\\x00\\x55\\x00\\x00\\x00\\x7F\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x57\\x00\\x00\\x00\"\n\"\\x56\\x00\\x00\\x00\\x0C\\x00\\x06\\x00\\x06\\x00\\x00\\x00\\x58\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x57\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\"\n\"\\x29\\x00\\x00\\x00\\x58\\x00\\x00\\x00\\xF9\\x00\\x02\\x00\\x1A\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x1A\\x00\\x00\\x00\\x41\\x00\\x06\\x00\\x32\\x00\\x00\\x00\"\n\"\\x5B\\x00\\x00\\x00\\x30\\x00\\x00\\x00\\x5A\\x00\\x00\\x00\\x25\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x5C\\x00\\x00\\x00\\x5B\\x00\\x00\\x00\"\n\"\\x83\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x5D\\x00\\x00\\x00\\x59\\x00\\x00\\x00\\x5C\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x5E\\x00\\x00\\x00\"\n\"\\x29\\x00\\x00\\x00\\x0C\\x00\\x08\\x00\\x06\\x00\\x00\\x00\\x5F\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x2B\\x00\\x00\\x00\\x5E\\x00\\x00\\x00\\x47\\x00\\x00\\x00\"\n\"\\x59\\x00\\x00\\x00\\x41\\x00\\x06\\x00\\x32\\x00\\x00\\x00\\x60\\x00\\x00\\x00\\x30\\x00\\x00\\x00\\x5A\\x00\\x00\\x00\\x25\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x06\\x00\\x00\\x00\\x61\\x00\\x00\\x00\\x60\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x62\\x00\\x00\\x00\\x5F\\x00\\x00\\x00\\x61\\x00\\x00\\x00\"\n\"\\x81\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x63\\x00\\x00\\x00\\x5D\\x00\\x00\\x00\\x62\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x29\\x00\\x00\\x00\\x63\\x00\\x00\\x00\"\n\"\\x41\\x00\\x05\\x00\\x64\\x00\\x00\\x00\\x65\\x00\\x00\\x00\\x30\\x00\\x00\\x00\\x5A\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x1D\\x00\\x00\\x00\\x66\\x00\\x00\\x00\"\n\"\\x65\\x00\\x00\\x00\\x4F\\x00\\x08\\x00\\x07\\x00\\x00\\x00\\x67\\x00\\x00\\x00\\x66\\x00\\x00\\x00\\x66\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x68\\x00\\x00\\x00\\x0A\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x69\\x00\\x00\\x00\"\n\"\\x29\\x00\\x00\\x00\\x50\\x00\\x06\\x00\\x07\\x00\\x00\\x00\\x6A\\x00\\x00\\x00\\x69\\x00\\x00\\x00\\x69\\x00\\x00\\x00\\x69\\x00\\x00\\x00\\x0C\\x00\\x08\\x00\"\n\"\\x07\\x00\\x00\\x00\\x6B\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x2E\\x00\\x00\\x00\\x67\\x00\\x00\\x00\\x68\\x00\\x00\\x00\\x6A\\x00\\x00\\x00\\xFE\\x00\\x02\\x00\"\n\"\\x6B\\x00\\x00\\x00\\x38\\x00\\x01\\x00\"},\n#endif\n#ifdef D3D9QUAKE\n{QR_DIRECT3D9, 9, \"defaultskybox\",\n\"!!samps reflectcube\\n\"\n\n\"struct a2v\\n\"\n\"{\\n\"\n\"float4 pos: POSITION;\\n\"\n\"};\\n\"\n\"struct v2f\\n\"\n\"{\\n\"\n\"#ifndef FRAGMENT_SHADER\\n\"\n\"float4 pos: POSITION;\\n\"\n\"#endif\\n\"\n\"float3 texc: TEXCOORD0;\\n\"\n\"};\\n\"\n\n\"#ifdef VERTEX_SHADER\\n\"\n\"float4x4  m_modelviewprojection;\\n\"\n\"v2f main (a2v inp)\\n\"\n\"{\\n\"\n\"v2f outp;\\n\"\n\"outp.pos = mul(m_modelviewprojection, inp.pos);\\n\"\n\"outp.texc = inp.pos.xyz;\\n\"\n\"return outp;\\n\"\n\"}\\n\"\n\"#endif\\n\"\n\n\"#ifdef FRAGMENT_SHADER\\n\"\n\"float3 e_eyepos;\\n\"\n\"sampler s_reflectcube;\\n\"\n\"float4 main (v2f inp) : COLOR0\\n\"\n\"{\\n\"\n\"float3 tc = inp.texc - e_eyepos.xyz;\\n\"\n\"return texCUBE(s_reflectcube, tc);\\n\"\n\"}\\n\"\n\"#endif\\n\"\n},\n#endif\n#ifdef D3D11QUAKE\n{QR_DIRECT3D11, 11, \"defaultskybox\",\n\"!!samps reflectcube\\n\"\n\n//regular sky shader for scrolling q1 skies\n//the sky surfaces are thrown through this as-is.\n\n\"struct a2v\\n\"\n\"{\\n\"\n\"float4 pos: POSITION;\\n\"\n\"};\\n\"\n\"struct v2f\\n\"\n\"{\\n\"\n\"float4 pos: SV_POSITION;\\n\"\n\"float3 texc: TEXCOORD0;\\n\"\n\"};\\n\"\n\n\"#include <ftedefs.h>\\n\"\n\n\"#ifdef VERTEX_SHADER\\n\"\n\"v2f main (a2v inp)\\n\"\n\"{\\n\"\n\"v2f outp;\\n\"\n\"outp.pos = mul(m_model, inp.pos);\\n\"\n\"outp.texc= outp.pos.xyz - v_eyepos;\\n\"\n\"outp.pos = mul(m_view, outp.pos);\\n\"\n\"outp.pos = mul(m_projection, outp.pos);\\n\"\n\"return outp;\\n\"\n\"}\\n\"\n\"#endif\\n\"\n\n\"#ifdef FRAGMENT_SHADER\\n\"\n\"TextureCube t_reflectcube : register(t0);\\n\"\n\"SamplerState s_reflectcube : register(s0);\\n\"\n\n\"float4 main (v2f inp) : SV_TARGET\\n\"\n\"{\\n\"\n\"return t_reflectcube.Sample(s_reflectcube, inp.texc);\\n\"\n\"}\\n\"\n\"#endif\\n\"\n},\n#endif\n#ifdef GLQUAKE\n{QR_OPENGL, 110, \"defaultfill\",\n\"!!ver 100-450\\n\"\n\n\"#ifdef VERTEX_SHADER\\n\"\n\"attribute vec4 v_colour;\\n\"\n\"varying vec4 vc;\\n\"\n\n\"void main ()\\n\"\n\"{\\n\"\n\"vc = v_colour;\\n\"\n\"gl_Position = ftetransform();\\n\"\n\"}\\n\"\n\"#endif\\n\"\n\n\"#ifdef FRAGMENT_SHADER\\n\"\n\"varying vec4 vc;\\n\"\n\"void main ()\\n\"\n\"{\\n\"\n\"gl_FragColor = vc;\\n\"\n\"}\\n\"\n\"#endif\\n\"\n},\n#endif\n#ifdef VKQUAKE\n{QR_VULKAN, -1, \"defaultfill\",\n\"\\xFF\\x53\\x50\\x56\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x2C\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x2C\\x00\\x00\\x00\"\n\"\\xFC\\x09\\x00\\x00\\x28\\x0A\\x00\\x00\\xF8\\x05\\x00\\x00\\x03\\x02\\x23\\x07\\x00\\x00\\x01\\x00\\x0A\\x00\\x08\\x00\\x4B\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x11\\x00\\x02\\x00\\x01\\x00\\x00\\x00\\x0B\\x00\\x06\\x00\\x01\\x00\\x00\\x00\\x47\\x4C\\x53\\x4C\\x2E\\x73\\x74\\x64\\x2E\\x34\\x35\\x30\\x00\\x00\\x00\\x00\"\n\"\\x0E\\x00\\x03\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x0F\\x00\\x0E\\x00\\x00\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x6D\\x61\\x69\\x6E\\x00\\x00\\x00\\x00\"\n\"\\x1C\\x00\\x00\\x00\\x39\\x00\\x00\\x00\\x3B\\x00\\x00\\x00\\x3F\\x00\\x00\\x00\\x46\\x00\\x00\\x00\\x47\\x00\\x00\\x00\\x48\\x00\\x00\\x00\\x49\\x00\\x00\\x00\"\n\"\\x4A\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x11\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x13\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x13\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x13\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x13\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x80\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x13\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xC0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\xCC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xD0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xDC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xE0\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xEC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x09\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\xF0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x0A\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xFC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x13\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x40\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x4C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\"\n\"\\x0E\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x50\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x5C\\x01\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x60\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x11\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x6C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x70\\x01\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x13\\x00\\x00\\x00\\x13\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x14\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x90\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x94\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\"\n\"\\x16\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x98\\x01\\x00\\x00\\x47\\x00\\x03\\x00\\x13\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x15\\x00\\x00\\x00\"\n\"\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x15\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x1C\\x00\\x00\\x00\"\n\"\\x1E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x39\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x3B\\x00\\x00\\x00\"\n\"\\x1E\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x3D\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x03\\x00\"\n\"\\x3D\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x42\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x42\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x42\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x42\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x42\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x4C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x42\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x50\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x42\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x5C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x42\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x60\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x42\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x6C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x42\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x70\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x42\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x42\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x88\\x00\\x00\\x00\\x47\\x00\\x03\\x00\\x42\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x44\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x44\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x46\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x47\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x03\\x00\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x48\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x49\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x05\\x00\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x4A\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x13\\x00\\x02\\x00\\x02\\x00\\x00\\x00\\x21\\x00\\x03\\x00\\x03\\x00\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\x16\\x00\\x03\\x00\\x06\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x17\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x04\\x00\\x00\\x00\"\n\"\\x21\\x00\\x03\\x00\\x08\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x18\\x00\\x04\\x00\"\n\"\\x0D\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x17\\x00\\x04\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x15\\x00\\x04\\x00\"\n\"\\x0F\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x0F\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x1C\\x00\\x04\\x00\"\n\"\\x11\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x17\\x00\\x04\\x00\\x12\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x1E\\x00\\x19\\x00\"\n\"\\x13\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x11\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x12\\x00\\x00\\x00\"\n\"\\x20\\x00\\x04\\x00\\x14\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x13\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x14\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x15\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x17\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x20\\x00\\x04\\x00\\x18\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\"\n\"\\x3B\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\\x1C\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x00\\x00\\x80\\x3F\"\n\"\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x24\\x00\\x00\\x00\\x00\\x00\\x80\\xBF\\x2B\\x00\\x04\\x00\\x0F\\x00\\x00\\x00\\x25\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x20\\x00\\x04\\x00\\x26\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x0F\\x00\\x00\\x00\\x2B\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x2B\\x00\\x04\\x00\\x0F\\x00\\x00\\x00\\x2E\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x32\\x00\\x00\\x00\\x00\\x00\\x00\\x40\"\n\"\\x20\\x00\\x04\\x00\\x38\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x38\\x00\\x00\\x00\\x39\\x00\\x00\\x00\\x03\\x00\\x00\\x00\"\n\"\\x20\\x00\\x04\\x00\\x3A\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x3A\\x00\\x00\\x00\\x3B\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x1E\\x00\\x03\\x00\\x3D\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x3E\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x3D\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\"\n\"\\x3E\\x00\\x00\\x00\\x3F\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x1E\\x00\\x0C\\x00\\x42\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x20\\x00\\x04\\x00\"\n\"\\x43\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x42\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x43\\x00\\x00\\x00\\x44\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x20\\x00\\x04\\x00\"\n\"\\x45\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x45\\x00\\x00\\x00\\x46\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\"\n\"\\x45\\x00\\x00\\x00\\x47\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\\x48\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\"\n\"\\x1B\\x00\\x00\\x00\\x49\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\\x4A\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x36\\x00\\x05\\x00\"\n\"\\x02\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x05\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\"\n\"\\x3C\\x00\\x00\\x00\\x3B\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x39\\x00\\x00\\x00\\x3C\\x00\\x00\\x00\\x39\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x40\\x00\\x00\\x00\"\n\"\\x09\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x38\\x00\\x00\\x00\\x41\\x00\\x00\\x00\\x3F\\x00\\x00\\x00\\x17\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x41\\x00\\x00\\x00\"\n\"\\x40\\x00\\x00\\x00\\xFD\\x00\\x01\\x00\\x38\\x00\\x01\\x00\\x36\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x08\\x00\\x00\\x00\"\n\"\\xF8\\x00\\x02\\x00\\x0A\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x18\\x00\\x00\\x00\"\n\"\\x19\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x17\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\x1A\\x00\\x00\\x00\\x19\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x0E\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x1C\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x1F\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x21\\x00\\x00\\x00\"\n\"\\x1D\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x50\\x00\\x07\\x00\\x07\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x1F\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x21\\x00\\x00\\x00\"\n\"\\x1E\\x00\\x00\\x00\\x91\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x1A\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x0C\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x26\\x00\\x00\\x00\\x27\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x25\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\"\n\"\\x28\\x00\\x00\\x00\\x27\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x29\\x00\\x00\\x00\\x28\\x00\\x00\\x00\\x24\\x00\\x00\\x00\\x41\\x00\\x05\\x00\"\n\"\\x26\\x00\\x00\\x00\\x2A\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x25\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x2A\\x00\\x00\\x00\\x29\\x00\\x00\\x00\\x41\\x00\\x05\\x00\"\n\"\\x26\\x00\\x00\\x00\\x2C\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x2B\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\\x2C\\x00\\x00\\x00\"\n\"\\x41\\x00\\x05\\x00\\x26\\x00\\x00\\x00\\x2F\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x2E\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x30\\x00\\x00\\x00\"\n\"\\x2F\\x00\\x00\\x00\\x81\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x31\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\\x30\\x00\\x00\\x00\\x88\\x00\\x05\\x00\\x06\\x00\\x00\\x00\"\n\"\\x33\\x00\\x00\\x00\\x31\\x00\\x00\\x00\\x32\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x26\\x00\\x00\\x00\\x34\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x2B\\x00\\x00\\x00\"\n\"\\x3E\\x00\\x03\\x00\\x34\\x00\\x00\\x00\\x33\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x35\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\xFE\\x00\\x02\\x00\"\n\"\\x35\\x00\\x00\\x00\\x38\\x00\\x01\\x00\\x03\\x02\\x23\\x07\\x00\\x00\\x01\\x00\\x0A\\x00\\x08\\x00\\x19\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x11\\x00\\x02\\x00\"\n\"\\x01\\x00\\x00\\x00\\x0B\\x00\\x06\\x00\\x01\\x00\\x00\\x00\\x47\\x4C\\x53\\x4C\\x2E\\x73\\x74\\x64\\x2E\\x34\\x35\\x30\\x00\\x00\\x00\\x00\\x0E\\x00\\x03\\x00\"\n\"\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x0F\\x00\\x07\\x00\\x04\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x6D\\x61\\x69\\x6E\\x00\\x00\\x00\\x00\\x09\\x00\\x00\\x00\"\n\"\\x0B\\x00\\x00\\x00\\x10\\x00\\x03\\x00\\x04\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x09\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x11\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x10\\x00\\x00\\x00\"\n\"\\x48\\x00\\x04\\x00\\x13\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x13\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x13\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x13\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x05\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xC0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x13\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xCC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\xD0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xDC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xE0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xEC\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xF0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x0A\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\xFC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x13\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x4C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x50\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\"\n\"\\x0F\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x5C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x60\\x01\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x11\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x6C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x12\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x70\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x13\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x01\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x13\\x00\\x00\\x00\\x14\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x90\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x94\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x16\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x98\\x01\\x00\\x00\\x47\\x00\\x03\\x00\\x13\\x00\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x15\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x15\\x00\\x00\\x00\\x21\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x16\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x16\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x16\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x16\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x4C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x16\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x50\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x16\\x00\\x00\\x00\"\n\"\\x04\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x5C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x16\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x60\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x16\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x6C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x16\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x70\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x16\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x16\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x88\\x00\\x00\\x00\\x47\\x00\\x03\\x00\\x16\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\"\n\"\\x18\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x18\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x13\\x00\\x02\\x00\"\n\"\\x02\\x00\\x00\\x00\\x21\\x00\\x03\\x00\\x03\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x16\\x00\\x03\\x00\\x06\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x17\\x00\\x04\\x00\"\n\"\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x08\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\"\n\"\\x08\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x0A\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\"\n\"\\x0A\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x18\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x17\\x00\\x04\\x00\"\n\"\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x15\\x00\\x04\\x00\\x0F\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\"\n\"\\x0F\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x1C\\x00\\x04\\x00\\x11\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x17\\x00\\x04\\x00\"\n\"\\x12\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x1E\\x00\\x19\\x00\\x13\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\"\n\"\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x11\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x14\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x13\\x00\\x00\\x00\"\n\"\\x3B\\x00\\x04\\x00\\x14\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x1E\\x00\\x0C\\x00\\x16\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x12\\x00\\x00\\x00\"\n\"\\x20\\x00\\x04\\x00\\x17\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x16\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x17\\x00\\x00\\x00\\x18\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x36\\x00\\x05\\x00\\x02\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x05\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x07\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x09\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\xFD\\x00\\x01\\x00\\x38\\x00\\x01\\x00\"\n\"\"},\n#endif\n#ifdef D3D11QUAKE\n{QR_DIRECT3D11, 11, \"defaultfill\",\n\"struct a2v\\n\"\n\"{\\n\"\n\"float4 pos: POSITION;\\n\"\n\"float4 vcol: COLOR0;\\n\"\n\"};\\n\"\n\"struct v2f\\n\"\n\"{\\n\"\n\"float4 pos: SV_POSITION;\\n\"\n\"float4 vcol: COLOR0;\\n\"\n\"};\\n\"\n\n\"#include <ftedefs.h>\\n\"\n\n\"#ifdef VERTEX_SHADER\\n\"\n\"v2f main (a2v inp)\\n\"\n\"{\\n\"\n\"v2f outp;\\n\"\n\"outp.pos = mul(m_projection, inp.pos);\\n\"\n\"outp.vcol = inp.vcol;\\n\"\n\"return outp;\\n\"\n\"}\\n\"\n\"#endif\\n\"\n\n\"#ifdef FRAGMENT_SHADER\\n\"\n\"float4 main (v2f inp) : SV_TARGET\\n\"\n\"{\\n\"\n\"return inp.vcol;\\n\"\n\"}\\n\"\n\"#endif\\n\"\n\n},\n#endif\n#ifdef GLQUAKE\n{QR_OPENGL, 110, \"defaultsprite\",\n\"!!permu FOG\\n\"\n\"!!samps 1\\n\"\n//used by both particles and sprites.\n//note the fog blending mode is all that differs from defaultadditivesprite\n\n\"#include \\\"sys/defs.h\\\"\\n\"\n\"#include \\\"sys/fog.h\\\"\\n\"\n\"varying vec2 tc;\\n\"\n\"varying vec4 vc;\\n\"\n\"#ifdef VERTEX_SHADER\\n\"\n\"void main ()\\n\"\n\"{\\n\"\n\"tc = v_texcoord;\\n\"\n\"vc = v_colour;\\n\"\n\"gl_Position = ftetransform();\\n\"\n\"}\\n\"\n\"#endif\\n\"\n\"#ifdef FRAGMENT_SHADER\\n\"\n\"uniform vec4 e_vlscale;\\n\"\n\"void main ()\\n\"\n\"{\\n\"\n\"vec4 col = texture2D(s_t0, tc);\\n\"\n\"#ifdef MASK\\n\"\n\"if (col.a < float(MASK))\\n\"\n\"discard;\\n\"\n\"#endif\\n\"\n\"gl_FragColor = fog4blend(col * vc * e_colourident * e_vlscale);\\n\"\n\"}\\n\"\n\"#endif\\n\"\n},\n#endif\n#ifdef VKQUAKE\n{QR_VULKAN, -1, \"defaultsprite\",\n\"\\xFF\\x53\\x50\\x56\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x2C\\x00\\x00\\x00\\x35\\x00\\x00\\x00\\x64\\x00\\x00\\x00\"\n\"\\xCC\\x0A\\x00\\x00\\x30\\x0B\\x00\\x00\\x58\\x0F\\x00\\x00\\x01\\x00\\x46\\x31\\x4D\\x41\\x53\\x4B\\x00\\x00\\x00\\x00\\x00\\x01\\x01\\x62\\x31\\x72\\x5F\\x66\"\n\"\\x6F\\x67\\x5F\\x65\\x78\\x70\\x32\\x00\\x00\\x00\\x00\\x00\\x01\\x02\\x62\\x31\\x72\\x5F\\x66\\x6F\\x67\\x5F\\x6C\\x69\\x6E\\x65\\x61\\x72\\x00\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x03\\x02\\x23\\x07\\x00\\x00\\x01\\x00\\x0A\\x00\\x08\\x00\\x52\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x11\\x00\\x02\\x00\\x01\\x00\\x00\\x00\"\n\"\\x0B\\x00\\x06\\x00\\x01\\x00\\x00\\x00\\x47\\x4C\\x53\\x4C\\x2E\\x73\\x74\\x64\\x2E\\x34\\x35\\x30\\x00\\x00\\x00\\x00\\x0E\\x00\\x03\\x00\\x00\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x0F\\x00\\x0F\\x00\\x00\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x6D\\x61\\x69\\x6E\\x00\\x00\\x00\\x00\\x1C\\x00\\x00\\x00\\x39\\x00\\x00\\x00\"\n\"\\x3B\\x00\\x00\\x00\\x3E\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x44\\x00\\x00\\x00\\x4E\\x00\\x00\\x00\\x4F\\x00\\x00\\x00\\x50\\x00\\x00\\x00\\x51\\x00\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x11\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x13\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x05\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x13\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\"\n\"\\x48\\x00\\x04\\x00\\x13\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x80\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\"\n\"\\x03\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xC0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xCC\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xD0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\xDC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xE0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x13\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xEC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\xF0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x0A\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xFC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\"\n\"\\x0B\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x01\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x4C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x50\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x5C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x13\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x60\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x11\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x6C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x70\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\"\n\"\\x13\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x14\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x90\\x01\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x94\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x16\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x98\\x01\\x00\\x00\\x47\\x00\\x03\\x00\\x13\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x15\\x00\\x00\\x00\\x22\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x15\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x1C\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x39\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x3B\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x3E\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x40\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x42\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x03\\x00\\x42\\x00\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x47\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\x48\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x01\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\x49\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x02\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\x4A\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x15\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x4B\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x4B\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x4B\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x4B\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x4B\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x4C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x4B\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x50\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x4B\\x00\\x00\\x00\"\n\"\\x04\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x5C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x4B\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x60\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x4B\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x6C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x4B\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x70\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x4B\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x4B\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x88\\x00\\x00\\x00\\x47\\x00\\x03\\x00\\x4B\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\"\n\"\\x4D\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x4D\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x47\\x00\\x04\\x00\"\n\"\\x4E\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x4F\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x47\\x00\\x04\\x00\"\n\"\\x50\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x51\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x13\\x00\\x02\\x00\"\n\"\\x02\\x00\\x00\\x00\\x21\\x00\\x03\\x00\\x03\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x16\\x00\\x03\\x00\\x06\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x17\\x00\\x04\\x00\"\n\"\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x21\\x00\\x03\\x00\\x08\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x18\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x17\\x00\\x04\\x00\\x0E\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x15\\x00\\x04\\x00\\x0F\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x0F\\x00\\x00\\x00\"\n\"\\x10\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x1C\\x00\\x04\\x00\\x11\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x17\\x00\\x04\\x00\\x12\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x1E\\x00\\x19\\x00\\x13\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x11\\x00\\x00\\x00\"\n\"\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x14\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x13\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\"\n\"\\x14\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x15\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\"\n\"\\x16\\x00\\x00\\x00\\x17\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x18\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x20\\x00\\x04\\x00\"\n\"\\x1B\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\\x1C\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\"\n\"\\x06\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x00\\x00\\x80\\x3F\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x24\\x00\\x00\\x00\\x00\\x00\\x80\\xBF\\x2B\\x00\\x04\\x00\"\n\"\\x0F\\x00\\x00\\x00\\x25\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x26\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\"\n\"\\x0F\\x00\\x00\\x00\\x2B\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x0F\\x00\\x00\\x00\\x2E\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\"\n\"\\x06\\x00\\x00\\x00\\x32\\x00\\x00\\x00\\x00\\x00\\x00\\x40\\x20\\x00\\x04\\x00\\x38\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\"\n\"\\x38\\x00\\x00\\x00\\x39\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x3A\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\"\n\"\\x3A\\x00\\x00\\x00\\x3B\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x3D\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\"\n\"\\x3D\\x00\\x00\\x00\\x3E\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x3F\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\"\n\"\\x3F\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x1E\\x00\\x03\\x00\\x42\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x43\\x00\\x00\\x00\"\n\"\\x03\\x00\\x00\\x00\\x42\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x43\\x00\\x00\\x00\\x44\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x32\\x00\\x04\\x00\\x06\\x00\\x00\\x00\"\n\"\\x47\\x00\\x00\\x00\\x00\\x00\\x80\\x43\\x32\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x48\\x00\\x00\\x00\\x01\\x01\\x00\\x00\\x32\\x00\\x04\\x00\\x16\\x00\\x00\\x00\"\n\"\\x49\\x00\\x00\\x00\\x02\\x01\\x00\\x00\\x32\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x4A\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x1E\\x00\\x0C\\x00\\x4B\\x00\\x00\\x00\"\n\"\\x0D\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x12\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x4C\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x4B\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x4C\\x00\\x00\\x00\"\n\"\\x4D\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x3A\\x00\\x00\\x00\\x4E\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\"\n\"\\x4F\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\\x50\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\"\n\"\\x51\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x36\\x00\\x05\\x00\\x02\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\"\n\"\\x05\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x12\\x00\\x00\\x00\\x3C\\x00\\x00\\x00\\x3B\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x39\\x00\\x00\\x00\\x3C\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x41\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x3E\\x00\\x00\\x00\\x41\\x00\\x00\\x00\\x39\\x00\\x04\\x00\"\n\"\\x07\\x00\\x00\\x00\\x45\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x3D\\x00\\x00\\x00\\x46\\x00\\x00\\x00\\x44\\x00\\x00\\x00\\x17\\x00\\x00\\x00\"\n\"\\x3E\\x00\\x03\\x00\\x46\\x00\\x00\\x00\\x45\\x00\\x00\\x00\\xFD\\x00\\x01\\x00\\x38\\x00\\x01\\x00\\x36\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x09\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x0A\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x41\\x00\\x05\\x00\\x18\\x00\\x00\\x00\\x19\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x17\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\x1A\\x00\\x00\\x00\"\n\"\\x19\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0E\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x1C\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x1F\\x00\\x00\\x00\"\n\"\\x1D\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x51\\x00\\x05\\x00\"\n\"\\x06\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x50\\x00\\x07\\x00\\x07\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x1F\\x00\\x00\\x00\"\n\"\\x20\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x91\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x1A\\x00\\x00\\x00\\x22\\x00\\x00\\x00\"\n\"\\x3E\\x00\\x03\\x00\\x0C\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x26\\x00\\x00\\x00\\x27\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x25\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x28\\x00\\x00\\x00\\x27\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x29\\x00\\x00\\x00\\x28\\x00\\x00\\x00\"\n\"\\x24\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x26\\x00\\x00\\x00\\x2A\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x25\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x2A\\x00\\x00\\x00\"\n\"\\x29\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x26\\x00\\x00\\x00\\x2C\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x2B\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\"\n\"\\x2D\\x00\\x00\\x00\\x2C\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x26\\x00\\x00\\x00\\x2F\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x2E\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x06\\x00\\x00\\x00\\x30\\x00\\x00\\x00\\x2F\\x00\\x00\\x00\\x81\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x31\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\\x30\\x00\\x00\\x00\"\n\"\\x88\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x33\\x00\\x00\\x00\\x31\\x00\\x00\\x00\\x32\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x26\\x00\\x00\\x00\\x34\\x00\\x00\\x00\"\n\"\\x0C\\x00\\x00\\x00\\x2B\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x34\\x00\\x00\\x00\\x33\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x35\\x00\\x00\\x00\"\n\"\\x0C\\x00\\x00\\x00\\xFE\\x00\\x02\\x00\\x35\\x00\\x00\\x00\\x38\\x00\\x01\\x00\\x03\\x02\\x23\\x07\\x00\\x00\\x01\\x00\\x0A\\x00\\x08\\x00\\x8F\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x11\\x00\\x02\\x00\\x01\\x00\\x00\\x00\\x0B\\x00\\x06\\x00\\x01\\x00\\x00\\x00\\x47\\x4C\\x53\\x4C\\x2E\\x73\\x74\\x64\\x2E\\x34\\x35\\x30\"\n\"\\x00\\x00\\x00\\x00\\x0E\\x00\\x03\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x0F\\x00\\x09\\x00\\x04\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x6D\\x61\\x69\\x6E\"\n\"\\x00\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x71\\x00\\x00\\x00\\x80\\x00\\x00\\x00\\x82\\x00\\x00\\x00\\x10\\x00\\x03\\x00\\x04\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x0E\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x17\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x02\\x01\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x2C\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x10\\x00\\x00\\x00\"\n\"\\x48\\x00\\x04\\x00\\x2E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x2E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x2E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x2E\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x2E\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x2E\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x2E\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x05\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x2E\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x2E\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x2E\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xC0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x2E\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xCC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x2E\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\xD0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x2E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xDC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x2E\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xE0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x2E\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xEC\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x2E\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xF0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x2E\\x00\\x00\\x00\\x0A\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\xFC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x2E\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x2E\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x2E\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x4C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x2E\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x50\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x2E\\x00\\x00\\x00\"\n\"\\x0F\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x5C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x2E\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x60\\x01\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x2E\\x00\\x00\\x00\\x11\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x6C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x2E\\x00\\x00\\x00\\x12\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x70\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x2E\\x00\\x00\\x00\\x13\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x01\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x2E\\x00\\x00\\x00\\x14\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x90\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x2E\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x94\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x2E\\x00\\x00\\x00\\x16\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x98\\x01\\x00\\x00\\x47\\x00\\x03\\x00\\x2E\\x00\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x30\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x30\\x00\\x00\\x00\\x21\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x4D\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x01\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\x6E\\x00\\x00\\x00\\x22\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x6E\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x71\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x74\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\x80\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x82\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x8C\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x8C\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x8C\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x8C\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x8C\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x4C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x8C\\x00\\x00\\x00\\x03\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x50\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x8C\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x5C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x8C\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x60\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x8C\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x6C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x8C\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x70\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x8C\\x00\\x00\\x00\"\n\"\\x08\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x8C\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x88\\x00\\x00\\x00\"\n\"\\x47\\x00\\x03\\x00\\x8C\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x8E\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\"\n\"\\x8E\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x13\\x00\\x02\\x00\\x02\\x00\\x00\\x00\\x21\\x00\\x03\\x00\\x03\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x16\\x00\\x03\\x00\\x06\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x17\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x20\\x00\\x04\\x00\"\n\"\\x08\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x21\\x00\\x04\\x00\\x09\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\x15\\x00\\x04\\x00\"\n\"\\x0D\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x32\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\"\n\"\\x0D\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x14\\x00\\x02\\x00\\x10\\x00\\x00\\x00\\x34\\x00\\x06\\x00\\x10\\x00\\x00\\x00\\x11\\x00\\x00\\x00\"\n\"\\xAB\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\\x34\\x00\\x05\\x00\\x10\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\xA8\\x00\\x00\\x00\\x11\\x00\\x00\\x00\"\n\"\\x32\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\x17\\x00\\x00\\x00\\x02\\x01\\x00\\x00\\x34\\x00\\x06\\x00\\x10\\x00\\x00\\x00\\x18\\x00\\x00\\x00\\xAB\\x00\\x00\\x00\"\n\"\\x17\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x1D\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x1D\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x15\\x00\\x04\\x00\\x1F\\x00\\x00\\x00\"\n\"\\x20\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x1F\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x21\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x1F\\x00\\x00\\x00\\x24\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x18\\x00\\x04\\x00\\x29\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x17\\x00\\x04\\x00\\x2A\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x1F\\x00\\x00\\x00\"\n\"\\x2B\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x1C\\x00\\x04\\x00\\x2C\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x2B\\x00\\x00\\x00\\x17\\x00\\x04\\x00\\x2D\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x1E\\x00\\x19\\x00\\x2E\\x00\\x00\\x00\\x29\\x00\\x00\\x00\\x29\\x00\\x00\\x00\\x29\\x00\\x00\\x00\\x2A\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x2A\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x2A\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x2A\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x2C\\x00\\x00\\x00\"\n\"\\x2A\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x2A\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x2A\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x2F\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x2E\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\"\n\"\\x2F\\x00\\x00\\x00\\x30\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\x31\\x00\\x00\\x00\\x14\\x00\\x00\\x00\\x20\\x00\\x04\\x00\"\n\"\\x32\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\x39\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\"\n\"\\x06\\x00\\x00\\x00\\x47\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x32\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\x4D\\x00\\x00\\x00\\x01\\x01\\x00\\x00\\x34\\x00\\x06\\x00\"\n\"\\x10\\x00\\x00\\x00\\x4E\\x00\\x00\\x00\\xAB\\x00\\x00\\x00\\x4D\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x55\\x00\\x00\\x00\"\n\"\\x3B\\xAA\\xB8\\x3F\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x59\\x00\\x00\\x00\\x00\\x00\\x80\\x3F\\x2B\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\x5A\\x00\\x00\\x00\"\n\"\\x13\\x00\\x00\\x00\\x19\\x00\\x09\\x00\\x6B\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x1B\\x00\\x03\\x00\\x6C\\x00\\x00\\x00\\x6B\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x6D\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x6C\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x6D\\x00\\x00\\x00\\x6E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x70\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x2D\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x70\\x00\\x00\\x00\\x71\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x32\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x74\\x00\\x00\\x00\"\n\"\\x00\\x00\\x80\\x43\\x20\\x00\\x04\\x00\\x7F\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x7F\\x00\\x00\\x00\\x80\\x00\\x00\\x00\"\n\"\\x03\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x1D\\x00\\x00\\x00\\x82\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\x85\\x00\\x00\\x00\"\n\"\\x12\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x86\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x1E\\x00\\x0C\\x00\\x8C\\x00\\x00\\x00\\x29\\x00\\x00\\x00\"\n\"\\x2A\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x2A\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x2A\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\"\n\"\\x2D\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x8D\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x8C\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x8D\\x00\\x00\\x00\\x8E\\x00\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\x36\\x00\\x05\\x00\\x02\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x05\\x00\\x00\\x00\"\n\"\\x3B\\x00\\x04\\x00\\x08\\x00\\x00\\x00\\x6A\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x08\\x00\\x00\\x00\\x8A\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x6C\\x00\\x00\\x00\\x6F\\x00\\x00\\x00\\x6E\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x2D\\x00\\x00\\x00\\x72\\x00\\x00\\x00\\x71\\x00\\x00\\x00\"\n\"\\x57\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x73\\x00\\x00\\x00\\x6F\\x00\\x00\\x00\\x72\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x6A\\x00\\x00\\x00\\x73\\x00\\x00\\x00\"\n\"\\xB7\\x00\\x05\\x00\\x10\\x00\\x00\\x00\\x75\\x00\\x00\\x00\\x74\\x00\\x00\\x00\\x47\\x00\\x00\\x00\\xF7\\x00\\x03\\x00\\x77\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\xFA\\x00\\x04\\x00\\x75\\x00\\x00\\x00\\x76\\x00\\x00\\x00\\x77\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x76\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x1B\\x00\\x00\\x00\"\n\"\\x78\\x00\\x00\\x00\\x6A\\x00\\x00\\x00\\x24\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x79\\x00\\x00\\x00\\x78\\x00\\x00\\x00\\xB8\\x00\\x05\\x00\"\n\"\\x10\\x00\\x00\\x00\\x7A\\x00\\x00\\x00\\x79\\x00\\x00\\x00\\x74\\x00\\x00\\x00\\xF9\\x00\\x02\\x00\\x77\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x77\\x00\\x00\\x00\"\n\"\\xF5\\x00\\x07\\x00\\x10\\x00\\x00\\x00\\x7B\\x00\\x00\\x00\\x75\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x7A\\x00\\x00\\x00\\x76\\x00\\x00\\x00\\xF7\\x00\\x03\\x00\"\n\"\\x7D\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\\x7B\\x00\\x00\\x00\\x7C\\x00\\x00\\x00\\x7D\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x7C\\x00\\x00\\x00\"\n\"\\xFC\\x00\\x01\\x00\\xF8\\x00\\x02\\x00\\x7D\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x81\\x00\\x00\\x00\\x6A\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x07\\x00\\x00\\x00\\x83\\x00\\x00\\x00\\x82\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x84\\x00\\x00\\x00\\x81\\x00\\x00\\x00\\x83\\x00\\x00\\x00\"\n\"\\x41\\x00\\x05\\x00\\x86\\x00\\x00\\x00\\x87\\x00\\x00\\x00\\x30\\x00\\x00\\x00\\x85\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x88\\x00\\x00\\x00\"\n\"\\x87\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x89\\x00\\x00\\x00\\x84\\x00\\x00\\x00\\x88\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x8A\\x00\\x00\\x00\"\n\"\\x89\\x00\\x00\\x00\\x39\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x8B\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x8A\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x80\\x00\\x00\\x00\"\n\"\\x8B\\x00\\x00\\x00\\xFD\\x00\\x01\\x00\\x38\\x00\\x01\\x00\\x36\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x09\\x00\\x00\\x00\"\n\"\\x37\\x00\\x03\\x00\\x08\\x00\\x00\\x00\\x0A\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x0C\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\\x1C\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\\x28\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\xF7\\x00\\x03\\x00\\x14\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\xFA\\x00\\x04\\x00\\x12\\x00\\x00\\x00\\x13\\x00\\x00\\x00\\x14\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x13\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\"\n\"\\x15\\x00\\x00\\x00\\x0A\\x00\\x00\\x00\\xFE\\x00\\x02\\x00\\x15\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x14\\x00\\x00\\x00\\xF7\\x00\\x03\\x00\\x1A\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\\x18\\x00\\x00\\x00\\x19\\x00\\x00\\x00\\x3E\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x19\\x00\\x00\\x00\\x41\\x00\\x05\\x00\"\n\"\\x21\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x22\\x00\\x00\\x00\"\n\"\\x41\\x00\\x05\\x00\\x21\\x00\\x00\\x00\\x25\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x24\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x26\\x00\\x00\\x00\"\n\"\\x25\\x00\\x00\\x00\\x88\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x27\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x26\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x1C\\x00\\x00\\x00\"\n\"\\x27\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x32\\x00\\x00\\x00\\x33\\x00\\x00\\x00\\x30\\x00\\x00\\x00\\x31\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\"\n\"\\x34\\x00\\x00\\x00\\x33\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x35\\x00\\x00\\x00\\x1C\\x00\\x00\\x00\\x83\\x00\\x05\\x00\\x06\\x00\\x00\\x00\"\n\"\\x36\\x00\\x00\\x00\\x34\\x00\\x00\\x00\\x35\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x32\\x00\\x00\\x00\\x37\\x00\\x00\\x00\\x30\\x00\\x00\\x00\\x31\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x38\\x00\\x00\\x00\\x37\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x32\\x00\\x00\\x00\\x3A\\x00\\x00\\x00\\x30\\x00\\x00\\x00\"\n\"\\x39\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x3B\\x00\\x00\\x00\\x3A\\x00\\x00\\x00\\x83\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x3C\\x00\\x00\\x00\"\n\"\\x38\\x00\\x00\\x00\\x3B\\x00\\x00\\x00\\x88\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x3D\\x00\\x00\\x00\\x36\\x00\\x00\\x00\\x3C\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\"\n\"\\x28\\x00\\x00\\x00\\x3D\\x00\\x00\\x00\\xF9\\x00\\x02\\x00\\x1A\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x3E\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x32\\x00\\x00\\x00\"\n\"\\x3F\\x00\\x00\\x00\\x30\\x00\\x00\\x00\\x31\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x3F\\x00\\x00\\x00\\x41\\x00\\x05\\x00\"\n\"\\x21\\x00\\x00\\x00\\x41\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x42\\x00\\x00\\x00\\x41\\x00\\x00\\x00\"\n\"\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x43\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x42\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x21\\x00\\x00\\x00\\x44\\x00\\x00\\x00\"\n\"\\x1E\\x00\\x00\\x00\\x24\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x45\\x00\\x00\\x00\\x44\\x00\\x00\\x00\\x88\\x00\\x05\\x00\\x06\\x00\\x00\\x00\"\n\"\\x46\\x00\\x00\\x00\\x43\\x00\\x00\\x00\\x45\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x1C\\x00\\x00\\x00\\x46\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\"\n\"\\x48\\x00\\x00\\x00\\x1C\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x32\\x00\\x00\\x00\\x49\\x00\\x00\\x00\\x30\\x00\\x00\\x00\\x39\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x06\\x00\\x00\\x00\\x4A\\x00\\x00\\x00\\x49\\x00\\x00\\x00\\x83\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x4B\\x00\\x00\\x00\\x48\\x00\\x00\\x00\\x4A\\x00\\x00\\x00\"\n\"\\x0C\\x00\\x07\\x00\\x06\\x00\\x00\\x00\\x4C\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x28\\x00\\x00\\x00\\x47\\x00\\x00\\x00\\x4B\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\"\n\"\\x1C\\x00\\x00\\x00\\x4C\\x00\\x00\\x00\\xF7\\x00\\x03\\x00\\x50\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\\x4E\\x00\\x00\\x00\\x4F\\x00\\x00\\x00\"\n\"\\x50\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x4F\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x51\\x00\\x00\\x00\\x1C\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x06\\x00\\x00\\x00\\x52\\x00\\x00\\x00\\x1C\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x53\\x00\\x00\\x00\\x52\\x00\\x00\\x00\\x51\\x00\\x00\\x00\"\n\"\\x3E\\x00\\x03\\x00\\x1C\\x00\\x00\\x00\\x53\\x00\\x00\\x00\\xF9\\x00\\x02\\x00\\x50\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x50\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x06\\x00\\x00\\x00\\x54\\x00\\x00\\x00\\x1C\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x56\\x00\\x00\\x00\\x54\\x00\\x00\\x00\\x55\\x00\\x00\\x00\"\n\"\\x7F\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x57\\x00\\x00\\x00\\x56\\x00\\x00\\x00\\x0C\\x00\\x06\\x00\\x06\\x00\\x00\\x00\\x58\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x1D\\x00\\x00\\x00\\x57\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x28\\x00\\x00\\x00\\x58\\x00\\x00\\x00\\xF9\\x00\\x02\\x00\\x1A\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\"\n\"\\x1A\\x00\\x00\\x00\\x41\\x00\\x06\\x00\\x32\\x00\\x00\\x00\\x5B\\x00\\x00\\x00\\x30\\x00\\x00\\x00\\x5A\\x00\\x00\\x00\\x24\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x06\\x00\\x00\\x00\\x5C\\x00\\x00\\x00\\x5B\\x00\\x00\\x00\\x83\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x5D\\x00\\x00\\x00\\x59\\x00\\x00\\x00\\x5C\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x5E\\x00\\x00\\x00\\x28\\x00\\x00\\x00\\x0C\\x00\\x08\\x00\\x06\\x00\\x00\\x00\\x5F\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x2B\\x00\\x00\\x00\\x5E\\x00\\x00\\x00\\x47\\x00\\x00\\x00\\x59\\x00\\x00\\x00\\x41\\x00\\x06\\x00\\x32\\x00\\x00\\x00\\x60\\x00\\x00\\x00\\x30\\x00\\x00\\x00\"\n\"\\x5A\\x00\\x00\\x00\\x24\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x61\\x00\\x00\\x00\\x60\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\"\n\"\\x62\\x00\\x00\\x00\\x5F\\x00\\x00\\x00\\x61\\x00\\x00\\x00\\x81\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x63\\x00\\x00\\x00\\x5D\\x00\\x00\\x00\\x62\\x00\\x00\\x00\"\n\"\\x3E\\x00\\x03\\x00\\x28\\x00\\x00\\x00\\x63\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x64\\x00\\x00\\x00\\x0A\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x06\\x00\\x00\\x00\\x65\\x00\\x00\\x00\\x28\\x00\\x00\\x00\\x50\\x00\\x07\\x00\\x07\\x00\\x00\\x00\\x66\\x00\\x00\\x00\\x59\\x00\\x00\\x00\\x59\\x00\\x00\\x00\"\n\"\\x59\\x00\\x00\\x00\\x65\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x67\\x00\\x00\\x00\\x64\\x00\\x00\\x00\\x66\\x00\\x00\\x00\\xFE\\x00\\x02\\x00\"\n\"\\x67\\x00\\x00\\x00\\x38\\x00\\x01\\x00\"},\n#endif\n#ifdef D3D11QUAKE\n{QR_DIRECT3D11, 11, \"defaultsprite\",\n\"!!samps 1\\n\"\n\n\"struct a2v\\n\"\n\"{\\n\"\n\"float4 pos: POSITION;\\n\"\n\"float2 tc: TEXCOORD0;\\n\"\n\"float4 vcol: COLOR0;\\n\"\n\"};\\n\"\n\"struct v2f\\n\"\n\"{\\n\"\n\"float4 pos: SV_POSITION;\\n\"\n\"float2 tc: TEXCOORD0;\\n\"\n\"float4 vcol: COLOR0;\\n\"\n\"};\\n\"\n\n\"#include <ftedefs.h>\\n\"\n\n\"#ifdef VERTEX_SHADER\\n\"\n\"v2f main (a2v inp)\\n\"\n\"{\\n\"\n\"v2f outp;\\n\"\n\"outp.pos = mul(m_model, inp.pos);\\n\"\n\"outp.pos = mul(m_view, outp.pos);\\n\"\n\"outp.pos = mul(m_projection, outp.pos);\\n\"\n\"outp.tc = inp.tc.xy;\\n\"\n\"outp.vcol = inp.vcol;\\n\"\n\"return outp;\\n\"\n\"}\\n\"\n\"#endif\\n\"\n\n\"#ifdef FRAGMENT_SHADER\\n\"\n\"Texture2D t_diffuse  : register(t0);\\n\"\n\"SamplerState s_diffuse : register(s0);\\n\"\n\"float4 main (v2f inp) : SV_TARGET\\n\"\n\"{\\n\"\n\"float4 tex = t_diffuse.Sample(s_diffuse, inp.tc);\\n\"\n\"#ifdef MASK\\n\"\n\"if (tex.a < float(MASK))\\n\"\n\"discard;\\n\"\n\"#endif\\n\"\n//FIXME: no fog, no colourmod\n\"return tex * inp.vcol;\\n\"\n\"}\\n\"\n\"#endif\\n\"\n},\n#endif\n#ifdef GLQUAKE\n{QR_OPENGL, 110, \"defaultwall\",\n\"!!ver 100 450\\n\"\n\"!!permu TESS\\n\"\n\"!!permu DELUXE\\n\"\n\"!!permu FULLBRIGHT //lumas rather than no lightmaps\\n\"\n\"!!permu FOG\\n\"\n\"!!permu LIGHTSTYLED\\n\"\n\"!!permu BUMP\\n\"\n\"!!permu SPECULAR\\n\"\n\"!!permu REFLECTCUBEMASK\\n\"\n\"!!permu FAKESHADOWS\\n\"\n\"!!cvardf r_glsl_offsetmapping_scale\\n\"\n\"!!cvardf r_glsl_emissive=1\\n\"\n\"!!cvardf r_glsl_pcf\\n\"\n\"!!cvardf r_tessellation_level=5\\n\"\n\"!!samps diffuse\\n\"\n\"!!samps !EIGHTBIT =FULLBRIGHT fullbright\\n\"\n\"!!samps !EIGHTBIT =BUMP normalmap\\n\"\n\"!!samps !EIGHTBIT =REFLECTCUBEMASK reflectmask reflectcube\\n\"\n//diffuse gives us alpha, and prevents dlight from bugging out when there's no diffuse.\n\"!!samps =EIGHTBIT paletted 1\\n\"\n\"!!samps =SPECULAR specular\\n\"\n\"!!samps !VERTEXLIT lightmap\\n\"\n\"!!samps =LIGHTSTYLED lightmap1 lightmap2 lightmap3\\n\"\n\"!!samps =DELUXE deluxemap\\n\"\n\"!!samps =LIGHTSTYLED =DELUXE deluxemap1 deluxemap2 deluxemap3\\n\"\n\"!!samps =FAKESHADOWS shadowmap\\n\"\n\n\"#if defined(ORM) || defined(SG)\\n\"\n\"#define PBR\\n\"\n\"#endif\\n\"\n\n\"#include \\\"sys/defs.h\\\"\\n\"\n\n//this is what normally draws all of your walls, even with rtlights disabled\n//note that the '286' preset uses drawflat_walls instead.\n\n\"#include \\\"sys/fog.h\\\"\\n\"\n\n\"#if !defined(TESS_CONTROL_SHADER)\\n\"\n\"#if defined(OFFSETMAPPING) || defined(SPECULAR) || defined(REFLECTCUBEMASK) || defined(PBR)\\n\"\n\"varying vec3 eyevector;\\n\"\n\"#endif\\n\"\n\n\"#if defined(REFLECTCUBEMASK) || defined(BUMPMODELSPACE)\\n\"\n\"varying mat3 invsurface;\\n\"\n\"#endif\\n\"\n\n\"varying vec2 tc;\\n\"\n\"#ifdef VERTEXLIT\\n\"\n\"varying vec4 vc;\\n\"\n\"#else\\n\"\n\"#ifdef LIGHTSTYLED\\n\"\n//we could use an offset, but that would still need to be per-surface which would break batches\n//fixme: merge attributes?\n\"varying vec2 lm0, lm1, lm2, lm3;\\n\"\n\"#else\\n\"\n\"varying vec2 lm0;\\n\"\n\"#endif\\n\"\n\"#endif\\n\"\n\n\"#ifdef FAKESHADOWS \\n\"\n\"varying vec4 vtexprojcoord;\\n\"\n\"#endif\\n\"\n\"#endif\\n\"\n\n\"#ifdef VERTEX_SHADER\\n\"\n\"#ifdef TESS\\n\"\n\"varying vec3 vertex, normal;\\n\"\n\"#endif\\n\"\n\"void main ()\\n\"\n\"{\\n\"\n\"#if defined(OFFSETMAPPING) || defined(SPECULAR) || defined(REFLECTCUBEMASK) || defined(PBR)\\n\"\n\"vec3 eyeminusvertex = e_eyepos - v_position.xyz;\\n\"\n\"eyevector.x = dot(eyeminusvertex, v_svector.xyz);\\n\"\n\"eyevector.y = dot(eyeminusvertex, v_tvector.xyz);\\n\"\n\"eyevector.z = dot(eyeminusvertex, v_normal.xyz);\\n\"\n\"#endif\\n\"\n\"#if defined(REFLECTCUBEMASK) || defined(BUMPMODELSPACE)\\n\"\n\"invsurface = mat3(v_svector, v_tvector, v_normal);\\n\"\n\"#endif\\n\"\n\"tc = v_texcoord;\\n\"\n\"#ifdef FLOWV\\n\"\n\"tc.st += e_time * vec2(FLOWV);\\n\"\n\"#endif\\n\"\n\"#ifdef FLOW\\n\"\n\"tc.s += e_time * -0.5;\\n\"\n\"#endif\\n\"\n\"#ifdef VERTEXLIT\\n\"\n\"#ifdef LIGHTSTYLED\\n\"\n//FIXME, only one colour.\n\"vc = v_colour * e_lmscale[0];\\n\"\n\"#else\\n\"\n\"vc = v_colour * e_lmscale;\\n\"\n\"#endif\\n\"\n\"#else\\n\"\n\"lm0 = v_lmcoord;\\n\"\n\"#ifdef LIGHTSTYLED\\n\"\n\"lm1 = v_lmcoord2;\\n\"\n\"lm2 = v_lmcoord3;\\n\"\n\"lm3 = v_lmcoord4;\\n\"\n\"#endif\\n\"\n\"#endif\\n\"\n\n\"#ifdef TESS\\n\"\n\"vertex = v_position;\\n\"\n\"normal = v_normal;\\n\"\n\"#endif\\n\"\n\n\"#ifdef FAKESHADOWS \\n\"\n\"gl_Position = ftetransform();\\n\"\n\"vtexprojcoord = (l_cubematrix*vec4(v_position.xyz, 1.0));\\n\"\n\"#else\\n\"\n\"gl_Position = ftetransform();\\n\"\n\"#endif\\n\"\n\"}\\n\"\n\"#endif\\n\"\n\n\n\"#if defined(TESS_CONTROL_SHADER)\\n\"\n\"layout(vertices = 3) out;\\n\"\n\n\"in vec3 vertex[];\\n\"\n\"out vec3 t_vertex[];\\n\"\n\"in vec3 normal[];\\n\"\n\"out vec3 t_normal[];\\n\"\n\"#if defined(OFFSETMAPPING) || defined(SPECULAR) || defined(REFLECTCUBEMASK) || defined(PBR)\\n\"\n\"in vec3 eyevector[];\\n\"\n\"out vec3 t_eyevector[];\\n\"\n\"#endif\\n\"\n\"#ifdef REFLECTCUBEMASK\\n\"\n\"in mat3 invsurface[];\\n\"\n\"out mat3 t_invsurface[];\\n\"\n\"#endif\\n\"\n\"in vec2 tc[];\\n\"\n\"out vec2 t_tc[];\\n\"\n\"#ifdef VERTEXLIT\\n\"\n\"in vec4 vc[];\\n\"\n\"out vec4 t_vc[];\\n\"\n\"#else\\n\"\n\"in vec2 lm0[];\\n\"\n\"out vec2 t_lm0[];\\n\"\n\"#ifdef LIGHTSTYLED\\n\"\n\"in vec2 lm1[], lm2[], lm3[];\\n\"\n\"out vec2 t_lm1[], t_lm2[], t_lm3[];\\n\"\n\"#endif\\n\"\n\"#endif\\n\"\n\"void main()\\n\"\n\"{\\n\"\n//the control shader needs to pass stuff through\n\"#define id gl_InvocationID\\n\"\n\"t_vertex[id] = vertex[id];\\n\"\n\"t_normal[id] = normal[id];\\n\"\n\"#ifdef REFLECTCUBEMASK\\n\"\n\"t_invsurface[id] = invsurface[id];\\n\"\n\"#endif\\n\"\n\"t_tc[id] = tc[id];\\n\"\n\"#ifdef VERTEXLIT\\n\"\n\"t_vc[id] = vc[id];\\n\"\n\"#else\\n\"\n\"t_lm0[id] = lm0[id];\\n\"\n\"#ifdef LIGHTSTYLED\\n\"\n\"t_lm1[id] = lm1[id];\\n\"\n\"t_lm2[id] = lm2[id];\\n\"\n\"t_lm3[id] = lm3[id];\\n\"\n\"#endif\\n\"\n\"#endif\\n\"\n\n\"#if defined(SPECULAR) || defined(OFFSETMAPPING) || defined(REFLECTCUBEMASK) || defined(PBR)\\n\"\n\"t_eyevector[id] = eyevector[id];\\n\"\n\"#endif\\n\"\n\n\"gl_TessLevelOuter[0] = float(r_tessellation_level);\\n\"\n\"gl_TessLevelOuter[1] = float(r_tessellation_level);\\n\"\n\"gl_TessLevelOuter[2] = float(r_tessellation_level);\\n\"\n\"gl_TessLevelInner[0] = float(r_tessellation_level);\\n\"\n\"}\\n\"\n\"#endif\\n\"\n\n\n\n\n\n\n\n\n\n\"#if defined(TESS_EVALUATION_SHADER)\\n\"\n\"layout(triangles) in;\\n\"\n\n\"in vec3 t_vertex[];\\n\"\n\"in vec3 t_normal[];\\n\"\n\"#if defined(OFFSETMAPPING) || defined(SPECULAR) || defined(REFLECTCUBEMASK) || defined(PBR)\\n\"\n\"in vec3 t_eyevector[];\\n\"\n\"#endif\\n\"\n\"#ifdef REFLECTCUBEMASK\\n\"\n\"in mat3 t_invsurface[];\\n\"\n\"#endif\\n\"\n\"in vec2 t_tc[];\\n\"\n\"#ifdef VERTEXLIT\\n\"\n\"in vec4 t_vc[];\\n\"\n\"#else\\n\"\n\"#ifdef LIGHTSTYLED\\n\"\n//we could use an offset, but that would still need to be per-surface which would break batches\n//fixme: merge attributes?\n\"in vec2 t_lm0[], t_lm1[], t_lm2[], t_lm3[];\\n\"\n\"#else\\n\"\n\"in vec2 t_lm0[];\\n\"\n\"#endif\\n\"\n\"#endif\\n\"\n\n\"#define LERP(a) (gl_TessCoord.x*a[0] + gl_TessCoord.y*a[1] + gl_TessCoord.z*a[2])\\n\"\n\"void main()\\n\"\n\"{\\n\"\n\"#define factor 1.0\\n\"\n\"tc = LERP(t_tc);\\n\"\n\"#ifdef VERTEXLIT\\n\"\n\"vc = LERP(t_vc);\\n\"\n\"#else\\n\"\n\"lm0 = LERP(t_lm0);\\n\"\n\"#ifdef LIGHTSTYLED\\n\"\n\"lm1 = LERP(t_lm1);\\n\"\n\"lm2 = LERP(t_lm2);\\n\"\n\"lm3 = LERP(t_lm3);\\n\"\n\"#endif\\n\"\n\"#endif\\n\"\n\"vec3 w = LERP(t_vertex);\\n\"\n\n\"vec3 t0 = w - dot(w-t_vertex[0],t_normal[0])*t_normal[0];\\n\"\n\"vec3 t1 = w - dot(w-t_vertex[1],t_normal[1])*t_normal[1];\\n\"\n\"vec3 t2 = w - dot(w-t_vertex[2],t_normal[2])*t_normal[2];\\n\"\n\"w = w*(1.0-factor) + factor*(gl_TessCoord.x*t0+gl_TessCoord.y*t1+gl_TessCoord.z*t2);\\n\"\n\n\"#if defined(PCF) || defined(SPOT) || defined(CUBE)\\n\"\n//for texture projections/shadowmapping on dlights\n\"vtexprojcoord = (l_cubematrix*vec4(w.xyz, 1.0));\\n\"\n\"#endif\\n\"\n\n//FIXME: we should be recalcing these here, instead of just lerping them\n\"#ifdef REFLECTCUBEMASK\\n\"\n\"invsurface = LERP(t_invsurface);\\n\"\n\"#endif\\n\"\n\"#if defined(SPECULAR) || defined(OFFSETMAPPING) || defined(REFLECTCUBEMASK) || defined(PBR)\\n\"\n\"eyevector = LERP(t_eyevector);\\n\"\n\"#endif\\n\"\n\n\"gl_Position = m_modelviewprojection * vec4(w,1.0);\\n\"\n\"}\\n\"\n\"#endif\\n\"\n\n\n\n\n\n\n\"#ifdef FRAGMENT_SHADER\\n\"\n\"#define s_colourmap s_t0\\n\"\n\n\"#include \\\"sys/pbr.h\\\"\\n\"\n\"#include \\\"sys/pcf.h\\\"\\n\"\n\n\"#ifdef OFFSETMAPPING\\n\"\n\"#include \\\"sys/offsetmapping.h\\\"\\n\"\n\"#endif\\n\"\n\"void main ()\\n\"\n\"{\\n\"\n//adjust texture coords for offsetmapping\n\"#ifdef OFFSETMAPPING\\n\"\n\"vec2 tcoffsetmap = offsetmap(s_normalmap, tc, eyevector);\\n\"\n\"#define tc tcoffsetmap\\n\"\n\"#endif\\n\"\n\n\"#if defined(EIGHTBIT) && !defined(LIGHTSTYLED)\\n\"\n//optional: round the lightmap coords to ensure all pixels within a texel have different lighting values either. it just looks wrong otherwise.\n//don't bother if its lightstyled, such cases will have unpredictable correlations anyway.\n//FIXME: this rounding is likely not correct with respect to software rendering. oh well.\n\"#if __VERSION__ >= 130 && !defined(VERTEXLIT)\\n\"\n\"vec2 lmsize = vec2(textureSize(s_lightmap0, 0));\\n\"\n\"#else\\n\"\n\"#define lmsize vec2(128.0,2048.0)\\n\"\n\"#endif\\n\"\n\"#define texelstolightmap (16.0)\\n\"\n\"vec2 lmcoord0 = floor(lm0 * lmsize*texelstolightmap)/(lmsize*texelstolightmap);\\n\"\n\"#define lm0 lmcoord0\\n\"\n\"#endif\\n\"\n\n\n//Read the base texture (with EIGHTBIT only alpha is needed)\n\"vec4 col = texture2D(s_diffuse, tc);\\n\"\n\n\"#if defined(BUMP) && (defined(DELUXE) || defined(SPECULAR) || defined(REFLECTCUBEMASK))\\n\"\n\"vec3 norm = normalize(texture2D(s_normalmap, tc).rgb - 0.5);\\n\"\n\"#elif defined(PBR) || defined(SPECULAR) || defined(DELUXE) || defined(REFLECTCUBEMASK)\\n\"\n\"vec3 norm = vec3(0, 0, 1); //specular lighting expects this to exist.\\n\"\n\"#endif\\n\"\n\n//modulate that by the lightmap(s) including deluxemap(s)\n\"#ifdef VERTEXLIT\\n\"\n\"#ifdef LIGHTSTYLED\\n\"\n\"vec3 lightmaps = vc.rgb;\\n\"\n\"#else\\n\"\n\"vec3 lightmaps = vc.rgb;\\n\"\n\"#endif\\n\"\n\"#define deluxe vec3(0.0,0.0,1.0)\\n\"\n\"#else\\n\"\n\"#ifdef LIGHTSTYLED\\n\"\n\"#define deluxe vec3(0.0,0.0,1.0)\\n\"\n\"vec3 lightmaps;\\n\"\n\"#ifdef DELUXE\\n\"\n\"lightmaps  = texture2D(s_lightmap0, lm0).rgb * e_lmscale[0].rgb * dot(norm, 2.0*texture2D(s_deluxemap0, lm0).rgb-0.5);\\n\"\n\"lightmaps += texture2D(s_lightmap1, lm1).rgb * e_lmscale[1].rgb * dot(norm, 2.0*texture2D(s_deluxemap1, lm1).rgb-0.5);\\n\"\n\"lightmaps += texture2D(s_lightmap2, lm2).rgb * e_lmscale[2].rgb * dot(norm, 2.0*texture2D(s_deluxemap2, lm2).rgb-0.5);\\n\"\n\"lightmaps += texture2D(s_lightmap3, lm3).rgb * e_lmscale[3].rgb * dot(norm, 2.0*texture2D(s_deluxemap3, lm3).rgb-0.5);\\n\"\n\"#else\\n\"\n\"lightmaps  = texture2D(s_lightmap0, lm0).rgb * e_lmscale[0].rgb;\\n\"\n\"lightmaps += texture2D(s_lightmap1, lm1).rgb * e_lmscale[1].rgb;\\n\"\n\"lightmaps += texture2D(s_lightmap2, lm2).rgb * e_lmscale[2].rgb;\\n\"\n\"lightmaps += texture2D(s_lightmap3, lm3).rgb * e_lmscale[3].rgb;\\n\"\n\"#endif\\n\"\n\"#else\\n\"\n\"vec3 lightmaps = (texture2D(s_lightmap, lm0) * e_lmscale).rgb;\\n\"\n//modulate by the  bumpmap dot light\n\"#ifdef DELUXE\\n\"\n\"vec3 deluxe = (texture2D(s_deluxemap, lm0).rgb-0.5);\\n\"\n\"#ifdef BUMPMODELSPACE\\n\"\n\"deluxe = normalize(deluxe*invsurface);\\n\"\n\"#else\\n\"\n\"deluxe = normalize(deluxe);\\n\"\n\"lightmaps *= 2.0 / max(0.25, deluxe.z); //counter the darkening from deluxemaps\\n\"\n\"#endif\\n\"\n\"lightmaps *= dot(norm, deluxe);\\n\"\n\"#else\\n\"\n\"#define deluxe vec3(0.0,0.0,1.0)\\n\"\n\"#endif\\n\"\n\"#endif\\n\"\n\"#endif\\n\"\n\n//\tcol *= factor_base;\n\"#ifndef IOR\\n\"\n\"#define IOR 1.5 //Index Of Reflection.\\n\"\n\"#endif\\n\"\n\"#define dielectricSpecular pow(((IOR - 1.0)/(IOR + 1.0)),2.0)\\n\"\n\"#ifdef SPECULAR\\n\"\n\"vec4 specs = texture2D(s_specular, tc);//*factor_spec;\\n\"\n\"#ifdef ORM\\n\"\n\"#define occlusion specs.r\\n\"\n\"#define roughness specs.g\\n\"\n\"#define metalness specs.b\\n\"\n\"#define gloss (1.0-roughness)\\n\"\n\"#define ambientrgb (specrgb+col.rgb)\\n\"\n\"vec3 specrgb = mix(vec3(dielectricSpecular), col.rgb, metalness);\\n\"\n\"vec3 albedorgb = col.rgb * (1.0 - dielectricSpecular) * (1.0-metalness);\\n\"\n\"#elif defined(SG) //pbr-style specular+glossiness\\n\"\n//occlusion needs to be baked in. :(\n\"#define roughness (1.0-specs.a)\\n\"\n\"#define gloss specs.a\\n\"\n\"#define specrgb specs.rgb\\n\"\n\"#define ambientrgb (specs.rgb+col.rgb)\\n\"\n\"#define albedorgb col.rgb\\n\"\n\"#elif defined(PBR) //PBR using legacy texturemaps\\n\"\n\"#define gloss specs.a\\n\"\n\"#define roughness (1.0-gloss)\\n\"\n//metalness not relevant\n\n//our pbr stuff doesn't much like our inputs.\n\"vec3 specrgb, albedorgb;\\n\"\n//if (1==0)\n//{\t//metal\n//\tspecrgb = col.rgb;//+specs.rgb;\n//\talbedorgb = vec3(0.0);\n//}\n//else\n//{\t//non-metal\n\"specrgb = vec3(dielectricSpecular);\\n\"\n\"albedorgb = col.rgb;//+specs.rgb;\\n\"\n//}\n\"#define ambientrgb col.rgb\\n\"\n\"#else   //blinn-phong\\n\"\n\"#define gloss specs.a\\n\"\n//occlusion not defined\n\"#define specrgb specs.rgb\\n\"\n\"#endif\\n\"\n\"#else\\n\"\n//no specular map specified. doesn't mean we shouldn't have any though, at least with pbr enabled.\n\"#define roughness 0.3\\n\"\n\"#define specrgb 1.0 //vec3(dielectricSpecular)\\n\"\n\"#define albedorgb col.rgb\\n\"\n\"#endif\\n\"\n\n//add in specular, if applicable.\n\"#ifdef PBR\\n\"\n\"col.rgb = DoPBR(norm, normalize(eyevector), deluxe, roughness, albedorgb, specrgb, vec3(0.0,1.0,1.0));//*e_light_mul + e_light_ambient*.25*ambientrgb;\\n\"\n\"#elif defined(gloss)\\n\"\n\"vec3 halfdir = normalize(normalize(eyevector) + deluxe); //this norm should be the deluxemap info instead\\n\"\n\"float spec = pow(max(dot(halfdir, norm), 0.0), FTE_SPECULAR_EXPONENT * gloss);\\n\"\n\"spec *= FTE_SPECULAR_MULTIPLIER;\\n\"\n//NOTE: rtlights tend to have a *4 scaler here to over-emphasise the effect because it looks cool.\n//As not all maps will have deluxemapping, and the double-cos from the light util makes everything far too dark anyway,\n//we default to something that is not garish when the light value is directly infront of every single pixel.\n//we can justify this difference due to the rtlight editor etc showing the *4.\n\"col.rgb += spec * specrgb;\\n\"\n\"#endif\\n\"\n\n\"#ifdef REFLECTCUBEMASK\\n\"\n\"vec3 rtc = reflect(normalize(-eyevector), norm);\\n\"\n//todo: parallax correction: https://seblagarde.wordpress.com/2012/09/29/image-based-lighting-approaches-and-parallax-corrected-cubemap/\n//norm (and also eyevector) are in tangentspace but our cubemap wants worldspace, so convert.\n\"rtc = rtc.x*invsurface[0] + rtc.y*invsurface[1] + rtc.z*invsurface[2];\\n\"\n\"rtc = (m_model * vec4(rtc.xyz,0.0)).xyz;\\n\"\n\"col.rgb += texture2D(s_reflectmask, tc).rgb * textureCube(s_reflectcube, rtc).rgb;\\n\"\n\"#endif\\n\"\n\n\"#ifdef EIGHTBIT //FIXME: with this extra flag, half the permutations are redundant.\\n\"\n\"lightmaps *= 0.5; //counter the fact that the colourmap contains overbright values and logically ranges from 0 to 2 intead of to 1.\\n\"\n\"float pal = texture2D(s_paletted, tc).r; //the palette index. hopefully not interpolated.\\n\"\n\"lightmaps -= 1.0 / 128.0; //software rendering appears to round down, so make sure we favour the lower values instead of rounding to the nearest\\n\"\n\"col.r = texture2D(s_colourmap, vec2(pal, 1.0-lightmaps.r)).r; //do 3 lookups. this is to cope with lit files, would be a waste to not support those.\\n\"\n\"col.g = texture2D(s_colourmap, vec2(pal, 1.0-lightmaps.g)).g; //its not very softwarey, but re-palettizing is ugly.\\n\"\n\"col.b = texture2D(s_colourmap, vec2(pal, 1.0-lightmaps.b)).b; //without lits, it should be identical.\\n\"\n\"#else\\n\"\n//now we have our diffuse+specular terms, modulate by lightmap values.\n\"#if defined(FULLBRIGHT)\\n\"\n\"vec4 fb = texture2D(s_fullbright, tc);\\n\"\n\"#if r_glsl_emissive==0 //q2e-like mask that gets darker when lights get overbright.\\n\"\n\"col.rgb *= mix(lightmaps.rgb, vec3(1.0), fb.rgb*fb.a);\\n\"\n\"#else //actually emissive layer\\n\"\n\"col.rgb = col.rgb * lightmaps.rgb + fb.rgb*fb.a;\\n\"\n\"#endif\\n\"\n\"#else\\n\"\n\"col.rgb *= lightmaps.rgb;\\n\"\n\"#endif\\n\"\n\"#endif\\n\"\n\n//entity modifiers\n\"col *= e_colourident;\\n\"\n\n\"#ifdef FAKESHADOWS\\n\"\n/*filter the light by the shadowmap. logically a boolean, but we allow fractions for softer shadows*/\n\"col.rgb *= ShadowmapFilter(s_shadowmap, vtexprojcoord);\\n\"\n//col.g = ShadowmapFilter(s_shadowmap, vtexprojcoord);\n\"#endif\\n\"\n\n\"#if defined(MASK)\\n\"\n\"#if defined(MASKLT)\\n\"\n\"if (col.a < MASK)\\n\"\n\"discard;\\n\"\n\"#else\\n\"\n\"if (col.a >= MASK)\\n\"\n\"discard;\\n\"\n\"#endif\\n\"\n\"col.a = 1.0; //alpha blending AND alpha testing usually looks stupid, plus it screws up our fog.\\n\"\n\"#endif\\n\"\n\n//and finally hide it all if we're fogged.\n\"gl_FragColor = fog4(col);\\n\"\n\"}\\n\"\n\"#endif\\n\"\n\n},\n#endif\n#ifdef VKQUAKE\n{QR_VULKAN, -1, \"defaultwall\",\n\"\\xFF\\x53\\x50\\x56\\x01\\x00\\x00\\x00\\x9C\\x86\\x01\\x00\\x00\\x00\\x00\\x00\\x2B\\x00\\x00\\x00\\x2C\\x00\\x00\\x00\\xAA\\x00\\x00\\x00\\xD8\\x00\\x00\\x00\"\n\"\\x3C\\x10\\x00\\x00\\x14\\x11\\x00\\x00\\x28\\x31\\x00\\x00\\x01\\x00\\x66\\x31\\x72\\x5F\\x67\\x6C\\x73\\x6C\\x5F\\x6F\\x66\\x66\\x73\\x65\\x74\\x6D\\x61\\x70\"\n\"\\x70\\x69\\x6E\\x67\\x00\\x00\\x00\\x00\\x00\\x01\\x01\\x66\\x31\\x72\\x5F\\x67\\x6C\\x73\\x6C\\x5F\\x6F\\x66\\x66\\x73\\x65\\x74\\x6D\\x61\\x70\\x70\\x69\\x6E\"\n\"\\x67\\x5F\\x73\\x63\\x61\\x6C\\x65\\x00\\x3D\\x23\\xD7\\x0A\\x01\\x02\\x66\\x31\\x67\\x6C\\x5F\\x73\\x70\\x65\\x63\\x75\\x6C\\x61\\x72\\x00\\x3E\\x99\\x99\\x9A\"\n\"\\x01\\x03\\x62\\x31\\x72\\x5F\\x66\\x6F\\x67\\x5F\\x65\\x78\\x70\\x32\\x00\\x00\\x00\\x00\\x00\\x01\\x04\\x62\\x31\\x72\\x5F\\x66\\x6F\\x67\\x5F\\x6C\\x69\\x6E\"\n\"\\x65\\x61\\x72\\x00\\x00\\x00\\x00\\x00\\x01\\x05\\x42\\x31\\x76\\x65\\x72\\x74\\x65\\x78\\x6C\\x69\\x74\\x00\\x00\\x00\\x00\\x00\\x01\\x06\\x46\\x31\\x6D\\x61\"\n\"\\x73\\x6B\\x00\\x3F\\x80\\x00\\x00\\x01\\x07\\x42\\x31\\x6D\\x61\\x73\\x6B\\x6C\\x74\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x03\\x02\\x23\\x07\\x00\\x00\\x01\\x00\"\n\"\\x0A\\x00\\x08\\x00\\x8F\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x11\\x00\\x02\\x00\\x01\\x00\\x00\\x00\\x0B\\x00\\x06\\x00\\x01\\x00\\x00\\x00\\x47\\x4C\\x53\\x4C\"\n\"\\x2E\\x73\\x74\\x64\\x2E\\x34\\x35\\x30\\x00\\x00\\x00\\x00\\x0E\\x00\\x03\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x0F\\x00\\x12\\x00\\x00\\x00\\x00\\x00\"\n\"\\x04\\x00\\x00\\x00\\x6D\\x61\\x69\\x6E\\x00\\x00\\x00\\x00\\x1C\\x00\\x00\\x00\\x4D\\x00\\x00\\x00\\x4F\\x00\\x00\\x00\\x56\\x00\\x00\\x00\\x5B\\x00\\x00\\x00\"\n\"\\x64\\x00\\x00\\x00\\x6E\\x00\\x00\\x00\\x70\\x00\\x00\\x00\\x72\\x00\\x00\\x00\\x73\\x00\\x00\\x00\\x7A\\x00\\x00\\x00\\x7C\\x00\\x00\\x00\\x80\\x00\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x11\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x13\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x05\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x13\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\"\n\"\\x48\\x00\\x04\\x00\\x13\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x80\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\"\n\"\\x03\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xC0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xCC\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xD0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\xDC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xE0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x13\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xEC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\xF0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x0A\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xFC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\"\n\"\\x0B\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x01\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x4C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x50\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x5C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x13\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x60\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x11\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x6C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x70\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\"\n\"\\x13\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x14\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x90\\x01\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x94\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x16\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x98\\x01\\x00\\x00\\x47\\x00\\x03\\x00\\x13\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x15\\x00\\x00\\x00\\x22\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x15\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x1C\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x38\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\x3C\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x02\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\x3F\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x13\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x4D\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x4F\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x56\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x5B\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x64\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\"\n\"\\x04\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x6E\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x70\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x72\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x73\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\"\n\"\\x03\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x75\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x05\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\x7A\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\"\n\"\\x03\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x7C\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x7E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x0B\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x03\\x00\\x7E\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x83\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x01\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\x84\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x03\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\x85\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x04\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\x86\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x06\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\x87\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x07\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\x88\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x89\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x11\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x8A\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x8C\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x8C\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x8C\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x8C\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x8C\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x4C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x8C\\x00\\x00\\x00\\x03\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x50\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x8C\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x5C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x8C\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x60\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x8C\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x6C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x8C\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x70\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x8C\\x00\\x00\\x00\"\n\"\\x08\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x8C\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x88\\x00\\x00\\x00\"\n\"\\x47\\x00\\x03\\x00\\x8C\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x8E\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\"\n\"\\x8E\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x13\\x00\\x02\\x00\\x02\\x00\\x00\\x00\\x21\\x00\\x03\\x00\\x03\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x16\\x00\\x03\\x00\\x06\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x17\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x21\\x00\\x03\\x00\"\n\"\\x08\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x18\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x17\\x00\\x04\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x15\\x00\\x04\\x00\\x0F\\x00\\x00\\x00\"\n\"\\x20\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x0F\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x1C\\x00\\x04\\x00\\x11\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x17\\x00\\x04\\x00\\x12\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x1E\\x00\\x19\\x00\\x13\\x00\\x00\\x00\"\n\"\\x0D\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x11\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x20\\x00\\x04\\x00\"\n\"\\x14\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x13\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x14\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x15\\x00\\x04\\x00\"\n\"\\x16\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x17\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x20\\x00\\x04\\x00\"\n\"\\x18\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\"\n\"\\x1B\\x00\\x00\\x00\\x1C\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x00\\x00\\x80\\x3F\\x2B\\x00\\x04\\x00\"\n\"\\x06\\x00\\x00\\x00\\x24\\x00\\x00\\x00\\x00\\x00\\x80\\xBF\\x2B\\x00\\x04\\x00\\x0F\\x00\\x00\\x00\\x25\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x20\\x00\\x04\\x00\"\n\"\\x26\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x0F\\x00\\x00\\x00\\x2B\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\"\n\"\\x0F\\x00\\x00\\x00\\x2E\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x32\\x00\\x00\\x00\\x00\\x00\\x00\\x40\\x32\\x00\\x04\\x00\"\n\"\\x06\\x00\\x00\\x00\\x38\\x00\\x00\\x00\\x00\\x00\\x80\\x43\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x39\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x14\\x00\\x02\\x00\"\n\"\\x3A\\x00\\x00\\x00\\x32\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x3C\\x00\\x00\\x00\\x00\\x00\\x81\\x43\\x32\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x3F\\x00\\x00\\x00\"\n\"\\x13\\x00\\x00\\x00\\x34\\x00\\x06\\x00\\x3A\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\xAB\\x00\\x00\\x00\\x3F\\x00\\x00\\x00\\x17\\x00\\x00\\x00\\x20\\x00\\x04\\x00\"\n\"\\x44\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x46\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x20\\x00\\x04\\x00\"\n\"\\x47\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x4C\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\"\n\"\\x4C\\x00\\x00\\x00\\x4D\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\\x4F\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\"\n\"\\x0F\\x00\\x00\\x00\\x52\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x53\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\"\n\"\\x1B\\x00\\x00\\x00\\x56\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\\x5B\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x34\\x00\\x06\\x00\"\n\"\\x3A\\x00\\x00\\x00\\x5F\\x00\\x00\\x00\\xAB\\x00\\x00\\x00\\x3F\\x00\\x00\\x00\\x17\\x00\\x00\\x00\\x18\\x00\\x04\\x00\\x62\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\"\n\"\\x03\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x63\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x62\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x63\\x00\\x00\\x00\\x64\\x00\\x00\\x00\"\n\"\\x03\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x67\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x6A\\x00\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x6D\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x6D\\x00\\x00\\x00\\x6E\\x00\\x00\\x00\"\n\"\\x03\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x6F\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x6F\\x00\\x00\\x00\\x70\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x6D\\x00\\x00\\x00\\x72\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x6F\\x00\\x00\\x00\\x73\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x32\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x75\\x00\\x00\\x00\\x05\\x01\\x00\\x00\\x34\\x00\\x06\\x00\\x3A\\x00\\x00\\x00\\x76\\x00\\x00\\x00\"\n\"\\xAB\\x00\\x00\\x00\\x75\\x00\\x00\\x00\\x17\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x79\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\"\n\"\\x79\\x00\\x00\\x00\\x7A\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x7B\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\"\n\"\\x7B\\x00\\x00\\x00\\x7C\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x1E\\x00\\x03\\x00\\x7E\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x7F\\x00\\x00\\x00\"\n\"\\x03\\x00\\x00\\x00\\x7E\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x7F\\x00\\x00\\x00\\x80\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x32\\x00\\x04\\x00\\x06\\x00\\x00\\x00\"\n\"\\x83\\x00\\x00\\x00\\x00\\x80\\x80\\x43\\x32\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x84\\x00\\x00\\x00\\x03\\x01\\x00\\x00\\x32\\x00\\x04\\x00\\x16\\x00\\x00\\x00\"\n\"\\x85\\x00\\x00\\x00\\x04\\x01\\x00\\x00\\x32\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x86\\x00\\x00\\x00\\x00\\x00\\x83\\x43\\x32\\x00\\x04\\x00\\x16\\x00\\x00\\x00\"\n\"\\x87\\x00\\x00\\x00\\x07\\x01\\x00\\x00\\x32\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x88\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x32\\x00\\x04\\x00\\x16\\x00\\x00\\x00\"\n\"\\x89\\x00\\x00\\x00\\x11\\x00\\x00\\x00\\x32\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x8A\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x2A\\x00\\x03\\x00\\x3A\\x00\\x00\\x00\"\n\"\\x8B\\x00\\x00\\x00\\x1E\\x00\\x0C\\x00\\x8C\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x8D\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x8C\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x8D\\x00\\x00\\x00\\x8E\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x36\\x00\\x05\\x00\\x02\\x00\\x00\\x00\\x04\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x05\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x44\\x00\\x00\\x00\\x45\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\xBA\\x00\\x05\\x00\\x3A\\x00\\x00\\x00\\x3B\\x00\\x00\\x00\\x38\\x00\\x00\\x00\\x39\\x00\\x00\\x00\\xBA\\x00\\x05\\x00\\x3A\\x00\\x00\\x00\\x3D\\x00\\x00\\x00\"\n\"\\x3C\\x00\\x00\\x00\\x39\\x00\\x00\\x00\\xA6\\x00\\x05\\x00\\x3A\\x00\\x00\\x00\\x3E\\x00\\x00\\x00\\x3B\\x00\\x00\\x00\\x3D\\x00\\x00\\x00\\xA6\\x00\\x05\\x00\"\n\"\\x3A\\x00\\x00\\x00\\x41\\x00\\x00\\x00\\x3E\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\xF7\\x00\\x03\\x00\\x43\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\"\n\"\\x41\\x00\\x00\\x00\\x42\\x00\\x00\\x00\\x43\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x42\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x47\\x00\\x00\\x00\\x48\\x00\\x00\\x00\"\n\"\\x15\\x00\\x00\\x00\\x46\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0E\\x00\\x00\\x00\\x49\\x00\\x00\\x00\\x48\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0E\\x00\\x00\\x00\"\n\"\\x4A\\x00\\x00\\x00\\x1C\\x00\\x00\\x00\\x83\\x00\\x05\\x00\\x0E\\x00\\x00\\x00\\x4B\\x00\\x00\\x00\\x49\\x00\\x00\\x00\\x4A\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\"\n\"\\x45\\x00\\x00\\x00\\x4B\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0E\\x00\\x00\\x00\\x4E\\x00\\x00\\x00\\x45\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0E\\x00\\x00\\x00\"\n\"\\x50\\x00\\x00\\x00\\x4F\\x00\\x00\\x00\\x94\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x51\\x00\\x00\\x00\\x4E\\x00\\x00\\x00\\x50\\x00\\x00\\x00\\x41\\x00\\x05\\x00\"\n\"\\x53\\x00\\x00\\x00\\x54\\x00\\x00\\x00\\x4D\\x00\\x00\\x00\\x52\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x54\\x00\\x00\\x00\\x51\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x0E\\x00\\x00\\x00\\x55\\x00\\x00\\x00\\x45\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0E\\x00\\x00\\x00\\x57\\x00\\x00\\x00\\x56\\x00\\x00\\x00\\x94\\x00\\x05\\x00\"\n\"\\x06\\x00\\x00\\x00\\x58\\x00\\x00\\x00\\x55\\x00\\x00\\x00\\x57\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x53\\x00\\x00\\x00\\x59\\x00\\x00\\x00\\x4D\\x00\\x00\\x00\"\n\"\\x25\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x59\\x00\\x00\\x00\\x58\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0E\\x00\\x00\\x00\\x5A\\x00\\x00\\x00\\x45\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x0E\\x00\\x00\\x00\\x5C\\x00\\x00\\x00\\x5B\\x00\\x00\\x00\\x94\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x5D\\x00\\x00\\x00\\x5A\\x00\\x00\\x00\"\n\"\\x5C\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x53\\x00\\x00\\x00\\x5E\\x00\\x00\\x00\\x4D\\x00\\x00\\x00\\x2B\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x5E\\x00\\x00\\x00\"\n\"\\x5D\\x00\\x00\\x00\\xF9\\x00\\x02\\x00\\x43\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x43\\x00\\x00\\x00\\xF7\\x00\\x03\\x00\\x61\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\xFA\\x00\\x04\\x00\\x5F\\x00\\x00\\x00\\x60\\x00\\x00\\x00\\x61\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x60\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0E\\x00\\x00\\x00\"\n\"\\x65\\x00\\x00\\x00\\x4F\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x4C\\x00\\x00\\x00\\x66\\x00\\x00\\x00\\x64\\x00\\x00\\x00\\x17\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\"\n\"\\x66\\x00\\x00\\x00\\x65\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0E\\x00\\x00\\x00\\x68\\x00\\x00\\x00\\x56\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x4C\\x00\\x00\\x00\"\n\"\\x69\\x00\\x00\\x00\\x64\\x00\\x00\\x00\\x67\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x69\\x00\\x00\\x00\\x68\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0E\\x00\\x00\\x00\"\n\"\\x6B\\x00\\x00\\x00\\x5B\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x4C\\x00\\x00\\x00\\x6C\\x00\\x00\\x00\\x64\\x00\\x00\\x00\\x6A\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\"\n\"\\x6C\\x00\\x00\\x00\\x6B\\x00\\x00\\x00\\xF9\\x00\\x02\\x00\\x61\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x61\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x12\\x00\\x00\\x00\"\n\"\\x71\\x00\\x00\\x00\\x70\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x6E\\x00\\x00\\x00\\x71\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x12\\x00\\x00\\x00\\x74\\x00\\x00\\x00\"\n\"\\x73\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x72\\x00\\x00\\x00\\x74\\x00\\x00\\x00\\xF7\\x00\\x03\\x00\\x78\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\"\n\"\\x76\\x00\\x00\\x00\\x77\\x00\\x00\\x00\\x78\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x77\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x7D\\x00\\x00\\x00\"\n\"\\x7C\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x7A\\x00\\x00\\x00\\x7D\\x00\\x00\\x00\\xF9\\x00\\x02\\x00\\x78\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x78\\x00\\x00\\x00\"\n\"\\x39\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x81\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x79\\x00\\x00\\x00\\x82\\x00\\x00\\x00\\x80\\x00\\x00\\x00\"\n\"\\x17\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x82\\x00\\x00\\x00\\x81\\x00\\x00\\x00\\xFD\\x00\\x01\\x00\\x38\\x00\\x01\\x00\\x36\\x00\\x05\\x00\\x07\\x00\\x00\\x00\"\n\"\\x09\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x0A\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x18\\x00\\x00\\x00\\x19\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x17\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\"\n\"\\x1A\\x00\\x00\\x00\\x19\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0E\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x1C\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\"\n\"\\x1F\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x50\\x00\\x07\\x00\\x07\\x00\\x00\\x00\\x22\\x00\\x00\\x00\"\n\"\\x1F\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x91\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x1A\\x00\\x00\\x00\"\n\"\\x22\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x0C\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x26\\x00\\x00\\x00\\x27\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\"\n\"\\x25\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x28\\x00\\x00\\x00\\x27\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x29\\x00\\x00\\x00\"\n\"\\x28\\x00\\x00\\x00\\x24\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x26\\x00\\x00\\x00\\x2A\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x25\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\"\n\"\\x2A\\x00\\x00\\x00\\x29\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x26\\x00\\x00\\x00\\x2C\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x2B\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x06\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\\x2C\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x26\\x00\\x00\\x00\\x2F\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x2E\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x30\\x00\\x00\\x00\\x2F\\x00\\x00\\x00\\x81\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x31\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\"\n\"\\x30\\x00\\x00\\x00\\x88\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x33\\x00\\x00\\x00\\x31\\x00\\x00\\x00\\x32\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x26\\x00\\x00\\x00\"\n\"\\x34\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x2B\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x34\\x00\\x00\\x00\\x33\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\"\n\"\\x35\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\xFE\\x00\\x02\\x00\\x35\\x00\\x00\\x00\\x38\\x00\\x01\\x00\\x03\\x02\\x23\\x07\\x00\\x00\\x01\\x00\\x0A\\x00\\x08\\x00\"\n\"\\x19\\x02\\x00\\x00\\x00\\x00\\x00\\x00\\x11\\x00\\x02\\x00\\x01\\x00\\x00\\x00\\x0B\\x00\\x06\\x00\\x01\\x00\\x00\\x00\\x47\\x4C\\x53\\x4C\\x2E\\x73\\x74\\x64\"\n\"\\x2E\\x34\\x35\\x30\\x00\\x00\\x00\\x00\\x0E\\x00\\x03\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x0F\\x00\\x0C\\x00\\x04\\x00\\x00\\x00\\x04\\x00\\x00\\x00\"\n\"\\x6D\\x61\\x69\\x6E\\x00\\x00\\x00\\x00\\x2F\\x00\\x00\\x00\\x17\\x01\\x00\\x00\\x1E\\x01\\x00\\x00\\x25\\x01\\x00\\x00\\x4C\\x01\\x00\\x00\\x57\\x01\\x00\\x00\"\n\"\\xB3\\x01\\x00\\x00\\x10\\x00\\x03\\x00\\x04\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x1F\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x15\\x00\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x28\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x04\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\x2F\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x3C\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x3D\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x05\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x3D\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x3D\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x3D\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x3D\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x3D\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\"\n\"\\x48\\x00\\x04\\x00\\x3D\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x3D\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x80\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x3D\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x3D\\x00\\x00\\x00\"\n\"\\x03\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xC0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x3D\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xCC\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x3D\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xD0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x3D\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\xDC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x3D\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xE0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x3D\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xEC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x3D\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\xF0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x3D\\x00\\x00\\x00\\x0A\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xFC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x3D\\x00\\x00\\x00\"\n\"\\x0B\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x3D\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x01\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x3D\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x4C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x3D\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x50\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x3D\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x5C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x3D\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x60\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x3D\\x00\\x00\\x00\\x11\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x6C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x3D\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x70\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x3D\\x00\\x00\\x00\"\n\"\\x13\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x3D\\x00\\x00\\x00\\x14\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x90\\x01\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x3D\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x94\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x3D\\x00\\x00\\x00\\x16\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x98\\x01\\x00\\x00\\x47\\x00\\x03\\x00\\x3D\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x3F\\x00\\x00\\x00\\x22\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x3F\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x5C\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x03\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\x97\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x01\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\xE5\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x00\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\x17\\x01\\x00\\x00\\x1E\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x1C\\x01\\x00\\x00\\x22\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x1C\\x01\\x00\\x00\\x21\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x1E\\x01\\x00\\x00\\x1E\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x25\\x01\\x00\\x00\\x1E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x26\\x01\\x00\\x00\\x22\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x26\\x01\\x00\\x00\\x21\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x2A\\x01\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x10\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x2E\\x01\\x00\\x00\\x01\\x00\\x00\\x00\\x02\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\x31\\x01\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x13\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x47\\x01\\x00\\x00\\x01\\x00\\x00\\x00\\x05\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\x4C\\x01\\x00\\x00\\x1E\\x00\\x00\\x00\"\n\"\\x03\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x55\\x01\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x55\\x01\\x00\\x00\\x21\\x00\\x00\\x00\"\n\"\\x08\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x57\\x01\\x00\\x00\\x1E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x62\\x01\\x00\\x00\\x22\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x62\\x01\\x00\\x00\\x21\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x7A\\x01\\x00\\x00\\x22\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x7A\\x01\\x00\\x00\\x21\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\xB3\\x01\\x00\\x00\\x1E\\x00\\x00\\x00\"\n\"\\x04\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\xD0\\x01\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\xD0\\x01\\x00\\x00\\x21\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\xD8\\x01\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\xD8\\x01\\x00\\x00\\x21\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\xE9\\x01\\x00\\x00\\x01\\x00\\x00\\x00\\x11\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\xED\\x01\\x00\\x00\\x22\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\xED\\x01\\x00\\x00\\x21\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\xFC\\x01\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x06\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\x00\\x02\\x00\\x00\\x01\\x00\\x00\\x00\\x07\\x01\\x00\\x00\\x48\\x00\\x04\\x00\\x16\\x02\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x16\\x02\\x00\\x00\\x00\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x16\\x02\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x16\\x02\\x00\\x00\\x01\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x16\\x02\\x00\\x00\\x02\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x4C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x16\\x02\\x00\\x00\\x03\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x50\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x16\\x02\\x00\\x00\\x04\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x5C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x16\\x02\\x00\\x00\\x05\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x60\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x16\\x02\\x00\\x00\\x06\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x6C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x16\\x02\\x00\\x00\\x07\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x70\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x16\\x02\\x00\\x00\"\n\"\\x08\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x16\\x02\\x00\\x00\\x09\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x88\\x00\\x00\\x00\"\n\"\\x47\\x00\\x03\\x00\\x16\\x02\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x18\\x02\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\"\n\"\\x18\\x02\\x00\\x00\\x21\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x13\\x00\\x02\\x00\\x02\\x00\\x00\\x00\\x21\\x00\\x03\\x00\\x03\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x16\\x00\\x03\\x00\\x06\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x17\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x20\\x00\\x04\\x00\"\n\"\\x08\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x21\\x00\\x04\\x00\\x09\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\x17\\x00\\x04\\x00\"\n\"\\x0D\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x0E\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x21\\x00\\x04\\x00\"\n\"\\x0F\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x19\\x00\\x09\\x00\\x13\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x1B\\x00\\x03\\x00\\x14\\x00\\x00\\x00\\x13\\x00\\x00\\x00\\x20\\x00\\x04\\x00\"\n\"\\x15\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x14\\x00\\x00\\x00\\x17\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x20\\x00\\x04\\x00\"\n\"\\x17\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x16\\x00\\x00\\x00\\x21\\x00\\x06\\x00\\x18\\x00\\x00\\x00\\x16\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x17\\x00\\x00\\x00\"\n\"\\x08\\x00\\x00\\x00\\x15\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x32\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\\x1F\\x00\\x00\\x00\"\n\"\\x15\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x14\\x00\\x02\\x00\\x21\\x00\\x00\\x00\\x34\\x00\\x06\\x00\"\n\"\\x21\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\xAB\\x00\\x00\\x00\\x1F\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x34\\x00\\x05\\x00\\x21\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\xA8\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x32\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\\x28\\x00\\x00\\x00\\x04\\x01\\x00\\x00\\x34\\x00\\x06\\x00\\x21\\x00\\x00\\x00\"\n\"\\x29\\x00\\x00\\x00\\xAB\\x00\\x00\\x00\\x28\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x2C\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x20\\x00\\x04\\x00\\x2E\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x2E\\x00\\x00\\x00\\x2F\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x15\\x00\\x04\\x00\\x30\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x30\\x00\\x00\\x00\\x31\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x20\\x00\\x04\\x00\\x32\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x30\\x00\\x00\\x00\\x35\\x00\\x00\\x00\\x03\\x00\\x00\\x00\"\n\"\\x18\\x00\\x04\\x00\\x3A\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x30\\x00\\x00\\x00\\x3B\\x00\\x00\\x00\\x04\\x00\\x00\\x00\"\n\"\\x1C\\x00\\x04\\x00\\x3C\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x3B\\x00\\x00\\x00\\x1E\\x00\\x19\\x00\\x3D\\x00\\x00\\x00\\x3A\\x00\\x00\\x00\\x3A\\x00\\x00\\x00\"\n\"\\x3A\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x3C\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x0D\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x16\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x3E\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x3E\\x00\\x00\\x00\\x3F\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\\x40\\x00\\x00\\x00\"\n\"\\x14\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x41\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\\x48\\x00\\x00\\x00\"\n\"\\x15\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x56\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x32\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\\x5C\\x00\\x00\\x00\"\n\"\\x03\\x01\\x00\\x00\\x34\\x00\\x06\\x00\\x21\\x00\\x00\\x00\\x5D\\x00\\x00\\x00\\xAB\\x00\\x00\\x00\\x5C\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\"\n\"\\x06\\x00\\x00\\x00\\x64\\x00\\x00\\x00\\x3B\\xAA\\xB8\\x3F\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x68\\x00\\x00\\x00\\x00\\x00\\x80\\x3F\\x2B\\x00\\x04\\x00\"\n\"\\x1E\\x00\\x00\\x00\\x69\\x00\\x00\\x00\\x13\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x73\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x34\\x00\\x06\\x00\"\n\"\\x21\\x00\\x00\\x00\\x7D\\x00\\x00\\x00\\xAB\\x00\\x00\\x00\\x1F\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x34\\x00\\x05\\x00\\x21\\x00\\x00\\x00\\x7E\\x00\\x00\\x00\"\n\"\\xA8\\x00\\x00\\x00\\x7D\\x00\\x00\\x00\\x2A\\x00\\x03\\x00\\x21\\x00\\x00\\x00\\x90\\x00\\x00\\x00\\x32\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x97\\x00\\x00\\x00\"\n\"\\x00\\x80\\x80\\x43\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x9A\\x00\\x00\\x00\\x00\\x00\\x80\\xBF\\x2C\\x00\\x05\\x00\\x16\\x00\\x00\\x00\\x9B\\x00\\x00\\x00\"\n\"\\x9A\\x00\\x00\\x00\\x68\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\xA8\\x00\\x00\\x00\\x00\\x00\\x20\\x41\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\"\n\"\\xC9\\x00\\x00\\x00\\x00\\x00\\xA0\\x40\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\xD6\\x00\\x00\\x00\\x00\\x00\\x00\\x3F\\x32\\x00\\x04\\x00\\x06\\x00\\x00\\x00\"\n\"\\xE5\\x00\\x00\\x00\\x00\\x00\\x80\\x43\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\xF5\\x00\\x00\\x00\\xFA\\x7E\\xAA\\x3E\\x20\\x00\\x04\\x00\\x16\\x01\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x16\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x16\\x01\\x00\\x00\\x17\\x01\\x00\\x00\\x01\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x15\\x00\\x00\\x00\"\n\"\\x1C\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x1D\\x01\\x00\\x00\\x01\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x1D\\x01\\x00\\x00\"\n\"\\x1E\\x01\\x00\\x00\\x01\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x24\\x01\\x00\\x00\\x03\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x24\\x01\\x00\\x00\"\n\"\\x25\\x01\\x00\\x00\\x03\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x15\\x00\\x00\\x00\\x26\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x32\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\"\n\"\\x2A\\x01\\x00\\x00\\x10\\x00\\x00\\x00\\x34\\x00\\x06\\x00\\x21\\x00\\x00\\x00\\x2B\\x01\\x00\\x00\\xAB\\x00\\x00\\x00\\x2A\\x01\\x00\\x00\\x20\\x00\\x00\\x00\"\n\"\\x32\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x2E\\x01\\x00\\x00\\x00\\x00\\x81\\x43\\x32\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\\x31\\x01\\x00\\x00\\x13\\x00\\x00\\x00\"\n\"\\x34\\x00\\x06\\x00\\x21\\x00\\x00\\x00\\x32\\x01\\x00\\x00\\xAB\\x00\\x00\\x00\\x31\\x01\\x00\\x00\\x20\\x00\\x00\\x00\\x34\\x00\\x06\\x00\\x21\\x00\\x00\\x00\"\n\"\\x42\\x01\\x00\\x00\\xAB\\x00\\x00\\x00\\x31\\x01\\x00\\x00\\x20\\x00\\x00\\x00\\x2C\\x00\\x06\\x00\\x07\\x00\\x00\\x00\\x46\\x01\\x00\\x00\\x56\\x00\\x00\\x00\"\n\"\\x56\\x00\\x00\\x00\\x68\\x00\\x00\\x00\\x32\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\\x47\\x01\\x00\\x00\\x05\\x01\\x00\\x00\\x34\\x00\\x06\\x00\\x21\\x00\\x00\\x00\"\n\"\\x48\\x01\\x00\\x00\\xAB\\x00\\x00\\x00\\x47\\x01\\x00\\x00\\x20\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x2E\\x00\\x00\\x00\\x4C\\x01\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x2B\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\\x4F\\x01\\x00\\x00\\x0B\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x15\\x00\\x00\\x00\\x55\\x01\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x3B\\x00\\x04\\x00\\x16\\x01\\x00\\x00\\x57\\x01\\x00\\x00\\x01\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x61\\x01\\x00\\x00\\x00\\x00\\x00\\x40\"\n\"\\x3B\\x00\\x04\\x00\\x15\\x00\\x00\\x00\\x62\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x6A\\x01\\x00\\x00\\x00\\x00\\x80\\x3E\"\n\"\\x3B\\x00\\x04\\x00\\x15\\x00\\x00\\x00\\x7A\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x96\\x01\\x00\\x00\\x00\\x00\\x00\\x42\"\n\"\\x34\\x00\\x06\\x00\\x21\\x00\\x00\\x00\\xA6\\x01\\x00\\x00\\xAB\\x00\\x00\\x00\\x31\\x01\\x00\\x00\\x20\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x30\\x00\\x00\\x00\"\n\"\\xAE\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x18\\x00\\x04\\x00\\xB1\\x01\\x00\\x00\\x07\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\xB2\\x01\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\xB1\\x01\\x00\\x00\\x3B\\x00\\x04\\x00\\xB2\\x01\\x00\\x00\\xB3\\x01\\x00\\x00\\x01\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x30\\x00\\x00\\x00\"\n\"\\xB7\\x01\\x00\\x00\\x01\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\\xBA\\x01\\x00\\x00\\x01\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\"\n\"\\xC1\\x01\\x00\\x00\\x02\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\xC6\\x01\\x00\\x00\\x02\\x00\\x00\\x00\\x3A\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x15\\x00\\x00\\x00\"\n\"\\xD0\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x19\\x00\\x09\\x00\\xD5\\x01\\x00\\x00\\x06\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x1B\\x00\\x03\\x00\\xD6\\x01\\x00\\x00\\xD5\\x01\\x00\\x00\\x20\\x00\\x04\\x00\\xD7\\x01\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\xD6\\x01\\x00\\x00\\x3B\\x00\\x04\\x00\\xD7\\x01\\x00\\x00\\xD8\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x32\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\"\n\"\\xE9\\x01\\x00\\x00\\x11\\x00\\x00\\x00\\x34\\x00\\x06\\x00\\x21\\x00\\x00\\x00\\xEA\\x01\\x00\\x00\\xAB\\x00\\x00\\x00\\xE9\\x01\\x00\\x00\\x20\\x00\\x00\\x00\"\n\"\\x3B\\x00\\x04\\x00\\x15\\x00\\x00\\x00\\xED\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\\xF8\\x01\\x00\\x00\\x12\\x00\\x00\\x00\"\n\"\\x32\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\xFC\\x01\\x00\\x00\\x00\\x00\\x83\\x43\\x32\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\\x00\\x02\\x00\\x00\\x07\\x01\\x00\\x00\"\n\"\\x34\\x00\\x06\\x00\\x21\\x00\\x00\\x00\\x01\\x02\\x00\\x00\\xAB\\x00\\x00\\x00\\x00\\x02\\x00\\x00\\x20\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x04\\x02\\x00\\x00\"\n\"\\x03\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x1E\\x00\\x0C\\x00\\x16\\x02\\x00\\x00\\x3A\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x16\\x00\\x00\\x00\\x16\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x17\\x02\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\x16\\x02\\x00\\x00\\x3B\\x00\\x04\\x00\\x17\\x02\\x00\\x00\\x18\\x02\\x00\\x00\\x02\\x00\\x00\\x00\\x36\\x00\\x05\\x00\\x02\\x00\\x00\\x00\"\n\"\\x04\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x05\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x17\\x00\\x00\\x00\\x15\\x01\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x17\\x00\\x00\\x00\\x1F\\x01\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x08\\x00\\x00\\x00\\x21\\x01\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x08\\x00\\x00\\x00\\x37\\x01\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x08\\x00\\x00\\x00\\x4B\\x01\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x08\\x00\\x00\\x00\\x60\\x01\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x0E\\x00\\x00\\x00\\x79\\x01\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x08\\x00\\x00\\x00\\x80\\x01\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x2C\\x00\\x00\\x00\\x91\\x01\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x08\\x00\\x00\\x00\\xA9\\x01\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x0E\\x00\\x00\\x00\\x13\\x02\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x18\\x01\\x00\\x00\\x17\\x01\\x00\\x00\\x3E\\x00\\x03\\x00\\x15\\x01\\x00\\x00\\x18\\x01\\x00\\x00\"\n\"\\xBA\\x00\\x05\\x00\\x21\\x00\\x00\\x00\\x19\\x01\\x00\\x00\\xE5\\x00\\x00\\x00\\x56\\x00\\x00\\x00\\xF7\\x00\\x03\\x00\\x1B\\x01\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\xFA\\x00\\x04\\x00\\x19\\x01\\x00\\x00\\x1A\\x01\\x00\\x00\\x1B\\x01\\x00\\x00\\xF8\\x00\\x02\\x00\\x1A\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x16\\x00\\x00\\x00\"\n\"\\x20\\x01\\x00\\x00\\x15\\x01\\x00\\x00\\x3E\\x00\\x03\\x00\\x1F\\x01\\x00\\x00\\x20\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x22\\x01\\x00\\x00\"\n\"\\x1E\\x01\\x00\\x00\\x3E\\x00\\x03\\x00\\x21\\x01\\x00\\x00\\x22\\x01\\x00\\x00\\x39\\x00\\x07\\x00\\x16\\x00\\x00\\x00\\x23\\x01\\x00\\x00\\x1C\\x00\\x00\\x00\"\n\"\\x1C\\x01\\x00\\x00\\x1F\\x01\\x00\\x00\\x21\\x01\\x00\\x00\\x3E\\x00\\x03\\x00\\x15\\x01\\x00\\x00\\x23\\x01\\x00\\x00\\xF9\\x00\\x02\\x00\\x1B\\x01\\x00\\x00\"\n\"\\xF8\\x00\\x02\\x00\\x1B\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x14\\x00\\x00\\x00\\x27\\x01\\x00\\x00\\x26\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x16\\x00\\x00\\x00\"\n\"\\x28\\x01\\x00\\x00\\x15\\x01\\x00\\x00\\x57\\x00\\x05\\x00\\x0D\\x00\\x00\\x00\\x29\\x01\\x00\\x00\\x27\\x01\\x00\\x00\\x28\\x01\\x00\\x00\\x3E\\x00\\x03\\x00\"\n\"\\x25\\x01\\x00\\x00\\x29\\x01\\x00\\x00\\xF7\\x00\\x03\\x00\\x2D\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\\x2B\\x01\\x00\\x00\\x2C\\x01\\x00\\x00\"\n\"\\x2D\\x01\\x00\\x00\\xF8\\x00\\x02\\x00\\x2C\\x01\\x00\\x00\\xBA\\x00\\x05\\x00\\x21\\x00\\x00\\x00\\x2F\\x01\\x00\\x00\\x2E\\x01\\x00\\x00\\x56\\x00\\x00\\x00\"\n\"\\xA6\\x00\\x05\\x00\\x21\\x00\\x00\\x00\\x30\\x01\\x00\\x00\\x90\\x00\\x00\\x00\\x2F\\x01\\x00\\x00\\xA6\\x00\\x05\\x00\\x21\\x00\\x00\\x00\\x33\\x01\\x00\\x00\"\n\"\\x30\\x01\\x00\\x00\\x32\\x01\\x00\\x00\\xF9\\x00\\x02\\x00\\x2D\\x01\\x00\\x00\\xF8\\x00\\x02\\x00\\x2D\\x01\\x00\\x00\\xF5\\x00\\x07\\x00\\x21\\x00\\x00\\x00\"\n\"\\x34\\x01\\x00\\x00\\x2B\\x01\\x00\\x00\\x1B\\x01\\x00\\x00\\x33\\x01\\x00\\x00\\x2C\\x01\\x00\\x00\\xF7\\x00\\x03\\x00\\x36\\x01\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\xFA\\x00\\x04\\x00\\x34\\x01\\x00\\x00\\x35\\x01\\x00\\x00\\x3F\\x01\\x00\\x00\\xF8\\x00\\x02\\x00\\x35\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x14\\x00\\x00\\x00\"\n\"\\x38\\x01\\x00\\x00\\x1C\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x39\\x01\\x00\\x00\\x15\\x01\\x00\\x00\\x57\\x00\\x05\\x00\\x0D\\x00\\x00\\x00\"\n\"\\x3A\\x01\\x00\\x00\\x38\\x01\\x00\\x00\\x39\\x01\\x00\\x00\\x4F\\x00\\x08\\x00\\x07\\x00\\x00\\x00\\x3B\\x01\\x00\\x00\\x3A\\x01\\x00\\x00\\x3A\\x01\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x50\\x00\\x06\\x00\\x07\\x00\\x00\\x00\\x3C\\x01\\x00\\x00\\xD6\\x00\\x00\\x00\\xD6\\x00\\x00\\x00\"\n\"\\xD6\\x00\\x00\\x00\\x83\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x3D\\x01\\x00\\x00\\x3B\\x01\\x00\\x00\\x3C\\x01\\x00\\x00\\x0C\\x00\\x06\\x00\\x07\\x00\\x00\\x00\"\n\"\\x3E\\x01\\x00\\x00\\x01\\x00\\x00\\x00\\x45\\x00\\x00\\x00\\x3D\\x01\\x00\\x00\\x3E\\x00\\x03\\x00\\x37\\x01\\x00\\x00\\x3E\\x01\\x00\\x00\\xF9\\x00\\x02\\x00\"\n\"\\x36\\x01\\x00\\x00\\xF8\\x00\\x02\\x00\\x3F\\x01\\x00\\x00\\xBA\\x00\\x05\\x00\\x21\\x00\\x00\\x00\\x40\\x01\\x00\\x00\\x2E\\x01\\x00\\x00\\x56\\x00\\x00\\x00\"\n\"\\xA6\\x00\\x05\\x00\\x21\\x00\\x00\\x00\\x41\\x01\\x00\\x00\\x40\\x01\\x00\\x00\\x90\\x00\\x00\\x00\\xA6\\x00\\x05\\x00\\x21\\x00\\x00\\x00\\x43\\x01\\x00\\x00\"\n\"\\x41\\x01\\x00\\x00\\x42\\x01\\x00\\x00\\xF7\\x00\\x03\\x00\\x45\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\\x43\\x01\\x00\\x00\\x44\\x01\\x00\\x00\"\n\"\\x45\\x01\\x00\\x00\\xF8\\x00\\x02\\x00\\x44\\x01\\x00\\x00\\x3E\\x00\\x03\\x00\\x37\\x01\\x00\\x00\\x46\\x01\\x00\\x00\\xF9\\x00\\x02\\x00\\x45\\x01\\x00\\x00\"\n\"\\xF8\\x00\\x02\\x00\\x45\\x01\\x00\\x00\\xF9\\x00\\x02\\x00\\x36\\x01\\x00\\x00\\xF8\\x00\\x02\\x00\\x36\\x01\\x00\\x00\\xF7\\x00\\x03\\x00\\x4A\\x01\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\\x48\\x01\\x00\\x00\\x49\\x01\\x00\\x00\\x54\\x01\\x00\\x00\\xF8\\x00\\x02\\x00\\x49\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x0D\\x00\\x00\\x00\\x4D\\x01\\x00\\x00\\x4C\\x01\\x00\\x00\\x4F\\x00\\x08\\x00\\x07\\x00\\x00\\x00\\x4E\\x01\\x00\\x00\\x4D\\x01\\x00\\x00\\x4D\\x01\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x41\\x00\\x06\\x00\\x73\\x00\\x00\\x00\\x50\\x01\\x00\\x00\\x3F\\x00\\x00\\x00\\x4F\\x01\\x00\\x00\"\n\"\\x20\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\x51\\x01\\x00\\x00\\x50\\x01\\x00\\x00\\x4F\\x00\\x08\\x00\\x07\\x00\\x00\\x00\\x52\\x01\\x00\\x00\"\n\"\\x51\\x01\\x00\\x00\\x51\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x53\\x01\\x00\\x00\"\n\"\\x4E\\x01\\x00\\x00\\x52\\x01\\x00\\x00\\x3E\\x00\\x03\\x00\\x4B\\x01\\x00\\x00\\x53\\x01\\x00\\x00\\xF9\\x00\\x02\\x00\\x4A\\x01\\x00\\x00\\xF8\\x00\\x02\\x00\"\n\"\\x54\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x14\\x00\\x00\\x00\\x56\\x01\\x00\\x00\\x55\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x58\\x01\\x00\\x00\"\n\"\\x57\\x01\\x00\\x00\\x57\\x00\\x05\\x00\\x0D\\x00\\x00\\x00\\x59\\x01\\x00\\x00\\x56\\x01\\x00\\x00\\x58\\x01\\x00\\x00\\x41\\x00\\x06\\x00\\x73\\x00\\x00\\x00\"\n\"\\x5A\\x01\\x00\\x00\\x3F\\x00\\x00\\x00\\x4F\\x01\\x00\\x00\\x20\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\x5B\\x01\\x00\\x00\\x5A\\x01\\x00\\x00\"\n\"\\x85\\x00\\x05\\x00\\x0D\\x00\\x00\\x00\\x5C\\x01\\x00\\x00\\x59\\x01\\x00\\x00\\x5B\\x01\\x00\\x00\\x4F\\x00\\x08\\x00\\x07\\x00\\x00\\x00\\x5D\\x01\\x00\\x00\"\n\"\\x5C\\x01\\x00\\x00\\x5C\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x4B\\x01\\x00\\x00\\x5D\\x01\\x00\\x00\"\n\"\\xF7\\x00\\x03\\x00\\x5F\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\\x90\\x00\\x00\\x00\\x5E\\x01\\x00\\x00\\x5F\\x01\\x00\\x00\\xF8\\x00\\x02\\x00\"\n\"\\x5E\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x14\\x00\\x00\\x00\\x63\\x01\\x00\\x00\\x62\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x64\\x01\\x00\\x00\"\n\"\\x57\\x01\\x00\\x00\\x57\\x00\\x05\\x00\\x0D\\x00\\x00\\x00\\x65\\x01\\x00\\x00\\x63\\x01\\x00\\x00\\x64\\x01\\x00\\x00\\x4F\\x00\\x08\\x00\\x07\\x00\\x00\\x00\"\n\"\\x66\\x01\\x00\\x00\\x65\\x01\\x00\\x00\\x65\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x50\\x00\\x06\\x00\\x07\\x00\\x00\\x00\"\n\"\\x67\\x01\\x00\\x00\\xD6\\x00\\x00\\x00\\xD6\\x00\\x00\\x00\\xD6\\x00\\x00\\x00\\x83\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x68\\x01\\x00\\x00\\x66\\x01\\x00\\x00\"\n\"\\x67\\x01\\x00\\x00\\x8E\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x69\\x01\\x00\\x00\\x68\\x01\\x00\\x00\\x61\\x01\\x00\\x00\\x3E\\x00\\x03\\x00\\x60\\x01\\x00\\x00\"\n\"\\x69\\x01\\x00\\x00\\x41\\x00\\x05\\x00\\x2C\\x00\\x00\\x00\\x6B\\x01\\x00\\x00\\x60\\x01\\x00\\x00\\x31\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\"\n\"\\x6C\\x01\\x00\\x00\\x6B\\x01\\x00\\x00\\x0C\\x00\\x07\\x00\\x06\\x00\\x00\\x00\\x6D\\x01\\x00\\x00\\x01\\x00\\x00\\x00\\x28\\x00\\x00\\x00\\x6A\\x01\\x00\\x00\"\n\"\\x6C\\x01\\x00\\x00\\x88\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x6E\\x01\\x00\\x00\\x68\\x00\\x00\\x00\\x6D\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\"\n\"\\x6F\\x01\\x00\\x00\\x4B\\x01\\x00\\x00\\x8E\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x70\\x01\\x00\\x00\\x6F\\x01\\x00\\x00\\x6E\\x01\\x00\\x00\\x3E\\x00\\x03\\x00\"\n\"\\x4B\\x01\\x00\\x00\\x70\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x71\\x01\\x00\\x00\\x37\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\"\n\"\\x72\\x01\\x00\\x00\\x60\\x01\\x00\\x00\\x94\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x73\\x01\\x00\\x00\\x71\\x01\\x00\\x00\\x72\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x07\\x00\\x00\\x00\\x74\\x01\\x00\\x00\\x4B\\x01\\x00\\x00\\x8E\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x75\\x01\\x00\\x00\\x74\\x01\\x00\\x00\\x73\\x01\\x00\\x00\"\n\"\\x3E\\x00\\x03\\x00\\x4B\\x01\\x00\\x00\\x75\\x01\\x00\\x00\\xF9\\x00\\x02\\x00\\x5F\\x01\\x00\\x00\\xF8\\x00\\x02\\x00\\x5F\\x01\\x00\\x00\\xF9\\x00\\x02\\x00\"\n\"\\x4A\\x01\\x00\\x00\\xF8\\x00\\x02\\x00\\x4A\\x01\\x00\\x00\\xBA\\x00\\x05\\x00\\x21\\x00\\x00\\x00\\x76\\x01\\x00\\x00\\x2E\\x01\\x00\\x00\\x56\\x00\\x00\\x00\"\n\"\\xF7\\x00\\x03\\x00\\x78\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\\x76\\x01\\x00\\x00\\x77\\x01\\x00\\x00\\x78\\x01\\x00\\x00\\xF8\\x00\\x02\\x00\"\n\"\\x77\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x14\\x00\\x00\\x00\\x7B\\x01\\x00\\x00\\x7A\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x7C\\x01\\x00\\x00\"\n\"\\x15\\x01\\x00\\x00\\x57\\x00\\x05\\x00\\x0D\\x00\\x00\\x00\\x7D\\x01\\x00\\x00\\x7B\\x01\\x00\\x00\\x7C\\x01\\x00\\x00\\x3E\\x00\\x03\\x00\\x79\\x01\\x00\\x00\"\n\"\\x7D\\x01\\x00\\x00\\xF7\\x00\\x03\\x00\\x7F\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\\x90\\x00\\x00\\x00\\x7E\\x01\\x00\\x00\\x8C\\x01\\x00\\x00\"\n\"\\xF8\\x00\\x02\\x00\\x7E\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x81\\x01\\x00\\x00\\x1E\\x01\\x00\\x00\\x0C\\x00\\x06\\x00\\x07\\x00\\x00\\x00\"\n\"\\x82\\x01\\x00\\x00\\x01\\x00\\x00\\x00\\x45\\x00\\x00\\x00\\x81\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x14\\x00\\x00\\x00\\x83\\x01\\x00\\x00\\x62\\x01\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x84\\x01\\x00\\x00\\x57\\x01\\x00\\x00\\x57\\x00\\x05\\x00\\x0D\\x00\\x00\\x00\\x85\\x01\\x00\\x00\\x83\\x01\\x00\\x00\"\n\"\\x84\\x01\\x00\\x00\\x4F\\x00\\x08\\x00\\x07\\x00\\x00\\x00\\x86\\x01\\x00\\x00\\x85\\x01\\x00\\x00\\x85\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\x50\\x00\\x06\\x00\\x07\\x00\\x00\\x00\\x87\\x01\\x00\\x00\\xD6\\x00\\x00\\x00\\xD6\\x00\\x00\\x00\\xD6\\x00\\x00\\x00\\x83\\x00\\x05\\x00\"\n\"\\x07\\x00\\x00\\x00\\x88\\x01\\x00\\x00\\x86\\x01\\x00\\x00\\x87\\x01\\x00\\x00\\x8E\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x89\\x01\\x00\\x00\\x88\\x01\\x00\\x00\"\n\"\\x61\\x01\\x00\\x00\\x81\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x8A\\x01\\x00\\x00\\x82\\x01\\x00\\x00\\x89\\x01\\x00\\x00\\x0C\\x00\\x06\\x00\\x07\\x00\\x00\\x00\"\n\"\\x8B\\x01\\x00\\x00\\x01\\x00\\x00\\x00\\x45\\x00\\x00\\x00\\x8A\\x01\\x00\\x00\\x3E\\x00\\x03\\x00\\x80\\x01\\x00\\x00\\x8B\\x01\\x00\\x00\\xF9\\x00\\x02\\x00\"\n\"\\x7F\\x01\\x00\\x00\\xF8\\x00\\x02\\x00\\x8C\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x8D\\x01\\x00\\x00\\x1E\\x01\\x00\\x00\\x0C\\x00\\x06\\x00\"\n\"\\x07\\x00\\x00\\x00\\x8E\\x01\\x00\\x00\\x01\\x00\\x00\\x00\\x45\\x00\\x00\\x00\\x8D\\x01\\x00\\x00\\x81\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x8F\\x01\\x00\\x00\"\n\"\\x8E\\x01\\x00\\x00\\x46\\x01\\x00\\x00\\x0C\\x00\\x06\\x00\\x07\\x00\\x00\\x00\\x90\\x01\\x00\\x00\\x01\\x00\\x00\\x00\\x45\\x00\\x00\\x00\\x8F\\x01\\x00\\x00\"\n\"\\x3E\\x00\\x03\\x00\\x80\\x01\\x00\\x00\\x90\\x01\\x00\\x00\\xF9\\x00\\x02\\x00\\x7F\\x01\\x00\\x00\\xF8\\x00\\x02\\x00\\x7F\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x07\\x00\\x00\\x00\\x92\\x01\\x00\\x00\\x80\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x93\\x01\\x00\\x00\\x37\\x01\\x00\\x00\\x94\\x00\\x05\\x00\"\n\"\\x06\\x00\\x00\\x00\\x94\\x01\\x00\\x00\\x92\\x01\\x00\\x00\\x93\\x01\\x00\\x00\\x0C\\x00\\x07\\x00\\x06\\x00\\x00\\x00\\x95\\x01\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x28\\x00\\x00\\x00\\x94\\x01\\x00\\x00\\x56\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x2C\\x00\\x00\\x00\\x97\\x01\\x00\\x00\\x79\\x01\\x00\\x00\\x35\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x98\\x01\\x00\\x00\\x97\\x01\\x00\\x00\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x99\\x01\\x00\\x00\\x96\\x01\\x00\\x00\"\n\"\\x98\\x01\\x00\\x00\\x0C\\x00\\x07\\x00\\x06\\x00\\x00\\x00\\x9A\\x01\\x00\\x00\\x01\\x00\\x00\\x00\\x1A\\x00\\x00\\x00\\x95\\x01\\x00\\x00\\x99\\x01\\x00\\x00\"\n\"\\x3E\\x00\\x03\\x00\\x91\\x01\\x00\\x00\\x9A\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x9B\\x01\\x00\\x00\\x91\\x01\\x00\\x00\\x85\\x00\\x05\\x00\"\n\"\\x06\\x00\\x00\\x00\\x9C\\x01\\x00\\x00\\x9B\\x01\\x00\\x00\\x2E\\x01\\x00\\x00\\x3E\\x00\\x03\\x00\\x91\\x01\\x00\\x00\\x9C\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x06\\x00\\x00\\x00\\x9D\\x01\\x00\\x00\\x91\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\x9E\\x01\\x00\\x00\\x79\\x01\\x00\\x00\\x4F\\x00\\x08\\x00\"\n\"\\x07\\x00\\x00\\x00\\x9F\\x01\\x00\\x00\\x9E\\x01\\x00\\x00\\x9E\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x8E\\x00\\x05\\x00\"\n\"\\x07\\x00\\x00\\x00\\xA0\\x01\\x00\\x00\\x9F\\x01\\x00\\x00\\x9D\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\xA1\\x01\\x00\\x00\\x25\\x01\\x00\\x00\"\n\"\\x4F\\x00\\x08\\x00\\x07\\x00\\x00\\x00\\xA2\\x01\\x00\\x00\\xA1\\x01\\x00\\x00\\xA1\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x81\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\xA3\\x01\\x00\\x00\\xA2\\x01\\x00\\x00\\xA0\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\xA4\\x01\\x00\\x00\"\n\"\\x25\\x01\\x00\\x00\\x4F\\x00\\x09\\x00\\x0D\\x00\\x00\\x00\\xA5\\x01\\x00\\x00\\xA4\\x01\\x00\\x00\\xA3\\x01\\x00\\x00\\x04\\x00\\x00\\x00\\x05\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x25\\x01\\x00\\x00\\xA5\\x01\\x00\\x00\\xF9\\x00\\x02\\x00\\x78\\x01\\x00\\x00\\xF8\\x00\\x02\\x00\"\n\"\\x78\\x01\\x00\\x00\\xF7\\x00\\x03\\x00\\xA8\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\\xA6\\x01\\x00\\x00\\xA7\\x01\\x00\\x00\\xA8\\x01\\x00\\x00\"\n\"\\xF8\\x00\\x02\\x00\\xA7\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\xAA\\x01\\x00\\x00\\x1E\\x01\\x00\\x00\\x7F\\x00\\x04\\x00\\x07\\x00\\x00\\x00\"\n\"\\xAB\\x01\\x00\\x00\\xAA\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\xAC\\x01\\x00\\x00\\x37\\x01\\x00\\x00\\x0C\\x00\\x07\\x00\\x07\\x00\\x00\\x00\"\n\"\\xAD\\x01\\x00\\x00\\x01\\x00\\x00\\x00\\x47\\x00\\x00\\x00\\xAB\\x01\\x00\\x00\\xAC\\x01\\x00\\x00\\x3E\\x00\\x03\\x00\\xA9\\x01\\x00\\x00\\xAD\\x01\\x00\\x00\"\n\"\\x41\\x00\\x05\\x00\\x2C\\x00\\x00\\x00\\xAF\\x01\\x00\\x00\\xA9\\x01\\x00\\x00\\xAE\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\xB0\\x01\\x00\\x00\"\n\"\\xAF\\x01\\x00\\x00\\x41\\x00\\x05\\x00\\x1D\\x01\\x00\\x00\\xB4\\x01\\x00\\x00\\xB3\\x01\\x00\\x00\\x20\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\"\n\"\\xB5\\x01\\x00\\x00\\xB4\\x01\\x00\\x00\\x8E\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\xB6\\x01\\x00\\x00\\xB5\\x01\\x00\\x00\\xB0\\x01\\x00\\x00\\x41\\x00\\x05\\x00\"\n\"\\x2C\\x00\\x00\\x00\\xB8\\x01\\x00\\x00\\xA9\\x01\\x00\\x00\\xB7\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\xB9\\x01\\x00\\x00\\xB8\\x01\\x00\\x00\"\n\"\\x41\\x00\\x05\\x00\\x1D\\x01\\x00\\x00\\xBB\\x01\\x00\\x00\\xB3\\x01\\x00\\x00\\xBA\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\xBC\\x01\\x00\\x00\"\n\"\\xBB\\x01\\x00\\x00\\x8E\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\xBD\\x01\\x00\\x00\\xBC\\x01\\x00\\x00\\xB9\\x01\\x00\\x00\\x81\\x00\\x05\\x00\\x07\\x00\\x00\\x00\"\n\"\\xBE\\x01\\x00\\x00\\xB6\\x01\\x00\\x00\\xBD\\x01\\x00\\x00\\x41\\x00\\x05\\x00\\x2C\\x00\\x00\\x00\\xBF\\x01\\x00\\x00\\xA9\\x01\\x00\\x00\\x31\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\xC0\\x01\\x00\\x00\\xBF\\x01\\x00\\x00\\x41\\x00\\x05\\x00\\x1D\\x01\\x00\\x00\\xC2\\x01\\x00\\x00\\xB3\\x01\\x00\\x00\"\n\"\\xC1\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\xC3\\x01\\x00\\x00\\xC2\\x01\\x00\\x00\\x8E\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\xC4\\x01\\x00\\x00\"\n\"\\xC3\\x01\\x00\\x00\\xC0\\x01\\x00\\x00\\x81\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\xC5\\x01\\x00\\x00\\xBE\\x01\\x00\\x00\\xC4\\x01\\x00\\x00\\x3E\\x00\\x03\\x00\"\n\"\\xA9\\x01\\x00\\x00\\xC5\\x01\\x00\\x00\\x41\\x00\\x05\\x00\\xC6\\x01\\x00\\x00\\xC7\\x01\\x00\\x00\\x3F\\x00\\x00\\x00\\xBA\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x3A\\x00\\x00\\x00\\xC8\\x01\\x00\\x00\\xC7\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\xC9\\x01\\x00\\x00\\xA9\\x01\\x00\\x00\\x51\\x00\\x05\\x00\"\n\"\\x06\\x00\\x00\\x00\\xCA\\x01\\x00\\x00\\xC9\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\xCB\\x01\\x00\\x00\\xC9\\x01\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\xCC\\x01\\x00\\x00\\xC9\\x01\\x00\\x00\\x02\\x00\\x00\\x00\\x50\\x00\\x07\\x00\\x0D\\x00\\x00\\x00\"\n\"\\xCD\\x01\\x00\\x00\\xCA\\x01\\x00\\x00\\xCB\\x01\\x00\\x00\\xCC\\x01\\x00\\x00\\x56\\x00\\x00\\x00\\x91\\x00\\x05\\x00\\x0D\\x00\\x00\\x00\\xCE\\x01\\x00\\x00\"\n\"\\xC8\\x01\\x00\\x00\\xCD\\x01\\x00\\x00\\x4F\\x00\\x08\\x00\\x07\\x00\\x00\\x00\\xCF\\x01\\x00\\x00\\xCE\\x01\\x00\\x00\\xCE\\x01\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\xA9\\x01\\x00\\x00\\xCF\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x14\\x00\\x00\\x00\\xD1\\x01\\x00\\x00\"\n\"\\xD0\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\xD2\\x01\\x00\\x00\\x15\\x01\\x00\\x00\\x57\\x00\\x05\\x00\\x0D\\x00\\x00\\x00\\xD3\\x01\\x00\\x00\"\n\"\\xD1\\x01\\x00\\x00\\xD2\\x01\\x00\\x00\\x4F\\x00\\x08\\x00\\x07\\x00\\x00\\x00\\xD4\\x01\\x00\\x00\\xD3\\x01\\x00\\x00\\xD3\\x01\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\xD6\\x01\\x00\\x00\\xD9\\x01\\x00\\x00\\xD8\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\"\n\"\\xDA\\x01\\x00\\x00\\xA9\\x01\\x00\\x00\\x57\\x00\\x05\\x00\\x0D\\x00\\x00\\x00\\xDB\\x01\\x00\\x00\\xD9\\x01\\x00\\x00\\xDA\\x01\\x00\\x00\\x4F\\x00\\x08\\x00\"\n\"\\x07\\x00\\x00\\x00\\xDC\\x01\\x00\\x00\\xDB\\x01\\x00\\x00\\xDB\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x85\\x00\\x05\\x00\"\n\"\\x07\\x00\\x00\\x00\\xDD\\x01\\x00\\x00\\xD4\\x01\\x00\\x00\\xDC\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\xDE\\x01\\x00\\x00\\x25\\x01\\x00\\x00\"\n\"\\x4F\\x00\\x08\\x00\\x07\\x00\\x00\\x00\\xDF\\x01\\x00\\x00\\xDE\\x01\\x00\\x00\\xDE\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x81\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\xE0\\x01\\x00\\x00\\xDF\\x01\\x00\\x00\\xDD\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\xE1\\x01\\x00\\x00\"\n\"\\x25\\x01\\x00\\x00\\x4F\\x00\\x09\\x00\\x0D\\x00\\x00\\x00\\xE2\\x01\\x00\\x00\\xE1\\x01\\x00\\x00\\xE0\\x01\\x00\\x00\\x04\\x00\\x00\\x00\\x05\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x25\\x01\\x00\\x00\\xE2\\x01\\x00\\x00\\xF9\\x00\\x02\\x00\\xA8\\x01\\x00\\x00\\xF8\\x00\\x02\\x00\"\n\"\\xA8\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\xE3\\x01\\x00\\x00\\x4B\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\xE4\\x01\\x00\\x00\"\n\"\\x25\\x01\\x00\\x00\\x4F\\x00\\x08\\x00\\x07\\x00\\x00\\x00\\xE5\\x01\\x00\\x00\\xE4\\x01\\x00\\x00\\xE4\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\xE6\\x01\\x00\\x00\\xE5\\x01\\x00\\x00\\xE3\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\"\n\"\\xE7\\x01\\x00\\x00\\x25\\x01\\x00\\x00\\x4F\\x00\\x09\\x00\\x0D\\x00\\x00\\x00\\xE8\\x01\\x00\\x00\\xE7\\x01\\x00\\x00\\xE6\\x01\\x00\\x00\\x04\\x00\\x00\\x00\"\n\"\\x05\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x25\\x01\\x00\\x00\\xE8\\x01\\x00\\x00\\xF7\\x00\\x03\\x00\\xEC\\x01\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\\xEA\\x01\\x00\\x00\\xEB\\x01\\x00\\x00\\xEC\\x01\\x00\\x00\\xF8\\x00\\x02\\x00\\xEB\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x14\\x00\\x00\\x00\\xEE\\x01\\x00\\x00\\xED\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\xEF\\x01\\x00\\x00\\x15\\x01\\x00\\x00\\x57\\x00\\x05\\x00\"\n\"\\x0D\\x00\\x00\\x00\\xF0\\x01\\x00\\x00\\xEE\\x01\\x00\\x00\\xEF\\x01\\x00\\x00\\x4F\\x00\\x08\\x00\\x07\\x00\\x00\\x00\\xF1\\x01\\x00\\x00\\xF0\\x01\\x00\\x00\"\n\"\\xF0\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\xF2\\x01\\x00\\x00\\x25\\x01\\x00\\x00\"\n\"\\x4F\\x00\\x08\\x00\\x07\\x00\\x00\\x00\\xF3\\x01\\x00\\x00\\xF2\\x01\\x00\\x00\\xF2\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x81\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\xF4\\x01\\x00\\x00\\xF3\\x01\\x00\\x00\\xF1\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\xF5\\x01\\x00\\x00\"\n\"\\x25\\x01\\x00\\x00\\x4F\\x00\\x09\\x00\\x0D\\x00\\x00\\x00\\xF6\\x01\\x00\\x00\\xF5\\x01\\x00\\x00\\xF4\\x01\\x00\\x00\\x04\\x00\\x00\\x00\\x05\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x25\\x01\\x00\\x00\\xF6\\x01\\x00\\x00\\xF9\\x00\\x02\\x00\\xEC\\x01\\x00\\x00\\xF8\\x00\\x02\\x00\"\n\"\\xEC\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\xF7\\x01\\x00\\x00\\x25\\x01\\x00\\x00\\x41\\x00\\x05\\x00\\x73\\x00\\x00\\x00\\xF9\\x01\\x00\\x00\"\n\"\\x3F\\x00\\x00\\x00\\xF8\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\xFA\\x01\\x00\\x00\\xF9\\x01\\x00\\x00\\x85\\x00\\x05\\x00\\x0D\\x00\\x00\\x00\"\n\"\\xFB\\x01\\x00\\x00\\xF7\\x01\\x00\\x00\\xFA\\x01\\x00\\x00\\x3E\\x00\\x03\\x00\\x25\\x01\\x00\\x00\\xFB\\x01\\x00\\x00\\xB7\\x00\\x05\\x00\\x21\\x00\\x00\\x00\"\n\"\\xFD\\x01\\x00\\x00\\xFC\\x01\\x00\\x00\\x68\\x00\\x00\\x00\\xF7\\x00\\x03\\x00\\xFF\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\\xFD\\x01\\x00\\x00\"\n\"\\xFE\\x01\\x00\\x00\\xFF\\x01\\x00\\x00\\xF8\\x00\\x02\\x00\\xFE\\x01\\x00\\x00\\xF7\\x00\\x03\\x00\\x03\\x02\\x00\\x00\\x00\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\"\n\"\\x01\\x02\\x00\\x00\\x02\\x02\\x00\\x00\\x0B\\x02\\x00\\x00\\xF8\\x00\\x02\\x00\\x02\\x02\\x00\\x00\\x41\\x00\\x05\\x00\\x04\\x02\\x00\\x00\\x05\\x02\\x00\\x00\"\n\"\\x25\\x01\\x00\\x00\\x35\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x06\\x02\\x00\\x00\\x05\\x02\\x00\\x00\\xB8\\x00\\x05\\x00\\x21\\x00\\x00\\x00\"\n\"\\x07\\x02\\x00\\x00\\x06\\x02\\x00\\x00\\xFC\\x01\\x00\\x00\\xF7\\x00\\x03\\x00\\x09\\x02\\x00\\x00\\x00\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\\x07\\x02\\x00\\x00\"\n\"\\x08\\x02\\x00\\x00\\x09\\x02\\x00\\x00\\xF8\\x00\\x02\\x00\\x08\\x02\\x00\\x00\\xFC\\x00\\x01\\x00\\xF8\\x00\\x02\\x00\\x09\\x02\\x00\\x00\\xF9\\x00\\x02\\x00\"\n\"\\x03\\x02\\x00\\x00\\xF8\\x00\\x02\\x00\\x0B\\x02\\x00\\x00\\x41\\x00\\x05\\x00\\x04\\x02\\x00\\x00\\x0C\\x02\\x00\\x00\\x25\\x01\\x00\\x00\\x35\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x0D\\x02\\x00\\x00\\x0C\\x02\\x00\\x00\\xBE\\x00\\x05\\x00\\x21\\x00\\x00\\x00\\x0E\\x02\\x00\\x00\\x0D\\x02\\x00\\x00\"\n\"\\xFC\\x01\\x00\\x00\\xF7\\x00\\x03\\x00\\x10\\x02\\x00\\x00\\x00\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\\x0E\\x02\\x00\\x00\\x0F\\x02\\x00\\x00\\x10\\x02\\x00\\x00\"\n\"\\xF8\\x00\\x02\\x00\\x0F\\x02\\x00\\x00\\xFC\\x00\\x01\\x00\\xF8\\x00\\x02\\x00\\x10\\x02\\x00\\x00\\xF9\\x00\\x02\\x00\\x03\\x02\\x00\\x00\\xF8\\x00\\x02\\x00\"\n\"\\x03\\x02\\x00\\x00\\x41\\x00\\x05\\x00\\x04\\x02\\x00\\x00\\x12\\x02\\x00\\x00\\x25\\x01\\x00\\x00\\x35\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x12\\x02\\x00\\x00\"\n\"\\x68\\x00\\x00\\x00\\xF9\\x00\\x02\\x00\\xFF\\x01\\x00\\x00\\xF8\\x00\\x02\\x00\\xFF\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\x14\\x02\\x00\\x00\"\n\"\\x25\\x01\\x00\\x00\\x3E\\x00\\x03\\x00\\x13\\x02\\x00\\x00\\x14\\x02\\x00\\x00\\x39\\x00\\x05\\x00\\x0D\\x00\\x00\\x00\\x15\\x02\\x00\\x00\\x11\\x00\\x00\\x00\"\n\"\\x13\\x02\\x00\\x00\\x3E\\x00\\x03\\x00\\x25\\x01\\x00\\x00\\x15\\x02\\x00\\x00\\xFD\\x00\\x01\\x00\\x38\\x00\\x01\\x00\\x36\\x00\\x05\\x00\\x07\\x00\\x00\\x00\"\n\"\\x0B\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x37\\x00\\x03\\x00\\x08\\x00\\x00\\x00\\x0A\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x0C\\x00\\x00\\x00\"\n\"\\x3B\\x00\\x04\\x00\\x2C\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x2C\\x00\\x00\\x00\\x39\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\xF7\\x00\\x03\\x00\\x25\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\\x23\\x00\\x00\\x00\\x24\\x00\\x00\\x00\\x25\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\"\n\"\\x24\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x26\\x00\\x00\\x00\\x0A\\x00\\x00\\x00\\xFE\\x00\\x02\\x00\\x26\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\"\n\"\\x25\\x00\\x00\\x00\\xF7\\x00\\x03\\x00\\x2B\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\\x29\\x00\\x00\\x00\\x2A\\x00\\x00\\x00\\x4D\\x00\\x00\\x00\"\n\"\\xF8\\x00\\x02\\x00\\x2A\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x32\\x00\\x00\\x00\\x33\\x00\\x00\\x00\\x2F\\x00\\x00\\x00\\x31\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x06\\x00\\x00\\x00\\x34\\x00\\x00\\x00\\x33\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x32\\x00\\x00\\x00\\x36\\x00\\x00\\x00\\x2F\\x00\\x00\\x00\\x35\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x37\\x00\\x00\\x00\\x36\\x00\\x00\\x00\\x88\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x38\\x00\\x00\\x00\\x34\\x00\\x00\\x00\"\n\"\\x37\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x2D\\x00\\x00\\x00\\x38\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x41\\x00\\x00\\x00\\x42\\x00\\x00\\x00\\x3F\\x00\\x00\\x00\"\n\"\\x40\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x43\\x00\\x00\\x00\\x42\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x44\\x00\\x00\\x00\"\n\"\\x2D\\x00\\x00\\x00\\x83\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x45\\x00\\x00\\x00\\x43\\x00\\x00\\x00\\x44\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x41\\x00\\x00\\x00\"\n\"\\x46\\x00\\x00\\x00\\x3F\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x47\\x00\\x00\\x00\\x46\\x00\\x00\\x00\\x41\\x00\\x05\\x00\"\n\"\\x41\\x00\\x00\\x00\\x49\\x00\\x00\\x00\\x3F\\x00\\x00\\x00\\x48\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x4A\\x00\\x00\\x00\\x49\\x00\\x00\\x00\"\n\"\\x83\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x4B\\x00\\x00\\x00\\x47\\x00\\x00\\x00\\x4A\\x00\\x00\\x00\\x88\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x4C\\x00\\x00\\x00\"\n\"\\x45\\x00\\x00\\x00\\x4B\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x39\\x00\\x00\\x00\\x4C\\x00\\x00\\x00\\xF9\\x00\\x02\\x00\\x2B\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\"\n\"\\x4D\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x41\\x00\\x00\\x00\\x4E\\x00\\x00\\x00\\x3F\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\"\n\"\\x4F\\x00\\x00\\x00\\x4E\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x32\\x00\\x00\\x00\\x50\\x00\\x00\\x00\\x2F\\x00\\x00\\x00\\x31\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x06\\x00\\x00\\x00\\x51\\x00\\x00\\x00\\x50\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x52\\x00\\x00\\x00\\x4F\\x00\\x00\\x00\\x51\\x00\\x00\\x00\"\n\"\\x41\\x00\\x05\\x00\\x32\\x00\\x00\\x00\\x53\\x00\\x00\\x00\\x2F\\x00\\x00\\x00\\x35\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x54\\x00\\x00\\x00\"\n\"\\x53\\x00\\x00\\x00\\x88\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x55\\x00\\x00\\x00\\x52\\x00\\x00\\x00\\x54\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x2D\\x00\\x00\\x00\"\n\"\\x55\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x57\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x41\\x00\\x00\\x00\\x58\\x00\\x00\\x00\"\n\"\\x3F\\x00\\x00\\x00\\x48\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x59\\x00\\x00\\x00\\x58\\x00\\x00\\x00\\x83\\x00\\x05\\x00\\x06\\x00\\x00\\x00\"\n\"\\x5A\\x00\\x00\\x00\\x57\\x00\\x00\\x00\\x59\\x00\\x00\\x00\\x0C\\x00\\x07\\x00\\x06\\x00\\x00\\x00\\x5B\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x28\\x00\\x00\\x00\"\n\"\\x56\\x00\\x00\\x00\\x5A\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x2D\\x00\\x00\\x00\\x5B\\x00\\x00\\x00\\xF7\\x00\\x03\\x00\\x5F\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\xFA\\x00\\x04\\x00\\x5D\\x00\\x00\\x00\\x5E\\x00\\x00\\x00\\x5F\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x5E\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\"\n\"\\x60\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x61\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\"\n\"\\x62\\x00\\x00\\x00\\x61\\x00\\x00\\x00\\x60\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x2D\\x00\\x00\\x00\\x62\\x00\\x00\\x00\\xF9\\x00\\x02\\x00\\x5F\\x00\\x00\\x00\"\n\"\\xF8\\x00\\x02\\x00\\x5F\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x63\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\"\n\"\\x65\\x00\\x00\\x00\\x63\\x00\\x00\\x00\\x64\\x00\\x00\\x00\\x7F\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x66\\x00\\x00\\x00\\x65\\x00\\x00\\x00\\x0C\\x00\\x06\\x00\"\n\"\\x06\\x00\\x00\\x00\\x67\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x66\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x39\\x00\\x00\\x00\\x67\\x00\\x00\\x00\"\n\"\\xF9\\x00\\x02\\x00\\x2B\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x2B\\x00\\x00\\x00\\x41\\x00\\x06\\x00\\x41\\x00\\x00\\x00\\x6A\\x00\\x00\\x00\\x3F\\x00\\x00\\x00\"\n\"\\x69\\x00\\x00\\x00\\x35\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x6B\\x00\\x00\\x00\\x6A\\x00\\x00\\x00\\x83\\x00\\x05\\x00\\x06\\x00\\x00\\x00\"\n\"\\x6C\\x00\\x00\\x00\\x68\\x00\\x00\\x00\\x6B\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x6D\\x00\\x00\\x00\\x39\\x00\\x00\\x00\\x0C\\x00\\x08\\x00\"\n\"\\x06\\x00\\x00\\x00\\x6E\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x2B\\x00\\x00\\x00\\x6D\\x00\\x00\\x00\\x56\\x00\\x00\\x00\\x68\\x00\\x00\\x00\\x41\\x00\\x06\\x00\"\n\"\\x41\\x00\\x00\\x00\\x6F\\x00\\x00\\x00\\x3F\\x00\\x00\\x00\\x69\\x00\\x00\\x00\\x35\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x70\\x00\\x00\\x00\"\n\"\\x6F\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x71\\x00\\x00\\x00\\x6E\\x00\\x00\\x00\\x70\\x00\\x00\\x00\\x81\\x00\\x05\\x00\\x06\\x00\\x00\\x00\"\n\"\\x72\\x00\\x00\\x00\\x6C\\x00\\x00\\x00\\x71\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x39\\x00\\x00\\x00\\x72\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x73\\x00\\x00\\x00\"\n\"\\x74\\x00\\x00\\x00\\x3F\\x00\\x00\\x00\\x69\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\x75\\x00\\x00\\x00\\x74\\x00\\x00\\x00\\x4F\\x00\\x08\\x00\"\n\"\\x07\\x00\\x00\\x00\\x76\\x00\\x00\\x00\\x75\\x00\\x00\\x00\\x75\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x07\\x00\\x00\\x00\\x77\\x00\\x00\\x00\\x0A\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x78\\x00\\x00\\x00\\x39\\x00\\x00\\x00\\x50\\x00\\x06\\x00\"\n\"\\x07\\x00\\x00\\x00\\x79\\x00\\x00\\x00\\x78\\x00\\x00\\x00\\x78\\x00\\x00\\x00\\x78\\x00\\x00\\x00\\x0C\\x00\\x08\\x00\\x07\\x00\\x00\\x00\\x7A\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x2E\\x00\\x00\\x00\\x76\\x00\\x00\\x00\\x77\\x00\\x00\\x00\\x79\\x00\\x00\\x00\\xFE\\x00\\x02\\x00\\x7A\\x00\\x00\\x00\\x38\\x00\\x01\\x00\"\n\"\\x36\\x00\\x05\\x00\\x0D\\x00\\x00\\x00\\x11\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\\x37\\x00\\x03\\x00\\x0E\\x00\\x00\\x00\\x10\\x00\\x00\\x00\"\n\"\\xF8\\x00\\x02\\x00\\x12\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x08\\x00\\x00\\x00\\x83\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\xF7\\x00\\x03\\x00\\x80\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\\x7E\\x00\\x00\\x00\\x7F\\x00\\x00\\x00\\x80\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x7F\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x0D\\x00\\x00\\x00\\x81\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\xFE\\x00\\x02\\x00\\x81\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x80\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x0D\\x00\\x00\\x00\\x84\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x4F\\x00\\x08\\x00\\x07\\x00\\x00\\x00\\x85\\x00\\x00\\x00\\x84\\x00\\x00\\x00\\x84\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x83\\x00\\x00\\x00\\x85\\x00\\x00\\x00\\x39\\x00\\x05\\x00\\x07\\x00\\x00\\x00\"\n\"\\x86\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x83\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x87\\x00\\x00\\x00\\x86\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x88\\x00\\x00\\x00\\x86\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x89\\x00\\x00\\x00\"\n\"\\x86\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x50\\x00\\x07\\x00\\x0D\\x00\\x00\\x00\\x8A\\x00\\x00\\x00\\x87\\x00\\x00\\x00\\x88\\x00\\x00\\x00\\x89\\x00\\x00\\x00\"\n\"\\x68\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x2C\\x00\\x00\\x00\\x8B\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x35\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\"\n\"\\x8C\\x00\\x00\\x00\\x8B\\x00\\x00\\x00\\x8E\\x00\\x05\\x00\\x0D\\x00\\x00\\x00\\x8D\\x00\\x00\\x00\\x8A\\x00\\x00\\x00\\x8C\\x00\\x00\\x00\\xFE\\x00\\x02\\x00\"\n\"\\x8D\\x00\\x00\\x00\\x38\\x00\\x01\\x00\\x36\\x00\\x05\\x00\\x16\\x00\\x00\\x00\\x1C\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x18\\x00\\x00\\x00\\x37\\x00\\x03\\x00\"\n\"\\x15\\x00\\x00\\x00\\x19\\x00\\x00\\x00\\x37\\x00\\x03\\x00\\x17\\x00\\x00\\x00\\x1A\\x00\\x00\\x00\\x37\\x00\\x03\\x00\\x08\\x00\\x00\\x00\\x1B\\x00\\x00\\x00\"\n\"\\xF8\\x00\\x02\\x00\\x1D\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x08\\x00\\x00\\x00\\x93\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x08\\x00\\x00\\x00\"\n\"\\xA0\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x2C\\x00\\x00\\x00\\xAC\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x2C\\x00\\x00\\x00\"\n\"\\xC2\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x17\\x00\\x00\\x00\\xE9\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x17\\x00\\x00\\x00\"\n\"\\xF0\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\xF7\\x00\\x03\\x00\\x92\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\\x90\\x00\\x00\\x00\\x91\\x00\\x00\\x00\"\n\"\\xE4\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x91\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x94\\x00\\x00\\x00\\x1B\\x00\\x00\\x00\\x0C\\x00\\x06\\x00\"\n\"\\x07\\x00\\x00\\x00\\x95\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x45\\x00\\x00\\x00\\x94\\x00\\x00\\x00\\x4F\\x00\\x07\\x00\\x16\\x00\\x00\\x00\\x96\\x00\\x00\\x00\"\n\"\\x95\\x00\\x00\\x00\\x95\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x8E\\x00\\x05\\x00\\x16\\x00\\x00\\x00\\x98\\x00\\x00\\x00\\x96\\x00\\x00\\x00\"\n\"\\x97\\x00\\x00\\x00\\x8E\\x00\\x05\\x00\\x16\\x00\\x00\\x00\\x99\\x00\\x00\\x00\\x98\\x00\\x00\\x00\\x68\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x16\\x00\\x00\\x00\"\n\"\\x9C\\x00\\x00\\x00\\x99\\x00\\x00\\x00\\x9B\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x9D\\x00\\x00\\x00\\x9C\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x9E\\x00\\x00\\x00\\x9C\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x50\\x00\\x06\\x00\\x07\\x00\\x00\\x00\\x9F\\x00\\x00\\x00\"\n\"\\x9D\\x00\\x00\\x00\\x9E\\x00\\x00\\x00\\x9A\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x93\\x00\\x00\\x00\\x9F\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x16\\x00\\x00\\x00\"\n\"\\xA1\\x00\\x00\\x00\\x1A\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\xA2\\x00\\x00\\x00\\xA1\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x51\\x00\\x05\\x00\"\n\"\\x06\\x00\\x00\\x00\\xA3\\x00\\x00\\x00\\xA1\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x50\\x00\\x05\\x00\\x16\\x00\\x00\\x00\\xA4\\x00\\x00\\x00\\xA2\\x00\\x00\\x00\"\n\"\\xA3\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\xA5\\x00\\x00\\x00\\xA4\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\"\n\"\\xA6\\x00\\x00\\x00\\xA4\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x50\\x00\\x06\\x00\\x07\\x00\\x00\\x00\\xA7\\x00\\x00\\x00\\xA5\\x00\\x00\\x00\\xA6\\x00\\x00\\x00\"\n\"\\x68\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\xA0\\x00\\x00\\x00\\xA7\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\xA9\\x00\\x00\\x00\\x93\\x00\\x00\\x00\"\n\"\\x50\\x00\\x06\\x00\\x07\\x00\\x00\\x00\\xAA\\x00\\x00\\x00\\xA8\\x00\\x00\\x00\\xA8\\x00\\x00\\x00\\xA8\\x00\\x00\\x00\\x88\\x00\\x05\\x00\\x07\\x00\\x00\\x00\"\n\"\\xAB\\x00\\x00\\x00\\xA9\\x00\\x00\\x00\\xAA\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x93\\x00\\x00\\x00\\xAB\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\xAC\\x00\\x00\\x00\"\n\"\\x68\\x00\\x00\\x00\\xF9\\x00\\x02\\x00\\xAD\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\xAD\\x00\\x00\\x00\\xF6\\x00\\x04\\x00\\xAF\\x00\\x00\\x00\\xB0\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\xF9\\x00\\x02\\x00\\xB1\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\xB1\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\xB2\\x00\\x00\\x00\"\n\"\\xAC\\x00\\x00\\x00\\xB8\\x00\\x05\\x00\\x21\\x00\\x00\\x00\\xB3\\x00\\x00\\x00\\xB2\\x00\\x00\\x00\\xA8\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\\xB3\\x00\\x00\\x00\"\n\"\\xAE\\x00\\x00\\x00\\xAF\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\xAE\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\xB4\\x00\\x00\\x00\\x93\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x14\\x00\\x00\\x00\\xB5\\x00\\x00\\x00\\x19\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\xB6\\x00\\x00\\x00\\xA0\\x00\\x00\\x00\"\n\"\\x4F\\x00\\x07\\x00\\x16\\x00\\x00\\x00\\xB7\\x00\\x00\\x00\\xB6\\x00\\x00\\x00\\xB6\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x57\\x00\\x05\\x00\"\n\"\\x0D\\x00\\x00\\x00\\xB8\\x00\\x00\\x00\\xB5\\x00\\x00\\x00\\xB7\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\xB9\\x00\\x00\\x00\\xB8\\x00\\x00\\x00\"\n\"\\x03\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x2C\\x00\\x00\\x00\\xBA\\x00\\x00\\x00\\xA0\\x00\\x00\\x00\\x31\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\"\n\"\\xBB\\x00\\x00\\x00\\xBA\\x00\\x00\\x00\\x0C\\x00\\x07\\x00\\x06\\x00\\x00\\x00\\xBC\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x30\\x00\\x00\\x00\\xB9\\x00\\x00\\x00\"\n\"\\xBB\\x00\\x00\\x00\\x8E\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\xBD\\x00\\x00\\x00\\xB4\\x00\\x00\\x00\\xBC\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\"\n\"\\xBE\\x00\\x00\\x00\\xA0\\x00\\x00\\x00\\x81\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\xBF\\x00\\x00\\x00\\xBE\\x00\\x00\\x00\\xBD\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\"\n\"\\xA0\\x00\\x00\\x00\\xBF\\x00\\x00\\x00\\xF9\\x00\\x02\\x00\\xB0\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\xB0\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\"\n\"\\xC0\\x00\\x00\\x00\\xAC\\x00\\x00\\x00\\x81\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\xC1\\x00\\x00\\x00\\xC0\\x00\\x00\\x00\\x68\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\"\n\"\\xAC\\x00\\x00\\x00\\xC1\\x00\\x00\\x00\\xF9\\x00\\x02\\x00\\xAD\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\xAF\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\xAC\\x00\\x00\\x00\"\n\"\\x56\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\xC2\\x00\\x00\\x00\\x68\\x00\\x00\\x00\\xF9\\x00\\x02\\x00\\xC3\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\xC3\\x00\\x00\\x00\"\n\"\\xF6\\x00\\x04\\x00\\xC5\\x00\\x00\\x00\\xC6\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xF9\\x00\\x02\\x00\\xC7\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\xC7\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\xC8\\x00\\x00\\x00\\xAC\\x00\\x00\\x00\\xB8\\x00\\x05\\x00\\x21\\x00\\x00\\x00\\xCA\\x00\\x00\\x00\\xC8\\x00\\x00\\x00\"\n\"\\xC9\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\\xCA\\x00\\x00\\x00\\xC4\\x00\\x00\\x00\\xC5\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\xC4\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x07\\x00\\x00\\x00\\xCB\\x00\\x00\\x00\\x93\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x14\\x00\\x00\\x00\\xCC\\x00\\x00\\x00\\x19\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x07\\x00\\x00\\x00\\xCD\\x00\\x00\\x00\\xA0\\x00\\x00\\x00\\x4F\\x00\\x07\\x00\\x16\\x00\\x00\\x00\\xCE\\x00\\x00\\x00\\xCD\\x00\\x00\\x00\\xCD\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x57\\x00\\x05\\x00\\x0D\\x00\\x00\\x00\\xCF\\x00\\x00\\x00\\xCC\\x00\\x00\\x00\\xCE\\x00\\x00\\x00\\x51\\x00\\x05\\x00\"\n\"\\x06\\x00\\x00\\x00\\xD0\\x00\\x00\\x00\\xCF\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x2C\\x00\\x00\\x00\\xD1\\x00\\x00\\x00\\xA0\\x00\\x00\\x00\"\n\"\\x31\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\xD2\\x00\\x00\\x00\\xD1\\x00\\x00\\x00\\x0C\\x00\\x07\\x00\\x06\\x00\\x00\\x00\\xD3\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x30\\x00\\x00\\x00\\xD0\\x00\\x00\\x00\\xD2\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\xD4\\x00\\x00\\x00\\xC2\\x00\\x00\\x00\"\n\"\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\xD5\\x00\\x00\\x00\\xD3\\x00\\x00\\x00\\xD4\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\xD7\\x00\\x00\\x00\"\n\"\\xC2\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\xD8\\x00\\x00\\x00\\xD6\\x00\\x00\\x00\\xD7\\x00\\x00\\x00\\x83\\x00\\x05\\x00\\x06\\x00\\x00\\x00\"\n\"\\xD9\\x00\\x00\\x00\\xD5\\x00\\x00\\x00\\xD8\\x00\\x00\\x00\\x8E\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\xDA\\x00\\x00\\x00\\xCB\\x00\\x00\\x00\\xD9\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\xDB\\x00\\x00\\x00\\xA0\\x00\\x00\\x00\\x81\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\xDC\\x00\\x00\\x00\\xDB\\x00\\x00\\x00\"\n\"\\xDA\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\xA0\\x00\\x00\\x00\\xDC\\x00\\x00\\x00\\xF9\\x00\\x02\\x00\\xC6\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\xC6\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\xDD\\x00\\x00\\x00\\xAC\\x00\\x00\\x00\\x81\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\xDE\\x00\\x00\\x00\\xDD\\x00\\x00\\x00\"\n\"\\x68\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\xAC\\x00\\x00\\x00\\xDE\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\xDF\\x00\\x00\\x00\\xC2\\x00\\x00\\x00\"\n\"\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\xE0\\x00\\x00\\x00\\xDF\\x00\\x00\\x00\\xD6\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\xC2\\x00\\x00\\x00\\xE0\\x00\\x00\\x00\"\n\"\\xF9\\x00\\x02\\x00\\xC3\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\xC5\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\xE1\\x00\\x00\\x00\\xA0\\x00\\x00\\x00\"\n\"\\x4F\\x00\\x07\\x00\\x16\\x00\\x00\\x00\\xE2\\x00\\x00\\x00\\xE1\\x00\\x00\\x00\\xE1\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\xFE\\x00\\x02\\x00\"\n\"\\xE2\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\xE4\\x00\\x00\\x00\\xBA\\x00\\x05\\x00\\x21\\x00\\x00\\x00\\xE6\\x00\\x00\\x00\\xE5\\x00\\x00\\x00\\x56\\x00\\x00\\x00\"\n\"\\xF7\\x00\\x03\\x00\\xE8\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\\xE6\\x00\\x00\\x00\\xE7\\x00\\x00\\x00\\xE8\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\"\n\"\\xE7\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\xEA\\x00\\x00\\x00\\x1B\\x00\\x00\\x00\\x0C\\x00\\x06\\x00\\x07\\x00\\x00\\x00\\xEB\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x45\\x00\\x00\\x00\\xEA\\x00\\x00\\x00\\x4F\\x00\\x07\\x00\\x16\\x00\\x00\\x00\\xEC\\x00\\x00\\x00\\xEB\\x00\\x00\\x00\\xEB\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x8E\\x00\\x05\\x00\\x16\\x00\\x00\\x00\\xED\\x00\\x00\\x00\\xEC\\x00\\x00\\x00\\x97\\x00\\x00\\x00\\x8E\\x00\\x05\\x00\"\n\"\\x16\\x00\\x00\\x00\\xEE\\x00\\x00\\x00\\xED\\x00\\x00\\x00\\x68\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x16\\x00\\x00\\x00\\xEF\\x00\\x00\\x00\\xEE\\x00\\x00\\x00\"\n\"\\x9B\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\xE9\\x00\\x00\\x00\\xEF\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\xF1\\x00\\x00\\x00\\x1A\\x00\\x00\\x00\"\n\"\\x3E\\x00\\x03\\x00\\xF0\\x00\\x00\\x00\\xF1\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\xF2\\x00\\x00\\x00\\xE9\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x16\\x00\\x00\\x00\\xF3\\x00\\x00\\x00\\xF0\\x00\\x00\\x00\\x81\\x00\\x05\\x00\\x16\\x00\\x00\\x00\\xF4\\x00\\x00\\x00\\xF3\\x00\\x00\\x00\\xF2\\x00\\x00\\x00\"\n\"\\x3E\\x00\\x03\\x00\\xF0\\x00\\x00\\x00\\xF4\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\xF6\\x00\\x00\\x00\\xE9\\x00\\x00\\x00\\x8E\\x00\\x05\\x00\"\n\"\\x16\\x00\\x00\\x00\\xF7\\x00\\x00\\x00\\xF6\\x00\\x00\\x00\\xF5\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\xE9\\x00\\x00\\x00\\xF7\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x16\\x00\\x00\\x00\\xF8\\x00\\x00\\x00\\xE9\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x14\\x00\\x00\\x00\\xF9\\x00\\x00\\x00\\x19\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x16\\x00\\x00\\x00\\xFA\\x00\\x00\\x00\\xF0\\x00\\x00\\x00\\x57\\x00\\x05\\x00\\x0D\\x00\\x00\\x00\\xFB\\x00\\x00\\x00\\xF9\\x00\\x00\\x00\\xFA\\x00\\x00\\x00\"\n\"\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\xFC\\x00\\x00\\x00\\xFB\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x8E\\x00\\x05\\x00\\x16\\x00\\x00\\x00\\xFD\\x00\\x00\\x00\"\n\"\\xF8\\x00\\x00\\x00\\xFC\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\xFE\\x00\\x00\\x00\\xF0\\x00\\x00\\x00\\x83\\x00\\x05\\x00\\x16\\x00\\x00\\x00\"\n\"\\xFF\\x00\\x00\\x00\\xFE\\x00\\x00\\x00\\xFD\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\xF0\\x00\\x00\\x00\\xFF\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x16\\x00\\x00\\x00\"\n\"\\x00\\x01\\x00\\x00\\xE9\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x14\\x00\\x00\\x00\\x01\\x01\\x00\\x00\\x19\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x16\\x00\\x00\\x00\"\n\"\\x02\\x01\\x00\\x00\\xF0\\x00\\x00\\x00\\x57\\x00\\x05\\x00\\x0D\\x00\\x00\\x00\\x03\\x01\\x00\\x00\\x01\\x01\\x00\\x00\\x02\\x01\\x00\\x00\\x51\\x00\\x05\\x00\"\n\"\\x06\\x00\\x00\\x00\\x04\\x01\\x00\\x00\\x03\\x01\\x00\\x00\\x03\\x00\\x00\\x00\\x8E\\x00\\x05\\x00\\x16\\x00\\x00\\x00\\x05\\x01\\x00\\x00\\x00\\x01\\x00\\x00\"\n\"\\x04\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x06\\x01\\x00\\x00\\xF0\\x00\\x00\\x00\\x83\\x00\\x05\\x00\\x16\\x00\\x00\\x00\\x07\\x01\\x00\\x00\"\n\"\\x06\\x01\\x00\\x00\\x05\\x01\\x00\\x00\\x3E\\x00\\x03\\x00\\xF0\\x00\\x00\\x00\\x07\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x08\\x01\\x00\\x00\"\n\"\\xE9\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x14\\x00\\x00\\x00\\x09\\x01\\x00\\x00\\x19\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x0A\\x01\\x00\\x00\"\n\"\\xF0\\x00\\x00\\x00\\x57\\x00\\x05\\x00\\x0D\\x00\\x00\\x00\\x0B\\x01\\x00\\x00\\x09\\x01\\x00\\x00\\x0A\\x01\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\"\n\"\\x0C\\x01\\x00\\x00\\x0B\\x01\\x00\\x00\\x03\\x00\\x00\\x00\\x8E\\x00\\x05\\x00\\x16\\x00\\x00\\x00\\x0D\\x01\\x00\\x00\\x08\\x01\\x00\\x00\\x0C\\x01\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x0E\\x01\\x00\\x00\\xF0\\x00\\x00\\x00\\x83\\x00\\x05\\x00\\x16\\x00\\x00\\x00\\x0F\\x01\\x00\\x00\\x0E\\x01\\x00\\x00\"\n\"\\x0D\\x01\\x00\\x00\\x3E\\x00\\x03\\x00\\xF0\\x00\\x00\\x00\\x0F\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x10\\x01\\x00\\x00\\xF0\\x00\\x00\\x00\"\n\"\\xFE\\x00\\x02\\x00\\x10\\x01\\x00\\x00\\xF8\\x00\\x02\\x00\\xE8\\x00\\x00\\x00\\xF9\\x00\\x02\\x00\\x92\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x92\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x12\\x01\\x00\\x00\\x1A\\x00\\x00\\x00\\xFE\\x00\\x02\\x00\\x12\\x01\\x00\\x00\\x38\\x00\\x01\\x00\"},\n#endif\n#ifdef D3D9QUAKE\n{QR_DIRECT3D9, 9, \"defaultwall\",\n//!!ver 100 150\n//!!permu DELUXE\n\"!!permu FULLBRIGHT\\n\"\n\"!!permu FOG\\n\"\n//!!permu LIGHTSTYLED\n//!!permu BUMP\n//!!permu SPECULAR\n//!!permu REFLECTCUBEMASK\n//!!cvarf r_glsl_offsetmapping_scale\n//!!cvardf r_tessellation_level=5\n//!!samps diffuse lightmap specular normalmap fullbright reflectmask reflectcube paletted lightmap1 lightmap2 lightmap3\n\"!!samps diffuse fullbright lightmap\\n\"\n\n\"#undef SPECULAR\\n\"\n\n//#include \"sys/defs.h\"\n\"#define vec4 float4\\n\"\n\"#define vec3 float3\\n\"\n\"#define vec2 float2\\n\"\n\"#define texture2D tex2D\\n\"\n\"#define mat3 float3x3\\n\"\n\"#define mat4 float4x4\\n\"\n\"struct a2v\\n\"\n\"{\\n\"\n\"vec4 v_position : POSITION;\\n\"\n\"vec2 v_texcoord : TEXCOORD0;\\n\"\n\n\"#if defined(OFFSETMAPPING) || defined(SPECULAR) || defined(REFLECTCUBEMASK)\\n\"\n\"vec3 v_normal : NORMAL;\\n\"\n\"vec3 v_svector : TANGENT;\\n\"\n\"vec3 v_tvector : BINORMAL;\\n\"\n\"#endif\\n\"\n\"#ifdef VERTEXLIT\\n\"\n\"vec4 v_colour : COLOR0;\\n\"\n\"#else\\n\"\n\"vec2 v_lmcoord  : TEXCOORD1;\\n\"\n\"#endif\\n\"\n\"};\\n\"\n\"struct v2f\\n\"\n\"{\\n\"\n\"#ifndef FRAGMENT_SHADER\\n\"\n\"vec4 pos: POSITION;\\n\"\n\"#endif\\n\"\n\n\"#if defined(OFFSETMAPPING) || defined(SPECULAR) || defined(REFLECTCUBEMASK) || defined(FOG)\\n\"\n\"vec3 eyevector : TEXCOORD4;\\n\"\n\"#endif\\n\"\n\n\"#if defined(REFLECTCUBEMASK) || defined(BUMPMODELSPACE)\\n\"\n\"mat3 invsurface : TEXCOORD5;\\n\"\n\"#endif\\n\"\n\n\"#ifdef VERTEXLIT\\n\"\n\"vec2 tclm : TEXCOORD0;\\n\"\n\"#else\\n\"\n\"vec4 tclm : TEXCOORD0;\\n\"\n\"#endif\\n\"\n\"vec4 vc : COLOR0;\\n\"\n\"#ifndef VERTEXLIT\\n\"\n\"#ifdef LIGHTSTYLED\\n\"\n//we could use an offset, but that would still need to be per-surface which would break batches\n//fixme: merge attributes?\n\"vec2 lm1 : TEXCOORD1, lm2 : TEXCOORD2, lm3 : TEXCOORD3;\\n\"\n\"#endif\\n\"\n\"#endif\\n\"\n\"};\\n\"\n\n//this is what normally draws all of your walls, even with rtlights disabled\n//note that the '286' preset uses drawflat_walls instead.\n\n\"#include \\\"sys/fog.h\\\"\\n\"\n\n\"#ifdef VERTEX_SHADER\\n\"\n\"float4x4  m_modelviewprojection;\\n\"\n\"float4x4  m_modelview;\\n\"\n\"vec3 e_eyepos;\\n\"\n\"vec4 e_lmscale;\\n\"\n\"v2f main (a2v inp)\\n\"\n\"{\\n\"\n\"v2f outp;\\n\"\n\n\"#if defined(OFFSETMAPPING) || defined(SPECULAR) || defined(REFLECTCUBEMASK)\\n\"\n\"vec3 eyeminusvertex = e_eyepos - inp.v_position.xyz;\\n\"\n\"outp.eyevector.x = dot(eyeminusvertex, inp.v_svector.xyz);\\n\"\n\"outp.eyevector.y = dot(eyeminusvertex, inp.v_tvector.xyz);\\n\"\n\"outp.eyevector.z = dot(eyeminusvertex, inp.v_normal.xyz);\\n\"\n\"#elif defined(FOG)\\n\"\n\"outp.eyevector = mul(m_modelview, inp.v_position);\\n\"\n\"#endif\\n\"\n\"#if defined(REFLECTCUBEMASK) || defined(BUMPMODELSPACE)\\n\"\n\"outp.invsurface[0] = inp.v_svector;\\n\"\n\"outp.invsurface[1] = inp.v_tvector;\\n\"\n\"outp.invsurface[2] = inp.v_normal;\\n\"\n\"#endif\\n\"\n\"outp.tclm.xy = inp.v_texcoord;\\n\"\n\"#ifdef FLOW\\n\"\n//\toutp.tclm.x += e_time * -0.5;\n\"#endif\\n\"\n\"#ifdef VERTEXLIT\\n\"\n\"#ifdef LIGHTSTYLED\\n\"\n//FIXME, only one colour.\n\"outp.vc = inp.v_colour * e_lmscale[0];\\n\"\n\"#else\\n\"\n\"outp.vc = inp.v_colour * e_lmscale;\\n\"\n\"#endif\\n\"\n\"#else\\n\"\n\"outp.vc = e_lmscale;\\n\"\n\"outp.tclm.zw = inp.v_lmcoord;\\n\"\n\"#ifdef LIGHTSTYLED\\n\"\n\"outp.lm1 = inp.v_lmcoord2;\\n\"\n\"outp.lm2 = inp.v_lmcoord3;\\n\"\n\"outp.lm3 = inp.v_lmcoord4;\\n\"\n\"#endif\\n\"\n\"#endif\\n\"\n\"outp.pos = mul(m_modelviewprojection, inp.v_position);\\n\"\n\n\"return outp;\\n\"\n\"}\\n\"\n\"#endif\\n\"\n\n\n\n\n\n\n\n\n\n\n\"#ifdef FRAGMENT_SHADER\\n\"\n\"sampler s_diffuse  : register(s0);\\n\"\n//sampler s_normalmap;\n//sampler s_specular;\n//sampler s_upper;\n//sampler s_lower;\n\"sampler s_fullbright  : register(s1);\\n\"\n//sampler s_paletted;\n//sampler s_reflectcube;\n//sampler s_reflectmask;\n\"sampler s_lightmap  : register(s2);\\n\"\n//sampler s_deluxmap;\n\n\n//samplers\n\"#define s_colourmap s_t0\\n\"\n//sampler2D\ts_colourmap;\n\n//vec4 e_lmscale;\n\"vec4 e_colourident;\\n\"\n\n\"#ifdef OFFSETMAPPING\\n\"\n\"#include \\\"sys/offsetmapping.h\\\"\\n\"\n\"#endif\\n\"\n\"vec4 main (v2f inp) : COLOR0\\n\"\n\"{\\n\"\n\"vec4 gl_FragColor;\\n\"\n\"#define tc (inp.tclm.xy)\\n\"\n\"#define lm0 (inp.tclm.zw)\\n\"\n\n\n//adjust texture coords for offsetmapping\n\"#ifdef OFFSETMAPPING\\n\"\n\"vec2 tcoffsetmap = offsetmap(s_normalmap, tc, eyevector);\\n\"\n\"#define tc tcoffsetmap\\n\"\n\"#endif\\n\"\n\n\"#if defined(EIGHTBIT) && !defined(LIGHTSTYLED)\\n\"\n//optional: round the lightmap coords to ensure all pixels within a texel have different lighting values either. it just looks wrong otherwise.\n//don't bother if its lightstyled, such cases will have unpredictable correlations anyway.\n//FIXME: this rounding is likely not correct with respect to software rendering. oh well.\n\"#if __VERSION__ >= 130\\n\"\n\"vec2 lmsize = vec2(textureSize(s_lightmap0, 0));\\n\"\n\"#else\\n\"\n\"#define lmsize vec2(128.0,2048.0)\\n\"\n\"#endif\\n\"\n\"#define texelstolightmap (16.0)\\n\"\n\"vec2 lmcoord0 = floor(lm0 * lmsize*texelstolightmap)/(lmsize*texelstolightmap);\\n\"\n\"#define lm0 lmcoord0\\n\"\n\"#endif\\n\"\n\n\n//yay, regular texture!\n\"gl_FragColor = texture2D(s_diffuse, tc);\\n\"\n\n\"#if defined(BUMP) && (defined(DELUXE) || defined(SPECULAR) || defined(REFLECTCUBEMASK))\\n\"\n\"vec3 norm = normalize(texture2D(s_normalmap, tc).rgb - 0.5);\\n\"\n\"#elif defined(SPECULAR) || defined(DELUXE) || defined(REFLECTCUBEMASK)\\n\"\n\"vec3 norm = vec3(0, 0, 1); //specular lighting expects this to exist.\\n\"\n\"#endif\\n\"\n\n//modulate that by the lightmap(s) including deluxemap(s)\n\"#ifdef VERTEXLIT\\n\"\n\"#ifdef LIGHTSTYLED\\n\"\n\"vec3 lightmaps = inp.vc.rgb;\\n\"\n\"#else\\n\"\n\"vec3 lightmaps = inp.vc.rgb;\\n\"\n\"#endif\\n\"\n\"#define delux vec3(0.0,0.0,1.0)\\n\"\n\"#else\\n\"\n\"#ifdef LIGHTSTYLED\\n\"\n\"#error foobar\\n\"\n\"#define delux vec3(0.0,0.0,1.0)\\n\"\n\"vec3 lightmaps;\\n\"\n\"#ifdef DELUXE\\n\"\n\"lightmaps  = texture2D(s_lightmap0, lm0).rgb * e_lmscale[0].rgb * dot(norm, 2.0*texture2D(s_deluxmap0, lm0).rgb-0.5);\\n\"\n\"lightmaps += texture2D(s_lightmap1, lm1).rgb * e_lmscale[1].rgb * dot(norm, 2.0*texture2D(s_deluxmap1, lm1).rgb-0.5);\\n\"\n\"lightmaps += texture2D(s_lightmap2, lm2).rgb * e_lmscale[2].rgb * dot(norm, 2.0*texture2D(s_deluxmap2, lm2).rgb-0.5);\\n\"\n\"lightmaps += texture2D(s_lightmap3, lm3).rgb * e_lmscale[3].rgb * dot(norm, 2.0*texture2D(s_deluxmap3, lm3).rgb-0.5);\\n\"\n\"#else\\n\"\n\"lightmaps  = texture2D(s_lightmap0, lm0).rgb * e_lmscale[0].rgb;\\n\"\n\"lightmaps += texture2D(s_lightmap1, lm1).rgb * e_lmscale[1].rgb;\\n\"\n\"lightmaps += texture2D(s_lightmap2, lm2).rgb * e_lmscale[2].rgb;\\n\"\n\"lightmaps += texture2D(s_lightmap3, lm3).rgb * e_lmscale[3].rgb;\\n\"\n\"#endif\\n\"\n\"#else\\n\"\n\"vec3 lightmaps = texture2D(s_lightmap, lm0).rgb;\\n\"\n//modulate by the  bumpmap dot light\n\"#ifdef DELUXE\\n\"\n\"#error foobar\\n\"\n\"vec3 delux = (texture2D(s_deluxmap, lm0).rgb-0.5);\\n\"\n\"#ifdef BUMPMODELSPACE\\n\"\n\"delux = normalize(delux*invsurface);\\n\"\n\"#else\\n\"\n\"lightmaps *= 2.0 / max(0.25, delux.z); //counter the darkening from deluxmaps\\n\"\n\"#endif\\n\"\n\"lightmaps *= dot(norm, delux);\\n\"\n\"#else\\n\"\n\"#define delux vec3(0.0,0.0,1.0)\\n\"\n\"#endif\\n\"\n\"#endif\\n\"\n\"lightmaps *= inp.vc.rgb;\\n\"\n\"#endif\\n\"\n\n//add in specular, if applicable.\n\"#ifdef SPECULAR\\n\"\n\"vec4 specs = texture2D(s_specular, tc);\\n\"\n\"vec3 halfdir = normalize(normalize(eyevector) + delux); //this norm should be the deluxemap info instead\\n\"\n\"float spec = pow(max(dot(halfdir, norm), 0.0), FTE_SPECULAR_EXPONENT * specs.a);\\n\"\n\"spec *= FTE_SPECULAR_MULTIPLIER;\\n\"\n//NOTE: rtlights tend to have a *4 scaler here to over-emphasise the effect because it looks cool.\n//As not all maps will have deluxemapping, and the double-cos from the light util makes everything far too dark anyway,\n//we default to something that is not garish when the light value is directly infront of every single pixel.\n//we can justify this difference due to the rtlight editor etc showing the *4.\n\"gl_FragColor.rgb += spec * specs.rgb;\\n\"\n\"#endif\\n\"\n\n\"#ifdef REFLECTCUBEMASK\\n\"\n\"vec3 rtc = reflect(normalize(-eyevector), norm);\\n\"\n\"rtc = rtc.x*invsurface[0] + rtc.y*invsurface[1] + rtc.z*invsurface[2];\\n\"\n\"rtc = (m_model * vec4(rtc.xyz,0.0)).xyz;\\n\"\n\"gl_FragColor.rgb += texture2D(s_reflectmask, tc).rgb * textureCube(s_reflectcube, rtc).rgb;\\n\"\n\"#endif\\n\"\n\n\"#ifdef EIGHTBIT //FIXME: with this extra flag, half the permutations are redundant.\\n\"\n\"lightmaps *= 0.5; //counter the fact that the colourmap contains overbright values and logically ranges from 0 to 2 intead of to 1.\\n\"\n\"float pal = texture2D(s_paletted, tc).r; //the palette index. hopefully not interpolated.\\n\"\n\"lightmaps -= 1.0 / 128.0; //software rendering appears to round down, so make sure we favour the lower values instead of rounding to the nearest\\n\"\n\"gl_FragColor.r = texture2D(s_colourmap, vec2(pal, 1.0-lightmaps.r)).r; //do 3 lookups. this is to cope with lit files, would be a waste to not support those.\\n\"\n\"gl_FragColor.g = texture2D(s_colourmap, vec2(pal, 1.0-lightmaps.g)).g; //its not very softwarey, but re-palettizing is ugly.\\n\"\n\"gl_FragColor.b = texture2D(s_colourmap, vec2(pal, 1.0-lightmaps.b)).b; //without lits, it should be identical.\\n\"\n\"#else\\n\"\n//now we have our diffuse+specular terms, modulate by lightmap values.\n\"gl_FragColor.rgb *= lightmaps.rgb;\\n\"\n\n//add on the fullbright\n\"#ifdef FULLBRIGHT\\n\"\n\"vec4 fb = texture2D(s_fullbright, tc);\\n\"\n\"gl_FragColor.rgb += fb.rgb*fb.a;\\n\"\n\"#endif\\n\"\n\"#endif\\n\"\n\n//entity modifiers\n\"gl_FragColor = gl_FragColor * e_colourident;\\n\"\n\n\"#if defined(MASK)\\n\"\n\"#if defined(MASKLT)\\n\"\n\"if (gl_FragColor.a < MASK)\\n\"\n\"discard;\\n\"\n\"#else\\n\"\n\"if (gl_FragColor.a >= MASK)\\n\"\n\"discard;\\n\"\n\"#endif\\n\"\n\"gl_FragColor.a = 1.0; //alpha blending AND alpha testing usually looks stupid, plus it screws up our fog.\\n\"\n\"#endif\\n\"\n\n//and finally hide it all if we're fogged.\n\"#ifdef FOG\\n\"\n\"gl_FragColor = fog4(gl_FragColor, length(inp.eyevector));\\n\"\n\"#endif\\n\"\n\"return gl_FragColor;\\n\"\n\"}\\n\"\n\"#endif\\n\"\n\n},\n#endif\n#ifdef D3D11QUAKE\n{QR_DIRECT3D11, 11, \"defaultwall\",\n\"!!samps diffuse fullbright lightmap\\n\"\n\n\"struct a2v\\n\"\n\"{\\n\"\n\"float4 pos: POSITION;\\n\"\n\"float2 tc: TEXCOORD0;\\n\"\n\"float2 lmtc: TEXCOORD1;\\n\"\n\"};\\n\"\n\"struct v2f\\n\"\n\"{\\n\"\n\"float4 pos: SV_POSITION;\\n\"\n\"float2 tc: TEXCOORD0;\\n\"\n\"float2 lmtc: TEXCOORD1;\\n\"\n\"};\\n\"\n\n\"#include <ftedefs.h>\\n\"\n\n\"#ifdef VERTEX_SHADER\\n\"\n\"v2f main (a2v inp)\\n\"\n\"{\\n\"\n\"v2f outp;\\n\"\n\"outp.pos = mul(m_model, inp.pos);\\n\"\n\"outp.pos = mul(m_view, outp.pos);\\n\"\n\"outp.pos = mul(m_projection, outp.pos);\\n\"\n\"outp.tc = inp.tc;\\n\"\n\"outp.lmtc = inp.lmtc;\\n\"\n\"return outp;\\n\"\n\"}\\n\"\n\"#endif\\n\"\n\n\"#ifdef FRAGMENT_SHADER\\n\"\n\"Texture2D t_lightmap : register(t2);\\n\"\n\"SamplerState s_lightmap : register(s2);\\n\"\n\n\"Texture2D t_diffuse : register(t0);\\n\"\n\"SamplerState s_diffuse : register(s0);\\n\"\n\n\"Texture2D t_fullbright : register(t1);\\n\"\n\"SamplerState s_fullbright : register(s1);\\n\"\n\n\"float4 main (v2f inp) : SV_TARGET\\n\"\n\"{\\n\"\n\"float4 result;\\n\"\n\"result = t_diffuse.Sample(s_diffuse, inp.tc);\\n\"\n\"result.rgb *= t_lightmap.Sample(s_lightmap, inp.lmtc).rgb * e_lmscale[0].rgb;\\n\"\n\"float4 fb = t_fullbright.Sample(s_fullbright, inp.tc);\\n\"\n\"result.rgb += fb.rgb * fb.a;//lerp(result.rgb, fb.rgb, fb.a);\\n\"\n\"return result;\\n\"\n\"}\\n\"\n\"#endif\\n\"\n},\n#endif\n#ifdef GLQUAKE\n{QR_OPENGL, 110, \"defaultwarp\",\n\"!!ver 100 450\\n\"\n\"!!permu FOG\\n\"\n\"!!samps diffuse lightmap\\n\"\n\n\"#include \\\"sys/defs.h\\\"\\n\"\n\n//this is the shader that's responsible for drawing default q1 turbulant water surfaces\n//this is expected to be moderately fast.\n\n\"#include \\\"sys/fog.h\\\"\\n\"\n\n\"varying vec2 tc;\\n\"\n\"#ifdef LIT\\n\"\n\"varying vec2 lm0;\\n\"\n\"#endif\\n\"\n\"#ifdef VERTEX_SHADER\\n\"\n\"void main ()\\n\"\n\"{\\n\"\n\"tc = v_texcoord.st;\\n\"\n\"#ifdef FLOWV\\n\"\n\"tc.st += e_time * vec2(FLOWV);\\n\"\n\"#endif\\n\"\n\"#ifdef FLOW\\n\"\n\"tc.s += e_time * -0.5;\\n\"\n\"#endif\\n\"\n\"#ifdef LIT\\n\"\n\"lm0 = v_lmcoord;\\n\"\n\"#endif\\n\"\n\"gl_Position = ftetransform();\\n\"\n\"}\\n\"\n\"#endif\\n\"\n\"#ifdef FRAGMENT_SHADER\\n\"\n\"void main ()\\n\"\n\"{\\n\"\n\"vec2 ntc = tc + sin(tc.ts+e_time)*0.125;\\n\"\n\"vec3 ts = vec3(texture2D(s_diffuse, ntc));\\n\"\n\n\"#ifdef LIT\\n\"\n\"ts *= (texture2D(s_lightmap, lm0) * e_lmscale).rgb;\\n\"\n\"#endif\\n\"\n\n\"#ifdef ALPHA\\n\"\n\"gl_FragColor = fog4blend(vec4(ts, float(ALPHA)) * e_colourident);\\n\"\n\"#else\\n\"\n\"gl_FragColor = fog4(vec4(ts, 1.0) * e_colourident);\\n\"\n\"#endif\\n\"\n\"}\\n\"\n\"#endif\\n\"\n},\n#endif\n#ifdef VKQUAKE\n{QR_VULKAN, -1, \"defaultwarp\",\n\"\\xFF\\x53\\x50\\x56\\x01\\x00\\x00\\x00\\x04\\x80\\x00\\x00\\x00\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x2C\\x00\\x00\\x00\\x57\\x00\\x00\\x00\\x84\\x00\\x00\\x00\"\n\"\\x0C\\x0B\\x00\\x00\\x90\\x0B\\x00\\x00\\x68\\x14\\x00\\x00\\x01\\x00\\x66\\x31\\x72\\x5F\\x77\\x61\\x74\\x65\\x72\\x61\\x6C\\x70\\x68\\x61\\x00\\x3F\\x80\\x00\"\n\"\\x00\\x01\\x01\\x62\\x31\\x72\\x5F\\x66\\x6F\\x67\\x5F\\x65\\x78\\x70\\x32\\x00\\x00\\x00\\x00\\x00\\x01\\x02\\x62\\x31\\x72\\x5F\\x66\\x6F\\x67\\x5F\\x6C\\x69\"\n\"\\x6E\\x65\\x61\\x72\\x00\\x00\\x00\\x00\\x00\\x01\\x03\\x46\\x31\\x61\\x6C\\x70\\x68\\x61\\x00\\x00\\x00\\x00\\x00\\x01\\x04\\x42\\x31\\x6C\\x69\\x74\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x03\\x02\\x23\\x07\\x00\\x00\\x01\\x00\\x0A\\x00\\x08\\x00\\x54\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x11\\x00\\x02\\x00\\x01\\x00\\x00\\x00\"\n\"\\x0B\\x00\\x06\\x00\\x01\\x00\\x00\\x00\\x47\\x4C\\x53\\x4C\\x2E\\x73\\x74\\x64\\x2E\\x34\\x35\\x30\\x00\\x00\\x00\\x00\\x0E\\x00\\x03\\x00\\x00\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x0F\\x00\\x0F\\x00\\x00\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x6D\\x61\\x69\\x6E\\x00\\x00\\x00\\x00\\x1C\\x00\\x00\\x00\\x39\\x00\\x00\\x00\"\n\"\\x3B\\x00\\x00\\x00\\x3D\\x00\\x00\\x00\\x3E\\x00\\x00\\x00\\x42\\x00\\x00\\x00\\x50\\x00\\x00\\x00\\x51\\x00\\x00\\x00\\x52\\x00\\x00\\x00\\x53\\x00\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x11\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x13\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x05\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x13\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\"\n\"\\x48\\x00\\x04\\x00\\x13\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x80\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\"\n\"\\x03\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xC0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xCC\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xD0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\xDC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xE0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x13\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xEC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\xF0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x0A\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xFC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\"\n\"\\x0B\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x01\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x4C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x50\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x5C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x13\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x60\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x11\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x6C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x70\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\"\n\"\\x13\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x14\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x90\\x01\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x94\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x16\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x98\\x01\\x00\\x00\\x47\\x00\\x03\\x00\\x13\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x15\\x00\\x00\\x00\\x22\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x15\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x1C\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x39\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x3B\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x3D\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x3E\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\"\n\"\\x03\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x40\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x03\\x00\\x40\\x00\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x46\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\x47\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x01\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\x48\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x02\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\x49\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x03\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\x4A\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x04\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\x4B\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x15\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x4C\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x4C\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x4C\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x4C\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x4C\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x4C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x4C\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x50\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x4C\\x00\\x00\\x00\"\n\"\\x04\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x5C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x4C\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x60\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x4C\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x6C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x4C\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x70\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x4C\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x4C\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x88\\x00\\x00\\x00\\x47\\x00\\x03\\x00\\x4C\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\"\n\"\\x4E\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x4E\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x47\\x00\\x04\\x00\"\n\"\\x50\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x51\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x47\\x00\\x04\\x00\"\n\"\\x52\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x53\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x13\\x00\\x02\\x00\"\n\"\\x02\\x00\\x00\\x00\\x21\\x00\\x03\\x00\\x03\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x16\\x00\\x03\\x00\\x06\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x17\\x00\\x04\\x00\"\n\"\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x21\\x00\\x03\\x00\\x08\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x18\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x17\\x00\\x04\\x00\\x0E\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x15\\x00\\x04\\x00\\x0F\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x0F\\x00\\x00\\x00\"\n\"\\x10\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x1C\\x00\\x04\\x00\\x11\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x17\\x00\\x04\\x00\\x12\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x1E\\x00\\x19\\x00\\x13\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x11\\x00\\x00\\x00\"\n\"\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x14\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x13\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\"\n\"\\x14\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x15\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\"\n\"\\x16\\x00\\x00\\x00\\x17\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x18\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x20\\x00\\x04\\x00\"\n\"\\x1B\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\\x1C\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\"\n\"\\x06\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x00\\x00\\x80\\x3F\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x24\\x00\\x00\\x00\\x00\\x00\\x80\\xBF\\x2B\\x00\\x04\\x00\"\n\"\\x0F\\x00\\x00\\x00\\x25\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x26\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\"\n\"\\x0F\\x00\\x00\\x00\\x2B\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x0F\\x00\\x00\\x00\\x2E\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\"\n\"\\x06\\x00\\x00\\x00\\x32\\x00\\x00\\x00\\x00\\x00\\x00\\x40\\x20\\x00\\x04\\x00\\x38\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\"\n\"\\x38\\x00\\x00\\x00\\x39\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x3A\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\"\n\"\\x3A\\x00\\x00\\x00\\x3B\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x38\\x00\\x00\\x00\\x3D\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\"\n\"\\x3A\\x00\\x00\\x00\\x3E\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x1E\\x00\\x03\\x00\\x40\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x41\\x00\\x00\\x00\"\n\"\\x03\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x41\\x00\\x00\\x00\\x42\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x44\\x00\\x00\\x00\"\n\"\\x03\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x32\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x46\\x00\\x00\\x00\\x00\\x00\\x80\\x43\\x32\\x00\\x04\\x00\\x16\\x00\\x00\\x00\"\n\"\\x47\\x00\\x00\\x00\\x01\\x01\\x00\\x00\\x32\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x48\\x00\\x00\\x00\\x02\\x01\\x00\\x00\\x32\\x00\\x04\\x00\\x06\\x00\\x00\\x00\"\n\"\\x49\\x00\\x00\\x00\\x00\\x80\\x81\\x43\\x32\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x4A\\x00\\x00\\x00\\x04\\x01\\x00\\x00\\x32\\x00\\x04\\x00\\x16\\x00\\x00\\x00\"\n\"\\x4B\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x1E\\x00\\x0C\\x00\\x4C\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x4D\\x00\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\x4C\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x4D\\x00\\x00\\x00\\x4E\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x4F\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x4F\\x00\\x00\\x00\\x50\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\"\n\"\\x51\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\\x52\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\"\n\"\\x53\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x36\\x00\\x05\\x00\\x02\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\"\n\"\\x05\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x12\\x00\\x00\\x00\\x3C\\x00\\x00\\x00\\x3B\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x39\\x00\\x00\\x00\\x3C\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x12\\x00\\x00\\x00\\x3F\\x00\\x00\\x00\\x3E\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x3D\\x00\\x00\\x00\\x3F\\x00\\x00\\x00\\x39\\x00\\x04\\x00\"\n\"\\x07\\x00\\x00\\x00\\x43\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x44\\x00\\x00\\x00\\x45\\x00\\x00\\x00\\x42\\x00\\x00\\x00\\x17\\x00\\x00\\x00\"\n\"\\x3E\\x00\\x03\\x00\\x45\\x00\\x00\\x00\\x43\\x00\\x00\\x00\\xFD\\x00\\x01\\x00\\x38\\x00\\x01\\x00\\x36\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x09\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x0A\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x41\\x00\\x05\\x00\\x18\\x00\\x00\\x00\\x19\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x17\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\x1A\\x00\\x00\\x00\"\n\"\\x19\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0E\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x1C\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x1F\\x00\\x00\\x00\"\n\"\\x1D\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x51\\x00\\x05\\x00\"\n\"\\x06\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x50\\x00\\x07\\x00\\x07\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x1F\\x00\\x00\\x00\"\n\"\\x20\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x91\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x1A\\x00\\x00\\x00\\x22\\x00\\x00\\x00\"\n\"\\x3E\\x00\\x03\\x00\\x0C\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x26\\x00\\x00\\x00\\x27\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x25\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x28\\x00\\x00\\x00\\x27\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x29\\x00\\x00\\x00\\x28\\x00\\x00\\x00\"\n\"\\x24\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x26\\x00\\x00\\x00\\x2A\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x25\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x2A\\x00\\x00\\x00\"\n\"\\x29\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x26\\x00\\x00\\x00\\x2C\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x2B\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\"\n\"\\x2D\\x00\\x00\\x00\\x2C\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x26\\x00\\x00\\x00\\x2F\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x2E\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x06\\x00\\x00\\x00\\x30\\x00\\x00\\x00\\x2F\\x00\\x00\\x00\\x81\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x31\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\\x30\\x00\\x00\\x00\"\n\"\\x88\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x33\\x00\\x00\\x00\\x31\\x00\\x00\\x00\\x32\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x26\\x00\\x00\\x00\\x34\\x00\\x00\\x00\"\n\"\\x0C\\x00\\x00\\x00\\x2B\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x34\\x00\\x00\\x00\\x33\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x35\\x00\\x00\\x00\"\n\"\\x0C\\x00\\x00\\x00\\xFE\\x00\\x02\\x00\\x35\\x00\\x00\\x00\\x38\\x00\\x01\\x00\\x03\\x02\\x23\\x07\\x00\\x00\\x01\\x00\\x0A\\x00\\x08\\x00\\xCB\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x11\\x00\\x02\\x00\\x01\\x00\\x00\\x00\\x0B\\x00\\x06\\x00\\x01\\x00\\x00\\x00\\x47\\x4C\\x53\\x4C\\x2E\\x73\\x74\\x64\\x2E\\x34\\x35\\x30\"\n\"\\x00\\x00\\x00\\x00\\x0E\\x00\\x03\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x0F\\x00\\x09\\x00\\x04\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x6D\\x61\\x69\\x6E\"\n\"\\x00\\x00\\x00\\x00\\x24\\x00\\x00\\x00\\x89\\x00\\x00\\x00\\xA8\\x00\\x00\\x00\\xB7\\x00\\x00\\x00\\x10\\x00\\x03\\x00\\x04\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x14\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x1D\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x02\\x01\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x24\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x31\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x10\\x00\\x00\\x00\"\n\"\\x48\\x00\\x04\\x00\\x33\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x33\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x33\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x33\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x33\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x33\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x33\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x05\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x33\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x33\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x33\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xC0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x33\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xCC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x33\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\xD0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x33\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xDC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x33\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xE0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x33\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xEC\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x33\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xF0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x33\\x00\\x00\\x00\\x0A\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\xFC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x33\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x33\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x33\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x4C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x33\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x50\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x33\\x00\\x00\\x00\"\n\"\\x0F\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x5C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x33\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x60\\x01\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x33\\x00\\x00\\x00\\x11\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x6C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x33\\x00\\x00\\x00\\x12\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x70\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x33\\x00\\x00\\x00\\x13\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x01\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x33\\x00\\x00\\x00\\x14\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x90\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x33\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x94\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x33\\x00\\x00\\x00\\x16\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x98\\x01\\x00\\x00\\x47\\x00\\x03\\x00\\x33\\x00\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x35\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x35\\x00\\x00\\x00\\x21\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x52\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x01\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\x89\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x9A\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x9A\\x00\\x00\\x00\\x21\\x00\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\xA2\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x04\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\xA6\\x00\\x00\\x00\\x22\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\xA6\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\xA8\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\xB2\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x03\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\xB7\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\xC1\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x48\\x00\\x04\\x00\\xC8\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\xC8\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\xC8\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\xC8\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\xC8\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x4C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\xC8\\x00\\x00\\x00\\x03\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x50\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\xC8\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x5C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\xC8\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x60\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\xC8\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x6C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\xC8\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x70\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\xC8\\x00\\x00\\x00\"\n\"\\x08\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\xC8\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x88\\x00\\x00\\x00\"\n\"\\x47\\x00\\x03\\x00\\xC8\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\xCA\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\"\n\"\\xCA\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x13\\x00\\x02\\x00\\x02\\x00\\x00\\x00\\x21\\x00\\x03\\x00\\x03\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x16\\x00\\x03\\x00\\x06\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x17\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x20\\x00\\x04\\x00\"\n\"\\x08\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x21\\x00\\x04\\x00\\x09\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\x17\\x00\\x04\\x00\"\n\"\\x0D\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x0E\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x21\\x00\\x04\\x00\"\n\"\\x0F\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x15\\x00\\x04\\x00\\x13\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x32\\x00\\x04\\x00\"\n\"\\x13\\x00\\x00\\x00\\x14\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x13\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x14\\x00\\x02\\x00\"\n\"\\x16\\x00\\x00\\x00\\x34\\x00\\x06\\x00\\x16\\x00\\x00\\x00\\x17\\x00\\x00\\x00\\xAB\\x00\\x00\\x00\\x14\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x34\\x00\\x05\\x00\"\n\"\\x16\\x00\\x00\\x00\\x18\\x00\\x00\\x00\\xA8\\x00\\x00\\x00\\x17\\x00\\x00\\x00\\x32\\x00\\x04\\x00\\x13\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x02\\x01\\x00\\x00\"\n\"\\x34\\x00\\x06\\x00\\x16\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\xAB\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x21\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x23\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x23\\x00\\x00\\x00\"\n\"\\x24\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x15\\x00\\x04\\x00\\x25\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x25\\x00\\x00\\x00\"\n\"\\x26\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x27\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x25\\x00\\x00\\x00\"\n\"\\x2A\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x18\\x00\\x04\\x00\\x2F\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x25\\x00\\x00\\x00\"\n\"\\x30\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x1C\\x00\\x04\\x00\\x31\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x30\\x00\\x00\\x00\\x17\\x00\\x04\\x00\\x32\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x1E\\x00\\x19\\x00\\x33\\x00\\x00\\x00\\x2F\\x00\\x00\\x00\\x2F\\x00\\x00\\x00\\x2F\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x31\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x32\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x34\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x33\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\"\n\"\\x34\\x00\\x00\\x00\\x35\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x13\\x00\\x00\\x00\\x36\\x00\\x00\\x00\\x14\\x00\\x00\\x00\\x20\\x00\\x04\\x00\"\n\"\\x37\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x13\\x00\\x00\\x00\\x3E\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\"\n\"\\x06\\x00\\x00\\x00\\x4C\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x32\\x00\\x04\\x00\\x13\\x00\\x00\\x00\\x52\\x00\\x00\\x00\\x01\\x01\\x00\\x00\\x34\\x00\\x06\\x00\"\n\"\\x16\\x00\\x00\\x00\\x53\\x00\\x00\\x00\\xAB\\x00\\x00\\x00\\x52\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x5A\\x00\\x00\\x00\"\n\"\\x3B\\xAA\\xB8\\x3F\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x5E\\x00\\x00\\x00\\x00\\x00\\x80\\x3F\\x2B\\x00\\x04\\x00\\x13\\x00\\x00\\x00\\x5F\\x00\\x00\\x00\"\n\"\\x13\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x69\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x34\\x00\\x06\\x00\\x16\\x00\\x00\\x00\\x73\\x00\\x00\\x00\"\n\"\\xAB\\x00\\x00\\x00\\x14\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x34\\x00\\x05\\x00\\x16\\x00\\x00\\x00\\x74\\x00\\x00\\x00\\xA8\\x00\\x00\\x00\\x73\\x00\\x00\\x00\"\n\"\\x20\\x00\\x04\\x00\\x86\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x32\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x88\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x32\\x00\\x00\\x00\"\n\"\\x3B\\x00\\x04\\x00\\x88\\x00\\x00\\x00\\x89\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x13\\x00\\x00\\x00\\x8D\\x00\\x00\\x00\\x04\\x00\\x00\\x00\"\n\"\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x93\\x00\\x00\\x00\\x00\\x00\\x00\\x3E\\x19\\x00\\x09\\x00\\x97\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x1B\\x00\\x03\\x00\\x98\\x00\\x00\\x00\\x97\\x00\\x00\\x00\"\n\"\\x20\\x00\\x04\\x00\\x99\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x98\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x99\\x00\\x00\\x00\\x9A\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x32\\x00\\x04\\x00\\x13\\x00\\x00\\x00\\xA2\\x00\\x00\\x00\\x04\\x01\\x00\\x00\\x34\\x00\\x06\\x00\\x16\\x00\\x00\\x00\\xA3\\x00\\x00\\x00\\xAB\\x00\\x00\\x00\"\n\"\\xA2\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x99\\x00\\x00\\x00\\xA6\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x88\\x00\\x00\\x00\"\n\"\\xA8\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x13\\x00\\x00\\x00\\xAB\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x32\\x00\\x04\\x00\\x06\\x00\\x00\\x00\"\n\"\\xB2\\x00\\x00\\x00\\x00\\x80\\x81\\x43\\x20\\x00\\x04\\x00\\xB6\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\xB6\\x00\\x00\\x00\"\n\"\\xB7\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x32\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\xC1\\x00\\x00\\x00\\x00\\x00\\x80\\x43\\x1E\\x00\\x0C\\x00\\xC8\\x00\\x00\\x00\"\n\"\\x2F\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\"\n\"\\x32\\x00\\x00\\x00\\x32\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\xC9\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\xC8\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\xC9\\x00\\x00\\x00\"\n\"\\xCA\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x36\\x00\\x05\\x00\\x02\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\"\n\"\\x05\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x86\\x00\\x00\\x00\\x87\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x08\\x00\\x00\\x00\\x96\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x0E\\x00\\x00\\x00\\xBD\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x0E\\x00\\x00\\x00\\xC6\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x32\\x00\\x00\\x00\\x8A\\x00\\x00\\x00\\x89\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x32\\x00\\x00\\x00\\x8B\\x00\\x00\\x00\"\n\"\\x89\\x00\\x00\\x00\\x4F\\x00\\x07\\x00\\x32\\x00\\x00\\x00\\x8C\\x00\\x00\\x00\\x8B\\x00\\x00\\x00\\x8B\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x41\\x00\\x05\\x00\\x37\\x00\\x00\\x00\\x8E\\x00\\x00\\x00\\x35\\x00\\x00\\x00\\x8D\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x8F\\x00\\x00\\x00\"\n\"\\x8E\\x00\\x00\\x00\\x50\\x00\\x05\\x00\\x32\\x00\\x00\\x00\\x90\\x00\\x00\\x00\\x8F\\x00\\x00\\x00\\x8F\\x00\\x00\\x00\\x81\\x00\\x05\\x00\\x32\\x00\\x00\\x00\"\n\"\\x91\\x00\\x00\\x00\\x8C\\x00\\x00\\x00\\x90\\x00\\x00\\x00\\x0C\\x00\\x06\\x00\\x32\\x00\\x00\\x00\\x92\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\"\n\"\\x91\\x00\\x00\\x00\\x8E\\x00\\x05\\x00\\x32\\x00\\x00\\x00\\x94\\x00\\x00\\x00\\x92\\x00\\x00\\x00\\x93\\x00\\x00\\x00\\x81\\x00\\x05\\x00\\x32\\x00\\x00\\x00\"\n\"\\x95\\x00\\x00\\x00\\x8A\\x00\\x00\\x00\\x94\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x87\\x00\\x00\\x00\\x95\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x98\\x00\\x00\\x00\"\n\"\\x9B\\x00\\x00\\x00\\x9A\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x32\\x00\\x00\\x00\\x9C\\x00\\x00\\x00\\x87\\x00\\x00\\x00\\x57\\x00\\x05\\x00\\x0D\\x00\\x00\\x00\"\n\"\\x9D\\x00\\x00\\x00\\x9B\\x00\\x00\\x00\\x9C\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x9E\\x00\\x00\\x00\\x9D\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x9F\\x00\\x00\\x00\\x9D\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\xA0\\x00\\x00\\x00\"\n\"\\x9D\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x50\\x00\\x06\\x00\\x07\\x00\\x00\\x00\\xA1\\x00\\x00\\x00\\x9E\\x00\\x00\\x00\\x9F\\x00\\x00\\x00\\xA0\\x00\\x00\\x00\"\n\"\\x3E\\x00\\x03\\x00\\x96\\x00\\x00\\x00\\xA1\\x00\\x00\\x00\\xF7\\x00\\x03\\x00\\xA5\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\\xA3\\x00\\x00\\x00\"\n\"\\xA4\\x00\\x00\\x00\\xA5\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\xA4\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x98\\x00\\x00\\x00\\xA7\\x00\\x00\\x00\\xA6\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x32\\x00\\x00\\x00\\xA9\\x00\\x00\\x00\\xA8\\x00\\x00\\x00\\x57\\x00\\x05\\x00\\x0D\\x00\\x00\\x00\\xAA\\x00\\x00\\x00\\xA7\\x00\\x00\\x00\"\n\"\\xA9\\x00\\x00\\x00\\x41\\x00\\x06\\x00\\x69\\x00\\x00\\x00\\xAC\\x00\\x00\\x00\\x35\\x00\\x00\\x00\\xAB\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x0D\\x00\\x00\\x00\\xAD\\x00\\x00\\x00\\xAC\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x0D\\x00\\x00\\x00\\xAE\\x00\\x00\\x00\\xAA\\x00\\x00\\x00\\xAD\\x00\\x00\\x00\"\n\"\\x4F\\x00\\x08\\x00\\x07\\x00\\x00\\x00\\xAF\\x00\\x00\\x00\\xAE\\x00\\x00\\x00\\xAE\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\xB0\\x00\\x00\\x00\\x96\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\xB1\\x00\\x00\\x00\\xB0\\x00\\x00\\x00\"\n\"\\xAF\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x96\\x00\\x00\\x00\\xB1\\x00\\x00\\x00\\xF9\\x00\\x02\\x00\\xA5\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\xA5\\x00\\x00\\x00\"\n\"\\xB7\\x00\\x05\\x00\\x16\\x00\\x00\\x00\\xB3\\x00\\x00\\x00\\xB2\\x00\\x00\\x00\\x4C\\x00\\x00\\x00\\xF7\\x00\\x03\\x00\\xB5\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\xFA\\x00\\x04\\x00\\xB3\\x00\\x00\\x00\\xB4\\x00\\x00\\x00\\xBF\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\xB4\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\"\n\"\\xB8\\x00\\x00\\x00\\x96\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\xB9\\x00\\x00\\x00\\xB8\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x51\\x00\\x05\\x00\"\n\"\\x06\\x00\\x00\\x00\\xBA\\x00\\x00\\x00\\xB8\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\xBB\\x00\\x00\\x00\\xB8\\x00\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\x50\\x00\\x07\\x00\\x0D\\x00\\x00\\x00\\xBC\\x00\\x00\\x00\\xB9\\x00\\x00\\x00\\xBA\\x00\\x00\\x00\\xBB\\x00\\x00\\x00\\xB2\\x00\\x00\\x00\"\n\"\\x3E\\x00\\x03\\x00\\xBD\\x00\\x00\\x00\\xBC\\x00\\x00\\x00\\x39\\x00\\x05\\x00\\x0D\\x00\\x00\\x00\\xBE\\x00\\x00\\x00\\x11\\x00\\x00\\x00\\xBD\\x00\\x00\\x00\"\n\"\\x3E\\x00\\x03\\x00\\xB7\\x00\\x00\\x00\\xBE\\x00\\x00\\x00\\xF9\\x00\\x02\\x00\\xB5\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\xBF\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x07\\x00\\x00\\x00\\xC0\\x00\\x00\\x00\\x96\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\xC2\\x00\\x00\\x00\\xC0\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\xC3\\x00\\x00\\x00\\xC0\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\xC4\\x00\\x00\\x00\"\n\"\\xC0\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x50\\x00\\x07\\x00\\x0D\\x00\\x00\\x00\\xC5\\x00\\x00\\x00\\xC2\\x00\\x00\\x00\\xC3\\x00\\x00\\x00\\xC4\\x00\\x00\\x00\"\n\"\\xC1\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\xC6\\x00\\x00\\x00\\xC5\\x00\\x00\\x00\\x39\\x00\\x05\\x00\\x0D\\x00\\x00\\x00\\xC7\\x00\\x00\\x00\\x11\\x00\\x00\\x00\"\n\"\\xC6\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\xB7\\x00\\x00\\x00\\xC7\\x00\\x00\\x00\\xF9\\x00\\x02\\x00\\xB5\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\xB5\\x00\\x00\\x00\"\n\"\\xFD\\x00\\x01\\x00\\x38\\x00\\x01\\x00\\x36\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x37\\x00\\x03\\x00\"\n\"\\x08\\x00\\x00\\x00\\x0A\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x0C\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x21\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x3B\\x00\\x04\\x00\\x21\\x00\\x00\\x00\\x2E\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\xF7\\x00\\x03\\x00\\x1A\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\"\n\"\\x18\\x00\\x00\\x00\\x19\\x00\\x00\\x00\\x1A\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x19\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x1B\\x00\\x00\\x00\"\n\"\\x0A\\x00\\x00\\x00\\xFE\\x00\\x02\\x00\\x1B\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x1A\\x00\\x00\\x00\\xF7\\x00\\x03\\x00\\x20\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\xFA\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\\x1F\\x00\\x00\\x00\\x43\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x1F\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x27\\x00\\x00\\x00\"\n\"\\x28\\x00\\x00\\x00\\x24\\x00\\x00\\x00\\x26\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x29\\x00\\x00\\x00\\x28\\x00\\x00\\x00\\x41\\x00\\x05\\x00\"\n\"\\x27\\x00\\x00\\x00\\x2B\\x00\\x00\\x00\\x24\\x00\\x00\\x00\\x2A\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x2C\\x00\\x00\\x00\\x2B\\x00\\x00\\x00\"\n\"\\x88\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\\x29\\x00\\x00\\x00\\x2C\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x22\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\"\n\"\\x41\\x00\\x05\\x00\\x37\\x00\\x00\\x00\\x38\\x00\\x00\\x00\\x35\\x00\\x00\\x00\\x36\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x39\\x00\\x00\\x00\"\n\"\\x38\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x3A\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x83\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x3B\\x00\\x00\\x00\"\n\"\\x39\\x00\\x00\\x00\\x3A\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x37\\x00\\x00\\x00\\x3C\\x00\\x00\\x00\\x35\\x00\\x00\\x00\\x36\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x06\\x00\\x00\\x00\\x3D\\x00\\x00\\x00\\x3C\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x37\\x00\\x00\\x00\\x3F\\x00\\x00\\x00\\x35\\x00\\x00\\x00\\x3E\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x3F\\x00\\x00\\x00\\x83\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x41\\x00\\x00\\x00\\x3D\\x00\\x00\\x00\"\n\"\\x40\\x00\\x00\\x00\\x88\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x42\\x00\\x00\\x00\\x3B\\x00\\x00\\x00\\x41\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x2E\\x00\\x00\\x00\"\n\"\\x42\\x00\\x00\\x00\\xF9\\x00\\x02\\x00\\x20\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x43\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x37\\x00\\x00\\x00\\x44\\x00\\x00\\x00\"\n\"\\x35\\x00\\x00\\x00\\x36\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x45\\x00\\x00\\x00\\x44\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x27\\x00\\x00\\x00\"\n\"\\x46\\x00\\x00\\x00\\x24\\x00\\x00\\x00\\x26\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x47\\x00\\x00\\x00\\x46\\x00\\x00\\x00\\x85\\x00\\x05\\x00\"\n\"\\x06\\x00\\x00\\x00\\x48\\x00\\x00\\x00\\x45\\x00\\x00\\x00\\x47\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x27\\x00\\x00\\x00\\x49\\x00\\x00\\x00\\x24\\x00\\x00\\x00\"\n\"\\x2A\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x4A\\x00\\x00\\x00\\x49\\x00\\x00\\x00\\x88\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x4B\\x00\\x00\\x00\"\n\"\\x48\\x00\\x00\\x00\\x4A\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x22\\x00\\x00\\x00\\x4B\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x4D\\x00\\x00\\x00\"\n\"\\x22\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x37\\x00\\x00\\x00\\x4E\\x00\\x00\\x00\\x35\\x00\\x00\\x00\\x3E\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\"\n\"\\x4F\\x00\\x00\\x00\\x4E\\x00\\x00\\x00\\x83\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x50\\x00\\x00\\x00\\x4D\\x00\\x00\\x00\\x4F\\x00\\x00\\x00\\x0C\\x00\\x07\\x00\"\n\"\\x06\\x00\\x00\\x00\\x51\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x28\\x00\\x00\\x00\\x4C\\x00\\x00\\x00\\x50\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x22\\x00\\x00\\x00\"\n\"\\x51\\x00\\x00\\x00\\xF7\\x00\\x03\\x00\\x55\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\\x53\\x00\\x00\\x00\\x54\\x00\\x00\\x00\\x55\\x00\\x00\\x00\"\n\"\\xF8\\x00\\x02\\x00\\x54\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x56\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\"\n\"\\x57\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x58\\x00\\x00\\x00\\x57\\x00\\x00\\x00\\x56\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\"\n\"\\x22\\x00\\x00\\x00\\x58\\x00\\x00\\x00\\xF9\\x00\\x02\\x00\\x55\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x55\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\"\n\"\\x59\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x5B\\x00\\x00\\x00\\x59\\x00\\x00\\x00\\x5A\\x00\\x00\\x00\\x7F\\x00\\x04\\x00\"\n\"\\x06\\x00\\x00\\x00\\x5C\\x00\\x00\\x00\\x5B\\x00\\x00\\x00\\x0C\\x00\\x06\\x00\\x06\\x00\\x00\\x00\\x5D\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\"\n\"\\x5C\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x2E\\x00\\x00\\x00\\x5D\\x00\\x00\\x00\\xF9\\x00\\x02\\x00\\x20\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x20\\x00\\x00\\x00\"\n\"\\x41\\x00\\x06\\x00\\x37\\x00\\x00\\x00\\x60\\x00\\x00\\x00\\x35\\x00\\x00\\x00\\x5F\\x00\\x00\\x00\\x2A\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\"\n\"\\x61\\x00\\x00\\x00\\x60\\x00\\x00\\x00\\x83\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x62\\x00\\x00\\x00\\x5E\\x00\\x00\\x00\\x61\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x06\\x00\\x00\\x00\\x63\\x00\\x00\\x00\\x2E\\x00\\x00\\x00\\x0C\\x00\\x08\\x00\\x06\\x00\\x00\\x00\\x64\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x2B\\x00\\x00\\x00\"\n\"\\x63\\x00\\x00\\x00\\x4C\\x00\\x00\\x00\\x5E\\x00\\x00\\x00\\x41\\x00\\x06\\x00\\x37\\x00\\x00\\x00\\x65\\x00\\x00\\x00\\x35\\x00\\x00\\x00\\x5F\\x00\\x00\\x00\"\n\"\\x2A\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x66\\x00\\x00\\x00\\x65\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x67\\x00\\x00\\x00\"\n\"\\x64\\x00\\x00\\x00\\x66\\x00\\x00\\x00\\x81\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x68\\x00\\x00\\x00\\x62\\x00\\x00\\x00\\x67\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\"\n\"\\x2E\\x00\\x00\\x00\\x68\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x69\\x00\\x00\\x00\\x6A\\x00\\x00\\x00\\x35\\x00\\x00\\x00\\x5F\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x0D\\x00\\x00\\x00\\x6B\\x00\\x00\\x00\\x6A\\x00\\x00\\x00\\x4F\\x00\\x08\\x00\\x07\\x00\\x00\\x00\\x6C\\x00\\x00\\x00\\x6B\\x00\\x00\\x00\\x6B\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x6D\\x00\\x00\\x00\\x0A\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x06\\x00\\x00\\x00\\x6E\\x00\\x00\\x00\\x2E\\x00\\x00\\x00\\x50\\x00\\x06\\x00\\x07\\x00\\x00\\x00\\x6F\\x00\\x00\\x00\\x6E\\x00\\x00\\x00\\x6E\\x00\\x00\\x00\"\n\"\\x6E\\x00\\x00\\x00\\x0C\\x00\\x08\\x00\\x07\\x00\\x00\\x00\\x70\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x2E\\x00\\x00\\x00\\x6C\\x00\\x00\\x00\\x6D\\x00\\x00\\x00\"\n\"\\x6F\\x00\\x00\\x00\\xFE\\x00\\x02\\x00\\x70\\x00\\x00\\x00\\x38\\x00\\x01\\x00\\x36\\x00\\x05\\x00\\x0D\\x00\\x00\\x00\\x11\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x0F\\x00\\x00\\x00\\x37\\x00\\x03\\x00\\x0E\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x12\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x08\\x00\\x00\\x00\"\n\"\\x79\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\xF7\\x00\\x03\\x00\\x76\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\\x74\\x00\\x00\\x00\\x75\\x00\\x00\\x00\"\n\"\\x76\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x75\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\x77\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\xFE\\x00\\x02\\x00\"\n\"\\x77\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x76\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\x7A\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x4F\\x00\\x08\\x00\"\n\"\\x07\\x00\\x00\\x00\\x7B\\x00\\x00\\x00\\x7A\\x00\\x00\\x00\\x7A\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\"\n\"\\x79\\x00\\x00\\x00\\x7B\\x00\\x00\\x00\\x39\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x7C\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x79\\x00\\x00\\x00\\x51\\x00\\x05\\x00\"\n\"\\x06\\x00\\x00\\x00\\x7D\\x00\\x00\\x00\\x7C\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x7E\\x00\\x00\\x00\\x7C\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x7F\\x00\\x00\\x00\\x7C\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x50\\x00\\x07\\x00\\x0D\\x00\\x00\\x00\"\n\"\\x80\\x00\\x00\\x00\\x7D\\x00\\x00\\x00\\x7E\\x00\\x00\\x00\\x7F\\x00\\x00\\x00\\x5E\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x21\\x00\\x00\\x00\\x81\\x00\\x00\\x00\"\n\"\\x10\\x00\\x00\\x00\\x2A\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x82\\x00\\x00\\x00\\x81\\x00\\x00\\x00\\x8E\\x00\\x05\\x00\\x0D\\x00\\x00\\x00\"\n\"\\x83\\x00\\x00\\x00\\x80\\x00\\x00\\x00\\x82\\x00\\x00\\x00\\xFE\\x00\\x02\\x00\\x83\\x00\\x00\\x00\\x38\\x00\\x01\\x00\"},\n#endif\n#ifdef D3D9QUAKE\n{QR_DIRECT3D9, 9, \"defaultwarp\",\n\"!!cvarf r_wateralpha\\n\"\n\"!!samps diffuse\\n\"\n\n\"struct a2v {\\n\"\n\"float4 pos: POSITION;\\n\"\n\"float2 tc: TEXCOORD0;\\n\"\n\"};\\n\"\n\"struct v2f {\\n\"\n\"#ifndef FRAGMENT_SHADER\\n\"\n\"float4 pos: POSITION;\\n\"\n\"#endif\\n\"\n\"float2 tc: TEXCOORD0;\\n\"\n\"};\\n\"\n\"#ifdef VERTEX_SHADER\\n\"\n\"float4x4  m_modelviewprojection;\\n\"\n\"v2f main (a2v inp)\\n\"\n\"{\\n\"\n\"v2f outp;\\n\"\n\"outp.pos = mul(m_modelviewprojection, inp.pos);\\n\"\n\"outp.tc = inp.tc;\\n\"\n\"return outp;\\n\"\n\"}\\n\"\n\"#endif\\n\"\n\n\"#ifdef FRAGMENT_SHADER\\n\"\n\"float cvar_r_wateralpha;\\n\"\n\"float e_time;\\n\"\n\"sampler s_diffuse;\\n\"\n\"float4 main (v2f inp) : COLOR0\\n\"\n\"{\\n\"\n\"float2 ntc = inp.tc + sin(inp.tc.yx+e_time)*0.125;\\n\"\n\"float3 ts = tex2D(s_diffuse, ntc).xyz;\\n\"\n\n\"#ifdef ALPHA\\n\"\n\"return float4(ts, float(ALPHA));\\n\"\n\"#else\\n\"\n\"return float4(ts, cvar_r_wateralpha);\\n\"\n\"#endif\\n\"\n\"}\\n\"\n\"#endif\\n\"\n},\n#endif\n#ifdef D3D11QUAKE\n{QR_DIRECT3D11, 11, \"defaultwarp\",\n\"!!samps diffuse\\n\"\n//!!cvarf r_wateralpha\n\n\"struct a2v\\n\"\n\"{\\n\"\n\"float4 pos: POSITION;\\n\"\n\"float2 tc: TEXCOORD0;\\n\"\n\"};\\n\"\n\"struct v2f\\n\"\n\"{\\n\"\n\"float4 pos: SV_POSITION;\\n\"\n\"float2 tc: TEXCOORD0;\\n\"\n\"};\\n\"\n\n\"#include <ftedefs.h>\\n\"\n\n\"#ifdef VERTEX_SHADER\\n\"\n\"v2f main (a2v inp)\\n\"\n\"{\\n\"\n\"v2f outp;\\n\"\n\"outp.pos = mul(m_model, inp.pos);\\n\"\n\"outp.pos = mul(m_view, outp.pos);\\n\"\n\"outp.pos = mul(m_projection, outp.pos);\\n\"\n\"outp.tc = inp.tc;\\n\"\n\"return outp;\\n\"\n\"}\\n\"\n\"#endif\\n\"\n\n\"#ifdef FRAGMENT_SHADER\\n\"\n//\tfloat cvar_r_wateralpha;\n//\tfloat e_time;\n\"SamplerState s_diffuse;\\n\"\n\"Texture2D t_diffuse;\\n\"\n\"float4 main (v2f inp) : SV_TARGET\\n\"\n\"{\\n\"\n\"float2 ntc = inp.tc + sin(inp.tc.yx+e_time)*0.125;\\n\"\n\"float4 r;\\n\"\n\"r = t_diffuse.Sample(s_diffuse, ntc);\\n\"\n\"#ifdef ALPHA\\n\"\n\"r.a = float(ALPHA);\\n\"\n\"#else\\n\"\n//\t\tr.a *= r_wateralpha;\n\"#endif\\n\"\n\"return r;\\n\"\n\"}\\n\"\n\"#endif\\n\"\n},\n#endif\n#ifdef GLQUAKE\n{QR_OPENGL, 110, \"defaultgammacb\",\n\"!!ver 100-450\\n\"\n\"!!samps 1\\n\"\n//this shader is applies gamma/contrast/brightness to the source image, and dumps it out.\n\n\"varying vec2 tc;\\n\"\n\"varying vec4 vc; //gamma, contrast, brightness, contrastboost\\n\"\n\n\"#ifdef VERTEX_SHADER\\n\"\n\"attribute vec2 v_texcoord;\\n\"\n\"attribute vec4 v_colour;\\n\"\n\"void main ()\\n\"\n\"{\\n\"\n\"tc = vec2(v_texcoord.s, 1.0-v_texcoord.t);\\n\"\n\"vc = v_colour;\\n\"\n\"gl_Position = ftetransform();\\n\"\n\"}\\n\"\n\"#endif\\n\"\n\"#ifdef FRAGMENT_SHADER\\n\"\n\"void main ()\\n\"\n\"{\\n\"\n\"vec3 t = texture2D(s_t0, tc).rgb;\\n\"\n\"t = vc.a * t/((vc.a-1.0)*t + 1.0);\\n\"\n\"gl_FragColor = vec4(pow(t, vec3(vc.r))*vc.g + vc.b, 1.0);\\n\"\n\"}\\n\"\n\"#endif\\n\"\n},\n#endif\n#ifdef VKQUAKE\n{QR_VULKAN, -1, \"defaultgammacb\",\n\"\\xFF\\x53\\x50\\x56\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x2C\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x2C\\x00\\x00\\x00\"\n\"\\x4C\\x0A\\x00\\x00\\x78\\x0A\\x00\\x00\\xE8\\x07\\x00\\x00\\x03\\x02\\x23\\x07\\x00\\x00\\x01\\x00\\x0A\\x00\\x08\\x00\\x4E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x11\\x00\\x02\\x00\\x01\\x00\\x00\\x00\\x0B\\x00\\x06\\x00\\x01\\x00\\x00\\x00\\x47\\x4C\\x53\\x4C\\x2E\\x73\\x74\\x64\\x2E\\x34\\x35\\x30\\x00\\x00\\x00\\x00\"\n\"\\x0E\\x00\\x03\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x0F\\x00\\x0F\\x00\\x00\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x6D\\x61\\x69\\x6E\\x00\\x00\\x00\\x00\"\n\"\\x1C\\x00\\x00\\x00\\x39\\x00\\x00\\x00\\x3B\\x00\\x00\\x00\\x3E\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x44\\x00\\x00\\x00\\x4A\\x00\\x00\\x00\\x4B\\x00\\x00\\x00\"\n\"\\x4C\\x00\\x00\\x00\\x4D\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x11\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x13\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x13\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x13\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x05\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x13\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xC0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x04\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\xCC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xD0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x13\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xDC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\xE0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xEC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\"\n\"\\x09\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xF0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x0A\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xFC\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x40\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x4C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x13\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x50\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x5C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x60\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\"\n\"\\x11\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x6C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x70\\x01\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x13\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x14\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x90\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x94\\x01\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x13\\x00\\x00\\x00\\x16\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x98\\x01\\x00\\x00\\x47\\x00\\x03\\x00\\x13\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\"\n\"\\x15\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x15\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\"\n\"\\x1C\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x39\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\"\n\"\\x3B\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x3E\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x47\\x00\\x04\\x00\"\n\"\\x40\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x42\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x47\\x00\\x03\\x00\\x42\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x47\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x47\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x47\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x10\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x47\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x47\\x00\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x4C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x47\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x50\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x47\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x5C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x47\\x00\\x00\\x00\\x05\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x60\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x47\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x6C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x47\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x70\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x47\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x80\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x47\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x88\\x00\\x00\\x00\\x47\\x00\\x03\\x00\\x47\\x00\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x49\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x49\\x00\\x00\\x00\\x21\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x4A\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x4B\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\"\n\"\\x04\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x4C\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x4D\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x13\\x00\\x02\\x00\\x02\\x00\\x00\\x00\\x21\\x00\\x03\\x00\\x03\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x16\\x00\\x03\\x00\\x06\\x00\\x00\\x00\"\n\"\\x20\\x00\\x00\\x00\\x17\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x21\\x00\\x03\\x00\\x08\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x20\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x18\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x04\\x00\\x00\\x00\"\n\"\\x17\\x00\\x04\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x15\\x00\\x04\\x00\\x0F\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x2B\\x00\\x04\\x00\\x0F\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x1C\\x00\\x04\\x00\\x11\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\"\n\"\\x17\\x00\\x04\\x00\\x12\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x1E\\x00\\x19\\x00\\x13\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\"\n\"\\x0D\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x11\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x14\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x13\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x14\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x15\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x20\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x17\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x18\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x0D\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\\x1C\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x00\\x00\\x80\\x3F\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x24\\x00\\x00\\x00\"\n\"\\x00\\x00\\x80\\xBF\\x2B\\x00\\x04\\x00\\x0F\\x00\\x00\\x00\\x25\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x26\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x0F\\x00\\x00\\x00\\x2B\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x0F\\x00\\x00\\x00\\x2E\\x00\\x00\\x00\"\n\"\\x03\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x32\\x00\\x00\\x00\\x00\\x00\\x00\\x40\\x20\\x00\\x04\\x00\\x38\\x00\\x00\\x00\\x03\\x00\\x00\\x00\"\n\"\\x12\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x38\\x00\\x00\\x00\\x39\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x3A\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x12\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x3A\\x00\\x00\\x00\\x3B\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x3D\\x00\\x00\\x00\\x03\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x3D\\x00\\x00\\x00\\x3E\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x3F\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x3F\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x1E\\x00\\x03\\x00\\x42\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x20\\x00\\x04\\x00\\x43\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x42\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x43\\x00\\x00\\x00\\x44\\x00\\x00\\x00\\x03\\x00\\x00\\x00\"\n\"\\x1E\\x00\\x0C\\x00\\x47\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x48\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x00\\x00\"\n\"\\x3B\\x00\\x04\\x00\\x48\\x00\\x00\\x00\\x49\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x3A\\x00\\x00\\x00\\x4A\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x3B\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\\x4B\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\\x4C\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x3B\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\\x4D\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x36\\x00\\x05\\x00\\x02\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x03\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x05\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x12\\x00\\x00\\x00\\x3C\\x00\\x00\\x00\\x3B\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\"\n\"\\x39\\x00\\x00\\x00\\x3C\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x41\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x3E\\x00\\x00\\x00\"\n\"\\x41\\x00\\x00\\x00\\x39\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x45\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x3D\\x00\\x00\\x00\\x46\\x00\\x00\\x00\"\n\"\\x44\\x00\\x00\\x00\\x17\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x46\\x00\\x00\\x00\\x45\\x00\\x00\\x00\\xFD\\x00\\x01\\x00\\x38\\x00\\x01\\x00\\x36\\x00\\x05\\x00\"\n\"\\x07\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x0A\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\"\n\"\\x0C\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x18\\x00\\x00\\x00\\x19\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x17\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x0D\\x00\\x00\\x00\\x1A\\x00\\x00\\x00\\x19\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0E\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x1C\\x00\\x00\\x00\\x51\\x00\\x05\\x00\"\n\"\\x06\\x00\\x00\\x00\\x1F\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x50\\x00\\x07\\x00\\x07\\x00\\x00\\x00\"\n\"\\x22\\x00\\x00\\x00\\x1F\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x91\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x1A\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x0C\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x26\\x00\\x00\\x00\\x27\\x00\\x00\\x00\"\n\"\\x0C\\x00\\x00\\x00\\x25\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x28\\x00\\x00\\x00\\x27\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\"\n\"\\x29\\x00\\x00\\x00\\x28\\x00\\x00\\x00\\x24\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x26\\x00\\x00\\x00\\x2A\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x25\\x00\\x00\\x00\"\n\"\\x3E\\x00\\x03\\x00\\x2A\\x00\\x00\\x00\\x29\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x26\\x00\\x00\\x00\\x2C\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x2B\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\\x2C\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x26\\x00\\x00\\x00\\x2F\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\"\n\"\\x2E\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x30\\x00\\x00\\x00\\x2F\\x00\\x00\\x00\\x81\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x31\\x00\\x00\\x00\"\n\"\\x2D\\x00\\x00\\x00\\x30\\x00\\x00\\x00\\x88\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x33\\x00\\x00\\x00\\x31\\x00\\x00\\x00\\x32\\x00\\x00\\x00\\x41\\x00\\x05\\x00\"\n\"\\x26\\x00\\x00\\x00\\x34\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x2B\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x34\\x00\\x00\\x00\\x33\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x07\\x00\\x00\\x00\\x35\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\xFE\\x00\\x02\\x00\\x35\\x00\\x00\\x00\\x38\\x00\\x01\\x00\\x03\\x02\\x23\\x07\\x00\\x00\\x01\\x00\"\n\"\\x0A\\x00\\x08\\x00\\x30\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x11\\x00\\x02\\x00\\x01\\x00\\x00\\x00\\x0B\\x00\\x06\\x00\\x01\\x00\\x00\\x00\\x47\\x4C\\x53\\x4C\"\n\"\\x2E\\x73\\x74\\x64\\x2E\\x34\\x35\\x30\\x00\\x00\\x00\\x00\\x0E\\x00\\x03\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x0F\\x00\\x08\\x00\\x04\\x00\\x00\\x00\"\n\"\\x04\\x00\\x00\\x00\\x6D\\x61\\x69\\x6E\\x00\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x11\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x10\\x00\\x03\\x00\\x04\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x09\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\x22\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x11\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x15\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x29\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x2A\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x2A\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x2A\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\"\n\"\\x2A\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x2A\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x2A\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x2A\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x2A\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x2A\\x00\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x2A\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xC0\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x2A\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xCC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x2A\\x00\\x00\\x00\\x05\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\xD0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x2A\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xDC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x2A\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xE0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x2A\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\xEC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x2A\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xF0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x2A\\x00\\x00\\x00\"\n\"\\x0A\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xFC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x2A\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x01\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x2A\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x2A\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x4C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x2A\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x50\\x01\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x2A\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x5C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x2A\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x60\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x2A\\x00\\x00\\x00\\x11\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x6C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x2A\\x00\\x00\\x00\"\n\"\\x12\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x70\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x2A\\x00\\x00\\x00\\x13\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x01\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x2A\\x00\\x00\\x00\\x14\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x90\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x2A\\x00\\x00\\x00\\x15\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x94\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x2A\\x00\\x00\\x00\\x16\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x98\\x01\\x00\\x00\\x47\\x00\\x03\\x00\"\n\"\\x2A\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x2C\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x2C\\x00\\x00\\x00\"\n\"\\x21\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x2D\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x2D\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x2D\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x2D\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x2D\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x4C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x2D\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x50\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x2D\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x5C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x2D\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x60\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x2D\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x6C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x2D\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x70\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x2D\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x2D\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x88\\x00\\x00\\x00\\x47\\x00\\x03\\x00\\x2D\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x2F\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x2F\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x13\\x00\\x02\\x00\\x02\\x00\\x00\\x00\\x21\\x00\\x03\\x00\\x03\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x16\\x00\\x03\\x00\\x06\\x00\\x00\\x00\\x20\\x00\\x00\\x00\"\n\"\\x17\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x08\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x3B\\x00\\x04\\x00\\x08\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x19\\x00\\x09\\x00\\x0A\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x1B\\x00\\x03\\x00\\x0B\\x00\\x00\\x00\\x0A\\x00\\x00\\x00\"\n\"\\x20\\x00\\x04\\x00\\x0C\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x0C\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x17\\x00\\x04\\x00\\x0F\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x10\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\"\n\"\\x3B\\x00\\x04\\x00\\x10\\x00\\x00\\x00\\x11\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x14\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x3B\\x00\\x04\\x00\\x14\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x15\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x2B\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x17\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x18\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x2B\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x1C\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x18\\x00\\x04\\x00\\x26\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x17\\x00\\x04\\x00\\x27\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x03\\x00\\x00\\x00\"\n\"\\x2B\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x28\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x1C\\x00\\x04\\x00\\x29\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x28\\x00\\x00\\x00\"\n\"\\x1E\\x00\\x19\\x00\\x2A\\x00\\x00\\x00\\x26\\x00\\x00\\x00\\x26\\x00\\x00\\x00\\x26\\x00\\x00\\x00\\x27\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x27\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x27\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x27\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x29\\x00\\x00\\x00\\x27\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x27\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x27\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x0F\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x2B\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x2A\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x2B\\x00\\x00\\x00\\x2C\\x00\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\x1E\\x00\\x0C\\x00\\x2D\\x00\\x00\\x00\\x26\\x00\\x00\\x00\\x27\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x27\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x27\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x2E\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x2D\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x2E\\x00\\x00\\x00\\x2F\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x36\\x00\\x05\\x00\\x02\\x00\\x00\\x00\\x04\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x05\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x0F\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x11\\x00\\x00\\x00\\x57\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x13\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\"\n\"\\x12\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x18\\x00\\x00\\x00\\x19\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x17\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\"\n\"\\x1A\\x00\\x00\\x00\\x19\\x00\\x00\\x00\\x8E\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x1B\\x00\\x00\\x00\\x13\\x00\\x00\\x00\\x1A\\x00\\x00\\x00\\x41\\x00\\x05\\x00\"\n\"\\x18\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x1C\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\"\n\"\\x50\\x00\\x07\\x00\\x07\\x00\\x00\\x00\\x1F\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x0C\\x00\\x07\\x00\"\n\"\\x07\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x1A\\x00\\x00\\x00\\x1B\\x00\\x00\\x00\\x1F\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x18\\x00\\x00\\x00\"\n\"\\x22\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x50\\x00\\x07\\x00\"\n\"\\x07\\x00\\x00\\x00\\x24\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x81\\x00\\x05\\x00\\x07\\x00\\x00\\x00\"\n\"\\x25\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x24\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x09\\x00\\x00\\x00\\x25\\x00\\x00\\x00\\xFD\\x00\\x01\\x00\\x38\\x00\\x01\\x00\"\n\"\"},\n#endif\n#ifdef GLQUAKE\n{QR_OPENGL, 110, \"drawflat_wall\",\n\"!!cvard_srgb_b r_floorcolor\\n\"\n\"!!cvard_srgb_b r_wallcolor\\n\"\n\"!!permu FOG\\n\"\n\"!!samps lm=0\\n\"\n\n//this is for the '286' preset walls, and just draws lightmaps coloured based upon surface normals.\n\n\"#include \\\"sys/fog.h\\\"\\n\"\n\"varying vec4 col;\\n\"\n\"#ifdef VERTEX_SHADER\\n\"\n\"attribute vec3 v_normal;\\n\"\n\"attribute vec2 v_lmcoord;\\n\"\n\"varying vec2 lm;\\n\"\n\"uniform vec4 e_lmscale;\\n\"\n\"void main ()\\n\"\n\"{\\n\"\n\"#ifdef LM\\n\"\n\"col = vec4(1.0);\\n\"\n\"#else\\n\"\n\"col = vec4(e_lmscale.rgb * ((v_normal.z < 0.73)?r_wallcolor:r_floorcolor), e_lmscale.a);\\n\"\n\"#endif\\n\"\n\"lm = v_lmcoord;\\n\"\n\"gl_Position = ftetransform();\\n\"\n\"}\\n\"\n\"#endif\\n\"\n\"#ifdef FRAGMENT_SHADER\\n\"\n\"varying vec2 lm;\\n\"\n\"void main ()\\n\"\n\"{\\n\"\n\"gl_FragColor = fog4(col * texture2D(s_lm, lm));\\n\"\n\"}\\n\"\n\"#endif\\n\"\n},\n#endif\n#ifdef VKQUAKE\n{QR_VULKAN, -1, \"drawflat_wall\",\n\"\\xFF\\x53\\x50\\x56\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x2C\\x00\\x00\\x00\\x61\\x00\\x00\\x00\\x90\\x00\\x00\\x00\"\n\"\\xE0\\x0D\\x00\\x00\\x70\\x0E\\x00\\x00\\x30\\x11\\x00\\x00\\x01\\x00\\x66\\x33\\x72\\x5F\\x66\\x6C\\x6F\\x6F\\x72\\x63\\x6F\\x6C\\x6F\\x72\\x00\\x3F\\x00\\x00\"\n\"\\x00\\x3F\\x00\\x00\\x00\\x3F\\x00\\x00\\x00\\x01\\x03\\x66\\x33\\x72\\x5F\\x77\\x61\\x6C\\x6C\\x63\\x6F\\x6C\\x6F\\x72\\x00\\x3E\\x80\\x00\\x00\\x3E\\x80\\x00\"\n\"\\x00\\x3F\\x00\\x00\\x00\\x01\\x06\\x62\\x31\\x72\\x5F\\x66\\x6F\\x67\\x5F\\x65\\x78\\x70\\x32\\x00\\x00\\x00\\x00\\x00\\x01\\x07\\x62\\x31\\x72\\x5F\\x66\\x6F\"\n\"\\x67\\x5F\\x6C\\x69\\x6E\\x65\\x61\\x72\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x03\\x02\\x23\\x07\\x00\\x00\\x01\\x00\\x0A\\x00\\x08\\x00\\x76\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x11\\x00\\x02\\x00\\x01\\x00\\x00\\x00\\x0B\\x00\\x06\\x00\\x01\\x00\\x00\\x00\\x47\\x4C\\x53\\x4C\\x2E\\x73\\x74\\x64\\x2E\\x34\\x35\\x30\"\n\"\\x00\\x00\\x00\\x00\\x0E\\x00\\x03\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x0F\\x00\\x0F\\x00\\x00\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x6D\\x61\\x69\\x6E\"\n\"\\x00\\x00\\x00\\x00\\x27\\x00\\x00\\x00\\x44\\x00\\x00\\x00\\x4D\\x00\\x00\\x00\\x62\\x00\\x00\\x00\\x64\\x00\\x00\\x00\\x68\\x00\\x00\\x00\\x71\\x00\\x00\\x00\"\n\"\\x73\\x00\\x00\\x00\\x74\\x00\\x00\\x00\\x75\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x0E\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x47\\x00\\x04\\x00\"\n\"\\x0F\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x01\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\x10\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x02\\x01\\x00\\x00\\x47\\x00\\x04\\x00\"\n\"\\x13\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x03\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\x14\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x04\\x01\\x00\\x00\\x47\\x00\\x04\\x00\"\n\"\\x15\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x05\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\x1C\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\"\n\"\\x1E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x1E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x1E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x1E\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x1E\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x1E\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x1E\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x10\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x1E\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xC0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x1E\\x00\\x00\\x00\"\n\"\\x04\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xCC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x1E\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xD0\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x1E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xDC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x1E\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\xE0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x1E\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xEC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x1E\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xF0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x1E\\x00\\x00\\x00\\x0A\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\xFC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x1E\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x1E\\x00\\x00\\x00\"\n\"\\x0C\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x1E\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x4C\\x01\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x1E\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x50\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x1E\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x5C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x1E\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x60\\x01\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x1E\\x00\\x00\\x00\\x11\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x6C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x1E\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x70\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x1E\\x00\\x00\\x00\\x13\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x1E\\x00\\x00\\x00\"\n\"\\x14\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x90\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x1E\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x94\\x01\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x1E\\x00\\x00\\x00\\x16\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x98\\x01\\x00\\x00\\x47\\x00\\x03\\x00\\x1E\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x20\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x20\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x27\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x44\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x4D\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x62\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x64\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x66\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x47\\x00\\x03\\x00\\x66\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x6B\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x06\\x01\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x6C\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x07\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\x6D\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x15\\x00\\x00\\x00\"\n\"\\x48\\x00\\x04\\x00\\x6E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x6E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x6E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x6E\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x6E\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x4C\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x6E\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x50\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x6E\\x00\\x00\\x00\\x04\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x5C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x6E\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x60\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x6E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x6C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x6E\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x70\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x6E\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x6E\\x00\\x00\\x00\"\n\"\\x09\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x88\\x00\\x00\\x00\\x47\\x00\\x03\\x00\\x6E\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x70\\x00\\x00\\x00\"\n\"\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x70\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x71\\x00\\x00\\x00\"\n\"\\x1E\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x73\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x74\\x00\\x00\\x00\"\n\"\\x1E\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x75\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x13\\x00\\x02\\x00\\x02\\x00\\x00\\x00\"\n\"\\x21\\x00\\x03\\x00\\x03\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x16\\x00\\x03\\x00\\x06\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x17\\x00\\x04\\x00\\x07\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x21\\x00\\x03\\x00\\x08\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x17\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x03\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x0C\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x0C\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x32\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x00\\x00\\x80\\x43\\x32\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\"\n\"\\x00\\x80\\x80\\x43\\x32\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x00\\x00\\x81\\x43\\x3B\\x00\\x04\\x00\\x0C\\x00\\x00\\x00\\x12\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x32\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x13\\x00\\x00\\x00\\x00\\x80\\x81\\x43\\x32\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x14\\x00\\x00\\x00\"\n\"\\x00\\x00\\x82\\x43\\x32\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x00\\x80\\x82\\x43\\x20\\x00\\x04\\x00\\x17\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x18\\x00\\x04\\x00\\x19\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x15\\x00\\x04\\x00\\x1A\\x00\\x00\\x00\\x20\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x1A\\x00\\x00\\x00\\x1B\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x1C\\x00\\x04\\x00\\x1C\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x1B\\x00\\x00\\x00\\x17\\x00\\x04\\x00\\x1D\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x1E\\x00\\x19\\x00\\x1E\\x00\\x00\\x00\\x19\\x00\\x00\\x00\"\n\"\\x19\\x00\\x00\\x00\\x19\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x0B\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x1C\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x1F\\x00\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x1F\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x15\\x00\\x04\\x00\\x21\\x00\\x00\\x00\"\n\"\\x20\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x21\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x23\\x00\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\x19\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x26\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x26\\x00\\x00\\x00\"\n\"\\x27\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x29\\x00\\x00\\x00\\x00\\x00\\x80\\x3F\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\"\n\"\\x2F\\x00\\x00\\x00\\x00\\x00\\x80\\xBF\\x2B\\x00\\x04\\x00\\x1A\\x00\\x00\\x00\\x30\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x31\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x1A\\x00\\x00\\x00\\x36\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x1A\\x00\\x00\\x00\"\n\"\\x39\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x3D\\x00\\x00\\x00\\x00\\x00\\x00\\x40\\x20\\x00\\x04\\x00\\x43\\x00\\x00\\x00\"\n\"\\x03\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x43\\x00\\x00\\x00\\x44\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x21\\x00\\x00\\x00\"\n\"\\x45\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x46\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\"\n\"\\x4A\\x00\\x00\\x00\\x00\\x00\\x7F\\x43\\x3B\\x00\\x04\\x00\\x26\\x00\\x00\\x00\\x4D\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x4E\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x51\\x00\\x00\\x00\\x48\\xE1\\x3A\\x3F\\x14\\x00\\x02\\x00\\x52\\x00\\x00\\x00\"\n\"\\x17\\x00\\x04\\x00\\x56\\x00\\x00\\x00\\x52\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x5A\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x20\\x00\\x04\\x00\\x61\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x61\\x00\\x00\\x00\\x62\\x00\\x00\\x00\\x03\\x00\\x00\\x00\"\n\"\\x20\\x00\\x04\\x00\\x63\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x63\\x00\\x00\\x00\\x64\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x1E\\x00\\x03\\x00\\x66\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x67\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x66\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\"\n\"\\x67\\x00\\x00\\x00\\x68\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x32\\x00\\x04\\x00\\x21\\x00\\x00\\x00\\x6B\\x00\\x00\\x00\\x06\\x01\\x00\\x00\\x32\\x00\\x04\\x00\"\n\"\\x21\\x00\\x00\\x00\\x6C\\x00\\x00\\x00\\x07\\x01\\x00\\x00\\x32\\x00\\x04\\x00\\x21\\x00\\x00\\x00\\x6D\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x1E\\x00\\x0C\\x00\"\n\"\\x6E\\x00\\x00\\x00\\x19\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x6F\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x6E\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\"\n\"\\x6F\\x00\\x00\\x00\\x70\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x63\\x00\\x00\\x00\\x71\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x20\\x00\\x04\\x00\"\n\"\\x72\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x72\\x00\\x00\\x00\\x73\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\"\n\"\\x26\\x00\\x00\\x00\\x74\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x26\\x00\\x00\\x00\\x75\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x36\\x00\\x05\\x00\"\n\"\\x02\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x05\\x00\\x00\\x00\\x50\\x00\\x06\\x00\\x0B\\x00\\x00\\x00\"\n\"\\x11\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x0D\\x00\\x00\\x00\\x11\\x00\\x00\\x00\\x50\\x00\\x06\\x00\"\n\"\\x0B\\x00\\x00\\x00\\x16\\x00\\x00\\x00\\x13\\x00\\x00\\x00\\x14\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x12\\x00\\x00\\x00\\x16\\x00\\x00\\x00\"\n\"\\x41\\x00\\x06\\x00\\x46\\x00\\x00\\x00\\x47\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x45\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\"\n\"\\x48\\x00\\x00\\x00\\x47\\x00\\x00\\x00\\x4F\\x00\\x08\\x00\\x0B\\x00\\x00\\x00\\x49\\x00\\x00\\x00\\x48\\x00\\x00\\x00\\x48\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x50\\x00\\x06\\x00\\x0B\\x00\\x00\\x00\\x4B\\x00\\x00\\x00\\x4A\\x00\\x00\\x00\\x4A\\x00\\x00\\x00\\x4A\\x00\\x00\\x00\"\n\"\\x88\\x00\\x05\\x00\\x0B\\x00\\x00\\x00\\x4C\\x00\\x00\\x00\\x49\\x00\\x00\\x00\\x4B\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x4E\\x00\\x00\\x00\\x4F\\x00\\x00\\x00\"\n\"\\x4D\\x00\\x00\\x00\\x36\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x50\\x00\\x00\\x00\\x4F\\x00\\x00\\x00\\xB8\\x00\\x05\\x00\\x52\\x00\\x00\\x00\"\n\"\\x53\\x00\\x00\\x00\\x50\\x00\\x00\\x00\\x51\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\x54\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x0B\\x00\\x00\\x00\\x55\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x50\\x00\\x06\\x00\\x56\\x00\\x00\\x00\\x57\\x00\\x00\\x00\\x53\\x00\\x00\\x00\\x53\\x00\\x00\\x00\"\n\"\\x53\\x00\\x00\\x00\\xA9\\x00\\x06\\x00\\x0B\\x00\\x00\\x00\\x58\\x00\\x00\\x00\\x57\\x00\\x00\\x00\\x54\\x00\\x00\\x00\\x55\\x00\\x00\\x00\\x85\\x00\\x05\\x00\"\n\"\\x0B\\x00\\x00\\x00\\x59\\x00\\x00\\x00\\x4C\\x00\\x00\\x00\\x58\\x00\\x00\\x00\\x41\\x00\\x07\\x00\\x5A\\x00\\x00\\x00\\x5B\\x00\\x00\\x00\\x20\\x00\\x00\\x00\"\n\"\\x45\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x39\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x5C\\x00\\x00\\x00\\x5B\\x00\\x00\\x00\\x51\\x00\\x05\\x00\"\n\"\\x06\\x00\\x00\\x00\\x5D\\x00\\x00\\x00\\x59\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x5E\\x00\\x00\\x00\\x59\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x5F\\x00\\x00\\x00\\x59\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x50\\x00\\x07\\x00\\x07\\x00\\x00\\x00\"\n\"\\x60\\x00\\x00\\x00\\x5D\\x00\\x00\\x00\\x5E\\x00\\x00\\x00\\x5F\\x00\\x00\\x00\\x5C\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x44\\x00\\x00\\x00\\x60\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x1D\\x00\\x00\\x00\\x65\\x00\\x00\\x00\\x64\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x62\\x00\\x00\\x00\\x65\\x00\\x00\\x00\\x39\\x00\\x04\\x00\"\n\"\\x07\\x00\\x00\\x00\\x69\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x43\\x00\\x00\\x00\\x6A\\x00\\x00\\x00\\x68\\x00\\x00\\x00\\x22\\x00\\x00\\x00\"\n\"\\x3E\\x00\\x03\\x00\\x6A\\x00\\x00\\x00\\x69\\x00\\x00\\x00\\xFD\\x00\\x01\\x00\\x38\\x00\\x01\\x00\\x36\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x09\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x0A\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x17\\x00\\x00\\x00\\x18\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x41\\x00\\x05\\x00\\x23\\x00\\x00\\x00\\x24\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x19\\x00\\x00\\x00\\x25\\x00\\x00\\x00\"\n\"\\x24\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\x28\\x00\\x00\\x00\\x27\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x2A\\x00\\x00\\x00\"\n\"\\x28\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x2B\\x00\\x00\\x00\\x28\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x51\\x00\\x05\\x00\"\n\"\\x06\\x00\\x00\\x00\\x2C\\x00\\x00\\x00\\x28\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x50\\x00\\x07\\x00\\x07\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\\x2A\\x00\\x00\\x00\"\n\"\\x2B\\x00\\x00\\x00\\x2C\\x00\\x00\\x00\\x29\\x00\\x00\\x00\\x91\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x2E\\x00\\x00\\x00\\x25\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\"\n\"\\x3E\\x00\\x03\\x00\\x18\\x00\\x00\\x00\\x2E\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x31\\x00\\x00\\x00\\x32\\x00\\x00\\x00\\x18\\x00\\x00\\x00\\x30\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x33\\x00\\x00\\x00\\x32\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x34\\x00\\x00\\x00\\x33\\x00\\x00\\x00\"\n\"\\x2F\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x31\\x00\\x00\\x00\\x35\\x00\\x00\\x00\\x18\\x00\\x00\\x00\\x30\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x35\\x00\\x00\\x00\"\n\"\\x34\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x31\\x00\\x00\\x00\\x37\\x00\\x00\\x00\\x18\\x00\\x00\\x00\\x36\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\"\n\"\\x38\\x00\\x00\\x00\\x37\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x31\\x00\\x00\\x00\\x3A\\x00\\x00\\x00\\x18\\x00\\x00\\x00\\x39\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x06\\x00\\x00\\x00\\x3B\\x00\\x00\\x00\\x3A\\x00\\x00\\x00\\x81\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x3C\\x00\\x00\\x00\\x38\\x00\\x00\\x00\\x3B\\x00\\x00\\x00\"\n\"\\x88\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x3E\\x00\\x00\\x00\\x3C\\x00\\x00\\x00\\x3D\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x31\\x00\\x00\\x00\\x3F\\x00\\x00\\x00\"\n\"\\x18\\x00\\x00\\x00\\x36\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x3F\\x00\\x00\\x00\\x3E\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x40\\x00\\x00\\x00\"\n\"\\x18\\x00\\x00\\x00\\xFE\\x00\\x02\\x00\\x40\\x00\\x00\\x00\\x38\\x00\\x01\\x00\\x03\\x02\\x23\\x07\\x00\\x00\\x01\\x00\\x0A\\x00\\x08\\x00\\xA4\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x11\\x00\\x02\\x00\\x01\\x00\\x00\\x00\\x0B\\x00\\x06\\x00\\x01\\x00\\x00\\x00\\x47\\x4C\\x53\\x4C\\x2E\\x73\\x74\\x64\\x2E\\x34\\x35\\x30\"\n\"\\x00\\x00\\x00\\x00\\x0E\\x00\\x03\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x0F\\x00\\x09\\x00\\x04\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x6D\\x61\\x69\\x6E\"\n\"\\x00\\x00\\x00\\x00\\x2F\\x00\\x00\\x00\\x92\\x00\\x00\\x00\\x93\\x00\\x00\\x00\\x9B\\x00\\x00\\x00\\x10\\x00\\x03\\x00\\x04\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x15\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x01\\x01\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x17\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x02\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\x1A\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x03\\x01\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x04\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\x1C\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x05\\x01\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x1F\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x28\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x07\\x01\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x2F\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x3C\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x10\\x00\\x00\\x00\"\n\"\\x48\\x00\\x04\\x00\\x3E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x3E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x3E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x3E\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x3E\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x3E\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x3E\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x05\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x3E\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x3E\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x3E\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xC0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x3E\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xCC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x3E\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\xD0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x3E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xDC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x3E\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xE0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x3E\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xEC\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x3E\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xF0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x3E\\x00\\x00\\x00\\x0A\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\xFC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x3E\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x3E\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x3E\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x4C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x3E\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x50\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x3E\\x00\\x00\\x00\"\n\"\\x0F\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x5C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x3E\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x60\\x01\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x3E\\x00\\x00\\x00\\x11\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x6C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x3E\\x00\\x00\\x00\\x12\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x70\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x3E\\x00\\x00\\x00\\x13\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x01\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x3E\\x00\\x00\\x00\\x14\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x90\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x3E\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x94\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x3E\\x00\\x00\\x00\\x16\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x98\\x01\\x00\\x00\\x47\\x00\\x03\\x00\\x3E\\x00\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x40\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x40\\x00\\x00\\x00\\x21\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x5D\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x06\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\x92\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x93\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x98\\x00\\x00\\x00\\x22\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x98\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x9B\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\xA1\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\xA1\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\xA1\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\xA1\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\xA1\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x4C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\xA1\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x50\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\xA1\\x00\\x00\\x00\"\n\"\\x04\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x5C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\xA1\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x60\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\xA1\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x6C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\xA1\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x70\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\xA1\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\xA1\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x88\\x00\\x00\\x00\\x47\\x00\\x03\\x00\\xA1\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\"\n\"\\xA3\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\xA3\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x13\\x00\\x02\\x00\"\n\"\\x02\\x00\\x00\\x00\\x21\\x00\\x03\\x00\\x03\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x16\\x00\\x03\\x00\\x06\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x17\\x00\\x04\\x00\"\n\"\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x08\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x21\\x00\\x04\\x00\"\n\"\\x09\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\x17\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x20\\x00\\x04\\x00\"\n\"\\x0E\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x21\\x00\\x04\\x00\\x0F\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x20\\x00\\x04\\x00\"\n\"\\x13\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x13\\x00\\x00\\x00\\x14\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x32\\x00\\x04\\x00\"\n\"\\x06\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x00\\x00\\x80\\x43\\x32\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x16\\x00\\x00\\x00\\x00\\x80\\x80\\x43\\x32\\x00\\x04\\x00\"\n\"\\x06\\x00\\x00\\x00\\x17\\x00\\x00\\x00\\x00\\x00\\x81\\x43\\x3B\\x00\\x04\\x00\\x13\\x00\\x00\\x00\\x19\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x32\\x00\\x04\\x00\"\n\"\\x06\\x00\\x00\\x00\\x1A\\x00\\x00\\x00\\x00\\x80\\x81\\x43\\x32\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x1B\\x00\\x00\\x00\\x00\\x00\\x82\\x43\\x32\\x00\\x04\\x00\"\n\"\\x06\\x00\\x00\\x00\\x1C\\x00\\x00\\x00\\x00\\x80\\x82\\x43\\x15\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x32\\x00\\x04\\x00\"\n\"\\x1E\\x00\\x00\\x00\\x1F\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x14\\x00\\x02\\x00\"\n\"\\x21\\x00\\x00\\x00\\x34\\x00\\x06\\x00\\x21\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\xAB\\x00\\x00\\x00\\x1F\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x34\\x00\\x05\\x00\"\n\"\\x21\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xA8\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x32\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\\x28\\x00\\x00\\x00\\x07\\x01\\x00\\x00\"\n\"\\x34\\x00\\x06\\x00\\x21\\x00\\x00\\x00\\x29\\x00\\x00\\x00\\xAB\\x00\\x00\\x00\\x28\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x2C\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x2E\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x2E\\x00\\x00\\x00\"\n\"\\x2F\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x15\\x00\\x04\\x00\\x30\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x30\\x00\\x00\\x00\"\n\"\\x31\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x32\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x30\\x00\\x00\\x00\"\n\"\\x35\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x18\\x00\\x04\\x00\\x3A\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x30\\x00\\x00\\x00\"\n\"\\x3B\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x1C\\x00\\x04\\x00\\x3C\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x3B\\x00\\x00\\x00\\x17\\x00\\x04\\x00\\x3D\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x1E\\x00\\x19\\x00\\x3E\\x00\\x00\\x00\\x3A\\x00\\x00\\x00\\x3A\\x00\\x00\\x00\\x3A\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x3C\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x3D\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x3F\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x3E\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\"\n\"\\x3F\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\\x41\\x00\\x00\\x00\\x14\\x00\\x00\\x00\\x20\\x00\\x04\\x00\"\n\"\\x42\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\\x49\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\"\n\"\\x06\\x00\\x00\\x00\\x57\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x32\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\\x5D\\x00\\x00\\x00\\x06\\x01\\x00\\x00\\x34\\x00\\x06\\x00\"\n\"\\x21\\x00\\x00\\x00\\x5E\\x00\\x00\\x00\\xAB\\x00\\x00\\x00\\x5D\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x65\\x00\\x00\\x00\"\n\"\\x3B\\xAA\\xB8\\x3F\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x69\\x00\\x00\\x00\\x00\\x00\\x80\\x3F\\x2B\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\\x6A\\x00\\x00\\x00\"\n\"\\x13\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x74\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x34\\x00\\x06\\x00\\x21\\x00\\x00\\x00\\x7E\\x00\\x00\\x00\"\n\"\\xAB\\x00\\x00\\x00\\x1F\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x34\\x00\\x05\\x00\\x21\\x00\\x00\\x00\\x7F\\x00\\x00\\x00\\xA8\\x00\\x00\\x00\\x7E\\x00\\x00\\x00\"\n\"\\x20\\x00\\x04\\x00\\x91\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x91\\x00\\x00\\x00\\x92\\x00\\x00\\x00\\x03\\x00\\x00\\x00\"\n\"\\x3B\\x00\\x04\\x00\\x2E\\x00\\x00\\x00\\x93\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x19\\x00\\x09\\x00\\x95\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x1B\\x00\\x03\\x00\\x96\\x00\\x00\\x00\\x95\\x00\\x00\\x00\"\n\"\\x20\\x00\\x04\\x00\\x97\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x96\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x97\\x00\\x00\\x00\\x98\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x20\\x00\\x04\\x00\\x9A\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x3D\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x9A\\x00\\x00\\x00\\x9B\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x1E\\x00\\x0C\\x00\\xA1\\x00\\x00\\x00\\x3A\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x3D\\x00\\x00\\x00\\x3D\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\xA2\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\xA1\\x00\\x00\\x00\"\n\"\\x3B\\x00\\x04\\x00\\xA2\\x00\\x00\\x00\\xA3\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x36\\x00\\x05\\x00\\x02\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x03\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x05\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x0E\\x00\\x00\\x00\\x9F\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x50\\x00\\x06\\x00\"\n\"\\x07\\x00\\x00\\x00\\x18\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x16\\x00\\x00\\x00\\x17\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x14\\x00\\x00\\x00\\x18\\x00\\x00\\x00\"\n\"\\x50\\x00\\x06\\x00\\x07\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x1A\\x00\\x00\\x00\\x1B\\x00\\x00\\x00\\x1C\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x19\\x00\\x00\\x00\"\n\"\\x1D\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\x94\\x00\\x00\\x00\\x93\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x96\\x00\\x00\\x00\\x99\\x00\\x00\\x00\"\n\"\\x98\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x3D\\x00\\x00\\x00\\x9C\\x00\\x00\\x00\\x9B\\x00\\x00\\x00\\x57\\x00\\x05\\x00\\x0D\\x00\\x00\\x00\\x9D\\x00\\x00\\x00\"\n\"\\x99\\x00\\x00\\x00\\x9C\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x0D\\x00\\x00\\x00\\x9E\\x00\\x00\\x00\\x94\\x00\\x00\\x00\\x9D\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\"\n\"\\x9F\\x00\\x00\\x00\\x9E\\x00\\x00\\x00\\x39\\x00\\x05\\x00\\x0D\\x00\\x00\\x00\\xA0\\x00\\x00\\x00\\x11\\x00\\x00\\x00\\x9F\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\"\n\"\\x92\\x00\\x00\\x00\\xA0\\x00\\x00\\x00\\xFD\\x00\\x01\\x00\\x38\\x00\\x01\\x00\\x36\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x09\\x00\\x00\\x00\\x37\\x00\\x03\\x00\\x08\\x00\\x00\\x00\\x0A\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x0C\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x2C\\x00\\x00\\x00\"\n\"\\x2D\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x2C\\x00\\x00\\x00\\x39\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\xF7\\x00\\x03\\x00\\x25\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\\x23\\x00\\x00\\x00\\x24\\x00\\x00\\x00\\x25\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x24\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x07\\x00\\x00\\x00\\x26\\x00\\x00\\x00\\x0A\\x00\\x00\\x00\\xFE\\x00\\x02\\x00\\x26\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x25\\x00\\x00\\x00\\xF7\\x00\\x03\\x00\"\n\"\\x2B\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\\x29\\x00\\x00\\x00\\x2A\\x00\\x00\\x00\\x4E\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x2A\\x00\\x00\\x00\"\n\"\\x41\\x00\\x05\\x00\\x32\\x00\\x00\\x00\\x33\\x00\\x00\\x00\\x2F\\x00\\x00\\x00\\x31\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x34\\x00\\x00\\x00\"\n\"\\x33\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x32\\x00\\x00\\x00\\x36\\x00\\x00\\x00\\x2F\\x00\\x00\\x00\\x35\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\"\n\"\\x37\\x00\\x00\\x00\\x36\\x00\\x00\\x00\\x88\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x38\\x00\\x00\\x00\\x34\\x00\\x00\\x00\\x37\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\"\n\"\\x2D\\x00\\x00\\x00\\x38\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x42\\x00\\x00\\x00\\x43\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x41\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x06\\x00\\x00\\x00\\x44\\x00\\x00\\x00\\x43\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x45\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\\x83\\x00\\x05\\x00\"\n\"\\x06\\x00\\x00\\x00\\x46\\x00\\x00\\x00\\x44\\x00\\x00\\x00\\x45\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x42\\x00\\x00\\x00\\x47\\x00\\x00\\x00\\x40\\x00\\x00\\x00\"\n\"\\x41\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x48\\x00\\x00\\x00\\x47\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x42\\x00\\x00\\x00\\x4A\\x00\\x00\\x00\"\n\"\\x40\\x00\\x00\\x00\\x49\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x4B\\x00\\x00\\x00\\x4A\\x00\\x00\\x00\\x83\\x00\\x05\\x00\\x06\\x00\\x00\\x00\"\n\"\\x4C\\x00\\x00\\x00\\x48\\x00\\x00\\x00\\x4B\\x00\\x00\\x00\\x88\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x4D\\x00\\x00\\x00\\x46\\x00\\x00\\x00\\x4C\\x00\\x00\\x00\"\n\"\\x3E\\x00\\x03\\x00\\x39\\x00\\x00\\x00\\x4D\\x00\\x00\\x00\\xF9\\x00\\x02\\x00\\x2B\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x4E\\x00\\x00\\x00\\x41\\x00\\x05\\x00\"\n\"\\x42\\x00\\x00\\x00\\x4F\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x41\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x50\\x00\\x00\\x00\\x4F\\x00\\x00\\x00\"\n\"\\x41\\x00\\x05\\x00\\x32\\x00\\x00\\x00\\x51\\x00\\x00\\x00\\x2F\\x00\\x00\\x00\\x31\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x52\\x00\\x00\\x00\"\n\"\\x51\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x53\\x00\\x00\\x00\\x50\\x00\\x00\\x00\\x52\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x32\\x00\\x00\\x00\"\n\"\\x54\\x00\\x00\\x00\\x2F\\x00\\x00\\x00\\x35\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x55\\x00\\x00\\x00\\x54\\x00\\x00\\x00\\x88\\x00\\x05\\x00\"\n\"\\x06\\x00\\x00\\x00\\x56\\x00\\x00\\x00\\x53\\x00\\x00\\x00\\x55\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x2D\\x00\\x00\\x00\\x56\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x06\\x00\\x00\\x00\\x58\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x42\\x00\\x00\\x00\\x59\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x49\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x5A\\x00\\x00\\x00\\x59\\x00\\x00\\x00\\x83\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x5B\\x00\\x00\\x00\\x58\\x00\\x00\\x00\"\n\"\\x5A\\x00\\x00\\x00\\x0C\\x00\\x07\\x00\\x06\\x00\\x00\\x00\\x5C\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x28\\x00\\x00\\x00\\x57\\x00\\x00\\x00\\x5B\\x00\\x00\\x00\"\n\"\\x3E\\x00\\x03\\x00\\x2D\\x00\\x00\\x00\\x5C\\x00\\x00\\x00\\xF7\\x00\\x03\\x00\\x60\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\\x5E\\x00\\x00\\x00\"\n\"\\x5F\\x00\\x00\\x00\\x60\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x5F\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x61\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x62\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x63\\x00\\x00\\x00\\x62\\x00\\x00\\x00\"\n\"\\x61\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x2D\\x00\\x00\\x00\\x63\\x00\\x00\\x00\\xF9\\x00\\x02\\x00\\x60\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x60\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x64\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x66\\x00\\x00\\x00\\x64\\x00\\x00\\x00\"\n\"\\x65\\x00\\x00\\x00\\x7F\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x67\\x00\\x00\\x00\\x66\\x00\\x00\\x00\\x0C\\x00\\x06\\x00\\x06\\x00\\x00\\x00\\x68\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x67\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x39\\x00\\x00\\x00\\x68\\x00\\x00\\x00\\xF9\\x00\\x02\\x00\\x2B\\x00\\x00\\x00\"\n\"\\xF8\\x00\\x02\\x00\\x2B\\x00\\x00\\x00\\x41\\x00\\x06\\x00\\x42\\x00\\x00\\x00\\x6B\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x6A\\x00\\x00\\x00\\x35\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x6C\\x00\\x00\\x00\\x6B\\x00\\x00\\x00\\x83\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x6D\\x00\\x00\\x00\\x69\\x00\\x00\\x00\"\n\"\\x6C\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x6E\\x00\\x00\\x00\\x39\\x00\\x00\\x00\\x0C\\x00\\x08\\x00\\x06\\x00\\x00\\x00\\x6F\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x2B\\x00\\x00\\x00\\x6E\\x00\\x00\\x00\\x57\\x00\\x00\\x00\\x69\\x00\\x00\\x00\\x41\\x00\\x06\\x00\\x42\\x00\\x00\\x00\\x70\\x00\\x00\\x00\"\n\"\\x40\\x00\\x00\\x00\\x6A\\x00\\x00\\x00\\x35\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x71\\x00\\x00\\x00\\x70\\x00\\x00\\x00\\x85\\x00\\x05\\x00\"\n\"\\x06\\x00\\x00\\x00\\x72\\x00\\x00\\x00\\x6F\\x00\\x00\\x00\\x71\\x00\\x00\\x00\\x81\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x73\\x00\\x00\\x00\\x6D\\x00\\x00\\x00\"\n\"\\x72\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x39\\x00\\x00\\x00\\x73\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x74\\x00\\x00\\x00\\x75\\x00\\x00\\x00\\x40\\x00\\x00\\x00\"\n\"\\x6A\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\x76\\x00\\x00\\x00\\x75\\x00\\x00\\x00\\x4F\\x00\\x08\\x00\\x07\\x00\\x00\\x00\\x77\\x00\\x00\\x00\"\n\"\\x76\\x00\\x00\\x00\\x76\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x78\\x00\\x00\\x00\"\n\"\\x0A\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x79\\x00\\x00\\x00\\x39\\x00\\x00\\x00\\x50\\x00\\x06\\x00\\x07\\x00\\x00\\x00\\x7A\\x00\\x00\\x00\"\n\"\\x79\\x00\\x00\\x00\\x79\\x00\\x00\\x00\\x79\\x00\\x00\\x00\\x0C\\x00\\x08\\x00\\x07\\x00\\x00\\x00\\x7B\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x2E\\x00\\x00\\x00\"\n\"\\x77\\x00\\x00\\x00\\x78\\x00\\x00\\x00\\x7A\\x00\\x00\\x00\\xFE\\x00\\x02\\x00\\x7B\\x00\\x00\\x00\\x38\\x00\\x01\\x00\\x36\\x00\\x05\\x00\\x0D\\x00\\x00\\x00\"\n\"\\x11\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\\x37\\x00\\x03\\x00\\x0E\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x12\\x00\\x00\\x00\"\n\"\\x3B\\x00\\x04\\x00\\x08\\x00\\x00\\x00\\x84\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\xF7\\x00\\x03\\x00\\x81\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\"\n\"\\x7F\\x00\\x00\\x00\\x80\\x00\\x00\\x00\\x81\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x80\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\x82\\x00\\x00\\x00\"\n\"\\x10\\x00\\x00\\x00\\xFE\\x00\\x02\\x00\\x82\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x81\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\x85\\x00\\x00\\x00\"\n\"\\x10\\x00\\x00\\x00\\x4F\\x00\\x08\\x00\\x07\\x00\\x00\\x00\\x86\\x00\\x00\\x00\\x85\\x00\\x00\\x00\\x85\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x84\\x00\\x00\\x00\\x86\\x00\\x00\\x00\\x39\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x87\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\"\n\"\\x84\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x88\\x00\\x00\\x00\\x87\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\"\n\"\\x89\\x00\\x00\\x00\\x87\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x8A\\x00\\x00\\x00\\x87\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x50\\x00\\x07\\x00\\x0D\\x00\\x00\\x00\\x8B\\x00\\x00\\x00\\x88\\x00\\x00\\x00\\x89\\x00\\x00\\x00\\x8A\\x00\\x00\\x00\\x69\\x00\\x00\\x00\\x41\\x00\\x05\\x00\"\n\"\\x2C\\x00\\x00\\x00\\x8C\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x35\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x8D\\x00\\x00\\x00\\x8C\\x00\\x00\\x00\"\n\"\\x8E\\x00\\x05\\x00\\x0D\\x00\\x00\\x00\\x8E\\x00\\x00\\x00\\x8B\\x00\\x00\\x00\\x8D\\x00\\x00\\x00\\xFE\\x00\\x02\\x00\\x8E\\x00\\x00\\x00\\x38\\x00\\x01\\x00\"\n\"\"},\n#endif\n#ifdef D3D9QUAKE\n{QR_DIRECT3D9, 9, \"drawflat_wall\",\n\"!!cvard3 r_floorcolour\\n\"\n\"!!cvard3 r_wallcolour\\n\"\n\"!!samps 1\\n\"\n//FIXME !!permu FOG\n\n\"struct a2v {\\n\"\n\"float4 pos: POSITION;\\n\"\n\"float2 lmtc: TEXCOORD1;\\n\"\n\"float3 normal: NORMAL;\\n\"\n\"};\\n\"\n\"struct v2f {\\n\"\n\"#ifndef FRAGMENT_SHADER\\n\"\n\"float4 pos: POSITION;\\n\"\n\"#endif\\n\"\n\"float2 lmtc: TEXCOORD0;\\n\"\n\"float4 col: TEXCOORD1; //tc not colour to preserve range for oversaturation\\n\"\n\"};\\n\"\n\"#ifdef VERTEX_SHADER\\n\"\n\"float4x4  m_modelviewprojection;\\n\"\n\"float4 e_lmscale;\\n\"\n\"v2f main (a2v inp)\\n\"\n\"{\\n\"\n\"v2f outp;\\n\"\n\"outp.pos = mul(m_modelviewprojection, inp.pos);\\n\"\n\"outp.lmtc = inp.lmtc;\\n\"\n\"outp.col = e_lmscale * float4(((inp.normal.z < 0.73)?r_wallcolour:r_floorcolour)/255.0, 1.0);\\n\"\n\"return outp;\\n\"\n\"}\\n\"\n\"#endif\\n\"\n\n\"#ifdef FRAGMENT_SHADER\\n\"\n\"sampler s_t0;\\n\"\n\"float4 main (v2f inp) : COLOR0\\n\"\n\"{\\n\"\n\"return inp.col * tex2D(s_t0, inp.lmtc).xyzw;\\n\"\n\"}\\n\"\n\"#endif\\n\"\n},\n#endif\n#ifdef D3D11QUAKE\n{QR_DIRECT3D11, 11, \"drawflat_wall\",\n\"!!samps lightmap\\n\"\n\n\"struct a2v\\n\"\n\"{\\n\"\n\"float4 pos: POSITION;\\n\"\n\"float2 tc: TEXCOORD0;\\n\"\n\"float2 lmtc: TEXCOORD1;\\n\"\n\"float3 norm: NORMAL;\\n\"\n\"};\\n\"\n\"struct v2f\\n\"\n\"{\\n\"\n\"float4 pos: SV_POSITION;\\n\"\n\"float2 tc: TEXCOORD0;\\n\"\n\"float2 lmtc: TEXCOORD1;\\n\"\n\"float4 col: TEXCOORD2;\\n\"\n\"};\\n\"\n\n\"#include <ftedefs.h>\\n\"\n\n\"#ifdef VERTEX_SHADER\\n\"\n\"v2f main (a2v inp)\\n\"\n\"{\\n\"\n\"v2f outp;\\n\"\n\"outp.pos = mul(m_model, inp.pos);\\n\"\n\"outp.pos = mul(m_view, outp.pos);\\n\"\n\"outp.pos = mul(m_projection, outp.pos);\\n\"\n\"outp.tc = inp.tc;\\n\"\n\"outp.lmtc = inp.lmtc;\\n\"\n\"outp.col = ((inp.norm.z<0.73)?float4(0.5, 0.5, 0.5, 1):float4(0.25, 0.25, 0.5, 1));\\n\"\n\"return outp;\\n\"\n\"}\\n\"\n\"#endif\\n\"\n\n\"#ifdef FRAGMENT_SHADER\\n\"\n\"Texture2D t_lightmap : register(t0);\\n\"\n\"SamplerState s_lightmap : register(s0);\\n\"\n\n\"float4 main (v2f inp) : SV_TARGET\\n\"\n\"{\\n\"\n\"return inp.col * t_lightmap.Sample(s_lightmap, inp.lmtc);\\n\"\n\"}\\n\"\n\"#endif\\n\"\n},\n#endif\n#ifdef GLQUAKE\n{QR_OPENGL, 110, \"wireframe\",\n\"!!ver 100 150\\n\"\n\"!!permu TESS\\n\"\n\"!!permu FRAMEBLEND\\n\"\n\"!!permu SKELETAL\\n\"\n\"!!permu FOG\\n\"\n\"!!cvardf r_tessellation_level=5\\n\"\n\n\"#include \\\"sys/defs.h\\\"\\n\"\n\n//standard shader used for wireframe stuff.\n//must support skeletal and 2-way vertex blending or Bad Things Will Happen.\n\n\n\n\n\n\n\n\n\"#ifdef VERTEX_SHADER\\n\"\n\"#include \\\"sys/skeletal.h\\\"\\n\"\n\n\"#ifdef TESS\\n\"\n\"varying vec3 vertex;\\n\"\n\"varying vec3 normal;\\n\"\n\"#endif\\n\"\n\n\"void main ()\\n\"\n\"{\\n\"\n\"vec3 n, s, t, w;\\n\"\n\"gl_Position = skeletaltransform_wnst(w,n,s,t);\\n\"\n\n\"#ifdef TESS\\n\"\n\"normal = n;\\n\"\n\"vertex = w;\\n\"\n\"#endif\\n\"\n\"}\\n\"\n\"#endif\\n\"\n\n\n\n\n\n\n\n\n\n\n\"#if defined(TESS_CONTROL_SHADER)\\n\"\n\"layout(vertices = 3) out;\\n\"\n\n\"in vec3 vertex[];\\n\"\n\"out vec3 t_vertex[];\\n\"\n\"in vec3 normal[];\\n\"\n\"out vec3 t_normal[];\\n\"\n\"void main()\\n\"\n\"{\\n\"\n//the control shader needs to pass stuff through\n\"#define id gl_InvocationID\\n\"\n\"t_vertex[id] = vertex[id];\\n\"\n\"t_normal[id] = normal[id];\\n\"\n\n\"gl_TessLevelOuter[0] = float(r_tessellation_level);\\n\"\n\"gl_TessLevelOuter[1] = float(r_tessellation_level);\\n\"\n\"gl_TessLevelOuter[2] = float(r_tessellation_level);\\n\"\n\"gl_TessLevelInner[0] = float(r_tessellation_level);\\n\"\n\"}\\n\"\n\"#endif\\n\"\n\n\n\n\n\n\n\n\n\n\"#if defined(TESS_EVALUATION_SHADER)\\n\"\n\"layout(triangles) in;\\n\"\n\n\"in vec3 t_vertex[];\\n\"\n\"in vec3 t_normal[];\\n\"\n\n\"#define LERP(a) (gl_TessCoord.x*a[0] + gl_TessCoord.y*a[1] + gl_TessCoord.z*a[2])\\n\"\n\"void main()\\n\"\n\"{\\n\"\n\"#define factor 1.0\\n\"\n\"vec3 w = LERP(t_vertex);\\n\"\n\n\"vec3 t0 = w - dot(w-t_vertex[0],t_normal[0])*t_normal[0];\\n\"\n\"vec3 t1 = w - dot(w-t_vertex[1],t_normal[1])*t_normal[1];\\n\"\n\"vec3 t2 = w - dot(w-t_vertex[2],t_normal[2])*t_normal[2];\\n\"\n\"w = w*(1.0-factor) + factor*(gl_TessCoord.x*t0+gl_TessCoord.y*t1+gl_TessCoord.z*t2);\\n\"\n\n\n\"gl_Position = m_modelviewprojection * vec4(w,1.0);\\n\"\n\"}\\n\"\n\"#endif\\n\"\n\n\n\n\n\n\n\n\n\n\n\"#ifdef FRAGMENT_SHADER\\n\"\n\n\"#include \\\"sys/fog.h\\\"\\n\"\n\n\"void main ()\\n\"\n\"{\\n\"\n\"gl_FragColor = fog4(vec4(1.0) * e_colourident);\\n\"\n\"}\\n\"\n\"#endif\\n\"\n\n},\n#endif\n#ifdef GLQUAKE\n{QR_OPENGL, 110, \"itemtimer\",\n\"!!permu FOG\\n\"\n\n\"#include \\\"sys/defs.h\\\"\\n\"\n\"#include \\\"sys/fog.h\\\"\\n\"\n\n\"varying vec2 tc;\\n\"\n\"varying vec4 vc;\\n\"\n\n\"#ifdef VERTEX_SHADER\\n\"\n\"void main ()\\n\"\n\"{\\n\"\n\"tc = v_texcoord;\\n\"\n\"vc = v_colour;\\n\"\n\"gl_Position = ftetransform();\\n\"\n\"}\\n\"\n\"#endif\\n\"\n\n\n\"#ifdef FRAGMENT_SHADER\\n\"\n\"void main ()\\n\"\n\"{\\n\"\n\"gl_FragColor = vec4(0.5,0.5,0.5,1);//texture2D(s_diffuse, tc.xy);\\n\"\n\n\"vec2 st = (tc-floor(tc)) - 0.5;\\n\"\n\"st *= 2.0;\\n\"\n\"float dist = sqrt(dot(st,st));\\n\"\n\n\"float ring = 1.0 + smoothstep(0.9, 1.0, dist)\\n\"\n\"- smoothstep(0.8, 0.9, dist);\\n\"\n\n//fade out the rim\n\"if ((atan(st.t, st.s)+3.14)/6.28 > vc.a)\\n\"\n\"gl_FragColor.a *= 0.25;\\n\"\n\"gl_FragColor.rgb *= mix(vc.rgb, vec3(0.0), ring);\\n\"\n//gl_FragColor.a;\n\n//and finally hide it all if we're fogged.\n\"#ifdef FOG\\n\"\n\"gl_FragColor = fog4additive(gl_FragColor);\\n\"\n\"#endif\\n\"\n\"}\\n\"\n\"#endif\\n\"\n\n},\n#endif\n#ifdef GLQUAKE\n{QR_OPENGL, 110, \"lpp_depthnorm\",\n\"!!ver 100 130\\n\"\n\"!!permu BUMP\\n\"\n\"!!permu SKELETAL\\n\"\n\"!!permu FRAMEBLEND\\n\"\n\"!!cvarf r_glsl_offsetmapping_scale\\n\"\n\"!!samps normalmap specular\\n\"\n\n//light pre-pass rendering (defered lighting)\n//this is the initial pass, that draws the surface normals and depth to the initial colour buffer\n\n\"#include \\\"sys/defs.h\\\"\\n\"\n\n\"#if defined(OFFSETMAPPING)\\n\"\n\"varying vec3 eyevector;\\n\"\n\"#endif\\n\"\n\n\"varying vec3 norm;\\n\"\n\"#if defined(BUMP)\\n\"\n\"varying vec3 tang, bitang;\\n\"\n\"#endif\\n\"\n\"#if defined(BUMP) || defined(SPECULAR)\\n\"\n\"varying vec2 tc;\\n\"\n\"#endif\\n\"\n\"#ifdef VERTEX_SHADER\\n\"\n\"#include \\\"sys/skeletal.h\\\"\\n\"\n\n\"void main()\\n\"\n\"{\\n\"\n\"#if defined(BUMP)\\n\"\n\"gl_Position = skeletaltransform_nst(norm, tang, bitang);\\n\"\n\"#else\\n\"\n\"gl_Position = skeletaltransform_n(norm);\\n\"\n\"#endif\\n\"\n\"#if defined(BUMP) || defined(SPECULAR)\\n\"\n\"tc = v_texcoord;\\n\"\n\"#endif\\n\"\n\n\"#if defined(OFFSETMAPPING)\\n\"\n\"vec3 eyeminusvertex = e_eyepos - v_position.xyz;\\n\"\n\"eyevector.x = dot(eyeminusvertex, v_svector.xyz);\\n\"\n\"eyevector.y = dot(eyeminusvertex, v_tvector.xyz);\\n\"\n\"eyevector.z = dot(eyeminusvertex, v_normal.xyz);\\n\"\n\"#endif\\n\"\n\"}\\n\"\n\"#endif\\n\"\n\"#ifdef FRAGMENT_SHADER\\n\"\n\"#ifdef OFFSETMAPPING\\n\"\n\"#include \\\"sys/offsetmapping.h\\\"\\n\"\n\"#endif\\n\"\n\"void main()\\n\"\n\"{\\n\"\n//adjust texture coords for offsetmapping\n\"#ifdef OFFSETMAPPING\\n\"\n\"vec2 tcoffsetmap = offsetmap(s_normalmap, tc, eyevector);\\n\"\n\"#define tc tcoffsetmap\\n\"\n\"#endif\\n\"\n\n\"vec3 onorm;\\n\"\n\"vec4 ospec;\\n\"\n\n//need to write surface normals so that light shines on the surfaces properly\n\"#if defined(BUMP)\\n\"\n\"vec3 bm = 2.0*texture2D(s_normalmap, tc).xyz - 1.0;\\n\"\n\"onorm = normalize(bm.x * tang + bm.y * bitang + bm.z * norm);\\n\"\n\"#else\\n\"\n\"onorm = norm;\\n\"\n\"#endif\\n\"\n\n//we need to write specular exponents if we want per-pixel control over that\n\"#if defined(SPECULAR)\\n\"\n\"ospec = texture2D(s_specular, tc);\\n\"\n\"#else\\n\"\n\"ospec = vec4(0.0, 0.0, 0.0, 0.0);\\n\"\n\"#endif\\n\"\n\n\"gl_FragColor = vec4(onorm.xyz, ospec.a * FTE_SPECULAR_EXPONENT);\\n\"\n\"}\\n\"\n\"#endif\\n\"\n},\n#endif\n#ifdef GLQUAKE\n{QR_OPENGL, 110, \"lpp_light\",\n//this shader is a light shader. ideally drawn with a quad covering the entire region\n//the output is contribution from this light (which will be additively blended)\n//you can blame Electro for much of the maths in here.\n\"!!ver 100 450\\n\"\n//FIXME: !!permu FOG\n\"!!samps shadowmap 2\\n\"\n\n\"#include \\\"sys/defs.h\\\"\\n\"\n\"#include \\\"sys/pcf.h\\\"\\n\"\n\n//s_t0 is the depth\n//s_t1 is the normals+spec-exponent\n//output should be amount of light hitting the surface.\n\n\"varying vec4 tf;\\n\"\n\"#ifdef VERTEX_SHADER\\n\"\n\"void main()\\n\"\n\"{\\n\"\n\"tf = ftetransform();\\n\"\n\"gl_Position = tf;\\n\"\n\"}\\n\"\n\"#endif\\n\"\n\"#ifdef FRAGMENT_SHADER\\n\"\n\n\"#define out_diff fte_fragdata0\\n\"\n\"#define out_spec fte_fragdata1\\n\"\n\n\"vec3 calcLightWorldPos(vec2 screenPos, float depth)\\n\"\n\"{\\n\"\n\"vec4 pos = m_invviewprojection * vec4(screenPos.xy, (depth*2.0)-1.0, 1.0);\\n\"\n\"return pos.xyz / pos.w;\\n\"\n\"}\\n\"\n\"void main ()\\n\"\n\"{\\n\"\n\"vec3 lightColour  = l_lightcolour.rgb;\\n\"\n\n\"vec2 fc = tf.xy / tf.w;\\n\"\n\"vec2 gc = (1.0 + fc) / 2.0;\\n\"\n\"float depth = texture2D(s_t0, gc).r;\\n\"\n\"vec4 data = texture2D(s_t1, gc);\\n\"\n\"vec3 norm = data.xyz;\\n\"\n\"float spec_exponent = data.a;\\n\"\n\n/* calc where the wall that generated this sample came from */\n\"vec3 worldPos = calcLightWorldPos(fc, depth);\\n\"\n\n/*we need to know the cube projection (for both cubemaps+shadows)*/\n\"vec4 cubeaxis = l_cubematrix*vec4(worldPos.xyz, 1.0);\\n\"\n\n/*calc ambient lighting term*/\n\"vec3 lightDir = l_lightposition - worldPos;\\n\"\n\"float atten = max(1.0 - (dot(lightDir, lightDir)/(l_lightradius*l_lightradius)), 0.0);\\n\"\n\n/*calc diffuse lighting term*/\n\"lightDir = normalize(lightDir);\\n\"\n\"float nDotL = dot(norm, lightDir);\\n\"\n\"float lightDiffuse = max(0.0, nDotL);\\n\"\n\n/*calc specular lighting term*/\n\"vec3 halfdir = normalize(normalize(e_eyepos - worldPos) + lightDir); //ASSUMPTION: e_eyepos requires an identity modelmatrix (true for world+sprites, but usually not for models/bsps)\\n\"\n\"float spec = pow(max(dot(halfdir, norm), 0.0), spec_exponent);\\n\"\n\n//fixme: apply fog?\n//fixme: cubemap filters\n\n\"float shadows = ShadowmapFilter(s_shadowmap, cubeaxis);\\n\"\n\n\"out_diff = vec4(lightColour * (l_lightcolourscale.x + l_lightcolourscale.y*lightDiffuse*shadows), 1.0);\\n\"\n\"out_spec = vec4(lightColour * l_lightcolourscale.z*spec*shadows, 1.0);\\n\"\n\"}\\n\"\n\"#endif\\n\"\n},\n#endif\n#ifdef GLQUAKE\n{QR_OPENGL, 110, \"lpp_wall\",\n\"!!ver 100 150\\n\"\n\"!!permu BUMP //for offsetmapping rather than bumpmapping (real bumps are handled elsewhere)\\n\"\n\"!!cvarf r_glsl_offsetmapping_scale\\n\"\n\"!!samps diffuse specular fullbright lightmap\\n\"\n\"!!samps 2\\n\"\n\n//the final defered lighting pass.\n//the lighting values were written to some render target, which is fed into this shader, and now we draw all the wall textures with it.\n\n\"#include \\\"sys/defs.h\\\"\\n\"\n\n\"#if defined(OFFSETMAPPING)\\n\"\n\"varying vec3 eyevector;\\n\"\n\"#endif\\n\"\n\n\n\"varying vec2 tc, lm;\\n\"\n\"varying vec4 tf;\\n\"\n\"#ifdef VERTEX_SHADER\\n\"\n\"void main ()\\n\"\n\"{\\n\"\n\"tc = v_texcoord;\\n\"\n\"lm = v_lmcoord;\\n\"\n\"gl_Position = tf = ftetransform();\\n\"\n\n\"#if defined(OFFSETMAPPING)\\n\"\n\"vec3 eyeminusvertex = e_eyepos - v_position.xyz;\\n\"\n\"eyevector.x = dot(eyeminusvertex, v_svector.xyz);\\n\"\n\"eyevector.y = dot(eyeminusvertex, v_tvector.xyz);\\n\"\n\"eyevector.z = dot(eyeminusvertex, v_normal.xyz);\\n\"\n\"#endif\\n\"\n\"}\\n\"\n\"#endif\\n\"\n\"#ifdef FRAGMENT_SHADER\\n\"\n\"#ifdef OFFSETMAPPING\\n\"\n\"#include \\\"sys/offsetmapping.h\\\"\\n\"\n\"#endif\\n\"\n\"void main ()\\n\"\n\"{\\n\"\n//adjust texture coords for offsetmapping\n\"#ifdef OFFSETMAPPING\\n\"\n\"vec2 tcoffsetmap = offsetmap(s_normalmap, tc, eyevector);\\n\"\n\"#define tc tcoffsetmap\\n\"\n\"#endif\\n\"\n\n\"vec2 nst;\\n\"\n\"nst = tf.xy / tf.w;\\n\"\n\"nst = (1.0 + nst) / 2.0;\\n\"\n\"vec4 dl = texture2D(s_t0, nst); //diffuse lighting\\n\"\n\"vec4 sl = texture2D(s_t1, nst); //specular lighting\\n\"\n\"vec4 c = texture2D(s_diffuse, tc);\\n\"\n\"vec4 s = texture2D(s_specular, tc);\\n\"\n\"vec4 f = texture2D(s_fullbright, tc);\\n\"\n//fixme: top+bottom should add upper+lower colours to c here\n\"vec3 lmsamp = texture2D(s_lightmap, lm).rgb*e_lmscale.rgb;\\n\"\n//fixme: fog the legacy lightmap data\n\"vec3 diff = dl.rgb + lmsamp;\\n\"\n\"vec3 spec = sl.rgb * float(SPECMUL); //should be rgb, but whatever.\\n\"\n\n//fixme: do specular somehow\n\"gl_FragColor = vec4(diff*c.rgb + spec*s.rgb + f.rgb, 1.0);\\n\"\n//fixme: fullbrights should add to the rgb value\n\"}\\n\"\n\"#endif\\n\"\n},\n#endif\n#ifdef GLQUAKE\n{QR_OPENGL, 110, \"postproc_fisheye\",\n\"!!cvarf ffov\\n\"\n\"!!samps screen:samplerCube=0\\n\"\n\n//fisheye view rendering, for silly fovs that are still playable.\n\n\"#ifdef VERTEX_SHADER\\n\"\n\"attribute vec2 v_texcoord;\\n\"\n\"varying vec2 texcoord;\\n\"\n\"void main()\\n\"\n\"{\\n\"\n\"texcoord = v_texcoord.xy;\\n\"\n\"gl_Position = ftetransform();\\n\"\n\"}\\n\"\n\"#endif\\n\"\n\"#ifdef FRAGMENT_SHADER\\n\"\n\"varying vec2 texcoord;\\n\"\n\"uniform float cvar_ffov;\\n\"\n\"void main()\\n\"\n\"{\\n\"\n\"vec3 tc; \\n\"\n\"vec2 d; \\n\"\n\"vec2 ang; \\n\"\n\"d = texcoord; \\n\"\n\"ang.x = sqrt(d.x*d.x+d.y*d.y)*radians(cvar_ffov); \\n\"\n\"ang.y = -atan(d.y, d.x); \\n\"\n\"tc.x = sin(ang.x) * cos(ang.y); \\n\"\n\"tc.y = sin(ang.x) * sin(ang.y); \\n\"\n\"tc.z = cos(ang.x); \\n\"\n\"gl_FragColor = textureCube(s_screen, tc);\\n\"\n\"}\\n\"\n\"#endif\\n\"\n},\n#endif\n#ifdef VKQUAKE\n{QR_VULKAN, -1, \"postproc_fisheye\",\n\"\\xFF\\x53\\x50\\x56\\x01\\x00\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x2C\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x3C\\x00\\x00\\x00\"\n\"\\x2C\\x0A\\x00\\x00\\x68\\x0A\\x00\\x00\\xD0\\x0A\\x00\\x00\\x01\\x00\\x66\\x31\\x66\\x66\\x6F\\x76\\x00\\x43\\xB4\\x00\\x00\\x00\\x00\\x00\\x03\\x02\\x23\\x07\"\n\"\\x00\\x00\\x01\\x00\\x0A\\x00\\x08\\x00\\x4D\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x11\\x00\\x02\\x00\\x01\\x00\\x00\\x00\\x0B\\x00\\x06\\x00\\x01\\x00\\x00\\x00\"\n\"\\x47\\x4C\\x53\\x4C\\x2E\\x73\\x74\\x64\\x2E\\x34\\x35\\x30\\x00\\x00\\x00\\x00\\x0E\\x00\\x03\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x0F\\x00\\x0E\\x00\"\n\"\\x00\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x6D\\x61\\x69\\x6E\\x00\\x00\\x00\\x00\\x1C\\x00\\x00\\x00\\x39\\x00\\x00\\x00\\x3B\\x00\\x00\\x00\\x3F\\x00\\x00\\x00\"\n\"\\x48\\x00\\x00\\x00\\x49\\x00\\x00\\x00\\x4A\\x00\\x00\\x00\\x4B\\x00\\x00\\x00\\x4C\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x11\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x13\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\"\n\"\\x13\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x13\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xC0\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xCC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x05\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\xD0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xDC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x13\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xE0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\xEC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xF0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\"\n\"\\x0A\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xFC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x01\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x4C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x50\\x01\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x13\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x5C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x60\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x11\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x6C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\"\n\"\\x12\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x70\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x13\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x01\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x14\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x90\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x15\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x94\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x16\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x98\\x01\\x00\\x00\\x47\\x00\\x03\\x00\"\n\"\\x13\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x15\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x15\\x00\\x00\\x00\"\n\"\\x21\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x1C\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x39\\x00\\x00\\x00\"\n\"\\x1E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x3B\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x3D\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x03\\x00\\x3D\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x43\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x48\\x00\\x04\\x00\\x44\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x44\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x44\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x44\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x44\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x4C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x44\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x50\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x44\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x5C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x44\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x60\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x44\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x6C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x44\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x70\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x44\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x44\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x88\\x00\\x00\\x00\\x47\\x00\\x03\\x00\\x44\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x46\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x46\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x48\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x49\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x03\\x00\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x4A\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x4B\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x05\\x00\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x4C\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x13\\x00\\x02\\x00\\x02\\x00\\x00\\x00\\x21\\x00\\x03\\x00\\x03\\x00\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\x16\\x00\\x03\\x00\\x06\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x17\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x04\\x00\\x00\\x00\"\n\"\\x21\\x00\\x03\\x00\\x08\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x18\\x00\\x04\\x00\"\n\"\\x0D\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x17\\x00\\x04\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x15\\x00\\x04\\x00\"\n\"\\x0F\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x0F\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x1C\\x00\\x04\\x00\"\n\"\\x11\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x17\\x00\\x04\\x00\\x12\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x1E\\x00\\x19\\x00\"\n\"\\x13\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x11\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x12\\x00\\x00\\x00\"\n\"\\x20\\x00\\x04\\x00\\x14\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x13\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x14\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x15\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x17\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x20\\x00\\x04\\x00\\x18\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\"\n\"\\x3B\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\\x1C\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x00\\x00\\x80\\x3F\"\n\"\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x24\\x00\\x00\\x00\\x00\\x00\\x80\\xBF\\x2B\\x00\\x04\\x00\\x0F\\x00\\x00\\x00\\x25\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x20\\x00\\x04\\x00\\x26\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x0F\\x00\\x00\\x00\\x2B\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x2B\\x00\\x04\\x00\\x0F\\x00\\x00\\x00\\x2E\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x32\\x00\\x00\\x00\\x00\\x00\\x00\\x40\"\n\"\\x20\\x00\\x04\\x00\\x38\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x38\\x00\\x00\\x00\\x39\\x00\\x00\\x00\\x03\\x00\\x00\\x00\"\n\"\\x20\\x00\\x04\\x00\\x3A\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x3A\\x00\\x00\\x00\\x3B\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x1E\\x00\\x03\\x00\\x3D\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x3E\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x3D\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\"\n\"\\x3E\\x00\\x00\\x00\\x3F\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x41\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x32\\x00\\x04\\x00\"\n\"\\x06\\x00\\x00\\x00\\x43\\x00\\x00\\x00\\x00\\x00\\x80\\x43\\x1E\\x00\\x0C\\x00\\x44\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x20\\x00\\x04\\x00\"\n\"\\x45\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x44\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x45\\x00\\x00\\x00\\x46\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x20\\x00\\x04\\x00\"\n\"\\x47\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x47\\x00\\x00\\x00\\x48\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\"\n\"\\x3A\\x00\\x00\\x00\\x49\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\\x4A\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\"\n\"\\x1B\\x00\\x00\\x00\\x4B\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\\x4C\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x36\\x00\\x05\\x00\"\n\"\\x02\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x05\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x12\\x00\\x00\\x00\"\n\"\\x3C\\x00\\x00\\x00\\x3B\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x39\\x00\\x00\\x00\\x3C\\x00\\x00\\x00\\x39\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x40\\x00\\x00\\x00\"\n\"\\x09\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x41\\x00\\x00\\x00\\x42\\x00\\x00\\x00\\x3F\\x00\\x00\\x00\\x17\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x42\\x00\\x00\\x00\"\n\"\\x40\\x00\\x00\\x00\\xFD\\x00\\x01\\x00\\x38\\x00\\x01\\x00\\x36\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x08\\x00\\x00\\x00\"\n\"\\xF8\\x00\\x02\\x00\\x0A\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x18\\x00\\x00\\x00\"\n\"\\x19\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x17\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\x1A\\x00\\x00\\x00\\x19\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x0E\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x1C\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x1F\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x21\\x00\\x00\\x00\"\n\"\\x1D\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x50\\x00\\x07\\x00\\x07\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x1F\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x21\\x00\\x00\\x00\"\n\"\\x1E\\x00\\x00\\x00\\x91\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x1A\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x0C\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x26\\x00\\x00\\x00\\x27\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x25\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\"\n\"\\x28\\x00\\x00\\x00\\x27\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x29\\x00\\x00\\x00\\x28\\x00\\x00\\x00\\x24\\x00\\x00\\x00\\x41\\x00\\x05\\x00\"\n\"\\x26\\x00\\x00\\x00\\x2A\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x25\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x2A\\x00\\x00\\x00\\x29\\x00\\x00\\x00\\x41\\x00\\x05\\x00\"\n\"\\x26\\x00\\x00\\x00\\x2C\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x2B\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\\x2C\\x00\\x00\\x00\"\n\"\\x41\\x00\\x05\\x00\\x26\\x00\\x00\\x00\\x2F\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x2E\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x30\\x00\\x00\\x00\"\n\"\\x2F\\x00\\x00\\x00\\x81\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x31\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\\x30\\x00\\x00\\x00\\x88\\x00\\x05\\x00\\x06\\x00\\x00\\x00\"\n\"\\x33\\x00\\x00\\x00\\x31\\x00\\x00\\x00\\x32\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x26\\x00\\x00\\x00\\x34\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x2B\\x00\\x00\\x00\"\n\"\\x3E\\x00\\x03\\x00\\x34\\x00\\x00\\x00\\x33\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x35\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\xFE\\x00\\x02\\x00\"\n\"\\x35\\x00\\x00\\x00\\x38\\x00\\x01\\x00\\x03\\x02\\x23\\x07\\x00\\x00\\x01\\x00\\x0A\\x00\\x08\\x00\\x54\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x11\\x00\\x02\\x00\"\n\"\\x01\\x00\\x00\\x00\\x0B\\x00\\x06\\x00\\x01\\x00\\x00\\x00\\x47\\x4C\\x53\\x4C\\x2E\\x73\\x74\\x64\\x2E\\x34\\x35\\x30\\x00\\x00\\x00\\x00\\x0E\\x00\\x03\\x00\"\n\"\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x0F\\x00\\x07\\x00\\x04\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x6D\\x61\\x69\\x6E\\x00\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\"\n\"\\x43\\x00\\x00\\x00\\x10\\x00\\x03\\x00\\x04\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\x43\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x47\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x47\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x4D\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x4E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x05\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x4E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x4E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x4E\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x4E\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x4E\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\"\n\"\\x48\\x00\\x04\\x00\\x4E\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x4E\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x80\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x4E\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x4E\\x00\\x00\\x00\"\n\"\\x03\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xC0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x4E\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xCC\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x4E\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xD0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x4E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\xDC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x4E\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xE0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x4E\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xEC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x4E\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\xF0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x4E\\x00\\x00\\x00\\x0A\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xFC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x4E\\x00\\x00\\x00\"\n\"\\x0B\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x4E\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x01\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x4E\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x4C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x4E\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x50\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x4E\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x5C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x4E\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x60\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x4E\\x00\\x00\\x00\\x11\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x6C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x4E\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x70\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x4E\\x00\\x00\\x00\"\n\"\\x13\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x4E\\x00\\x00\\x00\\x14\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x90\\x01\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x4E\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x94\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x4E\\x00\\x00\\x00\\x16\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x98\\x01\\x00\\x00\\x47\\x00\\x03\\x00\\x4E\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x50\\x00\\x00\\x00\\x22\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x50\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x51\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x51\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x51\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x51\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x51\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x4C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x51\\x00\\x00\\x00\\x03\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x50\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x51\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x5C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x51\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x60\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x51\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x6C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x51\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x70\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x51\\x00\\x00\\x00\"\n\"\\x08\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x51\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x88\\x00\\x00\\x00\"\n\"\\x47\\x00\\x03\\x00\\x51\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x53\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\"\n\"\\x53\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x13\\x00\\x02\\x00\\x02\\x00\\x00\\x00\\x21\\x00\\x03\\x00\\x03\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x16\\x00\\x03\\x00\\x06\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x17\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x20\\x00\\x04\\x00\"\n\"\\x08\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x0A\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\"\n\"\\x0A\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x15\\x00\\x04\\x00\\x0E\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\"\n\"\\x0E\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x10\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\"\n\"\\x0E\\x00\\x00\\x00\\x16\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x32\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x00\\x00\\x80\\x43\\x17\\x00\\x04\\x00\"\n\"\\x29\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x2A\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x29\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\"\n\"\\x0E\\x00\\x00\\x00\\x3F\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x17\\x00\\x04\\x00\\x41\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x20\\x00\\x04\\x00\"\n\"\\x42\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x41\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x42\\x00\\x00\\x00\\x43\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x19\\x00\\x09\\x00\"\n\"\\x44\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x1B\\x00\\x03\\x00\\x45\\x00\\x00\\x00\\x44\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x46\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x45\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\"\n\"\\x46\\x00\\x00\\x00\\x47\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x18\\x00\\x04\\x00\\x4B\\x00\\x00\\x00\\x41\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\"\n\"\\x0E\\x00\\x00\\x00\\x4C\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x1C\\x00\\x04\\x00\\x4D\\x00\\x00\\x00\\x41\\x00\\x00\\x00\\x4C\\x00\\x00\\x00\\x1E\\x00\\x19\\x00\"\n\"\\x4E\\x00\\x00\\x00\\x4B\\x00\\x00\\x00\\x4B\\x00\\x00\\x00\\x4B\\x00\\x00\\x00\\x29\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x29\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x29\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x29\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x4D\\x00\\x00\\x00\\x29\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x29\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x29\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x41\\x00\\x00\\x00\\x41\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x20\\x00\\x04\\x00\\x4F\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x4E\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x4F\\x00\\x00\\x00\\x50\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x1E\\x00\\x0C\\x00\\x51\\x00\\x00\\x00\\x4B\\x00\\x00\\x00\\x29\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x29\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x29\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x41\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x52\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x51\\x00\\x00\\x00\"\n\"\\x3B\\x00\\x04\\x00\\x52\\x00\\x00\\x00\\x53\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x36\\x00\\x05\\x00\\x02\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x03\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x05\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x08\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\"\n\"\\x08\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x2A\\x00\\x00\\x00\\x2B\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x07\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x09\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x10\\x00\\x00\\x00\"\n\"\\x11\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x11\\x00\\x00\\x00\\x41\\x00\\x05\\x00\"\n\"\\x10\\x00\\x00\\x00\\x13\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x14\\x00\\x00\\x00\\x13\\x00\\x00\\x00\"\n\"\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x14\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x10\\x00\\x00\\x00\\x17\\x00\\x00\\x00\"\n\"\\x09\\x00\\x00\\x00\\x16\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x18\\x00\\x00\\x00\\x17\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x10\\x00\\x00\\x00\"\n\"\\x19\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x16\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x1A\\x00\\x00\\x00\\x19\\x00\\x00\\x00\\x85\\x00\\x05\\x00\"\n\"\\x06\\x00\\x00\\x00\\x1B\\x00\\x00\\x00\\x18\\x00\\x00\\x00\\x1A\\x00\\x00\\x00\\x81\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x1C\\x00\\x00\\x00\\x15\\x00\\x00\\x00\"\n\"\\x1B\\x00\\x00\\x00\\x0C\\x00\\x06\\x00\\x06\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x1F\\x00\\x00\\x00\\x1C\\x00\\x00\\x00\\x0C\\x00\\x06\\x00\"\n\"\\x06\\x00\\x00\\x00\\x1F\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x20\\x00\\x00\\x00\"\n\"\\x1D\\x00\\x00\\x00\\x1F\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x10\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\"\n\"\\x21\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x10\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x16\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x06\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x10\\x00\\x00\\x00\\x24\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x25\\x00\\x00\\x00\\x24\\x00\\x00\\x00\\x0C\\x00\\x07\\x00\\x06\\x00\\x00\\x00\\x26\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x19\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x25\\x00\\x00\\x00\\x7F\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x27\\x00\\x00\\x00\\x26\\x00\\x00\\x00\\x41\\x00\\x05\\x00\"\n\"\\x10\\x00\\x00\\x00\\x28\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x16\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x28\\x00\\x00\\x00\\x27\\x00\\x00\\x00\\x41\\x00\\x05\\x00\"\n\"\\x10\\x00\\x00\\x00\\x2C\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\\x2C\\x00\\x00\\x00\"\n\"\\x0C\\x00\\x06\\x00\\x06\\x00\\x00\\x00\\x2E\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x10\\x00\\x00\\x00\"\n\"\\x2F\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x16\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x30\\x00\\x00\\x00\\x2F\\x00\\x00\\x00\\x0C\\x00\\x06\\x00\"\n\"\\x06\\x00\\x00\\x00\\x31\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x30\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x32\\x00\\x00\\x00\"\n\"\\x2E\\x00\\x00\\x00\\x31\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x10\\x00\\x00\\x00\\x33\\x00\\x00\\x00\\x2B\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\"\n\"\\x33\\x00\\x00\\x00\\x32\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x10\\x00\\x00\\x00\\x34\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x06\\x00\\x00\\x00\\x35\\x00\\x00\\x00\\x34\\x00\\x00\\x00\\x0C\\x00\\x06\\x00\\x06\\x00\\x00\\x00\\x36\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\"\n\"\\x35\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x10\\x00\\x00\\x00\\x37\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x16\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\"\n\"\\x38\\x00\\x00\\x00\\x37\\x00\\x00\\x00\\x0C\\x00\\x06\\x00\\x06\\x00\\x00\\x00\\x39\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x38\\x00\\x00\\x00\"\n\"\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x3A\\x00\\x00\\x00\\x36\\x00\\x00\\x00\\x39\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x10\\x00\\x00\\x00\\x3B\\x00\\x00\\x00\"\n\"\\x2B\\x00\\x00\\x00\\x16\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x3B\\x00\\x00\\x00\\x3A\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x10\\x00\\x00\\x00\\x3C\\x00\\x00\\x00\"\n\"\\x0D\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x3D\\x00\\x00\\x00\\x3C\\x00\\x00\\x00\\x0C\\x00\\x06\\x00\\x06\\x00\\x00\\x00\"\n\"\\x3E\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x3D\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x10\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x2B\\x00\\x00\\x00\"\n\"\\x3F\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x40\\x00\\x00\\x00\\x3E\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x45\\x00\\x00\\x00\\x48\\x00\\x00\\x00\\x47\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x29\\x00\\x00\\x00\\x49\\x00\\x00\\x00\\x2B\\x00\\x00\\x00\\x57\\x00\\x05\\x00\\x41\\x00\\x00\\x00\\x4A\\x00\\x00\\x00\\x48\\x00\\x00\\x00\"\n\"\\x49\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x43\\x00\\x00\\x00\\x4A\\x00\\x00\\x00\\xFD\\x00\\x01\\x00\\x38\\x00\\x01\\x00\"},\n#endif\n#ifdef GLQUAKE\n{QR_OPENGL, 110, \"postproc_panorama\",\n\"!!cvarf ffov\\n\"\n\"!!samps screen:samplerCube=0\\n\"\n\n//panoramic view rendering, for promo map shots or whatever.\n\n\"#ifdef VERTEX_SHADER\\n\"\n\"attribute vec2 v_texcoord;\\n\"\n\"varying vec2 texcoord;\\n\"\n\"void main()\\n\"\n\"{\\n\"\n\"texcoord = v_texcoord.xy;\\n\"\n\"gl_Position = ftetransform();\\n\"\n\"}\\n\"\n\"#endif\\n\"\n\"#ifdef FRAGMENT_SHADER\\n\"\n\"varying vec2 texcoord;\\n\"\n\"uniform float cvar_ffov;\\n\"\n\"void main()\\n\"\n\"{\\n\"\n\"vec3 tc; \\n\"\n\"float ang; \\n\"\n\"ang = texcoord.x*radians(cvar_ffov); \\n\"\n\"tc.x = sin(ang); \\n\"\n\"tc.y = -texcoord.y; \\n\"\n\"tc.z = cos(ang); \\n\"\n\"gl_FragColor = textureCube(s_screen, tc);\\n\"\n\"}\\n\"\n\"#endif\\n\"\n},\n#endif\n#ifdef VKQUAKE\n{QR_VULKAN, -1, \"postproc_panorama\",\n\"\\xFF\\x53\\x50\\x56\\x01\\x00\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x2C\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x3C\\x00\\x00\\x00\"\n\"\\x2C\\x0A\\x00\\x00\\x68\\x0A\\x00\\x00\\x6C\\x08\\x00\\x00\\x01\\x00\\x66\\x31\\x66\\x66\\x6F\\x76\\x00\\x43\\xB4\\x00\\x00\\x00\\x00\\x00\\x03\\x02\\x23\\x07\"\n\"\\x00\\x00\\x01\\x00\\x0A\\x00\\x08\\x00\\x4D\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x11\\x00\\x02\\x00\\x01\\x00\\x00\\x00\\x0B\\x00\\x06\\x00\\x01\\x00\\x00\\x00\"\n\"\\x47\\x4C\\x53\\x4C\\x2E\\x73\\x74\\x64\\x2E\\x34\\x35\\x30\\x00\\x00\\x00\\x00\\x0E\\x00\\x03\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x0F\\x00\\x0E\\x00\"\n\"\\x00\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x6D\\x61\\x69\\x6E\\x00\\x00\\x00\\x00\\x1C\\x00\\x00\\x00\\x39\\x00\\x00\\x00\\x3B\\x00\\x00\\x00\\x3F\\x00\\x00\\x00\"\n\"\\x48\\x00\\x00\\x00\\x49\\x00\\x00\\x00\\x4A\\x00\\x00\\x00\\x4B\\x00\\x00\\x00\\x4C\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x11\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x13\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\"\n\"\\x13\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x13\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xC0\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xCC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x05\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\xD0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xDC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x13\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xE0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\xEC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xF0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\"\n\"\\x0A\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xFC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x01\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x4C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x50\\x01\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x13\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x5C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x60\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x11\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x6C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\"\n\"\\x12\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x70\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x13\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x01\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x14\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x90\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x15\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x94\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x16\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x98\\x01\\x00\\x00\\x47\\x00\\x03\\x00\"\n\"\\x13\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x15\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x15\\x00\\x00\\x00\"\n\"\\x21\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x1C\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x39\\x00\\x00\\x00\"\n\"\\x1E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x3B\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x3D\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x03\\x00\\x3D\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x43\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x48\\x00\\x04\\x00\\x44\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x44\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x44\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x44\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x44\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x4C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x44\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x50\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x44\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x5C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x44\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x60\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x44\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x6C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x44\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x70\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x44\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x44\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x88\\x00\\x00\\x00\\x47\\x00\\x03\\x00\\x44\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x46\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x46\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x48\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x49\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x03\\x00\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x4A\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x4B\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x05\\x00\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x4C\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x13\\x00\\x02\\x00\\x02\\x00\\x00\\x00\\x21\\x00\\x03\\x00\\x03\\x00\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\x16\\x00\\x03\\x00\\x06\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x17\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x04\\x00\\x00\\x00\"\n\"\\x21\\x00\\x03\\x00\\x08\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x18\\x00\\x04\\x00\"\n\"\\x0D\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x17\\x00\\x04\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x15\\x00\\x04\\x00\"\n\"\\x0F\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x0F\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x1C\\x00\\x04\\x00\"\n\"\\x11\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x17\\x00\\x04\\x00\\x12\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x1E\\x00\\x19\\x00\"\n\"\\x13\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x11\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x12\\x00\\x00\\x00\"\n\"\\x20\\x00\\x04\\x00\\x14\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x13\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x14\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x15\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x17\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x20\\x00\\x04\\x00\\x18\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\"\n\"\\x3B\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\\x1C\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x00\\x00\\x80\\x3F\"\n\"\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x24\\x00\\x00\\x00\\x00\\x00\\x80\\xBF\\x2B\\x00\\x04\\x00\\x0F\\x00\\x00\\x00\\x25\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x20\\x00\\x04\\x00\\x26\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x0F\\x00\\x00\\x00\\x2B\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x2B\\x00\\x04\\x00\\x0F\\x00\\x00\\x00\\x2E\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x32\\x00\\x00\\x00\\x00\\x00\\x00\\x40\"\n\"\\x20\\x00\\x04\\x00\\x38\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x38\\x00\\x00\\x00\\x39\\x00\\x00\\x00\\x03\\x00\\x00\\x00\"\n\"\\x20\\x00\\x04\\x00\\x3A\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x3A\\x00\\x00\\x00\\x3B\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x1E\\x00\\x03\\x00\\x3D\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x3E\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x3D\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\"\n\"\\x3E\\x00\\x00\\x00\\x3F\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x41\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x32\\x00\\x04\\x00\"\n\"\\x06\\x00\\x00\\x00\\x43\\x00\\x00\\x00\\x00\\x00\\x80\\x43\\x1E\\x00\\x0C\\x00\\x44\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x20\\x00\\x04\\x00\"\n\"\\x45\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x44\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x45\\x00\\x00\\x00\\x46\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x20\\x00\\x04\\x00\"\n\"\\x47\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x47\\x00\\x00\\x00\\x48\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\"\n\"\\x3A\\x00\\x00\\x00\\x49\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\\x4A\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\"\n\"\\x1B\\x00\\x00\\x00\\x4B\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\\x4C\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x36\\x00\\x05\\x00\"\n\"\\x02\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x05\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x12\\x00\\x00\\x00\"\n\"\\x3C\\x00\\x00\\x00\\x3B\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x39\\x00\\x00\\x00\\x3C\\x00\\x00\\x00\\x39\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x40\\x00\\x00\\x00\"\n\"\\x09\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x41\\x00\\x00\\x00\\x42\\x00\\x00\\x00\\x3F\\x00\\x00\\x00\\x17\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x42\\x00\\x00\\x00\"\n\"\\x40\\x00\\x00\\x00\\xFD\\x00\\x01\\x00\\x38\\x00\\x01\\x00\\x36\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x08\\x00\\x00\\x00\"\n\"\\xF8\\x00\\x02\\x00\\x0A\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x18\\x00\\x00\\x00\"\n\"\\x19\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x17\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\x1A\\x00\\x00\\x00\\x19\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x0E\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x1C\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x1F\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x21\\x00\\x00\\x00\"\n\"\\x1D\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x50\\x00\\x07\\x00\\x07\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x1F\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x21\\x00\\x00\\x00\"\n\"\\x1E\\x00\\x00\\x00\\x91\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x1A\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x0C\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x26\\x00\\x00\\x00\\x27\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x25\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\"\n\"\\x28\\x00\\x00\\x00\\x27\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x29\\x00\\x00\\x00\\x28\\x00\\x00\\x00\\x24\\x00\\x00\\x00\\x41\\x00\\x05\\x00\"\n\"\\x26\\x00\\x00\\x00\\x2A\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x25\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x2A\\x00\\x00\\x00\\x29\\x00\\x00\\x00\\x41\\x00\\x05\\x00\"\n\"\\x26\\x00\\x00\\x00\\x2C\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x2B\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\\x2C\\x00\\x00\\x00\"\n\"\\x41\\x00\\x05\\x00\\x26\\x00\\x00\\x00\\x2F\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x2E\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x30\\x00\\x00\\x00\"\n\"\\x2F\\x00\\x00\\x00\\x81\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x31\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\\x30\\x00\\x00\\x00\\x88\\x00\\x05\\x00\\x06\\x00\\x00\\x00\"\n\"\\x33\\x00\\x00\\x00\\x31\\x00\\x00\\x00\\x32\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x26\\x00\\x00\\x00\\x34\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x2B\\x00\\x00\\x00\"\n\"\\x3E\\x00\\x03\\x00\\x34\\x00\\x00\\x00\\x33\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x35\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\xFE\\x00\\x02\\x00\"\n\"\\x35\\x00\\x00\\x00\\x38\\x00\\x01\\x00\\x03\\x02\\x23\\x07\\x00\\x00\\x01\\x00\\x0A\\x00\\x08\\x00\\x36\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x11\\x00\\x02\\x00\"\n\"\\x01\\x00\\x00\\x00\\x0B\\x00\\x06\\x00\\x01\\x00\\x00\\x00\\x47\\x4C\\x53\\x4C\\x2E\\x73\\x74\\x64\\x2E\\x34\\x35\\x30\\x00\\x00\\x00\\x00\\x0E\\x00\\x03\\x00\"\n\"\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x0F\\x00\\x07\\x00\\x04\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x6D\\x61\\x69\\x6E\\x00\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\"\n\"\\x25\\x00\\x00\\x00\\x10\\x00\\x03\\x00\\x04\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x11\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\x25\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x29\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x29\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x2F\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x30\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x05\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x30\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x30\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x30\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x30\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x30\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\"\n\"\\x48\\x00\\x04\\x00\\x30\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x30\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x80\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x30\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x30\\x00\\x00\\x00\"\n\"\\x03\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xC0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x30\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xCC\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x30\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xD0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x30\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\xDC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x30\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xE0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x30\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xEC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x30\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\xF0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x30\\x00\\x00\\x00\\x0A\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xFC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x30\\x00\\x00\\x00\"\n\"\\x0B\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x30\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x01\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x30\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x4C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x30\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x50\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x30\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x5C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x30\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x60\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x30\\x00\\x00\\x00\\x11\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x6C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x30\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x70\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x30\\x00\\x00\\x00\"\n\"\\x13\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x30\\x00\\x00\\x00\\x14\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x90\\x01\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x30\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x94\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x30\\x00\\x00\\x00\\x16\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x98\\x01\\x00\\x00\\x47\\x00\\x03\\x00\\x30\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x32\\x00\\x00\\x00\\x22\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x32\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x33\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x33\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x33\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x33\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x33\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x4C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x33\\x00\\x00\\x00\\x03\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x50\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x33\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x5C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x33\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x60\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x33\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x6C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x33\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x70\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x33\\x00\\x00\\x00\"\n\"\\x08\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x33\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x88\\x00\\x00\\x00\"\n\"\\x47\\x00\\x03\\x00\\x33\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x35\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\"\n\"\\x35\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x13\\x00\\x02\\x00\\x02\\x00\\x00\\x00\\x21\\x00\\x03\\x00\\x03\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x16\\x00\\x03\\x00\\x06\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x17\\x00\\x04\\x00\"\n\"\\x09\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x0A\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\"\n\"\\x0A\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x15\\x00\\x04\\x00\\x0C\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\"\n\"\\x0C\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x0E\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x32\\x00\\x04\\x00\"\n\"\\x06\\x00\\x00\\x00\\x11\\x00\\x00\\x00\\x00\\x00\\x80\\x43\\x17\\x00\\x04\\x00\\x14\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x20\\x00\\x04\\x00\"\n\"\\x15\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x14\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x0C\\x00\\x00\\x00\\x1A\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\"\n\"\\x0C\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x17\\x00\\x04\\x00\\x23\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x20\\x00\\x04\\x00\"\n\"\\x24\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x24\\x00\\x00\\x00\\x25\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x19\\x00\\x09\\x00\"\n\"\\x26\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x1B\\x00\\x03\\x00\\x27\\x00\\x00\\x00\\x26\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x28\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x27\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\"\n\"\\x28\\x00\\x00\\x00\\x29\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x18\\x00\\x04\\x00\\x2D\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\"\n\"\\x0C\\x00\\x00\\x00\\x2E\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x1C\\x00\\x04\\x00\\x2F\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x2E\\x00\\x00\\x00\\x1E\\x00\\x19\\x00\"\n\"\\x30\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\\x14\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x14\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x14\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x14\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x2F\\x00\\x00\\x00\\x14\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x14\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x14\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x09\\x00\\x00\\x00\"\n\"\\x20\\x00\\x04\\x00\\x31\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x30\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x31\\x00\\x00\\x00\\x32\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x1E\\x00\\x0C\\x00\\x33\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\\x14\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x14\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x14\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x34\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x33\\x00\\x00\\x00\"\n\"\\x3B\\x00\\x04\\x00\\x34\\x00\\x00\\x00\\x35\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x36\\x00\\x05\\x00\\x02\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x03\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x05\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\"\n\"\\x15\\x00\\x00\\x00\\x16\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x0E\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\\x0C\\x00\\x06\\x00\\x06\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x0B\\x00\\x00\\x00\\x11\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x13\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\"\n\"\\x08\\x00\\x00\\x00\\x13\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x17\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\x0C\\x00\\x06\\x00\\x06\\x00\\x00\\x00\"\n\"\\x18\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x17\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x19\\x00\\x00\\x00\\x16\\x00\\x00\\x00\"\n\"\\x0D\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x19\\x00\\x00\\x00\\x18\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x0E\\x00\\x00\\x00\\x1B\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\"\n\"\\x1A\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x1C\\x00\\x00\\x00\\x1B\\x00\\x00\\x00\\x7F\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\"\n\"\\x1C\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x16\\x00\\x00\\x00\\x1A\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x1E\\x00\\x00\\x00\"\n\"\\x1D\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x1F\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\x0C\\x00\\x06\\x00\\x06\\x00\\x00\\x00\\x20\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x1F\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x16\\x00\\x00\\x00\\x21\\x00\\x00\\x00\"\n\"\\x3E\\x00\\x03\\x00\\x22\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x27\\x00\\x00\\x00\\x2A\\x00\\x00\\x00\\x29\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x14\\x00\\x00\\x00\\x2B\\x00\\x00\\x00\\x16\\x00\\x00\\x00\\x57\\x00\\x05\\x00\\x23\\x00\\x00\\x00\\x2C\\x00\\x00\\x00\\x2A\\x00\\x00\\x00\\x2B\\x00\\x00\\x00\"\n\"\\x3E\\x00\\x03\\x00\\x25\\x00\\x00\\x00\\x2C\\x00\\x00\\x00\\xFD\\x00\\x01\\x00\\x38\\x00\\x01\\x00\"},\n#endif\n#ifdef GLQUAKE\n{QR_OPENGL, 110, \"postproc_laea\",\n\"!!cvarf ffov\\n\"\n\"!!samps screen:samplerCube=0\\n\"\n\n//my attempt at lambert azimuthal equal-area view rendering, because you'll remember that name easily.\n\n\"#ifdef VERTEX_SHADER\\n\"\n\"attribute vec2 v_texcoord;\\n\"\n\"varying vec2 texcoord;\\n\"\n\"uniform float cvar_ffov;\\n\"\n\"void main()\\n\"\n\"{\\n\"\n\"texcoord = v_texcoord.xy;\\n\"\n\n//make sure the ffov cvar actually does something meaningful\n\"texcoord *= cvar_ffov / 90.0;\\n\"\n\n\"gl_Position = ftetransform();\\n\"\n\"}\\n\"\n\"#endif\\n\"\n\"#ifdef FRAGMENT_SHADER\\n\"\n\"varying vec2 texcoord;\\n\"\n\"void main()\\n\"\n\"{\\n\"\n\"vec3 tc; \\n\"\n\"vec2 d; \\n\"\n\"vec2 ang; \\n\"\n\"d = texcoord; \\n\"\n\n//compute the 2d->3d projection\n\"float sq = d.x*d.x+d.y*d.y;\\n\"\n\"if (sq > 4.0)\\n\"\n\"gl_FragColor = vec4(0,0,0,1);\\n\"\n\"else\\n\"\n\"{\\n\"\n\"tc.x = sqrt(1.0-(sq/4.0))*d.x;\\n\"\n\"tc.y = sqrt(1.0-(sq/4.0))*d.y;\\n\"\n\"tc.z = -1.0 + (sq/2.0);\\n\"\n\n\"tc.y *= -1.0;\\n\"\n\"tc.z *= -1.0;\\n\"\n\n\"gl_FragColor = textureCube(s_screen, tc);\\n\"\n\"}\\n\"\n\"}\\n\"\n\"#endif\\n\"\n},\n#endif\n#ifdef GLQUAKE\n{QR_OPENGL, 110, \"postproc_stereographic\",\n\"!!cvarf ffov\\n\"\n\"!!samps screen:samplerCube=0\\n\"\n\n//stereographic view rendering, for high fovs that are still playable.\n\n\"#ifdef VERTEX_SHADER\\n\"\n\"attribute vec2 v_texcoord;\\n\"\n\"varying vec2 texcoord;\\n\"\n\"uniform float cvar_ffov;\\n\"\n\"void main()\\n\"\n\"{\\n\"\n\"texcoord = v_texcoord.xy;\\n\"\n\n//make sure the ffov cvar actually does something meaningful\n\"texcoord *= cvar_ffov / 90.0;\\n\"\n\n\"gl_Position = ftetransform();\\n\"\n\"}\\n\"\n\"#endif\\n\"\n\"#ifdef FRAGMENT_SHADER\\n\"\n\"varying vec2 texcoord;\\n\"\n\"void main()\\n\"\n\"{\\n\"\n\"vec3 tc;\\n\"\n\"vec2 d;\\n\"\n\"vec2 ang;\\n\"\n\"d = texcoord; \\n\"\n\n//compute the 2d->3d projection\n\"float div = 1.0 + d.x*d.x + d.y*d.y;\\n\"\n\"tc.x = 2.0*d.x/div;\\n\"\n\"tc.y = -2.0*d.y/div;\\n\"\n\"tc.z = -(-1.0 + d.x*d.x + d.y*d.y)/div;\\n\"\n\n\"gl_FragColor = textureCube(s_screen, tc);\\n\"\n\"}\\n\"\n\"#endif\\n\"\n},\n#endif\n#ifdef VKQUAKE\n{QR_VULKAN, -1, \"postproc_stereographic\",\n\"\\xFF\\x53\\x50\\x56\\x01\\x00\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x2C\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x3C\\x00\\x00\\x00\"\n\"\\x80\\x0A\\x00\\x00\\xBC\\x0A\\x00\\x00\\xB0\\x0A\\x00\\x00\\x01\\x00\\x66\\x31\\x66\\x66\\x6F\\x76\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x03\\x02\\x23\\x07\"\n\"\\x00\\x00\\x01\\x00\\x0A\\x00\\x08\\x00\\x51\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x11\\x00\\x02\\x00\\x01\\x00\\x00\\x00\\x0B\\x00\\x06\\x00\\x01\\x00\\x00\\x00\"\n\"\\x47\\x4C\\x53\\x4C\\x2E\\x73\\x74\\x64\\x2E\\x34\\x35\\x30\\x00\\x00\\x00\\x00\\x0E\\x00\\x03\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x0F\\x00\\x0E\\x00\"\n\"\\x00\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x6D\\x61\\x69\\x6E\\x00\\x00\\x00\\x00\\x1C\\x00\\x00\\x00\\x39\\x00\\x00\\x00\\x3B\\x00\\x00\\x00\\x44\\x00\\x00\\x00\"\n\"\\x4C\\x00\\x00\\x00\\x4D\\x00\\x00\\x00\\x4E\\x00\\x00\\x00\\x4F\\x00\\x00\\x00\\x50\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x11\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x13\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\"\n\"\\x13\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x13\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xC0\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xCC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x05\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\xD0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xDC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x13\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xE0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\xEC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xF0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\"\n\"\\x0A\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xFC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x01\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x4C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x50\\x01\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x13\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x5C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x60\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x11\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x6C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\"\n\"\\x12\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x70\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x13\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x01\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x14\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x90\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x15\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x94\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x16\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x98\\x01\\x00\\x00\\x47\\x00\\x03\\x00\"\n\"\\x13\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x15\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x15\\x00\\x00\\x00\"\n\"\\x21\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x1C\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x39\\x00\\x00\\x00\"\n\"\\x1E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x3B\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x3D\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x42\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x03\\x00\"\n\"\\x42\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x48\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x48\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x48\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x48\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x48\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x4C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x48\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x50\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x48\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x5C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x48\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x60\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x48\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x6C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x48\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x70\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x48\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x48\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x88\\x00\\x00\\x00\\x47\\x00\\x03\\x00\\x48\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x4A\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x4A\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x4C\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x4D\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x03\\x00\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x4E\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x4F\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x05\\x00\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x50\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x13\\x00\\x02\\x00\\x02\\x00\\x00\\x00\\x21\\x00\\x03\\x00\\x03\\x00\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\x16\\x00\\x03\\x00\\x06\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x17\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x04\\x00\\x00\\x00\"\n\"\\x21\\x00\\x03\\x00\\x08\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x18\\x00\\x04\\x00\"\n\"\\x0D\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x17\\x00\\x04\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x15\\x00\\x04\\x00\"\n\"\\x0F\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x0F\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x1C\\x00\\x04\\x00\"\n\"\\x11\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x17\\x00\\x04\\x00\\x12\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x1E\\x00\\x19\\x00\"\n\"\\x13\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x11\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x12\\x00\\x00\\x00\"\n\"\\x20\\x00\\x04\\x00\\x14\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x13\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x14\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x15\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x17\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x20\\x00\\x04\\x00\\x18\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\"\n\"\\x3B\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\\x1C\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x00\\x00\\x80\\x3F\"\n\"\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x24\\x00\\x00\\x00\\x00\\x00\\x80\\xBF\\x2B\\x00\\x04\\x00\\x0F\\x00\\x00\\x00\\x25\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x20\\x00\\x04\\x00\\x26\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x0F\\x00\\x00\\x00\\x2B\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x2B\\x00\\x04\\x00\\x0F\\x00\\x00\\x00\\x2E\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x32\\x00\\x00\\x00\\x00\\x00\\x00\\x40\"\n\"\\x20\\x00\\x04\\x00\\x38\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x38\\x00\\x00\\x00\\x39\\x00\\x00\\x00\\x03\\x00\\x00\\x00\"\n\"\\x20\\x00\\x04\\x00\\x3A\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x3A\\x00\\x00\\x00\\x3B\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x32\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x3D\\x00\\x00\\x00\\x00\\x00\\x80\\x43\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x3E\\x00\\x00\\x00\\x00\\x00\\xB4\\x42\"\n\"\\x1E\\x00\\x03\\x00\\x42\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x43\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x42\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\"\n\"\\x43\\x00\\x00\\x00\\x44\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x46\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x1E\\x00\\x0C\\x00\"\n\"\\x48\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x49\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x48\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\"\n\"\\x49\\x00\\x00\\x00\\x4A\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x4B\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\"\n\"\\x4B\\x00\\x00\\x00\\x4C\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x3A\\x00\\x00\\x00\\x4D\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\"\n\"\\x1B\\x00\\x00\\x00\\x4E\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\\x4F\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\"\n\"\\x1B\\x00\\x00\\x00\\x50\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x36\\x00\\x05\\x00\\x02\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x03\\x00\\x00\\x00\"\n\"\\xF8\\x00\\x02\\x00\\x05\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x12\\x00\\x00\\x00\\x3C\\x00\\x00\\x00\\x3B\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x39\\x00\\x00\\x00\"\n\"\\x3C\\x00\\x00\\x00\\x88\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x3F\\x00\\x00\\x00\\x3D\\x00\\x00\\x00\\x3E\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x12\\x00\\x00\\x00\"\n\"\\x40\\x00\\x00\\x00\\x39\\x00\\x00\\x00\\x8E\\x00\\x05\\x00\\x12\\x00\\x00\\x00\\x41\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x3F\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\"\n\"\\x39\\x00\\x00\\x00\\x41\\x00\\x00\\x00\\x39\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x45\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x46\\x00\\x00\\x00\"\n\"\\x47\\x00\\x00\\x00\\x44\\x00\\x00\\x00\\x17\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x47\\x00\\x00\\x00\\x45\\x00\\x00\\x00\\xFD\\x00\\x01\\x00\\x38\\x00\\x01\\x00\"\n\"\\x36\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x0A\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\"\n\"\\x0B\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x18\\x00\\x00\\x00\\x19\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x17\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\x1A\\x00\\x00\\x00\\x19\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0E\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x1C\\x00\\x00\\x00\"\n\"\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x1F\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x20\\x00\\x00\\x00\"\n\"\\x1D\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x50\\x00\\x07\\x00\"\n\"\\x07\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x1F\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x91\\x00\\x05\\x00\\x07\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x1A\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x0C\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x26\\x00\\x00\\x00\"\n\"\\x27\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x25\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x28\\x00\\x00\\x00\\x27\\x00\\x00\\x00\\x85\\x00\\x05\\x00\"\n\"\\x06\\x00\\x00\\x00\\x29\\x00\\x00\\x00\\x28\\x00\\x00\\x00\\x24\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x26\\x00\\x00\\x00\\x2A\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\"\n\"\\x25\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x2A\\x00\\x00\\x00\\x29\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x26\\x00\\x00\\x00\\x2C\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\"\n\"\\x2B\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\\x2C\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x26\\x00\\x00\\x00\\x2F\\x00\\x00\\x00\"\n\"\\x0C\\x00\\x00\\x00\\x2E\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x30\\x00\\x00\\x00\\x2F\\x00\\x00\\x00\\x81\\x00\\x05\\x00\\x06\\x00\\x00\\x00\"\n\"\\x31\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\\x30\\x00\\x00\\x00\\x88\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x33\\x00\\x00\\x00\\x31\\x00\\x00\\x00\\x32\\x00\\x00\\x00\"\n\"\\x41\\x00\\x05\\x00\\x26\\x00\\x00\\x00\\x34\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x2B\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x34\\x00\\x00\\x00\\x33\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x35\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\xFE\\x00\\x02\\x00\\x35\\x00\\x00\\x00\\x38\\x00\\x01\\x00\\x03\\x02\\x23\\x07\"\n\"\\x00\\x00\\x01\\x00\\x0A\\x00\\x08\\x00\\x56\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x11\\x00\\x02\\x00\\x01\\x00\\x00\\x00\\x0B\\x00\\x06\\x00\\x01\\x00\\x00\\x00\"\n\"\\x47\\x4C\\x53\\x4C\\x2E\\x73\\x74\\x64\\x2E\\x34\\x35\\x30\\x00\\x00\\x00\\x00\\x0E\\x00\\x03\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x0F\\x00\\x07\\x00\"\n\"\\x04\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x6D\\x61\\x69\\x6E\\x00\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x44\\x00\\x00\\x00\\x10\\x00\\x03\\x00\\x04\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x44\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x48\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x48\\x00\\x00\\x00\\x21\\x00\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x4C\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\x4F\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x50\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x50\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x50\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\"\n\"\\x50\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x50\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x50\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x50\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x50\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x50\\x00\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x50\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xC0\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x50\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xCC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x50\\x00\\x00\\x00\\x05\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\xD0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x50\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xDC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x50\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xE0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x50\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\xEC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x50\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xF0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x50\\x00\\x00\\x00\"\n\"\\x0A\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xFC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x50\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x01\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x50\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x50\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x4C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x50\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x50\\x01\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x50\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x5C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x50\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x60\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x50\\x00\\x00\\x00\\x11\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x6C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x50\\x00\\x00\\x00\"\n\"\\x12\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x70\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x50\\x00\\x00\\x00\\x13\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x01\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x50\\x00\\x00\\x00\\x14\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x90\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x50\\x00\\x00\\x00\\x15\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x94\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x50\\x00\\x00\\x00\\x16\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x98\\x01\\x00\\x00\\x47\\x00\\x03\\x00\"\n\"\\x50\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x52\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x52\\x00\\x00\\x00\"\n\"\\x21\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x53\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x53\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x53\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x53\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x53\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x4C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x53\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x50\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x53\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x5C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x53\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x60\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x53\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x6C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x53\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x70\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x53\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x53\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x88\\x00\\x00\\x00\\x47\\x00\\x03\\x00\\x53\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x55\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x55\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x13\\x00\\x02\\x00\\x02\\x00\\x00\\x00\\x21\\x00\\x03\\x00\\x03\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x16\\x00\\x03\\x00\\x06\\x00\\x00\\x00\\x20\\x00\\x00\\x00\"\n\"\\x17\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x08\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x20\\x00\\x04\\x00\\x0A\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x0A\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x20\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\\x00\\x00\\x80\\x3F\"\n\"\\x15\\x00\\x04\\x00\\x10\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x10\\x00\\x00\\x00\\x11\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x2B\\x00\\x04\\x00\\x10\\x00\\x00\\x00\\x18\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x17\\x00\\x04\\x00\\x1F\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x03\\x00\\x00\\x00\"\n\"\\x20\\x00\\x04\\x00\\x20\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x1F\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x40\"\n\"\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x29\\x00\\x00\\x00\\x00\\x00\\x00\\xC0\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x30\\x00\\x00\\x00\\x00\\x00\\x80\\xBF\"\n\"\\x2B\\x00\\x04\\x00\\x10\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x17\\x00\\x04\\x00\\x42\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x04\\x00\\x00\\x00\"\n\"\\x20\\x00\\x04\\x00\\x43\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x42\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x43\\x00\\x00\\x00\\x44\\x00\\x00\\x00\\x03\\x00\\x00\\x00\"\n\"\\x19\\x00\\x09\\x00\\x45\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x1B\\x00\\x03\\x00\\x46\\x00\\x00\\x00\\x45\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x47\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x46\\x00\\x00\\x00\"\n\"\\x3B\\x00\\x04\\x00\\x47\\x00\\x00\\x00\\x48\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x32\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x4C\\x00\\x00\\x00\\x00\\x00\\x80\\x43\"\n\"\\x18\\x00\\x04\\x00\\x4D\\x00\\x00\\x00\\x42\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x10\\x00\\x00\\x00\\x4E\\x00\\x00\\x00\\x04\\x00\\x00\\x00\"\n\"\\x1C\\x00\\x04\\x00\\x4F\\x00\\x00\\x00\\x42\\x00\\x00\\x00\\x4E\\x00\\x00\\x00\\x1E\\x00\\x19\\x00\\x50\\x00\\x00\\x00\\x4D\\x00\\x00\\x00\\x4D\\x00\\x00\\x00\"\n\"\\x4D\\x00\\x00\\x00\\x1F\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x1F\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x1F\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x1F\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x4F\\x00\\x00\\x00\\x1F\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x1F\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x1F\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x42\\x00\\x00\\x00\\x42\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x51\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x50\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x51\\x00\\x00\\x00\\x52\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x1E\\x00\\x0C\\x00\\x53\\x00\\x00\\x00\\x4D\\x00\\x00\\x00\"\n\"\\x1F\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x1F\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x1F\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x42\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x54\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x53\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x54\\x00\\x00\\x00\\x55\\x00\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\x36\\x00\\x05\\x00\\x02\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x05\\x00\\x00\\x00\"\n\"\\x3B\\x00\\x04\\x00\\x08\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x3B\\x00\\x04\\x00\\x20\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\"\n\"\\x3E\\x00\\x03\\x00\\x09\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x0D\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x11\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x13\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x0D\\x00\\x00\\x00\\x14\\x00\\x00\\x00\\x09\\x00\\x00\\x00\"\n\"\\x11\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x14\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x16\\x00\\x00\\x00\"\n\"\\x13\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x81\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x17\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\\x16\\x00\\x00\\x00\\x41\\x00\\x05\\x00\"\n\"\\x0D\\x00\\x00\\x00\\x19\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x18\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x1A\\x00\\x00\\x00\\x19\\x00\\x00\\x00\"\n\"\\x41\\x00\\x05\\x00\\x0D\\x00\\x00\\x00\\x1B\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x18\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x1C\\x00\\x00\\x00\"\n\"\\x1B\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x1A\\x00\\x00\\x00\\x1C\\x00\\x00\\x00\\x81\\x00\\x05\\x00\\x06\\x00\\x00\\x00\"\n\"\\x1E\\x00\\x00\\x00\\x17\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x0E\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x0D\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x11\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x24\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x85\\x00\\x05\\x00\"\n\"\\x06\\x00\\x00\\x00\\x25\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x24\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x26\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\"\n\"\\x88\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x27\\x00\\x00\\x00\\x25\\x00\\x00\\x00\\x26\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x0D\\x00\\x00\\x00\\x28\\x00\\x00\\x00\"\n\"\\x21\\x00\\x00\\x00\\x11\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x28\\x00\\x00\\x00\\x27\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x0D\\x00\\x00\\x00\\x2A\\x00\\x00\\x00\"\n\"\\x09\\x00\\x00\\x00\\x18\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x2B\\x00\\x00\\x00\\x2A\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\"\n\"\\x2C\\x00\\x00\\x00\\x29\\x00\\x00\\x00\\x2B\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x88\\x00\\x05\\x00\"\n\"\\x06\\x00\\x00\\x00\\x2E\\x00\\x00\\x00\\x2C\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x0D\\x00\\x00\\x00\\x2F\\x00\\x00\\x00\\x21\\x00\\x00\\x00\"\n\"\\x18\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x2F\\x00\\x00\\x00\\x2E\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x0D\\x00\\x00\\x00\\x31\\x00\\x00\\x00\\x09\\x00\\x00\\x00\"\n\"\\x11\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x32\\x00\\x00\\x00\\x31\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x0D\\x00\\x00\\x00\\x33\\x00\\x00\\x00\"\n\"\\x09\\x00\\x00\\x00\\x11\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x34\\x00\\x00\\x00\\x33\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\"\n\"\\x35\\x00\\x00\\x00\\x32\\x00\\x00\\x00\\x34\\x00\\x00\\x00\\x81\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x36\\x00\\x00\\x00\\x30\\x00\\x00\\x00\\x35\\x00\\x00\\x00\"\n\"\\x41\\x00\\x05\\x00\\x0D\\x00\\x00\\x00\\x37\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x18\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x38\\x00\\x00\\x00\"\n\"\\x37\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x0D\\x00\\x00\\x00\\x39\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x18\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\"\n\"\\x3A\\x00\\x00\\x00\\x39\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x3B\\x00\\x00\\x00\\x38\\x00\\x00\\x00\\x3A\\x00\\x00\\x00\\x81\\x00\\x05\\x00\"\n\"\\x06\\x00\\x00\\x00\\x3C\\x00\\x00\\x00\\x36\\x00\\x00\\x00\\x3B\\x00\\x00\\x00\\x7F\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x3D\\x00\\x00\\x00\\x3C\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x3E\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x88\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x3F\\x00\\x00\\x00\\x3D\\x00\\x00\\x00\"\n\"\\x3E\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x0D\\x00\\x00\\x00\\x41\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x41\\x00\\x00\\x00\"\n\"\\x3F\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x46\\x00\\x00\\x00\\x49\\x00\\x00\\x00\\x48\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x1F\\x00\\x00\\x00\\x4A\\x00\\x00\\x00\"\n\"\\x21\\x00\\x00\\x00\\x57\\x00\\x05\\x00\\x42\\x00\\x00\\x00\\x4B\\x00\\x00\\x00\\x49\\x00\\x00\\x00\\x4A\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x44\\x00\\x00\\x00\"\n\"\\x4B\\x00\\x00\\x00\\xFD\\x00\\x01\\x00\\x38\\x00\\x01\\x00\"},\n#endif\n#ifdef GLQUAKE\n{QR_OPENGL, 110, \"postproc_equirectangular\",\n\"!!cvarf ffov\\n\"\n\"!!samps screen:samplerCube=0\\n\"\n\n//equirectangular view rendering, commonly used for sphere->2d map projections.\n\n\"#ifdef VERTEX_SHADER\\n\"\n\"attribute vec2 v_texcoord;\\n\"\n\"varying vec2 texcoord;\\n\"\n\"void main()\\n\"\n\"{\\n\"\n\"texcoord = v_texcoord.xy;\\n\"\n\"gl_Position = ftetransform();\\n\"\n\"}\\n\"\n\"#endif\\n\"\n\"#ifdef FRAGMENT_SHADER\\n\"\n\"varying vec2 texcoord;\\n\"\n\"uniform float cvar_ffov;\\n\"\n\n\"#define PI 3.1415926535897932384626433832795\\n\"\n\"void main()\\n\"\n\"{\\n\"\n\"vec3 tc;\\n\"\n\"float lng = (texcoord.x - 0.5) * PI * 2.0;\\n\"\n\"float lat = (texcoord.y) * PI * 1.0;\\n\"\n\n\"tc.z = cos(lng) * sin(lat); \\n\"\n\"tc.x = sin(lng) * sin(lat);\\n\"\n\"tc.y = cos(lat);\\n\"\n\"gl_FragColor = textureCube(s_screen, tc);\\n\"\n\"}\\n\"\n\"#endif\\n\"\n},\n#endif\n#ifdef GLQUAKE\n{QR_OPENGL, 110, \"postproc_panini\",\n\"!!cvarf ffov\\n\"\n\"!!samps screen:samplerCube=0\\n\"\n\n//panini view rendering, for high fovs that are still playable.\n\n\"#ifdef VERTEX_SHADER\\n\"\n\"attribute vec2 v_texcoord;\\n\"\n\"varying vec2 texcoord;\\n\"\n\"uniform float cvar_ffov;\\n\"\n\"void main()\\n\"\n\"{\\n\"\n\"texcoord = v_texcoord.xy;\\n\"\n\n//make sure the ffov cvar actually does something meaningful\n\"texcoord *= cvar_ffov / 90.0;\\n\"\n\n\"gl_Position = ftetransform();\\n\"\n\"}\\n\"\n\"#endif\\n\"\n\"#ifdef FRAGMENT_SHADER\\n\"\n\"varying vec2 texcoord;\\n\"\n\"void main()\\n\"\n\"{\\n\"\n\"vec3 tc;\\n\"\n\"vec2 d;\\n\"\n\"vec2 ang;\\n\"\n\"d = texcoord; \\n\"\n\n//compute the 2d->3d projection\n\"float div = 1.0 + d.x*d.x + d.y*d.y;\\n\"\n\"tc.x = 2.0*d.x/div;\\n\"\n\"tc.y = -d.y;\\n\"\n\"tc.z = -(-1.0 + d.x*d.x + d.y*d.y)/div;\\n\"\n\n\"gl_FragColor = textureCube(s_screen, tc);\\n\"\n\"}\\n\"\n\"#endif\\n\"\n},\n#endif\n#ifdef GLQUAKE\n{QR_OPENGL, 110, \"postproc_ascii\",\n\"!!cvardf r_glsl_ascii_mono=0\\n\"\n\"!!samps screen=0\\n\"\n\n//derived from https://www.shadertoy.com/view/lssGDj\n\n\"#include \\\"sys/defs.h\\\"\\n\"\n\"varying vec2 texcoord;\\n\"\n\n\"#ifdef VERTEX_SHADER\\n\"\n\"void main()\\n\"\n\"{\\n\"\n\"texcoord = v_texcoord.xy;\\n\"\n\"texcoord.y = 1.0 - texcoord.y;\\n\"\n\"gl_Position = ftetransform();\\n\"\n\"}\\n\"\n\"#endif\\n\"\n\"#ifdef FRAGMENT_SHADER\\n\"\n\"uniform vec2 e_sourcesize;\\n\"\n\n\"float character(float n, vec2 p)\\n\"\n\"{\\n\"\n\"p = floor(p*vec2(4.0, -4.0) + 2.5);\\n\"\n\"if (clamp(p.x, 0.0, 4.0) == p.x && clamp(p.y, 0.0, 4.0) == p.y)\\n\"\n\"{\\n\"\n\"if (int(mod(n/exp2(p.x + 5.0*p.y), 2.0)) == 1) return 1.0;\\n\"\n\"} \\n\"\n\"return 0.0;\\n\"\n\"}\\n\"\n\n\"void main(void)\\n\"\n\"{\\n\"\n\"vec2 uv = floor(texcoord.xy * e_sourcesize); //in pixels.\\n\"\n\"vec3 col = texture2D(s_screen, (floor(uv/8.0)*8.0+4.0)/e_sourcesize.xy).rgb; \\n\"\n\n\"float gray = 0.3 * col.r + 0.59 * col.g + 0.11 * col.b;\\n\"\n\n\"if (float(r_glsl_ascii_mono) != 0.0)\\n\"\n\"gray = gray = pow(gray, 0.7); //quake is just too dark otherwise.\\n\"\n\"else\\n\"\n\"gray = gray = pow(gray, 0.45); //col*char is FAR too dark otherwise, and much of the colour will come from the col term anyway.\\n\"\n\n\"float n =  0.0;              // space\\n\"\n\"if (gray > 0.1) n = 4096.0; // .\\n\"\n\"if (gray > 0.2) n = 65600.0;    // :\\n\"\n\"if (gray > 0.3) n = 332772.0;   // *\\n\"\n\"if (gray > 0.4) n = 15255086.0; // o \\n\"\n\"if (gray > 0.5) n = 23385164.0; // &\\n\"\n\"if (gray > 0.6) n = 15252014.0; // 8\\n\"\n\"if (gray > 0.7) n = 13199452.0; // @\\n\"\n\"if (gray > 0.8) n = 11512810.0; // #\\n\"\n\n\"vec2 p = mod(uv/4.0, 2.0) - vec2(1.0);\\n\"\n\"if (float(r_glsl_ascii_mono) != 0.0)\\n\"\n\"col = vec3(character(n, p));\\n\"\n\"else\\n\"\n\"col = col*character(n, p); //note that this is kinda cheating.\\n\"\n\"gl_FragColor = vec4(col, 1.0);\\n\"\n\"}\\n\"\n\"#endif\\n\"\n},\n#endif\n#ifdef GLQUAKE\n{QR_OPENGL, 110, \"fxaa\",\n\"!!samps 1\\n\"\n\"#include \\\"sys/defs.h\\\"\\n\"\n//\n//This shader implements super-sampled anti-aliasing.\n//\n\n\"varying vec2 texcoord;\\n\"\n\n\"#ifdef VERTEX_SHADER\\n\"\n\"void main()\\n\"\n\"{\\n\"\n\"texcoord = v_texcoord.xy;\\n\"\n\"texcoord.y = 1.0 - texcoord.y;\\n\"\n\"gl_Position = ftetransform();\\n\"\n\"}\\n\"\n\"#endif\\n\"\n\"#ifdef FRAGMENT_SHADER\\n\"\n\"uniform vec2 e_sourcesize;\\n\"\n\n\"void main( void )\\n\"\n\"{\\n\"\n\"float FXAA_SPAN_MAX = 8.0;\\n\"\n\"float FXAA_REDUCE_MUL = 1.0/8.0;\\n\"\n\"float FXAA_REDUCE_MIN = 1.0/128.0;\\n\"\n\n\"vec3 rgbNW=texture2D(s_t0,texcoord+(vec2(-1.0,-1.0)/e_sourcesize)).xyz;\\n\"\n\"vec3 rgbNE=texture2D(s_t0,texcoord+(vec2(1.0,-1.0)/e_sourcesize)).xyz;\\n\"\n\"vec3 rgbSW=texture2D(s_t0,texcoord+(vec2(-1.0,1.0)/e_sourcesize)).xyz;\\n\"\n\"vec3 rgbSE=texture2D(s_t0,texcoord+(vec2(1.0,1.0)/e_sourcesize)).xyz;\\n\"\n\"vec3 rgbM=texture2D(s_t0,texcoord).xyz;\\n\"\n\n\"vec3 luma=vec3(0.299, 0.587, 0.114);\\n\"\n\"float lumaNW = dot(rgbNW, luma);\\n\"\n\"float lumaNE = dot(rgbNE, luma);\\n\"\n\"float lumaSW = dot(rgbSW, luma);\\n\"\n\"float lumaSE = dot(rgbSE, luma);\\n\"\n\"float lumaM  = dot(rgbM,  luma);\\n\"\n\n\"float lumaMin = min(lumaM, min(min(lumaNW, lumaNE), min(lumaSW, lumaSE)));\\n\"\n\"float lumaMax = max(lumaM, max(max(lumaNW, lumaNE), max(lumaSW, lumaSE)));\\n\"\n\n\"vec2 dir;\\n\"\n\"dir.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE));\\n\"\n\"dir.y =  ((lumaNW + lumaSW) - (lumaNE + lumaSE));\\n\"\n\n\"float dirReduce = max((lumaNW + lumaNE + lumaSW + lumaSE) * (0.25 * FXAA_REDUCE_MUL), FXAA_REDUCE_MIN);\\n\"\n\n\"float rcpDirMin = 1.0/(min(abs(dir.x), abs(dir.y)) + dirReduce);\\n\"\n\n\"dir = min(vec2( FXAA_SPAN_MAX,  FXAA_SPAN_MAX),\\n\"\n\"max(vec2(-FXAA_SPAN_MAX, -FXAA_SPAN_MAX),\\n\"\n\"dir * rcpDirMin)) / e_sourcesize;\\n\"\n\n\"vec3 rgbA = (1.0/2.0) * (texture2D(s_t0, texcoord.xy + dir * (1.0/3.0 - 0.5)).xyz + texture2D(s_t0, texcoord.xy + dir * (2.0/3.0 - 0.5)).xyz);\\n\"\n\"vec3 rgbB = rgbA * (1.0/2.0) + (1.0/4.0) * (texture2D(s_t0, texcoord.xy + dir * (0.0/3.0 - 0.5)).xyz + texture2D(s_t0, texcoord.xy + dir * (3.0/3.0 - 0.5)).xyz);\\n\"\n\"float lumaB = dot(rgbB, luma);\\n\"\n\n\"if((lumaB < lumaMin) || (lumaB > lumaMax))\\n\"\n\"gl_FragColor.xyz=rgbA;\\n\"\n\"else\\n\"\n\"gl_FragColor.xyz=rgbB;\\n\"\n\"gl_FragColor.a = 1.0;\\n\"\n\"}\\n\"\n\"#endif\\n\"\n},\n#endif\n#ifdef VKQUAKE\n{QR_VULKAN, -1, \"fxaa\",\n\"\\xFF\\x53\\x50\\x56\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x2C\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x2C\\x00\\x00\\x00\"\n\"\\xD0\\x0A\\x00\\x00\\xFC\\x0A\\x00\\x00\\x08\\x18\\x00\\x00\\x03\\x02\\x23\\x07\\x00\\x00\\x01\\x00\\x0A\\x00\\x08\\x00\\x54\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x11\\x00\\x02\\x00\\x01\\x00\\x00\\x00\\x0B\\x00\\x06\\x00\\x01\\x00\\x00\\x00\\x47\\x4C\\x53\\x4C\\x2E\\x73\\x74\\x64\\x2E\\x34\\x35\\x30\\x00\\x00\\x00\\x00\"\n\"\\x0E\\x00\\x03\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x0F\\x00\\x0F\\x00\\x00\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x6D\\x61\\x69\\x6E\\x00\\x00\\x00\\x00\"\n\"\\x1C\\x00\\x00\\x00\\x39\\x00\\x00\\x00\\x3B\\x00\\x00\\x00\\x42\\x00\\x00\\x00\\x44\\x00\\x00\\x00\\x49\\x00\\x00\\x00\\x50\\x00\\x00\\x00\\x51\\x00\\x00\\x00\"\n\"\\x52\\x00\\x00\\x00\\x53\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x11\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x13\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x13\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x13\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x05\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x13\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xC0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x04\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\xCC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xD0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x13\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xDC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\xE0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xEC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\"\n\"\\x09\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xF0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x0A\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xFC\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x40\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x4C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x13\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x50\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x5C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x60\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\"\n\"\\x11\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x6C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x70\\x01\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x13\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x14\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x90\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x94\\x01\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x13\\x00\\x00\\x00\\x16\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x98\\x01\\x00\\x00\\x47\\x00\\x03\\x00\\x13\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\"\n\"\\x15\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x15\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\"\n\"\\x1C\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x39\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\"\n\"\\x3B\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x42\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x47\\x00\\x04\\x00\"\n\"\\x44\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x47\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x47\\x00\\x03\\x00\\x47\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x4D\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x4D\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x4D\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x10\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x4D\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x4D\\x00\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x4C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x4D\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x50\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x4D\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x5C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x4D\\x00\\x00\\x00\\x05\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x60\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x4D\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x6C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x4D\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x70\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x4D\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x80\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x4D\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x88\\x00\\x00\\x00\\x47\\x00\\x03\\x00\\x4D\\x00\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x4F\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x4F\\x00\\x00\\x00\\x21\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x50\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x51\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\"\n\"\\x04\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x52\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x53\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x13\\x00\\x02\\x00\\x02\\x00\\x00\\x00\\x21\\x00\\x03\\x00\\x03\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x16\\x00\\x03\\x00\\x06\\x00\\x00\\x00\"\n\"\\x20\\x00\\x00\\x00\\x17\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x21\\x00\\x03\\x00\\x08\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x20\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x18\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x04\\x00\\x00\\x00\"\n\"\\x17\\x00\\x04\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x15\\x00\\x04\\x00\\x0F\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x2B\\x00\\x04\\x00\\x0F\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x1C\\x00\\x04\\x00\\x11\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\"\n\"\\x17\\x00\\x04\\x00\\x12\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x1E\\x00\\x19\\x00\\x13\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\"\n\"\\x0D\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x11\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x14\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x13\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x14\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x15\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x20\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x17\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x18\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x0D\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\\x1C\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x00\\x00\\x80\\x3F\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x24\\x00\\x00\\x00\"\n\"\\x00\\x00\\x80\\xBF\\x2B\\x00\\x04\\x00\\x0F\\x00\\x00\\x00\\x25\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x26\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x0F\\x00\\x00\\x00\\x2B\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x0F\\x00\\x00\\x00\\x2E\\x00\\x00\\x00\"\n\"\\x03\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x32\\x00\\x00\\x00\\x00\\x00\\x00\\x40\\x20\\x00\\x04\\x00\\x38\\x00\\x00\\x00\\x03\\x00\\x00\\x00\"\n\"\\x12\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x38\\x00\\x00\\x00\\x39\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x3A\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x12\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x3A\\x00\\x00\\x00\\x3B\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x3D\\x00\\x00\\x00\\x03\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x38\\x00\\x00\\x00\\x42\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x43\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x43\\x00\\x00\\x00\\x44\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x1E\\x00\\x03\\x00\\x47\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x20\\x00\\x04\\x00\\x48\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x47\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x48\\x00\\x00\\x00\\x49\\x00\\x00\\x00\\x03\\x00\\x00\\x00\"\n\"\\x20\\x00\\x04\\x00\\x4B\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x1E\\x00\\x0C\\x00\\x4D\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x12\\x00\\x00\\x00\"\n\"\\x20\\x00\\x04\\x00\\x4E\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x4D\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x4E\\x00\\x00\\x00\\x4F\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x3B\\x00\\x04\\x00\\x3A\\x00\\x00\\x00\\x50\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\\x51\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x3B\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\\x52\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\\x53\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x36\\x00\\x05\\x00\\x02\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x05\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x12\\x00\\x00\\x00\\x3C\\x00\\x00\\x00\\x3B\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x39\\x00\\x00\\x00\\x3C\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x3D\\x00\\x00\\x00\"\n\"\\x3E\\x00\\x00\\x00\\x39\\x00\\x00\\x00\\x25\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x3F\\x00\\x00\\x00\\x3E\\x00\\x00\\x00\\x83\\x00\\x05\\x00\"\n\"\\x06\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x3F\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x3D\\x00\\x00\\x00\\x41\\x00\\x00\\x00\\x39\\x00\\x00\\x00\"\n\"\\x25\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x41\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x45\\x00\\x00\\x00\\x44\\x00\\x00\\x00\"\n\"\\x4F\\x00\\x07\\x00\\x12\\x00\\x00\\x00\\x46\\x00\\x00\\x00\\x45\\x00\\x00\\x00\\x45\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\"\n\"\\x42\\x00\\x00\\x00\\x46\\x00\\x00\\x00\\x39\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x4A\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x4B\\x00\\x00\\x00\"\n\"\\x4C\\x00\\x00\\x00\\x49\\x00\\x00\\x00\\x17\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x4C\\x00\\x00\\x00\\x4A\\x00\\x00\\x00\\xFD\\x00\\x01\\x00\\x38\\x00\\x01\\x00\"\n\"\\x36\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x0A\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\"\n\"\\x0B\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x18\\x00\\x00\\x00\\x19\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x17\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\x1A\\x00\\x00\\x00\\x19\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0E\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x1C\\x00\\x00\\x00\"\n\"\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x1F\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x20\\x00\\x00\\x00\"\n\"\\x1D\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x50\\x00\\x07\\x00\"\n\"\\x07\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x1F\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x91\\x00\\x05\\x00\\x07\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x1A\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x0C\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x26\\x00\\x00\\x00\"\n\"\\x27\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x25\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x28\\x00\\x00\\x00\\x27\\x00\\x00\\x00\\x85\\x00\\x05\\x00\"\n\"\\x06\\x00\\x00\\x00\\x29\\x00\\x00\\x00\\x28\\x00\\x00\\x00\\x24\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x26\\x00\\x00\\x00\\x2A\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\"\n\"\\x25\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x2A\\x00\\x00\\x00\\x29\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x26\\x00\\x00\\x00\\x2C\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\"\n\"\\x2B\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\\x2C\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x26\\x00\\x00\\x00\\x2F\\x00\\x00\\x00\"\n\"\\x0C\\x00\\x00\\x00\\x2E\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x30\\x00\\x00\\x00\\x2F\\x00\\x00\\x00\\x81\\x00\\x05\\x00\\x06\\x00\\x00\\x00\"\n\"\\x31\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\\x30\\x00\\x00\\x00\\x88\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x33\\x00\\x00\\x00\\x31\\x00\\x00\\x00\\x32\\x00\\x00\\x00\"\n\"\\x41\\x00\\x05\\x00\\x26\\x00\\x00\\x00\\x34\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x2B\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x34\\x00\\x00\\x00\\x33\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x35\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\xFE\\x00\\x02\\x00\\x35\\x00\\x00\\x00\\x38\\x00\\x01\\x00\\x03\\x02\\x23\\x07\"\n\"\\x00\\x00\\x01\\x00\\x0A\\x00\\x08\\x00\\xFB\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x11\\x00\\x02\\x00\\x01\\x00\\x00\\x00\\x0B\\x00\\x06\\x00\\x01\\x00\\x00\\x00\"\n\"\\x47\\x4C\\x53\\x4C\\x2E\\x73\\x74\\x64\\x2E\\x34\\x35\\x30\\x00\\x00\\x00\\x00\\x0E\\x00\\x03\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x0F\\x00\\x08\\x00\"\n\"\\x04\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x6D\\x61\\x69\\x6E\\x00\\x00\\x00\\x00\\x18\\x00\\x00\\x00\\x1C\\x00\\x00\\x00\\xE7\\x00\\x00\\x00\\x10\\x00\\x03\\x00\"\n\"\\x04\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x14\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x14\\x00\\x00\\x00\"\n\"\\x21\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x18\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x1C\\x00\\x00\\x00\"\n\"\\x1E\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\xE7\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\xF4\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\xF5\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\xF5\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\xF5\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\"\n\"\\x48\\x00\\x04\\x00\\xF5\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\xF5\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x40\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\xF5\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\xF5\\x00\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\xF5\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\xF5\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\xF5\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\xC0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\xF5\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xCC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\xF5\\x00\\x00\\x00\"\n\"\\x05\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xD0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\xF5\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xDC\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\xF5\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xE0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\xF5\\x00\\x00\\x00\\x08\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\xEC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\xF5\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xF0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\xF5\\x00\\x00\\x00\\x0A\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xFC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\xF5\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x00\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\xF5\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\xF5\\x00\\x00\\x00\"\n\"\\x0D\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x4C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\xF5\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x50\\x01\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\xF5\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x5C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\xF5\\x00\\x00\\x00\\x10\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x60\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\xF5\\x00\\x00\\x00\\x11\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x6C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\xF5\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x70\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\xF5\\x00\\x00\\x00\\x13\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x80\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\xF5\\x00\\x00\\x00\\x14\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x90\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\xF5\\x00\\x00\\x00\"\n\"\\x15\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x94\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\xF5\\x00\\x00\\x00\\x16\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x98\\x01\\x00\\x00\"\n\"\\x47\\x00\\x03\\x00\\xF5\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\xF7\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\"\n\"\\xF7\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\xF8\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\xF8\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\xF8\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x10\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\xF8\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\xF8\\x00\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x4C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\xF8\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x50\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\xF8\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x5C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\xF8\\x00\\x00\\x00\\x05\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x60\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\xF8\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x6C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\xF8\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x70\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\xF8\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x80\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\xF8\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x88\\x00\\x00\\x00\\x47\\x00\\x03\\x00\\xF8\\x00\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\xFA\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\xFA\\x00\\x00\\x00\\x21\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x13\\x00\\x02\\x00\\x02\\x00\\x00\\x00\\x21\\x00\\x03\\x00\\x03\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x16\\x00\\x03\\x00\\x06\\x00\\x00\\x00\"\n\"\\x20\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x09\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x41\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x00\\x00\\x00\\x3E\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x3C\\x17\\x00\\x04\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x0F\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x0E\\x00\\x00\\x00\\x19\\x00\\x09\\x00\\x11\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x1B\\x00\\x03\\x00\\x12\\x00\\x00\\x00\\x11\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x13\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x12\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x13\\x00\\x00\\x00\\x14\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x17\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x17\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x16\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x17\\x00\\x00\\x00\\x18\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x1A\\x00\\x00\\x00\\x00\\x00\\x80\\xBF\\x2C\\x00\\x05\\x00\\x16\\x00\\x00\\x00\\x1B\\x00\\x00\\x00\"\n\"\\x1A\\x00\\x00\\x00\\x1A\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x17\\x00\\x00\\x00\\x1C\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x17\\x00\\x04\\x00\\x20\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x26\\x00\\x00\\x00\\x00\\x00\\x80\\x3F\\x2C\\x00\\x05\\x00\\x16\\x00\\x00\\x00\"\n\"\\x27\\x00\\x00\\x00\\x26\\x00\\x00\\x00\\x1A\\x00\\x00\\x00\\x2C\\x00\\x05\\x00\\x16\\x00\\x00\\x00\\x30\\x00\\x00\\x00\\x1A\\x00\\x00\\x00\\x26\\x00\\x00\\x00\"\n\"\\x2C\\x00\\x05\\x00\\x16\\x00\\x00\\x00\\x39\\x00\\x00\\x00\\x26\\x00\\x00\\x00\\x26\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x45\\x00\\x00\\x00\"\n\"\\x87\\x16\\x99\\x3E\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x46\\x00\\x00\\x00\\xA2\\x45\\x16\\x3F\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x47\\x00\\x00\\x00\"\n\"\\xD5\\x78\\xE9\\x3D\\x2C\\x00\\x06\\x00\\x0E\\x00\\x00\\x00\\x48\\x00\\x00\\x00\\x45\\x00\\x00\\x00\\x46\\x00\\x00\\x00\\x47\\x00\\x00\\x00\\x20\\x00\\x04\\x00\"\n\"\\x71\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x16\\x00\\x00\\x00\\x15\\x00\\x04\\x00\\x7B\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\"\n\"\\x7B\\x00\\x00\\x00\\x7C\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x7B\\x00\\x00\\x00\\x85\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\"\n\"\\x06\\x00\\x00\\x00\\x8F\\x00\\x00\\x00\\x00\\x00\\x80\\x3E\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\xB0\\x00\\x00\\x00\\x00\\x00\\x00\\x3F\\x2B\\x00\\x04\\x00\"\n\"\\x06\\x00\\x00\\x00\\xB4\\x00\\x00\\x00\\xAB\\xAA\\x2A\\xBE\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\xBC\\x00\\x00\\x00\\xAB\\xAA\\x2A\\x3E\\x2B\\x00\\x04\\x00\"\n\"\\x06\\x00\\x00\\x00\\xC9\\x00\\x00\\x00\\x00\\x00\\x00\\xBF\\x14\\x00\\x02\\x00\\xDE\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\xE6\\x00\\x00\\x00\\x03\\x00\\x00\\x00\"\n\"\\x20\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\xE6\\x00\\x00\\x00\\xE7\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x7B\\x00\\x00\\x00\\xEF\\x00\\x00\\x00\"\n\"\\x03\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\xF0\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x18\\x00\\x04\\x00\\xF2\\x00\\x00\\x00\\x20\\x00\\x00\\x00\"\n\"\\x04\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x7B\\x00\\x00\\x00\\xF3\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x1C\\x00\\x04\\x00\\xF4\\x00\\x00\\x00\\x20\\x00\\x00\\x00\"\n\"\\xF3\\x00\\x00\\x00\\x1E\\x00\\x19\\x00\\xF5\\x00\\x00\\x00\\xF2\\x00\\x00\\x00\\xF2\\x00\\x00\\x00\\xF2\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\xF4\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x16\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\xF6\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\xF5\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\xF6\\x00\\x00\\x00\"\n\"\\xF7\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x1E\\x00\\x0C\\x00\\xF8\\x00\\x00\\x00\\xF2\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x16\\x00\\x00\\x00\\x16\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\xF9\\x00\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\xF8\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\xF9\\x00\\x00\\x00\\xFA\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x36\\x00\\x05\\x00\\x02\\x00\\x00\\x00\"\n\"\\x04\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x05\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x08\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x0A\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x0F\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x0F\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x0F\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x0F\\x00\\x00\\x00\\x36\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x0F\\x00\\x00\\x00\\x3F\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x0F\\x00\\x00\\x00\\x44\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x49\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x4D\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x51\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x55\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x59\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x5D\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x67\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x71\\x00\\x00\\x00\\x72\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x87\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x95\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x0F\\x00\\x00\\x00\\xAF\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x0F\\x00\\x00\\x00\\xC3\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\xD8\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x08\\x00\\x00\\x00\\x09\\x00\\x00\\x00\"\n\"\\x3E\\x00\\x03\\x00\\x0A\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x0C\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x12\\x00\\x00\\x00\"\n\"\\x15\\x00\\x00\\x00\\x14\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x19\\x00\\x00\\x00\\x18\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x16\\x00\\x00\\x00\"\n\"\\x1D\\x00\\x00\\x00\\x1C\\x00\\x00\\x00\\x88\\x00\\x05\\x00\\x16\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x1B\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x81\\x00\\x05\\x00\"\n\"\\x16\\x00\\x00\\x00\\x1F\\x00\\x00\\x00\\x19\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x57\\x00\\x05\\x00\\x20\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x15\\x00\\x00\\x00\"\n\"\\x1F\\x00\\x00\\x00\\x4F\\x00\\x08\\x00\\x0E\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x10\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x12\\x00\\x00\\x00\\x24\\x00\\x00\\x00\\x14\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x25\\x00\\x00\\x00\\x18\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x28\\x00\\x00\\x00\\x1C\\x00\\x00\\x00\"\n\"\\x88\\x00\\x05\\x00\\x16\\x00\\x00\\x00\\x29\\x00\\x00\\x00\\x27\\x00\\x00\\x00\\x28\\x00\\x00\\x00\\x81\\x00\\x05\\x00\\x16\\x00\\x00\\x00\\x2A\\x00\\x00\\x00\"\n\"\\x25\\x00\\x00\\x00\\x29\\x00\\x00\\x00\\x57\\x00\\x05\\x00\\x20\\x00\\x00\\x00\\x2B\\x00\\x00\\x00\\x24\\x00\\x00\\x00\\x2A\\x00\\x00\\x00\\x4F\\x00\\x08\\x00\"\n\"\\x0E\\x00\\x00\\x00\\x2C\\x00\\x00\\x00\\x2B\\x00\\x00\\x00\\x2B\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\"\n\"\\x23\\x00\\x00\\x00\\x2C\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x12\\x00\\x00\\x00\\x2E\\x00\\x00\\x00\\x14\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x16\\x00\\x00\\x00\"\n\"\\x2F\\x00\\x00\\x00\\x18\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x31\\x00\\x00\\x00\\x1C\\x00\\x00\\x00\\x88\\x00\\x05\\x00\\x16\\x00\\x00\\x00\"\n\"\\x32\\x00\\x00\\x00\\x30\\x00\\x00\\x00\\x31\\x00\\x00\\x00\\x81\\x00\\x05\\x00\\x16\\x00\\x00\\x00\\x33\\x00\\x00\\x00\\x2F\\x00\\x00\\x00\\x32\\x00\\x00\\x00\"\n\"\\x57\\x00\\x05\\x00\\x20\\x00\\x00\\x00\\x34\\x00\\x00\\x00\\x2E\\x00\\x00\\x00\\x33\\x00\\x00\\x00\\x4F\\x00\\x08\\x00\\x0E\\x00\\x00\\x00\\x35\\x00\\x00\\x00\"\n\"\\x34\\x00\\x00\\x00\\x34\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x2D\\x00\\x00\\x00\\x35\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x12\\x00\\x00\\x00\\x37\\x00\\x00\\x00\\x14\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x38\\x00\\x00\\x00\\x18\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x3A\\x00\\x00\\x00\\x1C\\x00\\x00\\x00\\x88\\x00\\x05\\x00\\x16\\x00\\x00\\x00\\x3B\\x00\\x00\\x00\\x39\\x00\\x00\\x00\"\n\"\\x3A\\x00\\x00\\x00\\x81\\x00\\x05\\x00\\x16\\x00\\x00\\x00\\x3C\\x00\\x00\\x00\\x38\\x00\\x00\\x00\\x3B\\x00\\x00\\x00\\x57\\x00\\x05\\x00\\x20\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x00\\x00\\x37\\x00\\x00\\x00\\x3C\\x00\\x00\\x00\\x4F\\x00\\x08\\x00\\x0E\\x00\\x00\\x00\\x3E\\x00\\x00\\x00\\x3D\\x00\\x00\\x00\\x3D\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x36\\x00\\x00\\x00\\x3E\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x12\\x00\\x00\\x00\"\n\"\\x40\\x00\\x00\\x00\\x14\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x41\\x00\\x00\\x00\\x18\\x00\\x00\\x00\\x57\\x00\\x05\\x00\\x20\\x00\\x00\\x00\"\n\"\\x42\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x41\\x00\\x00\\x00\\x4F\\x00\\x08\\x00\\x0E\\x00\\x00\\x00\\x43\\x00\\x00\\x00\\x42\\x00\\x00\\x00\\x42\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x3F\\x00\\x00\\x00\\x43\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x44\\x00\\x00\\x00\"\n\"\\x48\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0E\\x00\\x00\\x00\\x4A\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0E\\x00\\x00\\x00\\x4B\\x00\\x00\\x00\"\n\"\\x44\\x00\\x00\\x00\\x94\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x4C\\x00\\x00\\x00\\x4A\\x00\\x00\\x00\\x4B\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x49\\x00\\x00\\x00\"\n\"\\x4C\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0E\\x00\\x00\\x00\\x4E\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0E\\x00\\x00\\x00\\x4F\\x00\\x00\\x00\"\n\"\\x44\\x00\\x00\\x00\\x94\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x50\\x00\\x00\\x00\\x4E\\x00\\x00\\x00\\x4F\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x4D\\x00\\x00\\x00\"\n\"\\x50\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0E\\x00\\x00\\x00\\x52\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0E\\x00\\x00\\x00\\x53\\x00\\x00\\x00\"\n\"\\x44\\x00\\x00\\x00\\x94\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x54\\x00\\x00\\x00\\x52\\x00\\x00\\x00\\x53\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x51\\x00\\x00\\x00\"\n\"\\x54\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0E\\x00\\x00\\x00\\x56\\x00\\x00\\x00\\x36\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0E\\x00\\x00\\x00\\x57\\x00\\x00\\x00\"\n\"\\x44\\x00\\x00\\x00\\x94\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x58\\x00\\x00\\x00\\x56\\x00\\x00\\x00\\x57\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x55\\x00\\x00\\x00\"\n\"\\x58\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0E\\x00\\x00\\x00\\x5A\\x00\\x00\\x00\\x3F\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0E\\x00\\x00\\x00\\x5B\\x00\\x00\\x00\"\n\"\\x44\\x00\\x00\\x00\\x94\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x5C\\x00\\x00\\x00\\x5A\\x00\\x00\\x00\\x5B\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x59\\x00\\x00\\x00\"\n\"\\x5C\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x5E\\x00\\x00\\x00\\x59\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x5F\\x00\\x00\\x00\"\n\"\\x49\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x60\\x00\\x00\\x00\\x4D\\x00\\x00\\x00\\x0C\\x00\\x07\\x00\\x06\\x00\\x00\\x00\\x61\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x25\\x00\\x00\\x00\\x5F\\x00\\x00\\x00\\x60\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x62\\x00\\x00\\x00\\x51\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x63\\x00\\x00\\x00\\x55\\x00\\x00\\x00\\x0C\\x00\\x07\\x00\\x06\\x00\\x00\\x00\\x64\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x25\\x00\\x00\\x00\\x62\\x00\\x00\\x00\\x63\\x00\\x00\\x00\\x0C\\x00\\x07\\x00\\x06\\x00\\x00\\x00\\x65\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x25\\x00\\x00\\x00\"\n\"\\x61\\x00\\x00\\x00\\x64\\x00\\x00\\x00\\x0C\\x00\\x07\\x00\\x06\\x00\\x00\\x00\\x66\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x25\\x00\\x00\\x00\\x5E\\x00\\x00\\x00\"\n\"\\x65\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x5D\\x00\\x00\\x00\\x66\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x68\\x00\\x00\\x00\\x59\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x69\\x00\\x00\\x00\\x49\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x6A\\x00\\x00\\x00\\x4D\\x00\\x00\\x00\"\n\"\\x0C\\x00\\x07\\x00\\x06\\x00\\x00\\x00\\x6B\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x28\\x00\\x00\\x00\\x69\\x00\\x00\\x00\\x6A\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x06\\x00\\x00\\x00\\x6C\\x00\\x00\\x00\\x51\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x6D\\x00\\x00\\x00\\x55\\x00\\x00\\x00\\x0C\\x00\\x07\\x00\"\n\"\\x06\\x00\\x00\\x00\\x6E\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x28\\x00\\x00\\x00\\x6C\\x00\\x00\\x00\\x6D\\x00\\x00\\x00\\x0C\\x00\\x07\\x00\\x06\\x00\\x00\\x00\"\n\"\\x6F\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x28\\x00\\x00\\x00\\x6B\\x00\\x00\\x00\\x6E\\x00\\x00\\x00\\x0C\\x00\\x07\\x00\\x06\\x00\\x00\\x00\\x70\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x28\\x00\\x00\\x00\\x68\\x00\\x00\\x00\\x6F\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x67\\x00\\x00\\x00\\x70\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x06\\x00\\x00\\x00\\x73\\x00\\x00\\x00\\x49\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x74\\x00\\x00\\x00\\x4D\\x00\\x00\\x00\\x81\\x00\\x05\\x00\"\n\"\\x06\\x00\\x00\\x00\\x75\\x00\\x00\\x00\\x73\\x00\\x00\\x00\\x74\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x76\\x00\\x00\\x00\\x51\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x77\\x00\\x00\\x00\\x55\\x00\\x00\\x00\\x81\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x78\\x00\\x00\\x00\\x76\\x00\\x00\\x00\"\n\"\\x77\\x00\\x00\\x00\\x83\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x79\\x00\\x00\\x00\\x75\\x00\\x00\\x00\\x78\\x00\\x00\\x00\\x7F\\x00\\x04\\x00\\x06\\x00\\x00\\x00\"\n\"\\x7A\\x00\\x00\\x00\\x79\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x7D\\x00\\x00\\x00\\x72\\x00\\x00\\x00\\x7C\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\"\n\"\\x7D\\x00\\x00\\x00\\x7A\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x7E\\x00\\x00\\x00\\x49\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\"\n\"\\x7F\\x00\\x00\\x00\\x51\\x00\\x00\\x00\\x81\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x80\\x00\\x00\\x00\\x7E\\x00\\x00\\x00\\x7F\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x06\\x00\\x00\\x00\\x81\\x00\\x00\\x00\\x4D\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x82\\x00\\x00\\x00\\x55\\x00\\x00\\x00\\x81\\x00\\x05\\x00\"\n\"\\x06\\x00\\x00\\x00\\x83\\x00\\x00\\x00\\x81\\x00\\x00\\x00\\x82\\x00\\x00\\x00\\x83\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x84\\x00\\x00\\x00\\x80\\x00\\x00\\x00\"\n\"\\x83\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x86\\x00\\x00\\x00\\x72\\x00\\x00\\x00\\x85\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x86\\x00\\x00\\x00\"\n\"\\x84\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x88\\x00\\x00\\x00\\x49\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x89\\x00\\x00\\x00\"\n\"\\x4D\\x00\\x00\\x00\\x81\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x8A\\x00\\x00\\x00\\x88\\x00\\x00\\x00\\x89\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\"\n\"\\x8B\\x00\\x00\\x00\\x51\\x00\\x00\\x00\\x81\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x8C\\x00\\x00\\x00\\x8A\\x00\\x00\\x00\\x8B\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x06\\x00\\x00\\x00\\x8D\\x00\\x00\\x00\\x55\\x00\\x00\\x00\\x81\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x8E\\x00\\x00\\x00\\x8C\\x00\\x00\\x00\\x8D\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x90\\x00\\x00\\x00\\x0A\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x91\\x00\\x00\\x00\\x8F\\x00\\x00\\x00\"\n\"\\x90\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x92\\x00\\x00\\x00\\x8E\\x00\\x00\\x00\\x91\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\"\n\"\\x93\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x0C\\x00\\x07\\x00\\x06\\x00\\x00\\x00\\x94\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x28\\x00\\x00\\x00\\x92\\x00\\x00\\x00\"\n\"\\x93\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x87\\x00\\x00\\x00\\x94\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x96\\x00\\x00\\x00\\x72\\x00\\x00\\x00\"\n\"\\x7C\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x97\\x00\\x00\\x00\\x96\\x00\\x00\\x00\\x0C\\x00\\x06\\x00\\x06\\x00\\x00\\x00\\x98\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x97\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x99\\x00\\x00\\x00\\x72\\x00\\x00\\x00\\x85\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x9A\\x00\\x00\\x00\\x99\\x00\\x00\\x00\\x0C\\x00\\x06\\x00\\x06\\x00\\x00\\x00\\x9B\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x04\\x00\\x00\\x00\\x9A\\x00\\x00\\x00\\x0C\\x00\\x07\\x00\\x06\\x00\\x00\\x00\\x9C\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x25\\x00\\x00\\x00\\x98\\x00\\x00\\x00\"\n\"\\x9B\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x9D\\x00\\x00\\x00\\x87\\x00\\x00\\x00\\x81\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x9E\\x00\\x00\\x00\"\n\"\\x9C\\x00\\x00\\x00\\x9D\\x00\\x00\\x00\\x88\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x9F\\x00\\x00\\x00\\x26\\x00\\x00\\x00\\x9E\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\"\n\"\\x95\\x00\\x00\\x00\\x9F\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\xA0\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\"\n\"\\xA1\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\x50\\x00\\x05\\x00\\x16\\x00\\x00\\x00\\xA2\\x00\\x00\\x00\\xA0\\x00\\x00\\x00\\xA1\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x06\\x00\\x00\\x00\\xA3\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\x7F\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\xA4\\x00\\x00\\x00\\xA3\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x06\\x00\\x00\\x00\\xA5\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\x7F\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\xA6\\x00\\x00\\x00\\xA5\\x00\\x00\\x00\\x50\\x00\\x05\\x00\"\n\"\\x16\\x00\\x00\\x00\\xA7\\x00\\x00\\x00\\xA4\\x00\\x00\\x00\\xA6\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\xA8\\x00\\x00\\x00\\x72\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\xA9\\x00\\x00\\x00\\x95\\x00\\x00\\x00\\x8E\\x00\\x05\\x00\\x16\\x00\\x00\\x00\\xAA\\x00\\x00\\x00\\xA8\\x00\\x00\\x00\"\n\"\\xA9\\x00\\x00\\x00\\x0C\\x00\\x07\\x00\\x16\\x00\\x00\\x00\\xAB\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x28\\x00\\x00\\x00\\xA7\\x00\\x00\\x00\\xAA\\x00\\x00\\x00\"\n\"\\x0C\\x00\\x07\\x00\\x16\\x00\\x00\\x00\\xAC\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x25\\x00\\x00\\x00\\xA2\\x00\\x00\\x00\\xAB\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x16\\x00\\x00\\x00\\xAD\\x00\\x00\\x00\\x1C\\x00\\x00\\x00\\x88\\x00\\x05\\x00\\x16\\x00\\x00\\x00\\xAE\\x00\\x00\\x00\\xAC\\x00\\x00\\x00\\xAD\\x00\\x00\\x00\"\n\"\\x3E\\x00\\x03\\x00\\x72\\x00\\x00\\x00\\xAE\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x12\\x00\\x00\\x00\\xB1\\x00\\x00\\x00\\x14\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x16\\x00\\x00\\x00\\xB2\\x00\\x00\\x00\\x18\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\xB3\\x00\\x00\\x00\\x72\\x00\\x00\\x00\\x8E\\x00\\x05\\x00\"\n\"\\x16\\x00\\x00\\x00\\xB5\\x00\\x00\\x00\\xB3\\x00\\x00\\x00\\xB4\\x00\\x00\\x00\\x81\\x00\\x05\\x00\\x16\\x00\\x00\\x00\\xB6\\x00\\x00\\x00\\xB2\\x00\\x00\\x00\"\n\"\\xB5\\x00\\x00\\x00\\x57\\x00\\x05\\x00\\x20\\x00\\x00\\x00\\xB7\\x00\\x00\\x00\\xB1\\x00\\x00\\x00\\xB6\\x00\\x00\\x00\\x4F\\x00\\x08\\x00\\x0E\\x00\\x00\\x00\"\n\"\\xB8\\x00\\x00\\x00\\xB7\\x00\\x00\\x00\\xB7\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x12\\x00\\x00\\x00\"\n\"\\xB9\\x00\\x00\\x00\\x14\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\xBA\\x00\\x00\\x00\\x18\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x16\\x00\\x00\\x00\"\n\"\\xBB\\x00\\x00\\x00\\x72\\x00\\x00\\x00\\x8E\\x00\\x05\\x00\\x16\\x00\\x00\\x00\\xBD\\x00\\x00\\x00\\xBB\\x00\\x00\\x00\\xBC\\x00\\x00\\x00\\x81\\x00\\x05\\x00\"\n\"\\x16\\x00\\x00\\x00\\xBE\\x00\\x00\\x00\\xBA\\x00\\x00\\x00\\xBD\\x00\\x00\\x00\\x57\\x00\\x05\\x00\\x20\\x00\\x00\\x00\\xBF\\x00\\x00\\x00\\xB9\\x00\\x00\\x00\"\n\"\\xBE\\x00\\x00\\x00\\x4F\\x00\\x08\\x00\\x0E\\x00\\x00\\x00\\xC0\\x00\\x00\\x00\\xBF\\x00\\x00\\x00\\xBF\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\x81\\x00\\x05\\x00\\x0E\\x00\\x00\\x00\\xC1\\x00\\x00\\x00\\xB8\\x00\\x00\\x00\\xC0\\x00\\x00\\x00\\x8E\\x00\\x05\\x00\\x0E\\x00\\x00\\x00\"\n\"\\xC2\\x00\\x00\\x00\\xC1\\x00\\x00\\x00\\xB0\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\xAF\\x00\\x00\\x00\\xC2\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0E\\x00\\x00\\x00\"\n\"\\xC4\\x00\\x00\\x00\\xAF\\x00\\x00\\x00\\x8E\\x00\\x05\\x00\\x0E\\x00\\x00\\x00\\xC5\\x00\\x00\\x00\\xC4\\x00\\x00\\x00\\xB0\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x12\\x00\\x00\\x00\\xC6\\x00\\x00\\x00\\x14\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\xC7\\x00\\x00\\x00\\x18\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x16\\x00\\x00\\x00\\xC8\\x00\\x00\\x00\\x72\\x00\\x00\\x00\\x8E\\x00\\x05\\x00\\x16\\x00\\x00\\x00\\xCA\\x00\\x00\\x00\\xC8\\x00\\x00\\x00\\xC9\\x00\\x00\\x00\"\n\"\\x81\\x00\\x05\\x00\\x16\\x00\\x00\\x00\\xCB\\x00\\x00\\x00\\xC7\\x00\\x00\\x00\\xCA\\x00\\x00\\x00\\x57\\x00\\x05\\x00\\x20\\x00\\x00\\x00\\xCC\\x00\\x00\\x00\"\n\"\\xC6\\x00\\x00\\x00\\xCB\\x00\\x00\\x00\\x4F\\x00\\x08\\x00\\x0E\\x00\\x00\\x00\\xCD\\x00\\x00\\x00\\xCC\\x00\\x00\\x00\\xCC\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x12\\x00\\x00\\x00\\xCE\\x00\\x00\\x00\\x14\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x16\\x00\\x00\\x00\"\n\"\\xCF\\x00\\x00\\x00\\x18\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\xD0\\x00\\x00\\x00\\x72\\x00\\x00\\x00\\x8E\\x00\\x05\\x00\\x16\\x00\\x00\\x00\"\n\"\\xD1\\x00\\x00\\x00\\xD0\\x00\\x00\\x00\\xB0\\x00\\x00\\x00\\x81\\x00\\x05\\x00\\x16\\x00\\x00\\x00\\xD2\\x00\\x00\\x00\\xCF\\x00\\x00\\x00\\xD1\\x00\\x00\\x00\"\n\"\\x57\\x00\\x05\\x00\\x20\\x00\\x00\\x00\\xD3\\x00\\x00\\x00\\xCE\\x00\\x00\\x00\\xD2\\x00\\x00\\x00\\x4F\\x00\\x08\\x00\\x0E\\x00\\x00\\x00\\xD4\\x00\\x00\\x00\"\n\"\\xD3\\x00\\x00\\x00\\xD3\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x81\\x00\\x05\\x00\\x0E\\x00\\x00\\x00\\xD5\\x00\\x00\\x00\"\n\"\\xCD\\x00\\x00\\x00\\xD4\\x00\\x00\\x00\\x8E\\x00\\x05\\x00\\x0E\\x00\\x00\\x00\\xD6\\x00\\x00\\x00\\xD5\\x00\\x00\\x00\\x8F\\x00\\x00\\x00\\x81\\x00\\x05\\x00\"\n\"\\x0E\\x00\\x00\\x00\\xD7\\x00\\x00\\x00\\xC5\\x00\\x00\\x00\\xD6\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\xC3\\x00\\x00\\x00\\xD7\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x0E\\x00\\x00\\x00\\xD9\\x00\\x00\\x00\\xC3\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0E\\x00\\x00\\x00\\xDA\\x00\\x00\\x00\\x44\\x00\\x00\\x00\\x94\\x00\\x05\\x00\"\n\"\\x06\\x00\\x00\\x00\\xDB\\x00\\x00\\x00\\xD9\\x00\\x00\\x00\\xDA\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\xD8\\x00\\x00\\x00\\xDB\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x06\\x00\\x00\\x00\\xDC\\x00\\x00\\x00\\xD8\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\xDD\\x00\\x00\\x00\\x5D\\x00\\x00\\x00\\xB8\\x00\\x05\\x00\"\n\"\\xDE\\x00\\x00\\x00\\xDF\\x00\\x00\\x00\\xDC\\x00\\x00\\x00\\xDD\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\xE0\\x00\\x00\\x00\\xD8\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\xE1\\x00\\x00\\x00\\x67\\x00\\x00\\x00\\xBA\\x00\\x05\\x00\\xDE\\x00\\x00\\x00\\xE2\\x00\\x00\\x00\\xE0\\x00\\x00\\x00\"\n\"\\xE1\\x00\\x00\\x00\\xA6\\x00\\x05\\x00\\xDE\\x00\\x00\\x00\\xE3\\x00\\x00\\x00\\xDF\\x00\\x00\\x00\\xE2\\x00\\x00\\x00\\xF7\\x00\\x03\\x00\\xE5\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\\xE3\\x00\\x00\\x00\\xE4\\x00\\x00\\x00\\xEB\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\xE4\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x0E\\x00\\x00\\x00\\xE8\\x00\\x00\\x00\\xAF\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x20\\x00\\x00\\x00\\xE9\\x00\\x00\\x00\\xE7\\x00\\x00\\x00\\x4F\\x00\\x09\\x00\"\n\"\\x20\\x00\\x00\\x00\\xEA\\x00\\x00\\x00\\xE9\\x00\\x00\\x00\\xE8\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x03\\x00\\x00\\x00\"\n\"\\x3E\\x00\\x03\\x00\\xE7\\x00\\x00\\x00\\xEA\\x00\\x00\\x00\\xF9\\x00\\x02\\x00\\xE5\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\xEB\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x0E\\x00\\x00\\x00\\xEC\\x00\\x00\\x00\\xC3\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x20\\x00\\x00\\x00\\xED\\x00\\x00\\x00\\xE7\\x00\\x00\\x00\\x4F\\x00\\x09\\x00\"\n\"\\x20\\x00\\x00\\x00\\xEE\\x00\\x00\\x00\\xED\\x00\\x00\\x00\\xEC\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x03\\x00\\x00\\x00\"\n\"\\x3E\\x00\\x03\\x00\\xE7\\x00\\x00\\x00\\xEE\\x00\\x00\\x00\\xF9\\x00\\x02\\x00\\xE5\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\xE5\\x00\\x00\\x00\\x41\\x00\\x05\\x00\"\n\"\\xF0\\x00\\x00\\x00\\xF1\\x00\\x00\\x00\\xE7\\x00\\x00\\x00\\xEF\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\xF1\\x00\\x00\\x00\\x26\\x00\\x00\\x00\\xFD\\x00\\x01\\x00\"\n\"\\x38\\x00\\x01\\x00\"},\n#endif\n#ifdef GLQUAKE\n{QR_OPENGL, 110, \"underwaterwarp\",\n\"!!cvarf r_waterwarp\\n\"\n\"!!samps screen=0 warp=1 edge=2\\n\"\n\n//this is a post processing shader that is drawn fullscreen whenever the view is underwater.\n//its generally expected to warp the view a little.\n\n\"varying vec2 v_stc;\\n\"\n\"varying vec2 v_warp;\\n\"\n\"varying vec2 v_edge;\\n\"\n\"#ifdef VERTEX_SHADER\\n\"\n\"attribute vec2 v_texcoord;\\n\"\n\"uniform float e_time;\\n\"\n\"void main ()\\n\"\n\"{\\n\"\n\"gl_Position = ftetransform();\\n\"\n\"v_stc = vec2(v_texcoord.x, 1.0-v_texcoord.y);\\n\"\n\"v_warp.s = e_time * 0.25 + v_texcoord.s;\\n\"\n\"v_warp.t = e_time * 0.25 + v_texcoord.t;\\n\"\n\"v_edge = v_texcoord.xy;\\n\"\n\"}\\n\"\n\"#endif\\n\"\n\"#ifdef FRAGMENT_SHADER\\n\"\n\"uniform vec4 e_rendertexturescale;\\n\"\n\"uniform float cvar_r_waterwarp;\\n\"\n\"void main ()\\n\"\n\"{\\n\"\n\"vec2 amp  = (0.010 / 0.625) * cvar_r_waterwarp * texture2D(s_edge, v_edge).rg;\\n\"\n\"vec3 offset = (texture2D(s_warp, v_warp).rgb - 0.5) * 2.0;\\n\"\n\"vec2 temp  = v_stc + offset.xy * amp;\\n\"\n\"gl_FragColor = texture2D(s_screen, temp*e_rendertexturescale.st);\\n\"\n\"}\\n\"\n\"#endif\\n\"\n},\n#endif\n#ifdef VKQUAKE\n{QR_VULKAN, -1, \"underwaterwarp\",\n\"\\xFF\\x53\\x50\\x56\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x2C\\x00\\x00\\x00\\x14\\x00\\x00\\x00\\x40\\x00\\x00\\x00\"\n\"\\x5C\\x0C\\x00\\x00\\x9C\\x0C\\x00\\x00\\xB0\\x09\\x00\\x00\\x01\\x00\\x66\\x31\\x72\\x5F\\x77\\x61\\x74\\x65\\x72\\x77\\x61\\x72\\x70\\x00\\x3F\\x80\\x00\\x00\"\n\"\\x03\\x02\\x23\\x07\\x00\\x00\\x01\\x00\\x0A\\x00\\x08\\x00\\x68\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x11\\x00\\x02\\x00\\x01\\x00\\x00\\x00\\x0B\\x00\\x06\\x00\"\n\"\\x01\\x00\\x00\\x00\\x47\\x4C\\x53\\x4C\\x2E\\x73\\x74\\x64\\x2E\\x34\\x35\\x30\\x00\\x00\\x00\\x00\\x0E\\x00\\x03\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x0F\\x00\\x10\\x00\\x00\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x6D\\x61\\x69\\x6E\\x00\\x00\\x00\\x00\\x1C\\x00\\x00\\x00\\x3A\\x00\\x00\\x00\\x3F\\x00\\x00\\x00\"\n\"\\x41\\x00\\x00\\x00\\x49\\x00\\x00\\x00\\x5C\\x00\\x00\\x00\\x63\\x00\\x00\\x00\\x64\\x00\\x00\\x00\\x65\\x00\\x00\\x00\\x66\\x00\\x00\\x00\\x67\\x00\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x11\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x13\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x05\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x13\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\"\n\"\\x48\\x00\\x04\\x00\\x13\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x80\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\"\n\"\\x03\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xC0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xCC\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xD0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\xDC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xE0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x13\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xEC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\xF0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x0A\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xFC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\"\n\"\\x0B\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x01\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x4C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x50\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x5C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x13\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x60\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x11\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x6C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x70\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\"\n\"\\x13\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x14\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x90\\x01\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x94\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x16\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x98\\x01\\x00\\x00\\x47\\x00\\x03\\x00\\x13\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x15\\x00\\x00\\x00\\x22\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x15\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x1C\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x38\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x03\\x00\\x38\\x00\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x3F\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x41\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x49\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x5C\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x5E\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x48\\x00\\x04\\x00\\x5F\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x5F\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x5F\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x5F\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x5F\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x4C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x5F\\x00\\x00\\x00\\x03\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x50\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x5F\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x5C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x5F\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x60\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x5F\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x6C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x5F\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x70\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x5F\\x00\\x00\\x00\"\n\"\\x08\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x5F\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x88\\x00\\x00\\x00\"\n\"\\x47\\x00\\x03\\x00\\x5F\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x61\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\"\n\"\\x61\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x63\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\"\n\"\\x64\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x65\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x47\\x00\\x04\\x00\"\n\"\\x66\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x67\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x13\\x00\\x02\\x00\"\n\"\\x02\\x00\\x00\\x00\\x21\\x00\\x03\\x00\\x03\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x16\\x00\\x03\\x00\\x06\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x17\\x00\\x04\\x00\"\n\"\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x21\\x00\\x03\\x00\\x08\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x18\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x17\\x00\\x04\\x00\\x0E\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x15\\x00\\x04\\x00\\x0F\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x0F\\x00\\x00\\x00\"\n\"\\x10\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x1C\\x00\\x04\\x00\\x11\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x17\\x00\\x04\\x00\\x12\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x1E\\x00\\x19\\x00\\x13\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x11\\x00\\x00\\x00\"\n\"\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x14\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x13\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\"\n\"\\x14\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x15\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\"\n\"\\x16\\x00\\x00\\x00\\x17\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x18\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x20\\x00\\x04\\x00\"\n\"\\x1B\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\\x1C\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\"\n\"\\x06\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x00\\x00\\x80\\x3F\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x24\\x00\\x00\\x00\\x00\\x00\\x80\\xBF\\x2B\\x00\\x04\\x00\"\n\"\\x0F\\x00\\x00\\x00\\x25\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x26\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\"\n\"\\x0F\\x00\\x00\\x00\\x2B\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x0F\\x00\\x00\\x00\\x2E\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\"\n\"\\x06\\x00\\x00\\x00\\x32\\x00\\x00\\x00\\x00\\x00\\x00\\x40\\x1E\\x00\\x03\\x00\\x38\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x39\\x00\\x00\\x00\"\n\"\\x03\\x00\\x00\\x00\\x38\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x39\\x00\\x00\\x00\\x3A\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x3C\\x00\\x00\\x00\"\n\"\\x03\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x3E\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x3E\\x00\\x00\\x00\"\n\"\\x3F\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x40\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x40\\x00\\x00\\x00\"\n\"\\x41\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x0F\\x00\\x00\\x00\\x42\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x43\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x3E\\x00\\x00\\x00\\x49\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x16\\x00\\x00\\x00\"\n\"\\x4A\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x4B\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\"\n\"\\x4E\\x00\\x00\\x00\\x00\\x00\\x80\\x3E\\x20\\x00\\x04\\x00\\x53\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x3E\\x00\\x00\\x00\"\n\"\\x5C\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x32\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x5E\\x00\\x00\\x00\\x00\\x00\\x80\\x43\\x1E\\x00\\x0C\\x00\\x5F\\x00\\x00\\x00\"\n\"\\x0D\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x12\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x60\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x5F\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x60\\x00\\x00\\x00\"\n\"\\x61\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x62\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x62\\x00\\x00\\x00\"\n\"\\x63\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x40\\x00\\x00\\x00\\x64\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\"\n\"\\x65\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\\x66\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\"\n\"\\x67\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x36\\x00\\x05\\x00\\x02\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\"\n\"\\x05\\x00\\x00\\x00\\x39\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x3C\\x00\\x00\\x00\\x3D\\x00\\x00\\x00\"\n\"\\x3A\\x00\\x00\\x00\\x17\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x3D\\x00\\x00\\x00\\x3B\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x43\\x00\\x00\\x00\\x44\\x00\\x00\\x00\"\n\"\\x41\\x00\\x00\\x00\\x42\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x45\\x00\\x00\\x00\\x44\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x43\\x00\\x00\\x00\"\n\"\\x46\\x00\\x00\\x00\\x41\\x00\\x00\\x00\\x25\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x47\\x00\\x00\\x00\\x46\\x00\\x00\\x00\\x50\\x00\\x05\\x00\"\n\"\\x12\\x00\\x00\\x00\\x48\\x00\\x00\\x00\\x45\\x00\\x00\\x00\\x47\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x3F\\x00\\x00\\x00\\x48\\x00\\x00\\x00\\x41\\x00\\x05\\x00\"\n\"\\x4B\\x00\\x00\\x00\\x4C\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x4A\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x4D\\x00\\x00\\x00\\x4C\\x00\\x00\\x00\"\n\"\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x4F\\x00\\x00\\x00\\x4D\\x00\\x00\\x00\\x4E\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x43\\x00\\x00\\x00\\x50\\x00\\x00\\x00\"\n\"\\x41\\x00\\x00\\x00\\x42\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x51\\x00\\x00\\x00\\x50\\x00\\x00\\x00\\x81\\x00\\x05\\x00\\x06\\x00\\x00\\x00\"\n\"\\x52\\x00\\x00\\x00\\x4F\\x00\\x00\\x00\\x51\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x53\\x00\\x00\\x00\\x54\\x00\\x00\\x00\\x49\\x00\\x00\\x00\\x42\\x00\\x00\\x00\"\n\"\\x3E\\x00\\x03\\x00\\x54\\x00\\x00\\x00\\x52\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x4B\\x00\\x00\\x00\\x55\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x4A\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x56\\x00\\x00\\x00\\x55\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x57\\x00\\x00\\x00\\x56\\x00\\x00\\x00\"\n\"\\x4E\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x43\\x00\\x00\\x00\\x58\\x00\\x00\\x00\\x41\\x00\\x00\\x00\\x25\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\"\n\"\\x59\\x00\\x00\\x00\\x58\\x00\\x00\\x00\\x81\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x5A\\x00\\x00\\x00\\x57\\x00\\x00\\x00\\x59\\x00\\x00\\x00\\x41\\x00\\x05\\x00\"\n\"\\x53\\x00\\x00\\x00\\x5B\\x00\\x00\\x00\\x49\\x00\\x00\\x00\\x25\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x5B\\x00\\x00\\x00\\x5A\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x12\\x00\\x00\\x00\\x5D\\x00\\x00\\x00\\x41\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x5C\\x00\\x00\\x00\\x5D\\x00\\x00\\x00\\xFD\\x00\\x01\\x00\\x38\\x00\\x01\\x00\"\n\"\\x36\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x0A\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\"\n\"\\x0B\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x18\\x00\\x00\\x00\\x19\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x17\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x0D\\x00\\x00\\x00\\x1A\\x00\\x00\\x00\\x19\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0E\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x1C\\x00\\x00\\x00\"\n\"\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x1F\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x20\\x00\\x00\\x00\"\n\"\\x1D\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x50\\x00\\x07\\x00\"\n\"\\x07\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x1F\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x91\\x00\\x05\\x00\\x07\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x1A\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x0C\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x26\\x00\\x00\\x00\"\n\"\\x27\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x25\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x28\\x00\\x00\\x00\\x27\\x00\\x00\\x00\\x85\\x00\\x05\\x00\"\n\"\\x06\\x00\\x00\\x00\\x29\\x00\\x00\\x00\\x28\\x00\\x00\\x00\\x24\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x26\\x00\\x00\\x00\\x2A\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\"\n\"\\x25\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x2A\\x00\\x00\\x00\\x29\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x26\\x00\\x00\\x00\\x2C\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\"\n\"\\x2B\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\\x2C\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x26\\x00\\x00\\x00\\x2F\\x00\\x00\\x00\"\n\"\\x0C\\x00\\x00\\x00\\x2E\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x30\\x00\\x00\\x00\\x2F\\x00\\x00\\x00\\x81\\x00\\x05\\x00\\x06\\x00\\x00\\x00\"\n\"\\x31\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\\x30\\x00\\x00\\x00\\x88\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x33\\x00\\x00\\x00\\x31\\x00\\x00\\x00\\x32\\x00\\x00\\x00\"\n\"\\x41\\x00\\x05\\x00\\x26\\x00\\x00\\x00\\x34\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x2B\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x34\\x00\\x00\\x00\\x33\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x35\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\xFE\\x00\\x02\\x00\\x35\\x00\\x00\\x00\\x38\\x00\\x01\\x00\\x03\\x02\\x23\\x07\"\n\"\\x00\\x00\\x01\\x00\\x0A\\x00\\x08\\x00\\x42\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x11\\x00\\x02\\x00\\x01\\x00\\x00\\x00\\x0B\\x00\\x06\\x00\\x01\\x00\\x00\\x00\"\n\"\\x47\\x4C\\x53\\x4C\\x2E\\x73\\x74\\x64\\x2E\\x34\\x35\\x30\\x00\\x00\\x00\\x00\\x0E\\x00\\x03\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x0F\\x00\\x09\\x00\"\n\"\\x04\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x6D\\x61\\x69\\x6E\\x00\\x00\\x00\\x00\\x13\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x28\\x00\\x00\\x00\\x30\\x00\\x00\\x00\"\n\"\\x10\\x00\\x03\\x00\\x04\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x47\\x00\\x04\\x00\"\n\"\\x10\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x10\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x47\\x00\\x04\\x00\"\n\"\\x13\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x1C\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\"\n\"\\x1C\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x47\\x00\\x04\\x00\"\n\"\\x28\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x30\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\"\n\"\\x31\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x31\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\"\n\"\\x3B\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x3C\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x3C\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x3C\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x3C\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x3C\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x3C\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\"\n\"\\x3C\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x3C\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x3C\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x3C\\x00\\x00\\x00\\x03\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\xC0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x3C\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xCC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x3C\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xD0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x3C\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\xDC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x3C\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xE0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x3C\\x00\\x00\\x00\"\n\"\\x08\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xEC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x3C\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xF0\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x3C\\x00\\x00\\x00\\x0A\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xFC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x3C\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x3C\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x01\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x3C\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x4C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x3C\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x50\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x3C\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x5C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x3C\\x00\\x00\\x00\"\n\"\\x10\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x60\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x3C\\x00\\x00\\x00\\x11\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x6C\\x01\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x3C\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x70\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x3C\\x00\\x00\\x00\\x13\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x80\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x3C\\x00\\x00\\x00\\x14\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x90\\x01\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x3C\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x94\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x3C\\x00\\x00\\x00\\x16\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x98\\x01\\x00\\x00\\x47\\x00\\x03\\x00\\x3C\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x3E\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x3E\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x3F\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x05\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x3F\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x3F\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x3F\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x3F\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x4C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x3F\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x50\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x3F\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x5C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x3F\\x00\\x00\\x00\"\n\"\\x05\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x60\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x3F\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x6C\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x3F\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x70\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x3F\\x00\\x00\\x00\\x08\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x80\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x3F\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x88\\x00\\x00\\x00\\x47\\x00\\x03\\x00\"\n\"\\x3F\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x41\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x41\\x00\\x00\\x00\"\n\"\\x21\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x13\\x00\\x02\\x00\\x02\\x00\\x00\\x00\\x21\\x00\\x03\\x00\\x03\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x16\\x00\\x03\\x00\"\n\"\\x06\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x17\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x08\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x0A\\x00\\x00\\x00\\x6F\\x12\\x83\\x3C\\x32\\x00\\x04\\x00\\x06\\x00\\x00\\x00\"\n\"\\x0B\\x00\\x00\\x00\\x00\\x00\\x80\\x43\\x19\\x00\\x09\\x00\\x0D\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x1B\\x00\\x03\\x00\\x0E\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x0F\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x0F\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x12\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x12\\x00\\x00\\x00\\x13\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x17\\x00\\x04\\x00\\x15\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x17\\x00\\x04\\x00\\x19\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x1A\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x19\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x0F\\x00\\x00\\x00\\x1C\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x12\\x00\\x00\\x00\"\n\"\\x1E\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x3F\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\"\n\"\\x25\\x00\\x00\\x00\\x00\\x00\\x00\\x40\\x3B\\x00\\x04\\x00\\x12\\x00\\x00\\x00\\x28\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x2F\\x00\\x00\\x00\"\n\"\\x03\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x2F\\x00\\x00\\x00\\x30\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x0F\\x00\\x00\\x00\"\n\"\\x31\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x34\\x00\\x00\\x00\\x00\\x00\\x80\\x3F\\x2C\\x00\\x05\\x00\\x07\\x00\\x00\\x00\"\n\"\\x35\\x00\\x00\\x00\\x34\\x00\\x00\\x00\\x34\\x00\\x00\\x00\\x18\\x00\\x04\\x00\\x38\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x15\\x00\\x04\\x00\"\n\"\\x39\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x39\\x00\\x00\\x00\\x3A\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x1C\\x00\\x04\\x00\"\n\"\\x3B\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x3A\\x00\\x00\\x00\\x1E\\x00\\x19\\x00\\x3C\\x00\\x00\\x00\\x38\\x00\\x00\\x00\\x38\\x00\\x00\\x00\\x38\\x00\\x00\\x00\"\n\"\\x19\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x19\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x19\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x19\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x3B\\x00\\x00\\x00\\x19\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x19\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x19\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x15\\x00\\x00\\x00\"\n\"\\x15\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x3D\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x3C\\x00\\x00\\x00\"\n\"\\x3B\\x00\\x04\\x00\\x3D\\x00\\x00\\x00\\x3E\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x1E\\x00\\x0C\\x00\\x3F\\x00\\x00\\x00\\x38\\x00\\x00\\x00\\x19\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x19\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x19\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x20\\x00\\x04\\x00\\x40\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x3F\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x40\\x00\\x00\\x00\\x41\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x36\\x00\\x05\\x00\\x02\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x05\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\"\n\"\\x08\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x1A\\x00\\x00\\x00\\x1B\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\"\n\"\\x08\\x00\\x00\\x00\\x27\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x0A\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x0E\\x00\\x00\\x00\\x11\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x14\\x00\\x00\\x00\\x13\\x00\\x00\\x00\"\n\"\\x57\\x00\\x05\\x00\\x15\\x00\\x00\\x00\\x16\\x00\\x00\\x00\\x11\\x00\\x00\\x00\\x14\\x00\\x00\\x00\\x4F\\x00\\x07\\x00\\x07\\x00\\x00\\x00\\x17\\x00\\x00\\x00\"\n\"\\x16\\x00\\x00\\x00\\x16\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x8E\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x18\\x00\\x00\\x00\\x17\\x00\\x00\\x00\"\n\"\\x0C\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x09\\x00\\x00\\x00\\x18\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0E\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x1C\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x1F\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x57\\x00\\x05\\x00\\x15\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\"\n\"\\x1F\\x00\\x00\\x00\\x4F\\x00\\x08\\x00\\x19\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\x50\\x00\\x06\\x00\\x19\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x83\\x00\\x05\\x00\"\n\"\\x19\\x00\\x00\\x00\\x24\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x8E\\x00\\x05\\x00\\x19\\x00\\x00\\x00\\x26\\x00\\x00\\x00\\x24\\x00\\x00\\x00\"\n\"\\x25\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x1B\\x00\\x00\\x00\\x26\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x29\\x00\\x00\\x00\\x28\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x19\\x00\\x00\\x00\\x2A\\x00\\x00\\x00\\x1B\\x00\\x00\\x00\\x4F\\x00\\x07\\x00\\x07\\x00\\x00\\x00\\x2B\\x00\\x00\\x00\\x2A\\x00\\x00\\x00\"\n\"\\x2A\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x2C\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x85\\x00\\x05\\x00\"\n\"\\x07\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\\x2B\\x00\\x00\\x00\\x2C\\x00\\x00\\x00\\x81\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x2E\\x00\\x00\\x00\\x29\\x00\\x00\\x00\"\n\"\\x2D\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x27\\x00\\x00\\x00\\x2E\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0E\\x00\\x00\\x00\\x32\\x00\\x00\\x00\\x31\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x33\\x00\\x00\\x00\\x27\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x36\\x00\\x00\\x00\\x33\\x00\\x00\\x00\"\n\"\\x35\\x00\\x00\\x00\\x57\\x00\\x05\\x00\\x15\\x00\\x00\\x00\\x37\\x00\\x00\\x00\\x32\\x00\\x00\\x00\\x36\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x30\\x00\\x00\\x00\"\n\"\\x37\\x00\\x00\\x00\\xFD\\x00\\x01\\x00\\x38\\x00\\x01\\x00\"},\n#endif\n#ifdef GLQUAKE\n{QR_OPENGL, 110, \"menutint\",\n\"!!cvari r_menutint_inverse\\n\"\n\"!!cvard_srgb r_menutint\\n\"\n\"!!samps 1\\n\"\n\n\"#ifdef VERTEX_SHADER\\n\"\n\"attribute vec2 v_texcoord;\\n\"\n\"varying vec2 texcoord;\\n\"\n\"uniform vec4 e_rendertexturescale;\\n\"\n\"void main(void)\\n\"\n\"{\\n\"\n\"texcoord.x = v_texcoord.x*e_rendertexturescale.x;\\n\"\n\"texcoord.y = (1.0-v_texcoord.y)*e_rendertexturescale.y;\\n\"\n\"gl_Position = ftetransform();\\n\"\n\"}\\n\"\n\"#endif\\n\"\n\"#ifdef FRAGMENT_SHADER\\n\"\n\n\"varying vec2 texcoord;\\n\"\n\"uniform int cvar_r_menutint_inverse;\\n\"\n\"const vec3 lumfactors = vec3(0.299, 0.587, 0.114);\\n\"\n\"const vec3 invertvec = vec3(1.0, 1.0, 1.0);\\n\"\n\"void main(void)\\n\"\n\"{\\n\"\n\"vec3 texcolor = texture2D(s_t0, texcoord).rgb;\\n\"\n\"float luminance = dot(lumfactors, texcolor);\\n\"\n\"texcolor = vec3(luminance, luminance, luminance);\\n\"\n\"texcolor *= r_menutint;\\n\"\n\"texcolor = (cvar_r_menutint_inverse > 0) ? (invertvec - texcolor) : texcolor;\\n\"\n\"gl_FragColor = vec4(texcolor, 1.0);\\n\"\n\"}\\n\"\n\"#endif\\n\"\n},\n#endif\n#ifdef VKQUAKE\n{QR_VULKAN, -1, \"menutint\",\n\"\\xFF\\x53\\x50\\x56\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x2C\\x00\\x00\\x00\\x36\\x00\\x00\\x00\\x64\\x00\\x00\\x00\"\n\"\\x14\\x0B\\x00\\x00\\x78\\x0B\\x00\\x00\\x70\\x0A\\x00\\x00\\x01\\x00\\x69\\x31\\x72\\x5F\\x6D\\x65\\x6E\\x75\\x74\\x69\\x6E\\x74\\x5F\\x69\\x6E\\x76\\x65\\x72\"\n\"\\x73\\x65\\x00\\x00\\x00\\x00\\x00\\x01\\x01\\x66\\x33\\x72\\x5F\\x6D\\x65\\x6E\\x75\\x74\\x69\\x6E\\x74\\x00\\x3F\\x2E\\x14\\x7B\\x3E\\xCC\\xCC\\xCD\\x3E\\x05\"\n\"\\x1E\\xB8\\x00\\x00\\x03\\x02\\x23\\x07\\x00\\x00\\x01\\x00\\x0A\\x00\\x08\\x00\\x56\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x11\\x00\\x02\\x00\\x01\\x00\\x00\\x00\"\n\"\\x0B\\x00\\x06\\x00\\x01\\x00\\x00\\x00\\x47\\x4C\\x53\\x4C\\x2E\\x73\\x74\\x64\\x2E\\x34\\x35\\x30\\x00\\x00\\x00\\x00\\x0E\\x00\\x03\\x00\\x00\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x0F\\x00\\x0E\\x00\\x00\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x6D\\x61\\x69\\x6E\\x00\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x3F\\x00\\x00\\x00\"\n\"\\x41\\x00\\x00\\x00\\x47\\x00\\x00\\x00\\x50\\x00\\x00\\x00\\x51\\x00\\x00\\x00\\x52\\x00\\x00\\x00\\x53\\x00\\x00\\x00\\x54\\x00\\x00\\x00\\x47\\x00\\x04\\x00\"\n\"\\x0E\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x01\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\x0F\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x02\\x01\\x00\\x00\\x47\\x00\\x04\\x00\"\n\"\\x10\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x03\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\x17\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\"\n\"\\x19\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x19\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x19\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x19\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x19\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x19\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x19\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x19\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x19\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x10\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x19\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xC0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x19\\x00\\x00\\x00\"\n\"\\x04\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xCC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x19\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xD0\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x19\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xDC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x19\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\xE0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x19\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xEC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x19\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xF0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x19\\x00\\x00\\x00\\x0A\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\xFC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x19\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x19\\x00\\x00\\x00\"\n\"\\x0C\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x19\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x4C\\x01\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x19\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x50\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x19\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x5C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x19\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x60\\x01\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x19\\x00\\x00\\x00\\x11\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x6C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x19\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x70\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x19\\x00\\x00\\x00\\x13\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x19\\x00\\x00\\x00\"\n\"\\x14\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x90\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x19\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x94\\x01\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x19\\x00\\x00\\x00\\x16\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x98\\x01\\x00\\x00\\x47\\x00\\x03\\x00\\x19\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x22\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x3F\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x41\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x45\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x47\\x00\\x03\\x00\\x45\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x4B\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x01\\x00\\x00\"\n\"\\x48\\x00\\x04\\x00\\x4C\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x4C\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x4C\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x4C\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x4C\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x4C\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x4C\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x50\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x4C\\x00\\x00\\x00\\x04\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x5C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x4C\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x60\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x4C\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x6C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x4C\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x70\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x4C\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x4C\\x00\\x00\\x00\"\n\"\\x09\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x88\\x00\\x00\\x00\\x47\\x00\\x03\\x00\\x4C\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x4E\\x00\\x00\\x00\"\n\"\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x4E\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x50\\x00\\x00\\x00\"\n\"\\x1E\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x51\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x52\\x00\\x00\\x00\"\n\"\\x1E\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x53\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x54\\x00\\x00\\x00\"\n\"\\x1E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x13\\x00\\x02\\x00\\x02\\x00\\x00\\x00\\x21\\x00\\x03\\x00\\x03\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x16\\x00\\x03\\x00\"\n\"\\x06\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x17\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x21\\x00\\x03\\x00\\x08\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x17\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x0C\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x0B\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x0C\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x32\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\"\n\"\\x00\\x80\\x80\\x43\\x32\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\\x00\\x00\\x81\\x43\\x32\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x10\\x00\\x00\\x00\"\n\"\\x00\\x80\\x81\\x43\\x20\\x00\\x04\\x00\\x12\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x18\\x00\\x04\\x00\\x14\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x04\\x00\\x00\\x00\\x15\\x00\\x04\\x00\\x15\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x15\\x00\\x00\\x00\\x16\\x00\\x00\\x00\"\n\"\\x04\\x00\\x00\\x00\\x1C\\x00\\x04\\x00\\x17\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x16\\x00\\x00\\x00\\x17\\x00\\x04\\x00\\x18\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\x1E\\x00\\x19\\x00\\x19\\x00\\x00\\x00\\x14\\x00\\x00\\x00\\x14\\x00\\x00\\x00\\x14\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x0B\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x17\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x18\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x1A\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x19\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x1A\\x00\\x00\\x00\"\n\"\\x1B\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x15\\x00\\x04\\x00\\x1C\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x1C\\x00\\x00\\x00\"\n\"\\x1D\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x14\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x21\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x21\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\"\n\"\\x24\\x00\\x00\\x00\\x00\\x00\\x80\\x3F\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x2A\\x00\\x00\\x00\\x00\\x00\\x80\\xBF\\x2B\\x00\\x04\\x00\\x15\\x00\\x00\\x00\"\n\"\\x2B\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x2C\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x15\\x00\\x00\\x00\"\n\"\\x31\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x15\\x00\\x00\\x00\\x34\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\"\n\"\\x38\\x00\\x00\\x00\\x00\\x00\\x00\\x40\\x20\\x00\\x04\\x00\\x3E\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x18\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x3E\\x00\\x00\\x00\"\n\"\\x3F\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x40\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x18\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x40\\x00\\x00\\x00\"\n\"\\x41\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x2C\\x00\\x05\\x00\\x18\\x00\\x00\\x00\\x43\\x00\\x00\\x00\\x24\\x00\\x00\\x00\\x24\\x00\\x00\\x00\\x1E\\x00\\x03\\x00\"\n\"\\x45\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x46\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x45\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x46\\x00\\x00\\x00\"\n\"\\x47\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x49\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x32\\x00\\x04\\x00\\x1C\\x00\\x00\\x00\"\n\"\\x4B\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x1E\\x00\\x0C\\x00\\x4C\\x00\\x00\\x00\\x14\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x18\\x00\\x00\\x00\\x18\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x4D\\x00\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\x4C\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x4D\\x00\\x00\\x00\\x4E\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x4F\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x4F\\x00\\x00\\x00\\x50\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x40\\x00\\x00\\x00\"\n\"\\x51\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x21\\x00\\x00\\x00\\x52\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x21\\x00\\x00\\x00\"\n\"\\x53\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x21\\x00\\x00\\x00\\x54\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x2C\\x00\\x07\\x00\\x07\\x00\\x00\\x00\"\n\"\\x55\\x00\\x00\\x00\\x24\\x00\\x00\\x00\\x24\\x00\\x00\\x00\\x24\\x00\\x00\\x00\\x24\\x00\\x00\\x00\\x36\\x00\\x05\\x00\\x02\\x00\\x00\\x00\\x04\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x05\\x00\\x00\\x00\\x50\\x00\\x06\\x00\\x0B\\x00\\x00\\x00\\x11\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\"\n\"\\x0F\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x0D\\x00\\x00\\x00\\x11\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x18\\x00\\x00\\x00\\x42\\x00\\x00\\x00\"\n\"\\x41\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x18\\x00\\x00\\x00\\x44\\x00\\x00\\x00\\x42\\x00\\x00\\x00\\x43\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x3F\\x00\\x00\\x00\"\n\"\\x44\\x00\\x00\\x00\\x39\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x48\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x49\\x00\\x00\\x00\\x4A\\x00\\x00\\x00\"\n\"\\x47\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x4A\\x00\\x00\\x00\\x48\\x00\\x00\\x00\\xFD\\x00\\x01\\x00\\x38\\x00\\x01\\x00\\x36\\x00\\x05\\x00\"\n\"\\x07\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x0A\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x12\\x00\\x00\\x00\"\n\"\\x13\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x1E\\x00\\x00\\x00\\x1F\\x00\\x00\\x00\\x1B\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x14\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x1F\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x51\\x00\\x05\\x00\"\n\"\\x06\\x00\\x00\\x00\\x25\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x26\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x27\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x50\\x00\\x07\\x00\\x07\\x00\\x00\\x00\"\n\"\\x28\\x00\\x00\\x00\\x25\\x00\\x00\\x00\\x26\\x00\\x00\\x00\\x27\\x00\\x00\\x00\\x24\\x00\\x00\\x00\\x91\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x29\\x00\\x00\\x00\"\n\"\\x20\\x00\\x00\\x00\\x28\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x13\\x00\\x00\\x00\\x29\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x2C\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\"\n\"\\x13\\x00\\x00\\x00\\x2B\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x2E\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\"\n\"\\x2F\\x00\\x00\\x00\\x2E\\x00\\x00\\x00\\x2A\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x2C\\x00\\x00\\x00\\x30\\x00\\x00\\x00\\x13\\x00\\x00\\x00\\x2B\\x00\\x00\\x00\"\n\"\\x3E\\x00\\x03\\x00\\x30\\x00\\x00\\x00\\x2F\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x2C\\x00\\x00\\x00\\x32\\x00\\x00\\x00\\x13\\x00\\x00\\x00\\x31\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x33\\x00\\x00\\x00\\x32\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x2C\\x00\\x00\\x00\\x35\\x00\\x00\\x00\\x13\\x00\\x00\\x00\"\n\"\\x34\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x36\\x00\\x00\\x00\\x35\\x00\\x00\\x00\\x81\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x37\\x00\\x00\\x00\"\n\"\\x33\\x00\\x00\\x00\\x36\\x00\\x00\\x00\\x88\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x39\\x00\\x00\\x00\\x37\\x00\\x00\\x00\\x38\\x00\\x00\\x00\\x41\\x00\\x05\\x00\"\n\"\\x2C\\x00\\x00\\x00\\x3A\\x00\\x00\\x00\\x13\\x00\\x00\\x00\\x31\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x3A\\x00\\x00\\x00\\x39\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x07\\x00\\x00\\x00\\x3B\\x00\\x00\\x00\\x13\\x00\\x00\\x00\\xFE\\x00\\x02\\x00\\x3B\\x00\\x00\\x00\\x38\\x00\\x01\\x00\\x03\\x02\\x23\\x07\\x00\\x00\\x01\\x00\"\n\"\\x0A\\x00\\x08\\x00\\x4C\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x11\\x00\\x02\\x00\\x01\\x00\\x00\\x00\\x0B\\x00\\x06\\x00\\x01\\x00\\x00\\x00\\x47\\x4C\\x53\\x4C\"\n\"\\x2E\\x73\\x74\\x64\\x2E\\x34\\x35\\x30\\x00\\x00\\x00\\x00\\x0E\\x00\\x03\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x0F\\x00\\x07\\x00\\x04\\x00\\x00\\x00\"\n\"\\x04\\x00\\x00\\x00\\x6D\\x61\\x69\\x6E\\x00\\x00\\x00\\x00\\x17\\x00\\x00\\x00\\x3B\\x00\\x00\\x00\\x10\\x00\\x03\\x00\\x04\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x0A\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x01\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x02\\x01\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x0C\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x03\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\x13\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x13\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x17\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x2C\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\x3B\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x44\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x45\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x05\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x45\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x45\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x45\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x45\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x45\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\"\n\"\\x48\\x00\\x04\\x00\\x45\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x45\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x80\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x45\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x45\\x00\\x00\\x00\"\n\"\\x03\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xC0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x45\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xCC\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x45\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xD0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x45\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\xDC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x45\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xE0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x45\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xEC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x45\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\xF0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x45\\x00\\x00\\x00\\x0A\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xFC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x45\\x00\\x00\\x00\"\n\"\\x0B\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x45\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x01\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x45\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x4C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x45\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x50\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x45\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x5C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x45\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x60\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x45\\x00\\x00\\x00\\x11\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x6C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x45\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x70\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x45\\x00\\x00\\x00\"\n\"\\x13\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x45\\x00\\x00\\x00\\x14\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x90\\x01\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x45\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x94\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x45\\x00\\x00\\x00\\x16\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x98\\x01\\x00\\x00\\x47\\x00\\x03\\x00\\x45\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x47\\x00\\x00\\x00\\x22\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x47\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x48\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x48\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x48\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x48\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x48\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x4C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x48\\x00\\x00\\x00\\x03\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x50\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x48\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x5C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x48\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x60\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x48\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x6C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x48\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x70\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x48\\x00\\x00\\x00\"\n\"\\x08\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x48\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x88\\x00\\x00\\x00\"\n\"\\x47\\x00\\x03\\x00\\x48\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x4A\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\"\n\"\\x4A\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x13\\x00\\x02\\x00\\x02\\x00\\x00\\x00\\x21\\x00\\x03\\x00\\x03\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x16\\x00\\x03\\x00\\x06\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x17\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x20\\x00\\x04\\x00\"\n\"\\x08\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x08\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x32\\x00\\x04\\x00\"\n\"\\x06\\x00\\x00\\x00\\x0A\\x00\\x00\\x00\\x00\\x80\\x80\\x43\\x32\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x00\\x00\\x81\\x43\\x32\\x00\\x04\\x00\"\n\"\\x06\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x00\\x80\\x81\\x43\\x20\\x00\\x04\\x00\\x0E\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x19\\x00\\x09\\x00\"\n\"\\x10\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x1B\\x00\\x03\\x00\\x11\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x12\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x11\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\"\n\"\\x12\\x00\\x00\\x00\\x13\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x17\\x00\\x04\\x00\\x15\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x20\\x00\\x04\\x00\"\n\"\\x16\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x17\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x17\\x00\\x04\\x00\"\n\"\\x19\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x1C\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\"\n\"\\x06\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x87\\x16\\x99\\x3E\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x1F\\x00\\x00\\x00\\xA2\\x45\\x16\\x3F\\x2B\\x00\\x04\\x00\"\n\"\\x06\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\xD5\\x78\\xE9\\x3D\\x2C\\x00\\x06\\x00\\x07\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x1F\\x00\\x00\\x00\"\n\"\\x20\\x00\\x00\\x00\\x15\\x00\\x04\\x00\\x2B\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x32\\x00\\x04\\x00\\x2B\\x00\\x00\\x00\\x2C\\x00\\x00\\x00\"\n\"\\x00\\x01\\x00\\x00\\x2B\\x00\\x04\\x00\\x2B\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x14\\x00\\x02\\x00\\x2E\\x00\\x00\\x00\\x34\\x00\\x06\\x00\"\n\"\\x2E\\x00\\x00\\x00\\x2F\\x00\\x00\\x00\\xAD\\x00\\x00\\x00\\x2C\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x33\\x00\\x00\\x00\"\n\"\\x00\\x00\\x80\\x3F\\x2C\\x00\\x06\\x00\\x07\\x00\\x00\\x00\\x34\\x00\\x00\\x00\\x33\\x00\\x00\\x00\\x33\\x00\\x00\\x00\\x33\\x00\\x00\\x00\\x20\\x00\\x04\\x00\"\n\"\\x3A\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x19\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x3A\\x00\\x00\\x00\\x3B\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x18\\x00\\x04\\x00\"\n\"\\x41\\x00\\x00\\x00\\x19\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x15\\x00\\x04\\x00\\x42\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\"\n\"\\x42\\x00\\x00\\x00\\x43\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x1C\\x00\\x04\\x00\\x44\\x00\\x00\\x00\\x19\\x00\\x00\\x00\\x43\\x00\\x00\\x00\\x1E\\x00\\x19\\x00\"\n\"\\x45\\x00\\x00\\x00\\x41\\x00\\x00\\x00\\x41\\x00\\x00\\x00\\x41\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x44\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x19\\x00\\x00\\x00\\x19\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x15\\x00\\x00\\x00\"\n\"\\x20\\x00\\x04\\x00\\x46\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x45\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x46\\x00\\x00\\x00\\x47\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x1E\\x00\\x0C\\x00\\x48\\x00\\x00\\x00\\x41\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x19\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x49\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x48\\x00\\x00\\x00\"\n\"\\x3B\\x00\\x04\\x00\\x49\\x00\\x00\\x00\\x4A\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x2C\\x00\\x07\\x00\\x19\\x00\\x00\\x00\\x4B\\x00\\x00\\x00\\x33\\x00\\x00\\x00\"\n\"\\x33\\x00\\x00\\x00\\x33\\x00\\x00\\x00\\x33\\x00\\x00\\x00\\x36\\x00\\x05\\x00\\x02\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x03\\x00\\x00\\x00\"\n\"\\xF8\\x00\\x02\\x00\\x05\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x0E\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x1C\\x00\\x00\\x00\"\n\"\\x1D\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x0E\\x00\\x00\\x00\\x30\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x50\\x00\\x06\\x00\\x07\\x00\\x00\\x00\"\n\"\\x0D\\x00\\x00\\x00\\x0A\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x09\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x11\\x00\\x00\\x00\\x14\\x00\\x00\\x00\\x13\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x15\\x00\\x00\\x00\\x18\\x00\\x00\\x00\\x17\\x00\\x00\\x00\\x57\\x00\\x05\\x00\"\n\"\\x19\\x00\\x00\\x00\\x1A\\x00\\x00\\x00\\x14\\x00\\x00\\x00\\x18\\x00\\x00\\x00\\x4F\\x00\\x08\\x00\\x07\\x00\\x00\\x00\\x1B\\x00\\x00\\x00\\x1A\\x00\\x00\\x00\"\n\"\\x1A\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x0F\\x00\\x00\\x00\\x1B\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x07\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\\x94\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x22\\x00\\x00\\x00\"\n\"\\x3E\\x00\\x03\\x00\\x1D\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x24\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x06\\x00\\x00\\x00\\x25\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x26\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x50\\x00\\x06\\x00\"\n\"\\x07\\x00\\x00\\x00\\x27\\x00\\x00\\x00\\x24\\x00\\x00\\x00\\x25\\x00\\x00\\x00\\x26\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x0F\\x00\\x00\\x00\\x27\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x28\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x29\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\"\n\"\\x85\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x2A\\x00\\x00\\x00\\x29\\x00\\x00\\x00\\x28\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x0F\\x00\\x00\\x00\\x2A\\x00\\x00\\x00\"\n\"\\xF7\\x00\\x03\\x00\\x32\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\\x2F\\x00\\x00\\x00\\x31\\x00\\x00\\x00\\x37\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\"\n\"\\x31\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x35\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\\x83\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x36\\x00\\x00\\x00\"\n\"\\x34\\x00\\x00\\x00\\x35\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x30\\x00\\x00\\x00\\x36\\x00\\x00\\x00\\xF9\\x00\\x02\\x00\\x32\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\"\n\"\\x37\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x38\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x30\\x00\\x00\\x00\\x38\\x00\\x00\\x00\"\n\"\\xF9\\x00\\x02\\x00\\x32\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x32\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x39\\x00\\x00\\x00\\x30\\x00\\x00\\x00\"\n\"\\x3E\\x00\\x03\\x00\\x0F\\x00\\x00\\x00\\x39\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x3C\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\\x51\\x00\\x05\\x00\"\n\"\\x06\\x00\\x00\\x00\\x3D\\x00\\x00\\x00\\x3C\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x3E\\x00\\x00\\x00\\x3C\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x3F\\x00\\x00\\x00\\x3C\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x50\\x00\\x07\\x00\\x19\\x00\\x00\\x00\"\n\"\\x40\\x00\\x00\\x00\\x3D\\x00\\x00\\x00\\x3E\\x00\\x00\\x00\\x3F\\x00\\x00\\x00\\x33\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x3B\\x00\\x00\\x00\\x40\\x00\\x00\\x00\"\n\"\\xFD\\x00\\x01\\x00\\x38\\x00\\x01\\x00\"},\n#endif\n#ifdef D3D11QUAKE\n{QR_DIRECT3D11, 11, \"menutint\",\n\"!!samps 1\\n\"\n\"!!cvard3 r_menutint=0.2 0.2 0.2\\n\"\n\"!!cvardf r_menutint_inverse=0.0\\n\"\n\n\"struct a2v\\n\"\n\"{\\n\"\n\"float4 pos: POSITION;\\n\"\n\"float2 tc: TEXCOORD0;\\n\"\n\"float4 vcol: COLOR0;\\n\"\n\"};\\n\"\n\"struct v2f\\n\"\n\"{\\n\"\n\"float4 pos: SV_POSITION;\\n\"\n\"float2 tc: TEXCOORD0;\\n\"\n\"float4 vcol: COLOR0;\\n\"\n\"};\\n\"\n\n\"#include <ftedefs.h>\\n\"\n\n\"#ifdef VERTEX_SHADER\\n\"\n\"v2f main (a2v inp)\\n\"\n\"{\\n\"\n\"v2f outp;\\n\"\n\"outp.pos = mul(m_projection, inp.pos);\\n\"\n\"outp.tc = inp.tc;\\n\"\n\"outp.vcol = inp.vcol;\\n\"\n\"return outp;\\n\"\n\"}\\n\"\n\"#endif\\n\"\n\n\"#ifdef FRAGMENT_SHADER\\n\"\n\"Texture2D t_t0;\\n\"\n\"SamplerState s_t0;\\n\"\n\"static const float3 lumfactors = float3 (0.299, 0.587, 0.114);\\n\"\n\"float4 main (v2f inp) : SV_TARGET\\n\"\n\"{\\n\"\n\"float4 texcolor = t_t0.Sample(s_t0, inp.tc);\\n\"\n\"float luminance = dot(lumfactors, texcolor.rgb);\\n\"\n\"texcolor.rgb = float3(luminance, luminance, luminance);\\n\"\n\"texcolor.rgb *= r_menutint;\\n\"\n\"texcolor.rgb = (r_menutint_inverse > 0) ? (1.0 - texcolor.rgb) : texcolor.rgb;\\n\"\n\n\"return texcolor * inp.vcol;\\n\"\n\"}\\n\"\n\"#endif\\n\"\n\n},\n#endif\n#ifdef GLQUAKE\n{QR_OPENGL, 110, \"terrain\",\n\"!!ver 100 300\\n\"\n\"!!permu FOG\\n\"\n//RTLIGHT (+PCF,CUBE,SPOT,etc)\n\"!!samps tr=0 tg=1 tb=2 tx=3 //the four texturemaps\\n\"\n\"!!samps mix=4 //how the ground is blended\\n\"\n\"!!samps =PCF shadowmap\\n\"\n\"!!samps =CUBE projectionmap\\n\"\n\n//light levels\n\n\"#include \\\"sys/fog.h\\\"\\n\"\n\"varying vec2 tc;\\n\"\n\"varying vec2 lm;\\n\"\n\"varying vec4 vc;\\n\"\n\n\"#ifdef RTLIGHT\\n\"\n\"varying vec3 lightvector;\\n\"\n//\t#if defined(SPECULAR) || defined(OFFSETMAPPING)\n//\t\tvarying vec3 eyevector;\n//\t#endif\n\"#if defined(PCF) || defined(CUBE) || defined(SPOT)\\n\"\n\"varying vec4 vtexprojcoord;\\n\"\n\"#endif\\n\"\n\"#endif\\n\"\n\n\n\n\n\n\"#ifdef VERTEX_SHADER\\n\"\n\n\"#ifdef RTLIGHT\\n\"\n\"uniform vec3 l_lightposition;\\n\"\n//\t#if defined(SPECULAR) || defined(OFFSETMAPPING)\n//\t\tuniform vec3 e_eyepos;\n//\t#endif\n\"#if defined(PCF) || defined(CUBE) || defined(SPOT)\\n\"\n\"uniform mat4 l_cubematrix;\\n\"\n\"#endif\\n\"\n\"attribute vec3 v_normal;\\n\"\n\"attribute vec3 v_svector;\\n\"\n\"attribute vec3 v_tvector;\\n\"\n\"#endif\\n\"\n\n\"attribute vec2 v_texcoord;\\n\"\n\"attribute vec2 v_lmcoord;\\n\"\n\"attribute vec4 v_colour;\\n\"\n\n\"void main (void)\\n\"\n\"{\\n\"\n\"tc = v_texcoord.st;\\n\"\n\"lm = v_lmcoord.st;\\n\"\n\"vc = v_colour;\\n\"\n\"gl_Position = ftetransform();\\n\"\n\n\"#ifdef RTLIGHT\\n\"\n//light position is in model space, which is handy.\n\"vec3 lightminusvertex = l_lightposition - v_position.xyz;\\n\"\n\n//no bumpmapping, so we can just use distance without regard for actual surface direction. we still do scalecos stuff. you might notice it on steep slopes.\n\"lightvector = lightminusvertex;\\n\"\n//\t\tlightvector.x = dot(lightminusvertex, v_svector.xyz);\n//\t\tlightvector.y = dot(lightminusvertex, v_tvector.xyz);\n//\t\tlightvector.z = dot(lightminusvertex, v_normal.xyz);\n\n//\t\t#if defined(SPECULAR)||defined(OFFSETMAPPING)\n//\t\t\tvec3 eyeminusvertex = e_eyepos - v_position.xyz;\n//\t\t\teyevector.x = dot(eyeminusvertex, v_svector.xyz);\n//\t\t\teyevector.y = dot(eyeminusvertex, v_tvector.xyz);\n//\t\t\teyevector.z = dot(eyeminusvertex, v_normal.xyz);\n//\t\t#endif\n\"#if defined(PCF) || defined(SPOT) || defined(CUBE)\\n\"\n//for texture projections/shadowmapping on dlights\n\"vtexprojcoord = (l_cubematrix*vec4(v_position.xyz, 1.0));\\n\"\n\"#endif\\n\"\n\"#endif\\n\"\n\"}\\n\"\n\"#endif\\n\"\n\n\n\n\n\"#ifdef FRAGMENT_SHADER\\n\"\n\"#ifdef PCF\\n\"\n\"#include \\\"sys/pcf.h\\\"\\n\"\n\"#endif\\n\"\n\n//light levels\n\"uniform vec4 e_lmscale;\\n\"\n\n\"#ifdef RTLIGHT\\n\"\n\"uniform float l_lightradius;\\n\"\n\"uniform vec3 l_lightcolour;\\n\"\n\"uniform vec3 l_lightcolourscale;\\n\"\n\"#endif\\n\"\n\n\"void main (void)\\n\"\n\"{\\n\"\n\"vec4 r;\\n\"\n\"vec4 m = texture2D(s_mix, lm);\\n\"\n\n\"r  = texture2D(s_tr, tc)*m.r;\\n\"\n\"r += texture2D(s_tg, tc)*m.g;\\n\"\n\"r += texture2D(s_tb, tc)*m.b;\\n\"\n\"r += texture2D(s_tx, tc)*(1.0 - (m.r + m.g + m.b));\\n\"\n\n\"r.rgb *= 1.0/r.a; //fancy maths, so low alpha values give other textures a greater focus\\n\"\n\n//vertex colours provide a scaler that applies even through rtlights.\n\"r *= vc;\\n\"\n\n\"#ifdef RTLIGHT\\n\"\n\"vec3 nl = normalize(lightvector);\\n\"\n\"float colorscale = max(1.0 - (dot(lightvector, lightvector)/(l_lightradius*l_lightradius)), 0.0);\\n\"\n\"vec3 diff;\\n\"\n//\t#ifdef BUMP\n//\t\tcolorscale *= (l_lightcolourscale.x + l_lightcolourscale.y * max(dot(bumps, nl), 0.0));\n//\t#else\n\"colorscale *= (l_lightcolourscale.x + l_lightcolourscale.y * max(dot(vec3(0.0, 0.0, 1.0), nl), 0.0));\\n\"\n//\t#endif\n\n//\t#ifdef SPECULAR\n//\t\tvec3 halfdir = normalize(normalize(eyevector) + nl);\n//\t\tfloat spec = pow(max(dot(halfdir, bumps), 0.0), 32.0 * specs.a);\n//\t\tdiff += l_lightcolourscale.z * spec * specs.rgb;\n//\t#endif\n\n\n\n\"#if defined(SPOT)\\n\"\n\"if (vtexprojcoord.w < 0.0) discard;\\n\"\n\"vec2 spot = ((vtexprojcoord.st)/vtexprojcoord.w);\\n\"\n\"colorscale *= 1.0-(dot(spot,spot));\\n\"\n\"#endif\\n\"\n\"#ifdef PCF\\n\"\n\"colorscale *= ShadowmapFilter(s_shadowmap, vtexprojcoord);\\n\"\n\"#endif\\n\"\n\n\"r.rgb *= colorscale * l_lightcolour;\\n\"\n\n\"#ifdef CUBE\\n\"\n\"r.rgb *= textureCube(s_projectionmap, vtexprojcoord.xyz).rgb;\\n\"\n\"#endif\\n\"\n\n\"gl_FragColor = fog4additive(r);\\n\"\n\"#else\\n\"\n//lightmap is greyscale in m.a. probably we should just scale the texture mix, but precision errors when editing make me paranoid.\n\"r *= e_lmscale*vec4(m.aaa,1.0);\\n\"\n\"gl_FragColor = fog4(r);\\n\"\n\"#endif\\n\"\n\"}\\n\"\n\"#endif\\n\"\n},\n#endif\n#ifdef D3D9QUAKE\n{QR_DIRECT3D9, 9, \"terrain\",\n\"!!permu FOG\\n\"\n\"!!samps 5\\n\"\n\n\"#include \\\"sys/fog.h\\\"\\n\"\n\n//FIXME: too lazy to implement this right now. rtlights on d3d9 are just bad right now.\n\"#undef RTLIGHT\\n\"\n\"#undef PCF\\n\"\n\"#undef CUBE\\n\"\n\n\"struct a2v\\n\"\n\"{\\n\"\n\"float3 pos: POSITION0;\\n\"\n\"float2 tc: TEXCOORD0;\\n\"\n\"float2 lm: TEXCOORD1;\\n\"\n\"float4 vc: COLOR;\\n\"\n\n\"#ifdef RTLIGHT\\n\"\n\"attribute vec3 v_normal;\\n\"\n\"attribute vec3 v_svector;\\n\"\n\"attribute vec3 v_tvector;\\n\"\n\"#endif\\n\"\n\"};\\n\"\n\"struct v2f\\n\"\n\"{\\n\"\n\"float1 depth: TEXCOORD1;\\n\"\n\"float4 tclm: TEXCOORD0;\\n\"\n\"float4 vc: COLOR;\\n\"\n\n\"#ifdef RTLIGHT\\n\"\n\"varying vec3 lightvector;\\n\"\n//\t#if defined(SPECULAR) || defined(OFFSETMAPPING)\n//\t\tvarying vec3 eyevector;\n//\t#endif\n\"#if defined(PCF) || defined(CUBE) || defined(SPOT)\\n\"\n\"varying vec4 vtexprojcoord;\\n\"\n\"#endif\\n\"\n\"#endif\\n\"\n\n\"#ifndef FRAGMENT_SHADER\\n\"\n\"float4 pos: POSITION;\\n\"\n\"#endif\\n\"\n\"};\\n\"\n\n\n\n\n\n\"#ifdef VERTEX_SHADER\\n\"\n\n\"float4x4  m_model;\\n\"\n\"float4x4  m_view;\\n\"\n\"float4x4  m_projection;\\n\"\n\n\"#ifdef RTLIGHT\\n\"\n\"uniform vec3 l_lightposition;\\n\"\n//\t#if defined(SPECULAR) || defined(OFFSETMAPPING)\n//\t\tuniform vec3 e_eyepos;\n//\t#endif\n\"#if defined(PCF) || defined(CUBE) || defined(SPOT)\\n\"\n\"uniform mat4 l_cubematrix;\\n\"\n\"#endif\\n\"\n\"#endif\\n\"\n\n\"float3 e_lmscale;\\n\"\n\n\"v2f main (a2v inp)\\n\"\n\"{\\n\"\n\"v2f outp;\\n\"\n\"outp.tclm = float4(inp.tc, inp.lm);\\n\"\n\"outp.vc = inp.vc;\\n\"\n\n\"float4 pos = float4(inp.pos, 1);\\n\"\n\"pos = mul(m_model, pos);\\n\"\n\"pos = mul(m_view, pos);\\n\"\n\"outp.depth = pos.z;\\n\"\n\"pos = mul(m_projection, pos);\\n\"\n\"outp.pos = pos;\\n\"\n\n\"#ifdef RTLIGHT\\n\"\n//light position is in model space, which is handy.\n\"vec3 lightminusvertex = l_lightposition - v_position.xyz;\\n\"\n\n//no bumpmapping, so we can just use distance without regard for actual surface direction. we still do scalecos stuff. you might notice it on steep slopes.\n\"lightvector = lightminusvertex;\\n\"\n//\t\tlightvector.x = dot(lightminusvertex, v_svector.xyz);\n//\t\tlightvector.y = dot(lightminusvertex, v_tvector.xyz);\n//\t\tlightvector.z = dot(lightminusvertex, v_normal.xyz);\n\n//\t\t#if defined(SPECULAR)||defined(OFFSETMAPPING)\n//\t\t\tvec3 eyeminusvertex = e_eyepos - v_position.xyz;\n//\t\t\teyevector.x = dot(eyeminusvertex, v_svector.xyz);\n//\t\t\teyevector.y = dot(eyeminusvertex, v_tvector.xyz);\n//\t\t\teyevector.z = dot(eyeminusvertex, v_normal.xyz);\n//\t\t#endif\n\"#if defined(PCF) || defined(SPOT) || defined(CUBE)\\n\"\n//for texture projections/shadowmapping on dlights\n\"vtexprojcoord = (l_cubematrix*vec4(v_position.xyz, 1.0));\\n\"\n\"#endif\\n\"\n\"#else\\n\"\n\"outp.vc.rgb *= e_lmscale.rgb;\\n\"\n\"#endif\\n\"\n\n\"return outp;\\n\"\n\"}\\n\"\n\"#endif\\n\"\n\n\n\n\n\"#ifdef FRAGMENT_SHADER\\n\"\n//four texture passes\n\"sampler s_t0;\\n\"\n\"sampler s_t1;\\n\"\n\"sampler s_t2;\\n\"\n\"sampler s_t3;\\n\"\n\n//mix values\n\"sampler s_t4;\\n\"\n\n\"#ifdef PCF\\n\"\n\"uniform sampler2DShadow s_t5;\\n\"\n\"#include \\\"sys/pcf.h\\\"\\n\"\n\"#endif\\n\"\n\"#ifdef CUBE\\n\"\n\"uniform samplerCube s_t6;\\n\"\n\"#endif\\n\"\n\n\"#ifdef RTLIGHT\\n\"\n\"uniform float l_lightradius;\\n\"\n\"uniform vec3 l_lightcolour;\\n\"\n\"uniform vec3 l_lightcolourscale;\\n\"\n\"#endif\\n\"\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\"float4 main (v2f inp) : COLOR\\n\"\n\"{\\n\"\n\"float2 lm = inp.tclm.zw;\\n\"\n\"float2 tc = inp.tclm.xy;\\n\"\n\"float4 vc = inp.vc;\\n\"\n\n\"float4 r;\\n\"\n\"float4 m = tex2D(s_t4, lm);\\n\"\n\n\"r  = tex2D(s_t0, tc)*m.r;\\n\"\n\"r += tex2D(s_t1, tc)*m.g;\\n\"\n\"r += tex2D(s_t2, tc)*m.b;\\n\"\n\"r += tex2D(s_t3, tc)*(1.0 - (m.r + m.g + m.b));\\n\"\n\"r.a = 1.0;\\n\"\n\n//vertex colours provide a scaler that applies even through rtlights.\n\"r *= vc;\\n\"\n\n\"#ifdef RTLIGHT\\n\"\n\"vec3 nl = normalize(lightvector);\\n\"\n\"float colorscale = max(1.0 - (dot(lightvector, lightvector)/(l_lightradius*l_lightradius)), 0.0);\\n\"\n\"vec3 diff;\\n\"\n//\t#ifdef BUMP\n//\t\tcolorscale *= (l_lightcolourscale.x + l_lightcolourscale.y * max(dot(bumps, nl), 0.0));\n//\t#else\n\"colorscale *= (l_lightcolourscale.x + l_lightcolourscale.y * max(dot(vec3(0.0, 0.0, 1.0), nl), 0.0));\\n\"\n//\t#endif\n\n//\t#ifdef SPECULAR\n//\t\tvec3 halfdir = normalize(normalize(eyevector) + nl);\n//\t\tfloat spec = pow(max(dot(halfdir, bumps), 0.0), 32.0 * specs.a);\n//\t\tdiff += l_lightcolourscale.z * spec * specs.rgb;\n//\t#endif\n\n\n\n\"#if defined(SPOT)\\n\"\n\"if (vtexprojcoord.w < 0.0) discard;\\n\"\n\"vec2 spot = ((vtexprojcoord.st)/vtexprojcoord.w);\\n\"\n\"colorscale *= 1.0-(dot(spot,spot));\\n\"\n\"#endif\\n\"\n\"#ifdef PCF\\n\"\n\"colorscale *= ShadowmapFilter(s_t5);\\n\"\n\"#endif\\n\"\n\n\"r.rgb *= colorscale * l_lightcolour;\\n\"\n\n\"#ifdef CUBE\\n\"\n\"r.rgb *= textureCube(s_t6, vtexprojcoord.xyz).rgb;\\n\"\n\"#endif\\n\"\n\n\"r = fog4additive(r, inp.pos);\\n\"\n\"#else\\n\"\n//lightmap is greyscale in m.a. probably we should just scale the texture mix, but precision errors when editing make me paranoid.\n\"r.rgb *= m.aaa;\\n\"\n\"r = fog4(r, inp.depth);\\n\"\n\"#endif\\n\"\n\"return r;\\n\"\n\"}\\n\"\n\"#endif\\n\"\n},\n#endif\n#ifdef D3D11QUAKE\n{QR_DIRECT3D11, 11, \"terrain\",\n\"struct a2v\\n\"\n\"{\\n\"\n\"float4 pos: POSITION;\\n\"\n\"float2 tc: TEXCOORD0;\\n\"\n\"float2 lmtc: TEXCOORD0;\\n\"\n\"float4 vcol: COLOR0;\\n\"\n\"};\\n\"\n\"struct v2f\\n\"\n\"{\\n\"\n\"float4 pos: SV_POSITION;\\n\"\n\"float2 tc: TEXCOORD0;\\n\"\n\"float2 lmtc: TEXCOORD1;\\n\"\n\"float4 vcol: COLOR0;\\n\"\n\"float3 vtexprojcoord: TEXCOORD2;\\n\"\n\"float3 vtexprojcoord: TEXCOORD2;\\n\"\n\"};\\n\"\n\n\"#include <ftedefs.h>\\n\"\n\n\"#ifdef VERTEX_SHADER\\n\"\n\"v2f main (a2v inp)\\n\"\n\"{\\n\"\n\"v2f outp;\\n\"\n\"outp.pos = mul(m_model, inp.pos);\\n\"\n\"outp.pos = mul(m_view, outp.pos);\\n\"\n\"outp.pos = mul(m_projection, outp.pos);\\n\"\n\"outp.tc = inp.tc;\\n\"\n\"outp.lmtc = inp.lmtc;\\n\"\n\"outp.vcol = inp.vcol;\\n\"\n\"return outp;\\n\"\n\"}\\n\"\n\"#endif\\n\"\n\n\"#ifdef FRAGMENT_SHADER\\n\"\n\"Texture2D shaderTexture[7];\\n\"\n\"SamplerState SampleType[7];\\n\"\n\n\"float4 main (v2f inp) : SV_TARGET\\n\"\n\"{\\n\"\n\"float4 result;\\n\"\n\n\"float4 base = shaderTexture[0].Sample(SampleType[0], inp.tc);\\n\"\n\"#ifdef BUMP\\n\"\n\"float4 bump = shaderTexture[1].Sample(SampleType[1], inp.tc);\\n\"\n\"#else\\n\"\n\"float4 bump = float4(0, 0, 1, 0);\\n\"\n\"#endif\\n\"\n\"float4 spec = shaderTexture[2].Sample(SampleType[2], inp.tc);\\n\"\n\"#ifdef CUBE\\n\"\n\"float4 cubemap = shaderTexture[3].Sample(SampleType[3], inp.vtexprojcoord);\\n\"\n\"#endif\\n\"\n//shadowmap 2d\n\"#ifdef LOWER\\n\"\n\"float4 lower = shaderTexture[5].Sample(SampleType[5], inp.tc);\\n\"\n\"base += lower;\\n\"\n\"#endif\\n\"\n\"#ifdef UPPER\\n\"\n\"float4 upper = shaderTexture[6].Sample(SampleType[6], inp.tc);\\n\"\n\"base += upper;\\n\"\n\"#endif\\n\"\n\n\"float lightscale = max(1.0 - (dot(inp.lightvector,inp.lightvector)/(l_lightradius*l_lightradius)), 0.0);\\n\"\n\"float3 nl = normalize(inp.lightvector);\\n\"\n\"float bumpscale = max(dot(bump.xyz, nl), 0.0);\\n\"\n\"float3 halfdir = normalize(normalize(eyevector) + nl);\\n\"\n\"float specscale = pow(max(dot(halfdir, bumps), 0.0), 32.0 * spec.a);\\n\"\n\n\"result.a = base.a;\\n\"\n\"result.rgb = base.rgb * (l_lightcolourscale.x + l_lightcolourscale.y * bumpscale); //amient light + diffuse\\n\"\n\"result.rgb += spec.rgb * l_lightcolourscale.z * specscale; //specular\\n\"\n\n\"result.rgb *= lightscale; //fade light by distance\\n\"\n\n\"#ifdef CUBE\\n\"\n\"result.rgb *= cubemap.rgb; //fade by cubemap\\n\"\n\"#endif\\n\"\n\n\"return result;\\n\"\n\"}\\n\"\n\"#endif\\n\"\n},\n#endif\n#ifdef GLQUAKE\n{QR_OPENGL, 110, \"rtlight\",\n\"!!ver 100 300\\n\"\n\"!!permu TESS\\n\"\n\"!!permu BUMP\\n\"\n\"!!permu FRAMEBLEND\\n\"\n\"!!permu SKELETAL\\n\"\n\"!!permu UPPERLOWER\\n\"\n\"!!permu FOG\\n\"\n\"!!permu REFLECTCUBEMASK\\n\"\n\"!!cvarf r_glsl_offsetmapping_scale\\n\"\n\"!!cvardf r_glsl_pcf\\n\"\n\"!!cvardf r_tessellation_level=5\\n\"\n\"!!samps diffuse normalmap specular upper lower reflectcube reflectmask\\n\"\n\"!!samps =PCF shadowmap\\n\"\n\"!!samps =CUBE projectionmap\\n\"\n\n\"#if defined(ORM) || defined(SG)\\n\"\n\"#define PBR\\n\"\n\"#endif\\n\"\n\n\"#include \\\"sys/defs.h\\\"\\n\"\n\n//this is the main shader responsible for realtime dlights.\n\n//texture units:\n//s0=diffuse, s1=normal, s2=specular, s3=shadowmap\n//custom modifiers:\n//PCF(shadowmap)\n//CUBEPROJ(projected cubemap)\n//SPOT(projected circle\n//CUBESHADOW\n\n\"#if 0 && defined(GL_ARB_texture_gather) && defined(PCF) \\n\"\n\"#extension GL_ARB_texture_gather : enable\\n\"\n\"#endif\\n\"\n\n\"#ifdef UPPERLOWER\\n\"\n\"#define UPPER\\n\"\n\"#define LOWER\\n\"\n\"#endif\\n\"\n\n//if there's no vertex normals known, disable some stuff.\n//FIXME: this results in dupe permutations.\n\"#ifdef NOBUMP\\n\"\n\"#undef SPECULAR\\n\"\n\"#undef BUMP\\n\"\n\"#undef OFFSETMAPPING\\n\"\n\"#endif\\n\"\n\n\"#if !defined(TESS_CONTROL_SHADER)\\n\"\n\"varying vec2 tcbase;\\n\"\n\"varying vec3 lightvector;\\n\"\n\"#if defined(VERTEXCOLOURS)\\n\"\n\"varying vec4 vc;\\n\"\n\"#endif\\n\"\n\"#if defined(SPECULAR) || defined(OFFSETMAPPING) || defined(REFLECTCUBEMASK) || defined(PBR)\\n\"\n\"varying vec3 eyevector;\\n\"\n\"#endif\\n\"\n\"#ifdef REFLECTCUBEMASK\\n\"\n\"varying mat3 invsurface;\\n\"\n\"#endif\\n\"\n\"#if defined(PCF) || defined(CUBE) || defined(SPOT) || defined(ORTHO)\\n\"\n\"varying vec4 vtexprojcoord;\\n\"\n\"#endif\\n\"\n\"#endif\\n\"\n\n\n\"#ifdef VERTEX_SHADER\\n\"\n\"#ifdef TESS\\n\"\n\"varying vec3 vertex, normal;\\n\"\n\"#endif\\n\"\n\"#include \\\"sys/skeletal.h\\\"\\n\"\n\"void main ()\\n\"\n\"{\\n\"\n\"vec3 n, s, t, w;\\n\"\n\"gl_Position = skeletaltransform_wnst(w,n,s,t);\\n\"\n\"n = normalize(n);\\n\"\n\"s = normalize(s);\\n\"\n\"t = normalize(t);\\n\"\n\"tcbase = v_texcoord; //pass the texture coords straight through\\n\"\n\"#ifdef ORTHO\\n\"\n\"vec3 lightminusvertex = -l_lightdirection;\\n\"\n\"lightvector.x = dot(lightminusvertex, s.xyz);\\n\"\n\"lightvector.y = dot(lightminusvertex, t.xyz);\\n\"\n\"lightvector.z = dot(lightminusvertex, n.xyz);\\n\"\n\"#else\\n\"\n\"vec3 lightminusvertex = l_lightposition - w.xyz;\\n\"\n\"#ifdef NOBUMP\\n\"\n//the only important thing is distance\n\"lightvector = lightminusvertex;\\n\"\n\"#else\\n\"\n//the light direction relative to the surface normal, for bumpmapping.\n\"lightvector.x = dot(lightminusvertex, s.xyz);\\n\"\n\"lightvector.y = dot(lightminusvertex, t.xyz);\\n\"\n\"lightvector.z = dot(lightminusvertex, n.xyz);\\n\"\n\"#endif\\n\"\n\"#endif\\n\"\n\"#if defined(VERTEXCOLOURS)\\n\"\n\"vc = v_colour;\\n\"\n\"#endif\\n\"\n\"#if defined(SPECULAR)||defined(OFFSETMAPPING) || defined(REFLECTCUBEMASK) || defined(PBR)\\n\"\n\"vec3 eyeminusvertex = e_eyepos - w.xyz;\\n\"\n\"eyevector.x = dot(eyeminusvertex, s.xyz);\\n\"\n\"eyevector.y = dot(eyeminusvertex, t.xyz);\\n\"\n\"eyevector.z = dot(eyeminusvertex, n.xyz);\\n\"\n\"#endif\\n\"\n\"#ifdef REFLECTCUBEMASK\\n\"\n\"invsurface = mat3(v_svector, v_tvector, v_normal);\\n\"\n\"#endif\\n\"\n\"#if defined(PCF) || defined(SPOT) || defined(CUBE) || defined(ORTHO)\\n\"\n//for texture projections/shadowmapping on dlights\n\"vtexprojcoord = (l_cubematrix*vec4(w.xyz, 1.0));\\n\"\n\"#endif\\n\"\n\n\"#ifdef TESS\\n\"\n\"vertex = w;\\n\"\n\"normal = n;\\n\"\n\"#endif\\n\"\n\"}\\n\"\n\"#endif\\n\"\n\n\n\n\n\n\n\"#if defined(TESS_CONTROL_SHADER)\\n\"\n\"layout(vertices = 3) out;\\n\"\n\n\"in vec3 vertex[];\\n\"\n\"out vec3 t_vertex[];\\n\"\n\"in vec3 normal[];\\n\"\n\"out vec3 t_normal[];\\n\"\n\"in vec2 tcbase[];\\n\"\n\"out vec2 t_tcbase[];\\n\"\n\"in vec3 lightvector[];\\n\"\n\"out vec3 t_lightvector[];\\n\"\n\"#if defined(VERTEXCOLOURS)\\n\"\n\"in vec4 vc[];\\n\"\n\"out vec4 t_vc[];\\n\"\n\"#endif\\n\"\n\"#if defined(SPECULAR) || defined(OFFSETMAPPING) || defined(REFLECTCUBEMASK) || defined(PBR)\\n\"\n\"in vec3 eyevector[];\\n\"\n\"out vec3 t_eyevector[];\\n\"\n\"#endif\\n\"\n\"void main()\\n\"\n\"{\\n\"\n//the control shader needs to pass stuff through\n\"#define id gl_InvocationID\\n\"\n\"t_vertex[id] = vertex[id];\\n\"\n\"t_normal[id] = normal[id];\\n\"\n\"t_tcbase[id] = tcbase[id];\\n\"\n\"t_lightvector[id] = lightvector[id];\\n\"\n\"#if defined(VERTEXCOLOURS)\\n\"\n\"t_vc[id] = vc[id];\\n\"\n\"#endif\\n\"\n\"#if defined(SPECULAR) || defined(OFFSETMAPPING) || defined(REFLECTCUBEMASK) || defined(PBR)\\n\"\n\"t_eyevector[id] = eyevector[id];\\n\"\n\"#endif\\n\"\n\n\"gl_TessLevelOuter[0] = float(r_tessellation_level);\\n\"\n\"gl_TessLevelOuter[1] = float(r_tessellation_level);\\n\"\n\"gl_TessLevelOuter[2] = float(r_tessellation_level);\\n\"\n\"gl_TessLevelInner[0] = float(r_tessellation_level);\\n\"\n\"}\\n\"\n\"#endif\\n\"\n\n\n\n\n\n\n\n\n\n\"#if defined(TESS_EVALUATION_SHADER)\\n\"\n\"layout(triangles) in;\\n\"\n\n\"in vec3 t_vertex[];\\n\"\n\"in vec3 t_normal[];\\n\"\n\"in vec2 t_tcbase[];\\n\"\n\"in vec3 t_lightvector[];\\n\"\n\"#if defined(VERTEXCOLOURS)\\n\"\n\"in vec4 t_vc[];\\n\"\n\"#endif\\n\"\n\"#if defined(SPECULAR) || defined(OFFSETMAPPING) || defined(REFLECTCUBEMASK) || defined(PBR)\\n\"\n\"in vec3 t_eyevector[];\\n\"\n\"#endif\\n\"\n\n\"#define LERP(a) (gl_TessCoord.x*a[0] + gl_TessCoord.y*a[1] + gl_TessCoord.z*a[2])\\n\"\n\"void main()\\n\"\n\"{\\n\"\n\"#define factor 1.0\\n\"\n\"tcbase = LERP(t_tcbase);\\n\"\n\"vec3 w = LERP(t_vertex);\\n\"\n\n\"vec3 t0 = w - dot(w-t_vertex[0],t_normal[0])*t_normal[0];\\n\"\n\"vec3 t1 = w - dot(w-t_vertex[1],t_normal[1])*t_normal[1];\\n\"\n\"vec3 t2 = w - dot(w-t_vertex[2],t_normal[2])*t_normal[2];\\n\"\n\"w = w*(1.0-factor) + factor*(gl_TessCoord.x*t0+gl_TessCoord.y*t1+gl_TessCoord.z*t2);\\n\"\n\n\"#if defined(PCF) || defined(SPOT) || defined(CUBE) || defined(ORTHO)\\n\"\n//for texture projections/shadowmapping on dlights\n\"vtexprojcoord = (l_cubematrix*vec4(w.xyz, 1.0));\\n\"\n\"#endif\\n\"\n\n//FIXME: we should be recalcing these here, instead of just lerping them\n\"lightvector = LERP(t_lightvector);\\n\"\n\"#if defined(VERTEXCOLOURS)\\n\"\n\"vc = LERP(t_vc);\\n\"\n\"#endif\\n\"\n\"#if defined(SPECULAR) || defined(OFFSETMAPPING) || defined(REFLECTCUBEMASK) || defined(PBR)\\n\"\n\"eyevector = LERP(t_eyevector);\\n\"\n\"#endif\\n\"\n\n\"gl_Position = m_modelviewprojection * vec4(w,1.0);\\n\"\n\"}\\n\"\n\"#endif\\n\"\n\n\n\n\n\n\n\n\n\n\n\n\"#ifdef FRAGMENT_SHADER\\n\"\n\n\"#include \\\"sys/fog.h\\\"\\n\"\n\"#include \\\"sys/pcf.h\\\"\\n\"\n\"#ifdef OFFSETMAPPING\\n\"\n\"#include \\\"sys/offsetmapping.h\\\"\\n\"\n\"#endif\\n\"\n\n\"#include \\\"sys/pbr.h\\\"\\n\"\n\n\"void main ()\\n\"\n\"{\\n\"\n\"#ifdef ORTHO\\n\"\n\"float colorscale = 1.0;\\n\"\n\"#else\\n\"\n\"float colorscale = max(1.0 - (dot(lightvector, lightvector)/(l_lightradius*l_lightradius)), 0.0);\\n\"\n\"#endif\\n\"\n\"#ifdef PCF\\n\"\n/*filter the light by the shadowmap. logically a boolean, but we allow fractions for softer shadows*/\n\"colorscale *= ShadowmapFilter(s_shadowmap, vtexprojcoord);\\n\"\n\"#endif\\n\"\n\"#if defined(SPOT)\\n\"\n/*filter the colour by the spotlight. discard anything behind the light so we don't get a mirror image*/\n\"if (vtexprojcoord.w < 0.0) discard;\\n\"\n\"vec2 spot = ((vtexprojcoord.st)/vtexprojcoord.w);\\n\"\n\"colorscale*=1.0-(dot(spot,spot));\\n\"\n\"#endif\\n\"\n\n//read raw texture samples (offsetmapping munges the tex coords first)\n\"#ifdef OFFSETMAPPING\\n\"\n\"vec2 tcoffsetmap = offsetmap(s_normalmap, tcbase, eyevector);\\n\"\n\"#define tcbase tcoffsetmap\\n\"\n\"#endif\\n\"\n\"#if defined(FLAT)\\n\"\n\"vec4 bases = vec4(FLAT, FLAT, FLAT, 1.0);\\n\"\n\"#else\\n\"\n\"vec4 bases = texture2D(s_diffuse, tcbase);\\n\"\n\"#ifdef VERTEXCOLOURS\\n\"\n\"bases.rgb *= bases.a;\\n\"\n\"#endif\\n\"\n\"#endif\\n\"\n\"#ifdef UPPER\\n\"\n\"vec4 uc = texture2D(s_upper, tcbase);\\n\"\n\"bases.rgb += uc.rgb*e_uppercolour*uc.a;\\n\"\n\"#endif\\n\"\n\"#ifdef LOWER\\n\"\n\"vec4 lc = texture2D(s_lower, tcbase);\\n\"\n\"bases.rgb += lc.rgb*e_lowercolour*lc.a;\\n\"\n\"#endif\\n\"\n\"#if defined(BUMP) || defined(SPECULAR) || defined(REFLECTCUBEMASK) || defined(PBR)\\n\"\n\"vec3 bumps = normalize(vec3(texture2D(s_normalmap, tcbase)) - 0.5);\\n\"\n\"#elif defined(REFLECTCUBEMASK)\\n\"\n\"vec3 bumps = vec3(0.0,0.0,1.0);\\n\"\n\"#endif\\n\"\n\"#ifdef SPECULAR\\n\"\n\"vec4 specs = texture2D(s_specular, tcbase);\\n\"\n\"#endif\\n\"\n\n\"#define dielectricSpecular 0.04\\n\"\n\"#ifdef SPECULAR\\n\"\n\"#ifdef ORM //pbr-style occlusion+roughness+metalness\\n\"\n\"#define occlusion specs.r\\n\"\n\"#define roughness clamp(specs.g, 0.04, 1.0)\\n\"\n\"#define metalness specs.b\\n\"\n\"#define gloss 1.0 //sqrt(1.0-roughness)\\n\"\n\"#define ambientrgb (specrgb+col.rgb)\\n\"\n\"vec3 specrgb = mix(vec3(dielectricSpecular), bases.rgb, metalness);\\n\"\n\"bases.rgb = bases.rgb * (1.0 - dielectricSpecular) * (1.0-metalness);\\n\"\n\"#elif defined(SG) //pbr-style specular+glossiness\\n\"\n//occlusion needs to be baked in. :(\n\"#define roughness (1.0-specs.a)\\n\"\n\"#define gloss specs.a\\n\"\n\"#define specrgb specs.rgb\\n\"\n\"#define ambientrgb (specs.rgb+col.rgb)\\n\"\n\"#else   //blinn-phong\\n\"\n\"#define roughness (1.0-specs.a)\\n\"\n\"#define gloss specs.a\\n\"\n\"#define specrgb specs.rgb\\n\"\n\"#define ambientrgb col.rgb\\n\"\n\"#endif\\n\"\n\"#else\\n\"\n\"#define roughness 0.3\\n\"\n\"#define specrgb bases.rgb //vec3(dielectricSpecular)\\n\"\n\"#endif\\n\"\n\n\"#ifdef PBR\\n\"\n\"vec3 diff = DoPBR(bumps, normalize(eyevector), normalize(lightvector), roughness, bases.rgb, specrgb, l_lightcolourscale);\\n\"\n\"#else\\n\"\n\"vec3 diff;\\n\"\n\"#ifdef NOBUMP\\n\"\n//surface can only support ambient lighting, even for lights that try to avoid it.\n\"diff = bases.rgb * (l_lightcolourscale.x+l_lightcolourscale.y);\\n\"\n\"#else\\n\"\n\"vec3 nl = normalize(lightvector);\\n\"\n\"#ifdef BUMP\\n\"\n\"diff = bases.rgb * (l_lightcolourscale.x + l_lightcolourscale.y * max(dot(bumps, nl), 0.0));\\n\"\n\"#else\\n\"\n//we still do bumpmapping even without bumps to ensure colours are always sane. light.exe does it too.\n\"diff = bases.rgb * (l_lightcolourscale.x + l_lightcolourscale.y * max(dot(vec3(0.0, 0.0, 1.0), nl), 0.0));\\n\"\n\"#endif\\n\"\n\"#endif\\n\"\n\"#ifdef SPECULAR\\n\"\n\"vec3 halfdir = normalize(normalize(eyevector) + nl);\\n\"\n\"float spec = pow(max(dot(halfdir, bumps), 0.0), FTE_SPECULAR_EXPONENT * gloss)*float(SPECMUL);\\n\"\n\"diff += l_lightcolourscale.z * spec * specrgb;\\n\"\n\"#endif\\n\"\n\"#endif\\n\"\n\n\"#ifdef REFLECTCUBEMASK\\n\"\n\"vec3 rtc = reflect(-eyevector, bumps);\\n\"\n\"rtc = rtc.x*invsurface[0] + rtc.y*invsurface[1] + rtc.z*invsurface[2];\\n\"\n\"rtc = (m_model * vec4(rtc.xyz,0.0)).xyz;\\n\"\n\"diff += texture2D(s_reflectmask, tcbase).rgb * textureCube(s_reflectcube, rtc).rgb;\\n\"\n\"#endif\\n\"\n\n\"#ifdef CUBE\\n\"\n/*filter the colour by the cubemap projection*/\n\"diff *= textureCube(s_projectionmap, vtexprojcoord.xyz).rgb;\\n\"\n\"#endif\\n\"\n\n\"#if defined(PROJECTION)\\n\"\n/*2d projection, not used*/\n//\tdiff *= texture2d(s_projectionmap, shadowcoord);\n\"#endif\\n\"\n\"#if defined(occlusion) && !defined(NOOCCLUDE)\\n\"\n\"diff *= occlusion;\\n\"\n\"#endif\\n\"\n\"#if defined(VERTEXCOLOURS)\\n\"\n\"diff *= vc.rgb * vc.a;\\n\"\n\"#endif\\n\"\n\n\"diff *= colorscale*l_lightcolour;\\n\"\n\"gl_FragColor = vec4(fog3additive(diff), 1.0);\\n\"\n\"}\\n\"\n\"#endif\\n\"\n\n},\n#endif\n#ifdef VKQUAKE\n{QR_VULKAN, -1, \"rtlight\",\n\"\\xFF\\x53\\x50\\x56\\x01\\x00\\x00\\x00\\x7F\\x06\\x00\\x00\\x00\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\\x2C\\x00\\x00\\x00\\xB5\\x00\\x00\\x00\\xE4\\x00\\x00\\x00\"\n\"\\xCC\\x15\\x00\\x00\\xB0\\x16\\x00\\x00\\xEC\\x47\\x00\\x00\\x01\\x00\\x66\\x31\\x72\\x5F\\x67\\x6C\\x73\\x6C\\x5F\\x6F\\x66\\x66\\x73\\x65\\x74\\x6D\\x61\\x70\"\n\"\\x70\\x69\\x6E\\x67\\x00\\x00\\x00\\x00\\x00\\x01\\x01\\x66\\x31\\x67\\x6C\\x5F\\x73\\x70\\x65\\x63\\x75\\x6C\\x61\\x72\\x00\\x00\\x00\\x00\\x00\\x01\\x02\\x66\"\n\"\\x31\\x72\\x5F\\x67\\x6C\\x73\\x6C\\x5F\\x6F\\x66\\x66\\x73\\x65\\x74\\x6D\\x61\\x70\\x70\\x69\\x6E\\x67\\x5F\\x73\\x63\\x61\\x6C\\x65\\x00\\x3D\\x23\\xD7\\x0A\"\n\"\\x01\\x03\\x69\\x31\\x72\\x5F\\x67\\x6C\\x73\\x6C\\x5F\\x70\\x63\\x66\\x00\\x00\\x00\\x00\\x05\\x01\\x04\\x42\\x31\\x70\\x63\\x66\\x00\\x00\\x00\\x00\\x00\\x01\"\n\"\\x05\\x42\\x31\\x73\\x70\\x6F\\x74\\x00\\x00\\x00\\x00\\x00\\x01\\x06\\x42\\x31\\x63\\x75\\x62\\x65\\x00\\x00\\x00\\x00\\x00\\x01\\x07\\x62\\x31\\x72\\x5F\\x66\"\n\"\\x6F\\x67\\x5F\\x65\\x78\\x70\\x32\\x00\\x00\\x00\\x00\\x00\\x01\\x08\\x62\\x31\\x72\\x5F\\x66\\x6F\\x67\\x5F\\x6C\\x69\\x6E\\x65\\x61\\x72\\x00\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x03\\x02\\x23\\x07\\x00\\x00\\x01\\x00\\x0A\\x00\\x08\\x00\\xD4\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x11\\x00\\x02\\x00\\x01\\x00\\x00\\x00\"\n\"\\x0B\\x00\\x06\\x00\\x01\\x00\\x00\\x00\\x47\\x4C\\x53\\x4C\\x2E\\x73\\x74\\x64\\x2E\\x34\\x35\\x30\\x00\\x00\\x00\\x00\\x0E\\x00\\x03\\x00\\x00\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x0F\\x00\\x12\\x00\\x00\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x6D\\x61\\x69\\x6E\\x00\\x00\\x00\\x00\\x24\\x00\\x00\\x00\\x40\\x00\\x00\\x00\"\n\"\\x42\\x00\\x00\\x00\\x44\\x00\\x00\\x00\\x4C\\x00\\x00\\x00\\x5D\\x00\\x00\\x00\\x5F\\x00\\x00\\x00\\x79\\x00\\x00\\x00\\x9B\\x00\\x00\\x00\\xAD\\x00\\x00\\x00\"\n\"\\xBE\\x00\\x00\\x00\\xD2\\x00\\x00\\x00\\xD3\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x19\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\"\n\"\\x1B\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x1B\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x1B\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x1B\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x1B\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x1B\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x1B\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x10\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x1B\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xC0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x1B\\x00\\x00\\x00\"\n\"\\x04\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xCC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x1B\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xD0\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x1B\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xDC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x1B\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\xE0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x1B\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xEC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x1B\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xF0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x1B\\x00\\x00\\x00\\x0A\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\xFC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x1B\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x1B\\x00\\x00\\x00\"\n\"\\x0C\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x1B\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x4C\\x01\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x1B\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x50\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x1B\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x5C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x1B\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x60\\x01\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x1B\\x00\\x00\\x00\\x11\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x6C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x1B\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x70\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x1B\\x00\\x00\\x00\\x13\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x1B\\x00\\x00\\x00\"\n\"\\x14\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x90\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x1B\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x94\\x01\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x1B\\x00\\x00\\x00\\x16\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x98\\x01\\x00\\x00\\x47\\x00\\x03\\x00\\x1B\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x1D\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x1D\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x24\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x40\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x04\\x00\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x42\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x44\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x4A\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x03\\x00\\x4A\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x5D\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x5F\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x48\\x00\\x04\\x00\\x65\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x65\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x65\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x65\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x65\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x4C\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x65\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x50\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x65\\x00\\x00\\x00\\x04\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x5C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x65\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x60\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x65\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x6C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x65\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x70\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x65\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x65\\x00\\x00\\x00\"\n\"\\x09\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x88\\x00\\x00\\x00\\x47\\x00\\x03\\x00\\x65\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x67\\x00\\x00\\x00\"\n\"\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x67\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x79\\x00\\x00\\x00\"\n\"\\x1E\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x8A\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x01\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\x8D\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\x90\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x13\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x9B\\x00\\x00\\x00\"\n\"\\x1E\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\xAD\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\xB4\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x04\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\xB6\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x05\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\xB9\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x06\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\xBE\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\xCA\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x02\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\xCB\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x03\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\xCC\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x07\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\xCD\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x08\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\xCE\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\xCF\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\xD0\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\xD2\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\xD3\\x00\\x00\\x00\"\n\"\\x1E\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x13\\x00\\x02\\x00\\x02\\x00\\x00\\x00\\x21\\x00\\x03\\x00\\x03\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x16\\x00\\x03\\x00\"\n\"\\x06\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x17\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x21\\x00\\x03\\x00\\x08\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x17\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x0C\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x0B\\x00\\x00\\x00\\x21\\x00\\x07\\x00\\x0D\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\"\n\"\\x20\\x00\\x04\\x00\\x14\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x18\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x04\\x00\\x00\\x00\"\n\"\\x15\\x00\\x04\\x00\\x17\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x17\\x00\\x00\\x00\\x18\\x00\\x00\\x00\\x04\\x00\\x00\\x00\"\n\"\\x1C\\x00\\x04\\x00\\x19\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x18\\x00\\x00\\x00\\x17\\x00\\x04\\x00\\x1A\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x1E\\x00\\x19\\x00\\x1B\\x00\\x00\\x00\\x16\\x00\\x00\\x00\\x16\\x00\\x00\\x00\\x16\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x19\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x0B\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x1A\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x1C\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x1B\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x1C\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\x15\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\\x1F\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x20\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x16\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x23\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x0B\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x23\\x00\\x00\\x00\\x24\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x26\\x00\\x00\\x00\"\n\"\\x00\\x00\\x80\\x3F\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x2C\\x00\\x00\\x00\\x00\\x00\\x80\\xBF\\x2B\\x00\\x04\\x00\\x17\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x2E\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x17\\x00\\x00\\x00\\x33\\x00\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x17\\x00\\x00\\x00\\x36\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x3A\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x40\\x3B\\x00\\x04\\x00\\x23\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x23\\x00\\x00\\x00\\x42\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x23\\x00\\x00\\x00\\x44\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x1E\\x00\\x03\\x00\\x4A\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x20\\x00\\x04\\x00\\x4B\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x4A\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x4B\\x00\\x00\\x00\\x4C\\x00\\x00\\x00\\x03\\x00\\x00\\x00\"\n\"\\x20\\x00\\x04\\x00\\x5A\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x5C\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x1A\\x00\\x00\\x00\"\n\"\\x3B\\x00\\x04\\x00\\x5C\\x00\\x00\\x00\\x5D\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x5E\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x1A\\x00\\x00\\x00\"\n\"\\x3B\\x00\\x04\\x00\\x5E\\x00\\x00\\x00\\x5F\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\\x62\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x1E\\x00\\x0C\\x00\\x65\\x00\\x00\\x00\\x16\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x1A\\x00\\x00\\x00\\x1A\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x66\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x65\\x00\\x00\\x00\"\n\"\\x3B\\x00\\x04\\x00\\x66\\x00\\x00\\x00\\x67\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\\x68\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x20\\x00\\x04\\x00\\x69\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x14\\x00\\x02\\x00\\x74\\x00\\x00\\x00\\x29\\x00\\x03\\x00\\x74\\x00\\x00\\x00\"\n\"\\x75\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x78\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x78\\x00\\x00\\x00\\x79\\x00\\x00\\x00\"\n\"\\x03\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x17\\x00\\x00\\x00\\x7D\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x7E\\x00\\x00\\x00\\x03\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x32\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x8A\\x00\\x00\\x00\\x00\\x80\\x80\\x43\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x8B\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x32\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x8D\\x00\\x00\\x00\\x00\\x00\\x80\\x43\\x32\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\\x90\\x00\\x00\\x00\"\n\"\\x13\\x00\\x00\\x00\\x34\\x00\\x06\\x00\\x74\\x00\\x00\\x00\\x91\\x00\\x00\\x00\\xAB\\x00\\x00\\x00\\x90\\x00\\x00\\x00\\x1F\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\"\n\"\\x1E\\x00\\x00\\x00\\x96\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x78\\x00\\x00\\x00\\x9B\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x34\\x00\\x06\\x00\"\n\"\\x74\\x00\\x00\\x00\\xA8\\x00\\x00\\x00\\xAB\\x00\\x00\\x00\\x90\\x00\\x00\\x00\\x1F\\x00\\x00\\x00\\x18\\x00\\x04\\x00\\xAB\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\"\n\"\\x03\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\xAC\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\xAB\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\xAC\\x00\\x00\\x00\\xAD\\x00\\x00\\x00\"\n\"\\x03\\x00\\x00\\x00\\x32\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\\xB4\\x00\\x00\\x00\\x04\\x01\\x00\\x00\\x34\\x00\\x06\\x00\\x74\\x00\\x00\\x00\\xB5\\x00\\x00\\x00\"\n\"\\xAB\\x00\\x00\\x00\\xB4\\x00\\x00\\x00\\x1F\\x00\\x00\\x00\\x32\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\\xB6\\x00\\x00\\x00\\x05\\x01\\x00\\x00\\x34\\x00\\x06\\x00\"\n\"\\x74\\x00\\x00\\x00\\xB7\\x00\\x00\\x00\\xAB\\x00\\x00\\x00\\xB6\\x00\\x00\\x00\\x1F\\x00\\x00\\x00\\x34\\x00\\x06\\x00\\x74\\x00\\x00\\x00\\xB8\\x00\\x00\\x00\"\n\"\\xA6\\x00\\x00\\x00\\xB5\\x00\\x00\\x00\\xB7\\x00\\x00\\x00\\x32\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\\xB9\\x00\\x00\\x00\\x06\\x01\\x00\\x00\\x34\\x00\\x06\\x00\"\n\"\\x74\\x00\\x00\\x00\\xBA\\x00\\x00\\x00\\xAB\\x00\\x00\\x00\\xB9\\x00\\x00\\x00\\x1F\\x00\\x00\\x00\\x34\\x00\\x06\\x00\\x74\\x00\\x00\\x00\\xBB\\x00\\x00\\x00\"\n\"\\xA6\\x00\\x00\\x00\\xB8\\x00\\x00\\x00\\xBA\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x5A\\x00\\x00\\x00\\xBE\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x32\\x00\\x04\\x00\"\n\"\\x06\\x00\\x00\\x00\\xCA\\x00\\x00\\x00\\x00\\x00\\x81\\x43\\x32\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\\xCB\\x00\\x00\\x00\\x03\\x01\\x00\\x00\\x32\\x00\\x04\\x00\"\n\"\\x1E\\x00\\x00\\x00\\xCC\\x00\\x00\\x00\\x07\\x01\\x00\\x00\\x32\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\\xCD\\x00\\x00\\x00\\x08\\x01\\x00\\x00\\x32\\x00\\x04\\x00\"\n\"\\x1E\\x00\\x00\\x00\\xCE\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x32\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\\xCF\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x32\\x00\\x04\\x00\"\n\"\\x1E\\x00\\x00\\x00\\xD0\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\xD1\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\"\n\"\\xD1\\x00\\x00\\x00\\xD2\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x5E\\x00\\x00\\x00\\xD3\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x36\\x00\\x05\\x00\"\n\"\\x02\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x05\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x0C\\x00\\x00\\x00\"\n\"\\x4D\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x0C\\x00\\x00\\x00\\x4E\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x0C\\x00\\x00\\x00\"\n\"\\x4F\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x0C\\x00\\x00\\x00\\x50\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x0C\\x00\\x00\\x00\"\n\"\\x51\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x0C\\x00\\x00\\x00\\x52\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x0C\\x00\\x00\\x00\"\n\"\\x53\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x0C\\x00\\x00\\x00\\x54\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x0C\\x00\\x00\\x00\"\n\"\\x61\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x0C\\x00\\x00\\x00\\x95\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x39\\x00\\x08\\x00\\x07\\x00\\x00\\x00\"\n\"\\x55\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x51\\x00\\x00\\x00\\x52\\x00\\x00\\x00\\x53\\x00\\x00\\x00\\x54\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\"\n\"\\x56\\x00\\x00\\x00\\x51\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x4D\\x00\\x00\\x00\\x56\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\x57\\x00\\x00\\x00\"\n\"\\x52\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x4E\\x00\\x00\\x00\\x57\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\x58\\x00\\x00\\x00\\x53\\x00\\x00\\x00\"\n\"\\x3E\\x00\\x03\\x00\\x4F\\x00\\x00\\x00\\x58\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\x59\\x00\\x00\\x00\\x54\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\"\n\"\\x50\\x00\\x00\\x00\\x59\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x5A\\x00\\x00\\x00\\x5B\\x00\\x00\\x00\\x4C\\x00\\x00\\x00\\x1F\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\"\n\"\\x5B\\x00\\x00\\x00\\x55\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x1A\\x00\\x00\\x00\\x60\\x00\\x00\\x00\\x5F\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x5D\\x00\\x00\\x00\"\n\"\\x60\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x20\\x00\\x00\\x00\\x63\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x62\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x16\\x00\\x00\\x00\"\n\"\\x64\\x00\\x00\\x00\\x63\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x69\\x00\\x00\\x00\\x6A\\x00\\x00\\x00\\x67\\x00\\x00\\x00\\x68\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x0B\\x00\\x00\\x00\\x6B\\x00\\x00\\x00\\x6A\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x6C\\x00\\x00\\x00\\x6B\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x6D\\x00\\x00\\x00\\x6B\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x6E\\x00\\x00\\x00\"\n\"\\x6B\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x50\\x00\\x07\\x00\\x07\\x00\\x00\\x00\\x6F\\x00\\x00\\x00\\x6C\\x00\\x00\\x00\\x6D\\x00\\x00\\x00\\x6E\\x00\\x00\\x00\"\n\"\\x26\\x00\\x00\\x00\\x91\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x70\\x00\\x00\\x00\\x64\\x00\\x00\\x00\\x6F\\x00\\x00\\x00\\x4F\\x00\\x08\\x00\\x0B\\x00\\x00\\x00\"\n\"\\x71\\x00\\x00\\x00\\x70\\x00\\x00\\x00\\x70\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\"\n\"\\x72\\x00\\x00\\x00\\x4D\\x00\\x00\\x00\\x83\\x00\\x05\\x00\\x0B\\x00\\x00\\x00\\x73\\x00\\x00\\x00\\x71\\x00\\x00\\x00\\x72\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\"\n\"\\x61\\x00\\x00\\x00\\x73\\x00\\x00\\x00\\xF7\\x00\\x03\\x00\\x77\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\\x75\\x00\\x00\\x00\\x76\\x00\\x00\\x00\"\n\"\\x88\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x76\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\x7A\\x00\\x00\\x00\\x61\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x0B\\x00\\x00\\x00\\x7B\\x00\\x00\\x00\\x4F\\x00\\x00\\x00\\x94\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x7C\\x00\\x00\\x00\\x7A\\x00\\x00\\x00\\x7B\\x00\\x00\\x00\"\n\"\\x41\\x00\\x05\\x00\\x7E\\x00\\x00\\x00\\x7F\\x00\\x00\\x00\\x79\\x00\\x00\\x00\\x7D\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x7F\\x00\\x00\\x00\\x7C\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\x80\\x00\\x00\\x00\\x61\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\x81\\x00\\x00\\x00\\x50\\x00\\x00\\x00\"\n\"\\x94\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x82\\x00\\x00\\x00\\x80\\x00\\x00\\x00\\x81\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x7E\\x00\\x00\\x00\\x83\\x00\\x00\\x00\"\n\"\\x79\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x83\\x00\\x00\\x00\\x82\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\x84\\x00\\x00\\x00\"\n\"\\x61\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\x85\\x00\\x00\\x00\\x4E\\x00\\x00\\x00\\x94\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x86\\x00\\x00\\x00\"\n\"\\x84\\x00\\x00\\x00\\x85\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x7E\\x00\\x00\\x00\\x87\\x00\\x00\\x00\\x79\\x00\\x00\\x00\\x33\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\"\n\"\\x87\\x00\\x00\\x00\\x86\\x00\\x00\\x00\\xF9\\x00\\x02\\x00\\x77\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x88\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\"\n\"\\x89\\x00\\x00\\x00\\x61\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x79\\x00\\x00\\x00\\x89\\x00\\x00\\x00\\xF9\\x00\\x02\\x00\\x77\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\"\n\"\\x77\\x00\\x00\\x00\\xBA\\x00\\x05\\x00\\x74\\x00\\x00\\x00\\x8C\\x00\\x00\\x00\\x8A\\x00\\x00\\x00\\x8B\\x00\\x00\\x00\\xBA\\x00\\x05\\x00\\x74\\x00\\x00\\x00\"\n\"\\x8E\\x00\\x00\\x00\\x8D\\x00\\x00\\x00\\x8B\\x00\\x00\\x00\\xA6\\x00\\x05\\x00\\x74\\x00\\x00\\x00\\x8F\\x00\\x00\\x00\\x8C\\x00\\x00\\x00\\x8E\\x00\\x00\\x00\"\n\"\\xA6\\x00\\x05\\x00\\x74\\x00\\x00\\x00\\x92\\x00\\x00\\x00\\x8F\\x00\\x00\\x00\\x91\\x00\\x00\\x00\\xF7\\x00\\x03\\x00\\x94\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\xFA\\x00\\x04\\x00\\x92\\x00\\x00\\x00\\x93\\x00\\x00\\x00\\x94\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x93\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x69\\x00\\x00\\x00\"\n\"\\x97\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x96\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\x98\\x00\\x00\\x00\\x97\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x0B\\x00\\x00\\x00\\x99\\x00\\x00\\x00\\x4D\\x00\\x00\\x00\\x83\\x00\\x05\\x00\\x0B\\x00\\x00\\x00\\x9A\\x00\\x00\\x00\\x98\\x00\\x00\\x00\\x99\\x00\\x00\\x00\"\n\"\\x3E\\x00\\x03\\x00\\x95\\x00\\x00\\x00\\x9A\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\x9C\\x00\\x00\\x00\\x95\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x0B\\x00\\x00\\x00\\x9D\\x00\\x00\\x00\\x4F\\x00\\x00\\x00\\x94\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x9E\\x00\\x00\\x00\\x9C\\x00\\x00\\x00\\x9D\\x00\\x00\\x00\"\n\"\\x41\\x00\\x05\\x00\\x7E\\x00\\x00\\x00\\x9F\\x00\\x00\\x00\\x9B\\x00\\x00\\x00\\x7D\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x9F\\x00\\x00\\x00\\x9E\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\xA0\\x00\\x00\\x00\\x95\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\xA1\\x00\\x00\\x00\\x50\\x00\\x00\\x00\"\n\"\\x94\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\xA2\\x00\\x00\\x00\\xA0\\x00\\x00\\x00\\xA1\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x7E\\x00\\x00\\x00\\xA3\\x00\\x00\\x00\"\n\"\\x9B\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\xA3\\x00\\x00\\x00\\xA2\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\xA4\\x00\\x00\\x00\"\n\"\\x95\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\xA5\\x00\\x00\\x00\\x4E\\x00\\x00\\x00\\x94\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\xA6\\x00\\x00\\x00\"\n\"\\xA4\\x00\\x00\\x00\\xA5\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x7E\\x00\\x00\\x00\\xA7\\x00\\x00\\x00\\x9B\\x00\\x00\\x00\\x33\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\"\n\"\\xA7\\x00\\x00\\x00\\xA6\\x00\\x00\\x00\\xF9\\x00\\x02\\x00\\x94\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x94\\x00\\x00\\x00\\xF7\\x00\\x03\\x00\\xAA\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\\xA8\\x00\\x00\\x00\\xA9\\x00\\x00\\x00\\xAA\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\xA9\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x0B\\x00\\x00\\x00\\xAE\\x00\\x00\\x00\\x42\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x78\\x00\\x00\\x00\\xAF\\x00\\x00\\x00\\xAD\\x00\\x00\\x00\\x1F\\x00\\x00\\x00\"\n\"\\x3E\\x00\\x03\\x00\\xAF\\x00\\x00\\x00\\xAE\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\xB0\\x00\\x00\\x00\\x44\\x00\\x00\\x00\\x41\\x00\\x05\\x00\"\n\"\\x78\\x00\\x00\\x00\\xB1\\x00\\x00\\x00\\xAD\\x00\\x00\\x00\\x68\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\xB1\\x00\\x00\\x00\\xB0\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x0B\\x00\\x00\\x00\\xB2\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x78\\x00\\x00\\x00\\xB3\\x00\\x00\\x00\\xAD\\x00\\x00\\x00\\x62\\x00\\x00\\x00\"\n\"\\x3E\\x00\\x03\\x00\\xB3\\x00\\x00\\x00\\xB2\\x00\\x00\\x00\\xF9\\x00\\x02\\x00\\xAA\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\xAA\\x00\\x00\\x00\\xF7\\x00\\x03\\x00\"\n\"\\xBD\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\\xBB\\x00\\x00\\x00\\xBC\\x00\\x00\\x00\\xBD\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\xBC\\x00\\x00\\x00\"\n\"\\x41\\x00\\x05\\x00\\x20\\x00\\x00\\x00\\xBF\\x00\\x00\\x00\\x67\\x00\\x00\\x00\\x1F\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\xC0\\x00\\x00\\x00\"\n\"\\xBF\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x20\\x00\\x00\\x00\\xC1\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x68\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x16\\x00\\x00\\x00\"\n\"\\xC2\\x00\\x00\\x00\\xC1\\x00\\x00\\x00\\x92\\x00\\x05\\x00\\x16\\x00\\x00\\x00\\xC3\\x00\\x00\\x00\\xC0\\x00\\x00\\x00\\xC2\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x0B\\x00\\x00\\x00\\xC4\\x00\\x00\\x00\\x4D\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\xC5\\x00\\x00\\x00\\xC4\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\xC6\\x00\\x00\\x00\\xC4\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\xC7\\x00\\x00\\x00\"\n\"\\xC4\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x50\\x00\\x07\\x00\\x07\\x00\\x00\\x00\\xC8\\x00\\x00\\x00\\xC5\\x00\\x00\\x00\\xC6\\x00\\x00\\x00\\xC7\\x00\\x00\\x00\"\n\"\\x26\\x00\\x00\\x00\\x91\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\xC9\\x00\\x00\\x00\\xC3\\x00\\x00\\x00\\xC8\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\xBE\\x00\\x00\\x00\"\n\"\\xC9\\x00\\x00\\x00\\xF9\\x00\\x02\\x00\\xBD\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\xBD\\x00\\x00\\x00\\xFD\\x00\\x01\\x00\\x38\\x00\\x01\\x00\\x36\\x00\\x05\\x00\"\n\"\\x07\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x0A\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x14\\x00\\x00\\x00\"\n\"\\x15\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x20\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x1F\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x16\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\x25\\x00\\x00\\x00\\x24\\x00\\x00\\x00\\x51\\x00\\x05\\x00\"\n\"\\x06\\x00\\x00\\x00\\x27\\x00\\x00\\x00\\x25\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x28\\x00\\x00\\x00\\x25\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x29\\x00\\x00\\x00\\x25\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x50\\x00\\x07\\x00\\x07\\x00\\x00\\x00\"\n\"\\x2A\\x00\\x00\\x00\\x27\\x00\\x00\\x00\\x28\\x00\\x00\\x00\\x29\\x00\\x00\\x00\\x26\\x00\\x00\\x00\\x91\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x2B\\x00\\x00\\x00\"\n\"\\x22\\x00\\x00\\x00\\x2A\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x15\\x00\\x00\\x00\\x2B\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x2E\\x00\\x00\\x00\\x2F\\x00\\x00\\x00\"\n\"\\x15\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x30\\x00\\x00\\x00\\x2F\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\"\n\"\\x31\\x00\\x00\\x00\\x30\\x00\\x00\\x00\\x2C\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x2E\\x00\\x00\\x00\\x32\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\"\n\"\\x3E\\x00\\x03\\x00\\x32\\x00\\x00\\x00\\x31\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x2E\\x00\\x00\\x00\\x34\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x33\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x35\\x00\\x00\\x00\\x34\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x2E\\x00\\x00\\x00\\x37\\x00\\x00\\x00\\x15\\x00\\x00\\x00\"\n\"\\x36\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x38\\x00\\x00\\x00\\x37\\x00\\x00\\x00\\x81\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x39\\x00\\x00\\x00\"\n\"\\x35\\x00\\x00\\x00\\x38\\x00\\x00\\x00\\x88\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x3B\\x00\\x00\\x00\\x39\\x00\\x00\\x00\\x3A\\x00\\x00\\x00\\x41\\x00\\x05\\x00\"\n\"\\x2E\\x00\\x00\\x00\\x3C\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x33\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x3C\\x00\\x00\\x00\\x3B\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x07\\x00\\x00\\x00\\x3D\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\xFE\\x00\\x02\\x00\\x3D\\x00\\x00\\x00\\x38\\x00\\x01\\x00\\x36\\x00\\x05\\x00\\x07\\x00\\x00\\x00\"\n\"\\x12\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x37\\x00\\x03\\x00\\x0C\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x37\\x00\\x03\\x00\\x0C\\x00\\x00\\x00\"\n\"\\x0F\\x00\\x00\\x00\\x37\\x00\\x03\\x00\\x0C\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x37\\x00\\x03\\x00\\x0C\\x00\\x00\\x00\\x11\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\"\n\"\\x13\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\x41\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x0F\\x00\\x00\\x00\\x41\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\x43\\x00\\x00\\x00\\x42\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x10\\x00\\x00\\x00\\x43\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x0B\\x00\\x00\\x00\\x45\\x00\\x00\\x00\\x44\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x11\\x00\\x00\\x00\\x45\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\"\n\"\\x46\\x00\\x00\\x00\\x24\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x0E\\x00\\x00\\x00\\x46\\x00\\x00\\x00\\x39\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x47\\x00\\x00\\x00\"\n\"\\x09\\x00\\x00\\x00\\xFE\\x00\\x02\\x00\\x47\\x00\\x00\\x00\\x38\\x00\\x01\\x00\\x03\\x02\\x23\\x07\\x00\\x00\\x01\\x00\\x0A\\x00\\x08\\x00\\x37\\x03\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x11\\x00\\x02\\x00\\x01\\x00\\x00\\x00\\x0B\\x00\\x06\\x00\\x01\\x00\\x00\\x00\\x47\\x4C\\x53\\x4C\\x2E\\x73\\x74\\x64\\x2E\\x34\\x35\\x30\"\n\"\\x00\\x00\\x00\\x00\\x0E\\x00\\x03\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x0F\\x00\\x0C\\x00\\x04\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x6D\\x61\\x69\\x6E\"\n\"\\x00\\x00\\x00\\x00\\x30\\x00\\x00\\x00\\x7D\\x00\\x00\\x00\\x2D\\x02\\x00\\x00\\x2F\\x02\\x00\\x00\\x78\\x02\\x00\\x00\\xD3\\x02\\x00\\x00\\x29\\x03\\x00\\x00\"\n\"\\x10\\x00\\x03\\x00\\x04\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x1F\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x47\\x00\\x04\\x00\"\n\"\\x28\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x08\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\x30\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\\x47\\x00\\x04\\x00\"\n\"\\x3D\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x3E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x3E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x3E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x3E\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x3E\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x3E\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\"\n\"\\x3E\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x3E\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x3E\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x3E\\x00\\x00\\x00\\x03\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\xC0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x3E\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xCC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x3E\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xD0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x3E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\xDC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x3E\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xE0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x3E\\x00\\x00\\x00\"\n\"\\x08\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xEC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x3E\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xF0\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x3E\\x00\\x00\\x00\\x0A\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xFC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x3E\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x3E\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x01\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x3E\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x4C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x3E\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x50\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x3E\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x5C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x3E\\x00\\x00\\x00\"\n\"\\x10\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x60\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x3E\\x00\\x00\\x00\\x11\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x6C\\x01\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x3E\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x70\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x3E\\x00\\x00\\x00\\x13\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x80\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x3E\\x00\\x00\\x00\\x14\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x90\\x01\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x3E\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x94\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x3E\\x00\\x00\\x00\\x16\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x98\\x01\\x00\\x00\\x47\\x00\\x03\\x00\\x3E\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x40\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x40\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x5D\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x07\\x01\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x79\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x05\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\x7D\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x03\\x00\\x00\\x00\"\n\"\\x48\\x00\\x04\\x00\\xC2\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\xC2\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\xC2\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\xC2\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\xC2\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x4C\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\xC2\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x50\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\xC2\\x00\\x00\\x00\\x04\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x5C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\xC2\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x60\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\xC2\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x6C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\xC2\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x70\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\xC2\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\xC2\\x00\\x00\\x00\"\n\"\\x09\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x88\\x00\\x00\\x00\\x47\\x00\\x03\\x00\\xC2\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\xC4\\x00\\x00\\x00\"\n\"\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\xC4\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\xDB\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x03\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\xE3\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\xE3\\x00\\x00\\x00\"\n\"\\x21\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\xAC\\x01\\x00\\x00\\x01\\x00\\x00\\x00\\x02\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\xF7\\x01\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\x2B\\x02\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x2B\\x02\\x00\\x00\"\n\"\\x21\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x2D\\x02\\x00\\x00\\x1E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x2F\\x02\\x00\\x00\"\n\"\\x1E\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x38\\x02\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x38\\x02\\x00\\x00\"\n\"\\x21\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x40\\x02\\x00\\x00\\x01\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x46\\x02\\x00\\x00\"\n\"\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x46\\x02\\x00\\x00\\x21\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x57\\x02\\x00\\x00\"\n\"\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x57\\x02\\x00\\x00\\x21\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x66\\x02\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x78\\x02\\x00\\x00\\x1E\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\xA4\\x02\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x01\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\xA9\\x02\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\xA9\\x02\\x00\\x00\"\n\"\\x21\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\xC6\\x02\\x00\\x00\\x01\\x00\\x00\\x00\\x13\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\xD3\\x02\\x00\\x00\"\n\"\\x1E\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\xEF\\x02\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\xEF\\x02\\x00\\x00\"\n\"\\x21\\x00\\x00\\x00\\x0A\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\xF7\\x02\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\xF7\\x02\\x00\\x00\"\n\"\\x21\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\xFF\\x02\\x00\\x00\\x01\\x00\\x00\\x00\\x06\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\x03\\x03\\x00\\x00\"\n\"\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x03\\x03\\x00\\x00\\x21\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x21\\x03\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x04\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\x29\\x03\\x00\\x00\\x1E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x13\\x00\\x02\\x00\\x02\\x00\\x00\\x00\"\n\"\\x21\\x00\\x03\\x00\\x03\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x16\\x00\\x03\\x00\\x06\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x17\\x00\\x04\\x00\\x07\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x08\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x21\\x00\\x04\\x00\\x09\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\x21\\x00\\x03\\x00\\x0D\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x21\\x00\\x03\\x00\\x10\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x19\\x00\\x09\\x00\\x13\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x1B\\x00\\x03\\x00\\x14\\x00\\x00\\x00\\x13\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x15\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x14\\x00\\x00\\x00\"\n\"\\x17\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x17\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x16\\x00\\x00\\x00\"\n\"\\x21\\x00\\x06\\x00\\x18\\x00\\x00\\x00\\x16\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x17\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\x15\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\"\n\"\\x20\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x32\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\\x1F\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\"\n\"\\x20\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x14\\x00\\x02\\x00\\x21\\x00\\x00\\x00\\x34\\x00\\x06\\x00\\x21\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\xAB\\x00\\x00\\x00\"\n\"\\x1F\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x34\\x00\\x05\\x00\\x21\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xA8\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x32\\x00\\x04\\x00\"\n\"\\x1E\\x00\\x00\\x00\\x28\\x00\\x00\\x00\\x08\\x01\\x00\\x00\\x34\\x00\\x06\\x00\\x21\\x00\\x00\\x00\\x29\\x00\\x00\\x00\\xAB\\x00\\x00\\x00\\x28\\x00\\x00\\x00\"\n\"\\x20\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x2C\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x17\\x00\\x04\\x00\\x2E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x04\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x2F\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x2E\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x2F\\x00\\x00\\x00\\x30\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x15\\x00\\x04\\x00\\x31\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x31\\x00\\x00\\x00\\x32\\x00\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x33\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x31\\x00\\x00\\x00\\x36\\x00\\x00\\x00\"\n\"\\x03\\x00\\x00\\x00\\x18\\x00\\x04\\x00\\x3B\\x00\\x00\\x00\\x2E\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x31\\x00\\x00\\x00\\x3C\\x00\\x00\\x00\"\n\"\\x04\\x00\\x00\\x00\\x1C\\x00\\x04\\x00\\x3D\\x00\\x00\\x00\\x2E\\x00\\x00\\x00\\x3C\\x00\\x00\\x00\\x1E\\x00\\x19\\x00\\x3E\\x00\\x00\\x00\\x3B\\x00\\x00\\x00\"\n\"\\x3B\\x00\\x00\\x00\\x3B\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x3D\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x2E\\x00\\x00\\x00\\x2E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x16\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x3F\\x00\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\x3E\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x3F\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\"\n\"\\x41\\x00\\x00\\x00\\x14\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x42\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\"\n\"\\x49\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x57\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x32\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\"\n\"\\x5D\\x00\\x00\\x00\\x07\\x01\\x00\\x00\\x34\\x00\\x06\\x00\\x21\\x00\\x00\\x00\\x5E\\x00\\x00\\x00\\xAB\\x00\\x00\\x00\\x5D\\x00\\x00\\x00\\x20\\x00\\x00\\x00\"\n\"\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x65\\x00\\x00\\x00\\x3B\\xAA\\xB8\\x3F\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x69\\x00\\x00\\x00\\x00\\x00\\x80\\x3F\"\n\"\\x2B\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\\x6A\\x00\\x00\\x00\\x13\\x00\\x00\\x00\\x32\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\\x79\\x00\\x00\\x00\\x05\\x01\\x00\\x00\"\n\"\\x34\\x00\\x06\\x00\\x21\\x00\\x00\\x00\\x7A\\x00\\x00\\x00\\xAB\\x00\\x00\\x00\\x79\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x2F\\x00\\x00\\x00\"\n\"\\x7D\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x80\\x00\\x00\\x00\\x8F\\xC2\\x75\\x3C\\x2C\\x00\\x06\\x00\\x07\\x00\\x00\\x00\"\n\"\\x81\\x00\\x00\\x00\\x57\\x00\\x00\\x00\\x57\\x00\\x00\\x00\\x80\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x87\\x00\\x00\\x00\\x00\\x00\\x80\\xBF\"\n\"\\x2C\\x00\\x06\\x00\\x07\\x00\\x00\\x00\\x88\\x00\\x00\\x00\\x69\\x00\\x00\\x00\\x87\\x00\\x00\\x00\\x69\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\"\n\"\\x8A\\x00\\x00\\x00\\x00\\x00\\x00\\x3F\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x8B\\x00\\x00\\x00\\x00\\x00\\x00\\xBF\\x2C\\x00\\x06\\x00\\x07\\x00\\x00\\x00\"\n\"\\x8C\\x00\\x00\\x00\\x8A\\x00\\x00\\x00\\x8B\\x00\\x00\\x00\\x8A\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x9A\\x00\\x00\\x00\\xAB\\xAA\\x2A\\x3E\"\n\"\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x9B\\x00\\x00\\x00\\x00\\x00\\x80\\x3E\\x2C\\x00\\x06\\x00\\x07\\x00\\x00\\x00\\x9C\\x00\\x00\\x00\\x9A\\x00\\x00\\x00\"\n\"\\x9B\\x00\\x00\\x00\\x8A\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x31\\x00\\x00\\x00\\x9D\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x31\\x00\\x00\\x00\"\n\"\\xA9\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\xB4\\x00\\x00\\x00\\x55\\x55\\x55\\x3F\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\"\n\"\\xBB\\x00\\x00\\x00\\x00\\x00\\x40\\x3F\\x1E\\x00\\x0C\\x00\\xC2\\x00\\x00\\x00\\x3B\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x2E\\x00\\x00\\x00\\x16\\x00\\x00\\x00\\x16\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\xC3\\x00\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\xC2\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\xC3\\x00\\x00\\x00\\xC4\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\"\n\"\\xC5\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\xC6\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x2E\\x00\\x00\\x00\\x32\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\"\n\"\\xDB\\x00\\x00\\x00\\x03\\x01\\x00\\x00\\x2B\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\\xDC\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x34\\x00\\x06\\x00\\x21\\x00\\x00\\x00\"\n\"\\xDD\\x00\\x00\\x00\\xB1\\x00\\x00\\x00\\xDB\\x00\\x00\\x00\\xDC\\x00\\x00\\x00\\x19\\x00\\x09\\x00\\xE0\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x1B\\x00\\x03\\x00\\xE1\\x00\\x00\\x00\\xE0\\x00\\x00\\x00\"\n\"\\x20\\x00\\x04\\x00\\xE2\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xE1\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\xE2\\x00\\x00\\x00\\xE3\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x2C\\x00\\x06\\x00\\x07\\x00\\x00\\x00\\xE6\\x00\\x00\\x00\\x57\\x00\\x00\\x00\\x57\\x00\\x00\\x00\\x57\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\"\n\"\\xE7\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\xE8\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x16\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\"\n\"\\xF5\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x34\\x00\\x06\\x00\\x21\\x00\\x00\\x00\\xF6\\x00\\x00\\x00\\xB1\\x00\\x00\\x00\\xDB\\x00\\x00\\x00\\xF5\\x00\\x00\\x00\"\n\"\\x2C\\x00\\x06\\x00\\x07\\x00\\x00\\x00\\xFB\\x00\\x00\\x00\\x87\\x00\\x00\\x00\\x57\\x00\\x00\\x00\\x57\\x00\\x00\\x00\\x2C\\x00\\x06\\x00\\x07\\x00\\x00\\x00\"\n\"\\x07\\x01\\x00\\x00\\x57\\x00\\x00\\x00\\x87\\x00\\x00\\x00\\x57\\x00\\x00\\x00\\x2C\\x00\\x06\\x00\\x07\\x00\\x00\\x00\\x1E\\x01\\x00\\x00\\x57\\x00\\x00\\x00\"\n\"\\x69\\x00\\x00\\x00\\x57\\x00\\x00\\x00\\x2C\\x00\\x06\\x00\\x07\\x00\\x00\\x00\\x2A\\x01\\x00\\x00\\x69\\x00\\x00\\x00\\x57\\x00\\x00\\x00\\x57\\x00\\x00\\x00\"\n\"\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x35\\x01\\x00\\x00\\x00\\x00\\xA0\\x40\\x2C\\x00\\x06\\x00\\x07\\x00\\x00\\x00\\x3B\\x01\\x00\\x00\\x87\\x00\\x00\\x00\"\n\"\\x87\\x00\\x00\\x00\\x57\\x00\\x00\\x00\\x2C\\x00\\x06\\x00\\x07\\x00\\x00\\x00\\x52\\x01\\x00\\x00\\x87\\x00\\x00\\x00\\x69\\x00\\x00\\x00\\x57\\x00\\x00\\x00\"\n\"\\x2C\\x00\\x06\\x00\\x07\\x00\\x00\\x00\\x7F\\x01\\x00\\x00\\x69\\x00\\x00\\x00\\x87\\x00\\x00\\x00\\x57\\x00\\x00\\x00\\x2C\\x00\\x06\\x00\\x07\\x00\\x00\\x00\"\n\"\\x96\\x01\\x00\\x00\\x69\\x00\\x00\\x00\\x69\\x00\\x00\\x00\\x57\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\xA1\\x01\\x00\\x00\\x00\\x00\\x10\\x41\"\n\"\\x2A\\x00\\x03\\x00\\x21\\x00\\x00\\x00\\xA5\\x01\\x00\\x00\\x32\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\xAC\\x01\\x00\\x00\\x00\\x00\\x81\\x43\\x2C\\x00\\x05\\x00\"\n\"\\x16\\x00\\x00\\x00\\xAF\\x01\\x00\\x00\\x87\\x00\\x00\\x00\\x69\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\xBC\\x01\\x00\\x00\\x00\\x00\\x20\\x41\"\n\"\\x32\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\xF7\\x01\\x00\\x00\\x00\\x00\\x80\\x43\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x07\\x02\\x00\\x00\\xFA\\x7E\\xAA\\x3E\"\n\"\\x3B\\x00\\x04\\x00\\x15\\x00\\x00\\x00\\x2B\\x02\\x00\\x00\\x00\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x2C\\x02\\x00\\x00\\x01\\x00\\x00\\x00\\x16\\x00\\x00\\x00\"\n\"\\x3B\\x00\\x04\\x00\\x2C\\x02\\x00\\x00\\x2D\\x02\\x00\\x00\\x01\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x2E\\x02\\x00\\x00\\x01\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x3B\\x00\\x04\\x00\\x2E\\x02\\x00\\x00\\x2F\\x02\\x00\\x00\\x01\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x15\\x00\\x00\\x00\\x38\\x02\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x32\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\\x40\\x02\\x00\\x00\\x12\\x00\\x00\\x00\\x34\\x00\\x06\\x00\\x21\\x00\\x00\\x00\\x41\\x02\\x00\\x00\\xAB\\x00\\x00\\x00\"\n\"\\x40\\x02\\x00\\x00\\x20\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x44\\x02\\x00\\x00\\x07\\x00\\x00\\x00\\x2E\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x15\\x00\\x00\\x00\"\n\"\\x46\\x02\\x00\\x00\\x00\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\\x4C\\x02\\x00\\x00\\x0C\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x4D\\x02\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x15\\x00\\x00\\x00\\x57\\x02\\x00\\x00\\x00\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\"\n\"\\x5D\\x02\\x00\\x00\\x0E\\x00\\x00\\x00\\x32\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\\x66\\x02\\x00\\x00\\x10\\x00\\x00\\x00\\x34\\x00\\x06\\x00\\x21\\x00\\x00\\x00\"\n\"\\x67\\x02\\x00\\x00\\xAB\\x00\\x00\\x00\\x66\\x02\\x00\\x00\\x20\\x00\\x00\\x00\\x2C\\x00\\x06\\x00\\x07\\x00\\x00\\x00\\x76\\x02\\x00\\x00\\x57\\x00\\x00\\x00\"\n\"\\x57\\x00\\x00\\x00\\x69\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x2E\\x02\\x00\\x00\\x78\\x02\\x00\\x00\\x01\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\"\n\"\\x7C\\x02\\x00\\x00\\x06\\x00\\x00\\x00\\x34\\x00\\x06\\x00\\x21\\x00\\x00\\x00\\x88\\x02\\x00\\x00\\xAB\\x00\\x00\\x00\\x66\\x02\\x00\\x00\\x20\\x00\\x00\\x00\"\n\"\\x32\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\xA4\\x02\\x00\\x00\\x00\\x80\\x80\\x43\\x3B\\x00\\x04\\x00\\x15\\x00\\x00\\x00\\xA9\\x02\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\xB8\\x02\\x00\\x00\\x00\\x00\\x00\\x42\\x32\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\\xC6\\x02\\x00\\x00\\x13\\x00\\x00\\x00\"\n\"\\x34\\x00\\x06\\x00\\x21\\x00\\x00\\x00\\xC7\\x02\\x00\\x00\\xAB\\x00\\x00\\x00\\xC6\\x02\\x00\\x00\\x20\\x00\\x00\\x00\\x18\\x00\\x04\\x00\\xD1\\x02\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\xD2\\x02\\x00\\x00\\x01\\x00\\x00\\x00\\xD1\\x02\\x00\\x00\\x3B\\x00\\x04\\x00\\xD2\\x02\\x00\\x00\"\n\"\\xD3\\x02\\x00\\x00\\x01\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\\xD9\\x02\\x00\\x00\\x01\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\"\n\"\\xE0\\x02\\x00\\x00\\x02\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\xE5\\x02\\x00\\x00\\x02\\x00\\x00\\x00\\x3B\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x15\\x00\\x00\\x00\"\n\"\\xEF\\x02\\x00\\x00\\x00\\x00\\x00\\x00\\x19\\x00\\x09\\x00\\xF4\\x02\\x00\\x00\\x06\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x1B\\x00\\x03\\x00\\xF5\\x02\\x00\\x00\\xF4\\x02\\x00\\x00\\x20\\x00\\x04\\x00\\xF6\\x02\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\xF5\\x02\\x00\\x00\\x3B\\x00\\x04\\x00\\xF6\\x02\\x00\\x00\\xF7\\x02\\x00\\x00\\x00\\x00\\x00\\x00\\x32\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\"\n\"\\xFF\\x02\\x00\\x00\\x06\\x01\\x00\\x00\\x34\\x00\\x06\\x00\\x21\\x00\\x00\\x00\\x00\\x03\\x00\\x00\\xAB\\x00\\x00\\x00\\xFF\\x02\\x00\\x00\\x20\\x00\\x00\\x00\"\n\"\\x3B\\x00\\x04\\x00\\xF6\\x02\\x00\\x00\\x03\\x03\\x00\\x00\\x00\\x00\\x00\\x00\\x34\\x00\\x06\\x00\\x21\\x00\\x00\\x00\\x0B\\x03\\x00\\x00\\xAB\\x00\\x00\\x00\"\n\"\\x79\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x32\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\\x21\\x03\\x00\\x00\\x04\\x01\\x00\\x00\\x34\\x00\\x06\\x00\\x21\\x00\\x00\\x00\"\n\"\\x22\\x03\\x00\\x00\\xAB\\x00\\x00\\x00\\x21\\x03\\x00\\x00\\x20\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x28\\x03\\x00\\x00\\x03\\x00\\x00\\x00\\x2E\\x00\\x00\\x00\"\n\"\\x3B\\x00\\x04\\x00\\x28\\x03\\x00\\x00\\x29\\x03\\x00\\x00\\x03\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\\x2D\\x03\\x00\\x00\\x03\\x00\\x00\\x00\"\n\"\\x20\\x00\\x04\\x00\\x35\\x03\\x00\\x00\\x03\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x36\\x00\\x05\\x00\\x02\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x03\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x05\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x17\\x00\\x00\\x00\\x2A\\x02\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\"\n\"\\x17\\x00\\x00\\x00\\x30\\x02\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x08\\x00\\x00\\x00\\x32\\x02\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\"\n\"\\x08\\x00\\x00\\x00\\x37\\x02\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x44\\x02\\x00\\x00\\x45\\x02\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\"\n\"\\x44\\x02\\x00\\x00\\x56\\x02\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x08\\x00\\x00\\x00\\x6A\\x02\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\"\n\"\\x2C\\x00\\x00\\x00\\x77\\x02\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x08\\x00\\x00\\x00\\x85\\x02\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\"\n\"\\x08\\x00\\x00\\x00\\x8B\\x02\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x44\\x02\\x00\\x00\\xA8\\x02\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\"\n\"\\x08\\x00\\x00\\x00\\xAD\\x02\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x2C\\x00\\x00\\x00\\xB3\\x02\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\"\n\"\\x08\\x00\\x00\\x00\\xCA\\x02\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x17\\x00\\x00\\x00\\x14\\x03\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\"\n\"\\x08\\x00\\x00\\x00\\x31\\x03\\x00\\x00\\x07\\x00\\x00\\x00\\xBA\\x00\\x05\\x00\\x21\\x00\\x00\\x00\\x27\\x02\\x00\\x00\\xF7\\x01\\x00\\x00\\x57\\x00\\x00\\x00\"\n\"\\xF7\\x00\\x03\\x00\\x29\\x02\\x00\\x00\\x00\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\\x27\\x02\\x00\\x00\\x28\\x02\\x00\\x00\\x35\\x02\\x00\\x00\\xF8\\x00\\x02\\x00\"\n\"\\x28\\x02\\x00\\x00\\x3D\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x31\\x02\\x00\\x00\\x2D\\x02\\x00\\x00\\x3E\\x00\\x03\\x00\\x30\\x02\\x00\\x00\\x31\\x02\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x33\\x02\\x00\\x00\\x2F\\x02\\x00\\x00\\x3E\\x00\\x03\\x00\\x32\\x02\\x00\\x00\\x33\\x02\\x00\\x00\\x39\\x00\\x07\\x00\"\n\"\\x16\\x00\\x00\\x00\\x34\\x02\\x00\\x00\\x1C\\x00\\x00\\x00\\x2B\\x02\\x00\\x00\\x30\\x02\\x00\\x00\\x32\\x02\\x00\\x00\\x3E\\x00\\x03\\x00\\x2A\\x02\\x00\\x00\"\n\"\\x34\\x02\\x00\\x00\\xF9\\x00\\x02\\x00\\x29\\x02\\x00\\x00\\xF8\\x00\\x02\\x00\\x35\\x02\\x00\\x00\\x3D\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x36\\x02\\x00\\x00\"\n\"\\x2D\\x02\\x00\\x00\\x3E\\x00\\x03\\x00\\x2A\\x02\\x00\\x00\\x36\\x02\\x00\\x00\\xF9\\x00\\x02\\x00\\x29\\x02\\x00\\x00\\xF8\\x00\\x02\\x00\\x29\\x02\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x14\\x00\\x00\\x00\\x39\\x02\\x00\\x00\\x38\\x02\\x00\\x00\\x3D\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x3A\\x02\\x00\\x00\\x2A\\x02\\x00\\x00\"\n\"\\x57\\x00\\x05\\x00\\x2E\\x00\\x00\\x00\\x3B\\x02\\x00\\x00\\x39\\x02\\x00\\x00\\x3A\\x02\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x3C\\x02\\x00\\x00\"\n\"\\x3B\\x02\\x00\\x00\\x00\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x3D\\x02\\x00\\x00\\x3B\\x02\\x00\\x00\\x01\\x00\\x00\\x00\\x51\\x00\\x05\\x00\"\n\"\\x06\\x00\\x00\\x00\\x3E\\x02\\x00\\x00\\x3B\\x02\\x00\\x00\\x02\\x00\\x00\\x00\\x50\\x00\\x06\\x00\\x07\\x00\\x00\\x00\\x3F\\x02\\x00\\x00\\x3C\\x02\\x00\\x00\"\n\"\\x3D\\x02\\x00\\x00\\x3E\\x02\\x00\\x00\\x3E\\x00\\x03\\x00\\x37\\x02\\x00\\x00\\x3F\\x02\\x00\\x00\\xF7\\x00\\x03\\x00\\x43\\x02\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\xFA\\x00\\x04\\x00\\x41\\x02\\x00\\x00\\x42\\x02\\x00\\x00\\x43\\x02\\x00\\x00\\xF8\\x00\\x02\\x00\\x42\\x02\\x00\\x00\\x3D\\x00\\x04\\x00\\x14\\x00\\x00\\x00\"\n\"\\x47\\x02\\x00\\x00\\x46\\x02\\x00\\x00\\x3D\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x48\\x02\\x00\\x00\\x2A\\x02\\x00\\x00\\x57\\x00\\x05\\x00\\x2E\\x00\\x00\\x00\"\n\"\\x49\\x02\\x00\\x00\\x47\\x02\\x00\\x00\\x48\\x02\\x00\\x00\\x3E\\x00\\x03\\x00\\x45\\x02\\x00\\x00\\x49\\x02\\x00\\x00\\x3D\\x00\\x04\\x00\\x2E\\x00\\x00\\x00\"\n\"\\x4A\\x02\\x00\\x00\\x45\\x02\\x00\\x00\\x4F\\x00\\x08\\x00\\x07\\x00\\x00\\x00\\x4B\\x02\\x00\\x00\\x4A\\x02\\x00\\x00\\x4A\\x02\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x4D\\x02\\x00\\x00\\x4E\\x02\\x00\\x00\\x40\\x00\\x00\\x00\\x4C\\x02\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x07\\x00\\x00\\x00\\x4F\\x02\\x00\\x00\\x4E\\x02\\x00\\x00\\x85\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x50\\x02\\x00\\x00\\x4B\\x02\\x00\\x00\\x4F\\x02\\x00\\x00\"\n\"\\x41\\x00\\x05\\x00\\x2C\\x00\\x00\\x00\\x51\\x02\\x00\\x00\\x45\\x02\\x00\\x00\\x36\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x52\\x02\\x00\\x00\"\n\"\\x51\\x02\\x00\\x00\\x8E\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x53\\x02\\x00\\x00\\x50\\x02\\x00\\x00\\x52\\x02\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\"\n\"\\x54\\x02\\x00\\x00\\x37\\x02\\x00\\x00\\x81\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x55\\x02\\x00\\x00\\x54\\x02\\x00\\x00\\x53\\x02\\x00\\x00\\x3E\\x00\\x03\\x00\"\n\"\\x37\\x02\\x00\\x00\\x55\\x02\\x00\\x00\\x3D\\x00\\x04\\x00\\x14\\x00\\x00\\x00\\x58\\x02\\x00\\x00\\x57\\x02\\x00\\x00\\x3D\\x00\\x04\\x00\\x16\\x00\\x00\\x00\"\n\"\\x59\\x02\\x00\\x00\\x2A\\x02\\x00\\x00\\x57\\x00\\x05\\x00\\x2E\\x00\\x00\\x00\\x5A\\x02\\x00\\x00\\x58\\x02\\x00\\x00\\x59\\x02\\x00\\x00\\x3E\\x00\\x03\\x00\"\n\"\\x56\\x02\\x00\\x00\\x5A\\x02\\x00\\x00\\x3D\\x00\\x04\\x00\\x2E\\x00\\x00\\x00\\x5B\\x02\\x00\\x00\\x56\\x02\\x00\\x00\\x4F\\x00\\x08\\x00\\x07\\x00\\x00\\x00\"\n\"\\x5C\\x02\\x00\\x00\\x5B\\x02\\x00\\x00\\x5B\\x02\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x4D\\x02\\x00\\x00\"\n\"\\x5E\\x02\\x00\\x00\\x40\\x00\\x00\\x00\\x5D\\x02\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x5F\\x02\\x00\\x00\\x5E\\x02\\x00\\x00\\x85\\x00\\x05\\x00\"\n\"\\x07\\x00\\x00\\x00\\x60\\x02\\x00\\x00\\x5C\\x02\\x00\\x00\\x5F\\x02\\x00\\x00\\x41\\x00\\x05\\x00\\x2C\\x00\\x00\\x00\\x61\\x02\\x00\\x00\\x56\\x02\\x00\\x00\"\n\"\\x36\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x62\\x02\\x00\\x00\\x61\\x02\\x00\\x00\\x8E\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x63\\x02\\x00\\x00\"\n\"\\x60\\x02\\x00\\x00\\x62\\x02\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x64\\x02\\x00\\x00\\x37\\x02\\x00\\x00\\x81\\x00\\x05\\x00\\x07\\x00\\x00\\x00\"\n\"\\x65\\x02\\x00\\x00\\x64\\x02\\x00\\x00\\x63\\x02\\x00\\x00\\x3E\\x00\\x03\\x00\\x37\\x02\\x00\\x00\\x65\\x02\\x00\\x00\\xF9\\x00\\x02\\x00\\x43\\x02\\x00\\x00\"\n\"\\xF8\\x00\\x02\\x00\\x43\\x02\\x00\\x00\\xF7\\x00\\x03\\x00\\x69\\x02\\x00\\x00\\x00\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\\x67\\x02\\x00\\x00\\x68\\x02\\x00\\x00\"\n\"\\x75\\x02\\x00\\x00\\xF8\\x00\\x02\\x00\\x68\\x02\\x00\\x00\\x3D\\x00\\x04\\x00\\x14\\x00\\x00\\x00\\x6B\\x02\\x00\\x00\\x2B\\x02\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x16\\x00\\x00\\x00\\x6C\\x02\\x00\\x00\\x2A\\x02\\x00\\x00\\x57\\x00\\x05\\x00\\x2E\\x00\\x00\\x00\\x6D\\x02\\x00\\x00\\x6B\\x02\\x00\\x00\\x6C\\x02\\x00\\x00\"\n\"\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x6E\\x02\\x00\\x00\\x6D\\x02\\x00\\x00\\x00\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x6F\\x02\\x00\\x00\"\n\"\\x6D\\x02\\x00\\x00\\x01\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x70\\x02\\x00\\x00\\x6D\\x02\\x00\\x00\\x02\\x00\\x00\\x00\\x50\\x00\\x06\\x00\"\n\"\\x07\\x00\\x00\\x00\\x71\\x02\\x00\\x00\\x6E\\x02\\x00\\x00\\x6F\\x02\\x00\\x00\\x70\\x02\\x00\\x00\\x50\\x00\\x06\\x00\\x07\\x00\\x00\\x00\\x72\\x02\\x00\\x00\"\n\"\\x8A\\x00\\x00\\x00\\x8A\\x00\\x00\\x00\\x8A\\x00\\x00\\x00\\x83\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x73\\x02\\x00\\x00\\x71\\x02\\x00\\x00\\x72\\x02\\x00\\x00\"\n\"\\x0C\\x00\\x06\\x00\\x07\\x00\\x00\\x00\\x74\\x02\\x00\\x00\\x01\\x00\\x00\\x00\\x45\\x00\\x00\\x00\\x73\\x02\\x00\\x00\\x3E\\x00\\x03\\x00\\x6A\\x02\\x00\\x00\"\n\"\\x74\\x02\\x00\\x00\\xF9\\x00\\x02\\x00\\x69\\x02\\x00\\x00\\xF8\\x00\\x02\\x00\\x75\\x02\\x00\\x00\\x3E\\x00\\x03\\x00\\x6A\\x02\\x00\\x00\\x76\\x02\\x00\\x00\"\n\"\\xF9\\x00\\x02\\x00\\x69\\x02\\x00\\x00\\xF8\\x00\\x02\\x00\\x69\\x02\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x79\\x02\\x00\\x00\\x78\\x02\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x7A\\x02\\x00\\x00\\x78\\x02\\x00\\x00\\x94\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x7B\\x02\\x00\\x00\\x79\\x02\\x00\\x00\"\n\"\\x7A\\x02\\x00\\x00\\x41\\x00\\x05\\x00\\x42\\x00\\x00\\x00\\x7D\\x02\\x00\\x00\\xC4\\x00\\x00\\x00\\x7C\\x02\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\"\n\"\\x7E\\x02\\x00\\x00\\x7D\\x02\\x00\\x00\\x41\\x00\\x05\\x00\\x42\\x00\\x00\\x00\\x7F\\x02\\x00\\x00\\xC4\\x00\\x00\\x00\\x7C\\x02\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x06\\x00\\x00\\x00\\x80\\x02\\x00\\x00\\x7F\\x02\\x00\\x00\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x81\\x02\\x00\\x00\\x7E\\x02\\x00\\x00\\x80\\x02\\x00\\x00\"\n\"\\x88\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x82\\x02\\x00\\x00\\x7B\\x02\\x00\\x00\\x81\\x02\\x00\\x00\\x83\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x83\\x02\\x00\\x00\"\n\"\\x69\\x00\\x00\\x00\\x82\\x02\\x00\\x00\\x0C\\x00\\x07\\x00\\x06\\x00\\x00\\x00\\x84\\x02\\x00\\x00\\x01\\x00\\x00\\x00\\x28\\x00\\x00\\x00\\x83\\x02\\x00\\x00\"\n\"\\x57\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x77\\x02\\x00\\x00\\x84\\x02\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x86\\x02\\x00\\x00\\x78\\x02\\x00\\x00\"\n\"\\x0C\\x00\\x06\\x00\\x07\\x00\\x00\\x00\\x87\\x02\\x00\\x00\\x01\\x00\\x00\\x00\\x45\\x00\\x00\\x00\\x86\\x02\\x00\\x00\\x3E\\x00\\x03\\x00\\x85\\x02\\x00\\x00\"\n\"\\x87\\x02\\x00\\x00\\xF7\\x00\\x03\\x00\\x8A\\x02\\x00\\x00\\x00\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\\x88\\x02\\x00\\x00\\x89\\x02\\x00\\x00\\x98\\x02\\x00\\x00\"\n\"\\xF8\\x00\\x02\\x00\\x89\\x02\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x8C\\x02\\x00\\x00\\x37\\x02\\x00\\x00\\x41\\x00\\x06\\x00\\x42\\x00\\x00\\x00\"\n\"\\x8D\\x02\\x00\\x00\\xC4\\x00\\x00\\x00\\xDC\\x00\\x00\\x00\\x9D\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x8E\\x02\\x00\\x00\\x8D\\x02\\x00\\x00\"\n\"\\x41\\x00\\x06\\x00\\x42\\x00\\x00\\x00\\x8F\\x02\\x00\\x00\\xC4\\x00\\x00\\x00\\xDC\\x00\\x00\\x00\\xA9\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\"\n\"\\x90\\x02\\x00\\x00\\x8F\\x02\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x91\\x02\\x00\\x00\\x6A\\x02\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\"\n\"\\x92\\x02\\x00\\x00\\x85\\x02\\x00\\x00\\x94\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x93\\x02\\x00\\x00\\x91\\x02\\x00\\x00\\x92\\x02\\x00\\x00\\x0C\\x00\\x07\\x00\"\n\"\\x06\\x00\\x00\\x00\\x94\\x02\\x00\\x00\\x01\\x00\\x00\\x00\\x28\\x00\\x00\\x00\\x93\\x02\\x00\\x00\\x57\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\"\n\"\\x95\\x02\\x00\\x00\\x90\\x02\\x00\\x00\\x94\\x02\\x00\\x00\\x81\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x96\\x02\\x00\\x00\\x8E\\x02\\x00\\x00\\x95\\x02\\x00\\x00\"\n\"\\x8E\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x97\\x02\\x00\\x00\\x8C\\x02\\x00\\x00\\x96\\x02\\x00\\x00\\x3E\\x00\\x03\\x00\\x8B\\x02\\x00\\x00\\x97\\x02\\x00\\x00\"\n\"\\xF9\\x00\\x02\\x00\\x8A\\x02\\x00\\x00\\xF8\\x00\\x02\\x00\\x98\\x02\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x99\\x02\\x00\\x00\\x37\\x02\\x00\\x00\"\n\"\\x41\\x00\\x06\\x00\\x42\\x00\\x00\\x00\\x9A\\x02\\x00\\x00\\xC4\\x00\\x00\\x00\\xDC\\x00\\x00\\x00\\x9D\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\"\n\"\\x9B\\x02\\x00\\x00\\x9A\\x02\\x00\\x00\\x41\\x00\\x06\\x00\\x42\\x00\\x00\\x00\\x9C\\x02\\x00\\x00\\xC4\\x00\\x00\\x00\\xDC\\x00\\x00\\x00\\xA9\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x9D\\x02\\x00\\x00\\x9C\\x02\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x9E\\x02\\x00\\x00\\x85\\x02\\x00\\x00\"\n\"\\x94\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x9F\\x02\\x00\\x00\\x76\\x02\\x00\\x00\\x9E\\x02\\x00\\x00\\x0C\\x00\\x07\\x00\\x06\\x00\\x00\\x00\\xA0\\x02\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x28\\x00\\x00\\x00\\x9F\\x02\\x00\\x00\\x57\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\xA1\\x02\\x00\\x00\\x9D\\x02\\x00\\x00\"\n\"\\xA0\\x02\\x00\\x00\\x81\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\xA2\\x02\\x00\\x00\\x9B\\x02\\x00\\x00\\xA1\\x02\\x00\\x00\\x8E\\x00\\x05\\x00\\x07\\x00\\x00\\x00\"\n\"\\xA3\\x02\\x00\\x00\\x99\\x02\\x00\\x00\\xA2\\x02\\x00\\x00\\x3E\\x00\\x03\\x00\\x8B\\x02\\x00\\x00\\xA3\\x02\\x00\\x00\\xF9\\x00\\x02\\x00\\x8A\\x02\\x00\\x00\"\n\"\\xF8\\x00\\x02\\x00\\x8A\\x02\\x00\\x00\\xBA\\x00\\x05\\x00\\x21\\x00\\x00\\x00\\xA5\\x02\\x00\\x00\\xA4\\x02\\x00\\x00\\x57\\x00\\x00\\x00\\xF7\\x00\\x03\\x00\"\n\"\\xA7\\x02\\x00\\x00\\x00\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\\xA5\\x02\\x00\\x00\\xA6\\x02\\x00\\x00\\xA7\\x02\\x00\\x00\\xF8\\x00\\x02\\x00\\xA6\\x02\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x14\\x00\\x00\\x00\\xAA\\x02\\x00\\x00\\xA9\\x02\\x00\\x00\\x3D\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\xAB\\x02\\x00\\x00\\x2A\\x02\\x00\\x00\"\n\"\\x57\\x00\\x05\\x00\\x2E\\x00\\x00\\x00\\xAC\\x02\\x00\\x00\\xAA\\x02\\x00\\x00\\xAB\\x02\\x00\\x00\\x3E\\x00\\x03\\x00\\xA8\\x02\\x00\\x00\\xAC\\x02\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\xAE\\x02\\x00\\x00\\x2F\\x02\\x00\\x00\\x0C\\x00\\x06\\x00\\x07\\x00\\x00\\x00\\xAF\\x02\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x45\\x00\\x00\\x00\\xAE\\x02\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\xB0\\x02\\x00\\x00\\x85\\x02\\x00\\x00\\x81\\x00\\x05\\x00\\x07\\x00\\x00\\x00\"\n\"\\xB1\\x02\\x00\\x00\\xAF\\x02\\x00\\x00\\xB0\\x02\\x00\\x00\\x0C\\x00\\x06\\x00\\x07\\x00\\x00\\x00\\xB2\\x02\\x00\\x00\\x01\\x00\\x00\\x00\\x45\\x00\\x00\\x00\"\n\"\\xB1\\x02\\x00\\x00\\x3E\\x00\\x03\\x00\\xAD\\x02\\x00\\x00\\xB2\\x02\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\xB4\\x02\\x00\\x00\\xAD\\x02\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\xB5\\x02\\x00\\x00\\x6A\\x02\\x00\\x00\\x94\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\xB6\\x02\\x00\\x00\\xB4\\x02\\x00\\x00\"\n\"\\xB5\\x02\\x00\\x00\\x0C\\x00\\x07\\x00\\x06\\x00\\x00\\x00\\xB7\\x02\\x00\\x00\\x01\\x00\\x00\\x00\\x28\\x00\\x00\\x00\\xB6\\x02\\x00\\x00\\x57\\x00\\x00\\x00\"\n\"\\x41\\x00\\x05\\x00\\x2C\\x00\\x00\\x00\\xB9\\x02\\x00\\x00\\xA8\\x02\\x00\\x00\\x36\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\xBA\\x02\\x00\\x00\"\n\"\\xB9\\x02\\x00\\x00\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\xBB\\x02\\x00\\x00\\xB8\\x02\\x00\\x00\\xBA\\x02\\x00\\x00\\x0C\\x00\\x07\\x00\\x06\\x00\\x00\\x00\"\n\"\\xBC\\x02\\x00\\x00\\x01\\x00\\x00\\x00\\x1A\\x00\\x00\\x00\\xB7\\x02\\x00\\x00\\xBB\\x02\\x00\\x00\\x3E\\x00\\x03\\x00\\xB3\\x02\\x00\\x00\\xBC\\x02\\x00\\x00\"\n\"\\x41\\x00\\x06\\x00\\x42\\x00\\x00\\x00\\xBD\\x02\\x00\\x00\\xC4\\x00\\x00\\x00\\xDC\\x00\\x00\\x00\\x32\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\"\n\"\\xBE\\x02\\x00\\x00\\xBD\\x02\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\xBF\\x02\\x00\\x00\\xB3\\x02\\x00\\x00\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\"\n\"\\xC0\\x02\\x00\\x00\\xBE\\x02\\x00\\x00\\xBF\\x02\\x00\\x00\\x3D\\x00\\x04\\x00\\x2E\\x00\\x00\\x00\\xC1\\x02\\x00\\x00\\xA8\\x02\\x00\\x00\\x4F\\x00\\x08\\x00\"\n\"\\x07\\x00\\x00\\x00\\xC2\\x02\\x00\\x00\\xC1\\x02\\x00\\x00\\xC1\\x02\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x8E\\x00\\x05\\x00\"\n\"\\x07\\x00\\x00\\x00\\xC3\\x02\\x00\\x00\\xC2\\x02\\x00\\x00\\xC0\\x02\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\xC4\\x02\\x00\\x00\\x8B\\x02\\x00\\x00\"\n\"\\x81\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\xC5\\x02\\x00\\x00\\xC4\\x02\\x00\\x00\\xC3\\x02\\x00\\x00\\x3E\\x00\\x03\\x00\\x8B\\x02\\x00\\x00\\xC5\\x02\\x00\\x00\"\n\"\\xF9\\x00\\x02\\x00\\xA7\\x02\\x00\\x00\\xF8\\x00\\x02\\x00\\xA7\\x02\\x00\\x00\\xF7\\x00\\x03\\x00\\xC9\\x02\\x00\\x00\\x00\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\"\n\"\\xC7\\x02\\x00\\x00\\xC8\\x02\\x00\\x00\\xC9\\x02\\x00\\x00\\xF8\\x00\\x02\\x00\\xC8\\x02\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\xCB\\x02\\x00\\x00\"\n\"\\x2F\\x02\\x00\\x00\\x7F\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\xCC\\x02\\x00\\x00\\xCB\\x02\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\xCD\\x02\\x00\\x00\"\n\"\\x6A\\x02\\x00\\x00\\x0C\\x00\\x07\\x00\\x07\\x00\\x00\\x00\\xCE\\x02\\x00\\x00\\x01\\x00\\x00\\x00\\x47\\x00\\x00\\x00\\xCC\\x02\\x00\\x00\\xCD\\x02\\x00\\x00\"\n\"\\x3E\\x00\\x03\\x00\\xCA\\x02\\x00\\x00\\xCE\\x02\\x00\\x00\\x41\\x00\\x05\\x00\\x2C\\x00\\x00\\x00\\xCF\\x02\\x00\\x00\\xCA\\x02\\x00\\x00\\x9D\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\xD0\\x02\\x00\\x00\\xCF\\x02\\x00\\x00\\x41\\x00\\x05\\x00\\x2E\\x02\\x00\\x00\\xD4\\x02\\x00\\x00\\xD3\\x02\\x00\\x00\"\n\"\\x20\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\xD5\\x02\\x00\\x00\\xD4\\x02\\x00\\x00\\x8E\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\xD6\\x02\\x00\\x00\"\n\"\\xD5\\x02\\x00\\x00\\xD0\\x02\\x00\\x00\\x41\\x00\\x05\\x00\\x2C\\x00\\x00\\x00\\xD7\\x02\\x00\\x00\\xCA\\x02\\x00\\x00\\xA9\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x06\\x00\\x00\\x00\\xD8\\x02\\x00\\x00\\xD7\\x02\\x00\\x00\\x41\\x00\\x05\\x00\\x2E\\x02\\x00\\x00\\xDA\\x02\\x00\\x00\\xD3\\x02\\x00\\x00\\xD9\\x02\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\xDB\\x02\\x00\\x00\\xDA\\x02\\x00\\x00\\x8E\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\xDC\\x02\\x00\\x00\\xDB\\x02\\x00\\x00\"\n\"\\xD8\\x02\\x00\\x00\\x81\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\xDD\\x02\\x00\\x00\\xD6\\x02\\x00\\x00\\xDC\\x02\\x00\\x00\\x41\\x00\\x05\\x00\\x2C\\x00\\x00\\x00\"\n\"\\xDE\\x02\\x00\\x00\\xCA\\x02\\x00\\x00\\x32\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\xDF\\x02\\x00\\x00\\xDE\\x02\\x00\\x00\\x41\\x00\\x05\\x00\"\n\"\\x2E\\x02\\x00\\x00\\xE1\\x02\\x00\\x00\\xD3\\x02\\x00\\x00\\xE0\\x02\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\xE2\\x02\\x00\\x00\\xE1\\x02\\x00\\x00\"\n\"\\x8E\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\xE3\\x02\\x00\\x00\\xE2\\x02\\x00\\x00\\xDF\\x02\\x00\\x00\\x81\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\xE4\\x02\\x00\\x00\"\n\"\\xDD\\x02\\x00\\x00\\xE3\\x02\\x00\\x00\\x3E\\x00\\x03\\x00\\xCA\\x02\\x00\\x00\\xE4\\x02\\x00\\x00\\x41\\x00\\x05\\x00\\xE5\\x02\\x00\\x00\\xE6\\x02\\x00\\x00\"\n\"\\x40\\x00\\x00\\x00\\xD9\\x02\\x00\\x00\\x3D\\x00\\x04\\x00\\x3B\\x00\\x00\\x00\\xE7\\x02\\x00\\x00\\xE6\\x02\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\"\n\"\\xE8\\x02\\x00\\x00\\xCA\\x02\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\xE9\\x02\\x00\\x00\\xE8\\x02\\x00\\x00\\x00\\x00\\x00\\x00\\x51\\x00\\x05\\x00\"\n\"\\x06\\x00\\x00\\x00\\xEA\\x02\\x00\\x00\\xE8\\x02\\x00\\x00\\x01\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\xEB\\x02\\x00\\x00\\xE8\\x02\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\x50\\x00\\x07\\x00\\x2E\\x00\\x00\\x00\\xEC\\x02\\x00\\x00\\xE9\\x02\\x00\\x00\\xEA\\x02\\x00\\x00\\xEB\\x02\\x00\\x00\\x57\\x00\\x00\\x00\"\n\"\\x91\\x00\\x05\\x00\\x2E\\x00\\x00\\x00\\xED\\x02\\x00\\x00\\xE7\\x02\\x00\\x00\\xEC\\x02\\x00\\x00\\x4F\\x00\\x08\\x00\\x07\\x00\\x00\\x00\\xEE\\x02\\x00\\x00\"\n\"\\xED\\x02\\x00\\x00\\xED\\x02\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\xCA\\x02\\x00\\x00\\xEE\\x02\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x14\\x00\\x00\\x00\\xF0\\x02\\x00\\x00\\xEF\\x02\\x00\\x00\\x3D\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\xF1\\x02\\x00\\x00\\x2A\\x02\\x00\\x00\"\n\"\\x57\\x00\\x05\\x00\\x2E\\x00\\x00\\x00\\xF2\\x02\\x00\\x00\\xF0\\x02\\x00\\x00\\xF1\\x02\\x00\\x00\\x4F\\x00\\x08\\x00\\x07\\x00\\x00\\x00\\xF3\\x02\\x00\\x00\"\n\"\\xF2\\x02\\x00\\x00\\xF2\\x02\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\xF5\\x02\\x00\\x00\\xF8\\x02\\x00\\x00\"\n\"\\xF7\\x02\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\xF9\\x02\\x00\\x00\\xCA\\x02\\x00\\x00\\x57\\x00\\x05\\x00\\x2E\\x00\\x00\\x00\\xFA\\x02\\x00\\x00\"\n\"\\xF8\\x02\\x00\\x00\\xF9\\x02\\x00\\x00\\x4F\\x00\\x08\\x00\\x07\\x00\\x00\\x00\\xFB\\x02\\x00\\x00\\xFA\\x02\\x00\\x00\\xFA\\x02\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\xFC\\x02\\x00\\x00\\xF3\\x02\\x00\\x00\\xFB\\x02\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x07\\x00\\x00\\x00\\xFD\\x02\\x00\\x00\\x8B\\x02\\x00\\x00\\x81\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\xFE\\x02\\x00\\x00\\xFD\\x02\\x00\\x00\\xFC\\x02\\x00\\x00\"\n\"\\x3E\\x00\\x03\\x00\\x8B\\x02\\x00\\x00\\xFE\\x02\\x00\\x00\\xF9\\x00\\x02\\x00\\xC9\\x02\\x00\\x00\\xF8\\x00\\x02\\x00\\xC9\\x02\\x00\\x00\\xF7\\x00\\x03\\x00\"\n\"\\x02\\x03\\x00\\x00\\x00\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\\x00\\x03\\x00\\x00\\x01\\x03\\x00\\x00\\x02\\x03\\x00\\x00\\xF8\\x00\\x02\\x00\\x01\\x03\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\xF5\\x02\\x00\\x00\\x04\\x03\\x00\\x00\\x03\\x03\\x00\\x00\\x3D\\x00\\x04\\x00\\x2E\\x00\\x00\\x00\\x05\\x03\\x00\\x00\\x7D\\x00\\x00\\x00\"\n\"\\x4F\\x00\\x08\\x00\\x07\\x00\\x00\\x00\\x06\\x03\\x00\\x00\\x05\\x03\\x00\\x00\\x05\\x03\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x57\\x00\\x05\\x00\\x2E\\x00\\x00\\x00\\x07\\x03\\x00\\x00\\x04\\x03\\x00\\x00\\x06\\x03\\x00\\x00\\x4F\\x00\\x08\\x00\\x07\\x00\\x00\\x00\\x08\\x03\\x00\\x00\"\n\"\\x07\\x03\\x00\\x00\\x07\\x03\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x09\\x03\\x00\\x00\"\n\"\\x8B\\x02\\x00\\x00\\x85\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x0A\\x03\\x00\\x00\\x09\\x03\\x00\\x00\\x08\\x03\\x00\\x00\\x3E\\x00\\x03\\x00\\x8B\\x02\\x00\\x00\"\n\"\\x0A\\x03\\x00\\x00\\xF9\\x00\\x02\\x00\\x02\\x03\\x00\\x00\\xF8\\x00\\x02\\x00\\x02\\x03\\x00\\x00\\xF7\\x00\\x03\\x00\\x0D\\x03\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\xFA\\x00\\x04\\x00\\x0B\\x03\\x00\\x00\\x0C\\x03\\x00\\x00\\x0D\\x03\\x00\\x00\\xF8\\x00\\x02\\x00\\x0C\\x03\\x00\\x00\\x41\\x00\\x05\\x00\\x33\\x00\\x00\\x00\"\n\"\\x0E\\x03\\x00\\x00\\x7D\\x00\\x00\\x00\\x36\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x0F\\x03\\x00\\x00\\x0E\\x03\\x00\\x00\\xB8\\x00\\x05\\x00\"\n\"\\x21\\x00\\x00\\x00\\x10\\x03\\x00\\x00\\x0F\\x03\\x00\\x00\\x57\\x00\\x00\\x00\\xF7\\x00\\x03\\x00\\x12\\x03\\x00\\x00\\x00\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\"\n\"\\x10\\x03\\x00\\x00\\x11\\x03\\x00\\x00\\x12\\x03\\x00\\x00\\xF8\\x00\\x02\\x00\\x11\\x03\\x00\\x00\\xFC\\x00\\x01\\x00\\xF8\\x00\\x02\\x00\\x12\\x03\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x2E\\x00\\x00\\x00\\x15\\x03\\x00\\x00\\x7D\\x00\\x00\\x00\\x4F\\x00\\x07\\x00\\x16\\x00\\x00\\x00\\x16\\x03\\x00\\x00\\x15\\x03\\x00\\x00\"\n\"\\x15\\x03\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x33\\x00\\x00\\x00\\x17\\x03\\x00\\x00\\x7D\\x00\\x00\\x00\\x36\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x18\\x03\\x00\\x00\\x17\\x03\\x00\\x00\\x50\\x00\\x05\\x00\\x16\\x00\\x00\\x00\\x19\\x03\\x00\\x00\\x18\\x03\\x00\\x00\"\n\"\\x18\\x03\\x00\\x00\\x88\\x00\\x05\\x00\\x16\\x00\\x00\\x00\\x1A\\x03\\x00\\x00\\x16\\x03\\x00\\x00\\x19\\x03\\x00\\x00\\x3E\\x00\\x03\\x00\\x14\\x03\\x00\\x00\"\n\"\\x1A\\x03\\x00\\x00\\x3D\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x1B\\x03\\x00\\x00\\x14\\x03\\x00\\x00\\x3D\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x1C\\x03\\x00\\x00\"\n\"\\x14\\x03\\x00\\x00\\x94\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x1D\\x03\\x00\\x00\\x1B\\x03\\x00\\x00\\x1C\\x03\\x00\\x00\\x83\\x00\\x05\\x00\\x06\\x00\\x00\\x00\"\n\"\\x1E\\x03\\x00\\x00\\x69\\x00\\x00\\x00\\x1D\\x03\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x1F\\x03\\x00\\x00\\x77\\x02\\x00\\x00\\x85\\x00\\x05\\x00\"\n\"\\x06\\x00\\x00\\x00\\x20\\x03\\x00\\x00\\x1F\\x03\\x00\\x00\\x1E\\x03\\x00\\x00\\x3E\\x00\\x03\\x00\\x77\\x02\\x00\\x00\\x20\\x03\\x00\\x00\\xF9\\x00\\x02\\x00\"\n\"\\x0D\\x03\\x00\\x00\\xF8\\x00\\x02\\x00\\x0D\\x03\\x00\\x00\\xF7\\x00\\x03\\x00\\x24\\x03\\x00\\x00\\x00\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\\x22\\x03\\x00\\x00\"\n\"\\x23\\x03\\x00\\x00\\x24\\x03\\x00\\x00\\xF8\\x00\\x02\\x00\\x23\\x03\\x00\\x00\\x39\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x25\\x03\\x00\\x00\\x11\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x26\\x03\\x00\\x00\\x77\\x02\\x00\\x00\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x27\\x03\\x00\\x00\\x26\\x03\\x00\\x00\"\n\"\\x25\\x03\\x00\\x00\\x3E\\x00\\x03\\x00\\x77\\x02\\x00\\x00\\x27\\x03\\x00\\x00\\xF9\\x00\\x02\\x00\\x24\\x03\\x00\\x00\\xF8\\x00\\x02\\x00\\x24\\x03\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x2A\\x03\\x00\\x00\\x8B\\x02\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x2B\\x03\\x00\\x00\\x77\\x02\\x00\\x00\"\n\"\\x8E\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x2C\\x03\\x00\\x00\\x2A\\x03\\x00\\x00\\x2B\\x03\\x00\\x00\\x41\\x00\\x05\\x00\\x4D\\x02\\x00\\x00\\x2E\\x03\\x00\\x00\"\n\"\\xC4\\x00\\x00\\x00\\x2D\\x03\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x2F\\x03\\x00\\x00\\x2E\\x03\\x00\\x00\\x85\\x00\\x05\\x00\\x07\\x00\\x00\\x00\"\n\"\\x30\\x03\\x00\\x00\\x2C\\x03\\x00\\x00\\x2F\\x03\\x00\\x00\\x3E\\x00\\x03\\x00\\x31\\x03\\x00\\x00\\x30\\x03\\x00\\x00\\x39\\x00\\x05\\x00\\x07\\x00\\x00\\x00\"\n\"\\x32\\x03\\x00\\x00\\x0B\\x00\\x00\\x00\\x31\\x03\\x00\\x00\\x3D\\x00\\x04\\x00\\x2E\\x00\\x00\\x00\\x33\\x03\\x00\\x00\\x29\\x03\\x00\\x00\\x4F\\x00\\x09\\x00\"\n\"\\x2E\\x00\\x00\\x00\\x34\\x03\\x00\\x00\\x33\\x03\\x00\\x00\\x32\\x03\\x00\\x00\\x04\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x03\\x00\\x00\\x00\"\n\"\\x3E\\x00\\x03\\x00\\x29\\x03\\x00\\x00\\x34\\x03\\x00\\x00\\x41\\x00\\x05\\x00\\x35\\x03\\x00\\x00\\x36\\x03\\x00\\x00\\x29\\x03\\x00\\x00\\x36\\x00\\x00\\x00\"\n\"\\x3E\\x00\\x03\\x00\\x36\\x03\\x00\\x00\\x69\\x00\\x00\\x00\\xFD\\x00\\x01\\x00\\x38\\x00\\x01\\x00\\x36\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x37\\x00\\x03\\x00\\x08\\x00\\x00\\x00\\x0A\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x0C\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\"\n\"\\x2C\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x2C\\x00\\x00\\x00\\x3A\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\xF7\\x00\\x03\\x00\"\n\"\\x25\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\\x23\\x00\\x00\\x00\\x24\\x00\\x00\\x00\\x25\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x24\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x26\\x00\\x00\\x00\\x0A\\x00\\x00\\x00\\xFE\\x00\\x02\\x00\\x26\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x25\\x00\\x00\\x00\"\n\"\\xF7\\x00\\x03\\x00\\x2B\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\\x29\\x00\\x00\\x00\\x2A\\x00\\x00\\x00\\x4E\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\"\n\"\\x2A\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x33\\x00\\x00\\x00\\x34\\x00\\x00\\x00\\x30\\x00\\x00\\x00\\x32\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\"\n\"\\x35\\x00\\x00\\x00\\x34\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x33\\x00\\x00\\x00\\x37\\x00\\x00\\x00\\x30\\x00\\x00\\x00\\x36\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x06\\x00\\x00\\x00\\x38\\x00\\x00\\x00\\x37\\x00\\x00\\x00\\x88\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x39\\x00\\x00\\x00\\x35\\x00\\x00\\x00\\x38\\x00\\x00\\x00\"\n\"\\x3E\\x00\\x03\\x00\\x2D\\x00\\x00\\x00\\x39\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x42\\x00\\x00\\x00\\x43\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x41\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x44\\x00\\x00\\x00\\x43\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x45\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\"\n\"\\x83\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x46\\x00\\x00\\x00\\x44\\x00\\x00\\x00\\x45\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x42\\x00\\x00\\x00\\x47\\x00\\x00\\x00\"\n\"\\x40\\x00\\x00\\x00\\x41\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x48\\x00\\x00\\x00\\x47\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x42\\x00\\x00\\x00\"\n\"\\x4A\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x49\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x4B\\x00\\x00\\x00\\x4A\\x00\\x00\\x00\\x83\\x00\\x05\\x00\"\n\"\\x06\\x00\\x00\\x00\\x4C\\x00\\x00\\x00\\x48\\x00\\x00\\x00\\x4B\\x00\\x00\\x00\\x88\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x4D\\x00\\x00\\x00\\x46\\x00\\x00\\x00\"\n\"\\x4C\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x3A\\x00\\x00\\x00\\x4D\\x00\\x00\\x00\\xF9\\x00\\x02\\x00\\x2B\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x4E\\x00\\x00\\x00\"\n\"\\x41\\x00\\x05\\x00\\x42\\x00\\x00\\x00\\x4F\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x41\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x50\\x00\\x00\\x00\"\n\"\\x4F\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x33\\x00\\x00\\x00\\x51\\x00\\x00\\x00\\x30\\x00\\x00\\x00\\x32\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\"\n\"\\x52\\x00\\x00\\x00\\x51\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x53\\x00\\x00\\x00\\x50\\x00\\x00\\x00\\x52\\x00\\x00\\x00\\x41\\x00\\x05\\x00\"\n\"\\x33\\x00\\x00\\x00\\x54\\x00\\x00\\x00\\x30\\x00\\x00\\x00\\x36\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x55\\x00\\x00\\x00\\x54\\x00\\x00\\x00\"\n\"\\x88\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x56\\x00\\x00\\x00\\x53\\x00\\x00\\x00\\x55\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x2D\\x00\\x00\\x00\\x56\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x58\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x42\\x00\\x00\\x00\\x59\\x00\\x00\\x00\\x40\\x00\\x00\\x00\"\n\"\\x49\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x5A\\x00\\x00\\x00\\x59\\x00\\x00\\x00\\x83\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x5B\\x00\\x00\\x00\"\n\"\\x58\\x00\\x00\\x00\\x5A\\x00\\x00\\x00\\x0C\\x00\\x07\\x00\\x06\\x00\\x00\\x00\\x5C\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x28\\x00\\x00\\x00\\x57\\x00\\x00\\x00\"\n\"\\x5B\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x2D\\x00\\x00\\x00\\x5C\\x00\\x00\\x00\\xF7\\x00\\x03\\x00\\x60\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\"\n\"\\x5E\\x00\\x00\\x00\\x5F\\x00\\x00\\x00\\x60\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x5F\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x61\\x00\\x00\\x00\"\n\"\\x2D\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x62\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x63\\x00\\x00\\x00\"\n\"\\x62\\x00\\x00\\x00\\x61\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x2D\\x00\\x00\\x00\\x63\\x00\\x00\\x00\\xF9\\x00\\x02\\x00\\x60\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\"\n\"\\x60\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x64\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x66\\x00\\x00\\x00\"\n\"\\x64\\x00\\x00\\x00\\x65\\x00\\x00\\x00\\x7F\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x67\\x00\\x00\\x00\\x66\\x00\\x00\\x00\\x0C\\x00\\x06\\x00\\x06\\x00\\x00\\x00\"\n\"\\x68\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x67\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x3A\\x00\\x00\\x00\\x68\\x00\\x00\\x00\\xF9\\x00\\x02\\x00\"\n\"\\x2B\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x2B\\x00\\x00\\x00\\x41\\x00\\x06\\x00\\x42\\x00\\x00\\x00\\x6B\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x6A\\x00\\x00\\x00\"\n\"\\x36\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x6C\\x00\\x00\\x00\\x6B\\x00\\x00\\x00\\x83\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x6D\\x00\\x00\\x00\"\n\"\\x69\\x00\\x00\\x00\\x6C\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x6E\\x00\\x00\\x00\\x3A\\x00\\x00\\x00\\x0C\\x00\\x08\\x00\\x06\\x00\\x00\\x00\"\n\"\\x6F\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x2B\\x00\\x00\\x00\\x6E\\x00\\x00\\x00\\x57\\x00\\x00\\x00\\x69\\x00\\x00\\x00\\x41\\x00\\x06\\x00\\x42\\x00\\x00\\x00\"\n\"\\x70\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x6A\\x00\\x00\\x00\\x36\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x71\\x00\\x00\\x00\\x70\\x00\\x00\\x00\"\n\"\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x72\\x00\\x00\\x00\\x6F\\x00\\x00\\x00\\x71\\x00\\x00\\x00\\x81\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x73\\x00\\x00\\x00\"\n\"\\x6D\\x00\\x00\\x00\\x72\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x3A\\x00\\x00\\x00\\x73\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x74\\x00\\x00\\x00\"\n\"\\x0A\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x75\\x00\\x00\\x00\\x3A\\x00\\x00\\x00\\x8E\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x76\\x00\\x00\\x00\"\n\"\\x74\\x00\\x00\\x00\\x75\\x00\\x00\\x00\\xFE\\x00\\x02\\x00\\x76\\x00\\x00\\x00\\x38\\x00\\x01\\x00\\x36\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x0F\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x08\\x00\\x00\\x00\\x8F\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x3B\\x00\\x04\\x00\\x08\\x00\\x00\\x00\\x93\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x2C\\x00\\x00\\x00\\x96\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x3B\\x00\\x04\\x00\\x08\\x00\\x00\\x00\\x99\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\xF7\\x00\\x03\\x00\\x7C\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\"\n\"\\x7A\\x00\\x00\\x00\\x7B\\x00\\x00\\x00\\x7C\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x7B\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x2E\\x00\\x00\\x00\\x7E\\x00\\x00\\x00\"\n\"\\x7D\\x00\\x00\\x00\\x4F\\x00\\x08\\x00\\x07\\x00\\x00\\x00\\x7F\\x00\\x00\\x00\\x7E\\x00\\x00\\x00\\x7E\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\x83\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x82\\x00\\x00\\x00\\x7F\\x00\\x00\\x00\\x81\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x33\\x00\\x00\\x00\"\n\"\\x83\\x00\\x00\\x00\\x7D\\x00\\x00\\x00\\x36\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x84\\x00\\x00\\x00\\x83\\x00\\x00\\x00\\x50\\x00\\x06\\x00\"\n\"\\x07\\x00\\x00\\x00\\x85\\x00\\x00\\x00\\x84\\x00\\x00\\x00\\x84\\x00\\x00\\x00\\x84\\x00\\x00\\x00\\x88\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x86\\x00\\x00\\x00\"\n\"\\x82\\x00\\x00\\x00\\x85\\x00\\x00\\x00\\x81\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x89\\x00\\x00\\x00\\x86\\x00\\x00\\x00\\x88\\x00\\x00\\x00\\x85\\x00\\x05\\x00\"\n\"\\x07\\x00\\x00\\x00\\x8D\\x00\\x00\\x00\\x89\\x00\\x00\\x00\\x8C\\x00\\x00\\x00\\xFE\\x00\\x02\\x00\\x8D\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x7C\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x2E\\x00\\x00\\x00\\x90\\x00\\x00\\x00\\x7D\\x00\\x00\\x00\\x4F\\x00\\x08\\x00\\x07\\x00\\x00\\x00\\x91\\x00\\x00\\x00\\x90\\x00\\x00\\x00\"\n\"\\x90\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x0C\\x00\\x06\\x00\\x07\\x00\\x00\\x00\\x92\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x04\\x00\\x00\\x00\\x91\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x8F\\x00\\x00\\x00\\x92\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x2E\\x00\\x00\\x00\\x94\\x00\\x00\\x00\"\n\"\\x7D\\x00\\x00\\x00\\x4F\\x00\\x08\\x00\\x07\\x00\\x00\\x00\\x95\\x00\\x00\\x00\\x94\\x00\\x00\\x00\\x94\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x93\\x00\\x00\\x00\\x95\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x2C\\x00\\x00\\x00\\x97\\x00\\x00\\x00\\x8F\\x00\\x00\\x00\"\n\"\\x32\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x98\\x00\\x00\\x00\\x97\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x96\\x00\\x00\\x00\\x98\\x00\\x00\\x00\"\n\"\\x3E\\x00\\x03\\x00\\x99\\x00\\x00\\x00\\x9C\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x2C\\x00\\x00\\x00\\x9E\\x00\\x00\\x00\\x8F\\x00\\x00\\x00\\x9D\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x9F\\x00\\x00\\x00\\x9E\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\xA0\\x00\\x00\\x00\\x96\\x00\\x00\\x00\"\n\"\\xBA\\x00\\x05\\x00\\x21\\x00\\x00\\x00\\xA1\\x00\\x00\\x00\\x9F\\x00\\x00\\x00\\xA0\\x00\\x00\\x00\\xF7\\x00\\x03\\x00\\xA3\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\xFA\\x00\\x04\\x00\\xA1\\x00\\x00\\x00\\xA2\\x00\\x00\\x00\\xA3\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\xA2\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x2C\\x00\\x00\\x00\"\n\"\\xA4\\x00\\x00\\x00\\x8F\\x00\\x00\\x00\\x9D\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\xA5\\x00\\x00\\x00\\xA4\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\"\n\"\\x96\\x00\\x00\\x00\\xA5\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x2E\\x00\\x00\\x00\\xA6\\x00\\x00\\x00\\x7D\\x00\\x00\\x00\\x4F\\x00\\x08\\x00\\x07\\x00\\x00\\x00\"\n\"\\xA7\\x00\\x00\\x00\\xA6\\x00\\x00\\x00\\xA6\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x93\\x00\\x00\\x00\"\n\"\\xA7\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x2C\\x00\\x00\\x00\\xA8\\x00\\x00\\x00\\x99\\x00\\x00\\x00\\x9D\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\xA8\\x00\\x00\\x00\"\n\"\\x8A\\x00\\x00\\x00\\xF9\\x00\\x02\\x00\\xA3\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\xA3\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x2C\\x00\\x00\\x00\\xAA\\x00\\x00\\x00\"\n\"\\x8F\\x00\\x00\\x00\\xA9\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\xAB\\x00\\x00\\x00\\xAA\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\"\n\"\\xAC\\x00\\x00\\x00\\x96\\x00\\x00\\x00\\xBA\\x00\\x05\\x00\\x21\\x00\\x00\\x00\\xAD\\x00\\x00\\x00\\xAB\\x00\\x00\\x00\\xAC\\x00\\x00\\x00\\xF7\\x00\\x03\\x00\"\n\"\\xAF\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\\xAD\\x00\\x00\\x00\\xAE\\x00\\x00\\x00\\xAF\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\xAE\\x00\\x00\\x00\"\n\"\\x41\\x00\\x05\\x00\\x2C\\x00\\x00\\x00\\xB0\\x00\\x00\\x00\\x8F\\x00\\x00\\x00\\xA9\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\xB1\\x00\\x00\\x00\"\n\"\\xB0\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x96\\x00\\x00\\x00\\xB1\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x2E\\x00\\x00\\x00\\xB2\\x00\\x00\\x00\\x7D\\x00\\x00\\x00\"\n\"\\x4F\\x00\\x08\\x00\\x07\\x00\\x00\\x00\\xB3\\x00\\x00\\x00\\xB2\\x00\\x00\\x00\\xB2\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x3E\\x00\\x03\\x00\\x93\\x00\\x00\\x00\\xB3\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x2C\\x00\\x00\\x00\\xB5\\x00\\x00\\x00\\x99\\x00\\x00\\x00\\x9D\\x00\\x00\\x00\"\n\"\\x3E\\x00\\x03\\x00\\xB5\\x00\\x00\\x00\\xB4\\x00\\x00\\x00\\xF9\\x00\\x02\\x00\\xAF\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\xAF\\x00\\x00\\x00\\x41\\x00\\x05\\x00\"\n\"\\x2C\\x00\\x00\\x00\\xB6\\x00\\x00\\x00\\x93\\x00\\x00\\x00\\x32\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\xB7\\x00\\x00\\x00\\xB6\\x00\\x00\\x00\"\n\"\\xBA\\x00\\x05\\x00\\x21\\x00\\x00\\x00\\xB8\\x00\\x00\\x00\\xB7\\x00\\x00\\x00\\x57\\x00\\x00\\x00\\xF7\\x00\\x03\\x00\\xBA\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\xFA\\x00\\x04\\x00\\xB8\\x00\\x00\\x00\\xB9\\x00\\x00\\x00\\xBA\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\xB9\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x2C\\x00\\x00\\x00\"\n\"\\xBC\\x00\\x00\\x00\\x99\\x00\\x00\\x00\\xA9\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\xBC\\x00\\x00\\x00\\xBB\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x2C\\x00\\x00\\x00\"\n\"\\xBD\\x00\\x00\\x00\\x93\\x00\\x00\\x00\\x32\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\xBE\\x00\\x00\\x00\\xBD\\x00\\x00\\x00\\x7F\\x00\\x04\\x00\"\n\"\\x06\\x00\\x00\\x00\\xBF\\x00\\x00\\x00\\xBE\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x2C\\x00\\x00\\x00\\xC0\\x00\\x00\\x00\\x93\\x00\\x00\\x00\\x32\\x00\\x00\\x00\"\n\"\\x3E\\x00\\x03\\x00\\xC0\\x00\\x00\\x00\\xBF\\x00\\x00\\x00\\xF9\\x00\\x02\\x00\\xBA\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\xBA\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x07\\x00\\x00\\x00\\xC1\\x00\\x00\\x00\\x99\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\xC6\\x00\\x00\\x00\\xC7\\x00\\x00\\x00\\xC4\\x00\\x00\\x00\\xC5\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x2E\\x00\\x00\\x00\\xC8\\x00\\x00\\x00\\xC7\\x00\\x00\\x00\\x4F\\x00\\x08\\x00\\x07\\x00\\x00\\x00\\xC9\\x00\\x00\\x00\\xC8\\x00\\x00\\x00\"\n\"\\xC8\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\xCA\\x00\\x00\\x00\\x93\\x00\\x00\\x00\"\n\"\\x85\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\xCB\\x00\\x00\\x00\\xC9\\x00\\x00\\x00\\xCA\\x00\\x00\\x00\\x41\\x00\\x06\\x00\\x42\\x00\\x00\\x00\\xCC\\x00\\x00\\x00\"\n\"\\xC4\\x00\\x00\\x00\\xC5\\x00\\x00\\x00\\x36\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\xCD\\x00\\x00\\x00\\xCC\\x00\\x00\\x00\\x50\\x00\\x06\\x00\"\n\"\\x07\\x00\\x00\\x00\\xCE\\x00\\x00\\x00\\x57\\x00\\x00\\x00\\x57\\x00\\x00\\x00\\xCD\\x00\\x00\\x00\\x81\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\xCF\\x00\\x00\\x00\"\n\"\\xCB\\x00\\x00\\x00\\xCE\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x2C\\x00\\x00\\x00\\xD0\\x00\\x00\\x00\\x93\\x00\\x00\\x00\\x32\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x06\\x00\\x00\\x00\\xD1\\x00\\x00\\x00\\xD0\\x00\\x00\\x00\\x7F\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\xD2\\x00\\x00\\x00\\xD1\\x00\\x00\\x00\\x50\\x00\\x06\\x00\"\n\"\\x07\\x00\\x00\\x00\\xD3\\x00\\x00\\x00\\xD2\\x00\\x00\\x00\\xD2\\x00\\x00\\x00\\xD2\\x00\\x00\\x00\\x88\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\xD4\\x00\\x00\\x00\"\n\"\\xCF\\x00\\x00\\x00\\xD3\\x00\\x00\\x00\\x81\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\xD5\\x00\\x00\\x00\\xC1\\x00\\x00\\x00\\xD4\\x00\\x00\\x00\\xFE\\x00\\x02\\x00\"\n\"\\xD5\\x00\\x00\\x00\\x38\\x00\\x01\\x00\\x36\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x11\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\"\n\"\\x12\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x08\\x00\\x00\\x00\\xD8\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x2C\\x00\\x00\\x00\\xDA\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x39\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\xD9\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\xD8\\x00\\x00\\x00\\xD9\\x00\\x00\\x00\"\n\"\\x3E\\x00\\x03\\x00\\xDA\\x00\\x00\\x00\\x57\\x00\\x00\\x00\\xF7\\x00\\x03\\x00\\xDF\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\\xDD\\x00\\x00\\x00\"\n\"\\xDE\\x00\\x00\\x00\\xF4\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\xDE\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\xE1\\x00\\x00\\x00\\xE4\\x00\\x00\\x00\\xE3\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\xE5\\x00\\x00\\x00\\xD8\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\xE8\\x00\\x00\\x00\\xE9\\x00\\x00\\x00\\xC4\\x00\\x00\\x00\"\n\"\\xE7\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\xEA\\x00\\x00\\x00\\xE9\\x00\\x00\\x00\\x4F\\x00\\x08\\x00\\x07\\x00\\x00\\x00\\xEB\\x00\\x00\\x00\"\n\"\\xEA\\x00\\x00\\x00\\xEA\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\xEC\\x00\\x00\\x00\"\n\"\\xE6\\x00\\x00\\x00\\xEB\\x00\\x00\\x00\\x81\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\xED\\x00\\x00\\x00\\xE5\\x00\\x00\\x00\\xEC\\x00\\x00\\x00\\x51\\x00\\x05\\x00\"\n\"\\x06\\x00\\x00\\x00\\xEE\\x00\\x00\\x00\\xED\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x59\\x00\\x06\\x00\\x06\\x00\\x00\\x00\\xEF\\x00\\x00\\x00\\xE4\\x00\\x00\\x00\"\n\"\\xED\\x00\\x00\\x00\\xEE\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\xF0\\x00\\x00\\x00\\xDA\\x00\\x00\\x00\\x81\\x00\\x05\\x00\\x06\\x00\\x00\\x00\"\n\"\\xF1\\x00\\x00\\x00\\xF0\\x00\\x00\\x00\\xEF\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\xDA\\x00\\x00\\x00\\xF1\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\"\n\"\\xF2\\x00\\x00\\x00\\xDA\\x00\\x00\\x00\\xFE\\x00\\x02\\x00\\xF2\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\xF4\\x00\\x00\\x00\\xF7\\x00\\x03\\x00\\xF8\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\\xF6\\x00\\x00\\x00\\xF7\\x00\\x00\\x00\\x38\\x01\\x00\\x00\\xF8\\x00\\x02\\x00\\xF7\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\xE1\\x00\\x00\\x00\\xF9\\x00\\x00\\x00\\xE3\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\xFA\\x00\\x00\\x00\\xD8\\x00\\x00\\x00\\x41\\x00\\x05\\x00\"\n\"\\xE8\\x00\\x00\\x00\\xFC\\x00\\x00\\x00\\xC4\\x00\\x00\\x00\\xE7\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\xFD\\x00\\x00\\x00\\xFC\\x00\\x00\\x00\"\n\"\\x4F\\x00\\x08\\x00\\x07\\x00\\x00\\x00\\xFE\\x00\\x00\\x00\\xFD\\x00\\x00\\x00\\xFD\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x85\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\xFF\\x00\\x00\\x00\\xFB\\x00\\x00\\x00\\xFE\\x00\\x00\\x00\\x81\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x00\\x01\\x00\\x00\"\n\"\\xFA\\x00\\x00\\x00\\xFF\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x01\\x01\\x00\\x00\\x00\\x01\\x00\\x00\\x02\\x00\\x00\\x00\\x59\\x00\\x06\\x00\"\n\"\\x06\\x00\\x00\\x00\\x02\\x01\\x00\\x00\\xF9\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x01\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x03\\x01\\x00\\x00\"\n\"\\xDA\\x00\\x00\\x00\\x81\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x04\\x01\\x00\\x00\\x03\\x01\\x00\\x00\\x02\\x01\\x00\\x00\\x3E\\x00\\x03\\x00\\xDA\\x00\\x00\\x00\"\n\"\\x04\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\xE1\\x00\\x00\\x00\\x05\\x01\\x00\\x00\\xE3\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x06\\x01\\x00\\x00\"\n\"\\xD8\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\xE8\\x00\\x00\\x00\\x08\\x01\\x00\\x00\\xC4\\x00\\x00\\x00\\xE7\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x16\\x00\\x00\\x00\"\n\"\\x09\\x01\\x00\\x00\\x08\\x01\\x00\\x00\\x4F\\x00\\x08\\x00\\x07\\x00\\x00\\x00\\x0A\\x01\\x00\\x00\\x09\\x01\\x00\\x00\\x09\\x01\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x0B\\x01\\x00\\x00\\x07\\x01\\x00\\x00\\x0A\\x01\\x00\\x00\\x81\\x00\\x05\\x00\"\n\"\\x07\\x00\\x00\\x00\\x0C\\x01\\x00\\x00\\x06\\x01\\x00\\x00\\x0B\\x01\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x0D\\x01\\x00\\x00\\x0C\\x01\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\x59\\x00\\x06\\x00\\x06\\x00\\x00\\x00\\x0E\\x01\\x00\\x00\\x05\\x01\\x00\\x00\\x0C\\x01\\x00\\x00\\x0D\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x06\\x00\\x00\\x00\\x0F\\x01\\x00\\x00\\xDA\\x00\\x00\\x00\\x81\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x10\\x01\\x00\\x00\\x0F\\x01\\x00\\x00\\x0E\\x01\\x00\\x00\"\n\"\\x3E\\x00\\x03\\x00\\xDA\\x00\\x00\\x00\\x10\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\xE1\\x00\\x00\\x00\\x11\\x01\\x00\\x00\\xE3\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x07\\x00\\x00\\x00\\x12\\x01\\x00\\x00\\xD8\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\xE8\\x00\\x00\\x00\\x13\\x01\\x00\\x00\\xC4\\x00\\x00\\x00\\xE7\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x14\\x01\\x00\\x00\\x13\\x01\\x00\\x00\\x4F\\x00\\x08\\x00\\x07\\x00\\x00\\x00\\x15\\x01\\x00\\x00\\x14\\x01\\x00\\x00\"\n\"\\x14\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x16\\x01\\x00\\x00\\xE6\\x00\\x00\\x00\"\n\"\\x15\\x01\\x00\\x00\\x81\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x17\\x01\\x00\\x00\\x12\\x01\\x00\\x00\\x16\\x01\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\"\n\"\\x18\\x01\\x00\\x00\\x17\\x01\\x00\\x00\\x02\\x00\\x00\\x00\\x59\\x00\\x06\\x00\\x06\\x00\\x00\\x00\\x19\\x01\\x00\\x00\\x11\\x01\\x00\\x00\\x17\\x01\\x00\\x00\"\n\"\\x18\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x1A\\x01\\x00\\x00\\xDA\\x00\\x00\\x00\\x81\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x1B\\x01\\x00\\x00\"\n\"\\x1A\\x01\\x00\\x00\\x19\\x01\\x00\\x00\\x3E\\x00\\x03\\x00\\xDA\\x00\\x00\\x00\\x1B\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\xE1\\x00\\x00\\x00\\x1C\\x01\\x00\\x00\"\n\"\\xE3\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x1D\\x01\\x00\\x00\\xD8\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\xE8\\x00\\x00\\x00\\x1F\\x01\\x00\\x00\"\n\"\\xC4\\x00\\x00\\x00\\xE7\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x20\\x01\\x00\\x00\\x1F\\x01\\x00\\x00\\x4F\\x00\\x08\\x00\\x07\\x00\\x00\\x00\"\n\"\\x21\\x01\\x00\\x00\\x20\\x01\\x00\\x00\\x20\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x07\\x00\\x00\\x00\"\n\"\\x22\\x01\\x00\\x00\\x1E\\x01\\x00\\x00\\x21\\x01\\x00\\x00\\x81\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x23\\x01\\x00\\x00\\x1D\\x01\\x00\\x00\\x22\\x01\\x00\\x00\"\n\"\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x24\\x01\\x00\\x00\\x23\\x01\\x00\\x00\\x02\\x00\\x00\\x00\\x59\\x00\\x06\\x00\\x06\\x00\\x00\\x00\\x25\\x01\\x00\\x00\"\n\"\\x1C\\x01\\x00\\x00\\x23\\x01\\x00\\x00\\x24\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x26\\x01\\x00\\x00\\xDA\\x00\\x00\\x00\\x81\\x00\\x05\\x00\"\n\"\\x06\\x00\\x00\\x00\\x27\\x01\\x00\\x00\\x26\\x01\\x00\\x00\\x25\\x01\\x00\\x00\\x3E\\x00\\x03\\x00\\xDA\\x00\\x00\\x00\\x27\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\xE1\\x00\\x00\\x00\\x28\\x01\\x00\\x00\\xE3\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x29\\x01\\x00\\x00\\xD8\\x00\\x00\\x00\\x41\\x00\\x05\\x00\"\n\"\\xE8\\x00\\x00\\x00\\x2B\\x01\\x00\\x00\\xC4\\x00\\x00\\x00\\xE7\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x2C\\x01\\x00\\x00\\x2B\\x01\\x00\\x00\"\n\"\\x4F\\x00\\x08\\x00\\x07\\x00\\x00\\x00\\x2D\\x01\\x00\\x00\\x2C\\x01\\x00\\x00\\x2C\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x85\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x2E\\x01\\x00\\x00\\x2A\\x01\\x00\\x00\\x2D\\x01\\x00\\x00\\x81\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x2F\\x01\\x00\\x00\"\n\"\\x29\\x01\\x00\\x00\\x2E\\x01\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x30\\x01\\x00\\x00\\x2F\\x01\\x00\\x00\\x02\\x00\\x00\\x00\\x59\\x00\\x06\\x00\"\n\"\\x06\\x00\\x00\\x00\\x31\\x01\\x00\\x00\\x28\\x01\\x00\\x00\\x2F\\x01\\x00\\x00\\x30\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x32\\x01\\x00\\x00\"\n\"\\xDA\\x00\\x00\\x00\\x81\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x33\\x01\\x00\\x00\\x32\\x01\\x00\\x00\\x31\\x01\\x00\\x00\\x3E\\x00\\x03\\x00\\xDA\\x00\\x00\\x00\"\n\"\\x33\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x34\\x01\\x00\\x00\\xDA\\x00\\x00\\x00\\x88\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x36\\x01\\x00\\x00\"\n\"\\x34\\x01\\x00\\x00\\x35\\x01\\x00\\x00\\xFE\\x00\\x02\\x00\\x36\\x01\\x00\\x00\\xF8\\x00\\x02\\x00\\x38\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\xE1\\x00\\x00\\x00\"\n\"\\x39\\x01\\x00\\x00\\xE3\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x3A\\x01\\x00\\x00\\xD8\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\xE8\\x00\\x00\\x00\"\n\"\\x3C\\x01\\x00\\x00\\xC4\\x00\\x00\\x00\\xE7\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x3D\\x01\\x00\\x00\\x3C\\x01\\x00\\x00\\x4F\\x00\\x08\\x00\"\n\"\\x07\\x00\\x00\\x00\\x3E\\x01\\x00\\x00\\x3D\\x01\\x00\\x00\\x3D\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x85\\x00\\x05\\x00\"\n\"\\x07\\x00\\x00\\x00\\x3F\\x01\\x00\\x00\\x3B\\x01\\x00\\x00\\x3E\\x01\\x00\\x00\\x81\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x40\\x01\\x00\\x00\\x3A\\x01\\x00\\x00\"\n\"\\x3F\\x01\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x41\\x01\\x00\\x00\\x40\\x01\\x00\\x00\\x02\\x00\\x00\\x00\\x59\\x00\\x06\\x00\\x06\\x00\\x00\\x00\"\n\"\\x42\\x01\\x00\\x00\\x39\\x01\\x00\\x00\\x40\\x01\\x00\\x00\\x41\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x43\\x01\\x00\\x00\\xDA\\x00\\x00\\x00\"\n\"\\x81\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x44\\x01\\x00\\x00\\x43\\x01\\x00\\x00\\x42\\x01\\x00\\x00\\x3E\\x00\\x03\\x00\\xDA\\x00\\x00\\x00\\x44\\x01\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\xE1\\x00\\x00\\x00\\x45\\x01\\x00\\x00\\xE3\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x46\\x01\\x00\\x00\\xD8\\x00\\x00\\x00\"\n\"\\x41\\x00\\x05\\x00\\xE8\\x00\\x00\\x00\\x47\\x01\\x00\\x00\\xC4\\x00\\x00\\x00\\xE7\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x48\\x01\\x00\\x00\"\n\"\\x47\\x01\\x00\\x00\\x4F\\x00\\x08\\x00\\x07\\x00\\x00\\x00\\x49\\x01\\x00\\x00\\x48\\x01\\x00\\x00\\x48\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x4A\\x01\\x00\\x00\\xFB\\x00\\x00\\x00\\x49\\x01\\x00\\x00\\x81\\x00\\x05\\x00\\x07\\x00\\x00\\x00\"\n\"\\x4B\\x01\\x00\\x00\\x46\\x01\\x00\\x00\\x4A\\x01\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x4C\\x01\\x00\\x00\\x4B\\x01\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x59\\x00\\x06\\x00\\x06\\x00\\x00\\x00\\x4D\\x01\\x00\\x00\\x45\\x01\\x00\\x00\\x4B\\x01\\x00\\x00\\x4C\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\"\n\"\\x4E\\x01\\x00\\x00\\xDA\\x00\\x00\\x00\\x81\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x4F\\x01\\x00\\x00\\x4E\\x01\\x00\\x00\\x4D\\x01\\x00\\x00\\x3E\\x00\\x03\\x00\"\n\"\\xDA\\x00\\x00\\x00\\x4F\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\xE1\\x00\\x00\\x00\\x50\\x01\\x00\\x00\\xE3\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\"\n\"\\x51\\x01\\x00\\x00\\xD8\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\xE8\\x00\\x00\\x00\\x53\\x01\\x00\\x00\\xC4\\x00\\x00\\x00\\xE7\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x16\\x00\\x00\\x00\\x54\\x01\\x00\\x00\\x53\\x01\\x00\\x00\\x4F\\x00\\x08\\x00\\x07\\x00\\x00\\x00\\x55\\x01\\x00\\x00\\x54\\x01\\x00\\x00\\x54\\x01\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x56\\x01\\x00\\x00\\x52\\x01\\x00\\x00\\x55\\x01\\x00\\x00\"\n\"\\x81\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x57\\x01\\x00\\x00\\x51\\x01\\x00\\x00\\x56\\x01\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x58\\x01\\x00\\x00\"\n\"\\x57\\x01\\x00\\x00\\x02\\x00\\x00\\x00\\x59\\x00\\x06\\x00\\x06\\x00\\x00\\x00\\x59\\x01\\x00\\x00\\x50\\x01\\x00\\x00\\x57\\x01\\x00\\x00\\x58\\x01\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x5A\\x01\\x00\\x00\\xDA\\x00\\x00\\x00\\x81\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x5B\\x01\\x00\\x00\\x5A\\x01\\x00\\x00\"\n\"\\x59\\x01\\x00\\x00\\x3E\\x00\\x03\\x00\\xDA\\x00\\x00\\x00\\x5B\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\xE1\\x00\\x00\\x00\\x5C\\x01\\x00\\x00\\xE3\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x5D\\x01\\x00\\x00\\xD8\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\xE8\\x00\\x00\\x00\\x5E\\x01\\x00\\x00\\xC4\\x00\\x00\\x00\"\n\"\\xE7\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x5F\\x01\\x00\\x00\\x5E\\x01\\x00\\x00\\x4F\\x00\\x08\\x00\\x07\\x00\\x00\\x00\\x60\\x01\\x00\\x00\"\n\"\\x5F\\x01\\x00\\x00\\x5F\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x61\\x01\\x00\\x00\"\n\"\\x07\\x01\\x00\\x00\\x60\\x01\\x00\\x00\\x81\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x62\\x01\\x00\\x00\\x5D\\x01\\x00\\x00\\x61\\x01\\x00\\x00\\x51\\x00\\x05\\x00\"\n\"\\x06\\x00\\x00\\x00\\x63\\x01\\x00\\x00\\x62\\x01\\x00\\x00\\x02\\x00\\x00\\x00\\x59\\x00\\x06\\x00\\x06\\x00\\x00\\x00\\x64\\x01\\x00\\x00\\x5C\\x01\\x00\\x00\"\n\"\\x62\\x01\\x00\\x00\\x63\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x65\\x01\\x00\\x00\\xDA\\x00\\x00\\x00\\x81\\x00\\x05\\x00\\x06\\x00\\x00\\x00\"\n\"\\x66\\x01\\x00\\x00\\x65\\x01\\x00\\x00\\x64\\x01\\x00\\x00\\x3E\\x00\\x03\\x00\\xDA\\x00\\x00\\x00\\x66\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\xE1\\x00\\x00\\x00\"\n\"\\x67\\x01\\x00\\x00\\xE3\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x68\\x01\\x00\\x00\\xD8\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\xE8\\x00\\x00\\x00\"\n\"\\x69\\x01\\x00\\x00\\xC4\\x00\\x00\\x00\\xE7\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x6A\\x01\\x00\\x00\\x69\\x01\\x00\\x00\\x4F\\x00\\x08\\x00\"\n\"\\x07\\x00\\x00\\x00\\x6B\\x01\\x00\\x00\\x6A\\x01\\x00\\x00\\x6A\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x85\\x00\\x05\\x00\"\n\"\\x07\\x00\\x00\\x00\\x6C\\x01\\x00\\x00\\xE6\\x00\\x00\\x00\\x6B\\x01\\x00\\x00\\x81\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x6D\\x01\\x00\\x00\\x68\\x01\\x00\\x00\"\n\"\\x6C\\x01\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x6E\\x01\\x00\\x00\\x6D\\x01\\x00\\x00\\x02\\x00\\x00\\x00\\x59\\x00\\x06\\x00\\x06\\x00\\x00\\x00\"\n\"\\x6F\\x01\\x00\\x00\\x67\\x01\\x00\\x00\\x6D\\x01\\x00\\x00\\x6E\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x70\\x01\\x00\\x00\\xDA\\x00\\x00\\x00\"\n\"\\x81\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x71\\x01\\x00\\x00\\x70\\x01\\x00\\x00\\x6F\\x01\\x00\\x00\\x3E\\x00\\x03\\x00\\xDA\\x00\\x00\\x00\\x71\\x01\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\xE1\\x00\\x00\\x00\\x72\\x01\\x00\\x00\\xE3\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x73\\x01\\x00\\x00\\xD8\\x00\\x00\\x00\"\n\"\\x41\\x00\\x05\\x00\\xE8\\x00\\x00\\x00\\x74\\x01\\x00\\x00\\xC4\\x00\\x00\\x00\\xE7\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x75\\x01\\x00\\x00\"\n\"\\x74\\x01\\x00\\x00\\x4F\\x00\\x08\\x00\\x07\\x00\\x00\\x00\\x76\\x01\\x00\\x00\\x75\\x01\\x00\\x00\\x75\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x77\\x01\\x00\\x00\\x1E\\x01\\x00\\x00\\x76\\x01\\x00\\x00\\x81\\x00\\x05\\x00\\x07\\x00\\x00\\x00\"\n\"\\x78\\x01\\x00\\x00\\x73\\x01\\x00\\x00\\x77\\x01\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x79\\x01\\x00\\x00\\x78\\x01\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x59\\x00\\x06\\x00\\x06\\x00\\x00\\x00\\x7A\\x01\\x00\\x00\\x72\\x01\\x00\\x00\\x78\\x01\\x00\\x00\\x79\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\"\n\"\\x7B\\x01\\x00\\x00\\xDA\\x00\\x00\\x00\\x81\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x7C\\x01\\x00\\x00\\x7B\\x01\\x00\\x00\\x7A\\x01\\x00\\x00\\x3E\\x00\\x03\\x00\"\n\"\\xDA\\x00\\x00\\x00\\x7C\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\xE1\\x00\\x00\\x00\\x7D\\x01\\x00\\x00\\xE3\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\"\n\"\\x7E\\x01\\x00\\x00\\xD8\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\xE8\\x00\\x00\\x00\\x80\\x01\\x00\\x00\\xC4\\x00\\x00\\x00\\xE7\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x16\\x00\\x00\\x00\\x81\\x01\\x00\\x00\\x80\\x01\\x00\\x00\\x4F\\x00\\x08\\x00\\x07\\x00\\x00\\x00\\x82\\x01\\x00\\x00\\x81\\x01\\x00\\x00\\x81\\x01\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x83\\x01\\x00\\x00\\x7F\\x01\\x00\\x00\\x82\\x01\\x00\\x00\"\n\"\\x81\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x84\\x01\\x00\\x00\\x7E\\x01\\x00\\x00\\x83\\x01\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x85\\x01\\x00\\x00\"\n\"\\x84\\x01\\x00\\x00\\x02\\x00\\x00\\x00\\x59\\x00\\x06\\x00\\x06\\x00\\x00\\x00\\x86\\x01\\x00\\x00\\x7D\\x01\\x00\\x00\\x84\\x01\\x00\\x00\\x85\\x01\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x87\\x01\\x00\\x00\\xDA\\x00\\x00\\x00\\x81\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x88\\x01\\x00\\x00\\x87\\x01\\x00\\x00\"\n\"\\x86\\x01\\x00\\x00\\x3E\\x00\\x03\\x00\\xDA\\x00\\x00\\x00\\x88\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\xE1\\x00\\x00\\x00\\x89\\x01\\x00\\x00\\xE3\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x8A\\x01\\x00\\x00\\xD8\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\xE8\\x00\\x00\\x00\\x8B\\x01\\x00\\x00\\xC4\\x00\\x00\\x00\"\n\"\\xE7\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x8C\\x01\\x00\\x00\\x8B\\x01\\x00\\x00\\x4F\\x00\\x08\\x00\\x07\\x00\\x00\\x00\\x8D\\x01\\x00\\x00\"\n\"\\x8C\\x01\\x00\\x00\\x8C\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x8E\\x01\\x00\\x00\"\n\"\\x2A\\x01\\x00\\x00\\x8D\\x01\\x00\\x00\\x81\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x8F\\x01\\x00\\x00\\x8A\\x01\\x00\\x00\\x8E\\x01\\x00\\x00\\x51\\x00\\x05\\x00\"\n\"\\x06\\x00\\x00\\x00\\x90\\x01\\x00\\x00\\x8F\\x01\\x00\\x00\\x02\\x00\\x00\\x00\\x59\\x00\\x06\\x00\\x06\\x00\\x00\\x00\\x91\\x01\\x00\\x00\\x89\\x01\\x00\\x00\"\n\"\\x8F\\x01\\x00\\x00\\x90\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x92\\x01\\x00\\x00\\xDA\\x00\\x00\\x00\\x81\\x00\\x05\\x00\\x06\\x00\\x00\\x00\"\n\"\\x93\\x01\\x00\\x00\\x92\\x01\\x00\\x00\\x91\\x01\\x00\\x00\\x3E\\x00\\x03\\x00\\xDA\\x00\\x00\\x00\\x93\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\xE1\\x00\\x00\\x00\"\n\"\\x94\\x01\\x00\\x00\\xE3\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x95\\x01\\x00\\x00\\xD8\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\xE8\\x00\\x00\\x00\"\n\"\\x97\\x01\\x00\\x00\\xC4\\x00\\x00\\x00\\xE7\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x98\\x01\\x00\\x00\\x97\\x01\\x00\\x00\\x4F\\x00\\x08\\x00\"\n\"\\x07\\x00\\x00\\x00\\x99\\x01\\x00\\x00\\x98\\x01\\x00\\x00\\x98\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x85\\x00\\x05\\x00\"\n\"\\x07\\x00\\x00\\x00\\x9A\\x01\\x00\\x00\\x96\\x01\\x00\\x00\\x99\\x01\\x00\\x00\\x81\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x9B\\x01\\x00\\x00\\x95\\x01\\x00\\x00\"\n\"\\x9A\\x01\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x9C\\x01\\x00\\x00\\x9B\\x01\\x00\\x00\\x02\\x00\\x00\\x00\\x59\\x00\\x06\\x00\\x06\\x00\\x00\\x00\"\n\"\\x9D\\x01\\x00\\x00\\x94\\x01\\x00\\x00\\x9B\\x01\\x00\\x00\\x9C\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x9E\\x01\\x00\\x00\\xDA\\x00\\x00\\x00\"\n\"\\x81\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x9F\\x01\\x00\\x00\\x9E\\x01\\x00\\x00\\x9D\\x01\\x00\\x00\\x3E\\x00\\x03\\x00\\xDA\\x00\\x00\\x00\\x9F\\x01\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\xA0\\x01\\x00\\x00\\xDA\\x00\\x00\\x00\\x88\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\xA2\\x01\\x00\\x00\\xA0\\x01\\x00\\x00\"\n\"\\xA1\\x01\\x00\\x00\\xFE\\x00\\x02\\x00\\xA2\\x01\\x00\\x00\\xF8\\x00\\x02\\x00\\xF8\\x00\\x00\\x00\\xFF\\x00\\x01\\x00\\xF8\\x00\\x02\\x00\\xDF\\x00\\x00\\x00\"\n\"\\xFF\\x00\\x01\\x00\\x38\\x00\\x01\\x00\\x36\\x00\\x05\\x00\\x16\\x00\\x00\\x00\\x1C\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x18\\x00\\x00\\x00\\x37\\x00\\x03\\x00\"\n\"\\x15\\x00\\x00\\x00\\x19\\x00\\x00\\x00\\x37\\x00\\x03\\x00\\x17\\x00\\x00\\x00\\x1A\\x00\\x00\\x00\\x37\\x00\\x03\\x00\\x08\\x00\\x00\\x00\\x1B\\x00\\x00\\x00\"\n\"\\xF8\\x00\\x02\\x00\\x1D\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x08\\x00\\x00\\x00\\xA8\\x01\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x08\\x00\\x00\\x00\"\n\"\\xB4\\x01\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x2C\\x00\\x00\\x00\\xC0\\x01\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x2C\\x00\\x00\\x00\"\n\"\\xD6\\x01\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x17\\x00\\x00\\x00\\xFB\\x01\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x17\\x00\\x00\\x00\"\n\"\\x02\\x02\\x00\\x00\\x07\\x00\\x00\\x00\\xF7\\x00\\x03\\x00\\xA7\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\\xA5\\x01\\x00\\x00\\xA6\\x01\\x00\\x00\"\n\"\\xF6\\x01\\x00\\x00\\xF8\\x00\\x02\\x00\\xA6\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\xA9\\x01\\x00\\x00\\x1B\\x00\\x00\\x00\\x0C\\x00\\x06\\x00\"\n\"\\x07\\x00\\x00\\x00\\xAA\\x01\\x00\\x00\\x01\\x00\\x00\\x00\\x45\\x00\\x00\\x00\\xA9\\x01\\x00\\x00\\x4F\\x00\\x07\\x00\\x16\\x00\\x00\\x00\\xAB\\x01\\x00\\x00\"\n\"\\xAA\\x01\\x00\\x00\\xAA\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x8E\\x00\\x05\\x00\\x16\\x00\\x00\\x00\\xAD\\x01\\x00\\x00\\xAB\\x01\\x00\\x00\"\n\"\\xAC\\x01\\x00\\x00\\x8E\\x00\\x05\\x00\\x16\\x00\\x00\\x00\\xAE\\x01\\x00\\x00\\xAD\\x01\\x00\\x00\\x69\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x16\\x00\\x00\\x00\"\n\"\\xB0\\x01\\x00\\x00\\xAE\\x01\\x00\\x00\\xAF\\x01\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\xB1\\x01\\x00\\x00\\xB0\\x01\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\xB2\\x01\\x00\\x00\\xB0\\x01\\x00\\x00\\x01\\x00\\x00\\x00\\x50\\x00\\x06\\x00\\x07\\x00\\x00\\x00\\xB3\\x01\\x00\\x00\"\n\"\\xB1\\x01\\x00\\x00\\xB2\\x01\\x00\\x00\\x87\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\xA8\\x01\\x00\\x00\\xB3\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x16\\x00\\x00\\x00\"\n\"\\xB5\\x01\\x00\\x00\\x1A\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\xB6\\x01\\x00\\x00\\xB5\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x51\\x00\\x05\\x00\"\n\"\\x06\\x00\\x00\\x00\\xB7\\x01\\x00\\x00\\xB5\\x01\\x00\\x00\\x01\\x00\\x00\\x00\\x50\\x00\\x05\\x00\\x16\\x00\\x00\\x00\\xB8\\x01\\x00\\x00\\xB6\\x01\\x00\\x00\"\n\"\\xB7\\x01\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\xB9\\x01\\x00\\x00\\xB8\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\"\n\"\\xBA\\x01\\x00\\x00\\xB8\\x01\\x00\\x00\\x01\\x00\\x00\\x00\\x50\\x00\\x06\\x00\\x07\\x00\\x00\\x00\\xBB\\x01\\x00\\x00\\xB9\\x01\\x00\\x00\\xBA\\x01\\x00\\x00\"\n\"\\x69\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\xB4\\x01\\x00\\x00\\xBB\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\xBD\\x01\\x00\\x00\\xA8\\x01\\x00\\x00\"\n\"\\x50\\x00\\x06\\x00\\x07\\x00\\x00\\x00\\xBE\\x01\\x00\\x00\\xBC\\x01\\x00\\x00\\xBC\\x01\\x00\\x00\\xBC\\x01\\x00\\x00\\x88\\x00\\x05\\x00\\x07\\x00\\x00\\x00\"\n\"\\xBF\\x01\\x00\\x00\\xBD\\x01\\x00\\x00\\xBE\\x01\\x00\\x00\\x3E\\x00\\x03\\x00\\xA8\\x01\\x00\\x00\\xBF\\x01\\x00\\x00\\x3E\\x00\\x03\\x00\\xC0\\x01\\x00\\x00\"\n\"\\x69\\x00\\x00\\x00\\xF9\\x00\\x02\\x00\\xC1\\x01\\x00\\x00\\xF8\\x00\\x02\\x00\\xC1\\x01\\x00\\x00\\xF6\\x00\\x04\\x00\\xC3\\x01\\x00\\x00\\xC4\\x01\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\xF9\\x00\\x02\\x00\\xC5\\x01\\x00\\x00\\xF8\\x00\\x02\\x00\\xC5\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\xC6\\x01\\x00\\x00\"\n\"\\xC0\\x01\\x00\\x00\\xB8\\x00\\x05\\x00\\x21\\x00\\x00\\x00\\xC7\\x01\\x00\\x00\\xC6\\x01\\x00\\x00\\xBC\\x01\\x00\\x00\\xFA\\x00\\x04\\x00\\xC7\\x01\\x00\\x00\"\n\"\\xC2\\x01\\x00\\x00\\xC3\\x01\\x00\\x00\\xF8\\x00\\x02\\x00\\xC2\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\xC8\\x01\\x00\\x00\\xA8\\x01\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x14\\x00\\x00\\x00\\xC9\\x01\\x00\\x00\\x19\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\xCA\\x01\\x00\\x00\\xB4\\x01\\x00\\x00\"\n\"\\x4F\\x00\\x07\\x00\\x16\\x00\\x00\\x00\\xCB\\x01\\x00\\x00\\xCA\\x01\\x00\\x00\\xCA\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x57\\x00\\x05\\x00\"\n\"\\x2E\\x00\\x00\\x00\\xCC\\x01\\x00\\x00\\xC9\\x01\\x00\\x00\\xCB\\x01\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\xCD\\x01\\x00\\x00\\xCC\\x01\\x00\\x00\"\n\"\\x03\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x2C\\x00\\x00\\x00\\xCE\\x01\\x00\\x00\\xB4\\x01\\x00\\x00\\x32\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\"\n\"\\xCF\\x01\\x00\\x00\\xCE\\x01\\x00\\x00\\x0C\\x00\\x07\\x00\\x06\\x00\\x00\\x00\\xD0\\x01\\x00\\x00\\x01\\x00\\x00\\x00\\x30\\x00\\x00\\x00\\xCD\\x01\\x00\\x00\"\n\"\\xCF\\x01\\x00\\x00\\x8E\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\xD1\\x01\\x00\\x00\\xC8\\x01\\x00\\x00\\xD0\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\"\n\"\\xD2\\x01\\x00\\x00\\xB4\\x01\\x00\\x00\\x81\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\xD3\\x01\\x00\\x00\\xD2\\x01\\x00\\x00\\xD1\\x01\\x00\\x00\\x3E\\x00\\x03\\x00\"\n\"\\xB4\\x01\\x00\\x00\\xD3\\x01\\x00\\x00\\xF9\\x00\\x02\\x00\\xC4\\x01\\x00\\x00\\xF8\\x00\\x02\\x00\\xC4\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\"\n\"\\xD4\\x01\\x00\\x00\\xC0\\x01\\x00\\x00\\x81\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\xD5\\x01\\x00\\x00\\xD4\\x01\\x00\\x00\\x69\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\"\n\"\\xC0\\x01\\x00\\x00\\xD5\\x01\\x00\\x00\\xF9\\x00\\x02\\x00\\xC1\\x01\\x00\\x00\\xF8\\x00\\x02\\x00\\xC3\\x01\\x00\\x00\\x3E\\x00\\x03\\x00\\xC0\\x01\\x00\\x00\"\n\"\\x57\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\xD6\\x01\\x00\\x00\\x69\\x00\\x00\\x00\\xF9\\x00\\x02\\x00\\xD7\\x01\\x00\\x00\\xF8\\x00\\x02\\x00\\xD7\\x01\\x00\\x00\"\n\"\\xF6\\x00\\x04\\x00\\xD9\\x01\\x00\\x00\\xDA\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\xF9\\x00\\x02\\x00\\xDB\\x01\\x00\\x00\\xF8\\x00\\x02\\x00\\xDB\\x01\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\xDC\\x01\\x00\\x00\\xC0\\x01\\x00\\x00\\xB8\\x00\\x05\\x00\\x21\\x00\\x00\\x00\\xDD\\x01\\x00\\x00\\xDC\\x01\\x00\\x00\"\n\"\\x35\\x01\\x00\\x00\\xFA\\x00\\x04\\x00\\xDD\\x01\\x00\\x00\\xD8\\x01\\x00\\x00\\xD9\\x01\\x00\\x00\\xF8\\x00\\x02\\x00\\xD8\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x07\\x00\\x00\\x00\\xDE\\x01\\x00\\x00\\xA8\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x14\\x00\\x00\\x00\\xDF\\x01\\x00\\x00\\x19\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x07\\x00\\x00\\x00\\xE0\\x01\\x00\\x00\\xB4\\x01\\x00\\x00\\x4F\\x00\\x07\\x00\\x16\\x00\\x00\\x00\\xE1\\x01\\x00\\x00\\xE0\\x01\\x00\\x00\\xE0\\x01\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x57\\x00\\x05\\x00\\x2E\\x00\\x00\\x00\\xE2\\x01\\x00\\x00\\xDF\\x01\\x00\\x00\\xE1\\x01\\x00\\x00\\x51\\x00\\x05\\x00\"\n\"\\x06\\x00\\x00\\x00\\xE3\\x01\\x00\\x00\\xE2\\x01\\x00\\x00\\x03\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x2C\\x00\\x00\\x00\\xE4\\x01\\x00\\x00\\xB4\\x01\\x00\\x00\"\n\"\\x32\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\xE5\\x01\\x00\\x00\\xE4\\x01\\x00\\x00\\x0C\\x00\\x07\\x00\\x06\\x00\\x00\\x00\\xE6\\x01\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x30\\x00\\x00\\x00\\xE3\\x01\\x00\\x00\\xE5\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\xE7\\x01\\x00\\x00\\xD6\\x01\\x00\\x00\"\n\"\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\xE8\\x01\\x00\\x00\\xE6\\x01\\x00\\x00\\xE7\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\xE9\\x01\\x00\\x00\"\n\"\\xD6\\x01\\x00\\x00\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\xEA\\x01\\x00\\x00\\x8A\\x00\\x00\\x00\\xE9\\x01\\x00\\x00\\x83\\x00\\x05\\x00\\x06\\x00\\x00\\x00\"\n\"\\xEB\\x01\\x00\\x00\\xE8\\x01\\x00\\x00\\xEA\\x01\\x00\\x00\\x8E\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\xEC\\x01\\x00\\x00\\xDE\\x01\\x00\\x00\\xEB\\x01\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\xED\\x01\\x00\\x00\\xB4\\x01\\x00\\x00\\x81\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\xEE\\x01\\x00\\x00\\xED\\x01\\x00\\x00\"\n\"\\xEC\\x01\\x00\\x00\\x3E\\x00\\x03\\x00\\xB4\\x01\\x00\\x00\\xEE\\x01\\x00\\x00\\xF9\\x00\\x02\\x00\\xDA\\x01\\x00\\x00\\xF8\\x00\\x02\\x00\\xDA\\x01\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\xEF\\x01\\x00\\x00\\xC0\\x01\\x00\\x00\\x81\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\xF0\\x01\\x00\\x00\\xEF\\x01\\x00\\x00\"\n\"\\x69\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\xC0\\x01\\x00\\x00\\xF0\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\xF1\\x01\\x00\\x00\\xD6\\x01\\x00\\x00\"\n\"\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\xF2\\x01\\x00\\x00\\xF1\\x01\\x00\\x00\\x8A\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\xD6\\x01\\x00\\x00\\xF2\\x01\\x00\\x00\"\n\"\\xF9\\x00\\x02\\x00\\xD7\\x01\\x00\\x00\\xF8\\x00\\x02\\x00\\xD9\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\xF3\\x01\\x00\\x00\\xB4\\x01\\x00\\x00\"\n\"\\x4F\\x00\\x07\\x00\\x16\\x00\\x00\\x00\\xF4\\x01\\x00\\x00\\xF3\\x01\\x00\\x00\\xF3\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\xFE\\x00\\x02\\x00\"\n\"\\xF4\\x01\\x00\\x00\\xF8\\x00\\x02\\x00\\xF6\\x01\\x00\\x00\\xBA\\x00\\x05\\x00\\x21\\x00\\x00\\x00\\xF8\\x01\\x00\\x00\\xF7\\x01\\x00\\x00\\x57\\x00\\x00\\x00\"\n\"\\xF7\\x00\\x03\\x00\\xFA\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\\xF8\\x01\\x00\\x00\\xF9\\x01\\x00\\x00\\xFA\\x01\\x00\\x00\\xF8\\x00\\x02\\x00\"\n\"\\xF9\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\xFC\\x01\\x00\\x00\\x1B\\x00\\x00\\x00\\x0C\\x00\\x06\\x00\\x07\\x00\\x00\\x00\\xFD\\x01\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x45\\x00\\x00\\x00\\xFC\\x01\\x00\\x00\\x4F\\x00\\x07\\x00\\x16\\x00\\x00\\x00\\xFE\\x01\\x00\\x00\\xFD\\x01\\x00\\x00\\xFD\\x01\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x8E\\x00\\x05\\x00\\x16\\x00\\x00\\x00\\xFF\\x01\\x00\\x00\\xFE\\x01\\x00\\x00\\xAC\\x01\\x00\\x00\\x8E\\x00\\x05\\x00\"\n\"\\x16\\x00\\x00\\x00\\x00\\x02\\x00\\x00\\xFF\\x01\\x00\\x00\\x69\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x16\\x00\\x00\\x00\\x01\\x02\\x00\\x00\\x00\\x02\\x00\\x00\"\n\"\\xAF\\x01\\x00\\x00\\x3E\\x00\\x03\\x00\\xFB\\x01\\x00\\x00\\x01\\x02\\x00\\x00\\x3D\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x03\\x02\\x00\\x00\\x1A\\x00\\x00\\x00\"\n\"\\x3E\\x00\\x03\\x00\\x02\\x02\\x00\\x00\\x03\\x02\\x00\\x00\\x3D\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x04\\x02\\x00\\x00\\xFB\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x16\\x00\\x00\\x00\\x05\\x02\\x00\\x00\\x02\\x02\\x00\\x00\\x81\\x00\\x05\\x00\\x16\\x00\\x00\\x00\\x06\\x02\\x00\\x00\\x05\\x02\\x00\\x00\\x04\\x02\\x00\\x00\"\n\"\\x3E\\x00\\x03\\x00\\x02\\x02\\x00\\x00\\x06\\x02\\x00\\x00\\x3D\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x08\\x02\\x00\\x00\\xFB\\x01\\x00\\x00\\x8E\\x00\\x05\\x00\"\n\"\\x16\\x00\\x00\\x00\\x09\\x02\\x00\\x00\\x08\\x02\\x00\\x00\\x07\\x02\\x00\\x00\\x3E\\x00\\x03\\x00\\xFB\\x01\\x00\\x00\\x09\\x02\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x16\\x00\\x00\\x00\\x0A\\x02\\x00\\x00\\xFB\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x14\\x00\\x00\\x00\\x0B\\x02\\x00\\x00\\x19\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x16\\x00\\x00\\x00\\x0C\\x02\\x00\\x00\\x02\\x02\\x00\\x00\\x57\\x00\\x05\\x00\\x2E\\x00\\x00\\x00\\x0D\\x02\\x00\\x00\\x0B\\x02\\x00\\x00\\x0C\\x02\\x00\\x00\"\n\"\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x0E\\x02\\x00\\x00\\x0D\\x02\\x00\\x00\\x03\\x00\\x00\\x00\\x8E\\x00\\x05\\x00\\x16\\x00\\x00\\x00\\x0F\\x02\\x00\\x00\"\n\"\\x0A\\x02\\x00\\x00\\x0E\\x02\\x00\\x00\\x3D\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x10\\x02\\x00\\x00\\x02\\x02\\x00\\x00\\x83\\x00\\x05\\x00\\x16\\x00\\x00\\x00\"\n\"\\x11\\x02\\x00\\x00\\x10\\x02\\x00\\x00\\x0F\\x02\\x00\\x00\\x3E\\x00\\x03\\x00\\x02\\x02\\x00\\x00\\x11\\x02\\x00\\x00\\x3D\\x00\\x04\\x00\\x16\\x00\\x00\\x00\"\n\"\\x12\\x02\\x00\\x00\\xFB\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x14\\x00\\x00\\x00\\x13\\x02\\x00\\x00\\x19\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x16\\x00\\x00\\x00\"\n\"\\x14\\x02\\x00\\x00\\x02\\x02\\x00\\x00\\x57\\x00\\x05\\x00\\x2E\\x00\\x00\\x00\\x15\\x02\\x00\\x00\\x13\\x02\\x00\\x00\\x14\\x02\\x00\\x00\\x51\\x00\\x05\\x00\"\n\"\\x06\\x00\\x00\\x00\\x16\\x02\\x00\\x00\\x15\\x02\\x00\\x00\\x03\\x00\\x00\\x00\\x8E\\x00\\x05\\x00\\x16\\x00\\x00\\x00\\x17\\x02\\x00\\x00\\x12\\x02\\x00\\x00\"\n\"\\x16\\x02\\x00\\x00\\x3D\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x18\\x02\\x00\\x00\\x02\\x02\\x00\\x00\\x83\\x00\\x05\\x00\\x16\\x00\\x00\\x00\\x19\\x02\\x00\\x00\"\n\"\\x18\\x02\\x00\\x00\\x17\\x02\\x00\\x00\\x3E\\x00\\x03\\x00\\x02\\x02\\x00\\x00\\x19\\x02\\x00\\x00\\x3D\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x1A\\x02\\x00\\x00\"\n\"\\xFB\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x14\\x00\\x00\\x00\\x1B\\x02\\x00\\x00\\x19\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x1C\\x02\\x00\\x00\"\n\"\\x02\\x02\\x00\\x00\\x57\\x00\\x05\\x00\\x2E\\x00\\x00\\x00\\x1D\\x02\\x00\\x00\\x1B\\x02\\x00\\x00\\x1C\\x02\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\"\n\"\\x1E\\x02\\x00\\x00\\x1D\\x02\\x00\\x00\\x03\\x00\\x00\\x00\\x8E\\x00\\x05\\x00\\x16\\x00\\x00\\x00\\x1F\\x02\\x00\\x00\\x1A\\x02\\x00\\x00\\x1E\\x02\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x20\\x02\\x00\\x00\\x02\\x02\\x00\\x00\\x83\\x00\\x05\\x00\\x16\\x00\\x00\\x00\\x21\\x02\\x00\\x00\\x20\\x02\\x00\\x00\"\n\"\\x1F\\x02\\x00\\x00\\x3E\\x00\\x03\\x00\\x02\\x02\\x00\\x00\\x21\\x02\\x00\\x00\\x3D\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x22\\x02\\x00\\x00\\x02\\x02\\x00\\x00\"\n\"\\xFE\\x00\\x02\\x00\\x22\\x02\\x00\\x00\\xF8\\x00\\x02\\x00\\xFA\\x01\\x00\\x00\\xF9\\x00\\x02\\x00\\xA7\\x01\\x00\\x00\\xF8\\x00\\x02\\x00\\xA7\\x01\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x24\\x02\\x00\\x00\\x1A\\x00\\x00\\x00\\xFE\\x00\\x02\\x00\\x24\\x02\\x00\\x00\\x38\\x00\\x01\\x00\"},\n#endif\n#ifdef D3D9QUAKE\n{QR_DIRECT3D9, 9, \"rtlight\",\n\"!!permu BUMP\\n\"\n\"!!permu SPECULAR\\n\"\n\"!!permu OFFSETMAPPING\\n\"\n\"!!permu SKELETAL\\n\"\n\"!!permu FOG\\n\"\n\"!!samps diffuse\\n\"\n\n//\ttexture units:\n//\ts0=diffuse, s1=normal, s2=specular, s3=shadowmap\n//\tcustom modifiers:\n//\tPCF(shadowmap)\n//\tCUBE(projected cubemap)\n\n\n\n\"struct a2v\\n\"\n\"{\\n\"\n\"float4 pos: POSITION;\\n\"\n\"float3 tc: TEXCOORD0;\\n\"\n\"float3 n: NORMAL0;\\n\"\n\"float3 s: TANGENT0;\\n\"\n\"float3 t: BINORMAL0;\\n\"\n\"};\\n\"\n\"struct v2f\\n\"\n\"{\\n\"\n\"#ifndef FRAGMENT_SHADER\\n\"\n\"float4 pos: POSITION;\\n\"\n\"#endif\\n\"\n\"float3 tc: TEXCOORD0;\\n\"\n\"float3 lpos: TEXCOORD1;\\n\"\n\"};\\n\"\n\n\"#ifdef VERTEX_SHADER\\n\"\n\"float4x4  m_modelviewprojection;\\n\"\n\"float3 l_lightposition;\\n\"\n\"v2f main (a2v inp)\\n\"\n\"{\\n\"\n\"v2f outp;\\n\"\n\"outp.pos = mul(m_modelviewprojection, inp.pos);\\n\"\n\"outp.tc = inp.tc;\\n\"\n\n\"float3 lightminusvertex = l_lightposition - inp.pos.xyz;\\n\"\n\"outp.lpos.x = dot(lightminusvertex, inp.s.xyz);\\n\"\n\"outp.lpos.y = dot(lightminusvertex, inp.t.xyz);\\n\"\n\"outp.lpos.z = dot(lightminusvertex, inp.n.xyz);\\n\"\n\"return outp;\\n\"\n\"}\\n\"\n\"#endif\\n\"\n\n\"#ifdef FRAGMENT_SHADER\\n\"\n\"sampler s_diffuse;\\n\"\n\"float l_lightradius;\\n\"\n\"float3 l_lightcolour;\\n\"\n\"float4 main (v2f inp) : COLOR0\\n\"\n\"{\\n\"\n\"float3 col = l_lightcolour;\\n\"\n\"col *= max(1.0 - dot(inp.lpos, inp.lpos)/(l_lightradius*l_lightradius), 0.0);\\n\"\n\"#ifdef FLAT\\n\"\n\"float3 diff = FLAT;\\n\"\n\"#else\\n\"\n\"float3 diff = tex2D(s_diffuse, inp.tc);\\n\"\n\"#endif\\n\"\n\"return float4(diff * col, 1);\\n\"\n\"}\\n\"\n\"#endif\\n\"\n},\n#endif\n#ifdef D3D11QUAKE\n{QR_DIRECT3D11, 11, \"rtlight\",\n\"!!samps diffuse normalmap specular upper lower shadowmap projectionmap\\n\"\n\"!!permu BUMP\\n\"\n\"!!permu FRAMEBLEND\\n\"\n\"!!permu SKELETAL\\n\"\n\"!!permu UPPERLOWER\\n\"\n\"!!permu FOG\\n\"\n\"!!cvarf r_glsl_offsetmapping_scale\\n\"\n\"!!cvardf r_glsl_pcf=5\\n\"\n\n\n//this is the main shader responsible for realtime dlights.\n\n//texture units:\n//s0=diffuse, s1=normal, s2=specular, s3=shadowmap\n//custom modifiers:\n//PCF(shadowmap)\n//CUBEPROJ(projected cubemap)\n//SPOT(projected circle\n//CUBESHADOW\n\n\"#undef CUBE //engine cannot load cubemaps properly with d3d yet.\\n\"\n\n\"#ifndef r_glsl_pcf\\n\"\n\"#error r_glsl_pcf wasn't defined\\n\"\n\"#endif\\n\"\n\"#if r_glsl_pcf < 1\\n\"\n\"#undef r_glsl_pcf\\n\"\n\"#define r_glsl_pcf 9\\n\"\n\"#endif\\n\"\n\n\"#ifdef UPPERLOWER\\n\"\n\"#define UPPER\\n\"\n\"#define LOWER\\n\"\n\"#endif\\n\"\n\n\n\"struct a2v\\n\"\n\"{\\n\"\n\"float4 pos: POSITION;\\n\"\n\"float2 tc: TEXCOORD0;\\n\"\n\"float3 n: NORMAL;\\n\"\n\"float3 s: TANGENT;\\n\"\n\"float3 t: BINORMAL;\\n\"\n\"};\\n\"\n\"struct v2f\\n\"\n\"{\\n\"\n\"float4 pos: SV_POSITION;\\n\"\n\"float2 tc: TEXCOORD0;\\n\"\n\"float3 lightvector: TEXCOORD1;\\n\"\n\"float3 eyevector: TEXCOORD2;\\n\"\n\"float3 vtexprojcoord: TEXCOORD3;\\n\"\n\"};\\n\"\n\n\"#include <ftedefs.h>\\n\"\n\n\"#ifdef VERTEX_SHADER\\n\"\n\"v2f main (a2v inp)\\n\"\n\"{\\n\"\n\"v2f outp;\\n\"\n\"float4 wpos;\\n\"\n\"wpos = mul(m_model, inp.pos);\\n\"\n\"outp.pos = mul(m_view, wpos);\\n\"\n\"outp.pos = mul(m_projection, outp.pos);\\n\"\n\"outp.tc = inp.tc.xy;\\n\"\n\"float3 lightminusvertex = l_lightposition - wpos.xyz;\\n\"\n\"outp.lightvector.x = -dot(lightminusvertex, inp.s.xyz);\\n\"\n\"outp.lightvector.y = dot(lightminusvertex, inp.t.xyz);\\n\"\n\"outp.lightvector.z = dot(lightminusvertex, inp.n.xyz);\\n\"\n\"float3 eyeminusvertex = e_eyepos - wpos.xyz;\\n\"\n\"outp.eyevector.x = -dot(eyeminusvertex, inp.s.xyz);\\n\"\n\"outp.eyevector.y = dot(eyeminusvertex, inp.t.xyz);\\n\"\n\"outp.eyevector.z = dot(eyeminusvertex, inp.n.xyz);\\n\"\n\"outp.vtexprojcoord = mul(l_cubematrix, wpos).xyz;\\n\"\n\"return outp;\\n\"\n\"}\\n\"\n\"#endif\\n\"\n\n\n\"#ifdef FRAGMENT_SHADER\\n\"\n\n\"Texture2D t_shadowmap : register(t0);\\n\"\n\"TextureCube t_projectionmap : register(t1);\\n\"\n\"Texture2D t_diffuse : register(t2);\\n\"\n\"Texture2D t_normalmap : register(t3);\\n\"\n\"Texture2D t_specular : register(t4);\\n\"\n\"Texture2D t_upper : register(t5);\\n\"\n\"Texture2D t_lower : register(t6);\\n\"\n\n\"SamplerComparisonState s_shadowmap : register(s0);\\n\"\n\"SamplerState s_projectionmap  : register(s1);\\n\"\n\"SamplerState s_diffuse : register(s2);\\n\"\n\"SamplerState s_normalmap : register(s3);\\n\"\n\"SamplerState s_specular : register(s4);\\n\"\n\"SamplerState s_upper : register(s5);\\n\"\n\"SamplerState s_lower : register(s6);\\n\"\n\n\n\"#ifdef PCF\\n\"\n\"float3 ShadowmapCoord(float3 vtexprojcoord)\\n\"\n\"{\\n\"\n\"#ifdef SPOT\\n\"\n//bias it. don't bother figuring out which side or anything, its not needed\n//l_projmatrix contains the light's projection matrix so no other magic needed\n\"vtexprojcoord.z -= 0.015;\\n\"\n\"return (vtexprojcoord.xyz + float3(1.0, 1.0, 1.0)) * float3(0.5, 0.5, 0.5);\\n\"\n//#elif defined(CUBESHADOW)\n//\tvec3 shadowcoord = vshadowcoord.xyz / vshadowcoord.w;\n//\t#define dosamp(x,y) shadowCube(s_t4, shadowcoord + vec2(x,y)*texscale.xy).r\n\"#else\\n\"\n//figure out which axis to use\n//texture is arranged thusly:\n//forward left  up\n//back    right down\n\"float3 dir = abs(vtexprojcoord.xyz);\\n\"\n//assume z is the major axis (ie: forward from the light)\n\"float3 t = vtexprojcoord.xyz;\\n\"\n\"float ma = dir.z;\\n\"\n\"float3 axis = float3(0.5/3.0, 0.5/2.0, 0.5);\\n\"\n\"if (dir.x > ma)\\n\"\n\"{\\n\"\n\"ma = dir.x;\\n\"\n\"t = vtexprojcoord.zyx;\\n\"\n\"axis.x = 0.5;\\n\"\n\"}\\n\"\n\"if (dir.y > ma)\\n\"\n\"{\\n\"\n\"ma = dir.y;\\n\"\n\"t = vtexprojcoord.xzy;\\n\"\n\"axis.x = 2.5/3.0;\\n\"\n\"}\\n\"\n//if the axis is negative, flip it.\n\"if (t.z > 0.0)\\n\"\n\"{\\n\"\n\"axis.y = 1.5/2.0;\\n\"\n\"t.z = -t.z;\\n\"\n\"}\\n\"\n\n//we also need to pass the result through the light's projection matrix too\n//the 'matrix' we need only contains 5 actual values. and one of them is a -1. So we might as well just use a vec4.\n//note: the projection matrix also includes scalers to pinch the image inwards to avoid sampling over borders, as well as to cope with non-square source image\n//the resulting z is prescaled to result in a value between -0.5 and 0.5.\n//also make sure we're in the right quadrant type thing\n\"return axis + ((l_shadowmapproj.xyz*t.xyz + float3(0.0, 0.0, l_shadowmapproj.w)) / -t.z);\\n\"\n\"#endif\\n\"\n\"}\\n\"\n\n\"float ShadowmapFilter(float3 vtexprojcoord)\\n\"\n\"{\\n\"\n\"float3 shadowcoord = ShadowmapCoord(vtexprojcoord);\\n\"\n\n//\t#define dosamp(x,y) shadow2D(s_t4, shadowcoord.xyz + (vec3(x,y,0.0)*l_shadowmapscale.xyx)).r\n\n//\t#define dosamp(x,y) (t_shadowmap.Sample(s_shadowmap, shadowcoord.xy + (float2(x,y)*l_shadowmapscale.xy)).r < shadowcoord.z)\n\"#define dosamp(x,y) (t_shadowmap.SampleCmpLevelZero(s_shadowmap, shadowcoord.xy+(float2(x,y)*l_shadowmapscale.xy), shadowcoord.z))\\n\"\n\n\"float s = 0.0;\\n\"\n\"#if r_glsl_pcf >= 1 && r_glsl_pcf < 5\\n\"\n\"s += dosamp(0.0, 0.0);\\n\"\n\"return s;\\n\"\n\"#elif r_glsl_pcf >= 5 && r_glsl_pcf < 9\\n\"\n\"s += dosamp(-1.0, 0.0);\\n\"\n\"s += dosamp(0.0, -1.0);\\n\"\n\"s += dosamp(0.0, 0.0);\\n\"\n\"s += dosamp(0.0, 1.0);\\n\"\n\"s += dosamp(1.0, 0.0);\\n\"\n\"return s * (1.0/5.0);\\n\"\n\"#else\\n\"\n\"s += dosamp(-1.0, -1.0);\\n\"\n\"s += dosamp(-1.0, 0.0);\\n\"\n\"s += dosamp(-1.0, 1.0);\\n\"\n\"s += dosamp(0.0, -1.0);\\n\"\n\"s += dosamp(0.0, 0.0);\\n\"\n\"s += dosamp(0.0, 1.0);\\n\"\n\"s += dosamp(1.0, -1.0);\\n\"\n\"s += dosamp(1.0, 0.0);\\n\"\n\"s += dosamp(1.0, 1.0);\\n\"\n\"return s * (1.0/9.0);\\n\"\n\"#endif\\n\"\n\"}\\n\"\n\"#endif\\n\"\n\n\n\"float4 main (v2f inp) : SV_TARGET\\n\"\n\"{\\n\"\n\"float2 tc = inp.tc; //TODO: offsetmapping.\\n\"\n\n\"float4 base = t_diffuse.Sample(s_diffuse, tc);\\n\"\n\"#ifdef BUMP\\n\"\n\"float4 bump = t_normalmap.Sample(s_normalmap, tc);\\n\"\n\"bump.rgb = normalize(bump.rgb - 0.5);\\n\"\n\"#else\\n\"\n\"float4 bump = float4(0, 0, 1, 0);\\n\"\n\"#endif\\n\"\n\"float4 spec = t_specular.Sample(s_specular, tc);\\n\"\n\"#ifdef CUBE\\n\"\n\"float4 cubemap = t_projectionmap.Sample(s_projectionmap, inp.vtexprojcoord);\\n\"\n\"#endif\\n\"\n\"#ifdef LOWER\\n\"\n\"float4 lower = t_lower.Sample(s_lower, tc);\\n\"\n\"base += lower;\\n\"\n\"#endif\\n\"\n\"#ifdef UPPER\\n\"\n\"float4 upper = t_upper.Sample(s_upper, tc);\\n\"\n\"base += upper;\\n\"\n\"#endif\\n\"\n\n\"float lightscale = max(1.0 - (dot(inp.lightvector,inp.lightvector)/(l_lightradius*l_lightradius)), 0.0);\\n\"\n\"float3 nl = normalize(inp.lightvector);\\n\"\n\"float bumpscale = max(dot(bump.xyz, nl), 0.0);\\n\"\n\"float3 halfdir = normalize(normalize(inp.eyevector) + nl);\\n\"\n\"float specscale = pow(max(dot(halfdir, bump.rgb), 0.0), 32.0 * spec.a);\\n\"\n\n\"float4 result;\\n\"\n\"result.a    = base.a;\\n\"\n\"result.rgb  = base.rgb * (l_lightcolourscale.x + l_lightcolourscale.y * bumpscale); //amient light + diffuse\\n\"\n\"result.rgb += spec.rgb * l_lightcolourscale.z * specscale; //specular\\n\"\n\n\"result.rgb *= lightscale * l_colour; //fade light by distance and light colour.\\n\"\n\n\"#ifdef CUBE\\n\"\n\"result.rgb *= cubemap.rgb; //fade by cubemap\\n\"\n\"#endif\\n\"\n\n\"#ifdef PCF\\n\"\n\"result.rgb *= ShadowmapFilter(inp.vtexprojcoord);\\n\"\n\"#endif\\n\"\n\n//TODO: fog\n\"return result;\\n\"\n\"}\\n\"\n\"#endif\\n\"\n},\n#endif\n#ifdef VKQUAKE\n{QR_VULKAN, -1, \"rq_rtlight\",\n\"\\xFF\\x53\\x50\\x56\\x01\\x00\\x00\\x00\\x7F\\x06\\x00\\x00\\x00\\x00\\x00\\x00\\x2D\\x00\\x00\\x80\\x2C\\x00\\x00\\x00\\xB5\\x00\\x00\\x00\\xE4\\x00\\x00\\x00\"\n\"\\x38\\x17\\x00\\x00\\x1C\\x18\\x00\\x00\\x08\\x32\\x00\\x00\\x01\\x00\\x66\\x31\\x72\\x5F\\x67\\x6C\\x73\\x6C\\x5F\\x6F\\x66\\x66\\x73\\x65\\x74\\x6D\\x61\\x70\"\n\"\\x70\\x69\\x6E\\x67\\x00\\x00\\x00\\x00\\x00\\x01\\x01\\x66\\x31\\x67\\x6C\\x5F\\x73\\x70\\x65\\x63\\x75\\x6C\\x61\\x72\\x00\\x00\\x00\\x00\\x00\\x01\\x02\\x66\"\n\"\\x31\\x72\\x5F\\x67\\x6C\\x73\\x6C\\x5F\\x6F\\x66\\x66\\x73\\x65\\x74\\x6D\\x61\\x70\\x70\\x69\\x6E\\x67\\x5F\\x73\\x63\\x61\\x6C\\x65\\x00\\x3D\\x23\\xD7\\x0A\"\n\"\\x01\\x03\\x69\\x31\\x72\\x5F\\x67\\x6C\\x73\\x6C\\x5F\\x70\\x63\\x66\\x00\\x00\\x00\\x00\\x05\\x01\\x04\\x42\\x31\\x70\\x63\\x66\\x00\\x00\\x00\\x00\\x00\\x01\"\n\"\\x05\\x42\\x31\\x73\\x70\\x6F\\x74\\x00\\x00\\x00\\x00\\x00\\x01\\x06\\x42\\x31\\x63\\x75\\x62\\x65\\x00\\x00\\x00\\x00\\x00\\x01\\x07\\x62\\x31\\x72\\x5F\\x66\"\n\"\\x6F\\x67\\x5F\\x65\\x78\\x70\\x32\\x00\\x00\\x00\\x00\\x00\\x01\\x08\\x62\\x31\\x72\\x5F\\x66\\x6F\\x67\\x5F\\x6C\\x69\\x6E\\x65\\x61\\x72\\x00\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x03\\x02\\x23\\x07\\x00\\x00\\x01\\x00\\x0A\\x00\\x08\\x00\\xE5\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x11\\x00\\x02\\x00\\x01\\x00\\x00\\x00\"\n\"\\x0B\\x00\\x06\\x00\\x01\\x00\\x00\\x00\\x47\\x4C\\x53\\x4C\\x2E\\x73\\x74\\x64\\x2E\\x34\\x35\\x30\\x00\\x00\\x00\\x00\\x0E\\x00\\x03\\x00\\x00\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x0F\\x00\\x13\\x00\\x00\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x6D\\x61\\x69\\x6E\\x00\\x00\\x00\\x00\\x24\\x00\\x00\\x00\\x40\\x00\\x00\\x00\"\n\"\\x42\\x00\\x00\\x00\\x44\\x00\\x00\\x00\\x4C\\x00\\x00\\x00\\x5D\\x00\\x00\\x00\\x5F\\x00\\x00\\x00\\x79\\x00\\x00\\x00\\x9B\\x00\\x00\\x00\\xAD\\x00\\x00\\x00\"\n\"\\xBE\\x00\\x00\\x00\\xCA\\x00\\x00\\x00\\xE3\\x00\\x00\\x00\\xE4\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x19\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x10\\x00\\x00\\x00\"\n\"\\x48\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x1B\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x1B\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x1B\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x1B\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x05\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x1B\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x1B\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x1B\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xC0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x1B\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xCC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x1B\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\xD0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x1B\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xDC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x1B\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xE0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x1B\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xEC\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x1B\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xF0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x1B\\x00\\x00\\x00\\x0A\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\xFC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x1B\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x1B\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x1B\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x4C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x1B\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x50\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x1B\\x00\\x00\\x00\"\n\"\\x0F\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x5C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x1B\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x60\\x01\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x1B\\x00\\x00\\x00\\x11\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x6C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x1B\\x00\\x00\\x00\\x12\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x70\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x1B\\x00\\x00\\x00\\x13\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x01\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x1B\\x00\\x00\\x00\\x14\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x90\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x1B\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x94\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x1B\\x00\\x00\\x00\\x16\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x98\\x01\\x00\\x00\\x47\\x00\\x03\\x00\\x1B\\x00\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x1D\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x1D\\x00\\x00\\x00\\x21\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x24\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x40\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\"\n\"\\x04\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x42\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x44\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x4A\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x03\\x00\\x4A\\x00\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x5D\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x5F\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x65\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x65\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x65\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x65\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x65\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x4C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x65\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x50\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x65\\x00\\x00\\x00\"\n\"\\x04\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x5C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x65\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x60\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x65\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x6C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x65\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x70\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x65\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x65\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x88\\x00\\x00\\x00\\x47\\x00\\x03\\x00\\x65\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\"\n\"\\x67\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x67\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x47\\x00\\x04\\x00\"\n\"\\x79\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x8A\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x01\\x01\\x00\\x00\\x47\\x00\\x04\\x00\"\n\"\\x8D\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\x90\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x13\\x00\\x00\\x00\\x47\\x00\\x04\\x00\"\n\"\\x9B\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\xAD\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x47\\x00\\x04\\x00\"\n\"\\xB4\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x04\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\xB6\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x05\\x01\\x00\\x00\\x47\\x00\\x04\\x00\"\n\"\\xB9\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x06\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\xBE\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x47\\x00\\x04\\x00\"\n\"\\xCA\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\xDB\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x02\\x01\\x00\\x00\\x47\\x00\\x04\\x00\"\n\"\\xDC\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x03\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\xDD\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x07\\x01\\x00\\x00\\x47\\x00\\x04\\x00\"\n\"\\xDE\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x08\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\xDF\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x47\\x00\\x04\\x00\"\n\"\\xE0\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\xE1\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x47\\x00\\x04\\x00\"\n\"\\xE3\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\xE4\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x13\\x00\\x02\\x00\"\n\"\\x02\\x00\\x00\\x00\\x21\\x00\\x03\\x00\\x03\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x16\\x00\\x03\\x00\\x06\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x17\\x00\\x04\\x00\"\n\"\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x21\\x00\\x03\\x00\\x08\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x17\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x0C\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x21\\x00\\x07\\x00\\x0D\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x0C\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x14\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x18\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x15\\x00\\x04\\x00\\x17\\x00\\x00\\x00\\x20\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x17\\x00\\x00\\x00\\x18\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x1C\\x00\\x04\\x00\\x19\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x18\\x00\\x00\\x00\\x17\\x00\\x04\\x00\\x1A\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x1E\\x00\\x19\\x00\\x1B\\x00\\x00\\x00\\x16\\x00\\x00\\x00\"\n\"\\x16\\x00\\x00\\x00\\x16\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x0B\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x19\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x1A\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x1C\\x00\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\x1B\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x1C\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x15\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\"\n\"\\x20\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\\x1F\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x20\\x00\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\x16\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x23\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x23\\x00\\x00\\x00\"\n\"\\x24\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x26\\x00\\x00\\x00\\x00\\x00\\x80\\x3F\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\"\n\"\\x2C\\x00\\x00\\x00\\x00\\x00\\x80\\xBF\\x2B\\x00\\x04\\x00\\x17\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x2E\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x17\\x00\\x00\\x00\\x33\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x17\\x00\\x00\\x00\"\n\"\\x36\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x3A\\x00\\x00\\x00\\x00\\x00\\x00\\x40\\x3B\\x00\\x04\\x00\\x23\\x00\\x00\\x00\"\n\"\\x40\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x23\\x00\\x00\\x00\\x42\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x23\\x00\\x00\\x00\"\n\"\\x44\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x1E\\x00\\x03\\x00\\x4A\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x4B\\x00\\x00\\x00\\x03\\x00\\x00\\x00\"\n\"\\x4A\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x4B\\x00\\x00\\x00\\x4C\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x5A\\x00\\x00\\x00\\x03\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x5C\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x1A\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x5C\\x00\\x00\\x00\\x5D\\x00\\x00\\x00\"\n\"\\x03\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x5E\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x1A\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x5E\\x00\\x00\\x00\\x5F\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\\x62\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x1E\\x00\\x0C\\x00\\x65\\x00\\x00\\x00\\x16\\x00\\x00\\x00\"\n\"\\x0B\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x1A\\x00\\x00\\x00\"\n\"\\x1A\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x66\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x65\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x66\\x00\\x00\\x00\\x67\\x00\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\\x68\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x69\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x0B\\x00\\x00\\x00\\x14\\x00\\x02\\x00\\x74\\x00\\x00\\x00\\x29\\x00\\x03\\x00\\x74\\x00\\x00\\x00\\x75\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x78\\x00\\x00\\x00\"\n\"\\x03\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x78\\x00\\x00\\x00\\x79\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x17\\x00\\x00\\x00\"\n\"\\x7D\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x7E\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x32\\x00\\x04\\x00\\x06\\x00\\x00\\x00\"\n\"\\x8A\\x00\\x00\\x00\\x00\\x80\\x80\\x43\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x8B\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x32\\x00\\x04\\x00\\x06\\x00\\x00\\x00\"\n\"\\x8D\\x00\\x00\\x00\\x00\\x00\\x80\\x43\\x32\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\\x90\\x00\\x00\\x00\\x13\\x00\\x00\\x00\\x34\\x00\\x06\\x00\\x74\\x00\\x00\\x00\"\n\"\\x91\\x00\\x00\\x00\\xAB\\x00\\x00\\x00\\x90\\x00\\x00\\x00\\x1F\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\\x96\\x00\\x00\\x00\\x03\\x00\\x00\\x00\"\n\"\\x3B\\x00\\x04\\x00\\x78\\x00\\x00\\x00\\x9B\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x34\\x00\\x06\\x00\\x74\\x00\\x00\\x00\\xA8\\x00\\x00\\x00\\xAB\\x00\\x00\\x00\"\n\"\\x90\\x00\\x00\\x00\\x1F\\x00\\x00\\x00\\x18\\x00\\x04\\x00\\xAB\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\xAC\\x00\\x00\\x00\"\n\"\\x03\\x00\\x00\\x00\\xAB\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\xAC\\x00\\x00\\x00\\xAD\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x32\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\"\n\"\\xB4\\x00\\x00\\x00\\x04\\x01\\x00\\x00\\x34\\x00\\x06\\x00\\x74\\x00\\x00\\x00\\xB5\\x00\\x00\\x00\\xAB\\x00\\x00\\x00\\xB4\\x00\\x00\\x00\\x1F\\x00\\x00\\x00\"\n\"\\x32\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\\xB6\\x00\\x00\\x00\\x05\\x01\\x00\\x00\\x34\\x00\\x06\\x00\\x74\\x00\\x00\\x00\\xB7\\x00\\x00\\x00\\xAB\\x00\\x00\\x00\"\n\"\\xB6\\x00\\x00\\x00\\x1F\\x00\\x00\\x00\\x34\\x00\\x06\\x00\\x74\\x00\\x00\\x00\\xB8\\x00\\x00\\x00\\xA6\\x00\\x00\\x00\\xB5\\x00\\x00\\x00\\xB7\\x00\\x00\\x00\"\n\"\\x32\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\\xB9\\x00\\x00\\x00\\x06\\x01\\x00\\x00\\x34\\x00\\x06\\x00\\x74\\x00\\x00\\x00\\xBA\\x00\\x00\\x00\\xAB\\x00\\x00\\x00\"\n\"\\xB9\\x00\\x00\\x00\\x1F\\x00\\x00\\x00\\x34\\x00\\x06\\x00\\x74\\x00\\x00\\x00\\xBB\\x00\\x00\\x00\\xA6\\x00\\x00\\x00\\xB8\\x00\\x00\\x00\\xBA\\x00\\x00\\x00\"\n\"\\x3B\\x00\\x04\\x00\\x5A\\x00\\x00\\x00\\xBE\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x78\\x00\\x00\\x00\\xCA\\x00\\x00\\x00\\x03\\x00\\x00\\x00\"\n\"\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\xCF\\x00\\x00\\x00\\xCD\\xCC\\xCC\\x3D\\x32\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\xDB\\x00\\x00\\x00\\x00\\x00\\x81\\x43\"\n\"\\x32\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\\xDC\\x00\\x00\\x00\\x03\\x01\\x00\\x00\\x32\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\\xDD\\x00\\x00\\x00\\x07\\x01\\x00\\x00\"\n\"\\x32\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\\xDE\\x00\\x00\\x00\\x08\\x01\\x00\\x00\\x32\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\\xDF\\x00\\x00\\x00\\x10\\x00\\x00\\x00\"\n\"\\x32\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\\xE0\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x32\\x00\\x04\\x00\\x1E\\x00\\x00\\x00\\xE1\\x00\\x00\\x00\\x15\\x00\\x00\\x00\"\n\"\\x20\\x00\\x04\\x00\\xE2\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\xE2\\x00\\x00\\x00\\xE3\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x3B\\x00\\x04\\x00\\x5E\\x00\\x00\\x00\\xE4\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x36\\x00\\x05\\x00\\x02\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x03\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x05\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x0C\\x00\\x00\\x00\\x4D\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\"\n\"\\x0C\\x00\\x00\\x00\\x4E\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x0C\\x00\\x00\\x00\\x4F\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\"\n\"\\x0C\\x00\\x00\\x00\\x50\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x0C\\x00\\x00\\x00\\x51\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\"\n\"\\x0C\\x00\\x00\\x00\\x52\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x0C\\x00\\x00\\x00\\x53\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\"\n\"\\x0C\\x00\\x00\\x00\\x54\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x0C\\x00\\x00\\x00\\x61\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\"\n\"\\x0C\\x00\\x00\\x00\\x95\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x39\\x00\\x08\\x00\\x07\\x00\\x00\\x00\\x55\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x51\\x00\\x00\\x00\"\n\"\\x52\\x00\\x00\\x00\\x53\\x00\\x00\\x00\\x54\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\x56\\x00\\x00\\x00\\x51\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\"\n\"\\x4D\\x00\\x00\\x00\\x56\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\x57\\x00\\x00\\x00\\x52\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x4E\\x00\\x00\\x00\"\n\"\\x57\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\x58\\x00\\x00\\x00\\x53\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x4F\\x00\\x00\\x00\\x58\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\x59\\x00\\x00\\x00\\x54\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x50\\x00\\x00\\x00\\x59\\x00\\x00\\x00\\x41\\x00\\x05\\x00\"\n\"\\x5A\\x00\\x00\\x00\\x5B\\x00\\x00\\x00\\x4C\\x00\\x00\\x00\\x1F\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x5B\\x00\\x00\\x00\\x55\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x1A\\x00\\x00\\x00\\x60\\x00\\x00\\x00\\x5F\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x5D\\x00\\x00\\x00\\x60\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x20\\x00\\x00\\x00\"\n\"\\x63\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x62\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x64\\x00\\x00\\x00\\x63\\x00\\x00\\x00\\x41\\x00\\x05\\x00\"\n\"\\x69\\x00\\x00\\x00\\x6A\\x00\\x00\\x00\\x67\\x00\\x00\\x00\\x68\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\x6B\\x00\\x00\\x00\\x6A\\x00\\x00\\x00\"\n\"\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x6C\\x00\\x00\\x00\\x6B\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x6D\\x00\\x00\\x00\"\n\"\\x6B\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x6E\\x00\\x00\\x00\\x6B\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x50\\x00\\x07\\x00\"\n\"\\x07\\x00\\x00\\x00\\x6F\\x00\\x00\\x00\\x6C\\x00\\x00\\x00\\x6D\\x00\\x00\\x00\\x6E\\x00\\x00\\x00\\x26\\x00\\x00\\x00\\x91\\x00\\x05\\x00\\x07\\x00\\x00\\x00\"\n\"\\x70\\x00\\x00\\x00\\x64\\x00\\x00\\x00\\x6F\\x00\\x00\\x00\\x4F\\x00\\x08\\x00\\x0B\\x00\\x00\\x00\\x71\\x00\\x00\\x00\\x70\\x00\\x00\\x00\\x70\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\x72\\x00\\x00\\x00\\x4D\\x00\\x00\\x00\\x83\\x00\\x05\\x00\"\n\"\\x0B\\x00\\x00\\x00\\x73\\x00\\x00\\x00\\x71\\x00\\x00\\x00\\x72\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x61\\x00\\x00\\x00\\x73\\x00\\x00\\x00\\xF7\\x00\\x03\\x00\"\n\"\\x77\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\\x75\\x00\\x00\\x00\\x76\\x00\\x00\\x00\\x88\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x76\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\x7A\\x00\\x00\\x00\\x61\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\x7B\\x00\\x00\\x00\\x4F\\x00\\x00\\x00\"\n\"\\x94\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x7C\\x00\\x00\\x00\\x7A\\x00\\x00\\x00\\x7B\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x7E\\x00\\x00\\x00\\x7F\\x00\\x00\\x00\"\n\"\\x79\\x00\\x00\\x00\\x7D\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x7F\\x00\\x00\\x00\\x7C\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\x80\\x00\\x00\\x00\"\n\"\\x61\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\x81\\x00\\x00\\x00\\x50\\x00\\x00\\x00\\x94\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x82\\x00\\x00\\x00\"\n\"\\x80\\x00\\x00\\x00\\x81\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x7E\\x00\\x00\\x00\\x83\\x00\\x00\\x00\\x79\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\"\n\"\\x83\\x00\\x00\\x00\\x82\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\x84\\x00\\x00\\x00\\x61\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\"\n\"\\x85\\x00\\x00\\x00\\x4E\\x00\\x00\\x00\\x94\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x86\\x00\\x00\\x00\\x84\\x00\\x00\\x00\\x85\\x00\\x00\\x00\\x41\\x00\\x05\\x00\"\n\"\\x7E\\x00\\x00\\x00\\x87\\x00\\x00\\x00\\x79\\x00\\x00\\x00\\x33\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x87\\x00\\x00\\x00\\x86\\x00\\x00\\x00\\xF9\\x00\\x02\\x00\"\n\"\\x77\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x88\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\x89\\x00\\x00\\x00\\x61\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\"\n\"\\x79\\x00\\x00\\x00\\x89\\x00\\x00\\x00\\xF9\\x00\\x02\\x00\\x77\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x77\\x00\\x00\\x00\\xBA\\x00\\x05\\x00\\x74\\x00\\x00\\x00\"\n\"\\x8C\\x00\\x00\\x00\\x8A\\x00\\x00\\x00\\x8B\\x00\\x00\\x00\\xBA\\x00\\x05\\x00\\x74\\x00\\x00\\x00\\x8E\\x00\\x00\\x00\\x8D\\x00\\x00\\x00\\x8B\\x00\\x00\\x00\"\n\"\\xA6\\x00\\x05\\x00\\x74\\x00\\x00\\x00\\x8F\\x00\\x00\\x00\\x8C\\x00\\x00\\x00\\x8E\\x00\\x00\\x00\\xA6\\x00\\x05\\x00\\x74\\x00\\x00\\x00\\x92\\x00\\x00\\x00\"\n\"\\x8F\\x00\\x00\\x00\\x91\\x00\\x00\\x00\\xF7\\x00\\x03\\x00\\x94\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\\x92\\x00\\x00\\x00\\x93\\x00\\x00\\x00\"\n\"\\x94\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x93\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x69\\x00\\x00\\x00\\x97\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x96\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\x98\\x00\\x00\\x00\\x97\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\x99\\x00\\x00\\x00\\x4D\\x00\\x00\\x00\"\n\"\\x83\\x00\\x05\\x00\\x0B\\x00\\x00\\x00\\x9A\\x00\\x00\\x00\\x98\\x00\\x00\\x00\\x99\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x95\\x00\\x00\\x00\\x9A\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\x9C\\x00\\x00\\x00\\x95\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\x9D\\x00\\x00\\x00\\x4F\\x00\\x00\\x00\"\n\"\\x94\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x9E\\x00\\x00\\x00\\x9C\\x00\\x00\\x00\\x9D\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x7E\\x00\\x00\\x00\\x9F\\x00\\x00\\x00\"\n\"\\x9B\\x00\\x00\\x00\\x7D\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x9F\\x00\\x00\\x00\\x9E\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\xA0\\x00\\x00\\x00\"\n\"\\x95\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\xA1\\x00\\x00\\x00\\x50\\x00\\x00\\x00\\x94\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\xA2\\x00\\x00\\x00\"\n\"\\xA0\\x00\\x00\\x00\\xA1\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x7E\\x00\\x00\\x00\\xA3\\x00\\x00\\x00\\x9B\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\"\n\"\\xA3\\x00\\x00\\x00\\xA2\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\xA4\\x00\\x00\\x00\\x95\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\"\n\"\\xA5\\x00\\x00\\x00\\x4E\\x00\\x00\\x00\\x94\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\xA6\\x00\\x00\\x00\\xA4\\x00\\x00\\x00\\xA5\\x00\\x00\\x00\\x41\\x00\\x05\\x00\"\n\"\\x7E\\x00\\x00\\x00\\xA7\\x00\\x00\\x00\\x9B\\x00\\x00\\x00\\x33\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\xA7\\x00\\x00\\x00\\xA6\\x00\\x00\\x00\\xF9\\x00\\x02\\x00\"\n\"\\x94\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x94\\x00\\x00\\x00\\xF7\\x00\\x03\\x00\\xAA\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\\xA8\\x00\\x00\\x00\"\n\"\\xA9\\x00\\x00\\x00\\xAA\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\xA9\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\xAE\\x00\\x00\\x00\\x42\\x00\\x00\\x00\"\n\"\\x41\\x00\\x05\\x00\\x78\\x00\\x00\\x00\\xAF\\x00\\x00\\x00\\xAD\\x00\\x00\\x00\\x1F\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\xAF\\x00\\x00\\x00\\xAE\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\xB0\\x00\\x00\\x00\\x44\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x78\\x00\\x00\\x00\\xB1\\x00\\x00\\x00\\xAD\\x00\\x00\\x00\"\n\"\\x68\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\xB1\\x00\\x00\\x00\\xB0\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\xB2\\x00\\x00\\x00\\x40\\x00\\x00\\x00\"\n\"\\x41\\x00\\x05\\x00\\x78\\x00\\x00\\x00\\xB3\\x00\\x00\\x00\\xAD\\x00\\x00\\x00\\x62\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\xB3\\x00\\x00\\x00\\xB2\\x00\\x00\\x00\"\n\"\\xF9\\x00\\x02\\x00\\xAA\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\xAA\\x00\\x00\\x00\\xF7\\x00\\x03\\x00\\xBD\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\"\n\"\\xBB\\x00\\x00\\x00\\xBC\\x00\\x00\\x00\\xBD\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\xBC\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x20\\x00\\x00\\x00\\xBF\\x00\\x00\\x00\"\n\"\\x67\\x00\\x00\\x00\\x1F\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\xC0\\x00\\x00\\x00\\xBF\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x20\\x00\\x00\\x00\"\n\"\\xC1\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x68\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\xC2\\x00\\x00\\x00\\xC1\\x00\\x00\\x00\\x92\\x00\\x05\\x00\"\n\"\\x16\\x00\\x00\\x00\\xC3\\x00\\x00\\x00\\xC0\\x00\\x00\\x00\\xC2\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\xC4\\x00\\x00\\x00\\x4D\\x00\\x00\\x00\"\n\"\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\xC5\\x00\\x00\\x00\\xC4\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\xC6\\x00\\x00\\x00\"\n\"\\xC4\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\xC7\\x00\\x00\\x00\\xC4\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x50\\x00\\x07\\x00\"\n\"\\x07\\x00\\x00\\x00\\xC8\\x00\\x00\\x00\\xC5\\x00\\x00\\x00\\xC6\\x00\\x00\\x00\\xC7\\x00\\x00\\x00\\x26\\x00\\x00\\x00\\x91\\x00\\x05\\x00\\x07\\x00\\x00\\x00\"\n\"\\xC9\\x00\\x00\\x00\\xC3\\x00\\x00\\x00\\xC8\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\xBE\\x00\\x00\\x00\\xC9\\x00\\x00\\x00\\xF9\\x00\\x02\\x00\\xBD\\x00\\x00\\x00\"\n\"\\xF8\\x00\\x02\\x00\\xBD\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x20\\x00\\x00\\x00\\xCB\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x68\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x16\\x00\\x00\\x00\\xCC\\x00\\x00\\x00\\xCB\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\xCD\\x00\\x00\\x00\\x4D\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x0B\\x00\\x00\\x00\\xCE\\x00\\x00\\x00\\x4E\\x00\\x00\\x00\\x8E\\x00\\x05\\x00\\x0B\\x00\\x00\\x00\\xD0\\x00\\x00\\x00\\xCE\\x00\\x00\\x00\\xCF\\x00\\x00\\x00\"\n\"\\x81\\x00\\x05\\x00\\x0B\\x00\\x00\\x00\\xD1\\x00\\x00\\x00\\xCD\\x00\\x00\\x00\\xD0\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\xD2\\x00\\x00\\x00\"\n\"\\xD1\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\xD3\\x00\\x00\\x00\\xD1\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x51\\x00\\x05\\x00\"\n\"\\x06\\x00\\x00\\x00\\xD4\\x00\\x00\\x00\\xD1\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x50\\x00\\x07\\x00\\x07\\x00\\x00\\x00\\xD5\\x00\\x00\\x00\\xD2\\x00\\x00\\x00\"\n\"\\xD3\\x00\\x00\\x00\\xD4\\x00\\x00\\x00\\x26\\x00\\x00\\x00\\x91\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\xD6\\x00\\x00\\x00\\xCC\\x00\\x00\\x00\\xD5\\x00\\x00\\x00\"\n\"\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\xD7\\x00\\x00\\x00\\xD6\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\xD8\\x00\\x00\\x00\"\n\"\\xD6\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\xD9\\x00\\x00\\x00\\xD6\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x50\\x00\\x06\\x00\"\n\"\\x0B\\x00\\x00\\x00\\xDA\\x00\\x00\\x00\\xD7\\x00\\x00\\x00\\xD8\\x00\\x00\\x00\\xD9\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\xCA\\x00\\x00\\x00\\xDA\\x00\\x00\\x00\"\n\"\\xFD\\x00\\x01\\x00\\x38\\x00\\x01\\x00\\x36\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\"\n\"\\x0A\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x14\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x20\\x00\\x00\\x00\\x21\\x00\\x00\\x00\"\n\"\\x1D\\x00\\x00\\x00\\x1F\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x16\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\"\n\"\\x25\\x00\\x00\\x00\\x24\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x27\\x00\\x00\\x00\\x25\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x51\\x00\\x05\\x00\"\n\"\\x06\\x00\\x00\\x00\\x28\\x00\\x00\\x00\\x25\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x29\\x00\\x00\\x00\\x25\\x00\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\x50\\x00\\x07\\x00\\x07\\x00\\x00\\x00\\x2A\\x00\\x00\\x00\\x27\\x00\\x00\\x00\\x28\\x00\\x00\\x00\\x29\\x00\\x00\\x00\\x26\\x00\\x00\\x00\"\n\"\\x91\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x2B\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x2A\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x15\\x00\\x00\\x00\\x2B\\x00\\x00\\x00\"\n\"\\x41\\x00\\x05\\x00\\x2E\\x00\\x00\\x00\\x2F\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x30\\x00\\x00\\x00\"\n\"\\x2F\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x31\\x00\\x00\\x00\\x30\\x00\\x00\\x00\\x2C\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x2E\\x00\\x00\\x00\"\n\"\\x32\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x32\\x00\\x00\\x00\\x31\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x2E\\x00\\x00\\x00\"\n\"\\x34\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x33\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x35\\x00\\x00\\x00\\x34\\x00\\x00\\x00\\x41\\x00\\x05\\x00\"\n\"\\x2E\\x00\\x00\\x00\\x37\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x36\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x38\\x00\\x00\\x00\\x37\\x00\\x00\\x00\"\n\"\\x81\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x39\\x00\\x00\\x00\\x35\\x00\\x00\\x00\\x38\\x00\\x00\\x00\\x88\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x3B\\x00\\x00\\x00\"\n\"\\x39\\x00\\x00\\x00\\x3A\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x2E\\x00\\x00\\x00\\x3C\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x33\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\"\n\"\\x3C\\x00\\x00\\x00\\x3B\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x3D\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\xFE\\x00\\x02\\x00\\x3D\\x00\\x00\\x00\"\n\"\\x38\\x00\\x01\\x00\\x36\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x37\\x00\\x03\\x00\\x0C\\x00\\x00\\x00\"\n\"\\x0E\\x00\\x00\\x00\\x37\\x00\\x03\\x00\\x0C\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\\x37\\x00\\x03\\x00\\x0C\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x37\\x00\\x03\\x00\"\n\"\\x0C\\x00\\x00\\x00\\x11\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x13\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\x41\\x00\\x00\\x00\\x40\\x00\\x00\\x00\"\n\"\\x3E\\x00\\x03\\x00\\x0F\\x00\\x00\\x00\\x41\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\x43\\x00\\x00\\x00\\x42\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\"\n\"\\x10\\x00\\x00\\x00\\x43\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\x45\\x00\\x00\\x00\\x44\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x11\\x00\\x00\\x00\"\n\"\\x45\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x0B\\x00\\x00\\x00\\x46\\x00\\x00\\x00\\x24\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x0E\\x00\\x00\\x00\\x46\\x00\\x00\\x00\"\n\"\\x39\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x47\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\xFE\\x00\\x02\\x00\\x47\\x00\\x00\\x00\\x38\\x00\\x01\\x00\\x03\\x02\\x23\\x07\"\n\"\\x00\\x00\\x01\\x00\\x0A\\x00\\x08\\x00\\x2B\\x02\\x00\\x00\\x00\\x00\\x00\\x00\\x11\\x00\\x02\\x00\\x01\\x00\\x00\\x00\\x11\\x00\\x02\\x00\\x78\\x11\\x00\\x00\"\n\"\\x0A\\x00\\x06\\x00\\x53\\x50\\x56\\x5F\\x4B\\x48\\x52\\x5F\\x72\\x61\\x79\\x5F\\x71\\x75\\x65\\x72\\x79\\x00\\x00\\x00\\x0B\\x00\\x06\\x00\\x01\\x00\\x00\\x00\"\n\"\\x47\\x4C\\x53\\x4C\\x2E\\x73\\x74\\x64\\x2E\\x34\\x35\\x30\\x00\\x00\\x00\\x00\\x0E\\x00\\x03\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x0F\\x00\\x0D\\x00\"\n\"\\x04\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x6D\\x61\\x69\\x6E\\x00\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\\x86\\x00\\x00\\x00\\x1E\\x01\\x00\\x00\\x1F\\x01\\x00\\x00\"\n\"\\x67\\x01\\x00\\x00\\xC4\\x01\\x00\\x00\\xF5\\x01\\x00\\x00\\x17\\x02\\x00\\x00\\x10\\x00\\x03\\x00\\x04\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x47\\x00\\x04\\x00\"\n\"\\x1C\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x25\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x08\\x01\\x00\\x00\\x47\\x00\\x04\\x00\"\n\"\\x2D\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x3A\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\"\n\"\\x3B\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x3B\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x3B\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x3B\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x3B\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x3B\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x3B\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x3B\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x3B\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x10\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x3B\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xC0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x3B\\x00\\x00\\x00\"\n\"\\x04\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xCC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x3B\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xD0\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x3B\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xDC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x3B\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\xE0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x3B\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xEC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x3B\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\xF0\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x3B\\x00\\x00\\x00\\x0A\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\xFC\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x3B\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x3B\\x00\\x00\\x00\"\n\"\\x0C\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x3B\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x4C\\x01\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x3B\\x00\\x00\\x00\\x0E\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x50\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x3B\\x00\\x00\\x00\\x0F\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x5C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x3B\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x60\\x01\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x3B\\x00\\x00\\x00\\x11\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x6C\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x3B\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x70\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x3B\\x00\\x00\\x00\\x13\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x80\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x3B\\x00\\x00\\x00\"\n\"\\x14\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x90\\x01\\x00\\x00\\x48\\x00\\x05\\x00\\x3B\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x94\\x01\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x3B\\x00\\x00\\x00\\x16\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x98\\x01\\x00\\x00\\x47\\x00\\x03\\x00\\x3B\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x3D\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x3D\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x5A\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x07\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\x7B\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x47\\x00\\x04\\x00\\x7B\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x48\\x00\\x04\\x00\\x7E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x05\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x7E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x7E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x7E\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x48\\x00\\x05\\x00\"\n\"\\x7E\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x4C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x7E\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x23\\x00\\x00\\x00\"\n\"\\x50\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x7E\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x5C\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x7E\\x00\\x00\\x00\"\n\"\\x05\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x60\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x7E\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x6C\\x00\\x00\\x00\"\n\"\\x48\\x00\\x05\\x00\\x7E\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x70\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x7E\\x00\\x00\\x00\\x08\\x00\\x00\\x00\"\n\"\\x23\\x00\\x00\\x00\\x80\\x00\\x00\\x00\\x48\\x00\\x05\\x00\\x7E\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x88\\x00\\x00\\x00\\x47\\x00\\x03\\x00\"\n\"\\x7E\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x80\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x80\\x00\\x00\\x00\"\n\"\\x21\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x86\\x00\\x00\\x00\\x1E\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x9A\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x02\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\xE8\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\x1C\\x01\\x00\\x00\"\n\"\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x1C\\x01\\x00\\x00\\x21\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x1E\\x01\\x00\\x00\"\n\"\\x1E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x1F\\x01\\x00\\x00\\x1E\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x28\\x01\\x00\\x00\"\n\"\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x28\\x01\\x00\\x00\\x21\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x30\\x01\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x12\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x36\\x01\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x36\\x01\\x00\\x00\"\n\"\\x21\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x46\\x01\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x46\\x01\\x00\\x00\"\n\"\\x21\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x55\\x01\\x00\\x00\\x01\\x00\\x00\\x00\\x10\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x67\\x01\\x00\\x00\"\n\"\\x1E\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x95\\x01\\x00\\x00\\x01\\x00\\x00\\x00\\x01\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\x9A\\x01\\x00\\x00\"\n\"\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x9A\\x01\\x00\\x00\\x21\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\xB7\\x01\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x13\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\xC4\\x01\\x00\\x00\\x1E\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\xDF\\x01\\x00\\x00\"\n\"\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\xDF\\x01\\x00\\x00\\x21\\x00\\x00\\x00\\x0B\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\xE7\\x01\\x00\\x00\"\n\"\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\xE7\\x01\\x00\\x00\\x21\\x00\\x00\\x00\\x0A\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\xEF\\x01\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x06\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\xF3\\x01\\x00\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\xF3\\x01\\x00\\x00\"\n\"\\x21\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\xF5\\x01\\x00\\x00\\x1E\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\xFC\\x01\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x05\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\x17\\x02\\x00\\x00\\x1E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x28\\x02\\x00\\x00\"\n\"\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x28\\x02\\x00\\x00\\x21\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x47\\x00\\x04\\x00\\x29\\x02\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x03\\x01\\x00\\x00\\x47\\x00\\x04\\x00\\x2A\\x02\\x00\\x00\\x01\\x00\\x00\\x00\\x04\\x01\\x00\\x00\\x13\\x00\\x02\\x00\\x02\\x00\\x00\\x00\"\n\"\\x21\\x00\\x03\\x00\\x03\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x16\\x00\\x03\\x00\\x06\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x17\\x00\\x04\\x00\\x07\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x08\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x21\\x00\\x04\\x00\\x09\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\x21\\x00\\x03\\x00\\x0D\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x19\\x00\\x09\\x00\\x10\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x1B\\x00\\x03\\x00\\x11\\x00\\x00\\x00\"\n\"\\x10\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x12\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x11\\x00\\x00\\x00\\x17\\x00\\x04\\x00\\x13\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x14\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x13\\x00\\x00\\x00\\x21\\x00\\x06\\x00\\x15\\x00\\x00\\x00\\x13\\x00\\x00\\x00\"\n\"\\x12\\x00\\x00\\x00\\x14\\x00\\x00\\x00\\x08\\x00\\x00\\x00\\x15\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x32\\x00\\x04\\x00\"\n\"\\x1B\\x00\\x00\\x00\\x1C\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x14\\x00\\x02\\x00\"\n\"\\x1E\\x00\\x00\\x00\\x34\\x00\\x06\\x00\\x1E\\x00\\x00\\x00\\x1F\\x00\\x00\\x00\\xAB\\x00\\x00\\x00\\x1C\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x34\\x00\\x05\\x00\"\n\"\\x1E\\x00\\x00\\x00\\x20\\x00\\x00\\x00\\xA8\\x00\\x00\\x00\\x1F\\x00\\x00\\x00\\x32\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\\x25\\x00\\x00\\x00\\x08\\x01\\x00\\x00\"\n\"\\x34\\x00\\x06\\x00\\x1E\\x00\\x00\\x00\\x26\\x00\\x00\\x00\\xAB\\x00\\x00\\x00\\x25\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x29\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x17\\x00\\x04\\x00\\x2B\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x2C\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x2B\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x2C\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x15\\x00\\x04\\x00\\x2E\\x00\\x00\\x00\"\n\"\\x20\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x2E\\x00\\x00\\x00\\x2F\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x30\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x2E\\x00\\x00\\x00\\x33\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x18\\x00\\x04\\x00\\x38\\x00\\x00\\x00\"\n\"\\x2B\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x2E\\x00\\x00\\x00\\x39\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x1C\\x00\\x04\\x00\\x3A\\x00\\x00\\x00\"\n\"\\x2B\\x00\\x00\\x00\\x39\\x00\\x00\\x00\\x1E\\x00\\x19\\x00\\x3B\\x00\\x00\\x00\\x38\\x00\\x00\\x00\\x38\\x00\\x00\\x00\\x38\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x3A\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x2B\\x00\\x00\\x00\\x2B\\x00\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x13\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x3C\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x3B\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\"\n\"\\x3C\\x00\\x00\\x00\\x3D\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\\x3E\\x00\\x00\\x00\\x14\\x00\\x00\\x00\\x20\\x00\\x04\\x00\"\n\"\\x3F\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\\x46\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\"\n\"\\x06\\x00\\x00\\x00\\x54\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x32\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\\x5A\\x00\\x00\\x00\\x07\\x01\\x00\\x00\\x34\\x00\\x06\\x00\"\n\"\\x1E\\x00\\x00\\x00\\x5B\\x00\\x00\\x00\\xAB\\x00\\x00\\x00\\x5A\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x62\\x00\\x00\\x00\"\n\"\\x3B\\xAA\\xB8\\x3F\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x66\\x00\\x00\\x00\\x00\\x00\\x80\\x3F\\x2B\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\\x67\\x00\\x00\\x00\"\n\"\\x13\\x00\\x00\\x00\\x78\\x11\\x02\\x00\\x76\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x77\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x76\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\"\n\"\\x77\\x00\\x00\\x00\\x78\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\xDD\\x14\\x02\\x00\\x79\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x7A\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x79\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x7A\\x00\\x00\\x00\\x7B\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x2E\\x00\\x00\\x00\\x7D\\x00\\x00\\x00\"\n\"\\xFF\\x00\\x00\\x00\\x1E\\x00\\x0C\\x00\\x7E\\x00\\x00\\x00\\x38\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x2B\\x00\\x00\\x00\\x13\\x00\\x00\\x00\\x13\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x7F\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x7E\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x7F\\x00\\x00\\x00\\x80\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\\x81\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x82\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x85\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x85\\x00\\x00\\x00\\x86\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x29\\x00\\x03\\x00\\x1E\\x00\\x00\\x00\\x8C\\x00\\x00\\x00\"\n\"\\x2B\\x00\\x04\\x00\\x2E\\x00\\x00\\x00\\x8E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x2A\\x00\\x03\\x00\\x1E\\x00\\x00\\x00\\x93\\x00\\x00\\x00\\x32\\x00\\x04\\x00\"\n\"\\x06\\x00\\x00\\x00\\x9A\\x00\\x00\\x00\\x00\\x00\\x81\\x43\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x9D\\x00\\x00\\x00\\x00\\x00\\x80\\xBF\\x2C\\x00\\x05\\x00\"\n\"\\x13\\x00\\x00\\x00\\x9E\\x00\\x00\\x00\\x9D\\x00\\x00\\x00\\x66\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\xAB\\x00\\x00\\x00\\x00\\x00\\x20\\x41\"\n\"\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\xCC\\x00\\x00\\x00\\x00\\x00\\xA0\\x40\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\xD9\\x00\\x00\\x00\\x00\\x00\\x00\\x3F\"\n\"\\x32\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\xE8\\x00\\x00\\x00\\x00\\x00\\x80\\x43\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\xF8\\x00\\x00\\x00\\xFA\\x7E\\xAA\\x3E\"\n\"\\x3B\\x00\\x04\\x00\\x12\\x00\\x00\\x00\\x1C\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x1D\\x01\\x00\\x00\\x01\\x00\\x00\\x00\\x13\\x00\\x00\\x00\"\n\"\\x3B\\x00\\x04\\x00\\x1D\\x01\\x00\\x00\\x1E\\x01\\x00\\x00\\x01\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x85\\x00\\x00\\x00\\x1F\\x01\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x3B\\x00\\x04\\x00\\x12\\x00\\x00\\x00\\x28\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x32\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\\x30\\x01\\x00\\x00\\x12\\x00\\x00\\x00\"\n\"\\x34\\x00\\x06\\x00\\x1E\\x00\\x00\\x00\\x31\\x01\\x00\\x00\\xAB\\x00\\x00\\x00\\x30\\x01\\x00\\x00\\x1D\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x34\\x01\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x2B\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x12\\x00\\x00\\x00\\x36\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\"\n\"\\x3C\\x01\\x00\\x00\\x0C\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x12\\x00\\x00\\x00\\x46\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\"\n\"\\x4C\\x01\\x00\\x00\\x0E\\x00\\x00\\x00\\x32\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\\x55\\x01\\x00\\x00\\x10\\x00\\x00\\x00\\x34\\x00\\x06\\x00\\x1E\\x00\\x00\\x00\"\n\"\\x56\\x01\\x00\\x00\\xAB\\x00\\x00\\x00\\x55\\x01\\x00\\x00\\x1D\\x00\\x00\\x00\\x2C\\x00\\x06\\x00\\x07\\x00\\x00\\x00\\x65\\x01\\x00\\x00\\x54\\x00\\x00\\x00\"\n\"\\x54\\x00\\x00\\x00\\x66\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x85\\x00\\x00\\x00\\x67\\x01\\x00\\x00\\x01\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\"\n\"\\x6B\\x01\\x00\\x00\\x06\\x00\\x00\\x00\\x34\\x00\\x06\\x00\\x1E\\x00\\x00\\x00\\x77\\x01\\x00\\x00\\xAB\\x00\\x00\\x00\\x55\\x01\\x00\\x00\\x1D\\x00\\x00\\x00\"\n\"\\x2B\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\\x7C\\x01\\x00\\x00\\x05\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x2E\\x00\\x00\\x00\\x7F\\x01\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x32\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x95\\x01\\x00\\x00\\x00\\x80\\x80\\x43\\x3B\\x00\\x04\\x00\\x12\\x00\\x00\\x00\\x9A\\x01\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x2B\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\xA9\\x01\\x00\\x00\\x00\\x00\\x00\\x42\\x32\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\\xB7\\x01\\x00\\x00\\x13\\x00\\x00\\x00\"\n\"\\x34\\x00\\x06\\x00\\x1E\\x00\\x00\\x00\\xB8\\x01\\x00\\x00\\xAB\\x00\\x00\\x00\\xB7\\x01\\x00\\x00\\x1D\\x00\\x00\\x00\\x18\\x00\\x04\\x00\\xC2\\x01\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\xC3\\x01\\x00\\x00\\x01\\x00\\x00\\x00\\xC2\\x01\\x00\\x00\\x3B\\x00\\x04\\x00\\xC3\\x01\\x00\\x00\"\n\"\\xC4\\x01\\x00\\x00\\x01\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\\xD0\\x01\\x00\\x00\\x02\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\xD5\\x01\\x00\\x00\"\n\"\\x02\\x00\\x00\\x00\\x38\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x12\\x00\\x00\\x00\\xDF\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x19\\x00\\x09\\x00\\xE4\\x01\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x1B\\x00\\x03\\x00\"\n\"\\xE5\\x01\\x00\\x00\\xE4\\x01\\x00\\x00\\x20\\x00\\x04\\x00\\xE6\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\xE5\\x01\\x00\\x00\\x3B\\x00\\x04\\x00\\xE6\\x01\\x00\\x00\"\n\"\\xE7\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x32\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\\xEF\\x01\\x00\\x00\\x06\\x01\\x00\\x00\\x34\\x00\\x06\\x00\\x1E\\x00\\x00\\x00\"\n\"\\xF0\\x01\\x00\\x00\\xAB\\x00\\x00\\x00\\xEF\\x01\\x00\\x00\\x1D\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\xE6\\x01\\x00\\x00\\xF3\\x01\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x3B\\x00\\x04\\x00\\x2C\\x00\\x00\\x00\\xF5\\x01\\x00\\x00\\x01\\x00\\x00\\x00\\x32\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\\xFC\\x01\\x00\\x00\\x05\\x01\\x00\\x00\"\n\"\\x34\\x00\\x06\\x00\\x1E\\x00\\x00\\x00\\xFD\\x01\\x00\\x00\\xAB\\x00\\x00\\x00\\xFC\\x01\\x00\\x00\\x1D\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x16\\x02\\x00\\x00\"\n\"\\x03\\x00\\x00\\x00\\x2B\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x16\\x02\\x00\\x00\\x17\\x02\\x00\\x00\\x03\\x00\\x00\\x00\\x2B\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\"\n\"\\x1B\\x02\\x00\\x00\\x03\\x00\\x00\\x00\\x20\\x00\\x04\\x00\\x23\\x02\\x00\\x00\\x03\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x19\\x00\\x09\\x00\\x25\\x02\\x00\\x00\"\n\"\\x06\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x1B\\x00\\x03\\x00\"\n\"\\x26\\x02\\x00\\x00\\x25\\x02\\x00\\x00\\x20\\x00\\x04\\x00\\x27\\x02\\x00\\x00\\x00\\x00\\x00\\x00\\x26\\x02\\x00\\x00\\x3B\\x00\\x04\\x00\\x27\\x02\\x00\\x00\"\n\"\\x28\\x02\\x00\\x00\\x00\\x00\\x00\\x00\\x32\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\\x29\\x02\\x00\\x00\\x03\\x01\\x00\\x00\\x32\\x00\\x04\\x00\\x1B\\x00\\x00\\x00\"\n\"\\x2A\\x02\\x00\\x00\\x04\\x01\\x00\\x00\\x36\\x00\\x05\\x00\\x02\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\"\n\"\\x05\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x14\\x00\\x00\\x00\\x1B\\x01\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x14\\x00\\x00\\x00\\x20\\x01\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x08\\x00\\x00\\x00\\x22\\x01\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x08\\x00\\x00\\x00\\x27\\x01\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x34\\x01\\x00\\x00\\x35\\x01\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x34\\x01\\x00\\x00\\x45\\x01\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x08\\x00\\x00\\x00\\x59\\x01\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x29\\x00\\x00\\x00\\x66\\x01\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x08\\x00\\x00\\x00\\x74\\x01\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x08\\x00\\x00\\x00\\x7A\\x01\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x34\\x01\\x00\\x00\\x99\\x01\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x08\\x00\\x00\\x00\\x9E\\x01\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x29\\x00\\x00\\x00\\xA4\\x01\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x08\\x00\\x00\\x00\\xBB\\x01\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x14\\x00\\x00\\x00\\x06\\x02\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x08\\x00\\x00\\x00\\x1F\\x02\\x00\\x00\"\n\"\\x07\\x00\\x00\\x00\\xBA\\x00\\x05\\x00\\x1E\\x00\\x00\\x00\\x18\\x01\\x00\\x00\\xE8\\x00\\x00\\x00\\x54\\x00\\x00\\x00\\xF7\\x00\\x03\\x00\\x1A\\x01\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\\x18\\x01\\x00\\x00\\x19\\x01\\x00\\x00\\x25\\x01\\x00\\x00\\xF8\\x00\\x02\\x00\\x19\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x13\\x00\\x00\\x00\\x21\\x01\\x00\\x00\\x1E\\x01\\x00\\x00\\x3E\\x00\\x03\\x00\\x20\\x01\\x00\\x00\\x21\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\"\n\"\\x23\\x01\\x00\\x00\\x1F\\x01\\x00\\x00\\x3E\\x00\\x03\\x00\\x22\\x01\\x00\\x00\\x23\\x01\\x00\\x00\\x39\\x00\\x07\\x00\\x13\\x00\\x00\\x00\\x24\\x01\\x00\\x00\"\n\"\\x19\\x00\\x00\\x00\\x1C\\x01\\x00\\x00\\x20\\x01\\x00\\x00\\x22\\x01\\x00\\x00\\x3E\\x00\\x03\\x00\\x1B\\x01\\x00\\x00\\x24\\x01\\x00\\x00\\xF9\\x00\\x02\\x00\"\n\"\\x1A\\x01\\x00\\x00\\xF8\\x00\\x02\\x00\\x25\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x13\\x00\\x00\\x00\\x26\\x01\\x00\\x00\\x1E\\x01\\x00\\x00\\x3E\\x00\\x03\\x00\"\n\"\\x1B\\x01\\x00\\x00\\x26\\x01\\x00\\x00\\xF9\\x00\\x02\\x00\\x1A\\x01\\x00\\x00\\xF8\\x00\\x02\\x00\\x1A\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x11\\x00\\x00\\x00\"\n\"\\x29\\x01\\x00\\x00\\x28\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x13\\x00\\x00\\x00\\x2A\\x01\\x00\\x00\\x1B\\x01\\x00\\x00\\x57\\x00\\x05\\x00\\x2B\\x00\\x00\\x00\"\n\"\\x2B\\x01\\x00\\x00\\x29\\x01\\x00\\x00\\x2A\\x01\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x2C\\x01\\x00\\x00\\x2B\\x01\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x2D\\x01\\x00\\x00\\x2B\\x01\\x00\\x00\\x01\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x2E\\x01\\x00\\x00\"\n\"\\x2B\\x01\\x00\\x00\\x02\\x00\\x00\\x00\\x50\\x00\\x06\\x00\\x07\\x00\\x00\\x00\\x2F\\x01\\x00\\x00\\x2C\\x01\\x00\\x00\\x2D\\x01\\x00\\x00\\x2E\\x01\\x00\\x00\"\n\"\\x3E\\x00\\x03\\x00\\x27\\x01\\x00\\x00\\x2F\\x01\\x00\\x00\\xF7\\x00\\x03\\x00\\x33\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\\x31\\x01\\x00\\x00\"\n\"\\x32\\x01\\x00\\x00\\x33\\x01\\x00\\x00\\xF8\\x00\\x02\\x00\\x32\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x11\\x00\\x00\\x00\\x37\\x01\\x00\\x00\\x36\\x01\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x13\\x00\\x00\\x00\\x38\\x01\\x00\\x00\\x1B\\x01\\x00\\x00\\x57\\x00\\x05\\x00\\x2B\\x00\\x00\\x00\\x39\\x01\\x00\\x00\\x37\\x01\\x00\\x00\"\n\"\\x38\\x01\\x00\\x00\\x3E\\x00\\x03\\x00\\x35\\x01\\x00\\x00\\x39\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x2B\\x00\\x00\\x00\\x3A\\x01\\x00\\x00\\x35\\x01\\x00\\x00\"\n\"\\x4F\\x00\\x08\\x00\\x07\\x00\\x00\\x00\\x3B\\x01\\x00\\x00\\x3A\\x01\\x00\\x00\\x3A\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x41\\x00\\x05\\x00\\x82\\x00\\x00\\x00\\x3D\\x01\\x00\\x00\\x3D\\x00\\x00\\x00\\x3C\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x3E\\x01\\x00\\x00\"\n\"\\x3D\\x01\\x00\\x00\\x85\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x3F\\x01\\x00\\x00\\x3B\\x01\\x00\\x00\\x3E\\x01\\x00\\x00\\x41\\x00\\x05\\x00\\x29\\x00\\x00\\x00\"\n\"\\x40\\x01\\x00\\x00\\x35\\x01\\x00\\x00\\x33\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x41\\x01\\x00\\x00\\x40\\x01\\x00\\x00\\x8E\\x00\\x05\\x00\"\n\"\\x07\\x00\\x00\\x00\\x42\\x01\\x00\\x00\\x3F\\x01\\x00\\x00\\x41\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x43\\x01\\x00\\x00\\x27\\x01\\x00\\x00\"\n\"\\x81\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x44\\x01\\x00\\x00\\x43\\x01\\x00\\x00\\x42\\x01\\x00\\x00\\x3E\\x00\\x03\\x00\\x27\\x01\\x00\\x00\\x44\\x01\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x11\\x00\\x00\\x00\\x47\\x01\\x00\\x00\\x46\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x13\\x00\\x00\\x00\\x48\\x01\\x00\\x00\\x1B\\x01\\x00\\x00\"\n\"\\x57\\x00\\x05\\x00\\x2B\\x00\\x00\\x00\\x49\\x01\\x00\\x00\\x47\\x01\\x00\\x00\\x48\\x01\\x00\\x00\\x3E\\x00\\x03\\x00\\x45\\x01\\x00\\x00\\x49\\x01\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x2B\\x00\\x00\\x00\\x4A\\x01\\x00\\x00\\x45\\x01\\x00\\x00\\x4F\\x00\\x08\\x00\\x07\\x00\\x00\\x00\\x4B\\x01\\x00\\x00\\x4A\\x01\\x00\\x00\"\n\"\\x4A\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x82\\x00\\x00\\x00\\x4D\\x01\\x00\\x00\\x3D\\x00\\x00\\x00\"\n\"\\x4C\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x4E\\x01\\x00\\x00\\x4D\\x01\\x00\\x00\\x85\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x4F\\x01\\x00\\x00\"\n\"\\x4B\\x01\\x00\\x00\\x4E\\x01\\x00\\x00\\x41\\x00\\x05\\x00\\x29\\x00\\x00\\x00\\x50\\x01\\x00\\x00\\x45\\x01\\x00\\x00\\x33\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x06\\x00\\x00\\x00\\x51\\x01\\x00\\x00\\x50\\x01\\x00\\x00\\x8E\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x52\\x01\\x00\\x00\\x4F\\x01\\x00\\x00\\x51\\x01\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x53\\x01\\x00\\x00\\x27\\x01\\x00\\x00\\x81\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x54\\x01\\x00\\x00\\x53\\x01\\x00\\x00\"\n\"\\x52\\x01\\x00\\x00\\x3E\\x00\\x03\\x00\\x27\\x01\\x00\\x00\\x54\\x01\\x00\\x00\\xF9\\x00\\x02\\x00\\x33\\x01\\x00\\x00\\xF8\\x00\\x02\\x00\\x33\\x01\\x00\\x00\"\n\"\\xF7\\x00\\x03\\x00\\x58\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\\x56\\x01\\x00\\x00\\x57\\x01\\x00\\x00\\x64\\x01\\x00\\x00\\xF8\\x00\\x02\\x00\"\n\"\\x57\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x11\\x00\\x00\\x00\\x5A\\x01\\x00\\x00\\x1C\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x13\\x00\\x00\\x00\\x5B\\x01\\x00\\x00\"\n\"\\x1B\\x01\\x00\\x00\\x57\\x00\\x05\\x00\\x2B\\x00\\x00\\x00\\x5C\\x01\\x00\\x00\\x5A\\x01\\x00\\x00\\x5B\\x01\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\"\n\"\\x5D\\x01\\x00\\x00\\x5C\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x5E\\x01\\x00\\x00\\x5C\\x01\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x5F\\x01\\x00\\x00\\x5C\\x01\\x00\\x00\\x02\\x00\\x00\\x00\\x50\\x00\\x06\\x00\\x07\\x00\\x00\\x00\\x60\\x01\\x00\\x00\"\n\"\\x5D\\x01\\x00\\x00\\x5E\\x01\\x00\\x00\\x5F\\x01\\x00\\x00\\x50\\x00\\x06\\x00\\x07\\x00\\x00\\x00\\x61\\x01\\x00\\x00\\xD9\\x00\\x00\\x00\\xD9\\x00\\x00\\x00\"\n\"\\xD9\\x00\\x00\\x00\\x83\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x62\\x01\\x00\\x00\\x60\\x01\\x00\\x00\\x61\\x01\\x00\\x00\\x0C\\x00\\x06\\x00\\x07\\x00\\x00\\x00\"\n\"\\x63\\x01\\x00\\x00\\x01\\x00\\x00\\x00\\x45\\x00\\x00\\x00\\x62\\x01\\x00\\x00\\x3E\\x00\\x03\\x00\\x59\\x01\\x00\\x00\\x63\\x01\\x00\\x00\\xF9\\x00\\x02\\x00\"\n\"\\x58\\x01\\x00\\x00\\xF8\\x00\\x02\\x00\\x64\\x01\\x00\\x00\\x3E\\x00\\x03\\x00\\x59\\x01\\x00\\x00\\x65\\x01\\x00\\x00\\xF9\\x00\\x02\\x00\\x58\\x01\\x00\\x00\"\n\"\\xF8\\x00\\x02\\x00\\x58\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x68\\x01\\x00\\x00\\x67\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\"\n\"\\x69\\x01\\x00\\x00\\x67\\x01\\x00\\x00\\x94\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x6A\\x01\\x00\\x00\\x68\\x01\\x00\\x00\\x69\\x01\\x00\\x00\\x41\\x00\\x05\\x00\"\n\"\\x3F\\x00\\x00\\x00\\x6C\\x01\\x00\\x00\\x80\\x00\\x00\\x00\\x6B\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x6D\\x01\\x00\\x00\\x6C\\x01\\x00\\x00\"\n\"\\x41\\x00\\x05\\x00\\x3F\\x00\\x00\\x00\\x6E\\x01\\x00\\x00\\x80\\x00\\x00\\x00\\x6B\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x6F\\x01\\x00\\x00\"\n\"\\x6E\\x01\\x00\\x00\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x70\\x01\\x00\\x00\\x6D\\x01\\x00\\x00\\x6F\\x01\\x00\\x00\\x88\\x00\\x05\\x00\\x06\\x00\\x00\\x00\"\n\"\\x71\\x01\\x00\\x00\\x6A\\x01\\x00\\x00\\x70\\x01\\x00\\x00\\x83\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x72\\x01\\x00\\x00\\x66\\x00\\x00\\x00\\x71\\x01\\x00\\x00\"\n\"\\x0C\\x00\\x07\\x00\\x06\\x00\\x00\\x00\\x73\\x01\\x00\\x00\\x01\\x00\\x00\\x00\\x28\\x00\\x00\\x00\\x72\\x01\\x00\\x00\\x54\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\"\n\"\\x66\\x01\\x00\\x00\\x73\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x75\\x01\\x00\\x00\\x67\\x01\\x00\\x00\\x0C\\x00\\x06\\x00\\x07\\x00\\x00\\x00\"\n\"\\x76\\x01\\x00\\x00\\x01\\x00\\x00\\x00\\x45\\x00\\x00\\x00\\x75\\x01\\x00\\x00\\x3E\\x00\\x03\\x00\\x74\\x01\\x00\\x00\\x76\\x01\\x00\\x00\\xF7\\x00\\x03\\x00\"\n\"\\x79\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\\x77\\x01\\x00\\x00\\x78\\x01\\x00\\x00\\x89\\x01\\x00\\x00\\xF8\\x00\\x02\\x00\\x78\\x01\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x7B\\x01\\x00\\x00\\x27\\x01\\x00\\x00\\x41\\x00\\x06\\x00\\x3F\\x00\\x00\\x00\\x7D\\x01\\x00\\x00\\x80\\x00\\x00\\x00\"\n\"\\x7C\\x01\\x00\\x00\\x8E\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x7E\\x01\\x00\\x00\\x7D\\x01\\x00\\x00\\x41\\x00\\x06\\x00\\x3F\\x00\\x00\\x00\"\n\"\\x80\\x01\\x00\\x00\\x80\\x00\\x00\\x00\\x7C\\x01\\x00\\x00\\x7F\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x81\\x01\\x00\\x00\\x80\\x01\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x82\\x01\\x00\\x00\\x59\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x83\\x01\\x00\\x00\\x74\\x01\\x00\\x00\"\n\"\\x94\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x84\\x01\\x00\\x00\\x82\\x01\\x00\\x00\\x83\\x01\\x00\\x00\\x0C\\x00\\x07\\x00\\x06\\x00\\x00\\x00\\x85\\x01\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x28\\x00\\x00\\x00\\x84\\x01\\x00\\x00\\x54\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x86\\x01\\x00\\x00\\x81\\x01\\x00\\x00\"\n\"\\x85\\x01\\x00\\x00\\x81\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x87\\x01\\x00\\x00\\x7E\\x01\\x00\\x00\\x86\\x01\\x00\\x00\\x8E\\x00\\x05\\x00\\x07\\x00\\x00\\x00\"\n\"\\x88\\x01\\x00\\x00\\x7B\\x01\\x00\\x00\\x87\\x01\\x00\\x00\\x3E\\x00\\x03\\x00\\x7A\\x01\\x00\\x00\\x88\\x01\\x00\\x00\\xF9\\x00\\x02\\x00\\x79\\x01\\x00\\x00\"\n\"\\xF8\\x00\\x02\\x00\\x89\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x8A\\x01\\x00\\x00\\x27\\x01\\x00\\x00\\x41\\x00\\x06\\x00\\x3F\\x00\\x00\\x00\"\n\"\\x8B\\x01\\x00\\x00\\x80\\x00\\x00\\x00\\x7C\\x01\\x00\\x00\\x8E\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x8C\\x01\\x00\\x00\\x8B\\x01\\x00\\x00\"\n\"\\x41\\x00\\x06\\x00\\x3F\\x00\\x00\\x00\\x8D\\x01\\x00\\x00\\x80\\x00\\x00\\x00\\x7C\\x01\\x00\\x00\\x7F\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\"\n\"\\x8E\\x01\\x00\\x00\\x8D\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x8F\\x01\\x00\\x00\\x74\\x01\\x00\\x00\\x94\\x00\\x05\\x00\\x06\\x00\\x00\\x00\"\n\"\\x90\\x01\\x00\\x00\\x65\\x01\\x00\\x00\\x8F\\x01\\x00\\x00\\x0C\\x00\\x07\\x00\\x06\\x00\\x00\\x00\\x91\\x01\\x00\\x00\\x01\\x00\\x00\\x00\\x28\\x00\\x00\\x00\"\n\"\\x90\\x01\\x00\\x00\\x54\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x92\\x01\\x00\\x00\\x8E\\x01\\x00\\x00\\x91\\x01\\x00\\x00\\x81\\x00\\x05\\x00\"\n\"\\x06\\x00\\x00\\x00\\x93\\x01\\x00\\x00\\x8C\\x01\\x00\\x00\\x92\\x01\\x00\\x00\\x8E\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x94\\x01\\x00\\x00\\x8A\\x01\\x00\\x00\"\n\"\\x93\\x01\\x00\\x00\\x3E\\x00\\x03\\x00\\x7A\\x01\\x00\\x00\\x94\\x01\\x00\\x00\\xF9\\x00\\x02\\x00\\x79\\x01\\x00\\x00\\xF8\\x00\\x02\\x00\\x79\\x01\\x00\\x00\"\n\"\\xBA\\x00\\x05\\x00\\x1E\\x00\\x00\\x00\\x96\\x01\\x00\\x00\\x95\\x01\\x00\\x00\\x54\\x00\\x00\\x00\\xF7\\x00\\x03\\x00\\x98\\x01\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\xFA\\x00\\x04\\x00\\x96\\x01\\x00\\x00\\x97\\x01\\x00\\x00\\x98\\x01\\x00\\x00\\xF8\\x00\\x02\\x00\\x97\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x11\\x00\\x00\\x00\"\n\"\\x9B\\x01\\x00\\x00\\x9A\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x13\\x00\\x00\\x00\\x9C\\x01\\x00\\x00\\x1B\\x01\\x00\\x00\\x57\\x00\\x05\\x00\\x2B\\x00\\x00\\x00\"\n\"\\x9D\\x01\\x00\\x00\\x9B\\x01\\x00\\x00\\x9C\\x01\\x00\\x00\\x3E\\x00\\x03\\x00\\x99\\x01\\x00\\x00\\x9D\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\"\n\"\\x9F\\x01\\x00\\x00\\x1F\\x01\\x00\\x00\\x0C\\x00\\x06\\x00\\x07\\x00\\x00\\x00\\xA0\\x01\\x00\\x00\\x01\\x00\\x00\\x00\\x45\\x00\\x00\\x00\\x9F\\x01\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\xA1\\x01\\x00\\x00\\x74\\x01\\x00\\x00\\x81\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\xA2\\x01\\x00\\x00\\xA0\\x01\\x00\\x00\"\n\"\\xA1\\x01\\x00\\x00\\x0C\\x00\\x06\\x00\\x07\\x00\\x00\\x00\\xA3\\x01\\x00\\x00\\x01\\x00\\x00\\x00\\x45\\x00\\x00\\x00\\xA2\\x01\\x00\\x00\\x3E\\x00\\x03\\x00\"\n\"\\x9E\\x01\\x00\\x00\\xA3\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\xA5\\x01\\x00\\x00\\x9E\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\"\n\"\\xA6\\x01\\x00\\x00\\x59\\x01\\x00\\x00\\x94\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\xA7\\x01\\x00\\x00\\xA5\\x01\\x00\\x00\\xA6\\x01\\x00\\x00\\x0C\\x00\\x07\\x00\"\n\"\\x06\\x00\\x00\\x00\\xA8\\x01\\x00\\x00\\x01\\x00\\x00\\x00\\x28\\x00\\x00\\x00\\xA7\\x01\\x00\\x00\\x54\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x29\\x00\\x00\\x00\"\n\"\\xAA\\x01\\x00\\x00\\x99\\x01\\x00\\x00\\x33\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\xAB\\x01\\x00\\x00\\xAA\\x01\\x00\\x00\\x85\\x00\\x05\\x00\"\n\"\\x06\\x00\\x00\\x00\\xAC\\x01\\x00\\x00\\xA9\\x01\\x00\\x00\\xAB\\x01\\x00\\x00\\x0C\\x00\\x07\\x00\\x06\\x00\\x00\\x00\\xAD\\x01\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x1A\\x00\\x00\\x00\\xA8\\x01\\x00\\x00\\xAC\\x01\\x00\\x00\\x3E\\x00\\x03\\x00\\xA4\\x01\\x00\\x00\\xAD\\x01\\x00\\x00\\x41\\x00\\x06\\x00\\x3F\\x00\\x00\\x00\"\n\"\\xAE\\x01\\x00\\x00\\x80\\x00\\x00\\x00\\x7C\\x01\\x00\\x00\\x2F\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\xAF\\x01\\x00\\x00\\xAE\\x01\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\xB0\\x01\\x00\\x00\\xA4\\x01\\x00\\x00\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\xB1\\x01\\x00\\x00\\xAF\\x01\\x00\\x00\"\n\"\\xB0\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x2B\\x00\\x00\\x00\\xB2\\x01\\x00\\x00\\x99\\x01\\x00\\x00\\x4F\\x00\\x08\\x00\\x07\\x00\\x00\\x00\\xB3\\x01\\x00\\x00\"\n\"\\xB2\\x01\\x00\\x00\\xB2\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x8E\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\xB4\\x01\\x00\\x00\"\n\"\\xB3\\x01\\x00\\x00\\xB1\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\xB5\\x01\\x00\\x00\\x7A\\x01\\x00\\x00\\x81\\x00\\x05\\x00\\x07\\x00\\x00\\x00\"\n\"\\xB6\\x01\\x00\\x00\\xB5\\x01\\x00\\x00\\xB4\\x01\\x00\\x00\\x3E\\x00\\x03\\x00\\x7A\\x01\\x00\\x00\\xB6\\x01\\x00\\x00\\xF9\\x00\\x02\\x00\\x98\\x01\\x00\\x00\"\n\"\\xF8\\x00\\x02\\x00\\x98\\x01\\x00\\x00\\xF7\\x00\\x03\\x00\\xBA\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\\xB8\\x01\\x00\\x00\\xB9\\x01\\x00\\x00\"\n\"\\xBA\\x01\\x00\\x00\\xF8\\x00\\x02\\x00\\xB9\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\xBC\\x01\\x00\\x00\\x1F\\x01\\x00\\x00\\x7F\\x00\\x04\\x00\"\n\"\\x07\\x00\\x00\\x00\\xBD\\x01\\x00\\x00\\xBC\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\xBE\\x01\\x00\\x00\\x59\\x01\\x00\\x00\\x0C\\x00\\x07\\x00\"\n\"\\x07\\x00\\x00\\x00\\xBF\\x01\\x00\\x00\\x01\\x00\\x00\\x00\\x47\\x00\\x00\\x00\\xBD\\x01\\x00\\x00\\xBE\\x01\\x00\\x00\\x3E\\x00\\x03\\x00\\xBB\\x01\\x00\\x00\"\n\"\\xBF\\x01\\x00\\x00\\x41\\x00\\x05\\x00\\x29\\x00\\x00\\x00\\xC0\\x01\\x00\\x00\\xBB\\x01\\x00\\x00\\x8E\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\"\n\"\\xC1\\x01\\x00\\x00\\xC0\\x01\\x00\\x00\\x41\\x00\\x05\\x00\\x85\\x00\\x00\\x00\\xC5\\x01\\x00\\x00\\xC4\\x01\\x00\\x00\\x1D\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x07\\x00\\x00\\x00\\xC6\\x01\\x00\\x00\\xC5\\x01\\x00\\x00\\x8E\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\xC7\\x01\\x00\\x00\\xC6\\x01\\x00\\x00\\xC1\\x01\\x00\\x00\"\n\"\\x41\\x00\\x05\\x00\\x29\\x00\\x00\\x00\\xC8\\x01\\x00\\x00\\xBB\\x01\\x00\\x00\\x7F\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\xC9\\x01\\x00\\x00\"\n\"\\xC8\\x01\\x00\\x00\\x41\\x00\\x05\\x00\\x85\\x00\\x00\\x00\\xCA\\x01\\x00\\x00\\xC4\\x01\\x00\\x00\\x81\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\"\n\"\\xCB\\x01\\x00\\x00\\xCA\\x01\\x00\\x00\\x8E\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\xCC\\x01\\x00\\x00\\xCB\\x01\\x00\\x00\\xC9\\x01\\x00\\x00\\x81\\x00\\x05\\x00\"\n\"\\x07\\x00\\x00\\x00\\xCD\\x01\\x00\\x00\\xC7\\x01\\x00\\x00\\xCC\\x01\\x00\\x00\\x41\\x00\\x05\\x00\\x29\\x00\\x00\\x00\\xCE\\x01\\x00\\x00\\xBB\\x01\\x00\\x00\"\n\"\\x2F\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\xCF\\x01\\x00\\x00\\xCE\\x01\\x00\\x00\\x41\\x00\\x05\\x00\\x85\\x00\\x00\\x00\\xD1\\x01\\x00\\x00\"\n\"\\xC4\\x01\\x00\\x00\\xD0\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\xD2\\x01\\x00\\x00\\xD1\\x01\\x00\\x00\\x8E\\x00\\x05\\x00\\x07\\x00\\x00\\x00\"\n\"\\xD3\\x01\\x00\\x00\\xD2\\x01\\x00\\x00\\xCF\\x01\\x00\\x00\\x81\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\xD4\\x01\\x00\\x00\\xCD\\x01\\x00\\x00\\xD3\\x01\\x00\\x00\"\n\"\\x3E\\x00\\x03\\x00\\xBB\\x01\\x00\\x00\\xD4\\x01\\x00\\x00\\x41\\x00\\x05\\x00\\xD5\\x01\\x00\\x00\\xD6\\x01\\x00\\x00\\x3D\\x00\\x00\\x00\\x81\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x38\\x00\\x00\\x00\\xD7\\x01\\x00\\x00\\xD6\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\xD8\\x01\\x00\\x00\\xBB\\x01\\x00\\x00\"\n\"\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\xD9\\x01\\x00\\x00\\xD8\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\xDA\\x01\\x00\\x00\"\n\"\\xD8\\x01\\x00\\x00\\x01\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\xDB\\x01\\x00\\x00\\xD8\\x01\\x00\\x00\\x02\\x00\\x00\\x00\\x50\\x00\\x07\\x00\"\n\"\\x2B\\x00\\x00\\x00\\xDC\\x01\\x00\\x00\\xD9\\x01\\x00\\x00\\xDA\\x01\\x00\\x00\\xDB\\x01\\x00\\x00\\x54\\x00\\x00\\x00\\x91\\x00\\x05\\x00\\x2B\\x00\\x00\\x00\"\n\"\\xDD\\x01\\x00\\x00\\xD7\\x01\\x00\\x00\\xDC\\x01\\x00\\x00\\x4F\\x00\\x08\\x00\\x07\\x00\\x00\\x00\\xDE\\x01\\x00\\x00\\xDD\\x01\\x00\\x00\\xDD\\x01\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\xBB\\x01\\x00\\x00\\xDE\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x11\\x00\\x00\\x00\"\n\"\\xE0\\x01\\x00\\x00\\xDF\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x13\\x00\\x00\\x00\\xE1\\x01\\x00\\x00\\x1B\\x01\\x00\\x00\\x57\\x00\\x05\\x00\\x2B\\x00\\x00\\x00\"\n\"\\xE2\\x01\\x00\\x00\\xE0\\x01\\x00\\x00\\xE1\\x01\\x00\\x00\\x4F\\x00\\x08\\x00\\x07\\x00\\x00\\x00\\xE3\\x01\\x00\\x00\\xE2\\x01\\x00\\x00\\xE2\\x01\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\xE5\\x01\\x00\\x00\\xE8\\x01\\x00\\x00\\xE7\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x07\\x00\\x00\\x00\\xE9\\x01\\x00\\x00\\xBB\\x01\\x00\\x00\\x57\\x00\\x05\\x00\\x2B\\x00\\x00\\x00\\xEA\\x01\\x00\\x00\\xE8\\x01\\x00\\x00\\xE9\\x01\\x00\\x00\"\n\"\\x4F\\x00\\x08\\x00\\x07\\x00\\x00\\x00\\xEB\\x01\\x00\\x00\\xEA\\x01\\x00\\x00\\xEA\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x02\\x00\\x00\\x00\"\n\"\\x85\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\xEC\\x01\\x00\\x00\\xE3\\x01\\x00\\x00\\xEB\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\xED\\x01\\x00\\x00\"\n\"\\x7A\\x01\\x00\\x00\\x81\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\xEE\\x01\\x00\\x00\\xED\\x01\\x00\\x00\\xEC\\x01\\x00\\x00\\x3E\\x00\\x03\\x00\\x7A\\x01\\x00\\x00\"\n\"\\xEE\\x01\\x00\\x00\\xF9\\x00\\x02\\x00\\xBA\\x01\\x00\\x00\\xF8\\x00\\x02\\x00\\xBA\\x01\\x00\\x00\\xF7\\x00\\x03\\x00\\xF2\\x01\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\xFA\\x00\\x04\\x00\\xF0\\x01\\x00\\x00\\xF1\\x01\\x00\\x00\\xF2\\x01\\x00\\x00\\xF8\\x00\\x02\\x00\\xF1\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\xE5\\x01\\x00\\x00\"\n\"\\xF4\\x01\\x00\\x00\\xF3\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x2B\\x00\\x00\\x00\\xF6\\x01\\x00\\x00\\xF5\\x01\\x00\\x00\\x4F\\x00\\x08\\x00\\x07\\x00\\x00\\x00\"\n\"\\xF7\\x01\\x00\\x00\\xF6\\x01\\x00\\x00\\xF6\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x57\\x00\\x05\\x00\\x2B\\x00\\x00\\x00\"\n\"\\xF8\\x01\\x00\\x00\\xF4\\x01\\x00\\x00\\xF7\\x01\\x00\\x00\\x4F\\x00\\x08\\x00\\x07\\x00\\x00\\x00\\xF9\\x01\\x00\\x00\\xF8\\x01\\x00\\x00\\xF8\\x01\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x02\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\xFA\\x01\\x00\\x00\\x7A\\x01\\x00\\x00\\x85\\x00\\x05\\x00\"\n\"\\x07\\x00\\x00\\x00\\xFB\\x01\\x00\\x00\\xFA\\x01\\x00\\x00\\xF9\\x01\\x00\\x00\\x3E\\x00\\x03\\x00\\x7A\\x01\\x00\\x00\\xFB\\x01\\x00\\x00\\xF9\\x00\\x02\\x00\"\n\"\\xF2\\x01\\x00\\x00\\xF8\\x00\\x02\\x00\\xF2\\x01\\x00\\x00\\xF7\\x00\\x03\\x00\\xFF\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\\xFD\\x01\\x00\\x00\"\n\"\\xFE\\x01\\x00\\x00\\xFF\\x01\\x00\\x00\\xF8\\x00\\x02\\x00\\xFE\\x01\\x00\\x00\\x41\\x00\\x05\\x00\\x30\\x00\\x00\\x00\\x00\\x02\\x00\\x00\\xF5\\x01\\x00\\x00\"\n\"\\x33\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x01\\x02\\x00\\x00\\x00\\x02\\x00\\x00\\xB8\\x00\\x05\\x00\\x1E\\x00\\x00\\x00\\x02\\x02\\x00\\x00\"\n\"\\x01\\x02\\x00\\x00\\x54\\x00\\x00\\x00\\xF7\\x00\\x03\\x00\\x04\\x02\\x00\\x00\\x00\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\\x02\\x02\\x00\\x00\\x03\\x02\\x00\\x00\"\n\"\\x04\\x02\\x00\\x00\\xF8\\x00\\x02\\x00\\x03\\x02\\x00\\x00\\xFC\\x00\\x01\\x00\\xF8\\x00\\x02\\x00\\x04\\x02\\x00\\x00\\x3D\\x00\\x04\\x00\\x2B\\x00\\x00\\x00\"\n\"\\x07\\x02\\x00\\x00\\xF5\\x01\\x00\\x00\\x4F\\x00\\x07\\x00\\x13\\x00\\x00\\x00\\x08\\x02\\x00\\x00\\x07\\x02\\x00\\x00\\x07\\x02\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x30\\x00\\x00\\x00\\x09\\x02\\x00\\x00\\xF5\\x01\\x00\\x00\\x33\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\"\n\"\\x0A\\x02\\x00\\x00\\x09\\x02\\x00\\x00\\x50\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x0B\\x02\\x00\\x00\\x0A\\x02\\x00\\x00\\x0A\\x02\\x00\\x00\\x88\\x00\\x05\\x00\"\n\"\\x13\\x00\\x00\\x00\\x0C\\x02\\x00\\x00\\x08\\x02\\x00\\x00\\x0B\\x02\\x00\\x00\\x3E\\x00\\x03\\x00\\x06\\x02\\x00\\x00\\x0C\\x02\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x13\\x00\\x00\\x00\\x0D\\x02\\x00\\x00\\x06\\x02\\x00\\x00\\x3D\\x00\\x04\\x00\\x13\\x00\\x00\\x00\\x0E\\x02\\x00\\x00\\x06\\x02\\x00\\x00\\x94\\x00\\x05\\x00\"\n\"\\x06\\x00\\x00\\x00\\x0F\\x02\\x00\\x00\\x0D\\x02\\x00\\x00\\x0E\\x02\\x00\\x00\\x83\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x10\\x02\\x00\\x00\\x66\\x00\\x00\\x00\"\n\"\\x0F\\x02\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x11\\x02\\x00\\x00\\x66\\x01\\x00\\x00\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x12\\x02\\x00\\x00\"\n\"\\x11\\x02\\x00\\x00\\x10\\x02\\x00\\x00\\x3E\\x00\\x03\\x00\\x66\\x01\\x00\\x00\\x12\\x02\\x00\\x00\\xF9\\x00\\x02\\x00\\xFF\\x01\\x00\\x00\\xF8\\x00\\x02\\x00\"\n\"\\xFF\\x01\\x00\\x00\\x39\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x13\\x02\\x00\\x00\\x0E\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x14\\x02\\x00\\x00\"\n\"\\x66\\x01\\x00\\x00\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x15\\x02\\x00\\x00\\x14\\x02\\x00\\x00\\x13\\x02\\x00\\x00\\x3E\\x00\\x03\\x00\\x66\\x01\\x00\\x00\"\n\"\\x15\\x02\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x18\\x02\\x00\\x00\\x7A\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x19\\x02\\x00\\x00\"\n\"\\x66\\x01\\x00\\x00\\x8E\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\x1A\\x02\\x00\\x00\\x18\\x02\\x00\\x00\\x19\\x02\\x00\\x00\\x41\\x00\\x05\\x00\\x82\\x00\\x00\\x00\"\n\"\\x1C\\x02\\x00\\x00\\x80\\x00\\x00\\x00\\x1B\\x02\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x1D\\x02\\x00\\x00\\x1C\\x02\\x00\\x00\\x85\\x00\\x05\\x00\"\n\"\\x07\\x00\\x00\\x00\\x1E\\x02\\x00\\x00\\x1A\\x02\\x00\\x00\\x1D\\x02\\x00\\x00\\x3E\\x00\\x03\\x00\\x1F\\x02\\x00\\x00\\x1E\\x02\\x00\\x00\\x39\\x00\\x05\\x00\"\n\"\\x07\\x00\\x00\\x00\\x20\\x02\\x00\\x00\\x0B\\x00\\x00\\x00\\x1F\\x02\\x00\\x00\\x3D\\x00\\x04\\x00\\x2B\\x00\\x00\\x00\\x21\\x02\\x00\\x00\\x17\\x02\\x00\\x00\"\n\"\\x4F\\x00\\x09\\x00\\x2B\\x00\\x00\\x00\\x22\\x02\\x00\\x00\\x21\\x02\\x00\\x00\\x20\\x02\\x00\\x00\\x04\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x06\\x00\\x00\\x00\"\n\"\\x03\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x17\\x02\\x00\\x00\\x22\\x02\\x00\\x00\\x41\\x00\\x05\\x00\\x23\\x02\\x00\\x00\\x24\\x02\\x00\\x00\\x17\\x02\\x00\\x00\"\n\"\\x33\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x24\\x02\\x00\\x00\\x66\\x00\\x00\\x00\\xFD\\x00\\x01\\x00\\x38\\x00\\x01\\x00\\x36\\x00\\x05\\x00\\x07\\x00\\x00\\x00\"\n\"\\x0B\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x09\\x00\\x00\\x00\\x37\\x00\\x03\\x00\\x08\\x00\\x00\\x00\\x0A\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x0C\\x00\\x00\\x00\"\n\"\\x3B\\x00\\x04\\x00\\x29\\x00\\x00\\x00\\x2A\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x29\\x00\\x00\\x00\\x37\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\xF7\\x00\\x03\\x00\\x22\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\\x20\\x00\\x00\\x00\\x21\\x00\\x00\\x00\\x22\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\"\n\"\\x21\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x23\\x00\\x00\\x00\\x0A\\x00\\x00\\x00\\xFE\\x00\\x02\\x00\\x23\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\"\n\"\\x22\\x00\\x00\\x00\\xF7\\x00\\x03\\x00\\x28\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\\x26\\x00\\x00\\x00\\x27\\x00\\x00\\x00\\x4B\\x00\\x00\\x00\"\n\"\\xF8\\x00\\x02\\x00\\x27\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x30\\x00\\x00\\x00\\x31\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\\x2F\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x06\\x00\\x00\\x00\\x32\\x00\\x00\\x00\\x31\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x30\\x00\\x00\\x00\\x34\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\\x33\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x35\\x00\\x00\\x00\\x34\\x00\\x00\\x00\\x88\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x36\\x00\\x00\\x00\\x32\\x00\\x00\\x00\"\n\"\\x35\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x2A\\x00\\x00\\x00\\x36\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x3F\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x3D\\x00\\x00\\x00\"\n\"\\x3E\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x41\\x00\\x00\\x00\\x40\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x42\\x00\\x00\\x00\"\n\"\\x2A\\x00\\x00\\x00\\x83\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x43\\x00\\x00\\x00\\x41\\x00\\x00\\x00\\x42\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x3F\\x00\\x00\\x00\"\n\"\\x44\\x00\\x00\\x00\\x3D\\x00\\x00\\x00\\x3E\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x45\\x00\\x00\\x00\\x44\\x00\\x00\\x00\\x41\\x00\\x05\\x00\"\n\"\\x3F\\x00\\x00\\x00\\x47\\x00\\x00\\x00\\x3D\\x00\\x00\\x00\\x46\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x48\\x00\\x00\\x00\\x47\\x00\\x00\\x00\"\n\"\\x83\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x49\\x00\\x00\\x00\\x45\\x00\\x00\\x00\\x48\\x00\\x00\\x00\\x88\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x4A\\x00\\x00\\x00\"\n\"\\x43\\x00\\x00\\x00\\x49\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x37\\x00\\x00\\x00\\x4A\\x00\\x00\\x00\\xF9\\x00\\x02\\x00\\x28\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\"\n\"\\x4B\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x3F\\x00\\x00\\x00\\x4C\\x00\\x00\\x00\\x3D\\x00\\x00\\x00\\x3E\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\"\n\"\\x4D\\x00\\x00\\x00\\x4C\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x30\\x00\\x00\\x00\\x4E\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\\x2F\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x06\\x00\\x00\\x00\\x4F\\x00\\x00\\x00\\x4E\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x50\\x00\\x00\\x00\\x4D\\x00\\x00\\x00\\x4F\\x00\\x00\\x00\"\n\"\\x41\\x00\\x05\\x00\\x30\\x00\\x00\\x00\\x51\\x00\\x00\\x00\\x2D\\x00\\x00\\x00\\x33\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x52\\x00\\x00\\x00\"\n\"\\x51\\x00\\x00\\x00\\x88\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x53\\x00\\x00\\x00\\x50\\x00\\x00\\x00\\x52\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x2A\\x00\\x00\\x00\"\n\"\\x53\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x55\\x00\\x00\\x00\\x2A\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x3F\\x00\\x00\\x00\\x56\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x00\\x00\\x46\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x57\\x00\\x00\\x00\\x56\\x00\\x00\\x00\\x83\\x00\\x05\\x00\\x06\\x00\\x00\\x00\"\n\"\\x58\\x00\\x00\\x00\\x55\\x00\\x00\\x00\\x57\\x00\\x00\\x00\\x0C\\x00\\x07\\x00\\x06\\x00\\x00\\x00\\x59\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x28\\x00\\x00\\x00\"\n\"\\x54\\x00\\x00\\x00\\x58\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x2A\\x00\\x00\\x00\\x59\\x00\\x00\\x00\\xF7\\x00\\x03\\x00\\x5D\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\xFA\\x00\\x04\\x00\\x5B\\x00\\x00\\x00\\x5C\\x00\\x00\\x00\\x5D\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x5C\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\"\n\"\\x5E\\x00\\x00\\x00\\x2A\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x5F\\x00\\x00\\x00\\x2A\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\"\n\"\\x60\\x00\\x00\\x00\\x5F\\x00\\x00\\x00\\x5E\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x2A\\x00\\x00\\x00\\x60\\x00\\x00\\x00\\xF9\\x00\\x02\\x00\\x5D\\x00\\x00\\x00\"\n\"\\xF8\\x00\\x02\\x00\\x5D\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x61\\x00\\x00\\x00\\x2A\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\"\n\"\\x63\\x00\\x00\\x00\\x61\\x00\\x00\\x00\\x62\\x00\\x00\\x00\\x7F\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x64\\x00\\x00\\x00\\x63\\x00\\x00\\x00\\x0C\\x00\\x06\\x00\"\n\"\\x06\\x00\\x00\\x00\\x65\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x1D\\x00\\x00\\x00\\x64\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x37\\x00\\x00\\x00\\x65\\x00\\x00\\x00\"\n\"\\xF9\\x00\\x02\\x00\\x28\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x28\\x00\\x00\\x00\\x41\\x00\\x06\\x00\\x3F\\x00\\x00\\x00\\x68\\x00\\x00\\x00\\x3D\\x00\\x00\\x00\"\n\"\\x67\\x00\\x00\\x00\\x33\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x69\\x00\\x00\\x00\\x68\\x00\\x00\\x00\\x83\\x00\\x05\\x00\\x06\\x00\\x00\\x00\"\n\"\\x6A\\x00\\x00\\x00\\x66\\x00\\x00\\x00\\x69\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x6B\\x00\\x00\\x00\\x37\\x00\\x00\\x00\\x0C\\x00\\x08\\x00\"\n\"\\x06\\x00\\x00\\x00\\x6C\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x2B\\x00\\x00\\x00\\x6B\\x00\\x00\\x00\\x54\\x00\\x00\\x00\\x66\\x00\\x00\\x00\\x41\\x00\\x06\\x00\"\n\"\\x3F\\x00\\x00\\x00\\x6D\\x00\\x00\\x00\\x3D\\x00\\x00\\x00\\x67\\x00\\x00\\x00\\x33\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x6E\\x00\\x00\\x00\"\n\"\\x6D\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x6F\\x00\\x00\\x00\\x6C\\x00\\x00\\x00\\x6E\\x00\\x00\\x00\\x81\\x00\\x05\\x00\\x06\\x00\\x00\\x00\"\n\"\\x70\\x00\\x00\\x00\\x6A\\x00\\x00\\x00\\x6F\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x37\\x00\\x00\\x00\\x70\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\"\n\"\\x71\\x00\\x00\\x00\\x0A\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\x72\\x00\\x00\\x00\\x37\\x00\\x00\\x00\\x8E\\x00\\x05\\x00\\x07\\x00\\x00\\x00\"\n\"\\x73\\x00\\x00\\x00\\x71\\x00\\x00\\x00\\x72\\x00\\x00\\x00\\xFE\\x00\\x02\\x00\\x73\\x00\\x00\\x00\\x38\\x00\\x01\\x00\\x36\\x00\\x05\\x00\\x06\\x00\\x00\\x00\"\n\"\\x0E\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x0D\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x0F\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x79\\x00\\x00\\x00\\x7C\\x00\\x00\\x00\"\n\"\\x7B\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x82\\x00\\x00\\x00\\x83\\x00\\x00\\x00\\x80\\x00\\x00\\x00\\x81\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\"\n\"\\x84\\x00\\x00\\x00\\x83\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x87\\x00\\x00\\x00\\x86\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x82\\x00\\x00\\x00\"\n\"\\x88\\x00\\x00\\x00\\x80\\x00\\x00\\x00\\x81\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x89\\x00\\x00\\x00\\x88\\x00\\x00\\x00\\x83\\x00\\x05\\x00\"\n\"\\x07\\x00\\x00\\x00\\x8A\\x00\\x00\\x00\\x87\\x00\\x00\\x00\\x89\\x00\\x00\\x00\\x79\\x11\\x09\\x00\\x78\\x00\\x00\\x00\\x7C\\x00\\x00\\x00\\x39\\x00\\x00\\x00\"\n\"\\x7D\\x00\\x00\\x00\\x84\\x00\\x00\\x00\\x54\\x00\\x00\\x00\\x8A\\x00\\x00\\x00\\x66\\x00\\x00\\x00\\x7D\\x11\\x04\\x00\\x1E\\x00\\x00\\x00\\x8B\\x00\\x00\\x00\"\n\"\\x78\\x00\\x00\\x00\\x7F\\x11\\x05\\x00\\x2E\\x00\\x00\\x00\\x8D\\x00\\x00\\x00\\x78\\x00\\x00\\x00\\x81\\x00\\x00\\x00\\xAA\\x00\\x05\\x00\\x1E\\x00\\x00\\x00\"\n\"\\x8F\\x00\\x00\\x00\\x8D\\x00\\x00\\x00\\x8E\\x00\\x00\\x00\\xA9\\x00\\x06\\x00\\x06\\x00\\x00\\x00\\x90\\x00\\x00\\x00\\x8F\\x00\\x00\\x00\\x66\\x00\\x00\\x00\"\n\"\\x54\\x00\\x00\\x00\\xFE\\x00\\x02\\x00\\x90\\x00\\x00\\x00\\x38\\x00\\x01\\x00\\x36\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x19\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x15\\x00\\x00\\x00\\x37\\x00\\x03\\x00\\x12\\x00\\x00\\x00\\x16\\x00\\x00\\x00\\x37\\x00\\x03\\x00\\x14\\x00\\x00\\x00\\x17\\x00\\x00\\x00\\x37\\x00\\x03\\x00\"\n\"\\x08\\x00\\x00\\x00\\x18\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x1A\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x08\\x00\\x00\\x00\\x96\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x3B\\x00\\x04\\x00\\x08\\x00\\x00\\x00\\xA3\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x29\\x00\\x00\\x00\\xAF\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x3B\\x00\\x04\\x00\\x29\\x00\\x00\\x00\\xC5\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\x3B\\x00\\x04\\x00\\x14\\x00\\x00\\x00\\xEC\\x00\\x00\\x00\\x07\\x00\\x00\\x00\"\n\"\\x3B\\x00\\x04\\x00\\x14\\x00\\x00\\x00\\xF3\\x00\\x00\\x00\\x07\\x00\\x00\\x00\\xF7\\x00\\x03\\x00\\x95\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\"\n\"\\x93\\x00\\x00\\x00\\x94\\x00\\x00\\x00\\xE7\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\x94\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\x97\\x00\\x00\\x00\"\n\"\\x18\\x00\\x00\\x00\\x0C\\x00\\x06\\x00\\x07\\x00\\x00\\x00\\x98\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x45\\x00\\x00\\x00\\x97\\x00\\x00\\x00\\x4F\\x00\\x07\\x00\"\n\"\\x13\\x00\\x00\\x00\\x99\\x00\\x00\\x00\\x98\\x00\\x00\\x00\\x98\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x8E\\x00\\x05\\x00\\x13\\x00\\x00\\x00\"\n\"\\x9B\\x00\\x00\\x00\\x99\\x00\\x00\\x00\\x9A\\x00\\x00\\x00\\x8E\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x9C\\x00\\x00\\x00\\x9B\\x00\\x00\\x00\\x66\\x00\\x00\\x00\"\n\"\\x85\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x9F\\x00\\x00\\x00\\x9C\\x00\\x00\\x00\\x9E\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\xA0\\x00\\x00\\x00\"\n\"\\x9F\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\xA1\\x00\\x00\\x00\\x9F\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x50\\x00\\x06\\x00\"\n\"\\x07\\x00\\x00\\x00\\xA2\\x00\\x00\\x00\\xA0\\x00\\x00\\x00\\xA1\\x00\\x00\\x00\\x9D\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x96\\x00\\x00\\x00\\xA2\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x13\\x00\\x00\\x00\\xA4\\x00\\x00\\x00\\x17\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\xA5\\x00\\x00\\x00\\xA4\\x00\\x00\\x00\"\n\"\\x00\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\xA6\\x00\\x00\\x00\\xA4\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x50\\x00\\x05\\x00\\x13\\x00\\x00\\x00\"\n\"\\xA7\\x00\\x00\\x00\\xA5\\x00\\x00\\x00\\xA6\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\xA8\\x00\\x00\\x00\\xA7\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\xA9\\x00\\x00\\x00\\xA7\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x50\\x00\\x06\\x00\\x07\\x00\\x00\\x00\\xAA\\x00\\x00\\x00\"\n\"\\xA8\\x00\\x00\\x00\\xA9\\x00\\x00\\x00\\x66\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\xA3\\x00\\x00\\x00\\xAA\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\"\n\"\\xAC\\x00\\x00\\x00\\x96\\x00\\x00\\x00\\x50\\x00\\x06\\x00\\x07\\x00\\x00\\x00\\xAD\\x00\\x00\\x00\\xAB\\x00\\x00\\x00\\xAB\\x00\\x00\\x00\\xAB\\x00\\x00\\x00\"\n\"\\x88\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\xAE\\x00\\x00\\x00\\xAC\\x00\\x00\\x00\\xAD\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\x96\\x00\\x00\\x00\\xAE\\x00\\x00\\x00\"\n\"\\x3E\\x00\\x03\\x00\\xAF\\x00\\x00\\x00\\x66\\x00\\x00\\x00\\xF9\\x00\\x02\\x00\\xB0\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\xB0\\x00\\x00\\x00\\xF6\\x00\\x04\\x00\"\n\"\\xB2\\x00\\x00\\x00\\xB3\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xF9\\x00\\x02\\x00\\xB4\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\xB4\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x06\\x00\\x00\\x00\\xB5\\x00\\x00\\x00\\xAF\\x00\\x00\\x00\\xB8\\x00\\x05\\x00\\x1E\\x00\\x00\\x00\\xB6\\x00\\x00\\x00\\xB5\\x00\\x00\\x00\\xAB\\x00\\x00\\x00\"\n\"\\xFA\\x00\\x04\\x00\\xB6\\x00\\x00\\x00\\xB1\\x00\\x00\\x00\\xB2\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\xB1\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\"\n\"\\xB7\\x00\\x00\\x00\\x96\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x11\\x00\\x00\\x00\\xB8\\x00\\x00\\x00\\x16\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\"\n\"\\xB9\\x00\\x00\\x00\\xA3\\x00\\x00\\x00\\x4F\\x00\\x07\\x00\\x13\\x00\\x00\\x00\\xBA\\x00\\x00\\x00\\xB9\\x00\\x00\\x00\\xB9\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\x57\\x00\\x05\\x00\\x2B\\x00\\x00\\x00\\xBB\\x00\\x00\\x00\\xB8\\x00\\x00\\x00\\xBA\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\"\n\"\\xBC\\x00\\x00\\x00\\xBB\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x29\\x00\\x00\\x00\\xBD\\x00\\x00\\x00\\xA3\\x00\\x00\\x00\\x2F\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\xBE\\x00\\x00\\x00\\xBD\\x00\\x00\\x00\\x0C\\x00\\x07\\x00\\x06\\x00\\x00\\x00\\xBF\\x00\\x00\\x00\\x01\\x00\\x00\\x00\"\n\"\\x30\\x00\\x00\\x00\\xBC\\x00\\x00\\x00\\xBE\\x00\\x00\\x00\\x8E\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\xC0\\x00\\x00\\x00\\xB7\\x00\\x00\\x00\\xBF\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\xC1\\x00\\x00\\x00\\xA3\\x00\\x00\\x00\\x81\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\xC2\\x00\\x00\\x00\\xC1\\x00\\x00\\x00\"\n\"\\xC0\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\xA3\\x00\\x00\\x00\\xC2\\x00\\x00\\x00\\xF9\\x00\\x02\\x00\\xB3\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\xB3\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\xC3\\x00\\x00\\x00\\xAF\\x00\\x00\\x00\\x81\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\xC4\\x00\\x00\\x00\\xC3\\x00\\x00\\x00\"\n\"\\x66\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\xAF\\x00\\x00\\x00\\xC4\\x00\\x00\\x00\\xF9\\x00\\x02\\x00\\xB0\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\xB2\\x00\\x00\\x00\"\n\"\\x3E\\x00\\x03\\x00\\xAF\\x00\\x00\\x00\\x54\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\xC5\\x00\\x00\\x00\\x66\\x00\\x00\\x00\\xF9\\x00\\x02\\x00\\xC6\\x00\\x00\\x00\"\n\"\\xF8\\x00\\x02\\x00\\xC6\\x00\\x00\\x00\\xF6\\x00\\x04\\x00\\xC8\\x00\\x00\\x00\\xC9\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xF9\\x00\\x02\\x00\\xCA\\x00\\x00\\x00\"\n\"\\xF8\\x00\\x02\\x00\\xCA\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\xCB\\x00\\x00\\x00\\xAF\\x00\\x00\\x00\\xB8\\x00\\x05\\x00\\x1E\\x00\\x00\\x00\"\n\"\\xCD\\x00\\x00\\x00\\xCB\\x00\\x00\\x00\\xCC\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\\xCD\\x00\\x00\\x00\\xC7\\x00\\x00\\x00\\xC8\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\"\n\"\\xC7\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\xCE\\x00\\x00\\x00\\x96\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x11\\x00\\x00\\x00\\xCF\\x00\\x00\\x00\"\n\"\\x16\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\xD0\\x00\\x00\\x00\\xA3\\x00\\x00\\x00\\x4F\\x00\\x07\\x00\\x13\\x00\\x00\\x00\\xD1\\x00\\x00\\x00\"\n\"\\xD0\\x00\\x00\\x00\\xD0\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x57\\x00\\x05\\x00\\x2B\\x00\\x00\\x00\\xD2\\x00\\x00\\x00\\xCF\\x00\\x00\\x00\"\n\"\\xD1\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\xD3\\x00\\x00\\x00\\xD2\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x41\\x00\\x05\\x00\\x29\\x00\\x00\\x00\"\n\"\\xD4\\x00\\x00\\x00\\xA3\\x00\\x00\\x00\\x2F\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\xD5\\x00\\x00\\x00\\xD4\\x00\\x00\\x00\\x0C\\x00\\x07\\x00\"\n\"\\x06\\x00\\x00\\x00\\xD6\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x30\\x00\\x00\\x00\\xD3\\x00\\x00\\x00\\xD5\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\"\n\"\\xD7\\x00\\x00\\x00\\xC5\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\xD8\\x00\\x00\\x00\\xD6\\x00\\x00\\x00\\xD7\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x06\\x00\\x00\\x00\\xDA\\x00\\x00\\x00\\xC5\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\xDB\\x00\\x00\\x00\\xD9\\x00\\x00\\x00\\xDA\\x00\\x00\\x00\"\n\"\\x83\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\xDC\\x00\\x00\\x00\\xD8\\x00\\x00\\x00\\xDB\\x00\\x00\\x00\\x8E\\x00\\x05\\x00\\x07\\x00\\x00\\x00\\xDD\\x00\\x00\\x00\"\n\"\\xCE\\x00\\x00\\x00\\xDC\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\xDE\\x00\\x00\\x00\\xA3\\x00\\x00\\x00\\x81\\x00\\x05\\x00\\x07\\x00\\x00\\x00\"\n\"\\xDF\\x00\\x00\\x00\\xDE\\x00\\x00\\x00\\xDD\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\xA3\\x00\\x00\\x00\\xDF\\x00\\x00\\x00\\xF9\\x00\\x02\\x00\\xC9\\x00\\x00\\x00\"\n\"\\xF8\\x00\\x02\\x00\\xC9\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\\xE0\\x00\\x00\\x00\\xAF\\x00\\x00\\x00\\x81\\x00\\x05\\x00\\x06\\x00\\x00\\x00\"\n\"\\xE1\\x00\\x00\\x00\\xE0\\x00\\x00\\x00\\x66\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\xAF\\x00\\x00\\x00\\xE1\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x06\\x00\\x00\\x00\"\n\"\\xE2\\x00\\x00\\x00\\xC5\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\xE3\\x00\\x00\\x00\\xE2\\x00\\x00\\x00\\xD9\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\"\n\"\\xC5\\x00\\x00\\x00\\xE3\\x00\\x00\\x00\\xF9\\x00\\x02\\x00\\xC6\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\xC8\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\"\n\"\\xE4\\x00\\x00\\x00\\xA3\\x00\\x00\\x00\\x4F\\x00\\x07\\x00\\x13\\x00\\x00\\x00\\xE5\\x00\\x00\\x00\\xE4\\x00\\x00\\x00\\xE4\\x00\\x00\\x00\\x00\\x00\\x00\\x00\"\n\"\\x01\\x00\\x00\\x00\\xFE\\x00\\x02\\x00\\xE5\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\xE7\\x00\\x00\\x00\\xBA\\x00\\x05\\x00\\x1E\\x00\\x00\\x00\\xE9\\x00\\x00\\x00\"\n\"\\xE8\\x00\\x00\\x00\\x54\\x00\\x00\\x00\\xF7\\x00\\x03\\x00\\xEB\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xFA\\x00\\x04\\x00\\xE9\\x00\\x00\\x00\\xEA\\x00\\x00\\x00\"\n\"\\xEB\\x00\\x00\\x00\\xF8\\x00\\x02\\x00\\xEA\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x07\\x00\\x00\\x00\\xED\\x00\\x00\\x00\\x18\\x00\\x00\\x00\\x0C\\x00\\x06\\x00\"\n\"\\x07\\x00\\x00\\x00\\xEE\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x45\\x00\\x00\\x00\\xED\\x00\\x00\\x00\\x4F\\x00\\x07\\x00\\x13\\x00\\x00\\x00\\xEF\\x00\\x00\\x00\"\n\"\\xEE\\x00\\x00\\x00\\xEE\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x8E\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\xF0\\x00\\x00\\x00\\xEF\\x00\\x00\\x00\"\n\"\\x9A\\x00\\x00\\x00\\x8E\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\xF1\\x00\\x00\\x00\\xF0\\x00\\x00\\x00\\x66\\x00\\x00\\x00\\x85\\x00\\x05\\x00\\x13\\x00\\x00\\x00\"\n\"\\xF2\\x00\\x00\\x00\\xF1\\x00\\x00\\x00\\x9E\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\xEC\\x00\\x00\\x00\\xF2\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x13\\x00\\x00\\x00\"\n\"\\xF4\\x00\\x00\\x00\\x17\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\xF3\\x00\\x00\\x00\\xF4\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x13\\x00\\x00\\x00\\xF5\\x00\\x00\\x00\"\n\"\\xEC\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x13\\x00\\x00\\x00\\xF6\\x00\\x00\\x00\\xF3\\x00\\x00\\x00\\x81\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\xF7\\x00\\x00\\x00\"\n\"\\xF6\\x00\\x00\\x00\\xF5\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\xF3\\x00\\x00\\x00\\xF7\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x13\\x00\\x00\\x00\\xF9\\x00\\x00\\x00\"\n\"\\xEC\\x00\\x00\\x00\\x8E\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\xFA\\x00\\x00\\x00\\xF9\\x00\\x00\\x00\\xF8\\x00\\x00\\x00\\x3E\\x00\\x03\\x00\\xEC\\x00\\x00\\x00\"\n\"\\xFA\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x13\\x00\\x00\\x00\\xFB\\x00\\x00\\x00\\xEC\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x11\\x00\\x00\\x00\\xFC\\x00\\x00\\x00\"\n\"\\x16\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x13\\x00\\x00\\x00\\xFD\\x00\\x00\\x00\\xF3\\x00\\x00\\x00\\x57\\x00\\x05\\x00\\x2B\\x00\\x00\\x00\\xFE\\x00\\x00\\x00\"\n\"\\xFC\\x00\\x00\\x00\\xFD\\x00\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\xFF\\x00\\x00\\x00\\xFE\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x8E\\x00\\x05\\x00\"\n\"\\x13\\x00\\x00\\x00\\x00\\x01\\x00\\x00\\xFB\\x00\\x00\\x00\\xFF\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x13\\x00\\x00\\x00\\x01\\x01\\x00\\x00\\xF3\\x00\\x00\\x00\"\n\"\\x83\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x02\\x01\\x00\\x00\\x01\\x01\\x00\\x00\\x00\\x01\\x00\\x00\\x3E\\x00\\x03\\x00\\xF3\\x00\\x00\\x00\\x02\\x01\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x13\\x00\\x00\\x00\\x03\\x01\\x00\\x00\\xEC\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x11\\x00\\x00\\x00\\x04\\x01\\x00\\x00\\x16\\x00\\x00\\x00\"\n\"\\x3D\\x00\\x04\\x00\\x13\\x00\\x00\\x00\\x05\\x01\\x00\\x00\\xF3\\x00\\x00\\x00\\x57\\x00\\x05\\x00\\x2B\\x00\\x00\\x00\\x06\\x01\\x00\\x00\\x04\\x01\\x00\\x00\"\n\"\\x05\\x01\\x00\\x00\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x07\\x01\\x00\\x00\\x06\\x01\\x00\\x00\\x03\\x00\\x00\\x00\\x8E\\x00\\x05\\x00\\x13\\x00\\x00\\x00\"\n\"\\x08\\x01\\x00\\x00\\x03\\x01\\x00\\x00\\x07\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x13\\x00\\x00\\x00\\x09\\x01\\x00\\x00\\xF3\\x00\\x00\\x00\\x83\\x00\\x05\\x00\"\n\"\\x13\\x00\\x00\\x00\\x0A\\x01\\x00\\x00\\x09\\x01\\x00\\x00\\x08\\x01\\x00\\x00\\x3E\\x00\\x03\\x00\\xF3\\x00\\x00\\x00\\x0A\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x13\\x00\\x00\\x00\\x0B\\x01\\x00\\x00\\xEC\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x11\\x00\\x00\\x00\\x0C\\x01\\x00\\x00\\x16\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\"\n\"\\x13\\x00\\x00\\x00\\x0D\\x01\\x00\\x00\\xF3\\x00\\x00\\x00\\x57\\x00\\x05\\x00\\x2B\\x00\\x00\\x00\\x0E\\x01\\x00\\x00\\x0C\\x01\\x00\\x00\\x0D\\x01\\x00\\x00\"\n\"\\x51\\x00\\x05\\x00\\x06\\x00\\x00\\x00\\x0F\\x01\\x00\\x00\\x0E\\x01\\x00\\x00\\x03\\x00\\x00\\x00\\x8E\\x00\\x05\\x00\\x13\\x00\\x00\\x00\\x10\\x01\\x00\\x00\"\n\"\\x0B\\x01\\x00\\x00\\x0F\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x13\\x00\\x00\\x00\\x11\\x01\\x00\\x00\\xF3\\x00\\x00\\x00\\x83\\x00\\x05\\x00\\x13\\x00\\x00\\x00\"\n\"\\x12\\x01\\x00\\x00\\x11\\x01\\x00\\x00\\x10\\x01\\x00\\x00\\x3E\\x00\\x03\\x00\\xF3\\x00\\x00\\x00\\x12\\x01\\x00\\x00\\x3D\\x00\\x04\\x00\\x13\\x00\\x00\\x00\"\n\"\\x13\\x01\\x00\\x00\\xF3\\x00\\x00\\x00\\xFE\\x00\\x02\\x00\\x13\\x01\\x00\\x00\\xF8\\x00\\x02\\x00\\xEB\\x00\\x00\\x00\\xF9\\x00\\x02\\x00\\x95\\x00\\x00\\x00\"\n\"\\xF8\\x00\\x02\\x00\\x95\\x00\\x00\\x00\\x3D\\x00\\x04\\x00\\x13\\x00\\x00\\x00\\x15\\x01\\x00\\x00\\x17\\x00\\x00\\x00\\xFE\\x00\\x02\\x00\\x15\\x01\\x00\\x00\"\n\"\\x38\\x00\\x01\\x00\"},\n#endif\n"
  },
  {
    "path": "engine/gl/shader.h",
    "content": "/*\nCopyright spike. GNU GPL V2. etc.\nMuch of this file and the parser derives originally from qfusion by vic.\n\nQuake1 rendering works by:\nDraw everything in depth order and stall lots from switching textures.\ndraw transparent water surfaces last.\n\nQuake3 rendering works by:\ngenerate a batch for every model+shader in the world.\nsort batches by shader sort key, entity, shader.\ndraw surfaces.\n\nDoom3 rendering works by:\ngenerate a batch for every model+shader in the world.\nsort batches by shader sort key, entity, shader.\ndepth is drawn (yay alpha masked surfaces)\nfor each light+batch\n\tdraw each bump/diffuse/specular stage. combine to one pass if that ordering is not maintained. switch diffuse/specular if needed\nambient stages from each batch are added over the top.\n\nFTE rtlight rendering works by:\ngenerate a batch for every model+shader in the world.\nsort batches by shader sort key, entity, shader.\ndraw surfaces. if rtworld_lightmaps is 0 and there's no additive stuff, draw as black, otherwise just scale lightmap passes.\nlights are then added over the top based upon the diffusemap, bumpmap and specularmap, and without any pass-specific info (no tcmods).\n*/\n\n\n#ifndef SHADER_H\n#define SHADER_H\nstruct shaderparsestate_s;\ntypedef void (shader_gen_t)(struct shaderparsestate_s *ps, const char *name, const void *args);\n\n#define SHADER_TMU_MAX 16\n#define SHADER_PASS_MAX\t16\n#define SHADER_MAX_TC_MODS\t8\n#define SHADER_DEFORM_MAX\t8\n#define SHADER_MAX_ANIMFRAMES\t16\n\ntypedef enum {\n\tSHADER_BSP,\n\tSHADER_BSP_VERTEX,\n\tSHADER_BSP_FLARE,\n\tSHADER_MD3,\n\tSHADER_2D\n} shadertype_t;\n\n/*\ntypedef enum {\n\tMF_NONE\t\t\t= 1<<0,\n\tMF_NORMALS\t\t= 1<<1,\n\tMF_TRNORMALS\t= 1<<2,\n\tMF_COLORS\t\t= 1<<3,\n\tMF_STCOORDS\t\t= 1<<4,\n\tMF_LMCOORDS\t\t= 1<<5,\n\tMF_NOCULL\t\t= 1<<6,\n\tMF_NONBATCHED\t= 1<<7\n} meshfeatures_t;\n*/\n\n//colour manipulation\ntypedef struct\n{\n    enum {\n\t\tSHADER_FUNC_SIN,\n\t\tSHADER_FUNC_TRIANGLE,\n\t\tSHADER_FUNC_SQUARE,\n\t\tSHADER_FUNC_SAWTOOTH,\n\t\tSHADER_FUNC_INVERSESAWTOOTH,\n\t\tSHADER_FUNC_NOISE,\n\t\tSHADER_FUNC_CONSTANT\n\t} type;\t\t\t\t// SHADER_FUNC enum\n    float\t\t\targs[4];\t\t\t// offset, amplitude, phase_offset, rate\n} shaderfunc_t;\n\n//tecture coordinate manipulation\ntypedef struct \n{\n\tenum {\n\t\tSHADER_TCMOD_NONE,\t\t//bug\n\t\tSHADER_TCMOD_SCALE,\t\t//some sorta tabled deformation\n\t\tSHADER_TCMOD_SCROLL,\t//boring moving texcoords with time\n\t\tSHADER_TCMOD_STRETCH,\t//constant factor\n\t\tSHADER_TCMOD_ROTATE,\n\t\tSHADER_TCMOD_TRANSFORM,\n\t\tSHADER_TCMOD_TURB,\n\t\tSHADER_TCMOD_PAGE\t\t//use a texture atlas. horizontal frames, vertical frames, time divisor\n\t} type;\n\tfloat\t\t\targs[6];\n} tcmod_t;\n\n//vertex positioning manipulation.\ntypedef struct\n{\n\tenum {\n\t\tDEFORMV_NONE,\t\t//bug\n\t\tDEFORMV_MOVE,\n\t\tDEFORMV_WAVE,\n\t\tDEFORMV_NORMAL,\n\t\tDEFORMV_BULGE,\n\t\tDEFORMV_AUTOSPRITE,\n\t\tDEFORMV_AUTOSPRITE2,\n\t\tDEFORMV_PROJECTION_SHADOW,\n\t\tDEFORMV_TEXT\n\t} type;\n    float\t\t\targs[4];\n    shaderfunc_t\tfunc;\n} deformv_t;\n\nenum\n{\n\t/*source and dest factors match each other for easier parsing\n\t  but they're not meant to ever be set on the shader itself\n\t  NONE is also invalid, and is used to signify disabled, it should never be set on only one\n\t*/\n\tSBITS_SRCBLEND_NONE\t\t\t\t\t= 0x00000000,\n\tSBITS_SRCBLEND_ZERO\t\t\t\t\t= 0x00000001,\n\tSBITS_SRCBLEND_ONE\t\t\t\t\t= 0x00000002,\n\tSBITS_SRCBLEND_DST_COLOR\t\t\t= 0x00000003,\n\tSBITS_SRCBLEND_ONE_MINUS_DST_COLOR\t= 0x00000004,\n\tSBITS_SRCBLEND_SRC_ALPHA\t\t\t= 0x00000005,\n\tSBITS_SRCBLEND_ONE_MINUS_SRC_ALPHA\t= 0x00000006,\n\tSBITS_SRCBLEND_DST_ALPHA\t\t\t= 0x00000007,\n\tSBITS_SRCBLEND_ONE_MINUS_DST_ALPHA\t= 0x00000008,\n\tSBITS_SRCBLEND_SRC_COLOR_INVALID\t\t\t= 0x00000009,\n\tSBITS_SRCBLEND_ONE_MINUS_SRC_COLOR_INVALID\t= 0x0000000a,\n\tSBITS_SRCBLEND_ALPHA_SATURATE\t\t= 0x0000000b,\n#define SBITS_SRCBLEND_BITS\t\t\t\t  0x0000000f\n\n\t/*must match src factors, just shifted 4*/\n\tSBITS_DSTBLEND_NONE\t\t\t\t\t= 0x00000000,\n\tSBITS_DSTBLEND_ZERO\t\t\t\t\t= 0x00000010,\n\tSBITS_DSTBLEND_ONE\t\t\t\t\t= 0x00000020,\n\tSBITS_DSTBLEND_DST_COLOR_INVALID\t\t\t= 0x00000030,\n\tSBITS_DSTBLEND_ONE_MINUS_DST_COLOR_INVALID\t= 0x00000040,\n\tSBITS_DSTBLEND_SRC_ALPHA\t\t\t= 0x00000050,\n\tSBITS_DSTBLEND_ONE_MINUS_SRC_ALPHA\t= 0x00000060,\n\tSBITS_DSTBLEND_DST_ALPHA\t\t\t= 0x00000070,\n\tSBITS_DSTBLEND_ONE_MINUS_DST_ALPHA\t= 0x00000080,\n\tSBITS_DSTBLEND_SRC_COLOR\t\t\t= 0x00000090,\n\tSBITS_DSTBLEND_ONE_MINUS_SRC_COLOR\t= 0x000000a0,\n\tSBITS_DSTBLEND_ALPHA_SATURATE_INVALID\t\t= 0x000000b0,\n#define SBITS_DSTBLEND_BITS\t\t\t\t  0x000000f0\n\n#define SBITS_BLEND_BITS\t\t\t\t(SBITS_SRCBLEND_BITS|SBITS_DSTBLEND_BITS)\n\n\tSBITS_MASK_RED\t\t\t\t\t\t= 0x00000100,\n\tSBITS_MASK_GREEN\t\t\t\t\t= 0x00000200,\n\tSBITS_MASK_BLUE\t\t\t\t\t\t= 0x00000400,\n\tSBITS_MASK_ALPHA\t\t\t\t\t= 0x00000800,\n#define SBITS_MASK_BITS\t\t\t\t  0x00000f00\n\n\tSBITS_ATEST_NONE\t\t\t\t\t= 0x00000000,\n\tSBITS_ATEST_GT0\t\t\t\t\t\t= 0x00001000,\n\tSBITS_ATEST_LT128\t\t\t\t\t= 0x00002000,\n\tSBITS_ATEST_GE128\t\t\t\t\t= 0x00003000,\n#define SBITS_ATEST_BITS\t\t\t\t  0x0000f000\n#define SBITS_ATEST_SHIFT\t\t\t\t  12\n\n\tSBITS_MISC_DEPTHWRITE\t\t\t\t= 0x00010000,\n\tSBITS_MISC_NODEPTHTEST\t\t\t\t= 0x00020000,\t//strictly speaking, this is NOT the same as 'depthfunc always', which is unfortunate.\n\n\tSBITS_DEPTHFUNC_CLOSEREQUAL\t\t\t= 0x00000000,\n\tSBITS_DEPTHFUNC_EQUAL\t\t\t\t= 0x00040000,\n\tSBITS_DEPTHFUNC_CLOSER\t\t\t\t= 0x00080000,\n\tSBITS_DEPTHFUNC_FURTHER\t\t\t\t= 0x000c0000,\n#define SBITS_DEPTHFUNC_BITS\t\t\t  0x000c0000\n\n\tSBITS_TESSELLATION\t\t\t\t\t= 0x00100000,\n\tSBITS_AFFINE\t\t\t\t\t\t= 0x00200000,\n\tSBITS_MISC_FULLRATE\t\t\t\t\t= 0x00400000,\t//don't use half-rate shading (for text/ui)\n\n\t//provided for the backend to hack about with\n\tSBITS_LINES\t\t\t\t\t\t\t= 0x80000000\n};\n\n\ntypedef struct shaderpass_s {\n\tint numMergedPasses;\n\n\tstruct programshared_s *prog;\n\n#ifdef HAVE_MEDIA_DECODER\n\tstruct cin_s *cin;\n#endif\n\t\n\tunsigned int\tshaderbits;\n\n\tenum {\n\t\tPBM_MODULATE,\n\t\tPBM_OVERBRIGHT,\n\t\tPBM_DECAL,\n\t\tPBM_ADD,\n\t\tPBM_DOTPRODUCT,\n\t\tPBM_REPLACE,\n\t\tPBM_REPLACELIGHT,\n\t\tPBM_MODULATE_PREV_COLOUR\n\t} blendmode;\n\n\tenum {\n\t\tRGB_GEN_WAVE,\n\t\tRGB_GEN_ENTITY,\n\t\tRGB_GEN_ONE_MINUS_ENTITY,\n\t\tRGB_GEN_VERTEX_LIGHTING,\n\t\tRGB_GEN_VERTEX_EXACT,\n\t\tRGB_GEN_ONE_MINUS_VERTEX,\n\t\tRGB_GEN_IDENTITY_LIGHTING,\n\t\tRGB_GEN_IDENTITY_OVERBRIGHT,\n\t\tRGB_GEN_IDENTITY,\n\t\tRGB_GEN_CONST,\n\t\tRGB_GEN_UNKNOWN,\n\t\tRGB_GEN_LIGHTING_DIFFUSE,\n\t\tRGB_GEN_ENTITY_LIGHTING_DIFFUSE,\n\t\tRGB_GEN_TOPCOLOR,\n\t\tRGB_GEN_BOTTOMCOLOR\n\t} rgbgen;\n\tshaderfunc_t rgbgen_func;\n\n\tenum {\n\t\tALPHA_GEN_UNDEFINED,\n\t\tALPHA_GEN_ENTITY,\n\t\tALPHA_GEN_WAVE,\n\t\tALPHA_GEN_PORTAL,\n\t\tALPHA_GEN_SPECULAR,\n\t\tALPHA_GEN_IDENTITY,\n\t\tALPHA_GEN_VERTEX,\n\t\tALPHA_GEN_CONST\n\t} alphagen;\n\tshaderfunc_t alphagen_func;\n\n\tenum {\n\t\tTC_GEN_BASE,\t//basic specified texture coords\n\t\tTC_GEN_LIGHTMAP,\t//use loaded lightmap coords\n\t\tTC_GEN_ENVIRONMENT,\n\t\tTC_GEN_DOTPRODUCT,\n\t\tTC_GEN_VECTOR,\n\n\t\t//these are really for use only in glsl stuff or perhaps cubemaps, as they generate 3d coords.\n\t\tTC_GEN_NORMAL,\n\t\tTC_GEN_SVECTOR,\n\t\tTC_GEN_TVECTOR,\n\t\tTC_GEN_SKYBOX,\n\t\tTC_GEN_WOBBLESKY,\n\t\tTC_GEN_REFLECT,\n\n\t\tTC_GEN_UNSPECIFIED\n\t} tcgen;\n\tvec3_t tcgenvec[2];\t//bloat :(\n\tint numtcmods;\n\ttcmod_t\t\ttcmods[SHADER_MAX_TC_MODS];\n\n\tint anim_numframes;\n\ttexid_t\t\t\tanim_frames[SHADER_MAX_ANIMFRAMES];\n\tfloat anim_fps;\n//\tunsigned int texturetype;\n\n\tenum {\n\t\tT_GEN_SINGLEMAP,\t//single texture specified in the shader\n\t\tT_GEN_ANIMMAP,\t\t//animating sequence of textures specified in the shader\n\t\tT_GEN_LIGHTMAP,\t\t//world light samples\n\t\tT_GEN_DELUXMAP,\t\t//world light directions\n\t\tT_GEN_SHADOWMAP,\t//light's depth values.\n\t\tT_GEN_LIGHTCUBEMAP,\t//light's projected cubemap\n\n\t\tT_GEN_DIFFUSE,\t\t//texture's default diffuse texture\n\t\tT_GEN_NORMALMAP,\t//texture's default normalmap\n\t\tT_GEN_SPECULAR,\t\t//texture's default specular texture\n\t\tT_GEN_UPPEROVERLAY,\t//texture's default personal colour\n\t\tT_GEN_LOWEROVERLAY,\t//texture's default team colour\n\t\tT_GEN_FULLBRIGHT,\t//texture's default fullbright overlay\n\t\tT_GEN_PALETTED,\t\t//texture's original paletted data (8bit)\n\t\tT_GEN_REFLECTCUBE,\t//dpreflectcube\n\t\tT_GEN_REFLECTMASK,\t//dpreflectcube mask\n\t\tT_GEN_DISPLACEMENT,\t//displacement texture (probably half-float or something so higher precision than normalmap.a)\n\t\tT_GEN_OCCLUSION,\t//occlusion mask (instead of baking it into the texture itself, required for correct pbr)\n\t\tT_GEN_TRANSMISSION,\t//.r fancy opacity mask (still contributes its own colour over the top, for KHR_materials_transmission)\n\t\tT_GEN_THICKNESS,\t//.g depth mask (could be replaced with raytracing, for KHR_materials_volume)\n\n\t\tT_GEN_CURRENTRENDER,//copy the current screen to a texture, and draw that\n\n\t\tT_GEN_SOURCECOLOUR, //used for render-to-texture targets\n\t\tT_GEN_SOURCEDEPTH,\t//used for render-to-texture targets\n\n\t\tT_GEN_REFLECTION,\t//reflection image (mirror-as-fbo)\n\t\tT_GEN_REFRACTION,\t//refraction image (portal-as-fbo)\n\t\tT_GEN_REFRACTIONDEPTH,\t//refraction image (portal-as-fbo)\n\t\tT_GEN_RIPPLEMAP,\t//ripplemap image (water surface distortions-as-fbo)\n\n\t\tT_GEN_SOURCECUBE,\t//used for render-to-texture targets\n\n#ifdef HAVE_MEDIA_DECODER\n\t\tT_GEN_VIDEOMAP,\t\t//use the media playback as an image source, updating each frame for which it is visible\n#endif\n\n#define GBUFFER_COUNT 8\n#define T_GEN_GBUFFERCASE T_GEN_GBUFFER0:case T_GEN_GBUFFER1:case T_GEN_GBUFFER2:case T_GEN_GBUFFER3:case T_GEN_GBUFFER4:case T_GEN_GBUFFER5:case T_GEN_GBUFFER6:case T_GEN_GBUFFER7 \n\t\tT_GEN_GBUFFER0,\t\t//one of the gbuffer images (deferred lighting).\n\t\tT_GEN_GBUFFER1,\t\t//one of the gbuffer images (deferred lighting).\n\t\tT_GEN_GBUFFER2,\t\t//one of the gbuffer images (deferred lighting).\n\t\tT_GEN_GBUFFER3,\t\t//one of the gbuffer images (deferred lighting).\n\t\tT_GEN_GBUFFER4,\t\t//one of the gbuffer images (deferred lighting).\n\t\tT_GEN_GBUFFER5,\t\t//one of the gbuffer images (deferred lighting).\n\t\tT_GEN_GBUFFER6,\t\t//one of the gbuffer images (deferred lighting).\n\t\tT_GEN_GBUFFER7,\t\t//one of the gbuffer images (deferred lighting).\n\t} texgen;\n\n\tenum {\n\t\tST_DIFFUSEMAP,\n\t\tST_AMBIENT,\n\t\tST_BUMPMAP,\n\t\tST_SPECULARMAP\n\t} stagetype;\n\n\tenum {\n\t\tSHADER_PASS_CLAMP\t\t= 1<<0,\t//needed for d3d's sampler states, MUST MATCH IMAGE FLAGS\n\t\tSHADER_PASS_NOMIPMAP    = 1<<1,\t//needed for d3d's sampler states, MUST MATCH IMAGE FLAGS\n\t\tSHADER_PASS_NEAREST\t\t= 1<<2,\t//needed for d3d's sampler states, MUST MATCH IMAGE FLAGS\n\t\tSHADER_PASS_LINEAR\t\t= 1<<3,\t//needed for d3d's sampler states, MUST MATCH IMAGE FLAGS\n\t\tSHADER_PASS_UIPIC\t\t= 1<<4, //                                 MUST MATCH IMAGE FLAGS\n\t\tSHADER_PASS_DEPTHCMP\t= 1<<5,\t//needed for d3d11's sampler states\n\t\tSHADER_PASS_SRGB\t\t= 1<<6, //d3d9 does srgb via samplers. everyone else does it via texture formats\n#define SHADER_PASS_IMAGE_FLAGS_D3D8\t(SHADER_PASS_CLAMP|SHADER_PASS_NOMIPMAP|SHADER_PASS_NEAREST|SHADER_PASS_LINEAR|SHADER_PASS_UIPIC)\n#define SHADER_PASS_IMAGE_FLAGS_D3D9\t(SHADER_PASS_CLAMP|SHADER_PASS_NOMIPMAP|SHADER_PASS_NEAREST|SHADER_PASS_LINEAR|SHADER_PASS_UIPIC|SHADER_PASS_SRGB)\n#define SHADER_PASS_IMAGE_FLAGS_D3D11\t(SHADER_PASS_CLAMP|SHADER_PASS_NOMIPMAP|SHADER_PASS_NEAREST|SHADER_PASS_LINEAR|SHADER_PASS_UIPIC|SHADER_PASS_DEPTHCMP) //NEEDS to be tightly-packed. indexing will bug out otherwise.\n\n\t\tSHADER_PASS_NOCOLORARRAY = 1<<7,\n\n\t\t//FIXME: remove these\n\t\tSHADER_PASS_VIDEOMAP\t= 1 << 8,\n\t\tSHADER_PASS_DETAIL\t\t= 1 << 9,\n\t\tSHADER_PASS_LIGHTMAP\t= 1 << 10,\n\t\tSHADER_PASS_DELUXMAP\t= 1 << 11,\n\t\tSHADER_PASS_ANIMMAP\t\t= 1 << 12\n\t} flags;\n\n#if defined(D3D11QUAKE) || defined(VKQUAKE)\n\tvoid *becache;\t//cache for blendstate objects.\n#endif\n} shaderpass_t;\n\ntypedef struct\n{\n\ttexid_t\t\t\tfarbox_textures[6];\n\ttexid_t\t\t\tnearbox_textures[6];\n} skydome_t;\n\nenum{\n\t#define PERMUTATION_GENERIC\t0\n\t#define PERMUTATION_BUMPMAP\t\t\t(1u<<PERMUTATION_BIT_BUMPMAP)\n\tPERMUTATION_BIT_BUMPMAP,\t\t\t//FIXME: make argument somehow\n\t#define PERMUTATION_FULLBRIGHT\t\t(1u<<PERMUTATION_BIT_FULLBRIGHT)\n\tPERMUTATION_BIT_FULLBRIGHT,\t\t\t//FIXME: make argument somehow\n\t#define PERMUTATION_UPPERLOWER\t\t(1u<<PERMUTATION_BIT_UPPERLOWER)\n\tPERMUTATION_BIT_UPPERLOWER,\t\t\t//FIXME: make argument somehow\n\t#define PERMUTATION_REFLECTCUBEMASK\t(1u<<PERMUTATION_BIT_REFLECTCUBEMASK)\n\tPERMUTATION_BIT_REFLECTCUBEMASK,\t//FIXME: make argument somehow\n\t#ifdef SKELETALMODELS\n\t\t#define PERMUTATION_SKELETAL\t(1u<<PERMUTATION_BIT_SKELETAL)\n\t\tPERMUTATION_BIT_SKELETAL,\n\t#else\n\t\t#define PERMUTATION_SKELETAL\t0u\n\t#endif\n\t#define PERMUTATION_FOG\t\t\t\t(1u<<PERMUTATION_BIT_FOG)\n\tPERMUTATION_BIT_FOG,\t\t\t\t//FIXME: remove (recompile shaders if its enabled).\n\t#ifdef NONSKELETALMODELS\n\t\t#define PERMUTATION_FRAMEBLEND\t(1u<<PERMUTATION_BIT_FRAMEBLEND)\n\t\tPERMUTATION_BIT_FRAMEBLEND,\n\t#else\n\t\t#define PERMUTATION_FRAMEBLEND\t0u\n\t#endif\n\t#if MAXRLIGHTMAPS > 1\n\t\t#define PERMUTATION_LIGHTSTYLES\t(1u<<PERMUTATION_BIT_LIGHTSTYLES)\n\t\tPERMUTATION_BIT_LIGHTSTYLES,\t//FIXME: make argument\n\t#else\n\t\t#define PERMUTATION_LIGHTSTYLES\t0u\n\t#endif\n\n\tPERMUTATION_BIT_MAX\n};\n#define PERMUTATIONS\t\t\t\t(1u<<PERMUTATION_BIT_MAX)\n\nextern cvar_t r_fog_permutation;\n\nenum shaderattribs_e\n{\n\t//GLES2 has a limit of 8.\n\t//GL2 has a limit of 16.\n\t//vendors may provide more.\n\tVATTR_VERTEX1=0,\t//NOTE: read the comment about VATTR_LEG_VERTEX\n\tVATTR_VERTEX2=1,\n\tVATTR_COLOUR=2,\n\tVATTR_TEXCOORD=3,\n\tVATTR_LMCOORD=4,\n\tVATTR_NORMALS=5,\n\tVATTR_SNORMALS=6,\n\tVATTR_TNORMALS=7,\n\tVATTR_BONENUMS=8, /*skeletal only*/\n\tVATTR_BONEWEIGHTS=9, /*skeletal only*/\n#if MAXRLIGHTMAPS > 1\n\tVATTR_LMCOORD2=10,\n\tVATTR_LMCOORD3=11,\n\tVATTR_LMCOORD4=12,\n\tVATTR_COLOUR2=13,\n\tVATTR_COLOUR3=14,\n\tVATTR_COLOUR4=15,\n#endif\n\n\n\tVATTR_LEG_VERTEX,\t//note: traditionally this is actually index 0.\n\t\t\t\t\t\t//however, implementations are allowed to directly alias, or remap,\n\t\t\t\t\t\t//so we're never quite sure if 0 is enabled or not when using legacy functions.\n\t\t\t\t\t\t//as a result, we use legacy verticies always and never custom attribute 0 if we have any fixed function support.\n\t\t\t\t\t\t//we then depend upon gl_Vertex always being supported by the glsl compiler.\n\t\t\t\t\t\t//this is likely needed anyway to ensure that ftransform works properly and in all cases for stencil shadows.\n\tVATTR_LEG_COLOUR,\n\tVATTR_LEG_ELEMENTS,\n\tVATTR_LEG_TMU0,\n\n\n\tVATTR_LEG_FIRST=VATTR_LEG_VERTEX\n};\n\ntypedef struct {\n\tenum shaderprogparmtype_e {\n\t\tSP_BAD,\t//never set (hopefully)\n\n\t\t/*entity properties*/\n\t\tSP_E_VBLEND,\n\t\tSP_E_LMSCALE,\t//lightmap scales\n\t\tSP_E_VLSCALE,\t//vertex lighting style scales\n\t\tSP_E_ORIGIN,\n\t\tSP_E_COLOURS,\n\t\tSP_E_COLOURSIDENT,\n\t\tSP_E_GLOWMOD,\n\t\tSP_E_TOPCOLOURS,\n\t\tSP_E_BOTTOMCOLOURS,\n\t\tSP_E_TIME,\n\t\tSP_E_L_DIR, /*these light values are non-dynamic light as in classic quake*/\n\t\tSP_E_L_MUL,\n\t\tSP_E_L_AMBIENT,\n\t\tSP_E_EYEPOS, /*viewer's eyepos, in model space*/\n\t\tSP_V_EYEPOS, /*viewer's eyepos, in world space*/\n\t\tSP_W_FOG,\n\t\tSP_W_USER,\t//user-specified blob of data.\n\n\t\tSP_M_ENTBONES_PACKED,\n\t\tSP_M_ENTBONES_MAT3X4,\n\t\tSP_M_ENTBONES_MAT4,\n\t\tSP_M_VIEW,\n\t\tSP_M_MODEL,\n\t\tSP_M_MODELVIEW,\n\t\tSP_M_PROJECTION,\n\t\tSP_M_MODELVIEWPROJECTION,\n\t\tSP_M_INVVIEWPROJECTION,\n\t\tSP_M_INVMODELVIEWPROJECTION,\n\t\tSP_M_INVMODELVIEW,\n\n\t\tSP_RENDERTEXTURESCALE,\t/*multiplier for currentrender->texcoord*/\n\t\tSP_SOURCESIZE,\t\t\t/*size of $sourcecolour*/\n\n\t\tSP_S_COLOUR,\n\n\t\tSP_LIGHTRADIUS, /*these light values are realtime lighting*/\n\t\tSP_LIGHTCOLOUR,\n\t\tSP_LIGHTCOLOURSCALE,\n\t\tSP_LIGHTPOSITION,\n\t\tSP_LIGHTDIRECTION,\n\t\tSP_LIGHTSCREEN,\n\t\tSP_LIGHTCUBEMATRIX,\n\t\tSP_LIGHTSHADOWMAPPROJ,\n\t\tSP_LIGHTSHADOWMAPSCALE,\n\n\t\t//things that are set immediatly\n\t\tSP_FIRSTIMMEDIATE,\t//never set\n\t\tSP_TEXTURE,\n\t\tSP_CONST1I,\n\t\tSP_CONST2I,\n\t\tSP_CONST3I,\n\t\tSP_CONST4I,\n\t\tSP_CONST1F,\n\t\tSP_CONST2F,\n\t\tSP_CONST3F,\n\t\tSP_CONST4F,\n\t\tSP_CVARI,\n\t\tSP_CVARF,\n\t\tSP_CVAR3F,\n\t\tSP_CVAR4F,\n\t} type;\n\tunion\n\t{\n\t\tint ival[4];\n\t\tfloat fval[4];\n\t\tvoid *pval;\n\t};\n\tunsigned int handle;\n} shaderprogparm_t;\n\nstruct programpermu_s\n{\n#if defined(GLQUAKE) || defined(D3DQUAKE)\n\tunion programhandle_u\n\t{\n\t#ifdef GLQUAKE\n\t\tstruct\n\t\t{\n\t\t\tunsigned int handle;\n\t\t\tqboolean usetesselation;\n\t\t} glsl;\n\t#endif\n\t#ifdef D3DQUAKE\n\t\tstruct\n\t\t{\n\t\t\tvoid *vert;\n\t\t\tvoid *frag;\n\t\t\t#ifdef D3D9QUAKE\n\t\t\t\tvoid *ctabf;\n\t\t\t\tvoid *ctabv;\n\t\t\t#endif\n\t\t\t#ifdef D3D11QUAKE\n\t\t\t\tint topology;\t//D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST\n\t\t\t\tvoid *hull;\n\t\t\t\tvoid *domain;\n\t\t\t\tvoid *geom;\n\t\t\t\tvoid *layouts[2];\n\t\t\t#endif\n\t\t} hlsl;\n\t#endif\n\t} h;\n#endif\n#ifdef GLQUAKE\n\tint factorsuniform;\n#endif\n\tunsigned int permutation;\n\tunsigned int attrmask;\n\tunsigned int texmask;\t//'standard' textures that are in use\n\tunsigned int numparms;\n\tshaderprogparm_t *parm;\n};\n\ntypedef struct programshared_s\n{\n\tchar *name;\n\tint refs;\n\tunsigned calcgens:1;\t\t//calculate legacy rgb/alpha/tc gens\n\tunsigned explicitsyms:1;\t//avoid defining symbol names that'll conflict with other glsl (any fte-specific names must have an fte_ prefix)\n\tunsigned tess:1;\t\t\t//has a tessellation control+evaluation shader\n\tunsigned geom:1;\t\t\t//has a geometry shader\n\tunsigned rayquery:1;\t\t//needs a top-level acceleration structure.\n\tunsigned warned:1;\t\t\t//one of the permutations of this shader has already been warned about. don't warn about all of them because that's potentially spammy.\n\tunsigned short numsamplers;\t//shader system can strip any passes above this\n\tunsigned int defaulttextures;\t//diffuse etc\n\n\tunsigned int supportedpermutations;\n\tunsigned char *cvardata;\n\tunsigned int cvardatasize;\n\tint shaderver;\t\t\t\t//glsl version\n\tchar *preshade;\t\t//general prefixed #defines\n\tchar *shadertext;\t\t//the glsl text\n\tunsigned char failed[(PERMUTATIONS+7)/8];\t\t//so we don't try recompiling endlessly\n\tstruct programpermu_s *permu[PERMUTATIONS];\t//set once compiled.\n\n#ifdef VKQUAKE\n\tqVkShaderModule vert;\t\t//for slightly faster regeneration\n\tqVkShaderModule frag;\n\tqVkPipelineLayout layout;\t//all permutations share the same layout. I'm too lazy not to.\n\tqVkDescriptorSetLayout desclayout;\n\tstruct pipeline_s *pipelines;\n#endif\n} program_t;\n\ntypedef struct {\n\tfloat factor;\n\tfloat unit;\n} polyoffset_t;\n\nenum\n{\n\tLSHADER_STANDARD=0u,\t//stencil or shadowless\n\tLSHADER_CUBE=1u<<0,\t\t//has a cubemap filter (FIXME: use custom 2d filter on spot lights)\n\tLSHADER_SMAP=1u<<1,\t\t//filter based upon a shadowmap instead of stencil/unlit\n\tLSHADER_SPOT=1u<<2,\t\t//filter based upon a single spotlight shadowmap\n\tLSHADER_RAYQUERY=1u<<3,\t//hardware raytrace.\n#ifdef LFLAG_ORTHO\n\tLSHADER_ORTHO=1u<<4,\t//uses a parallel projection(ortho) matrix, with the light source being an entire plane instead of a singular point. which is weird. read: infinitely far away sunlight\n\tLSHADER_MODES=1u<<5,\n#else\n\tLSHADER_ORTHO=0,\t//so bitmasks return false\n\tLSHADER_MODES=1u<<4,\n#endif\n\n\tLSHADER_FAKESHADOWS=1u<<10,\t//special 'light' type that isn't a light but still needs a shadowmap. ignores world+bsp shadows.\n};\nenum\n{\n\t//low numbers are used for various rtlight mode combinations\n\tbemoverride_crepuscular = LSHADER_MODES,\t//either black (non-sky) or a special crepuscular_sky shader\n\tbemoverride_depthonly,\t\t//depth masked. replace if you want alpha test.\n\tbemoverride_depthdark,\t\t//itself or a pure-black shader. replace for alpha test.\n\tbemoverride_gbuffer,\t\t//prelighting\n\tbemoverride_fog,\t\t\t//post-render volumetric fog\n\tbemoverride_max\n};\n//FIXME: split into separate materials and shaders\nstruct shader_s\n{\n\tchar name[MAX_QPATH];\n\tstruct model_s *model;\n\tenum {\n\t\tSUF_NONE\t\t= 0,\n\t\tSUF_LIGHTMAP\t= 1<<0,\t//$lightmap passes are valid. otherwise collapsed to an rgbgen\n\t\tSUF_2D\t\t\t= 1<<1,\t//any loaded textures will obey 2d picmips rather than 3d picmips\n\t\tSUR_FORCEFALLBACK = 1<<2//shader fallback is forced, will not load from disk\n\t} usageflags;\t//\n\tint uses;\t//released when the uses drops to 0\n\tint width;\t//when used as an image, this is the logical 'width' of the image. FIXME.\n\tint height;\n\tint numpasses;\n\tunsigned int numdefaulttextures;\t//0 is effectively 1.\n\tfloat\tdefaulttextures_fps;\n\ttexnums_t *defaulttextures;\t//must always have at least one entry. multiple will only appear if the diffuse texture was animmapped.\n\tstruct shader_s *next;\n\tint id;\n\n\trshader_t *bemoverrides[bemoverride_max];\n\tmaterial_t *remapto;\t//render using this material instead. for q3 nonsense.\n\tfloat\tremaptime;\n\n\tbyte_vec4_t fog_color;\n\tfloat fog_dist;\n\tfloat portaldist;\n\tfloat portalfboscale;\t//if we're using texturemaps for portal recursion, this is the scale of the texture relative to the screen.\n\n\tint numdeforms;\n\tdeformv_t\tdeforms[SHADER_DEFORM_MAX];\n\n\tpolyoffset_t polyoffset;\n\n\t#define SHADER_CULL_FLIP (SHADER_CULL_FRONT|SHADER_CULL_BACK)\n\tenum {\n\t\tSHADER_SKY\t\t\t\t= 1 << 0,\n\t\tSHADER_NOMIPMAPS\t\t= 1 << 1,\n\t\tSHADER_NOPICMIP\t\t\t= 1 << 2,\n\t\tSHADER_CULL_FRONT\t\t= 1 << 3,\n\t\tSHADER_CULL_BACK\t\t= 1 << 4,\n\t\tSHADER_NOMARKS\t\t\t= 1 << 5,\n\t\tSHADER_POLYGONOFFSET\t= 1 << 6,\n\t\tSHADER_FLARE\t\t\t= 1 << 7,\n\t\tSHADER_NEEDSARRAYS\t\t= 1 << 8,\t//shader uses deforms or rgbmod tcmods or something that will not work well with sparse vbos\n\t\tSHADER_ENTITY_MERGABLE\t= 1 << 9,\n\t\tSHADER_VIDEOMAP\t\t\t= 1 << 10,\n\t\tSHADER_DEPTHWRITE\t\t= 1 << 11,\t//some pass already wrote depth. not used by the renderer.\n\t\tSHADER_AGEN_PORTAL\t\t= 1 << 12,\n\t\tSHADER_BLEND\t\t\t= 1 << 13,\t//blend or alphatest (not 100% opaque).\n\t\tSHADER_NODRAW\t\t\t= 1 << 14,\t//parsed only to pee off developers when they forget it on no-pass shaders.\n\n\t\tSHADER_NODLIGHT\t\t\t= 1 << 15,\t//from surfaceflags\n\t\tSHADER_HASLIGHTMAP\t\t= 1 << 16,\n\t\tSHADER_HASTOPBOTTOM\t\t= 1 << 17,\n\t\tSHADER_HASREFLECTCUBE\t= 1 << 18,\t//shader has a T_GEN_REFLECTCUBE pass (otherwise we can skip surf envmaps for better batching)\n\t\tSHADER_HASREFLECT\t\t= 1 << 19,\t//says that we need to generate a reflection image first\n\t\tSHADER_HASREFRACT\t\t= 1 << 20,\t//says that we need to generate a refraction image first\n\t\tSHADER_HASREFRACTDEPTH\t= 1 << 21,\t//refraction generation needs to generate a depth texture too.\n\t\tSHADER_HASNORMALMAP\t\t= 1 << 22,\t//says that we need to load a normalmap texture\n\t\tSHADER_HASRIPPLEMAP\t\t= 1 << 23,\t//water surface disturbances for water splashes\n\t\tSHADER_HASGLOSS\t\t\t= 1 << 24,\t//needs a _spec texture, if possible.\n\t\tSHADER_NOSHADOWS\t\t= 1 << 25,\t//don't cast shadows\n\t\tSHADER_HASFULLBRIGHT\t= 1 << 26,\t//needs a fullbright texture, if possible.\n\t\tSHADER_HASDIFFUSE\t\t= 1 << 27,\t//has a T_GEN_DIFFUSE pass\n\t\tSHADER_HASPALETTED\t\t= 1 << 28,\t//has a T_GEN_PALETTED pass\n\t\tSHADER_HASCURRENTRENDER\t= 1 << 29,\t//has a $currentrender pass\n\t\tSHADER_HASPORTAL\t\t= 1 << 30,\t//reflection image is actually a portal rather than a simple reflection (must be paired with SHADER_HASREFRACT)\n\t\tSHADER_HASDISPLACEMENT\t= (int)(1u << 31)\n\t} flags;\n\n\tprogram_t *prog;\n\n\tshaderpass_t passes[SHADER_PASS_MAX];\n\n\tshadersort_t sort;\n\n\tskydome_t\t*skydome;\n\tshader_gen_t *generator;\n\tchar\t*genargs;\n\n\tstruct shader_clutter_s\n\t{\n\t\tstruct shader_clutter_s *next;\n\t\tfloat scalemin;\n\t\tfloat scalemax;\n\t\tfloat anglemin;\n\t\tfloat anglemax;\n\t\tfloat spacing;\n\t\tfloat zofs;\n\t\tchar modelname[1];\n\t} *clutter;\n\n\tbucket_t bucket;\n\n#define MATERIAL_FACTOR_BASE 0\n#define MATERIAL_FACTOR_SPEC 1\n#define MATERIAL_FACTOR_EMIT 2\n#define MATERIAL_FACTOR_TRANSMISSION 3\n#define MATERIAL_FACTOR_VOLUME 4\n#define MATERIAL_FACTOR_COUNT 5\n\tvec4_t factors[MATERIAL_FACTOR_COUNT];\n\n\t//arranged as a series of vec4s\n/*\tstruct\n\t{\n\t\tfloat offsetmappingscale;\t//default 1\n\t\tfloat offsetmappingbias;\t//default 0\n\t\tfloat specularexpscale;\t\t//default 1*gl_specular_power\n\t\tfloat specularvalscale;\t\t//default 1*gl_specular\n\t} fragpushdata;\n*/\n};\n\nstruct shaderparsestate_s;\nstruct sbuiltin_s\n{\n\tint qrtype;\n\tint apiver;\n\tchar name[MAX_QPATH];\n\tchar *body;\n};\ntypedef struct\n{\n\tconst char *loadername;\n\tqboolean (*ReadMaterial)(struct shaderparsestate_s *ps, const char *filename, void (*LoadMaterialString)(struct shaderparsestate_s *ps, const char *script));\n\n\tstruct sbuiltin_s *builtinshaders;\n#define plugmaterialloaderfuncs_name \"MaterialLoader\"\n} plugmaterialloaderfuncs_t;\nqboolean Material_RegisterLoader(void *module, plugmaterialloaderfuncs_t *loader);\n\nextern unsigned int r_numshaders;\nextern unsigned int r_maxshaders;\nextern shader_t\t**r_shaders;\nextern int be_maxpasses;\n\n\nchar *Shader_GetShaderBody(shader_t *s, char *fname, size_t fnamesize);\nvoid R_UnloadShader(shader_t *shader);\nint R_GetShaderSizes(shader_t *shader, int *width, int *height, qboolean blocktillloaded);\nshader_t *R_RegisterPic (const char *name, const char *subdirs);\nshader_t *QDECL R_RegisterShader (const char *name, unsigned int usageflags, const char *shaderscript);\nshader_t *R_RegisterShader_Lightmap (model_t *mod, const char *name);\nshader_t *R_RegisterShader_Vertex (model_t *mod, const char *name);\nshader_t *R_RegisterShader_Flare (model_t *mod, const char *name);\nshader_t *QDECL R_RegisterSkin  (model_t *mod, const char *shadername);\nshader_t *R_RegisterCustom (model_t *mod, const char *name, unsigned int usageflags, shader_gen_t *defaultgen, const void *args);\n//once loaded, most shaders should have one of the following two calls used upon it\nvoid QDECL R_BuildDefaultTexnums(texnums_t *tn, shader_t *shader, unsigned int imageflags);\nvoid QDECL R_BuildLegacyTexnums(shader_t *shader, const char *fallbackname, const char *subpath, unsigned int loadflags, unsigned int imageflags, uploadfmt_t basefmt, size_t width, size_t height, qbyte *mipdata, qbyte *palette);\nvoid R_RemapShader(const char *sourcename, const char *destname, float timeoffset);\n\nvoid Shader_TouchTexnums(texnums_t *t);\nvoid Shader_TouchTextures(void);\n\ncin_t *R_ShaderGetCinematic(shader_t *s);\ncin_t *R_ShaderFindCinematic(const char *name);\nshader_t *R_ShaderFind(const char *name);\t//does NOT increase the shader refcount.\n\nvoid Shader_DefaultSkin\t\t\t(struct shaderparsestate_s *ps, const char *shortname, const void *args);\nvoid Shader_DefaultSkinShell\t(struct shaderparsestate_s *ps, const char *shortname, const void *args);\nvoid Shader_Default2D\t\t\t(struct shaderparsestate_s *ps, const char *shortname, const void *args);\nvoid Shader_DefaultBSPLM\t\t(struct shaderparsestate_s *ps, const char *shortname, const void *args);\nvoid Shader_DefaultBSPQ1\t\t(struct shaderparsestate_s *ps, const char *shortname, const void *args);\nvoid Shader_DefaultBSPQ2\t\t(struct shaderparsestate_s *ps, const char *shortname, const void *args);\nvoid Shader_DefaultWaterShader\t(struct shaderparsestate_s *ps, const char *shortname, const void *args);\nvoid Shader_DefaultSkybox\t\t(struct shaderparsestate_s *ps, const char *shortname, const void *args);\nvoid Shader_DefaultCinematic\t(struct shaderparsestate_s *ps, const char *shortname, const void *args);\nvoid Shader_DefaultScript\t\t(struct shaderparsestate_s *ps, const char *shortname, const void *args);\nvoid Shader_PolygonShader\t\t(struct shaderparsestate_s *ps, const char *shortname, const void *args);\n\nvoid Shader_ResetRemaps(void);\t//called on map changes to reset remapped shaders.\nvoid Shader_DoReload(void);\t\t//called when the shader system dies.\nvoid Shader_Shutdown (void);\nqboolean Shader_Init (void);\nvoid Shader_NeedReload(qboolean rescanfs);\nvoid Shader_WriteOutGenerics_f(void);\nvoid Shader_RemapShader_f(void);\nvoid Shader_ShowShader_f(void);\nvoid Shader_ShaderList_f(void);\n\nprogram_t *Shader_FindGeneric(char *name, int qrtype);\nstruct programpermu_s *Shader_LoadPermutation(program_t *prog, unsigned int p);\nvoid Shader_ReleaseGeneric(program_t *prog);\n\nimage_t *Mod_CubemapForOrigin(model_t *wmodel, vec3_t org);\nmfog_t *Mod_FogForOrigin(model_t *wmodel, vec3_t org);\n\n#define BEF_FORCEDEPTHWRITE\t\t(1u<<0)\n#define BEF_FORCEDEPTHTEST\t\t(1u<<1)\n#define BEF_FORCEADDITIVE\t\t(1u<<2)\t//blend dest = GL_ONE\n#define BEF_FORCETRANSPARENT\t(1u<<3)\t//texenv replace -> modulate\n#define BEF_FORCENODEPTH\t\t(1u<<4)\t//disables any and all depth.\n#ifdef HAVE_LEGACY\n#define BEF_PUSHDEPTH\t\t\t(1u<<5)\t//additional polygon offset\n#endif\n//FIXME: the above should really be legacy-only\n#define BEF_NODLIGHT\t\t\t(1u<<6)  //don't use a dlight pass\n#define BEF_NOSHADOWS\t\t\t(1u<<7) //don't appear in shadows\n#define BEF_FORCECOLOURMOD\t\t(1u<<8) //q3 shaders default to 'rgbgen identity', and ignore ent colours. this forces ent colours to be considered\n#define BEF_LINES\t\t\t\t(1u<<9)\t//draw line pairs instead of triangles.\n#define BEF_FORCETWOSIDED\t\t(1u<<10) //more evilness.s\n//#define\tBEFF_POLYHASNORMALS\t\t(1u<<31) //false flag - for cl_scenetries and not actually used by the backend.\n\ntypedef struct\n{\n\tint fbo;\n\tint rb_size[2];\n\tint rb_depth;\n\tint rb_stencil;\n\tint rb_depthstencil;\n\ttexid_t colour;\n\tunsigned int enables;\n} fbostate_t;\n#define FBO_RB_DEPTH\t\t2\n#define FBO_RB_STENCIL\t\t4\n#define FBO_RESET\t\t\t8\t//resize all renderbuffers / free any that are not active. implied if the sizes differ\n#define FBO_TEX_DEPTH\t\t32\t//internal\n#define FBO_TEX_STENCIL\t\t64\t//internal\n\n#ifndef __cplusplus\t//C++ sucks\ntypedef struct\n{\n\tchar *progpath;\t//path to use for glsl/hlsl\n\tchar *blobpath;\t//path to use for binary glsl/hlsl blobs.\n\tchar *shadernamefmt;\t//optional postfix for shader names for this renderer FIXME: should probably have multiple, for gles to fallback to desktop gl etc.\n\n\tqboolean progs_supported;\t//can use programs (all but gles1)\n\tqboolean progs_required;\t//no fixed function if this is true (d3d11, gles, gl3core)\n\tunsigned int minver;\t\t//lowest glsl version usable\n\tunsigned int maxver;\t\t//highest glsl version usable\n\tunsigned int max_gpu_bones;\t//max number of bones supported by uniforms.\n\n\tint hw_bc, hw_etc, hw_astc;\t//these are set only if the hardware actually supports the format, and not if we think the drivers are software-decoding them (unlike texfmt).\n\tqboolean texfmt[PTI_MAX];\t\t//which texture formats are supported (renderable not implied)\n\tunsigned int texture2d_maxsize;\t\t\t//max size of a 2d texture\n\tunsigned int texture3d_maxsize;\t\t\t//max size of a 3d texture\n\tunsigned int texture2darray_maxlayers;\t//max layers of a 2darray texture\n\tunsigned int texturecube_maxsize;\n\tqboolean texture_non_power_of_two;\t\t//full support for npot\n\tqboolean texture_non_power_of_two_pic;\t//npot only works with clamp-to-edge mipless images.\n\tqboolean texture_allow_block_padding;\t//mip 0 of compressed formats can be any size, with implicit padding.\n\tqboolean npot_rounddown;\t\t\t\t//memory limited systems can say that they want to use less ram.\n\tqboolean tex_env_combine;\n\tqboolean nv_tex_env_combine4;\n\tqboolean env_add;\n\tqboolean can_mipcap;\t\t//gl1.2+\n\tqboolean can_mipbias;\t\t//gl1.4+\n\tqboolean can_genmips;\t\t//gl3.0+\n\tqboolean havecubemaps;\t//since gl1.3, so pretty much everyone will have this... should probably only be set if we also have seamless or clamp-to-edge.\n\tunsigned int stencilbits;\n\n\tvoid\t (*pDeleteProg)\t\t(program_t *prog);\n\tqboolean (*pLoadBlob)\t\t(program_t *prog, unsigned int permu, vfsfile_t *blobfile);\n\tqboolean (*pCreateProgram)\t(program_t *prog, struct programpermu_s *permu, int ver, const char **precompilerconstants, const char *vert, const char *tcs, const char *tes, const char *geom, const char *frag, qboolean noerrors, vfsfile_t *blobfile);\n\tqboolean (*pValidateProgram)(program_t *prog, struct programpermu_s *permu, qboolean noerrors, vfsfile_t *blobfile);\n\tvoid\t (*pProgAutoFields)\t(program_t *prog, struct programpermu_s *permu, char **cvarnames, int *cvartypes);\n\n\tqboolean showbatches;\t//print batches... cleared at end of video frame.\n} sh_config_t;\nextern sh_config_t sh_config;\n#endif\n\nenum\n{\n\tS_SHADOWMAP\t\t= 0,\n\tS_PROJECTIONMAP\t= 1,\n\tS_DIFFUSE\t\t= 2,\n\tS_NORMALMAP\t\t= 3,\n\tS_SPECULAR\t\t= 4,\n\tS_UPPERMAP\t\t= 5,\n\tS_LOWERMAP\t\t= 6,\n\tS_FULLBRIGHT\t= 7,\n\tS_PALETTED\t\t= 8,\n\tS_REFLECTCUBE\t= 9,\n\tS_REFLECTMASK\t= 10,\n\tS_DISPLACEMENT\t= 11,\n\tS_OCCLUSION\t\t= 12,\n\tS_TRANSMISSION\t= 13,\n\tS_THICKNESS\t\t= 14,\n\tS_LIGHTMAP0\t\t= 15,\n\tS_DELUXEMAP0\t= 16,\n#if MAXRLIGHTMAPS > 1\n\tS_LIGHTMAP1\t\t= 17,\n\tS_LIGHTMAP2\t\t= 18,\n\tS_LIGHTMAP3\t\t= 19,\n\tS_DELUXEMAP1\t= 20,\n\tS_DELUXEMAP2\t= 21,\n\tS_DELUXEMAP3\t= 22,\n#endif\n};\nextern const struct sh_defaultsamplers_s\n{\n\tconst char *name;\n\tunsigned int defaulttexbits;\n} sh_defaultsamplers[];\n\n#ifdef GLSLONLY\n\t#define gl_config_nofixedfunc true\n#else\n\t#define gl_config_nofixedfunc gl_config.nofixedfunc\n#endif\n#ifdef GLESONLY\n\t#define gl_config_gles true\n#else\n\t#define gl_config_gles gl_config.gles\n#endif\n\n#ifdef VKQUAKE\nqboolean VK_LoadBlob(program_t *prog, void *blobdata, const char *name);\nvoid VK_RegisterVulkanCvars(void);\n#endif\n\n#ifdef GLQUAKE\nvoid GLBE_Init(void);\nvoid GLBE_Shutdown(void);\nvoid GLBE_SelectMode(backendmode_t mode);\nvoid GLBE_DrawMesh_List(shader_t *shader, int nummeshes, mesh_t **mesh, vbo_t *vbo, texnums_t *texnums, unsigned int beflags);\nvoid GLBE_DrawMesh_Single(shader_t *shader, mesh_t *meshchain, vbo_t *vbo, unsigned int beflags);\nvoid GLBE_SubmitBatch(batch_t *batch);\nbatch_t *GLBE_GetTempBatch(void);\nvoid GLBE_GenBrushModelVBO(model_t *mod);\nvoid GLBE_ClearVBO(vbo_t *vbo, qboolean dataonly);\nvoid GLBE_UpdateLightmaps(void);\nvoid GLBE_DrawWorld (batch_t **worldbatches);\nqboolean GLBE_LightCullModel(vec3_t org, model_t *model);\nvoid GLBE_SelectEntity(entity_t *ent);\nqboolean GLBE_SelectDLight(dlight_t *dl, vec3_t colour, vec3_t axis[3], unsigned int lmode);\nvoid GLBE_Scissor(srect_t *rect);\nvoid GLBE_SubmitMeshes (batch_t **worldbatches, int start, int stop);\n//void GLBE_RenderToTexture(texid_t sourcecol, texid_t sourcedepth, texid_t destcol, texid_t destdepth, qboolean usedepth);\nvoid GLBE_RenderToTextureUpdate2d(qboolean destchanged);\nvoid GLBE_VBO_Begin(vbobctx_t *ctx, size_t maxsize);\nvoid GLBE_VBO_Data(vbobctx_t *ctx, void *data, size_t size, vboarray_t *varray);\nvoid GLBE_VBO_Finish(vbobctx_t *ctx, void *edata, size_t esize, vboarray_t *earray, void **vbomem, void **ebomem);\nvoid GLBE_VBO_Destroy(vboarray_t *vearray, void *mem);\n\nvoid GLBE_FBO_Sources(texid_t sourcecolour, texid_t sourcedepth);\nint GLBE_FBO_Push(fbostate_t *state);\nvoid GLBE_FBO_Pop(int oldfbo);\nvoid GLBE_FBO_Destroy(fbostate_t *state);\nint GLBE_FBO_Update(fbostate_t *state, unsigned int enables, texid_t *destcol, int colourbuffers, texid_t destdepth, int width, int height, int layer);\n\nqboolean GLBE_BeginShadowMap(int id, int w, int h, uploadfmt_t encoding, int *restorefbo);\nvoid GLBE_EndShadowMap(int restorefbo);\nvoid GLBE_SetupForShadowMap(dlight_t *dl, int texwidth, int texheight, float shadowscale);\n\nqboolean GLVID_ApplyGammaRamps (unsigned int size, unsigned short *ramps);\t//called when gamma ramps need to be reapplied\nqboolean GLVID_Init (rendererstate_t *info, unsigned char *palette);\t\t//the platform-specific function to init gl state\nvoid GLVID_SwapBuffers(void);\nchar *GLVID_GetRGBInfo(int *bytestride, int *truewidth, int *trueheight, enum uploadfmt *fmt);\nvoid GLVID_SetCaption(const char *caption);\n#endif\n#ifdef D3D8QUAKE\nvoid D3D8BE_Init(void);\nvoid D3D8BE_Shutdown(void);\nvoid D3D8BE_SelectMode(backendmode_t mode);\nvoid D3D8BE_DrawMesh_List(shader_t *shader, int nummeshes, mesh_t **mesh, vbo_t *vbo, texnums_t *texnums, unsigned int beflags);\nvoid D3D8BE_DrawMesh_Single(shader_t *shader, mesh_t *meshchain, vbo_t *vbo, unsigned int beflags);\nvoid D3D8BE_SubmitBatch(batch_t *batch);\nbatch_t *D3D8BE_GetTempBatch(void);\nvoid D3D8BE_GenBrushModelVBO(model_t *mod);\nvoid D3D8BE_ClearVBO(vbo_t *vbo, qboolean dataonly);\nvoid D3D8BE_UploadAllLightmaps(void);\nvoid D3D8BE_DrawWorld (batch_t **worldbatches);\nqboolean D3D8BE_LightCullModel(vec3_t org, model_t *model);\nvoid D3D8BE_SelectEntity(entity_t *ent);\nqboolean D3D8BE_SelectDLight(dlight_t *dl, vec3_t colour, vec3_t axis[3], unsigned int lmode);\nvoid D3D8BE_VBO_Begin(vbobctx_t *ctx, size_t maxsize);\nvoid D3D8BE_VBO_Data(vbobctx_t *ctx, void *data, size_t size, vboarray_t *varray);\nvoid D3D8BE_VBO_Finish(vbobctx_t *ctx, void *edata, size_t esize, vboarray_t *earray, void **vbomem, void **ebomem);\nvoid D3D8BE_VBO_Destroy(vboarray_t *vearray, void *mem);\nvoid D3D8BE_Scissor(srect_t *rect);\n\nvoid D3D8Shader_Init(void);\nvoid D3D8BE_Reset(qboolean before);\nvoid D3D8BE_Set2D(void);\n#endif\n#ifdef D3D9QUAKE\nvoid D3D9BE_Init(void);\nvoid D3D9BE_Shutdown(void);\nvoid D3D9BE_SelectMode(backendmode_t mode);\nvoid D3D9BE_DrawMesh_List(shader_t *shader, int nummeshes, mesh_t **mesh, vbo_t *vbo, texnums_t *texnums, unsigned int beflags);\nvoid D3D9BE_DrawMesh_Single(shader_t *shader, mesh_t *meshchain, vbo_t *vbo, unsigned int beflags);\nvoid D3D9BE_SubmitBatch(batch_t *batch);\nbatch_t *D3D9BE_GetTempBatch(void);\nvoid D3D9BE_GenBrushModelVBO(model_t *mod);\nvoid D3D9BE_ClearVBO(vbo_t *vbo, qboolean dataonly);\nvoid D3D9BE_UploadAllLightmaps(void);\nvoid D3D9BE_DrawWorld (batch_t **worldbatches);\nqboolean D3D9BE_LightCullModel(vec3_t org, model_t *model);\nvoid D3D9BE_SelectEntity(entity_t *ent);\nqboolean D3D9BE_SelectDLight(dlight_t *dl, vec3_t colour, vec3_t axis[3], unsigned int lmode);\nvoid D3D9BE_VBO_Begin(vbobctx_t *ctx, size_t maxsize);\nvoid D3D9BE_VBO_Data(vbobctx_t *ctx, void *data, size_t size, vboarray_t *varray);\nvoid D3D9BE_VBO_Finish(vbobctx_t *ctx, void *edata, size_t esize, vboarray_t *earray, void **vbomem, void **ebomem);\nvoid D3D9BE_VBO_Destroy(vboarray_t *vearray, void *mem);\nvoid D3D9BE_Scissor(srect_t *rect);\n\nvoid D3D9Shader_Init(void);\nvoid D3D9BE_Reset(qboolean before);\nvoid D3D9BE_Set2D(void);\nvoid D3D9BE_SetViewport(int x, int y, int w, int h);\n#endif\n#ifdef D3D11QUAKE\nvoid D3D11BE_Init(void);\nvoid D3D11BE_Shutdown(void);\nvoid D3D11BE_SelectMode(backendmode_t mode);\nvoid D3D11BE_DrawMesh_List(shader_t *shader, int nummeshes, mesh_t **mesh, vbo_t *vbo, texnums_t *texnums, unsigned int beflags);\nvoid D3D11BE_DrawMesh_Single(shader_t *shader, mesh_t *meshchain, vbo_t *vbo, unsigned int beflags);\nvoid D3D11BE_SubmitBatch(batch_t *batch);\nbatch_t *D3D11BE_GetTempBatch(void);\nvoid D3D11BE_GenBrushModelVBO(model_t *mod);\nvoid D3D11BE_ClearVBO(vbo_t *vbo, qboolean dataonly);\nvoid D3D11BE_UploadAllLightmaps(void);\nvoid D3D11BE_DrawWorld (batch_t **worldbatches);\nqboolean D3D11BE_LightCullModel(vec3_t org, model_t *model);\nvoid D3D11BE_SelectEntity(entity_t *ent);\nqboolean D3D11BE_SelectDLight(dlight_t *dl, vec3_t colour, vec3_t axis[3], unsigned int lmode);\n\nqboolean D3D11Shader_Init(unsigned int featurelevel);\nvoid D3D11BE_Reset(qboolean before);\nvoid D3D11BE_Set2D(void);\nvoid D3D11_UploadLightmap(lightmapinfo_t *lm);\nvoid D3D11BE_VBO_Begin(vbobctx_t *ctx, size_t maxsize);\nvoid D3D11BE_VBO_Data(vbobctx_t *ctx, void *data, size_t size, vboarray_t *varray);\nvoid D3D11BE_VBO_Finish(vbobctx_t *ctx, void *edata, size_t esize, vboarray_t *earray, void **vbomem, void **ebomem);\nvoid D3D11BE_VBO_Destroy(vboarray_t *vearray, void *mem);\nvoid D3D11BE_Scissor(srect_t *rect);\n\nqboolean D3D11_BeginShadowMap(int id, int w, int h);\nvoid D3D11_EndShadowMap(void);\nvoid D3D11BE_SetupForShadowMap(dlight_t *dl, int texwidth, int texheight, float shadowscale);\n\nenum\n{\t//these are the buffer indexes\n\tD3D11_BUFF_POS,\n\tD3D11_BUFF_COL,\n\tD3D11_BUFF_TC,\n\tD3D11_BUFF_LMTC,\n\tD3D11_BUFF_NORM,\n\tD3D11_BUFF_SKEL,\n\tD3D11_BUFF_POS2,\n\tD3D11_BUFF_MAX\n};\n#endif\n\n//Builds a hardware shader from the software representation\nvoid BE_GenerateProgram(shader_t *shader);\n\nvoid Sh_RegisterCvars(void);\n#ifdef RTLIGHTS\nvoid R_EditLights_DrawLights(void);\t//3d light previews\nvoid R_EditLights_DrawInfo(void);\t//2d light info display.\nvoid R_EditLights_RegisterCommands(void);\n//\n#ifdef BEF_PUSHDEPTH\nvoid GLBE_PolyOffsetStencilShadow(qboolean foobar);\n#else\nvoid GLBE_PolyOffsetStencilShadow(void);\n#endif\n//Called from shadowmapping code into backend\nvoid GLBE_BaseEntTextures(const qbyte *worldpvs, const int *worldareas);\nvoid D3D9BE_BaseEntTextures(const qbyte *worldpvs, const int *worldareas);\nvoid D3D11BE_BaseEntTextures(const qbyte *worldpvs, const int *worldareas);\n//prebuilds shadow volumes\nvoid Sh_PreGenerateLights(void);\n//Draws lights, called from the backend\nvoid Sh_DrawLights(qbyte *vis);\nvoid Sh_GenerateFakeShadows(void);\n#ifdef RTLIGHTS\nvoid Sh_CheckSettings(void);\n#endif\nvoid SH_FreeShadowMesh(struct shadowmesh_s *sm);\n//frees all memory\nvoid Sh_Shutdown(void);\n//resize any textures to match new screen resize\nvoid Sh_Reset(void);\nqboolean Sh_StencilShadowsActive(void);\n#else\n#define Sh_StencilShadowsActive() false\n#endif\n\nstruct shader_field_names_s\n{\n\tchar *name;\n\tint ptype;\n};\nextern struct shader_field_names_s shader_field_names[];\nextern struct shader_field_names_s shader_unif_names[];\nextern struct shader_field_names_s shader_attr_names[];\n\n\nvoid CLQ1_AddSpriteQuad(shader_t *shader, vec3_t mid, float radius);\nvoid CLQ1_DrawLine(shader_t *shader, vec3_t v1, vec3_t v2, float r, float g, float b, float a);\nvoid CLQ1_AddOrientedCube(shader_t *shader, vec3_t mins, vec3_t maxs, float *matrix, float r, float g, float b, float a);\nvoid CL_DrawDebugPlane(float *normal, float dist, float r, float g, float b, qboolean enqueue);\nvoid CLQ1_AddOrientedCylinder(shader_t *shader, float radius, float height, qboolean capsule, float *matrix, float r, float g, float b, float a);\nvoid CLQ1_AddOrientedSphere(shader_t *shader, float radius, float *matrix, float r, float g, float b, float a);\nvoid CLQ1_AddOrientedHalfSphere(shader_t *shader, float radius, float gap, float *matrix, float r, float g, float b, float a);\n\nextern cvar_t r_fastturb, r_fastsky, r_skyboxname, r_skybox_orientation, r_skybox_autorotate;\n#endif\n"
  },
  {
    "path": "engine/http/ftpclient.c",
    "content": "#include \"quakedef.h\"\n\n#ifdef FTPCLIENT\n\n#include \"iweb.h\"\n\n#include \"netinc.h\"\n\ntypedef struct FTPclientconn_s{\n\tchar server[256];\n\tchar name[64];\n\tchar pwd[64];\n\tchar path[256];\n\tchar pathprefix[256];\t//Urhum.. Without this we can browse various entire hard drives too easily.\n\tchar file[64];\n\tchar localfile[MAX_QPATH];\n\n\tint transfersize;\n\tint transfered;\n\n\tint controlsock;\n\tint datasock;\t//FTP only allows one transfer per connection.\n\n\tenum {ftp_control, ftp_listing, ftp_getting, ftp_putting} type;\n\tint stage;\n\n\tvfsfile_t *f;\n\n\tstruct FTPclientconn_s *next;\n\n\tvoid (*NotifyFunction)(vfsfile_t *file, char *localfile, qboolean sucess);\t//called when failed or succeeded, and only if it got a connection in the first place.\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t//ftp doesn't guarentee it for anything other than getting though. :(\n} FTPclientconn_t;\n\nFTPclientconn_t *FTPclientconn;\n\nFTPclientconn_t *FTP_CreateConnection(char *addy)\n{\n\tunsigned long _true = true;\n\tstruct sockaddr_qstorage\tfrom;\n\tFTPclientconn_t *con;\n\n\n\n\tcon = IWebMalloc(sizeof(FTPclientconn_t));\n\n\t\n\n\tif ((con->controlsock = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1)\n\t{\n\t\tSys_Error (\"FTP_CreateConnection: socket: %s\\n\", strerror(qerrno));\n\t}\n\n\n\t{//quake routines using dns and stuff (Really, I wanna keep quake and ftp fairly seperate)\n\t\tnetadr_t qaddy;\t\t\n\t\tNET_StringToAdr (addy, &qaddy);\n\t\tif (!qaddy.port)\n\t\t\tqaddy.port = htons(21);\n\t\tNetadrToSockadr(&qaddy, &from);\n\t}\n\n\t//not yet blocking.\n\tif (connect(con->controlsock, (struct sockaddr *)&from, sizeof(from)) == -1)\n\t{\n\t\tIWebWarnPrintf (\"FTP_TCP_OpenSocket: connect: %i %s\\n\", qerrno, strerror(qerrno));\n\t\tclosesocket(con->controlsock);\t\t\n\t\tIWebFree(con);\n\t\treturn NULL;\n\t}\n\t\n\tif (ioctlsocket (con->controlsock, FIONBIO, &_true) == -1)\t//now make it non blocking.\n\t{\n\t\tSys_Error (\"FTP_TCP_OpenSocket: ioctl FIONBIO: %s\\n\", strerror(qerrno));\n\t}\n\n\tQ_strncpyz(con->server, addy, sizeof(con->server));\n\tstrcpy(con->name, \"anonymous\");\n\n\tcon->next = FTPclientconn;\n\tFTPclientconn = con;\n\tcon->stage = 1;\n\tcon->type = ftp_control;\n\n\tstrcpy(con->path, \"/\");\n\tcon->datasock = INVALID_SOCKET;\t\n\tcon->transfersize = -1;\n\tcon->transfered = 0;\n\n\treturn FTPclientconn;\n}\n//duplicate a connection to get multiple data channels with a server.\nFTPclientconn_t *FTP_DuplicateConnection(FTPclientconn_t *old)\n{\n\tFTPclientconn_t *newf;\n\tnewf = FTP_CreateConnection(old->server);\n\t*newf->server = '\\0';\t//mark it as non control\n\tstrcpy(newf->name, old->name);\n\tstrcpy(newf->pwd, old->pwd);\n\tstrcpy(newf->path, old->path);\n\tstrcpy(newf->pathprefix, old->pathprefix);\n\n\treturn newf;\n}\n\nint FTP_CL_makelistensocket(void)\n{\n\tchar name[256];\n\tunsigned long _true = true;\n\tint sock;\n\tstruct hostent *hent;\n\t\n\tstruct sockaddr_in\taddress;\n//\tint fromlen;\n\n\taddress.sin_family = AF_INET;\n\tif (gethostname(name, sizeof(name)) == -1)\n\t\treturn INVALID_SOCKET;\n\thent = gethostbyname(name);\n\tif (!hent)\n\t\treturn INVALID_SOCKET;\n\taddress.sin_addr.s_addr = *(int *)(hent->h_addr_list[0]);\n\taddress.sin_port = 0;\n\n\n\n\tif ((sock = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1)\n\t{\n\t\tSys_Error (\"FTP_TCP_OpenSocket: socket: %s\", strerror(qerrno));\n\t}\n\n\tif (ioctlsocket (sock, FIONBIO, &_true) == -1)\n\t{\n\t\tSys_Error (\"FTP_TCP_OpenSocket: ioctl FIONBIO: %s\", strerror(qerrno));\n\t}\n\t\n\tif( bind (sock, (void *)&address, sizeof(address)) == -1)\n\t{\n\t\tclosesocket(sock);\n\t\treturn INVALID_SOCKET;\n\t}\n\t\n\tlisten(sock, 1);\n\n\treturn sock;\n}\nint FTP_CL_makeconnectsocket(char *ftpdest)\n{\n\tunsigned long _true = true;\n\tint sock;\n\t\n\tstruct sockaddr_in\taddress;\n\n\tif (!ftpdest)\n\t\treturn 0;\n\tif (*ftpdest == '(')\n\t\tftpdest++;\n\n\tif ((sock = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1)\n\t{\n\t\tIWebWarnPrintf (\"FTP_CL_makeconnectsocket: socket: %s\", strerror(qerrno));\n\t\treturn INVALID_SOCKET;\n\t}\n\n\tif (ioctlsocket (sock, FIONBIO, &_true) == -1)\n\t{\n\t\tclosesocket(sock);\n\t\tIWebWarnPrintf (\"FTP_CL_makeconnectsocket: ioctl FIONBIO: %s\", strerror(qerrno));\n\t\treturn INVALID_SOCKET;\n\t}\n\n\taddress.sin_family = AF_INET;\n\n\taddress.sin_addr.s_addr = INADDR_ANY;\n\n\taddress.sin_port = 0;\n\t\n\tif( bind (sock, (void *)&address, sizeof(address)) == -1)\n\t{\n\t\tclosesocket(sock);\n\n\t\tIWebWarnPrintf (\"FTP_CL_makeconnectsocket: bind: %s\", strerror(qerrno));\n\t\treturn INVALID_SOCKET;\n\t}\n\n\tFTP_StringToAdr(ftpdest, (qbyte *)&address.sin_addr, (qbyte *)&address.sin_port);\n\n\t//this is commented out because connect always reports would_block, no matter what happens. So why check?\n\t//if (\n\t\tconnect(sock, (struct sockaddr *)&address, sizeof(address));// == -1)\n/*\t{\n\t\tclosesocket(sock);\n\n\t\tCon_Printf (\"FTP_CL_makeconnectsocket: ioctl FIONBIO: %s\", strerror(qerrno));\n\t\treturn INVALID_SOCKET;\n\t}\n*/\n\treturn sock;\n}\n\niwboolean\tFTP_SocketToString (int socket, char *s)\n{\n\tstruct sockaddr_in addr;\n\tint adrlen = sizeof(addr);\n\n\tif (getsockname(socket, (struct sockaddr*)&addr, &adrlen) == -1)\n\t\treturn false;\n\t\n\tsprintf(s, \"%i,%i,%i,%i,%i,%i\", ((qbyte*)&addr.sin_addr)[0], ((qbyte*)&addr.sin_addr)[1], ((qbyte*)&addr.sin_addr)[2], ((qbyte*)&addr.sin_addr)[3], ((qbyte *)&addr.sin_port)[0], ((qbyte *)&addr.sin_port)[1]);\n\treturn true;\n}\n\niwboolean FTP_ClientConnThink (FTPclientconn_t *con)\t//true to kill con\n{\n\tunsigned long _true = true;\n\tchar *line, *msg;\n\tint ret;\n\n\tchar readdata[8192];\n\tchar tempbuff[8192];\n\n\tif (con->stage == 6)\n\t{\n\t\tint len;\n\t\tif (con->type == ftp_getting)\n\t\t{\n\t\t\tif (!cls.downloadmethod || (cls.downloadmethod == DL_FTP && !strcmp(cls.downloadlocalname, con->localfile)))\n\t\t\t{\n\t\t\t\tstrcpy(cls.downloadlocalname, con->localfile);\n\t\t\t\tstrcpy(cls.downloadremotename, con->localfile);\n\t\t\t\tcls.downloadmethod = DL_FTP;\n\t\t\t\tif (con->transfersize == -1)\n\t\t\t\t\tcls.downloadpercent=50;\n\t\t\t\telse\n\t\t\t\t\tcls.downloadpercent = con->transfered*100.0f/con->transfersize;\n\t\t\t}\n\t\t\twhile((len = recv(con->datasock, readdata, sizeof(readdata), 0)) >0 )\n\t\t\t{\t\t\t\n\t\t\t\tVFS_WRITE(con->f, readdata, len);\n\t\t\t\tcon->transfered += len;\n\t\t\t}\n\t\t\tif (len == 0)\n\t\t\t{\n\t\t\t\tclosesocket(con->datasock);\n\t\t\t\tcon->datasock = INVALID_SOCKET;\n\t\t\t}\n\t\t}\n\t\telse if (con->type == ftp_putting)\n\t\t{\n\t\t\tint pos, sent;\n\t\t\tint ammount, wanted = sizeof(readdata);\n\n\t\t\tpos = VFS_TELL(con->f);\n\t\t\tammount = VFS_READ(con->f, readdata, wanted);\n\t\t\tsent = send(con->datasock, readdata, ammount, 0);\n\t\t\tif (sent == -1)\n\t\t\t\tVFS_SEEK(con->f, pos);\t//go back. Too much data\n\t\t\telse\n\t\t\t{\n\t\t\t\tVFS_SEEK(con->f, pos + sent);\t//written this much\n\n\t\t\t\tif (!ammount)\t//file is over\n\t\t\t\t{\n\t\t\t\t\tclosesocket(con->datasock);\n\t\t\t\t\tcon->datasock = INVALID_SOCKET;\n\n\t//\t\t\t\tmsg = \"226 Transfer complete.\\r\\n\";\n\t//\t\t\t\tsend (con->controlsock, msg, strlen(msg), 0);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tret = recv(con->controlsock, (char *)readdata, sizeof(readdata)-1, 0);\n\tif (ret == -1)\n\t{\n\t\tif (qerrno == EWOULDBLOCK)\n\t\t\treturn false;\n\n\t\tif (qerrno == ECONNABORTED || qerrno == ECONNRESET)\n\t\t{\n\t\t\tCon_TPrintf (\"Connection lost or aborted\\n\");\n\t\t\treturn true;\n\t\t}\n\n//\t\tCon_Printf (\"NET_GetPacket: %s\\n\", strerror(qerrno));\n\t\treturn true;\n\t}\t\n\n\treaddata[ret] = '\\0';\t//null terminate. (it's a string)\n\n\t//we now have a message.\n\t//We've got to work out what has happened already.\n\n\t//a server can send many lines of text for one reply\n\t//220-hello\n\t// this\n\t//220-is\n\t// 220-all\n\t//220 one reply\n\t//so we only read lines that contain number space words, without any leading space\n\tline = readdata;\n\twhile (1)\n\t{\t\t\n\t\tmsg = line;\n\t\twhile (*line)\n\t\t{\n\t\t\tif (*line == '\\n')\n\t\t\t\tbreak;\n\t\t\tif (*line == '\\r')\n\t\t\t\tbreak;\n\t\t\tline++;\n\t\t}\t\t\t\t\t\n\t\tif (!*line)\t//broken message\n\t\t\tbreak;\n\t\t*line = '\\0';\n\t\tline++;\n\n\t\tif (*con->server)\n\t\t\tIWebDPrintf(\"FTP: %s\\n\", COM_TrimString(msg));\n\n\t\tif (*msg < '0' || *msg > '9')\t//make sure it starts with number\n\t\t\tcontinue;\n\t\tret = atoi(msg);\n\t\twhile(*msg >= '0' && *msg <= '9')\t//find next non number\n\t\t\tmsg++;\n\t\tif (*msg != ' ')\t//must be a space (definatly not a '-')\n\t\t\tcontinue;\n\t\tmsg++;\t\t\n\n\t\tif (ret == 220)\n\t\t{\n\t\t\tsprintf(tempbuff, \"USER %s\\r\\n\", con->name);\n\t\t\tsend(con->controlsock, tempbuff, strlen(tempbuff), 0);\n\t\t\tcon->stage = 1;\n\t\t}\n\t\telse if (ret == 331)\n\t\t{\n\t\t\tif (con->type == ftp_control)\n\t\t\t\tsprintf(tempbuff, \"PASS %s\\r\\nPWD %s\\r\\n\", con->pwd, con->path);\n\t\t\telse\n\t\t\t\tsprintf(tempbuff, \"PASS %s\\r\\n\", con->pwd);\n\t\t\tsend(con->controlsock, tempbuff, strlen(tempbuff), 0);\n\t\t\tcon->stage = 2;\n\t\t}\n\t\telse if (ret == 230)\t//we must now do something useful\n\t\t{\n\t\t\tchar adr[64];\n\t\t\tif (con->type == ftp_control)\t//control is for browsing and duplicating\n\t\t\t\tcontinue;\n\n\t\t\tcon->datasock = FTP_CL_makelistensocket();\n\t\t\tif (!con->datasock || !FTP_SocketToString(con->datasock, adr))\n\t\t\t{\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\tsprintf(tempbuff, \"CWD %s%s\\r\\n\", con->pathprefix, con->path);\n\t\t\tsend(con->controlsock, tempbuff, strlen(tempbuff), 0);\n\n\t\t\tgoto usepasv;\n\t\t\tsprintf(tempbuff, \"PORT %s\\r\\n\", adr);\n\t\t\tsend(con->controlsock, tempbuff, strlen(tempbuff), 0);\n\n\t\t\tcon->stage = 3;\n\t\t}\n\t\telse if (ret == 200)\n\t\t{\n\t\t\tstruct sockaddr addr;\n\t\t\tint addrlen = sizeof(addr);\n\t\t\tint temp;\n\t\t\tif (con->type == ftp_control)\n\t\t\t\tcontinue;\n\t\t\tif (con->stage == 3)\n\t\t\t{\n\t\t\t\ttemp = accept(con->datasock, &addr, &addrlen);\n\t\t\t\tclosesocket(con->datasock);\n\t\t\t\tcon->datasock = temp;\n\n\t\t\t\tif (temp != INVALID_SOCKET)\n\t\t\t\t{\n\t\t\t\t\tioctlsocket(temp, FIONBIO, &_true);\n\t\t\t\t\tcon->stage = 6;\n\t\t\t\t\tif (con->type == ftp_getting)\n\t\t\t\t\t{\n\t\t\t\t\t\tcon->f = FS_OpenVFS(con->localfile, \"wb\", FS_GAME);\n\t\t\t\t\t\tif (con->f)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tsprintf(tempbuff, \"RETR %s\\r\\n\", con->file);\n\t\t\t\t\t\t\tcon->stage = 6;\n\t\t\t\t\t\t\tcon->transfered = 0;\n\t\t\t\t\t\t\tcon->transfersize = -1;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tsprintf(tempbuff, \"QUIT\\r\\n\");\n\t\t\t\t\t\t\tcon->stage = 7;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\telse if (con->type == ftp_putting)\n\t\t\t\t\t{\n\t\t\t\t\t\tcon->f = FS_OpenVFS (con->localfile, \"rb\", FS_GAME);\n\t\t\t\t\t\tif (con->f)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tsprintf(tempbuff, \"STOR %s\\r\\n\", con->file);\n\t\t\t\t\t\t\tcon->stage = 6;\n\t\t\t\t\t\t\tcon->transfered = 0;\n\t\t\t\t\t\t\tcon->transfersize = VFS_GETLEN(con->f);\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tsprintf(tempbuff, \"QUIT\\r\\n\");\n\t\t\t\t\t\t\tcon->stage = 7;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\tsprintf(tempbuff, \"LIST %s\\r\\n\", con->pwd);\n\t\t\t\t\tsend(con->controlsock, tempbuff, strlen(tempbuff), 0);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\nusepasv:\n\t\t\t\t\tCon_Printf(\"FTP: Trying passive server mode\\n\");\n\t\t\t\t\tmsg = va(\"PASV\\r\\n\");\n\t\t\t\t\tsend(con->controlsock, msg, strlen(msg), 0);\n\t\t\t\t\tcon->stage = 4;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse if (ret == 213)\n\t\t{\n\t\t\tcon->transfersize = atoi(msg);\n\t\t\tmsg = va(\"RETR %s\\r\\n\", con->file);\n\t\t\tcon->stage = 6;\n\t\t\tcon->transfered = 0;\n\t\t\tsend(con->controlsock, msg, strlen(msg), 0);\n\t\t}\n\t\telse if (ret == 125)\t//begining transfer\n\t\t{\n\t\t\tif (con->type == ftp_getting)\n\t\t\t{\n\t\t\t\tchar tempname[MAX_OSPATH];\n\t\t\t\tCOM_StripExtension(con->localfile, tempname, MAX_OSPATH);\n\t\t\t\tstrcat(tempname, \".tmp\");\n\t\t\t\tcon->f = FS_OpenVFS (tempname, \"wb\", FS_GAME);\n\t\t\t\tif (!con->f)\n\t\t\t\t{\n\t\t\t\t\tmsg = va(\"ABOR\\r\\nQUIT\\r\\n\");\t//bummer. we couldn't open this file to output to.\n\t\t\t\t\tsend(con->controlsock, msg, strlen(msg), 0);\n\t\t\t\t\tcon->stage = 7;\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n//\t\t\tmsg = va(\"LIST\\r\\n\");\n//\t\t\tsend(con->controlsock, msg, strlen(msg), 0);\n\t\t\tcon->stage = 6;\n\t\t}\n\t\telse if (ret == 226)\t//transfer complete\n\t\t{\n\t\t\tint len;\n\t\t\tchar data[1024];\n\t\t\tif (con->f)\n\t\t\t{\n\t\t\t\tif (con->type == ftp_getting)\n\t\t\t\t{\n\t\t\t\t\twhile(1)\t//this is potentially dodgy.\n\t\t\t\t\t{\n\t\t\t\t\t\tlen = recv(con->datasock, data, sizeof(data), 0);\n\t\t\t\t\t\tif (len == 0)\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tif (len == -1)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (qerrno != EWOULDBLOCK)\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcon->transfered+=len;\n\t\t\t\t\t\tdata[len] = 0;\n\t\t\t\t\t\tVFS_WRITE(con->f, data, len);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tVFS_CLOSE(con->f);\n\t\t\t\tcon->f = NULL;\n\t\t\t\tclosesocket(con->datasock);\n\t\t\t\tcon->datasock = INVALID_SOCKET;\n\n\t\t\t\tif (con->NotifyFunction)\n\t\t\t\t{\n\t\t\t\t\tcon->NotifyFunction(con->localfile, true);\n\t\t\t\t\tcon->NotifyFunction = NULL;\n\t\t\t\t}\n\n\t\t\t\tif (con->transfersize != -1 && con->transfered != con->transfersize)\n\t\t\t\t{\n\t\t\t\t\tIWebPrintf(\"Transfer corrupt\\nTransfered %i of %i bytes\\n\", con->transfered, con->transfersize);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tIWebPrintf(\"Transfer compleate\\n\");\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\twhile((len = recv(con->datasock, data, sizeof(data), 0)) >0 )\n\t\t\t\t{\n\t\t\t\t\tdata[len] = 0;\n\t\t\t\t\tif (strchr(data, '\\r'))\n\t\t\t\t\t{\n\t\t\t\t\t\tline = data;\n\t\t\t\t\t\tfor(;;)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tmsg = strchr(line, '\\r');\n\t\t\t\t\t\t\tif (!msg)\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t*msg = '\\0';\n\t\t\t\t\t\t\tCon_Printf(\"%s\", line);\n\t\t\t\t\t\t\tline = msg+1;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tCon_Printf(\"%s\", line);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\tCon_Printf(\"%s\", data);\n\t\t\t\t}\n\t\t\t\tclosesocket(con->datasock);\n\t\t\t\tcon->datasock = INVALID_SOCKET;\n\t\t\t}\n\t\t\tmsg = va(\"QUIT\\r\\n\");\n\t\t\tsend(con->controlsock, msg, strlen(msg), 0);\n\n\t\t\tcon->stage = 7;\n\t\t}\n\t\telse if (ret == 227)\n\t\t{\n//\t\t\tCon_Printf(\"FTP: Got passive server mode\\n\");\n\t\t\tif (con->datasock != INVALID_SOCKET)\n\t\t\t\tclosesocket(con->datasock);\t\n\t\t\tcon->datasock = INVALID_SOCKET;\n\n\t\t\tcon->datasock = FTP_CL_makeconnectsocket(strchr(msg, '('));\n\t\t\tif (con->datasock != INVALID_SOCKET)\n\t\t\t{\n\t\t\t\tif (con->type == ftp_getting)\n\t\t\t\t{\n\t\t\t\t\tcon->f = FS_OpenVFS(con->localfile, \"wb\", FS_GAME);\n\t\t\t\t\tif (con->f)\n\t\t\t\t\t{\n\t\t\t\t\t\tcon->stage = 8;\n\t\t\t\t\t\tmsg = va(\"TYPE I\\r\\nSIZE %s\\r\\n\", con->file);\n\t\t\t\t\t\tcon->transfersize = -1;\n\n\t\t\t\t\t\t/*\n\t\t\t\t\t\tmsg = va(\"RETR %s\\r\\n\", con->file);\n\t\t\t\t\t\tcon->stage = 6;\n\t\t\t\t\t\tcon->transfered = 0;\n\t\t\t\t\t\t*/\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tmsg = va(\"QUIT\\r\\n\");\n\t\t\t\t\t\tcon->stage = 7;\n\t\t\t\t\t\tCon_Printf(\"FTP: Failed to open local file %s\\n\", con->localfile);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse if (con->type == ftp_putting)\n\t\t\t\t{\n\t\t\t\t\tcon->f = FS_OpenVFS(con->localfile, \"rb\", FS_GAME);\n\t\t\t\t\tif (con->f)\n\t\t\t\t\t{\n\t\t\t\t\t\tmsg = va(\"STOR %s\\r\\n\", con->file);\n\t\t\t\t\t\tcon->stage = 6;\n\t\t\t\t\t\tcon->transfered = 0;\n\t\t\t\t\t\tcon->transfersize = VFS_GETLEN(con->f);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tmsg = va(\"QUIT\\r\\n\");\n\t\t\t\t\t\tcon->stage = 7;\n\t\t\t\t\t\tCon_Printf(\"FTP: Failed to open local file %s\\n\", con->localfile);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tmsg = \"LIST\\r\\n\";\n\t\t\t\t\tcon->stage = 6;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tmsg = \"QUIT\\r\\n\";\n\t\t\t\tcon->stage = 7;\n\t\t\t\tCon_Printf(\"FTP: Didn't connect\\n\");\n\t\t\t}\n\n\t\t\tsend (con->controlsock, msg, strlen(msg), 0);\n\t\t}\n\t\telse if (ret == 250)\n\t\t{\n\t\t\tCon_Printf(\"FTP: %i %s\\n\", ret, msg);\n\t\t}\n\t\telse if (ret == 257)\n\t\t{\t//stick it on the beginning.\n\t\t\tCon_Printf(\"FTP: %i %s\\n\", ret, msg);\n\t\t\tmsg = strchr(msg, '\"');\n\t\t\tif (msg)\n\t\t\t{\n\t\t\t\tQ_strncpyz(con->pathprefix, msg+1, sizeof(con->pathprefix));\n\t\t\t\tmsg = strchr(con->pathprefix, '\"');\n\t\t\t\tif (msg)\n\t\t\t\t\t*msg = '\\0';\n\t\t\t}\n\t\t\telse\n\t\t\t\tQ_strcpyline(con->pathprefix, msg+4, sizeof(con->pathprefix)-1);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (ret < 200)\n\t\t\t\tcontinue;\n\t\t\tif (con->stage == 5)\n\t\t\t{\n\t\t\t\tCon_DPrintf(\"FTP: Trying passive server mode\\n\");\n\t\t\t\tmsg = va(\"PASV\\r\\n\");\n\t\t\t\tsend(con->controlsock, msg, strlen(msg), 0);\n\t\t\t\tcon->stage = 4;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (ret != 221)\n\t\t\t\tCon_Printf(CON_ERROR \"FTP: %i %s\\n\", ret, msg);\n\t\t\treturn true;\n\t\t}\n\n\t\tcontinue;\n\t}\n\treturn false;\n}\n\nvoid FTP_ClientThink (void)\n{\n\tFTPclientconn_t *con, *old=NULL;\n\tfor (con = FTPclientconn; con; con = con->next)\n\t{\n\t\tif (FTP_ClientConnThink(con))\n\t\t{\n\t\t\tif (con->NotifyFunction)\n\t\t\t\tcon->NotifyFunction(con->localfile, false);\n\n\t\t\tif (cls.downloadmethod == DL_FTP && !strcmp(cls.downloadlocalname, con->localfile))\n\t\t\t{\t//this was us\n\t\t\t\tcls.downloadmethod = DL_NONE;\n\t\t\t}\n\t\t\tif (con->f)\n\t\t\t\tVFS_CLOSE(con->f);\n\t\t\tif (con->controlsock != INVALID_SOCKET)\n\t\t\t\tclosesocket(con->controlsock);\n\t\t\tif (con->datasock != INVALID_SOCKET)\n\t\t\t\tclosesocket(con->datasock);\n\t\t\tif (!old)\n\t\t\t{\n\t\t\t\tFTPclientconn = con->next;\n\t\t\t\tIWebFree(con);\t\t\t\t\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\told->next = con->next;\n\t\t\t\tIWebFree(con);\n\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\told = con;\n\t}\n}\n\nFTPclientconn_t *FTP_FindControl(void)\n{\n\tFTPclientconn_t *con;\n\n\tfor (con = FTPclientconn; con; con = con->next)\n\t{\n\t\tif (*con->server)\n\t\t\treturn con;\n\t}\n\treturn NULL;\n}\n\nvoid FTP_FixupPath(char *out)\n{\t//convert \\[ to [\n\tchar *in;\n\tin = out;\n\twhile (*in)\n\t{\n\t\tif (*in == '\\\\')\n\t\t{\n\t\t\tin++;\n\t\t\tif (*in == '\\0')\n\t\t\t\tbreak;\n\t\t}\n\t\t*out++ = *in++;\n\t}\n\t*out++ = '\\0';\n}\n\nqboolean FTP_Client_Command (char *cmd, void (*NotifyFunction)(char *localfile, qboolean sucess))\n{\n\tchar command[64];\n\tchar server[MAX_OSPATH];\n\tFTPclientconn_t *con;\n\n\tcmd = COM_ParseOut(cmd, command, sizeof(command));\n\tif (!stricmp(command, \"open\"))\n\t{\n\t\tif (FTP_FindControl())\n\t\t\tCon_Printf(\"You are already connected\\n\");\n\t\telse\n\t\t{\n\t\t\tcmd = COM_ParseOut(cmd, server, sizeof(server));\n\t\t\tif ((con = FTP_CreateConnection(server)))\n\t\t\t{\n\t\t\t\tCon_Printf(\"FTP connect succeded\\n\");\n\t\t\t\tcmd = COM_ParseOut(cmd, command, sizeof(command));\n\t\t\t\tif (cmd)\n\t\t\t\t{\n\t\t\t\t\tQ_strncpyz(con->name, command, sizeof(con->name));\n\t\t\t\t\tcmd = COM_ParseOut(cmd, command, sizeof(command));\n\t\t\t\t\tif (cmd)\n\t\t\t\t\t\tQ_strncpyz(con->pwd, command, sizeof(con->pwd));\n\t\t\t\t}\n\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\telse\n\t\t\t\tCon_Printf(\"FTP connect failed\\n\");\n\t\t}\n\n\t\treturn false;\n\t}\n\telse if (!stricmp(command, \"download\"))\n\t{\n\t\tcmd = COM_ParseOut(cmd, server, sizeof(server));\n\t\tcon = FTP_CreateConnection(server);\n\t\tif (!con)\n\t\t{\n\t\t\tCon_Printf(\"FTP: Couldn't connect\\n\");\n\t\t\treturn false;\n\t\t}\n\t\tcon->NotifyFunction = NotifyFunction;\n\t\t*con->server = '\\0';\n\t\tcon->type = ftp_getting;\n\t\tcmd = COM_ParseOut(cmd, server, sizeof(server));\n\t\tQ_strncpyz(con->file, server, sizeof(con->file));\n\t\tQ_strncpyz(con->localfile, server, sizeof(con->localfile));\n\n\t\tif ((cmd = COM_ParseOut(cmd, server, sizeof(server))))\n\t\t\tQ_strncpyz(con->localfile, server, sizeof(con->localfile));\n\n\t\tFTP_FixupPath(con->file);\n\t\tFTP_FixupPath(con->localfile);\n\n\t\treturn true;\n\t}\n\telse if (!stricmp(command, \"quit\"))\n\t{\n\t\tcon = FTP_FindControl();\n\t\tif (con)\n\t\t{\n\t\t\tchar *msg;\n\t\t\tmsg = va(\"QUIT\\r\\n\");\n\t\t\tsend(con->controlsock, msg, strlen(msg), 0);\n//\t\t\tif (con->datasock)\n//\t\t\t\tclosesocket(con->datasock);\n//\t\t\tclosesocket(con->controlsock);\n\t\t}\n\t\telse\n\t\t\tCon_Printf(\"No main FTP connection\\n\");\n\n\t\treturn true;\n\t}\n\telse if (!stricmp(command, \"list\"))\n\t{\n\t\tFTPclientconn_t *newf, *con = FTP_FindControl();\n\t\tif (!con)\n\t\t{\n\t\t\tCon_Printf(\"Not connected\\n\");\n\t\t\treturn false;\n\t\t}\n\n\t\tnewf = FTP_DuplicateConnection(con);\n\t\tif (!newf)\n\t\t{\n\t\t\tCon_Printf(\"Failed duplicate connection\\n\");\n\t\t\treturn false;\n\t\t}\n\t\tnewf->type = ftp_listing;\n\t\tnewf->NotifyFunction = NotifyFunction;\n\t\treturn true;\n\t}\n\telse if (!stricmp(command, \"get\"))\n\t{\n\t\tFTPclientconn_t *newf, *con = FTP_FindControl();\n\t\tif (!con)\n\t\t{\n\t\t\tCon_Printf(\"Not connected\\n\");\n\t\t\treturn false;\n\t\t}\n\n\t\tcmd = COM_ParseOut(cmd, command, sizeof(command));\n\t\tif (!cmd)\n\t\t{\n\t\t\tCon_Printf(\"No file specified\\n\");\n\t\t\treturn false;\n\t\t}\n\n\t\tnewf = FTP_DuplicateConnection(con);\n\t\tif (!newf)\n\t\t{\n\t\t\tCon_Printf(\"Failed duplicate connection\\n\");\n\t\t\treturn false;\n\t\t}\n\t\tnewf->NotifyFunction = NotifyFunction;\n\t\tnewf->type = ftp_getting;\n\t\tsprintf(newf->file, command);\n\t\tsprintf(newf->localfile, \"%s%s\", newf->path, command);\n\t\treturn true;\n\t}\n\telse if (!stricmp(command, \"put\"))\n\t{\n\t\tFTPclientconn_t *newf, *con = FTP_FindControl();\n\t\tif (!con)\n\t\t{\n\t\t\tCon_Printf(\"Not connected\\n\");\n\t\t\treturn false;\n\t\t}\n\n\t\tcmd = COM_ParseOut(cmd, command, sizeof(command));\n\t\tif (!cmd)\n\t\t{\n\t\t\tCon_Printf(\"No file specified\\n\");\n\t\t\treturn false;\n\t\t}\n\n\t\tnewf = FTP_DuplicateConnection(con);\n\t\tif (!newf)\n\t\t{\n\t\t\tCon_Printf(\"Failed duplicate connection\\n\");\n\t\t\treturn false;\n\t\t}\n\t\tnewf->NotifyFunction = NotifyFunction;\n\t\tnewf->type = ftp_putting;\n\t\tsprintf(newf->file, command);\n\t\tsprintf(newf->localfile, \"%s%s\", newf->path, command);\n\n\t\treturn true;\n\t}\n\telse if (!stricmp(command, \"cwd\"))\n\t{\n\t\tFTPclientconn_t *con = FTP_FindControl();\n\t\tif (!con)\n\t\t{\n\t\t\tCon_Printf(\"Not connected\\n\");\n\t\t\treturn false;\n\t\t}\n\t\tCon_Printf(\"%s\\n\", con->path);\n\t\treturn true;\n\t}\n\telse if (!stricmp(command, \"cd\"))\n\t{\n\t\tchar *msg;\n\t\tFTPclientconn_t *con = FTP_FindControl();\n\t\tif (!con)\n\t\t{\n\t\t\tCon_Printf(\"Not connected\\n\");\n\t\t\treturn false;\n\t\t}\n\n\t\tcmd = COM_ParseOut(cmd, command, sizeof(command));\n\n\t\tif (*command == '/')\t//absolute\n\t\t\tQ_strncpyz(con->path, command, sizeof(con->path));\n\t\telse\t//bung it on the end\n\t\t{\n\t\t\tstrncat(con->path, \"/\", sizeof(con->path)-1);\n\t\t\tstrncat(con->path, command, sizeof(con->path)-1);\n\t\t}\n\n\t\tmsg = va(\"CWD %s%s\\r\\n\", con->pathprefix, con->path);\n\t\tif (send(con->controlsock, msg, strlen(msg), 0)==strlen(msg))\n\t\t\treturn true;\n\t}\n\telse\n\t\tCon_Printf(\"Unrecognised FTP command\\n\");\n\t/*\n\tcom = COM_ParseOut(com, command, sizeof(command));\n\tcom = COM_ParseOut(com, command, sizeof(command));\n\tcom = COM_ParseOut(com, command, sizeof(command));\n\t*/\n\n\treturn false;\n}\n\n#endif\n"
  },
  {
    "path": "engine/http/ftpserver.c",
    "content": "#include \"quakedef.h\"\n\n#ifdef WEBSVONLY\n#undef vsnprintf\n#undef _vsnprintf\n#ifdef _WIN32\n#define vsnprintf _vsnprintf\n#endif\n#endif\n\n#ifdef FTPSERVER\n\n#include \"iweb.h\"\n\n//hows this as a bug.\n//TCP data can travel at different speeds.\n//If the later bits of a data channel arrive after the message saying that a transfer was compleate,\n//the later bits of the file may not arrive before the client closes the conenction.\n//this is a major bug and can prevent the server from giving files at a high pl/ping\n\n#include \"netinc.h\"\n\nstatic iwboolean ftpserverinitied = false;\nstatic SOCKET\tftpserversocket = INVALID_SOCKET;\nstatic int\tftpserverport = 0;\nqboolean ftpserverfailed;\n\n\ntypedef struct FTPclient_s{\n\tchar peername[256];\n\tchar name[64];\n\tchar pwd[64];\n\tint auth;\t//has it got auth?\n\tchar path[256];\n\n\tchar renamefrom[256];\n\n\tchar commandbuffer[256];\n\tchar messagebuffer[256];\n\tint cmdbuflen;\n\tint msgbuflen;\n\n\tint controlaf;\n\tSOCKET controlsock;\n\tSOCKET datasock;\t//FTP only allows one transfer per connection.\n\tint dataislisten;\n\tint datadir;\t//0 no data, 1 reading, 2 writing\n\tvfsfile_t *file;\n\n\tqboolean brieflist;\t//incoming list command was an nlist\n\n\tqofs_t restartpos;\n\n\tunsigned long blocking;\n\n#ifdef MULTITHREAD\n\tvoid *transferthread;\n#endif\n\n\tstruct FTPclient_s *next;\n} FTPclient_t;\n\nFTPclient_t *FTPclient;\n\nSOCKET FTP_BeginListening(int aftype, int port)\n{\n\tstruct sockaddr_qstorage address;\n\tunsigned long _true = true;\n\tint i;\n\tSOCKET sock;\n\n\tint af;\n\tint prot;\n\n\tif (!port)\n\t\tport = IWebGetSafeListeningPort();\n\n\tswitch(aftype)\n\t{\n\tcase 0:\n#ifdef IPPROTO_IPV6\n\tcase 2:\n\t\taf = AF_INET6;\n\t\tprot = IPPROTO_TCP;\n\t\tbreak;\n#endif\n\tcase 1:\n\t\taf = AF_INET;\n\t\tprot = IPPROTO_TCP;\n\t\tbreak;\n//\tcase 11:\n//\t\taf = AF_IPX;\n//\t\tprot = NSPROTO_SPX;\n//\t\tbreak;\n\tdefault:\n\t\treturn INVALID_SOCKET;\n\t}\n\n\tif ((sock = socket (af, SOCK_STREAM, prot)) == -1)\n\t{\n\t\tIWebPrintf (\"FTP_BeginListening: socket: %s\\n\", strerror(neterrno()));\n\t\treturn INVALID_SOCKET;\n\t}\n\n\tif (ioctlsocket (sock, FIONBIO, &_true) == -1)\n\t{\n\t\tIWebPrintf (\"FTP_BeginListening: ioctl FIONBIO: %s\", strerror(neterrno()));\n\t\treturn INVALID_SOCKET;\n\t}\n\n#ifdef IPPROTO_IPV6\n\tif (aftype == 0 || aftype == 2)\n\t{\n\t\t//0=ipv4+ipv6\n\t\t//2=ipv6 only\n\t\tif (aftype == 0)\n\t\t{\n\t\t\tunsigned long _false = false;\n\t\t\tif (0 > setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&_false, sizeof(_false)))\n\t\t\t{\n\t\t\t\t//abort and do ipv4 only if hybrid sockets don't work.\n\t\t\t\tclosesocket(sock);\n\t\t\t\treturn FTP_BeginListening(1, port);\n\t\t\t}\n\t\t}\n\t\telse\n\t\t\tsetsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&_true, sizeof(_true));\n\n\t\tmemset(&address, 0, sizeof(address));\n\t\t((struct sockaddr_in6*)&address)->sin6_family = AF_INET6;\n\t\tif (port == PORT_ANY)\n\t\t\t((struct sockaddr_in6*)&address)->sin6_port = 0;\n\t\telse\n\t\t\t((struct sockaddr_in6*)&address)->sin6_port = htons((short)port);\n\t}\n\telse\n#endif\n\t{\n\t\t//1=ipv4 only\n\t\t((struct sockaddr_in*)&address)->sin_family = AF_INET;\n\t//ZOID -- check for interface binding option\n\t\tif ((i = COM_CheckParm(\"-ip\")) != 0 && i < com_argc) {\n\t\t\t((struct sockaddr_in*)&address)->sin_addr.s_addr = inet_addr(com_argv[i+1]);\n\t\t\tCon_TPrintf(\"Binding to IP Interface Address of %s\\n\",\n\t\t\t\t\tinet_ntoa(((struct sockaddr_in*)&address)->sin_addr));\n\t\t} else\n\t\t\t((struct sockaddr_in*)&address)->sin_addr.s_addr = INADDR_ANY;\n\n\t\tif (port == PORT_ANY)\n\t\t\t((struct sockaddr_in*)&address)->sin_port = 0;\n\t\telse\n\t\t\t((struct sockaddr_in*)&address)->sin_port = htons((short)port);\n\t}\n\n\tif( bind (sock, (void *)&address, sizeof(address)) == -1)\n\t{\n\t\tIWebPrintf(\"FTP_BeginListening: failed to bind socket\\n\");\n\t\tclosesocket(ftpserversocket);\n\t\treturn INVALID_SOCKET;\n\t}\n\n\tlisten(sock, 3);\n\n\treturn sock;\n}\n\nvoid FTP_ServerShutdown(void)\n{\n\tclosesocket(ftpserversocket);\n\tftpserversocket = INVALID_SOCKET;\n\tftpserverinitied = false;\n\tIWebPrintf(\"FTP server is deactivated\\n\");\n}\n\nstatic iwboolean FTP_AllowUpLoad(const char *fname, FTPclient_t *cl)\n{\n\tif (cl->auth & IWEBACC_FULL)\n\t\treturn true;\n\tif (!(cl->auth & IWEBACC_WRITE))\n\t\treturn false;\n\n\treturn IWebAllowUpLoad(fname, cl->name);\n}\nstatic iwboolean FTP_AllowDownLoad(const char *fname, FTPclient_t *cl)\n{\n\tif (cl->auth & IWEBACC_FULL)\n\t\treturn true;\n\tif (!(cl->auth & IWEBACC_READ))\n\t\treturn false;\n\n\tif (FTP_AllowUpLoad(fname, cl))\n\t\treturn true;\n\n\treturn SV_AllowDownload(fname);\n}\nstatic iwboolean FTP_AllowList(const char *fname, FTPclient_t *cl)\n{\n\treturn FTP_AllowDownLoad(fname, cl) || FTP_AllowUpLoad(fname, cl);\n}\n\n//we ought to filter this to remove duplicates.\nstatic int QDECL SendFileNameTo(const char *rawname, qofs_t size, time_t mtime, void *param, searchpathfuncs_t *spath)\n{\n\tFTPclient_t *cl = param;\n\tSOCKET socket = cl->datasock;\n//\tint i;\n\tchar buffer[256+1];\n\tchar *slash;\n\tchar nondirname[MAX_QPATH];\n\tint isdir = rawname[strlen(rawname)-1] == '/';\n\tchar *fname;\n\n#ifndef WEBSVONLY\t//copy protection of the like that QWSV normally has.\n\tif (!isdir)\n\t\tif (!FTP_AllowList(rawname, cl))\n\t\t\treturn true;\n#endif\n\n\tQ_strncpyz(nondirname, rawname, sizeof(nondirname));\n\tif (isdir)\n\t\tnondirname[strlen(nondirname)-1] = '\\0';\n\tfname = nondirname;\n\n\twhile((slash = strchr(fname, '/')))\n\t\tfname = slash+1;\n\n\tif (cl->brieflist)\n\t\tQ_snprintfz(buffer, sizeof(buffer), \"%s\\r\\n\", fname);\n\telse\n\t{\n\t\tchar timestamp[32];\n\t\tif (1)\n\t\t\tstrftime(timestamp, sizeof(timestamp), \"%b %d  %Y\", gmtime(&mtime));\n\t\telse\n\t\t\tstrftime(timestamp, sizeof(timestamp), \"%b %d %H:%M\", gmtime(&mtime));\n\n\t\tQ_snprintfz(buffer, sizeof(buffer), \"%c%c%c-------\\t1\\troot\\troot\\t%8\"PRIuQOFS\" %s %s\\r\\n\",\n\t\t\tisdir?'d':'-',\n\t\t\tFTP_AllowDownLoad(rawname, cl)?'r':'-',\n\t\t\tFTP_AllowUpLoad(rawname, cl)?'w':'-',\n\t\t\tsize, timestamp, fname);\n\t}\n\n//\tstrcpy(buffer, fname);\n//\tfor (i = strlen(buffer); i < 40; i+=8)\n//\t\tstrcat(buffer, \"\\t\");\n\tsend(socket, buffer, strlen(buffer), 0);\n\n\treturn true;\n}\n\nSOCKET FTP_SV_makelistensocket(unsigned long nblocking)\n{\n\tchar name[256];\n\tSOCKET sock;\n\tstruct hostent *hent;\n\n\tstruct sockaddr_in\taddress;\n//\tint fromlen;\n\n\taddress.sin_family = AF_INET;\n\tif (gethostname(name, sizeof(name)) == -1)\n\t\treturn INVALID_SOCKET;\n\thent = gethostbyname(name);\n\tif (!hent)\n\t\treturn INVALID_SOCKET;\n\taddress.sin_addr.s_addr = *(int *)(hent->h_addr_list[0]);\n\taddress.sin_port = 0;\n\n\n\n\tif ((sock = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1)\n\t{\n\t\tSys_Error (\"FTP_TCP_OpenSocket: socket: %s\", strerror(neterrno()));\n\t}\n\n\tif (ioctlsocket (sock, FIONBIO, &nblocking) == -1)\n\t{\n\t\tSys_Error (\"FTP_TCP_OpenSocket: ioctl FIONBIO: %s\", strerror(neterrno()));\n\t}\n\n\tif( bind (sock, (void *)&address, sizeof(address)) == -1)\n\t{\n\t\tclosesocket(sock);\n\t\treturn INVALID_SOCKET;\n\t}\n\n\tlisten(sock, 2);\n\n\treturn sock;\n}\nint\tFTP_SVGetSocketPort (SOCKET socket)\n{\n\tstruct sockaddr_qstorage addr;\n\tint adrlen = sizeof(addr);\n\n\tif (getsockname(socket, (struct sockaddr*)&addr, &adrlen) == -1)\n\t\treturn false;\n\n\tif (((struct sockaddr_in*)&addr)->sin_family == AF_INET6)\n\t\treturn ntohs(((struct sockaddr_in6*)&addr)->sin6_port);\n\telse if (((struct sockaddr_in*)&addr)->sin_family == AF_INET)\n\t\treturn ntohs(((struct sockaddr_in*)&addr)->sin_port);\n\telse\n\t\treturn 0; //no idea\n}\n//only to be used for ipv4 sockets.\niwboolean\tFTP_SVSocketToV4String (SOCKET socket, char *s)\n{\n\tstruct sockaddr_qstorage addr;\n\tqbyte *baddr;\n\tint adrlen = sizeof(addr);\n\tchar name[256];\n\tunsigned short port;\n\n\tif (getsockname(socket, (struct sockaddr*)&addr, &adrlen) == -1)\n\t\treturn false;\n\tif (((struct sockaddr_in*)&addr)->sin_family == AF_INET6)\n\t{\n\t\tport = ((struct sockaddr_in6*)&addr)->sin6_port;\n\t\tbaddr = ((struct sockaddr_in6*)&addr)->sin6_addr.s6_addr;\n\t\tif (memcmp(baddr, \"\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\xff\\xff\", 12))\n\t\t\treturn false;\t//must be ipv4-mapped for this ipv4 function\n\t\tbaddr += 12;\n\t}\n\telse if (((struct sockaddr_in*)&addr)->sin_family == AF_INET)\n\t{\n\t\tport = ((struct sockaddr_in*)&addr)->sin_port;\n\t\tbaddr = (qbyte*)&((struct sockaddr_in*)&addr)->sin_addr;\n\t}\n\telse\n\t\treturn false;\n\n\tif (!*(int*)baddr)\n\t{\n\t\t//FIXME doesn't work on anything but windows.\n\t\tif (gethostname(name, sizeof(name)) != -1)\n\t\t{\n\t\t\tstruct hostent *hent = gethostbyname(name);\n\t\t\tif (hent)\n\t\t\t\tbaddr = hent->h_addr_list[0];\n\t\t}\n\t}\n\n\tif (!baddr)\n\t\treturn false;\n\tsprintf(s, \"%i,%i,%i,%i,%i,%i\", baddr[0], baddr[1], baddr[2], baddr[3], ((qbyte *)&port)[0], ((qbyte *)&port)[1]);\n\treturn true;\n}\niwboolean\tFTP_V4StringToAdr (const char *s, struct sockaddr_in *addr)\n{\n\t((qbyte*)&addr->sin_addr)[0] = strtol(s, (char**)&s, 0);\n\tif (*s++ != ',') return false;\n\t((qbyte*)&addr->sin_addr)[1] = strtol(s, (char**)&s, 0);\n\tif (*s++ != ',') return false;\n\t((qbyte*)&addr->sin_addr)[2] = strtol(s, (char**)&s, 0);\n\tif (*s++ != ',') return false;\n\t((qbyte*)&addr->sin_addr)[3] = strtol(s, (char**)&s, 0);\n\tif (*s++ != ',') return false;\n\t((qbyte*)&addr->sin_port)[0] = strtol(s, (char**)&s, 0);\n\tif (*s++ != ',') return false;\n\t((qbyte*)&addr->sin_port)[1] = strtol(s, (char**)&s, 0);\n\n\treturn true;\n}\n\n#if defined(_WIN32) && !defined(WEBSVONLY)\n\tint (WINAPI *pgetaddrinfo) (\n\t  const char* nodename,\n\t  const char* servname,\n\t  const struct addrinfo* hints,\n\t  struct addrinfo** res\n\t);\n\tvoid (WSAAPI *pfreeaddrinfo) (struct addrinfo*);\n#else\n#define pgetaddrinfo getaddrinfo\n#define pfreeaddrinfo freeaddrinfo\n#endif\n\niwboolean FTP_HostToSockaddr(int prot, char *host, int port, struct sockaddr_qstorage *addr, size_t *addrsize)\n{\n\tiwboolean r = false;\n\tstruct addrinfo *res, hint;\n\tchar service[16];\n\n\t*addrsize = 0;\n\n\tmemset(&hint, 0, sizeof(hint));\n\thint.ai_flags = AI_NUMERICHOST;\n\tswitch(prot)\n\t{\n\tcase 1:\n\t\thint.ai_family = AF_INET;\n\t\tbreak;\n\tcase 2:\n\t\thint.ai_family = AF_INET6;\n\t\tbreak;\n\t}\n\tQ_snprintfz(service, sizeof(service), \"%i\", port);\n\tif (pgetaddrinfo(host, service, &hint, &res))\n\t\treturn false;\n\tif (res && res->ai_addr && res->ai_addrlen <= sizeof(*addr))\n\t{\n\t\t*addrsize = res->ai_addrlen;\n\t\tmemcpy(addr, res->ai_addr, res->ai_addrlen);\n\t\tr = true;\n\t}\n\tpfreeaddrinfo(res);\n\treturn r;\n#if 0\n\thost = va(\"[%s]\", host);\n\treturn NET_StringToSockaddr(host, port, addr, NULL, NULL);\n#endif\n}\n\n/*\n *\tResponsable for sending all control server -> client messages.\n *\tQueues the message if it cannot send now.\n *\tKicks if too big a queue.\n*/\nvoid QueueMessage(FTPclient_t *cl, char *msg)\n{\n\tIWebDPrintf(\"FTP> %s\", msg);\n\tif (send (cl->controlsock, msg, strlen(msg), 0) == -1)\n\t{\t//wasn't sent\n\t\tif (strlen(msg) + strlen(cl->messagebuffer) >= sizeof(cl->messagebuffer)-1)\n\t\t{\n\t\t\tclosesocket(cl->controlsock);\t//but don't mark it as closed, so we get errors later (for this is how we shall tell).\n\t\t\treturn;\n\t\t}\n\t\tstrcat(cl->messagebuffer, msg);\n\t}\n}\n\nvoid VARGS QueueMessageva(FTPclient_t *cl, char *fmt, ...)\n{\n\tva_list\t\targptr;\n\tchar\t\tmsg[1024];\n\n\tva_start (argptr, fmt);\n\tvsnprintf (msg,sizeof(msg)-1, fmt,argptr);\n\tmsg[sizeof(msg)-1] = 0;\n\tva_end (argptr);\n\n\tIWebDPrintf(\"FTP> %s\", msg);\n\tif (send (cl->controlsock, msg, strlen(msg), 0) == -1)\n\t{\t//wasn't sent\n\t\tif (strlen(msg) + strlen(cl->messagebuffer) >= sizeof(cl->messagebuffer)-1)\n\t\t{\n\t\t\tclosesocket(cl->controlsock);\n\t\t\tcl->controlsock = INVALID_SOCKET;\n\t\t}\n\t\tstrcat(cl->messagebuffer, msg);\n\t}\n}\n\n//if eg: \"RETR Some File Name.txt \\r\\n\", and the RETR was already parsed, we'll read out the Some File Name until the \\r\\n\n//returns a directly-usable game path.\nqboolean FTP_ReadToAbsFilename(FTPclient_t *cl, const char *msg, char *out, size_t outsize)\n{\n\tsize_t len = 0;\n\tconst char *end;\n\tif (*msg == ' ')\n\t\tmsg++;\n\telse\n\t{\n\t\t*out = 0;\n\t\treturn false;\n\t}\n\tend = msg;\n\twhile(*end)\n\t{\n\t\tif (*end == '\\r' || *end == '\\n')\n\t\t\tbreak;\n\t\tend++;\n\t}\n\t//trim stupid trailing space that windows insists on fucking shit up with\n\tif (end > msg && end[-1] == ' ')\n\t\tend--;\n\n\t//figure out the root\n\tif (*msg == '/')\n\t\tmsg++;\n\telse\n\t{\t//FIXME: nuke the +1s\n\t\tlen = strlen(cl->path);\n\t\tif (len >= outsize)\n\t\t{\t//too long...\n\t\t\t*out = 0;\n\t\t\treturn false;\n\t\t}\n\t\tmemcpy(out, cl->path, len);\n\t}\n\n\twhile (msg < end)\n\t{\n\t\tif (*msg == '/')\n\t\t\tmsg++;\t//double slashes will be silently ignored. also simplifies ../ etc.\n\t\telse if (!strncmp(msg, \"..\", 2) && (msg+2 == end || msg[2] == '/'))\n\t\t{\t// \"/foo/../bar\" should end up as just \"bar\"\n\t\t\tmsg+=2;\n\t\t\tif (!len)\n\t\t\t{\t//can't go above root...\n\t\t\t\t*out = 0;\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\twhile (len > 0)\n\t\t\t{\n\t\t\t\tif (out[--len] == '/')\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\telse if (!strncmp(msg, \".\", 1) && (msg+1 == end || msg[1] == '/'))\n\t\t{\t//filenames relative to the working directory are stupid, but whatever\n\t\t\tmsg+=1;\n\t\t\twhile (len > 0)\n\t\t\t{\n\t\t\t\tif (out[--len] == '/')\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tconst char *s;\n\t\t\tfor (s = msg; s < end; s++)\n\t\t\t{\n\t\t\t\tif (*s == '/')\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (s == msg)\n\t\t\t{\t//error...\n\t\t\t\t*out = 0;\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tif (len + s-msg + 2 > outsize)\n\t\t\t\tbreak;\n\t\t\tif (len)\n\t\t\t\tout[len++] = '/';\n\t\t\tmemcpy(out+len, msg, s-msg);\n\t\t\tlen += s-msg;\n\t\t\tmsg = s;\n\t\t}\n\t}\n\tout[len] = 0;\n\treturn true;\n}\n\n#ifdef MULTITHREAD\nint FTP_TransferThread(void *vcl)\n{\n\tchar resource[8192];\n\tFTPclient_t *cl = vcl;\n\tu_long _false = false;\n\tioctlsocket (cl->datasock, FIONBIO, &_false);\n\n\tif ((cl->datadir&~64) == 1)\n\t{\n\t\twhile(1)\n\t\t{\n\t\t\tint ammount = VFS_READ(cl->file, resource, sizeof(resource));\n\t\t\tint chunk, sent;\n\t\t\tif (ammount <= 0)\n\t\t\t\tbreak;\n\t\t\tfor (sent = 0; sent < ammount; sent += chunk)\n\t\t\t{\n\t\t\t\tchunk = send(cl->datasock, resource, ammount-sent, 0);\n\t\t\t\tif (chunk <= 0)\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (ammount != sent)\n\t\t\t\tbreak;\n\t\t}\n\t}\n\telse if ((cl->datadir&~64) == 2)\n\t{\n\t\twhile(1)\n\t\t{\n\t\t\tint ammount = recv(cl->datasock, resource, sizeof(resource), 0);\n\t\t\tif (ammount <= 0)\n\t\t\t\tbreak;\n\t\t\tif (ammount != VFS_WRITE(cl->file, resource, ammount))\n\t\t\t\tbreak;\n\t\t}\n\t}\n\tcl->datadir &= ~64;\n\n\treturn 0;\n}\n#endif\n\niwboolean FTP_ServerThinkForConnection(FTPclient_t *cl)\n{\n\tint \tret;\n\tstruct sockaddr_qstorage\tfrom;\n\tint\t\tfromlen;\n\tchar *msg, *line;\n\n\tchar mode[64];\n\tchar resource[8192];\n\tint _true = true;\n\n#ifdef MULTITHREAD\n\tif (cl->datadir & 64)\n\t{\n\t\tif (*cl->messagebuffer)\n\t\t{\t//fixme: gah!\n\t\t\tif (send (cl->controlsock, cl->messagebuffer, strlen(cl->messagebuffer), 0) != -1)\n\t\t\t\t*cl->messagebuffer = '\\0';\t//YAY! It went!\n\t\t}\n\t\treturn false;\n\t}\n\tif (cl->transferthread)\n\t{\n\t\tSys_WaitOnThread(cl->transferthread);\n\t\tcl->transferthread = NULL;\n\t}\n#endif\n\n\tif (cl->datadir == 1)\n\t{\n\t\tint pos, sent;\n\t\tint ammount, wanted = sizeof(resource);\n\n\t\tpos = VFS_TELL(cl->file);\n\t\tammount = VFS_READ(cl->file, resource, wanted);\n\t\tsent = send(cl->datasock, resource, ammount, 0);\n\n\t\tif (sent == -1)\n\t\t{\n\t\t\tVFS_SEEK(cl->file, pos);\n\t\t\tif (neterrno() != NET_EWOULDBLOCK)\n\t\t\t{\n\t\t\t\tclosesocket(cl->datasock);\n\t\t\t\tcl->datasock = INVALID_SOCKET;\n\t\t\t\tVFS_CLOSE(cl->file);\n\t\t\t\tcl->file = NULL;\n\n\t\t\t\tQueueMessage (cl, \"226 Transfer complete .\\r\\n\");\n\t\t\t\tcl->datadir = 0;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (sent != ammount)\n\t\t\t\tVFS_SEEK(cl->file, pos + sent);\n\n\t\t\tif (ammount != wanted && sent == ammount)\t//file is over\n\t\t\t{\n\t\t\t\tsend(cl->datasock, resource, 0, 0);\n\t\t\t\tsend(cl->datasock, resource, 0, 0);\n\t\t\t\tsend(cl->datasock, resource, 0, 0);\n\t\t\t\tclosesocket(cl->datasock);\n\t\t\t\tcl->datasock = INVALID_SOCKET;\n\t\t\t\tVFS_CLOSE(cl->file);\n\t\t\t\tcl->file = NULL;\n\n\t\t\t\tQueueMessage (cl, \"226 Transfer complete .\\r\\n\");\n\t\t\t\tcl->datadir = 0;\n\t\t\t}\n\t\t}\n\n\t\tpos = cl->datadir?1:!cl->blocking;\n\t\tif (ioctlsocket (cl->controlsock, FIONBIO, (u_long *)&pos) == -1)\n\t\t{\n\t\t\tIWebPrintf (\"FTP_ServerRun: blocking error: %s\\n\", strerror(neterrno()));\n\t\t\treturn 0;\n\t\t}\n\t}\n\telse if (cl->datadir == 2)\n\t{\n\t\tint len;\n\t\twhile((len = recv(cl->datasock, resource, sizeof(resource), 0)) >0 )\n\t\t{\n\t\t\tVFS_WRITE(cl->file, resource, len);\n\t\t}\n\t\tif (len == -1)\n\t\t{\n\t\t\tif (neterrno() != NET_EWOULDBLOCK)\n\t\t\t{\n\t\t\t\tclosesocket(cl->datasock);\n\t\t\t\tcl->datasock = INVALID_SOCKET;\n\t\t\t\tif (cl->file)\n\t\t\t\t\tVFS_CLOSE(cl->file);\n\t\t\t\tcl->file = NULL;\n\n\t\t\t\tQueueMessage (cl, \"226 Transfer complete .\\r\\n\");\n\t\t\t\tcl->datadir = 0;\n\t\t\t}\n\t\t}\n\t\tif (len == 0)\n\t\t{\n\t\t\tQueueMessage (cl, \"226 Transfer complete .\\r\\n\");\n\t\t\tVFS_CLOSE(cl->file);\n\t\t\tcl->file = NULL;\n\t\t\tcl->datadir = 0;\n\t\t}\n\t}\n\n\tret = recv(cl->controlsock, cl->commandbuffer+cl->cmdbuflen, sizeof(cl->commandbuffer)-1 - cl->cmdbuflen, 0);\n\tif (ret == -1)\n\t{\n\t\tint e = neterrno();\n\t\tif (e == NET_EWOULDBLOCK)\n\t\t\treturn false;\t//remove\n\n\t\tif (e == NET_ECONNABORTED || e == NET_ECONNRESET)\n\t\t\treturn true;\n\n\t\tIWebPrintf (\"NET_GetPacket: %s\\n\", strerror(e));\n\t\treturn true;\n\t}\n\tif (*cl->messagebuffer)\n\t{\n\t\tif (send (cl->controlsock, cl->messagebuffer, strlen(cl->messagebuffer), 0) != -1)\n\t\t\t*cl->messagebuffer = '\\0';\t//YAY! It went!\n\n\t}\n\n\tif (ret == 0)\n\t\treturn false;\n\tcl->cmdbuflen += ret;\n\tcl->commandbuffer[cl->cmdbuflen] = 0;\n\n\tline = cl->commandbuffer;\n\twhile (1)\n\t{\n\t\tmsg = line;\n\t\twhile (*line)\n\t\t{\n\t\t\tif (*line == '\\r')\n\t\t\t\t*line = ' ';\n\t\t\tif (*line == '\\n')\n\t\t\t\tbreak;\n\t\t\tline++;\n\t\t}\n\t\tif (!*line)\t//broken client\n\t\t{\n\t\t\tmemmove(cl->commandbuffer, line, strlen(line)+1);\n\t\t\tcl->cmdbuflen = strlen(line);\n\t\t\tbreak;\n\t\t}\n\t\t*line = '\\0';\n\t\tline++;\n\t\tIWebDPrintf(\"FTP: %s\\n\", msg);\n\n\t\tmsg = COM_ParseOut(msg, mode, sizeof(mode));\n\t\tif (!stricmp(mode, \"SYST\"))\n\t\t{\n\t\t\tQueueMessage (cl, \"215 UNIX Type: L8.\\r\\n\");\t//some browsers can be wierd about things.\n\t\t}\n\t\telse if (!stricmp(mode, \"FEAT\"))\n\t\t{\n\t\t\tQueueMessage (cl, \"211-Extensions supported:\\r\\n\");\n\t\t\tQueueMessage (cl, \" SIZE\\r\\n\");\n\t\t\tQueueMessage (cl, \" REST\\r\\n\");\n\t\t\tQueueMessage (cl, \" EPSV\\r\\n\");\n\t\t\tQueueMessage (cl, \" EPRT\\r\\n\");\n//\t\t\tQueueMessage (cl, \" MDTM\\r\\n\");\n\t\t\tQueueMessage (cl, \"211 End\\r\\n\");\n\t\t}\n\t\telse if (!stricmp(mode, \"user\"))\n\t\t{\n\t\t\tmsg = COM_ParseOut(msg, cl->name, sizeof(cl->name));\n\t\t\tcl->auth = 0;\t//any access rights go away now, so they can't spoof read access with dodgy filenames.\n\n\t\t\tQueueMessage (cl, \"331 User name received, will be checked with password.\\r\\n\");\n\t\t}\n\t\telse if (!stricmp(mode, \"pass\"))\n\t\t{\n\t\t\tmsg = COM_ParseOut(msg, cl->pwd, sizeof(cl->pwd));\n\n\t\t\tcl->auth = IWebAuthorize(cl->name, cl->pwd);\n\n\t\t\tif (cl->auth)\n\t\t\t\tQueueMessage (cl, \"230 User logged in.\\r\\n\");\n\t\t\telse\n\t\t\t\tQueueMessage (cl, \"530 Username or Password was incorrect or otherwise invalid.\\r\\n\");\n\t\t}\n\t\telse if (!stricmp(mode, \"TYPE\"))\n\t\t{\n\t\t\tif (!cl->auth)\n\t\t\t{\n\t\t\t\tQueueMessage (cl, \"530 Not logged in.\\r\\n\");\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tmsg = COM_ParseOut(msg, resource, sizeof(resource));\n\n\t\t\tif (!stricmp(resource, \"A\"))\t//ascii\n\t\t\t{\n\t\t\t\tQueueMessage (cl, \"200 ascii selected.\\r\\n\");\n\t\t\t}\n\t\t\telse if (!stricmp(resource, \"I\"))\t//binary\n\t\t\t{\n\t\t\t\tQueueMessage (cl, \"200 binary selected.\\r\\n\");\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tQueueMessage (cl, \"200 ascii selected.\\r\\n\");\n\t\t\t}\n\t\t}\n\t\telse if (!stricmp(mode, \"PWD\"))\n\t\t{\n\t\t\tif (!cl->auth)\n\t\t\t{\n\t\t\t\tQueueMessage (cl, \"530 Not logged in.\\r\\n\");\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (*cl->path)\n\t\t\t\tQueueMessageva (cl, \"257 \\\"/%s/\\\"\\r\\n\", cl->path);\n\t\t\telse\n\t\t\t\tQueueMessageva (cl, \"257 \\\"/\\\"\\r\\n\");\n\t\t}\n\t\telse if (!stricmp(mode, \"CWD\"))\n\t\t{\n\t\t\tif (!cl->auth)\n\t\t\t{\n\t\t\t\tQueueMessage (cl, \"530 Not logged in.\\r\\n\");\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (!FTP_ReadToAbsFilename(cl, msg, resource, sizeof(resource)))\n\t\t\t{\n\t\t\t\tQueueMessage (cl, \"550 invalid path.\\r\\n\");\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tQ_strncpyz(cl->path, resource, sizeof(cl->path));\n\t\t\tQueueMessage (cl, \"200 directory changed.\\r\\n\");\n\t\t}\n\t\telse if (!stricmp(mode, \"MKD\"))\n\t\t{\n\t\t\t//create directory\n\t\t\tFTP_ReadToAbsFilename(cl, msg, resource, sizeof(resource));\n\t\t\tif (!cl->auth)\n\t\t\t{\n\t\t\t\tQueueMessage (cl, \"530 Not logged in.\\r\\n\");\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (!*resource || !FTP_AllowUpLoad(resource, cl))\n\t\t\t{\n\t\t\t\tIWebPrintf(\"%s: Denied mkdir request for \\\"ftp://%s@%s/%s\\\"\\n\", cl->peername, cl->name, \"\", resource);\n\t\t\t\tQueueMessage (cl, \"550 Access denied.\\r\\n\");\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tIWebPrintf(\"%s: Mkdir request for \\\"ftp://%s@%s/%s\\\"\\n\", cl->peername, cl->name, \"\", resource);\n\n\t\t\tQ_strncatz(resource, \"/\", sizeof(resource));\n\t\t\tFS_CreatePath(resource, FS_GAMEONLY);\n\t\t\tQueueMessage (cl, \"250 Success.\\r\\n\");\n\t\t}\n\t\telse if (!stricmp(mode, \"EPSV\"))\n\t\t{\n\t\t\tint aftype = 0;\n\t\t\t//one argument, \"1\"=ipv4, \"2\"=ipv6, \"11\"=ipx (by rfc 1700). if not present, use same as control connection\n\t\t\t//reply: \"229 Entering Extended Passive Mode (|||$PORTNUM|)\\r\\n\"\n\n\t\t\twhile(*msg == ' ')\n\t\t\t\tmsg++;\n\t\t\tif (!strncmp(msg, \"ALL\", 3))\n\t\t\t\tcontinue;\t//rfc2428 'EPSV ALL' is a signal to client NATs that PORT/EPRT/PASV will not be used, and that they can just treat it as a regular TCP connection same as anything else. we 'must' also refuse any of those commands too, but we shouldn't receive them anyway.\n\t\t\taftype = atoi(msg);\n\t\t\tif (!aftype)\n\t\t\t\taftype = cl->controlaf;\n\n\t\t\tif (!cl->auth)\n\t\t\t{\n\t\t\t\tQueueMessage (cl, \"530 Not logged in.\\r\\n\");\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (cl->datasock != INVALID_SOCKET)\n\t\t\t{\n\t\t\t\tclosesocket(cl->datasock);\n\t\t\t\tcl->datasock = INVALID_SOCKET;\n\t\t\t}\n\n\t\t\tcl->datasock = FTP_BeginListening(aftype, 0);\n\t\t\tif (cl->datasock == INVALID_SOCKET)\n\t\t\t\tQueueMessage (cl, \"425 server was unable to make a listen socket\\r\\n\");\n\t\t\telse\n\t\t\t{\n\t\t\t\tQueueMessageva (cl, \"229 Entering Extended Passive Mode (|||%i|).\\r\\n\", FTP_SVGetSocketPort(cl->datasock));\n\t\t\t}\n\t\t\tcl->dataislisten = true;\n\t\t}\n\t\telse if (!stricmp(mode, \"PASV\"))\n\t\t{\n\t\t\tif (!cl->auth)\n\t\t\t{\n\t\t\t\tQueueMessage (cl, \"530 Not logged in.\\r\\n\");\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (cl->datasock != INVALID_SOCKET)\n\t\t\t{\n\t\t\t\tclosesocket(cl->datasock);\n\t\t\t\tcl->datasock = INVALID_SOCKET;\n\t\t\t}\n\n\t\t\tcl->datasock = FTP_BeginListening(1, 0);\n\t\t\tif (cl->datasock == INVALID_SOCKET)\n\t\t\t\tQueueMessage (cl, \"425 server was unable to make a listen socket\\r\\n\");\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (FTP_SVSocketToV4String(cl->datasock, resource))\n\t\t\t\t\tQueueMessageva (cl, \"227 Entering Passive Mode (%s).\\r\\n\", resource);\n\t\t\t\telse\n\t\t\t\t\tQueueMessageva (cl, \"550 Unable to parse address.\\r\\n\", resource);\n\t\t\t}\n\t\t\tcl->dataislisten = true;\n\t\t}\n\t\telse if (!stricmp(mode, \"EPRT\"))\n\t\t{\n\t\t\t//eg: one of:\n\t\t\t//EPRT |1|132.235.1.2|6275|\n\t\t\t//EPRT |2|1080::8:800:200C:417A|5282|\n\n\t\t\t//reply: 522 Network protocol not supported, use (1,2)\n\n\t\t\tchar d;\n\t\t\tint prot;\n\t\t\tint port;\n\t\t\tchar *eon, *host;\n\t\t\tstruct sockaddr_qstorage peer;\n\t\t\tsize_t peersize;\n\t\t\twhile(*msg == ' ')\n\t\t\t\tmsg++;\n\t\t\td = *msg++;\n\t\t\tprot = strtol(msg, &msg, 0);\n\t\t\thost = ++msg;\n\t\t\teon = strchr(msg, d);\n\t\t\tif (eon)\n\t\t\t{\n\t\t\t\t*eon++ = 0;\n\t\t\t\tmsg = eon;\n\t\t\t}\n\t\t\tcl->dataislisten = false;\n\t\t\tif (cl->datasock != INVALID_SOCKET)\n\t\t\t\tclosesocket(cl->datasock);\n\t\t\tcl->datasock = INVALID_SOCKET;\n\n\t\t\tport = strtol(msg, &msg, 0);\n\t\t\tif (*msg != d || !eon || !FTP_HostToSockaddr(prot, host, port, &peer, &peersize))\n\t\t\t\tQueueMessage (cl, \"522 Network protocol not supported, use (1,2).\\r\\n\");\n\t\t\telse\n\t\t\t{\n\t\t\t\tmemset(&from, 0, sizeof(from));\n\t\t\t\t((struct sockaddr*)&from)->sa_family = ((struct sockaddr*)&peer)->sa_family;\n\t\t\t\tif ((cl->datasock = socket (((struct sockaddr*)&from)->sa_family, SOCK_STREAM, IPPROTO_TCP)) != -1)\n\t\t\t\t{\n\t\t\t\t\tif (ioctlsocket (cl->datasock, FIONBIO, (u_long *)&_true) != -1)\n\t\t\t\t\tif( bind (cl->datasock, (void *)&from, peersize) != -1)\n\t\t\t\t\t{\n\t\t\t\t\t\tconnect(cl->datasock, (struct sockaddr *)&peer, peersize);\n\t\t\t\t\t\tQueueMessage (cl, \"200 Opened data channel.\\r\\n\");\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tclosesocket(cl->datasock);\n\t\t\t\t\tcl->datasock=INVALID_SOCKET;\n\t\t\t\t}\n\t\t\t\tQueueMessage (cl, \"550 Command not fully implemented.\\r\\n\");\n\t\t\t}\n\t\t}\n\t\telse if (!stricmp(mode, \"PORT\"))\n\t\t{\n\t\t\tif (!cl->auth)\n\t\t\t{\n\t\t\t\tQueueMessage (cl, \"530 Not logged in.\\r\\n\");\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (cl->datasock != INVALID_SOCKET)\n\t\t\t{\n\t\t\t\tclosesocket(cl->datasock);\n\t\t\t\tcl->datasock = INVALID_SOCKET;\n\t\t\t}\n\t\t\tmsg = COM_ParseOut(msg, resource, sizeof(resource));\n\n\t\t\tcl->dataislisten = false;\n\n\t\t\tmemset(&from, 0, sizeof(from));\n\t\t\t((struct sockaddr_in*)&from)->sin_family = AF_INET;\n\n\t\t\tif ((cl->datasock = socket (((struct sockaddr*)&from)->sa_family, SOCK_STREAM, IPPROTO_TCP)) == -1)\n\t\t\t{\n\t\t\t\tSys_Error (\"FTP_ServerThinkForConnection: socket: %s\", strerror(neterrno()));\n\t\t\t}\n\n\t\t\tif (ioctlsocket (cl->datasock, FIONBIO, (u_long *)&_true) == -1)\n\t\t\t{\n\t\t\t\tSys_Error (\"FTP_ServerThinkForConnection: ioctl FIONBIO: %s\", strerror(neterrno()));\n\t\t\t}\n\n\t\t\tif( bind (cl->datasock, (void *)&from, sizeof(from)) == -1)\n\t\t\t{\n\t\t\t\tclosesocket(cl->datasock);\n\t\t\t\tcl->datasock=INVALID_SOCKET;\n\n\t\t\t\tQueueMessage (cl, \"425 server bind error.\\r\\n\");\n\t\t\t\tcontinue;\n\t\t\t}\n\n\n\t\t\tfromlen = sizeof(from);\n\t\t\tif (FTP_V4StringToAdr(resource, (struct sockaddr_in *)&from))\n\t\t\t{\n\t\t\t\tconnect(cl->datasock, (struct sockaddr *)&from, fromlen);\n\n\t\t\t\tQueueMessage (cl, \"200 Opened data channel.\\r\\n\");\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tclosesocket(cl->datasock);\n\t\t\t\tcl->datasock=INVALID_SOCKET;\n\n\t\t\t\tQueueMessage (cl, \"425 server resolve error.\\r\\n\");\n\t\t\t}\n\t\t}\n\t\telse if (!stricmp(mode, \"LIST\") || !stricmp(mode, \"NLST\"))\n\t\t{\n\t\t\tchar buffer[256];\n\t\t\tcl->brieflist = !stricmp(mode, \"NLST\");\n\t\t\tif (!cl->auth)\n\t\t\t{\n\t\t\t\tQueueMessage (cl, \"530 Not logged in.\\r\\n\");\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (cl->dataislisten)\t//accept a connect.\n\t\t\t{\n\t\t\t\tint err;\n\t\t\t\tint _true = true;\n\t\t\t\tint temp;\n\t\t\t\tstruct sockaddr_qstorage adr;\n\t\t\t\tint adrlen = sizeof(adr);\n\t\t\t\ttemp = accept(cl->datasock, (struct sockaddr *)&adr, &adrlen);\n\t\t\t\tclosesocket(cl->datasock);\n\t\t\t\tcl->datasock = temp;\n\t\t\t\tcl->dataislisten = false;\n\n\t\t\t\tif (cl->datasock == INVALID_SOCKET)\n\t\t\t\t{\n\t\t\t\t\terr = neterrno();\n\t\t\t\t\tQueueMessageva (cl, \"425 Can't accept pasv data connection - %i.\\r\\n\", err);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tioctlsocket(cl->datasock, FIONBIO, (u_long *)&_true);\n\t\t\t}\n\t\t\tif (cl->datasock == INVALID_SOCKET)\n\t\t\t{\n\t\t\t\tQueueMessage (cl, \"503 Bad sequence of commands.\\r\\n\");\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (*cl->path == '/')\n\t\t\t\tstrcpy(buffer, cl->path+1);\n\t\t\telse\n\t\t\t\tstrcpy(buffer, cl->path);\n\n\t\t\tif (*buffer)\t//last character should be a /\n\t\t\t\tif (buffer[strlen(buffer)-1] != '/')\n\t\t\t\t\tstrcat(buffer, \"/\");\n\n\t\t\tstrcat(buffer, \"*\");\n\t\t\tQueueMessage (cl, \"125 Opening FAKE ASCII mode data connection for file.\\r\\n\");\n\n\t\t\tCOM_EnumerateFiles(buffer, SendFileNameTo, cl);\n\n\t\t\tQueueMessage (cl, \"226 Transfer complete.\\r\\n\");\n\n\t\t\tclosesocket(cl->datasock);\n\t\t\tcl->datasock = INVALID_SOCKET;\n\t\t}\n\t\t/*else if (!stricmp(mode, \"MDTM\"))\n\t\t{\n\t\t\tchar ospath[MAX_OSPATH];\n\t\t\tstruct tm *t;\n\t\t\tFTP_ReadToAbsFilename(cl, msg, resource, sizeof(resource));\n\t\t\tif (FS_NativePath(resource, FS_GAME, ospath, sizeof(ospath)))\n\t\t\t{\n\t\t\t\tt = gmtime(Sys_FileTime(path));\n\t\t\t\tQueueMessageva (cl, \"213 %04i%02i%02i%02i%02i%02i\\r\\n\",\n\t\t\t\t\t1900+t->tm_year, 1+t->tm->mon, 1+t->tm->mday,\n\t\t\t\t\tt->tm->hour, t->tm->min, t->tm_sec );\n\t\t\t}\n\t\t\telse\n\t\t\t\tQueueMessageva (cl, \"550 unavailable.\\r\\n\", size);\n\t\t}*/\n\t\telse if (!stricmp(mode, \"SIZE\"))\t//why IE can't use the list command to find file length, I've no idea.\n\t\t{\n\t\t\t//STRU, MODE, and TYPE may change the reported size...\n\t\t\tvfsfile_t *f;\n\t\t\tqofs_t size = qofs_ErrorValue();\n\t\t\tFTP_ReadToAbsFilename(cl, msg, resource, sizeof(resource));\n\n\t\t\tif (*resource && FTP_AllowList(resource, cl))\n\t\t\t{\n\t\t\t\tf = FS_OpenVFS(resource, \"rb\", FS_GAME);\n\t\t\t\tif (f)\n\t\t\t\t{\n\t\t\t\t\tsize = VFS_GETLEN(f);\n\t\t\t\t\tVFS_CLOSE(f);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (qofs_Error(size))\n\t\t\t\tQueueMessageva (cl, \"550 Couldn't read file.\\r\\n\", size);\n\t\t\telse\n\t\t\t\tQueueMessageva (cl, \"213 %\"PRIuQOFS\"\\r\\n\", size);\n\t\t}\n\t\telse if (!stricmp(mode, \"REST\"))\n\t\t{\n\t\t\tCOM_ParseOut(msg, resource, sizeof(resource));\n\t\t\tcl->restartpos = strtoull(resource, NULL, 0);\n\t\t}\n\t\telse if (!stricmp(mode, \"RETR\"))\n\t\t{\n\t\t\tqboolean waspassive = cl->dataislisten;\n\t\t\tif (!cl->auth)\n\t\t\t{\n\t\t\t\tQueueMessage (cl, \"530 Not logged in.\\r\\n\");\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (cl->dataislisten)\t//accept a connect.\n\t\t\t{\n\t\t\t\tint _true = true;\n\t\t\t\tint temp;\n\t\t\t\tstruct sockaddr_qstorage adr;\n\t\t\t\tint adrlen = sizeof(adr);\n\t\t\t\ttemp = accept(cl->datasock, (struct sockaddr *)&adr, &adrlen);\n\t\t\t\tclosesocket(cl->datasock);\n\t\t\t\tcl->datasock = temp;\n\t\t\t\tcl->dataislisten = false;\n\n\t\t\t\tif (cl->datasock == INVALID_SOCKET)\n\t\t\t\t{\n\t\t\t\t\tQueueMessageva (cl, \"425 Can't accept pasv data connection - %i.\\r\\n\", neterrno());\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tioctlsocket(cl->datasock, FIONBIO, (u_long *)&_true);\n\t\t\t}\n\t\t\tif (cl->datasock == INVALID_SOCKET)\n\t\t\t{\n\t\t\t\tQueueMessage (cl, \"503 Bad sequence of commands.\\r\\n\");\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tFTP_ReadToAbsFilename(cl, msg, resource, sizeof(resource));\n\n\t\t\tIWebPrintf(\"%s: Download request for \\\"ftp://%s@%s/%s\\\"\\n\", cl->peername, cl->name, \"\", resource);\n\n\t\t\tif (FTP_AllowDownLoad(resource, cl))\n\t\t\t\tcl->file = FS_OpenVFS(resource, \"rb\", FS_GAME);\n\t\t\telse\n\t\t\t\tcl->file = IWebGenerateFile(resource, NULL, 0);\n\n\t\t\tif (!cl->file)\n\t\t\t{\n\t\t\t\tQueueMessage (cl, \"550 File not found.\\r\\n\");\n\t\t\t}\n\t\t\telse\n\t\t\t{\t//send data\n\t\t\t\tif (waspassive)\n\t\t\t\t\tQueueMessage (cl, \"150 Opening BINARY mode data connection for file.\\r\\n\");\n\t\t\t\telse\n\t\t\t\t\tQueueMessage (cl, \"125 Opening BINARY mode data connection for file.\\r\\n\");\n\n\t\t\t\tcl->datadir = 1;\n\n\t\t\t\tif (cl->restartpos)\n\t\t\t\t\tVFS_SEEK(cl->file, cl->restartpos);\n\t\t\t}\n\t\t\tcl->restartpos = 0;\n\t\t}\n\t\telse if (!stricmp(mode, \"STOR\") || !stricmp(mode, \"APPE\"))\n\t\t{\n\t\t\tif (!cl->auth)\n\t\t\t{\n\t\t\t\tQueueMessage (cl, \"530 Not logged in.\\r\\n\");\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tFTP_ReadToAbsFilename(cl, msg, resource, sizeof(resource));\n\n\t\t\tif (!*resource || !FTP_AllowUpLoad(resource, cl))\n\t\t\t{\n\t\t\t\tIWebPrintf(\"%s: Denied upload request for \\\"ftp://%s@%s/%s\\\"\\n\", cl->peername, cl->name, \"\", resource);\n\t\t\t\tQueueMessage (cl, \"550 Permission denied.\\r\\n\");\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (cl->dataislisten)\t//accept a connect.\n\t\t\t\t{\n\t\t\t\t\tint _true = true;\n\t\t\t\t\tint temp;\n\t\t\t\t\tstruct sockaddr_qstorage adr;\n\t\t\t\t\tint adrlen = sizeof(adr);\n\t\t\t\t\ttemp = accept(cl->datasock, (struct sockaddr *)&adr, &adrlen);\n\t\t\t\t\tclosesocket(cl->datasock);\n\t\t\t\t\tcl->datasock = temp;\n\t\t\t\t\tcl->dataislisten = false;\n\n\t\t\t\t\tif (cl->datasock == INVALID_SOCKET)\n\t\t\t\t\t{\n\t\t\t\t\t\tQueueMessageva (cl, \"425 Can't accept pasv data connection - %i.\\r\\n\", neterrno());\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\tioctlsocket(cl->datasock, FIONBIO, (u_long *)&_true);\n\t\t\t\t}\n\t\t\t\tif (cl->datasock == INVALID_SOCKET)\n\t\t\t\t{\n\t\t\t\t\tQueueMessage (cl, \"502 Bad sequence of commands.\\r\\n\");\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tIWebPrintf(\"%s: Upload request for \\\"ftp://%s@%s/%s\\\"\\n\", cl->peername, cl->name, \"\", resource);\n\n\t\t\t\tif (cl->restartpos || !stricmp(mode, \"APPE\"))\t//write without truncating.\n\t\t\t\t\tcl->file = FS_OpenVFS(resource, \"w+b\", FS_GAMEONLY);\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tcl->file = FS_OpenVFS(resource, \"rb\", FS_GAMEONLY);\n\t\t\t\t\tif (cl->file)\n\t\t\t\t\t{\n\t\t\t\t\t\tVFS_CLOSE(cl->file);\n\t\t\t\t\t\tQueueMessage (cl, \"550 File already exists.\\r\\n\");\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tcl->file = FS_OpenVFS(resource, \"wb\", FS_GAME);\n\t\t\t\t}\n\n\t\t\t\tif (!cl->file)\n\t\t\t\t{\n\t\t\t\t\tQueueMessage (cl, \"550 Couldn't open output.\\r\\n\");\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\t//send data\n\t\t\t\t\tQueueMessage (cl, \"125 Opening BINARY mode data connection for input.\\r\\n\");\n\n\t\t\t\t\tcl->datadir = 2;\n\n\t\t\t\t\tif (cl->restartpos)\n\t\t\t\t\t\tVFS_SEEK(cl->file, cl->restartpos);\n\t\t\t\t\telse if (!stricmp(mode, \"APPE\"))\n\t\t\t\t\t\tVFS_SEEK(cl->file, VFS_GETLEN(cl->file));\n\t\t\t\t}\n\t\t\t\tcl->restartpos = 0;\n\t\t\t}\n\t\t}\n\t\telse if (!stricmp(mode, \"RNFR\"))\n\t\t{\n\t\t\tFTP_ReadToAbsFilename(cl, msg, cl->renamefrom, sizeof(cl->renamefrom));\n\n\t\t\tQueueMessage (cl, \"350 Success.\\r\\n\");\n\t\t}\n\t\telse if (!stricmp(mode, \"RNTO\"))\n\t\t{\n\t\t\tFTP_ReadToAbsFilename(cl, msg, resource, sizeof(resource));\n\n\t\t\tif (!cl->auth)\n\t\t\t{\n\t\t\t\tQueueMessage (cl, \"530 Not logged in.\\r\\n\");\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (!*cl->renamefrom)\n\n\n\t\t\tif (!FTP_AllowUpLoad(cl->renamefrom, cl) || !FTP_AllowUpLoad(resource, cl))\n\t\t\t{\n\t\t\t\tQueueMessage (cl, \"550 Access denied.\\r\\n\");\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tIWebPrintf(\"%s: Rename request from \\\"ftp://%s@/%s\\\" to \\\"/%s\\\"\\n\", cl->peername, cl->name, cl->renamefrom, resource);\n\n\t\t\tif (FS_Rename(cl->renamefrom, resource, FS_GAMEONLY))\n\t\t\t\tQueueMessage (cl, \"250 Success.\\r\\n\");\n\t\t\telse\n\t\t\t\tQueueMessage (cl, \"550 Requested action not taken.\\r\\n\");\n\n\t\t\tFS_FlushFSHashRemoved(cl->renamefrom);\n\t\t\tFS_FlushFSHashWritten(resource);\n\t\t\t*cl->renamefrom = 0;\n\t\t}\n\t\telse if (!stricmp(mode, \"DELE\") || !stricmp(mode, \"RMD\"))\n\t\t{\n\t\t\tFTP_ReadToAbsFilename(cl, msg, resource, sizeof(resource));\n\t\t\tif (!cl->auth)\n\t\t\t{\n\t\t\t\tQueueMessage (cl, \"530 Not logged in.\\r\\n\");\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (!*resource || !FTP_AllowUpLoad(resource, cl))\n\t\t\t{\n\t\t\t\tIWebPrintf(\"%s: Denied delete request for \\\"ftp://%s@/%s\\\"\\n\", cl->peername, cl->name, resource);\n\t\t\t\tQueueMessage (cl, \"550 Access denied.\\r\\n\");\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tIWebPrintf(\"%s: Delete request for \\\"ftp://%s@/%s\\\"\\n\", cl->peername, cl->name, resource);\n\t\t\tif (!stricmp(mode, \"RMD\"))\n\t\t\t{\n\t\t\t\tchar path[MAX_OSPATH];\n\t\t\t\tif (FS_SystemPath(resource, FS_GAMEONLY, path, sizeof(path)) && Sys_rmdir(path))\n\t\t\t\t\tQueueMessage (cl, \"250 Success.\\r\\n\");\n\t\t\t\telse\n\t\t\t\t\tQueueMessage (cl, \"550 Requested action not taken.\\r\\n\");\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (FS_Remove(resource, FS_GAMEONLY))\n\t\t\t\t\tQueueMessage (cl, \"250 Success.\\r\\n\");\n\t\t\t\telse\n\t\t\t\t\tQueueMessage (cl, \"550 Requested action not taken.\\r\\n\");\n\t\t\t}\n\n\t\t\tFS_FlushFSHashRemoved(resource);\n\t\t}\n\t\telse if (!stricmp(mode, \"STRU\"))\n\t\t{\n\t\t\tif (!cl->auth)\n\t\t\t{\n\t\t\t\tQueueMessage (cl, \"530 Not logged in.\\r\\n\");\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tmsg = COM_ParseOut(msg, resource, sizeof(resource));\n\t\t\tif (!strcmp(resource, \"F\"))\t//file\n\t\t\t{\n\t\t\t\tQueueMessage (cl, \"200 recordless structure selected.\\r\\n\");\n\t\t\t}\n//\t\t\telse if (!strcmp(resource, \"R\"))\t//record\n//\t\t\telse if (!strcmp(resource, \"P\"))\t//page\n\t\t\telse\n\t\t\t{\n\t\t\t\tQueueMessage (cl, \"504 not implemented (it's a simple server).\\r\\n\");\n\t\t\t}\n\t\t}\n\t\telse if (!stricmp(mode, \"NOOP\"))\n\t\t{\n\t\t\tQueueMessage (cl, \"200 Do something then!\\r\\n\");\n\t\t}\n\t\telse if (!stricmp(mode, \"QUIT\"))\n\t\t{\n\t\t\tQueueMessage (cl, \"200 About to quit.\\r\\n\");\n\t\t\treturn true;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tQueueMessage (cl, \"502 Command not implemented.\\r\\n\");\n\t\t}\n\t}\n\n#ifdef MULTITHREAD\n\tif (cl->datadir && !cl->transferthread)\n\t{\n\t\tcl->datadir|=64;\n\t\tcl->transferthread = Sys_CreateThread(\"FTP RECV\", FTP_TransferThread, cl, 0, 65536);\n\t}\n#endif\n\treturn false;\n}\n\n#if defined(WEBSVONLY) && defined(_WIN32)\nDWORD WINAPI BlockingClient(void *ctx)\n{\n\tFTPclient_t *cl = ctx;\n\tunsigned long _false = false;\n\tif (ioctlsocket (cl->controlsock, FIONBIO, &_false) == -1)\n\t{\n\t\tIWebPrintf (\"FTP_ServerRun: blocking error: %s\\n\", strerror(neterrno()));\n\t\treturn 0;\n\t}\n\n\tcl->blocking = true;\n\n\twhile (!FTP_ServerThinkForConnection(cl))\n\t{\n\t\tSleep(10);\n\t}\n\n\tif (cl->file)\n\t\tVFS_CLOSE(cl->file);\n\tclosesocket(cl->controlsock);\n\tif (cl->datasock)\n\t\tclosesocket(cl->datasock);\n\n\tIWebFree(cl);\n\treturn 0;\n}\n#endif\n\niwboolean FTP_ServerRun(iwboolean ftpserverwanted, int port)\n{\n\tFTPclient_t *cl, *prevcl;\n\tstruct sockaddr_qstorage\tfrom;\n\tint\t\tfromlen;\n\tSOCKET clientsock;\nunsigned long _true = true;\n\n\tif (!port)\n\t\tport = 21;\n\tif (ftpserverport != port)\n\t{\n\t\tftpserverport = port;\n\t\tftpserverwanted = false;\t//forces it to restart if the port is changed.\n\t}\n\n\tif (!ftpserverinitied)\n\t{\n\t\tif (ftpserverwanted)\n\t\t{\n\t\t\tftpserversocket = FTP_BeginListening(0, port);\n\t\t\tif (ftpserversocket == INVALID_SOCKET)\n\t\t\t{\n\t\t\t\tftpserverfailed = true;\n\t\t\t\tIWebPrintf(\"Unable to establish listening FTP socket\\n\");\n\t\t\t}\n\t\t\telse\n\t\t\t\tIWebPrintf(\"FTP server is running\\n\");\n\t\t\tftpserverinitied = true;\n\t\t}\n\t\treturn false;\n\t}\n\telse if (!ftpserverwanted)\n\t{\n\t\tFTP_ServerShutdown();\n\t\treturn false;\n\t}\n\n\tprevcl = NULL;\n\tfor (cl = FTPclient; cl; cl = cl->next)\n\t{\n\t\tif (FTP_ServerThinkForConnection(cl))\n\t\t{\n\t\t\tif (cl->file)\n\t\t\t\tVFS_CLOSE(cl->file);\n\t\t\tclosesocket(cl->controlsock);\n\t\t\tif (cl->datasock)\n\t\t\t\tclosesocket(cl->datasock);\n\n\t\t\tif (prevcl)\n\t\t\t{\n\t\t\t\tprevcl->next = cl->next;\n\t\t\t\tIWebFree(cl);\n\t\t\t\tcl = prevcl;\n\n\t\t\t\tif (!cl)\t//kills loop\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tFTPclient = cl->next;\n\t\t\t\tIWebFree(cl);\n\t\t\t\tcl = FTPclient;\n\n\t\t\t\tif (!cl)\t//kills loop\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tprevcl = cl;\n\t}\n\n\tfromlen = sizeof(from);\n\tif (ftpserversocket == INVALID_SOCKET)\n\t\tclientsock = INVALID_SOCKET;\n\telse\n\t\tclientsock = accept(ftpserversocket, (struct sockaddr *)&from, &fromlen);\n\n\tif (clientsock == INVALID_SOCKET)\n\t{\n\t\tint e = neterrno();\n\t\tif (e == NET_EWOULDBLOCK)\n\t\t\treturn false;\n\n\t\tif (e == NET_ECONNABORTED || e == NET_ECONNRESET)\n\t\t{\n\t\t\tCon_TPrintf (\"Connection lost or aborted\\n\");\n\t\t\treturn false;\n\t\t}\n\n\n\t\tIWebPrintf (\"NET_GetPacket: %s\\n\", strerror(e));\n\t\treturn false;\n\t}\n\n\tif (ioctlsocket (clientsock, FIONBIO, &_true) == -1)\n\t{\n\t\tIWebPrintf (\"FTP_ServerRun: blocking error: %s\\n\", strerror(neterrno()));\n\t\treturn false;\n\t}\n\tcl = IWebMalloc(sizeof(FTPclient_t));\n\tif (!cl)\t//iwebmalloc is allowed to fail.\n\t{\n\t\tchar *msg = \"421 Not enough memory is allocated.\\r\\n\";\t//don't be totally anti social\n\t\tsend(clientsock, msg, strlen(msg), 0);\n\t\tclosesocket(clientsock);\t//try to forget this ever happend\n\t\treturn true;\n\t}\n\tNET_SockadrToString(cl->peername, sizeof(cl->peername), &from, fromlen);\n\tIWebPrintf(\"%s: New FTP connection\\n\", cl->peername);\n\t//RFC1700\n\tif (((struct sockaddr *)&from)->sa_family == AF_INET)\n\t\tcl->controlaf = 1;\n\telse if (((struct sockaddr *)&from)->sa_family == AF_INET6)\n\t\tcl->controlaf = 2;\n#ifdef USEIPX\n\telse if (((struct sockaddr *)&from)->sa_family == AF_IPX)\n\t\tcl->controlaf = 11;\n#endif\n\telse\n\t\tcl->controlaf = 0;\n\n\tcl->controlsock = clientsock;\n\tcl->datasock = INVALID_SOCKET;\n\tcl->next = FTPclient;\n\tcl->blocking = false;\n\tstrcpy(cl->path, \"\");\n\n\tQueueMessage(cl, \"220-\" FULLENGINENAME \" FTP Server.\\r\\n220 Welcomes all new users.\\r\\n\");\n\n#if defined(WEBSVONLY) && defined(_WIN32)\n\tif (!CreateThread(NULL, 128, BlockingClient, cl, 0, NULL))\n#endif\n\t\tFTPclient = cl;\n\treturn true;\n}\n\n#endif\n"
  },
  {
    "path": "engine/http/httpclient.c",
    "content": "#include \"quakedef.h\"\n\n#include \"netinc.h\"\n#include \"fs.h\"\n\n#if defined(WEBCLIENT)\nstatic struct dl_download *activedownloads;\n\n#if defined(FTE_TARGET_WEB)\n\nvfsfile_t *FSWEB_OpenTempHandle(int f);\n\nstatic void DL_Cancel(struct dl_download *dl)\n{\n\t//FIXME: clear out the callbacks somehow\n\tdl->ctx = NULL;\n}\nstatic void DL_OnLoad(void *c, int buf)\n{\n\t//also fires from 404s.\n\tstruct dl_download *dl = c;\n\tvfsfile_t *tempfile = FSWEB_OpenTempHandle(buf);\n\t//make sure the file is 'open'.\n\tif (!dl->file)\n\t{\n\t\tif (*dl->localname)\n\t\t{\n\t\t\tFS_CreatePath(dl->localname, dl->fsroot);\n\t\t\tdl->file = FS_OpenVFS(dl->localname, \"w+b\", dl->fsroot);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tdl->file = tempfile;\n\t\t\ttempfile = NULL;\n\t\t}\n\t}\n\n\tif (dl->file)\n\t{\n\t\tdl->status = DL_FINISHED;\n\t\tif (tempfile)\n\t\t{\n\t\t\tqofs_t datasize = VFS_GETLEN(tempfile);\n\t\t\tchar *data = malloc(datasize);\t//grab a temp buffer so we can do the entire file at once...\n\t\t\tif (!data)\n\t\t\t\tdl->status = DL_FAILED;\n\t\t\telse\n\t\t\t{\n\t\t\t\tVFS_READ(tempfile, data, datasize);\n\n\t\t\t\tVFS_WRITE(dl->file, data, datasize);\n\t\t\t\tif (dl->file->seekstyle < SS_PIPE)\n\t\t\t\t\tVFS_SEEK(dl->file, 0);\n\n\t\t\t\tfree(data);\n\t\t\t}\n\t\t}\n\t}\n\telse\n\t\tdl->status = DL_FAILED;\n\n\tif (tempfile)\n\t\tVFS_CLOSE(tempfile);\n\n\tdl->replycode = 200;\n}\nstatic void DL_OnError(void *c, int ecode)\n{\n\tstruct dl_download *dl = c;\n\t//fires from cross-domain blocks, tls errors, etc.\n\t//anything which doesn't yield an http response (404 is NOT an error as far as js is aware).\n\n\tdl->replycode = ecode;\n\tCon_Printf(CON_WARNING\"dl error(%i): %s\\n\", ecode, dl->url);\n\tdl->status = DL_FAILED;\n}\nstatic void DL_OnProgress(void *c, int position, int totalsize)\n{\n\tstruct dl_download *dl = c;\n\n\tdl->completed = position;\n\tdl->totalsize = totalsize;\n}\n\n//this becomes a poll function. the main thread will call this once a frame or so.\nqboolean DL_Decide(struct dl_download *dl)\n{\n\tconst char *url = dl->redir;\n\tif (!*url)\n\t\turl = dl->url;\n\n\tif (dl->ctx)\n\t{\n\t\tif (dl->status == DL_FINISHED)\n\t\t\treturn false;\n\t\tif (dl->status == DL_FAILED)\n\t\t{\n\t\t\tDL_Cancel(dl);\n\t\t\treturn false;\n\t\t}\n\t}\n\telse if (dl->status == DL_PENDING)\n\t{\n\t\tdl->status = DL_ACTIVE;\n\t\tdl->abort = DL_Cancel;\n\t\tdl->ctx = dl;\n\n\t\temscriptenfte_async_wget_data2(url, dl->postdata, dl->postlen, dl->postmimetype, dl, DL_OnLoad, DL_OnError, DL_OnProgress);\n\t}\n\telse if (dl->status == DL_ACTIVE)\n\t{\t//canceled?\n\t\tdl->status = DL_FAILED;\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n#else\nqboolean DL_Decide(struct dl_download *dl);\n\n/*\nThis file does one thing. Connects to servers and grabs the specified file. It doesn't do any uploading whatsoever. Live with it.\nIt doesn't use persistant connections.\n\n*/\n\n#define COOKIECOOKIECOOKIE\n#ifdef COOKIECOOKIECOOKIE\ntypedef struct cookie_s\n{\n\tstruct cookie_s *next;\n\tchar *domain;\n\tint secure;\n\tchar *name;\n\tchar *value;\n} cookie_t;\nstatic cookie_t *cookies;\n\n\n//set a specific cookie.\nvoid Cookie_Feed(char *domain, int secure, char *name, char *value)\n{\n\tcookie_t **link, *c;\n\tSys_LockMutex(com_resourcemutex);\n\tfor(link = &cookies; (c=*link)!=NULL; link = &(*link)->next)\n\t{\n\t\tif (!strcmp(c->domain, domain) && c->secure == secure && !strcmp(c->name, name))\n\t\t\tbreak;\n\t}\n\t//delete it, if it exists, so we can create it anew.\n\tif (c)\n\t{\n\t\t*link = c->next;\n\t\tZ_Free(c);\n\t}\n\tif (value && *value)\n\t{\n//\t\tCon_Printf(\"Setting cookie http%s://%s/ %s=%s\\n\", secure?\"s\":\"\", domain, name, value);\n\t\tc = Z_Malloc(sizeof(*c) + strlen(domain) + strlen(name) + strlen(value) + 3);\n\t\tc->domain = (char*)(c+1);\n\t\tstrcpy(c->domain, domain);\n\t\tc->secure = secure;\n\t\tc->name = c->domain+strlen(c->domain)+1;\n\t\tstrcpy(c->name, name);\n\t\tc->value = c->name+strlen(c->name)+1;\n\t\tstrcpy(c->value, value);\n\t\tc->next = cookies;\n\t\tcookies = c;\n\t}\n\telse\n\t{\n//\t\tCon_Printf(\"Deleted cookie http%s://%s/ %s\\n\", secure?\"s\":\"\", domain, name);\n\t}\n\tSys_UnlockMutex(com_resourcemutex);\n}\n\n//just removes all the cookies it can.\nvoid Cookie_Monster(void)\n{\n\tcookie_t *c;\n\twhile (cookies)\n\t{\n\t\tc = cookies;\n\t\tcookies = c->next;\n\t\tZ_Free(c);\n\t}\n}\n\n//parses Set-Cookie: THISPARTONLY\\r\\n\n//we don't support:\n//domain) we don't have a list of public suffixes, like .co.uk, and thus cannot safely block dangerous super-cookies. thus we require the exact same host each time\n//path) I'm going to call this an optimisation feature and not bother with it... hopefully there won't be too many sites that have sub-paths or third-party stuff... gah.\n//httponly) irrelevant until we support javascript... which we don't.\n//secure) assumed to be true. https:// vs http:// are thus completely independant. sorry.\n//expires) gah, parsing time values sucks! plus we don't have persistent storage. All cookies are session cookies.\nvoid Cookie_Parse(char *domain, int secure, char *line, char *end)\n{\n\tchar *e;\n\twhile (*line == ' ' && line < end)\n\t\tline++;\n\tfor (e = line; e < end; e++)\n\t\tif (*e == ';')\n\t\t\tend = e;\n\n\tfor (e = line; e < end; e++)\n\t\tif (*e == '=')\n\t\t\tbreak;\n\n\t*e = 0;\n\t*end = 0;\n\tCookie_Feed(domain, secure, line, e+1);\n}\n//outputs a complete http line: Cookie: a=v1; b=v2\\r\\n\nvoid Cookie_Regurgitate(char *domain, int secure, char *buffer, size_t buffersize)\n{\n\tqboolean hascookies = false;\n\tcookie_t *c;\n//\tchar *l = buffer;\n\tbuffersize -= 3;\t//\\r\\n\\0\n\t*buffer = 0;\n\tSys_LockMutex(com_resourcemutex);\n\tfor (c = cookies; c; c = c->next)\n\t{\n\t\tif (!strcmp(c->domain, domain) && c->secure == secure)\n\t\t{\n\t\t\tint nlen,vlen;\n\t\t\tif (!hascookies)\n\t\t\t{\n\t\t\t\tif (buffersize < 8)\n\t\t\t\t\tbreak;\n\t\t\t\tstrcpy(buffer, \"Cookie: \");\n\t\t\t\tbuffersize -= 8;\n\t\t\t\thascookies=true;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (buffersize < 2)\n\t\t\t\t\tbreak;\n\t\t\t\tstrcpy(buffer, \"; \");\n\t\t\t\tbuffersize -= 2;\n\t\t\t}\n\t\t\tbuffer += strlen(buffer);\n\n\t\t\tnlen = strlen(c->name);\n\t\t\tvlen = strlen(c->value);\n\t\t\t\n\t\t\tif (buffersize < nlen+1+vlen)\n\t\t\t\tbreak;\n\t\t\tmemcpy(buffer, c->name, nlen);\n\t\t\tbuffer += nlen;\n\t\t\t*buffer++ = '=';\n\t\t\tmemcpy(buffer, c->value, vlen);\n\t\t\tbuffer += vlen;\n\t\t}\n\t}\n\tSys_UnlockMutex(com_resourcemutex);\n\n\tif (hascookies)\n\t\tstrcpy(buffer, \"\\r\\n\");\n\telse\n\t\t*buffer = 0;\n\n//\tif (*l)\n//\t\tCon_Printf(\"Sending cookie(s) to http%s://%s/ %s\\n\", secure?\"s\":\"\", domain, l);\n}\n#endif\n\nstruct http_dl_ctx_s {\n//\tstruct dl_download *dlctx;\n\n\tvfsfile_t *stream;\n\tSOCKET sock;\t//so we can wait on it when multithreaded.\n\n\tchar *buffer;\n\n\tchar server[128];\n\tqboolean secure;\n\n\tsize_t bufferused;\n\tsize_t bufferlen;\n\n\tsize_t totalreceived;\t//useful when we're just dumping to a file.\n\n\tstruct vfsfile_s *file;\t//if gzipping, this is a temporary file. we'll write to the real file from this after the transfer is complete.\n\tqboolean gzip;\n\tqboolean chunking;\n\tsize_t chunksize;\n\tsize_t chunked;\n\n\tenum {HC_REQUESTING, HC_GETTINGHEADER, HC_GETTING} state;\n\n\tsize_t contentlength;\n};\n\nvoid HTTP_Cleanup(struct dl_download *dl)\n{\n\tstruct http_dl_ctx_s *con = dl->ctx;\n\tdl->ctx = NULL;\n\n\tif (con->stream)\n\t\tVFS_CLOSE(con->stream);\n\tcon->stream = NULL;\n\tfree(con->buffer);\n\tfree(con);\n\n\tdl->abort = NULL;\n\tdl->status = DL_PENDING;\n\tdl->completed = 0;\n\tdl->totalsize = 0;\n}\n\nstatic void ExpandBuffer(struct http_dl_ctx_s *con, int quant)\n{\n\tint newlen;\n\tnewlen = con->bufferlen + quant;\n\tcon->buffer = realloc(con->buffer, newlen);\n\tcon->bufferlen = newlen;\n}\n\nstatic int VFSError_To_HTTP(int vfserr)\n{\n\tswitch(vfserr)\n\t{\n\tcase VFS_ERROR_TRYLATER:\n\t\treturn 0;\n\tdefault:\n\tcase VFS_ERROR_UNSPECIFIED:\n\t\treturn 0;\t//don't know, no reason given.\n\tcase VFS_ERROR_REFUSED:\n\t\treturn HTTP_REFUSED;\n\tcase VFS_ERROR_EOF:\n\t\treturn HTTP_EOF;\n\tcase VFS_ERROR_DNSFAILURE:\n\t\treturn HTTP_DNSFAILURE;\n\tcase VFS_ERROR_WRONGCERT:\n\t\treturn HTTP_MITM;\n\tcase VFS_ERROR_UNTRUSTED:\n\t\treturn HTTP_UNTRUSTED;\n\t}\n}\n\n#ifdef HAVE_EPOLL\n#include <poll.h>\n#endif\nstatic qboolean HTTP_DL_Work(struct dl_download *dl)\n{\n\tstruct http_dl_ctx_s *con = dl->ctx;\n\tchar buffer[256];\n\tchar Location[4096];\n\tchar mimetype[256];\n\tchar *nl;\n\tchar *msg;\n\tint ammount;\n\tqboolean transfercomplete = false;\n\n#ifdef MULTITHREAD\n\t//if we're running in a thread, wait for some actual activity instead of busylooping like an moron.\n\tif (dl->threadctx)\n\t{\n#ifdef HAVE_EPOLL\n\t\tstruct pollfd fd;\n\t\tfd.fd = con->sock;\n\t\tfd.events = POLLIN;\n\t\tfd.revents = 0;\n\t\tif (con->state == HC_REQUESTING)\n\t\t\tfd.events |= POLLOUT;\n\t\tpoll(&fd, 1, 0.1*1000);\t//wake up when we can read OR write\n\t\t//note that https should wake up more often, but we don't want to wake up because we *can* write when we're reading without any need to write.\n#else\n\t\tstruct timeval timeout;\n\t\tfd_set\trdset, wrset;\n\t\tFD_ZERO(&wrset);\n\t\tFD_ZERO(&rdset);\n\t\tFD_SET(con->sock, &wrset); // network socket\n\t\tFD_SET(con->sock, &rdset); // network socket\n\t\ttimeout.tv_sec = 0;\n\t\ttimeout.tv_usec = 0.1*1000*1000;\n\t\tif (con->state == HC_REQUESTING)\n\t\t\tselect(con->sock+1, &rdset, &wrset, NULL, &timeout);\t//wake up when we can read OR write\n\t\telse\n\t\t\tselect(con->sock+1, &rdset, NULL, NULL, &timeout);\t//wake when we can read.\n\t\t//note that https should wake up more often, but we don't want to wake up because we *can* write when we're reading without any need to write.\n#endif\n\t}\n#endif\n\n\tswitch(con->state)\n\t{\n\tcase HC_REQUESTING:\n\n\t\tammount = VFS_WRITE(con->stream, con->buffer, con->bufferused);\n\t\tif (!ammount)\n\t\t\treturn true;\n\t\tif (ammount < 0)\n\t\t{\n\t\t\tdl->status = DL_FAILED;\n\t\t\tdl->replycode = VFSError_To_HTTP(ammount);\n\t\t\treturn false;\n\t\t}\n\n\t\tcon->bufferused -= ammount;\n\t\tmemmove(con->buffer, con->buffer+ammount, con->bufferused);\n\t\tif (!con->bufferused)\t//that's it, all sent.\n\t\t\tcon->state = HC_GETTINGHEADER;\n\t\tbreak;\n\n\tcase HC_GETTINGHEADER:\n\t\tif (con->bufferlen - con->bufferused < 1530)\n\t\t\tExpandBuffer(con, 1530);\n\n\t\tammount = VFS_READ(con->stream, con->buffer+con->bufferused, con->bufferlen-con->bufferused-15);\n\t\tif (!ammount)\n\t\t\treturn true;\n\t\tif (ammount < 0)\n\t\t{\n\t\t\tdl->status = DL_FAILED;\n\t\t\tdl->replycode = VFSError_To_HTTP(ammount);\n\t\t\treturn false;\n\t\t}\n\n\t\tcon->bufferused+=ammount;\n\t\tcon->buffer[con->bufferused] = '\\0';\n\t\t//have we got the entire thing yet?\n\n\t\tmsg = con->buffer;\n\t\tcon->chunking = false;\n\t\tcon->contentlength = -1;\t//means unspecified\n\t\tcon->gzip = false;\n\t\t*mimetype = 0;\n\t\t*Location = 0;\n\t\tif (strnicmp(msg, \"HTTP/\", 5))\n\t\t{\t//pre version 1 (lame servers). no response headers at all.\n\t\t\tcon->state = HC_GETTING;\n\t\t\tdl->status = DL_ACTIVE;\n\t\t\tdl->replycode = 200;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t//check if the headers are complete or not\n\t\t\tqboolean hcomplete = false;\n\t\t\twhile(*msg)\n\t\t\t{\n\t\t\t\tif (*msg == '\\n')\n\t\t\t\t{\n\t\t\t\t\tif (msg[1] == '\\n')\n\t\t\t\t\t{\t//tut tut, not '\\r'? that's not really allowed...\n\t\t\t\t\t\tmsg+=2;\n\t\t\t\t\t\thcomplete = true;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tif (msg[1] == '\\r' && msg[2] == '\\n')\n\t\t\t\t\t{\n\t\t\t\t\t\tmsg+=3;\n\t\t\t\t\t\thcomplete = true;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tmsg++;\n\t\t\t\t}\n\t\t\t\twhile (*msg == ' ' || *msg == '\\t')\n\t\t\t\t\tmsg++;\n\n\t\t\t\tnl = strchr(msg, '\\n');\n\t\t\t\tif (!nl)\n\t\t\t\t\tbreak;//not complete, don't bother trying to parse it.\n\t\t\t\tmsg = nl;\n\t\t\t}\n\n\t\t\tif (!hcomplete)\n\t\t\t\tbreak;//headers not complete. break out of switch\n\n\t\t\t//okay, they're complete, woot. we need to actually go through the headers now\n\t\t\tmsg = con->buffer;\n\t\t\twhile(*msg)\n\t\t\t{\n\t\t\t\tif (*msg == '\\n')\n\t\t\t\t{\n\t\t\t\t\tif (msg[1] == '\\n')\n\t\t\t\t\t{\t//tut tut, not '\\r'? that's not really allowed...\n\t\t\t\t\t\tmsg+=2;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tif (msg[1] == '\\r' && msg[2] == '\\n')\n\t\t\t\t\t{\n\t\t\t\t\t\tmsg+=3;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tmsg++;\n\t\t\t\t}\n\t\t\t\twhile (*msg == ' ' || *msg == '\\t')\n\t\t\t\t\tmsg++;\n\n\t\t\t\tnl = strchr(msg, '\\n');\n\t\t\t\tif (!nl)\n\t\t\t\t\tbreak;//not complete, don't bother trying to parse it.\n\n\t\t\t\tif (!strnicmp(msg, \"Content-Length: \", 16))\n\t\t\t\t\tcon->contentlength = atoi(msg+16);\n\t\t\t\telse if (!strnicmp(msg, \"Content-Type:\", 13))\n\t\t\t\t{\n\t\t\t\t\t*nl = '\\0';\n\t\t\t\t\t//don't worry too much about truncation. its not like we can really do much with fancy mime types anyway.\n\t\t\t\t\tCOM_TrimString(msg+13, mimetype, sizeof(mimetype));\n\t\t\t\t\t*nl = '\\n';\n\t\t\t\t}\n\t\t\t\telse if (!strnicmp(msg, \"Location: \", 10))\n\t\t\t\t{\n\t\t\t\t\t*nl = '\\0';\n\t\t\t\t\tif (!COM_TrimString(msg+10, Location, sizeof(Location)))\n\t\t\t\t\t{\n\t\t\t\t\t\tCon_Printf(\"HTTP Redirect: location too long\\n\");\n\t\t\t\t\t\tdl->status = DL_FAILED;\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t\t*nl = '\\n';\n\t\t\t\t}\n\t\t\t\telse if (!strnicmp(msg, \"Content-Encoding: \", 18))\n\t\t\t\t{\n\t\t\t\t\tchar *chunk = strstr(msg, \"gzip\");\n\t\t\t\t\tif (chunk < nl)\n\t\t\t\t\t\tcon->gzip = true;\n\t\t\t\t}\n\t\t\t\telse if (!strnicmp(msg, \"Transfer-Encoding: \", 19))\n\t\t\t\t{\n\t\t\t\t\tchar *chunk = strstr(msg, \"chunked\");\n\t\t\t\t\tif (chunk < nl)\n\t\t\t\t\t\tcon->chunking = true;\n\t\t\t\t}\n#ifdef COOKIECOOKIECOOKIE\n\t\t\t\telse if (!strnicmp(msg, \"Set-Cookie: \", 12))\n\t\t\t\t{\n\t\t\t\t\tCookie_Parse(con->server, con->secure, msg+12, nl);\n\t\t\t\t}\n#endif\n\t\t\t\tmsg = nl;\n\t\t\t}\n\t\t\tif (!hcomplete)\n\t\t\t\tbreak;//headers not complete. break out of switch\n\n\t\t\tammount = msg - con->buffer;\n\n\t\t\tmsg = COM_ParseOut(con->buffer, buffer, sizeof(buffer));\n\t\t\tmsg = COM_ParseOut(msg, buffer, sizeof(buffer));\n\n\t\t\tdl->replycode = atoi(buffer);\n\n\t\t\tif (!stricmp(buffer, \"100\"))\n\t\t\t{\t//http/1.1 servers can give this. We ignore it.\n\t\t\t\tcon->bufferused -= ammount;\n\t\t\t\tmemmove(con->buffer, con->buffer+ammount, con->bufferused);\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\tif (!stricmp(buffer, \"301\") || !stricmp(buffer, \"302\") || !stricmp(buffer, \"303\"))\n\t\t\t{\n\t\t\t\tchar trimmed[256];\n\t\t\t\tnl = strchr(msg, '\\n');\n\t\t\t\tif (nl)\n\t\t\t\t\t*nl = '\\0';\n\t\t\t\tCOM_TrimString(msg, trimmed, sizeof(trimmed));\n\t\t\t\tCon_Printf(S_COLOR_GRAY \"%s: %s %s (%s)\\n\", dl->url, buffer, trimmed, Location);\n\t\t\t\tif (!*Location)\n\t\t\t\t{\n\t\t\t\t\tCon_Printf(\"Server redirected to null location\\n\");\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tif (dl->redircount++ > 10)\n\t\t\t\t\t{\n\t\t\t\t\t\tCon_Printf(\"HTTP: Recursive redirects\\n\");\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t\tHTTP_Cleanup(dl);\n\t\t\t\t\tif (*Location == '/')\n\t\t\t\t\t{\n\t\t\t\t\t\tchar *cur = *dl->redir?dl->redir:dl->url;\n\t\t\t\t\t\tchar *curserver = cur;\n\t\t\t\t\t\tchar *curpath;\n\t\t\t\t\t\t/*same server+protocol*/\n\t\t\t\t\t\tif (!strncmp(curserver, \"http://\", 7))\n\t\t\t\t\t\t\tcurserver += 7;\n\t\t\t\t\t\tcurpath = strchr(curserver, '/');\n\t\t\t\t\t\tif (!curpath)\n\t\t\t\t\t\t\tcurpath = curserver + strlen(curserver);\n\t\t\t\t\t\tif (cur == dl->redir)\n\t\t\t\t\t\t\t*curpath = 0;\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tQ_strncpyz(dl->redir, cur, (curpath-cur) + 1);\n\t\t\t\t\t\tQ_strncatz(dl->redir, Location, sizeof(dl->redir));\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\tQ_strncpyz(dl->redir, Location, sizeof(dl->redir));\n\t\t\t\t\tdl->poll = DL_Decide;\n\t\t\t\t\tdl->status = DL_PENDING;\n\t\t\t\t}\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\tif (stricmp(buffer, \"200\") && stricmp(buffer, \"201\") && stricmp(buffer, \"202\"))\n\t\t\t{\n\t\t\t\tnl = strchr(msg, '\\n');\n\t\t\t\tif (!nl)\n\t\t\t\t\treturn false;\t//eh?\n\t\t\t\tif (nl>msg&&nl[-1] == '\\r')\n\t\t\t\t\tnl--;\n\t\t\t\t*nl = '\\0';\n\t\t\t\tCon_Printf(\"%s: %s%s\\n\", dl->url, buffer, msg);\n\t\t\t\treturn false;\t//something went wrong.\n\t\t\t}\n\n\t\t\tif (con->contentlength != -1 && con->contentlength > dl->sizelimit)\n\t\t\t{\n\t\t\t\tchar s1[32],s2[32];\n\t\t\t\tdl->replycode = 413;\t//413 Payload Too Large \n\t\t\t\tCon_Printf(CON_WARNING\"Request exceeds size limit - %s (%s vs %s)\\n\", dl->url, FS_AbbreviateSize(s1,sizeof(s1),con->contentlength), FS_AbbreviateSize(s2,sizeof(s2),dl->sizelimit));\n\t\t\t\treturn false;\t//something went wrong.\n\t\t\t}\n\n\t\t\tcon->bufferused -= ammount;\n\n\t\t\tdl->totalsize = con->contentlength;\n\n\t\t\tmemmove(con->buffer, con->buffer+ammount, con->bufferused);\n\t\t}\n\n\t\tif (dl->notifystarted)\n\t\t{\n\t\t\tif (!dl->notifystarted(dl, *mimetype?mimetype:NULL))\n\t\t\t{\n\t\t\t\tdl->notifycomplete = NULL;\n\t\t\t\tdl->status = DL_FAILED;\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\n\t\tif (!dl->file)\n\t\t{\n\t\t\tif (*dl->localname)\n\t\t\t{\n\t\t\t\tFS_CreatePath(dl->localname, dl->fsroot);\n\t\t\t\tdl->file = FS_OpenVFS(dl->localname, \"w+b\", dl->fsroot);\n\t\t\t}\n\t\t\telse\n\t\t\t\tdl->file = FS_OpenTemp();\n\n\t\t\tif (!dl->file)\n\t\t\t{\n\t\t\t\tif (*dl->localname)\n\t\t\t\t\tCon_Printf(\"HTTP: Couldn't open file \\\"%s\\\"\\n\", dl->localname);\n\t\t\t\telse\n\t\t\t\t\tCon_Printf(\"HTTP: Couldn't open temporary file\\n\");\n\t\t\t\tdl->status = DL_FAILED;\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\tif (con->gzip)\n\t\t{\n#ifdef AVAIL_GZDEC\n\t\t\tcon->file = FS_GZ_WriteFilter(dl->file, false, false);\n#else\n\t\t\tCon_Printf(\"HTTP: no support for gzipped files \\\"%s\\\"\\n\", dl->localname);\n\t\t\tdl->status = DL_FAILED;\n\t\t\treturn false;\n#endif\n\t\t}\n\t\telse\n\t\t\tcon->file = dl->file;\n\t\tcon->state = HC_GETTING;\n\t\tdl->status = DL_ACTIVE;\n\t\tgoto firstread;\t//oh noes! an evil goto! this is the easiest way to avoid the 'return' here when no data follows a 0-byte download\n\tcase HC_GETTING:\n\t\tif (con->bufferlen - con->bufferused < 1530)\n\t\t\tExpandBuffer(con, 1530);\n\n\t\tammount = VFS_READ(con->stream, con->buffer+con->bufferused, con->bufferlen-con->bufferused-1);\n\t\tif (ammount == 0)\n\t\t\treturn true;\t//no data yet\n\t\telse if (ammount < 0)\n\t\t\tammount = 0;\t//error (EOF?)\n\n\t\tcon->bufferused+=ammount;\n\nfirstread:\n\t\tif (con->chunking)\n\t\t{\n\t\t\t//9\\r\\n\n\t\t\t//chunkdata\\r\\n\n\t\t\t//(etc)\n\t\t\tint trim;\n\t\t\tchar *nl;\n\t\t\tcon->buffer[con->bufferused] = '\\0';\n\t\t\tfor(;;)\n\t\t\t{\t//work out as we go.\n\t\t\t\tif (con->chunksize)//we are trying to parse a chunk.\n\t\t\t\t{\n\t\t\t\t\ttrim = con->bufferused - con->chunked;\n\t\t\t\t\tif (trim > con->chunksize)\n\t\t\t\t\t\ttrim = con->chunksize;\t//don't go into the next size field.\n\n\t\t\t\t\tif (con->chunksize == trim)\n\t\t\t\t\t{\t//we need to find the next \\n and trim it.\n\t\t\t\t\t\tnl = strchr(con->buffer+con->chunked+trim, '\\n');\n\t\t\t\t\t\tif (!nl)\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tnl++;\n\t\t\t\t\t\tcon->chunksize = 0;\n\t\t\t\t\t\tcon->chunked += trim;\n\n\t\t\t\t\t\t//chop out the \\r\\n from the stream\n\t\t\t\t\t\ttrim = nl - (con->buffer+con->chunked);\n\t\t\t\t\t\tmemmove(con->buffer + con->chunked, nl, con->buffer+con->bufferused-nl+1);\n\t\t\t\t\t\tcon->bufferused -= trim;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tcon->chunksize -= trim;\n\t\t\t\t\t\tcon->chunked += trim;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (!(con->bufferused - con->chunked))\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tsize_t nextsize;\n\t\t\t\t\tnl = strchr(con->buffer+con->chunked, '\\n');\n\t\t\t\t\tif (!nl)\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tnextsize = strtoul(con->buffer+con->chunked, NULL, 16);\t//it's hex.\n\t\t\t\t\tnl++;\n\t\t\t\t\tif (!nextsize) //eof. make sure we skip its \\n too\n\t\t\t\t\t{\n\t\t\t\t\t\tnl = strchr(nl, '\\n');\n\t\t\t\t\t\tif (!nl)\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\ttransfercomplete = true;\n\t\t\t\t\t}\n\t\t\t\t\tcon->chunksize = nextsize;\n\t\t\t\t\ttrim = nl - (con->buffer+con->chunked);\n\t\t\t\t\tmemmove(con->buffer + con->chunked, nl, con->buffer+con->bufferused-nl+1);\n\t\t\t\t\tcon->bufferused -= trim;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tcon->totalreceived+=con->chunked;\n\t\t\tif (con->totalreceived > dl->sizelimit)\n\t\t\t{\n\t\t\t\tchar s1[32],s2[32];\n\t\t\t\tdl->replycode = 413;\t//413 Payload Too Large \n\t\t\t\tCon_Printf(CON_WARNING\"Request exceeds size limit - %s (%s vs %s)\\n\", dl->url, FS_AbbreviateSize(s1,sizeof(s1),con->totalreceived), FS_AbbreviateSize(s2,sizeof(s2),dl->sizelimit));\n\t\t\t\treturn false;\t//something went wrong.\n\t\t\t}\n\t\t\tif (con->file && con->chunked)\t//we've got a chunk in the buffer\n\t\t\t{\t//write it\n\t\t\t\tif (VFS_WRITE(con->file, con->buffer, con->chunked) != con->chunked)\n\t\t\t\t{\n\t\t\t\t\tCon_Printf(\"Write error whilst downloading %s\\nDisk full?\\n\", dl->localname);\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\n\t\t\t\t//and move the unparsed chunk to the front.\n\t\t\t\tcon->bufferused -= con->chunked;\n\t\t\t\tmemmove(con->buffer, con->buffer+con->chunked, con->bufferused);\n\t\t\t\tcon->chunked = 0;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tint chunk = con->bufferused;\n\t\t\tif (con->contentlength != -1 && chunk > con->contentlength-con->totalreceived)\n\t\t\t\tchunk = con->contentlength-con->totalreceived;\n\n\t\t\tcon->totalreceived+=chunk;\n\t\t\tif (con->totalreceived > dl->sizelimit)\n\t\t\t{\n\t\t\t\tchar s1[32], s2[32];\n\t\t\t\tdl->replycode = 413;\t//413 Payload Too Large \n\t\t\t\tCon_Printf(CON_WARNING\"Request exceeds size limit - %s (%s vs %s)\\n\", dl->url, FS_AbbreviateSize(s1,sizeof(s1),con->totalreceived), FS_AbbreviateSize(s2,sizeof(s2),dl->sizelimit));\n\t\t\t\treturn false;\t//something went wrong.\n\t\t\t}\n\t\t\tif (con->file)\t//we've got a chunk in the buffer\n\t\t\t{\t//write it\n\t\t\t\tif (VFS_WRITE(con->file, con->buffer, chunk) != chunk)\n\t\t\t\t{\n\t\t\t\t\tCon_Printf(\"Write error whilst downloading %s\\nDisk full?\\n\", dl->localname);\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\n\t\t\t\t//and move the unparsed chunk to the front.\n\t\t\t\tmemmove(con->buffer, con->buffer+con->bufferused, con->bufferused-chunk);\n\t\t\t\tcon->bufferused -= chunk;\n\t\t\t}\n\t\t\tif (con->totalreceived == con->contentlength)\n\t\t\t\ttransfercomplete = true;\n\t\t}\n\n\t\tif (!ammount || transfercomplete)\n\t\t{\t//server closed off the connection (or signalled eof/sent enough data).\n\t\t\t//if (ammount) then we can save off the connection for reuse.\n\t\t\tif (con->chunksize)\n\t\t\t\tdl->status = DL_FAILED;\n\t\t\telse\n\t\t\t{\n#ifdef AVAIL_GZDEC\n\t\t\t\tif (con->gzip && con->file)\n\t\t\t\t{\n\t\t\t\t\tVFS_CLOSE(con->file);\n\t\t\t\t\tcon->file = NULL;\n\t\t\t\t}\n#endif\n\t\t\t\tif (con->contentlength != -1 && con->totalreceived != con->contentlength)\n\t\t\t\t\tdl->status = DL_FAILED;\t//file was truncated\n\t\t\t\telse\n\t\t\t\t\tdl->status = (dl->replycode == 200)?DL_FINISHED:DL_FAILED; \n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\t\tdl->completed = con->totalreceived;\n\n\t\tbreak;\n\t}\n\n\treturn true;\n}\n\nvoid HTTPDL_Establish(struct dl_download *dl)\n{\n\tstruct http_dl_ctx_s *con;\n\tqboolean https = false;\n\n#ifdef COOKIECOOKIECOOKIE\n\tchar cookies[8192];\n#else\n\tchar *cookies = \"\";\n#endif\n\tchar uri[MAX_OSPATH];\n\tchar *slash;\n\tconst char *url = dl->redir;\n\tif (!*url)\n\t\turl = dl->url;\n\n\tif (!strnicmp(url, \"https://\", 8))\n\t{\n\t\turl+=8;\n\t\thttps = true;\n\t}\n\telse if (!strnicmp(url, \"http://\", 7))\n\t\turl+=7;\n\n\tcon = malloc(sizeof(*con));\n\tmemset(con, 0, sizeof(*con));\n\n\tslash = strchr(url, '/');\n\tif (!slash)\n\t{\n\t\tQ_strncpyz(con->server, url, sizeof(con->server));\n\t\tQ_strncpyz(uri, \"/\", sizeof(uri));\n\t}\n\telse\n\t{\n\t\tQ_strncpyz(uri, slash, sizeof(uri));\n\t\tQ_strncpyz(con->server, url, sizeof(con->server));\n\t\tcon->server[slash-url] = '\\0';\n\t}\n\n\tdl->ctx = con;\n\tdl->abort = HTTP_Cleanup;\n\n\tdl->status = DL_RESOLVING;\n\n\tcon->sock = INVALID_SOCKET;\n\tcon->stream = NULL;\n\tcon->secure = false;\n#ifndef HAVE_SSL\n\tif (!https)\n#endif\n\t{\n\t\tnetadr_t adr = {0};\n\t\t//fixme: support more than one address possibility?\n\t\t//https uses a different default port\n\t\tif (NET_StringToAdr2(con->server, https?443:80, &adr, 1, NULL))\n\t\t\tcon->sock = TCP_OpenStream(&adr, *dl->redir?dl->redir:dl->url);\n\t\tcon->stream = FS_WrapTCPSocket(con->sock, true, con->server);\n\t\tif (!con->stream)\n\t\t{\n\t\t\tdl->status = DL_FAILED;\n\t\t\treturn;\n\t\t}\n\t}\n#ifdef HAVE_SSL\n\tif (https)\n\t{\n\t\t//https has an extra ssl/tls layer between tcp and http.\n\t\tcon->stream = FS_OpenSSL(con->server, con->stream, false);\n\t\tcon->secure = true;\n\t}\n#endif\n\tif (!con->stream)\n\t{\n\t\tdl->status = DL_FAILED;\n\t\treturn;\n\t}\n#ifdef COOKIECOOKIECOOKIE\n\tCookie_Regurgitate(con->server, con->secure, cookies, sizeof(cookies));\n#endif\n\tif (dl->postdata)\n\t{\n\t\tExpandBuffer(con, 1024 + strlen(uri) + strlen(con->server) + strlen(cookies) + strlen(dl->postmimetype) + dl->postlen);\n\t\tQ_snprintfz(con->buffer, con->bufferlen,\n\t\t\t\"POST %s HTTP/1.1\\r\\n\"\n\t\t\t\"Host: %s\\r\\n\"\n\t\t\t/*Cookie:*/ \"%s\"\n\t\t\t\"Content-Length: %u\\r\\n\"\n\t\t\t\"Content-Type: %s\\r\\n\"\n\t\t\t\"Connection: close\\r\\n\"\n#ifdef AVAIL_GZDEC\n\t\t\t\"Accept-Encoding: gzip\\r\\n\"\n#endif\n\t\t\t\"User-Agent: \"FULLENGINENAME\"\\r\\n\"\n\t\t\t\"\\r\\n\", uri, con->server, cookies, (unsigned int)dl->postlen, dl->postmimetype);\n\t\tcon->bufferused = strlen(con->buffer);\n\t\tmemcpy(con->buffer + con->bufferused, dl->postdata, dl->postlen);\n\t\tcon->bufferused += dl->postlen;\n\t}\n\telse\n\t{\n\t\tExpandBuffer(con, 512*1024);\n\t\tQ_snprintfz(con->buffer, con->bufferlen,\n\t\t\t\"GET %s HTTP/1.1\\r\\n\"\n\t\t\t\"Host: %s\\r\\n\"\n\t\t\t/*Cookie:*/ \"%s\"\n\t\t\t\"Connection: close\\r\\n\"\t\t\t//theoretically, this is not needed. but as our code will basically do it anyway, it might as well be here FIXME: implement connection reuse.\n#ifdef AVAIL_GZDEC\n\t\t\t\"Accept-Encoding: gzip\\r\\n\"\n#endif\n\t\t\t\"User-Agent: \"FULLENGINENAME\"\\r\\n\"\n\t\t\t\"\\r\\n\", uri, con->server, cookies);\n\t\tcon->bufferused = strlen(con->buffer);\n\t}\n\tcon->contentlength = -1;\n}\n\nqboolean HTTPDL_Poll(struct dl_download *dl)\n{\n\t/*failed previously*/\n\tif (dl->status == DL_FAILED)\n\t\treturn false;\n\n\tif (!dl->ctx)\n\t{\n\t\tHTTPDL_Establish(dl);\n\t\tif (dl->status == DL_FAILED)\n\t\t{\n\t\t\tHTTP_Cleanup(dl);\n\t\t\tdl->status = DL_FAILED;\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tif (dl->ctx)\n\t{\n\t\tif (!HTTP_DL_Work(dl))\n\t\t\tif (dl->status != DL_FINISHED)\n\t\t\t\tdl->status = DL_FAILED;\n\t\tif (dl->status == DL_FAILED)\n\t\t{\n\t\t\tHTTP_Cleanup(dl);\n\t\t\tdl->status = DL_FAILED;\n\t\t\treturn false;\n\t\t}\n\t\tif (dl->status == DL_FINISHED)\n\t\t\treturn false;\n\t}\n\n\treturn true;\n}\n\n/*\n//decode a base64 byte to a 0-63 value. Cannot cope with =.\nstatic unsigned int Base64_DecodeByte(char byt)\n{\n    if (byt >= 'A' && byt <= 'Z')\n        return (byt-'A') + 0;\n    if (byt >= 'a' && byt <= 'z')\n        return (byt-'a') + 26;\n    if (byt >= '0' && byt <= '9')\n        return (byt-'0') + 52;\n    if (byt == '+')\n        return 62;\n    if (byt == '/')\n        return 63;\n    return -1;\n}\n//FIXME: we should be able to skip whitespace.\nstatic int Base64_Decode(char *out, int outlen, char **srcout, int *srclenout)\n{\n\tint len = 0;\n\tunsigned int result;\n\n\tchar *src = *srcout;\n\tint srclen = *srclenout;\n\n\t//4 input chars give 3 output chars\n\twhile(srclen >= 4)\n\t{\n\t\tif (len+3 > outlen)\n\t\t{\n\t\t\t//ran out of space in the output buffer\n\t\t\t*srcout = src;\n\t\t\t*srclenout = srclen;\n\t\t\treturn len;\n\t\t}\n\t\tresult = Base64_DecodeByte(src[0])<<18;\n\t\tresult |= Base64_DecodeByte(src[1])<<12;\n\t\tout[len++] = (result>>16)&0xff;\n\t\tif (src[2] != '=')\n\t\t{\n\t\t\tresult |= Base64_DecodeByte(src[2])<<6;\n\t\t\tout[len++] = (result>>8)&0xff;\n\t\t\tif (src[3] != '=')\n\t\t\t{\n\t\t\t\tresult |= Base64_DecodeByte(src[3])<<0;\n\t\t\t\tout[len++] = (result>>0)&0xff;\n\t\t\t}\n\t\t}\n\t\tif (result & 0xff000000)\n\t\t\treturn 0;\t//some kind of invalid char\n\n\t\tsrc += 4;\n\t\tsrclen -= 4;\n\t}\n\n\t//end of string\n\t*srcout = src;\n\t*srclenout = srclen;\n\n\t//some kind of error\n\tif (srclen)\n\t{\n\t\tif (srclen != 1 || *src)\n\t\t\treturn 0;\n\t}\n\t\n\treturn len;\n}\n\nqboolean DataScheme_Decode(struct dl_download *dl)\n{\n\tchar block[8192];\n\tint remaining, blocksize;\n\tchar mimetype[256];\n\tchar baseval[256];\n\tchar charsetval[256];\n\tchar *url;\n\tchar *data;\n\tchar *charset;\n\tchar *base;\n\t//failed previously\n\tif (dl->status == DL_FAILED)\n\t\treturn false;\n\n\t//data:[<MIME-type>][;charset=<encoding>][;base64],<data>\n\n\t*mimetype = 0;\n\t*baseval = 0;\n\t*charsetval = 0;\n\n\turl = dl->url;\n\tif (!strncmp(url, \"data:\", 5))\n\t\turl+=5;\t//should always match\n\tdata = strchr(url, ',');\n\tif (!data)\n\t\treturn false;\n\tcharset = memchr(url, ';', data-url);\n\tif (charset)\n\t{\n\t\tbase = memchr(charset+1, ';', data-charset);\n\t\tif (base)\n\t\t{\n\t\t\tif (data-(base+1) >= sizeof(baseval))\n\t\t\t\treturn false;\n\t\t\tmemcpy(baseval, base+1, data-(base+1));\n\t\t\tbaseval[data-(base+1)] = 0;\n\t\t}\n\t\telse\n\t\t\tbase = data;\n\t\tif (base-(charset+1) >= sizeof(charsetval))\n\t\t\treturn false;\n\t\tmemcpy(charsetval, charset+1, base-(charset+1));\n\t\tcharsetval[base-(charset+1)] = 0;\n\n\t\tif (!strchr(charsetval, '='))\n\t\t{\n\t\t\tstrcpy(baseval, charsetval);\n\t\t\t*charsetval = 0;\n\t\t}\n\t}\n\telse\n\t\tcharset = data;\n\tif (charset-(url) >= sizeof(charsetval))\n\t\treturn false;\n\tmemcpy(mimetype, url, charset-(url));\n\tmimetype[charset-(url)] = 0;\n\n\tif (!*mimetype)\n\t\tQ_strncpyz(mimetype, \"text/plain\", sizeof(mimetype));\n\tif (!*charsetval)\n\t\tQ_strncpyz(charsetval, \"charset=US-ASCII\", sizeof(charsetval));\n\n\tif (dl->notifystarted)\n\t\tdl->notifystarted(dl, *mimetype?mimetype:NULL);\n\n\tif (!dl->file)\n\t{\n\t\tif (*dl->localname)\n\t\t{\n\t\t\tFS_CreatePath(dl->localname, dl->fsroot);\n\t\t\tdl->file = FS_OpenVFS(dl->localname, \"w+b\", dl->fsroot);\n\t\t}\n\t\telse\n\t\t\tdl->file = FS_OpenTemp();\n\t\tif (!dl->file)\n\t\t{\n\t\t\tif (*dl->localname)\n\t\t\t\tCon_Printf(\"HTTP: Couldn't open file \\\"%s\\\"\\n\", dl->localname);\n\t\t\telse\n\t\t\t\tCon_Printf(\"HTTP: Couldn't open temporary file\\n\");\n\t\t\tdl->status = DL_FAILED;\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tdata++;\n\tremaining = strlen(data);\n\twhile(remaining > 0)\n\t{\n\t\tblocksize = Base64_Decode(block, sizeof(block), &data, &remaining);\n\t\tif (!blocksize)\n\t\t{\n\t\t\tdl->status = DL_FAILED;\n\t\t\treturn false;\n\t\t}\n\t\tVFS_WRITE(dl->file, block, blocksize);\n\t}\n\n\tdl->status = DL_FINISHED;\n\treturn false;\n}\n*/\n\nqboolean DL_Decide(struct dl_download *dl)\n{\n\tconst char *url = dl->redir;\n\tif (!*url)\n\t\turl = dl->url;\n\n\t/*if (!strnicmp(url, \"data:\", 5))\n\t\tdl->poll = DataScheme_Decode;\n\telse*/\n\tif (!strnicmp(url, \"http://\", 7))\n\t\tdl->poll = HTTPDL_Poll;\n\telse if (!strnicmp(url, \"https://\", 7))\n\t\tdl->poll = HTTPDL_Poll;\n\telse\n\t{\n\t\tdl->status = DL_FAILED;\n\t\treturn false;\n\t}\n\treturn true;\n}\n#endif\t/*!defined(FTE_TARGET_WEB)*/\n\n#ifdef MULTITHREAD\nstatic unsigned int dlthreads = 0;\n#define MAXDOWNLOADTHREADS 4\n#if defined(LOADERTHREAD) \nstatic void HTTP_Wake_Think(void *ctx, void *data, size_t a, size_t b)\n{\n\tdlthreads--;\n\tHTTP_CL_Think(NULL, NULL);\n}\n#endif\nstatic int DL_Thread_Work(void *arg)\n{\n\tstruct dl_download *dl = arg;\n\n\twhile (dl->threadenable)\n\t{\n\t\tif (!dl->poll(dl))\n\t\t{\n\t\t\tif (dl->status != DL_FAILED && dl->status != DL_FINISHED)\n\t\t\t\tdl->status = DL_FAILED;\n\t\t\tbreak;\n\t\t}\n\t}\n\tdl->threadenable = false;\n\n#if defined(LOADERTHREAD) \n\tCOM_AddWork(WG_MAIN, HTTP_Wake_Think, NULL, NULL, 0, 0);\n#endif\n\treturn 0;\n}\n\n/*create a thread to perform the given download\nto use: call DL_Create (not HTTP_CL_Get!) to get a context, then call this.\nnote that you need to call DL_Close from another thread, NOT IN THE NOTIFY FUNC.\nthe file handle must be safe to write to in threads.\n*/\nqboolean DL_CreateThread(struct dl_download *dl, vfsfile_t *file, void (*NotifyFunction)(struct dl_download *dl))\n{\n\tif (!dl)\n\t\treturn false;\n\n\tif (file)\n\t\tdl->file = file;\n\tif (NotifyFunction)\n\t\tdl->notifycomplete = NotifyFunction;\n\n\tdl->threadenable = true;\n#if defined(LOADERTHREAD) \n\tif (dlthreads < 4)\n#endif\n\t{\n\t\tdl->threadctx = Sys_CreateThread(\"download\", DL_Thread_Work, dl, THREADP_NORMAL, 0);\n\t\tif (!dl->threadctx)\n\t\t\treturn false;\n\t\tdlthreads++;\n\t}\n\n\treturn true;\n}\n#else\nqboolean DL_CreateThread(struct dl_download *dl, vfsfile_t *file, void (*NotifyFunction)(struct dl_download *dl))\n{\n\tif (!dl)\n\t\treturn false;\n\n\tif (file)\n\t\tdl->file = file;\n\tif (NotifyFunction)\n\t\tdl->notifycomplete = NotifyFunction;\n\n\treturn false;\n}\n#endif\n\n/*create a standalone download context*/\nstruct dl_download *DL_Create(const char *url)\n{\n\tstruct dl_download *newdl;\n\tnewdl = malloc(sizeof(*newdl) + strlen(url)+1);\n\tif (!newdl)\n\t\treturn NULL;\n\tmemset(newdl, 0, sizeof(*newdl));\n\tnewdl->url = (char*)(newdl+1);\n\tstrcpy(newdl->url, url);\n\tnewdl->poll = DL_Decide;\n\tnewdl->fsroot = FS_GAMEONLY;\n\tnewdl->sizelimit = 0x80000000u;\t//some sanity limit.\n#if !defined(SERVERONLY)\n\tnewdl->qdownload.method = DL_HTTP;\n#endif\n\n#if !defined(FTE_TARGET_WEB)\n\tif (!newdl->poll(newdl))\n\t{\n\t\tfree(newdl);\n\t\tnewdl = NULL;\n\t}\n#endif\n\n\treturn newdl;\n}\n\n/*destroys an entire download context*/\nvoid DL_Close(struct dl_download *dl)\n{\n\tstruct dl_download **link = NULL;\n\n\tfor (link = &activedownloads; *link; link = &(*link)->next)\n\t{\n\t\tif (*link == dl)\n\t\t{\n\t\t\t*link = dl->next;\n\t\t\tbreak;\n\t\t}\n\t}\n\n#if !defined(SERVERONLY)\n\tif (cls.download == &dl->qdownload)\n\t\tcls.download = NULL;\n#endif\n\n#ifdef MULTITHREAD\n\tdl->threadenable = false;\n\tif (dl->threadctx)\n\t{\n\t\tSys_WaitOnThread(dl->threadctx);\n\t\tdl->threadctx = NULL;\n\t}\n#endif\n\tif (dl->file && dl->file->seekstyle < SS_PIPE)\n\t\tVFS_SEEK(dl->file, 0);\n\tif (dl->notifycomplete)\n\t\tdl->notifycomplete(dl);\n\tif (dl->abort)\n\t\tdl->abort(dl);\n\tif (dl->file)\n\t\tVFS_CLOSE(dl->file);\n\tif (dl->postdata)\n\t\tBZ_Free(dl->postdata);\n\tfree(dl);\n}\n\nvoid DL_DeThread(void)\n{\n#ifdef MULTITHREAD\n\t//if we're about to fork, ensure that any downloads are properly parked so that there's no workers in an unknown state.\n\tstruct dl_download *dl;\n\tfor (dl = activedownloads; dl; dl = dl->next)\n\t{\n\t\tdl->threadenable = false;\n\t\tif (dl->threadctx)\n\t\t{\n\t\t\tSys_WaitOnThread(dl->threadctx);\n\t\t\tdl->threadctx = NULL;\n\t\t}\n\t}\n#endif\n}\n\n/*updates pending downloads*/\nunsigned int HTTP_CL_GetActiveDownloads(void)\n{\n\tstruct dl_download *dl;\n\tunsigned int count = 0;\n\n\tfor (dl = activedownloads; dl; dl = dl->next)\n\t\tcount++;\n\treturn count;\n}\n\n/*create a download context and add it to the list, for lazy people. not threaded*/\nstruct dl_download *HTTP_CL_Get(const char *url, const char *localfile, void (*NotifyFunction)(struct dl_download *dl))\n{\n\tstruct dl_download *newdl = DL_Create(url);\n\tif (!newdl)\n\t\treturn newdl;\n\n\tnewdl->notifycomplete = NotifyFunction;\n\tif (localfile)\n\t\tQ_strncpyz(newdl->localname, localfile, sizeof(newdl->localname));\n\n\tnewdl->next = activedownloads;\n\tactivedownloads = newdl;\n\n\n#ifndef SERVERONLY\n\tif (!cls.download && localfile && !newdl->isquery)\n\t{\n\t\tcls.download = &newdl->qdownload;\n\t\tif (*newdl->localname)\n\t\t\tQ_strncpyz(newdl->qdownload.localname, newdl->localname, sizeof(newdl->qdownload.localname));\n\t\telse\n\t\t\tQ_strncpyz(newdl->qdownload.localname, newdl->url, sizeof(newdl->qdownload.localname));\n\t\tQ_strncpyz(newdl->qdownload.remotename, newdl->url, sizeof(newdl->qdownload.remotename));\n\t\tnewdl->qdownload.starttime = Sys_DoubleTime();\n\t}\n#endif\n\n\treturn newdl;\n}\n\nstruct dl_download *HTTP_CL_Put(const char *url, const char *mime, const char *data, size_t datalen, void (*NotifyFunction)(struct dl_download *dl))\n{\n\tstruct dl_download *dl;\n\tif (!*mime)\n\t\treturn NULL;\n\n\tdl = HTTP_CL_Get(url, NULL, NotifyFunction);\n\tif (dl)\n\t{\n\t\tQ_strncpyz(dl->postmimetype, mime, sizeof(dl->postmimetype));\n\t\tdl->postdata = BZ_Malloc(datalen);\n\t\tmemcpy(dl->postdata, data, datalen);\n\t\tdl->postlen = datalen;\n\t}\n\treturn dl;\n}\n\nvoid HTTP_CL_Think(const char **curname, float *curpercent)\n{\n\tstruct dl_download *dl = activedownloads;\n\tstruct dl_download **link = NULL;\n#ifndef SERVERONLY\n\tfloat currenttime;\n\tif (!activedownloads)\n\t\treturn;\n\tcurrenttime = Sys_DoubleTime();\n#endif\n\n\tlink = &activedownloads;\n\twhile (*link)\n\t{\n\t\tdl = *link;\n#ifdef MULTITHREAD\n\t\tif (dl->threadctx)\n\t\t{\n\t\t\tif (dl->status == DL_FINISHED || dl->status == DL_FAILED)\n\t\t\t{\n\t\t\t\tSys_WaitOnThread(dl->threadctx);\n\t\t\t\tdl->threadctx = NULL;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\t\telse if (dl->threadenable)\n\t\t{\n\t\t\tif (dlthreads < MAXDOWNLOADTHREADS)\n\t\t\t{\n\t\t\t\tdl->threadctx = Sys_CreateThread(\"download\", DL_Thread_Work, dl, THREADP_NORMAL, 0);\n\t\t\t\tif (dl->threadctx)\n\t\t\t\t\tdlthreads++;\n\t\t\t\telse\n\t\t\t\t\tdl->threadenable = false;\n\t\t\t}\n\t\t}\n\t\telse\n#endif\n\t\t{\n\t\t\tif (!dl->poll(dl))\n\t\t\t{\n\t\t\t\t*link = dl->next;\n\t\t\t\tDL_Close(dl);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\t\tlink = &dl->next;\n\n\t\tif (curname && curpercent)\n\t\t{\n\t\t\tif (*dl->localname)\n\t\t\t\t*curname = (const char*)dl->localname;\n\t\t\telse\n\t\t\t\t*curname = (const char*)dl->url;\n\n\t\t\tif (dl->status == DL_FINISHED)\n\t\t\t\t*curpercent = 100;\n\t\t\telse if (dl->status != DL_ACTIVE)\n\t\t\t\t*curpercent = 0;\n\t\t\telse if (dl->totalsize <= 0)\n\t\t\t\t*curpercent = -1;\n\t\t\telse\n\t\t\t\t*curpercent = dl->completed*100.0f/dl->totalsize;\n\t\t}\n\n#ifndef SERVERONLY\n\t\tif (!cls.download && !dl->isquery)\n#ifdef MULTITHREAD\n\t\tif (!dl->threadenable || dl->threadctx)\t//don't show pending downloads in preference to active ones.\n#endif\n\t\t{\n\t\t\tcls.download = &dl->qdownload;\n\t\t\tdl->qdownload.method = DL_HTTP;\n\t\t\tif (*dl->localname)\n\t\t\t\tQ_strncpyz(dl->qdownload.localname, dl->localname, sizeof(dl->qdownload.localname));\n\t\t\telse\n\t\t\t\tQ_strncpyz(dl->qdownload.localname, dl->url, sizeof(dl->qdownload.localname));\n\t\t\tQ_strncpyz(dl->qdownload.remotename, dl->url, sizeof(dl->qdownload.remotename));\n\t\t\tdl->qdownload.starttime = Sys_DoubleTime();\n\t\t}\n\n\t\tif (dl->status == DL_FINISHED)\n\t\t\tdl->qdownload.percent = 100;\n\t\telse if (dl->status != DL_ACTIVE)\n\t\t\tdl->qdownload.percent = 0;\n\t\telse if (dl->totalsize <= 0)\n\t\t{\n\t\t\tdl->qdownload.sizeunknown = true;\n\t\t\tdl->qdownload.percent = 50;\n\t\t}\n\t\telse\n\t\t\tdl->qdownload.percent = dl->completed*100.0f/dl->totalsize;\n\t\tdl->qdownload.completedbytes = dl->completed;\n\n\t\tif (dl->qdownload.ratetime < currenttime)\n\t\t{\n\t\t\tdl->qdownload.ratetime = currenttime+1;\n\t\t\tdl->qdownload.rate = (dl->qdownload.completedbytes - dl->qdownload.ratebytes) / 1;\n\t\t\tdl->qdownload.ratebytes = dl->qdownload.completedbytes;\n\t\t}\n#endif\n\t}\n}\n\nvoid HTTP_CL_Terminate(void)\n{\n\tstruct dl_download *dl = activedownloads;\n\tstruct dl_download *next = NULL;\n\tnext = activedownloads;\n\tactivedownloads = NULL;\n\twhile (next)\n\t{\n\t\tdl = next;\n\t\tnext = dl->next;\n\t\tDL_Close(dl);\n\t}\n\tHTTP_CL_Think(NULL, NULL);\n\n#ifdef COOKIECOOKIECOOKIE\n\tCookie_Monster();\n#endif\n}\n#endif\t/*WEBCLIENT*/\n\n\ntypedef struct \n{\n\tvfsfile_t funcs;\n\n\tchar *data;\n\tsize_t maxlen;\n\tsize_t writepos;\n\tsize_t readpos;\n\tvoid *mutex;\n\tint refs;\n\tqboolean terminate;\t//one end has closed, make the other report failures now that its no longer needed.\n\n\tvoid *ctx;\n\tvoid (*callback) (void *ctx, vfsfile_t *pipe);\n} vfspipe_t;\n\nstatic qboolean QDECL VFSPIPE_Close(vfsfile_t *f)\n{\n\tint r;\n\tvfspipe_t *p = (vfspipe_t*)f;\n\tSys_LockMutex(p->mutex);\n\tr = --p->refs;\n\tp->terminate = true;\n\tSys_UnlockMutex(p->mutex);\n\tif (!r)\n\t{\n\t\tfree(p->data);\n\t\tSys_DestroyMutex(p->mutex);\n\t\tfree(p);\n\t}\n\telse if (p->callback)\n\t\tp->callback(p->ctx, f);\n\treturn true;\n}\nstatic qofs_t QDECL VFSPIPE_GetLen(vfsfile_t *f)\n{\n\tvfspipe_t *p = (vfspipe_t*)f;\n\treturn p->writepos - p->readpos;\n}\nstatic qofs_t QDECL VFSPIPE_Tell(vfsfile_t *f)\n{\n\tvfspipe_t *p = (vfspipe_t*)f;\n\treturn p->readpos;\n}\nstatic qboolean QDECL VFSPIPE_Seek(vfsfile_t *f, qofs_t offset)\n{\n\tvfspipe_t *p = (vfspipe_t*)f;\n\tp->readpos = offset;\n\treturn true;\n}\nstatic int QDECL VFSPIPE_ReadBytes(vfsfile_t *f, void *buffer, int len)\n{\n\tvfspipe_t *p = (vfspipe_t*)f;\n\tSys_LockMutex(p->mutex);\n\tif (p->readpos > p->writepos)\n\t\tlen = 0;\n\tif (len > p->writepos - p->readpos)\n\t{\n\t\tlen = p->writepos - p->readpos;\n\t\tif (!len && p->terminate)\n\t\t{\t//if we reached eof, we started with two refs and are down to 1 and we're reading, then its the writer side that disconnected.\n\t\t\t//eof is now fatal rather than 'try again later'.\n\t\t\tSys_UnlockMutex(p->mutex);\n\t\t\treturn -1;\n\t\t}\n\t}\n\tmemcpy(buffer, p->data+p->readpos, len);\n\tp->readpos += len;\n\tSys_UnlockMutex(p->mutex);\n\treturn len;\n}\nstatic int QDECL VFSPIPE_WriteBytes(vfsfile_t *f, const void *buffer, int len)\n{\n\tvfspipe_t *p = (vfspipe_t*)f;\n\tif (len < 0)\n\t\treturn -1;\n\tSys_LockMutex(p->mutex);\n\tif (p->terminate)\n\t{\t//if we started with 2 refs, and we're down to one, and we're writing, then its the reader that closed. that means writing is redundant and we should signal an error.\n\t\tSys_UnlockMutex(p->mutex);\n\t\treturn -1;\n\t}\n\tif (p->readpos > 8192 && !p->funcs.Seek)\n\t{\t//don't grow infinitely if we're reading+writing at the same time\n\t\t//if we're seekable, then we have to buffer the ENTIRE file.\n\t\tmemmove(p->data, p->data+p->readpos, p->writepos-p->readpos);\n\t\tp->writepos -= p->readpos;\n\t\tp->readpos = 0;\n\t}\n\tif (p->writepos + len > p->maxlen)\n\t{\n\t\tp->maxlen = p->writepos + len;\n\t\tif (p->maxlen < (p->writepos-p->readpos)*2)\t//over-allocate a little\n\t\t\tp->maxlen = (p->writepos-p->readpos)*2;\n\t\tif (p->maxlen > 0x8000000 && p->data)\n\t\t{\n\t\t\tp->maxlen = max(p->writepos,0x8000000);\n\t\t\tif (p->maxlen <= p->writepos)\n\t\t\t{\n\t\t\t\tSys_UnlockMutex(p->mutex);\n\t\t\t\treturn -1;\t//try and get the caller to stop\n\t\t\t}\n\t\t\tlen = min(len, p->maxlen - p->writepos);\n\t\t}\n\t\tp->data = realloc(p->data, p->maxlen);\n\t}\n\tmemcpy(p->data+p->writepos, buffer, len);\n\tp->writepos += len;\n\tSys_UnlockMutex(p->mutex);\n\treturn len;\n}\n\nvfsfile_t *VFS_OpenPipeInternal(int refs, qboolean seekable, void (*callback)(void *ctx, vfsfile_t *file), void *ctx)\n{\n\tvfspipe_t *newf;\n\tnewf = malloc(sizeof(*newf));\n\tnewf->refs = refs;\n\tnewf->terminate = false;\n\tnewf->mutex = Sys_CreateMutex();\n\tnewf->data = NULL;\n\tnewf->maxlen = 0;\n\tnewf->readpos = 0;\n\tnewf->writepos = 0;\n\tnewf->funcs.Close = VFSPIPE_Close;\n\tnewf->funcs.Flush = NULL;\n\tnewf->funcs.GetLen = VFSPIPE_GetLen;\n\tnewf->funcs.ReadBytes = VFSPIPE_ReadBytes;\n\tnewf->funcs.WriteBytes = VFSPIPE_WriteBytes;\n\n\tnewf->ctx = ctx;\n\tnewf->callback = callback;\n\n\tif (seekable)\n\t{\t//if this is set, then we allow changing the readpos at the expense of buffering the ENTIRE file. no more fifo.\n\t\tnewf->funcs.Seek = VFSPIPE_Seek;\n\t\tnewf->funcs.Tell = VFSPIPE_Tell;\n\t\tnewf->funcs.seekstyle = SS_PIPE;\n\t}\n\telse\n\t{\t//periodically reclaim read data to avoid memory wastage. this means that seeking can't work.\n\t\tnewf->funcs.Seek = NULL;\n\t\tnewf->funcs.Tell = NULL;\n\t\tnewf->funcs.seekstyle = SS_UNSEEKABLE;\n\t}\n\n\treturn &newf->funcs;\n}\nvfsfile_t *VFS_OpenPipeCallback(void (*callback)(void*ctx, vfsfile_t *file), void *ctx)\n{\n\treturn VFS_OpenPipeInternal(2, false, callback, ctx);\n}\n\nvfsfile_t *VFSPIPE_Open(int refs, qboolean seekable)\n{\n\treturn VFS_OpenPipeInternal(refs, seekable, NULL, NULL);\n}\n\nstatic int QDECL VFSPIPE_WriteBytes_Pair(vfsfile_t *f, const void *buffer, int len)\n{\n\tf = ((vfspipe_t*)f)->ctx;\t//swap to its partner\n\tif (!f)\t//it got closed...\n\t\treturn VFS_ERROR_EOF;\n\treturn VFSPIPE_WriteBytes(f, buffer, len);\t//read it directly.\n}\nstatic int QDECL VFSPIPE_ReadBytes_Pair(vfsfile_t *f, void *buffer, int len)\n{\n\tf = ((vfspipe_t*)f)->ctx;\t//swap to its partner\n\tif (!f)\t//it got closed...\n\t\treturn VFS_ERROR_EOF;\n\treturn VFSPIPE_WriteBytes(f, buffer, len);\t//read it directly.\n}\nstatic qboolean QDECL VFSPIPE_Close_Pair(vfsfile_t *f)\n{\n\tvfspipe_t *p = ((vfspipe_t*)f)->ctx;\n\tif (p)\n\t\tp->ctx = NULL;\t//don't allow writes to a dead pipe...\n\treturn VFSPIPE_Close(f);\n}\nvoid VFSPIPE_OpenPair(vfsfile_t *pair[2])\n{\n\tpair[0] = VFS_OpenPipeInternal(1, false, NULL, NULL);\n\tpair[1] = VFS_OpenPipeInternal(1, false, NULL, NULL);\n\n\t//cross-link them\n\t((vfspipe_t*)pair[0])->ctx = pair[1];\n\t((vfspipe_t*)pair[1])->ctx = pair[0];\n\tpair[0]->Close = VFSPIPE_Close_Pair;\n\tpair[1]->Close = VFSPIPE_Close_Pair;\n\n\tpair[0]->WriteBytes = VFSPIPE_WriteBytes_Pair;\n\tpair[1]->WriteBytes = VFSPIPE_WriteBytes_Pair;\n\n\n\tpair[0]->ReadBytes = VFSPIPE_ReadBytes_Pair;\n\tpair[1]->ReadBytes = VFSPIPE_ReadBytes_Pair;\n}\n"
  },
  {
    "path": "engine/http/httpserver.c",
    "content": "#include \"quakedef.h\"\n\n#ifdef WEBSERVER\n\n#include \"iweb.h\"\n\n#include \"netinc.h\"\n\n//FIXME: Before any admins use this for any serious usage, make the server send bits of file slowly.\n\nstatic qboolean httpserverinitied = false;\nqboolean httpserverfailed = false;\nstatic int\thttpserversocket;\nstatic int\thttpserverport;\n\n#if 0//def WEBSVONLY\nstatic int natpmpsocket = INVALID_SOCKET;\nstatic int natpmptime;\n#ifdef _WIN32\n#include <mmsystem.h>\n#endif\nstatic void sendnatpmp(int port)\n{\n\tstruct sockaddr_in router;\n\tstruct\n\t{\n\t\tqbyte ver;\n\t\tqbyte op;\n\t\tshort reserved1;\n\t\tshort privport; short pubport;\n\t\tint mapping_expectancy;\n\t} pmpreqmsg;\n\n\tint curtime = timeGetTime();\n\tif (natpmpsocket == INVALID_SOCKET)\n\t{\n\t\tunsigned long _true = true;\n\t\tnatpmpsocket = socket(AF_INET, SOCK_CLOEXEC|SOCK_DGRAM, 0);\n\t\tif (natpmpsocket == INVALID_SOCKET)\n\t\t\treturn;\n\t\tioctlsocket (natpmpsocket, FIONBIO, &_true);\n\t}\n\telse if (curtime - natpmptime < 0)\n\t\treturn;\n\tnatpmptime = curtime+60*1000;\n\n\tmemset(&router, 0, sizeof(router));\n\trouter.sin_family = AF_INET;\n\trouter.sin_port = htons(5351);\n\trouter.sin_addr.S_un.S_un_b.s_b1 = 192;\n\trouter.sin_addr.S_un.S_un_b.s_b2 = 168;\n\trouter.sin_addr.S_un.S_un_b.s_b3 = 0;\n\trouter.sin_addr.S_un.S_un_b.s_b4 = 1;\n\n\tpmpreqmsg.ver = 0;\n\tpmpreqmsg.op = 0;\n\tpmpreqmsg.reserved1 = htons(0);\n\tpmpreqmsg.privport = htons(port);\n\tpmpreqmsg.pubport = htons(port);\n\tpmpreqmsg.mapping_expectancy = htons(60*5);\n\n\tsendto(natpmpsocket, (void*)&pmpreqmsg, 2, 0, (struct sockaddr*)&router, sizeof(router));\n\n\tpmpreqmsg.op = 2;\n\tsendto(natpmpsocket, (void*)&pmpreqmsg, sizeof(pmpreqmsg), 0, (struct sockaddr*)&router, sizeof(router));\n}\nvoid checknatpmp(int port)\n{\n\tstruct\n\t{\n\t\tqbyte ver; qbyte op; short resultcode;\n\t\tint age;\n\t\tunion\n\t\t{\n\t\t\tstruct\n\t\t\t{\n\t\t\t\tshort privport; short pubport;\n\t\t\t\tint mapping_expectancy;\n\t\t\t};\n\t\t\tqbyte ipv4[4];\n\t\t};\n\t} pmpreqrep;\n\tstruct sockaddr_in from;\n\tint fromlen = sizeof(from);\n\tint len;\n\tstatic int oldip=-1;\n\tstatic short oldport;\n\tmemset(&pmpreqrep, 0, sizeof(pmpreqrep));\n\tsendnatpmp(port);\n\tif (natpmpsocket == INVALID_SOCKET)\n\t\tlen = -1;\n\telse\n\t\tlen = recvfrom(natpmpsocket, (void*)&pmpreqrep, sizeof(pmpreqrep), 0, (struct sockaddr*)&from, &fromlen);\n\tif (len == 12 && pmpreqrep.op == 128)\n\t{\n\t\tif (oldip != *(int*)pmpreqrep.ipv4)\n\t\t{\n\t\t\toldip = *(int*)pmpreqrep.ipv4;\n\t\t\toldport = 0;\n\t\t\tIWebPrintf(\"Public ip is %i.%i.%i.%i\\n\", pmpreqrep.ipv4[0], pmpreqrep.ipv4[1], pmpreqrep.ipv4[2], pmpreqrep.ipv4[3]);\n\t\t}\n\t}\n\telse if (len == 16 && pmpreqrep.op == 129)\n\t{\n\t\tif (oldport != pmpreqrep.pubport)\n\t\t{\n\t\t\toldport = pmpreqrep.pubport;\n\t\t\tIWebPrintf(\"Public udp port %i (local %i)\\n\", ntohs(pmpreqrep.pubport), ntohs(pmpreqrep.privport));\n\t\t}\n\t}\n\telse if (len == 16 && pmpreqrep.op == 130)\n\t{\n\t\tif (oldport != pmpreqrep.pubport)\n\t\t{\n\t\t\toldport = pmpreqrep.pubport;\n\t\t\tIWebPrintf(\"Public tcp port %i (local %i)\\n\", ntohs(pmpreqrep.pubport), ntohs(pmpreqrep.privport));\n\t\t}\n\t}\n}\n#else\nvoid checknatpmp(int port)\n{\n}\n#endif\n\ntypedef enum {HTTP_WAITINGFORREQUEST,HTTP_SENDING} http_mode_t;\n\n\n#ifndef _WIN32\nstatic qboolean HTTP_DoAccepts(int epfd);\nstruct http_epoll_ctx\n{\n\tstruct epollctx_s pub;\n\tint epollfd;\n};\nstatic void HTTP_ServerEPolled(struct epollctx_s *pubctx, unsigned int ev)\n{\n\tstruct http_epoll_ctx *ctx = (struct http_epoll_ctx*)pubctx;\n\tif (!HTTP_DoAccepts(ctx->epollfd))\n\t{\n\t\tint e = neterrno();\n\t\tswitch(e)\n\t\t{\n\t\t/*case NET_EWOULDBLOCK:\n\t\t\tbreak;\n\t\tcase NET_ECONNABORTED:\n\t\tcase NET_ECONNRESET:\n\t\t\tCon_TPrintf (\"Connection lost or aborted\\n\");\n\t\t\tbreak;*/\n\t\tdefault:\n\t\t\tIWebPrintf (\"NET_GetPacket: %s\\n\", strerror(e));\n\t\t\tbreak;\n\t\t}\n\t}\n}\n#endif\n\nqboolean HTTP_ServerInit(int epfd, int port)\n{\n\tstruct sockaddr_qstorage address;\n\tunsigned long _true = true;\n\tint i;\n\n\tmemset(&address, 0, sizeof(address));\n\t//check for interface binding option. this also forces ipv4, oh well.\n\tif ((i = COM_CheckParm(\"-ip\")) != 0 && i < com_argc)\n\t{\n\t\t((struct sockaddr_in*)&address)->sin_addr.s_addr = inet_addr(com_argv[i+1]);\n\t\t\tCon_TPrintf(\"Binding to IP Interface Address of %s\\n\",\n\t\t\t\t\tinet_ntoa(((struct sockaddr_in*)&address)->sin_addr));\n\n\n\t\t((struct sockaddr_in*)&address)->sin_family = AF_INET;\n\t\tif (port != PORT_ANY)\n\t\t\t((struct sockaddr_in*)&address)->sin_port = htons((short)port);\n\t}\n\telse\n\t{\t//otherwise just use ipv6\n\t\t((struct sockaddr_in6*)&address)->sin6_family = AF_INET6;\n\t\tif (port != PORT_ANY)\n\t\t\t((struct sockaddr_in6*)&address)->sin6_port = htons((short)port);\n\t}\n\n\tif ((httpserversocket = socket (((struct sockaddr*)&address)->sa_family, SOCK_STREAM, IPPROTO_TCP)) == -1)\n\t{\n\t\tIWebPrintf (\"HTTP_ServerInit: socket: %s\\n\", strerror(neterrno()));\n\t\thttpserverfailed = true;\n\t\treturn false;\n\t}\n\n\tif (((struct sockaddr_in6*)&address)->sin6_family == AF_INET6 && !memcmp(((struct sockaddr_in6*)&address)->sin6_addr.s6_addr, \"\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\", 16))\n\t{\t//in6addr_any, allow ipv4 too, if we can do hybrid sockets.\n\t\tunsigned long v6only = false;\n\t\tsetsockopt(httpserversocket, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&v6only, sizeof(v6only));\n\t}\n\n#ifdef SO_REUSEADDR\n\t{\t//bypass TIME_WAIT (this is supposed to still fail if another process still has it bound)\n\t\tint reuse = true;\n\t\tsetsockopt(httpserversocket, SOL_SOCKET, SO_REUSEADDR, (char *)&reuse, sizeof(reuse));\n\t}\n#endif\n\n\tif (ioctlsocket (httpserversocket, FIONBIO, &_true) == -1)\n\t{\n\t\tIWebPrintf (\"HTTP_ServerInit: ioctl FIONBIO: %s\\n\", strerror(neterrno()));\n\t\thttpserverfailed = true;\n\t\treturn false;\n\t}\n\n\tif (bind (httpserversocket, (void *)&address, sizeof(address)) == -1)\n\t{\n\t\tclosesocket(httpserversocket);\n\t\tIWebPrintf(\"HTTP_ServerInit: failed to bind to socket\\n\");\n\t\thttpserverfailed = true;\n\t\treturn false;\n\t}\n\n\tlisten(httpserversocket, 3);\n\n\thttpserverinitied = true;\n\thttpserverfailed = false;\n\thttpserverport = port;\n\n#ifndef _WIN32\n\tif (epfd >= 0)\n\t{\n\t\tstatic struct http_epoll_ctx ctx;\n\t\tstruct epoll_event ev;\n\t\tctx.epollfd = epfd;\n\t\tctx.pub.Polled = HTTP_ServerEPolled;\n\t\tev.events = EPOLLIN;\n\t\tev.data.ptr = &ctx;\n\t\tepoll_ctl(epfd, EPOLL_CTL_ADD, httpserversocket, &ev);\n\t}\n#endif\n\n\tIWebPrintf(\"HTTP server is running\\n\");\n\treturn true;\n}\n\nvoid HTTP_ServerShutdown(void)\n{\n\tclosesocket(httpserversocket);\n\tIWebPrintf(\"HTTP server closed\\n\");\n\n\thttpserverinitied = false;\n}\n\ntypedef struct HTTP_active_connections_s {\n#ifndef _WIN32\n\tstruct epollctx_s pub;\n\tint epfd;\n#endif\n\tSOCKET datasock;\n\tchar peername[256];\n\tvfsfile_t *file;\n\tstruct HTTP_active_connections_s *next;\n\n\thttp_mode_t mode;\n\tqboolean modeswitched;\n\tqboolean closeaftertransaction;\n\tqboolean acceptgzip;\n\n\tchar *inbuffer;\n\tunsigned int inbuffersize;\n\tunsigned int inbufferused;\n\n\tchar *outbuffer;\n\tunsigned int outbuffersize;\n\tunsigned int outbufferused;\n} HTTP_active_connections_t;\nstatic HTTP_active_connections_t *HTTP_ServerConnections;\nstatic int httpconnectioncount;\n\nstatic void ExpandInBuffer(HTTP_active_connections_t *cl, unsigned int quant, qboolean fixedsize)\n{\n\tunsigned int newsize;\n\tif (fixedsize)\n\t\tnewsize = quant;\n\telse\n\t\tnewsize = cl->inbuffersize+quant;\n\tif (newsize <= cl->inbuffersize)\n\t\treturn;\n\n\tcl->inbuffer = IWebRealloc(cl->inbuffer, newsize);\n\tcl->inbuffersize = newsize;\n}\nstatic void ExpandOutBuffer(HTTP_active_connections_t *cl, unsigned int quant, qboolean fixedsize)\n{\n\tunsigned int newsize;\n\tif (fixedsize)\n\t\tnewsize = quant;\n\telse\n\t\tnewsize = cl->outbuffersize+quant;\n\tif (newsize <= cl->outbuffersize)\n\t\treturn;\n\n\tcl->outbuffer = IWebRealloc(cl->outbuffer, newsize);\n\tcl->outbuffersize = newsize;\n}\n\nconst char *HTTP_RunClient (HTTP_active_connections_t *cl)\n{\n\tchar *content;\n\tchar *msg, *nl;\n\tchar buf2[2560];\t//short lived temp buffer.\n\tchar resource[2560], *args;\n\tchar host[256];\n\tchar mode[80];\n\tqboolean hostspecified;\n\tunsigned int contentlen;\n\n\tint HTTPmarkup;\t//version\n\tint localerrno;\n\n\tint ammount, wanted;\n\tint matchetag;\n\n\tswitch(cl->mode)\n\t{\n\tcase HTTP_WAITINGFORREQUEST:\n\t\tif (cl->outbufferused)\n\t\t\tSys_Error(\"Persistant connection was waiting for input with unsent output\");\n\t\tammount = cl->inbuffersize-1 - cl->inbufferused;\n\t\tif (ammount < 128)\n\t\t{\n\t\t\tif (cl->inbuffersize>128*1024)\n\t\t\t\treturn \"headers larger than 128kb\";\t//that's just taking the piss.\n\n\t\t\tExpandInBuffer(cl, 1500, false);\n\t\t\tammount = cl->inbuffersize-1 - cl->inbufferused;\n\t\t}\n\t\tif (cl->modeswitched)\n\t\t{\n\t\t\tammount = 0;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t//we can't try and recv 0 bytes as we use an expanding buffer\n\t\t\tammount = recv(cl->datasock, cl->inbuffer+cl->inbufferused, ammount, 0);\n\t\t\tif (ammount < 0)\n\t\t\t{\n\t\t\t\tint e = neterrno();\n\t\t\t\tif (e == NET_EWOULDBLOCK)\t//they closed on us. Assume end.\n\t\t\t\t\treturn NULL;\n\t\t\t\treturn \"recv error\";\n\t\t\t}\n\t\t\tif (ammount == 0)\n\t\t\t\treturn \"peer closed connection\";\n\t\t}\n\t\tcl->modeswitched = false;\n\n\t\tcl->inbufferused += ammount;\n\t\tcl->inbuffer[cl->inbufferused] = '\\0';\n\n\t\tcontent = NULL;\n\t\tmsg = cl->inbuffer;\n\t\tnl = strchr(msg, '\\n');\n\t\tif (!nl)\n\t\t\treturn NULL;\t//we need more... MORE!!! MORE I TELL YOU!!!!\n\n\t\t//FIXME: COM_ParseOut recognises // comments, which is not correct.\n\t\tmsg = COM_ParseOut(msg, mode, sizeof(mode));\n\n\t\tmsg = COM_ParseOut(msg, resource, sizeof(resource));\n\n\t\tif (!*resource)\n\t\t\treturn \"not http\";\t//even if they forgot to specify a resource, we didn't find an HTTP so we have no option but to close.\n\n\t\thost[0] = '?';\n\t\thost[1] = 0;\n\n\t\thostspecified = false;\n\t\tif (!strnicmp(resource, \"http://\", 7))\n\t\t{\t//groan... 1.1 compliance requires parsing this correctly, without the client ever specifiying it.\n\t\t\tchar *slash;\t//we don't do multiple hosts.\n\t\t\thostspecified=true;\n\t\t\tslash = strchr(resource+7, '/');\n\t\t\tif (!slash)\n\t\t\t\tstrcpy(resource, \"/\");\n\t\t\telse\n\t\t\t{\n\t\t\t\tint hlen = slash-(resource+7);\n\t\t\t\tif (hlen > sizeof(host)-1)\n\t\t\t\t\thlen = sizeof(host)-1;\n\t\t\t\tmemcpy(host, resource+7, hlen);\n\t\t\t\thost[hlen] = 0;\n\t\t\t\tmemmove(resource, slash, strlen(slash+1));\t//just get rid of the http:// stuff.\n\t\t\t}\n\t\t}\n\n\t\targs = strchr(resource, '?');\n\t\tif (args)\n\t\t\t*args++=0;\n\n\t\tif (!strcmp(resource, \"/\"))\n\t\t\tstrcpy(resource, \"/index.html\");\n\n\t\tcl->acceptgzip = false;\n\n\t\tmsg = COM_ParseOut(msg, buf2, sizeof(buf2));\n\t\tcontentlen = 0;\n\t\tmatchetag = 0;\n\t\tif (!strnicmp(buf2, \"HTTP/\", 5))\n\t\t{\n\t\t\tif (!strncmp(buf2, \"HTTP/1.1\", 8))\n\t\t\t{\n\t\t\t\tHTTPmarkup = 3;\n\t\t\t\tcl->closeaftertransaction = false;\n\t\t\t}\n\t\t\telse if (!strncmp(buf2, \"HTTP/1\", 6))\n\t\t\t{\n\t\t\t\tHTTPmarkup = 2;\n\t\t\t\tcl->closeaftertransaction = true;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tHTTPmarkup = 1;\t//0.9... lamer.\n\t\t\t\tcl->closeaftertransaction = true;\n\t\t\t}\n\n\t\t\t//expect X lines containing options.\n\t\t\t//then a blank line. Don't continue till we have that.\n\n\t\t\tmsg = nl+1;\n\t\t\twhile (1)\n\t\t\t{\n\t\t\t\tif (*msg == '\\r')\n\t\t\t\t\tmsg++;\n\t\t\t\tif (*msg == '\\n')\n\t\t\t\t{\n\t\t\t\t\tmsg++;\n\t\t\t\t\tbreak;\t//that was our blank line.\n\t\t\t\t}\n\n\t\t\t\twhile(*msg == ' ')\n\t\t\t\t\tmsg++;\n\n\t\t\t\tif (!strnicmp(msg, \"Host: \", 6))\t//parse needed header fields\n\t\t\t\t{\n\t\t\t\t\tint l = 0;\n\t\t\t\t\tmsg += 6;\n\t\t\t\t\twhile (*msg == ' ' || *msg == '\\t')\n\t\t\t\t\t\tmsg++;\n\t\t\t\t\twhile (*msg != '\\r' && *msg != '\\n' && l < sizeof(host)-1)\n\t\t\t\t\t\thost[l++] = *msg++;\n\t\t\t\t\thost[l] = 0;\n\t\t\t\t\thostspecified = true;\n\t\t\t\t}\n\t\t\t\telse if (!strnicmp(msg, \"Content-Length: \", 16))\t//parse needed header fields\n\t\t\t\t\tcontentlen = strtoul(msg+16, NULL, 0);\n\t\t\t\telse if (!strnicmp(msg, \"Accept-Encoding:\", 16))\t//parse needed header fields\n\t\t\t\t{\n\t\t\t\t\tchar *e;\n\t\t\t\t\tmsg += 16;\n\t\t\t\t\twhile(*msg)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (*msg == '\\n')\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\twhile (*msg == ' ' || *msg == '\\t')\n\t\t\t\t\t\t\tmsg++;\n\t\t\t\t\t\tif (*msg == ',')\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tmsg++;\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t\te = msg;\n\t\t\t\t\t\twhile(*e)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (*e == ',' || *e == '\\r' || *e == '\\n')\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\te++;\n\t\t\t\t\t\t}\n\t\t\t\t\t\twhile(e > msg && (e[-1] == ' ' || e[-1] == '\\t'))\n\t\t\t\t\t\t\te--;\n\t\t\t\t\t\tif (e-msg == 4 && !strncmp(msg, \"gzip\", 4))\n\t\t\t\t\t\t\tcl->acceptgzip = true;\n\t\t\t\t\t\twhile(*msg && *msg != '\\n' && *msg != ',')\n\t\t\t\t\t\t\tmsg++;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse if (!strnicmp(msg, \"If-None-Match:\", 14))\n\t\t\t\t{\n\t\t\t\t\tmsg += 14;\n\t\t\t\t\twhile(*msg)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (*msg == ' ' || *msg == ',')\n\t\t\t\t\t\t\tmsg++;\n\t\t\t\t\t\telse if (*msg == '\\\"')\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tmsg++;\n\t\t\t\t\t\t\tmatchetag = strtoul(msg, &msg, 16);\n\t\t\t\t\t\t\tif (*msg == '\\\"')\n\t\t\t\t\t\t\t\tmsg++;\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tmatchetag=0;\t//something went wrong.\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse if (!strnicmp(msg, \"Transfer-Encoding: \", 18))\t//parse needed header fields\n\t\t\t\t{\n\t\t\t\t\tcl->closeaftertransaction = true;\n\t\t\t\t\tgoto notimplemented;\n\t\t\t\t}\n\t\t\t\telse if (!strnicmp(msg, \"Connection: close\", 17))\n\t\t\t\t\tcl->closeaftertransaction = true;\n\t\t\t\telse if (!strnicmp(msg, \"Connection: Keep-Alive\", 22))\n\t\t\t\t\tcl->closeaftertransaction = false;\n\n\t\t\t\twhile(*msg != '\\n')\n\t\t\t\t{\n\t\t\t\t\tif (!*msg)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (msg == cl->inbuffer+cl->inbufferused)\n\t\t\t\t\t\t\treturn NULL;\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\treturn \"Unexpected null byte\";\n\t\t\t\t\t}\n\t\t\t\t\tmsg++;\n\t\t\t\t}\n\t\t\t\tmsg++;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tHTTPmarkup = 0;\t//strimmed... totally...\n\t\t\tcl->closeaftertransaction = true;\n\t\t\t//don't bother running to nl.\n\t\t}\n\n\t\tif (cl->inbufferused-(msg-cl->inbuffer) < contentlen)\n\t\t\treturn NULL;\n\n\t\tcl->modeswitched = true;\n\n\t\tif (contentlen)\n\t\t{\n\t\t\tcontent = IWebMalloc(contentlen+1);\n\t\t\tmemcpy(content, msg, contentlen+1);\n\t\t}\n\n\t\tmemmove(cl->inbuffer, cl->inbuffer+(msg-cl->inbuffer+contentlen), cl->inbufferused-(msg-cl->inbuffer+contentlen));\n\t\tcl->inbufferused -= msg-cl->inbuffer+contentlen;\n\n\n\t\tif (HTTPmarkup == 3 && !hostspecified)\t//1.1 requires the host to be specified... we ca,just ignore it as we're not routing or imitating two servers. (for complience we need to encourage the client to send - does nothing for compatability or anything, just compliance to spec. not always the same thing)\n\t\t{\n\t\t\tmsg = \"HTTP/1.1 400 Bad Request\\r\\n\"\t/*\"Content-Type: application/octet-stream\\r\\n\"*/\t\t\"Content-Length: 69\\r\\n\"\t\"Server: \"FULLENGINENAME\"/0\\r\\n\"\t\"\\r\\n\"\t\"400 Bad Request\\r\\nYour client failed to provide the host header line\";\n\n\t\t\tIWebPrintf(\"%s: no host specified\\n\", cl->peername);\n\n\t\t\tammount = strlen(msg);\n\t\t\tExpandOutBuffer(cl, ammount, true);\n\t\t\tmemcpy(cl->outbuffer, msg, ammount);\n\t\t\tcl->outbufferused = ammount;\n\t\t\tcl->mode = HTTP_SENDING;\n\t\t}\n\t\telse if (!stricmp(mode, \"GET\") || !stricmp(mode, \"HEAD\") || !stricmp(mode, \"POST\"))\n\t\t{\n\t\t\ttime_t timestamp = 0;\n\t\t\tqboolean gzipped = false;\n\t\t\tif (*resource != '/')\n\t\t\t{\n\t\t\t\tresource[0] = '/';\n\t\t\t\tresource[1] = 0;\t//I'm lazy, they need to comply\n\t\t\t}\n\t\t\tIWebPrintf(\"%s: Download request for \\\"http://%s/%s\\\"\\n\", cl->peername, host, resource+1);\n\n\t\t\tif (!strnicmp(mode, \"P\", 1))\t//when stuff is posted, data is provided. Give an error message if we couldn't do anything with that data.\n\t\t\t\tcl->file = IWebGenerateFile(resource+1, content, contentlen);\n\t\t\telse\n\t\t\t{\n\t\t\t\tchar filename[MAX_OSPATH], *q;\n\t\t\t\tcl->file = NULL;\n\t\t\t\tQ_strncpyz(filename, resource+1, sizeof(filename));\n\t\t\t\tq = strchr(filename, '?');\n\t\t\t\tif (q) *q = 0;\n\n\t\t\t\tif (SV_AllowDownload(filename))\n\t\t\t\t{\n\t\t\t\t\tchar nbuf[MAX_OSPATH];\n\t\t\t\t\tflocation_t loc = {NULL};\n\n\t\t\t\t\tif (cl->acceptgzip && strlen(filename) < sizeof(nbuf)-4)\n\t\t\t\t\t{\n\t\t\t\t\t\tQ_strncpyz(nbuf, filename, sizeof(nbuf));\n\t\t\t\t\t\tQ_strncatz(nbuf, \".gz\", sizeof(nbuf));\n\t\t\t\t\t\tgzipped = !!FS_FLocateFile(nbuf, FSLF_IFFOUND|FSLF_DONTREFERENCE, &loc);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\tgzipped = false;\n\t\t\t\t\tif (gzipped || FS_FLocateFile(filename, FSLF_IFFOUND|FSLF_DONTREFERENCE, &loc))\n\t\t\t\t\t{\n\t\t\t\t\t\tFS_GetLocMTime(&loc, &timestamp);\n\t\t\t\t\t\tcl->file = FS_OpenReadLocation(NULL, &loc);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\tcl->file = NULL;\n\t\t\t\t}\n\n\t\t\t\tif (!cl->file)\n\t\t\t\t{\n\t\t\t\t\tcl->file = IWebGenerateFile(resource+1, content, contentlen);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!cl->file)\n\t\t\t{\n\t\t\t\tIWebPrintf(\"%s: 404 - not found\\n\", cl->peername);\n\n\t\t\t\tif (HTTPmarkup >= 3)\n\t\t\t\t\tmsg = \"HTTP/1.1 404 Not Found\\r\\n\"\t\"Content-Type: text/plain\\r\\n\"\t\t\"Content-Length: 15\\r\\n\"\t\"Server: \"FULLENGINENAME\"/0\\r\\n\"\t\"\\r\\n\"\t\"404 Bad address\";\n\t\t\t\telse if (HTTPmarkup == 2)\n\t\t\t\t\tmsg = \"HTTP/1.0 404 Not Found\\r\\n\"\t\"Content-Type: text/plain\\r\\n\"\t\t\"Content-Length: 15\\r\\n\"\t\"Server: \"FULLENGINENAME\"/0\\r\\n\"\t\"\\r\\n\"\t\"404 Bad address\";\n\t\t\t\telse if (HTTPmarkup)\n\t\t\t\t\tmsg = \"HTTP/0.9 404 Not Found\\r\\n\"\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\\r\\n\"\t\"404 Bad address\";\n\t\t\t\telse\n\t\t\t\t\tmsg = \"<HTML><HEAD><TITLE>404 Not Found</TITLE></HEAD><BODY>404 Not Found<BR>The specified file could not be found on the server</HEAD></HTML>\";\n\n\t\t\t\tammount = strlen(msg);\n\t\t\t\tExpandOutBuffer(cl, ammount, true);\n\t\t\t\tmemcpy(cl->outbuffer, msg, ammount);\n\t\t\t\tcl->outbufferused = ammount;\n\t\t\t\tcl->mode = HTTP_SENDING;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tconst char *mimeline;\n\t\t\t\tchar modifiedline[128];\n\t\t\t\tif (strstr(resource, \".htm\"))\n\t\t\t\t\tmimeline = \"Content-Type: text/html\\r\\n\";\n\t\t\t\telse if (strstr(resource, \".wasm\"))\n\t\t\t\t\tmimeline = \"Content-Type: application/wasm\\r\\n\";\n\t\t\t\telse if (strstr(resource, \".js\"))\n\t\t\t\t\tmimeline = \"Content-Type: text/javascript\\r\\n\";\n//\t\t\t\telse if (strstr(resource, \".mvd\"))\n//\t\t\t\t\tmimeline = \"Content-Type: application/x-multiviewdemo\\r\\nAccess-Control-Allow-Origin: *\\r\\n\";\n/*\n\t\t\t\telse if (strstr(resource, \".fmf\"))\n\t\t\t\t\tmimeline = \"Content-Type: application/x-multiviewdemo\\r\\nAccess-Control-Allow-Origin: *\\r\\n\";\n\t\t\t\telse if (strstr(resource, \".pak\"))\n\t\t\t\t\tmimeline = \"Content-Type: application/x-multiviewdemo\\r\\nAccess-Control-Allow-Origin: *\\r\\n\";\n\t\t\t\telse if (strstr(resource, \".pk3\"))\n\t\t\t\t\tmimeline = \"Content-Type: application/x-multiviewdemo\\r\\nAccess-Control-Allow-Origin: *\\r\\n\";*/\n\t\t\t\telse\n\t\t\t\t\tmimeline = \"\";\n\n\t\t\t\tif (timestamp)\n\t\t\t\t{\n\t\t\t\t\tstrftime(modifiedline, sizeof(modifiedline), \"Last-Modified: %a, %d %b %Y %H:%M:%S GMT\\r\\n\", gmtime(&timestamp));\n\t\t\t\t\tQ_snprintfz(modifiedline+strlen(modifiedline), sizeof(modifiedline)-strlen(modifiedline), \"ETag: \\\"%x\\\"\\r\\n\", (unsigned int)timestamp);\n\t\t\t\t}\n\t\t\t\telse if (gzipped)\n\t\t\t\t\tQ_snprintfz(modifiedline+strlen(modifiedline), sizeof(modifiedline)-strlen(modifiedline), \"Cache-Control: public, max-age=86400\\r\\n\");\n\t\t\t\telse\n\t\t\t\t\t*modifiedline = 0;\n\n\t\t\t\t//fixme: add connection: keep-alive or whatever so that ie3 is happy...\n\t\t\t\tif (HTTPmarkup >= 3 && matchetag && matchetag==(unsigned int)timestamp)\n\t\t\t\t{\n\t\t\t\t\tsprintf(resource, \"HTTP/1.1 304 Not Modified\\r\\n\"\t\"%s%s%s\"\t\t\"Connection: %s\\r\\n\"\t/*\"Content-Length: %i\\r\\n\"*/\t\"Server: \"FULLENGINENAME\"/0\\r\\n\"\t\"\\r\\n\", modifiedline, mimeline, gzipped?\"Content-Encoding: gzip\\r\\n\":\"\", cl->closeaftertransaction?\"close\":\"keep-alive\"/*, (int)VFS_GETLEN(cl->file)*/);\n\t\t\t\t\tif (cl->file)\n\t\t\t\t\t{\t//don't send any actual data...\n\t\t\t\t\t\tVFS_CLOSE(cl->file);\n\t\t\t\t\t\tcl->file = NULL;\n\t\t\t\t\t}\n\t\t\t\t\tIWebPrintf(\"%s:   Not Modified\\n\", cl->peername);\n\t\t\t\t}\n\t\t\t\telse if (HTTPmarkup>=3)\n\t\t\t\t\tsprintf(resource, \"HTTP/1.1 200 OK\\r\\n\"\t\t\t\t\"%s%s%s\"\t\t\"Connection: %s\\r\\n\"\t\"Content-Length: %i\\r\\n\"\t\"Server: \"FULLENGINENAME\"/0\\r\\n\"\t\"\\r\\n\", modifiedline, mimeline, gzipped?\"Content-Encoding: gzip\\r\\n\":\"\", cl->closeaftertransaction?\"close\":\"keep-alive\", (int)VFS_GETLEN(cl->file));\n\t\t\t\telse if (HTTPmarkup==2)\n\t\t\t\t\tsprintf(resource, \"HTTP/1.0 200 OK\\r\\n\"\t\t\t\t\"%s%s%s\"\t\t\"Connection: %s\\r\\n\"\t\"Content-Length: %i\\r\\n\"\t\"Server: \"FULLENGINENAME\"/0\\r\\n\"\t\"\\r\\n\", modifiedline, mimeline, gzipped?\"Content-Encoding: gzip\\r\\n\":\"\", cl->closeaftertransaction?\"close\":\"keep-alive\", (int)VFS_GETLEN(cl->file));\n\t\t\t\telse if (HTTPmarkup)\n\t\t\t\t\tsprintf(resource, \"HTTP/0.9 200 OK\\r\\n\\r\\n\");\n\t\t\t\telse\n\t\t\t\t\tstrcpy(resource, \"\");\n\t\t\t\tmsg = resource;\n\n\t\t\t\tif ((*mode == 'H' || *mode == 'h') && cl->file)\n\t\t\t\t{\t//'head'\n\t\t\t\t\tVFS_CLOSE(cl->file);\n\t\t\t\t\tcl->file = NULL;\n\t\t\t\t}\n\n\t\t\t\tammount = strlen(msg);\n\t\t\t\tExpandOutBuffer(cl, ammount, true);\n\t\t\t\tmemcpy(cl->outbuffer, msg, ammount);\n\t\t\t\tcl->outbufferused = ammount;\n\t\t\t\tcl->mode = HTTP_SENDING;\n\t\t\t}\n\t\t}\n\t\t//PUT/POST must support chunked transfer encoding for 1.1 compliance.\n/*\t\t\telse if (!stricmp(mode, \"PUT\"))\t//put is replacement of a resource. (file uploads)\n\t\t{\n\t\t}\n*/\n\t\telse\n\t\t{\nnotimplemented:\n\t\t\tif (HTTPmarkup >= 3)\n\t\t\t\tmsg = \"HTTP/1.1 501 Not Implemented\\r\\n\\r\\n\";\n\t\t\telse if (HTTPmarkup == 2)\n\t\t\t\tmsg = \"HTTP/1.0 501 Not Implemented\\r\\n\\r\\n\";\n\t\t\telse if (HTTPmarkup)\n\t\t\t\tmsg = \"HTTP/0.9 501 Not Implemented\\r\\n\\r\\n\";\n\t\t\telse\n\t\t\t{\n\t\t\t\tmsg = NULL;\n\t\t\t\treturn \"unsupported http version\";\n\t\t\t}\n\t\t\tIWebPrintf(\"%s: 501 - not implemented\\n\", cl->peername);\n\n\t\t\tif (msg)\n\t\t\t{\n\t\t\t\tammount = strlen(msg);\n\t\t\t\tExpandOutBuffer(cl, ammount, true);\n\t\t\t\tmemcpy(cl->outbuffer, msg, ammount);\n\t\t\t\tcl->outbufferused = ammount;\n\t\t\t\tcl->mode = HTTP_SENDING;\n\t\t\t}\n\t\t}\n\n\t\tif (content)\n\t\t\tIWebFree(content);\n\t\tbreak;\n\n\tcase HTTP_SENDING:\n\t\tif (cl->outbufferused < 8192)\n\t\t{\n\t\t\tif (cl->file)\n\t\t\t{\n\t\t\t\tExpandOutBuffer(cl, 32768, true);\n\t\t\t\twanted = cl->outbuffersize - cl->outbufferused;\n\t\t\t\tammount = VFS_READ(cl->file, cl->outbuffer+cl->outbufferused, wanted);\n\n\t\t\t\tif (!ammount)\n\t\t\t\t{\n\t\t\t\t\tVFS_CLOSE(cl->file);\n\t\t\t\t\tcl->file = NULL;\n\n\t\t\t\t\tIWebPrintf(\"%s: Download complete\\n\", cl->peername);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tcl->outbufferused+=ammount;\n\t\t\t}\n\t\t}\n\n\t\tammount = send(cl->datasock, cl->outbuffer, cl->outbufferused, 0);\n\n\t\tif (ammount == -1)\n\t\t{\n\t\t\tlocalerrno = neterrno();\n\t\t\tif (localerrno != NET_EWOULDBLOCK)\n\t\t\t\treturn \"some error when sending\";\n\t\t}\n\t\telse if (ammount||!cl->outbufferused)\n\t\t{\n\t\t\tmemcpy(cl->outbuffer, cl->outbuffer+ammount, cl->outbufferused-ammount);\n\t\t\tcl->outbufferused -= ammount;\n\t\t\tif (!cl->outbufferused && !cl->file)\n\t\t\t{\n\t\t\t\tcl->modeswitched = true;\n\t\t\t\tcl->mode = HTTP_WAITINGFORREQUEST;\n\t\t\t\tif (cl->closeaftertransaction)\n\t\t\t\t\treturn \"file sent\";\n\t\t\t}\n\t\t}\n\t\telse\n\t\t\treturn \"peer prematurely closed connection\";\n\t\tbreak;\n\n/*\tcase HTTP_RECEIVING:\n\t\tsent = recv(cl->datasock, resource, ammount, 0);\n\t\tif (sent == -1)\n\t\t{\n\t\t\tif (qerrno != EWOULDBLOCK)\t//they closed on us. Assume end.\n\t\t\t{\n\t\t\t\tVFS_CLOSE(cl->file);\n\t\t\t\tcl->file = NULL;\n\t\t\t\tcl->close = true;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\t\tif (sent != 0)\n\t\t\tIWebFWrite(resource, 1, sent, cl->file);\n\t\tbreak;*/\n\tdefault:\n\t\treturn \"Unknown state\";\n\t}\n\treturn NULL;\n}\n\nvoid HTTP_RunExisting (void)\n{\n\tconst char *err;\n\tHTTP_active_connections_t **link, *cl;\n\n\tlink = &HTTP_ServerConnections;\n\tfor (link = &HTTP_ServerConnections; *link;)\n\t{\n\t\tcl = *link;\n\n\t\terr = HTTP_RunClient(cl);\n\t\tif (err)\n\t\t{\n\t\t\tIWebPrintf(\"%s: Closing connection: %s\\n\", cl->peername, err);\n\n\t\t\t*link = cl->next;\n\t\t\tclosesocket(cl->datasock);\n\t\t\tcl->datasock = INVALID_SOCKET;\n\t\t\tif (cl->inbuffer)\n\t\t\t\tIWebFree(cl->inbuffer);\n\t\t\tif (cl->outbuffer)\n\t\t\t\tIWebFree(cl->outbuffer);\n\t\t\tif (cl->file)\n\t\t\t\tVFS_CLOSE(cl->file);\n\t\t\tIWebFree(cl);\n\t\t\thttpconnectioncount--;\n\t\t\tcontinue;\n\t\t}\n\t\tlink = &(*link)->next;\n\t}\n}\n\n#ifndef _WIN32\nstatic void HTTP_ClientEPolled(struct epollctx_s *pubctx, unsigned int ev)\n{\n\tHTTP_active_connections_t *cl = (HTTP_active_connections_t *)pubctx;\n\tconst char *err = HTTP_RunClient(cl);\n\n\tif (err)\n\t{\n\t\tIWebPrintf(\"%s: Closing connection: %s\\n\", cl->peername, err);\n\n\t\t//should be automatic thanks to the closesocket, but do it just in case.\n\t\tepoll_ctl(cl->epfd, EPOLL_CTL_DEL, cl->datasock, NULL);\n\n\t\tclosesocket(cl->datasock);\n\t\tcl->datasock = INVALID_SOCKET;\n\t\tif (cl->inbuffer)\n\t\t\tIWebFree(cl->inbuffer);\n\t\tif (cl->outbuffer)\n\t\t\tIWebFree(cl->outbuffer);\n\t\tif (cl->file)\n\t\t\tVFS_CLOSE(cl->file);\n\t\tIWebFree(cl);\n\t\thttpconnectioncount--;\n\t}\n\telse\n\t{\n\t\tstruct epoll_event ev;\n\t\tswitch(cl->mode)\n\t\t{\n\t\tcase HTTP_WAITINGFORREQUEST:\n\t\t\tev.events = EPOLLIN;\n\t\t\tbreak;\n\t\tcase HTTP_SENDING:\n\t\t\tev.events = EPOLLOUT;\n\t\t\tbreak;\n\t\tdefault:\t//don't know, spam it.\n\t\t\tev.events = EPOLLIN|EPOLLOUT;\n\t\t\tbreak;\n\t\t}\n\t\tev.data.ptr = &cl->pub;\n\t\tepoll_ctl(cl->epfd, EPOLL_CTL_MOD, cl->datasock, &ev);\n\t}\n}\n#endif\n\nstatic qboolean HTTP_DoAccepts(int epfd)\n{\n\tstruct sockaddr_qstorage\tfrom;\n\tint fromlen = sizeof(from);\n\tHTTP_active_connections_t *cl;\n\tint _true = true;\n\tint clientsock = accept(httpserversocket, (struct sockaddr *)&from, &fromlen);\n\n\tif (clientsock < 0)\n\t\treturn false;\n\n\tif (ioctlsocket (clientsock, FIONBIO, (u_long *)&_true) == -1)\n\t{\n\t\tIWebPrintf (\"HTTP_ServerInit: ioctl FIONBIO: %s\\n\", strerror(neterrno()));\n\t\tclosesocket(clientsock);\n\t\treturn false;\n\t}\n\n\tcl = IWebMalloc(sizeof(HTTP_active_connections_t));\n\tNET_SockadrToString(cl->peername, sizeof(cl->peername), &from, sizeof(from));\n\tIWebPrintf(\"%s: New http connection\\n\", cl->peername);\n\n\tcl->datasock = clientsock;\n\n#ifndef _WIN32\n\tif (epfd >= 0)\n\t{\n\t\tstruct epoll_event ev;\n\t\tcl->pub.Polled = HTTP_ClientEPolled;\n\t\tcl->epfd = epfd;\n\t\tev.events = EPOLLIN|EPOLLOUT;\n\t\tev.data.ptr = &cl->pub;\n\t\tepoll_ctl(epfd, EPOLL_CTL_ADD, clientsock, &ev);\n\t}\n\telse\n#endif\n\t{\n\t\tcl->next = HTTP_ServerConnections;\n\t\tHTTP_ServerConnections = cl;\n\t}\n\thttpconnectioncount++;\n\treturn true;\n}\n\nqboolean HTTP_ServerPoll(qboolean httpserverwanted, int portnum)\t//loop while true\n{\n\tif (httpserverport != portnum && httpserverinitied)\n\t\tHTTP_ServerShutdown();\n\tif (!httpserverinitied)\n\t{\n\t\tif (httpserverwanted)\n\t\t\treturn HTTP_ServerInit(-1, portnum);\n\t\treturn false;\n\t}\n\telse if (!httpserverwanted)\n\t{\n\t\tHTTP_ServerShutdown();\n\t\treturn false;\n\t}\n\n\tchecknatpmp(httpserverport);\n\n\tif (httpconnectioncount>32)\n\t\treturn false;\n\n\tif (!HTTP_DoAccepts(-1))\n\t{\n\t\tint e = neterrno();\n\t\tif (e == NET_EWOULDBLOCK)\n\t\t{\n\t\t\tHTTP_RunExisting();\n\t\t\treturn false;\n\t\t}\n\n\t\tif (e == NET_ECONNABORTED || e == NET_ECONNRESET)\n\t\t{\n\t\t\tCon_TPrintf (\"Connection lost or aborted\\n\");\n\t\t\treturn false;\n\t\t}\n\n\n\t\tIWebPrintf (\"NET_GetPacket: %s\\n\", strerror(e));\n\t\treturn false;\n\t}\n\treturn true;\n}\n\n#endif\n"
  },
  {
    "path": "engine/http/iweb.h",
    "content": "#ifndef IWEB_H__\n#define IWEB_H__\n\nqboolean SV_AllowDownload (const char *name);\n\n#if defined(WEBSERVER) || defined(FTPSERVER)\n\n#ifdef WEBSVONLY\n//When running standalone\n#define Con_TPrintf IWebPrintf\nvoid VARGS IWebDPrintf(char *fmt, ...) LIKEPRINTF(1);\n#define IWebPrintf printf\n#define com_gamedir \".\"\t//current dir.\n\n\n#define IWebMalloc(x) calloc(x, 1)\n#define IWebRealloc(x, y) realloc(x, y)\n#define IWebFree free\n#else\n//Inside FTE\n#define IWebDPrintf\tCon_DPrintf\n#define IWebPrintf\tCon_Printf\n\n#define IWebMalloc\tZ_Malloc\n#define IWebRealloc\tBZF_Realloc\n#define IWebFree\tZ_Free\n\nvoid VARGS IWebWarnPrintf(char *fmt, ...) LIKEPRINTF(1);\n#endif\n\n#define IWEBACC_READ\t1\n#define IWEBACC_WRITE\t2\n#define IWEBACC_FULL\t4\nstruct sockaddr_in;\nstruct sockaddr;\nstruct sockaddr_qstorage;\nint NetadrToSockadr (netadr_t *a, struct sockaddr_qstorage *s);\n\ntypedef qboolean iwboolean;\n\ntypedef struct {\n\tfloat gentime;\t//useful for generating a new file (if too old, removes reference)\n\tint references;\t//freed if 0\n\tchar *data;\n\tint len;\n} IWeb_FileGen_t;\n\nint IWebAuthorize(const char *name, const char *password);\niwboolean IWebAllowUpLoad(const char *fname, const char *uname);\n\nvfsfile_t *IWebGenerateFile(const char *name, const char *content, int contentlength);\nint IWebGetSafeListeningPort(void);\n\n//char *COM_ParseOut (const char *data, char *out, int outlen);\n//struct searchpath_s;\n//void COM_EnumerateFiles (const char *match, int (*func)(const char *, int, void *, struct searchpath_s *), void *parm);\n\n\nchar *Q_strcpyline(char *out, const char *in, int maxlen);\n\n\n//server tick/control functions\niwboolean FTP_ServerRun(iwboolean ftpserverwanted, int port);\n\nqboolean HTTP_ServerInit(int epfd, int port);\n\n//server interface called from main server routines.\nvoid IWebInit(void);\nvoid IWebRun(void);\nvoid IWebShutdown(void);\n/*\nqboolean FTP_Client_Command (char *cmd, void (*NotifyFunction)(vfsfile_t *file, char *localfile, qboolean sucess));\nvoid IRC_Command(char *imsg);\nvoid FTP_ClientThink (void);\nvoid IRC_Frame(void);\n*/\nqboolean SV_POP3(qboolean activewanted);\nqboolean SV_SMTP(qboolean activewanted);\n\n#endif\n\n\n#if 1\nstruct dl_download\n{\n\t/*not used by anything in the download itself, useful for context*/\n\tunsigned int user_num;\n\tfloat user_float;\n\tvoid *user_ctx;\n\tint user_sequence;\n\n\tqboolean isquery;\t//will not be displayed in the download/progress bar stuff.\n\n#ifdef HAVE_CLIENT\n\tqdownload_t qdownload;\n#endif\n\n\t/*stream config*/\n\tchar *url;\t/*original url*/\n\tchar redir[MAX_OSPATH];\t/*current redirected url*/\n\tunsigned int redircount; /* so no infinite redirects with naughty servers.*/\n\tchar localname[MAX_OSPATH]; /*leave empty for a temp file*/\n\tenum fs_relative fsroot;\n\tstruct vfsfile_s *file;\t/*downloaded to, if not already set when starting will open localname or a temp file*/\n\n\tchar postmimetype[64];\n\tchar *postdata;\t\t\t/*if set, this is a post and not a get*/\n\tsize_t postlen;\n\n\t/*stream status*/\n\tenum\n\t{\n\t\tDL_PENDING,\t\t/*not started*/\n\t\tDL_FAILED,\t\t/*gave up*/\n\t\tDL_RESOLVING,\t/*resolving the host*/\n\t\tDL_QUERY,\t\t/*sent query, waiting for response*/\n\t\tDL_ACTIVE,\t\t/*receiving data*/\n\t\tDL_FINISHED\t\t/*its complete*/\n\t} status;\n\tunsigned int\treplycode;\n\tsize_t\t\t\ttotalsize;\t/*max size (can be 0 for unknown)*/\n\tsize_t\t\t\tcompleted; /*ammount correctly received so far*/\n\n\tsize_t\t\t\tsizelimit;\n\n\t/*internals*/\n#ifdef MULTITHREAD\n\tqboolean threadenable;\n\tvoid *threadctx;\n#endif\n\tvoid *ctx;\t/*internal context, depending on http/ftp/etc protocol*/\n\tvoid (*abort) (struct dl_download *); /*cleans up the internal context*/\n\tqboolean (*poll) (struct dl_download *);\n\n\t/*not used internally by the backend, but used by HTTP_CL_Get/thread wrapper*/\n\tstruct dl_download *next;\n\tqboolean (*notifystarted) (struct dl_download *dl, char *mimetype);\t//mime can be null for some protocols, read dl->totalsize for size. false if the mime just isn't acceptable.\n\tvoid (*notifycomplete) (struct dl_download *dl);\t//lets the requester know that the download context is complete and the handle is no longer valid.\n};\n\nvfsfile_t *VFSPIPE_Open(int refs, qboolean seekable);\t//refs should be 1 or 2, to say how many times it must be closed before its actually closed, so both ends can close separately\nvfsfile_t *VFS_OpenPipeCallback(void (*callback)(void*ctx, vfsfile_t *file), void *ctx);\nvoid HTTP_CL_Think(const char **fname, float *percent);\nvoid HTTP_CL_Terminate(void);\t//kills all active downloads\nunsigned int HTTP_CL_GetActiveDownloads(void);\nstruct dl_download *HTTP_CL_Get(const char *url, const char *localfile, void (*NotifyFunction)(struct dl_download *dl));\nstruct dl_download *HTTP_CL_Put(const char *url, const char *mime, const char *data, size_t datalen, void (*NotifyFunction)(struct dl_download *dl));\n\nstruct dl_download *DL_Create(const char *url);\nqboolean DL_CreateThread(struct dl_download *dl, vfsfile_t *file, void (*NotifyFunction)(struct dl_download *dl));\nvoid DL_Close(struct dl_download *dl);\nvoid DL_DeThread(void);\n\n//internal 'http' error codes.\n#define HTTP_DNSFAILURE\t900\t//no ip known\n#define HTTP_NORESPONSE\t901\t//tcp failure\n#define HTTP_REFUSED\t902\t//tcp failure\n#define HTTP_EOF\t\t903\t//unexpected eof\n#define HTTP_MITM\t\t904\t//wrong cert\n#define HTTP_UNTRUSTED\t905\t//unverifiable cert\n\n#endif\n\n#endif\n"
  },
  {
    "path": "engine/http/iwebiface.c",
    "content": "#include \"quakedef.h\"\n\n#if defined(WEBSERVER) || defined(FTPSERVER)\n\n#include \"iweb.h\"\n#include \"netinc.h\"\n\nqboolean HTTP_ServerPoll(qboolean httpserverwanted, int portnum);\n\n#ifdef WEBSVONLY\t//we need some functions from quake\n\nchar *NET_SockadrToString(char *s, int slen, struct sockaddr_qstorage *addr, size_t sizeofaddr)\n{\n\tswitch(((struct sockaddr*)addr)->sa_family)\n\t{\n\tcase AF_INET:\n\t\tQ_snprintfz(s, slen, \"%s:%u\", inet_ntoa(((struct sockaddr_in*)addr)->sin_addr), ntohs(((struct sockaddr_in*)addr)->sin_port));\n\t\tbreak;\n\tcase AF_INET6:\n\t\tif (!memcmp(((struct sockaddr_in6*)addr)->sin6_addr.s6_addr, \"\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\xff\\xff\", 12))\n\t\t{\t//ipv4-mapped\n\t\t\tQ_snprintfz(s, slen, \"[::ffff:%u.%u.%u.%u]:%u\", \n\t\t\t\t((struct sockaddr_in6*)addr)->sin6_addr.s6_addr[12],\n\t\t\t\t((struct sockaddr_in6*)addr)->sin6_addr.s6_addr[13],\n\t\t\t\t((struct sockaddr_in6*)addr)->sin6_addr.s6_addr[14],\n\t\t\t\t((struct sockaddr_in6*)addr)->sin6_addr.s6_addr[15],\n\n\t\t\t\tntohs(((struct sockaddr_in6*)addr)->sin6_port));\n\t\t}\n\t\telse\n\t\t{\n\t\t\tQ_snprintfz(s, slen, \"[%x:%x:%x:%x:%x:%x:%x:%x]:%u\", \n\t\t\t\tntohs(((unsigned short*)((struct sockaddr_in6*)addr)->sin6_addr.s6_addr)[0]),\n\t\t\t\tntohs(((unsigned short*)((struct sockaddr_in6*)addr)->sin6_addr.s6_addr)[1]),\n\t\t\t\tntohs(((unsigned short*)((struct sockaddr_in6*)addr)->sin6_addr.s6_addr)[2]),\n\t\t\t\tntohs(((unsigned short*)((struct sockaddr_in6*)addr)->sin6_addr.s6_addr)[3]),\n\n\t\t\t\tntohs(((unsigned short*)((struct sockaddr_in6*)addr)->sin6_addr.s6_addr)[4]),\n\t\t\t\tntohs(((unsigned short*)((struct sockaddr_in6*)addr)->sin6_addr.s6_addr)[5]),\n\t\t\t\tntohs(((unsigned short*)((struct sockaddr_in6*)addr)->sin6_addr.s6_addr)[6]),\n\t\t\t\tntohs(((unsigned short*)((struct sockaddr_in6*)addr)->sin6_addr.s6_addr)[7]),\n\n\t\t\t\tntohs(((struct sockaddr_in6*)addr)->sin6_port));\n\t\t}\n\t\tbreak;\n\tdefault:\n\t\t*s = 0;\n\t\tbreak;\n\t}\n\treturn s;\n}\n\nqboolean SV_AllowDownload (const char *name)\n{\n\tif (strstr(name, \"..\"))\n\t\treturn false;\n\tif (strchr(name, ':'))\n\t\treturn false;\n\tif (*name == '/' || *name == '\\\\')\n\t\treturn false;\n\treturn true;\n}\nchar\t\tcom_token[sizeof(com_token)];\ncom_tokentype_t com_tokentype;\nint\t\tcom_argc;\nconst char\t**com_argv;\n\nvfsfile_t *IWebGenerateFile(const char *name, const char *content, int contentlength)\n{\n\treturn NULL;\n}\nvfsfile_t *VFSSTDIO_Open(const char *osname, const char *mode, qboolean *needsflush);\nvfsfile_t *QDECL FS_OpenVFS(const char *filename, const char *mode, enum fs_relative relativeto)\n{\n\treturn VFSSTDIO_Open(filename, mode, NULL);\n}\n\n#include \"fs.h\"\nsearchpathfuncs_t *QDECL FSSTDIO_OpenPath(vfsfile_t *mustbenull, searchpathfuncs_t *parent, const char *filename, const char *desc, const char *prefix);\nstatic searchpath_t filesystem;\nint FS_FLocateFile(const char *filename, unsigned int lflags, flocation_t *loc)\n{\n\tif (!filesystem.handle)\n\t{\n\t\tfilesystem.handle = FSSTDIO_OpenPath(NULL, NULL, \".\", \".\", NULL);\n\t\tif (!filesystem.handle)\n\t\t{\n\t\t\tprintf(\"Filesystem unavailable\\n\");\n\t\t\treturn 0;\n\t\t}\n\t}\n\n\tif (filesystem.handle->FindFile(filesystem.handle, loc, filename, NULL))\n\t{\n\t\tloc->search = &filesystem;\n\t\treturn 1;\n\t}\n\treturn (lflags&FSLF_DEEPONFAILURE)?0x7fffffff:0;\n}\nqboolean FS_GetLocMTime(flocation_t *location, time_t *modtime)\n{\n\t*modtime = 0;\n\tif (!location->search->handle->FileStat || !location->search->handle->FileStat(location->search->handle, location, modtime))\n\t\treturn false;\n\treturn true;\n}\nstruct vfsfile_s *FS_OpenReadLocation(const char *fname, flocation_t *location)\n{\n\treturn location->search->handle->OpenVFS(location->search->handle, location, \"rb\");\n}\n\nvoid Q_strncpyz(char *d, const char *s, int n)\n{\n\tint i;\n\tn--;\n\tif (n < 0)\n\t\treturn;\t//this could be an error\n\n\tfor (i=0; *s; i++)\n\t{\n\t\tif (i == n)\n\t\t\tbreak;\n\t\t*d++ = *s++;\n\t}\n\t*d='\\0';\n}\n\nqboolean VARGS Q_vsnprintfz (char *dest, size_t size, const char *fmt, va_list argptr)\n{\n\tsize_t ret;\n#ifdef _WIN32\n\t//doesn't null terminate.\n\t//returns -1 on truncation\n\tret = _vsnprintf (dest, size, fmt, argptr);\n\tdest[size-1] = 0;\t//shitty paranoia\n#else\n\t//always null terminates.\n\t//returns length regardless of truncation.\n\tret = vsnprintf (dest, size, fmt, argptr);\n#endif\n#ifdef _DEBUG\n\tif (ret>=size)\n\t\tSys_Error(\"Q_vsnprintfz: Truncation\\n\");\n#endif\n\t//if ret is -1 (windows oversize, or general error) then it'll be treated as unsigned so really long. this makes the following check quite simple.\n\treturn ret>=size;\n}\nqboolean VARGS Q_snprintfz (char *dest, size_t size, const char *fmt, ...)\n{\n\tva_list\t\targptr;\n\tqboolean ret;\n\tva_start (argptr, fmt);\n\tret = Q_vsnprintfz(dest, size, fmt, argptr);\n\tva_end (argptr);\n\treturn ret;\n}\n\n/*char\t*va(char *format, ...)\n{\n#define VA_BUFFERS 2 //power of two\n\tva_list\t\targptr;\n\tstatic char\t\tstring[VA_BUFFERS][1024];\n\tstatic int bufnum;\n\n\tbufnum++;\n\tbufnum &= (VA_BUFFERS-1);\n\t\n\tva_start (argptr, format);\n\t_vsnprintf (string[bufnum],sizeof(string[bufnum])-1, format,argptr);\n\tva_end (argptr);\n\n\treturn string[bufnum];\t\n}*/\n\n#undef _vsnprintf\nvoid Sys_Error(const char *format, ...)\n{\n\tva_list\t\targptr;\n\tchar\t\tstring[1024];\n\n\tva_start (argptr, format);\n#ifdef _WIN32\n\t_vsnprintf (string,sizeof(string)-1, format,argptr);\n\tstring[sizeof(string)-1] = 0;\n#else\n\tvsnprintf (string,sizeof(string), format,argptr);\n#endif\n\tva_end (argptr);\n\n\tprintf(\"%s\", string);\n\tgetchar();\n\texit(1000);\n}\n\nint COM_CheckParm(const char *parm)\n{\n\treturn 0;\n}\n\n#ifndef _WIN32\n#include <signal.h>\n#endif\n\n#ifdef HAVE_EPOLL\nstruct stun_ctx\n{\n\tstruct epollctx_s pub;\n\tint udpsock;\n\tint reportport;\n\tint id[3];\n};\nstruct stunheader_s\n{\n\tunsigned short msgtype;\n\tunsigned short msglen;\n\tunsigned int magiccookie;\n\tunsigned int transactid[3];\n};\nstatic void StunResponse(struct epollctx_s *pubctx, unsigned int ev)\n{\n\tstruct stun_ctx *ctx = (struct stun_ctx*)pubctx;\n\tunsigned char buf[8192];\n\tint respsize = recvfrom(ctx->udpsock, buf, sizeof(buf), 0, NULL, NULL);\n\tint offset;\n\tstruct stunheader_s *h = (struct stunheader_s*)buf;\n\tif (h->transactid[0] != ctx->id[0] ||\n\t\th->transactid[1] != ctx->id[1] ||\n\t\th->transactid[2] != ctx->id[2])\n\t\treturn;\t//someone trying to spoof?\n\n\tif (((buf[0]<<8)|buf[1]) == 0x0101)\n\t{\n\t\tunsigned short attr;\n\t\tunsigned short sz;\n\t\toffset = sizeof(struct stunheader_s);\n\t\twhile (offset+4 < respsize)\n\t\t{\n\t\t\tattr = (buf[offset+0]<<8)|buf[offset+1];\n\t\t\tsz   = (buf[offset+2]<<8)|buf[offset+3];\n\t\t\toffset+= 4;\n\n\t\t\tif (offset + sz > respsize)\n\t\t\t\tbreak;\t//corrupt.\n\t\t\tif ((attr == 0x1 || attr == 0x20) && sz >= 4)\n\t\t\t{\n\t\t\t\tunsigned short type = (buf[offset+0]<<8)|buf[offset+1];\n\t\t\t\tunsigned short port = (buf[offset+2]<<8)|buf[offset+3];\n\t\t\t\tif (attr == 0x20)\n\t\t\t\t\tport ^= (buf[4]<<8)|buf[5];\n\t\t\t\tif (sz == 4+4 && type == 1)\n\t\t\t\t{\n\t\t\t\t\tprintf(\"Address: %s%i.%i.%i.%i:%u\\n\",\n\t\t\t\t\t\tctx->reportport?\"http://\":\"\",\n\t\t\t\t\t\tbuf[offset+4]^((attr == 0x20)?buf[4]:0),\n\t\t\t\t\t\tbuf[offset+5]^((attr == 0x20)?buf[5]:0),\n\t\t\t\t\t\tbuf[offset+6]^((attr == 0x20)?buf[6]:0),\n\t\t\t\t\t\tbuf[offset+7]^((attr == 0x20)?buf[7]:0),ctx->reportport?ctx->reportport:port\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t\toffset += sz;\n\t\t}\n\t}\n}\nvoid PrepareStun(int epfd, int reportport)\n{\n#if 0\n\tchar *stunserver = \"localhost\";\n\tint stunport = 27500;\n#else\t//sorry about hardcoding a server, but probably few people are gonna care enough.\n\tchar *stunserver = \"master.frag-net.com\";\n\tint stunport = 27950;\n#endif\n\n\tSOCKET newsocket;\n\tstruct sockaddr_in address;\n\tstruct hostent *h;\n\n\tstruct stunheader_s msg = {htons(1), 0, htonl(0x2112a442), {42,42,42}};\n\tif (epfd < 0)\n\t\treturn;\n\n\th = gethostbyname(stunserver);\n\tif (!h)\n\t\treturn;\n\n\tif (h->h_addrtype != AF_INET)\n\t\treturn;\t//too many assumptions\n\n\tif ((newsocket = socket (h->h_addrtype, SOCK_CLOEXEC|SOCK_DGRAM, IPPROTO_UDP)) == INVALID_SOCKET)\n\t\treturn;\n\n\taddress.sin_family = h->h_addrtype;\n\taddress.sin_addr.s_addr = INADDR_ANY;\n\taddress.sin_port = 0;\n\n\tif (bind (newsocket, (void *)&address, sizeof(address)) == -1)\n\t\treturn;\n\n\t//FIXME: not random enough to avoid hacks.\n\tsrand(time(NULL)^*(int*)&msg);\n\tmsg.transactid[0] = rand();\n\tmsg.transactid[1] = rand();\n\tmsg.transactid[2] = rand();\n\tif (epfd >= 0)\n\t{\n\t\tstatic struct stun_ctx ctx;\n\t\tstruct epoll_event ev;\n\t\tctx.udpsock = newsocket;\n\t\tctx.pub.Polled = StunResponse;\n\t\tctx.id[0] = msg.transactid[0];\n\t\tctx.id[1] = msg.transactid[1];\n\t\tctx.id[2] = msg.transactid[2];\n\t\tctx.reportport = reportport;\n\t\tev.events = EPOLLIN;\n\t\tev.data.ptr = &ctx;\n\t\tepoll_ctl(epfd, EPOLL_CTL_ADD, newsocket, &ev);\n\t}\n\n\tmsg.msglen = htons(sizeof(msg)-20);\n\tmemcpy(&address.sin_addr, h->h_addr, h->h_length);\n\taddress.sin_port = htons(stunport);\n\tsendto(newsocket, &msg, sizeof(msg), 0, (struct sockaddr*)&address, sizeof(address));\n}\n#endif\n\nchar *authedusername;\nchar *autheduserpassword;\nint lport_min, lport_max;\nint anonaccess = IWEBACC_READ;\niwboolean verbose;\nint main(int argc, char **argv)\n{\n\tint httpport = 80;\n\tint ftpport = 21;\n\tint arg = 1;\n#ifdef _WIN32\n\tWSADATA pointlesscrap;\n\tWSAStartup(2, &pointlesscrap);\n#else\n#ifdef HAVE_EPOLL\n\tint ep = epoll_create1(0);\n#endif\n\tsignal(SIGPIPE, SIG_IGN);\t//so we don't crash out if a peer closes the socket half way through.\n#endif\n\n\twhile (arg < argc)\n\t{\n\t\tchar *a = argv[arg];\n\t\tif (!a)\n\t\t\tcontinue;\n\t\tif (*a != '-')\n\t\t\tbreak;\t//other stuff\n\t\twhile (*a == '-')\n\t\t\ta++;\n\t\targ++;\n\n\t\tif (!strcmp(a, \"help\"))\n\t\t{\n\t\t\tprintf(\"%s -http 80 -ftp 21 -user steve -pass swordfish -ports 5000 6000\\n\", argv[0]);\n\t\t\tprintf(\"runs a simple http server\\n\");\n\t\t\tprintf(\"\t-http <num> specifies the port to listen on for http\\n\");\n\t\t\tprintf(\"\t-ftp <num> specifies the port to listen on for ftp\\n\");\n\t\t\tprintf(\"\t-ports <lowest> <highest> specifies a port range for incoming ftp connections, to work around firewall rules\\n\");\n\t\t\tprintf(\"\t-user <name> specifies the username that has full access. if not supplied noone can write.\\n\");\n\t\t\tprintf(\"\t-pass <pass> specifies the password to go with that username\\n\");\n\t\t\tprintf(\"\t-noanon will refuse to serve files to anyone but the authed user\\n\");\n\t\t\treturn 0;\n\t\t}\n\t\telse if (!strcmp(a, \"port\") || !strcmp(a, \"p\"))\n\t\t{\n\t\t\thttpport = atoi(argv[arg++]);\n\t\t\tftpport = 0;\n\t\t}\n\t\telse if (!strcmp(a, \"http\") || !strcmp(a, \"h\"))\n\t\t\thttpport = atoi(argv[arg++]);\n\t\telse if (!strcmp(a, \"ftp\") || !strcmp(a, \"f\"))\n\t\t\tftpport = atoi(argv[arg++]);\n\t\telse if (!strcmp(a, \"noanon\"))\n\t\t\tanonaccess = 0;\n\t\telse if (!strcmp(a, \"ports\"))\n\t\t{\n\t\t\tlport_min = atoi(argv[arg++]);\n\t\t\tlport_max = atoi(argv[arg++]);\n\t\t\tif (lport_max < lport_min)\n\t\t\t\tlport_max = lport_min;\n\t\t}\n\t\telse if (!strcmp(a, \"verbose\") || !strcmp(a, \"v\"))\n\t\t\tverbose = true;\n\t\telse if (!strcmp(a, \"user\"))\n\t\t\tauthedusername = argv[arg++];\n\t\telse if (!strcmp(a, \"pass\"))\n\t\t\tautheduserpassword = argv[arg++];\n\t\telse\n\t\t\tprintf(\"Unknown argument: %s\\n\", a);\n\t}\n\n\tif (arg < argc && atoi(argv[arg]))\n\t{\n\t\thttpport = atoi(argv[arg++]);\n\t\tftpport = 0;\n\t}\n\tif (arg < argc)\n\t\tauthedusername = argv[arg++];\n\tif (arg < argc)\n\t\tautheduserpassword = argv[arg++];\n\n\tif (httpport)\n\t\tprintf(\"http port %i\\n\", httpport);\n\telse\n\t\tprintf(\"http not enabled\\n\");\n\tif (ftpport)\n\t\tprintf(\"ftp port %i\\n\", ftpport);\n\telse\n\t\tprintf(\"ftp not enabled\\n\");\n\tif (authedusername || autheduserpassword)\n\t\tprintf(\"Username = \\\"%s\\\"\\nPassword = \\\"%s\\\"\\n\", authedusername, autheduserpassword);\n\telse\n\t\tprintf(\"Server is read only\\n\");\n\n#ifndef HAVE_EPOLL\n\twhile(1)\n\t{\n\t\tif (ftpport)\n\t\t\tFTP_ServerRun(1, ftpport);\n\t\tif (httpport)\n\t\t\tHTTP_ServerPoll(1, httpport);\n#ifdef _WIN32\n\t\tSleep(1);\n#else\n\t\tusleep(10000);\n#endif\n\t}\n#else\n\twhile (!HTTP_ServerInit(ep, httpport))\n\t\tsleep(5);\n\tPrepareStun(ep, httpport);\n\tfor (;;)\n\t{\n\t\tstruct epoll_event events[1];\n\t\tint e, me = epoll_wait(ep, events, countof(events), -1);\n\t\tfor (e = 0; e < me; e++)\n\t\t{\n\t\t\tstruct epollctx_s *ctx = events[e].data.ptr;\n\t\t\tctx->Polled(ctx, events[e].events);\n\t\t}\n\t}\n#endif\n}\n\nint IWebGetSafeListeningPort(void)\n{\n\tstatic int sequence;\n\treturn lport_min + (sequence++ % (lport_max+1-lport_min));\n}\nvoid VARGS IWebDPrintf(char *fmt, ...)\n{\n\tva_list args;\n\tif (!verbose)\n\t\treturn;\n\tva_start (args, fmt);\n\tvprintf (fmt, args);\n\tva_end (args);\n}\n\n\n#ifdef _WIN32\n#ifdef _MSC_VER\n#define ULL(x) x##ui64\n#else\n#define ULL(x) x##ull\n#endif\n\nstatic time_t Sys_FileTimeToTime(FILETIME ft)\n{\n\tULARGE_INTEGER ull;\n\tull.LowPart = ft.dwLowDateTime;\n\tull.HighPart = ft.dwHighDateTime;\n\treturn ull.QuadPart / ULL(10000000) - ULL(11644473600);\n}\nvoid COM_EnumerateFiles (const char *match, int (*func)(const char *, qofs_t, time_t mtime, void *, searchpathfuncs_t *f), void *parm)\n{\n\tHANDLE r;\n\tWIN32_FIND_DATAA fd;\t\n\tchar apath[MAX_OSPATH];\n\tchar file[MAX_OSPATH+MAX_PATH];\n\tchar *s;\n\tint go;\n\tstrcpy(apath, match);\n//\tsprintf(apath, \"%s%s\", gpath, match);\n\tfor (s = apath+strlen(apath)-1; s>= apath; s--)\n\t{\n\t\tif (*s == '/')\t\t\t\n\t\t\tbreak;\n\t}\n\ts++;\n\t*s = '\\0';\t\n\t\n\tstrcpy(file, match);\n\tr = FindFirstFileA(file, &fd);\n\tif (r==(HANDLE)-1)\n\t\treturn;\n\tgo = true;\n\tdo\n\t{\n\t\tif (*fd.cFileName == '.');\n\t\telse if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)\t//is a directory\n\t\t{\n\t\t\tsprintf(file, \"%s%s/\", apath, fd.cFileName);\n\t\t\tgo = func(file, fd.nFileSizeLow, Sys_FileTimeToTime(fd.ftLastWriteTime), parm, NULL);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tsprintf(file, \"%s%s\", apath, fd.cFileName);\n\t\t\tgo = func(file, fd.nFileSizeLow, Sys_FileTimeToTime(fd.ftLastWriteTime), parm, NULL);\n\t\t}\n\t}\n\twhile(FindNextFileA(r, &fd) && go);\n\tFindClose(r);\n}\n#else\nvoid COM_EnumerateFiles (const char *match, int (*func)(const char *, qofs_t, time_t mtime, void *, searchpathfuncs_t *f), void *parm)\n{\n\t//No implementation on unix etc\n}\n#endif\n\nchar *COM_ParseType (const char *data, char *out, size_t outlen, com_tokentype_t *toktype)\n{\n\tint\t\tc;\n\tint\t\tlen;\n\t\n\tlen = 0;\n\tout[0] = 0;\n\t\n\tif (!data)\n\t\treturn NULL;\n\t\t\n// skip whitespace\nskipwhite:\n\twhile ( (c = *data) <= ' ')\n\t{\n\t\tif (c == 0)\n\t\t\treturn NULL;\t\t\t// end of file;\n\t\tdata++;\n\t}\n\t\n// skip // comments\n\tif (c=='/')\n\t{\n\t\tif (data[1] == '/')\n\t\t{\n\t\t\twhile (*data && *data != '\\n')\n\t\t\t\tdata++;\n\t\t\tgoto skipwhite;\n\t\t}\n\t}\n\t\n\n// handle quoted strings specially\n\tif (c == '\\\"')\n\t{\n\t\tcom_tokentype = TTP_STRING;\n\t\tdata++;\n\t\twhile (1)\n\t\t{\n\t\t\tif (len >= outlen-1)\n\t\t\t\treturn (char*)data;\n\n\t\t\tc = *data++;\n\t\t\tif (c=='\\\"' || !c)\n\t\t\t{\n\t\t\t\tout[len] = 0;\n\t\t\t\treturn (char*)data;\n\t\t\t}\n\t\t\tout[len] = c;\n\t\t\tlen++;\n\t\t}\n\t}\n\n\tcom_tokentype = TTP_UNKNOWN;\n\n// parse a regular word\n\tdo\n\t{\n\t\tif (len >= outlen-1)\n\t\t\treturn (char*)data;\n\n\t\tout[len] = c;\n\t\tdata++;\n\t\tlen++;\n\t\tc = *data;\n\t} while (c>32);\n\t\n\tout[len] = 0;\n\treturn (char*)data;\n}\n\n/*#undef COM_ParseToken\nchar *COM_ParseToken (const char *data, const char *punctuation)\n{\n\tint\t\tc;\n\tint\t\tlen;\n\tlen = 0;\n\tcom_token[0] = 0;\n\t\n\tif (!data)\n\t\treturn NULL;\n\t\t\n// skip whitespace\nskipwhite:\n\twhile ( (c = *data) <= ' ')\n\t{\n\t\tif (c == 0)\n\t\t\treturn NULL;\t\t\t// end of file;\n\t\tdata++;\n\t}\n\t\n// skip // comments\n\tif (c=='/')\n\t{\n\t\tif (data[1] == '/')\n\t\t{\n\t\t\twhile (*data && *data != '\\n')\n\t\t\t\tdata++;\n\t\t\tgoto skipwhite;\n\t\t}\n\t\telse if (data[1] == '*')\n\t\t{\n\t\t\tdata+=2;\n\t\t\twhile (*data && (*data != '*' || data[1] != '/'))\n\t\t\t\tdata++;\n\t\t\tdata+=2;\n\t\t\tgoto skipwhite;\n\t\t}\n\t}\n\t\n\n// handle quoted strings specially\n\tif (c == '\\\"')\n\t{\n\t\tcom_tokentype = TTP_STRING;\n\t\tdata++;\n\t\twhile (1)\n\t\t{\n\t\t\tc = *data++;\n\t\t\tif (c=='\\\"' || !c)\n\t\t\t{\n\t\t\t\tcom_token[len] = 0;\n\t\t\t\treturn (char*)data;\n\t\t\t}\n\t\t\tcom_token[len] = c;\n\t\t\tlen++;\n\t\t}\n\t}\n\n\tcom_tokentype = TTP_UNKNOWN;\n\n// parse single characters\n\tif (c==',' || c=='{' || c=='}'|| c==')'|| c=='(' || c=='\\'' || c==':' || c==';' || c == '=' || c == '!' || c == '>' || c == '<' || c == '&' || c == '|' || c == '+')\n\t{\n\t\tcom_token[len] = c;\n\t\tlen++;\n\t\tcom_token[len] = 0;\n\t\treturn (char*)data+1;\n\t}\n\n// parse a regular word\n\tdo\n\t{\n\t\tcom_token[len] = c;\n\t\tdata++;\n\t\tlen++;\n\t\tc = *data;\n\t\tif (c==',' || c=='{' || c=='}'|| c==')'|| c=='(' || c=='\\'' || c==':' || c==';' || c == '=' || c == '!' || c == '>' || c == '<' || c == '&' || c == '|' || c == '+')\n\t\t\tbreak;\n\t} while (c>32);\n\t\n\tcom_token[len] = 0;\n\treturn (char*)data;\n}*/\n\n/*\nIWEBFILE *IWebFOpenRead(char *name)\t\t\t\t\t//fread(name, \"rb\");\n{\n\tFILE *f;\n\tchar name2[512];\n\tif (strstr(name, \"..\"))\n\t\treturn NULL;\n\tsprintf(name2, \"%s/%s\", com_gamedir, name);\n\tf = fopen(name2, \"rb\");\n\tif (f)\n\t{\n\t\tIWEBFILE *ret = IWebMalloc(sizeof(IWEBFILE));\n\t\tif (!ret)\n\t\t{\n\t\t\tfclose(f);\n\t\t\treturn NULL;\n\t\t}\n\t\tret->f = f;\n\t\tret->start = 0;\n\n\t\tfseek(f, 0, SEEK_END);\n\t\tret->end = ftell(f);//ret->start+ret->length;\n\t\tfseek(f, 0, SEEK_SET);\n\n\t\tret->length = ret->end - ret->start;\n\t\treturn ret;\n\t}\n\treturn NULL;\n}\n*/\n\n\n#else\n\n#if defined(WEBSERVER) || defined(FTPSERVER)\nstatic cvar_t sv_readlevel = CVAR(\"sv_readlevel\", \"0\");\t//default to allow anyone\nstatic cvar_t sv_writelevel = CVARD(\"sv_writelevel\", \"35\", \"Specifies the required trust level at which user accounts may write to the user-specific subdir of /uploads/USERNAME/*. If blank, then no uploads are permitted\");\t//allowed to write to uploads/uname\nstatic cvar_t sv_fulllevel = CVARD(\"sv_fulllevel\", \"51\", \"User accounts with an access level greater than this may write anywhere, including the gamedir. Note that setting this low is increadibly risky. An empty value will be understood to never give this permission.\");\t//allowed to write anywhere, replace any file...\n#ifdef WEBSERVER\nstatic cvar_t httpserver = CVAR(\"sv_http\", \"0\");\nstatic cvar_t httpserver_port = CVAR(\"sv_http_port\", \"80\");\n#endif\n#ifdef FTPSERVER\nstatic cvar_t ftpserver = CVAR(\"sv_ftp\", \"0\");\nstatic cvar_t ftpserver_port = CVAR(\"sv_ftp_port\", \"21\");\nstatic cvar_t sv_ftp_port_range = CVARD(\"sv_ftp_port_range\", \"0\", \"Specifies the port range for the server to create listening sockets for 'active' ftp connections, to work around NAT/firewall issues.\\nMost FTP clients should use passive connections, but there's still some holdouts like windows.\");\n\nint IWebGetSafeListeningPort(void)\n{\n\tchar *e;\n\tint base, range;\n\tstatic int sequence;\n\tif (!sv_ftp_port_range.string || !*sv_ftp_port_range.string)\n\t\treturn 0;\t//lets the OS pick.\n\tbase = strtol(sv_ftp_port_range.string, &e, 0);\n\twhile(*e == ' ')\n\t\te++;\n\tif (*e == '-')\n\t\te++;\n\twhile(*e == ' ')\n\t\te++;\n\trange = strtol(e, NULL, 0);\n\tif (range < base)\n\t\trange = base;\n\treturn base + (sequence++ % (range+1-base));\n}\n#endif\n#endif\n\n//this file contains functions called from each side.\n\nvoid VARGS IWebWarnPrintf(char *fmt, ...)\n{\n\tva_list\t\targptr;\n\tchar\t\tmsg[4096];\n\n\tva_start (argptr,fmt);\n\tvsnprintf (msg,sizeof(msg)-10, fmt,argptr);\t//catch any nasty bugs... (this is hopefully impossible)\n\tva_end (argptr);\n\n\tCon_Printf(CON_WARNING \"%s\", msg);\n}\n\nvoid IWebInit(void)\n{\n#ifdef WEBSERVER\n\tCvar_Register(&sv_fulllevel, \"Internet Server Access\");\n\tCvar_Register(&sv_writelevel, \"Internet Server Access\");\n\tCvar_Register(&sv_readlevel, \"Internet Server Access\");\n\n\tCvar_Register(&ftpserver, \"Internet Server Access\");\n\tCvar_Register(&ftpserver_port, \"Internet Server Access\");\n\tCvar_Register(&sv_ftp_port_range, \"Internet Server Access\");\n\tCvar_Register(&httpserver, \"Internet Server Access\");\n\tCvar_Register(&httpserver_port, \"Internet Server Access\");\n\n\t//don't allow these to be changed easily\n\t//this basically blocks these from rcon / stuffcmd\n\tftpserver.restriction = RESTRICT_MAX;\n\thttpserver.restriction = RESTRICT_MAX;\n\tsv_fulllevel.restriction = RESTRICT_MAX;\n\tsv_writelevel.restriction = RESTRICT_MAX;\n\tsv_readlevel.restriction = RESTRICT_MAX;\n#endif\n}\nvoid IWebRun(void)\n{\n#ifdef FTPSERVER\n\t{\n\t\textern qboolean ftpserverfailed;\n\t\tFTP_ServerRun(ftpserver.ival!= 0, ftpserver_port.ival);\n\t\tif (ftpserverfailed)\n\t\t{\n\t\t\tCon_Printf(\"FTP Server failed to load, setting %s to 0\\n\", ftpserver.name);\n\t\t\tCvar_SetValue(&ftpserver, 0);\n\t\t\tftpserverfailed = false;\n\t\t}\n\t}\n#endif\n\n#ifdef WEBSERVER\n\t{\n\t\textern qboolean httpserverfailed;\n\t\tHTTP_ServerPoll(httpserver.ival!=0, httpserver_port.ival);\n\t\tif (httpserverfailed)\n\t\t{\n\t\t\tCon_Printf(\"HTTP Server failed to load, setting %s to 0\\n\", httpserver.name);\n\t\t\tCvar_SetValue(&httpserver, 0);\n\t\t\thttpserverfailed = false;\n\t\t}\n\t}\n#endif\n}\nvoid IWebShutdown(void)\n{\n}\n#endif\n\n#ifdef WEBSVONLY\nvoid *Sys_CreateThread(char *name, int (*func)(void *), void *args, int priority, int stacksize)\n{\n\treturn NULL;\n}\nvoid Sys_WaitOnThread(void *thread)\n{\n}\nqboolean FS_Remove(const char *fname, enum fs_relative relativeto)\n{\n\treturn false;\n}\nqboolean FS_SystemPath(const char *fname, enum fs_relative relativeto, char *out, int outlen)\n{\n\tQ_strncpyz(out, fname, outlen);\n\tif (*out == '/' || strstr(out, \"..\"))\n\t{\n\t\t*out = 0;\n\t\treturn false;\n\t}\n\treturn strlen(fname) == strlen(out);\n}\nvoid FS_FlushFSHashWritten(const char *fname) {}\nvoid FS_FlushFSHashRemoved(const char *fname) {}\nqboolean FS_Rename(const char *oldf, const char *newf, enum fs_relative relativeto)\n{\n\treturn rename(oldf, newf) != -1;\n}\n#ifdef _WIN32\n#include <direct.h>\nvoid FS_CreatePath(const char *pname, enum fs_relative relativeto)\n{\n\t_mkdir(pname);\n}\nqboolean Sys_rmdir (const char *path)\n{\n\treturn _rmdir(path) != -1;\n}\n#else\n#include <unistd.h>\n#include <sys/stat.h>\nvoid FS_CreatePath(const char *pname, enum fs_relative relativeto)\n{\n\tmkdir(pname, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);\n}\nqboolean Sys_rmdir (const char *path)\n{\n\treturn rmdir(path) != -1;\n}\n#endif\n\n#endif\n\n\n\n\nint IWebAuthorize(const char *name, const char *password)\n{\n#ifdef WEBSVONLY\n\tif (authedusername)\n\t{\n\t\tif (!strcmp(name, authedusername))\n\t\t{\n\t\t\tif (!strcmp(password, autheduserpassword))\n\t\t\t\treturn IWEBACC_FULL;\n\t\t}\n\t}\n\n\t//if they tried giving some other username, don't give them any access (prevents them from reading actual user files).\n\tif (*name && stricmp(name, \"anonymous\"))\n\t\treturn 0;\n\treturn anonaccess;\n#else\n#ifndef CLIENTONLY\n\tint id = Rank_GetPlayerID(NULL, name, atoi(password), false, true);\n\trankinfo_t info;\n\tif (!id)\n\t{\n\t\tif (!sv_readlevel.value && (!*name || !stricmp(name, \"anonymous\")))\n\t\t\treturn IWEBACC_READ;\t//read only anywhere\n\t\treturn 0;\n\t}\n\n\tRank_GetPlayerInfo(id, &info);\n\n\tif (*sv_fulllevel.string && info.s.trustlevel >= sv_fulllevel.value)\n\t\treturn IWEBACC_READ\t| IWEBACC_WRITE | IWEBACC_FULL;\t//allowed to read and write anywhere to the quake filesystem\n\tif (*sv_writelevel.string && info.s.trustlevel >= sv_writelevel.value)\n\t\treturn IWEBACC_READ\t| IWEBACC_WRITE;\t//allowed to read anywhere write to specific places\n\tif (info.s.trustlevel >= sv_readlevel.value)\n\t\treturn IWEBACC_READ;\t//read only anywhere\n#endif\n\treturn 0;\n#endif\n}\n\niwboolean IWebAllowUpLoad(const char *fname, const char *uname)\t//called for partial write access\n{\n\tif (strstr(fname, \"..\"))\n\t\treturn false;\n\tif (!strncmp(fname, \"uploads/\", 8))\n\t{\n\t\tif (!strncmp(fname+8, uname, strlen(uname)))\n\t\t\tif (fname[8+strlen(uname)] == '/')\n\t\t\t\treturn true;\n\t}\n\treturn false;\n}\n\nchar *Q_strcpyline(char *out, const char *in, int maxlen)\n{\n\tchar *w = out;\n\twhile (*in && maxlen > 0)\n\t{\n\t\tif (*in == '\\r' || *in == '\\n')\n\t\t\tbreak;\n\t\t*w = *in;\n\t\tin++;\n\t\tw++;\n\t\tmaxlen--;\n\t}\n\t*w = '\\0';\n\treturn out;\n}\n\n#endif\n"
  },
  {
    "path": "engine/http/webgen.c",
    "content": "#include \"quakedef.h\"\n\n#if defined(WEBSERVER) || defined(FTPSERVER)\n\n#include \"iweb.h\"\n\n#if defined(CLIENTONLY) || !defined(WEBSERVER)\nvfsfile_t *IWebGenerateFile(const char *name, const char *content, int contentlength)\n{\n\treturn NULL;\n}\n#else\n\nstatic char lastrecordedmvd[MAX_QPATH];\n\nstatic IWeb_FileGen_t *IWeb_GenerationBuffer;\nstatic size_t IWeb_GenerationBufferTotal;\n/*\nstatic void IWeb_MoreGeneratedResize(size_t newsize)\n{\n\tIWeb_FileGen_t *ob;\n\n\tif (IWeb_GenerationBuffer && IWeb_GenerationBufferTotal >= newsize)\n\t\treturn;\t//already big enough\n\n\tob = IWeb_GenerationBuffer;\n\tIWeb_GenerationBuffer = BZ_Malloc(sizeof(IWeb_GenerationBuffer) + newsize);\n\tmemset(IWeb_GenerationBuffer, 0, sizeof(*IWeb_GenerationBuffer));\n\n\tIWeb_GenerationBuffer->data = (char *)(IWeb_GenerationBuffer+1);\n\tif (ob)\n\t{\n\t\tmemcpy(IWeb_GenerationBuffer->data, ob->data, ob->len);\n\t\tIWeb_GenerationBuffer->len = ob->len;\n\t\tIWebFree(ob);\n\t}\n\n\tIWeb_GenerationBufferTotal = newsize;\n}\n*/\nstatic void IWeb_Generate(const char *buf)\n{\n\tsize_t count = strlen(buf);\n\tif (!IWeb_GenerationBuffer || IWeb_GenerationBuffer->len + count >= IWeb_GenerationBufferTotal)\n\t{\n\t\tIWeb_FileGen_t *ob;\n\t\tsize_t newsize = IWeb_GenerationBufferTotal + count+(16*1024);\n\n\t\tif (newsize < IWeb_GenerationBufferTotal)\n\t\t{\n\t\t\tSys_Error(\"Integer overflow\\n\");\n\t\t\treturn;\t//should probably crash or something.\n\t\t}\n\n\t\tob = IWeb_GenerationBuffer;\n\t\tIWeb_GenerationBuffer = BZ_Malloc(sizeof(IWeb_GenerationBuffer) + newsize);\n\t\tmemset(IWeb_GenerationBuffer, 0, sizeof(*IWeb_GenerationBuffer));\n\n\t\tIWeb_GenerationBuffer->data = (char *)(IWeb_GenerationBuffer+1);\n\t\tif (ob)\n\t\t{\n\t\t\tmemcpy(IWeb_GenerationBuffer->data, ob->data, ob->len);\n\t\t\tIWeb_GenerationBuffer->len = ob->len;\n\t\t\tIWebFree(ob);\n\t\t}\n\n\t\tIWeb_GenerationBufferTotal = newsize;\n\t}\n\n\tmemcpy(&IWeb_GenerationBuffer->data[IWeb_GenerationBuffer->len], buf, count);\n\tIWeb_GenerationBuffer->len+=count;\n}\n\nint Rank_Enumerate (unsigned int first, unsigned int last, void (*callback) (const rankinfo_t *ri));\t//leader first.\n\n/*\nstatic void IWeb_ParseForm(char *info, int infolen, char *text)\n{\n\tchar *eq, *and;\n\tchar *token, *out;\n\t*info = '\\0';\n\tif (!text)\n\t\treturn;\n\twhile(*text)\n\t{\n\t\teq = strchr(text, '=');\n\t\tif (!eq)\n\t\t\tbreak;\n\t\t*eq = '\\0';\n\t\tand = strchr(eq+1, '&');\n\t\tif (and)\n\t\t\t*and = '\\0';\n\n\t\tfor (out = token = eq+1; *token;)\n\t\t{\n\t\t\tif (*token == '+')\n\t\t\t{\n\t\t\t\t*out++ = ' ';\n\t\t\t\ttoken++;\n\t\t\t}\n\t\t\telse if (*token == '%' && token[1] && token[2])\n\t\t\t{\n\t\t\t\tint c = 0;\n\n\t\t\t\tif (token[1] >= '0' && token[1] <= '9')\n\t\t\t\t{\n\t\t\t\t\tc += token[1] - '0';\n\t\t\t\t}\n\t\t\t\telse if (token[1] >= 'a' && token[1] <= 'f')\n\t\t\t\t{\n\t\t\t\t\tc += token[1] - 'a'+10;\n\t\t\t\t}\n\t\t\t\telse if (token[1] >= 'A' && token[1] <= 'F')\n\t\t\t\t{\n\t\t\t\t\tc += token[1] - 'A'+10;\n\t\t\t\t}\n\t\t\t\tc*=16;\n\t\t\t\tif (token[2] >= '0' && token[2] <= '9')\n\t\t\t\t{\n\t\t\t\t\tc += token[2] - '0';\n\t\t\t\t}\n\t\t\t\telse if (token[2] >= 'a' && token[2] <= 'f')\n\t\t\t\t{\n\t\t\t\t\tc += token[2] - 'a'+10;\n\t\t\t\t}\n\t\t\t\telse if (token[2] >= 'A' && token[2] <= 'F')\n\t\t\t\t{\n\t\t\t\t\tc += token[2] - 'A'+10;\n\t\t\t\t}\n\n\t\t\t\t*out++ = c;\n\t\t\t\ttoken+=3;\n\t\t\t}\n\t\t\telse\n\t\t\t\t*out++ = *token++;\n\t\t}\n\t\t*out = '\\0';\n\n\t\tInfo_SetValueForKey(info, text, eq+1, infolen);\n\t\tif (!and)\n\t\t\treturn;\n\t\ttext = and+1;\n\t}\n}\n\nstatic void IWeb_GenerateAdminFile(char *parms, char *content, int contentlength)\n{\n\textern char\toutputbuf[];\t//redirected buffer - always null termed.\n\tchar info[16384];\n\tchar *pwd;\n\tchar *cmd;\n\tchar *mark, *start;\n\n\textern cvar_t\trcon_password;\n\tIWeb_Generate(\"<HTML><HEAD><TITLE>FTEQWSV - admin</TITLE></HEAD><BODY>\");\n\tif (*rcon_password.string)\n\t{\n\t\tIWeb_ParseForm(info, sizeof(info), content);\n\t\tpwd = Info_ValueForKey(info, \"pwd\");\n\t\tcmd = Info_ValueForKey(info, \"cmd\");\n\n\t\tIWeb_Generate(\"<FORM action=\\\"admin.html\\\" method=\\\"post\\\">\");\n\t\tIWeb_Generate(\"<CENTER>\");\n\t\tIWeb_Generate(\"<input name=pwd value=\\\"\");\n\t\tif (!strcmp(rcon_password.string, pwd))\n\t\t\tIWeb_Generate(rcon_password.string);\n\t\tIWeb_Generate(\"\\\">\");\n\t\tIWeb_Generate(\"<BR>\");\n\t\tIWeb_Generate(\"<input name=cmd maxsize=255 size=40 value=\\\"\\\">\");\n\t\tIWeb_Generate(\"<BR>\");\n\t\tIWeb_Generate(\"<input type=submit value=\\\"Submit\\\" name=btn>\");\n\t\tIWeb_Generate(\"</CENTER>\");\n\t\tIWeb_Generate(\"</FORM>\");\n\n\t\tif (!strcmp(rcon_password.string, pwd))\n\t\t{\n\t\t\tCon_Printf(\"Web based rcon: %s\\n\", cmd);\n\t\t\tSV_BeginRedirect(RD_OBLIVION, host_client->language);\n\t\t\tCmd_ExecuteString(cmd, RESTRICT_RCON);\n\t\t\tfor (mark = start = outputbuf; *mark; mark++)\n\t\t\t{\n\t\t\t\tif (*mark == '\\n')\n\t\t\t\t{\n\t\t\t\t\t*mark = '\\0';\n\t\t\t\t\tIWeb_Generate(start);\n\t\t\t\t\tIWeb_Generate(\"<br>\");\n\t\t\t\t\tstart = mark+1;\n\t\t\t\t}\n\n\t\t\t}\n\t\t\tIWeb_Generate(start);\n\t\t\tSV_EndRedirect();\n\t\t}\n\t\telse if (*pwd)\n\t\t\tIWeb_Generate(\"Password is incorrect.\");\n\t}\n\telse\n\t\tIWeb_Generate(\"<H1>Remote administration is not enabled.<H2>\");\n\tIWeb_Generate(\"</BODY></HTML>\");\n}\n*/\n\nstatic void IWeb_GenerateRankingsFileCallback(const rankinfo_t *ri)\n{\n\tIWeb_Generate(\"<TR><TD ALIGN = \\\"center\\\">\");\n\tIWeb_Generate(ri->h.name);\n\tIWeb_Generate(\"</TD><TD ALIGN = \\\"center\\\">\");\n\tIWeb_Generate(va(\"%i\", ri->s.kills));\n\tIWeb_Generate(\"</TD><TD ALIGN = \\\"center\\\">\");\n\tIWeb_Generate(va(\"%i\", ri->s.deaths));\n\tIWeb_Generate(\"</TD>\");\n\tIWeb_Generate(\"</TR>\");\n}\n\nstatic void IWeb_GenerateRankingsFile (const char *parms, const char *content, int contentlength)\n{\n\tIWeb_Generate(\"<HTML><HEAD></HEAD><BODY>\");\n\n\tif (Rank_OpenRankings())\n\t{\n\t\tIWeb_Generate(\"<TABLE CELLSPACING=\\\"1\\\" CELLPADDING=\\\"1\\\">\");\n\t\tIWeb_Generate(\"<CAPTION>Players</CAPTION>\");\n\t\tif (Rank_Enumerate(atoi(parms), atoi(parms)+20, IWeb_GenerateRankingsFileCallback) == 20)\n\t\t{\n\t\t\tIWeb_Generate(\"</TABLE>\");\n\t\t\tif (atoi(parms) >= 20)\n\t\t\t\tIWeb_Generate(va(\"<A HREF=allplayers.html?%i>Less</A>\", atoi(parms)-20));\n\t\t\telse if (atoi(parms) > 0)\n\t\t\t\tIWeb_Generate(va(\"<A HREF=allplayers.html?%i>Less</A>\", 0));\n\t\t\tIWeb_Generate(va(\"<A HREF=allplayers.html?%i>More</A>\", atoi(parms)+20));\n\t\t}\n\t\telse\n\t\t{\n\t\t\tIWeb_Generate(\"</TABLE>\");\n\t\t\tif (atoi(parms) >= 20)\n\t\t\t\tIWeb_Generate(va(\"<A HREF=allplayers.html?%i>Less</A>\", atoi(parms)-20));\n\t\t\telse if (atoi(parms) > 0)\n\t\t\t\tIWeb_Generate(va(\"<A HREF=allplayers.html?%i>Less</A>\", 0));\n\t\t}\n\t}\n\telse\n\t\tIWeb_Generate(\"<H1>Rankings are disabled. Sorry.<H2>\");\n\tIWeb_Generate(\"</BODY></HTML>\");\n}\n\nstatic void IWeb_GenerateIndexFile (const char *parms, const char *content, int contentlength)\n{\n\textern cvar_t\trcon_password;\n\tchar *s, *o;\n\tchar key[128], value[128];\n\tint l;\n\tqboolean added;\n\tclient_t *cl;\n\tIWeb_Generate(\"<HTML><HEAD><TITLE>FTEQWSV</TITLE></HEAD><BODY>\");\n\tIWeb_Generate(\"<H1>\");\n\tIWeb_Generate(hostname.string);\n\tIWeb_Generate(\"</H1>\");\n\n\tIWeb_Generate(\"<A HREF=\\\"\"ENGINEWEBSITE\"\\\">Engine website</A><P>\");\n\n\tif (Rank_OpenRankings())\n\t\tIWeb_Generate(\"<A HREF=\\\"allplayers.html\\\">Click here to see ranked players.</A><P>\");\n\tif (*rcon_password.string)\n\t\tIWeb_Generate(\"<A HREF=\\\"admin.html\\\">Admin.</A><P>\");\n\n\ts = svs.info;\n\n\tIWeb_Generate(\"<TABLE CELLSPACING=\\\"1\\\" CELLPADDING=\\\"1\\\">\");\n\tIWeb_Generate(\"<CAPTION>Server Info</CAPTION>\");\n\tif (*s == '\\\\')\n\t\ts++;\n\twhile (*s)\n\t{\n\t\to = key;\n\t\twhile (*s && *s != '\\\\')\n\t\t\t*o++ = *s++;\n\n\t\tl = o - key;\n//\t\tif (l < 20)\n//\t\t{\n//\t\t\tmemset (o, ' ', 20-l);\n//\t\t\tkey[20] = 0;\n//\t\t}\n//\t\telse\n\t\t\t*o = 0;\n\n\t\tIWeb_Generate(\"<TR><TD ALIGN = \\\"center\\\">\");\n\t\tIWeb_Generate(key);\n\n\t\tif (!*s)\n\t\t{\n\t\t\tIWeb_Generate(\"MISSING VALUE\\n\");\n\t\t\treturn;\n\t\t}\n\n\t\to = value;\n\t\ts++;\n\t\twhile (*s && *s != '\\\\')\n\t\t\t*o++ = *s++;\n\t\t*o = 0;\n\n\t\tif (*s)\n\t\t\ts++;\n\n\t\tIWeb_Generate(\"</TD><TD ALIGN = \\\"center\\\">\");\n\t\tIWeb_Generate(value);\n\t\tIWeb_Generate(\"</TD></TR>\");\n\t}\n\n\tIWeb_Generate(\"</TABLE>\");\n\n\tIWeb_Generate(\"<P><TABLE CELLSPACING=\\\"1\\\" CELLPADDING=\\\"1\\\">\");\n\tIWeb_Generate(\"<CAPTION>Players</CAPTION>\");\n\tadded = false;\n\tfor (l = 0, cl = svs.clients; l < sv.allocated_client_slots; l++, cl++)\n\t{\n\t\tif (cl->state <= cs_zombie)\n\t\t\tcontinue;\n\n\t\tIWeb_Generate(\"<TR><TD ALIGN = \\\"center\\\">\");\n\t\tIWeb_Generate(cl->name);\n\t\tIWeb_Generate(\"</TD><TD ALIGN = \\\"center\\\">\");\n\t\tIWeb_Generate(va(\"%i\", cl->old_frags));\n\t\tIWeb_Generate(\"</TD></TR>\");\n\t\tadded = true;\n\t}\n\tif (!added)\n\t{\n\t\tIWeb_Generate(\"<TR><TD ALIGN = \\\"center\\\">\");\n\t\tIWeb_Generate(\"No players on server\");\n\t\tIWeb_Generate(\"</TD><TD ALIGN = \\\"center\\\">\");\n\t}\n\n\tIWeb_Generate(\"</TABLE>\");\n\n\tIWeb_Generate(\"</BODY></HTML>\");\n}\n\ntypedef struct {\n\tchar *name;\n\tvoid (*GenerationFunction) (const char *parms, const char *content, int contentlength);\n\tfloat lastgenerationtime;\n\tfloat oldbysecs;\n\tIWeb_FileGen_t *buffer;\n\tint genid;\n} IWebFile_t;\n\nstatic IWebFile_t IWebFiles[] = {\n\t{\"allplayers.html\", IWeb_GenerateRankingsFile},\n\t{\"index.html\", IWeb_GenerateIndexFile},\n//code is too flawed for this\t{\"admin.html\", IWeb_GenerateAdminFile}\n};\n\ntypedef struct {\n\tvfsfile_t funcs;\n\n\tIWeb_FileGen_t *buffer;\n\tint pos;\n} vfsgen_t;\n\nstatic int QDECL VFSGen_ReadBytes(vfsfile_t *f, void *buffer, int bytes)\n{\n\tvfsgen_t *g = (vfsgen_t*)f;\n\tif (bytes + g->pos >= g->buffer->len)\n\t{\n\t\tbytes = g->buffer->len - g->pos;\n\t\tif (bytes <= 0)\n\t\t\treturn 0;\n\t}\n\n\tmemcpy(buffer, g->buffer->data+g->pos, bytes);\n\tg->pos += bytes;\n\n\treturn bytes;\n}\n\nstatic int QDECL VFSGen_WriteBytes(vfsfile_t *f, const void *buffer, int bytes)\n{\n\tSys_Error(\"VFSGen_WriteBytes: Readonly\\n\");\n\treturn 0;\n}\n\nstatic qboolean QDECL VFSGen_Seek(vfsfile_t *f, qofs_t newpos)\n{\n\tvfsgen_t *g = (vfsgen_t*)f;\n\tif (newpos < 0 || newpos >= g->buffer->len)\n\t\treturn false;\n\n\tg->pos = newpos;\n\n\treturn true;\n}\n\nstatic qofs_t QDECL VFSGen_Tell(vfsfile_t *f)\n{\n\tvfsgen_t *g = (vfsgen_t*)f;\n\treturn g->pos;\n}\n\nstatic qofs_t QDECL VFSGen_GetLen(vfsfile_t *f)\n{\n\tvfsgen_t *g = (vfsgen_t*)f;\n\treturn g->buffer->len;\n}\n\nstatic qboolean QDECL VFSGen_Close(vfsfile_t *f)\n{\n\tint fnum;\n\tvfsgen_t *g = (vfsgen_t*)f;\n\tg->buffer->references--;\n\tif (!g->buffer->references)\n\t{\n\t\tZ_Free(g->buffer->data);\n\t\tZ_Free(g->buffer);\n\n\t\tfor (fnum = 0; fnum < sizeof(IWebFiles) / sizeof(IWebFile_t); fnum++)\n\t\t\tif (IWebFiles[fnum].buffer == g->buffer)\n\t\t\t\tIWebFiles[fnum].buffer = NULL;\n\t}\n\tZ_Free(g);\n\treturn true;\n}\n\n\nstatic vfsfile_t *VFSGen_Create(IWeb_FileGen_t *gen)\n{\n\tvfsgen_t *ret;\n\tret = Z_Malloc(sizeof(vfsgen_t));\n\n\tret->funcs.ReadBytes = VFSGen_ReadBytes;\n\tret->funcs.WriteBytes = VFSGen_WriteBytes;\n\tret->funcs.Seek = VFSGen_Seek;\n\tret->funcs.Tell = VFSGen_Tell;\n\tret->funcs.GetLen = VFSGen_GetLen;\n\tret->funcs.Close = VFSGen_Close;\n\n\tret->buffer = gen;\n\tgen->references++;\n\n\treturn (vfsfile_t*)ret;\n}\n\nvfsfile_t *IWebGenerateFile(const char *name, const char *content, int contentlength)\n{\n\tint fnum;\n\tconst char *parms;\n\tint len;\n\n\tif (!sv.state)\n\t\treturn NULL;\n\n\tif (*lastrecordedmvd && !strcmp(name, \"lastdemo.mvd\"))\n\t\tif (strcmp(name, \"lastdemo.mvd\"))\t//no infinate loops please...\n\t\t\treturn FS_OpenVFS(lastrecordedmvd, \"rb\", FS_GAME);\n\n\tparms = strchr(name, '?');\n\tif (!parms)\n\t\tparms = name + strlen(name);\n\tlen = parms-name;\n\tif (*parms)\n\t\tparms++;\n\n\tif (!*name)\n\t\treturn NULL;\n\n\n\tfor (fnum = 0; fnum < sizeof(IWebFiles) / sizeof(IWebFile_t); fnum++)\n\t{\n\t\tif (!Q_strncasecmp(name, IWebFiles[fnum].name, len+1))\n\t\t{\n\t\t\tif (IWebFiles[fnum].buffer)\n\t\t\t{\n\t\t\t\tif (IWebFiles[fnum].lastgenerationtime < Sys_DoubleTime())\n\t\t\t\t{\n\t\t\t\t\tIWebFiles[fnum].buffer->references--;\t\t//remove our reference and check free\n\t\t\t\t\tif (IWebFiles[fnum].buffer->references<=0)\n\t\t\t\t\t{\n\t\t\t\t\t\tBZ_Free(IWebFiles[fnum].buffer);\n\t\t\t\t\t\tIWebFiles[fnum].buffer = NULL;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!IWebFiles[fnum].buffer)\n\t\t\t{\n\t\t\t\tif (IWeb_GenerationBuffer!=NULL)\n\t\t\t\t\tSys_Error(\"Recursive file generation\\n\");\n\t\t\t\tIWebFiles[fnum].GenerationFunction(parms, content, contentlength);\n\t\t\t\tIWebFiles[fnum].buffer = IWeb_GenerationBuffer;\n\n\t\t\t\t//so it can't be sent once and freed instantly.\n\t\t\t\tif (contentlength)\n\t\t\t\t\tIWebFiles[fnum].lastgenerationtime = Sys_DoubleTime()+10;\n\t\t\t}\n\t\t\tIWebFiles[fnum].buffer->references++;\n\t\t\tIWeb_GenerationBuffer = NULL;\n\n\t\t\treturn VFSGen_Create(IWebFiles[fnum].buffer);\n\t\t}\n\t}\n\treturn NULL;\n}\n\n#endif\n#endif\n"
  },
  {
    "path": "engine/makeconfig.sh",
    "content": "#!/bin/bash\n\n#This is a fairly lame shell script\n#It could be written so much better...\n#Anyway, what it does is ask the user lots of questions and then pipes some text to a file which can be used by the engine.\n#the Makefile explicitally tests for config.h, and will pass the right precompiler to gcc so that this file is actually used.\n#And so we don't break in the absence of this file.\n\nif [ \"$1\" = \"y\" ]; then\n\tdefaulttoyes=true\n\techo \"Checking installed libraries\"\nelse\n\techo \"Answer the questions to generate a config.h file\"\n\techo \"If you wish to remove the config, delete it and recompile, make will sort stuff out\"\n\techo \"Many of these questions are irrelevent if you want to build only a dedicated server, for instance\"\n\techo \"Some of them depend on others\"\n\techo \"Usage of this script is not fully supported by the FTE team, and not every combination will likly work\"\n\techo \"If using this script does produce compile errors, you can try reporting the issue preferably via irc\"\nfi\n\n#clear out the config\necho \"//warning: generated file.\" > config.h\necho \"//Use 'make config' to alter this file\" >> config.h\necho \"//it is safe to delete this file if you want to use the default settings\" >> config.h\necho \"\" >> config.h\n\nquery()\n{\n\tif [ \"$defaulttoyes\" = \"true\" ]; then\n\t\tans=y\n\telse\n\t\tread -n 1 -p \"$1 \" ans\n\t\techo \"\"\n\tfi\n\tif [ \"$ans\" = \"y\" -o \"$ans\" = \"Y\" ]; then\n\t\techo \"#define $2\" >> config.h\n\telse\n\t\techo \"//#define $2\" >> config.h\n\tfi\n}\nquerylibrary()\n{\n\tif [ -f /usr/include/$3 ] ; then\n\t\tquery \"$1\" \"$2\"\n\t\treturn\n\tfi\n\tif [ -f /usr/local/include/$4 ] ; then\n\t\tquery \"$1\" \"$2\"\n\t\treturn\n\tfi\n#they don't have it, force no.\n\techo \"$1 n\"\n\techo \"//#define $2\" >> config.h\n}\n\nquerylibrary \"Is libz (zlib) available on this system (zip support)?\" AVAIL_ZLIB \"zlib.h\"\nquerylibrary \"Is libvorbis (a free media library) available on this system (ogg support) ?\" AVAIL_OGGVORBIS \"vorbis/vorbisfile.h\"\n# querylibrary \"Is libmad (an mp3 library) available on this system (mp3 support) ?\" AVAIL_MP3\nquerylibrary \"Is libpng available on this system (png support)?\" AVAIL_PNGLIB \"png.h\"\nquerylibrary \"Is libjpeg available on this system (jpeg support)?\" AVAIL_JPEGLIB \"jpeglib.h\"\nquery \"Do you want to enable the dds support ?\" DDS\nquery \"Do you want to enable on-server rankings?\" SVRANKING\nquery \"Do you want to enable stainmaps in software rendering?\" SWSTAINS\nquery \"Do you want to enable secondary/reverse views?\" \"SIDEVIEWS 4\"\nquery \"Do you want to enable quake2 sprites (sp2) ?\" SP2MODELS\nquery \"Do you want to enable quake2 models (md2) ?\" MD2MODELS\nquery \"Do you want to enable quake3arena models (md3) ?\" MD3MODELS\nquery \"Do you want to enable doom3 models (md5) ?\" MD5MODELS\nquery \"Do you want to enable 'zymotic' models (zym, used by nexuiz) ?\" ZYMOTICMODELS\nquery \"Do you want to enable basic halflife model support (mdl) ?\" HALFLIFEMODELS\nquery \"Do you want to enable network compression (huffman) ?\" HUFFNETWORK\n#query \"Do you want to enable doom wad, map and sprite support (best to say no here) ?\" DOOMWADS\nquery \"Do you want to enable quake2 map support ?\" Q2BSPS\nquery \"Do you want to enable quake3 map support ?\" Q3BSPS\nquery \"Do you want to enable fte's heightmap support ?\" TERRAIN\nquery \"Do you want to enable the built in master server ?\" SV_MASTER\nquery \"Do you want to enable the FTE_NPCCHAT qc extention ?\" SVCHAT\nquery \"Do you want to enable the quake2 server ?\" Q2SERVER\nquery \"Do you want to enable the quake2 client ?\" Q2CLIENT\nquery \"Do you want to enable the quake3 server ?\" Q3SERVER\nquery \"Do you want to enable the quake3 client ?\" Q3CLIENT\nquery \"Do you want to enable netquake compatability ?\" NQPROT\nquery \"Do you want to allow connections via tcp (for suppose3rd party firewalls) ?\" TCPCONNECT\nquery \"Do you want to enable fish-eye views (only in software) ?\" FISH\nquery \"Do you want to enable the built in http/ftp server ?\" WEBSERVER\nquery \"Do you want to enable the built in http/ftp clients ?\" WEBCLIENT\nquery \"Do you want to enable the deluxemap generation routine ?\" RUNTIMELIGHTING\n#query \"Do you want to enable the 'qterm' (this is a major security risk) ?\" QTERM\nquery \"Do you want to enable the server browser ?\" CL_MASTER\nquery \"Do you want to enable the serial-mouse support (used in splitscreen) ?\" SERIALMOUSE\nquery \"Do you want to enable the per-pixel lighting routines ?\" PPL\nquery \"Do you want to enable the text editor ?\" TEXTEDITOR\nquery \"Do you want to enable the plugin support ?\" PLUGINS\nquery \"Do you want to enable csqc support ?\" CSQC_DAT\nquery \"Do you want to enable menu.dat support (used by nexuiz) ?\" MENU_DAT\nquery \"Do you want to enable the built in irc client (note that there is also a plugin irc client, which cooler) ?\" IRCCLIENT\n\n\n\n\n\n\n\n\n\necho \"#define R_XFLIP\" >> config.h\necho \"#define IN_XFLIP\" >> config.h\n\n"
  },
  {
    "path": "engine/partcfgs/faithful.cfg",
    "content": "// faithful, by TimeServ\nr_part t_gib\n{\n\ttexture \"particles/quake\"\n\tstep 3\n\tscale 4\n\tdie 2\n\talphadelta 0\n\trandomvel 80\n\tveladd 100\n\tcolorindex 67 4\n\tgravity 40\n\tspawnorg 3\n\tstains 1\n}\n\nr_part t_zomgib\n{\n\ttexture \"particles/quake\"\n\tstep 6\n\tscale 4\n\tdie 2\n\talphadelta 0\n\trandomvel 72\n\tveladd 100\n\tcolorindex 67 4\n\tgravity 40\n\tspawnorg 3\n\tstains 1\n}\n\nr_part t_tracer3\n{\n\ttexture \"particles/quake\"\n\tstep 3\n\tscale 4\n\tdie 0.3\n\talphadelta 0\n\tcolorindex 152 4\n\tspawnorg 8\n}\n\nr_part t_tracer\n{\n\ttexture \"particles/quake\"\n\tstep 3\n\tscale 4\n\tdie 0.5\n\talphadelta 0\n\tcolorindex 52\n\tcitracer\n\tspawnvel 30 0\n\tspawnmode tracer\n}\n\nr_part t_tracer2\n{\n\ttexture \"particles/quake\"\n\tstep 3\n\tscale 4\n\tdie 0.5\n\talphadelta 0\n\tcolorindex 230\n\tcitracer\n\tspawnvel 30 0\n\tspawnmode tracer\n}\n\nr_part t_rocket\n{\n\ttexture \"particles/quake\"\n\tstep 3\n\tscale 4\n\tdie 1.2\n\tdiesubrand 0.6\n\trampmode absolute\n\trampindex 109 1.0\n\trampindex 107 0.833\n\trampindex 6 0.667\n\trampindex 5 0.5\n\trampindex 4 0.333\n\trampindex 3 0.167\n\tspawnorg 3\n\tgravity -40\n}\n\nr_part t_altrocket\n{\n\ttexture \"particles/quake\"\n\tstep 3\n\tscale 4\n\tdie 1.2\n\tdiesubrand 0.6\n\trampmode absolute\n\trampindexlist 109 107 6 5 4 3\n\tspawnorg 3\n\tgravity -40\n}\n\nr_part t_grenade\n{\n\ttexture \"particles/quake\"\n\tstep 3\n\tscale 4\n\tdie 0.8\n\tdiesubrand 0.6\n\trampmode absolute\n\trampindex 6 0.667\n\trampindex 5 0.5\n\trampindex 4 0.333\n\trampindex 3 0.167\n\tspawnorg 3\n\tgravity -40\n}\n\nr_part pe_size3\n{\n\ttexture \"particles/quake\"\n\tcount 1\n\tscale 4\n\tveladd 15\n\tdie 0.4\n\talphadelta 0\n\tdiesubrand 0.4\n\tgravity 40\n\tspawnorg 24\n}\n\nr_part pe_size2\n{\n\ttexture \"particles/quake\"\n\tcount 1\n\tscale 4\n\tveladd 15\n\tdie 0.4\n\talphadelta 0\n\tdiesubrand 0.4\n\tgravity 40\n\tspawnorg 16\n}\n\nr_part pe_default\n{\n\ttexture \"particles/quake\"\n\tcount 1\n\tscale 4\n\tveladd 15\n\tdie 0.4\n\talphadelta 0\n\tdiesubrand 0.4\n\tgravity 40\n\tspawnorg 8\n}\n\nr_part explode2\n{\n\ttexture \"particles/quake\"\n\tcount 512\n\tscale 4\n\talphadelta 0\n\tdie 0.5333\n\tdiesubrand 0.2667\n\trampmode absolute\n\trampindexlist 111 110 109 108 107 106 104 102 \n\trandomvel 256\n\tgravity 40\n\tfriction 1\n\tspawnorg 16\n}\n\nr_part te_explosion\n{\n\ttexture \"particles/quake\"\n\tcount 512\n\tscale 4\n\tdie 0.8\n\tdiesubrand 0.4\n\trandomvel 256\n\trampmode absolute\n\trampindexlist 111 109 107 105 103 101 99 97 \n\tgravity 40\n\tfriction -4\n\tspawnorg 16\n\tassoc explode2\n}\n\nr_part blobexp2b\n{\n\ttexture \"particles/quake\"\n\tcount 256\n\tscale 4\n\talphadelta 0\n\tdie 1.4\n\tcolorindex 150 6\n\tgravity 40\n\tfriction 4 0\n\tspawnorg 16\n\trandomvel 256\n}\nr_part blobexp1b\n{\n\ttexture \"particles/quake\"\n\tcount 256\n\tscale 4\n\talphadelta 0\n\tdie 1.4\n\tcolorindex 66 6\n\tgravity 40\n\tfriction -4 0\n\tspawnorg 16\n\trandomvel 256\n\tassoc blobexp2b\n}\n\nr_part blobexp2\n{\n\ttexture \"particles/quake\"\n\tcount 256\n\tscale 4\n\talphadelta 0\n\tdie 1\n\tcolorindex 150 6\n\tgravity 40\n\tfriction 4 0\n\tspawnorg 16\n\trandomvel 256\n\tassoc blobexp1b\n}\nr_part te_tarexplosion\n{\n\ttexture \"particles/quake\"\n\tcount 256\n\tscale 4\n\talphadelta 0\n\tdie 1\n\tcolorindex 66 6\n\tgravity 40\n\tfriction -4 0\n\trandomvel 256\n\tspawnorg 16\n\tassoc blobexp2\n}\n\nr_part te_teleportsplash\n{\n\ttexture \"particles/quake\"\n\tcount 896\n\tscale 4\n\talphadelta 0\n\tdie 0.34\n\tdiesubrand 0.14\n\tcolorindex 7 8\n\tgravity 40\n\tup 4\n\tspawnmode telebox\n\tspawnorg 16 28\n\tspawnvel 113\n}\n\nr_part te_lavasplash\n{\n\ttexture \"particles/quake\"\n\tcount 1024\n\tscale 4\n\talphadelta 0\n\tdie 2.62\n\tdiesubrand 0.62\n\tcolorindex 224 8\n\tgravity 40\n\tspawnorg 128 63\n\tspawnvel 113\n\tspawnmode lavasplash\n}\n\nr_part pe_defaulttrail\n{\n\ttexture \"particles/quake\"\n\tstep 3\n\tscale 4\n\tdie 0.6\n\tdiesubrand 0.6\n\tspawnorg 3\n\tgravity -40\n}\n\nr_part pe_pointfile\n{\n\ttexture \"particles/quake\"\n\tcount 1\n\tscale 4\n\tdie 30\n\talphadelta 0\n\trgb 255 255 0\n}\n\n"
  },
  {
    "path": "engine/partcfgs/generatebuiltin.c",
    "content": "//simple tool to read the particle configs and generate a header file for inclusion in the engine.\r\n#include <stdio.h>\r\n#include <stdlib.h>\r\n#include <string.h>\r\n\r\nstruct\r\n{\r\n\tchar filename[64];\r\n\tchar *ifdef;\r\n} effects[] =\r\n{\r\n\t{\"spikeset.cfg\"},\r\n\t{\"faithful.cfg\"},\r\n\t{\"highfps.cfg\"},\r\n\t{\"high.cfg\"},\r\n\t{\"minimal.cfg\"},\r\n\t{\"h2part.cfg\", \"#ifdef HEXEN2\\n\"},\r\n\t{\"q2part.cfg\", \"#ifdef Q2CLIENT\\n\"},\r\n\t{\"tsshaft.cfg\"},\r\n\t{\"\"}\r\n};\r\n\r\nint main(void)\r\n{\r\n\tFILE *c, *h, *s;\r\n\tchar line[1024], *f;\r\n\tint i, j;\r\n\tc = fopen(\"../client/r_partset.c\", \"wt\");\r\n\th = fopen(\"../client/r_partset.h\", \"wt\");\r\n\r\n\tif (!c || !h)\r\n\t{\r\n\t\tprintf(\"unable to open a file\\n\");\r\n\t\treturn EXIT_FAILURE;\r\n\t}\r\n\r\n\tfprintf(h, \"/*\\nWARNING: THIS FILE IS GENERATED BY '\"__FILE__\"'.\\nYOU SHOULD NOT EDIT THIS FILE BY HAND\\n*/\\n\\n\");\r\n\tfprintf(c, \"/*\\nWARNING: THIS FILE IS GENERATED BY '\"__FILE__\"'.\\nYOU SHOULD NOT EDIT THIS FILE BY HAND\\n*/\\n\\n\");\r\n\tfprintf(c, \"#include \\\"bothdefs.h\\\"\\n\");\r\n\tfprintf(c, \"#ifndef QUAKETC\\n\");\r\n\tfprintf(h, \"#ifndef QUAKETC\\n\");\r\n\tfprintf(c, \"#include \\\"r_partset.h\\\"\\n\\n\\n\");\r\n\r\n\tfor (i = 0; *effects[i].filename; i++)\r\n\t{\r\n\t\ts = fopen(effects[i].filename, \"rt\");\r\n\t\tif (!s)\r\n\t\t{\r\n\t\t\tprintf(\"unable to open %s\\n\", effects[i].filename);\r\n\t\t\treturn EXIT_FAILURE;\r\n\t\t}\r\n\t\t*strchr(effects[i].filename, '.') = 0;\r\n\r\n\t\tif (effects[i].ifdef)\r\n\t\t\tfprintf(h, \"%s\", effects[i].ifdef);\r\n\r\n\t\tfprintf(h, \"extern char *particle_set_%s;\\n\", effects[i].filename);\r\n\t\tfprintf(h, \"#define R_PARTSET_BUILTINS_%s {\\\"%s\\\", &particle_set_%s},\\n\", effects[i].filename, effects[i].filename, effects[i].filename);\r\n\t\tif (effects[i].ifdef)\r\n\t\t{\r\n\t\t\tfputs(\"#else\\n\", h);\r\n\t\t\tfprintf(h, \"#define R_PARTSET_BUILTINS_%s\\n\", effects[i].filename);\r\n\t\t\tfputs(\"#endif\\n\", h);\r\n\t\t}\r\n\r\n\t\tif (i)\r\n\t\t\tfprintf(c, \"\\n\\n\\n//////////////////////////////////////////////////////\\n\\n\\n\");\r\n\t\tif (effects[i].ifdef)\r\n\t\t\tfprintf(c, \"%s\", effects[i].ifdef);\r\n\t\tfprintf(c, \"char *particle_set_%s =\\n\", effects[i].filename);\r\n\t\twhile(fgets(line, sizeof(line), s))\r\n\t\t{\r\n\t\t\tj = 0;\r\n\t\t\twhile (line[j] == ' ' || line[j] == '\\t')\r\n\t\t\t\tj++;\r\n\t\t\tif ((line[j] == '/' && line[j+1] == '/') || line[j] == '\\r' || line[j] == '\\n')\r\n\t\t\t{\r\n\t\t\t\twhile (line[j])\r\n\t\t\t\t\tfputc(line[j++], c);\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tfputc('\\\"', c);\t\t\t\t\r\n\t\t\t\twhile (line[j] && line[j] != '\\r' && line[j] != '\\n')\r\n\t\t\t\t{\r\n\t\t\t\t\tif (line[j] == '\\t')\r\n\t\t\t\t\t\tfputc(' ', c);\r\n\t\t\t\t\telse if (line[j] == '\\\"')\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tfputc('\\\\', c);\r\n\t\t\t\t\t\tfputc(line[j], c);\r\n\t\t\t\t\t}\r\n\t\t\t\t\telse\r\n\t\t\t\t\t\tfputc(line[j], c);\r\n\t\t\t\t\tj++;\r\n\t\t\t\t}\r\n\t\t\t\tfputs(\"\\\\n\\\"\\n\", c);\r\n\t\t\t}\r\n\t\t}\r\n\t\tfputs(\";\\n\", c);\r\n\t\tif (effects[i].ifdef)\r\n\t\t\tfputs(\"#endif\\n\", c);\r\n\t\tfclose(s);\r\n\t}\r\n\r\n\tfputs(\"#define R_PARTSET_BUILTINS \", h);\r\n\tfor (i = 0; *effects[i].filename; i++)\r\n\t{\r\n\t\tfprintf(h, \"R_PARTSET_BUILTINS_%s \", effects[i].filename);\r\n\t}\r\n\tfputs(\"\\n\", h);\r\n\r\n\tfprintf(c, \"#endif\\n\");\r\n\tfprintf(h, \"#endif\\n\");\r\n\r\n\tfclose(h);\r\n\tfclose(c);\r\n\r\n\treturn EXIT_SUCCESS;\r\n}\r\n"
  },
  {
    "path": "engine/partcfgs/h2part.cfg",
    "content": "//hexen2-compatible particle config\r\n//for the purposes of faithfulness, I'm using uhexen2 (with gl_missile_glows etc set to 0) as a baseline.\r\n\r\n\r\n//the engine uses the h2part namespace for all hexen2 effects, thus ensuring that the builtin config is loaded.\r\n//specifying this explicitly means that the engine can find these effects properly even if this config is loaded via some name other than h2part.\r\n//this line doesn't affect weak/strong stuff, so r_particledesc will still override builtin ones.\r\nr_part namespace h2part\r\n\r\n//transparent sprites look stupid when alpha tested too. really this shouldn't be here, but its needed to override fps_preset stuff...\r\ngl_blendsprites 1\r\n\r\n//pe4 effect 255 is reused for the generic\r\n//move the vel to org and ignore the spawn velocity to mimic hexen2's particleexplosion\r\n//colour gets overriden\r\nr_part pe4_255\r\n{\r\n\ttexture \"particles/fteparticlefont.tga\"\r\n\ttcoords 1 1 63 63 256 2 64\r\n\tcount 1\r\n\tscale 4\r\n\talpha 0.6\r\n\tdie 0.5\r\n\trandomvel 256\r\n\tveladd 0\r\n\torgadd 1\r\n\trotationspeed 360\r\n\trotationstart 0 360\r\n\tgravity 200\r\n\tscalefactor 0.8\r\n}\r\n\r\n//icemace hitting a monster\r\nr_part pe2_14_145\r\n{\r\n\ttexture \"particles/fteparticlefont.tga\"\r\n\ttcoords 1 1 63 63 256 2 64\r\n\tcount 20\r\n\tscale 4\r\n\talpha 1\r\n\tdie 1\r\n\trandomvel 256\r\n\trgb 160 160 240\r\n\tveladd 0\r\n\torgadd 1\r\n\trotationspeed 360\r\n\trotationstart 0 360\r\n\tgravity 200\r\n\tscalefactor 0.8\r\n}\r\n\r\n//praevus flame summoning particles\r\nr_part pe2_7\t//_427\r\n{\r\n\ttexture \"particles/fteparticlefont.tga\"\r\n\ttcoords 1 1 63 63 256 2 64\r\n\tcount 1\r\n\tscale 4\r\n\talpha 3\r\n\tdie 2\r\n\trandomvel 0\r\n\tveladd 1\r\n\tspawnorg 8\r\n\tspawnvel 0\r\n\trotationspeed 360\r\n\trotationstart 0 360\r\n\tgravity 0\r\n\tscalefactor 0.8\r\n}\r\n\r\n//grav. identical to slowgrav. used for the necro's boneshard particle puffs\r\nr_part pe4_1\r\n{\r\n\ttexture \"particles/fteparticlefont.tga\"\r\n\ttcoords 1 1 63 63 256 2 64\r\n\tcount 1\r\n\tscale 6\r\n\talpha 1\r\n\tdie 1\r\n\trandomvel 0\r\n\tveladd 1\r\n\torgadd 0\r\n\tspawnorg 8\r\n\trotationspeed 360\r\n\trotationstart 0 360\r\n\tgravity 200\r\n\tscalefactor 0.3\r\n}\r\n//slowgrav, used for the assassin's grenade's trail, stupidly enough\r\nr_part pe4_3\r\n{\r\n\ttexture \"particles/fteparticlefont.tga\"\r\n\ttcoords 1 1 63 63 256 2 64\r\n\tcount 1\r\n\tscale 4\r\n\talpha 0.6\r\n\tdie 0.5\r\n\trandomvel 0\r\n\tveladd 1\r\n\torgadd 0\r\n\tspawnorg 8\r\n\trotationspeed 360\r\n\trotationstart 0 360\r\n\tgravity 200\r\n\tscalefactor 0.8\r\n}\r\n\r\n//pt_fastgrav. blood splatters (like in the assassin's tomed set staff when the monster is chained up).\r\nr_part pe4_2\r\n{\r\n\ttexture \"particles/fteparticlefont.tga\"\r\n\ttcoords 1 1 63 63 256 2 64\r\n\tcount 1\r\n\tscale 8\r\n\talpha 2\r\n\tdie 1\r\n\trandomvel 0\r\n\tveladd 2\r\n\torgadd 0\r\n\tspawnorg 8\r\n\trotationspeed 360\r\n\trotationstart 0 360\r\n\tgravity 800\r\n\tscalefactor 0.8\r\n}\r\n\r\n//the 'rocket trail' flag from quake was repurposed in hexen2 for spider gibs\r\nr_part tr_rocket\r\n{\r\n\ttexture \"particles/fteparticlefont.tga\"\r\n\ttcoords 1 1 63 63 256 2 64\r\n\tstep 2\r\n\tscale 4\r\n\talpha 0.6\r\n\tdie 1\r\n\trandomvel 64\r\n\tveladd 10\r\n\trotationspeed 90\r\n\trotationstart 0 360\r\n\trgb 16 160 16\r\n\trgbrand 16 64 16\r\n\tgravity 200\r\n\tscalefactor 0.8\r\n}\r\n\r\n//used for the meteor staff trail (projectile and gibs)\r\nr_part tr_grenade\r\n{\r\n\ttexture \"particles/fteparticlefont.tga\"\r\n\ttcoords 1 1 63 63 256 2 64\r\n\tstep 4\r\n\tscale 4\r\n\talpha 1\r\n\tdie 1\r\n\trandomvel 8\r\n\tveladd 10\r\n\tgravity -40\r\n\trotationspeed 360\r\n\trotationstart 0 360\r\n\trgb 16\r\n\trgbrand 48\r\n\trgbrandsync 1\r\n\tscalefactor 0.8\r\n}\r\n\r\n//used on ice chunks (paladin ice wand thing)\r\nr_part tr_ice\r\n{\r\n\ttexture \"particles/fteparticlefont.tga\"\r\n\ttcoords 1 1 63 63 256 2 64\r\n\tstep 4\r\n\tscale 4\r\n\talpha 0.6\r\n\tdie 1\r\n\trandomvel 64\r\n\tveladd 10\r\n\trotationspeed 360\r\n\trotationstart 0 360\r\n\trgb 160 160 240\r\n\trgbrand 0 0 0\r\n\tgravity 200\r\n\tscalefactor 0.8\r\n}\r\n\r\n//hexen2 uses the exact same effect for blood and slightblood, just slightblood is half as dense.\r\nr_part tr_slightblood\r\n{\r\n\ttexture \"particles/fteparticlefont.tga\"\r\n\ttcoords 1 1 63 63 256 2 64\r\n\tstep 6\r\n\tscale 4\r\n\talpha 0.6\r\n\tdie 1\r\n\trandomvel 64\r\n\tveladd 10\r\n\trotationspeed 360\r\n\trotationstart 0 360\r\n\trgb 240 0 0\r\n\trgbrand 0 0 0\r\n\tgravity 200\r\n\tscalefactor 0.8\r\n}\r\nr_part tr_blood\r\n{\r\n\ttexture \"particles/fteparticlefont.tga\"\r\n\ttcoords 1 1 63 63 256 2 64\r\n\tstep 3\r\n\tscale 4\r\n\talpha 0.6\r\n\tdie 1\r\n\trandomvel 64\r\n\tveladd 10\r\n\trotationspeed 360\r\n\trotationstart 0 360\r\n\trgb 240 0 0\r\n\trgbrand 0 0 0\r\n\tgravity 200\r\n\tscalefactor 0.8\r\n}\r\n\r\nr_part tr_bloodshot\r\n{\r\n\ttexture \"particles/fteparticlefont.tga\"\r\n\ttcoords 1 1 63 63 256 2 64\r\n\tstep 3\r\n\tscale 4\r\n\talpha 0.6\r\n\tdie 1\r\n\trandomvel 64\r\n\tveladd 10\r\n\trotationspeed 90\r\n\trotationstart 0 360\r\n\trgb 200 0 0\r\n\trgbdelta -180 0 0\r\n\trgbrand 50 0 0\r\n\tgravity 200\r\n\tscalefactor 0.8\r\n}\r\n\r\n//demoness acid projectile trails\r\nr_part tr_spiderblood\r\n{\r\n\ttexture \"particles/fteparticlefont.tga\"\r\n\ttcoords 1 1 63 63 256 2 64\r\n\tstep 4\r\n\tscale 4\r\n\talpha 0.6\r\n\tdie 1\r\n\trandomvel 64\r\n\tveladd 10\r\n\trotationspeed 90\r\n\trotationstart 0 360\r\n\trgb 16 160 16\r\n\trgbrand 0 0 0\r\n\tgravity 200\r\n\tscalefactor 0.4\r\n}\r\n\r\n//demoness acid projectile trails\r\nr_part tr_acidball\r\n{\r\n\ttexture \"particles/fteparticlefont.tga\"\r\n\ttcoords 1 1 63 63 256 2 64\r\n\tstep 4\r\n\tscale 4\r\n\talpha 0.6\r\n\tdie 1\r\n\trandomvel 64\r\n\tveladd 10\r\n\trotationspeed 90\r\n\trotationstart 0 360\r\n\trgb 16 160 16\r\n\trgbrand 0 0 0\r\n\tgravity 200\r\n\tscalefactor 0.4\r\n\tlighttime 0\r\n\tlightshadows 1\r\n\tlightradius 100 120\r\n\tlightrgb    0.50 1.00 0.25\r\n}\r\n\r\n//hydra spit. generally blackish\r\nr_part tr_spit\t\r\n{\r\n\ttexture \"particles/fteparticlefont.tga\"\r\n\ttcoords 1 1 63 63 256 2 64\r\n\tstep 3\r\n\tscale 4\r\n\talpha 1\r\n\tdie 1\r\n\trandomvel 5\r\n\tveladd 10\r\n\tup 2\r\n\trotationspeed 90\r\n\trotationstart 0 360\r\n\trgb 0 0 0\r\n\trgbrand 0 0 0\r\n\tgravity 0\r\n\tscalefactor 0.3\r\n\r\n\tlighttime 0\r\n\tlightshadows 1\r\n\tlightradius 100 120\r\n\tlightrgb    -2.00 -1.00 -0.25\r\n}\r\n//famine missiles\r\nr_part tr_spell\r\n{\r\n\ttexture \"particles/fteparticlefont.tga\"\r\n\ttcoords 1 1 63 63 256 2 64\r\n\tstep 4\r\n\tscale 4\r\n\talpha 1\r\n\tdie 1\r\n\trandomvel 16\r\n\tspawnorg 4\r\n\tspawnvel 2\r\n\tveladd 64\r\n\trotationspeed 90\r\n\trotationstart 0 360\r\n\trgb 200 32 32\r\n\trgbrand 0 0 0\r\n\tgravity 0\r\n\tscalefactor 0.3\r\n}\r\n//tomed barbarian weapon2 trail\r\nr_part tr_vorpmissile\r\n{\r\n\ttexture \"particles/fteparticlefont.tga\"\r\n\ttcoords 1 1 63 63 256 2 64\r\n\tstep 4\r\n\tscale 4\r\n\talpha 0.6\r\n\tdie 0.5\r\n\trandomvel 4\r\n\tspawnorg 32 4\r\n\tveladd 64\r\n\trotationspeed 90\r\n\trotationstart 0 360\r\n\trgb 128 128 128\r\n\trgbrand 0 0 0\r\n\tgravity 0\r\n\tscalefactor 0.8\r\n}\r\n//this fades out much faster than regular hexen2. also slightly flies forwards with the missile\r\nr_part tr_magicmissile\r\n{\r\n\ttexture \"particles/fteparticlefont.tga\"\r\n\ttcoords 1 1 63 63 256 2 64\r\n\tstep 4\r\n\tscale 4\r\n\talpha 1\r\n\tdie 0.5\r\n\trandomvel 64\r\n\tveladd -128\r\n\tspawnorg 8\r\n\trotationspeed 90\r\n\trotationstart 0 360\r\n\trgb 100 100 160\r\n\trgbrand 0 0 0\r\n\tgravity 200\r\n\tscalefactor 0.8\r\n}\r\nr_part tr_boneshard\r\n{\r\n\ttexture \"particles/fteparticlefont.tga\"\r\n\ttcoords 1 1 63 63 256 2 64\r\n\tstep 4\r\n\tscale 4\r\n\talpha 1\r\n\tdie 0.5\r\n\trandomvel 64\r\n\tveladd -128\r\n\tspawnorg 8\r\n\trotationspeed 90\r\n\trotationstart 0 360\r\n\trgb 200 180 85\r\n\trgbrand 0 0 0\r\n\tgravity 200\r\n\tscalefactor 0.8\r\n}\r\n\r\n//imp fireballs\r\nr_part tr_fireball\r\n{\r\n\ttexture \"particles/fteparticlefont.tga\"\r\n\ttcoords 97 97 191 191 256\r\n\tstep 1\r\n\tscale 12\r\n\talpha 0.4\r\n\tdie 0.5\r\n\trgb 255 127 100\r\n\trgbdelta -14 -300 -300\r\n\tblend add\r\n\tscalefactor 1\r\n\tscaledelta -15\r\n\tlighttime 0\r\n\tlightshadows 1\r\n\tlightradius 100 120\r\n\tlightrgb    2.00 1.00 0.25\r\n}\r\nr_part +tr_fireball\r\n{\r\n\ttexture \"particles/fteparticlefont.tga\"\r\n\ttcoords 97 97 191 191 256\r\n\tstep 5\r\n\tscale 30\r\n\talpha 0.2\r\n\tdie 0.75\r\n\t//diesubrand 10.25\r\n\trandomvel 0.2\r\n\trgb 5 5 5\r\n\t//rgbdelta -230 -45 -9\r\n\tgravity -15\r\n\tscalefactor 1\r\n\tscaledelta 20\r\n\tspawnvel 5\r\n}\r\n\r\n//assassin weapon4\r\nr_part tr_setstaff\r\n{\r\n\ttexture \"particles/fteparticlefont.tga\"\r\n\ttcoords 1 1 63 63 256 2 64\r\n\tstep 2\r\n\tscale 4\r\n\talpha 0.6\r\n\tdie 1\r\n\tspawnorg 3 5\r\n\trandomvel 3.5\r\n\tveladd 10\r\n\trotationspeed 90\r\n\trotationstart 0 360\r\n\trgb 220 200 100\t\r\n\trgbrand 0 0 0\r\n\tgravity 40\r\n\tscalefactor 0.8\r\n}\r\n//assassin weapon4's tomed projectile trail thing. barely visible in hexen2. framerate dependant. nasty. this effect is not faithful.\r\nr_part tr_scarab\r\n{\r\n\ttexture \"particles/fteparticlefont.tga\"\r\n\ttcoords 1 1 63 63 256 2 64\r\n\tstep 2\r\n\tscale 4\r\n\talpha 0.3\r\n\tdie 0.2\r\n\tspawnorg 1 2\r\n\trandomvel 1\r\n\tveladd 10\r\n\trotationspeed 90\r\n\trotationstart 0 360\r\n\trgb 220 200 100\t\r\n\trgbrand 0 0 0\r\n\tgravity 40\r\n\tscalefactor 0.8\r\n}\r\n\r\n\r\n//generic rain. rgb comes from the gamecode's palette index. blurgh. real men specify things precisely.\r\nr_part te_rain\r\n{\r\n\ttexture \"particles/fteparticlefont.tga\"\r\n\ttcoords 1 1 63 63 256 2 64\r\n\tcount 1 2 1\r\n\tscale 5\r\n\talpha 3\r\n\tdie 2\r\n\tspawnorg 64 64\r\n\tspawnvel 1\r\n\tveladd 0.5\r\n\trotationspeed 90\r\n\trotationstart 0 30\r\n\trgb 255 255 255\r\n\tscalefactor 0.8\r\n}\r\n\r\n\r\n//Hexen2 triggers various client-side sprite/model effects.\r\n//model term:\r\n//model MODELNAME framestart frameend framerate alpha traileffect\r\n//sprites will always use a fixed alpha (frames should shrink in size or whatever).\r\n//models will fade out gradually, but can be forced to a constant alpha if a negative alpha is used (will be fabsed as needed) if you have a decent animation.\r\n\r\nr_part ce_white_smoke_05\r\n{\r\n\tmodel models/whtsmk1.spr 0 0 20 0.5\r\n\tveladd 1\r\n}\r\nr_part ce_white_smoke_10\r\n{\r\n\tmodel models/whtsmk1.spr 0 0 10 0.5\r\n\tveladd 1\r\n}\r\nr_part ce_white_smoke_15\r\n{\r\n\tmodel models/whtsmk1.spr 0 0 6.666 0.5\r\n\tveladd 1\r\n}\r\nr_part ce_white_smoke_20\r\n{\r\n\tmodel models/whtsmk1.spr 0 0 5 0.5\r\n\tveladd 1\r\n}\r\nr_part ce_white_smoke_50\r\n{\r\n\tmodel models/whtsmk1.spr 0 0 2 0.5\r\n\tveladd 1\r\n}\r\n\r\nr_part ce_bluespark\r\n{\r\n\tmodel models/bspark.spr 0 0 20 1\r\n}\r\nr_part ce_yellowspark\r\n{\r\n\tmodel models/spark.spr 0 0 20 1\r\n}\r\nr_part ce_sm_circle_exp\r\n{\r\n\tmodel models/fcircle.spr 0 0 20 1\r\n}\r\nr_part ce_bg_circle_exp\r\n{\r\n\tmodel models/xplod29.spr 0 0 20 1\r\n}\r\nr_part ce_sm_white_flash\r\n{\r\n\tmodel models/sm_white.spr 0 0 20 1\r\n}\r\nr_part ce_white_flash\r\n{\r\n\tmodel models/gryspt.spr 0 0 20 0.4\r\n}\r\nr_part ce_yellowred_flash\r\n{\r\n\tmodel models/yr_flsh.spr 0 0 20 0.4\r\n}\r\nr_part ce_blue_flash\r\n{\r\n\tmodel models/bluflash.spr 0 0 20 0.4\r\n}\r\nr_part ce_sm_blue_flash\r\n{\r\n\tmodel models/sm_blue.spr 0 0 20 0.4\r\n}\r\nr_part ce_red_flash\r\n{\r\n\tmodel models/redspt.spr 0 0 20 0.4\r\n}\r\nr_part ce_sm_explosion\r\n{\r\n\tmodel models/sm_expld.spr 0 0 20 1\r\n}\r\nr_part ce_lg_explosion\r\n{\r\n\tmodel models/bg_expld.spr 0 0 20 1\r\n}\r\nr_part ce_floor_explosion\r\n{\r\n\tmodel models/fl_expld.spr 0 0 20 1\r\n}\r\nr_part ce_rider_death\r\n{\r\n}\r\nr_part ce_blue_explosion\r\n{\r\n\tmodel models/xpspblue.spr 0 0 20 1\r\n}\r\nr_part ce_green_smoke_05\r\n{\r\n\tmodel models/grnsmk1.spr 0 0 20 0.5\r\n\tveladd 1\r\n}\r\nr_part ce_green_smoke_10\r\n{\r\n\tmodel models/grnsmk1.spr 0 0 10 0.5\r\n\tveladd 1\r\n}\r\nr_part ce_green_smoke_15\r\n{\r\n\tmodel models/grnsmk1.spr 0 0 6.666 0.5\r\n\tveladd 1\r\n}\r\nr_part ce_green_smoke_20\r\n{\r\n\tmodel models/grnsmk1.spr 0 0 5 0.5\r\n\tveladd 1\r\n}\r\n//\tce_grey_smoke\r\nr_part ce_grey_smoke_15\r\n{\r\n\tmodel models/grysmk1.spr 0 0 6.666 0.5\r\n\tveladd 1\r\n}\r\nr_part ce_red_smoke\r\n{\r\n\tmodel models/redsmk1.spr 0 0 6.666 0.5\r\n\tveladd 1\r\n}\r\nr_part ce_slow_white_smoke\r\n{\r\n\tmodel models/whtsmk1.spr 0 0 20 0.5\r\n\tveladd 1\r\n}\r\nr_part ce_redspark\r\n{\r\n\tmodel models/rspark.spr 0 0 20 1\r\n}\r\nr_part ce_greenspark\r\n{\r\n\tmodel models/gspark.spr 0 0 20 1\r\n}\r\nr_part ce_telesmk1\r\n{\r\n\tmodel models/telesmk1.spr 0 0 15 0.5\r\n\tveladd 1\r\n}\r\nr_part ce_telesmk2\r\n{\r\n\tmodel models/telesmk2.spr 0 0 15 1\r\n\tveladd 1\r\n}\r\nr_part ce_icehit\r\n{\r\n\tmodel models/icehit.spr 0 0 20 0.5\r\n}\r\nr_part ce_medusa_hit\r\n{\r\n\tmodel models/medhit.spr 0 0 20 1\r\n}\r\nr_part ce_mezzo_reflect\r\n{\r\n\tmodel models/mezzoref.spr 0 0 20 1\r\n}\r\nr_part ce_floor_explosion2\r\n{\r\n\tmodel models/flrexpl2.spr 0 0 20 1\r\n}\r\nr_part ce_xbow_explosion\r\n{\r\n\tmodel models/xbowexpl.spr 0 0 20 1\r\n}\r\nr_part ce_new_explosion\r\n{\r\n\tmodel models/gen_expl.spr 0 0 20 1\r\n}\r\nr_part ce_magic_missile_explosion\r\n{\r\n\tmodel models/mm_expld.spr 0 0 20 1\r\n}\r\n//\tce_ghost\r\nr_part ce_bone_explosion\r\n{\r\n\tmodel models/bonexpld.spr 0 0 20 1\r\n}\r\n//famine teleport effect\r\nr_part ce_redcloud\r\n{\r\n\tmodel models/rcloud.spr 0 0 20 1\r\n}\r\nr_part ce_teleporterpuffs\r\n{\r\n//\tmodel models/telesmk2.spr 0 0 20 1\r\n}\r\n//\tce_teleporterbody\r\n//\tce_boneshard\r\n//\tce_boneshrapnel\r\n//this is transparent so it doesn't obscure your view\r\nr_part ce_flamestream\r\n{\r\n\tmodel models/flamestr.spr 0 0 20 0.4\r\n\tveladd 1\r\n}\r\nr_part ce_gravitywell\r\n{\r\n\tspawnmode ball\r\n\tcount 100\r\n\tspawnorg 128\r\n\tspawnvel -64\r\n\ttexture \"particles/fteparticlefont.tga\"\r\n\ttcoords 1 1 63 63 256 2 64\r\n\tscale 4\r\n\talpha 1\r\n\tdie 2\r\n\trotationspeed 90\r\n\trotationstart 0 360\r\n\trgb 220 200 100\r\n\trgbrand 0 0 0\r\n\tgravity 0\r\n\tscalefactor 0.4\r\n}\r\nr_part ce_bldrn_expl\r\n{\r\n\tmodel models/xplsn_1.spr 0 0 20 1\r\n}\r\n//demoness tomed acid trail\r\nr_part ce_acid_muzzfl\r\n{\r\n\tmodel models/muzzle1.spr 0 0 20 0.4\r\n\tveladd 1\r\n}\r\nr_part ce_acid_hit\r\n{\r\n\tmodel models/axplsn_2.spr 0 0 20 1\r\n}\r\nr_part ce_firewall_small\r\n{\r\n\tmodel models/firewal1.spr 0 0 20 1\r\n}\r\nr_part ce_firewall_medium\r\n{\r\n\tmodel models/firewal5.spr 0 0 20 1\r\n}\r\nr_part ce_firewall_large\r\n{\r\n\tmodel models/firewal4.spr 0 0 20 1\r\n}\r\nr_part ce_onfire\r\n{\r\n\tmodel models/firewal1.spr 0 0 20 0.4\r\n\tmodel models/firewal2.spr 0 0 20 0.4\r\n\tmodel models/firewal3.spr 0 0 20 0.4\r\n\tveladd 1\r\n}\r\nr_part ce_flamewall\r\n{\r\n\tmodel models/firewal1.spr 0 0 20 1\r\n\tveladd 1\r\n}\r\nr_part ce_flamewall2\r\n{\r\n\tmodel models/firewal2.spr 0 0 20 0.4\r\n\tveladd 1\r\n}\r\nr_part ce_lball_expl\r\n{\r\n\tmodel models/Bluexp3.spr 0 0 20 1\r\n}\r\nr_part ce_acid_splat\r\n{\r\n\tmodel models/axplsn_1.spr 0 0 20 1\r\n}\r\nr_part ce_acid_expl\r\n{\r\n\tmodel models/axplsn_5.spr 0 0 20 1\r\n}\r\nr_part ce_fboom\r\n{\r\n\tmodel models/fboom.spr 0 0 20 1\r\n}\r\n//\tce_chunk\r\nr_part ce_bomb\r\n{\r\n\tmodel models/pow.spr 0 0 20 1\r\n}\r\nr_part ce_brn_bounce\r\n{\r\n\tmodel models/spark.spr 0 0 20 1\r\n}\r\nr_part ce_lshock\r\n{\r\n\tmodel models/vorpshok.mdl 0 0 20 1\r\n}\r\n//\tce_flamewall\r\n//\tce_flamewall2\r\nr_part ce_floor_explosion3\r\n{\r\n\tmodel models/biggy.spr 0 0 20 1\r\n}\r\n\r\nr_part ce_boneshard\r\n{\r\n\tmodel models/boneshot.mdl 0 1 1 1\r\n\trotationspeed 425\r\n\tveladd 2\r\n}\r\nr_part ce_boneshrapnel\r\n{\r\n\tmodel models/boneshrd.mdl 0 1 1 1\r\n\trotationspeed 425\r\n\tveladd 2\r\n}\r\n\r\nr_part ce_chunk_greystone\r\n{\r\n\tmodel models/schunk1.mdl 0 1 0.25 1\r\n\tmodel models/schunk2.mdl 0 1 0.25 1\r\n\tmodel models/schunk3.mdl 0 1 0.25 1\r\n\tmodel models/schunk4.mdl 0 1 0.25 1\r\n\trandomvel 210 70 280\r\n\tspawnorg 0\r\n\tgravity 800\r\n\trotationspeed 425\r\n}\r\nr_part ce_chunk_wood\r\n{\r\n\tmodel models/splnter1.mdl 0 1 0.25 1\r\n\tmodel models/splnter2.mdl 0 1 0.25 1\r\n\tmodel models/splnter3.mdl 0 1 0.25 1\r\n\tmodel models/splnter4.mdl 0 1 0.25 1\r\n\trandomvel 210 70 280\r\n\tspawnorg 0\r\n\tgravity 800\r\n\trotationspeed 425\r\n}\r\nr_part ce_chunk_metal\r\n{\r\n\tmodel models/metlchk1.mdl 0 1 0.25 1\r\n\tmodel models/metlchk2.mdl 0 1 0.25 1\r\n\tmodel models/metlchk3.mdl 0 1 0.25 1\r\n\tmodel models/metlchk4.mdl 0 1 0.25 1\r\n\trandomvel 210 70 280\r\n\tspawnorg 0\r\n\tgravity 800\r\n\trotationspeed 425\r\n}\r\nr_part ce_chunk_flesh\r\n{\r\n\tmodel models/flesh1.mdl 0 1 0.25 1 tr_bloodshot\r\n\tmodel models/flesh2.mdl 0 1 0.25 1 tr_bloodshot\r\n\tmodel models/flesh3.mdl 0 1 0.25 1 tr_bloodshot\r\n\trandomvel 210 70 280\r\n\tspawnorg 0\r\n\tgravity 800\r\n\trotationspeed 425\r\n}\r\n//r_part ce_chunk_fire\r\n//{\r\n//}\r\nr_part ce_chunk_clay\r\n{\r\n\tmodel models/clshard1.mdl 0 1 0.25 1\r\n\tmodel models/clshard2.mdl 0 1 0.25 1\r\n\tmodel models/clshard3.mdl 0 1 0.25 1\r\n\tmodel models/clshard4.mdl 0 1 0.25 1\r\n\trandomvel 210 70 280\r\n\tspawnorg 0\r\n\tgravity 800\r\n\trotationspeed 425\r\n}\r\nr_part ce_chunk_leaves\r\n{\r\n\tmodel models/leafchk1.mdl 0 1 0.25 1\r\n\tmodel models/leafchk2.mdl 0 1 0.25 1\r\n\tmodel models/leafchk3.mdl 0 1 0.25 1\r\n\trandomvel 210 70 280\r\n\tspawnorg 0\r\n\tgravity 800\r\n\trotationspeed 425\r\n}\r\nr_part ce_chunk_hay\r\n{\r\n\tmodel models/hay1.mdl 0 1 0.25 1\r\n\tmodel models/hay2.mdl 0 1 0.25 1\r\n\tmodel models/hay3.mdl 0 1 0.25 1\r\n\trandomvel 210 70 280\r\n\tspawnorg 0\r\n\tgravity 800\r\n\trotationspeed 425\r\n}\r\nr_part ce_chunk_brownstone\r\n{\r\n\tmodel models/schunk1.mdl 1 1 0.25 1\r\n\tmodel models/schunk2.mdl 1 1 0.25 1\r\n\tmodel models/schunk3.mdl 1 1 0.25 1\r\n\tmodel models/schunk4.mdl 1 1 0.25 1\r\n\trandomvel 210 70 280\r\n\tspawnorg 0\r\n\tgravity 800\r\n\trotationspeed 425\r\n}\r\nr_part ce_chunk_cloth\r\n{\r\n\tmodel models/clthchk1.mdl 0 1 0.25 1\r\n\tmodel models/clthchk2.mdl 0 1 0.25 1\r\n\tmodel models/clthchk3.mdl 0 1 0.25 1\r\n\trandomvel 210 70 280\r\n\tspawnorg 0\r\n\tgravity 800\r\n\trotationspeed 425\r\n}\r\nr_part ce_chunk_wood_leaf\r\n{\r\n\tmodel models/splnter1.mdl 0 1 0.25 1\r\n\tmodel models/splnter2.mdl 0 1 0.25 1\r\n\tmodel models/splnter3.mdl 0 1 0.25 1\r\n\tmodel models/splnter4.mdl 0 1 0.25 1\r\n\tmodel models/leafchk1.mdl 0 1 0.25 1\r\n\tmodel models/leafchk2.mdl 0 1 0.25 1\r\n\tmodel models/leafchk3.mdl 0 1 0.25 1\r\n\trandomvel 210 70 280\r\n\tspawnorg 0\r\n\tgravity 800\r\n\trotationspeed 425\r\n\r\n}\r\nr_part ce_chunk_wood_metal\r\n{\r\n\tmodel models/splnter1.mdl 0 1 0.25 1\r\n\tmodel models/splnter2.mdl 0 1 0.25 1\r\n\tmodel models/splnter3.mdl 0 1 0.25 1\r\n\tmodel models/splnter4.mdl 0 1 0.25 1\r\n\tmodel models/metlchk1.mdl 0 1 0.25 1\r\n\tmodel models/metlchk2.mdl 0 1 0.25 1\r\n\tmodel models/metlchk3.mdl 0 1 0.25 1\r\n\tmodel models/metlchk4.mdl 0 1 0.25 1\r\n\trandomvel 210 70 280\r\n\tspawnorg 0\r\n\tgravity 800\r\n\trotationspeed 425\r\n}\r\nr_part ce_chunk_wood_stone\r\n{\r\n\tmodel models/splnter1.mdl 0 1 0.25 1\r\n\tmodel models/splnter2.mdl 0 1 0.25 1\r\n\tmodel models/splnter3.mdl 0 1 0.25 1\r\n\tmodel models/splnter4.mdl 0 1 0.25 1\r\n\tmodel models/schunk1.mdl 0 1 0.25 1\r\n\tmodel models/schunk2.mdl 0 1 0.25 1\r\n\tmodel models/schunk3.mdl 0 1 0.25 1\r\n\tmodel models/schunk4.mdl 0 1 0.25 1\r\n\trandomvel 210 70 280\r\n\tspawnorg 0\r\n\tgravity 800\r\n\trotationspeed 425\r\n}\r\nr_part ce_chunk_metal_stone\r\n{\r\n\tmodel models/metlchk1.mdl 0 1 0.25 1\r\n\tmodel models/metlchk2.mdl 0 1 0.25 1\r\n\tmodel models/metlchk3.mdl 0 1 0.25 1\r\n\tmodel models/metlchk4.mdl 0 1 0.25 1\r\n\tmodel models/schunk1.mdl 0 1 0.25 1\r\n\tmodel models/schunk2.mdl 0 1 0.25 1\r\n\tmodel models/schunk3.mdl 0 1 0.25 1\r\n\tmodel models/schunk4.mdl 0 1 0.25 1\r\n\trandomvel 210 70 280\r\n\tspawnorg 0\r\n\tgravity 800\r\n\trotationspeed 425\r\n}\r\nr_part ce_chunk_metal_cloth\r\n{\r\n\tmodel models/metlchk1.mdl 0 1 0.25 1\r\n\tmodel models/metlchk2.mdl 0 1 0.25 1\r\n\tmodel models/metlchk3.mdl 0 1 0.25 1\r\n\tmodel models/metlchk4.mdl 0 1 0.25 1\r\n\tmodel models/clthchk1.mdl 0 1 0.25 1\r\n\tmodel models/clthchk2.mdl 0 1 0.25 1\r\n\tmodel models/clthchk3.mdl 0 1 0.25 1\r\n\trandomvel 210 70 280\r\n\tspawnorg 0\r\n\tgravity 800\r\n\trotationspeed 425\r\n}\r\nr_part ce_chunk_webs\r\n{\r\n\tmodel models/shard1.mdl 3 1 0.25 -0.5\r\n\tmodel models/shard2.mdl 3 1 0.25 -0.5\r\n\tmodel models/shard3.mdl 3 1 0.25 -0.5\r\n\tmodel models/shard4.mdl 3 1 0.25 -0.5\r\n\tmodel models/shard5.mdl 3 1 0.25 -0.5\r\n\trandomvel 210 70 280\r\n\tspawnorg 0\r\n\tgravity 500\r\n\trotationspeed 425\r\n}\r\nr_part ce_chunk_glass\r\n{\r\n\tmodel models/shard1.mdl 0 1 0.25 1\r\n\tmodel models/shard2.mdl 0 1 0.25 1\r\n\tmodel models/shard3.mdl 0 1 0.25 1\r\n\tmodel models/shard4.mdl 0 1 0.25 1\r\n\tmodel models/shard5.mdl 0 1 0.25 1\r\n\trandomvel 210 70 280\r\n\tspawnorg 0\r\n\tgravity 800\r\n\trotationspeed 425\r\n}\r\nr_part ce_chunk_ice\r\n{\r\n\tmodel models/shard.mdl 0 1 0.25 -0.4 tr_ice\r\n\tmodel models/shard.mdl 1 1 0.25 -0.4 tr_ice\r\n\trotationspeed 30\r\n\trandomvel 210 70 280\r\n\tspawnorg 0\r\n\tgravity 800\r\n}\r\nr_part ce_chunk_clearglass\r\n{\r\n\tmodel models/shard1.mdl 1 1 0.25 -0.5\r\n\tmodel models/shard2.mdl 1 1 0.25 -0.5\r\n\tmodel models/shard3.mdl 1 1 0.25 -0.5\r\n\tmodel models/shard4.mdl 1 1 0.25 -0.5\r\n\tmodel models/shard5.mdl 1 1 0.25 -0.5\r\n\trandomvel 210 70 280\r\n\tspawnorg 0\r\n\tgravity 800\r\n\trotationspeed 425\r\n}\r\nr_part ce_chunk_redglass\r\n{\r\n\tmodel models/shard1.mdl 2 1 0.25 1\r\n\tmodel models/shard2.mdl 2 1 0.25 1\r\n\tmodel models/shard3.mdl 2 1 0.25 1\r\n\tmodel models/shard4.mdl 2 1 0.25 1\r\n\tmodel models/shard5.mdl 2 1 0.25 1\r\n\trandomvel 210 70 280\r\n\tspawnorg 0\r\n\tgravity 800\r\n\trotationspeed 425\r\n}\r\nr_part ce_chunk_acid\r\n{\r\n\tmodel models/sucwp2p.mdl 0 1 0.25 1 tr_acidball\r\n\trandomvel 210 70 280\r\n\tspawnorg 0\r\n\tgravity 800\r\n\trotationspeed 425\r\n}\r\nr_part ce_chunk_meteor\r\n{\r\n\tmodel models/tempmetr.mdl framestart=0 framecount=1 framerate=0.5 alpha=-1 trail=tr_grenade scalemin=0.30 scalemax=.70 fullbright\r\n\trandomvel 360\r\n\tspawnorg 0\r\n\tgravity 800\r\n\trotationspeed 425\r\n}\r\nr_part ce_chunk_greenflesh\r\n{\r\n\tmodel models/sflesh1.mdl 0 1 0.25 1 tr_spiderblood\r\n\tmodel models/sflesh2.mdl 0 1 0.25 1 tr_spiderblood\r\n\tmodel models/sflesh3.mdl 0 1 0.25 1 tr_spiderblood\r\n\trandomvel 210 70 280\r\n\tspawnorg 0\r\n\tgravity 800\r\n\trotationspeed 425\r\n}\r\nr_part ce_chunk_bone\r\n{\r\n\tmodel models/clshard1.mdl 1 1 0.25 1\r\n\tmodel models/clshard2.mdl 1 1 0.25 1\r\n\tmodel models/clshard3.mdl 1 1 0.25 1\r\n\tmodel models/clshard4.mdl 1 1 0.25 1\r\n\trandomvel 210 70 280\r\n\tspawnorg 0\r\n\tgravity 800\r\n\trotationspeed 425\r\n}\r\n\r\nr_part ce_fountain\r\n{\r\n\ttexture \"particles/fteparticlefont.tga\"\r\n\ttcoords 1 1 63 63 256 2 64\r\n\tcount\t1\r\n\tscale 10\r\n\trotationspeed -64 64\r\n\tscalefactor 1\r\n\tdie 1\r\n\talpha 0.2\r\n\trgb 128 128 128\r\n\trgbdelta 0 -32 -32\r\n\tblend add\r\n\tspawnvel 100\r\n\tveladd 1\r\n\tgravity 800\r\n}\r\n\r\nr_part ce_snow\r\n{\r\n\ttexture \"particles/fteparticlefont.tga\"\r\n\ttcoords 1 1 63 63 256 2 64\r\n\tcount\t1\r\n\tscale 30\r\n\tscaledelta -10\r\n\trotationspeed -64 64\r\n\tscalefactor 1\r\n\tdie 7\r\n\talpha 0.2\r\n\trgb 255 255 255\r\n\trgbdelta 0 -32 -32\r\n\tfriction 0\r\n\tblend add\r\n\tveladd 1\r\n\tgravity 0\r\n\tflurry 32\r\n}\r\n\r\n//eidolon's arena\r\nr_part ce_rain\r\n{\r\n\ttexture \"particles/fteparticlefont.tga\"\r\n\ttcoords 1 1 63 63 256 2 64\r\n\ttype texturedspark\r\n\tcount   1\r\n\tscale 2\r\n\tscalefactor 1\r\n\tdie 1\r\n\talpha 0.2\r\n\talphadelta 0\r\n\trgb 255 255 255\r\n\tfriction 0\r\n\tblend add\r\n\tveladd 1\r\n\tgravity 0\r\n}\r\n\r\n//this teleport effect is nothing like hexen2's. hopefully it'll be acceptable :s\r\n//the down ring\r\nr_part ce_teleporterbody\r\n{\r\n\ttexture \"particles/fteparticlefont.tga\"\r\n\ttcoords 193 1 255 63 256\r\n\tcount 32\r\n\tscale 16\r\n\tscalefactor 1\r\n\talpha 0.3\r\n\tdie 1\r\n\tveladd -52\r\n\trgb 255 255 255\r\n\tfriction 1\r\n\tspawnorg 32 0\r\n\tspawnmode uniformcircle\r\n}\r\n//the up ring\r\nr_part +ce_teleporterbody\r\n{\r\n\ttexture \"particles/fteparticlefont.tga\"\r\n\ttcoords 193 1 255 63 256\r\n\tcount 32\r\n\tscale 16\r\n\tscalefactor 1\r\n\talpha 0.3\r\n\tdie 1\r\n\tveladd 52\r\n\trgb 255 255 255\r\n\tfriction 1\r\n\tspawnorg 32 0\r\n\tspawnmode uniformcircle\r\n}\r\n\r\n\r\n//h2part.ce_quake was not loaded\r\n//h2part.ce_ghost was not loaded\r\n//h2part.ce_teleporterbody_1 was not loaded\r\n//h2part.ce_grey_smoke_100 was not loaded\r\n//h2part.ce_chunk_fire was not loaded\r\n"
  },
  {
    "path": "engine/partcfgs/high.cfg",
    "content": "///////////////////////////////\r\n//rain\r\nr_part te_rain\r\n{\r\n\ttexture ball; scalefactor 1; count 1; alpha 0.4; rgb 255 255 255; die 2; veladd 2; scale 2; type texturedspark\r\n\tcliptype rainsplash\r\n\tclipbounce 1\r\n\tclipcount 5\r\n}\r\n\r\nr_part rainsplash\r\n{\r\n\trandomvel 50 50\r\n\tcount 1;\r\n\ttexture ball; scalefactor 1; alpha 0.1; rgb 255 255 255; die 0.4; scale 50;\r\n\tstretchfactor 4\r\n\tveladd 50; scale 1; type texturedspark\r\n\tgravity 400\r\n}\r\n\r\n///////////////////////////////\r\n//rocket trail\r\n\r\n// flame trail\r\nr_part tr_rocket\r\n{\r\n\ttexture \"particles/fteparticlefont.tga\"\r\n\ttcoords 97 97 191 191 256\r\n\tstep 1\r\n\tscale 12\r\n\talpha 0.4\r\n\tdie 0.5\r\n\trgb 255 127 100\r\n\trgbdelta -14 -300 -300\r\n\tblend add\r\n\tscalefactor 1\r\n\tscaledelta -15\r\n}\r\n\r\n// smoke puffs\r\nr_part +tr_rocket\r\n{\r\n\ttexture \"particles/fteparticlefont.tga\"\r\n\ttcoords 97 97 191 191 256\r\n\tstep 5\r\n\tscale 30\r\n\talpha 0.2\r\n\tdie 0.75\r\n\t//diesubrand 10.25\r\n\trandomvel 0.2\r\n\trgb 5 5 5\r\n\t//rgbdelta -230 -45 -9\r\n\tgravity -15\r\n\tscalefactor 1\r\n\tscaledelta 20\r\n\tspawnvel 5\r\n}\r\n\r\n\r\n// burst sparks\r\nr_part +tr_rocket\r\n{\r\n\ttype texturedspark\r\n\ttexture \"particles/fteparticlefont.tga\"\r\n\ttcoords 1 65 31 95 256 8 32\r\n\tcount 1\r\n\tscale 2\r\n\tscalefactor 1\r\n\tscaledelta -15\r\n\talpha 0.2\r\n\tdie 0.25\r\n\trgb 255 128 0\r\n\tblend add\r\n\tspawnmode ball\r\n\tspawnorg 1\r\n\tspawnvel 50\r\n\tveladd 500\r\n\tfriction 0.01\r\n\tgravity 100\r\n}\r\n\r\n///////////////////////////////////////////\r\n//alternate rocket trail, which is used by a handful of qw players.\r\n//r_part tr_altrocket\r\n//{\r\n//}\r\n\r\n\r\n///////////////////////////////////////////\r\n//grenade trail\r\n\r\nr_part tr_grenade\r\n{\r\n\ttexture \"particles/fteparticlefont.tga\"\r\n\ttcoords 97 97 191 191 256\r\n\tstep 6\r\n\tscale 32\r\n\tscaledelta 12\r\n\talpha 0.3\r\n\tdie 1.25\r\n\trandomvel 2\r\n\tveladd 15\r\n\trgb 75 75 75\r\n\t//rgb 255 50 50\r\n\t//rgbdelta -255 -75 -75\r\n\tgravity -25\r\n\tscalefactor 1\r\n\tblend modulate\r\n}\r\nr_part +tr_grenade\r\n{\r\n\ttexture \"particles/fteparticlefont.tga\"\r\n\ttcoords 97 97 191 191 256\r\n\tscale 1\r\n\tscaledelta 0.25\r\n\talpha 0.2\r\n\tstep 4\r\n\tdie 0.8\r\n\trandomvel 0\r\n\trgb 255 150 150\r\n\trgbdelta 0 -150 -150\r\n\ttype beam\r\n\tblend add\r\n}\r\n\r\n//////////////////////////////////\r\n//shotgun impacts\r\nr_part gunshotsmoke\r\n{\r\n\ttexture \"particles/fteparticlefont.tga\"\r\n\ttcoords 1 65 31 95 256 8 32\r\n\tcount 3\r\n\tscale 25\r\n\tscalefactor 1\r\n\tdie 0.8\r\n\talpha 0.12\r\n\trgb 32 32 32\r\n\tblend add\r\n\tspawnmode ball\r\n\tspawnorg 2\r\n\tspawnvel 20\r\n\tveladd -20\r\n}\r\nr_part te_gunshot\r\n{\r\n\ttype texturedspark\r\n\ttexture \"particles/fteparticlefont.tga\"\r\n\ttcoords 1 65 31 95 256 8 32\r\n\tcount 3\r\n\tscale 2\r\n\tscalefactor 1\r\n\talpha 0.5\r\n\tdie 0.8\r\n\trgb 255 128 0\r\n\tblend add\r\n\tspawnmode ball\r\n\tspawnorg 1\r\n\tspawnvel 100\r\n\tveladd -80\r\n\tfriction 0.3\r\n\tgravity 400\r\n\tassoc gunshotsmoke\r\n}\r\n\r\n//////////////////////////////////\r\n//nail impacts\r\n\r\nr_part te_spike\r\n{\r\n\ttype sparkfan\r\n\tcount 10\r\n\tscale 1\r\n\tscalefactor 1\r\n\talpha 0.5\r\n\tdie 0.2\r\n\trgb 255 128 0\r\n\tblend add\r\n\tspawnmode ball\r\n\tspawnorg 12\r\n\tspawnvel 300\r\n}\r\nr_part +te_spike\r\n{\r\n\ttexture \"particles/fteparticlefont.tga\"\r\n\ttcoords 1 97 95 191 256\r\n\tcount 1\r\n\tscale 1\r\n\tscalefactor 1\r\n\tscaledelta 190\r\n\tdie 0.1\r\n\talpha 0.6\r\n\trgb 255 128 0\r\n\tblend add\r\n\tassoc gunshotsmoke\r\n}\r\n\r\nr_part te_superspike\r\n{\r\n\ttype sparkfan\r\n\tcount 20\r\n\tscale 1\r\n\tscalefactor 1\r\n\talpha 0.5\r\n\tdie 0.2\r\n\trgb 255 128 0\r\n\tblend add\r\n\tspawnmode ball\r\n\tspawnorg 12\r\n\tspawnvel 300\r\n}\r\nr_part +te_superspike\r\n{\r\n\ttexture \"particles/fteparticlefont.tga\"\r\n\ttcoords 1 97 95 191 256\r\n\tcount 1\r\n\tscale 1\r\n\tscalefactor 1\r\n\tscaledelta 190\r\n\tdie 0.1\r\n\talpha 0.6\r\n\trgb 255 128 0\r\n\tblend add\r\n\tassoc gunshotsmoke\r\n}\r\n\r\n//simple slight trail, to show movement more than anything else.\r\nr_part tr_spike\r\n{\r\n\ttexture \"particles/fteparticlefont.tga\"\r\n\tstep 1.1\r\n\tscale 4\r\n\tdie .4\r\n\trgb 20 20 20\r\n\talpha 1\r\n\tblend add\r\n\tspawnmode spiral 512\r\n\tspawnvel 10\r\n}\r\nr_trail \"progs/spike.mdl\" tr_spike\r\nr_trail \"progs/s_spike.mdl\" tr_spike\r\n\r\n////////////////////////////////////////////////\r\n//explosion\r\n\r\n//red bit\r\nr_part te_explosion\r\n{\r\n\ttexture \"particles/fteparticlefont.tga\"\r\n\ttcoords 97 97 191 191 256\r\n\tcount 1\r\n\tscale 500\r\n\talpha 0.4\r\n\tdie 0.2\r\n\trgb 255 127 100\r\n\trgbdelta -14 -300 -300\r\n\tblend add\r\n\tscalefactor 1\r\n\tscaledelta -15\r\n\trandomvel 0\r\n//\tlightradius 350\r\n//\tlightrgb    1.4 1.2 1.05\r\n//\tlighttime   0.5\r\n//\tlightradiusfade 350\r\n//\tlightrgbfade 2 2 2\t\r\n}\r\n//smoke\r\nr_part +te_explosion\r\n{\r\n\ttexture \"particles/fteparticlefont.tga\"\r\n\ttcoords 97 97 191 191 256\r\n\tcount 7\r\n\tscale 300\r\n\talpha 0.2\r\n\tdie 0.8\r\n\t//diesubrand 10.25\r\n\trandomvel 100\r\n\trgb 5 5 5\r\n\t//rgbdelta -230 -45 -9\r\n\tgravity -15\r\n\tscalefactor 1\r\n\tscaledelta 40\r\n\tspawnvel 5\r\n}\r\n// burst sparks\r\nr_part +te_explosion\r\n{\r\n\ttype texturedspark\r\n\ttexture \"particles/fteparticlefont.tga\"\r\n\ttcoords 1 65 31 95 256 8 32\r\n\tcount 100\r\n\tscale 5\r\n\tscalefactor 1\r\n\tscaledelta -15\r\n\talpha 0.2\r\n\tdie 0.5\r\n\trgb 255 128 0\r\n\tblend add\r\n\tspawnmode ball\r\n\tspawnorg 1\r\n\trandomvel 1000\r\n\tfriction 0.01\r\n\tgravity 100\r\n\tstretchfactor -80\r\n}\r\n\r\n//hide lights in explosions.\r\n//r_explosionlight 0\r\n\r\n//hide the explosion sprite in qw\r\ncl_expsprite 0\r\n//hide it in nq - WARNING: some mods use this sprite as a flame thrower.\r\n//r_effect \"progs/s_explod.spr\" hidden 1\r\n\r\n\r\n//////////////////////////////////////////\r\n//rogue te_explosion2 effect\r\n//note: if not otherwise defined, te_explosion2_BASE_RAND maps to this, and specifies the palette colours.\r\nr_part te_explosion2\r\n{\r\n\ttype texturedspark\r\n\ttexture \"particles/fteparticlefont.tga\"\r\n\ttcoords 1 65 31 95 256 8 32\r\n\tcount 256\r\n\tscale 5\r\n\tscalefactor 1\r\n\tscaledelta -15\r\n\talpha 0.2\r\n\tdie 0.5\r\n\tblend add\r\n\tspawnmode ball\r\n\tspawnorg 1\r\n\trandomvel 1000\r\n\tfriction 0.01\r\n\tgravity 100\r\n\tstretchfactor -80\r\n}\r\n//dragon fireball\r\n//r_part te_explosion2_228_5\r\n\r\n//rogue multigrenade sub explosion\r\n//also triggered from a shielded rogue player touching another player (and doing some damage)\r\n//also used during the ending.\r\n//red particles\r\n//r_part te_explosion2_230_5\r\n\r\n//rogue plasma explosion\r\n//also rogue timemachine explosion\r\n//white particles splaying outwards\r\n//r_part te_explosion2_244_3\r\n\r\n//////////////////////////////////////////\r\n//for when a spawn dies.\r\n//also used by TF for emp explosions.\r\nr_part te_tarexplosion\r\n{\r\n\ttype texturedspark\r\n\ttexture \"particles/fteparticlefont.tga\"\r\n\ttcoords 1 65 31 95 256 8 32\r\n\tcount 128\r\n\tscale 5\r\n\tscalefactor 1\r\n\tscaledelta -15\r\n\trgb 0 0 17\r\n\talpha 0.5\r\n\tdie 0.5\r\n\tspawnmode ball\r\n\tspawnorg 1\r\n\trandomvel 500\r\n\tfriction 0.01\r\n\tgravity 100\r\n\tstretchfactor -80\r\n}\r\nr_part +te_tarexplosion\r\n{\r\n\ttype texturedspark\r\n\ttexture \"particles/fteparticlefont.tga\"\r\n\ttcoords 1 65 31 95 256 8 32\r\n\tcount 256\r\n\tscale 5\r\n\tscalefactor 1\r\n\tscaledelta -15\r\n\trgb 83 67 115\r\n\talpha 0.3\r\n\tdie 0.5\r\n\tblend add\r\n\tspawnmode ball\r\n\tspawnorg 1\r\n\trandomvel 500\r\n\tfriction 0.01\r\n\tgravity 100\r\n\tstretchfactor -80\r\n}\r\n\r\n//////////////////////////////////////////\r\n//cthon falling into lava.\r\n//often also used for TF gas grenades.\r\nr_part te_lavasplash\r\n{\r\n\ttexture \"particles/fteparticlefont.tga\"\r\n\ttcoords 129 1 191 63 256\r\n\tcount\t654\r\n\tscale 15\r\n\talpha 0.7\r\n\tdie 4\r\n\trandomvel 64\r\n\trgb 255 128 128\r\n\tgravity 50\r\n\tblend add\r\n\tspawnorg 192 64\r\n\tup 48\r\n}\r\n\r\n//////////////////////////////////////////\r\n//FIXME: what if we don't have glsl support?\r\nr_part te_teleport\r\n{\r\n\tscale 250\r\n\tcount 1\r\n\talpha 0.3\r\n\tdie 0.5\r\n\tscalefactor 1\r\n\trotationstart 45\r\n\trotationspeed 0\r\n\r\n\tshader\r\n\t{\r\n\t\tsurfaceparm noshadows\r\n\t\tsurfaceparm nodlight\r\n\t\tglslprogram\r\n\t\t{\r\n\t\t\t!!samps screen=0\r\n\t\t\tvarying vec2 tcoord;\r\n\t\t\tvarying vec4 scoord;\r\n\t\t\tvarying float alph;\r\n#ifdef VERTEX_SHADER\r\n\t\t\tattribute vec2 v_texcoord;\r\n\t\t\tattribute vec4 v_colour;\r\n\t\t\r\n\t\t\tvoid main(void)\r\n\t\t\t{\r\n\t\t\t\tscoord = ftetransform();\r\n\t\t\t\ttcoord = (v_texcoord.st - 0.5)*2.0;\r\n\t\t\t\talph = v_colour.a;\r\n\t\t\t\tgl_Position = ftetransform();\r\n\t\t\t}\r\n#endif\r\n#ifdef FRAGMENT_SHADER\r\n\t\t\tvoid main(void)\r\n\t\t\t{\r\n\t\t\t\tvec2 nst;\r\n\t\t\t\tfloat f;\r\n\t\t\t\tnst = scoord.xy / scoord.w;\r\n\t\t\t\tnst = (1.0 + nst)/2.0;\r\n\t\t\t\tf = 1.0 - length(tcoord);\r\n//\t\t\t\tf = 1.0 - tcoord*tcoord;\r\n\t\t\t\tif (f < 0.0) discard;\r\n\t\t\t\tf *= alph;\r\n\t\t\t\tgl_FragColor = texture2D(s_screen, nst - tcoord*f);\r\n\t\t\t}\r\n#endif\r\n\t\t}\r\n\t\t{\r\n\t\t\tmap $currentrender\r\n\t\t\tblendfunc blend\r\n\t\t}\r\n\t}\r\n}\r\n\r\n\r\n//////////////////////////////////////////\r\n//hellknight\r\nr_part tr_knightspike\r\n{\r\n\ttexture \"particles/fteparticlefont.tga\"\r\n\ttcoords 1 97 95 191 256\r\n\tscale 15\r\n\tstep 1\r\n\talpha 0.6\r\n\tdie 0.2\r\n\trgb 192 96 48\r\n\tveladd 0\r\n\trandomvel 2\r\n\tfriction 4\r\n\tscalefactor 0.825\r\n\tblend add\r\n\tspawnmode spiral\r\n\tspawnvel -50\r\n\tlighttime 0\r\n\tlightshadows 0\r\n\tlightradius 150\r\n\tlightrgb    0.75 0.37 0.18\r\n}\r\n\r\nr_part te_knightspike\r\n{\r\n\ttype sparkfan\r\n\tcount 200\r\n\tscale 3\r\n\tscalefactor 1\r\n\talpha 0.5\r\n\tdie 0.5\r\n\trgb 192 96 48\r\n\tblend add\r\n\tspawnmode ball\r\n\tspawnorg 12\r\n\tspawnvel 100\r\n\tstretchfactor 10\r\n}\r\n\r\n/////////////////////////////////////////\r\n//vore missiles\r\nr_part tr_vorespike\r\n{\r\n\ttexture \"particles/fteparticlefont.tga\"\r\n\ttcoords 1 97 95 191 256\r\n\tscale 15\r\n\tstep 1\r\n\talpha 0.6\r\n\tdie 0.5\r\n\trgb 192 96 192\r\n\tveladd 15\r\n\tspawnmode spiral\r\n\tspawnvel 50\r\n\trandomvel 0\r\n\tfriction 0\r\n\tscalefactor 1\r\n\tblend add\r\n\tlighttime 0\r\n\tlightshadows 0\r\n\tlightradius 150\r\n\tlightrgb    0.75 0.37 0.75\r\n}\r\n//rygel's pack sucks\r\nr_trail \"progs/v_spike.mdl\" tr_vorespike\r\n\r\n\r\n////////////////////\r\n//enforcer laser effect\r\nr_part tr_enforcerlaser\r\n{\r\n\ttype texturedspark\r\n\ttexture \"particles/fteparticlefont.tga\"\r\n\ttcoords 1 97 95 191 256\r\n\tscale 15\r\n\tstep 4\r\n\talpha 0.3\r\n\tdie 0.5\r\n\trgb 255 69 0\r\n\tveladd -32\r\n\tspawnmode spiral\r\n\tspawnvel 16\r\n\trandomvel 32\r\n\tfriction 0\r\n\tscalefactor 1\r\n\tblend add\r\n\tlighttime 0.2\r\n\tlightshadows 0\r\n\tlightradius 150\r\n\tlightrgb    1 0.27 0\r\n\tlightrgbfade    5 1 0\r\n\tlightcorona 2 0.25\r\n}\r\nr_trail \"progs/laser.mdl\" tr_enforcerlaser\r\n\r\n/////////////////////////////////////////\r\n//rogue wrath enemy's projectiles\r\nr_part tr_wrathball\r\n{\r\n\ttype texturedspark\r\n\ttexture \"particles/fteparticlefont.tga\"\r\n\ttcoords 1 97 95 191 256\r\n\tscale 15\r\n\tstep 4\r\n\talpha 0.3\r\n\tdie 0.5\r\n\trgb 255 0 0\r\n\tveladd -32\r\n\tspawnmode spiral\r\n\tspawnvel 16\r\n\trandomvel 32\r\n\tfriction 0\r\n\tscalefactor 1\r\n\tblend add\r\n\tlighttime 0.2\r\n\tlightshadows 0\r\n\tlightradius 150\r\n\tlightrgb    1 0.27 0\r\n\tlightrgbfade    5 1 0\r\n\tlightcorona 2 0.5\r\n}\r\nr_trail \"progs/w_ball.mdl\" tr_wrathball\r\n\r\n//wrath death\r\n//grey particles\r\n//no difference from the fallback except for the blend mode. this should ensure that we are not quite so invisible.\r\nr_part te_explosion2_0_4\r\n{\r\n\ttype texturedspark\r\n\ttexture \"particles/fteparticlefont.tga\"\r\n\ttcoords 1 65 31 95 256 8 32\r\n\tcount 256\r\n\tscale 5\r\n\tscalefactor 1\r\n\tscaledelta -15\r\n\talpha 0.2\r\n\tdie 0.5\r\n\tspawnmode ball\r\n\tspawnorg 1\r\n\trandomvel 1000\r\n\tfriction 0.01\r\n\tgravity 100\r\n\tstretchfactor -80\r\n}\r\n\r\n/////////////////////////////////////////\r\n//rogue lavaspikes\r\nr_part tr_lavaspike\r\n{\r\n\ttype spark\r\n\ttexture \"particles/fteparticlefont.tga\"\r\n\ttcoords 1 97 95 191 256\r\n\tscale 15\r\n\tstep 4\r\n\talpha 0.3\r\n\tdie 0.5\r\n\trgb 255 0 0\r\n\tveladd -32\r\n\tspawnmode spiral\r\n\tspawnvel 16\r\n\trandomvel 32\r\n\tfriction 0\r\n\tscalefactor 1\r\n\tblend add\r\n}\r\nr_trail \"progs/lspike.mdl\" tr_lavaspike\r\n\r\n/////////////////////////////////////////\r\n//rogue plasma gun\r\nr_part tr_plasma\r\n{\r\n\ttype texturedspark\r\n\ttexture \"particles/fteparticlefont.tga\"\r\n\ttcoords 1 97 95 191 256\r\n\tscale 15\r\n\tstep 4\r\n\talpha 0.3\r\n\tdie 0.25\r\n\trgb 128 128 255\r\n\tveladd -32\r\n\tspawnmode spiral\r\n\tspawnvel 16\r\n\trandomvel 32\r\n\tfriction 0\r\n\tscalefactor 1\r\n\tblend add\r\n\tlighttime 0.2\r\n\tlightshadows 0\r\n\tlightradius 150\r\n\tlightrgb    1 1 2\r\n\tlightrgbfade    5 1 0.5\r\n\tlightcorona 2 0.5\r\n}\r\nr_trail \"progs/plasma.mdl\" tr_plasma\r\n\r\n\r\n/////////////////////////////////////////\r\n//scrag missiles.\r\nr_part tr_wizspike\r\n{\r\n\ttexture \"particles/fteparticlefont.tga\"\r\n\ttcoords 1 97 95 191 256\r\n\tscale 15\r\n\tstep 4\r\n\talpha 0.6\r\n\tdie 0.2\r\n\trgb 25 200 25\r\n\tveladd 0\r\n\trandomvel 2\r\n\tfriction 4\r\n\tscalefactor 0.825\r\n\tspawnmode spiral\r\n\tspawnvel 25\r\n\tblend add\r\n\tlighttime 2\r\n\tlightradiusfade 75\r\n\tlightshadows 0\r\n\tlightradius 150\r\n\tlightrgb    0.1 0.7 0.1\r\n}\r\n\r\nr_part tr_wizspike2\r\n{\r\n\ttexture \"particles/fteparticlefont.tga\"\r\n\ttcoords 1 97 95 191 256\r\n\tscale 4\r\n\tstep 1\r\n\talpha 0.6\r\n\tdie 0.2\r\n\trgb 25 200 25\r\n\tveladd 64\r\n\trandomvel 64\r\n\tfriction 4\r\n\tscalefactor 0.825\r\n\tspawnmode spiral\r\n\tspawnvel 25\r\n\tblend add\r\n}\r\n//scrag impact\r\nr_part te_wizspike\r\n{\r\n\ttexture \"particles/fteparticlefont.tga\"\r\n\ttcoords 1 97 95 191 256\r\n\tscale 15\r\n\talpha 0.6\r\n\trgb 25 200 25\r\n\tfriction 0\r\n\tscalefactor 0.825\r\n\tblend add\r\n\tcount 5\r\n\tveladd -256\r\n\trandomvel 256\r\n\tdie 1\r\n\tdiesubrand 0.5\r\n\tgravity 800\r\n\temit tr_wizspike2\r\n\temitinterval -1\r\n\tbounce 1.5\r\n}\r\n\r\n/////////////////////////////////////////\r\n//shambler stuff\r\nr_part shambercharging\r\n{\r\n\tspawnmode ball\r\n\tcount 200\r\n\tspawnorg 128\r\n\tspawnvel -256\r\n\ttexture \"particles/fteparticlefont.tga\"\r\n\ttcoords 1 1 63 63 256 2 64\r\n\tscale 4\r\n\talpha 1\r\n\tdie 0.5\r\n\torgadd -64\r\n\trotationspeed 90\r\n\trotationstart 0 360\r\n\trgb 100 100 250\r\n\trgbrand 0 0 0\r\n\tgravity 0\r\n\tscalefactor 0.4\r\n\tlighttime 0\r\n\tlightshadows 0\r\n\tlightradius 400\r\n\tlightrgb    2 2 2\r\n}\r\nr_effect progs/s_light.mdl shambercharging 0\r\n\r\n/////////////////////////////////////////\r\n//blood effects\r\nr_part te_blood\r\n{\r\n\ttexture fte_bloodparticle\r\n\tblend subtract\r\n\tcount 1\r\n\tscale 32\r\n\talpha 0\r\n\tdie 1\r\n\trandomvel 64\r\n\tveladd 10\r\n\trotationspeed 90\r\n\trotationstart 0 360\r\n\trgb 32 64 64\r\n\trgbdelta -32 -64 -64\r\n\tgravity 200\r\n\tscalefactor 0.8\r\n}\r\n\r\nr_part high.pe_73\r\n{\r\n\ttexture fte_bloodparticle\r\n\tblend subtract\r\n\tcount 1\r\n\tscale 32\r\n\talpha 0\r\n\tdie 1\r\n\trandomvel 64\r\n\tveladd 10\r\n\trotationspeed 90\r\n\trotationstart 0 360\r\n\trgb 32 64 64\r\n\trgbdelta -32 -64 -64\r\n\tgravity 200\r\n\tscalefactor 0.8\r\n}\r\n\r\nr_part te_lightningblood\r\n{\r\n\ttexture fte_bloodparticle\r\n\tblend subtract\r\n\tcount 1\r\n\tscale 32\r\n\talpha 0\r\n\tdie 1\r\n\trandomvel 32\r\n\tveladd 5\r\n\trotationspeed 90\r\n\trotationstart 0 360\r\n\trgb 64 128 128\r\n\trgbdelta -64 -128 -128\r\n\tgravity 200\r\n\tscalefactor 0.8\r\n}\r\nr_part high.pe_225\r\n{\r\n\ttexture fte_bloodparticle\r\n\tblend subtract\r\n\tcount 0.5\r\n\tscale 32\r\n\talpha 0\r\n\tdie 1\r\n\trandomvel 32\r\n\tveladd 5\r\n\trotationspeed 90\r\n\trotationstart 0 360\r\n\trgb 64 128 128\r\n\trgbdelta -64 -128 -128\r\n\tgravity 200\r\n\tscalefactor 0.8\r\n}\r\n\r\n/////////////////////////////////////////\r\n//zombie body-part blood trails\r\nr_part tr_slightblood\r\n{\r\n\ttexture fte_bloodparticle\r\n\tblend subtract\r\n//\ttcoords 1 1 63 63 256 2 64\r\n\tstep 16\r\n\tscale 64\r\n\talpha 0\r\n\tdie 1\r\n\trandomvel 32\r\n\tveladd 10\r\n\trotationspeed 90\r\n\trotationstart 0 360\r\n\trgb 64 128 128 \r\n\trgbdelta -64 -128 -128\r\n\tgravity 200\r\n\tscalefactor 0.8\r\n\tscaledelta -10\r\n\tstains -0.5\r\n}\r\n\r\n//////////////////////////////////////////\r\n//regular ol' blood trails\r\nr_part tr_blood\r\n{\r\n\ttexture fte_bloodparticle\r\n\tblend subtract\r\n\tstep 8\r\n\tscale 64\r\n\talpha 0\r\n\tdie 1\r\n\trandomvel 32\r\n\tveladd 10\r\n\trotationspeed 90\r\n\trotationstart 0 360\r\n\trgb 32 128 128 \r\n\trgbdelta -32 -128 -128\r\n\tgravity 200\r\n\tscalefactor 0.8\r\n\tscaledelta -10\r\n\tstains -0.5\r\n}\r\n\r\n//////////////////////////////////\r\n//fallbacks\r\n\r\nr_part pe_default\r\n{\r\n\ttexture \"particles/fteparticlefont.tga\"\r\n\ttcoords 1 97 95 191 256\r\n\tcount 1\r\n\tscale 4\r\n\tveladd 15\r\n\tdie 0.4\r\n\talphadelta 0\r\n\tdiesubrand 0.4\r\n\tgravity 40\r\n\tspawnorg 8\r\n}\r\nr_part pe_defaulttrail\r\n{\r\n\ttexture \"particles/fteparticlefont.tga\"\r\n\ttcoords 1 97 95 191 256\r\n\tscale 15\r\n\tstep 1\r\n\talpha 0.6\r\n\tdie 0.2\r\n\trgb 192 96 48\r\n\tveladd 0\r\n\trandomvel 2\r\n\tfriction 4\r\n\tscalefactor 0.825\r\n\tspawnmode spiral\r\n\tspawnvel 25\r\n\tblend add\r\n}\r\n\r\n//////////////////////////////////\r\n//map debugging\r\nr_part pe_pointfile\r\n{\r\n\ttexture \"particles/fteparticlefont.tga\"\r\n\ttcoords 1 97 95 191 256\r\n\tcount 1\r\n\tscale 50\r\n\tdie 30\r\n\talphadelta 0\r\n\trgb 255 255 0\r\n}\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
  },
  {
    "path": "engine/partcfgs/highfps.cfg",
    "content": "// highfps, originally submitted by 'ShadowWalker'\n// rehashed by TimeServ\nr_part t_gib\n{\n\ttexture \"particles/bloodtrail\"\n\tstep 12\n\tscale 10\n\tdie 1\n\trandomvel 32\n\tveladd 32\n\trgb 64 0 0\n\trgbdelta -128 0 0\n}\nr_part t_zomgib\n{\n\ttexture \"particles/bloodtrail\"\n\tstep 16\n\tscale 8\n\tdie 1\n\trandomvel 32\n\tveladd 32\n\trgb 192 0 0\n\trgbdelta -128 0 0\n}\n\nr_part t_tracer\n{\n\ttexture \"particles/tracer\"\n\tscale 23\n\tstep 18\n\trgb 192 192 0\n\tdie 0.5\n}\n\nr_part t_tracer2\n{\n\ttexture \"particles/tracer\"\n\tscale 23\n\tstep 18\n\tdie 0.5\n\trgb 192 96 0\n}\n\nr_part t_tracer3\n{\n\ttexture \"particles/tracer\"\n\tscale 23\n\tstep 18\n\tdie 0.5\n\trgb 192 0 192\n}\n\nr_part te_lightningblood\n{\n\ttexture \"particles/bloodtrail\"\n\tcount 1\n\tscale 10\n\tdie 0.5\n\trandomvel 64\n\tveladd 128\n\trgb 192 0 0\n\tblend add\n}\n\nr_part te_blood\n{\n\ttexture \"particles/bloodtrail\"\n\tcount 1\n\tscale 12\n\tdie 0.5\n\trandomvel 32\n\tveladd 64\n\tspawnvel 0 10\n\trgb 64 0 0\n}\n\nr_part sparks\n{\n\ttexture \"particles/spark\"\n\tcount\t32\n\tscale 3\n\talpha 1\n\tdie 1\n\trandomvel 256\n\tveladd 128\n\trgb 255 128 0\n\tblend add\n\tcliptype sparks\n\tclipcount 1\n}\n\nr_part explosioncore\n{\n\ttexture \"particles/explosion\"\n\tcount 1\n\tscale 200\n\tscalefactor 1\n\tdie 1.2\n\trgb 255 128 76\n\tblend add\n\tassoc sparks\n}\n\nr_part te_explosion\n{\n\ttexture \"particles/explosion\"\n\tcount 8\n\tscale 60\n\talpha 0.5\n\tdie 1\n\trgb 255 128 76\n\tblend add\n\tassoc explosioncore\n\tspawnmode ball\n\tspawnorg 64\n}\n\nr_part te_railtrail\n{\n\tstep 1000000\n\tscale 5\n\tdie 1.2\n\talpha 0.7\n\trgb 16 16 255\n\tblend add\n\ttype beam\n\taverageout\n}\n\n//the blob tempent is used quite a bit with teamfortress emp grenades.\nr_part te_tarexplosion\n{\n\ttexture \"particles/blob\"\n\tcount 64\n\tscale 30\n\tscalefactor 1\n\tdie 1\n\trandomvel 32\n\tveladd 0\n\trgb 255 0 196\n\tspawnorg 8 56\n\tspawnvel 48 8\n}\n\nr_part te_gunshot\n{\n\ttexture \"particles/spark\"\n\tcount 2\n\tscale 3\n\talpha 0.7\n\tdie 0.5\n\trandomvel 64\n\trgb 255 128 0\n\tblend add\n}\n\nr_part te_lavasplash\n{\n\ttexture \"particles/lava\"\n\tcount 180\n\tscale 60\n\talpha 0.5\n\tdie 1.6\n\trgb 255 128 128\n\tspawnorg 178 64\n\tup 56\n\tscalefactor 1\n}\n\nr_part te_teleportsplash\n{\n\ttexture \"particles/teleport\"\n\tcount 48\n\tscale 30\n\tscalefactor 1\n\tdie 0.5\n\trandomvel 32\n\tveladd 0\n\trgb 255 255 255\n\tspawnorg 4 32\n\tspawnvel 25 4\n}\n\nr_part t_grenade\n{\n\ttexture \"particles/smoke\"\n\tstep 20\n\tscale 21\n\tdie 0.5\n\trandvel 16\n\trgb 128 128 128\n}\n\nr_part t_rocket\n{\n\ttexture \"particles/rocket\"\n\tstep 15\n\tscale 30\n\tdie 0.2\n\trgb 192 48 0\n\tblend add\n\tassoc t_grenade\n}\n\nr_part t_altrocket\n{\n\ttexture \"particles/rocket\"\n\tstep 15\n\tscale 25\n\trandomvel 30\n\tveladd 30\n\tdie 0.5\n\trgb 192 48 0\n\tblend add\n}\n\n//you'll probably never see this one\nr_part ef_entityparticles\n{\n\ttexture \"j\"\n\tcount\t1\n\tscale 10\n\talpha 0.3\n\tdie 0\n\tveladd 16\n\trgb 128 128 0\n}\n\nr_part pe_default\n{\n\ttexture \"particles/quake\"\n\tcount 1\n\tscale 4\n\tveladd 15\n\tdie 0.5\n\tspawnorg 8\n}\n\nr_part pe_defaulttrail\n{\n\ttexture \"particles/quake\"\n\tstep 15\n\tdie 0.5\n\tscale 8\n\tveladd 15\n\tspawnorg 1\n}\n\nr_part pe_pointfile\n{\n\ttexture \"particles/quake\"\n\tcount 1\n\tscale 50\n\tdie 30\n\talphadelta 0\n\trgb 255 255 0\n}\n\n"
  },
  {
    "path": "engine/partcfgs/minimal.cfg",
    "content": "// minimal, by TimeServ\nr_part pe_size3\n{\n\ttexture \"particles/quake\"\n\tcount 1\n\tdie 1\n\tscale 20\n\tscaledelta -20\n\tveladd 25\n\tspawnorg 38\n\tspawnvel 38\n\tscalefactor 0.8\n}\n\nr_part pe_size2\n{\n\ttexture \"particles/quake\"\n\tcount 1\n\tdie 1\n\tscale 12\n\tscaledelta -12\n\tveladd 20\n\tspawnorg 16\n\tspawnvel 16\n\tscalefactor 0.8\n}\n\nr_part pe_default\n{\n\ttexture \"particles/quake\"\n\tcount 1\n\tdie 1\n\tscale 10\n\tscaledelta -10\n\tveladd 15\n\tspawnorg 10\n\tspawnvel 10\n\tscalefactor 0.8\n}\n\nr_part pe_defaulttrail\n{\n\ttexture \"particles/quake\"\n\tstep 10\n\tdie 1\n\tscale 8\n\tscaledelta -8\n\tveladd 15\n\tspawnorg 2\n\tspawnvel 2\n\tscalefactor 0.8\n}\n"
  },
  {
    "path": "engine/partcfgs/q2part.cfg",
    "content": "//model \"name\" framestart= frames= framerate= alpha= trail= orient additive transparent fullbright shadow noshadow\r\n\r\n\r\nr_part namespace q2part\r\n\r\nr_part pe_default\r\n{\r\n\ttexture \"classicparticle\"\r\n\ttcoords 0 0 16 16 32\r\n\tcount 1\r\n\tscale 1\r\n\talpha 1\r\n\tdie 0.3 0.8\r\n\trandomvel 20\r\n\torgadd 0 31\r\n\tspawnorg 4\r\n\tgravity 40\r\n\tscalefactor 0.8\r\n}\r\n\r\nr_part te_splashsparks\r\n{\r\n\ttexture \"classicparticle\"\r\n\ttcoords 0 0 16 16 32\r\n\tcount 1\r\n\tscale 1\r\n\talpha 1\r\n\tdie 0.3 0.8\r\n\trandomvel 20\r\n\torgadd 0 31\r\n\tspawnorg 4\r\n\tgravity 40\r\n\tscalefactor 0.8\r\n\tcolorindex 0xe0\r\n}\r\nr_part te_splashunknown\r\n{\r\n\tassoc te_splashsparks\r\n}\r\nr_part teq2_sparks\r\n{\r\n\ttexture \"classicparticle\"\r\n\ttcoords 0 0 16 16 32\r\n\tcount 6\r\n\tscale 1\r\n\talpha 1\r\n\tdie 0.3 0.8\r\n\trandomvel 20\r\n\torgadd 0 31\r\n\tspawnorg 4\r\n\tgravity 40\r\n\tscalefactor 0.8\r\n\tcolorindex 0xe0\r\n}\r\nr_part te_splashbluewater\r\n{\r\n\ttexture \"classicparticle\"\r\n\ttcoords 0 0 16 16 32\r\n\tcount 1\r\n\tscale 1\r\n\talpha 1\r\n\tdie 0.3 0.8\r\n\trandomvel 20\r\n\torgadd 0 31\r\n\tspawnorg 4\r\n\tgravity 40\r\n\tscalefactor 0.8\r\n\tcolorindex 0xb0\r\n}\r\nr_part te_splashbrownwater\r\n{\r\n\ttexture \"classicparticle\"\r\n\ttcoords 0 0 16 16 32\r\n\tcount 1\r\n\tscale 1\r\n\talpha 1\r\n\tdie 0.3 0.8\r\n\trandomvel 20\r\n\torgadd 0 31\r\n\tspawnorg 4\r\n\tgravity 40\r\n\tscalefactor 0.8\r\n\tcolorindex 0x50\r\n}\r\nr_part te_splashslime\r\n{\r\n\ttexture \"classicparticle\"\r\n\ttcoords 0 0 16 16 32\r\n\tcount 1\r\n\tscale 1\r\n\talpha 1\r\n\tdie 0.3 0.8\r\n\trandomvel 20\r\n\torgadd 0 31\r\n\tspawnorg 4\r\n\tgravity 40\r\n\tscalefactor 0.8\r\n\tcolorindex 0xd0\r\n}\r\nr_part te_splashlava\r\n{\r\n\ttexture \"classicparticle\"\r\n\ttcoords 0 0 16 16 32\r\n\tcount 1\r\n\tscale 1\r\n\talpha 1\r\n\tdie 0.3 0.8\r\n\trandomvel 20\r\n\torgadd 0 31\r\n\tspawnorg 4\r\n\tgravity 40\r\n\tscalefactor 0.8\r\n\tcolorindex 0xe0\r\n}\r\nr_part te_splashelect\r\n{\r\n\ttexture \"classicparticle\"\r\n\ttcoords 0 0 16 16 32\r\n\tcount 20\r\n\tscale 1\r\n\talpha 1\r\n\tdie 0.3 0.5\r\n\trandomvel 20\r\n\torgadd 0 31\r\n\tspawnorg 4\r\n\tgravity 40\r\n\tscalefactor 0.8\r\n\tcolorindex 0xb0\r\n}\r\nr_part te_splashblood\r\n{\r\n\ttexture \"classicparticle\"\r\n\ttcoords 0 0 16 16 32\r\n\tcount 1\r\n\tscale 1\r\n\talpha 1\r\n\tdie 0.3 0.8\r\n\trandomvel 20\r\n\torgadd 0 31\r\n\tspawnorg 4\r\n\tgravity 40\r\n\tscalefactor 0.8\r\n\tcolorindex 0xe8\r\n}\r\n\r\nr_part teq2_laser_sparks\r\n{\r\n\ttexture \"classicparticle\"\r\n\ttcoords 0 0 16 16 32\r\n\tcount 1\r\n\tscale 1\r\n\talpha 1\r\n\tdie 0.3 0.8\r\n\trandomvel 20\r\n\torgadd 0 7\r\n\tspawnorg 4\r\n\tgravity 40\r\n\tscalefactor 0.8\r\n}\r\nr_part teq2_welding_sparks\r\n{\t//identical to teq2_laser_sparks, except for the +form that adds in some extra mesh+lighting effect.\r\n\ttexture \"classicparticle\"\r\n\ttcoords 0 0 16 16 32\r\n\tcount 1\r\n\tscale 1\r\n\talpha 1\r\n\tdie 0.3 0.8\r\n\trandomvel 20\r\n\torgadd 0 7\r\n\tspawnorg 4\r\n\tgravity 40\r\n\tscalefactor 0.8\r\n}\r\nr_part +teq2_welding_sparks\r\n{\r\n\tcount 0 0 1\r\n\tmodel \"models/objects/flash/tris.md2\" framestart=0 frameend=2 framerate=10 alpha=-1 fullbright\r\n\tlightradius 100 175\r\n\tlightradiusfade 400\r\n\tlightrgb 1 1 0.3\r\n}\r\nr_part teq2_tunnel_sparks\r\n{\t//this is apparently identical to teq2_laser_sparks... either way the protocol provides a palette colour (particle system provides a customised variation)\r\n\ttexture \"classicparticle\"\r\n\ttcoords 0 0 16 16 32\r\n\tcount 1\r\n\tscale 1\r\n\talpha 1\r\n\tdie 0.3 0.8\r\n\trandomvel 20\r\n\torgadd 0 7\r\n\tspawnorg 4\r\n\tgravity 40\r\n\tscalefactor 0.8\r\n}\r\nr_part teq2_shield_sparks\r\n{\r\n\ttexture \"classicparticle\"\r\n\ttcoords 0 0 16 16 32\r\n\tcount 40\r\n\tcolorindex 0xb0\r\n\tscale 1\r\n\talpha 1\r\n\tdie 0.3 0.8\r\n\trandomvel 20\r\n\torgadd 0 31\r\n\tspawnorg 4\r\n\tgravity 40\r\n\tscalefactor 0.8\r\n}\r\nr_part teq2_screen_sparks\r\n{\r\n\ttexture \"classicparticle\"\r\n\ttcoords 0 0 16 16 32\r\n\tcount 40\r\n\tcolorindex 0xd0\r\n\tscale 1\r\n\talpha 1\r\n\tdie 0.3 0.8\r\n\trandomvel 20\r\n\torgadd 0 31\r\n\tspawnorg 4\r\n\tgravity 40\r\n\tscalefactor 0.8\r\n}\r\nr_part teq2_bullet_sparks\r\n{\r\n\ttexture \"classicparticle\"\r\n\ttcoords 0 0 16 16 32\r\n\tcount 6\r\n\tcolorindex 0xe0\r\n\tscale 1\r\n\talpha 1\r\n\tdie 0.3 0.8\r\n\trandomvel 20\r\n\torgadd 0 31\r\n\tspawnorg 4\r\n\tgravity 40\r\n\tscalefactor 0.8\r\n}\r\n\r\nr_part q2_smoke\r\n{\r\n\tcount 0 0 1\r\n\tmodel \"models/objects/smoke/tris.md2\" framestart=0 frameend=4 framerate=10 alpha=1\r\n}\r\nr_part q2_smokeandflash\r\n{\r\n\tcount 0 0 1\r\n\tmodel \"models/objects/flash/tris.md2\" framestart=0 frameend=2 framerate=10 alpha=-1 fullbright\r\n\tassoc q2_smoke\r\n}\r\n\r\nr_part teq2_gunshot\t/*machinegun*/\r\n{\r\n\ttexture \"classicparticle\"\r\n\ttcoords 0 0 16 16 32\r\n\tcount 40\r\n\tscale 1\r\n\talpha 1\r\n\tdie 0.3 0.8\r\n\trandomvel 20\r\n\torgadd 0 31\r\n\tspawnorg 4\r\n\tgravity 40\r\n\tscalefactor 0.8\r\n\tcolorindex 0 7\r\n\t/*smoke puff models*/\r\n\tassoc q2_smokeandflash\r\n\t/*low chance of various sounds*/\r\n\tsound world/ric1.wav 1 1 0 0 1\r\n\tsound world/ric2.wav 1 1 0 0 1\r\n\tsound world/ric3.wav 1 1 0 0 1\r\n\tsound \"\" 1 1 0 0 12\r\n}\r\n\r\nr_part teq2_shotgun\t/*shotgun... duh*/\r\n{\r\n\ttexture \"classicparticle\"\r\n\ttcoords 0 0 16 16 32\r\n\tcount 20\r\n\tscale 1\r\n\talpha 1\r\n\tdie 0.3 0.8\r\n\trandomvel 20\r\n\torgadd 0 31\r\n\tspawnorg 4\r\n\tgravity 40\r\n\tscalefactor 0.8\r\n\tcolorindex 0 7\r\n\t/*smoke puff models*/\r\n\tassoc q2_smokeandflash\r\n}\r\n\r\nr_part teq2_blood\r\n{\r\n\ttexture \"classicparticle\"\r\n\ttcoords 0 0 16 16 32\r\n\tcount 60\r\n\tscale 1\r\n\talpha 1\r\n\tdie 0.3 0.8\r\n\trandomvel 20\r\n\torgadd 0 31\r\n\tspawnorg 4\r\n\tgravity 40\r\n\tscalefactor 0.8\r\n\tcolorindex 232 7\r\n}\r\nr_part teq2_moreblood\r\n{\t//teq2_blood, but with count 250 instead of 60.\r\n\ttexture \"classicparticle\"\r\n\ttcoords 0 0 16 16 32\r\n\tcount 250\r\n\tscale 1\r\n\talpha 1\r\n\tdie 0.3 0.8\r\n\trandomvel 20\r\n\torgadd 0 31\r\n\tspawnorg 4\r\n\tgravity 40\r\n\tscalefactor 0.8\r\n\tcolorindex 232 7\r\n}\r\nr_part teq2_greenblood\r\n{\r\n\ttexture \"classicparticle\"\r\n\ttcoords 0 0 16 16 32\r\n\tcount 30\r\n\tscale 1\r\n\talpha 1\r\n\tdie 0.3 0.8\r\n\trandomvel 20\r\n\torgadd 0 31\r\n\tspawnorg 4\r\n\tgravity 40\r\n\tscalefactor 0.8\r\n\tcolorindex 0xdf 7\r\n}\r\n\r\nr_part teq2_electric_sparks\r\n{\r\n\ttexture \"classicparticle\"\r\n\ttcoords 0 0 16 16 32\r\n\tcount 40\r\n\tscale 1\r\n\talpha 1\r\n\tdie 0.3 0.8\r\n\trandomvel 20\r\n\torgadd 0 31\r\n\tspawnorg 4\r\n\tgravity 40\r\n\tscalefactor 0.8\r\n\tcolorindex 0x75 7\r\n\tsound \"weapons/lashit.wav\" 1 1 0 0\r\n}\r\n\r\nr_part q2_blasterpuff\r\n{\r\n\tcount 0 0 1\r\n\tmodel \"models/objects/explode/tris.md2\" framestart=0 frameend=4 framerate=10 alpha=1 orient additive fullbright noshadow skin=0\r\n}\r\nr_part q2_blaster2puff\r\n{\r\n\tcount 0 0 1\r\n\tmodel \"models/objects/explode/tris.md2\" framestart=0 frameend=4 framerate=10 alpha=1 orient additive fullbright noshadow skin=1\r\n}\r\nr_part q2_flechettepuff\r\n{\r\n\tcount 0 0 1\r\n\tmodel \"models/objects/explode/tris.md2\" framestart=0 frameend=4 framerate=10 alpha=1 orient additive fullbright noshadow skin=2\r\n}\r\nr_part teq2_blaster\r\n{\r\n\ttexture \"classicparticle\"\r\n\ttcoords 0 0 16 16 32\r\n\tcount 60\r\n\tscale 1\r\n\talpha 1\r\n\tdie 0.3 0.8\r\n\trandomvel 40\r\n\torgadd 0 15\r\n\tveladd 30\r\n\tspawnorg 4\r\n\tgravity 40\r\n\tscalefactor 0.8\r\n\tcolorindex 0xe0 7\r\n\tassoc q2_blasterpuff /*the model*/\r\n\tlightradius 150\r\n\tlightradiusfade 400\r\n\tlightrgb 1 1 0\r\n\tlightshadows 1\r\n\tsound \"weapons/lashit.wav\" 1 1 0 0\r\n}\r\nr_part teq2_blaster2\r\n{\t//green version.\r\n\ttexture \"classicparticle\"\r\n\ttcoords 0 0 16 16 32\r\n\tcount 60\r\n\tscale 1\r\n\talpha 1\r\n\tdie 0.3 0.8\r\n\trandomvel 40\r\n\torgadd 0 15\r\n\tveladd 30\r\n\tspawnorg 4\r\n\tgravity 40\r\n\tscalefactor 0.8\r\n\tcolorindex 0xd0 7\r\n\tassoc q2_blaster2puff /*the model*/\r\n\tlightradius 150\r\n\tlightradiusfade 400\r\n\tlightrgb 0.05 1.0 0.05\r\n\tlightshadows 1\r\n\tsound \"weapons/lashit.wav\" 1 1 0 0\r\n}\r\nr_part teq2_flechette\r\n{\t//grey version.\r\n\ttexture \"classicparticle\"\r\n\ttcoords 0 0 16 16 32\r\n\tcount 60\r\n\tscale 1\r\n\talpha 1\r\n\tdie 0.3 0.8\r\n\trandomvel 40\r\n\torgadd 0 15\r\n\tveladd 30\r\n\tspawnorg 4\r\n\tgravity 40\r\n\tscalefactor 0.8\r\n\tcolorindex 0x6f 7\r\n\tassoc q2_flechettepuff /*the model*/\r\n\tlightradius 150\r\n\tlightradiusfade 400\r\n\tlightrgb 0.19 0.41 0.75\r\n\tlightshadows 1\r\n\tsound \"weapons/lashit.wav\" 1 1 0 0\r\n}\r\nr_part teq2_bluehyperblaster\r\n{\t//misnamed - just the regular orangey particles without sound/puff\r\n\ttexture \"classicparticle\"\r\n\ttcoords 0 0 16 16 32\r\n\tcount 60\r\n\tscale 1\r\n\talpha 1\r\n\tdie 0.3 0.8\r\n\trandomvel 40\r\n\torgadd 0 15\r\n\tveladd 30\r\n\tspawnorg 4\r\n\tgravity 40\r\n\tscalefactor 0.8\r\n\tcolorindex 0xe0 7\r\n}\r\nr_part TR_BLASTERTRAIL\r\n{\r\n\ttexture \"classicparticle\"\r\n\ttcoords 0 0 16 16 32\r\n\tscale 0.5\r\n\talpha 1\r\n\tscalefactor 0.8\r\n\tstep 5\r\n\tspawnorg 1\r\n\trandomvel 5\r\n\tdie 0.3 0.5\r\n\tcolorindex 0xe0\r\n\tlightradius 200\r\n\tlightradiusfade 400\r\n\tlightrgb 1.0 1.0 0.0\r\n\tlightshadows 1\r\n}\r\n\r\n//green version\r\nr_part TR_BLASTERTRAIL2\r\n{\r\n\ttexture \"classicparticle\"\r\n\ttcoords 0 0 16 16 32\r\n\tscale 0.5\r\n\talpha 1\r\n\tscalefactor 0.8\r\n\tstep 5\r\n\tspawnorg 1\r\n\trandomvel 5\r\n\tdie 0.3 0.5\r\n\tcolorindex 0xd0\r\n\tlightradius 200\r\n\tlightradiusfade 400\r\n\tlightrgb 0.0 1.0 0.0\r\n\tlightshadows 1\r\n}\r\n\r\n\r\nr_part teq2_bubbletrail\r\n{\r\n\ttexture \"classicparticle\"\r\n\ttcoords 0 0 16 16 32\r\n\tscale 0.5\r\n\talpha 1\r\n\tscalefactor 0.8\r\n\tstep 32\r\n\tspawnorg 2\r\n\tspawnvel 5\r\n\tdie 1 1.2\r\n\tcolorindex 4 7\r\n\tvelbias 0 0 6\r\n}\r\nr_part teq2_bubbletrail2\r\n{\r\n\ttexture \"classicparticle\"\r\n\ttcoords 0 0 16 16 32\r\n\tscale 0.5\r\n\talpha 1\r\n\tscalefactor 0.8\r\n\tstep 8\r\n\tspawnorg 2\r\n\tspawnvel 10\r\n\tdie 1 1.1\r\n\tcolorindex 4 7\r\n\tvelbias 0 0 20\r\n\tsound \"weapons/lashit.wav\" 1 1 0 0\r\n}\r\n\r\nr_part teq2_railtrail\r\n{\r\n\t//blue spiral\r\n\ttexture \"classicparticle\"\r\n\ttcoords 0 0 16 16 32\r\n\tscale 0.5\r\n\talpha 1\r\n\tscalefactor 0.8\r\n\tstep 1\r\n\tspawnmode spiral 64\r\n\tspawnorg 3\r\n\tspawnvel 6\r\n\tdie 1 1.2\r\n\tcolorindex 116 7\r\n\r\n\tsound \"weapons/railgf1a.wav\" 1 1 0 0\r\n}\r\nr_part +teq2_railtrail\r\n{\r\n\t//grey filler\r\n\ttexture \"classicparticle\"\r\n\ttcoords 0 0 16 16 32\r\n\tscale 0.5\r\n\talpha 1\r\n\tscalefactor 0.8\r\n\tstep 0.75\r\n\tspawnorg 3\r\n\tspawnvel 3\r\n\tdie 0.6 0.8\r\n\tcolorindex 0 15\r\n}\r\n\r\nr_part teq2_railtrail2\r\n{\t//This is not implemented in vanilla, so we've no idea what it should really look like.\r\n\t//we just use the blue spiral with no core.\r\n\t//blue spiral\r\n\ttexture \"classicparticle\"\r\n\ttcoords 0 0 16 16 32\r\n\tscale 0.5\r\n\talpha 1\r\n\tscalefactor 0.8\r\n\tstep 1\r\n\tspawnmode spiral 64\r\n\tspawnorg 3\r\n\tspawnvel 6\r\n\tdie 1 1.2\r\n\tcolorindex 116 7\r\n\r\n\tsound \"weapons/railgf1a.wav\" 1 1 0 0\r\n}\r\n\r\n//regular explosion particles\r\nr_part std_explosion_particles\r\n{\r\n\ttexture \"classicparticle\"\r\n\ttcoords 0 0 16 16 32\r\n\tcount 256\r\n\tscale 4\r\n\talpha 0.4\r\n\tdie 1 0.625\r\n\tspawnmode ball\r\n\tspawnorg 16\r\n\tspawnvel 192\r\n\tscalefactor 0.8\r\n\tgravity 40\r\n\tcolorindex 0xe0 7\r\n}\r\n\r\nr_part teq2_explosion1_big\r\n{\r\n\tlighttime 0.5\r\n\tlightradius 350\r\n\tlightradiusfade 300\r\n\tlightrgb 1.0 0.5 0.4\r\n\tlightrgbfade 0.36 0.19 0.19\r\n\tsound \"weapons/rocklx1a.wav\" 1 1 0 0\r\n\tmodel \"models/objects/r_explode2/tris.md2\" framestart=0 frames=15 skin=-1 transparent fullbright noshadow\r\n\tmodel \"models/objects/r_explode2/tris.md2\" framestart=15 frames=15 skin=-1 transparent fullbright noshadow\r\n}\r\nr_part teq2_explosion1_np\r\n{\r\n\tlighttime 0.5\r\n\tlightradius 350\r\n\tlightradiusfade 300\r\n\tlightrgb 1.0 0.5 0.4\r\n\tlightrgbfade 0.36 0.19 0.19\r\n\tsound \"weapons/rocklx1a.wav\" 1 1 0 0\r\n\tmodel \"models/objects/r_explode/tris.md2\" framestart=0 frames=15 skin=-1 fullbright noshadow\r\n\tmodel \"models/objects/r_explode/tris.md2\" framestart=15 frames=150 skin=-1 fullbright noshadow\r\n}\r\nr_part teq2_explosion1\r\n{\r\n\tassoc teq2_rocket_explosion\r\n}\r\nr_part teq2_rocket_explosion\r\n{\r\n\tassoc std_explosion_particles\r\n\tlighttime 0.5\r\n\tlightradius 350\r\n\tlightradiusfade 300\r\n\tlightrgb 1.0 0.5 0.4\r\n\tlightrgbfade 0.36 0.19 0.19\r\n\tsound \"weapons/rocklx1a.wav\" 1 1 0 0\r\n\tmodel \"models/objects/r_explode/tris.md2\" framestart=0 frames=15 skin=-1 fullbright noshadow\r\n\tmodel \"models/objects/r_explode/tris.md2\" framestart=15 frames=15 skin=-1 fullbright noshadow\r\n}\r\nr_part teq2_rocket_explosion_water\r\n{\r\n\tassoc std_explosion_particles\r\n\tlighttime 0.5\r\n\tlightradius 350\r\n\tlightradiusfade 300\r\n\tlightrgb 1.0 0.5 0.4\r\n\tlightrgbfade 0.36 0.19 0.19\r\n\tsound \"weapons/xpld_wat.wav\" 1 1 0 0\r\n\tmodel \"models/objects/r_explode/tris.md2\" framestart=0 frames=15 skin=-1 fullbright noshadow\r\n\tmodel \"models/objects/r_explode/tris.md2\" framestart=15 frames=15 skin=-1 fullbright noshadow\r\n}\r\n\r\nr_part teq2_explosion2\r\n{\r\n\tassoc teq2_grenade_explosion\r\n}\r\nr_part teq2_grenade_explosion\r\n{\r\n\tassoc std_explosion_particles\r\n\tlighttime 0.5\r\n\tlightradius 350\r\n\tlightradiusfade 300\r\n\tlightrgb 1.0 0.5 0.4\r\n\tlightrgbfade 0.36 0.19 0.19\r\n\tsound \"weapons/grenlx1a.wav\" 1 1 0 0\r\n\tmodel \"models/objects/r_explode/tris.md2\" framestart=30 frames=19 skin=-1 fullbright noshadow\r\n}\r\nr_part teq2_grenade_explosion_water\r\n{\r\n\tassoc std_explosion_particles\r\n\tlighttime 0.5\r\n\tlightradius 350\r\n\tlightradiusfade 300\r\n\tlightrgb 1.0 0.5 0.4\r\n\tlightrgbfade 0.36 0.19 0.19\r\n\tsound \"weapons/xpld_wat.wav\" 1 1 0 0\r\n\tmodel \"models/objects/r_explode/tris.md2\" framestart=30 frames=19 skin=-1 fullbright noshadow\r\n}\r\n\r\nr_part teq2_plain_explosion\r\n{\t//basically like regular explosions, but with no particle effect.\r\n\tlighttime 0.5\r\n\tlightradius 350\r\n\tlightradiusfade 300\r\n\tlightrgb 1.0 0.5 0.4\r\n\tlightrgbfade 0.36 0.19 0.19\r\n\tsound \"weapons/rocklx1a.wav\" 1 1 0 0\r\n\tmodel \"models/objects/r_explode/tris.md2\" framestart=0 frames=15 skin=-1 fullbright noshadow\r\n\tmodel \"models/objects/r_explode/tris.md2\" framestart=15 frames=15 skin=-1 fullbright noshadow\r\n}\r\nr_part teq2_plasma_explosion\r\n{\t//not actually any different\r\n\tassoc teq2_explosion1\r\n}\r\n\r\nr_part teq2_tracker_explosion\r\n{\r\n\ttexture \"classicparticle\"\r\n\ttcoords 0 0 16 16 32\r\n\tcount 128\r\n\tscale 1\r\n\talpha 1\r\n\tdie 1.5 2\r\n\trandomvel 128\r\n\torgadd 0 31\r\n\tspawnorg 16\r\n\tgravity 40\r\n\tscalefactor 0.8\r\n\tcolorindex 0 1\r\n\tlighttime 0.1\t//this is kinda too short.\r\n\tlightradius 150\r\n\tlightradiusfade 300\r\n\tlightrgb -1.0 -1.0 -1.0\r\n\tsound \"weapons/disrupthit.wav\" 1 1 0 0\r\n}\r\nr_part teq2_teleport_effect\r\n{\r\n\ttexture \"classicparticle\"\r\n\ttcoords 0 0 16 16 32\r\n\tcount 400\r\n\tscale 1\r\n\talpha 1\r\n\tdie 0.5 0.8\r\n\torgadd 8 -48\r\n\tveladd -100 -0\r\n\tspawnmode circle\r\n\tspawnorg 0 0\r\n\tspawnvel -50 30\r\n\trandomvel -32 -31\r\n\tgravity 0\r\n\tcolorindex 15\r\n\tscalefactor 0.8\r\n}\r\nr_part teq2_dball_goal\r\n{  // same as teq2_teleport_effect\r\n\tassoc teq2_teleport_effect\r\n}\r\nr_part teq2_widowsplash\r\n{  // particle ball that slowly expands\r\n\ttexture \"classicparticle\"\r\n\ttcoords 0 0 16 16 32\r\n\tcount 400\r\n\tscale 1\r\n\talpha 1\r\n\tdie 3 3\r\n\tveladd 10 10\r\n\tspawnmode circle\r\n\tspawnorg 0 0\r\n\trandomvel -10 10\r\n\tgravity 0\r\n\tcolorindex 104 7\r\n\tscalefactor 0.8\r\n}\r\nr_part teq2_debugtrail\r\n{\r\n\ttexture \"classicparticle\"\r\n\ttcoords 0 0 16 16 32\r\n\tscale 1\r\n\talpha 1\r\n\tstep 5\r\n\tdie 5 5\r\n\tcolorindex 111\r\n}\r\nr_part teq2_chainfist_smoke\r\n{\r\n\ttexture \"classicparticle\"\r\n\ttcoords 0 0 16 16 32\r\n\tcount 20\r\n\tscale 1\r\n\talpha 1\r\n\tdie 0.5 0.8\r\n\tspawnorg 2\r\n\trandomvel 6.66 20\r\n\tgravity 40\r\n\tscalefactor 0.8\r\n\tcolorindex 0 7\r\n}\r\n\r\nr_part teq2_flashlight\r\n{\t//JUST a light.\r\n\tlightradius 400\r\n\tlighttime 0.15\r\n\tlightrgb 1.0 1.0 1.0\r\n\tlightshadows 1\r\n}\r\n\r\nr_part trq2_rocket\r\n{\r\n\ttexture \"particles/quake\"\r\n\tstep 8\r\n\tscale 4\r\n\tdie 1.0 1.2\r\n\tcolorindex 0xdc 3\r\n\tspawnorg 1\r\n\tspawnvel 20\r\n\tgravity 40\r\n\tassoc trq2_grenade\r\n}\r\nr_part trq2_grenade\r\n{\r\n\ttexture \"particles/quake\"\r\n\tstep 3\r\n\tscale 4\r\n\tdie 1.0 1.2\r\n\tcolorindex 0x4 7\r\n\tspawnorg 1\r\n\tspawnvel 5\r\n\tgravity -20\r\n}\r\nr_part trq2_gib\r\n{\r\n\ttexture \"particles/quake\"\r\n\tstep 3\r\n\tscale 4\r\n\tdie 1.0 1.4\r\n\tcolorindex 0xe8 7\r\n\tspawnorg 1\r\n\tspawnvel 5\r\n\tgravity -20\r\n}\r\nr_part trq2_greengib\r\n{\r\n\ttexture \"particles/quake\"\r\n\tstep 3\r\n\tscale 4\r\n\tdie 1.0 1.4\r\n\tcolorindex 0xdb 7\r\n\tspawnorg 1\r\n\tspawnvel 5\r\n\tgravity -20\r\n}\r\n\r\nr_part TR_PLASMA\r\n{\r\n\tassoc TR_BLASTERTRAIL\r\n}\r\n\r\nr_part tr_ionripper\r\n{\r\n\ttexture \"classicparticle\"\r\n\ttcoords 0 0 16 16 32\r\n\tstep 3\r\n\tscale 1\r\n\talpha 0.5\r\n\tdie 0.15 0.25\r\n\tcolorindex 0xe4 3\r\n\tspawnmode tracer\r\n\tspawnorg 0\r\n\tspawnvel 10\r\n\r\n\tlighttime 0\r\n\tlightradius 100\r\n\tlightrgb 1.0 0.5 0.5\r\n}\r\n\r\nr_part tr_tracker\r\n{\t//FIXME: doesn't match vanilla. works well enough though I guess.\r\n\ttexture \"classicparticle\"\r\n\ttcoords 0 0 16 16 32\r\n\tstep 3\r\n\tscale 1\r\n\talpha 1\r\n\tdie 0.5\r\n\tspawnmode spiral 8.34\r\n\tspawnorg 1\r\n\tspawnvel 32\r\n\tveladd 32\r\n\tscalefactor 0\r\n\tcolorindex 0\r\n\tlighttime 0\r\n\tlightradius 200\r\n\tlightrgb -1.0 -1.0 -1.0\r\n}\r\n\r\nr_part tr_tagtrail\r\n{\r\n\ttexture \"classicparticle\"\r\n\ttcoords 0 0 16 16 32\r\n\tstep 5\r\n\tscale 1\r\n\talpha 1\r\n\tdie 1.0 0.8\r\n\tspawnorg 16\r\n\tspawnvel 5\r\n\tscalefactor 0\r\n\tcolorindex 220\r\n\tlighttime 0\r\n\tlightradius 225\r\n\tlightrgb 1.0 1.0 0.0\r\n}\r\n\r\nr_part tr_trap\r\n{\r\n\ttexture \"classicparticle\"\r\n\ttcoords 0 0 16 16 32\r\n\tcount 30\r\n\tscale 1\r\n\talpha 1\r\n\tdie 0.4 0.8\r\n\trandomvel 8\r\n\tveladd 48\r\n\tcolorindex 0xe0 7\r\n\tspawnmode telebox 0 4\r\n\tspawnorg 8 16\r\n\tlighttime 0\r\n\tlightradius 100 200\r\n\tlightrgb 1.0 0.8 0.25\r\n}\r\n\r\nr_part +tr_trap\r\n{\r\n\ttexture \"classicparticle\"\r\n\ttcoords 0 0 16 16 32\r\n\tcount 128\r\n\tscale 1\r\n\talpha 1\r\n\tdie 0.4 0.8\r\n\trandomvel 8\r\n\tveladd 20\r\n\tcolorindex 58\r\n\tspawnmode telebox 0 4\r\n\tspawnorg 1 48\r\n}\r\n\r\nr_part ef_trap\r\n{\t//FIXME\r\n\tplaceholder\r\n}\r\n\r\n//flags do NOT use coronas, because it obscures the holding player's skin colour\r\nr_part tr_flag1\r\n{\r\n\ttexture \"classicparticle\"\r\n\ttcoords 0 0 16 16 32\r\n\tstep 5\r\n\tscale 1\r\n\talpha 1\r\n\tdie 1.0 0.8\r\n\tspawnorg 16\r\n\tspawnvel 5\r\n\tveladd 32\r\n\tscalefactor 0\r\n\tcolorindex 0xf2\r\n\tlighttime 0\r\n\tlightcorona 0.0 0.0\r\n\tlightradius 225\r\n\tlightrgb 1.0 0.25 0.25\r\n}\r\nr_part tr_flag2\r\n{\r\n\ttexture \"classicparticle\"\r\n\ttcoords 0 0 16 16 32\r\n\tstep 5\r\n\tscale 1\r\n\talpha 1\r\n\tdie 1.0 0.8\r\n\tspawnorg 16\r\n\tspawnvel 5\r\n\tveladd 32\r\n\tscalefactor 0\r\n\tcolorindex 0x73\r\n\tlighttime 0\r\n\tlightcorona 0.0 0.0\r\n\tlightradius 225\r\n\tlightrgb 0.25 0.25 1.0\r\n}\r\n\r\nr_part EF_FLIES\r\n{\r\n\ttexture \"classicparticle\"\r\n\ttcoords 0 0 16 16 32\r\n\tcount 1\r\n\tscale 0.5\r\n\talpha 1\r\n\tdie 0\r\n\tspawnmode syncfield 16 64\r\n\tspawnorg 0\r\n\tscalefactor 0\r\n\tcolorindex 0\r\n}\r\n\r\nr_part EF_BFGPARTICLES\r\n{\r\n\ttexture \"classicparticle\"\r\n\ttcoords 0 0 16 16 32\r\n\tcount 0 0 162\r\n\tscale 0.5\r\n\talpha 1\r\n\tdie 0\r\n\tspawnmode syncfield 16 64\r\n\tspawnorg 0\r\n\tscalefactor 0\r\n\tcolorindex 0xd0 7\r\n}\r\n\r\nr_part ev_item_respawn\r\n{\r\n\tsound \"items/respawn1.wav\" 1 2 0 0 1\r\n\ttexture \"classicparticle\"\r\n\ttcoords 0 0 16 16 32\r\n\tcount 64\r\n\tscale 1\r\n\talpha 1\r\n\tdie 1.3 1\r\n\trandomvel 8\r\n\torgadd 0 31\r\n\tspawnorg 8\r\n\tgravity 8\r\n\tscalefactor 0.8\r\n\tcolorindex 0xd4 3\r\n}\r\nr_part ev_player_teleport\r\n{\r\n\tsound \"misc/tele1.wav\" 1 2 0 0 1\r\n\ttexture \"classicparticle\"\r\n\ttcoords 0 0 16 16 32\r\n\tcount 96\r\n\tscale 1\r\n\talpha 1\r\n\tdie 0.3 0.4\r\n\trandomvel 8\r\n\torgadd 0 31\r\n\tspawnmode telebox 0 4\r\n\tspawnorg 32 48\r\n\tgravity 40\r\n\tscalefactor 0.8\r\n\tcolorindex 0x07 7\r\n}\r\nr_part ev_footstep\r\n{\r\n\tsound \"player/step1.wav\" 1 1 0 0 1\r\n\tsound \"player/step2.wav\" 1 1 0 0 1\r\n\tsound \"player/step3.wav\" 1 1 0 0 1\r\n\tsound \"player/step4.wav\" 1 1 0 0 1\r\n}\r\nr_part ev_other_footstep\r\n{\t//q2e - same but with idle attenuation\r\n    sound \"player/step1.wav\" 1 2 0 0 1\r\n    sound \"player/step2.wav\" 1 2 0 0 1\r\n    sound \"player/step3.wav\" 1 2 0 0 1\r\n    sound \"player/step4.wav\" 1 2 0 0 1\r\n}\r\n\r\n//central explosion\r\nr_part teq2_bfg_bigexplosion\r\n{\r\n\ttexture \"classicparticle\"\r\n\ttcoords 0 0 16 16 32\r\n\tcount 256\r\n\tscale 1\r\n\talpha 1\r\n\tdie 0.625 1\r\n\tspawnmode ball\r\n\tspawnorg 16\r\n\tspawnvel 192\r\n\tscalefactor 0.8\r\n\tgravity 40\r\n\tcolorindex 0xd0 7\r\n}\r\n\r\n//splashed onto an entity\r\nr_part teq2_bfg_explosion\r\n{\r\n\tlighttime 0.5\r\n\tlightradius 350\r\n\tlightradiusfade 300\r\n\tlightrgb 0.0 1.0 0.0\r\n\tlightrgbfade 0.0 0.0 0.0\r\n\tsound \"weapons/xpld_wat.wav\" 1 1 0 0\r\n\tmodel \"sprites/s_bfg2.sp2\" framestart=0 frameend=4 alpha=0.3 transparent fullbright noshadow\r\n}\r\n\r\n\r\n//31qu cylinder, 8-98 high\r\n//should look like its sucked up into some thingie above\r\nr_part TEQ2_BOSSTPORT\r\n{\r\n\ttexture \"classicparticle\"\r\n\ttcoords 0 0 16 16 32\r\n\tcount 800\r\n\tscale 1\r\n\talpha 1\r\n\tdie 0.5 0.8\r\n\torgadd 8 -98\r\n\tveladd 100 200\r\n\tspawnmode circle\r\n\tspawnorg 48 0\r\n\tspawnvel -50 30\r\n\trandomvel 32 31\r\n\tgravity -800\r\n\trgbf 1 1 1\r\n\tscalefactor 0.8\r\n\tsound \"misc/bigtele.wav\" 1 0 0 0 1\r\n}\r\n\r\nr_part teq2_heatbeam_sparks\r\n{\r\n\ttexture \"classicparticle\"\r\n\ttcoords 0 0 16 16 32\r\n\tcount 30\r\n\tscale 1\r\n\talpha 1\r\n\tdie 0.3 0.8\r\n\tveladd 64\r\n\torgadd 0 16\r\n\tspawnorg 4\r\n\tgravity 40\r\n\tscalefactor 0.8\r\n\tcolorindex 9 7\r\n}\r\nr_part teq2_heatbeam_steam\r\n{\r\n\ttexture \"classicparticle\"\r\n\ttcoords 0 0 16 16 32\r\n\tcount 30\r\n\tscale 1\r\n\talpha 1\r\n\tdie 0.2 0.4\r\n\trandomvel 40\r\n\torgadd 0 15\r\n\tveladd 30\r\n\tspawnorg 4\r\n\tgravity 40\r\n\tscalefactor 0.8\r\n\tcolorindex 0xe0 7\r\n}\r\n\r\n//r_part teq2_heatbeam_steam\r\n//{\r\n//\tcount 20\r\n//\tcolorindex 0xe0 7\r\n////\tmagnitude 60\r\n//\ttexture \"classicparticle\"\r\n//\ttcoords 0 0 16 16 32\r\n//\tscale 1\r\n//\talpha 1\r\n//\tdie 0.3 0.8\r\n//\trandomvel 20 magnitude/3\r\n//\tveladd magnitude\r\n//\torgadd magnitude/10\r\n//\tspawnorg 4\r\n//\tgravity -400\r\n//\tscalefactor 0.8\r\n//}\r\n\r\n\r\n//this is apparently just a trail effect (palette index specified by netcode)\r\nr_part teq2_forcewall\r\n{\r\n\ttexture \"classicparticle\"\r\n\ttcoords 0 0 16 16 32\r\n\tscale 0.5\r\n\talpha 1\r\n\tscalefactor 0.8\r\n\tstep 5\r\n\tspawnorg 3\r\n\trandomvel 5\r\n\tdie 3 3.5\r\n}\r\n\r\nr_part teq2ex_bluehyperblaster_puff\r\n{\r\n\tcount 0 0 1\r\n\tmodel \"models/objects/explode/tris.md2\" framestart=0 frameend=4 framerate=10 alpha=1 orient additive fullbright noshadow skin=1\r\n}\r\n\r\nr_part teq2ex_bluehyperblaster\r\n{\r\n\ttexture \"classicparticle\"\r\n\ttcoords 0 0 16 16 32\r\n\tcount 60\r\n\tscale 1\r\n\talpha 1\r\n\tdie 0.3 0.8\r\n\trandomvel 40\r\n\torgadd 0 15\r\n\tveladd 30\r\n\tspawnorg 4\r\n\tgravity 40\r\n\tscalefactor 0.8\r\n\tcolorindex 111 108\r\n\tassoc teq2ex_bluehyperblaster_puff\r\n\tlightradius 150\r\n\tlightradiusfade 400\r\n\tlightrgb 0 0 1\r\n\tlightshadows 1\r\n\tsound \"weapons/lashit.wav\" 1 1 0 0\r\n}\r\n\r\nr_part teq2ex_bfgzap_end\r\n{\r\n\tassoc teq2_bfg_explosion\r\n}\r\n\r\nr_part teq2ex_bfgzap\r\n{\t// green bolt-beam with small green explosion sprite at the end\r\n\ttexture \"particles/fteparticlefont.tga\"\r\n\ttcoords 97 97 191 191 256\r\n\tscale 4\r\n\talpha 1.0\r\n\tstep 4\r\n\trandomvel 0\r\n\ttype beam\r\n\tdie 1\r\n\tcolorindex 0xd0\r\n}\r\n\r\nr_part teq2ex_berserk_slampuff\r\n{\r\n\tcount 0 0 1\r\n\tmodel \"models/objects/explode/tris.md2\" framestart=0 frameend=4 framerate=10 alpha=1 orient additive fullbright noshadow skin=3\r\n}\r\nr_part teq2ex_berserk_slam\r\n{\r\n\ttexture \"classicparticle\"\r\n\ttcoords 0 0 16 16 32\r\n\tcount 256\r\n\tscale 1\r\n\talpha 1\r\n\tdie 0.625 1\r\n\tspawnmode ball\r\n\tspawnorg 16\r\n\tspawnvel 192\r\n\tscalefactor 0.8\r\n\tgravity 40\r\n\tcolorindex 111 108\r\n\tassoc teq2ex_berserk_slampuff\r\n}\r\nr_part teq2ex_grapple_cable_2\r\n{\r\n\ttexture \"particles/fteparticlefont.tga\"\r\n\ttcoords 97 97 191 191 256\r\n\tscale 2\r\n\talpha 1.0\r\n\tstep 4\r\n\trandomvel 0\r\n\ttype beam\r\n\tdie 0.25\r\n\tcolorindex 6\r\n}\r\nr_part teq2ex_power_splash\r\n{\r\n\ttexture \"classicparticle\"\r\n\ttcoords 0 0 16 16 32\r\n\tcount 256\r\n\tscale 4\r\n\talpha 0.4\r\n\tdie 1 0.625\r\n\tspawnmode ball\r\n\tspawnorg 16\r\n\tspawnvel 192\r\n\tscalefactor 0.8\r\n\tgravity 40\r\n\tcolorindex 208 209\r\n}\r\nr_part teq2ex_lightning_beam\r\n{\r\n\ttexture \"particles/fteparticlefont.tga\"\r\n\ttcoords 97 97 191 191 256\r\n\tscale 2\r\n\talpha 1.0\r\n\tstep 4\r\n\trandomvel 0\r\n\ttype beam\r\n\tdie 0.25\r\n\tcolorindex 108\r\n}\r\nr_part teq2ex_explosion1_nl\r\n{\t// the _nl stands for 'no light'\r\n\tassoc std_explosion_particles\r\n\tsound \"weapons/rocklx1a.wav\" 1 1 0 0\r\n\tmodel \"models/objects/r_explode/tris.md2\" framestart=0 frames=15 skin=-1 fullbright noshadow\r\n}\r\nr_part teq2ex_explosion2_nl\r\n{\t// ditto\r\n\tassoc std_explosion_particles\r\n\tsound \"weapons/rocklx1a.wav\" 1 1 0 0\r\n\tmodel \"models/objects/r_explode/tris.md2\" framestart=15 frames=15 skin=-1 fullbright noshadow\r\n}\r\nr_part ef_trackershell\r\n{\t// the black glob model thing\r\n\tmodel \"models/objects/r_explode/tris.md2\" framestart=30 frames=19 skin=-1 fullbright noshadow\r\n}\r\nr_part ev_ladder_step\r\n{\r\n\tsound \"player/steps/ladder1.wav\" 1 1 0 0 1\r\n\tsound \"player/steps/ladder2.wav\" 1 1 0 0 1\r\n\tsound \"player/steps/ladder3.wav\" 1 1 0 0 1\r\n\tsound \"player/steps/ladder4.wav\" 1 1 0 0 1\r\n\tsound \"player/steps/ladder5.wav\" 1 1 0 0 1\r\n}\r\n"
  },
  {
    "path": "engine/partcfgs/spikeset.cfg",
    "content": "// spikeset, originally by Spike\n// with contributions from TimeServ, purplehaze, Jedilamma\n// and some others I probably forgot to mention\n/////////////////////////////////////////////////\n//rocket trails (derived from purplehaze's, with only minor tweeks)\n\nr_part rocketsmoke\n{\n\ttexture \"particles/fteparticlefont.tga\"\n\ttcoords 97 97 191 191 256\n\tstep 8\n\tscale 7.5\n\talpha 0.8\n\tdie 2\n\trandomvel 3\n\trgb 10 10 10\n\tblend modulate\n\tspawnmode spiral\n\tscalefactor 1\n\tspawnvel 5\n}\n\nr_part rockettrail\n{\n\ttexture \"particles/fteparticlefont.tga\"\n\ttcoords 97 97 191 191 256\n\tstep 4\n\tscale 30\n\talpha 0.3\n\tdie 1.4\n\tdiesubrand 0.7\n\trandomvel 1\n\trgb 255 50 10\n\trgbdelta -230 -45 -9\n\tgravity -25\n\tscalefactor 1\n\tassoc rocketsmoke\n\tspawnvel 10\n}\n\nr_part t_rocket\n{\n\ttexture \"particles/fteparticlefont.tga\"\n\ttcoords 97 97 191 191 256\n\tstep 2\n\tscale 10\n\talpha 0.6\n\tdie 0.25\n\trgb 255 192 128\n\trgbdelta -14 -300 -300\n\tblend add\n\tassoc rockettrail\n\tscalefactor 0.8\n\tscaledelta -10\n}\n\nr_part rockettail\n{\n\ttexture \"particles/fteparticlefont.tga\"\n\ttcoords 97 97 191 191 256\n\tstep 7\n\tscale 10\n\talpha 0.3\n\tdie 10\n\trandomvel 64\n\tveladd 512\n\trgb 192 192 192\n\tgravity 100\n\tcliptype rockettail\n}\n\nr_part t_altrocket\n{\n\ttexture \"particles/fteparticlefont.tga\"\n\ttcoords 97 97 191 191 256\n\tstep 4\n\tscale 10\n\talpha 0.3\n\tdie 0.7\n\trandomvel 32\n\tveladd 32\n\trgb 255 198 128\n\trgbdelta -64 0 0\n\tgravity -100\n\tblend add\n\tassoc rockettail\n}\n\n// te_railtrail, used with Quake 2 railgun and also used with\n// TeamFortress engineer railgun\nr_part railtrailinner\n{\n\tstep 30\n\tscale 5\n\tdie 1\n\talpha 0.5\n\trgb 255 255 255\n\tblend add\n\ttype beam\n\tspawnvel 2 2\n}\n\nr_part railtrail240\n{\n\tstep 15\n\tscale 3\n\tdie 1\n\talpha 0\n\trgb 32 32 255\n\trampmode delta\n\tramp -255 -255 0 -2.5 0\n\tramp 0 0 0 0.65 0\n\tramp 0 0 0 0.65 0\n\tramp 0 0 0 0.65 0\n\tramp 0 0 -128 0.65 10\n\tblend add\n\ttype beam\n\tspawnmode spiral\n\tspawnparam1 256\n\tspawnparam2 240\n\tspawnvel 12\n\tassoc railtrailinner\n}\n\nr_part railtrail120\n{\n\tstep 15\n\tscale 3\n\tdie 1\n\talpha 0\n\trgb 32 32 255\n\trampmode delta\n\tramp -255 -255 0 -2.5 0\n\tramp 0 0 0 0.65 0\n\tramp 0 0 0 0.65 0\n\tramp 0 0 0 0.65 0\n\tramp 0 0 -128 0.65 10\n\tblend add\n\ttype beam\n\tspawnmode spiral\n\tspawnparam1 256\n\tspawnparam2 120\n\tspawnvel 12\n\tassoc railtrail240\n}\n\nr_part te_railtrail\n{\n\tstep 15\n\tscale 3\n\tdie 1\n\talpha 0\n\trgb 32 32 255\n\trampmode delta\n\tramp -255 -255 0 -2.5 0\n\tramp 0 0 0 0.65 0\n\tramp 0 0 0 0.65 0\n\tramp 0 0 0 0.65 0\n\tramp 0 0 -128 0.65 10\n\tblend add\n\ttype beam\n\tspawnmode spiral\n\tspawnparam1 256\n\tspawnvel 12\n\tassoc railtrail120\n}\n\nr_part shortfume\n{\n\ttexture \"particles/fteparticlefont.tga\"\n\ttcoords 97 97 191 191 256\n\tscale 15\n\tscaledelta 20\n\talpha 0.5\n\tstep 8\n\tdie 0.3\n\trandomvel 12\n\tscaledelta 0.81\n\trgb 150 150 150\n}\n\nr_part t_grenade\n{\n\ttexture \"particles/fteparticlefont.tga\"\n\ttcoords 97 97 191 191 256\n\tstep 24\n\tscale 16\n\tscaledelta 4\n\talpha 0.3\n\tdie 4\n\trandomvel 8\n\tveladd 15\n\trgb 140 140 140\n\trgbdelta -55 -55 -55\n\tgravity -50\n\tscalefactor 0.0\n\tassoc shortfume\n}\n\n//cool's blood trails (cos they're cooler)\nr_part t_gib\n{\n\ttexture \"particles/fteparticlefont.tga\"\n\ttcoords 1 1 63 63 256 2 64\n\tstep 32\n\tscale 64\n\talpha 0.6\n\tdie 1\n\trandomvel 64\n\tveladd 10\n\trotationspeed 90\n\trotationstart 0 360\n\trgb 128 0 0\n\tgravity 200\n\tscalefactor 0.8\n\tscaledelta -10\n\tstains 5\n}\n\nr_part t_zomgib\n{\n\ttexture \"particles/fteparticlefont.tga\"\n\ttcoords 1 1 63 63 256 2 64\n\tstep 64\n\tscale 64\n\talpha 0.6\n\tdie 1\n\trandomvel 64\n\tveladd 10\n\trotationspeed 90\n\trotationstart 0 360\n\trgb 32 0 0\n\tgravity 200\n\tscalefactor 0.8\n\tscaledelta -10\n\tstains 5\n}\n\nr_part t_tracer\n{\n\ttexture \"particles/fteparticlefont.tga\"\n\ttcoords 1 97 95 191 256\n\tscale 15\n\tstep 5\n\talpha 0.6\n\trgb 192 192 48\n\tdie 1\n\tveladd 50\n\trandomvel 50\n\tfriction 4\n\tscalefactor 0.825\n}\n\nr_part t_tracer2\n{\n\ttexture \"particles/fteparticlefont.tga\"\n\ttcoords 1 97 95 191 256\n\tscale 15\n\tstep 5\n\talpha 0.6\n\tdie 1\n\trgb 192 96 48\n\tveladd 50\n\trandomvel 50\n\tfriction 4\n\tscalefactor 0.825\n}\n\nr_part t_tracer3\n{\n\ttexture \"particles/fteparticlefont.tga\"\n\ttcoords 1 97 95 191 256\n\tscale 10\n\tscaledelta -10\n\tstep 5\n\talpha 0.9\n\tdie 0.75\n\trgb 192 96 192\n\tveladd 20\n\trandomvel 5\n\tspawnmode spiral\n\tspawnvel 60 0\n\tfriction 4\n\tscalefactor 0.825\n}\n\n//qw blood\nr_part te_lightningblood\n{\n\ttexture \"particles/fteparticlefont.tga\"\n\ttcoords 193 97 255 159 256\n\tcount 3\n\tscale 20\n\talpha 0.4\n\tdie 2\n\trandomvel 32\n\tveladd 32\n\trgb 192 0 0\n\trgbdelta -128 0 0\n\tgravity 100\n\tfriction 1\n\tstains 1\n\tblend add\n}\n\n//qw blood\nr_part te_blood\n{\n\ttexture \"particles/fteparticlefont.tga\"\n\ttcoords 193 97 255 159 256\n\tcount 10\n\tscale 10\n\talpha 0.3\n\tdie 2\n\trandomvel 40\n\trgb 220 0 0\n\trgbdelta -100 0 0\n\tgravity 200\n\tstains 2\n\tscalefactor 0.9\n\trotationstart 0 360\n}\n\n//nq blood\nr_part pe_73\n{\n\ttexture \"particles/fteparticlefont.tga\"\n\ttcoords 193 97 255 159 256\n\tcount 1\n\tscale 20\n\talpha 0.3\n\tdie 2\n\trandomvel 40\n\trgb 220 0 0\n\trgbdelta -100 0 0\n\tgravity 200\n\tstains 2\n\tscalefactor 0.9\n\trotationstart 0 360\n}\n\n/////////////////////////////////////////////////\n//rocket explosions\nr_part ember\n{\n\tcount 1\n\ttexture \"particles/fteparticlefont.tga\"\n\ttcoords 97 97 191 191 256\n\trgb 255 128 76\n\talpha 0\n\tscale 15\n\tscalefactor 1\n\tfriction 8\n\tgravity 50\n\tdie 1\n\tblend add\n\trandomvel 5\n\tveladd 1\n\trampmode delta\n\tramp 0\t0\t0\t-0.5\t0\n\tramp 0\t0\t0\t0.1\t0\n\tramp 0\t0\t0\t0.1\t0\n\tramp 0\t0\t0\t0.1\t0\n\tramp 0\t0\t0\t0.1\t0\n\tramp 0\t0\t0\t0.1\t0\n}\n\n//the bits that fly off\nr_part expgib\n{\n\tcliptype expgib\n\ttexture \"particles/fteparticlefont.tga\"\n\ttcoords 97 97 191 191 256\n\talpha 0\n\tcount\t16\n\tdie 1\n\trandomvel 128\n\tgravity 50\n\tfriction 2\n\temit ember\n\temitinterval 0.01\n\tspawnmode circle\n}\n\n//the heart of the explosion\nr_part te_explosion\n{\n\ttexture \"particles/fteparticlefont.tga\"\n\ttcoords 97 97 191 191 256\n\tcount\t1\n\tscale 200\n\tscalefactor 1\n\tdie 1\n\trgb 255 128 76\n\trgbdelta 0 -32 -32\n\tfriction 1\n\tblend add\n\tassoc expgib\n}\n\nr_part gunshotsmoke\n{\n\ttexture \"particles/fteparticlefont.tga\"\n\ttcoords 1 65 31 95 256 8 32\n\tcount 3\n\tscale 25\n\tscalefactor 1\n\tdie 0.8\n\talpha 0.12\n\trgb 32 32 32\n\tblend add\n\tspawnmode ball\n\tspawnorg\t2\n\tspawnvel\t20\n\tveladd -20\n}\n\nr_part te_gunshot\n{\n\ttype texturedspark\n\ttexture \"particles/fteparticlefont.tga\"\n\ttcoords 1 65 31 95 256 8 32\n\tcount 3\n\tscale 2\n\tscalefactor 1\n\talpha 0.5\n\tdie 0.8\n\trgb 255 128 0\n\tblend add\n\tspawnmode ball\n\tspawnorg 1\n\tspawnvel 100\n\tveladd -80\n\tfriction 0.3\n\tgravity 400\n\tassoc gunshotsmoke\n}\n\nr_part spikecore\n{\n\ttexture \"particles/fteparticlefont.tga\"\n\ttcoords 1 97 95 191 256\n\tcount 1\n\tscale 1\n\tscalefactor 1\n\tscaledelta 190\n\tdie 0.1\n\talpha 0.6\n\trgb 255 128 0\n\tblend add\n\tassoc gunshotsmoke\n}\n\nr_part te_spike\n{\n\ttype sparkfan\n\tcount 10\n\tscale 1\n\tscalefactor 1\n\talpha 0.5\n\tdie 0.2\n\trgb 255 128 0\n\tblend add\n\tspawnmode ball\n\tspawnorg 12\n\tspawnvel 300\n\tassoc spikecore\n}\n\nr_part te_lavasplash\n{\n\ttexture \"particles/fteparticlefont.tga\"\n\ttcoords 129 1 191 63 256\n\tcount\t654\n\tscale 15\n\talpha 0.7\n\tdie 4\n\trandomvel 64\n\trgb 255 128 128\n\tgravity 50\n\tblend add\n\tspawnorg 192 64\n\tup 48\n}\n\n//////////////////////////////////////////////////\n//Teleport splash\n\n//two rings moving upwards, costs less\nr_part teleportsplashdown\n{\n\ttexture \"particles/fteparticlefont.tga\"\n\ttcoords 193 1 255 63 256\n\tcount\t32\n\tscale 32\n\tscalefactor 1\n\talpha 0.3\n\tdie 1\n\tveladd -52\n\trgb 255 255 255\n\tfriction 1\n\tspawnorg 32 0\n\tspawnmode uniformcircle\n}\nr_part te_teleportsplash\n{\n\ttexture \"particles/fteparticlefont.tga\"\n\ttcoords 193 1 255 63 256\n\tcount\t32\n\tscale 32\n\tscalefactor 1\n\talpha 0.3\n\tdie 1\n\tveladd 52\n\trgb 255 255 255\n\tfriction 1\n\tspawnorg 32 0\n\tspawnmode uniformcircle\n\tassoc teleportsplashdown\n}\n\n//flame effect\nr_part cu_flame\n{\n\ttexture \"particles/fteparticlefont.tga\"\n\ttcoords 129 1 191 63 256\n\tcount\t1024\n\tscale 0.4\n\tscalerand 6\n\tscalefactor 1\n\talpha 0.4\n\tdie 0.8\n\trandomvel 4 24\n\tveladd -24\n\trgb 255 128 76\n\tblend add\n\tup -8\n\tspawnorg 6 0\n\tspawnvel -15 0\n}\n\n//flame effect\nr_part cu_torch\n{\n\ttexture \"particles/fteparticlefont.tga\"\n\ttcoords 129 1 191 63 256\n\tcount\t256\n\tscale 3\n\tscalefactor 1\n\talpha 0.7\n\tdie 0.5\n\trandomvel 8\n\tveladd -32\n\trgb 255 128 76\n\tblend add\n\tspawnmode circle\n\tspawnorg 4 1\n\tspawnvel -12 -8\n}\n\nr_part explodesprite\n{\n\ttexture \"particles/fteparticlefont.tga\"\n\ttcoords 97 97 191 191 256\n\tcount 180\n\tscale 70\n\tscaledelta -140\n\tscalefactor 1\n\talpha 0.2\n\tdie 0.5\n\trandomvel 23\n\tveladd -20\n\trgb 255 128 76\n\tblend add\n\tspawnorg 4 1\n\tspawnvel -8 -2\n\tup -8\n}\n\n//you'll probably never see this one\nr_part ef_entityparticles\n{\n\ttexture \"particles/fteparticlefont.tga\"\n\ttcoords 1 97 95 191 256\n\tcount\t1\n\tscale 15\n\talpha 0.2\n\tdie 0\n\tveladd 16\n\trgb 255 128 128\n\tblend add\n}\n\n// emp effect, based off of purplehaze's idea\nr_part empshocktrail\n{\n\ttexture \"particles/fteparticlefont.tga\"\n\ttcoords 193 1 255 63 256\n\tstep 3.2\n\tscale 3\n\talpha 0.7\n\tdie 0.2\n\trgb 64 0 255\n\tblend add\n\tscalefactor 1\n\tspawnorg 12 0\n}\n\nr_part empcore\n{\n\ttexture \"particles/fteparticlefont.tga\"\n\ttcoords 193 1 255 63 256\n\tcount 90\n\tscale 55\n\tscaledelta -110\n\tdie 0.55\n\trgb 168 128 255\n\tspawnmode circle\n\tspawnorg 12\n\tspawnvel -192\n\tblend add\n\tscalefactor 0.8\n\temit empshocktrail\n\temitinterval -1\n}\n\n\nr_part empflash\n{\n\tdie 0.1\n\ttexture \"particles/fteparticlefont.tga\"\n\ttcoords 193 1 255 63 256\n\talpha 1\n\tcount 1\n\tscale 400\n\tscaledelta -4000\n\talphadelta 0\n\trgb 192 160 255\n\tblend add\n\tscalefactor 1\n\tassoc empcore\n}\n\nr_part te_tarexplosion\n{\n\ttexture \"particles/fteparticlefont.tga\"\n\ttcoords 1 97 95 191 256\n\tcount\t120\n\tscale 35\n\tdie 0.75\n\talpha 0.4\n\trgb 128 0 255\n\trampmode delta\n\tramp -32 0 0 0\n\tramp -32 0 0 0\n\tramp -32 0 0 2\n\tfriction -0.9\n\tblend add\n\tspawnmode uniformcircle\n\tspawnorg 24 0\n\tspawnvel 280 0\n\tscalefactor 1\n\temit empshocktrail\n\temitinterval -1\n\tassoc empflash\n}\n\nr_part pe_default\n{\n\ttexture \"particles/fteparticlefont.tga\"\n\ttcoords 1 97 95 191 256\n\tcount 1\n\tscale 4\n\tveladd 15\n\tdie 0.4\n\talphadelta 0\n\tdiesubrand 0.4\n\tgravity 40\n\tspawnorg 8\n}\n\nr_part pe_defaulttrail\n{\n\ttexture \"particles/fteparticlefont.tga\"\n\ttcoords 1 97 95 191 256\n\tstep 12\n\tdie 1\n\tscale 10\n\tscaledelta -10\n\tveladd 15\n\tspawnorg 1\n\tscalefactor 0.8\n}\n\nr_part pe_pointfile\n{\n\ttexture \"particles/fteparticlefont.tga\"\n\ttcoords 1 97 95 191 256\n\tcount 1\n\tscale 50\n\tdie 30\n\talphadelta 0\n\trgb 255 255 0\n}\n\nr_effect \"progs/s_explod.spr\" explodesprite 1\nr_effect \"progs/flame.spr\" explodesprite 1\n\nr_effect \"progs/flame2.mdl\" cu_flame 1\nr_effect \"progs/flame.mdl\" cu_torch\nr_trail \"progs/e_spike1.mdl\" te_railtrail\n"
  },
  {
    "path": "engine/partcfgs/tsshaft.cfg",
    "content": "// TE_LIGHTNING2 replacement, (c) 2005 TimeServ\r\n// If you steal this GPLed code you will be violating several international laws\r\n// as well as several laws of physics.\r\nr_part tlightningflash\r\n{\r\n\tspawntime 0.1\r\n\tspawnchance 0.1\r\n\tdie 0.25\r\n\ttype beam\r\n\talpha 1\r\n\tstep 80\r\n\tscale 14\r\n\tscaledelta -52\r\n\trgb 255 255 255\r\n\tspawnmode distball\r\n\tspawnorg 16\r\n\tspawnparam1 0.5\r\n\taverageout\r\n\tnospreadfirst\r\n\tblend add\r\n}\r\n\r\nr_part tlightningglow\r\n{\r\n\tstep 50\r\n\tscale 35\r\n\tscalefactor 1\r\n\talpha 1\r\n\tdie 0\r\n\trgb 1 1 8\r\n\tblend add\r\n\tassoc tlightningflash\r\n}\r\n\r\nr_part tlightningfade\r\n{\r\n\tspawntime 0.05\r\n\tdie 0.2\r\n\ttype beam\r\n\talpha 2\r\n\tstep 96\r\n\tscale 1.5\r\n\trgb 16 16 64\r\n\tspawnmode distball\r\n\tspawnorg 9\r\n\tspawnparam1 0.9\r\n\tblend add\r\n\taverageout\r\n\tnospreadfirst\r\n\tassoc tlightningglow\r\n}\r\n\r\nr_part te_lightning2\r\n{\r\n\tdie 0\r\n\ttype beam\r\n\talpha 2\r\n\tstep 96\r\n\tscale 4\r\n\trgb 196 196 255\r\n\tspawnmode distball\r\n\tspawnorg 9\r\n\tspawnparam1 0.9\r\n\tblend add\r\n\taverageout\r\n\tnospreadfirst\r\n\tassoc tlightningfade\r\n}\r\n\r\nr_part lbolttrail\r\n{\r\n\tdie 0.5\r\n\ttype beam\r\n\talpha 2\r\n\tstep 32\r\n\tscale 1\r\n\trgb 196 196 255\r\n\trgbdelta -512 -512 -128\r\n\tspawnmode distball\r\n\tspawnorg 5\r\n\tspawnvel 4\r\n\tspawnparam1 0.5\r\n\tblend add\r\n}\r\n\r\nr_part lbolt\r\n{\r\n\tdie 0.5\r\n\tcount 1\r\n\tspawnmode circle\r\n\tspawnvel 2000\r\n\tspawnorg 1\r\n\temit lbolttrail\r\n\temitinterval -1\r\n}\r\n\r\nr_part lemit\r\n{\r\n\tdie 0.1\r\n\tcount 1\r\n\tspawnchance 1\r\n\temit lbolt\r\n\temitinterval 100\r\n\tspawnchance 0.1\r\n\tcliptype lemit\r\n\tclipcount 1\r\n\tclipbounce 0\r\n}\r\n\r\nr_part lflash\r\n{\r\n\tdie 0.1\r\n\ttexture \"particles/lflash\"\r\n\tcount 1\r\n\talpha 1\r\n\tscale 100\r\n\tscalefactor 1\r\n\tscaledelta -500\r\n\trgb 255 255 255\r\n\tblend add\r\n\tassoc lemit\r\n}\r\n\r\nr_part te_lightning2_end\r\n{\r\n\tdie 0.3\r\n\talpha 1\r\n\tcount 8\r\n\tscale 2\r\n\trgb 128 128 255\r\n\trgbrand 63 63 0\r\n\trgbrandsync 1\r\n\tspawnvel 100\r\n\tspawnorg 5\r\n\tblend add\r\n\tassoc lflash\r\n}\r\n"
  },
  {
    "path": "engine/qclib/LICENSE",
    "content": "GNU GENERAL PUBLIC LICENSE\nVersion 2, June 1991 \n\nCopyright (C) 1989, 1991 Free Software Foundation, Inc.  \n59 Temple Place - Suite 330, Boston, MA  02111-1307, USA\n\nEveryone is permitted to copy and distribute verbatim copies\nof this license document, but changing it is not allowed.\nPreamble\nThe licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. \n\nWhen we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. \n\nTo protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. \n\nFor example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. \n\nWe protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. \n\nAlso, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. \n\nFinally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. \n\nThe precise terms and conditions for copying, distribution and modification follow. \n\nTERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION\n0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The \"Program\", below, refers to any such program or work, and a \"work based on the Program\" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term \"modification\".) Each licensee is addressed as \"you\". \n\nActivities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. \n\n1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. \n\nYou may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. \n\n2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: \n\n\na) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. \n\nb) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. \n\nc) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this 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.) \nThese 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 themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. \nThus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. \n\nIn addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. \n\n3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: \n\na) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, \n\nb) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, \n\nc) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) \nThe source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. \nIf distribution of executable or object code is made by offering 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. \n\n4. 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 void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. \n\n5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. \n\n6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. \n\n7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. \n\nIf any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. \n\nIt is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. \n\nThis section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. \n\n8. 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 may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. \n\n9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. \n\nEach version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and \"any later version\", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. \n\n10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. \n\nNO WARRANTY\n\n11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. \n\n12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. \n\n\nEND OF TERMS AND CONDITIONS\n"
  },
  {
    "path": "engine/qclib/Makefile",
    "content": "COMMON_OBJS=comprout.o hash.o qcc_cmdlib.o qcd_main.o\nQCC_OBJS=qccmain.o qcc_pr_comp.o qcc_pr_lex.o packager.o decomp.o\nVM_OBJS=pr_exec.o pr_edict.o pr_multi.o initlib.o qcdecomp.o\nGTKGUI_OBJS=qcc_gtk.o qccguistuff.o\nWIN32GUI_OBJS=qccgui.o qccguistuff.o packager.o\nTUI_OBJS=qcctui.o\nLIB_OBJS=\n\nCC?=gcc\nCFLAGS?=-Wall\n\nall: help qcc\nhelp:\n\t@echo for fteqccgui: win or nocyg\n\t@echo for commandline: qcc\n\t@echo for debug builds, add: DEBUG=1\n\t@echo \n\nUSEGUI_CFLAGS=\n# set to -DUSEGUI when compiling the GUI\nWARNING_CFLAGS=-Wno-pointer-sign\nBASE_CFLAGS+=$(WARNING_CFLAGS)\nBASE_CFLAGS+=$(USEGUI_CFLAGS)\n\nifneq ($(DEBUG),)\n\tBASE_CFLAGS+=-ggdb\nelse\n\tBASE_LDFLAGS+=-s\nendif\nBASE_LDFLAGS+=-lz\n# set to \"\" for debugging\n\nDO_CC?=$(CC) $(BASE_CFLAGS) -o $@ -c $< $(CFLAGS)\n\nlib: \n\nR_win_nocyg: $(QCC_OBJS) $(COMMON_OBJS) $(WIN32GUI_OBJS)\n\t$(CC) $(BASE_CFLAGS) -o fteqcc.exe -O3 $(BASE_LDFLAGS) $(QCC_OBJS) $(COMMON_OBJS) $(WIN32GUI_OBJS) -mno-cygwin -mwindows -lcomctl32 -lole32 -lshlwapi\nR_nocyg: $(QCC_OBJS) $(COMMON_OBJS) $(WIN32GUI_OBJS)\n\t$(CC) $(BASE_CFLAGS) -o fteqcc.exe -O3 $(BASE_LDFLAGS) $(QCC_OBJS) $(COMMON_OBJS) $(WIN32GUI_OBJS) -mno-cygwin -lcomctl32 -lole32 -lshlwapi\nR_win: $(QCC_OBJS) $(COMMON_OBJS) $(WIN32GUI_OBJS)\n\t$(CC) $(BASE_CFLAGS) -o fteqcc.exe -O3 $(BASE_LDFLAGS) $(QCC_OBJS) $(COMMON_OBJS) $(WIN32GUI_OBJS) -mwindows -lcomctl32 -lole32 -lshlwapi\n\nwin_nocyg:\n\t$(MAKE) USEGUI_CFLAGS=\"-DUSEGUI -DQCCONLY\" R_win_nocyg\nnocyg:\n\t$(MAKE) USEGUI_CFLAGS=\"-DUSEGUI -DQCCONLY\" R_nocyg\nwin:\n\t$(MAKE) USEGUI_CFLAGS=\"-DUSEGUI -DQCCONLY\" R_win\n\nR_qcc: $(QCC_OBJS) $(COMMON_OBJS) $(TUI_OBJS)\n\t$(CC) $(BASE_CFLAGS) -o fteqcc.bin -O3 $(QCC_OBJS) $(TUI_OBJS) $(COMMON_OBJS) $(BASE_LDFLAGS) -lm\nqcc:\n\t$(MAKE) USEGUI_CFLAGS=\"\" R_qcc\n\nqccmain.o: qccmain.c qcc.h\n\t$(DO_CC)\n\nqcc_cmdlib.o: qcc_cmdlib.c qcc.h\n\t$(DO_CC)\n\nqcc_pr_comp.o: qcc_pr_comp.c qcc.h\n\t$(DO_CC)\n\nqcc_pr_lex.o: qcc_pr_lex.c qcc.h\n\t$(DO_CC)\n\ncomprout.o: comprout.c qcc.h\n\t$(DO_CC)\n\nhash.o: hash.c qcc.h\n\t$(DO_CC)\n\nqcd_main.o: qcd_main.c qcc.h\n\t$(DO_CC)\n\nqccguistuff.o: qccguistuff.c qcc.h\n\t$(DO_CC)\n\npackager.o: packager.c qcc.h\n\t$(DO_CC)\n\n%.o: %.c\n\t$(DO_CC)\n\nqcc_gtk.o: qcc_gtk.c qcc.h\n\t$(DO_CC) `pkg-config --cflags gtk+-2.0`\n\nR_gtkgui: $(QCC_OBJS) $(COMMON_OBJS) $(GTKGUI_OBJS)\n\t$(CC) $(BASE_CFLAGS) $(USEGUI_CFLAGS) -o fteqccgui.bin -O3 $(GTKGUI_OBJS) $(QCC_OBJS) $(COMMON_OBJS) `pkg-config --libs gtk+-2.0`\ngtkgui:\n\t$(MAKE) USEGUI_CFLAGS=\"-DUSEGUI -DQCCONLY\" R_gtkgui\n\nclean:\n\t$(RM) fteqcc.bin fteqcc.exe $(QCC_OBJS) $(COMMON_OBJS) $(VM_OBJS) $(GTKGUI_OBJS) $(WIN32GUI_OBJS) $(TUI_OBJS)\n\nqcvm.so: $(QCC_OBJS) $(VM_OBJS) $(COMMON_OBJS)\n\t$(CC) $(BASE_CFLAGS) -o $@ -O3 $(BASE_LDFLAGS) $(QCC_OBJS) $(VM_OBJS) $(COMMON_OBJS) -shared\nqcvm.a: $(QCC_OBJS) $(VM_OBJS) $(COMMON_OBJS)\n\tar r $@ $^\n\ntest.o: test.c\n\t$(DO_CC)\n\nqcvm: test.o qcvm.a\n\t$(CC) $(BASE_CFLAGS) $(CFLAGS) -o qcvm -O3 $(BASE_LDFLAGS) $^ -lm -lz -ggdb\n\ntests: qcvm\n\t@echo Running Tests...\n\t@$(foreach a,$(wildcard tests/*.src), echo TEST: $a; rm progs.dat; ./testapp.bin progs.dat -srcfile $a; echo; echo)\n\t@echo Tests run.\n\n.PHONY: tests\n"
  },
  {
    "path": "engine/qclib/cmdlib.h",
    "content": "// cmdlib.h\n\n#ifndef __CMDLIB__\n#define __CMDLIB__\n\n#include \"progsint.h\"\n\n/*#include <stdio.h>\n#include <string.h>\n#include <stdlib.h>\n#include <errno.h>\n#include <ctype.h>\n#include <sys/types.h>\n#include <sys/stat.h>\n#include <fcntl.h>\n#include <stdarg.h>\n#include <setjmp.h>\n#include <io.h>\n\n#ifdef NeXT\n#include <libc.h>\n#endif\n*/\n\n// the dec offsetof macro doesn't work very well...\n#define myoffsetof(type,identifier) ((size_t)&((type *)NULL)->identifier)\n\n\n// set these before calling CheckParm\nextern int myargc;\nextern const char **myargv;\n\n//char *strupr (char *in);\n//char *strlower (char *in);\nint QCC_filelength (int handle);\nint QCC_tell (int handle);\n\n#if 0//def __GNUC__\n\t#define WARN_UNUSED_RESULT __attribute__((warn_unused_result))\n#else\n\t#define WARN_UNUSED_RESULT\n#endif\n\nint QC_strcasecmp (const char *s1, const char *s2);\nint QC_strncasecmp(const char *s1, const char *s2, int n);\n\npbool QC_strlcat(char *dest, const char *src, size_t destsize) WARN_UNUSED_RESULT;\npbool QC_strlcpy(char *dest, const char *src, size_t destsize) WARN_UNUSED_RESULT;\npbool QC_strnlcpy(char *dest, const char *src, size_t srclen, size_t destsize) WARN_UNUSED_RESULT;\nchar *QC_strcasestr(const char *haystack, const char *needle);\n\n#if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1))\n\t#define FTE_DEPRECATED  __attribute__((__deprecated__))\t//no idea about the actual gcc version\n\t#if defined(_WIN32)\n\t\t#include <stdio.h>\n\t\t#ifdef __MINGW_PRINTF_FORMAT\n\t\t\t#define LIKEPRINTF(x) __attribute__((format(__MINGW_PRINTF_FORMAT,x,x+1)))\n\t\t#else\n\t\t\t#define LIKEPRINTF(x) __attribute__((format(ms_printf,x,x+1)))\n\t\t#endif\n\t#else\n\t\t#define LIKEPRINTF(x) __attribute__((format(printf,x,x+1)))\n\t#endif\n#endif\n#if (__GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 5))\n\t#define NORETURN __attribute__((noreturn))\n#endif\n#ifndef NORETURN\n\t#define NORETURN\n#endif\n#ifndef LIKEPRINTF\n\t#define LIKEPRINTF(x)\n#endif\n\n#ifdef _MSC_VER\n#define QC_vsnprintf _vsnprintf\nstatic void VARGS QC_snprintfz (char *dest, size_t size, const char *fmt, ...) LIKEPRINTF(3)\n{\n\tva_list args;\n\tva_start (args, fmt);\n\t_vsnprintf (dest, size-1, fmt, args);\n\tva_end (args);\n\t//make sure its terminated.\n\tdest[size-1] = 0;\n}\n#define snprintf QC_snprintfz\n#else\n\t#define QC_vsnprintf vsnprintf\n\t#define QC_snprintfz snprintf\n#endif\n\ndouble I_FloatTime (void);\n\nvoid\tVARGS QCC_Error (int errortype, const char *error, ...) LIKEPRINTF(2);\nint QCC_CheckParm (const char *check);\nconst char *QCC_ReadParm (const char *check);\n\n\nint \tSafeOpenWrite (char *filename, int maxsize);\nint \tSafeOpenRead (char *filename);\nvoid \tSafeRead (int handle, void *buffer, long count);\nvoid \tSafeWrite (int handle, const void *buffer, long count);\npbool\tSafeClose(int hand);\nint SafeSeek(int hand, int ofs, int mode);\nvoid \t*SafeMalloc (long size);\n\n\nlong\tQCC_LoadFile (char *filename, void **bufferptr);\nvoid\tQCC_SaveFile (char *filename, void *buffer, long count);\n\nvoid \tDefaultExtension (char *path, char *extension);\nvoid \tDefaultPath (char *path, char *basepath);\nvoid \tStripFilename (char *path);\nvoid \tStripExtension (char *path);\n\nvoid \tExtractFilePath (char *path, char *dest);\nvoid \tExtractFileBase (char *path, char *dest);\nvoid\tExtractFileExtension (char *path, char *dest);\n\nlong \tParseNum (char *str);\n\nunsigned short *QCC_makeutf16(char *mem, size_t len, int *outlen, pbool *errors);\nchar *QCC_SanitizeCharSet(char *mem, size_t *len, pbool *freeresult, int *origfmt);\n\n\nchar *QCC_COM_Parse (const char *data);\nchar *QCC_COM_Parse2 (char *data);\n\nunsigned int utf8_check(const void *in, unsigned int *value);\n\nextern\tchar\tqcc_token[1024];\n\n\n#define qcc_iswhite(c) ((c) == ' ' || (c) == '\\r' || (c) == '\\n' || (c) == '\\t' || (c) == '\\v')\n#define qcc_iswhitesameline(c) ((c) == ' ' || (c) == '\\t')\n#define qcc_islineending(c,n) ((c) == '\\n' || ((c) == '\\r' && (n) != '\\n'))\t//to try to handle mac line endings, especially if they're in the middle of a line\n\n\nenum\n{\n\tUTF8_RAW,\n\tUTF8_BOM,\n\tUTF_ANSI,\n\tUTF16LE,\n\tUTF16BE,\n\tUTF32LE,\n\tUTF32BE,\n};\n\n#endif\n"
  },
  {
    "path": "engine/qclib/comprout.c",
    "content": "//compile routines\n\n#include \"qcc.h\"\n#undef progfuncs\n\nchar errorfile[128];\nint errorline;\n\nprogfuncs_t *qccprogfuncs;\n\n#include <setjmp.h>\n\nextern int qcc_compileactive;\njmp_buf qcccompileerror;\nchar qcc_gamedir[128];\nvoid QCC_PR_ResetErrorScope(void);\n\n\n\n#if defined(MINIMAL) || defined(OMIT_QCC)\n\n#else\n\nstatic struct qcchunk_s\n{\n\tstruct qcchunk_s *older;\n\tchar *data;\n\tchar *end;\n} *qcc_hunk;\nstatic void qccHunkExtend(size_t minsize)\n{\n\tstruct qcchunk_s *n;\n\tsize_t sz;\n\tminsize += sizeof(struct qcchunk_s)+64;\n\tsz = max(minsize, 256*1024*1024);\n\n\tfor (;;)\n\t{\n\t\tif (sz < minsize)\n\t\t\tQCC_Error(ERR_INTERNAL, \"Compile hunk was filled\");\n\t\tn = malloc(sz);\n\t\tif (n)\n\t\t\tbreak;\n\t\tsz /= 2;\n\t}\n\n\tn->data = (char*)(n+1);\n\tn->end = ((char*)n)+sz;\n\tn->older = qcc_hunk;\n\tqcc_hunk = n;\n}\nvoid *qccHunkAlloc(size_t mem)\n{\n\tchar *ret;\n\tmem = (mem + 7)&~7;\n\n\tif (qcc_hunk->data+mem > qcc_hunk->end)\n\t\tqccHunkExtend(mem);\n\tret = qcc_hunk->data;\n\tqcc_hunk->data += mem;\n\n\tmemset(ret, 0, mem);\n\treturn ret;\n}\nvoid qccClearHunk(void)\n{\n\tstruct qcchunk_s *t;\n\tQCC_PurgeTemps();\n\twhile (qcc_hunk)\n\t{\n\t\tt = qcc_hunk;\n\t\tqcc_hunk = t->older;\n\t\tfree(t);\n\t}\n}\nint qccpersisthunk;\nvoid PostCompile(void)\n{\n\tif (!qccpersisthunk)\n\t\tqccClearHunk();\n\tQCC_PR_CloseProcessor();\n\tQCC_Cleanup();\n\n\tif (asmfile)\n\t{\n\t\tfclose(asmfile);\n\t\tasmfile = NULL;\n\t}\n}\npbool PreCompile(void)\n{\n\tQCC_PR_ResetErrorScope();\n\n\tqccClearHunk();\n\tstrcpy(qcc_gamedir, \"\");\n\n\tqccHunkExtend(0);\n\treturn true;\n}\n\npbool QCC_main (int argc, const char **argv);\nvoid QCC_FinishCompile(void);\n\nint comp_nump;const char **comp_parms;\n//void Editor(char *fname, int line, int numparms, char **compileparms);\npbool CompileParams(progfuncs_t *progfuncs, void(*cb)(void), int nump, const char **parms)\n{\n\tcomp_nump = nump;\n\tcomp_parms = parms;\n\t*errorfile = '\\0';\n\tqccprogfuncs = progfuncs;\n\tif (setjmp(qcccompileerror))\n\t{\n\t\tPostCompile();\n\t\tif (*errorfile)\n\t\t{\n\t\t\texterns->Printf(\"Error in %s on line %i\\n\", errorfile, errorline);\n\t\t}\n\t\treturn false;\n\t}\n\n\tif (!QCC_main(nump, parms))\n\t\treturn false;\n\n\twhile(qcc_compileactive)\n\t{\n\t\tif (cb)\n\t\t\tcb();\n\n\t\tQCC_ContinueCompile();\n\t}\n\n\tPostCompile();\n\n\treturn !pr_error_count;\n}\nint PDECL Comp_Begin(pubprogfuncs_t *progfuncs, int nump, const char **parms)\n{\n\tcomp_nump = nump;\n\tcomp_parms = parms;\n\tqccprogfuncs = (progfuncs_t*)progfuncs;\n\t*errorfile = '\\0';\n\tif (setjmp(qcccompileerror))\n\t{\n\t\tPostCompile();\n\t\treturn false;\n\t}\n\n\tif (!QCC_main(nump, parms))\n\t\treturn false;\n\n\treturn true;\n}\nint PDECL Comp_Continue(pubprogfuncs_t *progfuncs)\n{\t\n\tqccprogfuncs = (progfuncs_t *)progfuncs;\n\tif (setjmp(qcccompileerror))\n\t{\n\t\tPostCompile();\n\t\treturn false;\n\t}\n\n\tif (qcc_compileactive)\n\t\tQCC_ContinueCompile();\n\telse\n\t{\n\t\tPostCompile();\n\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n#endif\npbool CompileFile(progfuncs_t *progfuncs, const char *filename)\n{\t\n#if defined(MINIMAL) || defined(OMIT_QCC)\n\treturn false;\n#else\n\tchar srcfile[32];\n\tchar newname[32];\n\tstatic const char *p[5];\n\tint parms;\n\tchar *s, *s2;\n\t\n\tp[0] = NULL;\n\tparms = 1;\n\t\n\tstrcpy(newname, filename);\n\ts = newname;\n\tif (strchr(s+1, '/'))\n\t{\n\t\twhile(1)\n\t\t{\n\t\t\ts2 = strchr(s+1, '/');\n\t\t\tif (!s2)\n\t\t\t{\n\t\t\t\t*s = '\\0';\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\ts = s2;\n\t\t}\n\t\tp[parms] = \"-src\";\n\t\tp[parms+1] = newname;\n\t\tparms+=2;\t\n\t\t\n\t\tstrcpy(srcfile, s+1);\n\t\tsrcfile[strlen(srcfile)-4] = '\\0';\n\t\tstrcat(srcfile, \".src\");\n\n\t\tif (externs->FileSize(qcva(\"%s/%s\", newname, srcfile))>0)\n\t\t{\n\t\t\tp[parms] = \"-srcfile\";\n\t\t\tp[parms+1] = srcfile;\n\t\t\tparms+=2;\n\t\t}\n\t}\n\telse\n\t{\n\t\tp[parms] = \"-srcfile\";\n\t\tp[parms+1] = newname;\n\t\tnewname[strlen(newname)-4] = '\\0';\n\t\tstrcat(newname, \".src\");\n\t\tparms+=2;\n\t}\n//\tp[2][strlen(p[2])-4] = '\\0';\n//\tstrcat(p[2], \"/\");\n\n\twhile (!CompileParams(progfuncs, NULL, parms, p))\n\t{\n\t\treturn false;\n\t}\n\treturn true;\n#endif\n}\n\nint QC_strncasecmp(const char *s1, const char *s2, int n)\n{\n\tint             c1, c2;\n\t\n\twhile (1)\n\t{\n\t\tc1 = *s1++;\n\t\tc2 = *s2++;\n\n\t\tif (!n--)\n\t\t\treturn 0;               // strings are equal until end point\n\t\t\n\t\tif (c1 != c2)\n\t\t{\n\t\t\tif (c1 >= 'a' && c1 <= 'z')\n\t\t\t\tc1 -= ('a' - 'A');\n\t\t\tif (c2 >= 'a' && c2 <= 'z')\n\t\t\t\tc2 -= ('a' - 'A');\n\t\t\tif (c1 != c2)\n\t\t\t\treturn -1;              // strings not equal\n\t\t}\n\t\tif (!c1)\n\t\t\treturn 0;               // strings are equal\n\t}\n\t\n\treturn -1;\n}\n\nvoid editbadfile(const char *fname, int line)\n{\n\tif (!*errorfile)\n\t{\n\t\tstrcpy(errorfile, fname);\n\t\terrorline = line;\n\t}\n}\n\n"
  },
  {
    "path": "engine/qclib/decomp.c",
    "content": "\n#include \"qcc.h\"\n//#include \"decomp.h\"\n\n//This file is derived from frikdec, but has some extra tweaks to make it more friendly with fte's compiler/opcodes (its not perfect though)\n//FIXME: there's still a load of mallocs, so we don't allow this more than once per process.\n//convert vector terms into floats when its revealed that they're using vector ops and not floats\n//FIXME: fteqcc converts do {} while(1); into a goto -1; which is a really weeeird construct.\n\n#if defined(_WIN32) || defined(__DJGPP__)\n#include <malloc.h>\n#elif defined(__unix__) && !defined(__linux__) // quick hack for the bsds and other unix systems\n#include<stdlib.h>\n#else\n#include <alloca.h>\n#endif\n\n#define\tDEF_H2ARRAY \t\t(1<<16)\t//[-1] is length.\n\n//#undef printf\n//#define printf GUIprintf\n\n//custom types, because we're lazy and lame with strings.\ntypedef struct\n{\n\tunsigned int\ttype;\t\t// if DEF_SAVEGLOBAL bit is set the variable needs to be saved in savegames\n\tunsigned int\tofs;\n\tconst char\t\t*s_name;\n} QCD_def_t;\ntypedef struct\n{\n\tint\t\tfirst_statement;\t// negative numbers are builtins\n\tint\t\tparm_start;\n\tint\t\tlocals;\t\t\t\t// total ints of parms + locals\n\tint\t\tprofile;\t\t// runtime\n\tconst char\t\t*s_name;\n\tconst char\t\t*s_file;\t\t\t// source file defined in\n\tint\t\tnumparms;\n\tpbyte\tparm_size[MAX_PARMS];\n} QCD_function_t;\n\n#define OP_MARK_END_DO\t\t0x00010000\t//do{\n#define OP_MARK_END_ELSE\t0x00000400\t//}\n\n#define MAX_REGS 65536\n#define dstatement_t QCC_dstatement32_t\n#define statements destatements\n#define functions defunctions\n#define strings destrings\n#define fields defields\nstatic dstatement_t *statements;\nstatic float *pr_globals;\nstatic char\t\t*strings;\nstatic QCD_def_t *globals;\nstatic QCD_def_t *fields;\nstatic QCD_function_t *functions;\n\nstatic int ofs_return;\nstatic int ofs_parms[MAX_PARMS];\nstatic int ofs_size = 3;\n\nstatic QCD_def_t\t\t*globalofsdef[MAX_REGS];\n\n\n//forward declarations.\nQCD_def_t *GetField(const char *name);\n\n#include <stdio.h>\n\n/*int QC_snprintfz(char *buffer, size_t maxlen, const char *format, ...)\n{\n\tint p;\n\tva_list\t\targptr;\n\n\tif (!maxlen)\n\t\treturn -1;\n\t\t\n\tva_start (argptr, format);\n\tp = _vsnprintf (buffer, maxlen, format,argptr);\n\tva_end (argptr);\n\tbuffer[maxlen-1] = 0;\n\n\treturn p;\n}*/\n\nconst char *GetString(unsigned int str)\n{\n\tif (str >= strofs)\n\t{\n\t\tchar s[256];\n\t\tQC_snprintfz(s, sizeof(s), \"INVALIDSTRING[%i]\", str);\n\t\treturn strdup(s);\n\t\treturn \"INVALIDSTRING\";\n\t}\n\telse\n\t\treturn strings+str;\n}\nconst char *GetNameString(const char *str){return str;}\n\nextern QCC_opcode_t pr_opcodes [];\n\nstatic int debug_offs = 0;\nstatic int assumeglobals = 0;\t//unknown globals are assumed to be actual globals and NOT unlocked temps\nstatic int assumelocals = 0;\t//unknown locals are assumed to be actual locals and NOT locked temps\n\nstatic vfile_t *Decompileofile;\nstatic vfile_t *Decompileprogssrc;\nstatic char **DecompileProfiles;//[MAX_FUNCTIONS];\nstatic char **rettypes;//[MAX_FUNCTIONS];\n\nextern int quakeforgeremap[];\n\nstatic char *type_names[] =\n{\n\t\"void\",\n\t\"string\",\n\t\"float\",\n\t\"vector\",\n\t\"entity\",\n\t\"ev_field\",\n\t\"void()\",\n\t\"ev_pointer\",\n\n\t\"int\",\n\t\"__uint\",\n\t\"__int64\",\n\t\"__uint64\",\n\t\"__double\",\n\n\t\"__variant\",\n\t\"__struct\",\n\t\"__union\",\n\t\"__accessor\",\n\t\"__enum\",\n\t\"__typedef\",\n\t\"__boolean\",\n};\nconst char *typetoname(QCC_type_t *type)\n{\n\treturn type->name;\n}\n\nconst char *temp_type (int temp, dstatement_t *start, QCD_function_t *df)\n{\n\tint i;\n\tdstatement_t *stat;\n\tstat = start - 1;\n\t// determine the type of a temp\n\n\twhile(stat > statements)\n\t{\n\t\tif (temp == stat->a)\n\t\t\treturn typetoname(*pr_opcodes[stat->op].type_a);\n\t\telse if (temp == stat->b)\n\t\t\treturn typetoname(*pr_opcodes[stat->op].type_b);\n\t\telse if (temp == stat->c)\n\t\t\treturn typetoname(*pr_opcodes[stat->op].type_c);\n\t\tstat--;\n\t}\n\n\t// method 2\n\t// find a call to this function\n\tfor (i = 0; i < numstatements; i++)\n\t{\n\t\tstat = &statements[i];\n\n\t\tif (stat->op >= OP_CALL0 && stat->op <= OP_CALL8 && ((eval_t *)&pr_globals[stat->a])->function == df - functions)\n\t\t{\n\t\t\tfor(i++; i < numstatements; i++)\n\t\t\t{\n\t\t\t\tstat = &statements[i];\n\t\t\t\tif (ofs_return == stat->a && (*pr_opcodes[stat->op].type_a)->type != ev_void)\n\t\t\t\t\treturn type_names[(*pr_opcodes[stat->op].type_a)->type];\n\t\t\t\telse if (ofs_return == stat->b && (*pr_opcodes[stat->op].type_b)->type != ev_void)\n\t\t\t\t\treturn type_names[(*pr_opcodes[stat->op].type_b)->type];\n\t\t\t\telse if (stat->op == OP_DONE)\n\t\t\t\t\tbreak;\n\t\t\t\telse if (stat->op >= OP_CALL0 && stat->op <= OP_CALL8 && stat->a != df - functions)\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\tprintf(\"warning: Could not determine return type for %s\\n\", GetNameString(df->s_name));\n\n\treturn \"float\";\n\n}\n\npbool IsConstant(QCD_def_t *def)\n{\n\n\tint i;\n\tdstatement_t *d;\n\n\tif (def->type & DEF_SAVEGLOBAL)\n\t\treturn false;\n\n\tif (pr_globals[def->ofs] == 0)\n\t\treturn false;\n\n\tfor (i = 1; i < numstatements; i++)\n\t{\n\t\td = &statements[i];\n\t\tif (d->b == def->ofs)\n\t\t{\n\t\t\tif (pr_opcodes[d->op].associative == ASSOC_RIGHT)\n\t\t\t{\n\t\t\t\tif (d->op - OP_STORE_F < 6)\n\t\t\t\t{\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn true;\n}\n\nchar *type_name (QCD_def_t *def)\n{\n\tQCD_def_t *j;\n\n\tswitch(def->type&~DEF_SAVEGLOBAL)\n\t{\n\tcase ev_field:\n\tcase ev_pointer:\n\t\tj = GetField(GetNameString(def->s_name));\n\t\tif (j)\n\t\t\treturn qcva(\".%s\",type_names[j->type]);\n\t\telse\n\t\t\treturn type_names[def->type&~DEF_SAVEGLOBAL];\n\tcase ev_void:\n\tcase ev_string:\n\tcase ev_entity:\n\tcase ev_vector:\n\tcase ev_float:\n\t\treturn type_names[def->type&~DEF_SAVEGLOBAL];\n\tcase ev_function:\n\t\treturn \"void()\";\n\tcase ev_integer:\n\t\treturn \"int\";\n//\tcase ev_uinteger:\n//\t\treturn \"unsigned\";\n//\tcase ev_quat:\n//\t\treturn \"quat\";\n\tdefault:\n\t\treturn \"float\";\n\t}\n};\n\nextern int numstatements;\n\nextern int numfunctions;\n\n#define FILELISTSIZE 62\n\n\n\n/*\n===============\nPR_String\n\nReturns a string suitable for printing (no newlines, max 60 chars length)\n===============\n*/\nconst char *PR_String (const char *string)\n{\n\tstatic char buf[80];\n\tchar\t*s;\n\t\n\ts = buf;\n\t*s++ = '\"';\n\twhile (string && *string)\n\t{\n\t\tif (s == buf + sizeof(buf) - 2)\n\t\t\tbreak;\n\t\tif (*string == '\\n')\n\t\t{\n\t\t\t*s++ = '\\\\';\n\t\t\t*s++ = 'n';\n\t\t}\n\t\telse if (*string == '\"')\n\t\t{\n\t\t\t*s++ = '\\\\';\n\t\t\t*s++ = '\"';\n\t\t}\n\t\telse\n\t\t\t*s++ = *string;\n\t\tstring++;\n\t\tif (s - buf > 60)\n\t\t{\n\t\t\t*s++ = '.';\n\t\t\t*s++ = '.';\n\t\t\t*s++ = '.';\n\t\t\tbreak;\n\t\t}\n\t}\n\t*s++ = '\"';\n\t*s++ = 0;\n\treturn buf;\n}\n/*\n============\nPR_ValueString\n\nReturns a string describing *data in a type specific manner\n=============\n*/\n\n\nstatic char *PR_ValueString (etype_t type, void *val)\n{\n\tstatic char\tline[8192];\n\n\tQCD_function_t\t*f;\n\t\n\tswitch (type)\n\t{\n\tcase ev_string:\n\t\tQC_snprintfz(line, sizeof(line), \"%s\", PR_String(GetString(*(int *)val)));\n\t\tbreak;\n\tcase ev_entity:\t\n\t\tQC_snprintfz(line, sizeof(line), \"entity %i\", *(int *)val);\n\t\tbreak;\n\tcase ev_function:\n\t\tif (*(unsigned int *)val >= numfunctions)\n\t\t\tQC_snprintfz(line, sizeof(line), \"undefined function\");\n\t\telse\n\t\t{\n\t\t\tf = functions + *(unsigned int *)val;\n\t\t\tQC_snprintfz(line, sizeof(line), \"%s()\", GetNameString(f->s_name));\n\t\t}\n\t\tbreak;\n\t/*\n\tcase ev_field:\n\t\tdef = PR_DefForFieldOfs ( *(int *)val );\n\t\tsprintf (line, \".%s\", def->name);\n\t\tbreak;\n\t*/\n\tcase ev_void:\n\t\tQC_snprintfz(line, sizeof(line), \"void\");\n\t\tbreak;\n\tcase ev_float:\n\t\t{\n\t\t\tunsigned int high = *(unsigned int*)val & 0xff000000;\n\t\t\tif (high == 0xff000000 || !high)\n\t\t\t\t//FIXME this is probably a string or something, but we don't really know what type it is.\n\t\t\t\tQC_snprintfz(line, sizeof(line), \"(float)(__variant)%ii\", *(int*)val);\n\t\t\telse\n\t\t\t\tQC_snprintfz(line, sizeof(line), \"%5.1f\", *(float *)val);\n\t\t}\n\t\tbreak;\n\tcase ev_vector:\n\t\tQC_snprintfz(line, sizeof(line), \"'%5.1f %5.1f %5.1f'\", ((float *)val)[0], ((float *)val)[1], ((float *)val)[2]);\n\t\tbreak;\n\tcase ev_pointer:\n\t\tQC_snprintfz(line, sizeof(line), \"pointer\");\n\t\tbreak;\n\tcase ev_field:\n\t\tQC_snprintfz(line, sizeof(line), \"<FIELD@%i>\", *(int*)val);\n\t\tbreak;\n\tdefault:\n\t\tQC_snprintfz(line, sizeof(line), \"bad type %i\", type);\n\t\tbreak;\n\t}\n\t\n\treturn line;\n}\n\n\nstatic char *filenames[] =\n{\n\t\"makevectors\",\t\t\"defs.qc\",\n\t\"button_wait\",\t\t\"buttons.qc\",\n\t\"anglemod\",\t\t\"ai.qc\",\n\t\"boss_face\",\t\t\"boss.qc\",\n\t\"info_intermission\",\t\"client.qc\",\n\t\"CanDamage\",\t\"combat.qc\",\n\t\"demon1_stand1\",\t\"demon.qc\",\n\t\"dog_bite\",\t\t\"dog.qc\",\n\t\"door_blocked\",\t\t\"doors.qc\",\n\t\"Laser_Touch\",\t\t\"enforcer.qc\",\n\t\"knight_attack\",\t\t\t\"fight.qc\",\n\t\"f_stand1\",\t\"fish.qc\",\n\t\"hknight_shot\",\t\"hknight.qc\",\n\t\"SUB_regen\",\t\t\"items.qc\",\n\t\"knight_stand1\",\t\"knight.qc\",\n\t\"info_null\",\t\t\"misc.qc\",\n\t\"monster_use\",\t\t\"monsters.qc\",\n\t\"OgreGrenadeExplode\",\t\"ogre.qc\",\n\t\"old_idle1\",\t\t\t\"oldone.qc\",\n\t\"plat_spawn_inside_trigger\", \"plats.qc\",\n\t\"player_stand1\",\t\t\"player.qc\",\n\t\"shal_stand\",\t\"shalrath.qc\",\n\t\"sham_stand1\",\t\t\"shambler.qc\",\n\t\"army_stand1\",\t\t\"soldier.qc\",\n\t\"SUB_Null\",\t\t\t\"subs.qc\",\n\t\"tbaby_stand1\",\t\t\"tarbaby.qc\",\n\t\"trigger_reactivate\",\t\"triggers.qc\",\n\t\"W_Precache\",\t\t\t\"weapons.qc\",\n\t\"LaunchMissile\",\t\"wizard.qc\",\n\t\"main\",\t\t\"world.qc\",\n\t\"zombie_stand1\",\t\"zombie.qc\"\n};\n\n//FIXME: parse fteextensions.qc instead, or something.\n#define QW(x) //x,\nstatic struct {\n\tint num;\n\tchar *name;\t//purly for readability.\n\tQCC_type_t **returns;\n\tQCC_type_t **params[8];\n\tchar *text;\n} builtins[]=\n{\n\t{0,\t\tNULL,\t\t\t\tNULL, {NULL},\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tNULL},\t\t\t\t\t\t\t\t\n\t{1,\t\t\"makevectors\",\t\tNULL, {&type_vector},\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"void (vector ang)\"},\n\t{2,\t\t\"setorigin\",\t\tNULL, {&type_entity, &type_vector},\t\t\t\t\t\t\t\t\t\t\t\"void (entity e, vector o)\"},\n\t{3,\t\t\"setmodel\",\t\t\tNULL, {&type_entity, &type_string},\t\t\t\t\t\t\t\t\t\t\t\"void (entity e, string m)\"},\n\t{4,\t\t\"setsize\",\t\t\tNULL, {&type_entity, &type_vector, &type_vector},\t\t\t\t\t\t\t\"void (entity e, vector min, vector max)\"},\n\t{5,\t\tNULL,\t\t\t\tNULL, {NULL},\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tNULL},\n\t{6,\t\tNULL,\t\t\t\tNULL, {NULL},\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"void ()\"},\n\t{7,\t\t\"random\",\t\t\tNULL, {NULL},\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"float ()\"},\n\t{8,\t\t\"sound\",\t\t\tNULL, {&type_entity, &type_float, &type_string, &type_float, &type_float},\t\"void (entity e, float chan, string samp, float vol, float atten)\"},\n\t{9,\t\t\"normalize\",\t\t&type_vector, {&type_vector},\t\t\t\t\t\t\t\t\t\t\t\t\"vector (vector v)\"},\n\t{10,\t\"error\",\t\t\tNULL, {&type_string},\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"void (string e)\"},\n\t{11,\t\"objerror\",\t\t\tNULL, {&type_string},\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"void (string e)\"},\n\t{12,\t\"vlen\",\t\t\t\t&type_float, {&type_vector},\t\t\t\t\t\t\t\t\t\t\t\t\"float (vector v)\"},\n\t{13,\t\"vectoyaw\",\t\t\t&type_float, {&type_vector},\t\t\t\t\t\t\t\t\t\t\t\t\"float (vector v)\"},\n\t{14,\t\"spawn\",\t\t\t&type_entity, {NULL},\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"entity ()\"},\n\t{15,\t\"remove\",\t\t\tNULL, {&type_entity},\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"void (entity e)\"},\n\t{16,\t\"traceline\",\t\tNULL, {&type_vector, &type_vector, &type_float, &type_entity},\t\t\t\t\"void (vector v1, vector v2, float nomonsters, entity forent)\"},\n\t{17,\tNULL,\t\t\t\tNULL, {NULL},\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"entity ()\"},\n\t{18,\t\"find\",\t\t\t\t&type_entity, {&type_entity, &type_field, &type_string},\t\t\t\t\t\"entity (entity start, .string fld, string match)\"},\n\t{19,\t\"precache_sound\",\tNULL, {&type_string},\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"string (string s)\"},\n\t{20,\t\"precache_model\",\tNULL, {&type_string},\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"string (string s)\"},\n\t{21,\t\"stuffcmd\",\t\t\tNULL, {&type_entity, &type_string},\t\t\t\t\t\t\t\t\t\t\t\"void (entity client, string s)\"},\n\t{22,\t\"findradius\",\t\tNULL, {&type_vector, &type_float},\t\t\t\t\t\t\t\t\t\t\t\"entity (vector org, float rad)\"},\n\t{23,\t\"bprint\",\t\t\tNULL, {QW(&type_float) &type_string,&type_string,&type_string,&type_string,&type_string,&type_string,&type_string}, \"void (...)\"},\n\t{24,\t\"sprint\",\t\t\tNULL, {&type_entity, QW(&type_float) &type_string,&type_string,&type_string,&type_string,&type_string,&type_string}, \"void (...)\"},\n\t{25,\t\"dprint\",\t\t\tNULL, {&type_string,&type_string,&type_string,&type_string,&type_string,&type_string,&type_string,&type_string}, \"void (...)\"},\n\t{26,\t\"ftos\",\t\t\t\t&type_string, {&type_float},\t\t\t\t\t\t\t\t\t\t\t\t\"string (float f)\"},\n\t{27,\t\"vtos\",\t\t\t\t&type_string, {&type_vector},\t\t\t\t\t\t\t\t\t\t\t\t\"string (vector v)\"},\n\t{28,\t\"coredump\",\t\t\tNULL, {NULL},\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"void ()\"},\n\t{29,\t\"traceon\",\t\t\tNULL, {NULL},\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"void ()\"},\n\t{30,\t\"traceoff\",\t\t\tNULL, {NULL},\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"void ()\"},\n\t{31,\t\"eprint\",\t\t\tNULL, {&type_entity},\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"void (entity e)\"},\n\t{32,\t\"walkmove\",\t\t\t&type_float, {&type_float, &type_float},\t\t\t\t\t\t\t\t\t\"float (float yaw, float dist)\"},\n\t{33,\tNULL,\t\t\t\tNULL, {NULL},\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tNULL},\n\t{34,\t\"droptofloor\",\t\tNULL, {&type_float, &type_float},\t\t\t\t\t\t\t\t\t\t\t\"float ()\"},\n\t{35,\t\"lightstyle\",\t\tNULL, {&type_float, &type_string},\t\t\t\t\t\t\t\t\t\t\t\"void (float style, string value)\"},\n\t{36,\t\"rint\",\t\t\t\t&type_float, {&type_vector},\t\t\t\t\t\t\t\t\t\t\t\t\"float (float v)\"},\n\t{37,\t\"floor\",\t\t\t&type_float, {&type_vector},\t\t\t\t\t\t\t\t\t\t\t\t\"float (float v)\"},\n\t{38,\t\"ceil\",\t\t\t\t&type_float, {&type_vector},\t\t\t\t\t\t\t\t\t\t\t\t\"float (float v)\"},\n\t{39,\tNULL,\t\t\t\tNULL, {NULL},\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tNULL},\n\t{40,\t\"checkbottom\",\t\t&type_float, {&type_entity},\t\t\t\t\t\t\t\t\t\t\t\t\"float (entity e)\"},\n\t{41,\t\"pointcontents\",\t&type_float, {&type_vector},\t\t\t\t\t\t\t\t\t\t\t\t\"float (vector v)\"},\n\t{42,\tNULL,\t\t\t\tNULL, {NULL},\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tNULL},\n\t{43,\t\"fabs\",\t\t\t\t&type_float, {&type_float},\t\t\t\t\t\t\t\t\t\t\t\t\t\"float (float f)\"},\n\t{44,\t\"aim\",\t\t\t\tNULL, {&type_entity, &type_float},\t\t\t\t\t\t\t\t\t\t\t\"vector (entity e, float speed)\"},\n\t{45,\t\"cvar\",\t\t\t\t&type_string, {&type_string},\t\t\t\t\t\t\t\t\t\t\t\t\"float (string s)\"},\n\t{46,\t\"localcmd\",\t\t\tNULL, {&type_string},\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"void (string s)\"},\n\t{47,\t\"nextent\",\t\t\t&type_entity, {&type_entity},\t\t\t\t\t\t\t\t\t\t\t\t\"entity (entity e)\"},\n\t{48,\t\"\",\t\t\t\t\tNULL, {&type_vector, &type_vector, &type_float, &type_float},\t\t\t\t\"void (vector o, vector d, float color, float count)\"},\n\t{49,\t\"changeyaw\",\t\tNULL, {NULL},\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"void ()\"},\n\t{50,\tNULL,\t\t\t\tNULL, {NULL},\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tNULL},\n\t{51,\t\"vectoangles\",\t\t&type_vector, {&type_vector},\t\t\t\t\t\t\t\t\t\t\t\t\"vector (vector v)\"},\n\t{52,\t\"WriteByte\",\t\tNULL, {&type_float, &type_float},\t\t\t\t\t\t\t\t\t\t\t\"void (float to, float f)\"},\n\t{53,\t\"WriteChar\",\t\tNULL, {&type_float, &type_float},\t\t\t\t\t\t\t\t\t\t\t\"void (float to, float f)\"},\n\t{54,\t\"WriteShort\",\t\tNULL, {&type_float, &type_float},\t\t\t\t\t\t\t\t\t\t\t\"void (float to, float f)\"},\n\t{55,\t\"WriteLong\",\t\tNULL, {&type_float, &type_float},\t\t\t\t\t\t\t\t\t\t\t\"void (float to, float f)\"},\n\t{56,\t\"WriteCoord\",\t\tNULL, {&type_float, &type_float},\t\t\t\t\t\t\t\t\t\t\t\"void (float to, float f)\"},\n\t{57,\t\"WriteAngle\",\t\tNULL, {&type_float, &type_float},\t\t\t\t\t\t\t\t\t\t\t\"void (float to, float f)\"},\n\t{58,\t\"WriteString\",\t\tNULL, {&type_float, &type_string},\t\t\t\t\t\t\t\t\t\t\t\"void (float to, string s)\"},\n\t{59,\t\"WriteEntity\",\t\tNULL, {&type_float, &type_entity},\t\t\t\t\t\t\t\t\t\t\t\"void (float to, entity s)\"},\n\t{60,\tNULL,\t\t\t\tNULL, {NULL},\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tNULL},\n\t{61,\tNULL,\t\t\t\tNULL, {NULL},\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tNULL},\n\t{62,\tNULL,\t\t\t\tNULL, {NULL},\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tNULL},\n\t{63,\tNULL,\t\t\t\tNULL, {NULL},\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tNULL},\n\t{64,\tNULL,\t\t\t\tNULL, {NULL},\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tNULL},\n\t{65,\tNULL,\t\t\t\tNULL, {NULL},\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tNULL},\n\t{66,\tNULL,\t\t\t\tNULL, {NULL},\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tNULL},\n\t{67,\t\"movetogoal\",\t\tNULL, {&type_float},\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"void (float step)\"},\n\t{68,\t\"precache_file\",\tNULL, {&type_string},\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"string (string s)\"},\n\t{69,\t\"makestatic\",\t\tNULL, {&type_entity},\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"void (entity e)\"},\n\t{70,\t\"changelevel\",\t\tNULL, {&type_string},\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"void (string s)\"},\n\t{71,\tNULL,\t\t\t\tNULL, {NULL},\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tNULL},\n\t{72,\t\"cvar_set\",\t\t\tNULL, {&type_string, &type_string},\t\t\t\t\t\t\t\t\t\t\t\"void (string var, string val)\"},\n\t{73,\t\"centerprint\",\t\tNULL, {&type_entity,&type_string,&type_string,&type_string,&type_string,&type_string,&type_string,&type_string},\t\"void (entity client, string s, ...)\"},\n\t{74,\t\"ambientsound\",\t\tNULL, {&type_vector, &type_string, &type_float, &type_float},\t\t\t\t\"void (vector pos, string samp, float vol, float atten)\"},\n\t{75,\t\"precache_model2\",\tNULL, {&type_string},\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"string (string s)\"},\n\t{76,\t\"precache_sound2\",\tNULL, {&type_string},\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"string (string s)\"},\n\t{77,\t\"precache_file2\",\tNULL, {&type_string},\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"string (string s)\"},\n\t{78,\t\"setspawnparms\",\tNULL, {&type_entity},\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"void (entity e)\"},\n\n//quakeworld specific\n\t{79,\t\"logfrag\",\t\t\tNULL, {&type_entity, &type_entity},\t\t\t\t\t\t\t\t\t\t\t\"void(entity killer, entity killee)\"},\n\t{80,\t\"infokey\",\t\t\t&type_string, {&type_entity, &type_string},\t\t\t\t\t\t\t\t\t\"string(entity e, string key)\"},\n\t{81,\t\"stof\",\t\t\t\t&type_float, {&type_string},\t\t\t\t\t\t\t\t\t\t\t\t\"float(string s)\"},\n\t{82,\t\"multicast\",\t\tNULL, {&type_vector, &type_float},\t\t\t\t\t\t\t\t\t\t\t\"void(vector where, float set)\"},\n/*\n//these are mvdsv specific\n\t{83,\t\"executecmd\",\t\tNULL, {NULL},\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tNULL},\n\t{84,\t\"tokenize\",\t\t\tNULL, {&type_string},\t\t\t\t\t\t\t\t\t\t\t\t\t\tNULL},\n\t{85,\t\"argc\",\t\t\t\t&type_float, {NULL},\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"float()\"},\n\t{86,\t\"argv\",\t\t\t\t&type_string, {&type_float},\t\t\t\t\t\t\t\t\t\t\t\t\"string(float f)\"},\n\t{87,\t\"teamfield\",\t\tNULL, {NULL},\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"void(.string fs)\"},\n\t{88,\t\"substr\",\t\t\t&type_string, {&type_string, &type_float, &type_float},\t\t\t\t\t\t\"string(string, float, float)\"},\n\t{89,\t\"strcat\",\t\t\t&type_string, {&type_string,&type_string,&type_string,&type_string,&type_string,&type_string,&type_string,&type_string},\t\t\t\t\t\"string (...)\"},\n\t{90,\t\"strlen\",\t\t\t&type_float, {&type_string},\t\t\t\t\t\t\t\t\t\t\t\t\"float(string s)\"},\n\t{91,\t\"str2byte\",\t\t\t&type_float, {&type_string},\t\t\t\t\t\t\t\t\t\t\t\t\"float(string s)\"},\n\t{92,\tNULL,\t\t\t\tNULL, {NULL},\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tNULL},\n\t{93,\t\"newstr\",\t\t\t&type_string, {&type_string},\t\t\t\t\t\t\t\t\t\t\t\t\"string(...)\"},\n\t{94,\t\"freestr\",\t\t\tNULL, {&type_string},\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"void(string s)\"},\n\t{95,\t\"conprint\",\t\t\tNULL, {NULL},\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tNULL},\n\t{96,\t\"readcmd\",\t\t\t&type_string, {&type_string},\t\t\t\t\t\t\t\t\t\t\t\t\"string(string cmd)\"},\n\t{97,\t\"strcpy\",\t\t\tNULL, {NULL},\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tNULL},\n\t{98,\t\"strstr\",\t\t\t&type_string, {&type_string, &type_string},\t\t\t\t\t\t\t\t\t\"string(string str, string sub)\"},\n\t{99,\t\"strncpy\",\t\t\tNULL, {NULL},\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tNULL},\n\t{100,\t\"log\",\t\t\t\tNULL, {NULL},\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tNULL},\n\t{101,\t\"redirectcmd\",\t\tNULL, {NULL},\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tNULL},\n\t{102,\t\"calltimeofday\",\tNULL, {NULL},\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tNULL},\n\t{103,\t\"forcedemoframe\",\tNULL, {NULL},\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tNULL},\n*/\n\n//some QSG extensions\n\n\t{83,\tNULL,\t\t\t\tNULL, {NULL},\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tNULL},\n\t{84,\tNULL,\t\t\t\tNULL, {NULL},\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tNULL},\n\t{85,\tNULL,\t\t\t\tNULL, {NULL},\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tNULL},\n\t{86,\tNULL,\t\t\t\tNULL, {NULL},\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tNULL},\n\t{87,\tNULL,\t\t\t\tNULL, {NULL},\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tNULL},\n\t{88,\tNULL,\t\t\t\tNULL, {NULL},\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tNULL},\n\t{89,\tNULL,\t\t\t\tNULL, {NULL},\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tNULL},\n\t{90,\t\"tracebox\",\t\tNULL, {&type_vector, &type_vector, &type_vector, &type_vector, &type_float, &type_entity},\t\"void(vector start, vector mins, vector maxs, vector end, float nomonsters, entity ent)\"},\n\t{91,\t\"randomvec\",\t&type_vector, {NULL},\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"vector()\"},\n\t{92,\t\"getlight\",\t\t&type_vector, {&type_vector},\t\t\t\t\t\t\t\t\t\t\t\t\t\"vector(vector org)\"},\n\t{93,\t\"registercvar\",\t&type_float, {&type_string, &type_string},\t\t\t\t\t\t\t\t\t\t\"float(string cvarname, string defaultvalue)\"},\n\t{94,\t\"min\",\t\t\t&type_float, {&type_float,&type_float,&type_float,&type_float,&type_float,&type_float,&type_float,&type_float},\"float(float a, float b, ...)\"},\n\t{95,\t\"max\",\t\t\t&type_float, {&type_float,&type_float,&type_float,&type_float,&type_float,&type_float,&type_float,&type_float},\"float(float a, float b, ...)\"},\n\t{96,\t\"bound\",\t\t&type_float, {&type_float,&type_float,&type_float},\t\t\t\t\t\t\t\t\"float(float minimum, float val, float maximum)\"},\n\t{97,\t\"pow\",\t\t\t&type_float, {&type_float,&type_float},\t\t\t\t\t\t\t\t\t\t\t\"float(float value, float exp)\"},\n\t{98,\t\"findfloat\",\t&type_entity, {&type_entity,&type_field,&type_float},\t\t\t\t\t\t\t\"entity(entity start, .__variant fld, __variant match)\"},\n\n\t{99,\t\"checkextension\",&type_float, {&type_string},\t\t\t\t\t\t\t\t\t\t\t\t\t\"float(string extname)\"},\n\t{100,\t\"builtin_find\",\t&type_float, {&type_string},\t\t\t\t\t\t\t\t\t\t\t\t\t\"float(string builtinname)\"},\n\t{101,\t\"redirectcmd\",\tNULL, {&type_entity,&type_string},\t\t\t\t\t\t\t\t\t\t\t\t\"void(entity to, string str)\"},\n\t{102,\t\"anglemod\",\t\t&type_float, {&type_float},\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"float(float value)\"},\n\t{103,\t\"cvar_string\",\t&type_string, {&type_string},\t\t\t\t\t\t\t\t\t\t\t\t\t\"string(string cvarname)\"},\n\n\t{104,\t\"showpic\",\t\tNULL, {NULL},\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"void(string slot, string picname, float x, float y, float zone, optional entity player)\"},\n\t{105,\t\"hidepic\",\t\tNULL, {NULL},\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"void(string slot, optional entity player)\"},\n\t{106,\t\"movepic\",\t\tNULL, {NULL},\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"void(string slot, float x, float y, float zone, optional entity player)\"},\n\t{107,\t\"changepic\",\tNULL, {NULL},\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"void(string slot, string picname, optional entity player)\"},\n\t{108,\t\"showpicent\",\tNULL, {NULL},\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"void(string slot, entity player)\"},\n\t{109,\t\"hidepicent\",\tNULL, {NULL},\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"void(string slot, entity player)\"},\n\n\t{110,\t\"fopen\",\t\t&type_float, {&type_string,&type_float},\t\t\t\t\t\t\t\t\t\"float(string filename, float mode, optional float mmapminsize)\"},\n\t{111,\t\"fclose\",\t\tNULL, {&type_float},\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"void(float fhandle)\"},\n\t{112,\t\"fgets\",\t\t&type_string, {&type_float,&type_string},\t\t\t\t\t\t\t\t\t\"string(float fhandle)\"},\n\t{113,\t\"fputs\",\t\tNULL, {&type_float,&type_string},\t\t\t\t\t\t\t\t\t\t\t\"void(float fhandle, string s, optional string s2, optional string s3, optional string s4, optional string s5, optional string s6, optional string s7)\"},\n\t{114,\t\"strlen\",\t\t&type_float, {&type_string},\t\t\t\t\t\t\t\t\t\t\t\t\"float(string s)\"},\n\t{115,\t\"strcat\",\t\t&type_string, {&type_string,&type_string,&type_string,&type_string,&type_string,&type_string,&type_string,&type_string},\"string(string s1, optional string s2, optional string s3, optional string s4, optional string s5, optional string s6, optional string s7, optional string s8)\"},\n\t{116,\t\"substring\",\t&type_string, {&type_string,&type_float,&type_float},\t\t\t\t\t\t\"string(string s, float start, float length)\"},\n\t{117,\t\"stov\",\t\t\t&type_vector, {&type_string},\t\t\t\t\t\t\t\t\t\t\t\t\"vector(string s)\"},\n\t{118,\t\"strzone\",\t\t&type_string, {&type_string},\t\t\t\t\t\t\t\t\t\t\t\t\"string(string s, ...)\"},\n\t{119,\t\"strunzone\",\tNULL, {&type_string},\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"void(string s)\"},\n};\n\nchar *DecompileValueString(etype_t type, void *val);\nQCD_def_t *DecompileGetParameter(gofs_t ofs);\nQCD_def_t *DecompileFindGlobal(const char *name);\nchar *DecompilePrintParameter(QCD_def_t * def);\nQCD_def_t *DecompileFunctionGlobal(int funcnum);\n\nchar *ReadProgsCopyright(char *buf, size_t bufsize)\n{\n\tchar *copyright, *e;\n\tdprograms_t *progs = (dprograms_t*)buf;\n\tint lowest = progs->ofs_statements;\n\tlowest = min(lowest, progs->ofs_globaldefs);\n\tlowest = min(lowest, progs->ofs_fielddefs);\n\tlowest = min(lowest, progs->ofs_functions);\n\tlowest = min(lowest, progs->ofs_strings);\n\tlowest = min(lowest, progs->ofs_globals);\n\tlowest = min(lowest, progs->ofs_fielddefs);\n\n\tcopyright = (char*)(progs+1);\n\tif (!strncmp(\"\\r\\n\\r\\n\", copyright, 4))\n\t{\n\t\tcopyright += 4;\n\t\te = copyright+strlen(copyright)+1;\n\t\tif (e && !strncmp(e, \"\\r\\n\\r\\n\", 4))\n\t\t{\n\t\t\tif (e+4 <= buf+lowest)\n\t\t\t{\n\t\t\t\treturn copyright;\n\t\t\t}\n\n\t\t}\n\t}\n\treturn NULL;\n}\n\nint DecompileReadData(const char *srcfilename, char *buf, size_t bufsize)\n{\n\tdprograms_t progs;\n\tint i, j;\n\tvoid *p;\n\tchar name[1024];\n\tQCD_def_t *fd;\n\n\tint stsz = 16, defsz=16;\n//\tint quakeforge = false;\n\t\n\tmemcpy(&progs, buf, sizeof(progs));\n\n\tif (progs.version == PROG_QTESTVERSION)\n\t{\n\t\tdefsz = -32;\t//ddefs_t is 32bit\n\t\tstsz = -16;\t//statements is mostly 16bit. there's some line numbers in there too.\n\t}\n\telse if (progs.version == PROG_VERSION)\n\t\tstsz = defsz = 16;\n\telse if (progs.version == 7)\n\t{\n\t\tif (progs.secondaryversion == PROG_SECONDARYVERSION16)\n\t\t{\n\t\t\t//regular 16bit progs, just an extended instruction set probably.\t\t\n\t\t\tstsz = defsz = 16;\n\t\t}\n\t\telse if (progs.secondaryversion == PROG_SECONDARYVERSION32)\n\t\t{\n\t\t\t//32bit fte progs. everything is 32bit.\n\t\t\tstsz = defsz = 32;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t//progs is kk7 (certain QW TF mods). defs are 16bit but statements are 32bit. so this is unusable for saved games.\n\t\t\tstsz = 32;\n\t\t\tdefsz = 16;\t//gah! fucked!\n\t\t}\n\t}\n\telse\n\t{\n\t\tstsz = defsz = 16;\n//\t\tquakeforge = true;\n\t}\n\n\tstrings = buf + progs.ofs_strings;\n\tstrofs = progs.numstrings;\n\n\tnumstatements = progs.numstatements;\n\n//\tif (numstatements > MAX_STATEMENTS)\n//\t\tSys_Error(\"Too many statements\");\n\tif (stsz == 32)\n\t\tstatements = (dstatement32_t*)(buf+progs.ofs_statements);\n\telse if (stsz == 16)\n\t{\t//need to expand the statements to 32bit.\n\t\tconst dstatement16_t\t*statements6 = (const dstatement16_t*)(buf+progs.ofs_statements);\n\t\tstatements = malloc(numstatements * sizeof(*statements));\n\t\tfor (i = 0; i < numstatements; i++)\n\t\t{\n\t\t\tstatements[i].op = statements6[i].op;\n\n\t\t\tif (statements[i].op == OP_GOTO)\n\t\t\t\tstatements[i].a = (signed short)statements6[i].a;\n\t\t\telse\n\t\t\t\tstatements[i].a = (unsigned short)statements6[i].a;\n\n\t\t\tif (statements[i].op == OP_IF_I || statements[i].op == OP_IFNOT_I ||\n\t\t\t\tstatements[i].op == OP_IF_F || statements[i].op == OP_IFNOT_F ||\n\t\t\t\tstatements[i].op == OP_IF_S || statements[i].op == OP_IFNOT_S ||\n\t\t\t\tstatements[i].op == OP_CASE || statements[i].op == OP_SWITCH_F)\n\t\t\t\tstatements[i].b = (signed short)statements6[i].b;\n\t\t\telse\n\t\t\t\tstatements[i].b = (unsigned short)statements6[i].b;\n\n\t\t\tif (statements[i].op == OP_CASERANGE)\n\t\t\t\tstatements[i].c = (signed short)statements6[i].c;\n\t\t\telse\n\t\t\t\tstatements[i].c = (unsigned short)statements6[i].c;\n\t\t}\n\t}\n\telse if (stsz == -16)\n\t{\n\t\tconst qtest_statement_t\t*statements3 = (const qtest_statement_t*)(buf+progs.ofs_statements);\n\t\tstatements = malloc(numstatements * sizeof(*statements));\n\t\tfor (i = 0; i < numstatements; i++)\n\t\t{\n\t\t\tstatements[i].op = statements3[i].op;\n\n\t\t\tif (statements[i].op == OP_GOTO)\n\t\t\t\tstatements[i].a = (signed short)statements3[i].a;\n\t\t\telse\n\t\t\t\tstatements[i].a = (unsigned short)statements3[i].a;\n\n\t\t\tif (statements[i].op == OP_IF_I || statements[i].op == OP_IFNOT_I ||\n\t\t\t\tstatements[i].op == OP_IF_F || statements[i].op == OP_IFNOT_F ||\n\t\t\t\tstatements[i].op == OP_IF_S || statements[i].op == OP_IFNOT_S ||\n\t\t\t\tstatements[i].op == OP_CASE || statements[i].op == OP_SWITCH_F)\n\t\t\t\tstatements[i].b = (signed short)statements3[i].b;\n\t\t\telse\n\t\t\t\tstatements[i].b = (unsigned short)statements3[i].b;\n\n\t\t\tif (statements[i].op == OP_CASERANGE)\n\t\t\t\tstatements[i].c = (signed short)statements3[i].c;\n\t\t\telse\n\t\t\t\tstatements[i].c = (unsigned short)statements3[i].c;\n\t\t}\n\t}\n\telse\n\t\texterns->Sys_Error(\"Unrecognised progs version\");\n\n\tnumfunctions = progs.numfunctions;\n//\tfunctions = (dfunction_t*)(buf+progs.ofs_functions);\n\tDecompileProfiles = calloc(numfunctions, sizeof(*DecompileProfiles));\n\trettypes = calloc(numfunctions, sizeof(*rettypes));\n\n\tnumglobaldefs = progs.numglobaldefs;\n\tnumfielddefs = progs.numfielddefs;\n\tif (defsz == 16)\n\t{\n\t\tconst dfunction_t *funcin = (const dfunction_t*)(buf+progs.ofs_functions);\n\t\tconst ddef16_t\t*gd16 = (const ddef16_t*)(buf+progs.ofs_globaldefs);\n\t\tconst ddef16_t\t*fd16 = (const ddef16_t*)(buf+progs.ofs_fielddefs);\n\t\tglobals = malloc(numglobaldefs * sizeof(*globals));\n\t\tfor (i = 0; i < numglobaldefs; i++)\n\t\t{\n\t\t\tglobals[i].ofs = gd16[i].ofs;\n\t\t\tglobals[i].s_name = GetString(gd16[i].s_name);\n\t\t\tglobals[i].type = gd16[i].type;\n\t\t}\n\n\t\tfields = malloc(numfielddefs * sizeof(*fields));\n\t\tfor (i = 0; i < numfielddefs; i++)\n\t\t{\n\t\t\tfields[i].ofs = fd16[i].ofs;\n\t\t\tfields[i].s_name = GetString(fd16[i].s_name);\n\t\t\tfields[i].type = fd16[i].type;\n\t\t}\n\n\t\tfunctions = malloc(numfunctions * sizeof(*functions));\n\t\tfor (i = 0; i < numfunctions; i++)\n\t\t{\n\t\t\tfunctions[i].first_statement = funcin[i].first_statement;\t// negative numbers are builtins\n\t\t\tfunctions[i].parm_start = funcin[i].parm_start;\n\t\t\tfunctions[i].locals = funcin[i].locals;\t\t\t\t// total ints of parms + locals\n\n\t\t\tfunctions[i].profile = funcin[i].profile;\t\t// runtime\n\n\t\t\tfunctions[i].s_name = GetString(funcin[i].s_name);\n\t\t\tfunctions[i].s_file = GetString(funcin[i].s_file);\t\t\t// source file defined in\n\n\t\t\tfunctions[i].numparms = funcin[i].numparms;\n\t\t\tfor (j = 0; j < MAX_PARMS; j++)\n\t\t\t\tfunctions[i].parm_size[j] = funcin[i].parm_size[j];\n\t\t}\n\t}\n\telse if (defsz == 32)\n\t{\n\t\tconst dfunction_t *funcin = (const dfunction_t*)(buf+progs.ofs_functions);\n\t\tconst ddef32_t\t*gdin = (const ddef32_t*)(buf+progs.ofs_globaldefs);\n\t\tconst ddef32_t\t*fdin = (const ddef32_t*)(buf+progs.ofs_fielddefs);\n\t\tglobals = malloc(numglobaldefs * sizeof(*globals));\n\t\tfor (i = 0; i < numglobaldefs; i++)\n\t\t{\n\t\t\tglobals[i].ofs = gdin[i].ofs;\n\t\t\tglobals[i].s_name = GetString(gdin[i].s_name);\n\t\t\tglobals[i].type = gdin[i].type;\n\t\t}\n\n\t\tfields = malloc(numfielddefs * sizeof(*fields));\n\t\tfor (i = 0; i < numfielddefs; i++)\n\t\t{\n\t\t\tfields[i].ofs = fdin[i].ofs;\n\t\t\tfields[i].s_name = GetString(fdin[i].s_name);\n\t\t\tfields[i].type = fdin[i].type;\n\t\t}\n\n\t\tfunctions = malloc(numfunctions * sizeof(*functions));\n\t\tfor (i = 0; i < numfunctions; i++)\n\t\t{\n\t\t\tfunctions[i].first_statement = funcin[i].first_statement;\t// negative numbers are builtins\n\t\t\tfunctions[i].parm_start = funcin[i].parm_start;\n\t\t\tfunctions[i].locals = funcin[i].locals;\t\t\t\t// total ints of parms + locals\n\n\t\t\tfunctions[i].profile = funcin[i].profile;\t\t// runtime\n\n\t\t\tfunctions[i].s_name = strings + funcin[i].s_name;\n\t\t\tfunctions[i].s_file = strings + funcin[i].s_file;\t\t\t// source file defined in\n\n\t\t\tfunctions[i].numparms = funcin[i].numparms;\n\t\t\tfor (j = 0; j < MAX_PARMS; j++)\n\t\t\t\tfunctions[i].parm_size[j] = funcin[i].parm_size[j];\n\t\t}\n\t}\n\telse if (defsz == -32)\n\t{\n\t\tconst qtest_function_t *funcin = (const qtest_function_t*)(buf+progs.ofs_functions);\n\t\tconst qtest_def_t\t*gdqt = (const qtest_def_t*)(buf+progs.ofs_globaldefs);\n\t\tglobals = malloc(numglobaldefs * sizeof(*globals));\n\t\tfor (i = 0; i < numglobaldefs; i++)\n\t\t{\n\t\t\tglobals[i].ofs = gdqt[i].ofs;\n\t\t\tglobals[i].s_name = strings + gdqt[i].s_name;\n\t\t\tglobals[i].type = gdqt[i].type;\n\t\t}\n\n\n\t\tgdqt = (const qtest_def_t*)(buf+progs.ofs_fielddefs);\n\t\tfields = malloc(numfielddefs * sizeof(*fields));\n\t\tfor (i = 0; i < numfielddefs; i++)\n\t\t{\n\t\t\tfields[i].ofs = gdqt[i].ofs;\n\t\t\tfields[i].s_name = strings + gdqt[i].s_name;\n\t\t\tfields[i].type = gdqt[i].type;\n\t\t}\n\n\t\tfunctions = malloc(numfunctions * sizeof(*functions));\n\t\tfor (i = 0; i < numfunctions; i++)\n\t\t{\n\t\t\tfunctions[i].first_statement = funcin[i].first_statement;\t// negative numbers are builtins\n\t\t\tfunctions[i].parm_start = funcin[i].parm_start;\n\t\t\tfunctions[i].locals = funcin[i].locals;\t\t\t\t// total ints of parms + locals\n\n\t\t\tfunctions[i].profile = funcin[i].profile;\t\t// runtime\n\n\t\t\tfunctions[i].s_name = strings + funcin[i].s_name;\n\t\t\tfunctions[i].s_file = strings + funcin[i].s_file;\t\t\t// source file defined in\n\n\t\t\tfunctions[i].numparms = funcin[i].numparms;\n\t\t\tfor (j = 0; j < MAX_PARMS; j++)\n\t\t\t\tfunctions[i].parm_size[j] = funcin[i].parm_size[j];\n\t\t}\n\t}\n\telse\n\t{\n\t\tprintf(\"fatal error: unsupported def size\\n\");\n\t\texit(1);\n\t}\n\n\tpr_globals = (float*)(buf+progs.ofs_globals);\n\tnumpr_globals = progs.numglobals;\n\n\tprintf(\"Decompiling...\\n\");\n\tprintf(\"Read Data from %s:\\n\", srcfilename);\n\tprintf(\"Total Size is %6i\\n\", (unsigned int)bufsize);\n\tprintf(\"Version Code is %i\\n\", progs.version);\n\tprintf(\"CRC is %i\\n\", progs.crc);\n\tprintf(\"%6i strofs\\n\", strofs);\n\tprintf(\"%6i numstatements\\n\", numstatements);\n\tprintf(\"%6i numfunctions\\n\", numfunctions);\n\tprintf(\"%6i numglobaldefs\\n\", numglobaldefs);\n\tprintf(\"%6i numfielddefs\\n\", numfielddefs);\n\tprintf(\"%6i numpr_globals\\n\", numpr_globals);\n\tprintf(\"----------------------\\n\");\n\n\tif (numpr_globals > MAX_REGS)\n\t{\n\t\tprintf(\"fatal error: progs exceeds a limit\\n\");\n\t\texit(1);\n\t}\n\n\tofs_return = OFS_RETURN;\n\tfor (i = 0; i < 8; i++)\n\t\tofs_parms[i] = OFS_PARM0 + i * 3;\n\tofs_size = 3;\n\n/*\n\tif (quakeforge)\n\t{\n\t\tint typeremap[] = {ev_void, ev_string, ev_float, ev_vector, ev_entity, ev_field, ev_function, ev_pointer, ev_quat, ev_integer, ev_uinteger};\n\t\tfor (i = 1; i < numglobaldefs; i++)\n\t\t{\n\t\t\tglobals[i].type = (globals[i].type & DEF_SAVEGLOBGAL) | typeremap[globals[i].type&~DEF_SAVEGLOBGAL];\n\t\t}\n\t\tfor (i = 1; i < numfielddefs; i++)\n\t\t{\n\t\t\tfields[i].type = (fields[i].type & DEF_SAVEGLOBGAL) | typeremap[fields[i].type&~DEF_SAVEGLOBGAL];\n\t\t}\n\n\t\tfor (i = 1; i < numstatements; i++)\n\t\t{\n\t\t\tif (statements[i].op >= OP_H2_FIRST)// && statements[i].op <= OP_H2_FIRST+sizeof(quakeforgeremap)/sizeof(quakeforgeremap[0]))\n\t\t\t\tstatements[i].op = quakeforgeremap[statements[i].op-OP_H2_FIRST];\n\t\t}\n\n\t\tfd = DecompileFindGlobal(\".zero\");\n\t\tif (fd)\n\t\t\tfd->ofs = -1;\n\n\t\tfd = DecompileFindGlobal(\".return\");\n\t\tif (fd)\n\t\t{\n\t\t\tofs_return = fd->ofs;\n\t\t\tfd->ofs = -1;\n\t\t}\n\n\t\tfor (i = 0; i < 8; i++)\n\t\t{\n\t\t\tQC_snprintfz(name, sizeof(name), \".param_%i\", i);\n\t\t\tfd = DecompileFindGlobal(name);\n\t\t\tif (fd)\n\t\t\t{\n\t\t\t\tofs_parms[i] = fd->ofs;\n\t\t\t\tfd->ofs = -1;\n\t\t\t}\n\t\t}\n\n\t\tfd = DecompileFindGlobal(\".param_size\");\n\t\tif (fd)\n\t\t\tofs_size = ((int*)pr_globals)[fd->ofs];\n\t}\n*/\n\n\t//fix up the globaldefs\n\tfor (i = 1; i < numglobaldefs; i++)\n\t{\n\t\tif (globals[i].ofs < RESERVED_OFS)\n\t\t\tglobals[i].ofs += numpr_globals;\n\t}\n// fix up the functions\n\tfor (i = 1; i < numfunctions; i++)\n\t{\n\t\tif ((unsigned)(functions[i].s_name-strings) >= (unsigned)strofs || strlen(functions[i].s_name) <= 0)\n\t\t{\n\t\t\tfd = DecompileFunctionGlobal(i);\n\t\t\tif (fd)\n\t\t\t{\n\t\t\t\tfunctions[i].s_name = fd->s_name;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tQC_snprintfz(name, sizeof(name), \"function%i\", i);\n\t\t\tname[strlen(name)] = 0;\n\t\t\tp = malloc(strlen(name) + 1);\n\t\t\tstrcpy(p, name);\n\t\t\tfunctions[i].s_name = p;\n\t\t}\n\t\tif (functions[i].first_statement > 0 && !functions[i].locals && functions[i].numparms)\n\t\t{\t//vanilla qcc apparently had a bug\n\t\t\tfor (j = 0; j < functions[i].numparms; j++)\n\t\t\t\tfunctions[i].locals += functions[i].parm_size[j];\n\t\t}\n\t}\n\n\treturn progs.version;\n}\n\nstatic void DecompileDetermineArrays(void)\n{\n\tint i, j;\n\tfor (i = 0; i < numstatements; i++)\n\t{\n\t\tif (statements[i].op >= OP_FETCH_GBL_F && statements[i].op <= OP_FETCH_GBL_FNC)\n\t\t{\n\t\t\tfor (j = 1; j < numglobaldefs; j++)\n\t\t\t{\n\t\t\t\tif (globals[j].ofs == statements[i].a)\n\t\t\t\t{\n\t\t\t\t\tglobals[j].type |= DEF_H2ARRAY;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nstatic etype_t DecompileGetFieldTypeByDef(QCD_def_t *def)\n{\n\tint i;\n\tint ofs = ((int*)pr_globals)[def->ofs];\n\n\tfor (i = 1; i < numfielddefs; i++)\n\t\tif (fields[i].ofs == ofs) \n\t\t{\n\t\t\tif (!strcmp(GetNameString(def->s_name), GetNameString(fields[i].s_name)))\n\t\t\t\treturn fields[i].type;\n\t\t}\n\treturn ev_void;\n}\nstatic const char *DecompileGetFieldNameIdxByFinalOffset(int ofs)\n{\n\tint i;\n\n\tfor (i = 1; i < numfielddefs; i++)\n\t\tif (fields[i].ofs == ofs) \n\t\t{\n\t\t\treturn GetNameString(fields[i].s_name);\n\t\t}\n\treturn \"UNKNOWN FIELD\";\n}\nvoid DecompileGetFieldNameIdxByFinalOffset2(char *out, size_t outsize, int ofs)\n{\n\tint i;\n\n\tfor (i = 1; i < numfielddefs; i++)\n\t{\n\t\tif (fields[i].ofs == ofs) \n\t\t{\n\t\t\tQC_snprintfz(out, outsize, \"%s\", GetNameString(fields[i].s_name));\n\t\t\treturn;\n\t\t}\n\t\telse if (fields[i].type == ev_vector && fields[i].ofs+1 == ofs)\n\t\t{\n\t\t\tQC_snprintfz(out, outsize, \"%s_y\", GetNameString(fields[i].s_name));\n\t\t\treturn;\n\t\t}\n\t\telse if (fields[i].type == ev_vector && fields[i].ofs+2 == ofs)\n\t\t{\n\t\t\tQC_snprintfz(out, outsize, \"%s_z\", GetNameString(fields[i].s_name));\n\t\t\treturn;\n\t\t}\n\t}\n\tQC_snprintfz(out, outsize, \"<FIELD@%i>\", ofs);\n}\n\nint \nDecompileAlreadySeen(char *fname, vfile_t **rfile)\n{\n\tint ret = 1;\n\n\tvfile_t *file;\n\n\tfile = QCC_FindVFile(fname);\n\tif (!file)\n\t{\n\t\tret = 0;\n\t\tif (rfile)\n\t\t{\n\t\t\tchar *header = \"//Decompiled code. Please respect the original copyright.\\n\";\n\t\t\t*rfile = QCC_AddVFile(fname, header, strlen(header));\n\t\t\tAddSourceFile(\"progs.src\",\tfname);\n\t\t}\n\t}\n\telse if (rfile)\n\t\t*rfile = file;\n\n\treturn ret;\n}\n\nchar *DecompileReturnType(QCD_function_t *df);\n\nchar *DecompileAgressiveType(QCD_function_t *df, dstatement_t *last, gofs_t ofs)\n{\n\tQCD_def_t *par;\n\tpar = DecompileGetParameter(ofs);\n\tif (par)\t//single = intended\n\t{\n\t\treturn type_name(par);\n\t}\n\n\tif (ofs == ofs_return && ((last->op >= OP_CALL0 && last->op <= OP_CALL8) || (last->op >= OP_CALL1H && last->op <= OP_CALL8H)))\n\t{\t//offset is a return value, go look at the called function's return type.\n\t\treturn DecompileReturnType(functions + ((int*)pr_globals)[last->a]);\n\t}\n\n\twhile(last >= &statements[df->first_statement])\n\t{\n\t\tif (last->c == ofs && \n\t\t\tpr_opcodes[last->op].associative == ASSOC_LEFT &&\n\t\t\tpr_opcodes[last->op].priorityclass)\n\t\t{\n\t\t\t//previous was an operation into the temp\n\t\t\treturn type_names[(*pr_opcodes[last->op].type_c)->type];\n\n\n\t//\t\t\t\t\tsprintf(fname, \"%s \", temp_type6(rds->a, rds, df));\n\t\t}\n\t\tlast--;\n\t}\n\n\treturn NULL;\t//got to start of function... shouldn't really happen.\n}\n\nstatic unsigned int DecompileBuiltin(QCD_function_t *df)\n{\n\tunsigned int bi, i;\n\tif (df->first_statement > 0)\n\t\treturn 0;\t//not a builtin.\n\tbi = -df->first_statement;\n\t//okay, so this is kinda screwy, different mods have different sets of builtins, and a load of fte's are #0 too\n\t//so just try to match by name first... lots of scanning. :(\n\tif (*df->s_name)\n\t{\n\t\tconst char *biname = GetNameString(df->s_name);\n\t\tfor (i = 0; i < (sizeof(builtins)/sizeof(builtins[0])); i++)\n\t\t{\n\t\t\tif (!builtins[i].name)\n\t\t\t\tcontinue;\n\t\t\tif (!strcmp(builtins[i].name, biname))\n\t\t\t{\t//okay, this one matched.\n\t\t\t\tbi = i;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\tif (bi >= (sizeof(builtins)/sizeof(builtins[0])))\n\t\treturn 0; //unknown.\n\treturn bi;\n}\n\nchar *DecompileReturnType(QCD_function_t *df)\n{\n\tdstatement_t *ds;\n\tunsigned short dom;\n\tpbool foundret = false;\n\tstatic int recursion;\n\tchar *ret = NULL;\t//return null if we don't know.\n\tint couldbeastring = true;\n\n\tif (df->first_statement <= 0)\n\t{\n\t\tunsigned int bi = DecompileBuiltin(df);\n\t\tif (bi)\n\t\t\tif (builtins[bi].returns)\n\t\t\t\treturn type_names[(*builtins[bi].returns)->type];\n\n\t\treturn \"void\";\t//no returns statements found\n\t}\n\n\tif (rettypes[df - functions])\n\t\treturn rettypes[df - functions];\n\n\trecursion++;\n\n\tds = statements + df->first_statement;\n\n\t/*\n\t * find a return statement, to determine the result type \n\t */\n\n\twhile (1) \n\t{\n\t\tdom = (ds->op) % OP_MARK_END_ELSE;\n\t\tif (!dom)\n\t\t\tbreak;\n//\t\tif (dom == OPQF_RETURN_V)\n//\t\t\tbreak;\n\t\tif (dom == OP_RETURN)\n\t\t{\n\t\t\tif (ds->a != 0)\t//some code is buggy.\n\t\t\t{\n\t\t\t\tfoundret = true;\n\n\t\t\t\tif (recursion < 10)\n\t\t\t\t{\n\t\t\t\t\tret = DecompileAgressiveType(df, ds-1, ds->a);\n\t\t\t\t\tif (ret)\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tif (((int*)pr_globals)[ds->a] < 0 && ((int*)pr_globals)[ds->a] >= strofs)\n\t\t\t\t\tcouldbeastring = false;\t//definatly not\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tchar buf[64];\n\t\t\t\t\tQC_snprintfz(buf, sizeof(buf), \"%f\", pr_globals[ds->a]);\n\t\t\t\t\tif (strcmp(buf, \"0.000000\"))\n\t\t\t\t\t\tcouldbeastring = false;\t//doesn't fit the profile\n\t\t\t\t}\n\t\t\t}\n\n\t\t}\n\t\tds++;\n\t}\n\trecursion--;\n\n\tif (foundret)\n\t{\n\t\tif (!ret)\n\t\t{\n\t\t\tif (couldbeastring)\n\t\t\t\tret = \"string /*WARNING: could not determine return type*/\";\n\t\t\telse\n\t\t\t\tret = \"float /*WARNING: could not determine return type*/\";\n\t\t}\n\t}\n\telse\n\t\tret = \"void\";\t//no returns statements found\n\n\trettypes[df - functions] = ret;\n\treturn ret;\n}\n\nvoid DecompileCalcProfiles(void)\n{\n\n\tint i, ps;\n\tgofs_t j;\n\tchar *knew;\n\tstatic char fname[512];\n\tstatic char line[512];\n\tQCD_function_t *df;\n\tQCD_def_t *par;\n\n\tfor (i = 1; i < numfunctions; i++)\n\t{\n\n\t\tdf = functions + i;\n\t\tfname[0] = '\\0';\n\t\tline[0] = '\\0';\n\t\tDecompileProfiles[i] = NULL;\n\n\t\tif (df->first_statement <= 0) \n\t\t{\n\t\t\tunsigned int bi = DecompileBuiltin(df);\n\t\t\tif (bi && builtins[bi].text)\n\t\t\t\tQC_snprintfz(fname, sizeof(fname), \"%s %s\", builtins[bi].text, GetNameString(functions[i].s_name));\n\t\t\telse\n\t\t\t{\n\t\t\t\tQC_snprintfz(fname, sizeof(fname), \"__variant(...) %s\", GetNameString(functions[i].s_name));\n\t\t\t\tprintf(\"warning: unknown builtin %s\\n\", GetNameString(functions[i].s_name));\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tchar *rettype;\n\n\t\t\trettype = DecompileReturnType(df);\n\t\t\tif (!rettype)\n\t\t\t{\t//but we do know that it's not void\n\t\t\t\trettype = \"float /*WARNING: could not determine return type*/\";\n\t\t\t}\n\t\t\tstrcpy(fname, rettype);\n\t\t\tstrcat(fname, \"(\");\n\n\t\t\t/*\n\t\t\t * determine overall parameter size \n\t\t\t */\n\n\t\t\tfor (j = 0, ps = 0; j < df->numparms; j++)\n\t\t\t\tps += df->parm_size[j];\n\n\t\t\tif (ps > 0) \n\t\t\t{\n\t\t\t\tint p;\n\t\t\t\tfor (p = 0, j = df->parm_start; j < (df->parm_start) + ps; p++)\n\t\t\t\t{\n\t\t\t\t\tline[0] = '\\0';\n\t\t\t\t\tpar = DecompileGetParameter(j);\n\t\t\t\t\tif (!par)\n\t\t\t\t\t\tpar = DecompileGetParameter((short)j);\n\n\t\t\t\t\tif (!par)\n\t\t\t\t\t{\n\t\t\t\t\t\t//Error(\"Error - No parameter names with offset %i.\", j);\n//\t\t\t\t\t\tprintf(\"No parameter names with offset %i\\n\", j);\n\t\t\t\t\t\tif (p<8)\n\t\t\t\t\t\t\tj += df->parm_size[p];\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tj += 1;\n\t\t\t\t\t\tif (p<8&&df->parm_size[p] == 3)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (j < (df->parm_start) + ps)\n\t\t\t\t\t\t\t\tQC_snprintfz(line, sizeof(line), \"vector par%i, \", p);\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\tQC_snprintfz(line, sizeof(line), \"vector par%i\", p);\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (j < (df->parm_start) + ps)\n\t\t\t\t\t\t\t\tQC_snprintfz(line, sizeof(line), \"__variant par%i, \", p);\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\tQC_snprintfz(line, sizeof(line), \"__variant par%i\", p);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tif (par->type == ev_vector)\n\t\t\t\t\t\t\tj += 2;\n\t\t\t\t\t\tj++;\n\t\t\t\t\t\tif (j < (df->parm_start) + ps)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tQC_snprintfz(line, sizeof(line), \"%s, \", DecompilePrintParameter(par));\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tQC_snprintfz(line, sizeof(line), \"%s\", DecompilePrintParameter(par));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tstrcat(fname, line);\n\t\t\t\t}\n\t\t\t}\n\t\t\tstrcat(fname, \") \");\n\t\t\tline[0] = '\\0';\n\t\t\tQC_snprintfz(line, sizeof(line), \"%s\", GetNameString(functions[i].s_name));\n\t\t\tstrcat(fname, line);\n\n\t\t}\n\n\t\tknew = (char *)malloc(strlen(fname) + 1);\n\t\tstrcpy(knew, fname);\n\t\tDecompileProfiles[i] = knew;\n\t}\n\n}\n\nQCD_def_t *GlobalAtOffset(QCD_function_t *df, gofs_t ofs)\n{\n\tQCD_def_t *def;\n\tint i, j;\n\n\tdef = globalofsdef[ofs];\n\tif (def)\n\t\treturn def;\n\n\tfor (i = 0; i < numglobaldefs; i++)\n\t{\n\t\tdef = &globals[i];\n\n\t\tif (def->ofs == ofs)\n\t\t{\n\n\t\t\t/*if (!GetString(def->s_name))\n\t\t\t{\n\t\t\t\tchar line[16];\n\t\t\t\tchar *buf;\n\n\t\t\t\tsprintf(line, \"_s_%i\", def->ofs);\t//globals, which are defined after the locals of the function they are first used in...\n\t\t\t\tbuf = malloc(strlen(line)+1);\t\t//must be static variables, but we can't handle them very well\n\t\t\t\tstrcpy(buf, line);\n\t\t\t\tdef->s_name = buf - strings;\n\t\t\t}*/\n\t\t\tglobalofsdef[ofs] = def;\n\t\t\treturn def;\n\t\t}\n\t}\n\n\tif (ofs >= df->parm_start && ofs < df->parm_start + df->locals)\n\t{\n\t\tstatic QCD_def_t parm[8];\n\t\tstatic char *parmnames[] = {\"par0\",\"par1\",\"par2\",\"par3\",\"par4\",\"par5\",\"par6\",\"par7\"};\n\t\tint parmofs = ofs - df->parm_start;\n\t\tfor (i = 0; i < df->numparms && i < 8; i++)\n\t\t{\n\t\t\tif (parmofs < df->parm_size[i])\n\t\t\t{\n\t\t\t\tparm[i].ofs = ofs - parmofs;\n\t\t\t\tparm[i].s_name = parmnames[i];\n\t\t\t\tparm[i].type = ev_void;\n\n\t\t\t\tofs = parm[i].ofs;\n\t\t\t\tfor (j = 0; j < numglobaldefs; j++)\n\t\t\t\t{\n\t\t\t\t\tdef = &globals[j];\n\t\t\t\t\tif (def->ofs == ofs)\n\t\t\t\t\t{\n\t\t\t\t\t\tchar line[256], *buf;\n\t\t\t\t\t\tsprintf(line, \"%s_%c\", GetNameString(def->s_name), 'x'+parmofs);\t//globals, which are defined after the locals of the function they are first used in...\n\t\t\t\t\t\tdef = malloc(sizeof(*def)+strlen(line)+1);\t\t//must be static variables, but we can't handle them very well\n\t\t\t\t\t\tbuf = (char*)(def+1);\n\t\t\t\t\t\tstrcpy(buf, line);\n\t\t\t\t\t\tdef->s_name = buf;\n\t\t\t\t\t\tdef->type = ev_float;\n\t\t\t\t\t\treturn def;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\treturn &parm[i];\n\t\t\t}\n\t\t\tparmofs -= df->parm_size[i];\n\t\t}\n\t\t//moo\n\t}\n\t//FIXME: if its within the current function's bounds, its:\n\t//\twithin param list: argument\n\t//\tnever written: immediate\n\t//\toptimised: a local / locked temp.\n\t//\tvanilla qcc: always a temp (other locals will be named)\n\t//elsewhere:\n\t//\tif its assigned to somewhere, then its a temp\n\t//\totherwise its a const.\n\n\treturn NULL;\n}\n\nchar *DecompileGlobal(QCD_function_t *df, gofs_t ofs, QCC_type_t * req_t)\n{\n\tint i;\n\tQCD_def_t *def;\n\tstatic char line[8192];\n\tchar *res;\n\n\tline[0] = '\\0';\n\n\t/*if (req_t == &def_short)\n\t{\n\t\tQC_snprintfz(line, sizeof(line), \"%ii\", ofs);\n\t\tres = (char *)malloc(strlen(line) + 1);\n\t\tstrcpy(res, line);\n\t\treturn res;\n\t}*/\n\n\tdef = GlobalAtOffset(df, ofs);\n\n\tif (def)\n\t{\n\t\tconst char *defname = GetNameString(def->s_name);\n\n\t\tif (!strcmp(defname, \"IMMEDIATE\") || !strcmp(defname, \".imm\") || !strcmp(defname, \"I+\") || !def->s_name)\n\t\t{\n\t\t\tetype_t ty;\n\t\t\tif (!req_t)\n\t\t\t\tty = def->type;\n\t\t\telse\n\t\t\t{\n\t\t\t\tty = (etype_t)(req_t->type);\n\t\t\t\tif (!ty)\n\t\t\t\t\tty = def->type;\n\t\t\t}\n\t\t\tQC_snprintfz(line, sizeof(line), \"%s\", DecompileValueString(ty, &pr_globals[def->ofs]));\n\t\t}\n\t\telse \n\t\t{\n\t\t\tif (!*defname)\n\t\t\t{\n\t\t\t\tchar line[16];\n\t\t\t\tchar *buf;\n\t\t\t\tQCD_def_t *parent;\n\t\t\t\tif (ofs >= df->parm_start && ofs < df->parm_start + df->locals)\n\t\t\t\t\tgoto lookslikealocal;\n\t\t\t\telse if ((parent = GlobalAtOffset(df, ofs-1)) && parent->type == ev_vector)\n\t\t\t\t{\t// _y\n\t\t\t\t\tQC_snprintfz(line, sizeof(line), \"%s_y\", GetNameString(parent->s_name));\t//globals, which are defined after the locals of the function they are first used in...\n\t\t\t\t\tbuf = malloc(strlen(line)+1);\t\t//must be static variables, but we can't handle them very well\n\t\t\t\t\tstrcpy(buf, line);\n\t\t\t\t\tdef->s_name = buf;\n\t\t\t\t}\n\t\t\t\telse if ((parent = GlobalAtOffset(df, ofs-2)) && parent->type == ev_vector)\n\t\t\t\t{\t// _z\n\t\t\t\t\tQC_snprintfz(line, sizeof(line), \"%s_z\", GetNameString(parent->s_name));\t//globals, which are defined after the locals of the function they are first used in...\n\t\t\t\t\tbuf = malloc(strlen(line)+1);\t\t//must be static variables, but we can't handle them very well\n\t\t\t\t\tstrcpy(buf, line);\n\t\t\t\t\tdef->s_name = buf;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tQC_snprintfz(line, sizeof(line), \"_sloc_%i\", def->ofs);\t//globals, which are defined after the locals of the function they are first used in...\n\t\t\t\t\tbuf = malloc(strlen(line)+1);\t\t//must be static variables, but we can't handle them very well\n\t\t\t\t\tstrcpy(buf, line);\n\t\t\t\t\tdef->s_name = buf;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tQC_snprintfz(line, sizeof(line), \"%s\", GetNameString(def->s_name));\n\t\t\tif (def->type == ev_field && req_t == type_field && req_t->aux_type == type_float && DecompileGetFieldTypeByDef(def) == ev_vector)\n\t\t\t\tstrcat(line, \"_x\");\n\t\t\telse if (def->type == ev_vector && req_t == type_float)\n\t\t\t\tstrcat(line, \"_x\");\n\n\t\t}\n\t\tres = (char *)malloc(strlen(line) + 1);\n\t\tstrcpy(res, line);\n\n\t\treturn res;\n\t}\n\n\tif (ofs >= df->parm_start && ofs < df->parm_start + df->locals)\n\t{\n\t\tint parmofs;\nlookslikealocal:\n\t\tQC_snprintfz(line, sizeof(line), \"local_%i\", ofs);\n\t\tfor (i = 0, parmofs = ofs - df->parm_start; i < df->numparms && i < 8; i++)\n\t\t{\n\t\t\tif (parmofs < df->parm_size[i])\n\t\t\t{\n\t\t\t\tif (parmofs)\n\t\t\t\t\tQC_snprintfz(line, sizeof(line), \"par%i_%c\", i, 'x'+parmofs);\n\t\t\t\telse\n\t\t\t\t\tQC_snprintfz(line, sizeof(line), \"par%i\", i);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tparmofs -= df->parm_size[i];\n\t\t}\n\t\tif (!assumelocals && i == df->numparms)\n\t\t\treturn NULL;\t//we don't know what this is. assume its a temp\n\n\t\tres = (char *)malloc(strlen(line) + 1);\n\t\tstrcpy(res, line);\n\t\treturn res;\n\t}\n\n\tif (assumeglobals)\n\t{\t//unknown globals are normally assumed to be temps\n\t\tif (ofs >= ofs_parms[7]+ofs_size)\n\t\t{\n\t\t\tQC_snprintfz(line, sizeof(line), \"tmp_%i\", ofs);\n\t\t\tres = (char *)malloc(strlen(line) + 1);\n\t\t\tstrcpy(res, line);\n\n\t\t\treturn res;\n\t\t}\n\t}\n\n\treturn NULL;\n}\n\nstatic struct\n{\n\tchar *text;\n\tQCC_type_t *type;\n} IMMEDIATES[MAX_REGS];\ngofs_t DecompileScaleIndex(QCD_function_t *df, gofs_t ofs)\n{\n\tgofs_t nofs = 0;\n\n\t/*if (ofs > ofs_parms[7]+ofs_size)\n\t\tnofs = ofs - df->parm_start + ofs_parms[7]+ofs_size;\n\telse*/\n\t\tnofs = ofs;\n\n\tif ((nofs < 0) || (nofs > MAX_REGS - 1))\n\t{\n\t\tprintf(\"Fatal Error - Index (%i) out of bounds.\\n\", nofs);\n\t\treturn 0;\n\t\texit(1);\n\t}\n\n\treturn nofs;\n}\n\nvoid DecompileImmediate_Free(void)\n{\n\tint i;\n\tfor (i = 0; i < MAX_REGS; i++)\n\t{\n\t\tif (IMMEDIATES[i].text) \n\t\t{\n\t\t\tfree(IMMEDIATES[i].text);\n\t\t\tIMMEDIATES[i].text = NULL;\n\t\t}\n\t}\n}\nvoid DecompileImmediate_Insert(QCD_function_t *df, gofs_t ofs, char *knew, QCC_type_t *type)\n{\n\tQCD_def_t *d;\n\tint nofs;\n\n\tnofs = DecompileScaleIndex(df, ofs);\n\n\tif (IMMEDIATES[nofs].text)\n\t{\n//\t\tfprintf(Decompileofile, \"/*WARNING: Discarding \\\"%s\\\"/\", IMMEDIATES[nofs]);\n\t\tfree(IMMEDIATES[nofs].text);\n\t\tIMMEDIATES[nofs].text = NULL;\n\t}\n\n\n\td = GlobalAtOffset(df, ofs);\n\tif (d && d->s_name)// && strcmp(GetString(d->s_name), \"IMMEDIATE\"))\n\t{\t//every operator has a src (or two) and a dest.\n\t\t//many compilers optimise by using the dest of a maths/logic operator to store to a local/global\n\t\t//they then skip off the storeopcode.\n\t\t//without this, we would never see these stores.\n\t\tIMMEDIATES[nofs].text = NULL;\n\t\tIMMEDIATES[nofs].type = NULL;\n\n\t\tQCC_CatVFile(Decompileofile, \"%s = %s;\\n\", GetNameString(d->s_name), knew);\n\t}\n\telse\n\t{\n\t\tIMMEDIATES[nofs].text = (char *)malloc(strlen(knew) + 1);\n\t\tstrcpy(IMMEDIATES[nofs].text, knew);\n\t\tIMMEDIATES[nofs].type = type;\n\t}\n}\n\nvoid FloatToString(char *out, size_t outsize, float f)\n{\n\tchar *e;\n\tQC_snprintfz(out, outsize, \"%f\", f);\n\n\t//trim any trailing decimals\n\te = strchr(out, '.');\n\tif (e)\n\t{\n\t\te = e+strlen(e);\n\t\twhile (e > out && e[-1] == '0')\n\t\t\te--;\n\t\tif (e > out && e[-1] == '.')\n\t\t\te--;\n\t\t*e = 0;\n\t}\n}\n\nchar *DecompileImmediate_Get(QCD_function_t *df, gofs_t ofs, QCC_type_t *req_t)\n{\n\tchar *res;\n \n\tgofs_t nofs;\n\n\tnofs = DecompileScaleIndex(df, ofs);\n  //  printf(\"DecompileImmediate - Index scale: %i -> %i.\\n\", ofs, nofs);\n\n\t// insert at nofs \n\tif (IMMEDIATES[nofs].text)\n\t{\n\n\t//\tprintf(\"DecompileImmediate - Reading \\\"%s\\\" at index %i.\\n\", IMMEDIATES[nofs], nofs);\n\n\t\tif (IMMEDIATES[nofs].type == type_vector && req_t == type_float)\n\t\t{\n\t\t\tres = (char *)malloc(strlen(IMMEDIATES[nofs].text) + 4);\n\t\t\tif (strchr(IMMEDIATES[nofs].text, '('))\n\t\t\t\tsprintf(res, \"%s[0]\", IMMEDIATES[nofs].text);\n\t\t\telse\n\t\t\t\tsprintf(res, \"%s_x\", IMMEDIATES[nofs].text);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tres = (char *)malloc(strlen(IMMEDIATES[nofs].text) + 1);\n\t\t\tstrcpy(res, IMMEDIATES[nofs].text);\n\t\t}\n\n\t\treturn res;\n\t}\n\telse\n\t{\t//you are now entering the hack zone.\n\t\tchar temp[8192];\n\t\t\n\t\tswitch(req_t?req_t->type:-1)\n\t\t{\n\t\tcase ev_void:\t//for lack of any better ideas.\n\t\tcase ev_float:\n\t\t\t//denormalised floats need special handling.\n\t\t\tif ((0x7fffffff&*(int*)&pr_globals[ofs]) >= 1 && (0x7fffffff&*(int*)&pr_globals[ofs]) < 0x00800000)\n\t\t\t{\n\t\t\t\tQC_snprintfz(temp, sizeof(temp), \"((float)(__variant)%ii)\", *(int*)&pr_globals[ofs]);\n\n//\t\t\t\tif (req_t && *(int*)&pr_globals[ofs] >= 1 && *(int*)&pr_globals[ofs] < strofs)\n//\t\t\t\t\t;\t//failure to break means we'll print out a trailing /*string*/\n//\t\t\t\telse\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tFloatToString(temp, sizeof(temp), pr_globals[ofs]);\n\t\t\t\tbreak;\n\t\t\t}\n\t\tcase ev_string:\n\t\t\t{\n\t\t\t\tconst char *in;\n\t\t\t\tchar *out;\n\t\t\t\tif (((int*)pr_globals)[ofs] < 0 || ((int*)pr_globals)[ofs] > strofs)\n\t\t\t\t{\n\t\t\t\t\tprintf(\"Hey! That's not a string! error in %s\\n\", GetNameString(df->s_name));\n\t\t\t\t\tQC_snprintfz(temp, sizeof(temp), \"%f\", pr_globals[ofs]);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tin = GetString(((int*)pr_globals)[ofs]);\n\t\t\t\tout = temp;\n\t\t\t\tif (req_t->type != ev_string)\n\t\t\t\t{\n\t\t\t\t\tQC_snprintfz(temp, sizeof(temp), \"/*%i*/\", ((int*)pr_globals)[ofs]);\n\t\t\t\t\tout += strlen(out);\n\t\t\t\t}\n\n\t\t\t\t*out++ =  '\\\"';\n\t\t\t\twhile (*in)\n\t\t\t\t{\n\t\t\t\t\tif (*in == '\\\"')\n\t\t\t\t\t{\n\t\t\t\t\t\t*out++ = '\\\\';\n\t\t\t\t\t\t*out++ = '\\\"';\n\t\t\t\t\t\tin++;\n\t\t\t\t\t}\n\t\t\t\t\telse if (*in == '\\n')\n\t\t\t\t\t{\n\t\t\t\t\t\t*out++ = '\\\\';\n\t\t\t\t\t\t*out++ = 'n';\n\t\t\t\t\t\tin++;\n\t\t\t\t\t}\n\t\t\t\t\telse if (*in == '\\\\')\n\t\t\t\t\t{\n\t\t\t\t\t\t*out++ = '\\\\';\n\t\t\t\t\t\t*out++ = '\\\\';\n\t\t\t\t\t\tin++;\n\t\t\t\t\t}\n\t\t\t\t\telse if (*in == '\\r')\n\t\t\t\t\t{\n\t\t\t\t\t\t*out++ = '\\\\';\n\t\t\t\t\t\t*out++ = 'r';\n\t\t\t\t\t\tin++;\n\t\t\t\t\t}\n\t\t\t\t\telse if (*in == '\\a')\n\t\t\t\t\t{\n\t\t\t\t\t\t*out++ = '\\\\';\n\t\t\t\t\t\t*out++ = 'a';\n\t\t\t\t\t\tin++;\n\t\t\t\t\t}\n\t\t\t\t\telse if (*in == '\\b')\n\t\t\t\t\t{\n\t\t\t\t\t\t*out++ = '\\\\';\n\t\t\t\t\t\t*out++ = 'b';\n\t\t\t\t\t\tin++;\n\t\t\t\t\t}\n\t\t\t\t\telse if (*in == '\\f')\n\t\t\t\t\t{\n\t\t\t\t\t\t*out++ = '\\\\';\n\t\t\t\t\t\t*out++ = 'f';\n\t\t\t\t\t\tin++;\n\t\t\t\t\t}\n\t\t\t\t\telse if (*in == '\\t')\n\t\t\t\t\t{\n\t\t\t\t\t\t*out++ = '\\\\';\n\t\t\t\t\t\t*out++ = 't';\n\t\t\t\t\t\tin++;\n\t\t\t\t\t}\n\t\t\t\t\telse if (*in == '\\v')\n\t\t\t\t\t{\n\t\t\t\t\t\t*out++ = '\\\\';\n\t\t\t\t\t\t*out++ = 'v';\n\t\t\t\t\t\tin++;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\t*out++ = *in++;\n\t\t\t\t}\n\t\t\t\t*out++ =  '\\\"';\n\t\t\t\t*out++ =  '\\0';\n\t\t\t}\n\t\t\tbreak;\n\t\tcase ev_vector:\n\t\t\t{\n\t\t\t\tchar x[64], y[64], z[64];\n\t\t\t\tFloatToString(x, sizeof(x), pr_globals[ofs+0]);\n\t\t\t\tFloatToString(y, sizeof(y), pr_globals[ofs+1]);\n\t\t\t\tFloatToString(z, sizeof(z), pr_globals[ofs+2]);\n\t\t\t\tQC_snprintfz(temp, sizeof(temp), \"\\'%s %s %s\\'\", x, y, z);\n\t\t\t}\n\t\t\tbreak;\n//\t\tcase ev_quat:\n//\t\t\tQC_snprintfz(temp, sizeof(temp), \"\\'%f %f %f %f\\'\", pr_globals[ofs],pr_globals[ofs+1],pr_globals[ofs+2],pr_globals[ofs+3]);\n//\t\t\tbreak;\n\t\tcase ev_integer:\n\t\t\tQC_snprintfz(temp, sizeof(temp), \"%ii\", ((int*)pr_globals)[ofs]);\n\t\t\tbreak;\n//\t\tcase ev_uinteger:\n//\t\t\tQC_snprintfz(temp, sizeof(temp), \"%uu\", ((int*)pr_globals)[ofs]);\n//\t\t\tbreak;\n\t\tcase ev_pointer:\n\t\t\tQC_snprintfz(temp, sizeof(temp), \"(__variant*)0x%xi\", ((int*)pr_globals)[ofs]);\n\t\t\tbreak;\n\t\tcase ev_function:\n\t\t\tif (!((int*)pr_globals)[ofs])\n\t\t\t\tQC_snprintfz(temp, sizeof(temp), \"__NULL__/*func*/\");\n\t\t\telse if (((int*)pr_globals)[ofs] > 0 && ((int*)pr_globals)[ofs] < numfunctions && functions[((int*)pr_globals)[ofs]].s_name>0)\n\t\t\t\tQC_snprintfz(temp, sizeof(temp), \"%s/*immediate*/\", GetNameString(functions[((int*)pr_globals)[ofs]].s_name));\n\t\t\telse\n\t\t\t\tQC_snprintfz(temp, sizeof(temp), \"((__variant(...))%i)\", ((int*)pr_globals)[ofs]);\n\t\t\tbreak;\n\t\tcase ev_entity:\n\t\t\tif (!pr_globals[ofs])\n\t\t\t\tQC_snprintfz(temp, sizeof(temp), \"((entity)__NULL__)\");\n\t\t\telse\n\t\t\t\tQC_snprintfz(temp, sizeof(temp), \"(entity)%i\", ((int*)pr_globals)[ofs]);\n\t\t\tbreak;\n\t\tcase ev_field:\n\t\t\tif (!pr_globals[ofs])\n\t\t\t\tQC_snprintfz(temp, sizeof(temp), \"((.void)__NULL__)\");\n\t\t\telse\n\t\t\t\tQC_snprintfz(temp, sizeof(temp), \"/*field %s*/%i\", DecompileGetFieldNameIdxByFinalOffset(((int*)pr_globals)[ofs]), ((int*)pr_globals)[ofs]);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tQC_snprintfz(temp, sizeof(temp), \"FIXME\");\n\t\t\tbreak;\n\t\t}\n\n\t\tres = (char *)malloc(strlen(temp) + 1);\n\t\tstrcpy(res, temp);\n\n\t\treturn res;\n\n\t}\n\treturn NULL;\n}\n\nchar *DecompileGet(QCD_function_t *df, gofs_t ofs, QCC_type_t *req_t)\n{\n\tchar *farg1;\n\t/*if (req_t == &def_short)\n\t{\n\t\tchar temp[16];\n\t\tQC_snprintfz(temp, sizeof(temp), \"%i\", ofs);\n\t\treturn strdup(temp);\n\t}*/\n\tfarg1 = NULL;\n\n\tfarg1 = DecompileGlobal(df, ofs, req_t);\n\n\tif (farg1 == NULL)\n\t\tfarg1 = DecompileImmediate_Get(df, ofs, req_t);\n\n\treturn farg1;\n}\n\nvoid DecompilePrintStatement(dstatement_t *s);\n\nvoid DecompileIndent(int c)\n{\n\tint i;\n\n\tif (c < 0)\n\t\tc = 0;\n\n\tfor (i = 0; i < c; i++) \n\t{\n\t\tQCC_CatVFile(Decompileofile, \"\\t\");\n\t}\n}\n\nvoid DecompileOpcode(QCD_function_t *df, int a, int b, int c, char *opcode, QCC_type_t *typ1, QCC_type_t *typ2, QCC_type_t *typ3, int usebrackets, int *indent)\n{\n\tstatic char line[8192];\n\tchar *arg1, *arg2, *arg3;\n\targ1 = DecompileGet(df, a, typ1);\n\targ2 = DecompileGet(df, b, typ2);\n\targ3 = DecompileGlobal(df, c, typ3);\n\n\tif (arg3)\n\t{\n\t\tDecompileIndent(*indent);\n\t\tif (usebrackets)\n\t\t\tQCC_CatVFile(Decompileofile, \"%s = %s %s %s;\\n\", arg3, arg1, opcode, arg2);\n\t\telse\n\t\t\tQCC_CatVFile(Decompileofile, \"%s = %s%s%s;\\n\", arg3, arg1, opcode, arg2);\n\t}\n\telse\n\t{\n\t\tif (usebrackets)\n\t\t\tQC_snprintfz(line, sizeof(line), \"(%s %s %s)\", arg1, opcode, arg2);\n\t\telse\n\t\t\tQC_snprintfz(line, sizeof(line), \"%s%s%s\", arg1, opcode, arg2);\n\t\tDecompileImmediate_Insert(df, c, line, typ3);\n\t}\n}\n\nstatic dstatement_t *jumptable;\nvoid DecompileDecompileStatement(QCD_function_t * df, dstatement_t * s, int *indent)\n{\n\tstatic char line[8192];\n\tstatic char fnam[512];\n\tchar *arg1, *arg2, *arg3;\n\tint nargs, i, j;\n\tdstatement_t *t;\n\tunsigned int dom, doc, ifc, tom;\n\tQCC_type_t *typ1, *typ2, *typ3;\n\tQCD_def_t *par;\n\tdstatement_t *k;\n\tint dum;\n\n\n\targ1 = arg2 = arg3 = NULL;\n\n\tline[0] = '\\0';\n\tfnam[0] = '\\0';\n\n\tif (jumptable)\n\t{\t//FIXME: the default case is the final jump, which will be misordered. flag the statement as needing to check them all or something.\n\t\t//FIXME: we need to push/pop these jumptables when switches are nested.\n\t\tdoc = jumptable->op%OP_MARK_END_ELSE;\n\t\tif ((doc == OP_CASE\t\t\t\t&& s == jumptable + (signed int)jumptable->b) ||\n\t\t\t(doc == OP_CASERANGE\t\t&& s == jumptable + (signed int)jumptable->c) ||\n\t\t\t(doc == OPD_GOTO_DEFAULT\t&& s == jumptable + (signed int)jumptable->a))\n\t\t{\n\t\t\tDecompileIndent(*indent-1);\n\t\t\tif (doc == OPD_GOTO_DEFAULT)\n\t\t\t\tQCC_CatVFile(Decompileofile, \"default:\\n\");\n\t\t\telse if (doc == OP_CASERANGE)\n\t\t\t{\n\t\t\t\targ1 = DecompileGet(df, jumptable->a, type_float);\n\t\t\t\targ2 = DecompileGet(df, jumptable->b, type_float);\n\t\t\t\tQCC_CatVFile(Decompileofile, \"case %s .. %s:\\n\", arg1, arg2);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\targ1 = DecompileGet(df, jumptable->a, type_float);\n\t\t\t\tQCC_CatVFile(Decompileofile, \"case %s:\\n\", arg1);\n\t\t\t}\n\t\t\tjumptable++;\n\t\t\tdoc = jumptable->op%OP_MARK_END_ELSE;\n\t\t\tif (doc != OP_CASE && doc!= OP_CASERANGE && doc != OPD_GOTO_DEFAULT)\n\t\t\t\tjumptable = NULL;\n\t\t}\n\t}\n\n\tdom = s->op;\n\n\tdoc = dom / OP_MARK_END_DO;\n\tifc = (dom % OP_MARK_END_DO) / OP_MARK_END_ELSE;\n\n\t// use program flow information \n\n\tfor (i = 0; i < ifc; i++) \n\t{\n\t\t(*indent)--;\n\t\tDecompileIndent(*indent);\n\t\tQCC_CatVFile(Decompileofile, \"}\\n\");//FrikaC style modification\n\t}\n\tfor (i = 0; i < doc; i++) \n\t{\n\t\tDecompileIndent(*indent);\n\t\tQCC_CatVFile(Decompileofile, \"do\\n\");\n\t\tDecompileIndent(*indent);\n\t\tQCC_CatVFile(Decompileofile, \"{\\n\");\n\t\t(*indent)++;\n\t}\n\n\t/*\n\t * remove all program flow information \n\t */\n\ts->op %= OP_MARK_END_ELSE;\n\ttyp1 = pr_opcodes[s->op].type_a?*pr_opcodes[s->op].type_a:NULL;\n\ttyp2 = pr_opcodes[s->op].type_b?*pr_opcodes[s->op].type_b:NULL;\n\ttyp3 = pr_opcodes[s->op].type_c?*pr_opcodes[s->op].type_c:NULL;\n\n\t/*\n\t * printf(\"DecompileDecompileStatement - decompiling %i (%i):\\n\",(int)(s - statements),dom);\n\t * DecompilePrintStatement (s);  \n\t */\n\t/*\n\t * states are handled at top level \n\t */\n\tif (s->op == OP_DONE)\n\t{\n\n\t}\n\telse if (s->op == OP_BOUNDCHECK)\n\t{\n\t\t/*these are auto-generated as a sideeffect. currently there is no syntax to explicitly use one (other than asm), but we don't want to polute the code when they're autogenerated, so ditch them all*/\n\t}\n\telse if (s->op == OP_STATE) \n\t{\n\n\t\tpar = DecompileGetParameter(s->a);\n\t\tif (!par)\n\t\t{\n\t\t\tprintf(\"Error - Can't determine frame number.\\n\");\n\t\t\texit(1);\n\t\t}\n\t\targ2 = DecompileGet(df, s->b, NULL);\n\t\tif (!arg2)\n\t\t{\n\t\t\tprintf(\"Error - No state parameter with offset %i.\\n\", s->b);\n\t\t\texit(1);\n\t\t}\n\t\tDecompileIndent(*indent);\n\t\tQCC_CatVFile(Decompileofile, \"state [ %s, %s ];\\n\", DecompileValueString((etype_t)(par->type), &pr_globals[par->ofs]), arg2);\n\n\t//\tfree(arg2);\n\t}\n\telse if (s->op == OP_RETURN/* || s->op == OPQF_RETURN_V*/)\n\t{\n\t\tDecompileIndent(*indent);\n\t\tQCC_CatVFile(Decompileofile, \"return\");\n\n\t\tif (s->a)\n\t\t{\n\t\t\tQCC_CatVFile(Decompileofile, \" \");\n\t\t\targ1 = DecompileGet(df, s->a, type_void);\t//FIXME: we should know the proper type better than this.\n\t\t\tQCC_CatVFile(Decompileofile, \"(%s)\", arg1);\n\t\t}\n\t\tQCC_CatVFile(Decompileofile, \";\\n\");\n\n\t}\n\telse if (pr_opcodes[s->op].flags & OPF_STD)\n\t{\n\t\tDecompileOpcode(df, s->a, s->b, s->c, pr_opcodes[s->op].name, typ1, typ2, typ3, true, indent);\n\t}\n\telse if ((pr_opcodes[s->op].flags & OPF_LOADPTR) || OP_GLOBALADDRESS == s->op)\n\t{\n\t\targ1 = DecompileGet(df, s->a, typ1);\n\t\targ2 = DecompileGet(df, s->b, typ2);\n\t\targ3 = DecompileGlobal(df, s->c, typ3);\n\n\t\tif (arg3)\n\t\t{\n\t\t\tDecompileIndent(*indent);\n\t\t\tif (s->b)\n\t\t\t\tQCC_CatVFile(Decompileofile, \"%s = &%s[%s];\\n\", arg3, arg1, arg2);\n\t\t\telse\n\t\t\t\tQCC_CatVFile(Decompileofile, \"%s = &%s;\\n\", arg3, arg1);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (s->b)\n\t\t\t\tQC_snprintfz(line, sizeof(line), \"%s[%s]\", arg1, arg2);\n\t\t\telse\n\t\t\t\tQC_snprintfz(line, sizeof(line), \"%s\", arg1);\n\t\t\tDecompileImmediate_Insert(df, s->c, line, typ3);\n\t\t}\n\t}\n\telse if ((OP_LOAD_F <= s->op && s->op <= OP_ADDRESS) || s->op == OP_LOAD_P || s->op == OP_LOAD_I)\n\t{\n\t\tif (s->op == OP_ADDRESS)\n\t\t{\n\t\t\tQCD_def_t *def = GlobalAtOffset(df, s->b);\n\t\t\tif (def && DecompileGetFieldTypeByDef(def) == ev_vector)\n\t\t\t\ttyp3 = type_vector;\n\t\t}\n\n\t\ttype_field->aux_type = typ3;\n\t\tDecompileOpcode(df, s->a, s->b, s->c, \".\", typ1, typ2, typ3, false, indent);\n\t\ttype_field->aux_type = NULL;\n\t}\n\telse if ((OP_LOADA_F <= s->op && s->op <= OP_LOADA_I))// || (OPQF_LOADBI_F <= s->op && s->op <= OPQF_LOADBI_P))\n\t{\n\t\tstatic char line[512];\n\t\tchar *arg1, *arg2, *arg3;\n\t\targ1 = DecompileGet(df, s->a, typ1);\n\t\targ2 = DecompileGet(df, s->b, typ2);\n\t\targ3 = DecompileGlobal(df, s->c, typ3);\n\n\t\tif (arg3) \n\t\t{\n\t\t\tDecompileIndent(*indent);\n\t\t\tQCC_CatVFile(Decompileofile, \"%s = %s[%s];\\n\", arg3, arg1, arg2);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tQC_snprintfz(line, sizeof(line), \"%s[%s]\", arg1, arg2);\n\t\t\tDecompileImmediate_Insert(df, s->c, line, typ3);\n\t\t}\n\t}\n\telse if (pr_opcodes[s->op].flags & OPF_STORE)\n\t{\n\t\tQCC_type_t *parmtype=NULL;\n\t\tif (s->b >= ofs_parms[0] && s->b < ofs_parms[7]+ofs_size)\n\t\t{\t//okay, so typ1 might not be what the store type says it should be.\n\t\t\tk = s+1;\n\t\t\twhile(k->op%OP_MARK_END_ELSE)\n\t\t\t{\n\t\t\t\tif ((k->op >= OP_CALL0 && k->op <= OP_CALL8) || (k->op >= OP_CALL1H && k->op <= OP_CALL8H))\n\t\t\t\t{\n\t\t\t\t\t//well, this is it.\n\t\t\t\t\tint fn = ((int*)pr_globals)[k->a];\n\t\t\t\t\tQCD_function_t *cf = &functions[fn];\n\t\t\t\t\tint bi = DecompileBuiltin(cf);\n\t\t\t\t\tint pn;\n\t\t\t\t\tQCD_def_t *def;\n\t\t\t\t\tif (bi)\n\t\t\t\t\t{\t//builtins don't have valid parm_start values\n\t\t\t\t\t\tQCC_type_t **p = builtins[bi].params[(s->b-ofs_parms[0])/ofs_size];\n\t\t\t\t\t\tparmtype = p?*p:NULL;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\t//qc functions do, though.\n\t\t\t\t\t\tfn = cf->parm_start;\n\t\t\t\t\t\tfor (pn = 0; pn < (s->b-ofs_parms[0])/ofs_size; pn++)\n\t\t\t\t\t\t\tfn += cf->parm_size[pn];\n\n\t\t\t\t\t\tdef = DecompileGetParameter(fn);\n\t\t\t\t\t\tif (def)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tswitch(def->type)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase ev_float:\n\t\t\t\t\t\t\t\tparmtype = type_float;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tcase ev_string:\n\t\t\t\t\t\t\t\tparmtype = type_string;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tcase ev_vector:\n\t\t\t\t\t\t\t\tparmtype = type_vector;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tcase ev_entity:\n\t\t\t\t\t\t\t\tparmtype = type_entity;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tcase ev_function:\n\t\t\t\t\t\t\t\tparmtype = type_function;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tcase ev_integer:\n\t\t\t\t\t\t\t\tparmtype = type_integer;\n\t\t\t\t\t\t\t\tbreak;\n//\t\t\t\t\t\t\tcase ev_uinteger:\n//\t\t\t\t\t\t\t\tparmtype = type_uinteger;\n//\t\t\t\t\t\t\t\tbreak;\n//\t\t\t\t\t\t\tcase ev_quat:\n//\t\t\t\t\t\t\t\tparmtype = type_quat;\n//\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tdefault:\n//\t\t\t\t\t\t\t\tparmtype = type_float;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\telse if (OP_STORE_F <= s->op && s->op <= OP_STORE_FNC)\n\t\t\t\t{\n\t\t\t\t\tif (k->b < s->b)\t//whoops... older QCCs can nest things awkwardly.\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tk++;\n\t\t\t}\n\t\t}\n\n\t\tif (parmtype)\n\t\t\targ1 = DecompileGet(df, s->a, parmtype);\n\t\telse\n\t\t\targ1 = DecompileGet(df, s->a, typ2);\t//types are backwards. *sigh*\n\t\targ3 = DecompileGlobal(df, s->b, typ1);\n\n\t\tif (arg3)\n\t\t{\n\t\t\tDecompileIndent(*indent);\n\t\t\tQCC_CatVFile(Decompileofile, \"%s %s %s;\\n\", arg3, pr_opcodes[s->op].name, arg1);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tQC_snprintfz(line, sizeof(line), \"%s\", arg1);\n\t\t\tDecompileImmediate_Insert(df, s->b, line, NULL);\n\t\t}\n\n\t}\n\telse if (pr_opcodes[s->op].flags & (OPF_STOREPTR|OPF_STOREPTROFS))\n\t{\n\t\targ1 = DecompileGet(df, s->a, typ2);\n\t\t//FIXME: we need to deal with ref types and other crazyness, so we know whether we need to add * or *& or if we can skip that completely\n\t\targ2 = DecompileGet(df, s->b, typ2);\n\n\t\tDecompileIndent(*indent);\n\t\tQCC_CatVFile(Decompileofile, \"%s %s %s;\\n\", arg2, pr_opcodes[s->op].name, arg1);\n\t}\n\telse if (pr_opcodes[s->op].flags & OPF_STOREFLD)\n\t{\n\t\targ1 = DecompileGet(df, s->a, typ1);\n\t\t//FIXME: we need to deal with ref types and other crazyness, so we know whether we need to add * or *& or if we can skip that completely\n\t\targ2 = DecompileGet(df, s->b, typ2);\n\t\targ3 = DecompileGet(df, s->c, typ3);\n\n\t\tDecompileIndent(*indent);\n\t\tQCC_CatVFile(Decompileofile, \"%s.%s %s %s;\\n\", arg1, arg2, pr_opcodes[s->op].name, arg3);\n\t}\n\telse if (OP_CONV_FTOI == s->op)\n\t{\n\t\targ1 = DecompileGet(df, s->a, typ1);\n\t\tQC_snprintfz(line, sizeof(line), \"(int)%s\", arg1);\n\t\tDecompileImmediate_Insert(df, s->c, line, type_integer);\n\t}\n\telse if (OP_CONV_ITOF == s->op)\n\t{\n\t\targ1 = DecompileGet(df, s->a, typ1);\n\t\tQC_snprintfz(line, sizeof(line), \"(float)%s\", arg1);\n\t\tDecompileImmediate_Insert(df, s->c, line, type_float);\n\t}\n\telse if (OP_RAND0 == s->op)\n\t{\n\t\tDecompileImmediate_Insert(df, ofs_return, \"random()\", type_float);\n\t}\n\telse if (OP_RAND1 == s->op)\n\t{\n\t\targ1 = DecompileGet(df, s->a, typ1);\n\t\tQC_snprintfz(line, sizeof(line), \"random(%s)\", arg1);\n\t\tDecompileImmediate_Insert(df, ofs_return, line, type_float);\n\t}\n\telse if (OP_RAND2 == s->op)\n\t{\n\t\targ1 = DecompileGet(df, s->a, typ1);\n\t\targ2 = DecompileGet(df, s->b, typ2);\n\t\tQC_snprintfz(line, sizeof(line), \"random(%s, %s)\", arg1, arg2);\n\t\tDecompileImmediate_Insert(df, ofs_return, line, type_float);\n\t}\n\telse if (OP_RANDV0 == s->op)\n\t{\n\t\tDecompileImmediate_Insert(df, ofs_return, \"randomv()\", type_vector);\n\t}\n\telse if (OP_RANDV1 == s->op)\n\t{\n\t\targ1 = DecompileGet(df, s->a, typ1);\n\t\tQC_snprintfz(line, sizeof(line), \"randomv(%s)\", arg1);\n\t\tDecompileImmediate_Insert(df, ofs_return, line, type_vector);\n\t}\n\telse if (OP_RANDV2 == s->op)\n\t{\n\t\targ1 = DecompileGet(df, s->a, typ1);\n\t\targ2 = DecompileGet(df, s->b, typ2);\n\t\tQC_snprintfz(line, sizeof(line), \"randomv(%s, %s)\", arg1, arg2);\n\t\tDecompileImmediate_Insert(df, ofs_return, line, type_vector);\n\t}\n\telse if (OP_FETCH_GBL_F <= s->op && s->op <= OP_FETCH_GBL_FNC)\n\t{\n\t\tif (s->op == OP_FETCH_GBL_F)\n\t\t\ttyp3 = type_float;\n\t\telse if (s->op == OP_FETCH_GBL_V)\n\t\t\ttyp3 = type_vector;\n\t\telse if (s->op == OP_FETCH_GBL_S)\n\t\t\ttyp3 = type_string;\n\t\telse if (s->op == OP_FETCH_GBL_E)\n\t\t\ttyp3 = type_entity;\n\t\telse if (s->op == OP_FETCH_GBL_FNC)\n\t\t\ttyp3 = type_function;\n\t\telse\n\t\t\ttyp3 = NULL;\n\t\targ1 = DecompileGet(df, s->a, typ1);\n\t\targ2 = DecompileGet(df, s->b, typ2);\n\t\tQC_snprintfz(line, sizeof(line), \"%s[%s]\", arg1, arg2);\n\t\tDecompileImmediate_Insert(df, s->c, line, typ3);\n\t}\n\telse if (pr_opcodes[s->op].flags & OPF_STDUNARY)\n\t{\n\t\targ1 = DecompileGet(df, s->a, typ1);\n\t\tQC_snprintfz(line, sizeof(line), \"%s%s\", pr_opcodes[s->op].name, arg1);\n\t\tDecompileImmediate_Insert(df, s->c, line, type_float);\n\t}\n\telse if ((OP_CALL0 <= s->op && s->op <= OP_CALL8) || (OP_CALL1H <= s->op && s->op <= OP_CALL8H))\n\t{\n\t\tif (OP_CALL1H <= s->op && s->op <= OP_CALL8H)\n\t\t\tnargs = (s->op - OP_CALL1H) + 1;\n\t\telse\n\t\t\tnargs = s->op - OP_CALL0;\n\n\t\targ1 = DecompileGet(df, s->a, type_function);\n\t\tQC_snprintfz(line, sizeof(line), \"%s(\", arg1);\n\t\tQC_snprintfz(fnam, sizeof(fnam), \"%s\", arg1);\n\n\t\tfor (i = 0; i < nargs; i++)\n\t\t{\n\n\t\t\ttyp1 = NULL;\n\n\t\t\tif (i == 0 && ((OP_CALL1H <= s->op && s->op <= OP_CALL8H) || s->b))\n\t\t\t\tj = s->b;\n\t\t\telse if (i == 1 && ((OP_CALL1H <= s->op && s->op <= OP_CALL8H) || s->c))\n\t\t\t\tj = s->c;\n\t\t\telse\n\t\t\t\tj = ofs_parms[i];\n\n\t\t\tif (arg1)\n\t\t\tfree(arg1);\n\n\t\t\targ1 = DecompileGet(df, (gofs_t)j, typ1);\n\t\t\tstrcat(line, arg1);\n\n\t\t\tif (i < nargs - 1)\n\t\t\t\tstrcat(line, \", \");//frikqcc modified\n\t\t}\n\n\t\tstrcat(line, \")\");\n\t\tDecompileImmediate_Insert(df, ofs_return, line, NULL);\n\n\t/*\n\t * if ( ( ( (s+1)->a != 1) && ( (s+1)->b != 1) && \n\t * ( (s+2)->a != 1) && ( (s+2)->b != 1) ) || \n\t * ( ((s+1)->op) % OP_MARK_END_ELSE == OP_CALL0 ) ) {\n\t * DecompileIndent(*indent);\n\t * fprintf(Decompileofile,\"%s;\\n\",line);\n\t * }\n\t */\n\n\t\tif ((((s + 1)->a != ofs_return) && ((s + 1)->b != ofs_return) &&\n\t\t\t((s + 2)->a != ofs_return) && ((s + 2)->b != ofs_return)) ||\n\t\t\t((((s + 1)->op) % OP_MARK_END_ELSE == OP_CALL0) && ((((s + 2)->a != ofs_return)) || ((s + 2)->b != ofs_return))))\n\t\t{\n\t\t\tDecompileIndent(*indent);\n\t\t\tQCC_CatVFile(Decompileofile, \"%s;\\n\", line);\n\t\t}\n\t}\n\telse if (s->op == OP_IF_I || s->op == OP_IFNOT_I ||\n\t\t\t s->op == OP_IF_F || s->op == OP_IFNOT_F ||\n\t\t\t s->op == OP_IF_S || s->op == OP_IFNOT_S/* || s->op == OPQF_IFA || s->op == OPQF_IFB || s->op == OPQF_IFAE || s->op == OPQF_IFBE*/)\n\t{\n\n\t\targ1 = DecompileGet(df, s->a, type_float);\t//FIXME: this isn't quite accurate...\n\t\targ2 = DecompileGlobal(df, s->a, NULL);\n\n\t\tif (s->op == OP_IFNOT_I || s->op == OP_IFNOT_F || s->op == OP_IFNOT_S)\n\t\t{\n\tlameifnot:\n\t\t\tif ((signed int)s->b < 1)\n\t\t\t{\n//\t\t\t\tif (arg1)\n//\t\t\t\t\tfree(arg1);\n//\t\t\t\tif (arg2)\n//\t\t\t\t\tfree(arg2);\n//\t\t\t\tif (arg3)\n//\t\t\t\t\tfree(arg3);\n\n\t\t\t\treturn;\n\n\n\t\t\t\tprintf(\"Found a negative IFNOT jump.\\n\");\n\t\t\t\texit(1);\n\t\t\t}\n\n\t\t\t/*\n\t\t\t * get instruction right before the target \n\t\t\t */\n\t\t\tt = s + (signed int)s->b - 1;\n\t\t\ttom = t->op % OP_MARK_END_ELSE;\n\n\t\t\tif (tom != OP_GOTO)\n\t\t\t{\n\n\t\t\t\t/*\n\t\t\t\t * pure if \n\t\t\t\t */\n\t\t\t\tDecompileIndent(*indent);\n\t\t\t\tQCC_CatVFile(Decompileofile, \"if (%s)\\n\", arg1);//FrikaC modified\n\t\t\t\tDecompileIndent(*indent);\n\t\t\t\tQCC_CatVFile(Decompileofile, \"{\\n\");\n\n\t\t\t\t(*indent)++;\n\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\n\t\t\t\tif ((signed int)t->a > 0)\n\t\t\t\t{\n\t\t\t\t\t/*\n\t\t\t\t\t * ite \n\t\t\t\t\t */\n\n\t\t\t\t\tDecompileIndent(*indent);\n\t\t\t\t\tQCC_CatVFile(Decompileofile, \"if (%s)\\n\", arg1);//FrikaC modified\n\t\t\t\t\tDecompileIndent(*indent);\n\t\t\t\t\tQCC_CatVFile(Decompileofile, \"{\\n\");\n\n\t\t\t\t\t(*indent)++;\n\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\n\n\t\t\t\t\tif (((signed int)t->a + (signed int)s->b) > 1)\n\t\t\t\t\t{\n\t\t\t\t\t\t/*\n\t\t\t\t\t\t * pure if  \n\t\t\t\t\t\t */\n\n\t\t\t\t\t\tDecompileIndent(*indent);\n\t\t\t\t\t\tQCC_CatVFile(Decompileofile, \"if (%s)\\n\", arg1);//FrikaC modified\n\t\t\t\t\t\tDecompileIndent(*indent);\n\t\t\t\t\t\tQCC_CatVFile(Decompileofile, \"{\\n\");\n\t\t\t\t\t\t(*indent)++;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\n\t\t\t\t\t\tdum = 1;\n\t\t\t\t\t\tfor (k = t + (signed int)(t->a); k < s; k++)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttom = k->op % OP_MARK_END_ELSE;\n\t\t\t\t\t\t\tif (tom == OP_GOTO ||\n\t\t\t\t\t\t\t\ttom == OP_IF_I || tom == OP_IFNOT_I ||\n\t\t\t\t\t\t\t\ttom == OP_IF_F || tom == OP_IFNOT_F ||\n\t\t\t\t\t\t\t\ttom == OP_IF_S || tom == OP_IFNOT_S)\n\t\t\t\t\t\t\t\tdum = 0;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (dum) \n\t\t\t\t\t\t{\n\n\t\t\t\t\t\t\tDecompileIndent(*indent);\n\t\t\t\t\t\t\tQCC_CatVFile(Decompileofile, \"while (%s)\\n\", arg1);\n\t\t\t\t\t\t\tDecompileIndent(*indent); //FrikaC\n\t\t\t\t\t\t\tQCC_CatVFile(Decompileofile, \"{\\n\");\n\t\t\t\t\t\t\t(*indent)++;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\n\t\t\t\t\t\t\tDecompileIndent(*indent);\n\n\n\t\t\t\t\t\t\tQCC_CatVFile(Decompileofile, \"if (%s)\\n\", arg1);//FrikaC modified\n\t\t\t\t\t\t\tDecompileIndent(*indent);\n\t\t\t\t\t\t\tQCC_CatVFile(Decompileofile, \"{\\n\");\n\t\t\t\t\t\t\t(*indent)++;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t/*else if (s->op == OPQF_IFA)\n\t\t{\n\t\t\tchar *t = arg1;\n\t\t\targ1 = malloc(strlen(arg1)+8);\n\t\t\tsprintf(arg1, \"(%s) <= 0\", t);\n\t\t\tfree(t);\n\t\t\tgoto lameifnot;\n\t\t}\n\t\telse if (s->op == OPQF_IFAE)\n\t\t{\n\t\t\tchar *t = arg1;\n\t\t\targ1 = malloc(strlen(arg1)+7);\n\t\t\tsprintf(arg1, \"(%s) < 0\", t);\n\t\t\tfree(t);\n\t\t\tgoto lameifnot;\n\t\t}\n\t\telse if (s->op == OPQF_IFB)\n\t\t{\n\t\t\tchar *t = arg1;\n\t\t\targ1 = malloc(strlen(arg1)+8);\n\t\t\tsprintf(arg1, \"(%s) >= 0\", t);\n\t\t\tfree(t);\n\t\t\tgoto lameifnot;\n\t\t}\n\t\telse if (s->op == OPQF_IFBE)\n\t\t{\n\t\t\tchar *t = arg1;\n\t\t\targ1 = malloc(strlen(arg1)+7);\n\t\t\tsprintf(arg1, \"(%s) > 0\", t);\n\t\t\tfree(t);\n\t\t\tgoto lameifnot;\n\t\t}*/\n\t\telse\n\t\t{\n\t\t\tif ((signed int)s->b>0)\n\t\t\t{\n\t\t\t\tchar *t = arg1;\n\t\t\t\t//if (!...)\n\n\t\t\t\targ1 = malloc(strlen(arg1)+2);\n\t\t\t\tsprintf(arg1, \"!%s\", t);\n\t\t\t\tfree(t);\n\t\t\t\tgoto lameifnot;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t/*\n\t\t\t * do ... while \n\t\t\t */\n\n\t\t\t\t(*indent)--;\n\t\t\t\tQCC_CatVFile(Decompileofile, \"\\n\");\n\t\t\t\tDecompileIndent(*indent);\n\t\t\t\tQCC_CatVFile(Decompileofile, \"} while (%s);\\n\", arg1);\n\t\t\t}\n\n\t\t}\n\n\t}\n\telse if (s->op == OP_SWITCH_F)\n\t{\n\t\targ1 = DecompileGet(df, s->a, type_float);\t//FIXME: this isn't quite accurate...\n\t\tjumptable = s+(signed int)s->b;\n\t\tDecompileIndent(*indent);\n\t\tQCC_CatVFile(Decompileofile, \"switch (%s)\\n\", arg1);\n\t\tDecompileIndent(*indent);\n\t\tQCC_CatVFile(Decompileofile, \"{\\n\");\n\t\t(*indent)++;\n\t}\n\telse if (s->op == OP_CASE || s->op == OP_CASERANGE || s->op == OPD_GOTO_DEFAULT)\n\t{\t//shoulda been handled as part of the jumptable handling.\n\t}\n\telse if (s->op == OPD_GOTO_FORSTART)\n\t{\n\t\tDecompileIndent(*indent);\n\t\tQCC_CatVFile(Decompileofile, \"do_tail\\n\");\n\t\tDecompileIndent(*indent);\n\t\tQCC_CatVFile(Decompileofile, \"{\\n\");\n\t\t(*indent)++;\n\t}\n\telse if (s->op == OPD_GOTO_WHILE1)\n\t{\n\t\t(*indent)--;\n\t\tDecompileIndent(*indent);\n\t\tQCC_CatVFile(Decompileofile, \"} while(1);\\n\");\n\t}\n\telse if (s->op == OPD_GOTO_BREAK)\n\t{\n\t\tDecompileIndent(*indent);\n\t\tQCC_CatVFile(Decompileofile, \"break;\\n\");\n\t}\n\telse if (s->op == OP_GOTO)\n\t{\n\n\t\tif ((signed int)s->a > 0)\n\t\t{\n\t\t\t/*\n\t\t\t * else \n\t\t\t */\n\t\t\t(*indent)--;\n\t\t\tDecompileIndent(*indent);\n\t\t\tQCC_CatVFile(Decompileofile, \"}\\n\");\n\t\t\tDecompileIndent(*indent);\n\t\t\tQCC_CatVFile(Decompileofile, \"else\\n\");\n\t\t\tDecompileIndent(*indent);\n\t\t\tQCC_CatVFile(Decompileofile, \"{\\n\");\n\t\t\t(*indent)++;\n\n\t\t\t//FIXME: look for the next control statement (ignoring writes to temps, maybe allow functions too if the following statement reads OFS_RETURN)\n\t\t\t//if its an IFNOT (optionally with its own else) that ends at our else then output this as \"}\\nelse \" instead\n\n\t\t}\n\t\telse\n\t\t{\n\t\t\t/*\n\t\t\t * while \n\t\t\t */\n\t\t\t(*indent)--;\n\n\t\t\tDecompileIndent(*indent);\n\t\t\tQCC_CatVFile(Decompileofile, \"}\\n\");\n\n\t\t}\n\n\t}\n\telse if (s->op == OP_THINKTIME)\n\t{\n\t\targ1 = DecompileGet(df, s->a, type_entity);\n\t\targ2 = DecompileGet(df, s->b, type_float);\n\n\t\tDecompileIndent(*indent);\n\t\tQCC_CatVFile(Decompileofile, \"__thinktime %s : %s;\\n\", arg1, arg2);\n\t}\n\telse\n\t{\n\t\tint op = s->op%OP_MARK_END_ELSE;\n\t\tif (op <= OP_BITOR_F && pr_opcodes[s->op].opname)\n\t\t\tprintf(\"warning: Unknown usage of OP_%s\", pr_opcodes[s->op].opname);\n\t\telse\n\t\t{\n\t\t\tDecompileIndent(*indent);\n\t\t\tQCC_CatVFile(Decompileofile, \"[OP_%s\", pr_opcodes[op].opname);\n\t\t\tif (s->a)\n\t\t\t\tQCC_CatVFile(Decompileofile, \", %s\", DecompileGet(df, s->a, typ1));\n\t\t\tif (s->b)\n\t\t\t\tQCC_CatVFile(Decompileofile, \", %s\", DecompileGet(df, s->b, typ1));\n\t\t\tif (s->c)\n\t\t\t\tQCC_CatVFile(Decompileofile, \", %s\", DecompileGet(df, s->c, typ1));\n\t\t\tQCC_CatVFile(Decompileofile, \"]\\n\");\n\t\t\tprintf(\"warning: Unknown opcode %i in %s\\n\", op, GetNameString(df->s_name));\n\t\t}\n\n\t}\n\n\n//    printf(\"DecompileDecompileStatement - Current line is \\\"%s\\\"\\n\", line);\n\n\n\tif (arg1)\n\t\tfree(arg1);\n\tif (arg2)\n\t\tfree(arg2);\n\tif (arg3)\n\t\tfree(arg3);\n\n\treturn;\n}\n\npbool DecompileDecompileFunction(QCD_function_t * df, dstatement_t *altdone)\n{\n\tdstatement_t *ds;\n\tint indent;\n\n\n\t// Initialize \n \n\tDecompileImmediate_Free();\n\n\tindent = 1;\n\n\tjumptable = NULL;\n\n\tds = statements + df->first_statement;\n\tif(ds->op == OP_STATE || ds->op == OP_CSTATE || ds->op == OP_CWSTATE)\n\t\tds++;\n\twhile (1) \n\t{\n\t\tif (ds == altdone)\n\t\t{\n\t\t\t//decompile the dummy done, cos we can\n\t\t\tDecompileDecompileStatement(df, statements, &indent);\n\t\t\tbreak;\n\t\t}\n\t\tDecompileDecompileStatement(df, ds, &indent);\n\t\tif (!ds->op)\n\t\t\tbreak;\n\t\tds++;\n\t}\n\n\tif (indent != 1)\n\t{\n\t\tprintf(\"warning: Indentation structure corrupt (in func %s)\\n\", GetNameString(df->s_name));\n\t\treturn false;\n\t}\n\treturn true;\n}\n\nchar *DecompileString(int qcstring)\n{\n\tstatic char buf[8192];\n\tchar *s;\n\tint c = 1;\n\tconst char *string = GetString(qcstring);\n\tif (qcstring < 0 || qcstring >= strofs)\n\t\treturn \"Invalid String\";\n\n\ts = buf;\n\t*s++ = '\"';\n\twhile (string && *string)\n\t{\n\t\tif (c == sizeof(buf) - 2)\n\t\t\tbreak;\n\t\tif (*string == '\\n')\n\t\t{\n\t\t\t*s++ = '\\\\';\n\t\t\t*s++ = 'n';\n\t\t\tc++;\n\t\t}\n\t\telse if (*string == '\"')\n\t\t{\n\t\t\t*s++ = '\\\\';\n\t\t\t*s++ = '\"';\n\t\t\tc++;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t*s++ = *string;\n\t\t\tc++;\n\t\t}\n\t\tstring++;\n\t\tif (c > (int)(sizeof(buf) - 10))\n\t\t{\n\t\t\t*s++ = '.';\n\t\t\t*s++ = '.';\n\t\t\t*s++ = '.';\n\t\t\tc += 3;\n\t\t\tbreak;\n\t\t}\n\t}\n\t*s++ = '\"';\n\t*s++ = 0;\n\treturn buf;\n}\n\nchar *DecompileValueString(etype_t type, void *val)\n{\n\tstatic char line[8192];\n\n\tline[0] = '\\0';\n\n\tswitch (type)\n\t{\n\t\tcase ev_string:\n\t\t\tQC_snprintfz(line, sizeof(line), \"%s\", DecompileString(*(int *)val));\n\t\t\tbreak;\n\t\tcase ev_void:\n\t\t\tQC_snprintfz(line, sizeof(line), \"void\");\n\t\t\tbreak;\n\t\tcase ev_float:\n\t\t\tif (*(float *)val > 999999 || *(float *)val < -999999) // ugh\n\t\t\t\tQC_snprintfz(line, sizeof(line), \"%.f\", *(float *)val);\n\t\t\telse if ((!(*(int*)val & 0x7f800000) || (*(int*)val & 0x7f800000)==0x7f800000) && (*(int*)val & 0x7fffffff))\n\t\t\t\t//QC_snprintfz(line, sizeof(line), \"%%%i\", *(int*)val);\n\t\t\t\tQC_snprintfz(line, sizeof(line), \"/*%di*/%s\", *(int*)val, DecompileString(*(int *)val));\n\t\t\telse if ((*(float *)val < 0.001) && (*(float *)val > 0))\n\t\t\t\tQC_snprintfz(line, sizeof(line), \"%.6f\", *(float *)val);\n\t\t\telse\t\t\t\n\t\t\t\tQC_snprintfz(line, sizeof(line), \"%g\", *(float *)val);\n\t\t\tbreak;\n\t\tcase ev_vector:\n\t\t\tQC_snprintfz(line, sizeof(line), \"'%g %g %g'\", ((float *)val)[0], ((float *)val)[1], ((float *)val)[2]);\n\t\t\tbreak;\n//\t\tcase ev_quat:\n//\t\t\tQC_snprintfz(line, sizeof(line), \"'%g %g %g %g'\", ((float *)val)[0], ((float *)val)[1], ((float *)val)[2], ((float *)val)[3]);\n//\t\t\tbreak;\n\t\tcase ev_field:\n\t\t\tDecompileGetFieldNameIdxByFinalOffset2(line, sizeof(line), *(int *)val);\n\t\t\tbreak;\n\t\tcase ev_entity:\n\t\t\tQC_snprintfz(line, sizeof(line), \"(entity)%ii\", *(int *)val);\n\t\t\tbreak;\n\t\tcase ev_integer:\n\t\t\tQC_snprintfz(line, sizeof(line), \"%ii\", *(int *)val);\n\t\t\tbreak;\n//\t\tcase ev_uinteger:\n//\t\t\tQC_snprintfz(line, sizeof(line), \"%uu\", *(int *)val);\n//\t\t\tbreak;\n\t\tcase ev_pointer:\n\t\t\tQC_snprintfz(line, sizeof(line), \"(__variant*)0x%xi\", *(int *)val);\n\t\t\tbreak;\n\t\tcase ev_function:\n\t\t\tif (*(int *)val>0 && *(int *)val<numfunctions)\n\t\t\t\tQC_snprintfz(line, sizeof(line), \"(/*func 0x%x*/%s)\", *(int *)val, GetNameString(functions[*(int *)val].s_name));\n\t\t\telse\n\t\t\t\tQC_snprintfz(line, sizeof(line), \"((void())0x%xi)\", *(int *)val);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tQC_snprintfz(line, sizeof(line), \"bad type %i\", type);\n\t\t\tbreak;\n\t}\n\n\treturn line;\n}\n\nchar *DecompilePrintParameter(QCD_def_t * def)\n{\n\tstatic char line[256];\n\tstatic char debug[128];\n\n\tline[0] = '0';\n\n\tif (debug_offs)\n\t{\n\t\tQC_snprintfz(debug, sizeof(debug), \" /*@%i*/\", def->ofs);\n\t}\n\telse\n\t\t*debug = 0;\n\n\tif (!*def->s_name)\t//null string...\n\t{\n\t\tQC_snprintfz(line, sizeof(line), \"%s _p_%i%s\", type_name(def), def->ofs, debug);\n\t}\n\telse if (!strcmp(GetNameString(def->s_name), \"IMMEDIATE\") || !strcmp(GetNameString(def->s_name), \".imm\") || !strcmp(GetNameString(def->s_name), \"I+\"))\n\t{\n\t\tQC_snprintfz(line, sizeof(line), \"%s%s\", DecompileValueString((etype_t)(def->type), &pr_globals[def->ofs]), debug);\n\t}\n\telse\n\t{\n\t\tQC_snprintfz(line, sizeof(line), \"%s %s%s\", type_name(def), GetNameString(def->s_name), debug);\n\t}\n\treturn line;\n}\n\n//we only work with prior fields.\nconst char *GetMatchingField(QCD_def_t *field)\n{\n\tint i;\n\tQCD_def_t *def;\n\tint ld, lf;\n\tconst char *ret = NULL;\n\n\tdef = NULL;\n\n\tfor (i = 0; i < numglobaldefs; i++)\n\t{\n\t\tdef = &globals[i];\n\n\t\tif ((def->type&~DEF_SAVEGLOBAL) == ev_field)\n\t\t{\n\t\t\tif (((int*)pr_globals)[def->ofs] == field->ofs)\n\t\t\t{\n\t\t\t\tif (!strcmp(GetNameString(def->s_name), GetNameString(field->s_name)))\n\t\t\t\t\treturn NULL;\t//found ourself, give up.\n\t\t\t\tlf = strlen(GetNameString(field->s_name));\n\t\t\t\tld = strlen(GetNameString(def->s_name));\n\t\t\t\tif (lf - 2 == ld)\n\t\t\t\t{\n\t\t\t\t\tif ((GetNameString(field->s_name)[lf-2]) == '_' && (GetNameString(field->s_name)[lf-1]) == 'x')\n\t\t\t\t\t\tif (!strncmp(GetNameString(field->s_name), GetNameString(def->s_name), ld))\n\t\t\t\t\t\t\treturn NULL;\t//vector found foo_x\n\t\t\t\t}\n\t\t\t\tif (!ret)\n\t\t\t\t\tret = GetNameString(def->s_name);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn ret;\n}\n\nQCD_def_t *GetField(const char *name)\n{\n\tint i;\n\tQCD_def_t *d;\n\tif (!*name)\n\t\treturn NULL;\n\n\tif (*name == '.')\n\t\tname++;\t//some idiot _intentionally_ decided to fuck shit up. go them!\n\n\tfor (i = 0; i < numfielddefs; i++)\n\t{\n\t\td = &fields[i];\n\n\t\tif (!strcmp(GetNameString(d->s_name), name))\n\t\t\treturn d;\n\t}\n\treturn NULL;\n}\nQCD_def_t *DecompileGetParameter(gofs_t ofs)\n{\n\tint i;\n\tQCD_def_t *def, *fb = NULL;\n\n\tdef = NULL;\n\n\tfor (i = 0; i < numglobaldefs; i++)\n\t{\n\t\tdef = &globals[i];\n\n\t\tif (def->ofs == ofs)\n\t\t{\n\t\t\tif (def->type > ev_variant)\n\t\t\t\tfb = def;\t//urgh, some weird one. see if its an alias.\n\t\t\telse\n\t\t\t\treturn def;\n\t\t}\n\t}\n\n\treturn fb;\n}\nQCD_def_t *DecompileFindGlobal(const char *findname)\n{\n\tint i;\n\tQCD_def_t *def;\n\tconst char *defname;\n\n\tdef = NULL;\n\n\tfor (i = 0; i < numglobaldefs; i++)\n\t{\n\t\tdef = &globals[i];\n\t\tdefname = GetNameString(def->s_name);\n\n\t\tif (!strcmp(findname, defname))\n\t\t{\n\t\t\treturn def;\n\t\t}\n\t}\n\n\treturn NULL;\n}\n\nQCD_def_t *DecompileFunctionGlobal(int funcnum)\n{\n\tint i;\n\tQCD_def_t *def;\n\n\tdef = NULL;\n\n\tfor (i = 0; i < numglobaldefs; i++)\n\t{\n\t\tdef = &globals[i];\n\n\t\tif (def->type == ev_function)\n\t\t{\n\t\t\tif (((int*)pr_globals)[def->ofs] == funcnum)\n\t\t\t{\n\t\t\t\treturn def;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn NULL;\n}\n\nvoid DecompilePreceedingGlobals(int start, int end, const char *name)\n{\n\tQCD_def_t *par;\n\tint j;\n\tQCD_def_t *ef;\n\tstatic char line[8192];\n\tchar asize[64];\n\tint arraysize;\n\n\tconst char *matchingfield;\n\n\t//print globals leading up to the function.\n\tfor (j = start; j < end; j++)\n\t{\n\n\t\tpar = DecompileGetParameter((gofs_t)j);\n\n\t\tif (par)\n\t\t{\n\t\t\tif (par->type & DEF_H2ARRAY)\n\t\t\t{\n\t\t\t\tarraysize = ((int*)pr_globals)[par->ofs-1]+1;\n\t\t\t\tQC_snprintfz(asize, sizeof(asize), \"[%i]\", arraysize);\n\t\t\t\tpar->type -= DEF_H2ARRAY;\n\t\t\t}\n\t\t\telse\n\t\t\t\tarraysize = *asize = 0;\n\t\t\tif (par->type & DEF_SAVEGLOBAL)\n\t\t\t\tpar->type -= DEF_SAVEGLOBAL;\n\n\t\t\tif (par->type == ev_function)\n\t\t\t{\n\t\t\t\tif (strcmp(GetNameString(par->s_name), \"IMMEDIATE\") && strcmp(GetNameString(par->s_name), \".imm\") && strcmp(GetNameString(par->s_name), \"I+\"))\n\t\t\t\t{\n\t\t\t\t\tif (strcmp(GetNameString(par->s_name), name))\n\t\t\t\t\t{\n\t\t\t\t\t\tint f = ((int*)pr_globals)[par->ofs];\n\t\t\t\t\t\t//DecompileGetFunctionIdxByName(strings + par->s_name);\n\t\t\t\t\t\tif (f && strcmp(GetNameString(functions[f].s_name), GetNameString(par->s_name)))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tchar *s = strrchr(DecompileProfiles[f], ' ');\n\t\t\t\t\t\t\t//happens with void() func = otherfunc;\n\t\t\t\t\t\t\t//such functions thus don't have their own type+body\n\t\t\t\t\t\t\t*s = 0;\n\t\t\t\t\t\t\tQCC_CatVFile(Decompileofile, \"var %s %s%s = %s;\\n\", DecompileProfiles[f], GetNameString(par->s_name), asize, s+1);\n\t\t\t\t\t\t\t*s = ' ';\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tQCC_CatVFile(Decompileofile, \"%s;\\n\", DecompileProfiles[f]);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (par->type != ev_pointer)\n\t\t\t{\n\t\t\t\tif (strcmp(GetNameString(par->s_name), \"IMMEDIATE\") && strcmp(GetNameString(par->s_name), \".imm\") && strcmp(GetNameString(par->s_name), \"I+\") && par->s_name)\n\t\t\t\t{\n\n\t\t\t\t\tif (par->type == ev_field)\n\t\t\t\t\t{\n\n\t\t\t\t\t\tef = GetField(GetNameString(par->s_name));\n\n\t\t\t\t\t\tif (!ef)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tQCC_CatVFile(Decompileofile, \"var .unknowntype %s%s;\\n\", GetNameString(par->s_name), asize);\n\t\t\t\t\t\t\tprintf(\"Fatal Error: Could not locate a field named \\\"%s\\\"\\n\", GetNameString(par->s_name));\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t//if (ef->type == ev_vector)\n\t\t\t\t\t\t\t//\tj += 2;\n\n\t\t\t\t\t\t\tmatchingfield = GetMatchingField(ef);\n\n#ifndef DONT_USE_DIRTY_TRICKS\t//could try scanning for an op_address+op_storep_fnc pair\n\t\t\t\t\t\t\tif ((ef->type == ev_function) && !strcmp(GetNameString(ef->s_name), \"th_pain\"))\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tQCC_CatVFile(Decompileofile, \".void(entity attacker, float damage) th_pain;\\n\");\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse\n#endif\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tif (matchingfield)\n\t\t\t\t\t\t\t\t\tQCC_CatVFile(Decompileofile, \"var .%s %s%s = %s;\\n\", type_name(ef), GetNameString(ef->s_name), asize, matchingfield);\n\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t\tQCC_CatVFile(Decompileofile, \".%s %s%s;\\n\", type_name(ef), GetNameString(ef->s_name), asize);\n\n//\t\t\t\t\t\t\t\tfprintf(Decompileofile, \"//%i %i %i %i\\n\", ef->ofs, ((int*)pr_globals)[ef->ofs], par->ofs, ((int*)pr_globals)[par->ofs]);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\n\t\t\t\t\t\tif (par->type == ev_vector)\n\t\t\t\t\t\t\tj += 2;\n\n\t\t\t\t\t\tif (par->type == ev_entity || par->type == ev_void)\n\t\t\t\t\t\t{\n\n\t\t\t\t\t\t\tQCC_CatVFile(Decompileofile, \"%s %s%s;\\n\", type_name(par), GetNameString(par->s_name), asize);\n\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\n\t\t\t\t\t\t\tline[0] = '\\0';\n\n\t\t\t\t\t\t\tif (IsConstant(par))\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tif (*asize)\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tint k;\n\t\t\t\t\t\t\t\t\tQCC_CatVFile(Decompileofile, \"%s %s%s    = {\", type_name(par), GetNameString(par->s_name), asize);\n\t\t\t\t\t\t\t\t\tfor (k = 0; k < arraysize; k++)\n\t\t\t\t\t\t\t\t\t\tQCC_CatVFile(Decompileofile, \"%s%s\", DecompileValueString((etype_t)(par->type), &pr_globals[par->ofs+type_size[par->type]*k]), (k+1)==arraysize?\"\":\", \");\n\t\t\t\t\t\t\t\t\tQCC_CatVFile(Decompileofile, \"};\\n\");\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tQC_snprintfz(line, sizeof(line), \"%s\", DecompileValueString((etype_t)(par->type), &pr_globals[par->ofs]));\n\t\t\t\t\t\t\t\t\tQCC_CatVFile(Decompileofile, \"%s %s%s    = %s;\\n\", type_name(par), GetNameString(par->s_name), asize, line);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tint k;\n\t\t\t\t\t\t\t\tfor (k = 0; k < type_size[par->type]; k++)\n\t\t\t\t\t\t\t\t\tif (pr_globals[par->ofs+k] != 0)\n\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\tif (k != type_size[par->type])\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tQC_snprintfz(line, sizeof(line), \"%s\", DecompileValueString((etype_t)(par->type), &pr_globals[par->ofs]));\n\t\t\t\t\t\t\t\t\tQCC_CatVFile(Decompileofile, \"%s %s%s /* = %s */;\\n\", type_name(par), GetNameString(par->s_name), asize, line);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t\tQCC_CatVFile(Decompileofile, \"%s %s%s;\\n\", type_name(par), GetNameString(par->s_name), asize);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\nvoid DecompileFunction(const char *name, int *lastglobal)\n{\n\tint i, findex, ps;\n\tdstatement_t *ds, *ts, *altdone;\n\tQCD_function_t *df;\n\tQCD_def_t *par;\n\tchar *arg2;\n\tunsigned short dom, tom;\n\tint j, start, end;\n\tstatic char line[8192];\n\tdstatement_t *k;\n\tint dum;\n\tsize_t startpos;\n\n\n\n\tfor (i = 1; i < numfunctions; i++)\n\t\tif (!strcmp(name, GetNameString(functions[i].s_name)))\n\t\t\tbreak;\n\tif (i == numfunctions)\n\t{\n\t\tprintf(\"Fatal Error: No function named \\\"%s\\\"\\n\", name);\n\t\texit(1);\n\t}\n\tdf = functions + i;\n\taltdone = statements + numstatements;\n\tfor (j = i+1; j < numfunctions; j++)\n\t{\n\t\tif (functions[j].first_statement <= 0)\n\t\t\tcontinue;\n\t\taltdone = statements + functions[j].first_statement;\n\t\tbreak;\n\t}\n\n\tfindex = i;\n\n\tstart = *lastglobal;\n//\tif (dfpred->first_statement <= 0 && df->first_statement > 0)\n//\t\tstart -= 1;\n\tend = df->parm_start;\n\tif (!end)\n\t{\n\t\tpar = DecompileFindGlobal(name);\n\t\tif (par)\n\t\t\tend = par - globals;\n\t}\n\t*lastglobal = max(*lastglobal, end + df->locals);\n\tDecompilePreceedingGlobals(start, end, name);\n\n\t/*\n\t * Check ''local globals''  \n\t */\n\n\tif (df->first_statement <= 0)\n\t{\n\n\t\tQCC_CatVFile(Decompileofile, \"%s\", DecompileProfiles[findex]);\n\t\tQCC_CatVFile(Decompileofile, \" = #%i; \\n\", -df->first_statement);\n\n\t\treturn;\n\t}\n\tds = statements + df->first_statement;\n\n\twhile (1)\n\t{\n\n\t\tdom = (ds->op) % OP_MARK_END_ELSE;\n\n\t\tif (!dom || ds == altdone)\n\t\t\tbreak;\n\t\telse if (dom == OP_GOTO)\n\t\t{\n\t\t\t// check for i-t-e \n\t\t\tif ((signed int)ds->a > 0)\n\t\t\t{\n\t\t\t\tts = ds + (signed int)ds->a;\n\t\t\t\tts->op += OP_MARK_END_ELSE;\t// mark the end of a if/ite construct \n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tts = ds + (signed int)ds->a;\n\t\t\t\t//if its a negative goto then it should normally be the end of a while{} loop. if we can't find the while statement itself, then its an infinite loop\n\t\t\t\tfor (k = (ts); k < ds; k++)\n\t\t\t\t{\n\t\t\t\t\ttom = k->op % OP_MARK_END_ELSE;\n\t\t\t\t\tif (tom == OP_IF_I || tom == OP_IFNOT_I ||\n\t\t\t\t\t\ttom == OP_IF_F || tom == OP_IFNOT_F ||\n\t\t\t\t\t\ttom == OP_IF_S || tom == OP_IFNOT_S)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (k + (signed int)k->b == ds+1)\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (k == ds)\n\t\t\t\t{\n\t\t\t\t\tds->op += OPD_GOTO_WHILE1-OP_GOTO;\n\t\t\t\t\tts->op += OP_MARK_END_DO;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse if (dom == OP_SWITCH_F && (signed int)ds->b > 0)\n\t\t{\t//essentially a goto that jumps to the OP_CASE lines. any gotos within that block which jump to the end of those cases are breaks.\n\t\t\tts = ds + (signed int)ds->b;\n\t\t\tfor (k = (ds+1); k < ts; k++)\n\t\t\t{\n\t\t\t\ttom = k->op % OP_MARK_END_ELSE;\n\t\t\t\tif (tom == OP_GOTO && k+(signed int)k->a > ts)\n\t\t\t\t\tk->op += OPD_GOTO_BREAK-OP_GOTO;\n\t\t\t}\n\t\t\tts->op += OP_MARK_END_ELSE;\n\t\t\twhile ((ts->op%OP_MARK_END_ELSE) == OP_CASE || (ts->op%OP_MARK_END_ELSE) == OP_CASERANGE)\n\t\t\t\tts++;\n\t\t\tif ((ts->op%OP_MARK_END_ELSE) == OP_GOTO && (signed int)ts->a < 0 && ts+(signed int)ts->a > ds)\n\t\t\t\tts->op += OPD_GOTO_DEFAULT-OP_GOTO;\n\t\t}\n\t\telse if (dom == OP_IFNOT_I || dom == OP_IFNOT_F || dom == OP_IFNOT_S)\n\t\t{\n\t\t\t// check for pure if \n\n\t\t\tts = ds + (signed int)ds->b;\n\t\t\ttom = (ts - 1)->op % OP_MARK_END_ELSE;\n\n\t\t\tif (tom != OP_GOTO)\n\t\t\t\tts->op += OP_MARK_END_ELSE;\t// mark the end of a if construct \n\t\t\telse if ((signed int)(ts - 1)->a < 0)\n\t\t\t{\n\t\t\t\tif (((signed int)(ts - 1)->a + (signed int)ds->b) > 1)\n\t\t\t\t{\n\t\t\t\t\t// pure if\n\t\t\t\t\tts->op += OP_MARK_END_ELSE;\t// mark the end of a if/ite construct \n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\n\t\t\t\t\tdum = 1;\n\t\t\t\t\tfor (k = (ts - 1) + (signed int)((ts - 1)->a); k < ds; k++)\n\t\t\t\t\t{\n\t\t\t\t\t\ttom = k->op % OP_MARK_END_ELSE;\n\t\t\t\t\t\tif (tom == OP_GOTO ||\n\t\t\t\t\t\t\ttom == OP_IF_I || tom == OP_IFNOT_I ||\n\t\t\t\t\t\t\ttom == OP_IF_F || tom == OP_IFNOT_F ||\n\t\t\t\t\t\t\ttom == OP_IF_S || tom == OP_IFNOT_S)\n\t\t\t\t\t\t\tdum = 0;\n\t\t\t\t\t}\n\t\t\t\t\tif (!dum)\n\t\t\t\t\t{\n\t\t\t\t\t\t// pure if  \n\t\t\t\t\t\tts->op += OP_MARK_END_ELSE;\t// mark the end of a if/ite construct \n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse if (dom == OP_IF_I || dom == OP_IF_F || dom == OP_IF_S)\n\t\t{\n\t\t\tif ((signed int)ds->b<1)\n\t\t\t{\n\t\t\t\tts = ds + (signed int)ds->b;\n\t\t\t\t//this is some kind of loop, either a while or for.\n\n\t\t\t\t//if the statement before the 'do' is a forwards goto, and it jumps to within the loop (instead of after), then we have to assume that it is a for loop and not a loop inside an else block.\n\t\t\t\tif ((ts-1)->op%OP_MARK_END_ELSE == OP_GOTO && (signed int)(ts-1)->a > 0 && (ts-1)+(signed int)(ts-1)->a <= ds)\n\t\t\t\t{\n\t\t\t\t\t(--ts)->op += OPD_GOTO_FORSTART - OP_GOTO;\n\t\t\t\t\t//because it was earlier, we need to unmark that goto's target as an end_else\n\t\t\t\t\tts = ts + (signed int)ts->a;\n\t\t\t\t\tts->op -= OP_MARK_END_ELSE;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tts->op += OP_MARK_END_DO;\t// mark the start of a do construct \n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tts = ds + ds->b;\n\t\t\t\tif ((ts-1)->op%OP_MARK_END_ELSE != OP_GOTO)\n\t\t\t\t\tts->op += OP_MARK_END_ELSE;\t// mark the end of an if construct \n\t\t\t\telse if ((signed int)(ts - 1)->a < 0)\n\t\t\t\t{\n\t\t\t\t\tif (((signed int)(ts - 1)->a + (signed int)ds->b) > 1)\n\t\t\t\t\t{\n\t\t\t\t\t\t// pure if\n\t\t\t\t\t\tts->op += OP_MARK_END_ELSE;\t// mark the end of a if/ite construct \n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\n\t\t\t\t\t\tdum = 1;\n\t\t\t\t\t\tfor (k = (ts - 1) + (signed int)((ts - 1)->a); k < ds; k++)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttom = k->op % OP_MARK_END_ELSE;\n\t\t\t\t\t\t\tif (tom == OP_GOTO ||\n\t\t\t\t\t\t\t\ttom == OP_IF_I || tom == OP_IFNOT_I ||\n\t\t\t\t\t\t\t\ttom == OP_IF_F || tom == OP_IFNOT_F ||\n\t\t\t\t\t\t\t\ttom == OP_IF_S || tom == OP_IFNOT_S)\n\t\t\t\t\t\t\t\tdum = 0;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (!dum)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t// pure if  \n\t\t\t\t\t\t\tts->op += OP_MARK_END_ELSE;\t// mark the end of a if/ite construct \n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tds++;\n\t}\n\n\t/*\n\t * print the prototype \n\t */\n\tQCC_CatVFile(Decompileofile, \"\\n%s\", DecompileProfiles[findex]);\n\n\t// handle state functions \n\n\tds = statements + df->first_statement;\n\n\tif (ds->op == OP_STATE)\n\t{\n\n\t\tpar = DecompileGetParameter(ds->a);\n\t\tif (!par)\n\t\t{\n\t\t\tstatic QCD_def_t pars;\n\t\t\t//must be a global (gotta be a float), create the def as needed\n\t\t\tpars.ofs = ds->a;\n\t\t\tpars.s_name = \"IMMEDIATE\";\n\t\t\tpars.type = ev_float;\n\t\t\tpar = &pars;\n//\t\t\tprintf(\"Fatal Error - Can't determine frame number.\");\n//\t\t\texit(1);\n\t\t}\n\n\t\targ2 = DecompileGet(df, ds->b, NULL);\n\t\tif (!arg2)\n\t\t{\n\t\t\tprintf(\"Fatal Error - No state parameter with offset %i.\", ds->b);\n\t\t\texit(1);\n\t\t}\n\n\t\tQCC_CatVFile(Decompileofile, \" = [ %s, %s ]\", DecompileValueString((etype_t)(par->type), &pr_globals[par->ofs]), arg2);\n\n\t\tfree(arg2);\n\n\t}\n\telse if (ds->op == OP_CSTATE || ds->op == OP_CWSTATE)\n\t{\n\t\tpbool backwards = false;\n\t\tchar *e1,*e2;\n\t\tchar *arg1 = DecompileGet(df, ds->a, type_float);\n\t\targ2 = DecompileGet(df, ds->b, type_float);\n\t\tif (!arg2)\n\t\t{\n\t\t\tprintf(\"Fatal Error - No state parameter with offset %i.\", ds->b);\n\t\t\texit(1);\n\t\t}\n\n\t\tif (strtod(arg1, &e1) > strtod(arg2, &e2) && !*e1 && !*e2)\n\t\t\tbackwards = true;\t//doesn't really make any difference, but does trigger an error.\n\n\t\tQCC_CatVFile(Decompileofile, \" = [%s%s %s .. %s ]\", backwards?\"--\":\"++\", (ds->op==OP_CWSTATE)?\"(W)\":\"\", arg1, arg2);\n\n\t\tfree(arg2);\n\t}\n\telse\n\t{\n\t\tQCC_CatVFile(Decompileofile, \" =\");\n\t}\n\tQCC_CatVFile(Decompileofile, \"\\n{\\n\");\n\n\tstartpos = Decompileofile->size;\n\n/*\n\tfprintf(Decompileprofile, \"%s\", DecompileProfiles[findex]);\n\tfprintf(Decompileprofile, \") %s;\\n\", name);\n*/\n\n\t/*\n\t * calculate the parameter size \n\t */\n\n\tfor (j = 0, ps = 0; j < df->numparms; j++)\n\t{\n\t\tpar = DecompileGetParameter((gofs_t)(df->parm_start + ps));\n\n\t\tif (par)\n\t\t{\n\t\t\tif (!*par->s_name)\n\t\t\t{\n\t\t\t\tQC_snprintfz(line, sizeof(line), \"_p_%i\", par->ofs);\n\t\t\t\targ2 = malloc(strlen(line)+1);\n\t\t\t\tstrcpy(arg2, line);\n\t\t\t\tpar->s_name = arg2;\n\t\t\t}\n\t\t}\n\n\t\tps += df->parm_size[j];\n\t}\n\n\t/*\n\t * print the locals \n\t */\n\n\tif (df->locals > 0)\n\t{\n\n\t\tif ((df->parm_start) + df->locals - 1 >= (df->parm_start) + ps)\n\t\t{\n\n\t\t\tfor (i = df->parm_start + ps; i < (df->parm_start) + df->locals; i++) \n\t\t\t{\n\n\t\t\t\tpar = DecompileGetParameter((gofs_t)i);\n\n\t\t\t\tif (!par)\n\t\t\t\t{\n\t\t\t\t\t// temps, or stripped...\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tif (!strcmp(GetNameString(par->s_name), \"IMMEDIATE\") || !strcmp(GetNameString(par->s_name), \".imm\") || !strcmp(GetNameString(par->s_name), \"I+\"))\n\t\t\t\t\t\tcontinue; // immediates don't belong\n\n\t\t\t\t\tif (!GetNameString(par->s_name))\n\t\t\t\t\t{\n\t\t\t\t\t\tQC_snprintfz(line, sizeof(line), \"_l_%i\", par->ofs);\n\t\t\t\t\t\targ2 = malloc(strlen(line)+1);\n\t\t\t\t\t\tstrcpy(arg2, line);\n\t\t\t\t\t\tpar->s_name = arg2;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (par->type == ev_function)\n\t\t\t\t\t{\n\t\t\t\t\t\tprintf(\"Warning Fields and functions must be global\\n\");\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tif (((int*)pr_globals)[par->ofs])\n\t\t\t\t\t\t\tQCC_CatVFile(Decompileofile, \"\\tlocal %s = %s;\\n\", DecompilePrintParameter(par), DecompileValueString(par->type, &pr_globals[par->ofs]));\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tQCC_CatVFile(Decompileofile, \"\\tlocal %s;\\n\", DecompilePrintParameter(par));\n\t\t\t\t\t}\n\t\t\t\t\tif (par->type == ev_vector)\n\t\t\t\t\t\ti += 2;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tQCC_CatVFile(Decompileofile, \"\\n\");\n\n\t\t}\n\t}\n\t/*\n\t * do the hard work \n\t */\n\n\tif (!DecompileDecompileFunction(df, altdone))\n\t{\n\t\tQCC_InsertVFile(Decompileofile, startpos, \"#error Corrupt Function: %s\\n#if 0\\n\", GetNameString(df->s_name));\n\t\tQCC_CatVFile(Decompileofile, \"#endif\\n\");\n\t}\n\n\tQCC_CatVFile(Decompileofile, \"};\\n\");\n}\n\nextern pbool safedecomp;\nstatic int fake_name;\nstatic char synth_name[1024]; // fake name part2\n\npbool TrySynthName(const char *first)\n{\n\tint i;\n\n\t// try to figure out the filename\n\t// based on the first function in the file\n\tfor (i=0; i < FILELISTSIZE; i+=2)\n\t{\n\t\tif (!strcmp(filenames[i], first))\n\t\t{\n\t\t\tQC_snprintfz(synth_name, sizeof(synth_name), \"%s\", filenames[i + 1]);\n\t\t\treturn true;\n\t\t}\n\t}\n\treturn false;\n}\n\nvoid DecompileDecompileFunctions(const char *origcopyright)\n{\n\tint i;\n\tunsigned int o;\n\tQCD_function_t *d;\n\tpbool bogusname;\n\tvfile_t *f = NULL;\n\tchar fname[1024];\n\tint lastglob = 1;\n\tconst char *lastfileofs = NULL;\n\tQCD_def_t *def;\n\n\tDecompileCalcProfiles();\n\n\tAddSourceFile(NULL, \"progs.src\");\n\tDecompileprogssrc = QCC_AddVFile(\"progs.src\", NULL, 0);\n\tif (!Decompileprogssrc)\n\t{\n\t\tprintf(\"Fatal Error - Could not open \\\"progs.src\\\" for output.\\n\");\n\t\texit(1);\n\t}\n\n\tQCC_CatVFile(Decompileprogssrc, \"./progs.dat\\n\\n\");\n\n\tQCC_CatVFile(Decompileprogssrc, \"#pragma flag enable lax //remove this line once you've fixed up any decompiler bugs...\\n\");\n\tif (origcopyright)\n\t\tQCC_CatVFile(Decompileprogssrc, \"//#pragma copyright \\\"%s\\\"\\n\", origcopyright);\n\tQCC_CatVFile(Decompileprogssrc, \"\\n\");\n\n\tdef = DecompileFindGlobal(\"end_sys_fields\");\n\tlastglob = def?def->ofs+1:1;\n\tif (lastglob != 1)\n\t{\n\t\tQC_snprintfz(synth_name, sizeof(synth_name), \"sysdefs.qc\");\n\t\tQC_snprintfz(fname, sizeof(fname), \"%s\", synth_name);\n\t\tif (!DecompileAlreadySeen(fname, &f))\n\t\t{\n\t\t\tprintf(\"decompiling %s\\n\", fname);\n\t\t\tcompilecb();\n\t\t\tQCC_CatVFile(Decompileprogssrc, \"%s\\n\", fname);\n\t\t}\n\t\tif (!f)\n\t\t{\n\t\t\tprintf(\"Fatal Error - Could not open \\\"%s\\\" for output.\\n\", fname);\n\t\t\texit(1);\n\t\t}\n\t\tDecompileofile = f;\n\n\t\tDecompilePreceedingGlobals(1, lastglob, \"\");\n\t}\n\n\tfor (i = 1; i < numfunctions; i++)\n\t{\n\t\td = &functions[i];\n\n\t\tif (d->s_file != lastfileofs || f == NULL)\n\t\t{\n\t\t\tlastfileofs = d->s_file;\n\t\t\tfname[0] = '\\0';\n\t\t\t//if (d->s_file <= strofs && d->s_file >= 0)\n\t\t\t\tsprintf(fname, \"%s\", GetNameString(d->s_file));\n\t\t\t// FrikaC -- not sure if this is cool or what?\n\t\t\tbogusname = false;\n\t\t\tif (strlen(fname) <= 0)\n\t\t\t\tbogusname = true;\n\t\t\telse for (o = 0; o < strlen(fname); o++)\n\t\t\t{\n\t\t\t\tif ((fname[o] < 'a' || fname[o] > 'z') &&\n\t\t\t\t  (fname[o] < '0' || fname[o] > '9') &&\n\t\t\t\t  (fname[o] <'A' || fname[o] > 'Z') &&\n\t\t\t\t  (fname[o] != '.' && fname[o] != '!' && fname[o] != '_'))\n\t\t\t\t{\n\t\t\t\t\tif (fname[o] == '/')\n\t\t\t\t\t\tfname[o] = '.';\n\t\t\t\t\telse if (fname[o] == '\\\\')\n\t\t\t\t\t\tfname[o] = '.';\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\t\tbogusname = true;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (bogusname)\n\t\t\t{\n\t\t\t\tif (*fname && !DecompileAlreadySeen(fname, NULL))\n\t\t\t\t{\n\t\t\t\t\tsynth_name[0] = 0;\n\t\t\t\t}\n\t\t\t\tif(!TrySynthName(qcva(\"%s\", GetNameString(d->s_name))) && !synth_name[0])\n\t\t\t\t\tQC_snprintfz(synth_name, sizeof(synth_name), \"frik%i.qc\", fake_name++);\n\n\t\t\t\tQC_snprintfz(fname, sizeof(fname), \"%s\", synth_name);\n\t\t\t}\n\t\t\telse\n\t\t\t\tsynth_name[0] = 0;\n\n\n\n\t\t\tif (!DecompileAlreadySeen(fname, &f))\n\t\t\t{\n\t\t\t\tprintf(\"decompiling %s\\n\", fname);\n\t\t\t\tcompilecb();\n\t\t\t\tQCC_CatVFile(Decompileprogssrc, \"%s\\n\", fname);\n\t\t\t}\n\t\t\tif (!f)\n\t\t\t{\n\t\t\t\tprintf(\"Fatal Error - Could not open \\\"%s\\\" for output.\\n\", fname);\n\t\t\t\texit(1);\n\t\t\t}\n\t\t}\n\t\tDecompileofile = f;\n\t\tDecompileFunction(GetNameString(d->s_name), &lastglob);\n\t}\n}\n\nvoid DecompileProgsDat(const char *name, void *buf, size_t bufsize)\n{\n\tchar *c = ReadProgsCopyright(buf, bufsize);\n\tif (c)\n\t\tprintf(\"Copyright: %s\\n\", c);\n\n\tPreCompile();\n\tpHash_Get = &Hash_Get;\n\tpHash_GetNext = &Hash_GetNext;\n\tpHash_Add = &Hash_Add;\n\tpHash_RemoveData = &Hash_RemoveData;\n\tHash_InitTable(&typedeftable, 1024, qccHunkAlloc(Hash_BytesForBuckets(1024)));\n\n\tmaxtypeinfos = 64;\n\tqcc_typeinfo = (void *)malloc(sizeof(QCC_type_t)*maxtypeinfos);\n\tnumtypeinfos = 0;\n\n\ttype_void = QCC_PR_NewType(\"void\", ev_void, true);\n\ttype_string = QCC_PR_NewType(\"string\", ev_string, true);\n\ttype_float = QCC_PR_NewType(\"float\", ev_float, true);\n\ttype_bfloat = type_float;//QCC_PR_NewType(\"float\", ev_float, true);\n\ttype_vector = QCC_PR_NewType(\"vector\", ev_vector, true);\n\ttype_entity = QCC_PR_NewType(\"entity\", ev_entity, true);\n\ttype_field = QCC_PR_NewType(\"__field\", ev_field, false);\n\ttype_function = QCC_PR_NewType(\"__function\", ev_function, false);\n\ttype_function->aux_type = type_void;\n\ttype_pointer = QCC_PR_NewType(\"__pointer\", ev_pointer, false);\n\ttype_integer = QCC_PR_NewType(\"__integer\", ev_integer, true);\n\ttype_bint = type_integer;\n\ttype_variant = QCC_PR_NewType(\"variant\", ev_variant, true);\n\ttype_variant = QCC_PR_NewType(\"__variant\", ev_variant, true);\n\ttype_invalid = QCC_PR_NewType(\"invalid\", ev_void, false);\n\n\tDecompileReadData(name, buf, bufsize);\n\tDecompileDetermineArrays();\n\tDecompileDecompileFunctions(c);\n\n\tprintf(\"Done.\\n\");\n}\n\nchar *DecompileGlobalStringNoContents(gofs_t ofs)\n{\n\tint i;\n\tQCD_def_t *def;\n\tstatic char line[128];\n\n\tline[0] = '0';\n\tQC_snprintfz(line, sizeof(line), \"%i(??\"\"?)\", ofs);\n\n\tfor (i = 0; i < numglobaldefs; i++)\n\t{\n\t\tdef = &globals[i];\n\n\t\tif (def->ofs == ofs)\n\t\t{\n\t\t\tline[0] = '0';\n\t\t\tQC_snprintfz(line, sizeof(line), \"%i(%s)\", def->ofs, GetNameString(def->s_name));\n\t\t\tbreak;\n\t\t}\n\t}\n\n\ti = strlen(line);\n\tfor (; i < 16; i++)\n\t\tstrcat(line, \" \");\n\tstrcat(line, \" \");\n\n\treturn line;\n}\n\nchar *DecompileGlobalString(gofs_t ofs)\n{\n\tchar *s;\n\tint i;\n\tQCD_def_t *def;\n\tstatic char line[128];\n\n\tline[0] = '0';\n\tQC_snprintfz(line, sizeof(line), \"%i(??\"\"?)\", ofs);\n\n\tfor (i = 0; i < numglobaldefs; i++)\n\t{\n\t\tdef = &globals[i];\n\n\t\tif (def->ofs == ofs)\n\t\t{\n\n\t\t\tline[0] = '0';\n\t\t\tif (!strcmp(GetNameString(def->s_name), \"IMMEDIATE\") || !strcmp(GetNameString(def->s_name), \".imm\") || !strcmp(GetNameString(def->s_name), \"I+\"))\n\t\t\t{\n\t\t\t\ts = PR_ValueString((etype_t)(def->type), &pr_globals[ofs]);\n\t\t\t\tQC_snprintfz(line, sizeof(line), \"%i(%s)\", def->ofs, s);\n\t\t\t}\n\t\t\telse\n\t\t\t\tQC_snprintfz(line, sizeof(line), \"%i(%s)\", def->ofs, GetNameString(def->s_name));\n\t\t}\n\t}\n\n\ti = strlen(line);\n\tfor (; i < 16; i++)\n\t\tstrcat(line, \" \");\n\tstrcat(line, \" \");\n\n\treturn line;\n}\n\nvoid DecompilePrintStatement(dstatement_t * s)\n{\n\tint i;\n\n\tprintf(\"%4i : %s \", (int)(s - statements), pr_opcodes[s->op].opname);\n\ti = strlen(pr_opcodes[s->op].opname);\n\tfor (; i < 10; i++)\n\t\tprintf(\" \");\n\n\tif (s->op == OP_IF_I || s->op == OP_IFNOT_I ||\n\t\ts->op == OP_IF_F || s->op == OP_IFNOT_F ||\n\t\ts->op == OP_IF_S || s->op == OP_IFNOT_S)\n\t\tprintf(\"%sbranch %i\", DecompileGlobalString(s->a), s->b);\n\telse if (s->op == OP_GOTO)\n\t{\n\t\tprintf(\"branch %i\", s->a);\n\t}\n\telse if ((unsigned)(s->op - OP_STORE_F) < 6)\n\t{\n\t\tprintf(\"%s\", DecompileGlobalString(s->a));\n\t\tprintf(\"%s\", DecompileGlobalStringNoContents(s->b));\n\t}\n\telse\n\t{\n\t\tif (s->a)\n\t\t\tprintf(\"%s\", DecompileGlobalString(s->a));\n\t\tif (s->b)\n\t\t\tprintf(\"%s\", DecompileGlobalString(s->b));\n\t\tif (s->c)\n\t\t\tprintf(\"%s\", DecompileGlobalStringNoContents(s->c));\n\t}\n\tprintf(\"\\n\");\n}\n\nvoid DecompilePrintFunction(char *name)\n{\n\tint i;\n\tdstatement_t *ds;\n\tQCD_function_t *df;\n\n\tfor (i = 0; i < numfunctions; i++)\n\t\tif (!strcmp(name, GetNameString(functions[i].s_name)))\n\t\t\tbreak;\n\tif (i == numfunctions)\n\t{\n\t\tprintf(\"Fatal Error: No function names \\\"%s\\\"\\n\", name);\n\t\texit(1);\n\t}\n\tdf = functions + i;\n\n\tprintf(\"Statements for %s:\\n\", name);\n\tds = statements + df->first_statement;\n\twhile (1)\n\t{\n\t\tDecompilePrintStatement(ds);\n\n\t\tif (!ds->op)\n\t\t\tbreak;\n\t\tds++;\n\t}\n}\n\n\npbool qcc_vfiles_changed;\nvfile_t *qcc_vfiles;\nvoid QCC_CloseAllVFiles(void)\n{\n\tvfile_t *f;\n\n\twhile(qcc_vfiles)\n\t{\n\t\tf = qcc_vfiles;\n\t\tqcc_vfiles = f->next;\n\n\t\tfree(f->file);\n\t\tfree(f);\n\t}\n\tqcc_vfiles_changed = false;\n}\nvfile_t *QCC_FindVFile(const char *name)\n{\n\tvfile_t *f;\n\tfor (f = qcc_vfiles; f; f = f->next)\n\t{\n\t\tif (!strcmp(f->filename, name))\n\t\t\treturn f;\n\t}\n\t//give it another go, for case\n\tfor (f = qcc_vfiles; f; f = f->next)\n\t{\n\t\tif (!QC_strcasecmp(f->filename, name))\n\t\t\treturn f;\n\t}\n\treturn NULL;\n}\nvfile_t *QCC_AddVFile(const char *name, void *data, size_t size)\n{\n\tvfile_t *f = QCC_FindVFile(name);\n\tif (!f)\n\t{\n\t\tf = malloc(sizeof(vfile_t) + strlen(name));\n\t\tf->next = qcc_vfiles;\n\t\tstrcpy(f->filename, name);\n\t\tqcc_vfiles = f;\n\t}\n\telse\n\t\tfree(f->file);\n\tf->file = malloc(size);\n\tf->type = FT_CODE;\n\tmemcpy(f->file, data, size);\n\tf->size = f->bufsize = size;\n\n\tqcc_vfiles_changed = true;\n\treturn f;\n}\nvoid QCC_CatVFile(vfile_t *f, const char *fmt, ...)\n{\n\tva_list argptr;\n\tchar msg[65536];\n\tsize_t n;\n\n\tva_start (argptr,fmt);\n\tQC_vsnprintf (msg,sizeof(msg)-1, fmt, argptr);\n\tva_end (argptr);\n\n\tn = strlen(msg);\n\tif (f->size+n > f->bufsize)\n\t{\n\t\tsize_t msize = f->bufsize + n + 8192;\n\t\tf->file = realloc(f->file, msize);\n\t\tf->bufsize = msize;\n\t}\n\tmemcpy((char*)f->file+f->size, msg, n);\n\tf->size += n;\n}\nvoid QCC_InsertVFile(vfile_t *f, size_t pos, const char *fmt, ...)\n{\n\tva_list argptr;\n\tchar msg[65536];\n\tsize_t n;\n\tva_start (argptr,fmt);\n\tQC_vsnprintf (msg,sizeof(msg)-1, fmt, argptr);\n\tva_end (argptr);\n\n\tn = strlen(msg);\n\tif (f->size+n > f->bufsize)\n\t{\n\t\tsize_t msize = f->bufsize + n + 8192;\n\t\tf->file = realloc(f->file, msize);\n\t\tf->bufsize = msize;\n\t}\n\tmemmove((char*)f->file+pos+n, (char*)f->file+pos, f->size-pos);\n\tf->size += n;\n\tmemcpy((char*)f->file+pos, msg, n);\n}\n"
  },
  {
    "path": "engine/qclib/execloop.h",
    "content": "//qc execution code.\n//we have two conditions.\n//one allows us to debug and trace through our code, the other doesn't.\n\n//hopefully, the compiler will do a great job at optimising this code for us, where required.\n//if it dosn't, then bum.\n\n//the general overhead should be reduced significantly, and I would be supprised if it did run slower.\n\n//run away loops are checked for ONLY on gotos and function calls. This might give a poorer check, but it will run faster overall.\n\n//Appears to work fine.\n\n#if INTSIZE == 16\n#define reeval reeval16\n#define pr_statements pr_statements16\n#define fakeop fakeop16\n#define dstatement_t dstatement16_t\n#define sofs signed short\n#elif INTSIZE == 32\n#define reeval reeval32\n#define pr_statements pr_statements32\n#define fakeop fakeop32\n#define dstatement_t dstatement32_t\n#define sofs signed int\n#elif INTSIZE == 24\n#error INTSIZE should be set to 32.\n#else\n#error Bad cont size\n#endif\n\n#define ENGINEPOINTER(p) ((char*)(p) - progfuncs->funcs.stringtable)\n#define QCPOINTER(p) (eval_t *)(p->_int+progfuncs->funcs.stringtable)\n#define QCPOINTERM(p) (eval_t *)((p)+progfuncs->funcs.stringtable)\n#define QCPOINTERWRITEFAIL(p,sz) ((unsigned int)(p)-1 >= prinst.addressableused-1-(sz))\t//disallows null writes\n#define QCPOINTERREADFAIL(p,sz) ((unsigned int)(p) >= prinst.addressableused-(sz))\t\t//permits null reads\n\n\n\n#define QCFAULT return (prinst.pr_xstatement=(st-pr_statements)-1),PR_HandleFault\n#define EVAL_FLOATISTRUE(ev) ((ev)->_int & 0x7fffffff) //mask away sign bit. This avoids using denormalized floats.\n\n#define A_RSHIFT_I(x,y) ((x < 0) ? ~(~(x) >> (y)) : ((x) >> (y)))\t//C leaves it undefined whether signed rshift is arithmatic or logical. gcc should be smart enough to fold this to the proper signed instruction at least on x86.\n\n#ifdef __GNUC__\n#define errorif(x) if(__builtin_expect(x,0))\n#else\n#define errorif(x) if(x)\n#endif\n\n//rely upon just st\n{\n#ifdef DEBUGABLE\n\ts = st-pr_statements;\n\ts+=1;\n\n\terrorif (prinst.watch_ptr && prinst.watch_ptr->_int != prinst.watch_old._int)\n\t{\n\t\t//this will fire on the next instruction after the variable got changed.\n\t\tprinst.pr_xstatement = s;\n\t\tif (current_progstate->linenums)\n\t\t\texterns->Printf(\"^b^3Watch point hit in %s:%u, \\\"%s\\\" changed\", PR_StringToNative(&progfuncs->funcs, prinst.pr_xfunction->s_name), current_progstate->linenums[s-1], prinst.watch_name);\n\t\telse\n\t\t\texterns->Printf(\"^b^3Watch point hit in %s, \\\"%s\\\" changed\", PR_StringToNative(&progfuncs->funcs, prinst.pr_xfunction->s_name), prinst.watch_name);\n\t\tswitch(prinst.watch_type)\n\t\t{\n\t\tcase ev_float:\n\t\t\texterns->Printf(\" from %g to %g\", prinst.watch_old._float, prinst.watch_ptr->_float);\n\t\t\tbreak;\n\t\tcase ev_vector:\n\t\t\texterns->Printf(\" from '%g %g %g' to '%g %g %g'\", prinst.watch_old._vector[0], prinst.watch_old._vector[1], prinst.watch_old._vector[2], prinst.watch_ptr->_vector[0], prinst.watch_ptr->_vector[1], prinst.watch_ptr->_vector[2]);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\texterns->Printf(\" from %i to %i\", prinst.watch_old._int, prinst.watch_ptr->_int);\n\t\t\tbreak;\n\t\tcase ev_entity:\n\t\t\texterns->Printf(\" from %i(%s) to %i(%s)\", prinst.watch_old._int, PR_GetEdictClassname(progfuncs, prinst.watch_old._int), prinst.watch_ptr->_int, PR_GetEdictClassname(progfuncs, prinst.watch_ptr->_int));\n\t\t\tbreak;\n\t\tcase ev_function:\n\t\tcase ev_string:\n\t\t\texterns->Printf(\", now set to %s\", PR_ValueString(progfuncs, prinst.watch_type, prinst.watch_ptr, false));\n\t\t\tbreak;\n\t\t}\n\t\texterns->Printf(\".\\n\");\n\t\tprinst.watch_old = *prinst.watch_ptr;\n//\t\tprinst.watch_ptr = NULL;\n\t\tprogfuncs->funcs.debug_trace=DEBUG_TRACE_INTO;\t//this is what it's for\n\n\t\ts=ShowStep(progfuncs, s, \"Watchpoint hit\", false);\n\t}\n\telse if (progfuncs->funcs.debug_trace)\n\t\ts=ShowStep(progfuncs, s, NULL, false);\n\tst = pr_statements + s;\n\tprinst.pr_xfunction->profile+=1;\n\n\top = (progfuncs->funcs.debug_trace?(st->op & ~0x8000):st->op);\nreeval:\n#else\n\tst++;\n\top = st->op;\n#endif\n\n\tsafeswitch ((enum qcop_e)op)\n\t{\n\tcase OP_ADD_F:\n\t\tOPC->_float = OPA->_float + OPB->_float;\n\t\tbreak;\n\tcase OP_ADD_V:\n\t\tOPC->_vector[0] = OPA->_vector[0] + OPB->_vector[0];\n\t\tOPC->_vector[1] = OPA->_vector[1] + OPB->_vector[1];\n\t\tOPC->_vector[2] = OPA->_vector[2] + OPB->_vector[2];\n\t\tbreak;\n\n\tcase OP_SUB_F:\n\t\tOPC->_float = OPA->_float - OPB->_float;\n\t\tbreak;\n\tcase OP_SUB_V:\n\t\tOPC->_vector[0] = OPA->_vector[0] - OPB->_vector[0];\n\t\tOPC->_vector[1] = OPA->_vector[1] - OPB->_vector[1];\n\t\tOPC->_vector[2] = OPA->_vector[2] - OPB->_vector[2];\n\t\tbreak;\n\n\tcase OP_MUL_F:\n\t\tOPC->_float = OPA->_float * OPB->_float;\n\t\tbreak;\n\tcase OP_MUL_V:\n\t\tOPC->_float = OPA->_vector[0]*OPB->_vector[0]\n\t\t\t\t+ OPA->_vector[1]*OPB->_vector[1]\n\t\t\t\t+ OPA->_vector[2]*OPB->_vector[2];\n\t\tbreak;\n\tcase OP_MUL_FV:\n\t\ttmpf = OPA->_float;\n\t\tOPC->_vector[0] = tmpf * OPB->_vector[0];\n\t\tOPC->_vector[1] = tmpf * OPB->_vector[1];\n\t\tOPC->_vector[2] = tmpf * OPB->_vector[2];\n\t\tbreak;\n\tcase OP_MUL_VF:\n\t\ttmpf = OPB->_float;\n\t\tOPC->_vector[0] = tmpf * OPA->_vector[0];\n\t\tOPC->_vector[1] = tmpf * OPA->_vector[1];\n\t\tOPC->_vector[2] = tmpf * OPA->_vector[2];\n\t\tbreak;\n\n\tcase OP_DIV_F:\n/*\t\terrorif (OPB->_float == 0)\n\t\t{\n\t\t\tprinst.pr_xstatement = st-pr_statements;\n\t\t\texterns->Printf (\"Division by 0 in %s\\n\", PR_StringToNative(&progfuncs->funcs, prinst.pr_xfunction->s_name));\n\t\t\tPR_StackTrace (&progfuncs->funcs, 1);\n\t\t\tOPC->_float = 0.0;\n\t\t}\n\t\telse\n*/\t\t\tOPC->_float = OPA->_float / OPB->_float;\n\t\tbreak;\n\tcase OP_DIV_VF:\n\t\ttmpf = OPB->_float;\n/*\t\terrorif (!tmpf)\n\t\t{\n\t\t\tprinst.pr_xstatement = st-pr_statements;\n\t\t\texterns->Printf (\"Division by 0 in %s\\n\", PR_StringToNative(&progfuncs->funcs, prinst.pr_xfunction->s_name));\n\t\t\tPR_StackTrace (&progfuncs->funcs, 1);\n\t\t}\n*/\n\t\tOPC->_vector[0] = OPA->_vector[0] / tmpf;\n\t\tOPC->_vector[1] = OPA->_vector[1] / tmpf;\n\t\tOPC->_vector[2] = OPA->_vector[2] / tmpf;\n\t\tbreak;\n\n\tcase OP_BITAND_F:\n\t\tOPC->_float = (float)((int)OPA->_float & (int)OPB->_float);\n\t\tbreak;\n\n\tcase OP_BITOR_F:\n\t\tOPC->_float = (float)((int)OPA->_float | (int)OPB->_float);\n\t\tbreak;\n\n\n\tcase OP_GE_F:\n\t\tOPC->_float = (float)(OPA->_float >= OPB->_float);\n\t\tbreak;\n\tcase OP_GE_I:\n\t\tOPC->_int = (int)(OPA->_int >= OPB->_int);\n\t\tbreak;\n\tcase OP_GE_IF:\n\t\tOPC->_int = (int)(OPA->_int >= OPB->_float);\n\t\tbreak;\n\tcase OP_GE_FI:\n\t\tOPC->_int = (int)(OPA->_float >= OPB->_int);\n\t\tbreak;\n\n\tcase OP_LE_F:\n\t\tOPC->_float = (float)(OPA->_float <= OPB->_float);\n\t\tbreak;\n\tcase OP_LE_I:\n\t\tOPC->_int = (int)(OPA->_int <= OPB->_int);\n\t\tbreak;\n\tcase OP_LE_IF:\n\t\tOPC->_int = (int)(OPA->_int <= OPB->_float);\n\t\tbreak;\n\tcase OP_LE_FI:\n\t\tOPC->_int = (int)(OPA->_float <= OPB->_int);\n\t\tbreak;\n\tcase OP_LE_U:\n\t\tOPC->_int = (int)(OPA->_uint <= OPB->_uint);\n\t\tbreak;\n\n\tcase OP_GT_F:\n\t\tOPC->_float = (float)(OPA->_float > OPB->_float);\n\t\tbreak;\n\tcase OP_GT_I:\n\t\tOPC->_int = (int)(OPA->_int > OPB->_int);\n\t\tbreak;\n\tcase OP_GT_IF:\n\t\tOPC->_int = (int)(OPA->_int > OPB->_float);\n\t\tbreak;\n\tcase OP_GT_FI:\n\t\tOPC->_int = (int)(OPA->_float > OPB->_int);\n\t\tbreak;\n\n\tcase OP_LT_F:\n\t\tOPC->_float = (float)(OPA->_float < OPB->_float);\n\t\tbreak;\n\tcase OP_LT_I:\n\t\tOPC->_int = (int)(OPA->_int < OPB->_int);\n\t\tbreak;\n\tcase OP_LT_IF:\n\t\tOPC->_int = (int)(OPA->_int < OPB->_float);\n\t\tbreak;\n\tcase OP_LT_FI:\n\t\tOPC->_int = (int)(OPA->_float < OPB->_int);\n\t\tbreak;\n\tcase OP_LT_U:\n\t\tOPC->_int = (OPA->_uint < OPB->_uint);\n\t\tbreak;\n\n\tcase OP_AND_F:\n\t\t//original logic\n\t\t//OPC->_float = (float)(OPA->_float && OPB->_float);\n\t\t//deal with denormalized floats by ensuring that they're not 0 (ignoring sign bit).\n\t\t//this avoids issues where the fpu treats denormalised floats as 0, or fpus that don't support denormals.\n\t\tOPC->_float = (float)(EVAL_FLOATISTRUE(OPA) && EVAL_FLOATISTRUE(OPB));\n\t\tbreak;\n\tcase OP_OR_F:\n\t\tOPC->_float = (float)(EVAL_FLOATISTRUE(OPA) || EVAL_FLOATISTRUE(OPB));\n\t\tbreak;\n\n\tcase OP_NOT_F:\n\t\tOPC->_float = (float)(!EVAL_FLOATISTRUE(OPA));\n\t\tbreak;\n\tcase OP_NOT_V:\n\t\tOPC->_float = (float)(!OPA->_vector[0] && !OPA->_vector[1] && !OPA->_vector[2]);\n\t\tbreak;\n\tcase OP_NOT_S:\n\t\tOPC->_float = (float)(!(OPA->string) || !*PR_StringToNative(&progfuncs->funcs, OPA->string));\n\t\tbreak;\n\tcase OP_NOT_FNC:\n\t\tOPC->_float = (float)(!(OPA->function & ~0xff000000));\n\t\tbreak;\n\tcase OP_NOT_ENT:\n\t\tOPC->_float = (float)(!(OPA->edict));//(PROG_TO_EDICT(progfuncs, OPA->edict) == (edictrun_t *)sv_edicts);\n\t\tbreak;\n\tcase OP_NOT_I:\n\t\tOPC->_int = !OPA->_int;\n\t\tbreak;\n\n\tcase OP_EQ_F:\n\t\tOPC->_float = (float)(OPA->_float == OPB->_float);\n\t\tbreak;\n\tcase OP_EQ_IF:\n\t\tOPC->_int = (float)(OPA->_int == OPB->_float);\n\t\tbreak;\n\tcase OP_EQ_FI:\n\t\tOPC->_int = (float)(OPA->_float == OPB->_int);\n\t\tbreak;\n\n\n\tcase OP_EQ_V:\n\t\tOPC->_float = (float)((OPA->_vector[0] == OPB->_vector[0]) &&\n\t\t\t\t\t(OPA->_vector[1] == OPB->_vector[1]) &&\n\t\t\t\t\t(OPA->_vector[2] == OPB->_vector[2]));\n\t\tbreak;\n\tcase OP_EQ_S:\n\t\tif (OPA->string==OPB->string)\n\t\t\tOPC->_float = true;\n\t\telse if (!OPA->string)\n\t\t{\n\t\t\tif (!OPB->string || !*PR_StringToNative(&progfuncs->funcs, OPB->string))\n\t\t\t\tOPC->_float = true;\n\t\t\telse\n\t\t\t\tOPC->_float = false;\n\t\t}\n\t\telse if (!OPB->string)\n\t\t{\n\t\t\tif (!OPA->string || !*PR_StringToNative(&progfuncs->funcs, OPA->string))\n\t\t\t\tOPC->_float = true;\n\t\t\telse\n\t\t\t\tOPC->_float = false;\n\t\t}\n\t\telse\n\t\t\tOPC->_float = (float)(!strcmp(PR_StringToNative(&progfuncs->funcs, OPA->string),PR_StringToNative(&progfuncs->funcs, OPB->string)));\n\t\tbreak;\n\tcase OP_EQ_E:\n\t\tOPC->_float = (float)(OPA->_int == OPB->_int);\n\t\tbreak;\n\tcase OP_EQ_FNC:\n\t\tOPC->_float = (float)(OPA->function == OPB->function);\n\t\tbreak;\n\n\n\tcase OP_NE_F:\n\t\tOPC->_float = (float)(OPA->_float != OPB->_float);\n\t\tbreak;\n\tcase OP_NE_V:\n\t\tOPC->_float = (float)((OPA->_vector[0] != OPB->_vector[0]) ||\n\t\t\t\t\t(OPA->_vector[1] != OPB->_vector[1]) ||\n\t\t\t\t\t(OPA->_vector[2] != OPB->_vector[2]));\n\t\tbreak;\n\tcase OP_NE_S:\n\t\tif (OPA->string==OPB->string)\n\t\t\tOPC->_float = false;\n\t\telse if (!OPA->string)\n\t\t{\n\t\t\tif (!OPB->string || !*(PR_StringToNative(&progfuncs->funcs, OPB->string)))\n\t\t\t\tOPC->_float = false;\n\t\t\telse\n\t\t\t\tOPC->_float = true;\n\t\t}\n\t\telse if (!OPB->string)\n\t\t{\n\t\t\tif (!OPA->string || !*PR_StringToNative(&progfuncs->funcs, OPA->string))\n\t\t\t\tOPC->_float = false;\n\t\t\telse\n\t\t\t\tOPC->_float = true;\n\t\t}\n\t\telse\n\t\t\tOPC->_float = (float)(strcmp(PR_StringToNative(&progfuncs->funcs, OPA->string),PR_StringToNative(&progfuncs->funcs, OPB->string)));\t\t\n\t\tbreak;\n\tcase OP_NE_E:\n\t\tOPC->_float = (float)(OPA->_int != OPB->_int);\n\t\tbreak;\n\tcase OP_NE_FNC:\n\t\tOPC->_float = (float)(OPA->function != OPB->function);\n\t\tbreak;\n\n//==================\n\tcase OP_STORE_IF:\n\t\tOPB->_float = (float)OPA->_int;\n\t\tbreak;\n\tcase OP_STORE_FI:\n\t\tOPB->_int = (int)OPA->_float;\n\t\tbreak;\n\t\t\n\tcase OP_STORE_F:\n\tcase OP_STORE_ENT:\n\tcase OP_STORE_FLD:\t\t// integers\n\tcase OP_STORE_S:\n\tcase OP_STORE_I:\n\tcase OP_STORE_FNC:\t\t// pointers\n\tcase OP_STORE_P:\n\t\tOPB->_int = OPA->_int;\n\t\tbreak;\n\tcase OP_STORE_V:\n\t\tOPB->_vector[0] = OPA->_vector[0];\n\t\tOPB->_vector[1] = OPA->_vector[1];\n\t\tOPB->_vector[2] = OPA->_vector[2];\n\t\tbreak;\n\n\t//store a value to a pointer\n\tcase OP_STOREP_IF:\n\t\ti = OPB->_int + OPC->_int*sizeof(ptr->_float);\n\t\terrorif (QCPOINTERWRITEFAIL(i, sizeof(float)))\n\t\t{\n\t\t\tif (!(ptr=PR_GetWriteTempStringPtr(progfuncs, OPB->_int, OPC->_int*sizeof(ptr->_float), sizeof(ptr->_float))))\n\t\t\t{\n\t\t\t\tif (i == -1)\n\t\t\t\t\tbreak;\n\t\t\t\tQCFAULT(&progfuncs->funcs, \"bad pointer write in %s\", PR_StringToNative(&progfuncs->funcs, prinst.pr_xfunction->s_name));\n\t\t\t}\n\t\t}\n\t\telse\n\t\t\tptr = QCPOINTERM(i);\n\t\tptr->_float = (float)OPA->_int;\n\t\tbreak;\n\tcase OP_STOREP_FI:\n\t\ti = OPB->_int + OPC->_int*sizeof(ptr->_int);\n\t\terrorif (QCPOINTERWRITEFAIL(i, sizeof(int)))\n\t\t{\n\t\t\tif (!(ptr=PR_GetWriteTempStringPtr(progfuncs, OPB->_int, OPC->_int*sizeof(ptr->_int), sizeof(ptr->_int))))\n\t\t\t{\n\t\t\t\tif (i == -1)\n\t\t\t\t\tbreak;\n\t\t\t\tQCFAULT(&progfuncs->funcs, \"bad pointer write in %s\", PR_StringToNative(&progfuncs->funcs, prinst.pr_xfunction->s_name));\n\t\t\t}\n\t\t}\n\t\telse\n\t\t\tptr = QCPOINTERM(i);\n\t\tptr->_int = (int)OPA->_float;\n\t\tbreak;\n\tcase OP_STOREP_I:\n\tcase OP_STOREP_F:\n\tcase OP_STOREP_ENT:\n\tcase OP_STOREP_FLD:\t\t// integers\n\tcase OP_STOREP_S:\n\tcase OP_STOREP_FNC:\t\t// pointers\n\t\ti = OPB->_int + OPC->_int*sizeof(ptr->_int);\n\t\terrorif (QCPOINTERWRITEFAIL(i, sizeof(ptr->_int)))\n\t\t{\n\t\t\tif (!(ptr=PR_GetWriteTempStringPtr(progfuncs, OPB->_int, OPC->_int*sizeof(ptr->_int), sizeof(ptr->_int))))\n\t\t\t{\n\t\t\t\tif (i == -1)\n\t\t\t\t\tbreak;\n\t\t\t\tif (i == 0)\n\t\t\t\t\tQCFAULT(&progfuncs->funcs, \"bad pointer write in %s (null pointer)\", PR_StringToNative(&progfuncs->funcs, prinst.pr_xfunction->s_name));\n\t\t\t\telse\n\t\t\t\t\tQCFAULT(&progfuncs->funcs, \"bad pointer write in %s (%x >= %x)\", PR_StringToNative(&progfuncs->funcs, prinst.pr_xfunction->s_name), i, prinst.addressableused);\n\t\t\t}\n\t\t}\n\t\telse\n\t\t\tptr = QCPOINTERM(i);\n\t\tptr->_int = OPA->_int;\n\t\tbreak;\n\tcase OP_STOREP_I64:\t\t// 64bit\n\t\ti = OPB->_int + OPC->_int*sizeof(ptr->_int);\n\t\terrorif (QCPOINTERWRITEFAIL(i, sizeof(ptr->i64)))\n\t\t{\n\t\t\tif (!(ptr=PR_GetWriteTempStringPtr(progfuncs, OPB->_int, OPC->_int*sizeof(ptr->_int), sizeof(ptr->i64))))\n\t\t\t{\n\t\t\t\tif (i == -1)\n\t\t\t\t\tbreak;\n\t\t\t\tif (i == 0)\n\t\t\t\t\tQCFAULT(&progfuncs->funcs, \"bad pointer write in %s (null pointer)\", PR_StringToNative(&progfuncs->funcs, prinst.pr_xfunction->s_name));\n\t\t\t\telse\n\t\t\t\t\tQCFAULT(&progfuncs->funcs, \"bad pointer write in %s (%x >= %x)\", PR_StringToNative(&progfuncs->funcs, prinst.pr_xfunction->s_name), i, prinst.addressableused);\n\t\t\t}\n\t\t}\n\t\telse\n\t\t\tptr = QCPOINTERM(i);\n\t\tptr->i64 = OPA->i64;\n\t\tbreak;\n\tcase OP_STOREP_V:\n\t\ti = OPB->_int + (OPC->_int*sizeof(ptr->_int));\n\t\terrorif (QCPOINTERWRITEFAIL(i, sizeof(pvec3_t)))\n\t\t{\n\t\t\tif (!(ptr=PR_GetWriteTempStringPtr(progfuncs, OPB->_int, OPC->_int*sizeof(ptr->_int), sizeof(pvec3_t))))\n\t\t\t{\n\t\t\t\tif (i == -1)\n\t\t\t\t\tbreak;\n\t\t\t\tQCFAULT(&progfuncs->funcs, \"bad pointer write in %s (%x >= %x)\", PR_StringToNative(&progfuncs->funcs, prinst.pr_xfunction->s_name), i, prinst.addressableused);\n\t\t\t}\n\t\t}\n\t\telse\n\t\t\tptr = QCPOINTERM(i);\n\t\tptr->_vector[0] = OPA->_vector[0];\n\t\tptr->_vector[1] = OPA->_vector[1];\n\t\tptr->_vector[2] = OPA->_vector[2];\n\t\tbreak;\n\n\tcase OP_STOREP_C:\t//store (float) character in a string\n\t\ti = OPB->_int + (OPC->_int)*sizeof(char);\n\t\terrorif (QCPOINTERWRITEFAIL(i, sizeof(char)))\n\t\t{\n\t\t\tif (!(ptr=PR_GetWriteTempStringPtr(progfuncs, OPB->_int, OPC->_int*sizeof(char), sizeof(char))))\n\t\t\t{\n\t\t\t\tif (i == -1)\n\t\t\t\t\tbreak;\n\t\t\t\tQCFAULT(&progfuncs->funcs, \"bad pointer write in %s (%x >= %x)\", PR_StringToNative(&progfuncs->funcs, prinst.pr_xfunction->s_name), i, prinst.addressableused);\n\t\t\t}\n\t\t}\n\t\telse\n\t\t\tptr = QCPOINTERM(i);\n\t\t*(unsigned char *)ptr = (char)OPA->_float;\n\t\tbreak;\n\tcase OP_STOREP_I8:\t//store (byte) character in a string\n\t\ti = OPB->_int + (OPC->_int)*sizeof(pbyte);\n\t\terrorif (QCPOINTERWRITEFAIL(i, sizeof(pbyte)))\n\t\t{\n\t\t\tif (!(ptr=PR_GetWriteTempStringPtr(progfuncs, OPB->_int, OPC->_int*sizeof(pbyte), sizeof(pbyte))))\n\t\t\t{\n\t\t\t\tif (i == -1)\n\t\t\t\t\tbreak;\n\t\t\t\tQCFAULT(&progfuncs->funcs, \"bad pointer write in %s (%x >= %x)\", PR_StringToNative(&progfuncs->funcs, prinst.pr_xfunction->s_name), i, prinst.addressableused);\n\t\t\t}\n\t\t}\n\t\telse\n\t\t\tptr = QCPOINTERM(i);\n\t\t*(pbyte *)ptr = (pbyte)OPA->_int;\n\t\tbreak;\n\tcase OP_STOREP_I16:\t//store short to a pointer\n\t\ti = OPB->_int + (OPC->_int)*sizeof(short);\n\t\terrorif (QCPOINTERWRITEFAIL(i, sizeof(short)))\n\t\t{\n\t\t\tif (!(ptr=PR_GetWriteTempStringPtr(progfuncs, OPB->_int, OPC->_int*sizeof(short), sizeof(short))))\n\t\t\t{\n\t\t\t\tif (i == -1)\n\t\t\t\t\tbreak;\n\t\t\t\tQCFAULT(&progfuncs->funcs, \"bad pointer write in %s (%x >= %x)\", PR_StringToNative(&progfuncs->funcs, prinst.pr_xfunction->s_name), i, prinst.addressableused);\n\t\t\t}\n\t\t}\n\t\telse\n\t\t\tptr = QCPOINTERM(i);\n\t\t*(short *)ptr = (short)OPA->_int;\n\t\tbreak;\n\n\tcase OP_STOREF_F:\n\tcase OP_STOREF_I:\n\tcase OP_STOREF_S:\n\t\terrorif ((unsigned)OPA->edict >= (unsigned)num_edicts)\n\t\t{\n\t\t\tif (PR_ExecRunWarning (&progfuncs->funcs, st-pr_statements, \"OP_STOREF_? references invalid entity in %s\\n\", PR_StringToNative(&progfuncs->funcs, prinst.pr_xfunction->s_name)))\n\t\t\t\treturn prinst.pr_xstatement;\n\t\t\tbreak;\n\t\t}\n\t\ted = PROG_TO_EDICT_PB(progfuncs, OPA->edict);\n\t\terrorif (!ed || ed->readonly)\n\t\t{\t//boot it over to the debugger\n#if INTSIZE == 16\n\t\t\tddef16_t *d = ED_GlobalAtOfs16(progfuncs, st->a);\n#else\n\t\t\tddef32_t *d = ED_GlobalAtOfs32(progfuncs, st->a);\n#endif\n\t\t\tfdef_t *f = ED_FieldAtOfs(progfuncs, OPB->_int + progfuncs->funcs.fieldadjust);\n\t\t\tif (PR_ExecRunWarning(&progfuncs->funcs, st-pr_statements, \"assignment to read-only entity %i in %s (%s.%s)\\n\", OPA->edict, PR_StringToNative(&progfuncs->funcs, prinst.pr_xfunction->s_name), d?PR_StringToNative(&progfuncs->funcs, d->s_name):\"??\", f?f->name:\"??\"))\n\t\t\t\treturn prinst.pr_xstatement;\n\t\t\tbreak;\n\t\t}\n\n//Whilst the next block would technically be correct, we don't use it as it breaks too many quake mods.\n#ifdef NOLEGACY\n\t\terrorif (ed->ereftype == ER_FREE)\n\t\t{\n\t\t\tif (PR_ExecRunWarning (&progfuncs->funcs, st-pr_statements, \"assignment to free entity in %s\", PR_StringToNative(&progfuncs->funcs, prinst.pr_xfunction->s_name)))\n\t\t\t\treturn prinst.pr_xstatement;\n\t\t\tbreak;\n\t\t}\n#endif\n\n\t\ti = OPB->_int + progfuncs->funcs.fieldadjust;\n\t\terrorif ((unsigned int)i*4 >= ed->fieldsize)\t//FIXME:lazy size check\n\t\t{\n\t\t\tif (PR_ExecRunWarning (&progfuncs->funcs, st-pr_statements, \"OP_STOREF_? references invalid field %i in %s\\n\", OPB->_int, PR_StringToNative(&progfuncs->funcs, prinst.pr_xfunction->s_name)))\n\t\t\t\treturn prinst.pr_xstatement;\n\t\t\tbreak;\n\t\t}\n\n\t\tptr = (eval_t *)(((int *)edvars(ed)) + i);\n\t\tptr->_int = OPC->_int;\n\t\tbreak;\n\tcase OP_STOREF_I64:\n\t\terrorif ((unsigned)OPA->edict >= (unsigned)num_edicts)\n\t\t{\n\t\t\tif (PR_ExecRunWarning (&progfuncs->funcs, st-pr_statements, \"OP_STOREF_? references invalid entity in %s\\n\", PR_StringToNative(&progfuncs->funcs, prinst.pr_xfunction->s_name)))\n\t\t\t\treturn prinst.pr_xstatement;\n\t\t\tbreak;\n\t\t}\n\t\ted = PROG_TO_EDICT_PB(progfuncs, OPA->edict);\n\t\terrorif (!ed || ed->readonly)\n\t\t{\t//boot it over to the debugger\n#if INTSIZE == 16\n\t\t\tddef16_t *d = ED_GlobalAtOfs16(progfuncs, st->a);\n#else\n\t\t\tddef32_t *d = ED_GlobalAtOfs32(progfuncs, st->a);\n#endif\n\t\t\tfdef_t *f = ED_FieldAtOfs(progfuncs, OPB->_int + progfuncs->funcs.fieldadjust);\n\t\t\tif (PR_ExecRunWarning(&progfuncs->funcs, st-pr_statements, \"assignment to read-only entity %i in %s (%s.%s)\\n\", OPA->edict, PR_StringToNative(&progfuncs->funcs, prinst.pr_xfunction->s_name), d?PR_StringToNative(&progfuncs->funcs, d->s_name):\"??\", f?f->name:\"??\"))\n\t\t\t\treturn prinst.pr_xstatement;\n\t\t\tbreak;\n\t\t}\n\n//Whilst the next block would technically be correct, we don't use it as it breaks too many quake mods.\n#ifdef NOLEGACY\n\t\terrorif (ed->ereftype == ER_FREE)\n\t\t{\n\t\t\tif (PR_ExecRunWarning (&progfuncs->funcs, st-pr_statements, \"assignment to free entity in %s\", PR_StringToNative(&progfuncs->funcs, prinst.pr_xfunction->s_name)))\n\t\t\t\treturn prinst.pr_xstatement;\n\t\t\tbreak;\n\t\t}\n#endif\n\n\t\ti = OPB->_int + progfuncs->funcs.fieldadjust;\n\t\terrorif ((unsigned int)i*4 >= ed->fieldsize)\t//FIXME:lazy size check\n\t\t{\n\t\t\tif (PR_ExecRunWarning (&progfuncs->funcs, st-pr_statements, \"OP_STOREF_? references invalid field %i in %s\\n\", OPB->_int, PR_StringToNative(&progfuncs->funcs, prinst.pr_xfunction->s_name)))\n\t\t\t\treturn prinst.pr_xstatement;\n\t\t\tbreak;\n\t\t}\n\n\t\tptr = (eval_t *)(((int *)edvars(ed)) + i);\n\t\tptr->i64 = OPC->i64;\n\t\tbreak;\n\tcase OP_STOREF_V:\n\t\terrorif ((unsigned)OPA->edict >= (unsigned)num_edicts)\n\t\t{\n\t\t\tif (PR_ExecRunWarning (&progfuncs->funcs, st-pr_statements, \"OP_STOREF_? references invalid entity in %s\\n\", PR_StringToNative(&progfuncs->funcs, prinst.pr_xfunction->s_name)))\n\t\t\t\treturn prinst.pr_xstatement;\n\t\t\tbreak;\n\t\t}\n\t\ted = PROG_TO_EDICT_PB(progfuncs, OPA->edict);\n\t\terrorif (!ed || ed->readonly)\n\t\t{\t//boot it over to the debugger\n#if INTSIZE == 16\n\t\t\tddef16_t *d = ED_GlobalAtOfs16(progfuncs, st->a);\n#else\n\t\t\tddef32_t *d = ED_GlobalAtOfs32(progfuncs, st->a);\n#endif\n\t\t\tfdef_t *f = ED_FieldAtOfs(progfuncs, OPB->_int + progfuncs->funcs.fieldadjust);\n\t\t\tif (PR_ExecRunWarning(&progfuncs->funcs, st-pr_statements, \"assignment to read-only entity %i in %s (%s.%s)\\n\", OPA->edict, PR_StringToNative(&progfuncs->funcs, prinst.pr_xfunction->s_name), d?PR_StringToNative(&progfuncs->funcs, d->s_name):\"??\", f?f->name:\"??\"))\n\t\t\t\treturn prinst.pr_xstatement;\n\t\t\tbreak;\n\t\t}\n\n//Whilst the next block would technically be correct, we don't use it as it breaks too many quake mods.\n#ifdef NOLEGACY\n\t\terrorif (ed->ereftype == ER_FREE)\n\t\t{\n\t\t\tif (PR_ExecRunWarning (&progfuncs->funcs, st-pr_statements, \"assignment to free entity in %s\", PR_StringToNative(&progfuncs->funcs, prinst.pr_xfunction->s_name)))\n\t\t\t\treturn prinst.pr_xstatement;\n\t\t\tbreak;\n\t\t}\n#endif\n\n\t\ti = OPB->_int + progfuncs->funcs.fieldadjust;\n\t\terrorif ((unsigned int)i*4 >= ed->fieldsize)\t//FIXME:lazy size check\n\t\t{\n\t\t\tif (PR_ExecRunWarning (&progfuncs->funcs, st-pr_statements, \"OP_STOREF_? references invalid field %i in %s\\n\", OPB->_int, PR_StringToNative(&progfuncs->funcs, prinst.pr_xfunction->s_name)))\n\t\t\t\treturn prinst.pr_xstatement;\n\t\t\tbreak;\n\t\t}\n\n\t\tptr = (eval_t *)(((int *)edvars(ed)) + i);\n\t\tptr->_vector[0] = OPC->_vector[0];\n\t\tptr->_vector[1] = OPC->_vector[1];\n\t\tptr->_vector[2] = OPC->_vector[2];\n\t\tbreak;\n\n\n\t//get a pointer to a field var\n\tcase OP_ADDRESS:\n\t\terrorif ((unsigned)OPA->edict >= (unsigned)num_edicts)\n\t\t{\n\t\t\tif (PR_ExecRunWarning (&progfuncs->funcs, st-pr_statements, \"OP_ADDRESS references invalid entity in %s\\n\", PR_StringToNative(&progfuncs->funcs, prinst.pr_xfunction->s_name)))\n\t\t\t\treturn prinst.pr_xstatement;\n\t\t\tbreak;\n\t\t}\n\t\ted = PROG_TO_EDICT_PB(progfuncs, OPA->edict);\n#ifdef PARANOID\n\t\tNUM_FOR_EDICT(ed);\t\t// make sure it's in range\n#endif\n\t\terrorif (!ed || ed->readonly)\n\t\t{\n\n\t\t\t//boot it over to the debugger\n\t\t\t{\n#if INTSIZE == 16\n\t\t\t\tddef16_t *d = ED_GlobalAtOfs16(progfuncs, st->a);\n#else\n\t\t\t\tddef32_t *d = ED_GlobalAtOfs32(progfuncs, st->a);\n#endif\n\t\t\t\tfdef_t *f = ED_FieldAtOfs(progfuncs, OPB->_int + progfuncs->funcs.fieldadjust);\n\t\t\t\tif (PR_ExecRunWarning(&progfuncs->funcs, st-pr_statements, \"assignment to read-only entity %i in %s (%s.%s)\\n\", OPA->edict, PR_StringToNative(&progfuncs->funcs, prinst.pr_xfunction->s_name), d?PR_StringToNative(&progfuncs->funcs, d->s_name):\"??\", f?f->name:\"??\"))\n\t\t\t\t\treturn prinst.pr_xstatement;\n\t\t\t\tOPC->_int = ~0;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n//Whilst the next block would technically be correct, we don't use it as it breaks too many quake mods.\n#ifdef NOLEGACY\n\t\terrorif (ed->ereftype == ER_FREE)\n\t\t{\n\t\t\tif (PR_ExecRunWarning (&progfuncs->funcs, st-pr_statements, \"assignment to free entity in %s\", PR_StringToNative(&progfuncs->funcs, prinst.pr_xfunction->s_name)))\n\t\t\t\treturn prinst.pr_xstatement;\n\t\t\tbreak;\n\t\t}\n#endif\n\n\t\ti = OPB->_int + progfuncs->funcs.fieldadjust;\n#ifdef PARANOID\n\t\terrorif ((unsigned int)i*4 >= ed->fieldsize)\t//FIXME:lazy size check\n\t\t{\n\t\t\tif (PR_ExecRunWarning (&progfuncs->funcs, st-pr_statements, \"OP_ADDRESS references invalid field %i in %s\\n\", OPB->_int, PR_StringToNative(&progfuncs->funcs, prinst.pr_xfunction->s_name)))\n\t\t\t\treturn prinst.pr_xstatement;\n\t\t\tOPC->_int = 0;\n\t\t\tbreak;\n\t\t}\n#endif\n\n\t\tOPC->_int = ENGINEPOINTER((((int *)edvars(ed)) + i));\n\t\tbreak;\n\n\t//load a field to a value\n\tcase OP_LOAD_P:\n\tcase OP_LOAD_I:\n\tcase OP_LOAD_F:\n\tcase OP_LOAD_FLD:\n\tcase OP_LOAD_ENT:\n\tcase OP_LOAD_S:\n\tcase OP_LOAD_FNC:\n\t\terrorif ((unsigned)OPA->edict >= (unsigned)num_edicts)\n\t\t{\n\t\t\tif (PR_ExecRunWarning (&progfuncs->funcs, st-pr_statements, \"OP_LOAD references invalid entity %i in %s\\n\", OPA->edict, PR_StringToNative(&progfuncs->funcs, prinst.pr_xfunction->s_name)))\n\t\t\t\treturn prinst.pr_xstatement;\n\t\t\tOPC->_int = 0;\n\t\t\tbreak;\n\t\t}\n\t\ted = PROG_TO_EDICT_PB(progfuncs, OPA->edict);\n#ifdef PARANOID\n\t\tNUM_FOR_EDICT(ed);\t\t// make sure it's in range\n#endif\n#ifdef NOLEGACY\n\t\tif (ed->ereftype == ER_FREE)\n\t\t{\n\t\t\tif (PR_ExecRunWarning (&progfuncs->funcs, st-pr_statements, \"OP_LOAD references free entity %i in %s\\n\", OPA->edict, PR_StringToNative(&progfuncs->funcs, prinst.pr_xfunction->s_name)))\n\t\t\t\treturn prinst.pr_xstatement;\n\t\t\tOPC->_int = 0;\n\t\t}\n\t\telse\n#endif\n\t\t{\n\t\t\ti = OPB->_int + progfuncs->funcs.fieldadjust;\n\t\t\terrorif ((unsigned int)(i+1)*4 > ed->fieldsize)\t//FIXME:lazy size check\n\t\t\t{\n\t\t\t\tif (PR_ExecRunWarning (&progfuncs->funcs, st-pr_statements, \"OP_LOAD references invalid field %i in %s\\n\", OPB->_int, PR_StringToNative(&progfuncs->funcs, prinst.pr_xfunction->s_name)))\n\t\t\t\t\treturn prinst.pr_xstatement;\n\t\t\t\tOPC->_int = 0;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tptr = (eval_t *)(((int *)edvars(ed)) + i);\n\t\t\tOPC->_int = ptr->_int;\n\t\t}\n\t\tbreak;\n\tcase OP_LOAD_I64:\n\t\terrorif ((unsigned)OPA->edict >= (unsigned)num_edicts)\n\t\t{\n\t\t\tif (PR_ExecRunWarning (&progfuncs->funcs, st-pr_statements, \"OP_LOAD_V references invalid entity %i in %s\\n\", OPA->edict, PR_StringToNative(&progfuncs->funcs, prinst.pr_xfunction->s_name)))\n\t\t\t\treturn prinst.pr_xstatement;\n\t\t\tOPC->_vector[0] = 0;\n\t\t\tOPC->_vector[1] = 0;\n\t\t\tOPC->_vector[2] = 0;\n\t\t\tbreak;\n\t\t}\n\t\ted = PROG_TO_EDICT_PB(progfuncs, OPA->edict);\n#ifdef PARANOID\n\t\tNUM_FOR_EDICT(ed);\t\t// make sure it's in range\n#endif\n#ifdef NOLEGACY\n\t\tif (ed->ereftype == ER_FREE)\n\t\t{\n\t\t\tif (PR_ExecRunWarning (&progfuncs->funcs, st-pr_statements, \"OP_LOAD references free entity %i in %s\\n\", OPA->edict, PR_StringToNative(&progfuncs->funcs, prinst.pr_xfunction->s_name)))\n\t\t\t\treturn prinst.pr_xstatement;\n\t\t\tOPC->_vector[0] = 0;\n\t\t\tOPC->_vector[1] = 0;\n\t\t\tOPC->_vector[2] = 0;\n\t\t}\n\t\telse\n#endif\n\t\t{\n\t\t\ti = OPB->_int + progfuncs->funcs.fieldadjust;\n\t\t\terrorif ((unsigned int)(i+2)*4 > ed->fieldsize)\t//FIXME:lazy size check\n\t\t\t{\n\t\t\t\tif (PR_ExecRunWarning (&progfuncs->funcs, st-pr_statements, \"OP_LOAD references invalid field %i in %s\\n\", OPB->_int, PR_StringToNative(&progfuncs->funcs, prinst.pr_xfunction->s_name)))\n\t\t\t\t\treturn prinst.pr_xstatement;\n\t\t\t\tOPC->_int = 0;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tptr = (eval_t *)(((int *)edvars(ed)) + i);\n\t\t\tOPC->i64 = ptr->i64;\n\t\t}\n\t\tbreak;\n\tcase OP_LOAD_V:\n\t\terrorif ((unsigned)OPA->edict >= (unsigned)num_edicts)\n\t\t{\n\t\t\tif (PR_ExecRunWarning (&progfuncs->funcs, st-pr_statements, \"OP_LOAD_V references invalid entity %i in %s\\n\", OPA->edict, PR_StringToNative(&progfuncs->funcs, prinst.pr_xfunction->s_name)))\n\t\t\t\treturn prinst.pr_xstatement;\n\t\t\tOPC->_vector[0] = 0;\n\t\t\tOPC->_vector[1] = 0;\n\t\t\tOPC->_vector[2] = 0;\n\t\t\tbreak;\n\t\t}\n\t\ted = PROG_TO_EDICT_PB(progfuncs, OPA->edict);\n#ifdef PARANOID\n\t\tNUM_FOR_EDICT(ed);\t\t// make sure it's in range\n#endif\n#ifdef NOLEGACY\n\t\tif (ed->ereftype == ER_FREE)\n\t\t{\n\t\t\tif (PR_ExecRunWarning (&progfuncs->funcs, st-pr_statements, \"OP_LOAD references free entity %i in %s\\n\", OPA->edict, PR_StringToNative(&progfuncs->funcs, prinst.pr_xfunction->s_name)))\n\t\t\t\treturn prinst.pr_xstatement;\n\t\t\tOPC->_vector[0] = 0;\n\t\t\tOPC->_vector[1] = 0;\n\t\t\tOPC->_vector[2] = 0;\n\t\t}\n\t\telse\n#endif\n\t\t{\n\t\t\ti = OPB->_int + progfuncs->funcs.fieldadjust;\n\t\t\terrorif ((unsigned int)(i+3)*4 > ed->fieldsize)\t//FIXME:lazy size check\n\t\t\t{\n\t\t\t\tif (PR_ExecRunWarning (&progfuncs->funcs, st-pr_statements, \"OP_LOAD references invalid field %i in %s\\n\", OPB->_int, PR_StringToNative(&progfuncs->funcs, prinst.pr_xfunction->s_name)))\n\t\t\t\t\treturn prinst.pr_xstatement;\n\t\t\t\tOPC->_int = 0;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tptr = (eval_t *)(((int *)edvars(ed)) + i);\n\t\t\tOPC->_vector[0] = ptr->_vector[0];\n\t\t\tOPC->_vector[1] = ptr->_vector[1];\n\t\t\tOPC->_vector[2] = ptr->_vector[2];\n\t\t}\n\t\tbreak;\n\n//==================\n\n\tcase OP_IFNOT_S:\n\t\tRUNAWAYCHECK();\n\t\tif (!OPA->string || !PR_StringToNative(&progfuncs->funcs, OPA->string))\n\t\t\tst += (sofs)st->b - 1;\t// offset the s++\n\t\tbreak;\n\n\tcase OP_IFNOT_F:\n\t\tRUNAWAYCHECK();\n\t\tif (!EVAL_FLOATISTRUE(OPA))\n\t\t\tst += (sofs)st->b - 1;\t// offset the s++\n\t\tbreak;\n\n\t//WARNING: vanilla uses this for floats too, which results in a discrepancy with -0\n\tcase OP_IFNOT_I:\n\t\tRUNAWAYCHECK();\n\t\tif (!OPA->_int)\n\t\t\tst += (sofs)st->b - 1;\t// offset the s++\n\t\tbreak;\n\n\tcase OP_IF_S:\n\t\tRUNAWAYCHECK();\n\t\tif (OPA->string && PR_StringToNative(&progfuncs->funcs, OPA->string))\n\t\t\tst += (sofs)st->b - 1;\t// offset the s++\n\t\tbreak;\n\n\tcase OP_IF_F:\n\t\tRUNAWAYCHECK();\n\t\tif (EVAL_FLOATISTRUE(OPA))\n\t\t\tst += (sofs)st->b - 1;\t// offset the s++\n\t\tbreak;\n\n\t//WARNING: vanilla uses this for floats too, which results in a discrepancy with -0\n\tcase OP_IF_I:\n\t\tRUNAWAYCHECK();\n\t\tif (OPA->_int)\n\t\t\tst += (sofs)st->b - 1;\t// offset the s++\n\t\tbreak;\n\n\tcase OP_GOTO:\n\t\tRUNAWAYCHECK();\n\t\tst += (sofs)st->a - 1;\t// offset the s++\n\t\tbreak;\n\n\tcase OP_CALL8H:\n\tcase OP_CALL7H:\n\tcase OP_CALL6H:\n\tcase OP_CALL5H:\n\tcase OP_CALL4H:\n\tcase OP_CALL3H:\n\tcase OP_CALL2H:\n\t\tG_VECTOR(OFS_PARM1)[0] = OPC->_vector[0];\n\t\tG_VECTOR(OFS_PARM1)[1] = OPC->_vector[1];\n\t\tG_VECTOR(OFS_PARM1)[2] = OPC->_vector[2];\n\tcase OP_CALL1H:\n\t\tG_VECTOR(OFS_PARM0)[0] = OPB->_vector[0];\n\t\tG_VECTOR(OFS_PARM0)[1] = OPB->_vector[1];\n\t\tG_VECTOR(OFS_PARM0)[2] = OPB->_vector[2];\n\n\tcase OP_CALL8:\n\tcase OP_CALL7:\n\tcase OP_CALL6:\n\tcase OP_CALL5:\n\tcase OP_CALL4:\n\tcase OP_CALL3:\n\tcase OP_CALL2:\n\tcase OP_CALL1:\n\tcase OP_CALL0:\n\t\t{\n\t\t\tint newpr;\n\t\t\tunsigned int fnum;\n\t\t\tRUNAWAYCHECK();\n\t\t\tprinst.pr_xstatement = st-pr_statements;\n\n\t\t\tif (op > OP_CALL8)\n\t\t\t\tprogfuncs->funcs.callargc = op - (OP_CALL1H-1);\n\t\t\telse\n\t\t\t\tprogfuncs->funcs.callargc = op - OP_CALL0;\n\t\t\tfnum = OPA->function;\n\n\t\t\tglob = NULL;\t//try to derestrict it.\n\n\t\t\tprogfuncs->funcs.callprogs=prinst.pr_typecurrent;\t\t\t//so we can revert to the right caller.\n\t\t\tnewpr = (fnum & 0xff000000)>>24;\t//this is the progs index of the callee\n\t\t\tfnum &= ~0xff000000;\t\t\t\t//the callee's function index.\n\n\t\t\t//if it's an external call, switch now (before any function pointers are used)\n\t\t\terrorif (!PR_SwitchProgsParms(progfuncs, newpr) || !fnum || fnum > pr_progs->numfunctions)\n\t\t\t{\n\t\t\t\tchar *msg = fnum?\"OP_CALL references invalid function in %s\\n\":\"NULL function from qc (inside %s).\\n\";\n\t\t\t\tPR_SwitchProgsParms(progfuncs, progfuncs->funcs.callprogs);\n\n\t\t\t\tglob = pr_globals;\n\t\t\t\tif (!progfuncs->funcs.debug_trace)\n\t\t\t\t\tQCFAULT(&progfuncs->funcs, msg, PR_StringToNative(&progfuncs->funcs, prinst.pr_xfunction->s_name));\n\n\t\t\t\t//skip the instruction if they just try stepping over it anyway.\n\t\t\t\tPR_StackTrace(&progfuncs->funcs, 0);\n\t\t\t\texterns->Printf(msg, PR_StringToNative(&progfuncs->funcs, prinst.pr_xfunction->s_name));\n\n\t\t\t\tpr_globals[OFS_RETURN] = 0;\n\t\t\t\tpr_globals[OFS_RETURN+1] = 0;\n\t\t\t\tpr_globals[OFS_RETURN+2] = 0;\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tnewf = &pr_cp_functions[fnum & ~0xff000000];\n\n\t\t\tif (newf->first_statement <= 0)\n\t\t\t{\t// negative statements are built in functions\n\t\t\t\t/*calling a builtin in another progs may affect that other progs' globals instead, is the theory anyway, so args and stuff need to move over*/\n\t\t\t\tif (prinst.pr_typecurrent != 0)\n\t\t\t\t{\n\t\t\t\t\t//builtins quite hackily refer to only a single global.\n\t\t\t\t\t//for builtins to affect the globals of other progs, we need to first switch to the progs that it will affect, so they'll be correct when we switch back\n\t\t\t\t\tPR_SwitchProgsParms(progfuncs, 0);\n\t\t\t\t}\n\t\t\t\ti = -newf->first_statement;\n\t\t\t\tif (i < externs->numglobalbuiltins)\n\t\t\t\t{\n#ifndef QCGC\n\t\t\t\t\tprinst.numtempstringsstack = prinst.numtempstrings;\n#endif\n\t\t\t\t\t(*externs->globalbuiltins[i]) (&progfuncs->funcs, (struct globalvars_s *)current_progstate->globals);\n\n\t\t\t\t\t//in case ed_alloc was called\n\t\t\t\t\tnum_edicts = sv_num_edicts;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tPR_RunError (&progfuncs->funcs, \"Bad builtin call number - %i\", -newf->first_statement);\n\t\t\t\tPR_SwitchProgsParms(progfuncs, (progsnum_t)progfuncs->funcs.callprogs);\n\n\t\t\t\t//decide weather non debugger wants to start debugging.\n\t\t\t\treturn prinst.pr_xstatement;\n\t\t\t}\n\t\t\ts = PR_EnterFunction (progfuncs, newf, progfuncs->funcs.callprogs);\n\t\t\tst = &pr_statements[s];\n\t\t}\n\t\t\n\t\t//resume at the new statement, which might be in a different progs\n\t\treturn s;\n\n\tcase OP_DONE:\n\tcase OP_RETURN:\n\n\t\tRUNAWAYCHECK();\n\n\t\tglob[OFS_RETURN] = glob[st->a];\n\t\tglob[OFS_RETURN+1] = glob[st->a+1];\n\t\tglob[OFS_RETURN+2] = glob[st->a+2];\n\n\t\ts = PR_LeaveFunction (progfuncs);\n\t\tst = &pr_statements[s];\t\t\n\t\tif (prinst.pr_depth == prinst.exitdepth)\n\t\t{\n\t\t\tprinst.pr_xstatement = s;\n\t\t\treturn -1;\t\t// all done\n\t\t}\n\t\treturn s;\n//\t\tbreak;\n\n\tcase OP_STATE:\n\t\texterns->stateop(&progfuncs->funcs, OPA->_float, OPB->function);\n\t\tbreak;\n\n\tcase OP_ADD_I:\t\t\n\t\tOPC->_int = OPA->_int + OPB->_int;\n\t\tbreak;\n\tcase OP_ADD_FI:\n\t\tOPC->_float = OPA->_float + (float)OPB->_int;\n\t\tbreak;\n\tcase OP_ADD_IF:\n\t\tOPC->_float = (float)OPA->_int + OPB->_float;\n\t\tbreak;\n  \n\tcase OP_SUB_I:\n\t\tOPC->_int = OPA->_int - OPB->_int;\n\t\tbreak;\n\tcase OP_SUB_FI:\n\t\tOPC->_float = OPA->_float - (float)OPB->_int;\n\t\tbreak;\n\tcase OP_SUB_IF:\n\t\tOPC->_float = (float)OPA->_int - OPB->_float;\n\t\tbreak;\n\n\tcase OP_CONV_ITOF:\n\t\tOPC->_float = (float)OPA->_int;\n\t\tbreak;\n\tcase OP_CONV_FTOI:\n\t\tOPC->_int = (int)OPA->_float;\n\t\tbreak;\n\n\tcase OP_LOADP_ITOF:\n\t\ti = OPA->_int;\n\t\terrorif (QCPOINTERREADFAIL(i, sizeof(char)))\n\t\t{\n\t\t\tQCFAULT(&progfuncs->funcs, \"bad pointer read in %s (%#x)\", PR_StringToNative(&progfuncs->funcs, prinst.pr_xfunction->s_name), OPA->_int);\n\t\t}\n\t\tptr = QCPOINTERM(i);\n\t\tOPC->_float = (float)ptr->_int;\n\t\tbreak;\n\n\tcase OP_LOADP_FTOI:\n\t\ti = OPA->_int;\n\t\terrorif (QCPOINTERREADFAIL(i, sizeof(char)))\n\t\t{\n\t\t\tQCFAULT(&progfuncs->funcs, \"bad pointer read in %s (%#x)\", PR_StringToNative(&progfuncs->funcs, prinst.pr_xfunction->s_name), OPA->_int);\n\t\t}\n\t\tptr = QCPOINTERM(i);\n\t\tOPC->_int = (int)ptr->_float;\n\t\tbreak;\n\n\tcase OP_BITAND_I:\n\t\tOPC->_int = (OPA->_int & OPB->_int);\n\t\tbreak;\n\t\n\tcase OP_BITOR_I:\n\t\tOPC->_int = (OPA->_int | OPB->_int);\n\t\tbreak;\n\n\tcase OP_MUL_I:\t\t\n\t\tOPC->_int = OPA->_int * OPB->_int;\n\t\tbreak;\n\tcase OP_DIV_I:\n\t\tif (OPB->_int == 0)\t//no division by zero allowed...\n\t\t\tOPC->_int = 0;\n\t\telse if (OPB->_int == -1 && OPA->_int==(int)0x80000000)\n\t\t\tOPC->_int = 0x7fffffff;\n\t\telse\n\t\t\tOPC->_int = OPA->_int / OPB->_int;\n\t\tbreak;\n\tcase OP_DIV_U:\n\t\tif (OPB->_uint == 0)\t//no division by zero allowed...\n\t\t\tOPC->_uint = 0;\n\t\telse\n\t\t\tOPC->_uint = OPA->_uint / OPB->_uint;\n\t\tbreak;\n\tcase OP_EQ_I:\n\t\tOPC->_int = (OPA->_int == OPB->_int);\n\t\tbreak;\n\tcase OP_NE_I:\n\t\tOPC->_int = (OPA->_int != OPB->_int);\n\t\tbreak;\n\t\n\n\t//array/structure reading/writing.\n\tcase OP_GLOBALADDRESS:\n\t\tOPC->_int = ENGINEPOINTER(&OPA->_int + OPB->_int); /*pointer arithmatic*/\n\t\tbreak;\n\tcase OP_ADD_PIW:\t//pointer to 32 bit (remember to *3 for vectors)\n\t\tOPC->_int = OPA->_int + OPB->_int*sizeof(float);\n\t\tbreak;\n\n\tcase OP_LOADA_I:\n\tcase OP_LOADA_F:\n\tcase OP_LOADA_FLD:\n\tcase OP_LOADA_ENT:\n\tcase OP_LOADA_S:\n\tcase OP_LOADA_FNC:\n\t\ti = st->a + OPB->_int;\n\t\tif ((size_t)i >= (size_t)(current_progstate->globals_bytes>>2))\n\t\t{\n\t\t\tQCFAULT(&progfuncs->funcs, \"bad array read in %s (index %i)\", PR_StringToNative(&progfuncs->funcs, prinst.pr_xfunction->s_name), OPB->_int);\n\t\t}\n\t\telse\n\t\t\tOPC->_int = ((eval_t *)&glob[i])->_int;\n\t\tbreak;\n\tcase OP_LOADA_I64:\n\t\ti = st->a + OPB->_int;\n\t\tif ((size_t)i >= (size_t)(current_progstate->globals_bytes>>2)-1u)\n\t\t{\n\t\t\tQCFAULT(&progfuncs->funcs, \"bad array read in %s (index %i)\", PR_StringToNative(&progfuncs->funcs, prinst.pr_xfunction->s_name), OPB->_int);\n\t\t}\n\t\telse\n\t\t\tOPC->i64 = ((eval_t *)&glob[i])->i64;\n\t\tbreak;\n\tcase OP_LOADA_V:\n\t\ti = st->a + OPB->_int;\n\t\tif ((size_t)(i) >= (size_t)(current_progstate->globals_bytes>>2)-2u)\n\t\t{\n\t\t\tQCFAULT(&progfuncs->funcs, \"bad array read in %s (index %i)\", PR_StringToNative(&progfuncs->funcs, prinst.pr_xfunction->s_name), OPB->_int);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tOPC->_vector[0] = ((eval_t *)&glob[i])->_vector[0];\n\t\t\tOPC->_vector[1] = ((eval_t *)&glob[i])->_vector[1];\n\t\t\tOPC->_vector[2] = ((eval_t *)&glob[i])->_vector[2];\n\t\t}\n\t\tbreak;\n\n\n\n\tcase OP_ADD_SF:\t//(char*)c = (char*)a + (float)b\n\t\tOPC->_int = OPA->_int + (int)OPB->_float;\n\t\tbreak;\n\tcase OP_SUB_S:\t//(float)c = (char*)a - (char*)b\n\t\tOPC->_int = OPA->_int - OPB->_int;\n\t\tbreak;\n\tcase OP_LOADP_C:\t//load character from a string/pointer\n\t\ti = (unsigned int)OPA->_int + (int)OPB->_float;\n\t\terrorif (QCPOINTERREADFAIL(i, sizeof(char)))\n\t\t{\n\t\t\tif (!(ptr=PR_GetReadTempStringPtr(progfuncs, OPA->_int, OPB->_float, sizeof(char))))\n\t\t\t{\n\t\t\t\tif (i == -1)\n\t\t\t\t{\n\t\t\t\t\tOPC->_float = 0;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tQCFAULT(&progfuncs->funcs, \"bad pointer read in %s (%i bytes into %s)\", PR_StringToNative(&progfuncs->funcs, prinst.pr_xfunction->s_name), i, ptr);\n\t\t\t}\n\t\t}\n\t\telse \n\t\t\tptr = QCPOINTERM(i);\n\t\tOPC->_float = *(unsigned char *)ptr;\n\t\tbreak;\n\tcase OP_LOADP_U8:\t//load character from a string/pointer\n\t\ti = (unsigned int)OPA->_int + (int)OPB->_int;\n\t\terrorif (QCPOINTERREADFAIL(i, sizeof(pbyte)))\n\t\t{\n\t\t\tif (!(ptr=PR_GetReadTempStringPtr(progfuncs, OPA->_int, OPB->_int, sizeof(pbyte))))\n\t\t\t{\n\t\t\t\tif (i == -1)\n\t\t\t\t{\n\t\t\t\t\tOPC->_int = 0;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tQCFAULT(&progfuncs->funcs, \"bad pointer read in %s (%i bytes into %s)\", PR_StringToNative(&progfuncs->funcs, prinst.pr_xfunction->s_name), i, ptr);\n\t\t\t}\n\t\t}\n\t\telse\n\t\t\tptr = QCPOINTERM(i);\n\t\tOPC->_int = *(pbyte *)ptr;\n\t\tbreak;\n\tcase OP_LOADP_I8:\t//load character from a string/pointer\n\t\ti = (unsigned int)OPA->_int + (int)OPB->_int;\n\t\terrorif (QCPOINTERREADFAIL(i, sizeof(pbyte)))\n\t\t{\n\t\t\tif (!(ptr=PR_GetReadTempStringPtr(progfuncs, OPA->_int, OPB->_int, sizeof(pbyte))))\n\t\t\t{\n\t\t\t\tif (i == -1)\n\t\t\t\t{\n\t\t\t\t\tOPC->_int = 0;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tQCFAULT(&progfuncs->funcs, \"bad pointer read in %s (%i bytes into %s)\", PR_StringToNative(&progfuncs->funcs, prinst.pr_xfunction->s_name), i, ptr);\n\t\t\t}\n\t\t}\n\t\telse\n\t\t\tptr = QCPOINTERM(i);\n\t\tOPC->_int = *(char *)ptr;\n\t\tbreak;\n\tcase OP_LOADP_U16:\t//load character from a string/pointer\n\t\ti = (unsigned int)OPA->_int + (int)OPB->_int*2;\n\t\terrorif (QCPOINTERREADFAIL(i, sizeof(short)))\n\t\t{\n\t\t\tif (!(ptr=PR_GetReadTempStringPtr(progfuncs, OPA->_int, OPB->_int*2, sizeof(short))))\n\t\t\t{\n\t\t\t\tif (i == -1)\n\t\t\t\t{\n\t\t\t\t\tOPC->_int = 0;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tQCFAULT(&progfuncs->funcs, \"bad pointer read in %s (%i bytes into %s)\", PR_StringToNative(&progfuncs->funcs, prinst.pr_xfunction->s_name), i, ptr);\n\t\t\t}\n\t\t}\n\t\telse\n\t\t\tptr = QCPOINTERM(i);\n\t\tOPC->_int = *(unsigned short *)ptr;\n\t\tbreak;\n\tcase OP_LOADP_I16:\t//load character from a string/pointer\n\t\ti = (unsigned int)OPA->_int + (int)OPB->_int*2;\n\t\terrorif (QCPOINTERREADFAIL(i, sizeof(short)))\n\t\t{\n\t\t\tif (!(ptr=PR_GetReadTempStringPtr(progfuncs, OPA->_int, OPB->_int*2, sizeof(short))))\n\t\t\t{\n\t\t\t\tif (i == -1)\n\t\t\t\t{\n\t\t\t\t\tOPC->_int = 0;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tQCFAULT(&progfuncs->funcs, \"bad pointer read in %s (%i bytes into %s)\", PR_StringToNative(&progfuncs->funcs, prinst.pr_xfunction->s_name), i, ptr);\n\t\t\t}\n\t\t}\n\t\telse\n\t\t\tptr = QCPOINTERM(i);\n\t\tOPC->_int = *(short *)ptr;\n\t\tbreak;\n\tcase OP_LOADP_I:\n\tcase OP_LOADP_F:\n\tcase OP_LOADP_FLD:\n\tcase OP_LOADP_ENT:\n\tcase OP_LOADP_S:\n\tcase OP_LOADP_FNC:\n\t\ti = OPA->_int + OPB->_int*4;\n\t\terrorif (QCPOINTERREADFAIL(i, sizeof(int)))\n\t\t{\n\t\t\tif (!(ptr=PR_GetReadTempStringPtr(progfuncs, OPA->_int, OPB->_int*4, sizeof(int))))\n\t\t\t{\n\t\t\t\tif (i == -1)\n\t\t\t\t{\n\t\t\t\t\tOPC->_int = 0;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tQCFAULT(&progfuncs->funcs, \"bad pointer read in %s (from %#x)\", PR_StringToNative(&progfuncs->funcs, prinst.pr_xfunction->s_name), i);\n\t\t\t}\n\t\t}\n\t\telse\n\t\t\tptr = QCPOINTERM(i);\n\t\tOPC->_int = ptr->_int;\n\t\tbreak;\n\n\tcase OP_LOADP_I64:\n\t\ti = OPA->_int + OPB->_int*4;\n\t\terrorif (QCPOINTERREADFAIL(i, sizeof(pint64_t)))\n\t\t{\n\t\t\tif (!(ptr=PR_GetReadTempStringPtr(progfuncs, OPA->_int, OPB->_int*4, sizeof(pint64_t))))\n\t\t\t{\n\t\t\t\tif (i == -1)\n\t\t\t\t{\n\t\t\t\t\tOPC->i64 = 0;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tQCFAULT(&progfuncs->funcs, \"bad pointer read in %s (from %#x)\", PR_StringToNative(&progfuncs->funcs, prinst.pr_xfunction->s_name), i);\n\t\t\t}\n\t\t}\n\t\telse\n\t\t\tptr = QCPOINTERM(i);\n\t\tOPC->i64 = ptr->i64;\n\t\tbreak;\n\n\tcase OP_LOADP_V:\n\t\ti = OPA->_int + OPB->_int*4;\t//NOTE: inconsistant, but a bit more practical for the qcc when structs etc are involved\n\t\terrorif (QCPOINTERREADFAIL(i, sizeof(pvec3_t)))\n\t\t{\n\t\t\tif (!(ptr=PR_GetReadTempStringPtr(progfuncs, OPA->_int, OPB->_int*4, sizeof(pvec3_t))))\n\t\t\t{\n\t\t\t\tif (i == -1)\n\t\t\t\t{\n\t\t\t\t\tOPC->_vector[0] = 0;\n\t\t\t\t\tOPC->_vector[1] = 0;\n\t\t\t\t\tOPC->_vector[2] = 0;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tQCFAULT(&progfuncs->funcs, \"bad pointer read in %s (from %#x)\", PR_StringToNative(&progfuncs->funcs, prinst.pr_xfunction->s_name), i);\n\t\t\t}\n\t\t}\n\t\telse\n\t\t\tptr = QCPOINTERM(i);\n\t\tOPC->_vector[0] = ptr->_vector[0];\n\t\tOPC->_vector[1] = ptr->_vector[1];\n\t\tOPC->_vector[2] = ptr->_vector[2];\n\t\tbreak;\n\n\tcase OP_BITXOR_I:\n\t\tOPC->_int = OPA->_int ^ OPB->_int;\n\t\tbreak;\n\tcase OP_RSHIFT_I:\n\t\tOPC->_int = A_RSHIFT_I(OPA->_int, OPB->_int);\n\t\tbreak;\n\tcase OP_RSHIFT_U:\n\t\tOPC->_uint = OPA->_uint >> OPB->_uint;\n\t\tbreak;\n\tcase OP_LSHIFT_I:\n\t\tOPC->_int = OPA->_int << OPB->_int;\n\t\tbreak;\n\n\t//hexen2 arrays contain a prefix global set to (arraysize-1) inserted before the actual array data\n\t//for vectors, this prefix is the number of vectors rather than the number of globals. this can cause issues with using OP_FETCH_GBL_V within structs.\n\tcase OP_FETCH_GBL_F:\n\tcase OP_FETCH_GBL_S:\n\tcase OP_FETCH_GBL_E:\n\tcase OP_FETCH_GBL_FNC:\n\t\ti = OPB->_float;\n\t\terrorif((unsigned)i > (unsigned)((eval_t *)&glob[st->a-1])->_int)\n\t\t{\n\t\t\tprinst.pr_xstatement = st-pr_statements;\n\t\t\tPR_RunError(&progfuncs->funcs, \"array index out of bounds: %s[%d] (max %d)\", PR_GlobalStringNoContents(progfuncs, st->a), i, ((eval_t *)&glob[st->a-1])->_int);\n\t\t}\n\t\tOPC->_int = ((eval_t *)&glob[st->a + i])->_int;\n\t\tbreak;\n\tcase OP_FETCH_GBL_V:\n\t\ti = OPB->_float;\n\t\terrorif((unsigned)i > (unsigned)((eval_t *)&glob[st->a-1])->_int)\n\t\t{\n\t\t\tprinst.pr_xstatement = st-pr_statements;\n\t\t\tPR_RunError(&progfuncs->funcs, \"array index out of bounds: %s[%d]\", PR_GlobalStringNoContents(progfuncs, st->a), i);\n\t\t}\n\t\tptr = (eval_t *)&glob[st->a + i*3];\n\t\tOPC->_vector[0] = ptr->_vector[0];\n\t\tOPC->_vector[1] = ptr->_vector[1];\n\t\tOPC->_vector[2] = ptr->_vector[2];\n\t\tbreak;\n\n\tcase OP_CSTATE:\n\t\texterns->cstateop(&progfuncs->funcs, OPA->_float, OPB->_float, prinst.pr_xfunction - pr_cp_functions);\n\t\tbreak;\n\n\tcase OP_CWSTATE:\n\t\texterns->cwstateop(&progfuncs->funcs, OPA->_float, OPB->_float, prinst.pr_xfunction - pr_cp_functions);\n\t\tbreak;\n\n\tcase OP_THINKTIME:\n\t\texterns->thinktimeop(&progfuncs->funcs, (struct edict_s *)PROG_TO_EDICT_UB(progfuncs, OPA->edict), OPB->_float);\n\t\tbreak;\n\n\tcase OP_MULSTORE_F:\n\t\t/*OPC->_float = */OPB->_float *= OPA->_float;\n\t\tbreak;\n\tcase OP_MULSTORE_VF:\n\t\ttmpf = OPA->_float;\t//don't break on vec*=vec_x;\n\t\t/*OPC->_vector[0] = */OPB->_vector[0] *= tmpf;\n\t\t/*OPC->_vector[1] = */OPB->_vector[1] *= tmpf;\n\t\t/*OPC->_vector[2] = */OPB->_vector[2] *= tmpf;\n\t\tbreak;\n\tcase OP_MULSTOREP_F:\n\t\ti = OPB->_int;\n\t\terrorif (QCPOINTERWRITEFAIL(i, sizeof(float)))\n\t\t{\n\t\t\tprinst.pr_xstatement = st-pr_statements;\n\t\t\tPR_RunError (&progfuncs->funcs, \"bad pointer write in %s (%x >= %x)\", PR_StringToNative(&progfuncs->funcs, prinst.pr_xfunction->s_name), i, (unsigned)prinst.addressableused);\n\t\t}\n\t\tptr = QCPOINTERM(i);\n\t\tOPC->_float = ptr->_float *= OPA->_float;\n\t\tbreak;\n\tcase OP_MULSTOREP_VF:\n\t\ti = OPB->_int;\n\t\terrorif (QCPOINTERWRITEFAIL(i, sizeof(pvec3_t)))\n\t\t{\n\t\t\tprinst.pr_xstatement = st-pr_statements;\n\t\t\tPR_RunError (&progfuncs->funcs, \"bad pointer write in %s (%x >= %x)\", PR_StringToNative(&progfuncs->funcs, prinst.pr_xfunction->s_name), i, (unsigned)prinst.addressableused);\n\t\t}\n\t\tptr = QCPOINTERM(i);\n\t\ttmpf = OPA->_float;\t//don't break on vec*=vec_x;\n\t\tOPC->_vector[0] = ptr->_vector[0] *= tmpf;\n\t\tOPC->_vector[1] = ptr->_vector[1] *= tmpf;\n\t\tOPC->_vector[2] = ptr->_vector[2] *= tmpf;\n\t\tbreak;\n\tcase OP_DIVSTORE_F:\n\t\t/*OPC->_float = */OPB->_float /= OPA->_float;\n\t\tbreak;\n\tcase OP_DIVSTOREP_F:\n\t\ti = OPB->_int;\n\t\terrorif (QCPOINTERWRITEFAIL(i, sizeof(float)))\n\t\t{\n\t\t\tprinst.pr_xstatement = st-pr_statements;\n\t\t\tPR_RunError (&progfuncs->funcs, \"bad pointer write in %s (%x >= %x)\", PR_StringToNative(&progfuncs->funcs, prinst.pr_xfunction->s_name), i, (unsigned)prinst.addressableused);\n\t\t}\n\t\tptr = QCPOINTERM(i);\n\t\tOPC->_float = ptr->_float /= OPA->_float;\n\t\tbreak;\n\tcase OP_ADDSTORE_F:\n\t\t/*OPC->_float = */OPB->_float += OPA->_float;\n\t\tbreak;\n\tcase OP_ADDSTORE_V:\n\t\t/*OPC->_vector[0] =*/ OPB->_vector[0] += OPA->_vector[0];\n\t\t/*OPC->_vector[1] =*/ OPB->_vector[1] += OPA->_vector[1];\n\t\t/*OPC->_vector[2] =*/ OPB->_vector[2] += OPA->_vector[2];\n\t\tbreak;\n\tcase OP_ADDSTOREP_F:\n\t\ti = OPB->_int;\n\t\terrorif (QCPOINTERWRITEFAIL(i, sizeof(float)))\n\t\t{\n\t\t\tprinst.pr_xstatement = st-pr_statements;\n\t\t\tPR_RunError (&progfuncs->funcs, \"bad pointer write in %s (%x >= %x)\", PR_StringToNative(&progfuncs->funcs, prinst.pr_xfunction->s_name), i, (unsigned)prinst.addressableused);\n\t\t}\n\t\tptr = QCPOINTERM(i);\n\t\tOPC->_float = ptr->_float += OPA->_float;\n\t\tbreak;\n\tcase OP_ADDSTOREP_V:\n\t\ti = OPB->_int;\n\t\terrorif (QCPOINTERWRITEFAIL(i, sizeof(pvec3_t)))\n\t\t{\n\t\t\tprinst.pr_xstatement = st-pr_statements;\n\t\t\tPR_RunError (&progfuncs->funcs, \"bad pointer write in %s (%x >= %x)\", PR_StringToNative(&progfuncs->funcs, prinst.pr_xfunction->s_name), i, (unsigned)prinst.addressableused);\n\t\t}\n\t\tptr = QCPOINTERM(i);\n\t\tOPC->_vector[0] = ptr->_vector[0] += OPA->_vector[0];\n\t\tOPC->_vector[1] = ptr->_vector[1] += OPA->_vector[1];\n\t\tOPC->_vector[2] = ptr->_vector[2] += OPA->_vector[2];\n\t\tbreak;\n\tcase OP_SUBSTORE_F:\n\t\t/*OPC->_float = */OPB->_float -= OPA->_float;\n\t\tbreak;\n\tcase OP_SUBSTORE_V:\n\t\t/*OPC->_vector[0] = */OPB->_vector[0] -= OPA->_vector[0];\n\t\t/*OPC->_vector[1] = */OPB->_vector[1] -= OPA->_vector[1];\n\t\t/*OPC->_vector[2] = */OPB->_vector[2] -= OPA->_vector[2];\n\t\tbreak;\n\tcase OP_SUBSTOREP_F:\n\t\ti = OPB->_int;\n\t\terrorif (QCPOINTERWRITEFAIL(i, sizeof(float)))\n\t\t{\n\t\t\tprinst.pr_xstatement = st-pr_statements;\n\t\t\tPR_RunError (&progfuncs->funcs, \"bad pointer write in %s (%x >= %x)\", PR_StringToNative(&progfuncs->funcs, prinst.pr_xfunction->s_name), i, (unsigned)prinst.addressableused);\n\t\t}\n\t\tptr = QCPOINTERM(i);\n\t\tOPC->_float = ptr->_float -= OPA->_float;\n\t\tbreak;\n\tcase OP_SUBSTOREP_V:\n\t\ti = OPB->_int;\n\t\terrorif (QCPOINTERWRITEFAIL(i, sizeof(pvec3_t)))\n\t\t{\n\t\t\tprinst.pr_xstatement = st-pr_statements;\n\t\t\tPR_RunError (&progfuncs->funcs, \"bad pointer write in %s (%x >= %x)\", PR_StringToNative(&progfuncs->funcs, prinst.pr_xfunction->s_name), i, (unsigned)prinst.addressableused);\n\t\t}\n\t\tptr = QCPOINTERM(i);\n\t\tOPC->_vector[0] = ptr->_vector[0] -= OPA->_vector[0];\n\t\tOPC->_vector[1] = ptr->_vector[1] -= OPA->_vector[1];\n\t\tOPC->_vector[2] = ptr->_vector[2] -= OPA->_vector[2];\n\t\tbreak;\n\tcase OP_BITSETSTORE_F:\n\t\tOPB->_float = (int)OPB->_float | (int)OPA->_float;\n\t\tbreak;\n\tcase OP_BITSETSTOREP_F:\n\t\ti = OPB->_int;\n\t\terrorif (QCPOINTERWRITEFAIL(i, sizeof(float)))\n\t\t{\n\t\t\tprinst.pr_xstatement = st-pr_statements;\n\t\t\tPR_RunError (&progfuncs->funcs, \"bad pointer write in %s (%x >= %x)\", PR_StringToNative(&progfuncs->funcs, prinst.pr_xfunction->s_name), i, (unsigned)prinst.addressableused);\n\t\t}\n\t\tptr = QCPOINTERM(i);\n\t\tptr->_float = (int)ptr->_float | (int)OPA->_float;\n\t\tbreak;\n\tcase OP_BITCLRSTORE_F:\n\t\tOPB->_float = (int)OPB->_float & ~(int)OPA->_float;\n\t\tbreak;\n\tcase OP_BITCLRSTOREP_F:\n\t\ti = OPB->_int;\n\t\terrorif (QCPOINTERWRITEFAIL(i, sizeof(float)))\n\t\t{\n\t\t\tprinst.pr_xstatement = st-pr_statements;\n\t\t\tPR_RunError (&progfuncs->funcs, \"bad pointer write in %s (%x >= %x)\", PR_StringToNative(&progfuncs->funcs, prinst.pr_xfunction->s_name), i, (unsigned)prinst.addressableused);\n\t\t}\n\t\tptr = QCPOINTERM(i);\n\t\tptr->_float = (int)ptr->_float & ~(int)OPA->_float;\n\t\tbreak;\n\n\t//for scaler randoms, prevent the random value from ever reaching 1\n\t//this avoids issues when array[random()*array.length]\n\tcase OP_RAND0:\n\t\tOPC->_float = (rand ()&0x7fff) / ((float)0x8000);\n\t\tbreak;\n\tcase OP_RAND1:\n\t\tOPC->_float = (rand ()&0x7fff) / ((float)0x8000)*OPA->_float;\n\t\tbreak;\n\tcase OP_RAND2:\t//backwards range shouldn't matter (except that it is b that is never reached, rather than the higher of the two)\n\t\tOPC->_float = OPA->_float + (rand ()&0x7fff) / ((float)0x8000)*(OPB->_float-OPA->_float);\n\t\tbreak;\n\t//random vectors DO result in 0 to 1 inclusive, to try to ensure a more balanced range\n\tcase OP_RANDV0:\n\t\tOPC->_vector[0] = (rand ()&0x7fff) / ((float)0x7fff);\n\t\tOPC->_vector[1] = (rand ()&0x7fff) / ((float)0x7fff);\n\t\tOPC->_vector[2] = (rand ()&0x7fff) / ((float)0x7fff);\n\t\tbreak;\n\tcase OP_RANDV1:\n\t\tOPC->_vector[0] = (rand ()&0x7fff) / ((float)0x7fff)*OPA->_vector[0];\n\t\tOPC->_vector[1] = (rand ()&0x7fff) / ((float)0x7fff)*OPA->_vector[1];\n\t\tOPC->_vector[2] = (rand ()&0x7fff) / ((float)0x7fff)*OPA->_vector[2];\n\t\tbreak;\n\tcase OP_RANDV2:\t//backwards range shouldn't matter\n\t\tOPC->_vector[0] = OPA->_vector[0] + (rand ()&0x7fff) / ((float)0x7fff)*(OPB->_vector[0]-OPA->_vector[0]);\n\t\tOPC->_vector[1] = OPA->_vector[1] + (rand ()&0x7fff) / ((float)0x7fff)*(OPB->_vector[1]-OPA->_vector[1]);\n\t\tOPC->_vector[2] = OPA->_vector[2] + (rand ()&0x7fff) / ((float)0x7fff)*(OPB->_vector[2]-OPA->_vector[2]);\n\t\tbreak;\n\n\tcase OP_SWITCH_F:\n\tcase OP_SWITCH_V:\n\tcase OP_SWITCH_S:\n\tcase OP_SWITCH_E:\n\tcase OP_SWITCH_FNC:\n\t\t//the case opcodes depend upon the preceding switch.\n\t\t//otherwise the switch itself is much like a goto\n\t\t//don't embed the case/caserange checks directly into the switch so that custom caseranges can be potentially be implemented with hybrid emulation.\n\t\tswitchcomparison = op - OP_SWITCH_F;\n\t\tswitchref = OPA;\n\t\tRUNAWAYCHECK();\n\t\tst += (sofs)st->b - 1;\t// offset the s++\n\t\tbreak;\n\tcase OP_SWITCH_I:\n\t\t//the case opcodes depend upon the preceding switch.\n\t\t//otherwise the switch itself is much like a goto\n\t\t//don't embed the case/caserange checks directly into the switch so that custom caseranges can be potentially be implemented with hybrid emulation.\n\t\tswitchcomparison = OP_SWITCH_E - OP_SWITCH_F;\n\t\tswitchref = OPA;\n\t\tRUNAWAYCHECK();\n\t\tst += (sofs)st->b - 1;\t// offset the s++\n\t\tbreak;\n\tcase OP_CASE:\n\t\t//if the comparison is true, jump (back up) to the relevent code block\n\t\tif (casecmp[switchcomparison](progfuncs, switchref, OPA))\n\t\t{\n\t\t\tRUNAWAYCHECK();\n\t\t\tst += (sofs)st->b-1; // -1 to offset the s++\n\t\t}\n\t\tbreak;\n\tcase OP_CASERANGE:\n\t\t//if the comparison is true, jump (back up) to the relevent code block\n\t\tif (casecmprange[switchcomparison](progfuncs, switchref, OPA, OPB))\n\t\t{\n\t\t\tRUNAWAYCHECK();\n\t\t\tst += (sofs)st->c-1; // -1 to offset the s++\n\t\t}\n\t\tbreak;\n\n\n\n\n\n\n\n\n\tcase OP_BITAND_IF:\n\t\tOPC->_int = (OPA->_int & (int)OPB->_float);\n\t\tbreak;\n\tcase OP_BITOR_IF:\n\t\tOPC->_int = (OPA->_int | (int)OPB->_float);\n\t\tbreak;\n\tcase OP_BITAND_FI:\n\t\tOPC->_int = ((int)OPA->_float & OPB->_int);\n\t\tbreak;\n\tcase OP_BITOR_FI:\n\t\tOPC->_int = ((int)OPA->_float | OPB->_int);\n\t\tbreak;\n\n\tcase OP_MUL_IF:\n\t\tOPC->_float = (OPA->_int * OPB->_float);\n\t\tbreak;\n\tcase OP_MUL_FI:\n\t\tOPC->_float = (OPA->_float * OPB->_int);\n\t\tbreak;\n\n\tcase OP_MUL_VI:\n\t\ttmpi = OPB->_int;\n\t\tOPC->_vector[0] = OPA->_vector[0] * tmpi;\n\t\tOPC->_vector[1] = OPA->_vector[1] * tmpi;\n\t\tOPC->_vector[2] = OPA->_vector[2] * tmpi;\n\t\tbreak;\n\tcase OP_MUL_IV:\n\t\ttmpi = OPA->_int;\n\t\tOPC->_vector[0] = tmpi * OPB->_vector[0];\n\t\tOPC->_vector[1] = tmpi * OPB->_vector[1];\n\t\tOPC->_vector[2] = tmpi * OPB->_vector[2];\n\t\tbreak;\n\n\tcase OP_DIV_IF:\n\t\tOPC->_float = (OPA->_int / OPB->_float);\n\t\tbreak;\n\tcase OP_DIV_FI:\n\t\tOPC->_float = (OPA->_float / OPB->_int);\n\t\tbreak;\n\n\t/*case OP_MOD_I:\n\t\tOPC->_int = (OPA->_int % OPB->_int);\n\t\tbreak;\n\tcase OP_MOD_U:\n\t\tOPC->_uint = (OPA->_uint % OPB->_uint);\n\t\tbreak;\n\tcase OP_MOD_F:\n\t\tOPC->_float = OPA->_float - OPB->_float*(int)(OPA->_float/OPB->_float);\n\t\tbreak;\n\tcase OP_MOD_V:\n\t\tOPC->_vector[0] = OPA->_vector[0] - OPB->_vector[0]*(int)(OPA->_vector[0]/OPB->_vector[0]);\n\t\tOPC->_vector[1] = OPA->_vector[1] - OPB->_vector[1]*(int)(OPA->_vector[1]/OPB->_vector[1]);\n\t\tOPC->_vector[2] = OPA->_vector[2] - OPB->_vector[2]*(int)(OPA->_vector[2]/OPB->_vector[2]);\n\t\tbreak;*/\n\n\n\tcase OP_AND_I:\n\t\tOPC->_int = (OPA->_int && OPB->_int);\n\t\tbreak;\n\tcase OP_OR_I:\n\t\tOPC->_int = (OPA->_int || OPB->_int);\n\t\tbreak;\n\n\tcase OP_AND_IF:\n\t\tOPC->_int = (OPA->_int && OPB->_float);\n\t\tbreak;\n\tcase OP_OR_IF:\n\t\tOPC->_int = (OPA->_int || OPB->_float);\n\t\tbreak;\n\n\tcase OP_AND_FI:\n\t\tOPC->_int = (OPA->_float && OPB->_int);\n\t\tbreak;\n\tcase OP_OR_FI:\n\t\tOPC->_int = (OPA->_float || OPB->_int);\n\t\tbreak;\n\n\tcase OP_NE_IF:\n\t\tOPC->_int = (OPA->_int != OPB->_float);\n\t\tbreak;\n\tcase OP_NE_FI:\n\t\tOPC->_int = (OPA->_float != OPB->_int);\n\t\tbreak;\n\n\tcase OP_GADDRESS: //return glob[aint+bfloat]\n\t\t//this instruction is not implemented due to the weirdness of it.\n\t\t//its theoretically a more powerful load... but untyped?\n\t\t//or is it meant to be an LEA instruction (that could simply be switched with OP_GLOAD_I)\n\t\tprinst.pr_xstatement = st-pr_statements;\n\t\tPR_RunError (&progfuncs->funcs, \"OP_GADDRESS not implemented (found in %s)\", PR_StringToNative(&progfuncs->funcs, prinst.pr_xfunction->s_name));\n\t\tbreak;\n\tcase OP_GLOAD_I:\n\tcase OP_GLOAD_F:\n\tcase OP_GLOAD_FLD:\n\tcase OP_GLOAD_ENT:\n\tcase OP_GLOAD_S:\n\tcase OP_GLOAD_FNC:\n\t\terrorif (OPA->_int < 0 || OPA->_int >= (current_progstate->globals_bytes>>2))\n\t\t{\n\t\t\tprinst.pr_xstatement = st-pr_statements;\n\t\t\tPR_RunError (&progfuncs->funcs, \"bad indexed global read in %s (%x >= %x)\", PR_StringToNative(&progfuncs->funcs, prinst.pr_xfunction->s_name), OPA->_int, current_progstate->globals_bytes>>2);\n\t\t}\n\t\tptr = ((eval_t *)&glob[OPA->_int]);\n\t\tOPC->_int = ptr->_int;\n\t\tbreak;\n\tcase OP_GLOAD_V:\n\t\terrorif (OPA->_int < 0 || OPA->_int >= (current_progstate->globals_bytes>>2)-2u)\n\t\t{\n\t\t\tprinst.pr_xstatement = st-pr_statements;\n\t\t\tPR_RunError (&progfuncs->funcs, \"bad indexed global read in %s (%x >= %x)\", PR_StringToNative(&progfuncs->funcs, prinst.pr_xfunction->s_name), OPA->_int, current_progstate->globals_bytes>>2);\n\t\t}\n\t\tptr = ((eval_t *)&glob[OPA->_int]);\n\t\tOPC->_vector[0] = ptr->_vector[0];\n\t\tOPC->_vector[1] = ptr->_vector[1];\n\t\tOPC->_vector[2] = ptr->_vector[2];\n\t\tbreak;\n\tcase OP_GSTOREP_I:\n\tcase OP_GSTOREP_F:\n\tcase OP_GSTOREP_ENT:\n\tcase OP_GSTOREP_FLD:\n\tcase OP_GSTOREP_S:\n\tcase OP_GSTOREP_FNC:\n\t\terrorif (OPB->_int < 0 || OPB->_int >= (current_progstate->globals_bytes>>2))\n\t\t{\n\t\t\tprinst.pr_xstatement = st-pr_statements;\n\t\t\tPR_RunError (&progfuncs->funcs, \"bad indexed global write in %s (%x >= %x)\", PR_StringToNative(&progfuncs->funcs, prinst.pr_xfunction->s_name), OPB->_int, current_progstate->globals_bytes>>2);\n\t\t}\n\t\tptr = ((eval_t *)&glob[OPB->_int]);\n\t\tptr->_int = OPA->_int;\n\t\tbreak;\n\tcase OP_GSTOREP_V:\n\t\terrorif (OPB->_int < 0 || OPB->_int >= (current_progstate->globals_bytes>>2)-2u)\n\t\t{\n\t\t\tprinst.pr_xstatement = st-pr_statements;\n\t\t\tPR_RunError (&progfuncs->funcs, \"bad indexed global write in %s (%x >= %x)\", PR_StringToNative(&progfuncs->funcs, prinst.pr_xfunction->s_name), OPB->_int, current_progstate->globals_bytes>>2);\n\t\t}\n\t\tptr = ((eval_t *)&glob[OPB->_int]);\n\t\tptr->_vector[0] = OPA->_vector[0];\n\t\tptr->_vector[1] = OPA->_vector[1];\n\t\tptr->_vector[2] = OPA->_vector[2];\n\t\tbreak;\n\n\tcase OP_BOUNDCHECK:\n\t\terrorif ((unsigned int)OPA->_int < (unsigned int)st->c || (unsigned int)OPA->_int >= (unsigned int)st->b)\n\t\t{\n\t\t\texterns->Printf(\"Progs boundcheck failed. Value is %i. Must be %u<=value<%u\\n\", OPA->_int, st->c, st->b);\n\t\t\tQCFAULT(&progfuncs->funcs, \"Progs boundcheck failed. Value is %i. Must be %u<=value<%u\\n\", OPA->_int, st->c, st->b);\n/*\t\t\ts=ShowStepf(progfuncs, st - pr_statements, \"Progs boundcheck failed. Value is %i. Must be between %u and %u\\n\", OPA->_int, st->c, st->b);\n\t\t\tif (st == pr_statements + s)\n\t\t\t\tPR_RunError(&progfuncs->funcs, \"unable to resume boundcheck\");\n\t\t\tst = pr_statements + s;\n\t\t\treturn s;\n*/\t\t}\n\t\tbreak;\n\tcase OP_PUSH:\t//note: OPA is words, not bytes.\n\t\tOPC->_int = ENGINEPOINTER(&prinst.localstack[prinst.localstack_used+prinst.spushed]);\n\t\tprinst.spushed += OPA->_uint;\n\t\tif (prinst.spushed + prinst.localstack_used >= LOCALSTACK_SIZE)\n\t\t{\n\t\t\ti = prinst.spushed;\n\t\t\tprinst.spushed = 0;\n\t\t\tprinst.pr_xstatement = st-pr_statements;\n\t\t\tPR_RunError(&progfuncs->funcs, \"Progs pushed too much (%i bytes, %i parents, max %i)\", i, prinst.localstack_used, LOCALSTACK_SIZE);\n\t\t}\n\t\tbreak;\n/*\tcase OP_POP:\n\t\tpr_spushed -= OPA->_uint;\n\t\tif (pr_spushed < 0)\n\t\t{\n\t\t\tpr_spushed = 0;\n\t\t\tprinst.pr_xstatement = st-pr_statements;\n\t\t\tPR_RunError(progfuncs, \"Progs poped more than it pushed\");\n\t\t}\n\t\tbreak;\n*/\n\n\n\t//[u]int64+double opcodes\n\tcase OP_ADD_I64:\t\tOPC->i64     = OPA->i64  + OPB->i64;   break;\n\tcase OP_SUB_I64:\t\tOPC->i64     = OPA->i64  -  OPB->i64;  break;\n\tcase OP_MUL_I64:\t\tOPC->i64     = OPA->i64  *  OPB->i64;  break;\n\tcase OP_DIV_I64:\t\tOPC->i64     = OPA->i64  /  OPB->i64;  break;\n\tcase OP_BITAND_I64:\t\tOPC->i64     = OPA->i64  &  OPB->i64;  break;\n\tcase OP_BITOR_I64:\t\tOPC->i64     = OPA->i64  |  OPB->i64;  break;\n\tcase OP_BITXOR_I64:\t\tOPC->i64     = OPA->i64  ^  OPB->i64;  break;\n\tcase OP_LSHIFT_I64I:\tOPC->i64     = OPA->i64  << OPB->_int; break;\n\tcase OP_RSHIFT_I64I:\tOPC->i64\t = A_RSHIFT_I(OPA->i64, OPB->_int);\tbreak;\n\tcase OP_LT_I64:\t\t\tOPC->_int    = OPA->i64  <  OPB->i64;  break;\n\tcase OP_LE_I64:\t\t\tOPC->_int    = OPA->i64  <= OPB->i64;  break;\n\tcase OP_EQ_I64:\t\t\tOPC->_int    = OPA->i64  == OPB->i64;  break;\n\tcase OP_NE_I64:\t\t\tOPC->_int    = OPA->i64  != OPB->i64;  break;\n\tcase OP_LT_U64:\t\t\tOPC->_int    = OPA->u64 <  OPB->u64;   break;\n\tcase OP_LE_U64:\t\t\tOPC->_int    = OPA->u64 <= OPB->u64;   break;\n\tcase OP_DIV_U64:\t\tOPC->u64     = OPA->u64 /  OPB->u64;   break;\n\tcase OP_RSHIFT_U64I:\tOPC->u64     = OPA->u64 >> OPB->_int;  break;\n\tcase OP_STORE_I64:\t\tOPB->i64     = OPA->i64;\t break;\n\tcase OP_CONV_UI64:\t\tOPC->i64     = OPA->_uint;   break;\n\tcase OP_CONV_II64:\t\tOPC->i64     = OPA->_int;    break;\n\tcase OP_CONV_I64I:\t\tOPC->_int    = OPA->i64;     break;\n\tcase OP_CONV_FD:\t\tOPC->_double = OPA->_float;  break;\n\tcase OP_CONV_DF:\t\tOPC->_float  = OPA->_double; break;\n\tcase OP_CONV_I64F:\t\tOPC->_float  = OPA->i64;     break;\n\tcase OP_CONV_FI64:\t\tOPC->i64     = OPA->_float;  break;\n\tcase OP_CONV_I64D:\t\tOPC->_double = OPA->i64;     break;\n\tcase OP_CONV_DI64:\t\tOPC->i64     = OPA->_double; break;\n\tcase OP_CONV_U64D:\t\tOPC->_double = OPA->u64;     break;\n\tcase OP_CONV_DU64:\t\tOPC->u64     = OPA->_double; break;\n\tcase OP_CONV_U64F:\t\tOPC->_float\t = OPA->u64;     break;\n\tcase OP_CONV_FU64:\t\tOPC->u64     = OPA->_float;  break;\n\tcase OP_ADD_D:\t\t\tOPC->_double = OPA->_double +  OPB->_double; break;\n\tcase OP_SUB_D:\t\t\tOPC->_double = OPA->_double -  OPB->_double; break;\n\tcase OP_MUL_D:\t\t\tOPC->_double = OPA->_double *  OPB->_double; break;\n\tcase OP_DIV_D:\t\t\tOPC->_double = OPA->_double /  OPB->_double; break;\n\tcase OP_LT_D:\t\t\tOPC->_int    = OPA->_double <  OPB->_double; break;\n\tcase OP_LE_D:\t\t\tOPC->_int    = OPA->_double <= OPB->_double; break;\n\tcase OP_EQ_D:\t\t\tOPC->_int    = OPA->_double == OPB->_double; break;\n\tcase OP_NE_D:\t\t\tOPC->_int    = OPA->_double != OPB->_double; break;\n\n\tcase OP_BITEXTEND_I:\tOPC->_int    = A_RSHIFT_I((  signed int)(OPA->_int  << (32-(OPB->_uint&0xff)-(OPB->_uint>>8))), (signed)(32-(OPB->_uint&0xff))); break;\t//shift it up and down. should sign extend.\n\tcase OP_BITEXTEND_U:\tOPC->_uint   = (unsigned int)(OPA->_uint << (32-(OPB->_uint&0xff)-(OPB->_uint>>8))) >> (32-(OPB->_uint&0xff)); break;\t//shift it up and down. should clear the bits.\n\tcase OP_BITCOPY_I:\t\ti=((1<<(OPB->_uint&0xff))-1);OPC->_uint=(OPC->_uint&~(i<<(OPB->_uint>>8)))|(((OPA->_uint&i)<<(OPB->_uint>>8)));break;\t\t\t//replaces the specified bits (uses the same format bitextend uses to select its input to extend)\n\tcase OP_CONV_UF:\t\tOPC->_float  = OPA->_uint;   break;\n\tcase OP_CONV_FU:\t\tOPC->_uint   = OPA->_float;  break;\n\n\tcase OP_UNUSED:\n\tcase OP_POP:\n\n#ifdef __GNUC__\n\tcase OP_NUMREALOPS ... OP_NUMOPS:\n#endif\n\n\tsafedefault:\n\t\tif (op & OP_BIT_BREAKPOINT)\t//break point!\n\t\t{\n\t\t\top &= ~OP_BIT_BREAKPOINT;\n\t\t\ts = st-pr_statements;\n\t\t\tif (prinst.pr_xstatement != s)\n\t\t\t{\n\t\t\t\tprinst.pr_xstatement = s;\n\t\t\t\texterns->Printf(\"Break point hit in %s.\\n\", PR_StringToNative(&progfuncs->funcs, prinst.pr_xfunction->s_name));\n\t\t\t\ts = ShowStep(progfuncs, s, NULL, false);\n\t\t\t\tst = &pr_statements[s];\t//let the user move execution\n\t\t\t\tprinst.pr_xstatement = s = st-pr_statements;\n\t\t\t\top = st->op & ~OP_BIT_BREAKPOINT;\n\t\t\t}\n\t\t\tgoto reeval;\t//reexecute\n\t\t}\n\t\tprinst.pr_xstatement = st-pr_statements;\n\t\tPR_RunError (&progfuncs->funcs, \"Bad opcode %i\", st->op);\n\t}\n}\n\n\n#undef reeval\n#undef st\n#undef pr_statements\n#undef fakeop\n#undef dstatement_t\n#undef sofs\n#undef OPCODE\n\n#undef ENGINEPOINTER\n#undef QCPOINTER\n#undef QCPOINTERM\n\n"
  },
  {
    "path": "engine/qclib/fteqcc.rc",
    "content": "101               ICON                    \"byshpuld.ico\"\r\n"
  },
  {
    "path": "engine/qclib/gui.h",
    "content": "void GoToDefinition(const char *name);\nint Grep(const char *filename, const char *string);\nvoid EditFile(const char *name, int line, pbool setcontrol);\n\nvoid GUI_SetDefaultOpts(void);\nint GUI_BuildParms(const char *args, const char **argv, int argv_size, pbool quick);\n\n//unsigned char *PDECL QCC_ReadFile (const char *fname, void *buffer, int len, size_t *sz);\nint QCC_RawFileSize (const char *fname);\npbool QCC_WriteFile (const char *name, void *data, int len);\nvoid GUI_DialogPrint(const char *title, const char *text);\n\nvoid *GUIReadFile(const char *fname, unsigned char *(*buf_get)(void *ctx, size_t len), void *buf_ctx, size_t *out_size, pbool issourcefile);\nint GUIFileSize(const char *fname);\n\nint GUI_ParseCommandLine(const char *args, pbool keepsrcanddir); //0=gui, 1=commandline\nvoid GUI_SaveConfig(void);\nvoid GUI_RevealOptions(void);\nint GUIprintf(const char *msg, ...);\n\npbool GenBuiltinsList(char *buffer, int buffersize);\npbool GenAutoCompleteList(char *prefix, char *buffer, int buffersize);\n\nextern char parameters[16384];\n\nextern char progssrcname[256];\nextern char progssrcdir[256];\n\nextern pbool fl_nondfltopts;\nextern pbool fl_hexen2;\nextern pbool fl_ftetarg;\nextern pbool fl_autohighlight;\nextern pbool fl_compileonstart;\nextern pbool fl_showall;\nextern pbool fl_log;\n"
  },
  {
    "path": "engine/qclib/hash.c",
    "content": "#if _MSC_VER >= 1300\n\t#ifndef _CRT_SECURE_NO_WARNINGS\n\t\t#define _CRT_SECURE_NO_WARNINGS\n\t#endif\n\t#ifndef _CRT_NONSTDC_NO_WARNINGS\n\t\t#define _CRT_NONSTDC_NO_WARNINGS\n\t#endif\n#endif\n\n#include \"hash.h\"\n#include <stdlib.h>\n#include <string.h>\n\n#ifndef _WIN32\n#ifndef stricmp \n#define stricmp strcasecmp\n#endif\n#endif\n\n// hash init assumes we get clean memory\nvoid Hash_InitTable(hashtable_t *table, unsigned int numbucks, void *mem)\n{\n\ttable->numbuckets = numbucks;\n\ttable->bucket = (bucket_t **)mem;\n}\n\nvoid *Hash_Enumerate(hashtable_t *table, void (*callback) (void *ctx, void *data), void *ctx)\n{\n\tunsigned int bucknum;\n\tbucket_t *buck;\n\tvoid *data;\n\n\tfor (bucknum = 0; bucknum < table->numbuckets; bucknum++)\n\t{\n\t\tbuck = table->bucket[bucknum];\n\n\t\twhile(buck)\n\t\t{\n\t\t\tdata = buck->data;\n\t\t\tbuck = buck->next;\n\n\t\t\t//now that we don't care about backlinks etc, we can call the callback and it can safely nuke it (Hash_RemoveData or even destroy the bucket if the hash table is going to die).\n\t\t\tcallback(ctx, data);\n\t\t}\n\t}\n\treturn NULL;\n}\n\nunsigned int Hash_Key(const char *name, unsigned int modulus)\n{\t//fixme: optimize.\n\tunsigned int key;\n\tfor (key=0;*name; name++)\n\t\tkey += ((key<<3) + (key>>28) + *name);\n\t\t\n\treturn (key%modulus);\n}\nunsigned int Hash_KeyInsensitive(const char *name, unsigned int modulus)\n{\t//fixme: optimize.\n\tunsigned int key;\n\tfor (key=0;*name; name++)\n\t{\n\t\tif (*name >= 'A' && *name <= 'Z')\n\t\t\tkey += ((key<<3) + (key>>28) + (*name-'A'+'a'));\n\t\telse\n\t\t\tkey += ((key<<3) + (key>>28) + *name);\n\t}\n\t\t\n\treturn (key%modulus);\n}\n\nvoid *Hash_GetIdx(hashtable_t *table, unsigned int idx)\n{\n\tunsigned int bucknum;\n\tbucket_t *buck;\n\n\tfor (bucknum = 0; bucknum < table->numbuckets; bucknum++)\n\t{\n\t\tbuck = table->bucket[bucknum];\n\n\t\twhile(buck)\n\t\t{\n\t\t\tif (!idx--)\n\t\t\t\treturn buck->data;\n\n\t\t\tbuck = buck->next;\n\t\t}\n\t}\n\treturn NULL;\n}\n\nvoid *Hash_Get(hashtable_t *table, const char *name)\n{\n\tunsigned int bucknum = Hash_Key(name, table->numbuckets);\n\tbucket_t *buck;\n\n\tbuck = table->bucket[bucknum];\n\n\twhile(buck)\n\t{\n\t\tif (!STRCMP(name, buck->key.string))\n\t\t\treturn buck->data;\n\n\t\tbuck = buck->next;\n\t}\n\treturn NULL;\n}\nvoid *Hash_GetInsensitive(hashtable_t *table, const char *name)\n{\n\tunsigned int bucknum = Hash_KeyInsensitive(name, table->numbuckets);\n\tbucket_t *buck;\n\n\tbuck = table->bucket[bucknum];\n\n\twhile(buck)\n\t{\n\t\tif (!stricmp(name, buck->key.string))\n\t\t\treturn buck->data;\n\n\t\tbuck = buck->next;\n\t}\n\treturn NULL;\n}\nvoid *Hash_GetInsensitiveBucket(hashtable_t *table, const char *name)\n{\n\tunsigned int bucknum = Hash_KeyInsensitive(name, table->numbuckets);\n\tbucket_t *buck;\n\n\tbuck = table->bucket[bucknum];\n\n\twhile(buck)\n\t{\n\t\tif (!stricmp(name, buck->key.string))\n\t\t\treturn buck;\n\n\t\tbuck = buck->next;\n\t}\n\treturn NULL;\n}\nvoid *Hash_GetKey(hashtable_t *table, unsigned int key)\n{\n\tunsigned int bucknum = key%table->numbuckets;\n\tbucket_t *buck;\n\n\tbuck = table->bucket[bucknum];\n\n\twhile(buck)\n\t{\n\t\tif (buck->key.value == key)\n\t\t\treturn buck->data;\n\n\t\tbuck = buck->next;\n\t}\n\treturn NULL;\n}\n/*Does _NOT_ support items that are added with two names*/\nvoid *Hash_GetNextKey(hashtable_t *table, unsigned int key, void *old)\n{\n\tunsigned int bucknum = key%table->numbuckets;\n\tbucket_t *buck;\n\n\tbuck = table->bucket[bucknum];\n\n\twhile(buck)\n\t{\n\t\tif (buck->data == old)\t//found the old one\n\t\t\tbreak;\n\t\tbuck = buck->next;\n\t}\n\tif (!buck)\n\t\treturn NULL;\n\n\tbuck = buck->next;//don't return old\n\twhile(buck)\n\t{\n\t\tif (buck->key.value == key)\n\t\t\treturn buck->data;\n\n\t\tbuck = buck->next;\n\t}\n\treturn NULL;\n}\n/*Does _NOT_ support items that are added with two names*/\nvoid *Hash_GetNext(hashtable_t *table, const char *name, void *old)\n{\n\tunsigned int bucknum = Hash_Key(name, table->numbuckets);\n\tbucket_t *buck;\n\n\tbuck = table->bucket[bucknum];\n\n\twhile(buck)\n\t{\n\t\tif (buck->data == old)\t//found the old one\n//\t\t\tif (!STRCMP(name, buck->key.string))\n\t\t\t\tbreak;\n\t\tbuck = buck->next;\n\t}\n\tif (!buck)\n\t\treturn NULL;\n\n\tbuck = buck->next;//don't return old\n\twhile(buck)\n\t{\n\t\tif (!STRCMP(name, buck->key.string))\n\t\t\treturn buck->data;\n\n\t\tbuck = buck->next;\n\t}\n\treturn NULL;\n}\n/*Does _NOT_ support items that are added with two names*/\nvoid *Hash_GetNextInsensitive(hashtable_t *table, const char *name, void *old)\n{\n\tunsigned int bucknum = Hash_KeyInsensitive(name, table->numbuckets);\n\tbucket_t *buck;\n\n\tbuck = table->bucket[bucknum];\n\n\twhile(buck)\n\t{\n\t\tif (buck->data == old)\t//found the old one\n\t\t{\n//\t\t\tif (!stricmp(name, buck->key.string))\n\t\t\t\tbreak;\n\t\t}\n\n\t\tbuck = buck->next;\n\t}\n\tif (!buck)\n\t\treturn NULL;\n\n\tbuck = buck->next;//don't return old\n\twhile(buck)\n\t{\n\t\tif (!stricmp(name, buck->key.string))\n\t\t\treturn buck->data;\n\n\t\tbuck = buck->next;\n\t}\n\treturn NULL;\n}\n\n\nvoid *Hash_Add(hashtable_t *table, const char *name, void *data, bucket_t *buck)\n{\n\tunsigned int bucknum = Hash_Key(name, table->numbuckets);\n\n\tbuck->data = data;\n\tbuck->key.string = name;\n\tbuck->next = table->bucket[bucknum];\n\ttable->bucket[bucknum] = buck;\n\n\treturn buck;\n}\nvoid *Hash_AddInsensitive(hashtable_t *table, const char *name, void *data, bucket_t *buck)\n{\n\tunsigned int bucknum = Hash_KeyInsensitive(name, table->numbuckets);\n\n\tbuck->data = data;\n\tbuck->key.string = name;\n\tbuck->next = table->bucket[bucknum];\n\ttable->bucket[bucknum] = buck;\n\n\treturn buck;\n}\nvoid *Hash_AddKey(hashtable_t *table, unsigned int key, void *data, bucket_t *buck)\n{\n\tunsigned int bucknum = key%table->numbuckets;\n\n\tbuck->data = data;\n\tbuck->key.value = key;\n\tbuck->next = table->bucket[bucknum];\n\ttable->bucket[bucknum] = buck;\n\n\treturn buck;\n}\n\nvoid Hash_Remove(hashtable_t *table, const char *name)\n{\n\tunsigned int bucknum = Hash_Key(name, table->numbuckets);\n\tbucket_t *buck;\t\n\n\tbuck = table->bucket[bucknum];\n\n\tif (!STRCMP(name, buck->key.string))\n\t{\n\t\ttable->bucket[bucknum] = buck->next;\n\t\treturn;\n\t}\n\n\n\twhile(buck->next)\n\t{\n\t\tif (!STRCMP(name, buck->next->key.string))\n\t\t{\n\t\t\tbuck->next = buck->next->next;\n\t\t\treturn;\n\t\t}\n\n\t\tbuck = buck->next;\n\t}\n\treturn;\n}\n\nvoid Hash_RemoveDataInsensitive(hashtable_t *table, const char *name, void *data)\n{\n\tunsigned int bucknum = Hash_KeyInsensitive(name, table->numbuckets);\n\tbucket_t **link, *buck;\t\n\n\tfor (link = &table->bucket[bucknum]; *link; link = &(*link)->next)\n\t{\n\t\tbuck = *link;\n\t\tif (buck->data == data && !stricmp(name, buck->key.string))\n\t\t{\n\t\t\t*link = buck->next;\n\t\t\treturn;\n\t\t}\n\t}\n}\nvoid Hash_RemoveData(hashtable_t *table, const char *name, void *data)\n{\n\tunsigned int bucknum = Hash_Key(name, table->numbuckets);\n\tbucket_t **link, *buck;\t\n\n\tfor (link = &table->bucket[bucknum]; *link; link = &(*link)->next)\n\t{\n\t\tbuck = *link;\n\t\tif (buck->data == data && !stricmp(name, buck->key.string))\n\t\t{\n\t\t\t*link = buck->next;\n\t\t\treturn;\n\t\t}\n\t}\n}\nvoid Hash_RemoveBucket(hashtable_t *table, const char *name, bucket_t *data)\n{\n\tunsigned int bucknum = Hash_Key(name, table->numbuckets);\n\tbucket_t **link, *buck;\t\n\n\tfor (link = &table->bucket[bucknum]; *link; link = &(*link)->next)\n\t{\n\t\tbuck = *link;\n\t\tif (buck == data && !stricmp(name, buck->key.string))\n\t\t{\n\t\t\t*link = buck->next;\n\t\t\treturn;\n\t\t}\n\t}\n\treturn;\n}\n\nvoid Hash_RemoveDataKey(hashtable_t *table, unsigned int key, void *data)\n{\n\tunsigned int bucknum = key%table->numbuckets;\n\tbucket_t **link, *buck;\n\n\tfor (link = &table->bucket[bucknum]; *link; link = &(*link)->next)\n\t{\n\t\tbuck = *link;\n\t\tif (buck->data == data && buck->key.value == key)\n\t\t{\n\t\t\t*link = buck->next;\n\t\t\treturn;\n\t\t}\n\t}\n}\nvoid Hash_RemoveKey(hashtable_t *table, unsigned int key)\n{\n\tunsigned int bucknum = key%table->numbuckets;\n\tbucket_t *buck;\t\n\n\tbuck = table->bucket[bucknum];\n\n\tif (buck->key.value == key)\n\t{\n\t\ttable->bucket[bucknum] = buck->next;\n\t\treturn;\n\t}\n\n\n\twhile(buck->next)\n\t{\n\t\tif (buck->next->key.value == key)\n\t\t{\n\t\t\tbuck->next = buck->next->next;\n\t\t\treturn;\n\t\t}\n\n\t\tbuck = buck->next;\n\t}\n\treturn;\n}\n"
  },
  {
    "path": "engine/qclib/hash.h",
    "content": "//=============================\n//David's hash tables\n//string based.\n\n#ifndef HASH_H__\n#define HASH_H__\n\n#define Hash_BytesForBuckets(b) (sizeof(bucket_t*)*(b))\n\n#define STRCMP(s1,s2) (((*s1)!=(*s2)) || strcmp(s1,s2))\t//saves about 2-6 out of 120 - expansion of idea from fastqcc\ntypedef struct bucket_s {\n\tvoid *data;\n\tunion {\n\t\tconst char *string;\n\t\tunsigned int value;\n\t} key;\n\tstruct bucket_s *next;\n} bucket_t;\ntypedef struct hashtable_s {\n\tunsigned int numbuckets;\n\tbucket_t **bucket;\n} hashtable_t;\n\nvoid Hash_InitTable(hashtable_t *table, unsigned int numbucks, void *mem);\t//mem must be 0 filled. (memset(mem, 0, size))\nvoid *Hash_Enumerate(hashtable_t *table, void (*callback) (void *ctx, void *data), void *ctx);\nunsigned int Hash_Key(const char *name, unsigned int modulus);\nvoid *Hash_GetIdx(hashtable_t *table, unsigned int idx);\nvoid *Hash_Get(hashtable_t *table, const char *name);\nvoid *Hash_GetInsensitive(hashtable_t *table, const char *name);\nvoid *Hash_GetInsensitiveBucket(hashtable_t *table, const char *name);\nvoid *Hash_GetKey(hashtable_t *table, unsigned int key);\nvoid *Hash_GetNext(hashtable_t *table, const char *name, void *old);\nvoid *Hash_GetNextInsensitive(hashtable_t *table, const char *name, void *old);\nvoid *Hash_GetNextKey(hashtable_t *table, unsigned int key, void *old);\nvoid *Hash_Add(hashtable_t *table, const char *name, void *data, bucket_t *buck);\nvoid *Hash_AddInsensitive(hashtable_t *table, const char *name, void *data, bucket_t *buck);\nvoid Hash_Remove(hashtable_t *table, const char *name);\nvoid Hash_RemoveData(hashtable_t *table, const char *name, void *data);\nvoid Hash_RemoveDataInsensitive(hashtable_t *table, const char *name, void *data);\nvoid Hash_RemoveBucket(hashtable_t *table, const char *name, bucket_t *data);\nvoid Hash_RemoveKey(hashtable_t *table, unsigned int key);\nvoid Hash_RemoveDataKey(hashtable_t *table, unsigned int key, void *data);\nvoid *Hash_AddKey(hashtable_t *table, unsigned int key, void *data, bucket_t *buck);\n\n#endif\n"
  },
  {
    "path": "engine/qclib/initlib.c",
    "content": "#define PROGSUSED\n#include \"progsint.h\"\n#include <stdlib.h>\n\nstatic void PR_FreeAllTemps\t\t\t(progfuncs_t *progfuncs);\n\ntypedef struct prmemb_s {\n\tstruct prmemb_s *prev;\n\tint level;\n} prmemb_t;\nvoid *PRHunkAlloc(progfuncs_t *progfuncs, int ammount, const char *name)\n{\n\tprmemb_t *mem;\n\tammount = sizeof(prmemb_t)+((ammount + 3)&~3);\n\tmem = progfuncs->funcs.parms->memalloc(ammount); \n\tmemset(mem, 0, ammount);\n\tmem->prev = prinst.memblocks;\n\tif (!prinst.memblocks)\n\t\tmem->level = 1;\n\telse\n\t\tmem->level = ((prmemb_t *)prinst.memblocks)->level+1;\n\tprinst.memblocks = mem;\n\n\treturn ((char *)mem)+sizeof(prmemb_t);\n}\nstatic void *PDECL QC_HunkAlloc(pubprogfuncs_t *ppf, int ammount, char *name)\n{\n\treturn PRHunkAlloc((progfuncs_t*)ppf, ammount, name);\n}\n\nint PRHunkMark(progfuncs_t *progfuncs)\n{\n\treturn ((prmemb_t *)prinst.memblocks)->level;\n}\nvoid PRHunkFree(progfuncs_t *progfuncs, int mark)\n{\n\tprmemb_t *omem;\n\twhile(prinst.memblocks)\n\t{\n\t\tif (prinst.memblocks->level <= mark)\n\t\t\treturn;\n\n\t\tomem = prinst.memblocks;\n\t\tprinst.memblocks = prinst.memblocks->prev;\n\t\texterns->memfree(omem);\n\t}\n\treturn;\n}\n\n/*if we ran out of memory, the vm can allocate a new block, but doing so requires fixing up all sorts of pointers*/\nstatic void PRAddressableRelocate(progfuncs_t *progfuncs, char *oldb, char *newb, int oldlen)\n{\n\tunsigned int i;\n\tedictrun_t *e;\n\tfor (i=0 ; i<prinst.maxedicts; i++)\n\t{\n\t\te = (edictrun_t *)(prinst.edicttable[i]);\n\t\tif (e && (char*)e->fields >= oldb && (char*)e->fields < oldb+oldlen)\n\t\t\te->fields = ((char*)e->fields - oldb) + newb;\n\t}\n\n\tif (progfuncs->funcs.stringtable >= oldb && progfuncs->funcs.stringtable < oldb+oldlen)\n\t\tprogfuncs->funcs.stringtable = (progfuncs->funcs.stringtable - oldb) + newb;\n\n\tfor (i=0; i < prinst.maxprogs; i++)\n\t{\n\t\tif ((char*)prinst.progstate[i].globals >= oldb && (char*)prinst.progstate[i].globals < oldb+oldlen)\n\t\t\tprinst.progstate[i].globals = (float*)(((char*)prinst.progstate[i].globals - oldb) + newb);\n\t\tif (prinst.progstate[i].strings >= oldb && prinst.progstate[i].strings < oldb+oldlen)\n\t\t\tprinst.progstate[i].strings = (prinst.progstate[i].strings - oldb) + newb;\n\t}\n\n\tfor (i = 0; i < prinst.numfields; i++)\n\t{\n\t\tif (prinst.field[i].name >= oldb && prinst.field[i].name < oldb+oldlen)\n\t\t\tprinst.field[i].name = (prinst.field[i].name - oldb) + newb;\n\t}\n\n\texterns->addressablerelocated(&progfuncs->funcs, oldb, newb, oldlen);\n}\n\n//for 64bit systems. :)\n//addressable memory is memory available to the vm itself for writing.\n//once allocated, it cannot be freed for the lifetime of the VM.\n//if src is null, data srcsize is left uninitialised for speed.\n//pad is always 0-filled.\nvoid *PRAddressableExtend(progfuncs_t *progfuncs, void *src, size_t srcsize, int pad)\n{\n\tchar *ptr;\n\tint ammount = (srcsize+pad + 4)&~3;\t//round up to 4\n\tpad = ammount - srcsize;\n\tpad++;\t//make sure there's always a null, to allow strings to be a little more lazy.\n\tif (prinst.addressableused + ammount >= prinst.addressablesize)\n\t{\n\t\t/*only do this if the caller states that it can cope with addressable-block relocations/resizes*/\n\t\tif (externs->addressablerelocated)\n\t\t{\n#if defined(_WIN32) && !defined(WINRT)\n\t\t\tchar *newblock;\n\t\t#if 0//def _DEBUG\n\t\t\tint oldtot = addressablesize;\n\t\t#endif\n\t\t\tint newsize = (prinst.addressableused + ammount + 4096) & ~(4096-1);\n\t\t\tnewblock = VirtualAlloc (NULL, prinst.addressablesize, MEM_RESERVE, PAGE_NOACCESS);\n\t\t\tif (newblock)\n\t\t\t{\n\t\t\t\tVirtualAlloc (newblock, prinst.addressableused, MEM_COMMIT, PAGE_READWRITE);\n\t\t\t\tmemcpy(newblock, prinst.addressablehunk, prinst.addressableused);\n\t\t#if 0//def _DEBUG\n\t\t\t\tVirtualAlloc (prinst.addressablehunk, oldtot, MEM_RESERVE, PAGE_NOACCESS);\n\t\t#else\n\t\t\t\tVirtualFree (prinst.addressablehunk, 0, MEM_RELEASE);\n\t\t#endif\n\t\t\t\tPRAddressableRelocate(progfuncs, prinst.addressablehunk, newblock, prinst.addressableused);\n\t\t\t\tprinst.addressablehunk = newblock;\n\t\t\t\tprinst.addressablesize = newsize;\n\t\t\t}\n#else\n\t\t\tint newsize = (prinst.addressableused + ammount + 1024*1024) & ~(1024*1024-1);\n\t\t\tchar *newblock = malloc(newsize);\n\t\t\tif (newblock)\n\t\t\t{\n\t\t\t\tPRAddressableRelocate(progfuncs, prinst.addressablehunk, newblock, prinst.addressableused);\n\t\t\t\tfree(prinst.addressablehunk);\n\t\t\t\tprinst.addressablehunk = newblock;\n\t\t\t\tprinst.addressablesize = newsize;\n\t\t\t}\n#endif\n\t\t}\n\n\t\tif (prinst.addressableused + ammount >= prinst.addressablesize)\n\t\t\texterns->Sys_Error(\"Not enough addressable memory for progs VM (using %gmb)\", prinst.addressablesize/(1024.0*1024));\n\t}\n\n\tprinst.addressableused += ammount;\n\tprogfuncs->funcs.stringtablesize = prinst.addressableused;\n\n#if defined(_WIN32) && !defined(WINRT)\n\tif (!VirtualAlloc (prinst.addressablehunk, prinst.addressableused+1, MEM_COMMIT, PAGE_READWRITE))\n\t\texterns->Sys_Error(\"VirtualAlloc failed. Blame windows.\");\n#endif\n\n\tptr = &prinst.addressablehunk[prinst.addressableused-ammount];\n\tif (src)\n\t\tmemcpy(ptr, src, srcsize);\n#ifdef _DEBUG\n\telse\n\t\tmemset(ptr, 0xcc, srcsize);\n#endif\n\tmemset(ptr+srcsize, 0, pad);\n\treturn &prinst.addressablehunk[prinst.addressableused-ammount];\n}\n\n\n#define MARKER_USED 0xC2A4F5A6u\n#define MARKER_FREE 0xF1E3E3E7u\ntypedef struct\n{\n#ifdef _DEBUG\n\tunsigned int marker;\n#endif\n\tunsigned int next;\n\tunsigned int prev;\n\tunsigned int size;\t//includes header size\n} qcmemfreeblock_t;\ntypedef struct\n{\n\tunsigned int marker;\n#ifdef _DEBUG\n\tunsigned int next;\n\tunsigned int prev;\n#endif\n\tunsigned int size;\t//includes header size\n} qcmemusedblock_t;\nstatic void PF_fmem_unlink(progfuncs_t *progfuncs, qcmemfreeblock_t *p)\n{\n\tqcmemfreeblock_t *np;\n#ifdef _DEBUG\n\tif (p->marker != MARKER_FREE)\n\t{\n\t\texterns->Printf(\"PF_fmem_unlink: memory corruption\\n\");\n\t\tPR_StackTrace(&progfuncs->funcs, false);\n\t}\n\tp->marker = 0;\n#endif\n\tif (p->prev)\n\t{\n\t\tnp = (qcmemfreeblock_t*)(progfuncs->funcs.stringtable + p->prev);\n\t\tnp->next = p->next;\n\t}\n\telse\n\t\tprogfuncs->inst.mfreelist = p->next;\n\tif (p->next)\n\t{\n\t\tnp = (qcmemfreeblock_t*)(progfuncs->funcs.stringtable + p->next);\n\t\tnp->prev = p->prev;\n\t}\n}\n\nvoid PR_memvalidate (progfuncs_t *progfuncs)\n{\n\tqcmemfreeblock_t *p;\n\tunsigned int b,l;\n\n\tb = prinst.mfreelist;\n\tl = 0;\n\twhile (b)\n\t{\n\t\tif ((size_t)b >= (size_t)prinst.addressableused)\n\t\t{\n\t\t\texterns->Printf(\"PF_memalloc: memory corruption\\n\");\n\t\t\tPR_StackTrace(&progfuncs->funcs, false);\n\t\t\treturn;\n\t\t}\n\t\tp = (qcmemfreeblock_t*)(progfuncs->funcs.stringtable + b);\n\n\t\tif (\n#ifdef _DEBUG\n\t\t\tp->marker != MARKER_FREE ||\n#endif\n\t\t\tp->prev != l ||\n\t\t\t(p->next && p->next < b + p->size) ||\n\t\t\tp->next >= prinst.addressableused ||\n\t\t\tb + p->size >= prinst.addressableused ||\n\t\t\tp->prev >= b)\n\t\t{\n\t\t\texterns->Printf(\"PF_memalloc: memory corruption\\n\");\n\t\t\tPR_StackTrace(&progfuncs->funcs, false);\n\t\t\treturn;\n\t\t}\n\t\tl = b;\n\t\tb = p->next;\n\t}\n}\n\nstatic void *PDECL PR_memalloc (pubprogfuncs_t *ppf, unsigned int size)\n{\n\tprogfuncs_t *progfuncs = (progfuncs_t*)ppf;\n\tqcmemfreeblock_t *p, *np;\n\tqcmemusedblock_t *ub = NULL;\n\tunsigned int b,n;\n\t/*round size up*/\n\tsize = (size+sizeof(qcmemusedblock_t) + 63) & ~63;\n\n\tPR_memvalidate(progfuncs);\n\n\tb = prinst.mfreelist;\n\twhile (b)\n\t{\n\t\tif (/*b < 0 || */b+sizeof(qcmemfreeblock_t) >= prinst.addressableused)\n\t\t{\n\t\t\texterns->Printf(\"PF_memalloc: memory corruption\\n\");\n\t\t\tPR_StackTrace(&progfuncs->funcs, false);\n\t\t\treturn NULL;\n\t\t}\n\t\tp = (qcmemfreeblock_t*)(progfuncs->funcs.stringtable + b);\n\t\tif (p->size >= size)\n\t\t{\n\t\t\tif ((p->next && p->next < b + p->size) ||\n\t\t\t\tp->next >= prinst.addressableused ||\n\t\t\t\tb + p->size >= prinst.addressableused ||\n\t\t\t\tp->prev >= b)\n\t\t\t{\n\t\t\t\texterns->Printf(\"PF_memalloc: memory corruption\\n\");\n\t\t\t\tPR_StackTrace(&progfuncs->funcs, false);\n\t\t\t\treturn NULL;\n\t\t\t}\n\n\t\t\tub = (qcmemusedblock_t*)p;\n\t\t\tif (p->size > size + 63)\n\t\t\t{\n\t\t\t\t/*make a new header just after it, with basically the same properties, and shift the important fields over*/\n\t\t\t\tn = b + size;\n\t\t\t\tnp = (qcmemfreeblock_t*)(progfuncs->funcs.stringtable + b + size);\n#ifdef _DEBUG\n\t\t\t\tnp->marker = MARKER_FREE;\n#endif\n\t\t\t\tnp->prev = p->prev;\n\t\t\t\tnp->next = p->next;\n\t\t\t\tnp->size = p->size - size;\n\t\t\t\tif (np->prev)\n\t\t\t\t{\n\t\t\t\t\tp = (qcmemfreeblock_t*)(progfuncs->funcs.stringtable + np->prev);\n\t\t\t\t\tp->next = n;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tprinst.mfreelist = n;\n\t\t\t\tif (p->next)\n\t\t\t\t{\n\t\t\t\t\tp = (qcmemfreeblock_t*)(progfuncs->funcs.stringtable + np->next);\n\t\t\t\t\tp->prev = n;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tsize = p->size; /*alloc the entire block*/\n\t\t\t\t/*unlink this entry*/\n\t\t\t\tPF_fmem_unlink(progfuncs, p);\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t\tb = p->next;\n\t}\n\n\t/*assign more space*/\n\tif (!ub)\n\t{\n\t\tub = PRAddressableExtend(progfuncs, NULL, size, 0);\n\t\tif (!ub)\n\t\t{\n\t\t\texterns->Printf(\"PF_memalloc: memory exausted\\n\");\n\t\t\tPR_StackTrace(&progfuncs->funcs, false);\n\t\t\treturn NULL;\n\t\t}\n\t\t//FIXME: merge with previous block\n\t}\n\tmemset(ub, 0, size);\n\tub->marker = MARKER_USED;\n\tub->size = size;\n\n\tPR_memvalidate(progfuncs);\n\n\treturn ub+1;\n}\nstatic void PDECL PR_memfree (pubprogfuncs_t *ppf, void *memptr)\n{\n\tprogfuncs_t *progfuncs = (progfuncs_t*)ppf;\n\tqcmemusedblock_t *ub;\n\tqcmemfreeblock_t *b, *nb, *pb; \n\tunsigned int pa, na;\t//prev addr, next addr\n\tunsigned int size;\n\tunsigned int ptr = memptr?((char*)memptr - progfuncs->funcs.stringtable):0;\n\n\t/*freeing NULL is ignored*/\n\tif (!ptr)\n\t\treturn;\n\tPR_memvalidate(progfuncs);\n\tptr -= sizeof(qcmemusedblock_t);\n\tif (/*ptr < 0 ||*/ ptr >= prinst.addressableused)\n\t{\n\t\tptr += sizeof(qcmemusedblock_t);\n\t\tif (ptr < prinst.addressableused && !*(char*)memptr)\n\t\t{\n\t\t\t//the empty string is a point of contention. while we can detect it from fteqcc, its best to not give any special favours (other than nicer debugging, where possible)\n\t\t\t//we might not actually spot it from other qccs, so warning about it where possible is probably a very good thing.\n\t\t\texterns->Printf(\"PF_memfree: unable to free the non-null empty string constant at %x\\n\", ptr);\n\t\t}\n\t\telse\n\t\t\texterns->Printf(\"PF_memfree: pointer invalid - out of range (%x >= %x)\\n\", ptr, (unsigned int)prinst.addressableused);\n\t\tPR_StackTrace(&progfuncs->funcs, false);\n\t\treturn;\n\t}\n\n\t//this is the used block that we're trying to free\n\tub = (qcmemusedblock_t*)(progfuncs->funcs.stringtable + ptr);\n\tif (ub->marker != MARKER_USED || ub->size <= sizeof(*ub) || ptr + ub->size > (unsigned int)prinst.addressableused)\n\t{\n\t\texterns->Printf(\"PR_memfree: pointer lacks marker - double-freed?\\n\");\n\t\tPR_StackTrace(&progfuncs->funcs, false);\n\t\treturn;\n\t}\n\tub->marker = 0;\t//invalidate it\n\tsize = ub->size;\n\tub = NULL;\n\n\t//we have an (ordered) list of free blocks.\n\t//in order to free our memory, we need to find the free block before+after the 'new' block\n\tfor (na = prinst.mfreelist, pa = 0; ;)\n\t{\n\t\tif (/*na < 0 ||*/ na >= prinst.addressableused)\n\t\t{\n\t\t\texterns->Printf(\"PF_memfree: memory corruption\\n\");\n\t\t\tPR_StackTrace(&progfuncs->funcs, false);\n\t\t\treturn;\n\t\t}\n\t\tif (!na || na >= ptr)\n\t\t{\n\t\t\tpb = pa?(qcmemfreeblock_t*)(progfuncs->funcs.stringtable + pa):NULL;\n\t\t\tif (pb && pa+pb->size>ptr)\n\t\t\t{\t//previous free block extends into the block that we're trying to free.\n\t\t\t\texterns->Printf(\"PF_memfree: double free\\n\");\n\t\t\t\tPR_StackTrace(&progfuncs->funcs, false);\n\t\t\t\treturn;\n\t\t\t}\n#ifdef _DEBUG\n\t\t\tif (pb && pb->marker != MARKER_FREE)\n\t\t\t{\n\t\t\t\texterns->Printf(\"PF_memfree: use-after-free?\\n\");\n\t\t\t\tPR_StackTrace(&progfuncs->funcs, false);\n\t\t\t\treturn;\n\t\t\t}\n#endif\n\n\t\t\tnb = na?(qcmemfreeblock_t*)(progfuncs->funcs.stringtable + na):NULL;\n\t\t\tif (nb && ptr+size > na)\n\t\t\t{\n\t\t\t\texterns->Printf(\"PF_memfree: block extends into neighbour\\n\");\n\t\t\t\tPR_StackTrace(&progfuncs->funcs, false);\n\t\t\t\treturn;\n\t\t\t}\n#ifdef _DEBUG\n\t\t\tif (nb && nb->marker != MARKER_FREE)\n\t\t\t{\n\t\t\t\texterns->Printf(\"PF_memfree: use-after-free?\\n\");\n\t\t\t\tPR_StackTrace(&progfuncs->funcs, false);\n\t\t\t\treturn;\n\t\t\t}\n#endif\n\n\t\t\t/*generate the free block, now we know its proper values*/\n\t\t\tb = (qcmemfreeblock_t*)(progfuncs->funcs.stringtable + ptr);\n#ifdef _DEBUG\n\t\t\tb->marker = MARKER_FREE;\n#endif\n\t\t\tb->prev = pa;\n\t\t\tb->next = na;\n\t\t\tb->size = size;\n\t\t\tif (na)\n\t\t\t\tnb->prev = ptr;\n\t\t\tif (!pa)\n\t\t\t\tprinst.mfreelist = ptr;\n\t\t\telse\n\t\t\t\tpb->next = ptr;\n\n\t\t\t/*extend this block and kill the next if they are adjacent*/\n\t\t\tif (na && b->next == ptr + size)\n\t\t\t{\n\t\t\t\tb->size += nb->size; \n\t\t\t\tPF_fmem_unlink(progfuncs, nb);\n\t\t\t}\n\t\t\t/*we're adjacent to the previous block, so merge them by killing the newly freed region*/\n\t\t\tif (pa && pa + pb->size == ptr)\n\t\t\t{\n\t\t\t\tpb->size += size;\n\t\t\t\tPF_fmem_unlink(progfuncs, b);\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\n\t\tpa = na;\n\t\tb = (qcmemfreeblock_t*)(progfuncs->funcs.stringtable + pa);\n\t\tna = b->next;\n\t}\n\n\tPR_memvalidate(progfuncs);\n}\n\nstatic void *PDECL PR_memrealloc (pubprogfuncs_t *ppf, void *memptr, unsigned int newsize)\n{\n\tprogfuncs_t *progfuncs = (progfuncs_t*)ppf;\n\tqcmemusedblock_t *ub;\n\tunsigned int ptr = memptr?((char*)memptr - progfuncs->funcs.stringtable):0;\n\tvoid *newptr;\n\tunsigned int oldsize;\n\n\t/*freeing NULL is ignored*/\n\tif (!ptr)\t//realloc instead of malloc is accepted.\n\t\treturn PR_memalloc(ppf, newsize);\n\tPR_memvalidate(progfuncs);\n\tptr -= sizeof(qcmemusedblock_t);\n\tif (/*ptr < 0 ||*/ ptr >= prinst.addressableused)\n\t{\n\t\tptr += sizeof(qcmemusedblock_t);\n\t\tif (ptr < prinst.addressableused && !*(char*)memptr)\n\t\t{\n\t\t\t//the empty string is a point of contention. while we can detect it from fteqcc, its best to not give any special favours (other than nicer debugging, where possible)\n\t\t\t//we might not actually spot it from other qccs, so warning about it where possible is probably a very good thing.\n\t\t\texterns->Printf(\"PR_memrealloc: unable to free the non-null empty string constant at %x\\n\", ptr);\n\t\t}\n\t\telse\n\t\t\texterns->Printf(\"PR_memrealloc: pointer invalid - out of range (%x >= %x)\\n\", ptr, (unsigned int)prinst.addressableused);\n\t\tPR_StackTrace(&progfuncs->funcs, false);\n\t\treturn NULL;\n\t}\n\n\t//this is the used block that we're trying to free\n\tub = (qcmemusedblock_t*)(progfuncs->funcs.stringtable + ptr);\n\tif (ub->marker != MARKER_USED || ub->size <= sizeof(*ub) || ptr + ub->size > (unsigned int)prinst.addressableused)\n\t{\n\t\texterns->Printf(\"PR_memrealloc: pointer lacks marker - double-freed?\\n\");\n\t\tPR_StackTrace(&progfuncs->funcs, false);\n\t\treturn NULL;\n\t}\n\toldsize = ub->size;\n\toldsize -= sizeof(qcmemusedblock_t);\t//ignore the header.\n\n\tnewptr = PR_memalloc(ppf, newsize);\n\tif (oldsize > newsize)\n\t\toldsize = newsize;\t\t//don't copy it all.\n\tmemcpy(newptr, memptr, oldsize);\n\tnewsize -= oldsize;\n\tmemset((char*)newptr+oldsize, 0, newsize);\t//clear out any extended part.\n\tPR_memfree(ppf, memptr);\t//free the old.\n\n\treturn newptr;\n}\n\nvoid PRAddressableFlush(progfuncs_t *progfuncs, size_t totalammount)\n{\n\tprinst.addressableused = 0;\n\tprinst.mfreelist = 0;\n\t// Clear localstack state when flushing memory\n\tprinst.localstack = NULL;\n\tprinst.localstack_used = 0;\n\tprinst.spushed = 0;\n\n\tif (totalammount <= 0)\t//flush\n\t{\n\t\ttotalammount = prinst.addressablesize;\n//\t\treturn;\n\t}\n\n#if defined(_WIN32) && !defined(WINRT)\n\tif (prinst.addressablehunk && prinst.addressablesize != totalammount)\n\t{\n\t\tVirtualFree(prinst.addressablehunk, 0, MEM_RELEASE);\t//doesn't this look complicated? :p\n\t\tprinst.addressablehunk = NULL;\n\t}\n\tif (!prinst.addressablehunk)\n\t\tprinst.addressablehunk = VirtualAlloc (prinst.addressablehunk, totalammount, MEM_RESERVE, PAGE_NOACCESS);\n#else\n\tif (prinst.addressablehunk && prinst.addressablesize != totalammount)\n\t{\n\t\tfree(prinst.addressablehunk);\n\t\tprinst.addressablehunk = NULL;\n\t}\n\tif (!prinst.addressablehunk)\n\t\tprinst.addressablehunk = malloc(totalammount);\t//linux will allocate-on-use anyway, which is handy.\n//\tmemset(prinst.addressablehunk, 0xff, totalammount);\n#endif\n\tif (!prinst.addressablehunk)\n\t\texterns->Sys_Error(\"Out of memory\\n\");\n\tprinst.addressablesize = totalammount;\n\tprogfuncs->funcs.stringtablemaxsize = totalammount;\n}\n\nint PDECL PR_InitEnts(pubprogfuncs_t *ppf, int max_ents)\n{\n\tprogfuncs_t *progfuncs = (progfuncs_t*)ppf;\n\tedictrun_t *e;\n\tprinst.maxedicts = max_ents;\n\n\tsv_num_edicts = 0;\n\n#if 0\n\t{\n\t\tint i;\n\t\tfor (i = 0; i < prinst.numfields; i++)\n\t\t{\n\t\t\texterns->Printf(\"%s(%i) %i -> %i\\n\", prinst.field[i].name, prinst.field[i].type, prinst.field[i].progsofs, prinst.field[i].ofs);\n\t\t}\n\t}\n#endif\n\n\tprinst.max_fields_size = prinst.fields_size;\n\n\tprinst.edicttable = (struct edictrun_s**)(progfuncs->funcs.edicttable = PRHunkAlloc(progfuncs, prinst.maxedicts*sizeof(struct edicts_s *), \"edicttable\"));\n\tprogfuncs->funcs.edicttable_length = prinst.maxedicts;\n\te = PRHunkAlloc(progfuncs, externs->edictsize, \"edict0\");\n\te->fieldsize = prinst.fields_size;\n\te->entnum = 0;\n\te->ereftype = ER_ENTITY;\n\tsv_edicts = (struct edict_s *)e;\n\tsv_num_edicts = 1;\n\tprogfuncs->funcs.edicttable[0] = sv_edicts;\n\te->fields = PRAddressableExtend(progfuncs, NULL, e->fieldsize, prinst.max_fields_size-e->fieldsize);\n\tQC_ClearEdict(&progfuncs->funcs, sv_edicts);\n\n\tif (externs->entspawn)\n\t\texterns->entspawn(sv_edicts, false);\n\n\treturn prinst.max_fields_size;\n}\nedictrun_t tempedict={ER_FREE};\t//used as a safty buffer\nstatic float tempedictfields[2048];\n\nstatic void PDECL PR_Configure (pubprogfuncs_t *ppf, size_t addressable_size, int max_progs, pbool profiling)\t//can be used to wipe all memory\n{\n\tprogfuncs_t *progfuncs = (progfuncs_t*)ppf;\n\tunsigned int i;\n\tedictrun_t *e;\n\n\tprinst.max_fields_size=0;\n\tprinst.fields_size = 0;\n\tprogfuncs->funcs.stringtable = 0;\n\tQC_StartShares(progfuncs);\n\tQC_InitShares(progfuncs);\n\n\tfor ( i=1 ; i<prinst.maxedicts; i++)\n\t{\n\t\te = (edictrun_t *)(prinst.edicttable[i]);\n\t\tprinst.edicttable[i] = NULL;\n//\t\te->entnum = i;\n\t\tif (e)\n\t\t\texterns->memfree(e);\n\t}\n\n\tPRHunkFree(progfuncs, 0);\t//clear mem - our hunk may not be a real hunk.\n\tif (addressable_size == (size_t)-1)\n\t{\n#if defined(_WIN64) && !defined(WINRT)\n\t\taddressable_size = 0x80000000;\t//use of virtual address space rather than physical memory means we can just go crazy and use the max of 2gb.\n#elif defined(FTE_TARGET_WEB)\n\t\taddressable_size = 8*1024*1024;\n#else\n\t\taddressable_size = 32*1024*1024;\n#endif\n\t}\n\tif (addressable_size > 0x80000000)\n\t\taddressable_size = 0x80000000;\n\tPRAddressableFlush(progfuncs, addressable_size);\t\n\tprogfuncs->funcs.stringtable = prinst.addressablehunk;\n\n\tpr_progstate = PRHunkAlloc(progfuncs, sizeof(progstate_t) * max_progs, \"progstatetable\");\n\n/*\t\tfor(a = 0; a < max_progs; a++)\n\t\t{\n\t\t\tpr_progstate[a].progs = NULL;\n\t\t}\t\t\n*/\n\t\t\n\tprinst.maxprogs = max_progs;\n\tprinst.pr_typecurrent=-1;\n\n\tPR_FreeAllTemps(progfuncs);\n\n\tprinst.reorganisefields = false;\n\n\tprinst.profiling = profiling;\n\tprinst.profilingalert = Sys_GetClockRate();\n\tprogfuncs->funcs.edicttable_length = prinst.maxedicts = 0;\n\tprinst.edicttable = (edictrun_t**)(progfuncs->funcs.edicttable = &sv_edicts);\n\tsv_num_edicts = 0;\t//set up a safty buffer so things won't go horribly wrong too often\n\tsv_edicts=(struct edict_s *)&tempedict;\n\ttempedict.readonly = true;\n\ttempedict.fields = tempedictfields;\n\ttempedict.ereftype = ER_OBJECT;\n}\n\n\n\nstatic struct globalvars_s *PDECL PR_globals (pubprogfuncs_t *ppf, progsnum_t pnum)\n{\n\tprogfuncs_t *progfuncs = (progfuncs_t*)ppf;\n\tif (pnum < 0)\n\t{\n\t\tif (!current_progstate)\n\t\t{\n\t\t\tstatic float fallback[RESERVED_OFS];\n\t\t\treturn (struct globalvars_s *)fallback;\t//err.. you've not loaded one yet.\n\t\t}\n\t\treturn (struct globalvars_s *)current_progstate->globals;\n\t}\n\treturn (struct globalvars_s *)pr_progstate[pnum].globals;\n}\n\nstatic struct entvars_s *PDECL PR_entvars (pubprogfuncs_t *ppf, struct edict_s *ed)\n{\n//\tprogfuncs_t *progfuncs = (progfuncs_t*)ppf;\n\tif (((edictrun_t *)ed)->ereftype != ER_ENTITY)\n\t\treturn NULL;\n\n\treturn (struct entvars_s *)edvars(ed);\n}\n\nstatic pbool PDECL PR_GetFunctionInfo(pubprogfuncs_t *ppf, func_t func, int *args, pbyte **argsizes, int *builtinnum, char *funcname, size_t funcnamesize)\n{\n\tprogfuncs_t *progfuncs = (progfuncs_t*)ppf;\n\n\tunsigned int pnum;\n\tunsigned int fnum;\n\tmfunction_t *f;\n\n\tpnum = (func & 0xff000000)>>24;\n\tfnum = (func & 0x00ffffff);\n\n\tif (pnum >= prinst.maxprogs || !pr_progstate[pnum].functions)\n\t\treturn false;\n\telse if (fnum >= pr_progstate[pnum].progs->numfunctions)\n\t\treturn false;\n\telse\n\t{\n\t\tf = pr_progstate[pnum].functions + fnum;\n\t\tif (args)\n\t\t\t*args = f->numparms;\n\t\tif (argsizes)\n\t\t\t*argsizes = f->parm_size;\n\t\tif (builtinnum)\n\t\t\t*builtinnum = -f->first_statement;\n\t\tif (funcname)\n\t\t{\n\t\t\tconst char *srcname = PR_StringToNative(ppf, f->s_name);\n\t\t\tsize_t nlen = strlen(srcname);\n\t\t\tif (nlen < funcnamesize)\n\t\t\t\tmemcpy(funcname, srcname, nlen+1);\n\t\t\telse\n\t\t\t\t*funcname = 0;\n\t\t}\n\t\treturn true;\n\t}\n}\n\nfunc_t PDECL PR_FindFunc(pubprogfuncs_t *ppf, const char *funcname, progsnum_t pnum)\n{\n\tprogfuncs_t *progfuncs = (progfuncs_t*)ppf;\n\tmfunction_t *f=NULL;\n\tif (pnum == PR_ANY)\n\t{\n\t\tfor (pnum = 0; (unsigned)pnum < prinst.maxprogs; pnum++)\n\t\t{\n\t\t\tif (!pr_progstate[pnum].progs)\n\t\t\t\tcontinue;\n\t\t\tf = ED_FindFunction(progfuncs, funcname, &pnum, pnum);\n\t\t\tif (f)\n\t\t\t\tbreak;\n\t\t}\n\t}\n\telse if (pnum == PR_ANYBACK)\t//run backwards\n\t{\n\t\tfor (pnum = prinst.maxprogs-1; pnum >= 0; pnum--)\n\t\t{\n\t\t\tif (!pr_progstate[pnum].progs)\n\t\t\t\tcontinue;\n\t\t\tf = ED_FindFunction(progfuncs, funcname, &pnum, pnum);\n\t\t\tif (f)\n\t\t\t\tbreak;\n\t\t}\n\t}\n\telse\n\t\tf = ED_FindFunction(progfuncs, funcname, &pnum, pnum);\n\tif (!f)\n\t\treturn 0;\n\n\t{\n\tddef16_t *var16;\n\tddef32_t *var32;\n\tprogstate_t *ps = &pr_progstate[pnum];\n\tswitch(ps->structtype)\n\t{\n\tcase PST_KKQWSV:\n\tcase PST_DEFAULT:\n\t\tvar16 = ED_FindTypeGlobalFromProgs16(progfuncs, ps, funcname, ev_function);\t//we must make sure we actually have a function def - 'light' is defined as a field before it is defined as a function.\n\t\tif (!var16)\n\t\t\treturn (f - ps->functions) | (pnum << 24);\n\t\treturn *(int *)&ps->globals[var16->ofs];\n\tcase PST_QTEST:\n\tcase PST_FTE32:\n\tcase PST_UHEXEN2:\n\t\tvar32 = ED_FindTypeGlobalFromProgs32(progfuncs, ps, funcname, ev_function);\t//we must make sure we actually have a function def - 'light' is defined as a field before it is defined as a function.\n\t\tif (!var32)\n\t\t\treturn (f - ps->functions) | (pnum << 24);\n\t\treturn *(int *)&ps->globals[var32->ofs];\n\t}\n\texterns->Sys_Error(\"Error with def size (PR_FindFunc)\");\n\t}\n\treturn 0;\n}\n\nstatic void PDECL QC_FindPrefixedGlobals(pubprogfuncs_t *ppf, int pnum, char *prefix, void (PDECL *found) (pubprogfuncs_t *progfuncs, char *name, union eval_s *val, etype_t type, void *ctx), void *ctx)\n{\n\tprogfuncs_t *progfuncs = (progfuncs_t*)ppf;\n\tunsigned int i;\n\tddef16_t\t\t*def16;\n\tddef32_t\t\t*def32;\n\tint len = strlen(prefix);\n\n\tif (pnum == PR_CURRENT)\n\t\tpnum = prinst.pr_typecurrent;\n\tif (pnum == PR_ANY)\n\t{\n\t\tfor (pnum = 0; (unsigned)pnum < prinst.maxprogs; pnum++)\n\t\t{\n\t\t\tif (!pr_progstate[pnum].progs)\n\t\t\t\tcontinue;\n\t\t\tQC_FindPrefixedGlobals(ppf, pnum, prefix, found, ctx);\n\t\t}\n\t\treturn;\n\t}\n\n\tif (!pr_progstate[pnum].progs)\n\t\treturn;\n\n\tswitch(pr_progstate[pnum].structtype)\n\t{\n\tcase PST_DEFAULT:\n\tcase PST_KKQWSV:\n\t\tfor (i=1 ; i<pr_progstate[pnum].progs->numglobaldefs ; i++)\n\t\t{\n\t\t\tdef16 = &pr_progstate[pnum].globaldefs16[i];\n\t\t\tif (!strncmp(def16->s_name+progfuncs->funcs.stringtable,prefix, len))\n\t\t\t\tfound(&progfuncs->funcs, def16->s_name+progfuncs->funcs.stringtable, (eval_t *)&pr_progstate[pnum].globals[def16->ofs], def16->type, ctx);\n\t\t}\n\t\tbreak;\n\tcase PST_QTEST:\n\tcase PST_FTE32:\n\tcase PST_UHEXEN2:\n\t\tfor (i=1 ; i<pr_progstate[pnum].progs->numglobaldefs ; i++)\n\t\t{\n\t\t\tdef32 = &pr_progstate[pnum].globaldefs32[i];\n\t\t\tif (!strncmp(def32->s_name+progfuncs->funcs.stringtable,prefix, len))\n\t\t\t\tfound(&progfuncs->funcs, def32->s_name+progfuncs->funcs.stringtable, (eval_t *)&pr_progstate[pnum].globals[def32->ofs], def32->type, ctx);\n\t\t}\n\t\tbreak;\n\t}\n}\nstatic pbool\tPDECL PR_FindBuiltins\t(pubprogfuncs_t *ppf, progsnum_t prnum, int binum, pbool (PDECL *found) (pubprogfuncs_t *progfuncs, const char *name, void *ctx), void *ctx)\t//calls the callback for each function reference that's mapped to the specified builtin number.\n{\n\tprogfuncs_t *progfuncs = (progfuncs_t*)ppf;\n\tmfunction_t\t\t*func;\n\tunsigned int\t\t\t\ti;\n\n\tif ((unsigned)prnum > (unsigned)prinst.maxprogs)\n\t{\n\t\texterns->Printf(\"Progsnum %\"pPRIi\" out of bounds\\n\", prnum);\n\t\treturn false;\n\t}\n\n\tif (!pr_progstate[prnum].progs)\n\t\treturn false;\n\n\tif (binum < 0)\n\t\treturn false;\t//invalid\n\tbinum = -binum;\n\n\tfor (i=1 ; i<pr_progstate[prnum].progs->numfunctions ; i++)\n\t{\n\t\tfunc = &pr_progstate[prnum].functions[i];\n\t\tif (func->first_statement == binum)\n\t\t\tif (!found(ppf, PR_StringToNative(ppf, func->s_name), ctx))\n\t\t\t\treturn false;\n\t}\n\treturn true;\n}\n\neval_t *PDECL PR_FindGlobal(pubprogfuncs_t *ppf, const char *globname, progsnum_t pnum, etype_t *type)\n{\n\tprogfuncs_t *progfuncs = (progfuncs_t*)ppf;\n\tunsigned int i;\n\tddef16_t *var16;\n\tddef32_t *var32;\n\tprogstate_t *cp;\n\tif (type)\n\t\t*type = ev_void;\n\tif (pnum == PR_CURRENT && current_progstate)\n\t\tcp = current_progstate;\n\telse if (pnum == PR_ANY)\n\t{\n\t\teval_t *ev;\n\t\tfor (i = 0; i < prinst.maxprogs; i++)\n\t\t{\n\t\t\tif (!pr_progstate[i].progs)\n\t\t\t\tcontinue;\n\t\t\tev = PR_FindGlobal(&progfuncs->funcs, globname, i, type);\n\t\t\tif (ev)\n\t\t\t\treturn ev;\n\t\t}\n\t\treturn NULL;\n\t}\n\telse if (pnum >= 0 && (unsigned)pnum < prinst.maxprogs && pr_progstate[pnum].progs)\n\t\tcp = &pr_progstate[pnum];\n\telse\n\t\treturn NULL;\n\tswitch(cp->structtype)\n\t{\n\tcase PST_DEFAULT:\n\tcase PST_KKQWSV:\n\t\tif (!(var16 = ED_FindGlobalFromProgs16(progfuncs, cp, globname)))\n\t\t\treturn NULL;\n\n\t\tif (type)\n\t\t\t*type = var16->type;\n\t\treturn (eval_t *)&cp->globals[var16->ofs];\n\tcase PST_QTEST:\n\tcase PST_FTE32:\n\tcase PST_UHEXEN2:\n\t\tif (!(var32 = ED_FindGlobalFromProgs32(progfuncs, cp, globname)))\n\t\t\treturn NULL;\n\n\t\tif (type)\n\t\t\t*type = var32->type;\n\t\treturn (eval_t *)&cp->globals[var32->ofs];\n\t}\n\texterns->Sys_Error(\"Error with def size (PR_FindGlobal)\");\n\treturn NULL;\n}\n\nstatic char *PDECL PR_VarString (pubprogfuncs_t *ppf, int\tfirst)\n{\n\tprogfuncs_t *progfuncs = (progfuncs_t*)ppf;\n\tint\t\ti;\n\tstatic char out[1024];\n\tconst char *s;\n\n\tout[0] = 0;\n\tfor (i=first ; i<progfuncs->funcs.callargc ; i++)\n\t{\n\t\ts = PR_StringToNative(ppf, G_STRING(OFS_PARM0+i*3));\n\t\tif (s)\n\t\t{\n\t\t\tif (strlen(out) + strlen(s) + 1 >= sizeof(out))\n\t\t\t\treturn out;\n\t\t\tstrcat (out, s);\n\t\t}\n\t}\n\treturn out;\n}\n\nstatic int PDECL PR_QueryField (pubprogfuncs_t *ppf, unsigned int fieldoffset, etype_t *type, char const**name, evalc_t *fieldcache)\n{\n\tprogfuncs_t *progfuncs = (progfuncs_t*)ppf;\n\tfdef_t *var;\n\tvar = ED_FieldAtOfs(progfuncs, fieldoffset);\n\tif (!var)\n\t\treturn false;\n\n\tif (type)\n\t\t*type = var->type & ~(DEF_SAVEGLOBAL|DEF_SHARED);\n\tif (name)\n\t\t*name = var->name;\n\tif (fieldcache)\n\t{\n\t\tfieldcache->ofs32 = var;\n\t\tfieldcache->varname = var->name;\n\t}\n\t\t\n\treturn true;\n}\n\neval_t *PDECL QC_GetEdictFieldValue(pubprogfuncs_t *ppf, struct edict_s *ed, const char *name, etype_t type, evalc_t *cache)\n{\n\tprogfuncs_t *progfuncs = (progfuncs_t*)ppf;\n\tfdef_t *var;\n\tif (!cache)\n\t{\n\t\tvar = ED_FindField(progfuncs, name);\n\t\tif (!var || (var->type != type && type))\n\t\t\treturn NULL;\n\t\treturn (eval_t *) &(((int*)(((edictrun_t*)ed)->fields))[var->ofs]);\n\t}\n\tif (!cache->varname)\n\t{\n\t\tcache->varname = name;\n\t\tvar = ED_FindField(progfuncs, name);\t\t\n\t\tif (!var || (var->type != type && type))\n\t\t{\n\t\t\tcache->ofs32 = NULL;\n\t\t\treturn NULL;\n\t\t}\n\t\tcache->ofs32 = var;\n\t\tcache->varname = var->name;\n\t\tif (!ed)\n\t\t\treturn (void*)~0;\t//something not null\n\t\treturn (eval_t *) &(((int*)(((edictrun_t*)ed)->fields))[var->ofs]);\n\t}\n\tif (cache->ofs32 == NULL)\n\t\treturn NULL;\n\treturn (eval_t *) &(((int*)(((edictrun_t*)ed)->fields))[cache->ofs32->ofs]);\n}\n\nstatic struct edict_s *PDECL ProgsToEdict (pubprogfuncs_t *ppf, int progs)\n{\n\tprogfuncs_t *progfuncs = (progfuncs_t*)ppf;\n\tif ((unsigned)progs >= (unsigned)prinst.maxedicts)\n\t{\n\t\texterns->Printf(\"Bad entity index %i\\n\", progs);\n\t\tif (prinst.pr_depth)\n\t\t{\n\t\t\tPR_StackTrace (ppf, false);\n//\t\t\tprogfuncs->funcs.pr_trace += 1;\n\t\t}\n\t\tprogs = 0;\n\t}\n\treturn (struct edict_s *)PROG_TO_EDICT_PB(progfuncs.inst, progs);\n}\nstatic int PDECL EdictToProgs (pubprogfuncs_t *ppf, struct edict_s *ed)\n{\n//\tprogfuncs_t *progfuncs = (progfuncs_t*)ppf;\n\treturn EDICT_TO_PROG(progfuncs, ed);\n}\n\nstring_t PDECL PR_StringToProgs\t\t\t(pubprogfuncs_t *ppf, const char *str)\n{\n\tprogfuncs_t *progfuncs = (progfuncs_t*)ppf;\n\tchar **ntable;\n\tint i, free=-1;\n\n\tif (!str)\n\t\treturn 0;\n\n\tif (str >= progfuncs->funcs.stringtable && str < progfuncs->funcs.stringtable + prinst.addressableused)\n\t\treturn str - progfuncs->funcs.stringtable;\n\n\tfor (i = prinst.numallocedstrings-1; i >= 0; i--)\n\t{\n\t\tif (prinst.allocedstrings[i] == str)\n\t\t\treturn (string_t)((unsigned int)i | STRING_STATIC);\n\t\tif (!prinst.allocedstrings[i])\n\t\t\tfree = i;\n\t}\n\n\tif (free != -1)\n\t{\n\t\ti = free;\n\t\tprinst.allocedstrings[i] = (char*)str;\n\t\treturn (string_t)((unsigned int)i | STRING_STATIC);\n\t}\n\tif (prinst.numallocedstrings < prinst.maxallocedstrings)\n\t{\n\t\ti = prinst.numallocedstrings++;\n\t\tprinst.allocedstrings[i] = (char*)str;\n\t\treturn (string_t)((unsigned int)i | STRING_STATIC);\n\t}\n\n\tprinst.maxallocedstrings += 1024;\n\tntable = progfuncs->funcs.parms->memalloc(sizeof(char*) * prinst.maxallocedstrings); \n\tmemcpy(ntable, prinst.allocedstrings, sizeof(char*) * prinst.numallocedstrings);\n\tmemset(ntable + prinst.numallocedstrings, 0, sizeof(char*) * (prinst.maxallocedstrings - prinst.numallocedstrings));\n\tif (prinst.allocedstrings)\n\t\tprogfuncs->funcs.parms->memfree(prinst.allocedstrings);\n\tprinst.allocedstrings = ntable;\n\n\ti = prinst.numallocedstrings++;\n\tprinst.allocedstrings[i] = (char*)str;\n\treturn (string_t)((unsigned int)i | STRING_STATIC);\n}\n//if ed is null, fld points to a global. if str_is_static, then s doesn't need its own memory allocated.\nstatic void PDECL PR_SetStringField(pubprogfuncs_t *progfuncs, struct edict_s *ed, string_t *fld, const char *str, pbool str_is_static)\n{\n\tif (!str)\n\t\t*fld = 0;\n\telse\n\t{\n#ifdef QCGC\n\t\t*fld = PR_AllocTempString(progfuncs, str);\n#else\n\t\tif (!str_is_static)\n\t\t\tstr = PR_AddString(progfuncs, str, 0, false);\n\t\t*fld = PR_StringToProgs(progfuncs, str);\n#endif\n\t}\n}\n\nstatic char *PDECL PR_RemoveProgsString\t\t\t\t(pubprogfuncs_t *ppf, string_t str)\n{\n\tprogfuncs_t *progfuncs = (progfuncs_t*)ppf;\n\tchar *ret;\n\n\t//input string is expected to be an allocated string\n\t//if its a temp, or a constant, just return NULL.\n\tif (((unsigned int)str & STRING_SPECMASK) == STRING_STATIC)\n\t{\n\t\tint i = str & ~STRING_SPECMASK;\n\t\tif (i >= prinst.numallocedstrings)\n\t\t{\n\t\t\tPR_RunWarning(&progfuncs->funcs, \"invalid static string %x\\n\", str);\n\t\t\treturn NULL;\n\t\t}\n\t\tif (prinst.allocedstrings[i])\n\t\t{\n\t\t\tret = prinst.allocedstrings[i];\n\t\t\tprinst.allocedstrings[i] = NULL;\t//remove it\n\t\t\treturn ret;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tPR_RunWarning(&progfuncs->funcs, \"invalid static string %x (already free)\\n\", str);\n\t\t\treturn NULL;\t//urm, was freed...\n\t\t}\n\t}\n\tPR_RunWarning(&progfuncs->funcs, \"invalid static string %x\\n\", str);\n\treturn NULL;\n}\n\nconst char *ASMCALL PR_StringToNative\t\t\t\t(pubprogfuncs_t *ppf, string_t str)\n{\n\tprogfuncs_t *progfuncs = (progfuncs_t*)ppf;\n\tif (((unsigned int)str & STRING_SPECMASK) == STRING_STATIC)\n\t{\n\t\tint i = str & ~STRING_SPECMASK;\n\t\tif (i >= prinst.numallocedstrings)\n\t\t{\t\n\t\t\tif (!progfuncs->funcs.debug_trace)\t//don't spam this\n\t\t\t\tPR_RunWarning(&progfuncs->funcs, \"invalid static string %x\\n\", str);\n\t\t\treturn \"\";\n\t\t}\n\t\tif (prinst.allocedstrings[i])\n\t\t\treturn prinst.allocedstrings[i];\n\t\telse\n\t\t{\n\t\t\tif (!progfuncs->funcs.debug_trace)\n\t\t\t\tPR_RunWarning(&progfuncs->funcs, \"invalid static string %x\\n\", str);\n\t\t\treturn \"\";\t//urm, was freed...\n\t\t}\n\t}\n\tif (((unsigned int)str & STRING_SPECMASK) == STRING_TEMP)\n\t{\n\t\tunsigned int i = str & ~STRING_SPECMASK;\n\t\ttempstr_t *ts;\n\t\tif (i >= prinst.maxtempstrings || !(ts=prinst.tempstrings[i]))\n\t\t{\n\t\t\tif (!progfuncs->funcs.debug_trace)\n\t\t\t\tPR_RunWarning(&progfuncs->funcs, \"invalid temp string %x\\n\", str);\n\t\t\treturn \"\";\n\t\t}\n\t\treturn ts->value;\n\t}\n\n\tif ((unsigned int)str >= (unsigned int)prinst.addressableused)\n\t{\n\t\tif (!progfuncs->funcs.debug_trace)\n\t\t\tPR_RunWarning(&progfuncs->funcs, \"invalid string offset %x\\n\", str);\n\t\treturn \"\";\n\t}\n\treturn progfuncs->funcs.stringtable + str;\n}\n\n//guarentees a return value for tempstrings, but requires a small enough data size to do so.\neval_t *PR_GetReadTempStringPtr(progfuncs_t *progfuncs, string_t str, size_t offset, size_t datasize)\n{\n\tstatic eval_t dummy;\t//don't resize anything when reading.\n\tif (((unsigned int)str & STRING_SPECMASK) == STRING_TEMP)\n\t{\n\t\tunsigned int i = str & ~STRING_SPECMASK;\n\t\ttempstr_t *temp;\n\t\tif (i < prinst.maxtempstrings && (temp=prinst.tempstrings[i]))\n\t\t{\n\t\t\tif (offset + datasize <= temp->size)\n\t\t\t\treturn (eval_t*)(temp->value + offset);\n\t\t\telse if (datasize <= sizeof(dummy))\n\t\t\t\treturn &dummy;\n\t\t}\n\t}\n\treturn NULL;\n}\neval_t *PR_GetWriteTempStringPtr(progfuncs_t *progfuncs, string_t str, size_t offset, size_t datasize)\n{\n\tif (((unsigned int)str & STRING_SPECMASK) == STRING_TEMP)\n\t{\n\t\tunsigned int i = str & ~STRING_SPECMASK;\n\t\ttempstr_t *temp;\n\t\tif (i < prinst.maxtempstrings && (temp=prinst.tempstrings[i]))\n\t\t{\n\t\t\tif (offset + datasize >= temp->size)\n\t\t\t{\t//access is beyond the current size. expand it.\n\t\t\t\tunsigned int newsize;\n\t\t\t\ttempstr_t *newtemp;\n\t\t\t\tnewsize = offset + datasize;\n\t\t\t\tif (newsize > (1u<<20u))\n\t\t\t\t\treturn NULL;\t//gotta have a cut-off point somewhere.\n\t\t\t\tnewsize = (newsize+sizeof(float)-1)&~(sizeof(float)-1);\n\t\t\t\tnewtemp = progfuncs->funcs.parms->memalloc(sizeof(tempstr_t) - sizeof(((tempstr_t*)NULL)->value) + newsize);\n\t\t\t\tif (!newtemp)\n\t\t\t\t\treturn NULL;\n\t\t\t\tnewtemp->size = newsize;\n\t\t\t\tmemcpy(newtemp->value, temp->value, temp->size);\n\t\t\t\tmemset(newtemp->value+temp->size, 0, newsize-temp->size);\n\t\t\t\tprogfuncs->funcs.parms->memfree(temp);\n\t\t\t\tprinst.tempstrings[i] = temp = newtemp;\n\t\t\t}\n\t\t\treturn (eval_t*)(temp->value + offset);\n\t\t}\n\t}\n\treturn NULL;\n}\n\n//returns null for invalid accesses. WARNING: invalidates any other pointers to the same tempstring so use this before getting any read pointers (or strings!).\nvoid *PR_PointerToNative_Resize(pubprogfuncs_t *inst, pint_t ptr, size_t offset, size_t datasize)\n{\n\tprogfuncs_t *progfuncs = (progfuncs_t*)inst;\n\tif (((unsigned int)ptr & STRING_SPECMASK) == STRING_TEMP)\n\t{\t//buffer. these auto-upsize.\n\t\tunsigned int i = ptr & ~STRING_SPECMASK;\n\t\ttempstr_t *temp;\n\t\tif (i < prinst.maxtempstrings && (temp=prinst.tempstrings[i]))\n\t\t{\n\t\t\tif (datasize > temp->size || offset >= temp->size-datasize)\n\t\t\t{\t//access is beyond the current size. expand it.\n\t\t\t\tunsigned int newsize;\n\t\t\t\ttempstr_t *newtemp;\n\t\t\t\tnewsize = offset + datasize;\n\t\t\t\tif (newsize > (1u<<20u))\n\t\t\t\t\treturn NULL;\t//gotta have a cut-off point somewhere.\n\t\t\t\tnewsize = (newsize+sizeof(float)-1)&~(sizeof(float)-1);\n\t\t\t\tnewtemp = progfuncs->funcs.parms->memalloc(sizeof(tempstr_t) - sizeof(((tempstr_t*)NULL)->value) + newsize);\n\t\t\t\tif (!newtemp)\n\t\t\t\t\treturn NULL;\t//erk!\n\t\t\t\tnewtemp->size = newsize;\n\t\t\t\tmemcpy(newtemp->value, temp->value, temp->size);\n\t\t\t\tmemset(newtemp->value+temp->size, 0, newsize-temp->size);\n\t\t\t\tprogfuncs->funcs.parms->memfree(temp);\n\t\t\t\tprinst.tempstrings[i] = temp = newtemp;\n\t\t\t}\n\t\t\treturn (eval_t*)(temp->value + offset);\n\t\t}\n\t\treturn NULL;\t//nothing not allocated.\n\t}\n\telse\n\t{\t//regular pointer\n\t\toffset += ptr;\n\t\tif (datasize > inst->stringtablesize || offset >= inst->stringtablesize-datasize || !offset)\n\t\t\treturn NULL;\t//can't autoresize these. just fail.\n\t\treturn inst->stringtable + ptr;\n\t}\n\treturn NULL;\n}\nvoid *PR_PointerToNative_MoInvalidate(pubprogfuncs_t *inst, pint_t ptr, size_t offset, size_t datasize)\n{\n\tprogfuncs_t *progfuncs = (progfuncs_t*)inst;\n\tif (((unsigned int)ptr & STRING_SPECMASK) == STRING_TEMP)\n\t{\t//buffer. these auto-upsize.\n\t\tunsigned int i = ptr & ~STRING_SPECMASK;\n\t\ttempstr_t *temp;\n\t\tif (i < prinst.maxtempstrings && (temp=prinst.tempstrings[i]))\n\t\t{\n\t\t\tif (datasize > temp->size || offset >= temp->size-datasize)\n\t\t\t{\t//access is beyond the current size. we're not allowed to break any other pointers though, so just fail and let the caller handle that.\n\t\t\t\treturn NULL;\t//gotta have a cut-off point somewhere.\n\t\t\t}\n\t\t\treturn (eval_t*)(temp->value + offset);\n\t\t}\n\t\treturn NULL;\t//nothing not allocated.\n\t}\n\telse\n\t{\t//regular pointer\n\t\toffset += ptr;\n\t\tif (datasize > inst->stringtablesize || offset >= inst->stringtablesize-datasize || (!offset && datasize>1))\n\t\t\treturn NULL;\t//can't autoresize these. just fail.\n\t\treturn inst->stringtable + ptr;\n\t}\n\treturn NULL;\n}\n\nvoid QCBUILTIN PF_memgetval (pubprogfuncs_t *inst, struct globalvars_s *globals)\n{\n\tprogfuncs_t *progfuncs = (progfuncs_t*)inst;\n\t//read 32 bits from a pointer.\n\tint dst = G_INT(OFS_PARM0);\n\tfloat ofs = G_FLOAT(OFS_PARM1);\n\tint size = sizeof(int);\n\tif (ofs != (float)(int)ofs)\n\t\tPR_RunWarning(inst, \"PF_memgetval: non-integer offset\\n\");\n\tdst += ofs*size;\n\tif (dst < 0 || dst+size >= inst->stringtablesize)\n\t{\n\t\tPR_RunError(inst, \"PF_memgetval: invalid dest\\n\");\n\t\treturn;\n\t}\n\tif (dst & 3)\n\t\tPR_RunWarning(inst, \"PF_memgetval: misaligned pointer (%#x)\\n\", dst);\n\tG_INT(OFS_RETURN) = *(int*)(inst->stringtable + dst);\n}\nvoid QCBUILTIN PF_memsetval (pubprogfuncs_t *inst, struct globalvars_s *globals)\n{\n\tprogfuncs_t *progfuncs = (progfuncs_t*)inst;\n\t//write 32 bits to a pointer.\n\tint dst = G_INT(OFS_PARM0);\n\tfloat ofs = G_FLOAT(OFS_PARM1);\n\tint val = G_INT(OFS_PARM2);\n\tint size = sizeof(int);\n\tif (ofs != (float)(int)ofs)\n\t\tPR_RunWarning(inst, \"PF_memsetval: non-integer offset\\n\");\n\tdst += ofs*size;\n\tif (dst < 0 || dst+size >= inst->stringtablesize)\n\t{\n\t\tPR_RunError(inst, \"PF_memsetval: invalid dest\\n\");\n\t\treturn;\n\t}\n\tif (dst & 3)\n\t\tPR_RunWarning(inst, \"PF_memgetval: misaligned pointer (%#x)\\n\", dst);\n\t*(int*)(inst->stringtable + dst) = val;\n}\n\n//#define GCTIMINGS\n\n#ifdef QCGC\n#define smallbool char\nstatic smallbool *PR_QCGC_Mark(void *mem, size_t memsize, size_t numtemps)\n{\n\tunsigned int *str;\t//the reference we're considering\n\tsize_t p;\n\tsmallbool *marked;\t//just booleans. could compact.\n\tmarked = malloc(sizeof(*marked) * numtemps);\n\tmemset(marked, 0, sizeof(*marked) * numtemps);\n\n\t//mark everything the qc has access to, even if it isn't even a string!\n\t//note that I did try specifically checking only data explicitly marked as a string type, but that was:\n\t//a) a smidge slower (lots of extra loops and conditions I guess)\n\t//b) doesn't work with pointers/structs (yes, we assume it'll all be aligned).\n\t//c) both methods got the same number of false positives in my test (2, probably dead strunzoned references)\n\tfor (str = mem, p = 0; p < memsize; p+=sizeof(*str), str++)\n\t{\n\t\tif ((*str & STRING_SPECMASK) == STRING_TEMP)\n\t\t{\n\t\t\tunsigned int idx = *str &~ STRING_SPECMASK;\n\t\t\tif (idx < numtemps)\n\t\t\t\tmarked[idx] = true;\n\t\t}\n\t}\n\treturn marked;\n}\nstatic size_t PR_QCGC_Sweep(progfuncs_t *progfuncs, smallbool *marked, tempstr_t **tempstrings, unsigned int numtemps)\n{\n\tunsigned int p;\n\tunsigned int swept = 0;\n#ifdef GCTIMINGS\n\tunsigned int unswept = 0;\n\tunsigned int errors = 0;\n#endif\n\tfor (p = 0; p < numtemps; p++)\n\t{\n\t\tif (marked[p])\n\t\t{\t//still live...\n#ifdef GCTIMINGS\n\t\t\tunswept++;\n\n\t\t\tif (!tempstrings[p])\n\t\t\t\terrors++;\n#endif\n\t\t}\n\t\telse if (tempstrings[p])\n\t\t{\t//not marked, but was valid at the time our snapshot was taken\n\n#ifdef _DEBUG\n\t\t\tif (tempstrings[p] != prinst.tempstrings[p])\n\t\t\t{\t//something weird happened. tempstrings are supposed to be immutable (at least in length).\n\t\t\t\texterns->Sys_Error(\"tempstring was reallocated while gc was running\");\n\t\t\t\tcontinue;\n\t\t\t}\n#endif\n\n\t\t\tswept++;\n\n\t\t\t//FIXME: Security Race: its possible for a mod to do weird manipulations to access the tempstring while we're still freeing it, allowing it to read something outside of its sandbox.\n\t\t\t//one option would be to have the main thread bounce it back to the worker after its complete, so we can actually free the memory only after main thread has acknowledged that its tempstrings are nulled.\n\t\t\tprinst.tempstrings[p] = NULL;\n\n\t\t\texterns->memfree(tempstrings[p]);\n\t\t}\n\t}\n\n\tfree(marked);\n\n\treturn swept;\n}\n\n#ifdef THREADEDGC\n#include \"quakedef.h\"\nstruct qcgccontext_s\n{\n\tint done;\n\tunsigned int clearedtemps;\t//number of temps that were swept away\n\tprogfuncs_t *progfuncs;\t//careful!\n\n\tsize_t maxtemps;\t\t//so it doesn't go stale\n\ttempstr_t **tempstrings;//so we don't get confused over temps added while marking\n\n\tsize_t memsize;\n\tunsigned int amem[1];\n};\nvoid PR_QCGC_Done(void *ctx, void *data, size_t a, size_t b)\n{\n\tstruct qcgccontext_s *gc = ctx;\n\tgc->done = true;\n}\nvoid PR_QCGC_Thread(void *ctx, void *data, size_t a, size_t b)\n{\n\tstruct qcgccontext_s *gc = ctx;\n\tprogfuncs_t *progfuncs = gc->progfuncs;\n\tsmallbool *marked;\n\n#ifdef GCTIMINGS\n\tdouble starttime, markedtime, endtime;\n\tstarttime = Sys_DoubleTime();\n#endif\n\tmarked = PR_QCGC_Mark(gc->amem, gc->memsize, gc->maxtemps);\n#ifdef GCTIMINGS\n\tmarkedtime = Sys_DoubleTime();\n#endif\n\tgc->clearedtemps = PR_QCGC_Sweep(progfuncs, marked, gc->tempstrings, gc->maxtemps);\n#ifdef GCTIMINGS\n\tendtime = Sys_DoubleTime();\n\tgc->externs->Printf(\"live: %u, dead: %u, threadtime: mark=%f, sweep=%f, total=%f\\n\", prinst.livetemps-gc->clearedtemps, gc->clearedtemps, (markedtime - starttime), (endtime - markedtime), endtime-starttime);\n#endif\n\n\tCOM_InsertWork(WG_MAIN, PR_QCGC_Done, gc, NULL, 0, 0);\n}\n#endif\nstatic void PR_ExpandTempStrings(progfuncs_t *progfuncs, size_t newmax)\n{\n\ttempstr_t **ntable = progfuncs->funcs.parms->memalloc(sizeof(*ntable) * newmax);\n\tmemcpy(ntable, prinst.tempstrings, sizeof(*ntable) * prinst.maxtempstrings);\n\tmemset(ntable+prinst.maxtempstrings, 0, sizeof(*ntable) * (newmax-prinst.maxtempstrings));\n\tprinst.maxtempstrings = newmax;\n\tif (prinst.tempstrings)\n\t\tprogfuncs->funcs.parms->memfree(prinst.tempstrings);\n\tprinst.tempstrings = ntable;\n}\nstatic string_t PDECL PR_AllocTempStringLen\t\t\t(pubprogfuncs_t *ppf, char **str, unsigned int len)\n{\n\tprogfuncs_t *progfuncs = (progfuncs_t *)ppf;\n\tint i;\n\n\tif (!str)\n\t\treturn 0;\n\n\tif (prinst.livetemps == prinst.maxtempstrings)\n\t{\n#ifdef THREADEDGC\n\t\t//need to wait for the gc to finish, otherwise it might be wiping freed strings that we're still using.\n\t\twhile (prinst.gccontext)\n\t\t{\n\t\t\tCOM_WorkerPartialSync(prinst.gccontext, &prinst.gccontext->done, false);\n\t\t\tPR_RunGC(progfuncs);\n\t\t}\n#endif\n\n\t\tPR_ExpandTempStrings(progfuncs, prinst.maxtempstrings*2 + 1024);\n\t}\n\n\tfor (i = prinst.nexttempstring; i < prinst.maxtempstrings && prinst.tempstrings[i]; i++)\n\t\t;\n\tif (i == prinst.maxtempstrings)\n\t{\n\t\tfor (i = 0; i < prinst.nexttempstring && prinst.tempstrings[i]; i++)\n\t\t\t;\n\t\tif (i == prinst.nexttempstring)\n\t\t\treturn 0;\t//panic!\n\t}\n\tprinst.nexttempstring = i;\n\tprinst.livetemps++;\n\n\tlen = ((len+3)&~3);\t//round up, primarily so its safe to use loadp_i to read the last few chars of the string\n\n\tprinst.tempstrings[i] = progfuncs->funcs.parms->memalloc(sizeof(tempstr_t) - sizeof(((tempstr_t*)NULL)->value) + len);\n\tprinst.tempstrings[i]->size = len;\n\t*str = prinst.tempstrings[i]->value;\n\n\treturn (string_t)((unsigned int)i | STRING_TEMP);\n}\nvoid PR_RunGC (progfuncs_t *progfuncs)\n{\n#ifdef THREADEDGC\n\tif (!prinst.gccontext)\n#endif\n\t{\n\t\tif (prinst.livetemps < prinst.maxtempstrings/2 || prinst.nexttempstring < prinst.maxtempstrings/2)\n\t\t{\t//don't bother yet\n\t\t\treturn;\n\t\t}\n#ifdef THREADEDGC\n\t\tif (externs->usethreadedgc)\n\t\t{\n#ifdef GCTIMINGS\n\t\t\tdouble starttime = Sys_DoubleTime(), endtime;\n#endif\n\t\t\tstruct qcgccontext_s *gc = prinst.gccontext = malloc(sizeof(*gc) - sizeof(gc->amem) + prinst.addressableused + sizeof(*gc->tempstrings)*prinst.maxtempstrings);\n\t\t\tgc->done = false;\n\t\t\tgc->clearedtemps = 0;\n\t\t\tgc->progfuncs = progfuncs;\n\n\t\t\tgc->memsize = prinst.addressableused;\n\t\t\tmemcpy(gc->amem, prinst.addressablehunk, prinst.addressableused);\n\n\t\t\tgc->maxtemps = prinst.maxtempstrings;\n\t\t\tgc->tempstrings = (void*)((char*)gc->amem+prinst.addressableused);\n\t\t\tmemcpy(gc->tempstrings, prinst.tempstrings, sizeof(*gc->tempstrings)*gc->maxtemps);\n\n\t\t\tCOM_InsertWork(WG_LOADER, PR_QCGC_Thread, gc, NULL, 0, 0);\n\n#ifdef GCTIMINGS\n\t\t\tendtime = Sys_DoubleTime();\n\t\t\tgc->externs->Printf(\"preparetime=%f\\n\", (endtime - starttime));\n#endif\n\t\t\treturn;\n\t\t}\n#endif\n\t\t{\t//same-thread gc.\n\t\t\tsmallbool *marked = PR_QCGC_Mark(prinst.addressablehunk, prinst.addressableused, prinst.maxtempstrings);\n\t\t\tsize_t swept = PR_QCGC_Sweep(progfuncs, marked, prinst.tempstrings, prinst.maxtempstrings);\n\t\t\tprinst.livetemps -= swept;\n\n\t\t\t//if over half the (max)strings are still live, just increase the max so we are not spamming collections\n\t\t\tif (prinst.livetemps >= prinst.maxtempstrings/2)\n\t\t\t\tPR_ExpandTempStrings(progfuncs, prinst.maxtempstrings * 2);\n\t\t}\n\t}\n#ifdef THREADEDGC\n\telse if (prinst.gccontext->done)\n\t{\n\t\tprinst.livetemps -= prinst.gccontext->clearedtemps;\n\t\tfree(prinst.gccontext);\n\t\tprinst.gccontext = NULL;\n\n\t\t//if over half the (max)strings are still live, just increase the max so we are not spamming collections\n\t\tif (prinst.livetemps >= prinst.maxtempstrings/2)\n\t\t\tPR_ExpandTempStrings(progfuncs, prinst.maxtempstrings * 2);\n\t}\n#endif\n}\n\nstatic void PR_FreeAllTemps\t\t\t(progfuncs_t *progfuncs)\n{\n\tunsigned int i;\n#ifdef THREADEDGC\n\twhile (prinst.gccontext)\n\t{\n\t\tCOM_WorkerPartialSync(prinst.gccontext, &prinst.gccontext->done, false);\n\t\tPR_RunGC(progfuncs);\n\t}\n#endif\n\tfor (i = 0; i < prinst.maxtempstrings; i++)\n\t{\n\t\texterns->memfree(prinst.tempstrings[i]);\n\t\tprinst.tempstrings[i] = NULL;\n\t}\n\tprinst.maxtempstrings = 0;\n\tprinst.nexttempstring = 0;\n\tprinst.livetemps = 0;\n}\n#else\nstatic string_t PDECL PR_AllocTempStringLen\t\t\t(pubprogfuncs_t *ppf, char **str, unsigned int len)\n{\n\tprogfuncs_t *progfuncs = (progfuncs_t*)ppf;\n\ttempstr_t **ntable, *n;\n\tint newmax;\n\tint i;\n\n\tif (!str)\n\t\treturn 0;\n\n\tif (prinst.numtempstrings == prinst.maxtempstrings)\n\t{\n\t\tnewmax = prinst.maxtempstrings + 1024;\n\t\tntable = progfuncs->funcs.parms->memalloc(sizeof(char*) * newmax);\n\t\tmemcpy(ntable, prinst.tempstrings, sizeof(char*) * prinst.numtempstrings);\n\t\tprinst.maxtempstrings = newmax;\n\t\tif (prinst.tempstrings)\n\t\t\tprogfuncs->funcs.parms->memfree(prinst.tempstrings);\n\t\tprinst.tempstrings = ntable;\n\t}\n\n\ti = prinst.numtempstrings;\n\tif (i == 0x10000000)\n\t\treturn 0;\n\n\tprinst.numtempstrings++;\n\n\tn = progfuncs->funcs.parms->memalloc(sizeof(tempstr_t) - sizeof(((tempstr_t*)NULL)->value) + len);\n\tn->size = len;\n\t*str = n->value;\n\tprinst.tempstrings[i] = n;\t//doesn't have its value yet...\n\n\treturn (string_t)((unsigned int)i | STRING_TEMP);\n}\nvoid PR_FreeTemps\t\t\t(progfuncs_t *progfuncs, int depth)\n{\n\tint i;\n\tif (depth > prinst.numtempstrings)\n\t{\n\t\tSys_Error(\"QC Temp stack inverted\\n\");\n\t\treturn;\n\t}\n\tfor (i = depth; i < prinst.numtempstrings; i++)\n\t{\n\t\texterns->memfree(prinst.tempstrings[i]);\n\t}\n\n\tprinst.numtempstrings = depth;\n}\nstatic void PR_FreeAllTemps\t\t\t(progfuncs_t *progfuncs)\n{\n\tunsigned int i;\n\tfor (i = 0; i < prinst.numtempstrings; i++)\n\t{\n\t\texterns->memfree(prinst.tempstrings[i]);\n\t\tprinst.tempstrings[i] = NULL;\n\t}\n\tprinst.numtempstrings = 0;\n\tprinst.nexttempstring = 0;\n}\n#endif\n\nstring_t PDECL PR_AllocTempString\t\t\t(pubprogfuncs_t *ppf, const char *str)\n{\n\tchar *out;\n\tstring_t res;\n\tsize_t len;\n\tif (!str)\n\t\treturn 0;\n\tlen = strlen(str)+1;\n\tres = PR_AllocTempStringLen(ppf, &out, len);\n\tif (res)\n\t\tmemcpy(out, str, len);\n\treturn res;\n}\nstatic pbool PDECL PR_DumpProfiles (pubprogfuncs_t *ppf, pbool resetprofiles)\n{\n\tprogfuncs_t *progfuncs = (progfuncs_t*)ppf;\n\tstruct progstate_s *ps;\n\tunsigned int i, f, j, s;\n\tprclocks_t cpufrequency;\n\tstruct\n\t{\n\t\tchar *fname;\n\t\tint profile;\n\t\tprclocks_t profiletime;\n\t\tprclocks_t totaltime;\n\t} *sorted, t;\n\tif (!prinst.profiling)\n\t{\n\t\tprinst.profiling = true;\n\t\treturn false;\n\t}\n\n\tcpufrequency = Sys_GetClockRate();\n\n\tfor (i = 0; i < prinst.maxprogs; i++)\n\t{\n\t\tps = &pr_progstate[i];\n\t\tif (ps->progs == NULL)\t//we havn't loaded it yet, for some reason\n\t\t\tcontinue;\t\n\n\t\texterns->Printf(\"%s:\\n\", ps->filename);\n\t\tsorted = malloc(sizeof(*sorted) * ps->progs->numfunctions);\n\t\t//pull out the functions in order to sort them\n\t\tfor (s = 0, f = 0; f < ps->progs->numfunctions; f++)\n\t\t{\n\t\t\tif (!ps->functions[f].profile)\n\t\t\t\tcontinue;\n\t\t\tsorted[s].fname = ps->functions[f].s_name+progfuncs->funcs.stringtable;\n\t\t\tsorted[s].profile = ps->functions[f].profile;\n\t\t\tsorted[s].profiletime = ps->functions[f].profiletime - ps->functions[f].profilechildtime;\n\t\t\tsorted[s].totaltime = ps->functions[f].profiletime;\n\t\t\tif (resetprofiles)\n\t\t\t{\n\t\t\t\tps->functions[f].profile = 0;\n\t\t\t\tps->functions[f].profiletime = 0;\n\t\t\t\tps->functions[f].profilechildtime = 0;\n\t\t\t}\n\t\t\ts++;\n\t\t}\n\n\t\t// good 'ol bubble sort\n\t\tfor (f = 0; f < s; f++)\n\t\t{\n\t\t\tfor (j = f; j < s; j++)\n\t\t\t\tif (sorted[f].profiletime > sorted[j].profiletime)\n\t\t\t\t{\n\t\t\t\t\tt = sorted[f];\n\t\t\t\t\tsorted[f] = sorted[j];\n\t\t\t\t\tsorted[j] = t;\n\t\t\t\t}\n\t\t}\n\n\t\t//print it out\n\t\texterns->Printf(\"%8s %9s %10s: %s\\n\", \"ops\", \"self-time\", \"total-time\", \"function\");\n\t\tfor (f = 0; f < s; f++)\n\t\t\texterns->Printf(\"%8u %9f %10f: %s\\n\", sorted[f].profile, ull2dbl(sorted[f].profiletime) / ull2dbl(cpufrequency), ull2dbl(sorted[f].totaltime) / ull2dbl(cpufrequency), sorted[f].fname);\n\t\tfree(sorted);\n\t}\n\treturn true;\n}\n\nstatic void PDECL PR_Shutdown(pubprogfuncs_t *ppf);\n\nstatic pubprogfuncs_t deffuncs = {\n\tPROGSTRUCT_VERSION,\n\tPR_Shutdown,\n\tPR_Configure,\n\tPR_LoadProgs,\n\tPR_InitEnts,\n\tPR_ExecuteProgram,\n\tPR_globals,\n\tPR_entvars,\n\tPR_RunError,\n\tED_Print,\n\tED_Alloc,\n\tED_AllocIndex,\n\tED_Free,\n\n\tQC_EDICT_NUM,\n\tQC_NUM_FOR_EDICT,\n\n\tPR_VarString,\n\n\tNULL,\t//progstate\n\t0,\t\t//numprogs\n\n\tPR_FindFunc,\n#if defined(MINIMAL) || defined(OMIT_QCC)\n\tNULL,\n\tNULL,\n#else\n\tComp_Begin,\n\tComp_Continue,\n#endif\n\n\tfilefromprogs,\n\tNULL,//filefromnewprogs,\n\n\tED_Print,\n\tPR_SaveEnts,\n\tPR_LoadEnts,\n\n\tPR_SaveEnt,\n\tPR_RestoreEnt,\n\n\tPR_FindGlobal,\n\n\tQC_GetEdictFieldValue,\n\tProgsToEdict,\n\tEdictToProgs,\n\n\tPR_EvaluateDebugString,\n\t0,//trace\n\tPR_StackTrace,\n\tPR_ToggleBreakpoint,\n\n\tNULL,\t//parms\n#if 1//defined(MINIMAL) || defined(OMIT_QCC)\n\tNULL,\t//decompile\n#else\n\tQC_Decompile,\n#endif\n\t0,\t//callargc\n\t0,\n\n\t0,\t//string table(pointer base address)\n\t0,\t\t//string table size\n\t0,\t//max size\n\t0,\t//field adjust(aditional field offset)\n\t0,\t//field slots allocated (for builtins to clamp field reference args).\n\n\tPR_ForkStack,\n\tPR_ResumeThread,\n\tPR_AbortStack,\n\tPR_GetBuiltinCallInfo,\n\tPR_FindBuiltins,\n\n\tQC_RegisterFieldVar,\n\n\tED_NewString,\n\tQC_HunkAlloc,\n\tPR_memalloc,\n\tPR_memrealloc,\n\tPR_memfree,\n\tPR_AllocTempString,\n\tPR_AllocTempStringLen,\n\tPR_StringToProgs,\n\tPR_StringToNative,\n\n\tPR_QueryField,\n\tQC_ClearEdict,\n\tQC_FindPrefixedGlobals,\n\tPR_SetWatchPoint,\n\n\tQC_AddSharedVar,\n\tQC_AddSharedFieldVar,\n\tPR_RemoveProgsString,\n\tPR_GetFunctionInfo,\n\tPR_GenerateStatementString,\n\tED_FieldInfo,\n\tPR_UglyValueString,\n\tED_ParseEval,\n\tPR_SetStringField,\n\tPR_DumpProfiles,\n\n\t0, NULL,\n};\nstatic int PDECL qclib_null_printf(const char *s, ...)\n{\n\treturn 0;\n}\nstatic void *PDECL qclib_malloc(int size)\n{\n\treturn malloc(size);\n}\nstatic void PDECL qclib_free(void *ptr)\n{\n\tfree(ptr);\n}\n#ifdef FTE_TARGET_WEB\n#undef printf\n#define printf NULL\t//should be some null wrapper instead\n#endif\n\n//progfuncs_t *progfuncs = NULL;\n#undef memfree\n#undef prinst\n#undef extensionbuiltin\n#undef field\n#undef shares\n#undef maxedicts\n#undef sv_num_edicts\n\nstatic void PDECL PR_Shutdown(pubprogfuncs_t *ppf)\n{\n\tvoid (VARGS *f) (void *);\n\tprogfuncs_t *inst = (progfuncs_t*)ppf;\n\n\tunsigned int i;\n\tedictrun_t *e;\n\n\tf = inst->funcs.parms->memfree;\n\n\tfor ( i=1 ; i<inst->inst.maxedicts; i++)\n\t{\n\t\te = (edictrun_t *)(inst->funcs.edicttable[i]);\n\t\tinst->funcs.edicttable[i] = NULL;\n\t\tif (e)\n\t\t{\n//\t\t\te->entnum = i;\n\t\t\tf(e);\n\t\t}\n\t}\n\n\tPRHunkFree(inst, 0);\n\n#if defined(_WIN32) && !defined(WINRT)\n\tVirtualFree(inst->inst.addressablehunk, 0, MEM_RELEASE);\t//doesn't this look complicated? :p\n#else\n\tfree(inst->inst.addressablehunk);\n#endif\n\n\tPR_FreeAllTemps(inst);\n\n\tif (inst->inst.allocedstrings)\n\t\tf(inst->inst.allocedstrings);\n\tinst->inst.allocedstrings = NULL;\n\tif (inst->inst.tempstrings)\n\t\tf(inst->inst.tempstrings);\n\tinst->inst.tempstrings = NULL;\n\n\tfree(inst->inst.watch_name);\n\n\tif (inst->inst.field)\n\t\tf(inst->inst.field);\n\tif (inst->inst.shares)\n\t\tf(inst->inst.shares);\t//free memory\n\tf(inst);\n}\n\n#ifndef WIN32\n#define QCLIBINT\t//don't use dllspecifications\n#endif\n\n#if defined(QCLIBDLL_EXPORTS)\n\t#ifdef _WIN32\n\t\t__declspec(dllexport)\n\t#else\n\t\t__attribute__((visibility(\"default\")))\n\t#endif\n#endif\npubprogfuncs_t * PDECL InitProgs(progexterns_t *ext)\n{\t\n\tprogfuncs_t *funcs;\n\n\tif (!ext)\n\t{\n\t\tstatic progexterns_t defexterns;\n\t\text = &defexterns;\n\t}\n\telse if (ext->progsversion != PROGSTRUCT_VERSION)\n\t\treturn NULL;\n\n#undef memalloc\n#undef pr_progstate\n#undef pr_argc\n\tfuncs = (ext->memalloc?ext->memalloc:qclib_malloc)(sizeof(progfuncs_t));\t\n\tmemcpy(&funcs->funcs, &deffuncs, sizeof(pubprogfuncs_t));\n\tmemset(&funcs->inst, 0, sizeof(funcs->inst));\n\n\tfuncs->funcs.progstate = &funcs->inst.progstate;\n\n\tfuncs->funcs.parms = ext;\n\n\n\t{\n\t\t//defs incase following structure is not passed.\n\t\tstatic struct edict_s *safe_edicts;\n\t\tstatic int safe_num_edicts;\n\t\tstatic double safetime=0;\n\t\tif (!ext->progsversion)\text->progsversion = PROGSTRUCT_VERSION;\n\t\tif (!ext->Printf)\t\text->Printf = qclib_null_printf;\n\t\tif (!ext->DPrintf)\t\text->DPrintf = qclib_null_printf;\n\t\tif (!ext->Sys_Error)\text->Sys_Error = (void*)exit;\n\t\tif (!ext->memalloc)\t\text->memalloc = qclib_malloc;\n\t\tif (!ext->memfree)\t\text->memfree = qclib_free;\n\t\tif (!ext->gametime)\t\text->gametime = &safetime;\n\t\tif (!ext->edicts)\t\text->edicts = &safe_edicts;\n\t\tif (!ext->num_edicts)\text->num_edicts = &safe_num_edicts;\n\t\tif (!ext->edictsize)\text->edictsize = sizeof(edictrun_t);\n\t}\n\n\tSetEndian();\n\t\n\treturn &funcs->funcs;\n}\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n#ifdef QCC\nvoid main (int argc, char **argv)\n{\n\tprogexterns_t ext;\n\n\tprogfuncs_t *funcs;\n\tfuncs = InitProgs(&ext);\n\tif (funcs->PR_StartCompile(argc, argv))\n\t\twhile(funcs->PR_ContinueCompile());\n}\n#endif\n"
  },
  {
    "path": "engine/qclib/packager.c",
    "content": "#include \"qcc.h\"\r\n#if !defined(MINIMAL) && !defined(OMIT_QCC)\r\n#include <time.h>\r\n#ifdef _WIN32\r\n#include <sys/types.h>\r\n#include <sys/stat.h>\r\n#else\r\n#include <unistd.h>\r\n#include <dirent.h>\r\n#include <sys/stat.h>\r\n#endif\r\nvoid QCC_JoinPaths(char *fullname, size_t fullnamesize, const char *newfile, const char *base);\r\n\r\n//package formats:\r\n//pakzip - files are uncompressed, with both a pak header and a zip trailer, allowing it to be read as either type of file.\r\n//zip - standard zips\r\n//spanned zip - the full list of files is written into a separate central-directory-only zip, the actual file data comes from regular zips named foo.z##/.p## instead of foo.zip/foo.pk3\r\n\r\n/*\r\ndataset common\r\n{\r\n\toutput default data.pk3\r\n\toutput logic textures.pk3\r\n}\r\ndataset desktop\r\n{\r\n\toutput tex textures_pc.pk3\r\n}\r\ndataset mobile\r\n{\r\n\toutput tex textures_mobile.pk3\r\n}\r\ninput pak0.pk3\r\n\r\nrule dxt1 {\r\n dataset desktop\r\n output tex\r\n newext dds\r\n command \"@\\\"c:/program files/Compressonator/CompressonatorCLI\\\" -fd DXT1 $input $output\"\r\n}\r\n\r\nrule etc2 {\r\n dataset mobile\r\n output tex\r\n newext ktx\r\n command \"@\\\"c:/program files/Compressonator/CompressonatorCLI\\\" -fd ETC2 $input $output\"\r\n}\r\n\r\nlogic {\r\n\tprogs.dat\r\n}\r\nclass texa0 {\r\n\toutput tex\r\n\tdesktop: dxt1\r\n\tmobile: etc2\r\n}\r\ntexa0\r\n{\r\n\tgfx/conback.txt \r\n}\r\n*/\r\n\r\n#define quint64_t long long\r\n#define qofs_t size_t\r\n\r\n#define countof(x) (sizeof(x)/sizeof((x)[0]))\r\n\r\nstruct pkgctx_s\r\n{\r\n\tvoid (*messagecallback)(void *userctx, const char *message, ...);\r\n\tvoid *userctx;\r\n\r\n\tchar *listfile;\r\n\r\n\tpbool test;\r\n\tpbool readoldpacks;\r\n\tchar gamepath[MAX_OSPATH];\r\n\tchar sourcepath[MAX_OSPATH];\r\n\ttime_t buildtime;\r\n\r\n\t//skips the file if its listed in one of these packages, unless the modification time on disk is newer.\r\n\tstruct oldpack_s\r\n\t{\r\n\t\tstruct oldpack_s *next;\r\n\t\tchar filename[128];\r\n\t\tsize_t numfiles;\r\n\t\tunsigned int part;\r\n\t\tstruct\r\n\t\t{\r\n\t\t\tchar name[128];\r\n\r\n\t\t\tunsigned short zmethod;\r\n\t\t\tunsigned int zcrc;\r\n\t\t\tqofs_t zhdrofs;\r\n\t\t\tqofs_t rawsize;\r\n\t\t\tqofs_t zipsize;\r\n\t\t\tunsigned short dostime;\r\n\t\t\tunsigned short dosdate;\r\n\t\t} *file;\r\n\t} *oldpacks;\r\n\r\n\tstruct dataset_s\r\n\t{\r\n\t\tstruct dataset_s *next;\r\n\r\n\t\t//these are the output pk3s from this package.\r\n\t\tstruct output_s\r\n\t\t{\r\n\t\t\tstruct output_s *next;\r\n\t\t\tchar code[128];\r\n\t\t\tchar filename[128];\r\n\t\t\tstruct file_s *files;\r\n\r\n\t\t\tpbool usediffs;\r\n\t\t\tunsigned int numparts;\r\n\t\t\tstruct oldpack_s *oldparts;\r\n\t\t} *outputs;\r\n\r\n\t\tchar name[1];\r\n\t} *datasets;\r\n\tstruct rule_s\r\n\t{\r\n\t\tstruct rule_s *next;\r\n\t\tchar name[128];\r\n\r\n\t\tint dropfile:1;\r\n\r\n\t\tchar *newext;\r\n\t\tchar *command;\r\n\t} *rules;\r\n\r\n\tstruct class_s\r\n\t{\r\n\t\tchar name[128];\r\n\t\tstruct class_s *next;\r\n\r\n\t\t//the output package codename to write to. class is skipped if the dataset doesn't include that name.\r\n\t\tchar outname[128];\r\n\r\n\t\tstruct\r\n\t\t{\r\n\t\t\tstruct dataset_s *set;\r\n\t\t\tstruct rule_s *rule;\r\n\t\t} dataset[8];\r\n\t\tstruct rule_s *defaultrule;\r\n\r\n\t\tstruct file_s\r\n\t\t{\r\n\t\t\tstruct file_s *next;\r\n\t\t\tchar name[128];\r\n\r\n\t\t\t//temp data for tracking what's getting written.\r\n\t\t\tstruct\r\n\t\t\t{\r\n\t\t\t\tchar name[128];\r\n\t\t\t\tstruct file_s *nextwrite;\r\n\t\t\t\tstruct rule_s *rule;\r\n\t\t\t\tunsigned int zdisk;\r\n\t\t\t\tunsigned short zmethod;\r\n\t\t\t\tunsigned int zcrc;\r\n\t\t\t\tqofs_t zhdrofs;\r\n\t\t\t\tqofs_t pakofs;\r\n\t\t\t\tqofs_t rawsize;\r\n\t\t\t\tqofs_t zipsize;\r\n\t\t\t\tunsigned short dostime;\r\n\t\t\t\tunsigned short dosdate;\r\n\t\t\t\ttime_t\ttimestamp;\r\n\t\t\t} write;\r\n\t\t} *files;\r\n\t} *classes;\r\n};\r\n\r\n#ifdef _WIN32\r\nstatic time_t filetime_to_timet(FILETIME ft)\r\n{\r\n\tULARGE_INTEGER ull;\r\n\tull.LowPart = ft.dwLowDateTime;\r\n\tull.HighPart = ft.dwHighDateTime;\r\n\treturn ull.QuadPart / 10000000ULL - 11644473600ULL;\r\n}\r\n#endif\r\n\r\nstatic struct rule_s *PKG_FindRule(struct pkgctx_s *ctx, char *code)\r\n{\r\n\tstruct rule_s *o;\r\n\tfor (o = ctx->rules; o; o = o->next)\r\n\t{\r\n\t\tif (!strcmp(o->name, code))\r\n\t\t\treturn o;\r\n\t}\r\n\treturn NULL;\r\n}\r\nstatic struct class_s *PKG_FindClass(struct pkgctx_s *ctx, char *code)\r\n{\r\n\tstruct class_s *c;\r\n\tfor (c = ctx->classes; c; c = c->next)\r\n\t{\r\n\t\tif (!strcmp(c->name, code))\r\n\t\t\treturn c;\r\n\t}\r\n\treturn NULL;\r\n}\r\nstatic struct dataset_s *PKG_FindDataset(struct pkgctx_s *ctx, const char *code)\r\n{\r\n\tstruct dataset_s *o;\r\n\tfor (o = ctx->datasets; o; o = o->next)\r\n\t{\r\n\t\tif (!strcmp(o->name, code))\r\n\t\t\treturn o;\r\n\t}\r\n\treturn NULL;\r\n}\r\nstatic struct dataset_s *PKG_GetDataset(struct pkgctx_s *ctx, const char *code)\r\n{\r\n\tstruct dataset_s *s = PKG_FindDataset(ctx, code);\r\n\tif (!s)\r\n\t{\r\n\t\ts = malloc(sizeof(*s)+strlen(code));\r\n\t\tstrcpy(s->name, code);\r\n\t\ts->outputs = NULL;\r\n\t\ts->next = ctx->datasets;\r\n\t\tctx->datasets = s;\r\n\t}\r\n\treturn s;\r\n}\r\n\r\nstatic pbool PKG_SkipWhite(struct pkgctx_s *ctx, pbool linebreak)\r\n{\r\n\tfor(;;)\r\n\t{\r\n\t\tif (qcc_iswhite(*ctx->listfile))\r\n\t\t{\r\n\t\t\tif (qcc_islineending(ctx->listfile[0], ctx->listfile[1]) && !linebreak)\r\n\t\t\t\treturn false;\r\n\t\t\tctx->listfile++;\r\n\t\t\tcontinue;\r\n\t\t}\r\n\t\tif (ctx->listfile[0] == '/' && ctx->listfile[1] == '/')\r\n\t\t{\r\n\t\t\twhile (!qcc_islineending(ctx->listfile[0], ctx->listfile[1]))\r\n\t\t\t\tctx->listfile++;\r\n\t\t\tcontinue;\r\n\t\t}\r\n\t\tif (ctx->listfile[0] == '/' && ctx->listfile[1] == '*')\r\n\t\t{\r\n\t\t\tctx->listfile+=2;\r\n\t\t\twhile (*ctx->listfile)\r\n\t\t\t{\r\n\t\t\t\tif (ctx->listfile[0]=='*' && ctx->listfile[1]=='/')\r\n\t\t\t\t{\r\n\t\t\t\t\tctx->listfile+=2;\r\n\t\t\t\t\tbreak;\r\n\t\t\t\t}\r\n\t\t\t\tctx->listfile++;\r\n\t\t\t}\r\n\t\t\tcontinue;\r\n\t\t}\r\n\t\tbreak;\r\n\t}\r\n\treturn true;\r\n}\r\nstatic pbool PKG_GetToken(struct pkgctx_s *ctx, char *token, size_t sizeoftoken, pbool linebreak)\r\n{\r\n\tif (!PKG_SkipWhite(ctx, linebreak))\r\n\t\treturn false;\r\n\tif (*ctx->listfile)\r\n\t{\r\n\t\twhile(*ctx->listfile)\r\n\t\t{\r\n\t\t\tif (qcc_iswhite(*ctx->listfile))\r\n\t\t\t\tbreak;\r\n\t\t\t*token = *ctx->listfile++;\r\n\t\t\tif (sizeoftoken > 1)\r\n\t\t\t{\r\n\t\t\t\ttoken++;\r\n\t\t\t\tsizeoftoken--;\r\n\t\t\t}\r\n\t\t}\r\n\t\t*token = 0;\r\n\t\treturn true;\r\n\t}\r\n\treturn false;\r\n}\r\n\r\nstatic pbool PKG_GetStringToken(struct pkgctx_s *ctx, char *token, size_t sizeoftoken)\r\n{\r\n\tif (!PKG_SkipWhite(ctx, false))\r\n\t\treturn false;\r\n\tif (*ctx->listfile == '\\\"')\r\n\t{\r\n\t\tctx->listfile++;\r\n\t\twhile(*ctx->listfile)\r\n\t\t{\r\n\t\t\tif (*ctx->listfile == '\\\"')\r\n\t\t\t{\r\n\t\t\t\tctx->listfile++;\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\t\t\telse if (*ctx->listfile == '\\\\')\r\n\t\t\t{\r\n\t\t\t\tctx->listfile++;\r\n\t\t\t\tswitch(*ctx->listfile++)\r\n\t\t\t\t{\r\n\t\t\t\tcase '\\\"':\t*token = '\\\"'; break;\r\n\t\t\t\tcase '\\\\':\t*token = '\\\\'; break;\r\n\t\t\t\tcase '\\r':\t*token = '\\r'; break;\r\n\t\t\t\tcase '\\n':\t*token = '\\n'; break;\r\n\t\t\t\tcase '\\t':\t*token = '\\t'; break;\r\n\t\t\t\tdefault:\t*token = '?'; break;\r\n\t\t\t\t}\r\n\t\t\t\tsizeoftoken--;\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t\t*token = *ctx->listfile++;\r\n\t\t\tif (sizeoftoken > 1)\r\n\t\t\t{\r\n\t\t\t\ttoken++;\r\n\t\t\t\tsizeoftoken--;\r\n\t\t\t}\r\n\t\t}\r\n\t\t*token = 0;\r\n\t\treturn true;\r\n\t}\r\n\treturn false;\r\n}\r\n\r\nstatic pbool PKG_Expect(struct pkgctx_s *ctx, char *token)\r\n{\r\n\tchar tok[128];\r\n\tif (PKG_GetToken(ctx, tok, sizeof(tok), true))\r\n\t{\r\n\t\tif (!strcmp(tok, token))\r\n\t\t\treturn true;\r\n\t}\r\n\tctx->messagecallback(ctx->userctx, \"Expected '%s', found '%s'\\n\", token, tok);\r\n\treturn false;\r\n}\r\n\r\nstatic void PKG_ReplaceString(char *str, char *find, char *newpart)\r\n{\r\n\tchar *oldpart;\r\n\tsize_t oldlen = strlen(find);\r\n\tsize_t nlen = strlen(newpart);\r\n\twhile((oldpart = strstr(str, find)))\r\n\t{\r\n\t\tmemmove(oldpart+nlen, oldpart+oldlen, strlen(oldpart+oldlen)+1);\r\n\t\tmemmove(oldpart, newpart, nlen);\r\n\t\tstr = oldpart+nlen;\r\n\t}\r\n}\r\nstatic void PKG_CreateOutput(struct pkgctx_s *ctx, struct dataset_s *s, const char *code, const char *filename, pbool diff)\r\n{\r\n\tchar path[MAX_OSPATH];\r\n\tchar date[64];\r\n\tstruct output_s *o;\r\n\tfor (o = s->outputs; o; o = o->next)\r\n\t{\r\n\t\tif (!strcmp(o->code, code))\r\n\t\t{\r\n\t\t\tctx->messagecallback(ctx->userctx, \"Dataset '%s' defined with dupe output\\n\", s->name, code);\r\n\t\t\treturn;\r\n\t\t}\r\n\t}\r\n\r\n\tif (strlen(code) >= sizeof(o->code))\r\n\t{\r\n\t\tctx->messagecallback(ctx->userctx, \"Output '%s' name too long\\n\", code);\r\n\t\treturn;\r\n\t}\r\n\r\n\tstrcpy(path, filename);\r\n\tstrftime(date, sizeof(date), \"%Y%m%d\", localtime(&ctx->buildtime));\r\n\tPKG_ReplaceString(path, \"$date\", date);\r\n\r\n\to = malloc(sizeof(*o));\r\n\tmemset(o, 0, sizeof(*o));\r\n\tstrcpy(o->code, code);\r\n\to->usediffs = diff;\r\n\tQCC_JoinPaths(o->filename, sizeof(o->filename), path, ctx->gamepath);\r\n\to->next = s->outputs;\r\n\ts->outputs = o;\r\n\r\n\r\n\tif (diff)\r\n\t{\r\n\t\tchar *end = path + strlen(path)-2;\r\n\t\tunsigned int i;\r\n\t\tfor (i = 0; i <= 99; i++)\r\n\t\t{\r\n#ifdef _WIN32\r\n\t\t\tstruct _stat statbuf;\r\n#else\r\n\t\t\tstruct stat statbuf;\r\n#endif\r\n\t\t\tsprintf(end, \"%02u\", i+1);\r\n#ifdef _WIN32\r\n\t\t\t//FIXME: use the utf16 version because microsoft suck and don't allow utf-8\r\n\t\t\tif (_stat(path, &statbuf) == 0)\r\n#else\r\n\t\t\tif (stat(path, &statbuf) == 0)\r\n#endif\r\n\t\t\t{\r\n\t\t\t\tstruct oldpack_s *span = malloc(sizeof(*span));\r\n\t\t\t\tstrcpy(span->filename, path);\r\n\t\t\t\tspan->numfiles = 0;\r\n\t\t\t\tspan->file = NULL;\r\n\t\t\t\tspan->next = o->oldparts;\r\n\t\t\t\tspan->part = i;\r\n\t\t\t\to->oldparts = span;\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n}\r\n\r\nstatic void PKG_ParseOutput(struct pkgctx_s *ctx, pbool diff)\r\n{\r\n\tstruct dataset_s *s;\r\n\tchar name[128];\r\n\tchar prop[128];\r\n\tchar fname[128];\r\n\r\n\tif (!PKG_GetToken(ctx, name, sizeof(name), false))\r\n\t{\r\n\t\tctx->messagecallback(ctx->userctx, \"Output: Expected name\\n\");\r\n\t\treturn;\r\n\t}\r\n\r\n\tif (PKG_GetStringToken(ctx, prop, sizeof(prop)))\r\n\t{\r\n\t\ts = PKG_GetDataset(ctx, \"core\");\r\n\t\tPKG_CreateOutput(ctx, s, name, prop, diff);\r\n\t}\r\n\telse\r\n\t{\r\n\t\tif (!PKG_Expect(ctx, \"{\"))\r\n\t\t\treturn;\r\n\t\twhile(PKG_GetToken(ctx, prop, sizeof(prop), true))\r\n\t\t{\r\n\t\t\tif (!strcmp(prop, \"}\"))\r\n\t\t\t\tbreak;\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tchar *e = strchr(prop, ':');\r\n\t\t\t\tif (e && !e[1])\r\n\t\t\t\t{\r\n\t\t\t\t\t*e = 0;\r\n\t\t\t\t\ts = PKG_GetDataset(ctx, prop);\r\n\t\t\t\t\tif (PKG_GetStringToken(ctx, fname, sizeof(fname)))\r\n\t\t\t\t\t\tPKG_CreateOutput(ctx, s, name, fname, diff);\r\n\t\t\t\t\telse\r\n\t\t\t\t\t\tctx->messagecallback(ctx->userctx, \"Output '%s[%s]' filename omitted\\n\", name, prop);\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t\tctx->messagecallback(ctx->userctx, \"Output '%s' has unknown property '%s'\\n\", name, prop);\r\n\t\t\t}\r\n\r\n\t\t\t//skip any junk\r\n\t\t\twhile(PKG_GetToken(ctx, prop, sizeof(prop), false))\r\n\t\t\t{\r\n\t\t\t\tif (!strcmp(prop, \";\"))\r\n\t\t\t\t\tbreak;\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n}\r\n\r\n#ifdef _WIN32\r\nstatic void PKG_AddOldPack(struct pkgctx_s *ctx, const char *fname)\r\n{\r\n\tstruct oldpack_s *pack;\r\n\r\n\tpack = malloc(sizeof(*pack));\r\n\tstrcpy(pack->filename, fname);\r\n\tpack->numfiles = 0;\r\n\tpack->file = NULL;\r\n\tpack->next = ctx->oldpacks;\r\n\tctx->oldpacks = pack;\r\n}\r\n#endif\r\n\r\nstatic void PKG_ParseOldPack(struct pkgctx_s *ctx)\r\n{\r\n\tchar token[MAX_OSPATH];\r\n\r\n\tif (!PKG_GetStringToken(ctx, token, sizeof(token)))\r\n\t\treturn;\r\n\r\n#ifdef _WIN32\r\n\t{\r\n\t\tchar oldpack[MAX_OSPATH];\r\n\t\tWIN32_FIND_DATA fd;\r\n\t\tHANDLE h;\r\n\t\tQCC_JoinPaths(oldpack, sizeof(oldpack), token, ctx->gamepath);\r\n\t\th = FindFirstFile(oldpack, &fd);\r\n\t\tif (h == INVALID_HANDLE_VALUE)\r\n\t\t\tctx->messagecallback(ctx->userctx, \"wildcard string '%s' found no files\\n\", token);\r\n\t\telse\r\n\t\t{\r\n\t\t\tdo\r\n\t\t\t{\r\n\t\t\t\tQCC_JoinPaths(token, sizeof(token), fd.cFileName, oldpack);\r\n\t\t\t\tPKG_AddOldPack(ctx, token);\r\n\t\t\t} while(FindNextFile(h, &fd));\r\n\t\t}\r\n\t}\r\n#else\r\n\tctx->messagecallback(ctx->userctx, \"no wildcard support, sorry\\n\");\r\n#endif\r\n}\r\n/*\r\nstatic void PKG_ParseDataset(struct pkgctx_s *ctx)\r\n{\r\n\tstruct dataset_s *s;\r\n\tchar name[128];\r\n\tchar prop[128];\r\n\r\n\tif (!PKG_GetToken(ctx, name, sizeof(name), false))\r\n\t{\r\n\t\tctx->messagecallback(ctx->userctx, \"Dataset: Expected name\\n\");\r\n\t\treturn;\r\n\t}\r\n\r\n\tif (strlen(name) >= sizeof(s->name))\r\n\t{\r\n\t\tctx->messagecallback(ctx->userctx, \"Dataset '%s' name too long\\n\", name);\r\n\t\treturn;\r\n\t}\r\n\r\n\ts = malloc(sizeof(*s));\r\n\tmemset(s, 0, sizeof(*s));\r\n\tstrcpy(s->name, name);\r\n\r\n\tif (PKG_Expect(ctx, \"{\"))\r\n\t{\r\n\t\twhile(PKG_GetToken(ctx, prop, sizeof(prop), true))\r\n\t\t{\r\n\t\t\tif (!strcmp(prop, \"}\"))\r\n\t\t\t\tbreak;\r\n\t\t\telse if (!strcmp(prop, \"output\"))\r\n\t\t\t{\r\n\t\t\t\tif (PKG_GetToken(ctx, name, sizeof(name), false))\r\n\t\t\t\t\tif (PKG_GetStringToken(ctx, prop, sizeof(prop)))\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tPKG_CreateOutput(ctx, s, name, prop);\r\n\t\t\t\t\t}\r\n\t\t\t}\r\n\t\t\telse if (!strcmp(prop, \"base\"))\r\n\t\t\t\tPKG_GetStringToken(ctx, prop, sizeof(prop));\r\n\t\t\telse\r\n\t\t\t\tctx->messagecallback(ctx->userctx, \"Dataset '%s' has unknown property '%s'\\n\", name, prop);\r\n\r\n\t\t\t//skip any junk\r\n\t\t\twhile(PKG_GetToken(ctx, prop, sizeof(prop), false))\r\n\t\t\t{\r\n\t\t\t\tif (!strcmp(prop, \";\"))\r\n\t\t\t\t\tbreak;\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\tif (PKG_FindDataset(ctx, name))\r\n\t\tctx->messagecallback(ctx->userctx, \"Dataset '%s' is already defined\\n\", name);\r\n\telse\r\n\t{\t//link it in!\r\n\t\ts->next = ctx->datasets;\r\n\t\tctx->datasets = s;\r\n\t\treturn;\r\n\t}\r\n\tPKG_DestroyDataset(s);\r\n\treturn;\r\n}*/\r\n\r\nstatic void PKG_ParseRule(struct pkgctx_s *ctx)\r\n{\r\n\tstruct rule_s *r;\r\n\tchar name[128];\r\n\tchar prop[128];\r\n\tchar newext[128];\r\n\tchar command[4096];\r\n\tint dropfile = false;\r\n\r\n\tif (!PKG_GetToken(ctx, name, sizeof(name), false))\r\n\t\treturn;\r\n\r\n\tif (strlen(name) >= sizeof(r->name))\r\n\t{\r\n\t\tctx->messagecallback(ctx->userctx, \"Rule '%s' name too long\\n\", name);\r\n\t\treturn;\r\n\t}\r\n\r\n\t*newext = *command = 0;\r\n\tif (PKG_Expect(ctx, \"{\"))\r\n\t{\r\n\t\twhile(PKG_GetToken(ctx, prop, sizeof(prop), true))\r\n\t\t{\r\n\t\t\tif (!strcmp(prop, \"}\"))\r\n\t\t\t\tbreak;\r\n\t\t\telse if (!strcmp(prop, \"newext\"))\r\n\t\t\t\tPKG_GetToken(ctx, newext, sizeof(newext), false);\r\n\t\t\telse if (!strcmp(prop, \"skip\"))\r\n\t\t\t{\r\n\t\t\t\tif (PKG_GetToken(ctx, prop, sizeof(prop), false))\r\n\t\t\t\t\tdropfile = atoi(prop);\r\n\t\t\t\telse\r\n\t\t\t\t\tdropfile = true;\r\n\t\t\t}\r\n\t\t\telse if (!strcmp(prop, \"command\"))\r\n\t\t\t\tPKG_GetStringToken(ctx, command, sizeof(command));\r\n\t\t\telse\r\n\t\t\t\tctx->messagecallback(ctx->userctx, \"Rule '%s' has unknown property '%s'\\n\", name, prop);\r\n\r\n\t\t\t//skip any junk\r\n\t\t\twhile(PKG_GetToken(ctx, prop, sizeof(prop), false))\r\n\t\t\t{\r\n\t\t\t\tif (!strcmp(prop, \";\"))\r\n\t\t\t\t\tbreak;\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\tr = PKG_FindRule(ctx, name);\r\n\tif (r)\r\n\t{\r\n\t\tctx->messagecallback(ctx->userctx, \"Rule %s is already defined\\n\", name);\r\n\t\treturn;\r\n\t}\r\n\r\n\tr = malloc(sizeof(*r));\r\n\tmemset(r, 0, sizeof(*r));\r\n\tstrcpy(r->name, name);\r\n\tr->newext = strdup(newext);\r\n\tr->command = strdup(command);\r\n\tr->dropfile = dropfile;\r\n\tr->next = ctx->rules;\r\n\tctx->rules = r;\r\n}\r\nstatic void PKG_AddClassFile(struct pkgctx_s *ctx, struct class_s *c, const char *fname, time_t mtime)\r\n{\r\n\tstruct file_s *f;\r\n\tstruct tm *t;\r\n\r\n\tif (strlen(fname) >= sizeof(f->name))\r\n\t{\r\n\t\tctx->messagecallback(ctx->userctx, \"File name '%s' too long in class %s\\n\", fname, c->name);\r\n\t\treturn;\r\n\t}\r\n\r\n\tf = malloc(sizeof(*f));\r\n\tmemset(f, 0, sizeof(*f));\r\n\tstrcpy(f->name, fname);\r\n\tf->write.timestamp = mtime;\r\n\tt = localtime(&f->write.timestamp);\r\n\tf->write.dostime = (t->tm_sec>>1)|(t->tm_min<<5)|(t->tm_hour<<11);\r\n\tf->write.dosdate = (t->tm_mday<<0)|(t->tm_mon<<5)|((t->tm_year+1900-1980)<<9);\r\n\tf->next = c->files;\r\n\tc->files = f;\r\n}\r\nstatic void PKG_AddClassFiles(struct pkgctx_s *ctx, struct class_s *c, const char *fname)\r\n{\r\n#ifdef _WIN32\r\n\tWIN32_FIND_DATA fd;\r\n\tHANDLE h;\r\n\tchar basepath[MAX_PATH];\r\n\tQCC_JoinPaths(basepath, sizeof(basepath), fname, ctx->sourcepath);\r\n\th = FindFirstFile(basepath, &fd);\r\n\tif (h == INVALID_HANDLE_VALUE)\r\n\t\tctx->messagecallback(ctx->userctx, \"wildcard string '%s' found no files\\n\", fname);\r\n\telse\r\n\t{\r\n\t\tdo\r\n\t\t{\r\n\t\t\tQCC_JoinPaths(basepath, sizeof(basepath), fd.cFileName, fname);\r\n\t\t\tPKG_AddClassFile(ctx, c, basepath, filetime_to_timet(fd.ftLastWriteTime));\r\n\t\t} while(FindNextFile(h, &fd));\r\n\t}\r\n#else\r\n\tDIR *dir;\r\n\tstruct dirent *ent;\r\n\tchar basepath[MAX_OSPATH], tmppath[MAX_OSPATH];\r\n\tstruct stat statbuf;\r\n\r\n\tQCC_JoinPaths(basepath, sizeof(basepath), fname, ctx->sourcepath);\r\n\tQC_strlcat(basepath, \"/\", sizeof(basepath));\r\n\tdir = opendir(basepath);\r\n\tif (!dir)\r\n\t{\r\n\t\tctx->messagecallback(ctx->userctx, \"unable to open dir %s\\n\", basepath);\r\n\t\treturn;\r\n\t}\r\n\twhile ((ent = readdir(dir)))\r\n\t{\r\n\t\tif (*ent->d_name == '.')\r\n\t\t\tcontinue;\r\n\t\tQCC_JoinPaths(basepath, sizeof(basepath), ent->d_name, fname);\r\n\t\tQCC_JoinPaths(tmppath, sizeof(tmppath), basepath, ctx->sourcepath);\r\n\t\tif (stat(tmppath, &statbuf)!=0)\r\n\t\t\tcontinue;\r\n\r\n\t\tswitch (statbuf.st_mode & S_IFMT)\r\n\t\t{\r\n\t\tdefault:\t//some weird file type. shouldn't be a symlink sadly.\r\n//\t\t\tctx->messagecallback(ctx->userctx, \"found weird %s\\n\", basepath);\r\n\t\t\tbreak;\r\n\t\tcase S_IFDIR:\r\n\t\t\tQC_strlcat(basepath, \"/\", sizeof(basepath));\r\n//\t\t\tctx->messagecallback(ctx->userctx, \"found dir %s\\n\", basepath);\r\n\t\t\tPKG_AddClassFiles(ctx, c, basepath);\r\n\t\t\tbreak;\r\n\t\tcase S_IFREG:\r\n//\t\t\tctx->messagecallback(ctx->userctx, \"found file %s\\n\", basepath);\r\n\t\t\tPKG_AddClassFile(ctx, c, basepath, statbuf.st_mtime);\r\n\t\t\tbreak;\r\n\t\t}\r\n\t}\r\n\tclosedir(dir);\r\n#endif\r\n}\r\nstatic void PKG_ParseClass(struct pkgctx_s *ctx, char *output)\r\n{\r\n\tstruct class_s *c;\r\n\tstruct rule_s *r;\r\n\tstruct dataset_s *s;\r\n\tchar *e;\r\n\tchar name[128];\r\n\tchar prop[128];\r\n\tsize_t u;\r\n\r\n\tif (output)\r\n\t{\r\n\t\tif (!PKG_Expect(ctx, \"{\"))\r\n\t\t\treturn;\r\n\t\t*name = 0;\r\n\t}\r\n\telse if (!PKG_GetToken(ctx, name, sizeof(name), false))\r\n\t\treturn;\r\n\r\n\tif (output || !strcmp(name, \"{\"))\r\n\t{\r\n\t\tc = malloc(sizeof(*c));\r\n\t\tmemset(c, 0, sizeof(*c));\r\n\t\tstrcpy(c->name, \"\");\r\n\t\tstrcpy(c->outname, (output && *output)?output:\"default\");\r\n\t\tc->next = ctx->classes;\r\n\t\tctx->classes = c;\r\n\t}\r\n\telse\r\n\t{\r\n\t\tif (strlen(name) >= sizeof(c->name))\r\n\t\t{\r\n\t\t\tctx->messagecallback(ctx->userctx, \"Class '%s' name too long\\n\", name);\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\tc = PKG_FindClass(ctx, name);\r\n\t\tif (!c)\r\n\t\t{\r\n\t\t\tc = malloc(sizeof(*c));\r\n\t\t\tmemset(c, 0, sizeof(*c));\r\n\t\t\tstrcpy(c->name, name);\r\n\t\t\tstrcpy(c->outname, (output && *output)?output:\"default\");\r\n\t\t\tc->next = ctx->classes;\r\n\t\t\tctx->classes = c;\r\n\t\t}\r\n\r\n\t\tif (!PKG_Expect(ctx, \"{\"))\r\n\t\t\treturn;\r\n\t}\r\n\r\n\t{\r\n\t\twhile(PKG_GetToken(ctx, prop, sizeof(prop), true))\r\n\t\t{\r\n\t\t\tif (!strcmp(prop, \"}\"))\r\n\t\t\t\tbreak;\r\n\t\t\telse if (!strcmp(prop, \"output\"))\r\n\t\t\t\tPKG_GetToken(ctx, c->outname, sizeof(c->outname), false);\r\n\t\t\telse if (!strcmp(prop, \"rule\"))\r\n\t\t\t{\r\n\t\t\t\tif (PKG_GetToken(ctx, prop, sizeof(prop), false))\r\n\t\t\t\t{\r\n\t\t\t\t\tif (c->defaultrule)\r\n\t\t\t\t\t\tctx->messagecallback(ctx->userctx, \"Class '%s' already has a default rule\\n\", name);\r\n\t\t\t\t\tc->defaultrule = PKG_FindRule(ctx, prop);\r\n\t\t\t\t\tif (!c->defaultrule)\r\n\t\t\t\t\t\tctx->messagecallback(ctx->userctx, \"Class '%s' specifies unknown rule %s\\n\", name, prop);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\te = strchr(prop, ':');\r\n\t\t\t\tif (e && !e[1])\r\n\t\t\t\t{\r\n\t\t\t\t\t*e = 0;\r\n\t\t\t\t\ts = PKG_FindDataset(ctx, prop);\r\n\t\t\t\t\tPKG_GetToken(ctx, prop, sizeof(prop), false);\r\n\t\t\t\t\tif (s)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tr = PKG_FindRule(ctx, prop);\r\n\t\t\t\t\t\tfor (u = 0; ; u++)\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tif (u == countof(c->dataset))\r\n\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\tctx->messagecallback(ctx->userctx, \"Class '%s' specialises for too many datasets\\n\", c->name, s->name);\r\n\t\t\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\tif (c->dataset[u].set == s)\r\n\t\t\t\t\t\t\t\tctx->messagecallback(ctx->userctx, \"Class '%s' already defines a rule for dataset '%s'\\n\", c->name, s->name);\r\n\t\t\t\t\t\t\telse if (!c->dataset[u].set)\r\n\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\tc->dataset[u].set = s;\r\n\t\t\t\t\t\t\t\tc->dataset[u].rule = r;\r\n\t\t\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t\telse if (strchr(prop, '.'))\r\n\t\t\t\t{\r\n//\t\t\t\t\tif (strchr(prop, '*') || strchr(prop, '?'))\r\n\t\t\t\t\t\tPKG_AddClassFiles(ctx, c, prop);\r\n//\t\t\t\t\telse\r\n//\t\t\t\t\t\tPKG_AddClassFile(ctx, c, prop);\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t\tctx->messagecallback(ctx->userctx, \"Class '%s' has unknown property '%s'\\n\", name, prop);\r\n\t\t\t}\r\n\r\n\t\t\t//skip any junk\r\n\t\t\twhile(PKG_GetToken(ctx, prop, sizeof(prop), false))\r\n\t\t\t{\r\n\t\t\t\tif (!strcmp(prop, \";\"))\r\n\t\t\t\t\tbreak;\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n}\r\nstatic void PKG_ParseClassFiles(struct pkgctx_s *ctx, struct class_s *c)\r\n{\r\n\tchar prop[128];\r\n\r\n\tif (PKG_Expect(ctx, \"{\"))\r\n\t{\r\n\t\twhile(PKG_GetToken(ctx, prop, sizeof(prop), true))\r\n\t\t{\r\n\t\t\tif (!strcmp(prop, \"}\"))\r\n\t\t\t\tbreak;\r\n\t\t\tif (!strcmp(prop, \";\"))\r\n\t\t\t\tcontinue;\r\n\r\n//\t\t\tif (strchr(prop, '*') || strchr(prop, '?'))\r\n\t\t\t\tPKG_AddClassFiles(ctx, c, prop);\r\n//\t\t\telse\r\n//\t\t\t\tPKG_AddClassFile(ctx, c, prop);\r\n\t\t}\r\n\t}\r\n}\r\n\r\n\r\n\r\n\r\n#ifdef AVAIL_ZLIB\r\n#include <zlib.h>\r\nstatic unsigned int PKG_DeflateToFile(FILE *f, unsigned int rawsize, void *in, int method)\r\n{\r\n\tchar out[8192];\r\n\tint i=0;\r\n\r\n\tz_stream strm = {\r\n\t\t(char *)in,\r\n\t\trawsize,\r\n\t\t0,\r\n\r\n\t\tout,\r\n\t\tsizeof(out),\r\n\t\t0,\r\n\r\n\t\tNULL,\r\n\t\tNULL,\r\n\r\n\t\tNULL,\r\n\t\tNULL,\r\n\t\tNULL,\r\n\r\n\t\tZ_BINARY,\r\n\t\t0,\r\n\t\t0\r\n\t};\r\n\r\n\tif (method == 8)\r\n\t\tdeflateInit2(&strm, 9, Z_DEFLATED, -MAX_WBITS, 9, Z_DEFAULT_STRATEGY);\t\t//zip deflate compression\r\n\telse\r\n\t\tdeflateInit(&strm, Z_BEST_COMPRESSION);\t//zlib compression\r\n\twhile(deflate(&strm, Z_FINISH) == Z_OK)\r\n\t{\r\n\t\tfwrite(out, 1, sizeof(out) - strm.avail_out, f);\t//compress in chunks of 8192. Saves having to allocate a huge-mega-big buffer\r\n\t\ti+=sizeof(out) - strm.avail_out;\r\n\t\tstrm.next_out = out;\r\n\t\tstrm.avail_out = sizeof(out);\r\n\t}\r\n\tdeflateEnd(&strm);\r\n\tfwrite(out, 1, sizeof(out) - strm.avail_out, f);\r\n\ti+=sizeof(out) - strm.avail_out;\r\n\treturn i;\r\n}\r\n#endif\r\n\r\n#ifdef _WIN32\r\nstatic void StupidWindowsPopenAlternativeCrap(struct pkgctx_s *ctx, char *commandline)\r\n{\r\n\tPROCESS_INFORMATION piProcInfo = {0}; \r\n\tSECURITY_ATTRIBUTES saAttr = {sizeof(SECURITY_ATTRIBUTES), NULL, TRUE};\r\n\tSTARTUPINFO siStartInfo = {sizeof(STARTUPINFO)};\r\n\tHANDLE readpipe = INVALID_HANDLE_VALUE;\r\n\tHANDLE writepipe = INVALID_HANDLE_VALUE;\r\n\tsiStartInfo.hStdError = siStartInfo.hStdOutput = siStartInfo.hStdInput = INVALID_HANDLE_VALUE;\r\n\tif (CreatePipe(&readpipe, &siStartInfo.hStdOutput, &saAttr, 0))\r\n\t{\r\n\t\tif (CreatePipe(&siStartInfo.hStdInput, &writepipe, &saAttr, 0))\r\n\t\t{\r\n\t\t\tSetHandleInformation(readpipe, HANDLE_FLAG_INHERIT, 0);\r\n\t\t\tSetHandleInformation(writepipe, HANDLE_FLAG_INHERIT, 0);\r\n\t\t\tsiStartInfo.hStdError = siStartInfo.hStdOutput;\r\n\t\t\tsiStartInfo.dwFlags |= STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW/*ZOMGWTFBBQ*/;\r\n\t\t\tif (!CreateProcess(NULL, (*commandline=='@')?commandline+1:commandline,  NULL, NULL, TRUE, 0, NULL, NULL, &siStartInfo, &piProcInfo)) \r\n\t\t\t\tctx->messagecallback(ctx->userctx, \"Unable to execute command %s\\n\", commandline);\r\n\t\t\telse \r\n\t\t\t{\r\n\t\t\t\tCloseHandle(piProcInfo.hProcess);\r\n\t\t\t\tCloseHandle(piProcInfo.hThread);\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\tCloseHandle(siStartInfo.hStdOutput);\r\n\tCloseHandle(siStartInfo.hStdInput);\r\n\r\n\tCloseHandle(writepipe);\r\n\tfor (;;) \r\n\t{ \r\n\t\tchar buf[64];\r\n\t\tDWORD SHOUTY;\r\n\t\tif (!ReadFile(readpipe, buf, sizeof(buf)-1, &SHOUTY, NULL) || SHOUTY == 0)\r\n\t\t\tbreak;\r\n\t\tif (*commandline == '@')\r\n\t\t\tcontinue;\r\n\t\tbuf[SHOUTY] = 0;\r\n\t\tctx->messagecallback(ctx->userctx, \"%s\", buf);\r\n\t}\r\n\tCloseHandle(readpipe);\r\n}\r\n#endif\r\n\r\nstatic void *PKG_OpenSourceFile(struct pkgctx_s *ctx, struct file_s *file, size_t *fsize)\r\n{\r\n\tchar fullname[1024];\r\n\tFILE *f;\r\n\tchar *data;\r\n\tsize_t size;\r\n\tstruct rule_s *rule = file->write.rule;\r\n\r\n\t*fsize = 0;\r\n\r\n\tQCC_JoinPaths(fullname, sizeof(fullname), file->name, ctx->sourcepath);\r\n\tstrcpy(file->write.name, file->name);\r\n\r\n\t//WIN32 FIXME: use the utf16 version because microsoft suck and don't allow utf-8\r\n\tf = fopen(fullname, \"rb\");\r\n\tif (!f)\r\n\t\treturn NULL;\r\n\r\n\tif (rule)\r\n\t\tctx->messagecallback(ctx->userctx, \"\\t\\tProcessing %s (%s)\\n\", file->name, rule->name);\r\n\telse\r\n\t\tctx->messagecallback(ctx->userctx, \"\\t\\tCompressing %s\\n\", file->name);\r\n\r\n\tif (rule)\r\n\t{\r\n\t\tdata = strrchr(file->write.name, '.');\r\n\t\tif (!data)\r\n\t\t\tdata = file->write.name+strlen(file->write.name);\r\n\t\tif (strchr(rule->newext, '.'))\r\n\t\t\tstrcpy(data, rule->newext);\t//note: this allows weird _foo.tga postfixes.\r\n\t\telse\r\n\t\t{\r\n\t\t\t*data = '.';\r\n\t\t\tstrcpy(data+1, rule->newext);\r\n\t\t}\r\n\r\n\t\tif (rule->command)\r\n\t\t{\r\n\t\t\tint i;\r\n\t\t\tchar commandline[4096];\r\n\t\t\tchar *cmd;\r\n\t\t\tchar tempname[1024]; \r\n\t\t\t//generate a sequenced temp filename\r\n\t\t\t//run the external tool to write that file\r\n\t\t\t//read the temp file.\r\n\t\t\t//delete temp file...\r\n\t\t\tfclose(f);\r\n\r\n\t\t\tQCC_JoinPaths(tempname, sizeof(tempname), file->write.name, ctx->sourcepath);\r\n\t\t\tf = fopen(tempname, \"rb\");\r\n\t\t\tif (f)\r\n\t\t\t{\r\n\t\t\t\tfclose(f);\r\n\t\t\t\tctx->messagecallback(ctx->userctx, \"Temp file %s already exists... not replacing+deleting\\n\", tempname);\r\n\t\t\t\treturn NULL;\r\n\t\t\t}\r\n\t\t\t\r\n\t\t\tfor (i = 0, cmd = rule->command; *cmd && i < countof(commandline)-1; )\r\n\t\t\t{\r\n\t\t\t\tif (!strncmp(cmd, \"$input\", 6))\r\n\t\t\t\t{\r\n\t\t\t\t\tstrcpy(&commandline[i], fullname);\r\n\t\t\t\t\ti += strlen(&commandline[i]);\r\n\t\t\t\t\tcmd += 6;\r\n\t\t\t\t}\r\n\t\t\t\telse if (!strncmp(cmd, \"$output\", 7))\r\n\t\t\t\t{\r\n\t\t\t\t\tstrcpy(&commandline[i], tempname);\r\n\t\t\t\t\ti += strlen(&commandline[i]);\r\n\t\t\t\t\tcmd += 7;\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t\tcommandline[i++] = *cmd++;\r\n\t\t\t}\r\n\t\t\tcommandline[i] = 0;\r\n//\t\t\tctx->messagecallback(ctx->userctx, \"Commandline is %s\\n\", commandline);\r\n\r\n\r\n#ifdef _WIN32\t\t//windows is so fucking useless sometimes. sure, _popen 'works'... its just perverse enough that its not an option, forcing system-specific crap in anything that isn't originally from unix... maybe it is just incompetence? still feels like malice to me.\r\n\t\t\tStupidWindowsPopenAlternativeCrap(ctx, commandline);\r\n#else\r\n\t\t\t{\r\n\t\t\t\tFILE *p;\r\n\t\t\t\tp = popen((*commandline=='@')?commandline+1:commandline, \"rt\");\r\n\t\t\t\tif (!p)\r\n\t\t\t\t{\r\n\t\t\t\t\tctx->messagecallback(ctx->userctx, \"Unable to execute command\\n\", tempname);\r\n\t\t\t\t\treturn NULL;\r\n\t\t\t\t}\r\n\t\t\t\twhile(fgets(commandline, sizeof(commandline), p))\r\n\t\t\t\t\tctx->messagecallback(ctx->userctx, \"%s\", commandline);\r\n\t\t\t\tif (feof(p))\r\n\t\t\t\t\tctx->messagecallback(ctx->userctx, \"Process returned %d\\n\", pclose( p ));\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\tfprintf(stderr, \"Error: Failed to read the pipe to the end.\\n\");\r\n\t\t\t\t\tpclose(p);\r\n\t\t\t\t}\r\n\t\t\t}\r\n#endif\r\n\r\n\t\t\tf = fopen(tempname, \"rb\");\r\n\t\t\tif (!f)\r\n\t\t\t{\r\n\t\t\t\tctx->messagecallback(ctx->userctx, \"Temp file %s wasn't created\\n\", tempname);\r\n\t\t\t\treturn NULL;\r\n\t\t\t}\r\n\r\n\t\t\tfseek(f, 0, SEEK_END);\r\n\t\t\tsize = ftell(f);\r\n\t\t\tfseek(f, 0, SEEK_SET);\r\n\t\t\tdata = malloc(size+1);\r\n\t\t\tfread(data, 1, size, f);\r\n\t\t\tfclose(f);\r\n\t\t\t*fsize = size;\r\n\r\n#ifdef _WIN32\r\n\t\t\t_unlink(tempname);\r\n#else\r\n\t\t\tunlink(tempname);\r\n#endif\r\n\t\t\treturn data;\r\n\t\t}\r\n\t}\r\n\r\n\r\n\tfseek(f, 0, SEEK_END);\r\n\tsize = ftell(f);\r\n\tfseek(f, 0, SEEK_SET);\r\n\tdata = malloc(size+1);\r\n\tfread(data, 1, size, f);\r\n\tfclose(f);\r\n\t*fsize = size;\r\n\r\n\treturn data;\r\n}\r\n\r\nstatic pbool PKG_WritePackageData(struct pkgctx_s *ctx, struct output_s *out, unsigned int index, pbool directoryonly)\r\n{\r\n\t//helpers to deal with misaligned data. writes little-endian.\r\n#define misbyte(ptr,ofs,data)  ((unsigned char*)(ptr))[ofs] = (data)&0xff\r\n#define misshort(ptr,ofs,data) do{misbyte((ptr),(ofs),(data));misbyte((ptr),(ofs)+1,(data)>>8);}while(0)\r\n#define misint(ptr,ofs,data)   do{misshort((ptr),(ofs),(data));misshort((ptr),(ofs)+2,(data)>>16);}while(0)\r\n#define misint64(ptr,ofs,data) do{misint((ptr),(ofs),(data));misint((ptr),(ofs)+4,((quint64_t)(data))>>32);}while(0)\r\n\tqofs_t num=0;\r\n\tpbool pak = false;\r\n\r\n\tstruct file_s *f;\r\n\tchar centralheader[46+sizeof(f->write.name)];\r\n\tqofs_t centraldirsize;\r\n\tqofs_t centraldirofs;\r\n\tqofs_t z64eocdofs;\r\n\r\n\tchar *filedata;\r\n\r\n\tFILE *outf;\r\n\tstruct\r\n\t{\r\n\t\tchar magic[4];\r\n\t\tunsigned int tabofs;\r\n\t\tunsigned int tabbytes;\r\n\t} pakheader = {\"PACK\", 0, 0};\r\n\tchar *ext;\r\n\r\n#define GPF_TRAILINGSIZE (1u<<3)\r\n#define GPF_UTF8 (1u<<11)\r\n#ifdef AVAIL_ZLIB\r\n\t#define compmethod (pak?0:8)/*Z_DEFLATED*/\r\n#else\r\n\t#define compmethod 0/*Z_RAW*/\r\n#endif\r\n\tif (!compmethod && !directoryonly && !index)\r\n\t\tpak = true; //might as well boost compat...\r\n\text = strrchr(out->filename, '.');\r\n\tif (ext && !QC_strcasecmp(ext, \".pak\") && !index)\r\n\t\tpak = true;\r\n\r\n\tif (!directoryonly)\r\n\t{\r\n\t\tfor (f = out->files; f ; f=f->write.nextwrite)\r\n\t\t{\r\n\t\t\tif (index != f->write.zdisk)\r\n\t\t\t\tcontinue;\t//not in this disk...\r\n\t\t\tbreak;\r\n\t\t}\r\n\t\tif (!f)\r\n\t\t{\r\n\t\t\tctx->messagecallback(ctx->userctx, \"\\t\\tNo files to write to %s\\n\", out->filename);\r\n\t\t\treturn false;\r\n\t\t}\r\n\t}\r\n\r\n\tif (out->usediffs && !directoryonly)\r\n\t{\r\n\t\tchar newname[MAX_OSPATH];\r\n\t\tmemcpy(newname, out->filename, sizeof(newname));\r\n\t\tif (ext)\r\n\t\t{\r\n\t\t\text = newname+(ext-out->filename);\r\n\t\t\text+=1;\r\n\t\t\tif (*ext)\r\n\t\t\t\text++;\r\n\t\t\tQC_snprintfz(ext, sizeof(newname)-(ext-newname), \"%02u\", index+1);\r\n\t\t}\r\n\t\toutf = fopen(newname, \"wb\");\r\n\t}\r\n\telse\r\n\t\toutf = fopen(out->filename, \"wb\");\r\n\tif (!outf)\r\n\t{\r\n\t\tctx->messagecallback(ctx->userctx, \"\\t\\tUnable to open %s\\n\", out->filename);\r\n\t\treturn false;\r\n\t}\r\n\r\n\tif (pak)\t//reserve space for the pak header\r\n\t\tfwrite(&pakheader, 1, sizeof(pakheader), outf);\r\n\r\n\tif (!directoryonly)\r\n\t{\r\n\t\tfor (f = out->files; f ; f=f->write.nextwrite)\r\n\t\t{\r\n\t\t\tchar header[32+sizeof(f->write.name)];\r\n\t\t\tsize_t fnamelen;\r\n\t\t\tsize_t hofs;\r\n\t\t\tunsigned short gpflags = GPF_UTF8;\r\n\r\n\t\t\tif (index != f->write.zdisk)\r\n\t\t\t\tcontinue;\t//not in this disk...\r\n\r\n\t\t\tfiledata = PKG_OpenSourceFile(ctx, f, &f->write.rawsize);\r\n\t\t\tif (!filedata)\r\n\t\t\t{\r\n\t\t\t\tctx->messagecallback(ctx->userctx, \"\\t\\tUnable to open %s\\n\", f->name);\r\n\t\t\t}\r\n\t\t\tfnamelen = strlen(f->write.name);\r\n\r\n\t\t\tf->write.zcrc = QC_encodecrc(f->write.rawsize, filedata);\r\n\t\t\tmisint  (header, 0, 0x04034b50);\r\n\t\t\tmisshort(header, 4, 45);//minver\r\n\t\t\tmisshort(header, 6, gpflags);//general purpose flags\r\n\t\t\tmisshort(header, 8, 0);//compression method, 0=store, 8=deflate\r\n\t\t\tmisshort(header, 10, f->write.dostime);//lastmodfiletime\r\n\t\t\tmisshort(header, 12, f->write.dosdate);//lastmodfiledate\r\n\t\t\tmisint  (header, 14, f->write.zcrc);//crc32\r\n\t\t\tmisint  (header, 18, f->write.rawsize);//compressed size\r\n\t\t\tmisint  (header, 22, f->write.rawsize);//uncompressed size\r\n\t\t\tmisshort(header, 26, fnamelen);//filename length\r\n\t\t\tmisshort(header, 28, 0);//extradata length (filled in later)\r\n\t\t\tmemcpy(header+30, f->write.name, fnamelen);\r\n\t\t\thofs = 30+fnamelen;\r\n\t\t\t//Write extra data here...\r\n\t\t\tmisshort(header, 28, hofs-(30+fnamelen));//extradata length\r\n\t\t\tf->write.zhdrofs = ftell(outf);\r\n\t\t\tfwrite(header, 1, hofs, outf);\r\n\r\n#ifdef AVAIL_ZLIB\r\n\t\t\tif (f->write.rawsize && (compmethod == 2 || compmethod == 8))\r\n\t\t\t{\r\n\t\t\t\tgpflags |= 1u<<1;\r\n\t\t\t\tf->write.pakofs = 0;\r\n\r\n\t\t\t\tf->write.zmethod = compmethod;\r\n\t\t\t\tf->write.zipsize = PKG_DeflateToFile(outf, f->write.rawsize, filedata, compmethod);\r\n\t\t\t}\r\n\t\t\telse\r\n#endif\r\n\t\t\t{\r\n\t\t\t\tf->write.zmethod = 0;\r\n\t\t\t\tf->write.pakofs = ftell(outf);\r\n\t\t\t\tf->write.zipsize = fwrite(filedata, 1, f->write.rawsize, outf);\r\n\t\t\t}\r\n\r\n\t\t\t//update the header\r\n\t\t\tmisshort(header, 8, f->write.zmethod);//compression method, 0=store, 8=deflate\r\n\t\t\tif (f->write.zipsize > 0xffffffff)\r\n\t\t\t{\r\n\t\t\t\tmisint  (header, 18, 0xffffffff);//compressed size\r\n\t\t\t\tgpflags |= GPF_TRAILINGSIZE;\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t\tmisint  (header, 18, f->write.zipsize);//compressed size\r\n\t\t\tif (f->write.rawsize > 0xffffffff)\r\n\t\t\t{\r\n\t\t\t\tmisint  (header, 22, 0xffffffff);//compressed size\r\n\t\t\t\tgpflags |= GPF_TRAILINGSIZE;\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t\tmisint  (header, 22, f->write.rawsize);//compressed size\r\n\t\t\tmisshort(header, 6, gpflags);//general purpose flags\r\n\r\n\t\t\tfseek(outf, f->write.zhdrofs, SEEK_SET);\r\n\t\t\tfwrite(header, 1, hofs, outf);\r\n\t\t\tfseek(outf, 0, SEEK_END);\r\n\r\n\t\t\tif (gpflags & GPF_TRAILINGSIZE) //if (gpflags & GPF_TRAILINGSIZE)\r\n\t\t\t{\r\n\t\t\t\tmisint  (header, 0, 0x08074b50);\r\n\t\t\t\tmisint  (header, 4, f->write.zcrc);\r\n\t\t\t\tmisint64(header, 8, f->write.zipsize);\r\n\t\t\t\tmisint64(header, 16, f->write.rawsize);\r\n\t\t\t\tfwrite(header, 1, 24, outf);\r\n\t\t\t}\r\n\r\n\t\t\tfree(filedata);\r\n\t\t\tnum++;\r\n\t\t}\r\n\t}\r\n\r\n\tif (pak)\r\n\t{\r\n\t\tstruct \r\n\t\t{\r\n\t\t\tchar name[56];\r\n\t\t\tunsigned int offset;\r\n\t\t\tunsigned int size;\r\n\t\t} pakentry;\r\n\t\tpakheader.tabofs = ftell(outf);\r\n\r\n\t\t//write the pak file table.\r\n\t\tfor (f = out->files,num=0; f ; f=f->write.nextwrite)\r\n\t\t{\r\n\t\t\tif (index != f->write.zdisk)\r\n\t\t\t\tcontinue;\t//not in this disk...\r\n\r\n\t\t\tmemset(&pakentry, 0, sizeof(pakentry));\r\n\t\t\tQC_strlcpy(pakentry.name, f->write.name, sizeof(pakentry.name));\r\n\t\t\tpakentry.size = (f->write.pakofs==0)?0:f->write.rawsize;\r\n\t\t\tpakentry.offset = f->write.pakofs;\r\n\t\t\tfwrite(&pakentry, 1, sizeof(pakentry), outf);\r\n\t\t\tnum++;\r\n\t\t}\r\n\r\n\t\t//replace the pak header, then return to the end of the file for the zip end-of-central-directory\r\n\t\tpakheader.tabbytes = num * sizeof(pakentry);\r\n\t\tfseek(outf, 0, SEEK_SET);\r\n\t\tfwrite(&pakheader, 1, sizeof(pakheader), outf);\r\n\t\tfseek(outf, 0, SEEK_END);\r\n\t}\r\n\r\n\tcentraldirofs = ftell(outf);\r\n\tfor (f = out->files,num=0; f ; f=f->write.nextwrite)\r\n\t{\r\n\t\tsize_t hofs;\r\n\t\tsize_t fnamelen;\r\n\t\tif (!directoryonly && index != f->write.zdisk)\r\n\t\t\tcontinue;\r\n\r\n\t\tfnamelen = strlen(f->write.name);\r\n\t\tmisint  (centralheader, 0, 0x02014b50);\r\n\t\tmisshort(centralheader, 4, (3<<8)|63);//ourver\r\n\t\tmisshort(centralheader, 6, 45);//minver\r\n\t\tmisshort(centralheader, 8, GPF_UTF8);//general purpose flags\r\n\t\tmisshort(centralheader, 10, f->write.rawsize?compmethod:0);//compression method, 0=store, 8=deflate\r\n\t\tmisshort(centralheader, 12, f->write.dostime);//lastmodfiletime\r\n\t\tmisshort(centralheader, 14, f->write.dosdate);//lastmodfiledate\r\n\t\tmisint  (centralheader, 16, f->write.zcrc);//crc32\r\n\t\tmisint  (centralheader, 20, f->write.zipsize);//compressed size\r\n\t\tmisint  (centralheader, 24, f->write.rawsize);//uncompressed size\r\n\t\tmisshort(centralheader, 28, fnamelen);//filename length\r\n\t\tmisshort(centralheader, 30, 0);//extradata length (filled in later)\r\n\t\tmisshort(centralheader, 32, 0);//comment length\r\n\t\tmisshort(centralheader, 34, f->write.zdisk);//first disk number\r\n\t\tmisshort(centralheader, 36, 0);//internal file attribs\r\n\t\tmisint  (centralheader, 38, 0);//external file attribs\r\n\t\tmisint  (centralheader, 42, f->write.zhdrofs);//local header offset\r\n\t\tstrcpy(centralheader+46, f->write.name);\r\n\r\n\t\thofs = 46+fnamelen;\r\n\t\tif (f->write.zdisk >= 0xffff || f->write.zhdrofs >= 0xffffffff || f->write.rawsize >= 0xffffffff || f->write.zipsize >= 0xffffffff)\r\n\t\t{\r\n\t\t\tmisshort(centralheader, hofs, 0x0001);//zip64 tagid\r\n\t\t\tmisshort(centralheader, hofs+2, 0x0001);//zip64 tag size\r\n\t\t\thofs+=4;\r\n\t\t\tif (f->write.rawsize >= 0xffffffff)\r\n\t\t\t{\r\n\t\t\t\tmisint64(centralheader, hofs, f->write.rawsize);//uncompressed size\r\n\t\t\t\thofs += 8;\r\n\t\t\t}\r\n\t\t\tif (f->write.zipsize >= 0xffffffff)\r\n\t\t\t{\r\n\t\t\t\tmisint64(centralheader, hofs, f->write.zipsize);//compressed size\r\n\t\t\t\thofs += 8;\r\n\t\t\t}\r\n\t\t\tif (f->write.zhdrofs >= 0xffffffff)\r\n\t\t\t{\r\n\t\t\t\tmisint64(centralheader, hofs, f->write.zhdrofs);//localheader offset\r\n\t\t\t\thofs += 8;\r\n\t\t\t}\r\n\t\t\tif (f->write.zdisk >= 0xffff)\r\n\t\t\t{\r\n\t\t\t\tmisint  (centralheader, hofs, f->write.zdisk);//compressed size\r\n\t\t\t\thofs += 4;\r\n\t\t\t}\r\n\t\t}\r\n\t\tmisshort(centralheader, 30, hofs-(46+fnamelen));//extradata length\r\n\r\n\t\tfwrite(centralheader, 1, hofs, outf);\r\n\t\tnum++;\r\n\t}\r\n\tcentraldirsize = ftell(outf)-centraldirofs;\r\n\r\n\t//zip64 end of central dir \r\n\tz64eocdofs = ftell(outf);\r\n\tmisint  (centralheader, 0, 0x06064b50);\r\n\tmisint64(centralheader, 4, (qofs_t)(56-16));\r\n\tmisshort(centralheader, 12, (3<<8)|63);\t//ver made by = unix|appnote ver\r\n\tmisshort(centralheader, 14, 45);\t//ver needed\r\n\tmisint  (centralheader, 16, index);\t//thisdisk number\r\n\tmisint  (centralheader, 20, index);\t//centraldir start disk\r\n    misint64(centralheader, 24, num);\t//centraldir entry count (disk)\r\n\tmisint64(centralheader, 32, num);\t//centraldir entry count (total)\r\n\tmisint64(centralheader, 40, centraldirsize);//centraldir entry bytes\r\n\tmisint64(centralheader, 48, centraldirofs);\t//centraldir start offset\r\n\tfwrite(centralheader, 1, 56, outf);\r\n\r\n\t//zip64 end of central dir locator \r\n\tmisint  (centralheader, 0, 0x07064b50);\r\n\tmisint  (centralheader, 4, index);\t\t//centraldir first disk\r\n\tmisint64(centralheader, 8, z64eocdofs);\r\n\tmisint  (centralheader, 16, index+1);\t\t//total disk count\r\n\tfwrite(centralheader, 1, 20, outf);\r\n\r\n//\tcentraldirofs = ftell(outf) - centraldirofs;\r\n\t//write zip end-of-central-directory\r\n\tmisint  (centralheader, 0, 0x06054b50);\r\n\tmisshort(centralheader, 4,  (index         >    0xffff)?    0xffff:index);\t//this disk number\r\n\tmisshort(centralheader, 6,  (index         >    0xffff)?    0xffff:index);\t//centraldir first disk\r\n\tmisshort(centralheader, 8,  (num           >    0xffff)?    0xffff:num);\t//centraldir entries\r\n\tmisshort(centralheader, 10, (num           >    0xffff)?    0xffff:num);\t//total centraldir entries\r\n\tmisint  (centralheader, 12, (centraldirsize>0xffffffff)?0xffffffff:centraldirsize);\t//centraldir size\r\n\tmisint  (centralheader, 16, (centraldirofs >0xffffffff)?0xffffffff:centraldirofs);\t//centraldir offset\r\n\tmisshort(centralheader, 20, 0);\t//comment length\r\n\tfwrite(centralheader, 1, 22, outf);\r\n\r\n\tfclose(outf);\r\n\r\n\treturn true;\r\n}\r\n\r\n/*\r\n#include <sys/stat.h>\r\nstatic time_t PKG_GetFileTime(const char *filename)\r\n{\r\n\tstruct stat s;\r\n\tif (stat(filename, &s) != -1)\r\n\t\treturn s.st_mtime;\r\n}\r\n*/\r\n\r\nstatic void PKG_ReadPackContents(struct pkgctx_s *ctx, struct oldpack_s *old)\r\n{\r\n#define longfromptr(p) (((p)[0]<<0)|((p)[1]<<8)|((p)[2]<<16)|((p)[3]<<24))\r\n#define shortfromptr(p) (((p)[0]<<0)|((p)[1]<<8))\r\n\tsize_t u, namelen;\r\n\tunsigned int foffset;\r\n\tunsigned char header[46];\r\n\tint i;\r\n\tFILE *f;\r\n\r\n\t//ignore packages if we're going to be overwritten.\r\n\tstruct dataset_s *set;\r\n\tstruct output_s *out;\r\n\tfor (set = ctx->datasets; set; set = set->next)\r\n\t{\r\n\t\tfor (out = set->outputs; out; out = out->next)\r\n\t\t{\r\n\t\t\tif (!strcmp(out->filename, old->filename))\r\n\t\t\t\treturn;\r\n\t\t}\r\n\t}\r\n\r\n\r\n\tf = fopen(old->filename, \"rb\");\r\n\tif (f)\r\n\t{\r\n\t\t//find end-of-central-dir\r\n\t\t//assume no comment\r\n\t\tfseek(f, -22, SEEK_END);\r\n\t\tfread(header, 1, 22, f);\r\n\r\n\t\tif (header[0] == 'P' && header[1] == 'K' && header[2] == 5 && header[3] == 6)\r\n\t\t{\r\n\t\t\told->part = shortfromptr(header+4);\r\n\t\t\t//centraldirstart = shortfromptr(header+6);\r\n\t\t\told->numfiles = shortfromptr(header+8);\r\n\t\t\t//numfiles_all = shortfromptr(header+10);\r\n\t\t\t//centaldirsize = shortfromptr(header+12);\r\n\t\t\tfoffset = longfromptr(header+16);\r\n\t\t\t//commentength = shortfromptr(header+20);\r\n\r\n\t\t\told->file = malloc(sizeof(*old->file)*old->numfiles);\r\n\r\n\r\n\t\t\tfseek(f, foffset, SEEK_SET);\r\n\t\t\tfor(u = 0; u < old->numfiles; u++)\r\n\t\t\t{\r\n\t\t\t\tunsigned int extra_len, comment_len;\r\n\t\t\t\tfread(header, 1, 46, f);\r\n\t\t\t\t//zcrc @ 16\r\n\r\n\t\t\t\t//version_madeby = shortfromptr(header+4);\r\n\t\t\t\t//version_needed = shortfromptr(header+6);\r\n\t\t\t\t//gflags = shortfromptr(header+8);\r\n\t\t\t\told->file[u].zmethod = shortfromptr(header+10);\r\n\t\t\t\told->file[u].dostime = shortfromptr(header+12);\r\n\t\t\t\told->file[u].dosdate = shortfromptr(header+14);\r\n\t\t\t\told->file[u].zcrc = longfromptr(header+16);\r\n\t\t\t\told->file[u].zipsize = longfromptr(header+20);\r\n\t\t\t\told->file[u].rawsize = longfromptr(header+24);\r\n\t\t\t\tnamelen = shortfromptr(header+28);\r\n\t\t\t\textra_len = shortfromptr(header+30);\r\n\t\t\t\tcomment_len = shortfromptr(header+32);\r\n\t\t\t\t//disknum = shortfromptr(header+34);\r\n\t\t\t\t//iattributes = shortfromptr(header+36);\r\n\t\t\t\t//eattributes = longfromptr(header+38);\r\n\t\t\t\t//localheaderoffset = longfromptr(header+42);\r\n\r\n\t\t\t\tfread(old->file[u].name, 1, namelen, f);\r\n\t\t\t\told->file[u].name[namelen] = 0;\r\n\t\t\t\ti = extra_len+comment_len;\r\n\t\t\t\tif (i)\r\n\t\t\t\t\tfseek(f, i, SEEK_CUR);\r\n\t\t\t}\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tfseek(f, 0, SEEK_SET);\r\n\t\t\tfread(header, 1, 12, f);\r\n\t\t\tif (header[0] == 'P' && header[1] == 'A' && header[2] == 'C' && header[3] == 'K')\r\n\t\t\t{\r\n\t\t\t\tunsigned int ofs = longfromptr(header+4);\r\n\t\t\t\tunsigned int dsz = longfromptr(header+8);\r\n\t\t\t\tstruct \r\n\t\t\t\t{\r\n\t\t\t\t\tchar name[56];\r\n\t\t\t\t\tunsigned int size;\r\n\t\t\t\t\tunsigned int offset;\r\n\t\t\t\t} *files;\r\n\t\t\t\tfiles = malloc(dsz);\r\n\t\t\t\tfseek(f, ofs, SEEK_SET);\r\n\t\t\t\tfread(files, 1, dsz, f);\r\n\t\t\t\told->numfiles = dsz / sizeof(*files);\r\n\t\t\t\told->file = malloc(sizeof(*old->file)*old->numfiles);\r\n\t\t\t\tfor (u = 0; u < old->numfiles; u++)\r\n\t\t\t\t{\r\n\t\t\t\t\tstrcpy(old->file[u].name, files[u].name);\r\n\t\t\t\t\told->file[u].rawsize = files[u].size;\r\n\t\t\t\t}\r\n\t\t\t\tfree(files);\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t\tctx->messagecallback(ctx->userctx, \"%s does not appear to be a package\\n\", old->filename);\r\n\t\t}\r\n\r\n\t\t//walk central directory\r\n\t\tfclose(f);\r\n\t}\r\n}\r\n\r\nstatic pbool PKG_FileIsModified(struct pkgctx_s *ctx, struct oldpack_s *old, struct file_s *file)\r\n{\r\n\tsize_t u;\r\n\r\n\tfor (u = 0; u < old->numfiles; u++)\r\n\t{\r\n\t\t//should check filesize etc, but rules and extension changes make that messy\r\n\t\tif (!strcmp(old->file[u].name, file->name))\r\n\t\t{\r\n\t\t\tif(file->write.dosdate < old->file[u].dosdate || (file->write.dosdate == old->file[u].dosdate && file->write.dostime <= old->file[u].dostime))\r\n\t\t\t{\r\n\t\t\t\tfile->write.zmethod = old->file[u].zmethod;\r\n\t\t\t\t//char name[128];\r\n\t\t\t\tfile->write.zcrc = old->file[u].zcrc;\r\n\t\t\t\tfile->write.zhdrofs = old->file[u].zhdrofs;\r\n\t\t\t\tfile->write.pakofs = 0;\r\n\t\t\t\tfile->write.rawsize = old->file[u].rawsize;\r\n\t\t\t\tfile->write.zipsize = old->file[u].zipsize;\r\n\t\t\t\tfile->write.dostime = old->file[u].dostime;\r\n\t\t\t\tfile->write.dosdate = old->file[u].dosdate;\r\n\t\t\t\treturn false;\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\treturn true;\r\n}\r\n\r\nstatic void PKG_WriteDataset(struct pkgctx_s *ctx, struct dataset_s *set)\r\n{\r\n\tstruct class_s *cls;\r\n\tstruct output_s *out;\r\n\tstruct file_s *file;\r\n\tstruct rule_s *rule;\r\n\tstruct oldpack_s *old;\r\n\tsize_t u;\r\n\r\n\tif (!ctx->readoldpacks)\r\n\t{\r\n\t\tctx->readoldpacks = true;\r\n\r\n\t\tfor(old = ctx->oldpacks; old; old = old->next)\r\n\t\t{\t//fixme: strip any wildcarded paks that match an output, to avoid weirdness.\r\n\t\t\tPKG_ReadPackContents(ctx, old);\r\n\t\t}\r\n\r\n\t\tfor (out = set->outputs; out; out = out->next)\r\n\t\t{\r\n\t\t\tif(out->usediffs)\r\n\t\t\t{\r\n\t\t\t\tfor (old = out->oldparts; old; old = old->next)\r\n\t\t\t\t{\r\n\t\t\t\t\tPKG_ReadPackContents(ctx, old);\r\n\t\t\t\t\tif (out->numparts <= old->part)\r\n\t\t\t\t\t\tout->numparts = old->part + 1;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\tctx->messagecallback(ctx->userctx, \"Building dataset %s\\n\", set->name);\r\n\r\n\tfor (cls = ctx->classes; cls; cls = cls->next)\r\n\t{\r\n\t\tfor (out = set->outputs; out; out = out->next)\r\n\t\t{\r\n\t\t\tif (!strcmp(out->code, cls->outname))\r\n\t\t\t\tbreak;\r\n\t\t}\r\n\t\tif (!out)\t//dataset doesn't name this.\r\n\t\t\tcontinue;\r\n\r\n\t\trule = cls->defaultrule;\r\n\t\tfor (u = 0; u < countof(cls->dataset); u++)\r\n\t\t{\r\n\t\t\tif (cls->dataset[u].set == set)\r\n\t\t\t{\r\n\t\t\t\trule = cls->dataset[u].rule;\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tif (rule && rule->dropfile)\r\n\t\t\tcontinue;\r\n\r\n\t\tfor (file = cls->files; file; file = file->next)\r\n\t\t{\r\n\t\t\tfor (old = ctx->oldpacks; old; old = old->next)\r\n\t\t\t{\r\n\t\t\t\tif (!PKG_FileIsModified(ctx, old, file))\r\n\t\t\t\t\tbreak;\r\n\t\t\t}\r\n\t\t\tif (old)\r\n\t\t\t{\r\n\t\t\t\tctx->messagecallback(ctx->userctx, \"\\t\\tFile %s found inside %s\\n\", file->name, old->filename);\r\n\t\t\t\tfile->write.zdisk = ~0u;\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n//\t\t\t\tctx->messagecallback(ctx->userctx, \"\\t\\tFile %s, rule %s\\n\", file->name, rule?rule->name:\"\");\r\n\r\n\t\t\t\tfile->write.zdisk = out->numparts;\r\n\r\n\t\t\t\tfor (old = out->oldparts; old; old = old->next)\r\n\t\t\t\t{\r\n\t\t\t\t\tif (!PKG_FileIsModified(ctx, old, file))\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tfile->write.zdisk = old->part;\r\n\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\r\n\t\t\t\tfile->write.nextwrite = out->files;\r\n\t\t\t\tfile->write.rule = rule;\r\n\t\t\t\tout->files = file;\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\tfor (out = set->outputs; out; out = out->next)\r\n\t{\r\n\t\tif (!out->files)\r\n\t\t{\r\n\t\t\tctx->messagecallback(ctx->userctx, \"\\tOutput %s[%s] \\\"%s\\\" has no files\\n\", out->code, set->name, out->filename);\r\n\t\t\tcontinue;\r\n\t\t}\r\n\r\n\t\tif (ctx->test)\r\n\t\t{\r\n\t\t\tfor (file = out->files; file; file = file->write.nextwrite)\r\n\t\t\t{\r\n\t\t\t\tif (file->write.rule)\r\n\t\t\t\t\tctx->messagecallback(ctx->userctx, \"\\t\\tFile %s has changed (rule %s)\\n\", file->name, file->write.rule->name);\r\n\t\t\t\telse\r\n\t\t\t\t\tctx->messagecallback(ctx->userctx, \"\\t\\tFile %s has changed\\n\", file->name);\r\n\t\t\t}\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tctx->messagecallback(ctx->userctx, \"\\tGenerating %s[%s] \\\"%s\\\"\\n\", out->code, set->name, out->filename);\r\n\t\t\tif (PKG_WritePackageData(ctx, out, out->numparts, false))\r\n\t\t\t{\r\n\t\t\t\tif(out->usediffs)\r\n\t\t\t\t\tPKG_WritePackageData(ctx, out, out->numparts+1, true);\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n}\r\nvoid Packager_WriteDataset(struct pkgctx_s *ctx, char *setname)\r\n{\r\n\tstruct dataset_s *dataset;\r\n\tif (setname && strcmp(setname, \"*\"))\r\n\t{\r\n\t\tdataset = PKG_FindDataset(ctx, setname);\r\n\t\tif (dataset)\r\n\t\t\tPKG_WriteDataset(ctx, dataset);\r\n\t\telse\r\n\t\t\tctx->messagecallback(ctx->userctx, \"Dataset %s not known\\n\", setname);\r\n\t}\r\n\telse\r\n\t{\r\n\t\tfor (dataset = ctx->datasets; dataset; dataset = dataset->next)\r\n\t\t\tPKG_WriteDataset(ctx, dataset);\r\n\t}\r\n}\r\nstruct pkgctx_s *Packager_Create(void (*messagecallback)(void *userctx, const char *message, ...), void *userctx)\r\n{\r\n\tstruct pkgctx_s *ctx;\r\n\tctx = malloc(sizeof(*ctx));\r\n\tmemset(ctx, 0, sizeof(*ctx));\r\n\tctx->messagecallback = messagecallback;\r\n\tctx->userctx = userctx;\r\n\tctx->test = false;\r\n\ttime(&ctx->buildtime);\r\n\treturn ctx;\r\n}\r\nvoid Packager_ParseText(struct pkgctx_s *ctx, char *scripttext)\r\n{\r\n\tchar cmd[128];\r\n\r\n\tctx->listfile = scripttext;\r\n\twhile (PKG_GetToken(ctx, cmd, sizeof(cmd), true))\r\n\t{\r\n//\t\tif (!strcmp(cmd, \"dataset\"))\r\n//\t\t\tPKG_ParseDataset(ctx);\r\n\t\tif (!strcmp(cmd, \"output\"))\r\n\t\t\tPKG_ParseOutput(ctx, false);\r\n\t\telse if (!strcmp(cmd, \"diffoutput\") || !strcmp(cmd, \"splitoutput\"))\r\n\t\t\tPKG_ParseOutput(ctx, true);\r\n\t\telse if (!strcmp(cmd, \"inputdir\"))\r\n\t\t{\r\n\t\t\tchar old[MAX_OSPATH];\r\n\t\t\tmemcpy(old, ctx->sourcepath, sizeof(old));\r\n\t\t\tif (PKG_GetStringToken(ctx, cmd, sizeof(cmd)))\r\n\t\t\t{\r\n\t\t\t\tQC_strlcat(cmd, \"/\", sizeof(cmd));\r\n\t\t\t\tQCC_JoinPaths(ctx->sourcepath, sizeof(ctx->sourcepath), cmd, old);\r\n\t\t\t}\r\n\t\t}\r\n\t\telse if (!strcmp(cmd, \"rule\"))\r\n\t\t\tPKG_ParseRule(ctx);\r\n\t\telse if (!strcmp(cmd, \"class\"))\r\n\t\t\tPKG_ParseClass(ctx, NULL);\r\n\t\telse if (!strcmp(cmd, \"ignore\")||!strcmp(cmd, \"oldpack\"))\r\n\t\t\tPKG_ParseOldPack(ctx);\r\n\t\telse\r\n\t\t{\r\n\t\t\tchar *e = strchr(cmd, ':');\r\n\t\t\tif (e && !e[1])\r\n\t\t\t{\r\n\t\t\t\t*e = 0;\r\n\t\t\t\tPKG_ParseClass(ctx, cmd);\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tstruct class_s *c = PKG_FindClass(ctx, cmd);\r\n\t\t\t\tif (c)\r\n\t\t\t\t\tPKG_ParseClassFiles(ctx, c);\r\n\t\t\t\telse\r\n\t\t\t\t\tctx->messagecallback(ctx->userctx, \"Unrecognised token at global scope '%s'\\n\", cmd);\r\n\t\t\t}\r\n\t\t}\r\n\t\t//skip any junk\r\n\t\twhile(PKG_GetToken(ctx, cmd, sizeof(cmd), false))\r\n\t\t{\r\n\t\t\tif (!strcmp(cmd, \";\"))\r\n\t\t\t\tbreak;\r\n\t\t}\r\n\t}\r\n}\r\n\r\nvoid Packager_ParseFile(struct pkgctx_s *ctx, char *scriptname)\r\n{\r\n\tsize_t remaining = 0;\r\n\tchar *file = qccprogfuncs->funcs.parms->ReadFile(scriptname, NULL, NULL, &remaining, true);\r\n\tstrcpy(ctx->gamepath, scriptname);\r\n\tstrcpy(ctx->sourcepath, scriptname);\r\n\tPackager_ParseText(ctx, file);\r\n\tfree(file);\r\n}\r\n\r\nvoid Packager_Destroy(struct pkgctx_s *ctx)\r\n{\r\n\tfree(ctx);\r\n}\r\n\r\npbool\t\t\tPackager_CompressDir(const char *dirname, enum pkgtype_e type, void (*messagecallback)(void *userctx, const char *message, ...), void *userctx)\r\n{\r\n\tchar *ext;\r\n\tchar filename[MAX_QPATH];\r\n\tstruct pkgctx_s *ctx = Packager_Create(messagecallback, userctx);\r\n\tstruct dataset_s *s;\r\n\tstruct class_s *c;\r\n\tQC_strlcpy(ctx->sourcepath, dirname, sizeof(ctx->sourcepath));\r\n\text = strrchr(ctx->sourcepath, '/');\r\n\tif (*ctx->sourcepath && (!ext || ext[1]))\r\n\t\tQC_strlcat(ctx->sourcepath, \"/\", sizeof(ctx->sourcepath));\r\n\r\n\tQC_strlcpy(filename, dirname, sizeof(filename));\r\n\tfor (;(ext = strrchr(filename, '/')) && !ext[1]; *ext = 0)\r\n\t\t;\r\n\text = strrchr(filename, '.');\r\n\tif (ext)\r\n\t\t*ext = 0;\r\n\tif (type == PACKAGER_PAK)\r\n\t\tQC_strlcat(filename, \".pak\", sizeof(filename));\r\n\telse\r\n\t\tQC_strlcat(filename, \".pk3\", sizeof(filename));\r\n\r\n\ts = PKG_GetDataset(ctx, \"default\");\r\n\tPKG_CreateOutput(ctx, s, \"default\", filename, type == PACKAGER_PK3_SPANNED);\r\n\r\n\tc = malloc(sizeof(*c));\r\n\tmemset(c, 0, sizeof(*c));\r\n\tstrcpy(c->name, \"file\");\r\n\tstrcpy(c->outname, \"default\");\r\n\tc->next = ctx->classes;\r\n\tctx->classes = c;\r\n\tPKG_AddClassFiles(ctx, c, \"\");\r\n\r\n\tPackager_WriteDataset(ctx, NULL);\r\n\tPackager_Destroy(ctx);\r\n\r\n\treturn true;\r\n}\r\n#endif\r\n"
  },
  {
    "path": "engine/qclib/pr_comp.h",
    "content": "// this file is shared by the execution and compiler\n\n/*i'm part way through making this work\nI've given up now that I can't work out a way to load pointers.\nSetting them should be fine.\n*/\n#ifndef __PR_COMP_H__\n#define __PR_COMP_H__\n#include \"progtype.h\"\n\n/*\n#ifdef USE_MSVCRT_DEBUG\nvoid *BZ_MallocNamed(int size, char *file, int line);\nvoid *BZ_ReallocNamed(void *data, int newsize, char *file, int line);\nvoid BZ_Free(void *data);\n#define BZ_Malloc(size) BZ_MallocNamed(size, __FILE__, __LINE__)\n#define BZ_Realloc(ptr, size) BZ_ReallocNamed(ptr, size, __FILE__, __LINE__)\n#define malloc BZ_Malloc\n#define realloc BZ_Realloc\n#define free BZ_Free\n#endif\n*/\n\ntypedef int dstring_t;\n#define QCC_string_t dstring_t\n\n#if defined(_MSC_VER) && _MSC_VER < 1300\n#define prclocks_t unsigned __int64\n#define ull2dbl(x)\t((double)(__int64)x)\n#else\n#define prclocks_t unsigned long long\n#define ull2dbl(x) ((double)x)\n#endif\n\n//typedef enum {ev_void, ev_string, ev_float, ev_vector, ev_entity, ev_field, ev_function, ev_pointer, ev_integer, ev_struct, ev_union} etype_t;\n//\t\t\t\t0\t\t\t1\t\t2\t\t\t3\t\t\t4\t\t5\t\t\t6\t\t\t\t7\t\t\t8\t\t9\t\t\t10\n\n#define\tOFS_NULL\t\t0\n#define\tOFS_RETURN\t\t1\n#define\tOFS_PARM0\t\t4\t\t// leave 3 ofs for each parm to hold vectors\n#define\tOFS_PARM1\t\t7\n#define\tOFS_PARM2\t\t10\n#define\tOFS_PARM3\t\t13\n#define\tOFS_PARM4\t\t16\n#define\tOFS_PARM5\t\t19\n#define\tOFS_PARM6\t\t22\n#define\tOFS_PARM7\t\t25\n#define\tRESERVED_OFS\t28\n\n\nenum qcop_e {\n\tOP_DONE,\t//0\n\tOP_MUL_F,\n\tOP_MUL_V,\n\tOP_MUL_FV,\n\tOP_MUL_VF,\n\tOP_DIV_F,\n\tOP_ADD_F,\n\tOP_ADD_V,\n\tOP_SUB_F,\n\tOP_SUB_V,\n\t\n\tOP_EQ_F,\t//10\n\tOP_EQ_V,\n\tOP_EQ_S,\n\tOP_EQ_E,\n\tOP_EQ_FNC,\n\t\n\tOP_NE_F,\n\tOP_NE_V,\n\tOP_NE_S,\n\tOP_NE_E,\n\tOP_NE_FNC,\n\t\n\tOP_LE_F,\t//20\n\tOP_GE_F,\n\tOP_LT_F,\n\tOP_GT_F,\n\n\tOP_LOAD_F,\n\tOP_LOAD_V,\n\tOP_LOAD_S,\n\tOP_LOAD_ENT,\n\tOP_LOAD_FLD,\n\tOP_LOAD_FNC,\n\n\tOP_ADDRESS,\t//30\n\n\tOP_STORE_F,\n\tOP_STORE_V,\n\tOP_STORE_S,\n\tOP_STORE_ENT,\n\tOP_STORE_FLD,\n\tOP_STORE_FNC,\n\n\tOP_STOREP_F,\n\tOP_STOREP_V,\n\tOP_STOREP_S,\n\tOP_STOREP_ENT,\t//40\n\tOP_STOREP_FLD,\n\tOP_STOREP_FNC,\n\n\tOP_RETURN,\n\tOP_NOT_F,\n\tOP_NOT_V,\n\tOP_NOT_S,\n\tOP_NOT_ENT,\n\tOP_NOT_FNC,\n\tOP_IF_I,\n\tOP_IFNOT_I,\t\t//50\n\tOP_CALL0,\t\t//careful... hexen2 and q1 have different calling conventions\n\tOP_CALL1,\t\t//remap hexen2 calls to OP_CALL2H\n\tOP_CALL2,\n\tOP_CALL3,\n\tOP_CALL4,\n\tOP_CALL5,\n\tOP_CALL6,\n\tOP_CALL7,\n\tOP_CALL8,\n\tOP_STATE,\t\t//60\n\tOP_GOTO,\n\tOP_AND_F,\n\tOP_OR_F,\n\t\n\tOP_BITAND_F,\n\tOP_BITOR_F,\n\n\t\n\t//these following ones are Hexen 2 constants.\n\t\n\tOP_MULSTORE_F,\t//66 redundant, for h2 compat\n\tOP_MULSTORE_VF,\t//67 redundant, for h2 compat\n\tOP_MULSTOREP_F,\t//68\n\tOP_MULSTOREP_VF,//69\n\n\tOP_DIVSTORE_F,\t//70 redundant, for h2 compat\n\tOP_DIVSTOREP_F,\t//71\n\n\tOP_ADDSTORE_F,\t//72 redundant, for h2 compat\n\tOP_ADDSTORE_V,\t//73 redundant, for h2 compat\n\tOP_ADDSTOREP_F,\t//74\n\tOP_ADDSTOREP_V,\t//75\n\n\tOP_SUBSTORE_F,\t//76 redundant, for h2 compat\n\tOP_SUBSTORE_V,\t//77 redundant, for h2 compat\n\tOP_SUBSTOREP_F,\t//78\n\tOP_SUBSTOREP_V,\t//79\n\n\tOP_FETCH_GBL_F,\t//80 has built-in bounds check\n\tOP_FETCH_GBL_V,\t//81 has built-in bounds check\n\tOP_FETCH_GBL_S,\t//82 has built-in bounds check\n\tOP_FETCH_GBL_E,\t//83 has built-in bounds check\n\tOP_FETCH_GBL_FNC,//84 has built-in bounds check\n\n\tOP_CSTATE,\t\t//85\n\tOP_CWSTATE,\t\t//86\n\n\tOP_THINKTIME,\t//87 shortcut for OPA.nextthink=time+OPB\n\n\tOP_BITSETSTORE_F,\t//88 redundant, for h2 compat\n\tOP_BITSETSTOREP_F,\t//89\n\tOP_BITCLRSTORE_F,\t//90\n\tOP_BITCLRSTOREP_F,\t//91\n\n\tOP_RAND0,\t\t//92\tOPC = random()\n\tOP_RAND1,\t\t//93\tOPC = random()*OPA\n\tOP_RAND2,\t\t//94\tOPC = random()*(OPB-OPA)+OPA\n\tOP_RANDV0,\t\t//95\t//3d/box versions of the above.\n\tOP_RANDV1,\t\t//96\n\tOP_RANDV2,\t\t//97\n\n\tOP_SWITCH_F,\t//98\tswitchref=OPA; PC += OPB   --- the jump allows the jump table (such as it is) to be inserted after the block.\n\tOP_SWITCH_V,\t//99\n\tOP_SWITCH_S,\t//100\n\tOP_SWITCH_E,\t//101\n\tOP_SWITCH_FNC,\t//102\n\n\tOP_CASE,\t\t//103\tif (OPA===switchref) PC += OPB\n\tOP_CASERANGE,\t//104   if (OPA<=switchref&&switchref<=OPB) PC += OPC\n\n\n\n\n\n\t//the rest are added\n\t//mostly they are various different ways of adding two vars with conversions.\n\n\t//hexen2 calling convention (-TH2 requires us to remap OP_CALLX to these on load, -TFTE just uses these directly.)\n\tOP_CALL1H,\t//OFS_PARM0=OPB\n\tOP_CALL2H,\t//OFS_PARM0,1=OPB,OPC\n\tOP_CALL3H,\t//no extra args\n\tOP_CALL4H,\n\tOP_CALL5H,\n\tOP_CALL6H,\t\t//110\n\tOP_CALL7H,\n\tOP_CALL8H,\n\n\n\tOP_STORE_I,\n\tOP_STORE_IF,\t\t\t//OPB.f = (float)OPA.i (makes more sense when written as a->b)\n\tOP_STORE_FI,\t\t\t//OPB.i = (int)OPA.f\n\t\n\tOP_ADD_I,\n\tOP_ADD_FI,\t\t\t\t//OPC.f = OPA.f + OPB.i\n\tOP_ADD_IF,\t\t\t\t//OPC.f = OPA.i + OPB.f\t-- redundant...\n  \n\tOP_SUB_I,\t\t\t\t//OPC.i = OPA.i - OPB.i\n\tOP_SUB_FI,\t\t//120\t//OPC.f = OPA.f - OPB.i\n\tOP_SUB_IF,\t\t\t\t//OPC.f = OPA.i - OPB.f\n\n\tOP_CONV_ITOF,\t\t\t//OPC.f=(float)OPA.i -- useful mostly so decompilers don't do weird stuff.\n\tOP_CONV_FTOI,\t\t\t//OPC.i=(int)OPA.f\n\tOP_LOADP_ITOF,\t\t\t\t//OPC.f=(float)(*OPA).i\t-- fixme: rename to LOADP_ITOF\n\tOP_LOADP_FTOI,\t\t\t\t//OPC.i=(int)(*OPA).f\n\tOP_LOAD_I,\n\tOP_STOREP_I,\n\tOP_STOREP_IF,\n\tOP_STOREP_FI,\n\n\tOP_BITAND_I,\t//130\n\tOP_BITOR_I,\n\n\tOP_MUL_I,\n\tOP_DIV_I,\n\tOP_EQ_I,\n\tOP_NE_I,\n\n\tOP_IFNOT_S,\t//compares string empty, rather than just null.\n\tOP_IF_S,\n\n\tOP_NOT_I,\n\n\tOP_DIV_VF,\n\n\tOP_BITXOR_I,\t\t//140\n\tOP_RSHIFT_I,\n\tOP_LSHIFT_I,\n\n\tOP_GLOBALADDRESS,\t//C.p = &A + B.i*4\n\tOP_ADD_PIW,\t\t\t//C.p = A.p + B.i*4\n\n\tOP_LOADA_F,\n\tOP_LOADA_V,\t\n\tOP_LOADA_S,\n\tOP_LOADA_ENT,\n\tOP_LOADA_FLD,\n\tOP_LOADA_FNC,\t//150\n\tOP_LOADA_I,\n\n\tOP_STORE_P,\n\tOP_LOAD_P,\n\n\tOP_LOADP_F,\n\tOP_LOADP_V,\t\n\tOP_LOADP_S,\n\tOP_LOADP_ENT,\n\tOP_LOADP_FLD,\n\tOP_LOADP_FNC,\n\tOP_LOADP_I,\t\t//160\n\n\tOP_LE_I,\n\tOP_GE_I,\n\tOP_LT_I,\n\tOP_GT_I,\n\n\tOP_LE_IF,\n\tOP_GE_IF,\n\tOP_LT_IF,\n\tOP_GT_IF,\n\n\tOP_LE_FI,\n\tOP_GE_FI,\t\t//170\n\tOP_LT_FI,\n\tOP_GT_FI,\n\n\tOP_EQ_IF,\n\tOP_EQ_FI,\n\n\t//-------------------------------------\n\t//string manipulation.\n\tOP_ADD_SF,\t//(char*)c = (char*)a + (float)b    add_fi->i\n\tOP_SUB_S,\t//(float)c = (char*)a - (char*)b    sub_ii->f\n\tOP_STOREP_C,//(float)c = *(char*)b = (float)a\n\tOP_LOADP_C,\t//(float)c = *(char*)\n\t//-------------------------------------\n\n\n\tOP_MUL_IF,\n\tOP_MUL_FI,\t\t//180\n\tOP_MUL_VI,\n\tOP_MUL_IV,\n\tOP_DIV_IF,\n\tOP_DIV_FI,\n\tOP_BITAND_IF,\n\tOP_BITOR_IF,\n\tOP_BITAND_FI,\n\tOP_BITOR_FI,\n\tOP_AND_I,\n\tOP_OR_I,\t\t//190\n\tOP_AND_IF,\n\tOP_OR_IF,\n\tOP_AND_FI,\n\tOP_OR_FI,\n\tOP_NE_IF,\n\tOP_NE_FI,\n\n//fte doesn't really model two separate pointer types. these are thus special-case things for array access only.\n\tOP_GSTOREP_I,\n\tOP_GSTOREP_F,\n\tOP_GSTOREP_ENT,\n\tOP_GSTOREP_FLD,\t\t//200\n\tOP_GSTOREP_S,\n\tOP_GSTOREP_FNC,\t\t\n\tOP_GSTOREP_V,\n\tOP_GADDRESS,\t//poorly defined opcode, which makes it too unreliable to actually use.\n\tOP_GLOAD_I,\n\tOP_GLOAD_F,\n\tOP_GLOAD_FLD,\n\tOP_GLOAD_ENT,\n\tOP_GLOAD_S,\n\tOP_GLOAD_FNC,\t\t//210\n\n//back to ones that we do use.\n\tOP_BOUNDCHECK,\n\tOP_UNUSED,\t//used to be OP_STOREP_P, which is now emulated with OP_STOREP_I, fteqcc nor fte generated it\n\tOP_PUSH,\t//push 4octets onto the local-stack (which is ALWAYS poped on function return). Returns a pointer.\n\tOP_POP,\t\t//pop those ones that were pushed (don't over do it). Needs assembler.\n\n\tOP_SWITCH_I,//hmm.\n\tOP_GLOAD_V,\n//r3349+\n\tOP_IF_F,\t\t//compares as an actual float, instead of treating -0 as positive.\n\tOP_IFNOT_F,\n\n//r5697+\n\tOP_STOREF_V,\t//3 elements...\n\tOP_STOREF_F,\t//1 fpu element...\n\tOP_STOREF_S,\t//1 string reference\n\tOP_STOREF_I,\t//1 non-string reference/int\n\n//r5744+\n\tOP_STOREP_I8,\t//((char*)b)[(int)c] = (int)a\n\tOP_LOADP_U8,\t//(int)c = *(unsigned char*)\n\n//r5768+\n//opcodes for 32bit uints\n\tOP_LE_U,\t\t//aka GE\n\tOP_LT_U,\t\t//aka GT\n\tOP_DIV_U,\t\t//don't need mul+add+sub\n\tOP_RSHIFT_U,\t//lshift is the same for signed+unsigned\n\n//opcodes for 64bit ints\n\tOP_ADD_I64,\n\tOP_SUB_I64,\n\tOP_MUL_I64,\n\tOP_DIV_I64,\n\tOP_BITAND_I64,\n\tOP_BITOR_I64,\n\tOP_BITXOR_I64,\n\tOP_LSHIFT_I64I,\n\tOP_RSHIFT_I64I,\n\tOP_LE_I64,\t\t//aka GE\n\tOP_LT_I64,\t\t//aka GT\n\tOP_EQ_I64,\n\tOP_NE_I64,\n//extra opcodes for 64bit uints\n\tOP_LE_U64,\t\t//aka GE\n\tOP_LT_U64,\t\t//aka GT\n\tOP_DIV_U64,\n\tOP_RSHIFT_U64I,\n\n//general 64bitness\n\tOP_STORE_I64,\n\tOP_STOREP_I64,\n\tOP_STOREF_I64,\n\tOP_LOAD_I64,\n\tOP_LOADA_I64,\n\tOP_LOADP_I64,\n//various conversions for our 64bit types (yay type promotion)\n\tOP_CONV_UI64, //zero extend\n\tOP_CONV_II64, //sign extend\n\tOP_CONV_I64I,\t//truncate\n\tOP_CONV_FD,\t//extension\n\tOP_CONV_DF,\t//truncation\n\tOP_CONV_I64F,\t//logically a promotion (always signed)\n\tOP_CONV_FI64,\t//demotion (always signed)\n\tOP_CONV_I64D,\t//'promotion' (always signed)\n\tOP_CONV_DI64,\t//demotion (always signed)\n\n//opcodes for doubles.\n\tOP_ADD_D,\n\tOP_SUB_D,\n\tOP_MUL_D,\n\tOP_DIV_D,\n\tOP_LE_D,\n\tOP_LT_D,\n\tOP_EQ_D,\n\tOP_NE_D,\n\n//r6614+\n\tOP_STOREP_I16, //((short*)b)[(int)c] = (int)a\n\tOP_LOADP_I16,\t//(int)c = *(signed short*)a   (sign extends)\n\tOP_LOADP_U16,\t//(unsigned int)c = *(unsigned short*)a\n\tOP_LOADP_I8,\t//(unsigned int)c = *(signed char*)a\t(sign extends)\n\tOP_BITEXTEND_I,\t//sign extend (for signed bitfields)\n\tOP_BITEXTEND_U,\t//zero extend (for unsigned bitfields)\n\tOP_BITCOPY_I,\t//copy lower bits from the input to some part of the output\n\tOP_CONV_UF,\t//OPC.f=(float)OPA.i -- 0xffffffffu*0.5=0 otherwise.\n\tOP_CONV_FU,\t//OPC.i=(int)OPA.f\n\tOP_CONV_U64D,\t//OPC.d=(double)OPA.u64 -- useful mostly so decompilers don't do weird stuff.\n\tOP_CONV_DU64,\t//OPC.u64=(uint64_t)OPA.d\n\tOP_CONV_U64F,\t//OPC.f=(float)OPA.u64 -- useful mostly so decompilers don't do weird stuff.\n\tOP_CONV_FU64,\t//OPC.u64=(uint64_t)OPA.f\n\n\tOP_NUMREALOPS,\n\n\t/*\n\tThese ops are emulated out, always, and are only present in the compiler.\n\t*/\n\n\tOP_BITSETSTORE_I,\t//220\n\tOP_BITSETSTOREP_I,\n\tOP_BITCLRSTORE_I,\n\n\tOP_MULSTORE_I,\n\tOP_DIVSTORE_I,\n\tOP_ADDSTORE_I,\n\tOP_SUBSTORE_I,\n\tOP_MULSTOREP_I,\n\tOP_DIVSTOREP_I,\n\tOP_ADDSTOREP_I,\n\tOP_SUBSTOREP_I,\t//230\n\n\tOP_MULSTORE_IF,\n\tOP_MULSTOREP_IF,\n\tOP_DIVSTORE_IF,\n\tOP_DIVSTOREP_IF,\n\tOP_ADDSTORE_IF,\n\tOP_ADDSTOREP_IF,\n\tOP_SUBSTORE_IF,\n\tOP_SUBSTOREP_IF,\n\n\tOP_MULSTORE_FI,\n\tOP_MULSTOREP_FI,\t//240\n\tOP_DIVSTORE_FI,\n\tOP_DIVSTOREP_FI,\n\tOP_ADDSTORE_FI,\n\tOP_ADDSTOREP_FI,\n\tOP_SUBSTORE_FI,\n\tOP_SUBSTOREP_FI,\n\n\tOP_MULSTORE_VI,\n\tOP_MULSTOREP_VI,\n\n\tOP_LOADA_STRUCT,\n\tOP_LOADP_P,\n\tOP_STOREP_P,\n\n\tOP_BITNOT_F,\n\tOP_BITNOT_I,\n\n\tOP_EQ_P,\n\tOP_NE_P,\n\tOP_LE_P,\n\tOP_GE_P,\n\tOP_LT_P,\n\tOP_GT_P,\n\n\tOP_ANDSTORE_F,\n\tOP_BITCLR_F,\n\tOP_BITCLR_I,\n\tOP_BITCLR_V,\n\n\tOP_ADD_SI,\n\tOP_ADD_IS,\n\tOP_ADD_PF,\n\tOP_ADD_FP,\n\tOP_ADD_PI,\n\tOP_ADD_IP,\n\tOP_ADD_PU,\n\tOP_ADD_UP,\n\n\tOP_SUB_SI,\n\tOP_SUB_PF,\n\tOP_SUB_PI,\n\tOP_SUB_PU,\n\n\tOP_SUB_PP,\n\n\tOP_MOD_F,\n\tOP_MOD_I,\n\tOP_MOD_FI,\n\tOP_MOD_IF,\n\tOP_MOD_V,\n\n\tOP_BITXOR_F,\n\tOP_RSHIFT_F,\n\tOP_LSHIFT_F,\n\tOP_RSHIFT_IF,\n\tOP_LSHIFT_IF,\n\tOP_RSHIFT_FI,\n\tOP_LSHIFT_FI,\n\n\tOP_AND_ANY,\n\tOP_OR_ANY,\n\n\tOP_ADD_EI,\n\tOP_ADD_EF,\n\tOP_SUB_EI,\n\tOP_SUB_EF,\n\n\tOP_BITAND_V,\n\tOP_BITOR_V,\n\tOP_BITNOT_V,\n\tOP_BITXOR_V,\n\n\tOP_POW_F,\n\tOP_POW_I,\n\tOP_POW_FI,\n\tOP_POW_IF,\n\tOP_CROSS_V,\n\n\tOP_EQ_FLD,\n\tOP_NE_FLD,\n\n\tOP_SPACESHIP_F,\t//lame\n\tOP_SPACESHIP_S,\t//basically strcmp.\n\n\n\t//uint32 opcodes. they match the int32 ones so emulation is basically swapping them over.\n\tOP_ADD_U,\n\tOP_SUB_U,\n\tOP_MUL_U,\n\tOP_MOD_U,\t//complex\n\tOP_BITAND_U,\n\tOP_BITOR_U,\n\tOP_BITXOR_U,\n\tOP_BITNOT_U,\t//BITXOR ~0\n\tOP_BITCLR_U,\n\tOP_LSHIFT_U,\t//same as signed (unlike rshift)\n\tOP_GE_U,\t//LT_U\n\tOP_GT_U,\t//LE_U\n//\tOP_AND_U,\n//\tOP_OR_U,\n\tOP_EQ_U,\n\tOP_NE_U,\n\n\t//uint64 opcodes. they match the int32 ones so emulation is basically swapping them over.\n\tOP_BITNOT_I64,\t//BITXOR ~0\n\tOP_BITCLR_I64,\n\tOP_GE_I64,\t//LE_I64\n\tOP_GT_I64,\t//LT_I64\n\n\tOP_ADD_U64,\n\tOP_SUB_U64,\n\tOP_MUL_U64,\n\tOP_MOD_U64,\t//complex\n\tOP_BITAND_U64,\n\tOP_BITOR_U64,\n\tOP_BITXOR_U64,\n\tOP_BITNOT_U64,\t//BITXOR ~0\n\tOP_BITCLR_U64,\n\tOP_LSHIFT_U64I,\n\tOP_GE_U64,\t//LE_U64\n\tOP_GT_U64,\t//LT_U64\n\tOP_EQ_U64,\n\tOP_NE_U64,\n\n\t//generally implemented by forcing to int64.\n\tOP_BITAND_D,\n\tOP_BITOR_D,\n\tOP_BITXOR_D,\n\tOP_BITNOT_D,\n\tOP_BITCLR_D,\n\tOP_LSHIFT_DI,\n\tOP_RSHIFT_DI,\n\tOP_GE_D,\t//LE_D\n\tOP_GT_D,\t//LT_D\n\n\tOP_WSTATE,\t//for the 'w' part of CWSTATE. will probably never be used, but hey, hexen2...\n\n\t//special/fake opcodes used by the decompiler.\n\tOPD_GOTO_FORSTART,\n\tOPD_GOTO_WHILE1,\n\tOPD_GOTO_BREAK,\n\tOPD_GOTO_DEFAULT,\n\n\tOP_NUMOPS,\n#define OP_BIT_BREAKPOINT 0x8000\n};\n\n#define\tMAX_PARMS\t8\n\n// qtest structs (used for reordering and not execution)\ntypedef struct qtest_statement_s\n{\n\tunsigned int\tline; // line number in source code file\n\tunsigned short\top;\n\tunsigned short\ta,b,c;\n} qtest_statement_t;\n\ntypedef struct qtest_def_s\n{\n\tunsigned int\ttype; // no DEFGLOBAL found in qtest progs\n\tunsigned int\ts_name; // different order!\n\tunsigned int\tofs;\n} qtest_def_t;\n\ntypedef struct qtest_function_s\n{\n\tint\t\tfirst_statement;\n\tint\t\tunused1;\n\tint\t\tlocals;\t// assumed! (always 0 in real qtest progs)\n\tint\t\tprofile; // assumed! (always 0 in real qtest progs)\n\n\tint\t\ts_name;\n\tint\t\ts_file;\n\n\tint\t\tnumparms;\n\tint\t\tparm_start; // different order\n\tint\t\tparm_size[MAX_PARMS]; // ints instead of bytes...\n} qtest_function_t;\n\ntypedef struct statement16_s\n{\n\tunsigned short\top;\n\tunsigned short\ta,b,c;\n} dstatement16_t;\ntypedef struct statement32_s\n{\n\tunsigned int\top;\n\tunsigned int\ta,b,c;\n} dstatement32_t;\n#define QCC_dstatement16_t dstatement16_t\n#define QCC_dstatement32_t dstatement32_t\n\ntypedef struct\n{\n\tstruct QCC_def_s *sym;\n\tunion\n\t{\n\t\tunsigned int ofs;\n//\t\tunsigned int bofs;\n\t\tsigned int jumpofs;\n\t};\n\tstruct QCC_type_s *cast;\t//the entire sref is considered null if there is no cast, although it *MAY* have an ofs specified if its part of a jump instruction\n} QCC_sref_t;\ntypedef struct qcc_statement_s\n{\n\tunsigned short\t\top;\n\t#define STF_LOGICOP (1u<<0)\t//do not bother following when looking for uninitialised variables.\n\t#define STF_NOFOLD (1u<<1)\t//do not allow changing its var_c to fold the following store.\n\tunsigned short\t\tflags;\n\tQCC_sref_t\t\t\ta, b, c;\n\tunsigned int\t\tlinenum;\n} QCC_statement_t;\n\n//these should be the same except the string type\ntypedef struct ddef16_s\n{\n\tunsigned short\ttype;\t\t// if DEF_SAVEGLOBAL bit is set\n\t\t\t\t\t\t\t\t// the variable needs to be saved in savegames\n\tunsigned short\tofs;\n\tstring_t\t\ts_name;\n} ddef16_t;\n\ntypedef struct ddef32_s\n{\n\tunsigned int\ttype;\t\t// if DEF_SAVEGLOBAL bit is set\n\t\t\t\t\t\t\t\t// the variable needs to be saved in savegames\n\tunsigned int\tofs;\n\tstring_t\t\ts_name;\n} ddef32_t;\n\ntypedef void *ddefXX_t;\n\ntypedef struct QCC_ddef16_s\n{\n\tunsigned short\ttype;\t\t// if DEF_SAVEGLOBAL bit is set\n\t\t\t\t\t\t\t\t// the variable needs to be saved in savegames\n\tunsigned short\tofs;\n\tQCC_string_t\t\ts_name;\n} QCC_ddef16_t;\n\ntypedef struct QCC_ddef32_s\n{\n\tunsigned int\ttype;\t\t// if DEF_SAVEGLOBAL bit is set\n\t\t\t\t\t\t\t\t// the variable needs to be saved in savegames\n\tunsigned int\tofs;\n\tQCC_string_t\t\ts_name;\n} QCC_ddef32_t;\n#define QCC_ddef_t QCC_ddef32_t\n\n#define\tDEF_SAVEGLOBAL \t\t(1<<15)\n#define\tDEF_SHARED \t\t(1<<14)\n\ntypedef struct\n{\n\tint\t\tfirst_statement;\t// negative numbers are builtins\n\tint\t\tparm_start;\n\tint\t\tlocals;\t\t\t\t// total ints of parms + locals\n\n\tint\t\tprofile;\t\t// runtime\n\n\tstring_t\ts_name;\n\tstring_t\ts_file;\t\t\t// source file defined in\n\n\tint\t\tnumparms;\n\tpbyte\tparm_size[MAX_PARMS];\n} dfunction_t;\n\ntypedef struct\n{\n\tint\t\tfirst_statement;\t// negative numbers are builtins\n\tint\t\tparm_start;\n\tint\t\tlocals;\t\t\t\t// total ints of parms + locals\n\n\tint\t\tprofile;\t\t\t\t\t\t//number of qc instructions executed.\n\tprclocks_t profiletime;\t\t\t//total time inside (cpu cycles)\n\tprclocks_t profilechildtime;\t//time inside children (excluding builtins, cpu cycles)\n\n\tstring_t\ts_name;\n\tstring_t\ts_file;\t\t\t// source file defined in\n\n\tint\t\tnumparms;\n\tpbyte\tparm_size[MAX_PARMS];\n} mfunction_t;\n\n#define PROG_QTESTVERSION\t3\n#define\tPROG_VERSION\t6\n#define PROG_KKQWSVVERSION 7\n#define\tPROG_EXTENDEDVERSION\t7\n#define PROG_SECONDARYVERSION16 ((('1'<<0)|('F'<<8)|('T'<<16)|('E'<<24))^(('P'<<0)|('R'<<8)|('O'<<16)|('G'<<24)))\t//something unlikly and still meaningful (to me)\n#define PROG_SECONDARYVERSION32 ((('1'<<0)|('F'<<8)|('T'<<16)|('E'<<24))^(('3'<<0)|('2'<<8)|('B'<<16)|(' '<<24)))\t//something unlikly and still meaningful (to me)\n#define PROG_SECONDARYUHEXEN2\t((('U'<<0)|('H'<<8)|('2'<<16)|('7'<<24)))\t//something unlikly and still meaningful (to me)\n#define PROG_SECONDARYKKQWSV\t((('K'<<0)|('K'<<8)|('Q'<<16)|('W'<<24)))\t//something unlikly and still meaningful (to me)\ntypedef struct\n{\n\tint\t\tversion;\n\tint\t\tcrc;\t\t\t// check of header file\n\n\tunsigned int\t\tofs_statements;\t//comp 1\n\tunsigned int\t\tnumstatements;\t// statement 0 is an error\n\n\tunsigned int\t\tofs_globaldefs;\t//comp 2\n\tunsigned int\t\tnumglobaldefs;\n\n\tunsigned int\t\tofs_fielddefs;\t//comp 4\n\tunsigned int\t\tnumfielddefs;\n\n\tunsigned int\t\tofs_functions;\t//comp 8\n\tunsigned int\t\tnumfunctions;\t// function 0 is an empty\n\n\tunsigned int\t\tofs_strings;\t//comp 16\n\tunsigned int\t\tnumstrings;\t\t// first string is a null string\n\n\tunsigned int\t\tofs_globals;\t//comp 32\n\tunsigned int\t\tnumglobals;\n\n\tunsigned int\t\tentityfields;\n\n\t//debug / version 7 extensions\n\tunsigned int\t\tofsfiles;\t//non list format. no comp\n\tunsigned int\t\tofslinenums;\t//numstatements big\t//comp 64\n\tunsigned int\t\tofsbodylessfuncs;\t//no comp\n\tunsigned int\t\tnumbodylessfuncs;\n\n\tunsigned int\tofs_types;\t//comp 128\n\tunsigned int\tnumtypes;\n\tunsigned int\tblockscompressed;\n\n\tint\tsecondaryversion;\t//Constant - to say that any version 7 progs are actually ours, not someone else's alterations.\n} dprograms_t;\n#define standard_dprograms_t_size ((size_t)&((dprograms_t*)NULL)->ofsfiles)\n\n\n\n\n\ntypedef struct {\n\tchar filename[128];\n\tint size;\n\tint compsize;\n\tint compmethod;\n\tint ofs;\n} includeddatafile_t;\n\n\n\n\ntypedef struct typeinfo_s\n{\n\tetype_t\ttype;\n\n\tint\t\tnext;\n\tint\t\taux_type;\n\tint\t\tnum_parms;\n\n\tint\t\tofs;\t//inside a structure.\n\tint\t\tsize;\n\tstring_t\tname;\n} typeinfo_t;\n\n#endif\n"
  },
  {
    "path": "engine/qclib/pr_edict.c",
    "content": "\n\n#define PROGSUSED\nstruct edict_s;\n#include \"progsint.h\"\n//#include \"crc.h\"\n#include \"qcc.h\"\n\n#ifdef _WIN32\n//this is windows  all files are written with this endian standard. we do this to try to get a little more speed.\n#define NOENDIAN\n#endif\n\n#define qcc_iswhite(c) ((c) == ' ' || (c) == '\\r' || (c) == '\\n' || (c) == '\\t' || (c) == '\\v')\n\npbool\tED_ParseEpair (progfuncs_t *progfuncs, size_t qcptr, unsigned int fldofs, int fldtype, char *s);\n\n/*\n=================\nQC_ClearEdict\n\nSets everything to NULL\n=================\n*/\nvoid PDECL QC_ClearEdict (pubprogfuncs_t *ppf, struct edict_s *ed)\n{\n//\tprogfuncs_t *progfuncs = (progfuncs_t*)ppf;\n\tedictrun_t *e = (edictrun_t *)ed;\n\tint num = e->entnum;\n\tmemset (e->fields, 0, e->fieldsize);\n\te->ereftype = ER_ENTITY;\n\te->entnum = num;\n}\n\nstruct edict_s *PDECL ED_AllocIndex (pubprogfuncs_t *ppf, unsigned int num, pbool object, size_t extrasize)\n{\n\tprogfuncs_t *progfuncs = (progfuncs_t*)ppf;\n\tedictrun_t *e;\n\n\tunsigned int fields_size;\n\n\tif (num >= prinst.maxedicts)\n\t{\n\t\texterns->Sys_Error (\"ED_AllocIndex: index %u exceeds limit of %u\", num, prinst.maxedicts);\n\t\treturn NULL;\n\t}\n\tif (!object)\n\t{\n\t\twhile(sv_num_edicts < num)\n\t\t{\t//fill in any holes\n\t\t\te = (edictrun_t*)EDICT_NUM(progfuncs, sv_num_edicts);\n\t\t\tif (!e)\n\t\t\t{\n\t\t\t\te = (edictrun_t*)ED_AllocIndex(&progfuncs->funcs, sv_num_edicts, object, extrasize);\n\t\t\t\te->ereftype=ER_FREE;\n\t\t\t}\n\t\t\tsv_num_edicts++;\n\t\t}\n\t\tif (num >= sv_num_edicts)\n\t\t\tsv_num_edicts=num+1;\n\t}\n\n\te = prinst.edicttable[num];\n\tif (!e)\n\t{\n\t\te = (void*)externs->memalloc(externs->edictsize);\n\t\tprinst.edicttable[num] = e;\n\t\tmemset(e, 0, externs->edictsize);\n\t}\n\n\tfields_size = object?0:prinst.fields_size;\n\tfields_size += extrasize;\n\tif (e->fieldsize != fields_size)\n\t{\n\t\tif (e->fields)\n\t\t\tprogfuncs->funcs.AddressableFree(&progfuncs->funcs, e->fields);\n\t\te->fields = progfuncs->funcs.AddressableAlloc(&progfuncs->funcs, fields_size);\n\t\tif (!e->fields)\n\t\t\texterns->Sys_Error (\"ED_Alloc: Unable to allocate more field space\");\n\t\te->fieldsize = fields_size;\n\n//\t\te->fields = PRAddressableExtend(progfuncs, NULL, fields_size, 0);\n\t}\n\te->entnum = num;\n\tmemset (e->fields, 0, e->fieldsize);\n\n\te->ereftype = object?ER_OBJECT:ER_ENTITY;\n\n\tif (externs->entspawn)\n\t\texterns->entspawn((struct edict_s *) e, false);\n\treturn (struct edict_s*)e;\n}\n\n/*\n=================\nED_Alloc\n\nEither finds a free edict, or allocates a new one.\nTry to avoid reusing an entity that was recently freed, because it\ncan cause the client to think the entity morphed into something else\ninstead of being removed and recreated, which can cause interpolated\nangles and bad trails.\n=================\n*/\nstruct edict_s *PDECL ED_Alloc (pubprogfuncs_t *ppf, pbool object, size_t extrasize)\n{\n\tprogfuncs_t *progfuncs = (progfuncs_t*)ppf;\n\tunsigned int\t\t\ti;\n\tedictrun_t\t\t*e;\n\n\tif (object)\n\t{\n\t\t//objects are allocated at the end (won't be networked, so this reduces issues with users on old protocols).\n\t\t//also they're potentially higher than num_edicts, which is handy.\n\t\tfor ( i=prinst.maxedicts-1 ; i>0 ; i--)\n\t\t{\n\t\t\te = (edictrun_t*)EDICT_NUM(progfuncs, i);\n\t\t\t// the first couple seconds of server time can involve a lot of\n\t\t\t// freeing and allocating, so relax the replacement policy\n\t\t\tif (!e || (e->ereftype==ER_FREE && ( e->freetime < 2 || *externs->gametime - e->freetime > 0.5 ) ))\n\t\t\t\treturn ED_AllocIndex(&progfuncs->funcs, i, object, extrasize);\n\t\t}\n\t\texterns->Sys_Error (\"ED_Alloc: no free edicts (max is %i)\", prinst.maxedicts);\n\t}\n\n\t//define this to wastefully allocate extra ents, to test network capabilities.\n#define STEP 1//((i <= 32)?1:8)\n\n\tfor ( i=0 ; i<sv_num_edicts ; i+=STEP)\n\t{\n\t\te = (edictrun_t*)EDICT_NUM(progfuncs, i);\n\t\t// the first couple seconds of server time can involve a lot of\n\t\t// freeing and allocating, so relax the replacement policy\n\t\tif (!e || (e->ereftype==ER_FREE && ( e->freetime < 2 || *externs->gametime - e->freetime > 0.5 ) ))\n\t\t\treturn ED_AllocIndex(&progfuncs->funcs, i, object, extrasize);\n\t}\n\n\tif (i >= prinst.maxedicts-1)\t//try again, but use timed out ents.\n\t{\n\t\tfor ( i=0 ; i<sv_num_edicts ; i+=STEP)\n\t\t{\n\t\t\te = (edictrun_t*)EDICT_NUM(progfuncs, i);\n\t\t\t// the first couple seconds of server time can involve a lot of\n\t\t\t// freeing and allocating, so relax the replacement policy\n\t\t\tif (!e || (e->ereftype==ER_FREE))\n\t\t\t\treturn ED_AllocIndex(&progfuncs->funcs, i, object, extrasize);\n\t\t}\n\n\t\tif (i >= prinst.maxedicts-2)\n\t\t{\n\t\t\tPR_RunWarning(&progfuncs->funcs, \"Running out of edicts\\n\");\n\t\t}\n\t\tif (i >= prinst.maxedicts-1)\n\t\t{\n\t\t\tsize_t size;\n\t\t\tchar *buf;\n\t\t\tbuf = PR_SaveEnts(&progfuncs->funcs, NULL, &size, 0, 0);\n\t\t\tprogfuncs->funcs.parms->WriteFile(\"edalloc.dump\", buf, size);\n\t\t\texterns->Sys_Error (\"ED_Alloc: no free edicts (max is %i)\", prinst.maxedicts);\n\t\t}\n\t}\n\n\treturn ED_AllocIndex(&progfuncs->funcs, i, object, extrasize);\n}\n\n/*\n=================\nED_Free\n\nMarks the edict as free\nFIXME: walk all entities and NULL out references to this entity\n=================\n*/\nvoid PDECL ED_Free (pubprogfuncs_t *ppf, struct edict_s *ed, pbool instant)\n{\n\tprogfuncs_t *progfuncs = (progfuncs_t*)ppf;\n\tedictrun_t *e = (edictrun_t *)ed;\n//\tSV_UnlinkEdict (ed);\t\t// unlink from world bsp\n\n\tif (e->ereftype == ER_FREE)\t//this happens on start.bsp where an onlyregistered trigger killtargets itself (when all of this sort die after 1 trigger anyway).\n\t{\n\t\tif (prinst.pr_depth)\n\t\t\texterns->Printf(\"Tried to free free entity within %s\\n\", prinst.pr_xfunction->s_name+progfuncs->funcs.stringtable);\n\t\telse\n\t\t\texterns->Printf(\"Engine tried to free free entity\\n\");\n//\t\tif (developer.value == 1)\n//\t\t\tprogfuncs->funcs.pr_trace = true;\n\t\treturn;\n\t}\n\n\tif (externs->entcanfree)\n\t\tif (!externs->entcanfree(ed))\t//can stop an ent from being freed.\n\t\t\treturn;\n\n\te->ereftype = ER_FREE;\n\te->freetime = instant?0:(float)*externs->gametime;\n\n/*\n\ted->v.model = 0;\n\ted->v.takedamage = 0;\n\ted->v.modelindex = 0;\n\ted->v.colormap = 0;\n\ted->v.skin = 0;\n\ted->v.frame = 0;\n\tVectorCopy (vec3_origin, ed->v.origin);\n\tVectorCopy (vec3_origin, ed->v.angles);\n\ted->v.nextthink = -1;\n\ted->v.solid = 0;\n*/\n}\n\n//===========================================================================\n\n/*\n============\nED_GlobalAtOfs\n============\n*/\nddef16_t *ED_GlobalAtOfs16 (progfuncs_t *progfuncs, int ofs)\n{\n\tddef16_t\t\t*def;\n\tunsigned int\t\t\ti;\n\n\tfor (i=0 ; i<pr_progs->numglobaldefs ; i++)\n\t{\n\t\tdef = &pr_globaldefs16[i];\n\t\tif (def->ofs == ofs)\n\t\t\treturn def;\n\t}\n\treturn NULL;\n}\nddef32_t *ED_GlobalAtOfs32 (progfuncs_t *progfuncs, unsigned int ofs)\n{\n\tddef32_t\t\t*def;\n\tunsigned int\t\t\ti;\n\n\tfor (i=0 ; i<pr_progs->numglobaldefs ; i++)\n\t{\n\t\tdef = &pr_globaldefs32[i];\n\t\tif (def->ofs == ofs)\n\t\t\treturn def;\n\t}\n\treturn NULL;\n}\n\n/*\n============\nED_FieldAtOfs\n============\n*/\nfdef_t *ED_FieldAtOfs (progfuncs_t *progfuncs, unsigned int ofs)\n{\n//\tddef_t\t\t*def;\n\tunsigned int\t\t\ti;\n\n\tfor (i=0 ; i<prinst.numfields ; i++)\n\t{\n\t\tif (prinst.field[i].ofs == ofs)\n\t\t\treturn &prinst.field[i];\n\t}\n\treturn NULL;\n}\nfdef_t *ED_ClassFieldAtOfs (progfuncs_t *progfuncs, unsigned int ofs, const char *classname)\n{\n\tint classnamelen = strlen(classname);\n\tunsigned int j;\n\tconst char *mname;\n\tfor (j = 0; j < prinst.numfields; j++)\n\t{\n\t\tif (prinst.field[j].ofs == ofs)\n\t\t{\n\t\t\tmname = prinst.field[j].name;\n\t\t\tif (!strncmp(mname, classname, classnamelen) && mname[classnamelen] == ':')\n\t\t\t{\n\t\t\t\t//okay, we have a match...\n\t\t\t\treturn &prinst.field[j];\n\t\t\t}\n\t\t}\n\t}\n\treturn ED_FieldAtOfs(progfuncs, ofs);\n}\nfdef_t *PDECL ED_FieldInfo (pubprogfuncs_t *ppf, unsigned int *count)\n{\n\tprogfuncs_t *progfuncs = (progfuncs_t*)ppf;\n\t*count = prinst.numfields;\n\treturn prinst.field;\n}\n/*\n============\nED_FindField\n============\n*/\nfdef_t *ED_FindField (progfuncs_t *progfuncs, const char *name)\n{\n\tunsigned int\t\t\ti;\n\n\tfor (i=0 ; i<prinst.numfields ; i++)\n\t{\n\t\tif (!strcmp(prinst.field[i].name, name) )\n\t\t\treturn &prinst.field[i];\n\t}\n\treturn NULL;\n}\n\n\n/*\n============\nED_FindGlobal\n============\n*/\nddef16_t *ED_FindGlobal16 (progfuncs_t *progfuncs, const char *name)\n{\n\tddef16_t\t\t*def;\n\tunsigned int\t\t\ti;\n\n\tfor (i=1 ; i<pr_progs->numglobaldefs ; i++)\n\t{\n\t\tdef = &pr_globaldefs16[i];\n\t\tif (!strcmp(def->s_name+progfuncs->funcs.stringtable,name) )\n\t\t\treturn def;\n\t}\n\treturn NULL;\n}\nddef32_t *ED_FindGlobal32 (progfuncs_t *progfuncs, const char *name)\n{\n\tddef32_t\t\t*def;\n\tunsigned int\t\t\ti;\n\n\tfor (i=1 ; i<pr_progs->numglobaldefs ; i++)\n\t{\n\t\tdef = &pr_globaldefs32[i];\n\t\tif (!strcmp(def->s_name+progfuncs->funcs.stringtable,name) )\n\t\t\treturn def;\n\t}\n\treturn NULL;\n}\n\nunsigned int ED_FindGlobalOfs (progfuncs_t *progfuncs, char *name)\n{\n\tddef16_t *d16;\n\tddef32_t *d32;\n\tswitch(current_progstate->structtype)\n\t{\n\tcase PST_KKQWSV:\n\tcase PST_DEFAULT:\n\t\td16 = ED_FindGlobal16(progfuncs, name);\n\t\treturn d16?d16->ofs:0;\n\tcase PST_QTEST:\n\tcase PST_FTE32:\n\tcase PST_UHEXEN2:\n\t\td32 = ED_FindGlobal32(progfuncs, name);\n\t\treturn d32?d32->ofs:0;\n\tdefault:\n\t\texterns->Sys_Error(\"ED_FindGlobalOfs - bad struct type\");\n\t}\n\treturn 0;\n}\n\nddef16_t *ED_FindGlobalFromProgs16 (progfuncs_t *progfuncs, progstate_t *ps, const char *name)\n{\n\tddef16_t\t\t*def;\n\tunsigned int\t\t\ti;\n\n\tfor (i=1 ; i<ps->progs->numglobaldefs ; i++)\n\t{\n\t\tdef = &ps->globaldefs16[i];\n\t\tif (!strcmp(def->s_name+progfuncs->funcs.stringtable,name) )\n\t\t\treturn def;\n\t}\n\treturn NULL;\n}\nddef32_t *ED_FindGlobalFromProgs32 (progfuncs_t *progfuncs, progstate_t *ps, const char *name)\n{\n\tddef32_t\t\t*def;\n\tunsigned int\t\t\ti;\n\n\tfor (i=1 ; i<ps->progs->numglobaldefs ; i++)\n\t{\n\t\tdef = &ps->globaldefs32[i];\n\t\tif (!strcmp(def->s_name+progfuncs->funcs.stringtable,name) )\n\t\t\treturn def;\n\t}\n\treturn NULL;\n}\n\nddef16_t *ED_FindTypeGlobalFromProgs16 (progfuncs_t *progfuncs, progstate_t *ps, const char *name, int type)\n{\n\tddef16_t\t\t*def;\n\tunsigned int\t\t\ti;\n\n\tfor (i=1 ; i<ps->progs->numglobaldefs ; i++)\n\t{\n\t\tdef = &ps->globaldefs16[i];\n\t\tif (!strcmp(def->s_name+progfuncs->funcs.stringtable,name) )\n\t\t{\n\t\t\tif (ps->types)\n\t\t\t{\n\t\t\t\tif (ps->types[def->type&~DEF_SAVEGLOBAL].type != type)\n\t\t\t\t\tcontinue;\n\t\t\t}\n\t\t\telse if ((def->type&(~DEF_SAVEGLOBAL)) != type)\n\t\t\t\tcontinue;\n\t\t\treturn def;\n\t\t}\n\t}\n\treturn NULL;\n}\n\n\nddef32_t *ED_FindTypeGlobalFromProgs32 (progfuncs_t *progfuncs, progstate_t *ps, const char *name, int type)\n{\n\tddef32_t\t\t*def;\n\tunsigned int\t\t\ti;\n\n\tfor (i=1 ; i<ps->progs->numglobaldefs ; i++)\n\t{\n\t\tdef = &ps->globaldefs32[i];\n\t\tif (!strcmp(def->s_name+progfuncs->funcs.stringtable,name) )\n\t\t{\n\t\t\tif (ps->types)\n\t\t\t{\n\t\t\t\tif (ps->types[def->type&~DEF_SAVEGLOBAL].type != type)\n\t\t\t\t\tcontinue;\n\t\t\t}\n\t\t\telse if ((def->type&(~DEF_SAVEGLOBAL)) != (unsigned)type)\n\t\t\t\tcontinue;\n\t\t\treturn def;\n\t\t}\n\t}\n\treturn NULL;\n}\n\nunsigned int *ED_FindGlobalOfsFromProgs (progfuncs_t *progfuncs, progstate_t *ps, char *name, int type)\n{\n\tddef16_t\t\t*def16;\n\tddef32_t\t\t*def32;\n\tstatic unsigned int pos;\n\tswitch(ps->structtype)\n\t{\n\tcase PST_DEFAULT:\n\tcase PST_KKQWSV:\n\t\tdef16 = ED_FindTypeGlobalFromProgs16(progfuncs, ps, name, type);\n\t\tif (!def16)\n\t\t\treturn NULL;\n\t\tpos = def16->ofs;\n\t\treturn &pos;\n\tcase PST_QTEST:\n\tcase PST_FTE32:\n\tcase PST_UHEXEN2:\n\t\tdef32 = ED_FindTypeGlobalFromProgs32(progfuncs, ps, name, type);\n\t\tif (!def32)\n\t\t\treturn NULL;\n\t\treturn &def32->ofs;\n\tdefault:\n\t\texterns->Sys_Error(\"ED_FindGlobalOfsFromProgs - bad struct type\");\n\t}\n\treturn 0;\n}\n\n/*\n============\nED_FindFunction\n============\n*/\nmfunction_t *ED_FindFunction (progfuncs_t *progfuncs, const char *name, progsnum_t *prnum, progsnum_t fromprogs)\n{\n\tmfunction_t\t\t*func;\n\tunsigned int\t\t\t\ti;\n\tchar *sep;\n\n\tprogsnum_t pnum;\n\n\tif (prnum)\n\t{\n\t\tsep = strchr(name, ':');\n\t\tif (sep)\n\t\t{\n\t\t\tpnum = atoi(name);\n\t\t\tname = sep+1;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (fromprogs>=0)\n\t\t\t\tpnum = fromprogs;\n\t\t\telse\n\t\t\t\tpnum = prinst.pr_typecurrent;\n\t\t}\n\t\t*prnum = pnum;\n\t}\n\telse\n\t\tpnum = prinst.pr_typecurrent;\n\n\tif ((unsigned)pnum > (unsigned)prinst.maxprogs)\n\t{\n\t\texterns->Printf(\"Progsnum %\"pPRIi\" out of bounds\\n\", pnum);\n\t\treturn NULL;\n\t}\n\n\tif (!pr_progstate[pnum].progs)\n\t\treturn NULL;\n\n\tfor (i=1 ; i<pr_progstate[pnum].progs->numfunctions ; i++)\n\t{\n\t\tfunc = &pr_progstate[pnum].functions[i];\n\t\tif (!strcmp(func->s_name+progfuncs->funcs.stringtable,name) )\n\t\t\treturn func;\n\t}\n\treturn NULL;\n}\n\n/*\n============\nPR_ValueString\n\nReturns a string describing *data in a human-readable type specific manner\nif verbose, contains entity field listing etc too\n=============\n*/\nchar *PR_ValueString (progfuncs_t *progfuncs, etype_t type, eval_t *val, pbool verbose)\n{\n\tstatic char\tline[4096];\n\tfdef_t\t\t\t*fielddef;\n\tmfunction_t\t*f;\n\n#ifdef DEF_SAVEGLOBAL\n\ttype &= ~DEF_SAVEGLOBAL;\n#endif\n\n\tif (current_progstate && pr_types)\n\t\ttype = pr_types[type].type;\n\n\tif (!val)\n\t\ttype = ev_void;\n\n\tswitch (type)\n\t{\n\tcase ev_struct:\n\t\tQC_snprintfz (line, sizeof(line), \"struct\");\n\t\tbreak;\n\tcase ev_union:\n\t\tQC_snprintfz (line, sizeof(line), \"union\");\n\t\tbreak;\n\tcase ev_string:\n#ifndef QCGC\n\t\tif (((unsigned int)val->string & STRING_SPECMASK) == STRING_TEMP)\n\t\t\treturn \"<Stale Temporary String>\";\n\t\telse\n#endif\n\t\t\tQC_snprintfz (line, sizeof(line), \"%s\", PR_StringToNative(&progfuncs->funcs, val->string));\n\t\tbreak;\n\tcase ev_entity:\n\t\tfielddef = ED_FindField(progfuncs, \"classname\");\n\t\tif (fielddef && (unsigned)val->edict < (unsigned)sv_num_edicts)\n\t\t{\n\t\t\tedictrun_t *ed;\n\t\t\tstring_t *v;\n\t\t\ted = (edictrun_t *)EDICT_NUM(progfuncs, val->edict);\n\t\t\tv = (string_t *)((char *)edvars(ed) + fielddef->ofs*4);\n\t\t\tQC_snprintfz (line, sizeof(line), \"entity %i(%s)\", val->edict, PR_StringToNative(&progfuncs->funcs, *v));\n\t\t}\n\t\telse\n\t\t\tQC_snprintfz (line, sizeof(line), \"entity %i\", val->edict);\n\n\t\tif (verbose && (unsigned)val->edict < (unsigned)sv_num_edicts)\n\t\t{\n\t\t\tstruct edict_s *ed = EDICT_NUM(progfuncs, val->edict);\n\t\t\tsize_t size = strlen(line);\n\t\t\tif (ed)\n\t\t\t\tPR_SaveEnt(&progfuncs->funcs, line, &size, sizeof(line), ed);\n\t\t}\n\t\tbreak;\n\tcase ev_function:\n\t\tif (!val->function)\n\t\t\tQC_snprintfz (line, sizeof(line), \"NULL function\");\n\t\telse\n\t\t{\n\t\t\tif ((val->function & 0xff000000)>>24 >= prinst.maxprogs || !pr_progstate[(val->function & 0xff000000)>>24].functions)\n\t\t\t\tQC_snprintfz (line, sizeof(line), \"Bad function %\"pPRIi\":%\"pPRIi\"\", (val->function & 0xff000000)>>24, val->function & ~0xff000000);\n\t\t\telse\n\t\t\t{\n\t\t\t\tif ((val->function &~0xff000000) >= pr_progs->numfunctions)\n\t\t\t\t\tQC_snprintfz (line, sizeof(line), \"bad function %\"pPRIi\":%\"pPRIi\"\\n\", (val->function & 0xff000000)>>24, val->function & ~0xff000000);\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tf = pr_progstate[(val->function & 0xff000000)>>24].functions + (val->function & ~0xff000000);\n\t\t\t\t\tQC_snprintfz (line, sizeof(line), \"%\"pPRIi\":%s()\", (val->function & 0xff000000)>>24, f->s_name+progfuncs->funcs.stringtable);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tbreak;\n\tcase ev_field:\n\t\tfielddef = ED_FieldAtOfs (progfuncs,  val->_int + progfuncs->funcs.fieldadjust);\n\t\tif (!fielddef)\n\t\t\tQC_snprintfz (line, sizeof(line), \".??? (#%i)\", val->_int);\n\t\telse\n\t\t\tQC_snprintfz (line, sizeof(line), \".%s (#%i)\", fielddef->name, val->_int);\n\t\tbreak;\n\tcase ev_void:\n\t\tQC_snprintfz (line, sizeof(line), \"void type\");\n\t\tbreak;\n\tcase ev_float:\n\t\tQC_snprintfz (line, sizeof(line), \"%g\", val->_float);\n\t\tbreak;\n\tcase ev_double:\n\t\tQC_snprintfz (line, sizeof(line), \"%g\", val->_double);\n\t\tbreak;\n\tcase ev_integer:\n\t\tQC_snprintfz (line, sizeof(line), \"%\"pPRIi, val->_int);\n\t\tbreak;\n\tcase ev_uint:\n\t\tQC_snprintfz (line, sizeof(line), \"%\"pPRIu, val->_uint);\n\t\tbreak;\n\tcase ev_int64:\n\t\tQC_snprintfz (line, sizeof(line), \"%\"pPRIi64, val->i64);\n\t\tbreak;\n\tcase ev_uint64:\n\t\tQC_snprintfz (line, sizeof(line), \"%\"pPRIu64, val->u64);\n\t\tbreak;\n\tcase ev_vector:\n\t\tQC_snprintfz (line, sizeof(line), \"'%g %g %g'\", val->_vector[0], val->_vector[1], val->_vector[2]);\n\t\tbreak;\n\tcase ev_pointer:\n\t\tQC_snprintfz (line, sizeof(line), \"%#x\", val->_int);\n\t\t{\n//\t\t\tint entnum;\n//\t\t\tint valofs;\n\t\t//FIXME: :/\n//\t\t\tentnum = ((qbyte *)val->edict - (qbyte *)sv_edicts) / pr_edict_size;\n//\t\t\tvalofs = (int *)val->edict - (int *)edvars(EDICT_NUM(progfuncs, entnum));\n//\t\t\tfielddef = ED_FieldAtOfs (progfuncs, valofs );\n//\t\t\tif (fielddef)\n//\t\t\t\tsprintf(line, \"ent%i.%s\", entnum, fielddef->s_name);\n\t\t}\n\t\tbreak;\n\tcase ev_accessor:\n\t\tQC_snprintfz (line, sizeof(line), \"(accessor)\");\n\t\tbreak;\n\tdefault:\n\t\tQC_snprintfz (line, sizeof(line), \"(bad type %i)\", type);\n\t\tbreak;\n\t}\n\n\treturn line;\n}\n\n/*\n============\nPR_UglyValueString\n\nReturns a string describing *data in a type specific manner\nEasier to parse than PR_ValueString\n=============\n*/\nchar *PDECL PR_UglyValueString (pubprogfuncs_t *ppf, etype_t type, eval_t *val)\n{\n\tprogfuncs_t *progfuncs = (progfuncs_t*)ppf;\n\tstatic char\tline[4096];\n\tfdef_t\t\t*fielddef;\n\tmfunction_t\t*f;\n\tint i, j;\n\n#ifdef DEF_SAVEGLOBAL\n\ttype &= ~DEF_SAVEGLOBAL;\n#endif\n\n//\tif (pr_types)\n//\t\ttype = pr_types[type].type;\n\n\tswitch (type)\n\t{\n\tcase ev_struct:\n\t\tsprintf (line, \"structures cannot yet be saved\");\n\t\tbreak;\n\tcase ev_union:\n\t\tsprintf (line, \"unions cannot yet be saved\");\n\t\tbreak;\n\tcase ev_string:\n\t\t{\n\t\t\tchar *outs = line;\n\t\t\tint outb = sizeof(line)-2;\n\t\t\tconst char *ins;\n#ifndef QCGC\n\t\t\tif (((unsigned int)val->string & STRING_SPECMASK) == STRING_TEMP)\n\t\t\t\treturn \"<Stale Temporary String>\";\n\t\t\telse\n#endif\n\t\t\t\tins = PR_StringToNative(&progfuncs->funcs, val->string);\n\t\t\t//markup the output string.\n\t\t\twhile(*ins && outb > 0)\n\t\t\t{\n\t\t\t\tswitch(*ins)\n\t\t\t\t{\n\t\t\t\tcase '\\n':\n\t\t\t\t\t*outs++ = '\\\\';\n\t\t\t\t\t*outs++ = 'n';\n\t\t\t\t\tins++;\n\t\t\t\t\toutb-=2;\n\t\t\t\t\tbreak;\n\t\t\t\tcase '\\\"':\n\t\t\t\t\t*outs++ = '\\\\';\n\t\t\t\t\t*outs++ = '\"';\n\t\t\t\t\tins++;\n\t\t\t\t\toutb-=2;\n\t\t\t\t\tbreak;\n\t\t\t\tcase '\\\\':\n\t\t\t\t\t*outs++ = '\\\\';\n\t\t\t\t\t*outs++ = '\\\\';\n\t\t\t\t\tins++;\n\t\t\t\t\toutb-=2;\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\t*outs++ = *ins++;\n\t\t\t\t\toutb--;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\t*outs = 0;\n\t\t}\n\t\tbreak;\n\tcase ev_entity:\n\t\tsprintf (line, \"%i\", val->_int);\n\t\tbreak;\n\tcase ev_function:\n\t\ti = (val->function & 0xff000000)>>24;\t//progs number\n\t\tif ((unsigned)i >= prinst.maxprogs || !pr_progstate[(unsigned)i].progs)\n\t\t\tsprintf (line, \"BAD FUNCTION INDEX: %#\"pPRIx\"\", val->function);\n\t\telse\n\t\t{\n\t\t\tj = (val->function & ~0xff000000);\t//function number\n\t\t\tif ((unsigned)j >= pr_progstate[(unsigned)i].progs->numfunctions)\n\t\t\t\tsprintf(line, \"%i:%s\", i, \"CORRUPT FUNCTION POINTER\");\n\t\t\telse\n\t\t\t{\n\t\t\t\tf = pr_progstate[(unsigned)i].functions + j;\n\t\t\t\tsprintf (line, \"%i:%s\", i, f->s_name+progfuncs->funcs.stringtable);\n\t\t\t}\n\t\t}\n\t\tbreak;\n\tcase ev_field:\n\t\tfielddef = ED_FieldAtOfs (progfuncs, val->_int + progfuncs->funcs.fieldadjust);\n\t\tif (fielddef)\n\t\t\tsprintf (line, \"%s\", fielddef->name);\n\t\telse\n\t\t\tsprintf (line, \"bad field %i\", type);\n\t\tbreak;\n\tcase ev_void:\n\t\tsprintf (line, \"void\");\n\t\tbreak;\n\tcase ev_float:\n\t\tif (val->_float == (int)val->_float)\n\t\t\tsprintf (line, \"%i\", (int)val->_float);\t//an attempt to cut down on the number of .000000 vars..\n\t\telse\n\t\t\tsprintf (line, \"%f\", val->_float);\n\t\tbreak;\n\tcase ev_double:\n\t\tif (val->_double == (pint64_t)val->_double)\n\t\t\tsprintf (line, \"%\"pPRIi64, (pint64_t)val->_double);\t//an attempt to cut down on the number of .000000 vars..\n\t\telse\n\t\t\tsprintf (line, \"%f\", val->_double);\n\t\tbreak;\n\tcase ev_integer:\n\t\tsprintf (line, \"%\"pPRIi, val->_int);\n\t\tbreak;\n\tcase ev_uint:\n\t\tsprintf (line, \"%\"pPRIu, val->_uint);\n\t\tbreak;\n\tcase ev_int64:\n\t\tsprintf (line, \"%\"pPRIi64, val->i64);\n\t\tbreak;\n\tcase ev_uint64:\n\t\tsprintf (line, \"%\"pPRIu64, val->u64);\n\t\tbreak;\n\tcase ev_vector:\n\t\tif (val->_vector[0] == (int)val->_vector[0] && val->_vector[1] == (int)val->_vector[1] && val->_vector[2] == (int)val->_vector[2])\n\t\t\tsprintf (line, \"%i %i %i\", (int)val->_vector[0], (int)val->_vector[1], (int)val->_vector[2]);\n\t\telse\n\t\t\tsprintf (line, \"%g %g %g\", val->_vector[0], val->_vector[1], val->_vector[2]);\n\t\tbreak;\n\tcase ev_pointer:\n\t\tQC_snprintfz (line, sizeof(line), \"%#x\", val->_int);\n\t\tbreak;\n\tdefault:\n\t\tsprintf (line, \"bad type %i\", type);\n\t\tbreak;\n\t}\n\n\treturn line;\n}\n\n//compatible with Q1 (for savegames)\nchar *PR_UglyOldValueString (progfuncs_t *progfuncs, etype_t type, eval_t *val)\n{\n\tstatic char\tline[4096];\n\tfdef_t\t\t*fielddef;\n\tmfunction_t\t*f;\n\n#ifdef DEF_SAVEGLOBAL\n\ttype &= ~DEF_SAVEGLOBAL;\n#endif\n\n\tif (pr_types)\n\t\ttype = pr_types[type].type;\n\n\tswitch (type)\n\t{\n\tcase ev_struct:\n\t\tQC_snprintfz (line, sizeof(line), \"structures cannot yet be saved\");\n\t\tbreak;\n\tcase ev_union:\n\t\tQC_snprintfz (line, sizeof(line), \"unions cannot yet be saved\");\n\t\tbreak;\n\tcase ev_string:\n\t\t//FIXME: we should probably add markup. vanilla does _not_, so we can expect problems reloading anyway.\n\t\tQC_snprintfz (line, sizeof(line), \"%s\", PR_StringToNative(&progfuncs->funcs, val->string));\n\t\tbreak;\n\tcase ev_entity:\n\t\tQC_snprintfz (line, sizeof(line), \"%i\", val->edict);\n\t\tbreak;\n\tcase ev_function:\n\t\tf = pr_progstate[(val->function & 0xff000000)>>24].functions + (val->function & ~0xff000000);\n\t\tQC_snprintfz (line, sizeof(line), \"%s\", f->s_name+progfuncs->funcs.stringtable);\n\t\tbreak;\n\tcase ev_field:\n\t\tfielddef = ED_FieldAtOfs (progfuncs, val->_int + progfuncs->funcs.fieldadjust);\n\t\tQC_snprintfz (line, sizeof(line), \"%s\", fielddef->name);\n\t\tbreak;\n\tcase ev_void:\n\t\tQC_snprintfz (line, sizeof(line), \"void\");\n\t\tbreak;\n\tcase ev_float:\n\t\tif (val->_float == (int)val->_float)\n\t\t\tQC_snprintfz (line, sizeof(line), \"%i\", (int)val->_float);\t//an attempt to cut down on the number of .000000 vars..\n\t\telse\n\t\t\tQC_snprintfz (line, sizeof(line), \"%f\", val->_float);\n\t\tbreak;\n\tcase ev_double:\n\t\tif (val->_double == (int)val->_double)\n\t\t\tQC_snprintfz (line, sizeof(line), \"%i\", (int)val->_double);\t//an attempt to cut down on the number of .000000 vars..\n\t\telse\n\t\t\tQC_snprintfz (line, sizeof(line), \"%f\", val->_double);\n\t\tbreak;\n\tcase ev_integer:\n\t\tQC_snprintfz (line, sizeof(line), \"%\"pPRIi, val->_int);\n\t\tbreak;\n\tcase ev_uint:\n\t\tQC_snprintfz (line, sizeof(line), \"%\"pPRIu, val->_uint);\n\t\tbreak;\n\tcase ev_int64:\n\t\tQC_snprintfz (line, sizeof(line), \"%\"pPRIi64, val->i64);\n\t\tbreak;\n\tcase ev_uint64:\n\t\tQC_snprintfz (line, sizeof(line), \"%\"pPRIu64, val->u64);\n\t\tbreak;\n\tcase ev_vector:\n\t\tif (val->_vector[0] == (int)val->_vector[0] && val->_vector[1] == (int)val->_vector[1] && val->_vector[2] == (int)val->_vector[2])\n\t\t\tQC_snprintfz (line, sizeof(line), \"%i %i %i\", (int)val->_vector[0], (int)val->_vector[1], (int)val->_vector[2]);\n\t\telse\n\t\t\tQC_snprintfz (line, sizeof(line), \"%f %f %f\", val->_vector[0], val->_vector[1], val->_vector[2]);\n\t\tbreak;\n\tcase ev_pointer:\n\t\tQC_snprintfz (line, sizeof(line), \"%#x\", val->_int);\n\t\tbreak;\n\tdefault:\n\t\tQC_snprintfz (line, sizeof(line), \"bad type %i\", type);\n\t\tbreak;\n\t}\n\n\treturn line;\n}\n\nchar *PR_TypeString(progfuncs_t *progfuncs, etype_t type)\n{\n#ifdef DEF_SAVEGLOBAL\n\ttype &= ~DEF_SAVEGLOBAL;\n#endif\n\n\tif (pr_types)\n\t\ttype = pr_types[type].type;\n\n\tswitch (type)\n\t{\n\tcase ev_struct:\n\t\treturn \"struct\";\n\tcase ev_union:\n\t\treturn \"union\";\n\tcase ev_string:\n\t\treturn \"string\";\n\tcase ev_entity:\n\t\treturn \"entity\";\n\tcase ev_function:\n\t\treturn \"function\";\n\tcase ev_field:\n\t\treturn \"field\";\n\tcase ev_void:\n\t\treturn \"void\";\n\tcase ev_float:\n\t\treturn \"float\";\n\tcase ev_double:\n\t\treturn \"double\";\n\tcase ev_vector:\n\t\treturn \"vector\";\n\tcase ev_integer:\n\t\treturn \"integer\";\n\tcase ev_uint:\n\t\treturn \"uint\";\n\tcase ev_int64:\n\t\treturn \"int64\";\n\tcase ev_uint64:\n\t\treturn \"uint64\";\n\tdefault:\n\t\treturn \"BAD TYPE\";\n\t}\n}\n\n/*\n============\nPR_GlobalString\n\nReturns a string with a description and the contents of a global,\npadded to 20 field width\n============\n*/\nchar *PR_GlobalString (progfuncs_t *progfuncs, int ofs, struct QCC_type_s **typehint)\n{\n\tchar\t*s;\n\tint\t\ti;\n\tddef16_t\t*def16;\n\tddef32_t\t*def32, def32tmp;\n\tvoid\t*val;\n\tstatic char\tline[128];\n\n\tswitch (current_progstate->structtype)\n\t{\n\tcase PST_DEFAULT:\n\tcase PST_KKQWSV:\n\t\tdef16 = ED_GlobalAtOfs16(progfuncs, ofs);\n\t\tif (def16)\n\t\t{\n\t\t\tdef32 = &def32tmp;\n\t\t\tdef32->ofs = def16->ofs;\n\t\t\tdef32->type = def16->type;\n\t\t\tdef32->s_name = def16->s_name;\n\t\t}\n\t\telse\n\t\t\tdef32 = NULL;\n\t\tbreak;\n\tcase PST_QTEST:\n\tcase PST_FTE32:\n\tcase PST_UHEXEN2:\n\t\tdef32 = ED_GlobalAtOfs32(progfuncs, ofs);\n\t\tbreak;\n\tdefault:\n\t\texterns->Sys_Error(\"Bad struct type in PR_GlobalString\");\n\t\treturn \"\";\n\t}\n\n\tval = (void *)&pr_globals[ofs];\n\tif (!def32)\n\t{\n\t\tetype_t type;\n\t\t//urgh, this is so hideous\n#if !defined(MINIMAL) && !defined(OMIT_QCC)\n\t\tif (typehint == &type_float)\n\t\t\ttype = ev_float;\n\t\telse if (typehint == &type_string)\n\t\t\ttype = ev_string;\n\t\telse if (typehint == &type_vector)\n\t\t\ttype = ev_vector;\n\t\telse if (typehint == &type_function)\n\t\t\ttype = ev_function;\n\t\telse if (typehint == &type_field)\n\t\t\ttype = ev_field;\n\t\telse\n#endif\n\t\t\ttype = ev_integer;\n\t\ts = PR_ValueString (progfuncs, type, val, false);\n\t\tsprintf (line,\"%i(?)%s\", ofs, s);\n\t}\n\telse\n\t{\n\t\ts = PR_ValueString (progfuncs, def32->type, val, false);\n\t\tsprintf (line,\"%i(%s)%s\", ofs, def32->s_name+progfuncs->funcs.stringtable, s);\n\t}\n\n\ti = strlen(line);\n\tfor ( ; i<20 ; i++)\n\t\tstrcat (line,\" \");\n\tstrcat (line,\" \");\n\treturn line;\n}\n\nchar *PR_GlobalStringNoContents (progfuncs_t *progfuncs, int ofs)\n{\n\tint\t\ti;\n\tddef16_t\t*def16;\n\tddef32_t\t*def32;\n\tint nameofs = 0;\n\tstatic char\tline[128];\n\n\tswitch (current_progstate->structtype)\n\t{\n\tcase PST_DEFAULT:\n\tcase PST_KKQWSV:\n\t\tdef16 = ED_GlobalAtOfs16(progfuncs, ofs);\n\t\tif (def16)\n\t\t\tnameofs = def16->s_name;\n\t\tbreak;\n\tcase PST_QTEST:\n\tcase PST_FTE32:\n\tcase PST_UHEXEN2:\n\t\tdef32 = ED_GlobalAtOfs32(progfuncs, ofs);\n\t\tif (def32)\n\t\t\tnameofs = def32->s_name;\n\t\tbreak;\n\tdefault:\n\t\texterns->Sys_Error(\"Bad struct type in PR_GlobalStringNoContents\");\n\t}\n\n\tif (nameofs)\n\t\tsprintf (line,\"%i(%s)\", ofs, nameofs+progfuncs->funcs.stringtable);\n\telse\n\t{\n\t\tif (ofs >= OFS_RETURN && ofs < OFS_PARM0)\n\t\t\tsprintf (line,\"%i(return_%c)\", ofs, 'x' + (ofs - OFS_RETURN)%3);\n\t\telse if (ofs >= OFS_PARM0 && ofs < RESERVED_OFS)\n\t\t\tsprintf (line,\"%i(parm%i_%c)\", ofs, (ofs - OFS_PARM0)/3, 'x' + (ofs - OFS_PARM0)%3);\n\t\telse\n\t\t\tsprintf (line,\"%i(?\"\"?\"\"?)\", ofs);\n\t}\n\n\ti = strlen(line);\n\tfor ( ; i<20 ; i++)\n\t\tstrcat (line,\" \");\n\tstrcat (line,\" \");\n\n\treturn line;\n}\n\nchar *PR_GlobalStringImmediate (progfuncs_t *progfuncs, int ofs)\n{\n\tint\t\ti;\n\tstatic char\tline[128];\n\tsprintf (line,\"%i\", ofs);\n\n\ti = strlen(line);\n\tfor ( ; i<20 ; i++)\n\t\tstrcat (line,\" \");\n\tstrcat (line,\" \");\n\n\treturn line;\n}\n\n/*\n=============\nED_Print\n\nFor debugging\n=============\n*/\nvoid PDECL ED_Print (pubprogfuncs_t *ppf, struct edict_s *ed)\n{\n\tprogfuncs_t *progfuncs = (progfuncs_t*)ppf;\n\tint\t\tl;\n\tfdef_t\t*d;\n\tint\t\t*v;\n\tunsigned int\t\ti;unsigned int j;\n\tconst char\t*name;\n\tint\t\ttype;\n\n\tif (((edictrun_t *)ed)->ereftype == ER_FREE)\n\t{\n\t\texterns->Printf (\"FREE\\n\");\n\t\treturn;\n\t}\n\n\texterns->Printf(\"\\nEDICT %i:\\n\", NUM_FOR_EDICT(progfuncs, (struct edict_s *)ed));\n\tfor (i=1 ; i<prinst.numfields ; i++)\n\t{\n\t\td = &prinst.field[i];\n\t\tname = d->name;\n\t\tl = strlen(name);\n\t\tif (l >= 2 && name[l-2] == '_')\n\t\t\tcontinue;\t// skip _x, _y, _z vars\n\n\t\tv = (int *)((char *)edvars(ed) + d->ofs*4);\n\n\t// if the value is still all 0, skip the field\n#ifdef DEF_SAVEGLOBAL\n\t\ttype = d->type & ~DEF_SAVEGLOBAL;\n#else\n\t\ttype = d->type;\n#endif\n\n\t\tfor (j=0 ; j<type_size[type] ; j++)\n\t\t\tif (v[j])\n\t\t\t\tbreak;\n\t\tif (j == type_size[type])\n\t\t\tcontinue;\n\n\t\texterns->Printf (\"%s\",name);\n\t\tl = strlen (name);\n\t\twhile (l++ < 15)\n\t\t\texterns->Printf (\" \");\n\n\t\texterns->Printf (\"%s\\n\", PR_ValueString(progfuncs, d->type, (eval_t *)v, false));\n\t}\n}\n#if 0\nvoid ED_PrintNum (progfuncs_t *progfuncs, int ent)\n{\n\tED_Print (&progfuncs->funcs, EDICT_NUM(progfuncs, ent));\n}\n\n/*\n=============\nED_PrintEdicts\n\nFor debugging, prints all the entities in the current server\n=============\n*/\nvoid ED_PrintEdicts (progfuncs_t *progfuncs)\n{\n\tunsigned int\t\ti;\n\n\texterns->Printf (\"%i entities\\n\", sv_num_edicts);\n\tfor (i=0 ; i<sv_num_edicts ; i++)\n\t\tED_PrintNum (progfuncs, i);\n}\n\n/*\n=============\nED_Count\n\nFor debugging\n=============\n*/\nvoid ED_Count (progfuncs_t *progfuncs)\n{\n\tunsigned int\t\ti;\n\tedictrun_t\t*ent;\n\tunsigned int\t\tactive, models, solid, step;\n\n\tactive = models = solid = step = 0;\n\tfor (i=0 ; i<sv_num_edicts ; i++)\n\t{\n\t\tent = (edictrun_t *)EDICT_NUM(progfuncs, i);\n\t\tif (ent->isfree)\n\t\t\tcontinue;\n\t\tactive++;\n//\t\tif (ent->v.solid)\n//\t\t\tsolid++;\n//\t\tif (ent->v.model)\n//\t\t\tmodels++;\n//\t\tif (ent->v.movetype == MOVETYPE_STEP)\n//\t\t\tstep++;\n\t}\n\n\texterns->Printf (\"num_edicts:%3i\\n\", sv_num_edicts);\n\texterns->Printf (\"active    :%3i\\n\", active);\n//\tCon_Printf (\"view      :%3i\\n\", models);\n//\tCon_Printf (\"touch     :%3i\\n\", solid);\n//\tCon_Printf (\"step      :%3i\\n\", step);\n\n}\n#endif\n\n\n//============================================================================\n\n\n/*\n=============\nED_NewString\n=============\n*/\nchar *PDECL ED_NewString (pubprogfuncs_t *ppf, const char *string, int minlength, pbool demarkup)\n{\n\tprogfuncs_t *progfuncs = (progfuncs_t*)ppf;\n\tchar\t*newc, *new_p;\n\tint\t\ti,l;\n\n\tminlength++;\n\n\tl = strlen(string) + 1;\n\n\tnewc = progfuncs->funcs.AddressableAlloc (&progfuncs->funcs, l<minlength?minlength:l);\n\tif (!newc)\n\t\treturn progfuncs->funcs.stringtable;\n\n\tnew_p = newc;\n\n\tfor (i=0 ; i< l ; i++)\n\t{\n\t\tif (demarkup && string[i] == '\\\\' && i < l-1 && string[i+1] != 0)\n\t\t{\n\t\t\ti++;\n\t\t\tswitch(string[i])\n\t\t\t{\n\t\t\tcase 'n': *new_p++ = '\\n'; break;\n\t\t\tcase '\\'': *new_p++ = '\\''; break;\n\t\t\tcase '\\\"': *new_p++ = '\\\"'; break;\n\t\t\tcase 'r': *new_p++ = '\\r'; break;\n\t\t\tdefault:\n\t\t\t\t*new_p++ = '\\\\';\n\t\t\t\ti--;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t\t*new_p++ = string[i];\n\t}\n\n\treturn newc;\n}\n\n\n/*\n=============\nED_ParseEval\n\nCan parse either fields or globals\nreturns false if error\n=============\n*/\npbool\tPDECL ED_ParseEval (pubprogfuncs_t *ppf, eval_t *eval, int type, const char *s)\n{\n\tprogfuncs_t *progfuncs = (progfuncs_t*)ppf;\n\tint\t\ti;\n\tprogsnum_t\tmodule;\n\tchar\tstring[128];\n\tfdef_t\t*def;\n\tchar\t*v, *w;\n\tstring_t st;\n\tmfunction_t\t*func;\n\n\tswitch (type & ~DEF_SAVEGLOBAL)\n\t{\n\tcase ev_string:\n#ifdef QCGC\n\t\tst = PR_AllocTempString(&progfuncs->funcs, s);\n#else\n\t\tst = PR_StringToProgs(&progfuncs->funcs, ED_NewString (&progfuncs->funcs, s, 0, true));\n#endif\n\t\teval->string = st;\n\t\tbreak;\n\n\tcase ev_float:\n\t\teval->_float = (float)atof (s);\n\t\tbreak;\n\tcase ev_double:\n\t\teval->_double = atof (s);\n\t\tbreak;\n\n\tcase ev_integer:\n\t\teval->_int = strtol (s, NULL, 0);\n\t\tbreak;\n\tcase ev_uint:\n\t\teval->_uint = strtoul (s, NULL, 0);\n\t\tbreak;\n\tcase ev_int64:\n\t\teval->i64 = strtoll (s, NULL, 0);\n\t\tbreak;\n\tcase ev_uint64:\n\t\teval->u64 = strtoull (s, NULL, 0);\n\t\tbreak;\n\n\tcase ev_vector:\n\t\tstrncpy (string, s, sizeof(string));\n\t\tstring[sizeof(string)-1] = 0;\n\t\tv = string;\n\t\tw = string;\n\t\tfor (i=0 ; i<3 ; i++)\n\t\t{\n\t\t\twhile (*v && *v != ' ')\n\t\t\t\tv++;\n\t\t\tif (!*v)\n\t\t\t{\n\t\t\t\teval->_vector[i] = (float)atof (w);\n\t\t\t\tw = v;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t*v = 0;\n\t\t\t\teval->_vector[i] = (float)atof (w);\n\t\t\t\tw = v = v+1;\n\t\t\t}\n\t\t}\n\t\tbreak;\n\n\tcase ev_entity:\n\t\tif (!strncmp(s, \"entity \", 7))\t//cope with etos weirdness.\n\t\t\ts += 7;\n\t\teval->edict = atoi (s);\n\t\tbreak;\n\n\tcase ev_field:\n\t\tdef = ED_FindField (progfuncs, s);\n\t\tif (!def)\n\t\t{\n\t\t\texterns->Printf (\"Can't find field %s\\n\", s);\n\t\t\treturn false;\n\t\t}\n\t\teval->_int = def->ofs;\n\t\tbreak;\n\n\tcase ev_function:\n\t\tif (s[1]==':'&&s[2]=='\\0')\n\t\t{\n\t\t\teval->function = 0;\n\t\t\treturn true;\n\t\t}\n\t\tfunc = ED_FindFunction (progfuncs, s, &module, -1);\n\t\tif (!func)\n\t\t{\n\t\t\texterns->Printf (\"Can't find function %s\\n\", s);\n\t\t\treturn false;\n\t\t}\n\t\teval->function = (func - pr_progstate[module].functions) | (module<<24);\n\t\tbreak;\n\n\tdefault:\n\t\treturn false;\n\t}\n\treturn true;\n}\n\npbool\tED_ParseEpair (progfuncs_t *progfuncs, size_t qcptr, unsigned int fldofs, int fldtype, char *s)\n{\n\tpint64_t\ti;\n\tpuint64_t\tu;\n\tprogsnum_t module;\n\tfdef_t\t*def;\n\tstring_t st;\n\tmfunction_t\t*func;\n\tint type = fldtype & ~DEF_SAVEGLOBAL;\n\tdouble d;\n\teval_t *eval = (eval_t *)(progfuncs->funcs.stringtable + qcptr + (fldofs*sizeof(int)));\n\n\tswitch (type)\n\t{\n\tcase ev_string:\n#ifdef QCGC\n\t\tst = PR_AllocTempString(&progfuncs->funcs, s);\n#else\n\t\tst = PR_StringToProgs(&progfuncs->funcs, ED_NewString (&progfuncs->funcs, s, 0, true));\n#endif\n\t\teval->string = st;\n\t\tbreak;\n\n\tcase ev_float:\n\t\twhile(*s == ' ' || *s == '\\t')\n\t\t\ts++;\n\t\td = strtod(s, &s);\n\t\twhile(*s == ' ' || *s == '\\t')\n\t\t\ts++;\n\t\teval->_float = d;\n\t\tif (*s)\n\t\t\treturn false;\t//some kind of junk in there.\n\t\tbreak;\n\tcase ev_double:\n\t\twhile(*s == ' ' || *s == '\\t')\n\t\t\ts++;\n\t\td = strtod(s, &s);\n\t\twhile(*s == ' ' || *s == '\\t')\n\t\t\ts++;\n\t\teval->_double = d;\n\t\tif (*s)\n\t\t\treturn false;\t//some kind of junk in there.\n\t\tbreak;\n\n\tcase ev_integer:\n\t\twhile(*s == ' ' || *s == '\\t')\n\t\t\ts++;\n\t\ti = strtol(s, &s, 0);\n\t\twhile(*s == ' ' || *s == '\\t')\n\t\t\ts++;\n\t\teval->_int = i;\n\t\tif (*s)\n\t\t\treturn false;\t//some kind of junk in there.\n\t\tbreak;\n\tcase ev_entity:\t//ent references are simple ints for us.\n\tcase ev_uint:\n\t\twhile(*s == ' ' || *s == '\\t')\n\t\t\ts++;\n\t\tu = strtoul(s, &s, 0);\n\t\twhile(*s == ' ' || *s == '\\t')\n\t\t\ts++;\n\t\teval->_uint = u;\n\t\tif (*s)\n\t\t\treturn false;\t//some kind of junk in there.\n\t\tbreak;\n\n\tcase ev_int64:\n\t\twhile(*s == ' ' || *s == '\\t')\n\t\t\ts++;\n\t\ti = strtoll(s, &s, 0);\n\t\twhile(*s == ' ' || *s == '\\t')\n\t\t\ts++;\n\t\teval->i64 = i;\n\t\tif (*s)\n\t\t\treturn false;\t//some kind of junk in there.\n\t\tbreak;\n\tcase ev_uint64:\n\t\twhile(*s == ' ' || *s == '\\t')\n\t\t\ts++;\n\t\tu = strtoull(s, &s, 0);\n\t\twhile(*s == ' ' || *s == '\\t')\n\t\t\ts++;\n\t\teval->u64 = u;\n\t\tif (*s)\n\t\t\treturn false;\t//some kind of junk in there.\n\t\tbreak;\n\n\tcase ev_vector:\n\t\tfor (i=0 ; i<3 ; i++)\n\t\t{\n\t\t\twhile(*s == ' ' || *s == '\\t')\n\t\t\t\ts++;\n\t\t\td = strtod(s, &s);\n\t\t\teval->_vector[i] = d;\n\t\t}\n\t\twhile(*s == ' ' || *s == '\\t')\n\t\t\ts++;\n\t\tif (*s)\n\t\t\treturn false;\t//some kind of junk in there.\n\t\tbreak;\n\n\tcase ev_field:\n\t\tdef = ED_FindField (progfuncs, s);\n\t\tif (!def)\n\t\t{\n\t\t\texterns->Printf (\"Can't find field %s\\n\", s);\n\t\t\treturn false;\n\t\t}\n\t\teval->_int = def->ofs;\n\t\tbreak;\n\n\tcase ev_function:\n\t\tif (s[0] && s[1]==':'&&s[2]=='\\0')\t//this isn't right...\n\t\t{\n\t\t\teval->function = 0;\n\t\t\treturn true;\n\t\t}\n\t\tfunc = ED_FindFunction (progfuncs, s, &module, -1);\n\t\tif (!func)\n\t\t{\n\t\t\texterns->Printf (\"Can't find function %s\\n\", s);\n\t\t\treturn false;\n\t\t}\n\t\teval->function = (func - pr_progstate[module].functions) | (module<<24);\n\t\tbreak;\n\n\tdefault:\n\t\treturn false;\n\t}\n\treturn true;\n}\n\n/*\n====================\nED_ParseEdict\n\nParses an edict out of the given string, returning the new position\ned should be a properly initialized empty edict.\nUsed for initial level load and for savegames.\n====================\n*/\n#if 1\nstatic const char *ED_ParseEdict (progfuncs_t *progfuncs, const char *data, edictrun_t *ent, pbool *out_maphack)\n{\n\tfdef_t\t\t*key;\n\tpbool\tinit;\n\tchar\t\tkeyname[256];\n\tint\t\t\tn;\n\tint\t\t\tnest = 1;\n\n//\teval_t\t\t*val;\n\n\tinit = false;\n\n// clear it\n//\tif (ent != (edictrun_t *)sv_edicts)\t// hack\n//\t\tmemset (ent+1, 0, pr_edict_size - sizeof(edictrun_t));\n\n// go through all the dictionary pairs\n\twhile (1)\n\t{\n\t// parse key\n\t\tdata = QCC_COM_Parse (data);\n\t\tif (qcc_token[0] == '}')\n\t\t{\n\t\t\tif (--nest)\n\t\t\t\tcontinue;\n\t\t\tbreak;\n\t\t}\n\t\tif (qcc_token[0] == '{' && !qcc_token[1])\n\t\t\tnest++;\n\t\tif (!data)\n\t\t{\n\t\t\texterns->Printf (\"ED_ParseEntity: EOF without closing brace\\n\");\n\t\t\treturn NULL;\n\t\t}\n\t\tif (nest > 1)\n\t\t\tcontinue;\n\n\t\tstrncpy (keyname, qcc_token, sizeof(keyname)-1);\n\t\tkeyname[sizeof(keyname)-1] = 0;\n\n\t\t// another hack to fix heynames with trailing spaces\n\t\tn = strlen(keyname);\n\t\twhile (n && keyname[n-1] == ' ')\n\t\t{\n\t\t\tkeyname[n-1] = 0;\n\t\t\tn--;\n\t\t}\n\n\t// parse value\n\t\tdata = QCC_COM_Parse (data);\n\t\tif (!data)\n\t\t{\n\t\t\texterns->Printf (\"ED_ParseEntity: EOF without closing brace\\n\");\n\t\t\treturn NULL;\n\t\t}\n\n\t\tif (qcc_token[0] == '}')\n\t\t{\n\t\t\texterns->Printf (\"ED_ParseEntity: closing brace without data\\n\");\n\t\t\treturn NULL;\n\t\t}\n\n\t\tinit = true;\n\n// keynames with a leading underscore are used for utility comments,\n// and are immediately discarded by quake\n\t\tif (keyname[0] == '_')\n\t\t{\n\t\t\tif (externs->badfield)\n\t\t\t\texterns->badfield(&progfuncs->funcs, (struct edict_s*)ent, keyname, qcc_token);\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (!strcmp(keyname, \"angle\"))\t//Quake anglehack - we've got to leave it in cos it doesn't work for quake otherwise, and this is a QuakeC lib!\n\t\t{\n\t\t\tif ((key = ED_FindField (progfuncs, \"angles\")))\n\t\t\t{\n\t\t\t\tQC_snprintfz (qcc_token, sizeof(qcc_token), \"0 %f 0\", atof(qcc_token));\t//change it from yaw to 3d angle\n\t\t\t\tgoto cont;\n\t\t\t}\n\t\t}\n\n\t\tkey = ED_FindField (progfuncs, keyname);\n\t\tif (!key)\n\t\t{\n\t\t\tif (!strcmp(keyname, \"light\"))\t//Quake lighthack - allows a field name and a classname to go by the same thing in the level editor\n\t\t\t\tif ((key = ED_FindField (progfuncs, \"light_lev\")))\n\t\t\t\t\tgoto cont;\n\t\t\tif (externs->badfield && externs->badfield(&progfuncs->funcs, (struct edict_s*)ent, keyname, qcc_token))\n\t\t\t\tcontinue;\n\t\t\tPR_DPrintf (\"'%s' is not a field\\n\", keyname);\n\t\t\tcontinue;\n\t\t}\n\ncont:\n\t\tswitch(key->type)\n\t\t{\n\t\tcase ev_function:\n\t\tcase ev_field:\n\t\tcase ev_entity:\n\t\tcase ev_pointer:\n\t\t\t*out_maphack = true;\t//one of these types of fields means evil maphacks are at play.\n\t\t\tbreak;\n\t\t}\n\t\tif (!ED_ParseEpair (progfuncs, (char*)ent->fields - progfuncs->funcs.stringtable, key->ofs, key->type, qcc_token))\n\t\t{\n\t\t\tif (externs->badfield && externs->badfield(&progfuncs->funcs, (struct edict_s*)ent, keyname, qcc_token))\n\t\t\t\tcontinue;\n\t\t\tcontinue;\n//\t\t\tSys_Error (\"ED_ParseEdict: parse error on entities\");\n\t\t}\n\t}\n\n\tif (!init)\n\t\tent->ereftype = ER_FREE;\n\n\treturn data;\n}\n#endif\n\nstatic void PR_Cat(char *out, const char *in, size_t *len, size_t max)\n{\n\tsize_t newl = strlen(in);\n\tmax-=1;\n\tif (*len + newl > max)\n\t\tnewl = max - *len;\t//truncate\n\tmemcpy(out + *len, in, newl+1);\n\t*len += newl;\n}\n\n/*\n================\nED_LoadFromFile\n\nThe entities are directly placed in the array, rather than allocated with\nED_Alloc, because otherwise an error loading the map would have entity\nnumber references out of order.\n\nCreates a server's entity / program execution context by\nparsing textual entity definitions out of an ent file.\n\nUsed for both fresh maps and savegame loads.  A fresh map would also need\nto call ED_CallSpawnFunctions () to let the objects initialize themselves.\n================\n*/\n\nchar *ED_WriteGlobals(progfuncs_t *progfuncs, char *buf, size_t *bufofs, size_t bufmax)\t//switch first.\n{\n#define AddS(str) PR_Cat(buf, str, bufofs, bufmax)\n\tint\t\t*v;\n\tddef32_t\t\t*def32;\n\tddef16_t\t\t*def16;\n\tunsigned int\t\t\ti;\n//\tunsigned int j;\n\tconst char\t*name;\n\tint\t\t\ttype;\n\tint curprogs = prinst.pr_typecurrent;\n\tint len;\n\tswitch(current_progstate->structtype)\n\t{\n\tcase PST_DEFAULT:\n\tcase PST_KKQWSV:\n\t\tfor (i=0 ; i<pr_progs->numglobaldefs ; i++)\n\t\t{\n\t\t\tdef16 = &pr_globaldefs16[i];\n\t\t\tname = def16->s_name + progfuncs->funcs.stringtable;\n\t\t\tlen = strlen(name);\n\t\t\tif (!*name)\n\t\t\t\tcontinue;\n\t\t\tif (len >= 2 && name[len-2] == '_' && (name[len-1] == 'x' || name[len-1] == 'y' || name[len-1] == 'z'))\n\t\t\t\tcontinue;\t// skip _x, _y, _z vars (vector components, which are saved as one vector not 3 floats)\n\n\t\t\ttype = def16->type;\n\n#ifdef DEF_SAVEGLOBAL\n\t\t\tif ( !(def16->type & DEF_SAVEGLOBAL) )\n\t\t\t\tcontinue;\n\t\t\ttype &= ~DEF_SAVEGLOBAL;\n#endif\n\t\t\tif (current_progstate->types)\n\t\t\t\ttype = current_progstate->types[type].type;\n\t\t\tif (type == ev_function)\n\t\t\t{\n\t\t\t\tv = (int *)&current_progstate->globals[def16->ofs];\n\t\t\t\tif ((v[0]&0xff000000)>>24 == (unsigned)curprogs)\t//same progs\n\t\t\t\t{\n\t\t\t\t\tif (!progfuncs->funcs.stringtable[current_progstate->functions[v[0]&0x00ffffff].s_name])\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\telse if (!strcmp(current_progstate->functions[v[0]&0x00ffffff].s_name+ progfuncs->funcs.stringtable, name))\t//names match. Assume function is at initial value.\n\t\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tif (curprogs!=0)\n\t\t\t\tif ((v[0]&0xff000000)>>24 == 0)\n\t\t\t\t\tif (!ED_FindFunction(progfuncs, name, NULL, curprogs))\t//defined as extern\n\t\t\t\t\t{\n\t\t\t\t\t\tif (!progfuncs->funcs.stringtable[pr_progstate[0].functions[v[0]&0x00ffffff].s_name])\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\telse if (!strcmp(pr_progstate[0].functions[v[0]&0x00ffffff].s_name + progfuncs->funcs.stringtable, name))\t//same name.\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t//else function has been redirected externally.\n\t\t\t\tgoto add16;\n\t\t\t}\n\t\t\telse if (type != ev_string\t//anything other than these is not saved\n\t\t\t&& type != ev_float\n\t\t\t&& type != ev_double\n\t\t\t&& type != ev_integer\n\t\t\t&& type != ev_uint\n\t\t\t&& type != ev_int64\n\t\t\t&& type != ev_uint64\n\t\t\t&& type != ev_entity\n\t\t\t&& type != ev_vector)\n\t\t\t\tcontinue;\n\n\t\t\tv = (int *)&current_progstate->globals[def16->ofs];\n\n/*\t\t\t// make sure the value is not null, where there's no point in saving\n\t\t\tfor (j=0 ; j<type_size[type] ; j++)\n\t\t\t\tif (v[j])\n\t\t\t\t\tbreak;\n\t\t\tif (j == type_size[type])\n\t\t\t\tcontinue;\n*/\n add16:\n\t\t\tAddS(\"\\\"\"); AddS(name); AddS(\"\\\" \\\"\"); AddS(PR_UglyValueString(&progfuncs->funcs, def16->type&~DEF_SAVEGLOBAL, (eval_t *)v)); AddS(\"\\\"\\n\");\n\t\t}\n\t\tbreak;\n\tcase PST_QTEST:\n\tcase PST_FTE32:\n\tcase PST_UHEXEN2:\n\t\tfor (i=0 ; i<pr_progs->numglobaldefs ; i++)\n\t\t{\n\t\t\tsize_t nlen;\n\t\t\tdef32 = &pr_globaldefs32[i];\n\t\t\tname = PR_StringToNative(&progfuncs->funcs, def32->s_name);\n\t\t\tnlen = strlen(name);\n\t\t\tif (nlen >= 3 && name[nlen-2] == '_')\n\t\t\t\tcontinue;\t// skip _x, _y, _z vars (vector components, which are saved as one vector not 3 floats)\n\n\t\t\ttype = def32->type;\n\n#ifdef DEF_SAVEGLOBAL\n\t\t\tif ( !(def32->type & DEF_SAVEGLOBAL) )\n\t\t\t\tcontinue;\n\t\t\ttype &= ~DEF_SAVEGLOBAL;\n#endif\n\t\t\tif (current_progstate->types)\n\t\t\t\ttype = current_progstate->types[type].type;\n\t\t\tif (type == ev_function)\n\t\t\t{\n\t\t\t\tv = (int *)&current_progstate->globals[def32->ofs];\n\t\t\t\tif ((v[0]&0xff000000)>>24 == (unsigned)curprogs)\t//same progs\n\t\t\t\t\tif (!strcmp(current_progstate->functions[v[0]&0x00ffffff].s_name+ progfuncs->funcs.stringtable, name))\t//names match. Assume function is at initial value.\n\t\t\t\t\t\tcontinue;\n\n\t\t\t\tif (curprogs!=0)\n\t\t\t\tif ((v[0]&0xff000000)>>24 == 0)\n\t\t\t\t\tif (!ED_FindFunction(progfuncs, name, NULL, curprogs))\t//defined as extern\n\t\t\t\t\t\tif (!strcmp(pr_progstate[0].functions[v[0]&0x00ffffff].s_name+ progfuncs->funcs.stringtable, name))\t//same name.\n\t\t\t\t\t\t\tcontinue;\n\n\t\t\t\t//else function has been redirected externally.\n\t\t\t\tgoto add32;\n\t\t\t}\n\t\t\telse if (type != ev_string\t//anything other than these is not saved\n\t\t\t&& type != ev_float\n\t\t\t&& type != ev_double\n\t\t\t&& type != ev_integer\n\t\t\t&& type != ev_uint\n\t\t\t&& type != ev_int64\n\t\t\t&& type != ev_uint64\n\t\t\t&& type != ev_entity\n\t\t\t&& type != ev_vector)\n\t\t\t\tcontinue;\n\n\t\t\tv = (int *)&current_progstate->globals[def32->ofs];\n\n/*\t\t\t// make sure the value is not null, where there's no point in saving\n\t\t\tfor (j=0 ; j<type_size[type] ; j++)\n\t\t\t\tif (v[j])\n\t\t\t\t\tbreak;\n\t\t\tif (j == type_size[type])\n\t\t\t\tcontinue;*/\nadd32:\n\t\t\tAddS(\"\\\"\"); AddS(name); AddS(\"\\\" \\\"\"); AddS(PR_UglyValueString(&progfuncs->funcs, def32->type&~DEF_SAVEGLOBAL, (eval_t *)v)); AddS(\"\\\"\\n\");\n\t\t}\n\t\tbreak;\n\tdefault:\n\t\texterns->Sys_Error(\"Bad struct type in SaveEnts\");\n\t}\n\n\treturn buf;\n#undef AddS\n}\n\nchar *ED_WriteEdict(progfuncs_t *progfuncs, edictrun_t *ed, char *buf, size_t *bufofs, size_t bufmax, pbool q1compatible)\n{\n#define AddS(str) PR_Cat(buf, str, bufofs, bufmax)\n\tfdef_t\t*d;\n\tint\t\t*v;\n\tunsigned int\t\ti;unsigned int j;\n\tconst char\t*name;\n\tint\t\ttype;\n\tint len;\n\tchar *tmp;\n\n\tfor (i=0 ; i<prinst.numfields ; i++)\n\t{\n\t\td = &prinst.field[i];\n\t\tname = d->name;\n\t\tlen = strlen(name);\n\t\tif (len>4 && (name[len-2] == '_' && (name[len-1] == 'x' || name[len-1] == 'y' || name[len-1] == 'z')))\n\t\t\tcontinue;\t// skip _x, _y, _z vars\n\n\t\tv = (int *)((char*)ed->fields + d->ofs*4);\n\n\t// if the value is still all 0, skip the field\n#ifdef DEF_SAVEGLOBAL\n\t\ttype = d->type & ~DEF_SAVEGLOBAL;\n#else\n\t\ttype = d->type;\n#endif\n\n\t\tfor (j=0 ; j<type_size[type] ; j++)\n\t\t\tif (v[j])\n\t\t\t\tbreak;\n\t\tif (j == type_size[type])\n\t\t\tcontinue;\n\n\t\t//add it to the file\n\t\tAddS(\"\\\"\"); AddS(name); AddS(\"\\\" \");\n\t\tif (q1compatible)\n\t\t\ttmp = PR_UglyOldValueString(progfuncs, d->type, (eval_t *)v);\n\t\telse\n\t\t\ttmp = PR_UglyValueString(&progfuncs->funcs, d->type, (eval_t *)v);\n\t\tAddS(\"\\\"\"); AddS(tmp); AddS(\"\\\"\\n\");\n\t}\n\n\treturn buf;\n#undef AddS\n}\n\n//just a simple helper that makes sure the s_name+s_file values are actually valid. some qccs generate really dodgy values intended to crash decompilers, but also crash debuggers too.\nstatic char *PR_StaticString(progfuncs_t *progfuncs, string_t thestring)\n{\n\tif (thestring <= 0 || thestring >= progfuncs->funcs.stringtablesize)\n\t\treturn \"???\";\n\treturn thestring + progfuncs->funcs.stringtable;\n}\n\nchar *PR_SaveCallStack (progfuncs_t *progfuncs, char *buf, size_t *bufofs, size_t bufmax)\n{\n#define AddS(str) PR_Cat(buf, str, bufofs, bufmax)\n\tchar buffer[8192];\n\tconst mfunction_t\t*f;\n\tint\t\t\ti;\n\tint progs;\n\n\tint arg;\n\tint *globalbase;\n\n\tprogs = -1;\n\n\tif (prinst.pr_depth == 0)\n\t{\n\t\tAddS (\"<NO STACK>\\n\");\n\t\treturn buf;\n\t}\n\n\tglobalbase = (int *)pr_globals + prinst.pr_xfunction->parm_start + prinst.pr_xfunction->locals;\n\n\tprinst.pr_stack[prinst.pr_depth].f = prinst.pr_xfunction;\n\tfor (i=prinst.pr_depth ; i>0 ; i--)\n\t{\n\t\tf = prinst.pr_stack[i].f;\n\n\t\tif (!f)\n\t\t{\n\t\t\tAddS (\"<NO FUNCTION>\\n\");\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (prinst.pr_stack[i].progsnum != progs)\n\t\t\t{\n\t\t\t\tprogs = prinst.pr_stack[i].progsnum;\n\n\t\t\t\tsprintf(buffer, \"//%i %s\\n\", progs, pr_progstate[progs].filename);\n\t\t\t\tAddS (buffer);\n\t\t\t}\n\t\t\tif (!f->s_file)\n\t\t\t\tsprintf(buffer, \"\\t\\\"%i:%s\\\"\\n\", progs, PR_StaticString(progfuncs, f->s_name));\n\t\t\telse\n\t\t\t\tsprintf(buffer, \"\\t\\\"%i:%s\\\" //%s\\n\", progs, PR_StaticString(progfuncs, f->s_name), PR_StaticString(progfuncs, f->s_file));\n\t\t\tAddS (buffer);\n\n\t\t\tAddS (\"\\t{\\n\");\n\t\t\tfor (arg = 0; arg < f->locals; arg++)\n\t\t\t{\n\t\t\t\tddef16_t *local;\n\t\t\t\tlocal = ED_GlobalAtOfs16(progfuncs, f->parm_start+arg);\n\t\t\t\tif (!local)\n\t\t\t\t\tsprintf(buffer, \"\\t\\tofs%i %i // %f\\n\", f->parm_start+arg, *(int *)(globalbase - f->locals+arg), *(float *)(globalbase - f->locals+arg) );\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tif (local->type == ev_entity)\n\t\t\t\t\t{\n\t\t\t\t\t\tsprintf(buffer, \"\\t\\t\\\"%s\\\" \\\"entity %i\\\"\\n\", PR_StaticString(progfuncs, local->s_name), ((eval_t*)(globalbase - f->locals+arg))->edict);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\tsprintf(buffer, \"\\t\\t\\\"%s\\\"\\t\\\"%s\\\"\\n\", PR_StaticString(progfuncs, local->s_name), PR_ValueString(progfuncs, local->type, (eval_t*)(globalbase - f->locals+arg), false));\n\n\t\t\t\t\tif (local->type == ev_vector)\n\t\t\t\t\t\targ+=2;\n\t\t\t\t}\n\t\t\t\tAddS (buffer);\n\t\t\t}\n\t\t\tAddS (\"\\t}\\n\");\n\n\t\t\tif (i == prinst.pr_depth)\n\t\t\t\tglobalbase = prinst.localstack + prinst.localstack_used - f->locals;\n\t\t\telse\n\t\t\t\tglobalbase -= f->locals;\n\t\t}\n\t}\n\treturn buf;\n#undef AddS\n}\n\n//there are two ways of saving everything.\n//0 is to save just the entities.\n//1 is to save the entites, and all the progs info so that all the variables are saved off, and it can be reloaded to exactly how it was (provided no files or data has been changed outside, like the progs.dat for example)\n//2 is for vanilla-compatible saved games\n//3 is a (human-readable) coredump\n//4 is binary saved games.\nchar *PDECL PR_SaveEnts(pubprogfuncs_t *ppf, char *buf, size_t *bufofs, size_t bufmax, int alldata)\n{\n\tprogfuncs_t *progfuncs = (progfuncs_t*)ppf;\n#define AddS(str) PR_Cat(buf, str, bufofs, bufmax)\n\tunsigned int a;\n\tchar *buffree = NULL;\n\tint oldprogs;\n\n\tif (!buf)\n\t{\n\t\tif (bufmax <= 0)\n\t\t\tbufmax = 5*1024*1024;\n\t\tbuffree = buf = externs->memalloc(bufmax);\n\t}\n\t*bufofs = 0;\n\n\tswitch(alldata)\n\t{\n\tdefault:\n\t\treturn NULL;\n\tcase 2:\n\t\t//special Q1 savegame compatability mode.\n\t\t//engine will need to store references to progs type and will need to preload the progs and inti the ents itself before loading.\n\n\t\t//Make sure there is only 1 progs loaded.\n\t\tfor (a = 1; a < prinst.maxprogs; a++)\n\t\t{\n\t\t\tif (pr_progstate[a].progs)\n\t\t\t\tbreak;\n\t\t}\n\t\tif (!pr_progstate[0].progs || a != prinst.maxprogs)\t//the state of the progs wasn't Q1 compatible.\n\t\t{\n\t\t\texterns->memfree(buffree);\n\t\t\treturn NULL;\n\t\t}\n\n\t\t//write the globals\n\t\tAddS (\"{\\n\");\n\n\t\toldprogs = prinst.pr_typecurrent;\n\t\tPR_SwitchProgs(progfuncs, 0);\n\t\tED_WriteGlobals(progfuncs, buf, bufofs, bufmax);\n\t\tPR_SwitchProgs(progfuncs, oldprogs);\n\n\t\tAddS (\"}\\n\");\n\n\n\t\t//write the ents\n\t\tfor (a = 0; a < sv_num_edicts; a++)\n\t\t{\n\t\t\tchar head[64];\n\t\t\tedictrun_t *ed = (edictrun_t *)EDICT_NUM(progfuncs, a);\n\n\t\t\tQC_snprintfz(head, sizeof(head), \"{//%i\\n\", a);\n\t\t\tAddS (head);\n\n\t\t\tif (ed->ereftype == ER_ENTITY)\t//free entities write a {} with no data. the loader detects this specifically.\n\t\t\t\tED_WriteEdict(progfuncs, ed, buf, bufofs, bufmax, true);\n\n\t\t\tAddS (\"}\\n\");\n\t\t}\n\n\t\treturn buf;\n\n\tcase 0:\t//Writes entities only\n\t\tbreak;\n\tcase 1:\n\tcase 3:\n\t\tAddS(\"general {\\n\");\n\t\tAddS(qcva(\"\\\"maxprogs\\\" \\\"%i\\\"\\n\", prinst.maxprogs));\n//\t\tAddS(qcva(\"\\\"maxentities\\\" \\\"%i\\\"\\n\", maxedicts));\n//\t\tAddS(qcva(\"\\\"mem\\\" \\\"%i\\\"\\n\", hunksize));\n//\t\tAddS(qcva(\"\\\"crc\\\" \\\"%i\\\"\\n\", header_crc));\n\t\tAddS(qcva(\"\\\"numentities\\\" \\\"%i\\\"\\n\", sv_num_edicts));\n\t\tAddS(\"}\\n\");\n\n\t\toldprogs = prinst.pr_typecurrent;\n\n\t\tfor (a = 0; a < prinst.maxprogs; a++)\n\t\t{\n\t\t\tif (!pr_progstate[a].progs)\n\t\t\t\tcontinue;\n\t\t\t{\n\t\t\t\tAddS (qcva(\"progs %i {\\n\", a));\n\t\t\t\tAddS (qcva(\"\\\"filename\\\" \\\"%s\\\"\\n\", pr_progstate[a].filename));\n\t\t\t\tAddS (qcva(\"\\\"crc\\\" \\\"%i\\\"\\n\", pr_progstate[a].progs->crc));\n\t\t\t\tAddS (\"}\\n\");\n\t\t\t}\n\t\t}\n\n\t\tif (alldata == 3)\n\t\t{\n\t\t\t//include callstack\n\t\t\tAddS(\"stacktrace {\\n\");\n\t\t\tPR_SaveCallStack(progfuncs, buf, bufofs, bufmax);\n\t\t\tAddS(\"}\\n\");\n\t\t}\n\n\t\tfor (a = 0; a < prinst.maxprogs; a++)\t//I would mix, but external functions rely on other progs being loaded\n\t\t{\n\t\t\tif (!pr_progstate[a].progs)\n\t\t\t\tcontinue;\n\n\t\t\tAddS (qcva(\"globals %i {\\n\", a));\n\n\t\t\tPR_SwitchProgs(progfuncs, a);\n\n\t\t\tED_WriteGlobals(progfuncs, buf, bufofs, bufmax);\n\n\t\t\tAddS (\"}\\n\");\n\t\t}\n\t\tPR_SwitchProgs(progfuncs, oldprogs);\n\t}\n\n\tfor (a = 0; a < sv_num_edicts; a++)\n\t{\n\t\tedictrun_t *ed = (edictrun_t *)EDICT_NUM(progfuncs, a);\n\n\t\tif (!ed || ed->ereftype != ER_ENTITY)\n\t\t\tcontinue;\n\n\t\tAddS (qcva(\"entity %i{\\n\", a));\n\n\t\tED_WriteEdict(progfuncs, ed, buf, bufofs, bufmax, false);\n\n\t\tAddS (\"}\\n\");\n\t}\n\n\treturn buf;\n\n#undef AddS\n}\n\n//if 'general' block is found, this is a compleate state, otherwise, we should spawn entities like\nint PDECL PR_LoadEnts(pubprogfuncs_t *ppf, const char *file, void *ctx, void (PDECL *memoryreset) (pubprogfuncs_t *progfuncs, void *ctx), void (PDECL *entspawned) (pubprogfuncs_t *progfuncs, struct edict_s *ed, void *ctx, const char *entstart, const char *entend), pbool(PDECL *extendedterm)(pubprogfuncs_t *progfuncs, void *ctx, const char **extline))\n{\n\tprogfuncs_t *progfuncs = (progfuncs_t*)ppf;\n\tconst char *datastart;\n\n//\teval_t *selfvar = NULL;\n//\teval_t *var;\n//\tconst char *spawnwarned[20] = {NULL};\n\n\tchar filename[128];\n\tint num;\n\tedictrun_t *ed=NULL;\n\tddef16_t *d16;\n\tddef32_t *d32;\n\tvoid *oldglobals = NULL;\n\tint oldglobalssize = 0;\n\n\textern edictrun_t tempedict;\n\n\tint entsize = 0;\n\tint numents = 0;\n\tpbool maphacks = false;\n\n\tpbool resethunk=0;\n\tpbool isloadgame;\n\tif (file && !strncmp(file, \"loadgame\", 8))\n\t{\t//this is internally inserted for legacy saved games.\n\t\tisloadgame = true;\n\t\tnuments = -1;\n\t\tfile+=8;\n\t}\n\telse\n\t\tisloadgame = false;\n\n\twhile(1)\n\t{\n\t\tdatastart = file;\n\n\t\tif (extendedterm)\n\t\t{\n\t\t\t//skip simple leading whitespace\n\t\t\twhile (qcc_iswhite(*file))\n\t\t\t\tfile++;\n\t\t\tif (file[0] == '/' && file[1] == '*')\n\t\t\t{\t//looks like we have a hidden extension.\n\t\t\t\tfile+=2;\n\t\t\t\tfor(;;)\n\t\t\t\t{\n\t\t\t\t\t//skip to end of line\n\t\t\t\t\tif (!*file)\n\t\t\t\t\t\tbreak;\t//unexpected EOF\n\t\t\t\t\telse if (file[0] == '*' && file[1] == '/')\n\t\t\t\t\t{\t//end of comment\n\t\t\t\t\t\tfile+=2;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\telse if (*file != '\\n')\n\t\t\t\t\t{\n\t\t\t\t\t\tfile++;\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tfile++;\t//skip past the \\n\n\t\t\t\t\twhile (*file == ' ' || *file == '\\t')\n\t\t\t\t\t\tfile++;\t//skip leading indentation\n\n\t\t\t\t\tif (file[0] == '*' && file[1] == '/')\n\t\t\t\t\t{\t//end of comment\n\t\t\t\t\t\tfile+=2;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\telse if (*file == '/')\n\t\t\t\t\t\tcontinue;\t//embedded comment. ignore the line. not going to do nested comments, because those are not normally valid anyway, just C++-style inside C-style.\n\t\t\t\t\telse if (extendedterm(ppf, ctx, &file))\n\t\t\t\t\t\t;\t//found a term we recognised\n\t\t\t\t\telse\n\t\t\t\t\t\t;\t//unknown line, but this is a comment so whatever\n\n\t\t\t\t}\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\n\t\tfile = QCC_COM_Parse(file);\n\t\tif (file == NULL)\n\t\t\tbreak;\t//finished reading file\n\t\telse if (!strcmp(qcc_token, \"Version\"))\n\t\t{\n\t\t\tfile = QCC_COM_Parse(file);\n\t\t\t//qcc_token is a version number\n\t\t}\n\t\telse if (!strcmp(qcc_token, \"entity\"))\n\t\t{\n\t\t\tif (entsize == 0 && resethunk)\t//edicts have not yet been initialized, and this is a compleate load (memsize has been set)\n\t\t\t{\n\t\t\t\tentsize = PR_InitEnts(&progfuncs->funcs, prinst.maxedicts);\n//\t\t\t\tsv_num_edicts = numents;\n\n\t\t\t\tfor (num = 0; num < numents; num++)\n\t\t\t\t{\n\t\t\t\t\ted = (edictrun_t *)EDICT_NUM(progfuncs, num);\n\n\t\t\t\t\tif (!ed)\n\t\t\t\t\t{\n\t\t\t\t\t\ted = (edictrun_t *)ED_AllocIndex(&progfuncs->funcs, num, false, 0);\n\t\t\t\t\t\ted->ereftype = ER_FREE;\n\t\t\t\t\t\tif (externs->entspawn)\n\t\t\t\t\t\t\texterns->entspawn((struct edict_s *) ed, true);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tfile = QCC_COM_Parse(file);\n\t\t\tnum = atoi(qcc_token);\n\t\t\tdatastart = file;\n\t\t\tfile = QCC_COM_Parse(file);\n\t\t\tif (qcc_token[0] != '{')\n\t\t\t\texterns->Sys_Error(\"Progs loading found %s, not '{'\", qcc_token);\n\t\t\tif (!resethunk)\n\t\t\t\ted = (edictrun_t *)ED_Alloc(&progfuncs->funcs, false, 0);\n\t\t\telse\n\t\t\t{\n\t\t\t\ted = (edictrun_t *)EDICT_NUM(progfuncs, num);\n\n\t\t\t\tif (!ed)\n\t\t\t\t{\n\t\t\t\t\texterns->Sys_Error(\"Edict was not allocated\\n\");\n\t\t\t\t\ted = (edictrun_t *)ED_AllocIndex(&progfuncs->funcs, num, false, 0);\n\t\t\t\t}\n\t\t\t}\n\t\t\ted->ereftype = ER_ENTITY;\n\t\t\tif (externs->entspawn)\n\t\t\t\texterns->entspawn((struct edict_s *) ed, true);\n\t\t\tfile = ED_ParseEdict(progfuncs, file, ed, &maphacks);\n\n\t\t\tif (entspawned)\n\t\t\t\tentspawned(ppf, (struct edict_s *)ed, ctx, datastart, file);\n\t\t}\n\t\telse if (!strcmp(qcc_token, \"progs\"))\n\t\t{\n\t\t\tfile = QCC_COM_Parse(file);\n\t\t\tnum = atoi(qcc_token);\n\t\t\tfile = QCC_COM_Parse(file);\n\t\t\tif (qcc_token[0] != '{')\n\t\t\t\texterns->Sys_Error(\"Progs loading found %s, not '{'\", qcc_token);\n\n\n\t\t\tfilename[0] = '\\0';\n\n\t\t\twhile(1)\n\t\t\t{\n\t\t\t\tfile = QCC_COM_Parse(file);\t//read the key\n\t\t\t\tif (!file)\n\t\t\t\t\texterns->Sys_Error(\"EOF in progs block\");\n\n\t\t\t\tif (!strcmp(\"filename\", qcc_token))\t//check key get and save values\n\t\t\t\t\t{file = QCC_COM_Parse(file); strcpy(filename, qcc_token);}\n\t\t\t\telse if (!strcmp(\"crc\", qcc_token))\n\t\t\t\t\t{file = QCC_COM_Parse(file); /*header_crc = atoi(qcc_token);*/}\n\t\t\t\telse if (!strcmp(\"numbuiltins\", qcc_token))\t//no longer supported.\n\t\t\t\t\t{file = QCC_COM_Parse(file); /*qcc_token unused*/}\n\t\t\t\telse if (qcc_token[0] == '}')\t//end of block\n\t\t\t\t\tbreak;\n\t\t\t\telse\n\t\t\t\t\texterns->Sys_Error(\"Bad key \\\"%s\\\" in progs block\", qcc_token);\n\t\t\t}\n\n\t\t\tPR_ReallyLoadProgs(progfuncs, filename, &pr_progstate[num], true);\n\n\t\t\tif (num == 0 && oldglobals)\n\t\t\t{\n\t\t\t\tif (pr_progstate[0].globals_bytes == oldglobalssize)\n\t\t\t\t\tmemcpy(pr_progstate[0].globals, oldglobals, pr_progstate[0].globals_bytes);\n\t\t\t\tfree(oldglobals);\n\t\t\t\toldglobals = NULL;\n\t\t\t}\n\n\t\t\tPR_SwitchProgs(progfuncs, 0);\n\t\t}\n\t\telse if (!strcmp(qcc_token, \"globals\"))\n\t\t{\n\t\t\tif (entsize == 0 && resethunk)\t//by the time we parse some globals, we MUST have loaded all progs\n\t\t\t{\n\t\t\t\tentsize = PR_InitEnts(&progfuncs->funcs, prinst.maxedicts);\n\t\t\t\tif (memoryreset)\n\t\t\t\t\tmemoryreset(&progfuncs->funcs, ctx);\n//\t\t\t\tsv_num_edicts = numents;\n\n\t\t\t\tfor (num = 0; num < numents; num++)\n\t\t\t\t{\n\t\t\t\t\ted = (edictrun_t *)EDICT_NUM(progfuncs, num);\n\n\t\t\t\t\tif (!ed)\n\t\t\t\t\t{\n\t\t\t\t\t\ted = (edictrun_t *)ED_AllocIndex(&progfuncs->funcs, num, false, 0);\n\t\t\t\t\t\ted->ereftype = ER_FREE;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (externs->entspawn)\n\t\t\t\t\t\texterns->entspawn((struct edict_s *) ed, true);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tfile = QCC_COM_Parse(file);\n\t\t\tnum = atoi(qcc_token);\n\n\t\t\tfile = QCC_COM_Parse(file);\n\t\t\tif (qcc_token[0] != '{')\n\t\t\t\texterns->Sys_Error(\"Globals loading found \\'%s\\', not '{'\", qcc_token);\n\n\t\t\tPR_SwitchProgs(progfuncs, num);\n\t\t\twhile (1)\n\t\t\t{\n\t\t\t\tfile = QCC_COM_Parse(file);\n\t\t\t\tif (qcc_token[0] == '}')\n\t\t\t\t\tbreak;\n\t\t\t\telse if (!qcc_token[0] || !file)\n\t\t\t\t\texterns->Sys_Error(\"EOF when parsing global values\");\n\n\t\t\t\tswitch(current_progstate->structtype)\n\t\t\t\t{\n\t\t\t\tcase PST_DEFAULT:\n\t\t\t\tcase PST_KKQWSV:\n\t\t\t\t\tif (!(d16 = ED_FindGlobal16(progfuncs, qcc_token)))\n\t\t\t\t\t{\n\t\t\t\t\t\texterns->Printf(\"global value %s not found\\n\", qcc_token);\n\t\t\t\t\t\tfile = QCC_COM_Parse(file);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tfile = QCC_COM_Parse(file);\n\t\t\t\t\t\tED_ParseEpair(progfuncs, (char*)pr_globals - progfuncs->funcs.stringtable, d16->ofs, d16->type, qcc_token);\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase PST_QTEST:\n\t\t\t\tcase PST_FTE32:\n\t\t\t\tcase PST_UHEXEN2:\n\t\t\t\t\tif (!(d32 = ED_FindGlobal32(progfuncs, qcc_token)))\n\t\t\t\t\t{\n\t\t\t\t\t\texterns->Printf(\"global value %s not found\\n\", qcc_token);\n\t\t\t\t\t\tfile = QCC_COM_Parse(file);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tfile = QCC_COM_Parse(file);\n\t\t\t\t\t\tED_ParseEpair(progfuncs, (char*)pr_globals - progfuncs->funcs.stringtable, d32->ofs, d32->type, qcc_token);\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\texterns->Sys_Error(\"Bad struct type in LoadEnts\");\n\t\t\t\t}\n\t\t\t}\n\t\t\tPR_SwitchProgs(progfuncs, 0);\n\n//\t\t\tfile = QCC_COM_Parse(file);\n//\t\t\tif (com_token[0] != '}')\n//\t\t\t\tSys_Error(\"Progs loading found %s, not '}'\", qcc_token);\n\t\t}\n\t\telse if (!strcmp(qcc_token, \"general\"))\n\t\t{\n\t\t\tQC_StartShares(progfuncs);\n//\t\t\tQC_InitShares();\t//forget stuff\n//\t\t\tpr_edict_size = 0;\n\t\t\tprinst.max_fields_size=0;\n\n\t\t\tfile = QCC_COM_Parse(file);\n\t\t\tif (qcc_token[0] != '{')\n\t\t\t\texterns->Sys_Error(\"Progs loading found %s, not '{'\", qcc_token);\n\n\t\t\twhile(1)\n\t\t\t{\n\t\t\t\tfile = QCC_COM_Parse(file);\t//read the key\n\t\t\t\tif (!file)\n\t\t\t\t\texterns->Sys_Error(\"EOF in general block\");\n\n\t\t\t\tif (!strcmp(\"maxprogs\", qcc_token))\t//check key get and save values\n\t\t\t\t\t{file = QCC_COM_Parse(file); prinst.maxprogs = atoi(qcc_token);}\n//\t\t\t\telse if (!strcmp(\"maxentities\", com_token))\n//\t\t\t\t\t{file = QCC_COM_Parse(file); maxedicts = atoi(qcc_token);}\n//\t\t\t\telse if (!strcmp(\"mem\", com_token))\n//\t\t\t\t\t{file = QCC_COM_Parse(file); memsize = atoi(qcc_token);}\n//\t\t\t\telse if (!strcmp(\"crc\", com_token))\n//\t\t\t\t\t{file = QCC_COM_Parse(file); crc = atoi(qcc_token);}\n\t\t\t\telse if (!strcmp(\"numentities\", qcc_token))\n\t\t\t\t\t{file = QCC_COM_Parse(file); numents = atoi(qcc_token);}\n\t\t\t\telse if (qcc_token[0] == '}')\t//end of block\n\t\t\t\t\tbreak;\n\t\t\t\telse\n\t\t\t\t\texterns->Sys_Error(\"Bad key \\\"%s\\\" in general block\", qcc_token);\n\t\t\t}\n\n\t\t\tif (oldglobals)\n\t\t\t\tfree(oldglobals);\n\t\t\toldglobals = NULL;\n\t\t\tif (pr_progstate[0].globals_bytes)\n\t\t\t{\n\t\t\t\toldglobals = malloc(pr_progstate[0].globals_bytes);\n\t\t\t\tif (oldglobals)\n\t\t\t\t{\n\t\t\t\t\toldglobalssize = pr_progstate[0].globals_bytes;\n\t\t\t\t\tmemcpy(oldglobals, pr_progstate[0].globals, oldglobalssize);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\texterns->Printf(\"Unable to alloc %i bytes\\n\", pr_progstate[0].globals_bytes);\n\t\t\t}\n\n\t\t\tPRAddressableFlush(progfuncs, 0);\n\t\t\tresethunk=true;\n\n\t\t\tpr_progstate = PRHunkAlloc(progfuncs, sizeof(progstate_t) * prinst.maxprogs, \"progstatetable\");\n\t\t\tprinst.pr_typecurrent=0;\n\n\t\t\tsv_num_edicts = 1;\t//set up a safty buffer so things won't go horribly wrong too often\n\t\t\tsv_edicts=(struct edict_s *)&tempedict;\n\t\t\tprinst.edicttable = (struct edictrun_s**)(progfuncs->funcs.edicttable = &sv_edicts);\n\t\t\tprogfuncs->funcs.edicttable_length = numents;\n\n\t\t\tsv_num_edicts = numents;\t//should be fine\n\n//\t\t\tPR_Configure(crc, NULL, memsize, maxedicts, maxprogs);\n\t\t}\n\t\telse if (!strcmp(qcc_token, \"{\"))\n\t\t{\n\t\t\tif (isloadgame)\n\t\t\t{\n\t\t\t\tif (numents == -1)\t//globals\n\t\t\t\t{\n\t\t\t\t\twhile (1)\n\t\t\t\t\t{\n\t\t\t\t\t\tfile = QCC_COM_Parse(file);\n\t\t\t\t\t\tif (qcc_token[0] == '}')\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\telse if (!qcc_token[0] || !file)\n\t\t\t\t\t\t\texterns->Sys_Error(\"EOF when parsing global values\");\n\n\t\t\t\t\t\tswitch(current_progstate->structtype)\n\t\t\t\t\t\t{\n\t\t\t\t\t\tcase PST_DEFAULT:\n\t\t\t\t\t\tcase PST_KKQWSV:\n\t\t\t\t\t\t\tif (!(d16 = ED_FindGlobal16(progfuncs, qcc_token)))\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\texterns->Printf(\"global value %s not found\\n\", qcc_token);\n\t\t\t\t\t\t\t\tfile = QCC_COM_Parse(file);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tfile = QCC_COM_Parse(file);\n\t\t\t\t\t\t\t\tED_ParseEpair(progfuncs, (char*)pr_globals - progfuncs->funcs.stringtable, d16->ofs, d16->type, qcc_token);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase PST_QTEST:\n\t\t\t\t\t\tcase PST_FTE32:\n\t\t\t\t\t\tcase PST_UHEXEN2:\n\t\t\t\t\t\t\tif (!(d32 = ED_FindGlobal32(progfuncs, qcc_token)))\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\texterns->Printf(\"global value %s not found\\n\", qcc_token);\n\t\t\t\t\t\t\t\tfile = QCC_COM_Parse(file);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tfile = QCC_COM_Parse(file);\n\t\t\t\t\t\t\t\tED_ParseEpair(progfuncs, (char*)pr_globals - progfuncs->funcs.stringtable, d32->ofs, d32->type, qcc_token);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\texterns->Sys_Error(\"Bad struct type in LoadEnts\");\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\ted = (edictrun_t *)ED_AllocIndex(&progfuncs->funcs, numents, false, 0);\n\n\t\t\t\t\tif (externs->entspawn)\n\t\t\t\t\t\texterns->entspawn((struct edict_s *) ed, true);\n\n\t\t\t\t\ted->ereftype = ER_ENTITY;\n\t\t\t\t\tfile = ED_ParseEdict (progfuncs, file, ed, &maphacks);\n\t\t\t\t}\n\t\t\t\tsv_num_edicts = ++numents;\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (entsize == 0 && resethunk)\t//edicts have not yet been initialized, and this is a compleate load (memsize has been set)\n\t\t\t{\n\t\t\t\tentsize = PR_InitEnts(&progfuncs->funcs, prinst.maxedicts);\n//\t\t\t\tsv_num_edicts = numents;\n\n\t\t\t\tfor (num = 0; num < numents; num++)\n\t\t\t\t{\n\t\t\t\t\ted = (edictrun_t *)EDICT_NUM(progfuncs, num);\n\n\t\t\t\t\tif (!ed)\n\t\t\t\t\t{\n\t\t\t\t\t\ted = (edictrun_t *)ED_AllocIndex(&progfuncs->funcs, num, false, 0);\n\t\t\t\t\t\ted->ereftype = ER_FREE;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (!ed)\t//first entity\n\t\t\t\ted = (edictrun_t *)EDICT_NUM(progfuncs, 0);\n\t\t\telse\n\t\t\t\ted = (edictrun_t *)ED_Alloc(&progfuncs->funcs, false, 0);\n\t\t\ted->ereftype = ER_ENTITY;\n\t\t\tif (externs->entspawn)\n\t\t\t\texterns->entspawn((struct edict_s *) ed, true);\n\t\t\tfile = ED_ParseEdict(progfuncs, file, ed, &maphacks);\n\n\t\t\tif (entspawned)\n\t\t\t\tentspawned(ppf, (struct edict_s *)ed, ctx, datastart, file);\n\t\t}\n\t\telse if (extendedterm && extendedterm(ppf, ctx, &datastart))\n\t\t\tfile = datastart;\n\t\telse\n\t\t\texterns->Sys_Error(\"Bad entity lump: '%s' not recognised (last ent was %i)\", qcc_token, ed?ed->entnum:0);\n\t}\n\tif (resethunk)\n\t{\n\t\tif (externs->loadcompleate)\n\t\t\texterns->loadcompleate(entsize);\n\n\t\tsv_num_edicts = numents;\n\t}\n\n\tif (oldglobals)\n\t\tfree(oldglobals);\n\toldglobals = NULL;\n\n\tif (resethunk)\n\t{\n\t\treturn entsize;\n\t}\n\telse\n\t\treturn prinst.max_fields_size;\n}\n\n//FIXME: maxsize is ignored.\nchar *PDECL PR_SaveEnt (pubprogfuncs_t *ppf, char *buf, size_t *size, size_t maxsize, struct edict_s *ed)\n{\n#define AddS(str) PR_Cat(buf, str, size, maxsize)\n\tprogfuncs_t *progfuncs = (progfuncs_t*)ppf;\n\tfdef_t\t*d;\n\tint\t\t*v;\n\tunsigned int\t\ti;unsigned int j;\n\tconst char\t*name, *mname;\n\tconst char\t*classname = NULL;\n\tint\t\tclassnamelen = 0;\n\tint\t\ttype;\n\n//\tif (ed->free)\n//\t\tcontinue;\n\n\tAddS (\"{\\n\");\n\n\n\tfor (i=0 ; i<prinst.numfields ; i++)\n\t{\n\t\tint len;\n\n\t\td = &prinst.field[i];\n\t\tname = d->name;\n\t\tlen = strlen(name); // should we skip vars with no name?\n\t\tif (len > 2 && name[len-2] == '_' && (name[len-1] == 'x' || name[len-1] == 'y' || name[len-1] == 'z'))\n\t\t\tcontinue;\t// skip _x, _y, _z vars\n\n\t\tv = (int*)((edictrun_t*)ed)->fields + d->ofs;\n\n\t// if the value is still all 0, skip the field\n\t\ttype = d->type & ~DEF_SAVEGLOBAL;\n\t\tfor (j=0 ; j<type_size[type] ; j++)\n\t\t\tif (v[j])\n\t\t\t\tbreak;\n\t\tif (j == type_size[type])\n\t\t\tcontinue;\n\n\t\tif (strstr(name, \"::\"))\n\t\t{\n\t\t\tif (*name != ':')\n\t\t\t\tcontinue;\t//don't directly generate anything from class::foo\n\n\t\t\tif (!classname)\n\t\t\t{\n\t\t\t\tfdef_t *cnfd;\n\t\t\t\tcnfd = ED_FindField(progfuncs, \"classname\");\n\t\t\t\tif (cnfd)\n\t\t\t\t{\n\t\t\t\t\tstring_t *v;\n\t\t\t\t\tv = (string_t *)((char *)edvars(ed) + cnfd->ofs*4);\n\t\t\t\t\tclassname = PR_StringToNative(&progfuncs->funcs, *v);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tclassname = \"\";\n\t\t\t\tclassnamelen = strlen(classname);\n\t\t\t}\n\t\t\tfor (j = i+1; j < prinst.numfields; j++)\n\t\t\t{\n\t\t\t\tif (prinst.field[j].ofs == d->ofs)\n\t\t\t\t{\n\t\t\t\t\tmname = prinst.field[j].name;\n\t\t\t\t\tif (!strncmp(mname, classname, classnamelen) && mname[classnamelen] == ':')\n\t\t\t\t\t{\n\t\t\t\t\t\t//okay, we have a match...\n\t\t\t\t\t\tname = prinst.field[j].name;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t//add it to the file\n\t\tAddS(\"\\\"\"); AddS(name); AddS(\"\\\" \\\"\"); AddS(PR_UglyValueString(&progfuncs->funcs, d->type, (eval_t *)v)); AddS(\"\\\"\\n\");\n\t}\n\n\tAddS (\"}\\n\");\n\n\treturn buf;\n}\nstruct edict_s *PDECL PR_RestoreEnt (pubprogfuncs_t *ppf, const char *buf, size_t *size, struct edict_s *ed)\n{\n\tprogfuncs_t *progfuncs = (progfuncs_t*)ppf;\n\tedictrun_t *ent;\n\tconst char *start = buf;\n\tpbool maphacks = false;\t//don't really care.\n\n\tbuf = QCC_COM_Parse(buf);\t//read the key\n\tif (!buf || !*qcc_token)\n\t\treturn NULL;\n\n\tif (strcmp(qcc_token, \"{\"))\n\t{\n\t\texterns->Printf(\"PR_RestoreEnt: with no opening brace\");\n\t\treturn NULL;\n\t}\n\n\tif (!ed)\n\t\tent = (edictrun_t *)ED_Alloc(&progfuncs->funcs, false, 0);\n\telse\n\t\tent = (edictrun_t *)ed;\n\n\tif (ent->ereftype == ER_FREE && externs->entspawn)\n\t{\n\t\tmemset (ent->fields, 0, ent->fieldsize);\n\t\tent->ereftype = ER_ENTITY;\n\t\texterns->entspawn((struct edict_s *) ent, false);\n\t}\n\tif (ent->ereftype != ER_ENTITY)\n\t\treturn NULL;\t//not allowed to spawn it into that ent.\n\n\tbuf = ED_ParseEdict(progfuncs, buf, ent, &maphacks);\n\n\t*size = buf - start;\n\n\treturn (struct edict_s *)ent;\n}\n\n#define Host_Error Sys_Error\n\n//return true if pr_progs needs recompiling (source files have changed)\npbool PR_TestRecompile(progfuncs_t *progfuncs)\n{\n\tint newsize;\n\tint num, found=0, lost=0, changed=0;\n\tincludeddatafile_t *s;\n\tif (!pr_progs->ofsfiles)\n\t\treturn false;\n\n\tnum = *(int*)((char *)pr_progs + pr_progs->ofsfiles);\n\ts = (includeddatafile_t *)((char *)pr_progs + pr_progs->ofsfiles+4);\n\twhile(num>0)\n\t{\n\t\tnewsize = externs->FileSize(s->filename);\n\t\tif (newsize == -1)\t//ignore now missing files. - the referencer must have changed...\n\t\t\tlost++;\n\t\telse if (s->size != newsize)\t//file\n\t\t\tchanged++;\n\t\telse\n\t\t\tfound++;\n\n\t\ts++;\n\t\tnum--;\n\t}\n\tif (lost > found+changed)\n\t\treturn false;\n\tif (changed)\n\t\treturn true;\n\treturn false;\n}\n/*\n#ifdef _DEBUG\n//this is for debugging.\n//I'm using this to detect incorrect string types while converting 32bit string pointers with bias to bound indexes.\nvoid PR_TestForWierdness(progfuncs_t *progfuncs)\n{\n\tunsigned int i;\n\tint e;\n\tedictrun_t *ed;\n\tfor (i = 0; i < pr_progs->numglobaldefs; i++)\n\t{\n\t\tif ((pr_globaldefs16[i].type&~(DEF_SHARED|DEF_SAVEGLOBAL)) == ev_string)\n\t\t{\n\t\t\tif (G_INT(pr_globaldefs16[i].ofs) < 0 || G_INT(pr_globaldefs16[i].ofs) >= addressableused)\n\t\t\t\texterns->Printf(\"String type irregularity on \\\"%s\\\" \\\"%s\\\"\\n\", pr_globaldefs16[i].s_name+progfuncs->funcs.stringtable, G_INT(pr_globaldefs16[i].ofs)+progfuncs->funcs.stringtable);\n\t\t}\n\t}\n\n\tfor (i = 0; i < numfields; i++)\n\t{\n\t\tif ((field[i].type&~(DEF_SHARED|DEF_SAVEGLOBAL)) == ev_string)\n\t\t{\n\t\t\tfor (e = 0; e < sv_num_edicts; e++)\n\t\t\t{\n\t\t\t\ted = (edictrun_t*)EDICT_NUM(progfuncs, e);\n\t\t\t\tif (ed->isfree)\n\t\t\t\t\tcontinue;\n\t\t\t\tif (((int *)ed->fields)[field[i].ofs] < 0 || ((int *)ed->fields)[field[i].ofs] >= addressableused)\n\t\t\t\t\texterns->Printf(\"String type irregularity \\\"%s\\\" \\\"%s\\\"\\n\", field[i].name, ((int *)ed->fields)[field[i].ofs]+progfuncs->funcs.stringtable);\n\t\t\t}\n\t\t}\n\t}\n}\n#endif\n*/\n\nvoid PR_CleanUpStatements16(progfuncs_t *progfuncs, dstatement16_t *st, pbool hexencalling)\n{\n\tunsigned int numst = pr_progs->numstatements;\n\tunsigned int numglob = pr_progs->numglobals+3;\t//+3 because I'm too lazy to deal with vectors\n\tunsigned int i;\n\tfor (i=0 ; i<numst ; i++)\n\t{\n\t\tif (st[i].op >= OP_CALL1 && st[i].op <= OP_CALL8 && hexencalling)\n\t\t\tst[i].op += OP_CALL1H - OP_CALL1;\n\t\tif (st[i].op >= OP_RAND0 && st[i].op <= OP_RANDV2 && hexencalling)\n\t\t\tif (!st[i].c)\n\t\t\t\tst[i].c = OFS_RETURN;\n\n\t\t//sanitise inputs\n\t\tif (st[i].a >= numglob)\n\t\t\tif (st[i].op != OP_GOTO)\n\t\t\t\tst[i].op = ~0;\n\t\tif (st[i].b >= numglob)\n\t\t\tif (st[i].op != OP_IFNOT_I && st[i].op != OP_IF_I &&\n\t\t\t\tst[i].op != OP_IFNOT_F && st[i].op != OP_IF_F &&\n\t\t\t\tst[i].op != OP_IFNOT_S && st[i].op != OP_IF_S &&\n\t\t\t\tst[i].op != OP_BOUNDCHECK && st[i].op != OP_CASE)\n\t\t\t\tst[i].op = ~0;\n\t\tif (st[i].c >= numglob)\n\t\t\tif (st[i].op != OP_BOUNDCHECK && st[i].op != OP_CASERANGE)\n\t\t\t\tst[i].op = ~0;\n\t}\n}\nvoid PR_CleanUpStatements32(progfuncs_t *progfuncs, dstatement32_t *st, pbool hexencalling)\n{\n\tunsigned int numst = pr_progs->numstatements;\n\tunsigned int numglob = pr_progs->numglobals+3;\t//+3 because I'm too lazy to deal with vectors\n\tunsigned int i;\n\tfor (i=0 ; i<numst ; i++)\n\t{\n\t\tif (st[i].op >= OP_CALL1 && st[i].op <= OP_CALL8 && hexencalling)\n\t\t\tst[i].op += OP_CALL1H - OP_CALL1;\n\t\tif (st[i].op >= OP_RAND0 && st[i].op <= OP_RANDV2 && hexencalling)\n\t\t\tif (!st[i].c)\n\t\t\t\tst[i].c = OFS_RETURN;\n\n\t\t//sanitise inputs\n\t\tif (st[i].a >= numglob)\n\t\t\tif (st[i].op != OP_GOTO)\n\t\t\t\tst[i].op = ~0;\n\t\tif (st[i].b >= numglob)\n\t\t\tif (st[i].op != OP_IFNOT_I && st[i].op != OP_IF_I &&\n\t\t\t\tst[i].op != OP_IFNOT_F && st[i].op != OP_IF_F &&\n\t\t\t\tst[i].op != OP_IFNOT_S && st[i].op != OP_IF_S &&\n\t\t\t\tst[i].op != OP_BOUNDCHECK && st[i].op != OP_CASE)\n\t\t\t\tst[i].op = ~0;\n\t\tif (st[i].c >= numglob)\n\t\t\tif (st[i].op != OP_BOUNDCHECK && st[i].op != OP_CASERANGE)\n\t\t\t\tst[i].op = ~0;\n\t}\n}\n\nchar *decode(int complen, int len, int method, char *info, char *buffer);\nunsigned char *PDECL PR_GetHeapBuffer (void *ctx, size_t bufsize)\n{\n\treturn PRHunkAlloc(ctx, bufsize+1, \"proginfo\");\n}\n/*\n===============\nPR_LoadProgs\n===============\n*/\npbool PR_ReallyLoadProgs (progfuncs_t *progfuncs, const char *filename, progstate_t *progstate, pbool complain)\n{\n\tunsigned int\t\ti, j, type;\n//\tfloat\tfl;\n//\tint len;\n//\tint num;\n//\tdfunction_t *f, *f2;\n\tddef16_t *d16;\n\tddef32_t *d32;\n\tint *d2;\n\teval_t *eval;\n\tchar *s;\n\tint progstype;\n\tint trysleft = 2;\n//\tbool qfhack = false;\n\tpbool isfriked = false;\t//all imediate values were stripped, which causes problems with strings.\n\tpbool hexencalling = false;\t//hexen style calling convention. The opcodes themselves are used as part of passing the arguments;\n\tddef16_t *gd16, *fld16;\n\tfloat *glob;\n\tdfunction_t *fnc;\n\tmfunction_t *fnc2;\n\tdstatement16_t *st16;\n\tsize_t fsz;\n\tint len;\n\n\tint hmark=0xffffffff;\n\n\tint reorg = prinst.reorganisefields || prinst.numfields;\n\n\tint stringadjust;\n\tint pointeradjust;\n\n\tint *basictypetable;\n\n\tcurrent_progstate = progstate;\n\n\tstrcpy(current_progstate->filename, filename);\n\n\n// flush the non-C variable lookup cache\n//\tfor (i=0 ; i<GEFV_CACHESIZE ; i++)\n//\t\tgefvCache[i].field[0] = 0;\n\n\tmemset(&prinst.spawnflagscache, 0, sizeof(evalc_t));\n\n\tif (externs->autocompile == PR_COMPILEALWAYS)\t//always compile before loading\n\t{\n\t\texterns->Printf(\"Forcing compile of progs %s\\n\", filename);\n\t\tif (!CompileFile(progfuncs, filename))\n\t\t\treturn false;\n\t}\n\n//\tCRC_Init (&pr_crc);\n\nretry:\n\thmark = PRHunkMark(progfuncs);\n\tpr_progs = externs->ReadFile(filename, PR_GetHeapBuffer, progfuncs, &fsz, false);\n\tif (!pr_progs)\n\t{\n\t\tif (externs->autocompile == PR_COMPILENEXIST || externs->autocompile == PR_COMPILECHANGED)\t//compile if file is not found (if 2, we have already tried, so don't bother)\n\t\t{\n\t\t\tif (hmark==0xffffffff)\t//first try\n\t\t\t{\n\t\t\t\texterns->Printf(\"couldn't open progs %s. Attempting to compile.\\n\", filename);\n\t\t\t\tCompileFile(progfuncs, filename);\n\t\t\t}\n\t\t\tpr_progs = externs->ReadFile(filename, PR_GetHeapBuffer, progfuncs, &fsz, false);\n\t\t\tif (!pr_progs)\n\t\t\t{\n\t\t\t\texterns->Printf(\"Couldn't find or compile file %s\\n\", filename);\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\telse if (externs->autocompile == PR_COMPILEIGNORE)\n\t\t\treturn false;\n\t\telse\n\t\t{\n\t\t\texterns->Printf(\"Couldn't find file %s\\n\", filename);\n\t\t\treturn false;\n\t\t}\n\t}\n\n//\tfor (i=0 ; i<len ; i++)\n//\t\tCRC_ProcessByte (&pr_crc, ((byte *)pr_progs)[i]);\n\n// byte swap the header\n#ifndef NOENDIAN\n\tfor (i=0 ; i<standard_dprograms_t_size/sizeof(int); i++)\n\t\t((int *)pr_progs)[i] = PRLittleLong ( ((int *)pr_progs)[i] );\n#endif\n\n\tif (pr_progs->version == PROG_VERSION)\n\t{\n//\t\texterns->Printf(\"Opening standard progs file \\\"%s\\\"\\n\", filename);\n\t\tcurrent_progstate->structtype = PST_DEFAULT;\n\t}\n\telse if (pr_progs->version == PROG_QTESTVERSION)\n\t{\n\t\tcurrent_progstate->structtype = PST_QTEST;\n\t}\n\telse if (pr_progs->version == PROG_EXTENDEDVERSION)\n\t{\n#ifndef NOENDIAN\n\t\tfor (i = standard_dprograms_t_size/sizeof(int); i < sizeof(dprograms_t)/sizeof(int); i++)\n\t\t\t((int *)pr_progs)[i] = PRLittleLong ( ((int *)pr_progs)[i] );\n#endif\n\t\tif (pr_progs->secondaryversion == PROG_SECONDARYVERSION16)\n\t\t{\n//\t\t\texterns->Printf(\"Opening 16bit fte progs file \\\"%s\\\"\\n\", filename);\n\t\t\tcurrent_progstate->structtype = PST_DEFAULT;\n\t\t}\n\t\telse if (pr_progs->secondaryversion == PROG_SECONDARYVERSION32)\n\t\t{\n//\t\t\texterns->Printf(\"Opening 32bit fte progs file \\\"%s\\\"\\n\", filename);\n\t\t\tcurrent_progstate->structtype = PST_FTE32;\n\t\t}\n\t\telse if (pr_progs->secondaryversion == PROG_SECONDARYUHEXEN2)\n\t\t{\n//\t\t\texterns->Printf(\"Opening uhexen2 progs file \\\"%s\\\"\\n\", filename);\n\t\t\tcurrent_progstate->structtype = PST_UHEXEN2;\n\t\t\tpr_progs->version = PROG_VERSION;\t//not fte.\n\t\t}\n\t\telse if (pr_progs->secondaryversion == PROG_SECONDARYKKQWSV)\n\t\t{\n//\t\t\texterns->Printf(\"Opening KK7 progs file \\\"%s\\\"\\n\", filename);\n\t\t\tcurrent_progstate->structtype = PST_KKQWSV;\t//KK progs. Yuck. Disabling saving would be a VERY good idea.\n\t\t\tpr_progs->version = PROG_VERSION;\t//not fte.\n\t\t}\n\t\telse\n\t\t{\n\t\t\texterns->Printf (\"%s has no v7 verification code, assuming kkqwsv format\\n\", filename);\n//\t\t\texterns->Printf(\"Opening KK7 progs file \\\"%s\\\"\\n\", filename);\n\t\t\tcurrent_progstate->structtype = PST_KKQWSV;\t//KK progs. Yuck. Disabling saving would be a VERY good idea.\n\t\t\tpr_progs->version = PROG_VERSION;\t//not fte.\n\t\t}\n/*\t\telse\n\t\t{\n\t\t\texterns->Printf (\"Progs extensions are not compatible\\nTry recompiling with the FTE compiler\\n\");\n\t\t\tHunkFree(hmark);\n\t\t\tpr_progs=NULL;\n\t\t\treturn false;\n\t\t}\n*/\t}\n\telse\n\t{\n\t\texterns->Printf (\"%s has wrong version number (%i should be %i)\\n\", filename, pr_progs->version, PROG_VERSION);\n\t\tPRHunkFree(progfuncs, hmark);\n\t\tpr_progs=NULL;\n\t\treturn false;\n\t}\n\n//progs contains enough info for use to recompile it.\n\tif (trysleft && (externs->autocompile == PR_COMPILECHANGED  || externs->autocompile == PR_COMPILEEXISTANDCHANGED) && pr_progs->version == PROG_EXTENDEDVERSION)\n\t{\n\t\tif (PR_TestRecompile(progfuncs))\n\t\t{\n\t\t\texterns->Printf(\"Source file has changed\\nRecompiling.\\n\");\n\t\t\tif (CompileFile(progfuncs, filename))\n\t\t\t{\n\t\t\t\tPRHunkFree(progfuncs, hmark);\n\t\t\t\tpr_progs=NULL;\n\n\t\t\t\ttrysleft--;\n\t\t\t\tgoto retry;\n\t\t\t}\n\t\t}\n\t}\n\tif (!trysleft)\t//the progs exists, let's just be happy about it.\n\t\texterns->Printf(\"Progs is out of date and uncompilable\\n\");\n\n\tif (externs->CheckHeaderCrc && !externs->CheckHeaderCrc(&progfuncs->funcs, prinst.pr_typecurrent, pr_progs->crc, filename))\n\t{\n//\t\texterns->Printf (\"%s system vars have been modified, progdefs.h is out of date\\n\", filename);\n\t\tPRHunkFree(progfuncs, hmark);\n\t\tpr_progs=NULL;\n\t\treturn false;\n\t}\n\n\tif (pr_progs->version == PROG_EXTENDEDVERSION && pr_progs->blockscompressed && !QC_decodeMethodSupported(2))\n\t{\n\t\texterns->Printf (\"%s uses compression\\n\", filename);\n\t\tPRHunkFree(progfuncs, hmark);\n\t\tpr_progs=NULL;\n\t\treturn false;\n\t}\n\n\tfnc = (dfunction_t *)((pbyte *)pr_progs + pr_progs->ofs_functions);\n\tpr_strings = ((char *)pr_progs + pr_progs->ofs_strings);\n\tcurrent_progstate->globaldefs = *(void**)&gd16 = (void *)((pbyte *)pr_progs + pr_progs->ofs_globaldefs);\n\tcurrent_progstate->fielddefs = *(void**)&fld16 = (void *)((pbyte *)pr_progs + pr_progs->ofs_fielddefs);\n\tcurrent_progstate->statements = (void *)((pbyte *)pr_progs + pr_progs->ofs_statements);\n\n\tglob = pr_globals = (void *)((pbyte *)pr_progs + pr_progs->ofs_globals);\n\tcurrent_progstate->globals_bytes = pr_progs->numglobals*sizeof(*pr_globals);\n\n\tpr_linenums=NULL;\n\tpr_types=NULL;\n\tif (pr_progs->version == PROG_EXTENDEDVERSION)\n\t{\n\t\tif (pr_progs->ofslinenums)\n\t\t\tpr_linenums = (int *)((pbyte *)pr_progs + pr_progs->ofslinenums);\n\t\tif (pr_progs->ofs_types)\n\t\t\tpr_types = (typeinfo_t *)((pbyte *)pr_progs + pr_progs->ofs_types);\n\n\t\t//start decompressing stuff...\n\t\tif (pr_progs->blockscompressed & 1)\t//statements\n\t\t{\n\t\t\tswitch(current_progstate->structtype)\n\t\t\t{\n\t\t\tcase PST_DEFAULT:\n\t\t\t\tlen=sizeof(dstatement16_t)*pr_progs->numstatements;\n\t\t\t\tbreak;\n\t\t\tcase PST_FTE32:\n\t\t\tcase PST_UHEXEN2:\n\t\t\t\tlen=sizeof(dstatement32_t)*pr_progs->numstatements;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\texterns->Sys_Error(\"Bad struct type\");\n\t\t\t\tlen = 0;\n\t\t\t}\n\t\t\ts = PRHunkAlloc(progfuncs, len, \"dstatements\");\n\t\t\tQC_decode(progfuncs, PRLittleLong(*(int *)pr_statements16), len, 2, (char *)(((int *)pr_statements16)+1), s);\n\n\t\t\tcurrent_progstate->statements = (dstatement16_t *)s;\n\t\t}\n\t\tif (pr_progs->blockscompressed & 2)\t//global defs\n\t\t{\n\t\t\tswitch(current_progstate->structtype)\n\t\t\t{\n\t\t\tcase PST_DEFAULT:\n\t\t\t\tlen=sizeof(ddef16_t)*pr_progs->numglobaldefs;\n\t\t\t\tbreak;\n\t\t\tcase PST_FTE32:\n\t\t\tcase PST_UHEXEN2:\n\t\t\t\tlen=sizeof(ddef32_t)*pr_progs->numglobaldefs;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\texterns->Sys_Error(\"Bad struct type\");\n\t\t\t\tlen = 0;\n\t\t\t}\n\t\t\ts = PRHunkAlloc(progfuncs, len, \"dglobaldefs\");\n\t\t\tQC_decode(progfuncs, PRLittleLong(*(int *)pr_globaldefs16), len, 2, (char *)(((int *)pr_globaldefs16)+1), s);\n\n\t\t\tgd16 = *(ddef16_t**)&current_progstate->globaldefs = (ddef16_t *)s;\n\t\t}\n\t\tif (pr_progs->blockscompressed & 4)\t//fields\n\t\t{\n\t\t\tswitch(current_progstate->structtype)\n\t\t\t{\n\t\t\tcase PST_DEFAULT:\n\t\t\t\tlen=sizeof(ddef16_t)*pr_progs->numglobaldefs;\n\t\t\t\tbreak;\n\t\t\tcase PST_FTE32:\n\t\t\tcase PST_UHEXEN2:\n\t\t\t\tlen=sizeof(ddef32_t)*pr_progs->numglobaldefs;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\texterns->Sys_Error(\"Bad struct type\");\n\t\t\t\tlen = 0;\n\t\t\t}\n\t\t\ts = PRHunkAlloc(progfuncs, len, \"progfieldtable\");\n\t\t\tQC_decode(progfuncs, PRLittleLong(*(int *)pr_fielddefs16), len, 2, (char *)(((int *)pr_fielddefs16)+1), s);\n\n\t\t\t*(ddef16_t**)&current_progstate->fielddefs = (ddef16_t *)s;\n\t\t}\n\t\tif (pr_progs->blockscompressed & 8)\t//functions\n\t\t{\n\t\t\tlen=sizeof(dfunction_t)*pr_progs->numfunctions;\n\t\t\ts = PRHunkAlloc(progfuncs, len, \"dfunctiontable\");\n\t\t\tQC_decode(progfuncs, PRLittleLong(*(int *)fnc), len, 2, (char *)(((int *)fnc)+1), s);\n\n\t\t\tfnc = (dfunction_t *)s;\n\t\t}\n\t\tif (pr_progs->blockscompressed & 16)\t//string table\n\t\t{\n\t\t\tlen=sizeof(char)*pr_progs->numstrings;\n\t\t\ts = PRHunkAlloc(progfuncs, len, \"dstringtable\");\n\t\t\tQC_decode(progfuncs, PRLittleLong(*(int *)pr_strings), len, 2, (char *)(((int *)pr_strings)+1), s);\n\n\t\t\tpr_strings = (char *)s;\n\t\t}\n\t\tif (pr_progs->blockscompressed & 32)\t//globals\n\t\t{\n\t\t\tlen=sizeof(float)*pr_progs->numglobals;\n\t\t\ts = PRHunkAlloc(progfuncs, len + sizeof(float)*2, \"dglobaltable\");\n\t\t\tQC_decode(progfuncs, PRLittleLong(*(int *)pr_globals), len, 2, (char *)(((int *)pr_globals)+1), s);\n\n\t\t\tglob = pr_globals = (float *)s;\n\t\t}\n\t\tif (pr_linenums && pr_progs->blockscompressed & 64)\t//line numbers\n\t\t{\n\t\t\tlen=sizeof(int)*pr_progs->numstatements;\n\t\t\ts = PRHunkAlloc(progfuncs, len, \"dlinenumtable\");\n\t\t\tQC_decode(progfuncs, PRLittleLong(*(int *)pr_linenums), len, 2, (char *)(((int *)pr_linenums)+1), s);\n\n\t\t\tpr_linenums = (int *)s;\n\t\t}\n\t\tif (pr_types && pr_progs->blockscompressed & 128)\t//types\n\t\t{\n\t\t\tlen=sizeof(typeinfo_t)*pr_progs->numtypes;\n\t\t\ts = PRHunkAlloc(progfuncs, len, \"dtypes\");\n\t\t\tQC_decode(progfuncs, PRLittleLong(*(int *)pr_types), len, 2, (char *)(((int *)pr_types)+1), s);\n\n\t\t\tpr_types = (typeinfo_t *)s;\n\t\t}\n\t}\n\n\tlen=sizeof(char)*pr_progs->numstrings;\n\ts = PRAddressableExtend(progfuncs, pr_strings, len, 0);\n\tpr_strings = (char *)s;\n\n\tlen=sizeof(float)*pr_progs->numglobals;\n\ts = PRAddressableExtend(progfuncs, pr_globals, len, sizeof(float)*2);\n\tglob = pr_globals = (float *)s;\n\n\tif (progfuncs->funcs.stringtable)\n\t{\n\t\tstringadjust = pr_strings - progfuncs->funcs.stringtable;\n\t\tpointeradjust = (char*)glob - progfuncs->funcs.stringtable;\n\t}\n\telse\n\t{\n\t\tstringadjust = 0;\n\t\tpointeradjust = (char*)glob - pr_strings;\n\t}\n\n\tif (!pr_linenums)\n\t{\n\t\tunsigned int lnotype = *(unsigned int*)\"LNOF\";\n\t\tunsigned int version = 1;\n\t\tint ohm;\n\t\tunsigned int *file;\n\t\tchar lnoname[128];\n\t\tohm = PRHunkMark(progfuncs);\n\n\t\tstrcpy(lnoname, filename);\n\t\tStripExtension(lnoname);\n\t\tstrcat(lnoname, \".lno\");\n\t\tfile = externs->ReadFile(lnoname, PR_GetHeapBuffer, progfuncs, &fsz, false);\n\t\tif (file)\n\t\t{\n\t\t\tif (\tfile[0] != lnotype\n\t\t\t\t||\tfile[1] != version\n\t\t\t\t||\tfile[2] != pr_progs->numglobaldefs\n\t\t\t\t||\tfile[3] != pr_progs->numglobals\n\t\t\t\t||\tfile[4] != pr_progs->numfielddefs\n\t\t\t\t||\tfile[5] != pr_progs->numstatements\n\t\t\t\t)\n\t\t\t{\n\t\t\t\tPRHunkFree(progfuncs, ohm);\t//whoops: old progs or incompatible\n\t\t\t}\n\t\t\telse\n\t\t\t\tpr_linenums = file + 6;\n\t\t}\n\t}\n\n\tpr_cp_functions = NULL;\n//\tpr_strings = ((char *)pr_progs + pr_progs->ofs_strings);\n\tgd16 = *(ddef16_t**)&current_progstate->globaldefs = (ddef16_t *)((pbyte *)pr_progs + pr_progs->ofs_globaldefs);\n\tfld16 = (ddef16_t *)((pbyte *)pr_progs + pr_progs->ofs_fielddefs);\n//\tpr_statements16 = (dstatement16_t *)((qbyte *)pr_progs + pr_progs->ofs_statements);\n\tpr_globals = glob;\n\tst16 = pr_statements16;\n#undef pr_globals\n#undef pr_globaldefs16\n#undef pr_functions\n#undef pr_statements16\n#undef pr_fielddefs16\n\n\n\tcurrent_progstate->edict_size = pr_progs->entityfields * 4 + externs->edictsize;\n\n\tif (sizeof(mfunction_t) > sizeof(qtest_function_t))\n\t\texterns->Sys_Error(\"assumption no longer works\");\n\n// byte swap the lumps\n\tswitch(current_progstate->structtype)\n\t{\n\tcase PST_QTEST:\n\t\t// qtest needs a struct remap\n\t\tpr_cp_functions = (mfunction_t*)fnc;\n\t\tfnc2 = pr_cp_functions;\n\t\tfor (i=0 ; i<pr_progs->numfunctions; i++)\n\t\t{\n\t\t\t//qtest functions are bigger, so we can just do this in-place\n\t\t\tqtest_function_t qtfunc = ((qtest_function_t*)fnc)[i];\n\n\t\t\tfnc2[i].first_statement\t= PRLittleLong (qtfunc.first_statement);\n\t\t\tfnc2[i].parm_start\t= PRLittleLong (qtfunc.parm_start);\n\t\t\tfnc2[i].s_name\t= (string_t)PRLittleLong (qtfunc.s_name);\n\t\t\tfnc2[i].s_file\t= (string_t)PRLittleLong (qtfunc.s_file);\n\t\t\tfnc2[i].numparms\t= PRLittleLong (qtfunc.numparms);\n\t\t\tfnc2[i].locals\t= PRLittleLong (qtfunc.locals);\n\n\t\t\tfor (j=0; j<MAX_PARMS;j++)\n\t\t\t\tfnc2[i].parm_size[j] = PRLittleLong (qtfunc.parm_size[j]);\n\n\t\t\tfnc2[i].s_name += stringadjust;\n\t\t\tfnc2[i].s_file += stringadjust;\n\t\t}\n\t\tbreak;\n\tcase PST_KKQWSV:\n\tcase PST_DEFAULT:\n\tcase PST_FTE32:\n\tcase PST_UHEXEN2:\n\t\tpr_cp_functions = PRHunkAlloc(progfuncs, sizeof(*pr_cp_functions)*pr_progs->numfunctions, \"mfunctions\");\n\t\tfor (i=0,fnc2=pr_cp_functions; i<pr_progs->numfunctions; i++, fnc2++)\n\t\t{\n\t\t\tfnc2->first_statement\t= PRLittleLong (fnc[i].first_statement);\n\t\t\tfnc2->parm_start\t= PRLittleLong (fnc[i].parm_start);\n\t\t\tfnc2->s_name\t= (string_t)PRLittleLong ((long)fnc[i].s_name) + stringadjust;\n\t\t\tfnc2->s_file\t= (string_t)PRLittleLong ((long)fnc[i].s_file) + stringadjust;\n\t\t\tfnc2->numparms\t= PRLittleLong (fnc[i].numparms);\n\t\t\tfnc2->locals\t= PRLittleLong (fnc[i].locals);\n\n\t\t\tfor (j=0; j<MAX_PARMS;j++)\n\t\t\t\tfnc2->parm_size[j] = fnc[i].parm_size[j];\n\t\t}\n\t\tbreak;\n\tdefault:\n\t\texterns->Sys_Error(\"Bad struct type\");\n\t}\n\n\t//actual global values\n#ifndef NOENDIAN\n\tfor (i=0 ; i<pr_progs->numglobals ; i++)\n\t\t((int *)glob)[i] = PRLittleLong (((int *)glob)[i]);\n#endif\n\n\tif (pr_types)\n\t{\n\t\tfor (i=0 ; i<pr_progs->numtypes ; i++)\n\t\t{\n#ifndef NOENDIAN\n\t\t\tpr_types[i].type = PRLittleLong(current_progstate->types[i].type);\n\t\t\tpr_types[i].next = PRLittleLong(current_progstate->types[i].next);\n\t\t\tpr_types[i].aux_type = PRLittleLong(current_progstate->types[i].aux_type);\n\t\t\tpr_types[i].num_parms = PRLittleLong(current_progstate->types[i].num_parms);\n\t\t\tpr_types[i].ofs = PRLittleLong(current_progstate->types[i].ofs);\n\t\t\tpr_types[i].size = PRLittleLong(current_progstate->types[i].size);\n\t\t\tpr_types[i].name = PRLittleLong(current_progstate->types[i].name);\n#endif\n\t\t\tpr_types[i].name += stringadjust;\n\t\t}\n\t}\n\n\tQC_FlushProgsOffsets(progfuncs);\n\tswitch(current_progstate->structtype)\n\t{\n\tcase PST_KKQWSV:\n\tcase PST_DEFAULT:\n\t\t//byteswap the globals and fix name offsets\n\t\tfor (i=0 ; i<pr_progs->numglobaldefs ; i++)\n\t\t{\n#ifndef NOENDIAN\n\t\t\tgd16[i].type = PRLittleShort (gd16[i].type);\n\t\t\tgd16[i].ofs = PRLittleShort (gd16[i].ofs);\n\t\t\tgd16[i].s_name = (string_t)PRLittleLong ((long)gd16[i].s_name);\n#endif\n\t\t\tgd16[i].s_name += stringadjust;\n\t\t}\n\n\t\t//byteswap fields and fix name offets. Also register the fields (which will result in some offset adjustments in the globals segment).\n\t\tfor (i=0 ; i<pr_progs->numfielddefs ; i++)\n\t\t{\n#ifndef NOENDIAN\n\t\t\tfld16[i].type = PRLittleShort (fld16[i].type);\n\t\t\tfld16[i].ofs = PRLittleShort (fld16[i].ofs);\n\t\t\tfld16[i].s_name = (string_t)PRLittleLong ((long)fld16[i].s_name);\n#endif\n\t\t\tif (reorg)\n\t\t\t{\n\t\t\t\tif (pr_types)\n\t\t\t\t\ttype = pr_types[fld16[i].type & ~(DEF_SHARED|DEF_SAVEGLOBAL)].type;\n\t\t\t\telse\n\t\t\t\t\ttype = fld16[i].type & ~(DEF_SHARED|DEF_SAVEGLOBAL);\n\n\t\t\t\tif (progfuncs->funcs.fieldadjust && !prinst.pr_typecurrent)\t//we need to make sure all fields appear in their original place.\n\t\t\t\t\tQC_RegisterFieldVar(&progfuncs->funcs, type, fld16[i].s_name+pr_strings, 4*(fld16[i].ofs+progfuncs->funcs.fieldadjust), fld16[i].ofs);\n\t\t\t\telse if (type == ev_vector)\t//emit vector vars early, so their fields cannot be alocated before the vector itself. (useful against scramblers)\n\t\t\t\t{\n\t\t\t\t\tQC_RegisterFieldVar(&progfuncs->funcs, type, fld16[i].s_name+pr_strings, -1, fld16[i].ofs);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tfdef_t *nf;\n\t\t\t\tif (prinst.numfields+1>prinst.maxfields)\n\t\t\t\t{\n\t\t\t\t\ti = prinst.maxfields;\n\t\t\t\t\tprinst.maxfields += 32;\n\t\t\t\t\tnf = externs->memalloc(sizeof(fdef_t) * prinst.maxfields);\n\t\t\t\t\tmemcpy(nf, prinst.field, sizeof(fdef_t) * i);\n\t\t\t\t\texterns->memfree(prinst.field);\n\t\t\t\t\tprinst.field = nf;\n\t\t\t\t}\n\t\t\t\tnf = &prinst.field[prinst.numfields];\n\t\t\t\tnf->name = fld16[i].s_name+pr_strings;\n\t\t\t\tnf->type = fld16[i].type;\n\t\t\t\tnf->progsofs = fld16[i].ofs;\n\t\t\t\tnf->ofs = fld16[i].ofs;\n\n\t\t\t\tif (prinst.fields_size < (nf->ofs+type_size[nf->type])*sizeof(pvec_t))\n\t\t\t\t{\n\t\t\t\t\tprinst.fields_size = (nf->ofs+type_size[nf->type])*sizeof(pvec_t);\n\t\t\t\t\tprogfuncs->funcs.activefieldslots = nf->ofs+type_size[nf->type];\n\t\t\t\t}\n\n\t\t\t\tprinst.numfields++;\n\t\t\t}\n\t\t\tfld16[i].s_name += stringadjust;\n\t\t}\n\t\tif (reorg && !(progfuncs->funcs.fieldadjust && !prinst.pr_typecurrent))\n\t\tfor (i=0 ; i<pr_progs->numfielddefs ; i++)\n\t\t{\n\t\t\tif (pr_types)\n\t\t\t\ttype = pr_types[fld16[i].type & ~(DEF_SHARED|DEF_SAVEGLOBAL)].type;\n\t\t\telse\n\t\t\t\ttype = fld16[i].type & ~(DEF_SHARED|DEF_SAVEGLOBAL);\n\t\t\tif (type != ev_vector)\n\t\t\t\tQC_RegisterFieldVar(&progfuncs->funcs, type, fld16[i].s_name+pr_strings-stringadjust, -1, fld16[i].ofs);\n\t\t}\n\n\t\tbreak;\n\tcase PST_QTEST:\n\t\t// qtest needs a struct remap\n\t\tfor (i=0 ; i<pr_progs->numglobaldefs ; i++)\n\t\t{\n\t\t\tqtest_def_t qtdef = ((qtest_def_t *)pr_globaldefs32)[i];\n\n\t\t\tpr_globaldefs32[i].type = qtdef.type;\n\t\t\tpr_globaldefs32[i].s_name = qtdef.s_name;\n\t\t\tpr_globaldefs32[i].ofs = qtdef.ofs;\n\t\t}\n\t\tfor (i=0 ; i<pr_progs->numfielddefs ; i++)\n\t\t{\n\t\t\tqtest_def_t qtdef = ((qtest_def_t *)pr_fielddefs32)[i];\n\n\t\t\tpr_fielddefs32[i].type = qtdef.type;\n\t\t\tpr_fielddefs32[i].s_name = qtdef.s_name;\n\t\t\tpr_fielddefs32[i].ofs = qtdef.ofs;\n\t\t}\n\t\t// passthrough\n\tcase PST_FTE32:\n\t\tfor (i=0 ; i<pr_progs->numglobaldefs ; i++)\n\t\t{\n#ifndef NOENDIAN\n\t\t\tpr_globaldefs32[i].type = PRLittleLong (pr_globaldefs32[i].type);\n\t\t\tpr_globaldefs32[i].ofs = PRLittleLong (pr_globaldefs32[i].ofs);\n\t\t\tpr_globaldefs32[i].s_name = (string_t)PRLittleLong ((long)pr_globaldefs32[i].s_name);\n#endif\n\t\t\tpr_globaldefs32[i].s_name += stringadjust;\n\t\t}\n\n\t\tfor (i=0 ; i<pr_progs->numfielddefs ; i++)\n\t\t{\n\t#ifndef NOENDIAN\n\t\t\tpr_fielddefs32[i].type = PRLittleLong (pr_fielddefs32[i].type);\n\t\t\tpr_fielddefs32[i].ofs = PRLittleLong (pr_fielddefs32[i].ofs);\n\t\t\tpr_fielddefs32[i].s_name = (string_t)PRLittleLong ((long)pr_fielddefs32[i].s_name);\n\t#endif\n\n\t\t\tif (reorg)\n\t\t\t{\n\t\t\t\tif (pr_types)\n\t\t\t\t\ttype = pr_types[pr_fielddefs32[i].type & ~(DEF_SHARED|DEF_SAVEGLOBAL)].type;\n\t\t\t\telse\n\t\t\t\t\ttype = pr_fielddefs32[i].type & ~(DEF_SHARED|DEF_SAVEGLOBAL);\n\t\t\t\tif (progfuncs->funcs.fieldadjust && !prinst.pr_typecurrent)\t//we need to make sure all fields appear in their original place.\n\t\t\t\t\tQC_RegisterFieldVar(&progfuncs->funcs, type, pr_fielddefs32[i].s_name+pr_strings, 4*(pr_fielddefs32[i].ofs+progfuncs->funcs.fieldadjust), -1);\n\t\t\t\telse if (type == ev_vector)\n\t\t\t\t\tQC_RegisterFieldVar(&progfuncs->funcs, type, pr_fielddefs32[i].s_name+pr_strings, -1, pr_fielddefs32[i].ofs);\n\t\t\t}\n\t\t\tpr_fielddefs32[i].s_name += stringadjust;\n\t\t}\n\t\tif (reorg && !(progfuncs->funcs.fieldadjust && !prinst.pr_typecurrent))\n\t\tfor (i=0 ; i<pr_progs->numfielddefs ; i++)\n\t\t{\n\t\t\tif (pr_types)\n\t\t\t\ttype = pr_types[pr_fielddefs32[i].type & ~(DEF_SHARED|DEF_SAVEGLOBAL)].type;\n\t\t\telse\n\t\t\t\ttype = pr_fielddefs32[i].type & ~(DEF_SHARED|DEF_SAVEGLOBAL);\n\t\t\tif (type != ev_vector)\n\t\t\t\tQC_RegisterFieldVar(&progfuncs->funcs, type, pr_fielddefs32[i].s_name+pr_strings-stringadjust, -1, pr_fielddefs32[i].ofs);\n\t\t}\n\t\tbreak;\n\tcase PST_UHEXEN2:\n\t\tfor (i=0 ; i<pr_progs->numglobaldefs ; i++)\n\t\t{\n\t\t\tpr_globaldefs32[i].type = (unsigned int)PRLittleLong (pr_globaldefs32[i].type)>>16;\n#ifndef NOENDIAN\n\t\t\tpr_globaldefs32[i].ofs = PRLittleLong (pr_globaldefs32[i].ofs);\n\t\t\tpr_globaldefs32[i].s_name = (string_t)PRLittleLong ((long)pr_globaldefs32[i].s_name);\n#endif\n\t\t\tpr_globaldefs32[i].s_name += stringadjust;\n\t\t}\n\n\t\tfor (i=0 ; i<pr_progs->numfielddefs ; i++)\n\t\t{\n\t\t\tpr_fielddefs32[i].type = (unsigned int)PRLittleLong (pr_fielddefs32[i].type)>>16;\n#ifndef NOENDIAN\n\t\t\tpr_fielddefs32[i].ofs = PRLittleLong (pr_fielddefs32[i].ofs);\n\t\t\tpr_fielddefs32[i].s_name = (string_t)PRLittleLong ((long)pr_fielddefs32[i].s_name);\n#endif\n\n\t\t\tif (reorg)\n\t\t\t{\n\t\t\t\tif (pr_types)\n\t\t\t\t\ttype = pr_types[pr_fielddefs32[i].type & ~(DEF_SHARED|DEF_SAVEGLOBAL)].type;\n\t\t\t\telse\n\t\t\t\t\ttype = pr_fielddefs32[i].type & ~(DEF_SHARED|DEF_SAVEGLOBAL);\n\t\t\t\tif (progfuncs->funcs.fieldadjust && !prinst.pr_typecurrent)\t//we need to make sure all fields appear in their original place.\n\t\t\t\t\tQC_RegisterFieldVar(&progfuncs->funcs, type, pr_fielddefs32[i].s_name+pr_strings, 4*(pr_fielddefs32[i].ofs+progfuncs->funcs.fieldadjust), -1);\n\t\t\t\telse if (type == ev_vector)\n\t\t\t\t\tQC_RegisterFieldVar(&progfuncs->funcs, type, pr_fielddefs32[i].s_name+pr_strings, -1, pr_fielddefs32[i].ofs);\n\t\t\t}\n\t\t\tpr_fielddefs32[i].s_name += stringadjust;\n\t\t}\n\t\tif (reorg && !(progfuncs->funcs.fieldadjust && !prinst.pr_typecurrent))\n\t\tfor (i=0 ; i<pr_progs->numfielddefs ; i++)\n\t\t{\n\t\t\tif (pr_types)\n\t\t\t\ttype = pr_types[pr_fielddefs32[i].type & ~(DEF_SHARED|DEF_SAVEGLOBAL)].type;\n\t\t\telse\n\t\t\t\ttype = pr_fielddefs32[i].type & ~(DEF_SHARED|DEF_SAVEGLOBAL);\n\t\t\tif (type != ev_vector)\n\t\t\t\tQC_RegisterFieldVar(&progfuncs->funcs, type, pr_fielddefs32[i].s_name+pr_strings-stringadjust, -1, pr_fielddefs32[i].ofs);\n\t\t}\n\t\tbreak;\n\tdefault:\n\t\texterns->Sys_Error(\"Bad struct type\");\n\t}\n\n//ifstring fixes arn't performed anymore.\n//the following switch just fixes endian and hexen2 calling conventions (by using different opcodes).\n\tswitch(current_progstate->structtype)\n\t{\n\tcase PST_QTEST:\n\t\tfor (i=0 ; i<pr_progs->numstatements ; i++)\n\t\t{\n\t\t\tqtest_statement_t qtst = ((qtest_statement_t*)st16)[i];\n\n\t\t\tst16[i].op = PRLittleShort(qtst.op);\n\t\t\tst16[i].a = PRLittleShort(qtst.a);\n\t\t\tst16[i].b = PRLittleShort(qtst.b);\n\t\t\tst16[i].c = PRLittleShort(qtst.c);\n\t\t\t// could use the line info as lno information maybe? is it really worth it?\n\t\t\t// also never assuming h2 calling mechanism\n\t\t}\n\t\tPR_CleanUpStatements16(progfuncs, st16, false);\n\t\tbreak;\n\tcase PST_DEFAULT:\n\t\tfor (i=0 ; i<pr_progs->numstatements ; i++)\n\t\t{\n#ifndef NOENDIAN\n\t\t\tst16[i].op = PRLittleShort(st16[i].op);\n\t\t\tst16[i].a = PRLittleShort(st16[i].a);\n\t\t\tst16[i].b = PRLittleShort(st16[i].b);\n\t\t\tst16[i].c = PRLittleShort(st16[i].c);\n#endif\n\t\t\tif (st16[i].op >= OP_CALL1 && st16[i].op <= OP_CALL8)\n\t\t\t{\n\t\t\t\tif (st16[i].b)\n\t\t\t\t{\n\t\t\t\t\thexencalling = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tPR_CleanUpStatements16(progfuncs, st16, hexencalling);\n\t\tbreak;\n\n\tcase PST_UHEXEN2:\n\t\thexencalling = true;\n\t\tfor (i=0 ; i<pr_progs->numstatements ; i++)\n\t\t{\n\t\t\tpr_statements32[i].op = (unsigned int)PRLittleLong(pr_statements32[i].op)>>16;\n#ifndef NOENDIAN\n\t\t\tpr_statements32[i].a = PRLittleLong(pr_statements32[i].a);\n\t\t\tpr_statements32[i].b = PRLittleLong(pr_statements32[i].b);\n\t\t\tpr_statements32[i].c = PRLittleLong(pr_statements32[i].c);\n#endif\n\t\t}\n\t\tPR_CleanUpStatements32(progfuncs, pr_statements32, hexencalling);\n\t\tbreak;\n\tcase PST_KKQWSV:\n\tcase PST_FTE32:\n\t\tfor (i=0 ; i<pr_progs->numstatements ; i++)\n\t\t{\n#ifndef NOENDIAN\n\t\t\tpr_statements32[i].op = PRLittleLong(pr_statements32[i].op);\n\t\t\tpr_statements32[i].a = PRLittleLong(pr_statements32[i].a);\n\t\t\tpr_statements32[i].b = PRLittleLong(pr_statements32[i].b);\n\t\t\tpr_statements32[i].c = PRLittleLong(pr_statements32[i].c);\n#endif\n\t\t\tif (pr_statements32[i].op >= OP_CALL1 && pr_statements32[i].op <= OP_CALL8)\n\t\t\t{\n\t\t\t\tif (pr_statements32[i].b)\n\t\t\t\t{\n\t\t\t\t\thexencalling = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tPR_CleanUpStatements32(progfuncs, pr_statements32, hexencalling);\n\t\tbreak;\n\t}\n\n/*\n\tif (headercrc == -1)\n\t{\n\t\tisfriked = true;\n\t\tif (current_progstate->structtype != PST_DEFAULT)\n\t\t\texterns->Sys_Error(\"Decompiling a bigprogs\");\n\t\treturn true;\n\t}\n*/\n\tprogstype = current_progstate-pr_progstate;\n\n//\tQC_StartShares(progfuncs);\n\n\tisfriked = true;\n\tif (!prinst.pr_typecurrent)\t//progs 0 always acts as string stripped.\n\t\tisfriked = -1;\t\t\t//partly to avoid some bad/optimised progs.\n\n\n\tbasictypetable = NULL;\n\tif (prinst.reorganisefields == 2)\n\t{\n\t\tswitch(current_progstate->structtype)\n\t\t{\t//gmqcc fucks up the globals. it writes FLOAT defs instead of field defs. stupid stupid stupid.\n\t\tcase PST_DEFAULT:\n\t\t\t{\n\t\t\t\tdstatement16_t *st = current_progstate->statements;\n\t\t\t\tbasictypetable = externs->memalloc(sizeof(*basictypetable) * pr_progs->numglobals);\n\t\t\t\tmemset(basictypetable, 0, sizeof(*basictypetable) * pr_progs->numglobals);\n\t\t\t\tfor (i = 0; i < pr_progs->numstatements; i++)\n\t\t\t\t{\n\t\t\t\t\tswitch(st[i].op)\n\t\t\t\t\t{\n\t\t\t\t\tcase OP_ADDRESS:\n\t\t\t\t\t\tif (st[i+1].op == OP_STOREP_V && st[i+1].b == st[i].c)\n\t\t\t\t\t\t{\t//following stores a vector to this field.\n\t\t\t\t\t\t\tif (st[i].b+2u < pr_progs->numglobals)\n\t\t\t\t\t\t\t{\t//vectors are usually 3 fields. if they're not then we're screwed.\n\t\t\t\t\t\t\t\tbasictypetable[st[i].b+0] = ev_field;\n\t\t\t\t\t\t\t\tbasictypetable[st[i].b+1] = ev_field;\n\t\t\t\t\t\t\t\tbasictypetable[st[i].b+2] = ev_field;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t\t//fallthrough\n\t\t\t\t\tcase OP_LOAD_F:\n\t\t\t\t\tcase OP_LOAD_S:\n\t\t\t\t\tcase OP_LOAD_ENT:\n\t\t\t\t\tcase OP_LOAD_FLD:\n\t\t\t\t\tcase OP_LOAD_FNC:\n\t\t\t\t\tcase OP_LOAD_I:\n\t\t\t\t\tcase OP_LOAD_P:\n\t\t\t\t\t\tif (st[i].b < pr_progs->numglobals)\n\t\t\t\t\t\t\tbasictypetable[st[i].b] = ev_field;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase OP_LOAD_V:\n\t\t\t\t\t\tif (st[i].b+2u < pr_progs->numglobals)\n\t\t\t\t\t\t{\t//vectors are usually 3 fields. if they're not then we're screwed.\n\t\t\t\t\t\t\tbasictypetable[st[i].b+0] = ev_field;\n\t\t\t\t\t\t\tbasictypetable[st[i].b+1] = ev_field;\n\t\t\t\t\t\t\tbasictypetable[st[i].b+2] = ev_field;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tfor (i = 0; i < pr_progs->numglobaldefs; i++)\n\t\t\t\t{\n\t\t\t\t\tddef16_t *gd = gd16+i;\n\t\t\t\t\tswitch(gd->type & ~(DEF_SAVEGLOBAL|DEF_SHARED))\n\t\t\t\t\t{\n\t\t\t\t\tcase ev_field:\t//depend on _y _z to mark those globals.\n\t\t\t\t\t\tbasictypetable[gd->ofs] = ev_field;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tfor (i = 0; i < pr_progs->numglobals; i++)\n\t\t\t\t{\n\t\t\t\t\tif (basictypetable[i] == ev_field)\n\t\t\t\t\t\tQC_AddFieldGlobal(&progfuncs->funcs, (int *)glob + i);\n\t\t\t\t}\n\t\t\t\texterns->memfree(basictypetable);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase PST_QTEST:\t\t//not likely to need this\n\t\tcase PST_KKQWSV:\t//fixme...\n\t\tcase PST_FTE32:\t\t//fingers crossed...\n\t\tcase PST_UHEXEN2:\n\t\t\tbreak;\n\t\t}\n\t}\n\n//\tlen = 0;\n\tswitch(current_progstate->structtype)\n\t{\n\tcase PST_DEFAULT:\n\tcase PST_KKQWSV:\n\t\tfor (i=0 ; i<pr_progs->numglobaldefs ; i++)\n\t\t{\n\t\t\tif (pr_types)\n\t\t\t\ttype = pr_types[gd16[i].type & ~(DEF_SHARED|DEF_SAVEGLOBAL)].type;\n\t\t\telse\n\t\t\t\ttype = gd16[i].type & ~(DEF_SHARED|DEF_SAVEGLOBAL);\n\n\t\t\tif (gd16[i].type & DEF_SHARED)\n\t\t\t{\n\t\t\t\tgd16[i].type &= ~DEF_SHARED;\n\t\t\t\tif (pr_types)\n\t\t\t\t\tQC_AddSharedVar(&progfuncs->funcs, gd16[i].ofs, pr_types[gd16[i].type & ~(DEF_SHARED|DEF_SAVEGLOBAL)].size);\n\t\t\t\telse\n\t\t\t\t\tQC_AddSharedVar(&progfuncs->funcs, gd16[i].ofs, type_size[type]);\n\t\t\t}\n\t\t\tswitch(type)\n\t\t\t{\n\t\t\tcase ev_field:\n\t\t\t\tif (reorg && !basictypetable)\n\t\t\t\t\tQC_AddSharedFieldVar(&progfuncs->funcs, i, pr_strings - stringadjust);\n\t\t\t\tbreak;\n\t\t\tcase ev_pointer:\n\t\t\t\tif (((int *)glob)[gd16[i].ofs] & 0x80000000)\n\t\t\t\t{\n\t\t\t\t\t((int *)glob)[gd16[i].ofs] &= ~0x80000000;\n\t\t\t\t\t((int *)glob)[gd16[i].ofs] += pointeradjust;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tFALLTHROUGH\n\t\t\tcase ev_string:\n\t\t\t\tif (((unsigned int *)glob)[gd16[i].ofs]>=progstate->progs->numstrings)\n\t\t\t\t\texterns->Printf(\"PR_LoadProgs: invalid string value (%x >= %x) in '%s'\\n\", ((unsigned int *)glob)[gd16[i].ofs], progstate->progs->numstrings, gd16[i].s_name+pr_strings-stringadjust);\n\t\t\t\telse if (isfriked != -1)\n\t\t\t\t{\n\t\t\t\t\tif (((int *)glob)[gd16[i].ofs])\t//quakec uses string tables. 0 must remain null, or 'if (s)' can break.\n\t\t\t\t\t{\n\t\t\t\t\t\t((int *)glob)[gd16[i].ofs] += stringadjust;\n\t\t\t\t\t\tisfriked = false;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\t((int *)glob)[gd16[i].ofs] = 0;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase ev_function:\n\t\t\t\tif (((int *)glob)[gd16[i].ofs])\t//don't change null funcs\n\t\t\t\t{\n//\t\t\t\t\tif (fnc[((int *)glob)[gd16[i].ofs]].first_statement>=0)\t//this is a hack. Make all builtins switch to the main progs first. Allows builtin funcs to cache vars from just the main progs.\n\t\t\t\t\t\t((int *)glob)[gd16[i].ofs] |= progstype << 24;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tbreak;\n\tcase PST_QTEST:\n\tcase PST_FTE32:\n\tcase PST_UHEXEN2:\n\t\tfor (i=0 ; i<pr_progs->numglobaldefs ; i++)\n\t\t{\n\t\t\tif (pr_types)\n\t\t\t\ttype = pr_types[pr_globaldefs32[i].type & ~(DEF_SHARED|DEF_SAVEGLOBAL)].type;\n\t\t\telse\n\t\t\t\ttype = pr_globaldefs32[i].type & ~(DEF_SHARED|DEF_SAVEGLOBAL);\n\n\t\t\tif (pr_globaldefs32[i].type & DEF_SHARED)\n\t\t\t{\n\t\t\t\tpr_globaldefs32[i].type &= ~DEF_SHARED;\n\t\t\t\tif (pr_types)\n\t\t\t\t\tQC_AddSharedVar(&progfuncs->funcs, pr_globaldefs32[i].ofs, pr_types[pr_globaldefs32[i].type & ~(DEF_SHARED|DEF_SAVEGLOBAL)].size);\n\t\t\t\telse\n\t\t\t\t\tQC_AddSharedVar(&progfuncs->funcs, pr_globaldefs32[i].ofs, type_size[type]);\n\t\t\t}\n\t\t\tswitch(type)\n\t\t\t{\n\t\t\tcase ev_field:\n\t\t\t\tQC_AddSharedFieldVar(&progfuncs->funcs, i, pr_strings - stringadjust);\n\t\t\t\tbreak;\n\t\t\tcase ev_pointer:\n\t\t\t\tif (((int *)glob)[pr_globaldefs32[i].ofs] & 0x80000000)\n\t\t\t\t{\n\t\t\t\t\t((int *)glob)[pr_globaldefs32[i].ofs] &= ~0x80000000;\n\t\t\t\t\t((int *)glob)[pr_globaldefs32[i].ofs] += pointeradjust;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tFALLTHROUGH\n\t\t\tcase ev_string:\n\t\t\t\tif (((unsigned int *)glob)[pr_globaldefs32[i].ofs]>=progstate->progs->numstrings)\n\t\t\t\t\texterns->Printf(\"PR_LoadProgs: invalid string value (%x >= %x) in '%s'\\n\", ((unsigned int *)glob)[pr_globaldefs32[i].ofs], progstate->progs->numstrings, pr_globaldefs32[i].s_name+pr_strings-stringadjust);\n\t\t\t\telse if (((int *)glob)[pr_globaldefs32[i].ofs])\t//quakec uses string tables. 0 must remain null, or 'if (s)' can break.\n\t\t\t\t{\n\t\t\t\t\t((int *)glob)[pr_globaldefs32[i].ofs] += stringadjust;\n\t\t\t\t\tisfriked = false;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase ev_function:\n\t\t\t\tif (((int *)glob)[pr_globaldefs32[i].ofs])\t//don't change null funcs\n\t\t\t\t\t((int *)glob)[pr_globaldefs32[i].ofs] |= progstype << 24;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tif (pr_progs->version == PROG_EXTENDEDVERSION && pr_progs->numbodylessfuncs)\n\t\t{\n\t\t\ts = &((char *)pr_progs)[pr_progs->ofsbodylessfuncs];\n\t\t\tfor (i = 0; i < pr_progs->numbodylessfuncs; i++)\n\t\t\t{\n\t\t\t\td32 = ED_FindGlobal32(progfuncs, s);\n\t\t\t\td2 = ED_FindGlobalOfsFromProgs(progfuncs, &pr_progstate[0], s, ev_function);\n\t\t\t\tif (!d2)\n\t\t\t\t\texterns->Sys_Error(\"Runtime-linked function %s was not found in existing progs\", s);\n\t\t\t\tif (!d32)\n\t\t\t\t\texterns->Sys_Error(\"Couldn't find def for \\\"%s\\\"\", s);\n\t\t\t\t((int *)glob)[d32->ofs] = (*(func_t *)&pr_progstate[0].globals[*d2]);\n\n\t\t\t\ts+=strlen(s)+1;\n\t\t\t}\n\t\t}\n\t\tbreak;\n\tdefault:\n\t\texterns->Sys_Error(\"Bad struct type\");\n\t}\n\n\tif ((isfriked && prinst.pr_typecurrent))\t//friked progs only allow one file.\n\t{\n\t\texterns->Printf(\"You are trying to load a string-stripped progs as an addon.\\nThis behaviour is not supported. Try removing some optimizations.\");\n\t\tPRHunkFree(progfuncs, hmark);\n\t\tpr_progs=NULL;\n\t\treturn false;\n\t}\n\n\tpr_strings+=stringadjust;\n\tif (!progfuncs->funcs.stringtable)\n\t\tprogfuncs->funcs.stringtable = pr_strings;\n\n\tif (progfuncs->funcs.stringtablesize + progfuncs->funcs.stringtable < pr_strings + pr_progs->numstrings)\n\t\tprogfuncs->funcs.stringtablesize = (pr_strings + pr_progs->numstrings) - progfuncs->funcs.stringtable;\n\n\t//make sure the localstack is addressable to the qc, so we can OP_PUSH okay.\n\tif (!prinst.localstack)\n\t\tprinst.localstack = PRAddressableExtend(progfuncs, NULL, 0, sizeof(float)*LOCALSTACK_SIZE);\n\n\tif (externs->MapNamedBuiltin)\n\t{\n\t\tfor (i=0,fnc2=pr_cp_functions; i<pr_progs->numfunctions; i++, fnc2++)\n\t\t{\n\t\t\tif (i && !fnc2->first_statement)\n\t\t\t\tfnc2->first_statement = -externs->MapNamedBuiltin(&progfuncs->funcs, pr_progs->crc, PR_StringToNative(&progfuncs->funcs, fnc2->s_name));\n\t\t}\n\t}\n\n\teval = PR_FindGlobal(&progfuncs->funcs, \"thisprogs\", progstype, NULL);\n\tif (eval)\n\t\teval->prog = progstype;\n\n\tswitch(current_progstate->structtype)\n\t{\n\tcase PST_DEFAULT:\n\t\tif (pr_progs->version == PROG_EXTENDEDVERSION && pr_progs->numbodylessfuncs)\n\t\t{\n\t\t\ts = &((char *)pr_progs)[pr_progs->ofsbodylessfuncs];\n\t\t\tfor (i = 0; i < pr_progs->numbodylessfuncs; i++)\n\t\t\t{\n\t\t\t\td16 = ED_FindGlobal16(progfuncs, s);\n\t\t\t\tif (!d16)\n\t\t\t\t{\n\t\t\t\t\texterns->Printf(\"\\\"%s\\\" requires the external function \\\"%s\\\", but the definition was stripped\\n\", filename, s);\n\t\t\t\t\tPRHunkFree(progfuncs, hmark);\n\t\t\t\t\tpr_progs=NULL;\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\n\t\t\t\t((int *)glob)[d16->ofs] = PR_FindFunc(&progfuncs->funcs, s, PR_ANY);\n\t\t\t\tif (!((int *)glob)[d16->ofs])\n\t\t\t\t\texterns->Printf(\"Warning: Runtime-linked function %s could not be found (loading %s)\\n\", s, filename);\n\t\t\t\ts+=strlen(s)+1;\n\t\t\t}\n\t\t}\n\t\tbreak;\n\tcase PST_QTEST:\n\tcase PST_KKQWSV:\n\t\tbreak;\t//cannot happen anyway.\n\tcase PST_UHEXEN2:\n\tcase PST_FTE32:\n\t\tif (pr_progs->version == PROG_EXTENDEDVERSION && pr_progs->numbodylessfuncs)\n\t\t{\n\t\t\ts = &((char *)pr_progs)[pr_progs->ofsbodylessfuncs];\n\t\t\tfor (i = 0; i < pr_progs->numbodylessfuncs; i++)\n\t\t\t{\n\t\t\t\td32 = ED_FindGlobal32(progfuncs, s);\n\t\t\t\tif (!d32)\n\t\t\t\t{\n\t\t\t\t\texterns->Printf(\"\\\"%s\\\" requires the external function \\\"%s\\\", but the definition was stripped\\n\", filename, s);\n\t\t\t\t\tPRHunkFree(progfuncs, hmark);\n\t\t\t\t\tpr_progs=NULL;\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\n\t\t\t\t((int *)glob)[d32->ofs] = PR_FindFunc(&progfuncs->funcs, s, PR_ANY);\n\t\t\t\tif (!((int *)glob)[d32->ofs])\n\t\t\t\t\texterns->Printf(\"Warning: Runtime-linked function %s could not be found (loading %s)\\n\", s, filename);\n\t\t\t\ts+=strlen(s)+1;\n\t\t\t}\n\t\t}\n\t\tbreak;\n\t}\n\n\teval = PR_FindGlobal(&progfuncs->funcs, \"__ext__fasttrackarrays\", PR_CURRENT, NULL);\n\tif (eval)\t//we support these opcodes\n\t\teval->_float = true;\n\n\treturn true;\n}\n\n\n\nstruct edict_s *PDECL QC_EDICT_NUM(pubprogfuncs_t *ppf, unsigned int n)\n{\n\tprogfuncs_t *progfuncs = (progfuncs_t*)ppf;\n\tif (n >= prinst.maxedicts)\n\t\texterns->Sys_Error (\"QCLIB: EDICT_NUM: bad number %i\", n);\n\n\treturn (struct edict_s*)prinst.edicttable[n];\n}\n\nunsigned int PDECL QC_NUM_FOR_EDICT(pubprogfuncs_t *ppf, struct edict_s *e)\n{\n\tprogfuncs_t *progfuncs = (progfuncs_t*)ppf;\n\tedictrun_t *er = (edictrun_t*)e;\n\tif (!er || er->entnum >= prinst.maxedicts)\n\t\texterns->Sys_Error (\"QCLIB: NUM_FOR_EDICT: bad pointer (%p)\", e);\n\treturn er->entnum;\n}\n"
  },
  {
    "path": "engine/qclib/pr_exec.c",
    "content": "#define PROGSUSED\n#include \"progsint.h\"\n//#include \"editor.h\"\n\n#if __STDC_VERSION__ >= 199901L\n\t#define fte_restrict restrict\n#elif defined(_MSC_VER) && _MSC_VER >= 1400\n\t#define fte_restrict __restrict\n#else\n\t#define fte_restrict\n#endif\n\n#if defined(_WIN32) || defined(__DJGPP__)\n\t#include <malloc.h>\n#elif defined(__unix__) && !defined(__linux__) // quick hack for the bsds and other unix systems\n\t#include<stdlib.h>\n#elif !defined(alloca)\t//alloca.h isn't present on bsd (stdlib.h should define it to __builtin_alloca, and we can check for that here).\n\t#include <alloca.h>\n#endif\n\n#define HunkAlloc BADGDFG sdfhhsf FHS\n\n\n#define Host_Error Sys_Error\n\n// I put the following here to resolve \"undefined reference to `__imp__vsnprintf'\" with MinGW64 ~ Moodles\n#if 0//def _WIN32\n\t#if (_MSC_VER >= 1400)\n\t\t//with MSVC 8, use MS extensions\n\t\t#define snprintf linuxlike_snprintf_vc8\n\t\tvoid VARGS linuxlike_snprintf_vc8(char *buffer, int size, const char *format, ...) LIKEPRINTF(3);\n\t\t#define vsnprintf(a, b, c, d) (void)(vsnprintf_s(a, b, _TRUNCATE, c, d))\n\t#else\n\t\t//msvc crap\n\t\t#define snprintf linuxlike_snprintf\n\t\tvoid VARGS linuxlike_snprintf(char *buffer, int size, const char *format, ...) LIKEPRINTF(3);\n\t\t#define vsnprintf linuxlike_vsnprintf\n\t\tvoid VARGS linuxlike_vsnprintf(char *buffer, int size, const char *format, va_list argptr);\n\t#endif\n#endif\n\n\n//cpu clock stuff (glorified rdtsc), for profile timing only\n#if !defined(Sys_GetClock) && defined(_WIN32)\n\t//windows has some specific functions for this (traditionally wrapping rdtsc)\n\t//note: on some systems, you may need to force cpu affinity to a single core via task manager\n\tstatic prclocks_t Sys_GetClock(void)\n\t{\n\t\tLARGE_INTEGER li;\n\t\tQueryPerformanceCounter(&li);\n\t\treturn li.QuadPart;\n\t}\n\tprclocks_t Sys_GetClockRate(void)\n\t{\n\t\tLARGE_INTEGER li;\n\t\tQueryPerformanceFrequency(&li);\n\t\treturn li.QuadPart;\n\t}\n\t#define Sys_GetClock Sys_GetClock\n#endif\n\n#if 0//!defined(Sys_GetClock) && defined(__unix__)\n\t//linux/unix has some annoying abstraction and shows time in nanoseconds rather than cycles. lets hope we don't waste too much time  reading it.\n\t#include <unistd.h>\n\t#if defined(_POSIX_TIMERS) && _POSIX_TIMERS >= 0\n\t\t#include <time.h>\n\t\t#ifdef CLOCK_PROCESS_CPUTIME_ID\n\t\t\tstatic prclocks_t Sys_GetClock(void)\n\t\t\t{\n\t\t\t\tstruct timespec c;\n\t\t\t\tclock_gettime(CLOCK_PROCESS_CPUTIME_ID, &c);\n\t\t\t\treturn (c.tv_sec*1000000000ull) + c.tv_nsec;\n\t\t\t}\n\t\t\t#define Sys_GetClock Sys_GetClock\n\t\t\tprclocks_t Sys_GetClockRate(void)\n\t\t\t{\n\t\t\t\treturn 1000000000ull;\n\t\t\t}\n\t\t#endif\n\t#endif\n#endif\n\n#if !defined(Sys_GetClock) && defined(__unix__)\n\t#include <time.h>\n\t#define Sys_GetClock() clock()\n\tprclocks_t Sys_GetClockRate(void) { return CLOCKS_PER_SEC; }\n#endif\n\n#ifndef Sys_GetClock\n\t//other systems have no choice but to omit this feature in some way. this is just for profiling, so we can get away with stubs.\n\t#define Sys_GetClock() 0\n\tprclocks_t Sys_GetClockRate(void) { return 1; }\n#endif\n\n//=============================================================================\n\n/*\n=================\nPR_PrintStatement\n=================\n*/\nstatic void PR_PrintStatement (progfuncs_t *progfuncs, int statementnum)\n{\n\tunsigned int op;\n\tunsigned int arg[3];\n\n\tswitch(current_progstate->structtype)\n\t{\n\tdefault:\n\tcase PST_DEFAULT:\n\tcase PST_QTEST:\n\t\top = ((dstatement16_t*)current_progstate->statements + statementnum)->op;\n\t\targ[0] = ((dstatement16_t*)current_progstate->statements + statementnum)->a;\n\t\targ[1] = ((dstatement16_t*)current_progstate->statements + statementnum)->b;\n\t\targ[2] = ((dstatement16_t*)current_progstate->statements + statementnum)->c;\n\t\tbreak;\n\tcase PST_KKQWSV:\n\tcase PST_FTE32:\n\t\top = ((dstatement32_t*)current_progstate->statements + statementnum)->op;\n\t\targ[0] = ((dstatement32_t*)current_progstate->statements + statementnum)->a;\n\t\targ[1] = ((dstatement32_t*)current_progstate->statements + statementnum)->b;\n\t\targ[2] = ((dstatement32_t*)current_progstate->statements + statementnum)->c;\n\t\tbreak;\n\t}\n\n#if !defined(MINIMAL) && !defined(OMIT_QCC)\n#define TYPEHINT(a) (pr_opcodes[op].type_##a)\n\tif ( (unsigned)op < OP_NUMOPS)\n\t{\n\t\tint i;\n\t\texterns->Printf (\"%s \",  pr_opcodes[op].opname);\n\t\ti = strlen(pr_opcodes[op].opname);\n\t\tfor ( ; i<10 ; i++)\n\t\t\texterns->Printf (\" \");\n\t}\n\telse\n#endif\n\t\texterns->Printf (\"op%3i \", op);\n\n#ifndef TYPEHINT\n#define TYPEHINT(a) NULL\n#endif\n\n\tif (op == OP_IF_F || op == OP_IFNOT_F || op == OP_IF_I || op == OP_IFNOT_I || op == OP_IF_S || op == OP_IFNOT_S)\n\t\texterns->Printf (\"%sbranch %i\",PR_GlobalString(progfuncs, arg[0], TYPEHINT(a)),arg[1]);\n\telse if (op == OP_GOTO)\n\t{\n\t\texterns->Printf (\"branch %i\",arg[0]);\n\t}\n\telse if (op == OP_BOUNDCHECK)\n\t{\n\t\texterns->Printf (\"%s\",PR_GlobalString(progfuncs, arg[0], TYPEHINT(a)));\n\t\texterns->Printf (\"%s\",PR_GlobalStringImmediate(progfuncs, arg[1]));\n\t\texterns->Printf (\"%s\",PR_GlobalStringImmediate(progfuncs, arg[2]));\n\t}\n\telse if ( (unsigned)(op - OP_STORE_F) < 6)\n\t{\n\t\texterns->Printf (\"%s\",PR_GlobalString(progfuncs, arg[0], TYPEHINT(a)));\n\t\texterns->Printf (\"%s\",PR_GlobalStringNoContents(progfuncs, arg[1]));\n\t}\n\telse\n\t{\n\t\tif (arg[0])\n\t\t\texterns->Printf (\"%s\",PR_GlobalString(progfuncs, arg[0], TYPEHINT(a)));\n\t\tif (arg[1])\n\t\t\texterns->Printf (\"%s\",PR_GlobalString(progfuncs, arg[1], TYPEHINT(b)));\n\t\tif (arg[2])\n\t\t\texterns->Printf (\"%s\",PR_GlobalStringNoContents(progfuncs, arg[2]));\n\t}\n\texterns->Printf (\"\\n\");\n}\n\n#ifdef _WIN32\nstatic void VARGS QC_snprintfz (char *dest, size_t size, const char *fmt, ...)\n{\n\tva_list args;\n\tva_start (args, fmt);\n\t_vsnprintf (dest, size-1, fmt, args);\n\tva_end (args);\n\t//make sure its terminated.\n\tdest[size-1] = 0;\n}\n#else\n#define QC_snprintfz snprintf\n#endif\n\nvoid PDECL PR_GenerateStatementString (pubprogfuncs_t *ppf, int statementnum, char *out, int outlen)\n{\n\tprogfuncs_t *progfuncs = (progfuncs_t*)ppf;\n\tunsigned int op;\n\tunsigned int arg[3];\n\n\t*out = 0;\n\toutlen--;\n\n\tif ((unsigned)statementnum >= current_progstate->progs->numstatements)\n\t\treturn;\n\tswitch(current_progstate->structtype)\n\t{\n\tcase PST_DEFAULT:\n\tcase PST_QTEST:\n\t\top = ((dstatement16_t*)current_progstate->statements + statementnum)->op;\n\t\targ[0] = ((dstatement16_t*)current_progstate->statements + statementnum)->a;\n\t\targ[1] = ((dstatement16_t*)current_progstate->statements + statementnum)->b;\n\t\targ[2] = ((dstatement16_t*)current_progstate->statements + statementnum)->c;\n\t\tbreak;\n\tcase PST_KKQWSV:\n\tcase PST_FTE32:\n\t\top = ((dstatement32_t*)current_progstate->statements + statementnum)->op;\n\t\targ[0] = ((dstatement32_t*)current_progstate->statements + statementnum)->a;\n\t\targ[1] = ((dstatement32_t*)current_progstate->statements + statementnum)->b;\n\t\targ[2] = ((dstatement32_t*)current_progstate->statements + statementnum)->c;\n\t\tbreak;\n\tdefault:\n\t\treturn;\n\t}\n\top = op & ~0x8000;\t//break points.\n\n\tif (current_progstate->linenums)\n\t{\n\t\tQC_snprintfz (out, outlen, \"%3i: \", current_progstate->linenums[statementnum]);\n\t\toutlen -= strlen(out);\n\t\tout += strlen(out);\n\t}\n\telse\n\t{\n\t\tQC_snprintfz (out, outlen, \"%3i: \", statementnum);\n\t\toutlen -= strlen(out);\n\t\tout += strlen(out);\n\t}\n\n#if !defined(MINIMAL) && !defined(OMIT_QCC)\n\tif ( (unsigned)op < OP_NUMOPS)\n\t{\n\t\tQC_snprintfz (out, outlen, \"%-12s \", pr_opcodes[op].opname);\n\t\toutlen -= strlen(out);\n\t\tout += strlen(out);\n\t}\n\telse\n#endif\n\t{\n\t\tQC_snprintfz (out, outlen, \"op%3i \", op);\n\t\toutlen -= strlen(out);\n\t\tout += strlen(out);\n\t}\n\n\tif (op == OP_IF_F || op == OP_IFNOT_F || op == OP_IF_I || op == OP_IFNOT_I || op == OP_IF_S || op == OP_IFNOT_S)\n\t{\n\t\tQC_snprintfz (out, outlen, \"%sbranch %i(%+i)\",PR_GlobalString(progfuncs, arg[0], TYPEHINT(a)),(short)arg[1], statementnum+(short)arg[0]);\n\t\toutlen -= strlen(out);\n\t\tout += strlen(out);\n\t}\n\telse if (op == OP_GOTO)\n\t{\n\t\tQC_snprintfz (out, outlen, \"branch %i(%+i)\",(short)arg[0], statementnum+(short)arg[0]);\n\t\toutlen -= strlen(out);\n\t\tout += strlen(out);\n\t}\n\telse if ( (unsigned)(op - OP_STORE_F) < 6)\n\t{\n\t\tQC_snprintfz (out, outlen, \"%s\",PR_GlobalString(progfuncs, arg[0], TYPEHINT(a)));\n\t\toutlen -= strlen(out);\n\t\tout += strlen(out);\n\t\tQC_snprintfz (out, outlen, \"%s\", PR_GlobalStringNoContents(progfuncs, arg[1]));\n\t\toutlen -= strlen(out);\n\t\tout += strlen(out);\n\t}\n\telse\n\t{\n\t\tif (arg[0])\n\t\t{\n\t\t\tQC_snprintfz (out, outlen, \"%s\",PR_GlobalString(progfuncs, arg[0], TYPEHINT(a)));\n\t\t\toutlen -= strlen(out);\n\t\t\tout += strlen(out);\n\t\t}\n\t\tif (arg[1])\n\t\t{\n\t\t\tQC_snprintfz (out, outlen, \"%s\",PR_GlobalString(progfuncs, arg[1], TYPEHINT(b)));\n\t\t\toutlen -= strlen(out);\n\t\t\tout += strlen(out);\n\t\t}\n\t\tif (arg[2])\n\t\t{\n\t\t\tQC_snprintfz (out, outlen, \"%s\", PR_GlobalStringNoContents(progfuncs, arg[2]));\n\t\t\toutlen -= strlen(out);\n\t\t\tout += strlen(out);\n\t\t}\n\t}\n\tQC_snprintfz (out, outlen, \"\\n\");\n\toutlen -= 1;\n\tout += 1;\n}\n\n\n/*\n============\nPR_StackTrace\n============\n*/\nstatic void PDECL PR_PrintRelevantLocals(progfuncs_t *progfuncs)\n{\n\t//scan for op_address/op_load instructions within the function\n\tint st, st2;\n\tint op;\n\tdstatement16_t *st16 = current_progstate->statements;\n\tint line;\n\tif (!current_progstate->linenums || current_progstate->structtype != PST_DEFAULT)\n\t\treturn;\n\n\tline = current_progstate->linenums[prinst.pr_xstatement];\n\tfor (st = prinst.pr_xfunction->first_statement; st16[st].op != OP_DONE; st++)\n\t{\n\t\tif (current_progstate->linenums[st] < line - 2 || current_progstate->linenums[st] > line + 2)\n\t\t\tcontinue;\t//don't go crazy with this.\n\t\top = st16[st].op & ~0x8000;\n\t\tif (op == OP_ADDRESS || (op >= OP_LOAD_F && op <= OP_LOAD_FNC) || op == OP_LOAD_I || op == OP_LOAD_P)\n\t\t{\n\t\t\tddef16_t *ent = ED_GlobalAtOfs16(progfuncs, st16[st].a);\n\t\t\tddef16_t *fld = ED_GlobalAtOfs16(progfuncs, st16[st].b);\n\t\t\tpbool skip = false;\n\t\t\tedictrun_t *ed;\n\t\t\tunsigned int entnum;\n\t\t\teval_t *ptr;\n\t\t\tfdef_t *fdef;\n\t\t\tfdef_t *cnfd;\n\t\t\tconst char *classname;\n\t\t\tif (!ent || !fld)\n\t\t\t\tcontinue;\n\t\t\t//all this extra code to avoid printing dupes...\n\t\t\tfor (st2 = st-1; st2 >= prinst.pr_xfunction->first_statement; st2--)\n\t\t\t{\n\t\t\t\tif (current_progstate->linenums[st2] < line - 2 || current_progstate->linenums[st2] > line + 2)\n\t\t\t\t\tcontinue;\n\t\t\t\top = st16[st2].op & ~0x8000;\n\t\t\t\tif (op == OP_ADDRESS || (op >= OP_LOAD_F && op <= OP_LOAD_FNC) || op == OP_LOAD_I || op == OP_LOAD_P)\n\t\t\t\t\tif (st16[st].a == st16[st2].a && st16[st].b == st16[st2].b)\n\t\t\t\t\t{\n\t\t\t\t\t\tskip = true;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t}\n\t\t\tif (skip)\n\t\t\t\tcontinue;\n\t\t\tentnum = ((eval_t *)&pr_globals[st16[st].a])->edict;\n\t\t\tif (entnum >= sv_num_edicts)\n\t\t\t{\n\t\t\t\tclassname = \"INVALID\";\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\ted = PROG_TO_EDICT_PB(progfuncs, entnum);\n\t\t\t\tif ((unsigned int)((eval_t *)&pr_globals[st16[st].b])->_int*4u >= ed->fieldsize)\n\t\t\t\t\tcontinue;\n\t\t\t\telse\n\t\t\t\t\tptr = (eval_t *)(((int *)edvars(ed)) + ((eval_t *)&pr_globals[st16[st].b])->_int + progfuncs->funcs.fieldadjust);\n\n\t\t\t\tcnfd = ED_FindField(progfuncs, \"classname\");\n\t\t\t\tif (cnfd)\n\t\t\t\t{\n\t\t\t\t\tstring_t *v = (string_t *)((char *)edvars(ed) + cnfd->ofs*4);\n\t\t\t\t\tclassname = PR_StringToNative(&progfuncs->funcs, *v);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tclassname = \"\";\n\t\t\t}\n\t\t\tif (*classname)\n\t\t\t\tfdef = ED_ClassFieldAtOfs(progfuncs, ((eval_t *)&pr_globals[st16[st].b])->_int, classname);\n\t\t\telse\n\t\t\t\tfdef = ED_FieldAtOfs(progfuncs, ((eval_t *)&pr_globals[st16[st].b])->_int);\n\t\t\tif (fdef)\n\t\t\t\texterns->Printf(\"    %s.%s: %s\\n\", PR_StringToNative(&progfuncs->funcs, ent->s_name), PR_StringToNative(&progfuncs->funcs, fld->s_name), PR_ValueString(progfuncs, fdef->type, ptr, false));\n\t\t\telse\n\t\t\t\texterns->Printf(\"    %s.%s: BAD FIELD DEF - %#x\\n\", PR_StringToNative(&progfuncs->funcs, ent->s_name), PR_StringToNative(&progfuncs->funcs, fld->s_name), ptr->_int);\n\t\t}\n\t}\n}\n\nvoid PDECL PR_StackTrace (pubprogfuncs_t *ppf, int showlocals)\n{\n\tprogfuncs_t *progfuncs = (progfuncs_t *)ppf;\n\tconst mfunction_t\t*f;\n\tint prnum;\n\tint\t\t\ti, st;\n\tint progs;\n\tint ofs;\n\tint *globalbase;\n\tint tracing = progfuncs->funcs.debug_trace;\n\tprogs = -1;\n\n\tif (prinst.pr_depth == 0)\n\t{\n\t\texterns->Printf (\"<NO STACK>\\n\");\n\t\treturn;\n\t}\n\n\tprogfuncs->funcs.debug_trace = -10;\t//PR_StringToNative(+via PR_ValueString) has various error conditions that we want to mute instead of causing recursive errors.\n\n\t//point this to the function's locals\n\tglobalbase = (int *)pr_globals + prinst.pr_xfunction->parm_start + prinst.pr_xfunction->locals;\n\n\tfor (i=prinst.pr_depth ; i>0 ; i--)\n\t{\n\t\tif (i == prinst.pr_depth)\n\t\t{\n\t\t\tf = prinst.pr_xfunction;\n\t\t\tst = prinst.pr_xstatement;\n\t\t\tprnum = prinst.pr_typecurrent;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tf = prinst.pr_stack[i].f;\n\t\t\tst = prinst.pr_stack[i].s;\n\t\t\tprnum = prinst.pr_stack[i].progsnum;\n\t\t}\n\n\t\tif (!f)\n\t\t{\n\t\t\texterns->Printf (\"<NO FUNCTION>\\n\");\n\t\t}\n\t\telse\n\t\t{\n\t\t\tglobalbase -= f->locals;\n\n\t\t\tif (prnum != progs)\n\t\t\t{\n\t\t\t\tprogs = prnum;\n\n\t\t\t\texterns->DPrintf (\"<%s>\\n\", pr_progstate[progs].filename);\n\t\t\t}\n\t\t\tif (!f->s_file)\n\t\t\t\texterns->Printf (\"unknown-file : %s\\n\", PR_StringToNative(ppf, f->s_name));\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (pr_progstate[progs].linenums)\n\t\t\t\t\texterns->Printf (\"%12s:%i: %s\\n\", PR_StringToNative(ppf, f->s_file), pr_progstate[progs].linenums[st], PR_StringToNative(ppf, f->s_name));\n\t\t\t\telse\n\t\t\t\t\texterns->Printf (\"%12s : %s+%i\\n\", PR_StringToNative(ppf, f->s_file), PR_StringToNative(ppf, f->s_name), st-f->first_statement);\n\t\t\t}\n\n\t\t\t//locals:0 = no locals\n\t\t\t//locals:1 = top only\n\t\t\t//locals:2 = ALL locals.\n\t\t\tif ((i == prinst.pr_depth && showlocals == 1) || showlocals >= 2)\n\t\t\t{\n\t\t\t\tfor (ofs = 0; ofs < f->locals; ofs++)\n\t\t\t\t{\n\t\t\t\t\tddef16_t *local;\n\t\t\t\t\tlocal = ED_GlobalAtOfs16(progfuncs, f->parm_start+ofs);\n\t\t\t\t\tif (!local)\n\t\t\t\t\t{\n\t\t\t\t\t\tint arg, aofs;\n\t\t\t\t\t\tfor (arg = 0, aofs = 0; arg < f->numparms; arg++)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (ofs >= aofs && ofs < aofs + f->parm_size[arg])\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\taofs += f->parm_size[arg];\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (arg < f->numparms)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (f->parm_size[arg] == 3)\n\t\t\t\t\t\t\t{\t//looks like a vector. print it as such\n\t\t\t\t\t\t\t\texterns->Printf(\"    arg%i(%i): [%g, %g, %g]\\n\", arg, f->parm_start+ofs, *(float *)(globalbase+ofs), *(float *)(globalbase+ofs+1), *(float *)(globalbase+ofs+2));\n\t\t\t\t\t\t\t\tofs += 2;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\texterns->Printf(\"    arg%i(%i): %g===%i\\n\", arg, f->parm_start+ofs, *(float *)(globalbase+ofs), *(int *)(globalbase+ofs) );\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\texterns->Printf(\"     unk(%i): %g===%i\\n\", f->parm_start+ofs, *(float *)(globalbase+ofs), *(int *)(globalbase+ofs) );\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\texterns->Printf(\"    %s: %s\\n\", PR_StringToNative(ppf, local->s_name), PR_ValueString(progfuncs, local->type, (eval_t*)(globalbase+ofs), false));\n\t\t\t\t\t\tif (local->type == ev_vector)\n\t\t\t\t\t\t\tofs+=2;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (i == prinst.pr_depth)\n\t\t\t{\t//scan for op_address/op_load instructions within the function\n\t\t\t\tPR_PrintRelevantLocals(progfuncs);\n\t\t\t}\n\n\t\t\tif (i == prinst.pr_depth)\n\t\t\t\tglobalbase = prinst.localstack + prinst.localstack_used;\n\t\t}\n\t}\n\tprogfuncs->funcs.debug_trace = tracing;\n}\n\n/*\n============================================================================\nPR_ExecuteProgram\n\nThe interpretation main loop\n============================================================================\n*/\n\n/*\n====================\nPR_EnterFunction\n\nReturns the new program statement counter\n====================\n*/\nstatic int ASMCALL PR_EnterFunction (progfuncs_t *progfuncs, mfunction_t *f, int progsnum)\n{\n\tint\t\ti, j, c, o;\n\tprstack_t *st;\n\n\tif (prinst.pr_depth == MAX_STACK_DEPTH)\n\t{\n\t\tPR_StackTrace (&progfuncs->funcs, false);\n\n\t\texterns->Printf (\"stack overflow on call to %s (depth %i)\\n\", progfuncs->funcs.stringtable+f->s_name, prinst.pr_depth);\n\n\t\t//comment this out if you want the progs to try to continue anyway (could cause infinate loops)\n\t\tPR_AbortStack(&progfuncs->funcs);\n\t\texterns->Abort(\"Stack Overflow in %s\\n\", progfuncs->funcs.stringtable+f->s_name);\n\t\treturn prinst.pr_xstatement;\n\t}\n\tst = &prinst.pr_stack[prinst.pr_depth++];\n\tst->s = prinst.pr_xstatement;\n\tst->f = prinst.pr_xfunction;\n\tst->progsnum = progsnum;\n\tst->pushed = prinst.spushed;\n\tst->stepping = progfuncs->funcs.debug_trace;\n\tif (progfuncs->funcs.debug_trace == DEBUG_TRACE_OVER)\n\t\tprogfuncs->funcs.debug_trace = DEBUG_TRACE_OFF;\n\tif (prinst.profiling)\n\t{\n\t\tst->timestamp = Sys_GetClock();\n\t}\n\n\tprinst.localstack_used += prinst.spushed;\t//make sure the call doesn't hurt pushed pointers\n\n// save off any locals that the new function steps on (to a side place, fromwhere they are restored on exit)\n\tc = f->locals;\n\tif (prinst.localstack_used + c > LOCALSTACK_SIZE)\n\t{\n\t\tprinst.localstack_used -= prinst.spushed;\n\t\tprinst.pr_depth--;\n\t\tPR_RunError (&progfuncs->funcs, \"PR_ExecuteProgram: locals stack overflow\\n\");\n\t}\n\n\tfor (i=0 ; i < c ; i++)\n\t\tprinst.localstack[prinst.localstack_used+i] = ((int *)pr_globals)[f->parm_start + i];\n\tprinst.localstack_used += c;\n\n// copy parameters (set initial values)\n\to = f->parm_start;\n\tfor (i=0 ; i<f->numparms ; i++)\n\t{\n\t\tfor (j=0 ; j<f->parm_size[i] ; j++)\n\t\t{\n\t\t\t((int *)pr_globals)[o] = ((int *)pr_globals)[OFS_PARM0+i*3+j];\n\t\t\to++;\n\t\t}\n\t}\n\n\tprinst.pr_xfunction = f;\n\tprinst.spushed = 0;\n\treturn f->first_statement - 1;\t// offset the s++\n}\n\n/*\n====================\nPR_LeaveFunction\n====================\n*/\nstatic int ASMCALL PR_LeaveFunction (progfuncs_t *progfuncs)\n{\n\tint\t\ti, c;\n\tprstack_t *st;\n\n\tif (prinst.pr_depth <= 0)\n\t\texterns->Sys_Error (\"prog stack underflow\");\n\n\t// up stack\n\tst = &prinst.pr_stack[--prinst.pr_depth];\n\n// restore locals from the stack\n\tc = prinst.pr_xfunction->locals;\n\tprinst.localstack_used -= c;\n\tif (prinst.localstack_used < 0)\n\t\tPR_RunError (&progfuncs->funcs, \"PR_ExecuteProgram: locals stack underflow\\n\");\n\n\tfor (i=0 ; i < c ; i++)\n\t\t((int *)pr_globals)[prinst.pr_xfunction->parm_start + i] = prinst.localstack[prinst.localstack_used+i];\n\n\tPR_SwitchProgsParms(progfuncs, st->progsnum);\n\tprinst.spushed = st->pushed;\n\n\tif (!progfuncs->funcs.debug_trace)\n\t\tprogfuncs->funcs.debug_trace = st->stepping;\n\n\tif (prinst.profiling)\n\t{\n\t\tprclocks_t cycles;\n\t\tcycles = Sys_GetClock() - st->timestamp;\n\t\tif (cycles > prinst.profilingalert)\n\t\t\texterns->Printf(\"QC call to %s took over a second\\n\", PR_StringToNative(&progfuncs->funcs,prinst.pr_xfunction->s_name));\n\t\tprinst.pr_xfunction->profiletime += cycles;\n\t\tprinst.pr_xfunction = st->f;\n\t\tif (prinst.pr_depth)\n\t\t\tprinst.pr_xfunction->profilechildtime += cycles;\n\t}\n\telse\n\t\tprinst.pr_xfunction = st->f;\n\n\tprinst.localstack_used -= prinst.spushed;\n\treturn st->s;\n}\n\nddef32_t *ED_FindLocalOrGlobal(progfuncs_t *progfuncs, const char *name, eval_t **val)\n{\n\tstatic ddef32_t def;\n\tddef32_t *def32;\n\tddef16_t *def16;\n\tint i;\n\tprogstate_t *cp = current_progstate;\n\n\tif (!cp)\n\t\treturn NULL;\n\n\tswitch (cp->structtype)\n\t{\n\tcase PST_DEFAULT:\n\tcase PST_KKQWSV:\n\t\t//this gets parms fine, but not locals\n\t\tif (prinst.pr_xfunction)\n\t\tfor (i = 0; i < prinst.pr_xfunction->locals; i++)\n\t\t{\n\t\t\tdef16 = ED_GlobalAtOfs16(progfuncs, prinst.pr_xfunction->parm_start+i);\n\t\t\tif (!def16)\n\t\t\t\tcontinue;\n\t\t\tif (!strcmp(def16->s_name+progfuncs->funcs.stringtable, name))\n\t\t\t{\n\t\t\t\t*val = (eval_t *)&cp->globals[prinst.pr_xfunction->parm_start+i];\n\n\t\t\t\t//we need something like this for functions that are not the top layer\n\t//\t\t\t*val = (eval_t *)&localstack[localstack_used-pr_xfunction->numparms*4];\n\t\t\t\tdef.ofs = def16->ofs;\n\t\t\t\tdef.s_name = def16->s_name;\n\t\t\t\tdef.type = def16->type;\n\t\t\t\treturn &def;\n\t\t\t}\n\t\t}\n\t\tdef16 = ED_FindGlobal16(progfuncs, name);\n\t\tif (!def16)\n\t\t\treturn NULL;\n\t\tdef.ofs = def16->ofs;\n\t\tdef.type = def16->type;\n\t\tdef.s_name = def16->s_name;\n\t\tdef32 = &def;\n\t\tbreak;\n\tcase PST_QTEST:\n\tcase PST_FTE32:\n\t\t//this gets parms fine, but not locals\n\t\tif (prinst.pr_xfunction)\n\t\tfor (i = 0; i < prinst.pr_xfunction->numparms; i++)\n\t\t{\n\t\t\tdef32 = ED_GlobalAtOfs32(progfuncs, prinst.pr_xfunction->parm_start+i);\n\t\t\tif (!def32)\n\t\t\t\tcontinue;\n\t\t\tif (!strcmp(def32->s_name+progfuncs->funcs.stringtable, name))\n\t\t\t{\n\t\t\t\t*val = (eval_t *)&cp->globals[prinst.pr_xfunction->parm_start+i];\n\n\t\t\t\t//we need something like this for functions that are not the top layer\n\t//\t\t\t*val = (eval_t *)&localstack[localstack_used-pr_xfunction->numparms*4];\n\t\t\t\treturn def32;\n\t\t\t}\n\t\t}\n\t\tdef32 = ED_FindGlobal32(progfuncs, name);\n\t\tif (!def32)\n\t\t\treturn NULL;\n\t\tbreak;\n\tdefault:\n\t\texterns->Sys_Error(\"Bad struct type in ED_FindLocalOrGlobal\");\n\t\tdef32 = NULL;\n\t}\n\n\t*val = (eval_t *)&cp->globals[def32->ofs];\n\treturn &def;\n}\n\nstatic char *TrimString(const char *str, char *buffer, int buffersize)\n{\n\tint i;\n\twhile (*str <= ' ' && *str>'\\0')\n\t\tstr++;\n\n\tfor (i = 0; i < buffersize-1; i++)\n\t{\n\t\tif (*str <= ' ')\n\t\t\tbreak;\n\t\tbuffer[i] = *str++;\n\t}\n\tbuffer[i] = '\\0';\n\treturn buffer;\n}\n\npbool LocateDebugTerm(progfuncs_t *progfuncs, const char *key, eval_t **result, etype_t *rettype, eval_t *store)\n{\n\tddef32_t *def;\n\tfdef_t *fdef;\n\tint fofs;\n\teval_t *val = NULL, *fval=NULL;\n\tchar *c, *c2;\n\tetype_t type = ev_void;\n\tstruct edictrun_s *ed;\n//\tetype_t ptrtype = ev_void;\n\n\tif (!strncmp(key, \"*(float*)\", 9))\n\t{\n\t\tfofs = strtoul(key+9, NULL, 0);\n\t\tif (fofs < 0 || fofs+3 >= prinst.addressableused)\n\t\t\treturn false;\n\t\t*result = (eval_t*)(pr_strings + fofs);\n\t\t*rettype = ev_float;\n\t\treturn true;\n\t}\n\tif (!strncmp(key, \"*(int*)\", 7))\n\t{\n\t\tfofs = strtoul(key+7, NULL, 0);\n\t\tif (fofs < 0 || fofs+3 >= prinst.addressableused)\n\t\t\treturn false;\n\t\t*result = (eval_t*)(pr_strings + fofs);\n\t\t*rettype = ev_integer;\n\t\treturn true;\n\t}\n\tif (!strncmp(key, \"*(string*)\", 7))\n\t{\n\t\tfofs = strtoul(key+7, NULL, 0);\n\t\tif (fofs < 0 || fofs+3 >= prinst.addressableused)\n\t\t\treturn false;\n\t\t*result = (eval_t*)(pr_strings + fofs);\n\t\t*rettype = ev_string;\n\t\treturn true;\n\t}\n\n\tc = strchr(key, '.');\n\tif (c) *c = '\\0';\n\tdef = ED_FindLocalOrGlobal(progfuncs, key, &val);\n\tif (!def)\n\t{\n\t\tif (*key == '\\'')\n\t\t{\n\t\t\ttype = ev_vector;\n\t\t\tval = store;\n\t\t\tval->_vector[0] = 0;\n\t\t\tval->_vector[1] = 0;\n\t\t\tval->_vector[2] = 0;\n\t\t}\n\t\telse if (*key == '\\\"')\n\t\t{\n\t\t\ttype = ev_string;\n\t\t\tval = store;\n\t\t\tval->string = 0;\n\t\t}\n\t\telse if (atoi(key))\n\t\t{\n\t\t\ttype = ev_entity;\n\t\t\tval = store;\n\t\t\tval->edict = atoi(key);\n\t\t}\n\t}\n\telse\n\t\ttype = def->type;\n\n\tif (c) *c = '.';\n\tif (!val)\n\t{\n\t\treturn false;\n\t}\n\n\t//go through ent vars\n\tc = strchr(key, '.');\n\twhile(c)\n\t{\n\t\tc2 = c+1;\n\t\tc = strchr(c2, '.');\n\t\ttype = type &~DEF_SAVEGLOBAL;\n\t\tif (current_progstate && current_progstate->types)\n\t\t\ttype = current_progstate->types[type].type;\n\t\tif (type != ev_entity)\n\t\t\treturn false;\n\t\tif (c)*c = '\\0';\n\n\t\tfdef = ED_FindField(progfuncs, c2);\n\t\tif (!fdef)\n\t\t{\n\t\t\tchar trimmed[256];\n\t\t\tc2 = TrimString(c2, trimmed, sizeof(trimmed));\n\t\t\tdef = ED_FindLocalOrGlobal(progfuncs, c2, &fval);\n\t\t\tif (def && def->type == ev_field)\n\t\t\t{\n\t\t\t\tfofs = fval->_int + progfuncs->funcs.fieldadjust;\n\t\t\t\tfdef = ED_FieldAtOfs(progfuncs, fofs);\n\t\t\t}\n\t\t}\n\n\t\tif (c)*c = '.';\n\t\tif (!fdef)\n\t\t\treturn false;\n\t\tfofs = fdef->ofs;\n\t\ttype = fdef->type;\n\n\t\tif ((unsigned int)val->_int >= prinst.maxedicts)\n\t\t\ted = NULL;\n\t\telse\n\t\t\ted = PROG_TO_EDICT_PB(progfuncs, val->_int);\n\t\tif (!ed)\n\t\t\treturn false;\n\t\tif (fofs < 0 || fofs >= (int)prinst.max_fields_size)\n\t\t\treturn false;\n\t\tval = (eval_t *) (((char *)ed->fields) + fofs*4);\n\t}\n\t*rettype = type;\n\t*result = val;\n\treturn true;\n}\n\npbool PDECL PR_SetWatchPoint(pubprogfuncs_t *ppf, const char *desc, const char *location)\n{\n\tprogfuncs_t *progfuncs = (progfuncs_t *)ppf;\n\teval_t *val;\n\teval_t fakeval;\n\tetype_t type;\n\n\tif (!location)\n\t{\n\t\tfree(prinst.watch_name);\n\t\tprinst.watch_name = NULL;\n\t\tprinst.watch_ptr = NULL;\n\t\tprinst.watch_type = ev_void;\n\t\treturn false;\n\t}\n\tif (!LocateDebugTerm(progfuncs, location, &val, &type, &fakeval))\n\t{\n\t\texterns->Printf(\"Unable to evaluate watch term \\\"%s\\\"\\n\", location);\n\t\treturn false;\n\t}\n\tif (val == &fakeval)\n\t{\n\t\texterns->Printf(\"Do you like watching paint dry?\\n\");\n\t\treturn false;\n\t}\n\tif (type == ev_vector)\n\t{\n\t\texterns->Printf(\"Unable to watch vectors. Watching the x field instead.\\n\");\n\t\ttype = ev_float;\n\t}\n\n\tfree(prinst.watch_name);\n\tprinst.watch_name = strdup(desc);\n\tprinst.watch_ptr = val;\n\tprinst.watch_old = *prinst.watch_ptr;\n\tprinst.watch_type = type &~ DEF_SAVEGLOBAL;\n\treturn true;\n}\n\nstatic const char *PR_ParseCast(const char *key, etype_t *t, pbool *isptr)\n{\n\textern char *basictypenames[];\n\tint type;\n\t*t = ev_void;\n\t*isptr = false;\n\twhile(*key == ' ')\n\t\tkey++;\n\tif (*key == '(')\n\t{\n\t\tkey++;\n\t\tfor (type = 0; type <= ev_variant; type++)\n\t\t{\n\t\t\tif (!strncmp(key, basictypenames[type], strlen(basictypenames[type])))\n\t\t\t{\n\t\t\t\tkey += strlen(basictypenames[type]);\n\t\t\t\twhile(*key == ' ')\n\t\t\t\t\tkey++;\n\t\t\t\tif (*key == '*')\n\t\t\t\t{\n\t\t\t\t\t*isptr = true;\n\t\t\t\t\tkey++;\n\t\t\t\t}\n\t\t\t\t*t = type;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (type > ev_variant)\n\t\t\treturn NULL;\n\n\t\twhile(*key == ' ')\n\t\t\tkey++;\n\t\tif (*key++ != ')')\n\t\t\treturn NULL;\n\t}\n\treturn key;\n}\nchar *PDECL PR_EvaluateDebugString(pubprogfuncs_t *ppf, const char *key)\n{\n\tprogfuncs_t *progfuncs = (progfuncs_t*)ppf;\n\tstatic char buf[8192];\n\tfdef_t *fdef;\n\teval_t *val;\n\tchar *assignment;\n\tetype_t type;\n\teval_t fakeval;\n\textern char *basictypenames[];\n\n\tif (*key == '*')\n\t{\n\t\tint ptr;\n\t\teval_t v;\n\t\tetype_t cast;\n\t\tpbool isptr;\n\t\ttype = ev_void;\n\t\tkey = PR_ParseCast(key+1, &cast, &isptr);\n\t\tif (!key || !isptr)\n\t\t\treturn \"(unable to evaluate)\";\n\t\tif (*key == '&')\n\t\t{\n\t\t\tif (!LocateDebugTerm(progfuncs, key+1, &val, &type, &fakeval) && val != &fakeval)\n\t\t\t\treturn \"(unable to evaluate)\";\n\t\t\tv._int = (char*)val - progfuncs->funcs.stringtable;\n\t\t\tval = &v;\n\t\t\ttype = ev_pointer;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (!LocateDebugTerm(progfuncs, key, &val, &type, &fakeval) && val != &fakeval)\n\t\t\t\treturn \"(unable to evaluate)\";\n\t\t}\n\t\tif (type == ev_integer || type == ev_string || type == ev_pointer)\n\t\t\tptr = val->_int;\n\t\telse if (type == ev_float)\n\t\t\tptr = val->_float;\n\t\telse\n\t\t\treturn \"(unable to evaluate)\";\n\t\treturn PR_ValueString(progfuncs, cast, (eval_t*)(progfuncs->funcs.stringtable + ptr), true);\n\t}\n\tif (*key == '&')\n\t{\n\t\tif (!LocateDebugTerm(progfuncs, key+1, &val, &type, &fakeval) && val != &fakeval)\n\t\t\treturn \"(unable to evaluate)\";\n\t\tQC_snprintfz(buf, sizeof(buf), \"(%s*)%#x\", ((type>=10)?\"???\":basictypenames[type]), (unsigned int)((char*)val - progfuncs->funcs.stringtable));\n\t\treturn buf;\n\t}\n\n\tassignment = strchr(key, '=');\n\tif (assignment)\n\t\t*assignment = '\\0';\n\n\tif (!LocateDebugTerm(progfuncs, key, &val, &type, &fakeval))\n\t\treturn \"(unable to evaluate)\";\n\n\t\t/*\n\tc = strchr(key, '.');\n\tif (c) *c = '\\0';\n\tdef = ED_FindLocalOrGlobal(progfuncs, key, &val);\n\tif (!def)\n\t{\n\t\tif (atoi(key))\n\t\t{\n\t\t\tdef = &fakedef;\n\t\t\tdef->ofs = 0;\n\t\t\tdef->type = ev_entity;\n\t\t\tval = &fakeval;\n\t\t\tval->edict = atoi(key);\n\t\t}\n\t}\n\tif (c) *c = '.';\n\tif (!def)\n\t{\n\t\treturn \"(Bad string)\";\n\t}\n\ttype = def->type;\n\n\t//go through ent vars\n\tc = strchr(key, '.');\n\twhile(c)\n\t{\n\t\tc2 = c+1;\n\t\tc = strchr(c2, '.');\n\t\ttype = type &~DEF_SAVEGLOBAL;\n\t\tif (current_progstate && current_progstate->types)\n\t\t\ttype = current_progstate->types[type].type;\n\t\tif (type != ev_entity)\n\t\t\treturn \"'.' without entity\";\n\t\tif (c)*c = '\\0';\n\t\tfdef = ED_FindField(progfuncs, TrimString(c2));\n\t\tif (c)*c = '.';\n\t\tif (!fdef)\n\t\t\treturn \"(Bad string)\";\n\t\ted = PROG_TO_EDICT(progfuncs, val->_int);\n\t\tif (!ed)\n\t\t\treturn \"(Invalid Entity)\";\n\t\tval = (eval_t *) (((char *)ed->fields) + fdef->ofs*4);\n\t\ttype = fdef->type;\n\t}\n*/\n\tif (assignment)\n\t{\n\t\tchar *str = assignment+1;\n\t\twhile(*str == ' ')\n\t\t\tstr++;\n\t\tswitch (type&~DEF_SAVEGLOBAL)\n\t\t{\n\t\tcase ev_string:\n#ifdef QCGC\n\t\t\t*(string_t *)val = PR_AllocTempString(&progfuncs->funcs, str);\n#else\n\t\t\t*(string_t *)val = PR_StringToProgs(&progfuncs->funcs, ED_NewString (&progfuncs->funcs, assignment, 0, true));\n#endif\n\t\t\tbreak;\n\n\t\tcase ev_float:\n\t\t\tif (str[0] == '0' && (str[1] == 'x' || str[1] == 'X'))\n\t\t\t\t*(float*)val = strtoul(str, NULL, 0);\n\t\t\telse\n\t\t\t\t*(float *)val = (float)atof (str);\n\t\t\tbreak;\n\n\t\tcase ev_integer:\n\t\t\t*(int *)val = atoi (str);\n\t\t\tbreak;\n\n\t\tcase ev_vector:\n\t\t\t{\n\t\t\t\tint i;\n\t\t\t\tif (*str == '\\'')\n\t\t\t\t\tstr++;\n\t\t\t\tfor (i = 0; i < 3; i++)\n\t\t\t\t{\n\t\t\t\t\twhile(*str == ' ' || *str == '\\t')\n\t\t\t\t\t\tstr++;\n\t\t\t\t\t((float *)val)[i] = strtod(str, &str);\n\t\t\t\t}\n\t\t\t\twhile(*str == ' ' || *str == '\\t')\n\t\t\t\t\tstr++;\n\t\t\t\tif (*str == '\\'')\n\t\t\t\t\tstr++;\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase ev_entity:\n\t\t\tif (!EDICT_NUM(progfuncs, atoi (str)))\n\t\t\t\treturn \"(invalid entity)\";\n\t\t\t*(int *)val = EDICT_TO_PROG(progfuncs, EDICT_NUM(progfuncs, atoi (str)));\n\t\t\tbreak;\n\n\t\tcase ev_field:\n\t\t\tfdef = ED_FindField (progfuncs, str);\n\t\t\tif (!fdef)\n\t\t\t{\n\t\t\t\tsize_t l,nl = strlen(str);\n\n\t\t\t\t*assignment = '=';\n\n\t\t\t\tstrcpy(buf, \"Can't find field \");\n\t\t\t\tl = strlen(buf);\n\t\t\t\tif (nl > sizeof(buf)-l-2)\n\t\t\t\t\tnl = sizeof(buf)-l-2;\n\t\t\t\tmemcpy(buf+l, str, nl);\n\t\t\t\tbuf[l+nl+1] = 0;\n\t\t\t\treturn buf;\n\t\t\t}\n\t\t\t*(int *)val = G_INT(fdef->ofs);\n\t\t\tbreak;\n\n\t\tcase ev_function:\n\t\t\t{\n\t\t\t\tmfunction_t *func;\n\t\t\t\tprogsnum_t i;\n\t\t\t\tprogsnum_t progsnum = -1;\n\t\t\t\tchar *end;\n\t\t\t\tif (!strcmp(str, \"0\"))\n\t\t\t\t{\n\t\t\t\t\t*(func_t *)val = 0;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tprogsnum = strtol(str, &end, 10);\n\t\t\t\tif (end != str && *end == ':')\n\t\t\t\t\tstr = end+1;\t//skip past the num: prefix\n\t\t\t\telse\n\t\t\t\t\tprogsnum = -1;\t//wasn't a num: prefix\n\n\t\t\t\tfunc = ED_FindFunction (progfuncs, str, &i, progsnum);\n\t\t\t\tif (!func)\n\t\t\t\t{\n\t\t\t\t\tsize_t l,nl = strlen(str);\n\n\t\t\t\t\t*assignment = '=';\n\n\t\t\t\t\tstrcpy(buf, \"Can't find function \");\n\t\t\t\t\tl = strlen(buf);\n\t\t\t\t\tif (nl > sizeof(buf)-l-2)\n\t\t\t\t\t\tnl = sizeof(buf)-l-2;\n\t\t\t\t\tmemcpy(buf+l, str, nl);\n\t\t\t\t\tbuf[l+nl+1] = 0;\n\t\t\t\t\treturn buf;\n\t\t\t\t}\n\t\t\t\t*(func_t *)val = (func - pr_progstate[i].functions) | (i<<24);\n\t\t\t}\n\t\t\tbreak;\n\n\t\tdefault:\n\t\t\tbreak;\n\n\t\t}\n\t\t*assignment = '=';\n\t}\n\tQC_snprintfz(buf, sizeof(buf), \"%s\", PR_ValueString(progfuncs, type, val, true));\n\n\treturn buf;\n}\n\n//int EditorHighlightLine(window_t *wnd, int line);\nvoid SetExecutionToLine(progfuncs_t *progfuncs, int linenum)\n{\n\tint pn = prinst.pr_typecurrent;\n\tint snum;\n\tconst mfunction_t *f = prinst.pr_xfunction;\n\n\tswitch(current_progstate->structtype)\n\t{\n\tcase PST_DEFAULT:\n\tcase PST_QTEST:\n\t\tfor (snum = f->first_statement; pr_progstate[pn].linenums[snum] < linenum; snum++)\n\t\t{\n\t\t\tif (pr_statements16[snum].op == OP_DONE)\n\t\t\t\treturn;\n\t\t}\n\t\tbreak;\n\tcase PST_KKQWSV:\n\tcase PST_FTE32:\n\t\tfor (snum = f->first_statement; pr_progstate[pn].linenums[snum] < linenum; snum++)\n\t\t{\n\t\t\tif (pr_statements32[snum].op == OP_DONE)\n\t\t\t\treturn;\n\t\t}\n\t\tbreak;\n\tdefault:\n\t\texterns->Sys_Error(\"Bad struct type\");\n\t\tsnum = 0;\n\t}\n\tprinst.debugstatement = snum;\n//\tEditorHighlightLine(editwnd, pr_progstate[pn].linenums[snum]);\n}\n\nstruct sortedfunc_s\n{\n\tint firststatement;\n\tint firstline;\n};\nint PDECL PR_SortBreakFunctions(const void *va, const void *vb)\n{\n\tconst struct sortedfunc_s *a = va;\n\tconst struct sortedfunc_s *b = vb;\n\tif (a->firstline == b->firstline)\n\t\treturn 0;\n\treturn a->firstline > b->firstline;\n}\n\n//0 clear. 1 set, 2 toggle, 3 check\nint PDECL PR_ToggleBreakpoint(pubprogfuncs_t *ppf, const char *filename, int linenum, int flag)\t//write alternate route to work by function name.\n{\n\tprogfuncs_t *progfuncs = (progfuncs_t*)ppf;\n\tint ret=0;\n\tunsigned int fl, stline;\n\tunsigned int i, j;\n\n\tprogstate_t *cp;\n\tmfunction_t *f;\n\tint op = 0; //warning about not being initialized before use\n\n\tif (!pr_progstate)\n\t\treturn ret;\n\n\tfor (j = 0; j < prinst.maxprogs; j++)\n\t{\n\t\tcp = &pr_progstate[j];\n\t\tif (!cp->progs)\n\t\t\tcontinue;\n\n\t\tif (linenum)\t//linenum is set means to set the breakpoint on a file and line\n\t\t{\n\t\t\tstruct sortedfunc_s *sortedstatements;\n\t\t\tint numfilefunctions = 0;\n\t\t\tif (!cp->linenums)\n\t\t\t\tcontinue;\n\t\t\tsortedstatements = alloca(cp->progs->numfunctions * sizeof(*sortedstatements));\n\n\t\t\t//we need to use the function table in order to set breakpoints in the right file.\n\t\t\tfor (f = cp->functions, fl = 0; fl < cp->progs->numfunctions; f++, fl++)\n\t\t\t{\n\t\t\t\tconst char *fncfile = f->s_file+progfuncs->funcs.stringtable;\n\t\t\t\tif (fncfile[0] == '.' && fncfile[1] == '/')\n\t\t\t\t\tfncfile+=2;\n\t\t\t\tif (!stricmp(fncfile, filename))\n\t\t\t\t{\n\t\t\t\t\tsortedstatements[numfilefunctions].firststatement = f->first_statement;\n\t\t\t\t\tif (f->first_statement < 0 || f->first_statement >= (int)cp->progs->numstatements)\n\t\t\t\t\t\tsortedstatements[numfilefunctions].firstline = 0;\n\t\t\t\t\telse\n\t\t\t\t\t\tsortedstatements[numfilefunctions].firstline = cp->linenums[f->first_statement];\n\t\t\t\t\tnumfilefunctions++;\n\t\t\t\t}\n\t\t\t}\n\t\t\tf = NULL;\n\t\t\tqsort(sortedstatements, numfilefunctions, sizeof(*sortedstatements), PR_SortBreakFunctions);\n\n\t\t\t//our functions are now in terms of ascending line numbers.\n\t\t\tfor (fl = 0; fl < numfilefunctions; fl++)\n\t\t\t{\n\t\t\t\tfor (i = sortedstatements[fl].firststatement; i < cp->progs->numstatements; i++)\n\t\t\t\t{\n\t\t\t\t\tif (cp->linenums[i] >= linenum)\n\t\t\t\t\t{\n\t\t\t\t\t\tstline = cp->linenums[i];\n\t\t\t\t\t\tfor (; ; i++)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif ((unsigned int)cp->linenums[i] != stline)\n\t\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\t\tswitch(cp->structtype)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase PST_DEFAULT:\n\t\t\t\t\t\t\tcase PST_QTEST:\n\t\t\t\t\t\t\t\top = ((dstatement16_t*)cp->statements + i)->op;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tcase PST_KKQWSV:\n\t\t\t\t\t\t\tcase PST_FTE32:\n\t\t\t\t\t\t\tcase PST_UHEXEN2:\n\t\t\t\t\t\t\t\top = ((dstatement32_t*)cp->statements + i)->op;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\t\texterns->Sys_Error(\"Bad structtype\");\n\t\t\t\t\t\t\t\top = 0;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tswitch (flag)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\t\tif (op & OP_BIT_BREAKPOINT)\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\top &= ~OP_BIT_BREAKPOINT;\n\t\t\t\t\t\t\t\t\tret = false;\n\t\t\t\t\t\t\t\t\tflag = 0;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\top |= OP_BIT_BREAKPOINT;\n\t\t\t\t\t\t\t\t\tret = true;\n\t\t\t\t\t\t\t\t\tflag = 1;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tcase 0:\n\t\t\t\t\t\t\t\top &= ~OP_BIT_BREAKPOINT;\n\t\t\t\t\t\t\t\tret = false;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tcase 1:\n\t\t\t\t\t\t\t\top |= OP_BIT_BREAKPOINT;\n\t\t\t\t\t\t\t\tret = true;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tcase 3:\n\t\t\t\t\t\t\t\tif (op & OP_BIT_BREAKPOINT)\n\t\t\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tswitch(cp->structtype)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase PST_DEFAULT:\n\t\t\t\t\t\t\tcase PST_QTEST:\n\t\t\t\t\t\t\t\t((dstatement16_t*)cp->statements + i)->op = op;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tcase PST_KKQWSV:\n\t\t\t\t\t\t\tcase PST_FTE32:\n\t\t\t\t\t\t\tcase PST_UHEXEN2:\n\t\t\t\t\t\t\t\t((dstatement32_t*)cp->statements + i)->op = op;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\t\texterns->Sys_Error(\"Bad structtype\");\n\t\t\t\t\t\t\t\top = 0;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (ret)\t//if its set, only set one breakpoint statement, not all of them.\n\t\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\t\tif ((op & ~OP_BIT_BREAKPOINT) == OP_DONE)\n\t\t\t\t\t\t\t\tbreak;\t//give up when we see the function's done.\n\t\t\t\t\t\t}\n\t\t\t\t\t\tgoto cont;\t//next progs\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse\t//set the breakpoint on the first statement of the function specified.\n\t\t{\n\t\t\tfor (f = cp->functions, fl = 0; fl < cp->progs->numfunctions; f++, fl++)\n\t\t\t{\n\t\t\t\tif (!strcmp(f->s_name+progfuncs->funcs.stringtable, filename))\n\t\t\t\t{\n\t\t\t\t\ti = f->first_statement;\n\t\t\t\t\tswitch(cp->structtype)\n\t\t\t\t\t{\n\t\t\t\t\tcase PST_DEFAULT:\n\t\t\t\t\tcase PST_QTEST:\n\t\t\t\t\t\top = ((dstatement16_t*)cp->statements + i)->op;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase PST_KKQWSV:\n\t\t\t\t\tcase PST_FTE32:\n\t\t\t\t\tcase PST_UHEXEN2:\n\t\t\t\t\t\top = ((dstatement32_t*)cp->statements + i)->op;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\texterns->Sys_Error(\"Bad structtype\");\n\t\t\t\t\t}\n\t\t\t\t\tswitch (flag)\n\t\t\t\t\t{\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tif (op & 0x8000)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\top &= ~0x8000;\n\t\t\t\t\t\t\tret = false;\n\t\t\t\t\t\t\tflag = 0;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\top |= 0x8000;\n\t\t\t\t\t\t\tret = true;\n\t\t\t\t\t\t\tflag = 1;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\top &= ~0x8000;\n\t\t\t\t\t\tret = false;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\top |= 0x8000;\n\t\t\t\t\t\tret = true;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 3:\n\t\t\t\t\t\tif (op & 0x8000)\n\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t\tswitch(cp->structtype)\n\t\t\t\t\t{\n\t\t\t\t\tcase PST_DEFAULT:\n\t\t\t\t\tcase PST_QTEST:\n\t\t\t\t\t\t((dstatement16_t*)cp->statements + i)->op = op;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase PST_KKQWSV:\n\t\t\t\t\tcase PST_FTE32:\n\t\t\t\t\tcase PST_UHEXEN2:\n\t\t\t\t\t\t((dstatement32_t*)cp->statements + i)->op = op;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\texterns->Sys_Error(\"Bad structtype\");\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\ncont:\n\t\tcontinue;\n\t}\n\n\treturn ret;\n}\n\nint ShowStep(progfuncs_t *progfuncs, int statement, char *fault, pbool fatal)\n{\n//FIXME: statics are evil, but at least the lastfile pointer check _should_ isolate different vms.\nstatic unsigned int lastline = 0;\nstatic unsigned int ignorestatement = 0;\nstatic const char *lastfile = NULL;\n\tconst char *file = NULL;\n\n\tint pn = prinst.pr_typecurrent;\n\tint i;\n\tconst mfunction_t *f = prinst.pr_xfunction;\n\tint faultline;\n\tint debugaction;\n\tprinst.pr_xstatement = statement;\n\n\tif (!externs->useeditor)\n\t{\n\t\tPR_PrintStatement(progfuncs, statement);\n\t\tif (fatal)\n\t\t{\n\t\t\tprogfuncs->funcs.debug_trace = DEBUG_TRACE_ABORTERROR;\n\t\t\tprogfuncs->funcs.parms->Abort (\"%s\", fault?fault:\"Debugger Abort\");\n\t\t}\n\t\treturn statement;\n\t}\n\n\tif (f)\n\t{\n\t\tfor(;;)\t//for DEBUG_TRACE_NORESUME handling\n\t\t{\n\t\t\tfile = PR_StringToNative(&progfuncs->funcs, f->s_file);\n\t\t\tif (pr_progstate[pn].linenums)\n\t\t\t{\n\t\t\t\tif (lastline == pr_progstate[pn].linenums[statement] && lastfile == file && statement == ignorestatement && !fault)\n\t\t\t\t{\n\t\t\t\t\tignorestatement++;\n\t\t\t\t\treturn statement;\t//no info/same line as last time\n\t\t\t\t}\n\n\t\t\t\tlastline = pr_progstate[pn].linenums[statement];\n\t\t\t}\n\t\t\telse\n\t\t\t\tlastline = -1;\n\t\t\tlastfile = file;\n\n\t\t\tfaultline = lastline;\n\t\t\tdebugaction = externs->useeditor(&progfuncs->funcs, lastfile, ((lastline!=-1)?&lastline:NULL), &statement, f->first_statement, fault, fatal);\n\n//\t\t\tif (pn != prinst.pr_typecurrent)\n\n\t\t\t//if they changed the line to execute, we need to find a statement that is on that line\n\t\t\tif (lastline && faultline != lastline)\n\t\t\tif (pr_progstate[pn].linenums)\n\t\t\t{\n\t\t\t\tswitch(pr_progstate[pn].structtype)\n\t\t\t\t{\n\t\t\t\tcase PST_UHEXEN2:\n\t\t\t\tcase PST_FTE32:\n\t\t\t\tcase PST_KKQWSV:\n\t\t\t\t\t{\n\t\t\t\t\t\tdstatement32_t *st = pr_progstate[pn].statements;\n\t\t\t\t\t\tunsigned int *lnos = pr_progstate[pn].linenums;\n\t\t\t\t\t\tfor (i = f->first_statement; ; i++)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (lastline == lnos[i])\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tstatement = i;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse if (lastline <= lnos[i])\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\telse if (st[i].op == OP_DONE)\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase PST_DEFAULT:\n\t\t\t\tcase PST_QTEST:\n\t\t\t\t\t{\n\t\t\t\t\t\tdstatement16_t *st = pr_progstate[pn].statements;\n\t\t\t\t\t\tunsigned int *lnos = pr_progstate[pn].linenums;\n\t\t\t\t\t\tfor (i = f->first_statement; ; i++)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (lastline == lnos[i])\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tstatement = i;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse if (lastline <= lnos[i])\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\telse if (st[i].op == OP_DONE)\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (debugaction == DEBUG_TRACE_NORESUME)\n\t\t\t\tcontinue;\n\t\t\telse if(debugaction == DEBUG_TRACE_ABORTERROR)\n\t\t\t\tprogfuncs->funcs.parms->Abort (\"%s\", fault?fault:\"Debugger Abort\");\n\t\t\telse if (debugaction == DEBUG_TRACE_OFF)\n\t\t\t{\n\t\t\t\t//if we're resuming, don't hit any lingering step-over triggers\n\t\t\t\tprogfuncs->funcs.debug_trace = DEBUG_TRACE_OFF;\n\t\t\t\tfor (i = 0; i < prinst.pr_depth; i++)\n\t\t\t\t\tprinst.pr_stack[prinst.pr_depth-1].stepping = DEBUG_TRACE_OFF;\n\t\t\t}\n\t\t\telse if (debugaction == DEBUG_TRACE_OUT)\n\t\t\t{\n\t\t\t\t//clear tracing for now, but ensure that it'll be reactivated once we reach the caller (if from qc)\n\t\t\t\tprogfuncs->funcs.debug_trace = DEBUG_TRACE_OFF;\n\t\t\t\tif (prinst.pr_depth)\n\t\t\t\t\tprinst.pr_stack[prinst.pr_depth-1].stepping = DEBUG_TRACE_INTO;\n\t\t\t}\n\t\t\telse\t//some other debug action. maybe resume.\n\t\t\t\tprogfuncs->funcs.debug_trace = debugaction;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tignorestatement = statement+1;\n\treturn statement;\n}\n\n//called by the qcvm when executing some statement that cannot be execed.\nint PR_HandleFault (pubprogfuncs_t *ppf, char *error, ...)\n{\n\tprogfuncs_t *progfuncs = (progfuncs_t *)ppf;\n\tva_list\t\targptr;\n\tchar\t\tstring[1024];\n\tint resumestatement;\n\n\tva_start (argptr,error);\n\tQ_vsnprintf (string,sizeof(string)-1, error,argptr);\n\tva_end (argptr);\n\n\tPR_StackTrace (ppf, true);\n\tppf->parms->Printf (\"%s\\n\", string);\n\n\tresumestatement = ShowStep(progfuncs, prinst.pr_xstatement, string, true);\n\n\tif (resumestatement == 0)\n\t{\n\t\tPR_AbortStack(ppf);\n\t\treturn -1;\n//\t\tppf->parms->Abort (\"%s\", string);\n\t}\n\treturn resumestatement;\n}\n\n/*\n============\nPR_RunError\n\nAborts the currently executing function\n============\n*/\nvoid VARGS PR_RunError (pubprogfuncs_t *progfuncs, const char *error, ...)\n{\n\tva_list\t\targptr;\n\tchar\t\tstring[1024];\n\n\tva_start (argptr,error);\n\tQ_vsnprintf (string,sizeof(string)-1, error,argptr);\n\tva_end (argptr);\n\n//\tPR_PrintStatement (pr_statements + pr_xstatement);\n\tPR_StackTrace (progfuncs, true);\n\tprogfuncs->parms->Printf (\"\\n\");\n\n//editbadfile(pr_strings + pr_xfunction->s_file, -1);\n\n\tprogfuncs->parms->Abort (\"%s\", string);\n}\n\npbool PR_RunWarning (pubprogfuncs_t *ppf, char *error, ...)\n{\n\tprogfuncs_t *progfuncs = (progfuncs_t *)ppf;\n\tva_list\t\targptr;\n\tchar\t\tstring[1024];\n\n\tva_start (argptr,error);\n\tQ_vsnprintf (string,sizeof(string)-1, error,argptr);\n\tva_end (argptr);\n\n\tprogfuncs->funcs.parms->Printf (\"%s\", string);\n\tif (prinst.pr_depth != 0)\n\t\tPR_StackTrace (ppf, false);\n\n\tif (progfuncs->funcs.debug_trace == 0)\n\t{\n\t\tprogfuncs->funcs.debug_trace = DEBUG_TRACE_INTO;\n\t\treturn true;\n\t}\n\treturn false;\n}\nstatic pbool PR_ExecRunWarning (pubprogfuncs_t *ppf, int xstatement, char *error, ...)\n{\n\tprogfuncs_t *progfuncs = (progfuncs_t *)ppf;\n\tva_list\t\targptr;\n\tchar\t\tstring[1024];\n\n\tprinst.pr_xstatement = xstatement;\n\n\tva_start (argptr,error);\n\tQ_vsnprintf (string,sizeof(string)-1, error,argptr);\n\tva_end (argptr);\n\n\tprogfuncs->funcs.parms->Printf (\"%s\", string);\n\tif (prinst.pr_depth != 0)\n\t\tPR_StackTrace (ppf, false);\n\n\tif (progfuncs->funcs.debug_trace == DEBUG_TRACE_OFF)\n\t{\n\t\tprinst.pr_xstatement = ShowStep(progfuncs, xstatement, string, false);\n\t\tif (progfuncs->funcs.debug_trace != DEBUG_TRACE_OFF)\n\t\t\treturn true;\n\t}\n\treturn false;\n}\n\n//For debugging. Assumes classname field exists.\nconst char *PR_GetEdictClassname(progfuncs_t *progfuncs, unsigned int edict)\n{\n\tfdef_t *cnfd = ED_FindField(progfuncs, \"classname\");\n\tif (cnfd && edict < prinst.maxedicts)\n\t{\n\t\tstring_t *v = (string_t *)((char *)edvars(PROG_TO_EDICT_PB(progfuncs, edict)) + cnfd->ofs*4);\n\t\treturn PR_StringToNative(&progfuncs->funcs, *v);\n\t}\n\treturn \"\";\n}\n\n\nstatic pbool casecmp_f(progfuncs_t *progfuncs, eval_t *ref, eval_t *val)\t{return ref->_float == val->_float;}\nstatic pbool casecmp_i(progfuncs_t *progfuncs, eval_t *ref, eval_t *val)\t{return ref->_int == val->_int;}\nstatic pbool casecmp_v(progfuncs_t *progfuncs, eval_t *ref, eval_t *val)\t{return ref->_vector[0] == val->_vector[0] && \n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tref->_vector[1] == val->_vector[1] &&\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tref->_vector[2] == val->_vector[2];}\nstatic pbool casecmp_s(progfuncs_t *progfuncs, eval_t *ref, eval_t *val)\t{\tconst char *refs = PR_StringToNative(&progfuncs->funcs, ref->string);\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tconst char *vals = PR_StringToNative(&progfuncs->funcs, val->string);\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\treturn !strcmp(refs, vals);}\nstatic pbool casecmprange_f(progfuncs_t *progfuncs, eval_t *ref, eval_t *min, eval_t *max)\t{return ref->_float >= min->_float && ref->_float <= max->_float;}\nstatic pbool casecmprange_i(progfuncs_t *progfuncs, eval_t *ref, eval_t *min, eval_t *max)\t{return ref->_int >= min->_int && ref->_int <= max->_int;}\nstatic pbool casecmprange_v(progfuncs_t *progfuncs, eval_t *ref, eval_t *min, eval_t *max)\t{return ref->_vector[0] >= min->_vector[0] && ref->_vector[0] <= max->_vector[0] &&\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tref->_vector[1] >= min->_vector[1] && ref->_vector[1] <= max->_vector[1] &&\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tref->_vector[2] >= min->_vector[2] && ref->_vector[2] <= max->_vector[2];}\nstatic pbool casecmprange_bad(progfuncs_t *progfuncs, eval_t *ref, eval_t *min, eval_t *max){\tPR_RunError (&progfuncs->funcs, \"OP_CASERANGE type not supported\");//BUG: pr_xstatement will not be correct.\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\treturn false;}\ntypedef pbool (*casecmp_t)(progfuncs_t *progfuncs, eval_t *ref, eval_t *val);\ntypedef pbool (*casecmprange_t)(progfuncs_t *progfuncs, eval_t *ref, eval_t *min, eval_t *max);\nstatic casecmp_t casecmp[] =\n{\n\tcasecmp_f,\t//float\n\tcasecmp_v,\t//vector\n\tcasecmp_s,\t//string\n\tcasecmp_i,\t//ent\n\tcasecmp_i\t//func\n\t//pointer, field, int, etc are emulated with func or something. I dunno\n};\nstatic casecmprange_t casecmprange[] =\n{\n\tcasecmprange_f,\t//float\n\tcasecmprange_v,\t//vector - I'm using a bbox, not really sure what it should be\n\tcasecmprange_bad,\t//string - should it use stof? string ranges don't relly make sense, at all.\n\tcasecmprange_i,\t//ent - doesn't really make sense, but as ints/pointers/fields/etc might be emulated with this, allow it anyway, as an int type.\n\tcasecmprange_i\t//func\n};\n\n#define RUNAWAYCHECK()\t\t\t\t\t\t\t\t\\\n\tif (!--*runaway)\t\t\t\t\t\t\t\t\\\n\t{\t\t\t\t\t\t\t\t\t\t\t\t\\\n\t\tprinst.pr_xstatement = st-pr_statements;\t\\\n\t\tPR_RunError (&progfuncs->funcs, \"runaway loop error\\n\");\\\n\t\tPR_StackTrace(&progfuncs->funcs,false);\t\t\\\n\t\texterns->Printf (\"runaway loop error\\n\");\t\\\n\t\twhile(prinst.pr_depth > prinst.exitdepth)\t\\\n\t\t\tPR_LeaveFunction(progfuncs);\t\t\t\\\n\t\tprinst.spushed = 0;\t\t\t\t\t\t\t\\\n\t\treturn -1;\t\t\t\t\t\t\t\t\t\\\n\t}\n\n#if defined(SIMPLE_QCVM)\nstatic int PR_NoDebugVM(progfuncs_t *fte_restrict progfuncs)\n{\n\tchar stack[4*1024];\n\tsize_t ofs;\n\tstrcpy(stack, \"This platform does not support QC debugging\\nStack Trace:\");\n\tofs = strlen(stack);\n\tPR_SaveCallStack (progfuncs, stack, &ofs, sizeof(stack));\n\tPR_RunError (&progfuncs->funcs, \"%s\", stack);\n\treturn -1;\n}\n#endif\n\nstatic int PR_ExecuteCode16 (progfuncs_t *fte_restrict progfuncs, int s, int *fte_restrict runaway)\n{\n\tunsigned int switchcomparison = 0;\n\tconst dstatement16_t\t*fte_restrict st;\n\tmfunction_t\t*fte_restrict newf;\n\tint\t\ti;\n\tedictrun_t\t*ed;\n\teval_t\t*ptr;\n\n\tfloat *fte_restrict glob = pr_globals;\n\tfloat tmpf;\n\tint tmpi;\n\tunsigned short op;\n\n\teval_t\t*switchref = (eval_t*)glob;\n\tunsigned int num_edicts = sv_num_edicts;\n\n#define OPA ((eval_t *)&glob[st->a])\n#define OPB ((eval_t *)&glob[st->b])\n#define OPC ((eval_t *)&glob[st->c])\n\n#define INTSIZE 16\n\tst = &pr_statements16[s];\n\twhile (progfuncs->funcs.debug_trace || prinst.watch_ptr || prinst.profiling)\n\t{\n#if defined(SIMPLE_QCVM)\n\t\treeval16:\n\t\t//this can generate huge functions, so disable it on systems that can't realiably cope with such things (IE initiates an unwanted denial-of-service attack when pointed our javascript, and firefox prints a warning too)\n\t\tprinst.pr_xstatement = st-pr_statements16;\n\t\treturn PR_NoDebugVM(progfuncs);\n#else\n\t\t#define DEBUGABLE\n\t\t#ifdef SEPARATEINCLUDES\n\t\t\t#include \"execloop16d.h\"\n\t\t#else\n\t\t\t#include \"execloop.h\"\n\t\t#endif\n\t\t#undef DEBUGABLE\n#endif\n\t}\n\n\twhile(1)\n\t{\n\t\t#include \"execloop.h\"\n\t}\n#undef INTSIZE\n}\n\nstatic int PR_ExecuteCode32 (progfuncs_t *fte_restrict progfuncs, int s, int *fte_restrict runaway)\n{\n#if defined(SIMPLE_QCVM)\n\t//this can generate huge functions, so disable it on systems that can't realiably cope with such things (IE initiates an unwanted denial-of-service attack when pointed our javascript, and firefox prints a warning too)\n\tprinst.pr_xstatement = s;\n\tPR_RunError (&progfuncs->funcs, \"32bit qc statement support was disabled for this platform.\\n\");\n\tPR_StackTrace(&progfuncs->funcs, false);\n\treturn -1;\n#else\n\n\tunsigned int switchcomparison = 0;\n\tconst dstatement32_t\t*fte_restrict st;\n\tmfunction_t\t*fte_restrict newf;\n\tint\t\ti;\n\tedictrun_t\t*ed;\n\teval_t\t*ptr;\n\n\tfloat *fte_restrict glob = pr_globals;\n\tfloat tmpf;\n\tint tmpi;\n\teval_t\t*switchref = (eval_t*)glob;\n\tunsigned int num_edicts = sv_num_edicts;\n\n\tunsigned int op;\n\n#define OPA ((eval_t *)&glob[st->a])\n#define OPB ((eval_t *)&glob[st->b])\n#define OPC ((eval_t *)&glob[st->c])\n\n#define INTSIZE 32\n\tst = &pr_statements32[s];\n\twhile (progfuncs->funcs.debug_trace || prinst.watch_ptr || prinst.profiling)\n\t{\n\t\t#define DEBUGABLE\n\t\t#ifdef SEPARATEINCLUDES\n\t\t\t#include \"execloop32d.h\"\n\t\t#else\n\t\t\t#include \"execloop.h\"\n\t\t#endif\n\t\t#undef DEBUGABLE\n\t}\n\n\twhile(1)\n\t{\n\t\t#ifdef SEPARATEINCLUDES\n\t\t\t#include \"execloop32.h\"\n\t\t#else\n\t\t\t#include \"execloop.h\"\n\t\t#endif\n\t}\n#undef INTSIZE\n#endif\n}\n\n/*\n====================\nPR_ExecuteProgram\n====================\n*/\nstatic void PR_ExecuteCode (progfuncs_t *progfuncs, int s)\n{\n\tint\t\trunaway;\n\n\tif (prinst.watch_ptr && prinst.watch_ptr->_int != prinst.watch_old._int)\n\t{\n\t\tswitch(prinst.watch_type)\n\t\t{\n\t\tcase ev_float:\n\t\t\texterns->Printf(\"Watch point \\\"%s\\\" changed by engine from %g to %g.\\n\", prinst.watch_name, prinst.watch_old._float, prinst.watch_ptr->_float);\n\t\t\tbreak;\n\t\tcase ev_vector:\n\t\t\texterns->Printf(\"Watch point \\\"%s\\\" changed by engine from '%g %g %g' to '%g %g %g'.\\n\", prinst.watch_name, prinst.watch_old._vector[0], prinst.watch_old._vector[1], prinst.watch_old._vector[2], prinst.watch_ptr->_vector[0], prinst.watch_ptr->_vector[1], prinst.watch_ptr->_vector[2]);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\texterns->Printf(\"Watch point \\\"%s\\\" changed by engine from %i to %i.\\n\", prinst.watch_name, prinst.watch_old._int, prinst.watch_ptr->_int);\n\t\t\tbreak;\n\t\tcase ev_entity:\n\t\t\texterns->Printf(\"Watch point \\\"%s\\\" changed by engine from %i(%s) to %i(%s).\\n\", prinst.watch_name, prinst.watch_old._int, PR_GetEdictClassname(progfuncs, prinst.watch_old._int), prinst.watch_ptr->_int, PR_GetEdictClassname(progfuncs, prinst.watch_ptr->_int));\n\t\t\tbreak;\n\t\tcase ev_function:\n\t\tcase ev_string:\n\t\t\texterns->Printf(\"Watch point \\\"%s\\\" set by engine to %s.\\n\", prinst.watch_name, PR_ValueString(progfuncs, prinst.watch_type, prinst.watch_ptr, false));\n\t\t\tbreak;\n\t\t}\n\t\tprinst.watch_old = *prinst.watch_ptr;\n\n\t\t//we can't dump stack or anything, as we don't really know the stack frame that it happened in.\n\n\t\t//stop watching\n//\t\tprinst->watch_ptr = NULL;\n\t}\n\n#ifdef QCJIT\n\tif (current_progstate->jit)\n\t{\n\t\tPR_EnterJIT(progfuncs, current_progstate->jit, s);\n\t\treturn;\n\t}\n#endif\n\n\trunaway = 100000000;\n\n\tfor(;;)\n\t{\n\t\tswitch (current_progstate->structtype)\n\t\t{\n\t\tcase PST_DEFAULT:\n\t\tcase PST_QTEST:\n\t\t\ts = PR_ExecuteCode16(progfuncs, s, &runaway);\n\t\t\tif (s == -1)\n\t\t\t\treturn;\n\t\t\tcontinue;\n\t\tcase PST_KKQWSV:\n\t\tcase PST_FTE32:\n\t\tcase PST_UHEXEN2:\n\t\t\ts = PR_ExecuteCode32(progfuncs, s, &runaway);\n\t\t\tif (s == -1)\n\t\t\t\treturn;\n\t\t\tcontinue;\n\t\tdefault:\n\t\t\texterns->Sys_Error(\"PR_ExecuteProgram - bad structtype\");\n\t\t}\n\t}\n}\n\n#if defined(__GNUC__) && defined(__GLIBC__)\n#define __USE_GNU\n#include <dlfcn.h>\n#endif\n\nvoid PDECL PR_ExecuteProgram (pubprogfuncs_t *ppf, func_t fnum)\n{\n\tprogfuncs_t *progfuncs = (progfuncs_t*)ppf;\n\tmfunction_t\t*f;\n\tint\t\ti;\n\tunsigned int initial_progs;\n\tint\t\toldexitdepth;\n\n\tint s;\n\n#ifndef QCGC\n\tint tempdepth;\n#endif\n\n\tunsigned int newprogs = (fnum & 0xff000000)>>24;\n\n\tinitial_progs = prinst.pr_typecurrent;\n\tif (newprogs != initial_progs)\n\t{\n\t\tif (newprogs >= prinst.maxprogs || !pr_progstate[newprogs].globals)\t//can happen with hexen2...\n\t\t{\n\t\t\texterns->Printf(\"PR_ExecuteProgram: tried branching into invalid progs (%#\"pPRIx\")\\n\", fnum);\n\t\t\treturn;\n\t\t}\n\t\tPR_SwitchProgsParms(progfuncs, newprogs);\n\t}\n\n\tif (!(fnum & ~0xff000000) || (signed)(fnum & ~0xff000000) >= pr_progs->numfunctions)\n\t{\n//\t\tif (pr_global_struct->self)\n//\t\t\tED_Print (PROG_TO_EDICT(pr_global_struct->self));\n#if defined(__GNUC__) && defined(__GLIBC__)\n\t\tDl_info info;\n\t\tvoid *caller = __builtin_return_address(0);\n\t\tdladdr(caller, &info);\n\t\texterns->Printf(\"PR_ExecuteProgram: NULL function from %s+%p(%s)\\n\", info.dli_fname, (void*)((intptr_t)caller - (intptr_t)info.dli_fbase), info.dli_sname?info.dli_sname:\"<function not known>\");\n#elif defined(__GNUC__) && !defined(FTE_TARGET_WEB)\n\t\texterns->Printf(\"PR_ExecuteProgram: NULL function from exe (address %p)\\n\", __builtin_return_address(0));\n#else\n\t\texterns->Printf(\"PR_ExecuteProgram: NULL function from exe\\n\");\n#endif\n//\t\tHost_Error (\"PR_ExecuteProgram: NULL function from exe\");\n\n//\t\tPR_MoveParms(0, pr_typecurrent);\n\t\tPR_SwitchProgs(progfuncs, initial_progs);\n\t\treturn;\n\t}\n\n\toldexitdepth = prinst.exitdepth;\n\n\tf = &pr_cp_functions[fnum & ~0xff000000];\n\n\tif (f->first_statement < 0)\n\t{\t// negative statements are built in functions\n\t\ti = -f->first_statement;\n\n\t\tif (i < externs->numglobalbuiltins)\n\t\t\t(*externs->globalbuiltins[i]) (&progfuncs->funcs, (struct globalvars_s *)current_progstate->globals);\n\t\telse\n\t\t{\n\t\t\texterns->Printf (\"Bad builtin call number %i (from exe)\\n\", -f->first_statement);\n\t\t//\tPR_MoveParms(p, pr_typecurrent);\n\t\t\tPR_SwitchProgs(progfuncs, initial_progs);\n\t\t}\n\t\tPR_SwitchProgsParms(progfuncs, initial_progs);\n\t\treturn;\n\t}\n\n\t//forget about any tracing if its active. control returning to the engine should not look like its calling some random function.\n\tprogfuncs->funcs.debug_trace = DEBUG_TRACE_OFF;\n\n// make a stack frame\n\tprinst.exitdepth = prinst.pr_depth;\n\n\n\ts = PR_EnterFunction (progfuncs, f, initial_progs);\n\n#ifndef QCGC\n\ttempdepth = prinst.numtempstringsstack;\n#endif\n\tPR_ExecuteCode(progfuncs, s);\n\n\n\tPR_SwitchProgsParms(progfuncs, initial_progs);\n\n#ifndef QCGC\n\tPR_FreeTemps(progfuncs, tempdepth);\n\tprinst.numtempstringsstack = tempdepth;\n#else\n\tif (!prinst.pr_depth)\n\t\tPR_RunGC(progfuncs);\n#endif\n\n\tprinst.exitdepth = oldexitdepth;\n}\n\n\n\n\n\n\ntypedef struct {\n\tint fnum;\n\tint progsnum;\n\tint statement;\n\tint spushed;\n} qcthreadstack_t;\ntypedef struct qcthread_s {\n\tint fstackdepth;\n\tqcthreadstack_t fstack[MAX_STACK_DEPTH];\n\tint lstackused;\n\tint lstack[LOCALSTACK_SIZE];\n} qcthread_t;\n\nstruct qcthread_s *PDECL PR_ForkStack(pubprogfuncs_t *ppf)\n{\t//QC code can call builtins that call qc code.\n\t//to get around the problems of restoring the builtins we simply don't save the thread over the builtin.\n\t//this may be an error when OP_PUSH has been used.\n\n\tprogfuncs_t *progfuncs = (progfuncs_t*)ppf;\n\tint i, pushed;\n\tint ed = prinst.exitdepth;\n\tint localsoffset, baselocalsoffset;\n\tqcthread_t *thread = externs->memalloc(sizeof(qcthread_t));\n\tconst mfunction_t *f;\n\tint curprogs = ppf->callprogs;\n\n\t//notes:\n\t//pr_stack[prinst.exitdepth] is a dummy entry, with null function refs. we don't care about it.\n\t//pr_stack[pr_depth] is technically invalid but logically required - it refers to the current function instead. stoopid extra indirection.\n\t//[pr_depth] is the qc function that called whichever builtin we're executing right now so we want that (logical) one. [0] is irrelevant though.\n\t//entering a function copys its locals into the local stack for restoration on return, any OP_PUSHED stuff within the frame is then after that.\n\t//OP_PUSH/'pushed' is the PARENT function's pushes. f->locals stuff is the CHILD function's pushes.\n\n\t//copy out the functions stack.\n\tfor (i = 1,localsoffset=0; i <= ed; i++)\n\t{\n\t\tif (i == prinst.pr_depth)\n\t\t{\n\t\t\tlocalsoffset += prinst.spushed;\n\n\t\t\tf = prinst.pr_xfunction;\n\t\t\tlocalsoffset += f->locals;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tlocalsoffset += prinst.pr_stack[i].pushed;\n\n\t\t\tf = prinst.pr_stack[i].f;\n\t\t\tif (f)\n\t\t\t\tlocalsoffset += f->locals;\n\t\t}\n\t}\n\t//now we can start copying the stack.\n\tbaselocalsoffset = localsoffset;\n\tthread->fstackdepth = 0;\n\tfor (; i <= prinst.pr_depth; i++)\n\t{\n\t\tif (i == prinst.pr_depth)\n\t\t{\t//top of the stack. whichever function called the builtin we're executing.\n\t\t\tthread->fstack[thread->fstackdepth].fnum = prinst.pr_xfunction - pr_progstate[curprogs].functions;\n\t\t\tthread->fstack[thread->fstackdepth].progsnum = curprogs;\n\t\t\tthread->fstack[thread->fstackdepth].statement = prinst.pr_xstatement;\n\t\t\tthread->fstack[thread->fstackdepth].spushed = prinst.spushed;\n\t\t\tthread->fstackdepth++;\n\n\t\t\tlocalsoffset += prinst.spushed;\n\t\t\tf = prinst.pr_xfunction;\n\t\t\tlocalsoffset += f->locals;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tthread->fstack[thread->fstackdepth].fnum = prinst.pr_stack[i].f - pr_progstate[prinst.pr_stack[i].progsnum].functions;\n\t\t\tthread->fstack[thread->fstackdepth].progsnum = prinst.pr_stack[i].progsnum;\n\t\t\tthread->fstack[thread->fstackdepth].statement = prinst.pr_stack[i].s;\n\t\t\tthread->fstack[thread->fstackdepth].spushed = prinst.pr_stack[i].pushed;\n\t\t\tthread->fstackdepth++;\n\n\t\t\tlocalsoffset += prinst.pr_stack[i].pushed;\n\t\t\tf = prinst.pr_stack[i].f;\n\t\t\tlocalsoffset += f->locals;\n\t\t}\n\t}\n\n\t//we now know how many locals we need... but life is not easy.\n\t//preserving on entry means the definitive location is the pr_globals - and they'll have been 'corrupted' by any child functions.\n\t//so we need to unwind(rewind) them here to find their proper 'current' values, so that resumption can rebuild the execution stack on resume to revert them properly to their prior values. messy.\n\tfor (i = prinst.pr_depth; i > ed ; i--)\n\t{\n\t\tif (i == prinst.pr_depth)\n\t\t\tf = prinst.pr_xfunction,\tpushed = prinst.spushed;\n\t\telse\n\t\t\tf = prinst.pr_stack[i].f,\tpushed = prinst.pr_stack[i].pushed;\n\n\t\t//preseve any OP_PUSH stuff\n\t\tlocalsoffset -= pushed;\n\t\tmemcpy(&thread->lstack[localsoffset-baselocalsoffset],  prinst.localstack+localsoffset, pushed*sizeof(int));\n\n\t\t//preserve the current locals\n\t\tlocalsoffset -= f->locals;\n\t\tmemcpy(&thread->lstack[localsoffset-baselocalsoffset], ((int *)pr_globals)+f->parm_start, f->locals*sizeof(int));\n\t\t//unwind the locals so parent functions we preserve have the proper current values.\n\t\tmemcpy(((int *)pr_globals)+f->parm_start, prinst.localstack+localsoffset, f->locals*sizeof(int));\n\t}\n\n\t//rewind the locals so they don't get corrupt when returning from fork etc.\n\tfor (i = ed+1; i <= prinst.pr_depth ; i++)\t//we need to get the locals back to how they were.\n\t{\n\t\tif (i == prinst.pr_depth)\n\t\t\tf = prinst.pr_xfunction,\tpushed = prinst.spushed;\n\t\telse\n\t\t\tf = prinst.pr_stack[i].f,\tpushed = prinst.pr_stack[i].pushed;\n\n\t\tmemcpy(((int *)pr_globals)+f->parm_start, &thread->lstack[localsoffset-baselocalsoffset], f->locals*sizeof(int));\n\t\tlocalsoffset += f->locals;\n\n\t\tlocalsoffset += pushed;\t//we didn't need to clobber this, just skip it to avoid corrupting.\n\t}\n\tthread->lstackused = localsoffset - baselocalsoffset;\n\n\treturn thread;\n}\n\nvoid PDECL PR_ResumeThread (pubprogfuncs_t *ppf, struct qcthread_s *thread)\n{\n\tprogfuncs_t *progfuncs = (progfuncs_t*)ppf;\n\tmfunction_t\t*f;\n\tint\t\ti;\n\tprogsnum_t initial_progs = prinst.pr_typecurrent;\n\tunsigned int initial_stack;\n\tint\t\toldexitdepth;\n\tint\t\t*glob;\n\n#ifndef QCGC\n\tint tempdepth;\n#endif\n\n\tif (prinst.localstack_used + thread->lstackused > LOCALSTACK_SIZE)\n\t\tPR_RunError(&progfuncs->funcs, \"Too many locals on resumtion of QC thread\\n\");\n\n\tif (prinst.pr_depth + thread->fstackdepth > MAX_STACK_DEPTH)\n\t\tPR_RunError(&progfuncs->funcs, \"Too large stack on resumtion of QC thread\\n\");\n\n\n\t//do progs switching stuff as appropriate. (fteqw only)\n\n\toldexitdepth = prinst.exitdepth;\n\tprinst.exitdepth = prinst.pr_depth;\n\n\tinitial_stack = prinst.localstack_used;\n\t//add on the callstack.\n\tfor (i = 0; i < thread->fstackdepth; i++)\n\t{\n\t\t//make the new stack frame from the working values so stuff gets restored properly...\n\t\tprinst.pr_stack[prinst.pr_depth].f = prinst.pr_xfunction;\n\t\tprinst.pr_stack[prinst.pr_depth].s = prinst.pr_xstatement;\n\t\tprinst.pr_stack[prinst.pr_depth].progsnum = prinst.pr_typecurrent;\n\t\tprinst.pr_stack[prinst.pr_depth].pushed = prinst.spushed;\n\t\tprinst.pr_depth++;\n\n\t\t//restore the OP_PUSH data\n\t\tmemcpy(&prinst.localstack[prinst.localstack_used], &thread->lstack[prinst.localstack_used-initial_stack], prinst.spushed*sizeof(int));\n\t\tprinst.localstack_used += prinst.spushed;\n\n\t\t//and refill the working values from the inbound stack\n\t\tPR_SwitchProgs(progfuncs, thread->fstack[i].progsnum);\n\t\tprinst.pr_xfunction = pr_progstate[thread->fstack[i].progsnum].functions + thread->fstack[i].fnum;\n\t\tprinst.pr_xstatement = thread->fstack[i].statement;\n\t\tprinst.spushed = thread->fstack[i].spushed;\n\n\t\tf = pr_progstate[thread->fstack[i].progsnum].functions + thread->fstack[i].fnum;\n\t\tglob = (int*)pr_progstate[thread->fstack[i].progsnum].globals;\n\n\t\t//copy the 'new' function's current globals into the local stack for restoration\n\t\tmemcpy(&prinst.localstack[prinst.localstack_used], &glob[f->parm_start], sizeof(int)*f->locals);\n\t\t//and overwrite them with the saved values.\n\t\tmemcpy(&glob[f->parm_start], &thread->lstack[prinst.localstack_used-initial_stack], sizeof(int)*f->locals);\n\t\tprinst.localstack_used += f->locals;\n\t}\n\n\tif (prinst.localstack_used-initial_stack != thread->lstackused)\n\t\tPR_RunError(&progfuncs->funcs, \"Thread stores incorrect locals count\\n\");\n\n#ifndef QCGC\n\ttempdepth = prinst.numtempstringsstack;\n#endif\n\tPR_ExecuteCode(progfuncs, prinst.pr_xstatement);\n\n\n\tPR_SwitchProgsParms(progfuncs, initial_progs); //just in case\n#ifndef QCGC\n\tPR_FreeTemps(progfuncs, tempdepth);\n\tprinst.numtempstringsstack = tempdepth;\n#endif\n\n\tprinst.exitdepth = oldexitdepth;\n}\n\nvoid\tPDECL PR_AbortStack\t\t\t(pubprogfuncs_t *ppf)\n{\n\tprogfuncs_t *progfuncs = (progfuncs_t*)ppf;\n\twhile(prinst.pr_depth > prinst.exitdepth)\n\t\tPR_LeaveFunction(progfuncs);\n\tprinst.pr_xstatement = -1;\t//should make the loop abort.\n}\n\npbool\tPDECL PR_GetBuiltinCallInfo\t(pubprogfuncs_t *ppf, int *builtinnum, char *function, size_t sizeoffunction)\n{\n\tprogfuncs_t *progfuncs = (progfuncs_t*)ppf;\n\tint st = prinst.pr_xstatement;\n\tint op;\n\tint a;\n\tconst char *fname;\n\t\n\tswitch (current_progstate->structtype)\n\t{\n\tcase PST_DEFAULT:\n\tcase PST_QTEST:\n\t\top = pr_statements16[st].op;\n\t\ta = pr_statements16[st].a;\n\t\tbreak;\n\tcase PST_KKQWSV:\n\tcase PST_FTE32:\n\t\top = pr_statements32[st].op;\n\t\ta = pr_statements32[st].a;\n\t\tbreak;\n\tdefault:\n\t\top = OP_DONE;\n\t\ta = 0;\n\t\tbreak;\n\t}\n\n\t*builtinnum = 0;\n\t*function = 0;\n\tif ((op >= OP_CALL0 && op <= OP_CALL8) || (op >= OP_CALL1H && op <= OP_CALL8H))\n\t{\n\t\ta = ((eval_t *)&pr_globals[a])->function;\n\n\t\t*builtinnum = -current_progstate->functions[a].first_statement;\n\t\tfname = PR_StringToNative(ppf, current_progstate->functions[a].s_name);\n\t\tstrncpy(function, fname, sizeoffunction-1);\n\t\tfunction[sizeoffunction-1] = 0;\n\t\treturn true;\n\t}\n\treturn false;\n}\n"
  },
  {
    "path": "engine/qclib/pr_multi.c",
    "content": "#define PROGSUSED\n#include \"progsint.h\"\n\n//#define MAPPING_DEBUG\n//#define MAPPING_PARANOID\t//may actually break unions, so beware.\n\n/*\nprogstate_t *pr_progstate;\nprogsnum_t pr_typecurrent;\nint maxprogs;\n\nprogstate_t *current_progstate;\nint numshares;\n\nsharedvar_t *shares;\t//shared globals, not including parms\nint maxshares;\n*/\n\n//switches progs without preserving parms/ret/shared\npbool PR_SwitchProgs(progfuncs_t *progfuncs, progsnum_t type)\n{\t\n\tif ((unsigned)type >= prinst.maxprogs)\n\t{\n\t\tif (type == -1)\n\t\t{\n\t\t\tprinst.pr_typecurrent = -1;\n\t\t\tcurrent_progstate = NULL;\n\t\t\treturn true;\n\t\t}\n\t\tPR_RunError(&progfuncs->funcs, \"QCLIB: Bad prog type - %\"pPRIi, type);\n//\t\tSys_Error(\"Bad prog type - %i\", type);\n\t}\n\n\tif (pr_progstate[(unsigned)type].progs == NULL)\t//we havn't loaded it yet, for some reason\n\t\treturn false;\t\n\n\tcurrent_progstate = &pr_progstate[(unsigned)type];\n\n\tprinst.pr_typecurrent = type;\n\n\treturn true;\n}\n\n//switch to new progs, preserving all arguments. oldpr should be 'pr_typecurrent'\npbool PR_SwitchProgsParms(progfuncs_t *progfuncs, progsnum_t newpr)\t//from 2 to 1\n{\n\tunsigned int a;\n\tprogstate_t *np;\n\tprogstate_t *op;\n\tprogsnum_t oldpr = prinst.pr_typecurrent;\n\n\tif (newpr == oldpr)\n\t{\n\t\t//don't bother coping variables to themselves...\n\t\treturn true;\n\t}\n\n\tnp = &pr_progstate[(int)newpr];\n\top = &pr_progstate[(int)oldpr];\n\n\tif ((unsigned)newpr >= prinst.maxprogs || !np->globals)\n\t{\n\t\texterns->Printf(\"QCLIB: Bad prog type - %\"pPRIi, newpr);\n\t\treturn false;\n\t}\n\tif ((unsigned)oldpr >= prinst.maxprogs || !op->globals)\t//startup?\n\t\treturn PR_SwitchProgs(progfuncs, newpr);\n\n\t//copy parms.\n\tfor (a = 0; a < MAX_PARMS;a++)\n\t{\n\t\t*(int *)&np->globals[OFS_PARM0+3*a  ] = *(int *)&op->globals[OFS_PARM0+3*a  ];\n\t\t*(int *)&np->globals[OFS_PARM0+3*a+1] = *(int *)&op->globals[OFS_PARM0+3*a+1];\n\t\t*(int *)&np->globals[OFS_PARM0+3*a+2] = *(int *)&op->globals[OFS_PARM0+3*a+2];\n\t}\n\tnp->globals[OFS_RETURN] = op->globals[OFS_RETURN];\n\tnp->globals[OFS_RETURN+1] = op->globals[OFS_RETURN+1];\n\tnp->globals[OFS_RETURN+2] = op->globals[OFS_RETURN+2];\n\n\t//move the vars defined as shared.\n\tfor (a = 0; a < prinst.numshares; a++)//fixme: make offset per progs\n\t{\n\t\tmemmove(&((int *)np->globals)[prinst.shares[a].varofs], &((int *)op->globals)[prinst.shares[a].varofs], prinst.shares[a].size*4);\n/*\t\t((int *)p1->globals)[shares[a].varofs] = ((int *)p2->globals)[shares[a].varofs];\n\t\tif (shares[a].size > 1)\n\t\t{\n\t\t\t((int *)p1->globals)[shares[a].varofs+1] = ((int *)p2->globals)[shares[a].varofs+1];\n\t\t\tif (shares[a].size > 2)\n\t\t\t\t((int *)p1->globals)[shares[a].varofs+2] = ((int *)p2->globals)[shares[a].varofs+2];\n\t\t}\n*/\n\t}\n\treturn PR_SwitchProgs(progfuncs, newpr);\n}\n\nprogsnum_t PDECL PR_LoadProgs(pubprogfuncs_t *ppf, const char *s)\n{\n\tprogfuncs_t *progfuncs = (progfuncs_t*)ppf;\n\tunsigned int a;\n\tprogsnum_t oldtype;\n\toldtype = prinst.pr_typecurrent;\t\n\tfor (a = 0; a < prinst.maxprogs; a++)\n\t{\n\t\tif (pr_progstate[a].progs == NULL)\n\t\t{\n\t\t\tprinst.pr_typecurrent = a;\n\t\t\tcurrent_progstate = &pr_progstate[a];\n\t\t\tif (PR_ReallyLoadProgs(progfuncs, s, &pr_progstate[a], false))\t//try and load it\t\t\t\n\t\t\t{\n\t\t\t\tif (a <= progfuncs->funcs.numprogs)\n\t\t\t\t\tprogfuncs->funcs.numprogs = a+1;\n\n#ifdef QCJIT\n\t\t\t\tcurrent_progstate->jit = PR_GenerateJit(progfuncs);\n#endif\n\t\t\t\tif (oldtype != -1)\n\t\t\t\t\tPR_SwitchProgs(progfuncs, oldtype);\n\t\t\t\treturn a;\t//we could load it. Yay!\n\t\t\t}\n\t\t\tPR_SwitchProgs(progfuncs, oldtype);\n\t\t\treturn -1; // loading failed.\n\t\t}\n\t}\n\tPR_SwitchProgs(progfuncs, oldtype);\n\treturn -1;\n}\n\nvoid PR_ShiftParms(progfuncs_t *progfuncs, int amount)\n{\n\tint a;\n\tfor (a = 0; a < MAX_PARMS - amount;a++)\n\t\t*(int *)&pr_globals[OFS_PARM0+3*a] = *(int *)&pr_globals[OFS_PARM0+3*(amount+a)];\n}\n\n//forget a progs\nvoid PR_Clear(progfuncs_t *progfuncs)\n{\n\tunsigned int a;\n\tfor (a = 0; a < prinst.maxprogs; a++)\n\t{\n#ifdef QCJIT\n\t\tif (pr_progstate[a].jit)\n\t\t\tPR_CloseJit(pr_progstate[a].jit);\n#endif\n\t\tpr_progstate[a].progs = NULL;\n\t}\n}\n\n\n\nvoid QC_StartShares(progfuncs_t *progfuncs)\n{\n\tprinst.numshares = 0;\n\tprinst.maxshares = 32;\n\tif (prinst.shares)\n\t\texterns->memfree(prinst.shares);\n\tprinst.shares = externs->memalloc(sizeof(sharedvar_t)*prinst.maxshares);\n}\nvoid PDECL QC_AddSharedVar(pubprogfuncs_t *ppf, int start, int size)\t//fixme: make offset per progs and optional\n{\n\tprogfuncs_t *progfuncs = (progfuncs_t*)ppf;\n\tint ofs;\n\tunsigned int a;\n\n\tif (prinst.numshares >= prinst.maxshares)\n\t{\n\t\tvoid *buf;\n\t\tbuf = prinst.shares;\n\t\tprinst.maxshares += 16;\t\t\n\t\tprinst.shares = externs->memalloc(sizeof(sharedvar_t)*prinst.maxshares);\n\n\t\tmemcpy(prinst.shares, buf, sizeof(sharedvar_t)*prinst.numshares);\n\n\t\texterns->memfree(buf);\n\t}\n\tofs = start;\n\tfor (a = 0; a < prinst.numshares; a++)\n\t{\n\t\tif (prinst.shares[a].varofs+prinst.shares[a].size == ofs)\n\t\t{\n\t\t\tprinst.shares[a].size += size;\t//expand size.\n\t\t\treturn;\n\t\t}\n\t\tif (prinst.shares[a].varofs == start)\n\t\t\treturn;\n\t}\n\n\n\tprinst.shares[prinst.numshares].varofs = start;\n\tprinst.shares[prinst.numshares].size = size;\n\tprinst.numshares++;\n}\n\n\n//void ShowWatch(void);\n\nvoid QC_InitShares(progfuncs_t *progfuncs)\n{\n//\tShowWatch();\n\tif (!prinst.field)\t//don't make it so we will just need to remalloc everything\n\t{\n\t\tprinst.maxfields = 64;\n\t\tprinst.field = externs->memalloc(sizeof(fdef_t) * prinst.maxfields);\n\t}\n\n\tprinst.numfields = 0;\n\tprogfuncs->funcs.fieldadjust = 0;\n}\n\nvoid QC_FlushProgsOffsets(progfuncs_t *progfuncs)\n{\t//sets the fields up for loading a new progs.\n\t//fields are matched by name to other progs\n\t//not by offset\n\tunsigned int i;\n\tfor (i = 0; i < prinst.numfields; i++)\n\t\tprinst.field[i].progsofs = -1;\n}\n\n\n//called if a global is defined as a field\n//returns offset.\n\n//vectors must be added before any of their corresponding _x/y/z vars\n//in this way, even screwed up progs work.\n\n//requestedpos is the offset the engine WILL put it at.\n//origionaloffs is used to track matching field offsets. fields with the same progs offset overlap\n\n//note: we probably suffer from progs with renamed system globals.\nint PDECL QC_RegisterFieldVar(pubprogfuncs_t *ppf, unsigned int type, const char *name, signed long engineofs, signed long progsofs)\n{\n\tprogfuncs_t *progfuncs = (progfuncs_t*)ppf;\n//\tprogstate_t *p;\n//\tint pnum;\n\tunsigned int i;\n\tint namelen;\n\tint ofs;\n\n\tint fnum;\n\n\tif (!name)\t//engine can use this to offset all progs fields\n\t{\t\t\t//which fixes constant field offsets (some ktpro arrays)\n\t\tif (engineofs == 2)\n\t\t\tprinst.reorganisefields = 2;\n\t\telse if (engineofs)\n\t\t{\n\t\t\tprogfuncs->funcs.fieldadjust = prinst.fields_size/sizeof(pvec_t);\n#ifdef MAPPING_DEBUG\n\t\t\texterns->Printf(\"FIELD ADJUST: %i %i %i\\n\", progfuncs->funcs.fieldadjust, prinst.fields_size, (int)prinst.fields_size/4);\n#endif\n\t\t}\n\t\treturn 0;\n\t}\n\telse if (!prinst.reorganisefields)\n\t\tprinst.reorganisefields = true;\n\n\t//look for an existing match\n\tfor (i = 0; i < prinst.numfields; i++)\n\t{\t\t\n\t\tif (!strcmp(name, prinst.field[i].name))\n\t\t{\n\t\t\tif (prinst.field[i].type != type)\n\t\t\t{\n\t\t\t\t/*Hexen2/DP compat hack: if the new type is a float and the original type is a vector, make the new def alias to the engine's _x field\n\t\t\t\tthis 'works around' the unused .vector color field used for rtlight colours vs the .float color used for particle colours (the float initialisers in map files will expand into the x slot safely).\n\t\t\t\tqc/hc can work around this by just using .vector color/color_x instead, which is the same as this hack, but would resolve defs to allow rtlight colours.\n\t\t\t\t*/\n\t\t\t\tif (prinst.field[i].type != ev_vector || type != ev_float)\n\t\t\t\t{\n\t\t\t\t\tif (prinst.field[i].type == ev_string && type == ev_float && !strcmp(name, \"message\"))\n\t\t\t\t\t\t;\t//hexen2 uses floats here instead of strings.\n\t\t\t\t\telse\n\t\t\t\t\t\texterns->Printf(\"Field type mismatch on \\\"%s\\\". %i != %i\\n\", name, prinst.field[i].type, type);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!progfuncs->funcs.fieldadjust && engineofs>=0)\n\t\t\t\tif ((unsigned)engineofs/4 != prinst.field[i].ofs)\n\t\t\t\t\texterns->Sys_Error(\"Field %s at wrong offset\", name);\n\n\t\t\tif (prinst.field[i].progsofs == -1)\n\t\t\t\tprinst.field[i].progsofs = progsofs;\n#ifdef MAPPING_DEBUG\n\t\t\texterns->Printf(\"Dupfield %s %i -> %i\\n\", name, prinst.field[i].progsofs,prinst.field[i].ofs);\n#endif\n\t\t\treturn prinst.field[i].ofs-progfuncs->funcs.fieldadjust;\t//got a match\n\t\t}\n\t}\n\n\tif (prinst.numfields+1>prinst.maxfields)\n\t{\n\t\tfdef_t *nf;\n\t\ti = prinst.maxfields;\n\t\tprinst.maxfields += 32;\n\t\tnf = externs->memalloc(sizeof(fdef_t) * prinst.maxfields);\n\t\tmemcpy(nf, prinst.field, sizeof(fdef_t) * i);\n\t\texterns->memfree(prinst.field);\n\t\tprinst.field = nf;\n\t}\n\n\t//try to add a new one\n\tfnum = prinst.numfields;\n\tprinst.numfields++;\n\tprinst.field[fnum].name = name;\t\n\tprinst.field[fnum].type = type;\n\tprinst.field[fnum].progsofs = progsofs;\n\tif (engineofs >= 0)\n\t{\t//the engine is setting up a list of required field indexes.\n\n\t\t//paranoid checking of the offset.\n#if 0//def MAPPING_PARANOID\n\t\tfor (i = 0; i < numfields-1; i++)\n\t\t{\n\t\t\tif (field[i].ofs == ((unsigned)engineofs)/4)\n\t\t\t{\n\t\t\t\tif (type == ev_float && field[i].type == ev_vector)\t//check names\n\t\t\t\t{\n\t\t\t\t\tif (strncmp(field[i].name, name, strlen(field[i].name)))\n\t\t\t\t\t\tSys_Error(\"Duplicated offset\");\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tSys_Error(\"Duplicated offset\");\n\t\t\t}\n\t\t}\n#endif\n\t\tif (engineofs&3)\n\t\t\texterns->Sys_Error(\"field %s is %i&3\", name, (int)engineofs);\n\t\tprinst.field[fnum].ofs = ofs = engineofs/4;\n\t}\n\telse\n\t{\t//we just found a new fieldname inside a progs\n\t\tprinst.field[fnum].ofs = ofs = prinst.fields_size/sizeof(pvec_t);\t//add on the end\n\n\t\t//if the progs field offset matches another offset in the same progs, make it match up with the earlier one.\n\t\tif (progsofs>=0)\n\t\t{\n\t\t\tunsigned otherofs;\n\t\t\tfor (i = 0; i < prinst.numfields-1; i++)\n\t\t\t{\n\t\t\t\totherofs = prinst.field[i].progsofs;\n\t\t\t\tif (otherofs == -1)\t//qc unions work purely by progs offsets, not by engine offsets.\n\t\t\t\t\tcontinue;\t\t//this is because there really is no reliable mapping between progs and engine, so don't get confused.\n\t\t\t\tif (otherofs == (unsigned)progsofs)\n\t\t\t\t{\n#ifdef MAPPING_DEBUG\n\t\t\t\t\texterns->Printf(\"union(%s) \", prinst.field[i].name);\n#endif\n\t\t\t\t\tprinst.field[fnum].ofs = ofs = prinst.field[i].ofs;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tif (prinst.field[i].type == ev_vector && otherofs+1 == (unsigned)progsofs)\n\t\t\t\t{\n#ifdef MAPPING_DEBUG\n\t\t\t\t\texterns->Printf(\"union(%s) \", prinst.field[i].name);\n#endif\n\t\t\t\t\tprinst.field[fnum].ofs = ofs = prinst.field[i].ofs+1;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tif (prinst.field[i].type == ev_vector && otherofs+2 == (unsigned)progsofs)\n\t\t\t\t{\n#ifdef MAPPING_DEBUG\n\t\t\t\t\texterns->Printf(\"union(%s) \", prinst.field[i].name);\n#endif\n\t\t\t\t\tprinst.field[fnum].ofs = ofs = prinst.field[i].ofs+2;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n//\tif (type != ev_vector)\n\t\tif (prinst.fields_size < (ofs+type_size[type])*sizeof(pvec_t))\n\t\t{\n\t\t\tprinst.fields_size = (ofs+type_size[type])*sizeof(pvec_t);\n\t\t\tprogfuncs->funcs.activefieldslots = prinst.fields_size/sizeof(pvec_t);\n\t\t}\n\n\tif (prinst.max_fields_size && prinst.fields_size > prinst.max_fields_size)\n\t\texterns->Sys_Error(\"Allocated too many additional fields after ents were inited.\");\n\n#ifdef MAPPING_DEBUG\n\texterns->Printf(\"Field %s %i -> %i\\n\", name, prinst.field[fnum].progsofs,prinst.field[fnum].ofs);\n#endif\n\n\tif (type == ev_vector)\n\t{\n\t\t//vectors define float fields too. this avoids issues if the mod has (pointless) anti-decompile field reordering/stripping that works thanks to decompilers expecting things to be ordered exactly..\n\t\tchar *n;\t\t\n\t\tnamelen = strlen(name)+5;\t\n\n\t\tn=PRHunkAlloc(progfuncs, namelen, \"str\");\n\t\tsprintf(n, \"%s_x\", name);\n\t\tofs = QC_RegisterFieldVar(&progfuncs->funcs, ev_float, n, engineofs, progsofs);\n\n\t\tn=PRHunkAlloc(progfuncs, namelen, \"str\");\n\t\tsprintf(n, \"%s_y\", name);\n\t\tQC_RegisterFieldVar(&progfuncs->funcs, ev_float, n, (engineofs==-1)?-1:(engineofs+4), (progsofs==-1)?-1:progsofs+1);\n\n\t\tn=PRHunkAlloc(progfuncs, namelen, \"str\");\n\t\tsprintf(n, \"%s_z\", name);\n\t\tQC_RegisterFieldVar(&progfuncs->funcs, ev_float, n, (engineofs==-1)?-1:(engineofs+8), (progsofs==-1)?-1:progsofs+2);\n\t}\n\t\n\t//we've finished setting the structure\t\n\treturn ofs - progfuncs->funcs.fieldadjust;\n}\n\n\n//called for each global defined as a field\nvoid PDECL QC_AddSharedFieldVar(pubprogfuncs_t *ppf, int num, char *stringtable)\n{\n\tprogfuncs_t *progfuncs = (progfuncs_t*)ppf;\n//\tprogstate_t *p;\n//\tint pnum;\n\tunsigned int i, o;\n\n\t//look for an existing match not needed, cos we look a little later too.\n\t/*\n\tfor (i = 0; i < numfields; i++)\n\t{\t\t\n\t\tif (!strcmp(pr_globaldefs[num].s_name, field[i].s_name))\n\t\t{\n\t\t\t//really we should look for a field def\n\n\t\t\t*(int *)&pr_globals[pr_globaldefs[num].ofs] = field[i].ofs;\t//got a match\n\n\t\t\treturn;\n\t\t}\n\t}\n\t*/\n\t\n\tswitch(current_progstate->structtype)\n\t{\n\tcase PST_KKQWSV:\n\tcase PST_DEFAULT:\n\t\t{\n\t\t\tddef16_t *gd = pr_globaldefs16;\n\t\t\tddef16_t *fld = pr_fielddefs16;\n\t\t\tchar *gname = gd[num].s_name+stringtable;\n\t\t\tint *eval = (int*)&pr_globals[gd[num].ofs];\n\t\t\tif (*gname == '.')\n\t\t\t\tgname++;\n\n\t\t\tfor (i=1 ; i<pr_progs->numfielddefs; i++)\n\t\t\t{\n\t\t\t\tif (!strcmp(fld[i].s_name+stringtable, gname))\n\t\t\t\t{\t\t\n#ifdef MAPPING_DEBUG\n\t\t\t\t\tint old = *eval;\n#endif\n\t\t\t\t\t*eval = QC_RegisterFieldVar(&progfuncs->funcs, fld[i].type, gname, -1, *eval);\n#ifdef MAPPING_DEBUG\n\t\t\t\t\texterns->Printf(\"Field=%s global %i -> %i\\n\", gd[num].s_name+stringtable, old, *eval);\n#endif\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tfor (i = 0; i < prinst.numfields; i++)\n\t\t\t{\n\t\t\t\to = prinst.field[i].progsofs;\n\t\t\t\tif (o == *eval)\n\t\t\t\t{\n#ifdef MAPPING_DEBUG\n\t\t\t\t\tint old = *eval;\n#endif\n\t\t\t\t\t*eval = prinst.field[i].ofs-progfuncs->funcs.fieldadjust;\n#ifdef MAPPING_DEBUG\n\t\t\t\t\texterns->Printf(\"Field global=%s %i -> %i\\n\", gname, old, *eval);\n#endif\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t//oh well, must be a parameter.\n//\t\t\tif (*(int *)&pr_globals[pr_globaldefs16[num].ofs])\n//\t\t\t\tSys_Error(\"QCLIB: Global field var with no matching field \\\"%s\\\", from offset %i\", pr_globaldefs16[num].s_name+stringtable, *(int *)&pr_globals[pr_globaldefs16[num].ofs]);\n\t\t}\n\t\treturn;\n\tcase PST_FTE32:\n\tcase PST_QTEST:\n\tcase PST_UHEXEN2:\n\t\t{\n\t\t\tddef32_t *gd = pr_globaldefs32;\n\t\t\tddef32_t *fld = pr_fielddefs32;\n\t\t\tfor (i=1 ; i<pr_progs->numfielddefs; i++)\n\t\t\t{\n\t\t\t\tif (!strcmp(fld[i].s_name+stringtable, gd[num].s_name+stringtable))\n\t\t\t\t{\n\t\t\t\t\t*(int *)&pr_globals[gd[num].ofs] = QC_RegisterFieldVar(&progfuncs->funcs, fld[i].type, gd[num].s_name+stringtable, -1, *(int *)&pr_globals[gd[num].ofs]);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tfor (i = 0; i < prinst.numfields; i++)\n\t\t\t{\n\t\t\t\to = prinst.field[i].progsofs;\n\t\t\t\tif (o == *(unsigned int *)&pr_globals[gd[num].ofs])\n\t\t\t\t{\n\t\t\t\t\t*(int *)&pr_globals[gd[num].ofs] = prinst.field[i].ofs-progfuncs->funcs.fieldadjust;\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t//oh well, must be a parameter.\n\t\t\tif (*(int *)&pr_globals[gd[num].ofs])\n\t\t\t\texterns->Sys_Error(\"QCLIB: Global field var with no matching field \\\"%s\\\", from offset %i\", gd[num].s_name+stringtable, *(int *)&pr_globals[gd[num].ofs]);\n\t\t}\n\t\treturn;\n\tdefault:\n\t\texterns->Sys_Error(\"Bad bits\");\n\t\tbreak;\n\t}\n\texterns->Sys_Error(\"Should be unreachable\");\n}\n\nvoid QC_AddFieldGlobal(pubprogfuncs_t *ppf, int *globdata)\n{\n\tunsigned int i;\n\tint o;\n\tprogfuncs_t *progfuncs = (progfuncs_t*)ppf;\n\tfor (i = 0; i < prinst.numfields; i++)\n\t{\n\t\to = prinst.field[i].progsofs;\n\t\tif (o == *globdata)\n\t\t{\n#ifdef MAPPING_DEBUG\n\t\t\tint old = *globdata;\n#endif\n\t\t\t*globdata = prinst.field[i].ofs-progfuncs->funcs.fieldadjust;\n#ifdef MAPPING_DEBUG\n\t\t\texterns->Printf(\"Field global %i -> %i\\n\", old, *globdata);\n#endif\n\t\t\treturn;\n\t\t}\n\t}\n\texterns->Printf(\"Unable to map fieldglobal\\n\");\n}\n"
  },
  {
    "path": "engine/qclib/pr_x86.c",
    "content": "/*\r\nwhen I say JIT, I mean load time, not execution time.\r\n\r\nnotes:\r\n\tqc jump offsets are all constants. we have no variable offset jumps (other than function calls/returns)\r\n\tfield remapping... fields are in place, and cannot be adjusted. if a field is not set to 0, its assumed to be a constant.\r\n\r\noptimisations:\r\n\tnone at the moment...\r\n\tinstructions need to be chained. stuff that writes to C should be cacheable, etc. maybe we don't even need to do the write to C\r\n\tit should also be possible to fold in eq+ifnot, so none of this silly storeing of floats in equality tests\r\n\r\n\tthis means that we need to track which vars are cached and in what form: fpreg, ireg+floatasint, ireg+float.\r\n\tcertain qccx hacks can use fpu operations on ints, so do what the instruction says, rather than considering an add an add regardless of types.\r\n\r\n\tOP_AND_F, OP_OR_F etc will generally result in ints, and we should be able to keep them as ints if they combine with other ints.\r\n\r\n\tsome instructions are jump sites. any cache must be flushed before the start of the instruction.\r\n\tsome variables are locals, and will only ever be written by a single instruction, then read by the following instruction. such temps do not need to be written, or are overwritten later in the function anyway.\r\n\tsuch locals need to be calculated PER FUNCTION as (fte)qcc can overlap locals making multiple distinct locals on a single offset.\r\n\r\n\tstore locals on a proper stack instead of the current absurd mechanism.\r\n\r\n\teax - tmp\r\n\tebx - prinst->edicttable\r\n\tecx\t- tmp\r\n\tedx - tmp\r\n\tesi - debug opcode number\r\n\tedi - tmp (because its preserved by subfunctions\r\n\tebp -\r\n\r\n  to use gas to provide binary opcodes:\r\n  vim -N blob.s && as blob.s && objdump.exe -d a.out\r\n\r\n\r\n  notable mods to test:\r\n  prydon gate, due to fpu mangling to carry values between maps\r\n*/\r\n\r\n#define PROGSUSED\r\n#include \"progsint.h\"\r\n\r\n#ifdef QCJIT\r\n\r\n#ifndef _WIN32\r\n#include <sys/mman.h>\r\n#endif\r\n\r\nstatic float ta, tb, nullfloat=0;\r\n\r\nstruct jitstate\r\n{\r\n\tunsigned int *statementjumps;\t//[MAX_STATEMENTS*3]\r\n\tunsigned char **statementoffsets; //[MAX_STATEMENTS]\r\n\tunsigned int numjumps;\r\n\tunsigned char *code;\r\n\tunsigned int codesize;\r\n\tunsigned int jitstatements;\r\n\r\n\tfloat *glob;\r\n\tunsigned int cachedglobal;\r\n\tunsigned int cachereg;\r\n};\r\n\r\nstatic void Jit_EmitByte(struct jitstate *jit, unsigned char byte)\r\n{\r\n\tjit->code[jit->codesize++] = byte;\r\n}\r\nstatic void Jit_Emit4Byte(struct jitstate *jit, unsigned int value)\r\n{\r\n\tjit->code[jit->codesize++] = (value>> 0)&0xff;\r\n\tjit->code[jit->codesize++] = (value>> 8)&0xff;\r\n\tjit->code[jit->codesize++] = (value>>16)&0xff;\r\n\tjit->code[jit->codesize++] = (value>>24)&0xff;\r\n}\r\nstatic void Jit_EmitAdr(struct jitstate *jit, void *value)\r\n{\r\n\tJit_Emit4Byte(jit, (unsigned int)value);\r\n}\r\nstatic void Jit_EmitFloat(struct jitstate *jit, float value)\r\n{\r\n\tunion {float f; unsigned int i;} u;\r\n\tu.f = value;\r\n\tJit_Emit4Byte(jit, u.i);\r\n}\r\nstatic void Jit_Emit2Byte(struct jitstate *jit, unsigned short value)\r\n{\r\n\tjit->code[jit->codesize++] = (value>> 0)&0xff;\r\n\tjit->code[jit->codesize++] = (value>> 8)&0xff;\r\n}\r\n\r\nstatic void Jit_EmitFOffset(struct jitstate *jit, const void *func, int bias)\r\n{\r\n\tunion {const void *f; unsigned int i;} u;\r\n\tu.f = func;\r\n\tu.i -= (unsigned int)&jit->code[jit->codesize+bias];\r\n\tJit_Emit4Byte(jit, u.i);\r\n}\r\n\r\nstatic void Jit_Emit4ByteJump(struct jitstate *jit, int statementnum, int offset)\r\n{\r\n\tjit->statementjumps[jit->numjumps++] = jit->codesize;\r\n\tjit->statementjumps[jit->numjumps++] = statementnum;\r\n\tjit->statementjumps[jit->numjumps++] = offset;\r\n\r\n\t//the offset is filled in later\r\n\tjit->codesize += 4;\r\n}\r\n\r\n#ifdef _WIN32\r\n#undef REG_NONE\r\n#endif\r\n\r\nenum\r\n{\r\n\tREG_EAX,\r\n\tREG_ECX,\r\n\tREG_EDX,\r\n\tREG_EBX,\t//note: edicttable\r\n\tREG_ESP,\r\n\tREG_EBP,\r\n\tREG_ESI,\r\n\tREG_EDI,\r\n\r\n\t/*I'm not going to list S1 here, as that makes things too awkward*/\r\n\tREG_S0,\r\n\tREG_NONE\r\n};\r\n#define XOR(sr,dr) EmitByte(0x31);EmitByte(0xc0 | (sr<<3) | dr);\r\n#define CLEARREG(reg) XOR(reg,reg)\r\n#define LOADREG(addr, reg) if (reg == REG_EAX) {EmitByte(0xa1);} else {EmitByte(0x8b); EmitByte((reg<<3) | 0x05);} EmitAdr(addr);\r\n#define STOREREG(reg, addr) if (reg == REG_EAX) {EmitByte(0xa3);} else {EmitByte(0x89); EmitByte((reg<<3) | 0x05);} EmitAdr(addr);\r\n#define STOREF(f, addr) EmitByte(0xc7);EmitByte(0x05); EmitAdr(addr);EmitFloat(f);\r\n#define STOREI(i, addr) EmitByte(0xc7);EmitByte(0x05); EmitAdr(addr);Emit4Byte(i);\r\n#define SETREGI(val,reg) EmitByte(0xbe);Emit4Byte(val);\r\n\r\n#define ARGREGS(a,b,c)\tGCache_Load(jit, op[i].a, a, op[i].b, b, op[i].c, c)\r\n#define RESULTREG(r) GCache_Store(jit, op[i].c, r)\r\n\r\n\r\n#define EmitByte(v) Jit_EmitByte(jit, v)\r\n#define EmitAdr(v) Jit_EmitAdr(jit, v)\r\n#define EmitFOffset(a,b) Jit_EmitFOffset(jit, a, b)\r\n#define Emit4ByteJump(a,b) Jit_Emit4ByteJump(jit, a, b)\r\n#define Emit4Byte(v) Jit_Emit4Byte(jit, v)\r\n#define EmitFloat(v) Jit_EmitFloat(jit, v)\r\n\r\n#define LocalJmp(v) Jit_LocalJmp(jit, v)\r\n#define LocalLoc() Jit_LocalLoc(jit)\r\n\r\n//for the purposes of the cache, 'temp' offsets are only read when they have been written only within the preceeding control block.\r\n//if they were read at any other time, then we must write them out in full.\r\n//this logic applies only to locals of a function.\r\n//#define USECACHE\r\n\r\nstatic void GCache_Load(struct jitstate *jit, int ao, int ar, int bo, int br, int co, int cr)\r\n{\r\n#if USECACHE\r\n\tif (jit->cachedreg != REG_NONE)\r\n\t{\r\n\t\t/*something is cached, if its one of the input offsets then can chain the instruction*/\r\n\r\n\t\tif (jit->cachedglobal === ao && ar != REG_NONE)\r\n\t\t{\r\n\t\t\tif (jit->cachedreg == ar)\r\n\t\t\t\tar = REG_NONE;\r\n\t\t}\r\n\t\tif (jit->cachedglobal === bo && br != REG_NONE)\r\n\t\t{\r\n\t\t\tif (jit->cachedreg == br)\r\n\t\t\t\tbr = REG_NONE;\r\n\t\t}\r\n\t\tif (jit->cachedglobal === co && cr != REG_NONE)\r\n\t\t{\r\n\t\t\tif (jit->cachedreg == cr)\r\n\t\t\t\tcr = REG_NONE;\r\n\t\t}\r\n\r\n\t\tif (!istemp(ao))\r\n\t\t{\r\n\t\t\t/*purge the old cache*/\r\n\t\t\tswitch(jit->cachedreg)\r\n\t\t\t{\r\n\t\t\tcase REG_NONE:\r\n\t\t\t\tbreak;\r\n\t\t\tcase REG_S0:\r\n\t\t\t\t//fstps glob[C]\r\n\t\t\t\tEmitByte(0xd9);EmitByte(0x1d);EmitAdr(jit->glob + jit->cachedglobal);\r\n\t\t\t\tbreak;\r\n\t\t\tdefault:\r\n\t\t\t\tSTOREREG(jit->cachedreg, jit->glob + jit->cachedglobal);\r\n\t\t\t\tbreak;\r\n\t\t}\r\n\t\tjit->cachedglobal = -1;\r\n\t\tjit->cachedreg = REG_NONE;\r\n\t}\r\n\r\n#endif\r\n\tswitch(ar)\r\n\t{\r\n\tcase REG_NONE:\r\n\t\tbreak;\r\n\tcase REG_S0:\r\n\t\t//flds glob[A]\r\n\t\tEmitByte(0xd9);EmitByte(0x05);EmitAdr(jit->glob + ao);\r\n\t\tbreak;\r\n\tdefault:\r\n\t\tLOADREG(jit->glob + ao, ar);\r\n\t\tbreak;\r\n\t}\r\n\r\n\tswitch(br)\r\n\t{\r\n\tcase REG_NONE:\r\n\t\tbreak;\r\n\tcase REG_S0:\r\n\t\t//flds glob[A]\r\n\t\tEmitByte(0xd9);EmitByte(0x05);EmitAdr(jit->glob + bo);\r\n\t\tbreak;\r\n\tdefault:\r\n\t\tLOADREG(jit->glob + bo, br);\r\n\t\tbreak;\r\n\t}\r\n\r\n\tswitch(cr)\r\n\t{\r\n\tcase REG_NONE:\r\n\t\tbreak;\r\n\tcase REG_S0:\r\n\t\t//flds glob[A]\r\n\t\tEmitByte(0xd9);EmitByte(0x05);EmitAdr(jit->glob + co);\r\n\t\tbreak;\r\n\tdefault:\r\n\t\tLOADREG(jit->glob + co, cr);\r\n\t\tbreak;\r\n\t}\r\n}\r\nstatic void GCache_Store(struct jitstate *jit, int ofs, int reg)\r\n{\r\n#if USECACHE\r\n\tjit->cachedglobal = ofs;\r\n\tjit->cachedreg = reg;\r\n#else\r\n\tswitch(reg)\r\n\t{\r\n\tcase REG_NONE:\r\n\t\tbreak;\r\n\tcase REG_S0:\r\n\t\t//fstps glob[C]\r\n\t\tEmitByte(0xd9);EmitByte(0x1d);EmitAdr(jit->glob + ofs);\r\n\t\tbreak;\r\n\tdefault:\r\n\t\tSTOREREG(reg, jit->glob + ofs);\r\n\t\tbreak;\r\n\t}\r\n#endif\r\n}\r\n\r\nstatic void *Jit_LocalLoc(struct jitstate *jit)\r\n{\r\n\treturn &jit->code[jit->codesize];\r\n}\r\nstatic void *Jit_LocalJmp(struct jitstate *jit, int cond)\r\n{\r\n\t/*floating point ops don't set the sign flag, thus we use the 'above/below' instructions instead of 'greater/less' instructions*/\r\n\tif (cond == OP_GOTO)\r\n\t\tJit_EmitByte(jit, 0xeb);\t//jmp\r\n\telse if (cond == OP_LE_F)\r\n\t\tJit_EmitByte(jit, 0x76);\t//jbe\r\n\telse if (cond == OP_GE_F)\r\n\t\tJit_EmitByte(jit, 0x73);\t//jae\r\n\telse if (cond == OP_LT_F)\r\n\t\tJit_EmitByte(jit, 0x72);\t//jb\r\n\telse if (cond == OP_GT_F)\r\n\t\tJit_EmitByte(jit, 0x77);\t//ja\r\n\telse if (cond == OP_LE_I)\r\n\t\tJit_EmitByte(jit, 0x7e);\t//jle\r\n\telse if (cond == OP_LT_I)\r\n\t\tJit_EmitByte(jit, 0x7c);\t//jl\r\n\telse if ((cond >= OP_NE_F && cond <= OP_NE_FNC) || cond == OP_NE_I)\r\n\t\tJit_EmitByte(jit, 0x75);\t//jne\r\n\telse if ((cond >= OP_EQ_F && cond <= OP_EQ_FNC) || cond == OP_EQ_I)\r\n\t\tJit_EmitByte(jit, 0x74);\t//je\r\n#if defined(DEBUG) && defined(_WIN32)\r\n\telse\r\n\t{\r\n\t\tOutputDebugString(\"oh noes!\\n\");\r\n\t\treturn NULL;\r\n\t}\r\n#endif\r\n\r\n\tJit_EmitByte(jit, 0);\r\n\r\n\treturn Jit_LocalLoc(jit);\r\n}\r\nstatic void LocalJmpLoc(void *jmp, void *loc)\r\n{\r\n\tint offs;\r\n\tunsigned char *a = jmp;\r\n\toffs = (char *)loc - (char *)jmp;\r\n#if defined(DEBUG) && defined(_WIN32)\r\n\tif (offs > 127 || offs <= -128)\r\n\t{\r\n\t\tOutputDebugStringA(\"bad jump\\n\");\r\n\t\ta[-2] = 0xcd;\r\n\t\ta[-1] = 0xcc;\r\n\t\treturn;\r\n\t}\r\n#endif\r\n\ta[-1] = offs;\r\n}\r\n\r\nstatic void FixupJumps(struct jitstate *jit)\r\n{\r\n\tunsigned int j;\r\n\tunsigned char *codesrc;\r\n\tunsigned char *codedst;\r\n\tunsigned int offset;\r\n\r\n\tunsigned int v;\r\n\r\n\tfor (j = 0; j < jit->numjumps;)\r\n\t{\r\n\t\tv = jit->statementjumps[j++];\r\n\t\tcodesrc = &jit->code[v];\r\n\r\n\t\tv = jit->statementjumps[j++];\r\n\t\tcodedst = jit->statementoffsets[v];\r\n\r\n\t\tv = jit->statementjumps[j++];\r\n\t\toffset = (int)(codedst - (codesrc-v));\t//3rd term because the jump is relative to the instruction start, not the instruction's offset\r\n\r\n\t\tcodesrc[0] = (offset>> 0)&0xff;\r\n\t\tcodesrc[1] = (offset>> 8)&0xff;\r\n\t\tcodesrc[2] = (offset>>16)&0xff;\r\n\t\tcodesrc[3] = (offset>>24)&0xff;\r\n\t}\r\n}\r\n\r\nint ASMCALL PR_LeaveFunction (progfuncs_t *progfuncs);\r\nint ASMCALL PR_EnterFunction (progfuncs_t *progfuncs, dfunction_t *f, int progsnum);\r\n\r\nvoid PR_CloseJit(struct jitstate *jit)\r\n{\r\n\tif (jit)\r\n\t{\r\n\t\tfree(jit->statementjumps);\r\n\t\tfree(jit->statementoffsets);\r\n#ifndef _WIN32\r\n\t\tmunmap(jit->code, jit->jitstatements * 500);\r\n#else\r\n\t\tfree(jit->code);\r\n#endif\r\n\t\tfree(jit);\r\n\t}\r\n}\r\n\r\n#if 0\r\n//called from jit code\r\nstatic PDECL PR_CallFuncion(progfuncs_t *progfuncs, int fnum)\r\n{\r\n\tint callerprogs;\r\n\tint newpr;\r\n\tunsigned int fnum;\r\n\r\n\tfnum = OPA->function;\r\n\r\n\tglob = NULL;\t//try to derestrict it.\r\n\r\n\tcallerprogs=prinst.pr_typecurrent;\t\t\t//so we can revert to the right caller.\r\n\tnewpr = (fnum & 0xff000000)>>24;\t//this is the progs index of the callee\r\n\tfnum &= ~0xff000000;\t\t\t\t//the callee's function index.\r\n\r\n\t//if it's an external call, switch now (before any function pointers are used)\r\n\tif (callerprogs != newpr || !fnum || fnum > pr_progs->numfunctions)\r\n\t{\r\n\t\tchar *msg = fnum?\"OP_CALL references invalid function in %s\\n\":\"NULL function from qc (inside %s).\\n\";\r\n\t\tPR_SwitchProgsParms(progfuncs, callerprogs);\r\n\r\n\t\tglob = pr_globals;\r\n\t\tif (!progfuncs->funcs.debug_trace)\r\n\t\t\tQCFAULT(&progfuncs->funcs, msg, PR_StringToNative(&progfuncs->funcs, pr_xfunction->s_name));\r\n\r\n\t\t//skip the instruction if they just try stepping over it anyway.\r\n\t\tPR_StackTrace(&progfuncs->funcs, 0);\r\n\t\tprintf(msg, PR_StringToNative(&progfuncs->funcs, pr_xfunction->s_name));\r\n\r\n\t\tpr_globals[OFS_RETURN] = 0;\r\n\t\tpr_globals[OFS_RETURN+1] = 0;\r\n\t\tpr_globals[OFS_RETURN+2] = 0;\r\n\t\tbreak;\r\n\t}\r\n\r\n\tnewf = &pr_cp_functions[fnum & ~0xff000000];\r\n\r\n\tif (newf->first_statement <= 0)\r\n\t{\t// negative statements are built in functions\r\n\t\t/*calling a builtin in another progs may affect that other progs' globals instead, is the theory anyway, so args and stuff need to move over*/\r\n\t\tif (prinst.pr_typecurrent != 0)\r\n\t\t{\r\n\t\t\t//builtins quite hackily refer to only a single global.\r\n\t\t\t//for builtins to affect the globals of other progs, we need to first switch to the progs that it will affect, so they'll be correct when we switch back\r\n\t\t\tPR_SwitchProgsParms(progfuncs, 0);\r\n\t\t}\r\n\t\ti = -newf->first_statement;\r\n//\t\t\tp = pr_typecurrent;\r\n\t\tif (i < externs->numglobalbuiltins)\r\n\t\t{\r\n#ifndef QCGC\r\n\t\t\tprinst.numtempstringsstack = prinst.numtempstrings;\r\n#endif\r\n\t\t\t(*externs->globalbuiltins[i]) (&progfuncs->funcs, (struct globalvars_s *)current_progstate->globals);\r\n\r\n\t\t\t//in case ed_alloc was called\r\n\t\t\tnum_edicts = sv_num_edicts;\r\n\r\n\t\t\tif (prinst.continuestatement!=-1)\r\n\t\t\t{\r\n\t\t\t\tst=&pr_statements[prinst.continuestatement];\r\n\t\t\t\tprinst.continuestatement=-1;\r\n\t\t\t\tglob = pr_globals;\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n//\t\t\t\t\tif (newf->first_statement == -0x7fffffff)\r\n//\t\t\t\t\t\t((builtin_t)newf->profile) (progfuncs, (struct globalvars_s *)current_progstate->globals);\r\n//\t\t\t\t\telse\r\n\t\t\t\tPR_RunError (&progfuncs->funcs, \"Bad builtin call number - %i\", -newf->first_statement);\r\n\t\t}\r\n//\t\t\tmemcpy(&pr_progstate[p].globals[OFS_RETURN], &current_progstate->globals[OFS_RETURN], sizeof(vec3_t));\r\n\t\tPR_SwitchProgsParms(progfuncs, (progsnum_t)callerprogs);\r\n\r\n\t\t//decide weather non debugger wants to start debugging.\r\n\t\ts = st-pr_statements;\r\n\t\treturn s;\r\n\t}\r\n//\t\tPR_SwitchProgsParms((OPA->function & 0xff000000)>>24);\r\n\ts = PR_EnterFunction (progfuncs, newf, callerprogs);\r\n\tst = &pr_statements[s];\r\n}\r\n#endif\r\n\r\nstruct jitstate *PR_GenerateJit(progfuncs_t *progfuncs)\r\n{\r\n\tstruct jitstate *jit;\r\n\r\n\tvoid *j0, *l0;\r\n\tvoid *j1, *l1;\r\n\tvoid *j2, *l2;\r\n\tunsigned int i;\r\n\tdstatement16_t *op = (dstatement16_t*)current_progstate->statements;\r\n\tunsigned int numstatements = current_progstate->progs->numstatements;\r\n\tunsigned int numglobals = current_progstate->progs->numglobals+3;\t//vectors are annoying.\r\n\tint *glob = (int*)current_progstate->globals;\r\n\tunsigned int numfunctions = current_progstate->progs->numfunctions;\r\n\tmfunction_t *func;\r\n//\tpbyte *isconst;\r\n\tpbool failed = false;\r\n\r\n\tjit = malloc(sizeof(*jit));\r\n\tjit->jitstatements = numstatements;\r\n\r\n//\tisconst = malloc(numglobals*sizeof(*isconst));\r\n\tjit->statementjumps = malloc(numstatements*3*sizeof(int));\r\n\tjit->statementoffsets = malloc(numstatements*sizeof(*jit->statementoffsets));\r\n#ifndef _WIN32\r\n\tjit->code = mmap(NULL, numstatements*500, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);\r\n#else\r\n\tjit->code = malloc(numstatements*500);\r\n#endif\r\n\tif (!jit->code)\r\n\t\treturn NULL;\r\n\r\n\tjit->numjumps = 0;\r\n\tjit->codesize = 0;\r\n\r\n\tfor (i = 0; i < numstatements; i++)\r\n\t\tjit->statementoffsets[i] = NULL;\r\n//\tfor (i = 0; i < numglobals; i++)\r\n//\t\tisconst[i] = true;\r\n\tfor (i = 0; i < numfunctions; i++)\r\n\t{\r\n\t\t\r\n\t}\r\n\tfor (i = 0; i < numstatements; i++)\r\n\t{\r\n\t\t//figure out which statements are jumped to. these are statements that must flush registers prior to execution.\r\n\t\tswitch(op[i].op)\r\n\t\t{\r\n\t\tcase OP_GOTO:\r\n\t\t\tjit->statementoffsets[i + (short)op[i].a] = (void*)~0;\r\n\t\t\tbreak;\r\n\t\tcase OP_IF_I:\r\n\t\tcase OP_IFNOT_I:\r\n\t\tcase OP_IF_F:\r\n\t\tcase OP_IFNOT_F:\r\n\t\tcase OP_IF_S:\r\n\t\tcase OP_IFNOT_S:\r\n\t\tcase OP_CASE:\r\n\t\t\tjit->statementoffsets[i + (short)op[i].b] = (void*)~0;\r\n\t\t\tbreak;\r\n\t\tcase OP_CASERANGE:\r\n\t\t\tjit->statementoffsets[i + (short)op[i].c] = (void*)~0;\r\n\t\t\tbreak;\r\n\t\t}\r\n\t\t//we probably can't do anything about consts.\r\n\t\t//we might be able to do something about locals, but we would need to fix this to generate per-function.\r\n\t\t//we CAN do something about consts, most of them anyway.\r\n\t\t//visible types \r\n/*\r\n\t\tif (OpAssignsToA(op[i].op))\r\n\t\t{\r\n\t\t\tif (op[i].a >= numglobals)\r\n\t\t\t\tfailed = true;\r\n\t\t\telse\r\n\t\t\t\tisconst[op[i].a] = false;\r\n\t\t}\r\n\t\tif (OpAssignsToB(op[i].op))\r\n\t\t{\r\n\t\t\tif (op[i].b >= numglobals)\r\n\t\t\t\tfailed = true;\r\n\t\t\telse\r\n\t\t\t\tisconst[op[i].b] = false;\r\n\t\t}\r\n\t\tif (OpAssignsToC(op[i].op))\r\n\t\t{\r\n\t\t\tif (op[i].c >= numglobals)\r\n\t\t\t\tfailed = true;\r\n\t\t\telse\r\n\t\t\t\tisconst[op[i].c] = false;\r\n\t\t}\r\n*/\r\n\t}\r\n\r\n\tfor (i = 0; i < numstatements && !failed; i++)\r\n\t{\r\n\t\tif (jit->statementoffsets[i])\r\n\t\t{\r\n\t\t\t//FIXME: flush any registers.\r\n\t\t}\r\n\t\tjit->statementoffsets[i] = &jit->code[jit->codesize];\r\n\r\n#ifdef _DEBUG\r\n\t\t/*DEBUG*/\r\n\t\tSETREGI(op[i].op, REG_ESI);\r\n#endif\r\n\r\n\t\tswitch(op[i].op)\r\n\t\t{\r\n\t\t//jumps\r\n\t\tcase OP_IF_I:\r\n\t\t\t//integer compare\r\n\t\t\t//if a, goto b\r\n\r\n\t\t\t//cmpl $0,glob[A]\r\n\t\t\tEmitByte(0x83);EmitByte(0x3d);EmitAdr(glob + op[i].a);EmitByte(0x0);\r\n\t\t\t//jne B\r\n\t\t\tEmitByte(0x0f);EmitByte(0x85);Emit4ByteJump(i + (signed short)op[i].b, -4);\r\n\t\t\tbreak;\r\n\r\n\t\tcase OP_IFNOT_I:\r\n\t\t\t//integer compare\r\n\t\t\t//if !a, goto b\r\n\r\n\t\t\t//cmpl $0,glob[A]\r\n\t\t\tEmitByte(0x83);EmitByte(0x3d);EmitAdr(glob + op[i].a);EmitByte(0x0);\r\n\t\t\t//je B\r\n\t\t\tEmitByte(0x0f);EmitByte(0x84);Emit4ByteJump(i + (signed short)op[i].b, -4);\r\n\t\t\tbreak;\r\n\r\n\t\tcase OP_GOTO:\r\n\t\t\tEmitByte(0xE9);Emit4ByteJump(i + (signed short)op[i].a, -4);\r\n\t\t\tbreak;\r\n\t\t\t\r\n\t\t//function returns\r\n\t\tcase OP_DONE:\r\n\t\tcase OP_RETURN:\r\n\t\t\t//done and return are the same\r\n\r\n\t\t\t//part 1: store A into OFS_RETURN\r\n\r\n\t\t\tif (!op[i].a)\r\n\t\t\t{\r\n\t\t\t\t//assumption: anything that returns address 0 is a void or zero return.\r\n\t\t\t\t//thus clear eax and copy that to the return vector.\r\n\t\t\t\tCLEARREG(REG_EAX);\r\n\t\t\t\tSTOREREG(REG_EAX, glob + OFS_RETURN+0);\r\n\t\t\t\tSTOREREG(REG_EAX, glob + OFS_RETURN+1);\r\n\t\t\t\tSTOREREG(REG_EAX, glob + OFS_RETURN+2);\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tLOADREG(glob + op[i].a+0, REG_EAX);\r\n\t\t\t\tLOADREG(glob + op[i].a+1, REG_EDX);\r\n\t\t\t\tLOADREG(glob + op[i].a+2, REG_ECX);\r\n\t\t\t\tSTOREREG(REG_EAX, glob + OFS_RETURN+0);\r\n\t\t\t\tSTOREREG(REG_EDX, glob + OFS_RETURN+1);\r\n\t\t\t\tSTOREREG(REG_ECX, glob + OFS_RETURN+2);\r\n\t\t\t}\r\n\t\t\t\r\n\t\t\t//call leavefunction to get the return address\r\n\t\t\t\r\n//\t\t\tpushl progfuncs\r\n\t\t\tEmitByte(0x68);EmitAdr(progfuncs);\r\n//\t\t\tcall PR_LeaveFunction\r\n\t\t\tEmitByte(0xe8);EmitFOffset(PR_LeaveFunction, 4);\r\n//\t\t\tadd $4,%esp\r\n\t\t\tEmitByte(0x83);EmitByte(0xc4);EmitByte(0x04);\r\n//\t\t\tmovl pr_depth,%edx\r\n\t\t\tEmitByte(0x8b);EmitByte(0x15);EmitAdr(&pr_depth);\r\n//\t\t\tcmp prinst->exitdepth,%edx\r\n\t\t\tEmitByte(0x3b);EmitByte(0x15);EmitAdr(&prinst.exitdepth);\r\n//\t\t\tje returntoc\r\n\t\t\tj1 = LocalJmp(OP_EQ_E);\r\n//\t\t\t\tmov statementoffsets[%eax*4],%eax\r\n\t\t\t\tEmitByte(0x8b);EmitByte(0x04);EmitByte(0x85);EmitAdr(jit->statementoffsets+1);\r\n//\t\t\t\tjmp *eax\r\n\t\t\t\tEmitByte(0xff);EmitByte(0xe0);\r\n//\t\t\treturntoc:\r\n\t\t\tl1 = LocalLoc();\r\n//\t\t\tret\r\n\t\t\tEmitByte(0xc3);\r\n\r\n\t\t\tLocalJmpLoc(j1,l1);\r\n\t\t\tbreak;\r\n\r\n\t\t//function calls\r\n\t\tcase OP_CALL0:\r\n\t\tcase OP_CALL1:\r\n\t\tcase OP_CALL2:\r\n\t\tcase OP_CALL3:\r\n\t\tcase OP_CALL4:\r\n\t\tcase OP_CALL5:\r\n\t\tcase OP_CALL6:\r\n\t\tcase OP_CALL7:\r\n\t\tcase OP_CALL8:\r\n\t\t\t//FIXME: the size of this instruction is going to hurt cache performance if every single function call is expanded into this HUGE CHUNK of gibberish!\r\n\t\t\t//FIXME: consider the feasability of just calling a C function and just jumping to the address it returns.\r\n\r\n\t\t//save the state in place the rest of the engine can cope with\r\n\t\t\t//movl $i, pr_xstatement\r\n\t\t\tEmitByte( 0xc7);EmitByte(0x05);EmitAdr(&pr_xstatement);Emit4Byte(i);\r\n\t\t\t//movl $(op[i].op-OP_CALL0), pr_argc\r\n\t\t\tEmitByte( 0xc7);EmitByte(0x05);EmitAdr(&progfuncs->funcs.callargc);Emit4Byte(op[i].op-OP_CALL0);\r\n\r\n\t\t//figure out who we're calling, and what that involves\r\n\t\t\t//%eax = glob[A]\r\n\t\t\tLOADREG(glob + op[i].a, REG_EAX);\r\n\t\t//eax is now the func num\r\n\r\n\t\t\t//mov %eax,%ecx\r\n\t\t\tEmitByte(0x89); EmitByte(0xc1);\r\n\t\t\t//shr $24,%ecx\r\n\t\t\tEmitByte(0xc1); EmitByte(0xe9); EmitByte(0x18);\r\n\t\t//ecx is now the progs num for the new func\r\n\r\n/*\r\n\t\t\t//cmp %ecx,pr_typecurrent\r\n\t\t\tEmitByte(0x39); EmitByte(0x0d); EmitAdr(&pr_typecurrent);\r\n\t\t\t//je sameprogs\r\n\t\t\tj1 = LocalJmp(OP_EQ_I);\r\n\t\t\t{\r\n\t\t\t\t//can't handle switching progs\r\n\r\n\t\t\t\t//FIXME: recurse though PR_ExecuteProgram\r\n\t\t\t\t//push eax\r\n\t\t\t\t//push progfuncs\r\n\t\t\t\t//call PR_ExecuteProgram\r\n\t\t\t\t//add $8,%esp\r\n\t\t\t\t//remember to change the je above\r\n\r\n\t\t\t\t//err... exit depth? no idea\r\n\t\t\t\tEmitByte(0xcd);EmitByte(op[i].op);\t//int $X\r\n\r\n\r\n\t\t\t\t//ret\r\n\t\t\t\tEmitByte(0xc3);\r\n\t\t\t}\r\n\t\t\t//sameprogs:\r\n\t\t\tl1 = LocalLoc();\r\n\t\t\tLocalJmpLoc(j1,l1);\r\n*/\r\n\r\n\t\t\t//andl $0x00ffffff, %eax\r\n\t\t\tEmitByte(0x25);Emit4Byte(0x00ffffff);\r\n\t\t\t\r\n\t\t\t//mov $sizeof(dfunction_t),%edx\r\n\t\t\tEmitByte(0xba);Emit4Byte(sizeof(dfunction_t));\r\n\t\t\t//mul %edx\r\n\t\t\tEmitByte(0xf7); EmitByte(0xe2);\r\n\t\t\t//add pr_functions,%eax\r\n\t\t\tEmitByte(0x05); EmitAdr(current_progstate->functions);\r\n\r\n\t\t//eax is now the dfunction_t to be called\r\n\t\t//edx is clobbered.\r\n\r\n\t\t\t//mov (%eax),%edx\r\n\t\t\tEmitByte(0x8b);EmitByte(0x10);\r\n\t\t//edx is now the first statement number\r\n\t\t\t//cmp $0,%edx\r\n\t\t\tEmitByte(0x83);EmitByte(0xfa);EmitByte(0x00);\r\n\t\t\t//jl isabuiltin\r\n\t\t\tj1 = LocalJmp(OP_LT_I);\r\n\t\t\t{\r\n\t\t\t\t/* call the function*/\r\n\t\t\t\t//push %ecx\r\n\t\t\t\tEmitByte(0x51);\r\n\t\t\t\t//push %eax\r\n\t\t\t\tEmitByte(0x50);\r\n\t\t\t\t//pushl progfuncs\r\n\t\t\t\tEmitByte(0x68);EmitAdr(progfuncs);\r\n\t\t\t\t//call PR_EnterFunction\r\n\t\t\t\tEmitByte(0xe8);EmitFOffset(PR_EnterFunction, 4);\r\n\t\t\t\t//sub $12,%esp\r\n\t\t\t\tEmitByte(0x83);EmitByte(0xc4);EmitByte(0xc);\r\n\t\t//eax is now the next statement number (first of the new function, usually equal to ecx, but not always)\r\n\r\n\t\t\t\t//jmp statementoffsets[%eax*4]\r\n\t\t\t\tEmitByte(0xff);EmitByte(0x24);EmitByte(0x85);EmitAdr(jit->statementoffsets+1);\r\n\t\t\t}\r\n\t\t\t/*its a builtin, figure out which, and call it*/\r\n\t\t\t//isabuiltin:\r\n\t\t\tl1 = LocalLoc();\r\n\t\t\tLocalJmpLoc(j1,l1);\r\n\r\n\t\t\t//push current_progstate->globals\r\n\t\t\tEmitByte(0x68);EmitAdr(current_progstate->globals);\r\n\t\t\t//push progfuncs\r\n\t\t\tEmitByte(0x68);EmitAdr(progfuncs);\r\n\t\t\t//neg %edx\r\n\t\t\tEmitByte(0xf7);EmitByte(0xda);\r\n\t\t\t//call externs->globalbuiltins[%edx,4]\r\n//FIXME: make sure this dereferences\r\n\t\t\tEmitByte(0xff);EmitByte(0x14);EmitByte(0x95);EmitAdr(externs->globalbuiltins);\r\n\t\t\t//add $8,%esp\r\n\t\t\tEmitByte(0x83);EmitByte(0xc4);EmitByte(0x8);\r\n\r\n\t\t//but that builtin might have been Abort()\r\n\r\n\t\t\tLOADREG(&prinst.continuestatement, REG_EAX);\r\n\t\t\t//cmp $-1,%eax\r\n\t\t\tEmitByte(0x83);EmitByte(0xf8);EmitByte(0xff);\r\n\t\t\t//je donebuiltincall\r\n\t\t\tj1 = LocalJmp(OP_EQ_I);\r\n\t\t\t{\r\n\t\t\t\t//mov $-1,prinst->continuestatement\r\n\t\t\t\tEmitByte(0xc7);EmitByte(0x05);EmitAdr(&prinst.continuestatement);Emit4Byte((unsigned int)-1);\r\n\r\n\t\t\t\t//jmp statementoffsets[%eax*4]\r\n\t\t\t\tEmitByte(0xff);EmitByte(0x24);EmitByte(0x85);EmitAdr(jit->statementoffsets);\r\n\t\t\t}\r\n\t\t\t//donebuiltincall:\r\n\t\t\tl1 = LocalLoc();\r\n\t\t\tLocalJmpLoc(j1,l1);\r\n\t\t\tbreak;\r\n\r\n\t\tcase OP_MUL_F:\r\n\t\t\t//flds glob[A]\r\n\t\t\tEmitByte(0xd9);EmitByte(0x05);EmitAdr(glob + op[i].a);\r\n\t\t\t//fmuls glob[B]\r\n\t\t\tEmitByte(0xd8);EmitByte(0x0d);EmitAdr(glob + op[i].b);\r\n\t\t\t//fstps glob[C]\r\n\t\t\tEmitByte(0xd9);EmitByte(0x1d);EmitAdr(glob + op[i].c);\r\n\t\t\tbreak;\r\n\t\tcase OP_DIV_F:\r\n\t\t\t//flds glob[A]\r\n\t\t\tEmitByte(0xd9);EmitByte(0x05);EmitAdr(glob + op[i].a);\r\n\t\t\t//fdivs glob[B]\r\n\t\t\tEmitByte(0xd8);EmitByte(0x35);EmitAdr(glob + op[i].b);\r\n\t\t\t//fstps glob[C]\r\n\t\t\tEmitByte(0xd9);EmitByte(0x1d);EmitAdr(glob + op[i].c);\r\n\t\t\tbreak;\r\n\t\tcase OP_ADD_F:\r\n\t\t\t//flds glob[A]\r\n\t\t\tEmitByte(0xd9);EmitByte(0x05);EmitAdr(glob + op[i].a);\r\n\t\t\t//fadds glob[B]\r\n\t\t\tEmitByte(0xd8);EmitByte(0x05);EmitAdr(glob + op[i].b);\r\n\t\t\t//fstps glob[C]\r\n\t\t\tEmitByte(0xd9);EmitByte(0x1d);EmitAdr(glob + op[i].c);\r\n\t\t\tbreak;\r\n\t\tcase OP_SUB_F:\r\n\t\t\t//flds glob[A]\r\n\t\t\tEmitByte(0xd9);EmitByte(0x05);EmitAdr(glob + op[i].a);\r\n\t\t\t//fsubs glob[B]\r\n\t\t\tEmitByte(0xd8);EmitByte(0x25);EmitAdr(glob + op[i].b);\r\n\t\t\t//fstps glob[C]\r\n\t\t\tEmitByte(0xd9);EmitByte(0x1d);EmitAdr(glob + op[i].c);\r\n\t\t\tbreak;\r\n\r\n\t\tcase OP_NOT_F:\r\n\t\t\t//fldz\r\n\t\t\tEmitByte(0xd9);EmitByte(0xee);\r\n\t\t\t//fcomps\tglob[A]\r\n\t\t\tEmitByte(0xd8); EmitByte(0x1d); EmitAdr(glob + op[i].a);\r\n\t\t\t//fnstsw %ax\r\n\t\t\tEmitByte(0xdf);EmitByte(0xe0);\r\n\t\t\t//testb 0x40,%ah\r\n\t\t\tEmitByte(0xf6);EmitByte(0xc4);EmitByte(0x40);\r\n\t\t\t\r\n\t\t\tj1 = LocalJmp(OP_NE_F);\r\n\t\t\t{\r\n\t\t\t\tSTOREF(0.0f, glob + op[i].c);\r\n\t\t\t\tj2 = LocalJmp(OP_GOTO);\r\n\t\t\t}\r\n\t\t\t{\r\n\t\t\t\t//noteq:\r\n\t\t\t\tl1 = LocalLoc();\r\n\t\t\t\tSTOREF(1.0f, glob + op[i].c);\r\n\t\t\t}\r\n\t\t\t//end:\r\n\t\t\tl2 = LocalLoc();\r\n\t\t\tLocalJmpLoc(j1,l1);\r\n\t\t\tLocalJmpLoc(j2,l2);\r\n\t\t\tbreak;\r\n\r\n\t\tcase OP_STORE_F:\r\n\t\tcase OP_STORE_S:\r\n\t\tcase OP_STORE_ENT:\r\n\t\tcase OP_STORE_FLD:\r\n\t\tcase OP_STORE_FNC:\r\n\t\t\tLOADREG(glob + op[i].a, REG_EAX);\r\n\t\t\tSTOREREG(REG_EAX, glob + op[i].b);\r\n\t\t\tbreak;\r\n\r\n\t\tcase OP_STORE_V:\r\n\t\t\tLOADREG(glob + op[i].a+0, REG_EAX);\r\n\t\t\tLOADREG(glob + op[i].a+1, REG_EDX);\r\n\t\t\tLOADREG(glob + op[i].a+2, REG_ECX);\r\n\t\t\tSTOREREG(REG_EAX, glob + op[i].b+0);\r\n\t\t\tSTOREREG(REG_EDX, glob + op[i].b+1);\r\n\t\t\tSTOREREG(REG_ECX, glob + op[i].b+2);\r\n\t\t\tbreak;\r\n\r\n\t\tcase OP_LOAD_F:\r\n\t\tcase OP_LOAD_S:\r\n\t\tcase OP_LOAD_ENT:\r\n\t\tcase OP_LOAD_FLD:\r\n\t\tcase OP_LOAD_FNC:\r\n\t\tcase OP_LOAD_V:\r\n\t\t//a is the ent number, b is the field\r\n\t\t//c is the dest\r\n\r\n\t\t\tLOADREG(glob + op[i].a, REG_EAX);\r\n\t\t\tLOADREG(glob + op[i].b, REG_ECX);\r\n\r\n\t\t//FIXME: bound eax (ent number)\r\n\t\t//FIXME: bound ecx (field index)\r\n\t\t\t//mov (ebx,eax,4).%eax\r\n\t\t\tEmitByte(0x8b); EmitByte(0x04); EmitByte(0x83);\r\n\t\t//eax is now an edictrun_t\r\n\t\t\t//mov fields(,%eax,4),%edx\r\n\t\t\tEmitByte(0x8b);EmitByte(0x50);EmitByte((int)&((edictrun_t*)NULL)->fields);\r\n\t\t//edx is now the field array for that ent\r\n\r\n\t\t\t//mov fieldajust(%edx,%ecx,4),%eax\r\n\t\t\tEmitByte(0x8b); EmitByte(0x84); EmitByte(0x8a); Emit4Byte(progfuncs->funcs.fieldadjust*4);\r\n\r\n\t\t\tSTOREREG(REG_EAX, glob + op[i].c)\r\n\r\n\t\t\tif (op[i].op == OP_LOAD_V)\r\n\t\t\t{\r\n\t\t\t\t//mov fieldajust+4(%edx,%ecx,4),%eax\r\n\t\t\t\tEmitByte(0x8b); EmitByte(0x84); EmitByte(0x8a); Emit4Byte(4+progfuncs->funcs.fieldadjust*4);\r\n\t\t\t\tSTOREREG(REG_EAX, glob + op[i].c+1)\r\n\r\n\t\t\t\t//mov fieldajust+8(%edx,%ecx,4),%eax\r\n\t\t\t\tEmitByte(0x8b); EmitByte(0x84); EmitByte(0x8a); Emit4Byte(8+progfuncs->funcs.fieldadjust*4);\r\n\t\t\t\tSTOREREG(REG_EAX, glob + op[i].c+2)\r\n\t\t\t}\r\n\t\t\tbreak;\r\n\r\n\t\tcase OP_ADDRESS:\r\n\t\t\t//a is the ent number, b is the field\r\n\t\t//c is the dest\r\n\r\n\t\t\tLOADREG(glob + op[i].a, REG_EAX);\r\n\t\t\tLOADREG(glob + op[i].b, REG_ECX);\r\n\r\n\t\t//FIXME: bound eax (ent number)\r\n\t\t//FIXME: bound ecx (field index)\r\n\t\t\t//mov (ebx,eax,4).%eax\r\n\t\t\tEmitByte(0x8b); EmitByte(0x04); EmitByte(0x83);\r\n\t\t//eax is now an edictrun_t\r\n\t\t\t//mov fields(,%eax,4),%edx\r\n\t\t\tEmitByte(0x8b);EmitByte(0x50);EmitByte((int)&((edictrun_t*)NULL)->fields);\r\n\t\t//edx is now the field array for that ent\r\n\t\t\t//mov fieldajust(%edx,%ecx,4),%eax\t//offset = progfuncs->fieldadjust\r\n\t\t\t//EmitByte(0x8d); EmitByte(0x84); EmitByte(0x8a); EmitByte(progfuncs->funcs.fieldadjust*4);\r\n\t\t\tEmitByte(0x8d); EmitByte(0x84); EmitByte(0x8a); Emit4Byte(progfuncs->funcs.fieldadjust*4);\r\n\t\t\tSTOREREG(REG_EAX, glob + op[i].c);\r\n\t\t\tbreak;\r\n\r\n\t\tcase OP_STOREP_F:\r\n\t\tcase OP_STOREP_S:\r\n\t\tcase OP_STOREP_ENT:\r\n\t\tcase OP_STOREP_FLD:\r\n\t\tcase OP_STOREP_FNC:\r\n\t\t\tLOADREG(glob + op[i].a, REG_EAX);\r\n\t\t\tLOADREG(glob + op[i].b, REG_ECX);\r\n\t\t\t//mov %eax,(%ecx)\r\n\t\t\tEmitByte(0x89);EmitByte(0x01);\r\n\t\t\tbreak;\r\n\r\n\t\tcase OP_STOREP_V:\r\n\t\t\tLOADREG(glob + op[i].b, REG_ECX);\r\n\r\n\t\t\tLOADREG(glob + op[i].a+0, REG_EAX);\r\n\t\t\t//mov %eax,0(%ecx)\r\n\t\t\tEmitByte(0x89);EmitByte(0x01);\r\n\r\n\t\t\tLOADREG(glob + op[i].a+1, REG_EAX);\r\n\t\t\t//mov %eax,4(%ecx)\r\n\t\t\tEmitByte(0x89);EmitByte(0x41);EmitByte(0x04);\r\n\r\n\t\t\tLOADREG(glob + op[i].a+2, REG_EAX);\r\n\t\t\t//mov %eax,8(%ecx)\r\n\t\t\tEmitByte(0x89);EmitByte(0x41);EmitByte(0x08);\r\n\t\t\tbreak;\r\n\r\n\t\tcase OP_NE_I:\r\n\t\tcase OP_NE_E:\r\n\t\tcase OP_NE_FNC:\r\n\t\tcase OP_EQ_I:\r\n\t\tcase OP_EQ_E:\r\n\t\tcase OP_EQ_FNC:\r\n\t\t\t//integer equality\r\n\t\t\tLOADREG(glob + op[i].a, REG_EAX);\r\n\r\n\t\t\t//cmp glob[B],%eax\r\n\t\t\tEmitByte(0x3b); EmitByte(0x04); EmitByte(0x25); EmitAdr(glob + op[i].b);\r\n\t\t\tj1 = LocalJmp(op[i].op);\r\n\t\t\t{\r\n\t\t\t\tSTOREF(0.0f, glob + op[i].c);\r\n\t\t\t\tj2 = LocalJmp(OP_GOTO);\r\n\t\t\t}\r\n\t\t\t{\r\n\t\t\t\tl1 = LocalLoc();\r\n\t\t\t\tSTOREF(1.0f, glob + op[i].c);\r\n\t\t\t}\r\n\t\t\tl2 = LocalLoc();\r\n\t\t\tLocalJmpLoc(j1,l1);\r\n\t\t\tLocalJmpLoc(j2,l2);\r\n\t\t\tbreak;\r\n\r\n\t\tcase OP_NOT_I:\r\n\t\tcase OP_NOT_ENT:\r\n\t\tcase OP_NOT_FNC:\r\n\t\t\t//cmp glob[B],$0\r\n\t\t\tEmitByte(0x83); EmitByte(0x3d); EmitAdr(glob + op[i].a); EmitByte(0x00); \r\n\t\t\tj1 = LocalJmp(OP_NE_I);\r\n\t\t\t{\r\n\t\t\t\tSTOREF(1.0f, glob + op[i].c);\r\n\t\t\t\tj2 = LocalJmp(OP_GOTO);\r\n\t\t\t}\r\n\t\t\t{\r\n\t\t\t\tl1 = LocalLoc();\r\n\t\t\t\tSTOREF(0.0f, glob + op[i].c);\r\n\t\t\t}\r\n\t\t\tl2 = LocalLoc();\r\n\t\t\tLocalJmpLoc(j1,l1);\r\n\t\t\tLocalJmpLoc(j2,l2);\r\n\t\t\tbreak;\r\n\r\n\t\tcase OP_BITOR_F:\t//floats...\r\n\t\t\t//flds glob[A]\r\n\t\t\tEmitByte(0xd9); EmitByte(0x05);EmitAdr(glob + op[i].a);\r\n\t\t\t//flds glob[B]\r\n\t\t\tEmitByte(0xd9); EmitByte(0x05);EmitAdr(glob + op[i].b);\r\n\t\t\t//fistp tb\r\n\t\t\tEmitByte(0xdb); EmitByte(0x1d);EmitAdr(&tb);\r\n\t\t\t//fistp ta\r\n\t\t\tEmitByte(0xdb); EmitByte(0x1d);EmitAdr(&ta);\r\n\t\t\tLOADREG(&ta, REG_EAX)\r\n\t\t\t//or %eax,tb\r\n\t\t\tEmitByte(0x09); EmitByte(0x05);EmitAdr(&tb);\r\n\t\t\t//fild tb\r\n\t\t\tEmitByte(0xdb); EmitByte(0x05);EmitAdr(&tb);\r\n\t\t\t//fstps glob[C]\r\n\t\t\tEmitByte(0xd9); EmitByte(0x1d);EmitAdr(glob + op[i].c);\r\n\t\t\tbreak;\r\n\r\n\t\tcase OP_BITAND_F:\r\n\t\t\t//flds glob[A]\r\n\t\t\tEmitByte(0xd9); EmitByte(0x05);EmitAdr(glob + op[i].a);\r\n\t\t\t//flds glob[B]\r\n\t\t\tEmitByte(0xd9); EmitByte(0x05);EmitAdr(glob + op[i].b);\r\n\t\t\t//fistp tb\r\n\t\t\tEmitByte(0xdb); EmitByte(0x1d);EmitAdr(&tb);\r\n\t\t\t//fistp ta\r\n\t\t\tEmitByte(0xdb); EmitByte(0x1d);EmitAdr(&ta);\r\n\t\t\t/*two args are now at ta and tb*/\r\n\t\t\tLOADREG(&ta, REG_EAX)\r\n\t\t\t//and tb,%eax\r\n\t\t\tEmitByte(0x21); EmitByte(0x05);EmitAdr(&tb);\r\n\t\t\t/*we just wrote the int value to tb, convert that to a float and store it at c*/\r\n\t\t\t//fild tb\r\n\t\t\tEmitByte(0xdb); EmitByte(0x05);EmitAdr(&tb);\r\n\t\t\t//fstps glob[C]\r\n\t\t\tEmitByte(0xd9); EmitByte(0x1d);EmitAdr(glob + op[i].c);\r\n\t\t\tbreak;\r\n\r\n\t\tcase OP_AND_F:\r\n\t\t\t//test floats properly, so we don't get confused with -0.0\r\n\t\t\t//FIXME: is it feasable to grab the value as an int and test it against 0x7fffffff?\r\n\r\n\t\t\t//flds\tglob[A]\r\n\t\t\tEmitByte(0xd9); EmitByte(0x05); EmitAdr(glob + op[i].a);\r\n\t\t\t//fcomps\tnullfloat\r\n\t\t\tEmitByte(0xd8); EmitByte(0x1d); EmitAdr(&nullfloat);\r\n\t\t\t//fnstsw\t%ax\r\n\t\t\tEmitByte(0xdf); EmitByte(0xe0);\r\n\t\t\t//test\t$0x40,%ah\r\n\t\t\tEmitByte(0xf6); EmitByte(0xc4);EmitByte(0x40);\r\n\t\t\t//jz onefalse\r\n\t\t\tEmitByte(0x75); EmitByte(0x1f);\r\n\r\n\t\t\t//flds\tglob[B]\r\n\t\t\tEmitByte(0xd9); EmitByte(0x05); EmitAdr(glob + op[i].b);\r\n\t\t\t//fcomps\tnullfloat\r\n\t\t\tEmitByte(0xd8); EmitByte(0x1d); EmitAdr(&nullfloat);\r\n\t\t\t//fnstsw\t%ax\r\n\t\t\tEmitByte(0xdf); EmitByte(0xe0);\r\n\t\t\t//test\t$0x40,%ah\r\n\t\t\tEmitByte(0xf6); EmitByte(0xc4);EmitByte(0x40);\r\n\t\t\t//jnz onefalse\r\n\t\t\tEmitByte(0x75); EmitByte(0x0c);\r\n\r\n\t\t\t//mov float0,glob[C]\r\n\t\t\tEmitByte(0xc7); EmitByte(0x05); EmitAdr(glob + op[i].c); EmitFloat(1.0f);\r\n\t\t\t//jmp done\r\n\t\t\tEmitByte(0xeb); EmitByte(0x0a);\r\n\r\n\t\t\t//onefalse:\r\n\t\t\t//mov float1,glob[C]\r\n\t\t\tEmitByte(0xc7); EmitByte(0x05); EmitAdr(glob + op[i].c); EmitFloat(0.0f);\r\n\t\t\t//done:\r\n\t\t\tbreak;\r\n\t\tcase OP_OR_F:\r\n\t\t\t//test floats properly, so we don't get confused with -0.0\r\n\r\n\t\t\t//flds\tglob[A]\r\n\t\t\tEmitByte(0xd9); EmitByte(0x05); EmitAdr(glob + op[i].a);\r\n\t\t\t//fcomps\tnullfloat\r\n\t\t\tEmitByte(0xd8); EmitByte(0x1d); EmitAdr(&nullfloat);\r\n\t\t\t//fnstsw\t%ax\r\n\t\t\tEmitByte(0xdf); EmitByte(0xe0);\r\n\t\t\t//test\t$0x40,%ah\r\n\t\t\tEmitByte(0xf6); EmitByte(0xc4);EmitByte(0x40);\r\n\t\t\t//je onetrue\r\n\t\t\tEmitByte(0x74); EmitByte(0x1f);\r\n\r\n\t\t\t//flds\tglob[B]\r\n\t\t\tEmitByte(0xd9); EmitByte(0x05); EmitAdr(glob + op[i].b);\r\n\t\t\t//fcomps\tnullfloat\r\n\t\t\tEmitByte(0xd8); EmitByte(0x1d); EmitAdr(&nullfloat);\r\n\t\t\t//fnstsw\t%ax\r\n\t\t\tEmitByte(0xdf); EmitByte(0xe0);\r\n\t\t\t//test\t$0x40,%ah\r\n\t\t\tEmitByte(0xf6); EmitByte(0xc4);EmitByte(0x40);\r\n\t\t\t//je onetrue\r\n\t\t\tEmitByte(0x74); EmitByte(0x0c);\r\n\r\n\t\t\t//mov float0,glob[C]\r\n\t\t\tEmitByte(0xc7); EmitByte(0x05); EmitAdr(glob + op[i].c); EmitFloat(0.0f);\r\n\t\t\t//jmp done\r\n\t\t\tEmitByte(0xeb); EmitByte(0x0a);\r\n\r\n\t\t\t//onetrue:\r\n\t\t\t//mov float1,glob[C]\r\n\t\t\tEmitByte(0xc7); EmitByte(0x05); EmitAdr(glob + op[i].c); EmitFloat(1.0f);\r\n\t\t\t//done:\r\n\t\t\tbreak;\r\n\r\n\t\tcase OP_EQ_S:\r\n\t\tcase OP_NE_S:\r\n\t\t\t{\r\n\t\t\t//put a in ecx\r\n\t\t\tLOADREG(glob + op[i].a, REG_ECX);\r\n\t\t\t//put b in edi\r\n\t\t\tLOADREG(glob + op[i].b, REG_EDI);\r\n/*\r\n\t\t\t//early out if they're equal\r\n\t\t\t//cmp %ecx,%edi\r\n\t\t\tEmitByte(0x39); EmitByte(0xc0 | (REG_EDI<<3) | REG_ECX);\r\n\t\t\tj1c = LocalJmp(OP_EQ_S);\r\n\r\n\t\t\t//if a is 0, check if b is \"\"\r\n\t\t\t//jecxz ais0\r\n\t\t\tEmitByte(0xe3); EmitByte(0x1a);\r\n\r\n\t\t\t//if b is 0, check if a is \"\"\r\n  \t\t\t//cmp $0,%edi\r\n\t\t\tEmitByte(0x83); EmitByte(0xff); EmitByte(0x00);\r\n\t\t\t//jne bnot0\r\n\t\t\tEmitByte(0x75); EmitByte(0x2a);\r\n\t\t\t{\r\n\t\t\t\t//push a\r\n\t\t\t\tEmitByte(0x51);\r\n\t\t\t\t//push progfuncs\r\n\t\t\t\tEmitByte(0x68); EmitAdr(progfuncs);\r\n\t\t\t\t//call PR_StringToNative\r\n\t\t\t\tEmitByte(0xe8); EmitFOffset(PR_StringToNative,4);\r\n\t\t\t\t//add $8,%esp\r\n\t\t\t\tEmitByte(0x83); EmitByte(0xc4); EmitByte(0x08);\r\n\t\t\t\t//cmpb $0,(%eax)\r\n\t\t\t\tEmitByte(0x80); EmitByte(0x38); EmitByte(0x00);\r\n\t\t\t\tj1b = LocalJmp(OP_EQ_S);\r\n\t\t\t\tj0b = LocalJmp(OP_GOTO);\r\n\t\t\t}\r\n\r\n\t\t\t//ais0:\r\n\t\t\t{\r\n\t\t\t\t//push edi\r\n\t\t\t\tEmitByte(0x57);\r\n\t\t\t\t//push progfuncs\r\n\t\t\t\tEmitByte(0x68); EmitAdr(progfuncs);\r\n\t\t\t\t//call PR_StringToNative\r\n\t\t\t\tEmitByte(0xe8); EmitFOffset(PR_StringToNative,4);\r\n\t\t\t\t//add $8,%esp\r\n\t\t\t\tEmitByte(0x83); EmitByte(0xc4); EmitByte(0x08);\r\n\t\t\t\t//cmpb $0,(%eax)\r\n\t\t\t\tEmitByte(0x80); EmitByte(0x38); EmitByte(0x00);\r\n\t\t\t\t//je _true\r\n\t\t\t\tEmitByte(0x74); EmitByte(0x36);\r\n\t\t\t\t//jmp _false\r\n\t\t\t\tEmitByte(0xeb); EmitByte(0x28);\r\n\t\t\t}\r\n\t\t\t//bnot0:\r\n*/\r\nLOADREG(glob + op[i].a, REG_ECX);\r\n\t\t\t//push ecx\r\n\t\t\tEmitByte(0x51);\r\n\t\t\t//push progfuncs\r\n\t\t\tEmitByte(0x68); EmitAdr(progfuncs);\r\n\t\t\t//call PR_StringToNative\r\n\t\t\tEmitByte(0xe8); EmitFOffset(PR_StringToNative,4);\r\n\t\t\t//push %eax\r\n\t\t\tEmitByte(0x50);\r\n\r\nLOADREG(glob + op[i].b, REG_EDI);\r\n\t\t\t//push %edi\r\n\t\t\tEmitByte(0x57);\r\n\t\t\t//push progfuncs\r\n\t\t\tEmitByte(0x68); EmitAdr(progfuncs);\r\n\t\t\t//call PR_StringToNative\r\n\t\t\tEmitByte(0xe8); EmitFOffset(PR_StringToNative,4);\r\n\t\t\t//add $8,%esp\r\n\t\t\tEmitByte(0x83); EmitByte(0xc4); EmitByte(0x08);\r\n\r\n\r\n\t\t\t//push %eax\r\n\t\t\tEmitByte(0x50);\r\n\t\t\t//call strcmp\r\n\t\t\tEmitByte(0xe8); EmitFOffset(strcmp,4);\r\n\t\t\t//add $16,%esp\r\n\t\t\tEmitByte(0x83); EmitByte(0xc4); EmitByte(0x10);\r\n\r\n\t\t\t//cmp $0,%eax\r\n\t\t\tEmitByte(0x83); EmitByte(0xf8); EmitByte(0x00);\r\n\t\t\tj1 = LocalJmp(OP_EQ_S);\r\n\t\t\t{\r\n\t\t\t\tl0 = LocalLoc();\r\n\t\t\t\tSTOREF((op[i].op == OP_NE_S)?1.0f:0.0f, glob + op[i].c);\r\n\t\t\t\tj2 = LocalJmp(OP_GOTO);\r\n\t\t\t}\r\n\t\t\t{\r\n\t\t\t\tl1 = LocalLoc();\r\n\t\t\t\tSTOREF((op[i].op == OP_NE_S)?0.0f:1.0f, glob + op[i].c);\r\n\t\t\t}\r\n\t\t\tl2 = LocalLoc();\r\n\r\n//\t\t\tLocalJmpLoc(j0b, l0);\r\n\t\t\tLocalJmpLoc(j1, l1);\r\n//\t\t\tLocalJmpLoc(j1b, l1);\r\n\t\t\tLocalJmpLoc(j2, l2);\r\n\t\t\t}\r\n\t\t\tbreak;\r\n\r\n\t\tcase OP_NOT_S:\r\n\t\t\tLOADREG(glob + op[i].a, REG_EAX)\r\n\r\n\t\t\t//cmp $0,%eax\r\n\t\t\tEmitByte(0x83); EmitByte(0xf8); EmitByte(0x00);\r\n\t\t\tj2 = LocalJmp(OP_EQ_S);\r\n\r\n\t\t\t//push %eax\r\n\t\t\tEmitByte(0x50);\r\n\t\t\t//push progfuncs\r\n\t\t\tEmitByte(0x68); EmitAdr(progfuncs);\r\n\t\t\t//call PR_StringToNative\r\n\t\t\tEmitByte(0xe8); EmitFOffset(PR_StringToNative,4);\r\n\t\t\t//add $8,%esp\r\n\t\t\tEmitByte(0x83); EmitByte(0xc4); EmitByte(0x08);\r\n\r\n\t\t\t//cmpb $0,(%eax)\r\n\t\t\tEmitByte(0x80); EmitByte(0x38); EmitByte(0x00);\r\n\t\t\tj1 = LocalJmp(OP_EQ_S);\r\n\t\t\t{\r\n\t\t\t\tSTOREF(0.0f, glob + op[i].c);\r\n\t\t\t\tj0 = LocalJmp(OP_GOTO);\r\n\t\t\t}\r\n\t\t\t{\r\n\t\t\t\tl1 = LocalLoc();\r\n\t\t\t\tSTOREF(1.0f, glob + op[i].c);\r\n\t\t\t}\r\n\t\t\tl2 = LocalLoc();\r\n\t\t\tLocalJmpLoc(j2, l1);\r\n\t\t\tLocalJmpLoc(j1, l1);\r\n\t\t\tLocalJmpLoc(j0, l2);\r\n\t\t\tbreak;\r\n\r\n\t\tcase OP_ADD_V:\r\n\t\t\t//flds glob[A]\r\n\t\t\tEmitByte(0xd9);EmitByte(0x05);EmitAdr(glob + op[i].a+0);\r\n\t\t\t//fadds glob[B]\r\n\t\t\tEmitByte(0xd8);EmitByte(0x05);EmitAdr(glob + op[i].b+0);\r\n\t\t\t//fstps glob[C]\r\n\t\t\tEmitByte(0xd9);EmitByte(0x1d);EmitAdr(glob + op[i].c+0);\r\n\r\n\t\t\t//flds glob[A]\r\n\t\t\tEmitByte(0xd9);EmitByte(0x05);EmitAdr(glob + op[i].a+1);\r\n\t\t\t//fadds glob[B]\r\n\t\t\tEmitByte(0xd8);EmitByte(0x05);EmitAdr(glob + op[i].b+1);\r\n\t\t\t//fstps glob[C]\r\n\t\t\tEmitByte(0xd9);EmitByte(0x1d);EmitAdr(glob + op[i].c+1);\r\n\r\n\t\t\t//flds glob[A]\r\n\t\t\tEmitByte(0xd9);EmitByte(0x05);EmitAdr(glob + op[i].a+2);\r\n\t\t\t//fadds glob[B]\r\n\t\t\tEmitByte(0xd8);EmitByte(0x05);EmitAdr(glob + op[i].b+2);\r\n\t\t\t//fstps glob[C]\r\n\t\t\tEmitByte(0xd9);EmitByte(0x1d);EmitAdr(glob + op[i].c+2);\r\n\t\t\tbreak;\r\n\t\tcase OP_SUB_V:\r\n\t\t\t//flds glob[A]\r\n\t\t\tEmitByte(0xd9);EmitByte(0x05);EmitAdr(glob + op[i].a+0);\r\n\t\t\t//fsubs glob[B]\r\n\t\t\tEmitByte(0xd8);EmitByte(0x25);EmitAdr(glob + op[i].b+0);\r\n\t\t\t//fstps glob[C]\r\n\t\t\tEmitByte(0xd9);EmitByte(0x1d);EmitAdr(glob + op[i].c+0);\r\n\r\n\t\t\t//flds glob[A]\r\n\t\t\tEmitByte(0xd9);EmitByte(0x05);EmitAdr(glob + op[i].a+1);\r\n\t\t\t//fsubs glob[B]\r\n\t\t\tEmitByte(0xd8);EmitByte(0x25);EmitAdr(glob + op[i].b+1);\r\n\t\t\t//fstps glob[C]\r\n\t\t\tEmitByte(0xd9);EmitByte(0x1d);EmitAdr(glob + op[i].c+1);\r\n\r\n\t\t\t//flds glob[A]\r\n\t\t\tEmitByte(0xd9);EmitByte(0x05);EmitAdr(glob + op[i].a+2);\r\n\t\t\t//fsubs glob[B]\r\n\t\t\tEmitByte(0xd8);EmitByte(0x25);EmitAdr(glob + op[i].b+2);\r\n\t\t\t//fstps glob[C]\r\n\t\t\tEmitByte(0xd9);EmitByte(0x1d);EmitAdr(glob + op[i].c+2);\r\n\t\t\tbreak;\r\n\r\n\t\tcase OP_MUL_V:\r\n\t\t\t//this is actually a dotproduct\r\n\t\t\t//flds glob[A]\r\n\t\t\tEmitByte(0xd9);EmitByte(0x05);EmitAdr(glob + op[i].a+0);\r\n\t\t\t//fmuls glob[B]\r\n\t\t\tEmitByte(0xd8);EmitByte(0x0d);EmitAdr(glob + op[i].b+0);\r\n\r\n\t\t\t//flds glob[A]\r\n\t\t\tEmitByte(0xd9);EmitByte(0x05);EmitAdr(glob + op[i].a+1);\r\n\t\t\t//fmuls glob[B]\r\n\t\t\tEmitByte(0xd8);EmitByte(0x0d);EmitAdr(glob + op[i].b+1);\r\n\r\n\t\t\t//flds glob[A]\r\n\t\t\tEmitByte(0xd9);EmitByte(0x05);EmitAdr(glob + op[i].a+2);\r\n\t\t\t//fmuls glob[B]\r\n\t\t\tEmitByte(0xd8);EmitByte(0x0d);EmitAdr(glob + op[i].b+2);\r\n\r\n\t\t\t//faddp\r\n\t\t\tEmitByte(0xde);EmitByte(0xc1);\r\n\t\t\t//faddp\r\n\t\t\tEmitByte(0xde);EmitByte(0xc1);\r\n\r\n\t\t\t//fstps glob[C]\r\n\t\t\tEmitByte(0xd9);EmitByte(0x1d);EmitAdr(glob + op[i].c);\r\n\t\t\tbreak;\r\n\r\n\t\tcase OP_EQ_F:\r\n\t\tcase OP_NE_F:\r\n\t\tcase OP_LE_F:\r\n\t\tcase OP_GE_F:\r\n\t\tcase OP_LT_F:\r\n\t\tcase OP_GT_F:\r\n\t\t\t//flds glob[A]\r\n\t\t\tEmitByte(0xd9);EmitByte(0x05);EmitAdr(glob + op[i].b);\r\n\t\t\t//flds glob[B]\r\n\t\t\tEmitByte(0xd9);EmitByte(0x05);EmitAdr(glob + op[i].a);\r\n\t\t\t//fcomip %st(1),%st\r\n\t\t\tEmitByte(0xdf);EmitByte(0xe9);\r\n\t\t\t//fstp %st(0)\t(aka: pop)\r\n\t\t\tEmitByte(0xdd);EmitByte(0xd8);\r\n\r\n\t\t\tj1 = LocalJmp(op[i].op);\r\n\t\t\t{\r\n\t\t\t\tSTOREF(0.0f, glob + op[i].c);\r\n\t\t\t\tj2 = LocalJmp(OP_GOTO);\r\n\t\t\t}\r\n\t\t\t{\r\n\t\t\t\tl1 = LocalLoc();\r\n\t\t\t\tSTOREF(1.0f, glob + op[i].c);\r\n\t\t\t}\r\n\t\t\tl2 = LocalLoc();\r\n\t\t\tLocalJmpLoc(j1,l1);\r\n\t\t\tLocalJmpLoc(j2,l2);\r\n\t\t\tbreak;\r\n\r\n\t\tcase OP_MUL_FV:\r\n\t\tcase OP_MUL_VF:\r\n\t\t\t//\r\n\t\t\t{\r\n\t\t\t\tint v;\r\n\t\t\t\tint f;\r\n\t\t\t\tif (op[i].op == OP_MUL_FV)\r\n\t\t\t\t{\r\n\t\t\t\t\tf = op[i].a;\r\n\t\t\t\t\tv = op[i].b;\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\tv = op[i].a;\r\n\t\t\t\t\tf = op[i].b;\r\n\t\t\t\t}\r\n\r\n\t\t\t\t//flds glob[F]\r\n\t\t\t\tEmitByte(0xd9);EmitByte(0x05);EmitAdr(glob + f);\r\n\r\n\t\t\t\t//flds glob[V0]\r\n\t\t\t\tEmitByte(0xd9);EmitByte(0x05);EmitAdr(glob + v+0);\r\n\t\t\t\t//fmul st(1)\r\n\t\t\t\tEmitByte(0xd8);EmitByte(0xc9);\r\n\t\t\t\t//fstps glob[C]\r\n\t\t\t\tEmitByte(0xd9);EmitByte(0x1d);EmitAdr(glob + op[i].c+0);\r\n\r\n\t\t\t\t//flds glob[V0]\r\n\t\t\t\tEmitByte(0xd9);EmitByte(0x05);EmitAdr(glob + v+1);\r\n\t\t\t\t//fmul st(1)\r\n\t\t\t\tEmitByte(0xd8);EmitByte(0xc9);\r\n\t\t\t\t//fstps glob[C]\r\n\t\t\t\tEmitByte(0xd9);EmitByte(0x1d);EmitAdr(glob + op[i].c+1);\r\n\r\n\t\t\t\t//flds glob[V0]\r\n\t\t\t\tEmitByte(0xd9);EmitByte(0x05);EmitAdr(glob + v+2);\r\n\t\t\t\t//fmul st(1)\r\n\t\t\t\tEmitByte(0xd8);EmitByte(0xc9);\r\n\t\t\t\t//fstps glob[C]\r\n\t\t\t\tEmitByte(0xd9);EmitByte(0x1d);EmitAdr(glob + op[i].c+2);\r\n\r\n\t\t\t\t//fstp %st(0)\t(aka: pop)\r\n\t\t\t\tEmitByte(0xdd);EmitByte(0xd8);\r\n\t\t\t}\r\n\t\t\tbreak;\r\n\r\n\t\tcase OP_STATE:\r\n\t\t\t//externs->stateop(progfuncs, OPA->_float, OPB->function);\r\n\t\t\t//push b\r\n\t\t\tEmitByte(0xff);EmitByte(0x35);EmitAdr(glob + op[i].b);\r\n\t\t\t//push a\r\n\t\t\tEmitByte(0xff);EmitByte(0x35);EmitAdr(glob + op[i].a);\r\n\t\t\t//push $progfuncs\r\n\t\t\tEmitByte(0x68); EmitAdr(progfuncs);\r\n\t\t\t//call externs->stateop\r\n\t\t\tEmitByte(0xe8); EmitFOffset(externs->stateop, 4);\r\n\t\t\t//add $12,%esp\r\n\t\t\tEmitByte(0x83); EmitByte(0xc4); EmitByte(0x0c);\r\n\t\t\tbreak;\r\n#if 1\r\n/*\t\tcase OP_NOT_V:\r\n\t\t\t//flds 0\r\n\t\t\t//flds glob[A+0]\r\n\t\t\t//fcomip %st(1),%st\r\n\t\t\t//jne _true\r\n\t\t\t//flds glob[A+1]\r\n\t\t\t//fcomip %st(1),%st\r\n\t\t\t//jne _true\r\n\t\t\t//flds glob[A+1]\r\n\t\t\t//fcomip %st(1),%st\r\n\t\t\t//jne _true\r\n\t\t\t//mov 1,C\r\n\t\t\t//jmp done\r\n\t\t\t//_true:\r\n\t\t\t//mov 0,C\r\n\t\t\t//done:\r\n\t\t\tbreak;\r\n*/\r\n\t\t\t\r\n\t\tcase OP_NOT_V:\r\n\t\t\tEmitByte(0xcd);EmitByte(op[i].op);\r\n\t\t\tprintf(\"QCJIT: instruction %i is not implemented\\n\", op[i].op);\r\n\t\t\tbreak;\r\n#endif\r\n\t\tcase OP_NE_V:\r\n\t\tcase OP_EQ_V:\r\n\t\t{\r\n\t\t\tvoid *f0, *f1, *f2, *floc;\r\n//compare v[0]\r\n\t\t\t//flds glob[A]\r\n\t\t\tEmitByte(0xd9);EmitByte(0x05);EmitAdr(glob + op[i].a+0);\r\n\t\t\t//flds glob[B]\r\n\t\t\tEmitByte(0xd9);EmitByte(0x05);EmitAdr(glob + op[i].b+0);\r\n\t\t\t//fcomip %st(1),%st\r\n\t\t\tEmitByte(0xdf);EmitByte(0xe9);\r\n\t\t\t//fstp %st(0)\t(aka: pop)\r\n\t\t\tEmitByte(0xdd);EmitByte(0xd8);\r\n\r\n\t\t\t/*if the condition is true, don't fail*/\r\n\t\t\tj1 = LocalJmp(op[i].op);\r\n\t\t\t{\r\n\t\t\t\tSTOREF(0.0f, glob + op[i].c);\r\n\t\t\t\tf0 = LocalJmp(OP_GOTO);\r\n\t\t\t}\r\n\t\t\tl1 = LocalLoc();\r\n\t\t\tLocalJmpLoc(j1,l1);\r\n\r\n//compare v[1]\r\n\t\t\t//flds glob[A]\r\n\t\t\tEmitByte(0xd9);EmitByte(0x05);EmitAdr(glob + op[i].a+1);\r\n\t\t\t//flds glob[B]\r\n\t\t\tEmitByte(0xd9);EmitByte(0x05);EmitAdr(glob + op[i].b+1);\r\n\t\t\t//fcomip %st(1),%st\r\n\t\t\tEmitByte(0xdf);EmitByte(0xe9);\r\n\t\t\t//fstp %st(0)\t(aka: pop)\r\n\t\t\tEmitByte(0xdd);EmitByte(0xd8);\r\n\r\n\t\t\t/*if the condition is true, don't fail*/\r\n\t\t\tj1 = LocalJmp(op[i].op);\r\n\t\t\t{\r\n\t\t\t\tSTOREF(0.0f, glob + op[i].c);\r\n\t\t\t\tf1 = LocalJmp(OP_GOTO);\r\n\t\t\t}\r\n\t\t\tl1 = LocalLoc();\r\n\t\t\tLocalJmpLoc(j1,l1);\r\n\r\n//compare v[2]\r\n\t\t\t//flds glob[A]\r\n\t\t\tEmitByte(0xd9);EmitByte(0x05);EmitAdr(glob + op[i].a+2);\r\n\t\t\t//flds glob[B]\r\n\t\t\tEmitByte(0xd9);EmitByte(0x05);EmitAdr(glob + op[i].b+2);\r\n\t\t\t//fcomip %st(1),%st\r\n\t\t\tEmitByte(0xdf);EmitByte(0xe9);\r\n\t\t\t//fstp %st(0)\t(aka: pop)\r\n\t\t\tEmitByte(0xdd);EmitByte(0xd8);\r\n\r\n\t\t\t/*if the condition is true, don't fail*/\r\n\t\t\tj1 = LocalJmp(op[i].op);\r\n\t\t\t{\r\n\t\t\t\tSTOREF(0.0f, glob + op[i].c);\r\n\t\t\t\tf2 = LocalJmp(OP_GOTO);\r\n\t\t\t}\r\n\t\t\tl1 = LocalLoc();\r\n\t\t\tLocalJmpLoc(j1,l1);\r\n\r\n//success!\r\n\t\t\tSTOREF(1.0f, glob + op[i].c);\r\n\r\n\t\t\tfloc = LocalLoc();\r\n\t\t\tLocalJmpLoc(f0,floc);\r\n\t\t\tLocalJmpLoc(f1,floc);\r\n\t\t\tLocalJmpLoc(f2,floc);\r\n\t\t\tbreak;\r\n\t\t}\r\n\r\n\t\t/*fteqcc generates these from reading 'fast arrays', and are part of hexenc extras*/\r\n\t\tcase OP_FETCH_GBL_F:\r\n\t\tcase OP_FETCH_GBL_S:\r\n\t\tcase OP_FETCH_GBL_E:\r\n\t\tcase OP_FETCH_GBL_FNC:\r\n\t\tcase OP_FETCH_GBL_V:\r\n\t\t{\r\n\t\t\tunsigned int max = ((unsigned int*)glob)[op[i].a-1];\r\n\t\t\tunsigned int base = op[i].a;\r\n\t\t\t//flds glob[B]\r\n\t\t\tEmitByte(0xd9); EmitByte(0x05);EmitAdr(glob + op[i].b);\r\n\t\t\t//fistp ta\r\n\t\t\tEmitByte(0xdb); EmitByte(0x1d);EmitAdr(&ta);\r\n\t\t\tLOADREG(&ta, REG_EAX)\r\n\t\t\t//FIXME: if eax >= $max, abort\r\n\r\n\t\t\tif (op[i].op == OP_FETCH_GBL_V)\r\n\t\t\t{\r\n\t\t\t\t/*scale the index by 3*/\r\n\t\t\t\tSETREGI(3, REG_EDX)\r\n\t\t\t\t//mul %edx\r\n\t\t\t\tEmitByte(0xf7); EmitByte(0xe2);\r\n\t\t\t}\r\n\r\n\t\t\t//lookup global\r\n\t\t\t//mov &glob[base](,%eax,4),%edx\r\n\t\t\tEmitByte(0x8b);EmitByte(0x14);EmitByte(0x85);Emit4Byte((unsigned int)(glob + base+0));\r\n\t\t\tSTOREREG(REG_EDX, glob + op[i].c+0)\r\n\t\t\tif (op[i].op == OP_FETCH_GBL_V)\r\n\t\t\t{\r\n\t\t\t\t//mov &glob[base+1](,%eax,4),%edx\r\n\t\t\t\tEmitByte(0x8b);EmitByte(0x14);EmitByte(0x85);Emit4Byte((unsigned int)(glob + base+1));\r\n\t\t\t\tSTOREREG(REG_EDX, glob + op[i].c+1)\r\n\t\t\t\t//mov &glob[base+2](,%eax,4),%edx\r\n\t\t\t\tEmitByte(0x8b);EmitByte(0x14);EmitByte(0x85);Emit4Byte((unsigned int)(glob + base+2));\r\n\t\t\t\tSTOREREG(REG_EDX, glob + op[i].c+2)\r\n\t\t\t}\r\n\t\t\tbreak;\r\n\t\t}\r\n\r\n\t\t/*fteqcc generates these from writing 'fast arrays'*/\r\n\t\tcase OP_GLOBALADDRESS:\r\n\t\t\tLOADREG(glob + op[i].b, REG_EAX);\r\n\t\t\t//lea &glob[A](, %eax, 4),%eax\r\n\t\t\tEmitByte(0x8d);EmitByte(0x04);EmitByte(0x85);EmitAdr(glob + op[i].b+2);\r\n\t\t\tSTOREREG(REG_EAX, glob + op[i].c);\r\n\t\t\tbreak;\r\n//\t\tcase OP_BOUNDCHECK:\r\n\t\t\t//FIXME: assert b <= a < c\r\n\t\t\tbreak;\r\n\t\tcase OP_CONV_FTOI:\r\n\t\t\t//flds glob[A]\r\n\t\t\tEmitByte(0xd9); EmitByte(0x05);EmitAdr(glob + op[i].a);\r\n\t\t\t//fistp glob[C]\r\n\t\t\tEmitByte(0xdb); EmitByte(0x1d);EmitAdr(glob + op[i].c);\r\n\t\t\tbreak;\r\n\t\tcase OP_MUL_I:\r\n\t\t\tLOADREG(glob + op[i].a, REG_EAX);\r\n\t\t\t//mull glob[C]       (arg*eax => edx:eax)\r\n\t\t\tEmitByte(0xfc); EmitByte(0x25);EmitAdr(glob + op[i].b);\r\n\t\t\tSTOREREG(REG_EAX, glob + op[i].c);\r\n\t\t\tbreak;\r\n\r\n\t\t/*other extended opcodes*/\r\n\t\tcase OP_BITOR_I:\r\n\t\t\tLOADREG(glob + op[i].a, REG_EAX)\r\n\t\t\t//or %eax,tb\r\n\t\t\tEmitByte(0x0b); EmitByte(0x05);EmitAdr(glob + op[i].b);\r\n\t\t\tSTOREREG(REG_EAX, glob + op[i].c);\r\n\t\t\tbreak;\r\n\r\n\r\n\t\tdefault:\r\n\t\t\t{\r\n\t\t\t\tenum qcop_e e = op[i].op;\r\n\t\t\tprintf(\"QCJIT: Extended instruction set %i is not supported, not using jit.\\n\", e);\r\n\t\t\t}\r\n\r\n\r\n\t\t\tfailed = true;\r\n\t\t\tbreak;\r\n\t\t}\r\n\t}\r\n\r\n\tif(failed)\r\n\t{\t\r\n\t\tfree(jit->statementjumps);\t//[MAX_STATEMENTS]\r\n\t\tfree(jit->statementoffsets); //[MAX_STATEMENTS]\r\n\t\tfree(jit->code);\r\n\t\tfree(jit);\r\n\t\treturn NULL;\r\n\t}\r\n\r\n\tFixupJumps(jit);\r\n\r\n\t/* most likely want executable memory calls somewhere else more common */\r\n#ifdef _WIN32\r\n\t{\r\n\t\tDWORD old;\r\n\r\n\t\t//this memory is on the heap.\r\n\t\t//this means that we must maintain read/write protection, or libc will crash us\r\n\t\tVirtualProtect(jit->code, jit->codesize, PAGE_EXECUTE_READWRITE, &old);\r\n\t}\r\n#else\r\n\tmprotect(jit->code, jit->codesize, PROT_READ|PROT_EXEC);\r\n#endif\r\n\r\n//\texterns->WriteFile(\"jit.x86\", jit->code, jit->codesize);\r\n\r\n\treturn jit;\r\n}\r\n\r\nstatic float foo(float arg)\r\n{\r\n\tfloat f;\r\n\tif (!arg)\r\n\t\tf = 1;\r\n\telse\r\n\t\tf = 0;\r\n\treturn f;\r\n}\r\n\r\nvoid PR_EnterJIT(progfuncs_t *progfuncs, struct jitstate *jit, int statement)\r\n{\r\n#ifdef __GNUC__\r\n\t//call, it clobbers pretty much everything.\r\n\tasm(\"call *%0\" :: \"r\"(jit->statementoffsets[statement+1]),\"b\"(prinst->edicttable):\"cc\",\"memory\",\"eax\",\"ecx\",\"edx\",\"esi\",\"edi\");\r\n#elif defined(_MSC_VER)\r\n\tvoid *entry = jit->statementoffsets[statement+1];\r\n\tvoid *edicttable = prinst.edicttable;\r\n\t__asm {\r\n\t\tpushad\r\n\t\tmov eax,entry\r\n\t\tmov ebx,edicttable\r\n\t\tcall eax\r\n\t\tpopad\r\n\t}\r\n#else\r\n\t#error \"Sorry, no idea how to enter assembler safely for your compiler\"\r\n#endif\r\n}\r\n#endif\r\n"
  },
  {
    "path": "engine/qclib/progsint.h",
    "content": "#ifndef PROGSINT_H_INCLUDED\n#define PROGSINT_H_INCLUDED\n\n#ifdef _WIN32\n\t#ifndef _CRT_SECURE_NO_WARNINGS\n\t\t#define _CRT_SECURE_NO_WARNINGS\n\t#endif\n\t#define _CRT_NONSTDC_NO_WARNINGS\n\t#ifndef _CRT_SECURE_NO_DEPRECATE\n\t\t#define _CRT_SECURE_NO_DEPRECATE\n\t#endif\n\t#ifndef _CRT_NONSTDC_NO_DEPRECATE\n\t\t#define _CRT_NONSTDC_NO_DEPRECATE\n\t#endif\n\t#ifndef AVAIL_ZLIB\n\t\t#ifdef _MSC_VER\n\t\t\t//#define AVAIL_ZLIB\n\t\t#endif\n\t#endif\n#ifndef _XBOX\n\t#include <windows.h>\n#else \n\t#include <xtl.h>\n#endif\n#else\n\t#include <stdarg.h>\n\t#include <math.h>\n\n\t#include <stdlib.h>\n\t#include <setjmp.h>\n\t#include <string.h>\n\t#include <ctype.h>\n\n\t#ifndef __declspec\n\t\t#define __declspec(mode)\n\t#endif\n//#define _inline inline\n#endif\ntypedef unsigned char pbyte;\n#include <stdio.h>\n\n#define DLL_PROG\n#ifndef PROGSUSED\n#define PROGSUSED\n#endif\n\n#define false 0\n#define true 1\n\n#include \"progtype.h\"\n#include \"progslib.h\"\n\n#include \"pr_comp.h\"\n\n#ifndef safeswitch\n\t//safeswitch(foo){safedefault: break;}\n\t//switch, but errors for any omitted enum values despite the presence of a default case.\n\t//(gcc will generally give warnings without the default, but sometimes you don't have control over the source of your enumeration values)\n\t#if (__GNUC__ >= 4)\n\t\t#define safeswitch\t\\\n\t\t\t_Pragma(\"GCC diagnostic push\")\t\\\n\t\t\t_Pragma(\"GCC diagnostic error \\\"-Wswitch-enum\\\"\") \\\n\t\t\t_Pragma(\"GCC diagnostic error \\\"-Wswitch-default\\\"\") \\\n\t\t\tswitch\n\t\t#define safedefault _Pragma(\"GCC diagnostic pop\") default\n\t#else\n\t\t#define safeswitch switch\n\t\t#define safedefault default\n\t#endif\n#endif\n\n\n#ifdef _MSC_VER\n#pragma warning(disable : 4244)\n#pragma warning(disable : 4267)\n#endif\n\n#ifndef stricmp\n#ifdef _WIN32\n\t//Windows-specific...\n\t#define stricmp _stricmp\n\t#define strnicmp _strnicmp\n#else\n\t//Posix\n\t#define stricmp strcasecmp\n\t#define strnicmp strncasecmp\n#endif\n#endif\n\n//extern progfuncs_t *progfuncs;\ntypedef struct sharedvar_s\n{\n\tint varofs;\n\tint size;\n} sharedvar_t;\ntypedef struct\n{\n\tmfunction_t\t\t*f;\n\tunsigned char\tstepping;\n\tunsigned char\tprogsnum;\n\tint\t\t\t\ts;\n\tint\t\t\t\tpushed;\n\tprclocks_t\t\ttimestamp;\n} prstack_t;\n\n#if defined(QCGC) && defined(MULTITHREAD)\n\t#define THREADEDGC\n#endif\n\ntypedef struct\n{\n\tunsigned int size;\t\t\t//size of the data.\n\tchar value[4];\t\t\t\t//contents of the tempstring (or really any binary data - but not tempstring references because we don't mark these!).\n} tempstr_t;\n\n//FIXME: the defines hidden inside this structure are evil.\ntypedef struct prinst_s\n {\n\t//temp strings are GCed, and can be created by engine, builtins, or just by ent parsing code.\n\ttempstr_t **tempstrings;\n\tunsigned int maxtempstrings;\n#if defined(QCGC)\n\tunsigned int nexttempstring;\n\tunsigned int livetemps;\t//increased on alloc, decremented after sweep\n\t#ifdef THREADEDGC\n\t\tstruct qcgccontext_s *gccontext;\n\t#endif\n#else\n\tunsigned int numtempstrings;\n\tunsigned int numtempstringsstack;\n#endif\n\n\t//alloced strings are generally used to direct strings outside of the vm itself, sharing them with general engine state.\n\tchar **allocedstrings;\n\tint maxallocedstrings;\n\tint numallocedstrings;\n\n\tstruct progstate_s * progstate;\n#define pr_progstate prinst.progstate\n\tunsigned int maxprogs;\n\n\tprogsnum_t pr_typecurrent;\t//active index into progstate array. fixme: remove in favour of only using current_progstate\n\n\tstruct progstate_s *current_progstate;\n#define current_progstate prinst.current_progstate\n\n\tchar * watch_name;\n\teval_t * watch_ptr;\n\teval_t watch_old;\n\tetype_t watch_type;\n\n\tunsigned int numshares;\n\tsharedvar_t *shares;\t//shared globals, not including parms\n\tunsigned int maxshares;\n\n\tstruct prmemb_s     *memblocks;\n\n\tunsigned int maxfields;\n\tunsigned int numfields;\n\tfdef_t *field;\t//biggest size\n\n\tint reorganisefields;\n\n\n//pr_exec.c\n\t//call stack\n#define\tMAX_STACK_DEPTH\t\t1024\t//insanely high value requried for xonotic.\n\tprstack_t pr_stack[MAX_STACK_DEPTH];\n\tint pr_depth;\n\n\t//locals\n#define\tLOCALSTACK_SIZE\t\t(65536*16)\t//in words\n\tint *localstack;\n\tint localstack_used;\n\tint spushed; //extra\n\n\t//step-by-step debug state\n\tint debugstatement;\n\tint exitdepth;\n\n\tpbool profiling;\n\tprclocks_t profilingalert;\t//one second, in cpu clocks\n\tmfunction_t\t*pr_xfunction;\t//active function\n\tint pr_xstatement;\t\t\t//active statement\n\n//pr_edict.c\n\tevalc_t spawnflagscache;\n\tunsigned int fields_size;\t// in bytes\n\tunsigned int max_fields_size;\n\n\n//initlib.c\n\tint mfreelist;\n\tchar * addressablehunk;\n\tsize_t addressableused;\n\tsize_t addressablesize;\n\n\tunsigned int maxedicts;\n\tstruct edictrun_s **edicttable;\n} prinst_t;\n\ntypedef struct progfuncs_s\n{\n\tstruct pubprogfuncs_s funcs;\n\tstruct prinst_s\tinst;\t//private fields. Leave alone.\n} progfuncs_t;\n\n#define prinst progfuncs->inst\n#define externs progfuncs->funcs.parms\n\n#include \"qcd.h\"\n\n#define STRING_SPECMASK\t0xc0000000\t//\n#define STRING_TEMP\t\t0x80000000\t//temp string, will be collected.\n#define STRING_STATIC\t0xc0000000\t//pointer to non-qcvm string.\n#define STRING_NORMAL_\t0x00000000\t//stringtable/mutable. should always be a fallthrough\n#define STRING_NORMAL2_\t0x40000000\t//stringtable/mutable. should always be a fallthrough\n\ntypedef struct\n{\n\tint\t\t\ttargetflags;\t//weather we need to mark the progs as a newer version\n\tchar\t\t*name;\n\tchar\t\t*opname;\n\tint\t\tpriorityclass;\n\tenum {ASSOC_LEFT, ASSOC_RIGHT, ASSOC_RIGHT_RESULT}\t\t\tassociative;\n\tstruct QCC_type_s\t\t**type_a, **type_b, **type_c;\n\n\tunsigned int flags;\t//OPF_*\n\t//ASSIGNS_B\n\t//ASSIGNS_IB\n\t//ASSIGNS_C\n\t//ASSIGNS_IC\n} QCC_opcode_t;\nextern\tQCC_opcode_t\tpr_opcodes[];\t\t// sized by initialization\n#define OPF_VALID\t\t0x001\t//we're allowed to use this opcode in the current target.\n#define OPF_STD\t\t\t0x002\t//reads a+b, writes c.\n#define OPF_STORE\t\t0x010\t//b+=a or just b=a\n#define OPF_STOREPTR\t0x020\t//the form of c=(*b+=a)\n#define OPF_STOREPTROFS\t0x040\t//a[c] <- b   (c must be 0 when QCC_OPCode_StorePOffset returns false)\n#define OPF_STOREFLD\t0x080\t//a.b <- c\n#define OPF_LOADPTR\t\t0x100\n#define OPF_STDUNARY\t0x200\t//reads a, writes c.\n//FIXME: add jumps\n\n\n\n#if defined(_MSC_VER) && _MSC_VER < 1900\n#define Q_vsnprintf _vsnprintf\n#else\n#define Q_vsnprintf vsnprintf\n#endif\n\n#ifndef max\n#define max(a,b) ((a) > (b) ? (a) : (b))\n#define min(a,b) ((a) < (b) ? (a) : (b))\n#endif\n\n#define sv_num_edicts (*externs->num_edicts)\n#define sv_edicts (*externs->edicts)\n\n#define PR_DPrintf externs->DPrintf\n//#define printf syntax error\n//#define Sys_Error externs->Sys_Error\n\nint PRHunkMark(progfuncs_t *progfuncs);\nvoid PRHunkFree(progfuncs_t *progfuncs, int mark);\nvoid *PRHunkAlloc(progfuncs_t *progfuncs, int size, const char *name);\nvoid *PRAddressableExtend(progfuncs_t *progfuncs, void *src, size_t srcsize, int pad);\n\n#ifdef printf\n#undef LIKEPRINTF\n#define LIKEPRINTF(x)\n#endif\n\n//void *HunkAlloc (int size);\nchar *VARGS qcva (char *text, ...) LIKEPRINTF(1);\nvoid QC_InitShares(progfuncs_t *progfuncs);\nvoid QC_StartShares(progfuncs_t *progfuncs);\nvoid PDECL QC_AddSharedVar(pubprogfuncs_t *progfuncs, int num, int type);\nvoid PDECL QC_AddSharedFieldVar(pubprogfuncs_t *progfuncs, int num, char *stringtable);\nvoid QC_AddFieldGlobal(pubprogfuncs_t *ppf, int *globdata);\nint PDECL QC_RegisterFieldVar(pubprogfuncs_t *progfuncs, unsigned int type, const char *name, signed long requestedpos, signed long originalofs);\npbool PDECL QC_Decompile(pubprogfuncs_t *progfuncs, const char *fname);\nint PDECL PR_ToggleBreakpoint(pubprogfuncs_t *progfuncs, const char *filename, int linenum, int flag);\nvoid    StripExtension (char *path);\n\n\n#define edvars(ed) (((edictrun_t*)ed)->fields)\t//pointer to the field vars, given an edict\n\n\nvoid SetEndian(void);\nextern short   (*PRBigShort) (short l);\nextern short   (*PRLittleShort) (short l);\nextern int     (*PRBigLong) (int l);\nextern int     (*PRLittleLong) (int l);\nextern float   (*PRBigFloat) (float l);\nextern float   (*PRLittleFloat) (float l);\n\n\n\n/*\n#ifndef COMPILER\ntypedef union eval_s\n{\n\tstring_t\t\tstring;\n\tfloat\t\t\t_float;\n\tfloat\t\t\tvector[3];\n\tfunc_t\t\t\tfunction;\n\tint\t\t\t\t_int;\n\tint\t\t\t\tedict;\n\tprogsnum_t\t\tprog;\t//so it can easily be changed\n} eval_t;\n#endif\n*/\ntypedef struct edictrun_s\n{\n\tenum ereftype_e\tereftype;\n\tfloat\t\t\tfreetime;\t\t\t// realtime when the object was freed\n\tunsigned int\tentnum;\n\tunsigned int\tfieldsize;\n\tpbool\t\t\treadonly;\t//causes error when QC tries writing to it. (quake's world entity)\n\tvoid\t\t\t*fields;\n\n// other fields from progs come immediately after\n} edictrun_t;\n\n\nint PDECL Comp_Begin(pubprogfuncs_t *progfuncs, int nump, const char **parms);\nint PDECL Comp_Continue(pubprogfuncs_t *progfuncs);\n\npbool PDECL PR_SetWatchPoint(pubprogfuncs_t *progfuncs, const char *desc, const char *location);\nchar *PDECL PR_EvaluateDebugString(pubprogfuncs_t *progfuncs, const char *key);\nchar *PDECL PR_SaveEnts(pubprogfuncs_t *progfuncs, char *mem, size_t *size, size_t maxsize, int mode);\nint PDECL PR_LoadEnts(pubprogfuncs_t *ppf, const char *file, void *ctx, void (PDECL *memoryreset) (pubprogfuncs_t *progfuncs, void *ctx), void (PDECL *entspawned) (pubprogfuncs_t *progfuncs, struct edict_s *ed, void *ctx, const char *entstart, const char *entend), pbool(PDECL *extendedterm)(pubprogfuncs_t *progfuncs, void *ctx, const char **extline));\nchar *PDECL PR_SaveEnt (pubprogfuncs_t *progfuncs, char *buf, size_t *size, size_t maxsize, struct edict_s *ed);\nstruct edict_s *PDECL PR_RestoreEnt (pubprogfuncs_t *progfuncs, const char *buf, size_t *size, struct edict_s *ed);\nvoid PDECL PR_StackTrace (pubprogfuncs_t *progfuncs, int showlocals);\n\neval_t *PR_GetReadTempStringPtr(progfuncs_t *progfuncs, string_t str, size_t offset, size_t datasize);\neval_t *PR_GetWriteTempStringPtr(progfuncs_t *progfuncs, string_t str, size_t offset, size_t datasize);\n\nextern int noextensions;\n\ntypedef enum\n{\n\tPST_DEFAULT,//everything 16bit\n\tPST_FTE32,\t//everything 32bit\n\tPST_KKQWSV, //32bit statements, 16bit globaldefs. NO SAVED GAMES.\n\tPST_QTEST,\t//16bit statements, 32bit globaldefs(other differences converted on load)\n\tPST_UHEXEN2,//everything 32bit like fte's without a header, but with pre-padding rather than post-extended (little-endian) types.\n} progstructtype_t;\n\n#ifndef COMPILER\ntypedef struct progstate_s\n{\n\tdprograms_t\t\t*progs;\n\tmfunction_t\t\t*functions;\n\tchar\t\t\t*strings;\n\tunion {\n\t\tddefXX_t\t\t*globaldefs;\n\t\tddef16_t\t\t*globaldefs16;\t//vanilla, kk\n\t\tddef32_t\t\t*globaldefs32;\t//fte, qtest\n\t};\n\tunion {\n\t\tddefXX_t\t\t*fielddefs;\n\t\tddef16_t\t\t*fielddefs16;\t//vanilla, kk\n\t\tddef32_t\t\t*fielddefs32;\t//fte, qtest\n\t};\n//\tunion {\n\t\tvoid\t*statements;\n//\t\tdstatement16_t *statements16;\t//vanilla, qtest\n//\t\tdstatement32_t *statements32;\t//fte, kk\n//\t};\n//\tvoid\t\t\t*global_struct;\n\tfloat\t\t\t*globals;\t\t\t// same as pr_global_struct\n\tunsigned int\tglobals_bytes;\t// in bytes\n\n\ttypeinfo_t\t*types;\n\n\tint\t\t\t\tedict_size;\t// in bytes\n\n\tchar\t\t\tfilename[128];\n\n\tint *linenums;\t//debug versions only\n\n\tprogstructtype_t structtype;\t//specifies the sized struct types above. FIXME: should probably just load as 32bit or something.\n\n#ifdef QCJIT\n\tstruct jitstate *jit;\n#endif\n} progstate_t;\n\n//============================================================================\n\n\n#define pr_progs\t\t\tcurrent_progstate->progs\n#define\tpr_cp_functions\t\tcurrent_progstate->functions\n#define\tpr_strings\t\t\tcurrent_progstate->strings\n#define\tpr_globaldefs16\t\t((ddef16_t*)current_progstate->globaldefs16)\n#define\tpr_globaldefs32\t\t((ddef32_t*)current_progstate->globaldefs32)\n#define\tpr_fielddefs16\t\t((ddef16_t*)current_progstate->fielddefs16)\n#define\tpr_fielddefs32\t\t((ddef32_t*)current_progstate->fielddefs32)\n#define\tpr_statements16\t\t((dstatement16_t*)current_progstate->statements)\n#define\tpr_statements32\t\t((dstatement32_t*)current_progstate->statements)\n//#define\tpr_global_struct\tcurrent_progstate->global_struct\n#define pr_globals\t\t\tcurrent_progstate->globals\n#define pr_linenums\t\t\tcurrent_progstate->linenums\n#define pr_types\t\t\tcurrent_progstate->types\n\n\n\n//============================================================================\n\nvoid PR_Init (void);\n\npbool PR_RunWarning (pubprogfuncs_t *progfuncs, char *error, ...);\n\nvoid PDECL PR_ExecuteProgram (pubprogfuncs_t *progfuncs, func_t fnum);\nprogsnum_t PDECL PR_LoadProgs(pubprogfuncs_t *progfncs, const char *s);\npbool PR_ReallyLoadProgs (progfuncs_t *progfuncs, const char *filename, progstate_t *progstate, pbool complain);\n\nvoid *PRHunkAlloc(progfuncs_t *progfuncs, int ammount, const char *name);\n\nvoid PR_Profile_f (void);\n\nstruct edict_s *PDECL ED_Alloc (pubprogfuncs_t *progfuncs, pbool object, size_t extrasize);\nstruct edict_s *PDECL ED_AllocIndex (pubprogfuncs_t *progfuncs, unsigned int num, pbool object, size_t extrasize);\nvoid PDECL ED_Free (pubprogfuncs_t *progfuncs, struct edict_s *ed, pbool instant);\n\n#ifdef QCGC\nvoid PR_RunGC\t\t\t(progfuncs_t *progfuncs);\n#else\nvoid PR_FreeTemps\t\t\t(progfuncs_t *progfuncs, int depth);\n#endif\n\nstring_t PDECL PR_AllocTempString\t\t\t(pubprogfuncs_t *ppf, const char *str);\nchar *PDECL ED_NewString (pubprogfuncs_t *ppf, const char *string, int minlength, pbool demarkup);\n// returns a copy of the string allocated from the server's string heap\n\nvoid PDECL ED_Print (pubprogfuncs_t *progfuncs, struct edict_s *ed);\n//void ED_Write (FILE *f, edictrun_t *ed);\n\n//void ED_WriteGlobals (FILE *f);\nvoid ED_ParseGlobals (char *data);\n\n//void ED_LoadFromFile (char *data);\n\n//define EDICT_NUM(n) ((edict_t *)(sv.edicts+ (n)*pr_edict_size))\n//define NUM_FOR_EDICT(e) (((byte *)(e) - sv.edicts)/pr_edict_size)\n\nstruct edict_s *PDECL QC_EDICT_NUM(pubprogfuncs_t *progfuncs, unsigned int n);\nunsigned int PDECL QC_NUM_FOR_EDICT(pubprogfuncs_t *progfuncs, struct edict_s *e);\n\n#define EDICT_NUM(pf, num)\tQC_EDICT_NUM(&pf->funcs,num)\n#define NUM_FOR_EDICT(pf, e) QC_NUM_FOR_EDICT(&pf->funcs,e)\n\n//#define\tNEXT_EDICT(e) ((edictrun_t *)( (byte *)e + pr_edict_size))\n\n#define\tEDICT_TO_PROG(pf, e) (((edictrun_t*)e)->entnum)\n#define PROG_TO_EDICT_PB(pf, e) ((struct edictrun_s *)prinst.edicttable[e])\t//index already validated\n#define PROG_TO_EDICT_UB(pf, e) ((struct edictrun_s *)prinst.edicttable[((unsigned int)(e)<prinst.maxedicts)?e:0])\t//(safely) pokes world if the index is otherwise invalid\n\n//============================================================================\n\n#define\tG_FLOAT(o) (pr_globals[o])\n#define\tG_FLOAT2(o) (pr_globals[OFS_PARM0 + o*3])\n#define\tG_INT(o) (*(int *)&pr_globals[o])\n#define\tG_EDICT(o) ((edict_t *)((qbyte *)sv_edicts+ *(int *)&pr_globals[o]))\n#define G_EDICTNUM(o) NUM_FOR_EDICT(G_EDICT(o))\n#define\tG_VECTOR(o) (&pr_globals[o])\n#define\tG_STRING(o) (*(string_t *)&pr_globals[o])\n#define\tG_FUNCTION(o) (*(func_t *)&pr_globals[o])\n#define G_PROG(o) G_FLOAT(o)\t//simply so it's nice and easy to change...\n\n#define\tRETURN_EDICT(e) (((int *)pr_globals)[OFS_RETURN] = EDICT_TO_PROG(e))\n\n#define\tE_FLOAT(e,o) (((float*)&e->v)[o])\n#define\tE_INT(e,o) (*(int *)&((float*)&e->v)[o])\n#define\tE_VECTOR(e,o) (&((float*)&e->v)[o])\n//#define\tE_STRING(e,o) (*(string_t *)&((float*)(e+1))[o])\n\nextern\tconst unsigned int\t\ttype_size[];\n\n\nextern\tunsigned short\t\tpr_crc;\n\nvoid VARGS PR_RunError (pubprogfuncs_t *progfuncs, const char *error, ...) LIKEPRINTF(2);\n\nvoid ED_PrintEdicts (progfuncs_t *progfuncs);\nvoid ED_PrintNum (progfuncs_t *progfuncs, int ent);\n\n\npbool PR_SwitchProgs(progfuncs_t *progfuncs, progsnum_t type);\npbool PR_SwitchProgsParms(progfuncs_t *progfuncs, progsnum_t newprogs);\n\n\n\n\neval_t *PDECL QC_GetEdictFieldValue(pubprogfuncs_t *progfuncs, struct edict_s *ed, const char *name, etype_t type, evalc_t *cache);\nvoid PDECL PR_GenerateStatementString (pubprogfuncs_t *progfuncs, int statementnum, char *out, int outlen);\nfdef_t *PDECL ED_FieldInfo (pubprogfuncs_t *progfuncs, unsigned int *count);\nchar *PDECL PR_UglyValueString (pubprogfuncs_t *progfuncs, etype_t type, eval_t *val);\npbool\tPDECL ED_ParseEval (pubprogfuncs_t *progfuncs, eval_t *eval, int type, const char *s);\nchar *PR_SaveCallStack (progfuncs_t *progfuncs, char *buf, size_t *bufofs, size_t bufmax);\n\nprclocks_t Sys_GetClockRate(void);\n#endif\n\n\n\n\n#ifndef COMPILER\n\n//this is windows - all files are written with this endian standard\n//optimisation\n//leave undefined if in doubt over os.\n#ifdef _WIN32\n#define NOENDIAN\n#endif\n\n\n\n\n//pr_multi.c\n\nextern pvec3_t pvec3_origin;\n\nstruct qcthread_s *PDECL PR_ForkStack\t(pubprogfuncs_t *progfuncs);\nvoid PDECL PR_ResumeThread\t\t\t(pubprogfuncs_t *progfuncs, struct qcthread_s *thread);\nvoid\tPDECL PR_AbortStack\t\t\t(pubprogfuncs_t *progfuncs);\npbool\tPDECL PR_GetBuiltinCallInfo\t(pubprogfuncs_t *ppf, int *builtinnum, char *function, size_t sizeoffunction);\n\neval_t *PDECL PR_FindGlobal(pubprogfuncs_t *prfuncs, const char *globname, progsnum_t pnum, etype_t *type);\nddef16_t *ED_FindTypeGlobalFromProgs16 (progfuncs_t *progfuncs, progstate_t *ps, const char *name, int type);\nddef32_t *ED_FindTypeGlobalFromProgs32 (progfuncs_t *progfuncs, progstate_t *ps, const char *name, int type);\nddef16_t *ED_FindGlobalFromProgs16 (progfuncs_t *progfuncs, progstate_t *ps, const char *name);\nddef32_t *ED_FindGlobalFromProgs32 (progfuncs_t *progfuncs, progstate_t *ps, const char *name);\nfdef_t *ED_FindField (progfuncs_t *progfuncs, const char *name);\nfdef_t *ED_ClassFieldAtOfs (progfuncs_t *progfuncs, unsigned int ofs, const char *classname);\nfdef_t *ED_FieldAtOfs (progfuncs_t *progfuncs, unsigned int ofs);\nmfunction_t *ED_FindFunction (progfuncs_t *progfuncs, const char *name, progsnum_t *pnum, progsnum_t fromprogs);\nfunc_t PDECL PR_FindFunc(pubprogfuncs_t *progfncs, const char *funcname, progsnum_t pnum);\n//void PDECL PR_Configure (pubprogfuncs_t *progfncs, size_t addressable_size, int max_progs);\nint PDECL PR_InitEnts(pubprogfuncs_t *progfncs, int maxents);\nchar *PR_ValueString (progfuncs_t *progfuncs, etype_t type, eval_t *val, pbool verbose);\nvoid PDECL QC_ClearEdict (pubprogfuncs_t *progfuncs, struct edict_s *ed);\nvoid PRAddressableFlush(progfuncs_t *progfuncs, size_t totalammount);\nvoid QC_FlushProgsOffsets(progfuncs_t *progfuncs);\n\nddef16_t *ED_GlobalAtOfs16 (progfuncs_t *progfuncs, int ofs);\nddef16_t *ED_FindGlobal16 (progfuncs_t *progfuncs, const char *name);\nddef32_t *ED_FindGlobal32 (progfuncs_t *progfuncs, const char *name);\nddef32_t *ED_GlobalAtOfs32 (progfuncs_t *progfuncs, unsigned int ofs);\n\nstring_t PDECL PR_StringToProgs\t\t\t(pubprogfuncs_t *inst, const char *str);\nconst char *ASMCALL PR_StringToNative\t\t\t\t(pubprogfuncs_t *inst, string_t str);\n\nchar *PR_GlobalString (progfuncs_t *progfuncs, int ofs, struct QCC_type_s **typehint);\nchar *PR_GlobalStringNoContents (progfuncs_t *progfuncs, int ofs);\nchar *PR_GlobalStringImmediate (progfuncs_t *progfuncs, int ofs);\n\npbool CompileFile(progfuncs_t *progfuncs, const char *filename);\n\nstruct jitstate;\nstruct jitstate *PR_GenerateJit(progfuncs_t *progfuncs);\nvoid PR_EnterJIT(progfuncs_t *progfuncs, struct jitstate *jitstate, int statement);\nvoid PR_CloseJit(struct jitstate *jit);\n\nchar *QCC_COM_Parse (const char *data);\nextern char\tqcc_token[1024];\nextern char *basictypenames[];\n#endif\n\n#endif\n"
  },
  {
    "path": "engine/qclib/progslib.h",
    "content": "\n#ifndef PROGSLIB_H\n#define PROGSLIB_H\n\n#include \"progtype.h\"\n#include <stdlib.h>\n#ifdef _MSC_VER\n\t#define VARGS __cdecl\n#endif\n#if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1))\n\t#if defined(_WIN32)\n\t\t#include <stdio.h>\n\t\t#ifdef __MINGW_PRINTF_FORMAT\n\t\t\t#define LIKEPRINTF(x) __attribute__((format(__MINGW_PRINTF_FORMAT,x,x+1)))\n\t\t#else\n\t\t\t#define LIKEPRINTF(x) __attribute__((format(ms_printf,x,x+1)))\n\t\t#endif\n\t#else\n\t\t#define LIKEPRINTF(x) __attribute__((format(printf,x,x+1)))\n\t#endif\n#endif\n#ifndef LIKEPRINTF\n\t#define LIKEPRINTF(x)\n#endif\n#ifndef VARGS\n\t#define VARGS\n#endif\n\n#if __STDC_VERSION__ >= 202311L // c23\n\t#define FALLTHROUGH  [[fallthrough]];\n#elif defined(__GNUC__) && __GNUC__ >= 7\n\t#define FALLTHROUGH __attribute__((fallthrough));\n#elif defined(__clang__) && __clang_major__ >= 7\n\t#define FALLTHROUGH __attribute__((fallthrough));\n#else\n\t#define FALLTHROUGH\n#endif\n\n#if defined(_M_IX86) || defined(__i386__)\t//supported arch\n\t#if defined(__GNUC__) || defined(_MSC_VER)\t//supported compilers (yay for inline asm)\n\t//#define QCJIT\n\t#endif\n#endif\n\n#define QCBUILTIN ASMCALL\n\n#ifdef _WIN32\n#define PDECL __cdecl\n#else\n#define PDECL\n#endif\n\n#ifdef QCJIT\n#define ASMCALL VARGS\n#else\n#define ASMCALL PDECL\n#endif\n\n\n#define QCGC\n\nstruct edict_s;\nstruct entvars_s;\nstruct globalvars_s;\nstruct qcthread_s;\ntypedef struct pubprogfuncs_s pubprogfuncs_t;\ntypedef void (ASMCALL *builtin_t) (pubprogfuncs_t *prinst, struct globalvars_s *gvars);\n\nenum ereftype_e\n{\n\tER_ENTITY,\n\tER_FREE,\n\tER_OBJECT\t//custom sized, no vm/engine fields.\n};\n#define ED_ISFREE(e) ((e)->ereftype != ER_ENTITY)\n\n//used by progs engine. All nulls is reset.\ntypedef struct {\n\tconst char *varname;\n\tstruct fdef_s *ofs32;\n\n\tint spare[2];\n} evalc_t;\n#define sizeofevalc sizeof(evalc_t)\ntypedef enum {\n//vanilla types\nev_void,\nev_string,\t//offset into the string table - but if the high bit is set then its probably some special thing.\nev_float,\t//can hold up to 24 bits... sucks, but this is our basic numeric type.\nev_vector,\t//3 floats.\nev_entity,\t//index into the edicts array (vanilla used byte offsets from world).\nev_field,\t//index into the per-entity field table.\nev_function,//all functions are called via reference.\nev_pointer,\t//exists in vanilla - *(&ent.fld) opcodes are valid there - how else would you store to a field?\n//extended types\nev_integer,\t//our first extended type... probably won't help performance much but at least it doesn't have the imprecision issue of floats.\nev_uint,\t//mostly just reuses int opcodes.\nev_int64,\t//large int type, because we can. might be useful for system handles perhaps? dunno, probably not that useful.\nev_uint64,\t//mostly just reuses int64 opcodes.\nev_double,\t//useful for timers, for the extra precision.\n//qc-only types\nev_variant,\t//used primarily for builtin args, or for type punning casts without using pointers. should never be used for a global.\nev_struct,\t//big complex type\nev_union,\t//not really sure why this is separate from struct\nev_accessor,//some weird type to provide class-like functions over a basic type.\nev_enum,\t//just a numeric type\nev_typedef,\t//so typedefs can refer to their original type (primarily for structs).\nev_boolean,\t//exists to optimise if(-0) workarounds. engine just sees int/float. uses parentclass\nev_bitfld,\t//erk... structs only... converted to their parentclass on read.\n} etype_t;\nenum {\n\tDEBUG_TRACE_OFF,\t\t//debugging should be off.\n\tDEBUG_TRACE_INTO,\t\t//debug into functions\n\tDEBUG_TRACE_OVER,\t\t//switch debugging off while executing child functions (and back on afterwards)\n\tDEBUG_TRACE_OUT,\t\t//keep running until the end of the current function (trigger single-stepping again at that point)\n\tDEBUG_TRACE_ABORTERROR,\t//give up with an endgame.\n//\tDEBUG_TRACE_ABORTSTACK,\t//stop executing, without any errors.\n\tDEBUG_TRACE_NORESUME\t//line number or something changed, but we should still be sitting at the debugger.\n};\n\ntypedef struct fdef_s\n{\n\tunsigned int\ttype;\t\t//if DEF_SAVEGLOBAL bit is set then the variable needs to be saved in savegames\n\tint\t\t\t\tofs;\t\t//runtime offset. add fieldadj to get the real array index.\n\tunsigned int\tprogsofs;\t//used at loading time, so maching field offsets (unions/members) are positioned at the same runtime offset.\n\tconst char *\tname;\t\t//proper name for the field.\n} fdef_t;\n\n//the number of pointers to variables (as opposed to functions - those are fine) in these structures is excessive.\n//Many of the functions are also obsolete.\nstruct pubprogfuncs_s\n{\n\tint progsversion;\t//PROGSTRUCT_VERSION\n\n\tvoid\t(PDECL *Shutdown)\t\t\t\t\t(pubprogfuncs_t *inst);\n\n\tvoid\t(PDECL *Configure)\t\t\t\t\t(pubprogfuncs_t *prinst, size_t addressablesize, int max_progs, pbool enableprofiling);\t\t//configure buffers and memory. Used to reset and must be called first. Flushes a running VM.\n\tprogsnum_t\t(PDECL *LoadProgs)\t\t\t\t(pubprogfuncs_t *prinst, const char *s);\t//load a progs\n\tint\t\t(PDECL *InitEnts)\t\t\t\t\t(pubprogfuncs_t *prinst, int max_ents);\t//returns size of edicts for use with nextedict macro\n\tvoid\t(PDECL *ExecuteProgram)\t\t\t\t(pubprogfuncs_t *prinst, func_t fnum);\t//start execution\n\tstruct globalvars_s\t*(PDECL *globals)\t\t(pubprogfuncs_t *prinst, progsnum_t num);\t//get the globals of a progs\n\tstruct entvars_s\t*(PDECL *entvars)\t\t(pubprogfuncs_t *prinst, struct edict_s *ent);\t//return a pointer to the entvars of an ent. can be achieved via the edict_t structure instead, so obsolete.\n\n\tvoid\t(VARGS *RunError)\t\t\t\t\t(pubprogfuncs_t *prinst, const char *msg, ...) LIKEPRINTF(2);\t\t//builtins call this to say there was a problem\n\tvoid\t(PDECL *PrintEdict)\t\t\t\t\t(pubprogfuncs_t *prinst, struct edict_s *ed);\t//get a listing of all vars on an edict (sent back via 'print')\n\n\tstruct edict_s\t*(PDECL *EntAlloc)\t\t\t(pubprogfuncs_t *prinst, pbool object, size_t extrasize);\t\t\t//allocate a random index.\n\tstruct edict_s\t*(PDECL *EntAllocIndex)\t\t(pubprogfuncs_t *prinst, unsigned int idx, pbool object, size_t extrasize);\t//allocate a specific index.\n\tvoid\t(PDECL *EntFree)\t\t\t\t\t(pubprogfuncs_t *prinst, struct edict_s *ed, pbool instant);\n\n\tstruct edict_s\t*(PDECL *EdictNum)\t\t\t(pubprogfuncs_t *prinst, unsigned int n);\t\t//get the nth edict\n\tunsigned int\t\t(PDECL *NumForEdict)\t(pubprogfuncs_t *prinst, struct edict_s *e);\t//so you can find out what that 'n' will be\n\n\tchar\t*(PDECL *VarString)\t\t\t\t\t(pubprogfuncs_t *prinst, int\tfirst);\t//returns a string made up of multiple arguments\n\n\tstruct progstate_s **progstate;\t//internal to the library.\n\tint\t\tnumprogs;\n\n\tfunc_t\t(PDECL *FindFunction)\t\t\t\t(pubprogfuncs_t *prinst, const char *funcname, progsnum_t num);\n\n\tint\t\t(PDECL *StartCompile)\t\t\t\t(pubprogfuncs_t *prinst, int argv, const char **argc);\t//1 if can compile, 0 if failed to compile\n\tint\t\t(PDECL *ContinueCompile)\t\t\t(pubprogfuncs_t *prinst);\t//2 if finished, 1 if more to go, 0 if failed\n\n\tchar\t*(PDECL *filefromprogs)\t\t\t\t(pubprogfuncs_t *prinst, progsnum_t prnum, const char *fname, size_t *size, char *buffer);\t//reveals encoded/added files from already loaded progs\n\tchar\t*(PDECL *filefromnewprogs)\t\t\t(pubprogfuncs_t *prinst, const char *prname, const char *fname, size_t *size, char *buffer);\t//reveals encoded/added files from a progs on the disk somewhere\n\n\tvoid\t(PDECL *ED_Print)\t\t\t\t\t(pubprogfuncs_t *prinst, struct edict_s *ed);\n\tchar\t*(PDECL *save_ents)\t\t\t\t\t(pubprogfuncs_t *prinst, char *buf, size_t *size, size_t maxsize, int mode);\t//dump the entire progs info into one big self allocated string\n\tint\t\t(PDECL *load_ents)\t\t\t\t\t(pubprogfuncs_t *prinst, const char *s, void *ctx,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tvoid (PDECL *memoryreset) (pubprogfuncs_t *progfuncs, void *ctx),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tvoid (PDECL *entspawned) (pubprogfuncs_t *progfuncs, struct edict_s *ed, void *ctx, const char *entstart, const char *entend),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tpbool(PDECL *extendedterm)(pubprogfuncs_t *progfuncs, void *ctx, const char **extline)\n\t\t\t\t\t\t\t\t\t\t\t\t); //restore the entire progs state (or just add some more ents) (returns edicts ize)\n\n\tchar\t*(PDECL *saveent)\t\t\t\t\t(pubprogfuncs_t *prinst, char *buf, size_t *size, size_t maxsize, struct edict_s *ed);\t//will save just one entities vars\n\tstruct edict_s\t*(PDECL *restoreent)\t\t(pubprogfuncs_t *prinst, const char *buf, size_t *size, struct edict_s *ed);\t//will restore the entity that had it's values saved (can use NULL for ed)\n\n\tunion eval_s\t*(PDECL *FindGlobal)\t\t(pubprogfuncs_t *prinst, const char *name, progsnum_t num, etype_t *type);\t//find a pointer to the globals value\n\n\tunion eval_s\t*(PDECL *GetEdictFieldValue)(pubprogfuncs_t *prinst, struct edict_s *ent, const char *name, etype_t type, evalc_t *s); //get an entityvar (cache it) and return the possible values\n\tstruct edict_s\t*(PDECL *ProgsToEdict)\t\t(pubprogfuncs_t *prinst, int progs);\t//edicts are stored as ints and need to be adjusted\n\tint\t\t(PDECL *EdictToProgs)\t\t\t\t(pubprogfuncs_t *prinst, struct edict_s *ed);\t\t//edicts are stored as ints and need to be adjusted\n\n\tchar\t*(PDECL *EvaluateDebugString)\t\t(pubprogfuncs_t *prinst, const char *key);\t//evaluate a string and return it's value (according to current progs) (expands edict vars)\n\tint\t\tdebug_trace;\t//start calling the editor for each line executed\t\n\tvoid\t(PDECL *StackTrace)\t\t\t\t\t(pubprogfuncs_t *prinst, int showlocals);\n\tint\t\t(PDECL *ToggleBreak)\t\t\t\t(pubprogfuncs_t *prinst, const char *filename, int linenum, int mode);\n\n\tstruct\tprogexterns_s *parms;\t//these are the initial parms, they may be changed\n\n\tpbool\t(PDECL *Decompile)\t\t\t\t\t(pubprogfuncs_t *prinst, const char *fname);\n\n\tint\t\tcallargc;\t//number of args of built-in call\n\tint\t\tcallprogs;\t//which progs it was called from...\n\n\tchar *stringtable;\t//qc strings are all relative. add to a qc string. this is required for support of frikqcc progs that strip string immediates.\n\tunsigned int stringtablesize;\n\tunsigned int stringtablemaxsize;\n\tint fieldadjust;\t//FrikQCC style arrays can cause problems due to field remapping. This causes us to leave gaps but offsets identical. except for system fields, qc-addressable variables use their old offsets, this is the bias so that the offset pokes the correct memory.\n\tunsigned int activefieldslots; //f+=fieldadjust; invalidfield = (f<0)||(f+fldsize>=activefieldslots); note that this does NOT apply to 'object' entities which are variable sized, use ed->fieldsize for those.\n\n\n\tstruct qcthread_s *(PDECL *Fork)\t\t\t(pubprogfuncs_t *prinst);\t//returns a pointer to a thread which can be resumed via RunThread.\n\tvoid\t(PDECL *RunThread)\t\t\t\t\t(pubprogfuncs_t *prinst, struct qcthread_s *thread);\n\tvoid\t(PDECL *AbortStack)\t\t\t\t\t(pubprogfuncs_t *prinst);\t//annigilates the current stack, positioning on a return statement. It is expected that this is only used via a builtin!\n\n\tpbool\t(PDECL *GetBuiltinCallInfo)\t\t\t(pubprogfuncs_t *prinst, int *builtinnum, char *function, size_t sizeoffunction);\t//call to query the qc's name+index for the builtin\n\tpbool\t(PDECL *FindBuiltins)\t\t\t\t(pubprogfuncs_t *progfuncs, progsnum_t prnum, int binum, pbool (PDECL *found) (pubprogfuncs_t *progfuncs, const char *name, void *ctx), void *ctx);\t//calls the callback for each function reference that's mapped to the specified builtin number.\n\n\tint (PDECL *RegisterFieldVar)\t\t\t\t(pubprogfuncs_t *prinst, unsigned int type, const char *name, signed long requestedpos, signed long originalofs);\n\n\tchar\t*(PDECL *AddString)\t\t\t\t\t(pubprogfuncs_t *prinst, const char *val, int minlength, pbool demarkup);\t//dump a string into the progs memory (for setting globals and whatnot)\n\tvoid\t*(PDECL *Tempmem)\t\t\t\t\t(pubprogfuncs_t *prinst, int ammount, char *whatfor);\t//grab some mem for as long as the progs stays loaded\n\tvoid *(PDECL *AddressableAlloc)\t\t\t\t(pubprogfuncs_t *progfuncs, unsigned int ammount); /*returns memory within the qc block, use stringtoprogs to get a usable qc pointer/string*/\n\tvoid *(PDECL *AddressableRealloc)\t\t\t(pubprogfuncs_t *progfuncs, void *oldptr, unsigned int newammount); /*returns memory within the qc block, use stringtoprogs to get a usable qc pointer/string*/\n\tvoid (PDECL *AddressableFree)\t\t\t\t(pubprogfuncs_t *progfuncs, void *mem); /*frees a block of addressable memory*/\n\tstring_t (PDECL *TempString)\t\t\t\t(pubprogfuncs_t *prinst, const char *str);\n\tstring_t (PDECL *AllocTempString)\t\t\t(pubprogfuncs_t *prinst, char **str, unsigned int len);\n\tstring_t (PDECL *StringToProgs)\t\t\t\t(pubprogfuncs_t *prinst, const char *str);\t//commonly makes a semi-permanent mapping from some table to the string value. mapping can be removed via RemoveProgsString\n\tconst char *(ASMCALL *StringToNative)\t\t(pubprogfuncs_t *prinst, string_t str);\n\n\tint (PDECL *QueryField)\t\t\t\t\t\t(pubprogfuncs_t *prinst, unsigned int fieldoffset, etype_t *type, char const**name, evalc_t *fieldcache);\t//find info on a field definition at an offset\n\n\tvoid (PDECL *EntClear)\t\t\t\t\t\t(pubprogfuncs_t *progfuncs, struct edict_s *e);\n\tvoid (PDECL *FindPrefixGlobals)\t\t\t\t(pubprogfuncs_t *progfuncs, int prnum, char *prefix, void (PDECL *found) (pubprogfuncs_t *progfuncs, char *name, union eval_s *val, etype_t type, void *ctx), void *ctx);\t//calls the callback for each named global found\n\n\tpbool (PDECL *SetWatchPoint)\t\t\t\t(pubprogfuncs_t *prinst, const char *desc, const char *location);\n\n\tvoid (PDECL *AddSharedVar)\t\t\t\t\t(pubprogfuncs_t *progfuncs, int start, int size);\n\tvoid (PDECL *AddSharedFieldVar)\t\t\t\t(pubprogfuncs_t *progfuncs, int num, char *relstringtable);\n\tchar *(PDECL *RemoveProgsString)\t\t\t(pubprogfuncs_t *progfuncs, string_t str);\n\tpbool (PDECL *GetFunctionInfo)\t\t\t\t(pubprogfuncs_t *progfuncs, func_t func, int *argcount, unsigned char **argsizes, int *builtinnum, char *funcname, size_t funcnamesize); //queries the interesting info from a function def\n\tvoid (PDECL *GenerateStatementString)\t\t(pubprogfuncs_t *progfuncs, int statementnum, char *out, int outlen);\t//disassembles a specific statement. for debugging reports.\n\tfdef_t *(PDECL *FieldInfo)\t\t\t\t\t(pubprogfuncs_t *progfuncs, unsigned int *count);\n\tchar *(PDECL *UglyValueString)\t\t\t\t(pubprogfuncs_t *progfuncs, etype_t type, union eval_s *val);\n\tpbool (PDECL *ParseEval)\t\t\t\t\t(pubprogfuncs_t *progfuncs, union eval_s *eval, int type, const char *s);\n\tvoid (PDECL *SetStringField)\t\t\t\t(pubprogfuncs_t *progfuncs, struct edict_s *ed, string_t *fld, const char *str, pbool str_is_static);\t//if ed is null, fld points to a global. if str_is_static, then s doesn't need its own memory allocated.\n\tpbool (PDECL *DumpProfile)\t\t\t\t\t(pubprogfuncs_t *progfuncs, pbool resetprofiles);\n\n\tunsigned int edicttable_length;\n\tstruct edict_s **edicttable;\n\n\t//stuff not used by the qclib at all, but provided for lazy user storage.\n\tstruct\n\t{\n\t\tchar\t*tempstringbase;\t\t\t\t\t//for engine's use. Store your base tempstring pointer here.\n\t\tint\t\ttempstringnum;\t\t\t\t\t\t//for engine's use.\n\t} user;\n};\n\ntypedef struct progexterns_s {\n\tint progsversion;\t//PROGSTRUCT_VERSION\n\n\tvoid *(PDECL *ReadFile)\t\t\t\t(const char *fname, unsigned char *(PDECL *buf_get)(void *ctx, size_t len), void *buf_ctx, size_t *out_size, pbool issourcefile);\n\tint (PDECL *FileSize)\t\t\t\t(const char *fname);\t//-1 if file does not exist\n\tpbool (PDECL *WriteFile)\t\t\t(const char *name, void *data, int len);\n\tint (VARGS *Printf)\t\t\t\t\t(const char *, ...) LIKEPRINTF(1);\n\tint (VARGS *DPrintf)\t\t\t\t(const char *, ...) LIKEPRINTF(1);\n\tvoid (VARGS *Sys_Error)\t\t\t\t(const char *, ...) LIKEPRINTF(1);\n\tvoid (VARGS *Abort)\t\t\t\t\t(const char *, ...) LIKEPRINTF(1);\n\tpbool (PDECL *CheckHeaderCrc)\t\t(pubprogfuncs_t *inst, progsnum_t idx, int crc, const char *filename);\n\n\tvoid (PDECL *entspawn)\t\t\t\t(struct edict_s *ent, int loading);\t//ent has been spawned, but may not have all the extra variables (that may need to be set) set\n\tpbool (PDECL *entcanfree)\t\t\t(struct edict_s *ent);\t//return true to stop ent from being freed\n\tvoid (ASMCALL *stateop)\t\t\t\t(pubprogfuncs_t *prinst, float var, func_t func);\t//what to do on qc's state opcode.\n\tvoid (ASMCALL *cstateop)\t\t\t(pubprogfuncs_t *prinst, float vara, float varb, func_t currentfunc);\t\t//a hexen2 opcode.\n\tvoid (ASMCALL *cwstateop)\t\t\t(pubprogfuncs_t *prinst, float vara, float varb, func_t currentfunc);\t//a hexen2 opcode.\n\tvoid (ASMCALL *thinktimeop)\t\t\t(pubprogfuncs_t *prinst, struct edict_s *ent, float varb);\t\t\t//a hexen2 opcode.\n\n\n\t//used when loading a game\n\tint (PDECL *MapNamedBuiltin)\t\t(pubprogfuncs_t *prinst, int headercrc, const char *builtinname);\t//return 0 for not found.\n\tvoid (PDECL *loadcompleate)\t\t\t(int edictsize);\t//notification to reset any pointers.\n\tpbool (PDECL *badfield)\t\t\t\t(pubprogfuncs_t *prinst, struct edict_s *ent, const char *keyname, const char *value);\t//called for any fields that are not registered\n\n\tvoid *(VARGS *memalloc)\t\t\t\t(int size);\t//small string allocation\tmalloced and freed randomly by the executor. (use malloc if you want)\n\tvoid (VARGS *memfree)\t\t\t\t(void * mem);\n\n\tint (PDECL *useeditor)\t\t\t\t(pubprogfuncs_t *prinst, const char *filename, int *line, int *statement, int funcstart, char *reason, pbool fatal);\t//called on syntax errors or step-by-step debugging. line and statement(if line was set to 0) can be used to change the next line. return value is the new debug state to use/step.\n\tvoid (PDECL *addressablerelocated)\t(pubprogfuncs_t *progfuncs, char *oldb, char *newb, int oldlen);\t//called when the progs memory was resized. you must fix up all pointers to globals, strings, fields, addressable blocks.\n\n\tbuiltin_t *globalbuiltins;\t//these are available to all progs\n\tint numglobalbuiltins;\n\n\tenum {PR_NOCOMPILE, PR_COMPILENEXIST, PR_COMPILEEXISTANDCHANGED, PR_COMPILECHANGED, PR_COMPILEALWAYS, PR_COMPILEIGNORE} autocompile;\n\n\tdouble *gametime;\t//used to prevent the vm from reusing an entity faster than 2 secs.\n\n\tpbool usethreadedgc;\n\n\tstruct edict_s **edicts;\t//pointer to the engine's reference to world.\n\tunsigned int *num_edicts;\t\t//pointer to the engine's edict count.\n\tint edictsize;\t//size of edict_t\n\n\tvoid *user;\t/*contains the owner's world reference in FTE*/\n} progparms_t, progexterns_t;\n\n#if defined(QCLIBDLL_EXPORTS)\n\t#ifdef _WIN32\n\t\t__declspec(dllexport)\n\t#else\n\t\t__attribute__((visibility(\"default\")))\n\t#endif\n#endif\npubprogfuncs_t * PDECL InitProgs(progparms_t *ext);\n\ntypedef union eval_s\n{\t//FIXME: we should not be using a leading underscore here. that is reserved for libc.\n\tstring_t\t\tstring;\n\tpvec_t\t\t\t_float;\n\tpvec_t\t\t\t_vector[3];\n\tfunc_t\t\t\tfunction;\t//module=0xff000000, func=0x00ffffff\n\tpint_t\t\t\t_int;\n\tpuint_t\t\t\t_uint;\n\tpint64_t\t\ti64;\n\tpuint64_t\t\tu64;\n\tpdouble_t\t\t_double;\n\tpint_t\t\t\tedict;\n\tpvec_t\t\t\tprog;\t//so it can easily be changed\n} eval_t;\n\n#define PR_CURRENT\t-1\n#define PR_ANY\t-2\t//not always valid. Use for finding funcs\n#define PR_ANYBACK -3\n#define PROGSTRUCT_VERSION 4\n\n\n#ifndef DLL_PROG\n#define PR_Configure(pf, memsize, max_progs, profiling)\t\t(*pf->Configure)\t\t\t(pf, memsize, max_progs, profiling)\n#define PR_LoadProgs(pf, s)\t\t\t\t\t\t\t\t\t(*pf->LoadProgs)\t\t\t(pf, s)\n#define PR_InitEnts(pf, maxents)\t\t\t\t\t\t\t(*pf->InitEnts)\t\t\t\t(pf, maxents)\n#define PR_ExecuteProgram(pf, fnum)\t\t\t\t\t\t\t(*pf->ExecuteProgram)\t\t(pf, fnum)\n#define PR_globals(pf, num)\t\t\t\t\t\t\t\t\t(*pf->globals)\t\t\t\t(pf, num)\n#define PR_entvars(pf, ent)\t\t\t\t\t\t\t\t\t(*pf->entvars)\t\t\t\t(pf, ent)\n\n#define PR_RegisterFieldVar(pf,type,name,reqofs,qcofs)\t\t(*pf->RegisterFieldVar)\t\t(pf,type,name,reqofs,qcofs)\n\n#define ED_Alloc(pf,isobj,extsize)\t\t\t\t\t\t\t(*pf->EntAlloc)\t\t\t\t(pf, isobj, extsize)\n#define ED_Free(pf, ed)\t\t\t\t\t\t\t\t\t\t(*pf->EntFree)\t\t\t\t(pf, ed, false)\n#define ED_Clear(pf, ed)\t\t\t\t\t\t\t\t\t(*pf->EntClear)\t\t\t\t(pf, ed)\n\n#define PR_LoadEnts(pf, s, ctx, memreset, entcb, extcb)\t\t(*pf->load_ents)\t\t\t(pf, s, ctx, memreset, entcb, extcb)\n#define PR_SaveEnts(pf, buf, size, maxsize, mode)\t\t\t(*pf->save_ents)\t\t\t(pf, buf, size, maxsize, mode)\n\n#if 0//def _DEBUG\n#define EDICT_NUM(pf, num)\t\t\t\t\t\t\t\t\t(*pf->EDICT_NUM)\t\t\t(pf, num)\n#else\n#define EDICT_NUM_PB(pf, num)\t\t\t\t\t\t\t\t(pf->edicttable[num])\n#define EDICT_NUM_UB(pf, num)\t\t\t\t\t\t\t\tEDICT_NUM_PB(pf,(((unsigned int)(num))>=pf->edicttable_length)?0:num)\n#endif\n#define NUM_FOR_EDICT(pf, e)\t\t\t\t\t\t\t\t(*pf->NumForEdict)\t\t\t(pf, (struct edict_s*)(e))\n#define SetGlobalEdict(pf, ed, ofs)\t\t\t\t\t\t\t(*pf->SetGlobalEdict)\t\t(pf, ed, ofs)\n#define PR_VarString(pf,first)\t\t\t\t\t\t\t\t(*pf->VarString)\t\t\t(pf,first)\n\n#define PR_StartCompile(pf,argc,argv)\t\t\t\t\t\t(*pf->StartCompile)\t\t\t(pf,argc,argv)\n#define PR_ContinueCompile(pf)\t\t\t\t\t\t\t\t(*pf->ContinueCompile)\t\t(pf)\n\n#define PR_StackTrace(pf,locals)\t\t\t\t\t\t\t(*pf->StackTrace)\t\t\t(pf,locals)\n#define PR_AbortStack(pf)\t\t\t\t\t\t\t\t\t(*pf->AbortStack)\t\t\t(pf)\n\n#define PR_RunError(pf,str)\t\t\t\t\t\t\t\t\t(*pf->RunError)\t\t\t\t(pf,str)\n\n#define PR_PrintEdict(pf,ed)\t\t\t\t\t\t\t\t(*pf->PrintEdict)\t\t\t(pf, ed)\n\n#define PR_FindFunction(pf, name, num)\t\t\t\t\t\t(*pf->FindFunction)\t\t\t(pf, name, num)\n#define PR_FindGlobal(pf, name, progs, type)\t\t\t\t(*pf->FindGlobal)\t\t\t(pf, name, progs, type)\n#define PR_AddString(pf, ed, len, demarkup)\t\t\t\t\t(*pf->AddString)\t\t\t(pf, ed, len, demarkup)\n#define PR_Alloc(pf,size,whatfor)\t\t\t\t\t\t\t(*pf->Tempmem)\t\t\t\t(pf, size, whatfor)\n#define PR_AddressableAlloc(pf,size)\t\t\t\t\t\t(*pf->AddressableAlloc)\t\t(pf, size)\n#define PR_AddressableFree(pf,mem)\t\t\t\t\t\t\t(*pf->AddressableFree)\t\t(pf, mem)\n\n#define PROG_TO_EDICTINDEX(pf, ed)\t\t\t\t\t\t\ted\n#define PROG_TO_EDICT(pf, ed)\t\t\t\t\t\t\t\t(*pf->ProgsToEdict)\t\t\t(pf, ed)\n#define EDICT_TO_PROG(pf, ed)\t\t\t\t\t\t\t\t(*pf->EdictToProgs)\t\t\t(pf, (struct edict_s*)ed)\n\n#define PR_GetString(pf,s)\t\t\t\t\t\t\t\t\t(*pf->StringToNative)\t\t(pf, s)\n#define PR_GetStringOfs(pf,o)\t\t\t\t\t\t\t\t(*pf->StringToNative)\t\t(pf, G_INT(o))\n#define PR_SetString(pf, s)\t\t\t\t\t\t\t\t\t(*pf->StringToProgs)\t\t(pf, s)\n\n#define NEXT_EDICT(pf,o)\t\tEDICT_NUM(pf, NUM_FOR_EDICT(pf, o)+1)\n#define\tRETURN_EDICT(pf, e) (((pint_t *)pr_globals)[OFS_RETURN] = EDICT_TO_PROG(pf, e))\n\n\n//builtin funcs (which operate on globals)\n//To use these outside of builtins, you will likly have to use the 'globals' method.\n#define\tG_FLOAT(o) (((pvec_t *)pr_globals)[o])\n#define\tG_FLOAT2(o) (((pvec_t *)pr_globals)[OFS_PARM0 + o*3])\n#define\tG_DOUBLE(o) (*(pdouble_t *)(((pvec_t *)pr_globals+(o))))\n#define\tG_INT(o) (((pint_t *)pr_globals)[o])\n#define\tG_UINT(o) (((puint_t *)pr_globals)[o])\n#define\tG_INT64(o) (*(pint64_t *)((pint_t *)pr_globals+(o)))\n#define\tG_UINT64(o) (*(puint64_t *)((puint_t *)pr_globals+(o)))\n#define\tG_EDICT(pf, o) PROG_TO_EDICT(pf, G_INT(o)) //((edict_t *)((char *) sv.edicts+ *(int *)&((float *)pr_globals)[o]))\n#define G_EDICTNUM(pf, o) NUM_FOR_EDICT(pf, G_EDICT(pf, o))\n#define\tG_VECTOR(o) (&((pvec_t *)pr_globals)[o])\n#define\tG_FUNCTION(o) (*(func_t *)&((pvec_t *)pr_globals)[o])\n\n/*\n#define PR_GetString(p,s) (s?s + p->stringtable:\"\")\n#define PR_GetStringOfs(p,o) (G_INT(o)?G_INT(o) + p->stringtable:\"\")\n#define PR_SetStringOfs(p,o,s) (G_INT(o) = s - p->stringtable)\n*/\n//#define PR_SetString(p, s) ((s&&*s)?(s - p->stringtable):0)\n/**/\n\n#ifdef QCGC\n#define PR_NewString(p, s) (p)->TempString(p, s)\n#else\n#define PR_NewString(p, s) PR_SetString(p, PR_AddString(p, s, 0, false))\n#endif\n\n#define ev_prog ev_integer\n\n#define E_STRING(o) (char *)(((pint_t *)((char *)ed) + progparms.edictsize)[o])\n\n//#define pr_global_struct pr_globals\n\n#endif\n\n\n#define\tOFS_NULL\t\t0\n#define\tOFS_RETURN\t\t1\n#define\tOFS_PARM0\t\t4\t\t// leave 3 ofs for each parm to hold vectors\n#define\tOFS_PARM1\t\t7\n#define\tOFS_PARM2\t\t10\n#define\tOFS_PARM3\t\t13\n#define\tOFS_PARM4\t\t16\n#define\tOFS_PARM5\t\t19\n#define\tOFS_PARM6\t\t22\n#define\tOFS_PARM7\t\t25\n#define\tRESERVED_OFS\t28\n\n\n#undef edict_t\n#undef globalvars_t\n\n#endif //PROGSLIB_H\n"
  },
  {
    "path": "engine/qclib/progtype.h",
    "content": "#ifndef QCLIB_PROGTYPE_H\n#define QCLIB_PROGTYPE_H\n\n#if _MSC_VER >= 1300\n\t#define QC_ALIGN(a) __declspec(align(a))\n#elif (__GNUC__ >= 3) || defined(__clang__)\n\t#define QC_ALIGN(a) __attribute__((aligned(a)))\n#else\n\t#define QC_ALIGN(a)\t//I hope misaligned accesses are okay...\n#endif\n\n#if 0\n//64bit primitives allows for:\n//\tgreater precision timers (so maps can last longer without getting restarted)\n//\tplanet-sized maps (with the engine's vec_t types changed too, and with some sort of magic for the gpu's precision).\n//TODO: for this to work, someone'll have to go through the code to somehow deal with the vec_t/pvec_t/float differences.\n#warning FTE isnt ready for this.\n#include <stdint.h>\ntypedef double pvec_t;\ntypedef int64_t pint_t;\ntypedef uint64_t puint_t;\n\n#include <inttypes.h>\n#define pPRId PRId64\n#define pPRIi PRIi64\n#define pPRIu PRIu64\n#define pPRIx PRIx64\n#define QCVM_64\n#else\n//use 32bit types, for sanity.\ntypedef float pvec_t;\ntypedef int pint_t;\ntypedef unsigned int puint_t;\n#ifdef _MSC_VER\n\ttypedef QC_ALIGN(4) __int64 pint64_t;\n\ttypedef QC_ALIGN(4) unsigned __int64 puint64_t;\n\n\t#define pPRId \"d\"\n\t#define pPRIi \"i\"\n\t#define pPRIu \"u\"\n\t#define pPRIx \"x\"\n\t#define pPRIi64 \"I64i\"\n\t#define pPRIu64 \"I64u\"\n\t#define pPRIx64 \"I64x\"\n\t#define pPRIuSIZE PRIxPTR\n#else\n\t#include <inttypes.h>\n\ttypedef int64_t pint64_t QC_ALIGN(4);\n\ttypedef uint64_t puint64_t QC_ALIGN(4);\n\n\t#define pPRId PRId32\n\t#define pPRIi PRIi32\n\t#define pPRIu PRIu32\n\t#define pPRIx PRIx32\n\t#define pPRIi64 PRIi64\n\t#define pPRIu64 PRIu64\n\t#define pPRIx64 PRIx64\n\t#define pPRIuSIZE PRIxPTR\n#endif\n#define QCVM_32\n#endif\n\ntypedef QC_ALIGN(4) double pdouble_t;\t//the qcvm uses vectors and stuff, so any 64bit types are only 4-byte aligned. we don't do atomics so this is fine so long as the compiler handles it for us.\ntypedef unsigned int pbool;\ntypedef pvec_t pvec3_t[3];\ntypedef pint_t progsnum_t;\ntypedef puint_t func_t;\ntypedef puint_t string_t;\n\nextern pvec3_t pvec3_origin;\n\n#endif /* QCLIB_PROGTYPE_H */\n\n"
  },
  {
    "path": "engine/qclib/qcc.h",
    "content": "#define COMPILER\n#define PROGSUSED\n\n//#define COMMONINLINES\n//#define inline _inline\n\n#include \"cmdlib.h\"\n#include <setjmp.h>\n/*\n#include <stdio.h>\n#include <conio.h>\n\n\n#include \"pr_comp.h\"\n*/\n\n//this is for testing\n#define WRITEASM\n\n#ifndef MAX_QPATH\n#define MAX_QPATH 128\n#endif\n#ifndef MAX_OSPATH\n#define MAX_OSPATH 1024\n#endif\n\n#ifdef __MINGW32_VERSION\n\t#define MINGW\n#endif\n\n#define progfuncs qccprogfuncs\nextern progfuncs_t *qccprogfuncs;\n\n#if defined(_MSC_VER) && _MSC_VER < 1900\n\t#define strtoll _strtoi64\n\t#define strtoull _strtoui64\n\t#ifndef PRIxPTR\n\t\t#define PRIxPTR \"Ix\"\n\t#endif\n#else\n\t#include <inttypes.h>\n\t#ifndef PRIxPTR\n\t\t#define PRIxPTR \"p\"\n\t#endif\n#endif\n\n#ifndef STRINGIFY\n\t#define STRINGIFY2(s) #s\n\t#define STRINGIFY(s) STRINGIFY2(s)\n#endif\n\n#if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1))\n\t#define FTE_DEPRECATED  __attribute__((__deprecated__))\t//no idea about the actual gcc version\n\t#if defined(_WIN32)\n\t\t#include <stdio.h>\n\t\t#ifdef __MINGW_PRINTF_FORMAT\n\t\t\t#define LIKEPRINTF(x) __attribute__((format(__MINGW_PRINTF_FORMAT,x,x+1)))\n\t\t#else\n\t\t\t#define LIKEPRINTF(x) __attribute__((format(ms_printf,x,x+1)))\n\t\t#endif\n\t#else\n\t\t#define LIKEPRINTF(x) __attribute__((format(printf,x,x+1)))\n\t#endif\n#endif\n#ifndef LIKEPRINTF\n\t#define LIKEPRINTF(x)\n#endif\n\n#if __STDC_VERSION__ >= 199901L\n\t#define qc_inlinestatic static inline\n#else\n\t#define qc_inlinestatic static\n#endif\n\nvoid *qccHunkAlloc(size_t mem);\nvoid qccClearHunk(void);\n\nextern short   (*PRBigShort) (short l);\nextern short   (*PRLittleShort) (short l);\nextern int     (*PRBigLong) (int l);\nextern int     (*PRLittleLong) (int l);\nextern float   (*PRBigFloat) (float l);\nextern float   (*PRLittleFloat) (float l);\n\n\n#define\tMAX_ERRORS\t\t10\n\n#define\tMAX_NAME\t\t256\t\t// chars long\n\nextern unsigned int MAX_REGS;\nextern unsigned int MAX_LOCALS;\nextern unsigned int MAX_TEMPS;\n\nextern int\tMAX_STRINGS;\nextern int\tMAX_GLOBALS;\nextern int\tMAX_FIELDS;\nextern int\tMAX_STATEMENTS;\nextern int\tMAX_FUNCTIONS;\n\n#define\tQCC_MAX_SOUNDS\t\t1024\t//convert to int?\n#define QCC_MAX_TEXTURES\t1024\t//convert to int?\n#define\tQCC_MAX_MODELS\t\t1024\t//convert to int?\n#define\tQCC_MAX_FILES\t\t1024\t//convert to int?\n#define\tMAX_DATA_PATH\t64\n\nextern int MAX_CONSTANTS;\n#define MAXCONSTANTNAMELENGTH 64\n#define MAXCONSTANTPARAMLENGTH 32\n#define MAXCONSTANTPARAMS 32\n\ntypedef enum {QCF_STANDARD, QCF_HEXEN2, QCF_UHEXEN2, QCF_DARKPLACES, QCF_QSS, QCF_FTE, QCF_FTEDEBUG, QCF_FTEH2, QCF_KK7, QCF_QTEST} qcc_targetformat_t;\nextern qcc_targetformat_t qcc_targetformat;\n#define qcc_targetformat_ishexen2() (qcc_targetformat == QCF_HEXEN2 || qcc_targetformat == QCF_UHEXEN2 || qcc_targetformat == QCF_FTEH2)\nextern unsigned int qcc_targetversion;\nvoid QCC_OPCodeSetTarget(qcc_targetformat_t targfmt, unsigned int targver);\npbool QCC_OPCodeSetTargetName(const char *targ);\n\n\n/*\n\nTODO:\n\n\"stopped at 10 errors\"\n\nother pointer types for models and clients?\n\ncompact string heap?\n\nalways initialize all variables to something safe\n\nthe def->type->type arrangement is really silly.\n\nreturn type checking\n\nparm count type checking\n\nimmediate overflow checking\n\npass the first two parms in call->b and call->c\n\n*/\n\n/*\n\ncomments\n--------\n// comments discard text until the end of line\n/ *  * / comments discard all enclosed text (spaced out on this line because this documentation is in a regular C comment block, and typing them in normally causes a parse error)\n\ncode structure\n--------------\nA definition is:\n\t<type> <name> [ = <immediate>] {, <name> [ = <immediate>] };\n\n\ntypes\n-----\nsimple types: void, float, vector, string, or entity\n\tfloat\t\twidth, height;\n\tstring\t\tname;\n\tentity\t\tself, other;\n\nvector types:\n\tvector\t\torg;\t// also creates org_x, org_y, and org_z float defs\n\t\n\t\nA function type is specified as: \tsimpletype ( type name {,type name} )\nThe names are ignored except when the function is initialized.\t\n\tvoid()\t\tthink;\n\tentity()\tFindTarget;\n\tvoid(vector destination, float speed, void() callback)\tSUB_CalcMove;\n\tvoid(...)\tdprint;\t\t// variable argument builtin\n\nA field type is specified as:  .type\n\t.vector\t\torigin;\n\t.string\t\tnetname;\n\t.void()\t\tthink, touch, use;\n\t\n\nnames\n-----\nNames are a maximum of 64 characters, must begin with A-Z,a-z, or _, and can continue with those characters or 0-9.\n\nThere are two levels of scoping: global, and function.  The parameter list of a function and any vars declared inside a function with the \"local\" statement are only visible within that function, \n\n\nimmediates\n----------\nFloat immediates must begin with 0-9 or minus sign.  .5 is illegal.\n\t\nA parsing ambiguity is present with negative constants. \"a-5\" will be parsed as \"a\", then \"-5\", causing an error.  Seperate the - from the digits with a space \"a - 5\" to get the proper behavior.\n\t12\n\t1.6\n\t0.5\n\t-100\n\nVector immediates are three float immediates enclosed in single quotes.\n\t'0 0 0'\n\t'20.5 -10 0.00001'\n\t\nString immediates are characters enclosed in double quotes.  The string cannot contain explicit newlines, but the escape character \\n can embed one.  The \\\" escape can be used to include a quote in the string.\n\t\"maps/jrwiz1.bsp\"\n\t\"sound/nin/pain.wav\"\n\t\"ouch!\\n\"\n\nCode immediates are statements enclosed in {} braces.\nstatement:\n\t{ <multiple statements> }\n\t<expression>;\n\tlocal <type> <name> [ = <immediate>] {, <name> [ = <immediate>] };\n\treturn <expression>;\n\tif ( <expression> ) <statement> [ else <statement> ];\n\twhile ( <expression> ) <statement>;\n\tdo <statement> while ( <expression> );\n\t<function name> ( <function parms> );\n\t\nexpression:\n\tcombiations of names and these operators with standard C precedence:\n\t\"&&\", \"||\", \"<=\", \">=\",\"==\", \"!=\", \"!\", \"*\", \"/\", \"-\", \"+\", \"=\", \".\", \"<\", \">\", \"&\", \"|\"\n\tParenthesis can be used to alter order of operation.\n\tThe & and | operations perform integral bit ops on floats\n\t\nA built in function immediate is a number sign followed by an integer.\n\t#1\n\t#12\n\n\ncompilation\n-----------\nSource files are processed sequentially without dumping any state, so if a defs file is the first one processed, the definitions will be available to all other files.\n\nThe language is strongly typed and there are no casts.\n\nAnything that is initialized is assumed to be constant, and will have immediates folded into it.  If you change the value, your program will malfunction.  All uninitialized globals will be saved to savegame files.\n\nFunctions cannot have more than eight parameters.\n\nError recovery during compilation is minimal.  It will skip to the next global definition, so you will never see more than one error at a time in a given function.  All compilation aborts after ten error messages.\n\nNames can be defined multiple times until they are defined with an initialization, allowing functions to be prototyped before their definition.\n\nvoid()\tMyFunction;\t\t\t// the prototype\n\nvoid()\tMyFunction =\t\t// the initialization\n{\n\tdprint (\"we're here\\n\");\n};\n\n\nentities and fields\n-------------------\n\n\nexecution\n---------\nCode execution is initiated by C code in quake from two main places:  the timed think routines for periodic control, and the touch function when two objects impact each other.\n\nThere are three global variables that are set before beginning code execution:\n\tentity\tworld;\t\t// the server's world object, which holds all global\n\t\t\t\t\t\t// state for the server, like the deathmatch flags\n\t\t\t\t\t\t// and the body ques.\n\tentity\tself;\t\t// the entity the function is executing for\n\tentity\tother;\t\t// the other object in an impact, not used for thinks\n\tfloat\ttime;\t\t// the current game time.  Note that because the\n\t\t\t\t\t\t// entities in the world are simulated sequentially,\n\t\t\t\t\t\t// time is NOT strictly increasing.  An impact late\n\t\t\t\t\t\t// in one entity's time slice may set time higher\n\t\t\t\t\t\t// than the think function of the next entity. \n\t\t\t\t\t\t// The difference is limited to 0.1 seconds.\nExecution is also caused by a few uncommon events, like the addition of a new client to an existing server.\n\t\nThere is a runnaway counter that stops a program if 100000 statements are executed, assuming it is in an infinite loop.\n\nIt is acceptable to change the system set global variables.  This is usually done to pose as another entity by changing self and calling a function.\n\nThe interpretation is fairly efficient, but it is still over an order of magnitude slower than compiled C code.  All time consuming operations should be made into built in functions.\n\nA profile counter is kept for each function, and incremented for each interpreted instruction inside that function.  The \"profile\" console command in Quake will dump out the top 10 functions, then clear all the counters.  The \"profile all\" command will dump sorted stats for every function that has been executed.\n\n\nafunc ( 4, bfunc(1,2,3));\nwill fail because there is a shared parameter marshaling area, which will cause the 1 from bfunc to overwrite the 4 already placed in parm0.  When a function is called, it copies the parms from the globals into it's privately scoped variables, so there is no collision when calling another function.\n\ntotal = factorial(3) + factorial(4);\nWill fail because the return value from functions is held in a single global area.  If this really gets on your nerves, tell me and I can work around it at a slight performance and space penalty by allocating a new register for the function call and copying it out.\n\n\nbuilt in functions\n------------------\nvoid(string text)\tdprint;\nPrints the string to the server console.\n\nvoid(entity client, string text)\tcprint;\nPrints a message to a specific client.\n\nvoid(string text)\tbprint;\nBroadcast prints a message to all clients on the current server.\n\nentity()\tspawn;\nReturns a totally empty entity.  You can manually set everything up, or just set the origin and call one of the existing entity setup functions.\n\nentity(entity start, .string field, string match) find;\nSearches the server entity list beginning at start, looking for an entity that has entity.field = match.  To start at the beginning of the list, pass world.  World is returned when the end of the list is reached.\n\n<FIXME: define all the other functions...>\n\n\ngotchas\n-------\n\nThe && and || operators DO NOT EARLY OUT like C!\n\nDon't confuse single quoted vectors with double quoted strings\n\nThe function declaration syntax takes a little getting used to.\n\nDon't forget the ; after the trailing brace of a function initialization.\n\nDon't forget the \"local\" before defining local variables.\n\nThere are no ++ / -- operators, or operate/assign operators.\n\n*/\n\n\n#if 1\n#include \"hash.h\"\nextern hashtable_t compconstantstable;\nextern hashtable_t globalstable, localstable, typedeftable;\n#endif\n\n#ifdef WRITEASM\nextern FILE *asmfile;\nextern pbool asmfilebegun;\n#endif\n//=============================================================================\n\n// offsets are always multiplied by 4 before using\ntypedef unsigned int\tgofs_t;\t\t\t\t// offset in global data block\ntypedef struct QCC_function_s QCC_function_t;\n\n#define\tMAX_PARMS\t8\n\n//keep this sizeof(float)\ntypedef union QCC_eval_basic_s\n{\n\tQCC_string_t\t\t\tstring;\n\tpvec_t\t\t\t\t_float;\n#ifdef __GNUC__\n\tpvec_t\t\t\t\tvector[0];\t//gnuc extension. I'm using it to mute clang warnings.\n#else\n\tpvec_t\t\t\t\tvector[1];\t//should be 3, except that then eval_t would be too big.\n#endif\n\tfunc_t\t\t\t\tfunction;\n\tpint_t\t\t\t\t\t_int;\n\tpuint_t\t\t\t\t\t_uint;\n//\tunion QCC_eval_s\t\t*ptr;\n} QCC_eval_basic_t;\n\n//must be the maximum size possible for a single basic type.\ntypedef union QCC_eval_s\n{\n\tQCC_string_t\t\t\tstring;\n\tpvec_t\t\t\t\t_float;\n\tpdouble_t\t\t\t_double;\n\tpvec_t\t\t\t\tvector[3];\n\tfunc_t\t\t\t\tfunction;\n\tpint_t\t\t\t\t_int;\n\tpuint_t\t\t\t\t_uint;\n\tpint64_t\t\t\ti64;\n\tpuint64_t\t\t\tu64;\n//\tunion QCC_eval_s\t\t*ptr;\n} QCC_eval_t;\n\nstruct QCC_typeparam_s\n{\n\tstruct QCC_type_s *type;\n\tQCC_sref_t defltvalue;\n\tpbool optional:1;\t//argument may safely be omitted, for builtin functions. for qc functions use the defltvalue instead.\n\tpbool isvirtual:1;\t//const, with implicit initialisation only. valid for structs\n\tunsigned char out;\t//0=in,1==inout,2=out\n\tunsigned int ofs;\t//word offset.\n\tunsigned int bitofs;\t//for bitfields (and chars/shorts).\n\tunsigned int arraysize;\n\tchar *paramname;\n};\nstruct accessor_s\n{\n\tstruct accessor_s *next;\n\tstruct QCC_type_s *type;\n\tstruct QCC_type_s *indexertype;\t//null if not indexer\n\tQCC_sref_t getset_func[2];\n\tQCC_sref_t staticval;\n\tpbool getset_isref[2];\n\tchar *fieldname;\n};\n\ntypedef struct QCC_type_s\n{\n\tetype_t\t\t\ttype;\n\n\tstruct QCC_type_s\t*parentclass;\t//type_entity...\n// function types are more complex\n\tstruct QCC_type_s\t*aux_type;\t// return type or field type\n\t\n\tstruct QCC_typeparam_s *params; //[num_parms]\n\tunsigned int\t\tnum_parms;\n\n\tunsigned int size;\t//FIXME: make bytes, for bytes+shorts\n\tpbool typedefed:1;\t//name is in the typenames list.\n\tpbool vargs:1;\t\t//function has vargs\n\tpbool vargtodouble:1; //promote floats to double when passing vargs (set according to flag_assume_double, on the def's type, so it can be enabled/disabled as required to deal with builtins\n\tpbool vargcount:1;\t//function has special varg count param\n\tunsigned int align:7;\n\tunsigned int bits;//valid for bitfields (and structs).\n\tconst char *name;\n\tconst char *aname;\n\n\tconst char *filen;\n\tunsigned int line;\n\n\tstruct accessor_s *accessors;\n\n\tstruct QCC_function_s *scope;\t//stoopid scoped typedefs...\n\tstruct QCC_type_s *ptrto;\t//(cache) this points to a type that is a pointer back to this type. yeah, weird.\n\tstruct QCC_type_s *fldto;\t//(cache) this points to a type that is a pointer back to this type. yeah, weird.\n} QCC_type_t;\nint typecmp(QCC_type_t *a, QCC_type_t *b);\nint typecmp_lax(QCC_type_t *a, QCC_type_t *b);\nQCC_type_t *QCC_PR_DuplicateType(QCC_type_t *in, pbool recurse);\n\ntypedef struct temp_s temp_t;\nvoid QCC_PurgeTemps(void);\nvoid QCC_FinaliseTemps(void);\n\n//not written\ntypedef struct QCC_def_s\n{\n\tQCC_type_t\t\t*type;\n\tchar\t\t*name;\n\tchar\t\t*comment;\t\t//ui info\n\tstruct QCC_def_s\t*next;\n\tstruct QCC_def_s\t*nextlocal;\t//provides a chain of local variables for the opt_locals_marshalling optimisation.\n\tgofs_t\t\tofs;\t//offset of symbol relative to symbol header.\n\tstruct QCC_function_s\t*scope;\t\t// function the var was defined in, or NULL\n\tstruct QCC_def_s\t*deftail;\t// arrays and structs create multiple globaldef objects providing different types at the different parts of the single object (struct), or alternative names (vectors). this allows us to correctly set the const type based upon how its initialised.\n\tstruct QCC_def_s\t*generatedfor;\n\tint\t\t\tconstant;\t\t// 1 says we can use the value over and over again. 2 is used on fields, for some reason.\n\n\tstruct QCC_def_s\t*reloc;\t\t//the symbol that we're a reloc for\n\tstruct QCC_def_s\t*gaddress;\t//a def that holds our offset.\n\tstruct QCC_def_s\t*symbolheader;\t//this is the original symbol within which the def is stored.\n\tunion QCC_eval_basic_s\t*symboldata;\t//null if uninitialised. use sym->symboldata[sym->ofs] to index.\n\tunsigned int\t\tsymbolsize;\t\t//total byte size of symbol\n\n\tint refcount;\t\t\t//if 0, temp can be reused. tracked on globals too in order to catch bugs that would otherwise be a little too obscure.\n\tint timescalled;\t//part of the opt_stripfunctions optimisation.\n\n\tconst char *unitn;\t//for static globals.\n\tconst char *filen;\n\tint s_filed;\n\tint s_line;\n\n\tint arraysize;\n\t//should really use proper flags\n\tpbool funccalled:1;\t\t\t//was called somewhere.\n\tpbool read:1;\t\t\t\t//variable was read\n\tpbool written:1;\t\t\t//variable was written\n\tpbool referenced:1;\t\t\t//was used somewhere in the code (even if it can still be stripped). this controls warnings only.\n\tpbool shared:1;\t\t\t\t//weird multiprogs flag thing.\n\tpbool saved:1;\t\t\t\t//def may be saved to saved games.\n\tpbool isstatic:1;\t\t\t//global, even if scoped. also specific to the file it was seen in.\n\tpbool subscoped_away:1;\t\t//this local is no longer linked into the locals hash table. don't do remove it twice.\n//\tpbool followptr:1;\t\t\t//float &foo;\n\tpbool strip:1;\t\t\t\t//info about this def should be stripped. it may still consume globals space however, and its storage can still be used, its just not visible.\n\tpbool nostrip:1;\t\t\t//don't strip reflection data for this symbol.\n\tpbool allowinline:1;\t\t//calls to this function will attempt to inline the specified function. requires const, supposedly.\n\tpbool used:1;\t\t\t\t//if it remains 0, it may be stripped. this is forced for functions and fields. commonly 0 on fields.\n\tpbool unused:1;\t\t\t\t//silently strip it if it wasn't referenced.\n\tpbool localscope:1;\t\t\t//is a local, as opposed to a static (which is only visible within its scope)\n\tpbool arraylengthprefix:1;\t//hexen2 style arrays have a length prefixed to them for auto bounds checks. this can only work reliably for simple non-struct arrays.\n\tpbool assumedtype:1;\t\t//#merged. the type is not reliable.\n\tpbool weak:1;\t\t\t\t//ignore any initialiser value (only permitted on functions)\n\tpbool accumulate:1;\t\t\t//don't finalise the function's statements.\n\tpbool nofold:1;\n\tpbool initialized:1;\t\t//true when a declaration included \"= immediate\".\n\tpbool isextern:1;\t\t\t//fteqw-specific lump entry\n\tpbool isparameter:1;\t\t//its an engine parameter (thus preinitialised).\n\tpbool addressedwarned:1;\t//warned about it once, back off now.\n\tpbool autoderef:1;\t\t\t//like C++'s int &foo; - probably local pointers to alloca memory.\n\tconst char *deprecated;\t\t//reason its deprecated (or empty for no reason given)\n\n\tint\tfromstatement;\t\t//statement that it is valid from.\n\ttemp_t *temp;\n} QCC_def_t;\n\nstruct temp_s {\n\tQCC_def_t *def;\n\tunsigned char locked;\n\tunsigned int size;\n\n\tstruct QCC_function_s *lastfunc;\n\tunsigned int lastline;\n\tunsigned int laststatement;\n};\nextern size_t tempsused;\n\ntypedef struct\n{\n\tenum{\n\t\tREF_GLOBAL,\t//(global.ofs)\t\t\t\t- use vector[2] is an array ref or vector_z\n\t\tREF_ARRAY,\t//(global.ofs+wordoffset)\t- constant offsets should be direct references, variable offsets will generally result in function calls\n\t\tREF_ARRAYHEAD,//(global)\t\t\t\t- like REF_ARRAY, but otherwise convert to a pointer. check arraysize for length.\n\n\t\tREF_POINTER,//*(pointerdef+alignindex)\t- maths...\n\t\tREF_POINTERARRAY,//(pointerdef+alignindex) - head of an array. &REF_POINTER but with array size info. cannot be directly assigned to.\n\n\t\tREF_FIELD,\t//(entity.field)\t\t\t- reading is a single load, writing requires address+storep\n\n\t\tREF_STRING,\t//\"hello\"[1]=='e'\t\t\t- special opcodes, or str2chr builtin, or something\n\n\t\tREF_NONVIRTUAL,\t//(global.ofs)\t\t\t- identical to global except for function calls, where index can be used to provide the 'newself' for the call.\n\t\tREF_THISCALL,\t//(global.ofs)\t\t\t- identical to global except for function calls, where index is used as the first argument.\n\t\tREF_ACCESSOR //buf_create()[5]\n\t} type;\n\n\tQCC_sref_t base;\n\tunsigned int bitofs;\t//for bitfields.\n\tQCC_sref_t index;\n\tQCC_type_t *cast;\t//entity.float is float, not pointer.\n\tunsigned int arraysize;\t//for sizeof/boundchecks, not much else.\n\tstruct accessor_s *accessor;\t//the accessor field of base that we're trying to use\n\tint\t\tpostinc;\t//+1 or -1\n\tpbool\treadonly;\t//for whatever reason, like base being a const\n} QCC_ref_t;\n\n//============================================================================\n\n// pr_loc.h -- program local defs\n\n\n//=============================================================================\nextern char QCC_copyright[1024];\nextern char QCC_Packname[5][128];\nextern int QCC_packid;\n\nextern\tconst unsigned int\t\ttype_size[];\n//extern\tQCC_def_t\t*def_for_type[9];\n\nextern\tQCC_type_t\t*type_void, *type_string, *type_float, *type_double, *type_vector, *type_entity, *type_field, *type_function, *type_floatfunction, *type_pointer, *type_floatpointer, *type_intpointer, *type_bint, *type_bfloat, *type_sint8, *type_uint8, *type_sint16, *type_uint16, *type_integer, *type_uint, *type_int64, *type_uint64, *type_invalid, *type_variant, *type_floatfield;\nextern char *basictypenames[];\n\nstruct QCC_function_s\n{\n\tint\t\t\t\t\tbuiltin;\t// the builtin number. >= 0\n\tint\t\t\t\t\tcode;\t\t// first statement. if -1, is a builtin.\n\tdfunction_t\t\t\t*merged;\t// this function was merged. this is the index to use to ensure that the parms are sized correctly..\n\tstring_t\t\t\ts_filed;\t// source file with definition\n\tconst char\t\t\t*filen;\t\t// full scope, printed for debugging.\n\tconst char\t\t\t*unitn; //scope for static variables.\n\tint\t\t\t\t\tline;\n\tint\t\t\t\t\tline_end;\n\tchar\t\t\t\t*name;\t\t//internal name of function\n\tstruct QCC_function_s *parentscope;\t//for nested functions\n\tstruct QCC_type_s\t*type;\t\t//same as the def's type\n\tstruct QCC_def_s\t*def;\n\tstruct QCC_def_s\t*firstlocal;\n\tQCC_sref_t\t\t\treturndef;\t//default return value\n\tpbool\t\t\t\tprivatelocals;\t//false means locals may overlap with other functions, true is needed for compat if stuff is uninitialised.\n//\tunsigned int\t\tparm_ofs[MAX_PARMS];\t// always contiguous, right?\n\n\tQCC_statement_t *statements;\t//if set, then this function isn't finialised yet.\n\tsize_t numstatements;\n};\n\n\n//\n// output generated by prog parsing\n//\ntypedef struct\n{\n\tchar\t\t*memory;\n\tint\t\t\tmax_memory;\n\tint\t\t\tcurrent_memory;\n\tQCC_type_t\t\t*types;\n\t\n\tQCC_def_t\t\tdef_head;\t\t// unused head of linked list\n\tQCC_def_t\t\t*def_tail;\t\t// add new defs after this and move it\n\tQCC_def_t\t\tlocal_head;\t\t// chain of variables which need to be pushed and stuff (head unused).\n\tQCC_def_t\t\t*local_tail;\t// add new defs after this and move it\n\t\n\tunsigned int\tsize_fields;\n} QCC_pr_info_t;\n\nextern\tQCC_pr_info_t\tpr;\n\n\ntypedef struct\n{\n\tchar name[MAXCONSTANTNAMELENGTH];\n\tchar *value;\n\tchar params[MAXCONSTANTPARAMS][MAXCONSTANTPARAMLENGTH];\n\tint numparams;\n\tint inside:10; //cuts off at some point\n\tpbool used:1;\n\tpbool evil:1;\n\tpbool varg:1;\n\tconst char *fromfile;\n\tint fromline;\n\n\tint namelen;\n} CompilerConstant_t;\n\nchar *QCC_PR_GetDefinesList(void);\n\n//============================================================================\n\nextern\tpbool\tpr_dumpasm;\nextern pbool preprocessonly;\n\n//extern\tQCC_def_t\t\t**pr_global_defs;\t// to find def for a global variable\n\ntypedef enum {\ntt_eof,\t\t\t// end of file reached\ntt_name, \t\t// an alphanumeric name token\ntt_punct, \t\t// code punctuation\ntt_immediate,\t// string, float, vector\n} token_type_t;\n\nextern\tchar\t\t\t*pr_token_precomment;\nextern\tchar\t\tpr_token[8192];\nextern\ttoken_type_t\tpr_token_type;\nextern\tint\t\t\t\tpr_token_line;\nextern\tint\t\t\t\tpr_token_line_last;\nextern\tQCC_type_t\t\t*pr_immediate_type;\nextern\tQCC_eval_t\t\tpr_immediate;\n\nextern int verbose;\n#define VERBOSE_WARNINGSONLY -1\n#define VERBOSE_PROGRESS 0\n#define VERBOSE_STANDARD 1\n#define VERBOSE_DEBUG 2\n#define VERBOSE_DEBUGSTATEMENTS 3\t//figuring out the files can be expensive.\n\nextern pbool keyword_asm;\nextern pbool keyword_break;\nextern pbool keyword_case;\nextern pbool keyword_class;\nextern pbool keyword_accessor;\nextern pbool keyword_const;\nextern pbool keyword_inout;\nextern pbool keyword_optional;\nextern pbool keyword_continue;\nextern pbool keyword_default;\nextern pbool keyword_do;\nextern pbool keyword_entity;\nextern pbool keyword_float;\nextern pbool keyword_double;\nextern pbool keyword_for;\nextern pbool keyword_goto;\nextern pbool keyword_char;\nextern pbool keyword_byte;\nextern pbool keyword_short;\nextern pbool keyword_int;\nextern pbool keyword_integer;\nextern pbool keyword_long;\nextern pbool keyword_signed;\nextern pbool keyword_unsigned;\nextern pbool keyword_register;\nextern pbool keyword_volatile;\nextern pbool keyword_state;\nextern pbool keyword_string;\nextern pbool keyword_struct;\nextern pbool keyword_switch;\nextern pbool keyword_thinktime;\nextern pbool keyword_loop;\nextern pbool keyword_until;\nextern pbool keyword_var;\nextern pbool keyword_vector;\nextern pbool keyword_union;\nextern pbool keyword_enum;\t//kinda like in c, but typedef not supported.\nextern pbool keyword_enumflags;\t//like enum, but doubles instead of adds 1.\nextern pbool keyword_typedef;\t//fixme\nextern pbool keyword_extern;\t//function is external, don't error or warn if the body was not found\nextern pbool keyword_shared;\t//mark global to be copied over when progs changes (part of FTE_MULTIPROGS)\nextern pbool keyword_noref;\t//nowhere else references this, don't strip it.\nextern pbool keyword_nosave;\t//don't write the def to the output.\nextern pbool keyword_inline;\t//don't write the def to the output.\nextern pbool keyword_strip;\t//don't write the def to the output.\nextern pbool keyword_union;\t//you surly know what a union is!\nextern pbool keyword_wrap;\nextern pbool keyword_weak;\nextern pbool keyword_accumulate;\nextern pbool keyword_using;\n\nextern pbool keyword_unused;\nextern pbool keyword_used;\nextern pbool keyword_local;\nextern pbool keyword_static;\nextern pbool keyword_auto;\nextern pbool keyword_nonstatic;\nextern pbool keyword_ignore;\n\nextern pbool keywords_coexist;\nextern pbool output_parms;\nextern pbool autoprototype, autoprototyped, parseonly;\nextern pbool pr_subscopedlocals;\nextern pbool flag_nullemptystr, flag_ifstring, flag_brokenifstring, flag_iffloat, flag_ifvector, flag_vectorlogic;\nextern pbool flag_acc;\nextern pbool flag_caseinsensitive;\nextern pbool flag_laxcasts;\nextern pbool flag_hashonly;\nextern pbool flag_macroinstrings;\nextern pbool flag_fasttrackarrays;\nextern pbool flag_assume_integer;\nextern pbool flag_assume_double;\nextern pbool flag_msvcstyle;\nextern pbool flag_debugmacros;\nextern pbool flag_filetimes;\nextern pbool flag_typeexplicit;\nextern pbool flag_boundchecks;\nextern pbool flag_brokenarrays;\nextern pbool flag_rootconstructor;\nextern pbool flag_guiannotate;\nextern pbool flag_qccx;\nextern pbool flag_attributes;\nextern pbool flag_assumevar;\nextern pbool flag_dblstarexp;\nextern pbool flag_allowuninit;\nextern pbool flag_cpriority;\nextern pbool flag_qcfuncs;\nextern pbool flag_embedsrc;\nextern pbool flag_noreflection;\nextern pbool flag_nopragmafileline;\nextern pbool flag_utf8strings;\nextern pbool flag_reciprocalmaths;\nextern pbool flag_ILP32;\nextern pbool flag_undefwordsize;\nextern pbool flag_pointerrelocs;\n\nextern pbool opt_overlaptemps;\nextern pbool opt_shortenifnots;\nextern pbool opt_noduplicatestrings;\nextern pbool opt_constantarithmatic;\nextern pbool opt_nonvec_parms;\nextern pbool opt_constant_names;\nextern pbool opt_precache_file;\nextern pbool opt_filenames;\nextern pbool opt_assignments;\nextern pbool opt_unreferenced;\nextern pbool opt_function_names;\nextern pbool opt_locals;\nextern pbool opt_dupconstdefs;\nextern pbool opt_constant_names_strings;\nextern pbool opt_return_only;\nextern pbool opt_compound_jumps;\n//extern pbool opt_comexprremoval;\nextern pbool opt_stripfunctions;\nextern pbool opt_locals_overlapping;\nextern pbool opt_logicops;\nextern pbool opt_vectorcalls;\nextern pbool opt_classfields;\n\nextern int optres_shortenifnots;\nextern int optres_overlaptemps;\nextern int optres_noduplicatestrings;\nextern int optres_constantarithmatic;\nextern int optres_nonvec_parms;\nextern int optres_constant_names;\nextern int optres_precache_file;\nextern int optres_filenames;\nextern int optres_assignments;\nextern int optres_unreferenced;\nextern int optres_function_names;\nextern int optres_locals;\nextern int optres_dupconstdefs;\nextern int optres_constant_names_strings;\nextern int optres_return_only;\nextern int optres_compound_jumps;\n//extern int optres_comexprremoval;\nextern int optres_stripfunctions;\nextern int optres_locals_overlapping;\nextern int optres_logicops;\nextern int optres_inlines;\n\npbool CompileParams(progfuncs_t *progfuncs, void(*cb)(void), int nump, const char **parms);\npbool QCC_RegisterSourceFile(const char *filename);\n\nvoid QCC_PR_PrintStatement (QCC_statement_t *s);\n\nvoid QCC_PR_Lex (void);\n// reads the next token into pr_token and classifies its type\n\nQCC_type_t *QCC_PR_NewType (const char *name, int basictype, pbool typedefed);\t//note: name must be hunk/immediate\nQCC_type_t *QCC_PointerTypeTo(QCC_type_t *type);\nQCC_type_t *QCC_GenArrayType(QCC_type_t *type, unsigned int arraysize);\nQCC_type_t *QCC_PR_ParseType (int newtype, pbool silentfail, pbool ignoreptr);\nQCC_sref_t QCC_PR_ParseDefaultInitialiser(QCC_type_t *type);\nextern pbool type_inlinefunction;\nQCC_type_t *QCC_TypeForName(const char *name);\nQCC_type_t *QCC_PR_ParseFunctionType (int newtype, QCC_type_t *returntype);\nQCC_type_t *QCC_PR_ParseFunctionTypeReacc (int newtype, QCC_type_t *returntype);\nQCC_type_t *QCC_PR_GenFunctionType (QCC_type_t *rettype, struct QCC_typeparam_s *args, int numargs);\nchar *QCC_PR_ParseName (void);\nstruct QCC_typeparam_s *QCC_PR_FindStructMember(QCC_type_t *t, const char *membername, unsigned int *out_ofs, unsigned int *out_bitofs);\nQCC_type_t *QCC_PR_PointerType (QCC_type_t *pointsto);\n\nconst char *QCC_VarAtOffset(QCC_sref_t ref);\n\nvoid QCC_PrioritiseOpcodes(void);\npbool QCC_OPCodeValid(QCC_opcode_t *op);\nlong QCC_PR_IntConstExpr(void);\n\n#ifndef COMMONINLINES\npbool QCC_PR_CheckImmediate (const char *string);\npbool QCC_PR_CheckToken (const char *string);\npbool QCC_PR_PeekToken (const char *string);\npbool QCC_PR_CheckName (const char *string);\nvoid QCC_PR_Expect (const char *string);\npbool QCC_PR_CheckKeyword(int keywordenabled, const char *string);\n#endif\npbool QCC_PR_CheckTokenComment(const char *string, char **comment);\nNORETURN void VARGS QCC_PR_ParseError (int errortype, const char *error, ...) LIKEPRINTF(2);\npbool VARGS QCC_PR_ParseWarning (int warningtype, const char *error, ...) LIKEPRINTF(2);\npbool VARGS QCC_PR_Warning (int type, const char *file, int line, const char *error, ...) LIKEPRINTF(4);\nvoid VARGS QCC_PR_Note (int type, const char *file, int line, const char *error, ...) LIKEPRINTF(4);\nvoid QCC_PR_ParsePrintDef (int warningtype, QCC_def_t *def);\nvoid QCC_PR_ParsePrintSRef (int warningtype, QCC_sref_t sref);\nNORETURN void VARGS QCC_PR_ParseErrorPrintDef (int errortype, QCC_def_t *def, const char *error, ...) LIKEPRINTF(3);\nNORETURN void VARGS QCC_PR_ParseErrorPrintSRef (int errortype, QCC_sref_t sref, const char *error, ...) LIKEPRINTF(3);\n\nQCC_type_t *QCC_PR_MakeThiscall(QCC_type_t *orig, QCC_type_t *thistype);\n\nint QCC_WarningForName(const char *name);\nchar *QCC_NameForWarning(int idx);\n\n//QccMain.c must be changed if this is changed.\nenum {\n\tWARN_DEBUGGING,\n\tWARN_ERROR,\n\tWARN_REMOVEDWARNING,\t//to silence warnings about old warnings.\n\tWARN_WRITTENNOTREAD,\n\tWARN_READNOTWRITTEN,\n\tWARN_NOTREFERENCED,\n\tWARN_NOTREFERENCEDCONST,\n\tWARN_NOTREFERENCEDFIELD,\n\tWARN_CONFLICTINGRETURNS,\n\tWARN_TOOFEWPARAMS,\n\tWARN_TOOMANYPARAMS,\n\tWARN_UNEXPECTEDPUNCT,\n\tWARN_UNINITIALIZED,\n\tWARN_DENORMAL,\n\tWARN_STRINGOFFSET,\t//works for static/memalloc strings, but fundamentally unsafe on tempstrings/etc, and varies between engine\n\tWARN_OVERFLOW,\t\t//compile time overflow or inprecision.\n\tWARN_ASSIGNMENTTOCONSTANT,\n\tWARN_ASSIGNMENTTOCONSTANTFUNC,\n\tWARN_MISSINGRETURNVALUE,\n\tWARN_WRONGRETURNTYPE,\n\tWARN_CORRECTEDRETURNTYPE,\n\tWARN_POINTLESSSTATEMENT,\n\tWARN_MISSINGRETURN,\n\tWARN_DUPLICATEDEFINITION,\n\tWARN_UNDEFNOTDEFINED,\n\tWARN_PRECOMPILERMESSAGE,\n\tWARN_TOOMANYPARAMETERSFORFUNC,\n\tWARN_TOOMANYPARAMETERSVARARGS,\n\tWARN_NESTEDCOMMENT,\n\tWARN_STRINGTOOLONG,\n\tWARN_BADTARGET,\n\tWARN_BADPRAGMA,\n\tWARN_NOTUTF8,\n\tWARN_HANGINGSLASHR,\n\tWARN_MEMBERNOTDEFINED,\n\tWARN_NOTCONSTANT,\n\tWARN_SWITCHTYPEMISMATCH,\n\tWARN_CONFLICTINGUNIONMEMBER,\n\tWARN_KEYWORDDISABLED,\n\tWARN_ENUMFLAGS_NOTINTEGER,\n\tWARN_ENUMFLAGS_NOTBINARY,\n\tWARN_CASEINSENSITIVEFRAMEMACRO,\n\tWARN_STALEMACRO,\n\tWARN_DUPLICATEMACRO,\n\tWARN_DUPLICATELABEL,\n\tWARN_ASSIGNMENTINCONDITIONAL,\n\tWARN_MACROINSTRING,\n\tWARN_BADPARAMS,\n\tWARN_IMPLICITCONVERSION,\n\tWARN_EXTRAPRECACHE,\n\tWARN_NOTPRECACHED,\n\tWARN_NONPORTABLEFILENAME,\n\tWARN_DEADCODE,\n\tWARN_UNREACHABLECODE,\n\tWARN_NOTSTANDARDBEHAVIOUR,\n\tWARN_BOUNDS,\n\tWARN_DUPLICATEPRECOMPILER,\n\tWARN_IDENTICALPRECOMPILER,\n\tWARN_FORMATSTRING,\t\t//sprintf\n\tWARN_DEPRECACTEDSYNTAX,\t\t//triggered when syntax is used that I'm trying to kill\n\tWARN_DEPRECATEDVARIABLE,\t//triggered from usage of a symbol that someone tried to kill\n\tWARN_MUTEDEPRECATEDVARIABLE,\t//triggered from usage of a symbol that someone tried to kill (without having been muted).\n\tWARN_OCTAL_IMMEDIATE,\t// 0400!=400... (not found in\n\tWARN_GMQCC_SPECIFIC,\t//extension created by gmqcc that conflicts or isn't properly implemented.\n\tWARN_FTE_SPECIFIC,\t//extension that only FTEQCC will have a clue about.\n\tWARN_EXTENSION_USED,\t//extension that frikqcc also understands\n\tWARN_IFSTRING_USED,\n\tWARN_IFVECTOR_DISABLED,\t//if(vector) does if(vector_x) if ifvector is disabled\n\tWARN_SLOW_LARGERETURN,\t\t\t//just a perf warning. also requires working pointers but that'll give opcode errors\n\tWARN_LAXCAST,\t//some errors become this with a compiler flag\n\tWARN_TYPEMISMATCHREDECOPTIONAL,\n\tWARN_UNDESIRABLECONVENTION,\n\tWARN_UNSAFELOCALPOINTER,\n\tWARN_SAMENAMEASGLOBAL,\n\tWARN_CONSTANTCOMPARISON,\n\tWARN_DIVISIONBY0,\n\tWARN_UNSAFEFUNCTIONRETURNTYPE,\n\tWARN_MISSINGOPTIONAL,\n\tWARN_SYSTEMCRC,\t\t\t\t//unknown system crc\n\tWARN_SYSTEMCRC2,\t\t\t//legacy/dp system crc\n\tWARN_CONDITIONALTYPEMISMATCH,\n\tWARN_MISSINGMEMBERQUALIFIER,//virtual/static/nonvirtual qualifier is missing\n\tWARN_SELFNOTTHIS,\t\t\t//warned for because 'self' does not have the right type. we convert such references to 'this' instead, which is more usable.\n\tWARN_EVILPREPROCESSOR,\t\t//exploited by nexuiz, and generally unsafe.\n\tWARN_UNARYNOTSCOPE,\t\t\t//!foo & bar  the ! applies to the result of &. This is unlike C.\n\tWARN_STRICTTYPEMISMATCH,\t//self.think = T_Damage; both are functions, but the arguments/return types/etc differ.\n\tWARN_MISUSEDAUTOCVAR,\t\t//various issues with autocvar definitions.\n\tWARN_IGNORECOMMANDLINE,\n\tWARN_POINTERASSIGNMENT,\t\t//&somefloat = 5; disabled for qccx compat sanity.\n\tWARN_COMPATIBILITYHACK,\t\t//work around old defs.qc or invalid dpextensions.qc\n\tWARN_REDECLARATIONMISMATCH,\n\tWARN_PARAMWITHNONAME,\n\tWARN_ARGUMENTCHECK,\n\tWARN_IGNOREDKEYWORD,\t\t//use of a keyword that fteqcc does not support at this time.\n\tWARN_WORDSIZEUNDEFINED,\n\n\tERR_PARSEERRORS,\t//caused by qcc_pr_parseerror being called.\n\n\t//these are definatly my fault...\n\tERR_INTERNAL,\n\tERR_TOOCOMPLEX,\n\tERR_BADOPCODE,\n\tERR_TOOMANYSTATEMENTS,\n\tERR_TOOMANYSTRINGS,\n\tERR_BADTARGETSWITCH,\n\tERR_TOOMANYTYPES,\n\tERR_TOOMANYPAKFILES,\n\tERR_PRECOMPILERCONSTANTTOOLONG,\n\tERR_MACROTOOMANYPARMS,\n\tERR_TOOMANYFRAMEMACROS,\n\n\t//limitations, some are imposed by compiler, some arn't.\n\tERR_TOOMANYGLOBALS,\n\tERR_TOOMANYGOTOS,\n\tERR_TOOMANYBREAKS,\n\tERR_TOOMANYCONTINUES,\n\tERR_TOOMANYCASES,\n\tERR_TOOMANYLABELS,\n\tERR_TOOMANYOPENFILES,\n\tERR_TOOMANYTOTALPARAMETERS,\n\n\t//these are probably yours, or qcc being fussy.\n\tERR_BADEXTENSION,\n\tERR_BADIMMEDIATETYPE,\n\tERR_NOOUTPUT,\n\tERR_NOTAFUNCTION,\n\tERR_FUNCTIONWITHVARGS,\n\tERR_BADHEX,\n\tERR_UNKNOWNPUCTUATION,\n\tERR_EXPECTED,\n\tERR_NOTANAME,\n\tERR_NAMETOOLONG,\n\tERR_NOFUNC,\n\tERR_COULDNTOPENFILE,\n\tERR_NOTFUNCTIONTYPE,\n\tERR_TOOFEWPARAMS,\n\tERR_TOOMANYPARAMS,\n\tERR_CONSTANTNOTDEFINED,\n\tERR_BADFRAMEMACRO,\n\tERR_TYPEMISMATCH,\n\tERR_TYPEMISMATCHREDEC,\n\tERR_TYPEMISMATCHPARM,\n\tERR_TYPEMISMATCHARRAYSIZE,\n\tERR_UNEXPECTEDPUNCTUATION,\n\tERR_NOTACONSTANT,\n\tERR_REDECLARATION,\n\tERR_INITIALISEDLOCALFUNCTION,\n\tERR_NOTDEFINED,\n\tERR_ARRAYNEEDSSIZE,\n\tERR_TOOMANYINITIALISERS,\n\tERR_TYPEINVALIDINSTRUCT,\n\tERR_NOSHAREDLOCALS,\n\tERR_TYPEWITHNONAME,\n\tERR_BADARRAYSIZE,\n\tERR_NONAME,\n\tERR_SHAREDINITIALISED,\n\tERR_UNKNOWNVALUE,\n\tERR_BADARRAYINDEXTYPE,\n\tERR_NOVALIDOPCODES,\n\tERR_MEMBERNOTVALID,\n\tERR_BADPLUSPLUSOPERATOR,\n\tERR_BADNOTTYPE,\n\tERR_BADTYPECAST,\n\tERR_BADMEMBER,\n\tERR_MULTIPLEDEFAULTS,\n\tERR_CASENOTIMMEDIATE,\n\tERR_BADSWITCHTYPE,\n\tERR_BADLABELNAME,\n\tERR_NOLABEL,\n\tERR_THINKTIMETYPEMISMATCH,\n\tERR_STATETYPEMISMATCH,\n\tERR_BADBUILTINIMMEDIATE,\n\tERR_BADPARAMORDER,\n\tERR_ILLEGALCONTINUES,\n\tERR_ILLEGALBREAKS,\n\tERR_ILLEGALCASES,\n\tERR_NOTANUMBER,\n\tERR_WRONGSUBTYPE,\n\tERR_EOF,\n\tERR_NOPRECOMPILERIF,\n\tERR_NOENDIF,\n\tERR_HASHERROR,\n\tERR_NOTATYPE,\n\tERR_TOOMANYPACKFILES,\n\tERR_INVALIDVECTORIMMEDIATE,\n\tERR_INVALIDSTRINGIMMEDIATE,\n\tERR_BADCHARACTERCODE,\n\tERR_BADPARMS,\n\tERR_WERROR,\n\n\tWARN_MAX\n};\n\n//ansi colour codes, for debugging stuff.\nenum\n{\n\tCOL_NONE,\t\t//white/regular text.\n\tCOL_ERROR,\t\t//to highlight errors\n\tCOL_WARNING,\t//to highlight warnings\n\tCOL_LOCATION,\t//to highlight file:line locations\n\tCOL_NAME,\t\t//unknown symbols.\n\tCOL_SYMBOL,\t\t//known symbols\n\tCOL_TYPE,\t\t//known types\n\tCOL_MAX\n};\nextern const char *qcccol[COL_MAX];\n#define col_none qcccol[COL_NONE]\n#define col_location qcccol[COL_LOCATION]\n#define col_error qcccol[COL_ERROR]\n#define col_name qcccol[COL_NAME]\n#define col_warning qcccol[COL_WARNING]\n#define col_symbol qcccol[COL_SYMBOL]\n#define col_type qcccol[COL_TYPE]\n\n#define FLAG_KILLSDEBUGGERS\t1\n#define FLAG_ASDEFAULT\t\t2\n#define FLAG_SETINGUI\t\t4\n#define FLAG_HIDDENINGUI\t8\n#define FLAG_MIDCOMPILE\t\t16\t//option can be changed mid-compile with the special pragma\ntypedef struct {\n\tpbool *enabled;\n\tchar *abbrev;\n\tint optimisationlevel;\n\tint flags;\t//1: kills debuggers. 2: applied as default.\n\tchar *fullname;\n\tchar *description;\n\tvoid *guiinfo;\n} optimisations_t;\nextern optimisations_t optimisations[];\n\ntypedef struct {\n\tpbool *enabled;\n\tint flags;\t//2 applied as default\n\tchar *abbrev;\n\tchar *fullname;\n\tchar *description;\n\tvoid *guiinfo;\n} compiler_flag_t;\nextern compiler_flag_t compiler_flag[];\n\n#define WA_IGNORE 0\n#define WA_WARN 1\n#define WA_ERROR 2\nextern unsigned char qccwarningaction[WARN_MAX];\n\nextern\tjmp_buf\t\tpr_parse_abort;\t\t// longjump with this on parse error\nextern const char\t*s_unitn;\t\t\t//used to track compilation units, for global statics.\nextern const char\t*s_filen;\t\t\t//name of the file we're currently compiling.\nextern QCC_string_t\ts_filed;\t\t\t//name of the file we're currently compiling, as seen by whoever reads the .dat\nextern\tint\t\t\tpr_source_line;\nextern\tchar\t\t*pr_file_p;\n\nvoid *QCC_PR_Malloc (int size);\n\n\n#define\tOFS_NULL\t\t0\n#define\tOFS_RETURN\t\t1\n#define\tOFS_PARM0\t\t4\t\t// leave 3 ofs for each parm to hold vectors\n#define\tOFS_PARM1\t\t7\n#define\tOFS_PARM2\t\t10\n#define\tOFS_PARM3\t\t13\n#define\tOFS_PARM4\t\t16\n#define\tRESERVED_OFS\t28\n\n#define VMWORDSIZE\t\t4\n\n\nextern\tstruct QCC_function_s\t*pr_scope;\nextern\tint\t\tpr_error_count, pr_warning_count;\n\nvoid QCC_PR_NewLine (pbool incomment);\n#define GDF_NONE\t\t0\n#define GDF_SAVED\t\t(1<<0)\n#define GDF_STATIC\t\t(1<<1)\n#define GDF_CONST\t\t(1<<2)\n#define GDF_STRIP\t\t(1<<3)\t//always stripped, regardless of optimisations. used for class member fields\n#define GDF_SILENT\t\t(1<<4)\t//used by the gui, to suppress ALL warnings associated with querying the def.\n#define GDF_INLINE\t\t(1<<5)\t//attempt to inline calls to this function\n#define GDF_USED\t\t(1<<6)\t//don't strip this, ever.\n#define GDF_BASICTYPE\t(1<<7)\t//don't care about #merge types not being known correctly.\n#define GDF_SCANLOCAL\t(1<<8)\t//don't use the locals hash table\n#define GDF_POSTINIT\t(1<<9)\t//field must be initialised at the end of the compile (allows arrays to be extended later)\n#define GDF_PARAMETER\t(1<<10)\n#define GDF_AUTODEREF\t(1<<11)\t//for hidden pointers (alloca-ed locals)\n#define GDF_ALIAS\t\t(1<<12)\t//symbol is a later alias within the root symbol rather than a core part of it - don't insert extra defs. generally paired with GDF_STRIP.\nQCC_def_t *QCC_PR_GetDef (QCC_type_t *type, const char *name, struct QCC_function_s *scope, pbool allocate, int arraysize, unsigned int flags);\nQCC_sref_t QCC_PR_GetSRef (QCC_type_t *type, const char *name, struct QCC_function_s *scope, pbool allocate, int arraysize, unsigned int flags);\nvoid QCC_FreeTemp(QCC_sref_t t);\nvoid QCC_FreeDef(QCC_def_t *def);\nchar *QCC_PR_CheckCompConstTooltip(char *word, char *outstart, char *outend);\n\nvoid QCC_PR_PrintDefs (void);\n\nvoid QCC_PR_SkipToSemicolon (void);\n\nextern char *pr_parm_argcount_name;\n#define MAX_EXTRA_PARMS 128\n#ifdef MAX_EXTRA_PARMS\nextern\tchar\t\tpr_parm_names[MAX_PARMS+MAX_EXTRA_PARMS][MAX_NAME];\nextern\tQCC_sref_t\textra_parms[MAX_EXTRA_PARMS];\n#else\nextern\tchar\t\tpr_parm_names[MAX_PARMS][MAX_NAME];\n#endif\n\nchar *QCC_PR_ValueString (etype_t type, void *val);\n\nvoid QCC_PR_ClearGrabMacros (pbool newfile);\n\nvoid QCC_ImportProgs(const char *filename);\npbool\tQCC_PR_CompileFile (char *string, char *filename);\nvoid QCC_PR_ResetErrorScope(void);\n\nextern\tpbool\tpr_dumpasm;\n\nextern\tQCC_def_t\tdef_ret, def_parms[MAX_PARMS];\n\nvoid QCC_PR_EmitArrayGetFunction(QCC_def_t *defscope, QCC_def_t *thearray, char *arrayname);\nvoid QCC_PR_EmitArraySetFunction(QCC_def_t *defscope, QCC_def_t *thearray, char *arrayname);\nvoid QCC_PR_EmitClassFromFunction(QCC_def_t *defscope, QCC_type_t *basetype);\n\nvoid QCC_PR_ParseDefs (const char *classname, pbool fatal);\nQCC_def_t *QCC_PR_DummyDef(QCC_type_t *type, const char *name, QCC_function_t *scope, int arraysize, QCC_def_t *rootsymbol, unsigned int ofs, int referable, unsigned int flags);\nvoid QCC_PR_ParseInitializerDef(QCC_def_t *def, unsigned int flags);\nvoid QCC_PR_FinaliseFunctions(void);\n\n\npbool QCC_main (int argc, const char **argv);\t//as part of the quake engine\nvoid QCC_ContinueCompile(void);\nvoid PostCompile(void);\npbool PreCompile(void);\nvoid QCC_Cleanup(void);\n\n\n#define FIRST_LOCAL 0//(MAX_REGS)\n//=============================================================================\n\nextern char\tpr_immediate_string[8192];\nextern size_t\tpr_immediate_strlen;\n\nextern QCC_eval_basic_t\t\t*qcc_pr_globals;\nextern unsigned int\tnumpr_globals;\n\nextern char\t\t*strings;\nextern int\t\t\tstrofs;\n\nextern QCC_statement_t\t*statements;\nextern int\t\t\tnumstatements;\n\nextern QCC_function_t\t*functions;\nextern dfunction_t\t*dfunctions;\nextern int\t\t\tnumfunctions;\n\nextern QCC_ddef_t\t\t*qcc_globals;\nextern int\t\t\tnumglobaldefs;\n\nextern QCC_def_t\t\t*activetemps;\n\nextern QCC_ddef_t\t\t*fields;\nextern int\t\t\tnumfielddefs;\n\nextern QCC_type_t *qcc_typeinfo;\nextern int numtypeinfos;\nextern int maxtypeinfos;\n\nextern int ForcedCRC;\nextern float qcc_framerate;\t//number of OP_STATE ticks per second.\nextern pbool defaultnoref;\nextern pbool defaultnosave;\nextern pbool defaultstatic;\n\nextern int *qcc_tempofs;\nextern int max_temps;\n//extern int qcc_functioncalled;\t//unuse temps if this is true - don't want to reuse the same space.\n\nextern int tempsstart;\nextern int numtemps;\n\nextern char compilingrootfile[];\t//.src file currently being compiled\n\ntypedef char PATHSTRING[MAX_DATA_PATH];\n\ntypedef struct\n{\n\tPATHSTRING name;\n\tint block;\n\tint used;\n\tint fileline;\n\tconst char *filename;\n} precache_t;\nextern precache_t\t*precache_sound;\nextern int\t\t\tnumsounds;\nextern precache_t\t*precache_texture;\nextern int\t\t\tnumtextures;\nextern precache_t\t*precache_model;\nextern int\t\t\tnummodels;\nextern precache_t\t*precache_file;\nextern int\t\t\tnumfiles;\n\ntypedef struct qcc_includechunk_s {\n\tstruct qcc_includechunk_s *prev;//chunk it was expanded/included from\n\tconst char *currentfilename;\t//filename it was expended from\n\tint currentlinenumber;\t\t\t//line it was expanded from\n\tchar *currentdatapoint;\n\tCompilerConstant_t *cnst;\t\t//define we're expanding from\n\tchar *datastart;\t\t\t\t//the start of the expanded data\n} qcc_includechunk_t;\nextern qcc_includechunk_t *currentchunk;\n\npbool QCC_Include(const char *filename, pbool newunit);\nvoid QCC_PR_IncludeChunkEx (char *data, pbool duplicate, char *filename, CompilerConstant_t *cnst);\n\nint\tQCC_CopyString (const char *str);\nint\tQCC_CopyStringLength (const char *str, size_t length);\n\n\n\n\ntypedef struct qcc_cachedsourcefile_s {\n\tsize_t size;\n\tsize_t bufsize;\n\tsize_t zhdrofs;\n\tint zcrc;\n\tchar *file;\n\tenum{FT_CODE, FT_DATA} type;\t//quakec source file or not.\n\tstruct qcc_cachedsourcefile_s *next;\n\tchar filename[1];\n} qcc_cachedsourcefile_t;\nextern qcc_cachedsourcefile_t *qcc_sourcefile;\nint WriteSourceFiles(qcc_cachedsourcefile_t *filelist, int h, pbool sourceaswell, pbool legacyembed);\n\n\nstruct pkgctx_s;\nenum pkgtype_e\n{\n\tPACKAGER_PAK,\n\tPACKAGER_PK3,\n\tPACKAGER_PK3_SPANNED,\n};\npbool\t\t\tPackager_CompressDir(const char *dirname, enum pkgtype_e type, void (*messagecallback)(void *userctx, const char *message, ...), void *userctx);\nstruct pkgctx_s *Packager_Create(void (*messagecallback)(void *userctx, const char *message, ...), void *userctx);\nvoid\t\t\tPackager_ParseFile(struct pkgctx_s *ctx, char *scriptfilename);\nvoid\t\t\tPackager_ParseText(struct pkgctx_s *ctx, char *scripttext);\nvoid\t\t\tPackager_WriteDataset(struct pkgctx_s *ctx, char *setname);\nvoid\t\t\tPackager_Destroy(struct pkgctx_s *ctx);\n\n\n\n#ifdef COMMONINLINES\nstatic bool inline QCC_PR_CheckToken (char *string)\n{\n\tif (pr_token_type != tt_punct)\n\t\treturn false;\n\n\tif (STRCMP (string, pr_token))\n\t\treturn false;\n\n\tQCC_PR_Lex ();\n\treturn true;\n}\nstatic bool inline QCC_PR_PeekToken (char *string)\n{\n\tif (pr_token_type != tt_punct)\n\t\treturn false;\n\n\tif (STRCMP (string, pr_token))\n\t\treturn false;\n\n\treturn true;\n}\n\nstatic void inline QCC_PR_Expect (const char *string)\n{\n\tif (strcmp (string, pr_token))\n\t\tQCC_PR_ParseError (\"expected %s, found %s\",string, pr_token);\n\tQCC_PR_Lex ();\n}\n#endif\n\nCompilerConstant_t *QCC_PR_DefineName(const char *name, const char *value);\nvoid editbadfile(const char *fname, int line);\nchar *TypeName(QCC_type_t *type, char *buffer, int buffersize);\nvoid QCC_PR_AddIncludePath(const char *newinc);\nvoid QCC_PR_IncludeChunk (char *data, pbool duplicate, char *filename);\nvoid QCC_PR_CloseProcessor(void);\nvoid QCC_FindBestInclude(char *newfile, char *currentfile, pbool verbose);\npbool QCC_PR_UnInclude(void);\nextern void *(*pHash_Get)(hashtable_t *table, const char *name);\nextern void *(*pHash_GetNext)(hashtable_t *table, const char *name, void *old);\nextern void *(*pHash_Add)(hashtable_t *table, const char *name, void *data, bucket_t *);\nextern void (*pHash_RemoveData)(hashtable_t *table, const char *name, void *data);\n\n//when originally running from a .dat, we load up all the functions and work from those rather than actual files.\n//(these get re-written into the resulting .dat)\ntypedef struct qcc_cachedsourcefile_s vfile_t;\nvoid QCC_CloseAllVFiles(void);\nvfile_t *QCC_FindVFile(const char *name);\nvfile_t *QCC_AddVFile(const char *name, void *data, size_t size);\nvoid QCC_CatVFile(vfile_t *, const char *fmt, ...) LIKEPRINTF(2);\nvoid QCC_InsertVFile(vfile_t *, size_t pos, const char *fmt, ...) LIKEPRINTF(3);\nchar *ReadProgsCopyright(char *buf, size_t bufsize);\n\n//void *QCC_ReadFile(const char *fname, unsigned char *(*buf_get)(void *ctx, size_t len), void *buf_ctx, size_t *out_size, pbool issourcefile);\n\n\ntypedef struct\n{\n\tchar\tname[56];\n\tint\t\tfilepos, filelen;\n} packfile_t;\n\ntypedef struct\n{\n\tchar\tid[4];\n\tint\t\tdirofs;\n\tint\t\tdirlen;\n} packheader_t;\n"
  },
  {
    "path": "engine/qclib/qcc_cmdlib.c",
    "content": "// cmdlib.c\n\n#include \"qcc.h\"\n#include <ctype.h>\n//#include <sys/time.h>\n\n#undef progfuncs\n\n#define PATHSEPERATOR   '/'\n\n#ifndef QCC\nextern jmp_buf qcccompileerror;\n#endif\n\n// set these before calling CheckParm\nint myargc;\nconst char **myargv;\n\nchar\tqcc_token[1024];\nint\t\tqcc_eof;\n\nconst unsigned int\t\ttype_size[] = {1,\t//void\n\t\t\t\t\t\tsizeof(string_t)/4,\t//string\n\t\t\t\t\t\t1,\t//float\n\t\t\t\t\t\t3,\t//vector\n\t\t\t\t\t\t1,\t//entity\n\t\t\t\t\t\t1,\t//field\n\t\t\t\t\t\tsizeof(func_t)/4,//function\n\t\t\t\t\t\t1,  //pointer (its an int index)\n\t\t\t\t\t\t1,\t//integer\n\t\t\t\t\t\t1,\t//uint\n\t\t\t\t\t\t2,\t//long\n\t\t\t\t\t\t2,\t//ulong\n\t\t\t\t\t\t2,\t//double\n\t\t\t\t\t\t3,\t//fixme: how big should a variant be?\n\t\t\t\t\t\t0,\t//ev_struct. variable sized.\n\t\t\t\t\t\t0,\t//ev_union. variable sized.\n\t\t\t\t\t\t0,\t//ev_accessor...\n\t\t\t\t\t\t0,\t//ev_enum...\n\t\t\t\t\t\t0,\t//ev_typedef\n\t\t\t\t\t\t1,\t//ev_bool...\n\t\t\t\t\t\t0,\t//bitfld...\n\t\t\t\t\t\t};\n\nchar *basictypenames[] = {\n\t\"void\",\n\t\"string\",\n\t\"float\",\n\t\"vector\",\n\t\"entity\",\n\t\"field\",\n\t\"function\",\n\t\"pointer\",\n\t\"integer\",\n\t\"uint\",\n\t\"long\",\n\t\"ulong\",\n\t\"double\",\n\t\"variant\",\n\t\"struct\",\n\t\"union\",\n\t\"accessor\",\n\t\"enum\",\n\t\"typedef\",\n\t\"bool\",\n\t\"bitfield\",\n};\n\n/*\n============================================================================\n\n\t\t\t\t\tBYTE ORDER FUNCTIONS\n\n============================================================================\n*/\nshort   (*PRBigShort) (short l);\nshort   (*PRLittleShort) (short l);\nint     (*PRBigLong) (int l);\nint     (*PRLittleLong) (int l);\nfloat   (*PRBigFloat) (float l);\nfloat   (*PRLittleFloat) (float l);\n\n\nstatic short   QCC_SwapShort (short l)\n{\n\tpbyte    b1,b2;\n\n\tb1 = l&255;\n\tb2 = (l>>8)&255;\n\n\treturn (b1<<8) + b2;\n}\n\nstatic short   QCC_Short (short l)\n{\n\treturn l;\n}\n\n\nstatic int    QCC_SwapLong (int l)\n{\n\tpbyte    b1,b2,b3,b4;\n\n\tb1 = (pbyte)l;\n\tb2 = (pbyte)(l>>8);\n\tb3 = (pbyte)(l>>16);\n\tb4 = (pbyte)(l>>24);\n\n\treturn ((int)b1<<24) + ((int)b2<<16) + ((int)b3<<8) + b4;\n}\n\nstatic int    QCC_Long (int l)\n{\n\treturn l;\n}\n\n\nstatic float\tQCC_SwapFloat (float l)\n{\n\tunion {pbyte b[4]; float f;} in, out;\n\n\tin.f = l;\n\tout.b[0] = in.b[3];\n\tout.b[1] = in.b[2];\n\tout.b[2] = in.b[1];\n\tout.b[3] = in.b[0];\n\n\treturn out.f;\n}\n\nstatic float\tQCC_Float (float l)\n{\n\treturn l;\n}\n\nvoid SetEndian(void)\n{\n\tunion {pbyte b[2]; unsigned short s;} ed;\n\ted.s = 255;\n\tif (ed.b[0] == 255)\n\t{\n\t\tPRBigShort\t\t= QCC_SwapShort;\n\t\tPRLittleShort\t= QCC_Short;\n\t\tPRBigLong\t\t= QCC_SwapLong;\n\t\tPRLittleLong\t= QCC_Long;\n\t\tPRBigFloat\t\t= QCC_SwapFloat;\n\t\tPRLittleFloat\t= QCC_Float;\n\t}\n\telse\n\t{\n\t\tPRBigShort\t\t= QCC_Short;\n\t\tPRLittleShort\t= QCC_SwapShort;\n\t\tPRBigLong\t\t= QCC_Long;\n\t\tPRLittleLong\t= QCC_SwapLong;\n\t\tPRBigFloat\t\t= QCC_Float;\n\t\tPRLittleFloat\t= QCC_SwapFloat;\n\t}\n}\n\n\npbool QC_strlcat(char *dest, const char *src, size_t destsize)\n{\n\tsize_t curlen = strlen(dest);\n\tif (!destsize)\n\t\treturn false;\t//err\n\tdest += curlen;\n\twhile(*src && ++curlen < destsize)\n\t\t*dest++ = *src++;\n\t*dest = 0;\n\treturn !*src;\n}\npbool QC_strlcpy(char *dest, const char *src, size_t destsize)\n{\n\tsize_t curlen = 0;\n\tif (!destsize)\n\t\treturn false;\t//err\n\twhile(*src && ++curlen < destsize)\n\t\t*dest++ = *src++;\n\t*dest = 0;\n\treturn !*src;\n}\npbool QC_strnlcpy(char *dest, const char *src, size_t srclen, size_t destsize)\n{\n\tsize_t curlen = 0;\n\tif (!destsize)\n\t\treturn false;\t//err\n\tfor(; *src && srclen > 0 && ++curlen < destsize; srclen--)\n\t\t*dest++ = *src++;\n\t*dest = 0;\n\treturn !srclen;\n}\n\nchar *QC_strcasestr(const char *haystack, const char *needle)\n{\n\tint i;\n\tint matchamt=0;\n\tfor(i=0;haystack[i];i++)\n\t{\n\t\tif (tolower(haystack[i]) != tolower(needle[matchamt]))\n\t\t\tmatchamt = 0;\n\t\tif (tolower(haystack[i]) == tolower(needle[matchamt]))\n\t\t{\n\t\t\tmatchamt++;\n\t\t\tif (needle[matchamt]==0)\n\t\t\t\treturn (char *)&haystack[i-(matchamt-1)];\n\t\t}\n\t}\n\treturn 0;\n}\n\n#if !defined(MINIMAL) && !defined(OMIT_QCC)\n/*\n================\nI_FloatTime\n================\n*/\n/*\ndouble I_FloatTime (void)\n{\n\tstruct timeval tp;\n\tstruct timezone tzp;\n\tstatic int\t\tsecbase;\n\n\tgettimeofday(&tp, &tzp);\n\n\tif (!secbase)\n\t{\n\t\tsecbase = tp.tv_sec;\n\t\treturn tp.tv_usec/1000000.0;\n\t}\n\n\treturn (tp.tv_sec - secbase) + tp.tv_usec/1000000.0;\n}\n\n  */\n\n\n#ifdef QCC\nint QC_strncasecmp (const char *s1, const char *s2, int n)\n{\n\tint             c1, c2;\n\n\twhile (1)\n\t{\n\t\tc1 = *s1++;\n\t\tc2 = *s2++;\n\n\t\tif (!n--)\n\t\t\treturn 0;               // strings are equal until end point\n\n\t\tif (c1 != c2)\n\t\t{\n\t\t\tif (c1 >= 'a' && c1 <= 'z')\n\t\t\t\tc1 -= ('a' - 'A');\n\t\t\tif (c2 >= 'a' && c2 <= 'z')\n\t\t\t\tc2 -= ('a' - 'A');\n\t\t\tif (c1 != c2)\n\t\t\t\treturn -1;              // strings not equal\n\t\t}\n\t\tif (!c1)\n\t\t\treturn 0;               // strings are equal\n//              s1++;\n//              s2++;\n\t}\n\n\treturn -1;\n}\n\nint QC_strcasecmp (const char *s1, const char *s2)\n{\n\treturn QC_strncasecmp(s1, s2, 0x7fffffff);\n}\n\n#else\nint QC_strncasecmp(const char *s1, const char *s2, int n);\nint QC_strcasecmp (const char *s1, const char *s2)\n{\n\treturn QC_strncasecmp(s1, s2, 0x7fffffff);\n}\n\n#endif\n\n\n\n#endif\t//minimal\n/*\n==============\nCOM_Parse\n\nParse a token out of a string\n==============\n*/\nchar *QCC_COM_Parse (const char *data)\n{\n\tint\t\tc;\n\tint\t\tlen;\n\n\tlen = 0;\n\tqcc_token[0] = 0;\n\n\tif (!data)\n\t\treturn NULL;\n\n// skip whitespace\nskipwhite:\n\twhile ((c = *data) && qcc_iswhite(c))\n\t\tdata++;\n\tif (!c)\n\t\treturn NULL;\n\n// skip // comments\n\tif (c=='/' && data[1] == '/')\n\t{\n\t\twhile (*data && *data != '\\n')\n\t\t\tdata++;\n\t\tgoto skipwhite;\n\t}\n\n\t// skip /* comments\n\tif (c=='/' && data[1] == '*')\n\t{\n\t\twhile (data[1] && (data[0] != '*' || data[1] != '/'))\n\t\t\tdata++;\n\t\tdata+=2;\n\t\tgoto skipwhite;\n\t}\n\n\n// handle quoted strings specially\n\tif (c == '\\\"')\n\t{\n\t\tdata++;\n\t\tdo\n\t\t{\n\t\t\tc = *data++;\n\t\t\tif (c=='\\\\' && *data == '\\\"')\n\t\t\t\tc = *data++;\t//allow C-style string escapes\n\t\t\telse if (c=='\\\\' && *data == '\\\\')\n\t\t\t\tc = *data++;\t// \\ is now a special character so it needs to be marked up using itself\n\t\t\telse if (c=='\\\\' && *data == 'n')\n\t\t\t{\t\t\t\t\t// and do new lines while we're at it.\n\t\t\t\tc = '\\n';\n\t\t\t\tdata++;\n\t\t\t}\n\t\t\telse if (c=='\\\\' && *data == 'r')\n\t\t\t{\t\t\t\t\t// and do mac lines while we're at it.\n\t\t\t\tc = '\\r';\n\t\t\t\tdata++;\n\t\t\t}\n\t\t\telse if (c=='\\\\' && *data == 't')\n\t\t\t{\t\t\t\t\t// and do tabs while we're at it.\n\t\t\t\tc = '\\t';\n\t\t\t\tdata++;\n\t\t\t}\n\t\t\telse if (c=='\\\"')\n\t\t\t{\n\t\t\t\tqcc_token[len] = 0;\n\t\t\t\treturn (char*)data;\n\t\t\t}\n\t\t\telse if (c=='\\0')\n\t\t\t{\n//\t\t\t\tprintf(\"ERROR: Unterminated string\\n\");\n\t\t\t\tqcc_token[len] = 0;\n\t\t\t\treturn (char*)data;\n\t\t\t}\n\t\t\telse if (c=='\\n' || c=='\\r')\n\t\t\t{\t//new lines are awkward.\n\t\t\t\t//vanilla saved games do not add \\ns on load\n\t\t\t\t//terminating the string on a new line thus has compatbility issues.\n\t\t\t\t//while \"wad\" \"c:\\foo\\\" does happen in the TF community (fucked tools)\n\t\t\t\t//so \\r\\n terminates the string if the last char was an escaped quote, but not otherwise.\n\t\t\t\tif (len > 0 && qcc_token[len-1] == '\\\"')\n\t\t\t\t{\n//\t\t\t\t\tprintf(\"ERROR: new line in string\\n\");\n\t\t\t\t\tqcc_token[len] = 0;\n\t\t\t\t\treturn (char*)data;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (len >= sizeof(qcc_token)-1)\n\t\t\t\t;\n\t\t\telse\n\t\t\t\tqcc_token[len] = c;\n\t\t\tlen++;\n\t\t} while (1);\n\t}\n\n// parse single characters\n\tif (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\\'' || c==':' || c==',')\n\t{\n\t\tqcc_token[len] = c;\n\t\tlen++;\n\t\tqcc_token[len] = 0;\n\t\treturn (char*)data+1;\n\t}\n\n// parse a regular word\n\tdo\n\t{\n\t\tif (len >= sizeof(qcc_token)-1)\n\t\t\t;\n\t\telse\n\t\t\tqcc_token[len++] = c;\n\t\tdata++;\n\t\tc = *data;\n\t\tif (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\\'' || c==':' || c=='\\\"' || c==',')\n\t\t\tbreak;\n\t} while (c && !qcc_iswhite(c));\n\n\tqcc_token[len] = 0;\n\treturn (char*)data;\n}\n\n//more C tokens...\nchar *QCC_COM_Parse2 (char *data)\n{\n\tint\t\tc;\n\tint\t\tlen;\n\n\tlen = 0;\n\tqcc_token[0] = 0;\n\n\tif (!data)\n\t\treturn NULL;\n\n// skip whitespace\nskipwhite:\n\twhile ((c = *data) && qcc_iswhite(c))\n\t\tdata++;\n\tif (!c)\n\t\treturn NULL;\n\n// skip // comments\n\tif (c=='/' && data[1] == '/')\n\t{\n\t\twhile (*data && *data != '\\n')\n\t\t\tdata++;\n\t\tgoto skipwhite;\n\t}\n\n\n// handle quoted strings specially\n\tif (c == '\\\"')\n\t{\n\t\tdata++;\n\t\tdo\n\t\t{\n\t\t\tc = *data++;\n\t\t\tif (c=='\\\\' && *data == '\\\"')\n\t\t\t\tc = *data++;\t//allow C-style string escapes\n\t\t\telse if (c=='\\\\' && *data == '\\\\')\n\t\t\t\tc = *data++;\t// \\ is now a special character so it needs to be marked up using itself\n\t\t\telse if (c=='\\\\' && *data == 'n')\n\t\t\t{\t\t\t\t\t// and do new lines while we're at it.\n\t\t\t\tc = '\\n';\n\t\t\t\tdata++;\n\t\t\t}\n\t\t\telse if (c=='\\\"'||c=='\\0')\n\t\t\t{\n\t\t\t\tif (len < sizeof(qcc_token)-1)\n\t\t\t\t\tqcc_token[len++] = 0;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (len >= sizeof(qcc_token)-1)\n\t\t\t\t;\n\t\t\telse\n\t\t\t\tqcc_token[len++] = c;\n\t\t} while (1);\n\t}\n\n// parse numbers\n\tif (c >= '0' && c <= '9')\n\t{\n\t\tif (c == '0' && data[1] == 'x')\n\t\t{\t//parse hex\n\t\t\tqcc_token[0] = '0';\n\t\t\tc='x';\n\t\t\tlen=1;\n\t\t\tdata++;\n\t\t\tfor(;;)\n\t\t\t{\t//parse regular number\n\t\t\t\tif (len >= sizeof(qcc_token)-1)\n\t\t\t\t\t;\n\t\t\t\telse\n\t\t\t\t\tqcc_token[len++] = c;\n\t\t\t\tdata++;\n\t\t\t\tc = *data;\n\t\t\t\tif ((c<'0'|| c>'9') && (c<'a'||c>'f') && (c<'A'||c>'F') && c != '.')\n\t\t\t\t\tbreak;\n\t\t\t}\n\n\t\t}\n\t\telse\n\t\t{\n\t\t\tfor(;;)\n\t\t\t{\t//parse regular number\n\t\t\t\tif (len >= sizeof(qcc_token)-1)\n\t\t\t\t\t;\n\t\t\t\telse\n\t\t\t\t\tqcc_token[len++] = c;\n\t\t\t\tdata++;\n\t\t\t\tc = *data;\n\t\t\t\tif ((c<'0'|| c>'9') && c != '.')\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tqcc_token[len] = 0;\n\t\treturn data;\n\t}\n// parse words\n\telse if ((c>= 'a' && c <= 'z') || (c>= 'A' && c <= 'Z') || c == '_')\n\t{\n\t\tdo\n\t\t{\n\t\t\tif (len >= sizeof(qcc_token)-1)\n\t\t\t\t;\n\t\t\telse\n\t\t\t\tqcc_token[len++] = c;\n\t\t\tdata++;\n\t\t\tc = *data;\n\t\t} while ((c>= 'a' && c <= 'z') || (c>= 'A' && c <= 'Z') || (c>= '0' && c <= '9') || c == '_');\n\n\t\tqcc_token[len] = 0;\n\t\treturn data;\n\t}\n\telse\n\t{\n\t\tqcc_token[len] = c;\n\t\tlen++;\n\t\tqcc_token[len] = 0;\n\t\treturn data+1;\n\t}\n}\n\nchar *VARGS qcva (char *text, ...)\n{\n\tva_list argptr;\n\tstatic char msg[2048];\n\n\tva_start (argptr,text);\n\tQC_vsnprintf (msg,sizeof(msg)-1, text,argptr);\n\tva_end (argptr);\n\n\treturn msg;\n}\n\n\n#if !defined(MINIMAL) && !defined(OMIT_QCC)\n/*\n=============================================================================\n\n\t\t\t\t\t\tMISC FUNCTIONS\n\n=============================================================================\n*/\n\n/*\n=================\nError\n\nFor abnormal program terminations\n=================\n*/\nvoid VARGS QCC_Error (int errortype, const char *error, ...)\n{\n\tprogfuncs_t *progfuncs = qccprogfuncs;\n\textern int numsourcefiles;\n\tva_list argptr;\n\tchar msg[2048];\n\n\tva_start (argptr,error);\n\tQC_vsnprintf (msg,sizeof(msg)-1, error,argptr);\n\tva_end (argptr);\n\n\texterns->Printf (\"\\n************ ERROR ************\\n%s\\n\", msg);\n\n\n\teditbadfile(s_filen, pr_source_line);\n\n\tnumsourcefiles = 0;\n\n#ifndef QCC\n\tlongjmp(qcccompileerror, 1);\n#else\n\tprint (\"Press any key\\n\");\n\tgetch();\n#endif\n\texit (1);\n}\n\n\n/*\n=================\nCheckParm\n\nChecks for the given parameter in the program's command line arguments\nReturns the argument number (1 to argc-1) or 0 if not present\n=================\n*/\nint QCC_CheckParm (const char *check)\n{\n\tint i;\n\n\tfor (i = 1;i<myargc;i++)\n\t{\n\t\tif ( !QC_strcasecmp(check, myargv[i]) )\n\t\t\treturn i;\n\t}\n\n\treturn 0;\n}\n\nconst char *QCC_ReadParm (const char *check)\n{\n\tint i;\n\n\tfor (i = 1;i+1<myargc;i++)\n\t{\n\t\tif ( !QC_strcasecmp(check, myargv[i]) )\n\t\t\treturn myargv[i+1];\n\t}\n\n\treturn NULL;\n}\n\n/*\n\n\n#ifndef O_BINARY\n#define O_BINARY 0\n#endif\n\n#ifdef QCC\nint SafeOpenWrite (char *filename)\n{\n\tint     handle;\n\n\tumask (0);\n\n\thandle = open(filename,O_WRONLY | O_CREAT | O_TRUNC | O_BINARY\n\t, 0666);\n\n\tif (handle == -1)\n\t\tQCC_Error (\"Error opening %s: %s\",filename,strerror(errno));\n\n\treturn handle;\n}\n#endif\n\nint SafeOpenRead (char *filename)\n{\n\tint     handle;\n\n\thandle = open(filename,O_RDONLY | O_BINARY);\n\n\tif (handle == -1)\n\t\tQCC_Error (\"Error opening %s: %s\",filename,strerror(errno));\n\n\treturn handle;\n}\n\n\nvoid SafeRead (int handle, void *buffer, long count)\n{\n\tif (read (handle,buffer,count) != count)\n\t\tQCC_Error (\"File read failure\");\n}\n\n#ifdef QCC\nvoid SafeWrite (int handle, void *buffer, long count)\n{\n\tif (write (handle,buffer,count) != count)\n\t\tQCC_Error (\"File write failure\");\n}\n#endif\n\n\nvoid *SafeMalloc (long size)\n{\n\tvoid *ptr;\n\n\tptr = (void *)Hunk_Alloc (size);\n\n\tif (!ptr)\n\t\tQCC_Error (\"Malloc failure for %lu bytes\",size);\n\n\treturn ptr;\n}\n\n*/\n\n\n\nvoid DefaultExtension (char *path, char *extension)\n{\n\tchar    *src;\n//\n// if path doesn't have a .EXT, append extension\n// (extension should include the .)\n//\n\tsrc = path + strlen(path) - 1;\n\n\twhile (*src != PATHSEPERATOR && src != path)\n\t{\n\t\tif (*src == '.')\n\t\t\treturn;                 // it has an extension\n\t\tsrc--;\n\t}\n\n\tstrcat (path, extension);\n}\n\n\nvoid DefaultPath (char *path, char *basepath)\n{\n\tchar    temp[128];\n\n\tif (path[0] == PATHSEPERATOR)\n\t\treturn;                   // absolute path location\n\tstrcpy (temp,path);\n\tstrcpy (path,basepath);\n\tstrcat (path,temp);\n}\n\n\nvoid    StripFilename (char *path)\n{\n\tint             length;\n\n\tlength = strlen(path)-1;\n\twhile (length > 0 && path[length] != PATHSEPERATOR)\n\t\tlength--;\n\tpath[length] = 0;\n}\n\n/*\n====================\nExtract file parts\n====================\n*/\nvoid ExtractFilePath (char *path, char *dest)\n{\n\tchar    *src;\n\n\tsrc = path + strlen(path) - 1;\n\n//\n// back up until a \\ or the start\n//\n\twhile (src != path && *(src-1) != PATHSEPERATOR)\n\t\tsrc--;\n\n\tmemcpy (dest, path, src-path);\n\tdest[src-path] = 0;\n}\n\nvoid ExtractFileBase (char *path, char *dest)\n{\n\tchar    *src;\n\n\tsrc = path + strlen(path) - 1;\n\n//\n// back up until a \\ or the start\n//\n\twhile (src != path && *(src-1) != PATHSEPERATOR)\n\t\tsrc--;\n\n\twhile (*src && *src != '.')\n\t{\n\t\t*dest++ = *src++;\n\t}\n\t*dest = 0;\n}\n\nvoid ExtractFileExtension (char *path, char *dest)\n{\n\tchar    *src;\n\n\tsrc = path + strlen(path) - 1;\n\n//\n// back up until a . or the start\n//\n\twhile (src != path && *(src-1) != '.')\n\t\tsrc--;\n\tif (src == path)\n\t{\n\t\t*dest = 0;\t// no extension\n\t\treturn;\n\t}\n\n\tstrcpy (dest,src);\n}\n\n\n/*\n==============\nParseNum / ParseHex\n==============\n*/\nstatic long ParseHex (char *hex)\n{\n\tchar    *str;\n\tlong    num;\n\n\tnum = 0;\n\tstr = hex;\n\n\twhile (*str)\n\t{\n\t\tnum <<= 4;\n\t\tif (*str >= '0' && *str <= '9')\n\t\t\tnum += *str-'0';\n\t\telse if (*str >= 'a' && *str <= 'f')\n\t\t\tnum += 10 + *str-'a';\n\t\telse if (*str >= 'A' && *str <= 'F')\n\t\t\tnum += 10 + *str-'A';\n\t\telse\n\t\t\tQCC_Error (ERR_BADHEX, \"Bad hex number: %s\",hex);\n\t\tstr++;\n\t}\n\n\treturn num;\n}\n\n\nlong ParseNum (char *str)\n{\n\tif (str[0] == '$')\n\t\treturn ParseHex (str+1);\n\tif (str[0] == '0' && str[1] == 'x')\n\t\treturn ParseHex (str+2);\n\treturn atol (str);\n}\n\n\n\n\n\n\n\n\n//buffer size and max size are different. buffer is bigger.\n\n#define MAXQCCFILES 3\nstruct {\n\tchar *name;\n\tFILE *stdio;\n\tchar *buff;\n\tint buffsize;\n\tint ofs;\n\tint maxofs;\n} qccfile[MAXQCCFILES];\nint SafeOpenWrite (char *filename, int maxsize)\n{\n\tint i;\n\tfor (i = 0; i < MAXQCCFILES; i++)\n\t{\n\t\tif (!qccfile[i].stdio && !qccfile[i].buff)\n\t\t{\n\t\t\tqccfile[i].name = strdup(filename);\n\t\t\tqccfile[i].buffsize = maxsize;\n\t\t\tqccfile[i].maxofs = 0;\n\t\t\tqccfile[i].ofs = 0;\n\t\t\tqccfile[i].stdio = NULL;\n\t\t\tqccfile[i].buff = NULL;\n\t\t\tif (maxsize < 0)\n\t\t\t\tqccfile[i].stdio = fopen(filename, \"wb\");\n\t\t\telse\n\t\t\t\tqccfile[i].buff = malloc(qccfile[i].buffsize);\n\t\t\tif (!qccfile[i].stdio && !qccfile[i].buff)\n\t\t\t{\n\t\t\t\tQCC_Error(ERR_TOOMANYOPENFILES, \"Unable to open %s\", filename);\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t\treturn i;\n\t\t}\n\t}\n\tQCC_Error(ERR_TOOMANYOPENFILES, \"Too many open files on file %s\", filename);\n\treturn -1;\n}\n\nstatic void ResizeBuf(int hand, int newsize)\n{\n\tchar *nb;\n\n\tif (qccfile[hand].buffsize >= newsize)\n\t\treturn;\t//already big enough\n\tnb = malloc(newsize);\n\n\tmemcpy(nb, qccfile[hand].buff, qccfile[hand].maxofs);\n\tfree(qccfile[hand].buff);\n\tqccfile[hand].buff = nb;\n\tqccfile[hand].buffsize = newsize;\n}\nvoid SafeWrite(int hand, const void *buf, long count)\n{\n\tif (qccfile[hand].stdio)\n\t{\n\t\tfwrite(buf, 1, count, qccfile[hand].stdio);\n\t}\n\telse\n\t{\n\t\tif (qccfile[hand].ofs +count >= qccfile[hand].buffsize)\n\t\t\tResizeBuf(hand, qccfile[hand].ofs + count+(64*1024));\n\n\t\tmemcpy(&qccfile[hand].buff[qccfile[hand].ofs], buf, count);\n\t}\n\tqccfile[hand].ofs+=count;\n\tif (qccfile[hand].ofs > qccfile[hand].maxofs)\n\t\tqccfile[hand].maxofs = qccfile[hand].ofs;\n}\nint SafeSeek(int hand, int ofs, int mode)\n{\n\tif (mode == SEEK_CUR)\n\t\treturn qccfile[hand].ofs;\n\telse\n\t{\n\t\tif (qccfile[hand].stdio)\n\t\t\tfseek(qccfile[hand].stdio, ofs, SEEK_SET);\n\t\telse\n\t\t\tResizeBuf(hand, ofs+1024);\n\t\tqccfile[hand].ofs = ofs;\n\t\tif (qccfile[hand].ofs > qccfile[hand].maxofs)\n\t\t\tqccfile[hand].maxofs = qccfile[hand].ofs;\n\t\treturn 0;\n\t}\n}\npbool SafeClose(int hand)\n{\n\tprogfuncs_t *progfuncs = qccprogfuncs;\n\tpbool ret;\n\tif (qccfile[hand].stdio)\n\t\tret = 0==fclose(qccfile[hand].stdio);\n\telse\n\t{\n\t\tret = externs->WriteFile(qccfile[hand].name, qccfile[hand].buff, qccfile[hand].maxofs);\n\t\tfree(qccfile[hand].buff);\n\t}\n\tfree(qccfile[hand].name);\n\tqccfile[hand].name = NULL;\n\tqccfile[hand].buff = NULL;\n\tqccfile[hand].stdio = NULL;\n\treturn ret;\n}\n\nqcc_cachedsourcefile_t *qcc_sourcefile;\n\n//return 0 if the input is not valid utf-8.\nunsigned int utf8_check(const void *in, unsigned int *value)\n{\n\t//uc is the output unicode char\n\tunsigned int uc = 0xfffdu;\t//replacement character\n\tconst unsigned char *str = in;\n\n\tif (!(*str & 0x80))\n\t{\n\t\t*value = *str;\n\t\treturn 1;\n\t}\n\telse if ((*str & 0xe0) == 0xc0)\n\t{\n\t\tif ((str[1] & 0xc0) == 0x80)\n\t\t{\n\t\t\t*value = uc = ((str[0] & 0x1f)<<6) | (str[1] & 0x3f);\n\t\t\tif (!uc || uc >= (1u<<7))\t//allow modified utf-8 (only for nulls)\n\t\t\t\treturn 2;\n\t\t}\n\t}\n\telse if ((*str & 0xf0) == 0xe0)\n\t{\n\t\tif ((str[1] & 0xc0) == 0x80 && (str[2] & 0xc0) == 0x80)\n\t\t{\n\t\t\t*value = uc = ((str[0] & 0x0f)<<12) | ((str[1] & 0x3f)<<6) | ((str[2] & 0x3f)<<0);\n\t\t\tif (uc >= (1u<<11))\n\t\t\t\treturn 3;\n\t\t}\n\t}\n\telse if ((*str & 0xf8) == 0xf0)\n\t{\n\t\tif ((str[1] & 0xc0) == 0x80 && (str[2] & 0xc0) == 0x80 && (str[3] & 0xc0) == 0x80)\n\t\t{\n\t\t\t*value = uc = ((str[0] & 0x07)<<18) | ((str[1] & 0x3f)<<12) | ((str[2] & 0x3f)<<6) | ((str[3] & 0x3f)<<0);\n\t\t\tif (uc >= (1u<<16))\t//overlong\n\t\t\t\tif (uc <= 0x10ffff)\t//aand we're not allowed to exceed utf-16 surrogates.\n\t\t\t\t\treturn 4;\n\t\t}\n\t}\n\t*value = 0xFFFD;\n\treturn 0;\n}\n\n//read utf-16 chars and output the 'native' utf-8.\n//we don't expect essays written in code, so we don't need much actual support for utf-8.\nstatic char *decodeUTF(int type, unsigned char *inputf, size_t inbytes, size_t *outlen, pbool usemalloc)\n{\n\tchar *utf8, *start;\n\tunsigned int inc;\n\tunsigned int chars, i;\n\tint w, maxperchar;\n\tswitch(type)\n\t{\n\tcase UTF16LE:\n\t\tw = 2;\n\t\tmaxperchar = 4;\n\t\tbreak;\n\tcase UTF16BE:\n\t\tw = 2;\n\t\tmaxperchar = 4;\n\t\tbreak;\n\tcase UTF32LE:\n\t\tw = 4;\n\t\tmaxperchar = 4;\t//we adhere to RFC3629 and clamp to U+10FFFF, which is only 4 bytes.\n\t\tbreak;\n\tcase UTF32BE:\n\t\tw = 4;\n\t\tmaxperchar = 4;\n\t\tbreak;\n\tdefault:\n\t\t//error\n\t\t*outlen = 0;\n\t\treturn NULL;\n\t}\n\tchars = inbytes / w;\n\tif (usemalloc)\n\t\tutf8 = start = malloc(chars * maxperchar + 2+1);\n\telse\n\t\tutf8 = start = qccHunkAlloc(chars * maxperchar + 2+1);\n\tfor (i = 0; i < chars; i++)\n\t{\n\t\tswitch(type)\n\t\t{\n\t\tdefault:\n\t\t\tinc = 0;\n\t\t\tbreak;\n\t\tcase UTF16LE:\n\t\tcase UTF16BE:\n\t\t\tinc = inputf[type==UTF16BE];\n\t\t\tinc|= inputf[type==UTF16LE]<<8;\n\t\t\tinputf += 2;\n\t\t\t//handle surrogates\n\t\t\tif (inc >= 0xd800u && inc < 0xdc00u && i+1 < chars)\n\t\t\t{\n\t\t\t\tunsigned int l;\n\n\t\t\t\tl = inputf[type==UTF16BE];\n\t\t\t\tl|= inputf[type==UTF16LE]<<8;\n\t\t\t\tif (l >= 0xdc00u && l < 0xe000u)\n\t\t\t\t{\n\t\t\t\t\tinputf+=2;\n\t\t\t\t\tinc = (((inc & 0x3ffu)<<10) | (l & 0x3ffu)) + 0x10000;\n\t\t\t\t\ti++;\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\t\tcase UTF32LE:\n\t\t\tinc = *inputf++;\n\t\t\tinc|= (*inputf++)<<8;\n\t\t\tinc|= (*inputf++)<<16;\n\t\t\tinc|= (*inputf++)<<24;\n\t\t\tbreak;\n\t\tcase UTF32BE:\n\t\t\tinc = (*inputf++)<<24;\n\t\t\tinc|= (*inputf++)<<16;\n\t\t\tinc|= (*inputf++)<<8;\n\t\t\tinc|= *inputf++;\n\t\t\tbreak;\n\t\t}\n\t\tif (inc > 0x10FFFF)\n\t\t\tinc = 0xFFFD;\n\n\t\tif (inc <= 127)\n\t\t\t*utf8++ = inc;\n\t\telse if (inc <= 0x7ff)\n\t\t{\n\t\t\t*utf8++ = ((inc>>6) & 0x1f) | 0xc0;\n\t\t\t*utf8++ = ((inc>>0) & 0x3f) | 0x80;\n\t\t}\n\t\telse if (inc <= 0xffff)\n\t\t{\n\t\t\t*utf8++ = ((inc>>12) & 0xf) | 0xe0;\n\t\t\t*utf8++ = ((inc>>6) & 0x3f) | 0x80;\n\t\t\t*utf8++ = ((inc>>0) & 0x3f) | 0x80;\n\t\t}\n\t\telse if (inc <= 0x1fffff)\n\t\t{\n\t\t\t*utf8++ = ((inc>>18) & 0x07) | 0xf0;\n\t\t\t*utf8++ = ((inc>>12) & 0x3f) | 0x80;\n\t\t\t*utf8++ = ((inc>> 6) & 0x3f) | 0x80;\n\t\t\t*utf8++ = ((inc>> 0) & 0x3f) | 0x80;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tinc = 0xFFFD;\n\t\t\t*utf8++ = ((inc>>12) & 0xf) | 0xe0;\n\t\t\t*utf8++ = ((inc>>6) & 0x3f) | 0x80;\n\t\t\t*utf8++ = ((inc>>0) & 0x3f) | 0x80;\n\t\t}\n\t}\n\t*outlen = utf8 - start;\n\t*utf8 = 0;\n\treturn start;\n}\n\n//the gui is a windows program.\n//this means that its fucked.\n//on the plus side, its okay with a bom...\nunsigned short *QCC_makeutf16(char *mem, size_t len, int *outlen, pbool *errors)\n{\n\tunsigned int code;\n\tint l;\n\tunsigned short *out, *outstart;\n\tpbool nonascii = false;\n\t//sanitise the input.\n\tif (len >= 4 && mem[0] == '\\xff' && mem[1] == '\\xfe' && mem[2] == '\\x00' && mem[3] == '\\x00')\n\t\tmem = decodeUTF(UTF32LE, (unsigned char*)mem+4, len-4, &len, true);\n\telse if (len >= 4 && mem[0] == '\\x00' && mem[1] == '\\x00' && mem[2] == '\\xfe' && mem[3] == '\\xff')\n\t\tmem = decodeUTF(UTF32BE, (unsigned char*)mem+4, len-4, &len, true);\n\telse if (len >= 2 && mem[0] == '\\xff' && mem[1] == '\\xfe')\n\t{\n\t\t//already utf8, just return it as-is\n\t\tout = malloc(len+3);\n\t\tmemcpy(out, mem, len);\n\t\tout[len/2] = 0;\n\t\treturn out;\n\t\t//mem = decodeUTF(UTF16LE, (unsigned char*)mem+2, len-2, &len, false);\n\t}\n\telse if (len >= 2 && mem[0] == '\\xfe' && mem[1] == '\\xff')\n\t\tmem = decodeUTF(UTF16BE, (unsigned char*)mem+2, len-2, &len, false);\n\t//utf-8 BOM, for compat with broken text editors (like windows notepad).\n\telse if (len >= 3 && mem[0] == '\\xef' && mem[1] == '\\xbb' && mem[2] == '\\xbf')\n\t{\n\t\tmem += 3;\n\t\tlen -= 3;\n\t}\n\n\toutstart = malloc(len*2+3);\n\tout = outstart;\n\twhile(len)\n\t{\n\t\tl = utf8_check(mem, &code);\n\t\tif (!l)\n\t\t{l = 1; code = 0xe000|(unsigned char)*mem; nonascii = true;}//fucked up. convert to 0xe000 private-use range.\n\t\tlen -= l;\n\t\tmem += l;\n\n\t\tif (code > 0xffff)\n\t\t{\n\t\t\tcode -= 0x10000;\n\t\t\t*out++ = 0xd800u | ((code>>10) & 0x3ff);\n//\t\t\t*out++ = 0xdc00u | ((code>>00) & 0x3ff);\n\t\t}\n\t\telse\n\t\t\t*out++ = code;\n\t}\n\tif (outlen)\n\t\t*outlen = out - outstart;\n\t*out++ = 0;\n\n\tif (errors)\n\t\t*errors = nonascii;\n\treturn outstart;\n}\n\n//input is a raw file (will not be changed\n//output is utf-8 data\nchar *QCC_SanitizeCharSet(char *mem, size_t *len, pbool *freeresult, int *origfmt)\n{\n\tif (freeresult)\n\t\t*freeresult = true;\n\tif (*len >= 4 && mem[0] == '\\xff' && mem[1] == '\\xfe' && mem[2] == '\\x00' && mem[3] == '\\x00')\n\t\tmem = decodeUTF(*origfmt=UTF32LE, (unsigned char*)mem+4, *len-4, len, !!freeresult);\n\telse if (*len >= 4 && mem[0] == '\\x00' && mem[1] == '\\x00' && mem[2] == '\\xfe' && mem[3] == '\\xff')\n\t\tmem = decodeUTF(*origfmt=UTF32BE, (unsigned char*)mem+4, *len-4, len, !!freeresult);\n\telse if (*len >= 2 && mem[0] == '\\xff' && mem[1] == '\\xfe')\n\t\tmem = decodeUTF(*origfmt=UTF16LE, (unsigned char*)mem+2, *len-2, len, !!freeresult);\n\telse if (*len >= 2 && mem[0] == '\\xfe' && mem[1] == '\\xff')\n\t\tmem = decodeUTF(*origfmt=UTF16BE, (unsigned char*)mem+2, *len-2, len, !!freeresult);\n\t//utf-8 BOM, for compat with broken text editors (like windows notepad).\n\telse if (*len >= 3 && mem[0] == '\\xef' && mem[1] == '\\xbb' && mem[2] == '\\xbf')\n\t{\n\t\t*origfmt=UTF8_BOM;\n\t\tmem += 3;\n\t\t*len -= 3;\n\t\tif (freeresult)\n\t\t\t*freeresult = false;\n\t}\n\telse\n\t{\n\t\tunsigned int ch, cl;\n\t\tchar *p, *e=mem+*len;\n\n\t\t*origfmt=UTF8_RAW;\n\t\tfor (p = mem; p < e; p += cl)\t//validate it, so we're sure what format it actually is\n\t\t{\n\t\t\tcl = utf8_check(p, &ch);\n\t\t\tif (!cl)\n\t\t\t{\n\t\t\t\t*origfmt = UTF_ANSI;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (freeresult)\n\t\t\t*freeresult = false;\n/*\n#ifdef _WIN32\n\t\t//even if we wrote the bom, resaving with wordpad will translate the file to the system's active code page, which will fuck up any comments. thanks for that, wordpad.\n\t\t//(weirdly, notepad does the right thing)\n\t\tint wchars = MultiByteToWideChar(CP_ACP, 0, mem, *len, NULL, 0);\n\n\t\tif (wchars)\n\t\t{\n\t\t\tBOOL failed = false;\n\t\t\twchar_t *wc = malloc(wchars * sizeof(wchar_t));\n\t\t\tint mchars;\n\t\t\tMultiByteToWideChar(CP_ACP, 0, mem, *len, wc, wchars);\n\t\t\tmchars = WideCharToMultiByte(CP_UTF8, 0, wc, wchars, NULL, 0, NULL, NULL);\n\t\t\tif (mchars && !failed)\n\t\t\t{\n\t\t\t\tmem = (freeresult?malloc(mchars+2):qccHunkAlloc(mchars+2));\n\t\t\t\tmem[mchars] = 0;\n\t\t\t\t*len = mchars;\n\t\t\t\tif (freeresult)\n\t\t\t\t\t*freeresult = true;\n\t\t\t\tWideCharToMultiByte(CP_UTF8, 0, wc, wchars, mem, mchars, NULL, NULL);\n\t\t\t}\n\t\t\tfree(wc);\n\t\t}\n#endif\n*/\n\t}\n\n\n\treturn mem;\n}\n\nstatic unsigned char *PDECL QCC_LoadFileHunk(void *ctx, size_t size)\n{\t//2 ensures we can always put a \\n in there.\n\treturn (unsigned char*)qccHunkAlloc(sizeof(qcc_cachedsourcefile_t)+strlen(ctx)+size+2) + sizeof(qcc_cachedsourcefile_t) + strlen(ctx);\n}\n\nlong\tQCC_LoadFile (char *filename, void **bufferptr)\n{\n\tqcc_cachedsourcefile_t *sfile;\n\tprogfuncs_t *progfuncs = qccprogfuncs;\n\tchar *mem;\n\tint check;\n\tsize_t len;\n\tint line;\n\tint orig;\n\tpbool warned = false;\n\n\tmem = externs->ReadFile(filename, QCC_LoadFileHunk, filename, &len, true);\n\tif (!mem)\n\t{\n\t\tQCC_Error(ERR_COULDNTOPENFILE, \"Couldn't open file %s\", filename);\n\t\treturn -1;\n\t}\n\tsfile = (qcc_cachedsourcefile_t*)(mem-sizeof(qcc_cachedsourcefile_t)-strlen(filename));\n\tmem[len] = 0;\n\n\tmem = QCC_SanitizeCharSet(mem, &len, NULL, &orig);\n\n\t//actual utf-8 handling is somewhat up to the engine. the qcc can only ensure that utf8 works in symbol names etc.\n\t//its only in strings where it actually makes a difference, and the interpretation of those is basically entirely up to the engine.\n\t//that said, we could insert a utf-8 BOM into ones with utf-8 chars, but that would mess up a lot of builtins+mods, so we won't.\n\n\tfor (check = 0, line = 1; check < len; check++)\n\t{\n\t\tif (mem[check] == '\\n')\n\t\t\tline++;\n\t\telse if (!mem[check])\n\t\t{\n\t\t\tif (!warned)\n\t\t\t\tQCC_PR_Warning(WARN_UNEXPECTEDPUNCT, filename, line, \"file contains null bytes %u/%\"pPRIuSIZE, check, len);\n\t\t\twarned = true;\n\t\t\t//fixme: insert modified-utf-8 nulls instead.\n\t\t\tmem[check] = ' ';\n\t\t}\n\t}\n\n\tmem[len] = '\\n';\n\tmem[len+1] = '\\0';\n\n\tstrcpy(sfile->filename, filename);\n\tsfile->size = len;\n\tsfile->file = mem;\n\tsfile->type = FT_CODE;\n\tsfile->next = qcc_sourcefile;\n\tqcc_sourcefile = sfile;\n\n\t*bufferptr=mem;\n\n\treturn len;\n}\nvoid\tQCC_AddFile (char *filename)\n{\n\tqcc_cachedsourcefile_t *sfile;\n\tprogfuncs_t *progfuncs = qccprogfuncs;\n\tchar *mem;\n\tsize_t len;\n\n\tmem = externs->ReadFile(filename, QCC_LoadFileHunk, filename, &len, false);\n\tif (!mem)\n\t\texterns->Abort(\"failed to find file %s\", filename);\n\tsfile = (qcc_cachedsourcefile_t*)(mem-sizeof(qcc_cachedsourcefile_t)-strlen(filename));\n\tmem[len] = '\\0';\n\n\tsfile->size = len;\n\tstrcpy(sfile->filename, filename);\n\tsfile->file = mem;\n\tsfile->type = FT_DATA;\n\tsfile->next = qcc_sourcefile;\n\tqcc_sourcefile = sfile;\n\n}\nstatic unsigned char *PDECL FS_ReadToMem_Alloc(void *ctx, size_t size)\n{\n\tunsigned char *mem;\n\tprogfuncs_t *progfuncs = qccprogfuncs;\n\tmem = externs->memalloc(size+1);\n\tmem[size] = 0;\n\treturn mem;\n}\nvoid *FS_ReadToMem(char *filename, size_t *len)\n{\n\tprogfuncs_t *progfuncs = qccprogfuncs;\n\treturn externs->ReadFile(filename, FS_ReadToMem_Alloc, NULL, len, false);\n}\n\nvoid FS_CloseFromMem(void *mem)\n{\n\tprogfuncs_t *progfuncs = qccprogfuncs;\n\texterns->memfree(mem);\n}\n\n\n#endif\n\nvoid    StripExtension (char *path)\n{\n\tint             length;\n\n\tlength = strlen(path)-1;\n\twhile (length > 0 && path[length] != '.')\n\t{\n\t\tlength--;\n\t\tif (path[length] == '/')\n\t\t\treturn;\t\t// no extension\n\t}\n\tif (length)\n\t\tpath[length] = 0;\n}\n"
  },
  {
    "path": "engine/qclib/qcc_pr_comp.c",
    "content": "#if !defined(MINIMAL) && !defined(OMIT_QCC)\n\n#include \"qcc.h\"\n#include <math.h>\n\n#if defined(_WIN32) || defined(__DJGPP__)\n\t#include <malloc.h>\n#elif defined(__unix__) && !defined(__linux__) // quick hack for the bsds and other unix systems\n    #include<stdlib.h>\n#elif !defined(alloca)\t//alloca.h isn't present on bsd (stdlib.h should define it to __builtin_alloca, and we can check for that here).\n\t#include <alloca.h>\n#endif\n\n#ifdef _MSC_VER\n#define longlong __int64\n#define LL(x) x##i64\n#else\n#define longlong long long\n#define LL(x) x##ll\n#endif\n\n/*\nTODO:\n*foo++ = 5;\nis currently store foo->tmp; store 5->*tmp; add tmp,1->foo\nshould be just store 5->*foo; add foo,1->foo\nthe dereference does the post-inc. needs to be delayed somehow. could build a list of post-inc terms after the current expression, but might be really messy.\n*/\n\n#define WARN_IMPLICITVARIANTCAST 0\n\n//FIXME: #define IAMNOTLAZY\n\n#define SUPPORTINLINE\n\nvoid QCC_PR_ParseAsm(void);\n\n#define MEMBERFIELDNAME \"__m%s\"\n\n#define STRCMP(s1,s2) (((*s1)!=(*s2)) || strcmp(s1,s2))\t//saves about 2-6 out of 120 - expansion of idea from fastqcc\n#define STRNCMP(s1,s2,l) (((*s1)!=(*s2)) || strncmp(s1,s2,l))\t//pathetic saving here.\n\nextern char *compilingfile;\n\nint conditional;\n\n//standard qc keywords\n#define keyword_do\t\t1\n#define keyword_return\t1\n#define keyword_if\t\t1\n#define keyword_else\t1\n#define keyword_while\t1\n\n//extended keywords.\npbool keyword_switch;\t//hexen2/c\npbool keyword_case;\t\t//hexen2/c\npbool keyword_default;\t//hexen2/c\npbool keyword_break;\t//hexen2/c\npbool keyword_continue;\t//hexen2/c\npbool keyword_loop;\t\t//hexen2\npbool keyword_until;\t//hexen2\npbool keyword_thinktime;//hexen2\npbool keyword_asm;\npbool keyword_class;\npbool keyword_accessor;\npbool keyword_inout;\npbool keyword_optional;\npbool keyword_const;\t//fixme\npbool keyword_entity;\t//for skipping the local\npbool keyword_float;\t//for skipping the local\npbool keyword_double;\npbool keyword_for;\npbool keyword_goto;\npbool keyword_char;\npbool keyword_short;\npbool keyword_int;\t\t//for skipping the local\npbool keyword_integer;\t//for skipping the local\npbool keyword_long;\npbool keyword_signed;\npbool keyword_unsigned;\npbool keyword_register;\npbool keyword_volatile;\npbool keyword_state;\npbool keyword_string;\t//for skipping the local\npbool keyword_struct;\npbool keyword_var;\t\t//allow it to be initialised and set around the place.\npbool keyword_vector;\t//for skipping the local\npbool keyword_local;\npbool keyword_static;\npbool keyword_auto;\npbool keyword_nonstatic;\npbool keyword_used;\npbool keyword_unused;\n\n\npbool keyword_enum;\t//kinda like in c, but typedef not supported.\npbool keyword_enumflags;\t//like enum, but doubles instead of adds 1.\npbool keyword_typedef;\t//fixme\n#define keyword_codesys\t\tflag_acc\t//reacc needs this (forces the resultant crc)\n#define keyword_function\tflag_acc\t//reacc needs this (reacc has this on all functions, wierd eh?)\n#define keyword_objdata\t\tflag_acc\t//reacc needs this (following defs are fields rather than globals, use var to disable)\n#define keyword_object\t\tflag_acc\t//reacc needs this (an entity)\n#define keyword_pfunc\t\tflag_acc\t//reacc needs this (pointer to function)\n#define keyword_system\t\tflag_acc\t//reacc needs this (potatos)\n#define keyword_real\t\tflag_acc\t//reacc needs this (a float)\n#define keyword_exit\t\tflag_acc\t//emits an OP_DONE opcode.\n#define keyword_external\tflag_acc\t//reacc needs this (a builtin)\npbool keyword_extern;\t//function is external, don't error or warn if the body was not found\npbool keyword_shared;\t//mark global to be copied over when progs changes (part of FTE_MULTIPROGS)\npbool keyword_noref;\t//nowhere else references this, don't strip it.\npbool keyword_nosave;\t//don't write the def to the output.\npbool keyword_inline;\npbool keyword_strip;\npbool keyword_ignore;\npbool keyword_union;\t//you surly know what a union is!\npbool keyword_weak;\npbool keyword_wrap;\npbool keyword_accumulate;\npbool keyword_using;\n\n#define keyword_not\t\t\t1\t//hexenc support needs this, and fteqcc can optimise without it, but it adds an extra token after the if, so it can cause no namespace conflicts\n\npbool keywords_coexist;\t\t//don't disable a keyword simply because a var was made with the same name.\npbool output_parms;\t\t\t//emit some PARMX fields. confuses decompilers.\npbool autoprototype;\t\t//take two passes over the source code. First time round doesn't enter and functions or initialise variables.\npbool autoprototyped;\t\t//previously autoprototyped. no longer allowed to enable autoproto, but don't warn about it.\npbool parseonly;\t\t\t//parse defs and stuff, but don't bother compiling any actual code.\npbool pr_subscopedlocals;\t//causes locals to be valid ONLY within their statement block. (they simply can't be referenced by name outside of it)\npbool flag_nullemptystr;\t//null immediates are 0, not 1.\npbool flag_brokenifstring;\t//break strings even more\npbool flag_ifstring;\t\t//makes if (blah) equivelent to if (blah != \"\") which resolves some issues in multiprogs situations.\npbool flag_iffloat;\t\t\t//use an op_if_f instruction instead of op_if so if(-0) evaluates to false.\npbool flag_ifvector;\t\t//use an op_not_v instruction instead of testing only _x.\npbool flag_vectorlogic;\t\t//flag_ifvector but for && and ||\npbool flag_acc;\t\t\t\t//reacc like behaviour of src files (finds *.qc in start dir and compiles all in alphabetical order)\npbool flag_caseinsensitive;\t//symbols will be matched to an insensitive case if the specified case doesn't exist. This should b usable for any mod\npbool flag_laxcasts;\t\t//Allow lax casting. This'll produce loadsa warnings of course. But allows compilation of certain dodgy code.\npbool flag_hashonly;\t\t//Allows use of only #constant for precompiler constants, allows certain preqcc using mods to compile\npbool flag_macroinstrings;\t//preqcc's support macro expansion in strings (blind expansion).\npbool flag_fasttrackarrays;\t//Faster arrays, dynamically detected, activated only in supporting engines.\npbool flag_msvcstyle;\t\t//MSVC style warnings, so msvc's ide works properly\npbool flag_debugmacros;\t\t//Print out #defines as they are expanded, for debugging.\npbool flag_assume_integer;\t//5 - is that an integer or a float? qcc says float. but we support int too, so maybe we want that instead?\npbool flag_assume_double;\t//5.0 - is that single or double precision? QC says float, but C says double. should probably only be used with assume-int enabled too.\npbool flag_filetimes;\t\t//only rebuild if files were modified.\npbool flag_typeexplicit;\t//no implicit type conversions, you must do the casts yourself.\npbool flag_boundchecks;\t\t//Disable generation of bound check instructions.\npbool flag_guiannotate;\t\t//spit out lots of extra text that the gui can interpret to display some inline asm\npbool flag_brokenarrays;\t//return array; returns array[0] instead of &array;\npbool flag_rootconstructor;\t//if true, class constructors are ordered to call the super constructor first, rather than the child constructor\npbool flag_qccx;\t\t\t//accept qccx syntax. you may wish to disable warnings separately.\npbool flag_attributes;\t\t//gmqcc-style attributes\npbool flag_assumevar;\t\t//initialised globals will no longer be considered constant\npbool flag_dblstarexp;\t\t// a**b is pow(a,b) instead of a*(*b)\npbool flag_cpriority;\t\t//operator precidence should adhere to C standards, instead of QC compatibility.\npbool flag_qcfuncs;\t\t\t//void() is a function type, and not a syntax error.\npbool flag_allowuninit;\t\t//ignore uninitialised locals, avoiding all private locals.\npbool flag_embedsrc;\t\t//embed all source files inside the .dat (can be opened with any zip program)\npbool flag_noreflection;\t//no reflection stuff, for smaller unsavable binaries.\npbool flag_nopragmafileline;//ignore #pragma file and #pragma line, so that I can actually read+debug xonotic's code.\npbool flag_utf8strings;\t\t//strings default to u8\"\" string rules.\npbool flag_reciprocalmaths;\t//unsafe maths optimisations\npbool flag_ILP32;\t\t\t//restrict long to 32 bits. long long still exists.\npbool flag_undefwordsize;\t//pointers are NOT always multiples of 4. sizeof becomes an error foo.length will still work though. pointer maths will resort to OP_ADD_PIW in order to still work (or just break). char and short types are unusable. casting between strings and pointers is an error (though you can still contrive casts past it). for compat with DP and its potential 64bit qcvm.\npbool flag_pointerrelocs;\t//engine accepts ev_pointer globaldefs and biases them by the in-memory globals.\n\npbool opt_overlaptemps;\t\t//reduce numpr_globals by reuse of temps. When they are not needed they are freed for reuse. The way this is implemented is better than frikqcc's. (This is the single most important optimisation)\npbool opt_assignments;\t\t//STORE_F isn't used if an operation wrote to a temp.\npbool opt_shortenifnots;\t\t//if(!var) is made an IF rather than NOT IFNOT\npbool opt_noduplicatestrings;\t//brute force string check. time consuming but more effective than the equivelent in frikqcc.\npbool opt_constantarithmatic;\t//3*5 appears as 15 instead of the extra statement.\npbool opt_nonvec_parms;\t\t\t//store_f instead of store_v on function calls, where possible.\npbool opt_constant_names;\t\t//take out the defs and name strings of constants.\npbool opt_constant_names_strings;//removes the defs of strings too. plays havok with multiprogs.\npbool opt_precache_file;\t\t\t//remove the call, the parameters, everything.\npbool opt_filenames;\t\t\t\t//strip filenames. hinders older decompilers.\npbool opt_unreferenced;\t\t\t//strip defs that are not referenced.\npbool opt_function_names;\t\t//strip out the names of builtin functions.\npbool opt_locals;\t\t\t\t//strip out the names of locals and immediates.\npbool opt_dupconstdefs;\t\t\t//float X = 5; and float Y = 5; occupy the same global with this.\npbool opt_return_only;\t\t\t//RETURN; DONE; at the end of a function strips out the done statement if there is no way to get to it.\npbool opt_compound_jumps;\t\t//jumps to jump statements jump to the final point.\npbool opt_stripfunctions;\t\t//if a functions is only ever called directly or by exe, don't emit the def.\npbool opt_locals_overlapping;\t//make the local vars of all functions occupy the same globals.\npbool opt_logicops;\t\t\t\t//don't make conditions enter functions if the return value will be discarded due to a previous value. (C style if statements)\npbool opt_vectorcalls;\t\t\t//vectors can be packed into 3 floats, which can yield lower numpr_globals, but cost two more statements per call (only works for q1 calling conventions).\npbool opt_classfields;\npbool opt_simplifiedifs;\t\t//if (f != 0) -> if_f (f). if (f == 0) -> ifnot_f (f)\n//bool opt_comexprremoval;\n\n//these are the results of the opt_. The values are printed out when compilation is compleate, showing effectivness.\nint optres_shortenifnots;\nint optres_assignments;\nint optres_overlaptemps;\nint optres_noduplicatestrings;\nint optres_constantarithmatic;\nint optres_nonvec_parms;\nint optres_constant_names;\nint optres_constant_names_strings;\nint optres_precache_file;\nint optres_filenames;\nint optres_unreferenced;\nint optres_function_names;\nint optres_locals;\nint optres_dupconstdefs;\nint optres_return_only;\nint optres_compound_jumps;\n//int optres_comexprremoval;\nint optres_stripfunctions;\nint optres_locals_overlapping;\nint optres_logicops;\nint optres_inlines;\n\nint optres_test1;\nint optres_test2;\n\nvoid *(*pHash_Get)(hashtable_t *table, const char *name);\nvoid *(*pHash_GetNext)(hashtable_t *table, const char *name, void *old);\nvoid *(*pHash_Add)(hashtable_t *table, const char *name, void *data, bucket_t *);\nvoid (*pHash_RemoveData)(hashtable_t *table, const char *name, void *data);\n\nQCC_type_t *QCC_PR_FindType (QCC_type_t *type);\nQCC_type_t *QCC_PR_PointerType (QCC_type_t *pointsto);\nQCC_type_t *QCC_PR_FieldType (QCC_type_t *pointsto);\nQCC_sref_t\tQCC_PR_Term (unsigned int exprflags);\nQCC_sref_t\tQCC_PR_ParseValue (QCC_type_t *assumeclass, pbool allowarrayassign, pbool expandmemberfields, pbool makearraypointers);\nvoid\t\tQCC_Marshal_Locals(int firststatement, int laststatement);\nQCC_sref_t\tQCC_PR_ParseArrayPointer (QCC_sref_t d, pbool allowarrayassign, pbool makestructpointers);\nQCC_sref_t\tQCC_LoadFromArray(QCC_sref_t base, QCC_sref_t index, QCC_type_t *t, pbool preserve);\nvoid\t\t QCC_PR_ParseInitializerDef(QCC_def_t *def, unsigned int flags);\n\nstatic pbool QCC_RefNeedsCalls(QCC_ref_t *ref);\nQCC_ref_t *QCC_DefToRef(QCC_ref_t *ref, QCC_sref_t def);\t//ref is a buffer to write into, to avoid excessive allocs\nQCC_sref_t\tQCC_RefToDef(QCC_ref_t *ref, pbool freetemps);\nQCC_ref_t *QCC_PR_RefExpression (QCC_ref_t *retbuf, int priority, int exprflags);\nQCC_ref_t *QCC_PR_ParseRefValue (QCC_ref_t *refbuf, QCC_type_t *assumeclass, pbool allowarrayassign, pbool expandmemberfields, pbool makearraypointers);\nQCC_ref_t *QCC_PR_ParseRefArrayPointer (QCC_ref_t *refbuf, QCC_ref_t *d, pbool allowarrayassign, pbool makestructpointers);\nQCC_ref_t *QCC_PR_BuildRef(QCC_ref_t *retbuf, unsigned int reftype, QCC_sref_t base, QCC_sref_t index, QCC_type_t *cast, pbool\treadonly, unsigned int bitofs);\nQCC_ref_t *QCC_PR_BuildAccessorRef(QCC_ref_t *retbuf, QCC_sref_t base, QCC_sref_t index, struct accessor_s *accessor, pbool readonly);\nQCC_sref_t\tQCC_StoreSRefToRef(QCC_ref_t *dest, QCC_sref_t source, pbool readable, pbool preservedest);\nQCC_sref_t\tQCC_StoreRefToRef(QCC_ref_t *dest, QCC_ref_t *source, pbool readable, pbool preservedest);\nvoid QCC_PR_DiscardRef(QCC_ref_t *ref);\nQCC_function_t *QCC_PR_ParseImmediateStatements (QCC_def_t *def, QCC_type_t *type, pbool dowrap);\nconst char *QCC_VarAtOffset(QCC_sref_t ref);\nQCC_sref_t QCC_EvaluateCast(QCC_sref_t src, QCC_type_t *cast, pbool implicit);\nQCC_sref_t QCC_PR_ParseInitializerTemp(QCC_type_t *type);\npbool QCC_PR_ParseInitializerType(int arraysize, QCC_def_t *basedef, QCC_sref_t def, unsigned bitofs, unsigned int flags);\n#define PIF_WRAP\t\t1\t//new initialisation is meant to wrap an existing one.\n#define PIF_STRONGER\t2\t//previous initialisation was weak.\n#define PIF_ACCUMULATE\t4\t//glue them together...\n#define PIF_AUTOWRAP\t8\t//accumulate without wrap must autowrap if the function was not previously defined as accumulate...\n\nQCC_statement_t *QCC_Generate_OP_IFNOT(QCC_sref_t e, pbool preserve);\nQCC_statement_t *QCC_Generate_OP_IF(QCC_sref_t e, pbool preserve);\nQCC_statement_t *QCC_Generate_OP_GOTO(void);\nQCC_sref_t QCC_PR_GenerateLogicalNot(QCC_sref_t e, const char *errormessage);\nstatic QCC_function_t *QCC_PR_GenerateQCFunction (QCC_def_t *def, QCC_type_t *type, unsigned int *pif_flags);\nstatic void QCC_StoreToSRef(QCC_sref_t dest, QCC_sref_t source, QCC_type_t *type, pbool preservesource, pbool preservedest);\nvoid QCC_PR_ParseStatement (void);\n\n//NOTE: prints may use from the func argument's symbol, which can be awkward if its a temp.\nQCC_sref_t\tQCC_PR_GenerateFunctionCallSref (QCC_sref_t newself, QCC_sref_t func, QCC_sref_t *arglist, int argcount);\nQCC_sref_t QCC_PR_GenerateFunctionCallRef (QCC_sref_t newself, QCC_sref_t func, QCC_ref_t **arglist, unsigned int argcount);\nQCC_sref_t QCC_PR_GenerateFunctionCall1 (QCC_sref_t newself, QCC_sref_t func, QCC_sref_t a, QCC_type_t *type_a);\nQCC_sref_t QCC_PR_GenerateFunctionCall2 (QCC_sref_t newself, QCC_sref_t func, QCC_sref_t a, QCC_type_t *type_a, QCC_sref_t b, QCC_type_t *type_b);\nQCC_sref_t QCC_PR_GenerateFunctionCall3 (QCC_sref_t newself, QCC_sref_t func, QCC_sref_t a, QCC_type_t *type_a, QCC_sref_t b, QCC_type_t *type_b, QCC_sref_t c, QCC_type_t *type_c);\n\nQCC_sref_t QCC_MakeTranslateStringConst(const char *value);\nQCC_sref_t QCC_MakeStringConst(const char *value);\nQCC_sref_t QCC_MakeStringConstLength(const char *value, int length);\nQCC_sref_t QCC_MakeFloatConst(pvec_t value);\nQCC_sref_t QCC_MakeDoubleConst(double value);\nQCC_sref_t QCC_MakeFloatConstFromInt(longlong llvalue);\nQCC_sref_t QCC_MakeIntConst(longlong llvalue);\t//longlongs for warnings\nQCC_sref_t QCC_MakeUIntConst(unsigned longlong llvalue);\nQCC_sref_t QCC_MakeInt64Const(longlong llvalue);\nQCC_sref_t QCC_MakeUInt64Const(unsigned longlong llvalue);\nstatic QCC_sref_t QCC_MakeUniqueConst(QCC_type_t *type, void *data);\nQCC_sref_t QCC_MakeVectorConst(pvec_t a, pvec_t b, pvec_t c);\nstatic QCC_sref_t QCC_MakeGAddress(QCC_type_t *type, QCC_def_t *relocof, int idx, int bitofs);\n\nenum\n{\n\tSTFL_PRESERVEA=1u<<0,\t//if a temp is released as part of the statement, it can be reused for the result. Which is bad if the temp is needed for something else, like e.e.f += 4;\n\tSTFL_CONVERTA=1u<<1,\t\t//convert to/from ints/floats to match the operand types required by the opcode\n\tSTFL_PRESERVEB=1u<<2,\n\tSTFL_CONVERTB=1u<<3,\n\tSTFL_DISCARDRESULT=1u<<4,\n\tSTFL_NOEMULATE=1u<<5,\t//don't emulate unsupported opcode with other opcodes.\n};\n#define QCC_PR_Statement(op,a,b,st) QCC_PR_StatementFlags(op,a,b,st,STFL_CONVERTA|STFL_CONVERTB)\nQCC_sref_t QCC_PR_StatementFlags ( QCC_opcode_t *op, QCC_sref_t var_a, QCC_sref_t var_b, QCC_statement_t **outstatement, unsigned int flags);\n#ifdef __GNUC__\nvoid QCC_PR_StatementAnnotation(const char *fmt, ...) __attribute__((deprecated(\"don't forget about me\")))LIKEPRINTF(1);\n#endif\n\nvoid QCC_PR_ParseState (void);\npbool expandedemptymacro;\t//just inhibits warnings about hanging semicolons\n\nQCC_pr_info_t\tpr;\n//QCC_def_t\t\t**pr_global_defs/*[MAX_REGS]*/;\t// to find def for a global variable\n\n//keeps track of how many funcs are called while parsing a statement\n//int qcc_functioncalled;\n\n//========================================\n\nconst QCC_sref_t nullsref = {0};\n\nstruct QCC_function_s\t\t*pr_scope;\t\t// the function being parsed, or NULL\nQCC_type_t\t\t*pr_classtype;\t// the class that the current function is part of.\nQCC_type_t\t\t*pr_assumetermtype;\t//undefined things get this time, with no warning about being undeclared (used for the state function, so prototypes are not needed)\nQCC_function_t\t*pr_assumetermscope;\nunsigned int\tpr_assumetermflags; //GDF_\npbool\t\t\tpr_ignoredeprecation;\npbool\tpr_dumpasm;\nconst char\t\t*s_unitn;\nconst char\t\t*s_filen;\nQCC_string_t\ts_filed;\t\t\t// filename for function definition\n\nunsigned int\t\t\tlocals_marshalled;\t// largest local block size that needs to be allocated for locals overlapping.\n\njmp_buf\t\tpr_parse_abort;\t\t// longjump with this on parse error\n\npbool qcc_usefulstatement;\n\npbool debug_armour_defined;\n\nint max_breaks;\nint max_continues;\nint max_cases;\nint num_continues;\nint num_breaks;\nint num_cases;\nint *pr_breaks;\nint *pr_continues;\nint *pr_cases;\nQCC_ref_t *pr_casesref;\nQCC_ref_t *pr_casesref2;\n\n#define MAX_LABEL_LENGTH 256\ntypedef struct {\n\tint statementno;\n\tint lineno;\n\tchar name[MAX_LABEL_LENGTH];\n} gotooperator_t;\n\nint max_labels;\nint max_gotos;\ngotooperator_t *pr_labels;\ngotooperator_t *pr_gotos;\nint num_gotos;\nint num_labels;\n\nQCC_sref_t extra_parms[MAX_EXTRA_PARMS];\n\nstatic QCC_statement_t *initstatements;\nstatic size_t numinitstatements, maxinitstatements;\n\n//#define ASSOC_RIGHT_RESULT ASSOC_RIGHT\n\n//========================================\n\n#undef PC_NONE\nenum\n{\n\tPC_NONE,\n\tPC_STORE,\t//stores are handled specially, but its still nice to mark them\n\tPC_UNARY,\t//these happen elsewhere\n\tPC_MEMBER,\t//these happen elsewhere\n\tPC_TERNARY,\n\tPC_UNARYNOT,\n\n\tPC_MULDIV,\n\tPC_ADDSUB,\n\tPC_SHIFT,\n\tPC_RELATION,\n\tPC_EQUALITY,\n\tPC_BITAND,\n\tPC_BITXOR,\n\tPC_BITOR,\n\tPC_LOGICAND,\n\tPC_LOGICOR,\n\tMAX_PRIORITY_CLASSES\n};\nstatic int priority_class[MAX_PRIORITY_CLASSES+1];\t//to simplify implementation slightly\n\n/*static char *reftypename[] =\n{\n\t\"REF_GLOBAL\",\n\t\"REF_ARRAY\",\n\t\"REF_ARRAYHEAD\",\n\t\"REF_POINTER\",\n\t\"REF_POINTERARRAY\",\n\t\"REF_FIELD\",\n\t\"REF_STRING\",\n\t\"REF_NONVIRTUAL\",\n\t\"REF_THISCALL\",\n\t\"REF_ACCESSOR\"\n};*/\n\n//FIXME: modifiy list so most common GROUPS are first\n//use look up table for value of first char and sort by first char and most common...?\n\n//if true, effectivly {b=a; return a;}\nQCC_opcode_t pr_opcodes[] =\n{\n {6, \"<DONE>\", \"DONE\",\t\tPC_NONE,\tASSOC_LEFT,\t\t&type_void,\t\t&type_void,\t\t&type_void},\n\n {6, \"*\", \"MUL_F\",\t\t\tPC_MULDIV,\tASSOC_LEFT,\t\t&type_float,\t&type_float,\t&type_float,\tOPF_STD},\n {6, \"*\", \"MUL_V\",\t\t\tPC_MULDIV,\tASSOC_LEFT,\t\t&type_vector,\t&type_vector,\t&type_float,\tOPF_STD},\n {6, \"*\", \"MUL_FV\",\t\t\tPC_MULDIV,\tASSOC_LEFT,\t\t&type_float,\t&type_vector,\t&type_vector,\tOPF_STD},\n {6, \"*\", \"MUL_VF\",\t\t\tPC_MULDIV,\tASSOC_LEFT,\t\t&type_vector,\t&type_float,\t&type_vector,\tOPF_STD},\n\n {6, \"/\", \"DIV_F\",\t\t\tPC_MULDIV,\tASSOC_LEFT,\t\t&type_float,\t&type_float,\t&type_float,\tOPF_STD},\n\n {6, \"+\", \"ADD_F\",\t\t\tPC_ADDSUB,\tASSOC_LEFT,\t\t&type_float,\t&type_float,\t&type_float,\tOPF_STD},\n {6, \"+\", \"ADD_V\",\t\t\tPC_ADDSUB,\tASSOC_LEFT,\t\t&type_vector,\t&type_vector,\t&type_vector,\tOPF_STD},\n\n {6, \"-\", \"SUB_F\",\t\t\tPC_ADDSUB,\tASSOC_LEFT,\t\t&type_float,\t&type_float,\t&type_float,\tOPF_STD},\n {6, \"-\", \"SUB_V\",\t\t\tPC_ADDSUB,\tASSOC_LEFT,\t\t&type_vector,\t&type_vector,\t&type_vector,\tOPF_STD},\n\n {6, \"==\", \"EQ_F\",\t\t\tPC_EQUALITY, ASSOC_LEFT,\t&type_float,\t&type_float,\t&type_bfloat,\tOPF_STD},\n {6, \"==\", \"EQ_V\",\t\t\tPC_EQUALITY, ASSOC_LEFT,\t&type_vector,\t&type_vector,\t&type_bfloat,\tOPF_STD},\n {6, \"==\", \"EQ_S\",\t\t\tPC_EQUALITY, ASSOC_LEFT,\t&type_string,\t&type_string,\t&type_bfloat,\tOPF_STD},\n {6, \"==\", \"EQ_E\",\t\t\tPC_EQUALITY, ASSOC_LEFT,\t&type_entity,\t&type_entity,\t&type_bfloat,\tOPF_STD},\n {6, \"==\", \"EQ_FNC\",\t\tPC_EQUALITY, ASSOC_LEFT,\t&type_function,\t&type_function,\t&type_bfloat,\tOPF_STD},\n\n {6, \"!=\", \"NE_F\",\t\t\tPC_EQUALITY, ASSOC_LEFT,\t&type_float,\t&type_float,\t&type_bfloat,\tOPF_STD},\n {6, \"!=\", \"NE_V\",\t\t\tPC_EQUALITY, ASSOC_LEFT,\t&type_vector,\t&type_vector,\t&type_bfloat,\tOPF_STD},\n {6, \"!=\", \"NE_S\",\t\t\tPC_EQUALITY, ASSOC_LEFT,\t&type_string,\t&type_string,\t&type_bfloat,\tOPF_STD},\n {6, \"!=\", \"NE_E\",\t\t\tPC_EQUALITY, ASSOC_LEFT,\t&type_entity,\t&type_entity,\t&type_bfloat,\tOPF_STD},\n {6, \"!=\", \"NE_FNC\",\t\tPC_EQUALITY, ASSOC_LEFT,\t&type_function,\t&type_function,\t&type_bfloat,\tOPF_STD},\n\n {6, \"<=\", \"LE_F\",\t\t\tPC_RELATION, ASSOC_LEFT,\t&type_float,\t&type_float,\t&type_bfloat,\tOPF_STD},\n {6, \">=\", \"GE_F\",\t\t\tPC_RELATION, ASSOC_LEFT,\t&type_float,\t&type_float,\t&type_bfloat,\tOPF_STD},\n {6, \"<\", \"LT_F\",\t\t\tPC_RELATION, ASSOC_LEFT,\t&type_float,\t&type_float,\t&type_bfloat,\tOPF_STD},\n {6, \">\", \"GT_F\",\t\t\tPC_RELATION, ASSOC_LEFT,\t&type_float,\t&type_float,\t&type_bfloat,\tOPF_STD},\n\n {6, \".\", \"LOADF_F\",\t\tPC_MEMBER, ASSOC_LEFT,\t\t&type_entity,\t&type_field,\t&type_float},\n {6, \".\", \"LOADF_V\",\t\tPC_MEMBER, ASSOC_LEFT,\t\t&type_entity,\t&type_field,\t&type_vector},\n {6, \".\", \"LOADF_S\",\t\tPC_MEMBER, ASSOC_LEFT,\t\t&type_entity,\t&type_field,\t&type_string},\n {6, \".\", \"LOADF_ENT\",\t\tPC_MEMBER, ASSOC_LEFT,\t\t&type_entity,\t&type_field,\t&type_entity},\n {6, \".\", \"LOADF_FLD\",\t\tPC_MEMBER, ASSOC_LEFT,\t\t&type_entity,\t&type_field,\t&type_field},\n {6, \".\", \"LOADF_FNC\",\t\tPC_MEMBER, ASSOC_LEFT,\t\t&type_entity,\t&type_field,\t&type_function},\n\n {6, \".\", \"FLDADDRESS\",\t\tPC_MEMBER, ASSOC_LEFT,\t\t&type_entity,\t&type_field,\t&type_pointer},\n\n {6, \"=\", \"STORE_F\",\t\tPC_STORE, ASSOC_RIGHT,\t\t&type_float,\t&type_float,\t&type_float,\tOPF_STORE},\n {6, \"=\", \"STORE_V\",\t\tPC_STORE, ASSOC_RIGHT,\t\t&type_vector,\t&type_vector,\t&type_vector,\tOPF_STORE},\n {6, \"=\", \"STORE_S\",\t\tPC_STORE, ASSOC_RIGHT,\t\t&type_string,\t&type_string,\t&type_string,\tOPF_STORE},\n {6, \"=\", \"STORE_ENT\",\t\tPC_STORE, ASSOC_RIGHT,\t\t&type_entity,\t&type_entity,\t&type_entity,\tOPF_STORE},\n {6, \"=\", \"STORE_FLD\",\t\tPC_STORE, ASSOC_RIGHT,\t\t&type_field,\t&type_field,\t&type_field,\tOPF_STORE},\n {6, \"=\", \"STORE_FNC\",\t\tPC_STORE, ASSOC_RIGHT,\t\t&type_function,\t&type_function,\t&type_function,\tOPF_STORE},\n\n {6, \"=\", \"STOREP_F\",\t\tPC_STORE, ASSOC_RIGHT,\t\t&type_pointer,\t&type_float,\t&type_float,\tOPF_STOREPTROFS},\n {6, \"=\", \"STOREP_V\",\t\tPC_STORE, ASSOC_RIGHT,\t\t&type_pointer,\t&type_vector,\t&type_vector,\tOPF_STOREPTROFS},\n {6, \"=\", \"STOREP_S\",\t\tPC_STORE, ASSOC_RIGHT,\t\t&type_pointer,\t&type_string,\t&type_string,\tOPF_STOREPTROFS},\n {6, \"=\", \"STOREP_ENT\",\t\tPC_STORE, ASSOC_RIGHT,\t\t&type_pointer,\t&type_entity,\t&type_entity,\tOPF_STOREPTROFS},\n {6, \"=\", \"STOREP_FLD\",\t\tPC_STORE, ASSOC_RIGHT,\t\t&type_pointer,\t&type_field,\t&type_field,\tOPF_STOREPTROFS},\n {6, \"=\", \"STOREP_FNC\",\t\tPC_STORE, ASSOC_RIGHT,\t\t&type_pointer,\t&type_function,\t&type_function,\tOPF_STOREPTROFS},\n\n {6, \"<RETURN>\", \"RETURN\",\tPC_NONE, ASSOC_LEFT,\t\t&type_vector,\t&type_void,\t\t&type_void},\n\n {6, \"!\", \"NOT_F\",\t\t\tPC_UNARY, ASSOC_LEFT,\t\t&type_float,\t&type_void,\t\t&type_bfloat,\tOPF_STDUNARY},\n {6, \"!\", \"NOT_V\",\t\t\tPC_UNARY, ASSOC_LEFT,\t\t&type_vector,\t&type_void,\t\t&type_bfloat,\tOPF_STDUNARY},\n {6, \"!\", \"NOT_S\",\t\t\tPC_UNARY, ASSOC_LEFT,\t\t&type_vector,\t&type_void,\t\t&type_bfloat,\tOPF_STDUNARY},\n {6, \"!\", \"NOT_ENT\",\t\tPC_UNARY, ASSOC_LEFT,\t\t&type_entity,\t&type_void,\t\t&type_bfloat,\tOPF_STDUNARY},\n {6, \"!\", \"NOT_FNC\",\t\tPC_UNARY, ASSOC_LEFT,\t\t&type_function,\t&type_void,\t\t&type_bfloat,\tOPF_STDUNARY},\n\n  {6, \"<IF>\", \"IF\",\t\t\tPC_NONE, ASSOC_RIGHT,\t\t&type_float,\tNULL,\t\t\t&type_void},\n  {6, \"<IFNOT>\", \"IFNOT\",\tPC_NONE, ASSOC_RIGHT,\t\t&type_float,\tNULL,\t\t\t&type_void},\n\n// calls returns REG_RETURN\n {6, \"<CALL0>\", \"CALL0\",\tPC_NONE, ASSOC_LEFT,\t\t&type_function,\t&type_void,\t\t&type_void},\n {6, \"<CALL1>\", \"CALL1\",\tPC_NONE, ASSOC_LEFT,\t\t&type_function,\t&type_void,\t\t&type_void},\n {6, \"<CALL2>\", \"CALL2\",\tPC_NONE, ASSOC_LEFT,\t\t&type_function,\t&type_void,\t\t&type_void},\n {6, \"<CALL3>\", \"CALL3\",\tPC_NONE, ASSOC_LEFT,\t\t&type_function,\t&type_void,\t\t&type_void},\n {6, \"<CALL4>\", \"CALL4\",\tPC_NONE, ASSOC_LEFT,\t\t&type_function,\t&type_void,\t\t&type_void},\n {6, \"<CALL5>\", \"CALL5\",\tPC_NONE, ASSOC_LEFT,\t\t&type_function,\t&type_void,\t\t&type_void},\n {6, \"<CALL6>\", \"CALL6\",\tPC_NONE, ASSOC_LEFT,\t\t&type_function,\t&type_void,\t\t&type_void},\n {6, \"<CALL7>\", \"CALL7\",\tPC_NONE, ASSOC_LEFT,\t\t&type_function,\t&type_void,\t\t&type_void},\n {6, \"<CALL8>\", \"CALL8\",\tPC_NONE, ASSOC_LEFT,\t\t&type_function,\t&type_void,\t\t&type_void},\n\n {6, \"<STATE>\", \"STATE\",\tPC_NONE, ASSOC_LEFT,\t\t&type_float,\t&type_float,\t&type_void},\n\n {6, \"<GOTO>\", \"GOTO\",\t\tPC_NONE, ASSOC_RIGHT,\t\tNULL,\t\t\t&type_void,\t\t&type_void},\n\n {6, \"&&\", \"AND_F\",\t\t\tPC_LOGICAND, ASSOC_LEFT,\t&type_float,\t&type_float,\t&type_bfloat,\tOPF_STD},\n {6, \"||\", \"OR_F\",\t\t\tPC_LOGICOR, ASSOC_LEFT,\t\t&type_float,\t&type_float,\t&type_bfloat,\tOPF_STD},\n\n {6, \"&\", \"BITAND\",\t\t\tPC_BITAND, ASSOC_LEFT,\t\t&type_float,\t&type_float,\t&type_float,\t\tOPF_STD},\n {6, \"|\", \"BITOR\",\t\t\tPC_BITOR, ASSOC_LEFT,\t\t&type_float,\t&type_float,\t&type_float,\t\tOPF_STD},\n\n //version 6 are in normal progs.\n\n\n\n//these are hexen2\n {7, \"*=\", \"MULSTORE_F\",\tPC_STORE, ASSOC_RIGHT_RESULT,\t\t\t\t&type_float, &type_float, &type_float,\t\tOPF_STORE},\n {7, \"*=\", \"MULSTORE_VF\",\tPC_STORE, ASSOC_RIGHT_RESULT,\t\t\t\t&type_vector, &type_float, &type_vector,\tOPF_STORE},\n {7, \"*=\", \"MULSTOREP_F\",\tPC_STORE, ASSOC_RIGHT_RESULT,\t\t\t\t&type_pointer, &type_float, &type_float,\tOPF_STOREPTR},\n {7, \"*=\", \"MULSTOREP_VF\",\tPC_STORE, ASSOC_RIGHT_RESULT,\t\t\t\t&type_pointer, &type_float, &type_vector,\tOPF_STOREPTR},\n\n {7, \"/=\", \"DIVSTORE_F\",\tPC_STORE, ASSOC_RIGHT_RESULT,\t\t\t\t&type_float, &type_float, &type_float,\t\tOPF_STORE},\n {7, \"/=\", \"DIVSTOREP_F\",\tPC_STORE, ASSOC_RIGHT_RESULT,\t\t\t\t&type_pointer, &type_float, &type_float,\tOPF_STOREPTR},\n\n {7, \"+=\", \"ADDSTORE_F\",\tPC_STORE, ASSOC_RIGHT_RESULT,\t\t\t\t&type_float, &type_float, &type_float,\t\tOPF_STORE},\n {7, \"+=\", \"ADDSTORE_V\",\tPC_STORE, ASSOC_RIGHT_RESULT,\t\t\t\t&type_vector, &type_vector, &type_vector,\tOPF_STORE},\n {7, \"+=\", \"ADDSTOREP_F\",\tPC_STORE, ASSOC_RIGHT_RESULT,\t\t\t\t&type_pointer, &type_float, &type_float,\tOPF_STOREPTR},\n {7, \"+=\", \"ADDSTOREP_V\",\tPC_STORE, ASSOC_RIGHT_RESULT,\t\t\t\t&type_pointer, &type_vector, &type_vector,\tOPF_STOREPTR},\n\n {7, \"-=\", \"SUBSTORE_F\",\tPC_STORE, ASSOC_RIGHT_RESULT,\t\t\t\t&type_float, &type_float, &type_float,\t\tOPF_STORE},\n {7, \"-=\", \"SUBSTORE_V\",\tPC_STORE, ASSOC_RIGHT_RESULT,\t\t\t\t&type_vector, &type_vector, &type_vector,\tOPF_STORE},\n {7, \"-=\", \"SUBSTOREP_F\",\tPC_STORE, ASSOC_RIGHT_RESULT,\t\t\t\t&type_pointer, &type_float, &type_float,\tOPF_STOREPTR},\n {7, \"-=\", \"SUBSTOREP_V\",\tPC_STORE, ASSOC_RIGHT_RESULT,\t\t\t\t&type_pointer, &type_vector, &type_vector,\tOPF_STOREPTR},\n\n {7, \"<FETCH_GBL_F>\", \"FETCH_GBL_F\",\t\tPC_NONE, ASSOC_LEFT,\t&type_float, &type_float, &type_float},\n {7, \"<FETCH_GBL_V>\", \"FETCH_GBL_V\",\t\tPC_NONE, ASSOC_LEFT,\t&type_vector, &type_float, &type_vector},\n {7, \"<FETCH_GBL_S>\", \"FETCH_GBL_S\",\t\tPC_NONE, ASSOC_LEFT,\t&type_string, &type_float, &type_string},\n {7, \"<FETCH_GBL_E>\", \"FETCH_GBL_E\",\t\tPC_NONE, ASSOC_LEFT,\t&type_entity, &type_float, &type_entity},\n {7, \"<FETCH_GBL_FNC>\", \"FETCH_GBL_FNC\",\tPC_NONE, ASSOC_LEFT,\t&type_function, &type_float, &type_function},\n\n {7, \"<CSTATE>\", \"CSTATE\",\t\t\t\t\tPC_NONE, ASSOC_LEFT,\t&type_float, &type_float, &type_void},\n\n {7, \"<CWSTATE>\", \"CWSTATE\",\t\t\t\tPC_NONE, ASSOC_LEFT,\t&type_float, &type_float, &type_void},\n\n {7, \"<THINKTIME>\", \"THINKTIME\",\t\t\tPC_NONE, ASSOC_LEFT,\t&type_entity, &type_float, &type_void},\n\n {7, \"|=\", \"BITSETSTORE_F\",\t\t\t\t\tPC_STORE,\tASSOC_RIGHT,\t&type_float, &type_float, &type_float,\tOPF_STORE},\n {7, \"|=\", \"BITSETSTOREP_F\",\t\t\t\tPC_STORE,\tASSOC_RIGHT,\t&type_pointer, &type_float, &type_float,OPF_STOREPTR},\n {7, \"&~=\", \"BITCLRSTORE_F\",\t\t\t\tPC_STORE,\tASSOC_RIGHT,\t&type_float, &type_float, &type_float,\tOPF_STORE},\n {7, \"&~=\", \"BITCLRSTOREP_F\",\t\t\t\tPC_STORE,\tASSOC_RIGHT,\t&type_pointer, &type_float, &type_float,OPF_STOREPTR},\n\n {7, \"<RAND0>\", \"RAND0\",\t\t\t\t\tPC_NONE, ASSOC_LEFT,\t&type_void, &type_void, &type_float},\n {7, \"<RAND1>\", \"RAND1\",\t\t\t\t\tPC_NONE, ASSOC_LEFT,\t&type_float, &type_void, &type_float},\n {7, \"<RAND2>\", \"RAND2\",\t\t\t\t\tPC_NONE, ASSOC_LEFT,\t&type_float, &type_float, &type_float},\n {7, \"<RANDV0>\", \"RANDV0\",\t\t\t\t\tPC_NONE, ASSOC_LEFT,\t&type_void, &type_void, &type_vector},\n {7, \"<RANDV1>\", \"RANDV1\",\t\t\t\t\tPC_NONE, ASSOC_LEFT,\t&type_vector, &type_void, &type_vector},\n {7, \"<RANDV2>\", \"RANDV2\",\t\t\t\t\tPC_NONE, ASSOC_LEFT,\t&type_vector, &type_vector, &type_vector},\n\n {7, \"<SWITCH_F>\", \"SWITCH_F\",\t\t\t\tPC_NONE, ASSOC_RIGHT,\t&type_float, NULL, &type_void},\n {7, \"<SWITCH_V>\", \"SWITCH_V\",\t\t\t\tPC_NONE, ASSOC_RIGHT,\t&type_vector, NULL, &type_void},\n {7, \"<SWITCH_S>\", \"SWITCH_S\",\t\t\t\tPC_NONE, ASSOC_RIGHT,\t&type_string, NULL, &type_void},\n {7, \"<SWITCH_E>\", \"SWITCH_E\",\t\t\t\tPC_NONE, ASSOC_RIGHT,\t&type_entity, NULL, &type_void},\n {7, \"<SWITCH_FNC>\", \"SWITCH_FNC\",\t\t\tPC_NONE, ASSOC_RIGHT,\t&type_function, NULL, &type_void},\n\n {7, \"<CASE>\", \"CASE\",\t\t\t\t\t\tPC_NONE, ASSOC_RIGHT,\t&type_variant, NULL, &type_void},\n {7, \"<CASERANGE>\", \"CASERANGE\",\t\t\tPC_NONE, ASSOC_RIGHT,\t&type_float, &type_float, NULL},\n\n\n//Later are additions by DMW.\n\n {7, \"<CALL1H>\", \"CALL1H\",\tPC_NONE, ASSOC_RIGHT,\t\t&type_function, &type_variant, &type_void},\n {7, \"<CALL2H>\", \"CALL2H\",\tPC_NONE, ASSOC_RIGHT,\t\t&type_function, &type_variant, &type_variant},\n {7, \"<CALL3H>\", \"CALL3H\",\tPC_NONE, ASSOC_RIGHT,\t\t&type_function, &type_variant, &type_variant},\n {7, \"<CALL4H>\", \"CALL4H\",\tPC_NONE, ASSOC_RIGHT,\t\t&type_function, &type_variant, &type_variant},\n {7, \"<CALL5H>\", \"CALL5H\",\tPC_NONE, ASSOC_RIGHT,\t\t&type_function, &type_variant, &type_variant},\n {7, \"<CALL6H>\", \"CALL6H\",\tPC_NONE, ASSOC_RIGHT,\t\t&type_function, &type_variant, &type_variant},\n {7, \"<CALL7H>\", \"CALL7H\",\tPC_NONE, ASSOC_RIGHT,\t\t&type_function, &type_variant, &type_variant},\n {7, \"<CALL8H>\", \"CALL8H\",\tPC_NONE, ASSOC_RIGHT,\t\t&type_function, &type_variant, &type_variant},\n\n {7, \"=\",\t\"STORE_I\",\t\tPC_STORE, ASSOC_RIGHT,\t\t&type_integer, &type_integer, &type_integer,\tOPF_STORE},\n {7, \"=\",\t\"STORE_IF\",\t\tPC_STORE, ASSOC_RIGHT,\t\t&type_float, &type_integer, &type_integer,\tOPF_STORE},\n {7, \"=\",\t\"STORE_FI\",\t\tPC_STORE, ASSOC_RIGHT,\t\t&type_integer, &type_float, &type_float,\tOPF_STORE},\n\n {7, \"+\", \"ADD_I\",\t\t\tPC_ADDSUB, ASSOC_LEFT,\t\t&type_integer, &type_integer, &type_integer,OPF_STD},\n {7, \"+\", \"ADD_FI\",\t\t\tPC_ADDSUB, ASSOC_LEFT,\t\t&type_float, &type_integer, &type_float,\tOPF_STD},\n {7, \"+\", \"ADD_IF\",\t\t\tPC_ADDSUB, ASSOC_LEFT,\t\t&type_integer, &type_float, &type_float,\tOPF_STD},\n\n {7, \"-\", \"SUB_I\",\t\t\tPC_ADDSUB, ASSOC_LEFT,\t\t&type_integer, &type_integer, &type_integer,OPF_STD},\n {7, \"-\", \"SUB_FI\",\t\t\tPC_ADDSUB, ASSOC_LEFT,\t\t&type_float, &type_integer, &type_float,\tOPF_STD},\n {7, \"-\", \"SUB_IF\",\t\t\tPC_ADDSUB, ASSOC_LEFT,\t\t&type_integer, &type_float, &type_float,\tOPF_STD},\n\n {7, \"<CIF>\", \"CONV_IF\",\tPC_STORE, ASSOC_LEFT,\t\t&type_integer, &type_void, &type_float},\n {7, \"<CFI>\", \"CONV_FI\",\tPC_STORE, ASSOC_LEFT,\t\t&type_float, &type_void, &type_integer},\n {7, \"<CPIF>\", \"CONVP_IF\",\tPC_STORE, ASSOC_LEFT,\t\t&type_pointer, &type_integer, &type_float},\n {7, \"<CPFI>\", \"CONVP_FI\",\tPC_STORE, ASSOC_LEFT,\t\t&type_pointer, &type_float, &type_integer},\n\n {7, \".\", \"LOADF_I\",\tPC_MEMBER, ASSOC_LEFT,\t\t\t&type_entity,\t&type_field, &type_integer},\n {7, \"=\", \"STOREP_I\",\tPC_STORE, ASSOC_RIGHT,\t\t\t&type_pointer,\t&type_integer, &type_integer,\tOPF_STOREPTROFS},\n {7, \"=\", \"STOREP_IF\",\tPC_STORE, ASSOC_RIGHT,\t\t\t&type_pointer,\t&type_float, &type_integer,\tOPF_STOREPTROFS},\n {7, \"=\", \"STOREP_FI\",\tPC_STORE, ASSOC_RIGHT,\t\t\t&type_pointer,\t&type_integer, &type_float,\tOPF_STOREPTROFS},\n\n {7, \"&\", \"BITAND_I\",\tPC_BITAND, ASSOC_LEFT,\t\t\t&type_integer,\t&type_integer, &type_integer,OPF_STD},\n {7, \"|\", \"BITOR_I\",\tPC_BITOR, ASSOC_LEFT,\t\t\t&type_integer,\t&type_integer, &type_integer,OPF_STD},\n\n {7, \"*\", \"MUL_I\",\t\tPC_MULDIV, ASSOC_LEFT,\t\t\t&type_integer,\t&type_integer, &type_integer,OPF_STD},\n {7, \"/\", \"DIV_I\",\t\tPC_MULDIV, ASSOC_LEFT,\t\t\t&type_integer,\t&type_integer, &type_integer,OPF_STD},\n {7, \"==\", \"EQ_I\",\t\tPC_EQUALITY, ASSOC_LEFT,\t\t&type_integer,\t&type_integer, &type_bint\t,OPF_STD},\n {7, \"!=\", \"NE_I\",\t\tPC_EQUALITY, ASSOC_LEFT,\t\t&type_integer,\t&type_integer, &type_bint\t,OPF_STD},\n\n {7, \"<IFNOTS>\", \"IFNOTS\",\tPC_NONE, ASSOC_RIGHT,\t\t&type_string,\tNULL, &type_void},\n {7, \"<IFS>\", \"IFS\",\t\tPC_NONE, ASSOC_RIGHT,\t\t&type_string,\tNULL, &type_void},\n\n {7, \"!\", \"NOT_I\",\t\t\tPC_UNARY, ASSOC_LEFT,\t\t&type_integer,\t&type_void,\t\t&type_bint,\tOPF_STDUNARY},\n\n {7, \"/\", \"DIV_VF\",\t\t\tPC_MULDIV, ASSOC_LEFT,\t\t&type_vector,\t&type_float, &type_vector,\tOPF_STD},\n\n {7, \"^\", \"BITXOR_I\",\t\tPC_BITXOR, ASSOC_LEFT,\t\t&type_integer,\t&type_integer, &type_integer,OPF_STD},\n {7, \">>\", \"RSHIFT_I\",\t\tPC_SHIFT, ASSOC_LEFT,\t\t&type_integer,\t&type_integer, &type_integer,OPF_STD},\n {7, \"<<\", \"LSHIFT_I\",\t\tPC_SHIFT, ASSOC_LEFT,\t\t&type_integer,\t&type_integer, &type_integer,OPF_STD},\n\n\t\t\t\t\t\t\t\t\t\t//var,\t\toffset\t\t\treturn\n {7, \"<ARRAY>\", \"GLOBALADDRESS\", PC_NONE, ASSOC_LEFT,\t&type_float,\t\t&type_integer, &type_pointer},\n {7, \"<ARRAY>\", \"ADD_PIW\", PC_NONE, ASSOC_LEFT,\t\t\t&type_pointer,\t&type_integer, &type_pointer},\n\n {7, \"=\", \"LOADA_F\",\t\tPC_STORE, ASSOC_LEFT,\t\t&type_float,\t&type_integer, &type_float},\n {7, \"=\", \"LOADA_V\",\t\tPC_STORE, ASSOC_LEFT,\t\t&type_vector,\t&type_integer, &type_vector},\n {7, \"=\", \"LOADA_S\",\t\tPC_STORE, ASSOC_LEFT,\t\t&type_string,\t&type_integer, &type_string},\n {7, \"=\", \"LOADA_ENT\",\t\tPC_STORE, ASSOC_LEFT,\t\t&type_entity,\t&type_integer, &type_entity},\n {7, \"=\", \"LOADA_FLD\",\t\tPC_STORE, ASSOC_LEFT,\t\t&type_field,\t&type_integer, &type_field},\n {7, \"=\", \"LOADA_FNC\",\t\tPC_STORE, ASSOC_LEFT,\t\t&type_function,\t&type_integer, &type_function},\n {7, \"=\", \"LOADA_I\",\t\tPC_STORE, ASSOC_LEFT,\t\t&type_integer,\t&type_integer, &type_integer},\n\n {7, \"=\", \"STORE_P\",\t\tPC_STORE, ASSOC_RIGHT,\t\t&type_pointer,\t&type_pointer, &type_void,\tOPF_STORE},\n {7, \".\", \"LOADF_P\",\t\tPC_MEMBER, ASSOC_LEFT,\t\t&type_entity,\t&type_field, &type_pointer},\n\n {7, \"=\", \"LOADP_F\",\t\tPC_STORE, ASSOC_LEFT,\t\t&type_pointer,\t&type_integer, &type_float,\tOPF_LOADPTR},\n {7, \"=\", \"LOADP_V\",\t\tPC_STORE, ASSOC_LEFT,\t\t&type_pointer,\t&type_integer, &type_vector,\tOPF_LOADPTR},\n {7, \"=\", \"LOADP_S\",\t\tPC_STORE, ASSOC_LEFT,\t\t&type_pointer,\t&type_integer, &type_string,\tOPF_LOADPTR},\n {7, \"=\", \"LOADP_ENT\",\t\tPC_STORE, ASSOC_LEFT,\t\t&type_pointer,\t&type_integer, &type_entity,\tOPF_LOADPTR},\n {7, \"=\", \"LOADP_FLD\",\t\tPC_STORE, ASSOC_LEFT,\t\t&type_pointer,\t&type_integer, &type_field,\tOPF_LOADPTR},\n {7, \"=\", \"LOADP_FNC\",\t\tPC_STORE, ASSOC_LEFT,\t\t&type_pointer,\t&type_integer, &type_function,\tOPF_LOADPTR},\n {7, \"=\", \"LOADP_I\",\t\tPC_STORE, ASSOC_LEFT,\t\t&type_pointer,\t&type_integer, &type_integer,\tOPF_LOADPTR},\n\n\n {7, \"<=\",\t\"LE_I\",\t\t\tPC_RELATION, ASSOC_LEFT,\t&type_integer,\t&type_integer,\t&type_bint,\tOPF_STD},\n {7, \">=\",\t\"GE_I\",\t\t\tPC_RELATION, ASSOC_LEFT,\t&type_integer,\t&type_integer,\t&type_bint,\tOPF_STD},\n {7, \"<\",\t\"LT_I\",\t\t\tPC_RELATION, ASSOC_LEFT,\t&type_integer,\t&type_integer,\t&type_bint,\tOPF_STD},\n {7, \">\",\t\"GT_I\",\t\t\tPC_RELATION, ASSOC_LEFT,\t&type_integer,\t&type_integer,\t&type_bint,\tOPF_STD},\n\n {7, \"<=\",\t\"LE_IF\",\t\tPC_RELATION, ASSOC_LEFT,\t&type_integer,\t&type_float,\t&type_bint,\tOPF_STD},\n {7, \">=\",\t\"GE_IF\",\t\tPC_RELATION, ASSOC_LEFT,\t&type_integer,\t&type_float,\t&type_bint,\tOPF_STD},\n {7, \"<\",\t\"LT_IF\",\t\tPC_RELATION, ASSOC_LEFT,\t&type_integer,\t&type_float,\t&type_bint,\tOPF_STD},\n {7, \">\",\t\"GT_IF\",\t\tPC_RELATION, ASSOC_LEFT,\t&type_integer,\t&type_float,\t&type_bint,\tOPF_STD},\n\n {7, \"<=\",\t\"LE_FI\",\t\tPC_RELATION, ASSOC_LEFT,\t&type_float,\t&type_integer,\t&type_bint,\tOPF_STD},\n {7, \">=\",\t\"GE_FI\",\t\tPC_RELATION, ASSOC_LEFT,\t&type_float,\t&type_integer,\t&type_bint,\tOPF_STD},\n {7, \"<\",\t\"LT_FI\",\t\tPC_RELATION, ASSOC_LEFT,\t&type_float,\t&type_integer,\t&type_bint,\tOPF_STD},\n {7, \">\",\t\"GT_FI\",\t\tPC_RELATION, ASSOC_LEFT,\t&type_float,\t&type_integer,\t&type_bint,\tOPF_STD},\n\n {7, \"==\",\t\"EQ_IF\",\t\tPC_EQUALITY, ASSOC_LEFT,\t&type_integer,\t&type_float,\t&type_bint,\tOPF_STD},\n {7, \"==\",\t\"EQ_FI\",\t\tPC_EQUALITY, ASSOC_LEFT,\t&type_float,\t&type_integer,\t&type_bint,\tOPF_STD},\n\n \t//-------------------------------------\n\t//string manipulation.\n {7, \"+\", \"ADD_SF\",\tPC_ADDSUB, ASSOC_LEFT,\t\t\t\t&type_string,\t&type_float,\t&type_string,\tOPF_STD},\n {7, \"-\", \"SUB_S\",\tPC_ADDSUB, ASSOC_LEFT,\t\t\t\t&type_string,\t&type_string,\t&type_float,\tOPF_STD},\n {7, \"<STOREP_C>\", \"STOREP_C\",\tPC_STORE, ASSOC_RIGHT,\t&type_string,\t&type_float,\t&type_float,\tOPF_STOREPTROFS},\n {7, \"<LOADP_C>\", \"LOADP_C\",\tPC_STORE, ASSOC_LEFT,\t&type_string,\t&type_float,\t&type_float,\tOPF_LOADPTR},\n\t//-------------------------------------\n\n\n\n{7, \"*\", \"MUL_IF\",\tPC_MULDIV, ASSOC_LEFT,\t\t\t\t&type_integer,\t&type_float,\t&type_float,\tOPF_STD},\n{7, \"*\", \"MUL_FI\",\tPC_MULDIV, ASSOC_LEFT,\t\t\t\t&type_float,\t&type_integer,\t&type_float,\tOPF_STD},\n{7, \"*\", \"MUL_VI\",\tPC_MULDIV, ASSOC_LEFT,\t\t\t\t&type_vector,\t&type_integer,\t&type_vector,\tOPF_STD},\n{7, \"*\", \"MUL_IV\",\tPC_MULDIV, ASSOC_LEFT,\t\t\t\t&type_integer,\t&type_vector,\t&type_vector,\tOPF_STD},\n\n{7, \"/\", \"DIV_IF\",\tPC_MULDIV, ASSOC_LEFT,\t\t\t\t&type_integer,\t&type_float,\t&type_float,\tOPF_STD},\n{7, \"/\", \"DIV_FI\",\tPC_MULDIV, ASSOC_LEFT,\t\t\t\t&type_float,\t&type_integer,\t&type_float,\tOPF_STD},\n\n{7, \"&\", \"BITAND_IF\",\tPC_BITAND, ASSOC_LEFT,\t\t\t&type_integer,\t&type_float,\t&type_integer,\tOPF_STD},\n{7, \"|\", \"BITOR_IF\",\tPC_BITOR, ASSOC_LEFT,\t\t\t&type_integer,\t&type_float,\t&type_integer,\tOPF_STD},\n{7, \"&\", \"BITAND_FI\",\tPC_BITAND, ASSOC_LEFT,\t\t\t&type_float,\t&type_integer,\t&type_integer,\tOPF_STD},\n{7, \"|\", \"BITOR_FI\",\tPC_BITOR, ASSOC_LEFT,\t\t\t&type_float,\t&type_integer,\t&type_integer,\tOPF_STD},\n\n{7, \"&&\", \"AND_I\",\tPC_LOGICAND, ASSOC_LEFT,\t\t\t&type_integer,\t&type_integer,\t&type_bint,\t\tOPF_STD},\n{7, \"||\", \"OR_I\",\tPC_LOGICOR, ASSOC_LEFT,\t\t\t\t&type_integer,\t&type_integer,\t&type_bint,\t\tOPF_STD},\n{7, \"&&\", \"AND_IF\",\tPC_LOGICAND, ASSOC_LEFT,\t\t\t&type_integer,\t&type_float,\t&type_bint,\t\tOPF_STD},\n{7, \"||\", \"OR_IF\",\tPC_LOGICOR, ASSOC_LEFT,\t\t\t\t&type_integer,\t&type_float,\t&type_bint,\t\tOPF_STD},\n{7, \"&&\", \"AND_FI\",\tPC_LOGICAND, ASSOC_LEFT,\t\t\t&type_float,\t&type_integer,\t&type_bint,\t\tOPF_STD},\n{7, \"||\", \"OR_FI\",\tPC_LOGICOR, ASSOC_LEFT,\t\t\t\t&type_float,\t&type_integer,\t&type_bint,\t\tOPF_STD},\n{7, \"!=\", \"NE_IF\",\tPC_EQUALITY, ASSOC_LEFT,\t\t\t&type_integer,\t&type_float,\t&type_bint,\t\tOPF_STD},\n{7, \"!=\", \"NE_FI\",\tPC_EQUALITY, ASSOC_LEFT,\t\t\t&type_float,\t&type_integer,\t&type_bint,\t\tOPF_STD},\n\n\n\n\n\n\n{7, \"<>\",\t\"GSTOREP_I\",\tPC_NONE, ASSOC_LEFT,\t\t&type_float,\t&type_float,\t&type_float},\n{7, \"<>\",\t\"GSTOREP_F\",\tPC_NONE, ASSOC_LEFT,\t\t&type_float,\t&type_float,\t&type_float},\n{7, \"<>\",\t\"GSTOREP_ENT\",\tPC_NONE, ASSOC_LEFT,\t\t&type_float,\t&type_float,\t&type_float},\n{7, \"<>\",\t\"GSTOREP_FLD\",\tPC_NONE, ASSOC_LEFT,\t\t&type_float,\t&type_float,\t&type_float},\n{7, \"<>\",\t\"GSTOREP_S\",\tPC_NONE, ASSOC_LEFT,\t\t&type_float,\t&type_float,\t&type_float},\n{7, \"<>\",\t\"GSTOREP_FNC\",\tPC_NONE, ASSOC_LEFT,\t\t&type_float,\t&type_float,\t&type_float},\n{7, \"<>\",\t\"GSTOREP_V\",\tPC_NONE, ASSOC_LEFT,\t\t&type_float,\t&type_float,\t&type_float},\n\n{7, \"<>\",\t\"GADDRESS\",\t\tPC_NONE, ASSOC_LEFT,\t\t&type_float,\t&type_float,\t&type_float},\n\n{7, \"<>\",\t\"GLOAD_I\",\t\tPC_NONE, ASSOC_LEFT,\t\t&type_float,\t&type_float,\t&type_float},\n{7, \"<>\",\t\"GLOAD_F\",\t\tPC_NONE, ASSOC_LEFT,\t\t&type_float,\t&type_float,\t&type_float},\n{7, \"<>\",\t\"GLOAD_FLD\",\tPC_NONE, ASSOC_LEFT,\t\t&type_float,\t&type_float,\t&type_float},\n{7, \"<>\",\t\"GLOAD_ENT\",\tPC_NONE, ASSOC_LEFT,\t\t&type_float,\t&type_float,\t&type_float},\n{7, \"<>\",\t\"GLOAD_S\",\t\tPC_NONE, ASSOC_LEFT,\t\t&type_float,\t&type_float,\t&type_float},\n{7, \"<>\",\t\"GLOAD_FNC\",\tPC_NONE, ASSOC_LEFT,\t\t&type_float,\t&type_float,\t&type_float},\n\n{7, \"<>\",\t\"BOUNDCHECK\",\tPC_NONE, ASSOC_LEFT,\t\t&type_integer,\tNULL,\tNULL},\n\n{7, \"<UNUSED>\",\t\"UNUSED\",\tPC_NONE, ASSOC_RIGHT,\t\t&type_void,\t&type_void,\t&type_void},\n{7, \"<PUSH>\",\t\"PUSH\",\t\tPC_NONE, ASSOC_RIGHT,\t\t&type_float,\t&type_void,\t\t&type_pointer},\n{7, \"<POP>\",\t\"POP\",\t\tPC_NONE, ASSOC_RIGHT,\t\t&type_float,\t&type_void,\t\t&type_void},\n\n{7, \"<SWITCH_I>\", \"SWITCH_I\",PC_NONE, ASSOC_LEFT,\t\t&type_void, NULL, &type_void},\n{7, \"<>\",\t\"GLOAD_V\",\t\tPC_NONE, ASSOC_LEFT,\t\t&type_float,\t&type_float,\t&type_float},\n\n{7, \"<IF_F>\",\t\"IF_F\",\t\tPC_NONE, ASSOC_RIGHT,\t\t&type_float, NULL, &type_void},\n{7, \"<IFNOT_F>\",\"IFNOT_F\",\tPC_NONE, ASSOC_RIGHT,\t\t&type_float, NULL, &type_void},\n\n{7, \"=\",\t\"STOREF_V\",\t\tPC_NONE,\tASSOC_RIGHT,\t\t\t&type_entity, &type_field, &type_vector,\t\tOPF_STOREFLD},\t//ent.fld=c\n{7, \"=\",\t\"STOREF_F\",\t\tPC_NONE,\tASSOC_RIGHT,\t\t\t&type_entity, &type_field, &type_float,\t\t\tOPF_STOREFLD},\n{7, \"=\",\t\"STOREF_S\",\t\tPC_NONE,\tASSOC_RIGHT,\t\t\t&type_entity, &type_field, &type_string,\t\tOPF_STOREFLD},\n{7, \"=\",\t\"STOREF_I\",\t\tPC_NONE,\tASSOC_RIGHT,\t\t\t&type_entity, &type_field, &type_integer,\t\tOPF_STOREFLD},\n\n{7, \"<STOREP_B>\", \"STOREP_I8\",\tPC_STORE, ASSOC_RIGHT,\t&type_string,\t&type_integer, &type_integer,\t\t\tOPF_STOREPTROFS},\n{7, \"<LOADP_B>\", \"LOADP_U8\",\tPC_STORE, ASSOC_LEFT,\t&type_string,\t&type_integer, &type_integer,\t\t\tOPF_LOADPTR},\n\n//uint opcodes (not many, they're shared with ints for the most part)\n{7, \"<=\",\t\"LE_U\",\t\t\tPC_RELATION, ASSOC_LEFT,\t&type_uint,\t\t&type_uint,\t\t&type_bint,\t\t\t\tOPF_LOADPTR},\n{7, \"<\",\t\"LT_U\",\t\t\tPC_RELATION, ASSOC_LEFT,\t&type_uint,\t\t&type_uint,\t\t&type_bint,\t\t\t\tOPF_LOADPTR},\n{7, \"/\",\t\"DIV_U\",\t\tPC_MULDIV, ASSOC_LEFT,\t\t&type_uint,\t\t&type_uint,\t\t&type_uint,\t\t\t\tOPF_LOADPTR},\n{7, \">>\",\t\"RSHIFT_U\",\t\tPC_SHIFT, ASSOC_LEFT,\t\t&type_uint,\t\t&type_integer,\t&type_uint,\t\t\t\tOPF_LOADPTR},\n\n//[u]int64+double opcodes\n{7, \"+\",\t\"ADD_I64\",\t\tPC_ADDSUB,\tASSOC_LEFT,\t\t&type_int64,\t\t&type_int64,\t\t&type_int64,\tOPF_STD},\n{7, \"-\",\t\"SUB_I64\",\t\tPC_ADDSUB,\tASSOC_LEFT,\t\t&type_int64,\t\t&type_int64,\t\t&type_int64,\tOPF_STD},\n{7, \"*\",\t\"MUL_I64\",\t\tPC_MULDIV,\tASSOC_LEFT,\t\t&type_int64,\t\t&type_int64,\t\t&type_int64,\tOPF_STD},\n{7, \"/\",\t\"DIV_I64\",\t\tPC_MULDIV,\tASSOC_LEFT,\t\t&type_int64,\t\t&type_int64,\t\t&type_int64,\tOPF_STD},\n{7, \"&\",\t\"BITAND_I64\",\tPC_BITAND,\tASSOC_LEFT,\t\t&type_int64,\t\t&type_int64,\t\t&type_int64,\tOPF_STD},\n{7, \"|\",\t\"BITOR_I64\",\tPC_BITOR,\tASSOC_LEFT,\t\t&type_int64,\t\t&type_int64,\t\t&type_int64,\tOPF_STD},\n{7, \"^\",\t\"BITXOR_I64\",\tPC_BITXOR,\tASSOC_LEFT,\t\t&type_int64,\t\t&type_int64,\t\t&type_int64,\tOPF_STD},\n{7, \"<<\",\t\"LSHIFT_I64I\",\tPC_SHIFT,\tASSOC_LEFT,\t\t&type_int64,\t\t&type_integer,\t\t&type_int64,\tOPF_STD},\n{7, \">>\",\t\"RSHIFT_I64I\",\tPC_SHIFT,\tASSOC_LEFT,\t\t&type_int64,\t\t&type_integer,\t\t&type_int64,\tOPF_STD},\n{7, \"<=\",\t\"LE_I64\",\t\tPC_RELATION, ASSOC_LEFT,\t&type_int64,\t\t&type_int64,\t\t&type_bint,\t\tOPF_STD},\n{7, \"<\",\t\"LT_I64\",\t\tPC_RELATION, ASSOC_LEFT,\t&type_int64,\t\t&type_int64,\t\t&type_bint,\t\tOPF_STD},\n{7, \"==\",\t\"EQ_I64\",\t\tPC_EQUALITY, ASSOC_LEFT,\t&type_int64,\t\t&type_int64,\t\t&type_bint,\t\tOPF_STD},\n{7, \"!=\",\t\"NE_I64\",\t\tPC_EQUALITY, ASSOC_LEFT,\t&type_int64,\t\t&type_int64,\t\t&type_bint,\t\tOPF_STD},\n{7, \"<\",\t\"LE_U64\",\t\tPC_RELATION, ASSOC_LEFT,\t&type_uint64,\t\t&type_uint64,\t\t&type_bint,\t\tOPF_STD},\n{7, \"<\",\t\"LT_U64\",\t\tPC_RELATION, ASSOC_LEFT,\t&type_uint64,\t\t&type_uint64,\t\t&type_bint,\t\tOPF_STD},\n{7, \"%\",\t\"DIV_U64\",\t\tPC_MULDIV,\tASSOC_LEFT,\t\t&type_uint64,\t\t&type_uint64,\t\t&type_uint64,\tOPF_STD},\n{7, \">>\",\t\"RSHIFT_U64I\",\tPC_SHIFT,\tASSOC_LEFT,\t\t&type_uint64,\t\t&type_uint,\t\t\t&type_uint64,\tOPF_STD},\n{7, \"=\",\t\"STORE_I64\",\tPC_STORE, ASSOC_RIGHT,\t\t&type_int64,\t\t&type_int64,\t\t&type_int64,\tOPF_STORE},\n{7, \"=\",\t\"STOREP_I64\",\tPC_STORE, ASSOC_RIGHT,\t\t&type_pointer,\t\t&type_int64,\t\t&type_int64,\tOPF_STOREPTROFS},\n{7, \"<=>\",\t\"STOREF_I64\",\tPC_NONE,\tASSOC_RIGHT,\t&type_entity,\t\t&type_field,\t\t&type_int64,\tOPF_STOREFLD},\n{7, \".\",\t\"LOADF_I64\",\tPC_MEMBER, ASSOC_LEFT,\t\t&type_entity,\t\t&type_field,\t\t&type_pointer},\n{7, \"=\",\t\"LOADA_I64\",\tPC_STORE, ASSOC_LEFT,\t\t&type_int64,\t\t&type_integer,\t\t&type_int64},\n{7, \"=\",\t\"LOADP_I64\",\tPC_STORE, ASSOC_LEFT,\t\t&type_pointer,\t\t&type_int64,\t\t&type_int64,\tOPF_LOADPTR},\n{7, \"=\",\t\"CONV_UI64\",\tPC_STORE, ASSOC_LEFT,\t\t&type_uint,\t\t\t&type_void,\t\t\t&type_int64},\n{7, \"=\",\t\"CONV_II64\",\tPC_STORE, ASSOC_LEFT,\t\t&type_integer,\t\t&type_void,\t\t\t&type_int64},\n{7, \"=\",\t\"CONV_I64I\",\tPC_STORE, ASSOC_LEFT,\t\t&type_int64,\t\t&type_void,\t\t\t&type_integer},\n{7, \"=\",\t\"CONV_FD\",\t\tPC_STORE, ASSOC_LEFT,\t\t&type_float,\t\t&type_void,\t\t\t&type_double},\n{7, \"=\",\t\"CONV_DF\",\t\tPC_STORE, ASSOC_LEFT,\t\t&type_double,\t\t&type_void,\t\t\t&type_float},\n{7, \"=\",\t\"CONV_I64F\",\tPC_STORE, ASSOC_LEFT,\t\t&type_int64,\t\t&type_void,\t\t\t&type_float},\n{7, \"=\",\t\"CONV_FI64\",\tPC_STORE, ASSOC_LEFT,\t\t&type_float,\t\t&type_void,\t\t\t&type_int64},\n{7, \"=\",\t\"CONV_I64D\",\tPC_STORE, ASSOC_LEFT,\t\t&type_int64,\t\t&type_void,\t\t\t&type_double},\n{7, \"=\",\t\"CONV_DI64\",\tPC_STORE, ASSOC_LEFT,\t\t&type_double,\t\t&type_void,\t\t\t&type_int64},\n{7, \"+\",\t\"ADD_D\",\t\tPC_ADDSUB,\tASSOC_LEFT,\t\t&type_double,\t\t&type_double,\t\t&type_double,\tOPF_STD},\n{7, \"-\",\t\"SUB_D\",\t\tPC_ADDSUB,\tASSOC_LEFT,\t\t&type_double,\t\t&type_double,\t\t&type_double,\tOPF_STD},\n{7, \"*\",\t\"MUL_D\",\t\tPC_MULDIV,\tASSOC_LEFT,\t\t&type_double,\t\t&type_double,\t\t&type_double,\tOPF_STD},\n{7, \"/\",\t\"DIV_D\",\t\tPC_MULDIV,\tASSOC_LEFT,\t\t&type_double,\t\t&type_double,\t\t&type_double,\tOPF_STD},\n{7, \"<=\",\t\"LE_D\",\t\t\tPC_RELATION, ASSOC_LEFT,\t&type_double,\t\t&type_double,\t\t&type_bint,\t\tOPF_STD},\n{7, \"<\",\t\"LT_D\",\t\t\tPC_RELATION, ASSOC_LEFT,\t&type_double,\t\t&type_double,\t\t&type_bint,\t\tOPF_STD},\n{7, \"==\",\t\"EQ_D\",\t\t\tPC_EQUALITY, ASSOC_LEFT,\t&type_double,\t\t&type_double,\t\t&type_bint,\t\tOPF_STD},\n{7, \"!=\",\t\"NE_D\",\t\t\tPC_EQUALITY, ASSOC_LEFT,\t&type_double,\t\t&type_double,\t\t&type_bint,\t\tOPF_STD},\n\n{7, \"<STOREP_I16>\", \"STOREP_I16\",\tPC_STORE, ASSOC_RIGHT,\t&type_pointer,\t&type_integer, &type_integer,\t\t\tOPF_STOREPTROFS},\n{7, \"<LOADP_I16>\", \"LOADP_I16\",\t\tPC_STORE, ASSOC_LEFT,\t&type_pointer,\t&type_integer, &type_integer,\t\t\tOPF_LOADPTR},\n{7, \"<LOADP_U16>\", \"LOADP_U16\",\t\tPC_STORE, ASSOC_LEFT,\t&type_pointer,\t&type_integer, &type_integer,\t\t\tOPF_LOADPTR},\n{7, \"<LOADP_I8>\", \"LOADP_I8\",\t\tPC_STORE, ASSOC_LEFT,\t&type_pointer,\t&type_integer, &type_integer,\t\t\tOPF_LOADPTR},\n{7, \"<BITEXTEND_I>\", \"BITEXTEND_I\",\tPC_NONE, ASSOC_LEFT,\t&type_integer,\t&type_integer, &type_integer,\t\t\tOPF_STD},\n{7, \"<BITEXTEND_U>\", \"BITEXTEND_U\",\tPC_NONE, ASSOC_LEFT,\t&type_integer,\t&type_integer, &type_integer,\t\t\tOPF_STD},\n{7, \"<BITCOPY_I>\", \"BITCOPY_I\",\tPC_NONE, ASSOC_LEFT,\t&type_integer,\t&type_integer, &type_integer,\t\t\tOPF_STD},\n{7, \"=\",\t\"CONV_UF\",\t\tPC_STORE, ASSOC_LEFT,\t\t&type_uint,\t\t&type_void,\t\t\t\t&type_float},\n{7, \"=\",\t\"CONV_FU\",\t\tPC_STORE, ASSOC_LEFT,\t\t&type_float,\t\t&type_void,\t\t\t&type_uint},\n{7, \"=\",\t\"CONV_U64D\",\t\tPC_STORE, ASSOC_LEFT,\t\t&type_uint64,\t\t&type_void,\t\t\t&type_double},\n{7, \"=\",\t\"CONV_DU64\",\t\tPC_STORE, ASSOC_LEFT,\t\t&type_double,\t\t&type_void,\t\t\t&type_uint64},\n{7, \"=\",\t\"CONV_U64F\",\t\tPC_STORE, ASSOC_LEFT,\t\t&type_uint64,\t\t&type_void,\t\t\t&type_float},\n{7, \"=\",\t\"CONV_FU64\",\t\tPC_STORE, ASSOC_LEFT,\t\t&type_float,\t\t&type_void,\t\t\t&type_uint64},\n\n/* emulated ops begin here */\n {7, \"<>\",\t\"OP_EMULATED\",\tPC_NONE, ASSOC_LEFT,\t\t\t\t&type_float,\t&type_float,\t&type_float},\n\n {7, \"|=\", \"BITSET_I\",\t\tPC_STORE,\tASSOC_RIGHT_RESULT,\t\t&type_integer, &type_integer, &type_integer,\tOPF_STORE},\n {7, \"|=\", \"BITSETP_I\",\t\tPC_STORE,\tASSOC_RIGHT_RESULT,\t\t&type_pointer, &type_integer, &type_integer,\tOPF_STOREPTR},\n {7, \"&~=\", \"BITCLR_I\",\t\tPC_STORE,\tASSOC_RIGHT_RESULT,\t\t&type_integer, &type_integer, &type_integer,\tOPF_STORE},\n\n\n {7, \"*=\", \"MULSTORE_I\",\tPC_STORE,\tASSOC_RIGHT_RESULT,\t\t&type_integer, &type_integer, &type_integer,\tOPF_STORE},\n {7, \"/=\", \"DIVSTORE_I\",\tPC_STORE,\tASSOC_RIGHT_RESULT,\t\t&type_integer, &type_integer, &type_integer,\tOPF_STORE},\n {7, \"+=\", \"ADDSTORE_I\",\tPC_STORE,\tASSOC_RIGHT_RESULT,\t\t&type_integer, &type_integer, &type_integer,\tOPF_STORE},\n {7, \"-=\", \"SUBSTORE_I\",\tPC_STORE,\tASSOC_RIGHT_RESULT,\t\t&type_integer, &type_integer, &type_integer,\tOPF_STORE},\n\n {7, \"*=\", \"MULSTOREP_I\",\tPC_STORE,\tASSOC_RIGHT_RESULT,\t\t&type_pointer, &type_integer, &type_integer,\tOPF_STOREPTR},\n {7, \"/=\", \"DIVSTOREP_I\",\tPC_STORE,\tASSOC_RIGHT_RESULT,\t\t&type_pointer, &type_integer, &type_integer,\tOPF_STOREPTR},\n {7, \"+=\", \"ADDSTOREP_I\",\tPC_STORE,\tASSOC_RIGHT_RESULT,\t\t&type_pointer, &type_integer, &type_integer,\tOPF_STOREPTR},\n {7, \"-=\", \"SUBSTOREP_I\",\tPC_STORE,\tASSOC_RIGHT_RESULT,\t\t&type_pointer, &type_integer, &type_integer,\tOPF_STOREPTR},\n\n {7, \"*=\", \"MULSTORE_IF\",\tPC_STORE,\tASSOC_RIGHT_RESULT,\t\t&type_integer, &type_float, &type_float,\tOPF_STORE},\n {7, \"*=\", \"MULSTOREP_IF\",\tPC_STORE,\tASSOC_RIGHT_RESULT,\t\t&type_intpointer, &type_float, &type_float,\tOPF_STOREPTR},\n {7, \"/=\", \"DIVSTORE_IF\",\tPC_STORE,\tASSOC_RIGHT_RESULT,\t\t&type_integer, &type_float, &type_float,\tOPF_STORE},\n {7, \"/=\", \"DIVSTOREP_IF\",\tPC_STORE,\tASSOC_RIGHT_RESULT,\t\t&type_intpointer, &type_float, &type_float,\tOPF_STOREPTR},\n {7, \"+=\", \"ADDSTORE_IF\",\tPC_STORE,\tASSOC_RIGHT_RESULT,\t\t&type_integer, &type_float, &type_float,\tOPF_STORE},\n {7, \"+=\", \"ADDSTOREP_IF\",\tPC_STORE,\tASSOC_RIGHT_RESULT,\t\t&type_intpointer, &type_float, &type_float,\tOPF_STOREPTR},\n {7, \"-=\", \"SUBSTORE_IF\",\tPC_STORE,\tASSOC_RIGHT_RESULT,\t\t&type_integer, &type_float, &type_float,\tOPF_STORE},\n {7, \"-=\", \"SUBSTOREP_IF\",\tPC_STORE,\tASSOC_RIGHT_RESULT,\t\t&type_intpointer, &type_float, &type_float,\tOPF_STOREPTR},\n\n {7, \"*=\", \"MULSTORE_FI\",\tPC_STORE,\tASSOC_RIGHT_RESULT,\t\t&type_float, &type_integer, &type_float,\tOPF_STORE},\n {7, \"*=\", \"MULSTOREP_FI\",\tPC_STORE,\tASSOC_RIGHT_RESULT,\t\t&type_floatpointer, &type_integer, &type_float,\tOPF_STOREPTR},\n {7, \"/=\", \"DIVSTORE_FI\",\tPC_STORE,\tASSOC_RIGHT_RESULT,\t\t&type_float, &type_integer, &type_float,\tOPF_STORE},\n {7, \"/=\", \"DIVSTOREP_FI\",\tPC_STORE,\tASSOC_RIGHT_RESULT,\t\t&type_floatpointer, &type_integer, &type_float,\tOPF_STOREPTR},\n {7, \"+=\", \"ADDSTORE_FI\",\tPC_STORE,\tASSOC_RIGHT_RESULT,\t\t&type_float, &type_integer, &type_float,\tOPF_STORE},\n {7, \"+=\", \"ADDSTOREP_FI\",\tPC_STORE,\tASSOC_RIGHT_RESULT,\t\t&type_floatpointer, &type_integer, &type_float,\tOPF_STOREPTR},\n {7, \"-=\", \"SUBSTORE_FI\",\tPC_STORE,\tASSOC_RIGHT_RESULT,\t\t&type_float, &type_integer, &type_float,\tOPF_STORE},\n {7, \"-=\", \"SUBSTOREP_FI\",\tPC_STORE,\tASSOC_RIGHT_RESULT,\t\t&type_floatpointer, &type_integer, &type_float,\tOPF_STOREPTR},\n\n {7, \"*=\", \"MULSTORE_VI\",\tPC_STORE, ASSOC_RIGHT_RESULT,\t\t&type_vector, &type_integer, &type_vector,\tOPF_STORE},\n {7, \"*=\", \"MULSTOREP_VI\",\tPC_STORE, ASSOC_RIGHT_RESULT,\t\t&type_pointer, &type_integer, &type_vector,\tOPF_STOREPTR},\n\n {7, \"=\", \"LOADA_STRUCT\",\tPC_STORE, ASSOC_LEFT,\t\t\t\t&type_float,\t&type_integer, &type_float},\n\n {7, \"=\",\t\"LOADP_P\",\t\tPC_STORE, ASSOC_LEFT,\t\t\t\t&type_pointer,\t&type_integer, &type_pointer,\tOPF_LOADPTR},\n {7, \"=\",\t\"STOREP_P\",\t\tPC_STORE,\tASSOC_RIGHT,\t\t\t&type_pointer,\t&type_pointer, &type_pointer,\tOPF_STOREPTROFS},\n {7, \"~\",\t\"BITNOT_F\",\t\tPC_UNARY, ASSOC_LEFT,\t\t\t\t&type_float,\t&type_void, &type_float,\t\tOPF_STDUNARY},\n {7, \"~\",\t\"BITNOT_I\",\t\tPC_UNARY, ASSOC_LEFT,\t\t\t\t&type_integer,\t&type_void, &type_integer,\t\tOPF_STDUNARY},\n\n {7, \"==\", \"EQ_P\",\t\t\tPC_EQUALITY, ASSOC_LEFT,\t\t\t\t\t\t&type_pointer,\t&type_pointer, &type_bfloat, OPF_STD},\n {7, \"!=\", \"NE_P\",\t\t\tPC_EQUALITY, ASSOC_LEFT,\t\t\t\t\t\t&type_pointer,\t&type_pointer, &type_bfloat, OPF_STD},\n {7, \"<=\", \"LE_P\",\t\t\tPC_RELATION, ASSOC_LEFT,\t\t\t\t\t\t&type_pointer,\t&type_pointer, &type_bfloat, OPF_STD},\n {7, \">=\", \"GE_P\",\t\t\tPC_RELATION, ASSOC_LEFT,\t\t\t\t\t\t&type_pointer,\t&type_pointer, &type_bfloat, OPF_STD},\n {7, \"<\", \"LT_P\",\t\t\tPC_RELATION, ASSOC_LEFT,\t\t\t\t\t\t&type_pointer,\t&type_pointer, &type_bfloat, OPF_STD},\n {7, \">\", \"GT_P\",\t\t\tPC_RELATION, ASSOC_LEFT,\t\t\t\t\t\t&type_pointer,\t&type_pointer, &type_bfloat, OPF_STD},\n\n {7, \"&=\", \"ANDSTORE_F\",\tPC_STORE, ASSOC_RIGHT_RESULT,\t\t&type_float, &type_float, &type_float, OPF_STORE},\n {7, \"&~=\", \"BITCLR_F\",\t\tPC_STORE, ASSOC_LEFT,\t\t\t\t\t&type_float, &type_float, &type_float, OPF_STORE},\n {7, \"&~=\", \"BITCLR_I\",\t\tPC_STORE, ASSOC_LEFT,\t\t\t\t\t&type_integer, &type_integer, &type_integer, OPF_STORE},\n {7, \"&~=\", \"BITCLR_V\",\t\tPC_STORE, ASSOC_LEFT,\t\t\t\t\t&type_vector, &type_vector, &type_vector, OPF_STORE},\n\n {7, \"+\", \"ADD_SI\",\tPC_ADDSUB, ASSOC_LEFT,\t\t\t\t\t\t&type_string,\t&type_integer,\t&type_string,\tOPF_STD},\n {7, \"+\", \"ADD_IS\",\tPC_ADDSUB, ASSOC_LEFT,\t\t\t\t\t\t&type_integer,\t&type_string,\t&type_string,\tOPF_STD},\n {7, \"+\", \"ADD_PF\",\tPC_ADDSUB, ASSOC_LEFT,\t\t\t\t\t\t&type_pointer,\t&type_float,\t&type_pointer,\tOPF_STD},//var_a.cast->auxtype matters\n {7, \"+\", \"ADD_FP\",\tPC_ADDSUB, ASSOC_LEFT,\t\t\t\t\t\t&type_float,\t&type_pointer,\t&type_pointer,\tOPF_STD},//var_b.cast->auxtype matters\n {7, \"+\", \"ADD_PI\",\tPC_ADDSUB, ASSOC_LEFT,\t\t\t\t\t\t&type_pointer,\t&type_integer,\t&type_pointer,\tOPF_STD},//var_a.cast->auxtype matters\n {7, \"+\", \"ADD_IP\",\tPC_ADDSUB, ASSOC_LEFT,\t\t\t\t\t\t&type_integer,\t&type_pointer,\t&type_pointer,\tOPF_STD},//var_b.cast->auxtype matters\n {7, \"+\", \"ADD_PU\",\tPC_ADDSUB, ASSOC_LEFT,\t\t\t\t\t\t&type_pointer,\t&type_uint,\t\t&type_pointer,\tOPF_STD},//var_a.cast->auxtype matters\n {7, \"+\", \"ADD_UP\",\tPC_ADDSUB, ASSOC_LEFT,\t\t\t\t\t\t&type_uint,\t\t&type_pointer,\t&type_pointer,\tOPF_STD},//var_b.cast->auxtype matters\n {7, \"-\", \"SUB_SI\",\tPC_ADDSUB, ASSOC_LEFT,\t\t\t\t\t\t&type_string,\t&type_integer,\t&type_string,\tOPF_STD},\n {7, \"-\", \"SUB_PF\",\tPC_ADDSUB, ASSOC_LEFT,\t\t\t\t\t\t&type_pointer,\t&type_float,\t&type_pointer,\tOPF_STD},\n {7, \"-\", \"SUB_PI\",\tPC_ADDSUB, ASSOC_LEFT,\t\t\t\t\t\t&type_pointer,\t&type_integer,\t&type_pointer,\tOPF_STD},\n {7, \"-\", \"SUB_PU\",\tPC_ADDSUB, ASSOC_LEFT,\t\t\t\t\t\t&type_pointer,\t&type_uint,\t\t&type_pointer,\tOPF_STD},\n {7, \"-\", \"SUB_PP\",\tPC_ADDSUB, ASSOC_LEFT,\t\t\t\t\t\t&type_pointer,\t&type_pointer,\t&type_integer,\tOPF_STD},\n\n {7, \"%\", \"MOD_F\",\tPC_MULDIV, ASSOC_LEFT,\t\t\t\t\t\t&type_float,\t&type_float,\t&type_float,\tOPF_STD},\n {7, \"%\", \"MOD_I\",\tPC_MULDIV, ASSOC_LEFT,\t\t\t\t\t\t&type_integer,\t&type_integer,\t&type_integer,\tOPF_STD},\n {7, \"%\", \"MOD_FI\",\tPC_MULDIV, ASSOC_LEFT,\t\t\t\t\t\t&type_float,\t&type_integer,\t&type_integer,\tOPF_STD},\n {7, \"%\", \"MOD_IF\",\tPC_MULDIV, ASSOC_LEFT,\t\t\t\t\t\t&type_integer,\t&type_float,\t&type_integer,\tOPF_STD},\n {7, \"%\", \"MOD_V\",\tPC_MULDIV, ASSOC_LEFT,\t\t\t\t\t\t&type_vector,\t&type_vector,\t&type_vector,\tOPF_STD},\n\n {7, \"^\", \"BITXOR_F\",\tPC_BITXOR, ASSOC_LEFT,\t\t\t\t\t&type_float,\t&type_float,\t&type_float,\tOPF_STD},\n {7, \">>\", \"RSHIFT_F\",\tPC_SHIFT, ASSOC_LEFT,\t\t\t\t\t&type_float,\t&type_float,\t&type_float,\tOPF_STD},\n {7, \"<<\", \"LSHIFT_F\",\tPC_SHIFT, ASSOC_LEFT,\t\t\t\t\t&type_float,\t&type_float,\t&type_float,\tOPF_STD},\n {7, \">>\", \"RSHIFT_IF\",\tPC_SHIFT, ASSOC_LEFT,\t\t\t\t\t&type_integer,\t&type_float,\t&type_integer,\tOPF_STD},\n {7, \"<<\", \"LSHIFT_IF\",\tPC_SHIFT, ASSOC_LEFT,\t\t\t\t\t&type_integer,\t&type_float,\t&type_integer,\tOPF_STD},\n {7, \">>\", \"RSHIFT_FI\",\tPC_SHIFT, ASSOC_LEFT,\t\t\t\t\t&type_float,\t&type_integer,\t&type_integer,\tOPF_STD},\n {7, \"<<\", \"LSHIFT_FI\",\tPC_SHIFT, ASSOC_LEFT,\t\t\t\t\t&type_float,\t&type_integer,\t&type_integer,\tOPF_STD},\n\n {7, \"&&\", \"AND_ANY\",\tPC_LOGICAND, ASSOC_LEFT,\t\t\t\t\t&type_variant,\t&type_variant,\t&type_bfloat,\tOPF_STD},\n {7, \"||\", \"OR_ANY\",\tPC_LOGICOR, ASSOC_LEFT,\t\t\t\t\t&type_variant,\t&type_variant,\t&type_bfloat,\tOPF_STD},\n\n {7, \"+\", \"ADD_EI\",\t\tPC_ADDSUB, ASSOC_LEFT,\t\t\t\t\t\t&type_entity,\t&type_integer,\t&type_entity,\tOPF_STD},\n {7, \"+\", \"ADD_EF\",\t\tPC_ADDSUB, ASSOC_LEFT,\t\t\t\t\t\t&type_entity,\t&type_float,\t&type_entity,\tOPF_STD},\n {7, \"-\", \"SUB_EI\",\t\tPC_ADDSUB, ASSOC_LEFT,\t\t\t\t\t\t&type_entity,\t&type_integer,\t&type_entity,\tOPF_STD},\n {7, \"-\", \"SUB_EF\",\t\tPC_ADDSUB, ASSOC_LEFT,\t\t\t\t\t\t&type_entity,\t&type_float,\t&type_entity,\tOPF_STD},\n\n {7, \"&\", \"BITAND_V\",\tPC_BITAND, ASSOC_LEFT,\t\t\t\t\t&type_vector,\t&type_vector,\t&type_vector,\tOPF_STD},\n {7, \"|\", \"BITOR_V\",\tPC_BITOR, ASSOC_LEFT,\t\t\t\t\t\t&type_vector,\t&type_vector,\t&type_vector,\tOPF_STD},\n {7, \"~\", \"BITNOT_V\",\tPC_UNARY, ASSOC_LEFT,\t\t\t\t\t&type_vector,\t&type_void,\t\t&type_vector,\tOPF_STD},\n {7, \"^\", \"BITXOR_V\",\tPC_BITXOR, ASSOC_LEFT,\t\t\t\t\t&type_vector,\t&type_vector,\t&type_vector,\tOPF_STD},\n\n {7, \"*^\", \"POW_F\",\t\tPC_MULDIV, ASSOC_LEFT,\t\t\t\t\t&type_float,\t&type_float,\t&type_float,\tOPF_STD},\n {7, \"*^\", \"POW_I\",\t\tPC_MULDIV, ASSOC_LEFT,\t\t\t\t\t&type_integer,\t&type_integer,\t&type_integer,\tOPF_STD},\n {7, \"*^\", \"POW_FI\",\tPC_MULDIV, ASSOC_LEFT,\t\t\t\t\t&type_float,\t&type_integer,\t&type_float,\tOPF_STD},\n {7, \"*^\", \"POW_IF\",\tPC_MULDIV, ASSOC_LEFT,\t\t\t\t\t&type_integer,\t&type_float,\t&type_float,\tOPF_STD},\n {7, \"><\", \"CROSS_V\",\tPC_MULDIV, ASSOC_LEFT,\t\t\t\t\t&type_vector,\t&type_vector,\t&type_vector,\tOPF_STD},\n\n {7, \"==\", \"EQ_FLD\",\tPC_EQUALITY, ASSOC_LEFT,\t\t\t\t&type_field,\t&type_field,\t&type_bfloat,\tOPF_STD},\n {7, \"!=\", \"NE_FLD\",\tPC_EQUALITY, ASSOC_LEFT,\t\t\t\t&type_field,\t&type_field,\t&type_bfloat,\tOPF_STD},\n\n {7, \"<=>\", \"SPACESHIP_F\",\tPC_EQUALITY, ASSOC_LEFT,\t\t\t\t&type_float,\t&type_float,\t&type_float,\tOPF_STD},\n {7, \"<=>\", \"SPACESHIP_S\",\tPC_EQUALITY, ASSOC_LEFT,\t\t\t\t&type_string,\t&type_string,\t&type_float,\tOPF_STD},\n\n \t//uint32 opcodes. they match the int32 ones so emulation is basically swapping them over.\n{7, \"+\",\t\"ADD_U\",\t\tPC_ADDSUB,\tASSOC_LEFT,\t\t&type_uint,\t\t&type_uint,\t\t&type_uint,\tOPF_STD},\n{7, \"-\",\t\"SUB_U\",\t\tPC_ADDSUB,\tASSOC_LEFT,\t\t&type_uint,\t\t&type_uint,\t\t&type_uint,\tOPF_STD},\n{7, \"*\",\t\"MUL_U\",\t\tPC_MULDIV,\tASSOC_LEFT,\t\t&type_uint,\t\t&type_uint,\t\t&type_uint,\tOPF_STD},\n{7, \"%\",\t\"MOD_U\",\t\tPC_MULDIV,\tASSOC_LEFT,\t\t&type_uint,\t\t&type_uint,\t\t&type_uint,\tOPF_STD},\n{7, \"&\",\t\"BITAND_U\",\t\tPC_BITAND,\tASSOC_LEFT,\t\t&type_uint,\t\t&type_uint,\t\t&type_uint,\tOPF_STD},\n{7, \"|\",\t\"BITOR_U\",\t\tPC_BITOR,\tASSOC_LEFT,\t\t&type_uint,\t\t&type_uint,\t\t&type_uint,\tOPF_STD},\n{7, \"^\",\t\"BITXOR_U\",\t\tPC_BITXOR,\tASSOC_LEFT,\t\t&type_uint,\t\t&type_uint,\t\t&type_uint,\tOPF_STD},\n{7, \"~\",\t\"BITNOT_U\",\t\tPC_UNARY,\tASSOC_LEFT,\t\t&type_uint,\t\t&type_void,\t\t&type_uint,\tOPF_STDUNARY},\n{7, \"&~=\",\t\"BITCLR_U\",\t\tPC_STORE,\tASSOC_LEFT,\t\t&type_uint,\t\t&type_uint,\t\t&type_uint,\tOPF_STORE},\n{7, \"<<\",\t\"LSHIFT_U\",\t\tPC_SHIFT,\tASSOC_LEFT,\t\t&type_uint,\t\t&type_uint,\t\t&type_uint,\tOPF_STD},\n{7, \">=\",\t\"GE_U\",\t\t\tPC_RELATION, ASSOC_LEFT,\t&type_uint,\t\t&type_uint,\t\t&type_bint,\tOPF_STD},\n{7, \">\",\t\"GT_U\",\t\t\tPC_RELATION, ASSOC_LEFT,\t&type_uint,\t\t&type_uint,\t\t&type_bint,\tOPF_STD},\n{7, \"==\",\t\"EQ_U\",\t\t\tPC_EQUALITY, ASSOC_LEFT,\t&type_uint,\t\t&type_uint,\t\t&type_bint, OPF_STD},\n{7, \"!=\",\t\"NE_U\",\t\t\tPC_EQUALITY, ASSOC_LEFT,\t&type_uint,\t\t&type_uint,\t\t&type_bint, OPF_STD},\n\n//64bit ones that we can emulate cheaply or are rare.\n{7, \"~\",\t\"BITNOT_I64\",\tPC_UNARY,\tASSOC_LEFT,\t\t&type_int64,\t\t&type_void,\t\t\t&type_int64,\tOPF_STDUNARY},\n{7, \"&~=\",\t\"BITCLR_I64\",\tPC_STORE,\tASSOC_LEFT,\t\t&type_int64,\t\t&type_int64,\t\t&type_int64, OPF_STORE},\n{7, \">=\",\t\"GE_I64\",\t\tPC_RELATION, ASSOC_LEFT,\t&type_int64,\t\t&type_int64,\t\t&type_bint,\t\tOPF_STD},\n{7, \">\",\t\"GT_I64\",\t\tPC_RELATION, ASSOC_LEFT,\t&type_int64,\t\t&type_int64,\t\t&type_bint,\t\tOPF_STD},\n\n//unsigned versions (emulated via signed int64 ones, just with different types).\n{7, \"+\",\t\"ADD_U64\",\t\tPC_ADDSUB,\tASSOC_LEFT,\t\t&type_uint64,\t\t&type_uint64,\t\t&type_uint64,\tOPF_STD},\n{7, \"-\",\t\"SUB_U64\",\t\tPC_ADDSUB,\tASSOC_LEFT,\t\t&type_uint64,\t\t&type_uint64,\t\t&type_uint64,\tOPF_STD},\n{7, \"*\",\t\"MUL_U64\",\t\tPC_MULDIV,\tASSOC_LEFT,\t\t&type_uint64,\t\t&type_uint64,\t\t&type_uint64,\tOPF_STD},\n{7, \"%\",\t\"MOD_U64\",\t\tPC_MULDIV,\tASSOC_LEFT,\t\t&type_uint64,\t\t&type_uint64,\t\t&type_uint64,\tOPF_STD},\n{7, \"&\",\t\"BITAND_U64\",\tPC_BITAND,\tASSOC_LEFT,\t\t&type_uint64,\t\t&type_uint64,\t\t&type_uint64,\tOPF_STD},\n{7, \"|\",\t\"BITOR_U64\",\tPC_BITOR,\tASSOC_LEFT,\t\t&type_uint64,\t\t&type_uint64,\t\t&type_uint64,\tOPF_STD},\n{7, \"^\",\t\"BITXOR_U64\",\tPC_BITXOR,\tASSOC_LEFT,\t\t&type_uint64,\t\t&type_uint64,\t\t&type_uint64,\tOPF_STD},\n{7, \"~\",\t\"BITNOT_U64\",\tPC_UNARY,\tASSOC_LEFT,\t\t&type_uint64,\t\t&type_void,\t\t\t&type_uint64,\tOPF_STDUNARY},\n{7, \"&~=\",\t\"BITCLR_U64\",\tPC_STORE,\tASSOC_LEFT,\t\t&type_uint64,\t\t&type_uint64,\t\t&type_uint64,\tOPF_STORE},\n{7, \"<<\",\t\"LSHIFT_U64I\",\tPC_SHIFT,\tASSOC_LEFT,\t\t&type_uint64,\t\t&type_integer,\t\t&type_uint64,\tOPF_STD},\n{7, \">=\",\t\"GE_U64\",\t\tPC_RELATION, ASSOC_LEFT,\t&type_uint64,\t\t&type_uint64,\t\t&type_bint,\t\tOPF_STD},\n{7, \">\",\t\"GT_U64\",\t\tPC_RELATION, ASSOC_LEFT,\t&type_uint64,\t\t&type_uint64,\t\t&type_bint,\t\tOPF_STD},\n{7, \"==\",\t\"EQ_U64\",\t\tPC_EQUALITY, ASSOC_LEFT,\t&type_uint64,\t\t&type_uint64,\t\t&type_bint,\t\tOPF_STD},\n{7, \"!=\",\t\"NE_U64\",\t\tPC_EQUALITY, ASSOC_LEFT,\t&type_uint64,\t\t&type_uint64,\t\t&type_bint,\t\tOPF_STD},\n\n\n{7, \"&\",\t\"BITAND_D\",\t\tPC_BITAND,\tASSOC_LEFT,\t\t&type_double,\t\t&type_double,\t\t&type_double,\tOPF_STD},\n{7, \"|\",\t\"BITOR_D\",\t\tPC_BITOR,\tASSOC_LEFT,\t\t&type_double,\t\t&type_double,\t\t&type_double,\tOPF_STD},\n{7, \"^\",\t\"BITXOR_D\",\t\tPC_BITXOR,\tASSOC_LEFT,\t\t&type_double,\t\t&type_double,\t\t&type_double,\tOPF_STD},\n{7, \"~\",\t\"BITNOT_D\",\t\tPC_UNARY,\tASSOC_LEFT,\t\t&type_double,\t\t&type_void,\t\t\t&type_bint,\t\tOPF_STDUNARY},\n{7, \"&~=\",\t\"BITCLR_D\",\t\tPC_STORE,\tASSOC_LEFT,\t\t&type_double,\t\t&type_double,\t\t&type_double,\tOPF_STORE},\n{7, \"<<\",\t\"LSHIFT_DI\",\tPC_SHIFT,\tASSOC_LEFT,\t\t&type_double,\t\t&type_integer,\t\t&type_double,\tOPF_STD},\n{7, \">>\",\t\"RSHIFT_DI\",\tPC_SHIFT,\tASSOC_LEFT,\t\t&type_double,\t\t&type_integer,\t\t&type_double,\tOPF_STD},\n{7, \">=\",\t\"GE_D\",\t\t\tPC_RELATION, ASSOC_LEFT,\t&type_double,\t\t&type_double,\t\t&type_bint,\t\tOPF_STD},\n{7, \">\",\t\"GT_D\",\t\t\tPC_RELATION, ASSOC_LEFT,\t&type_double,\t\t&type_double,\t\t&type_bint,\t\tOPF_STD},\n\n{7, \"<WSTATE>\", \"WSTATE\",\tPC_NONE, ASSOC_LEFT,\t\t&type_float,\t&type_float,\t&type_void},\n\n {0, NULL, \"OPD_GOTO_FORSTART\"},\n {0, NULL, \"OPD_GOTO_WHILE1\"},\n {0, NULL, \"OPD_GOTO_BREAK\"},\n {0, NULL, \"OPD_GOTO_DEFAULT\"},\n\n {0, NULL}\n};\n\n\nstatic pbool OpAssignsToC(unsigned int op)\n{\n\t// calls, switches and cases DON'T\n\tif(pr_opcodes[op].type_c == &type_void)\n\t\treturn false;\n\tif(op >= OP_SWITCH_F && op <= OP_CALL8H)\n\t\treturn false;\n//\tif(op >= OP_RAND0 && op <= OP_RANDV2)\n//\t\treturn false;\n\t// they use a and b, but have 3 types\n\t// safety\n\tif(op >= OP_BITSETSTORE_F && op <= OP_BITCLRSTOREP_F)\n\t\treturn false;\n\t/*if(op >= OP_STORE_I && op <= OP_STORE_FI)\n\t  return false; <- add STOREP_*?*/\n\tif(op == OP_STOREP_C || op == OP_STOREP_I8 || op == OP_STOREP_I16)\n\t\treturn false;\n\tif((op >= OP_STORE_F && op <= OP_STOREP_FNC) || op == OP_STOREP_P || op == OP_STORE_P || op == OP_STORE_I64)\n\t\treturn false;\n\tif(op >= OP_MULSTORE_F && op <= OP_SUBSTOREP_V)\n\t\treturn false;\n\tif (op >= OP_STORE_I && op <= OP_STORE_FI)\n\t\treturn false;\n\tif ((op >= OP_STOREF_V && op <= OP_STOREF_I) || op == OP_STOREF_I64)\n\t\treturn false;\t//reads it, doesn't write.\n\tif (op == OP_BOUNDCHECK || op == OP_UNUSED || op == OP_POP)\n\t\treturn false;\n\treturn true;\n}\nstatic pbool OpAssignsToB(unsigned int op)\n{\n\tif(op >= OP_BITSETSTORE_F && op <= OP_BITCLRSTOREP_F)\n\t\treturn true;\n\tif(op >= OP_STORE_I && op <= OP_STORE_FI)\n\t\treturn true;\n\tif(op == OP_STOREP_C || op == OP_STOREP_I8 || op == OP_STOREP_I16)\n\t\treturn true;\n\tif(op >= OP_MULSTORE_F && op <= OP_SUBSTOREP_V)\n\t\treturn true;\n\tif((op >= OP_STORE_F && op <= OP_STOREP_FNC) || op == OP_STOREP_P || op == OP_STORE_P || op == OP_STORE_I64)\n\t\treturn true;\n\treturn false;\n}\n#define OpAssignsToA(op) false\n#ifdef _DEBUG\nstatic int OpAssignsCount(unsigned int op)\n{\n\tswitch(op)\n\t{\n\tcase OP_DONE:\n\tcase OP_RETURN:\n\t\treturn 0;\t//eep\n\tcase OP_CALL0:\n\tcase OP_CALL1:\n\tcase OP_CALL2:\n\tcase OP_CALL3:\n\tcase OP_CALL4:\n\tcase OP_CALL5:\n\tcase OP_CALL6:\n\tcase OP_CALL7:\n\tcase OP_CALL8:\n\tcase OP_CALL1H:\n\tcase OP_CALL2H:\n\tcase OP_CALL3H:\n\tcase OP_CALL4H:\n\tcase OP_CALL5H:\n\tcase OP_CALL6H:\n\tcase OP_CALL7H:\n\tcase OP_CALL8H:\n\t\treturn 0;\t//also, eep.\n\tcase OP_STATE:\n\tcase OP_WSTATE:\n\tcase OP_CSTATE:\n\tcase OP_CWSTATE:\n\tcase OP_THINKTIME:\n\t\treturn 0;\t//egads\n\tcase OP_RAND0:\n\tcase OP_RAND1:\n\tcase OP_RAND2:\n\tcase OP_RANDV0:\n\tcase OP_RANDV1:\n\tcase OP_RANDV2:\n\t\treturn 1;\t//writes C, even when there's no A or B arg specified.\n\tcase OP_UNUSED:\n\tcase OP_POP:\n\t\treturn 0;\t//FIXME\n\t//branches have no side effects, other than the next instruction (or runaway loop)\n\tcase OP_SWITCH_F:\n\tcase OP_SWITCH_V:\n\tcase OP_SWITCH_S:\n\tcase OP_SWITCH_E:\n\tcase OP_SWITCH_FNC:\n\tcase OP_SWITCH_I:\n\tcase OP_GOTO:\n\tcase OP_IF_I:\n\tcase OP_IFNOT_I:\n\tcase OP_IF_S:\n\tcase OP_IFNOT_S:\n\tcase OP_IF_F:\n\tcase OP_IFNOT_F:\n\tcase OP_CASE:\n\tcase OP_CASERANGE:\n\t\treturn 0;\n\tcase OP_STOREF_V:\n\tcase OP_STOREF_F:\n\tcase OP_STOREF_S:\n\tcase OP_STOREF_I:\n\tcase OP_STOREF_I64:\n\t\treturn 0;\t//stores to a.b rather than any direct value...\n\tcase OP_BOUNDCHECK:\n\t\treturn 0;\n\tdefault:\t//the majority will write c\n\t\treturn 1;\n\t}\n}\nstatic void OpAssignsTo_Debug(void)\n{\n\tint i;\n\tfor (i = 0; i < OP_NUMREALOPS; i++)\n\t{\n\t\tif (OpAssignsToA(i) + OpAssignsToB(i) + OpAssignsToC(i) != OpAssignsCount(i))\n\t\t{\n\t\t\t//we don't know what it assigns to. bug.\n\t\t\tQCC_PR_ParseError(0, \"opcode %s metadata is bugged\", pr_opcodes[i].opname);\n\t\t}\n\t}\n}\n#endif\n/*pbool OpAssignedTo(QCC_def_t *v, unsigned int op)\n{\n\tif(OpAssignsToC(op))\n\t{\n\t}\n\telse if(OpAssignsToB(op))\n\t{\n\t}\n\treturn false;\n}\n*/\n#undef ASSOC_RIGHT_RESULT\n\n//#define TERM_PRIORITY\t\t0\n#define FUNC_PRIORITY\t\t1\n#define UNARY_PRIORITY\t\t1\t//~ !\n//#define MULDIV_PRIORITY\t\tpriority_class[PC_MULDIV]\t//* / %\n//#define ADDSUB_PRIORITY\t\tpriority_class[PC_ADDSUB]\t//+ -\n//#define BITSHIFT_PRIORITY\tpriority_class[PC_BITSHIFT]\t//<< >>\n//#define COMPARISON_PRIORITY\tpriority_class[PC_COMPARISON]\t//< <= > >=\n//#define EQUALITY_PRIORITY\tpriority_class[PC_EQUALITY]\t//== !=\n//#define BITAND_PRIORITY\t\tpriority_class[PC_BITAND]\t//&\n//#define BITXOR_PRIORITY\t\tpriority_class[PC_BITXOR]\t//^\n//#define BITOR_PRIORITY\t\tpriority_class[PC_BITOR]\t//|\n//#define LOGICAND_PRIORITY\tpriority_class[PC_LOGICAND]\t//&&\n//#define LOGICOR_PRIORITY\tpriority_class[PC_LOGICOR]\t//||\n#define TERNARY_PRIORITY\tpriority_class[PC_TERNARY]\t//?: (in C: (a||b)?(c):(d||e) )\n#define\tASSIGN_PRIORITY\t\tpriority_class[PC_STORE]\t//QC is WRONG compared to C\n//#define COMMA_PRIORITY\n\n#define\tTOP_PRIORITY\t\tpriority_class[MAX_PRIORITY_CLASSES]\n#define\tNOT_PRIORITY\t\tpriority_class[PC_UNARYNOT]\n\nQCC_opcode_t *opcodes_store[] =\n{\n\tNULL\n};\nQCC_opcode_t *opcodes_addstore[] =\n{\n\t&pr_opcodes[OP_ADD_F],\n\t&pr_opcodes[OP_ADD_V],\n\t&pr_opcodes[OP_ADD_I],\n\t&pr_opcodes[OP_ADD_U],\n\t&pr_opcodes[OP_ADD_I64],\n\t&pr_opcodes[OP_ADD_U64],\n\t&pr_opcodes[OP_ADD_D],\n\t&pr_opcodes[OP_ADD_FI],\n\t&pr_opcodes[OP_ADD_IF],\n\t&pr_opcodes[OP_ADD_SF],\n\t&pr_opcodes[OP_ADD_PI],\n\t&pr_opcodes[OP_ADD_IP],\n\t&pr_opcodes[OP_ADD_PU],\n\t&pr_opcodes[OP_ADD_UP],\n\t&pr_opcodes[OP_ADD_PF],\n\t&pr_opcodes[OP_ADD_FP],\n\t&pr_opcodes[OP_ADD_SI],\n\t&pr_opcodes[OP_ADD_IS],\n\t&pr_opcodes[OP_ADD_EI],\n\t&pr_opcodes[OP_ADD_EF],\n\tNULL\n};\nQCC_opcode_t *opcodes_addstorep[] =\n{\n\t&pr_opcodes[OP_ADDSTOREP_F],\n\t&pr_opcodes[OP_ADDSTOREP_V],\n\t&pr_opcodes[OP_ADDSTOREP_I],\n\t&pr_opcodes[OP_ADDSTOREP_IF],\n\t&pr_opcodes[OP_ADDSTOREP_FI],\n\tNULL\n};\nQCC_opcode_t *opcodes_substore[] =\n{\n\t&pr_opcodes[OP_SUB_F],\n\t&pr_opcodes[OP_SUB_V],\n\t&pr_opcodes[OP_SUB_I],\n\t&pr_opcodes[OP_SUB_U],\n\t&pr_opcodes[OP_SUB_FI],\n\t&pr_opcodes[OP_SUB_IF],\n\t&pr_opcodes[OP_SUB_S],\n\t&pr_opcodes[OP_SUB_PP],\n\t&pr_opcodes[OP_SUB_PI],\n\t&pr_opcodes[OP_SUB_PU],\n\t&pr_opcodes[OP_SUB_PF],\n\t&pr_opcodes[OP_SUB_SI],\n\t&pr_opcodes[OP_SUB_EI],\n\t&pr_opcodes[OP_SUB_EF],\n\t&pr_opcodes[OP_SUB_I64],\n\t&pr_opcodes[OP_SUB_U64],\n\t&pr_opcodes[OP_SUB_D],\n\tNULL\n};\nQCC_opcode_t *opcodes_substorep[] =\n{\n\t&pr_opcodes[OP_SUBSTOREP_F],\n\t&pr_opcodes[OP_SUBSTOREP_V],\n\t&pr_opcodes[OP_SUBSTOREP_I],\n\t&pr_opcodes[OP_SUBSTOREP_IF],\n\t&pr_opcodes[OP_SUBSTOREP_FI],\n\tNULL\n};\nQCC_opcode_t *opcodes_mulstore[] =\n{\n\t&pr_opcodes[OP_MUL_F],\n\t&pr_opcodes[OP_MUL_V],\n\t&pr_opcodes[OP_MUL_FV],\n\t&pr_opcodes[OP_MUL_IV],\n\t&pr_opcodes[OP_MUL_VF],\n\t&pr_opcodes[OP_MUL_VI],\n\t&pr_opcodes[OP_MUL_I],\n\t&pr_opcodes[OP_MUL_U],\n\t&pr_opcodes[OP_MUL_FI],\n\t&pr_opcodes[OP_MUL_IF],\n\t&pr_opcodes[OP_MUL_I64],\n\t&pr_opcodes[OP_MUL_U64],\n\t&pr_opcodes[OP_MUL_D],\n\tNULL\n};\nQCC_opcode_t *opcodes_mulstorep[] =\n{\n\t&pr_opcodes[OP_MULSTOREP_F],\n\t&pr_opcodes[OP_MULSTOREP_VF],\n\t&pr_opcodes[OP_MULSTOREP_VI],\n\t&pr_opcodes[OP_MULSTOREP_I],\n\t&pr_opcodes[OP_MULSTOREP_IF],\n\t&pr_opcodes[OP_MULSTOREP_FI],\n\tNULL\n};\nQCC_opcode_t *opcodes_divstore[] =\n{\n\t&pr_opcodes[OP_DIV_F],\n\t&pr_opcodes[OP_DIV_I],\n\t&pr_opcodes[OP_DIV_U],\n\t&pr_opcodes[OP_DIV_FI],\n\t&pr_opcodes[OP_DIV_IF],\n\t&pr_opcodes[OP_DIV_VF],\n\t&pr_opcodes[OP_DIV_I64],\n\t&pr_opcodes[OP_DIV_U64],\n\t&pr_opcodes[OP_DIV_D],\n\tNULL\n};\nQCC_opcode_t *opcodes_divstorep[] =\n{\n\t&pr_opcodes[OP_DIVSTOREP_F],\n\tNULL\n};\nQCC_opcode_t *opcodes_orstore[] =\n{\n\t&pr_opcodes[OP_BITOR_F],\n\t&pr_opcodes[OP_BITOR_I],\n\t&pr_opcodes[OP_BITOR_U],\n\t&pr_opcodes[OP_BITOR_IF],\n\t&pr_opcodes[OP_BITOR_FI],\n\t&pr_opcodes[OP_BITOR_V],\n\t&pr_opcodes[OP_BITOR_I64],\n\t&pr_opcodes[OP_BITOR_U64],\n\t&pr_opcodes[OP_BITOR_D],\n\tNULL\n};\nQCC_opcode_t *opcodes_orstorep[] =\n{\n\t&pr_opcodes[OP_BITSETSTOREP_F],\n\tNULL\n};\nQCC_opcode_t *opcodes_xorstore[] =\n{\n\t&pr_opcodes[OP_BITXOR_I],\n\t&pr_opcodes[OP_BITXOR_U],\n\t&pr_opcodes[OP_BITXOR_F],\n\t&pr_opcodes[OP_BITXOR_V],\n\t&pr_opcodes[OP_BITXOR_I64],\n\t&pr_opcodes[OP_BITXOR_U64],\n\t&pr_opcodes[OP_BITXOR_D],\n\tNULL\n};\nQCC_opcode_t *opcodes_andstore[] =\n{\n\t&pr_opcodes[OP_BITAND_F],\n\t&pr_opcodes[OP_BITAND_I],\n\t&pr_opcodes[OP_BITAND_U],\n\t&pr_opcodes[OP_BITAND_IF],\n\t&pr_opcodes[OP_BITAND_FI],\n\t&pr_opcodes[OP_BITAND_V],\n\t&pr_opcodes[OP_BITAND_I64],\n\t&pr_opcodes[OP_BITAND_U64],\n\t&pr_opcodes[OP_BITAND_D],\n\tNULL\n};\nQCC_opcode_t *opcodes_clearstore[] =\n{\n\t&pr_opcodes[OP_BITCLR_F],\n\t&pr_opcodes[OP_BITCLR_I],\n\t&pr_opcodes[OP_BITCLR_U],\n\t&pr_opcodes[OP_BITCLR_V],\n\t&pr_opcodes[OP_BITCLR_I64],\n\t&pr_opcodes[OP_BITCLR_U64],\n\t&pr_opcodes[OP_BITCLR_D],\n\tNULL\n};\nQCC_opcode_t *opcodes_clearstorep[] =\n{\n\t&pr_opcodes[OP_BITCLRSTOREP_F],\n\tNULL\n};\nQCC_opcode_t *opcodes_shlstore[] =\n{\n\t&pr_opcodes[OP_LSHIFT_F],\n\t&pr_opcodes[OP_LSHIFT_I],\n\t&pr_opcodes[OP_LSHIFT_U],\n\t&pr_opcodes[OP_LSHIFT_IF],\n\t&pr_opcodes[OP_LSHIFT_FI],\n\t&pr_opcodes[OP_LSHIFT_I64I],\n\t&pr_opcodes[OP_LSHIFT_U64I],\n\t&pr_opcodes[OP_LSHIFT_DI],\n\tNULL\n};\nQCC_opcode_t *opcodes_shrstore[] =\n{\n\t&pr_opcodes[OP_RSHIFT_F],\n\t&pr_opcodes[OP_RSHIFT_I],\n\t&pr_opcodes[OP_RSHIFT_U],\n\t&pr_opcodes[OP_RSHIFT_IF],\n\t&pr_opcodes[OP_RSHIFT_FI],\n\t&pr_opcodes[OP_RSHIFT_I64I],\n\t&pr_opcodes[OP_RSHIFT_U64I],\n\t&pr_opcodes[OP_RSHIFT_DI],\n\tNULL\n};\n\nQCC_opcode_t *opcodes_spaceship[] =\n{\n\t&pr_opcodes[OP_SPACESHIP_F],\n\t&pr_opcodes[OP_SPACESHIP_S],\n//\t&pr_opcodes[OP_SPACESHIP_I],\n//\t&pr_opcodes[OP_SPACESHIP_U],\n//\t&pr_opcodes[OP_SPACESHIP_I64],\n//\t&pr_opcodes[OP_SPACESHIP_U64],\n//\t&pr_opcodes[OP_SPACESHIP_D],\n\tNULL\n};\n\nQCC_opcode_t *opcodes_none[] =\n{\n\tNULL\n};\n\n//these evaluate as top first.\nstatic QCC_opcode_t *opcodeprioritized[13+1][128];\nstatic int\n#ifdef _MSC_VER\n__cdecl\n#endif\nsort_opcodenames(const void*a,const void*b)\n{\n\tconst QCC_opcode_t *opa = *(QCC_opcode_t*const*)a;\n\tconst QCC_opcode_t *opb = *(QCC_opcode_t*const*)b;\n\tif (opa == NULL)\n\t\treturn opb?1:0;\n\tif (opb == NULL)\n\t\treturn -1;\n\treturn strcmp(opa->name, opb->name);\n}\nvoid QCC_PrioritiseOpcodes(void)\n{\n\tint pcount[MAX_PRIORITY_CLASSES];\n\tint i, j;\n\tQCC_opcode_t *op;\n\n\tpriority_class[PC_NONE] = 0;\n\tpriority_class[PC_UNARY] = 0;\n\tpriority_class[PC_MEMBER] = 0;\n\n\tif (flag_cpriority)\n\t{\n\t\tpriority_class[PC_UNARYNOT] = 1;\n\t\tpriority_class[PC_MULDIV] = 2;\n\t\tpriority_class[PC_ADDSUB] = 3;\n\t\tpriority_class[PC_SHIFT] = 4;\n\t\tpriority_class[PC_RELATION] = 5;\n\t\tpriority_class[PC_EQUALITY] = 6;\n\t\tpriority_class[PC_BITAND] = 7;\n\t\tpriority_class[PC_BITXOR] = 8;\n\t\tpriority_class[PC_BITOR] = 9;\n\t\tpriority_class[PC_LOGICAND] = 10;\n\t\tpriority_class[PC_LOGICOR] = 11;\n\t\tpriority_class[PC_TERNARY] = 12;\n\t\tpriority_class[PC_STORE] = 13;\n\t}\n\telse\n\t{\n\t\tpriority_class[PC_UNARYNOT] = 5;\n\t\tpriority_class[PC_MULDIV] = 3;\n\t\tpriority_class[PC_ADDSUB] = 4;\n\t\tpriority_class[PC_SHIFT] = 3;\n\t\tpriority_class[PC_RELATION] = 5;\n\t\tpriority_class[PC_EQUALITY] = 5;\n\t\tpriority_class[PC_BITAND] = 3;\n\t\tpriority_class[PC_BITXOR] = 3;\n\t\tpriority_class[PC_BITOR] = 3;\n\t\tpriority_class[PC_LOGICAND] = 7;\n\t\tpriority_class[PC_LOGICOR] = 7;\n\t\tpriority_class[PC_TERNARY] = 6;\n\t\tpriority_class[PC_STORE] = 6;\n\t}\n\tpriority_class[MAX_PRIORITY_CLASSES] = 0;\n\tfor (j = 0; j < MAX_PRIORITY_CLASSES; j++)\n\t\tif (priority_class[MAX_PRIORITY_CLASSES] < priority_class[j])\n\t\t\tpriority_class[MAX_PRIORITY_CLASSES] = priority_class[j];\n\n\tmemset(pcount, 0, sizeof(pcount));\n\tmemset(opcodeprioritized, 0, sizeof(opcodeprioritized));\n\tfor (i = 0; pr_opcodes[i].name; i++)\n\t{\n\t\top = &pr_opcodes[i];\n\t\tj = priority_class[op->priorityclass];\n\t\tif (j <= 0 || j > priority_class[MAX_PRIORITY_CLASSES])\n\t\t\tcontinue;\t//class doesn't need prioritising\n\t\topcodeprioritized[j][pcount[j]++] = op;\n\t}\n\n\t//operators need to be sorted so we don't have to scan through all of them. should probably have some better table for that.\n\tfor (j = 0; j <= TOP_PRIORITY; j++)\n\t\tqsort (&opcodeprioritized[j][0], pcount[j], sizeof(opcodeprioritized[j][0]), sort_opcodenames);\n}\n\nstatic pbool QCC_OPCodeValidForTarget(qcc_targetformat_t targfmt, unsigned int qcc_targetversion, QCC_opcode_t *op)\n{\n\tint num;\n\tnum = op - pr_opcodes;\n\n\t//never any emulated opcodes\n\tif (num >= OP_NUMREALOPS)\n\t\treturn false;\n\n\tswitch(targfmt)\n\t{\n\tcase QCF_STANDARD:\n\tcase QCF_KK7:\n\tcase QCF_QTEST:\n\t\tif (num < OP_MULSTORE_F)\n\t\t\treturn true;\n\t\treturn false;\n\tcase QCF_UHEXEN2:\n\tcase QCF_HEXEN2:\n\t\tif (num >= OP_SWITCH_V && num <= OP_SWITCH_FNC)\t//these were assigned numbers but were never actually implemtented in standard h2.\n\t\t\treturn false;\n//\t\tif (num >= OP_MULSTORE_F && num <= OP_SUBSTOREP_V)\n//\t\t\treturn false;\n\t\tif (num <= OP_CALL8H)\t//CALLXH are fixed up. This is to provide more dynamic switching...\n\t\t\treturn true;\n\t\treturn false;\n\tcase QCF_FTEH2:\n\tcase QCF_FTE:\n\tcase QCF_FTEDEBUG:\n#define QCTARGVER_FTE_DEF\t\t\t5768//5529\n#define QCTARGVER_FTE_MAX\t\t\t   QCTARGVER_FTE_PRELOCS\n\t\tif (num == OP_PUSH || num >= OP_STOREP_I16)\n#define QCTARGVER_FTE_PRELOCS\t\t6614//FIXME\n\t\t\treturn (qcc_targetversion>=QCTARGVER_FTE_PRELOCS);\t//OP_PUSH was buggy before this.\n\t\tif (num >= OP_LT_U)\t\t//uint+double+int64+uint64 ops\n\t\t\treturn (qcc_targetversion>=5768);\n\t\tif (num >= OP_STOREP_I8)\t//byte\n\t\t\treturn (qcc_targetversion>=5744);\n#define QCTARGVER_FTE_STOREP_IDX\t5712//added support for the argc arg for storep_* opcodes.\n\t\t//\treturn (qcc_targetversion>=5744);\n\t\tif (num >= OP_STOREF_V)\t//field stores\n\t\t\treturn (qcc_targetversion>=5698);\n\t\tif (num >= OP_IF_F)\t\t//iffloat fixes\n\t\t\treturn (qcc_targetversion>=3349);\n\t\treturn true;\n\tcase QCF_QSS:\n#define QCTARGVER_QSS_MAX\t0\n\t\tif (num < OP_MULSTORE_F)\n\t\t\treturn true;\n\t\tswitch(num)\n\t\t{\n\t\t//int operations.\n\t\tcase OP_ADD_I:\n\t\tcase OP_ADD_IF:\n\t\tcase OP_ADD_FI:\n\t\tcase OP_SUB_I:\n\t\tcase OP_SUB_IF:\n\t\tcase OP_SUB_FI:\n\t\tcase OP_MUL_I:\n\t\tcase OP_MUL_IF:\n\t\tcase OP_MUL_FI:\n\t\tcase OP_MUL_VI:\n\t\tcase OP_DIV_VF:\n\t\tcase OP_DIV_I:\n\t\tcase OP_DIV_IF:\n\t\tcase OP_DIV_FI:\n\t\tcase OP_BITAND_I:\n\t\tcase OP_BITOR_I:\n\t\tcase OP_BITAND_IF:\n\t\tcase OP_BITOR_IF:\n\t\tcase OP_BITAND_FI:\n\t\tcase OP_BITOR_FI:\n\t\tcase OP_GE_I:\n\t\tcase OP_LE_I:\n\t\tcase OP_GT_I:\n\t\tcase OP_LT_I:\n\t\tcase OP_AND_I:\n\t\tcase OP_OR_I:\n\t\tcase OP_GE_IF:\n\t\tcase OP_LE_IF:\n\t\tcase OP_GT_IF:\n\t\tcase OP_LT_IF:\n\t\tcase OP_AND_IF:\n\t\tcase OP_OR_IF:\n\t\tcase OP_GE_FI:\n\t\tcase OP_LE_FI:\n\t\tcase OP_GT_FI:\n\t\tcase OP_LT_FI:\n\t\tcase OP_AND_FI:\n\t\tcase OP_OR_FI:\n\t\tcase OP_NOT_I:\n\t\tcase OP_EQ_I:\n\t\tcase OP_EQ_IF:\n\t\tcase OP_EQ_FI:\n\t\tcase OP_NE_I:\n\t\tcase OP_NE_IF:\n\t\tcase OP_NE_FI:\n\t\tcase OP_CONV_ITOF:\n\t\tcase OP_CONV_FTOI:\n\t\tcase OP_BITXOR_I:\n\t\tcase OP_RSHIFT_I:\n\t\tcase OP_LSHIFT_I:\n\t\tcase OP_STORE_I:\n\t\tcase OP_STORE_IF:\n\t\tcase OP_STORE_FI:\n\t\t\treturn true;\n\n\t\t//bugfixes...\n\t\tcase OP_IF_F:\n\t\tcase OP_IFNOT_F:\n\t\tcase OP_IF_S:\n\t\tcase OP_IFNOT_S:\n\t\t\treturn true;\n\n\t\t//faster field access\n\t\tcase OP_STOREF_V:\t//3 elements...\n\t\tcase OP_STOREF_F:\t//1 fpu element...\n\t\tcase OP_STOREF_S:\t//1 string reference\n\t\tcase OP_STOREF_I:\t//1 non-string reference/int\n\t\t\treturn true;\n\n\t\t//dp-style arrays\n\t\tcase OP_GLOAD_I:\n\t\tcase OP_GLOAD_F:\n\t\tcase OP_GLOAD_FLD:\n\t\tcase OP_GLOAD_ENT:\n\t\tcase OP_GLOAD_S:\n\t\tcase OP_GLOAD_FNC:\n\t\tcase OP_GLOAD_V:\n\t\tcase OP_GSTOREP_I:\n\t\tcase OP_GSTOREP_F:\n\t\tcase OP_GSTOREP_ENT:\n\t\tcase OP_GSTOREP_FLD:\n\t\tcase OP_GSTOREP_S:\n\t\tcase OP_GSTOREP_FNC:\n\t\tcase OP_GSTOREP_V:\n\t\t\treturn true;\n\n\t\tcase OP_BOUNDCHECK:\n\t\t\treturn true;\n\n\t\t//fte-style arrays\n\t\tcase OP_LOADA_F:\n\t\tcase OP_LOADA_V:\n\t\tcase OP_LOADA_S:\n\t\tcase OP_LOADA_ENT:\n\t\tcase OP_LOADA_FLD:\n\t\tcase OP_LOADA_FNC:\n\t\tcase OP_LOADA_I:\n\t\tcase OP_GLOBALADDRESS:\n\t\t\treturn true;\n\n\t\t//pointer-to-global\n\t\t//other useful pointer stuff\n//\t\tcase OP_LOADP_F:\n//\t\tcase OP_LOADP_V:\n//\t\tcase OP_LOADP_S:\n//\t\tcase OP_LOADP_ENT:\n//\t\tcase OP_LOADP_FLD:\n//\t\tcase OP_LOADP_FNC:\n//\t\tcase OP_LOADP_I:\n//\t\tcase OP_STOREP_I:\n//\t\t\treturn true;\n\n\t\t//hexen2-style arrays.\n\t\tcase OP_FETCH_GBL_F:\n\t\tcase OP_FETCH_GBL_S:\n\t\tcase OP_FETCH_GBL_E:\n\t\tcase OP_FETCH_GBL_FNC:\n\t\tcase OP_FETCH_GBL_V:\n\t\t\treturn true;\n\t\t//hexen2's calling convention.\n\t\tcase OP_CALL1H:\n\t\tcase OP_CALL2H:\n\t\tcase OP_CALL3H:\n\t\tcase OP_CALL4H:\n\t\tcase OP_CALL5H:\n\t\tcase OP_CALL6H:\n\t\tcase OP_CALL7H:\n\t\tcase OP_CALL8H:\n\t\t\treturn true;\n\n\t\tdefault:\n\t\t\treturn false;\n\t\t}\n\t\treturn false;\n\tcase QCF_DARKPLACES:\n#define QCTARGVER_DP_DEF\t\t12901\n#define QCTARGVER_DP_MAX\t\t20250104\t//https://github.com/DarkPlacesEngine/DarkPlaces/pull/237\n\t\t//all id opcodes.\n\t\tif (num < OP_MULSTORE_F)\n\t\t\treturn true;\n\n\t\t//extended opcodes.\n\t\tswitch(num)\n\t\t{\n\t\tcase OP_RSHIFT_I:\n\t\tcase OP_LSHIFT_I:\n\t\tcase OP_LE_U:\n\t\tcase OP_LT_U:\n\t\tcase OP_DIV_U:\n\t\tcase OP_RSHIFT_U:\n\t\t\treturn (qcc_targetversion>=20250104);\t//https://github.com/DarkPlacesEngine/DarkPlaces/pull/237\n\n\t\tcase OP_ADD_PIW:\n\t\tcase OP_GLOBALADDRESS:\n\t\tcase OP_LOADA_F:\n\t\tcase OP_LOADA_V:\n\t\tcase OP_LOADA_S:\n\t\tcase OP_LOADA_ENT:\n\t\tcase OP_LOADA_FLD:\n\t\tcase OP_LOADA_FNC:\n\t\tcase OP_LOADA_I:\n\t\tcase OP_LOAD_P:\n\t\tcase OP_LOADP_F:\n\t\tcase OP_LOADP_V:\n\t\tcase OP_LOADP_S:\n\t\tcase OP_LOADP_ENT:\n\t\tcase OP_LOADP_FLD:\n\t\tcase OP_LOADP_FNC:\n\t\tcase OP_LOADP_I:\n#define QCTARGVER_DP_STOREP_IDX\t20241108\t//FIXME: set properly once https://github.com/DarkPlacesEngine/DarkPlaces/pull/215 is merged.\n\t\t\treturn (qcc_targetversion>=QCTARGVER_DP_STOREP_IDX);\t//https://github.com/DarkPlacesEngine/DarkPlaces/pull/215\n\t\t//opcodes that were buggy in DP.\n\t\tcase OP_ADD_IF:\t\t//dp wrote these to ints, which doesn't match our defined opcodes. not really a problem.\n\t\tcase OP_SUB_IF:\t\t//dp wrote these to ints, which doesn't match our defined opcodes. revert to _F.\n\t\tcase OP_MUL_IF:\t\t//dp wrote these to ints, which doesn't match our defined opcodes. revert to _F\n\t\tcase OP_DIV_IF:\t\t//dp wrote these to ints, which doesn't match our defined opcodes. revert to _F\n\t\tcase OP_BITAND_FI:\t//dp outputs floats, which doesn't match our defined opcodes.\n\t\tcase OP_BITOR_FI:\t//dp outputs floats, which doesn't match our defined opcodes.\n\t\tcase OP_STORE_I:\t//was omitted.\n\t\tcase OP_STORE_P:\t//was omitted.\n\t\t\treturn (qcc_targetversion>=12901);\n\n\t\tcase OP_ADD_I:\n\t\tcase OP_ADD_FI:\n\t\tcase OP_SUB_I:\n\t\tcase OP_SUB_FI:\n\t\tcase OP_CONV_ITOF:\n\t\tcase OP_CONV_FTOI:\n\t\tcase OP_LOAD_I:\t\t//no worse than the other OP_LOAD_X functions.\n\t\tcase OP_STOREP_I:\t//no worse than the other OP_STOREP_X functions\n\t\tcase OP_BITAND_I:\n\t\tcase OP_BITOR_I:\n\t\tcase OP_MUL_I:\n\t\tcase OP_DIV_I:\n\t\tcase OP_EQ_I:\n\t\tcase OP_NE_I:\n\t\tcase OP_NOT_I:\n\t\tcase OP_DIV_VF:\n\t\tcase OP_LE_I:\n\t\tcase OP_GE_I:\n\t\tcase OP_LT_I:\n\t\tcase OP_GT_I:\n\t\tcase OP_LE_IF:\n\t\tcase OP_GE_IF:\n\t\tcase OP_LT_IF:\n\t\tcase OP_GT_IF:\n\t\tcase OP_LE_FI:\n\t\tcase OP_GE_FI:\n\t\tcase OP_LT_FI:\n\t\tcase OP_GT_FI:\n\t\tcase OP_EQ_IF:\n\t\tcase OP_EQ_FI:\n\t\tcase OP_MUL_FI:\n\t\tcase OP_MUL_VI:\n\t\tcase OP_DIV_FI:\n\t\tcase OP_BITAND_IF:\n\t\tcase OP_BITOR_IF:\n\t\tcase OP_AND_I:\n\t\tcase OP_OR_I:\n\t\tcase OP_AND_IF:\n\t\tcase OP_OR_IF:\n\t\tcase OP_AND_FI:\n\t\tcase OP_OR_FI:\n\t\tcase OP_NE_IF:\n\t\tcase OP_NE_FI:\n\t\tcase OP_GSTOREP_I:\t\t//stores into the globals array, they can change any global dynamically, but thats supposedly no real security risk.\n\t\tcase OP_GSTOREP_F:\n\t\tcase OP_GSTOREP_ENT:\n\t\tcase OP_GSTOREP_FLD:\n\t\tcase OP_GSTOREP_S:\n\t\tcase OP_GSTOREP_FNC:\n\t\tcase OP_GSTOREP_V:\n//\t\tcase OP_GADDRESS:\n\t\tcase OP_GLOAD_I://c = globals[inta]\n\t\tcase OP_GLOAD_F://note: fte does not support these\n\t\tcase OP_GLOAD_FLD:\n\t\tcase OP_GLOAD_ENT:\n\t\tcase OP_GLOAD_S:\n\t\tcase OP_GLOAD_FNC:\n\t\tcase OP_BOUNDCHECK:\n\t\tcase OP_GLOAD_V:\n\t\t\treturn true;\n\n\n\t\t//this opcode looks weird\n\t\tcase OP_GADDRESS://floatc = globals[inta + floatb] (fte does not support)\n\t\t\treturn false;\n\n\t\tdefault:\t\t\t//anything I forgot to mention is new, and doesn't work in DP that I'm aware of.\n\t\t\treturn false;\n\t\t}\n\t}\n\treturn false;\n}\n\npbool QCC_OPCodeValid(QCC_opcode_t *op)\n{\n\treturn op->flags & OPF_VALID;\n}\nstatic pbool QCC_OPCode_StorePOffset(void)\n{\t//5712\n\tswitch(qcc_targetformat)\n\t{\n\tcase QCF_FTE:\n\tcase QCF_FTEH2:\n\tcase QCF_FTEDEBUG:\n\t\treturn (qcc_targetversion>=QCTARGVER_FTE_STOREP_IDX);\n\tcase QCF_DARKPLACES:\n\t\treturn (qcc_targetversion>=QCTARGVER_DP_STOREP_IDX);\n\tcase QCF_QSS:\n\t\treturn true;\n\tdefault:\n\t\treturn true;\n\t}\n}\n\nvoid QCC_OPCodeSetTarget(qcc_targetformat_t targfmt, unsigned int targver)\n{\n\tsize_t i;\n\tqcc_targetformat = targfmt;\n\tqcc_targetversion = targver;\n\tflag_undefwordsize = false;\n\tflag_pointerrelocs = false;\n\n\tswitch(qcc_targetformat)\n\t{\n\tcase QCF_FTE:\n\tcase QCF_FTEH2:\n\tcase QCF_FTEDEBUG:\n\t\tif (qcc_targetversion > QCTARGVER_FTE_MAX)\n\t\t{\n\t\t\tif (qcc_targetversion != ~0u)\n\t\t\t\tQCC_PR_ParseWarning(WARN_BADTARGET, \"target revision %u is unknown, assuming revision %u\", qcc_targetversion, QCTARGVER_FTE_MAX);\n\t\t\tqcc_targetversion = QCTARGVER_FTE_MAX;\n\t\t}\n\n\t\tflag_pointerrelocs = qcc_targetversion >= QCTARGVER_FTE_PRELOCS;\n\t\tbreak;\n\tcase QCF_DARKPLACES:\n\t\tflag_undefwordsize = true;\t//DP insists on using word-aligned pointers instead of byte-aligned ones. This breaks both pointer maths and string<>pointer casts.\n\t\tif (qcc_targetversion > QCTARGVER_DP_MAX)\n\t\t{\n\t\t\tif (qcc_targetversion != ~0u)\n\t\t\t\tQCC_PR_ParseWarning(WARN_BADTARGET, \"target revision %u is unknown, assuming revision %u\", qcc_targetversion, QCTARGVER_DP_MAX);\n\t\t\tqcc_targetversion = QCTARGVER_DP_MAX;\n\t\t}\n\t\tbreak;\n\tcase QCF_QSS:\n\t\tif (qcc_targetversion > QCTARGVER_QSS_MAX)\n\t\t{\n\t\t\tif (qcc_targetversion != ~0u)\n\t\t\t\tQCC_PR_ParseWarning(WARN_BADTARGET, \"target revision %u is unknown, assuming revision %u\", qcc_targetversion, QCTARGVER_QSS_MAX);\n\t\t\tqcc_targetversion = QCTARGVER_QSS_MAX;\n\t\t}\n\t\tbreak;\n\tdefault:\n\t\tif (qcc_targetversion > 0)\n\t\t{\n\t\t\tif (qcc_targetversion != ~0u)\n\t\t\t\tQCC_PR_ParseWarning(WARN_BADTARGET, \"target revision %u is unknown, assuming revision %u\", qcc_targetversion, 0);\n\t\t\tqcc_targetversion = 0;\n\t\t}\n\t\tbreak;\n\t}\n\n\tfor (i = 0; i < OP_NUMOPS; i++)\n\t{\n\t\tQCC_opcode_t *op = &pr_opcodes[i];\n\t\tif (QCC_OPCodeValidForTarget(targfmt, qcc_targetversion, op))\n\t\t\top->flags |= OPF_VALID;\n\t\telse\n\t\t\top->flags &= ~OPF_VALID;\n\t}\n}\n\nstatic struct {\n\tqcc_targetformat_t target;\n\tconst char *name;\n\tunsigned int defaultrev;\n} targets[] = {\n\t{QCF_STANDARD,\t\"standard\",\t\t0},\n\t{QCF_STANDARD,\t\"vanilla\",\t\t0},\n\t{QCF_STANDARD,\t\"q1\",\t\t\t0},\n\t{QCF_STANDARD,\t\"id\",\t\t\t0},\n\t{QCF_STANDARD,\t\"quakec\",\t\t0},\n\n\t{QCF_STANDARD,\t\"qs\",\t\t\t0},\n\t{QCF_QSS,\t\t\"qss\",\t\t\tQCTARGVER_QSS_MAX},\n\n\t{QCF_HEXEN2,\t\"hexen2\",\t\t0},\n\t{QCF_HEXEN2,\t\"h2\",\t\t\t0},\n\t{QCF_UHEXEN2,\t\"uhexen2\",\t\t0},\n\n\t{QCF_KK7,\t\t\"kkqwsv\",\t\t0},\n\t{QCF_KK7,\t\t\"kk7\",\t\t\t0},\n\t{QCF_KK7,\t\t\"version7\",\t\t0},\n\n\t{QCF_FTE,\t\t\"fte\",\t\t\tQCTARGVER_FTE_DEF},\t//'latest' stable revision.\n\t{QCF_FTEH2,\t\t\"fteh2\",\t\tQCTARGVER_FTE_DEF},\n\t{QCF_FTEDEBUG,\t\"ftedebug\",\t\tQCTARGVER_FTE_DEF},\n\t{QCF_FTEDEBUG,\t\"debug\",\t\tQCTARGVER_FTE_DEF},\n\n\t{QCF_FTE,\t\t\"quake2c\",\t\t5744},\t//an alias for Paril's project, which does various pointer stuff. the revision should be high enough for str[int] ops.\n\n\t{QCF_DARKPLACES,\"darkplaces\",\tQCTARGVER_DP_DEF},\n\t{QCF_DARKPLACES,\"dp\",\t\t\tQCTARGVER_DP_DEF},\n\n\t{QCF_QTEST,\t\t\"qtest\",\t\t0},\n\t{0,\t\t\t\tNULL}\n};\npbool QCC_OPCodeSetTargetName(const char *targ)\n{\t//fte_24 -> fmt=QCF_FTE, ver=24\n\tconst char *ver;\n\tsize_t i, tlen;\n\tver = strchr(targ, '_');\n\tif (ver)\n\t{\n\t\ttlen = ver-targ;\n\t\tver++;\n\t}\n\telse\n\t{\n\t\ttlen = strlen(targ);\n\t\tver = NULL;\n\t}\n\n\tfor (i = 0; targets[i].name; i++)\n\t\tif (!strnicmp(targ, targets[i].name, tlen) && strlen(targets[i].name)==tlen)\n\t\t{\n\t\t\tqcc_targetformat_t newtype = targets[i].target;\n\n\t\t\tif (numstatements > 1)\n\t\t\t{\n#define ish2(fmt) ((fmt) == QCF_HEXEN2 || (fmt) == QCF_UHEXEN2 || (fmt) == QCF_FTEH2)\n\t\t\t\tpbool nowh2  = ish2(newtype);\n\t\t\t\tpbool wash2 = ish2(qcc_targetformat);\n\n\t\t\t\tif (nowh2 != wash2)\n\t\t\t\t{\t//hexen2 involves bulk renaming of OP_CALL->OP_CALLH opcodes on load time, which breaks any previously compiled code, rather than just extra statements.\n\t\t\t\t\tQCC_PR_ParseWarning(WARN_BADTARGET, \"Cannot switch to %shexen2 target \\'%s\\' after the first statement. Ignored.\", targ, wash2?\"non-\":\"\");\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tQCC_OPCodeSetTarget(targets[i].target, ver?atoi(ver):targets[i].defaultrev);\n\t\t\treturn true;\n\t\t}\n\treturn false;\n}\n\n#define EXPR_WARN_ABOVE_1 2\n#define EXPR_DISALLOW_COMMA 4\n#define EXPR_DISALLOW_ARRAYASSIGN 8\nQCC_sref_t QCC_PR_Expression (int priority, int exprflags);\nint QCC_AStatementJumpsTo(int targ, int first, int last);\npbool QCC_StatementIsAJump(int stnum, int notifdest);\n\n//===========================================================================\n\n//typically used for debugging. Also used to determine function names for intrinsics.\nstatic const char *QCC_GetSRefName(QCC_sref_t ref)\n{\n\tif (ref.sym && ref.sym->name/* && !ref.ofs*/)\n\t{\n\t\tif (ref.sym->temp)\n\t\t\treturn ref.cast->name;\n\t\treturn ref.sym->name;\n\t}\n\treturn \"TEMP\";\n}\n\nqc_inlinestatic QCC_eval_t *QCC_SRef_Data(QCC_sref_t ref)\n{\n\treturn (QCC_eval_t*)&ref.sym->symboldata[ref.ofs];\n}\nqc_inlinestatic QCC_eval_t *QCC_SRef_DataWord(QCC_sref_t ref, int word)\n{\n\treturn (QCC_eval_t*)&ref.sym->symboldata[ref.ofs + word];\n}\n\n\n//retrieves the data associated with the reference if its constant and thus readable at compile time.\nqc_inlinestatic const QCC_eval_t *QCC_SRef_EvalConst(QCC_sref_t ref)\n{\n\tif (ref.sym && ref.sym->initialized && ref.sym->constant && !ref.sym->reloc)\n\t{\n\t\tref.sym->referenced = true;\n\t\treturn QCC_SRef_Data(ref);\n\t}\n\treturn NULL;\n}\n//retrieves the data associated with the reference if its constant and thus readable at compile time.\nqc_inlinestatic const char *QCC_SRef_EvalStringConst(QCC_sref_t ref)\n{\n\tif (ref.cast->type == ev_string)\n\t{\n\t\tconst QCC_eval_t *c = QCC_SRef_EvalConst(ref);\n\t\tif (c)\n\t\t\treturn &strings[c->string];\n\t}\n\treturn NULL;\n}\n//NULL is defined as the immediate 0 or 0i (aka __NULL__).\n//named constants that merely have the value 0 are NOT meant to count.\nqc_inlinestatic pbool QCC_SRef_IsNull(QCC_sref_t ref)\n{\n\tif (ref.cast->type == ev_integer || ref.cast->type == ev_uint || ref.cast->type == ev_float || ref.cast->type == ev_pointer)\n\t{\n\t\tconst QCC_eval_t *c = QCC_SRef_EvalConst(ref);\n\t\tif (c)\n\t\t\treturn !c->_int;\n\t}\n\treturn false;\n}\n\n//retrieves the int value associated with the reference if its constant and thus readable at compile time.\nstatic pint64_t QCC_Eval_Int(const QCC_eval_t *eval, QCC_type_t *type)\n{\n\tif (!eval)\n\t{\n\t\tQCC_PR_ParseWarning(ERR_BADIMMEDIATETYPE, \"Not an initialised constant\");\n\t\treturn 0;\n\t}\n\tswitch(type->type)\n\t{\n\tcase ev_float:\t\treturn eval->_float;\n\tcase ev_double:\t\treturn eval->_double;\n\tcase ev_integer:\treturn eval->_int;\n//\tcase ev_function:\n//\tcase ev_entity:\n//\tcase ev_field:\n//\tcase ev_pointer:\n\tcase ev_uint:\t\treturn eval->_uint;\n\tcase ev_boolean:\treturn QCC_Eval_Int(eval, type->parentclass);\t//bools are weird.\n\tcase ev_int64:\t\treturn eval->i64;\n\tcase ev_uint64:\t\treturn eval->u64;\n//\tcase ev_string:\n//\tcase ev_vector:\n\tcase ev_struct:\n\tcase ev_union:\n\tdefault:\n\t\tQCC_PR_ParseWarning(ERR_BADIMMEDIATETYPE, \"Unable to evaluate to numeric constant\");\n\t\treturn 0;\n\t}\n}\n\n//retrieves the int value associated with the reference if its constant and thus readable at compile time.\nstatic float QCC_Eval_Float(const QCC_eval_t *eval, QCC_type_t *type)\n{\n\tif (!eval)\n\t{\n\t\tQCC_PR_ParseWarning(ERR_BADIMMEDIATETYPE, \"Not an initialised constant\");\n\t\treturn 0;\n\t}\n\tswitch(type->type)\n\t{\n\tcase ev_float:\t\treturn eval->_float;\n\tcase ev_double:\t\treturn eval->_double;\n\tcase ev_integer:\treturn eval->_int;\n//\tcase ev_function:\n//\tcase ev_entity:\n//\tcase ev_field:\n//\tcase ev_pointer:\n\tcase ev_uint:\t\treturn eval->_uint;\n\tcase ev_boolean:\treturn QCC_Eval_Float(eval, type->parentclass);\t//bools are weird.\n\tcase ev_int64:\t\treturn eval->i64;\n\tcase ev_uint64:\t\treturn eval->u64;\n//\tcase ev_string:\n//\tcase ev_vector:\n\tcase ev_struct:\n\tcase ev_union:\n\tdefault:\n\t\tQCC_PR_ParseWarning(ERR_BADIMMEDIATETYPE, \"Unable to evaluate to numeric constant\");\n\t\treturn 0;\n\t}\n}\n\n//retrieves the data associated with the reference if its constant and thus readable at compile time.\nstatic pbool QCC_Eval_Truth(const QCC_eval_t *eval, QCC_type_t *type, pbool assume)\n{\n\tpbool istrue = false;\n\tint i;\n\tif (!eval)\n\t\treturn assume;\n\tswitch(type->type)\n\t{\n\tcase ev_float:\n\t\tistrue = (eval->_float != 0);\n\t\tbreak;\n\tcase ev_double:\n\t\tistrue = (eval->_double != 0);\n\t\tbreak;\n\tcase ev_integer:\n\tcase ev_uint:\n\tcase ev_function:\n\tcase ev_entity:\n\tcase ev_field:\n\tcase ev_pointer:\n\tcase ev_boolean:\n\t\tistrue = (eval->_int != 0);\n\t\tbreak;\n\tcase ev_int64:\n\tcase ev_uint64:\n\t\tistrue = (eval->i64 != 0);\n\t\tbreak;\n\tcase ev_string:\n\t\tif (flag_ifstring)\n\t\t\tistrue = !!strings[eval->_int];\t//value compare.\n\t\telse\n\t\t\tistrue = (eval->_int != 0);\t\t//offset compare\n\t\tbreak;\n\tcase ev_vector:\n\t\tif (flag_ifvector)\n\t\t\tistrue = eval->vector[0] || eval->vector[1] || eval->vector[2];\n\t\telse\n\t\t\tistrue = (eval->_float != 0);\t\t//legacy buggy behaviour\n\t\tbreak;\n\n\tcase ev_struct:\n\tcase ev_union:\n\t\tQCC_PR_ParseWarning(WARN_CONDITIONALTYPEMISMATCH, \"conditional type mismatch: %s\", basictypenames[type->type]);\n\t\t//fall through anyway.\n\tdefault:\n\t\tfor (i = 0; i < type->size; i++)\n\t\t\tif ((&eval->_int)[i])\n\t\t\t\tbreak;\n\t\tistrue = i!=type->size;\n\t\tbreak;\n\t}\n\treturn istrue;\n}\n\nstatic const char *QCC_GetRefName(QCC_ref_t *ref, char *buffer, size_t buffersize)\n{\n\tswitch(ref->type)\n\t{\n\tcase REF_FIELD:\n\tcase REF_NONVIRTUAL:\n\t\tQC_snprintfz(buffer, buffersize, \"%s.%s\", QCC_GetSRefName(ref->base), QCC_GetSRefName(ref->index));\n\t\treturn buffer;\n\tcase REF_ARRAY:\n\tcase REF_STRING:\n\t\tQC_snprintfz(buffer, buffersize, \"%s[%s]\", QCC_GetSRefName(ref->base), QCC_GetSRefName(ref->index));\n\t\treturn buffer;\n\tcase REF_POINTER:\n\t\tQC_snprintfz(buffer, buffersize, \"%s->%s\", QCC_GetSRefName(ref->base), QCC_GetSRefName(ref->index));\n\t\treturn buffer;\n\tcase REF_ACCESSOR:\n\t\tif (*ref->accessor->fieldname)\n\t\t{\t//not an anonymous field\n\t\t\tif (ref->index.sym)\n\t\t\t\tQC_snprintfz(buffer, buffersize, \"%s.%s[%s]\", QCC_GetSRefName(ref->base), ref->accessor->fieldname, QCC_GetSRefName(ref->index));\n\t\t\telse\n\t\t\t\tQC_snprintfz(buffer, buffersize, \"%s.%s\", QCC_GetSRefName(ref->base), ref->accessor->fieldname);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (ref->index.sym)\n\t\t\t\tQC_snprintfz(buffer, buffersize, \"%s[%s]\", QCC_GetSRefName(ref->base), QCC_GetSRefName(ref->index));\n\t\t\telse\n\t\t\t\tQC_snprintfz(buffer, buffersize, \"*%s\", QCC_GetSRefName(ref->base));\n\t\t}\n\t\tbreak;\n\tcase REF_ARRAYHEAD:\n\tcase REF_GLOBAL:\n\tdefault:\n\t\tbreak;\n\t}\n\treturn QCC_GetSRefName(ref->base);\n}\n\n/*\n============\nPR_Statement\n\nEmits a primitive statement, returning the var it places it's value in\n============\n*/\nstatic int QCC_ShouldConvert(QCC_type_t *from, etype_t wanted)\n{\n\tif (from->type == ev_boolean && wanted != ev_boolean)\n\t\tfrom = from->parentclass;\n\n\t/*no conversion needed*/\n\tif (from->type == wanted)\n\t\treturn 0;\n\tif (from->type == ev_integer && wanted == ev_function)\n\t\treturn 0;\n\tif (from->type == ev_integer && wanted == ev_pointer)\n\t\treturn 0;\n\t/*stuff needs converting*/\n\tif (from->type == ev_pointer && from->aux_type)\n\t{\n\t\tif (from->aux_type->type == ev_float && wanted == ev_integer)\n\t\t\treturn OP_LOADP_FTOI;\n\n\t\tif (from->aux_type->type == ev_integer && wanted == ev_float)\n\t\t\treturn OP_LOADP_ITOF;\n\t}\n\telse\n\t{\n\t\tif (from->type == ev_float && wanted == ev_integer)\n\t\t\treturn OP_CONV_FTOI;\n\t\tif (from->type == ev_integer && wanted == ev_float)\n\t\t\treturn OP_CONV_ITOF;\n\n\t\tif (from->type == ev_float && wanted == ev_uint)\n\t\t\treturn OP_CONV_FU;\n\t\tif (from->type == ev_uint && wanted == ev_float)\n\t\t\treturn OP_CONV_UF;\n\n\t\tif ((from->type == ev_integer||from->type == ev_uint) && (wanted == ev_integer||wanted == ev_uint))\n\t\t\treturn 0;\n\t\tif ((from->type == ev_int64||from->type == ev_uint64) && (wanted == ev_int64||wanted == ev_uint64))\n\t\t\treturn 0;\n\t\tif ((from->type == ev_int64||from->type == ev_uint64) && (wanted == ev_integer||wanted == ev_uint))\n\t\t\treturn OP_CONV_I64I;\n\t\tif ((from->type == ev_integer) && (wanted == ev_int64 || wanted == ev_uint64))\n\t\t\treturn OP_CONV_II64;\n\t\tif (from->type == ev_uint && (wanted == ev_int64 || wanted == ev_uint64))\n\t\t\treturn OP_CONV_UI64;\n\n\t\tif (from->type == ev_float && wanted == ev_double)\n\t\t\treturn OP_CONV_FD;\n\t\tif (from->type == ev_double && wanted == ev_float)\n\t\t\treturn OP_CONV_DF;\n\n\t\tif (from->type == ev_int64 && wanted == ev_float)\n\t\t\treturn OP_CONV_I64F;\n\t\tif (from->type == ev_float && wanted == ev_int64)\n\t\t\treturn OP_CONV_FI64;\n\t\tif (from->type == ev_int64 && wanted == ev_double)\n\t\t\treturn OP_CONV_I64D;\n\t\tif (from->type == ev_double && wanted == ev_int64)\n\t\t\treturn OP_CONV_DI64;\n\t\tif (from->type == ev_uint64 && wanted == ev_double)\n\t\t\treturn OP_CONV_U64D;\n\t\tif (from->type == ev_double && wanted == ev_uint64)\n\t\t\treturn OP_CONV_DU64;\n\t\tif (from->type == ev_uint64 && wanted == ev_float)\n\t\t\treturn OP_CONV_U64F;\n\t\tif (from->type == ev_float && wanted == ev_uint64)\n\t\t\treturn OP_CONV_FU64;\n\t\n\t\tif (from->type == ev_float && wanted == ev_vector)\n\t\t\treturn OP_MUL_FV;\n\t}\n\n\t/*impossible*/\n\treturn -1;\n}\nstatic QCC_sref_t QCC_TryEvaluateCast(QCC_sref_t src, QCC_type_t *cast, pbool implicit);\nstatic QCC_sref_t QCC_SupplyConversionForAssignment(QCC_ref_t *to, QCC_sref_t from, QCC_type_t *wanted, pbool fatal)\n{\n\tint o;\n\tQCC_sref_t rhs;\n\n\trhs = QCC_TryEvaluateCast(from, to->cast, !fatal);\n\tif (rhs.cast)\n\t\treturn rhs;\n\n\tif (wanted->type == ev_accessor && wanted->parentclass && from.cast->type != ev_accessor)\n\t\twanted = wanted->parentclass;\n\tif (from.cast->type == ev_accessor && from.cast->parentclass && wanted->type != ev_accessor)\n\t\tfrom.cast = from.cast->parentclass;\n\n\to = QCC_ShouldConvert(from.cast, wanted->type);\n\n\tif (o == 0) //type already matches\n\t\treturn from;\n\tif (flag_typeexplicit)\n\t{\n\t\tchar totypename[256], fromtypename[256], destname[256];\n\t\tTypeName(wanted, totypename, sizeof(totypename));\n\t\tTypeName(from.cast, fromtypename, sizeof(fromtypename));\n\t\tQCC_PR_ParseErrorPrintSRef(ERR_TYPEMISMATCH, from, \"Implicit type mismatch on assignment to %s. Needed %s, got %s.\", QCC_GetRefName(to, destname, sizeof(destname)), totypename, fromtypename);\n\t}\n\tif (o < 0)\n\t{\n\t\tif (fatal && wanted->type != ev_variant && from.cast->type != ev_variant)\n\t\t{\n\t\t\tchar totypename[256], fromtypename[256], destname[256];\n\t\t\tTypeName(wanted, totypename, sizeof(totypename));\n\t\t\tTypeName(from.cast, fromtypename, sizeof(fromtypename));\n\t\t\tif (flag_laxcasts)\n\t\t\t{\n\t\t\t\tQCC_PR_ParseWarning(WARN_LAXCAST, \"Implicit type mismatch on assignment to %s. Needed %s, got %s.\", QCC_GetRefName(to, destname, sizeof(destname)), totypename, fromtypename);\n\t\t\t\tQCC_PR_ParsePrintSRef(WARN_LAXCAST, from);\n\t\t\t}\n\t\t\telse\n\t\t\t\tQCC_PR_ParseErrorPrintSRef(ERR_TYPEMISMATCH, from, \"Implicit type mismatch on assignment to %s. Needed %s, got %s.\", QCC_GetRefName(to, destname, sizeof(destname)), totypename, fromtypename);\n\t\t}\n\t\treturn from;\n\t}\n\n\tif (o == OP_MUL_FV)\n\t\trhs = QCC_MakeVectorConst(1,1,1);\n\telse\n\t\trhs = nullsref;\n\treturn QCC_PR_Statement(&pr_opcodes[o], from, rhs, NULL);\t//conversion return value\n}\nstatic QCC_sref_t QCC_SupplyConversion(QCC_sref_t  var, etype_t wanted, pbool fatal)\n{\n\textern char *basictypenames[];\n\tint o;\n\tQCC_sref_t rhs;\n\n\to = QCC_ShouldConvert(var.cast, wanted);\n\n\tif (o == 0) //type already matches\n\t\treturn var;\n\tif (flag_typeexplicit)// && !QCC_SRef_IsNull(var))\n\t\tQCC_PR_ParseErrorPrintSRef(ERR_TYPEMISMATCH, var, \"Automatic type conversions disabled. Needed %s, got %s.\", basictypenames[wanted], basictypenames[var.cast->type]);\n\tif (o < 0)\n\t{\n\t\tif (fatal && wanted != ev_variant && var.cast->type != ev_variant)\n\t\t{\n\t\t\tif (flag_laxcasts)\n\t\t\t{\n\t\t\t\tQCC_PR_ParseWarning(WARN_LAXCAST, \"Implicit type mismatch. Needed %s%s%s, got %s%s%s.\", col_type,basictypenames[wanted],col_none, col_type,basictypenames[var.cast->type],col_none);\n\t\t\t\tQCC_PR_ParsePrintSRef(WARN_LAXCAST, var);\n\t\t\t}\n\t\t\telse\n\t\t\t\tQCC_PR_ParseErrorPrintSRef(ERR_TYPEMISMATCH, var, \"Implicit type mismatch. Needed %s%s%s, got %s%s%s.\", col_type,basictypenames[wanted],col_none, col_type,basictypenames[var.cast->type],col_none);\n\t\t}\n\t\treturn var;\n\t}\n\n\tif (o == OP_MUL_FV)\n\t\trhs = QCC_MakeVectorConst(1,1,1);\n\telse\n\t\trhs = nullsref;\n\treturn QCC_PR_Statement(&pr_opcodes[o], var, rhs, NULL);\t//conversion return value\n}\n\nsize_t tempslocked;\t//stats\nsize_t tempsused;\nsize_t tempsmax;\ntemp_t *tempsinfo;\n\nQCC_def_t *aliases;\nQCC_def_t *allaliases;\n\nstatic QCC_sref_t QCC_GetTemp(QCC_type_t *type);\nvoid QCC_FreeTemp(QCC_sref_t t);\nvoid QCC_FreeDef(QCC_def_t *def);\nQCC_sref_t QCC_MakeSRefForce(QCC_def_t *def, unsigned int ofs, QCC_type_t *type);\nQCC_sref_t QCC_MakeSRef(QCC_def_t *def, unsigned int ofs, QCC_type_t *type);\n\n//we're about to overwrite the given def, so if there's any aliases to it, we need to clear them out.\n//if def == NULL, then clobber all.\n//def should never be a temp...\nstatic void QCC_ClobberDef(QCC_def_t *def)\n{\n\tQCC_def_t *a, **link;\n\tfor (link = &aliases; *link;)\n\t{\n\t\ta = *link;\n\t\tif (!def || a->generatedfor == def)\n\t\t{\n\t\t\t//okay, we have a live alias. bum.\n\t\t\t//create a new temp for it. update previous statements to refer to the original location instead of the alias.\n\t\t\t//copy the source into the temp, and then update the alias's def to be a sub-symbol of the temp's def instead of a sub-symbol of the original location.\n\t\t\t//yes. all this just to make mods like xonotic not an insane sea of copies.\n\t\t\tQCC_sref_t tmp, from;\n\t\t\tint st;\n\t\t\t*link = a->nextlocal;\n\t\t\ta->nextlocal = NULL;\n\t\t\tif (a->refcount)\n\t\t\t{\n\t\t\t\ttmp = QCC_GetTemp(a->type->type==ev_variant?type_vector:a->type);\n\t\t\t\tfor (st = a->fromstatement; st < numstatements; st++)\n\t\t\t\t{\n\t\t\t\t\tif (statements[st].a.sym == a)\n\t\t\t\t\t\tstatements[st].a.sym = a->generatedfor;\n\t\t\t\t\tif (statements[st].b.sym == a)\n\t\t\t\t\t\tstatements[st].b.sym = a->generatedfor;\n\t\t\t\t\tif (statements[st].c.sym == a)\n\t\t\t\t\t\tstatements[st].c.sym = a->generatedfor;\n\t\t\t\t}\n\t\t\t\ttmp.sym->refcount = a->symbolheader->refcount;\n\t\t\t\ta->symbolheader = tmp.sym;\t//the alias now refers to a temp\n\t\t\t\tfrom = QCC_MakeSRefForce(a->generatedfor, 0, a->type);\n\t\t\t\ta->generatedfor = tmp.sym;\n\t\t\t\ta->name = tmp.sym->name;\n\t\t\t\ta->temp = tmp.sym->temp;\n\t\t\t\ta->ofs = tmp.sym->ofs;\n\t\t\t\ttmp.sym = a;\n\t\t\t\tif (a->type->type==ev_variant || a->type->type == ev_vector)\n\t\t\t\t\tQCC_FreeTemp(QCC_PR_StatementFlags(&pr_opcodes[OP_STORE_V], from, tmp, NULL, STFL_PRESERVEB));\n\t\t\t\telse if (a->type->type==ev_int64 || a->type->type == ev_uint64 || a->type->type == ev_double)\n\t\t\t\t\tQCC_FreeTemp(QCC_PR_StatementFlags(&pr_opcodes[OP_STORE_I64], from, tmp, NULL, STFL_PRESERVEB));\n\t\t\t\telse\n\t\t\t\t\tQCC_FreeTemp(QCC_PR_StatementFlags(&pr_opcodes[OP_STORE_F], from, tmp, NULL, STFL_PRESERVEB));\n\t\t\t}\n\t\t}\n\t\telse\n\t\t\tlink = &(*link)->nextlocal;\n\t}\n}\n//return an alias to a reference. typically the return value.\nstatic QCC_sref_t QCC_GetAliasTemp(QCC_sref_t ref)\n{\n\tQCC_def_t *def;\n\tdef = qccHunkAlloc(sizeof(QCC_def_t));\n\tdef->type = ref.cast;\n\tdef->generatedfor = ref.sym;\n\tdef->symbolheader = def;\n\tdef->symbolsize = ref.sym->symbolsize;\n\tdef->name = ref.sym->name;\n\tdef->referenced = true;\n\tdef->fromstatement = numstatements;\n\tdef->scope = pr_scope;\n\n\t//allaliases allows them to be finalized correctly\n\tdef->strip = true;\n\tdef->next = allaliases;\n\tallaliases = def;\n\n\t//and active aliases are the ones that are currently live in their original location\n\tdef->nextlocal = aliases;\n\taliases = def;\n\n\tQCC_FreeTemp(ref);\n\n\treturn QCC_MakeSRefForce(def, 0, ref.cast);\n}\n\nstatic QCC_sref_t QCC_GetTemp(QCC_type_t *type)\n{\n\tQCC_sref_t var_c = nullsref;\n\tsize_t u;\n\n\tvar_c.cast = type;\n\n\tif (opt_overlaptemps)\t//don't exceed. This lets us allocate a huge block, and still be able to compile smegging big funcs.\n\t{\n\t\tfor (u = 0; u < tempsused; u += tempsinfo[u].size)\n\t\t{\n\t\t\tif (!tempsinfo[u].def->refcount && tempsinfo[u].size == type->size)\n\t\t\t\tbreak;\n\t\t}\n\t}\n\telse\n\t\tu = tempsused;\n\n\tif (u == tempsused)\n\t{\n\t\tchar buffer[32];\n\t\tgofs_t ofs = tempsused;\n\t\tunsigned int i;\n\t\tunsigned int size = type->size;\n\t\tif (type->type == ev_accessor || type->type == ev_bitfld)\n\t\t\tsize = type->parentclass->size;\n\t\ttempsused += size;\n\n\t\tif (tempsused > tempsmax)\n\t\t{\n\t\t\tsize_t newmax = (tempsused + 64) & ~63;\n\t\t\ttempsinfo = realloc(tempsinfo, newmax*sizeof(*tempsinfo));\n\t\t\tmemset(tempsinfo+ofs, 0, (newmax-ofs)*sizeof(*tempsinfo));\n\t\t\ttempsmax = newmax;\n\t\t}\n\t\tfor(i = u; size > 0; i++, size--)\n\t\t{\n//\t\t\ttempsinfo[i].used = (i==ofs)?0:2;\t//2 is an 'padded' temp. if encountered, scan down for one that doesn't have a 2 there.\n\t\t\ttempsinfo[i].size = (i==ofs)?size:0;\n\t\t\ttempsinfo[i].def = qccHunkAlloc(sizeof(QCC_def_t));\n\t\t\ttempsinfo[i].def->symbolheader = tempsinfo[i].def;\n\t\t\ttempsinfo[i].def->symbolsize = tempsinfo[i].size;\n\t\t}\n\t\tfor (i = 0; i < tempsused; i++)\n\t\t\ttempsinfo[i].def->temp = &tempsinfo[i];\n\t\ttempsinfo[u].def->ofs = u;\n\t\ttempsinfo[u].def->type = type;\n\n\t\tsprintf(buffer, \"temp_%u\", (unsigned)u);\n\t\ttempsinfo[u].def->name = qccHunkAlloc(strlen(buffer)+1);\n\t\tstrcpy(tempsinfo[u].def->name, buffer);\n\t}\n\telse\n\t\toptres_overlaptemps+=type->size;\n\n\tvar_c.sym = tempsinfo[u].def;\n\tvar_c.ofs = 0;//u;\n\ttempsinfo[u].def->refcount+=1;\n\n\ttempsinfo[u].lastfunc = pr_scope;\n\ttempsinfo[u].lastline = pr_source_line;\n\ttempsinfo[u].laststatement = numstatements;\n\n\tvar_c.sym->referenced = true;\n\treturn var_c;\n}\n\nvoid QCC_FinaliseTemps(void)\n{\n\tunsigned int i;\n\tfor (i = 0; i < tempsused; )\n\t{\n\t\ttempsinfo[i].def->ofs = numpr_globals;\n\t\tnumpr_globals += tempsinfo[i].size;\n\t\ti += tempsinfo[i].size;\n\t}\n\n\tif (numpr_globals >= MAX_REGS)\n\t{\n\t\tif (!opt_overlaptemps || !opt_locals_overlapping)\n\t\t\tQCC_Error(ERR_TOOMANYGLOBALS, \"numpr_globals exceeded MAX_REGS - you'll need to use more optimisations\");\n\t\telse\n\t\t\tQCC_Error(ERR_TOOMANYGLOBALS, \"numpr_globals exceeded MAX_REGS of %u. Increase with eg: -max_regs %u\", MAX_REGS, MAX_REGS*2);\n\t}\n\n\t//finalize alises so they map correctly.\n\twhile(allaliases)\n\t{\n\t\tallaliases->symbolheader = allaliases->generatedfor->symbolheader;\n\t\tallaliases->ofs = allaliases->generatedfor->ofs;\n\t\tallaliases = allaliases->next;\n\t}\n}\n\nvoid QCC_FreeDef(QCC_def_t *def)\n{\n\tif (def && def->symbolheader)\n\t{\n\t\tif (--def->symbolheader->refcount < 0)\n\t\t\tQCC_PR_ParseWarning(WARN_DEBUGGING, \"INTERNAL: over-freed refcount to %s\", def->name);\n\t}\n}\n//nothing else references this temp.\nvoid QCC_FreeTemp(QCC_sref_t t)\n{\n\tif (t.sym && t.sym->symbolheader)\n\t{\n\t\tif (--t.sym->symbolheader->refcount < 0)\n\t\t\tQCC_PR_ParseWarning(WARN_DEBUGGING, \"INTERNAL: over-freed refcount to %s\", QCC_VarAtOffset(t));\n\t}\n}\n\nstatic void QCC_ForceUnFreeDef(QCC_def_t *def)\n{\n\tif (def && def->symbolheader)\n\t\tdef->symbolheader->refcount++;\n}\n/*\nstatic void QCC_UnFreeDef(QCC_def_t *def)\n{\n\tif (def && def->symbolheader)\n\t{\n\t\tif (!def->symbolheader->refcount++)\n\t\t\tQCC_PR_ParseWarning(WARN_DEBUGGING, \"INTERNAL: %s was already fully freed.\", def->name);\n\t}\n}\n*/\nstatic void QCC_UnFreeTemp(QCC_sref_t t)\n{\n\tif (t.sym && t.sym->symbolheader)\n\t{\n\t\tif (!t.sym->symbolheader->refcount++)\n\t\t\tQCC_PR_ParseWarning(WARN_DEBUGGING, \"INTERNAL: %s+%i@%i was already fully freed.\", QCC_VarAtOffset(t), t.ofs, t.sym->ofs);\n\t}\n}\n\n//We've just parsed a statement.\n//We can gaurentee that any used temps are now not used.\n#ifdef _DEBUG\nstatic void QCC_FreeTemps(void)\n{\n}\n#else\n#define QCC_FreeTemps()\n#endif\nvoid QCC_PurgeTemps(void)\n{\n\tfree(tempsinfo);\n\ttempsinfo = NULL;\n\ttempsmax = 0;\n\ttempsused = 0;\n\taliases = NULL;\n\tallaliases = NULL;\n\n\n\tmax_labels = 0;\n\tmax_gotos = 0;\n\tfree(pr_labels);\n\tpr_labels = NULL;\n\tfree(pr_gotos);\n\tpr_gotos = NULL;\n\tnum_gotos = num_labels = 0;\n\n\tfree(initstatements);\n\tinitstatements = NULL;\n\tnuminitstatements = maxinitstatements = 0;\n}\n\n//temps that are still in use over a function call can be considered dodgy.\n//we need to remap these to locally defined temps, on return from the function so we know we got them all.\nstatic void QCC_LockActiveTemps(QCC_sref_t exclude)\n{\n\tsize_t u;\n\tsize_t excludeofs = ~0;\n\n\tQCC_ClobberDef(NULL);\n\n\tif (exclude.sym && exclude.sym->temp)\n\t\texcludeofs = exclude.sym->temp - tempsinfo;\n\n\tfor (u = 0; u < tempsused; u += tempsinfo[u].size)\n\t{\n\t\tif (tempsinfo[u].def->refcount && u != excludeofs)\t//don't print this after an error jump out.\n\t\t\ttempsinfo[u].locked = true;\n\t}\n}\n\n/*\nstatic void QCC_ForceLockTempForOffset(int ofs)\n{\n\ttempsinfo[ofs].locked = true;\n}\n*/\n\nstatic QCC_def_t *QCC_MakeLocked(gofs_t tofs, gofs_t tsize, QCC_def_t *tmp)\n{\n#ifdef WRITEASM\n\tchar buffer[128];\n#endif\n\n\tQCC_def_t *def = NULL;\n\tQCC_def_t *a, **link;\n\ttempslocked+=tsize;\n\n\tdef = QCC_PR_DummyDef(type_float, NULL, pr_scope, tsize==1?0:tsize, NULL, 0, false, GDF_STRIP);\n\tdef->arraylengthprefix = false;\t//don't waste space with temps.\n#ifdef WRITEASM\n\tsprintf(buffer, \"locked_%i\", tofs);\n\tdef->name = qccHunkAlloc(strlen(buffer)+1);\n\tstrcpy(def->name, buffer);\n#endif\n\tdef->referenced = true;\n\n\n\t//aliases might refer to this temp.\n\t//make sure they point to the local instead.\n\tfor (link = &allaliases; *link;)\n\t{\n\t\ta = *link;\n\t\tif (a->generatedfor == tmp && a->scope == pr_scope)\n\t\t{\n//\t\t\t*link = a->next;\n//\t\t\ta->next = NULL;\n\t\t\t\n\t\t\ta->generatedfor = def;\n\t\t\ta->name = def->name;\n\t\t}\n\t\telse\n\t\t\tlink = &(*link)->next;\n\t}\n\treturn def;\n}\nstatic void QCC_RemapLockedTemp(gofs_t tofs, gofs_t tsize, int firststatement, int laststatement)\n{\n\tQCC_def_t *def = NULL;\n\tQCC_statement_t *st;\n\tint i;\n\n\tfor (i = firststatement, st = &statements[i]; i < laststatement; i++, st++)\n\t{\n\t\tif (pr_opcodes[st->op].type_a && st->a.sym && st->a.sym->temp && st->a.sym->ofs >= tofs && st->a.sym->ofs < tofs + tsize)\n\t\t{\n\t\t\tif (!def)\n\t\t\t\tdef = QCC_MakeLocked(tofs, tsize, st->a.sym);\n\t\t\tst->a.sym = def;\n//\t\t\tst->a.ofs = st->a.ofs - tofs;\n\t\t}\n\t\tif (pr_opcodes[st->op].type_b && st->b.sym && st->b.sym->temp && st->b.sym->ofs >= tofs && st->b.sym->ofs < tofs + tsize)\n\t\t{\n\t\t\tif (!def)\n\t\t\t\tdef = QCC_MakeLocked(tofs, tsize, st->b.sym);\n\t\t\tst->b.sym = def;\n//\t\t\tst->b.ofs = st->b.ofs - tofs;\n\t\t}\n\t\tif (pr_opcodes[st->op].type_c && st->c.sym && st->c.sym->temp && st->c.sym->ofs >= tofs && st->c.sym->ofs < tofs + tsize)\n\t\t{\n\t\t\tif (!def)\n\t\t\t\tdef = QCC_MakeLocked(tofs, tsize, st->c.sym);\n\t\t\tst->c.sym = def;\n//\t\t\tst->c.ofs = st->c.ofs - tofs;\n\t\t}\n\t}\n}\n\n//called for function calls to avoid wasting copies\nstatic pbool QCC_RemapTemp(int firststatement, int laststatement, QCC_sref_t temp, QCC_sref_t targ)\n{\n\tQCC_def_t *def = NULL;\n\tQCC_statement_t *st;\n\tint i;\n\tpbool remapped = false;\n\n\t//if its not a temp, we'll need to do a copy.\n\tif (!temp.sym->temp)\n\t\treturn false;\n\tif (temp.sym->refcount != 1)\n\t\treturn false;\t//should only have one reference... something weird is happening if it doesn't.\n\n\t//make sure the target is not already used in the interim, because that gets messy.\n\tdef = targ.sym->symbolheader;\n\tfor (i = firststatement, st = &statements[i]; i < laststatement; i++, st++)\n\t{\n\t\tif (pr_opcodes[st->op].type_a && st->a.sym && st->a.sym->symbolheader == def)\n\t\t\treturn false;\n\t\tif (pr_opcodes[st->op].type_b && st->b.sym && st->b.sym->symbolheader == def)\n\t\t\treturn false;\n\t\tif (pr_opcodes[st->op].type_c && st->c.sym && st->c.sym->symbolheader == def)\n\t\t\treturn false;\n\t}\n\n\tdef = temp.sym->symbolheader;\n\t//make sure things actually look okay. reads from it imply something weird\n\tfor (i = firststatement, st = &statements[i]; i < laststatement; i++, st++)\n\t{\n\t\tif (pr_opcodes[st->op].type_a && st->a.sym && st->a.sym->temp == def->temp)\n\t\t\tif (!OpAssignsToA(st->op))\n\t\t\t\treturn false;\n\t\tif (pr_opcodes[st->op].type_b && st->b.sym && st->b.sym->temp == def->temp)\n\t\t\tif (!OpAssignsToB(st->op))\n\t\t\t\treturn false;\n\t\tif (pr_opcodes[st->op].type_c && st->c.sym && st->c.sym->temp == def->temp)\n\t\t\tif (!OpAssignsToC(st->op))\n\t\t\t\treturn false;\n\t}\n\n\t//okay, go ahead and remap it\n\tfor (i = firststatement, st = &statements[i]; i < laststatement; i++, st++)\n\t{\n\t\tif (pr_opcodes[st->op].type_a && st->a.sym && st->a.sym->temp == def->temp)\n\t\t{\n\t\t\tst->a.sym = targ.sym;\n\t\t\tremapped = true;\n\t\t}\n\t\tif (pr_opcodes[st->op].type_b && st->b.sym && st->b.sym->temp == def->temp)\n\t\t{\n\t\t\tst->b.sym = targ.sym;\n\t\t\tremapped = true;\n\t\t}\n\t\tif (pr_opcodes[st->op].type_c && st->c.sym && st->c.sym->temp == def->temp)\n\t\t{\n\t\t\tst->c.sym = targ.sym;\n\t\t\tremapped = true;\n\t\t}\n\t}\n\treturn remapped;\n}\n\nstatic void QCC_RemapLockedTemps(int firststatement, int laststatement)\n{\n\tsize_t u;\n\tfor (u = 0; u < tempsused; u += tempsinfo[u].size)\n\t{\n\t\tif (tempsinfo[u].locked)\n\t\t{\n\t\t\tQCC_RemapLockedTemp(u, tempsinfo[u].size, firststatement, laststatement);\n\t\t\ttempsinfo[u].locked = false;\n\t\t}\n\t}\n}\n\nstatic void QCC_fprintfLocals(FILE *f, QCC_def_t *locals)\n{\n\tQCC_def_t\t*var;\n\tchar typebuf[1024];\n\tsize_t u;\n\n\tfor (var = locals; var; var = var->nextlocal)\n\t{\n\t\tif (var->arraysize)\n\t\t\tfprintf(f, \"local %s %s[%i];\\n\", TypeName(var->type, typebuf, sizeof(typebuf)), var->name, var->arraysize);\n\t\telse\n\t\t\tfprintf(f, \"local %s %s;\\n\", TypeName(var->type, typebuf, sizeof(typebuf)), var->name);\n\t}\n\n\tif (opt_overlaptemps)\t//don't spam.\n\tfor (u = 0; u < tempsused; u += tempsinfo[u].size)\n\t{\n\t\tif (!tempsinfo[u].locked)\n//\t\tif (tempsinfo[u].lastfunc == pr_scope)\n\t\t{\n\t\t\tfprintf(f, \"local %s temp_%u; //%i\\n\", (tempsinfo[u].size == 1)?\"float\":\"vector\", (unsigned)u, tempsinfo[u].lastline);\n\t\t}\n\t}\n}\n\n#ifdef WRITEASM\nvoid QCC_WriteAsmFunction(QCC_function_t\t*sc, unsigned int firststatement, QCC_def_t *firstlocal);\nconst char *QCC_VarAtOffset(QCC_sref_t ref)\n{\t//for debugging, we don't need to preserve the cast.\n\tstatic char message[1024];\n\t//check the temps\n\n\tif (ref.sym)\n\t{\n\t\tif (ref.sym && ref.sym->temp)\n\t\t{\n\t\t\tif (!ref.ofs)\n\t\t\t\tQC_snprintfz(message, sizeof(message), \"temp_%i\", ref.sym->ofs-tempsinfo[0].def->ofs);\n\t\t\telse\n\t\t\t\tQC_snprintfz(message, sizeof(message), \"temp_%i+%i\", ref.sym->ofs-tempsinfo[0].def->ofs, ref.ofs);\n\t\t\treturn message;\n\t\t}\n\t\telse if (ref.sym->name && !STRCMP(ref.sym->name, \"IMMEDIATE\"))\n\t\t{\n\t\t\tint type;\n\t\t\tconst QCC_eval_t *val = QCC_SRef_EvalConst(ref);\n\t\t\ttype = !val?-1:ref.cast->type;\n\t\t\tif (type == ev_variant)\n\t\t\t\ttype = ref.sym->type->type;\n\t\t\tif (ref.sym->reloc)// && ref.cast->type == ev_pointer)\n\t\t\t{\n\t\t\t\tQC_snprintfz(message, sizeof(message), \"&(%s+%i)\", ref.sym->reloc->name, ref.sym->symboldata[ref.ofs]._int);\n\t\t\t\treturn message;\n\t\t\t}\n\t\t\tswitch(type)\n\t\t\t{\n\t\t\tcase ev_string:\n\t\t\t\t{\n\t\t\t\t\tchar *in = &strings[val->string], *out=message;\n\t\t\t\t\tchar *end = out+sizeof(message)-3;\n\t\t\t\t\t*out++ = '\\\"';\n\t\t\t\t\tfor(; out < end && *in; in++)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (*in == '\\n')\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t*out++ = '\\\\';\n\t\t\t\t\t\t\t*out++ = 'n';\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (*in == '\\t')\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t*out++ = '\\\\';\n\t\t\t\t\t\t\t*out++ = 't';\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (*in == '\\r')\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t*out++ = '\\\\';\n\t\t\t\t\t\t\t*out++ = 'r';\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (*in == '\\\"')\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t*out++ = '\\\\';\n\t\t\t\t\t\t\t*out++ = '\"';\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (*in == '\\'')\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t*out++ = '\\\\';\n\t\t\t\t\t\t\t*out++ = '\\'';\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\t*out++ = *in;\n\t\t\t\t\t}\n\t\t\t\t\t*out++ = '\\\"';\n\t\t\t\t\t*out++ = 0;\n\t\t\t\t}\n\t\t\t\treturn message;\n\t\t\tcase ev_function:\n\t\t\t\tif (val->_int>0 && val->_int < numfunctions && *functions[val->_int].name)\n\t\t\t\t\tQC_snprintfz(message, sizeof(message), \"%s\", functions[val->_int].name);\n\t\t\t\telse\n\t\t\t\t\tQC_snprintfz(message, sizeof(message), \"%\"pPRIi\"i\", val->_int);\n\t\t\t\treturn message;\n\t\t\tcase ev_field:\n\t\t\tcase ev_integer:\n\t\t\t\tQC_snprintfz(message, sizeof(message), \"%#\"pPRIx\"i\", val->_int);\n\t\t\t\treturn message;\n\t\t\tcase ev_uint:\n\t\t\t\tQC_snprintfz(message, sizeof(message), \"%#\"pPRIx\"u\", val->_uint);\n\t\t\t\treturn message;\n\t\t\tcase ev_int64:\n\t\t\t\t//QC_snprintfz(message, sizeof(message), \"%\"pPRIu64\"ill\", val->i64);\n\t\t\t\tQC_snprintfz(message, sizeof(message), \"%#\"pPRIx64\"ill\", val->i64);\n\t\t\t\treturn message;\n\t\t\tcase ev_uint64:\n\t\t\t\t//QC_snprintfz(message, sizeof(message), \"%\"pPRIu64\"ull\", val->u64);\n\t\t\t\tQC_snprintfz(message, sizeof(message), \"%#\"pPRIx64\"ull\", val->u64);\n\t\t\t\treturn message;\n\t\t\tcase ev_entity:\n\t\t\t\tQC_snprintfz(message, sizeof(message), \"%\"pPRIi\"e\", val->_int);\n\t\t\t\treturn message;\n\t\t\tcase ev_float:\n\t\t\t\tif (!val->_float || val->_int & 0x7f800000)\n\t\t\t\t\tQC_snprintfz(message, sizeof(message), \"%gf\", val->_float);\n\t\t\t\telse\n\t\t\t\t\tQC_snprintfz(message, sizeof(message), \"%%%\"pPRIi, val->_int);\n\t\t\t\treturn message;\n\t\t\tcase ev_double:\n\t\t\t\tQC_snprintfz(message, sizeof(message), \"%gd\", val->_double);\n\t\t\t\treturn message;\n\t\t\tcase ev_vector:\n\t\t\t\tQC_snprintfz(message, sizeof(message), \"'%g %g %g'\", val->vector[0], val->vector[1], val->vector[2]);\n\t\t\t\treturn message;\n\t\t\tdefault:\n\t\t\t\tif (!ref.ofs)\n\t\t\t\t\tQC_snprintfz(message, sizeof(message), \"IMMEDIATE\");\n\t\t\t\telse\n\t\t\t\t\tQC_snprintfz(message, sizeof(message), \"IMMEDIATE+%i\", ref.ofs);\n\t\t\t\treturn message;\n\t\t\t}\n\t\t}\n\t\telse if (ref.ofs || ref.cast != ref.sym->type)\n\t\t{\n\t\t\tif (!ref.ofs)\n\t\t\t\tQC_snprintfz(message, sizeof(message), \"%s\", ref.sym->name);\n\t\t\telse\n\t\t\t\tQC_snprintfz(message, sizeof(message), \"%s+%i\", ref.sym->name, ref.ofs);\n\t\t\treturn message;\n\t\t}\n\n\t\treturn ref.sym->name;\n\t}\n\n\tQC_snprintfz(message, sizeof(message), \"offset_%i\", ref.ofs);\n\treturn message;\n}\n#endif\n\n#if IAMNOTLAZY\n//need_lock is set if it crossed a function call.\nstatic int QCC_PR_FindSourceForTemp(QCC_def_t *tempdef, int op, pbool *need_lock)\n{\n\tint st = -1;\n\t*need_lock = false;\n\tif (tempdef->temp)\n\t{\n\t\tfor (st = numstatements-1; st>=0; st--)\n\t\t{\n\t\t\tif (statements[st].c == tempdef->ofs)\n\t\t\t{\n\t\t\t\tif (statements[st].op == op)\n\t\t\t\t\treturn st;\n\t\t\t\treturn -1;\n\t\t\t}\n\n\t\t\tif ((statements[st].op >= OP_CALL0 && statements[st].op <= OP_CALL8) || (statements[st].op >= OP_CALL1H && statements[st].op <= OP_CALL8H))\n\t\t\t\t*need_lock = true;\n\t\t}\n\t}\n\treturn st;\n}\n#endif\n\nstatic int QCC_PR_FindSourceForAssignedOffset(QCC_def_t *sym, int firstst)\n{\n\tint st = -1;\n\tfor (st = numstatements-1; st>=firstst; st--)\n\t{\n\t\tif (statements[st].c.sym == sym && OpAssignsToC(statements[st].op))\n\t\t\treturn st;\n\t\tif (statements[st].b.sym == sym && OpAssignsToB(statements[st].op))\n\t\t\treturn st;\n\t}\n\treturn -1;\n}\n\npbool QCC_Temp_Describe(QCC_def_t *def, char *buffer, int buffersize)\n{\n\tQCC_statement_t\t*s;\n\tint st;\n\ttemp_t *t = def->temp;\n\tif (!t)\n\t\treturn false;\n\tif (t->lastfunc != pr_scope)\n\t\treturn false;\n\tst = QCC_PR_FindSourceForAssignedOffset(t->def, t->laststatement);\n\tif (st == -1)\n\t\treturn false;\n\ts = &statements[st];\n\tswitch(s->op)\n\t{\n\tdefault:\n\t\tQC_snprintfz(buffer, buffersize, \"%s %s %s\", QCC_VarAtOffset(s->a), pr_opcodes[s->op].name, QCC_VarAtOffset(s->b));\n\t\tbreak;\n\t}\n\treturn true;\n}\n\nstatic int QCC_PR_RoundFloatConst(const QCC_eval_t *eval)\n{\n\tfloat val = eval->_float;\n\tint ival = val;\n\tif (val != (float)ival)\n\t\tQCC_PR_ParseWarning(WARN_OVERFLOW, \"Constant float operand %f will be truncated to %i\", val, ival);\n\treturn ival;\n}\n\nQCC_statement_t *QCC_PR_SimpleStatement ( QCC_opcode_t *op, QCC_sref_t var_a, QCC_sref_t var_b, QCC_sref_t var_c, int force);\n\nQCC_sref_t QCC_PR_StatementFlags ( QCC_opcode_t *op, QCC_sref_t var_a, QCC_sref_t var_b, QCC_statement_t **outstatement, unsigned int flags)\n{\n\tchar typea[256], typeb[256];\n\tQCC_statement_t\t*statement;\n\tQCC_sref_t\t\t\tvar_c=nullsref;\n\tpbool nan_eq_cond, sym_cmp;\n\n\tif (var_a.sym)\n\t{\n\t\tvar_a.sym->referenced = true;\n\t\tif (flags&STFL_PRESERVEA)\n\t\t\tQCC_UnFreeTemp(var_a);\n\t}\n\tif (var_b.sym)\n\t{\n\t\tvar_b.sym->referenced = true;\n\t\tif (flags&STFL_PRESERVEB)\n\t\t\tQCC_UnFreeTemp(var_b);\n\t}\n\n/*\tif (op->priority != -1 && op->priority != CONDITION_PRIORITY)\n\t{\n\t\tif (op->associative!=ASSOC_LEFT)\n\t\t{\n\t\t\tif (op->type_a != &type_pointer && (flags&STFL_CONVERTB))\n\t\t\t\tvar_b = QCC_SupplyConversion(var_b, (*op->type_a)->type, false);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (var_a.cast && (flags&STFL_CONVERTA))\n\t\t\t\tvar_a = QCC_SupplyConversion(var_a, (*op->type_a)->type, false);\n\t\t\tif (var_b.cast && (flags&STFL_CONVERTB))\n\t\t\t\tvar_b = QCC_SupplyConversion(var_b, (*op->type_b)->type, false);\n\t\t}\n\t}\n*/\n\t//maths operators\n\tif (opt_constantarithmatic || !pr_scope)\n\t{\n\t\tconst QCC_eval_t *eval_a = QCC_SRef_EvalConst(var_a);\n\t\tconst QCC_eval_t *eval_b = QCC_SRef_EvalConst(var_b);\n\t\tif (eval_a)\n\t\t{\n\t\t\tif (outstatement)\n\t\t\t\t*outstatement = NULL;\n\t\t\tif (eval_b)\n\t\t\t{\n\t\t\t\t//both are constants\n\t\t\t\tswitch (op - pr_opcodes)\t//improve some of the maths.\n\t\t\t\t{\n//\t\t\t\tcase OP_GLOBALADDRESS:\n//\t\t\t\t\tif (flag_pointerrelocs)\n//\t\t\t\t\t\treturn QCC_MakeGAddress(type_pointer, var_a.sym, var_a.ofs + QCC_Eval_Int(QCC_SRef_EvalConst(var_b), var_b.cast));\n//\t\t\t\t\tbreak;\n\t\t\t\tcase OP_AND_ANY:\n//\t\t\t\tcase OP_AND_F:\n\t\t\t\tcase OP_AND_FI:\n//\t\t\t\tcase OP_AND_I:\n\t\t\t\tcase OP_AND_IF:\n\t\t\t\t\t/*if (flag_pythonlogic)\n\t\t\t\t\t{\t//c=(a?b:a);\n\t\t\t\t\t\toptres_constantarithmatic++;\n\t\t\t\t\t\tif (QCC_Eval_Truth(eval_a, var_a.cast))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tQCC_FreeTemp(var_a);\n\t\t\t\t\t\t\treturn var_b;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tQCC_FreeTemp(var_b);\n\t\t\t\t\t\t\treturn var_a;\n\t\t\t\t\t\t}\n\t\t\t\t\t}*/\n\t\t\t\t\toptres_constantarithmatic++;\n\t\t\t\t\tQCC_FreeTemp(var_a);\n\t\t\t\t\tQCC_FreeTemp(var_b);\n\t\t\t\t\tif (QCC_Eval_Truth(eval_a, var_a.cast, false) && QCC_Eval_Truth(eval_b, var_b.cast, false))\n\t\t\t\t\t\treturn flag_assume_integer?QCC_MakeIntConst(1):QCC_MakeFloatConst(1);\n\t\t\t\t\telse\n\t\t\t\t\t\treturn flag_assume_integer?QCC_MakeIntConst(0):QCC_MakeFloatConst(0);\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase OP_OR_ANY:\n//\t\t\t\tcase OP_OR_F:\n\t\t\t\tcase OP_OR_FI:\n//\t\t\t\tcase OP_OR_I:\n\t\t\t\tcase OP_OR_IF:\n\t\t\t\t\t/*if (flag_pythonlogic)\n\t\t\t\t\t{\t//c=(a?a:b);\n\t\t\t\t\t\toptres_constantarithmatic++;\n\t\t\t\t\t\tif (QCC_Eval_Truth(eval_a, var_a.cast))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tQCC_FreeTemp(var_b);\n\t\t\t\t\t\t\treturn var_a;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tQCC_FreeTemp(var_a);\n\t\t\t\t\t\t\treturn var_b;\n\t\t\t\t\t\t}\n\t\t\t\t\t}*/\n\t\t\t\t\toptres_constantarithmatic++;\n\t\t\t\t\tQCC_FreeTemp(var_a);\n\t\t\t\t\tQCC_FreeTemp(var_b);\n\t\t\t\t\tif (QCC_Eval_Truth(eval_a, var_a.cast, false) || QCC_Eval_Truth(eval_b, var_b.cast, false))\n\t\t\t\t\t\treturn flag_assume_integer?QCC_MakeIntConst(1):QCC_MakeFloatConst(1);\n\t\t\t\t\telse\n\t\t\t\t\t\treturn flag_assume_integer?QCC_MakeIntConst(0):QCC_MakeFloatConst(0);\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase OP_LOADA_F:\n\t\t\t\tcase OP_LOADA_V:\n\t\t\t\tcase OP_LOADA_S:\n\t\t\t\tcase OP_LOADA_ENT:\n\t\t\t\tcase OP_LOADA_FLD:\n\t\t\t\tcase OP_LOADA_FNC:\n\t\t\t\tcase OP_LOADA_I:\n\t\t\t\t\t{\n\t\t\t\t\t\tQCC_sref_t nd = var_a;\n\t\t\t\t\t\tQCC_FreeTemp(var_b);\n\t\t\t\t\t\tnd.ofs += eval_b->_int;\n\t\t\t\t\t\t//FIXME: case away the array...\n\t\t\t\t\t\treturn nd;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase OP_BITXOR_I:\n\t\t\t\t\tQCC_FreeTemp(var_a); QCC_FreeTemp(var_b);\n\t\t\t\t\toptres_constantarithmatic++;\n\t\t\t\t\treturn QCC_MakeIntConst(eval_a->_int ^ eval_b->_int);\n\t\t\t\tcase OP_RSHIFT_I:\n\t\t\t\t\tQCC_FreeTemp(var_a); QCC_FreeTemp(var_b);\n\t\t\t\t\toptres_constantarithmatic++;\n\t\t\t\t\treturn QCC_MakeIntConst(eval_a->_int >> eval_b->_int);\n\t\t\t\tcase OP_LSHIFT_I:\n\t\t\t\t\tQCC_FreeTemp(var_a); QCC_FreeTemp(var_b);\n\t\t\t\t\toptres_constantarithmatic++;\n\t\t\t\t\treturn QCC_MakeIntConst(eval_a->_int << eval_b->_int);\n\t\t\t\tcase OP_BITXOR_F:\n\t\t\t\t\tQCC_FreeTemp(var_a); QCC_FreeTemp(var_b);\n\t\t\t\t\toptres_constantarithmatic++;\n\t\t\t\t\treturn QCC_MakeFloatConst(QCC_PR_RoundFloatConst(eval_a) ^ QCC_PR_RoundFloatConst(eval_b));\n\t\t\t\tcase OP_BITXOR_V:\n\t\t\t\t\tQCC_FreeTemp(var_a); QCC_FreeTemp(var_b);\n\t\t\t\t\toptres_constantarithmatic++;\n\t\t\t\t\treturn QCC_MakeVectorConst(\n\t\t\t\t\t\t(int)eval_a->vector[0] ^ (int)eval_b->vector[0],\n\t\t\t\t\t\t(int)eval_a->vector[1] ^ (int)eval_b->vector[1],\n\t\t\t\t\t\t(int)eval_a->vector[2] ^ (int)eval_b->vector[2]);\n\t\t\t\tcase OP_RSHIFT_F:\n\t\t\t\t\tQCC_FreeTemp(var_a); QCC_FreeTemp(var_b);\n\t\t\t\t\toptres_constantarithmatic++;\n\t\t\t\t\treturn QCC_MakeFloatConst(QCC_PR_RoundFloatConst(eval_a) >> QCC_PR_RoundFloatConst(eval_b));\n\t\t\t\tcase OP_RSHIFT_IF:\n\t\t\t\t\tQCC_FreeTemp(var_a); QCC_FreeTemp(var_b);\n\t\t\t\t\toptres_constantarithmatic++;\n\t\t\t\t\treturn QCC_MakeIntConst(eval_a->_int >> QCC_PR_RoundFloatConst(eval_b));\n\t\t\t\tcase OP_RSHIFT_FI:\n\t\t\t\t\tQCC_FreeTemp(var_a); QCC_FreeTemp(var_b);\n\t\t\t\t\toptres_constantarithmatic++;\n\t\t\t\t\treturn QCC_MakeIntConst(QCC_PR_RoundFloatConst(eval_a) >> eval_b->_int);\n\t\t\t\tcase OP_LSHIFT_F:\n\t\t\t\t\tQCC_FreeTemp(var_a); QCC_FreeTemp(var_b);\n\t\t\t\t\toptres_constantarithmatic++;\n\t\t\t\t\treturn QCC_MakeFloatConst(QCC_PR_RoundFloatConst(eval_a) << QCC_PR_RoundFloatConst(eval_b));\n\t\t\t\tcase OP_LSHIFT_IF:\n\t\t\t\t\tQCC_FreeTemp(var_a); QCC_FreeTemp(var_b);\n\t\t\t\t\toptres_constantarithmatic++;\n\t\t\t\t\treturn QCC_MakeIntConst(eval_a->_int << QCC_PR_RoundFloatConst(eval_b));\n\t\t\t\tcase OP_LSHIFT_FI:\n\t\t\t\t\tQCC_FreeTemp(var_a); QCC_FreeTemp(var_b);\n\t\t\t\t\toptres_constantarithmatic++;\n\t\t\t\t\treturn QCC_MakeIntConst(QCC_PR_RoundFloatConst(eval_a) << eval_b->_int);\n\t\t\t\tcase OP_BITOR_F:\n\t\t\t\t\tQCC_FreeTemp(var_a); QCC_FreeTemp(var_b);\n\t\t\t\t\toptres_constantarithmatic++;\n\t\t\t\t\treturn QCC_MakeFloatConst(QCC_PR_RoundFloatConst(eval_a) | QCC_PR_RoundFloatConst(eval_b));\n\t\t\t\tcase OP_BITOR_V:\n\t\t\t\t\tQCC_FreeTemp(var_a); QCC_FreeTemp(var_b);\n\t\t\t\t\toptres_constantarithmatic++;\n\t\t\t\t\treturn QCC_MakeVectorConst(\n\t\t\t\t\t\t(int)eval_a->vector[0] | (int)eval_b->vector[0],\n\t\t\t\t\t\t(int)eval_a->vector[1] | (int)eval_b->vector[1],\n\t\t\t\t\t\t(int)eval_a->vector[2] | (int)eval_b->vector[2]);\n\t\t\t\tcase OP_BITAND_F:\n\t\t\t\t\tQCC_FreeTemp(var_a); QCC_FreeTemp(var_b);\n\t\t\t\t\toptres_constantarithmatic++;\n\t\t\t\t\treturn QCC_MakeFloatConst(QCC_PR_RoundFloatConst(eval_a) & QCC_PR_RoundFloatConst(eval_b));\n\t\t\t\tcase OP_BITAND_V:\n\t\t\t\t\tQCC_FreeTemp(var_a); QCC_FreeTemp(var_b);\n\t\t\t\t\toptres_constantarithmatic++;\n\t\t\t\t\treturn QCC_MakeVectorConst(\n\t\t\t\t\t\t(int)eval_a->vector[0] & (int)eval_b->vector[0],\n\t\t\t\t\t\t(int)eval_a->vector[1] & (int)eval_b->vector[1],\n\t\t\t\t\t\t(int)eval_a->vector[2] & (int)eval_b->vector[2]);\n\t\t\t\tcase OP_MUL_F:\n\t\t\t\t\tQCC_FreeTemp(var_a); QCC_FreeTemp(var_b);\n\t\t\t\t\toptres_constantarithmatic++;\n\t\t\t\t\treturn QCC_MakeFloatConst(eval_a->_float * eval_b->_float);\n\t\t\t\tcase OP_DIV_F:\n\t\t\t\t\tQCC_FreeTemp(var_a); QCC_FreeTemp(var_b);\n\t\t\t\t\toptres_constantarithmatic++;\n\t\t\t\t\tif (!eval_b->_float)\n\t\t\t\t\t\tQCC_PR_ParseWarning(WARN_DIVISIONBY0, \"Division of %g by 0\\n\", eval_a->_float);\n\t\t\t\t\treturn QCC_MakeFloatConst(eval_a->_float / eval_b->_float);\n\t\t\t\tcase OP_ADD_F:\n\t\t\t\t\tQCC_FreeTemp(var_a); QCC_FreeTemp(var_b);\n\t\t\t\t\toptres_constantarithmatic++;\n\t\t\t\t\treturn QCC_MakeFloatConst(eval_a->_float + eval_b->_float);\n\t\t\t\tcase OP_SUB_F:\n\t\t\t\t\tQCC_FreeTemp(var_a); QCC_FreeTemp(var_b);\n\t\t\t\t\toptres_constantarithmatic++;\n\t\t\t\t\treturn QCC_MakeFloatConst(eval_a->_float - eval_b->_float);\n\n\t\t\t\tcase OP_BITOR_I:\n\t\t\t\t\tQCC_FreeTemp(var_a); QCC_FreeTemp(var_b);\n\t\t\t\t\toptres_constantarithmatic++;\n\t\t\t\t\treturn QCC_MakeIntConst(eval_a->_int | eval_b->_int);\n\t\t\t\tcase OP_BITAND_I:\n\t\t\t\t\tQCC_FreeTemp(var_a); QCC_FreeTemp(var_b);\n\t\t\t\t\toptres_constantarithmatic++;\n\t\t\t\t\treturn QCC_MakeIntConst(eval_a->_int & eval_b->_int);\n\t\t\t\tcase OP_MUL_I:\n\t\t\t\t\tQCC_FreeTemp(var_a); QCC_FreeTemp(var_b);\n\t\t\t\t\toptres_constantarithmatic++;\n\t\t\t\t\treturn QCC_MakeIntConst(eval_a->_int * eval_b->_int);\n\t\t\t\tcase OP_MUL_IF:\n\t\t\t\t\tQCC_FreeTemp(var_a); QCC_FreeTemp(var_b);\n\t\t\t\t\toptres_constantarithmatic++;\n\t\t\t\t\treturn QCC_MakeFloatConst(eval_a->_int * eval_b->_float);\n\t\t\t\tcase OP_MUL_FI:\n\t\t\t\t\tQCC_FreeTemp(var_a); QCC_FreeTemp(var_b);\n\t\t\t\t\toptres_constantarithmatic++;\n\t\t\t\t\treturn QCC_MakeFloatConst(eval_a->_float * eval_b->_int);\n\t\t\t\tcase OP_DIV_I:\n\t\t\t\t\tQCC_FreeTemp(var_a); QCC_FreeTemp(var_b);\n\t\t\t\t\toptres_constantarithmatic++;\n\t\t\t\t\tif (eval_b->_int == 0)\n\t\t\t\t\t{\n\t\t\t\t\t\tQCC_PR_ParseWarning(WARN_DIVISIONBY0, \"Division by constant 0\");\n\t\t\t\t\t\treturn QCC_MakeIntConst(0);\n\t\t\t\t\t}\n\t\t\t\t\telse if (eval_b->_int == -1 && eval_a->_int == (int)0x80000000)\n\t\t\t\t\t{\n\t\t\t\t\t\tQCC_PR_ParseWarning(WARN_DIVISIONBY0, \"Signed overflow on division by -1\");\n\t\t\t\t\t\treturn QCC_MakeIntConst(0x7fffffff);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\treturn QCC_MakeIntConst(eval_a->_int / eval_b->_int);\n\t\t\t\tcase OP_ADD_I:\n\t\t\t\t\tQCC_FreeTemp(var_a); QCC_FreeTemp(var_b);\n\t\t\t\t\toptres_constantarithmatic++;\n\t\t\t\t\treturn QCC_MakeIntConst(eval_a->_int + eval_b->_int);\n\t\t\t\tcase OP_SUB_I:\n\t\t\t\t\tQCC_FreeTemp(var_a); QCC_FreeTemp(var_b);\n\t\t\t\t\toptres_constantarithmatic++;\n\t\t\t\t\treturn QCC_MakeIntConst(eval_a->_int - eval_b->_int);\n\n\t\t\t\tcase OP_MOD_I:\n\t\t\t\t\tif (!eval_b->_int)\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tQCC_FreeTemp(var_a); QCC_FreeTemp(var_b);\n\t\t\t\t\toptres_constantarithmatic++;\n\t\t\t\t\treturn QCC_MakeIntConst(eval_a->_int % eval_b->_int);\n\t\t\t\tcase OP_MOD_U:\n\t\t\t\t\tif (!eval_b->_uint)\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tQCC_FreeTemp(var_a); QCC_FreeTemp(var_b);\n\t\t\t\t\toptres_constantarithmatic++;\n\t\t\t\t\treturn QCC_MakeUIntConst(eval_a->_uint % eval_b->_uint);\n\t\t\t\tcase OP_MOD_F:\n\t\t\t\t\t{\n\t\t\t\t\t\tfloat a = eval_a->_float,n=eval_b->_float;\n\t\t\t\t\t\tif (!n)\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tQCC_FreeTemp(var_a); QCC_FreeTemp(var_b);\n\t\t\t\t\t\toptres_constantarithmatic++;\n\t\t\t\t\t\treturn QCC_MakeFloatConst(a - (n * (int)(a/n)));\n\t\t\t\t\t}\n\n\t\t\t\tcase OP_ADD_IF:\n\t\t\t\t\tQCC_FreeTemp(var_a); QCC_FreeTemp(var_b);\n\t\t\t\t\toptres_constantarithmatic++;\n\t\t\t\t\treturn QCC_MakeFloatConst(eval_a->_int + eval_b->_float);\n\t\t\t\tcase OP_ADD_FI:\n\t\t\t\t\tQCC_FreeTemp(var_a); QCC_FreeTemp(var_b);\n\t\t\t\t\toptres_constantarithmatic++;\n\t\t\t\t\treturn QCC_MakeFloatConst(eval_a->_float + eval_b->_int);\n\n\t\t\t\tcase OP_ADD_PIW:\n\t\t\t\t\tif (flag_undefwordsize)\n\t\t\t\t\t\tbreak;\t//don't make assumptions.\n\t\t\t\t\tQCC_FreeTemp(var_a); QCC_FreeTemp(var_b);\n\t\t\t\t\toptres_constantarithmatic++;\n\t\t\t\t\treturn QCC_MakeIntConst(eval_a->_int + eval_b->_int*VMWORDSIZE);\n\n\t\t\t\tcase OP_SUB_IF:\n\t\t\t\t\tQCC_FreeTemp(var_a); QCC_FreeTemp(var_b);\n\t\t\t\t\toptres_constantarithmatic++;\n\t\t\t\t\treturn QCC_MakeFloatConst(eval_a->_int - eval_b->_float);\n\t\t\t\tcase OP_SUB_FI:\n\t\t\t\t\tQCC_FreeTemp(var_a); QCC_FreeTemp(var_b);\n\t\t\t\t\toptres_constantarithmatic++;\n\t\t\t\t\treturn QCC_MakeFloatConst(eval_a->_float - eval_b->_int);\n\n\t\t\t\tcase OP_AND_I:\n\t\t\t\t\tQCC_FreeTemp(var_a); QCC_FreeTemp(var_b);\n\t\t\t\t\toptres_constantarithmatic++;\n\t\t\t\t\treturn QCC_MakeIntConst(eval_a->_int && eval_b->_int);\n\t\t\t\tcase OP_OR_I:\n\t\t\t\t\tQCC_FreeTemp(var_a); QCC_FreeTemp(var_b);\n\t\t\t\t\toptres_constantarithmatic++;\n\t\t\t\t\treturn QCC_MakeIntConst(eval_a->_int || eval_b->_int);\n\t\t\t\tcase OP_AND_F:\n\t\t\t\t\tQCC_FreeTemp(var_a); QCC_FreeTemp(var_b);\n\t\t\t\t\toptres_constantarithmatic++;\n\t\t\t\t\treturn QCC_MakeIntConst(eval_a->_float && eval_b->_float);\n\t\t\t\tcase OP_OR_F:\n\t\t\t\t\tQCC_FreeTemp(var_a); QCC_FreeTemp(var_b);\n\t\t\t\t\toptres_constantarithmatic++;\n\t\t\t\t\treturn QCC_MakeIntConst(eval_a->_float || eval_b->_float);\n\t\t\t\tcase OP_MUL_V:\t//mul_v is actually a dot-product\n\t\t\t\t\tQCC_FreeTemp(var_a); QCC_FreeTemp(var_b);\n\t\t\t\t\toptres_constantarithmatic++;\n\t\t\t\t\treturn QCC_MakeFloatConst(\teval_a->vector[0] * eval_b->vector[0] +\n\t\t\t\t\t\t\t\t\t\t\t\teval_a->vector[1] * eval_b->vector[1] +\n\t\t\t\t\t\t\t\t\t\t\t\teval_a->vector[2] * eval_b->vector[2]);\n\t\t\t\tcase OP_MUL_FV:\n\t\t\t\t\tQCC_FreeTemp(var_a); QCC_FreeTemp(var_b);\n\t\t\t\t\toptres_constantarithmatic++;\n\t\t\t\t\treturn QCC_MakeVectorConst(\teval_a->_float * eval_b->vector[0],\n\t\t\t\t\t\t\t\t\t\t\t\teval_a->_float * eval_b->vector[1],\n\t\t\t\t\t\t\t\t\t\t\t\teval_a->_float * eval_b->vector[2]);\n\t\t\t\tcase OP_MUL_VF:\n\t\t\t\t\tQCC_FreeTemp(var_a); QCC_FreeTemp(var_b);\n\t\t\t\t\toptres_constantarithmatic++;\n\t\t\t\t\treturn QCC_MakeVectorConst(\teval_a->vector[0] * eval_b->_float,\n\t\t\t\t\t\t\t\t\t\t\t\teval_a->vector[1] * eval_b->_float,\n\t\t\t\t\t\t\t\t\t\t\t\teval_a->vector[2] * eval_b->_float);\n\t\t\t\tcase OP_ADD_V:\n\t\t\t\t\tQCC_FreeTemp(var_a); QCC_FreeTemp(var_b);\n\t\t\t\t\toptres_constantarithmatic++;\n\t\t\t\t\treturn QCC_MakeVectorConst(\teval_a->vector[0] + eval_b->vector[0],\n\t\t\t\t\t\t\t\t\t\t\t\teval_a->vector[1] + eval_b->vector[1],\n\t\t\t\t\t\t\t\t\t\t\t\teval_a->vector[2] + eval_b->vector[2]);\n\t\t\t\tcase OP_SUB_V:\n\t\t\t\t\tQCC_FreeTemp(var_a); QCC_FreeTemp(var_b);\n\t\t\t\t\toptres_constantarithmatic++;\n\t\t\t\t\treturn QCC_MakeVectorConst(\teval_a->vector[0] - eval_b->vector[0],\n\t\t\t\t\t\t\t\t\t\t\t\teval_a->vector[1] - eval_b->vector[1],\n\t\t\t\t\t\t\t\t\t\t\t\teval_a->vector[2] - eval_b->vector[2]);\n\n\n\t\t\t\t\t\n\t\t\t\tcase OP_LE_I:\n\t\t\t\t\tQCC_FreeTemp(var_a); QCC_FreeTemp(var_b);\n\t\t\t\t\treturn QCC_MakeIntConst(eval_a->_int <= eval_b->_int);\n\t\t\t\tcase OP_GE_I:\n\t\t\t\t\tQCC_FreeTemp(var_a); QCC_FreeTemp(var_b);\n\t\t\t\t\treturn QCC_MakeIntConst(eval_a->_int >= eval_b->_int);\n\t\t\t\tcase OP_LT_I:\n\t\t\t\t\tQCC_FreeTemp(var_a); QCC_FreeTemp(var_b);\n\t\t\t\t\treturn QCC_MakeIntConst(eval_a->_int < eval_b->_int);\n\t\t\t\tcase OP_GT_I:\n\t\t\t\t\tQCC_FreeTemp(var_a); QCC_FreeTemp(var_b);\n\t\t\t\t\treturn QCC_MakeIntConst(eval_a->_int > eval_b->_int);\n\n\t\t\t\tcase OP_LE_IF:\n\t\t\t\t\tQCC_FreeTemp(var_a); QCC_FreeTemp(var_b);\n\t\t\t\t\treturn QCC_MakeIntConst(eval_a->_int <= eval_b->_float);\n\t\t\t\tcase OP_GE_IF:\n\t\t\t\t\tQCC_FreeTemp(var_a); QCC_FreeTemp(var_b);\n\t\t\t\t\treturn QCC_MakeIntConst(eval_a->_int >= eval_b->_float);\n\t\t\t\tcase OP_LT_IF:\n\t\t\t\t\tQCC_FreeTemp(var_a); QCC_FreeTemp(var_b);\n\t\t\t\t\treturn QCC_MakeIntConst(eval_a->_int < eval_b->_float);\n\t\t\t\tcase OP_GT_IF:\n\t\t\t\t\tQCC_FreeTemp(var_a); QCC_FreeTemp(var_b);\n\t\t\t\t\treturn QCC_MakeIntConst(eval_a->_int > eval_b->_float);\n\n\t\t\t\tcase OP_LE_FI:\n\t\t\t\t\tQCC_FreeTemp(var_a); QCC_FreeTemp(var_b);\n\t\t\t\t\treturn QCC_MakeIntConst(eval_a->_float <= eval_b->_int);\n\t\t\t\tcase OP_GE_FI:\n\t\t\t\t\tQCC_FreeTemp(var_a); QCC_FreeTemp(var_b);\n\t\t\t\t\treturn QCC_MakeIntConst(eval_a->_float >= eval_b->_int);\n\t\t\t\tcase OP_LT_FI:\n\t\t\t\t\tQCC_FreeTemp(var_a); QCC_FreeTemp(var_b);\n\t\t\t\t\treturn QCC_MakeIntConst(eval_a->_float < eval_b->_int);\n\t\t\t\tcase OP_GT_FI:\n\t\t\t\t\tQCC_FreeTemp(var_a); QCC_FreeTemp(var_b);\n\t\t\t\t\treturn QCC_MakeIntConst(eval_a->_float > eval_b->_int);\n\n\t\t\t\tcase OP_EQ_F:\n\t\t\t\t\tQCC_FreeTemp(var_a); QCC_FreeTemp(var_b);\n\t\t\t\t\treturn QCC_MakeFloatConst(eval_a->_float == eval_b->_float);\n\t\t\t\tcase OP_EQ_I:\n\t\t\t\t\tQCC_FreeTemp(var_a); QCC_FreeTemp(var_b);\n\t\t\t\t\treturn QCC_MakeIntConst(eval_a->_int == eval_b->_int);\n\t\t\t\tcase OP_EQ_IF:\n\t\t\t\t\tQCC_FreeTemp(var_a); QCC_FreeTemp(var_b);\n\t\t\t\t\treturn QCC_MakeIntConst(eval_a->_int == eval_b->_float);\n\t\t\t\tcase OP_EQ_FI:\n\t\t\t\t\tQCC_FreeTemp(var_a); QCC_FreeTemp(var_b);\n\t\t\t\t\treturn QCC_MakeIntConst(eval_a->_float == eval_b->_int);\n\n\t\t\t\tcase OP_MUL_VI:\n\t\t\t\t\tQCC_FreeTemp(var_a); QCC_FreeTemp(var_b);\n\t\t\t\t\treturn QCC_MakeVectorConst(\teval_a->vector[0] * eval_b->_int,\n\t\t\t\t\t\t\t\t\t\t\t\teval_a->vector[1] * eval_b->_int,\n\t\t\t\t\t\t\t\t\t\t\t\teval_a->vector[2] * eval_b->_int);\n\t\t\t\tcase OP_MUL_IV:\n\t\t\t\t\tQCC_FreeTemp(var_a); QCC_FreeTemp(var_b);\n\t\t\t\t\treturn QCC_MakeVectorConst(\teval_a->_int * eval_b->vector[0],\n\t\t\t\t\t\t\t\t\t\t\t\teval_a->_int * eval_b->vector[1],\n\t\t\t\t\t\t\t\t\t\t\t\teval_a->_int * eval_b->vector[2]);\n\t\t\t\tcase OP_DIV_IF:\n\t\t\t\t\tQCC_FreeTemp(var_a); QCC_FreeTemp(var_b);\n\t\t\t\t\tif (!eval_b->_float)\n\t\t\t\t\t\tQCC_PR_ParseWarning(WARN_DIVISIONBY0, \"Division of %d by 0\\n\", eval_a->_int);\n\t\t\t\t\treturn QCC_MakeFloatConst(eval_a->_int / eval_b->_float);\n\t\t\t\tcase OP_DIV_FI:\n\t\t\t\t\tQCC_FreeTemp(var_a); QCC_FreeTemp(var_b);\n\t\t\t\t\tif (!eval_b->_int)\n\t\t\t\t\t\tQCC_PR_ParseWarning(WARN_DIVISIONBY0, \"Division of %g by 0\\n\", eval_a->_float);\n\t\t\t\t\treturn QCC_MakeFloatConst(eval_a->_float / eval_b->_int);\n\t\t\t\tcase OP_BITAND_IF:\n\t\t\t\t\tQCC_FreeTemp(var_a); QCC_FreeTemp(var_b);\n\t\t\t\t\treturn QCC_MakeIntConst(eval_a->_int & QCC_PR_RoundFloatConst(eval_b));\n\t\t\t\tcase OP_BITOR_IF:\n\t\t\t\t\tQCC_FreeTemp(var_a); QCC_FreeTemp(var_b);\n\t\t\t\t\treturn QCC_MakeIntConst(eval_a->_int | QCC_PR_RoundFloatConst(eval_b));\n\t\t\t\tcase OP_BITAND_FI:\n\t\t\t\t\tQCC_FreeTemp(var_a); QCC_FreeTemp(var_b);\n\t\t\t\t\treturn QCC_MakeIntConst(QCC_PR_RoundFloatConst(eval_a) & eval_b->_int);\n\t\t\t\tcase OP_BITOR_FI:\n\t\t\t\t\tQCC_FreeTemp(var_a); QCC_FreeTemp(var_b);\n\t\t\t\t\treturn QCC_MakeIntConst(QCC_PR_RoundFloatConst(eval_a) | eval_b->_int);\n\t\t\t\tcase OP_NE_F:\n\t\t\t\t\tQCC_FreeTemp(var_a); QCC_FreeTemp(var_b);\n\t\t\t\t\treturn QCC_MakeFloatConst(eval_a->_float != eval_b->_float);\n\t\t\t\tcase OP_NE_I:\n\t\t\t\t\tQCC_FreeTemp(var_a); QCC_FreeTemp(var_b);\n\t\t\t\t\treturn QCC_MakeIntConst(eval_a->_int != eval_b->_int);\n\t\t\t\tcase OP_NE_IF:\n\t\t\t\t\tQCC_FreeTemp(var_a); QCC_FreeTemp(var_b);\n\t\t\t\t\treturn QCC_MakeIntConst(eval_a->_int != eval_b->_float);\n\t\t\t\tcase OP_NE_FI:\n\t\t\t\t\tQCC_FreeTemp(var_a); QCC_FreeTemp(var_b);\n\t\t\t\t\treturn QCC_MakeIntConst(eval_a->_float != eval_b->_int);\n\n\n\t\t\t\tcase OP_LT_U:\n\t\t\t\t\tQCC_FreeTemp(var_a); QCC_FreeTemp(var_b);\n\t\t\t\t\treturn QCC_MakeUIntConst(eval_a->_uint < eval_b->_uint);\n\t\t\t\tcase OP_LE_U:\n\t\t\t\t\tQCC_FreeTemp(var_a); QCC_FreeTemp(var_b);\n\t\t\t\t\treturn QCC_MakeUIntConst(eval_a->_uint <= eval_b->_uint);\n\t\t\t\tcase OP_DIV_U:\n\t\t\t\t\tQCC_FreeTemp(var_a); QCC_FreeTemp(var_b);\n\t\t\t\t\treturn QCC_MakeUIntConst(eval_a->_uint / eval_b->_uint);\n\t\t\t\tcase OP_RSHIFT_U:\n\t\t\t\t\tQCC_FreeTemp(var_a); QCC_FreeTemp(var_b);\n\t\t\t\t\treturn QCC_MakeUIntConst(eval_a->_uint >> eval_b->_int);\n\n\t\t\t\tcase OP_ADD_I64:\n\t\t\t\t\tQCC_FreeTemp(var_a); QCC_FreeTemp(var_b);\n\t\t\t\t\treturn QCC_MakeInt64Const(eval_a->i64 + eval_b->i64);\n\t\t\t\tcase OP_SUB_I64:\n\t\t\t\t\tQCC_FreeTemp(var_a); QCC_FreeTemp(var_b);\n\t\t\t\t\treturn QCC_MakeInt64Const(eval_a->i64 - eval_b->i64);\n\t\t\t\tcase OP_MUL_I64:\n\t\t\t\t\tQCC_FreeTemp(var_a); QCC_FreeTemp(var_b);\n\t\t\t\t\treturn QCC_MakeInt64Const(eval_a->i64 * eval_b->i64);\n\t\t\t\tcase OP_DIV_I64:\n\t\t\t\t\tQCC_FreeTemp(var_a); QCC_FreeTemp(var_b);\n\t\t\t\t\toptres_constantarithmatic++;\n\t\t\t\t\tif (eval_b->i64 == 0)\n\t\t\t\t\t{\n\t\t\t\t\t\tQCC_PR_ParseWarning(WARN_DIVISIONBY0, \"Division by constant 0\");\n\t\t\t\t\t\treturn QCC_MakeInt64Const(0);\n\t\t\t\t\t}\n\t\t\t\t\telse if (eval_b->i64 == -1 && eval_a->i64 == INT64_MIN)\n\t\t\t\t\t{\n\t\t\t\t\t\tQCC_PR_ParseWarning(WARN_DIVISIONBY0, \"Signed overflow on division by -1\");\n\t\t\t\t\t\treturn QCC_MakeInt64Const(INT64_MAX);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\treturn QCC_MakeInt64Const(eval_a->i64 / eval_b->i64);\n\t\t\t\tcase OP_LSHIFT_I64I:\n\t\t\t\t\tQCC_FreeTemp(var_a); QCC_FreeTemp(var_b);\n\t\t\t\t\treturn QCC_MakeInt64Const(eval_a->i64 << eval_b->_int);\n\t\t\t\tcase OP_RSHIFT_I64I:\n\t\t\t\t\tQCC_FreeTemp(var_a); QCC_FreeTemp(var_b);\n\t\t\t\t\treturn QCC_MakeInt64Const(eval_a->i64 >> eval_b->_int);\n\t\t\t\tcase OP_BITAND_I64:\n\t\t\t\t\tQCC_FreeTemp(var_a); QCC_FreeTemp(var_b);\n\t\t\t\t\treturn QCC_MakeInt64Const(eval_a->i64 & eval_b->i64);\n\t\t\t\tcase OP_BITOR_I64:\n\t\t\t\t\tQCC_FreeTemp(var_a); QCC_FreeTemp(var_b);\n\t\t\t\t\treturn QCC_MakeInt64Const(eval_a->i64 | eval_b->i64);\n\t\t\t\tcase OP_BITXOR_I64:\n\t\t\t\t\tQCC_FreeTemp(var_a); QCC_FreeTemp(var_b);\n\t\t\t\t\treturn QCC_MakeInt64Const(eval_a->i64 ^ eval_b->i64);\n\t\t\t\tcase OP_LE_I64:\n\t\t\t\t\tQCC_FreeTemp(var_a); QCC_FreeTemp(var_b);\n\t\t\t\t\treturn QCC_MakeIntConst(eval_a->i64 <= eval_b->i64);\n\t\t\t\tcase OP_LT_I64:\n\t\t\t\t\tQCC_FreeTemp(var_a); QCC_FreeTemp(var_b);\n\t\t\t\t\treturn QCC_MakeIntConst(eval_a->i64 < eval_b->i64);\n\t\t\t\tcase OP_EQ_I64:\n\t\t\t\t\tQCC_FreeTemp(var_a); QCC_FreeTemp(var_b);\n\t\t\t\t\treturn QCC_MakeIntConst(eval_a->i64 == eval_b->i64);\n\t\t\t\tcase OP_NE_I64:\n\t\t\t\t\tQCC_FreeTemp(var_a); QCC_FreeTemp(var_b);\n\t\t\t\t\treturn QCC_MakeIntConst(eval_a->i64 != eval_b->i64);\n\t\t\t\tcase OP_LT_U64:\n\t\t\t\t\tQCC_FreeTemp(var_a); QCC_FreeTemp(var_b);\n\t\t\t\t\treturn QCC_MakeIntConst(eval_a->u64 < eval_b->u64);\n\t\t\t\tcase OP_LE_U64:\n\t\t\t\t\tQCC_FreeTemp(var_a); QCC_FreeTemp(var_b);\n\t\t\t\t\treturn QCC_MakeIntConst(eval_a->u64 <= eval_b->u64);\n\t\t\t\tcase OP_DIV_U64:\n\t\t\t\t\tQCC_FreeTemp(var_a); QCC_FreeTemp(var_b);\n\t\t\t\t\toptres_constantarithmatic++;\n\t\t\t\t\tif (eval_b->u64 == 0)\n\t\t\t\t\t{\n\t\t\t\t\t\tQCC_PR_ParseWarning(WARN_DIVISIONBY0, \"Division by constant 0\");\n\t\t\t\t\t\treturn QCC_MakeUIntConst(0);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\treturn QCC_MakeUInt64Const(eval_a->u64 / eval_b->u64);\n\t\t\t\tcase OP_RSHIFT_U64I:\n\t\t\t\t\tQCC_FreeTemp(var_a); QCC_FreeTemp(var_b);\n\t\t\t\t\treturn QCC_MakeUInt64Const(eval_a->u64 >> eval_b->_int);\n\n\t\t\t\tcase OP_ADD_D:\n\t\t\t\t\tQCC_FreeTemp(var_a); QCC_FreeTemp(var_b);\n\t\t\t\t\treturn QCC_MakeDoubleConst(eval_a->_double + eval_b->_double);\n\t\t\t\tcase OP_SUB_D:\n\t\t\t\t\tQCC_FreeTemp(var_a); QCC_FreeTemp(var_b);\n\t\t\t\t\treturn QCC_MakeDoubleConst(eval_a->_double - eval_b->_double);\n\t\t\t\tcase OP_MUL_D:\n\t\t\t\t\tQCC_FreeTemp(var_a); QCC_FreeTemp(var_b);\n\t\t\t\t\treturn QCC_MakeDoubleConst(eval_a->_double * eval_b->_double);\n\t\t\t\tcase OP_DIV_D:\n\t\t\t\t\tQCC_FreeTemp(var_a); QCC_FreeTemp(var_b);\n\t\t\t\t\treturn QCC_MakeDoubleConst(eval_a->_double / eval_b->_double);\n\n\t\t\t\tcase OP_LE_D:\n\t\t\t\t\tQCC_FreeTemp(var_a); QCC_FreeTemp(var_b);\n\t\t\t\t\treturn QCC_MakeIntConst(eval_a->_double <= eval_b->_double);\n\t\t\t\tcase OP_LT_D:\n\t\t\t\t\tQCC_FreeTemp(var_a); QCC_FreeTemp(var_b);\n\t\t\t\t\treturn QCC_MakeIntConst(eval_a->_double < eval_b->_double);\n\t\t\t\tcase OP_EQ_D:\n\t\t\t\t\tQCC_FreeTemp(var_a); QCC_FreeTemp(var_b);\n\t\t\t\t\treturn QCC_MakeIntConst(eval_a->_double == eval_b->_double);\n\t\t\t\tcase OP_NE_D:\n\t\t\t\t\tQCC_FreeTemp(var_a); QCC_FreeTemp(var_b);\n\t\t\t\t\treturn QCC_MakeIntConst(eval_a->_double != eval_b->_double);\n\n\n\t\t\t\tcase OP_LSHIFT_DI:\n\t\t\t\t\tQCC_FreeTemp(var_a); QCC_FreeTemp(var_b);\n\t\t\t\t\treturn QCC_MakeDoubleConst((pint64_t)eval_a->_double << eval_b->_int);\n\t\t\t\tcase OP_RSHIFT_DI:\n\t\t\t\t\tQCC_FreeTemp(var_a); QCC_FreeTemp(var_b);\n\t\t\t\t\treturn QCC_MakeDoubleConst((pint64_t)eval_a->_double >> eval_b->_int);\n\t\t\t\tcase OP_BITAND_D:\n\t\t\t\t\tQCC_FreeTemp(var_a); QCC_FreeTemp(var_b);\n\t\t\t\t\treturn QCC_MakeDoubleConst((pint64_t)eval_a->_double & (pint64_t)eval_b->_double);\n\t\t\t\tcase OP_BITOR_D:\n\t\t\t\t\tQCC_FreeTemp(var_a); QCC_FreeTemp(var_b);\n\t\t\t\t\treturn QCC_MakeDoubleConst((pint64_t)eval_a->_double | (pint64_t)eval_b->_double);\n\t\t\t\tcase OP_BITXOR_D:\n\t\t\t\t\tQCC_FreeTemp(var_a); QCC_FreeTemp(var_b);\n\t\t\t\t\treturn QCC_MakeDoubleConst((pint64_t)eval_a->_double ^ (pint64_t)eval_b->_double);\n\n\t\t\t\tcase OP_BITEXTEND_I:\n\t\t\t\t\tQCC_FreeTemp(var_a); QCC_FreeTemp(var_b);\n\t\t\t\t\treturn QCC_MakeIntConst((eval_a->_int << (32-(eval_b->_uint&0xff)-(eval_b->_uint>>8))) >> (32-(eval_b->_uint&0xff)));\n\t\t\t\tcase OP_BITEXTEND_U:\n\t\t\t\t\tQCC_FreeTemp(var_a); QCC_FreeTemp(var_b);\n\t\t\t\t\treturn QCC_MakeUIntConst((eval_a->_uint << (32u-(eval_b->_uint&0xff)-(eval_b->_uint>>8))) >> (32u-(eval_b->_uint&0xff)));\n\t\t\t\t//case OP_BITCOPY_I: //reads var_c too.\n\t\t\t\t//\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t//a is const, b is not\n\t\t\t\tswitch (op - pr_opcodes)\n\t\t\t\t{\n\t\t\t\t\t//OP_NOT_S needs to do a string comparison\n\t\t\t\tcase OP_NOT_F:\n\t\t\t\t\tQCC_FreeTemp(var_a);\n\t\t\t\t\toptres_constantarithmatic++;\n\t\t\t\t\treturn QCC_MakeFloatConst(!eval_a->_float);\n\t\t\t\tcase OP_NOT_V:\n\t\t\t\t\tQCC_FreeTemp(var_a);\n\t\t\t\t\toptres_constantarithmatic++;\n\t\t\t\t\treturn QCC_MakeFloatConst(!eval_a->vector[0] && !eval_a->vector[1] && !eval_a->vector[2]);\n\t\t\t\tcase OP_NOT_ENT: // o.O\n\t\t\t\tcase OP_NOT_FNC: // o.O\n\t\t\t\t\tQCC_FreeTemp(var_a);\n\t\t\t\t\toptres_constantarithmatic++;\n\t\t\t\t\treturn QCC_MakeFloatConst(!eval_a->_int);\n\t\t\t\tcase OP_NOT_I:\n\t\t\t\t\tQCC_FreeTemp(var_a);\n\t\t\t\t\toptres_constantarithmatic++;\n\t\t\t\t\treturn QCC_MakeIntConst(!eval_a->_int);\n\t\t\t\tcase OP_BITNOT_F:\n\t\t\t\t\tQCC_FreeTemp(var_a);\n\t\t\t\t\treturn QCC_MakeFloatConst(~QCC_PR_RoundFloatConst(eval_a));\n\t\t\t\tcase OP_BITNOT_I:\n\t\t\t\t\tQCC_FreeTemp(var_a);\n\t\t\t\t\treturn QCC_MakeIntConst(~eval_a->_int);\n\t\t\t\tcase OP_BITNOT_V:\n\t\t\t\t\tQCC_FreeTemp(var_a);\n\t\t\t\t\treturn QCC_MakeVectorConst(~(int)eval_a->vector[0], ~(int)eval_a->vector[1], ~(int)eval_a->vector[2]);\n\t\t\t\tcase OP_CONV_FTOI:\n\t\t\t\t\tQCC_FreeTemp(var_a);\n\t\t\t\t\toptres_constantarithmatic++;\n\t\t\t\t\tif ((int)eval_a->_float != eval_a->_float)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (eval_a->_int & 0x7f800000)\n\t\t\t\t\t\t\tQCC_PR_ParseWarning(WARN_OVERFLOW, \"Numerical overflow. %f will be rounded to %i\", eval_a->_float, (int)eval_a->_float);\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tQCC_PR_ParseWarning(WARN_OVERFLOW, \"Denormalized float %g will be rounded to %i. This is probably not what you want.\", eval_a->_float, (int)eval_a->_float);\n\t\t\t\t\t}\n\t\t\t\t\treturn QCC_MakeIntConst(eval_a->_float);\n\t\t\t\tcase OP_CONV_ITOF:\n\t\t\t\t\tQCC_FreeTemp(var_a);\n\t\t\t\t\toptres_constantarithmatic++;\n\t\t\t\t\t{\n\t\t\t\t\t\tfloat fl = eval_a->_int;\n\t\t\t\t\t\tif ((int)fl != eval_a->_int)\n\t\t\t\t\t\t\tQCC_PR_ParseWarning(WARN_OVERFLOW, \"Numerical truncation of %#x to %#x.\", eval_a->_int, (int)fl);\n\t\t\t\t\t}\n\t\t\t\t\treturn QCC_MakeFloatConst(eval_a->_int);\n\t\t\t\tcase OP_CONV_FU:\n\t\t\t\t\tQCC_FreeTemp(var_a);\n\t\t\t\t\toptres_constantarithmatic++;\n\t\t\t\t\tif ((unsigned int)eval_a->_float != eval_a->_float)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (eval_a->_int & 0x7f800000)\n\t\t\t\t\t\t\tQCC_PR_ParseWarning(WARN_OVERFLOW, \"Numerical overflow. %f will be rounded to %i\", eval_a->_float, (unsigned int)eval_a->_float);\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tQCC_PR_ParseWarning(WARN_OVERFLOW, \"Denormalized float %g will be rounded to %i. This is probably not what you want.\", eval_a->_float, (unsigned int)eval_a->_float);\n\t\t\t\t\t}\n\t\t\t\t\treturn QCC_MakeUIntConst(eval_a->_float);\n\t\t\t\tcase OP_CONV_UF:\n\t\t\t\t\tQCC_FreeTemp(var_a);\n\t\t\t\t\toptres_constantarithmatic++;\n\t\t\t\t\t{\n\t\t\t\t\t\tfloat fl = eval_a->_uint;\n\t\t\t\t\t\tif ((unsigned int)fl != eval_a->_uint)\n\t\t\t\t\t\t\tQCC_PR_ParseWarning(WARN_OVERFLOW, \"Numerical truncation of %#x to %#x.\", eval_a->_uint, (unsigned int)fl);\n\t\t\t\t\t}\n\t\t\t\t\treturn QCC_MakeFloatConst(eval_a->_uint);\n\n\t\t\t\tcase OP_CONV_FD:\n\t\t\t\t\tQCC_FreeTemp(var_a);\n\t\t\t\t\toptres_constantarithmatic++;\n\t\t\t\t\treturn QCC_MakeDoubleConst(eval_a->_float);\n\t\t\t\tcase OP_CONV_DF:\n\t\t\t\t\tQCC_FreeTemp(var_a);\n\t\t\t\t\toptres_constantarithmatic++;\n\t\t\t\t\t{\n\t\t\t\t\t\tfloat fl = eval_a->_double;\n//\t\t\t\t\t\tif ((double)fl != eval_a->_double)\n//\t\t\t\t\t\t\tQCC_PR_ParseWarning(WARN_OVERFLOW, \"Numerical truncation of %g to %g.\", eval_a->_double, (float)fl);\n\t\t\t\t\t\treturn QCC_MakeFloatConst(fl);\n\t\t\t\t\t}\n\n\t\t\t\tcase OP_CONV_DI64:\n\t\t\t\t\tQCC_FreeTemp(var_a);\n\t\t\t\t\toptres_constantarithmatic++;\n\t\t\t\t\tif ((pint64_t)eval_a->_double != eval_a->_double)\n\t\t\t\t\t\tQCC_PR_ParseWarning(WARN_OVERFLOW, \"Numerical overflow. %f will be rounded to %\"pPRIi64\"\", eval_a->_double, (pint64_t)eval_a->_double);\n\t\t\t\t\treturn QCC_MakeInt64Const(eval_a->_double);\n\t\t\t\tcase OP_CONV_I64D:\n\t\t\t\t\tQCC_FreeTemp(var_a);\n\t\t\t\t\toptres_constantarithmatic++;\n\t\t\t\t\t{\n\t\t\t\t\t\tdouble d = eval_a->i64;\n\t\t\t\t\t\tif ((pint64_t)d != eval_a->i64)\n\t\t\t\t\t\t\tQCC_PR_ParseWarning(WARN_OVERFLOW, \"Numerical truncation of %#\"pPRIx64\" to %#\"pPRIx64\".\", eval_a->i64, (pint64_t)d);\n\t\t\t\t\t\treturn QCC_MakeDoubleConst(d);\n\t\t\t\t\t}\n\t\t\t\tcase OP_CONV_DU64:\n\t\t\t\t\tQCC_FreeTemp(var_a);\n\t\t\t\t\toptres_constantarithmatic++;\n\t\t\t\t\tif ((pint64_t)eval_a->_double != eval_a->_double)\n\t\t\t\t\t\tQCC_PR_ParseWarning(WARN_OVERFLOW, \"Numerical overflow. %f will be rounded to %\"pPRIi64\"\", eval_a->_double, (puint64_t)eval_a->_double);\n\t\t\t\t\treturn QCC_MakeUInt64Const(eval_a->_double);\n\t\t\t\tcase OP_CONV_U64D:\n\t\t\t\t\tQCC_FreeTemp(var_a);\n\t\t\t\t\toptres_constantarithmatic++;\n\t\t\t\t\t{\n\t\t\t\t\t\tdouble d = eval_a->u64;\n\t\t\t\t\t\tif ((pint64_t)d != eval_a->u64)\n\t\t\t\t\t\t\tQCC_PR_ParseWarning(WARN_OVERFLOW, \"Numerical truncation of %#\"pPRIx64\" to %#\"pPRIx64\".\", eval_a->u64, (puint64_t)d);\n\t\t\t\t\t\treturn QCC_MakeDoubleConst(d);\n\t\t\t\t\t}\n\t\t\t\tcase OP_CONV_FI64:\n\t\t\t\t\tQCC_FreeTemp(var_a);\n\t\t\t\t\toptres_constantarithmatic++;\n\t\t\t\t\tif ((pint64_t)eval_a->_float != eval_a->_float)\n\t\t\t\t\t\tQCC_PR_ParseWarning(WARN_OVERFLOW, \"Numerical overflow. %f will be rounded to %\"pPRIi64\"\", eval_a->_float, (pint64_t)eval_a->_float);\n\t\t\t\t\treturn QCC_MakeInt64Const(eval_a->_float);\n\t\t\t\tcase OP_CONV_I64F:\n\t\t\t\t\tQCC_FreeTemp(var_a);\n\t\t\t\t\toptres_constantarithmatic++;\n\t\t\t\t\t{\n\t\t\t\t\t\tfloat d = eval_a->i64;\n\t\t\t\t\t\tif ((pint64_t)d != eval_a->i64)\n\t\t\t\t\t\t\tQCC_PR_ParseWarning(WARN_OVERFLOW, \"Numerical truncation of %#\"pPRIx64\" to %#\"pPRIx64\".\", eval_a->i64, (pint64_t)d);\n\t\t\t\t\t\treturn QCC_MakeFloatConst(d);\n\t\t\t\t\t}\n\t\t\t\tcase OP_CONV_FU64:\n\t\t\t\t\tQCC_FreeTemp(var_a);\n\t\t\t\t\toptres_constantarithmatic++;\n\t\t\t\t\tif ((puint64_t)eval_a->_float != eval_a->_float)\n\t\t\t\t\t\tQCC_PR_ParseWarning(WARN_OVERFLOW, \"Numerical overflow. %f will be rounded to %\"pPRIu64\"\", eval_a->_float, (puint64_t)eval_a->_float);\n\t\t\t\t\treturn QCC_MakeUInt64Const(eval_a->_float);\n\t\t\t\tcase OP_CONV_U64F:\n\t\t\t\t\tQCC_FreeTemp(var_a);\n\t\t\t\t\toptres_constantarithmatic++;\n\t\t\t\t\t{\n\t\t\t\t\t\tfloat d = eval_a->u64;\n\t\t\t\t\t\tif ((puint64_t)d != eval_a->u64)\n\t\t\t\t\t\t\tQCC_PR_ParseWarning(WARN_OVERFLOW, \"Numerical truncation of %#\"pPRIx64\" to %#\"pPRIx64\".\", eval_a->u64, (puint64_t)d);\n\t\t\t\t\t\treturn QCC_MakeFloatConst(d);\n\t\t\t\t\t}\n\t\t\t\tcase OP_CONV_II64:\n\t\t\t\t\tQCC_FreeTemp(var_a);\n\t\t\t\t\toptres_constantarithmatic++;\n\t\t\t\t\t{\n\t\t\t\t\t\tpint64_t d = eval_a->_int;\n\t\t\t\t\t\tif ((pint_t)d != eval_a->_int)\n\t\t\t\t\t\t\tQCC_PR_ParseWarning(WARN_OVERFLOW, \"Numerical truncation of %\"pPRIi\" to %#\"pPRIx64\".\", eval_a->_int, (pint64_t)d);\n\t\t\t\t\t\treturn QCC_MakeInt64Const(d);\n\t\t\t\t\t}\n\t\t\t\tcase OP_CONV_UI64:\n\t\t\t\t\tQCC_FreeTemp(var_a);\n\t\t\t\t\toptres_constantarithmatic++;\n\t\t\t\t\t{\n\t\t\t\t\t\tpuint64_t d = eval_a->_uint;\n\t\t\t\t\t\tif ((puint_t)d != eval_a->_uint)\n\t\t\t\t\t\t\tQCC_PR_ParseWarning(WARN_OVERFLOW, \"Numerical truncation of %\"pPRIu\" to %#\"pPRIx64\".\", eval_a->_uint, (pint64_t)d);\n\t\t\t\t\t\treturn QCC_MakeUInt64Const(d);\n\t\t\t\t\t}\n\t\t\t\tcase OP_CONV_I64I:\n\t\t\t\t\tQCC_FreeTemp(var_a);\n\t\t\t\t\toptres_constantarithmatic++;\n\t\t\t\t\t{\n\t\t\t\t\t\tpint_t d = eval_a->i64;\n\t\t\t\t\t\tif ((pint64_t)d != eval_a->i64)\n\t\t\t\t\t\t\tQCC_PR_ParseWarning(WARN_OVERFLOW, \"Numerical truncation of %\"pPRIx64\" to %#\"pPRIx\".\", eval_a->i64, (pint_t)d);\n\t\t\t\t\t\treturn QCC_MakeIntConst(d);\n\t\t\t\t\t}\n\n\t\t\t\tcase OP_BITOR_F:\n\t\t\t\tcase OP_ADD_F:\n\t\t\t\t\tif (eval_a->_float == 0)\n\t\t\t\t\t{\n\t\t\t\t\t\toptres_constantarithmatic++;\n\t\t\t\t\t\tQCC_FreeTemp(var_a);\n\t\t\t\t\t\treturn var_b;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase OP_MUL_F:\n\t\t\t\t\tif (eval_a->_float == 1)\n\t\t\t\t\t{\n\t\t\t\t\t\toptres_constantarithmatic++;\n\t\t\t\t\t\tQCC_FreeTemp(var_a);\n\t\t\t\t\t\treturn var_b;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase OP_BITAND_F:\n\t\t\t\tcase OP_BITAND_FI:\n\t\t\t\t\tif (QCC_PR_RoundFloatConst(eval_a) == 0)\n\t\t\t\t\t{\n\t\t\t\t\t\tQCC_FreeTemp(var_a); QCC_FreeTemp(var_b);\n\t\t\t\t\t\treturn QCC_MakeFloatConst(0);\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase OP_LSHIFT_U:\n\t\t\t\tcase OP_RSHIFT_U:\n\t\t\t\t\tif (eval_a->_uint == 0)\n\t\t\t\t\t{\n\t\t\t\t\t\tQCC_FreeTemp(var_a); QCC_FreeTemp(var_b);\n\t\t\t\t\t\treturn QCC_MakeUIntConst(0);\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase OP_LSHIFT_I:\n\t\t\t\tcase OP_RSHIFT_I:\n\t\t\t\tcase OP_BITAND_I:\n\t\t\t\tcase OP_BITAND_IF:\n\t\t\t\t\tif (eval_a->_int == 0)\n\t\t\t\t\t{\n\t\t\t\t\t\tQCC_FreeTemp(var_a); QCC_FreeTemp(var_b);\n\t\t\t\t\t\treturn QCC_MakeIntConst(0);\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase OP_AND_ANY:\n\t\t\t\tcase OP_AND_F:\n\t\t\t\tcase OP_AND_FI:\n\t\t\t\tcase OP_AND_I:\n\t\t\t\tcase OP_AND_IF:\n\t\t\t\t\t/*if (flag_pythonlogic)\n\t\t\t\t\t{\t//c=(a?b:a);\n\t\t\t\t\t\toptres_constantarithmatic++;\n\t\t\t\t\t\tif (QCC_Eval_Truth(eval_a, var_a.cast))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tQCC_FreeTemp(var_a);\n\t\t\t\t\t\t\treturn var_b;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tQCC_FreeTemp(var_b);\n\t\t\t\t\t\t\treturn var_a;\n\t\t\t\t\t\t}\n\t\t\t\t\t}*/\n\t\t\t\t\tif (!QCC_Eval_Truth(eval_a, var_a.cast, false))\n\t\t\t\t\t{\t//one side is false, thus return false.\n\t\t\t\t\t\toptres_constantarithmatic++;\n\t\t\t\t\t\tQCC_FreeTemp(var_a);\n\t\t\t\t\t\tQCC_FreeTemp(var_b);\n\t\t\t\t\t\treturn QCC_MakeFloatConst(0);\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase OP_OR_ANY:\n\t\t\t\tcase OP_OR_F:\n\t\t\t\tcase OP_OR_FI:\n\t\t\t\tcase OP_OR_I:\n\t\t\t\tcase OP_OR_IF:\n\t\t\t\t\t/*if (flag_pythonlogic)\n\t\t\t\t\t{\t//c=(a?a:b);\n\t\t\t\t\t\toptres_constantarithmatic++;\n\t\t\t\t\t\tif (QCC_Eval_Truth(eval_a, var_a.cast))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tQCC_FreeTemp(var_b);\n\t\t\t\t\t\t\treturn var_a;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tQCC_FreeTemp(var_a);\n\t\t\t\t\t\t\treturn var_b;\n\t\t\t\t\t\t}\n\t\t\t\t\t}*/\n\t\t\t\t\tif (QCC_Eval_Truth(eval_a, var_a.cast, false))\n\t\t\t\t\t{\t//one side is true, thus return true.\n\t\t\t\t\t\toptres_constantarithmatic++;\n\t\t\t\t\t\tQCC_FreeTemp(var_a);\n\t\t\t\t\t\tQCC_FreeTemp(var_b);\n\t\t\t\t\t\treturn QCC_MakeFloatConst(1);\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase OP_BITOR_I:\n\t\t\t\tcase OP_ADD_I:\n\t\t\t\t\tif (eval_a->_int == 0)\n\t\t\t\t\t{\n\t\t\t\t\t\toptres_constantarithmatic++;\n\t\t\t\t\t\tQCC_FreeTemp(var_a);\n\t\t\t\t\t\treturn var_b;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase OP_MUL_I:\n\t\t\t\t\tif (eval_a->_int == 1)\n\t\t\t\t\t{\n\t\t\t\t\t\toptres_constantarithmatic++;\n\t\t\t\t\t\tQCC_FreeTemp(var_a);\n\t\t\t\t\t\treturn var_b;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse if (eval_b)\n\t\t{\n\t\t\tif (outstatement)\n\t\t\t\t*outstatement = NULL;\n\t\t\t//b is const, a is not\n\t\t\tswitch (op - pr_opcodes)\n\t\t\t{\n\t\t\t\tcase OP_GLOBALADDRESS:\n\t\t\t\t\tif (flag_pointerrelocs && !pr_scope)\n\t\t\t\t\t\treturn QCC_MakeGAddress(type_pointer, var_a.sym, var_a.ofs + QCC_Eval_Int(QCC_SRef_EvalConst(var_b), var_b.cast), 0);\n\t\t\t\t\tbreak;\n\t\t\t\tcase OP_AND_ANY:\n\t\t\t\tcase OP_AND_F:\n\t\t\t\tcase OP_AND_FI:\n\t\t\t\tcase OP_AND_I:\n\t\t\t\tcase OP_AND_IF:\n\t\t\t\t\t/*if (flag_pythonlogic)\n\t\t\t\t\t{\t//c=(a?b:a);\n\t\t\t\t\t\toptres_constantarithmatic++;\n\t\t\t\t\t\tif (QCC_Eval_Truth(eval_a, var_a.cast))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tQCC_FreeTemp(var_a);\n\t\t\t\t\t\t\treturn var_b;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tQCC_FreeTemp(var_b);\n\t\t\t\t\t\t\treturn var_a;\n\t\t\t\t\t\t}\n\t\t\t\t\t} else*/\n\t\t\t\t\tif (!QCC_Eval_Truth(eval_b, var_b.cast, false))\n\t\t\t\t\t{\t//one side is false, thus return false.\n\t\t\t\t\t\toptres_constantarithmatic++;\n\t\t\t\t\t\tQCC_FreeTemp(var_a);\n\t\t\t\t\t\tQCC_FreeTemp(var_b);\n\t\t\t\t\t\treturn QCC_MakeFloatConst(0);\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase OP_OR_ANY:\n\t\t\t\tcase OP_OR_F:\n\t\t\t\tcase OP_OR_FI:\n\t\t\t\tcase OP_OR_I:\n\t\t\t\tcase OP_OR_IF:\n\t\t\t\t\t/*if (flag_pythonlogic)\n\t\t\t\t\t{\t//c=(a?a:b);\n\t\t\t\t\t\toptres_constantarithmatic++;\n\t\t\t\t\t\tif (QCC_Eval_Truth(eval_a, var_a.cast))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tQCC_FreeTemp(var_b);\n\t\t\t\t\t\t\treturn var_a;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tQCC_FreeTemp(var_a);\n\t\t\t\t\t\t\treturn var_b;\n\t\t\t\t\t\t}\n\t\t\t\t\t}*/\n\t\t\t\t\tif (QCC_Eval_Truth(eval_a, var_a.cast, false))\n\t\t\t\t\t{\t//one side is true, thus return true.\n\t\t\t\t\t\toptres_constantarithmatic++;\n\t\t\t\t\t\tQCC_FreeTemp(var_a);\n\t\t\t\t\t\tQCC_FreeTemp(var_b);\n\t\t\t\t\t\treturn QCC_MakeFloatConst(1);\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\n\n#if IAMNOTLAZY\n\t\t\tcase OP_LOADA_F:\n\t\t\tcase OP_LOADA_V:\n\t\t\tcase OP_LOADA_S:\n\t\t\tcase OP_LOADA_ENT:\n\t\t\tcase OP_LOADA_FLD:\n\t\t\tcase OP_LOADA_FNC:\n\t\t\tcase OP_LOADA_I:\n\t\t\t\t{\n\t\t\t\t\tQCC_def_t *nd;\n\t\t\t\t\tnd = (void *)qccHunkAlloc (sizeof(QCC_def_t));\n\t\t\t\t\tmemset (nd, 0, sizeof(QCC_def_t));\n\t\t\t\t\tnd->type = var_a->type;\n\t\t\t\t\tnd->ofs = var_a->ofs + G_INT(var_b->ofs);\n\t\t\t\t\tnd->temp = var_a->temp;\n\t\t\t\t\tnd->constant = false;\n\t\t\t\t\tnd->name = var_a->name;\n\t\t\t\t\treturn nd;\n\t\t\t\t}\n\t\t\t\tbreak;\n#endif\n\t\t\tcase OP_BITOR_F:\n\t\t\tcase OP_SUB_F:\n\t\t\tcase OP_ADD_F:\n\t\t\t\tif (eval_b->_float == 0)\n\t\t\t\t{\n\t\t\t\t\toptres_constantarithmatic++;\n\t\t\t\t\tQCC_FreeTemp(var_b);\n\t\t\t\t\treturn var_a;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase OP_DIV_VF:\n\t\t\t\tif (!eval_b->_float)\n\t\t\t\t\tQCC_PR_ParseWarning(WARN_DIVISIONBY0, \"Division by 0\\n\");\n\t\t\t\telse if (flag_reciprocalmaths && eval_b->_int&(0xff<<23))\n\t\t\t\t{\n\t\t\t\t\tQCC_FreeTemp(var_b);\n\t\t\t\t\tvar_b = QCC_MakeFloatConst(1.0 / eval_b->_float);\n\t\t\t\t\treturn QCC_PR_StatementFlags(&pr_opcodes[OP_MUL_VF], var_a, var_b, outstatement, 0);\n\t\t\t\t}\n\t\t\t\tif (eval_b->_float == 1)\n\t\t\t\t{\n\t\t\t\t\toptres_constantarithmatic++;\n\t\t\t\t\tQCC_FreeTemp(var_b);\n\t\t\t\t\treturn var_a;\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase OP_DIV_F:\n\t\t\tcase OP_DIV_IF:\n\t\t\t\tif (!eval_b->_float)\n\t\t\t\t\tQCC_PR_ParseWarning(WARN_DIVISIONBY0, \"Division by 0\\n\");\n\t\t\t\telse if (flag_reciprocalmaths && (eval_b->_int&(0xff<<23)) && eval_b->_int != 3/*paranoid about vector indexing. don't ruin precision*/)\n\t\t\t\t{\n\t\t\t\t\tQCC_FreeTemp(var_b);\n\t\t\t\t\tvar_b = QCC_MakeFloatConst(1.0 / eval_b->_float);\n\t\t\t\t\treturn QCC_PR_StatementFlags((op == &pr_opcodes[OP_DIV_F])?&pr_opcodes[OP_MUL_F]:&pr_opcodes[OP_MUL_IF], var_a, var_b, outstatement, 0);\n\t\t\t\t}\n\t\t\t\t//fallthrough\n\t\t\tcase OP_MUL_F:\n\t\t\tcase OP_MUL_IF:\n\t\t\t\tif (eval_b->_float == 1)\n\t\t\t\t{\n\t\t\t\t\toptres_constantarithmatic++;\n\t\t\t\t\tQCC_FreeTemp(var_b);\n\t\t\t\t\treturn var_a;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase OP_BITOR_I:\n\t\t\tcase OP_SUB_I:\n\t\t\tcase OP_ADD_I:\n\t\t\tcase OP_ADD_PIW:\n\t\t\t\tif (eval_b->_int == 0)\n\t\t\t\t{\n\t\t\t\t\toptres_constantarithmatic++;\n\t\t\t\t\tQCC_FreeTemp(var_b);\n\t\t\t\t\treturn var_a;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase OP_DIV_I:\n\t\t\tcase OP_DIV_U:\n\t\t\tcase OP_DIV_FI:\n\t\t\t\tif (!eval_b->_int)\n\t\t\t\t\tQCC_PR_ParseWarning(WARN_DIVISIONBY0, \"Division by 0\\n\");\n\t\t\tcase OP_MUL_I:\n\t\t\tcase OP_MUL_FI:\n\t\t\t\tif (eval_b->_int == 1)\n\t\t\t\t{\n\t\t\t\t\toptres_constantarithmatic++;\n\t\t\t\t\tQCC_FreeTemp(var_b);\n\t\t\t\t\treturn var_a;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase OP_BITAND_I:\n\t\t\t\tif (eval_b->_int == ~0)\n\t\t\t\t{\n\t\t\t\t\toptres_constantarithmatic++;\n\t\t\t\t\tQCC_FreeTemp(var_b);\n\t\t\t\t\treturn var_a;\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase OP_BITEXTEND_I:\n\t\t\t\tif (eval_b->_uint == 32)\n\t\t\t\t{\n\t\t\t\t\tQCC_FreeTemp(var_b);\n\t\t\t\t\treturn var_a;\t//wtf?\n\t\t\t\t}\n\t\t\t\tif ((eval_b->_uint&0xff) == 0)\n\t\t\t\t{\n\t\t\t\t\tQCC_FreeTemp(var_b);\n\t\t\t\t\tQCC_FreeTemp(var_a);\n\t\t\t\t\treturn QCC_MakeIntConst(0);\t//wtf?\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase OP_BITEXTEND_U:\n\t\t\t\tif (eval_b->_uint == 32)\n\t\t\t\t{\n\t\t\t\t\tQCC_FreeTemp(var_b);\n\t\t\t\t\treturn var_a;\t//wtf?\n\t\t\t\t}\n\t\t\t\tif ((eval_b->_uint&0xff) == 0)\n\t\t\t\t{\n\t\t\t\t\tQCC_FreeTemp(var_b);\n\t\t\t\t\tQCC_FreeTemp(var_a);\n\t\t\t\t\treturn QCC_MakeUIntConst(0);\t//wtf?\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\t// self-comparison that is impacted when NaN\n\t// e.g. NaN == NaN, NaN != NaN, [NaN, 0, 0] == [NaN, 0, 0], etc.\n\tnan_eq_cond = false;\n\n\tswitch (op - pr_opcodes)\n\t{\n\tcase OP_STATE:\n\t\tif (qcc_framerate>0 && qcc_framerate != (qcc_targetformat_ishexen2()?20:10))\n\t\t{\t//can't use the normal opcode.\n\t\t\tQCC_ref_t tempref;\n\t\t\tQCC_sref_t self = QCC_PR_GetSRef(type_entity, \"self\", NULL, true, 0, false);\n\t\t\tQCC_sref_t time = QCC_PR_GetSRef(type_float, \"time\", NULL, true, 0, false);\n\t\t\tQCC_sref_t fldframe = QCC_PR_GetSRef(type_floatfield, \"frame\", NULL, true, 0, false);\n\t\t\tQCC_sref_t fldthink = QCC_PR_GetSRef(QCC_PR_FieldType(type_function), \"think\", NULL, true, 0, false);\n\t\t\tQCC_sref_t fldnextthink = QCC_PR_GetSRef(type_floatfield, \"nextthink\", NULL, true, 0, false);\n\n\t\t\tQCC_UnFreeTemp(self);\n\t\t\tQCC_UnFreeTemp(self);\n\n\t\t\t//self.frame = var_a;\n\t\t\tQCC_StoreSRefToRef(QCC_PR_BuildRef(&tempref, REF_FIELD,\tself,\n\t\t\t\tfldframe, fldframe.cast->aux_type,\n\t\t\t\tfalse, 0), var_a, false, false);\n\n\t\t\t//self.think = var_b;\n\t\t\tQCC_StoreSRefToRef(QCC_PR_BuildRef(&tempref, REF_FIELD, self,\n\t\t\t\tfldthink, fldthink.cast->aux_type,\n\t\t\t\tfalse, 0), var_b, false, false);\n\n\t\t\t//self.frame = time + interval;\n\t\t\ttime = QCC_PR_Statement(&pr_opcodes[OP_ADD_F], time, QCC_MakeFloatConst(1/qcc_framerate), NULL);\n\t\t\tQCC_StoreSRefToRef(QCC_PR_BuildRef(&tempref, REF_FIELD, self,\n\t\t\t\tfldnextthink, fldnextthink.cast->aux_type,\n\t\t\t\tfalse, 0), time, false, false);\n\t\t\treturn nullsref;\n\t\t}\n\t\tbreak;\n\tcase OP_WSTATE:\n\t\t{\t//there is no normal opcode.\n\t\t\tQCC_ref_t tempref;\n\t\t\tQCC_sref_t self = QCC_PR_GetSRef(type_entity, \"self\", NULL, true, 0, false);\n\t\t\tQCC_sref_t time = QCC_PR_GetSRef(type_float, \"time\", NULL, true, 0, false);\n\t\t\tQCC_sref_t fldframe = QCC_PR_GetSRef(type_floatfield, \"weaponframe\", NULL, true, 0, false);\n\t\t\tQCC_sref_t fldthink = QCC_PR_GetSRef(QCC_PR_FieldType(type_function), \"think\", NULL, true, 0, false);\n\t\t\tQCC_sref_t fldnextthink = QCC_PR_GetSRef(type_floatfield, \"nextthink\", NULL, true, 0, false);\n\n\t\t\tfloat framerate = (qcc_framerate>0)?qcc_framerate:(qcc_targetformat_ishexen2()?20:10);\n\n\t\t\tQCC_UnFreeTemp(self);\n\t\t\tQCC_UnFreeTemp(self);\n\n\t\t\t//self.frame = var_a;\n\t\t\tQCC_StoreSRefToRef(QCC_PR_BuildRef(&tempref, REF_FIELD,\tself,\n\t\t\t\tfldframe, fldframe.cast->aux_type,\n\t\t\t\tfalse, 0), var_a, false, false);\n\n\t\t\t//self.think = var_b;\n\t\t\tQCC_StoreSRefToRef(QCC_PR_BuildRef(&tempref, REF_FIELD, self,\n\t\t\t\tfldthink, fldthink.cast->aux_type,\n\t\t\t\tfalse, 0), var_b, false, false);\n\n\t\t\t//self.frame = time + interval;\n\t\t\ttime = QCC_PR_Statement(&pr_opcodes[OP_ADD_F], time, QCC_MakeFloatConst(1/framerate), NULL);\n\t\t\tQCC_StoreSRefToRef(QCC_PR_BuildRef(&tempref, REF_FIELD, self,\n\t\t\t\tfldnextthink, fldnextthink.cast->aux_type,\n\t\t\t\tfalse, 0), time, false, false);\n\t\t\treturn nullsref;\n\t\t}\n\t\tbreak;\n\n\tcase OP_STORE_F:\n\tcase OP_STORE_V:\n\tcase OP_STORE_FLD:\n\tcase OP_STORE_P:\n\tcase OP_STORE_I:\n\tcase OP_STORE_ENT:\n\tcase OP_STORE_FNC:\n\tcase OP_STORE_I64:\n//\tcase OP_STORE_D:\n\t\t{\n\t\t\tconst QCC_eval_t *idxeval = QCC_SRef_EvalConst(var_a);\n\t\t\tif (idxeval && (var_a.cast->type == ev_integer || var_a.cast->type == ev_float) && !idxeval->_int)\n\t\t\t{\n\t\t\t\t//you're allowed to assign 0i to anything\n\t\t\t\tif (op - pr_opcodes == OP_STORE_V)\t//make sure vectors get set properly.\n\t\t\t\t{\n\t\t\t\t\tQCC_FreeTemp(var_a);\n\t\t\t\t\tvar_a = QCC_MakeVectorConst(0, 0, 0);\n\t\t\t\t}\n\t\t\t\tif (op - pr_opcodes == OP_STORE_I64)\t//make sure vectors get set properly.\n\t\t\t\t{\n\t\t\t\t\tQCC_FreeTemp(var_a);\n\t\t\t\t\tvar_a = QCC_MakeInt64Const(0);\n\t\t\t\t}\n\t\t\t}\n\t\t\t/*else\n\t\t\t{\n\t\t\t\tQCC_type_t *t = var_a->type;\n\t\t\t\twhile(t)\n\t\t\t\t{\n\t\t\t\t\tif (!typecmp_lax(t, var_b->type))\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tt = t->parentclass;\n\t\t\t\t}\n\t\t\t\tif (!t)\n\t\t\t\t{\n\t\t\t\t\tTypeName(var_a->type, typea, sizeof(typea));\n\t\t\t\t\tTypeName(var_b->type, typeb, sizeof(typeb));\n\t\t\t\t\tQCC_PR_ParseWarning(WARN_STRICTTYPEMISMATCH, \"Implicit assignment from %s to %s %s\", typea, typeb, var_b->name);\n\t\t\t\t}\n\t\t\t}*/\n\t\t}\n\t\tbreak;\n\n\tcase OP_STOREP_F:\n\tcase OP_STOREP_V:\n\tcase OP_STOREP_FLD:\n\tcase OP_STOREP_P:\n\tcase OP_STOREP_I:\n\tcase OP_STOREP_ENT:\n\tcase OP_STOREP_FNC:\n\t\t{\n\t\t\tconst QCC_eval_t *idxeval = QCC_SRef_EvalConst(var_a);\n\t\t\tif (idxeval && (var_a.cast->type == ev_integer || var_a.cast->type == ev_float) && !idxeval->_int)\n\t\t\t{\n\t\t\t\t//you're allowed to assign 0i to anything\n\t\t\t\tif (op - pr_opcodes == OP_STOREP_V)\t//make sure vectors get set properly.\n\t\t\t\t{\n\t\t\t\t\tQCC_FreeTemp(var_a);\n\t\t\t\t\tvar_a = QCC_MakeVectorConst(0, 0, 0);\n\t\t\t\t}\n\t\t\t}\n\t\t\t/*else\n\t\t\t{\n\t\t\t\tQCC_type_t *t = var_a->type;\n\t\t\t\twhile(t)\n\t\t\t\t{\n\t\t\t\t\tif (!typecmp_lax(t, var_b->type->aux_type))\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tt = t->parentclass;\n\t\t\t\t}\n\t\t\t\tif (!t)\n\t\t\t\t{\n\t\t\t\t\tTypeName(var_a->type, typea, sizeof(typea));\n\t\t\t\t\tTypeName(var_b->type->aux_type, typeb, sizeof(typeb));\n\t\t\t\t\tQCC_PR_ParseWarning(WARN_STRICTTYPEMISMATCH, \"Implicit field assignment from %s to %s\", typea, typeb);\n\t\t\t\t}\n\t\t\t}*/ \n\t\t}\n\t\tbreak;\n\n\tcase OP_LOADA_F:\n\tcase OP_LOADA_V:\n\tcase OP_LOADA_S:\n\tcase OP_LOADA_ENT:\n\tcase OP_LOADA_FLD:\n\tcase OP_LOADA_FNC:\n\tcase OP_LOADA_I:\n\t\tbreak;\n\tcase OP_AND_F:\n\t\tif (var_a.sym == var_b.sym && var_a.ofs == var_b.ofs)\n\t\t\tQCC_PR_ParseWarning(WARN_CONSTANTCOMPARISON, \"Parameter offsets for && are the same\");\n\t\tif (var_a.sym && var_b.sym && (var_a.sym->constant && var_b.sym->constant))\n\t\t{\n\t\t\tQCC_PR_ParseWarning(WARN_CONSTANTCOMPARISON, \"Result of comparison is constant\");\n\t\t\tQCC_PR_ParsePrintDef(WARN_CONSTANTCOMPARISON, var_a.sym);\n\t\t\tQCC_PR_ParsePrintDef(WARN_CONSTANTCOMPARISON, var_b.sym);\n\t\t}\n\t\tbreak;\n\tcase OP_OR_F:\n\t\tif (var_a.sym == var_b.sym && var_a.ofs == var_b.ofs)\n\t\t\tQCC_PR_ParseWarning(WARN_CONSTANTCOMPARISON, \"Parameters for || are the same\");\n\t\tif (var_a.sym && var_b.sym && (var_a.sym->constant || var_b.sym->constant))\n\t\t{\n\t\t\tQCC_PR_ParseWarning(WARN_CONSTANTCOMPARISON, \"Result of comparison is constant\");\n\t\t\tQCC_PR_ParsePrintDef(WARN_CONSTANTCOMPARISON, var_a.sym);\n\t\t\tQCC_PR_ParsePrintDef(WARN_CONSTANTCOMPARISON, var_b.sym);\n\t\t}\n\t\tbreak;\n\n\tcase OP_EQ_F:\n\tcase OP_NE_F:\n\tcase OP_EQ_V:\n\tcase OP_NE_V:\n\tcase OP_LE_F:\n\tcase OP_GE_F:\n\t\tnan_eq_cond = true;\n\tcase OP_EQ_S:\n\tcase OP_EQ_E:\n\tcase OP_EQ_FNC:\n//\t\tif (opt_shortenifnots)\n//\t\t\tif (var_b->constant && ((int*)qcc_pr_globals)[var_b->ofs]==0)\t// (a == 0) becomes (!a)\n//\t\t\t\top = &pr_opcodes[(op - pr_opcodes) - OP_EQ_F + OP_NOT_F];\n\n\tcase OP_NE_S:\n\tcase OP_NE_E:\n\tcase OP_NE_FNC:\n\n\tcase OP_LT_F:\n\tcase OP_GT_F:\n\t\tif (typecmp_lax(var_a.cast, var_b.cast))\n\t\t{\n\t\t\tQCC_type_t *t;\n\t\t\t//simplify a, see if we can get an inherited comparison\n\t\t\tfor (t = var_a.cast; t; t = t->parentclass)\n\t\t\t{\n\t\t\t\tif (typecmp_lax(t, var_b.cast))\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (t)\n\t\t\t\tbreak;\n\t\t\t//now try with b simplified\n\t\t\tfor (t = var_b.cast; t; t = t->parentclass)\n\t\t\t{\n\t\t\t\tif (typecmp_lax(var_a.cast, t))\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (t)\n\t\t\t\tbreak;\n\t\t\t//if both need to simplify then the classes are too diverse\n\t\t\tTypeName(var_a.cast, typea, sizeof(typea));\n\t\t\tTypeName(var_b.cast, typeb, sizeof(typeb));\n\t\t\tQCC_PR_ParseWarning(WARN_STRICTTYPEMISMATCH, \"'%s' type mismatch: %s with %s\", op->name, typea, typeb);\n\t\t}\n\n\t\tsym_cmp = !nan_eq_cond && var_a.sym == var_b.sym && var_a.ofs == var_b.ofs;\n\n\t\tif ((var_a.sym->constant && var_b.sym->constant && !var_a.sym->temp && !var_b.sym->temp) || sym_cmp)\n\t\t{\n\t\t\tQCC_PR_ParseWarning(WARN_CONSTANTCOMPARISON, \"Result of comparison is constant\");\n\t\t\tQCC_PR_ParsePrintDef(WARN_CONSTANTCOMPARISON, var_a.sym);\n\t\t\tQCC_PR_ParsePrintDef(WARN_CONSTANTCOMPARISON, var_b.sym);\n\t\t\t//Note: EQ_S and NE_S compares the pointed-to data, rather than the pointers themselves. it would be too unsafe to optimise these\n\t\t\t//fixme: fold other comparisons.\n\t\t}\n\t\tbreak;\n\tcase OP_IF_S:\n\tcase OP_IFNOT_S:\n\tcase OP_IF_F:\n\tcase OP_IFNOT_F:\n\tcase OP_IF_I:\n\tcase OP_IFNOT_I:\n//\t\tif (var_a.cast->type == ev_function && !var_a.sym->temp)\n//\t\t\tQCC_PR_ParseWarning(WARN_CONSTANTCOMPARISON, \"Result of comparison is constant\");\n//\t\tif (!var_a.sym || var_a.sym->constant && !var_a.sym->temp)\n//\t\t\tQCC_PR_ParseWarning(WARN_CONSTANTCOMPARISON, \"Result of comparison is constant\");\n\t\tbreak;\n\tdefault:\n\t\tbreak;\n\t}\n\n\tif (numstatements && !(statements[numstatements-1].flags&STF_NOFOLD))\n\t{\t//optimise based on last statement.\n\t\tif (op - pr_opcodes == OP_IFNOT_I)\n\t\t{\n\t\t\tif (opt_shortenifnots && var_a.cast && var_a.sym->temp && var_a.sym->refcount == 1 && (statements[numstatements-1].op == OP_NOT_F || statements[numstatements-1].op == OP_NOT_FNC || statements[numstatements-1].op == OP_NOT_ENT))\n\t\t\t{\n\t\t\t\tif (statements[numstatements-1].c.sym == var_a.sym && statements[numstatements-1].c.ofs == var_a.ofs)\n\t\t\t\t{\n\t\t\t\t\tif (statements[numstatements-1].op == OP_NOT_F && QCC_OPCodeValid(&pr_opcodes[OP_IF_F]))\n\t\t\t\t\t\top = &pr_opcodes[OP_IF_F];\n\t\t\t\t\telse\n\t\t\t\t\t\top = &pr_opcodes[OP_IF_I];\n\t\t\t\t\tnumstatements--;\n\t\t\t\t\tQCC_FreeTemp(var_a);\n\t\t\t\t\tvar_a = statements[numstatements].a;\n\t\t\t\t\tQCC_ForceUnFreeDef(var_a.sym);\n\n\t\t\t\t\toptres_shortenifnots++;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse if (op - pr_opcodes == OP_IFNOT_F)\n\t\t{\n\t\t\tif (opt_shortenifnots && var_a.cast && var_a.sym->temp && var_a.sym->refcount == 1 && statements[numstatements-1].op == OP_NOT_F)\n\t\t\t{\n\t\t\t\tif (statements[numstatements-1].c.sym == var_a.sym && statements[numstatements-1].c.ofs == var_a.ofs)\n\t\t\t\t{\n\t\t\t\t\top = &pr_opcodes[OP_IF_F];\n\t\t\t\t\tnumstatements--;\n\t\t\t\t\tQCC_FreeTemp(var_a);\n\t\t\t\t\tvar_a = statements[numstatements].a;\n\t\t\t\t\tQCC_ForceUnFreeDef(var_a.sym);\n\n\t\t\t\t\toptres_shortenifnots++;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse if (op - pr_opcodes == OP_IFNOT_S)\n\t\t{\n\t\t\tif (opt_shortenifnots && var_a.cast && var_a.sym->temp && var_a.sym->refcount == 1 && statements[numstatements-1].op == OP_NOT_S)\n\t\t\t{\n\t\t\t\tif (statements[numstatements-1].c.sym == var_a.sym && statements[numstatements-1].c.ofs == var_a.ofs)\n\t\t\t\t{\n\t\t\t\t\top = &pr_opcodes[OP_IF_S];\n\t\t\t\t\tnumstatements--;\n\t\t\t\t\tQCC_FreeTemp(var_a);\n\t\t\t\t\tvar_a = statements[numstatements].a;\n\t\t\t\t\tQCC_ForceUnFreeDef(var_a.sym);\n\n\t\t\t\t\toptres_shortenifnots++;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse if (((unsigned) ((op - pr_opcodes) - OP_STORE_F) < 6) || (op-pr_opcodes) == OP_STORE_P || (op-pr_opcodes) == OP_STORE_I || (op-pr_opcodes) == OP_STORE_I64)\n\t\t{\n\t\t\t// remove assignments if what should be assigned is the 3rd operand of the previous statement?\n\t\t\t// don't if it's a call, callH, switch or case\n\t\t\t// && var_a->ofs >RESERVED_OFS)\n\t\t\tif (OpAssignsToC(statements[numstatements-1].op) &&\n\t\t\t    opt_assignments && var_a.cast && var_a.sym == statements[numstatements-1].c.sym && var_a.ofs == statements[numstatements-1].c.ofs)\n\t\t\t{\n\t\t\t\tif (var_a.cast->type == var_b.cast->type)\n\t\t\t\t{\n\t\t\t\t\tif (var_a.sym && var_b.sym && var_a.sym->temp && var_a.sym->refcount==1)\n\t\t\t\t\t{\n\t\t\t\t\t\tstatement = &statements[numstatements-1];\n\t\t\t\t\t\tstatement->c = var_b;\n\n\t\t\t\t\t\tif (var_a.cast->type != var_b.cast->type)\n\t\t\t\t\t\t\tQCC_PR_ParseWarning(0, \"store type mismatch\");\n\t\t\t\t\t\tvar_b.sym->referenced=true;\n\t\t\t\t\t\tvar_a.sym->referenced=true;\n\t\t\t\t\t\tQCC_FreeTemp(var_a);\n\t\t\t\t\t\toptres_assignments++;\n\n\t\t\t\t\t\tif (flags&STFL_DISCARDRESULT)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tQCC_FreeTemp(var_b);\n\t\t\t\t\t\t\tvar_b = nullsref;\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn var_b;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse if (op - pr_opcodes == OP_ADD_I && statements[numstatements-1].op == OP_MUL_I && opt_assignments && !flag_undefwordsize)\n\t\t{\n\t\t\t//mul_i idx 4i tmp\n\t\t\t//add_i tmp 2i out\n\t\t\t//becomes add_piw 2i idx out\n\n//\t\t\tconst QCC_eval_t *eval_a = QCC_SRef_EvalConst(var_a);\n\t\t\tconst QCC_eval_t *eval_b = QCC_SRef_EvalConst(statements[numstatements-1].b);\n\n\t\t\tif (eval_b && eval_b->_int == VMWORDSIZE)\n\t\t\t{\n\t\t\t\tif (var_a.cast && var_a.sym == statements[numstatements-1].c.sym && var_a.ofs == statements[numstatements-1].c.ofs)\n\t\t\t\t\tif (var_a.sym && var_b.sym && var_a.sym->temp && var_a.sym->refcount==1)\n\t\t\t\t\t{\n\t\t\t\t\t\top = &pr_opcodes[OP_ADD_PIW];\n\t\t\t\t\t\tQCC_FreeDef(var_a.sym);\n\t\t\t\t\t\tvar_a = var_b;\n\t\t\t\t\t\tvar_b = statements[numstatements-1].a;\n\t\t\t\t\t\tnumstatements--;\n\t\t\t\t\t\tQCC_ForceUnFreeDef(var_b.sym);\n\t\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tif (!QCC_OPCodeValid(op) && !(flags&STFL_NOEMULATE))\n\t{\n#define QCC_PR_EmulationFunc(n) QCC_PR_GetSRef(NULL, #n, pr_scope, false, 0, 0)\n\t\tQCC_sref_t tmp;\n//FIXME: add support for flags so we don't corrupt temps\n\t\tswitch(op - pr_opcodes)\n\t\t{\n\t\tcase OP_LOADA_STRUCT:\n\t\t\t/*emit this anyway. if it reaches runtime then you messed up.\n\t\t\tthis is valid only if you do &foo[0]*/\n//\t\t\tQCC_PR_ParseWarning(0, \"OP_LOADA_STRUCT: cannot emulate\");\n\t\t\tbreak;\n\n\t\tcase OP_ADD_SF:\n\t\t\tvar_c = QCC_PR_EmulationFunc(AddStringFloat);\n\t\t\tif (var_c.cast)\n\t\t\t\tvar_c = QCC_PR_GenerateFunctionCall2(nullsref, var_c, var_a, type_string, var_b, type_float);\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (!var_a.sym->constant)\n\t\t\t\t\tQCC_PR_ParseWarning(WARN_STRINGOFFSET, \"OP_ADD_SF: string+float may be unsafe\");\n\t\t\t\tvar_b = QCC_SupplyConversion(var_b, ev_integer, true);\t//FIXME: this should be an unconditional float->int conversion\n\t\t\t\tvar_c = QCC_PR_StatementFlags(&pr_opcodes[OP_ADD_I], var_a, var_b, NULL, 0);\n\t\t\t}\n\t\t\tvar_c.cast = type_string;\n\t\t\treturn var_c;\n\n\t\tcase OP_ADD_EF:\n\t\tcase OP_SUB_EF:\n\t\t\tif (flag_qccx)\t//no implicit cast. qccx always uses denormalised floats everywhere.\n\t\t\t\tQCC_PR_ParseWarning(WARN_DENORMAL, \"OP_ADD_EF: qccx entity offsets are unsafe, and denormals are unsafe\");\t\n\t\t\telse if (1)\n\t\t\t{\t//slightly better defined.\n\t\t\t\tvar_b = QCC_PR_StatementFlags(&pr_opcodes[OP_CONV_FTOI], var_b, nullsref, NULL, (flags&STFL_PRESERVEB)?STFL_PRESERVEA:0);\n\t\t\t\treturn QCC_PR_StatementFlags(&pr_opcodes[(op==&pr_opcodes[OP_ADD_EF])?OP_ADD_EI:OP_SUB_EI], var_a, var_b, NULL, flags&STFL_PRESERVEA);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tvar_c = QCC_PR_EmulationFunc(nextent);\n\t\t\t\tif (!var_c.cast)\n\t\t\t\t{\n\t\t\t\t\tQCC_PR_ParseWarning(0, \"the nextent builtin is not defined\");\n\t\t\t\t\tgoto badopcode;\n\t\t\t\t}\n\t\t\t\tQCC_PR_ParseWarning(WARN_DENORMAL, \"OP_ADD_EF: denormals are unsafe\");\n\t\t\t\tvar_c = QCC_PR_GenerateFunctionCall1 (nullsref, var_c, QCC_MakeIntConst(0), type_entity);\n\t\t\t\tvar_b = QCC_PR_StatementFlags(&pr_opcodes[OP_CONV_FTOI], var_b, nullsref, NULL, flags&STFL_PRESERVEB);\n\t\t\t\tvar_b = QCC_PR_StatementFlags(&pr_opcodes[OP_MUL_F], var_c, var_b, NULL, 0);\n\t\t\t\tflags&=~STFL_PRESERVEB;\n\t\t\t}\n\t\t\tvar_c = QCC_PR_StatementFlags(&pr_opcodes[(op==&pr_opcodes[OP_ADD_EF])?OP_ADD_IF:OP_SUB_F], var_a, var_b, NULL, flags);\n\t\t\tvar_c.cast = type_entity;\n\t\t\treturn var_c;\n\n\t\tcase OP_ADD_EI:\n\t\tcase OP_SUB_EI:\n\t\t\tif (flag_qccx)\n\t\t\t\tQCC_PR_ParseWarning(0, \"qccx entity offsets are unsafe\");\t\n\t\t\telse\n\t\t\t{\n\t\t\t\tvar_c = QCC_PR_EmulationFunc(nextent);\n\t\t\t\tif (!var_c.cast)\n\t\t\t\t{\n\t\t\t\t\tQCC_PR_ParseWarning(0, \"the nextent builtin is not defined\");\n\t\t\t\t\tgoto badopcode;\n\t\t\t\t}\n\t\t\t\tvar_c = QCC_PR_GenerateFunctionCall1 (nullsref, var_c, QCC_MakeIntConst(0), type_entity);\n\t\t\t\tvar_b = QCC_PR_StatementFlags(&pr_opcodes[OP_MUL_I], var_c, var_b, NULL, flags&STFL_PRESERVEB);\n\t\t\t\tflags&=~STFL_PRESERVEB;\n\t\t\t}\n\t\t\tvar_c = QCC_PR_StatementFlags(&pr_opcodes[(op==&pr_opcodes[OP_ADD_EF])?OP_ADD_I:OP_SUB_I], var_a, var_b, NULL, flags);\n\t\t\tvar_c.cast = type_entity;\n\t\t\treturn var_c;\n\n\t\tcase OP_ADD_SI:\n\t\tcase OP_ADD_IS:\n\t\t\tQCC_PR_ParseWarning(WARN_STRINGOFFSET, \"OP_ADD_SI: string+int may be unsafe\");\n\t\t\tvar_c = QCC_PR_StatementFlags(&pr_opcodes[OP_ADD_I], var_a, var_b, NULL, 0);\n\t\t\tvar_c.cast = type_string;\n\t\t\treturn var_c;\n\t\tcase OP_ADD_PF:\n\t\tcase OP_ADD_FP:\n\t\tcase OP_ADD_PI:\n\t\tcase OP_ADD_IP:\n\t\tcase OP_ADD_PU:\n\t\tcase OP_ADD_UP:\n\t\t\t{\n\t\t\t\tQCC_type_t *t;\n\t\t\t\tvar_c = (op == &pr_opcodes[OP_ADD_PF] || op == &pr_opcodes[OP_ADD_PI] || op == &pr_opcodes[OP_ADD_PU])?var_a:var_b;\t//ptr\n\t\t\t\tvar_b = (op == &pr_opcodes[OP_ADD_PF] || op == &pr_opcodes[OP_ADD_PI] || op == &pr_opcodes[OP_ADD_PU])?var_b:var_a;\t//idx\n\t\t\t\tt = var_c.cast;\n\t\t\t\tif (op == &pr_opcodes[OP_ADD_FP] || op == &pr_opcodes[OP_ADD_PF])\n\t\t\t\t\tvar_b = QCC_SupplyConversion(var_b, ev_integer, true);\t//FIXME: this should be an unconditional float->int conversion\n\t\t\t\tif (t->aux_type->type == ev_void)\t//void* is treated as a byte type.\n\t\t\t\t{\n\t\t\t\t\tif (flag_undefwordsize)\n\t\t\t\t\t\tQCC_PR_ParseWarning(ERR_BADEXTENSION, \"void* maths was disabled.\");\n\t\t\t\t\tvar_c = QCC_PR_StatementFlags(&pr_opcodes[OP_ADD_I], var_c, var_b, NULL, 0);\n\t\t\t\t}\n\t\t\t\telse if (t->aux_type->bits)\t//awkward bittyness\n\t\t\t\t{\n\t\t\t\t\tif (flag_undefwordsize)\n\t\t\t\t\t\tQCC_PR_ParseWarning(ERR_BADEXTENSION, \"pointer* maths was disabled on this type.\");\n\t\t\t\t\tif (t->aux_type->bits != 8)\n\t\t\t\t\t\tvar_b = QCC_PR_StatementFlags(&pr_opcodes[OP_MUL_I], var_b, QCC_MakeIntConst(t->aux_type->bits/8), NULL, 0);\n\t\t\t\t\tvar_c = QCC_PR_StatementFlags(&pr_opcodes[OP_ADD_I], var_c, var_b, NULL, 0); //PIW doesn't make sense here.\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tvar_b = QCC_PR_StatementFlags(&pr_opcodes[OP_MUL_I], var_b, QCC_MakeIntConst(t->aux_type->size), NULL, 0);\n\t\t\t\t\tvar_c = QCC_PR_StatementFlags(&pr_opcodes[OP_ADD_PIW], var_c, var_b, NULL, 0);\n\t\t\t\t}\n\t\t\t\tvar_c.cast = t;\n\t\t\t}\n\t\t\treturn var_c;\n\t\tcase OP_SUB_PF:\n\t\tcase OP_SUB_PI:\n\t\tcase OP_SUB_PU:\n\t\t\tvar_c = var_a;\n\t\t\tif (op == &pr_opcodes[OP_SUB_PF])\n\t\t\t\tvar_b = QCC_SupplyConversion(var_b, ev_integer, true);\t//FIXME: this should be an unconditional float->int conversion\n\t\t\tif (var_c.cast->aux_type->type == ev_void)\t//void* is treated as a byte type.\n\t\t\t{\n\t\t\t\tif (flag_undefwordsize)\n\t\t\t\t\tQCC_PR_ParseWarning(ERR_BADEXTENSION, \"void* maths was disabled.\");\n\t\t\t\tvar_c = QCC_PR_StatementFlags(&pr_opcodes[OP_SUB_I], var_c, var_b, NULL, 0);\n\t\t\t}\n\t\t\telse if (var_c.cast->aux_type->bits)\t//awkward bittyness\n\t\t\t{\n\t\t\t\tif (flag_undefwordsize)\n\t\t\t\t\tQCC_PR_ParseWarning(ERR_BADEXTENSION, \"pointer* maths was disabled on this type.\");\n\t\t\t\tif (var_c.cast->aux_type->bits!=8)\n\t\t\t\t\tvar_b = QCC_PR_StatementFlags(&pr_opcodes[OP_MUL_I], var_b, QCC_MakeIntConst(var_c.cast->aux_type->bits/8), NULL, 0);\t//negated. this is a subtract after all.\n\t\t\t\tvar_c = QCC_PR_StatementFlags(&pr_opcodes[OP_SUB_I], var_c, var_b, NULL, 0); //PIW doesn't make sense here.\n\t\t\t}\n\t\t\telse if (1)\n\t\t\t{\n\t\t\t\tvar_b = QCC_PR_StatementFlags(&pr_opcodes[OP_MUL_I], var_b, QCC_MakeIntConst(-var_c.cast->aux_type->size), NULL, 0);\n\t\t\t\tvar_c = QCC_PR_StatementFlags(&pr_opcodes[OP_ADD_PIW], var_c, var_b/*negative, making this a subtraction*/, NULL, 0);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tQCC_sref_t idx = QCC_PR_Statement(&pr_opcodes[OP_ADD_PIW], QCC_MakeIntConst(0), QCC_MakeIntConst(var_c.cast->aux_type->size), NULL);\n\t\t\t\tvar_b = QCC_PR_StatementFlags(&pr_opcodes[OP_MUL_I], var_b, idx, NULL, 0);\n\t\t\t\tvar_c = QCC_PR_StatementFlags(&pr_opcodes[OP_SUB_I], var_c, var_b, NULL, 0);\n\t\t\t}\n\t\t\tvar_c.cast = var_a.cast;\n\t\t\treturn var_c;\n\t\tcase OP_SUB_PP:\n\t\t\tif (typecmp(var_a.cast, var_b.cast))\n\t\t\t\tQCC_PR_ParseError(ERR_BADEXTENSION, \"incompatible pointer types\");\n\t\t\t//determine byte offset\n\t\t\tvar_c = QCC_PR_StatementFlags(&pr_opcodes[OP_SUB_I], var_a, var_b, NULL, 0);\n\t\t\tif (var_a.cast->aux_type->type == ev_void)\n\t\t\t{\n\t\t\t\tif (flag_undefwordsize)\n\t\t\t\t\tQCC_PR_ParseWarning(ERR_BADEXTENSION, \"void* maths was disabled.\");\n\t\t\t\treturn var_c;\t//we're done if we're using void/bytes\n\t\t\t}\n\t\t\t//determine divisor\n\t\t\tif (var_a.cast->aux_type->bits)\n\t\t\t{\n\t\t\t\tif (flag_undefwordsize)\n\t\t\t\t\tQCC_PR_ParseWarning(ERR_BADEXTENSION, \"pointer sizes were marked as undefined.\");\n\t\t\t\tvar_b = QCC_MakeIntConst(var_a.cast->aux_type->bits/8);\n\t\t\t}\n\t\t\telse\n\t\t\t\tvar_b = QCC_PR_Statement(&pr_opcodes[OP_ADD_PIW], QCC_MakeIntConst(0), QCC_MakeIntConst(var_a.cast->aux_type->size), NULL);\n\t\t\t//divide the result\n\t\t\treturn QCC_PR_StatementFlags(&pr_opcodes[OP_DIV_I], var_c, var_b, NULL, 0);\n\n\t\tcase OP_BITAND_I:\n\t\t\t{\n\t\t\t\tQCC_sref_t fnc = QCC_PR_EmulationFunc(BitandInt);\n\t\t\t\tif (!fnc.cast)\n\t\t\t\t{\n\t\t\t\t\tQCC_PR_ParseWarning(0, \"BitandInt function not defined: cannot emulate int&int\");\n\t\t\t\t\tgoto badopcode;\n\t\t\t\t}\n\t\t\t\tvar_c = QCC_PR_GenerateFunctionCall2(nullsref, fnc, var_a, type_integer, var_b, type_integer);\n\t\t\t\tvar_c.cast = type_integer;\n\t\t\t\treturn var_c;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase OP_BITOR_I:\n\t\t\t{\n\t\t\t\tQCC_sref_t fnc = QCC_PR_EmulationFunc(BitorInt);\n\t\t\t\tif (!fnc.cast)\n\t\t\t\t{\n\t\t\t\t\tQCC_PR_ParseWarning(0, \"BitorInt function not defined: cannot emulate int|int\");\n\t\t\t\t\tgoto badopcode;\n\t\t\t\t}\n\t\t\t\tvar_c = QCC_PR_GenerateFunctionCall2(nullsref, fnc, var_a, type_integer, var_b, type_integer);\n\t\t\t\tvar_c.cast = type_integer;\n\t\t\t\treturn var_c;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase OP_BITAND_V:\n\t\tcase OP_BITOR_V:\n\t\t\top = &pr_opcodes[((op - pr_opcodes)==OP_BITAND_V)?OP_BITAND_F:OP_BITOR_F];\n\t\t\tvar_c = QCC_GetTemp(type_vector);\n\t\t\tvar_a.cast = type_float;\n\t\t\tvar_b.cast = type_float;\n\t\t\tvar_c.cast = type_float;\n\t\t\tQCC_PR_SimpleStatement(op, var_a, var_b, var_c, true);\n\t\t\tvar_a.ofs++; var_b.ofs++; var_c.ofs++;\n\t\t\tQCC_PR_SimpleStatement(op, var_a, var_b, var_c, true);\n\t\t\tvar_a.ofs++; var_b.ofs++; var_c.ofs++;\n\t\t\tQCC_PR_SimpleStatement(op, var_a, var_b, var_c, true);\n\t\t\tvar_a.ofs++; var_b.ofs++; var_c.ofs++;\n\t\t\tvar_c.cast = type_vector;\n\t\t\tvar_c.ofs -= 3;\n\t\t\treturn var_c;\n\t\tcase OP_ADD_I:\n\t\t\t{\n\t\t\t\tQCC_sref_t fnc = QCC_PR_EmulationFunc(AddInt);\n\t\t\t\tif (!fnc.cast)\n\t\t\t\t{\n\t\t\t\t\tQCC_PR_ParseWarning(0, \"AddInt function not defined: cannot emulate int+int\");\n\t\t\t\t\tgoto badopcode;\n\t\t\t\t}\n\t\t\t\tvar_c = QCC_PR_GenerateFunctionCall2(nullsref, fnc, var_a, type_integer, var_b, type_integer);\n\t\t\t\tvar_c.cast = type_integer;\n\t\t\t\treturn var_c;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase OP_MOD_F:\n\t\t\t{\n\t\t\t\tQCC_sref_t fnc = QCC_PR_EmulationFunc(mod);\n\t\t\t\tif (!fnc.cast)\n\t\t\t\t{\n\t\t\t\t\t//a - (n * floor(a/n));\n\t\t\t\t\t//(except using v|v instead of floor)\n\t\t\t\t\tvar_c = QCC_PR_StatementFlags(&pr_opcodes[OP_DIV_F], var_a, var_b, NULL, STFL_PRESERVEA|STFL_PRESERVEB);\n\t\t\t\t\tvar_c = QCC_PR_StatementFlags(&pr_opcodes[OP_BITOR_F], var_c, var_c, NULL, STFL_PRESERVEA);\n\t\t\t\t\tvar_c = QCC_PR_Statement(&pr_opcodes[OP_MUL_F], var_b, var_c, NULL);\n\t\t\t\t\treturn QCC_PR_Statement(&pr_opcodes[OP_SUB_F], var_a, var_c, NULL);\n\n//\t\t\t\t\tQCC_PR_ParseError(0, \"mod function not defined: cannot emulate float%%float\");\n\t\t\t\t}\n\t\t\t\tvar_c = QCC_PR_GenerateFunctionCall2(nullsref, fnc, var_a, type_float, var_b, type_float);\n\t\t\t\tvar_c.cast = type_float;\n\t\t\t\treturn var_c;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase OP_MOD_I:\n\t\t\t{\n\t\t\t\tQCC_sref_t fnc = QCC_PR_EmulationFunc(ModInt);\n\t\t\t\tif (!fnc.cast)\n\t\t\t\t{\n\t\t\t\t\t//a - (n * floor(a/n));\n\t\t\t\t\t//(except using v|v instead of floor)\n\t\t\t\t\tvar_c = QCC_PR_StatementFlags(&pr_opcodes[OP_DIV_I], var_a, var_b, NULL, STFL_PRESERVEA|STFL_PRESERVEB);\n\t\t\t\t\t//var_c = QCC_PR_StatementFlags(&pr_opcodes[OP_BITOR_I], var_c, var_c, NULL, STFL_PRESERVEA);\n\t\t\t\t\tvar_c = QCC_PR_Statement(&pr_opcodes[OP_MUL_I], var_b, var_c, NULL);\n\t\t\t\t\treturn QCC_PR_Statement(&pr_opcodes[OP_SUB_I], var_a, var_c, NULL);\n\n//\t\t\t\t\tQCC_PR_ParseError(0, \"mod function not defined: cannot emulate int%%int\");\n\t\t\t\t}\n\t\t\t\tvar_c = QCC_PR_GenerateFunctionCall2(nullsref, fnc, var_a, type_integer, var_b, type_integer);\n\t\t\t\tvar_c.cast = type_integer;\n\t\t\t\treturn var_c;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase OP_MOD_U:\n\t\t\t{\n\t\t\t\tQCC_sref_t fnc = QCC_PR_EmulationFunc(ModInt);\n\t\t\t\tif (!fnc.cast)\n\t\t\t\t{\n\t\t\t\t\t//a - (n * floor(a/n));\n\t\t\t\t\t//(except using v|v instead of floor)\n\t\t\t\t\tvar_c = QCC_PR_StatementFlags(&pr_opcodes[OP_DIV_U], var_a, var_b, NULL, STFL_PRESERVEA|STFL_PRESERVEB);\n\t\t\t\t\t//var_c = QCC_PR_StatementFlags(&pr_opcodes[OP_BITOR_U], var_c, var_c, NULL, STFL_PRESERVEA);\n\t\t\t\t\tvar_c = QCC_PR_Statement(&pr_opcodes[OP_MUL_U], var_b, var_c, NULL);\n\t\t\t\t\treturn QCC_PR_Statement(&pr_opcodes[OP_SUB_U], var_a, var_c, NULL);\n\n//\t\t\t\t\tQCC_PR_ParseError(0, \"mod function not defined: cannot emulate int%%int\");\n\t\t\t\t}\n\t\t\t\tvar_c = QCC_PR_GenerateFunctionCall2(nullsref, fnc, var_a, type_integer, var_b, type_integer);\n\t\t\t\tvar_c.cast = type_integer;\n\t\t\t\treturn var_c;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase OP_MOD_FI:\n\t\t\t{\n\t\t\t\tQCC_sref_t fnc = QCC_PR_EmulationFunc(mod);\n\t\t\t\tif (!fnc.cast)\n\t\t\t\t{\n\t\t\t\t\t//a - (n * floor(a/n));\n\t\t\t\t\t//(except using v|v instead of floor)\n\t\t\t\t\tvar_c = QCC_PR_StatementFlags(&pr_opcodes[OP_DIV_FI], var_a, var_b, NULL, STFL_PRESERVEA|STFL_PRESERVEB);\n\t\t\t\t\tvar_c = QCC_PR_StatementFlags(&pr_opcodes[OP_BITOR_F], var_c, var_c, NULL, STFL_PRESERVEA);\n\t\t\t\t\tvar_c = QCC_PR_Statement(&pr_opcodes[OP_MUL_IF], var_b, var_c, NULL);\n\t\t\t\t\treturn QCC_PR_Statement(&pr_opcodes[OP_SUB_F], var_a, var_c, NULL);\n\n//\t\t\t\t\tQCC_PR_ParseError(0, \"mod function not defined: cannot emulate float%%int\");\n\t\t\t\t}\n\t\t\t\tvar_b = QCC_PR_StatementFlags(&pr_opcodes[OP_CONV_FTOI], var_b, nullsref, NULL, (flags&STFL_PRESERVEB)?STFL_PRESERVEA:0);\n\t\t\t\tvar_c = QCC_PR_GenerateFunctionCall2(nullsref, fnc, var_a, type_float, var_b, type_float);\n\t\t\t\tvar_c.cast = type_float;\n\t\t\t\treturn var_c;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase OP_MOD_IF:\n\t\t\t{\n\t\t\t\tQCC_sref_t fnc = QCC_PR_EmulationFunc(mod);\n\t\t\t\tif (!fnc.cast)\n\t\t\t\t{\n\t\t\t\t\t//a - (n * floor(a/n));\n\t\t\t\t\t//(except using v|v instead of floor)\n\t\t\t\t\tvar_c = QCC_PR_StatementFlags(&pr_opcodes[OP_DIV_IF], var_a, var_b, NULL, STFL_PRESERVEA|STFL_PRESERVEB);\n\t\t\t\t\tvar_c = QCC_PR_StatementFlags(&pr_opcodes[OP_BITOR_F], var_c, var_c, NULL, STFL_PRESERVEA);\n\t\t\t\t\tvar_c = QCC_PR_Statement(&pr_opcodes[OP_MUL_F], var_b, var_c, NULL);\n\t\t\t\t\treturn QCC_PR_Statement(&pr_opcodes[OP_SUB_IF], var_a, var_c, NULL);\n\n//\t\t\t\t\tQCC_PR_ParseError(0, \"mod function not defined: cannot emulate int%%float\");\n\t\t\t\t}\n\t\t\t\tvar_a = QCC_PR_StatementFlags(&pr_opcodes[OP_CONV_FTOI], var_a, nullsref, NULL, flags&STFL_PRESERVEA);\n\t\t\t\tvar_c = QCC_PR_GenerateFunctionCall2(nullsref, fnc, var_a, type_float, var_b, type_float);\n\t\t\t\tvar_c.cast = type_float;\n\t\t\t\treturn var_c;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase OP_MOD_V:\n\t\t\t{\n\t\t\t\tQCC_sref_t fnc = QCC_PR_EmulationFunc(ModVec);\n\t\t\t\tif (!fnc.cast)\n\t\t\t\t{\n\t\t\t\t\tQCC_PR_ParseWarning(0, \"ModVec function not defined: cannot emulate vector%%vector\");\n\t\t\t\t\tgoto badopcode;\n\t\t\t\t}\n\t\t\t\tvar_c = QCC_PR_GenerateFunctionCall2(nullsref, fnc, var_a, type_vector, var_b, type_vector);\n\t\t\t\tvar_c.cast = type_vector;\n\t\t\t\treturn var_c;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase OP_SUB_I:\n\t\t\t{\n\t\t\t\tQCC_sref_t fnc = QCC_PR_EmulationFunc(SubInt);\n\t\t\t\tif (!fnc.cast)\n\t\t\t\t{\n\t\t\t\t\tQCC_PR_ParseWarning(0, \"SubInt function not defined: cannot emulate int-int\");\n\t\t\t\t\tgoto badopcode;\n\t\t\t\t}\n\t\t\t\tvar_c = QCC_PR_GenerateFunctionCall2(nullsref, fnc, var_a, type_integer, var_b, type_integer);\n\t\t\t\tvar_c.cast = type_integer;\n\t\t\t\treturn var_c;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase OP_MUL_I:\n\t\t\t{\n\t\t\t\tQCC_sref_t fnc = QCC_PR_EmulationFunc(MulInt);\n\t\t\t\tif (!fnc.cast)\n\t\t\t\t{\n\t\t\t\t\tQCC_PR_ParseWarning(0, \"MulInt function not defined: cannot emulate int*int\");\n\t\t\t\t\tgoto badopcode;\n\t\t\t\t}\n\t\t\t\tvar_c = QCC_PR_GenerateFunctionCall2(nullsref, fnc, var_a, type_integer, var_b, type_integer);\n\t\t\t\tvar_c.cast = type_integer;\n\t\t\t\treturn var_c;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase OP_DIV_I:\n\t\t\t{\n\t\t\t\tQCC_sref_t fnc = QCC_PR_EmulationFunc(DivInt);\n\t\t\t\tif (!fnc.cast)\n\t\t\t\t{\n\t\t\t\t\tQCC_PR_ParseWarning(0, \"DivInt function not defined: cannot emulate int/int\");\n\t\t\t\t\tgoto badopcode;\n\t\t\t\t}\n\t\t\t\tvar_c = QCC_PR_GenerateFunctionCall2(nullsref, fnc, var_a, type_integer, var_b, type_integer);\n\t\t\t\tvar_c.cast = type_integer;\n\t\t\t\treturn var_c;\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase OP_DIV_VF:\n\t\t\t//v/f === v*(1/f)\n\t\t\top = &pr_opcodes[OP_MUL_VF];\n\t\t\tvar_b = QCC_PR_Statement(&pr_opcodes[OP_DIV_F], QCC_MakeFloatConst(1), var_b, NULL);\n\t\t\tvar_b.sym->referenced = true;\n\t\t\tbreak;\n\n\t\tcase OP_POW_F:\n\t\t\t{\n\t\t\t\tQCC_sref_t fnc = QCC_PR_EmulationFunc(powf);\n\t\t\t\tif (!fnc.cast)\n\t\t\t\t\tfnc = QCC_PR_EmulationFunc(pow);\n\t\t\t\tif (!fnc.cast)\n\t\t\t\t{\n\t\t\t\t\tQCC_PR_ParseWarning(0, \"pow function not defined: cannot emulate float*^float\");\n\t\t\t\t\tgoto badopcode;\n\t\t\t\t}\n\t\t\t\tvar_c = QCC_PR_GenerateFunctionCall2(nullsref, fnc, var_a, type_float, var_b, type_float);\n\t\t\t\tvar_c.cast = type_float;\n\t\t\t\treturn var_c;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase OP_POW_I:\n\t\t\t{\n\t\t\t\tQCC_sref_t fnc = QCC_PR_EmulationFunc(powf);\n\t\t\t\tif (!fnc.cast)\n\t\t\t\t\tfnc = QCC_PR_EmulationFunc(pow);\n\t\t\t\tif (!fnc.cast)\n\t\t\t\t{\n\t\t\t\t\tQCC_PR_ParseWarning(0, \"pow function not defined: cannot emulate int*^int\");\n\t\t\t\t\tgoto badopcode;\n\t\t\t\t}\n\t\t\t\tvar_a = QCC_PR_StatementFlags(&pr_opcodes[OP_CONV_ITOF], var_a, nullsref, NULL, flags&STFL_PRESERVEA);\n\t\t\t\tvar_b = QCC_PR_StatementFlags(&pr_opcodes[OP_CONV_ITOF], var_b, nullsref, NULL, (flags&STFL_PRESERVEB)?STFL_PRESERVEA:0);\n\t\t\t\tvar_c = QCC_PR_GenerateFunctionCall2(nullsref, fnc, var_a, type_float, var_b, type_float);\n\t\t\t\tvar_c.cast = type_float;\n\t\t\t\tvar_c = QCC_PR_StatementFlags(&pr_opcodes[OP_CONV_FTOI], var_c, nullsref, NULL, 0);\n\t\t\t\treturn var_c;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase OP_POW_FI:\n\t\t\t{\n\t\t\t\tQCC_sref_t fnc = QCC_PR_EmulationFunc(powf);\n\t\t\t\tif (!fnc.cast)\n\t\t\t\t\tfnc = QCC_PR_EmulationFunc(pow);\n\t\t\t\tif (!fnc.cast)\n\t\t\t\t{\n\t\t\t\t\tQCC_PR_ParseWarning(0, \"pow function not defined: cannot emulate float*^int\");\n\t\t\t\t\tgoto badopcode;\n\t\t\t\t}\n\t\t\t\tvar_b = QCC_PR_StatementFlags(&pr_opcodes[OP_CONV_ITOF], var_b, nullsref, NULL, (flags&STFL_PRESERVEB)?STFL_PRESERVEA:0);\n\t\t\t\tvar_c = QCC_PR_GenerateFunctionCall2(nullsref, fnc, var_a, type_float, var_b, type_float);\n\t\t\t\tvar_c.cast = type_float;\n\t\t\t\treturn var_c;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase OP_POW_IF:\n\t\t\t{\n\t\t\t\tQCC_sref_t fnc = QCC_PR_EmulationFunc(powf);\n\t\t\t\tif (!fnc.cast)\n\t\t\t\t\tfnc = QCC_PR_EmulationFunc(pow);\n\t\t\t\tif (!fnc.cast)\n\t\t\t\t{\n\t\t\t\t\tQCC_PR_ParseWarning(0, \"pow function not defined: cannot emulate int*^float\");\n\t\t\t\t\tgoto badopcode;\n\t\t\t\t}\n\t\t\t\tvar_a = QCC_PR_StatementFlags(&pr_opcodes[OP_CONV_ITOF], var_a, nullsref, NULL, flags&STFL_PRESERVEA);\n\t\t\t\tvar_c = QCC_PR_GenerateFunctionCall2(nullsref, fnc, var_a, type_float, var_b, type_float);\n\t\t\t\tvar_c.cast = type_float;\n\t\t\t\treturn var_c;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase OP_CROSS_V:\n\t\t\t{\n\t\t\t\tQCC_sref_t t;\n\t\t\t\tvar_c = QCC_GetTemp(type_vector);\n\n\t\t\t\tt = QCC_PR_Statement(&pr_opcodes[OP_SUB_F], QCC_PR_Statement(&pr_opcodes[OP_MUL_F], QCC_MakeSRef(var_a.sym, var_a.ofs+1, type_float), QCC_MakeSRef(var_b.sym, var_b.ofs+2, type_float), NULL), QCC_PR_Statement(&pr_opcodes[OP_MUL_F], QCC_MakeSRef(var_a.sym, var_a.ofs+2, type_float), QCC_MakeSRef(var_b.sym, var_b.ofs+1, type_float), NULL), NULL);\n\t\t\t\tQCC_PR_StatementFlags(&pr_opcodes[OP_STORE_F], t, QCC_MakeSRef(var_c.sym, var_c.ofs+0, type_float), NULL, flags&STFL_DISCARDRESULT);\n\n\t\t\t\tt = QCC_PR_Statement(&pr_opcodes[OP_SUB_F], QCC_PR_Statement(&pr_opcodes[OP_MUL_F], QCC_MakeSRef(var_a.sym, var_a.ofs+2, type_float), QCC_MakeSRef(var_b.sym, var_b.ofs+0, type_float), NULL), QCC_PR_Statement(&pr_opcodes[OP_MUL_F], QCC_MakeSRef(var_a.sym, var_a.ofs+0, type_float), QCC_MakeSRef(var_b.sym, var_b.ofs+2, type_float), NULL), NULL);\n\t\t\t\tQCC_PR_StatementFlags(&pr_opcodes[OP_STORE_F], t, QCC_MakeSRef(var_c.sym, var_c.ofs+1, type_float), NULL, flags&STFL_DISCARDRESULT);\n\n\t\t\t\tt = QCC_PR_Statement(&pr_opcodes[OP_SUB_F], QCC_PR_Statement(&pr_opcodes[OP_MUL_F], QCC_MakeSRef(var_a.sym, var_a.ofs+0, type_float), QCC_MakeSRef(var_b.sym, var_b.ofs+1, type_float), NULL), QCC_PR_Statement(&pr_opcodes[OP_MUL_F], QCC_MakeSRef(var_a.sym, var_a.ofs+1, type_float), QCC_MakeSRef(var_b.sym, var_b.ofs+0, type_float), NULL), NULL);\n\t\t\t\tQCC_PR_StatementFlags(&pr_opcodes[OP_STORE_F], t, QCC_MakeSRef(var_c.sym, var_c.ofs+2, type_float), NULL, flags&STFL_DISCARDRESULT);\n\n\t\t\t\treturn var_c;\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase OP_SPACESHIP_F:\n\t\t\t{\t//basically just a subtraction-with-fsign.\n\t\t\t\tconst QCC_eval_t *eval;\n\t\t\t\tQCC_statement_t *patch1;\n\t\t\t\tvar_a = QCC_PR_Statement(&pr_opcodes[OP_SUB_F], var_a, var_b, NULL);\n\t\t\t\teval = QCC_SRef_EvalConst(var_c);\n\t\t\t\tif (eval)\n\t\t\t\t{\n\t\t\t\t\tif (eval->_float < 0)\n\t\t\t\t\t\treturn QCC_MakeFloatConst(-1);\n\t\t\t\t\telse\n\t\t\t\t\t\treturn QCC_MakeFloatConst(eval->_float > 0);\n\t\t\t\t}\n\n\t\t\t\t//hack: make local, not temp. this prevents assignment/temp folding...\n\t\t\t\tvar_c = QCC_MakeSRefForce(QCC_PR_DummyDef(type_float, \"ternary\", pr_scope, 0, NULL, 0, false, GDF_STRIP), 0, type_float);\n\n\t\t\t\t//var_c = a>0;\n\t\t\t\tQCC_PR_SimpleStatement(&pr_opcodes[OP_GT_F], var_a, QCC_MakeFloatConst(0), var_c, true);\n\t\t\t\tpatch1 = QCC_Generate_OP_IFNOT(QCC_PR_StatementFlags(&pr_opcodes[OP_LT_F], var_a, QCC_MakeFloatConst(0), NULL, 0), false);\n\t\t\t\tQCC_PR_SimpleStatement(&pr_opcodes[OP_STORE_F], QCC_MakeFloatConst(-1), var_c, nullsref, true);\n\t\t\t\tpatch1->b.jumpofs = &statements[numstatements] - patch1;\n\t\t\t\treturn var_c;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase OP_SPACESHIP_S:\n\t\t\t{\n\t\t\t\tQCC_sref_t fnc = QCC_PR_EmulationFunc(strcmp);\n\t\t\t\tif (!fnc.cast)\n\t\t\t\t\tQCC_PR_ParseError(0, \"strcmp function not defined: cannot emulate string<=>string\");\n\t\t\t\tvar_c = QCC_PR_GenerateFunctionCall2(nullsref, fnc, var_a, type_string, var_b, type_string);\n\t\t\t\tvar_c.cast = type_float;\n\t\t\t\treturn var_c;\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase OP_CONV_UF:\n\t\t\tvar_c = QCC_PR_EmulationFunc(utof);\n\t\t\tif (!var_c.cast)\n\t\t\t\tvar_c = QCC_PR_EmulationFunc(quake_utof);\n\t\t\tif (var_c.cast)\n\t\t\t{\n\t\t\t\tvar_a = QCC_PR_GenerateFunctionCall1(nullsref, var_c, var_a, type_uint);\n\t\t\t\tvar_a.cast = type_float;\n\t\t\t\treturn var_a;\n\t\t\t}\n\t\t\tvar_c = QCC_PR_EmulationFunc(itof);\n\t\t\tif (!var_c.cast)\n\t\t\t\tvar_c = QCC_PR_EmulationFunc(quake_itof);\n\t\t\tif (var_c.cast)\n\t\t\t{\t//there's some optional args that actually have it treated as unsigned\n\t\t\t\tvar_a = QCC_PR_GenerateFunctionCall3(nullsref, var_c, var_a,type_integer, QCC_MakeFloatConst(0),type_float, QCC_MakeFloatConst(32),type_float);\n\t\t\t\tvar_a.cast = type_float;\n\t\t\t\treturn var_a;\n\t\t\t}\n\n\t\t\t//urgh...\n\t\t\tvar_c = QCC_PR_StatementFlags(&pr_opcodes[OP_CONV_ITOF], var_a, nullsref, NULL, flags&STFL_PRESERVEA);\n\t\t\t/*statement = QCC_Generate_OP_IFNOT(QCC_PR_StatementFlags(&pr_opcodes[OP_LT_F], var_c, QCC_MakeFloatConst(0), NULL, STFL_PRESERVEA), false);\n\t\t\tif (statement)\n\t\t\t{\n\t\t\t\tQCC_PR_SimpleStatement(&pr_opcodes[OP_ADD_F], var_c, QCC_MakeFloatConst(0x100000000), var_c, false);\t//biiig number, to overpower the negative part.\n\t\t\t\tstatement->b.jumpofs = &statements[numstatements] - statement;\n\t\t\t}\n\t\t\telse*/\n\t\t\t\tQCC_PR_ParseWarning(WARN_NOTSTANDARDBEHAVIOUR, \"utof emulation: will break if %s is big\", var_a.sym->name);\n\t\t\treturn var_c;\n\n\t\tcase OP_CONV_FU:\n\t\t\tvar_c = QCC_PR_EmulationFunc(ftou);\n\t\t\tif (!var_c.cast)\n\t\t\t\treturn QCC_PR_StatementFlags(&pr_opcodes[OP_CONV_FTOI], var_a, var_b, NULL, flags&STFL_PRESERVEA);\n\t\t\tvar_a = QCC_PR_GenerateFunctionCall1(nullsref, var_c, var_a, type_float);\n\t\t\tvar_a.cast = type_uint;\n\t\t\treturn var_a;\n\t\tcase OP_CONV_ITOF:\n\t\tcase OP_STORE_IF:\n\t\t\t{\n\t\t\t\tconst QCC_eval_t *eval_a = QCC_SRef_EvalConst(var_a);\n\t\t\t\tif (eval_a)\n\t\t\t\t{\n\t\t\t\t\tQCC_FreeTemp(var_a);\n\t\t\t\t\tvar_a = QCC_MakeFloatConst(eval_a->_int);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tvar_c = QCC_PR_EmulationFunc(itof);\n\t\t\t\t\tif (!var_c.cast)\n\t\t\t\t\t{\n\t\t\t\t\t\t//with denormals, 5.0 * 1i -> 5i, and 5i / 1i = 5.0\n\t\t\t\t\t\tQCC_PR_ParseWarning(WARN_DENORMAL, \"itof emulation: denormals are unsafe %s\", var_a.sym->name);\n\t\t\t\t\t\tvar_a = QCC_PR_StatementFlags(&pr_opcodes[OP_DIV_F], var_a, QCC_MakeIntConst(1), NULL, 0);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\tvar_a = QCC_PR_GenerateFunctionCall1(nullsref, var_c, var_a, type_integer);\n\t\t\t\t\tvar_a.cast = type_float;\n\t\t\t\t}\n\t\t\t\tif (var_b.cast)\n\t\t\t\t\treturn QCC_PR_StatementFlags(&pr_opcodes[OP_STORE_F], var_a, var_b, NULL, flags&STFL_DISCARDRESULT);\n\t\t\t}\n\t\t\treturn var_a;\n\t\tcase OP_CONV_FTOI:\n\t\tcase OP_STORE_FI:\n\t\t\t{\n\t\t\t\tconst QCC_eval_t *eval_a = QCC_SRef_EvalConst(var_a);\n\t\t\t\tif (eval_a)\n\t\t\t\t{\n\t\t\t\t\tQCC_FreeTemp(var_a);\n\t\t\t\t\tvar_a = QCC_MakeIntConst(eval_a->_float);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tvar_c = QCC_PR_EmulationFunc(ftoi);\n\t\t\t\t\tif (!var_c.cast)\n\t\t\t\t\t{\n\t\t\t\t\t\t//with denormals, 5 * 1i -> 5i\n\t\t\t\t\t\tQCC_PR_ParseWarning(WARN_DENORMAL, \"ftoi emulation: denormals are unsafe\");\n\t\t\t\t\t\tvar_a = QCC_PR_StatementFlags(&pr_opcodes[OP_MUL_F], var_a, QCC_MakeIntConst(1), NULL, 0);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\tvar_a = QCC_PR_GenerateFunctionCall1(nullsref, var_c, var_a, type_float);\n\t\t\t\t\tvar_a.cast = type_integer;\n\t\t\t\t}\n\t\t\t\tif (var_b.cast)\n\t\t\t\t\treturn QCC_PR_StatementFlags(&pr_opcodes[OP_STORE_I], var_a, var_b, NULL, flags&STFL_DISCARDRESULT);\n\t\t\t}\n\t\t\treturn var_a;\n\t\tcase OP_STORE_P:\n\t\tcase OP_STORE_I:\n\t\t\top = pr_opcodes+OP_STORE_F;\n\t\t\tbreak;\n\n\t\tcase OP_BITXOR_F:\n//\t\t\tr = (a & ~b) | (b & ~a);\n\t\t\tvar_c = QCC_PR_StatementFlags(&pr_opcodes[OP_BITNOT_F], var_b, nullsref, NULL, STFL_PRESERVEA);\n\t\t\tvar_c = QCC_PR_StatementFlags(&pr_opcodes[OP_BITAND_F], var_a, var_c, NULL, STFL_PRESERVEA);\n\t\t\tvar_a = QCC_PR_StatementFlags(&pr_opcodes[OP_BITNOT_F], var_a, nullsref, NULL, 0);\n\t\t\tvar_a = QCC_PR_StatementFlags(&pr_opcodes[OP_BITAND_F], var_b, var_a, NULL, 0);\n\t\t\treturn QCC_PR_StatementFlags(&pr_opcodes[OP_BITOR_F], var_c, var_a, NULL, 0);\n\n\t\tcase OP_BITXOR_I:\n\t\t\t{\n\t\t\t\tQCC_sref_t fnc = QCC_PR_EmulationFunc(BitxorInt);\n\t\t\t\tif (!fnc.cast)\n\t\t\t\t{\n/*\t\t\t\t\tQCC_PR_ParseWarning(0, \"BitxorInt function not defined: cannot emulate int^int\");\n\t\t\t\t\tgoto badopcode;\n*/\n\t\t\t\t\tvar_c = QCC_PR_StatementFlags(&pr_opcodes[OP_BITNOT_I], var_b, nullsref, NULL, STFL_PRESERVEA);\n\t\t\t\t\tvar_c = QCC_PR_StatementFlags(&pr_opcodes[OP_BITAND_I], var_a, var_c, NULL, STFL_PRESERVEA);\n\t\t\t\t\tvar_a = QCC_PR_StatementFlags(&pr_opcodes[OP_BITNOT_I], var_a, nullsref, NULL, 0);\n\t\t\t\t\tvar_a = QCC_PR_StatementFlags(&pr_opcodes[OP_BITAND_I], var_b, var_a, NULL, 0);\n\t\t\t\t\treturn QCC_PR_StatementFlags(&pr_opcodes[OP_BITOR_I], var_c, var_a, NULL, 0);\n\t\t\t\t}\n\t\t\t\tvar_c = QCC_PR_GenerateFunctionCall2(nullsref, fnc, var_a, type_integer, var_b, type_integer);\n\t\t\t\tvar_c.cast = type_integer;\n\t\t\t\treturn var_c;\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase OP_BITXOR_V:\n//\t\t\tr = (a & ~b) | (b & ~a);\n\t\t\tvar_c = QCC_PR_StatementFlags(&pr_opcodes[OP_BITNOT_V], var_b, nullsref, NULL, STFL_PRESERVEA);\n\t\t\tvar_c = QCC_PR_StatementFlags(&pr_opcodes[OP_BITAND_V], var_a, var_c, NULL, STFL_PRESERVEA);\n\t\t\tvar_a = QCC_PR_StatementFlags(&pr_opcodes[OP_BITNOT_V], var_a, nullsref, NULL, STFL_PRESERVEA);\n\t\t\tvar_a = QCC_PR_StatementFlags(&pr_opcodes[OP_BITAND_V], var_b, var_a, NULL, STFL_PRESERVEB);\n\t\t\treturn QCC_PR_StatementFlags(&pr_opcodes[OP_BITOR_V], var_c, var_a, NULL, 0);\n\n\t\tcase OP_BITXOR_D:\n//\t\t\tr = (a & ~b) | (b & ~a);\n\t\t\tvar_c = QCC_PR_StatementFlags(&pr_opcodes[OP_BITNOT_D], var_b, nullsref, NULL, STFL_PRESERVEA);\n\t\t\tvar_c = QCC_PR_StatementFlags(&pr_opcodes[OP_BITAND_D], var_a, var_c, NULL, STFL_PRESERVEA);\n\t\t\tvar_a = QCC_PR_StatementFlags(&pr_opcodes[OP_BITNOT_D], var_a, nullsref, NULL, STFL_PRESERVEA);\n\t\t\tvar_a = QCC_PR_StatementFlags(&pr_opcodes[OP_BITAND_D], var_b, var_a, NULL, STFL_PRESERVEB);\n\t\t\treturn QCC_PR_StatementFlags(&pr_opcodes[OP_BITOR_D], var_c, var_a, NULL, 0);\n\n\t\tcase OP_IF_S:\n\t\t\ttmp = QCC_MakeFloatConst(0);\n\t\t\ttmp.cast = type_string;\n\t\t\tvar_a = QCC_PR_Statement(&pr_opcodes[OP_NE_S], var_a, tmp, NULL);\n\t\t\top = &pr_opcodes[OP_IF_I];\n\t\t\tbreak;\n\n\t\tcase OP_IFNOT_S:\n\t\t\ttmp = QCC_MakeFloatConst(0);\n\t\t\ttmp.cast = type_string;\n\t\t\tvar_a = QCC_PR_Statement(&pr_opcodes[OP_NE_S], var_a, tmp, NULL);\n\t\t\top = &pr_opcodes[OP_IFNOT_I];\n\t\t\tbreak;\n\n\t\tcase OP_IF_F:\n\t\t\ttmp = QCC_MakeFloatConst(0);\n\t\t\tvar_a = QCC_PR_Statement(&pr_opcodes[OP_NE_F], var_a, tmp, NULL);\n\t\t\top = &pr_opcodes[OP_IF_I];\n\t\t\tbreak;\n\n\t\tcase OP_IFNOT_F:\n\t\t\ttmp = QCC_MakeFloatConst(0);\n\t\t\tvar_a = QCC_PR_Statement(&pr_opcodes[OP_NE_F], var_a, tmp, NULL);\n\t\t\top = &pr_opcodes[OP_IFNOT_I];\n\t\t\tbreak;\n\n\t\tcase OP_ADDSTORE_F:\n\t\t\top = &pr_opcodes[OP_ADD_F];\n\t\t\ttmp = var_b;\n\t\t\tvar_b = var_a;\n\t\t\tvar_a = tmp;\n\t\t\tbreak;\n\t\tcase OP_ADDSTORE_I:\n\t\t\top = &pr_opcodes[OP_ADD_I];\n\t\t\ttmp = var_b;\n\t\t\tvar_b = var_a;\n\t\t\tvar_a = tmp;\n\t\t\tbreak;\n\t\tcase OP_ADDSTORE_FI:\n\t\t\top = &pr_opcodes[OP_ADD_FI];\n\t\t\ttmp = var_b;\n\t\t\tvar_b = var_a;\n\t\t\tvar_a = tmp;\n\t\t\tbreak;\n//\t\tcase OP_ADDSTORE_IF:\n//\t\t\tfixme: result is a float but needs to be an int\n//\t\t\top = &pr_opcodes[OP_ADD_IF];\n//\t\t\ttmp = var_b;\n//\t\t\tvar_b = var_a;\n//\t\t\tvar_a = tmp;\n//\t\t\tbreak;\n\n\t\tcase OP_SUBSTORE_F:\n\t\t\top = &pr_opcodes[OP_SUB_F];\n\t\t\ttmp = var_b;\n\t\t\tvar_b = var_a;\n\t\t\tvar_a = tmp;\n\t\t\tbreak;\n\t\tcase OP_SUBSTORE_FI:\n\t\t\top = &pr_opcodes[OP_SUB_FI];\n\t\t\ttmp = var_b;\n\t\t\tvar_b = var_a;\n\t\t\tvar_a = tmp;\n\t\t\tbreak;\n//\t\tcase OP_SUBSTORE_IF:\n//\t\t\tfixme: result is a float but needs to be an int\n//\t\t\top = &pr_opcodes[OP_SUB_IF];\n//\t\t\ttmp = var_b;\n//\t\t\tvar_b = var_a;\n//\t\t\tvar_a = tmp;\n//\t\t\tbreak;\n\t\tcase OP_SUBSTORE_I:\n\t\t\top = &pr_opcodes[OP_SUB_I];\n\t\t\ttmp = var_b;\n\t\t\tvar_b = var_a;\n\t\t\tvar_a = tmp;\n\t\t\tbreak;\n\n\t\tcase OP_BITNOT_I:\n\t\t\top = &pr_opcodes[OP_SUB_I];\n\t\t\tif (QCC_OPCodeValid(op))\n\t\t\t{\n\t\t\t\top = &pr_opcodes[OP_SUB_I];\n\t\t\t\tvar_b = var_a;\n\t\t\t\tvar_a = QCC_MakeIntConst(~0);\n\t\t\t\tvar_a.sym->referenced = true;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tQCC_sref_t fnc = QCC_PR_EmulationFunc(SubInt);\n\t\t\t\tif (!fnc.cast)\n\t\t\t\t{\n\t\t\t\t\tQCC_PR_ParseWarning(0, \"SubInt function not defined: cannot emulate ~int\");\n\t\t\t\t\tgoto badopcode;\n\t\t\t\t}\n\t\t\t\tvar_c = QCC_PR_GenerateFunctionCall2(nullsref, fnc, QCC_MakeIntConst(~0), type_integer, var_a, type_integer);\n\t\t\t\tvar_c.cast = type_integer;\n\t\t\t\treturn var_c;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase OP_BITNOT_I64:\n\t\t\top = &pr_opcodes[OP_SUB_I64];\n\t\t\tvar_b = var_a;\n\t\t\tvar_a = QCC_MakeInt64Const(~(longlong)0);\n\t\t\tvar_a.sym->referenced = true;\n\t\t\tbreak;\n\t\tcase OP_BITNOT_F:\n\t\t\top = &pr_opcodes[OP_SUB_F];\n\t\t\tvar_b = var_a;\n\t\t\tvar_a = QCC_MakeFloatConst(-1);\t//divVerent says -1 is safe, even with floats. I guess I'm just too paranoid.\n\t\t\tvar_a.sym->referenced = true;\n\t\t\tbreak;\n\t\tcase OP_BITNOT_V:\n\t\t\top = &pr_opcodes[OP_SUB_V];\n\t\t\tvar_b = var_a;\n\t\t\tvar_a = QCC_MakeVectorConst(-1, -1, -1);\n\t\t\tvar_a.sym->referenced=true;\n\t\t\tbreak;\n\n\t\tcase OP_DIVSTORE_F:\n\t\t\top = &pr_opcodes[OP_DIV_F];\n\t\t\ttmp = var_b;\n\t\t\tvar_b = var_a;\n\t\t\tvar_a = tmp;\n\t\t\tbreak;\n\t\tcase OP_DIVSTORE_FI:\n\t\t\top = &pr_opcodes[OP_DIV_FI];\n\t\t\ttmp = var_b;\n\t\t\tvar_b = var_a;\n\t\t\tvar_a = tmp;\n\t\t\tbreak;\n//\t\tcase OP_DIVSTORE_IF:\n//\t\t\tfixme: result is a float, but needs to be an int\n//\t\t\top = &pr_opcodes[OP_DIV_IF];\n//\t\t\ttmp = var_b;\n//\t\t\tvar_b = var_a;\n//\t\t\tvar_a = tmp;\n//\t\t\tbreak;\n\t\tcase OP_DIVSTORE_I:\n\t\t\top = &pr_opcodes[OP_DIV_I];\n\t\t\ttmp = var_b;\n\t\t\tvar_b = var_a;\n\t\t\tvar_a = tmp;\n\t\t\tbreak;\n\n\t\tcase OP_MULSTORE_F:\n\t\t\top = &pr_opcodes[OP_MUL_F];\n\t\t\ttmp = var_b;\n\t\t\tvar_b = var_a;\n\t\t\tvar_a = tmp;\n\t\t\tbreak;\n//\t\tcase OP_MULSTORE_IF:\n//\t\t\tfixme: result is a float, but needs to be an int\n//\t\t\top = &pr_opcodes[OP_MUL_IF];\n//\t\t\tvar_c = var_b;\n//\t\t\tvar_b = var_a;\n//\t\t\tvar_a = var_c;\n//\t\t\tbreak;\n\t\tcase OP_MULSTORE_FI:\n\t\t\top = &pr_opcodes[OP_MUL_FI];\n\t\t\ttmp = var_b;\n\t\t\tvar_b = var_a;\n\t\t\tvar_a = tmp;\n\t\t\tbreak;\n\n\t\tcase OP_ADDSTORE_V:\n\t\t\top = &pr_opcodes[OP_ADD_V];\n\t\t\ttmp = var_b;\n\t\t\tvar_b = var_a;\n\t\t\tvar_a = tmp;\n\t\t\tbreak;\n\n\t\tcase OP_SUBSTORE_V:\n\t\t\top = &pr_opcodes[OP_SUB_V];\n\t\t\ttmp = var_b;\n\t\t\tvar_b = var_a;\n\t\t\tvar_a = tmp;\n\t\t\tbreak;\n\n\t\tcase OP_MULSTORE_VF:\n\t\t\top = &pr_opcodes[OP_MUL_VF];\n\t\t\ttmp = var_b;\n\t\t\tvar_b = var_a;\n\t\t\tvar_a = tmp;\n\t\t\tbreak;\n\t\tcase OP_MULSTORE_VI:\n\t\t\top = &pr_opcodes[OP_MUL_VI];\n\t\t\ttmp = var_b;\n\t\t\tvar_b = var_a;\n\t\t\tvar_a = tmp;\n\t\t\tbreak;\n\n\t\tcase OP_BITSETSTORE_I:\n\t\t\top = &pr_opcodes[OP_BITOR_I];\n\t\t\ttmp = var_b;\n\t\t\tvar_b = var_a;\n\t\t\tvar_a = tmp;\n\t\t\tbreak;\n\t\tcase OP_BITSETSTORE_F:\n\t\t\top = &pr_opcodes[OP_BITOR_F];\n\t\t\ttmp = var_b;\n\t\t\tvar_b = var_a;\n\t\t\tvar_a = tmp;\n\t\t\tbreak;\n\n\t\tcase OP_LOAD_P:\n\t\tcase OP_LOAD_I:\n\t\t\top = &pr_opcodes[OP_LOAD_F];\n\t\t\tbreak;\n\t\tcase OP_STOREP_P:\n\t\t\top = &pr_opcodes[OP_STOREP_I];\n\t\t\tbreak;\n\n\t\tcase OP_EQ_P:\n\t\t\top = &pr_opcodes[OP_EQ_E];\n\t\t\tbreak;\n\t\tcase OP_NE_P:\n\t\t\top = &pr_opcodes[OP_NE_E];\n\t\t\tbreak;\n\n\t\tcase OP_GT_P:\n\t\t\top = &pr_opcodes[OP_GT_I];\n\t\t\tbreak;\n\t\tcase OP_GE_P:\n\t\t\top = &pr_opcodes[OP_GE_I];\n\t\t\tbreak;\n\t\tcase OP_LE_P:\n\t\t\top = &pr_opcodes[OP_LE_I];\n\t\t\tbreak;\n\t\tcase OP_LT_P:\n\t\t\top = &pr_opcodes[OP_LT_I];\n\t\t\tbreak;\n\n\t\tcase OP_BITCLR_I64:\n\t\t\t//b = var, a = bit field.\n\t\t\tvar_b = QCC_PR_StatementFlags(&pr_opcodes[OP_BITAND_I64], var_a, var_b, NULL, STFL_PRESERVEA);\n\t\t\tvar_c = nullsref;\n\t\t\top = &pr_opcodes[OP_SUB_I64];\n\t\t\tbreak;\n\n\t\tcase OP_BITCLR_I:\n\t\t\tif (QCC_OPCodeValid(&pr_opcodes[OP_BITCLRSTORE_I]))\n\t\t\t{\n\t\t\t\top = &pr_opcodes[OP_BITCLRSTORE_I];\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\ttmp = var_b;\n\t\t\tvar_b = var_a;\n\t\t\tvar_a = tmp;\n\t\t\t//fallthrough\n\t\tcase OP_BITCLRSTORE_I:\n\t\t\t//b = var, a = bit field.\n\t\t\tvar_c = QCC_PR_StatementFlags(&pr_opcodes[OP_BITAND_I], var_b, var_a, NULL, STFL_PRESERVEA);\n\n\t\t\tvar_a = var_b;\n\t\t\tvar_b = var_c;\n\t\t\tvar_c = ((op - pr_opcodes)==OP_BITCLRSTORE_I)?var_a:nullsref;\n\t\t\top = &pr_opcodes[OP_SUB_I];\n\t\t\tbreak;\n\t\tcase OP_BITCLR_V:\n\t\t\tvar_b = QCC_PR_StatementFlags(&pr_opcodes[OP_BITAND_V], var_a, var_b, NULL, STFL_PRESERVEA);\n\t\t\top = &pr_opcodes[OP_SUB_V];\n\t\t\tvar_c = nullsref;\n\t\t\tbreak;\n\t\tcase OP_BITCLR_D:\n\t\t\tvar_b = QCC_PR_StatementFlags(&pr_opcodes[OP_BITAND_D], var_a, var_b, NULL, STFL_PRESERVEA);\n\t\t\top = &pr_opcodes[OP_SUB_D];\n\t\t\tvar_c = nullsref;\n\t\t\tbreak;\n\n\t\tcase OP_BITCLR_F:\n\t\t\tvar_c = var_b;\n\t\t\tvar_b = var_a;\n\t\t\tvar_a = var_c;\n\t\t\tif (QCC_OPCodeValid(&pr_opcodes[OP_BITCLRSTORE_F]))\n\t\t\t{\t//its all okay, just use it\n\t\t\t\top = &pr_opcodes[OP_BITCLRSTORE_F];\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\t//fallthrough\n\t\tcase OP_BITCLRSTORE_F:\n\t\t\t//b = var, a = bit field.\n\n\t\t\t//b - (b&a)\n\n\t\t\tvar_c = QCC_PR_StatementFlags(&pr_opcodes[OP_BITAND_F], var_b, var_a, NULL, STFL_PRESERVEA);\n\n\t\t\tvar_a = var_b;\n\t\t\tvar_b = var_c;\n\t\t\tvar_c = ((op - pr_opcodes)==OP_BITCLRSTORE_F)?var_a:nullsref;\n\t\t\top = &pr_opcodes[OP_SUB_F];\n\t\t\tbreak;\n#if IAMNOTLAZY\n\t\tcase OP_SUBSTOREP_FI:\n\t\tcase OP_SUBSTOREP_IF:\n\t\tcase OP_ADDSTOREP_FI:\n\t\tcase OP_ADDSTOREP_IF:\n\t\tcase OP_MULSTOREP_FI:\n\t\tcase OP_MULSTOREP_IF:\n\t\tcase OP_DIVSTOREP_FI:\n\t\tcase OP_DIVSTOREP_IF:\n\n\t\tcase OP_MULSTOREP_VF:\n\t\tcase OP_MULSTOREP_VI:\n\t\tcase OP_SUBSTOREP_V:\n\t\tcase OP_ADDSTOREP_V:\n\n\t\tcase OP_SUBSTOREP_F:\n\t\tcase OP_SUBSTOREP_I:\n\t\tcase OP_ADDSTOREP_I:\n\t\tcase OP_ADDSTOREP_F:\n\t\tcase OP_MULSTOREP_F:\n\t\tcase OP_DIVSTOREP_F:\n\t\tcase OP_BITSETSTOREP_F:\n\t\tcase OP_BITSETSTOREP_I:\n\t\tcase OP_BITCLRSTOREP_F:\n//\t\t\tQCC_PR_ParseWarning(0, \"XSTOREP_F emulation is still experimental\");\n\t\t\tQCC_UnFreeTemp(var_a);\n\t\t\tQCC_UnFreeTemp(var_b);\n\n\t\t\tstatement = &statements[numstatements++];\n\n\t\t\t//don't chain these... this expansion is not the same.\n\t\t\t{\n\t\t\t\tint st;\n\t\t\t\tpbool need_lock;\n\t\t\t\tst = QCC_PR_FindSourceForTemp(var_b, OP_ADDRESS, &need_lock);\n\n\t\t\t\tvar_c = QCC_GetTemp(*op->type_c);\n\t\t\t\tif (st < 0)\n\t\t\t\t{\n\t\t\t\t\t/*generate new OP_LOADP instruction*/\n\t\t\t\t\tstatement->op = ((*op->type_c)->type==ev_vector)?OP_LOADP_V:OP_LOADP_F;\n\t\t\t\t\tstatement->a = var_b->ofs;\n\t\t\t\t\tstatement->b = var_c->ofs;\n\t\t\t\t\tstatement->c = 0;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t/*it came from an OP_ADDRESS - st says the instruction*/\n\t\t\t\t\tif (need_lock)\n\t\t\t\t\t{\n\t\t\t\t\t\tQCC_ForceLockTempForOffset(statements[st].a);\n\t\t\t\t\t\tQCC_ForceLockTempForOffset(statements[st].b);\n//\t\t\t\t\t\tQCC_LockTemp(var_c); /*that temp needs to be preserved over calls*/\n\t\t\t\t\t}\n\n\t\t\t\t\t/*generate new OP_ADDRESS instruction - FIXME: the arguments may have changed since the original instruction*/\n\t\t\t\t\tstatement->op = OP_ADDRESS;\n\t\t\t\t\tstatement->flags = 0;\n\t\t\t\t\tstatement->a = statements[st].a;\n\t\t\t\t\tstatement->b = statements[st].b;\n\t\t\t\t\tstatement->c = var_c->ofs;\n\t\t\t\t\tstatement->linenum = statements[st].linenum;\n\n\t\t\t\t\t/*convert old one to an OP_LOAD*/\n\t\t\t\t\tstatements[st].op = ((*op->type_c)->type==ev_vector)?OP_LOAD_V:OP_LOAD_F;\n\t\t\t\t\tstatement->flags = 0;\n//\t\t\t\t\tstatements[st].a = statements[st].a;\n//\t\t\t\t\tstatements[st].b = statements[st].b;\n//\t\t\t\t\tstatements[st].c = statements[st].c;\n\t\t\t\t\tstatements[st].linenum = pr_token_line_last;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tstatement = &statements[numstatements++];\n\n\t\t\tstatement->linenum = pr_token_line_last;\n\t\t\tswitch(op - pr_opcodes)\n\t\t\t{\n\t\t\tcase OP_SUBSTOREP_V:\n\t\t\t\tstatement->op = OP_SUB_V;\n\t\t\t\tbreak;\n\t\t\tcase OP_ADDSTOREP_V:\n\t\t\t\tstatement->op = OP_ADD_V;\n\t\t\t\tbreak;\n\t\t\tcase OP_MULSTOREP_VF:\n\t\t\t\tstatement->op = OP_MUL_VF;\n\t\t\t\tbreak;\n\t\t\tcase OP_MULSTOREP_VI:\n\t\t\t\tstatement->op = OP_MUL_VI;\n\t\t\t\tbreak;\n\t\t\tcase OP_SUBSTOREP_F:\n\t\t\t\tstatement->op = OP_SUB_F;\n\t\t\t\tbreak;\n\t\t\tcase OP_SUBSTOREP_I:\n\t\t\t\tstatement->op = OP_SUB_I;\n\t\t\t\tbreak;\n\t\t\tcase OP_SUBSTOREP_IF:\n\t\t\t\tstatement->op = OP_SUB_IF;\n\t\t\t\tbreak;\n\t\t\tcase OP_SUBSTOREP_FI:\n\t\t\t\tstatement->op = OP_SUB_FI;\n\t\t\t\tbreak;\n\t\t\tcase OP_ADDSTOREP_IF:\n\t\t\t\tstatement->op = OP_ADD_IF;\n\t\t\t\tbreak;\n\t\t\tcase OP_ADDSTOREP_FI:\n\t\t\t\tstatement->op = OP_ADD_FI;\n\t\t\t\tbreak;\n\t\t\tcase OP_MULSTOREP_IF:\n\t\t\t\tstatement->op = OP_MUL_IF;\n\t\t\t\tbreak;\n\t\t\tcase OP_MULSTOREP_FI:\n\t\t\t\tstatement->op = OP_MUL_FI;\n\t\t\t\tbreak;\n\t\t\tcase OP_DIVSTOREP_IF:\n\t\t\t\tstatement->op = OP_DIV_IF;\n\t\t\t\tbreak;\n\t\t\tcase OP_DIVSTOREP_FI:\n\t\t\t\tstatement->op = OP_DIV_FI;\n\t\t\t\tbreak;\n\t\t\tcase OP_ADDSTOREP_F:\n\t\t\t\tstatement->op = OP_ADD_F;\n\t\t\t\tbreak;\n\t\t\tcase OP_ADDSTOREP_I:\n\t\t\t\tstatement->op = OP_ADD_I;\n\t\t\t\tbreak;\n\t\t\tcase OP_MULSTOREP_F:\n\t\t\t\tstatement->op = OP_MUL_F;\n\t\t\t\tbreak;\n\t\t\tcase OP_DIVSTOREP_F:\n\t\t\t\tstatement->op = OP_DIV_F;\n\t\t\t\tbreak;\n\t\t\tcase OP_BITSETSTOREP_F:\n\t\t\t\tstatement->op = OP_BITOR_F;\n\t\t\t\tbreak;\n\t\t\tcase OP_BITSETSTOREP_I:\n\t\t\t\tstatement->op = OP_BITOR_I;\n\t\t\t\tbreak;\n\t\t\tcase OP_BITCLRSTOREP_F:\n\t\t\t\t//float pointer float\n\t\t\t\ttemp = QCC_GetTemp(type_float);\n\t\t\t\tstatement->op = OP_BITAND_F;\n\t\t\t\tstatement->flags = 0;\n\t\t\t\tstatement->a = var_c ? var_c->ofs : 0;\n\t\t\t\tstatement->b = var_a ? var_a->ofs : 0;\n\t\t\t\tstatement->c = temp->ofs;\n\n\t\t\t\tstatement = &statements[numstatements];\n\t\t\t\tnumstatements++;\n\n\t\t\t\tstatement->linenum = pr_token_line_last;\n\t\t\t\tstatement->op = OP_SUB_F;\n\t\t\t\tstatement->flags = 0;\n\n\t\t\t\t//t = c & i\n\t\t\t\t//c = c - t\n\t\t\t\tbreak;\n\t\t\tdefault:\t//no way will this be hit...\n\t\t\t\tQCC_PR_ParseError(ERR_INTERNAL, \"opcode invalid 3 times %i\", op - pr_opcodes);\n\t\t\t}\n\t\t\tif (op - pr_opcodes == OP_BITCLRSTOREP_F)\n\t\t\t{\n\t\t\t\tstatement->a = var_b ? var_b->ofs : 0;\n\t\t\t\tstatement->b = temp ? temp->ofs : 0;\n\t\t\t\tstatement->c = var_b->ofs;\n\t\t\t\tQCC_FreeTemp(temp);\n\t\t\t\tQCC_FreeTemp(var_a);\n\t\t\t\tvar_a = var_b;\t//this is the value.\n\t\t\t\tvar_b = var_c;\t//this is the ptr.\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tstatement->a = var_b ? var_b->ofs : 0;\n\t\t\t\tstatement->b = var_a ? var_a->ofs : 0;\n\t\t\t\tstatement->c = var_b->ofs;\n\t\t\t\tQCC_FreeTemp(var_a);\n\t\t\t\tvar_a = var_b;\t//this is the value.\n\t\t\t\tvar_b = var_c;\t//this is the ptr.\n\t\t\t}\n\n\t\t\top = &pr_opcodes[((*op->type_c)->type==ev_vector)?OP_STOREP_V:OP_STOREP_F];\n\t\t\tQCC_FreeTemp(var_c);\n\t\t\tvar_c = NULL;\n\t\t\tQCC_FreeTemp(var_b);\n\t\t\tbreak;\n#endif\n\n\t\tcase OP_LSHIFT_IF:\n\t\t\tvar_b = QCC_PR_StatementFlags(&pr_opcodes[OP_CONV_FTOI], var_b, nullsref, NULL, (flags&STFL_PRESERVEB)?STFL_PRESERVEA:0);\n\t\t\treturn QCC_PR_StatementFlags(&pr_opcodes[OP_LSHIFT_I], var_a, var_b, NULL, flags&STFL_PRESERVEA);\n\t\tcase OP_LSHIFT_FI:\n\t\t\tvar_a = QCC_PR_StatementFlags(&pr_opcodes[OP_CONV_FTOI], var_a, nullsref, NULL, flags&STFL_PRESERVEA);\n\t\t\treturn QCC_PR_StatementFlags(&pr_opcodes[OP_LSHIFT_I], var_a, var_b, NULL, flags&STFL_PRESERVEB);\n\t\tcase OP_RSHIFT_IF:\n\t\t\tvar_b = QCC_PR_StatementFlags(&pr_opcodes[OP_CONV_FTOI], var_b, nullsref, NULL, (flags&STFL_PRESERVEB)?STFL_PRESERVEA:0);\n\t\t\treturn QCC_PR_StatementFlags(&pr_opcodes[OP_RSHIFT_I], var_a, var_b, NULL, flags&STFL_PRESERVEA);\n\t\tcase OP_RSHIFT_FI:\n\t\t\tvar_a = QCC_PR_StatementFlags(&pr_opcodes[OP_CONV_FTOI], var_a, nullsref, NULL, flags&STFL_PRESERVEA);\n\t\t\treturn QCC_PR_StatementFlags(&pr_opcodes[OP_RSHIFT_I], var_a, var_b, NULL, flags&STFL_PRESERVEB);\n\n\t\tcase OP_LSHIFT_I:\n\t\t\t{\n\t\t\t\tQCC_sref_t fnc = QCC_PR_EmulationFunc(LShiftInt);\n\t\t\t\tif (!fnc.cast)\n\t\t\t\t{\n\t\t\t\t\tconst QCC_eval_t *eval_b = QCC_SRef_EvalConst(var_b);\n\t\t\t\t\tif (eval_b)\n\t\t\t\t\t\tvar_b = QCC_MakeIntConst(1<<eval_b->_int);\n\t\t\t\t\telse\n\t\t\t\t\t\tvar_b = QCC_PR_StatementFlags(&pr_opcodes[OP_POW_I], QCC_MakeIntConst(2), var_b, NULL, flags&STFL_PRESERVEB);\n\t\t\t\t\treturn QCC_PR_StatementFlags(&pr_opcodes[OP_MUL_I], var_a, var_b, NULL, flags&STFL_PRESERVEA);\n\t\t\t\t}\n\t\t\t\tvar_c = QCC_PR_GenerateFunctionCall2(nullsref, fnc, var_a, type_integer, var_b, type_integer);\n\t\t\t\tvar_c.cast = type_integer;\n\t\t\t\treturn var_c;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase OP_RSHIFT_I:\t//C leaves it undefined whether OP_RSHIFT_I is _I or actually _U\n\t\t\t{\n\t\t\t\tconst QCC_eval_t *eval_b;\n\t\t\t\tQCC_sref_t fnc = QCC_PR_EmulationFunc(RShiftInt);\n\t\t\t\tif (fnc.cast && strcmp(fnc.sym->name, pr_scope->name))\n\t\t\t\t{\n\t\t\t\t\tvar_c = QCC_PR_GenerateFunctionCall2(nullsref, fnc, var_a, type_integer, var_b, type_integer);\n\t\t\t\t\tvar_c.cast = type_integer;\n\t\t\t\t\treturn var_c;\n\t\t\t\t}\n\t\t\t\t//fix up the rhs of the shift to something we can actually use.\n\t\t\t\teval_b = QCC_SRef_EvalConst(var_b);\n\t\t\t\tif (eval_b)\n\t\t\t\t{\n\t\t\t\t\tif (!(flags&STFL_PRESERVEB))\n\t\t\t\t\t\tQCC_FreeTemp(var_b);\t//done with it now\n\t\t\t\t\tvar_b = QCC_MakeIntConst(1<<eval_b->_int);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tvar_b = QCC_PR_StatementFlags(&pr_opcodes[OP_POW_I], QCC_MakeIntConst(2), var_b, NULL, flags&STFL_PRESERVEB);\n\t\t\t\tif (QCC_OPCodeValid(&pr_opcodes[OP_DIV_U]))\n\t\t\t\t\top = &pr_opcodes[OP_DIV_U];\n\t\t\t\telse\n\t\t\t\t\top = &pr_opcodes[OP_DIV_I];\t//might be quirky when the high bit is set in the divisor\n\t\t\t\t//jump-if->=0\n\t\t\t\tstatement = QCC_Generate_OP_IFNOT(QCC_PR_StatementFlags(&pr_opcodes[OP_LT_I], var_a, QCC_MakeFloatConst(0), NULL, STFL_PRESERVEA), false);\n\t\t\t\t//negative. we need to do this explicitly to avoid rounding\n\t\t\t\tvar_c = QCC_PR_StatementFlags(&pr_opcodes[OP_BITNOT_I], var_a, nullsref, NULL, STFL_PRESERVEA);\t//flip it\n\t\t\t\tvar_c = QCC_PR_StatementFlags(op, var_c, var_b, NULL, STFL_PRESERVEB);\t\t//shift it (zero-extend)...\n\t\t\t\tvar_c = QCC_PR_StatementFlags(&pr_opcodes[OP_BITNOT_I], var_c, nullsref, NULL, 0);\t\t\t\t//now flip that.\n\t\t\t\t//else\n\t\t\t\tstatement->b.jumpofs = &statements[numstatements+1] - statement;//jump to after the following goto\n\t\t\t\tstatement = QCC_Generate_OP_GOTO();\n\t\t\t\t//positive\n\t\t\t\tQCC_PR_SimpleStatement(op, var_a, var_b, var_c, false)->flags|=STF_NOFOLD;\t//can just shift into the same temp the negative form would have produced..\n\t\t\t\t//end\n\t\t\t\tstatement->a.jumpofs = &statements[numstatements] - statement;\n\t\t\t\tQCC_FreeTemp(var_b);\t//done with it now\n\t\t\t\tif (!(flags&STFL_PRESERVEA))\n\t\t\t\t\tQCC_FreeTemp(var_a);\t//done with it now\n\t\t\t\tvar_c.cast = type_integer;\n\t\t\t\treturn var_c;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase OP_RSHIFT_U:\n\t\t\t{\n\t\t\t\tQCC_sref_t fnc = QCC_PR_EmulationFunc(RShiftUInt);\n\t\t\t\tif (!fnc.cast)\n\t\t\t\t{\n\t\t\t\t\tconst QCC_eval_t *eval_b = QCC_SRef_EvalConst(var_b);\n\t\t\t\t\tif (eval_b)\n\t\t\t\t\t\tvar_b = QCC_MakeUIntConst(1<<eval_b->_uint);\n\t\t\t\t\telse\n\t\t\t\t\t\tvar_b = QCC_PR_StatementFlags(&pr_opcodes[OP_POW_I], QCC_MakeUIntConst(2), var_b, NULL, flags&STFL_PRESERVEB);\n\t\t\t\t\treturn QCC_PR_StatementFlags(&pr_opcodes[OP_DIV_U], var_a, var_b, NULL, flags&STFL_PRESERVEA);\n\t\t\t\t}\n\t\t\t\tvar_c = QCC_PR_GenerateFunctionCall2(nullsref, fnc, var_a, type_uint, var_b, type_uint);\n\t\t\t\tvar_c.cast = type_uint;\n\t\t\t\treturn var_c;\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase OP_LSHIFT_DI:\n\t\t\tvar_a = QCC_PR_StatementFlags(&pr_opcodes[OP_CONV_DI64], var_a, nullsref, NULL, flags&STFL_PRESERVEA);\n\t\t\treturn QCC_PR_StatementFlags(&pr_opcodes[OP_LSHIFT_I64I], var_a, var_b, NULL, 0);\n\t\tcase OP_RSHIFT_DI:\n\t\t\tvar_a = QCC_PR_StatementFlags(&pr_opcodes[OP_CONV_DI64], var_a, nullsref, NULL, flags&STFL_PRESERVEA);\n\t\t\treturn QCC_PR_StatementFlags(&pr_opcodes[OP_RSHIFT_I64I], var_a, var_b, NULL, 0);\n\n\t\t//convert both to ints\n\t\tcase OP_LSHIFT_F:\n\t\t\tif (QCC_OPCodeValid(&pr_opcodes[OP_LSHIFT_I]))\n\t\t\t{\n\t\t\t\tvar_a = QCC_PR_StatementFlags(&pr_opcodes[OP_CONV_FTOI], var_a, nullsref, NULL, flags&STFL_PRESERVEA);\n\t\t\t\tvar_b = QCC_PR_StatementFlags(&pr_opcodes[OP_CONV_FTOI], var_b, nullsref, NULL, (flags&STFL_PRESERVEB)?STFL_PRESERVEA:0);\n\t\t\t\treturn QCC_PR_StatementFlags(&pr_opcodes[OP_LSHIFT_I], var_a, var_b, NULL, 0);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tQCC_sref_t fnc = QCC_PR_EmulationFunc(powf);\n\t\t\t\tif (!fnc.cast)\n\t\t\t\t\tfnc = QCC_PR_EmulationFunc(pow);\n\t\t\t\tif (fnc.cast)\n\t\t\t\t{\t//a<<b === floor(a*pow(2,b))\n\t\t\t\t\tQCC_sref_t fnc2 = QCC_PR_EmulationFunc(floor);\n\t\t\t\t\tif (fnc2.cast)\n\t\t\t\t\t{\n\t\t\t\t\t\tvar_c = QCC_PR_GenerateFunctionCall2(nullsref, fnc, QCC_MakeFloatConst(2), type_float, var_b, type_float);\n\t\t\t\t\t\tvar_c = QCC_PR_StatementFlags(&pr_opcodes[OP_MUL_F], var_a, var_c, NULL, flags&STFL_PRESERVEA);\n\t\t\t\t\t\tvar_c = QCC_PR_GenerateFunctionCall1(nullsref, fnc2, var_c, type_float);\n\t\t\t\t\t\treturn var_c;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tfnc = QCC_PR_EmulationFunc(bitshift);\n\t\t\t\tif (fnc.cast)\n\t\t\t\t{\t//warning: DP's implementation of bitshift is fucked, hence why we favour the more expensibe floor+pow above.\n\t\t\t\t\tvar_c = QCC_PR_GenerateFunctionCall2(nullsref, fnc, var_a, type_float, var_b, type_float);\n\t\t\t\t\tvar_c.cast = *op->type_c;\n\t\t\t\t\treturn var_c;\n\t\t\t\t}\n\t\t\t\tQCC_PR_ParseWarning(0, \"bitshift function not defined: cannot emulate OP_LSHIFT_F*\");\n\t\t\t\tgoto badopcode;\n\t\t\t}\n\t\tcase OP_RSHIFT_F:\n\t\t\tif (QCC_OPCodeValid(&pr_opcodes[OP_RSHIFT_I]))\n\t\t\t{\n\t\t\t\tvar_a = QCC_PR_StatementFlags(&pr_opcodes[OP_CONV_FTOI], var_a, nullsref, NULL, flags&STFL_PRESERVEA);\n\t\t\t\tvar_b = QCC_PR_StatementFlags(&pr_opcodes[OP_CONV_FTOI], var_b, nullsref, NULL, (flags&STFL_PRESERVEB)?STFL_PRESERVEA:0);\n\t\t\t\treturn QCC_PR_StatementFlags(&pr_opcodes[OP_RSHIFT_I], var_a, var_b, NULL, 0);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tQCC_sref_t fnc = QCC_PR_EmulationFunc(powf);\n\t\t\t\tif (!fnc.cast)\n\t\t\t\t\tfnc = QCC_PR_EmulationFunc(pow);\n\t\t\t\tif (fnc.cast)\n\t\t\t\t{\t//a<<b === floor(a/pow(2,b))\n\t\t\t\t\tQCC_sref_t fnc2 = QCC_PR_EmulationFunc(floor);\n\t\t\t\t\tif (fnc2.cast)\n\t\t\t\t\t{\n\t\t\t\t\t\tvar_c = QCC_PR_GenerateFunctionCall2(nullsref, fnc, QCC_MakeFloatConst(2), type_float, var_b, type_float);\n\t\t\t\t\t\tvar_c = QCC_PR_StatementFlags(&pr_opcodes[OP_DIV_F], var_a, var_c, NULL, flags&STFL_PRESERVEA);\n\t\t\t\t\t\tvar_c = QCC_PR_GenerateFunctionCall1(nullsref, fnc2, var_c, type_float);\n\t\t\t\t\t\treturn var_c;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tfnc = QCC_PR_EmulationFunc(bitshift);\n\t\t\t\tif (fnc.cast)\n\t\t\t\t{\t//warning: DP's implementation of bitshift is fucked, hence why we favour the more expensibe floor+pow above\n\t\t\t\t\tvar_b = QCC_PR_StatementFlags(&pr_opcodes[OP_SUB_F], QCC_MakeFloatConst(0), var_b, NULL, (flags&STFL_PRESERVEB)?STFL_PRESERVEB:0);\n\t\t\t\t\tvar_c = QCC_PR_GenerateFunctionCall2(nullsref, fnc, var_a, type_float, var_b, type_float);\n\t\t\t\t\tvar_c.cast = *op->type_c;\n\t\t\t\t\treturn var_c;\n\t\t\t\t}\n\t\t\t\tQCC_PR_ParseWarning(0, \"bitshift function not defined: cannot emulate OP_RSHIFT_F*\");\n\t\t\t\tgoto badopcode;\n\t\t\t}\n\n\t\tcase OP_BITEXTEND_I:\n\t\t\t{\n\t\t\t\tconst QCC_eval_t *eval_b = QCC_SRef_EvalConst(var_b);\n\t\t\t\tif (eval_b)\n\t\t\t\t{\n\t\t\t\t\tint bits = eval_b->_uint&0xff;\n\t\t\t\t\tint bitofs = eval_b->_uint>>8;\n\t\t\t\t\tvar_a = QCC_PR_StatementFlags(&pr_opcodes[OP_LSHIFT_I], var_a, QCC_MakeIntConst(32-bits-bitofs), NULL, flags&STFL_PRESERVEA);\n\t\t\t\t\tvar_c = QCC_PR_StatementFlags(&pr_opcodes[OP_RSHIFT_I], var_a, QCC_MakeIntConst(32-bits), NULL, flags&STFL_PRESERVEA);\n\t\t\t\t\treturn var_c;\n\t\t\t\t}\n\t\t\t}\n\t\t\tgoto badopcode;\n\t\tcase OP_BITEXTEND_U:\n\t\t\t{\n\t\t\t\tconst QCC_eval_t *eval_b = QCC_SRef_EvalConst(var_b);\n\t\t\t\tif (eval_b)\n\t\t\t\t{\n\t\t\t\t\tint bits = eval_b->_uint&0xff;\n\t\t\t\t\tint bitofs = eval_b->_uint>>8;\n\t\t\t\t\tvar_a = QCC_PR_StatementFlags(&pr_opcodes[OP_LSHIFT_U], var_a, QCC_MakeIntConst(32-bits-bitofs), NULL, flags&STFL_PRESERVEA);\n\t\t\t\t\tvar_c = QCC_PR_StatementFlags(&pr_opcodes[OP_RSHIFT_U], var_a, QCC_MakeIntConst(32-bits), NULL, flags&STFL_PRESERVEA);\n\t\t\t\t\treturn var_c;\n\t\t\t\t}\n\t\t\t}\n\t\t\tgoto badopcode;\n\n\t\tcase OP_BITAND_D:\n\t\t\tvar_a = QCC_PR_StatementFlags(&pr_opcodes[OP_CONV_DI64], var_a, nullsref, NULL, flags&STFL_PRESERVEA);\n\t\t\tvar_b = QCC_PR_StatementFlags(&pr_opcodes[OP_CONV_DI64], var_b, nullsref, NULL, (flags&STFL_PRESERVEB)?STFL_PRESERVEA:0);\n\t\t\tvar_c = QCC_PR_StatementFlags(&pr_opcodes[OP_BITAND_I64], var_a, var_b, NULL, 0);\n\t\t\tvar_c = QCC_PR_StatementFlags(&pr_opcodes[OP_CONV_I64D], var_c, nullsref, NULL, 0);\t//grr\n\t\t\treturn var_c;\n\t\tcase OP_BITOR_D:\n\t\t\tvar_a = QCC_PR_StatementFlags(&pr_opcodes[OP_CONV_DI64], var_a, nullsref, NULL, flags&STFL_PRESERVEA);\n\t\t\tvar_b = QCC_PR_StatementFlags(&pr_opcodes[OP_CONV_DI64], var_b, nullsref, NULL, (flags&STFL_PRESERVEB)?STFL_PRESERVEA:0);\n\t\t\tvar_c = QCC_PR_StatementFlags(&pr_opcodes[OP_BITOR_I64], var_a, var_b, NULL, 0);\n\t\t\tvar_c = QCC_PR_StatementFlags(&pr_opcodes[OP_CONV_I64D], var_c, nullsref, NULL, 0);\t//grr\n\t\t\treturn var_c;\n\n\t\tcase OP_LOAD_I64:\n\t\t\tvar_a.cast = type_integer;\n\t\t\tvar_b.cast = type_integer;\n\t\t\tvar_c = QCC_GetTemp(type_int64);\n\t\t\tQCC_PR_SimpleStatement(&pr_opcodes[OP_LOAD_FLD], var_a, var_b, var_c, false);\n\t\t\tvar_b.ofs++;\n\t\t\tvar_c.ofs++;\n\t\t\tQCC_PR_SimpleStatement(&pr_opcodes[OP_LOAD_FLD], var_a, var_b, var_c, false);\n\t\t\tvar_c.ofs--;\n\t\t\treturn var_c;\n\t\tcase OP_BITAND_I64:\n\t\t\tvar_a.cast = type_integer;\n\t\t\tvar_b.cast = type_integer;\n\t\t\tvar_c = QCC_GetTemp(type_int64);\n\t\t\tQCC_PR_SimpleStatement(&pr_opcodes[OP_BITAND_I], var_a, var_b, var_c, false);\n\t\t\tvar_a.ofs++;\n\t\t\tvar_b.ofs++;\n\t\t\tvar_c.ofs++;\n\t\t\tQCC_PR_SimpleStatement(&pr_opcodes[OP_BITAND_I], var_a, var_b, var_c, false);\n\t\t\tvar_c.ofs--;\n\t\t\treturn var_c;\n\t\tcase OP_BITOR_I64:\n\t\t\tvar_a.cast = type_integer;\n\t\t\tvar_b.cast = type_integer;\n\t\t\tvar_c = QCC_GetTemp(type_int64);\n\t\t\tQCC_PR_SimpleStatement(&pr_opcodes[OP_BITOR_I], var_a, var_b, var_c, false);\n\t\t\tvar_a.ofs++;\n\t\t\tvar_b.ofs++;\n\t\t\tvar_c.ofs++;\n\t\t\tQCC_PR_SimpleStatement(&pr_opcodes[OP_BITOR_I], var_a, var_b, var_c, false);\n\t\t\tvar_c.ofs--;\n\t\t\treturn var_c;\n\t\tcase OP_BITXOR_I64:\n\t\t\tvar_a.cast = type_integer;\n\t\t\tvar_b.cast = type_integer;\n\t\t\tvar_c = QCC_GetTemp(type_int64);\n\t\t\tQCC_PR_SimpleStatement(&pr_opcodes[OP_BITXOR_I], var_a, var_b, var_c, false);\n\t\t\tvar_a.ofs++;\n\t\t\tvar_b.ofs++;\n\t\t\tvar_c.ofs++;\n\t\t\tQCC_PR_SimpleStatement(&pr_opcodes[OP_BITXOR_I], var_a, var_b, var_c, false);\n\t\t\tvar_c.ofs--;\n\t\t\treturn var_c;\n\t\tcase OP_EQ_I64:\n\t\t\tvar_a.cast = type_integer;\n\t\t\tvar_b.cast = type_integer;\n\t\t\tvar_c = QCC_PR_StatementFlags(&pr_opcodes[OP_EQ_I], var_a, var_b, NULL, STFL_PRESERVEA|STFL_PRESERVEB);\n\t\t\tvar_a.ofs++;\n\t\t\tvar_b.ofs++;\n\t\t\tvar_a = QCC_PR_StatementFlags(&pr_opcodes[OP_EQ_I], var_a, var_b, NULL, 0);\n\t\t\treturn QCC_PR_StatementFlags(&pr_opcodes[OP_AND_I], var_c, var_a, NULL, 0);\n\t\tcase OP_NE_I64:\n\t\t\tvar_a.cast = type_integer;\n\t\t\tvar_b.cast = type_integer;\n\t\t\tvar_c = QCC_PR_StatementFlags(&pr_opcodes[OP_NE_I], var_a, var_b, NULL, STFL_PRESERVEA|STFL_PRESERVEB);\n\t\t\tvar_a.ofs++;\n\t\t\tvar_b.ofs++;\n\t\t\tvar_a = QCC_PR_StatementFlags(&pr_opcodes[OP_NE_I], var_a, var_b, NULL, 0);\n\t\t\treturn QCC_PR_StatementFlags(&pr_opcodes[OP_OR_I], var_c, var_a, NULL, 0);\n\n\t\t//statements where the rhs is an input int and can be swapped with a float\n\t\tcase OP_ADD_FI:\n\t\t\tvar_b = QCC_PR_StatementFlags(&pr_opcodes[OP_CONV_ITOF], var_b, nullsref, NULL, (flags&STFL_PRESERVEB)?STFL_PRESERVEA:0);\n\t\t\treturn QCC_PR_StatementFlags(&pr_opcodes[OP_ADD_F], var_a, var_b, NULL, flags&STFL_PRESERVEA);\n\t\tcase OP_SUB_FI:\n\t\t\tvar_b = QCC_PR_StatementFlags(&pr_opcodes[OP_CONV_ITOF], var_b, nullsref, NULL, (flags&STFL_PRESERVEB)?STFL_PRESERVEA:0);\n\t\t\treturn QCC_PR_StatementFlags(&pr_opcodes[OP_SUB_F], var_a, var_b, NULL, flags&STFL_PRESERVEA);\n\t\tcase OP_BITAND_FI:\n\t\t\tvar_b = QCC_PR_StatementFlags(&pr_opcodes[OP_CONV_ITOF], var_b, nullsref, NULL, (flags&STFL_PRESERVEB)?STFL_PRESERVEA:0);\n\t\t\treturn QCC_PR_StatementFlags(&pr_opcodes[OP_BITAND_F], var_a, var_b, NULL, flags&STFL_PRESERVEA);\n\t\tcase OP_BITOR_FI:\n\t\t\tvar_b = QCC_PR_StatementFlags(&pr_opcodes[OP_CONV_ITOF], var_b, nullsref, NULL, (flags&STFL_PRESERVEB)?STFL_PRESERVEA:0);\n\t\t\treturn QCC_PR_StatementFlags(&pr_opcodes[OP_BITOR_F], var_a, var_b, NULL, flags&STFL_PRESERVEA);\n\t\tcase OP_LT_FI:\n\t\t\tvar_b = QCC_PR_StatementFlags(&pr_opcodes[OP_CONV_ITOF], var_b, nullsref, NULL, (flags&STFL_PRESERVEB)?STFL_PRESERVEA:0);\n\t\t\treturn QCC_PR_StatementFlags(&pr_opcodes[OP_LT_F], var_a, var_b, NULL, flags&STFL_PRESERVEA);\n\t\tcase OP_LE_FI:\n\t\t\tvar_b = QCC_PR_StatementFlags(&pr_opcodes[OP_CONV_ITOF], var_b, nullsref, NULL, (flags&STFL_PRESERVEB)?STFL_PRESERVEA:0);\n\t\t\treturn QCC_PR_StatementFlags(&pr_opcodes[OP_LE_F], var_a, var_b, NULL, flags&STFL_PRESERVEA);\n\t\tcase OP_GT_FI:\n\t\t\tvar_b = QCC_PR_StatementFlags(&pr_opcodes[OP_CONV_ITOF], var_b, nullsref, NULL, (flags&STFL_PRESERVEB)?STFL_PRESERVEA:0);\n\t\t\treturn QCC_PR_StatementFlags(&pr_opcodes[OP_GT_F], var_a, var_b, NULL, flags&STFL_PRESERVEA);\n\t\tcase OP_GE_FI:\n\t\t\tvar_b = QCC_PR_StatementFlags(&pr_opcodes[OP_CONV_ITOF], var_b, nullsref, NULL, (flags&STFL_PRESERVEB)?STFL_PRESERVEA:0);\n\t\t\treturn QCC_PR_StatementFlags(&pr_opcodes[OP_GE_F], var_a, var_b, NULL, flags&STFL_PRESERVEA);\n\t\tcase OP_EQ_FI:\n\t\t\tvar_b = QCC_PR_StatementFlags(&pr_opcodes[OP_CONV_ITOF], var_b, nullsref, NULL, (flags&STFL_PRESERVEB)?STFL_PRESERVEA:0);\n\t\t\treturn QCC_PR_StatementFlags(&pr_opcodes[OP_EQ_F], var_a, var_b, NULL, flags&STFL_PRESERVEA);\n\t\tcase OP_NE_FI:\n\t\t\tvar_b = QCC_PR_StatementFlags(&pr_opcodes[OP_CONV_ITOF], var_b, nullsref, NULL, (flags&STFL_PRESERVEB)?STFL_PRESERVEA:0);\n\t\t\treturn QCC_PR_StatementFlags(&pr_opcodes[OP_NE_F], var_a, var_b, NULL, flags&STFL_PRESERVEA);\n\t\tcase OP_DIV_FI:\n\t\t\tvar_b = QCC_PR_StatementFlags(&pr_opcodes[OP_CONV_ITOF], var_b, nullsref, NULL, (flags&STFL_PRESERVEB)?STFL_PRESERVEA:0);\n\t\t\treturn QCC_PR_StatementFlags(&pr_opcodes[OP_DIV_F], var_a, var_b, NULL, flags&STFL_PRESERVEA);\n\t\tcase OP_MUL_FI:\n\t\t\tvar_b = QCC_PR_StatementFlags(&pr_opcodes[OP_CONV_ITOF], var_b, nullsref, NULL, (flags&STFL_PRESERVEB)?STFL_PRESERVEA:0);\n\t\t\treturn QCC_PR_StatementFlags(&pr_opcodes[OP_MUL_F], var_a, var_b, NULL, flags&STFL_PRESERVEA);\n\t\tcase OP_MUL_VI:\n\t\t\tvar_b = QCC_PR_StatementFlags(&pr_opcodes[OP_CONV_ITOF], var_b, nullsref, NULL, (flags&STFL_PRESERVEB)?STFL_PRESERVEA:0);\n\t\t\treturn QCC_PR_StatementFlags(&pr_opcodes[OP_MUL_VF], var_a, var_b, NULL, flags&STFL_PRESERVEA);\n\n\t\t//statements where the lhs is an input int and can be swapped with a float\n\t\tcase OP_ADD_IF:\n\t\t\tvar_a = QCC_PR_StatementFlags(&pr_opcodes[OP_CONV_ITOF], var_a, nullsref, NULL, flags&STFL_PRESERVEA);\n\t\t\treturn QCC_PR_StatementFlags(&pr_opcodes[OP_ADD_F], var_a, var_b, NULL, flags&STFL_PRESERVEB);\n\t\tcase OP_SUB_IF:\n\t\t\tvar_a = QCC_PR_StatementFlags(&pr_opcodes[OP_CONV_ITOF], var_a, nullsref, NULL, flags&STFL_PRESERVEA);\n\t\t\treturn QCC_PR_StatementFlags(&pr_opcodes[OP_SUB_F], var_a, var_b, NULL, flags&STFL_PRESERVEB);\n\t\tcase OP_BITAND_IF:\n\t\t\tvar_a = QCC_PR_StatementFlags(&pr_opcodes[OP_CONV_ITOF], var_a, nullsref, NULL, flags&STFL_PRESERVEA);\n\t\t\treturn QCC_PR_StatementFlags(&pr_opcodes[OP_BITAND_F], var_a, var_b, NULL, flags&STFL_PRESERVEB);\n\t\tcase OP_BITOR_IF:\n\t\t\tvar_a = QCC_PR_StatementFlags(&pr_opcodes[OP_CONV_ITOF], var_a, nullsref, NULL, flags&STFL_PRESERVEA);\n\t\t\treturn QCC_PR_StatementFlags(&pr_opcodes[OP_BITOR_F], var_a, var_b, NULL, flags&STFL_PRESERVEB);\n\t\tcase OP_LT_IF:\n\t\t\tvar_a = QCC_PR_StatementFlags(&pr_opcodes[OP_CONV_ITOF], var_a, nullsref, NULL, flags&STFL_PRESERVEA);\n\t\t\treturn QCC_PR_StatementFlags(&pr_opcodes[OP_LT_F], var_a, var_b, NULL, flags&STFL_PRESERVEB);\n\t\tcase OP_LE_IF:\n\t\t\tvar_a = QCC_PR_StatementFlags(&pr_opcodes[OP_CONV_ITOF], var_a, nullsref, NULL, flags&STFL_PRESERVEA);\n\t\t\treturn QCC_PR_StatementFlags(&pr_opcodes[OP_LE_F], var_a, var_b, NULL, flags&STFL_PRESERVEB);\n\t\tcase OP_GT_IF:\n\t\t\tvar_a = QCC_PR_StatementFlags(&pr_opcodes[OP_CONV_ITOF], var_a, nullsref, NULL, flags&STFL_PRESERVEA);\n\t\t\treturn QCC_PR_StatementFlags(&pr_opcodes[OP_GT_F], var_a, var_b, NULL, flags&STFL_PRESERVEB);\n\t\tcase OP_GE_IF:\n\t\t\tvar_a = QCC_PR_StatementFlags(&pr_opcodes[OP_CONV_ITOF], var_a, nullsref, NULL, flags&STFL_PRESERVEA);\n\t\t\treturn QCC_PR_StatementFlags(&pr_opcodes[OP_GE_F], var_a, var_b, NULL, flags&STFL_PRESERVEB);\n\t\tcase OP_EQ_IF:\n\t\t\tvar_a = QCC_PR_StatementFlags(&pr_opcodes[OP_CONV_ITOF], var_a, nullsref, NULL, flags&STFL_PRESERVEA);\n\t\t\treturn QCC_PR_StatementFlags(&pr_opcodes[OP_EQ_F], var_a, var_b, NULL, flags&STFL_PRESERVEB);\n\t\tcase OP_NE_IF:\n\t\t\tvar_a = QCC_PR_StatementFlags(&pr_opcodes[OP_CONV_ITOF], var_a, nullsref, NULL, flags&STFL_PRESERVEA);\n\t\t\treturn QCC_PR_StatementFlags(&pr_opcodes[OP_NE_F], var_a, var_b, NULL, flags&STFL_PRESERVEB);\n\t\tcase OP_DIV_IF:\n\t\t\tvar_a = QCC_PR_StatementFlags(&pr_opcodes[OP_CONV_ITOF], var_a, nullsref, NULL, flags&STFL_PRESERVEA);\n\t\t\treturn QCC_PR_StatementFlags(&pr_opcodes[OP_DIV_F], var_a, var_b, NULL, flags&STFL_PRESERVEB);\n\t\tcase OP_MUL_IF:\n\t\t\tvar_a = QCC_PR_StatementFlags(&pr_opcodes[OP_CONV_ITOF], var_a, nullsref, NULL, flags&STFL_PRESERVEA);\n\t\t\treturn QCC_PR_StatementFlags(&pr_opcodes[OP_MUL_F], var_a, var_b, NULL, flags&STFL_PRESERVEB);\n\t\tcase OP_MUL_IV:\n\t\t\tvar_a = QCC_PR_StatementFlags(&pr_opcodes[OP_CONV_ITOF], var_a, nullsref, NULL, flags&STFL_PRESERVEA);\n\t\t\treturn QCC_PR_StatementFlags(&pr_opcodes[OP_MUL_FV], var_a, var_b, NULL, flags&STFL_PRESERVEB);\n\n\t\t//statements where both sides will need to be converted to floats to work\n\n\t\tcase OP_LE_I:\n\t\t\tvar_a = QCC_PR_StatementFlags(&pr_opcodes[OP_CONV_ITOF], var_a, nullsref, NULL, flags&STFL_PRESERVEA);\n\t\t\treturn QCC_PR_StatementFlags(&pr_opcodes[OP_LE_FI], var_a, var_b, NULL, flags&STFL_PRESERVEB);\n\t\tcase OP_GE_I:\n\t\t\tvar_a = QCC_PR_StatementFlags(&pr_opcodes[OP_CONV_ITOF], var_a, nullsref, NULL, flags&STFL_PRESERVEA);\n\t\t\treturn QCC_PR_StatementFlags(&pr_opcodes[OP_GE_FI], var_a, var_b, NULL, flags&STFL_PRESERVEB);\n\t\tcase OP_LT_I:\n\t\t\tvar_a = QCC_PR_StatementFlags(&pr_opcodes[OP_CONV_ITOF], var_a, nullsref, NULL, flags&STFL_PRESERVEA);\n\t\t\treturn QCC_PR_StatementFlags(&pr_opcodes[OP_LT_FI], var_a, var_b, NULL, flags&STFL_PRESERVEB);\n\t\tcase OP_GT_I:\n\t\t\tvar_a = QCC_PR_StatementFlags(&pr_opcodes[OP_CONV_ITOF], var_a, nullsref, NULL, flags&STFL_PRESERVEA);\n\t\t\treturn QCC_PR_StatementFlags(&pr_opcodes[OP_GT_FI], var_a, var_b, NULL, flags&STFL_PRESERVEB);\n\t\tcase OP_EQ_FLD:\n\t\tcase OP_EQ_I:\n\t\t\treturn QCC_PR_StatementFlags(&pr_opcodes[OP_EQ_FNC], var_a, var_b, NULL, flags&(STFL_PRESERVEA|STFL_PRESERVEB));\n\t\tcase OP_NE_FLD:\n\t\tcase OP_NE_I:\n\t\t\treturn QCC_PR_StatementFlags(&pr_opcodes[OP_NE_FNC], var_a, var_b, NULL, flags&(STFL_PRESERVEA|STFL_PRESERVEB));\n\t\tcase OP_NOT_I:\n\t\t\treturn QCC_PR_StatementFlags(&pr_opcodes[OP_EQ_FNC], var_a, QCC_MakeIntConst(0), NULL, flags&(STFL_PRESERVEA));\n\n\t\tcase OP_AND_I:\n\t\tcase OP_AND_FI:\n\t\tcase OP_AND_IF:\n\t\tcase OP_AND_ANY:\n\t\t\tif (var_a.cast->type == ev_vector && flag_vectorlogic)\t//we can do a dot-product to test if a vector has a value, instead of a double-not\n\t\t\t\tvar_a = QCC_PR_StatementFlags(&pr_opcodes[OP_MUL_V], var_a, var_a, NULL, STFL_PRESERVEA | (flags&STFL_PRESERVEA?STFL_PRESERVEB:0));\n\t\t\tif (var_b.cast->type == ev_vector && flag_vectorlogic)\n\t\t\t\tvar_b = QCC_PR_StatementFlags(&pr_opcodes[OP_MUL_V], var_b, var_b, NULL, STFL_PRESERVEA | (flags&STFL_PRESERVEB?STFL_PRESERVEB:0));\n\n\t\t\tif (((var_a.cast->size != 1 && flag_vectorlogic) || (var_a.cast->type == ev_string && flag_ifstring)) && ((var_b.cast->size != 1 && flag_vectorlogic) || (var_b.cast->type == ev_string && flag_ifstring)))\n\t\t\t{\t//just 3 extra instructions instead of 4.\n\t\t\t\tvar_a = QCC_PR_GenerateLogicalNot(var_a, \"%s used as truth value\");\n\t\t\t\tvar_b = QCC_PR_GenerateLogicalNot(var_b, \"%s used as truth value\");\n\t\t\t\tvar_a = QCC_PR_StatementFlags(&pr_opcodes[OP_OR_ANY], var_a, var_b, NULL, 0);\n\t\t\t\treturn QCC_PR_GenerateLogicalNot(var_a, \"%s isn't a float...\");\n\t\t\t}\n\t\t\tif (var_a.cast->type == ev_string && flag_ifstring)\n\t\t\t\tvar_a = QCC_PR_StatementFlags(&pr_opcodes[OP_NE_S], var_a, QCC_MakeStringConst(\"\"), NULL, (flags&STFL_PRESERVEA?STFL_PRESERVEA:0));\n\t\t\telse if ((var_a.cast->type == ev_string && flag_ifstring))\n\t\t\t\tvar_a = QCC_PR_GenerateLogicalNot(QCC_PR_GenerateLogicalNot(var_a, \"%s used as truth value\"), \"%s isn't a float...\");\n\t\t\tif (var_b.cast->type == ev_string && flag_ifstring)\n\t\t\t\tvar_b = QCC_PR_StatementFlags(&pr_opcodes[OP_NE_S], QCC_MakeStringConst(\"\"), var_b, NULL, (flags&STFL_PRESERVEB?STFL_PRESERVEB:0));\n\t\t\telse if (var_b.cast->size != 1 && flag_vectorlogic)\n\t\t\t\tvar_b = QCC_PR_GenerateLogicalNot(QCC_PR_GenerateLogicalNot(var_b, \"%s used as truth value\"), \"%s isn't a float...\");\n\n\t\t\tif (var_a.cast->type != ev_float && var_b.cast->type != ev_float && QCC_OPCodeValid(&pr_opcodes[OP_AND_I]))\n\t\t\t\top = &pr_opcodes[OP_AND_I];\t//negative 0 as a float is considered zero by the fpu, which makes 0x80000000 tricky. avoid that if we can.\n\t\t\telse if (var_a.cast->type != ev_float && var_b.cast->type == ev_float && QCC_OPCodeValid(&pr_opcodes[OP_AND_IF]))\n\t\t\t\top = &pr_opcodes[OP_AND_IF];\n\t\t\telse if (var_a.cast->type == ev_float && var_b.cast->type != ev_float && QCC_OPCodeValid(&pr_opcodes[OP_AND_FI]))\n\t\t\t\top = &pr_opcodes[OP_AND_FI];\n\t\t\telse\n\t\t\t\top = &pr_opcodes[OP_AND_F];\t//generally works. if there's no other choice then meh.\n\t\t\tbreak;\n\t\tcase OP_OR_I:\n\t\tcase OP_OR_FI:\n\t\tcase OP_OR_IF:\n\t\tcase OP_OR_ANY:\n\t\t\tif (var_a.cast->type == ev_vector && flag_vectorlogic)\t//we can do a dot-product to test if a vector has a value, instead of a double-not\n\t\t\t\tvar_a = QCC_PR_StatementFlags(&pr_opcodes[OP_MUL_V], var_a, var_a, NULL, STFL_PRESERVEA | (flags&STFL_PRESERVEA?STFL_PRESERVEB:0));\n\t\t\tif (var_b.cast->type == ev_vector && flag_vectorlogic)\n\t\t\t\tvar_b = QCC_PR_StatementFlags(&pr_opcodes[OP_MUL_V], var_b, var_b, NULL, STFL_PRESERVEA | (flags&STFL_PRESERVEB?STFL_PRESERVEB:0));\n\n\t\t\tif (((var_a.cast->size != 1 && flag_vectorlogic) || (var_a.cast->type == ev_string && flag_ifstring)) && ((var_b.cast->size != 1 && flag_vectorlogic) || (var_b.cast->type == ev_string && flag_ifstring)))\n\t\t\t{\t//just 3 extra instructions instead of 4.\n\t\t\t\tvar_a = QCC_PR_GenerateLogicalNot(var_a, \"%s used as truth value\");\n\t\t\t\tvar_b = QCC_PR_GenerateLogicalNot(var_b, \"%s used as truth value\");\n\t\t\t\tvar_a = QCC_PR_StatementFlags(&pr_opcodes[OP_AND_ANY], var_a, var_b, NULL, 0);\n\t\t\t\treturn QCC_PR_GenerateLogicalNot(var_a, \"%s isn't a float...\");\n\t\t\t}\n\t\t\tif (var_a.cast->type == ev_string && flag_ifstring)\n\t\t\t\tvar_a = QCC_PR_StatementFlags(&pr_opcodes[OP_NE_S], var_a, QCC_MakeStringConst(\"\"), NULL, (flags&STFL_PRESERVEA?STFL_PRESERVEA:0));\n\t\t\telse if (var_a.cast->size != 1 && flag_vectorlogic)\n\t\t\t\tvar_a = QCC_PR_GenerateLogicalNot(QCC_PR_GenerateLogicalNot(var_a, \"%s used as truth value\"), \"%s isn't a float...\");\n\t\t\tif (var_b.cast->type == ev_string && flag_ifstring)\n\t\t\t\tvar_b = QCC_PR_StatementFlags(&pr_opcodes[OP_NE_S], QCC_MakeStringConst(\"\"), var_b, NULL, (flags&STFL_PRESERVEB?STFL_PRESERVEB:0));\n\t\t\telse if (var_b.cast->size != 1 && flag_vectorlogic)\n\t\t\t\tvar_b = QCC_PR_GenerateLogicalNot(QCC_PR_GenerateLogicalNot(var_b, \"%s used as truth value\"), \"%s isn't a float...\");\n\t\t\tif (var_a.cast->type != ev_float && var_b.cast->type != ev_float && QCC_OPCodeValid(&pr_opcodes[OP_OR_I]))\n\t\t\t\top = &pr_opcodes[OP_OR_I];\t//negative 0 as a float is considered zero by the fpu, which makes 0x80000000 tricky. avoid that if we can.\n\t\t\telse if (var_a.cast->type != ev_float && var_b.cast->type == ev_float && QCC_OPCodeValid(&pr_opcodes[OP_OR_IF]))\n\t\t\t\top = &pr_opcodes[OP_OR_IF];\n\t\t\telse if (var_a.cast->type == ev_float && var_b.cast->type != ev_float && QCC_OPCodeValid(&pr_opcodes[OP_OR_FI]))\n\t\t\t\top = &pr_opcodes[OP_OR_FI];\n\t\t\telse\n\t\t\t\top = &pr_opcodes[OP_OR_F];\t//generally works. if there's no other choice then meh.\n\t\t\tbreak;\n\n\n\t\tcase OP_LOADP_U8:\n\t\t\tif (!var_b.cast) var_b = QCC_MakeUIntConst(0);\n\t\t\tvar_c = QCC_PR_StatementFlags(&pr_opcodes[OP_BITAND_I], var_b, QCC_MakeUIntConst(3), NULL, STFL_PRESERVEA);\t//read byte index for later\n\t\t\tvar_b = QCC_PR_StatementFlags(&pr_opcodes[OP_RSHIFT_I], var_b, QCC_MakeUIntConst(2), NULL, (flags&STFL_PRESERVEB)?STFL_PRESERVEA:0);\t//go from byte to word precision\n\t\t\tvar_a = QCC_PR_StatementFlags(&pr_opcodes[OP_LOADP_I], var_a, var_b, NULL, flags&STFL_PRESERVEA); //read it as a[b], WARNING: var_a may be misaligned\n\t\t\tvar_c = QCC_PR_StatementFlags(&pr_opcodes[OP_MUL_I], QCC_MakeUIntConst(8), var_c, NULL, 0); //bytes to bits\n\t\t\tvar_c = QCC_PR_StatementFlags(&pr_opcodes[OP_SUB_I], QCC_MakeUIntConst(32-8), var_c, NULL, 0); //32-bits-bitofs\n\t\t\tvar_a = QCC_PR_StatementFlags(&pr_opcodes[OP_LSHIFT_U], var_a, var_c, NULL, 0); //shift the part we want to the high bit\n\t\t\treturn QCC_PR_StatementFlags(&pr_opcodes[OP_RSHIFT_U], var_a, QCC_MakeIntConst(32-8), NULL, 0); //and shift down to the bits we actually want\n\t\tcase OP_LOADP_I8:\n\t\t\tif (!var_b.cast) var_b = QCC_MakeUIntConst(0);\n\t\t\tif (QCC_OPCodeValid(&pr_opcodes[OP_LOADP_U8]))\n\t\t\t{\t//can just reextend it in combination with the older instruction set, for 2 or 3 instructions instead of 7.\n\t\t\t\tvar_c = QCC_PR_StatementFlags(&pr_opcodes[OP_LOADP_U8], var_a, var_b, NULL, flags&(STFL_PRESERVEA|STFL_PRESERVEB));\n\t\t\t\treturn QCC_PR_StatementFlags(&pr_opcodes[OP_BITEXTEND_I], var_c, QCC_MakeUIntConst(8), NULL, 0);\n\t\t\t}\n\t\t\tvar_c = QCC_PR_StatementFlags(&pr_opcodes[OP_BITAND_I], var_b, QCC_MakeUIntConst(3), NULL, STFL_PRESERVEA);\t//read byte index for later\n\t\t\tvar_b = QCC_PR_StatementFlags(&pr_opcodes[OP_RSHIFT_I], var_b, QCC_MakeUIntConst(2), NULL, (flags&STFL_PRESERVEB)?STFL_PRESERVEA:0);\t//go from byte to word precision\n\t\t\tvar_a = QCC_PR_StatementFlags(&pr_opcodes[OP_LOADP_I], var_a, var_b, NULL, flags&STFL_PRESERVEA); //read it as a[b], WARNING: var_a may be misaligned\n\t\t\tvar_c = QCC_PR_StatementFlags(&pr_opcodes[OP_MUL_I], QCC_MakeUIntConst(8), var_c, NULL, 0); //bytes to bits\n\t\t\tvar_c = QCC_PR_StatementFlags(&pr_opcodes[OP_SUB_I], QCC_MakeUIntConst(32-8), var_c, NULL, 0); //32-bits-bitofs\n\t\t\tvar_a = QCC_PR_StatementFlags(&pr_opcodes[OP_LSHIFT_I], var_a, var_c, NULL, 0); //shift the part we want to the high bit\n\t\t\treturn QCC_PR_StatementFlags(&pr_opcodes[OP_RSHIFT_I], var_a, QCC_MakeIntConst(32-8), NULL, 0); //and shift down to the bits we actually want\n\t\tcase OP_LOADP_U16:\n\t\t\tif (!var_b.cast) var_b = QCC_MakeUIntConst(0);\n\t\t\tvar_c = QCC_PR_StatementFlags(&pr_opcodes[OP_BITAND_I], var_b, QCC_MakeUIntConst(1), NULL, STFL_PRESERVEA);\t//read byte index for later\n\t\t\tvar_b = QCC_PR_StatementFlags(&pr_opcodes[OP_RSHIFT_I], var_b, QCC_MakeUIntConst(1), NULL, (flags&STFL_PRESERVEB)?STFL_PRESERVEA:0);\t//go from short to word precision\n\t\t\tvar_a = QCC_PR_StatementFlags(&pr_opcodes[OP_LOADP_I], var_a, var_b, NULL, flags&STFL_PRESERVEA); //read it as a[b], WARNING: var_a may be misaligned\n\t\t\tvar_c = QCC_PR_StatementFlags(&pr_opcodes[OP_MUL_I], QCC_MakeUIntConst(16), var_c, NULL, 0); //short to bits\n\t\t\tvar_c = QCC_PR_StatementFlags(&pr_opcodes[OP_SUB_I], QCC_MakeUIntConst(32-16), var_c, NULL, 0); //32-bits-bitofs\n\t\t\tvar_a = QCC_PR_StatementFlags(&pr_opcodes[OP_LSHIFT_U], var_a, var_c, NULL, 0); //shift the part we want to the high bit\n\t\t\treturn QCC_PR_StatementFlags(&pr_opcodes[OP_RSHIFT_U], var_a, QCC_MakeIntConst(32-16), NULL, 0); //and shift down to the bits we actually want\n\t\tcase OP_LOADP_I16:\n\t\t\tif (!var_b.cast) var_b = QCC_MakeUIntConst(0);\n\t\t\tvar_c = QCC_PR_StatementFlags(&pr_opcodes[OP_BITAND_I], var_b, QCC_MakeUIntConst(1), NULL, STFL_PRESERVEA);\t//read byte index for later\n\t\t\tvar_b = QCC_PR_StatementFlags(&pr_opcodes[OP_RSHIFT_I], var_b, QCC_MakeUIntConst(1), NULL, (flags&STFL_PRESERVEB)?STFL_PRESERVEA:0);\t//go from short to word precision\n\t\t\tvar_a = QCC_PR_StatementFlags(&pr_opcodes[OP_LOADP_I], var_a, var_b, NULL, flags&STFL_PRESERVEA); //read it as a[b], WARNING: var_a may be misaligned\n\t\t\tvar_c = QCC_PR_StatementFlags(&pr_opcodes[OP_MUL_I], QCC_MakeUIntConst(16), var_c, NULL, 0); //short to bits\n\t\t\tvar_c = QCC_PR_StatementFlags(&pr_opcodes[OP_SUB_I], QCC_MakeUIntConst(32-16), var_c, NULL, 0); //32-bits-bitofs\n\t\t\tvar_a = QCC_PR_StatementFlags(&pr_opcodes[OP_LSHIFT_I], var_a, var_c, NULL, 0); //shift the part we want to the high bit\n\t\t\treturn QCC_PR_StatementFlags(&pr_opcodes[OP_RSHIFT_I], var_a, QCC_MakeIntConst(32-16), NULL, 0); //and shift down to the bits we actually want\n\n//\t\tcase OP_LOADP_V:\n//\t\t\tbreak;\n\t\tcase OP_LOADP_F:\n\t\tcase OP_LOADP_S:\n\t\tcase OP_LOADP_ENT:\n\t\tcase OP_LOADP_FLD:\n\t\tcase OP_LOADP_FNC:\n\t\tcase OP_LOADP_I:\n\t\t\t{\n\t\t\t\tQCC_sref_t fnc = QCC_PR_EmulationFunc(memgetval);\n\t\t\t\tif (!fnc.cast)\n\t\t\t\t{\n\t\t\t\t\tQCC_PR_ParseWarning(0, \"memgetval function not defined: cannot emulate OP_LOADP_*\");\n\t\t\t\t\tgoto badopcode;\n\t\t\t\t}\n\t\t\t\tvar_c = QCC_PR_GenerateFunctionCall2(nullsref, fnc, var_a, type_pointer, QCC_PR_StatementFlags(&pr_opcodes[OP_CONV_ITOF], var_b, nullsref, NULL, (flags&STFL_PRESERVEB)?STFL_PRESERVEA:0), type_float);\n\t\t\t\tvar_c.cast = *op->type_c;\n\t\t\t\treturn var_c;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase OP_ADD_PIW:\n\t\t\tif (flag_undefwordsize)\t//fine if its the engine's doing it, not so fine if we're assuming it in the qcc.\n\t\t\t\tQCC_PR_ParseWarning(ERR_BADEXTENSION, \"Making assumptions about pointer sizes was blocked.\");\n\t\t\tvar_b = QCC_PR_StatementFlags(&pr_opcodes[OP_MUL_I], QCC_MakeIntConst(VMWORDSIZE), var_b, NULL, flags&STFL_PRESERVEB);\n\t\t\tvar_c = QCC_PR_StatementFlags(&pr_opcodes[OP_ADD_I], var_a, var_b, NULL, flags&~STFL_PRESERVEB);\n\t\t\tvar_c.cast = var_a.cast;\n\t\t\treturn var_c;\n\n\t\tcase OP_LT_U:\n\t\tcase OP_LE_U:\n\t\t\tif (!QCC_OPCodeValid(&pr_opcodes[OP_LT_I]))\n\t\t\t{\t//we're kinda fucked. go straight for floats instead. with any luck we might avoid precision issues.\n\t\t\t\tvar_a = QCC_PR_StatementFlags(&pr_opcodes[OP_CONV_UF], var_a, nullsref, NULL, flags&STFL_PRESERVEA);\n\t\t\t\tvar_b = QCC_PR_StatementFlags(&pr_opcodes[OP_CONV_UF], var_b, nullsref, NULL, (flags&STFL_PRESERVEB?STFL_PRESERVEA:0));\n\t\t\t\tif (op == &pr_opcodes[OP_LT_U])\n\t\t\t\t\treturn QCC_PR_StatementFlags(&pr_opcodes[OP_LT_F], var_a, var_b, NULL, flags&~(STFL_PRESERVEA|STFL_PRESERVEB));\n\t\t\t\telse if (op == &pr_opcodes[OP_LE_U])\n\t\t\t\t\treturn QCC_PR_StatementFlags(&pr_opcodes[OP_LE_F], var_a, var_b, NULL, flags&~(STFL_PRESERVEA|STFL_PRESERVEB));\n\t\t\t\tgoto badopcode;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t//bias down, forcing it to wrap. then use the regular compares\n\t\t\t\tvar_a = QCC_PR_StatementFlags(&pr_opcodes[OP_SUB_U], var_a, QCC_MakeUIntConst(0x80000000), NULL, flags&STFL_PRESERVEA);\n\t\t\t\tvar_b = QCC_PR_StatementFlags(&pr_opcodes[OP_SUB_U], var_b, QCC_MakeUIntConst(0x80000000), NULL, (flags&STFL_PRESERVEB?STFL_PRESERVEA:0));\n\t\t\t\tvar_a.cast = type_integer;\n\t\t\t\tvar_b.cast = type_integer;\n\t\t\t\tif (op == &pr_opcodes[OP_LT_U])\n\t\t\t\t\treturn QCC_PR_StatementFlags(&pr_opcodes[OP_LT_I], var_a, var_b, NULL, flags&~(STFL_PRESERVEA|STFL_PRESERVEB));\n\t\t\t\telse if (op == &pr_opcodes[OP_LE_U])\n\t\t\t\t\treturn QCC_PR_StatementFlags(&pr_opcodes[OP_LE_I], var_a, var_b, NULL, flags&~(STFL_PRESERVEA|STFL_PRESERVEB));\n\t\t\t\tgoto badopcode;\n\t\t\t}\n\n\t\t//FIXME: assume the high/sign bit is not set\n\t\tcase OP_DIV_U:\n\t\t\tQCC_PR_ParseWarning(0, \"OP_DIV_U emulation: assuming the high bit is not set...\");\n\t\t\tvar_a.cast = type_integer;\tvar_b.cast = type_integer;\tvar_c = QCC_PR_StatementFlags(&pr_opcodes[OP_DIV_I],\tvar_a, var_b, NULL, flags); var_c.cast = type_uint;\n\t\t\treturn var_c;\n\n\t\t//other uint ops have the same bit pattern\n\t\tcase OP_ADD_U:\t\tvar_a.cast = type_integer;\tvar_b.cast = type_integer;\tvar_c = QCC_PR_StatementFlags(&pr_opcodes[OP_ADD_I],\tvar_a, var_b, NULL, flags); var_c.cast = type_uint; return var_c;\n\t\tcase OP_SUB_U:\t\tvar_a.cast = type_integer;\tvar_b.cast = type_integer;\tvar_c = QCC_PR_StatementFlags(&pr_opcodes[OP_SUB_I],\tvar_a, var_b, NULL, flags); var_c.cast = type_uint; return var_c;\n\t\tcase OP_MUL_U:\t\tvar_a.cast = type_integer;\tvar_b.cast = type_integer;\tvar_c = QCC_PR_StatementFlags(&pr_opcodes[OP_MUL_I],\tvar_a, var_b, NULL, flags); var_c.cast = type_uint; return var_c; //carry/overflow differ, but we don't provide such feedback.\n//\t\tcase OP_MOD_U:\t\tvar_a.cast = type_integer;\tvar_b.cast = type_integer;\tvar_c = QCC_PR_StatementFlags(&pr_opcodes[OP_MOD_I],\tvar_a, var_b, NULL, flags); var_c.cast = type_uint; return var_c;\n\t\tcase OP_BITAND_U:\tvar_a.cast = type_integer;\tvar_b.cast = type_integer;\tvar_c = QCC_PR_StatementFlags(&pr_opcodes[OP_BITAND_I],\tvar_a, var_b, NULL, flags); var_c.cast = type_uint; return var_c;\n\t\tcase OP_BITOR_U:\tvar_a.cast = type_integer;\tvar_b.cast = type_integer;\tvar_c = QCC_PR_StatementFlags(&pr_opcodes[OP_BITOR_I],\tvar_a, var_b, NULL, flags); var_c.cast = type_uint; return var_c;\n\t\tcase OP_BITXOR_U:\tvar_a.cast = type_integer;\tvar_b.cast = type_integer;\tvar_c = QCC_PR_StatementFlags(&pr_opcodes[OP_BITXOR_I],\tvar_a, var_b, NULL, flags); var_c.cast = type_uint; return var_c;\n\t\tcase OP_BITNOT_U:\tvar_a.cast = type_integer;\tvar_b.cast = type_integer;\tvar_c = QCC_PR_StatementFlags(&pr_opcodes[OP_BITNOT_I],\tvar_a, var_b, NULL, flags); var_c.cast = type_uint; return var_c;\n\t\tcase OP_BITCLR_U:\tvar_a.cast = type_integer;\tvar_b.cast = type_integer;\tvar_c = QCC_PR_StatementFlags(&pr_opcodes[OP_BITCLR_I],\tvar_a, var_b, NULL, flags); var_c.cast = type_uint; return var_c;\n\t\tcase OP_LSHIFT_U:\tvar_a.cast = type_integer;\tvar_b.cast = type_integer;\tvar_c = QCC_PR_StatementFlags(&pr_opcodes[OP_LSHIFT_I],\tvar_a, var_b, NULL, flags); var_c.cast = type_uint; return var_c;\n\t\tcase OP_GE_U:\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\treturn  QCC_PR_StatementFlags(&pr_opcodes[OP_LE_U],\t\tvar_b, var_a, NULL, flags);\t//just swap the args, fewer opcodes needed.\n\t\tcase OP_GT_U:\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\treturn  QCC_PR_StatementFlags(&pr_opcodes[OP_LT_U],\t\tvar_b, var_a, NULL, flags);\t//just swap the args, fewer opcodes needed.\n\t\tcase OP_EQ_U:\t\tvar_a.cast = type_integer;\tvar_b.cast = type_integer;\tvar_c = QCC_PR_StatementFlags(&pr_opcodes[OP_EQ_I],\t\tvar_a, var_b, NULL, flags); var_c.cast = type_uint; return var_c;\n\t\tcase OP_NE_U:\t\tvar_a.cast = type_integer;\tvar_b.cast = type_integer;\tvar_c = QCC_PR_StatementFlags(&pr_opcodes[OP_NE_I],\t\tvar_a, var_b, NULL, flags); var_c.cast = type_uint; return var_c;\n\n\t\tcase OP_ADD_U64:\t\tvar_a.cast = type_int64;\tvar_b.cast = type_int64;\tvar_c = QCC_PR_StatementFlags(&pr_opcodes[OP_ADD_I64],\t\tvar_a, var_b, NULL, flags); var_c.cast = type_uint64; return var_c;\n\t\tcase OP_SUB_U64:\t\tvar_a.cast = type_int64;\tvar_b.cast = type_int64;\tvar_c = QCC_PR_StatementFlags(&pr_opcodes[OP_SUB_I64],\t\tvar_a, var_b, NULL, flags); var_c.cast = type_uint64; return var_c;\n\t\tcase OP_MUL_U64:\t\tvar_a.cast = type_int64;\tvar_b.cast = type_int64;\tvar_c = QCC_PR_StatementFlags(&pr_opcodes[OP_MUL_I64],\t\tvar_a, var_b, NULL, flags); var_c.cast = type_uint64; return var_c;\n//\t\tcase OP_MOD_U64:\t\tvar_a.cast = type_int64;\tvar_b.cast = type_int64;\tvar_c = QCC_PR_StatementFlags(&pr_opcodes[OP_MOD_I64],\t\tvar_a, var_b, NULL, flags); var_c.cast = type_uint64; return var_c;\n\t\tcase OP_BITAND_U64:\t\tvar_a.cast = type_int64;\tvar_b.cast = type_int64;\tvar_c = QCC_PR_StatementFlags(&pr_opcodes[OP_BITAND_I64],\tvar_a, var_b, NULL, flags); var_c.cast = type_uint64; return var_c;\n\t\tcase OP_BITOR_U64:\t\tvar_a.cast = type_int64;\tvar_b.cast = type_int64;\tvar_c = QCC_PR_StatementFlags(&pr_opcodes[OP_BITOR_I64],\tvar_a, var_b, NULL, flags); var_c.cast = type_uint64; return var_c;\n\t\tcase OP_BITXOR_U64:\t\tvar_a.cast = type_int64;\tvar_b.cast = type_int64;\tvar_c = QCC_PR_StatementFlags(&pr_opcodes[OP_BITXOR_I64],\tvar_a, var_b, NULL, flags); var_c.cast = type_uint64; return var_c;\n\t\tcase OP_BITNOT_U64:\t\tvar_a.cast = type_int64;\tvar_b.cast = type_int64;\tvar_c = QCC_PR_StatementFlags(&pr_opcodes[OP_BITNOT_I64],\tvar_a, var_b, NULL, flags); var_c.cast = type_uint64; return var_c;\n\t\tcase OP_BITCLR_U64:\t\tvar_a.cast = type_int64;\tvar_b.cast = type_int64;\tvar_c = QCC_PR_StatementFlags(&pr_opcodes[OP_BITCLR_I64],\tvar_a, var_b, NULL, flags); var_c.cast = type_uint64; return var_c;\n\t\tcase OP_LSHIFT_U64I:\tvar_a.cast = type_int64;\tvar_b.cast = type_int64;\tvar_c = QCC_PR_StatementFlags(&pr_opcodes[OP_LSHIFT_I64I],\tvar_a, var_b, NULL, flags); var_c.cast = type_uint64; return var_c;\n\t\tcase OP_GE_U64:\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\treturn  QCC_PR_StatementFlags(&pr_opcodes[OP_LE_U64],\t\tvar_b, var_a, NULL, flags);\n\t\tcase OP_GT_U64:\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\treturn  QCC_PR_StatementFlags(&pr_opcodes[OP_LT_U64],\t\tvar_b, var_a, NULL, flags);\n\t\tcase OP_EQ_U64:\t\t\tvar_a.cast = type_int64;\tvar_b.cast = type_int64;\tvar_c = QCC_PR_StatementFlags(&pr_opcodes[OP_EQ_I64],\t\tvar_a, var_b, NULL, flags); var_c.cast = type_uint64; return var_c;\n\t\tcase OP_NE_U64:\t\t\tvar_a.cast = type_int64;\tvar_b.cast = type_int64;\tvar_c = QCC_PR_StatementFlags(&pr_opcodes[OP_NE_I64],\t\tvar_a, var_b, NULL, flags); var_c.cast = type_uint64; return var_c;\n\t\tcase OP_GE_I64:\t\t\tvar_a.cast = type_int64;\tvar_b.cast = type_int64;\treturn  QCC_PR_StatementFlags(&pr_opcodes[OP_LE_I64],\t\tvar_b, var_a, NULL, flags);\n\t\tcase OP_GT_I64:\t\t\tvar_a.cast = type_int64;\tvar_b.cast = type_int64;\treturn  QCC_PR_StatementFlags(&pr_opcodes[OP_LT_I64],\t\tvar_b, var_a, NULL, flags);\n\t\tcase OP_GE_D:\t\t\treturn  QCC_PR_StatementFlags(&pr_opcodes[OP_LE_D],\t\tvar_b, var_a, NULL, flags);\n\t\tcase OP_GT_D:\t\t\treturn  QCC_PR_StatementFlags(&pr_opcodes[OP_LT_D],\t\tvar_b, var_a, NULL, flags);\n\n\t\tcase OP_STORE_I64:\n\t\t\tvar_c = var_b;\n\t\t\tvar_c.cast = type_integer;\n\t\t\tQCC_FreeTemp(QCC_PR_StatementFlags(&pr_opcodes[OP_STORE_FLD], var_a, var_c, NULL, STFL_PRESERVEA|STFL_PRESERVEB));\n\t\t\tvar_a.ofs++;\n\t\t\tvar_c.ofs++;\n\t\t\tQCC_FreeTemp(QCC_PR_StatementFlags(&pr_opcodes[OP_STORE_FLD], var_a, var_c, NULL, STFL_PRESERVEA|STFL_PRESERVEB));\n\t\t\treturn var_b;\n\n\t\tdefault:\n\t\tbadopcode:\n\t\t\tif (QCC_OPCodeValidForTarget(QCF_FTE, QCTARGVER_FTE_DEF, op))\n\t\t\t\tQCC_PR_ParseWarning(ERR_BADEXTENSION, \"Opcode \\\"%s|%s\\\" not valid for target. Consider the use of: #pragma target fte\", op->name, op->opname);\n\t\t\telse if (QCC_OPCodeValidForTarget(QCF_FTE, QCTARGVER_FTE_MAX, op))\n\t\t\t\tQCC_PR_ParseWarning(ERR_BADEXTENSION, \"Opcode \\\"%s|%s\\\" not valid for target. Consider the use of: #pragma target fte_%u\", op->name, op->opname, QCTARGVER_FTE_MAX);\n\t\t\telse\n\t\t\t\tQCC_PR_ParseWarning(ERR_BADEXTENSION, \"Opcode \\\"%s|%s\\\" is not supported.\", op->name, op->opname);\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tif (!pr_scope)\n\t{\n\t\tswitch(op - pr_opcodes)\n\t\t{\n\t\tcase OP_GLOBALADDRESS:\n\t\t\tif (flag_pointerrelocs)\n\t\t\t\treturn QCC_MakeGAddress(type_pointer, var_a.sym, var_a.ofs + (var_b.cast?QCC_Eval_Int(QCC_SRef_EvalConst(var_b), var_b.cast):0), 0);\n\t\t\tbreak;\n\t\t}\n\n\t\tQCC_PR_ParseWarning(ERR_BADEXTENSION, \"Unable to generate statements at global scope (%s).\", op->opname);\n\t}\n\n\tif (op->type_c == &type_void || op->associative==ASSOC_RIGHT || op->type_c == NULL)\n\t{\n\t\tQCC_FreeTemp(var_b);\t//returns a instead of some result/temp\n\t\tif (flags&STFL_DISCARDRESULT)\n\t\t\tQCC_FreeTemp(var_a);\n\t}\n\telse\n\t{\n\t\tQCC_FreeTemp(var_a);\n\t\tQCC_FreeTemp(var_b);\n\t}\n\n\tif (outstatement)\n\t\tQCC_ClobberDef(NULL);\n\n\tstatement = &statements[numstatements++];\n\n\tif (outstatement)\n\t\t*outstatement = statement;\n\n\tstatement->linenum = pr_token_line_last;\n\tstatement->op = op - pr_opcodes;\n\tstatement->flags = 0;\n\tstatement->a = var_a;\n\tstatement->b = var_b;\n\n\tif (var_c.cast && var_c.sym && !var_c.sym->referenced)\n\t{\n\t\tQCC_PR_ParseWarning(WARN_DEBUGGING, \"INTERNAL: var_c was not referenced\");\n\t\tQCC_PR_ParsePrintSRef(WARN_DEBUGGING, var_c);\n\t}\n\tif (var_b.cast && var_b.sym && !var_b.sym->referenced)\n\t{\n\t\tQCC_PR_ParseWarning(WARN_DEBUGGING, \"INTERNAL: var_b was not referenced\");\n\t\tQCC_PR_ParsePrintSRef(WARN_DEBUGGING, var_b);\n\t}\n\tif (var_a.cast && var_a.sym && !var_a.sym->referenced)\n\t{\n\t\tQCC_PR_ParseWarning(WARN_DEBUGGING, \"INTERNAL: var_a was not referenced\");\n\t\tQCC_PR_ParsePrintSRef(WARN_DEBUGGING, var_a);\n\t}\n\n\tif (var_c.cast)\n\t\tstatement->c = var_c;\n\telse if (op->type_c == &type_void || op->associative==ASSOC_RIGHT || op->type_c == NULL)\n\t{\n\t\tvar_c = nullsref;\n\t\tstatement->c = nullsref;\t\t\t// ifs, gotos, and assignments\n\t\t\t\t\t\t\t\t\t\t// don't need vars allocated\n\t\tif (flags&STFL_DISCARDRESULT)\n\t\t\treturn nullsref;\n\t\treturn var_a;\n\t}\n\telse if (op-pr_opcodes == OP_ADD_PIW)\n\t{\n\t\tvar_c = QCC_GetTemp(var_a.cast);\n\t\tstatement->c = var_c;\n\t}\n\telse\n\t{\t// allocate result space\n\t\tvar_c = QCC_GetTemp(*op->type_c);\n\t\tstatement->c = var_c;\n\t\tif (op->type_b == &type_field)\n\t\t{\n\t\t\t//&(a.b) returns a pointer to b, so that pointer's auxtype should have the same type as b's auxtype\n\t\t\tif (var_b.cast->type == ev_variant)\n\t\t\t\tvar_c.cast = type_variant;\n\t\t\telse if (var_c.cast->type == ev_pointer)\n\t\t\t\tvar_c.cast = QCC_PR_PointerType(var_b.cast->aux_type);\n\t\t\telse if (var_c.cast->type == ev_field)\n\t\t\t\tvar_c.cast = QCC_PR_FieldType(var_b.cast->aux_type);\n\t\t\telse\n\t\t\t\tvar_c.cast = var_b.cast->aux_type;\n\t\t}\n\t}\n\n\tif (flags&STFL_DISCARDRESULT)\n\t{\n\t\tQCC_FreeTemp(var_c);\n\t\tvar_c = nullsref;\n\t}\n\treturn var_c;\n}\n\n/*\n============\nQCC_PR_SimpleStatement\n\nEmits a primitive statement\n============\n*/\nQCC_statement_t *QCC_PR_SimpleStatement ( QCC_opcode_t *op, QCC_sref_t var_a, QCC_sref_t var_b, QCC_sref_t var_c, int force)\n{\n\tQCC_statement_t\t*statement;\n\n\tif (!force && !QCC_OPCodeValid(op))\n\t{\n//\t\toutputversion = op->extension;\n//\t\tif (noextensions)\n\t\tif (QCC_OPCodeValidForTarget(QCF_FTE, QCTARGVER_FTE_DEF, op))\n\t\t\tQCC_PR_ParseError(ERR_BADEXTENSION, \"Opcode \\\"%s|%s\\\" not valid for target. Consider the use of: #pragma target fte\", op->name, op->opname);\n\t\telse if (QCC_OPCodeValidForTarget(QCF_FTE, QCTARGVER_FTE_MAX, op))\n\t\t\tQCC_PR_ParseError(ERR_BADEXTENSION, \"Opcode \\\"%s|%s\\\" not valid for target. Consider the use of: #pragma target fte_%u\", op->name, op->opname, QCTARGVER_FTE_MAX);\n\t\telse\n\t\t\tQCC_PR_ParseError(ERR_BADEXTENSION, \"Opcode \\\"%s|%s\\\" is not supported.\", op->name, op->opname);\n\t}\n\n\tstatement = &statements[numstatements];\n\tnumstatements++;\n\n\tstatement->op = op - pr_opcodes;\n\tstatement->flags = 0;\n\tstatement->a = var_a;\n\tstatement->b = var_b;\n\tstatement->c = var_c;\n\tstatement->linenum = pr_token_line_last;\n\n\treturn statement;\n}\n\nQCC_statement_t *QCC_PR_SimpleInitStatement ( QCC_opcode_t *op, QCC_sref_t var_a, QCC_sref_t var_b, QCC_sref_t var_c)\n{\t//statements execed at global scope. will get inserted into an appropriate function at some later point.\n\tQCC_statement_t\t*statement;\n\n\tif (!QCC_OPCodeValid(op))\n\t{\n\t\tif (QCC_OPCodeValidForTarget(QCF_FTE, QCTARGVER_FTE_DEF, op))\n\t\t\tQCC_PR_ParseError(ERR_BADEXTENSION, \"Opcode \\\"%s|%s\\\" not valid for target. Consider the use of: #pragma target fte\", op->name, op->opname);\n\t\telse if (QCC_OPCodeValidForTarget(QCF_FTE, QCTARGVER_FTE_MAX, op))\n\t\t\tQCC_PR_ParseError(ERR_BADEXTENSION, \"Opcode \\\"%s|%s\\\" not valid for target. Consider the use of: #pragma target fte_%u\", op->name, op->opname, QCTARGVER_FTE_MAX);\n\t\telse\n\t\t\tQCC_PR_ParseError(ERR_BADEXTENSION, \"Opcode \\\"%s|%s\\\" is not supported.\", op->name, op->opname);\n\t}\n\n\tif (numinitstatements+1 > maxinitstatements)\n\t{\n\t\tmaxinitstatements+=64;\n\t\tinitstatements = realloc(initstatements, sizeof(*initstatements)*maxinitstatements);\n\t}\n\n\tstatement = &initstatements[numinitstatements];\n\tnuminitstatements++;\n\n\tstatement->op = op - pr_opcodes;\n\tstatement->flags = 0;\n\tstatement->a = var_a;\n\tstatement->b = var_b;\n\tstatement->c = var_c;\n\tstatement->linenum = 0;//pr_token_line_last; //will come from all over. the file won't be meaningful so nor will the line.\n\n\treturn statement;\n}\n\n//this opcode updates its dest, so can't use QCC_PR_StatementFlags because that's only two args. We still want to be able to emulate it though.\n//NOTE: emulation might require an extra copy after.\nQCC_sref_t QCC_PR_Statement_BitCopy(QCC_sref_t var_a, unsigned int bitofs, unsigned int bits, QCC_sref_t var_c)\n{\n\tif (QCC_OPCodeValid(&pr_opcodes[OP_BITCOPY_I]))\n\t{\t//can do it in a single opcode.\n\t\tQCC_sref_t var_b = QCC_MakeUIntConst((bitofs<<8) | bits);\n\t\tvar_b.sym->referenced = true;\n\t\tQCC_PR_SimpleStatement(&pr_opcodes[OP_BITCOPY_I], var_a, var_b, var_c, false);\n\t\tQCC_FreeTemp(var_a);\n\t\tQCC_FreeTemp(var_b);\n\t\treturn var_c;\n\t}\n\n\t//this will take effort. we might still be able to fold part of it at least.\n\tvar_c = QCC_PR_StatementFlags(&pr_opcodes[OP_BITAND_I], var_c, QCC_MakeUIntConst(~(((1u<<bits)-1u)<<bitofs)), NULL, 0);\t//clear the (cache of the) dest\n\tvar_a = QCC_PR_StatementFlags(&pr_opcodes[OP_BITAND_I], var_a, QCC_MakeUIntConst(((1u<<bits)-1u)), NULL, 0);\t//clear the (cache of the) dest\n\tvar_a = QCC_PR_StatementFlags(&pr_opcodes[OP_LSHIFT_I], var_a, QCC_MakeUIntConst(bitofs), NULL, 0);\t//shift the source...\n\treturn QCC_PR_StatementFlags(&pr_opcodes[OP_BITOR_I], var_c, var_a, NULL, 0);\t//and pack it together.\n}\nvoid QCC_PR_Statement_SmallStore(int opcode, QCC_sref_t val, QCC_sref_t ptr, QCC_sref_t idx)\n{\n\tQCC_sref_t tmp, byteidx;\n\tint idxshift;\n\tint bits;\n\n\tif (QCC_OPCodeValid(&pr_opcodes[opcode]))\n\t{\t//no need to emulate it, just spit out the instruction directly.\n\t\tQCC_PR_SimpleStatement(&pr_opcodes[opcode], val,ptr,idx, false);\n\t\treturn;\n\t}\n\n\tQCC_UnFreeTemp(ptr);\n\tQCC_UnFreeTemp(val);\n\tQCC_UnFreeTemp(idx);\n\tif (!idx.cast) idx = QCC_MakeUIntConst(0);\n\n\tif (opcode == OP_STOREP_I8)\n\t{\n\t\tbits = 8;\n\t\tidxshift=2;\n\t}\n\telse if (opcode == OP_STOREP_C)\n\t{\n\t\tbits = 8;\n\t\tidxshift=2;\n\t\tval = QCC_SupplyConversion(val, ev_uint, true);\n\t}\n\telse if (opcode == OP_STOREP_I16)\n\t{\n\t\tbits = 16;\n\t\tidxshift=1;\n\t}\n\telse\n\t\tQCC_PR_ParseError(ERR_BADEXTENSION, \"QCC_PR_Statement_SmallStore: Opcode \\\"%s\\\" is not supported.\", pr_opcodes[opcode].opname);\n\n\tbyteidx = QCC_PR_StatementFlags(&pr_opcodes[OP_BITAND_I], idx, QCC_MakeUIntConst((32/bits)-1), NULL, STFL_PRESERVEA);\t//drop the extra precision...\n\n\tidx = QCC_PR_StatementFlags(&pr_opcodes[OP_RSHIFT_I], idx, QCC_MakeUIntConst(idxshift), NULL, 0);\t//drop the extra precision...\n\ttmp = QCC_PR_StatementFlags(&pr_opcodes[OP_LOADP_I], ptr, idx, NULL, STFL_PRESERVEA|STFL_PRESERVEB);\t//read the input\n\n\t/*if (QCC_OPCodeValid(&pr_opcodes[opcode]))\n\t{\t//can do it in a single opcode.\n\t\tQCC_sref_t shiftinfo = QCC_MakeUIntConst((bitofs<<8) | bits);\n\t\tQCC_PR_SimpleStatement(&pr_opcodes[OP_BITCOPY_I], val, shiftinfo, tmp, false);\n\t\tval = tmp;\n\t}\n\telse*/\n\t{\n\t\tQCC_sref_t bitidx = QCC_PR_StatementFlags(&pr_opcodes[OP_MUL_I], byteidx, QCC_MakeUIntConst(bits), NULL, 0);\n\t\tQCC_sref_t bitmask = QCC_MakeUIntConst((1u<<bits)-1);\n\n\t\tval = QCC_PR_StatementFlags(&pr_opcodes[OP_BITAND_I], val, bitmask, NULL, STFL_PRESERVEB);\n\t\tval = QCC_PR_StatementFlags(&pr_opcodes[OP_LSHIFT_I], val, bitidx, NULL, STFL_PRESERVEB);\n\n\t\tbitmask = QCC_PR_StatementFlags(&pr_opcodes[OP_LSHIFT_I], bitmask, bitidx, NULL, 0);\n\n\t\tbitmask = QCC_PR_StatementFlags(&pr_opcodes[OP_BITNOT_I], bitmask, nullsref, NULL, 0);\n\t\ttmp = QCC_PR_StatementFlags(&pr_opcodes[OP_BITAND_I], tmp, bitmask, NULL, 0);\n\t\tval = QCC_PR_StatementFlags(&pr_opcodes[OP_BITOR_I], tmp, val, NULL, 0);\n\t}\n\n\tif (QCC_OPCodeValid(&pr_opcodes[OP_STOREP_I]))\n\t\tQCC_PR_SimpleStatement(&pr_opcodes[OP_STOREP_I], val,ptr,idx, false);\n\telse\n\t\tQCC_PR_SimpleStatement(&pr_opcodes[OP_STOREP_F], val,ptr,idx, false);\n\n\tQCC_FreeTemp(ptr);\n\tQCC_FreeTemp(val);\n\tQCC_FreeTemp(idx);\n}\n//For qcc debugging, inserts a no-op statement with a string argument\nvoid QCC_PR_StatementAnnotation(const char *fmt, ...)\n{\n\tchar buf[1024];\n\tva_list va;\n\tQCC_sref_t s, d;\n\tva_start(va, fmt);\n\tQC_vsnprintf(buf,sizeof(buf), fmt, va);\n\tva_end(va);\n\ts = QCC_MakeStringConst(buf);\n\td = QCC_PR_GetSRef(type_string, \"#note\", NULL, true, 0, false);\n\tQCC_PR_SimpleStatement(&pr_opcodes[OP_STORE_S], s,d,nullsref, true);\n}\n\n/*\n\tRemoves trailing statements, rewinding back to a known-safe position.\n*/\nvoid QCC_UngenerateStatements(int newstatementcount)\n{\n\tint i;\n\n\t//forget any indexes to statements if those statements are going to go away...\n\tfor (i = 0; i < num_gotos; )\n\t{\n\t\tif (pr_gotos[i].statementno >= newstatementcount)\n\t\t{\n\t\t\tmemmove(&pr_gotos[i], &pr_gotos[i+1], sizeof(*pr_gotos)*(num_gotos-(i+1)));\n\t\t\tnum_gotos--;\n\t\t}\n\t\telse\n\t\t\ti++;\n\t}\n\tfor (i = 0; i < num_labels; )\n\t{\t//FIXME: stripping a label? erk?\n\t\tif (pr_labels[i].statementno >= newstatementcount)\n\t\t{\n\t\t\tmemmove(&pr_labels[i], &pr_labels[i+1], sizeof(*pr_labels)*(num_labels-(i+1)));\n\t\t\tnum_labels--;\n\t\t}\n\t\telse\n\t\t\ti++;\n\t}\n\tfor (i = 0; i < num_breaks; )\n\t{\n\t\tif (pr_breaks[i] >= newstatementcount)\n\t\t{\n\t\t\tmemmove(&pr_breaks[i], &pr_breaks[i+1], sizeof(*pr_breaks)*(num_breaks-(i+1)));\n\t\t\tnum_breaks--;\n\t\t}\n\t\telse\n\t\t\ti++;\n\t}\n\tfor (i = 0; i < num_continues; )\n\t{\n\t\tif (pr_continues[i] >= newstatementcount)\n\t\t{\n\t\t\tmemmove(&pr_continues[i], &pr_continues[i+1], sizeof(*pr_continues)*(num_continues-(i+1)));\n\t\t\tnum_continues--;\n\t\t}\n\t\telse\n\t\t\ti++;\n\t}\n\tfor (i = 0; i < num_cases; )\n\t{\n\t\tif (pr_cases[i] >= newstatementcount)\n\t\t{\n\t\t\tmemmove(&pr_cases[i], &pr_cases[i+1], sizeof(*pr_cases)*(num_cases-(i+1)));\n\t\t\tmemmove(&pr_casesref[i], &pr_casesref[i+1], sizeof(*pr_casesref)*(num_cases-(i+1)));\n\t\t\tmemmove(&pr_casesref2[i], &pr_casesref2[i+1], sizeof(*pr_casesref2)*(num_cases-(i+1)));\n\t\t\tnum_cases--;\n\t\t}\n\t\telse\n\t\t\ti++;\n\t}\n\n\tnumstatements = newstatementcount;\n}\n\n/*\n============\nPR_ParseImmediate\n\nLooks for a preexisting constant\n============\n*/\nstatic QCC_sref_t\tQCC_PR_ParseImmediate (void)\n{\n\tQCC_sref_t\tcn;\n\n\tswitch(pr_immediate_type->type)\n\t{\n\tcase ev_float:\n\t\tcn = QCC_MakeFloatConst(pr_immediate._float);\n\t\tQCC_PR_Lex ();\n\t\treturn cn;\n\n\tcase ev_integer:\n\t\tcn = QCC_MakeIntConst(pr_immediate._int);\n\t\tQCC_PR_Lex ();\n\t\treturn cn;\n\tcase ev_uint:\n\t\tcn = QCC_MakeUIntConst(pr_immediate._uint);\n\t\tQCC_PR_Lex ();\n\t\treturn cn;\n\n\tcase ev_double:\n\t\tcn = QCC_MakeDoubleConst(pr_immediate._double);\n\t\tQCC_PR_Lex ();\n\t\treturn cn;\n\tcase ev_int64:\n\t\tcn = QCC_MakeInt64Const(pr_immediate.i64);\n\t\tQCC_PR_Lex ();\n\t\treturn cn;\n\tcase ev_uint64:\n\t\tcn = QCC_MakeUInt64Const(pr_immediate.u64);\n\t\tQCC_PR_Lex ();\n\t\treturn cn;\n\n\tcase ev_vector:\n\t\tcn = QCC_MakeVectorConst(pr_immediate.vector[0], pr_immediate.vector[1], pr_immediate.vector[2]);\n\t\tQCC_PR_Lex ();\n\t\treturn cn;\n\n\tcase ev_string:\n\t\t{\n\t\t\tint t=0,l;\n\t\t\tchar tmp[8192];\n\t\t\tdo\n\t\t\t{\n\t\t\t\tl = pr_immediate_strlen;\n\t\t\t\tif (t+l+1 > sizeof(tmp))\n\t\t\t\t\tQCC_PR_ParseError (ERR_NAMETOOLONG, \"string immediate is too long\");\n\t\t\t\tmemcpy(tmp+t, pr_immediate_string, l);\n\t\t\t\tt+=l;\n\t\t\t\t\n\t\t\t\tQCC_PR_Lex ();\n\t\t\t} while(pr_token_type == tt_immediate && pr_immediate_type == type_string);\n\t\t\ttmp[t++] = 0;\n\t\t\tcn = QCC_MakeStringConstLength(tmp, t);\n\t\t}\n\t\treturn cn;\n\tdefault:\n\t\tQCC_PR_ParseError (ERR_BADIMMEDIATETYPE, \"weird immediate type\");\n\t\treturn nullsref;\n\t}\n}\n\nstatic QCC_ref_t *QCC_PR_GenerateAddressOf(QCC_ref_t *retbuf, QCC_ref_t *operand)\n{\n//\tQCC_def_t *e2;\n\tif (operand->type == REF_FIELD)\n\t{\n\t\tif (operand->bitofs)\n\t\t\tQCC_PR_ParseWarning (ERR_BADEXTENSION, \"Address-of operator on bitfield\");\n\t\t//&e.f should generate a pointer def\n\t\t//as opposed to a ref\n\t\treturn QCC_PR_BuildRef(retbuf,\n\t\t\t\tREF_GLOBAL,\n\t\t\t\tQCC_PR_Statement(&pr_opcodes[OP_ADDRESS], operand->base, operand->index, NULL),\n\t\t\t\tnullsref,\n\t\t\t\tQCC_PR_PointerType((operand->index.cast->type == ev_field)?operand->index.cast->aux_type:type_variant),\n\t\t\t\ttrue, 0);\n\t}\n\tif (operand->type == REF_ARRAYHEAD || operand->type == REF_GLOBAL || operand->type == REF_ARRAY)\n\t{\n\t\tQCC_sref_t ptr;\n\t\tconst QCC_eval_t *eval;\n\t\tQCC_type_t *basetype = operand->cast;\n\t\tif (operand->type == REF_ARRAYHEAD)\n\t\t\tbasetype = basetype->aux_type;\n\n\t\tif (!QCC_OPCodeValid(&pr_opcodes[OP_GLOBALADDRESS]))\n\t\t{\n\t\t\tif (operand->type == REF_ARRAYHEAD)\n\t\t\t\tQCC_PR_ParseError (ERR_BADEXTENSION, \"Address-of operator is not supported in this form without extensions. Consider the use of either '#pragma target fte' or '#pragma flag enable brokenarray'\");\n\t\t\telse\n\t\t\t\tQCC_PR_ParseError (ERR_BADEXTENSION, \"Address-of operator is not supported in this form without extensions. Consider the use of: #pragma target fte\");\n\t\t}\n\t\tif (operand->base.sym->scope && !operand->base.sym->addressedwarned && !operand->base.sym->isstatic)\n\t\t{\n\t\t\tchar type[128];\n\t\t\tQCC_PR_ParseWarning (WARN_UNSAFELOCALPOINTER, \"Address-of operator on local %s %s is unsafe if recursing (and still bloated when not). Consider use of 'static' or 'auto'.\", TypeName(operand->base.cast, type,sizeof(type)), operand->base.sym->name);\n\t\t\toperand->base.sym->addressedwarned = true;\n\t\t\toperand->base.sym->scope->privatelocals = true;\t//just in case.\n\t\t}\n\t\tif (operand->base.sym->temp)\n\t\t\tQCC_PR_ParseWarning (WARN_NOTSTANDARDBEHAVIOUR, \"Address-of operator on temp from line %i\", operand->base.sym->temp->lastline);\n\n\t\teval = QCC_SRef_EvalConst(operand->index);\n\t\tif (eval && flag_pointerrelocs)\n\t\t{\n\t\t\tint ofs = QCC_Eval_Int(eval, operand->index.cast);\n\t\t\tptr = QCC_MakeGAddress(type_pointer, operand->base.sym, operand->base.ofs, basetype->align * ofs + operand->bitofs);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (basetype->align && basetype->align!=32)\n\t\t\t{\t//urgh. OP_GLOBALADDRESS is in terms of words, while pointers are often not (eg __int16, __int8, or even certain structs which lack 32bit members.)\n\t\t\t\tptr = QCC_PR_Statement(&pr_opcodes[OP_GLOBALADDRESS], operand->base, nullsref, NULL);\n\t\t\t\tif (operand->index.cast)\n\t\t\t\t{\n\t\t\t\t\tQCC_sref_t idx = QCC_SupplyConversion(operand->index, ev_integer, true);\n\t\t\t\t\tif (basetype->align!=8)\n\t\t\t\t\t\tidx = QCC_PR_Statement(&pr_opcodes[OP_MUL_I], idx, QCC_MakeIntConst(basetype->align/8), NULL);\n\t\t\t\t\tptr = QCC_PR_Statement(&pr_opcodes[OP_ADD_I], ptr, idx, NULL);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t\tptr = QCC_PR_Statement(&pr_opcodes[OP_GLOBALADDRESS], operand->base, operand->index.cast?QCC_SupplyConversion(operand->index, ev_integer, true):nullsref, NULL);\n\t\t\tif (operand->bitofs)\n\t\t\t\tptr = QCC_PR_Statement(&pr_opcodes[OP_ADD_I], ptr, QCC_MakeIntConst(operand->bitofs/8), NULL);\n\t\t}\n\n\t\t//&foo (or &((&foo)[5]), which is basically an array). the result is a temp and thus cannot be assigned to (but should be possible to dereference further).\n\t\treturn QCC_PR_BuildRef(retbuf,\n\t\t\t\tREF_GLOBAL,\n\t\t\t\tptr,\n\t\t\t\tnullsref,\n\t\t\t\tQCC_PR_PointerType(basetype),\n\t\t\t\ttrue, 0);\n\t}\n\tif (operand->type == REF_POINTER || operand->type == REF_POINTERARRAY /*technically already meant to be an address, but hey, silently allow taking the address of it anyway*/)\n\t{\n\t\t//&(p[5]) just reverts back to p+5. it cannot be assigned to.\n\t\tQCC_sref_t addr, idx;\n\t\tconst QCC_eval_t *eval;\n\t\tQCC_type_t *resulttype = operand->cast;\t//'int' *\n\t\tQCC_type_t *basetype = operand->cast;\t//'int'\n\t\tif (operand->type != REF_POINTERARRAY)\n\t\t\tresulttype = QCC_PR_PointerType(resulttype);\n\t\telse\n\t\t\tbasetype = basetype->aux_type;\t//already a pointer.\n\n\t\tif (operand->index.cast)\n\t\t{\n\t\t\tif (basetype->align != 32)\n\t\t\t{\t//index is bytes or shorts or something small.\n\t\t\t\tif (!flag_undefwordsize && (eval=QCC_SRef_EvalConst(operand->index)))\n\t\t\t\t{\n\t\t\t\t\tint byteofs = QCC_Eval_Int(eval, operand->index.cast)*(basetype->align/8) + operand->bitofs/8;\n\t\t\t\t\taddr = QCC_PR_Statement(&pr_opcodes[OP_ADD_I], operand->base, QCC_MakeIntConst(byteofs), NULL);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tidx = QCC_PR_Statement(&pr_opcodes[OP_MUL_I], QCC_MakeIntConst(basetype->align/8), QCC_SupplyConversion(operand->index, ev_integer, true), NULL);\n\t\t\t\t\tif (operand->bitofs)\n\t\t\t\t\t\taddr = QCC_PR_Statement(&pr_opcodes[OP_ADD_I], idx, QCC_MakeUInt64Const(operand->bitofs/8), NULL);\n\t\t\t\t\taddr = QCC_PR_Statement(&pr_opcodes[OP_ADD_I], operand->base, idx, NULL);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\t//index is words\n//\t\t\t\tif (!QCC_OPCodeValid(&pr_opcodes[OP_ADD_PIW]))\n//\t\t\t\t\tQCC_PR_ParseError (ERR_BADEXTENSION, \"Address-of operator is not supported in this form without extensions. Consider the use of: #pragma target fte\");\n\t\t\t\taddr = QCC_PR_Statement(&pr_opcodes[OP_ADD_PIW], operand->base, QCC_SupplyConversion(operand->index, ev_integer, true), NULL);\n\t\t\t\tif (operand->bitofs)\n\t\t\t\t\taddr = QCC_PR_Statement(&pr_opcodes[OP_ADD_I], addr, QCC_MakeUInt64Const(operand->bitofs/8), NULL);\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\taddr = operand->base;\n\t\t\tif (operand->bitofs)\n\t\t\t\taddr = QCC_PR_Statement(&pr_opcodes[OP_ADD_I], addr, QCC_MakeUInt64Const(operand->bitofs/8), NULL);\n\t\t}\n\t\treturn QCC_PR_BuildRef(retbuf,\n\t\t\t\tREF_GLOBAL,\n\t\t\t\taddr, \n\t\t\t\tnullsref,\n\t\t\t\tresulttype,\n\t\t\t\ttrue, 0);\n\t}\n\tQCC_PR_ParseError (ERR_BADEXTENSION, \"Cannot use addressof operator ('&') on a global. Please use the FTE target.\");\n\treturn operand;\n}\nQCC_sref_t QCC_DP_GlobalAddress(QCC_sref_t base, QCC_sref_t index, unsigned int flags)\n{\n\tQCC_sref_t ptr = QCC_MakeGAddress(type_integer, base.sym, 0, 0);\n\tbase.sym->used = true;\n\tif (!(flags&STFL_PRESERVEA))\n\t\tQCC_FreeTemp(base);\n\tif (base.ofs)\n\t\tptr = QCC_PR_StatementFlags(&pr_opcodes[OP_ADD_I], ptr, QCC_MakeIntConst(base.ofs), NULL, 0);\n\tif (index.cast)\n\t\tptr = QCC_PR_StatementFlags(&pr_opcodes[OP_ADD_I], ptr, index, NULL, flags);\n\treturn ptr;\n}\n\nstatic void QCC_PrecacheSound (const char *n, int ch)\n{\n\tint\t\ti;\n\n\tif (!*n)\n\t\treturn;\n\tif (ch >= '1'  && ch <= '9')\n\t\tch -= '0';\n\telse\n\t\tch = 1;\n\n\tfor (i=0 ; i<numsounds ; i++)\n\t\tif (!STRCMP(n, precache_sound[i].name))\n\t\t{\n\t\t\tif (!precache_sound[i].block || precache_sound[i].block > ch)\n\t\t\t\tprecache_sound[i].block = ch;\n\t\t\treturn;\n\t\t}\n\tif (strchr(n, '\\\\'))\n\t\tQCC_PR_ParseWarning(WARN_NONPORTABLEFILENAME, \"backslashes in path names are non-portable - %s\", n);\n\tif (numsounds == QCC_MAX_SOUNDS)\n\t\treturn;\n//\t\tQCC_Error (\"PrecacheSound: numsounds == MAX_SOUNDS\");\n\tstrcpy (precache_sound[i].name, n);\n\tprecache_sound[i].block = ch;\n\tprecache_sound[i].filename = s_filen;\n\tprecache_sound[i].fileline = pr_source_line;\n\tnumsounds++;\n}\n\nstatic void QCC_PrecacheModel (const char *n, int ch)\n{\n\tint\t\ti;\n\n\tif (!*n)\n\t\treturn;\n\tfor (i=0 ; i<nummodels ; i++)\n\t\tif (!STRCMP(n, precache_model[i].name))\n\t\t{\n\t\t\tif (!precache_model[i].block)\n\t\t\t{\n\t\t\t\tif (ch >= '1'  && ch <= '9')\n\t\t\t\t\tprecache_model[i].block = ch - '0';\n\t\t\t\telse\n\t\t\t\t\tprecache_model[i].block = 1;\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\tif (strchr(n, '\\\\'))\n\t\tQCC_PR_ParseWarning(WARN_NONPORTABLEFILENAME, \"backslashes in path names are non-portable - %s\", n);\n\tif (nummodels == QCC_MAX_MODELS)\n\t\treturn;\n//\t\tQCC_Error (\"PrecacheModels: nummodels == MAX_MODELS\");\n\tstrcpy (precache_model[i].name, n);\n\tif (ch >= '1'  && ch <= '9')\n\t\tprecache_model[i].block = ch - '0';\n\telse\n\t\tprecache_model[i].block = 1;\n\tprecache_model[i].filename = s_filen;\n\tprecache_model[i].fileline = pr_source_line;\n\tnummodels++;\n}\n\nstatic void QCC_SetModel (const char *n)\n{\n\tint\t\ti;\n\n\tif (!*n)\n\t\treturn;\n\tfor (i=0 ; i<nummodels ; i++)\n\t\tif (!STRCMP(n, precache_model[i].name))\n\t\t{\n\t\t\tprecache_model[i].used++;\n\t\t\treturn;\n\t\t}\n\tif (strchr(n, '\\\\'))\n\t\tQCC_PR_ParseWarning(WARN_NONPORTABLEFILENAME, \"backslashes in path names are non-portable - %s\", n);\n\tif (nummodels == QCC_MAX_MODELS)\n\t\treturn;\n\tstrcpy (precache_model[i].name, n);\n\tprecache_model[i].block = 0;\n\tprecache_model[i].used=1;\n\n\tprecache_model[i].filename = s_filen;\n\tprecache_model[i].fileline = pr_source_line;\n\tnummodels++;\n}\nstatic void QCC_SoundUsed (const char *n)\n{\n\tint\t\ti;\n\n\tif (!*n)\n\t\treturn;\n\tfor (i=0 ; i<numsounds ; i++)\n\t\tif (!STRCMP(n, precache_sound[i].name))\n\t\t{\n\t\t\tprecache_sound[i].used++;\n\t\t\treturn;\n\t\t}\n\tif (strchr(n, '\\\\'))\n\t\tQCC_PR_ParseWarning(WARN_NONPORTABLEFILENAME, \"backslashes in path names are non-portable - %s\", n);\n\tif (numsounds == QCC_MAX_SOUNDS)\n\t\treturn;\n\tstrcpy (precache_sound[i].name, n);\n\tprecache_sound[i].block = 0;\n\tprecache_sound[i].used=1;\n\n\tprecache_sound[i].filename = s_filen;\n\tprecache_sound[i].fileline = pr_source_line;\n\tnumsounds++;\n}\n\nstatic void QCC_PrecacheTexture (const char *n, int ch)\n{\n\tint\t\ti;\n\n\tif (!*n)\n\t\treturn;\n\tfor (i=0 ; i<numtextures ; i++)\n\t\tif (!STRCMP(n, precache_texture[i].name))\n\t\t\treturn;\n\tif (strchr(n, '\\\\'))\n\t\tQCC_PR_ParseWarning(WARN_NONPORTABLEFILENAME, \"backslashes in path names are non-portable - %s\", n);\n\tif (nummodels == QCC_MAX_MODELS)\n\t\treturn;\n//\t\tQCC_Error (\"PrecacheTextures: numtextures == MAX_TEXTURES\");\n\tstrcpy (precache_texture[i].name, n);\n\tif (ch >= '1'  && ch <= '9')\n\t\tprecache_texture[i].block = ch - '0';\n\telse\n\t\tprecache_texture[i].block = 1;\n\tnumtextures++;\n}\n\nstatic void QCC_PrecacheFile (const char *n, int ch)\n{\n\tint\t\ti;\n\n\tif (!*n)\n\t\treturn;\n\tfor (i=0 ; i<numfiles ; i++)\n\t\tif (!STRCMP(n, precache_file[i].name))\n\t\t\treturn;\n\tif (strchr(n, '\\\\'))\n\t\tQCC_PR_ParseWarning(WARN_NONPORTABLEFILENAME, \"backslashes in path names are non-portable - %s\", n);\n\tif (numfiles == QCC_MAX_FILES)\n\t\treturn;\n//\t\tQCC_Error (\"PrecacheFile: numfiles == MAX_FILES\");\n\tstrcpy (precache_file[i].name, n);\n\tif (ch >= '1'  && ch <= '9')\n\t\tprecache_file[i].block = ch - '0';\n\telse\n\t\tprecache_file[i].block = 1;\n\tnumfiles++;\n}\n\n\nstatic void QCC_VerifyFormatString (const char *funcname, QCC_ref_t **arglist, unsigned int argcount)\n{\n\tconst char *s = \"%s\", *reqtype;\n\tint firstarg = 1;\n\tconst char *s0;\n\tchar *err;\n\tint width, thisarg, arg;\n\tchar formatbuf[16];\n\tchar temp[256];\n\tint argpos = firstarg, argn_last = firstarg;\n\tint isfloat, is64bit;\n\tconst QCC_eval_t *formatstring = QCC_SRef_EvalConst(arglist[0]->base);\n\tif (!formatstring)\t//can't check variables.\n\t\treturn;\n\tif (!qccwarningaction[WARN_FORMATSTRING])\n\t\treturn;\t//don't bother if its not relevant anyway.\n\ts = strings + formatstring->string;\n\n#define ARGTYPE(a) (((a)>=firstarg && (a)<argcount) ? ((arglist[a]->cast->type==ev_boolean)?arglist[a]->cast->parentclass->type:arglist[a]->cast->type) : ev_void)\n#define ARGCTYPE(a) (((a)>=firstarg && (a)<argcount) ? (arglist[a]->cast) : type_void)\n\n\tfor(;;)\n\t{\n\t\ts0 = s;\n\t\tswitch(*s)\n\t\t{\n\t\tcase 0:\n\t\t\tif (argpos < argcount && argn_last < argcount)\n\t\t\t\tQCC_PR_ParseWarning(WARN_FORMATSTRING, \"%s: surplus trailing %s%s%s argument(s) for format %s\\\"%s\\\"%s\", funcname, col_type, TypeName(ARGCTYPE(argpos), temp, sizeof(temp)), col_none, col_name, strings + formatstring->string, col_none);\n\t\t\treturn;\n\t\tcase '%':\n\t\t\tif(*++s == '%')\n\t\t\t{\n\t\t\t\ts++;\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\t// complete directive format:\n\t\t\t// %3$*1$.*2$ld\n\t\t\t\n\t\t\twidth = -1;\n\t\t\tthisarg = -1;\n\t\t\tisfloat = -1;\n\t\t\tis64bit = 0;\n\n\t\t\t// is number following?\n\t\t\tif(*s >= '0' && *s <= '9')\n\t\t\t{\n\t\t\t\twidth = strtol(s, &err, 10);\n\t\t\t\tif(!err)\n\t\t\t\t{\n\t\t\t\t\tQCC_PR_ParseWarning(WARN_FORMATSTRING, \"%s: bad format string: %s%s%s\", funcname, col_name, s0, col_none);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif(*err == '$')\n\t\t\t\t{\n\t\t\t\t\tthisarg = width + (firstarg-1);\n\t\t\t\t\twidth = -1;\n\t\t\t\t\ts = err + 1;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tif(*s == '0')\n\t\t\t\t\t{\n\t\t\t\t\t\tif(width == 0)\n\t\t\t\t\t\t\twidth = -1; // it was just a flag\n\t\t\t\t\t}\n\t\t\t\t\ts = err;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif(width < 0)\n\t\t\t{\n\t\t\t\tfor(;;)\n\t\t\t\t{\n\t\t\t\t\tswitch(*s)\n\t\t\t\t\t{\n\t\t\t\t\t\tcase '#': //alternate\n\t\t\t\t\t\tcase '0': //zero-pad\n\t\t\t\t\t\tcase '-': //left-align\n\t\t\t\t\t\tcase ' ': //space-positive\n\t\t\t\t\t\tcase '+': //sign-positive\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\tgoto noflags;\n\t\t\t\t\t}\n\t\t\t\t\t++s;\n\t\t\t\t}\nnoflags:\n\t\t\t\tif(*s == '*')\n\t\t\t\t{\n\t\t\t\t\t++s;\n\t\t\t\t\tif(*s >= '0' && *s <= '9')\n\t\t\t\t\t{\n\t\t\t\t\t\targ = strtol(s, &err, 10);\n\t\t\t\t\t\tif(!err || *err != '$')\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tQCC_PR_ParseWarning(WARN_FORMATSTRING, \"%s: invalid format string: %s%s%s\", funcname, col_name, s0, col_none);\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\ts = err + 1;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\targ = argpos++;\n\t\t\t\t\tif (ARGTYPE(arg) != ev_float)\n\t\t\t\t\t\tQCC_PR_ParseWarning(WARN_FORMATSTRING, \"%s: width modifier requires float at arg %i\", funcname, arg+1);\n\t\t\t\t}\n\t\t\t\telse if(*s >= '0' && *s <= '9')\n\t\t\t\t{\n\t\t\t\t\tstrtol(s, &err, 10);\n\t\t\t\t\tif(!err)\n\t\t\t\t\t{\n\t\t\t\t\t\tQCC_PR_ParseWarning(WARN_FORMATSTRING, \"%s: invalid format string: %s%s%s\", funcname, col_name, s0, col_none);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\ts = err;\n\t\t\t\t}\n\t\t\t\t// otherwise width stays -1\n\t\t\t}\n\n\t\t\t//precision modifiers\n\t\t\tif(*s == '.')\n\t\t\t{\n\t\t\t\t++s;\n\t\t\t\tif(*s == '*')\n\t\t\t\t{\n\t\t\t\t\t++s;\n\t\t\t\t\tif(*s >= '0' && *s <= '9')\n\t\t\t\t\t{\n\t\t\t\t\t\targ = strtol(s, &err, 10);\n\t\t\t\t\t\tif(!err || *err != '$')\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tQCC_PR_ParseWarning(WARN_FORMATSTRING, \"%s: invalid format string: %s%s%s\", funcname, col_name, s0, col_none);\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\ts = err + 1;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\targ = argpos++;\n\n\t\t\t\t\tif (ARGTYPE(arg) != ev_float)\n\t\t\t\t\t\tQCC_PR_ParseWarning(WARN_FORMATSTRING, \"%s: precision modifier requires float at arg %i\", funcname, arg+1);\n\t\t\t\t}\n\t\t\t\telse if(*s >= '0' && *s <= '9')\n\t\t\t\t{\n\t\t\t\t\tstrtol(s, &err, 10);\n\t\t\t\t\tif(!err)\n\t\t\t\t\t{\n\t\t\t\t\t\tQCC_PR_ParseWarning(WARN_FORMATSTRING, \"%s: invalid format string: %s%s%s\", funcname, col_name, s0, col_none);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\ts = err;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tQCC_PR_ParseWarning(WARN_FORMATSTRING, \"%s: invalid format string: %s%s%s\", funcname, col_name, s0, col_none);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t//length modifiers\n\t\t\tfor(;;)\n\t\t\t{\n\t\t\t\tswitch(*s)\n\t\t\t\t{\n\t\t\t\t\t//case 'hh':\t//char\n\t\t\t\t\tcase 'h': isfloat = 1; break;\t//short\n\t\t\t\t\t//case 'll':\t//long long\n\t\t\t\t\tcase 'l': isfloat = 0; break;\t//long\n\t\t\t\t\tcase 'L': isfloat = 0; break;\t//long double\n\t\t\t\t\tcase 'q': is64bit = 1; break; //BSD's int64\n\t\t\t\t\tcase 'j':\t//[u]intmax_t\n\t\t\t\t\tcase 'z':\t//size_t\n\t\t\t\t\tcase 't':\t//ptrdiff_t\n\t\t\t\t\t\tQCC_PR_ParseWarning(WARN_FORMATSTRING, \"%s: length modifier %s%c%s is a placeholder\", funcname, col_type, *s, col_none);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tgoto nolength;\n\t\t\t\t}\n\t\t\t\t++s;\n\t\t\t}\nnolength:\n\n\t\t\t// now s points to the final directive char and is no longer changed\n\t\t\tif (*s == 'p' || *s == 'P')\n\t\t\t{\n\t\t\t\t//%p is slightly different from %x.\n\t\t\t\t//always 8-bytes wide with 0 padding, always ints.\n\t\t\t\tif (isfloat < 0) isfloat = 0;\n\t\t\t}\n\t\t\telse if (*s == 'i' || *s == 'I')\n\t\t\t{\n\t\t\t\t//%i defaults to ints, not floats.\n\t\t\t\tif(isfloat < 0) isfloat = 0;\n\t\t\t}\n\n\t\t\t//assume floats, not ints.\n\t\t\tif(isfloat < 0)\n\t\t\t\tisfloat = 1;\n\n\t\t\tif(thisarg < 0)\n\t\t\t\tthisarg = argpos++;\n\t\t\tif (argn_last < thisarg+1)\n\t\t\t\targn_last = thisarg+1;\n\n\t\t\tmemcpy(formatbuf, s0, s+1-s0);\n\t\t\tformatbuf[s+1-s0] = 0;\n\n\t\t\treqtype = NULL;\n\t\t\tswitch(*s)\n\t\t\t{\n\t\t\t//fixme: should we validate char ranges?\n\t\t\tcase 'd': case 'i': case 'I': case 'c':\n\t\t\tcase 'o': case 'u': case 'x': case 'X': case 'p': case 'P':\n\t\t\tcase 'e': case 'E': case 'f': case 'F': case 'g': case 'G':\n\t\t\t\tif (isfloat)\n\t\t\t\t{\n\t\t\t\t\tif (is64bit)\n\t\t\t\t\t{\n\t\t\t\t\t\tswitch(ARGTYPE(thisarg))\n\t\t\t\t\t\t{\n\t\t\t\t\t\tcase ev_double:\n\t\t\t\t\t\tcase ev_variant:\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\treqtype = \"double\";\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tswitch(ARGTYPE(thisarg))\n\t\t\t\t\t\t{\n\t\t\t\t\t\tcase ev_float:\n\t\t\t\t\t\tcase ev_variant:\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\treqtype = \"float\";\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tif (*s == 'p' || *s == 'P')\n\t\t\t\t\t{\n\t\t\t\t\t\tif (is64bit)\n\t\t\t\t\t\t\treqtype = \"some kind of double-size pointer! oh noes!\";\n\t\t\t\t\t\telse switch(ARGTYPE(thisarg))\n\t\t\t\t\t\t{\n\t\t\t\t\t\tcase ev_pointer:\n\t\t\t\t\t\tcase ev_variant:\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\treqtype = \"pointer\";\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tif (is64bit)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tswitch(ARGTYPE(thisarg))\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase ev_int64:\n\t\t\t\t\t\t\tcase ev_uint64:\n\t\t\t\t\t\t\tcase ev_variant:\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\t\treqtype = \"__int64\";\n\t\t\t\t\t\t\t\tQCC_PR_ParseWarning(WARN_FORMATSTRING, \"%s: %s%s%s requires __int64 at arg %i (got %s%s%s)\", funcname, col_name, formatbuf, col_none, thisarg+1, col_type, TypeName(ARGCTYPE(thisarg), temp, sizeof(temp)), col_none);\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tswitch(ARGTYPE(thisarg))\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase ev_integer:\n\t\t\t\t\t\t\tcase ev_uint:\n\t\t\t\t\t\t\tcase ev_variant:\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tcase ev_entity:\t//accept ents ONLY for %i\n\t\t\t\t\t\t\t\tif (*s == 'i')\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t//fallthrough\n\t\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\t\treqtype = \"int\";\n\t\t\t\t\t\t\t\tQCC_PR_ParseWarning(WARN_FORMATSTRING, \"%s: %s%s%s requires int at arg %i (got %s%s%s)\", funcname, col_name, formatbuf, col_none, thisarg+1, col_type, TypeName(ARGCTYPE(thisarg), temp, sizeof(temp)), col_none);\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase 'v': case 'V':\n\t\t\t\tif (isfloat)\n\t\t\t\t{\n\t\t\t\t\tswitch(ARGTYPE(thisarg))\n\t\t\t\t\t{\n\t\t\t\t\tcase ev_vector:\n\t\t\t\t\tcase ev_variant:\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\treqtype = \"vector\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\treqtype = \"intvector\";\n\t\t\t\tbreak;\n\t\t\tcase 's':\n\t\t\tcase 'S':\n\t\t\t\tswitch(ARGTYPE(thisarg))\n\t\t\t\t{\n\t\t\t\tcase ev_string:\n\t\t\t\tcase ev_variant:\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\treqtype = \"string\";\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tQCC_PR_ParseWarning(WARN_FORMATSTRING, \"%s: invalid format string: %s%s%s\", funcname, col_name, s0, col_none);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (reqtype)\n\t\t\t{\n\t\t\t\tQCC_PR_ParseWarning(WARN_FORMATSTRING, \"%s: %s%s%s requires %s%s%s at arg %i (got %s%s%s)\", funcname, col_name,formatbuf,col_none, col_type,reqtype,col_none, thisarg+1, col_type,TypeName(ARGCTYPE(thisarg), temp, sizeof(temp)),col_none);\n\t\t\t\tswitch(ARGCTYPE(thisarg)->type)\n\t\t\t\t{\n\t\t\t\tcase ev_string:\n\t\t\t\t\tQCC_PR_Note(WARN_FORMATSTRING, s_filen, pr_source_line, \"%s\", \"use %s or %S for strings\");\n\t\t\t\t\tbreak;\n\t\t\t\tcase ev_float:\n\t\t\t\t\tQCC_PR_Note(WARN_FORMATSTRING, s_filen, pr_source_line, \"%s\", \"use %g or %f or %hx for floats\");\n\t\t\t\t\tbreak;\n\t\t\t\tcase ev_vector:\n\t\t\t\t\tQCC_PR_Note(WARN_FORMATSTRING, s_filen, pr_source_line, \"%s\", \"use %v for vectors\");\n\t\t\t\t\tbreak;\n\t\t\t\tcase ev_entity:\n\t\t\t\t\tQCC_PR_Note(WARN_FORMATSTRING, s_filen, pr_source_line, \"%s\", \"use %i for entities\");\n\t\t\t\t\tbreak;\n\t\t\t\tcase ev_pointer:\n\t\t\t\t\tQCC_PR_Note(WARN_FORMATSTRING, s_filen, pr_source_line, \"%s\", \"use %p for pointer types\");\n\t\t\t\t\tbreak;\n\t\t\t\tcase ev_bitfld:\n\t\t\t\tcase ev_integer:\n\t\t\t\t\tQCC_PR_Note(WARN_FORMATSTRING, s_filen, pr_source_line, \"%s\", \"use %i or %lx for 32bit ints\");\n\t\t\t\t\tbreak;\n\t\t\t\tcase ev_uint:\n\t\t\t\t\tQCC_PR_Note(WARN_FORMATSTRING, s_filen, pr_source_line, \"%s\", \"use %lu or or %lx for 32bit ints\");\n\t\t\t\t\tbreak;\n\t\t\t\tcase ev_int64:\n\t\t\t\t\tQCC_PR_Note(WARN_FORMATSTRING, s_filen, pr_source_line, \"%s\", \"use %qi or %lqx for 64bit ints\");\n\t\t\t\t\tbreak;\n\t\t\t\tcase ev_uint64:\n\t\t\t\t\tQCC_PR_Note(WARN_FORMATSTRING, s_filen, pr_source_line, \"%s\", \"use %lqu or or %lqx for 64bit ints\");\n\t\t\t\t\tbreak;\n\t\t\t\tcase ev_double:\n\t\t\t\t\tQCC_PR_Note(WARN_FORMATSTRING, s_filen, pr_source_line, \"%s\", \"use %qg or %qf or %hqx for doubles\");\n\t\t\t\t\tbreak;\n\t\t\t\tcase ev_void:\t\t//coder's problem\n\t\t\t\tcase ev_field:\t\t//cast to int\n\t\t\t\tcase ev_function:\t//cast to int\n\t\t\t\tcase ev_variant:\t//should be accepted by anything...\n\t\t\t\tcase ev_struct:\t\t//coder's problem\n\t\t\t\tcase ev_union:\t\t//coder's problem\n\t\t\t\tcase ev_accessor:\t//should be unreachable\n\t\t\t\tcase ev_enum:\t\t//should be unreachable\n\t\t\t\tcase ev_typedef:\t//should be unreachable\n\t\t\t\tcase ev_boolean:\t//should be unreachable\n\t\t\t\t\tQCC_PR_Note(WARN_FORMATSTRING, s_filen, pr_source_line, \"%s\", \"cast to something else\");\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\ts++;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\ts++;\n\t\t\tbreak;\n\t\t}\n\t}\n}\n\nstatic void QCC_VerifyArgs_sendevent (const char *funcname, QCC_ref_t **arglist, unsigned int argcount)\n{\n\t//arg0 is the event name, and not meaningful\n\t//arg1 denotes the arg types passed and defines the types used for all other args.\n\tconst QCC_eval_t *eval = (argcount >= 2 && arglist[1]->type == REF_GLOBAL)?QCC_SRef_EvalConst(arglist[1]->base):NULL;\n\tconst char *argtypes = eval?strings + eval->string:NULL;\n\tsize_t arg = 2;\n\tQCC_type_t *t;\n\tchar temp[256];\n\tif (!argtypes)\n\t\treturn;\n\n\tfor (; *argtypes; argtypes++, arg++)\n\t{\n\t\tswitch(*argtypes)\n\t\t{\n\t\tcase 's':\tt = type_string;\tbreak;\n\t\tcase 'f':\tt = type_float;\t\tbreak;\n\t\tcase 'F':\tt = type_double;\tbreak;\n\t\tcase 'i':\tt = type_integer;\tbreak;\n\t\tcase 'I':\tt = type_int64;\t\tbreak;\n\t\tcase 'u':\tt = type_uint;\t\tbreak;\n\t\tcase 'U':\tt = type_uint64;\tbreak;\n\t\tcase 'v':\tt = type_vector;\tbreak;\n\t\tcase 'e':\tt = type_entity;\tbreak;\n\t\tdefault:\tt = NULL;\t\t\tbreak;\n\t\t}\n\t\tif (arg >= argcount)\n\t\t{\n\t\t\tQCC_PR_ParseWarning(WARN_ARGUMENTCHECK, \"%s: arg type list longer than args\", funcname);\n\t\t\tbreak;\n\t\t}\n\t\telse if (!t)\n\t\t\tQCC_PR_ParseWarning(WARN_ARGUMENTCHECK, \"%s: '%s%c%s' is not a recognised arg type\", funcname, col_name, *argtypes, col_none);\n\t\telse if (typecmp_lax(arglist[arg]->cast, t))\n\t\t\tQCC_PR_ParseWarning(WARN_ARGUMENTCHECK, \"%s: '%s%c%s' specified, but was passed %s%s%s\", funcname, col_name, *argtypes, col_none, col_type, TypeName(arglist[arg]->cast, temp, sizeof(temp)), col_none);\n\t}\n\n\tif (arg > 8)\n\t\tQCC_PR_ParseWarning(WARN_ARGUMENTCHECK, \"%s: cannot pass more than 8 args to a builtin.\", funcname);\n}\n\nstatic void QCC_VerifyArgs_setviewprop (const char *funcname, QCC_ref_t **arglist, unsigned int argcount)\n{\n\tstatic struct\n\t{\n\t\tconst char *name;\n\t\tint n;\n\t\tetype_t t1;\n\t\tetype_t t2;\n\t\tetype_t t3;\n\t} argtypes[] =\n\t{\n\t\t{\"VF_MIN\",\t\t\t\t1, ev_vector},\n\t\t{\"VF_MIN_X\",\t\t\t2, ev_float},\n\t\t{\"VF_MIN_Y\",\t\t\t3, ev_float},\n\t\t{\"VF_SIZE\",\t\t\t\t4, ev_vector},\n\t\t{\"VF_SIZE_X\",\t\t\t5, ev_float},\n\t\t{\"VF_SIZE_Y\",\t\t\t6, ev_float},\n\t\t{\"VF_VIEWPORT\",\t\t\t7, ev_vector, ev_vector},\n\t\t{\"VF_FOV\",\t\t\t\t8, ev_vector},\n\t\t{\"VF_FOVX\",\t\t\t\t9, ev_float},\n\t\t{\"VF_FOVY\",\t\t\t\t10, ev_float},\n\t\t{\"VF_ORIGIN\",\t\t\t11, ev_vector},\n\t\t{\"VF_ORIGIN_X\",\t\t\t12, ev_float},\n\t\t{\"VF_ORIGIN_Y\",\t\t\t13, ev_float},\n\t\t{\"VF_ORIGIN_Z\",\t\t\t14, ev_float},\n\t\t{\"VF_ANGLES\",\t\t\t15, ev_vector},\n\t\t{\"VF_ANGLES_X\",\t\t\t16, ev_float},\n\t\t{\"VF_ANGLES_Y\",\t\t\t17, ev_float},\n\t\t{\"VF_ANGLES_Z\",\t\t\t18, ev_float},\n\t\t{\"VF_DRAWWORLD\",\t\t19, ev_float},\n\t\t{\"VF_ENGINESBAR\",\t\t20, ev_float},\n\t\t{\"VF_DRAWCROSSHAIR\",\t21, ev_float},\n//\t\t{\"VF_CARTESIAN_ANGLES\",\t22, ev_vector},\n\t\t{\"VF_MINDIST\",\t\t\t23, ev_float},\n\t\t{\"VF_MAXDIST\",\t\t\t24, ev_float},\n\n\t\t{\"VF_CL_VIEWANGLES_V\",\t33, ev_vector},\n\t\t{\"VF_CL_VIEWANGLES_X\",\t34, ev_float},\n\t\t{\"VF_CL_VIEWANGLES_X\",\t35, ev_float},\n\t\t{\"VF_CL_VIEWANGLES_X\",\t36, ev_float},\n\t\t{\"VF_PERSPECTIVE\",\t\t200, ev_float},\n//\t\t{\"VF_DP_CLEARSCENE\",\t201, ev_float},\n\t\t{\"VF_ACTIVESEAT\",\t\t202, ev_float, ev_float},\n\t\t{\"VF_AFOV\",\t\t\t\t203, ev_float},\n//\t\t{\"VF_SCREENVSIZE\",\t\t204, ev_vector},\n//\t\t{\"VF_SCREENPSIZE\",\t\t205, ev_vector},\n\t\t{\"VF_VIEWENTITY\",\t\t206, ev_float},\n//\t\t{\"VF_STATSENTITY\",\t\t207, ev_float},\n//\t\t{\"VF_SCREENVOFFSET\",\t208, ev_float},\n\t\t{\"VF_RT_SOURCECOLOUR\",\t209, ev_string},\n\t\t{\"VF_RT_DEPTH\",\t\t\t210, ev_string, ev_float, ev_vector},\n\t\t{\"VF_RT_RIPPLE\",\t\t211, ev_string, ev_float, ev_vector},\n\t\t{\"VF_RT_DESTCOLOUR0\",\t212, ev_string, ev_float, ev_vector},\n\t\t{\"VF_RT_DESTCOLOUR1\",\t213, ev_string, ev_float, ev_vector},\n\t\t{\"VF_RT_DESTCOLOUR2\",\t214, ev_string, ev_float, ev_vector},\n\t\t{\"VF_RT_DESTCOLOUR3\",\t215, ev_string, ev_float, ev_vector},\n\t\t{\"VF_RT_DESTCOLOUR4\",\t216, ev_string, ev_float, ev_vector},\n\t\t{\"VF_RT_DESTCOLOUR5\",\t217, ev_string, ev_float, ev_vector},\n\t\t{\"VF_RT_DESTCOLOUR6\",\t218, ev_string, ev_float, ev_vector},\n\t\t{\"VF_RT_DESTCOLOUR7\",\t219, ev_string, ev_float, ev_vector},\n\t\t{\"VF_ENVMAP\",\t\t\t220, ev_string},\n\t\t{\"VF_USERDATA\",\t\t\t221, ev_pointer, ev_uint},\n\t\t{\"VF_SKYROOM_CAMERA\",\t222, ev_vector},\n//\t\t{\"VF_PIXELPSCALE\",\t\t223, ev_vector},\n\t\t{\"VF_PROJECTIONOFFSET\",\t224, ev_vector},\n\t\t{\"VF_VRBASEORIENTATION\",225, ev_vector, ev_vector},\n\n\t\t{\"VF_DP_MAINVIEW\",\t\t\t400, ev_float},\n//\t\t{\"VF_DP_MINFPS_QUALITY\",\t401, ev_float},\n\t};\n\n\tchar temp[256];\n\tconst QCC_eval_t *ev;\n\tint i, vf;\n\tif (!argcount)\n\t\treturn; // o.O\n\tev = QCC_SRef_EvalConst(arglist[0]->base);\n\tif (!ev)\t//can't check variables.\n\t\treturn;\n\tvf = ev->_float;\n\tif (!qccwarningaction[WARN_ARGUMENTCHECK])\n\t\treturn;\t//don't bother if its not relevant anyway.\n\n\tfor (i = 0; i < sizeof(argtypes)/sizeof(argtypes[0]); i++)\n\t{\n\t\tif (argtypes[i].n == vf)\n\t\t{\n\t\t\tif (argcount >= 2 && argtypes[i].t1 != ((arglist[1]->cast->type==ev_boolean)?arglist[1]->cast->parentclass->type:arglist[1]->cast->type))\n\t\t\t{\n\t\t\t\tQCC_PR_ParseWarning(WARN_ARGUMENTCHECK, \"%s(%s, ...): expected %s, got %s\", funcname, argtypes[i].name, basictypenames[argtypes[i].t1], TypeName(arglist[1]->cast, temp, sizeof(temp)));\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (argcount >= 3 && argtypes[i].t2 != ((arglist[2]->cast->type==ev_boolean)?arglist[2]->cast->parentclass->type:arglist[2]->cast->type))\n\t\t\t{\n\t\t\t\tQCC_PR_ParseWarning(WARN_ARGUMENTCHECK, \"%s(%s, X, ...): expected %s, got %s\", funcname, argtypes[i].name, basictypenames[argtypes[i].t2], TypeName(arglist[2]->cast, temp, sizeof(temp)));\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (argcount >= 4 && argtypes[i].t3 != ((arglist[3]->cast->type==ev_boolean)?arglist[3]->cast->parentclass->type:arglist[3]->cast->type))\n\t\t\t{\n\t\t\t\tQCC_PR_ParseWarning(WARN_ARGUMENTCHECK, \"%s(%s, X, Y, ...): expected %s, got %s\", funcname, argtypes[i].name, basictypenames[argtypes[i].t3], TypeName(arglist[3]->cast, temp, sizeof(temp)));\n\t\t\t\treturn;\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t}\n\tQCC_PR_ParseWarning(WARN_ARGUMENTCHECK, \"%s: unknown argument %i\", funcname, vf);\n}\n\nvoid QCC_VerifyArgs_cvar(const char *funcname, QCC_ref_t *cvarname)\n{\n\tif (cvarname->type == REF_GLOBAL && cvarname->cast->type == ev_string)\n\t{\n\t\tconst QCC_eval_t *a = QCC_SRef_EvalConst(cvarname->base);\n\t\tconst char *str;\n\t\tif (!a)\n\t\t\treturn;\n\t\tstr = strings + a->string;\n\t\tif (!strcmp(str, \"vid_conwidth\") || !strcmp(str, \"vid_conheight\"))\n\t\t\tQCC_PR_ParseWarning(WARN_ARGUMENTCHECK, \"%s: cvar(\\\"%s\\\") is deprecated and is likely to give some hacky value to work around old API usage which does not necessarily reflect the actual cvar value. Use getviewprop(VF_SCREENVSIZE) for the screen's virtual use, or use ftos+cvar_string to read the actual value of the cvar, or cast to variant to mute this warning.\", funcname, str);\n\t}\n}\n\nvoid QCC_VerifyArgs_MatchingFieldType(const char *funcname, QCC_ref_t *type, QCC_ref_t *fldref)\n{\n\tif (type->type == REF_GLOBAL && fldref->cast->type == ev_field)\n\t{\n\t\tchar temp[256];\n\t\tconst QCC_eval_t *a = QCC_SRef_EvalConst(type->base);\n\t\tif (a)\n\t\t{\n\t\t\tint i;\n\t\t\tif (type->cast->type == ev_integer || type->cast->type == ev_uint)\n\t\t\t\ti = a->_int;\n\t\t\telse if (type->cast->type == ev_float)\n\t\t\t\ti = a->_float;\n\t\t\telse\n\t\t\t\treturn;\n\t\t\tif (i != fldref->cast->aux_type->type && fldref->cast->aux_type->type != ev_variant)\n\t\t\t{\n\t\t\t\tif (i >= 0 && i < ev_variant)\n\t\t\t\t\tQCC_PR_ParseWarning(WARN_ARGUMENTCHECK, \"%s: indicated type ev_%s does not match passed field type .%s\", funcname, basictypenames[i], TypeName(fldref->cast->aux_type, temp, sizeof(temp)));\n\t\t\t\telse\n\t\t\t\t\tQCC_PR_ParseWarning(WARN_ARGUMENTCHECK, \"%s: indicated type %i is not a basic type\", funcname, i);\n\t\t\t}\n\t\t}\n\t}\n}\n\n#ifdef SUPPORTINLINE\nstruct inlinectx_s\n{\n\tQCC_def_t\t\t*fdef;\n\tQCC_function_t\t*func;\n\tQCC_sref_t arglist[8];\n\tpbool argisout[8];\n\n\tQCC_sref_t\t\tresult;\n\n\tstruct\n\t{\n\t\tQCC_def_t *def;\n\t\tQCC_def_t *srcsym;\n\t\tint bias;\n\t} locals[64];\n\tint numlocals;\n\n\tconst char *error;\n};\nstatic pbool QCC_PR_InlinePushResult(struct inlinectx_s *ctx, QCC_sref_t src/*original statement's symbol*/, QCC_sref_t mappedto/*effective value*/)\n{\n\tQCC_def_t *local;\n\tint i, p;\n\tfor (i = 0; i < ctx->numlocals; i++)\n\t{\n\t\tif (ctx->locals[i].srcsym == src.sym)\n\t\t\tbreak;\n\t}\n\tif (i == ctx->numlocals)\n\t{\n\t\tif (ctx->numlocals >= sizeof(ctx->locals)/sizeof(ctx->locals[0]))\n\t\t{\n\t\t\tctx->error = \"too many temps\";\n\t\t\treturn false;\n\t\t}\n\n\t\tfor (local = ctx->func->firstlocal, p = 0; local && p < MAX_PARMS && (unsigned int)p < ctx->func->type->num_parms; local = local->deftail->nextlocal, p++)\n\t\t{\n\t\t\tif (src.sym->symbolheader == local)\n\t\t\t{\n\t\t\t\tif (ctx->argisout[p])\n\t\t\t\t{\n\t\t\t\t\t/*if (ctx->arglist[p].sym->symbolheader != mappedto.sym || ctx->arglist[p].ofs != mappedto.ofs)\n\t\t\t\t\t{\n//\t\t\t\t\t\tctx->error = \"assignment wrote to variable other than intended output.\";\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}*/\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tctx->locals[i].srcsym = src.sym;\n\t\tctx->numlocals++;\n\t}\n\telse if (ctx->locals[i].def)\n\t\tQCC_FreeDef(ctx->locals[i].def);\n\n\tctx->locals[i].def = mappedto.sym;\n\tctx->locals[i].bias = mappedto.ofs - src.ofs;\t//FIXME: this feels unsafe (needed for array[immediate] fixups)\n\treturn true;\n}\n\nstatic QCC_sref_t QCC_PR_InlineFindDef(struct inlinectx_s *ctx, QCC_sref_t src, pbool assign)\n{\n\tQCC_def_t *d;\n\tint p;\n//\tint pstart = ctx->func->parm_start;\n\n\t//aliases are weird and annoying\n\tif (src.sym && src.sym->generatedfor)\n\t\tsrc.sym = src.sym->generatedfor;\n\n\tfor (p = 0; p < ctx->numlocals; p++)\n\t{\n\t\tif (ctx->locals[p].srcsym == src.sym && ctx->locals[p].def)\n\t\t{\n\t\t\td = ctx->locals[p].def;\n\t\t\tif (assign && src.sym)\n\t\t\t{\n\t\t\t\tif (!(src.sym->localscope || src.sym->temp))\n\t\t\t\t{\t//update the symbol to refer to its original value...\n//\t\t\t\t\tQCC_FreeDef(ctx->locals[p].def);\n\t\t\t\t\tctx->locals[p].def = ctx->locals[p].srcsym;\n\t\t\t\t\tctx->locals[p].bias = 0;\n\t\t\t\t\treturn QCC_MakeSRefForce(src.sym, src.ofs, src.cast);\n\t\t\t\t}\n\t\t\t\t//substitute the assignment with a new temp\n//\t\t\t\tQCC_FreeDef(ctx->locals[p].def);\n\t\t\t\tctx->locals[p].srcsym = src.sym;\n\t\t\t\tctx->locals[p].def = QCC_GetTemp(src.sym->type).sym;\n\t\t\t\tctx->locals[p].bias = 0;\n\t\t\t\treturn QCC_MakeSRefForce(ctx->locals[p].def, src.ofs, src.cast);\n\t\t\t}\n\t\t\treturn QCC_MakeSRefForce(d, src.ofs + ctx->locals[p].bias, src.cast);\n\t\t}\n\t}\n\n\tif (src.sym && (src.sym->localscope || src.sym->temp))\n\t{\n\t\t//if its a parm, use that\n\t\tQCC_def_t *local;\n\t\tfor (local = ctx->func->firstlocal, p = 0; local && p < MAX_PARMS && (unsigned int)p < ctx->func->type->num_parms; local = local->deftail->nextlocal, p++)\n\t\t{\n\t\t\tif (src.sym->symbolheader == local)\n\t\t\t{\n\t\t\t\tif (assign && !ctx->argisout[p])\n\t\t\t\t{\n//\t\t\t\t\tQCC_FreeDef(ctx->locals[p].def);\n\t\t\t\t\tctx->locals[p].srcsym = src.sym;\n\t\t\t\t\tctx->locals[p].def = QCC_GetTemp(src.sym->type).sym;\n\t\t\t\t\tctx->locals[p].bias = 0;\n\t\t\t\t\treturn QCC_MakeSRefForce(ctx->locals[p].def, src.ofs, src.cast);\n\t\t\t\t}\n\t\t\t\treturn QCC_MakeSRefForce(ctx->arglist[p].sym->symbolheader, ctx->arglist[p].ofs+src.ofs, src.cast);\n\t\t\t}\n\t\t}\n\n\t\t//otherwise its a local or a temp.\n\t\tif (ctx->numlocals >= sizeof(ctx->locals)/sizeof(ctx->locals[0]))\n\t\t\treturn nullsref;\n\t\tctx->locals[ctx->numlocals].srcsym = src.sym;\n\t\tctx->locals[ctx->numlocals].def = QCC_GetTemp(src.sym->type).sym;\n\t\tctx->locals[ctx->numlocals].bias = 0;\n\t\treturn QCC_MakeSRefForce(ctx->locals[ctx->numlocals++].def, src.ofs, src.cast);\n\t}\n\treturn QCC_MakeSRefForce(src.sym, src.ofs, src.cast);\n/*\n\tif (ofs < RESERVED_OFS)\n\t{\n\t\tif (ofs == OFS_RETURN)\n\t\t{\n\t\t\tdef_ret.type = type_void;\n\t\t\treturn &def_ret;\n\t\t}\n\t\tofs -= OFS_PARM0;\n\t\tofs /= 3;\n\t\tdef_parms[ofs].type = type_void;\n\t\treturn &def_parms[ofs];\n\t}\n\tif (ofs < pstart || ofs >= pstart+ctx->func->locals)\n\t{\n\t\tfor (d = &pr.def_head; d; d = d->next)\n\t\t{\n\t\t\tif (d->ofs == ofs)\n\t\t\t\treturn d;\n\t\t}\n\t\treturn NULL;\t//not found?\n\t}\n\n\tfor (p = 0; p < ctx->func->numparms; pstart += ctx->func->parm_size[p++])\n\t{\n\t\tif (ofs < pstart+ctx->func->parm_size[p])\n\t\t\treturn ctx->arglist[p];\n\t}\n\n\tif (ctx->numlocals >= sizeof(ctx->locals)/sizeof(ctx->locals[0]))\n\t\treturn NULL;\n\tctx->locals[ctx->numlocals].srcofs = ofs;\n\tctx->locals[ctx->numlocals].def = QCC_GetTemp(type_float);\n\treturn ctx->locals[ctx->numlocals++].def;\n*/\n}\n\n//returns a string saying why inlining failed.\nstatic const char *QCC_PR_InlineStatements(struct inlinectx_s *ctx)\n{\n\t/*FIXME: what happens with:\n\tt = foo;\n\tfoo = 5;\n\treturn t;\n\t*/\n\tQCC_sref_t a, b, c;\n\tQCC_statement_t *st, *est;\n\tconst QCC_eval_t *eval;\n//\tfloat af,bf;\n//\tint i;\n\n\tif (ctx->func->statements)\n\t{\n\t\tst = ctx->func->statements;\n\t\test = st + ctx->func->numstatements;\n\t}\n\telse\n\t{\n\t\tst = &statements[ctx->func->code];\n\t\test = statements+numstatements;\n\t}\n\twhile(st < est)\n\t{\n\t\tswitch(st->op)\n\t\t{\n\t\tcase OP_IF_F:\n\t\tcase OP_IFNOT_F:\n\t\tcase OP_IF_I:\n\t\tcase OP_IFNOT_I:\n\t\tcase OP_IF_S:\n\t\tcase OP_IFNOT_S:\n\t\t\tif (st->b.ofs > 0 && st[st->b.jumpofs].op == OP_DONE)\n\t\t\t{\n\t\t\t\t//logically, an if statement around the entire function is safe because the locals are safe\n\t\t\t}\n\t\tcase OP_GOTO:\n\t\tcase OP_SWITCH_F:\n\t\tcase OP_SWITCH_I:\n\t\tcase OP_SWITCH_E:\n\t\tcase OP_SWITCH_FNC:\n\t\tcase OP_SWITCH_S:\n\t\tcase OP_SWITCH_V:\n\t\t\treturn \"function contains branches\";\t//conditionals are not supported in any way. this keeps the code linear. each input can only come from a single place.\n/*\t\tcase OP_CALL0:\n\t\tcase OP_CALL1:\n\t\tcase OP_CALL2:\n\t\tcase OP_CALL3:\n\t\tcase OP_CALL4:\n\t\tcase OP_CALL5:\n\t\tcase OP_CALL6:\n\t\tcase OP_CALL7:\n\t\tcase OP_CALL8:\n\t\tcase OP_CALL1H:\n\t\tcase OP_CALL2H:\n\t\tcase OP_CALL3H:\n\t\tcase OP_CALL4H:\n\t\tcase OP_CALL5H:\n\t\tcase OP_CALL6H:\n\t\tcase OP_CALL7H:\n\t\tcase OP_CALL8H:\n\t\t\treturn \"function contains function calls\";\t//conditionals are not supported in any way. this keeps the code linear. each input can only come from a single place.\n*/\n\t\tcase OP_RETURN:\n\t\tcase OP_DONE:\n\t\t\ta = QCC_PR_InlineFindDef(ctx, st->a, false);\n\t\t\tctx->result = a;\n\t\t\tif (!a.cast)\n\t\t\t{\n\t\t\t\tif (ctx->func->type->aux_type->type == ev_void)\n\t\t\t\t\tctx->result.cast = type_void;\n\t\t\t\telse\n\t\t\t\t\treturn \"OP_RETURN no a\";\n\t\t\t}\n\t\t\treturn NULL;\n\t\tcase OP_BOUNDCHECK:\n\t\t\ta = QCC_PR_InlineFindDef(ctx, st->a, false);\n\t\t\tQCC_PR_InlinePushResult(ctx, st->a, a);\n\t\t\teval = QCC_SRef_EvalConst(a);\n\t\t\tif (eval)\n\t\t\t{\n\t\t\t\tif (eval->_int < st->c.jumpofs || eval->_int >= st->b.jumpofs)\n\t\t\t\t\tQCC_PR_ParseWarning(0, \"constant value exceeds bounds failed bounds check while inlining\");\n\t\t\t}\n\t\t\telse\n\t\t\t\tQCC_PR_SimpleStatement(&pr_opcodes[OP_BOUNDCHECK], a, st->b, st->c, false);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tif ((st->op >= OP_CALL0 && st->op <= OP_CALL8) || (st->op >= OP_CALL1H && st->op <= OP_CALL8H))\n\t\t\t{\n\t\t\t\t//function calls are a little weird in that they have no outputs\n\t\t\t\tif (st->a.cast)\n\t\t\t\t{\n\t\t\t\t\ta = QCC_PR_InlineFindDef(ctx, st->a, false);\n\t\t\t\t\tif (!a.cast)\n\t\t\t\t\t\treturn \"unable to determine what a was\";\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\ta = nullsref;\n\n\t\t\t\tif (st->b.cast)\n\t\t\t\t{\n\t\t\t\t\tb = QCC_PR_InlineFindDef(ctx, st->b, false);\n\t\t\t\t\tif (!b.cast)\n\t\t\t\t\t\treturn \"unable to determine what a was\";\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tb = nullsref;\n\t\t\t\tif (st->c.cast)\n\t\t\t\t{\n\t\t\t\t\tc = QCC_PR_InlineFindDef(ctx, st->c, false);\n\t\t\t\t\tif (!c.cast)\n\t\t\t\t\t\treturn \"unable to determine what a was\";\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tc = nullsref;\n\n\t\t\t\t{\n\t\t\t\t\tQCC_sref_t r;\n\t\t\t\t\tr.sym = &def_ret;\n\t\t\t\t\tr.ofs = 0;\n\t\t\t\t\tr.cast = a.cast;\n\t\t\t\t\tQCC_PR_InlinePushResult(ctx, r, nullsref);\n\t\t\t\t}\n\n\t\t\t\tQCC_ClobberDef(&def_ret);\n\t\t\t\tQCC_FreeTemp(a);\n\t\t\t\tQCC_FreeTemp(b);\n\t\t\t\tQCC_FreeTemp(c);\n\t\t\t\tQCC_LockActiveTemps(a);\n\t\t\t\tQCC_PR_SimpleStatement(&pr_opcodes[st->op], a, b, c, false);\n\n\t\t\t\t{\n\t\t\t\t\tQCC_sref_t r;\n\t\t\t\t\tr.sym = &def_ret;\n\t\t\t\t\tr.ofs = 0;\n\t\t\t\t\tr.cast = a.cast;\n\t\t\t\t\tQCC_PR_InlinePushResult(ctx, r, QCC_GetAliasTemp(QCC_MakeSRefForce(&def_ret, 0, a.cast->aux_type)));\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (pr_opcodes[st->op].flags & (OPF_STOREFLD|OPF_STOREPTROFS))\n\t\t\t{\t//these forms don't write to any actual globals, we've no real scope for optimising these out.\n\t\t\t\ta = QCC_PR_InlineFindDef(ctx, st->a, false);\n\t\t\t\tb = QCC_PR_InlineFindDef(ctx, st->b, false);\n\t\t\t\tc = QCC_PR_InlineFindDef(ctx, st->c, false);\n\t\t\t\tQCC_PR_SimpleStatement(&pr_opcodes[st->op], a, b, c, false);\n\t\t\t}\n\t\t\telse if (pr_opcodes[st->op].associative == ASSOC_RIGHT)\n\t\t\t{\n\t\t\t\t//a->b\n\t\t\t\tif (st->a.cast)\n\t\t\t\t{\n\t\t\t\t\ta = QCC_PR_InlineFindDef(ctx, st->a, false);\n\t\t\t\t\tif (!a.cast)\n\t\t\t\t\t\treturn \"unable to determine what a was\";\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\ta = nullsref;\n\t\t\t\tb = QCC_PR_InlineFindDef(ctx, st->b, !(pr_opcodes[st->op].flags & OPF_STOREPTR));\n\t\t\t\tc = QCC_PR_StatementFlags(&pr_opcodes[st->op], a, b, NULL, 0);\n\n\t\t\t\tif (!QCC_PR_InlinePushResult(ctx, st->b, c))\n\t\t\t\t\treturn ctx->error;\n\t\t\t}\n\t\t\telse if (OpAssignsToC(st->op))\n\t\t\t{\n\t\t\t\t//a+b->c\n\t\t\t\tif (st->a.cast)\n\t\t\t\t{\n\t\t\t\t\ta = QCC_PR_InlineFindDef(ctx, st->a, false);\n\t\t\t\t\tif (!a.cast)\n\t\t\t\t\t\treturn \"unable to determine what a was\";\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\ta = nullsref;\n\t\t\t\tif (st->b.cast)\n\t\t\t\t{\n\t\t\t\t\tb = QCC_PR_InlineFindDef(ctx, st->b, false);\n\t\t\t\t\tif (!b.cast)\n\t\t\t\t\t\treturn \"unable to determine what b was\";\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tb = nullsref;\n\n\t\t\t\tif (pr_opcodes[st->op].associative == ASSOC_LEFT && pr_opcodes[st->op].type_c != &type_void)\n\t\t\t\t{\n\t\t\t\t\tQCC_sref_t r;\n\t\t\t\t\tc = QCC_PR_InlineFindDef(ctx, st->c, true);\n\t\t\t\t\tr = QCC_PR_StatementFlags(&pr_opcodes[st->op], a, b, NULL, STFL_NOEMULATE);\n\t\t\t\t\tif (c.cast && !QCC_SRef_EvalConst(r))\n\t\t\t\t\t\tc = QCC_PR_StatementFlags(&pr_opcodes[OP_STORE_ENT], r, c, NULL, STFL_NOEMULATE);\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tQCC_FreeTemp(c);\n\t\t\t\t\t\tc = r;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (!QCC_PR_InlinePushResult(ctx, st->c, c))\n\t\t\t\t\t\treturn ctx->error;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tif (st->c.cast)\n\t\t\t\t\t{\n\t\t\t\t\t\tc = QCC_PR_InlineFindDef(ctx, st->c, false);\n\t\t\t\t\t\tif (!c.cast)\n\t\t\t\t\t\t\treturn \"unable to determine what c was\";\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\tc = nullsref;\n\t\t\t\t\tQCC_PR_SimpleStatement(&pr_opcodes[st->op], a, b, c, false);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\treturn \"nonstandard opcode form\";\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t\tst++;\n\t}\n\n\ta = QCC_PR_InlineFindDef(ctx, nullsref, false);\n\tctx->result = a;\n\tif (!a.cast)\n\t{\n\t\tif (ctx->func->type->aux_type->type == ev_void)\n\t\t\tctx->result.cast = type_void;\n\t\telse\n\t\t\treturn \"missing return type\";\n\t}\n\treturn NULL;\n}\n#endif\nstatic QCC_sref_t QCC_PR_Inline(QCC_sref_t fdef, QCC_ref_t **arglist, unsigned int argcount)\n{\n#ifndef SUPPORTINLINE\n\treturn nullsref;\n#else\n//\tQCC_def_t *dd = NULL;\n\tstruct inlinectx_s ctx;\n\tconst char *error;\n\tint statements, i;\n\tunsigned int a;\n\tconst QCC_eval_t *eval = QCC_SRef_EvalConst(fdef);\n\t//make sure that its a function type and that there's no special weirdness\n\tif (!eval || eval->function < 0 || argcount > 8 || eval->function >= numfunctions || fdef.sym->arraysize != 0 || fdef.cast->type != ev_function || argcount != fdef.cast->num_parms || fdef.cast->vargs || fdef.cast->vargcount)\n\t{\n\t\tQCC_PR_ParseWarning(0, \"Couldn't inline \\\"%s\\\": %s\", fdef.sym->name, \"inconsistent context\");\n\t\treturn nullsref;\n\t}\n\tctx.func = &functions[eval->function];\n\tif (fdef.cast != ctx.func->type)\n\t{\n\t\tQCC_PR_ParseWarning(0, \"Couldn't inline \\\"%s\\\": %s\", ctx.func->name, \"function was cast\");\n\t\treturn nullsref;\n\t}\n\tctx.numlocals = 0;\n\tfor (a = 0; a < argcount; a++)\n\t{\n\t\tctx.arglist[a] = QCC_RefToDef(arglist[a], true);\n\t\tctx.argisout[a] = ctx.func->type->params[a].out;\n\t}\n\tctx.fdef = fdef.sym;\n\tctx.result = nullsref;\n\tctx.error = NULL;\n\tif ((int)ctx.func->code <= 0)\n\t{\n\t\tchar *fname = ctx.func->name;\n\t\tif (argcount == 1)\n\t\t{\n\t\t\tconst QCC_eval_t *eval = QCC_SRef_EvalConst(ctx.arglist[0]);\n\t\t\tif (eval && !strcmp(fname, \"sin\"))\n\t\t\t\treturn QCC_MakeFloatConst(sin(eval->_float));\n\t\t\tif (eval && !strcmp(fname, \"cos\"))\n\t\t\t\treturn QCC_MakeFloatConst(cos(eval->_float));\n\t\t\tif (eval && !strcmp(fname, \"floor\"))\n\t\t\t\treturn QCC_MakeFloatConst(floor(eval->_float));\n\t\t\tif (eval && !strcmp(fname, \"ceil\"))\n\t\t\t\treturn QCC_MakeFloatConst(ceil(eval->_float));\n\t\t\tif (eval && !strcmp(fname, \"rint\"))\n\t\t\t\treturn QCC_MakeFloatConst((int)((eval->_float>0)?(eval->_float+0.5):(eval->_float-0.5)));\n\t\t\tif (eval && !strcmp(fname, \"fabs\"))\n\t\t\t\treturn QCC_MakeFloatConst(fabs(eval->_float));\n\t\t\tif (eval && !strcmp(fname, \"sqrt\"))\n\t\t\t\treturn QCC_MakeFloatConst(sqrt(eval->_float));\n\t\t\tif (eval && !strcmp(fname, \"log\"))\n\t\t\t\treturn QCC_MakeFloatConst(log(eval->_float));\n\t\t\tif (eval && !strcmp(fname, \"log10\"))\n\t\t\t\treturn QCC_MakeFloatConst(log10(eval->_float));\n\t\t\tif (eval && !strcmp(fname, \"ftoi\"))\n\t\t\t\treturn QCC_MakeIntConst(eval->_float);\n\t\t\tif (eval && !strcmp(fname, \"itof\"))\n\t\t\t\treturn QCC_MakeFloatConst(eval->_int);\n\t\t}\n\t\telse if (argcount == 2)\n\t\t{\n\t\t\tconst QCC_eval_t *a1 = QCC_SRef_EvalConst(ctx.arglist[0]);\n\t\t\tconst QCC_eval_t *a2 = QCC_SRef_EvalConst(ctx.arglist[1]);\n\t\t\tif (a1 && a2 && !strcmp(fname, \"pow\"))\n\t\t\t\treturn QCC_MakeFloatConst(pow(a1->_float, a2->_float));\n\t\t}\n\n\t\treturn nullsref;\t//don't try to inline builtins. that simply cannot work.\n\t}\n\n\t//FIXME: inefficient: we can't revert this on failure, so make sure its done early, just in case.\n\tif (argcount && ctx.arglist[0].sym->generatedfor == &def_ret)\n\t\tQCC_ClobberDef(&def_ret);\n\tQCC_ClobberDef(NULL);\n\n\tstatements = numstatements;\n\terror = QCC_PR_InlineStatements(&ctx);\n\tif (!error)\n\t\terror = ctx.error;\n\tif (error)\n\t{\n\t\tQCC_PR_ParseWarning(0, \"Couldn't inline \\\"%s\\\": %s\", ctx.func->name, error);\n\t\tQCC_PR_ParsePrintDef(0, fdef.sym);\n\t}\n\t\n\n\tfor(i = 0; i < ctx.numlocals; i++)\n\t{\n\t\tif (ctx.locals[i].def)\n\t\t\tQCC_FreeDef(ctx.locals[i].def);\n\t}\n\n\tif (!ctx.result.cast)\n\t\tQCC_UngenerateStatements(statements);\n\telse\n\t{\t//on success, make sure the args were freed\n\t\twhile (argcount-->0)\n\t\t\tQCC_FreeTemp(ctx.arglist[argcount]);\n\t}\n\n\treturn ctx.result;\n#endif\n}\npbool QCC_Intrinsic_strlen(QCC_sref_t *result, const QCC_eval_t *a)\n{\n\tconst char *str = &strings[a->string];\n\tsize_t l = 0;\n\tfor (l = 0; str[l]; l++)\n\t{\t//don't shortcut it when its an extended char. we don't know what the engine's strlen function would return.\n\t\tif ((unsigned char)str[l] & 0x80)\n\t\t\treturn false;\n\t}\n\tif (l > 1u<<24)\t//wait, what?\n\t\treturn false;\t//too big for a float.\n\t*result = QCC_MakeFloatConst(l);\n\treturn true;\n}\nQCC_sref_t QCC_PR_GenerateFunctionCallRef (QCC_sref_t newself, QCC_sref_t func, QCC_ref_t **arglist, unsigned int argcount)\t//warning, the func could have no name set if it's a field call.\n{\n\tQCC_sref_t\t\td, oself, self, retval;\n\tunsigned int\t\t\ti;\n\tQCC_type_t\t\t*t;\n//\tint np;\n\n\tint callconvention;\n\tQCC_statement_t *st;\n\tunsigned int parm;\n\tstruct\n\t{\n\t\tQCC_sref_t ref;\n\t\tint firststatement;\n\t} args[MAX_PARMS+MAX_EXTRA_PARMS];\n\tQCC_sref_t bigret = nullsref;\n\n\tconst char *funcname = QCC_GetSRefName(func);\n\tif (opt_constantarithmatic && argcount == 1 && arglist[0]->type == REF_GLOBAL)\n\t{\n\t\tconst QCC_eval_t *a = QCC_SRef_EvalConst(arglist[0]->base);\n\t\tif (a)\n\t\t{\n//\t\t\tif (!strcmp(funcname, \"isnan\"))\n//\t\t\t\treturn QCC_MakeFloatConst(isnan(a->_float));\n//\t\t\tif (!strcmp(funcname, \"isinf\"))\n//\t\t\t\treturn QCC_MakeFloatConst(isinf(a->_float));\n\t\t\tif (!strcmp(funcname, \"floor\"))\n\t\t\t\treturn QCC_MakeFloatConst(floor(a->_float));\n\t\t\tif (!strcmp(funcname, \"ceil\"))\n\t\t\t\treturn QCC_MakeFloatConst(ceil(a->_float));\n\t\t\tif (!strcmp(funcname, \"sin\"))\n\t\t\t\treturn QCC_MakeFloatConst(sin(a->_float));\n\t\t\tif (!strcmp(funcname, \"cos\"))\n\t\t\t\treturn QCC_MakeFloatConst(cos(a->_float));\n\t\t\tif (!strcmp(funcname, \"log\"))\n\t\t\t\treturn QCC_MakeFloatConst(log(a->_float));\n\t\t\tif (!strcmp(funcname, \"sqrt\"))\n\t\t\t\treturn QCC_MakeFloatConst(sqrt(a->_float));\n//\t\t\tif (!strcmp(funcname, \"ftos\"))\n//\t\t\t\treturn QCC_MakeStringConst(ftos(a->_float));\t//engines differ too much in their ftos implementation for this to be worthwhile\n\n\t\t\tif (!strcmp(funcname, \"strlen\") && QCC_Intrinsic_strlen(&d, a))\n\t\t\t\treturn d;\n\t\t\tif (!strcmp(funcname, \"stof\"))\n\t\t\t\treturn QCC_MakeFloatConst(atof(&strings[a->string]));\n\t\t}\n\t}\n\telse if (opt_constantarithmatic && argcount == 2 && arglist[0]->type == REF_GLOBAL && arglist[1]->type == REF_GLOBAL)\n\t{\n\t\tconst QCC_eval_t *a = QCC_SRef_EvalConst(arglist[0]->base);\n\t\tconst QCC_eval_t *b = QCC_SRef_EvalConst(arglist[1]->base);\n\t\tif (a && b)\n\t\t{\n\t\t\tif (arglist[0]->cast == type_float && arglist[1]->cast == type_float)\n\t\t\t{\n\t\t\t\tif (!strcmp(funcname, \"pow\"))\n\t\t\t\t\treturn QCC_MakeFloatConst(pow(a->_float, b->_float));\n\t\t\t\tif (!strcmp(funcname, \"mod\"))\n\t\t\t\t\treturn QCC_MakeFloatConst(fmodf((int)a->_float, (int)b->_float));\n\t\t\t\tif (!strcmp(funcname, \"min\"))\n\t\t\t\t\treturn QCC_MakeFloatConst(min(a->_float, b->_float));\n\t\t\t\tif (!strcmp(funcname, \"max\"))\n\t\t\t\t\treturn QCC_MakeFloatConst(max(a->_float, b->_float));\n\t\t\t\tif (!strcmp(funcname, \"bitshift\"))\n\t\t\t\t{\n\t\t\t\t\tif (b->_float < 0)\n\t\t\t\t\t\treturn QCC_MakeFloatConst((int)a->_float >> (int)-b->_float);\n\t\t\t\t\telse\n\t\t\t\t\t\treturn QCC_MakeFloatConst((int)a->_float << (int)b->_float);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tif (!strcmp(funcname, \"sprintf\"))\n\t\tQCC_VerifyFormatString(funcname, arglist, argcount);\n\tif (!strcmp(funcname, \"cvar\") && argcount == 1)\n\t\tQCC_VerifyArgs_cvar(funcname, arglist[0]);\n\tif ((!strcmp(funcname, \"clientstat\")||!strcmp(funcname, \"addstat\")) && argcount == 3)\n\t\tQCC_VerifyArgs_MatchingFieldType(funcname, arglist[1], arglist[2]);\n\tif (!strcmp(funcname, \"setviewprop\") || !strcmp(funcname, \"setproperty\"))\n\t\tQCC_VerifyArgs_setviewprop(funcname, arglist, argcount);\n\tif (!strcmp(funcname, \"sendevent\"))\t//void(string eventname, string argtypes, ...)\n\t\tQCC_VerifyArgs_sendevent(funcname, arglist, argcount);\n\n\tfunc.sym->timescalled++;\n\n\tif (!newself.cast && func.sym->constant && func.sym->allowinline)\n\t{\n\t\td = QCC_PR_Inline(func, arglist, argcount);\n\t\tif (d.cast)\n\t\t{\n\t\t\toptres_inlines++;\n\t\t\tfunc.sym->referenced = true;\t//not really, but hey, the warning is stupid.\n\t\t\tQCC_FreeTemp(func);\n\t\t\treturn d;\n\t\t}\n\t}\n\n\tif (QCC_OPCodeValid(&pr_opcodes[OP_CALL1H]))\n\t\tcallconvention = OP_CALL1H;\t//FTE extended\n\telse\n\t\tcallconvention = OP_CALL1;\t//standard\n\n\tt = func.cast;\n\tif (t->type == ev_function)\n\t{\n\t\tif (t->aux_type->size > type_vector->size)\n\t\t\tbigret = QCC_GetTemp(QCC_PR_PointerType(t->aux_type));\n\t}\n\telse if (t->type != ev_variant)\n\t{\t//all varg\n\t\tQCC_PR_ParseErrorPrintSRef (ERR_NOTAFUNCTION, func, \"not a function\");\n\t}\n\n\tself = nullsref;\n\toself = nullsref;\n\td = nullsref;\n\n\tif (newself.cast)\n\t{\n\t\t//we're entering OO code with a different self. make sure self is preserved.\n\t\t//eg: other.touch(self)\n\n\t\tself = QCC_PR_GetSRef(type_entity, \"self\", NULL, true, 0, false);\n\t\tif (newself.ofs != self.ofs || newself.sym != self.sym)\n\t\t{\n\t\t\toself = QCC_GetTemp(pr_classtype?pr_classtype:type_entity);\n\t\t\t//oself = self\n\t\t\tQCC_PR_SimpleStatement(&pr_opcodes[OP_STORE_ENT], self, oself, nullsref, false);\n\t\t\t//self = other\n\t\t\tQCC_PR_SimpleStatement(&pr_opcodes[OP_STORE_ENT], newself, self, nullsref, false);\n\n\t\t\t//if the args refered to self, update them to refer to oself instead\n\t\t\t//(as self is now set to 'other')\n\t\t\tfor (i = 0; i < argcount; i++)\n\t\t\t{\n\t\t\t\tif (arglist[i]->base.ofs == self.ofs && arglist[i]->base.sym && arglist[i]->base.sym->symbolheader == self.sym->symbolheader)\n\t\t\t\t{\n\t\t\t\t\tQCC_FreeTemp(arglist[i]->base);\n\t\t\t\t\targlist[i]->base = oself;\n\t\t\t\t\tQCC_UnFreeTemp(arglist[i]->base);\n\t\t\t\t}\n\t\t\t\tif (arglist[i]->index.ofs == self.ofs && arglist[i]->index.sym && arglist[i]->index.sym->symbolheader == self.sym->symbolheader)\n\t\t\t\t{\n\t\t\t\t\tQCC_FreeTemp(arglist[i]->index);\n\t\t\t\t\targlist[i]->index = oself;\n\t\t\t\t\tQCC_UnFreeTemp(arglist[i]->index);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tQCC_FreeTemp(self);\n\t\t\tself = nullsref;\n\t\t}\n\t\tQCC_FreeTemp(newself);\n\t}\n\n//\twrite the arguments into temps\n\tparm = 0;\n\tfor (i = 0; i < argcount; i++)\n\t{\n\t\tif (func.cast->type == ev_function && func.cast->params && i < func.cast->num_parms && func.cast->params[i].out == 2)\n\t\t{\n\t\t\targs[parm].firststatement = numstatements;\n\t\t\targs[parm++].ref = nullsref;\t//__out args do not actually need to pass anything.\n\t\t}\n\t\telse if (callconvention == OP_CALL1H && parm < 2 && arglist[i]->cast->size <= 3)\n\t\t{\n\t\t\targs[parm].firststatement = numstatements;\n\t\t\targs[parm++].ref = QCC_RefToDef(arglist[i], func.cast->type != ev_function || !func.cast->params || i >= func.cast->num_parms || !func.cast->params[i].out);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tint firststatement;\n\t\t\tQCC_sref_t sref = nullsref, copyop_index = nullsref;\n\t\t\tint copyop[3] = {0,0,0}, copyop_idx=0;\n\t\t\tif (arglist[i]->postinc || arglist[i]->cast->align!=32)\n\t\t\t{\n\t\t\t\targlist[i]->base = QCC_RefToDef(arglist[i], true);\n\t\t\t\targlist[i]->index = nullsref;\n\t\t\t\targlist[i]->type = REF_GLOBAL;\n\t\t\t\targlist[i]->postinc = false;\n\t\t\t\targlist[i]->readonly = true;\n\t\t\t}\n\n\t\t\tswitch(arglist[i]->type)\n\t\t\t{\n\t\t\tcase REF_GLOBAL:\n\t\t\tcase REF_ARRAY:\n\t\t\t\tif (!arglist[i]->index.cast || QCC_SRef_EvalConst(arglist[i]->index))\n\t\t\t\t\tbreak;\t//no problem\n\t\t\t\tif (arglist[i]->cast->align != 32)\n\t\t\t\t\tQCC_PR_ParseWarning (ERR_INTERNAL, \"QCC_PR_GenerateFunctionCallRef: REF_ARRAY: align not 32\");\n\t\t\t\tif (QCC_OPCodeValid(&pr_opcodes[OP_LOADA_F]))\n\t\t\t\t{\n\t\t\t\t\tcopyop[2] = OP_LOADA_V;\n\t\t\t\t\tcopyop[1] = OP_LOADA_I64;\n\t\t\t\t\tcopyop[0] = OP_LOADA_F;\n\t\t\t\t\tcopyop_idx = -2;\t//offset the base ref\n\t\t\t\t\tcopyop_index = arglist[i]->index;\n\t\t\t\t\tcopyop_index = QCC_SupplyConversion(copyop_index, ev_integer, true);\n\t\t\t\t\tsref = arglist[i]->base;\n\t\t\t\t}\n\t\t\t\telse if (arglist[i]->base.sym->arraylengthprefix && QCC_OPCodeValid(&pr_opcodes[OP_FETCH_GBL_F]))\n\t\t\t\t{\n\t\t\t\t\tcopyop[2] = 0;\n\t\t\t\t\tcopyop[1] = 0;\n\t\t\t\t\tcopyop[0] = OP_FETCH_GBL_F;\n\t\t\t\t\tcopyop_idx = OP_ADD_F;\n\t\t\t\t\tcopyop_index = arglist[i]->index;\n\t\t\t\t\tsref = arglist[i]->base;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase REF_FIELD:\n\t\t\t\tif (arglist[i]->cast->align != 32)\n\t\t\t\t\tQCC_PR_ParseWarning (ERR_INTERNAL, \"QCC_PR_GenerateFunctionCallRef: REF_FIELD: align not 32\");\n\t\t\t\tcopyop[2] = OP_LOAD_V;\n\t\t\t\tcopyop[1] = OP_LOAD_I64;\n\t\t\t\tcopyop[0] = OP_LOAD_F;\n\t\t\t\tcopyop_index = arglist[i]->index;\n\t\t\t\tcopyop_idx = -1;\n\t\t\t\tif (!QCC_SRef_EvalConst(copyop_index))\n\t\t\t\t{\t//if its a variable then its probably not a proper field (which has extra field ref values following it). do the shitty thing and make assumptions about ordering. :(\n\t\t\t\t\tcopyop_index.cast = type_integer;\n\t\t\t\t\tcopyop_idx = OP_ADD_I;\n\t\t\t\t}\n\t\t\t\tsref = arglist[i]->base;\n\t\t\t\tbreak;\n\t\t\tcase REF_POINTER:\n\t\t\t\tif (arglist[i]->cast->align == 8)\n\t\t\t\t{\n\t\t\t\t\tcopyop[2] = 0;\n\t\t\t\t\tcopyop[1] = 0;\n\t\t\t\t\tcopyop[0] = (arglist[i]->cast->type==ev_bitfld&&arglist[i]->cast->parentclass==type_integer)?OP_LOADP_I8:OP_LOADP_U8;\n\t\t\t\t}\n\t\t\t\telse if (arglist[i]->cast->align == 16)\n\t\t\t\t{\n\t\t\t\t\tcopyop[2] = 0;\n\t\t\t\t\tcopyop[1] = 0;\n\t\t\t\t\tcopyop[0] = (arglist[i]->cast->type==ev_bitfld&&arglist[i]->cast->parentclass==type_integer)?OP_LOADP_I16:OP_LOADP_U16;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tcopyop[2] = OP_LOADP_V;\n\t\t\t\t\tcopyop[1] = OP_LOADP_I64;\n\t\t\t\t\tcopyop[0] = OP_LOADP_F;\n\t\t\t\t}\n\t\t\t\tcopyop_idx = OP_ADD_I;\n\t\t\t\tcopyop_index = arglist[i]->index;\n\t\t\t\tif (!copyop_index.cast)\n\t\t\t\t\tcopyop_index = QCC_MakeUIntConst(0);\t//don't bug out!\n\t\t\t\tsref = arglist[i]->base;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\t//warning:reftodef may be doing a copy for various ref types. we're then copying it into the args as an extra step. this is obviously wasteful.\n\t\t\t\t//reading pointer, field, or struct types needs special code here to avoid that copy.\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tfirststatement = numstatements;\n\t\t\tif (!sref.cast)\n\t\t\t{\n\t\t\t\tsref = QCC_RefToDef(arglist[i], func.cast->type != ev_function || !func.cast->params || i >= func.cast->num_parms || !func.cast->params[i].out);\n\t\t\t\tcopyop[2] = OP_STORE_V;\n\t\t\t\tcopyop[1] = OP_STORE_I64;\n\t\t\t\tcopyop[0] = OP_STORE_F;\n\t\t\t\tcopyop_idx = 0;\n\t\t\t}\n\t\t\telse if (!(func.cast->type != ev_function || !func.cast->params || i >= func.cast->num_parms || !func.cast->params[i].out))\n\t\t\t{\n\t\t\t\tQCC_UnFreeTemp(sref);\n\t\t\t\tQCC_UnFreeTemp(copyop_index);\n\t\t\t}\n\n\t\t\tif (copyop_index.cast || arglist[i]->cast->size > 3)\n\t\t\t{\n\t\t\t\tunsigned int ofs;\n\t\t\t\tQCC_sref_t src, fparm, newindex;\n\t\t\t\tint asz;\n\t\t\t\tsrc = sref;\n\t\t\t\tif (copyop_idx == OP_ADD_I && copyop_index.cast)\n\t\t\t\t\tcopyop_index = QCC_SupplyConversion(copyop_index, ev_integer, true);\n\t\t\t\telse if (copyop_idx == OP_ADD_F && copyop_index.cast)\n\t\t\t\t\tcopyop_index = QCC_SupplyConversion(copyop_index, ev_float, true);\n\t\t\t\tfor (ofs = 0; ofs < arglist[i]->cast->size; )\n\t\t\t\t{\n\t\t\t\t\tif (copyop_idx == -1)\n\t\t\t\t\t{\t//with this mode, the base reference can just be updated. no mess with the index.\n\t\t\t\t\t\tnewindex = copyop_index;\n\t\t\t\t\t\tnewindex.ofs += ofs;\n\t\t\t\t\t\tQCC_UnFreeTemp(copyop_index);\n\t\t\t\t\t}\n\t\t\t\t\telse if (copyop_idx == -2)\n\t\t\t\t\t{\t//with this mode, the base reference can just be updated. no mess with the index.\n\t\t\t\t\t\tnewindex = copyop_index;\n\t\t\t\t\t\tQCC_UnFreeTemp(copyop_index);\n\t\t\t\t\t}\n\t\t\t\t\telse if (copyop_idx == OP_ADD_I)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (!copyop_index.cast)\n\t\t\t\t\t\t\tnewindex = QCC_MakeIntConst(ofs);\t//index can be simple constants\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\t//if the index is a constant, then things get a little easier\n\t\t\t\t\t\t\tconst QCC_eval_t *cnst = QCC_SRef_EvalConst(copyop_index);\n\t\t\t\t\t\t\tif (cnst)\n\t\t\t\t\t\t\t\tnewindex = QCC_MakeIntConst(ofs + cnst->_int);\n\t\t\t\t\t\t\telse if (ofs)\t//okay, looks like we'll have to actually do some maths then\n\t\t\t\t\t\t\t\tnewindex = QCC_PR_StatementFlags (&pr_opcodes[OP_ADD_I], copyop_index, QCC_MakeIntConst(ofs), NULL, STFL_PRESERVEA);\n\t\t\t\t\t\t\telse\t//first index needs no offset.\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tnewindex = copyop_index;\n\t\t\t\t\t\t\t\tQCC_UnFreeTemp(copyop_index);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\telse if (copyop_idx == OP_ADD_F)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (!copyop_index.cast)\n\t\t\t\t\t\t\tnewindex = QCC_MakeFloatConst(ofs);\t//index can be simple constants\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\t//if the index is a constant, then things get a little easier\n\t\t\t\t\t\t\tconst QCC_eval_t *cnst = QCC_SRef_EvalConst(copyop_index);\n\t\t\t\t\t\t\tif (cnst)\n\t\t\t\t\t\t\t\tnewindex = QCC_MakeFloatConst(ofs + cnst->_float);\n\t\t\t\t\t\t\telse if (ofs)\t//okay, looks like we'll have to actually do some maths then\n\t\t\t\t\t\t\t\tnewindex = QCC_PR_StatementFlags (&pr_opcodes[OP_ADD_F], copyop_index, QCC_MakeFloatConst(ofs), NULL, STFL_PRESERVEA);\n\t\t\t\t\t\t\telse\t//first index needs no offset.\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tnewindex = copyop_index;\n\t\t\t\t\t\t\t\tQCC_UnFreeTemp(copyop_index);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\tnewindex = nullsref;\n\n\t\t\t\t\tasz = 3-(ofs%3);\n\t\t\t\t\tif (ofs+asz > arglist[i]->cast->size)\n\t\t\t\t\t\tasz = arglist[i]->cast->size-ofs;\n\t\t\t\t\twhile (asz > 3 || !copyop[asz-1] || (asz>1&&!QCC_OPCodeValid(&pr_opcodes[copyop[asz-1]])))\n\t\t\t\t\t\tasz--;\t//can't do that size...\n\n\t\t\t\t\tif (copyop[0] == OP_STORE_F)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (ofs+asz != arglist[i]->cast->size)\n\t\t\t\t\t\t\tQCC_UnFreeTemp(src);\n\n\t\t\t\t\t\tif (!(ofs%3))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\targs[parm].firststatement = numstatements;\n\t\t\t\t\t\t\targs[parm].ref = QCC_GetTemp(type_vector);\n\t\t\t\t\t\t\tQCC_FreeTemp(QCC_PR_StatementFlags (&pr_opcodes[copyop[asz-1]], src, args[parm].ref, NULL, STFL_PRESERVEB));\n\t\t\t\t\t\t\tparm++;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tQCC_sref_t t = args[parm-1].ref;\n\t\t\t\t\t\t\tt.ofs += ofs%3;\n\t\t\t\t\t\t\tQCC_FreeTemp(QCC_PR_StatementFlags (&pr_opcodes[copyop[asz-1]], src, t, NULL, STFL_PRESERVEB));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tif (ofs%3)\n\t\t\t\t\t\t\tparm--;\n\t\t\t\t\t\tif (parm>=MAX_PARMS)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tfparm = extra_parms[parm - MAX_PARMS];\n\t\t\t\t\t\t\tif (!fparm.cast)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tchar name[128];\n\t\t\t\t\t\t\t\tQC_snprintfz(name, sizeof(name), \"$parm%u\", parm);\n\t\t\t\t\t\t\t\tfparm = extra_parms[parm - MAX_PARMS] = QCC_PR_GetSRef(type_vector, name, NULL, true, 0, GDF_STRIP);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\tQCC_ForceUnFreeDef(fparm.sym);\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tfparm.sym = &def_parms[parm];\n\t\t\t\t\t\t\tfparm.cast = type_vector;\n\t\t\t\t\t\t\tQCC_ForceUnFreeDef(fparm.sym);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tfparm.ofs = ofs%3;\n\t\t\t\t\t\tif (!fparm.ofs)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\targs[parm].firststatement = numstatements;\n\t\t\t\t\t\t\targs[parm].ref = fparm;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tparm++;\n\n\t\t\t\t\t\tif (ofs+asz == arglist[i]->cast->size)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tQCC_FreeTemp(src);\n\t\t\t\t\t\t\tQCC_FreeTemp(copyop_index);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tQCC_FreeTemp(newindex);\n\n\t\t\t\t\t\tif (copyop_idx == -2)\n\t\t\t\t\t\t{\t//with this mode, the base reference can just be updated. no mess with the index.\n\t\t\t\t\t\t\tsrc.ofs += ofs;\n\t\t\t\t\t\t\tQCC_PR_SimpleStatement(&pr_opcodes[copyop[asz-1]], src, newindex, fparm, false);\n\t\t\t\t\t\t\tsrc.ofs -= ofs;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tQCC_PR_SimpleStatement(&pr_opcodes[copyop[asz-1]], src, newindex, fparm, false);\n\t\t\t\t\t}\n\n\t\t\t\t\tofs += asz;\n\n\t\t\t\t\tif (copyop_idx == 0)\n\t\t\t\t\t\tsrc.ofs += asz;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\t//small and simple. yay.\n\t\t\t\targs[parm].firststatement = firststatement;\n\t\t\t\targs[parm].ref = sref;\n\t\t\t\tparm++;\n\t\t\t}\n\t\t}\n\t}\n\n\t//remap those temps to the actual parms if the parms were not used in the interim.\n\n\tfor (i = ((callconvention==OP_CALL1H)?2:0); i < parm; i++)\n\t{\n\t\tif (i>=MAX_PARMS)\n\t\t{\n\t\t\tif (i - MAX_PARMS >= MAX_EXTRA_PARMS)\n\t\t\t\tQCC_PR_ParseErrorPrintSRef (ERR_TOOMANYTOTALPARAMETERS, func, \"Function call needs too much paramater storage\");\n\n\t\t\td = extra_parms[i - MAX_PARMS];\n\t\t\tif (!d.cast)\n\t\t\t{\n\t\t\t\tchar name[128];\n\t\t\t\tQC_snprintfz(name, sizeof(name), \"$parm%u\", i);\n\t\t\t\td = extra_parms[i - MAX_PARMS] = QCC_PR_GetSRef(type_vector, name, NULL, true, 0, GDF_STRIP);\n\t\t\t\tQCC_FreeTemp(d);\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\td.sym = &def_parms[i];\n\t\t\td.ofs = 0;\n\t\t\td.cast = type_vector;\n\t\t}\n\t\td.cast = args[i].ref.cast;\n\t\tif (!d.cast)\n\t\t\tcontinue;\n\n\t\tif (QCC_RemapTemp(args[i].firststatement, numstatements, args[i].ref, d))\n\t\t{\n\t\t\tQCC_FreeTemp(args[i].ref);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (args[i].ref.sym != d.sym || args[i].ref.ofs != d.ofs)\n\t\t\t{\n\t\t\t\tQCC_ForceUnFreeDef(d.sym);\n#if 0\n\t\t\t\tQCC_StoreToSRef(d, args[i].ref, d.cast, false, false);\n#else\n\t\t\t\tif (args[i].ref.cast->size == 3)\n\t\t\t\t\tQCC_FreeTemp(QCC_PR_StatementFlags (&pr_opcodes[OP_STORE_V], args[i].ref, d, NULL, 0));\n\t\t\t\telse if (args[i].ref.cast->size == 2)\n\t\t\t\t\tQCC_FreeTemp(QCC_PR_StatementFlags (&pr_opcodes[QCC_OPCodeValid(&pr_opcodes[OP_STORE_I64])?OP_STORE_I64:OP_STORE_V], args[i].ref, d, NULL, 0));\n\t\t\t\telse if (args[i].ref.cast->size == 1)\n\t\t\t\t\tQCC_FreeTemp(QCC_PR_StatementFlags (&pr_opcodes[OP_STORE_F], args[i].ref, d, NULL, 0));\n\t\t\t\telse\n\t\t\t\t\tQCC_PR_ParseErrorPrintSRef (ERR_BADEXTENSION, func, \"arg storage not 1, 2, or 3.\");\n#endif\n\t\t\t}\n\t\t\telse\n\t\t\t\tQCC_FreeTemp(args[i].ref);\n\t\t}\n\t\targs[i].ref = d;\n\t}\n\n\tif (func.cast->vargcount)\n\t{\n\t\tQCC_sref_t va_passcount = QCC_PR_GetSRef(type_float, \"__va_count\", NULL, true, 0, 0);\n\t\tQCC_FreeTemp(QCC_PR_StatementFlags (&pr_opcodes[OP_STORE_F], QCC_MakeFloatConst(argcount), va_passcount, NULL, 0));\n\t}\n\n\t//free any references to ofs_ret if we know we're not going to make any calls that will clobber it.\n\t//ie: func(func()); should not try to preserve ofs_return because of it being in use as part of the nested call\n\tif (callconvention == OP_CALL1H)\n\t{\n\t\tfor (i = 0; i < parm && i < 2; i++)\n\t\t\tif (args[i].ref.sym && args[i].ref.sym->generatedfor == &def_ret && args[i].ref.sym->refcount == 1)\n\t\t\t{\n\t\t\t\tQCC_FreeTemp(args[i].ref);\n\t\t\t\targs[i].ref.sym = &def_ret;\n\t\t\t\tQCC_ForceUnFreeDef(args[i].ref.sym);\n\t\t\t\tbreak;\n\t\t\t}\n\t}\n\n\tQCC_ClobberDef(&def_ret);\n\n\t/*can free temps used for arguments now*/\n\tif (callconvention == OP_CALL1H)\n\t{\n\t\tfor (i = 0; i < parm && i < 2; i++)\n\t\t{\n\t\t\tif (!args[i].ref.cast)\n\t\t\t\tcontinue;\n\t\t\targs[i].ref.sym->referenced=true;\n\t\t\tQCC_FreeTemp(args[i].ref);\n\t\t}\n\t}\n\n\t//we dont need to lock the local containing the function index because its thrown away after the call anyway\n\t//(if a function is called in the argument list then it'll be locked as part of that call)\n\tQCC_LockActiveTemps(func);\t//any temps before are likly to be used with the return value.\n\n\tif (bigret.cast)\t//get some storage\n\t{\n\t\tQCC_PR_SimpleStatement(&pr_opcodes[OP_PUSH], QCC_MakeUIntConst(bigret.cast->aux_type->size), nullsref, bigret, false);\t//get some cheap/auto storage the child can safely write to.\n\t\tQCC_PR_SimpleStatement(QCC_OPCodeValid(&pr_opcodes[OP_STORE_P])?&pr_opcodes[OP_STORE_P]:&pr_opcodes[OP_STORE_F], bigret, QCC_MakeSRefForce(&def_ret, 0, bigret.cast), nullsref, false);\t//let the child know where to write.\n\t}\n\n\t//generate the call\n\tif (parm>MAX_PARMS)\n\t\tQCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[callconvention-1+MAX_PARMS], func, nullsref, (QCC_statement_t **)&st));\n\telse if (parm)\n\t\tQCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[callconvention-1+parm], func, nullsref, (QCC_statement_t **)&st));\n\telse\n\t\tQCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_CALL0], func, nullsref, (QCC_statement_t **)&st));\n\n\tif (callconvention == OP_CALL1H)\n\t{\n\t\tif (parm)\n\t\t{\n\t\t\tst->b = args[0].ref;\n\t\t\tif (parm>1)\n\t\t\t\tst->c = args[1].ref;\n\t\t}\n\t}\n\n\tif (bigret.cast)\t//get some storage\n\t{\n\t\tQCC_ref_t refbuf, *r;\n\t\tr = QCC_PR_BuildRef(&refbuf, REF_POINTER, bigret, nullsref, bigret.cast->aux_type, false, 0);\n\t\tretval = QCC_RefToDef(r, true);\t//this line sucks perf\n\t\t//QCC_PR_SimpleStatement(&pr_opcodes[OP_POP], QCC_MakeUIntConst(bigret.cast->aux_type->size), nullsref, bigret, false); //should really be part of the qcc_ref_t\n\t}\n\telse if (t->type == ev_variant)\n\t\tretval = QCC_GetAliasTemp(QCC_MakeSRefForce(&def_ret, 0, type_variant));\n\telse\n\t\tretval = QCC_GetAliasTemp(QCC_MakeSRefForce(&def_ret, 0, t->aux_type));\n\n\t//restore the class owner\n\tif (oself.cast)\n\t\tQCC_PR_SimpleStatement(&pr_opcodes[OP_STORE_ENT], oself, self, nullsref, false);\n\n\t//handle outs.\n\tif (func.cast->type == ev_function && func.cast->params)\n\t{\n\t\tunsigned int parm = 0;\n\t\tfor (i = 0; i < argcount && i < func.cast->num_parms; i++)\t//fixme: parm offset should be (deftype->size+2)/3\n\t\t{\n\t\t\tif (func.cast->params[i].out)\n\t\t\t{\n\t\t\t\tif (oself.cast)\n\t\t\t\t{\t//gah, this is messy\n\t\t\t\t\tif (arglist[i]->base.ofs == oself.ofs && arglist[i]->base.sym == oself.sym)\n\t\t\t\t\t{\n\t\t\t\t\t\tQCC_UnFreeTemp(self);\n\t\t\t\t\t\tQCC_FreeTemp(arglist[i]->base);\n\t\t\t\t\t\targlist[i]->base = self;\n\t\t\t\t\t}\n\t\t\t\t\tif (arglist[i]->index.ofs == oself.ofs && arglist[i]->index.sym == oself.sym)\n\t\t\t\t\t{\n\t\t\t\t\t\tQCC_UnFreeTemp(self);\n\t\t\t\t\t\tQCC_FreeTemp(arglist[i]->index);\n\t\t\t\t\t\targlist[i]->index = self;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\n\t\t\t\tif (arglist[i]->readonly)\n\t\t\t\t{\n\t\t\t\t\tQCC_PR_ParseWarning(ERR_TYPEMISMATCHPARM, \"Unable to write to read-only out argument\");\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (parm>=MAX_PARMS)\n\t\t\t\t{\n\t\t\t\t\td = extra_parms[parm - MAX_PARMS];\n\t\t\t\t\tif (!d.cast)\n\t\t\t\t\t{\n\t\t\t\t\t\tchar name[128];\n\t\t\t\t\t\tQC_snprintfz(name, sizeof(name), \"$parm%u\", parm);\n\t\t\t\t\t\td = extra_parms[parm - MAX_PARMS] = QCC_PR_GetSRef(type_vector, name, NULL, true, 0, GDF_STRIP);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\tQCC_ForceUnFreeDef(d.sym);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\td.sym = &def_parms[parm];\n\t\t\t\t\td.ofs = 0;\n\t\t\t\t\td.cast = type_vector;\n\t\t\t\t\tQCC_ForceUnFreeDef(d.sym);\n\t\t\t\t}\n\t\t\t\td.cast = arglist[i]->cast;\n\n\t\t\t\t//FIXME: this may need to generate function calls, which can potentially clobber parms. This would be bad. we may need to copy them all out first THEN do the assignments.\n\t\t\t\t//FIXME: this can't cope with splitting return values over different extra_parms.\n\t\t\t\tQCC_StoreSRefToRef(arglist[i], d, false, false);\n\t\t\t}\n\t\t\tparm += (func.cast->params[i].type->size+2)/3;\n\t\t}\n\t}\n\n\tQCC_FreeTemp(oself);\n\tQCC_FreeTemp(self);\n\n\treturn retval;\n}\nQCC_sref_t QCC_PR_GenerateFunctionCallSref (QCC_sref_t newself, QCC_sref_t func, QCC_sref_t *arglist, int argcount)\n{\n\tQCC_ref_t arg[MAX_PARMS];\n\tQCC_ref_t *outlist[MAX_PARMS];\n\tint i;\n\tfor (i = 0; i < argcount; i++)\n\t{\n\t\tmemset(&arg[i], 0, sizeof(arg[i]));\n\t\targ[i].type = REF_GLOBAL;\n\t\targ[i].base = arglist[i];\n\t\targ[i].cast = arglist[i].cast;\n\t\toutlist[i] = &arg[i];\n\t}\n\treturn QCC_PR_GenerateFunctionCallRef(newself, func, outlist, argcount);\n}\nQCC_sref_t QCC_PR_GenerateFunctionCall3 (QCC_sref_t newself, QCC_sref_t func, QCC_sref_t a, QCC_type_t *type_a, QCC_sref_t b, QCC_type_t *type_b, QCC_sref_t c, QCC_type_t *type_c)\n{\n\tQCC_ref_t arg_a = {REF_GLOBAL};\n\tQCC_ref_t arg_b = {REF_GLOBAL};\n\tQCC_ref_t arg_c = {REF_GLOBAL};\n\tQCC_ref_t *arglist[3] = {&arg_a, &arg_b, &arg_c};\n\targ_a.base = a;\n\targ_a.cast = type_a?type_a:a.cast;\n\targ_b.base = b;\n\targ_b.cast = type_b?type_b:b.cast;\n\targ_c.base = c;\n\targ_c.cast = type_c?type_c:c.cast;\n\treturn QCC_PR_GenerateFunctionCallRef(newself, func, arglist, 3);\n}\nQCC_sref_t QCC_PR_GenerateFunctionCall2 (QCC_sref_t newself, QCC_sref_t func, QCC_sref_t a, QCC_type_t *type_a, QCC_sref_t b, QCC_type_t *type_b)\n{\n\tQCC_ref_t arg_a = {REF_GLOBAL};\n\tQCC_ref_t arg_b = {REF_GLOBAL};\n\tQCC_ref_t *arglist[2] = {&arg_a, &arg_b};\n\targ_a.base = a;\n\targ_a.cast = type_a?type_a:a.cast;\n\targ_b.base = b;\n\targ_b.cast = type_b?type_b:b.cast;\n\treturn QCC_PR_GenerateFunctionCallRef(newself, func, arglist, 2);\n}\nQCC_sref_t QCC_PR_GenerateFunctionCall1 (QCC_sref_t newself, QCC_sref_t func, QCC_sref_t a, QCC_type_t *type_a)\n{\n\tQCC_ref_t arg_a = {REF_GLOBAL};\n\tQCC_ref_t *arglist[1] = {&arg_a};\n\targ_a.base = a;\n\targ_a.cast = type_a?type_a:a.cast;\n\treturn QCC_PR_GenerateFunctionCallRef(newself, func, arglist, 1);\n}\n\n\n/*\n============\nPR_ParseFunctionCall\n============\n*/\nstatic QCC_sref_t QCC_PR_ParseFunctionCall (QCC_ref_t *funcref)\t//warning, the func could have no name set if it's a field call.\n{\n\tQCC_sref_t\tnewself, func;\n\tQCC_sref_t\te, d, out;\n\tunsigned int\t\t\targ;\n\tQCC_type_t\t\t*t, *p;\n\tint extraparms=false;\n\tunsigned int np;\n\tconst char *funcname, *value;\n\n\tQCC_ref_t *param[MAX_PARMS+MAX_EXTRA_PARMS];\n\tQCC_ref_t parambuf[MAX_PARMS+MAX_EXTRA_PARMS];\n\n\tif (funcref->type == REF_FIELD && strstr(QCC_GetSRefName(funcref->index), \"::\"))\n\t{\n\t\tnewself = funcref->base;\n\t\tQCC_UnFreeTemp(newself);\n\t\tfunc = QCC_RefToDef(funcref, true);\n\t}\n\telse if (funcref->type == REF_NONVIRTUAL)\n\t{\n\t\tnewself = funcref->index;\n\t\tQCC_UnFreeTemp(newself);\n\t\tfunc = QCC_RefToDef(funcref, true);\n\t}\n\telse\n\t{\n\t\tnewself = nullsref;\n\t\tfunc = QCC_RefToDef(funcref, true);\n\t}\n\n\tfunc.sym->timescalled++;\n\n\tt = func.cast;\n\n\tif (t->type == ev_variant)\n\t{\n\t\tt->aux_type = type_variant;\n\t}\n\n\tif (t->type != ev_function && t->type != ev_variant)\n\t{\n\t\tQCC_PR_ParseErrorPrintSRef (ERR_NOTAFUNCTION, func, \"not a function\");\n\t}\n\n\tfuncname = QCC_GetSRefName(func);\n\tif (!newself.cast && !t->num_parms&&t->type != ev_variant)\t//intrinsics. These base functions have variable arguments. I would check for (...) args too, but that might be used for extended builtin functionality. (this code wouldn't compile otherwise)\n\t{\n\t\tif (!strcmp(funcname, \"alloca\"))\n\t\t{\t//FIXME: half of these functions with known arguments should be handled later or something\n\t\t\tQCC_sref_t sz, ret;\n\n\t\t\tfunc.sym->unused = true;\n\t\t\tfunc.sym->referenced = true;\n\t\t\tQCC_FreeTemp(func);\n\n\t\t\tsz = QCC_PR_Expression(TOP_PRIORITY, EXPR_DISALLOW_COMMA);\n\t\t\tQCC_PR_Expect(\")\");\n\t\t\tsz = QCC_SupplyConversion(sz, ev_integer, true);\n\t\t\t//result = push_words((sz+3)/4);\n\t\t\tsz = QCC_PR_Statement(&pr_opcodes[OP_ADD_I], sz, QCC_MakeIntConst(3), NULL);\n\t\t\tsz = QCC_PR_Statement(&pr_opcodes[OP_DIV_I], sz, QCC_MakeIntConst(4), NULL);\n\t\t\tQCC_FreeTemp(sz);\n\t\t\tret = QCC_GetTemp(QCC_PointerTypeTo(type_void));\n\t\t\tQCC_PR_SimpleStatement(&pr_opcodes[OP_PUSH], sz, nullsref, ret, false);\t//push *(int*)&a elements\n\t\t\treturn ret;\n\t\t}\n\t\tif (!strcmp(funcname, \"_\"))\n\t\t{\n\t\t\tchar *comment = pr_token_precomment;\n\t\t\tfunc.sym->unused = true;\n\t\t\tfunc.sym->referenced = true;\n\t\t\tQCC_FreeTemp(func);\n\t\t\tif (pr_token_type == tt_immediate && pr_immediate_type->type == ev_string)\n\t\t\t{\n\t\t\t\td = QCC_MakeTranslateStringConst(pr_immediate_string);\n\n\t\t\t\td.sym->comment = comment;\n\t\t\t\tQCC_PR_Lex();\n\t\t\t\tif (!d.sym->comment)\n\t\t\t\t\td.sym->comment = pr_token_precomment;\n\t\t\t\tif (QCC_PR_CheckTokenComment (\")\", &d.sym->comment))\n\t\t\t\t\treturn d;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tQCC_PR_ParseErrorPrintSRef (ERR_TYPEMISMATCHPARM, func, \"_() intrinsic accepts only a string immediate\");\n\t\t\t\td = nullsref;\n\n\t\t\t}\n\t\t\tQCC_PR_Expect(\")\");\n\t\t\treturn d;\n\t\t}\n\n\t\tif (!strcmp(funcname, \"va_arg\") || !strcmp(funcname, \"...\"))\t//second for compat with gmqcc\n\t\t{\n\t\t\tQCC_sref_t va_list;\n\t\t\tQCC_sref_t idx;\n\t\t\tQCC_type_t *type;\n\t\t\tva_list = QCC_PR_GetSRef(type_vector, \"__va_list\", pr_scope, false, 0, 0);\n\t\t\tidx = QCC_PR_Expression (TOP_PRIORITY, EXPR_DISALLOW_COMMA);\n\t\t\tif (idx.cast->type == ev_float)\n\t\t\t\tidx = QCC_PR_Statement(&pr_opcodes[OP_MUL_F], idx, QCC_MakeFloatConst(3), NULL);\n\t\t\telse\n\t\t\t\tidx = QCC_PR_Statement(&pr_opcodes[OP_MUL_I], QCC_SupplyConversion(idx, ev_integer, true), QCC_MakeIntConst(3), NULL);\n\t\t\tQCC_PR_Expect(\",\");\n\t\t\ttype = QCC_PR_ParseType(false, false, false);\n\t\t\tQCC_PR_Expect(\")\");\n\t\t\tif (!va_list.cast || !va_list.sym || !va_list.sym->arraysize)\n\t\t\t\tQCC_PR_ParseError (ERR_TYPEMISMATCHPARM, \"va_arg() intrinsic only works inside varadic functions\");\n\n\t\t\tfunc.sym->unused = true;\n\t\t\tfunc.sym->referenced = true;\n\t\t\tQCC_FreeTemp(func);\n\t\t\treturn QCC_LoadFromArray(va_list, idx, type, false);\n\t\t}\n\t\tif (!strcmp(funcname, \"random\"))\n\t\t{\n\t\t\tfunc.sym->unused = true;\n\t\t\tfunc.sym->referenced = true;\n\t\t\tQCC_FreeTemp(func);\n\t\t\tif (!QCC_PR_CheckToken(\")\"))\n\t\t\t{\n\t\t\t\te = QCC_PR_Expression (TOP_PRIORITY, EXPR_DISALLOW_COMMA);\n\t\t\t\te = QCC_SupplyConversion(e, ev_float, true);\n\t\t\t\tif (e.cast->type != ev_float)\n\t\t\t\t\tQCC_PR_ParseErrorPrintSRef (ERR_TYPEMISMATCHPARM, func, \"type mismatch on parm %i\", 1);\n\t\t\t\tif (!QCC_PR_CheckToken(\")\"))\n\t\t\t\t{\n\t\t\t\t\tQCC_PR_Expect(\",\");\n\t\t\t\t\td = QCC_PR_Expression (TOP_PRIORITY, EXPR_DISALLOW_COMMA);\n\t\t\t\t\td = QCC_SupplyConversion(d, ev_float, true);\n\t\t\t\t\tif (d.cast->type != ev_float)\n\t\t\t\t\t\tQCC_PR_ParseErrorPrintSRef (ERR_TYPEMISMATCHPARM, func, \"type mismatch on parm %i\", 2);\n\t\t\t\t\tQCC_PR_Expect(\")\");\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\td = nullsref;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\te = nullsref;\n\t\t\t\td = nullsref;\n\t\t\t}\n\n\t\t\tif (QCC_OPCodeValid(&pr_opcodes[OP_RAND0]))\n\t\t\t{\n\t\t\t\tif(qcc_targetformat != QCF_HEXEN2 && qcc_targetformat != QCF_UHEXEN2)\n\t\t\t\t\tout = QCC_GetTemp(type_float);\n\t\t\t\telse\n\t\t\t\t{\t//hexen2 requires the output be def_ret\n\t\t\t\t\tQCC_ClobberDef(&def_ret);\n\t\t\t\t\tout = nullsref;\n\t\t\t\t}\n\t\t\t\tif (e.cast)\n\t\t\t\t{\n\t\t\t\t\tif (d.cast)\n\t\t\t\t\t{\n\t\t\t\t\t\tQCC_PR_SimpleStatement(&pr_opcodes[OP_RAND2], e, d, out, false);\n\t\t\t\t\t\tQCC_FreeTemp(d);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\tQCC_PR_SimpleStatement(&pr_opcodes[OP_RAND1], e, nullsref, out, false);\n\t\t\t\t\tQCC_FreeTemp(e);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tQCC_PR_SimpleStatement(&pr_opcodes[OP_RAND0], nullsref, nullsref, out, false);\n\t\t\t\tif (!out.cast)\n\t\t\t\t\tout = QCC_GetAliasTemp(QCC_MakeSRefForce(&def_ret, 0, type_float));\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tQCC_ClobberDef(&def_ret);\n\t\t\t\t//this is normally a builtin, so don't bother locking temps.\n\t\t\t\tQCC_PR_SimpleStatement(&pr_opcodes[OP_CALL0], func, nullsref, nullsref, false);\n\t\t\t\tout = QCC_GetAliasTemp(QCC_MakeSRefForce(&def_ret, 0, type_float));\n\t\t\t\tif (d.cast)\n\t\t\t\t{\n\t\t\t\t\tQCC_sref_t t;\n\t\t\t\t\t//min + (max-min)*random()\n\t\t\t\t\tt = QCC_PR_StatementFlags(&pr_opcodes[OP_SUB_F], d, e, NULL, STFL_PRESERVEB);\n\t\t\t\t\tout = QCC_PR_Statement(&pr_opcodes[OP_MUL_F], out, t, NULL);\n\t\t\t\t\tout = QCC_PR_Statement(&pr_opcodes[OP_ADD_F], out, e, NULL);\n\t\t\t\t}\n\t\t\t\telse if (e.cast)\n\t\t\t\t\tout = QCC_PR_Statement(&pr_opcodes[OP_MUL_F], out, e, NULL);\n\t\t\t}\n\t\t\treturn out;\n\t\t}\n\t\tif (!strcmp(funcname, \"randomv\"))\n\t\t{\n\t\t\tfunc.sym->unused = true;\n\t\t\tfunc.sym->referenced=true;\n\t\t\tQCC_FreeTemp(func);\n\t\t\tif (!QCC_PR_CheckToken(\")\"))\n\t\t\t{\n\t\t\t\te = QCC_PR_Expression (TOP_PRIORITY, EXPR_DISALLOW_COMMA);\n\t\t\t\tif (e.cast->type != ev_vector)\n\t\t\t\t\tQCC_PR_ParseErrorPrintSRef (ERR_TYPEMISMATCHPARM, func, \"type mismatch on parm %i\", 1);\n\t\t\t\tif (!QCC_PR_CheckToken(\")\"))\n\t\t\t\t{\n\t\t\t\t\tQCC_PR_Expect(\",\");\n\t\t\t\t\td = QCC_PR_Expression (TOP_PRIORITY, EXPR_DISALLOW_COMMA);\n\t\t\t\t\tif (d.cast->type != ev_vector)\n\t\t\t\t\t\tQCC_PR_ParseErrorPrintSRef (ERR_TYPEMISMATCHPARM, func, \"type mismatch on parm %i\", 2);\n\t\t\t\t\tQCC_PR_Expect(\")\");\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\td = nullsref;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\te = nullsref;\n\t\t\t\td = nullsref;\n\t\t\t}\n\n\t\t\tif (QCC_OPCodeValid(&pr_opcodes[OP_RANDV0]))\n\t\t\t{\n\t\t\t\tif(qcc_targetformat != QCF_HEXEN2 && qcc_targetformat != QCF_UHEXEN2)\n\t\t\t\t\tout = QCC_GetTemp(type_vector);\n\t\t\t\telse\n\t\t\t\t{\t//hexen2 requires the output be def_ret\n\t\t\t\t\tQCC_ClobberDef(&def_ret);\n\t\t\t\t\tout = nullsref;\n\t\t\t\t}\n\t\t\t\tif (e.cast)\n\t\t\t\t{\n\t\t\t\t\tif (d.cast)\n\t\t\t\t\t{\n\t\t\t\t\t\tQCC_PR_SimpleStatement(&pr_opcodes[OP_RANDV2], e, d, out, false);\n\t\t\t\t\t\tQCC_FreeTemp(d);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\tQCC_PR_SimpleStatement(&pr_opcodes[OP_RANDV1], e, nullsref, out, false);\n\t\t\t\t\tQCC_FreeTemp(e);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tQCC_PR_SimpleStatement(&pr_opcodes[OP_RANDV0], nullsref, nullsref, out, false);\n\t\t\t\tif (!out.cast)\n\t\t\t\t\tout = QCC_GetAliasTemp(QCC_MakeSRefForce(&def_ret, 0, type_vector));\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tQCC_sref_t x,y,z;\n\t\t\t\tQCC_sref_t min = nullsref;\n\t\t\t\tQCC_sref_t scale = nullsref;\n\t\t\t\tif (d.cast)\n\t\t\t\t{\n\t\t\t\t\tmin = e;\n\t\t\t\t\tscale = QCC_PR_StatementFlags(&pr_opcodes[OP_SUB_V], d, min, NULL, STFL_PRESERVEB);\n\t\t\t\t}\n\t\t\t\telse if (e.cast)\n\t\t\t\t\tscale = e;\n\t\t\t\tQCC_ClobberDef(&def_ret);\n\t\t\t\tout = QCC_GetAliasTemp(QCC_MakeSRefForce(&def_ret, 0, type_vector));\n\t\t\t\tx = out;\n\t\t\t\tx.cast = type_float;\n\t\t\t\ty = x;\n\t\t\t\ty.ofs += 1;\n\t\t\t\tz = y;\n\t\t\t\tz.ofs += 1;\n\t\t\t\tQCC_PR_SimpleStatement(&pr_opcodes[OP_CALL0], func, nullsref, nullsref, false);\n\t\t\t\tif (scale.cast)\n\t\t\t\t{\n\t\t\t\t\tscale.cast = type_float;\n\t\t\t\t\tscale.ofs += 2;\n\t\t\t\t\tQCC_PR_SimpleStatement(&pr_opcodes[OP_MUL_F], x, scale, z, false);\n\t\t\t\t\tscale.ofs--;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tQCC_PR_SimpleStatement(&pr_opcodes[OP_STORE_F], x, z, nullsref, false);\n\t\t\t\tQCC_PR_SimpleStatement(&pr_opcodes[OP_CALL0], func, nullsref, nullsref, false);\n\t\t\t\tif (scale.cast)\n\t\t\t\t{\n\t\t\t\t\tQCC_PR_SimpleStatement(&pr_opcodes[OP_MUL_F], x, scale, y, false);\n\t\t\t\t\tscale.ofs--;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tQCC_PR_SimpleStatement(&pr_opcodes[OP_STORE_F], x, y, nullsref, false);\n\t\t\t\tQCC_PR_SimpleStatement(&pr_opcodes[OP_CALL0], func, nullsref, nullsref, false);\n\t\t\t\tif (scale.cast)\n\t\t\t\t{\n\t\t\t\t\tQCC_PR_SimpleStatement(&pr_opcodes[OP_MUL_F], x, scale, x, false);\n\t\t\t\t\tscale.sym->referenced = true;\n\t\t\t\t\tscale.ofs--;\n\t\t\t\t\tscale.cast = type_vector;\n\t\t\t\t\tQCC_FreeTemp(scale);\n\t\t\t\t}\n\t\t\t\tif (min.cast)\n\t\t\t\t\tout = QCC_PR_Statement(&pr_opcodes[OP_ADD_V], out, min, NULL);\n\t\t\t}\n\n\t\t\treturn out;\n\t\t}\n\t\telse if (!strcmp(funcname, \"spawn\"))\n\t\t{\n\t\t\t//foo_c e = spawn(foo_c, fld:val, fld:val);\t//regular spawn...\n\t\t\t//foo_c e = spawn(existingent, foo_c, fld:val, fld:val);\t//placement-spawn...\n\t\t\tQCC_sref_t result = nullsref;\n\t\t\tQCC_type_t *rettype;\n\t\t\t\n\t\t\t/*\n\t\t\tret = spawn();\n\t\t\tret.FOO* = FOO*;\n\t\t\tresult.(classcall)spawnfunc_foo();\n\t\t\treturn result;\n\t\t\tthis mechanism means entities can be spawned easily via maps.\n\t\t\t*/\n\n\t\t\tif (!QCC_PR_CheckToken(\")\"))\n\t\t\t{\n\t\t\t\trettype = QCC_PR_ParseType(false, true, false);\n\t\t\t\tif (!rettype)\n\t\t\t\t{\n\t\t\t\t\tresult = QCC_PR_Expression(TOP_PRIORITY, EXPR_DISALLOW_COMMA);\n\t\t\t\t\tQCC_PR_Expect(\",\");\n\t\t\t\t\trettype = QCC_PR_ParseType(false, true, false);\n\t\t\t\t}\n\t\t\t\t//FIXME: C++'s Placement New syntax: obj *p= new(ptr) obj();\n\t\t\t\tif (!rettype || rettype->type != ev_entity)\n\t\t\t\t\tQCC_PR_ParseError(ERR_NOTANAME, \"Spawn operator with undefined class: %s\", QCC_PR_ParseName());\n\t\t\t}\n\t\t\telse\n\t\t\t\trettype = NULL;\t//default, corrected to entity later\n\n\t\t\tif (!result.cast)\n\t\t\t{\n\t\t\t\t//ret = spawn()\n\t\t\t\tresult = QCC_PR_GenerateFunctionCallRef(nullsref, func, NULL, 0);\n\t\t\t}\n\n\t\t\tif (rettype)\n\t\t\t{\n\t\t\t\tchar genfunc[256];\n\t\t\t\t//do field assignments.\n\t\t\t\twhile(QCC_PR_CheckToken(\",\"))\n\t\t\t\t{\n\t\t\t\t\tQCC_sref_t f, p, v;\n\t\t\t\t\tf = QCC_PR_ParseValue(rettype, false, false, true);\n\t\t\t\t\tif (f.cast->type != ev_field)\n\t\t\t\t\t\tQCC_PR_ParseError(0, \"Named field is not a field.\");\n\t\t\t\t\tif (QCC_PR_CheckToken(\"=\"))\t\t\t\t\t\t\t//allow : or = as a separator, but throw a warning for =\n\t\t\t\t\t\tQCC_PR_ParseWarning(0, \"That = should be a :\");\t//rejecting = helps avoid qcc bugs. :P\n\t\t\t\t\telse\n\t\t\t\t\t\tQCC_PR_Expect(\":\");\n\t\t\t\t\tv = QCC_PR_Expression(TOP_PRIORITY, EXPR_DISALLOW_COMMA);\n\n\t\t\t\t\tp = QCC_PR_StatementFlags(&pr_opcodes[OP_ADDRESS], result, f, NULL, STFL_PRESERVEA);\n\t\t\t\t\tif (v.cast->size == 3)\n\t\t\t\t\t\tQCC_FreeTemp(QCC_PR_Statement(&pr_opcodes[OP_STOREP_V], v, p, NULL));\n\t\t\t\t\telse\n\t\t\t\t\t\tQCC_FreeTemp(QCC_PR_Statement(&pr_opcodes[OP_STOREP_F], v, p, NULL));\n\t\t\t\t}\n\t\t\t\tQCC_PR_Expect(\")\");\n\n\t\t\t\tQC_snprintfz(genfunc, sizeof(genfunc), \"spawnfunc_%s\", rettype->name);\n\t\t\t\tfunc = QCC_PR_GetSRef(type_function, genfunc, NULL, true, 0, GDF_CONST);\n\t\t\t\tfunc.sym->referenced = true;\n\n\t\t\t\tQCC_UnFreeTemp(result);\n\t\t\t\tQCC_FreeTemp(QCC_PR_GenerateFunctionCallRef(result, func, NULL, 0));\n\t\t\t\tresult.cast = rettype;\n\t\t\t}\n\n\t\t\treturn result;\n\t\t}\n\t\telse if (!strcmp(funcname, \"used_sound\"))\n\t\t{\n\t\t\te = QCC_PR_Expression(TOP_PRIORITY, EXPR_DISALLOW_COMMA);\n\t\t\tQCC_PR_Expect(\")\");\n\t\t\tif ((value=QCC_SRef_EvalStringConst(e)))\n\t\t\t\tQCC_SoundUsed(value);\n\t\t\telse\n\t\t\t\tQCC_PR_ParseWarning(ERR_BADIMMEDIATETYPE, \"Argument to used_sound intrinsic was not a string immediate.\");\n\t\t\treturn e;\n\t\t}\n\t\telse if (!strcmp(funcname, \"used_model\"))\n\t\t{\n\t\t\te = QCC_PR_Expression(TOP_PRIORITY, EXPR_DISALLOW_COMMA);\n\t\t\tQCC_PR_Expect(\")\");\n\t\t\tif ((value=QCC_SRef_EvalStringConst(e)))\n\t\t\t\tQCC_SetModel(value);\n\t\t\telse\n\t\t\t\tQCC_PR_ParseWarning(ERR_BADIMMEDIATETYPE, \"Argument to used_model intrinsic was not a string immediate.\");\n\t\t\treturn e;\n\t\t}\n\t\telse if (!strcmp(funcname, \"autocvar\") && !QCC_PR_CheckToken(\")\"))\n\t\t{\n\t\t\tchar autocvarname[256];\n\t\t\tchar *desc = NULL;\n\t\t\tQCC_FreeTemp(func);\n\t\t\tQC_snprintfz(autocvarname, sizeof(autocvarname), \"autocvar_%s\", QCC_PR_ParseName());\n\t\t\tQCC_PR_Expect(\",\");\n\t\t\te = QCC_PR_Expression(TOP_PRIORITY, EXPR_DISALLOW_COMMA);\n\t\t\tif (QCC_PR_CheckToken(\",\"))\n\t\t\t{\n\t\t\t\tif (pr_token_type == tt_immediate && pr_immediate_type->type == ev_string)\n\t\t\t\t{\t//okay, its a string immediate, consume it for the description, being careful to not generate any string immediate defs (which still require an entry in the string table).\n\t\t\t\t\tdesc = qccHunkAlloc(strlen(pr_immediate_string)+1);\n\t\t\t\t\tstrcpy(desc, pr_immediate_string);\n\t\t\t\t\tQCC_PR_Lex ();\n\t\t\t\t}\n\t\t\t}\n\t\t\tQCC_PR_Expect(\")\");\n\t\t\td = QCC_PR_GetSRef(e.cast, autocvarname, NULL, true, 0, GDF_USED);\n\t\t\tif (!d.sym->comment)\n\t\t\t\td.sym->comment = desc;\n\t\t\tif (!e.sym->constant)\n\t\t\t\tQCC_PR_ParseWarning(ERR_BADIMMEDIATETYPE, \"autocvar default value is not constant\");\n\t\t\t\n\t\t\tif (d.sym->initialized)\n\t\t\t{\n\t\t\t\tif (memcmp(QCC_SRef_Data(d), QCC_SRef_Data(e), d.sym->symbolsize*sizeof(int)))\n\t\t\t\t\tQCC_PR_ParseErrorPrintSRef (ERR_REDECLARATION, d, \"autocvar %s was already initialised with another value\", autocvarname+9);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tmemcpy(QCC_SRef_Data(d), QCC_SRef_Data(e), d.sym->symbolsize*sizeof(int));\n\t\t\t\td.sym->initialized = true;\n\t\t\t}\n\t\t\tQCC_FreeTemp(e);\n\t\t\treturn d;\n\t\t}\n\t\telse if (!strcmp(funcname, \"entnum\") && !QCC_PR_CheckToken(\")\"))\n\t\t{\n\t\t\t//t = (a/%1) / (nextent(world)/%1)\n\t\t\t//a/%1 does a (int)entity to float conversion type thing\n\t\t\tfunc.sym->unused = true;\n\t\t\tfunc.sym->referenced = true;\n\t\t\tQCC_FreeTemp(func);\n\n\t\t\te = QCC_PR_Expression(TOP_PRIORITY, EXPR_DISALLOW_COMMA);\n\t\t\tQCC_PR_Expect(\")\");\n\t\t\te = QCC_PR_StatementFlags(&pr_opcodes[OP_DIV_I], e, QCC_MakeIntConst(1), NULL, 0);\n\t\t\td = QCC_PR_EmulationFunc(nextent);\n\t\t\tif (!d.cast)\n\t\t\t\tQCC_PR_ParseError(0, \"the nextent builtin is not defined\");\n\t\t\tQCC_UnFreeTemp(e);\n\t\t\td = QCC_PR_GenerateFunctionCall1 (nullsref, d, e, type_entity);\n\t\t\td = QCC_PR_StatementFlags(&pr_opcodes[OP_DIV_I], d, QCC_MakeIntConst(1), NULL, 0);\n\t\t\te = QCC_PR_StatementFlags(&pr_opcodes[OP_DIV_I], e, d, NULL, 0);\n\n\t\t\treturn e;\n\t\t}\n\t}\t//so it's not an intrinsic.\n\telse if (!newself.cast && t->num_parms == 1 && t->type == ev_function)\n\t{\n\t\tif (!strcmp(funcname, \"checkbuiltin\"))\n\t\t{\n\t\t\tpr_ignoredeprecation = true;\n\t\t\tparam[0] = QCC_PR_RefExpression (&parambuf[0], TOP_PRIORITY, EXPR_DISALLOW_COMMA);\n\t\t\tpr_ignoredeprecation = false;\n\t\t\tQCC_PR_Expect(\")\");\n\n\t\t\tif (param[0]->type == REF_GLOBAL && param[0]->cast == param[0]->base.sym->type && !param[0]->base.sym->arraysize)\n\t\t\t{\n\t\t\t\te.ofs = param[0]->base.ofs;\n\t\t\t\te.cast = param[0]->cast;\n\t\t\t\te.sym = QCC_PR_DummyDef(e.cast, param[0]->base.sym->name, pr_scope, 0, param[0]->base.sym, 0, true, GDF_ALIAS|GDF_STRIP);\n\t\t\t\te = QCC_PR_GenerateFunctionCallSref(newself, func, &e, 1);\n\t\t\t}\n\t\t\telse\n\t\t\t\te = QCC_PR_GenerateFunctionCallRef(newself, func, param, 1);\n\t\t\treturn e;\n\t\t}\n\t}\n\n\tif (opt_precache_file)\t//should we strip out all precache_file calls?\n\t{\n\t\tif (!newself.cast && !strncmp(funcname,\"precache_file\", 13))\n\t\t{\n\t\t\tif (pr_token_type == tt_immediate && pr_immediate_type->type == ev_string && pr_scope && !strcmp(pr_scope->name, \"main\"))\n\t\t\t{\n\t\t\t\toptres_precache_file += strlen(pr_immediate_string);\n\t\t\t\tQCC_PR_Lex();\n\t\t\t\tQCC_PR_Expect(\")\");\n\t\t\t\tQCC_PrecacheFile (pr_immediate_string, funcname[13]);\n\t\t\t\tQCC_FreeTemp(func);\n\t\t\t\tQCC_FreeTemp(newself);\n\t\t\t\treturn QCC_MakeFloatConst(0);\n\t\t\t}\n\t\t}\n\t}\n\n// copy the arguments to the global parameter variables\n\targ = 0;\n\tif (t->type == ev_variant)\n\t{\n\t\textraparms = true;\n\t\tnp = 0;\n\t}\n\telse if (t->vargs)\n\t{\n\t\textraparms = true;\n\t\tnp = t->num_parms;\n\t}\n\telse\n\t\tnp = t->num_parms;\n\n\t//any temps referenced to build the parameters don't need to be locked.\n\tif (!QCC_PR_CheckToken(\")\"))\n\t{\n\t\tQCC_ref_t *e;\n\t\tdo\n\t\t{\n\t\t\tif (arg >= t->num_parms)\n\t\t\t\tp = NULL;\n\t\t\telse\n\t\t\t\tp = t->params[arg].type;\n\n\t\t\tif (arg >= MAX_PARMS+MAX_EXTRA_PARMS)\n\t\t\t\tQCC_PR_ParseErrorPrintSRef (ERR_TOOMANYTOTALPARAMETERS, func, \"More than %i parameters\", MAX_PARMS+MAX_EXTRA_PARMS);\n\n\t\t\tif (QCC_PR_CheckToken(\"#\"))\n\t\t\t{\n\t\t\t\tQCC_sref_t sr = QCC_MakeSRefForce(&def_parms[arg], 0, p?p:type_variant);\n//\t\t\t\tsr.sym = &def_parms[arg];\n//\t\t\t\tsr.ofs = 0;\n//\t\t\t\tsr.cast = p?p:type_variant;\n\t\t\t\te = QCC_PR_BuildRef(&parambuf[arg], REF_GLOBAL, sr, nullsref, p?p:type_variant, true, 0);\n\t\t\t}\n\t\t\telse if (arg < t->num_parms && (QCC_PR_PeekToken (\",\") || QCC_PR_PeekToken (\")\")))\n\t\t\t{\n\t\t\t\tif (!func.cast->params[arg].defltvalue.cast)\n\t\t\t\t\tQCC_PR_ParseErrorPrintSRef (ERR_NOTDEFINED, func, \"Default value not specified for implicit argument %i\", arg+1);\n\t\t\t\te = QCC_DefToRef(&parambuf[arg], func.cast->params[arg].defltvalue);\n\t\t\t}\n\t\t\telse\n\t\t\t\te = QCC_PR_RefExpression(&parambuf[arg], TOP_PRIORITY, EXPR_DISALLOW_COMMA);\n\n\t\t\tif (extraparms && arg >= MAX_PARMS && !t->vargcount)\n\t\t\t{\n\t\t\t\t//vararg builtins cannot accept more than 8 args. they can't tell if they got more, and wouldn't know where to read them.\n\t\t\t\tQCC_PR_ParseWarning (WARN_TOOMANYPARAMETERSVARARGS, \"More than %i parameters on varargs function\", MAX_PARMS);\n\t\t\t\tQCC_PR_ParsePrintSRef(WARN_TOOMANYPARAMETERSVARARGS, func);\n\t\t\t}\n\t\t\telse if (!extraparms && arg >= t->num_parms && !p)\n\t\t\t{\n\t\t\t\tchar buf[256];\n\t\t\t\tQCC_PR_ParseWarning (WARN_TOOMANYPARAMETERSFORFUNC, \"too many parameters on call to %s, argument %s will be ignored\", funcname, QCC_GetRefName(e, buf, sizeof(buf)));\n\t\t\t\tQCC_PR_ParsePrintSRef(WARN_TOOMANYPARAMETERSFORFUNC, func);\n\t\t\t}\n\n\t\t\t//with vectorcalls, we store the vector into the args as individual floats\n\t\t\t//this allows better reuse of vector constants.\n\t\t\t//the immediate vector def will be discarded while linking, if its still unused.\n\t\t\tif (opt_vectorcalls && e->cast == type_vector && e->type == REF_GLOBAL && !e->postinc && e->readonly)\n\t\t\t{\n\t\t\t\tconst QCC_eval_t *eval = QCC_SRef_EvalConst(e->base);\n\t\t\t\tif (eval)\n\t\t\t\t{\n\t\t\t\t\tQCC_sref_t t = QCC_GetTemp(type_vector);\n\t\t\t\t\tt.cast = type_float;\n\t\t\t\t\tt.ofs = 0;\n\t\t\t\t\tQCC_FreeTemp(QCC_PR_StatementFlags (&pr_opcodes[OP_STORE_F], QCC_MakeFloatConst(eval->vector[0]), t, NULL, STFL_PRESERVEB));\n\t\t\t\t\tt.ofs = 1;\n\t\t\t\t\tQCC_FreeTemp(QCC_PR_StatementFlags (&pr_opcodes[OP_STORE_F], QCC_MakeFloatConst(eval->vector[1]), t, NULL, STFL_PRESERVEB));\n\t\t\t\t\tt.ofs = 2;\n\t\t\t\t\tQCC_FreeTemp(QCC_PR_StatementFlags (&pr_opcodes[OP_STORE_F], QCC_MakeFloatConst(eval->vector[2]), t, NULL, STFL_PRESERVEB));\n\n\t\t\t\t\tt.ofs = 0;\n\t\t\t\t\tQCC_FreeTemp(e->base);\n\n\t\t\t\t\te = QCC_PR_BuildRef(&parambuf[arg], REF_GLOBAL, t, nullsref, type_vector, true, 0);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (!p && e->cast->type == ev_float && t->vargtodouble)\n\t\t\t\te = QCC_PR_BuildRef(&parambuf[arg], REF_GLOBAL, QCC_EvaluateCast(QCC_RefToDef(e, true), type_double, true), nullsref, p, true, 0);//C promotes floats to double on variadic functions, for some reason.\n\t\t\telse if (p && typecmp(e->cast, p))\n\t\t\t{\n\t\t\t\te = QCC_PR_BuildRef(&parambuf[arg], REF_GLOBAL, QCC_EvaluateCast(QCC_RefToDef(e, true), p, true), nullsref, p, true, 0);\n\t\t\t}\n\t\t\telse if (QCC_RefNeedsCalls(e))\n\t\t\t{\n\t\t\t\te = QCC_PR_BuildRef(&parambuf[arg], REF_GLOBAL, QCC_RefToDef(e, true), nullsref, p, true, 0);\n\t\t\t}\n\t\t\tparam[arg] = e;\n\n\t\t\tif (arg == 0)\n\t\t\t{\n\t\t\t// save information for model and sound caching\n\t\t\t\tif (!strncmp(funcname,\"precache_\", 9) && e->cast->type == ev_string && e->base.cast->type == ev_string && e->type == REF_GLOBAL)\n\t\t\t\t{\n\t\t\t\t\tconst QCC_eval_t *eval = QCC_SRef_EvalConst(e->base);\n\t\t\t\t\tif (eval)\n\t\t\t\t\t{\n\t\t\t\t\t\tconst char *value = &strings[eval->string];\n\t\t\t\t\t\tif (!strncmp(funcname+9,\"sound\", 5))\n\t\t\t\t\t\t\tQCC_PrecacheSound (value, funcname[14]);\n\t\t\t\t\t\telse if (!strncmp(funcname+9,\"model\", 5))\n\t\t\t\t\t\t\tQCC_PrecacheModel (value, funcname[14]);\n\t\t\t\t\t\telse if (!strncmp(funcname+9,\"texture\", 7))\n\t\t\t\t\t\t\tQCC_PrecacheTexture (value, funcname[16]);\n\t\t\t\t\t\telse if (!strncmp(funcname+9,\"file\", 4))\n\t\t\t\t\t\t\tQCC_PrecacheFile (value, funcname[13]);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (arg == 1 && e->cast->type == ev_string && e->type == REF_GLOBAL && !STRCMP(funcname, \"setmodel\") )\n\t\t\t{\n\t\t\t\tconst QCC_eval_t *eval = QCC_SRef_EvalConst(e->base);\n\t\t\t\tif (eval)\n\t\t\t\t{\n\t\t\t\t\tconst char *value = &strings[eval->string];\n\t\t\t\t\tQCC_SetModel(value);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (arg == 1 && e->cast->type == ev_string && e->type == REF_GLOBAL && !STRCMP(funcname, \"localsound\") )\n\t\t\t{\n\t\t\t\tconst QCC_eval_t *eval = QCC_SRef_EvalConst(e->base);\n\t\t\t\tif (eval)\n\t\t\t\t{\n\t\t\t\t\tconst char *value = &strings[eval->string];\n\t\t\t\t\tQCC_SoundUsed(value);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (arg == 2 && e->cast->type == ev_string && e->type == REF_GLOBAL && !STRCMP(funcname, \"sound\"))\n\t\t\t{\n\t\t\t\tconst QCC_eval_t *eval = QCC_SRef_EvalConst(e->base);\n\t\t\t\tif (eval)\n\t\t\t\t{\n\t\t\t\t\tconst char *value = &strings[eval->string];\n\t\t\t\t\tQCC_SoundUsed(value);\n\t\t\t\t}\n\t\t\t}\n\t\t\targ++;\n\t\t} while (QCC_PR_CheckToken (\",\"));\n\t\tQCC_PR_Expect (\")\");\n\t}\n\n\t//don't warn if we omited optional arguments\n\twhile (arg < np && func.cast->params[arg].defltvalue.cast && !func.cast->params[arg].optional)\n\t{\n\t\tQCC_ForceUnFreeDef(func.cast->params[arg].defltvalue.sym);\n\t\tparam[arg] = QCC_DefToRef(&parambuf[arg], func.cast->params[arg].defltvalue);\n\t\targ++;\n\t}\n\tif (arg < np && func.cast->params[arg].optional)\n\t\tnp = arg;\n\tif (arg < np)\n\t{\n\t\t/*if (arg+1==np && !strcmp(QCC_GetSRefName(func), \"makestatic\"))\n\t\t{\n\t\t\t//vanilla QC sucks. I want fteextensions.qc to compile with vanilla, yet also result in errors for when the mod fucks up.\n\t\t\tQCC_PR_ParseWarning (WARN_COMPATIBILITYHACK, \"too few parameters on call to %s. Passing 'self'.\", QCC_GetSRefName(func));\n\t\t\tQCC_PR_ParsePrintSRef (WARN_COMPATIBILITYHACK, func);\n\n\t\t\tparam[arg] = QCC_PR_GetSRef(NULL, \"self\", NULL, 0, 0, false);\n\t\t\targ++;\n\t\t}\n\t\telse if (arg+1==np && !strcmp(QCC_GetSRefName(func), \"ai_charge\"))\n\t\t{\n\t\t\t//vanilla QC sucks. I want fteextensions.qc to compile with vanilla, yet also result in errors for when the mod fucks up.\n\t\t\tQCC_PR_ParseWarning (WARN_COMPATIBILITYHACK, \"too few parameters on call to %s. Passing 0.\", QCC_GetSRefName(func));\n\t\t\tQCC_PR_ParsePrintSRef (WARN_COMPATIBILITYHACK, func);\n\n\t\t\tparam[arg] = QCC_MakeFloatConst(0);\n\t\t\targ++;\n\t\t}\n\t\telse*/\n\t\t{\n\t\t\tif (func.cast->params[arg].paramname)\n\t\t\t\tQCC_PR_ParseWarning (WARN_TOOFEWPARAMS, \"too few parameters on call to %s, %s will be UNDEFINED\", QCC_GetSRefName(func), func.cast->params[arg].paramname);\n\t\t\telse\n\t\t\t\tQCC_PR_ParseWarning (WARN_TOOFEWPARAMS, \"too few parameters on call to %s\", QCC_GetSRefName(func));\n\t\t\tQCC_PR_ParsePrintSRef (WARN_TOOFEWPARAMS, func);\n\t\t}\n\t}\n\n\treturn QCC_PR_GenerateFunctionCallRef(newself, func, param, arg);\n}\n\n//returns a usable sref_t, always increases the def's refcount even if the def is not live yet. should only be used when a term is created/named.\n//this special distinction allows temp reuse to be caught/debugged more reliably.\nQCC_sref_t QCC_MakeSRefForce(QCC_def_t *def, unsigned int ofs, QCC_type_t *type)\n{\n\tQCC_sref_t sr;\n\tsr.sym = def;\n\tsr.ofs = ofs;\n\tsr.cast = type;\n\tif (def)\n\t\tQCC_ForceUnFreeDef(def);\n\treturn sr;\n}\n//makes a sref from a def+ofs+type. also increases refcount. considered an error if the specified def is not currently live.\nQCC_sref_t QCC_MakeSRef(QCC_def_t *def, unsigned int ofs, QCC_type_t *type)\n{\n\tQCC_sref_t sr;\n\tsr.sym = def;\n\tsr.ofs = ofs;\n\tsr.cast = type;\n\tif (def)\n\t\tQCC_UnFreeTemp(sr);\n\treturn sr;\n}\n//int constchecks;\n//int varchecks;\n//int typechecks;\nextern hashtable_t floatconstdefstable;\nstatic QCC_sref_t QCC_Make32bitConst(QCC_type_t *type, puint_t value)\t//none of these types may be mutilated by the engine (allowing 0s to merge)\n{\n\tQCC_def_t\t*cn;\n\tunsigned int key = value;\n\n\tcn = Hash_GetKey(&floatconstdefstable, key);\n\twhile (cn)\n\t{\n\t\tif (cn->type->size == type->size)\n\t\t\tif (((QCC_eval_t*)cn->symboldata)->_uint == value)\n\t\t\t\treturn QCC_MakeSRefForce(cn, 0, type);\n\t\tcn = Hash_GetNextKey(&floatconstdefstable, key, cn);\n\t}\n\n// allocate a new one\n\tcn = (void *)qccHunkAlloc (sizeof(QCC_def_t) + sizeof(value));\n\tcn->next = NULL;\n\tpr.def_tail->next = cn;\n\tpr.def_tail = cn;\n\n\tcn->type = type;\n\tcn->name = \"IMMEDIATE\";\n\tcn->constant = true;\n\tcn->initialized = 1;\n\tcn->scope = NULL;\t\t// always share immediates\n\tcn->arraysize = 0;\n\tcn->referenced = true;\n\n\tcn->ofs = 0;\n\tcn->symbolheader = cn;\n\tcn->symbolsize = cn->type->size;\n\tcn->symboldata = (QCC_eval_basic_t*)(cn+1);\n\n\t((QCC_eval_t*)cn->symboldata)->_uint = value;\n\n\tHash_AddKey(&floatconstdefstable, key, cn, qccHunkAlloc(sizeof(bucket_t)));\n\n\treturn QCC_MakeSRefForce(cn, 0, type);\n}\nstatic QCC_sref_t QCC_Make64bitConst(QCC_type_t *type, puint64_t value)\t//all values MUST be word-swapped, for big-endian machines to byteswap their 64bit immediates, or something.\n{\n\tQCC_def_t\t*cn;\n\tunsigned int key = value ^ (value>>32);\n\n\tcn = Hash_GetKey(&floatconstdefstable, key);\n\twhile (cn)\n\t{\n\t\tif (cn->type->size == type->size)\n\t\t\tif (((QCC_eval_t*)cn->symboldata)->u64 == value)\n\t\t\t\treturn QCC_MakeSRefForce(cn, 0, type);\n\t\tcn = Hash_GetNextKey(&floatconstdefstable, key, cn);\n\t}\n\n// allocate a new one\n\tcn = (void *)qccHunkAlloc (sizeof(QCC_def_t) + sizeof(value));\n\tcn->next = NULL;\n\tpr.def_tail->next = cn;\n\tpr.def_tail = cn;\n\n\tcn->type = type;\n\tcn->name = \"IMMEDIATE\";\n\tcn->constant = true;\n\tcn->initialized = 1;\n\tcn->scope = NULL;\t\t// always share immediates\n\tcn->arraysize = 0;\n\n\tcn->ofs = 0;\n\tcn->symbolheader = cn;\n\tcn->symbolsize = cn->type->size;\n\tcn->symboldata = (QCC_eval_basic_t*)(cn+1);\n\n\t((QCC_eval_t*)cn->symboldata)->u64 = value;\n\n\tHash_AddKey(&floatconstdefstable, key, cn, qccHunkAlloc(sizeof(bucket_t)));\n\n\treturn QCC_MakeSRefForce(cn, 0, type);\n}\nstatic QCC_sref_t QCC_Make96bitConst(QCC_type_t *type, puint_t *value)\t//basically just vectors...\n{\n\tQCC_def_t\t*cn;\n\tunsigned int key = value[0] ^ value[1] ^ value[2];\n\n\tcn = Hash_GetKey(&floatconstdefstable, key);\n\twhile (cn)\n\t{\n\t\tif (cn->type->size == type->size)\n\t\t\tif (((puint_t*)cn->symboldata)[0] == value[0] &&\n\t\t\t\t((puint_t*)cn->symboldata)[1] == value[1] &&\n\t\t\t\t((puint_t*)cn->symboldata)[2] == value[2])\n\t\t\t\treturn QCC_MakeSRefForce(cn, 0, type);\n\t\tcn = Hash_GetNextKey(&floatconstdefstable, key, cn);\n\t}\n\n// allocate a new one\n\tcn = (void *)qccHunkAlloc (sizeof(QCC_def_t) + sizeof(*value)*3);\n\tcn->next = NULL;\n\tpr.def_tail->next = cn;\n\tpr.def_tail = cn;\n\n\tcn->type = type;\n\tcn->name = \"IMMEDIATE\";\n\tcn->constant = true;\n\tcn->initialized = 1;\n\tcn->scope = NULL;\t\t// always share immediates\n\tcn->arraysize = 0;\n\n\tcn->ofs = 0;\n\tcn->symbolheader = cn;\n\tcn->symbolsize = cn->type->size;\n\tcn->symboldata = (QCC_eval_basic_t*)(cn+1);\n\n\t((puint_t*)cn->symboldata)[0] = value[0];\n\t((puint_t*)cn->symboldata)[1] = value[1];\n\t((puint_t*)cn->symboldata)[2] = value[2];\n\n\tHash_AddKey(&floatconstdefstable, key, cn, qccHunkAlloc(sizeof(bucket_t)));\n\n\treturn QCC_MakeSRefForce(cn, 0, type);\n}\n\nQCC_sref_t QCC_MakeFloatConst(float value)\n{\n\tunion\n\t{\n\t\tfloat d;\n\t\tpuint_t i;\n\t} u = {value};\n\treturn QCC_Make32bitConst(type_float, u.i);\n}\nQCC_sref_t QCC_MakeIntConst(longlong llvalue)\n{\n\tpint_t value = llvalue;\n\tif (value != llvalue)\n\t\tQCC_PR_ParseWarning(WARN_OVERFLOW, \"Constant int operand %llu will be truncated to %i\", llvalue, value);\n\treturn QCC_Make32bitConst(type_integer, value);\n}\nQCC_sref_t QCC_MakeUIntConst(unsigned longlong llvalue)\n{\n\tpuint_t value = llvalue;\n\tif (value != llvalue)\n\t\tQCC_PR_ParseWarning(WARN_OVERFLOW, \"Constant int operand %llu will be truncated to %i\", llvalue, value);\n\treturn QCC_Make32bitConst(type_uint, value);\n}\nQCC_sref_t QCC_MakeInt64Const(longlong llvalue)\n{\n\tpint64_t value = llvalue;\n\tif (value != llvalue)\n\t\tQCC_PR_ParseWarning(WARN_OVERFLOW, \"Constant int operand %llu will be truncated to %\"pPRIi64, llvalue, value);\n\treturn QCC_Make64bitConst(type_int64, value);\n}\nQCC_sref_t QCC_MakeUInt64Const(unsigned longlong llvalue)\n{\n\tpuint64_t value = llvalue;\n\tif (value != llvalue)\n\t\tQCC_PR_ParseWarning(WARN_OVERFLOW, \"Constant int operand %llu will be truncated to %\"pPRIu64, llvalue, value);\n\treturn QCC_Make64bitConst(type_uint64, value);\n}\nQCC_sref_t QCC_MakeDoubleConst(double value)\n{\n\tunion\n\t{\n\t\tdouble d;\n\t\tpuint64_t i;\n\t} u = {value};\n\treturn QCC_Make64bitConst(type_double, u.i);\n}\n\n//immediates with no overlapping. this means aliases can be set up to mark them as strings/fields/functions and the engine can safely remap them as needed\nstatic QCC_sref_t QCC_MakeUniqueConst(QCC_type_t *type, void *data)\n{\n\tQCC_def_t\t*cn;\n\n// allocate a new one\n\tcn = (void *)qccHunkAlloc (sizeof(QCC_def_t) + sizeof(pint_t) * type->size);\n\tcn->next = NULL;\n\tpr.def_tail->next = cn;\n\tpr.def_tail = cn;\n\n\tcn->type = type;\n\tcn->name = \"IMMEDIATE\";\n\tcn->constant = true;\n\tcn->initialized = 1;\n\tcn->scope = NULL;\t\t// always share immediates\n\tcn->arraysize = 0;\n\n\tcn->ofs = 0;\n\tcn->symbolheader = cn;\n\tcn->symbolsize = cn->type->size;\n\tcn->symboldata = (QCC_eval_basic_t*)(cn+1);\n\n\tmemcpy(cn->symboldata, data, sizeof(pint_t) * type->size);\n\n\treturn QCC_MakeSRefForce(cn, 0, type);\n}\n\nstatic QCC_sref_t QCC_MakeGAddress(QCC_type_t *type, QCC_def_t *relocof, int idx, int bitofs)\n{\n\tQCC_def_t\t*cn;\n\n\tif (relocof->temp)\n\t\tQCC_PR_ParseWarning(ERR_INTERNAL, \"generating reloc of temp\");\n\n\tidx += bitofs>>5;\n\tbitofs &= 31;\n\tif (type->type == ev_pointer)\n\t{\n\t\tif (bitofs&7)\n\t\t\tQCC_PR_ParseWarning(ERR_INTERNAL, \"pointer has too fine granularity\");\n\t\tidx = idx*VMWORDSIZE + (bitofs>>3);\t//fix up fte style pointer\n\t}\n\telse\n\t{\n\t\tif (bitofs&7)\n\t\t\tQCC_PR_ParseWarning(ERR_INTERNAL, \"pointer has too fine granularity\");\n\t\t//sucky granularity\n\t}\n\n\tif (relocof->gaddress && !idx && relocof->gaddress->type->type == type->type)\n\t\treturn QCC_MakeSRefForce(relocof->gaddress, 0, type);\n\n// allocate a new one\n\tcn = (void *)qccHunkAlloc (sizeof(QCC_def_t) + sizeof(pint_t) * type->size);\n\tcn->next = NULL;\n\tpr.def_tail->next = cn;\n\tpr.def_tail = cn;\n\n\tcn->type = type;\n\tcn->name = \"IMMEDIATE\";\n\tcn->constant = true;\n\tcn->initialized = 0;\t//we don't know addresses until the end, which hurts folding. :(\n\tcn->scope = NULL;\t\t// always share immediates\n\tcn->arraysize = 0;\n\n\tcn->ofs = 0;\n\tcn->symbolheader = cn;\n\tcn->symbolsize = cn->type->size;\n\tcn->symboldata = (QCC_eval_basic_t*)(cn+1);\n\n\tcn->reloc = relocof;\n\tif (!idx && !bitofs)\n\t\trelocof->gaddress = cn;\n\n\tmemset(cn->symboldata, 0, sizeof(pint_t) * type->size);\n\tcn->symboldata->_int = idx;\n\n\treturn QCC_MakeSRefForce(cn, 0, type);\n}\n\n\n\nQCC_sref_t QCC_PR_GenerateVector(QCC_sref_t x, QCC_sref_t y, QCC_sref_t z);\nQCC_sref_t QCC_MakeVectorConst(pvec_t a, pvec_t b, pvec_t c)\n{\n/*\tQCC_def_t\t*cn;\n\n// check for a constant with the same value\n\tfor (cn=pr.def_head.next ; cn ; cn=cn->next)\n\t{\n\t\tif (!cn->initialized)\n\t\t\tcontinue;\n\t\tif (!cn->constant)\n\t\t\tcontinue;\n\t\tif (cn->type != type_vector)\n\t\t\tcontinue;\n\t\tif (cn->arraysize)\n\t\t\tcontinue;\n\n\t\tif (cn->symboldata[0].vector[0] == a &&\n\t\t\tcn->symboldata[0].vector[1] == b &&\n\t\t\tcn->symboldata[0].vector[2] == c)\n\t\t{\n\t\t\treturn QCC_MakeSRefForce(cn, 0, type_vector);\n\t\t}\n\t}*/\n\n\t{\n\t\tunion\n\t\t{\n\t\t\tpvec_t f[3];\n\t\t\tpint_t i[3];\n\t\t} u = {{a,b,c}};\n\t\treturn QCC_Make96bitConst(type_vector, u.i);\n\t}\n}\n\nextern hashtable_t stringconstdefstable, stringconstdefstable_trans;\nint dotranslate_count;\nstatic QCC_sref_t QCC_MakeStringConstInternal(const char *value, size_t length, pbool translate)\n{\n\tQCC_def_t\t*cn;\n\tint string;\n\tpbool usehash = (length == strlen(value)+1);\t//if there are embedded nulls, our hash code will not be able to cope.\n\n\tif (usehash)\n\t{\n\t\tcn = pHash_Get(translate?&stringconstdefstable_trans:&stringconstdefstable, value);\n\t\tif (cn)\n\t\t{\n\t\t\treturn QCC_MakeSRefForce(cn, 0, type_string);\n\t\t}\n\t}\n\n// allocate a new one\n\tif(translate)\n\t{\n\t\tchar buf[64];\n\t\tQC_snprintfz(buf, sizeof(buf), \"dotranslate_%i\", ++dotranslate_count);\n\t\tcn = (void *)qccHunkAlloc (sizeof(QCC_def_t)+sizeof(string_t) + strlen(buf)+1);\n\t\tcn->name = (char*)((string_t*)(cn+1)+1);\n\t\tstrcpy(cn->name, buf);\n\t\tcn->used = true;\t//\n\t\tcn->referenced = true;\n\t\tcn->nofold = true;\n\t}\n\telse\n\t{\n\t\tcn = (void *)qccHunkAlloc (sizeof(QCC_def_t)+sizeof(string_t));\n\t\tcn->name = \"IMMEDIATE\";\n\t}\n\tcn->next = NULL;\n\tpr.def_tail->next = cn;\n\tpr.def_tail = cn;\n\n\tcn->type = type_string;\n\tcn->constant = !translate;\n\tcn->initialized = 1;\n\tcn->scope = NULL;\t\t// always share immediates\n\tcn->arraysize = 0;\n\tcn->localscope = false;\n\n\tcn->filen = s_filen;\n\tcn->s_line = pr_source_line;\n\n// copy the immediate to the global area\n\tcn->ofs = 0;\n\tcn->symbolheader = cn;\n\tcn->symbolsize = cn->type->size;\n\tcn->symboldata = (QCC_eval_basic_t*)(cn+1);\n\n\tif (usehash)\n\t{\n\t\tstring = QCC_CopyString (value);\n\t\tpHash_Add(translate?&stringconstdefstable_trans:&stringconstdefstable, strings+string, cn, qccHunkAlloc(sizeof(bucket_t)));\n\t}\n\telse\n\t\tstring = QCC_CopyStringLength (value, length);\n\n\tcn->symboldata[0].string = string;\n\n\treturn QCC_MakeSRefForce(cn, 0, type_string);\n}\n\nQCC_sref_t QCC_MakeStringConstLength(const char *value, int length)\n{\n\treturn QCC_MakeStringConstInternal(value, length, false);\n}\nQCC_sref_t QCC_MakeStringConst(const char *value)\n{\n\treturn QCC_MakeStringConstInternal(value, strlen(value)+1, false);\n}\nQCC_sref_t QCC_MakeTranslateStringConst(const char *value)\n{\n\treturn QCC_MakeStringConstInternal(value, strlen(value)+1, true);\n}\n\nQCC_type_t *QCC_PointerTypeTo(QCC_type_t *type)\n{\n\tQCC_type_t *newtype;\n\tnewtype = QCC_PR_NewType(\"ptr\", ev_pointer, false);\n\tnewtype->aux_type = type;\n\treturn newtype;\n}\n\nQCC_type_t *QCC_GenArrayType(QCC_type_t *type, unsigned int arraysize)\n{\n\tstruct QCC_typeparam_s *param = qccHunkAlloc(sizeof(*param));\n\tparam->type = type;\n\tparam->arraysize = arraysize;\n\tparam->paramname = NULL;\n\ttype = QCC_PR_NewType(\"array\", ev_union, false);\n\ttype->params = param;\n\ttype->num_parms = 1;\n\tif (param->type->bits)\n\t{\n\t\ttype->bits = param->type->bits * param->arraysize;\n\t\ttype->size = (param->type->bits * param->arraysize + 31) & ~31;\n\t}\n\telse\n\t\ttype->size = param->type->size * param->arraysize;\n\ttype->align = param->type->align;\n\treturn type;\n}\n\nQCC_type_t **basictypes[] =\n{\n\t&type_void,\n\t&type_string,\n\t&type_float,\n\t&type_vector,\n\t&type_entity,\n\t&type_field,\n\t&type_function,\n\t&type_pointer,\n\t&type_integer,\n\t&type_uint,\n\t&type_int64,\n\t&type_uint64,\n\t&type_double,\n\t&type_variant,\n\tNULL,\t//type_struct\n\tNULL,\t//type_union\n\tNULL,\t//type_accessor\n\tNULL,\t//type_enum\n\tNULL,\t//type_boolean\n};\n\n/*static QCC_def_t *QCC_MemberInParentClass(char *name, QCC_type_t *clas)\n{\t//if a member exists, return the member field (rather than mapped-to field)\n\tQCC_def_t *def;\n\tunsigned int p;\n\tchar membername[2048];\n\n\tif (!clas)\n\t{\n\t\tdef = QCC_PR_GetDef(NULL, name, NULL, 0, 0, false);\n\t\tif (def && def->type->type == ev_field)\t//the member existed as a normal entity field.\n\t\t\treturn def;\n\t\treturn NULL;\n\t}\n\n\tfor (p = 0; p < clas->num_parms; p++)\n\t{\n\t\tif (strcmp(clas->params[p].paramname, name))\n\t\t\tcontinue;\n\n\t\t//the parent has it.\n\n\t\tQC_snprintfz(membername, sizeof(membername), \"%s::\"MEMBERFIELDNAME, clas->name, clas->params[p].paramname);\n\t\tdef = QCC_PR_GetDef(NULL, membername, NULL, false, 0, false);\n\t\tif (def)\n\t\t\treturn def;\n\t\tbreak;\n\t}\n\n\treturn QCC_MemberInParentClass(name, clas->parentclass);\n}*/\n\nstatic void QCC_PR_EmitClassFunctionTable(QCC_type_t *clas, QCC_type_t *childclas, QCC_sref_t ed)\n{\t//go through clas, do the virtual thing only if the child class does not override.\n\n\tchar membername[2048];\n\tQCC_type_t *type;\n\tQCC_type_t *oc;\n\tunsigned int p;\n\n\tQCC_sref_t point, member;\n\tQCC_sref_t virt;\n\n\tif (clas->parentclass)\n\t\tQCC_PR_EmitClassFunctionTable(clas->parentclass, childclas, ed);\n\n\tfor (p = 0; p < clas->num_parms; p++)\n\t{\n\t\ttype = clas->params[p].type;\n\t\tfor (oc = childclas; oc != clas; oc = oc->parentclass)\n\t\t{\n\t\t\tQC_snprintfz(membername, sizeof(membername), \"%s::\"MEMBERFIELDNAME, oc->name, clas->params[p].paramname);\n\t\t\tif (QCC_PR_GetSRef(NULL, membername, NULL, false, 0, false).cast)\n\t\t\t\tbreak;\t//a child class overrides.\n\t\t}\n\t\tif (oc != clas)\n\t\t\tcontinue;\n\n\t\tif (type->type == ev_function)\t//FIXME: inheritance will not install all the member functions.\n\t\t{\n\t\t\tmember = nullsref;\n\t\t\tfor (oc = childclas; oc && !member.cast; oc = oc->parentclass)\n\t\t\t{\n\t\t\t\tQC_snprintfz(membername, sizeof(membername), \"%s::\"MEMBERFIELDNAME, oc->name, clas->params[p].paramname);\n\t\t\t\tmember = QCC_PR_GetSRef(NULL, membername, NULL, false, 0, false);\n\t\t\t}\n\t\t\tif (!member.cast)\n\t\t\t{\n\t\t\t\tQC_snprintfz(membername, sizeof(membername), \"%s::\"MEMBERFIELDNAME, clas->name, clas->params[p].paramname);\n\t\t\t\tQCC_PR_Warning(ERR_INTERNAL, NULL, 0, \"Member function %s was not defined\", membername);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tQC_snprintfz(membername, sizeof(membername), \"%s::%s\", clas->name, clas->params[p].paramname);\n\t\t\tvirt = QCC_PR_GetSRef(type, membername, NULL, false, 0, false);\n\t\t\tif (!virt.cast)\n\t\t\t{\n\t\t\t\tQCC_PR_Warning(0, NULL, 0, \"Member function %s was not defined\", membername);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tpoint = QCC_PR_StatementFlags(&pr_opcodes[OP_ADDRESS], ed, member, NULL, STFL_PRESERVEA);\n\t\t\ttype_pointer->aux_type = virt.cast;\n\t\t\tQCC_PR_Statement(&pr_opcodes[OP_STOREP_FNC], virt, point, NULL);\n\t\t}\n\t}\n}\n\n//take all functions in the type, and parent types, and make sure the links all work properly.\nvoid QCC_PR_EmitClassFromFunction(QCC_def_t *scope, QCC_type_t *basetype)\n{\n\tQCC_type_t *parenttype;\n\n\tQCC_sref_t ed;\n\tQCC_sref_t constructor;\n\tint basictypefield[ev_union+1];\n\n\tint src,dst;\n\n//\tint func;\n\n\tif (numfunctions >= MAX_FUNCTIONS)\n\t\tQCC_Error(ERR_INTERNAL, \"Too many function defs\");\n\n\tpr_scope = NULL;\n\tmemset(basictypefield, 0, sizeof(basictypefield));\n//\tQCC_PR_EmitFieldsForMembers(basetype, basictypefield);\n\n\n\tpr_source_line = pr_token_line_last = scope->s_line;\n\n\tpr_scope = QCC_PR_GenerateQCFunction(scope, scope->type, NULL);\n\t//reset the locals chain\n\tpr.local_head.nextlocal = NULL;\n\tpr.local_tail = &pr.local_head;\n\n\tscope->initialized = true;\n\tscope->symboldata[0].function = pr_scope - functions;\n\n\ted = QCC_PR_GetSRef(type_entity, \"self\", NULL, true, 0, false);\n\n\t{\n\t\tQCC_sref_t fclassname = QCC_PR_GetSRef(NULL, \"classname\", NULL, false, 0, false);\n\t\tif (fclassname.cast)\n\t\t{\n\t\t\tQCC_sref_t point = QCC_PR_StatementFlags(&pr_opcodes[OP_ADDRESS], ed, fclassname, NULL, STFL_PRESERVEA);\n\t\t\ttype_pointer->aux_type = type_string;\n\t\t\tQCC_PR_Statement(&pr_opcodes[OP_STOREP_FNC], QCC_MakeStringConst(basetype->name), point, NULL);\n\t\t}\n\t}\n\n\tQCC_PR_EmitClassFunctionTable(basetype, basetype, ed);\n\n\tsrc = numstatements;\n\tfor (parenttype = basetype; parenttype; parenttype = parenttype->parentclass)\n\t{\n\t\tchar membername[2048];\n\t\tQC_snprintfz(membername, sizeof(membername), \"%s::%s\", parenttype->name, parenttype->name);\n\t\tconstructor = QCC_PR_GetSRef(NULL, membername, NULL, false, 0, false);\n\n\t\tif (constructor.cast)\n\t\t{\n\t\t\tconstructor.sym->referenced = true;\n\t\t\tQCC_PR_SimpleStatement(&pr_opcodes[OP_CALL0], constructor, nullsref, nullsref, false);\n\t\t\tQCC_FreeTemp(constructor);\n\t\t}\n\t}\n\n\tif (flag_rootconstructor)\n\t{\n\t\tdst = numstatements-1;\n\t\twhile(src < dst)\n\t\t{\n\t\t\tQCC_sref_t t = statements[src].a;\n\t\t\tstatements[src].a = statements[dst].a;\n\t\t\tstatements[dst].a = t;\n\t\t\tsrc++;\n\t\t\tdst--;\n\t\t}\n\t}\n\n\tQCC_FreeTemp(QCC_PR_Statement(&pr_opcodes[OP_DONE], nullsref, nullsref, NULL));\n\n\n\n\tQCC_WriteAsmFunction(pr_scope, pr_scope->code, pr_scope->firstlocal);\n\n\tQCC_Marshal_Locals(pr_scope->code, numstatements);\n}\n\nstatic QCC_sref_t QCC_PR_ExpandField(QCC_sref_t ent, QCC_sref_t field, QCC_type_t *fieldtype, unsigned int preserveflags)\n{\n\tQCC_type_t *basicfieldtype;\n\tQCC_sref_t r;\n\tif (!fieldtype)\n\t{\n\t\tif (field.cast->type == ev_field)\n\t\t\tfieldtype = field.cast->aux_type;\n\t\telse\n\t\t{\n\t\t\tif (field.cast->type != ev_variant)\n\t\t\t\tQCC_PR_ParseErrorPrintSRef(ERR_INTERNAL, field, \"QCC_PR_ExpandField: invalid field type\");\n\t\t\tfieldtype = type_variant;\n\t\t}\n\t}\n\tbasicfieldtype = fieldtype;\n\twhile(basicfieldtype->type == ev_accessor || basicfieldtype->type == ev_boolean || basicfieldtype->type == ev_enum)\n\t\tbasicfieldtype = (basicfieldtype->type == ev_enum)?basicfieldtype->aux_type:basicfieldtype->parentclass;\n\n\t//FIXME: class.staticmember should directly read staticmember instead of trying to dereference\n\tswitch(basicfieldtype->type)\n\t{\n\tcase ev_struct:\n\tcase ev_union:\n\t\t{\n\t\t\tint i = 0;\n\t\t\tQCC_type_t *type = fieldtype;\n\t\t\tQCC_sref_t dest = QCC_GetTemp(type);\n\t\t\tQCC_sref_t source = field;\n\t\t\t//don't bother trying to optimise any temps here, its not likely to happen anyway.\n\t\t\tfor (; i+2 < type->size; i+=3, dest.ofs += 3, source.ofs += 3)\n\t\t\t\tQCC_PR_SimpleStatement(&pr_opcodes[OP_LOAD_V], ent, source, dest, false);\n\t\t\tif (QCC_OPCodeValid(&pr_opcodes[OP_LOAD_I64]))\n\t\t\t\tfor (; i+1 < type->size; i+=2, dest.ofs += 2, source.ofs += 2)\n\t\t\t\t\tQCC_PR_SimpleStatement(&pr_opcodes[OP_LOAD_I64], ent, source, dest, false);\n\t\t\tfor (; i < type->size; i++, dest.ofs++, source.ofs++)\n\t\t\t\tQCC_PR_SimpleStatement(&pr_opcodes[OP_LOAD_F], ent, source, dest, false);\n\t\t\tsource.ofs -= type->size;\n\t\t\tdest.ofs -= type->size;\n\t\t\tif (!(preserveflags & STFL_PRESERVEA))\n\t\t\t\tQCC_FreeTemp(ent);\n\t\t\tif (!(preserveflags & STFL_PRESERVEB))\n\t\t\t\tQCC_FreeTemp(field);\n\n\t\t\tif (type->size > 3)\n\t\t\t\tQCC_PR_ParseWarning(WARN_UNDESIRABLECONVENTION, \"inefficient - copying %u words to a temp\", type->size);\n\t\t\treturn dest;\n\t\t}\n\t\tbreak;\n\tcase ev_void:\n\tcase ev_accessor:\n\tcase ev_boolean:\n\tcase ev_enum:\n\tdefault:\n\t\t{\n\t\t\tchar temp[256];\n\t\t\tQCC_PR_ParseErrorPrintSRef(ERR_INTERNAL, field, \"QCC_PR_ExpandField: invalid field type %s%s%s\",  col_type,TypeName(fieldtype, temp, sizeof(temp)),col_none);\n\t\t}\n\t\tr = field;\n\t\tbreak;\n\tcase ev_integer:\n\t\tr = QCC_PR_StatementFlags(&pr_opcodes[OP_LOAD_I], ent, field, NULL, preserveflags);\n\t\tbreak;\n\tcase ev_uint:\n\t\tr = QCC_PR_StatementFlags(&pr_opcodes[OP_LOAD_I], ent, field, NULL, preserveflags);\n\t\tr.cast = type_uint;\n\t\tbreak;\n\tcase ev_double:\n\t\tr = QCC_PR_StatementFlags(&pr_opcodes[OP_LOAD_I64], ent, field, NULL, preserveflags);\n\t\tr.cast = type_double;\n\t\tbreak;\n\tcase ev_int64:\n\t\tr = QCC_PR_StatementFlags(&pr_opcodes[OP_LOAD_I64], ent, field, NULL, preserveflags);\n\t\tr.cast = type_int64;\n\t\tbreak;\n\tcase ev_uint64:\n\t\tr = QCC_PR_StatementFlags(&pr_opcodes[OP_LOAD_I64], ent, field, NULL, preserveflags);\n\t\tr.cast = type_uint64;\n\t\tbreak;\n\n\tcase ev_pointer:\n\t\tr = QCC_PR_StatementFlags(&pr_opcodes[OP_LOAD_P], ent, field, NULL, preserveflags);\n\t\tbreak;\n\tcase ev_field:\n\t\tr = QCC_PR_StatementFlags(&pr_opcodes[OP_LOAD_FLD], ent, field, NULL, preserveflags);\n\t\tbreak;\n\tcase ev_variant:\n\t\tr = QCC_PR_StatementFlags(&pr_opcodes[OP_LOAD_FLD], ent, field, NULL, preserveflags);\n\t\tbreak;\n\tcase ev_float:\n\t\tr = QCC_PR_StatementFlags(&pr_opcodes[OP_LOAD_F], ent, field, NULL, preserveflags);\n\t\tbreak;\n\tcase ev_string:\n\t\tr = QCC_PR_StatementFlags(&pr_opcodes[OP_LOAD_S], ent, field, NULL, preserveflags);\n\t\tbreak;\n\tcase ev_vector:\n\t\tr = QCC_PR_StatementFlags(&pr_opcodes[OP_LOAD_V], ent, field, NULL, preserveflags);\n\t\tbreak;\n\tcase ev_function:\n\t\tr = QCC_PR_StatementFlags(&pr_opcodes[OP_LOAD_FNC], ent, field, NULL, preserveflags);\n\t\tbreak;\n\tcase ev_entity:\n\t\tr = QCC_PR_StatementFlags(&pr_opcodes[OP_LOAD_ENT], ent, field, NULL, preserveflags);\n\t\tbreak;\n\t}\n\tr.cast = fieldtype;\n\treturn r;\n}\n\n/*checks for <DEF>.foo and expands in a class-aware fashion\nnormally invoked via QCC_PR_ParseArrayPointer\n*/\nstatic QCC_ref_t *QCC_PR_ParseField(QCC_ref_t *refbuf, QCC_ref_t *lhs)\n{\n\tQCC_type_t *t;\n\tt = lhs->cast;\n\tif ((t->accessors || t->type == ev_entity) && (QCC_PR_CheckToken(\".\") || QCC_PR_CheckToken(\"->\")))\n\t{\n\t\tQCC_ref_t *field;\n\t\tQCC_ref_t fieldbuf;\n\n\t\tif (pr_token_type == tt_name)\n\t\t{\n\t\t\tQCC_sref_t index = nullsref;\n\t\t\tchar *fieldname = pr_token;\n\t\t\tstruct accessor_s *acc = NULL, *anon = NULL;\n\t\t\tQCC_type_t *a;\n\n\t\t\tfor (a = t; a && !acc; a = a->parentclass)\n\t\t\t\tfor (acc = a->accessors; acc; acc = acc->next)\n\t\t\t\t{\n\t\t\t\t\tif (!*acc->fieldname && acc->indexertype)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (!anon)\n\t\t\t\t\t\t\tanon = acc;\n\t\t\t\t\t}\n\t\t\t\t\telse if (!strcmp(acc->fieldname, fieldname))\n\t\t\t\t\t{\n\t\t\t\t\t\tfieldname = QCC_PR_ParseName(); //do it for real now.\n\t\t\t\t\t\tif (acc->indexertype)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (QCC_PR_CheckToken(\".\") || QCC_PR_CheckToken(\"->\"))\n\t\t\t\t\t\t\t\tindex = QCC_MakeStringConst(QCC_PR_ParseName());\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tQCC_PR_Expect(\"[\");\n\t\t\t\t\t\t\t\tindex = QCC_PR_Expression (TOP_PRIORITY, 0);\n\t\t\t\t\t\t\t\tQCC_PR_Expect(\"]\");\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\tif (!acc && anon)\n\t\t\t{\n\t\t\t\tacc = anon;\n\t\t\t\tfieldname = QCC_PR_ParseName(); //do it for real now.\n\t\t\t\tindex = QCC_MakeStringConst(fieldname);\n\t\t\t}\n\t\t\tif (acc)\n\t\t\t{\n\t\t\t\tlhs = QCC_PR_BuildAccessorRef(refbuf, QCC_RefToDef(lhs, true), index, acc, lhs->readonly);\n\t\t\t\tlhs = QCC_PR_ParseField(refbuf, lhs);\n\t\t\t\treturn lhs;\n\t\t\t}\n\t\t}\n\n\t\tif (t->type == ev_entity)\n\t\t{\n\t\t\tif (QCC_PR_CheckToken(\"(\"))\n\t\t\t{\n\t\t\t\tfield = QCC_PR_RefExpression(&fieldbuf, TOP_PRIORITY, 0);\n\t\t\t\tQCC_PR_Expect(\")\");\n\t\t\t}\n\t\t\telse\n\t\t\t\tfield = QCC_PR_ParseRefValue(&fieldbuf, t, false, false, true);\n\t\t\tif (field->type != REF_ARRAYHEAD && (field->cast->type == ev_field || field->cast->type == ev_variant))\n\t\t\t{\n\t\t\t\t//fields are generally always readonly. that refers to the field def itself, rather than products of said field.\n\t\t\t\t//entities, like 'world' might also be consts. just ignore that fact. the def itself is not assigned, but the fields of said def.\n\t\t\t\t//the engine may have a problem with this, but the qcc has no way to referenced locations as readonly separately from the def itself.\n\t\t\t\tlhs = QCC_PR_BuildRef(refbuf, REF_FIELD, QCC_RefToDef(lhs, true), QCC_RefToDef(field, true), (field->cast->type == ev_field)?field->cast->aux_type:type_variant, false, 0);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (field->type == REF_GLOBAL && strstr(QCC_GetSRefName(field->base), \"::\"))\n\t\t\t\t{\n\t\t\t\t\tQCC_sref_t theent = QCC_RefToDef(lhs, true);\n\t\t\t\t\t*refbuf = *field;\n\t\t\t\t\trefbuf->type = REF_NONVIRTUAL;\n\t\t\t\t\trefbuf->index = theent;\n\t\t\t\t\treturn refbuf;\n\t\t\t\t}\n\t\t\t\tif (t->parentclass)\n\t\t\t\t\tQCC_PR_ParseError(ERR_BADMEMBER, \"%s is not a field of class %s\", QCC_GetSRefName(QCC_RefToDef(field, false)), t->name);\n\t\t\t\telse\n\t\t\t\t\tQCC_PR_ParseError(ERR_BADMEMBER, \"%s is not a field\", QCC_GetSRefName(QCC_RefToDef(field, false)));\n\t\t\t}\n\n\t\t\tlhs = QCC_PR_ParseField(refbuf, lhs);\n\n\t\t\tlhs = QCC_PR_ParseRefArrayPointer (refbuf, lhs, false, false);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tQCC_PR_ParseWarning(ERR_BADMEMBER, \"%s is not a member of %s\", QCC_PR_ParseName(), t->name);\n\t\t\tif (t->filen)\n\t\t\t\tQCC_PR_Note(ERR_BADMEMBER, t->filen, t->line, \"%s is defined here\", t->name);\n\t\t\treturn QCC_PR_BuildRef(refbuf, REF_GLOBAL, QCC_MakeIntConst(0), nullsref, type_void, false, 0);\n\t\t}\n\t}\n\telse if (flag_qccx && t->type == ev_entity && QCC_PR_CheckToken(\"[\"))\n\t{\t//p[%0] gives a regular array reference. except that p is probably a float, and we're expecting OP_LOAD_F\n\t\t//might also be assigned to, so just create a regular field ref and figure that stuff out later.\n\t\tQCC_ref_t *field;\n\t\tQCC_ref_t fieldbuf;\n\t\tfield = QCC_PR_RefExpression(&fieldbuf, TOP_PRIORITY, 0);\n\t\tfield->cast = type_floatfield;\n\t\tQCC_PR_Expect(\"]\");\n\n\t\tlhs = QCC_PR_BuildRef(refbuf, REF_FIELD, QCC_RefToDef(lhs, true), QCC_RefToDef(field, true), type_float, false, 0);\n\n\t\t\n\t\tlhs = QCC_PR_ParseField(refbuf, lhs);\n\t\tlhs = QCC_PR_ParseRefArrayPointer (refbuf, lhs, false, false);\n\t}\n\telse if (flag_qccx && t->type == ev_entity && QCC_PR_CheckToken(\"^\"))\n\t{\t//p^[%0] is evaluated as an OP_LOAD_V (or OP_ADDRESS+OP_STOREP_V)\n\t\tQCC_ref_t *field;\n\t\tQCC_ref_t fieldbuf;\n\t\tQCC_PR_Expect(\"[\");\n\t\tfield = QCC_PR_RefExpression(&fieldbuf, TOP_PRIORITY, 0);\n\t\tfield->cast = type_floatfield;\n\t\tQCC_PR_Expect(\"]\");\n\n\t\tlhs = QCC_PR_BuildRef(refbuf, REF_FIELD, QCC_RefToDef(lhs, true), QCC_RefToDef(field, true), type_vector, false, 0);\n\n\t\t\n\t\tlhs = QCC_PR_ParseField(refbuf, lhs);\n\t\tlhs = QCC_PR_ParseRefArrayPointer (refbuf, lhs, false, false);\n\t}\n\treturn lhs;\n}\n\n//this is more complex than it needs to be, in order to ensure that anon unions/structs can be handled.\nstruct QCC_typeparam_s *QCC_PR_FindStructMember(QCC_type_t *t, const char *membername, unsigned int *out_ofs, unsigned int *out_bitofs)\n{\n\tunsigned int nofs, nbitofs;\n\tint i;\n\tstruct QCC_typeparam_s *r = NULL, *n;\n\tfor (i = 0; i < t->num_parms; i++)\n\t{\n\t\tif ((!t->params[i].paramname || !*t->params[i].paramname) && (t->params[i].type->type == ev_struct || t->params[i].type->type == ev_union))\n\t\t{\t//anonymous structs/unions can nest\n\t\t\tn = QCC_PR_FindStructMember(t->params[i].type, membername, &nofs, &nbitofs);\n\t\t\tif (n)\n\t\t\t{\n\t\t\t\tif (r)\n\t\t\t\t\tbreak;\n\t\t\t\tr = n;\n\t\t\t\t*out_ofs = t->params[i].ofs + nofs;\n\t\t\t\t*out_bitofs = t->params[i].bitofs + nbitofs;\n\t\t\t}\n\t\t}\n\t\telse if (flag_caseinsensitive?!stricmp (t->params[i].paramname, membername):!STRCMP(t->params[i].paramname, membername))\n\t\t{\n\t\t\tif (r)\n\t\t\t\tbreak;\n\t\t\tr = t->params+i;\n\t\t\t*out_ofs = r->ofs;\n\t\t\t*out_bitofs = r->bitofs;\n\t\t}\n\t}\n\tif (i < t->num_parms)\n\t{\n\t\tQCC_PR_ParseError(0, \"multiple members found matching %s.%s\", t->name, membername);\n\t\treturn NULL;\n\t}\n\tif (!r && t->parentclass)\t//chain through the parent struct\n\t\treturn QCC_PR_FindStructMember(t->parentclass, membername, out_ofs, out_bitofs);\n\n\treturn r;\n}\n\n/*checks for:\n<d>[X]\n<d>[X].foo\n<d>.foo\nwithin types which are a contiguous block, expanding to an array index.\n\nAlso calls QCC_PR_ParseField, which does fields too.\n*/\nQCC_ref_t *QCC_PR_ParseRefArrayPointer (QCC_ref_t *retbuf, QCC_ref_t *r, pbool allowarrayassign, pbool makearraypointers)\n{\n\tQCC_type_t *t, *p;\n\tQCC_sref_t idx;\n\tQCC_sref_t tmp;\n\tpbool allowarray, arraytype;\n\tunsigned int arraysize;\n\tunsigned int rewindpoint = numstatements;\n\tpbool dereference = false;\n\tconst QCC_eval_t *eval;\n\tunsigned int bitofs = 0;\n\tpbool dorecurse = false;\n\tQCC_ref_t addr;\n\n\tidx = nullsref;\n\n\tt = r->cast;\n\tif (r->type == REF_ARRAYHEAD || r->type == REF_POINTERARRAY)\n\t{\n\t\tif (r->type == REF_POINTERARRAY)\n\t\t\tdereference = true;\n\n\t\tif (t->type != ev_pointer)\n\t\t\tQCC_PR_ParseWarning(ERR_INTERNAL, \"QCC_PR_ParseRefArrayPointer: array reference not a cast to pointer\\n\");\n\t\tt = t->aux_type;\n\t\tarraysize = r->arraysize;\n\t}\n\telse\n\t{\n\t\tif (r->type == REF_POINTER && r->cast->type != ev_pointer && !r->postinc && (r->cast->type==ev_union || r->cast->type==ev_struct) && QCC_PR_PeekToken(\".\"))\n\t\t{\t// (*ptr).blah === ptr->blah\n\t\t\t//try to undo the *ptr so we can do it automatically from the ->\n//\t\t\tr = QCC_PR_GenerateAddressOf(&addr, r);\n//\t\t\treturn QCC_PR_ParseRefArrayPointer(retbuf, r, allowarrayassign, makearraypointers);\n\n\t\t\taddr = *r;\n\t\t\tr = &addr;\n\n\t\t\tr->cast = t = QCC_PR_PointerType(t);\n//\t\t\tdereference = true;\n\t\t\tidx = r->index;\n\t\t\tr->index = nullsref;\n\t\t\tr->type = REF_GLOBAL;\n\n\t\t}\n\t\tarraysize = 0;\n\t}\n\n\twhile(1)\n\t{\n\t\tallowarray = false;\n\t\tarraytype = (t->type == ev_union && t->num_parms == 1 && !t->params[0].paramname);\t//FIXME\n\t\tif (arraytype)\n\t\t\tallowarray = true;\n\t\tif (idx.cast)\n\t\t\tallowarray = arraysize>0 ||\n\t\t\t\t\t\t(t->type == ev_vector) ||\n\t\t\t\t\t\t(t->type == ev_field && t->aux_type->type == ev_vector) ||\n\t\t\t\t\t\t(arraytype && !arraysize);\n\t\telse if (!idx.cast)\n\t\t{\n\t\t\tallowarray = arraysize>0 ||\n\t\t\t\t\t\t(t->type == ev_pointer) ||\t//we can dereference pointers\n\t\t\t\t\t\t(t->type == ev_string) ||\t//strings are effectively pointers\n\t\t\t\t\t\t(t->type == ev_vector) ||\t//vectors are mini arrays\n\t\t\t\t\t\t(t->type == ev_field && t->aux_type->type == ev_vector) ||\t//as are field vectors\n\t\t\t\t\t\t(arraytype && !arraysize) ||\n\t\t\t\t\t\t(!arraysize&&t->accessors);\t//custom accessors\n\t\t}\n\n\t\tif (allowarray && QCC_PR_CheckToken(\"[\"))\n\t\t{\n\t\t\tp = t;\n\t\t\ttmp = QCC_PR_Expression (TOP_PRIORITY, 0);\n\t\t\tQCC_PR_Expect(\"]\");\n\n\t\t\tif (!arraysize && t->accessors)\n\t\t\t{\n\t\t\t\tstruct accessor_s *acc;\n\t\t\t\tfor (acc = t->accessors; acc; acc = acc->next)\n\t\t\t\t\tif (!*acc->fieldname)\n\t\t\t\t\t\tbreak;\n\t\t\t\tif(acc)\n\t\t\t\t{\n\t\t\t\t\tr = QCC_PR_BuildAccessorRef(retbuf, QCC_RefToDef(r, true), tmp, acc, r->readonly);\n\t\t\t\t\treturn QCC_PR_ParseRefArrayPointer(retbuf, r, allowarrayassign, makearraypointers);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t/*if its a pointer that got dereferenced, follow the type*/\n\t\t\tif (!idx.cast && t->type == ev_pointer && !arraysize)\n\t\t\t\tt = t->aux_type;\n\t\t\telse if (idx.cast && (arraytype && !arraysize))\n\t\t\t{\n\t\t\t\tarraysize = t->params[0].arraysize;\n\t\t\t\tbitofs += t->params[0].bitofs;\n\t\t\t\tt = t->params[0].type;\n\t\t\t}\n\n\t\t\tif (!idx.cast && p->type == ev_pointer && !arraysize)\n\t\t\t{\n\t\t\t\t/*no bounds checks on pointer dereferences*/\n\t\t\t\tif (dereference)\n\t\t\t\t{\t//resolve it now\n\t\t\t\t\tr = QCC_PR_BuildRef(retbuf, REF_POINTER, QCC_RefToDef(r, true), idx, t, r->readonly, bitofs);\n\t\t\t\t\tidx = nullsref;\n\t\t\t\t}\n\t\t\t\tdereference = true;\n\t\t\t}\n\t\t\telse if (!idx.cast && p->type == ev_string && !arraysize)\n\t\t\t{\n\t\t\t\tif (flag_qccx)\n\t\t\t\t{\n\t\t\t\t\tQCC_sref_t base = QCC_RefToDef(r, true);\n\t\t\t\t\tif (tmp.cast && tmp.cast->type == ev_float)\n\t\t\t\t\t{\n\t\t\t\t\t\tQCC_PR_ParseWarning(WARN_DENORMAL, \"string offsetting emulation: denormals are unsafe\");\n\t\t\t\t\t\tidx = QCC_PR_Statement(&pr_opcodes[OP_ADD_F], base, QCC_SupplyConversion(tmp, ev_float, true), NULL);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\tidx = QCC_PR_Statement(&pr_opcodes[OP_ADD_I], base, QCC_SupplyConversion(tmp, ev_integer, true), NULL);\n\t\t\t\t\treturn QCC_PR_BuildRef(retbuf, REF_GLOBAL, idx, nullsref, type_string, true, 0);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t/*automatic runtime bounds checks on strings, I'm not going to check this too much...*/\n\t\t\t\t\tr = QCC_PR_BuildRef(retbuf, REF_STRING, QCC_RefToDef(r, true), tmp, (tmp.cast->type != ev_float)?type_integer:type_float, r->readonly, 0);\n\t\t\t\t\treturn QCC_PR_ParseRefArrayPointer(retbuf, r, allowarrayassign, makearraypointers);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if ((!idx.cast && p->type == ev_vector && !arraysize) || (idx.cast && t->type == ev_vector && !arraysize))\n\t\t\t{\n\t\t\t\t/*array notation on vector*/\nvectorarrayindex:\n\t\t\t\tif ((eval=QCC_SRef_EvalConst(tmp)))\n\t\t\t\t{\n\t\t\t\t\tunsigned long i = QCC_Eval_Int(eval, tmp.cast);\n\t\t\t\t\tif (i >= 3u)\n\t\t\t\t\t\tQCC_PR_ParseErrorPrintSRef(0, r->base, \"(vector) array index out of bounds\");\n\t\t\t\t}\n\t\t\t\telse if (QCC_OPCodeValid(&pr_opcodes[OP_BOUNDCHECK]) && flag_boundchecks)\n\t\t\t\t{\n\t\t\t\t\ttmp = QCC_SupplyConversion(tmp, ev_integer, true);\n\t\t\t\t\tQCC_PR_SimpleStatement (&pr_opcodes[OP_BOUNDCHECK], tmp, QCC_MakeSRef(NULL, 3, NULL), nullsref, false);\n\t\t\t\t}\n\t\t\t\tt = type_float;\n\t\t\t}\n\t\t\telse if ((!idx.cast && p->type == ev_field && r->cast->aux_type->type == ev_vector && !arraysize) || (idx.cast && t->type == ev_field && t->aux_type->type && !arraysize))\n\t\t\t{\n\t\t\t\t/*array notation on vector field*/\nfieldarrayindex:\n\t\t\t\tif ((eval=QCC_SRef_EvalConst(tmp)))\n\t\t\t\t{\n\t\t\t\t\tunsigned long i = QCC_Eval_Int(eval, tmp.cast);\n\t\t\t\t\tif (i >= 3u)\n\t\t\t\t\t\tQCC_PR_ParseErrorPrintSRef(0, r->base, \"(.vector) array index out of bounds\");\n\t\t\t\t}\n\t\t\t\telse if (QCC_OPCodeValid(&pr_opcodes[OP_BOUNDCHECK]) && flag_boundchecks)\n\t\t\t\t{\n\t\t\t\t\ttmp = QCC_SupplyConversion(tmp, ev_integer, true);\n\t\t\t\t\tQCC_PR_SimpleStatement (&pr_opcodes[OP_BOUNDCHECK], tmp, QCC_MakeSRef(NULL, 3, NULL), nullsref, false);\n\t\t\t\t}\n\t\t\t\tt = type_floatfield;\n\t\t\t}\n\t\t\telse if (!arraysize)\n\t\t\t{\n\t\t\t\tQCC_PR_ParseErrorPrintSRef(0, r->base, \"array index on non-array\");\n\t\t\t}\n\t\t\telse if ((eval=QCC_SRef_EvalConst(tmp)))\n\t\t\t{\n\t\t\t\tunsigned i = QCC_Eval_Int(eval, tmp.cast);\n\t\t\t\tif (i >= (unsigned)arraysize)\n\t\t\t\t{\n\t\t\t\t\tQCC_PR_ParseWarning(WARN_BOUNDS, \"(constant) array index out of bounds (0 <= %i < %i)\", i, arraysize);\n\t\t\t\t\tQCC_PR_ParsePrintSRef(WARN_BOUNDS, r->base);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (QCC_OPCodeValid(&pr_opcodes[OP_BOUNDCHECK]) && flag_boundchecks)\n\t\t\t\t{\n\t\t\t\t\ttmp = QCC_SupplyConversion(tmp, ev_integer, true);\n\t\t\t\t\tQCC_PR_SimpleStatement (&pr_opcodes[OP_BOUNDCHECK], tmp, QCC_MakeSRef(NULL, arraysize, NULL), nullsref, false);\n\t\t\t\t}\n\t\t\t}\n\t\t\tarraysize = 0;\n\n\t\t\tif (t->bits)\n\t\t\t{\n\t\t\t\t//convert it to ints if that makes sense\n\t\t\t\tif (idx.cast)\n\t\t\t\t\tidx = QCC_SupplyConversion(idx, ev_integer, true);\n\t\t\t\ttmp = QCC_SupplyConversion(tmp, ev_integer, true);\n\t\t\t\ttmp = QCC_PR_Statement(&pr_opcodes[OP_MUL_I], QCC_SupplyConversion(tmp, ev_integer, true), QCC_MakeIntConst(t->bits/t->align), NULL);\n\t\t\t}\n\t\t\telse if (t->size != 1) /*don't multiply by type size if the instruction/emulation will do that instead*/\n\t\t\t{\n\t\t\t\t//convert it to ints if that makes sense\n\t\t\t\tif (QCC_OPCodeValid(&pr_opcodes[OP_ADD_I]) && ((idx.cast && idx.cast->type == ev_integer) || tmp.cast->type == ev_integer))\n\t\t\t\t{\n\t\t\t\t\tif (idx.cast)\n\t\t\t\t\t\tidx = QCC_SupplyConversion(idx, ev_integer, true);\n\t\t\t\t\ttmp = QCC_SupplyConversion(tmp, ev_integer, true);\n\t\t\t\t}\n\n\t\t\t\tif (tmp.cast->type == ev_float)\n\t\t\t\t\ttmp = QCC_PR_Statement(&pr_opcodes[OP_MUL_F], QCC_SupplyConversion(tmp, ev_float, true), QCC_MakeFloatConst(t->size), NULL);\n\t\t\t\telse\n\t\t\t\t\ttmp = QCC_PR_Statement(&pr_opcodes[OP_MUL_I], QCC_SupplyConversion(tmp, ev_integer, true), QCC_MakeIntConst(t->size), NULL);\n\n\t\t\t\t//FIXME: we really need some sort of x*stride+offset ref type. it would allow adding offsets more efficiently.\n\t\t\t}\n\n\t\t\t//legacy opcodes needs to stay using floats even if an int was specified. avoid int immediates.\n//\t\t\tif (!QCC_OPCodeValid(&pr_opcodes[OP_ADD_I]))\n//\t\t\t{\n//\t\t\t\tif (idx.cast)\n//\t\t\t\t\tidx = QCC_SupplyConversion(idx, ev_float, true);\n//\t\t\t\ttmp = QCC_SupplyConversion(tmp, ev_float, true);\n//\t\t\t}\n\n\t\t\t/*calc the new index*/\n\t\t\tif (idx.cast && idx.cast->type == ev_float && tmp.cast->type == ev_float)\n\t\t\t\tidx = QCC_PR_Statement(&pr_opcodes[OP_ADD_F], QCC_SupplyConversion(idx, ev_float, true), QCC_SupplyConversion(tmp, ev_float, true), NULL);\n\t\t\telse if (idx.cast)\n\t\t\t\tidx = QCC_PR_Statement(&pr_opcodes[OP_ADD_I], QCC_SupplyConversion(idx, ev_integer, true), QCC_SupplyConversion(tmp, ev_integer, true), NULL);\n\t\t\telse\n\t\t\t\tidx = tmp;\n\t\t}\n\t\telse if (arraysize && (QCC_PR_CheckToken(\".\") /*|| QCC_PR_CheckToken(\"->\")*/))\n\t\t{\n\t\t\t//the only field of an array type is the 'length' property.\n\t\t\t//if we calculated offsets etc, discard those statements.\n\t\t\t//FIXME: if this is an array of pointer-to-struct then '->' will dereference and we just misparsed.\n\t\t\tnumstatements = rewindpoint;\n\t\t\tQCC_PR_Expect(\"length\");\n\t\t\tQCC_FreeTemp(r->base);\n\t\t\tQCC_FreeTemp(r->index);\n\t\t\tQCC_FreeTemp(idx);\n\t\t\treturn QCC_PR_BuildRef(retbuf, REF_GLOBAL, QCC_MakeIntConst(arraysize), nullsref, type_integer, true, 0);\n\t\t}\n\t\telse if (arraytype && (QCC_PR_CheckToken(\".\")/* || QCC_PR_CheckToken(\"->\")*/))\n\t\t{\n\t\t\t//the only field of an array type is the 'length' property.\n\t\t\t//if we calculated offsets etc, discard those statements.\n\t\t\tnumstatements = rewindpoint;\n\t\t\tQCC_PR_Expect(\"length\");\n\t\t\tQCC_FreeTemp(r->base);\n\t\t\tQCC_FreeTemp(r->index);\n\t\t\tQCC_FreeTemp(idx);\n\t\t\treturn QCC_PR_BuildRef(retbuf, REF_GLOBAL, QCC_MakeIntConst(t->params[0].arraysize), nullsref, type_integer, true, 0);\n\t\t}\n\t\telse if (t->type == ev_string && !idx.cast && (QCC_PR_CheckToken(\".\")/* || QCC_PR_CheckToken(\"->\")*/))\n\t\t{\n\t\t\tif (QCC_PR_CheckName(\"length\"))\n\t\t\t{\n\t\t\t\tconst char *val = QCC_SRef_EvalStringConst(r->base);\n\t\t\t\tif (val)\n\t\t\t\t{\t//the only field of an array type is the 'length' property.\n\t\t\t\t\t//if we calculated offsets etc, discard those statements.\n\t\t\t\t\tnumstatements = rewindpoint;\n\t\t\t\t\tQCC_FreeTemp(r->base);\n\t\t\t\t\tQCC_FreeTemp(r->index);\n\t\t\t\t\tQCC_FreeTemp(idx);\n\t\t\t\t\treturn QCC_PR_BuildRef(retbuf, REF_GLOBAL, QCC_MakeIntConst(strlen(val)), nullsref, type_integer, true, 0);\n\t\t\t\t}\n\t\t\t\tQCC_PR_ParseError(0, \"not a constant\");\n\t\t\t}\n\t\t\telse\n\t\t\t\tQCC_PR_ParseError(0, \"unsupported string method %s\", pr_token);\n\t\t}\n\t\telse if (t->type == ev_vector && !arraysize && !t->accessors && QCC_PR_CheckToken(\".\"))\n\t\t{\n\t\t\tchar *swizzle = QCC_PR_ParseName();\n\t\t\t//single-channel swizzles just result in a float. nice and easy. assignable, too.\n\t\t\tif ((!strcmp(swizzle, \"x\") || !strcmp(swizzle, \"r\")) && t->size >= 1)\n\t\t\t{\n\t\t\t\ttmp = QCC_MakeIntConst(0);\n\t\t\t\tgoto vectorarrayindex;\n\t\t\t}\n\t\t\telse if ((!strcmp(swizzle, \"y\") || !strcmp(swizzle, \"g\")) && t->size >= 2)\n\t\t\t{\n\t\t\t\ttmp = QCC_MakeIntConst(1);\n\t\t\t\tgoto vectorarrayindex;\n\t\t\t}\n\t\t\telse if ((!strcmp(swizzle, \"z\") || !strcmp(swizzle, \"b\")) && t->size >= 3)\n\t\t\t{\n\t\t\t\ttmp = QCC_MakeIntConst(2);\n\t\t\t\tgoto vectorarrayindex;\n\t\t\t}\n\t\t\telse if ((!strcmp(swizzle, \"w\")  || !strcmp(swizzle, \"a\")) && t->size >= 4)\n\t\t\t{\n\t\t\t\ttmp = QCC_MakeIntConst(3);\n\t\t\t\tgoto vectorarrayindex;\n\t\t\t}\n\t\t\telse\n\t\t\t\tQCC_PR_ParseError(0, \"unsupported vector swizzle '.%s'\", swizzle);\n\t\t}\n\t\telse if ((t->type == ev_field && t->aux_type->type == ev_vector) && !arraysize && !t->accessors && QCC_PR_CheckToken(\".\"))\n\t\t{\n\t\t\tchar *swizzle = QCC_PR_ParseName();\n\t\t\t//single-channel swizzles just result in a float. nice and easy. assignable, too.\n\t\t\tif ((!strcmp(swizzle, \"x\") || !strcmp(swizzle, \"r\")) && t->size >= 1)\n\t\t\t{\n\t\t\t\ttmp = QCC_MakeIntConst(0);\n\t\t\t\tgoto fieldarrayindex;\n\t\t\t}\n\t\t\telse if ((!strcmp(swizzle, \"y\") || !strcmp(swizzle, \"g\")) && t->size >= 2)\n\t\t\t{\n\t\t\t\ttmp = QCC_MakeIntConst(1);\n\t\t\t\tgoto fieldarrayindex;\n\t\t\t}\n\t\t\telse if ((!strcmp(swizzle, \"z\") || !strcmp(swizzle, \"b\")) && t->size >= 3)\n\t\t\t{\n\t\t\t\ttmp = QCC_MakeIntConst(2);\n\t\t\t\tgoto fieldarrayindex;\n\t\t\t}\n\t\t\telse if ((!strcmp(swizzle, \"w\")  || !strcmp(swizzle, \"a\")) && t->size >= 4)\n\t\t\t{\n\t\t\t\ttmp = QCC_MakeIntConst(3);\n\t\t\t\tgoto fieldarrayindex;\n\t\t\t}\n\t\t\telse\n\t\t\t\tQCC_PR_ParseError(0, \"unsupported vector swizzle '.%s'\", swizzle);\n\t\t}\n\t\telse if (((t->type == ev_pointer && !arraysize) || (t->type == ev_field && (t->aux_type->type == ev_struct || t->aux_type->type == ev_union)) || t->type == ev_struct || t->type == ev_union) && (QCC_PR_CheckToken(\".\") || QCC_PR_CheckToken(\"->\")))\n\t\t{\n\t\t\tconst char *tname, *mname;\n\t\t\tunsigned int ofs, mbitofs;\n\t\t\tpbool fld = t->type == ev_field;\n\t\t\tstruct QCC_typeparam_s *p;\n\t\t\tif (t->type == ev_field)\n\t\t\t\tt = t->aux_type;\n\t\t\telse if (t->type == ev_pointer && !arraysize)\n\t\t\t{\n\t\t\t\tt = t->aux_type;\n\t\t\t\tif (dereference)\n\t\t\t\t{\n\t\t\t\t\tr = QCC_PR_BuildRef(retbuf, REF_POINTER, QCC_RefToDef(r, true), idx, t, false, bitofs);\n\t\t\t\t\tidx = nullsref;\n\t\t\t\t}\n\t\t\t\tdereference = true;\n\t\t\t}\n\t\t\ttname = t->name;\n\n\t\t\tif (t->type == ev_struct || t->type == ev_union)\n\t\t\t{\n\t\t\t\tif (!t->size)\n\t\t\t\t\tQCC_PR_ParseError(0, \"%s was not defined yet\", tname);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tchar typea[256];\n\t\t\t\tchar typeb[256];\n\t\t\t\tTypeName(t, typea, sizeof(typea));\n\t\t\t\tif (idx.cast)\n\t\t\t\t{\n\t\t\t\t\tTypeName(idx.cast, typeb, sizeof(typeb));\n\t\t\t\t\tQCC_PR_ParseError(0, \"indirection in %s [%s %s] - not a struct or union\", typea, typeb, idx.sym->name);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tQCC_PR_ParseError(0, \"indirection in %s - not a struct or union\", typea);\n\t\t\t}\n\n\t\t\tmname = QCC_PR_ParseName();\n\t\t\tp = QCC_PR_FindStructMember(t, mname, &ofs, &mbitofs);\n\t\t\tif (!p)\n\t\t\t{\t//check for static or non-virtual-function\n\t\t\t\tQCC_type_t *c;\n\t\t\t\tchar membername[2048];\n\t\t\t\tfor (c = t; c; c = c->parentclass)\n\t\t\t\t{\n\t\t\t\t\tQC_snprintfz(membername, sizeof(membername), \"%s::%s\", c->name, mname);\n\t\t\t\t\ttmp = QCC_PR_GetSRef(NULL, membername, NULL, false, 0, false);\n\t\t\t\t\tif (tmp.cast)\n\t\t\t\t\t{\t//static or non-virtual\n\t\t\t\t\t\tQCC_sref_t base = nullsref;\t//for static\n\n\t\t\t\t\t\tr = QCC_PR_BuildRef(retbuf, base.cast?REF_THISCALL:REF_GLOBAL, tmp, base, tmp.cast, tmp.sym->constant, 0);\n\t\t\t\t\t\treturn QCC_PR_ParseRefArrayPointer(retbuf, r, allowarrayassign, makearraypointers);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tQCC_PR_ParseWarning(ERR_BADMEMBER, \"%s is not a member of %s\", mname, t->name);\n\t\t\t\tif (t->filen)\n\t\t\t\t\tQCC_PR_Note(ERR_BADMEMBER, t->filen, t->line, \"%s is defined here\", t->name);\n\t\t\t\tQCC_PR_ParseError(ERR_BADMEMBER, NULL);\n\t\t\t}\n\n\t\t\tif (idx.cast && p->type->align != t->align)\t//switching alignment requires dealing with what the previous was defined as. should onlly switch to tighter alignment\n\t\t\t\tidx = QCC_PR_Statement(&pr_opcodes[OP_MUL_I], QCC_SupplyConversion(idx, ev_integer, true), QCC_MakeIntConst(t->align/p->type->align), NULL);\n\n\t\t\tif(p->type->align == 8)\n\t\t\t{\n\t\t\t\tofs*=(32/p->type->align);\n\t\t\t\tofs+=(mbitofs>>3);\n\t\t\t\tmbitofs &= 7;\n\t\t\t}\n\t\t\telse if(p->type->align == 16)\n\t\t\t{\n\t\t\t\tofs*=(32/p->type->align);\n\t\t\t\tofs+=(mbitofs>>4);\n\t\t\t\tmbitofs &= 15;\n\t\t\t}\n\t\t\telse\n\t\t\t\tofs *= (32/p->type->align);\n\n\t\t\tif (!ofs && idx.cast)\n\t\t\t\t;\n\t\t\telse if (QCC_OPCodeValid(&pr_opcodes[OP_ADD_I]))\n\t\t\t{\n\t\t\t\ttmp = QCC_MakeIntConst(ofs);\n\t\t\t\tif (idx.cast)\n\t\t\t\t\tidx = QCC_PR_Statement(&pr_opcodes[OP_ADD_I], QCC_SupplyConversion(idx, ev_integer, true), tmp, NULL);\n\t\t\t\telse\n\t\t\t\t\tidx = tmp;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\ttmp = QCC_MakeFloatConst(ofs*(32/p->type->align));\n\t\t\t\tif (idx.cast)\n\t\t\t\t\tidx = QCC_PR_Statement(&pr_opcodes[OP_ADD_F], QCC_SupplyConversion(idx, ev_float, true), QCC_SupplyConversion(tmp, ev_float, true), NULL);\n\t\t\t\telse\n\t\t\t\t\tidx = tmp;\n\t\t\t}\n\t\t\tarraysize = p->arraysize;\n\t\t\tt = p->type;\n\n\t\t\tbitofs += mbitofs;\n\n\t\t\tif (fld)\n\t\t\t\tt = QCC_PR_FieldType(t);\n\t\t}\n\t\telse\n\t\t\tbreak;\n\t\tdorecurse = true;\n\n\t\tif (t->type == ev_pointer && !arraysize)\n\t\t{\n\t\t\tQCC_sref_t base;\n\t\t\tif (r->type == REF_ARRAYHEAD)\n\t\t\t{\n\t\t\t\tr->type = REF_ARRAY;\n\t\t\t\tr->cast = type_void;\n\t\t\t\tbase = QCC_RefToDef(r, true);\n\t\t\t\tr->type = REF_ARRAYHEAD;\n\t\t\t}\n\t\t\telse\n\t\t\t\tbase = QCC_RefToDef(r, true);\n\n\t\t\tif (t->type == ev_union && t->num_parms == 1 && !t->params[0].paramname)\t//FIXME: this destroys type info required for sizeof, and breaks struct lvalues.\n\t\t\t{\n\t\t\t\tarraysize = t->params[0].arraysize;\n\t\t\t\tt = t->params[0].type;\n\t\t\t}\n\n//QCC_PR_StatementAnnotation(\"ParseRefArrayPointer:%i %s->\",__LINE__, reftypename[r->type]);\n\t\t\tif (dereference)\n\t\t\t\tr = QCC_PR_BuildRef(retbuf, REF_POINTER, base, idx, t, r->readonly, bitofs);\n\t\t\telse\n\t\t\t\tr = QCC_PR_BuildRef(retbuf, REF_ARRAY, base, idx, t, r->readonly, bitofs);\n//QCC_PR_StatementAnnotation(\"->%s\", reftypename[r->type]);\n\t\t\treturn QCC_PR_ParseRefArrayPointer(retbuf, r, allowarrayassign, makearraypointers);\n\t\t}\n\n\t}\n\n\tif (idx.cast)\n\t{\n\t\tQCC_sref_t base;\n\t\tif (r->type == REF_ARRAYHEAD)\n\t\t{\n\t\t\tr->type = REF_ARRAY;\n\t\t\tr->cast = type_void;\n\t\t\tbase = QCC_RefToDef(r, true);\n\t\t\tr->type = REF_ARRAYHEAD;\n\t\t}\n\t\telse\n\t\t\tbase = QCC_RefToDef(r, true);\n\n\t\tif (t->type == ev_union && t->num_parms == 1 && !t->params[0].paramname && makearraypointers)\t//FIXME: this destroys type info required for sizeof, and breaks struct lvalues.\n\t\t{\n\t\t\tarraysize = t->params[0].arraysize;\n\t\t\tt = t->params[0].type;\n\t\t}\n\n\t\t//okay, not a pointer, we'll have to read it in somehow\n//QCC_PR_StatementAnnotation(\"ParseRefArrayPointer:%i %s->\",__LINE__, reftypename[r->type]);\n\t\tif (arraysize && makearraypointers)\n\t\t{\n\t\t\tif (dereference)\n\t\t\t\tr = QCC_PR_BuildRef(retbuf, REF_POINTERARRAY, base, idx, QCC_PR_PointerType(t), r->readonly, bitofs);\n\t\t\telse\n\t\t\t\tr = QCC_PR_BuildRef(retbuf, REF_ARRAYHEAD, base, idx, QCC_PR_PointerType(t), r->readonly, bitofs);\n\t\t\tr->arraysize = arraysize;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (dereference)\n\t\t\t\tr = QCC_PR_BuildRef(retbuf, REF_POINTER, base, idx, t, r->readonly, bitofs);\n\t\t\telse\n\t\t\t\tr = QCC_PR_BuildRef(retbuf, REF_ARRAY, base, idx, t, r->readonly, bitofs);\n\t\t\tr->arraysize = arraysize;\n\t\t}\n//QCC_PR_StatementAnnotation(\"->%s\", reftypename[r->type]);\n\n\t\t//parse recursively\n\t\tif (dorecurse)\n\t\t\tr = QCC_PR_ParseRefArrayPointer(retbuf, r, allowarrayassign, makearraypointers);\n\t}\n\telse\n\t\tr->bitofs += bitofs;\n\n\tr = QCC_PR_ParseField(retbuf, r);\n\treturn r;\n}\n\nQCC_sref_t QCC_PR_GenerateVector(QCC_sref_t x, QCC_sref_t y, QCC_sref_t z)\n{\n\tQCC_sref_t\t\td;\n\tconst QCC_eval_t *c[3];\n\n\tif (x.cast->type != ev_float && x.cast->type != ev_integer)\n\t\tx = QCC_EvaluateCast(x, type_float, true);\n\tif (y.cast->type != ev_float && y.cast->type != ev_integer)\n\t\ty = QCC_EvaluateCast(y, type_float, true);\n\tif (z.cast->type != ev_float && z.cast->type != ev_integer)\n\t\tz = QCC_EvaluateCast(z, type_float, true);\n\n\tif ((x.cast->type != ev_float && x.cast->type != ev_integer) ||\n\t\t(y.cast->type != ev_float && y.cast->type != ev_integer) ||\n\t\t(z.cast->type != ev_float && z.cast->type != ev_integer))\n\t{\n\t\tQCC_PR_ParseError(ERR_TYPEMISMATCH, \"Argument not a single numeric value in vector constructor\");\n\t\treturn QCC_MakeVectorConst(0, 0, 0);\n\t}\n\n\t//return a constant if we can.\n\tc[0] = QCC_SRef_EvalConst(x);\n\tc[1] = QCC_SRef_EvalConst(y);\n\tc[2] = QCC_SRef_EvalConst(z);\n\tif (c[0] && c[1] && c[2])\n\t{\n\t\td = QCC_MakeVectorConst(\n\t\t\tQCC_Eval_Float(c[0], x.cast),\n\t\t\tQCC_Eval_Float(c[1], y.cast),\n\t\t\tQCC_Eval_Float(c[2], z.cast));\n\t\tQCC_FreeTemp(x);\n\t\tQCC_FreeTemp(y);\n\t\tQCC_FreeTemp(z);\n\t\treturn d;\n\t}\n\tif (QCC_SRef_IsNull(y) && QCC_SRef_IsNull(z))\n\t{\n\t\tQCC_FreeTemp(y);\n\t\tQCC_FreeTemp(z);\n\t\treturn QCC_PR_StatementFlags(pr_opcodes + OP_MUL_VF, QCC_MakeVectorConst(1, 0, 0), QCC_SupplyConversion(x, ev_float, true), NULL, 0);\n\t}\n\tif (QCC_SRef_IsNull(x) && QCC_SRef_IsNull(z))\n\t{\n\t\tQCC_FreeTemp(x);\n\t\tQCC_FreeTemp(z);\n\t\treturn QCC_PR_StatementFlags(pr_opcodes + OP_MUL_VF, QCC_MakeVectorConst(0, 1, 0), QCC_SupplyConversion(y, ev_float, true), NULL, 0);\n\t}\n\tif (QCC_SRef_IsNull(x) && QCC_SRef_IsNull(y))\n\t{\n\t\tQCC_FreeTemp(x);\n\t\tQCC_FreeTemp(y);\n\t\treturn QCC_PR_StatementFlags(pr_opcodes + OP_MUL_VF, QCC_MakeVectorConst(0, 0, 1), QCC_SupplyConversion(z, ev_float, true), NULL, 0);\n\t}\n\n\t//pack the variables into a vector\n\td = QCC_GetTemp(type_vector);\n\td.cast = type_float;\n\tif (x.cast->type == ev_float)\n\t\tQCC_PR_StatementFlags(pr_opcodes + OP_STORE_F, x, d, NULL, STFL_PRESERVEB|STFL_DISCARDRESULT);\n\telse\n\t\tQCC_PR_StatementFlags(pr_opcodes+OP_STORE_IF, x, d, NULL, STFL_PRESERVEB|STFL_DISCARDRESULT);\n\td.ofs++;\n\tif (y.cast->type == ev_float)\n\t\tQCC_PR_StatementFlags(pr_opcodes + OP_STORE_F, y, d, NULL, STFL_PRESERVEB|STFL_DISCARDRESULT);\n\telse\n\t\tQCC_PR_StatementFlags(pr_opcodes+OP_STORE_IF, y, d, NULL, STFL_PRESERVEB|STFL_DISCARDRESULT);\n\td.ofs++;\n\tif (z.cast->type == ev_float)\n\t\tQCC_PR_StatementFlags(pr_opcodes + OP_STORE_F, z, d, NULL, STFL_PRESERVEB|STFL_DISCARDRESULT);\n\telse\n\t\tQCC_PR_StatementFlags(pr_opcodes+OP_STORE_IF, z, d, NULL, STFL_PRESERVEB|STFL_DISCARDRESULT);\n\td.ofs++;\n\td.ofs -= 3;\n\td.cast = type_vector;\n\n\treturn d;\n}\n\n/*\n============\nPR_ParseValue\n\nReturns the global ofs for the current token\n============\n*/\nQCC_ref_t\t*QCC_PR_ParseRefValue (QCC_ref_t *refbuf, QCC_type_t *assumeclass, pbool allowarrayassign, pbool expandmemberfields, pbool makearraypointers)\n{\n\tQCC_sref_t\t\td;\n\tQCC_type_t\t\t*t;\n\tchar\t\t*name;\n\tQCC_ref_t *r;\n\n\tchar membername[2048];\n\n// if the token is an immediate, allocate a constant for it\n\tif (pr_token_type == tt_immediate)\n\t{\n\t\td = QCC_PR_ParseImmediate ();\n//\t\td.sym->referenced = true;\n//\t\treturn QCC_DefToRef(refbuf, d);\n\t\tname = NULL;\n\t}\n\telse if (QCC_PR_CheckToken(\"[\"))\n\t{\n\t\t//originally used for reacc - taking the form of [5 84 2]\n\t\t//we redefine it to include statements - [a+b, c, 3+(d*2)]\n\t\t//and to not need the 2nd/3rd parts if you're lazy - [5] or [5,6] - FIXME: should we accept 1-d vector? or is that too risky with arrays and weird error messages?\n\t\t//note the addition of commas.\n\t\t//if we're parsing reacc code, we will still accept [(a+b) c (3+(d*2))], as QCC_PR_Term contains the () handling. We do also allow optional commas.\n\t\tQCC_sref_t x,y,z;\n\t\tif (flag_acc)\n\t\t{\n\t\t\tx = QCC_PR_Term(EXPR_DISALLOW_COMMA);\n\t\t\tQCC_PR_CheckToken(\",\");\n\t\t\ty = QCC_PR_Term(EXPR_DISALLOW_COMMA);\n\t\t\tQCC_PR_CheckToken(\",\");\n\t\t\tz = QCC_PR_Term(EXPR_DISALLOW_COMMA);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tx = QCC_PR_Expression(TOP_PRIORITY, EXPR_DISALLOW_COMMA);\n\n\t\t\tif (QCC_PR_CheckToken(\",\"))\n\t\t\t\ty = QCC_PR_Expression(TOP_PRIORITY, EXPR_DISALLOW_COMMA);\n\t\t\telse\n\t\t\t\ty = QCC_MakeFloatConst(0);\n\n\t\t\tif (QCC_PR_CheckToken(\",\"))\n\t\t\t\tz = QCC_PR_Expression(TOP_PRIORITY, EXPR_DISALLOW_COMMA);\n\t\t\telse\n\t\t\t\tz = QCC_MakeFloatConst(0);\n\t\t}\n\n\t\tQCC_PR_Expect(\"]\");\n\n\t\td = QCC_PR_GenerateVector(x,y,z);\n//\t\td.sym->referenced = true;\n//\t\treturn QCC_DefToRef(refbuf, d);\n\t\tname = NULL;\n\t}\n\telse\n\t{\n\t\tif (QCC_PR_CheckToken(\"::\"))\n\t\t{\n\t\t\tassumeclass = NULL;\n\t\t\texpandmemberfields = false;\t//::classname is always usable for eg: the find builtin.\n\t\t}\n\t\tname = QCC_PR_ParseName ();\n\n\t\t//fixme: namespaces should be relative\n\t\tif (QCC_PR_CheckToken(\"::\"))\n\t\t{\n\t\t\tstruct accessor_s *a;\n\t\t\tQCC_type_t *p;\n\t\t\tchar membername[1024];\n\t\t\texpandmemberfields = false;\t//this::classname should also be available to the find builtin, etc. this won't affect self.classname::member nor classname::staticfunc\n\n\t\t\tif (assumeclass && !strcmp(name, \"super\"))\n\t\t\t\tt = assumeclass->parentclass;\n\t\t\telse if (assumeclass && !strcmp(name, \"this\"))\n\t\t\t\tt = assumeclass;\n\t\t\telse\n\t\t\t\tt = QCC_TypeForName(name);\n\t\t\tif (!t)\n\t\t\t{\n\t\t\t\td = QCC_PR_GetSRef (pr_assumetermtype, name, pr_assumetermscope, false, 0, pr_assumetermflags);\n\t\t\t\tif (d.cast)\n\t\t\t\t{\n\t\t\t\t\tQCC_FreeTemp(d);\n\t\t\t\t\tt = d.cast;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tQCC_PR_ParseError (ERR_UNKNOWNVALUE, \"\\\"%s\\\" is not a type\", name);\n\t\t\t}\n\n\t\t\tname = QCC_PR_ParseName ();\n\t\t\t//walk up the parents if needed, to find one that has that field\n\t\t\tfor(d = nullsref, p = t; ; )\n\t\t\t{\n\t\t\t\tif (!d.cast && p->accessors)\n\t\t\t\t{\n\t\t\t\t\tfor (a = t->accessors; a; a = a->next)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (!strcmp(a->fieldname, name))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\td = a->staticval;\n\t\t\t\t\t\t\tQCC_ForceUnFreeDef(d.sym);\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif (d.cast)\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tif (!d.cast && t->type == ev_entity)\n\t\t\t\t{\n\t\t\t\t\t//use static functions in preference to virtual functions. kinda needed so you can use super::func...\n\t\t\t\t\tQC_snprintfz(membername, sizeof(membername), \"%s::%s\", p->name, name);\n\t\t\t\t\td = QCC_PR_GetSRef (NULL, membername, pr_scope, false, 0, false);\n\t\t\t\t\tif (!d.cast)\n\t\t\t\t\t{\n\t\t\t\t\t\tQC_snprintfz(membername, sizeof(membername), \"%s::\"MEMBERFIELDNAME, p->name, name);\n\t\t\t\t\t\td = QCC_PR_GetSRef (NULL, membername, pr_scope, false, 0, false);\n\t\t\t\t\t}\n\n\t\t\t\t\tp = p->parentclass;\n\t\t\t\t\tif (p)\n\t\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (!d.cast && t->type == ev_struct)\n\t\t\t\t{\n\t\t\t\t\t//use static functions in preference to virtual functions. kinda needed so you can use super::func...\n\t\t\t\t\tQC_snprintfz(membername, sizeof(membername), \"%s::%s\", p->name, name);\n\t\t\t\t\td = QCC_PR_GetSRef (NULL, membername, pr_scope, false, 0, false);\n\t\t\t\t\tp = p->parentclass;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (!d.cast)\n\t\t\t{\n\t\t\t\tQCC_PR_ParseError (ERR_UNKNOWNVALUE, \"Unknown value \\\"%s::%s\\\"\", t->name, name);\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\td = nullsref;\n\t\t\t// 'testvar' becomes 'this::testvar'\n\t\t\tif (assumeclass && assumeclass->parentclass)\n\t\t\t{\t//try getting a member.\n\t\t\t\tQCC_type_t *type;\n\t\t\t\tif (assumeclass->type == ev_struct)\n\t\t\t\t{\n\t\t\t\t\tunsigned int ofs, bitofs;\n\t\t\t\t\tstruct QCC_typeparam_s *p = QCC_PR_FindStructMember(assumeclass, name, &ofs, &bitofs);\n\t\t\t\t\tif (p)\n\t\t\t\t\t{\n\t\t\t\t\t\tQCC_sref_t\t\tths;\n\t\t\t\t\t\tths = QCC_PR_GetSRef(QCC_PR_PointerType(pr_classtype), \"this\", pr_scope, false, 0, false);\n\t\t\t\t\t\tif (ths.cast)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tths.cast = QCC_PR_PointerType(p->type);\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\tif (d.sym->arraysize)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t//FIXME: this should result in a pointer type, and not this->member[0]\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\treturn QCC_PR_ParseRefArrayPointer(refbuf, QCC_PR_BuildRef(refbuf, REF_POINTER, ths, QCC_MakeIntConst(ofs), p->type, false, bitofs), allowarrayassign, makearraypointers);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tfor(type = assumeclass; type && !d.cast; type = type->parentclass)\n\t\t\t\t\t{\n\n\t\t\t\t\t\t//look for virtual things\n\t\t\t\t\t\tQC_snprintfz(membername, sizeof(membername), \"%s::\"MEMBERFIELDNAME, type->name, name);\n\t\t\t\t\t\td = QCC_PR_GetSRef (NULL, membername, pr_scope, false, 0, false);\n\t\t\t\t\t}\n\t\t\t\t\tfor(type = assumeclass; type && !d.cast; type = type->parentclass)\n\t\t\t\t\t{\n\t\t\t\t\t\t//look for non-virtual things (functions: after virtual stuff, because this will find the actual function def too)\n\t\t\t\t\t\tQC_snprintfz(membername, sizeof(membername), \"%s::%s\", type->name, name);\n\t\t\t\t\t\td = QCC_PR_GetSRef (NULL, membername, pr_scope, false, 0, false);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!d.cast)\n\t\t\t{\n\t\t\t\t// look through the defs\n\t\t\t\td = QCC_PR_GetSRef (NULL, name, pr_scope, false, 0, false);\n\t\t\t}\n\t\t}\n\n\t\tif (!d.cast)\n\t\t{\n\t\t\tif (!strcmp(name, \"nil\"))\n\t\t\t\td = QCC_MakeIntConst(0);\n\t\t\telse if (\t(!strcmp(name, \"randomv\"))\t||\n\t\t\t\t\t(!strcmp(name, \"alloca\"))\t||\n\t\t\t\t\t(!strcmp(name, \"entnum\"))\t||\n\t\t\t\t\t(!strcmp(name, \"autocvar\"))\t||\n\t\t\t\t\t(!strcmp(name, \"used_model\"))\t||\n\t\t\t\t\t(!strcmp(name, \"used_sound\"))\t||\n\t\t\t\t\t(!strcmp(name, \"va_arg\"))\t||\n\t\t\t\t\t(!strcmp(name, \"...\"))\t\t||\t//for compat. otherwise wtf?\n\t\t\t\t\t(!strcmp(name, \"_\"))\t\t)\t//intrinsics, any old function with no args will do.\n\t\t\t{\n\t\t\t\td = QCC_PR_GetSRef (type_function, name, NULL, true, 0, false);\n\t//\t\t\td->initialized = 0;\n\t\t\t}\n\t\t\telse if (\t(!strcmp(name, \"random\" ))\t)\t//intrinsics, any old function with no args will do. returning a float just in case people declare things in the wrong order\n\t\t\t{\n\t\t\t\td = QCC_PR_GetSRef (type_floatfunction, name, NULL, true, 0, false);\n\t//\t\t\td.sym->initialized = 0;\n\t\t\t}\n\t\t\telse if (keyword_class && !strcmp(name, \"this\"))\n\t\t\t{\n\t\t\t\tif (!pr_classtype)\n\t\t\t\t\tQCC_PR_ParseError(ERR_NOTANAME, \"Cannot use 'this' outside of an OO function\\n\");\n\t\t\t\td = QCC_PR_GetSRef(type_entity, \"self\", NULL, true, 0, false);\n\t\t\t\td.cast = pr_classtype;\n\t\t\t}\n\t\t\telse if (keyword_class && !strcmp(name, \"super\"))\n\t\t\t{\n\t\t\t\tif (!assumeclass)\n\t\t\t\t\tQCC_PR_ParseError(ERR_NOTANAME, \"Cannot use 'super' outside of an OO function\\n\");\n\t\t\t\tif (!assumeclass->parentclass)\n\t\t\t\t\tQCC_PR_ParseError(ERR_NOTANAME, \"class %s has no super\\n\", pr_classtype->name);\n\t\t\t\td = QCC_PR_GetSRef(NULL, \"self\", NULL, true, 0, false);\n\t\t\t\td.cast = assumeclass->parentclass;\n\t\t\t}\n\t\t\telse if (pr_assumetermtype)\n\t\t\t{\n\t\t\t\td = QCC_PR_GetSRef (pr_assumetermtype, name, pr_assumetermscope, true, 0, pr_assumetermflags);\n\t\t\t\tif (!d.cast)\n\t\t\t\t\tQCC_PR_ParseError (ERR_UNKNOWNVALUE, \"Unknown value \\\"%s\\\"\", name);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\td = QCC_PR_GetSRef (type_variant, name, pr_scope, true, 0, false);\n\t\t\t\tif (!expandmemberfields && assumeclass)\n\t\t\t\t{\n\t\t\t\t\tif (!d.cast)\n\t\t\t\t\t\tQCC_PR_ParseError (ERR_UNKNOWNVALUE, \"Unknown field \\\"%s\\\" in class \\\"%s\\\"\", name, assumeclass->name);\n\t\t\t\t\telse if (!assumeclass->parentclass && assumeclass != type_entity)\n\t\t\t\t\t{\n\t\t\t\t\t\tQCC_PR_ParseWarning (ERR_UNKNOWNVALUE, \"Class \\\"%s\\\" is not defined, cannot access member \\\"%s\\\"\", assumeclass->name, name);\n\t\t\t\t\t\tif (!autoprototype && !autoprototyped)\n\t\t\t\t\t\t\tQCC_PR_Note(ERR_UNKNOWNVALUE, s_filen, pr_source_line, \"Consider using #pragma autoproto\");\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tQCC_PR_ParseWarning (ERR_UNKNOWNVALUE, \"Unknown field \\\"%s\\\" in class \\\"%s\\\"\", name, assumeclass->name);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tif (!d.cast)\n\t\t\t\t\t\tQCC_PR_ParseError (ERR_UNKNOWNVALUE, \"Unknown value \\\"%s\\\"\", name);\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tQCC_PR_ParseWarning (ERR_UNKNOWNVALUE, \"Unknown value \\\"%s\\\".\", name);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\td.sym->referenced = true;\n\n\t//class code uses self as though it was 'this'. its a hack, but this is QC.\n\tif (assumeclass && pr_classtype && name && !strcmp(name, \"self\"))\n\t{\n\t\t//use 'this' instead.\n\t\tQCC_sref_t t = QCC_PR_GetSRef(NULL, \"this\", pr_scope, false, 0, false);\n\t\tif (!t.cast)\t//shouldn't happen.\n\t\t{\n\t\t\tt.sym = QCC_PR_DummyDef(pr_classtype, \"this\", pr_scope, 0, d.sym, 0, true, GDF_CONST|GDF_STRIP|GDF_ALIAS);\n\t\t\tt.cast = pr_classtype;\n\t\t\tt.ofs = 0;\n\t\t}\n\t\telse\n\t\t\tQCC_FreeTemp(d);\n\t\td = t;\n\t\tQCC_PR_ParseWarning (WARN_SELFNOTTHIS, \"'self' used inside OO function, use 'this'.\");\n\t}\n\n\tif (!d.cast)\n\t\tQCC_PR_ParseError (ERR_INTERNAL, \"d.cast == NULL\");\n\n\t//within class functions, refering to fields should use them directly.\n\tif (pr_classtype && expandmemberfields && d.cast->type == ev_field)\n\t{\n\t\tQCC_sref_t t;\n\t\tif (assumeclass)\n\t\t{\n\t\t\tt = QCC_PR_GetSRef(NULL, \"this\", pr_scope, false, 0, false);\n\t\t\tif (!t.cast)\n\t\t\t{\n\t\t\t\tt.sym = QCC_PR_DummyDef(pr_classtype, \"this\", pr_scope, 0, QCC_PR_GetDef(NULL, \"self\", NULL, true, 0, false), 0, true, GDF_CONST|GDF_STRIP|GDF_ALIAS);\t//create a union into it\n\t\t\t\tt.cast = pr_classtype;\n\t\t\t\tt.ofs = 0;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t\tt = QCC_PR_GetSRef(NULL, \"self\", NULL, true, 0, false);\n\n\t\tif (d.sym->arraysize)\n\t\t{\n\t\t\tQCC_DefToRef(refbuf, d);\n\t\t\trefbuf->type = REF_ARRAYHEAD;\n\t\t\trefbuf->arraysize = d.sym->arraysize;\n\t\t\trefbuf->cast = QCC_PointerTypeTo(refbuf->cast);\n\t\t\td = QCC_RefToDef(QCC_PR_ParseRefArrayPointer(refbuf, refbuf, allowarrayassign, makearraypointers), true);\n\t\t}\n\t\telse\n\t\t\td = QCC_PR_ParseArrayPointer(d, allowarrayassign, makearraypointers); //opportunistic vecmember[0] handling\n\n\t\t//then return a reference to this.field\n\t\tQCC_PR_BuildRef(refbuf, REF_FIELD, t, d, d.cast->aux_type, false, 0);\n\n\t\t//(a.(foo[4]))[2] should still function, and may be common with field vectors\n\t\treturn QCC_PR_ParseRefArrayPointer(refbuf, refbuf, allowarrayassign, makearraypointers); //opportunistic vecmember[0] handling\n\t}\n\n\tt = d.cast;\n\tif (d.sym->arraysize)\n\t{\n\t\tQCC_DefToRef(refbuf, d);\n\t\trefbuf->type = REF_ARRAYHEAD;\n\t\trefbuf->arraysize = d.sym->arraysize;\n\t\trefbuf->cast = QCC_PointerTypeTo(refbuf->cast);\n\t\tr = QCC_PR_ParseRefArrayPointer(refbuf, refbuf, allowarrayassign, makearraypointers);\n\t\tif (r->type == REF_ARRAYHEAD && flag_brokenarrays)\n\t\t{\n\t\t\tr->type = REF_GLOBAL;\n\t\t\tr->cast = r->cast->aux_type;\n\t\t}\n\t\t/*if (r->type == REF_ARRAYHEAD)\n\t\t{\n\t\t\tr->type = REF_GLOBAL;\n\t\t\treturn QCC_PR_GenerateAddressOf(refbuf, r);\n\t\t}*/\n\t\treturn r;\n\t}\n\telse if (t->type == ev_union && t->num_parms == 1 && !t->params[0].paramname)\t//FIXME: this destroys type info required for sizeof, and breaks struct lvalues.\n\t{\t//convert it to a proper array, instead of leaving as a simple global.\n\t\tQCC_DefToRef(refbuf, d);\n\t\trefbuf->type = REF_ARRAYHEAD;\n\t\trefbuf->arraysize = t->params[0].arraysize;\n\t\trefbuf->cast = QCC_PointerTypeTo(t->params[0].type);\n\t\treturn QCC_PR_ParseRefArrayPointer(refbuf, refbuf, allowarrayassign, makearraypointers);\n\t}\n\n\telse if (d.sym->autoderef)\n\t{\n\t\tt = t->aux_type;\n\t\tif (t->type == ev_union && t->num_parms == 1 && !t->params[0].paramname)\t//FIXME: this destroys type info required for sizeof, and breaks struct lvalues.\n\t\t{\n\t\t\tr = QCC_PR_BuildRef(refbuf, REF_POINTERARRAY, d, nullsref, QCC_PointerTypeTo(t->params[0].type), false, 0);\t//it points to an array, which resolves to a pointer to its base type...\n\t\t\tr->arraysize = t->params[0].arraysize;\t//make sure it has the proper size etc in case someone typedefs it.\n\t\t}\n\t\telse\n\t\t\tr = QCC_PR_BuildRef(refbuf, REF_POINTER, d, nullsref, t, false, 0); // just dereference it before parsing any array/fields/etc stuff.\n\t}\n\telse\n\t\tr = QCC_DefToRef(refbuf, d);\n\treturn QCC_PR_ParseRefArrayPointer(refbuf, r, allowarrayassign, makearraypointers);\n}\n\n//true if its NOT 0\nQCC_sref_t QCC_PR_GenerateLogicalTruth(QCC_sref_t e, const char *errormessage)\n{\n\tetype_t\tt;\n\tQCC_type_t *type = e.cast;\n\twhile(type->type == ev_accessor || type->type == ev_boolean)\n\t\ttype = type->parentclass;\n\tt = type->type;\n\tif (t == ev_float)\n\t\treturn QCC_PR_Statement (&pr_opcodes[OP_NE_F], e, QCC_MakeFloatConst(0), NULL);\n\telse if (t == ev_string)\n\t\treturn QCC_PR_Statement (&pr_opcodes[flag_brokenifstring?OP_NE_E:OP_NE_S], e, QCC_MakeIntConst(0), NULL);\n\telse if (t == ev_entity)\n\t\treturn QCC_PR_Statement (&pr_opcodes[OP_NE_E], e, QCC_MakeIntConst(0), NULL);\n\telse if (t == ev_vector)\n\t\treturn QCC_PR_Statement (&pr_opcodes[OP_NE_V], e, QCC_MakeVectorConst(0,0,0), NULL);\n\telse if (t == ev_function)\n\t\treturn QCC_PR_Statement (&pr_opcodes[OP_NE_FNC], e, QCC_MakeIntConst(0), NULL);\n\telse if (t == ev_integer || t == ev_uint)\n\t\treturn QCC_PR_Statement (&pr_opcodes[OP_NE_I], e, QCC_MakeIntConst(0), NULL);\t//functions are integer values too.\n\telse if (t == ev_pointer)\n\t\treturn QCC_PR_Statement (&pr_opcodes[OP_NE_I], e, QCC_MakeIntConst(0), NULL);\t//Pointers are too.\n\telse if (t == ev_double)\n\t\treturn QCC_PR_Statement (&pr_opcodes[OP_NE_D], e, QCC_MakeDoubleConst(0), NULL);\n\telse if (t == ev_int64)\n\t\treturn QCC_PR_Statement (&pr_opcodes[OP_NE_I64], e, QCC_MakeInt64Const(0), NULL);\n\telse if (t == ev_uint64)\n\t\treturn QCC_PR_Statement (&pr_opcodes[OP_NE_U64], e, QCC_MakeUInt64Const(0), NULL);\n\telse if (t == ev_void && flag_laxcasts)\n\t{\n\t\tQCC_PR_ParseWarning(WARN_LAXCAST, errormessage, \"void\");\n\t\treturn QCC_PR_Statement (&pr_opcodes[OP_NE_F], e, QCC_MakeFloatConst(0), NULL);\n\t}\n\telse\n\t{\n\t\tchar etype[256];\n\t\tTypeName(e.cast, etype, sizeof(etype));\n\n\t\tQCC_PR_ParseError (ERR_BADNOTTYPE, errormessage, etype);\n\t\treturn nullsref;\n\t}\n}\n\nQCC_sref_t QCC_PR_GenerateLogicalNot(QCC_sref_t e, const char *errormessage)\n{\n\tetype_t\tt;\n\tQCC_type_t *type = e.cast;\n\twhile(type->type == ev_accessor || type->type == ev_boolean)\n\t\ttype = type->parentclass;\n\tt = type->type;\n\tif (t == ev_float)\n\t\treturn QCC_PR_Statement (&pr_opcodes[OP_NOT_F], e, nullsref, NULL);\n\telse if (t == ev_string)\n\t\treturn QCC_PR_Statement (&pr_opcodes[flag_brokenifstring?OP_NOT_ENT:OP_NOT_S], e, nullsref, NULL);\n\telse if (t == ev_entity)\n\t\treturn QCC_PR_Statement (&pr_opcodes[OP_NOT_ENT], e, nullsref, NULL);\n\telse if (t == ev_vector)\n\t\treturn QCC_PR_Statement (&pr_opcodes[OP_NOT_V], e, nullsref, NULL);\n\telse if (t == ev_function)\n\t\treturn QCC_PR_Statement (&pr_opcodes[OP_NOT_FNC], e, nullsref, NULL);\n\telse if (t == ev_integer || t == ev_uint)\n\t\treturn QCC_PR_Statement (&pr_opcodes[OP_NOT_I], e, nullsref, NULL);\t//functions are integer values too.\n\telse if (t == ev_pointer)\n\t\treturn QCC_PR_Statement (&pr_opcodes[OP_NOT_I], e, nullsref, NULL);\t//Pointers are too.\n\telse if (t == ev_double)\n\t\treturn QCC_PR_Statement (&pr_opcodes[OP_EQ_D], e, QCC_MakeDoubleConst(0), NULL);\n\telse if (t == ev_int64)\n\t\treturn QCC_PR_Statement (&pr_opcodes[OP_EQ_I64], e, QCC_MakeInt64Const(0), NULL);\n\telse if (t == ev_uint64)\n\t\treturn QCC_PR_Statement (&pr_opcodes[OP_EQ_U64], e, QCC_MakeUInt64Const(0), NULL);\n\telse if (t == ev_void && flag_laxcasts)\n\t{\n\t\tQCC_PR_ParseWarning(WARN_LAXCAST, errormessage, \"void\");\n\t\treturn QCC_PR_Statement (&pr_opcodes[OP_NOT_F], e, nullsref, NULL);\n\t}\n\telse\n\t{\n\t\tchar etype[256];\n\t\tTypeName(e.cast, etype, sizeof(etype));\n\n\t\tQCC_PR_ParseError (ERR_BADNOTTYPE, errormessage, etype);\n\t\treturn nullsref;\n\t}\n}\nQCC_sref_t QCC_PR_GenerateBitwiseNot(QCC_sref_t e, const char *errormessage)\n{\n\tetype_t\tt;\n\tQCC_type_t *type = e.cast;\n\twhile(type->type == ev_accessor || type->type == ev_boolean)\n\t\ttype = type->parentclass;\n\tt = type->type;\n\tif (t == ev_float)\n\t\treturn QCC_PR_Statement (&pr_opcodes[OP_BITNOT_F], e, nullsref, NULL);\n\telse if (t == ev_string)\n\t\treturn QCC_PR_Statement (&pr_opcodes[flag_brokenifstring?OP_NOT_ENT:OP_NOT_S], e, nullsref, NULL);\n//\telse if (t == ev_entity)\n//\t\treturn QCC_PR_Statement (&pr_opcodes[OP_BITNOT_ENT], e, nullsref, NULL);\n\telse if (t == ev_vector)\n\t\treturn QCC_PR_Statement (&pr_opcodes[OP_BITNOT_V], e, nullsref, NULL);\n//\telse if (t == ev_function)\n//\t\treturn QCC_PR_Statement (&pr_opcodes[OP_BITNOT_FNC], e, nullsref, NULL);\n\telse if (t == ev_integer || t == ev_uint)\n\t\treturn QCC_PR_Statement (&pr_opcodes[OP_BITNOT_I], e, nullsref, NULL);\t//functions are integer values too.\n\telse if (t == ev_pointer)\n\t\treturn QCC_PR_Statement (&pr_opcodes[OP_BITNOT_I], e, nullsref, NULL);\t//Pointers are too.\n\telse if (t == ev_double)\n\t\treturn QCC_PR_Statement (&pr_opcodes[OP_BITNOT_D], e, QCC_MakeDoubleConst(0), NULL);\n\telse if (t == ev_int64)\n\t\treturn QCC_PR_Statement (&pr_opcodes[OP_BITNOT_I64], e, QCC_MakeInt64Const(0), NULL);\n\telse if (t == ev_uint64)\n\t\treturn QCC_PR_Statement (&pr_opcodes[OP_BITNOT_U64], e, QCC_MakeUInt64Const(0), NULL);\n\telse if (t == ev_void && flag_laxcasts)\n\t{\n\t\tQCC_PR_ParseWarning(WARN_LAXCAST, errormessage, \"void\");\n\t\treturn QCC_PR_Statement (&pr_opcodes[OP_NOT_F], e, nullsref, NULL);\n\t}\n\telse\n\t{\n\t\tchar etype[256];\n\t\tTypeName(e.cast, etype, sizeof(etype));\n\n\t\tQCC_PR_ParseError (ERR_BADNOTTYPE, errormessage, etype);\n\t\treturn nullsref;\n\t}\n}\n\n//doesn't consider parents\nstatic QCC_sref_t QCC_TryEvaluateCast(QCC_sref_t src, QCC_type_t *cast, pbool implicit)\n{\n\tQCC_type_t *tmp;\n\tint totype;\n\n\tfor (tmp = cast; tmp->type == ev_accessor || tmp->type == ev_bitfld; tmp = tmp->parentclass)\n\t\t;\n\ttotype = tmp->type;\n\n\twhile (src.cast->type == ev_boolean)\n\t\tsrc.cast = src.cast->parentclass;\n\n\t/*you may cast from a type to itself*/\n\tif (!typecmp(src.cast, cast))\n\t{\n\t\t//no-op\n\t}\n\t/*you may cast from const 0 to any other basic type for free (from either int or float for simplicity). things get messy when its a struct*/\n\telse if (QCC_SRef_IsNull(src) && totype != ev_struct && totype != ev_union)\n\t{\n\t\tQCC_FreeTemp(src);\n\t\tif (cast->size == 3)// || cast->type == ev_variant)\n\t\t\tsrc = QCC_MakeVectorConst(0,0,0);\n\t\telse\n\t\t\tsrc = QCC_MakeIntConst(0);\n\t\tsrc.cast = cast;\n\t}\n\telse if (totype == ev_boolean)\n\t{\n\t\tsrc = QCC_PR_GenerateLogicalTruth(src, \"cast to boolean\");\n\t\t//src will often be a float(eg:NQ_F) with a bint totype. make sure we evaluate it fully.\n\t\treturn QCC_TryEvaluateCast(src, tmp->parentclass, implicit);\n\t}\n\telse if (totype == ev_float && src.cast->type == ev_uint)\t//uint->float\n\t\tsrc = QCC_PR_Statement (&pr_opcodes[OP_CONV_UF], src, nullsref, NULL);\n\telse if (totype == ev_uint && src.cast->type == ev_float)\t//float->uint\n\t\tsrc = QCC_PR_Statement (&pr_opcodes[OP_CONV_FU], src, nullsref, NULL);\n\t/*cast from int->float will convert*/\n\telse if (totype == ev_float && (src.cast->type == ev_integer || (src.cast->type == ev_entity && !implicit)))\n\t{\n\t\tsrc = QCC_PR_Statement (&pr_opcodes[OP_CONV_ITOF], src, nullsref, NULL);\n\t\tsrc.cast = cast;\n\t}\n\t/*cast from float->int will convert*/\n\telse if ((totype == ev_integer || (totype == ev_entity && !implicit)) && src.cast->type == ev_float)\n\t{\n\t\tsrc = QCC_PR_Statement (&pr_opcodes[OP_CONV_FTOI], src, nullsref, NULL);\n\t\tsrc.cast = cast;\n\t}\n\telse if ((totype == ev_integer || totype == ev_uint) && (src.cast->type == ev_integer || src.cast->type == ev_uint))\n\t\tsrc.cast = cast;\t//fine, just treat it as-is\n\telse if ((totype == ev_int64 || totype == ev_uint64) && (src.cast->type == ev_int64 || src.cast->type == ev_uint64))\n\t\tsrc.cast = cast;\t//fine, just treat it as-is\n\n\telse if (totype == ev_float && src.cast->type == ev_double)\n\t\tsrc = QCC_PR_Statement (&pr_opcodes[OP_CONV_DF], src, nullsref, NULL);\n\telse if (totype == ev_double && src.cast->type == ev_float)\n\t\tsrc = QCC_PR_Statement (&pr_opcodes[OP_CONV_FD], src, nullsref, NULL);\n\n\telse if (totype == ev_float && src.cast->type == ev_int64)\n\t\tsrc = QCC_PR_Statement (&pr_opcodes[OP_CONV_I64F], src, nullsref, NULL);\n\telse if (totype == ev_int64 && src.cast->type == ev_float)\n\t\tsrc = QCC_PR_Statement (&pr_opcodes[OP_CONV_FI64], src, nullsref, NULL);\n\telse if (totype == ev_float && src.cast->type == ev_uint64)\n\t\tsrc = QCC_PR_Statement (&pr_opcodes[OP_CONV_U64F], src, nullsref, NULL);\n\telse if (totype == ev_uint64 && src.cast->type == ev_float)\n\t\tsrc = QCC_PR_Statement (&pr_opcodes[OP_CONV_FU64], src, nullsref, NULL);\n\n\telse if (totype == ev_uint64 && src.cast->type == ev_double)\n\t\tsrc = QCC_PR_Statement (&pr_opcodes[OP_CONV_DU64], src, nullsref, NULL);\n\telse if (totype == ev_double && src.cast->type == ev_uint64)\n\t\tsrc = QCC_PR_Statement (&pr_opcodes[OP_CONV_U64D], src, nullsref, NULL);\n\telse if (totype == ev_int64 && src.cast->type == ev_double)\n\t\tsrc = QCC_PR_Statement (&pr_opcodes[OP_CONV_DI64], src, nullsref, NULL);\n\telse if (totype == ev_double && src.cast->type == ev_int64)\n\t\tsrc = QCC_PR_Statement (&pr_opcodes[OP_CONV_I64D], src, nullsref, NULL);\n\n\telse if (totype == ev_uint && src.cast->type == ev_double)\n\t{\n\t\tsrc = QCC_PR_Statement (&pr_opcodes[OP_CONV_DU64], src, nullsref, NULL);\n\t\tsrc = QCC_PR_Statement (&pr_opcodes[OP_CONV_I64I], src, nullsref, NULL);\n\t\tsrc.cast = cast;\n\t}\n\telse if ((totype == ev_integer||totype == ev_uint) && src.cast->type == ev_double)\n\t{\n\t\tsrc = QCC_PR_Statement (&pr_opcodes[OP_CONV_DI64], src, nullsref, NULL);\n\t\tsrc = QCC_PR_Statement (&pr_opcodes[OP_CONV_I64I], src, nullsref, NULL);\n\t\tsrc.cast = cast;\n\t}\n\telse if (totype == ev_double && (src.cast->type == ev_integer || src.cast->type == ev_uint))\n\t{\n\t\tif (src.cast->type == ev_uint)\n\t\t\tsrc = QCC_PR_Statement (&pr_opcodes[OP_CONV_UI64], src, nullsref, NULL);\n\t\telse\n\t\t\tsrc = QCC_PR_Statement (&pr_opcodes[OP_CONV_II64], src, nullsref, NULL);\n\t\tsrc = QCC_PR_Statement (&pr_opcodes[OP_CONV_I64D], src, nullsref, NULL);\n\t\tsrc.cast = cast;\n\t}\n\n\n\telse if ((totype == ev_int64||totype == ev_uint64) && src.cast->type == ev_uint)\n\t{\t//zero extends. we could emulate this but that means endian issues.\n\t\tsrc = QCC_PR_Statement (&pr_opcodes[OP_CONV_UI64], src, nullsref, NULL);\n\t\tsrc.cast = cast;\n\t}\n\telse if ((totype == ev_int64||totype == ev_uint64) && src.cast->type == ev_integer)\n\t{\t//sign extends\n\t\tsrc = QCC_PR_Statement (&pr_opcodes[OP_CONV_II64], src, nullsref, NULL);\n\t\tsrc.cast = cast;\n\t}\n\telse if ((totype == ev_integer||totype == ev_uint) && (src.cast->type == ev_int64||src.cast->type == ev_uint64))\n\t{\t//truncates. we could emulate this but that means endian issues.\n\t\tsrc = QCC_PR_Statement (&pr_opcodes[OP_CONV_I64I], src, nullsref, NULL);\n\t\tsrc.cast = cast;\n\t}\n\telse if (totype == ev_integer && (src.cast->type == ev_bitfld && src.cast->parentclass->type == ev_integer))\n\t{\t//just read out the relevant bits, sign extending.\n\t\tsrc = QCC_PR_Statement (&pr_opcodes[OP_BITEXTEND_I], src, QCC_MakeUIntConst(src.cast->bits), NULL);\n\t\tsrc.cast = cast;\n\t}\n\telse if (totype == ev_uint && (src.cast->type == ev_bitfld && src.cast->parentclass->type == ev_uint))\n\t{\t//just read out the relevant bits, sign extending.\n\t\tsrc = QCC_PR_Statement (&pr_opcodes[OP_BITAND_I], src, QCC_MakeUIntConst(~((1<<src.cast->bits)-1)), NULL);\n\t\tsrc.cast = cast;\n\t}\n\n\telse if (totype == ev_vector && src.cast->type == ev_float)\n\t{\n\t\tif (implicit)\n\t\t{\n\t\t\tchar typea[256];\n\t\t\tchar typeb[256];\n\t\t\tTypeName(src.cast, typea, sizeof(typea));\n\t\t\tTypeName(cast, typeb, sizeof(typeb));\n\t\t\tQCC_PR_ParseWarning(WARN_LAXCAST, \"Implicit cast from %s%s%s to %s%s%s\", col_type,typea,col_none, col_type,typeb,col_none);\n\t\t}\n\t\tsrc = QCC_PR_Statement (&pr_opcodes[OP_MUL_FV], src, QCC_MakeVectorConst(1,1,1), NULL);\n\t}\n\telse if (totype == ev_vector && src.cast->type == ev_integer)\n\t{\n\t\tif (implicit)\n\t\t{\n\t\t\tchar typea[256];\n\t\t\tchar typeb[256];\n\t\t\tTypeName(src.cast, typea, sizeof(typea));\n\t\t\tTypeName(cast, typeb, sizeof(typeb));\n\t\t\tQCC_PR_ParseWarning(WARN_LAXCAST, \"Implicit cast from %s%s%s to %s%s%s\", col_type,typea,col_none, col_type,typeb,col_none);\n\t\t}\n\t\tsrc = QCC_PR_Statement (&pr_opcodes[OP_MUL_IV], src, QCC_MakeVectorConst(1,1,1), NULL);\n\t}\n\telse if (totype == ev_entity && src.cast->type == ev_entity)\n\t{\n\t\tif (implicit)\n\t\t{\t//this is safe if the source inherits from the dest type\n\t\t\t//but we should warn if the other way around\n\t\t\tQCC_type_t *t = src.cast;\n\t\t\twhile(t)\n\t\t\t{\n\t\t\t\tif (!typecmp_lax(t, cast))\n\t\t\t\t\tbreak;\n\t\t\t\tt = t->parentclass;\n\t\t\t}\n\t\t\tif (!t)\n\t\t\t{\n\t\t\t\tchar typea[256];\n\t\t\t\tchar typeb[256];\n\t\t\t\tTypeName(src.cast, typea, sizeof(typea));\n\t\t\t\tTypeName(cast, typeb, sizeof(typeb));\n\t\t\t\tQCC_PR_ParseWarning(WARN_LAXCAST, \"Implicit cast from %s%s%s to %s%s%s\", col_type,typea,col_none, col_type,typeb,col_none);\n\t\t\t}\n\t\t}\n\t\tsrc.cast = cast;\n\t}\n\telse if (flag_undefwordsize && ((totype == ev_pointer && src.cast->type == ev_string) || (totype == ev_pointer && src.cast->type == ev_string)))\n\t{\t//pointer<->string is blocked when pointers are not bytes. if words are 8 bytes then everything is screwed, or if pointers cannot store byte offsets...\n\t\tQCC_PR_ParseWarning(ERR_BADEXTENSION, \"pointer<->string casts were disabled.\");\n\t\tsrc.cast = cast;\n\t}\n\t//string and char* are the same type. don't care if its signed or unsigned.\n\telse if (((totype == ev_pointer && tmp->aux_type->type == ev_bitfld && tmp->aux_type->bits == 8) && src.cast->type == ev_string) ||\n\t\t\t (totype == ev_string && (src.cast->type == ev_pointer && src.cast->aux_type->type == ev_bitfld && src.cast->aux_type->bits == 8)))\n\t\tsrc.cast = cast;\n\t//can cast any pointer type to void*\n\telse if (totype == ev_pointer && tmp->aux_type->type == ev_void && (src.cast->type == ev_pointer || src.cast->type == ev_function || src.cast->type == ev_string))\n\t\tsrc.cast = cast;\n\t//can cast void* to any pointer type.\n\telse if (src.cast->type == ev_pointer && src.cast->aux_type->type == ev_void && (totype == ev_pointer || totype == ev_function || totype == ev_string))\n\t\tsrc.cast = cast;\n\telse if (totype == ev_pointer && src.cast->type == ev_pointer)\n\t{\n\t\tif (implicit)\n\t\t{\t//this is safe if the source inherits from the dest type\n\t\t\t//but we should warn if the other way around\n\t\t\tQCC_type_t *t = src.cast->aux_type;\n\t\t\twhile(t)\n\t\t\t{\n\t\t\t\tif (!typecmp_lax(t, cast->aux_type))\n\t\t\t\t\tbreak;\n\t\t\t\tt = t->parentclass;\n\t\t\t}\n\t\t\tif (!t)\n\t\t\t{\n\t\t\t\tchar typea[256];\n\t\t\t\tchar typeb[256];\n\t\t\t\tTypeName(src.cast, typea, sizeof(typea));\n\t\t\t\tTypeName(cast, typeb, sizeof(typeb));\n\t\t\t\tQCC_PR_ParseWarning(WARN_LAXCAST, \"Implicit cast from %s%s%s to %s%s%s\", col_type,typea,col_none, col_type,typeb,col_none);\n\t\t\t}\n\t\t}\n\t\tsrc.cast = cast;\n\t}\n\t/*variants can be cast from/to anything without warning, even implicitly. FIXME: size issues*/\n\telse if (totype == ev_variant || src.cast->type == ev_variant\n\t\t|| (totype == ev_field && totype == src.cast->type && (tmp->aux_type->type == ev_variant || src.cast->aux_type->type == ev_variant)))\n\t{\n\t\tsrc.cast = cast;\n\n\t\tif (implicit && typecmp_lax(src.cast, cast))\n\t\t{\n\t\t\tchar typea[256];\n\t\t\tchar typeb[256];\n\t\t\tTypeName(src.cast, typea, sizeof(typea));\n\t\t\tTypeName(cast, typeb, sizeof(typeb));\n\t\t\tQCC_PR_ParseWarning(WARN_LAXCAST, \"Implicit cast from %s%s%s to %s%s%s\", col_type,typea,col_none, col_type,typeb,col_none);\n\t\t}\n\t}\n\t/*these casts are acceptable but probably an error (so warn when implicit)*/\n\telse if (\n\t\t/*you may explicitly cast between pointers and ints (strings count as pointers - WARNING: some strings may not be expressable as pointers)*/\n\t\t((totype == ev_pointer || totype == ev_string || totype == ev_integer || totype == ev_uint || totype == ev_function) && (src.cast->type == ev_pointer || src.cast->type == ev_string || src.cast->type == ev_integer || src.cast->type == ev_uint || src.cast->type == ev_function))\n\t\t//ents->ints || ints->ents. WARNING: the integer value of ent types is engine specific.\n\t\t|| (totype == ev_entity && src.cast->type == ev_integer)\n\t\t|| (totype == ev_entity && src.cast->type == ev_float && flag_qccx)\n\t\t|| (totype == ev_integer && src.cast->type == ev_entity)\n\t\t|| (totype == ev_float && src.cast->type == ev_entity && flag_qccx)\n\t\t|| (totype == ev_string && src.cast->type == ev_float && flag_qccx)\n\t\t|| (totype == ev_float && src.cast->type == ev_string && flag_qccx)\n\t\t)\n\t{\n\t\t//direct cast\n\t\tif (implicit && typecmp_lax(src.cast, cast))\n\t\t{\n\t\t\tchar typea[256];\n\t\t\tchar typeb[256];\n\t\t\tTypeName(src.cast, typea, sizeof(typea));\n\t\t\tTypeName(cast, typeb, sizeof(typeb));\n\t\t\tQCC_PR_ParseWarning(WARN_LAXCAST, \"Implicit cast from %s%s%s to %s%s%s\", col_type,typea,col_none, col_type,typeb,col_none);\n\t\t}\n\t\tsrc.cast = cast;\n\t}\n\telse if (!implicit && !typecmp_lax(src.cast, cast))\n\t\tsrc.cast = cast;\n\telse if (!implicit && cast->type == ev_void)\n\t\tsrc.cast = type_void;\t//anything can be cast to void, but only do it explicitly.\n\telse\t//failed\n\t\treturn nullsref;\n\treturn src;\n}\n\nQCC_sref_t QCC_EvaluateCast(QCC_sref_t src, QCC_type_t *cast, pbool implicit)\n{\n\tQCC_sref_t r;\n\tif (\t(cast->type == ev_accessor && cast->parentclass == src.cast)\n\t\t||\t(src.cast->type == ev_accessor && src.cast->parentclass == cast))\n\t{\n\t\tif (implicit)\n\t\t{\n\t\t\tchar typea[256];\n\t\t\tchar typeb[256];\n\t\t\tTypeName(src.cast, typea, sizeof(typea));\n\t\t\tTypeName(cast, typeb, sizeof(typeb));\n\t\t\tQCC_PR_ParseWarning(0, \"Implicit cast from %s%s%s to %s%s%s\", col_type,typea,col_none, col_type,typeb,col_none);\n\t\t}\n\t\tsrc.cast = cast;\n\t\treturn src;\n\t}\n\n//casting from an accessor uses the base type of that accessor (this allows us to properly read void* accessors)\n\tfor(;;)\n\t{\n\t\tr = QCC_TryEvaluateCast(src, cast, implicit);\n\t\tif (r.cast)\n\t\t\treturn r;\t//success\n\n\t\tif (src.cast->type == ev_accessor || src.cast->type == ev_bitfld)\n\t\t\tsrc.cast = src.cast->parentclass;\n\t\telse if (flag_laxcasts)\n\t\t{\n\t\t\tif (implicit)\n\t\t\t{\n\t\t\t\tchar typea[256];\n\t\t\t\tchar typeb[256];\n\t\t\t\tTypeName(src.cast, typea, sizeof(typea));\n\t\t\t\tTypeName(cast, typeb, sizeof(typeb));\n\t\t\t\tQCC_PR_ParseWarning(0, \"Implicit lax cast from %s%s%s to %s%s%s\", col_type,typea,col_none, col_type,typeb,col_none);\n\t\t\t}\n\t\t\tr = src;\n\t\t\tr.cast = cast;\t\t//decompilers suck\n\t\t\treturn r;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tchar typea[256];\n\t\t\tchar typeb[256];\n\t\t\tTypeName(src.cast, typea, sizeof(typea));\n\t\t\tTypeName(cast, typeb, sizeof(typeb));\n\t\t\tQCC_PR_ParseError(0, \"Cannot cast from %s%s%s to %s%s%s\", col_type,typea,col_none, col_type, typeb,col_none);\n\t\t}\n\t}\n}\n\n/*\n============\nPR_Term\n============\n*/\nstatic QCC_ref_t *QCC_PR_RefTerm (QCC_ref_t *retbuf, unsigned int exprflags)\n{\n\tQCC_ref_t\t*r;\n\tQCC_sref_t\te, e2;\n\tif (pr_token_type == tt_punct)\t//a little extra speed...\n\t{\n\t\tint preinc;\n\t\tif (QCC_PR_CheckToken(\"++\"))\n\t\t\tpreinc = 1;\n\t\telse if (QCC_PR_CheckToken(\"--\"))\n\t\t\tpreinc = -1;\n\t\telse\n\t\t\tpreinc = 0;\n\n\t\tif (preinc)\n\t\t{\n\t\t\tQCC_ref_t tmp;\n\t\t\tqcc_usefulstatement=true;\n\t\t\tr = QCC_PR_RefTerm (&tmp, 0);\t//parse the term, as if we were going to return it\n\t\t\tif (r->readonly)\n\t\t\t\tQCC_PR_ParseError(ERR_BADPLUSPLUSOPERATOR, \"%s operator on read-only value\", (preinc>0)?\"++\":\"--\");\n\t\t\te = QCC_RefToDef(r, false);\t//read it as needed\n\t\t\tif (e.sym->constant)\n\t\t\t{\n\t\t\t\tQCC_PR_ParseWarning(WARN_ASSIGNMENTTOCONSTANT, \"Assignment to constant %s\", QCC_GetSRefName(e));\n\t\t\t\tQCC_PR_ParsePrintSRef(WARN_ASSIGNMENTTOCONSTANT, e);\n\t\t\t}\n//\t\t\tif (e.sym->temp && r->type == REF_GLOBAL)\n//\t\t\t\tQCC_PR_ParseWarning(WARN_ASSIGNMENTTOCONSTANT, \"Hey! That's a temp! ++ operators cannot work on temps!\");\n\t\t\tswitch (e.cast->type)\n\t\t\t{\n\t\t\tcase ev_integer:\n\t\t\t\te = QCC_PR_Statement(&pr_opcodes[OP_ADD_I], e, QCC_MakeIntConst(preinc), NULL);\n\t\t\t\tbreak;\n\t\t\tcase ev_uint:\n\t\t\t\te = QCC_PR_Statement(&pr_opcodes[OP_ADD_U], e, QCC_MakeIntConst(preinc), NULL);\n\t\t\t\tbreak;\n\t\t\tcase ev_pointer:\n\t\t\t\te = QCC_PR_Statement(&pr_opcodes[OP_ADD_PIW], e, QCC_MakeIntConst(preinc * e.cast->aux_type->size), NULL);\n\t\t\t\tbreak;\n\t\t\tcase ev_float:\n\t\t\t\te = QCC_PR_Statement(&pr_opcodes[OP_ADD_F], e, QCC_MakeFloatConst(preinc), NULL);\n\t\t\t\tbreak;\n\t\t\tcase ev_double:\n\t\t\t\te = QCC_PR_Statement(&pr_opcodes[OP_ADD_D], e, QCC_MakeDoubleConst(preinc), NULL);\n\t\t\t\tbreak;\n\t\t\tcase ev_int64:\n\t\t\t\te = QCC_PR_Statement(&pr_opcodes[OP_ADD_I64], e, QCC_MakeInt64Const(preinc), NULL);\n\t\t\t\tbreak;\n\t\t\tcase ev_uint64:\n\t\t\t\te = QCC_PR_Statement(&pr_opcodes[OP_ADD_U64], e, QCC_MakeInt64Const(preinc), NULL);\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tQCC_PR_ParseError(ERR_BADPLUSPLUSOPERATOR, \"++ operator on unsupported type\");\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\t//return the 'result' of the store. its read-only now.\n\t\t\treturn QCC_DefToRef(retbuf, QCC_StoreSRefToRef(r, e, true, false));\n\t\t}\n\t\tif (QCC_PR_CheckToken (\"!\"))\n\t\t{\n\t\t\te = QCC_PR_Expression (NOT_PRIORITY, EXPR_DISALLOW_COMMA|EXPR_WARN_ABOVE_1);\n\t\t\te = QCC_PR_GenerateLogicalNot(e, \"Type mismatch: !%s\");\n\t\t\treturn QCC_DefToRef(retbuf, e);\n\t\t}\n\n\t\tif (QCC_PR_CheckToken (\"~\"))\n\t\t{\n\t\t\te = QCC_PR_Expression (NOT_PRIORITY, EXPR_DISALLOW_COMMA|EXPR_WARN_ABOVE_1);\n\t\t\te = QCC_PR_GenerateBitwiseNot(e, \"Type mismatch: ~%s\");\n\t\t\treturn QCC_DefToRef(retbuf, e);\n\t\t}\n\t\tif (QCC_PR_CheckToken (\"&\"))\n\t\t{\n\t\t\tr = QCC_PR_RefExpression (retbuf, UNARY_PRIORITY, EXPR_DISALLOW_COMMA);\n\t\t\tif (flag_qccx && r->cast->type == ev_float)\n\t\t\t{\t//&%342 casts it to a (pre-dereferenced) pointer.\n\t\t\t\tr = QCC_PR_BuildRef(retbuf, REF_POINTER, QCC_RefToDef(r, true), nullsref, type_float, false, 0);\n\t\t\t}\n\t\t\telse if (flag_qccx && (r->cast->type == ev_string || r->cast->type == ev_field || r->cast->type == ev_entity || r->cast->type == ev_function))\n\t\t\t{\t//&string casts it to a float. does not dereference it\n\t\t\t\tr->cast = type_float;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (r->base.sym->temp && r->type != REF_POINTER)\n\t\t\t\t\tQCC_PR_ParseWarning(ERR_INTERNAL, \"Address-of temp on line %s:%i\", s_filen, pr_source_line);\n\t\t\t\telse\n\t\t\t\t\tr = QCC_PR_GenerateAddressOf(retbuf, r);\n\t\t\t}\n\t\t\treturn r;\n\t\t}\n\t\tif (QCC_PR_CheckToken (\"*\"))\n\t\t{\n\t\t\te = QCC_PR_Expression (UNARY_PRIORITY, EXPR_DISALLOW_COMMA);\n\t\t\tif (flag_qccx && (e.cast->type == ev_float || e.cast->type == ev_integer))\n\t\t\t{\t//just an evil cast. note that qccx assumes offsets rather than indexes, so these are often quite large and typically refer to some index into the world entity.\n\t\t\t\treturn QCC_PR_BuildRef(retbuf, REF_GLOBAL, e, nullsref, type_entity, false, 0);\n\t\t\t}\n\t\t\telse if (e.cast->type == ev_pointer)\t//FIXME: arrays\n\t\t\t\treturn QCC_PR_BuildRef(retbuf, REF_POINTER, e, nullsref, e.cast->aux_type, false, 0);\n\t\t\telse if (e.cast->type == ev_string)\t//FIXME: arrays\n\t\t\t\treturn QCC_PR_BuildRef(retbuf, REF_STRING, e, nullsref, type_integer, false, 0);\n\t\t\telse if (e.cast->type == ev_function)\n\t\t\t{\t// (*funcptr)(args); is one way to call a function pointer in C, but the * is actually irrelevant.\n\t\t\t\tif (flag_qcfuncs)\n\t\t\t\t\tQCC_PR_ParseErrorPrintSRef (ERR_TYPEMISMATCH, e, \"attempt to dereference function type.\");\n\t\t\t\treturn QCC_PR_BuildRef(retbuf, REF_GLOBAL, e, nullsref, e.cast, false, 0);\n\t\t\t}\n\t\t\telse if (e.cast->accessors)\n\t\t\t{\n\t\t\t\tstruct accessor_s *acc;\n\t\t\t\tfor (acc = e.cast->accessors; acc; acc = acc->next)\n\t\t\t\t\tif (!strcmp(acc->fieldname, \"\"))\n\t\t\t\t\t\treturn QCC_PR_BuildAccessorRef(retbuf, e, nullsref, acc, e.sym->constant);\n\t\t\t}\n\t\t\tQCC_PR_ParseErrorPrintSRef (ERR_TYPEMISMATCH, e, \"Unable to dereference non-pointer type.\");\n\t\t}\n\t\tif (flag_qccx && QCC_PR_CheckToken (\"@\"))\n\t\t{\t//@foo is equivelent to (string)*(int*)&foo\n\t\t\tr = QCC_PR_RefExpression (retbuf, UNARY_PRIORITY, EXPR_DISALLOW_COMMA);\n\t\t\tif (r->cast->type == ev_float || r->cast->type == ev_integer)\n\t\t\t{\n\t\t\t\tr->cast = type_string;\n\t\t\t\treturn r;\n\t\t\t}\n\t\t\tQCC_PR_ParseErrorPrintSRef (ERR_BADEXTENSION, QCC_RefToDef(r, true), \"'@' operator only functions on floats and ints. go figure.\");\n\t\t}\n\t\tif (QCC_PR_CheckToken (\"-\"))\n\t\t{\n\t\t\tQCC_type_t *type;\n\t\t\te = QCC_PR_Expression (UNARY_PRIORITY, EXPR_DISALLOW_COMMA);\n\n\t\t\ttype = e.cast;\n\t\t\twhile (type->type == ev_boolean)\n\t\t\t\ttype = type->parentclass;\n\t\t\tswitch(type->type)\n\t\t\t{\n\t\t\tcase ev_float:\n\t\t\t\te2 = QCC_PR_Statement (&pr_opcodes[OP_SUB_F], QCC_MakeFloatConst(0), e, NULL);\n\t\t\t\tbreak;\n\t\t\tcase ev_double:\n\t\t\t\te2 = QCC_PR_Statement (&pr_opcodes[OP_SUB_D], QCC_MakeDoubleConst(0), e, NULL);\n\t\t\t\tbreak;\n\t\t\tcase ev_vector:\n\t\t\t\te2 = QCC_PR_Statement (&pr_opcodes[OP_SUB_V], QCC_MakeVectorConst(0, 0, 0), e, NULL);\n\t\t\t\tbreak;\n\t\t\tcase ev_integer:\n\t\t\t\te2 = QCC_PR_Statement (&pr_opcodes[OP_SUB_I], QCC_MakeIntConst(0), e, NULL);\n\t\t\t\tbreak;\n\t\t\tcase ev_uint:\n\t\t\t\te2 = QCC_PR_Statement (&pr_opcodes[OP_SUB_U], QCC_MakeIntConst(0), e, NULL);\n\t\t\t\tbreak;\n\t\t\tcase ev_int64:\n\t\t\t\te2 = QCC_PR_Statement (&pr_opcodes[OP_SUB_I64], QCC_MakeInt64Const(0), e, NULL);\n\t\t\t\tbreak;\n\t\t\tcase ev_uint64:\n\t\t\t\te2 = QCC_PR_Statement (&pr_opcodes[OP_SUB_U64], QCC_MakeInt64Const(0), e, NULL);\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tQCC_PR_ParseError (ERR_BADNOTTYPE, \"type mismatch for -\");\n\t\t\t\te2 = nullsref;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\treturn QCC_DefToRef(retbuf, e2);\n\t\t}\n\t\tif (QCC_PR_CheckToken (\"+\"))\n\t\t{\n\t\t\te = QCC_PR_Expression (UNARY_PRIORITY, EXPR_DISALLOW_COMMA);\n\n\t\t\tswitch(e.cast->type)\n\t\t\t{\n\t\t\tcase ev_float:\n\t\t\tcase ev_double:\n\t\t\tcase ev_vector:\n\t\t\tcase ev_integer:\n\t\t\tcase ev_uint:\n\t\t\tcase ev_int64:\n\t\t\tcase ev_uint64:\n\t\t\t\te2 = e;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tQCC_PR_ParseError (ERR_BADNOTTYPE, \"type mismatch for +\");\n\t\t\t\te2 = nullsref;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\treturn QCC_DefToRef(retbuf, e2);\n\t\t}\n\t\tif (QCC_PR_CheckToken (\"(\"))\n\t\t{\n\t\t\tQCC_type_t *newtype;\n\t\t\tnewtype = QCC_PR_ParseType(false, true, false);\n\t\t\tif (newtype)\n\t\t\t{\n\t\t\t\t/*if (QCC_PR_Check (\"[\"))\n\t\t\t\t{\n\t\t\t\t\tQCC_PR_Expect (\"]\");\n\t\t\t\t\tQCC_PR_Expect(\")\");\n\t\t\t\t\tQCC_PR_Expect(\"{\");\n\n\t\t\t\t\tQCC_PR_Expect (\"}\");\n\t\t\t\t\treturn array;\n\t\t\t\t}*/\n\t\t\t\tQCC_PR_Expect (\")\");\n\t\t\t\tif (QCC_PR_PeekToken(\"{\"))\n\t\t\t\t{\n\t\t\t\t\tif (newtype->type == ev_vector)\n\t\t\t\t\t{\n\t\t\t\t\t\tQCC_sref_t x,y,z;\n\t\t\t\t\t\tQCC_PR_Expect(\"{\");\n\t\t\t\t\t\tx = QCC_PR_Expression(TOP_PRIORITY, EXPR_DISALLOW_COMMA);\n\t\t\t\t\t\tQCC_PR_Expect(\",\");\n\t\t\t\t\t\ty = QCC_PR_Expression(TOP_PRIORITY, EXPR_DISALLOW_COMMA);\n\t\t\t\t\t\tQCC_PR_Expect(\",\");\n\t\t\t\t\t\tz = QCC_PR_Expression(TOP_PRIORITY, EXPR_DISALLOW_COMMA);\n\t\t\t\t\t\tQCC_PR_Expect (\"}\");\n\t\t\t\t\t\treturn QCC_DefToRef(retbuf, QCC_PR_GenerateVector(x,y,z));\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\te = QCC_PR_ParseInitializerTemp(newtype);\n\t\t\t\t\t\tif (newtype->type == ev_function && QCC_PR_PeekToken(\"(\"))\n\t\t\t\t\t\t\te.sym->allowinline = true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t//not a single term, so we can cast the result of function calls. just make sure its not too high a priority\n\t\t\t\t\t//and yeah, okay, use defs not refs. whatever.\n\t\t\t\t\te = QCC_PR_Expression (UNARY_PRIORITY, EXPR_DISALLOW_COMMA);\n\t\t\t\t\te = QCC_EvaluateCast(e, newtype, false);\n\t\t\t\t}\n\t\t\t\treturn QCC_DefToRef(retbuf, e);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tpbool oldcond = conditional;\n\t\t\t\tconditional = conditional?2:0;\n\t\t\t\tr =\tQCC_PR_RefExpression(retbuf, TOP_PRIORITY, 0);\n\t\t\t\tQCC_PR_Expect (\")\");\n\t\t\t\tconditional = oldcond;\n\n//\t\t\t\tQCC_PR_ParseArrayPointer(r, true, true);\n\n\t\t\t\tr = QCC_PR_ParseRefArrayPointer(retbuf, r, true, true);\n\t\t\t}\n\t\t\treturn r;\n\t\t}\n\t}\n\tif (pr_token_type == tt_name)\t//a little extra speed...\n\t{\n\t\tif (QCC_PR_CheckKeyword(true, \"sizeof\"))\t//'returns size_t', which we interpret as uintptr_t aka uint32_t for us.\n\t\t{\n\t\t\tQCC_type_t *t;\n\t\t\tpbool bracket = QCC_PR_CheckToken(\"(\");\n\t\t\tt = QCC_PR_ParseType(false, true, false);\n\t\t\tif (t)\n\t\t\t{\n\t\t\t\tif (flag_undefwordsize)\n\t\t\t\t\tQCC_PR_ParseWarning(ERR_BADEXTENSION, \"sizeof was disabled, use array.length instead.\");\n\t\t\t\tif (bracket)\n\t\t\t\t\tQCC_PR_Expect(\")\");\n\t\t\t\tif (t->bits&31)\n\t\t\t\t\treturn QCC_DefToRef(retbuf, QCC_MakeUIntConst(t->bits/8));\n\t\t\t\telse\t//word-aligned types should use PIW to get byte counts, but its not necessarily meaningful, so be efficient instead.\n\t\t\t\t\treturn QCC_DefToRef(retbuf, QCC_MakeUIntConst(t->size*VMWORDSIZE));\n\t\t\t\t\t//return QCC_DefToRef(retbuf, QCC_PR_Statement(&pr_opcodes[OP_ADD_PIW], QCC_MakeIntConst(0), QCC_MakeIntConst(t->size), NULL));\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tint sz;\n\t\t\t\tint oldstcount = numstatements;\n\t\t\t\tQCC_ref_t refbuf, *r;\n\t\t\t\tr = QCC_PR_RefExpression(&refbuf, TOP_PRIORITY, 0);\t//formulas are accepted weirdly enough, its not just terms.\n\t\t\t\tif (r->type == REF_GLOBAL && r->base.sym->type == type_string && !strcmp(r->base.sym->name, \"IMMEDIATE\"))\n\t\t\t\t\tsz = strlen(&strings[QCC_SRef_EvalConst(r->base)->string]) + 1;\t//sizeof(\"hello\") includes the null, and is bytes not codepoints\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tif ((r->cast->align&7))\n\t\t\t\t\t\tQCC_PR_ParseWarning(ERR_BADEXTENSION, \"sizeof on bit-field is invalid\");\n\t\t\t\t\telse if (flag_undefwordsize)\n\t\t\t\t\t\tQCC_PR_ParseWarning(ERR_BADEXTENSION, \"sizeof was disabled.\");\n\n\t\t\t\t\tt = r->cast;\n\t\t\t\t\tif (r->type == REF_ARRAYHEAD || r->type == REF_POINTERARRAY)\n\t\t\t\t\t\tt = t->aux_type;\n//\t\t\t\t\telse if (r->cast->type == ev_pointer)\n//\t\t\t\t\t\tQCC_PR_ParseWarning(WARN_IGNOREDKEYWORD, \"sizeof on pointer? (%i)\", r->type);\n\t\t\t\t\tif (t->bits)\n\t\t\t\t\t\tsz = t->bits/8;\n\t\t\t\t\telse\n\t\t\t\t\t\tsz = VMWORDSIZE*t->size;\t//4 bytes per word. we don't support char/short (our string type is logically char*)\n\t\t\t\t\tif (r->type == REF_ARRAYHEAD || r->type == REF_POINTERARRAY)\n\t\t\t\t\t\tsz *= r->arraysize;\n\t\t\t\t}\n\t\t\t\tQCC_FreeTemp(r->base);\n\t\t\t\tif (r->index.cast)\n\t\t\t\t\tQCC_FreeTemp(r->index);\n\t\t\t\t//the term should not have side effects, or generate any actual statements.\n\t\t\t\tnumstatements = oldstcount;\n\t\t\t\tif (bracket)\n\t\t\t\t\tQCC_PR_Expect(\")\");\n\t\t\t\treturn QCC_DefToRef(retbuf, QCC_MakeUIntConst(sz));\n\t\t\t}\n\t\t}\n\t\t/*if (QCC_PR_CheckKeyword(keyword_new, \"new\"))\n\t\t{\t//note: C++ requires struct-based classes rather than entity ones.\n\t\t\tconst char *cname = QCC_PR_ParseName();\n\t\t\tQCC_type_t *rettype = QCC_TypeForName(cname);\n\t\t\tQCC_sref_t\tresult, func;\n\t\t\tif (!rettype || rettype->type != ev_entity)\n\t\t\t\tQCC_PR_ParseError (ERR_TYPEMISMATCHPARM, \"new %s() unsupported argument type for intrinsic\", cname);\n\t\t\te = QCC_PR_GetSRef(NULL, \"spawn\", NULL, 0, 0, 0);\n\t\t\tif (!e.cast)\n\t\t\t\tQCC_PR_ParseError (ERR_TYPEMISMATCHPARM, \"new %s() spawn builtin not defined\", cname);\n\t\t\tresult = QCC_PR_GenerateFunctionCallRef(nullsref, e, NULL,0);\n\t\t\tresult.cast = rettype;\n\t\t\tif (QCC_PR_CheckToken(\"(\"))\n\t\t\t{\t//arglist is optional, apparently\n\t\t\t\tchar genfunc[256];\n\t\t\t\t//do field assignments.\n\t\t\t\twhile(QCC_PR_CheckToken(\",\"))\n\t\t\t\t{\n\t\t\t\t\tQCC_sref_t f, p, v;\n\t\t\t\t\tf = QCC_PR_ParseValue(rettype, false, false, true);\n\t\t\t\t\tif (f.cast->type != ev_field)\n\t\t\t\t\t\tQCC_PR_ParseError(0, \"Named field is not a field.\");\n\t\t\t\t\tif (QCC_PR_CheckToken(\"=\"))\t\t\t\t\t\t\t//allow : or = as a separator, but throw a warning for =\n\t\t\t\t\t\tQCC_PR_ParseWarning(0, \"That = should be a :\");\t//rejecting = helps avoid qcc bugs. :P\n\t\t\t\t\telse\n\t\t\t\t\t\tQCC_PR_Expect(\":\");\n\t\t\t\t\tv = QCC_PR_Expression(TOP_PRIORITY, EXPR_DISALLOW_COMMA);\n\n\t\t\t\t\tp = QCC_PR_StatementFlags(&pr_opcodes[OP_ADDRESS], result, f, NULL, STFL_PRESERVEA);\n\t\t\t\t\tif (v.cast->size == 3)\n\t\t\t\t\t\tQCC_FreeTemp(QCC_PR_Statement(&pr_opcodes[OP_STOREP_V], v, p, NULL));\n\t\t\t\t\telse\n\t\t\t\t\t\tQCC_FreeTemp(QCC_PR_Statement(&pr_opcodes[OP_STOREP_F], v, p, NULL));\n\t\t\t\t}\n\t\t\t\tQCC_PR_Expect(\")\");\n\n\t\t\t\tQC_snprintfz(genfunc, sizeof(genfunc), \"spawnfunc_%s\", rettype->name);\n\t\t\t\tfunc = QCC_PR_GetSRef(type_function, genfunc, NULL, true, 0, GDF_CONST);\n\t\t\t\tfunc.sym->referenced = true;\n\n\t\t\t\tQCC_UnFreeTemp(result);\n\t\t\t\tQCC_FreeTemp(QCC_PR_GenerateFunctionCallRef(result, func, NULL, 0));\n\t\t\t\tresult.cast = rettype;\n\t\t\t}\n\t\t\treturn QCC_DefToRef(retbuf, result);\n\t\t}*/\n\t\tif (QCC_PR_CheckKeyword(true, \"_length\"))\n\t\t{\t//for compat with gmqcc. use array.length in fte instead.\n\t\t\tpbool bracket = QCC_PR_CheckToken(\"(\");\n\t\t\t/*QCC_type_t *t;\n\t\t\tt = QCC_PR_ParseType(false, true);\n\t\t\tif (t)\n\t\t\t{\n\t\t\t\tif (bracket)\n\t\t\t\t\tQCC_PR_Expect(\")\");\n\t\t\t\treturn QCC_DefToRef(retbuf, QCC_PR_Statement(&pr_opcodes[OP_ADD_PIW], QCC_MakeIntConst(0), QCC_MakeIntConst(t->size), NULL));\n\t\t\t}\n\t\t\telse*/\n\t\t\t{\n\t\t\t\tint sz = 0;\n\t\t\t\tint oldstcount = numstatements;\n\t\t\t\tQCC_ref_t refbuf, *r;\n\t\t\t\tr = QCC_PR_RefExpression(&refbuf, TOP_PRIORITY, EXPR_DISALLOW_COMMA);\n\t\t\t\tif (r->type == REF_ARRAYHEAD || r->type == REF_POINTERARRAY)\n\t\t\t\t\tsz = r->arraysize;\n\t\t\t\telse if (r->cast == type_string)\n\t\t\t\t{\n\t\t\t\t\tQCC_sref_t d = QCC_RefToDef(r, false);\n\t\t\t\t\tconst QCC_eval_t *c = QCC_SRef_EvalConst(d);\n\t\t\t\t\tif (c)\n\t\t\t\t\t\tsz = strlen(&strings[c->string]);\t//_length(\"hello\") does NOT include the null (like strlen), but is bytes not codepoints\n\t\t\t\t\telse\n\t\t\t\t\t\tQCC_PR_ParseError (ERR_TYPEMISMATCHPARM, \"_length(string) requires an initialised constant\");\n\t\t\t\t}\n\t\t\t\telse if (r->cast == type_vector)\n\t\t\t\t\tsz = 3;\t//might as well. considering that vectors can be indexed as an array.\n\t\t\t\telse\n\t\t\t\t\tQCC_PR_ParseError (ERR_TYPEMISMATCHPARM, \"_length() unsupported argument type for intrinsic\");\n\t\t\t\tQCC_FreeTemp(r->base);\n\t\t\t\tif (r->index.cast)\n\t\t\t\t\tQCC_FreeTemp(r->index);\n\t\t\t\t//the term should not have side effects, or generate any actual statements.\n\t\t\t\tnumstatements = oldstcount;\n\t\t\t\tif (bracket)\n\t\t\t\t\tQCC_PR_Expect(\")\");\n\t\t\t\treturn QCC_DefToRef(retbuf, QCC_MakeIntConst(sz));\n\t\t\t}\n\t\t}\n\t}\n\treturn QCC_PR_ParseRefValue (retbuf, pr_classtype, !(exprflags&EXPR_DISALLOW_ARRAYASSIGN), true, true);\n}\n\nstatic int QCC_NumericTypeRanking(etype_t t)\n{\n\tswitch(t)\n\t{\n\tcase ev_double:\treturn 15;\n\tcase ev_float:\treturn 10;\n\t//large gap, to try to de-prioritize the opcodes that mix float and int types.\n\tcase ev_uint64:\treturn 6;\n\tcase ev_int64:\treturn 3;\n\tcase ev_uint:\treturn 1;\n\tcase ev_integer:return 0;\n\tdefault: return -1; //unranked\n\t}\n}\n//type promotions:\n//identity=0 > inheritance=1 > null=2 > double=3 > float=4 > uint64=5 > int64=6 > uint=7 > int=8 > short=9 > char=10 > variant=11 > vector=12 > failure\nstatic int QCC_canConv(QCC_sref_t from, etype_t to)\n{\n\tint frompri;\n\tint topri;\n\n\twhile(from.cast->type == ev_accessor || from.cast->type == ev_boolean)\t//no opcodes use accessors. convert to their real type.\n\t\tfrom.cast = from.cast->parentclass;\n\n\tif (from.cast->type == to)\n\t\treturn 0;\t//identity\n\n\tif (pr_classtype)\n\t{\n\t\tif (from.cast->type == ev_field)\n\t\t{\n\t\t\tif (from.cast->aux_type->type == to)\n\t\t\t\treturn 1;\n\t\t}\n\t}\n\n\tif (QCC_SRef_IsNull(from))\n\t\treturn 2;\n\n\tfrompri = QCC_NumericTypeRanking(from.cast->type);\n\ttopri = QCC_NumericTypeRanking(to);\n\tif (frompri >= 0 && topri >= 0)\n\t{\t//3...12\n\t\tif (topri >= frompri)\n\t\t\treturn 3+(topri-frompri);\n\t\telse\n\t\t\treturn 150+(frompri-topri);\n\t}\n\n\t//somewhat high penalty, ensures the other side is correct\n\tif (to == ev_variant)\n\t\treturn 9;\n\tif (from.cast->type == ev_variant)\n\t\treturn 10;\n\n\t//triggers a warning.\n\tif (from.cast->type == ev_float && to == ev_vector)\n\t\treturn 200;\n\t//triggers a warning. conversion works by using _x\n\tif (from.cast->type == ev_vector && to == ev_float)\n\t\treturn 201;\n\n\treturn -1;\n}\n/*\n==============\nQCC_PR_RefExpression\n==============\n*/\n\nQCC_ref_t *QCC_PR_BuildAccessorRef(QCC_ref_t *retbuf, QCC_sref_t base, QCC_sref_t index, struct accessor_s *accessor, pbool readonly)\n{\n\tretbuf->postinc = 0;\n\tretbuf->type = REF_ACCESSOR;\n\tretbuf->base = base;\n\tretbuf->index = index;\n\tretbuf->accessor = accessor;\n\tretbuf->cast = accessor->type;\n\tretbuf->readonly = readonly;\n\tretbuf->bitofs = 0;\n\tretbuf->arraysize = 0;\n\treturn retbuf;\n}\nQCC_ref_t *QCC_PR_BuildRef(QCC_ref_t *retbuf, unsigned int reftype, QCC_sref_t base, QCC_sref_t index, QCC_type_t *cast, pbool\treadonly, unsigned int bitofs)\n{\n\tretbuf->postinc = 0;\n\tretbuf->type = reftype;\n\tretbuf->base = base;\n\tretbuf->index = index;\n\tretbuf->cast = cast?cast:base.cast;\n\tretbuf->readonly = readonly;\n\tretbuf->accessor = NULL;\n\tretbuf->bitofs = bitofs;\n\tretbuf->arraysize = base.sym->arraysize;\n\treturn retbuf;\n}\nQCC_ref_t *QCC_DefToRef(QCC_ref_t *retbuf, QCC_sref_t def)\n{\n\treturn QCC_PR_BuildRef(retbuf,\n\t\t\t\tREF_GLOBAL,\n\t\t\t\tdef, \n\t\t\t\tnullsref,\n\t\t\t\tdef.cast,\n\t\t\t\t!def.sym || !!def.sym->constant || def.sym->temp, 0);\n}\n/*\nvoid QCC_StoreToOffset(int dest, int source, QCC_type_t *type)\n{\n\t//fixme: we should probably handle entire structs or something\n\tswitch(type->type)\n\t{\n\tdefault:\n\tcase ev_float:\n\t\tQCC_PR_StatementFlags(&pr_opcodes[OP_STORE_F, source, dest, 0, false);\n\t\tbreak;\n\tcase ev_vector:\n\t\tQCC_PR_SimpleStatement(OP_STORE_V, source, dest, 0, false);\n\t\tbreak;\n\tcase ev_entity:\n\t\tQCC_PR_SimpleStatement(OP_STORE_ENT, source, dest, 0, false);\n\t\tbreak;\n\tcase ev_string:\n\t\tQCC_PR_SimpleStatement(OP_STORE_S, source, dest, 0, false);\n\t\tbreak;\n\tcase ev_function:\n\t\tQCC_PR_SimpleStatement(OP_STORE_FNC, source, dest, 0, false);\n\t\tbreak;\n\tcase ev_field:\n\t\tQCC_PR_SimpleStatement(OP_STORE_FLD, source, dest, 0, false);\n\t\tbreak;\n\tcase ev_integer:\n\t\tQCC_PR_SimpleStatement(OP_STORE_I, source, dest, 0, false);\n\t\tbreak;\n\tcase ev_pointer:\n\t\tQCC_PR_SimpleStatement(OP_STORE_P, source, dest, 0, false);\n\t\tbreak;\n\t}\n}*/\nstatic void QCC_StoreToSRef(QCC_sref_t dest, QCC_sref_t source, QCC_type_t *type, pbool preservesource, pbool preservedest)\n{\n\tunsigned int i;\n\tint flags = 0;\n\tif (preservesource)\n\t\tflags |= STFL_PRESERVEA;\n\tif (preservedest)\n\t\tflags |= STFL_PRESERVEB;\n\t//fixme: we should probably handle entire structs or something\n\tswitch(type->type)\n\t{\n\tdefault:\n\tcase ev_struct:\n\tcase ev_union:\n\tcase ev_enum:\n\t\t//don't bother trying to optimise any temps here, its not likely to happen anyway.\n\t\tif (QCC_SRef_IsNull(source))\n\t\t{\n\t\t\tfor (i = 0; i+2 < type->size; i+=3, dest.ofs += 3)\n\t\t\t\tQCC_PR_SimpleStatement(&pr_opcodes[OP_STORE_V], QCC_MakeVectorConst(0,0,0), dest, nullsref, false);\n\t\t\tif (QCC_OPCodeValid(&pr_opcodes[OP_STORE_I64]))\n\t\t\t\tfor (; i+1 < type->size; i+=2, dest.ofs+=2)\n\t\t\t\t\tQCC_PR_SimpleStatement(&pr_opcodes[OP_STORE_I64], QCC_MakeInt64Const(0), dest, nullsref, false);\n\t\t\tfor (; i < type->size; i++, dest.ofs++)\n\t\t\t\tQCC_PR_SimpleStatement(&pr_opcodes[OP_STORE_F], QCC_MakeIntConst(0), dest, nullsref, false);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tfor (i = 0; i+2 < type->size; i+=3, dest.ofs += 3, source.ofs += 3)\n\t\t\t\tQCC_PR_SimpleStatement(&pr_opcodes[OP_STORE_V], source, dest, nullsref, false);\n\t\t\tif (QCC_OPCodeValid(&pr_opcodes[OP_STORE_I64]))\n\t\t\t\tfor (; i+1 < type->size; i+=2, dest.ofs+=2, source.ofs+=2)\n\t\t\t\t\tQCC_PR_SimpleStatement(&pr_opcodes[OP_STORE_I64], source, dest, nullsref, false);\n\t\t\tfor (; i < type->size; i++, dest.ofs++, source.ofs++)\n\t\t\t\tQCC_PR_SimpleStatement(&pr_opcodes[OP_STORE_F], source, dest, nullsref, false);\n\t\t\tsource.ofs -= type->size;\n\t\t}\n\t\tdest.ofs -= type->size;\n\t\tif (!preservesource)\n\t\t\tQCC_FreeTemp(source);\n\t\tif (!preservedest)\n\t\t\tQCC_FreeTemp(dest);\n\t\tbreak;\n\tcase ev_float:\n\t\tQCC_FreeTemp(QCC_PR_StatementFlags(&pr_opcodes[OP_STORE_F], source, dest, NULL, flags));\n\t\tbreak;\n\tcase ev_vector:\n\t\tQCC_FreeTemp(QCC_PR_StatementFlags(&pr_opcodes[OP_STORE_V], source, dest, NULL, flags));\n\t\tbreak;\n\tcase ev_entity:\n\t\tQCC_FreeTemp(QCC_PR_StatementFlags(&pr_opcodes[OP_STORE_ENT], source, dest, NULL, flags));\n\t\tbreak;\n\tcase ev_string:\n\t\tQCC_FreeTemp(QCC_PR_StatementFlags(&pr_opcodes[OP_STORE_S], source, dest, NULL, flags));\n\t\tbreak;\n\tcase ev_function:\n\t\tQCC_FreeTemp(QCC_PR_StatementFlags(&pr_opcodes[OP_STORE_FNC], source, dest, NULL, flags));\n\t\tbreak;\n\tcase ev_field:\n\t\tQCC_FreeTemp(QCC_PR_StatementFlags(&pr_opcodes[OP_STORE_FLD], source, dest, NULL, flags));\n\t\tbreak;\n\tcase ev_boolean:\n\t\tQCC_StoreToSRef(dest, source, type->parentclass, preservesource, preservedest);\n\t\treturn;\n\tcase ev_integer:\n\tcase ev_uint:\n\t\tQCC_FreeTemp(QCC_PR_StatementFlags(&pr_opcodes[OP_STORE_I], source, dest, NULL, flags));\n\t\tbreak;\n\tcase ev_int64:\n\tcase ev_uint64:\n\t\tQCC_FreeTemp(QCC_PR_StatementFlags(&pr_opcodes[OP_STORE_I64], source, dest, NULL, flags));\n\t\tbreak;\n\tcase ev_pointer:\n\t\tQCC_FreeTemp(QCC_PR_StatementFlags(&pr_opcodes[OP_STORE_P], source, dest, NULL, flags));\n\t\tbreak;\n\t}\n}\n//if readable, returns source (or dest if the store was folded), otherwise returns NULL\nstatic QCC_sref_t QCC_CollapseStore(QCC_sref_t dest, QCC_sref_t source, QCC_type_t *type, pbool readable, pbool preservedest)\n{\n\tif (opt_assignments && OpAssignsToC(statements[numstatements-1].op) && source.sym == statements[numstatements-1].c.sym && source.ofs == statements[numstatements-1].c.ofs)\n\t{\n\t\tif (source.sym->temp)\n\t\t{\n\t\t\tQCC_statement_t *statement = &statements[numstatements-1];\n\t\t\tstatement->c.sym = dest.sym;\n\t\t\tstatement->c.ofs = dest.ofs;\n\t\t\tdest.sym->referenced = true;\n\n\t\t\toptres_assignments++;\n\t\t\tQCC_FreeTemp(source);\n\t\t\tif (readable)\n\t\t\t\treturn dest;\n\t\t\tif (!preservedest)\n\t\t\t\tQCC_FreeTemp(dest);\n\t\t\treturn nullsref;\n\t\t}\n\t}\n\tQCC_StoreToSRef(dest, source, type, readable, preservedest);\n\tif (readable)\n\t\treturn source;\n\treturn nullsref;\n}\nstatic void QCC_StoreToPointer(QCC_sref_t dest, QCC_sref_t idx, QCC_sref_t source, QCC_type_t *type)\n{\n\tpbool freedest = false;\n\tif (type->type == ev_variant)\n\t\ttype = source.cast;\n\n\tif (type->type == ev_bitfld)\n\t{\n\t\tif (type->bits == 8)\n\t\t\tQCC_PR_Statement_SmallStore(OP_STOREP_I8, source, dest, idx);\n\t\telse if (type->bits == 16)\n\t\t\tQCC_PR_Statement_SmallStore(OP_STOREP_I16, source, dest, idx);\n\t\telse\n\t\t\tQCC_PR_ParseError(ERR_TYPEMISMATCH, \"ptr-to-bitfld has unsupported bit depth\");\n\t\tif (freedest)\n\t\t\tQCC_FreeTemp(dest);\n\t\treturn;\n\t}\n\n\twhile (type->type == ev_accessor)\n\t\ttype = type->parentclass;\n\n\tif (idx.sym)\n\t{\n\t\tif (type->align != 32)\n\t\t{\t//these all assume word indexes. but our index is actually elements... misalign. eww.\n\t\t\tidx = QCC_PR_StatementFlags(&pr_opcodes[OP_MUL_I], idx, QCC_MakeIntConst(32/type->align), NULL, STFL_PRESERVEA);\n\t\t\tdest = QCC_PR_StatementFlags(&pr_opcodes[OP_ADD_I], dest, idx, NULL, STFL_PRESERVEA);\n\t\t\tidx = nullsref;\n\t\t}\n\t\telse if (!QCC_OPCode_StorePOffset())\n\t\t{\t//can't do an offset store yet... bake any index into the pointer.\n\t\t\tfreedest = true;\n\t\t\tdest = QCC_PR_StatementFlags(&pr_opcodes[OP_ADD_PIW], dest, idx, NULL, STFL_PRESERVEA);\n\t\t\tidx = nullsref;\n\t\t}\n\t}\n\n\t//fixme: we should probably handle entire structs or something\n\tswitch(type->type)\n\t{\n\tdefault:\n\tcase ev_struct:\n\tcase ev_union:\n\t\t{\n\t\t\tunsigned int i = 0;\n\t\t\tif (QCC_OPCode_StorePOffset())\n\t\t\t{\t//store-with-offset works.\n\t\t\t\tunsigned int bits = type->bits;\n\t\t\t\tif (!bits)\n\t\t\t\t\tbits = type->size*32;\n\t\t\t\tif (0 && bits > 96)\t//if its going to be bit, we can save some adds by combining pointer and base... but its unsafe where tempbuffers are involved.\n\t\t\t\t{\n\t\t\t\t\tdest = QCC_PR_StatementFlags(&pr_opcodes[OP_ADD_PIW], dest, idx, NULL, STFL_PRESERVEA);\n\t\t\t\t\tidx = nullsref;\n\t\t\t\t}\n\t\t\t\tif (QCC_OPCodeValid(&pr_opcodes[OP_STOREP_V]))\n\t\t\t\tfor (; i+(32*3) <= bits; i+=32*3)\n\t\t\t\t{\n\t\t\t\t\tQCC_sref_t newidx = idx.sym?QCC_PR_StatementFlags(&pr_opcodes[OP_ADD_I], idx, QCC_MakeIntConst(i/32), NULL, STFL_PRESERVEA):QCC_MakeIntConst(i/32);\n\t\t\t\t\tif (QCC_SRef_IsNull(source))\n\t\t\t\t\t\tQCC_PR_SimpleStatement(&pr_opcodes[OP_STOREP_V], QCC_MakeVectorConst(0,0,0), dest, newidx, false);\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tQCC_PR_SimpleStatement(&pr_opcodes[OP_STOREP_V], source, dest, newidx, false);\n\t\t\t\t\t\tsource.ofs += 3;\n\t\t\t\t\t}\n\t\t\t\t\tQCC_FreeTemp(newidx);\n\t\t\t\t}\n\t\t\t\tif (QCC_OPCodeValid(&pr_opcodes[OP_STOREP_I64]))\n\t\t\t\tfor (; i+(32*2) <= type->size; i+=32*2)\n\t\t\t\t{\n\t\t\t\t\tQCC_sref_t newidx = idx.sym?QCC_PR_StatementFlags(&pr_opcodes[OP_ADD_I], idx, QCC_MakeIntConst(i/32), NULL, STFL_PRESERVEA):QCC_MakeIntConst(i/32);\n\t\t\t\t\tif (QCC_SRef_IsNull(source))\n\t\t\t\t\t\tQCC_PR_SimpleStatement(&pr_opcodes[OP_STOREP_I64], QCC_MakeVectorConst(0,0,0), dest, newidx, false);\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tQCC_PR_SimpleStatement(&pr_opcodes[OP_STOREP_I64], source, dest, newidx, false);\n\t\t\t\t\t\tsource.ofs += 2;\n\t\t\t\t\t}\n\t\t\t\t\tQCC_FreeTemp(newidx);\n\t\t\t\t}\n\t\t\t\tfor (; i+32 <= bits; i+=32)\n\t\t\t\t{\n\t\t\t\t\tQCC_sref_t  newidx = idx.sym?QCC_PR_StatementFlags(&pr_opcodes[OP_ADD_I], idx, QCC_MakeIntConst(i/32), NULL, STFL_PRESERVEA):QCC_MakeIntConst(i/32);\n\t\t\t\t\tif (QCC_OPCodeValid(&pr_opcodes[OP_STOREP_I]))\n\t\t\t\t\t\tQCC_PR_SimpleStatement(&pr_opcodes[OP_STOREP_I], source, dest, newidx, false);\n\t\t\t\t\telse\n\t\t\t\t\t\tQCC_PR_SimpleStatement(&pr_opcodes[OP_STOREP_F], source, dest, newidx, false);\n\t\t\t\t\tQCC_FreeTemp(newidx);\n\t\t\t\t\tsource.ofs += 1;\n\t\t\t\t}\n\t\t\t\tif (i+16 <= bits)\n\t\t\t\t{\n\t\t\t\t\tQCC_sref_t  newidx = idx.sym?QCC_PR_StatementFlags(&pr_opcodes[OP_ADD_I], QCC_PR_StatementFlags(&pr_opcodes[OP_MUL_I], idx, QCC_MakeIntConst(2), NULL, STFL_PRESERVEA), QCC_MakeIntConst(i/16), NULL, 0):QCC_MakeIntConst(i/16);\n\t\t\t\t\tQCC_PR_Statement_SmallStore(OP_STOREP_I16, source, dest, newidx);\n\t\t\t\t\tQCC_FreeTemp(newidx);\n\t\t\t\t\tsource.ofs += 1;\n\t\t\t\t\ti+=16;\n\t\t\t\t}\n\t\t\t\telse if (i+8 <= bits)\n\t\t\t\t{\n\t\t\t\t\tQCC_sref_t  newidx = idx.sym?QCC_PR_StatementFlags(&pr_opcodes[OP_ADD_PIW], QCC_MakeIntConst(i/8), idx, NULL, STFL_PRESERVEB):QCC_MakeIntConst(i/8);\n\t\t\t\t\tQCC_PR_Statement_SmallStore(OP_STOREP_I8, source, dest, newidx);\n\t\t\t\t\tQCC_FreeTemp(newidx);\n\t\t\t\t\tsource.ofs += 1;\n\t\t\t\t\ti+=8;\n\t\t\t\t}\n\t\t\t\tif (i != bits)\n\t\t\t\t\tQCC_PR_ParseError(ERR_INTERNAL, \"Underwrote\");\n\t\t\t}\n\t\t\telse\n\t\t\t{\t//no store-with-offset.\n\t\t\t\tfor (i = 0; i+2 < type->size; i+=3)\n\t\t\t\t{\n\t\t\t\t\tQCC_sref_t newptr = QCC_PR_StatementFlags(&pr_opcodes[OP_ADD_PIW], dest, QCC_MakeIntConst(i), NULL, STFL_PRESERVEA);\n\t\t\t\t\tif (QCC_SRef_IsNull(source))\n\t\t\t\t\t\tQCC_PR_SimpleStatement(&pr_opcodes[OP_STOREP_V], QCC_MakeVectorConst(0,0,0), newptr, idx, false);\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tQCC_PR_SimpleStatement(&pr_opcodes[OP_STOREP_V], source, newptr, idx, false);\n\t\t\t\t\t\tsource.ofs += 3;\n\t\t\t\t\t}\n\t\t\t\t\tQCC_FreeTemp(newptr);\n\t\t\t\t}\n\t\t\t\tif (QCC_OPCodeValid(&pr_opcodes[OP_STOREP_I64]))\n\t\t\t\tfor (; i+1 < type->size; i+=2)\n\t\t\t\t{\n\t\t\t\t\tQCC_sref_t newptr = QCC_PR_StatementFlags(&pr_opcodes[OP_ADD_PIW], dest, QCC_MakeIntConst(i), NULL, STFL_PRESERVEA);\n\t\t\t\t\tif (QCC_SRef_IsNull(source))\n\t\t\t\t\t\tQCC_PR_SimpleStatement(&pr_opcodes[OP_STOREP_I64], QCC_MakeVectorConst(0,0,0), newptr, idx, false);\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tQCC_PR_SimpleStatement(&pr_opcodes[OP_STOREP_I64], source, newptr, idx, false);\n\t\t\t\t\t\tsource.ofs += 2;\n\t\t\t\t\t}\n\t\t\t\t\tQCC_FreeTemp(newptr);\n\t\t\t\t}\n\t\t\t\tfor (; i < type->size; i++)\n\t\t\t\t{\n\t\t\t\t\tQCC_sref_t newptr = QCC_PR_StatementFlags(&pr_opcodes[OP_ADD_PIW], dest, QCC_MakeIntConst(i), NULL, STFL_PRESERVEA);\n\t\t\t\t\tif (QCC_SRef_IsNull(source))\n\t\t\t\t\t\tQCC_PR_SimpleStatement(&pr_opcodes[OP_STOREP_F], QCC_MakeIntConst(0), newptr, idx, false);\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tQCC_PR_SimpleStatement(&pr_opcodes[OP_STOREP_F], source, newptr, idx, false);\n\t\t\t\t\t\tsource.ofs += 1;\n\t\t\t\t\t}\n\t\t\t\t\tQCC_FreeTemp(newptr);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tbreak;\n\tcase ev_float:\n\t\tQCC_PR_SimpleStatement(&pr_opcodes[OP_STOREP_F], source, dest, idx, false);\n\t\tbreak;\n\tcase ev_vector:\n\t\tQCC_PR_SimpleStatement(&pr_opcodes[OP_STOREP_V], source, dest, idx, false);\n\t\tbreak;\n\tcase ev_entity:\n\t\tQCC_PR_SimpleStatement(&pr_opcodes[OP_STOREP_ENT], source, dest, idx, false);\n\t\tbreak;\n\tcase ev_string:\n\t\tQCC_PR_SimpleStatement(&pr_opcodes[OP_STOREP_S], source, dest, idx, false);\n\t\tbreak;\n\tcase ev_function:\n\t\tQCC_PR_SimpleStatement(&pr_opcodes[OP_STOREP_FNC], source, dest, idx, false);\n\t\tbreak;\n\tcase ev_field:\n\t\tQCC_PR_SimpleStatement(&pr_opcodes[OP_STOREP_FLD], source, dest, idx, false);\n\t\tbreak;\n\tcase ev_integer:\n\tcase ev_uint:\n\t\tif (QCC_OPCodeValid(&pr_opcodes[OP_STOREP_I]))\n\t\t\tQCC_PR_SimpleStatement(&pr_opcodes[OP_STOREP_I], source, dest, idx, false);\n\t\telse\n\t\t\tQCC_PR_SimpleStatement(&pr_opcodes[OP_STOREP_FLD], source, dest, idx, false);\n\t\tbreak;\n\tcase ev_double:\n\tcase ev_int64:\n\tcase ev_uint64:\n\t\tif (QCC_OPCodeValid(&pr_opcodes[OP_STOREP_I64]))\n\t\t\tQCC_PR_SimpleStatement(&pr_opcodes[OP_STOREP_I64], source, dest, idx, false);\n\t\telse\n\t\t{\n\t\t\tQCC_PR_SimpleStatement(&pr_opcodes[OP_STOREP_FLD], source, dest, idx, false);\n\t\t\tif (QCC_OPCode_StorePOffset())\n\t\t\t\tidx = idx.sym?QCC_PR_StatementFlags(&pr_opcodes[OP_ADD_I], idx, QCC_MakeIntConst(1), NULL, STFL_PRESERVEA):QCC_MakeIntConst(1);\n\t\t\telse\n\t\t\t\tdest = QCC_PR_StatementFlags(&pr_opcodes[OP_ADD_PIW], dest, QCC_MakeIntConst(1), NULL, STFL_PRESERVEA);\n\t\t\tsource.ofs+=1;\n\t\t\tQCC_PR_SimpleStatement(&pr_opcodes[OP_STOREP_FLD], source, dest, idx, false);\n\t\t}\n\t\tbreak;\n\tcase ev_pointer:\n\t\tif (QCC_OPCodeValid(&pr_opcodes[OP_STOREP_P]))\n\t\t\tQCC_PR_SimpleStatement(&pr_opcodes[OP_STOREP_P], source, dest, idx, false);\n\t\telse if (QCC_OPCodeValid(&pr_opcodes[OP_STOREP_I]))\n\t\t\tQCC_PR_SimpleStatement(&pr_opcodes[OP_STOREP_I], source, dest, idx, false);\n\t\telse\n\t\t\tQCC_PR_SimpleStatement(&pr_opcodes[OP_STOREP_FLD], source, dest, idx, false);\n\t\tbreak;\n\t}\n\tif (freedest)\n\t\tQCC_FreeTemp(dest);\n}\nstatic void QCC_StoreToGPointer(QCC_sref_t dest, QCC_sref_t source, QCC_type_t *type)\n{\n\tpbool freedest = false;\n\tif (type->type == ev_variant)\n\t\ttype = source.cast;\n\n\twhile (type->type == ev_accessor)\n\t\ttype = type->parentclass;\n\n\t//fixme: we should probably handle entire structs or something\n\tswitch(type->type)\n\t{\n\tdefault:\n\t\tQCC_PR_ParseErrorPrintSRef(ERR_INTERNAL, dest, \"QCC_StoreToPointer doesn't know how to store to that type\");\n\tcase ev_struct:\n\tcase ev_union:\n\tcase ev_double:\n\tcase ev_int64:\n\tcase ev_uint64:\n\t\t{\n\t\t\tunsigned int i;\n\t\t\tfor (i = 0; i+2 < type->size; i+=3)\n\t\t\t{\n\t\t\t\tQCC_sref_t newptr = QCC_PR_StatementFlags(&pr_opcodes[OP_ADD_I], dest, QCC_MakeIntConst(i), NULL, STFL_PRESERVEA);\n\t\t\t\tif (QCC_SRef_IsNull(source))\n\t\t\t\t\tQCC_PR_SimpleStatement(&pr_opcodes[OP_GSTOREP_V], QCC_MakeVectorConst(0,0,0), newptr, nullsref, false);\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tQCC_PR_SimpleStatement(&pr_opcodes[OP_GSTOREP_V], source, newptr, nullsref, false);\n\t\t\t\t\tsource.ofs += 3;\n\t\t\t\t}\n\t\t\t\tQCC_FreeTemp(newptr);\n\t\t\t}\n\t\t\tfor (; i < type->size; i++)\n\t\t\t{\n\t\t\t\tQCC_sref_t newptr = QCC_PR_StatementFlags(&pr_opcodes[OP_ADD_I], dest, QCC_MakeIntConst(i), NULL, STFL_PRESERVEA);\n\t\t\t\tif (QCC_SRef_IsNull(source))\n\t\t\t\t\tQCC_PR_SimpleStatement(&pr_opcodes[OP_GSTOREP_F], QCC_MakeIntConst(0), newptr, nullsref, false);\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tQCC_PR_SimpleStatement(&pr_opcodes[OP_GSTOREP_F], source, newptr, nullsref, false);\n\t\t\t\t\tsource.ofs += 1;\n\t\t\t\t}\n\t\t\t\tQCC_FreeTemp(newptr);\n\t\t\t}\n\t\t}\n\t\tbreak;\n\tcase ev_float:\n\t\tQCC_PR_SimpleStatement(&pr_opcodes[OP_GSTOREP_F], source, dest, nullsref, false);\n\t\tbreak;\n\tcase ev_vector:\n\t\tQCC_PR_SimpleStatement(&pr_opcodes[OP_GSTOREP_V], source, dest, nullsref, false);\n\t\tbreak;\n\tcase ev_entity:\n\t\tQCC_PR_SimpleStatement(&pr_opcodes[OP_GSTOREP_ENT], source, dest, nullsref, false);\n\t\tbreak;\n\tcase ev_string:\n\t\tQCC_PR_SimpleStatement(&pr_opcodes[OP_GSTOREP_S], source, dest, nullsref, false);\n\t\tbreak;\n\tcase ev_function:\n\t\tQCC_PR_SimpleStatement(&pr_opcodes[OP_GSTOREP_FNC], source, dest, nullsref, false);\n\t\tbreak;\n\tcase ev_field:\n\t\tQCC_PR_SimpleStatement(&pr_opcodes[OP_GSTOREP_FLD], source, dest, nullsref, false);\n\t\tbreak;\n\tcase ev_integer:\n\tcase ev_uint:\n\tcase ev_pointer:\n\t\tQCC_PR_SimpleStatement(&pr_opcodes[OP_GSTOREP_I], source, dest, nullsref, false);\n\t\tbreak;\n\t}\n\tif (freedest)\n\t\tQCC_FreeTemp(dest);\n}\nstatic QCC_sref_t QCC_LoadFromPointer(QCC_sref_t source, QCC_sref_t idx, QCC_type_t *type)\n{\n\tQCC_sref_t ret;\n\tint op;\n\n\tif (type->type == ev_bitfld)\n\t{\n\t\tif (type->bits == 8)\n\t\t{\n\t\t\tret = QCC_PR_StatementFlags(&pr_opcodes[(type->parentclass->type == ev_integer)?OP_LOADP_I8:OP_LOADP_U8], source, idx, NULL, STFL_PRESERVEA|STFL_PRESERVEB);\t//get pointer to precise def.\n\t\t\tret.cast = type->parentclass;\n\t\t\treturn ret;\n\t\t}\n\t\telse if (type->bits == 16)\n\t\t{\n\t\t\tret = QCC_PR_StatementFlags(&pr_opcodes[(type->parentclass->type == ev_integer)?OP_LOADP_I16:OP_LOADP_U16], source, idx, NULL, STFL_PRESERVEA|STFL_PRESERVEB);\t//get pointer to precise def.\n\t\t\tret.cast = type->parentclass;\n\t\t\treturn ret;\n\t\t}\n\t\tQCC_PR_ParseError(ERR_TYPEMISMATCH, \"ptr-to-bitfld has unsupported bit depth\");\n\t}\n//\tif (type->align != 32)\n//\t\tQCC_PR_ParseWarning(ERR_TYPEMISMATCH, \"(%s) align is %i, not 32... %ibit\", type->name, type->align, type->bits);\n\n\twhile (type->type == ev_accessor)\n\t\ttype = type->parentclass;\n\n\tswitch(type->type)\n\t{\n\tdefault:\n\tcase ev_struct:\n\tcase ev_union:\n\t\t{\n\t\t\tunsigned int i;\n\t\t\tret = QCC_GetTemp(type);\n\t\t\tfor (i = 0; i+2 < type->size; i+=3)\n\t\t\t{\n\t\t\t\tQCC_sref_t ofs = QCC_MakeIntConst(i);\n\t\t\t\tif (idx.sym)\n\t\t\t\t\tofs = QCC_PR_StatementFlags(&pr_opcodes[OP_ADD_I], idx, ofs, NULL, STFL_PRESERVEA);\n\n\t\t\t\tQCC_PR_SimpleStatement(&pr_opcodes[OP_LOADP_V], source, ofs, ret, false);\n\t\t\t\tQCC_FreeTemp(ofs);\n\t\t\t\tret.ofs += 3;\n\t\t\t}\n\t\t\tfor (; i < type->size; i++)\n\t\t\t{\n\t\t\t\tQCC_sref_t ofs = QCC_MakeIntConst(i);\n\t\t\t\tif (idx.sym)\n\t\t\t\t\tofs = QCC_PR_StatementFlags(&pr_opcodes[OP_ADD_I], idx, ofs, NULL, STFL_PRESERVEA);\n\n\t\t\t\tQCC_PR_SimpleStatement(&pr_opcodes[OP_LOADP_I], source, ofs, ret, false);\n\t\t\t\tQCC_FreeTemp(ofs);\n\t\t\t\tret.ofs += 1;\n\t\t\t}\n\t\t\tret.ofs -= type->size;\n\t\t\treturn ret;\n\t\t}\n\t\tbreak;\n//\tcase ev_double:\t\top = OP_LOADP_D;\tbreak;\n\tcase ev_int64:\t\top = OP_LOADP_I64;\tbreak;\n\tcase ev_uint64:\t\top = OP_LOADP_I64;\tbreak;\n\tcase ev_float:\t\top = OP_LOADP_F;\tbreak;\n\tcase ev_string:\t\top = OP_LOADP_S;\tbreak;\n\tcase ev_vector:\t\top = OP_LOADP_V;\tbreak;\n\tcase ev_entity:\t\top = OP_LOADP_ENT;\tbreak;\n\tcase ev_field:\t\top = OP_LOADP_FLD;\tbreak;\n\tcase ev_function:\top = OP_LOADP_FNC;\tbreak;\n\tcase ev_integer:\top = OP_LOADP_I;\tbreak;\n\tcase ev_uint:\t\top = OP_LOADP_I;\tbreak;\n\tcase ev_void:\n\tcase ev_variant:\n\tcase ev_bitfld:\n\t\tQCC_PR_ParseError(ERR_TYPEMISMATCH, \"ptr-to-variants must be cast to a different type before being read\");\n\t\top = OP_LOADP_I;\tbreak;\n\tcase ev_pointer:\n\t\top = OP_LOADP_P;\n\t\tif (!QCC_OPCodeValid(&pr_opcodes[op]))\n\t\t\top = OP_LOADP_I;\n\t\tbreak;\n\t}\n\tret = QCC_PR_StatementFlags(&pr_opcodes[op], source, idx, NULL, STFL_PRESERVEA|STFL_PRESERVEB);\t//get pointer to precise def.\n\tret.cast = type;\n\treturn ret;\n}\nstatic void QCC_StoreToArray(QCC_sref_t base, QCC_sref_t index, QCC_sref_t source, QCC_type_t *t)\n{\n\t/*if its assigned to, generate a functioncall to do the store*/\n\tQCC_sref_t funcretr;\n\n\tif (QCC_OPCodeValid(&pr_opcodes[OP_GLOBALADDRESS]))\n\t{\n\t\tQCC_sref_t addr;\n\t\t//ptr = &base[index];\n\t\tif (QCC_OPCode_StorePOffset())\n\t\t\taddr = QCC_PR_Statement(&pr_opcodes[OP_GLOBALADDRESS], base, nullsref, NULL);\n\t\telse\n\t\t{\n\t\t\taddr = QCC_PR_Statement(&pr_opcodes[OP_GLOBALADDRESS], base, index, NULL);\n\t\t\tindex = nullsref;\n\t\t}\n\t\t//*ptr = source\n\t\tQCC_StoreToPointer(addr, index.cast?QCC_SupplyConversion(index, ev_integer, true):index, source, t);\n\t\tsource.sym->referenced = true;\n\t\tQCC_FreeTemp(addr);\n\t\tQCC_FreeTemp(source);\n\t}\n\telse if (QCC_OPCodeValid(&pr_opcodes[OP_GSTOREP_F]))\n\t{\n\t\tQCC_sref_t addr;\n\t\t//ptr = &base[index];\n\t\taddr = QCC_DP_GlobalAddress(base, QCC_SupplyConversion(index, ev_integer, true), 0);\n\t\t//*ptr = source\n\t\tQCC_StoreToGPointer(addr, source, t);\n\t\tsource.sym->referenced = true;\n\t\tQCC_FreeTemp(addr);\n\t\tQCC_FreeTemp(source);\n\t}\n\telse\n\t{\n\t\tconst char *basename = QCC_GetSRefName(base);\n\t\tbase.sym->referenced = true;\n\t\tQCC_FreeTemp(base);\n\n\t\tfuncretr = QCC_PR_GetSRef(NULL, qcva(\"ArraySet*%s\", basename), base.sym->scope, false, 0, GDF_CONST|(base.sym->scope?GDF_STATIC:0));\n\t\tif (!funcretr.cast)\n\t\t{\n\t\t\tQCC_type_t *arraysetfunc = qccHunkAlloc(sizeof(*arraysetfunc));\n\t\t\tstruct QCC_typeparam_s *fparms = qccHunkAlloc(sizeof(*fparms)*2);\n\t\t\tarraysetfunc->size = 1;\n\t\t\tarraysetfunc->type = ev_function;\n\t\t\tarraysetfunc->aux_type = type_void;\n\t\t\tarraysetfunc->params = fparms;\n\t\t\tarraysetfunc->num_parms = 2;\n\t\t\tarraysetfunc->name = \"ArraySet\";\n\t\t\tfparms[0].type = type_float;\n\t\t\tfparms[1].type = base.sym->type;\n\t\t\tfuncretr = QCC_PR_GetSRef(arraysetfunc, qcva(\"ArraySet*%s\", basename), base.sym->scope, true, 0, GDF_CONST|(base.sym->scope?GDF_STATIC:0));\n\t\t\tfuncretr.sym->generatedfor = base.sym;\n\t\t}\n\n\t\tif (QCC_SRef_IsNull(source))\n\t\t{\n\t\t\tif (base.sym->type->type == ev_vector)\n\t\t\t\tsource = QCC_MakeVectorConst(0,0,0);\n\t\t\telse\n\t\t\t\tsource = QCC_MakeFloatConst(0);\n\t\t}\n\t\telse if (source.cast->type != t->type)\n\t\t{\n\t\t\tchar typea[128], typeb[128];\n\t\t\tTypeName(source.cast, typea, sizeof(typea));\n\t\t\tTypeName(t, typeb, sizeof(typeb));\n\t\t\tQCC_PR_ParseErrorPrintSRef(ERR_TYPEMISMATCH, base, \"Type mismatch on indexed assignment of %s: %s %s, needed %s.\", basename, typea, QCC_GetSRefName(source), typeb);\n\t\t}\n\n\t\tif (base.sym->type->type == ev_vector)\n\t\t{\n\t\t\t//FIXME: we may very well have a *3 already, dividing by 3 again is crazy.\n\t\t\tindex = QCC_PR_Statement(&pr_opcodes[OP_DIV_F], index, QCC_MakeFloatConst(3), NULL);\n\t\t}\n\n\t\tqcc_usefulstatement=true;\n\t\tQCC_FreeTemp(QCC_PR_GenerateFunctionCall2(nullsref, funcretr, QCC_SupplyConversion(index, ev_float, true), NULL, source, NULL));\n\t}\n}\nQCC_sref_t QCC_LoadFromArray(QCC_sref_t base, QCC_sref_t index, QCC_type_t *t, pbool preserve)\n{\n\tint flags;\n\tint accel;\n\n\t//1: hexen2's FETCH_GBL opcodes take float indicies, but have a built in boundscheck that wrecks havoc with vectors and structs (and thus sucks when the types don't match)\n\t//2: fte's LOADA opcodes use ints without hidden boundcheck (engine will still ensure it reads an actual global), and vectors are not special(needs a separate *3 opcode) so they're struct friendly.\n\t//3: dp's GLOAD opcodes -- like fte's but without any offsetting so you need an extra addition.\n\n\tif (index.cast->type != ev_float || t->type != base.cast->type)\n\t\taccel = 2;\n\telse\n\t\taccel = 1;\n\n\tif (accel == 2 && !QCC_OPCodeValid(&pr_opcodes[OP_LOADA_F]))\n\t\taccel = QCC_OPCodeValid(&pr_opcodes[OP_GLOAD_F])&&!base.sym->temp?3:1;\t//chose best for ints\n\tif (accel == 1 && (!base.sym->arraylengthprefix || !QCC_OPCodeValid(&pr_opcodes[OP_FETCH_GBL_F])))\n\t\taccel = QCC_OPCodeValid(&pr_opcodes[OP_LOADA_F])?2:0;\t//chose best for floats\n\n\tif (accel == 3)\n\t{\t//dp-style, somewhat annoying.\n\t\tif (index.cast->type == ev_integer)\n\t\t\tflags = preserve?STFL_PRESERVEA|STFL_PRESERVEB:0;\n\t\telse\n\t\t{\n\t\t\tflags = preserve?STFL_PRESERVEA:0;\n\t\t\tif (preserve)\n\t\t\t{\n\t\t\t\tflags = STFL_PRESERVEA;\n\t\t\t\tQCC_UnFreeTemp(index);\n\t\t\t}\n\t\t\tQCC_SupplyConversion(index, ev_integer, true);\n\t\t}\n\n\t\tindex = QCC_DP_GlobalAddress(base, index, flags);\n\t\tswitch(t->type)\n\t\t{\n\t\tcase ev_string:\n\t\t\tbase = QCC_PR_StatementFlags(&pr_opcodes[OP_GLOAD_S], index, nullsref, NULL, flags);\t//get pointer to precise def.\n\t\t\tbreak;\n\t\tcase ev_float:\n\t\t\tbase = QCC_PR_StatementFlags(&pr_opcodes[OP_GLOAD_F], index, nullsref, NULL, flags);\t//get pointer to precise def.\n\t\t\tbreak;\n\t\tcase ev_vector:\n\t\t\tbase = QCC_PR_StatementFlags(&pr_opcodes[OP_GLOAD_V], index, nullsref, NULL, flags);\t//get pointer to precise def.\n\t\t\tbreak;\n\t\tcase ev_entity:\n\t\t\tbase = QCC_PR_StatementFlags(&pr_opcodes[OP_GLOAD_ENT], index, nullsref, NULL, flags);\t//get pointer to precise def.\n\t\t\tbreak;\n\t\tcase ev_field:\n\t\t\tbase = QCC_PR_StatementFlags(&pr_opcodes[OP_GLOAD_FLD], index, nullsref, NULL, flags);\t//get pointer to precise def.\n\t\t\tbreak;\n\t\tcase ev_function:\n\t\t\tbase = QCC_PR_StatementFlags(&pr_opcodes[OP_GLOAD_FNC], index, nullsref, NULL, flags);\t//get pointer to precise def.\n\t\t\tbreak;\n\t\tcase ev_pointer:\n\t\tcase ev_integer:\n\t\t\tbase = QCC_PR_StatementFlags(&pr_opcodes[OP_GLOAD_I], index, nullsref, NULL, flags);\t//get pointer to precise def.\n\t\t\tbreak;\n\t\tcase ev_variant:\n\t\tcase ev_struct:\n\t\tcase ev_union:\n\t\tcase ev_int64:\n\t\tcase ev_uint64:\n\t\tcase ev_double:\n\t\t\t{\n\t\t\t\tQCC_sref_t r;\n\t\t\t\tunsigned int i;\n\t\t\t\tr = QCC_GetTemp(t);\n\n\t\t\t\t//we can just bias the base offset instead of lots of statements to add to the index. how handy.\n\t\t\t\tfor (i = 0; i < t->size; )\n\t\t\t\t{\n\t\t\t\t\tif (t->size - i >= 3)\n\t\t\t\t\t{\n\t\t\t\t\t\tQCC_PR_SimpleStatement(&pr_opcodes[OP_GLOAD_V], index, nullsref, r, false);\n\t\t\t\t\t\ti+=3;\n\t\t\t\t\t\tindex = QCC_PR_Statement(&pr_opcodes[OP_ADD_I], index, QCC_MakeIntConst(3), NULL);\n\t\t\t\t\t\tr.ofs+=3;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tQCC_PR_SimpleStatement(&pr_opcodes[OP_GLOAD_I], index, nullsref, r, false);\n\t\t\t\t\t\ti++;\n\t\t\t\t\t\tindex = QCC_PR_Statement(&pr_opcodes[OP_ADD_I], index, QCC_MakeIntConst(1), NULL);\n\t\t\t\t\t\tr.ofs++;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (!preserve)\n\t\t\t\t\tQCC_FreeTemp(index);\n\t\t\t\tr.ofs -= i;\n\t\t\t\treturn r;\n\t\t\t}\n\t\t\treturn nullsref;\n\t\tdefault:\n\t\t\tQCC_PR_ParseError(ERR_NOVALIDOPCODES, \"Unable to load type %s with GLOAD... oops.\", basictypenames[t->type]);\n\t\t\treturn nullsref;\n\t\t}\n\n\t\tbase.cast = t;\n\t\treturn base;\n\t}\n\telse if (accel == 2)\n\t{\t//fte-style, simpler indexing.\n\t\tif (index.cast->type == ev_integer)\n\t\t\tflags = preserve?STFL_PRESERVEA|STFL_PRESERVEB:0;\n\t\telse\n\t\t{\n\t\t\tflags = preserve?STFL_PRESERVEA:0;\n\t\t\tif (preserve)\n\t\t\t{\n\t\t\t\tflags = STFL_PRESERVEA;\n\t\t\t\tQCC_UnFreeTemp(index);\n\t\t\t}\n\t\t\tindex = QCC_SupplyConversion(index, ev_integer, true);\n\t\t}\n\t\tswitch(t->type)\n\t\t{\n\t\tcase ev_string:\n\t\t\tbase = QCC_PR_StatementFlags(&pr_opcodes[OP_LOADA_S], base, index, NULL, flags);\t//get pointer to precise def.\n\t\t\tbreak;\n\t\tcase ev_float:\n\t\t\tbase = QCC_PR_StatementFlags(&pr_opcodes[OP_LOADA_F], base, index, NULL, flags);\t//get pointer to precise def.\n\t\t\tbreak;\n\t\tcase ev_vector:\n\t\t\tbase = QCC_PR_StatementFlags(&pr_opcodes[OP_LOADA_V], base, index, NULL, flags);\t//get pointer to precise def.\n\t\t\tbreak;\n\t\tcase ev_entity:\n\t\t\tbase = QCC_PR_StatementFlags(&pr_opcodes[OP_LOADA_ENT], base, index, NULL, flags);\t//get pointer to precise def.\n\t\t\tbreak;\n\t\tcase ev_field:\n\t\t\tbase = QCC_PR_StatementFlags(&pr_opcodes[OP_LOADA_FLD], base, index, NULL, flags);\t//get pointer to precise def.\n\t\t\tbreak;\n\t\tcase ev_function:\n\t\t\tbase = QCC_PR_StatementFlags(&pr_opcodes[OP_LOADA_FNC], base, index, NULL, flags);\t//get pointer to precise def.\n\t\t\tbreak;\n\t\tcase ev_pointer:\t//no OP_LOADA_P\n\t\tcase ev_integer:\n\t\tcase ev_uint:\n\t\t\tbase = QCC_PR_StatementFlags(&pr_opcodes[OP_LOADA_I], base, index, NULL, flags);\t//get pointer to precise def.\n\t\t\tbreak;\n\t\tcase ev_int64:\n\t\tcase ev_uint64:\n\t\tcase ev_double:\n\t\t\tbase = QCC_PR_StatementFlags(&pr_opcodes[OP_LOADA_I64], base, index, NULL, flags);\t//get pointer to precise def.\n\t\t\tbreak;\n\t\tcase ev_bitfld:\n\t\tcase ev_variant:\n\t\tcase ev_struct:\n\t\tcase ev_union:\n\t\t\t{\n\t\t\t\tQCC_sref_t r;\n\t\t\t\tunsigned int i;\n\t\t\t\tr = QCC_GetTemp(t);\n\n\t\t\t\t//we can just bias the base offset instead of lots of statements to add to the index. how handy.\n\t\t\t\tfor (i = 0; i < t->size; )\n\t\t\t\t{\n\t\t\t\t\tif (t->size - i >= 3)\n\t\t\t\t\t{\n\t\t\t\t\t\tQCC_PR_SimpleStatement(&pr_opcodes[OP_LOADA_V], base, index, r, false);\n\t\t\t\t\t\ti+=3;\n\t\t\t\t\t\tbase.ofs += 3;\n\t\t\t\t\t\tr.ofs+=3;\n\t\t\t\t\t}\n\t\t\t\t\telse if (t->size - i >= 2 && QCC_OPCodeValid(&pr_opcodes[OP_LOADA_I64]))\n\t\t\t\t\t{\n\t\t\t\t\t\tQCC_PR_SimpleStatement(&pr_opcodes[OP_LOADA_I64], base, index, r, false);\n\t\t\t\t\t\ti+=2;\n\t\t\t\t\t\tbase.ofs += 2;\n\t\t\t\t\t\tr.ofs+=2;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tQCC_PR_SimpleStatement(&pr_opcodes[OP_LOADA_I], base, index, r, false);\n\t\t\t\t\t\ti++;\n\t\t\t\t\t\tbase.ofs++;\n\t\t\t\t\t\tr.ofs++;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (!preserve)\n\t\t\t\t{\n\t\t\t\t\tQCC_FreeTemp(base);\n\t\t\t\t\tQCC_FreeTemp(index);\n\t\t\t\t}\n\t\t\t\tr.ofs -= i;\n\t\t\t\tbase.ofs -= i;\n\t\t\t\treturn r;\n\t\t\t}\n\t\t\treturn nullsref;\n\t\tdefault:\n\t\t\tQCC_PR_ParseError(ERR_NOVALIDOPCODES, \"Unable to load type %s with LOADA... oops.\", basictypenames[t->type]);\n\t\t\treturn nullsref;\n\t\t}\n\n\t\tbase.cast = t;\n\t\treturn base;\n\t}\n\telse if (accel == 1)\n\t{\t//hexen2-style, using float indexes and built-in bounds that hurts for arrays in structs.\n\t\tif (!base.sym->arraysize)\n\t\t\tQCC_PR_ParseErrorPrintSRef(ERR_TYPEMISMATCH, base, \"array lookup on non-array\");\n\t\tif (!base.sym->arraylengthprefix)\n\t\t\tQCC_PR_ParseErrorPrintSRef(ERR_TYPEMISMATCH, base, \"array lookup on symbol without length prefix\");\n\n\t\tif (base.sym->temp)\n\t\t\tQCC_PR_ParseErrorPrintSRef(ERR_TYPEMISMATCH, base, \"array lookup on a temp\");\n\n\t\tif (index.cast->type == ev_float)\n\t\t\tflags = preserve?STFL_PRESERVEA|STFL_PRESERVEB:0;\n\t\telse\n\t\t{\n\t\t\tflags = preserve?STFL_PRESERVEA:0;\n\t\t\tif (preserve)\n\t\t\t{\n\t\t\t\tflags = STFL_PRESERVEA;\n\t\t\t\tQCC_UnFreeTemp(index);\n\t\t\t}\n\t\t\tQCC_SupplyConversion(index, ev_float, true);\n\t\t}\n\n\t\t/*hexen2 format has opcodes to read arrays (but has no way to write)*/\n\t\tswitch(t->type)\n\t\t{\n\t\tcase ev_field:\n\t\tcase ev_pointer:\n\t\tcase ev_integer:\n\t\tcase ev_float:\n\t\t\tbase = QCC_PR_StatementFlags(&pr_opcodes[OP_FETCH_GBL_F], base, index, NULL, flags);\t//get pointer to precise def.\n\t\t\tbase.cast = t;\n\t\t\tbreak;\n\t\tcase ev_vector:\n\t\t\t//hexen2 uses element indicies. we internally use words.\n\t\t\t//words means you can pack vectors into structs without the offset needing to be a multiple of 3.\n\t\t\t//as its floats, I'm going to try using 0/0.33/0.66 just for the luls\n\t\t\t//FIXME: we may very well have a *3 already, dividing by 3 again is crazy.\n\t\t\tindex = QCC_PR_StatementFlags(&pr_opcodes[OP_DIV_F], index, QCC_MakeFloatConst(3), NULL, flags&STFL_PRESERVEA);\n\t\t\tflags &= ~STFL_PRESERVEB;\n\t\t\tbase = QCC_PR_StatementFlags(&pr_opcodes[OP_FETCH_GBL_V], base, index, NULL, flags);\t//get pointer to precise def.\n\t\t\tbreak;\n\t\tcase ev_string:\n\t\t\tbase = QCC_PR_StatementFlags(&pr_opcodes[OP_FETCH_GBL_S], base, index, NULL, flags);\t//get pointer to precise def.\n\t\t\tbreak;\n\t\tcase ev_entity:\n\t\t\tbase = QCC_PR_StatementFlags(&pr_opcodes[OP_FETCH_GBL_E], base, index, NULL, flags);\t//get pointer to precise def.\n\t\t\tbreak;\n\t\tcase ev_function:\n\t\t\tbase = QCC_PR_StatementFlags(&pr_opcodes[OP_FETCH_GBL_FNC], base, index, NULL, flags);\t//get pointer to precise def.\n\t\t\tbreak;\n\t\tdefault:\n\t\t\t{\n\t\t\t\tQCC_sref_t r, newidx;\n\t\t\t\tunsigned int i;\n\t\t\t\tr = QCC_GetTemp(t);\n\n\t\t\t\tfor (i = 0; i < t->size; )\n\t\t\t\t{\t//we can't cheese these offsets because OP_FETCH_GBL_ has some built-in bounds check based upon a global just before the start of the array.\n\t\t\t\t\t//I'm going to skip using vector reads, because offsets get weird then, necessitating division ops, and breaking bounds checks.\n\t\t\t\t\tif (i)\n\t\t\t\t\t{\n\t\t\t\t\t\tnewidx = QCC_PR_StatementFlags(&pr_opcodes[OP_ADD_F], index, QCC_MakeFloatConst(i), NULL, STFL_PRESERVEA);\n\t\t\t\t\t//\tif (t->size - i >= 3)\n\t\t\t\t\t//\t\tnewidx = QCC_PR_StatementFlags(&pr_opcodes[OP_DIV_F], newidx, QCC_MakeFloatConst(3), NULL, 0);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tnewidx = index;\n\t\t\t\t\t\tQCC_UnFreeTemp(newidx);\n\t\t\t\t\t}\n\n\t\t\t\t\t/*if (t->size - i >= 3)\n\t\t\t\t\t{\n\t\t\t\t\t\tQCC_PR_SimpleStatement(&pr_opcodes[OP_FETCH_GBL_V], base, newidx, r, false);\n\t\t\t\t\t\ti+=3;\n\t\t\t\t\t\tr.ofs+=3;\n\t\t\t\t\t}\n\t\t\t\t\telse*/\n\t\t\t\t\t{\n\t\t\t\t\t\tQCC_PR_SimpleStatement(&pr_opcodes[OP_FETCH_GBL_F], base, newidx, r, false);\n\t\t\t\t\t\ti++;\n\t\t\t\t\t\tr.ofs++;\n\t\t\t\t\t}\n\t\t\t\t\tQCC_FreeTemp(newidx);\n\t\t\t\t}\n\t\t\t\tif (!preserve)\n\t\t\t\t{\n\t\t\t\t\tQCC_FreeTemp(base);\n\t\t\t\t\tQCC_FreeTemp(index);\n\t\t\t\t}\n\t\t\t\tr.ofs -= i;\n\t\t\t\treturn r;\n\t\t\t}\n\t\t\tQCC_PR_ParseError(ERR_NOVALIDOPCODES, \"No op available to read array with FETCHGBL\");\n\t\t\treturn nullsref;\n\t\t}\n\t\tbase.cast = t;\n\t\treturn base;\n\t}\n\telse\n\t{\n\t\t/*emulate the array access using a function call to do the read for us*/\n\t\tQCC_sref_t args[1], funcretr;\n\n\t\tbase.sym->referenced = true;\n\n\t\tif (base.cast->type == ev_field && base.sym->constant && !base.sym->initialized && !flag_boundchecks && flag_fasttrackarrays)\n\t\t{\n\t\t\tint i;\n\t\t\t//denormalised floats means we could do:\n\t\t\t//return (add_f: base + (mul_f: index*1i))\n\t\t\t//make sure the array has no gaps\n\t\t\t//the initialised thing is to ensure that it doesn't contain random consecutive system fields that might get remapped weirdly by an engine.\n\t\t\tfor (i = 1; i < base.sym->arraysize; i++)\n\t\t\t{\n\t\t\t\tif (QCC_SRef_DataWord(base, i)->_int != QCC_SRef_DataWord(base, i-1)->_int+1)\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\t//its contiguous. we'll do this in two instructions.\n\t\t\tif (i == base.sym->arraysize)\n\t\t\t{\n\t\t\t\tif (QCC_OPCodeValid(&pr_opcodes[OP_ADD_I]))\n\t\t\t\t\treturn QCC_PR_StatementFlags(&pr_opcodes[OP_ADD_I], base, index, NULL, 0);\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tQCC_PR_ParseWarning(WARN_DENORMAL, \"using denormals to accelerate field-array access, which is unsafe\");\t\n\t\t\t\t\treturn QCC_PR_StatementFlags(&pr_opcodes[OP_ADD_F], base, QCC_PR_StatementFlags(&pr_opcodes[OP_MUL_F], index, QCC_MakeIntConst(1), NULL, 0), NULL, 0);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tfuncretr = QCC_PR_GetSRef(NULL, qcva(\"ArrayGet*%s\", QCC_GetSRefName(base)), base.sym->scope, false, 0, GDF_CONST|(base.sym->scope?GDF_STATIC:0));\n\t\tif (!funcretr.cast)\n\t\t{\n\t\t\tQCC_type_t *ftype = qccHunkAlloc(sizeof(*ftype));\n\t\t\tstruct QCC_typeparam_s *fparms = qccHunkAlloc(sizeof(*fparms)*1);\n\t\t\tftype->size = 1;\n\t\t\tftype->type = ev_function;\n\t\t\tftype->aux_type = base.cast;\n\t\t\tftype->params = fparms;\n\t\t\tftype->num_parms = 1;\n\t\t\tftype->name = \"ArrayGet\";\n\t\t\tfparms[0].type = type_float;\n\t\t\tfuncretr = QCC_PR_GetSRef(ftype, qcva(\"ArrayGet*%s\", QCC_GetSRefName(base)), base.sym->scope, true, 0, GDF_CONST|(base.sym->scope?GDF_STATIC:0));\n\t\t\tfuncretr.sym->generatedfor = base.sym;\n\t\t\tif (!funcretr.sym->constant)\n\t\t\t\texterns->Printf(\"not constant?\\n\");\n\t\t}\n\n\t\tif (preserve)\n\t\t\tQCC_UnFreeTemp(index);\n\t\telse\n\t\t\tQCC_FreeTemp(base);\n\n\t\t/*make sure the function type that we're calling exists*/\n\n\t\tif (base.sym->type->type == ev_vector)\n\t\t{\n\t\t\t//FIXME: we may very well have a *3 already, dividing by 3 again is crazy.\n\t\t\targs[0] = QCC_PR_Statement(&pr_opcodes[OP_DIV_F], QCC_SupplyConversion(index, ev_float, true), QCC_MakeFloatConst(3), NULL);\n\t\t\tbase = QCC_PR_GenerateFunctionCall1(nullsref, funcretr, args[0], NULL);\n\t\t\tbase.cast = t;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (t->size > 1)\n\t\t\t{\n\t\t\t\tQCC_sref_t r;\n\t\t\t\tunsigned int i;\n\t\t\t\tint old_op = opt_assignments;\n\t\t\t\tbase = QCC_GetTemp(t);\n\t\t\t\tindex = QCC_SupplyConversion(index, ev_float, true);\n\n\t\t\t\tfor (i = 0; i < t->size; i++)\n\t\t\t\t{\n\t\t\t\t\tif (i)\n\t\t\t\t\t\targs[0] = QCC_PR_StatementFlags(&pr_opcodes[OP_ADD_F], index, QCC_MakeFloatConst(i), NULL, STFL_PRESERVEA);\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tQCC_UnFreeTemp(index);\n\t\t\t\t\t\targs[0] = index;\n\t\t\t\t\t\topt_assignments = false;\n\t\t\t\t\t}\n\t\t\t\t\tQCC_UnFreeTemp(funcretr);\n\t\t\t\t\tr = QCC_PR_GenerateFunctionCall1(nullsref, funcretr, args[0], type_float);\n\t\t\t\t\topt_assignments = old_op;\n\t\t\t\t\tQCC_FreeTemp(QCC_PR_StatementFlags(&pr_opcodes[OP_STORE_F], r, base, NULL, STFL_PRESERVEB));\n\t\t\t\t\tbase.ofs++;\n\t\t\t\t}\n\t\t\t\tQCC_FreeTemp(funcretr);\n\t\t\t\tQCC_FreeTemp(index);\n\t\t\t\tbase.ofs -= i;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tbase = QCC_PR_GenerateFunctionCall1(nullsref, funcretr, QCC_SupplyConversion(index, ev_float, true), type_float);\n\t\t\t}\n\t\t\tbase.cast = t;\n\t\t}\n\t}\n\treturn base;\n}\n\nstatic pbool QCC_RefNeedsCalls(QCC_ref_t *ref)\n{\n\tif (ref->type == REF_ACCESSOR)\n\t\treturn true;\n\tif (ref->type == REF_ARRAY)\n\t{\n\t\tif (ref->index.cast)\n\t\t{\n\t\t\tint accel;\n\t\t\tif (QCC_SRef_EvalConst(ref->index))\n\t\t\t\treturn false;\t//can short it.\n\n\t\t\tif (ref->index.cast->type != ev_float || ref->cast->type != ref->base.cast->type)\n\t\t\t\taccel = 2;\n\t\t\telse\n\t\t\t\taccel = 1;\n\t\t\tif (accel == 2 && !QCC_OPCodeValid(&pr_opcodes[OP_LOADA_F]))\n\t\t\t\taccel = QCC_OPCodeValid(&pr_opcodes[OP_GLOAD_F])&&!ref->base.sym->temp?3:1;\n\t\t\tif (accel == 1 && (!ref->base.sym->arraylengthprefix || !QCC_OPCodeValid(&pr_opcodes[OP_FETCH_GBL_F])))\n\t\t\t\taccel = QCC_OPCodeValid(&pr_opcodes[OP_LOADA_F])?2:0;\n\n\t\t\treturn !accel;\t//if we've no acceleration, we need a call.\n\t\t}\n\t}\n\treturn false;\n}\n\nQCC_sref_t QCC_BitfieldToDef(QCC_sref_t field, unsigned int bitofs)\n{\n//Note: this assumes the bitfield does not cross its basetype boundary.\n\tQCC_type_t *basetype = field.cast->parentclass;\n\tunsigned int bits = field.cast->bits;\n\tQCC_sref_t mask = QCC_MakeUIntConst((bitofs<<8)|bits);\n\tmask.sym->referenced = true;\n\t//shift it by the base.\n\tif (basetype->type == ev_integer)\n\t\tfield = QCC_PR_StatementFlags(&pr_opcodes[OP_BITEXTEND_I], field, mask, NULL, 0);\n\telse if (basetype->type == ev_uint)\n\t\tfield = QCC_PR_StatementFlags(&pr_opcodes[OP_BITEXTEND_U], field, mask, NULL, 0);\n\telse\n\t\tQCC_PR_ParseErrorPrintSRef(ERR_INTERNAL, field, \"unsupported bitfield type\");\n\tfield.cast = basetype;\n\treturn field;\n}\n\n//reads a ref as required\n//the result sref should ALWAYS be freed, even if freetemps is set.\nQCC_sref_t QCC_RefToDef(QCC_ref_t *ref, pbool freetemps)\n{\n\tQCC_sref_t tmp = nullsref, idx;\n\tQCC_sref_t ret = ref->base;\n\tunsigned int bitofs = ref->bitofs;\n\n\tif (ref->postinc)\n\t{\n\t\tint inc = ref->postinc;\n\t\tif (ref->bitofs)\n\t\t\tQCC_PR_ParseErrorPrintSRef(ERR_INTERNAL, ret, \"post increment operator on bitfield\");\n\t\tref->postinc = 0;\n\t\t//read the value, without preventing the store later\n\t\tret = QCC_RefToDef(ref, false);\n\t\t//archive off the old value\n\t\ttmp = QCC_GetTemp(ret.cast);\n\t\tQCC_StoreToSRef(tmp, ret, ret.cast, false, true);\n\t\tret = tmp;\n\t\t//update the value\n\t\tswitch(ref->cast->type)\n\t\t{\n\t\tcase ev_float:\n\t\t\tQCC_StoreSRefToRef(ref, QCC_PR_StatementFlags(&pr_opcodes[OP_ADD_F], ret, QCC_MakeFloatConst(inc), NULL, STFL_PRESERVEA), false, !freetemps);\n\t\t\tbreak;\n\t\tcase ev_double:\n\t\t\tQCC_StoreSRefToRef(ref, QCC_PR_StatementFlags(&pr_opcodes[OP_ADD_D], ret, QCC_MakeDoubleConst(inc), NULL, STFL_PRESERVEA), false, !freetemps);\n\t\t\tbreak;\n\t\tcase ev_string:\n\t\tcase ev_integer:\n\t\t\tQCC_StoreSRefToRef(ref, QCC_PR_StatementFlags(&pr_opcodes[OP_ADD_I], ret, QCC_MakeIntConst(inc), NULL, STFL_PRESERVEA), false, !freetemps);\n\t\t\tbreak;\n\t\tcase ev_uint:\n\t\t\tQCC_StoreSRefToRef(ref, QCC_PR_StatementFlags(&pr_opcodes[OP_ADD_U], ret, QCC_MakeIntConst(inc), NULL, STFL_PRESERVEA), false, !freetemps);\n\t\t\tbreak;\n\t\tcase ev_int64:\n\t\t\tQCC_StoreSRefToRef(ref, QCC_PR_StatementFlags(&pr_opcodes[OP_ADD_I64], ret, QCC_MakeInt64Const(inc), NULL, STFL_PRESERVEA), false, !freetemps);\n\t\t\tbreak;\n\t\tcase ev_uint64:\n\t\t\tQCC_StoreSRefToRef(ref, QCC_PR_StatementFlags(&pr_opcodes[OP_ADD_U64], ret, QCC_MakeInt64Const(inc), NULL, STFL_PRESERVEA), false, !freetemps);\n\t\t\tbreak;\n\t\tcase ev_pointer:\n\t\t\tif (ref->cast->aux_type->bits)\n\t\t\t{\n\t\t\t\tif (flag_undefwordsize)\n\t\t\t\t\tQCC_PR_ParseErrorPrintSRef(ERR_INTERNAL, ret, \"not allowed to make assumptions about pointer sizes. sorry.\");\n\t\t\t\tinc *= ref->cast->aux_type->bits/8;\n\t\t\t\ttmp = QCC_PR_StatementFlags(&pr_opcodes[OP_ADD_I], ret, QCC_MakeIntConst(inc), NULL, STFL_PRESERVEA);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tinc *= ref->cast->aux_type->size;\n\t\t\t\ttmp = QCC_PR_StatementFlags(&pr_opcodes[OP_ADD_PIW], ret, QCC_MakeIntConst(inc), NULL, STFL_PRESERVEA);\n\t\t\t}\n\t\t\ttmp.cast = ref->cast;\t//make sure its the right pointer type. QCC_StoreSRefToRef is picky.\n\t\t\tQCC_StoreSRefToRef(ref, tmp, false, !freetemps);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tQCC_PR_ParseErrorPrintSRef(ERR_INTERNAL, ret, \"post increment operator not supported with this type\");\n\t\t\tbreak;\n\t\t}\n\t\t//hack any following uses of the ref to refer to the temp\n\t\tref->type = REF_GLOBAL;\n\t\tref->base = ret;\n\t\tref->index = nullsref;\n\t\tref->readonly = true;\n\n\t\treturn ret;\n\t}\n\n\tswitch(ref->type)\n\t{\n\tcase REF_THISCALL:\n\tcase REF_NONVIRTUAL:\n\t\tif (freetemps)\n\t\t\tQCC_FreeTemp(ref->index);\n\t\telse\n\t\t\tQCC_UnFreeTemp(ret);\n\t\tbreak;\n\tcase REF_POINTERARRAY:\n\tcase REF_ARRAYHEAD:\n\t\t{\n\t\t\tQCC_ref_t buf;\n\t\t\tref = QCC_PR_GenerateAddressOf(&buf, ref);\n\t\t\treturn QCC_RefToDef(ref, freetemps);\n\t\t}\n\t\tbreak;\n\tcase REF_GLOBAL:\n\tcase REF_ARRAY:\n\t\tif (!ret.sym->type->size)\n\t\t\tQCC_PR_ParseErrorPrintSRef(ERR_BADARRAYSIZE, ret, \"symbol definition is incomplete\");\n\t\tif (ref->index.cast)\n\t\t{\n\t\t\t//FIXME: array reads of array[immediate+offset] should generate (array+immediate)[offset] instead.\n\t\t\t//FIXME: this needs to be deprecated (and moved elsewhere)\n\t\t\tconst QCC_eval_t *idxeval = QCC_SRef_EvalConst(ref->index);\n\t\t\tif (idxeval)\n\t\t\t{\n\t\t\t\tref->base.sym->referenced = true;\n\t\t\t\tif (ref->index.sym)\n\t\t\t\t\tref->index.sym->referenced = true;\n\t\t\t\tif (ref->cast->align)\n\t\t\t\t{\n\t\t\t\t\tbitofs += QCC_Eval_Int(idxeval, ref->index.cast)*ref->cast->align;\n\t\t\t\t\tret.ofs += bitofs>>5;\n\t\t\t\t\tbitofs&=31;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tret.ofs += QCC_Eval_Int(idxeval, ref->index.cast);\n\n\t\t\t\tif (freetemps)\n\t\t\t\t\tQCC_FreeTemp(ref->index);\n\t\t\t\telse\n\t\t\t\t\tQCC_UnFreeTemp(ret);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (ref->cast->align!=32)\t///bits must be a multiple of 8 here.\n\t\t\t\t{\n\t\t\t\t\tQCC_ref_t buf;\n\t\t\t\t\tQCC_UnFreeTemp(ret);\n\t\t\t\t\ttmp = QCC_RefToDef(QCC_PR_GenerateAddressOf(&buf, ref), freetemps);\n\t\t\t\t\tret = QCC_LoadFromPointer(tmp, nullsref, ref->cast);\n\t\t\t\t\tif (ref->cast->type == ev_bitfld)\n\t\t\t\t\t\tret.cast = ref->cast->parentclass;\n\t\t\t\t\telse\n\t\t\t\t\t\tret.cast = ref->cast;\n\t\t\t\t\treturn ret;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tret = QCC_LoadFromArray(ref->base, ref->index, ref->cast, !freetemps);\n\t\t\t}\n\t\t}\n\t\telse if (freetemps)\n\t\t\tQCC_FreeTemp(ref->index);\n\t\telse\n\t\t\tQCC_UnFreeTemp(ret);\n\t\tbreak;\n\tcase REF_POINTER:\n\t\tif (!ret.sym->symbolheader->symbolsize)\n\t\t\tQCC_PR_ParseErrorPrintSRef(ERR_BADARRAYSIZE, ret, \"symbol definition is incomplete\");\n\n\t\tif (ref->index.cast)\n\t\t{\n//\t\t\tif (!freetemps)\n\t\t\t\tQCC_UnFreeTemp(ref->index);\n\t\t\tidx = QCC_SupplyConversion(ref->index, ev_integer, true);\n\t\t}\n\t\telse\n\t\t\tidx = nullsref;\n\t\tif (ref->cast->type == ev_bitfld && ref->bitofs)\n\t\t\tidx = QCC_PR_Statement(&pr_opcodes[OP_ADD_I], idx, QCC_MakeIntConst(ref->bitofs / ref->cast->bits), NULL);\n\t\ttmp = QCC_LoadFromPointer(ref->base, idx, ref->cast);\n\t\tQCC_FreeTemp(idx);\n\t\tif (freetemps)\n\t\t\tQCC_PR_DiscardRef(ref);\n\t\treturn tmp;\n\tcase REF_FIELD:\n\t\treturn QCC_PR_ExpandField(ref->base, ref->index, ref->cast, freetemps?0:(STFL_PRESERVEA|STFL_PRESERVEB));\n\tcase REF_STRING:\n\t\tif (ref->cast->type == ev_float)\n\t\t{\n\t\t\tidx = ref->index.cast?QCC_SupplyConversion(ref->index, ev_float, true):nullsref;\n\t\t\treturn QCC_PR_StatementFlags(&pr_opcodes[OP_LOADP_C], ref->base, idx, NULL, freetemps?0:(STFL_PRESERVEA|STFL_PRESERVEB));\n\t\t}\n\t\telse\n\t\t{\n\t\t\tidx = ref->index.cast?QCC_SupplyConversion(ref->index, ev_integer, true):nullsref;\n\t\t\treturn QCC_PR_StatementFlags(&pr_opcodes[OP_LOADP_U8], ref->base, idx, NULL, freetemps?0:(STFL_PRESERVEA|STFL_PRESERVEB));\n\t\t}\n\tcase REF_ACCESSOR:\n\t\tif (ref->accessor && ref->accessor->getset_func[0].cast)\n\t\t{\n\t\t\tint args = 0;\n\t\t\tQCC_sref_t arg[2];\n\t\t\tif (ref->accessor->getset_isref[0] == 1)\n\t\t\t{\n\t\t\t\tif (ref->base.sym->temp)\n\t\t\t\t\tQCC_PR_ParseErrorPrintSRef(ERR_NOFUNC, ref->base, \"Accessor %s(get) cannot be used on a temporary accessor reference\", ref->accessor?ref->accessor->fieldname:\"\");\n\t\t\t\t//there shouldn't really be any need for this, but its problematic if the accessor is a field.\n\t\t\t\targ[args++] = QCC_PR_Statement(&pr_opcodes[OP_GLOBALADDRESS], ref->base, nullsref, NULL);\n\t\t\t}\n\t\t\telse\n\t\t\t\targ[args++] = ref->base;\n\t\t\tif (ref->accessor->indexertype)\n\t\t\t\targ[args++] = ref->index.cast?QCC_SupplyConversion(ref->index, ref->accessor->indexertype->type, true):QCC_MakeIntConst(0);\n\t\t\tQCC_ForceUnFreeDef(ref->accessor->getset_func[0].sym);\n\t\t\treturn QCC_PR_GenerateFunctionCallSref(nullsref, ref->accessor->getset_func[0], arg, args);\n\t\t}\n\t\telse if (ref->accessor && ref->accessor->staticval.cast)\n\t\t{\n\t\t\tQCC_ForceUnFreeDef(ref->accessor->staticval.sym);\n\t\t\treturn ref->accessor->staticval;\n\t\t}\n\t\telse\n\t\t\tQCC_PR_ParseErrorPrintSRef(ERR_NOFUNC, ref->base, \"Accessor %s has no get function\", ref->accessor?ref->accessor->fieldname:\"\");\n\t\tbreak;\n\t}\n\tret.cast = ref->cast;\n\n\tif (ret.cast->type == ev_bitfld)\n\t\tret = QCC_BitfieldToDef(ret, bitofs);\n\treturn ret;\n}\n\n//return value is the 'source', unless we folded the store and stripped a temp, in which case it'll be the new value at the given location, either way should have the same value as source.\nQCC_sref_t QCC_StoreSRefToRef(QCC_ref_t *dest, QCC_sref_t source, pbool readable, pbool preservedest)\n{\n\tconst QCC_eval_t *eval;\n\tQCC_ref_t ptrref;\n\tpbool nullsrc = QCC_SRef_IsNull(source);\n\tif (dest->readonly)\n\t{\n\t\tQCC_PR_ParseWarning(WARN_ASSIGNMENTTOCONSTANT, \"Assignment to constant %s\", QCC_GetSRefName(dest->base));\n\t\tQCC_PR_ParsePrintSRef(WARN_ASSIGNMENTTOCONSTANT, dest->base);\n\t\tif (dest->index.cast)\n\t\t\tQCC_PR_ParsePrintSRef(WARN_ASSIGNMENTTOCONSTANT, dest->index);\n\t}\n\n\tif (!nullsrc)\n\t/*{\n\t\tif (dest->cast->type == ev_struct || dest->cast->type == ev_union)\n\t\t{\n\t\t\tQCC_FreeTemp(source);\n\t\t\tsource = QCC_MakeIntConst(0);\n\t\t}\n\t\telse if (dest->cast->type == ev_vector)\n\t\t{\n\t\t\tQCC_FreeTemp(source);\n\t\t\tsource = QCC_MakeVectorConst(0, 0, 0);\n\t\t}\n\t\tsource.cast = dest->cast;\n\t}\n\telse*/\n\t{\n\t\tQCC_type_t *t = source.cast;\n\t\twhile(t)\n\t\t{\n\t\t\tif (!typecmp_lax(t, dest->cast))\n\t\t\t\tbreak;\n\t\t\tt = t->parentclass;\n\t\t}\n\t\tif (!t && !(source.cast->type == ev_pointer && dest->cast->type == ev_pointer && (source.cast->aux_type->type == ev_void || source.cast->aux_type->type == ev_variant)) && source.cast->type != ev_variant && dest->cast->type != ev_variant)\n\t\t{\t//extra check to allow void*->any*\n\t\t\tchar typea[256];\n\t\t\tchar typeb[256];\n\t\t\tif (source.cast->type == ev_variant || dest->cast->type == ev_variant)\n\t\t\t\tQCC_PR_ParseWarning(WARN_IMPLICITVARIANTCAST, \"type mismatch: %s %s to %s %s.%s\", typea, QCC_GetSRefName(source), typeb, QCC_GetSRefName(dest->base), QCC_GetSRefName(dest->index));\n\t\t\telse if (dest->cast->type == ev_bitfld && dest->cast->parentclass == source.cast)\n\t\t\t\t;\t//dest is a bitfield of the same source type. silent truncation is fine.\n\t\t\telse if ((dest->cast->type == ev_float ||\n\t\t\t\t\t dest->cast->type == ev_integer ||\n\t\t\t\t\t dest->cast->type == ev_uint ||\n\t\t\t\t\t dest->cast->type == ev_int64 ||\n\t\t\t\t\t dest->cast->type == ev_uint64 ||\n\t\t\t\t\t dest->cast->type == ev_double ||\n\t\t\t\t\t dest->cast->type == ev_boolean) && (\n\t\t\t\t\t source.cast->type == ev_float ||\n\t\t\t\t\t source.cast->type == ev_integer ||\n\t\t\t\t\t source.cast->type == ev_uint ||\n\t\t\t\t\t source.cast->type == ev_int64 ||\n\t\t\t\t\t source.cast->type == ev_uint64 ||\n\t\t\t\t\t source.cast->type == ev_double ||\n\t\t\t\t\t source.cast->type == ev_boolean))\n\t\t\t{\n\t\t\t\tif (dest->cast->type == ev_boolean)\n\t\t\t\t\tsource = QCC_SupplyConversion(QCC_PR_GenerateLogicalTruth(source, \"cannot convert to boolean\"), dest->cast->parentclass->type, true);\n\t\t\t\telse\n\t\t\t\t\tsource = QCC_SupplyConversion(source, dest->cast->type, true);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tTypeName(source.cast, typea, sizeof(typea));\n\t\t\t\tTypeName(dest->cast, typeb, sizeof(typeb));\n\t\t\t\tif (dest->type == REF_FIELD)\n\t\t\t\t{\n\t\t\t\t\tQCC_PR_ParseWarning(WARN_STRICTTYPEMISMATCH, \"type mismatch: %s %s to %s %s.%s\", typea, QCC_GetSRefName(source), typeb, QCC_GetSRefName(dest->base), QCC_GetSRefName(dest->index));\n\t\t\t\t\tQCC_PR_ParsePrintDef(WARN_STRICTTYPEMISMATCH, source.sym);\n\t\t\t\t}\n\t\t\t\telse if (dest->index.cast && strcmp(\"IMMEDIATE\", dest->index.sym->name))\n\t\t\t\t\tQCC_PR_ParseWarning(WARN_STRICTTYPEMISMATCH, \"type mismatch: %s %s to %s %s[%s]\", typea, QCC_GetSRefName(source), typeb, QCC_GetSRefName(dest->base), QCC_GetSRefName(dest->index));\n\t\t\t\telse\n\t\t\t\t\tQCC_PR_ParseWarning(WARN_STRICTTYPEMISMATCH, \"type mismatch: %s %s to %s %s\", typea, QCC_GetSRefName(source), typeb, QCC_GetSRefName(dest->base));\n\t\t\t}\n\t\t}\n\t}\n\n\tif (dest->cast->type == ev_bitfld && dest->type != REF_POINTER && QCC_SRef_EvalConst(dest->index))\n\t{\n\t\tunsigned int bits = dest->cast->bits;\n\t\tunsigned int bitofs = dest->bitofs;\n\t\tQCC_sref_t old;\n\t\tif (bits == 32 && !bitofs)\n\t\t\t;\t//o.O no packing needed.\n\t\telse\n\t\t{\n\t\t\tQCC_type_t *bittype = dest->cast;\n\t\t\tdest->bitofs = 0;\n\t\t\tif (dest->cast->align != 32)\n\t\t\t{\n\t\t\t\tint idx = QCC_Eval_Int(QCC_SRef_EvalConst(dest->index), dest->index.cast);\n\t\t\t\tif (idx)\n\t\t\t\t{\n\t\t\t\t\tidx *= dest->cast->align;\n\t\t\t\t\tbitofs += idx&31;\n\t\t\t\t\tidx /= 32;\n\t\t\t\t\tdest->index = QCC_MakeUIntConst(idx);\n\t\t\t\t}\n\t\t\t}\n\t\t\tdest->cast = dest->cast->parentclass;\n\t\t\told = QCC_RefToDef(dest, false);\n\t\t\tdest->bitofs = bitofs;\n\t\t\tdest->cast = bittype;\n\n\t\t\tsource = QCC_PR_Statement_BitCopy(source, bitofs, bits, old);\n\t\t}\n\n//\t\tif (readable)\n//\t\t\tQCC_PR_ParseWarning(ERR_PARSEERRORS, \"left operand (%s) is a bitfield and not readable! oh noes!\", QCC_GetSRefName(dest->base));\n\t}\n\telse if (dest->bitofs)\n\t\tQCC_PR_ParseWarning(ERR_INTERNAL, \"left operand (%s) has a bit offset\", QCC_GetSRefName(dest->base));\n\n\tfor(;;)\n\t{\n\t\tswitch(dest->type)\n\t\t{\n\t\tcase REF_ARRAYHEAD:\n\t\tcase REF_POINTERARRAY:\n\t\t\tQCC_PR_ParseWarning(ERR_PARSEERRORS, \"left operand must be an l-value (did you mean %s[0]?)\", QCC_GetSRefName(dest->base));\n\t\t\tif (!preservedest)\n\t\t\t\tQCC_PR_DiscardRef(dest);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tQCC_PR_ParseWarning(ERR_PARSEERRORS, \"left operand must be an l-value (unsupported reference type)\");\n\t\t\tif (!preservedest)\n\t\t\t\tQCC_PR_DiscardRef(dest);\n\t\t\tbreak;\n\t\tcase REF_GLOBAL:\n\t\tcase REF_ARRAY:\n\t\t\tif (!dest->index.cast || QCC_SRef_EvalConst(dest->index))\n\t\t\t{\n\t\t\t\tQCC_sref_t dd;\n\t//\t\t\tQCC_PR_ParseWarning(0, \"FIXME: trying to do references: assignments to arrays with const offset not supported.\\n\");\n\t\tcase REF_NONVIRTUAL:\n\t\t\t\tdd.cast = dest->cast;\n\t\t\t\tdd.ofs = dest->base.ofs;\n\t\t\t\tdd.sym = dest->base.sym;\n\n\t\t\t\tif ((eval=QCC_SRef_EvalConst(dest->index)))\n\t\t\t\t{\n\t\t\t\t\tif (!preservedest)\n\t\t\t\t\t\tQCC_FreeTemp(dest->index);\n\t\t\t\t\tdd.ofs += QCC_Eval_Int(eval, dest->index.cast);\n\t\t\t\t}\n\n\t\t\t\t//FIXME: can dest even be a temp?\n//\t\t\t\tif (readable)\n//\t\t\t\t\tQCC_UnFreeTemp(source);\n\t\t\t\tsource = QCC_CollapseStore(dd, source, dest->cast, readable, preservedest);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (readable)\n\t\t\t\t\tQCC_UnFreeTemp(source);\n\t\t\t\tQCC_StoreToArray(dest->base, dest->index, source, dest->cast);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase REF_POINTER:\n\t\t\tsource.sym->referenced = true;\n\t\t\tQCC_StoreToPointer(dest->base, dest->index.cast?QCC_SupplyConversion(dest->index, ev_integer, true):nullsref, source, dest->cast);\n\t\t\tif (dest->base.sym)\n\t\t\t\tdest->base.sym->referenced = true;\n\t\t\tif (!preservedest)\n\t\t\t\tQCC_FreeTemp(dest->base);\n\t\t\tif (!readable)\n\t\t\t{\n\t\t\t\tQCC_FreeTemp(source);\n\t\t\t\tsource = nullsref;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase REF_STRING:\n\t\t\t{\n\t\t\t\tQCC_sref_t addr = dest->base;\n\t\t\t\tQCC_sref_t idx = dest->index;\n\t\t\t\tint op;\n\t\t\t\tif (source.cast->type==ev_float && QCC_OPCodeValid(&pr_opcodes[OP_STOREP_C]))\n\t\t\t\t\top = OP_STOREP_C;\n\t\t\t\telse if (QCC_OPCodeValid(&pr_opcodes[OP_STOREP_I8]))\n\t\t\t\t\top = OP_STOREP_I8;\n\t\t\t\telse if (QCC_OPCodeValid(&pr_opcodes[OP_STOREP_C]))\n\t\t\t\t\top = OP_STOREP_C;\n\t\t\t\telse\n\t\t\t\t\top = OP_STOREP_I8;\n\n\t\t\t\tif (op == OP_STOREP_C)\n\t\t\t\t\tsource = QCC_SupplyConversion(source, ev_float, true);\n\t\t\t\telse\n\t\t\t\t\tsource = QCC_SupplyConversion(source, ev_integer, true);\n\n\t\t\t\tif (idx.cast)\n\t\t\t\t\tidx = QCC_SupplyConversion(idx, ev_integer, true);\n\t\t\t\tif (idx.sym && !QCC_OPCode_StorePOffset())\n\t\t\t\t{\t//can't do an offset store yet... bake any index into the pointer.\n\t\t\t\t\tif (idx.cast)\n\t\t\t\t\t\taddr = QCC_PR_Statement(&pr_opcodes[OP_ADD_I], addr, idx, NULL);\n\t\t\t\t\tidx = nullsref;\n\t\t\t\t}\n\t\t\t\tQCC_PR_Statement_SmallStore(op, source, addr, idx);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase REF_ACCESSOR:\n\t\t\tif (dest->accessor && dest->accessor->getset_func[1].cast)\n\t\t\t{\n\t\t\t\tint args = 0;\n\t\t\t\tQCC_sref_t arg[3];\n\t\t\t\tif (dest->accessor->getset_isref[1] == 1)\n\t\t\t\t{\n\t\t\t\t\tif (dest->base.sym->temp)\n\t\t\t\t\t\tQCC_PR_ParseErrorPrintDef(ERR_NOFUNC, dest->base.sym, \"Accessor %s(set) cannot be used on a temporary accessor reference\", dest->accessor?dest->accessor->fieldname:\"\");\n\t\t\t\t\t//there shouldn't really be any need for this, but its problematic if the accessor is a field.\n\t\t\t\t\targ[args++] = QCC_PR_Statement(&pr_opcodes[OP_GLOBALADDRESS], dest->base, nullsref, NULL);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\targ[args++] = dest->base;\n\t\t\t\tif (dest->accessor->indexertype)\n\t\t\t\t\targ[args++] = dest->index.cast?QCC_SupplyConversion(dest->index, dest->accessor->indexertype->type, true):QCC_MakeIntConst(0);\n\t\t\t\targ[args++] = source;\n\n\t\t\t\tif (readable)\t//if we're returning source, make sure it can't get freed\n\t\t\t\t\tQCC_UnFreeTemp(source);\n\t\t\t\tQCC_ForceUnFreeDef(dest->accessor->getset_func[1].sym);\n\t\t\t\tQCC_FreeTemp(QCC_PR_GenerateFunctionCallSref(nullsref, dest->accessor->getset_func[1], arg, args));\n\t\t\t}\n\t\t\telse\n\t\t\t\tQCC_PR_ParseErrorPrintSRef(ERR_NOFUNC, dest->base, \"Accessor has no set function\");\n\t\t\tbreak;\n\t\tcase REF_FIELD:\n\t\t\t{\n\t\t\t\tint storef_opcode;\n\t\t\t\t//fixme: we should do this earlier, to preserve original instruction ordering.\n\t\t\t\t//such that self.enemy = (self = world); still has the same result (more common with function calls)\n\n\t\t\t\tdest->base.sym->referenced = true;\n\t\t\t\tdest->index.sym->referenced = true;\n\t\t\t\tsource.sym->referenced = true;\n\n\t\t\t\tif (dest->cast->type == ev_float)\n\t\t\t\t\tstoref_opcode = OP_STOREF_F;\n\t\t\t\telse if (dest->cast->type == ev_vector)\n\t\t\t\t\tstoref_opcode = OP_STOREF_V;\n\t\t\t\telse if (dest->cast->type == ev_string)\n\t\t\t\t\tstoref_opcode = OP_STOREF_S;\n\t\t\t\telse if (\tdest->cast->type == ev_entity ||\n\t\t\t\t\t\t\tdest->cast->type == ev_field ||\n\t\t\t\t\t\t\tdest->cast->type == ev_function ||\n\t\t\t\t\t\t\tdest->cast->type == ev_pointer ||\n\t\t\t\t\t\t\tdest->cast->type == ev_integer ||\n\t\t\t\t\t\t\tdest->cast->type == ev_uint ||\n\t\t\t\t\t\t\tdest->cast->type == ev_struct ||\n\t\t\t\t\t\t\tdest->cast->type == ev_union ||\n\t\t\t\t\t\t\tdest->cast->type == ev_enum ||\n\t\t\t\t\t\t\tdest->cast->type == ev_bitfld ||\n\t\t\t\t\t\t\tdest->cast->type == ev_int64 ||\n\t\t\t\t\t\t\tdest->cast->type == ev_uint64 ||\n\t\t\t\t\t\t\tdest->cast->type == ev_double)\n\t\t\t\t\tstoref_opcode = OP_STOREF_I;\n\t\t\t\telse\n\t\t\t\t\tstoref_opcode = OP_DONE;\n\t\t\t\t//don't use it for arrays. address+storep_with_offset is less opcodes.\n\t\t\t\tif (storef_opcode!=OP_DONE && dest->index.cast->size == dest->cast->size && QCC_OPCodeValid(&pr_opcodes[storef_opcode]))\n\t\t\t\t{\n\t\t\t\t\t//doesn't generate any temps.\n\t\t\t\t\tint sz = dest->cast->size, i;\n\t\t\t\t\tif (nullsrc)\n\t\t\t\t\t{\n\t\t\t\t\t\tfor (i = 0; i+2 < sz; i+=3, dest->index.ofs += 3)\n\t\t\t\t\t\t\tQCC_PR_SimpleStatement(&pr_opcodes[OP_STOREF_V], dest->base, dest->index, QCC_MakeVectorConst(0,0,0), true);\n\t\t\t\t\t\tif (QCC_OPCodeValid(&pr_opcodes[OP_STOREF_I64]))\n\t\t\t\t\t\t\tfor (; i+1 < sz; i+=2, dest->index.ofs += 2)\n\t\t\t\t\t\t\t\tQCC_PR_SimpleStatement(&pr_opcodes[OP_STOREF_V], dest->base, dest->index, QCC_MakeVectorConst(0,0,0), true);\n\t\t\t\t\t\tfor (; i < sz; i++, dest->index.ofs++)\n\t\t\t\t\t\t\tQCC_PR_SimpleStatement(&pr_opcodes[storef_opcode], dest->base, dest->index, QCC_MakeIntConst(0), true);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tfor (i = 0; i+2 < sz; i+=3, dest->index.ofs += 3, source.ofs += 3)\n\t\t\t\t\t\t\tQCC_PR_SimpleStatement(&pr_opcodes[OP_STOREF_V], dest->base, dest->index, source, true);\n\t\t\t\t\t\tif (QCC_OPCodeValid(&pr_opcodes[OP_STOREF_I64]))\n\t\t\t\t\t\t\tfor (; i+1 < sz; i+=2, dest->index.ofs += 2, source.ofs += 2)\n\t\t\t\t\t\t\t\tQCC_PR_SimpleStatement(&pr_opcodes[OP_STOREF_I64], dest->base, dest->index, source, true);\n\t\t\t\t\t\tfor (; i < sz; i++, dest->index.ofs++, source.ofs++)\n\t\t\t\t\t\t\tQCC_PR_SimpleStatement(&pr_opcodes[storef_opcode], dest->base, dest->index, source, true);\n\t\t\t\t\t\tsource.ofs -= i;\n\t\t\t\t\t}\n\t\t\t\t\tdest->index.ofs -= i;\n\t\t\t\t\tif (!readable)\n\t\t\t\t\t{\n\t\t\t\t\t\tQCC_FreeTemp(source);\n\t\t\t\t\t\tsource = nullsref;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (!preservedest)\n\t\t\t\t\t{\n\t\t\t\t\t\tQCC_FreeTemp(dest->base);\n\t\t\t\t\t\tQCC_FreeTemp(dest->index);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse if (1)//dest->cast->type >= ev_variant)\n\t\t\t\t{\n\t\t\t\t\tQCC_sref_t t;\n\t\t\t\t\tint sz = dest->cast->size, i;\n\t\t\t\t\tfor (i = 0; i+2 < sz; i+=3, dest->index.ofs += 3, source.ofs+=3)\n\t\t\t\t\t{\n\t\t\t\t\t\tt = QCC_PR_StatementFlags(&pr_opcodes[OP_ADDRESS], dest->base, dest->index, NULL, STFL_PRESERVEA|STFL_PRESERVEB);\n\t\t\t\t\t\tQCC_StoreToPointer(t, nullsref, nullsrc?QCC_MakeVectorConst(0,0,0):source, type_vector);\n\t\t\t\t\t\tQCC_FreeTemp(t);\n\t\t\t\t\t}\n\t\t\t\t\tif (QCC_OPCodeValid(&pr_opcodes[OP_STOREP_I64]))\n\t\t\t\t\tfor (; i+1 < sz; i+=2, dest->index.ofs += 2, source.ofs+=2)\n\t\t\t\t\t{\n\t\t\t\t\t\tt = QCC_PR_StatementFlags(&pr_opcodes[OP_ADDRESS], dest->base, dest->index, NULL, STFL_PRESERVEA|STFL_PRESERVEB);\n\t\t\t\t\t\tQCC_StoreToPointer(t, nullsref, nullsrc?QCC_MakeInt64Const(0):source, type_int64);\n\t\t\t\t\t\tQCC_FreeTemp(t);\n\t\t\t\t\t}\n\t\t\t\t\tfor (; i < sz; i++, dest->index.ofs++, source.ofs++)\n\t\t\t\t\t{\n\t\t\t\t\t\tt = QCC_PR_StatementFlags(&pr_opcodes[OP_ADDRESS], dest->base, dest->index, NULL, STFL_PRESERVEA|STFL_PRESERVEB);\n\t\t\t\t\t\tQCC_StoreToPointer(t, nullsref, nullsrc?QCC_MakeFloatConst(0):source, (dest->cast->size!=1)?type_float:dest->cast);\n\t\t\t\t\t\tQCC_FreeTemp(t);\n\t\t\t\t\t}\n\t\t\t\t\tsource.ofs -= i;\n\t\t\t\t\tdest->index.ofs -= i;\n\n\t\t\t\t\tif (!readable)\n\t\t\t\t\t{\n\t\t\t\t\t\tQCC_FreeTemp(source);\n\t\t\t\t\t\tsource = nullsref;\n\t\t\t\t\t}\n\t\t\t\t\tif (!preservedest)\n\t\t\t\t\t{\n\t\t\t\t\t\tQCC_FreeTemp(dest->base);\n\t\t\t\t\t\tQCC_FreeTemp(dest->index);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tdest = QCC_PR_BuildRef(&ptrref, REF_POINTER,\n\t\t\t\t\t\t\t\t\tQCC_PR_StatementFlags(&pr_opcodes[OP_ADDRESS], dest->base, dest->index, NULL, preservedest?STFL_PRESERVEA:0),\t//pointer address\n\t\t\t\t\t\t\t\t\tnullsref, (dest->index.cast->type == ev_field)?dest->index.cast->aux_type:type_variant, dest->readonly, 0);\n\t\t\t\t\tpreservedest = false;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t\tbreak;\n\t}\n\treturn source;\n}\n\n/*QCC_ref_t *QCC_PR_RefTerm (QCC_ref_t *ref, unsigned int exprflags)\n{\n\treturn QCC_DefToRef(ref, QCC_PR_Term(exprflags));\n}*/\nQCC_sref_t QCC_PR_Term (unsigned int exprflags)\n{\n\tQCC_ref_t refbuf;\n\treturn QCC_RefToDef(QCC_PR_RefTerm(&refbuf, exprflags), true);\n}\nQCC_sref_t\tQCC_PR_ParseValue (QCC_type_t *assumeclass, pbool allowarrayassign, pbool expandmemberfields, pbool makearraypointers)\n{\n\tQCC_ref_t refbuf;\n\treturn QCC_RefToDef(QCC_PR_ParseRefValue(&refbuf, assumeclass, allowarrayassign, expandmemberfields, makearraypointers), true);\n}\nQCC_sref_t QCC_PR_ParseArrayPointer (QCC_sref_t d, pbool allowarrayassign, pbool makestructpointers)\n{\n\tQCC_ref_t refbuf;\n\tQCC_ref_t inr;\n\tQCC_DefToRef(&inr, d);\n\treturn QCC_RefToDef(QCC_PR_ParseRefArrayPointer(&refbuf, &inr, allowarrayassign, makestructpointers), true);\n}\n\nvoid QCC_PR_DiscardRef(QCC_ref_t *ref)\n{\n\tif (ref->postinc)\n\t{\n\t\tQCC_sref_t oval;\n\t\tint inc = ref->postinc;\n\t\tif (ref->bitofs)\n\t\t\tQCC_PR_ParseErrorPrintSRef(ERR_INTERNAL, ref->base, \"post increment operator on bitfield\");\n\t\tref->postinc = 0;\n\t\t//read the value\n\t\toval = QCC_RefToDef(ref, false);\n\t\t//and update it\n\t\tswitch(oval.cast->type)\n\t\t{\n\t\tcase ev_float:\n\t\t\toval = QCC_PR_StatementFlags(&pr_opcodes[OP_ADD_F], oval, QCC_MakeFloatConst(inc), NULL, 0);\n\t\t\tbreak;\n\t\tcase ev_double:\n\t\t\toval = QCC_PR_StatementFlags(&pr_opcodes[OP_ADD_D], oval, QCC_MakeDoubleConst(inc), NULL, 0);\n\t\t\tbreak;\n\t\tcase ev_string:\n\t\tcase ev_integer:\n\t\t\toval = QCC_PR_StatementFlags(&pr_opcodes[OP_ADD_I], oval, QCC_MakeIntConst(inc), NULL, 0);\n\t\t\tbreak;\n\t\tcase ev_uint:\n\t\t\toval = QCC_PR_StatementFlags(&pr_opcodes[OP_ADD_U], oval, QCC_MakeIntConst(inc), NULL, 0);\n\t\t\tbreak;\n\t\tcase ev_int64:\n\t\t\toval = QCC_PR_StatementFlags(&pr_opcodes[OP_ADD_I64], oval, QCC_MakeInt64Const(inc), NULL, 0);\n\t\t\tbreak;\n\t\tcase ev_uint64:\n\t\t\toval = QCC_PR_StatementFlags(&pr_opcodes[OP_ADD_U64], oval, QCC_MakeInt64Const(inc), NULL, 0);\n\t\t\tbreak;\n\t\tcase ev_pointer:\n\t\t\tif (ref->cast->aux_type->bits)\n\t\t\t{\n\t\t\t\tinc *= ref->cast->aux_type->bits/8;\n\t\t\t\toval = QCC_PR_StatementFlags(&pr_opcodes[OP_ADD_I], oval, QCC_MakeIntConst(inc), NULL, 0);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tinc *= ref->cast->aux_type->size;\n\t\t\t\toval = QCC_PR_StatementFlags(&pr_opcodes[OP_ADD_PIW], oval, QCC_MakeIntConst(inc), NULL, 0);\n\t\t\t}\n\t\t\toval.cast = ref->cast;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tQCC_PR_ParseErrorPrintSRef(ERR_INTERNAL, oval, \"post increment operator not supported with this type\");\n\t\t\tbreak;\n\t\t}\n\t\tQCC_StoreSRefToRef(ref, oval, false, false);\n\t\tqcc_usefulstatement = true;\n\t}\n\telse\n\t{\n\t\tQCC_FreeTemp(ref->base);\n\t\tif (ref->index.cast)\n\t\t\tQCC_FreeTemp(ref->index);\n\t}\n}\n\nstatic QCC_opcode_t *QCC_PR_ChooseOpcode(QCC_sref_t lhs, QCC_sref_t rhs, QCC_opcode_t **priority)\n{\n\tQCC_opcode_t\t*op, *oldop;\n\n\tQCC_opcode_t *bestop;\n\tint numconversions, c;\n\n\tetype_t type_a;\n\tetype_t type_c;\n\n\top = oldop = *priority++;\n\n\t// type check\n\ttype_a = lhs.cast->type;\n//\ttype_b = rhs.cast->type;\n\n\tif (type_a == ev_enum)\n\t{\n\t\tif (lhs.cast == rhs.cast)\n\t\t{\n\t\t\tlhs.cast = lhs.cast->aux_type;\n\t\t\trhs.cast = rhs.cast->aux_type;\n//\t\t\ttype_a = lhs.cast->type;\n\t\t}\n\t}\n\n\tif (type_a == ev_boolean)\n\t{\n\t\tlhs.cast = lhs.cast->parentclass;\n\t\ttype_a = lhs.cast->type;\n\t}\n\tif (rhs.cast->type == ev_boolean)\n\t\trhs.cast = rhs.cast->parentclass;\n\n\tif (op->name[0] == '.')// field access gets type from field\n\t{\n\t\tif (rhs.cast->aux_type)\n\t\t\ttype_c = rhs.cast->aux_type->type;\n\t\telse\n\t\t\ttype_c = -1;\t// not a field\n\t}\n\telse\n\t\ttype_c = ev_void;\n\n\tbestop = NULL;\n\tnumconversions = 32767;\n\twhile (op)\n\t{\n\t\tif (!(type_c != ev_void && type_c != (*op->type_c)->type))\n\t\t{\n\t\t\tif (!STRCMP (op->name , oldop->name))\t//matches\n\t\t\t{\n\t\t\t\t//return values are never converted - what to?\n//\t\t\t\tif (type_c != ev_void && type_c != op->type_c->type->type)\n//\t\t\t\t{\n//\t\t\t\t\top++;\n//\t\t\t\t\tcontinue;\n//\t\t\t\t}\n\n\t\t\t\tif (op->associative!=ASSOC_LEFT)\n\t\t\t\t{//assignment\n#if 0\n\t\t\t\t\tif (op->type_a == &type_pointer)\t//ent var\n\t\t\t\t\t{\n\t\t\t\t\t\t/*FIXME: I don't like this code*/\n\t\t\t\t\t\tif (lhs->type->type != ev_pointer)\n\t\t\t\t\t\t\tc = -200;\t//don't cast to a pointer.\n\t\t\t\t\t\telse if ((*op->type_c)->type == ev_void && op->type_b == &type_pointer && rhs.cast->type == ev_pointer)\n\t\t\t\t\t\t\tc = 0;\t//generic pointer... fixme: is this safe? make sure both sides are equivelent\n\t\t\t\t\t\telse if (lhs->type->aux_type->type != (*op->type_b)->type)\t//if e isn't a pointer to a type_b\n\t\t\t\t\t\t\tc = -200;\t//don't let the conversion work\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tc = QCC_canConv(rhs, (*op->type_c)->type);\n\t\t\t\t\t}\n\t\t\t\t\telse\n#endif\n\t\t\t\t\t{\n\t\t\t\t\t\tc=QCC_canConv(rhs, (*op->type_b)->type);\n\t\t\t\t\t\tif (type_a != (*op->type_a)->type)\t//in this case, a is the final assigned value\n\t\t\t\t\t\t\tc = -300;\t//don't use this op, as we must not change var b's type\n\t\t\t\t\t\telse if ((*op->type_a)->type == ev_pointer && lhs.cast->aux_type->type != (*op->type_a)->aux_type->type)\n\t\t\t\t\t\t\tc = -300;\t//don't use this op if its a pointer to a different type\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tint l = QCC_canConv(lhs, (*op->type_a)->type);\n\t\t\t\t\tint r = QCC_canConv(rhs, (*op->type_b)->type);\n\t\t\t\t\tc = min(l,r);\n\t\t\t\t\tif (c >= 0)\n\t\t\t\t\t{\n\t\t\t\t\t\tc = max(l,r);\n\t\t\t\t\t\t/*QCC_PR_ParseWarning(WARN_IMPLICITCONVERSION, \"%s (%i): Possible conversion from %s to %s (%i), %s to %s (%i)\",\n\t\t\t\t\t\t\top->opname, c,\n\t\t\t\t\t\t\tlhs.cast->name, (*op->type_a)->name, l,\n\t\t\t\t\t\t\trhs.cast->name, (*op->type_b)->name, r);*/\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (c>=0 && c < numconversions)\n\t\t\t\t{\n\t\t\t\t\tbestop = op;\n\t\t\t\t\tnumconversions=c;\n\t\t\t\t\tif (c == 0)//can't get less conversions than 0...\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t\tbreak;\n\t\t}\n\t\top = *priority++;\n\t}\n\tif (bestop == NULL)\n\t{\n//\t\tif (oldop->priority == CONDITION_PRIORITY)\n//\t\t\top = oldop;\n//\t\telse\n\t\t{\n\t\t\tchar temp1[256];\n\t\t\tchar temp2[256];\n\t\t\top = oldop;\n\t\t\tQCC_PR_ParseWarning(flag_laxcasts?WARN_LAXCAST:ERR_TYPEMISMATCH, \"type mismatch for %s (%s%s%s and %s%s%s)\", oldop->name, col_type,TypeName(lhs.cast,temp1,sizeof(temp1)),col_none, col_type,TypeName(rhs.cast,temp2,sizeof(temp2)),col_none);\n\t\t\tQCC_PR_ParsePrintSRef(flag_laxcasts?WARN_LAXCAST:ERR_TYPEMISMATCH, lhs);\n\t\t\tQCC_PR_ParsePrintSRef(flag_laxcasts?WARN_LAXCAST:ERR_TYPEMISMATCH, rhs);\n\t\t}\n\t}\n\telse\n\t{\n\t\top = bestop;\n\t\t/*if (numconversions>3)\n\t\t{\n\t\t\tc=QCC_canConv(lhs, (*op->type_a)->type);\n\t\t\tif (c>3)\n\t\t\t\tQCC_PR_ParseWarning(WARN_IMPLICITCONVERSION, \"Implicit conversion from %s to %s\", lhs.cast->name, (*op->type_a)->name);\n\t\t\tc=QCC_canConv(rhs, (*op->type_b)->type);\n\t\t\tif (c>3)\n\t\t\t\tQCC_PR_ParseWarning(WARN_IMPLICITCONVERSION, \"Implicit conversion from %s to %s\", rhs.cast->name, (*op->type_b)->name);\n\t\t}*/\n\t}\n\treturn op;\n}\n\n//used to optimise logicops slightly.\nstatic pbool QCC_OpHasSideEffects(QCC_statement_t *st)\n{\n\t//function calls potentially always have side effects (and are expensive)\n\tif ((st->op >= OP_CALL0 && st->op <= OP_CALL8) || (st->op >= OP_CALL1H && st->op <= OP_CALL8H))\n\t\treturn true;\n\t//otherwise if we're assigning to some variable (that isn't a temp) then it has a side effect.\n\t//FIXME: this doesn't catch op_address+op_storep_*, but that should generally not happen as logicops is tied to a single statement.\n\tif (st->c.sym && !st->c.sym->temp && OpAssignsToC(st->op))\n\t\treturn true;\n\tif (st->b.sym && !st->b.sym->temp && OpAssignsToB(st->op))\n\t\treturn true;\n\tif (st->a.sym && !st->a.sym->temp && OpAssignsToA(st->op))\n\t\treturn true;\n\treturn false;\n}\n\nQCC_ref_t *QCC_PR_RefExpression (QCC_ref_t *retbuf, int priority, int exprflags)\n{\n\tQCC_ref_t rhsbuf;\n//\tQCC_dstatement32_t\t*st;\n\tQCC_opcode_t\t*op;\n\n\tint opnum;\n\n\tQCC_ref_t\t\t*lhsr, *rhsr;\n\tQCC_sref_t\t\tlhsd, rhsd;\n\n\tif (priority == 0)\n\t{\n\t\tlhsr = QCC_PR_RefTerm (retbuf, exprflags);\n\t\tif (!STRCMP(pr_token, \"++\"))\n\t\t{\n\t\t\tif (lhsr->readonly)\n\t\t\t\tQCC_PR_ParseError(ERR_PARSEERRORS, \"postincrement: lhs is readonly\");\n\t\t\tlhsr->postinc += 1;\n\t\t\tQCC_PR_Lex();\n\t\t}\n\t\telse if (!STRCMP(pr_token, \"--\"))\n\t\t{\n\t\t\tif (lhsr->readonly)\n\t\t\t\tQCC_PR_ParseError(ERR_PARSEERRORS, \"postdecrement: lhs is readonly\");\n\t\t\tlhsr->postinc += -1;\n\t\t\tQCC_PR_Lex();\n\t\t}\n\t\treturn lhsr;\n\t}\n\n\tlhsr = QCC_PR_RefExpression (retbuf, priority-1, exprflags);\n\n\twhile (1)\n\t{\n\t\tif (priority == FUNC_PRIORITY && QCC_PR_CheckToken (\"(\") )\n\t\t{\n\t\t\tqcc_usefulstatement=true;\n\t\t\tlhsd = QCC_PR_ParseFunctionCall (lhsr);\n\t\t\tlhsr = QCC_DefToRef(&rhsbuf, lhsd);\n\t\t\tlhsr = QCC_PR_ParseRefArrayPointer(retbuf, lhsr, true, true);\n\t\t\tif (lhsr == &rhsbuf)\n\t\t\t{\n\t\t\t\t*retbuf = rhsbuf;\n\t\t\t\tlhsr = retbuf;\n\t\t\t}\n\t\t}\n\t\tif (priority == TERNARY_PRIORITY && QCC_PR_CheckToken (\"?\"))\n\t\t{\n\t\t\t//if we have no int types, force all ints to floats here, just to ensure that we don't end up with non-constant ints that we then can't cope with.\n\n\t\t\tQCC_sref_t r;\n\t\t\tQCC_statement_t *fromj, *elsej, *truthstore;\n\t\t\tQCC_sref_t val = QCC_RefToDef(lhsr, true);\n\t\t\tconst QCC_eval_t *eval = QCC_SRef_EvalConst(val);\n\t\t\tpbool lvalisnull = false;\n\t\t\tif (pr_scope)\n\t\t\t\teval = NULL; //FIXME: we need the gotos to avoid sideeffects, which is annoying.\n\t\t\tif (QCC_PR_CheckToken(\":\"))\n\t\t\t{\n\t\t\t\tif (eval)\n\t\t\t\t{\n\t\t\t\t\tif (QCC_Eval_Truth(eval, val.cast, false))\n\t\t\t\t\t{\n\t\t\t\t\t\tQCC_FreeTemp(QCC_PR_Expression(TOP_PRIORITY, EXPR_DISALLOW_COMMA));\n\t\t\t\t\t\treturn QCC_DefToRef(retbuf, val);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tQCC_FreeTemp(val);\n\t\t\t\t\t\tval = QCC_PR_Expression(TOP_PRIORITY, EXPR_DISALLOW_COMMA);\n\t\t\t\t\t\treturn QCC_DefToRef(retbuf, val);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\teval = NULL;\n\t\t\t\t//r=a?:b -> if (a) r=a else r=b;\n\t\t\t\tfromj = QCC_Generate_OP_IFNOT(val, true);\n\t\t\t\tlvalisnull = QCC_SRef_IsNull(val);\n#if 1\n\t\t\t\t//hack: make local, not temp. this prevents assignment/temp folding...\n\t\t\t\tr = QCC_MakeSRefForce(QCC_PR_DummyDef(r.cast=val.cast, \"ternary\", pr_scope, 0, NULL, 0, false, GDF_STRIP), 0, val.cast);\n#else\n\t\t\t\tr = QCC_GetTemp(val.cast);\n#endif\n\t\t\t\tQCC_FreeTemp(QCC_PR_StatementFlags(&pr_opcodes[(r.cast->size>=3)?OP_STORE_V:OP_STORE_F], val, r, &truthstore, STFL_PRESERVEB));\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (eval)\n\t\t\t\t{\n\t\t\t\t\tif (eval->_int)\n\t\t\t\t\t{\n\t\t\t\t\t\tQCC_FreeTemp(val);\n\t\t\t\t\t\tval = QCC_PR_Expression(TOP_PRIORITY, 0);\n\t\t\t\t\t\tQCC_PR_Expect(\":\");\n\t\t\t\t\t\tQCC_FreeTemp(QCC_PR_Expression(TOP_PRIORITY, EXPR_DISALLOW_COMMA));\n\t\t\t\t\t\treturn QCC_DefToRef(retbuf, val);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tQCC_FreeTemp(val);\n\t\t\t\t\t\tQCC_FreeTemp(QCC_PR_Expression(TOP_PRIORITY, 0));\n\t\t\t\t\t\tQCC_PR_Expect(\":\");\n\t\t\t\t\t\tval = QCC_PR_Expression(TOP_PRIORITY, EXPR_DISALLOW_COMMA);\n\t\t\t\t\t\treturn QCC_DefToRef(retbuf, val);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tfromj = QCC_Generate_OP_IFNOT(val, false);\n\n\t\t\t\tval = QCC_PR_Expression(TOP_PRIORITY, 0);\n\t\t\t\tif (val.cast->type == ev_integer && !QCC_OPCodeValid(&pr_opcodes[OP_STORE_IF]))\n\t\t\t\t\tval = QCC_SupplyConversion(val, ev_float, true);\n\t\t\t\tlvalisnull = QCC_SRef_IsNull(val);\n#if 1\n\t\t\t\t//hack: make local, not temp. this prevents assignment/temp folding...\n\t\t\t\tr = QCC_MakeSRefForce(QCC_PR_DummyDef(r.cast=val.cast, \"ternary\", pr_scope, 0, NULL, 0, false, GDF_STRIP), 0, val.cast);\n#else\n\t\t\t\tr = QCC_GetTemp(val.cast);\n#endif\n\n\t\t\t\t//fixme: QCC_StoreToSRef if it were not for saving truthstore\n\t\t\t\tswitch(r.cast->size)\n\t\t\t\t{\n\t\t\t\tcase 3:\n\t\t\t\t\tQCC_FreeTemp(QCC_PR_StatementFlags(&pr_opcodes[OP_STORE_V], val, r, &truthstore, STFL_PRESERVEB));\n\t\t\t\t\tbreak;\n\t\t\t\tcase 2:\n\t\t\t\t\tQCC_FreeTemp(QCC_PR_StatementFlags(&pr_opcodes[OP_STORE_I64], val, r, &truthstore, STFL_PRESERVEB));\n\t\t\t\t\tbreak;\n\t\t\t\tcase 1:\n\t\t\t\t\tQCC_FreeTemp(QCC_PR_StatementFlags(&pr_opcodes[OP_STORE_F], val, r, &truthstore, STFL_PRESERVEB));\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tQCC_PR_ParseError(ERR_BADEXTENSION, \"oversized ternary result\");\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\t//r can be stomped upon until its reused anyway\n\n\t\t\t\tQCC_PR_Expect(\":\");\n\t\t\t}\n\t\t\tif (fromj)\n\t\t\t{\n\t\t\t\tQCC_PR_Statement(&pr_opcodes[OP_GOTO], nullsref, nullsref, &elsej);\n\t\t\t\tfromj->b.jumpofs = &statements[numstatements] - fromj;\n\t\t\t}\n\t\t\telse\n\t\t\t\telsej = NULL;\n\t\t\tval = QCC_PR_Expression(TOP_PRIORITY, EXPR_DISALLOW_COMMA);\n\t\t\tif (val.cast->type == ev_integer && !QCC_OPCodeValid(&pr_opcodes[OP_STORE_IF]))\n\t\t\t\tval = QCC_SupplyConversion(val, ev_float, true);\n\n\t\t\tif (r.cast->type == ev_integer && val.cast->type == ev_float)\n\t\t\t{\t//cond?5i:5.1  should be accepted. change the initial store_f to a store_if.\n\t\t\t\ttruthstore->op = OP_STORE_IF;\n\t\t\t\tr.cast = type_float;\n\t\t\t}\n\t\t\telse if (r.cast->type == ev_float && (val.cast->type == ev_integer||val.cast->type == ev_uint))\n\t\t\t{\n\t\t\t\t//cond?5.1:5i  should be accepted. change the just-parsed value to a float, as needed.\n\t\t\t\tval = QCC_SupplyConversion(val, ev_float, true);\n\t\t\t}\n\n\t\t\t//promote to unsigned if one isn't.\n\t\t\telse if (r.cast->type == ev_uint && val.cast->type == ev_integer)\n\t\t\t\tval.cast = type_uint;\n\t\t\telse if (r.cast->type == ev_integer && val.cast->type == ev_uint)\n\t\t\t\tr.cast = type_uint;\n\t\t\telse if (r.cast->type == ev_uint64 && val.cast->type == ev_int64)\n\t\t\t\tval.cast = type_uint64;\n\t\t\telse if (r.cast->type == ev_int64 && val.cast->type == ev_uint64)\n\t\t\t\tr.cast = type_uint64;\n\n\t\t\tif (typecmp(val.cast, r.cast) != 0)\n\t\t\t{\n\t\t\t\twhile (val.cast->type == ev_boolean)\n\t\t\t\t\tval.cast = val.cast->parentclass;\n\t\t\t\twhile (r.cast->type == ev_boolean)\n\t\t\t\t\tr.cast = r.cast->parentclass;\n\n\t\t\t\tif (typecmp(val.cast, r.cast) == 0)\n\t\t\t\t\t;\n\t\t\t\telse if (QCC_SRef_IsNull(val) && r.cast->size == val.cast->size)\n\t\t\t\t\tval.cast = r.cast;\t//null is null... unless its a vector...\n\t\t\t\telse if (lvalisnull && r.cast->size == val.cast->size)\n\t\t\t\t\tr.cast = val.cast;\t//null is null... unless its a vector...\n\t\t\t\telse if (typecmp_lax(val.cast, r.cast) != 0)\n\t\t\t\t{\n\t\t\t\t\tchar typebuf1[256];\n\t\t\t\t\tchar typebuf2[256];\n\t\t\t\t\tQCC_PR_ParseWarning(0, \"Type mismatch on ternary operator: %s vs %s\", TypeName(r.cast, typebuf1, sizeof(typebuf1)), TypeName(val.cast, typebuf2, sizeof(typebuf2)));\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t//if they're mixed int/float, cast to floats.\n\t\t\t\t\tQCC_PR_ParseError(0, \"Ternary operator with mismatching types\\n\");\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tQCC_StoreToSRef(r, val, val.cast, false, true);\n//\t\t\tQCC_FreeTemp(QCC_PR_StatementFlags(&pr_opcodes[(r.cast->size>=3)?OP_STORE_V:OP_STORE_F], val, r, NULL, STFL_PRESERVEB));\n\n\t\t\tif (elsej)\n\t\t\t\telsej->a.jumpofs = &statements[numstatements] - elsej;\n\t\t\treturn QCC_DefToRef(retbuf, r);\n\t\t}\n\n\t\topnum=0;\n\n\t\tif (pr_token_type == tt_immediate && *pr_token=='-')\n\t\t{\n\t\t\t//work around (4-3) being parsed as 4 -3 with no operator between\n\t\t\t//(don't get confused by \"-foo\" strings though\n\t\t\tif (pr_immediate_type->type == ev_float || pr_immediate_type->type == ev_double ||\n\t\t\t\tpr_immediate_type->type == ev_integer || pr_immediate_type->type == ev_uint ||\n\t\t\t\tpr_immediate_type->type == ev_int64 || pr_immediate_type->type == ev_uint64)\n\t\t\t{\n\t\t\t\tQCC_PR_IncludeChunk(pr_token, true, NULL);\n\t\t\t\tstrcpy(pr_token, \"+\");//two negatives would make a positive.\n\t\t\t\tpr_token_type = tt_punct;\n\t\t\t}\n\t\t}\n\n\t\tif (pr_token_type != tt_punct)\n\t\t{\n\t\t\tif (priority == TOP_PRIORITY)\n\t\t\t\tQCC_PR_ParseWarning(WARN_UNEXPECTEDPUNCT, \"Expected punctuation\");\n\t\t}\n\n\t\tif (priority == ASSIGN_PRIORITY)\n\t\t{\t//assignments\n\t\t\tQCC_opcode_t **ops = NULL, **ops_ptr;\n\t\t\tchar *opname = NULL;\n\t\t\tint i;\n\t\t\tif (QCC_PR_CheckToken (\"=\"))\n\t\t\t{\n\t\t\t\tops = opcodes_store;\n\t\t\t\tops_ptr = NULL;\n\t\t\t\topname = \"=\";\n\t\t\t}\n\t\t\telse if (QCC_PR_CheckToken (\"+=\"))\n\t\t\t{\n\t\t\t\tops = opcodes_addstore;\n\t\t\t\tops_ptr = opcodes_addstorep;\n\t\t\t\topname = \"+=\";\n\t\t\t}\n\t\t\telse if (QCC_PR_CheckToken (\"-=\"))\n\t\t\t{\n\t\t\t\tops = opcodes_substore;\n\t\t\t\tops_ptr = opcodes_substorep;\n\t\t\t\topname = \"-=\";\n\t\t\t}\n\t\t\telse if (QCC_PR_CheckToken (\"|=\"))\n\t\t\t{\n\t\t\t\tops = opcodes_orstore;\n\t\t\t\tops_ptr = opcodes_orstorep;\n\t\t\t\topname = \"|=\";\n\t\t\t}\n\t\t\telse if (QCC_PR_CheckToken (\"&=\"))\n\t\t\t{\n\t\t\t\tops = opcodes_andstore;\n\t\t\t\tops_ptr = NULL;\n\t\t\t\topname = \"&=\";\n\t\t\t}\n\t\t\telse if (QCC_PR_CheckToken (\"&~=\"))\n\t\t\t{\n\t\t\t\tops = opcodes_clearstore;\n\t\t\t\tops_ptr = opcodes_clearstorep;\n\t\t\t\topname = \"&~=\";\n\t\t\t}\n\t\t\telse if (QCC_PR_CheckToken (\"^=\"))\n\t\t\t{\n\t\t\t\tops = opcodes_xorstore;\n\t\t\t\tops_ptr = NULL;\n\t\t\t\topname = \"^=\";\n\t\t\t}\n\t\t\telse if (QCC_PR_CheckToken (\"*=\"))\n\t\t\t{\n\t\t\t\tops = opcodes_mulstore;\n\t\t\t\tops_ptr = opcodes_mulstorep;\n\t\t\t\topname = \"*=\";\n\t\t\t}\n\t\t\telse if (QCC_PR_CheckToken (\"<<=\"))\n\t\t\t{\n\t\t\t\tops = opcodes_shlstore;\n\t\t\t\tops_ptr = opcodes_none;\n\t\t\t\topname = \"<<=\";\n\t\t\t}\n\t\t\telse if (QCC_PR_CheckToken (\">>=\"))\n\t\t\t{\n\t\t\t\tops = opcodes_shrstore;\n\t\t\t\tops_ptr = opcodes_none;\n\t\t\t\topname = \">>=\";\n\t\t\t}\n\t\t\telse if (QCC_PR_CheckToken (\"/=\"))\n\t\t\t{\n\t\t\t\tops = opcodes_divstore;\n\t\t\t\tops_ptr = opcodes_divstorep;\n\t\t\t\topname = \"/=\";\n\t\t\t}\n\t\t\telse if (QCC_PR_CheckToken (\"<=>\"))\n\t\t\t{\n\t\t\t\tops = opcodes_spaceship;\n\t\t\t\tops_ptr = opcodes_none;\n\t\t\t\topname = \"<=>\";\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tops = NULL;\n\t\t\t\tops_ptr = NULL;\n\t\t\t\topname = NULL;\n\t\t\t}\n\n\t\t\tif (ops)\n\t\t\t{\n\t\t\t\tif (lhsr->postinc)\n\t\t\t\t\tQCC_PR_ParseError(ERR_INTERNAL, \"Assignment to post-inc result\");\n\t\t\t\tif (lhsr->readonly)\n\t\t\t\t{\n\t\t\t\t\tQCC_PR_ParseWarning(WARN_ASSIGNMENTTOCONSTANT, \"Assignment to const\");\n\t\t\t\t\tQCC_PR_ParsePrintSRef(WARN_ASSIGNMENTTOCONSTANT, lhsr->base);\n\t\t\t\t\tif (lhsr->index.cast)\n\t\t\t\t\t\tQCC_PR_ParsePrintSRef(WARN_ASSIGNMENTTOCONSTANT, lhsr->index);\n\t\t\t\t}\n\n\t\t\t\trhsr = QCC_PR_RefExpression (&rhsbuf, priority, exprflags | EXPR_DISALLOW_ARRAYASSIGN|EXPR_DISALLOW_COMMA);\n\n\t\t\t\tif ((lhsr->type == REF_POINTERARRAY || lhsr->type == REF_ARRAYHEAD) && lhsr->arraysize != 0 &&\n\t\t\t\t\t(rhsr->cast->type == ev_union && rhsr->cast->num_parms == 1 && !rhsr->cast->params[0].paramname) &&\n\t\t\t\t\trhsr->cast->params[0].arraysize == lhsr->arraysize)\n\t\t\t\t{\t//this is some sort of assignment, but these types are logically const pointers that cannot be assigned to themselves.\n\t\t\t\t\t//change the lhsr to a REF_POINTER instead\n\t\t\t\t\tif (lhsr->type == REF_POINTERARRAY)\n\t\t\t\t\t\tlhsr->type = REF_POINTER;\n\t\t\t\t\telse\n\t\t\t\t\t\tlhsr->type = REF_ARRAY;\n\t\t\t\t\tlhsr->cast = rhsr->cast;\n\t\t\t\t\tlhsr->arraysize = 0;\t//just in case.\n\t\t\t\t}\n\n\t\t\t\tif (conditional&1)\n\t\t\t\t\tQCC_PR_ParseWarning(WARN_ASSIGNMENTINCONDITIONAL, \"suggest parenthesis for assignment used as truth value\");\n\n\t\t\t\t//FIXME: if this is a simple store, rhsr->type == REF_FIELD, and lhsd is a simple def, then we should just expand the field directly to the lhsd for more effient .structs\n\t\t\t\trhsd = QCC_RefToDef(rhsr, true);\n\n\t\t\t\tif (ops_ptr && (lhsr->type == REF_FIELD || lhsr->type == REF_POINTER))\n\t\t\t\t{\n\t\t\t\t\t//*ptr += 5;\n\t\t\t\t\t//can become (address), addstorep_f 5,ptr,sideeffect\n\t\t\t\t\t//instead of loadf_f, add_f, address, storep_f\n\t\t\t\t\t//or instead of loadp_f, add_f, storep_f\n\n\t\t\t\t\tfor (i = 0; (op=ops_ptr[i]); i++)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (QCC_OPCodeValid(op))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif ((*op->type_b)->type == rhsd.cast->type && (*op->type_a)->type == ev_pointer && (*op->type_c)->type == lhsr->cast->type)\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif (op)\n\t\t\t\t\t{\n\t\t\t\t\t\tQCC_ref_t ptr;\n\t\t\t\t\t\tqcc_usefulstatement = true;\n\t\t\t\t\t\tlhsr = QCC_PR_GenerateAddressOf(&ptr, lhsr);\n\t\t\t\t\t\tlhsd = QCC_RefToDef(lhsr, true);\n\t\t\t\t\t\trhsd = QCC_PR_Statement(op, rhsd, lhsd, NULL);\n\t\t\t\t\t\tlhsr = QCC_DefToRef(retbuf, rhsd);\t//we read the rhs, we can just return that as the result\n\t\t\t\t\t\tlhsr->readonly = true;\t//(a=b)=c is an error\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (ops != opcodes_store)\n\t\t\t\t{\n\t\t\t\t\tlhsd = QCC_RefToDef(lhsr, false);\n\t\t\t\t\tfor (i = 0; (op=ops[i]); i++)\n\t\t\t\t\t{\n//\t\t\t\t\t\tif (QCC_OPCodeValid(op))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif ((*op->type_b)->type == rhsd.cast->type && (*op->type_a)->type == lhsd.cast->type)\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif (!ops[i])\n\t\t\t\t\t{\n\t\t\t\t\t\trhsd = QCC_EvaluateCast(rhsd, lhsd.cast, true);\n\t\t\t\t\t\tfor (i = 0; ops[i]; i++)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\top = ops[i];\n\t//\t\t\t\t\t\tif (QCC_OPCodeValid(op))\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tif ((*op->type_b)->type == rhsd.cast->type && (*op->type_a)->type == lhsd.cast->type)\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (!ops[i])\n\t\t\t\t\t\t\tQCC_PR_ParseError(0, \"Type mismatch on assignment. %s %s %s is not supported\", lhsd.cast->name, opname, rhsd.cast->name);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (op->associative != ASSOC_LEFT)\n\t\t\t\t\t\trhsd = QCC_PR_Statement(op, lhsd, rhsd, NULL);\n\t\t\t\t\telse\n\t\t\t\t\t\trhsd = QCC_PR_Statement(op, lhsd, rhsd, NULL);\n\n\t\t\t\t\t//convert so we don't have issues with: i = (int)(float)(i+f)\n\t\t\t\t\t//this will also catch things like vec *= vec; which would be trying to store a float into a vector.\n\t\t\t\t\trhsd = QCC_SupplyConversionForAssignment(lhsr, rhsd, lhsr->cast, true);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n#if 1\n\t\t\t\t\trhsd = QCC_EvaluateCast(rhsd, lhsr->cast, true);\n#else\n\t\t\t\t\t/*if (flag_qccx && lhsr->cast->type == ev_pointer && rhsd.cast->type == ev_float)\n\t\t\t\t\t{\t//&%555 = 4.0;\n\t\t\t\t\t\tchar destname[256];\n\t\t\t\t\t\tQCC_PR_ParseWarning(WARN_LAXCAST, \"Implicit pointer dereference on assignment to %s\", QCC_GetRefName(lhsr, destname, sizeof(destname)));\n\t\t\t\t\t\tlhsd = QCC_RefToDef(lhsr, true);\n\t\t\t\t\t\tlhsr = QCC_PR_BuildRef(retbuf, REF_POINTER, lhsd, nullsref, lhsd.cast->aux_type, false);\n\t\t\t\t\t}\n\t\t\t\t\telse */if (QCC_SRef_IsNull(rhsd))\n\t\t\t\t\t{\n\t\t\t\t\t\tQCC_FreeTemp(rhsd);\n\t\t\t\t\t\trhsd = QCC_MakeIntConst(0);\n\t\t\t\t\t\t/*if (lhsr->cast->type == ev_vector)\n\t\t\t\t\t\t\trhsd = QCC_MakeVectorConst(0,0,0);\n\t\t\t\t\t\telse if (lhsr->cast->type == ev_struct || lhsr->cast->type == ev_union)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tQCC_PR_ParseError(0, \"Type mismatch on assignment. %s %s %s is not supported\", lhsr->cast->name, opname, rhsd.cast->name);\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if(lhsr->cast->type == ev_float)\n\t\t\t\t\t\t\trhsd = QCC_MakeFloatConst(0);\n\t\t\t\t\t\telse if(lhsr->cast->type == ev_integer)\n\t\t\t\t\t\t\trhsd = QCC_MakeIntConst(0);\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\trhsd = QCC_MakeIntConst(0);\n\t\t\t\t\t\trhsd.cast = lhsr->cast;*/\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\trhsd = QCC_SupplyConversionForAssignment(lhsr, rhsd, lhsr->cast, true);\n#endif\n\t\t\t\t}\n\t\t\t\trhsd = QCC_StoreSRefToRef(lhsr, rhsd, true, false);\t//FIXME: this should not always be true, but we don't know if the caller actually needs it\n\t\t\t\tqcc_usefulstatement = true;\n\t\t\t\tlhsr = QCC_DefToRef(retbuf, rhsd);\t//we read the rhs, we can just return that as the result\n\t\t\t\tlhsr->readonly = true;\t//(a=b)=c is an error\n\t\t\t}\n\t\t\telse\n\t\t\t\tbreak;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tQCC_statement_t *logicjump;\n\t\t\tQCC_statement_t *logictest;\n\t\t\t//go straight for the correct priority.\n\t\t\tfor (op = opcodeprioritized[priority][opnum]; op; op = opcodeprioritized[priority][++opnum])\n\t//\t\tfor (op=pr_opcodes ; op->name ; op++)\n\t\t\t{\n\t//\t\t\tif (op->priority != priority)\n\t//\t\t\t\tcontinue;\n\t\t\t\tif (!QCC_PR_CheckToken (op->name))\n\t\t\t\t\tcontinue;\n\n\t\t\t\tlogicjump = NULL;\n\t\t\t\tlhsd = QCC_RefToDef(lhsr, true);\n\t\t\t\tif (opt_logicops && (lhsd.cast->size == 1 || lhsd.cast->type == ev_vector))\n\t\t\t\t{\n\t\t\t\t\tif (!strcmp(op->name, \"&&\"))\t\t//guarenteed to be false if the lhs is false\n\t\t\t\t\t{\n\t\t\t\t\t\tif (lhsd.cast->type == ev_vector && flag_ifvector)\n\t\t\t\t\t\t\tlhsd = QCC_PR_StatementFlags (&pr_opcodes[OP_MUL_V], lhsd, lhsd, NULL, STFL_PRESERVEA);\n\t\t\t\t\t\tif (lhsd.cast->type == ev_string && flag_ifstring)\n\t\t\t\t\t\t\tlhsd = QCC_PR_StatementFlags (&pr_opcodes[OP_NE_S], lhsd, QCC_MakeStringConst(\"\"), NULL, 0);\n\t\t\t\t\t\tif (lhsd.cast->type == ev_double)\n\t\t\t\t\t\t\tlhsd = QCC_PR_StatementFlags (&pr_opcodes[OP_NE_D], lhsd, QCC_MakeDoubleConst(0), NULL, 0);\n\t\t\t\t\t\tif (lhsd.cast->type == ev_int64 || lhsd.cast->type == ev_uint64)\n\t\t\t\t\t\t\tlhsd = QCC_PR_StatementFlags (&pr_opcodes[OP_NE_I64], lhsd, QCC_MakeInt64Const(0), NULL, 0);\n\t\t\t\t\t\tif (!QCC_Eval_Truth(QCC_SRef_EvalConst(lhsd), lhsd.cast, false))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tQCC_ClobberDef(NULL);\t//FIXME...\n\t\t\t\t\t\t\tlogicjump = QCC_Generate_OP_IFNOT(lhsd, true);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\telse if (!strcmp(op->name, \"||\"))\t//guarenteed to be true if the lhs is true\n\t\t\t\t\t{\n\t\t\t\t\t\tif (lhsd.cast->type == ev_vector && flag_ifvector)\n\t\t\t\t\t\t\tlhsd = QCC_PR_StatementFlags (&pr_opcodes[OP_MUL_V], lhsd, lhsd, NULL, STFL_PRESERVEA);\n\t\t\t\t\t\tif (lhsd.cast->type == ev_string && flag_ifstring)\n\t\t\t\t\t\t\tlhsd = QCC_PR_StatementFlags (&pr_opcodes[OP_NE_S], lhsd, QCC_MakeStringConst(\"\"), NULL, 0);\n\t\t\t\t\t\tif (lhsd.cast->type == ev_double)\n\t\t\t\t\t\t\tlhsd = QCC_PR_StatementFlags (&pr_opcodes[OP_NE_D], lhsd, QCC_MakeDoubleConst(0), NULL, 0);\n\t\t\t\t\t\tif (lhsd.cast->type == ev_int64 || lhsd.cast->type == ev_uint64)\n\t\t\t\t\t\t\tlhsd = QCC_PR_StatementFlags (&pr_opcodes[OP_NE_I64], lhsd, QCC_MakeInt64Const(0), NULL, 0);\n\t\t\t\t\t\tif (!QCC_Eval_Truth(QCC_SRef_EvalConst(lhsd), lhsd.cast, false))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tQCC_ClobberDef(NULL);\t//FIXME...\n\t\t\t\t\t\t\tlogicjump = QCC_Generate_OP_IF(lhsd, true);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\trhsr = QCC_PR_RefExpression (&rhsbuf, priority-1, exprflags | EXPR_DISALLOW_ARRAYASSIGN);\n\n\t\t\t\tif (op->associative!=ASSOC_LEFT)\n\t\t\t\t{\n\t\t\t\t\tQCC_PR_ParseError(ERR_INTERNAL, \"internal error: should be unreachable\\n\");\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\trhsd = QCC_RefToDef(rhsr, true);\n\t\t\t\t\top = QCC_PR_ChooseOpcode(lhsd, rhsd, &opcodeprioritized[priority][opnum]);\n\n\t\t\t\t\tif ((*op->type_a)->type != lhsd.cast->type && (*op->type_a)->type != ev_variant)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (QCC_canConv(lhsd, (*op->type_a)->type) >= 100)\n\t\t\t\t\t\t\tQCC_PR_ParseWarning(WARN_IMPLICITCONVERSION, \"Implicit conversion from %s to %s\", lhsd.cast->name, (*op->type_a)->name);\n\t\t\t\t\t\tlhsd = QCC_EvaluateCast(lhsd, (*op->type_a), true);\n\t\t\t\t\t}\n\t\t\t\t\tif ((*op->type_b)->type != rhsd.cast->type && (*op->type_b)->type != ev_variant)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (QCC_canConv(rhsd, (*op->type_b)->type) >= 100)\n\t\t\t\t\t\t\tQCC_PR_ParseWarning(WARN_IMPLICITCONVERSION, \"Implicit conversion from %s to %s\", rhsd.cast->name, (*op->type_b)->name);\n\t\t\t\t\t\trhsd = QCC_EvaluateCast(rhsd, (*op->type_b), true);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (logicjump)\t//logic shortcut jumps to just before the if. the rhs is uninitialised if the jump was taken, but the lhs makes it deterministic.\n\t\t\t\t\t{\n\t\t\t\t\t\tlogicjump->flags |= STF_LOGICOP;\n\t\t\t\t\t\tlogicjump->b.jumpofs = &statements[numstatements] - logicjump;\n\t\t\t\t\t\tif (logicjump->b.jumpofs == 1)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tnumstatements--;\t//err, that was pointless.\n\t\t\t\t\t\t\tlogicjump = NULL;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (logicjump->b.jumpofs == 2 && !QCC_OpHasSideEffects(&logicjump[1]))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tlogicjump[0] = logicjump[1];\n\t\t\t\t\t\t\tnumstatements--;\t//don't bother if the jump is the same cost as the thing we're trying to skip (calls are expensive despite being a single opcode).\n\t\t\t\t\t\t\tlogicjump = NULL;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\toptres_logicops++;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (logicjump)\n\t\t\t\t\t{\n\t\t\t\t\t\tlhsd = QCC_PR_Statement (op, lhsd, rhsd, &logictest);\n\t\t\t\t\t\tif (!logictest)\n\t\t\t\t\t\t\tnumstatements = logicjump-statements;\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tlogicjump->b.jumpofs = logictest - logicjump;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\tlhsd = QCC_PR_Statement (op, lhsd, rhsd, NULL);\n\t\t\t\t\tlhsr = QCC_DefToRef(retbuf, lhsd);\n\t\t\t\t}\n\n\t\t\t\tif (priority > 1 && exprflags & EXPR_WARN_ABOVE_1)\n\t\t\t\t\tQCC_PR_ParseWarning(WARN_UNARYNOTSCOPE, \"suggest parenthesis for unary operator that applies to multiple terms\");\n\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (!op)\n\t\t\t\tbreak;\n\t\t}\n\t}\n\tif (lhsr == NULL)\n\t\tQCC_PR_ParseError(ERR_INTERNAL, \"e == null\");\n\n\tif (!(exprflags&EXPR_DISALLOW_COMMA) && priority == TOP_PRIORITY && QCC_PR_CheckToken (\",\"))\n\t{\n\t\tQCC_PR_DiscardRef(lhsr);\n\t\tif (!qcc_usefulstatement)\n\t\t\tQCC_PR_ParseWarning(WARN_POINTLESSSTATEMENT, \"Statement does not do anything\");\n\t\tqcc_usefulstatement = false;\n\t\tlhsr = QCC_PR_RefExpression(retbuf, TOP_PRIORITY, exprflags);\n\t}\n\treturn lhsr;\n}\n\nQCC_sref_t QCC_PR_Expression (int priority, int exprflags)\n{\n\tQCC_ref_t refbuf, *ret;\n\tret = QCC_PR_RefExpression(&refbuf, priority, exprflags);\n\treturn QCC_RefToDef(ret, true);\n}\n//parse the expression and discard the result. generate a warning if there were no assignments\n//this avoids generating getter statements from RefToDef in QCC_PR_Expression.\nstatic void QCC_PR_DiscardExpression (int priority, int exprflags)\n{\n\tQCC_ref_t refbuf, *ref;\n\tpbool olduseful = qcc_usefulstatement;\n\tqcc_usefulstatement = false;\n\n\tref = QCC_PR_RefExpression(&refbuf, priority, exprflags);\n\tQCC_PR_DiscardRef(ref);\n\n\tif (ref->cast->type != ev_void && !qcc_usefulstatement)\n\t{\n//\t\tint osl = pr_source_line;\n//\t\tpr_source_line = statementstart;\n\t\tQCC_PR_ParseWarning(WARN_POINTLESSSTATEMENT, \"Statement does not do anything\");\n//\t\tpr_source_line = osl;\n\t}\n\tqcc_usefulstatement = olduseful;\n}\n\nlong QCC_PR_IntConstExpr(void)\n{\n\t//fixme: should make sure that no actual statements are generated\n\tQCC_sref_t def = QCC_PR_Expression(TOP_PRIORITY, 0);\n\tconst QCC_eval_t *ev = QCC_SRef_EvalConst(def);\n\tif (ev)\n\t{\n\t\tQCC_FreeTemp(def);\n\t\tdef.sym->referenced = true;\n\t\tswitch(def.cast->type)\n\t\t{\n\t\tcase ev_integer:\n\t\t\treturn ev->_int;\n\t\tcase ev_float:\n\t\t\t{\n\t\t\t\tint i = ev->_float;\n\t\t\t\tif ((float)i == ev->_float)\n\t\t\t\t\treturn i;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase ev_double:\n\t\t\t{\n\t\t\t\tint i = ev->_double;\n\t\t\t\tif ((double)i == ev->_double)\n\t\t\t\t\treturn i;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase ev_uint:\n\t\t\treturn ev->_uint;\n\t\tcase ev_int64:\n\t\t\treturn ev->i64;\n\t\tcase ev_uint64:\n\t\t\treturn ev->u64;\n\t\tdefault:\n\t\t\tQCC_PR_ParseError(ERR_NOTACONSTANT, \"Value is not an integer constant\");\n\t\t}\n\t}\n\tQCC_PR_ParseError(ERR_NOTACONSTANT, \"Value is not an integer constant\");\n\treturn true;\n}\n\nstatic void QCC_PR_GotoStatement (QCC_statement_t *patch2, char *labelname)\n{\n\tif (num_gotos >= max_gotos)\n\t{\n\t\tmax_gotos += 8;\n\t\tpr_gotos = realloc(pr_gotos, sizeof(*pr_gotos)*max_gotos);\n\t}\n\n\tif (!QC_strlcpy(pr_gotos[num_gotos].name, labelname, sizeof(pr_gotos[num_gotos].name)))\n\t\tQCC_PR_ParseWarning(WARN_STRINGTOOLONG, \"Label name too long\");\n\tpr_gotos[num_gotos].lineno = pr_source_line;\n\tpr_gotos[num_gotos].statementno = patch2 - statements;\n\n\tnum_gotos++;\n}\n\n/*\nstatic pbool QCC_PR_StatementBlocksMatch(QCC_statement_t *p1, int p1count, QCC_statement_t *p2, int p2count)\n{\n\tif (p1count != p2count)\n\t\treturn false;\n\n\twhile(p1count>0)\n\t{\n\t\tif (p1->op != p2->op)\n\t\t\treturn false;\n\t\tif (memcmp(&p1->a, &p2->a, sizeof(p1->a)))\n\t\t\treturn false;\n\t\tif (memcmp(&p1->b, &p2->b, sizeof(p1->b)))\n\t\t\treturn false;\n\t\tif (memcmp(&p1->c, &p2->c, sizeof(p1->c)))\n\t\t\treturn false;\n\t\tp1++;\n\t\tp2++;\n\t\tp1count--;\n\t}\n\n\treturn true;\n}*/\n\n//vanilla qc only has an OP_IFNOT_I, others will be emulated as required, so we tend to need to emulate other opcodes.\nQCC_statement_t *QCC_Generate_OP_IF(QCC_sref_t e, pbool preserve)\n{\n\tunsigned int flags = (preserve?STFL_PRESERVEA:0);\n\tQCC_statement_t *st;\n\tint op = 0;\n\twhile (e.cast->type == ev_accessor)\n\t\te.cast = e.cast->parentclass;\n\n\tswitch(e.cast->type)\n\t{\n\t//int/pointer types\n\tcase ev_entity:\n\tcase ev_field:\n\tcase ev_function:\n\tcase ev_pointer:\n\tcase ev_integer:\n\tcase ev_uint:\n\tcase ev_boolean:\t//should be 0, 1i, or 1.0f, either way -0 isn't a problem so we can use the vanilla OP_IF_I for this\n\t\top = OP_IF_I;\n\t\tbreak;\n\n\t//emulated types\n\tcase ev_string:\n\t\tQCC_PR_ParseWarning(WARN_IFSTRING_USED, \"if (string) tests for null, not empty.\");\n\t\tif (flag_ifstring)\n\t\t\top = OP_IF_S;\n\t\telse\n\t\t\top = OP_IF_I;\n\t\tbreak;\n\tcase ev_float:\n\t\tif (flag_iffloat || QCC_OPCodeValid(&pr_opcodes[OP_IF_F]))\n\t\t\top = OP_IF_F;\n\t\telse\n\t\t\top = OP_IF_I;\n\t\tbreak;\n\tcase ev_vector:\n\t\tif (flag_ifvector)\n\t\t{\n\t\t\te = QCC_PR_StatementFlags (&pr_opcodes[OP_NOT_V], e, nullsref, NULL, flags);\n\t\t\top = OP_IFNOT_I;\n\t\t}\n\t\telse\n\t\t\top = OP_IF_I;\n\t\tbreak;\n\n\tcase ev_double:\n\t\te = QCC_PR_StatementFlags (&pr_opcodes[OP_NE_D], e, QCC_MakeDoubleConst(0), NULL, flags);\n\t\top = OP_IF_I;\n\t\tbreak;\n\tcase ev_int64:\n\tcase ev_uint64:\n\t\te = QCC_PR_StatementFlags (&pr_opcodes[OP_NE_I64], e, QCC_MakeInt64Const(0), NULL, flags);\n\t\top = OP_IF_I;\n\t\tbreak;\n\n\tcase ev_variant:\n\tcase ev_struct:\n\tcase ev_union:\n\tcase ev_void:\n\tdefault:\n\t\tQCC_PR_ParseWarning(WARN_CONDITIONALTYPEMISMATCH, \"conditional type mismatch: %s\", basictypenames[e.cast->type]);\n\t\top = OP_IF_I;\n\t\tbreak;\n\t}\n\n\tQCC_FreeTemp(QCC_PR_StatementFlags (&pr_opcodes[op], e, nullsref, &st, flags));\n\treturn st;\n}\nQCC_statement_t *QCC_Generate_OP_IFNOT(QCC_sref_t e, pbool preserve)\n{\n\tunsigned int flags = (preserve?STFL_PRESERVEA:0);\n\tQCC_statement_t *st;\n\tint op = 0;\n\n\tif (!e.cast)\n\t\te.cast = type_void;\n\twhile (e.cast->type == ev_accessor)\n\t\te.cast = e.cast->parentclass;\n\tswitch(e.cast->type)\n\t{\n\t//int/pointer types\n\tcase ev_entity:\n\tcase ev_field:\n\tcase ev_function:\n\tcase ev_pointer:\n\tcase ev_integer:\n\tcase ev_uint:\n\tcase ev_boolean:\t//should be 0, 1i, or 1.0f, either way -0 isn't a problem so we can use the vanilla OP_IFNOT_I for this\n\t\top = OP_IFNOT_I;\n\t\tbreak;\n\n\t//emulated types\n\tcase ev_string:\n\t\tQCC_PR_ParseWarning(WARN_IFSTRING_USED, \"if (string) tests for null, not empty\");\n\t\tif (flag_ifstring)\n\t\t\top = OP_IFNOT_S;\n\t\telse\n\t\t\top = OP_IFNOT_I;\n\t\tbreak;\n\tcase ev_float:\n\t\tif (flag_iffloat || QCC_OPCodeValid(&pr_opcodes[OP_IFNOT_F]))\n\t\t\top = OP_IFNOT_F;\n\t\telse\n\t\t\top = OP_IFNOT_I;\n\t\tbreak;\n\tcase ev_vector:\n\t\tif (flag_ifvector)\n\t\t{\n\t\t\te = QCC_PR_StatementFlags (&pr_opcodes[OP_NOT_V], e, nullsref, NULL, flags);\n\t\t\top = OP_IF_I;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tQCC_PR_ParseWarning(WARN_IFVECTOR_DISABLED, \"if (vector) tests only the first element with the current compiler flags\");\n\t\t\top = OP_IFNOT_I;\n\t\t}\n\t\tbreak;\n\n\tcase ev_double:\n\t\te = QCC_PR_StatementFlags (&pr_opcodes[OP_NE_D], e, QCC_MakeDoubleConst(0), NULL, flags);\n\t\top = OP_IFNOT_I;\n\t\tbreak;\n\tcase ev_int64:\n\tcase ev_uint64:\n\t\te = QCC_PR_StatementFlags (&pr_opcodes[OP_NE_I64], e, QCC_MakeInt64Const(0), NULL, flags);\n\t\top = OP_IFNOT_I;\n\t\tbreak;\n\n\tcase ev_variant:\n\tcase ev_struct:\n\tcase ev_union:\n\tcase ev_void:\n\tdefault:\n\t\tQCC_PR_ParseWarning(WARN_CONDITIONALTYPEMISMATCH, \"conditional type mismatch: %s\", basictypenames[e.cast->type]);\n\t\top = OP_IFNOT_I;\n\t\tbreak;\n\t}\n\n\tQCC_FreeTemp(QCC_PR_StatementFlags (&pr_opcodes[op], e, nullsref, &st, flags));\n\treturn st;\n}\n\n//for consistancy with if+ifnot\nQCC_statement_t *QCC_Generate_OP_GOTO(void)\n{\n\tQCC_statement_t *st;\n\tQCC_FreeTemp(QCC_PR_StatementFlags (&pr_opcodes[OP_GOTO], nullsref, nullsref, &st, STFL_DISCARDRESULT));\n\treturn st;\n}\n\n//called for return statements\nstatic void PR_GenerateReturnOuts(void)\n{\n\tunsigned int i;\n\tQCC_sref_t p;\n\tQCC_def_t *local;\n\tint parm;\n\n\tfor (i = 0, parm = 0, local = pr.local_head.nextlocal; i < pr_scope->type->num_parms; i++)\n\t{\n\t\tif (pr_scope->type->params[i].out)\n\t\t{\n\t\t\tif (parm > MAX_PARMS)\n\t\t\t\tp = extra_parms[parm-MAX_PARMS];\n\t\t\telse\n\t\t\t{\n\t\t\t\tp.sym = &def_parms[parm];\n\t\t\t\tp.ofs = 0;\n\t\t\t\tp.cast = type_vector;\n\t\t\t}\n\t\t\tQCC_ForceUnFreeDef(p.sym);\n\t\t\tQCC_StoreToSRef(p, QCC_MakeSRefForce(local, 0, local->type), local->type, false, false);\n\t\t}\n\t\tparm += (pr_scope->type->params[i].type->size+2)/3;\n\t\tlocal = local->deftail->nextlocal;\n\t}\n}\n\nstatic void QCC_PR_ParseStatement_Using(void)\n{\t//the 'using(...) {}' control statement exists mainly to mute warnings about deprecations within its block.\n\t//it does this by basically creating a private alias for each name given in the parenthasis (new=orig to give the alias a different name).\n\tQCC_def_t\t*d;\n\n\tQCC_def_t *subscopestop;\n\tQCC_def_t *subscopestart = pr.local_tail;\n\t//QCC_type_t *lt = NULL, *type;\n\tpbool block = QCC_PR_CheckToken(\"(\");\n\tdo\n\t{\n\t\t/*type = QCC_PR_ParseType (false, true);\n\t\tif (type)\n\t\t{\n\t\t\td = QCC_PR_GetDef (type, QCC_PR_ParseName(), pr_scope, true, 0, 0);\n\t\t\tif (QCC_PR_CheckToken(\"=\"))\n\t\t\t\tQCC_PR_ParseInitializerDef(d, 0);\n\t\t\tQCC_FreeDef(d);\n\t\t\tlt = type;\n\t\t}\n\t\telse*/\n\t\t{\t//define an alias of the variable with the same name\n\t\t\tconst char *name = QCC_PR_ParseName();\n\n\t\t\tQCC_def_t *def = QCC_PR_GetDef (NULL, name, pr_scope, false, 0, 0);\n\t\t\tif (!def)\n\t\t\t\tQCC_PR_ParseError(ERR_NOTDEFINED, \"%s is not defined\", name);\n\t\t\tdef->referenced = true;\n\t\t\tif (QCC_PR_CheckToken(\"=\"))\n\t\t\t\tname = QCC_PR_ParseName();\t//they wanted a different name for it.\n\t\t\tif (def->deprecated)\n\t\t\t{\n\t\t\t\tif (*def->deprecated)\t//we have a reason for it\n\t\t\t\t\tQCC_PR_ParseWarning(WARN_MUTEDEPRECATEDVARIABLE, \"Variable \\\"%s\\\" is deprecated: %s\", def->name, def->deprecated);\n\t\t\t\telse\t\t//we don't have any reason for it.\n\t\t\t\t\tQCC_PR_ParseWarning(WARN_MUTEDEPRECATEDVARIABLE, \"Variable \\\"%s\\\" is deprecated\", def->name);\n\t\t\t}\n\t\t\tdef = QCC_PR_DummyDef(def->type, name, pr_scope, def->arraysize, def, 0, true, GDF_STRIP|GDF_ALIAS);\n\t\t}\n\t} while(QCC_PR_CheckToken(\",\"));\n\n\tpr_assumetermtype = NULL;\n\tif (block)\n\t\tQCC_PR_Expect(\")\");\n\telse\n\t{\t//applies to whole rest of scope...\n\t\tQCC_PR_Expect(\";\");\n\t\treturn;\n\t}\n\n\tsubscopestop = pr_subscopedlocals?NULL:pr.local_tail->nextlocal;\n\n\tQCC_PR_ParseStatement();\t//don't give the hanging ';' warning.\n\n\t//remove any new locals from the hashtable.\n\t//typically this is just the stuff inside the for(here;;)\n\tfor (d = subscopestart->nextlocal; d != subscopestop; d = d->nextlocal)\n\t{\n\t\tif (!d->subscoped_away)\n\t\t{\n\t\t\tpHash_RemoveData(&localstable, d->name, d);\n\t\t\td->subscoped_away = true;\n\t\t}\n\t}\n\n\treturn;\n}\n\n/*\n============\nQCC_PR_ParseStatement_For\n\npulled out of QCC_PR_ParseStatement because of stack use.\n============\n*/\nstatic void QCC_PR_ParseStatement_For(void)\n{\n\tint continues;\n\tint breaks;\n\tint i;\n\tQCC_sref_t\t\t\t\te;\n\tQCC_def_t\t*d;\n\tQCC_statement_t\t\t*patch1, *patch2, *patch3, *patch4;\n\n\n\tint old_numstatements;\n\tint numtemp;\n\tQCC_def_t *subscopestop;\n\tQCC_def_t *subscopestart = pr.local_tail;\n\n\tQCC_statement_t\t\ttemp[256];\n\n\tcontinues = num_continues;\n\tbreaks = num_breaks;\n\n\tQCC_PR_Expect(\"(\");\n\tif (!QCC_PR_CheckToken(\";\"))\n\t{\n\t\tQCC_type_t *lt = NULL, *type;\n\t\tdo\n\t\t{\n\t\t\ttype = QCC_PR_ParseType (false, true, false);\n\t\t\tif (type)\n\t\t\t{\n\t\t\t\td = QCC_PR_GetDef (type, QCC_PR_ParseName(), pr_scope, true, 0, 0);\n\t\t\t\tif (QCC_PR_CheckToken(\"=\"))\n\t\t\t\t\tQCC_PR_ParseInitializerDef(d, 0);\n\t\t\t\tQCC_FreeDef(d);\n\t\t\t\tlt = type;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tpr_assumetermtype = lt;\n\t\t\t\tpr_assumetermscope = pr_scope;\n\t\t\t\tpr_assumetermflags = 0;\n\t\t\t\tQCC_PR_DiscardExpression(TOP_PRIORITY, EXPR_DISALLOW_COMMA);\n\t\t\t}\n\t\t} while(QCC_PR_CheckToken(\",\"));\n\t\tpr_assumetermtype = NULL;\n\t\tQCC_PR_Expect(\";\");\n\t}\n\tsubscopestop = pr_subscopedlocals?NULL:pr.local_tail->nextlocal;\n\n\tQCC_ClobberDef(NULL);\n\n\tpatch2 = &statements[numstatements];\t//restart of the loop\n\tif (!QCC_PR_CheckToken(\";\"))\n\t{\n\t\tconditional = 1;\n\t\te = QCC_PR_Expression(TOP_PRIORITY, 0);\n\t\tconditional = 0;\n\t\tQCC_PR_Expect(\";\");\n\t}\n\telse\n\t\te = nullsref;\n\n\tif (e.cast)\t//final condition+jump\n\t\tQCC_FreeTemp(QCC_PR_StatementFlags(&pr_opcodes[OP_IFNOT_I], e, nullsref, &patch1, STFL_DISCARDRESULT));\n\telse\n\t\tpatch1 = NULL;\n\n\tif (!QCC_PR_CheckToken(\")\"))\n\t{\n\t\told_numstatements = numstatements;\n\t\tQCC_PR_DiscardExpression(TOP_PRIORITY, 0);\n\n\t\tnumtemp = numstatements - old_numstatements;\n\t\tif (numtemp > sizeof(temp)/sizeof(temp[0]))\n\t\t\tQCC_PR_ParseError(ERR_TOOCOMPLEX, \"Update expression too large\");\n\t\tnumstatements = old_numstatements;\n\t\tfor (i = 0 ; i < numtemp ; i++)\n\t\t{\n\t\t\ttemp[i] = statements[numstatements + i];\n\t\t}\n\n\t\tQCC_PR_Expect(\")\");\n\t}\n\telse\n\t\tnumtemp = 0;\n\n\t//parse the statement block\n\tif (!QCC_PR_CheckToken(\";\"))\n\t\tQCC_PR_ParseStatement();\t//don't give the hanging ';' warning.\n\tpatch3 = &statements[numstatements];\t//location for continues\n\t//reinsert the 'increment' statements. lets hope they didn't have any gotos...\n\tfor (i = 0 ; i < numtemp ; i++)\n\t{\n\t\tstatements[numstatements] = temp[i];\n\t\tstatements[numstatements].linenum = pr_token_line_last;\n\t\tnumstatements++;\n\t}\n\tpatch4 = QCC_Generate_OP_GOTO();\n\tpatch4->a.jumpofs = patch2 - patch4;\n\tif (patch1)\n\t\tpatch1->b.jumpofs = &statements[numstatements] - patch1;\t//condition failure jumps here\n\n\t//fix up breaks+continues\n\tif (breaks != num_breaks)\n\t{\n\t\tfor(i = breaks; i < num_breaks; i++)\n\t\t{\n\t\t\tpatch1 = &statements[pr_breaks[i]];\n\t\t\tstatements[pr_breaks[i]].a.jumpofs = &statements[numstatements] - patch1;\n\t\t}\n\t\tnum_breaks = breaks;\n\t}\n\tif (continues != num_continues)\n\t{\n\t\tfor(i = continues; i < num_continues; i++)\n\t\t{\n\t\t\tpatch1 = &statements[pr_continues[i]];\n\t\t\tstatements[pr_continues[i]].a.jumpofs = patch3 - patch1;\n\t\t}\n\t\tnum_continues = continues;\n\t}\n\n\n\t//remove any new locals from the hashtable.\n\t//typically this is just the stuff inside the for(here;;)\n\tfor (d = subscopestart->nextlocal; d != subscopestop; d = d->nextlocal)\n\t{\n\t\tif (!d->subscoped_away)\n\t\t{\n\t\t\tpHash_RemoveData(&localstable, d->name, d);\n\t\t\td->subscoped_away = true;\n\t\t}\n\t}\n\n\treturn;\n}\n/*\n============\nPR_ParseStatement\n\n============\n*/\nvoid QCC_PR_ParseStatement (void)\n{\n\tint continues;\n\tint breaks;\n\tint cases;\n\tint i;\n\tQCC_sref_t\t\t\t\te, e2;\n\tQCC_def_t\t*d;\n\tQCC_statement_t\t\t*patch1, *patch2, *patch3;\n\tint statementstart = pr_source_line;\n\tpbool wasuntil;\n\n\tQCC_ClobberDef(NULL);\t//make sure any conditionals don't weird out.\n\n\tif (QCC_PR_CheckToken (\"{\"))\n\t{\n\t\tint startingtypes = numtypeinfos;\n\t\td = pr.local_tail;\n\t\twhile (!QCC_PR_CheckToken(\"}\"))\n\t\t\tQCC_PR_ParseStatement ();\n\n\t\tif (pr_subscopedlocals)\n\t\t{\t//remove any new locals from the hashtable.\n\t\t\tfor (d = d->nextlocal; d; d = d->nextlocal)\n\t\t\t{\n\t\t\t\tif (!d->subscoped_away)\n\t\t\t\t{\n\t\t\t\t\tpHash_RemoveData(&localstable, d->name, d);\n\t\t\t\t\td->subscoped_away = true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tfor (; startingtypes < numtypeinfos; startingtypes++)\n\t\t{\n\t\t\tif (qcc_typeinfo[startingtypes].typedefed)\n\t\t\t{\n\t\t\t\tqcc_typeinfo[startingtypes].typedefed = false;\n\t\t\t\tpHash_RemoveData(&typedeftable, qcc_typeinfo[startingtypes].name, &qcc_typeinfo[startingtypes]);\n\t\t\t}\n\t\t}\n\t\treturn;\n\t}\n\n\tif (QCC_PR_CheckKeyword(keyword_return, \"return\"))\n\t{\n\t\t/*\n\t\taccumulate behaviour requires the ability to just run code without explicit returns.\n\t\treturn = foo; sets the value that will be returned when the function finally exits, without returning now.\n\t\treturn; returns that value now, without execing later accumulations.\n\t\treturn 5; also returns now.\n\t\t*/\n\n\t\tif (QCC_PR_CheckToken (\";\"))\n\t\t{\n\t\t\tPR_GenerateReturnOuts();\n\t\t\tif (pr_scope->type->aux_type->type != ev_void)\n\t\t\t{\t//accumulated functions are not required to return anything, on the assumption that a previous 'part' of the function did so\n\t\t\t\tif (pr_scope->type->aux_type->size > type_vector->size)\n\t\t\t\t\tQCC_PR_ParseError(ERR_BADEXTENSION, \"\\'%s\\' returned nothing, expected %s\", pr_scope->name, pr_scope->type->aux_type->name);\t//just make it fatal. too lazy to handle it\n\t\t\t\telse if ((!pr_scope->def || !pr_scope->def->accumulate) && !pr_scope->returndef.cast)\n\t\t\t\t\tQCC_PR_ParseWarning(WARN_MISSINGRETURNVALUE, \"\\'%s\\' returned nothing, expected %s\", pr_scope->name, pr_scope->type->aux_type->name);\n\t\t\t\t//this should not normally happen\n\t\t\t\tif (!pr_scope->returndef.cast)\n\t\t\t\t{\t//but if it does, allocate a local that can be return=foo; before the return. depend upon qc's null initialisation rules for the default value.\n\t\t\t\t\tpr_scope->returndef = QCC_PR_GetSRef(pr_scope->type->aux_type, \"ret*\", pr_scope, true, 0, GDF_NONE);\n\t\t\t\t\tQCC_FreeTemp(pr_scope->returndef);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (pr_scope->returndef.cast)\n\t\t\t{\n\t\t\t\tQCC_ForceUnFreeDef(pr_scope->returndef.sym);\n\t\t\t\tQCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_RETURN], pr_scope->returndef, nullsref, NULL));\n\t\t\t\treturn;\n\t\t\t}\n//\t\t\tif (opt_return_only)\n//\t\t\t\tQCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_DONE], nullsref, nullsref, NULL));\n//\t\t\telse\n\t\t\tif (pr_scope->type->aux_type->type == ev_vector)\t//make sure bad returns don't return junk in the y+z members.\n\t\t\t\tQCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_RETURN], QCC_MakeVectorConst(0,0,0), nullsref, NULL));\n\t\t\telse\n\t\t\t\tQCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_RETURN], nullsref, nullsref, NULL));\n\t\t\treturn;\n\t\t}\n\n\t\tif (QCC_PR_CheckToken (\"=\"))\n\t\t{\n\t\t\tQCC_ref_t r;\n\t\t\tif (pr_scope->type->aux_type->size > type_vector->size)\n\t\t\t\tQCC_PR_ParseError(ERR_BADEXTENSION, \"\\'%s\\' not supported when returning %s\", pr_scope->name, pr_scope->type->aux_type->name);\t//just make it fatal. too lazy to handle it\n\t\t\tif (!pr_scope->returndef.cast)\n\t\t\t\tpr_scope->returndef = QCC_PR_GetSRef(pr_scope->type->aux_type, \"ret*\", pr_scope, true, 0, GDF_NONE);\n\t\t\telse\n\t\t\t\tQCC_ForceUnFreeDef(pr_scope->returndef.sym);\n\n\t\t\te = QCC_PR_Expression(TOP_PRIORITY, 0);\n\t\t\tQCC_PR_Expect (\";\");\n\n\t\t\tQCC_StoreSRefToRef(QCC_PR_BuildRef(&r, REF_GLOBAL, pr_scope->returndef, nullsref, pr_scope->type->aux_type, false, 0), e, false, false);\n\t\t\treturn;\n\t\t}\n\n\t\te = QCC_PR_Expression (TOP_PRIORITY, 0);\n\t\tQCC_PR_Expect (\";\");\n\t\tif (QCC_SRef_IsNull(e))\n\t\t{\n\t\t\tQCC_FreeTemp(e);\n\t\t\t//return __NULL__; is allowed regardless of actual return type.\n\t\t\tswitch (pr_scope->type->aux_type->type)\n\t\t\t{\n\t\t\tcase ev_vector:\n\t\t\t\te = QCC_MakeVectorConst(0, 0, 0);\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\tcase ev_float:\n\t\t\t\te = QCC_MakeFloatConst(0);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\te.cast = pr_scope->type->aux_type;\n\t\t}\n\t\telse if (pr_scope->type->aux_type->type != e.cast->type)\n\t\t{\n\t\t\tif (pr_scope->type->aux_type->type == ev_void)\n\t\t\t{\t//returning a value inside a function defined to return void is bad dude.\n\t\t\t\tQCC_PR_ParseWarning(WARN_WRONGRETURNTYPE, \"\\'%s\\' returned %s, expected %s\", pr_scope->name, e.sym->type->name, pr_scope->type->aux_type->name);\n\t\t\t\te = QCC_EvaluateCast(e, type_variant, true);\n\t\t\t}\n\t\t\telse\n\t\t\t\te = QCC_EvaluateCast(e, pr_scope->type->aux_type, true);\n\t\t}\n\t\tPR_GenerateReturnOuts();\n\t\tif (pr_scope->type->aux_type->size > type_vector->size)\n\t\t{\n\t\t\tQCC_StoreToPointer(pr_scope->returndef, nullsref, e, pr_scope->type->aux_type);\n\t\t\tQCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_RETURN], pr_scope->returndef, nullsref, NULL));\t//standard return has no real meaning, might as well return the address though even though we'll treat it as void.\n\t\t}\n\t\telse\n\t\t\tQCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_RETURN], e, nullsref, NULL));\n\t\treturn;\n\t}\n\tif (QCC_PR_CheckKeyword(keyword_exit, \"exit\"))\n\t{\n\t\tPR_GenerateReturnOuts();\n\t\tQCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_DONE], nullsref, nullsref, NULL));\n\t\tQCC_PR_Expect (\";\");\n\t\treturn;\n\t}\n\n\tif (QCC_PR_CheckKeyword(keyword_loop, \"loop\"))\n\t{\n\t\tcontinues = num_continues;\n\t\tbreaks = num_breaks;\n\n\t\tpatch2 = &statements[numstatements];\n\t\tQCC_PR_ParseStatement ();\n\t\tQCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_GOTO], nullsref, nullsref, &patch3));\n\t\tpatch3->a.jumpofs = patch2 - patch3;\n\n\t\tif (breaks != num_breaks)\n\t\t{\n\t\t\tfor(i = breaks; i < num_breaks; i++)\n\t\t\t{\n\t\t\t\tpatch1 = &statements[pr_breaks[i]];\n\t\t\t\tstatements[pr_breaks[i]].a.jumpofs = &statements[numstatements] - patch1;\t//jump to after the return-to-top goto\n\t\t\t}\n\t\t\tnum_breaks = breaks;\n\t\t}\n\t\tif (continues != num_continues)\n\t\t{\n\t\t\tfor(i = continues; i < num_continues; i++)\n\t\t\t{\n\t\t\t\tpatch1 = &statements[pr_continues[i]];\n\t\t\t\tstatements[pr_continues[i]].a.jumpofs = patch2 - patch1;\t//jump back to top\n\t\t\t}\n\t\t\tnum_continues = continues;\n\t\t}\n\t\treturn;\n\t}\n\n\twasuntil = QCC_PR_CheckKeyword(keyword_until, \"until\");\n\tif (wasuntil || QCC_PR_CheckKeyword(keyword_while, \"while\"))\n\t{\n\t\tconst QCC_eval_t *eval;\n\t\tcontinues = num_continues;\n\t\tbreaks = num_breaks;\n\n\t\tQCC_PR_Expect (\"(\");\n\t\tpatch2 = &statements[numstatements];\n\t\tconditional = 1;\n\t\te = QCC_PR_Expression (TOP_PRIORITY, 0);\n\t\tconditional = 0;\n\n\t\teval = QCC_SRef_EvalConst(e);\n\t\tif (eval && /*opt_compound_jumps &&*/ e.cast->type == ev_float)\n\t\t{\n\t\t\t//optres_compound_jumps++;\n\t\t\tQCC_FreeTemp(e);\n\t\t\tif ((!eval->_float) != wasuntil)\n\t\t\t\tQCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_GOTO], nullsref, nullsref, &patch1));\n\t\t\telse\n\t\t\t\tpatch1 = NULL;\n\t\t}\n\t\telse if (wasuntil)\n\t\t\tpatch1 = QCC_Generate_OP_IF(e, false);\n\t\telse\n\t\t\tpatch1 = QCC_Generate_OP_IFNOT(e, false);\n\t\tQCC_PR_Expect (\")\");\t//after the line number is noted..\n\t\tQCC_PR_ParseStatement ();\n\t\tpatch3 = QCC_Generate_OP_GOTO();\n\t\tpatch3->a.jumpofs = patch2 - patch3;\n\t\tif (patch1)\n\t\t{\n\t\t\tif (patch1->op == OP_GOTO)\n\t\t\t\tpatch1->a.jumpofs = &statements[numstatements] - patch1;\n\t\t\telse\n\t\t\t\tpatch1->b.jumpofs = &statements[numstatements] - patch1;\n\t\t}\n\n\t\tif (breaks != num_breaks)\n\t\t{\n\t\t\tfor(i = breaks; i < num_breaks; i++)\n\t\t\t{\n\t\t\t\tpatch1 = &statements[pr_breaks[i]];\n\t\t\t\tstatements[pr_breaks[i]].a.jumpofs = &statements[numstatements] - patch1;\t//jump to after the return-to-top goto\n\t\t\t}\n\t\t\tnum_breaks = breaks;\n\t\t}\n\t\tif (continues != num_continues)\n\t\t{\n\t\t\tfor(i = continues; i < num_continues; i++)\n\t\t\t{\n\t\t\t\tpatch1 = &statements[pr_continues[i]];\n\t\t\t\tstatements[pr_continues[i]].a.jumpofs = patch2 - patch1;\t//jump back to top\n\t\t\t}\n\t\t\tnum_continues = continues;\n\t\t}\n\t\treturn;\n\t}\n\tif (QCC_PR_CheckKeyword(keyword_for, \"for\"))\n\t{\n\t\tQCC_PR_ParseStatement_For();\n\t\treturn;\n\t}\n\tif (QCC_PR_CheckKeyword(keyword_do, \"do\"))\n\t{\n\t\tconst QCC_eval_t *eval;\n\t\tpbool until;\n\t\tcontinues = num_continues;\n\t\tbreaks = num_breaks;\n\n\t\tpatch1 = &statements[numstatements];\n\t\tQCC_PR_ParseStatement ();\n\t\tuntil = QCC_PR_CheckKeyword(keyword_until, \"until\");\n\t\tif (!until)\n\t\t\tQCC_PR_Expect (\"while\");\n\t\tQCC_PR_Expect (\"(\");\n\t\tpatch3 = &statements[numstatements];\n\t\tconditional = 1;\n\t\te = QCC_PR_Expression (TOP_PRIORITY, 0);\n\t\tconditional = 0;\n\n\t\teval = QCC_SRef_EvalConst(e);\n\t\tif (eval)\n\t\t{\n\t\t\tif (until?!eval->_int:eval->_int)\n\t\t\t{\n\t\t\t\tQCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_GOTO], nullsref, nullsref, &patch2));\n\t\t\t\tpatch2->a.jumpofs = patch1 - patch2;\n\t\t\t}\n\t\t\tQCC_FreeTemp(e);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (until)\n\t\t\t\tpatch2 = QCC_Generate_OP_IFNOT(e, false);\n\t\t\telse\n\t\t\t\tpatch2 = QCC_Generate_OP_IF(e, false);\n\t\t\tpatch2->b.jumpofs = patch1 - patch2;\n\t\t}\n\n\t\tQCC_PR_Expect (\")\");\n\t\tQCC_PR_Expect (\";\");\n\n\t\tif (breaks != num_breaks)\n\t\t{\n\t\t\tfor(i = breaks; i < num_breaks; i++)\n\t\t\t{\n\t\t\t\tpatch2 = &statements[pr_breaks[i]];\n\t\t\t\tstatements[pr_breaks[i]].a.jumpofs = &statements[numstatements] - patch2;\n\t\t\t}\n\t\t\tnum_breaks = breaks;\n\t\t}\n\t\tif (continues != num_continues)\n\t\t{\t//continue in do{}while(cond); jumps to the while, not the do.\n\t\t\tfor(i = continues; i < num_continues; i++)\n\t\t\t{\n\t\t\t\tpatch2 = &statements[pr_continues[i]];\n\t\t\t\tstatements[pr_continues[i]].a.jumpofs = patch3 - patch2;\n\t\t\t}\n\t\t\tnum_continues = continues;\n\t\t}\n\n\t\treturn;\n\t}\n\n\tif (QCC_PR_CheckKeyword(keyword_local, \"local\"))\n\t{\n//\t\tif (locals_end != numpr_globals)\t//is this breaking because of locals?\n//\t\t\tQCC_PR_ParseWarning(\"local vars after temp vars\\n\");\n\t\tQCC_PR_ParseDefs (NULL, true);\n\t\treturn;\n\t}\n\n\tif (pr_token_type == tt_name)\n\t{\n\t\tQCC_type_t *type = QCC_TypeForName(pr_token);\n\t\tif (type)\n\t\t{\n\t\t\tif (strncmp(pr_file_p, \"::\", 2))\n\t\t\t{\n\t\t\t\tQCC_PR_ParseDefs (NULL, false);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\tif ((keyword_var && !STRCMP (\"var\", pr_token)) ||\n\t\t\t(keyword_noref && !STRCMP (\"noref\", pr_token)) ||\n\t\t\t(keyword_string && !STRCMP (\"string\", pr_token)) ||\n\t\t\t(keyword_float && !STRCMP (\"float\", pr_token)) ||\n\t\t\t(keyword_double && !STRCMP (\"double\", pr_token)) ||\n\t\t\t(keyword_entity && !STRCMP (\"entity\", pr_token)) ||\n\t\t\t(keyword_vector && !STRCMP (\"vector\", pr_token)) ||\n\t\t\t(keyword_integer && !STRCMP (\"integer\", pr_token)) ||\n\t\t\t(keyword_unsigned && !STRCMP (\"unsigned\", pr_token)) ||\n\t\t\t(keyword_signed && !STRCMP (\"signed\", pr_token)) ||\n\t\t\t(keyword_long && !STRCMP (\"long\", pr_token)) ||\n\t\t\t(keyword_int && !STRCMP (\"int\", pr_token)) ||\n\t\t\t(keyword_short && !STRCMP (\"short\", pr_token)) ||\n\t\t\t(keyword_char && !STRCMP (\"char\", pr_token)) ||\n\t\t\t(\t\t\t\t!STRCMP (\"_Bool\", pr_token)) ||\n\t\t\t(keyword_register && !STRCMP (\"register\", pr_token)) ||\n\t\t\t(keyword_volatile && !STRCMP (\"volatile\", pr_token)) ||\n\t\t\t(keyword_static && !STRCMP (\"static\", pr_token)) ||\n\t\t\t(keyword_class && !STRCMP (\"class\", pr_token)) ||\n\t\t\t(keyword_struct && !STRCMP (\"struct\", pr_token)) ||\n\t\t\t(keyword_union && !STRCMP (\"union\", pr_token)) ||\n\t\t\t(keyword_enum && !STRCMP (\"enum\", pr_token)) ||\n\t\t\t(keyword_extern && !STRCMP (\"extern\", pr_token)) ||\n\t\t\t(keyword_auto && !STRCMP (\"auto\", pr_token)) ||\n\t\t\t(keyword_typedef && !STRCMP (\"typedef\", pr_token)) ||\n\t\t\t(keyword_const && !STRCMP (\"const\", pr_token)))\n\t\t{\n\t\t\tQCC_PR_ParseDefs (NULL, true);\n\t\t\treturn;\n\t\t}\n\t}\n\tif (pr_token_type == tt_punct && QCC_PR_PeekToken (\".\"))\n\t{\t//for local .entity without var/local\n\t\tQCC_PR_ParseDefs (NULL, true);\n\t\treturn;\n\t}\n\n\tif (QCC_PR_CheckKeyword(keyword_state, \"state\"))\n\t{\n\t\tQCC_PR_Expect(\"[\");\n\t\tQCC_PR_ParseState();\n\t\tQCC_PR_Expect(\";\");\n\t\treturn;\n\t}\n\tif (QCC_PR_CheckToken(\"#\"))\n\t{\n\t\tchar *name;\n\t\tfloat frame = pr_immediate._float;\n\t\tQCC_PR_Lex();\n\t\tname = QCC_PR_ParseName();\n\t\tQCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_STATE], QCC_MakeFloatConst(frame), QCC_PR_GetSRef(type_function, name, NULL, false, 0, false), NULL));\n\t\tQCC_PR_Expect(\";\");\n\t\treturn;\n\t}\n\n\tif (QCC_PR_CheckKeyword(keyword_if, \"if\"))\n\t{\n\t\tunsigned int oldnumst, oldlab;\n\t\tpbool striptruth = false;\n\t\tpbool stripfalse = false;\n\t\tconst QCC_eval_t *eval;\n\t\tint negate = QCC_PR_CheckKeyword(keyword_not, \"not\")/*hexenc*/;\n\t\tif (!negate && QCC_PR_CheckToken(\"!\"))\n\t\t{\n\t\t\tQCC_PR_ParseWarning (WARN_FTE_SPECIFIC, \"if !() is specific to fteqcc\");\n\t\t\tnegate = 2;\n\t\t}\n\t\telse if (negate && qcc_targetformat != QCF_HEXEN2 && qcc_targetformat != QCF_UHEXEN2 && qcc_targetformat != QCF_FTEH2)\n\t\t\tQCC_PR_ParseWarning (WARN_FTE_SPECIFIC, \"if not() is specific to fteqcc or hexen2\");\n\n\t\tQCC_PR_Expect (\"(\");\n\t\tconditional = 1;\n\t\te = QCC_PR_Expression (TOP_PRIORITY, 0);\n\t\tconditional = 0;\n\n\t\tif (negate == 2)\n\t\t{\n\t\t\tif (e.cast->type == ev_string/*deal with empty properly*/\n\t\t\t\t|| e.cast->type == ev_float/*deal with -0.0*/\n\t\t\t\t|| e.cast->type == ev_accessor/*its complicated*/ ) \n\t\t\t{\n\t\t\t\te = QCC_PR_GenerateLogicalNot(e, \"Type mismatch: !%s\");\n\t\t\t\tnegate = 0;\n\t\t\t}\n\t\t}\n\n\t\teval = QCC_SRef_EvalConst(e);\n\n//\t\tnegate = negate != 0;\n\n\t\toldnumst = numstatements;\n\t\tif (eval)\n\t\t{\n\t\t\tif (e.cast->type == ev_float)\n\t\t\t\tstriptruth = eval->_float == 0;\n\t\t\telse\n\t\t\t\tstriptruth = eval->_int == 0;\n\t\t\tif (negate)\n\t\t\t\tstriptruth = !striptruth;\n\t\t\tstripfalse = !striptruth;\n\t\t\tpatch1 = NULL;\n\t\t\tQCC_FreeTemp(e);\n\n\t\t\tif (striptruth)\n\t\t\t\tpatch1 = QCC_Generate_OP_GOTO();\n\t\t}\n\t\telse if (negate)\n\t\t{\n\t\t\tpatch1 = QCC_Generate_OP_IF(e, false);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tpatch1 = QCC_Generate_OP_IFNOT(e, false);\n\t\t}\n\n\t\tQCC_PR_Expect (\")\");\t//close bracket is after we save the statement to mem (so debugger does not show the if statement as being on the line after\n\n\t\toldlab = num_labels;\n\t\tQCC_PR_ParseStatement ();\n\t\tif (striptruth && oldlab == num_labels)\n\t\t{\n\t\t\tQCC_UngenerateStatements(oldnumst);\n\t\t\tpatch1 = NULL;\n\t\t}\n\t\telse\n\t\t\tstriptruth = false;\n\n\t\tif (QCC_PR_CheckKeyword (keyword_else, \"else\"))\n\t\t{\n\t\t\tint lastwasreturn;\n\t\t\tlastwasreturn = statements[numstatements-1].op == OP_RETURN || statements[numstatements-1].op == OP_DONE ||\n\t\t\t\tstatements[numstatements-1].op == OP_GOTO;\n\n\t\t\t//the last statement of the if was a return, so we don't need the goto at the end\n\t\t\tif (lastwasreturn && opt_compound_jumps && patch1 && !QCC_AStatementJumpsTo(numstatements, patch1-statements, numstatements))\n\t\t\t{\n//\t\t\t\tQCC_PR_ParseWarning(0, \"optimised the else\");\n\t\t\t\toptres_compound_jumps++;\n\t\t\t\tif (patch1)\n\t\t\t\t\tpatch1->b.jumpofs = &statements[numstatements] - patch1;\n\t\t\t\toldnumst = numstatements;\n\t\t\t\toldlab = num_labels;\n\t\t\t\tQCC_PR_ParseStatement ();\n\t\t\t\tif (stripfalse && oldlab == num_labels)\n\t\t\t\t{\n\t\t\t\t\tpatch2 = NULL;\n\t\t\t\t\tQCC_UngenerateStatements(oldnumst);\n\n\t\t\t\t\tif (patch1)\n\t\t\t\t\t\tpatch1->b.jumpofs = &statements[numstatements] - patch1;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n//\t\t\t\tQCC_PR_ParseWarning(0, \"using the else\");\n\t\t\t\toldnumst = numstatements;\n\t\t\t\tif (striptruth)\n\t\t\t\t\tpatch2 = NULL;\n\t\t\t\telse\n\t\t\t\t\tpatch2 = QCC_Generate_OP_GOTO();\n\t\t\t\tif (patch1)\n\t\t\t\t\tpatch1->b.jumpofs = &statements[numstatements] - patch1;\n\n\t\t\t\toldlab = num_labels;\n\t\t\t\tQCC_PR_ParseStatement ();\n\t\t\t\tif (stripfalse && oldlab == num_labels)\n\t\t\t\t{\n\t\t\t\t\tpatch2 = NULL;\n\t\t\t\t\tQCC_UngenerateStatements(oldnumst);\n\n\t\t\t\t\tif (patch1)\n\t\t\t\t\t\tpatch1->b.jumpofs = &statements[numstatements] - patch1;\n\t\t\t\t}\n\n\t\t\t\tif (patch2)\n\t\t\t\t\tpatch2->a.jumpofs = &statements[numstatements] - patch2;\n\n/*FIXME: this doesn't work right\n\t\t\t\tif (patch1 && patch2)\n\t\t\t\t{\n\t\t\t\t\tif (QCC_PR_StatementBlocksMatch(patch1+1, patch2-(patch1+1), patch2+1, &statements[numstatements] - (patch2+1)))\n\t\t\t\t\t\tQCC_PR_ParseWarning(0, \"Two identical blocks each side of an else\");\n\t\t\t\t}\n*/\n\t\t\t}\n\t\t}\n\t\telse if (patch1)\n\t\t{\n\t\t\tif (patch1->op == OP_GOTO)\n\t\t\t\tpatch1->a.jumpofs = &statements[numstatements] - patch1;\n\t\t\telse\n\t\t\t\tpatch1->b.jumpofs = &statements[numstatements] - patch1;\n\t\t}\n\n\t\treturn;\n\t}\n\tif (QCC_PR_CheckKeyword(keyword_switch, \"switch\"))\n\t{\n\t\tint op;\n\t\tint hcstyle;\n\t\tint defaultcase = -1;\n\t\tint oldst;\n\t\tQCC_type_t *switchtype;\n\t\tstruct accessor_s *acc;\n\n\t\tbreaks = num_breaks;\n\t\tcases = num_cases;\n\n\n\t\tQCC_PR_Expect (\"(\");\n\n\t\tconditional = 1;\n\t\te = QCC_PR_Expression (TOP_PRIORITY, 0);\n\t\tconditional = 0;\n\t\te.sym->referenced = true;\n\n\t\t//expands\n\n\t\t//switch (CONDITION)\n\t\t//{\n\t\t//case 1:\n\t\t//\tbreak;\n\t\t//case 2:\n\t\t//default:\n\t\t//\tbreak;\n\t\t//}\n\n\t\t//to\n\n\t\t// x = CONDITION, goto start\n\t\t// l1:\n\t\t//\tgoto end\n\t\t// l2:\n\t\t// def:\n\t\t//\tgoto end\n\t\t//\tgoto end\t\t\tP1\n\t\t// start:\n\t\t//\tif (x == 1) goto l1;\n\t\t//\tif (x == 2) goto l2;\n\t\t//\tgoto def\n\t\t// end:\n\n\t\t//x is emitted in an opcode, stored as a register that we cannot access later.\n\t\t//it should be possible to nest these.\n\n\t\tswitchtype = e.cast;\n\t\tswitch(switchtype->type==ev_enum?switchtype->aux_type->type:switchtype->type)\n\t\t{\n\t\tcase ev_float:\n\t\t\top = OP_SWITCH_F;\n\t\t\tbreak;\n\t\tcase ev_entity:\t//whu???\n\t\t\top = OP_SWITCH_E;\n\t\t\tbreak;\n\t\tcase ev_vector:\n\t\t\top = OP_SWITCH_V;\n\t\t\tbreak;\n\t\tcase ev_string:\n\t\t\top = OP_SWITCH_S;\n\t\t\tbreak;\n\t\tcase ev_function:\n\t\t\top = OP_SWITCH_FNC;\n\t\t\tbreak;\n\t\tdefault:\t//err hmm.\n\t\t\top = 0;\n\t\t\tbreak;\n\t\t}\n\n\t\tif (op)\n\t\t\thcstyle = QCC_OPCodeValid(&pr_opcodes[op]);\n\t\telse\n\t\t\thcstyle = false;\n\n\t\tQCC_ClobberDef(NULL);\n\n\t\tif (hcstyle)\n\t\t\tQCC_FreeTemp(QCC_PR_StatementFlags (&pr_opcodes[op], e, nullsref, &patch1, STFL_DISCARDRESULT));\n\t\telse\n\t\t{\n\t\t\tpatch1 = QCC_Generate_OP_GOTO();\t//fixme: rearrange this, to avoid the goto\n\t\t\tQCC_FreeTemp(e);\n\t\t}\n\n\t\tQCC_PR_Expect (\")\");\t//close bracket is after we save the statement to mem (so debugger does not show the if statement as being on the line after\n\n\t\toldst = numstatements;\n\t\tQCC_PR_ParseStatement ();\n\n\t\t//this is so that a missing goto at the end of your switch doesn't end up in the jumptable again\n\t\tif (oldst == numstatements || !QCC_StatementIsAJump(numstatements-1, numstatements-1) || QCC_AStatementJumpsTo(numstatements, pr_scope->code, numstatements))\n\t\t{\n\t\t\tpatch2 = QCC_Generate_OP_GOTO();\t//the P1 statement/the theyforgotthebreak statement.\n//\t\t\tQCC_PR_ParseWarning(0, \"emitted goto\");\n\t\t}\n\t\telse\n\t\t{\n\t\t\tpatch2 = NULL;\n//\t\t\tQCC_PR_ParseWarning(0, \"No goto\");\n\t\t}\n\n\t\tif (hcstyle)\n\t\t\tpatch1->b.jumpofs = &statements[numstatements] - patch1;\t//the goto start part\n\t\telse\n\t\t\tpatch1->a.jumpofs = &statements[numstatements] - patch1;\t//the goto start part\n\n\t\toldst = numstatements;\n\n\t\tfor (acc = switchtype->accessors; acc; acc = acc->next)\n\t\t{\n\t\t\tconst QCC_eval_t *match = QCC_SRef_EvalConst(acc->staticval);\n\t\t\tif (!match)\n\t\t\t\tcontinue;\t//not an enum value, ignore it.\n\t\t\tfor (i = cases; i < num_cases; i++)\n\t\t\t{\n\t\t\t\tif (!pr_casesref[i].cast)\n\t\t\t\t\tbreak;\t//its a default\n\t\t\t\tif (pr_casesref2[i].cast)\n\t\t\t\t{\t//caserange\n\t\t\t\t\tbreak;\t//FIXME: too lazy to check these.\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\t//case\n\t\t\t\t\tif (pr_casesref[i].type == REF_GLOBAL && pr_casesref[i].cast == switchtype)\n\t\t\t\t\t{\n\t\t\t\t\t\tconst QCC_eval_t *eval = QCC_SRef_EvalConst(pr_casesref[i].base);\n\t\t\t\t\t\tif (!eval)\n\t\t\t\t\t\t\tbreak;\t//can't verify it\n\t\t\t\t\t\tif (!memcmp(eval, match, sizeof(*eval)*switchtype->size))\n\t\t\t\t\t\t\tbreak;\t//validated.\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\tbreak;\t//can't verify it\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (i == num_cases)\n\t\t\t{\n\t\t\t\tQCC_PR_ParseWarning(0, \"%s::%s not part of switch\", switchtype->name, acc->fieldname);\n\t\t\t}\n\t\t}\n\n\t\tQCC_ForceUnFreeDef(e.sym);\t//in the following code, e should still be live\n\t\tfor (i = cases; i < num_cases; i++)\n\t\t{\n\t\t\tif (!pr_casesref[i].cast)\n\t\t\t{\n\t\t\t\tif (defaultcase >= 0)\n\t\t\t\t\tQCC_PR_ParseError(ERR_MULTIPLEDEFAULTS, \"Duplicated default case\");\n\t\t\t\tdefaultcase = i;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tQCC_sref_t dmin, dmax;\n\t\t\t\tdmin = QCC_RefToDef(&pr_casesref[i], true);\n\t\t\t\tif (dmin.cast->type != e.cast->type)\n\t\t\t\t\tdmin = QCC_SupplyConversion(dmin, e.cast->type, true);\n\t\t\t\tif (pr_casesref2[i].cast)\n\t\t\t\t{\n\t\t\t\t\tdmax = QCC_RefToDef(&pr_casesref2[i], true);\n\t\t\t\t\tif (dmax.cast->type != e.cast->type)\n\t\t\t\t\t\tdmax = QCC_SupplyConversion(dmax, e.cast->type, true);\n\n\t\t\t\t\tif (hcstyle)\n\t\t\t\t\t{\n\t\t\t\t\t\tQCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_CASERANGE], dmin, dmax, &patch3));\n\t\t\t\t\t\tpatch3->c.jumpofs = &statements[pr_cases[i]] - patch3;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tQCC_sref_t e3;\n\n\t\t\t\t\t\tif (e.cast->type == ev_float)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\te2 = QCC_PR_StatementFlags (&pr_opcodes[OP_GE_F], e, dmin, NULL, STFL_PRESERVEA);\n\t\t\t\t\t\t\te3 = QCC_PR_StatementFlags (&pr_opcodes[OP_LE_F], e, dmax, NULL, STFL_PRESERVEA);\n\t\t\t\t\t\t\te2 = QCC_PR_Statement (&pr_opcodes[OP_AND_F], e2, e3, NULL);\n\t\t\t\t\t\t\tpatch3 = QCC_Generate_OP_IF(e2, false);\n\t\t\t\t\t\t\tpatch3->b.jumpofs = &statements[pr_cases[i]] - patch3;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (e.cast->type == ev_double)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\te2 = QCC_PR_StatementFlags (&pr_opcodes[OP_GE_D], e, dmin, NULL, STFL_PRESERVEA);\n\t\t\t\t\t\t\te3 = QCC_PR_StatementFlags (&pr_opcodes[OP_LE_D], e, dmax, NULL, STFL_PRESERVEA);\n\t\t\t\t\t\t\te2 = QCC_PR_Statement (&pr_opcodes[OP_AND_F], e2, e3, NULL);\n\t\t\t\t\t\t\tpatch3 = QCC_Generate_OP_IF(e2, false);\n\t\t\t\t\t\t\tpatch3->b.jumpofs = &statements[pr_cases[i]] - patch3;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (e.cast->type == ev_integer)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\te2 = QCC_PR_StatementFlags (&pr_opcodes[OP_GE_I], e, dmin, NULL, STFL_PRESERVEA);\n\t\t\t\t\t\t\te3 = QCC_PR_StatementFlags (&pr_opcodes[OP_LE_I], e, dmax, NULL, STFL_PRESERVEA);\n\t\t\t\t\t\t\te2 = QCC_PR_Statement (&pr_opcodes[OP_AND_I], e2, e3, NULL);\n\t\t\t\t\t\t\tpatch3 = QCC_Generate_OP_IF(e2, false);\n\t\t\t\t\t\t\tpatch3->b.jumpofs = &statements[pr_cases[i]] - patch3;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (e.cast->type == ev_uint)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\te2 = QCC_PR_StatementFlags (&pr_opcodes[OP_GE_U], e, dmin, NULL, STFL_PRESERVEA);\n\t\t\t\t\t\t\te3 = QCC_PR_StatementFlags (&pr_opcodes[OP_LE_U], e, dmax, NULL, STFL_PRESERVEA);\n\t\t\t\t\t\t\te2 = QCC_PR_Statement (&pr_opcodes[OP_AND_I], e2, e3, NULL);\n\t\t\t\t\t\t\tpatch3 = QCC_Generate_OP_IF(e2, false);\n\t\t\t\t\t\t\tpatch3->b.jumpofs = &statements[pr_cases[i]] - patch3;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (e.cast->type == ev_int64)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\te2 = QCC_PR_StatementFlags (&pr_opcodes[OP_GE_I64], e, dmin, NULL, STFL_PRESERVEA);\n\t\t\t\t\t\t\te3 = QCC_PR_StatementFlags (&pr_opcodes[OP_LE_I64], e, dmax, NULL, STFL_PRESERVEA);\n\t\t\t\t\t\t\te2 = QCC_PR_Statement (&pr_opcodes[OP_AND_I], e2, e3, NULL);\n\t\t\t\t\t\t\tpatch3 = QCC_Generate_OP_IF(e2, false);\n\t\t\t\t\t\t\tpatch3->b.jumpofs = &statements[pr_cases[i]] - patch3;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (e.cast->type == ev_uint64)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\te2 = QCC_PR_StatementFlags (&pr_opcodes[OP_GE_U64], e, dmin, NULL, STFL_PRESERVEA);\n\t\t\t\t\t\t\te3 = QCC_PR_StatementFlags (&pr_opcodes[OP_LE_U64], e, dmax, NULL, STFL_PRESERVEA);\n\t\t\t\t\t\t\te2 = QCC_PR_Statement (&pr_opcodes[OP_AND_I], e2, e3, NULL);\n\t\t\t\t\t\t\tpatch3 = QCC_Generate_OP_IF(e2, false);\n\t\t\t\t\t\t\tpatch3->b.jumpofs = &statements[pr_cases[i]] - patch3;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tQCC_PR_ParseWarning(WARN_SWITCHTYPEMISMATCH, \"switch caserange MUST be a float or integer\");\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tif (hcstyle)\n\t\t\t\t\t{\n\t\t\t\t\t\tQCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_CASE], dmin, nullsref, &patch3));\n\t\t\t\t\t\tpatch3->b.jumpofs = &statements[pr_cases[i]] - patch3;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tconst QCC_eval_t *eval = QCC_SRef_EvalConst(dmin);\n\t\t\t\t\t\tif (!eval || eval->_int)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tswitch(e.cast->type==ev_enum?e.cast->aux_type->type:e.cast->type)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase ev_float:\n\t\t\t\t\t\t\t\te2 = QCC_PR_StatementFlags (&pr_opcodes[OP_EQ_F], e, dmin, NULL, STFL_PRESERVEA);\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tcase ev_entity:\t//whu???\n\t\t\t\t\t\t\t\te2 = QCC_PR_StatementFlags (&pr_opcodes[OP_EQ_E], e, dmin, NULL, STFL_PRESERVEA);\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tcase ev_vector:\n\t\t\t\t\t\t\t\te2 = QCC_PR_StatementFlags (&pr_opcodes[OP_EQ_V], e, dmin, NULL, STFL_PRESERVEA);\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tcase ev_string:\n\t\t\t\t\t\t\t\te2 = QCC_PR_StatementFlags (&pr_opcodes[OP_EQ_S], e, dmin, NULL, STFL_PRESERVEA);\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tcase ev_function:\n\t\t\t\t\t\t\t\te2 = QCC_PR_StatementFlags (&pr_opcodes[OP_EQ_FNC], e, dmin, NULL, STFL_PRESERVEA);\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tcase ev_field:\n\t\t\t\t\t\t\t\te2 = QCC_PR_StatementFlags (&pr_opcodes[OP_EQ_FNC], e, dmin, NULL, STFL_PRESERVEA);\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tcase ev_pointer:\n\t\t\t\t\t\t\t\te2 = QCC_PR_StatementFlags (&pr_opcodes[OP_EQ_P], e, dmin, NULL, STFL_PRESERVEA);\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tcase ev_integer:\n\t\t\t\t\t\t\tcase ev_uint:\n\t\t\t\t\t\t\t\te2 = QCC_PR_StatementFlags (&pr_opcodes[OP_EQ_I], e, dmin, NULL, STFL_PRESERVEA);\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tcase ev_int64:\n\t\t\t\t\t\t\tcase ev_uint64:\n\t\t\t\t\t\t\t\te2 = QCC_PR_StatementFlags (&pr_opcodes[OP_EQ_I64], e, dmin, NULL, STFL_PRESERVEA);\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tcase ev_double:\n\t\t\t\t\t\t\t\te2 = QCC_PR_StatementFlags (&pr_opcodes[OP_EQ_D], e, dmin, NULL, STFL_PRESERVEA);\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\t\tQCC_PR_ParseError(ERR_BADSWITCHTYPE, \"Bad switch type\");\n\t\t\t\t\t\t\t\te2 = nullsref;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tQCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_IF_I], e2, nullsref, &patch3));\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tQCC_FreeTemp(dmin);\n\t\t\t\t\t\t\tQCC_UnFreeTemp(e);\n\t\t\t\t\t\t\tpatch3 = QCC_Generate_OP_IFNOT(e, false);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tpatch3->b.jumpofs = &statements[pr_cases[i]] - patch3;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tQCC_FreeTemp(e);\n\t\tif (defaultcase>=0)\n\t\t{\n\t\t\tpatch3 = QCC_Generate_OP_GOTO();\n\t\t\tpatch3->a.jumpofs = &statements[pr_cases[defaultcase]] - patch3;\n\t\t}\n\n\t\tnum_cases = cases;\n\n\n\t\tpatch3 = &statements[numstatements];\n\t\tif (patch2)\n\t\t\tpatch2->a.jumpofs = patch3 - patch2;\t//set P1 jump\n\n\t\tif (breaks != num_breaks)\n\t\t{\n\t\t\tfor(i = breaks; i < num_breaks; i++)\n\t\t\t{\n\t\t\t\tpatch2 = &statements[pr_breaks[i]];\n\t\t\t\tpatch2->a.jumpofs = patch3 - patch2;\n\t\t\t}\n\t\t\tnum_breaks = breaks;\n\t\t}\n\n\t\t//update the jumptable statements to hide as part of the switch itself.\n\t\twhile (oldst < numstatements)\n\t\t\tstatements[oldst++].linenum = patch1->linenum;\n\t\treturn;\n\t}\n\n\tif (QCC_PR_CheckKeyword(keyword_using, \"using\"))\n\t{\n\t\tQCC_PR_ParseStatement_Using();\n\t\treturn;\n\t}\n\n\tif (QCC_PR_CheckKeyword(keyword_asm, \"asm\"))\n\t{\n\t\tif (QCC_PR_CheckToken(\"{\"))\n\t\t{\n\t\t\twhile (!QCC_PR_CheckToken(\"}\"))\n\t\t\t\tQCC_PR_ParseAsm ();\n\t\t}\n\t\telse\n\t\t\tQCC_PR_ParseAsm ();\n\t\treturn;\n\t}\n\n\t//frikqcc-style labels\n\tif (QCC_PR_CheckToken(\":\"))\n\t{\n\t\tif (pr_token_type != tt_name)\n\t\t{\n\t\t\tQCC_PR_ParseError(ERR_BADLABELNAME, \"invalid label name \\\"%s\\\"\", pr_token);\n\t\t\treturn;\n\t\t}\n\n\t\tfor (i = 0; i < num_labels; i++)\n\t\t\tif (!STRNCMP(pr_labels[i].name, pr_token, sizeof(pr_labels[num_labels].name) -1))\n\t\t\t{\n\t\t\t\tQCC_PR_ParseWarning(WARN_DUPLICATELABEL, \"Duplicate label %s\", pr_token);\n\t\t\t\tQCC_PR_Lex();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\tif (num_labels >= max_labels)\n\t\t{\n\t\t\tmax_labels += 8;\n\t\t\tpr_labels = realloc(pr_labels, sizeof(*pr_labels)*max_labels);\n\t\t}\n\n\t\tQC_strlcpy(pr_labels[num_labels].name, pr_token, sizeof(pr_labels[num_labels].name));\n\t\tpr_labels[num_labels].lineno = pr_source_line;\n\t\tpr_labels[num_labels].statementno = numstatements;\n\n\t\tnum_labels++;\n\n//\t\tQCC_PR_ParseWarning(\"Gotos are evil\");\n\t\tQCC_PR_Lex();\n\t\treturn;\n\t}\n\tif (QCC_PR_CheckKeyword(keyword_goto, \"goto\"))\n\t{\n\t\tif (pr_token_type != tt_name)\n\t\t{\n\t\t\tQCC_PR_ParseError(ERR_NOLABEL, \"invalid label name \\\"%s\\\"\", pr_token);\n\t\t\treturn;\n\t\t}\n\n\t\tpatch2 = QCC_Generate_OP_GOTO();\n\n\t\tQCC_PR_GotoStatement (patch2, pr_token);\n\n//\t\tQCC_PR_ParseWarning(\"Gotos are evil\");\n\t\tQCC_PR_Lex();\n\t\tQCC_PR_Expect(\";\");\n\t\treturn;\n\t}\n\n\tif (QCC_PR_CheckKeyword(keyword_break, \"break\"))\n\t{\n\t\tif (!STRCMP (\"(\", pr_token))\n\t\t{\t//make sure it wasn't a call to the break function.\n\t\t\tQCC_PR_IncludeChunk(\"break(\", true, NULL);\n\t\t\tQCC_PR_Lex();\t//so it sees the break.\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (num_breaks >= max_breaks)\n\t\t\t{\n\t\t\t\tmax_breaks += 8;\n\t\t\t\tpr_breaks = realloc(pr_breaks, sizeof(*pr_breaks)*max_breaks);\n\t\t\t}\n\t\t\tpr_breaks[num_breaks] = numstatements;\n\t\t\tQCC_PR_Statement (&pr_opcodes[OP_GOTO], nullsref, nullsref, NULL);\n\t\t\tnum_breaks++;\n\t\t\tQCC_PR_Expect(\";\");\n\t\t\treturn;\n\t\t}\n\t}\n\tif (QCC_PR_CheckKeyword(keyword_continue, \"continue\"))\n\t{\n\t\tif (num_continues >= max_continues)\n\t\t{\n\t\t\tmax_continues += 8;\n\t\t\tpr_continues = realloc(pr_continues, sizeof(*pr_continues)*max_continues);\n\t\t}\n\t\tpr_continues[num_continues] = numstatements;\n\t\tQCC_PR_Statement (&pr_opcodes[OP_GOTO], nullsref, nullsref, NULL);\n\t\tnum_continues++;\n\t\tQCC_PR_Expect(\";\");\n\t\treturn;\n\t}\n\tif (QCC_PR_CheckKeyword(keyword_case, \"case\"))\n\t{\n\t\tif (num_cases >= max_cases)\n\t\t{\n\t\t\tmax_cases += 8;\n\t\t\tpr_cases = realloc(pr_cases, sizeof(*pr_cases)*max_cases);\n\t\t\tpr_casesref = realloc(pr_casesref, sizeof(*pr_casesref)*max_cases);\n\t\t\tpr_casesref2 = realloc(pr_casesref2, sizeof(*pr_casesref2)*max_cases);\n\t\t}\n\t\tpr_cases[num_cases] = numstatements;\n\t\tQCC_PR_RefExpression(&pr_casesref[num_cases], TOP_PRIORITY, EXPR_DISALLOW_COMMA);\n\t\tif (QCC_PR_CheckToken(\"..\"))\n\t\t{\n\t\t\t//const QCC_eval_t *evalmin, *evalmax;\n\t\t\tQCC_PR_RefExpression (&pr_casesref2[num_cases], TOP_PRIORITY, EXPR_DISALLOW_COMMA);\n\t\t\t//pr_casesref2[num_cases] = QCC_SupplyConversion(pr_casesdef2[num_cases], pr_casesdef[num_cases].cast->type, true);\n\n\t\t\t/*evalmin = QCC_Ref_EvalConst(pr_casesref[num_cases]);\n\t\t\tevalmax = QCC_Ref_EvalConst(pr_casesref2[num_cases]);\n\t\t\tif (evalmin && evalmax)\n\t\t\t{\n\t\t\t\tif ((pr_casesref[num_cases].cast->type == ev_integer && evalmin->_int >= evalmax->_int) ||\n\t\t\t\t\t(pr_casesref[num_cases].cast->type == ev_float && evalmin->_float >= evalmax->_float))\n\t\t\t\t\tQCC_PR_ParseError(ERR_CASENOTIMMEDIATE, \"Caserange statement uses backwards range\\n\");\n\t\t\t}*/\n\t\t}\n\t\telse\n\t\t\tpr_casesref2[num_cases].cast = NULL;\n\n\t\tif (numstatements != pr_cases[num_cases])\n\t\t\tQCC_PR_ParseError(ERR_CASENOTIMMEDIATE, \"Case statements may not use formulas\\n\");\t//fixme: insert them...\n\t\tnum_cases++;\n\t\tQCC_PR_Expect(\":\");\n\t\treturn;\n\t}\n\tif (QCC_PR_CheckKeyword(keyword_default, \"default\"))\n\t{\n\t\tif (num_cases >= max_cases)\n\t\t{\n\t\t\tmax_cases += 8;\n\t\t\tpr_cases = realloc(pr_cases, sizeof(*pr_cases)*max_cases);\n\t\t\tpr_casesref = realloc(pr_casesref, sizeof(*pr_casesref)*max_cases);\n\t\t\tpr_casesref2 = realloc(pr_casesref2, sizeof(*pr_casesref2)*max_cases);\n\t\t}\n\t\tpr_cases[num_cases] = numstatements;\n\t\tpr_casesref[num_cases].cast = NULL;\n\t\tpr_casesref2[num_cases].cast = NULL;\n\t\tnum_cases++;\n\t\tQCC_PR_Expect(\":\");\n\t\treturn;\n\t}\n\n\tif (QCC_PR_CheckKeyword(keyword_thinktime, \"thinktime\"))\n\t{\n\t\tQCC_sref_t nextthink;\n\t\tQCC_sref_t time;\n\t\te = QCC_PR_Expression (TOP_PRIORITY, 0);\n\t\tQCC_PR_Expect(\":\");\n\t\te2 = QCC_PR_Expression (TOP_PRIORITY, 0);\n\t\te2 = QCC_SupplyConversion(e2, ev_float, true);\n\t\tif (e.cast->type != ev_entity || e2.cast->type != ev_float)\n\t\t\tQCC_PR_ParseError(ERR_THINKTIMETYPEMISMATCH, \"thinktime type mismatch\");\n\n\t\tif (QCC_OPCodeValid(&pr_opcodes[OP_THINKTIME]))\n\t\t\tQCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_THINKTIME], e, e2, NULL));\n\t\telse\n\t\t{\n\t\t\tnextthink = QCC_PR_GetSRef(NULL, \"nextthink\", NULL, false, 0, false);\n\t\t\tif (!nextthink.cast)\n\t\t\t\tQCC_PR_ParseError (ERR_UNKNOWNVALUE, \"Unknown value \\\"%s\\\"\", \"nextthink\");\n\t\t\ttime = QCC_PR_GetSRef(type_float, \"time\", NULL, false, 0, false);\n\t\t\tif (!time.cast)\n\t\t\t\tQCC_PR_ParseError (ERR_UNKNOWNVALUE, \"Unknown value \\\"%s\\\"\", \"time\");\n\t\t\tnextthink = QCC_PR_Statement(&pr_opcodes[OP_ADDRESS], e, nextthink, NULL);\n\t\t\ttime = QCC_PR_Statement(&pr_opcodes[OP_ADD_F], time, e2, NULL);\n\t\t\tQCC_FreeTemp(QCC_PR_Statement(&pr_opcodes[OP_STOREP_F], time, nextthink, NULL));\n\t\t}\n\t\tQCC_PR_Expect(\";\");\n\t\treturn;\n\t}\n\tif (QCC_PR_CheckToken(\";\"))\n\t{\n\t\tint osl = pr_source_line;\n\t\tpr_source_line = statementstart;\n\t\tif (!expandedemptymacro)\n\t\t{\n\t\t\tif (!currentchunk || !currentchunk->cnst)\n\t\t\t\tQCC_PR_ParseWarning(WARN_POINTLESSSTATEMENT, \"Hanging ';'\");\n\t\t\twhile (QCC_PR_CheckToken(\";\"))\n\t\t\t\t;\n\t\t}\n\t\tpr_source_line = osl;\n\t\treturn;\n\t}\n\n\t//C-style labels.\n\tif (pr_token_type == tt_name && pr_file_p[0] == ':' && pr_file_p[1] != ':')\n\t{\n\t\tif (pr_token_type != tt_name)\n\t\t{\n\t\t\tQCC_PR_ParseError(ERR_BADLABELNAME, \"invalid label name \\\"%s\\\"\", pr_token);\n\t\t\treturn;\n\t\t}\n\n\t\tfor (i = 0; i < num_labels; i++)\n\t\t\tif (!STRNCMP(pr_labels[i].name, pr_token, sizeof(pr_labels[num_labels].name) -1))\n\t\t\t{\n\t\t\t\tQCC_PR_ParseWarning(WARN_DUPLICATELABEL, \"Duplicate label %s\", pr_token);\n\t\t\t\tQCC_PR_Lex();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\tif (num_labels >= max_labels)\n\t\t{\n\t\t\tmax_labels += 8;\n\t\t\tpr_labels = realloc(pr_labels, sizeof(*pr_labels)*max_labels);\n\t\t}\n\n\t\tQC_strlcpy(pr_labels[num_labels].name, pr_token, sizeof(pr_labels[num_labels].name));\n\t\tpr_labels[num_labels].lineno = pr_source_line;\n\t\tpr_labels[num_labels].statementno = numstatements;\n\n\t\tnum_labels++;\n\n//\t\tQCC_PR_ParseWarning(\"Gotos are evil\");\n\t\tQCC_PR_Lex();\n\t\tQCC_PR_Expect(\":\");\n\t\treturn;\n\t}\n\n//\tqcc_functioncalled=0;\n\n\tQCC_PR_DiscardExpression (TOP_PRIORITY, 0);\n\texpandedemptymacro = false;\n\tQCC_PR_Expect (\";\");\n\n//\tqcc_functioncalled=false;\n}\n\n\n/*\n==============\nPR_ParseState\n\nStates are special functions made for convenience.  They automatically\nset frame, nextthink (implicitly), and think (allowing forward definitions).\n\n// void() name = [framenum, nextthink] {code}\n// expands to:\n// function void name ()\n// {\n//\t\tself.frame=framenum;\n//\t\tself.nextthink = time + 0.1;\n//\t\tself.think = nextthink\n//\t\t<code>\n// };\n==============\n*/\nvoid QCC_PR_ParseState (void)\n{\n\tQCC_sref_t\ts1, def;\n\n\tpbool isinc;\n\n\t//FIXME: this is ambiguous with pre-inc and post-inc logic.\n\tif ((isinc=QCC_PR_CheckToken(\"++\")) || QCC_PR_CheckToken(\"--\"))\n\t{\n\t\tconst QCC_eval_t *first, *last;\n\t\tint dir = 0;\n\t\tint op = OP_CSTATE;\n\t\tif (QCC_PR_CheckToken(\"(\"))\n\t\t{\n\t\t\top = OP_CWSTATE;\n\t\t\tif (!QCC_PR_CheckToken(\"w\"))\n\t\t\t\tQCC_PR_Expect(\"W\");\n\t\t\tQCC_PR_Expect(\")\");\n\t\t}\n\n//\t\ts1 = QCC_PR_ParseImmediate ();\n\n\t\ts1 = QCC_PR_Expression (TOP_PRIORITY, EXPR_DISALLOW_COMMA);\n\t\ts1 = QCC_SupplyConversion(s1, ev_float, true);\n\n\t\tQCC_PR_Expect(\"..\");\n//\t\tdef = QCC_PR_ParseImmediate ();\n\t\tdef = QCC_PR_Expression (TOP_PRIORITY, EXPR_DISALLOW_COMMA);\n\t\tdef = QCC_SupplyConversion(def, ev_float, true);\n\t\tQCC_PR_Expect (\"]\");\n\n\t\tif (s1.cast->type != ev_float || def.cast->type != ev_float)\n\t\t\tQCC_PR_ParseError(ERR_STATETYPEMISMATCH, \"state type mismatch\");\n\n\t\tfirst = QCC_SRef_EvalConst(s1);\n\t\tlast = QCC_SRef_EvalConst(def);\n\t\tif (first&&last)\n\t\t{\t//whether its a ++ or -- doesn't really matter, but hcc generates an error so we should at least generate a warning.\n\t\t\tdir = (last->_float >= first->_float)?1:-1;\n\t\t\tif (isinc)\n\t\t\t{\n\t\t\t\tif (first->_float > last->_float)\n\t\t\t\t\tQCC_PR_ParseWarning(ERR_STATETYPEMISMATCH, \"Forwards State Cycle with backwards range\");\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (first->_float < last->_float)\n\t\t\t\t\tQCC_PR_ParseWarning(ERR_STATETYPEMISMATCH, \"Forwards State Cycle with backwards range\");\n\t\t\t}\n\t\t}\n\n\n\t\tif (QCC_OPCodeValid(&pr_opcodes[op]))\n\t\t\tQCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[op], s1, def, NULL));\n\t\telse\n\t\t{\n\t\t\tQCC_statement_t *patch1, *entercycf, *entercycb, *fwd, *back;\n\t\t\tQCC_sref_t t1, t2;\n\t\t\tQCC_sref_t framef, frame;\n\t\t\tQCC_sref_t self;\n\t\t\tQCC_sref_t cycle_wrapped;\n\n\t\t\tself = QCC_PR_GetSRef(type_entity, \"self\", NULL, false, 0, false);\n\t\t\tframef = QCC_PR_GetSRef(NULL, (op==OP_CWSTATE)?\"weaponframe\":\"frame\", NULL, false, 0, false);\n\t\t\tcycle_wrapped = QCC_PR_GetSRef(type_float, \"cycle_wrapped\", NULL, false, 0, false);\n\n\t\t\tframe = QCC_PR_StatementFlags(&pr_opcodes[OP_LOAD_F], self, framef, NULL, 0);\n\t\t\tif (cycle_wrapped.cast)\n\t\t\t\tQCC_FreeTemp(QCC_PR_StatementFlags(&pr_opcodes[OP_STORE_F], QCC_MakeFloatConst(0), cycle_wrapped, NULL, STFL_PRESERVEB));\n\n\t\t\tif (dir)\n\t\t\t\tfwd = NULL;\t//can skip the checks\n\t\t\telse\n\t\t\t{\n\t\t\t\tt1 = QCC_PR_StatementFlags(&pr_opcodes[OP_GE_F], def, s1, NULL, STFL_PRESERVEA|STFL_PRESERVEB);\n\t\t\t\tfwd = QCC_Generate_OP_IFNOT(t1, false);\n\t\t\t}\n\t\t\tif (dir >= 0)\n\t\t\t{\t//this block is the 'it's in a forwards direction'\n\t\t\t\t//make sure the frame is within the bounds given.\n\t\t\t\tt1 = QCC_PR_StatementFlags(&pr_opcodes[OP_LT_F], frame, s1, NULL, STFL_PRESERVEA|STFL_PRESERVEB);\n\t\t\t\tt2 = QCC_PR_StatementFlags(&pr_opcodes[OP_GT_F], frame, def, NULL, STFL_PRESERVEA|STFL_PRESERVEB);\n\t\t\t\tt1 = QCC_PR_Statement(&pr_opcodes[OP_OR_F], t1, t2, NULL);\n\t\t\t\tpatch1 = QCC_Generate_OP_IFNOT(t1, false);\n\t\t\t\t{\n\t\t\t\t\tQCC_FreeTemp(QCC_PR_StatementFlags(&pr_opcodes[OP_STORE_F], s1, frame, NULL, STFL_PRESERVEA|STFL_PRESERVEB));\n\t\t\t\t\tentercycf = QCC_Generate_OP_GOTO();\n\t\t\t\t}\n\t\t\t\tpatch1->b.jumpofs = &statements[numstatements] - patch1;\n\n\t\t\t\tQCC_PR_SimpleStatement(&pr_opcodes[OP_ADD_F], frame, QCC_MakeFloatConst(1), frame, false);\n\t\t\t\tt1 = QCC_PR_StatementFlags(&pr_opcodes[OP_GT_F], frame, def, NULL, STFL_PRESERVEA|STFL_PRESERVEB);\n\t\t\t\tpatch1 = QCC_Generate_OP_IFNOT(t1, false);\n\t\t\t\t{\n\t\t\t\t\tQCC_FreeTemp(QCC_PR_StatementFlags(&pr_opcodes[OP_STORE_F], s1, frame, NULL, STFL_PRESERVEA|STFL_PRESERVEB));\n\t\t\t\t\tif (cycle_wrapped.cast)\n\t\t\t\t\t\tQCC_FreeTemp(QCC_PR_StatementFlags(&pr_opcodes[OP_STORE_F], QCC_MakeFloatConst(1), cycle_wrapped, NULL, STFL_PRESERVEB));\n\t\t\t\t}\n\t\t\t\tpatch1->b.jumpofs = &statements[numstatements] - patch1;\n\t\t\t}\n\t\t\telse entercycf = NULL;\n\t\t\tif (fwd)\n\t\t\t{\n\t\t\t\tback = QCC_Generate_OP_GOTO();\n\t\t\t\tfwd->b.jumpofs = &statements[numstatements] - fwd;\n\t\t\t}\n\t\t\telse\n\t\t\t\tback = NULL;\n\t\t\tif (dir <= 0)\n\t\t\t{\t//reverse animation.\n\n\t\t\t\t//make sure the frame is within the bounds given.\n\t\t\t\tt1 = QCC_PR_StatementFlags(&pr_opcodes[OP_GT_F], frame, s1, NULL, STFL_PRESERVEA|STFL_PRESERVEB);\n\t\t\t\tt2 = QCC_PR_StatementFlags(&pr_opcodes[OP_LT_F], frame, def, NULL, STFL_PRESERVEA|STFL_PRESERVEB);\n\t\t\t\tt1 = QCC_PR_Statement(&pr_opcodes[OP_OR_F], t1, t2, NULL);\n\t\t\t\tpatch1 = QCC_Generate_OP_IFNOT(t1, false);\n\t\t\t\t{\n\t\t\t\t\tQCC_FreeTemp(QCC_PR_StatementFlags(&pr_opcodes[OP_STORE_F], s1, frame, NULL, STFL_PRESERVEA|STFL_PRESERVEB));\n\t\t\t\t\tentercycb = QCC_Generate_OP_GOTO();\n\t\t\t\t}\n\t\t\t\tpatch1->b.jumpofs = &statements[numstatements] - patch1;\n\n\t\t\t\tQCC_PR_SimpleStatement(&pr_opcodes[OP_SUB_F], frame, QCC_MakeFloatConst(1), frame, false);\n\t\t\t\tt1 = QCC_PR_StatementFlags(&pr_opcodes[OP_LT_F], frame, def, NULL, STFL_PRESERVEA);\n\t\t\t\tpatch1 = QCC_Generate_OP_IFNOT(t1, false);\n\t\t\t\t{\n\t\t\t\t\tQCC_FreeTemp(QCC_PR_StatementFlags(&pr_opcodes[OP_STORE_F], s1, frame, NULL, STFL_PRESERVEB));\n\t\t\t\t\tif (cycle_wrapped.cast)\n\t\t\t\t\t\tQCC_FreeTemp(QCC_PR_StatementFlags(&pr_opcodes[OP_STORE_F], QCC_MakeFloatConst(1), cycle_wrapped, NULL, 0));\n\t\t\t\t}\n\t\t\t\tpatch1->b.jumpofs = &statements[numstatements] - patch1;\n\t\t\t}\n\t\t\telse entercycb = NULL;\n\n\t\t\tif (back)\n\t\t\t\tback->a.jumpofs = &statements[numstatements] - back;\n\n\t\t\tif (entercycf)\n\t\t\t\t/*out of range*/entercycf->a.jumpofs = &statements[numstatements] - entercycf;\n\t\t\tif (entercycb)\n\t\t\t\t/*out of range*/entercycb->a.jumpofs = &statements[numstatements] - entercycb;\n\t\t\t//self.frame = frame happens with the normal state opcode.\n\t\t\tQCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[(op==OP_CWSTATE)?OP_WSTATE:OP_STATE], frame, QCC_MakeSRef(pr_scope->def, 0, pr_scope->type), NULL));\n\t\t}\n\t\treturn;\n\t}\n\n\ts1 = QCC_PR_Expression (TOP_PRIORITY, EXPR_DISALLOW_COMMA);\n\ts1 = QCC_SupplyConversion(s1, ev_float, true);\n\n\tif (!QCC_PR_CheckToken (\",\"))\n\t\tQCC_PR_ParseWarning(WARN_UNEXPECTEDPUNCT, \"missing comma in state definition\");\n\n\tpr_assumetermtype = type_function;\n\tpr_assumetermscope = pr_scope->parentscope;\n\tpr_assumetermflags = GDF_CONST | (pr_assumetermscope?GDF_STATIC:0);\n\tdef = QCC_PR_Expression (TOP_PRIORITY, EXPR_DISALLOW_COMMA);\n\tif (typecmp(def.cast, type_function))\n\t{\n\t\tif (!QCC_SRef_IsNull(def))\n\t\t{\n\t\t\tchar typebuf1[256];\n\t\t\tchar typebuf2[256];\n\t\t\tQCC_PR_ParseErrorPrintSRef (ERR_TYPEMISMATCH, def, \"Type mismatch: %s, should be %s\", TypeName(def.cast, typebuf1, sizeof(typebuf1)), TypeName(type_function, typebuf2, sizeof(typebuf2)));\n\t\t}\n\t}\n\tpr_assumetermtype = NULL;\n\n\tQCC_PR_Expect (\"]\");\n\n\tQCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_STATE], s1, def, NULL));\n}\n\nvoid QCC_PR_ParseAsm(void)\n{\n\tQCC_statement_t *patch1;\n\tint op, p;\n\tQCC_sref_t a, b, c;\n\n\tif (QCC_PR_CheckKeyword(keyword_local, \"local\"))\n\t{\n\t\tQCC_PR_ParseDefs (NULL, true);\n\t\treturn;\n\t}\n\n\tfor (op = 0; op < OP_NUMOPS; op++)\n\t{\n\t\tif (!STRCMP(pr_token, pr_opcodes[op].opname))\n\t\t{\n\t\t\tQCC_PR_Lex();\n\t\t\tif (/*pr_opcodes[op].priority==-1 &&*/ pr_opcodes[op].associative!=ASSOC_LEFT)\n\t\t\t{\n\t\t\t\tif (pr_opcodes[op].type_a==NULL)\n\t\t\t\t{\n\t\t\t\t\tpatch1 = QCC_PR_SimpleStatement(&pr_opcodes[op], nullsref, nullsref, nullsref, true);\n\n\t\t\t\t\tif (pr_token_type == tt_name)\n\t\t\t\t\t{\n\t\t\t\t\t\tQCC_PR_GotoStatement(patch1, QCC_PR_ParseName());\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tp = (int)pr_immediate._float;\n\t\t\t\t\t\tpatch1->a.ofs = p;\n\t\t\t\t\t}\n\n\t\t\t\t\tQCC_PR_Lex();\n\t\t\t\t}\n\t\t\t\telse if (pr_opcodes[op].type_b==NULL)\n\t\t\t\t{\n\t\t\t\t\ta = QCC_PR_ParseValue(pr_classtype, false, false, true);\n\t\t\t\t\tpatch1 = QCC_PR_SimpleStatement(&pr_opcodes[op], a, nullsref, nullsref, true);\n\n\t\t\t\t\tif (pr_token_type == tt_name)\n\t\t\t\t\t{\n\t\t\t\t\t\tQCC_PR_GotoStatement(patch1, QCC_PR_ParseName());\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tp = (int)pr_immediate._float;\n\t\t\t\t\t\tpatch1->b.ofs = (int)p;\n\t\t\t\t\t}\n\n\t\t\t\t\tQCC_PR_Lex();\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tif (QCC_PR_CheckName(\"void\"))\n\t\t\t\t\t\ta = nullsref;\n\t\t\t\t\telse\n\t\t\t\t\t\ta = QCC_PR_ParseValue(pr_classtype, false, false, true);\n\t\t\t\t\tQCC_PR_Expect(\",\");\n\t\t\t\t\tif (QCC_PR_CheckName(\"void\"))\n\t\t\t\t\t\tb = nullsref;\n\t\t\t\t\telse\n\t\t\t\t\t\tb = QCC_PR_ParseValue(pr_classtype, false, false, true);\n\t\t\t\t\tpatch1 = QCC_PR_SimpleStatement(&pr_opcodes[op], a, b, nullsref, true);\n\n\t\t\t\t\tif (pr_token_type == tt_name)\n\t\t\t\t\t{\n\t\t\t\t\t\tQCC_PR_GotoStatement(patch1, QCC_PR_ParseName());\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tp = (int)pr_immediate._float;\n\t\t\t\t\t\tpatch1->c.ofs = p;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (pr_opcodes[op].type_a != &type_void)\n\t\t\t\t{\n\t\t\t\t\tif (QCC_PR_CheckName(\"void\"))\n\t\t\t\t\t\ta = nullsref;\n\t\t\t\t\telse\n\t\t\t\t\t\ta = QCC_PR_ParseValue(pr_classtype, false, false, true);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\ta=nullsref;\n\t\t\t\tif (pr_opcodes[op].type_b != &type_void)\n\t\t\t\t{\n\t\t\t\t\tQCC_PR_CheckToken(\",\");\n\t\t\t\t\tif (QCC_PR_CheckName(\"void\"))\n\t\t\t\t\t\tb = nullsref;\n\t\t\t\t\telse\n\t\t\t\t\t\tb = QCC_PR_ParseValue(pr_classtype, false, false, true);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tb=nullsref;\n\t\t\t\tif (pr_opcodes[op].associative==ASSOC_LEFT && pr_opcodes[op].type_c != &type_void)\n\t\t\t\t{\n\t\t\t\t\tQCC_PR_CheckToken(\",\");\n\t\t\t\t\tif (QCC_PR_CheckName(\"void\"))\n\t\t\t\t\t\tc = nullsref;\n\t\t\t\t\telse\n\t\t\t\t\t\tc = QCC_PR_ParseValue(pr_classtype, false, false, true);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tc=nullsref;\n\n\t\t\t\tQCC_PR_SimpleStatement(&pr_opcodes[op], a, b, c, true);\n\t\t\t}\n\n\t\t\tQCC_PR_Expect(\";\");\n\t\t\treturn;\n\t\t}\n\t}\n\tQCC_PR_ParseError(ERR_BADOPCODE, \"Bad op code name %s\", pr_token);\n}\n\nstatic pbool QCC_FuncJumpsTo(int first, int last, int statement)\n{\n\tint st;\n\tfor (st = first; st < last; st++)\n\t{\n\t\tif (pr_opcodes[statements[st].op].type_a == NULL)\n\t\t{\n\t\t\tif (st + statements[st].a.jumpofs == statement)\n\t\t\t{\n\t\t\t\tif (st != first)\n\t\t\t\t{\n\t\t\t\t\tif (statements[st-1].op == OP_RETURN)\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\tif (statements[st-1].op == OP_DONE)\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (pr_opcodes[statements[st].op].type_b == NULL)\n\t\t{\n\t\t\tif (st + statements[st].b.jumpofs == statement)\n\t\t\t{\n\t\t\t\tif (st != first)\n\t\t\t\t{\n\t\t\t\t\tif (statements[st-1].op == OP_RETURN)\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\tif (statements[st-1].op == OP_DONE)\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (pr_opcodes[statements[st].op].type_c == NULL)\n\t\t{\n\t\t\tif (st + statements[st].c.jumpofs == statement)\n\t\t\t{\n\t\t\t\tif (st != first)\n\t\t\t\t{\n\t\t\t\t\tif (statements[st-1].op == OP_RETURN)\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\tif (statements[st-1].op == OP_DONE)\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn false;\n}\n/*\nstatic pbool QCC_FuncJumpsToRange(int first, int last, int firstr, int lastr)\n{\n\tint st;\n\tfor (st = first; st < last; st++)\n\t{\n\t\tif (pr_opcodes[statements[st].op].type_a == NULL)\n\t\t{\n\t\t\tif (st + (signed)statements[st].a >= firstr && st + (signed)statements[st].a <= lastr)\n\t\t\t{\n\t\t\t\tif (st != first)\n\t\t\t\t{\n\t\t\t\t\tif (statements[st-1].op == OP_RETURN)\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\tif (statements[st-1].op == OP_DONE)\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (pr_opcodes[statements[st].op].type_b == NULL)\n\t\t{\n\t\t\tif (st + (signed)statements[st].b >= firstr && st + (signed)statements[st].b <= lastr)\n\t\t\t{\n\t\t\t\tif (st != first)\n\t\t\t\t{\n\t\t\t\t\tif (statements[st-1].op == OP_RETURN)\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\tif (statements[st-1].op == OP_DONE)\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (pr_opcodes[statements[st].op].type_c == NULL)\n\t\t{\n\t\t\tif (st + (signed)statements[st].c >= firstr && st + (signed)statements[st].c <= lastr)\n\t\t\t{\n\t\t\t\tif (st != first)\n\t\t\t\t{\n\t\t\t\t\tif (statements[st-1].op == OP_RETURN)\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\tif (statements[st-1].op == OP_DONE)\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn false;\n}\n*/\n#if 0\nvoid QCC_CompoundJumps(int first, int last)\n{\n\t//jumps to jumps are reordered so they become jumps to the final target.\n\tint statement;\n\tint st;\n\tfor (st = first; st < last; st++)\n\t{\n\t\tif (pr_opcodes[statements[st].op].type_a == NULL)\n\t\t{\n\t\t\tstatement = st + (signed)statements[st].a;\n\t\t\tif (statements[statement].op == OP_RETURN || statements[statement].op == OP_DONE)\n\t\t\t{\t//goto leads to return. Copy the command out to remove the goto.\n\t\t\t\tstatements[st].op = statements[statement].op;\n\t\t\t\tstatements[st].a = statements[statement].a;\n\t\t\t\tstatements[st].b = statements[statement].b;\n\t\t\t\tstatements[st].c = statements[statement].c;\n\t\t\t\toptres_compound_jumps++;\n\t\t\t}\n\t\t\twhile (statements[statement].op == OP_GOTO)\n\t\t\t{\n\t\t\t\tstatements[st].a = statement+statements[statement].a - st;\n\t\t\t\tstatement = st + (signed)statements[st].a;\n\t\t\t\toptres_compound_jumps++;\n\t\t\t}\n\t\t}\n\t\tif (pr_opcodes[statements[st].op].type_b == NULL)\n\t\t{\n\t\t\tstatement = st + (signed)statements[st].b;\n\t\t\twhile (statements[statement].op == OP_GOTO)\n\t\t\t{\n\t\t\t\tstatements[st].b = statement+statements[statement].a - st;\n\t\t\t\tstatement = st + (signed)statements[st].b;\n\t\t\t\toptres_compound_jumps++;\n\t\t\t}\n\t\t}\n\t\tif (pr_opcodes[statements[st].op].type_c == NULL)\n\t\t{\n\t\t\tstatement = st + (signed)statements[st].c;\n\t\t\twhile (statements[statement].op == OP_GOTO)\n\t\t\t{\n\t\t\t\tstatements[st].c = statement+statements[statement].a - st;\n\t\t\t\tstatement = st + (signed)statements[st].c;\n\t\t\t\toptres_compound_jumps++;\n\t\t\t}\n\t\t}\n\t}\n}\n#else\nstatic void QCC_CompoundJumps(int first, int last)\n{\n\t//jumps to jumps are reordered so they become jumps to the final target.\n\tint statement;\n\tint st;\n\tint infloop;\n\tfor (st = first; st < last; st++)\n\t{\n\t\tif (pr_opcodes[statements[st].op].type_a == NULL)\n\t\t{\n\t\t\tstatement = st + statements[st].a.jumpofs;\n\t\t\tif (statements[statement].op == OP_RETURN || statements[statement].op == OP_DONE)\n\t\t\t{\t//goto leads to return. Copy the command out to remove the goto.\n\t\t\t\tstatements[st] = statements[statement];\n\t\t\t\toptres_compound_jumps++;\n\t\t\t}\n\t\t\tinfloop = 1000;\n\t\t\twhile (statements[statement].op == OP_GOTO)\n\t\t\t{\n\t\t\t\tif (!infloop--)\n\t\t\t\t{\n\t\t\t\t\tQCC_PR_ParseWarning(0, \"Infinate loop detected\");\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tstatements[st].a.ofs = (statement+statements[statement].a.ofs - st);\n\t\t\t\tstatement = st + statements[st].a.jumpofs;\n\t\t\t\toptres_compound_jumps++;\n\t\t\t}\n\t\t}\n\t\tif (pr_opcodes[statements[st].op].type_b == NULL)\n\t\t{\n\t\t\tstatement = st + statements[st].b.jumpofs;\n\t\t\tinfloop = 1000;\n\t\t\twhile (statements[statement].op == OP_GOTO)\n\t\t\t{\n\t\t\t\tif (!infloop--)\n\t\t\t\t{\n\t\t\t\t\tQCC_PR_ParseWarning(0, \"Infinate loop detected\");\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tstatements[st].b.ofs = (statement+statements[statement].a.ofs - st);\n\t\t\t\tstatement = st + statements[st].b.jumpofs;\n\t\t\t\toptres_compound_jumps++;\n\t\t\t}\n\t\t}\n\t\tif (pr_opcodes[statements[st].op].type_c == NULL)\n\t\t{\n\t\t\tstatement = st + statements[st].c.jumpofs;\n\t\t\tinfloop = 1000;\n\t\t\twhile (statements[statement].op == OP_GOTO)\n\t\t\t{\n\t\t\t\tif (!infloop--)\n\t\t\t\t{\n\t\t\t\t\tQCC_PR_ParseWarning(0, \"Infinate loop detected\");\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tstatements[st].c.ofs = (statement+statements[statement].a.ofs - st);\n\t\t\t\tstatement = st + statements[st].c.jumpofs;\n\t\t\t\toptres_compound_jumps++;\n\t\t\t}\n\t\t}\n\t}\n}\n#endif\n\nstatic void QCC_CheckForDeadAndMissingReturns(int first, int last, int rettype)\n{\n\tQCC_function_t *fnc;\n\tint st, st2;\n\n\tif (statements[last-1].op == OP_DONE)\n\t\tlast--;\t//don't want the done\n\n\tif (rettype != ev_void)\n\t\tif (statements[last-1].op != OP_RETURN)\n\t\t{\n\t\t\tif (statements[last-1].op != OP_GOTO || statements[last-1].a.jumpofs > 0)\n\t\t\t{\n\t\t\t\tQCC_PR_Warning(WARN_MISSINGRETURN, s_filen, statements[last].linenum, \"%s: not all control paths return a value\", pr_scope->name );\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\tfor (st = first; st < last; st++)\n\t{\n\t\tif (statements[st].op == OP_RETURN || statements[st].op == OP_GOTO)\n\t\t{\n\t\t\tst++;\n\t\t\tif (st == last)\n\t\t\t\tcontinue;\t//erm... end of function doesn't count as unreachable.\n\n\t\t\tif (!opt_compound_jumps)\n\t\t\t{\t//we can ignore single statements like these without compound jumps (compound jumps correctly removes all).\n\t\t\t\tif (statements[st].op == OP_GOTO)\t//inefficient compiler, we can ignore this.\n\t\t\t\t\tcontinue;\n\t\t\t\tif (statements[st].op == OP_DONE)\t//inefficient compiler, we can ignore this.\n\t\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t//always allow random return statements, because people like putting returns after switches even when all the switches have a return.\n\t\t\tif (statements[st].op == OP_RETURN)\t//inefficient compiler, we can ignore this.\n\t\t\t\tcontinue;\n\n\t\t\t//check for embedded functions. FIXME: should generate outside of the parent function.\n\t\t\tfor (fnc = pr_scope+1; fnc < &functions[numfunctions]; fnc++)\n\t\t\t{\n\t\t\t\tif (fnc->code == st)\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (fnc < &functions[numfunctions])\n\t\t\t\tcontinue;\n\n\t\t\t//make sure something goes to just after this return.\n\t\t\tfor (st2 = first; st2 < last; st2++)\n\t\t\t{\n\t\t\t\tif (pr_opcodes[statements[st2].op].associative == ASSOC_RIGHT)\n\t\t\t\t{\n\t\t\t\t\tif (pr_opcodes[statements[st2].op].type_a == NULL)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (st2 + statements[st2].a.jumpofs == st)\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tif (pr_opcodes[statements[st2].op].type_b == NULL)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (st2 + statements[st2].b.jumpofs == st)\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tif (pr_opcodes[statements[st2].op].type_c == NULL)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (st2 + statements[st2].c.jumpofs == st)\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (st2 == last)\n\t\t\t{\n\t\t\t\tQCC_PR_Warning(WARN_UNREACHABLECODE, pr_scope->filen, statements[st].linenum, \"%s: contains unreachable code (line %i)\", pr_scope->name, statements[st].linenum);\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\tif (rettype != ev_void && pr_opcodes[statements[st].op].associative == ASSOC_RIGHT)\n\t\t{\n\t\t\tif (pr_opcodes[statements[st].op].type_a == NULL)\n\t\t\t{\n\t\t\t\tif (st + statements[st].a.jumpofs == last)\n\t\t\t\t{\n\t\t\t\t\tQCC_PR_ParseWarning(WARN_MISSINGRETURN, \"%s: not all control paths return a value\", pr_scope->name );\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (pr_opcodes[statements[st].op].type_b == NULL)\n\t\t\t{\n\t\t\t\tif (st + statements[st].b.jumpofs == last)\n\t\t\t\t{\n\t\t\t\t\tQCC_PR_ParseWarning(WARN_MISSINGRETURN, \"%s: not all control paths return a value\", pr_scope->name );\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (pr_opcodes[statements[st].op].type_c == NULL)\n\t\t\t{\n\t\t\t\tif (st + statements[st].c.jumpofs == last)\n\t\t\t\t{\n\t\t\t\t\tQCC_PR_ParseWarning(WARN_MISSINGRETURN, \"%s: not all control paths return a value\", pr_scope->name );\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\npbool QCC_StatementIsAJump(int stnum, int notifdest)\t//only the unconditionals.\n{\n\tif (statements[stnum].op == OP_RETURN)\n\t\treturn true;\n\tif (statements[stnum].op == OP_DONE)\n\t\treturn true;\n\tif (statements[stnum].op == OP_GOTO)\n\t\tif (statements[stnum].a.jumpofs != notifdest)\n\t\t\treturn true;\n\treturn false;\n}\n\nint QCC_AStatementJumpsTo(int targ, int first, int last)\n{\n\tint st;\n\tfor (st = first; st < last; st++)\n\t{\n\t\tif (pr_opcodes[statements[st].op].type_a == NULL)\n\t\t{\n\t\t\tif (st + statements[st].a.jumpofs == targ && statements[st].a.ofs)\n\t\t\t{\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\tif (pr_opcodes[statements[st].op].type_b == NULL)\n\t\t{\n\t\t\tif (st + statements[st].b.jumpofs == targ)\n\t\t\t{\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\tif (pr_opcodes[statements[st].op].type_c == NULL)\n\t\t{\n\t\t\tif (st + statements[st].c.jumpofs == targ)\n\t\t\t{\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t}\n\n\tfor (st = 0; st < num_labels; st++)\t//assume it's used.\n\t{\n\t\tif (pr_labels[st].statementno == targ)\n\t\t\treturn true;\n\t}\n\n\tfor (st = 0; st < num_cases; st++)\t//assume it's used.\n\t{\n\t\tif (pr_cases[st] == targ)\n\t\t\treturn true;\n\t}\n\n\treturn false;\n}\n/*\n//goes through statements, if it sees a matching statement earlier, it'll strim out the current.\nvoid QCC_CommonSubExpressionRemoval(int first, int last)\n{\n\tint cur;\t//the current\n\tint prev;\t//the earlier statement\n\tfor (cur = last-1; cur >= first; cur--)\n\t{\n\t\tif (pr_opcodes[statements[cur].op].priority == -1)\n\t\t\tcontinue;\n\t\tfor (prev = cur-1; prev >= first; prev--)\n\t\t{\n\t\t\tif (statements[prev].op >= OP_CALL0 && statements[prev].op <= OP_CALL8)\n\t\t\t{\n\t\t\t\toptres_test1++;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (statements[prev].op >= OP_CALL1H && statements[prev].op <= OP_CALL8H)\n\t\t\t{\n\t\t\t\toptres_test1++;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (pr_opcodes[statements[prev].op].right_associative)\n\t\t\t{\t//make sure no changes to var_a occur.\n\t\t\t\tif (statements[prev].b == statements[cur].a)\n\t\t\t\t{\n\t\t\t\t\toptres_test2++;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tif (statements[prev].b == statements[cur].b && !pr_opcodes[statements[cur].op].right_associative)\n\t\t\t\t{\n\t\t\t\t\toptres_test2++;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (statements[prev].c == statements[cur].a)\n\t\t\t\t{\n\t\t\t\t\toptres_test2++;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tif (statements[prev].c == statements[cur].b && !pr_opcodes[statements[cur].op].right_associative)\n\t\t\t\t{\n\t\t\t\t\toptres_test2++;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (statements[prev].op == statements[cur].op)\n\t\t\t\tif (statements[prev].a == statements[cur].a)\n\t\t\t\t\tif (statements[prev].b == statements[cur].b)\n\t\t\t\t\t\tif (statements[prev].c == statements[cur].c)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (!QCC_FuncJumpsToRange(first, last, prev, cur))\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tstatements[cur].op = OP_STORE_F;\n\t\t\t\t\t\t\t\tstatements[cur].a = 28;\n\t\t\t\t\t\t\t\tstatements[cur].b = 28;\n\t\t\t\t\t\t\t\toptres_comexprremoval++;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\toptres_test1++;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t}\n\t}\n}\n*/\n\n//follow branches (by recursing).\n//stop on first read(error, return statement) or write(no error, return -1)\n//end-of-block returns 0, done/return/goto returns -2\nstatic int QCC_CheckOneUninitialised(int firststatement, int laststatement, union QCC_eval_basic_s *min, union QCC_eval_basic_s *max)\n{\n#define SPLIT \\\n\t\tif (!(ofs > max || ofs+sz <= min)) \\\n\t\t{\t\\\n\t\t\tif (min < ofs)\t\\\n\t\t\t{\t/*keep checking before*/ \\\n\t\t\t\tret = QCC_CheckOneUninitialised(i + 1, laststatement, min, ofs);\t\\\n\t\t\t\tif (ret > 0)\t\\\n\t\t\t\t\treturn ret;\t\\\n\t\t\t}\t\\\n\t\t\tif (ofs+sz < max)\t\\\n\t\t\t{\t/*keep checking after*/ \\\n\t\t\t\tret = QCC_CheckOneUninitialised(i + 1, laststatement, ofs+sz, max);\t\\\n\t\t\t\tif (ret > 0)\t\\\n\t\t\t\t\treturn ret;\t\\\n\t\t\t}\t\\\n\t\t\tif (iswrite) \\\n\t\t\t\treturn -1; /*okay, we wrote it all*/ \\\n\t\t\treturn i;\t/*an error when its a read*/ \\\n\t\t}\n\tint ret;\n\tint i;\n\tQCC_statement_t *st;\n\n\tfor (i = firststatement; i < laststatement; i++)\n\t{\n\t\tst = &statements[i];\n\n\t\tif (st->op == OP_DONE || st->op == OP_RETURN)\n\t\t{\n\t\t\tif (st->a.cast && st->a.sym)\n\t\t\t{\n\t\t\t\tunion QCC_eval_basic_s *ofs = st->a.sym->symboldata+st->a.ofs;\n\t\t\t\tint sz = st->a.cast->size;\n\t\t\t\tif (!(ofs > max || ofs+sz < min))\n\t\t\t\t\treturn i;\n\t\t\t}\n\t\t\treturn -2;\n\t\t}\n\n//\t\tthis code catches gotos, but can cause issues with while statements.\n//\t\tif (st->op == OP_GOTO && (int)st->a < 1)\n//\t\t\treturn -2;\n\n\t\tif (pr_opcodes[st->op].type_a && st->a.sym)\n\t\t{\n\t\t\tunion QCC_eval_basic_s *ofs = st->a.sym->symboldata+st->a.ofs;\n\t\t\tint sz = st->a.cast->size;\n\t\t\tpbool iswrite = OpAssignsToA(st->op) || st->op == OP_GLOBALADDRESS; /* address-of counts as a write here, because we're too dumb to track when/if that is assigned to.*/\n\n\t\t\tSPLIT\n\t\t}\n\t\telse if (pr_opcodes[st->op].associative == ASSOC_RIGHT && st->a.jumpofs > 0 && !st->a.sym)\n\t\t{\n\t\t\tint jump = i + st->a.jumpofs;\n\t\t\tret = QCC_CheckOneUninitialised(i + 1, jump, min, max);\n\t\t\tif (ret > 0)\n\t\t\t\treturn ret;\n\t\t\ti = jump-1;\n\t\t}\n\n\t\tif (pr_opcodes[st->op].type_b && st->b.sym)\n\t\t{\n\t\t\tunion QCC_eval_basic_s *ofs = st->b.sym->symboldata+st->b.jumpofs;\n\t\t\tint sz = st->b.cast->size;\n\t\t\tpbool iswrite = OpAssignsToB(st->op);\n\n\t\t\tSPLIT\n\t\t}\n\t\telse if (pr_opcodes[st->op].associative == ASSOC_RIGHT && st->b.jumpofs > 0 && !st->b.sym && !(st->flags & STF_LOGICOP))\n\t\t{\n\t\t\tint jump = i + st->b.jumpofs;\n\t\t\t//check if there's an else.\n\t\t\tst = &statements[jump-1];\n\t\t\tif (st->op == OP_GOTO && st->a.jumpofs > 0)\n\t\t\t{\n\t\t\t\tint jump2 = jump-1 + st->a.ofs;\n\t\t\t\tint rett = QCC_CheckOneUninitialised(i + 1, jump - 1, min, max);\n\t\t\t\tif (rett > 0)\n\t\t\t\t\treturn rett;\n\t\t\t\tret = QCC_CheckOneUninitialised(jump, jump2, min, max);\n\t\t\t\tif (ret > 0)\n\t\t\t\t\treturn ret;\n\t\t\t\tif (rett < 0 && ret < 0)\n\t\t\t\t\treturn (rett == ret)?ret:-1;\t//inited or aborted in both, don't need to continue along this branch\n\t\t\t\ti = jump2-1;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tret = QCC_CheckOneUninitialised(i + 1, jump, min, max);\n\t\t\t\tif (ret > 0)\n\t\t\t\t\treturn ret;\n\t\t\t\ti = jump-1;\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (pr_opcodes[st->op].type_c && st->c.sym)\n\t\t{\n\t\t\tunion QCC_eval_basic_s *ofs = st->c.sym->symboldata+st->c.ofs;\n\t\t\tint sz = st->c.cast->size;\n\t\t\tpbool iswrite = OpAssignsToC(st->op);\n\n\t\t\tSPLIT\n\t\t}\n\t\telse if (pr_opcodes[st->op].associative == ASSOC_RIGHT && st->c.jumpofs > 0 && !st->c.sym)\n\t\t{\n\t\t\tint jump = i + st->c.jumpofs;\n\t\t\tret = QCC_CheckOneUninitialised(i + 1, jump, min, max);\n\t\t\tif (ret > 0)\n\t\t\t\treturn ret;\n\t\t\ti = jump-1;\n\n\t\t\tcontinue;\n\t\t}\n\t}\n\n\treturn 0;\n#undef SPLIT\n}\n\nstatic pbool QCC_CheckUninitialised(int firststatement, int laststatement)\n{\n\tQCC_def_t *local, *c, *uninit;\n\tunsigned int i;\n\tpbool result = false;\n\tunsigned int paramend = FIRST_LOCAL;\n\tQCC_type_t *type = pr_scope->type;\n\tint err;\n\n\t//assume all, because we don't care for optimisations once we know we're not going to compile anything (removes warning about uninitialised unknown variables/typos).\n\tif (pr_error_count)\n\t\treturn true;\n\n\tfor (i = 0; i < type->num_parms; i++)\n\t{\n\t\tparamend += type->params[i].type->size;\n\t}\n\n\tfor (local = pr.local_head.nextlocal; local; local = local->nextlocal)\n\t{\n\t\tif (local->constant)\n\t\t\tcontinue;\t//will get some other warning, so we don't care.\n\t\tif (local->isstatic)\n\t\t\tcontinue;\t//not a real local, so will be properly initialised.\n\t\tif (local->symbolheader != local)\n\t\t\tcontinue;\t//ignore slave symbols, cos they're not interesting and should have been checked as part of the parent.\n\t\tif (local->isparameter)\n\t\t\tcontinue;\n\t\tif (local->arraysize)\n\t\t\tcontinue;\t//probably indexed. we won't detect things properly. its the user's resposibility to check. :(\n\t\terr = QCC_CheckOneUninitialised(firststatement, laststatement, local->symboldata, local->symboldata + local->type->size * (local->arraysize?local->arraysize:1));\n\t\tif (err > 0)\n\t\t{\t//try to refine it to a single component if we can.\n\t\t\tuninit = NULL;\n\t\t\tfor (c = local; ; c = c->next)\n\t\t\t{\n\t\t\t\tif (c != local)\n\t\t\t\t{\n\t\t\t\t\terr = QCC_CheckOneUninitialised(firststatement, laststatement, c->symboldata, c->symboldata + c->type->size * (c->arraysize?c->arraysize:1));\n\t\t\t\t\tif (err > 0)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (uninit)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tuninit = NULL;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tuninit = c;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (c == local->deftail)\n\t\t\t\t\tbreak;\t//that was the last of them.\n\t\t\t}\n\t\t\tif (!uninit)\t//otherwise give up and print the whole struct.\n\t\t\t\tuninit = local;\n\t\t\tQCC_PR_Warning(WARN_UNINITIALIZED, s_filen, statements[err].linenum, \"Potentially uninitialised variable %s%s%s\", col_symbol,uninit->name,col_none);\n\t\t\tresult = true;\n\n//\t\t\tbreak;\n\t\t}\n\t}\n\n\treturn result;\n}\n\nvoid QCC_Marshal_Locals(int firststatement, int laststatement)\n{\n\tQCC_def_t *local;\n\tpbool error = false;\n\n\tif (pr.local_head.nextlocal)\t//only check if there's actually somthing to check\n\t{\t//FIXME: we should just insert extra statements to clear any we deem uninitialised, instead of generating errors etc.\n\t\t//then we can overlap all functions always without worrying.\n\t\tif (flag_allowuninit)\n\t\t{\n\t\t\tif (qccwarningaction[WARN_UNINITIALIZED])\n\t\t\t\tQCC_CheckUninitialised(firststatement, laststatement);\t//still need to call it for warnings, but if those warnings are off we can skip the cost\n\t\t}\n\t\telse if (!opt_locals_overlapping)\n\t\t{\n\t\t\tif (qccwarningaction[WARN_UNINITIALIZED])\n\t\t\t\tQCC_CheckUninitialised(firststatement, laststatement);\t//still need to call it for warnings, but if those warnings are off we can skip the cost\n\t\t\terror = true;\t//always use the legacy behaviour\n\t\t}\n\t\telse if (QCC_CheckUninitialised(firststatement, laststatement))\n\t\t{\n\t\t\terror = true;\n\t//\t\tQCC_PR_Note(ERR_INTERNAL, strings+s_file, pr_source_line, \"Not overlapping locals from %s due to uninitialised locals\", pr_scope->name);\n\t\t}\n\t\telse\n\t\t{\n\t\t\t//make sure we're allowed to marshall this function's locals\n\t\t\tfor (local = pr.local_head.nextlocal; local; local = local->nextlocal)\n\t\t\t{\n\t\t\t\tif (local->isstatic)\n\t\t\t\t\tcontinue;\t//static variables are actually globals\n\t\t\t\tif (local->constant && local->initialized)\n\t\t\t\t\tcontinue;\t//as are initialised consts, because its pointless otherwise.\n\t\t\t\tif (local->symbolheader && local->symbolheader->scope != local->scope)\n\t\t\t\t\tcontinue;\n\n\t\t\t\t//FIXME: check for uninitialised locals.\n\t\t\t\t//these matter when the function goes recursive (and locals marshalling counts as recursive every time).\n\t\t\t\tif (local->symboldata[0]._int)\n\t\t\t\t{\n\t\t\t\t\tif (!error)\n\t\t\t\t\t\tQCC_PR_Note(ERR_INTERNAL, local->filen, local->s_line, \"Marshaling non-const initialised %s\", local->name);\n\t\t\t\t\terror = true;\n\t\t\t\t}\n\n\t\t\t\t/*if (local->constant)\n\t\t\t\t{\n\t\t\t\t\tQCC_PR_Note(ERR_INTERNAL, local->filen, local->s_line, \"Marshaling const %s\", local->name);\n\t\t\t\t\terror = true;\n\t\t\t\t}*/\n\t\t\t}\n\t\t}\n\n\t\t//func(&somelocal) reuses the same memory address for both caller and callee. there's nothing we safely do to fix recursive functions, but we can at least stop -Olo from breaking things more.\n\t\tif (!error)\n\t\t{\n\t\t\tint i;\n\t\t\tQCC_statement_t *st;\n\t\t\tfor (i = firststatement; i < laststatement; i++)\n\t\t\t{\n\t\t\t\tst = &statements[i];\n\n\t\t\t\tif (st->op == OP_GLOBALADDRESS && st->a.sym->scope && !st->a.sym->isstatic)\n\t\t\t\t{\n\t\t\t\t\terror = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tif (error)\n\t\tpr_scope->privatelocals = true;\n\telse\n\t\tpr_scope->privatelocals = false;\n\tpr_scope->firstlocal = pr.local_head.nextlocal;\n\tpr.local_head.nextlocal = NULL;\n\tpr.local_tail = &pr.local_head;\n}\n\n#ifdef WRITEASM\nstatic void QCC_WriteGUIAsmFunction(QCC_function_t\t*sc, unsigned int firststatement)\n{\n\tunsigned int\t\t\ti;\n//\tQCC_type_t *type;\n\tchar typebuf[512];\n\n\tchar line[2048];\n\textern int currentsourcefile;\n\n//\ttype = sc->type;\n\n\tfor (i = firststatement; i < (unsigned int)numstatements; i++)\n\t{\n\t\tline[0] = 0;\n//\t\tQC_snprintfz(line, sizeof(line), \"%i  \", QCC_VarAtOffset(statements[i].a));\n\t\tQC_strlcat(line, pr_opcodes[statements[i].op].opname, sizeof(line));\n\t\tif (pr_opcodes[statements[i].op].type_a != &type_void)\n\t\t{\n//\t\t\tif (strlen(pr_opcodes[statements[i].op].opname)<6)\n//\t\t\t\tQC_strlcat(line, \"  \", sizeof(line));\n\t\t\tif (pr_opcodes[statements[i].op].type_a)\n\t\t\t\tQC_snprintfz(typebuf, sizeof(typebuf), \"  %s\", QCC_VarAtOffset(statements[i].a));\n\t\t\telse\n\t\t\t\tQC_snprintfz(typebuf, sizeof(typebuf), \"  %i\", statements[i].a.jumpofs);\n\t\t\tQC_strlcat(line, typebuf, sizeof(line));\n\t\t\tif (pr_opcodes[statements[i].op].type_b != &type_void)\n\t\t\t{\n\t\t\t\tif (pr_opcodes[statements[i].op].type_b)\n\t\t\t\t\tQC_snprintfz(typebuf, sizeof(typebuf), \",  %s\", QCC_VarAtOffset(statements[i].b));\n\t\t\t\telse\n\t\t\t\t\tQC_snprintfz(typebuf, sizeof(typebuf), \",  %i\", statements[i].b.jumpofs);\n\t\t\t\tQC_strlcat(line, typebuf, sizeof(line));\n\t\t\t\tif (pr_opcodes[statements[i].op].type_c != &type_void && (pr_opcodes[statements[i].op].associative==ASSOC_LEFT || statements[i].c.cast))\n\t\t\t\t{\n\t\t\t\t\tif (pr_opcodes[statements[i].op].type_c)\n\t\t\t\t\t\tQC_snprintfz(typebuf, sizeof(typebuf), \",  %s\", QCC_VarAtOffset(statements[i].c));\n\t\t\t\t\telse\n\t\t\t\t\t\tQC_snprintfz(typebuf, sizeof(typebuf), \",  %i\", statements[i].c.jumpofs);\n\t\t\t\t\tQC_strlcat(line, typebuf, sizeof(line));\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (pr_opcodes[statements[i].op].type_c != &type_void)\n\t\t\t\t{\n\t\t\t\t\tif (pr_opcodes[statements[i].op].type_c)\n\t\t\t\t\t\tQC_snprintfz(typebuf, sizeof(typebuf), \",  %s\", QCC_VarAtOffset(statements[i].c));\n\t\t\t\t\telse\n\t\t\t\t\t\tQC_snprintfz(typebuf, sizeof(typebuf), \",  %i\", statements[i].c.jumpofs);\n\t\t\t\t\tQC_strlcat(line, typebuf, sizeof(line));\n\t\t\t\t}\n\t\t\t}\n\t\t}\t\n\t\telse\n\t\t{\n\t\t\tif (pr_opcodes[statements[i].op].type_c != &type_void)\n\t\t\t{\n\t\t\t\tif (pr_opcodes[statements[i].op].type_c)\n\t\t\t\t\tQC_snprintfz(typebuf, sizeof(typebuf), \"  %s\", QCC_VarAtOffset(statements[i].c));\n\t\t\t\telse\n\t\t\t\t\tQC_snprintfz(typebuf, sizeof(typebuf), \"  %i\", statements[i].c.jumpofs);\n\t\t\t\tQC_strlcat(line, typebuf, sizeof(line));\n\t\t\t}\n\t\t}\n\t\tif (currentsourcefile)\n\t\t\texterns->Printf(\"code: %s:%i: %i:%s;\\n\", sc->filen, statements[i].linenum, currentsourcefile, line);\n\t\telse\n\t\t\texterns->Printf(\"code: %s:%i: %s;\\n\", sc->filen, statements[i].linenum, line);\n\t}\n}\n\nvoid QCC_WriteAsmFunction(QCC_function_t\t*sc, unsigned int firststatement, QCC_def_t *firstparm)\n{\n\tunsigned int\t\t\ti;\n\tQCC_def_t *o = firstparm;\n\tQCC_type_t *type;\n\tchar typebuf[512];\n\n\tif (sc->parentscope)\t//don't print dupes.\n\t\treturn;\n\n\tif (flag_guiannotate)\n\t\tQCC_WriteGUIAsmFunction(sc, firststatement);\n\n\tif (!asmfile)\n\t\treturn;\n\n\ttype = sc->type;\n\tfprintf(asmfile, \"%s(\", TypeName(type->aux_type, typebuf, sizeof(typebuf)));\n\tfor (o = pr.local_head.nextlocal, i = 0; i < type->num_parms; i++)\n\t{\n\t\tif (i)\n\t\t\tfprintf(asmfile, \", \");\n\n\t\tif (o)\n\t\t{\n\t\t\tfprintf(asmfile, \"%s %s\", TypeName(o->type, typebuf, sizeof(typebuf)), o->name);\n\t\t\to = o->nextlocal;\n\t\t}\n\t\telse\n\t\t\tfprintf(asmfile, \"%s\", TypeName(type->params[i].type, typebuf, sizeof(typebuf)));\n\t}\n\n\tfprintf(asmfile, \") %s = asm\\n{\\n\", sc->name);\n\n\tQCC_fprintfLocals(asmfile, o);\n\n\tfor (i = firststatement; i < (unsigned int)numstatements; i++)\n\t{\n\t\tfprintf(asmfile, \"\\t%s\", pr_opcodes[statements[i].op].opname);\n\t\tif (pr_opcodes[statements[i].op].type_a != &type_void)\n\t\t{\n\t\t\tif (strlen(pr_opcodes[statements[i].op].opname)<6)\n\t\t\t\tfprintf(asmfile, \"\\t\");\n\t\t\tif (pr_opcodes[statements[i].op].type_a)\n\t\t\t\tfprintf(asmfile, \"\\t%s\", QCC_VarAtOffset(statements[i].a));\n\t\t\telse\n\t\t\t\tfprintf(asmfile, \"\\t%i\", statements[i].a.ofs);\n\t\t\tif (pr_opcodes[statements[i].op].type_b != &type_void)\n\t\t\t{\n\t\t\t\tif (pr_opcodes[statements[i].op].type_b)\n\t\t\t\t\tfprintf(asmfile, \",\\t%s\", QCC_VarAtOffset(statements[i].b));\n\t\t\t\telse\n\t\t\t\t\tfprintf(asmfile, \",\\t%i\", statements[i].b.ofs);\n\t\t\t\tif (pr_opcodes[statements[i].op].type_c != &type_void && (pr_opcodes[statements[i].op].associative==ASSOC_LEFT || statements[i].c.sym))\n\t\t\t\t{\n\t\t\t\t\tif (pr_opcodes[statements[i].op].type_c)\n\t\t\t\t\t\tfprintf(asmfile, \",\\t%s\", QCC_VarAtOffset(statements[i].c));\n\t\t\t\t\telse\n\t\t\t\t\t\tfprintf(asmfile, \",\\t%i\", statements[i].c.ofs);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (pr_opcodes[statements[i].op].type_c != &type_void)\n\t\t\t\t{\n\t\t\t\t\tif (pr_opcodes[statements[i].op].type_c)\n\t\t\t\t\t\tfprintf(asmfile, \",\\t%s\", QCC_VarAtOffset(statements[i].c));\n\t\t\t\t\telse\n\t\t\t\t\t\tfprintf(asmfile, \",\\t%i\", statements[i].c.ofs);\n\t\t\t\t}\n\t\t\t}\n\t\t}\t\n\t\telse\n\t\t{\n\t\t\tif (pr_opcodes[statements[i].op].type_c != &type_void)\n\t\t\t{\n\t\t\t\tif (pr_opcodes[statements[i].op].type_c)\n\t\t\t\t\tfprintf(asmfile, \"\\t%s\", QCC_VarAtOffset(statements[i].c));\n\t\t\t\telse\n\t\t\t\t\tfprintf(asmfile, \"\\t%i\", statements[i].c.ofs);\n\t\t\t}\n\t\t}\n\t\tfprintf(asmfile, \"; /*%i*/\\n\", statements[i].linenum);\n\t}\n\n\tfprintf(asmfile, \"}\\n\\n\");\n}\n#endif\n\nstatic QCC_function_t *QCC_PR_GenerateBuiltinFunction (QCC_def_t *def, int builtinnum, char *builtinname)\n{\n\tQCC_function_t *func;\n\tif (numfunctions >= MAX_FUNCTIONS)\n\t\tQCC_PR_ParseError(ERR_INTERNAL, \"Too many functions - %i\\nAdd '-max_functions %i' to the commandline\", numfunctions, (numfunctions+4096)&~4095);\n\tfunc = &functions[numfunctions++];\n\tfunc->filen = s_filen;\n\tfunc->unitn = s_unitn;\n\tfunc->s_filed = s_filed;\n\tfunc->line = def->s_line;\t//FIXME\n\tif (builtinname==def->name)\n\t\tfunc->name = builtinname;\n\telse\n\t{\n\t\tfunc->name = qccHunkAlloc(strlen(builtinname)+1);\n\t\tstrcpy(func->name, builtinname);\n\t}\n\tfunc->builtin = builtinnum;\n\tfunc->code = -1;\n\tfunc->type = def->type;\n\tfunc->firstlocal = NULL;\n\tfunc->def = def;\n\treturn func;\n}\nstatic QCC_function_t *QCC_PR_GenerateQCFunction (QCC_def_t *def, QCC_type_t *type, unsigned int *pif_flags)\n{\n\tQCC_function_t *func = NULL;\n\tif (numfunctions >= MAX_FUNCTIONS)\n\t\tQCC_PR_ParseError(ERR_INTERNAL, \"Too many functions - %i\\nAdd '-max_functions %i' to the commandline\", numfunctions, (numfunctions+4096)&~4095);\n\n\tif (!pif_flags)\n\t\t;\n\telse if ((*pif_flags & PIF_ACCUMULATE) && !(*pif_flags & PIF_WRAP))\n\t{\n\t\tif (def->symboldata[0].function)\n\t\t{\n\t\t\tfunc = &functions[def->symboldata[0].function];\t//just resume the old one...\n\n\t\t\tif (func->def != def || func->type != type || func->parentscope != pr_scope)\n\t\t\t\tQCC_PR_ParseError(ERR_INTERNAL, \"invalid function accumulation\");\n\n\t\t\t//fixme: validate stuff\n\t\t\treturn func;\n\t\t}\n\t}\n\telse if ((*pif_flags&PIF_WRAP) && def->symboldata[0].function)\n\t{\n\t\tQCC_def_t *locals;\n\t\tQCC_function_t *prior;\n\t\tfunc = &functions[def->symboldata[0].function];\n\t\tif ((*pif_flags&PIF_AUTOWRAP) && func->statements && func->def == def && func->type == type && func->parentscope == pr_scope)\n\t\t{\n\t\t\t*pif_flags &= ~(PIF_WRAP|PIF_AUTOWRAP);\n\t\t\treturn func;\t//looks like we should be able to just reuse it. no need to wrap.\n\t\t}\n\t\tprior = &functions[numfunctions++];\n\t\tmemcpy(prior, func, sizeof(*prior));\n\n\t\t//FIXME: we need a proper algorithm to generate valid anonymous function names.\n\t\tprior->name = qccHunkAlloc(6+strlen(func->name)+1);\n\t\tstrcpy(prior->name, \"prior*\");\n\t\tstrcpy(prior->name+6, func->name);\n\n\t\tmemset(func, 0, sizeof(*func));\n\n\t\tfor (locals = prior->firstlocal; locals; locals = locals->nextlocal)\n\t\t{\n\t\t\tif (locals->scope != func)\n\t\t\t\tQCC_PR_ParseError(ERR_INTERNAL, \"internal consistency check failed while wrapping %s\", def->name);\n\t\t\tlocals->scope = prior;\n\t\t}\n\t}\n\telse if (*pif_flags&PIF_WRAP)\n\t{\n\t\tQCC_PR_ParseError(ERR_INTERNAL, \"cannot wrap bodyless function %s\", def->name);\n\t\treturn NULL;\n\t}\n\tif (!func)\n\t\tfunc = &functions[numfunctions++];\n\tfunc->filen = s_filen;\n\tfunc->unitn = s_unitn;\n\tfunc->s_filed = s_filed;\n\tfunc->line = pr_source_line;//def?def->s_line:0;\t//FIXME\n\tfunc->name = def?def->name:\"\";\n\tfunc->builtin = 0;\n\tfunc->code = numstatements;\n\tfunc->firstlocal = NULL;\n\tfunc->def = def;\n\tfunc->type = type;\n\tfunc->parentscope = pr_scope;\n\treturn func;\n}\n\nstatic void QCC_PR_ResumeFunction(QCC_function_t *f)\n{\n\tif (pr_scope != f)\n\t{\n\t\tpr_scope = f;\n\n\t\t//reset the locals chain\n\t\tpr.local_head.nextlocal = f->firstlocal;\n\t\tpr.local_tail = &pr.local_head;\n\t\twhile (pr.local_tail->nextlocal)\n\t\t\tpr.local_tail = pr.local_tail->nextlocal;\n\n\t\t//qcvm sees the function start here.\n\t\tf->code = numstatements;\n\n\t\tif (f->statements)\n\t\t{\n\t\t\tmemcpy(statements+f->code, f->statements, sizeof(*statements) * f->numstatements);\n\t\t\tnumstatements += f->numstatements;\n\n\t\t\tf->statements = NULL;\n\t\t\tf->numstatements = 0;\n\t\t}\n\t}\n}\nstatic void QCC_PR_FinaliseFunction(QCC_function_t *f)\n{\n\tQCC_statement_t *st;\n\tpbool needsdone=false;\n\n\tQCC_PR_ResumeFunction(f);\n\n\tpr_token_line_last = f->line_end;\n\ts_filen = f->filen;\n\t\n\tif (f->returndef.cast && f->type->aux_type->size <= type_vector->size)\n\t{\n\t\tPR_GenerateReturnOuts();\n\t\tQCC_ForceUnFreeDef(f->returndef.sym);\n\t\tQCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_RETURN], f->returndef, nullsref, NULL));\n\t}\n\n\tif (f->code == numstatements)\n\t\tneedsdone = true;\n\telse if (statements[numstatements - 1].op != OP_RETURN && statements[numstatements - 1].op != OP_DONE)\n\t\tneedsdone = true;\n\n\tif (opt_return_only && !needsdone)\n\t\tneedsdone = QCC_FuncJumpsTo(f->code, numstatements, numstatements);\n\n\t// emit an end of statements opcode\n\tif (!opt_return_only || needsdone)\n\t{\n\t\t/*if (pr_classtype)\n\t\t{\n\t\t\tQCC_def_t *e, *e2;\n\t\t\te = QCC_PR_GetDef(NULL, \"__oself\", pr_scope, false, 0);\n\t\t\te2 = QCC_PR_GetDef(NULL, \"self\", NULL, false, 0);\n\t\t\tQCC_FreeTemp(QCC_PR_Statement(&pr_opcodes[OP_STORE_ENT], e, QCC_PR_DummyDef(pr_classtype, \"self\", pr_scope, 0, e2->ofs, false), NULL));\n\t\t}*/\n\n\t\tif (needsdone)\n\t\t\tPR_GenerateReturnOuts();\n\t\tQCC_PR_Statement (&pr_opcodes[OP_DONE], nullsref, nullsref, &st);\n\t}\n\telse\n\t\toptres_return_only++;\n\n\tQCC_CheckForDeadAndMissingReturns(f->code, numstatements, f->type->aux_type->type);\n\n//\tif (opt_comexprremoval)\n//\t\tQCC_CommonSubExpressionRemoval(f->code, numstatements);\n\n\n\tQCC_RemapLockedTemps(f->code, numstatements);\n\tQCC_Marshal_Locals(f->code, numstatements);\n\tQCC_WriteAsmFunction(f, f->code, f->firstlocal);\n\n\tif (opt_compound_jumps)\n\t\tQCC_CompoundJumps(f->code, numstatements);\n}\nvoid QCC_PR_FinaliseFunctions(void)\n{\n\tQCC_function_t *f;\n\tif (numinitstatements)\n\t{\n\t\tQCC_statement_t *ns;\n\t\tf = functions+numfunctions;\n\t\tif (f==functions+numfunctions) for (f = functions+1; f < functions+numfunctions; f++) if (!strcmp(f->name, \"_atinit\")) break;\n\t\tif (f==functions+numfunctions) for (f = functions+1; f < functions+numfunctions; f++) if (!strcmp(f->name, \"init\")) break;\n\t\tif (f==functions+numfunctions) for (f = functions+1; f < functions+numfunctions; f++) if (!strcmp(f->name, \"initents\")) break;\n\t\tif (f==functions+numfunctions) for (f = functions+1; f < functions+numfunctions; f++) if (!strcmp(f->name, \"m_init\")||!strcmp(f->name, \"CSQC_Init\")||!strcmp(f->name, \"worldspawn\")) break;\n\t\tif (f==functions+numfunctions)\n\t\t\tQCC_PR_ParseError (ERR_BADBUILTINIMMEDIATE, \"Construction statements were not part of any function. call _atinit manually.\");\n\t\telse\n\t\t{\n\t\t\t//copy it into the start of the function. shouldn't be any locals/temps/etc to worry about.\n\t\t\tns = qccHunkAlloc((numinitstatements + f->numstatements) * sizeof(*ns));\n\t\t\tmemcpy(ns, initstatements, numinitstatements*sizeof(*ns));\n\t\t\tmemcpy(ns+numinitstatements, f->statements, f->numstatements*sizeof(*ns));\n\t\t\tf->statements = ns;\n\t\t\tf->numstatements += numinitstatements;\n\t\t\tnuminitstatements = 0;\n\t\t}\n\t}\n\n\tfor (f = functions; f < functions+numfunctions; f++)\n\t{\n\t\tif (f->statements)\n\t\t\tQCC_PR_FinaliseFunction(f);\n\t}\n}\n/*\n============\nPR_ParseImmediateStatements\n\nParse a function body\n\nIf def is set, allows stuff to refer back to a def for the function.\n============\n*/\nQCC_function_t *QCC_PR_ParseImmediateStatements (QCC_def_t *def, QCC_type_t *type, unsigned int pif_flags)\n{\n\tunsigned int u, p;\n\tQCC_function_t\t*f;\n\tQCC_sref_t\t\tparm;\n\n\tQCC_def_t *prior = NULL, *d, *lastparm;\n\tpbool mergeargs;\n\n\tint startingtypes = numtypeinfos;\n\n\tconditional = 0;\n\n\texpandedemptymacro = false;\n\n//\n// check for builtin function definition #1, #2, etc\n//\n// hexenC has void name() : 2;\n\tif (!(pif_flags & PIF_ACCUMULATE))\n\t{\n\t\tif (QCC_PR_CheckToken (\"#\") || QCC_PR_CheckToken (\":\"))\n\t\t{\n\t\t\tint binum = 0;\n\t\t\tif (pr_token_type == tt_immediate\n\t\t\t&& pr_immediate_type == type_float\n\t\t\t&& pr_immediate._float == (int)pr_immediate._float)\n\t\t\t\tbinum = (int)pr_immediate._float;\n\t\t\telse if (pr_token_type == tt_immediate && pr_immediate_type == type_integer)\n\t\t\t\tbinum = pr_immediate._int;\n\t\t\telse\n\t\t\t\tQCC_PR_ParseError (ERR_BADBUILTINIMMEDIATE, \"Bad builtin immediate\");\n\t\t\tf = QCC_PR_GenerateBuiltinFunction(def, binum, def->name);\n\t\t\tQCC_PR_Lex ();\n\n\t\t\treturn f;\n\t\t}\n\t\tif (QCC_PR_CheckKeyword(keyword_external, \"external\"))\n\t\t{\t//reacc style builtin\n\t\t\tif (pr_token_type != tt_immediate\n\t\t\t|| pr_immediate_type != type_float\n\t\t\t|| pr_immediate._float != (int)pr_immediate._float)\n\t\t\t\tQCC_PR_ParseError (ERR_BADBUILTINIMMEDIATE, \"Bad builtin immediate\");\n\t\t\tf = QCC_PR_GenerateBuiltinFunction(def, (int)-pr_immediate._float, def->name);\n\t\t\tQCC_PR_Lex ();\n\t\t\tQCC_PR_Expect(\";\");\n\n\t\t\treturn f;\n\t\t}\n\t}\n\n//\tif (type->vargs)\n//\t\tQCC_PR_ParseError (ERR_FUNCTIONWITHVARGS, \"QC function with variable arguments and function body\");\n\n\tf = QCC_PR_GenerateQCFunction(def, type, &pif_flags);\n\n\tQCC_PR_ResumeFunction(f);\n\n\tQCC_RemapLockedTemps(-1, -1);\n\n\tmergeargs = !!f->firstlocal;\n//\n// define the basic parms\n//\n\tif (mergeargs)\n\t{\n\t\tQCC_def_t *arg;\n\t\tfor (u=0, p=0, arg=f->firstlocal ; u<type->num_parms; u++, arg = arg->deftail->nextlocal)\n\t\t{\n\t\t\tQCC_PR_DummyDef(type->params[u].type, pr_parm_names[u], pr_scope, 0, arg, 0, true, GDF_PARAMETER);\n\t\t}\n\n\t\tif (type->vargcount)\n\t\t{\n\t\t\tif (!pr_parm_argcount_name)\n\t\t\t\tQCC_Error(ERR_INTERNAL, \"I forgot what the va_count argument is meant to be called\");\n\t\t\telse\n\t\t\t\tQCC_PR_DummyDef(type_float, pr_parm_argcount_name, pr_scope, 0, arg, 0, true, 0);\n\t\t}\n\t}\n\telse\n\t{\n\t\tfor (u=0, p=0 ; u<type->num_parms; u++)\n\t\t{\n\t\t\tunsigned int o;\n\t\t\tif (!*pr_parm_names[u])\n\t\t\t{\n\t\t\t\tQC_snprintfz(pr_parm_names[u], sizeof(pr_parm_names[u]), \"$arg_%u\", u);\n\t\t\t\tQCC_PR_ParseWarning(WARN_PARAMWITHNONAME, \"Parameter %u of %s is not named\", u+1, pr_scope->name);\n\t\t\t}\n\t\t\tparm = QCC_PR_GetSRef (type->params[u].type, pr_parm_names[u], pr_scope, 2, 0, 0);\n\t\t\tparm.sym->used = true;\t//make sure system parameters get seen by the engine, even if the names are stripped..\n\t\t\tparm.sym->referenced = true;\n\n\t\t\tfor (o = 0; o < (type->params[u].type->size+2)/3; o++)\n\t\t\t{\n\t\t\t\tif (p < MAX_PARMS)\n\t\t\t\t{\n\t\t\t\t\tparm.sym->isparameter = true;\n\t\t\t\t\tparm.ofs+=3;//no need to copy anything, as the engine will do it for us.\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\t//extra parms need to be explicitly copied.\n\t\t\t\t\tif (!extra_parms[p - MAX_PARMS].sym)\n\t\t\t\t\t{\n\t\t\t\t\t\tchar name[128];\n\t\t\t\t\t\tQC_snprintfz(name, sizeof(name), \"$parm%u\", p);\n\t\t\t\t\t\textra_parms[p - MAX_PARMS] = QCC_PR_GetSRef(type_vector, name, NULL, true, 0, GDF_STRIP);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\tQCC_ForceUnFreeDef(extra_parms[p - MAX_PARMS].sym);\n\t\t\t\t\tQCC_UnFreeTemp(parm);\n\t\t\t\t\textra_parms[p - MAX_PARMS].cast = parm.cast;\n\t\t\t\t\tif (type->params[u].type->size-o*3 >= 3)\n\t\t\t\t\t{\n\t\t\t\t\t\tQCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_STORE_V], extra_parms[p - MAX_PARMS], parm, NULL));\n\t\t\t\t\t\tparm.ofs+=3;\n\t\t\t\t\t}\n\t\t\t\t\telse if (type->params[u].type->size-o*3 == 2 && QCC_OPCodeValid(&pr_opcodes[OP_STORE_I64]))\n\t\t\t\t\t{\n\t\t\t\t\t\tQCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_STORE_I64], extra_parms[p - MAX_PARMS], parm, NULL));\n\t\t\t\t\t\tparm.ofs+=2;\n\t\t\t\t\t}\n\t\t\t\t\telse if (type->params[u].type->size-o*3 == 2)\n\t\t\t\t\t{\n\t\t\t\t\t\tQCC_sref_t t = extra_parms[p - MAX_PARMS];\n\t\t\t\t\t\tQCC_UnFreeTemp(t);\n\t\t\t\t\t\tQCC_UnFreeTemp(parm);\n\t\t\t\t\t\tQCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_STORE_F], t, parm, NULL));\n\t\t\t\t\t\tt.ofs++;\n\t\t\t\t\t\tparm.ofs++;\n\t\t\t\t\t\tQCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_STORE_F], t, parm, NULL));\n\t\t\t\t\t\tparm.ofs++;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tQCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_STORE_F], extra_parms[p - MAX_PARMS], parm, NULL));\n\t\t\t\t\t\tparm.ofs++;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tp++;\n\t\t\t}\n\t\t\tQCC_FreeTemp(parm);\n\t\t}\n\n\t\tif (type->vargcount)\n\t\t{\n\t\t\tif (!pr_parm_argcount_name)\n\t\t\t\tQCC_Error(ERR_INTERNAL, \"I forgot what the va_count argument is meant to be called\");\n\t\t\telse\n\t\t\t{\n\t\t\t\tQCC_sref_t va_passcount = QCC_PR_GetSRef(type_float, \"__va_count\", NULL, true, 0, 0);\n\t\t\t\tQCC_sref_t va_count = QCC_PR_GetSRef(type_float, pr_parm_argcount_name, pr_scope, true, 0, GDF_SCANLOCAL);\n\t\t\t\tQCC_sref_t numparms = QCC_MakeFloatConst(type->num_parms);\n\t\t\t\tva_passcount.sym->referenced = true;\n\t\t\t\tQCC_PR_SimpleStatement(&pr_opcodes[OP_SUB_F], va_passcount, numparms, va_count, false);\n\t\t\t\tQCC_FreeTemp(numparms);\n\t\t\t\tQCC_FreeTemp(va_passcount);\n\t\t\t\tQCC_FreeTemp(va_count);\n\t\t\t}\n\t\t}\n\t}\n\tif (f->type->aux_type->size > type_vector->size)\n\tif (!mergeargs)\n\t{\t//okay, awkward... for large returns, the caller passed us a pointer via OFS_RETURN\n\t\tf->returndef = QCC_PR_GetSRef(QCC_PR_PointerType(f->type->aux_type), \"ret**\", f, true, 0, 0);\n\t\tQCC_PR_SimpleStatement(&pr_opcodes[OP_STORE_P], QCC_MakeSRefForce(&def_ret, 0, type_variant), f->returndef, nullsref, false);\n\t}\n\n\tif (type->vargs)\n\t{\n\t\tint i;\n\t\tint maxvacount = 24;\n\t\tQCC_sref_t a;\n\t\t//if we have opcode extensions, we can use those instead of via a function. this allows to use proper locals for the vargs.\n\t\t//otherwise we have to use static/globals instead, so that the child function can access them without clobbering. FIXME: this is silly. we should be able to bias our locals to avoid stomping the array access' locals\n\t\tpbool opcodeextensions = QCC_OPCodeValid(&pr_opcodes[OP_FETCH_GBL_F]) || QCC_OPCodeValid(&pr_opcodes[OP_LOADA_F]);\n\t\tQCC_sref_t va_list;\n\t\tva_list = QCC_PR_GetSRef(type_vector, \"__va_list\", pr_scope, true, maxvacount, GDF_SCANLOCAL|(opcodeextensions?0:GDF_STATIC));\n\t\tif (QCC_OPCodeValid(&pr_opcodes[OP_FETCH_GBL_F]) && !QCC_OPCodeValid(&pr_opcodes[OP_LOADA_F]))\n\t\t\tva_list.sym->arraylengthprefix = true;\n\t\tif (!mergeargs)\n\t\t{\n\t\t\tfor (i = 0; i < maxvacount; i++)\n\t\t\t{\n\t\t\t\tQCC_ref_t varef;\n\t\t\t\tu = i + type->num_parms;\n\t\t\t\tif (u >= MAX_PARMS)\n\t\t\t\t{\n\t\t\t\t\tif (!extra_parms[u - MAX_PARMS].sym)\n\t\t\t\t\t{\n\t\t\t\t\t\tchar name[128];\n\t\t\t\t\t\tQC_snprintfz(name, sizeof(name), \"$parm%u\", u);\n\t\t\t\t\t\textra_parms[u - MAX_PARMS] = QCC_PR_GetSRef(type_vector, name, NULL, true, 0, GDF_STRIP);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\tQCC_ForceUnFreeDef(extra_parms[u - MAX_PARMS].sym);\n\t\t\t\t\ta = extra_parms[u - MAX_PARMS];\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\ta.sym = &def_parms[u];\n\t\t\t\t\ta.ofs = 0;\n\t\t\t\t\tQCC_ForceUnFreeDef(a.sym);\n\t\t\t\t}\n\t\t\t\ta.cast = type_vector;\n\t\t\t\tQCC_UnFreeTemp(va_list);\n\t\t\t\tQCC_StoreSRefToRef(QCC_PR_BuildRef(&varef, REF_ARRAY, va_list, QCC_MakeIntConst(i*3), type_vector, false, 0), a, false, false);\n\t\t\t}\n\t\t}\n\t\tQCC_FreeTemp(va_list);\n\t}\n\n\tif (pif_flags & (PIF_WRAP|PIF_AUTOWRAP))\n\t{\t//if we're wrapping, then we moved the old function entry to the end and reused it for our function.\n\t\t//so we need to define some local that refers to the prior def.\n\t\tint funcref = numfunctions-1;\n\t\tQCC_sref_t priorim = QCC_MakeUniqueConst(type_function, &funcref);\n\t\tpriorim.sym->referenced = true;\n\t\tpriorim.cast = f->type;\n\t\tprior = QCC_PR_DummyDef(f->type, \"prior\", f, 0, priorim.sym, 0, true, GDF_CONST|GDF_STATIC|GDF_SCANLOCAL);\t//create a union into it\n\t\tprior->initialized = true;\n\t\tprior->filen = functions[numfunctions-1].filen;\n\t\tprior->s_filed = functions[numfunctions-1].s_filed;\n\t\tprior->s_line = functions[numfunctions-1].line;\n\n\t\tQCC_FreeTemp(priorim);\n\t}\n\n\t/*if (pr_classtype)\n\t{\n\t\tQCC_def_t *e, *e2;\n\t\te = QCC_PR_GetDef(pr_classtype, \"__oself\", pr_scope, true, 0);\n\t\te2 = QCC_PR_GetDef(type_entity, \"self\", NULL, true, 0);\n\t\tQCC_FreeTemp(QCC_PR_Statement(&pr_opcodes[OP_STORE_ENT], QCC_PR_DummyDef(pr_classtype, \"self\", pr_scope, 0, e2->ofs, false), e, NULL));\n\t}*/\n\n\tlastparm = pr.local_tail;\n\n//\n// check for a state opcode\n//\n\tif (QCC_PR_CheckToken (\"[\"))\n\t\tQCC_PR_ParseState ();\n\n\t//accumulate implies wrap when the first function wasn't defined to accumulate (should really be explicit, but gmqcc compat)\n\tif (pif_flags & PIF_AUTOWRAP)\n\t{\n\t\tQCC_def_t *arg;\n\t\tQCC_sref_t args[MAX_PARMS+MAX_EXTRA_PARMS];\n\t\tQCC_sref_t r;\n\t\tunsigned int i;\n\t\tfor (i=0, arg=pr.local_head.nextlocal ; i<type->num_parms; i++, arg = arg->deftail->nextlocal)\n\t\t{\n\t\t\tQCC_ForceUnFreeDef(arg);\n\t\t\targs[i].sym = arg;\n\t\t\targs[i].ofs = 0;\n\t\t\targs[i].cast = type->params[i].type;\n\t\t}\n\t\tr = QCC_PR_GenerateFunctionCallSref(nullsref, QCC_MakeSRefForce(prior, 0, prior->type), args, type->num_parms);\n\t\tprior->referenced = true;\n\n\t\tif (f->type->aux_type->type == ev_void)\n\t\t\tQCC_FreeTemp(r);\n\t\telse\n\t\t{\n\t\t\tif (!f->returndef.cast)\n\t\t\t\tf->returndef = QCC_PR_GetSRef(f->type->aux_type, \"ret*\", pr_scope, true, 0, 0);\n\t\t\tQCC_StoreToSRef(f->returndef, r, type, false, false);\n\t\t}\n\t}\n\n\tif (QCC_PR_CheckKeyword (keyword_asm, \"asm\"))\n\t{\n\t\tQCC_PR_Expect (\"{\");\n\t\twhile (STRCMP (\"}\", pr_token))\n\t\t\tQCC_PR_ParseAsm ();\n\t}\n\telse\n\t{\n\t\tif (!(pif_flags & PIF_ACCUMULATE) && QCC_PR_CheckKeyword (keyword_var, \"var\"))\t//reacc support\n\t\t{\t//parse lots of locals\n\t\t\tchar *name;\n\t\t\tdo {\n\t\t\t\tname = QCC_PR_ParseName();\n\t\t\t\tQCC_PR_Expect(\":\");\n\t\t\t\tQCC_FreeDef(QCC_PR_GetDef(QCC_PR_ParseType(false, false, false), name, pr_scope, true, 0, false));\n\t\t\t\tQCC_PR_Expect(\";\");\n\t\t\t} while(!QCC_PR_CheckToken(\"{\"));\n\t\t}\n\t\telse\n\t\t\tQCC_PR_Expect (\"{\");\n//\n// parse regular statements\n//\n\t\twhile (STRCMP (\"}\", pr_token))\t//not check token to avoid the lex consuming following pragmas\n\t\t{\n\t\t\tQCC_PR_ParseStatement ();\n\t\t\tQCC_FreeTemps();\n\t\t}\n\t}\n\n\tQCC_FreeTemps();\n\n\tif (prior && !prior->referenced)\n\t\tQCC_PR_ParseError(ERR_REDECLARATION, \"Wrapper function \\\"%s\\\" does not refer to its prior function\", def->name);\n\n\tpr_token_line_last = pr_token_line;\n\n\t// this is cheap\n//\tif (type->aux_type->type)\n//\t\tif (statements[numstatements - 1].op != OP_RETURN)\n//\t\t\tQCC_PR_ParseWarning(WARN_MISSINGRETURN, \"%s: not all control paths return a value\", pr_scope->name );\n\n\tif (num_gotos)\n\t{\n\t\tint i, j;\n\t\tfor (i = 0; i < num_gotos; i++)\n\t\t{\n\t\t\tfor (j = 0; j < num_labels; j++)\n\t\t\t{\n\t\t\t\tif (!strcmp(pr_gotos[i].name, pr_labels[j].name))\n\t\t\t\t{\n\t\t\t\t\tif (!pr_opcodes[statements[pr_gotos[i].statementno].op].type_a)\n\t\t\t\t\t\tstatements[pr_gotos[i].statementno].a.ofs += pr_labels[j].statementno - pr_gotos[i].statementno;\n\t\t\t\t\telse if (!pr_opcodes[statements[pr_gotos[i].statementno].op].type_b)\n\t\t\t\t\t\tstatements[pr_gotos[i].statementno].b.ofs += pr_labels[j].statementno - pr_gotos[i].statementno;\n\t\t\t\t\telse\n\t\t\t\t\t\tstatements[pr_gotos[i].statementno].c.ofs += pr_labels[j].statementno - pr_gotos[i].statementno;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (j == num_labels)\n\t\t\t{\n\t\t\t\tnum_gotos = 0;\n\t\t\t\tQCC_PR_ParseError(ERR_NOLABEL, \"Goto statement with no matching label \\\"%s\\\"\", pr_gotos[i].name);\n\t\t\t}\n\t\t}\n\t\tnum_gotos = 0;\n\t}\n\n\tf->line_end = pr_token_line_last;\n\tif (1)//(pif_flags & PIF_ACCUMULATE) || pr_scope->parentscope)\n\t{\t//FIXME: should probably always take this path, but kinda pointless until we have relocs for defs\n\t\tQCC_RemapLockedTemps(f->code, numstatements);\n\t\tQCC_Marshal_Locals(f->code, numstatements);\n//\t\tQCC_WriteAsmFunction(f, f->code, f->firstlocal);\t//FIXME: this will print the entire function, not just the part that we added. and we'll print it all again later, too. should probably make it a function attribute that we check at the end.\n\n\t\tf->numstatements = numstatements - f->code;\n\t\tf->statements = qccHunkAlloc(sizeof(*statements)*f->numstatements);\n\t\tmemcpy(f->statements, statements+f->code, sizeof(*statements) * f->numstatements);\n\t\tnumstatements = f->code;\n\t}\n\telse\n\t{\n\t\tQCC_PR_FinaliseFunction(f);\n\t}\n\tpr_scope = NULL;\n\n\tif (num_labels)\n\t\tnum_labels = 0;\n\n\tif (num_cases)\n\t{\n\t\tnum_cases = 0;\n\t\tQCC_PR_ParseError(ERR_ILLEGALCASES, \"%s: function contains illegal cases\", f->name);\n\t}\n\tif (num_continues)\n\t{\n\t\tnum_continues=0;\n\t\tQCC_PR_ParseError(ERR_ILLEGALCONTINUES, \"%s: function contains illegal continues\", f->name);\n\t}\n\tif (num_breaks)\n\t{\n\t\tnum_breaks=0;\n\t\tQCC_PR_ParseError(ERR_ILLEGALBREAKS, \"%s: function contains illegal breaks\", f->name);\n\t}\n\n\t//clean up the locals. remove parms from the hashtable but don't clean subscoped_away so that we can repopulate on the next accumulation\n\tfor (d = f->firstlocal; d != lastparm->nextlocal; d = d->nextlocal)\n\t{\n\t\tif (!d->subscoped_away)\n\t\t\tpHash_RemoveData(&localstable, d->name, d);\n\t}\n\t//any non-arg locals defined within the accumulation should not be visible next time around.\n\tfor (; d; d = d->nextlocal)\n\t{\n\t\tif (!d->subscoped_away)\n\t\t{\n\t\t\tpHash_RemoveData(&localstable, d->name, d);\n\t\t\td->subscoped_away = true;\n\t\t}\n\t}\n#if 0//def _DEBUG\n\tfor (u = 0; u < localstable.numbuckets; u++)\n\t{\n\t\tif (localstable.bucket[u])\n\t\t\tlocalstable.bucket[u] = NULL;\n\t}\n#endif\n\n\tfor (; startingtypes < numtypeinfos; startingtypes++)\n\t{\n\t\tif (qcc_typeinfo[startingtypes].typedefed)\n\t\t{\n\t\t\tqcc_typeinfo[startingtypes].typedefed = false;\n\t\t\tpHash_RemoveData(&typedeftable, qcc_typeinfo[startingtypes].name, &qcc_typeinfo[startingtypes]);\n\t\t}\n\t}\n\n\tQCC_PR_Lex();\n\n\treturn f;\n}\n\nstatic void QCC_PR_ArrayRecurseDivideRegular(QCC_sref_t array, QCC_sref_t index, int min, int max)\n{\n\tQCC_statement_t *st;\n\tQCC_sref_t eq;\n\tint stride;\n\n\tif (array.cast->type == ev_vector)\n\t\tstride = 3;\n\telse\n\t\tstride = 1;\t//struct arrays should be 1, so that every element can be accessed...\n\n\tif (min == max || min+1 == max)\n\t{\n\t\teq = QCC_PR_StatementFlags(pr_opcodes+OP_LT_F, index, QCC_MakeFloatConst(min+1), NULL, STFL_PRESERVEA);\n\t\tQCC_FreeTemp(QCC_PR_Statement(pr_opcodes+OP_IFNOT_I, eq, nullsref, &st));\n\t\tst->b.ofs = 2;\n\t\tQCC_PR_Statement(pr_opcodes+OP_RETURN, array, nullsref, &st);\n\t\tst->a.ofs += min*stride;\n\t}\n\telse\n\t{\n\t\tint mid = min + (max-min)/2;\n\n\t\tif (max-min>4)\n\t\t{\n\t\t\teq = QCC_PR_StatementFlags(pr_opcodes+OP_LT_F, index, QCC_MakeFloatConst(mid+1), NULL, STFL_PRESERVEA);\n\t\t\tQCC_FreeTemp(QCC_PR_Statement(pr_opcodes+OP_IFNOT_I, eq, nullsref, &st));\n\t\t}\n\t\telse\n\t\t\tst = NULL;\n\t\tQCC_PR_ArrayRecurseDivideRegular(array, index, min, mid);\n\t\tif (st)\n\t\t\tst->b.jumpofs = numstatements - (st-statements);\n\t\tQCC_PR_ArrayRecurseDivideRegular(array, index, mid, max);\n\t}\n}\n\n//the idea here is that we return a vector, the caller then figures out the extra 3rd.\n//This is useful when we have a load of indexes.\nstatic void QCC_PR_ArrayRecurseDivideUsingVectors(QCC_sref_t array, QCC_sref_t index, int min, int max)\n{\n\tQCC_statement_t *st;\n\tQCC_sref_t eq;\n\tif (min == max || min+1 == max)\n\t{\n\t\teq = QCC_PR_StatementFlags(pr_opcodes+OP_LT_F, index, QCC_MakeFloatConst(min+1), NULL, STFL_PRESERVEA);\n\t\tQCC_FreeTemp(QCC_PR_Statement(pr_opcodes+OP_IFNOT_I, eq, nullsref, &st));\n\t\tst->b.ofs = 2;\n\t\tQCC_PR_Statement(pr_opcodes+OP_RETURN, array, nullsref, &st);\n\t\tst->a.ofs += min*3;\n\t}\n\telse\n\t{\n\t\tint mid = min + (max-min)/2;\n\n\t\tif (max-min>4)\n\t\t{\n\t\t\teq = QCC_PR_StatementFlags(pr_opcodes+OP_LT_F, index, QCC_MakeFloatConst(mid+1), NULL, STFL_PRESERVEA);\n\t\t\tQCC_FreeTemp(QCC_PR_Statement(pr_opcodes+OP_IFNOT_I, eq, nullsref, &st));\n\t\t}\n\t\telse\n\t\t\tst = NULL;\n\t\tQCC_PR_ArrayRecurseDivideUsingVectors(array, index, min, mid);\n\t\tif (st)\n\t\t\tst->b.jumpofs = numstatements - (st-statements);\n\t\tQCC_PR_ArrayRecurseDivideUsingVectors(array, index, mid, max);\n\t}\n}\n\n//returns a vector overlapping the result needed.\nQCC_def_t *QCC_PR_EmitArrayGetVector(QCC_sref_t array)\n{\n\tQCC_sref_t temp, index;\n\tQCC_def_t *func;\n\n\tint numslots;\n\n\tQCC_type_t *ftype = qccHunkAlloc(sizeof(*ftype));\n\tstruct QCC_typeparam_s *fparms = qccHunkAlloc(sizeof(*fparms)*1);\n\tftype->size = 1;\n\tftype->type = ev_function;\n\tftype->aux_type = type_vector;\n\tftype->params = fparms;\n\tftype->num_parms = 1;\n\tftype->name = \"ArrayGet\";\n\tfparms[0].type = type_float;\n\n\t//array shouldn't ever be a vector array\n\tnumslots = array.sym->arraysize*array.cast->size;\n\tnumslots = (numslots+2)/3;\n\n\ts_filen = array.sym->filen;\n\ts_filed = array.sym->s_filed;\n\tfunc = QCC_PR_GetDef(ftype, qcva(\"ArrayGetVec*%s\", array.sym->name), NULL, true, 0, false);\n\n\tpr_source_line = pr_token_line_last = array.sym->s_line;\t//thankfully these functions are emitted after compilation.\n\n\tif (numfunctions >= MAX_FUNCTIONS)\n\t\tQCC_Error(ERR_INTERNAL, \"Too many function defs\");\n\n\tpr_scope = QCC_PR_GenerateQCFunction(func, ftype, NULL);\n\tpr_source_line = pr_token_line_last = pr_scope->line = array.sym->s_line;\t//thankfully these functions are emitted after compilation.\n\tpr_scope->filen = array.sym->filen;\n\tpr_scope->s_filed = array.sym->s_filed;\n\tfunc->symboldata[0]._int = pr_scope - functions;\n\n\tindex = QCC_PR_GetSRef(type_float, \"index___\", pr_scope, true, 0, false);\n\tindex.sym->referenced = true;\n\ttemp = QCC_PR_GetSRef(type_float, \"div3___\", pr_scope, true, 0, false);\n\tQCC_PR_SimpleStatement(pr_opcodes+OP_DIV_F, index, QCC_MakeFloatConst(3), temp, false);\n\tQCC_PR_SimpleStatement(pr_opcodes+OP_BITAND_F, temp, temp, temp, false);//round down to int\n\n\tQCC_PR_ArrayRecurseDivideUsingVectors(array, temp, 0, numslots);\n\n\tQCC_PR_Statement(pr_opcodes+OP_RETURN, QCC_MakeVectorConst(0,0,0), nullsref, NULL);\t//err... we didn't find it, give up.\n\tQCC_PR_Statement(pr_opcodes+OP_DONE, nullsref, nullsref, NULL);\t//err... we didn't find it, give up.\n\n\tfunc->initialized = 1;\n\n\n\tQCC_WriteAsmFunction(pr_scope, pr_scope->code, pr_scope->firstlocal);\n\tQCC_Marshal_Locals(pr_scope->code, numstatements);\n\tQCC_FreeTemps();\n\n\treturn func;\n}\n\nvoid QCC_PR_EmitArrayGetFunction(QCC_def_t *scope, QCC_def_t *arraydef, char *arrayname)\n{\n\tQCC_sref_t vectortrick;\n\tQCC_sref_t index, thearray = QCC_MakeSRefForce(arraydef, 0, arraydef->type);\n\n\tQCC_statement_t *st;\n\tQCC_sref_t eq;\n\tQCC_statement_t *bc1=NULL, *bc2=NULL;\n\n//\tQCC_sref_t fasttrackpossible = nullsref;\n\tint numslots;\n\n\tnumslots = thearray.sym->arraysize;\n\tif (!numslots)\n\t\tnumslots = 1;\n\tif (thearray.cast->type != ev_vector)\n\t\tnumslots *= thearray.cast->size;\n\n//\tif (flag_fasttrackarrays && numslots > 6)\n//\t\tfasttrackpossible = QCC_PR_GetSRef(type_float, \"__ext__fasttrackarrays\", NULL, true, 0, false);\n\n\ts_filen = scope->filen;\n\ts_filed = scope->s_filed;\n\n\tvectortrick = nullsref;\n//\tif (numslots >= 15 && thearray.cast->type != ev_vector)\n//\t{\n//\t\tvectortrick.sym = QCC_PR_EmitArrayGetVector(thearray);\n//\t\tvectortrick.cast = vectortrick.sym->type;\n//\t}\n\n\tpr_scope = QCC_PR_GenerateQCFunction(scope, scope->type, NULL);\n\tpr_source_line = pr_token_line_last = pr_scope->line = thearray.sym->s_line;\t//thankfully these functions are emitted after compilation.\n\tpr_scope->filen = thearray.sym->filen;\n\tpr_scope->s_filed = thearray.sym->s_filed;\n\n\tindex = QCC_PR_GetSRef(type_float, \"__indexg\", pr_scope, true, 0, GDF_PARAMETER);\n\n\tscope->initialized = true;\n\tscope->symboldata[0]._int = pr_scope - functions;\n\n/*\tif (fasttrackpossible)\n\t{\n\t\tQCC_PR_Statement(pr_opcodes+OP_IFNOT_I, fasttrackpossible, nullsref, &st);\n\t\t//fetch_gbl takes: (float size, variant array[]), float index, variant pos\n\t\t//note that the array size is coded into the globals, one index before the array.\n\n\t\tif (thearray.cast->type == ev_vector)\n\t\t\tQCC_PR_SimpleStatement(&pr_opcodes[OP_FETCH_GBL_V], thearray, index, sref_ret, true);\n\t\telse\n\t\t\tQCC_PR_SimpleStatement(&pr_opcodes[OP_FETCH_GBL_F], thearray, index, sref_ret, true);\n\n\t\tQCC_FreeTemp(QCC_PR_Statement(&pr_opcodes[OP_RETURN], sref_ret, nullsref, NULL));\n\n\t\t//finish the jump\n\t\tst->b.ofs = &statements[numstatements] - st;\n\t}\n*/\n\n\tif (flag_boundchecks)\n\t\tQCC_FreeTemp(QCC_PR_Statement(pr_opcodes+OP_IF_I, QCC_PR_StatementFlags(pr_opcodes+OP_LT_F, index, QCC_MakeFloatConst(0), NULL, STFL_PRESERVEA), nullsref, &bc1));\n\n\tif (vectortrick.cast)\n\t{\n\t\tQCC_sref_t div3, intdiv3, ret;\n\n\t\t//okay, we've got a function to retrieve the var as part of a vector.\n\t\t//we need to work out which part, x/y/z that it's stored in.\n\t\t//0,1,2 = i - ((int)i/3 *) 3;\n\n\t\tif (flag_boundchecks)\n\t\t\tQCC_FreeTemp(QCC_PR_Statement(pr_opcodes+OP_IF_I, QCC_PR_StatementFlags(pr_opcodes+OP_GE_F, index, QCC_MakeFloatConst(numslots), NULL, STFL_PRESERVEA), nullsref, &bc2));\n\n\t\tdiv3 = QCC_PR_GetSRef(type_float, \"div3___\", pr_scope, true, 0, false);\n\t\tintdiv3 = QCC_PR_GetSRef(type_float, \"intdiv3___\", pr_scope, true, 0, false);\n\n\t\tdiv3.sym->referenced = true;\n\t\tQCC_PR_SimpleStatement(pr_opcodes+OP_BITAND_F, index, index, index, false);\n\t\tQCC_PR_SimpleStatement(pr_opcodes+OP_DIV_F, index, QCC_MakeFloatConst(3), div3, false);\n\t\tQCC_PR_SimpleStatement(pr_opcodes+OP_BITAND_F, div3, div3, intdiv3, false);\n\n//\t\tret = QCC_PR_GenerateFunctionCall1(nullsref, floor, index, type_float);\n\n\t\tQCC_UnFreeTemp(index);\n\t\tret = QCC_PR_GenerateFunctionCall1(nullsref, vectortrick, index, type_float);\n\n\t\tdiv3 = QCC_PR_Statement(pr_opcodes+OP_MUL_F, intdiv3, QCC_MakeFloatConst(3), NULL);\n\t\tQCC_PR_SimpleStatement(pr_opcodes+OP_SUB_F, index, div3, index, false);\n\t\tQCC_FreeTemp(div3);\n\n\t\teq = QCC_PR_StatementFlags(pr_opcodes+OP_LT_F, index, QCC_MakeFloatConst(0+0.5f), NULL, STFL_PRESERVEA);\n\t\tQCC_FreeTemp(QCC_PR_Statement(pr_opcodes+OP_IFNOT_I, eq, nullsref, &st));\n\t\tst->b.ofs = 2;\n\t\tret.cast = type_float;\n\t\tQCC_PR_Statement(pr_opcodes+OP_RETURN, ret, nullsref, NULL);\n\t\tret.ofs++;\n\n\t\teq = QCC_PR_StatementFlags(pr_opcodes+OP_LT_F, index, QCC_MakeFloatConst(1+0.5f), NULL, STFL_PRESERVEA);\n\t\tQCC_FreeTemp(QCC_PR_Statement(pr_opcodes+OP_IFNOT_I, eq, nullsref, &st));\n\t\tst->b.ofs = 2;\n\t\tQCC_PR_Statement(pr_opcodes+OP_RETURN, ret, nullsref, NULL);\n\t\tret.ofs++;\n\n\t\teq = QCC_PR_StatementFlags(pr_opcodes+OP_LT_F, index, QCC_MakeFloatConst(2+0.5), NULL, STFL_PRESERVEA);\n\t\tQCC_FreeTemp(QCC_PR_Statement(pr_opcodes+OP_IFNOT_I, eq, nullsref, &st));\n\t\tst->b.ofs = 2;\n\t\tQCC_PR_Statement(pr_opcodes+OP_RETURN, ret, nullsref, NULL);\n\t\tret.ofs++;\n\t\tret.ofs-=3;\n\t\tQCC_FreeTemp(ret);\n\t}\n\telse\n\t{\n//\t\tQCC_PR_SimpleStatement(pr_opcodes+OP_BITAND_F, index, index, index, false);\n\t\tQCC_PR_ArrayRecurseDivideRegular(thearray, index, 0, numslots);\n\t}\n\n\tif (bc1)\n\t\tbc1->b.jumpofs = &statements[numstatements] - bc1;\n\tif (bc2)\n\t\tbc2->b.jumpofs = &statements[numstatements] - bc2;\n\tif (bc1 || bc2)\n\t{\n\t\tQCC_sref_t errfnc = QCC_PR_GetSRef(NULL, \"error\", NULL, false, 0, false);\n\t\tQCC_sref_t sprintffnc = QCC_PR_GetSRef(NULL, \"sprintf\", NULL, false, 0, false);\n\t\tQCC_sref_t errmsg;\n\t\tif (sprintffnc.cast)\n\t\t{\t//if sprintf is defined, we generate a more verbose error message. this message should appear in at least one of the engines the mod was made for, and in others it should be obivious enough.\n\t\t\tQCC_sref_t args[3];\n\t\t\targs[0] = QCC_MakeStringConst(\"bounds check failed (0 <= %g < %g is false)\\n\");\n\t\t\tQCC_UnFreeTemp(index);\n\t\t\targs[1] = index;\n\t\t\targs[2] = QCC_MakeFloatConst(numslots);\n\t\t\terrmsg = QCC_PR_GenerateFunctionCallSref(nullsref, sprintffnc, args, 3);\n\t\t}\n\t\telse\n\t\t\terrmsg = QCC_MakeStringConst(\"bounds check failed\\n\");\n\t\tif (!errfnc.cast)\n\t\t{\n\t\t\terrfnc = QCC_MakeIntConst(~0);\n\t\t\terrfnc.cast = type_function;\n\t\t}\n\t\tQCC_FreeTemp(QCC_PR_GenerateFunctionCall1(nullsref, errfnc, errmsg, type_string));\n\t}\n\tQCC_FreeTemp(index);\n\t//we get here if they tried reading beyond the end of the array with bounds checks disabled. just return the last valid element.\n\tif (thearray.cast->type == ev_vector)\n\t\tthearray.ofs += (numslots-1)*3;\n\telse\n\t\tthearray.ofs += (numslots-1);\n\tQCC_PR_Statement(pr_opcodes+OP_RETURN, thearray, nullsref, &st);\n\tQCC_PR_Statement(pr_opcodes+OP_DONE, nullsref, nullsref, NULL);\n\n\tQCC_WriteAsmFunction(pr_scope, pr_scope->code, pr_scope->firstlocal);\n\tQCC_Marshal_Locals(pr_scope->code, numstatements);\n\tQCC_FreeTemps();\n}\n\nstatic void QCC_PR_ArraySetRecurseDivide(QCC_sref_t array, QCC_sref_t index, QCC_sref_t value, int min, int max)\n{\n\tQCC_statement_t *st;\n\tQCC_sref_t eq;\n\tint stride;\n\n\tif (array.cast->type == ev_vector)\n\t\tstride = 3;\n\telse\n\t\tstride = 1;\t//struct arrays should be 1, so that every element can be accessed...\n\n\tif (min == max || min+1 == max)\n\t{\n\t\teq = QCC_PR_StatementFlags(pr_opcodes+OP_LT_F, index, QCC_MakeFloatConst(min+1), NULL, STFL_PRESERVEA);\n\t\tQCC_FreeTemp(QCC_PR_Statement(pr_opcodes+OP_IFNOT_I, eq, nullsref, &st));\n\t\tst->b.ofs = 3;\n\t\tif (stride == 3)\n\t\t\tQCC_PR_StatementFlags(pr_opcodes+OP_STORE_V, value, array, &st, STFL_PRESERVEB);\n\t\telse\n\t\t\tQCC_PR_StatementFlags(pr_opcodes+OP_STORE_F, value, array, &st, STFL_PRESERVEB);\n\t\tst->b.ofs += min*stride;\n\t\tQCC_PR_Statement(pr_opcodes+OP_RETURN, nullsref, nullsref, NULL);\n\t}\n\telse\n\t{\n\t\tint mid = min + (max-min)/2;\n\n\t\tif (max-min>4)\n\t\t{\n\t\t\teq = QCC_PR_StatementFlags(pr_opcodes+OP_LT_F, index, QCC_MakeFloatConst(mid+1), NULL, STFL_PRESERVEA);\n\t\t\tQCC_FreeTemp(QCC_PR_Statement(pr_opcodes+OP_IFNOT_I, eq, nullsref, &st));\n\t\t}\n\t\telse\n\t\t\tst = NULL;\n\t\tQCC_PR_ArraySetRecurseDivide(array, index, value, min, mid);\n\t\tif (st)\n\t\t\tst->b.jumpofs = numstatements - (st-statements);\n\t\tQCC_PR_ArraySetRecurseDivide(array, index, value, mid, max);\n\t}\n}\n\nvoid QCC_PR_EmitArraySetFunction(QCC_def_t *scope, QCC_def_t *arraydef, char *arrayname)\n{\n\tQCC_sref_t index, value, thearray = QCC_MakeSRefForce(arraydef, 0, arraydef->type);\n\n\tQCC_sref_t fasttrackpossible;\n\tint numslots;\n\tQCC_statement_t *bc1=NULL, *bc2=NULL;\n\n\tif (thearray.cast->type == ev_vector)\n\t\tnumslots = thearray.sym->arraysize;\n\telse\n\t\tnumslots = thearray.sym->arraysize*thearray.cast->size;\n\n\tfasttrackpossible = nullsref;\n\tif (flag_fasttrackarrays && numslots > 6)\n\t\tfasttrackpossible = QCC_PR_GetSRef(type_float, \"__ext__fasttrackarrays\", NULL, true, 0, false);\n\n\tif (numfunctions >= MAX_FUNCTIONS)\n\t\tQCC_Error(ERR_INTERNAL, \"Too many function defs\");\n\n\ts_filen = arraydef->filen;\n\ts_filed = arraydef->s_filed;\n\tpr_scope = QCC_PR_GenerateQCFunction(scope, scope->type, NULL);\n\tpr_source_line = pr_token_line_last = pr_scope->line = thearray.sym->s_line;\t//thankfully these functions are emitted after compilation.\n\tpr_scope->filen = thearray.sym->filen;\n\tpr_scope->s_filed = thearray.sym->s_filed;\n\n\tindex = QCC_PR_GetSRef(type_float, \"indexs___\", pr_scope, true, 0, GDF_PARAMETER);\n\tvalue = QCC_PR_GetSRef(thearray.cast, \"value___\", pr_scope, true, 0, GDF_PARAMETER);\n\n\tscope->initialized = true;\n\tscope->symboldata[0]._int = pr_scope - functions;\n\n\tif (fasttrackpossible.cast)\n\t{\n\t\tQCC_statement_t *st;\n\n\t\tQCC_PR_Statement(pr_opcodes+OP_IFNOT_I, fasttrackpossible, nullsref, &st);\n\t\t//note that the array size is coded into the globals, one index before the array.\n\n\t\tQCC_PR_SimpleStatement(&pr_opcodes[OP_CONV_FTOI], index, nullsref, index, true);\t//address stuff is integer based, but standard qc (which this accelerates in supported engines) only supports floats\n\t\tif (flag_boundchecks)\n\t\t\tQCC_PR_SimpleStatement (&pr_opcodes[OP_BOUNDCHECK], index, QCC_MakeSRef(NULL, numslots, NULL), nullsref, true);//annoy the programmer. :p\n\t\tif (thearray.cast->type == ev_vector)//shift it upwards for larger types\n\t\t\tQCC_PR_SimpleStatement(&pr_opcodes[OP_MUL_I], index, QCC_MakeIntConst(thearray.cast->size), index, true);\n\t\tQCC_PR_SimpleStatement(&pr_opcodes[OP_GLOBALADDRESS], thearray, index, index, true);\t//comes with built in add\n\t\tif (thearray.cast->type == ev_vector)\n\t\t\tQCC_PR_SimpleStatement(&pr_opcodes[OP_STOREP_V], value, index, nullsref, true);\t//*b = a\n\t\telse\n\t\t\tQCC_PR_SimpleStatement(&pr_opcodes[OP_STOREP_F], value, index, nullsref, true);\n\t\tQCC_PR_Statement(&pr_opcodes[OP_RETURN], value, nullsref, NULL);\n\n\t\t//finish the jump\n\t\tst->b.jumpofs = &statements[numstatements] - st;\n\t}\n\n\tif (flag_boundchecks)\n\t\tQCC_FreeTemp(QCC_PR_Statement(pr_opcodes+OP_IF_I, QCC_PR_StatementFlags(pr_opcodes+OP_LT_F, index, QCC_MakeFloatConst(0), NULL, STFL_PRESERVEA), nullsref, &bc1));\n\tif (flag_boundchecks)\n\t\tQCC_FreeTemp(QCC_PR_Statement(pr_opcodes+OP_IF_I, QCC_PR_StatementFlags(pr_opcodes+OP_GE_F, index, QCC_MakeFloatConst(numslots), NULL, STFL_PRESERVEA), nullsref, &bc2));\n\n\tQCC_PR_ArraySetRecurseDivide(thearray, index, value, 0, numslots);\n\n\tif (bc1)\n\t\tbc1->b.jumpofs = &statements[numstatements] - bc1;\n\tif (bc2)\n\t\tbc2->b.jumpofs = &statements[numstatements] - bc2;\n\tif (bc1 || bc2)\n\t{\n\t\tQCC_sref_t errfnc = QCC_PR_GetSRef(NULL, \"error\", NULL, false, 0, false);\n\t\tQCC_sref_t errmsg = QCC_MakeStringConst(\"bounds check failed\\n\");\n\t\tif (!errfnc.cast)\n\t\t{\n\t\t\terrfnc = QCC_MakeIntConst(~0);\n\t\t\terrfnc.cast = type_function;\n\t\t}\n\t\tQCC_FreeTemp(QCC_PR_GenerateFunctionCall1(nullsref, errfnc, errmsg, type_string));\n\t}\n\n\tQCC_PR_Statement(pr_opcodes+OP_DONE, nullsref, nullsref, NULL);\n\n\n\n\tQCC_WriteAsmFunction(pr_scope, pr_scope->code, pr_scope->firstlocal);\n\tQCC_Marshal_Locals(pr_scope->code, numstatements);\n\tQCC_FreeTemps();\n}\n\n//register a def, and all of it's sub parts.\n//only the main def is of use to the compiler.\n//the subparts are emitted to the compiler and allow correct saving/loading\n//be careful with fields, this doesn't allocated space, so will it allocate fields. It only creates defs at specified offsets.\nQCC_def_t *QCC_PR_DummyDef(QCC_type_t *type, const char *name, QCC_function_t *scope, int arraysize, QCC_def_t *rootsymbol, unsigned int ofs, int referable, unsigned int flags)\n{\n\tchar array[64];\n\tchar newname[256];\n\tint a;\n\tQCC_def_t *def, *first=NULL;\n\tchar typebuf[1024];\n\tpbool redec;\n\n\twhile (rootsymbol && rootsymbol->symbolheader != rootsymbol)\n\t{\n\t\tofs += rootsymbol->ofs;\n\t\trootsymbol = rootsymbol->symbolheader;\n\t}\n\n#define KEYWORD(x) if (!STRCMP(name, #x) && keyword_##x) do{if (keyword_##x)QCC_PR_ParseWarning(WARN_KEYWORDDISABLED, \"\\\"\"#x\"\\\" keyword used as variable name%s\", keywords_coexist?\" - coexisting\":\" - disabling\");keyword_##x=keywords_coexist;}while(0)\n\tif (name)\n\t{\n\t\tKEYWORD(var);\n\t\tKEYWORD(thinktime);\n\t\tKEYWORD(for);\n\t\tKEYWORD(switch);\n\t\tKEYWORD(case);\n\t\tKEYWORD(local);\n\t\tKEYWORD(default);\n\t\tKEYWORD(goto);\n\t\tif (type->type != ev_function)\n\t\t\tKEYWORD(break);\n\t\tKEYWORD(continue);\n\t\tKEYWORD(state);\n\t\tKEYWORD(string);\n\t\tif (qcc_targetformat != QCF_HEXEN2 && qcc_targetformat != QCF_UHEXEN2)\n\t\t\tKEYWORD(float);\t//hmm... hexen2 requires this...\n\t\tKEYWORD(entity);\n\t\tKEYWORD(vector);\n\t\tKEYWORD(const);\n\t\tKEYWORD(asm);\n\t}\n\n\tif (!type)\n\t\treturn NULL;\n\n\tfor (a = -1; a < arraysize||a==-1; a++)\n\t{\n\t\tif (a == -1)\n\t\t\t*array = '\\0';\n\t\telse\n\t\t\tQC_snprintfz(array, sizeof(array), \"[%i]\", a);\n\n\t\tif (name)\n\t\t\tQC_snprintfz(newname, sizeof(newname), \"%s%s\", name, array);\n\t\telse\n\t\t\tQC_snprintfz(newname, sizeof(newname), \"%s\", array);\n\n\t\t// allocate a new def\n\t\tif (a == -1 && rootsymbol && !rootsymbol->symbolsize)\n\t\t{\t//we had a prototype, but now we get to define everything else.\n\t\t\tdef = rootsymbol;\n\t\t\treferable = false; //should already be added.\n\t\t\tredec = true;\n\n\t\t\tif (def->constant != !!(flags & GDF_CONST) ||\n\t\t\t\tdef->isstatic != !!(flags & GDF_STATIC) ||\n\t\t\t\tdef->isparameter != !!(flags & GDF_PARAMETER) ||\n\t\t\t\tdef->autoderef != !!(flags & GDF_AUTODEREF))\n\t\t\t\tQCC_PR_ParseErrorPrintDef (ERR_TYPEMISMATCHREDEC, def, \"%s%s%s %s%s%s redeclared with different storage type\", col_type,TypeName(type, typebuf, sizeof(typebuf)),col_none, col_symbol,name,col_none);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tdef = (void *)qccHunkAlloc (sizeof(QCC_def_t));\n\t\t\tmemset (def, 0, sizeof(*def));\n\t\t\tdef->next = NULL;\n\n\t\t\tdef->name = (void *)qccHunkAlloc (strlen(newname)+1);\n\t\t\tstrcpy (def->name, newname);\n\t\t\tredec = false;\n\t\t}\n\t\tdef->arraysize = a>=0?0:arraysize;\n\n\t\tdef->s_line = pr_source_line;\n\t\tdef->unitn = s_unitn;\n\t\tdef->filen = s_filen;\n\t\tdef->s_filed = s_filed;\n\t\tif (a>=0)\n\t\t\tdef->initialized = 1;\n\n\t\tdef->type = type;\n\n\t\tdef->scope = scope;\n\t\tdef->constant = !!(flags & GDF_CONST);\n\t\tdef->isstatic = !!(flags & GDF_STATIC);\n\t\tdef->isparameter = !!(flags & GDF_PARAMETER);\n\t\tdef->autoderef = !!(flags & GDF_AUTODEREF);\t//should be a pointer type...\n\t\tif (arraysize && a < 0)\n\t\t{\t//array headers can be stripped safely.\n\t\t\tdef->saved = false;\n\t\t\tdef->strip = true;\n\t\t\tdef->arraylengthprefix = !rootsymbol && !def->localscope && QCC_OPCodeValid(&pr_opcodes[OP_FETCH_GBL_F]);\t//only prefix arrays with a length if its not an embedded symbol, its not a (true)local, and if there's actually a point to doing it.\n\t\t}\n\t\telse\n\t\t{\n\t\t\tdef->saved = (!arraysize || a>=0) && !!(flags & GDF_SAVED);\t//saved is never set on the head def in an array.\n\t\t\tdef->strip = !!(flags & GDF_STRIP);\n\t\t\tdef->arraylengthprefix = false;\n\t\t}\n\t\tdef->allowinline = !!(flags & GDF_INLINE);\n\n\t\tif (flags & GDF_USED)\n\t\t{\n\t\t\tdef->nostrip = true;\n\t\t\tdef->used = true;\n\t\t\tdef->referenced = true;\n\t\t}\n\n\t\tdef->ofs = ofs + ((a>0)?type->size*a:0);\n\t\tif (!first)\n\t\t\tfirst = def;\n\t\tif (!rootsymbol || (def==rootsymbol && !rootsymbol->symboldata))\n\t\t{\n\t\t\tif (!rootsymbol)\n\t\t\t{\n\t\t\t\trootsymbol = first;\n\t\t\t\trootsymbol->deftail = rootsymbol;\n\t\t\t}\n\t\t\tif ((flags & GDF_POSTINIT) || arraysize<0 || !type->size)\n\t\t\t\trootsymbol->symboldata = NULL;\n\t\t\telse\n\t\t\t\trootsymbol->symboldata = qccHunkAlloc ((def->arraysize?def->arraysize:1) * type->size * sizeof(float));\n\t\t}\n\n\t\tif (redec)\n\t\t\t;\t//symbol already linked.\n\t\telse if (rootsymbol != def && !(flags&GDF_ALIAS))\n\t\t{\t//we're inserting the symbol into the 'middle' of the parent after the fact. so this is kinda messy.\n\t\t\tif (!rootsymbol->deftail)\n\t\t\t\trootsymbol->deftail = rootsymbol;\n\t\t\tdef->next = rootsymbol->deftail->next;\n\t\t\trootsymbol->deftail->next = def;\n\t\t\tif (pr.def_tail == rootsymbol->deftail)\n\t\t\t\tpr.def_tail = def;\t//urgh\n\n\t\t\tif (scope)// && !(flags & (GDF_CONST|GDF_STATIC)))\n\t\t\t{\t//constants are never considered locals. static (local) variables are also not counted as locals as they're logically globals in all regards other than visibility.\n\n\t\t\t\t//just insert it at the start. who cares.\n\t\t\t\tdef->nextlocal = rootsymbol->deftail->nextlocal;\n\t\t\t\trootsymbol->deftail->nextlocal = def;\n\t\t\t\tif (pr.local_tail == rootsymbol->deftail)\n\t\t\t\t\tpr.local_tail = def;\n\t\t\t\tdef->localscope = true;\n\t\t\t}\n\n\t\t\trootsymbol->deftail = def;\n\t\t\tfirst->deftail = def;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tpr.def_tail->next = def;\n\t\t\tpr.def_tail = def;\n\t\t\tfirst->deftail = def;\n\n\t\t\tif (scope)// && !(flags & (GDF_CONST|GDF_STATIC)))\n\t\t\t{\t//constants are never considered locals. static (local) variables are also not counted as locals as they're logically globals in all regards other than visibility.\n\t\t\t\tpr.local_tail->nextlocal = def;\n\t\t\t\tpr.local_tail = def;\n\t\t\t\tdef->localscope = true;\n\t\t\t}\n\t\t}\n\n\t\tdef->symbolheader = rootsymbol;\n\t\tif (arraysize < 0 || !type->size)\n\t\t\tbreak;\t//don't define the rest if the struct/union ain't defined properly yet\n\n\t\tdef->symboldata = (rootsymbol->symboldata?rootsymbol->symboldata + def->ofs:NULL);\n\t\tif (type->bits)\n\t\t\tdef->symbolsize = (((def->arraysize?def->arraysize:1) * ((type->bits+7)/8))+(VMWORDSIZE-1))/VMWORDSIZE;\n\t\telse\n\t\t\tdef->symbolsize = (def->arraysize?def->arraysize:1) * type->size;\n\n\t\tif ((type->type == ev_struct||type->type == ev_union) && (!arraysize || a>=0))\n\t\t{\n\t\t\tunsigned int partnum;\n\t\t\tQCC_type_t *parttype;\n\t\t\tdef->saved = false;\t//struct headers don't get saved.\n\t\t\tfor (partnum = 0; partnum < (type->type == ev_union?max(1,type->num_parms):type->num_parms); partnum++)\n\t\t\t{\n\t\t\t\tparttype = type->params[partnum].type;\n\t\t\t\twhile (parttype->type == ev_accessor)\n\t\t\t\t\tparttype = parttype->parentclass;\n\t\t\t\tswitch (parttype->type)\n\t\t\t\t{\n\t\t\t\tcase ev_vector:\n\t\t\t\t\tQC_snprintfz(newname, sizeof(newname), \"%s.%s\", def->name, type->params[partnum].paramname);\n\t\t\t\t\tQCC_PR_DummyDef(parttype, newname, scope, type->params[partnum].arraysize, rootsymbol, def->ofs+type->params[partnum].ofs, false, flags);\n\n//\t\t\t\t\tQC_snprintfz(newname, sizeof(newname), \"%s.%s_x\", def->name, type->params[partnum].paramname);\n//\t\t\t\t\tQCC_PR_DummyDef(type_float, newname, scope, 0, rootsymbol, type->params[partnum].ofs - rootsymbol->ofs, false, flags | GDF_CONST);\n//\t\t\t\t\tQC_snprintfz(newname, sizeof(newname), \"%s.%s_y\", def->name, type->params[partnum].paramname);\n//\t\t\t\t\tQCC_PR_DummyDef(type_float, newname, scope, 0, rootsymbol, type->params[partnum].ofs+1 - rootsymbol->ofs, false, flags | GDF_CONST);\n//\t\t\t\t\tQC_snprintfz(newname, sizeof(newname), \"%s.%s_z\", def->name, type->params[partnum].paramname);\n//\t\t\t\t\tQCC_PR_DummyDef(type_float, newname, scope, 0, rootsymbol, type->params[partnum].ofs+2 - rootsymbol->ofs, false, flags | GDF_CONST);\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase ev_accessor:\t//shouldn't happen.\n\t\t\t\tcase ev_enum:\n\t\t\t\tcase ev_float:\n\t\t\t\tcase ev_double:\n\t\t\t\tcase ev_boolean:\n\t\t\t\tcase ev_string:\n\t\t\t\tcase ev_entity:\n\t\t\t\tcase ev_field:\n\t\t\t\tcase ev_pointer:\n\t\t\t\tcase ev_integer:\n\t\t\t\tcase ev_uint:\n\t\t\t\tcase ev_int64:\n\t\t\t\tcase ev_uint64:\n\t\t\t\tcase ev_struct:\n\t\t\t\tcase ev_union:\n\t\t\t\tcase ev_variant:\t//for lack of any better alternative\n\t\t\t\t\tif (type->params[partnum].paramname)\n\t\t\t\t\t\tQC_snprintfz(newname, sizeof(newname), \"%s.%s\", def->name, type->params[partnum].paramname);\n\t\t\t\t\telse\t//anon or something (eg array types).\n\t\t\t\t\t\tQC_snprintfz(newname, sizeof(newname), \"%s\", def->name);\n\t\t\t\t\tQCC_PR_DummyDef(parttype, newname, scope, type->params[partnum].arraysize, rootsymbol, def->ofs+type->params[partnum].ofs, false, flags);\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase ev_function:\n\t\t\t\t\tQC_snprintfz(newname, sizeof(newname), \"%s.%s\", def->name, parttype->name);\n\t\t\t\t\tQCC_PR_DummyDef(parttype, newname, scope, type->params[partnum].arraysize, rootsymbol, def->ofs+type->params[partnum].ofs, false, flags)->initialized = true;\n\t\t\t\t\tbreak;\n\t\t\t\tcase ev_bitfld:\t//FIXME: breaks saved games\n\t\t\t\tcase ev_void:\n\t\t\t\t\tbreak;\n\t\t\t\tcase ev_typedef:\t//invalid\n\t\t\t\t\tQCC_PR_ParseWarning(ERR_INTERNAL, \"unexpected typedef\");\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse if (type->type == ev_vector && !arraysize)\n\t\t{\t//do the vector thing.\n\t\t\tQC_snprintfz(newname, sizeof(newname), \"%s_x\", def->name);\n\t\t\tQCC_PR_DummyDef(type_float, newname, scope, 0, rootsymbol, def->ofs+0, referable, (flags&~GDF_SAVED) | GDF_STRIP);\n\t\t\tQC_snprintfz(newname, sizeof(newname), \"%s_y\", def->name);\n\t\t\tQCC_PR_DummyDef(type_float, newname, scope, 0, rootsymbol, def->ofs+1, referable, (flags&~GDF_SAVED) | GDF_STRIP);\n\t\t\tQC_snprintfz(newname, sizeof(newname), \"%s_z\", def->name);\n\t\t\tQCC_PR_DummyDef(type_float, newname, scope, 0, rootsymbol, def->ofs+2, referable, (flags&~GDF_SAVED) | GDF_STRIP);\n\t\t}\n\t\telse if (type->type == ev_field)\n\t\t{\n\t\t\tif (type->aux_type->type == ev_vector && !arraysize && *def->name != ':')\n\t\t\t{\n\t\t\t\t//do the vector thing.\n\t\t\t\tQC_snprintfz(newname, sizeof(newname), \"%s_x\", def->name);\n\t\t\t\tQCC_PR_DummyDef(type_floatfield, newname, scope, 0, rootsymbol, def->ofs+0, referable, flags);\n\t\t\t\tQC_snprintfz(newname, sizeof(newname), \"%s_y\", def->name);\n\t\t\t\tQCC_PR_DummyDef(type_floatfield, newname, scope, 0, rootsymbol, def->ofs+1, referable, flags);\n\t\t\t\tQC_snprintfz(newname, sizeof(newname), \"%s_z\", def->name);\n\t\t\t\tQCC_PR_DummyDef(type_floatfield, newname, scope, 0, rootsymbol, def->ofs+2, referable, flags);\n\t\t\t}\n\t\t}\n\t}\n\n\tif (referable)\n\t{\n//\t\tif (!arraysize && first->type->type != ev_field)\n//\t\t\tfirst->constant = false;\n\t\tif (scope)\n\t\t\tpHash_Add(&localstable, first->name, first, qccHunkAlloc(sizeof(bucket_t)));\n\t\telse\n\t\t\tpHash_Add(&globalstable, first->name, first, qccHunkAlloc(sizeof(bucket_t)));\n\n\t\tif (!scope && asmfile)\n\t\t\tfprintf(asmfile, \"%s %s;\\n\", TypeName(first->type, typebuf, sizeof(typebuf)), first->name);\n\t}\n\n\treturn first;\n}\n\n/*\n============\nPR_GetDef\n\nIf type is NULL, it will match any type\nIf arraysize=0, its not an array and has 1 element.\nIf arraysize>0, its an array and requires array notation\nIf arraysize<0, its an array with undefined size - GetDef will fail if its not already allocated.\n\nIf allocate is 0, will only get the def\nIf allocate is 1, a new def will be allocated if it can't be found\nIf allocate is 2, a new def will be allocated, and it'll error if there's a dupe with scope (for ensuring that arguments are created properly)\n============\n*/\n\nQCC_def_t *QCC_PR_GetDef (QCC_type_t *type, const char *name, struct QCC_function_s *scope, pbool allocate, int arraysize, unsigned int flags)\n{\n\tint ofs;\n\tQCC_def_t\t\t*def;\n//\tchar element[MAX_NAME];\n\tQCC_def_t *foundstatic = NULL;\n\tchar typebuf1[1024], typebuf2[1024];\n\tint ins, insmax;\n\n\tif (!allocate)\n\t\tarraysize = -1;\n\telse if (!strncmp(name, \"autocvar_\", 9))\n\t{\n\t\tif (scope)\n\t\t\tQCC_PR_ParseWarning(WARN_MISUSEDAUTOCVAR, \"Autocvar \\\"%s\\\" defined with local scope. promoting to global.\", name);\n\t\telse if (flags & GDF_CONST)\n\t\t\tQCC_PR_ParseWarning(WARN_MISUSEDAUTOCVAR, \"Autocvar \\\"%s\\\" defined as constant. attempting to correct that for you.\", name);\n\t\telse if (flags & GDF_STATIC)\n\t\t\tQCC_PR_ParseWarning(WARN_MISUSEDAUTOCVAR, \"Autocvar \\\"%s\\\" defined as static. attempting to correct that for you.\", name);\n\t\tscope = NULL;\n\t\tflags &= ~(GDF_CONST|GDF_STATIC);\n\t\tif (!(flags & GDF_STRIP))\n\t\t\tflags |= GDF_USED;\t//aka nostrip\n\t}\n\n\tif (pHash_Get != &Hash_Get)\n\t{\n\t\tins = 0;\n\t\tinsmax = allocate?1:2;\n\t}\n\telse\n\t{\n\t\tins = 1;\n\t\tinsmax = 2;\n\t}\n\tfor (; ins < insmax; ins++)\n\t{\n\t\tif (scope)\n\t\t{\n\t\t\t//FIXME: should we be scanning the locals list instead, and remove the localstable?\n\t\t\tdef = pHash_Get(&localstable, name);\n\n\t\t\twhile(def)\n\t\t\t{\n\t\t\t\t//ignore differing case the first time around.\n\t\t\t\tif (ins == 0 && strcmp(def->name, name))\n\t\t\t\t{\n\t\t\t\t\tdef = pHash_GetNext(&localstable, name, def);\n\t\t\t\t\tcontinue;\t\t// in a different function\n\t\t\t\t}\n\n\t\t\t\tif ( def->scope && def->scope != scope)\n\t\t\t\t{\n\t\t\t\t\tstruct QCC_function_s *pscope = NULL;\n\t\t\t\t\tif (def->isstatic)\n\t\t\t\t\t{\n\t\t\t\t\t\tfor (pscope = scope->parentscope; pscope; pscope = pscope->parentscope)\n\t\t\t\t\t\t\tif (def->scope == pscope)\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tif (!pscope)\n\t\t\t\t\t{\n\t\t\t\t\t\tdef = pHash_GetNext(&localstable, name, def);\n\t\t\t\t\t\tcontinue;\t\t// in a different function\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (type && typecmp(def->type, type))\n\t\t\t\t{\n\t\t\t\t\tif (scope && allocate && pr_subscopedlocals)\n\t\t\t\t\t{\t//assume it was defined in a different subscope. hopefully we'll start favouring the new one until it leaves subscope.\n\t\t\t\t\t\tdef = pHash_GetNext(&localstable, name, def);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tQCC_PR_ParseErrorPrintDef (ERR_TYPEMISMATCHREDEC, def, \"Type mismatch on redeclaration of %s%s%s. %s%s%s, should be %s%s%s\",col_symbol,name,col_none, col_type,TypeName(type, typebuf1, sizeof(typebuf1)),col_none, col_type,TypeName(def->type, typebuf2, sizeof(typebuf2)),col_none);\n\t\t\t\t}\n\t\t\t\tif (def->arraysize != arraysize && arraysize>=0)\n\t\t\t\t\tQCC_PR_ParseErrorPrintDef (ERR_TYPEMISMATCHARRAYSIZE, def, \"Array sizes for redecleration of %s%s%s do not match (%i -> %i)\",col_symbol,name,col_none, def->arraysize,arraysize);\n\t\t\t\tif (allocate && scope && !(flags & GDF_STATIC))\n\t\t\t\t{\n\t\t\t\t\tif (pr_subscopedlocals)\n\t\t\t\t\t{\t//subscopes mean that the later one replaces the first, hopefully.\n\t\t\t\t\t\tdef = pHash_GetNext(&localstable, name, def);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tif (allocate == 2)\n\t\t\t\t\t\tQCC_PR_ParseErrorPrintDef (ERR_TYPEMISMATCHREDEC, def, \"Duplicate definition of %s%s%s.\", col_symbol,name,col_none);\n\t\t\t\t\tif (def->isstatic)\n\t\t\t\t\t\tQCC_PR_ParseWarning (WARN_DUPLICATEDEFINITION, \"nonstatic redeclaration of %s%s%s ignored\", col_symbol,name,col_none);\n\t\t\t\t\telse\n\t\t\t\t\t\tQCC_PR_ParseWarning (WARN_DUPLICATEDEFINITION, \"%s%s%s duplicate definition ignored\", col_symbol,name,col_none);\n\t\t\t\t\tQCC_PR_ParsePrintDef(WARN_DUPLICATEDEFINITION, def);\n\t//\t\t\t\tif (!scope)\n\t//\t\t\t\t\tQCC_PR_ParsePrintDef(def);\n\t\t\t\t}\n\t\t\t\telse if (allocate && (flags & GDF_STATIC) && !def->isstatic)\n\t\t\t\t\tQCC_PR_ParseErrorPrintDef (ERR_TYPEMISMATCHREDEC, def, \"static redefinition of %s%s%s follows non-static definition.\", col_symbol,name,col_none);\n\n\t\t\t\tQCC_ForceUnFreeDef(def);\n\t\t\t\treturn def;\n\t\t\t}\n\t\t}\n\n\t\tdef = pHash_Get(&globalstable, name);\n\n\t\twhile(def)\n\t\t{\n\t\t\t//ignore differing case the first time around.\n\t\t\tif (ins == 0 && strcmp(def->name, name))\n\t\t\t{\n\t\t\t\tdef = pHash_GetNext(&globalstable, name, def);\n\t\t\t\tcontinue;\t\t// in a different function\n\t\t\t}\n\n\t\t\tif ( (def->scope || (scope && allocate)) && def->scope != scope)\n\t\t\t{\n\t\t\t\tdef = pHash_GetNext(&globalstable, name, def);\n\t\t\t\tcontinue;\t\t// in a different function\n\t\t\t}\n\n\t\t\t//ignore it if its static in some other file.\n\t\t\tif ((def->isstatic||(flags&GDF_STATIC)) && strcmp(def->unitn, scope?scope->unitn:s_unitn))\n\t\t\t{\n\t\t\t\tif (!foundstatic)\n\t\t\t\t\tfoundstatic = def;\t//save it off purely as a warning.\n\t\t\t\tdef = pHash_GetNext(&globalstable, name, def);\n\t\t\t\tcontinue;\t\t// in a different function\n\t\t\t}\n\n\t\t\tif (def->assumedtype && !(flags & GDF_BASICTYPE))\n\t\t\t{\n\t\t\t\tif (allocate)\n\t\t\t\t{\t//if we're asserting a type for it in some def then it'll no longer be assumed.\n\t\t\t\t\tif (def->type->type != type->type)\n\t\t\t\t\t\tQCC_PR_ParseErrorPrintDef (ERR_TYPEMISMATCHREDEC, def, \"Type mismatch on redeclaration of %s%s%s. Basic types are different.\",col_symbol,name,col_none);\n\t\t\t\t\tdef->type = type;\n\t\t\t\t\tdef->assumedtype = false;\n\t\t\t\t\tdef->filen = s_filen;\n\t\t\t\t\tdef->s_line = pr_source_line;\n\t\t\t\t\tif (flags & GDF_CONST)\n\t\t\t\t\t\tdef->constant = true;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\t//we know enough of its type to write it out, but not enough to actually use it safely, so pretend this def isn't defined yet.\n\t\t\t\t\tdef = pHash_GetNext(&globalstable, name, def);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (type && typecmp(def->type, type))\n\t\t\t{\n\t\t\t\tif (pr_scope || typecmp_lax(def->type, type))\n\t\t\t\t{\n\t\t\t\t\tif (!pr_scope && (\n\t\t\t\t\t\t!strcmp(\"droptofloor\", def->name)\t||\t//vanilla\n\t\t\t\t\t\t!strcmp(\"callfunction\", def->name)\t||\t//should be (..., string name) but dpextensions gets this wrong.\n\t\t\t\t\t\t!strcmp(\"trailparticles\", def->name)\t//dp got the two arguments the wrong way. fteqw doesn't care any more, but dp is still wrong.\n\t\t\t\t\t\t))\n\t\t\t\t\t{\n\t\t\t\t\t\t//this is a hack. droptofloor was wrongly declared in vanilla qc, which causes problems with replacement extensions.qc.\n\t\t\t\t\t\t//yes, this is a selfish lazy hack for this, there's probably a better way, but at least we spit out a warning still.\n\t\t\t\t\t\tQCC_PR_ParseWarning (WARN_COMPATIBILITYHACK, \"%s builtin was wrongly redefined as %s. ignoring later definition\",name, TypeName(type, typebuf1, sizeof(typebuf1)));\n\t\t\t\t\t\tQCC_PR_ParsePrintDef(WARN_COMPATIBILITYHACK, def);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tint flen = strlen(s_filen);\n\t\t\t\t\t\tif (!pr_scope && flen >= 13 && !QC_strcasecmp(s_filen+flen-13, \"extensions.qc\") && def->type->type == ev_function)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t//this is a hack. droptofloor was wrongly declared in vanilla qc, which causes problems with replacement extensions.qc.\n\t\t\t\t\t\t\t//yes, this is a selfish lazy hack for this, there's probably a better way, but at least we spit out a warning still.\n\t\t\t\t\t\t\tQCC_PR_ParseWarning (WARN_COMPATIBILITYHACK, \"%s builtin was redefined as %s. ignoring alternative definition\",name, TypeName(type, typebuf1, sizeof(typebuf1)));\n\t\t\t\t\t\t\tQCC_PR_ParsePrintDef(WARN_COMPATIBILITYHACK, def);\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (def->unused && !def->referenced && allocate && !def->scope)\n\t\t\t\t\t\t{\t//previous def was norefed and still wasn't used yet.\n\t\t\t\t\t\t\tQCC_PR_ParseWarning (WARN_COMPATIBILITYHACK, \"Type redeclaration of %s %s replaces existing variable\", TypeName(type, typebuf1, sizeof(typebuf1)), name);\n\t\t\t\t\t\t\tQCC_PR_ParsePrintDef(WARN_COMPATIBILITYHACK, def);\n\t\t\t\t\t\t\tdef = pHash_GetNext(&localstable, name, def);\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (type->type == ev_function && type->vargs && !type->num_parms && def->type->type == ev_function)\n\t\t\t\t\t\t\t\t;\t//c89-style dumb redeclarations...\n\t\t\t\t\t\t\telse\t//unequal even when we're lax\n\t\t\t\t\t\t\t\tQCC_PR_ParseErrorPrintDef (ERR_TYPEMISMATCHREDEC, def, \"Type mismatch on redeclaration of %s%s%s. %s%s%s, should be %s%s%s\",col_symbol,name,col_none, col_type,TypeName(type, typebuf1, sizeof(typebuf1)),col_none, col_type,TypeName(def->type, typebuf2, sizeof(typebuf2)),col_none);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tif (type->type != ev_function || type->num_parms != def->type->num_parms || !(def->type->vargs && !type->vargs))\n\t\t\t\t\t{\n\t\t\t\t\t\t//if the second def simply has no ..., don't bother warning about it.\n\n\t\t\t\t\t\tQCC_PR_ParseWarning (WARN_TYPEMISMATCHREDECOPTIONAL, \"Optional arguments differ on redeclaration of %s%s%s. %s%s%s, should be %s%s%s\",col_symbol,name,col_none, col_type,TypeName(type, typebuf1, sizeof(typebuf1)),col_none, col_type,TypeName(def->type, typebuf2, sizeof(typebuf2)),col_none);\n\t\t\t\t\t\tQCC_PR_ParsePrintDef(WARN_TYPEMISMATCHREDECOPTIONAL, def);\n\n\t\t\t\t\t\tif (type->type == ev_function)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t//update the def's type to the new one if the mandatory argument count is longer\n\t\t\t\t\t\t\t//FIXME: don't change the param names!\n\t\t\t\t\t\t\tif (type->num_parms > def->type->num_parms)\n\t\t\t\t\t\t\t\tdef->type = type;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (def->arraysize != arraysize && arraysize>=0)\n\t\t\t{\n\t\t\t\tif (allocate && !def->symbolsize)\n\t\t\t\t\tQCC_PR_DummyDef(def->type, name, def->scope, arraysize, def, 0, true, flags);\n\t\t\t\telse\n\t\t\t\t\tQCC_PR_ParseErrorPrintDef(ERR_TYPEMISMATCHARRAYSIZE, def, \"Array sizes for redecleration of %s do not match (%i->%i)\",name, def->arraysize,arraysize);\n\t\t\t}\n\t\t\tif (!def->symbolsize && def->arraysize >= 0)\t//variables are allowed to be declared (with their addresses taken) before struct types are even defined.\n\t\t\t\tQCC_PR_DummyDef(def->type, name, def->scope, def->arraysize, def, 0, true, flags);\n\n\t\t\tif (allocate && scope && !(flags & GDF_STATIC))\n\t\t\t{\n\t\t\t\tif (allocate == 2)\n\t\t\t\t\tQCC_PR_ParseErrorPrintDef (ERR_TYPEMISMATCHREDEC, def, \"Duplicate definition of %s.\", name);\n\t\t\t\tif (pr_scope)\n\t\t\t\t{\t//warn? or would that be pointless?\n\t\t\t\t\tdef = pHash_GetNext(&globalstable, name, def);\n\t\t\t\t\tcontinue;\t\t// in a different function\n\t\t\t\t}\n\n\t\t\t\tif (def->isstatic)\n\t\t\t\t\tQCC_PR_ParseWarning (WARN_DUPLICATEDEFINITION, \"nonstatic redeclaration of %s ignored\", name);\n\t\t\t\telse\n\t\t\t\t\tQCC_PR_ParseWarning (WARN_DUPLICATEDEFINITION, \"%s duplicate definition ignored\", name);\n\t\t\t\tQCC_PR_ParsePrintDef(WARN_DUPLICATEDEFINITION, def);\n\t//\t\t\tif (!scope)\n\t//\t\t\t\tQCC_PR_ParsePrintDef(def);\n\t\t\t}\n\t\t\tQCC_ForceUnFreeDef(def);\n\t\t\treturn def;\n\t\t}\n\t}\n\n\tif (foundstatic && !allocate && !(flags & GDF_SILENT))\n\t{\n\t\tQCC_PR_ParseWarning (WARN_SAMENAMEASGLOBAL, \"%s defined static\", name);\n\t\tQCC_PR_ParsePrintDef(WARN_SAMENAMEASGLOBAL, foundstatic);\n\t}\n\n\tif (flags & GDF_SCANLOCAL)\n\t{\n\t\tfor (def = pr.local_head.nextlocal; def; def = def->nextlocal)\n\t\t{\n\t\t\tif (!strcmp(def->name, name))\n\t\t\t{\n\t\t\t\tif (allocate && def->arraysize != arraysize)\n\t\t\t\t\tcontinue;\n\t\t\t\tif (allocate)\n\t\t\t\t{\t//relink it\n\t\t\t\t\tpHash_Add(&localstable, name, def, qccHunkAlloc(sizeof(bucket_t)));\n\t\t\t\t\tdef->subscoped_away = false;\n\t\t\t\t}\n\t\t\t\tQCC_ForceUnFreeDef(def);\n\t\t\t\treturn def;\n\t\t\t}\n\t\t}\n\t}\n\n\tif (!allocate)\n\t\treturn NULL;\n\n\tif (scope && qccwarningaction[WARN_SAMENAMEASGLOBAL])\n\t{\n\t\tdef = QCC_PR_GetDef(NULL, name, NULL, false, arraysize, GDF_SILENT);\n\t\tif (def && def->type->type == type->type)\n\t\t{\t//allow type differences. this means that arguments called 'min' or 'mins' are accepted with the 'min' builtin or the 'mins' field in existance.\n\t\t\tQCC_PR_ParseWarning(WARN_SAMENAMEASGLOBAL, \"Local \\\"%s\\\" hides global with same name and type\", name);\n\t\t\tQCC_PR_ParsePrintDef(WARN_SAMENAMEASGLOBAL, def);\n\t\t}\n\t\tQCC_FreeDef(def);\n\t}\n\n\tofs = 0;\n\n\tdef = QCC_PR_DummyDef(type, name, scope, arraysize, NULL, ofs, true, flags);\n\n\tQCC_ForceUnFreeDef(def);\n\treturn def;\n}\nQCC_sref_t QCC_PR_GetSRef (QCC_type_t *type, const char *name, QCC_function_t *scope, pbool allocate, int arraysize, unsigned int flags)\n{\n\tQCC_sref_t sr;\n\tQCC_def_t *def = QCC_PR_GetDef(type, name, scope, allocate, arraysize, flags);\n\tif (def)\n\t{\n\t\tsr.sym = def;\n\t\tsr.cast = def->type;\n\t\tsr.ofs = 0;\n\t\tif (def->deprecated)\n\t\t{\n\t\t\tif (*def->deprecated)\t//we have a reason for it\n\t\t\t\tQCC_PR_ParseWarning(pr_ignoredeprecation?WARN_MUTEDEPRECATEDVARIABLE:WARN_DEPRECATEDVARIABLE, \"Variable \\\"%s\\\" is deprecated: %s\", def->name, def->deprecated);\n\t\t\telse\t\t//we don't have any reason for it.\n\t\t\t\tQCC_PR_ParseWarning(pr_ignoredeprecation?WARN_MUTEDEPRECATEDVARIABLE:WARN_DEPRECATEDVARIABLE, \"Variable \\\"%s\\\" is deprecated\", def->name);\n\t\t}\n\t\treturn sr;\n\t}\n\treturn nullsref;\n}\n\n//.union {};\nstatic QCC_def_t *QCC_PR_DummyFieldDef(QCC_type_t *type, QCC_function_t *scope, int arraysize, unsigned int *fieldofs, unsigned int saved)\n{\n\tchar array[64];\n\tchar newname[256];\n\tint a, parms, o;\n\tQCC_def_t *def, *first=NULL;\n\tunsigned int maxfield, startfield;\n\tQCC_type_t *ftype;\n\tpbool isunion;\n\tstartfield = *fieldofs;\n\tmaxfield = startfield;\n\n\tfor (a = 0; a < (arraysize?arraysize:1); a++)\n\t{\n\t\tif (a == 0)\n\t\t\t*array = '\\0';\n\t\telse\n\t\t\tQC_snprintfz(array, sizeof(array), \"[%i]\", a);\n\n//\texterns->Printf(\"Emited %s\\n\", newname);\n\n\t\tif ((type)->type == ev_struct||(type)->type == ev_union)\n\t\t{\n\t\t\tint memberalen;\n\t\t\tint partnum;\n\t\t\tQCC_type_t *parttype;\n\t\t\tisunion = ((type)->type == ev_union);\n\t\t\tfor (partnum = 0, parms = (type)->num_parms; partnum < parms; partnum++)\n\t\t\t{\n\t\t\t\tparttype = type->params[partnum].type;\n\t\t\t\twhile(parttype->type == ev_accessor)\n\t\t\t\t\tparttype = parttype->parentclass;\n\n\t\t\t\tmemberalen = type->params[partnum].arraysize;\n\n\t\t\t\tswitch (parttype->type)\n\t\t\t\t{\n\t\t\t\tcase ev_union:\n\t\t\t\tcase ev_struct:\n\t\t\t\t\tif (!*type->params[partnum].paramname)\n\t\t\t\t\t{\t//recursively generate new fields\n\t\t\t\t\t\tQC_snprintfz(newname, sizeof(newname), \"%s%s\", type->params[partnum].paramname, array);\n\t\t\t\t\t\tdef = QCC_PR_DummyFieldDef(parttype, scope, memberalen, fieldofs, saved);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\t//fallthrough. any named structs will become global structs that contain field references. hopefully.\n\t\t\t\tcase ev_enum:\n\t\t\t\tcase ev_accessor:\n\t\t\t\tcase ev_float:\n\t\t\t\tcase ev_double:\n\t\t\t\tcase ev_boolean:\n\t\t\t\tcase ev_string:\n\t\t\t\tcase ev_vector:\n\t\t\t\tcase ev_entity:\n\t\t\t\tcase ev_field:\n\t\t\t\tcase ev_pointer:\n\t\t\t\tcase ev_integer:\n\t\t\t\tcase ev_uint:\n\t\t\t\tcase ev_int64:\n\t\t\t\tcase ev_uint64:\n\t\t\t\tcase ev_variant:\n\t\t\t\tcase ev_function:\n\t\t\t\t\tif (!*type->params[partnum].paramname)\n\t\t\t\t\t{\n\t\t\t\t\t\tQCC_PR_ParseWarning(WARN_CONFLICTINGUNIONMEMBER, \"nameless field union/struct generating nameless def.\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tQC_snprintfz(newname, sizeof(newname), \"%s%s\", type->params[partnum].paramname, array);\n\t\t\t\t\tftype = QCC_PR_NewType(\"FIELD_TYPE\", ev_field, false);\n\t\t\t\t\tftype->aux_type = parttype;\n\t\t\t\t\tif (parttype->type == ev_vector)\n\t\t\t\t\t\tftype->size = parttype->size;\t//vector fields create a _y and _z too, so we need this still.\n\t\t\t\t\tdef = QCC_PR_GetDef(NULL, newname, scope, false, memberalen, saved);\n\t\t\t\t\tif (!def)\n\t\t\t\t\t{\n\t\t\t\t\t\tdef = QCC_PR_GetDef(ftype, newname, scope, true, memberalen, saved);\n\t\t\t\t\t\tif (parttype->type == ev_function)\n\t\t\t\t\t\t\tdef->initialized = true;\n\t\t\t\t\t\tfor (o = 0; o < parttype->size*(memberalen?memberalen:1); o++)\n\t\t\t\t\t\t\tdef->symboldata[o]._int = *fieldofs + o;\n\t\t\t\t\t\t*fieldofs += parttype->size*(memberalen?memberalen:1);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tQCC_PR_ParseWarning(WARN_CONFLICTINGUNIONMEMBER, \"conflicting offsets for nameless union/struct expansion of %s. Ignoring new def.\", newname);\n\t\t\t\t\t\tQCC_PR_ParsePrintDef(WARN_CONFLICTINGUNIONMEMBER, def);\n\t\t\t\t\t\t//hcc just PR_GetDefs the fields. it allocates field space as part of the def, which is skipped if it already exists.\n\t\t\t\t\t\t//so don't update fieldofs, because that would result in incompatibilities.\n\t\t\t\t\t}\n\t\t\t\t\tQCC_FreeDef(def);\n\t\t\t\t\tbreak;\n\t\t\t\tcase ev_void:\n\t\t\t\tcase ev_bitfld:\t//FIXME: breaks saved games\n\t\t\t\t\tbreak;\n\t\t\t\tcase ev_typedef:\t//invalid\n\t\t\t\t\tQCC_PR_ParseWarning(ERR_INTERNAL, \"unexpected typedef\");\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tif (*fieldofs > maxfield)\n\t\t\t\t\tmaxfield = *fieldofs;\n\t\t\t\tif (isunion)\n\t\t\t\t\t*fieldofs = startfield;\n\t\t\t}\n\t\t}\n\t}\n\n\t*fieldofs = maxfield;\t//final size of the union.\n\treturn first;\n}\n\n\n\nstatic void QCC_PR_ExpandUnionToFields(QCC_type_t *type, unsigned int *fields)\n{\n\tQCC_type_t *pass = type->aux_type;\n\tQCC_PR_DummyFieldDef(pass, pr_scope, 1, fields, GDF_SAVED|GDF_CONST);\n}\n\n//copies tmp into def\n//FIXME: is basedef redundant?\n//FIXME: is type redundant?\nstatic pbool QCC_PR_GenerateInitializerType(QCC_def_t *basedef, QCC_sref_t tmp, QCC_sref_t def, QCC_type_t *type, unsigned bitofs, unsigned int flags)\n{\n\tpbool ret = true;\n\tunsigned i;\n\n\tdef.ofs += bitofs>>5;\n\tbitofs&=31;\n\n\tif (bitofs && !type->bits)\n\t\tQCC_PR_ParseWarning(ERR_BADIMMEDIATETYPE, \"'%s' is not word aligned\", basedef?basedef->name:\"<unknown>\");\n\tif (basedef && (!basedef->scope || basedef->constant || basedef->isstatic))\n\t{\n\t\tif (!tmp.sym->constant)\n\t\t{\n\t\t\tif (basedef->scope && !basedef->isstatic && !basedef->initialized)\n\t\t\t{\n//\t\t\t\tQCC_PR_ParseWarning(WARN_GMQCC_SPECIFIC, \"initializer for '%s' is not constant\", basedef->name);\n//\t\t\t\tQCC_PR_ParsePrintSRef(WARN_GMQCC_SPECIFIC, tmp);\n//\t\t\t\tbasedef->constant = false;\n\t\t\t\tgoto finalnotconst;\n\t\t\t}\n\t\t\tQCC_PR_ParseWarning(ERR_BADIMMEDIATETYPE, \"initializer for '%s' is not constant\", basedef->name);\n\t\t\tQCC_PR_ParsePrintSRef(ERR_BADIMMEDIATETYPE, tmp);\n\t\t}\n\t\telse\n\t\t{\n\t\t\ttmp.sym->referenced = true;\n\t\t\tif ((!basedef->scope||basedef->isstatic) && tmp.sym->reloc)\n\t\t\t{\n\t\t\t\tQCC_def_t *dt;\n\t\t\t\tif (!flag_pointerrelocs)\n\t\t\t\t\tQCC_PR_ParseWarning(ERR_BADEXTENSION, \"preinitialised pointer variables is disabled for this target\");\n\t\t\t\tdt = QCC_PR_DummyDef(type_pointer, \"$reloc\", basedef->scope, 0, def.sym, def.ofs, false, GDF_CONST);\n\t\t\t\tdt->reloc = tmp.sym->reloc;\n\t\t\t\tdt->referenced = true;\n\t\t\t\tdt->constant = 1;\n\t\t\t\tdt->initialized = 1;\n\t\t\t\tdt->strip = true;\t//engine does need to know it... but there should be some other def that we're mapping intohere.\n\n\t\t\t\tfor (i = 0; (unsigned)i < type->size; i++)\n\t\t\t\t\tQCC_SRef_DataWord(def, i)->_int = QCC_SRef_DataWord(tmp, i)->_int;//tmp.ofs + tmp.sym->reloc->symbolheader->arraylengthprefix;\n\n\t\t\t\tQCC_FreeTemp(tmp);\n\t\t\t\treturn ret;\n\t\t\t}\n\t\t\tif ((!basedef->scope||basedef->isstatic) && def.cast->type==ev_pointer && tmp.sym->type->type==ev_string)\n\t\t\t{\n\t\t\t\tQCC_def_t *dt;\n\t\t\t\tdt = QCC_PR_DummyDef(type_string, \"$reloc\", basedef->scope, 0, def.sym, def.ofs, false, GDF_CONST);\n\t\t\t\tdt->reloc = tmp.sym->reloc;\n\t\t\t\tdt->referenced = true;\n\t\t\t\tdt->constant = 1;\n\t\t\t\tdt->initialized = 1;\n\t\t\t\tdt->strip = true;\t//engine does need to know it... but there should be some other def that we're mapping intohere.\n\n\t\t\t\tfor (i = 0; (unsigned)i < type->size; i++)\n\t\t\t\t\tQCC_SRef_DataWord(def, i)->_int = QCC_SRef_DataWord(tmp, i)->_int;//tmp.ofs + tmp.sym->reloc->symbolheader->arraylengthprefix;\n\n\t\t\t\tQCC_FreeTemp(tmp);\n\t\t\t\treturn ret;\n\t\t\t}\n\n\t\t\tif (basedef->initialized && !basedef->unused && !(flags & PIF_STRONGER))\n\t\t\t{\t//dupe initialisation. compare the two.\n\n\t\t\t\tif (!tmp.sym->initialized)\n\t\t\t\t{\n\t\t\t\t\t//FIXME: we NEED to support relocs somehow\n\t\t\t\t\tQCC_PR_ParseWarning(WARN_UNINITIALIZED, \"initializer is not initialised, %s will be treated as 0\", QCC_GetSRefName(tmp));\n\t\t\t\t\tQCC_PR_ParsePrintSRef(WARN_UNINITIALIZED, tmp);\n\t\t\t\t}\n\n\t\t\t\tfor (i = 0; (unsigned)i < type->size; i++)\n\t\t\t\t\tif (QCC_SRef_DataWord(def,i)->_int != QCC_SRef_DataWord(tmp,i)->_int)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (!def.sym->arraysize && def.cast->type == ev_function && !strcmp(def.sym->name, \"parseentitydata\") && (functions[QCC_SRef_DataWord(def, i)->_int].builtin == 608 || functions[QCC_SRef_DataWord(def, i)->_int].builtin == 613))\n\t\t\t\t\t\t{\t//dpextensions is WRONG, and claims it to be 608. its also too common, so lets try working around that.\n\t\t\t\t\t\t\tif (functions[QCC_SRef_DataWord(def, i)->_int].builtin == 608)\n\t\t\t\t\t\t\t\tfunctions[QCC_SRef_DataWord(def, i)->_int].builtin = 613;\n\t\t\t\t\t\t\tQCC_PR_ParseWarning (WARN_COMPATIBILITYHACK, \"incompatible redeclaration. Please validate builtin numbers. parseentitydata is #613\");\n\t\t\t\t\t\t\tQCC_PR_ParsePrintSRef(WARN_COMPATIBILITYHACK, tmp);\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (!def.sym->arraysize && def.cast->type == ev_function && functions[QCC_SRef_DataWord(def, i)->_int].code>-1 && functions[QCC_SRef_DataWord(tmp, i)->_int].code==-1)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tQCC_PR_ParseWarning (WARN_COMPATIBILITYHACK, \"incompatible redeclaration. Ignoring replacement of qc function with builtin.\");\n\t\t\t\t\t\t\tQCC_PR_ParsePrintSRef(WARN_COMPATIBILITYHACK, tmp);\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tQCC_PR_ParseErrorPrintSRef (ERR_REDECLARATION, def, \"incompatible redeclaration\");\n\t\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tconst int *srcdata = (const void*)QCC_SRef_EvalConst(tmp);\n\t\t\t\tif (!srcdata)\n\t\t\t\t{\n\t\t\t\t\tif ((!basedef->scope||basedef->isstatic) && def.cast->type == ev_function && def.sym->symboldata==basedef->symboldata)\n\t\t\t\t\t{\t//set to a function which is not yet initialised. insert a function reloc at this location, so we can update it once it is actually known.\n\t\t\t\t\t\tfor (i = 0; (unsigned)i < type->size; i++)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tQCC_def_t *dt;\n\t\t\t\t\t\t\tdt = QCC_PR_DummyDef(type_function, \"$relocf\", basedef->scope, 0, def.sym, def.ofs, false, GDF_CONST);\n\t\t\t\t\t\t\tdt->reloc = tmp.sym;\n\t\t\t\t\t\t\tdt->referenced = true;\n\t\t\t\t\t\t\tdt->constant = 1;\n\t\t\t\t\t\t\tdt->initialized = 1;\n\t\t\t\t\t\t\tdt->strip = true;\t//hide it. engine doesn't need to know.\n\n\t\t\t\t\t\t\tQCC_SRef_DataWord(def, i)->_int = 0;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tQCC_PR_ParseWarning(WARN_NOTCONSTANT, \"initializer for %s is not initialised yet, %s will be treated as 0\", QCC_GetSRefName(def), QCC_GetSRefName(tmp));\n\t\t\t\t\t\tQCC_PR_ParsePrintSRef(WARN_NOTCONSTANT, tmp);\n\n\t\t\t\t\t\tfor (i = 0; (unsigned)i < type->size; i++)\n\t\t\t\t\t\t\tQCC_SRef_DataWord(def, i)->_int = 0;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse if (type->bits)\n\t\t\t\t{\t//a small type/bitfield...\n\t\t\t\t\tunsigned int old, new;\n\t\t\t\t\tif (bitofs + type->bits > 32)\n\t\t\t\t\t\tQCC_PR_ParseWarning(ERR_INTERNAL, \"bitfield cross 32bit boundary\");\n\t\t\t\t\told = (QCC_SRef_DataWord(def, bitofs>>5)->_int&~(((1u<<type->bits)-1)<<bitofs));\n\t\t\t\t\tnew = (srcdata[0] & ((1u<<type->bits)-1)) << bitofs;\n\t\t\t\t\tQCC_SRef_DataWord(def, bitofs>>5)->_int = old|new;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tfor (i = 0; (unsigned)i < type->size; i++)\n\t\t\t\t\t\tQCC_SRef_DataWord(def, i)->_int = srcdata[i];\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\telse\n\t{\n\t\tQCC_sref_t rhs;\n\t\tpbool nullsource;\nfinalnotconst:\n\t\trhs = tmp;\n\t\tnullsource = QCC_SRef_IsNull(rhs);\n\t\tif (def.sym->initialized)\n\t\t\tQCC_PR_ParseErrorPrintSRef (ERR_REDECLARATION, def, \"%s initialised twice\", basedef->name);\n\t\telse if (type->bits)\n\t\t{\n\t\t\tif (bitofs + type->bits > 32)\n\t\t\t\tQCC_PR_ParseErrorPrintSRef (ERR_REDECLARATION, def, \"%s dynamically initialised (%i bit)\", basedef->name, type->bits);\n\t\t\trhs = QCC_PR_Statement_BitCopy(nullsource?QCC_MakeVectorConst(0,0,0):rhs, bitofs, type->bits, def);\n\t\t\tif (rhs.sym == def.sym && rhs.ofs == def.ofs && rhs.cast == def.cast)\n\t\t\t{\n\t\t\t\tQCC_FreeTemp(rhs);\n\t\t\t\treturn ret;\n\t\t\t}\n\t\t\t//else still need to copy it.\n\t\t}\n\n\t\tret = 0;\n\t\tfor (i = 0; (unsigned)i < type->size; )\n\t\t{\n\t\t\tif (type->size - i >= 3)\n\t\t\t{\n\t\t\t\trhs.cast = def.cast = type_vector;\n\t\t\t\tif (type->size - i == 3)\n\t\t\t\t{\n\t\t\t\t\tQCC_FreeTemp(QCC_PR_StatementFlags(&pr_opcodes[OP_STORE_V], nullsource?QCC_MakeVectorConst(0,0,0):rhs, def, NULL, STFL_PRESERVEB));\n\t\t\t\t\treturn ret;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tQCC_FreeTemp(QCC_PR_StatementFlags(&pr_opcodes[OP_STORE_V], nullsource?QCC_MakeVectorConst(0,0,0):rhs, def, NULL, STFL_PRESERVEA|STFL_PRESERVEB));\n\t\t\t\ti+=3;\n\t\t\t\tdef.ofs += 3;\n\t\t\t\trhs.ofs += 3;\n\t\t\t}\n\t\t\telse if (type->size - i >= 2)\n\t\t\t{\n\t\t\t\trhs.cast = def.cast = type_vector;\n\t\t\t\tif (type->size - i == 2)\n\t\t\t\t{\n\t\t\t\t\tQCC_FreeTemp(QCC_PR_StatementFlags(&pr_opcodes[OP_STORE_I64], nullsource?QCC_MakeVectorConst(0,0,0):rhs, def, NULL, STFL_PRESERVEB));\n\t\t\t\t\treturn ret;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tQCC_FreeTemp(QCC_PR_StatementFlags(&pr_opcodes[OP_STORE_I64], nullsource?QCC_MakeVectorConst(0,0,0):rhs, def, NULL, STFL_PRESERVEA|STFL_PRESERVEB));\n\t\t\t\ti+=2;\n\t\t\t\tdef.ofs += 2;\n\t\t\t\trhs.ofs += 2;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (nullsource)\n\t\t\t\t\trhs = QCC_MakeIntConst(0);\n\n\t\t\t\tif (def.cast->type == ev_function)\n\t\t\t\t{\n\t\t\t\t\trhs.cast = def.cast = type_function;\n\t\t\t\t\tif (type->size - i == 1)\n\t\t\t\t\t{\n\t\t\t\t\t\tQCC_FreeTemp(QCC_PR_StatementFlags(&pr_opcodes[OP_STORE_FNC], rhs, def, NULL, STFL_PRESERVEB));\n\t\t\t\t\t\treturn ret;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\tQCC_FreeTemp(QCC_PR_StatementFlags(&pr_opcodes[OP_STORE_FNC], rhs, def, NULL, STFL_PRESERVEA|STFL_PRESERVEB));\n\t\t\t\t}\n\t\t\t\telse if (def.cast->type == ev_string)\n\t\t\t\t{\n\t\t\t\t\trhs.cast = def.cast = type_string;\n\t\t\t\t\tif (type->size - i == 1)\n\t\t\t\t\t{\n\t\t\t\t\t\tQCC_FreeTemp(QCC_PR_StatementFlags(&pr_opcodes[OP_STORE_S], rhs, def, NULL, STFL_PRESERVEB));\n\t\t\t\t\t\treturn ret;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\tQCC_FreeTemp(QCC_PR_StatementFlags(&pr_opcodes[OP_STORE_S], rhs, def, NULL, STFL_PRESERVEA|STFL_PRESERVEB));\n\t\t\t\t}\n\t\t\t\telse if (def.cast->type == ev_entity)\n\t\t\t\t{\n\t\t\t\t\trhs.cast = def.cast = type_entity;\n\t\t\t\t\tif (type->size - i == 1)\n\t\t\t\t\t{\n\t\t\t\t\t\tQCC_FreeTemp(QCC_PR_StatementFlags(&pr_opcodes[OP_STORE_ENT], rhs, def, NULL, STFL_PRESERVEB));\n\t\t\t\t\t\treturn ret;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\tQCC_FreeTemp(QCC_PR_StatementFlags(&pr_opcodes[OP_STORE_ENT], rhs, def, NULL, STFL_PRESERVEA|STFL_PRESERVEB));\n\t\t\t\t}\n\t\t\t\telse if (def.cast->type == ev_field)\n\t\t\t\t{\n\t\t\t\t\trhs.cast = def.cast = type_field;\n\t\t\t\t\tif (type->size - i == 1)\n\t\t\t\t\t{\n\t\t\t\t\t\tQCC_FreeTemp(QCC_PR_StatementFlags(&pr_opcodes[OP_STORE_FLD], rhs, def, NULL, STFL_PRESERVEB));\n\t\t\t\t\t\treturn ret;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\tQCC_FreeTemp(QCC_PR_StatementFlags(&pr_opcodes[OP_STORE_FLD], rhs, def, NULL, STFL_PRESERVEA|STFL_PRESERVEB));\n\t\t\t\t}\n\t\t\t\telse if (def.cast->type == ev_integer || def.cast->type == ev_uint)\n\t\t\t\t{\n\t\t\t\t\trhs.cast = def.cast = type_integer;\n\t\t\t\t\tif (type->size - i == 1)\n\t\t\t\t\t{\n\t\t\t\t\t\tQCC_FreeTemp(QCC_PR_StatementFlags(&pr_opcodes[OP_STORE_I], rhs, def, NULL, STFL_PRESERVEB));\n\t\t\t\t\t\treturn ret;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\tQCC_FreeTemp(QCC_PR_StatementFlags(&pr_opcodes[OP_STORE_I], rhs, def, NULL, STFL_PRESERVEA|STFL_PRESERVEB));\n\t\t\t\t}\n\t\t\t\telse if (def.cast->type == ev_int64 || def.cast->type == ev_uint64 || def.cast->type == ev_double)\n\t\t\t\t{\n\t\t\t\t\trhs.cast = def.cast = type_int64;\n\t\t\t\t\tif (type->size - i == 2)\n\t\t\t\t\t{\n\t\t\t\t\t\tQCC_FreeTemp(QCC_PR_StatementFlags(&pr_opcodes[OP_STORE_I64], rhs, def, NULL, STFL_PRESERVEB));\n\t\t\t\t\t\treturn ret;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\tQCC_FreeTemp(QCC_PR_StatementFlags(&pr_opcodes[OP_STORE_I64], rhs, def, NULL, STFL_PRESERVEA|STFL_PRESERVEB));\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\trhs.cast = def.cast = type_float;\n\t\t\t\t\tif (type->size - i == 1)\n\t\t\t\t\t{\n\t\t\t\t\t\tQCC_FreeTemp(QCC_PR_StatementFlags(&pr_opcodes[OP_STORE_F], rhs, def, NULL, STFL_PRESERVEB));\n\t\t\t\t\t\treturn ret;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\tQCC_FreeTemp(QCC_PR_StatementFlags(&pr_opcodes[OP_STORE_F], rhs, def, NULL, STFL_PRESERVEA|STFL_PRESERVEB));\n\t\t\t\t}\n\t\t\t\ti++;\n\t\t\t\tdef.ofs++;\n\t\t\t\trhs.ofs++;\n\t\t\t}\n\t\t}\n\t}\n\tQCC_FreeTemp(tmp);\n\treturn ret;\n}\n\nQCC_sref_t QCC_PR_ParseInitializerType_Internal(int arraysize, QCC_def_t *basedef, QCC_sref_t def, unsigned int bitofs, unsigned int flags)\n{\n\tQCC_sref_t tmp;\n\tint i;\n\tpbool ret = true;\n\n\tif (arraysize)\n\t{\n\t\tif (pr_token_type == tt_immediate && pr_immediate_type == type_string && def.cast->type == ev_bitfld && def.cast->bits == 8)\n\t\t{\n\t\t\tif (pr_immediate_strlen > arraysize)\t//problem!\n\t\t\t\tQCC_PR_ParseWarning (ERR_BADARRAYSIZE, \"initializer-string too long\");\n\n\t\t\tfor (i = 0; i < arraysize; i++)\n\t\t\t{\n\t\t\t\ttmp = QCC_MakeIntConst((i < pr_immediate_strlen)?pr_immediate_string[i]:0);\n\t\t\t\ttmp = QCC_EvaluateCast(tmp, def.cast, true);\n\t\t\t//\tQCC_ForceUnFreeDef(tmp.sym);\n\t\t\t\tret &= QCC_PR_GenerateInitializerType(basedef, tmp, def, def.cast, bitofs, flags);\n\t\t\t\tbitofs += def.cast->bits;\n\t\t\t}\n\t\t\tQCC_PR_Lex();\n\t\t}\n\t\telse\n\t\t{\n\t\t\t//arrays go recursive\n\t\t\tQCC_PR_Expect(\"{\");\n\t\t\tif (!QCC_PR_CheckToken(\"}\"))\n\t\t\t{\n\t\t\t\tfor (i = 0; i < arraysize; i++)\n\t\t\t\t{\n\t\t\t\t\tret &= QCC_PR_ParseInitializerType(0, basedef, def, bitofs, flags);\n\t\t\t\t\tif (def.cast->bits)\n\t\t\t\t\t\tbitofs += def.cast->bits;\n\t\t\t\t\telse\n\t\t\t\t\t\tdef.ofs += def.cast->size;\n\t\t\t\t\tif (!QCC_PR_CheckToken(\",\"))\n\t\t\t\t\t{\n\t\t\t\t\t\tQCC_PR_Expect(\"}\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tif (QCC_PR_CheckToken(\"}\"))\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\telse\n\t{\n\t\tQCC_type_t *type = def.cast;\n\t\tif (type->type == ev_function && pr_token_type == tt_punct)\n\t\t{\n\t\t\t/*begin function special case*/\n\t\t\tQCC_function_t *parentfunc = pr_scope;\n\t\t\tQCC_function_t *f;\n\t\t\tchar fname[256];\n\t\t\tconst char *defname = QCC_GetSRefName(def);\n\n\t\t\ttmp = nullsref;\n\t\t\t*fname = 0;\n\n\t\t\tif (QCC_PR_CheckToken (\"#\") || QCC_PR_CheckToken (\":\"))\n\t\t\t{\n\t\t\t\tint binum = 0;\n\t\t\t\tif (pr_token_type == tt_immediate\n\t\t\t\t&& pr_immediate_type == type_float\n\t\t\t\t&& pr_immediate._float == (int)pr_immediate._float)\n\t\t\t\t\tbinum = (int)pr_immediate._float;\n\t\t\t\telse if (pr_token_type == tt_immediate && pr_immediate_type == type_integer)\n\t\t\t\t\tbinum = pr_immediate._int;\n\t\t\t\telse if (pr_token_type == tt_immediate && pr_immediate_type == type_string)\n\t\t\t\t\tQC_strlcpy(fname, pr_immediate_string, sizeof(fname));\n\t\t\t\telse if (pr_token_type == tt_name)\n\t\t\t\t\tQC_strlcpy(fname, pr_token, sizeof(fname));\n\t\t\t\telse \n\t\t\t\t\tQCC_PR_ParseError (ERR_BADBUILTINIMMEDIATE, \"Bad builtin immediate\");\n\t\t\t\tQCC_PR_Lex();\n\n\t\t\t\tif (!*fname && QCC_PR_CheckToken (\":\"))\n\t\t\t\t\tQC_strlcpy(fname, QCC_PR_ParseName(), sizeof(fname));\n\n\t\t\t\t//if the builtin already exists, just use that dfunction instead\n\t\t\t\tif (basedef && basedef->initialized)\n\t\t\t\t{\n\t\t\t\t\tif (*fname)\n\t\t\t\t\t{\n\t\t\t\t\t\tfor (i = 1; i < numfunctions; i++)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (functions[i].code == -1 && functions[i].builtin == binum)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tif (!*functions[i].name)\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tfunctions[i].name = qccHunkAlloc(strlen(fname)+1);\n\t\t\t\t\t\t\t\t\tstrcpy(functions[i].name, fname);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tif (!strcmp(functions[i].name, fname))\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttmp = QCC_MakeIntConst(i);\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tfor (i = 1; i < numfunctions; i++)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (functions[i].code == -1 && functions[i].builtin == binum)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tif (!*functions[i].name || !strcmp(functions[i].name, defname))\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttmp = QCC_MakeIntConst(i);\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (!tmp.cast)\n\t\t\t\t\tf = QCC_PR_GenerateBuiltinFunction(def.sym, binum, *fname?fname:def.sym->name);\n\t\t\t\telse\n\t\t\t\t\tf = NULL;\n\t\t\t}\n\t\t\telse if (QCC_PR_PeekToken(\"{\") || QCC_PR_PeekToken(\"[\"))\n\t\t\t{\n\t\t\t\tif (basedef)\n\t\t\t\t{\n\t\t\t\t\tif (flags&PIF_WRAP)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (!basedef->initialized || !QCC_SRef_Data(def)->_int)\n\t\t\t\t\t\t\tQCC_PR_ParseErrorPrintSRef (ERR_REDECLARATION, def, \"wrapper function does not wrap anything\");\n\t\t\t\t\t}\n\t\t\t\t\telse if (basedef->initialized == 1 && !(flags & PIF_STRONGER))\n\t\t\t\t\t{\n\t\t\t\t\t\t//normally this is an error, but to aid supporting new stuff with old, we convert it into a warning if a vanilla(ish) qc function replaces extension builtins.\n\t\t\t\t\t\t//the qc function is the one that is used, but there is a warning so you know how to gain efficiency.\n\t\t\t\t\t\tint bi = -1;\n\t\t\t\t\t\tif (def.cast->type == ev_function && !arraysize)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (!strcmp(defname, \"anglemod\") || !strcmp(defname, \"crossproduct\"))\n\t\t\t\t\t\t\t\tbi = QCC_SRef_Data(def)->_int;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (bi <= 0 || bi >= numfunctions)\n\t\t\t\t\t\t\tbi = 0;\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tbi = functions[bi].code;\n\t\t\t\t\t\tif (bi < 0)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tQCC_PR_ParseWarning(WARN_NOTSTANDARDBEHAVIOUR, \"%s already declared as a builtin\", defname);\n\t\t\t\t\t\t\tQCC_PR_ParsePrintSRef(WARN_NOTSTANDARDBEHAVIOUR, def);\n\t\t\t\t\t\t\tbasedef->unused = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tQCC_PR_ParseWarning (ERR_REDECLARATION, \"redeclaration of function body\");\n\t\t\t\t\t\t\tQCC_PR_ParsePrintSRef(WARN_NOTSTANDARDBEHAVIOUR, def);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (pr_scope)\n\t\t\t\t{\n//\t\t\t\t\tQCC_PR_ParseErrorPrintSRef (ERR_INITIALISEDLOCALFUNCTION, def, \"initialisation of function body within function body\");\n\t\t\t\t\t//save some state of the parent\n\t\t\t\t\tQCC_def_t *firstlocal = pr.local_head.nextlocal;\n\t\t\t\t\tQCC_def_t *lastlocal = pr.local_tail;\n\t\t\t\t\tQCC_function_t *parent = pr_scope;\n\t\t\t\t\tQCC_statement_t *patch;\n\n\t\t\t\t\t//FIXME: make sure gotos/labels/cases/continues/breaks are not broken by this.\n\n\t\t\t\t\t//generate a goto statement around the nested function, so that nothing is hurt.\n\t\t\t\t\tpatch = QCC_Generate_OP_GOTO();\n\t\t\t\t\tf = QCC_PR_ParseImmediateStatements (def.sym->isstatic?def.sym:NULL, type, flags&PIF_WRAP);\n\t\t\t\t\tpatch->a.jumpofs = &statements[numstatements] - patch;\n\t\t\t\t\tif (patch->a.jumpofs == 1)\n\t\t\t\t\t\tnumstatements--;\t//never mind then.\n\n\t\t\t\t\t//make sure parent state is restored properly.\n\t\t\t\t\tpr.local_head.nextlocal = firstlocal;\n\t\t\t\t\tpr.local_tail = lastlocal;\n\t\t\t\t\tpr_scope = parent;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tf = QCC_PR_ParseImmediateStatements (def.sym, type, flags&PIF_WRAP);\n\n\t\t\t\t//allow dupes if its a builtin\n\t\t\t\tif (basedef && !f->code && basedef->initialized)\n\t\t\t\t{\n\t\t\t\t\tfor (i = 1; i < numfunctions; i++)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (functions[i].code == -f->builtin)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttmp = QCC_MakeIntConst(i);\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tf = NULL;\n\t\t\t\ttmp = QCC_PR_Expression(TOP_PRIORITY, EXPR_DISALLOW_COMMA);\n\t\t\t\ttmp = QCC_EvaluateCast(tmp, type, true);\n\t\t\t}\n\t\t\tif (!tmp.cast && f)\n\t\t\t{\t\n\t\t\t\tif (type->aux_type->size > 3 && !autoprototype)\t//fixme: handle properly, without breaking __out\n\t\t\t\t\tQCC_PR_ParseWarning(WARN_SLOW_LARGERETURN, \"Function %s returns (large) %s. This is inefficient.\", def.sym->name, TypeName(type->aux_type, fname,sizeof(fname)));\n\n\t\t\t\tpr_scope = parentfunc;\n\t\t\t\ttmp = QCC_MakeIntConst(f - functions);\n\n\t\t\t\tif (!basedef && def.sym->temp)\n\t\t\t\t{\t//skip the store-to-temp\n//\t\t\t\t\tQCC_FreeTemp(def);\n\t\t\t\t\ttmp.cast = def.cast;\n\t\t\t\t\treturn tmp;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse if (type->type == ev_string && QCC_PR_CheckName(\"_\"))\n\t\t{\n\t\t\tchar trname[128];\n\t\t\tQCC_PR_Expect(\"(\");\n\t\t\tif (pr_token_type != tt_immediate || pr_immediate_type->type != ev_string)\n\t\t\t\tQCC_PR_ParseError(0, \"_() intrinsic accepts only a string immediate\");\n\t\t\tif (!pr_scope || basedef->constant || basedef->isstatic)\n\t\t\t{\n\t\t\t\t//if this is a static initialiser, embed a dotranslate in there\n\t\t\t\tQCC_SRef_Data(def)->_int = QCC_CopyString (pr_immediate_string);\n\n\t\t\t\tif (!pr_scope || def.sym->constant)\n\t\t\t\t{\n\t\t\t\t\tQCC_def_t *dt;\n\t\t\t\t\tQC_snprintfz(trname, sizeof(trname), \"dotranslate_%i\", ++dotranslate_count);\n\t\t\t\t\tdt = QCC_PR_DummyDef(type_string, trname, pr_scope, 0, def.sym, def.ofs, true, GDF_CONST);\n\t\t\t\t\tdt->referenced = true;\n\t\t\t\t\tdt->constant = 1;\n\t\t\t\t\tdt->initialized = 1;\n\t\t\t\t}\n\t\t\t\tQCC_PR_Lex();\n\t\t\t\tQCC_PR_Expect(\")\");\n\t\t\t\treturn ret?def:nullsref;\n\t\t\t}\n\t\t\ttmp = QCC_MakeTranslateStringConst(pr_immediate_string);\n\t\t\tQCC_PR_Lex();\n\t\t\tQCC_PR_Expect(\")\");\n\t\t}\n\t\telse if (type->type == ev_union && type->num_parms == 1 && !type->params->paramname)\n\t\t{\t//weird typedefed array hack\n\t\t\tdef.cast = (type)->params[0].type;\n\t\t\tret &= QCC_PR_ParseInitializerType((type)->params[0].arraysize, basedef, def, bitofs, flags);\n\t\t\tdef.cast = type;\n\t\t\treturn ret?def:nullsref;\n\t\t}\n\t\telse if ((type->type == ev_struct || type->type == ev_union) && QCC_PR_CheckToken(\"{\"))\n\t\t{\n\t\t\t//structs go recursive\n\t\t\tQCC_type_t *parenttype;\n\t\t\tunsigned int partnum;\n\t\t\tpbool isunion;\n\t\t\tgofs_t offset = def.ofs;\n\t\t\tgofs_t reloffset;\n\t\t\tunsigned int *isinited = alloca(sizeof(*isinited)*type->size);\n\t\t\tconst char *mname;\n\t\t\tstruct QCC_typeparam_s *tp;\n\t\t\tmemset(isinited, 0, sizeof(pbool)*type->size);\n\n\t\t\tif (QCC_PR_PeekToken(\".\"))\t//won't be a float (.0 won't match)\n\t\t\t{\t//designated initialisers\n\t\t\t\tfor(;;)\n\t\t\t\t{\n\t\t\t\t\tif (QCC_PR_CheckToken(\".\"))\n\t\t\t\t\t{\n\t\t\t\t\t\tmname = QCC_PR_ParseName();\n\t\t\t\t\t\tQCC_PR_Expect(\"=\");\n\t\t\t\t\t\ttp = QCC_PR_FindStructMember(type, mname, &def.ofs, &reloffset);\n\t\t\t\t\t\tif (isinited[def.ofs] & (1u<<reloffset))\n\t\t\t\t\t\t\tQCC_PR_ParseError (ERR_EXPECTED, \"%s.%s was already ininitialised\", type->name, mname);\n\t\t\t\t\t\tisinited[def.ofs] |= 1u<<reloffset;\n\t\t\t\t\t\tdef.ofs += offset;\n\t\t\t\t\t\tdef.cast = tp->type;\n\n\t\t\t\t\t\tret &= QCC_PR_ParseInitializerType(tp->arraysize, basedef, def, bitofs+reloffset, flags);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\tQCC_PR_ParseError (ERR_EXPECTED, \"designated initialisers require all members to be initialised the same way\");\n\n\t\t\t\t\tif (QCC_PR_CheckToken(\"}\"))\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tQCC_PR_Expect(\",\");\n\t\t\t\t}\n\t\t\t\tpartnum = 0;\n\t\t\t}\n\t\t\telse if (type->parentclass)\n\t\t\t{\n\t\t\t\tif (!QCC_PR_CheckToken(\"}\"))\n\t\t\t\t\tQCC_PR_ParseError (ERR_EXPECTED, \"inherited structs must use designated initialisers\");\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t//FIXME: inheritance makes stuff weird\n//\t\t\t\tint i;\n\t\t\t\tisunion = ((type)->type == ev_union);\n\t\t\t\tfor (partnum = 0; partnum < (type)->num_parms; partnum++)\n\t\t\t\t{\n\t\t\t\t\tif (QCC_PR_CheckToken(\"}\"))\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tif ((type)->params[partnum].isvirtual)\n\t\t\t\t\t\tcontinue;\t//these are pre-initialised....\n//\t\t\t\t\tif ((type)->params[partnum].optional)\n//\t\t\t\t\t\tcontinue;\t//float parts of a vector.\n\n\t\t\t\t\tdef.cast = (type)->params[partnum].type;\n\t\t\t\t\tdef.ofs = (type)->params[partnum].ofs;\n\t\t\t\t\tif (isinited[def.ofs] & (1<<(type)->params[partnum].bitofs))\n\t\t\t\t\t{\n\t\t\t\t\t\tQCC_PR_ParseError (ERR_EXPECTED, \"member %s.%s was already ininitialised\", type->name, (type)->params[partnum].paramname);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tisinited[def.ofs] |= (1<<(type)->params[partnum].bitofs);\n\t\t\t\t\t/*i = (type)->params[partnum].arraysize;\n\t\t\t\t\tif (!i)\n\t\t\t\t\t\ti = 1;\n\t\t\t\t\tif ((type)->params[partnum].type->bits)\n\t\t\t\t\t\t;\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\ti *= (type)->params[partnum].type->size;\n\t\t\t\t\t\twhile(i --> 0)\n\t\t\t\t\t\t\tisinited[def.ofs+i] |= ~0;\n\t\t\t\t\t}*/\n\t\t\t\t\tdef.ofs += offset;\n\n\t\t\t\t\tret &= QCC_PR_ParseInitializerType((type)->params[partnum].arraysize, basedef, def, bitofs + (type)->params[partnum].bitofs, flags);\n\t\t\t\t\tif (isunion || !QCC_PR_CheckToken(\",\"))\n\t\t\t\t\t{\n\t\t\t\t\t\tQCC_PR_Expect(\"}\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t//anything not already set needs to be filled with a default value.\n\t\t\tfor (parenttype=type; parenttype; parenttype = parenttype->parentclass)\n\t\t\t{\n\t\t\t\tfor (partnum = 0; partnum < (parenttype)->num_parms; partnum++)\n\t\t\t\t{\n\t\t\t\t\t//copy into the def. should be from a const.\n\t\t\t\t\tdef.cast = (parenttype)->params[partnum].type;\n\t\t\t\t\tdef.ofs = (parenttype)->params[partnum].ofs;\n\t\t\t\t\tif (isinited[def.ofs] & (1<<(parenttype)->params[partnum].bitofs))\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\tisinited[def.ofs] |= (1<<(parenttype)->params[partnum].bitofs);\n\t\t\t\t\tdef.ofs += offset;\n\t\t\t\t\tif ((parenttype)->params[partnum].defltvalue.cast)\n\t\t\t\t\t\ttmp = (parenttype)->params[partnum].defltvalue;\n\t\t\t\t\telse\n\t\t\t\t\t\ttmp = QCC_MakeIntConst(0);\n\t\t\t\t\tQCC_ForceUnFreeDef(tmp.sym);\n\t\t\t\t\tQCC_PR_GenerateInitializerType(basedef, tmp, def, def.cast, bitofs, flags);\n\t\t\t\t}\n\t\t\t}\n\t\t\tdef.cast = type;\n\t\t\tdef.ofs = offset;\n\t\t\treturn ret?def:nullsref;\n\t\t}\n\t\telse if (type->type == ev_vector && QCC_PR_PeekToken(\"{\"))\n\t\t{\t//vectors can be treated as an array of 3 floats.\n\t\t\tdef.cast = type_float;\n\t\t\tret &= QCC_PR_ParseInitializerType(3, basedef, def, bitofs, flags);\n\t\t\tdef.cast = type;\n\t\t\treturn ret?def:nullsref;\n\t\t}\n\t\telse if (type->type == ev_pointer && QCC_PR_CheckToken(\"{\"))\n\t\t{\n\t\t\t//generate a temp array\n\t\t\tQCC_ref_t buf, buf2;\n\t\t\ttmp.sym = QCC_PR_DummyDef(type->aux_type, NULL, pr_scope, 0, NULL, 0, false, GDF_STRIP|(pr_scope?GDF_STATIC:0));\n\t\t\ttmp.ofs = 0;\n\t\t\ttmp.cast = tmp.sym->type;\n\n\t\t\ttmp.sym->refcount+=1;\n\n\t\t\t//fill up the array\n\t\t\tdo\n\t\t\t{\n\t\t\t\t//expand the array\n\t\t\t\tunsigned int newsize = tmp.sym->arraysize * tmp.cast->size;\n\t\t\t\tif (tmp.sym->symbolsize < newsize)\n\t\t\t\t{\n\t\t\t\t\tvoid *newdata;\n\t\t\t\t\tnewsize += 64 * tmp.cast->size;\n\t\t\t\t\tnewdata = qccHunkAlloc (newsize * sizeof(float));\n\t\t\t\t\tmemcpy(newdata, tmp.sym->symboldata, tmp.sym->symbolsize*sizeof(float));\n\t\t\t\t\ttmp.sym->symboldata = newdata;\n\t\t\t\t\ttmp.sym->symbolsize = newsize;\n\t\t\t\t}\n\t\t\t\ttmp.sym->arraysize++;\n\t\t\t\t//generate the def...\n\t\t\t\tQCC_PR_DummyDef(tmp.cast, NULL, pr_scope, 0, tmp.sym, tmp.ofs, false, GDF_STRIP|(pr_scope?GDF_STATIC:0));\n\n\t\t\t\t//and fill it in.\n\t\t\t\tret &= QCC_PR_ParseInitializerType(0, tmp.sym, tmp, bitofs, flags);\n\t\t\t\ttmp.ofs += type->aux_type->size;\n\t\t\t} while(QCC_PR_CheckToken(\",\"));\n\t\t\tQCC_PR_Expect(\"}\");\n\n\t\t\t//drop the size back down to something sane\n\t\t\ttmp.sym->symbolsize = tmp.ofs*sizeof(float);\n\n\t\t\t//grab the address of it.\n\t\t\ttmp.ofs = 0;\n\t\t\ttmp = QCC_RefToDef(QCC_PR_GenerateAddressOf(&buf, QCC_DefToRef(&buf2, tmp)), true);\n\t\t}\n\t\telse\n\t\t{\n\t\t\ttmp = QCC_PR_Expression(TOP_PRIORITY, EXPR_DISALLOW_COMMA);\n\t\t\ttmp = QCC_EvaluateCast(tmp, type, true);\n\t\t}\n\n\t\tret = QCC_PR_GenerateInitializerType(basedef, tmp, def, type, bitofs, flags);\n\t}\n\treturn ret?def:nullsref;\n}\nQCC_sref_t QCC_PR_ParseInitializerTemp(QCC_type_t *type)\n{\n\tQCC_sref_t def = QCC_GetTemp(type);\n\tQCC_sref_t imm;\n\timm = QCC_PR_ParseInitializerType_Internal(0, NULL, def, 0, 0);\n\tif (imm.cast)\n\t{\n\t\tif (imm.cast != type)\n\t\t\tQCC_PR_ParseError(ERR_INTERNAL, \"QCC_PR_ParseInitializerTemp changed type\\n\");\n\t\tQCC_FreeTemp(def);\n\t\treturn imm;\t//just use the immediate.\n\t}\n\treturn def;\t//use our silly temp.\n}\n//returns true where its a const/static initialiser. false if non-const/final initialiser\npbool QCC_PR_ParseInitializerType(int arraysize, QCC_def_t *basedef, QCC_sref_t def, unsigned bitofs, unsigned int flags)\n{\n\tif (QCC_PR_ParseInitializerType_Internal(arraysize, basedef, def, bitofs, flags).cast)\n\t\treturn true;\n\treturn false;\n}\n\nvoid QCC_PR_ParseInitializerDef(QCC_def_t *def, unsigned int flags)\n{\n\tpr_ignoredeprecation = !!def->deprecated;\t//mute deprecation warnings if the symbol we're defining has its own warning.\n\tif (QCC_PR_ParseInitializerType(def->arraysize, def, QCC_MakeSRef(def, 0, def->type), 0, flags))\n\t\tif (!def->initialized)\n\t\t\tdef->initialized = 1;\n\tpr_ignoredeprecation = false;\n\tQCC_FreeDef(def);\n}\nQCC_sref_t QCC_PR_ParseDefaultInitialiser(QCC_type_t *type)\n{\n\tQCC_sref_t ref = QCC_PR_Expression(TOP_PRIORITY, EXPR_DISALLOW_COMMA);\n\tif (!ref.sym->constant)\n\t\tQCC_PR_ParseError(0, \"Default value not a constant\\n\");\n\tif (!ref.sym->initialized)\n\t{\n\t\tif (autoprototype)\n\t\t\treturn nullsref;\n\t\tQCC_PR_ParseError(0, \"Default value not initialized yet\\n\");\n\t}\n\treturn QCC_EvaluateCast(ref, type, true);\n}\n\nint accglobalsblock;\t//0 = error, 1 = var, 2 = function, 3 = objdata\n\n\nQCC_type_t *QCC_PR_ParseEnum(pbool flags)\n{\n\tconst char *name = NULL;\n\tQCC_sref_t\t\tsref;\n\tpuint64_t next_i = flags?1:0;\n\tdouble next_f = next_i;\n\tstruct accessor_s *acc;\n\tQCC_type_t *enumtype = NULL, *basetype;\n\tpbool strictenum = false;\n\tbasetype = (flag_assume_integer?type_integer:type_float);\n\n\tif (!QCC_PR_CheckToken(\"{\"))\n\t{\n\t\tQCC_type_t *type;\n\n\t\tstrictenum = QCC_PR_CheckName(\"class\");\t//c++11 style\n\n\t\ttype = autoprototyped?NULL:QCC_PR_ParseType(false, true, false);\t//legacy behaviour\n\t\tif (type)\n\t\t{\n\t\t\tQCC_PR_ParseWarning(WARN_DEPRECACTEDSYNTAX, \"legacy enum base type. Use \\\"enum [class] [name_e]:type\\\" instead\");\n\t\t\tbasetype = type;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (pr_token_type == tt_name)\n\t\t\t\tname = QCC_PR_ParseName();\n\t\t\telse\n\t\t\t\tname = NULL;\n\t\t\tif (QCC_PR_CheckToken(\":\"))\n\t\t\t{\n\t\t\t\tbasetype = QCC_PR_ParseType(false, false, false);\n\t\t\t\tif (!basetype)\n\t\t\t\t\tQCC_PR_ParseError(ERR_NOTATYPE, \"enumflags - must be numeric type\");\n\t\t\t}\n\t\t\telse if (strictenum)\n\t\t\t\tQCC_PR_Expect(\":\");\n\t\t}\n\t\tQCC_PR_Expect(\"{\");\n\t}\n\n\tif (name && !enumtype)\n\t{\n\t\tenumtype = QCC_TypeForName(name);\n\t\tif (!enumtype)\n\t\t{\n\t\t\tif (strictenum)\n\t\t\t{\n\t\t\t\tenumtype = QCC_PR_NewType(name, basetype->type, true);\n\t\t\t\tenumtype->aux_type = basetype;\n\t\t\t\tenumtype->type = ev_enum;\n\t\t\t}\n\t\t\telse\n\t\t\t\tenumtype = QCC_PR_NewType(name, basetype->type, true);\n\t\t\tenumtype->size = basetype->size;\n\t\t}\n\t}\n\n\twhile(1)\n\t{\n\t\tname = QCC_PR_ParseName();\n\t\tif (QCC_PR_CheckToken(\"=\"))\n\t\t{\n\t\t\t/*if (pr_token_type == tt_immediate && pr_immediate_type->type == ev_float)\n\t\t\t{\n\t\t\t\tiv = fv = pr_immediate._float;\n\t\t\t\tQCC_PR_Lex();\n\t\t\t}\n\t\t\telse if (pr_token_type == tt_immediate && pr_immediate_type->type == ev_integer)\n\t\t\t{\n\t\t\t\tfv = iv = pr_immediate._int;\n\t\t\t\tQCC_PR_Lex();\n\t\t\t}\n\t\t\telse*/\n\t\t\t{\n\t\t\t\tconst QCC_eval_t *eval;\n\t\t\t\tsref = QCC_PR_Expression(TOP_PRIORITY, EXPR_DISALLOW_COMMA);\n\t\t\t\tsref = QCC_SupplyConversion(sref, basetype->type, true);\n\t\t\t\teval = QCC_SRef_EvalConst(sref);\n\t\t\t\tif (eval)\n\t\t\t\t{\n\t\t\t\t\tif (sref.cast->type == ev_float)\n\t\t\t\t\t\tnext_i = next_f = eval->_float;\n\t\t\t\t\telse if (sref.cast->type == ev_double)\n\t\t\t\t\t\tnext_i = next_f = eval->_double;\n\t\t\t\t\telse if (sref.cast->type == ev_integer)\n\t\t\t\t\t\tnext_f = (int)(next_i = eval->_int);\n\t\t\t\t\telse if (sref.cast->type == ev_uint)\n\t\t\t\t\t\tnext_f = next_i = eval->_uint;\n\t\t\t\t\telse if (sref.cast->type == ev_int64)\n\t\t\t\t\t\tnext_f = (longlong)(next_i = eval->i64);\n\t\t\t\t\telse if (sref.cast->type == ev_uint64)\n\t\t\t\t\t\tnext_f = next_i = eval->u64;\n\t\t\t\t}\n\t\t\t\telse if (sref.sym)\n\t\t\t\t\tQCC_PR_ParseError(ERR_NOTANUMBER, \"enum - %s is not a compile-time constant\", sref.sym->name);\n\t\t\t\telse\n\t\t\t\t\tQCC_PR_ParseError(ERR_NOTANUMBER, \"enum - not a number\");\n\n\t\t\t\t//do this, because we can. with any luck we'll just hit the same const anyway, and if not then we may have managed to avoid hitting a global.\n\t\t\t\tif (basetype->type==ev_integer)\n\t\t\t\t{\n\t\t\t\t\tQCC_FreeTemp(sref);\n\t\t\t\t\tsref = QCC_MakeIntConst(next_i);\n\t\t\t\t}\n\t\t\t\telse if (basetype->type==ev_uint)\n\t\t\t\t{\n\t\t\t\t\tQCC_FreeTemp(sref);\n\t\t\t\t\tsref = QCC_MakeUIntConst(next_i);\n\t\t\t\t}\n\t\t\t\telse if (basetype->type==ev_int64)\n\t\t\t\t{\n\t\t\t\t\tQCC_FreeTemp(sref);\n\t\t\t\t\tsref = QCC_MakeInt64Const(next_i);\n\t\t\t\t}\n\t\t\t\telse if (basetype->type==ev_uint64)\n\t\t\t\t{\n\t\t\t\t\tQCC_FreeTemp(sref);\n\t\t\t\t\tsref = QCC_MakeUInt64Const(next_i);\n\t\t\t\t}\n\t\t\t\telse if (basetype->type==ev_float)\n\t\t\t\t{\n\t\t\t\t\tQCC_FreeTemp(sref);\n\t\t\t\t\tsref = QCC_MakeFloatConst(next_f);\n\t\t\t\t}\n\t\t\t\telse if (basetype->type==ev_double)\n\t\t\t\t{\n\t\t\t\t\tQCC_FreeTemp(sref);\n\t\t\t\t\tsref = QCC_MakeDoubleConst(next_f);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (basetype->type==ev_integer)\n\t\t\t\tsref = QCC_MakeIntConst(next_i);\n\t\t\telse if (basetype->type==ev_uint)\n\t\t\t\tsref = QCC_MakeUIntConst(next_i);\n\t\t\telse if (basetype->type==ev_int64)\n\t\t\t\tsref = QCC_MakeInt64Const(next_i);\n\t\t\telse if (basetype->type==ev_uint64)\n\t\t\t\tsref = QCC_MakeUInt64Const(next_i);\n\t\t\telse if (basetype->type==ev_float)\n\t\t\t\tsref = QCC_MakeFloatConst(next_f);\n\t\t\telse if (basetype->type==ev_double)\n\t\t\t\tsref = QCC_MakeDoubleConst(next_f);\n\t\t\telse\n\t\t\t\tQCC_PR_ParseError(ERR_NOTANUMBER, \"values for enums of this type must be initialised\");\n\t\t}\n\t\tif (flags)\n\t\t{\n\t\t\tint bits = 0;\n\t\t\tpuint64_t i;\n\t\t\tif (basetype->type == ev_float)\n\t\t\t\ti = (longlong)next_f;\n\t\t\telse if (basetype->type == ev_double)\n\t\t\t\ti = (longlong)next_f;\n\t\t\telse if (basetype->type == ev_integer)\n\t\t\t\ti = (int)next_i;\n\t\t\telse if (basetype->type == ev_uint)\n\t\t\t\ti = (unsigned int)next_i;\n\t\t\telse if (basetype->type == ev_int64)\n\t\t\t\ti = (longlong)next_i;\n\t\t\telse if (basetype->type == ev_uint64)\n\t\t\t\ti = (unsigned longlong)next_i;\n\t\t\telse\n\t\t\t\tQCC_PR_ParseError(ERR_NOTATYPE, \"enumflags - must be numeric type\");\n\t\t\tif (basetype->type!=ev_integer && (double)i != next_f)\n\t\t\t\tQCC_PR_ParseWarning(WARN_ENUMFLAGS_NOTINTEGER, \"enumflags - %f not an integer value\", next_f);\n\t\t\telse\n\t\t\t{\n\t\t\t\twhile(i)\n\t\t\t\t{\n\t\t\t\t\tif (((i>>1u)<<1u) != i)\n\t\t\t\t\t\tbits++;\n\t\t\t\t\ti>>=1u;\n\t\t\t\t}\n\t\t\t\tif (bits > 1)\t//be mute about 0.\n\t\t\t\t\tQCC_PR_ParseWarning(WARN_ENUMFLAGS_NOTINTEGER, \"enumflags - %#\"pPRIx64\"(%\"pPRIi64\") has multiple bits set\", next_i, next_i);\n\t\t\t}\n\t\t}\n\n\t\tsref.sym->referenced = true;\n\n\t\t//value gets added to global pool too (but referable whenever not strict)\n\t\t//we just generate an entirely new def (within the parent's pr_globals allocation). this also gives 'symbol was defined HERE' info.\n\t\tsref.sym = QCC_PR_DummyDef(sref.cast, name, pr_scope, 0, sref.sym, sref.ofs, !strictenum, GDF_CONST|GDF_STRIP);\n\t\tsref.sym->initialized = true;\t//must be true for it to have been considered a compile-time constant.\n\t\tsref.ofs = 0;\n\t\tif (enumtype)\n\t\t{\n\t\t\t//generate enumname::valname symbol info\n\t\t\tfor (acc = enumtype->accessors; acc; acc = acc->next)\n\t\t\t\tif (!strcmp(acc->fieldname, name))\n\t\t\t\t{\n\t\t\t\t\tconst QCC_eval_t *old, *new;\n\t\t\t\t\told = QCC_SRef_EvalConst(acc->staticval);\n\t\t\t\t\tnew = QCC_SRef_EvalConst(sref);\n\t\t\t\t\tif (old && old == new && !typecmp(acc->staticval.cast, sref.cast))\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tQCC_PR_ParseError(ERR_TOOMANYINITIALISERS, \"%s::%s already declared\", enumtype->name, name);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\tif (!acc)\n\t\t\t{\n\t\t\t\tacc = qccHunkAlloc(sizeof(*acc));\n\t\t\t\tacc->fieldname = (char*)name;\n\t\t\t\tacc->next = enumtype->accessors;\n\t\t\t\tacc->type = enumtype;//sref.cast;\n\t\t\t\tacc->indexertype = NULL;\n\t\t\t\tenumtype->accessors = acc;\n\t\t\t\tacc->staticval = sref;\n\t\t\t\tacc->staticval.cast = enumtype;\n\t\t\t}\n\t\t}\n\t\tQCC_FreeTemp(sref);\n\n\t\tif (flags)\n\t\t{\n\t\t\tnext_f *= 2;\n\t\t\tnext_i <<= 1;\n\n\t\t\tif (!next_i)\n\t\t\t\tnext_f = next_i = 1;\t//so you can start with an explicit =0 without needing an =1.\n\t\t}\n\t\telse\n\t\t{\n\t\t\tnext_f++;\n\t\t\tnext_i++;\n\t\t}\n\n\t\tif (QCC_PR_CheckToken(\"}\"))\n\t\t\tbreak;\n\t\tQCC_PR_Expect(\",\");\n\t\tif (QCC_PR_CheckToken(\"}\"))\n\t\t\tbreak; // accept trailing comma\n\t}\n\treturn enumtype?enumtype:basetype;\n}\n\nQCC_sref_t QCC_PR_ParseDefArray(QCC_type_t **type, char *name, pbool istypedef, pbool fixedsize)\n{\n\tQCC_sref_t exr;\n\tQCC_sref_t dynlength = nullsref;\n\tconst QCC_eval_t *eval;\n\tsize_t dim[16];\n\tint dims = 0;\n\tdo\n\t{\n\t\tdim[dims] = 0;\t//not known yet...\n\n\t\tif (dims== 0 && QCC_PR_CheckToken(\"]\"))\n\t\t{\n\t\t\t//not specified, but that may be okay... perhaps.\n\t\t}\n\t\telse\n\t\t{\n\t\t\texr = QCC_PR_Expression(TOP_PRIORITY, 0);\n\t\t\teval = QCC_SRef_EvalConst(exr);\n\t\t\tif (pr_scope && dims==0 && QCC_OPCodeValid(&pr_opcodes[OP_PUSH]) && !flag_qcfuncs && !fixedsize)\n\t\t\t\tdynlength = exr;\n\t\t\telse if (eval)\n\t\t\t{\n\t\t\t\tdim[dims] = QCC_Eval_Int(eval, exr.cast);\n\t\t\t\tQCC_FreeTemp(exr);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tQCC_PR_ParseWarning (ERR_BADARRAYSIZE, \"Array length is not a constant\");\n\t\t\t\tQCC_FreeTemp(exr);\n\t\t\t}\n\t\t\tQCC_PR_Expect(\"]\");\n\t\t}\n\t\tdims++;\n\t} while(QCC_PR_CheckToken (\"[\"));\n\n\tif (dynlength.cast && QCC_PR_PeekToken(\"=\"))\n\t{\t//if its initialised then we need to statically init it. annoying.\n\t\teval = QCC_SRef_EvalConst(dynlength);\n\t\tif (eval)\n\t\t{\n\t\t\tdim[0] = QCC_Eval_Int(eval, dynlength.cast);\n\t\t\tQCC_FreeTemp(dynlength);\n\t\t}\n\t\telse\n\t\t\tQCC_PR_ParseWarning (ERR_BADARRAYSIZE, \"Array length is not a constant\");\n\t\tdynlength = nullsref;\n\t}\n\n#if 1\n\tif (dim[0] == 0 && !istypedef && !dynlength.cast)\n\t{\n\t\tchar *oldprfile = pr_file_p;\n\t\tint oldline = pr_token_line_last;\n\t\tint oldsline = pr_source_line;\n\t\tint depth;\n\n\t\t//FIXME: preprocessor will hate this with a passion.\n\t\tif (QCC_PR_CheckToken(\"=\"))\n\t\t{\n\t\t\tif (pr_token_type == tt_immediate && pr_immediate_type == type_string)\n\t\t\t\tdim[0] = pr_immediate_strlen+1;\n\t\t\telse\n\t\t\t{\n\t\t\t\tQCC_PR_Expect(\"{\");\n\t\t\t\tdim[0]++;\n\t\t\t\tdepth = 1;\n\t\t\t\twhile(1)\n\t\t\t\t{\n\t\t\t\t\tif(pr_token_type == tt_eof)\n\t\t\t\t\t{\n\t\t\t\t\t\tQCC_PR_ParseError (ERR_EOF, \"EOF inside definition of %s\", name);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\telse if (depth == 1 && QCC_PR_CheckToken(\",\"))\n\t\t\t\t\t{\n\t\t\t\t\t\tif (QCC_PR_CheckToken(\"}\"))\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tdim[0]++;\n\t\t\t\t\t}\n\t\t\t\t\telse if (QCC_PR_CheckToken(\"{\") || QCC_PR_CheckToken(\"[\"))\n\t\t\t\t\t\tdepth++;\n\t\t\t\t\telse if (QCC_PR_CheckToken(\"}\") || QCC_PR_CheckToken(\"]\"))\n\t\t\t\t\t{\n\t\t\t\t\t\tdepth--;\n\t\t\t\t\t\tif (depth == 0)\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\tQCC_PR_Lex();\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tpr_file_p = oldprfile;\n\t\t\tpr_token_line = oldline;\n\t\t\tpr_source_line = oldsline;\n\n\t\t\tpr_token_type = tt_punct;\n\t\t\tpr_immediate_type = type_void;\n\t\t\tstrcpy(pr_token, \"=\");\n\t\t}\n\t}\n#endif\n\n\twhile (dims-- > 1)\n\t{\t//go backwards. last parsed is the innermost array.\n\t\t*type = QCC_GenArrayType(*type, dim[dims]);\n\t}\n\n\tif (!dynlength.cast)\n\t\tdynlength.ofs = dim[0];\n\treturn dynlength;\n}\n\n/*\n================\nPR_ParseDefs\n\nCalled at the outer layer and when a local statement is hit\n================\n*/\nvoid QCC_PR_ParseDefs (const char *classname, pbool fatal_unused)\n{\n\tchar\t\t*name;\n\tQCC_type_t\t\t*basetype, *type, *defclass;\n\tQCC_def_t\t\t*def, *d;\n\tQCC_sref_t\t\tdynlength;\n\tQCC_function_t\t*f;\n\tpbool shared=false;\n\tpbool isstatic=defaultstatic;\n\tpbool externfnc=false;\n\tpbool isauto=false;\n\tpbool istypedef=false;\n\tpbool isconstant = false;\n\tpbool isvar = false;\n\tpbool isinitialised = false;\n\tpbool noref = defaultnoref;\n\tpbool nosave = defaultnosave;\n\tpbool allocatenew = true;\n\tpbool inlinefunction = false;\n\tpbool allowinline = false;\n\tpbool dostrip = false;\n\tpbool dowrap = false;\n\tpbool doweak = false;\n\tpbool forceused = false;\n\tpbool accumulate = false;\n\tpbool hadmodifier = false; //if true then its a def. and we assume int when the tye was omitted.\n\tconst char *deprecated = NULL;\n\tint arraysize;\n\tunsigned int gd_flags;\n\tconst char *aliasof = NULL;\n\n\tpr_assumetermtype = NULL;\n\tpr_ignoredeprecation = false;\n\n\twhile (QCC_PR_CheckToken(\";\"))\n\t\t;\n\n\tif (flag_acc)\n\t{\n\t\tchar *oldp;\n\t\tif (QCC_PR_CheckKeyword (keyword_codesys, \"CodeSys\"))\t//reacc support.\n\t\t{\n\t\t\tif (ForcedCRC)\n\t\t\t\tQCC_PR_ParseError(ERR_BADEXTENSION, \"progs crc was already specified - only one is allowed\");\n\t\t\tForcedCRC = (int)pr_immediate._float;\n\t\t\tQCC_PR_Lex();\n\t\t\tQCC_PR_Expect(\";\");\n\t\t\treturn;\n\t\t}\n\n\t\toldp = pr_file_p;\n\t\tif (QCC_PR_CheckKeyword (keyword_var, \"var\"))\t//reacc support.\n\t\t{\n\t\t\tif (accglobalsblock == 3)\n\t\t\t{\n\t\t\t\tif (!QCC_PR_GetDef(type_void, \"end_sys_fields\", NULL, false, 0, false))\n\t\t\t\t\tQCC_PR_GetDef(type_void, \"end_sys_fields\", NULL, true, 0, false);\n\t\t\t}\n\n\t\t\tQCC_PR_ParseName();\n\t\t\tif (QCC_PR_CheckToken(\":\"))\n\t\t\t\taccglobalsblock = 1;\n\t\t\tpr_file_p = oldp;\n\t\t\tQCC_PR_Lex();\n\t\t}\n\n\t\tif (QCC_PR_CheckKeyword (keyword_function, \"function\"))\t//reacc support.\n\t\t{\n\t\t\taccglobalsblock = 2;\n\t\t}\n\t\tif (QCC_PR_CheckKeyword (keyword_objdata, \"objdata\"))\t//reacc support.\n\t\t{\n\t\t\tif (accglobalsblock == 3)\n\t\t\t{\n\t\t\t\tif (!QCC_PR_GetDef(type_void, \"end_sys_fields\", NULL, false, 0, false))\n\t\t\t\t\tQCC_PR_GetDef(type_void, \"end_sys_fields\", NULL, true, 0, false);\n\t\t\t}\n\t\t\telse\n\t\t\t\tif (!QCC_PR_GetDef(type_void, \"end_sys_globals\", NULL, false, 0, false))\n\t\t\t\t\tQCC_PR_GetDef(type_void, \"end_sys_globals\", NULL, true, 0, false);\n\t\t\taccglobalsblock = 3;\n\t\t}\n\n\t\tif (!pr_scope)\n\t\tswitch(accglobalsblock)//reacc support.\n\t\t{\n\t\tcase 1:\n\t\t\t{\n\t\t\tchar *oldp = pr_file_p;\n\t\t\tname = QCC_PR_ParseName();\n\t\t\tif (!QCC_PR_CheckToken(\":\"))\t//nope, it wasn't!\n\t\t\t{\n\t\t\t\tQCC_PR_IncludeChunk(name, true, NULL);\n\t\t\t\tQCC_PR_Lex();\n\t\t\t\tQCC_PR_UnInclude();\n\t\t\t\tpr_file_p = oldp;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (QCC_PR_CheckKeyword(keyword_object, \"object\"))\n\t\t\t\tQCC_PR_GetDef(type_entity, name, NULL, true, 0, true);\n\t\t\telse if (QCC_PR_CheckKeyword(keyword_string, \"string\"))\n\t\t\t\tQCC_PR_GetDef(type_string, name, NULL, true, 0, true);\n\t\t\telse if (QCC_PR_CheckKeyword(keyword_real, \"real\"))\n\t\t\t{\n\t\t\t\tdef = QCC_PR_GetDef(type_float, name, NULL, true, 0, true);\n\t\t\t\tif (QCC_PR_CheckToken(\"=\"))\n\t\t\t\t{\n\t\t\t\t\tdef->symboldata[0]._float = pr_immediate._float;\n\t\t\t\t\tQCC_PR_Lex();\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (QCC_PR_CheckKeyword(keyword_vector, \"vector\"))\n\t\t\t{\n\t\t\t\tdef = QCC_PR_GetDef(type_vector, name, NULL, true, 0, true);\n\t\t\t\tif (QCC_PR_CheckToken(\"=\"))\n\t\t\t\t{\n\t\t\t\t\tQCC_PR_Expect(\"[\");\n\t\t\t\t\tdef->symboldata[0].vector[0] = pr_immediate._float;\n\t\t\t\t\tQCC_PR_Lex();\n\t\t\t\t\tdef->symboldata[0].vector[1] = pr_immediate._float;\n\t\t\t\t\tQCC_PR_Lex();\n\t\t\t\t\tdef->symboldata[0].vector[2] = pr_immediate._float;\n\t\t\t\t\tQCC_PR_Lex();\n\t\t\t\t\tQCC_PR_Expect(\"]\");\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (QCC_PR_CheckKeyword(keyword_pfunc, \"pfunc\"))\n\t\t\t\tQCC_PR_GetDef(type_function, name, NULL, true, 0, true);\n\t\t\telse\n\t\t\t\tQCC_PR_ParseError(ERR_BADNOTTYPE, \"Bad type\\n\");\n\t\t\tQCC_PR_Expect (\";\");\n\n\t\t\tif (QCC_PR_CheckKeyword (keyword_system, \"system\"))\n\t\t\t\tQCC_PR_Expect (\";\");\n\t\t\treturn;\n\t\t\t}\n\t\tcase 2:\n\t\t\tname = QCC_PR_ParseName();\n\t\t\tQCC_PR_GetDef(type_function, name, NULL, true, 0, true);\n\t\t\tQCC_PR_CheckToken (\";\");\n\t\t\treturn;\n\t\tcase 3:\n\t\t\t{\n\t\t\t\tchar *oldp = pr_file_p;\n\t\t\t\tname = QCC_PR_ParseName();\n\t\t\t\tif (!QCC_PR_CheckToken(\":\"))\t//nope, it wasn't!\n\t\t\t\t{\n\t\t\t\t\tQCC_PR_IncludeChunk(name, true, NULL);\n\t\t\t\t\tQCC_PR_Lex();\n\t\t\t\t\tQCC_PR_UnInclude();\n\t\t\t\t\tpr_file_p = oldp;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tif (QCC_PR_CheckKeyword(keyword_object, \"object\"))\n\t\t\t\t\tdef = QCC_PR_GetDef(QCC_PR_FieldType(type_entity), name, NULL, true, 0, GDF_CONST|GDF_SAVED);\n\t\t\t\telse if (QCC_PR_CheckKeyword(keyword_string, \"string\"))\n\t\t\t\t\tdef = QCC_PR_GetDef(QCC_PR_FieldType(type_string), name, NULL, true, 0, GDF_CONST|GDF_SAVED);\n\t\t\t\telse if (QCC_PR_CheckKeyword(keyword_real, \"real\"))\n\t\t\t\t\tdef = QCC_PR_GetDef(QCC_PR_FieldType(type_float), name, NULL, true, 0, GDF_CONST|GDF_SAVED);\n\t\t\t\telse if (QCC_PR_CheckKeyword(keyword_vector, \"vector\"))\n\t\t\t\t\tdef = QCC_PR_GetDef(QCC_PR_FieldType(type_vector), name, NULL, true, 0, GDF_CONST|GDF_SAVED);\n\t\t\t\telse if (QCC_PR_CheckKeyword(keyword_pfunc, \"pfunc\"))\n\t\t\t\t\tdef = QCC_PR_GetDef(QCC_PR_FieldType(type_function), name, NULL, true, 0, GDF_CONST|GDF_SAVED);\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tQCC_PR_ParseError(ERR_BADNOTTYPE, \"Bad type\\n\");\n\t\t\t\t\tQCC_PR_Expect (\";\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tif (!def->initialized)\n\t\t\t\t{\n\t\t\t\t\tunsigned int u;\n\t\t\t\t\tdef->initialized = 1;\n\t\t\t\t\tfor (u = 0; u < def->type->size*(def->arraysize?def->arraysize:1); u++)\t//make arrays of fields work.\n\t\t\t\t\t{\n\t\t\t\t\t\tif (*(int *)&def->symboldata[u])\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tQCC_PR_ParseWarning(0, \"Field def already has a value:\");\n\t\t\t\t\t\t\tQCC_PR_ParsePrintDef(0, def);\n\t\t\t\t\t\t}\n\t\t\t\t\t\t*(int *)&def->symboldata[u] = pr.size_fields+u;\n\t\t\t\t\t}\n\n\t\t\t\t\tpr.size_fields += u;\n\t\t\t\t}\n\n\t\t\t\tQCC_PR_Expect (\";\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n\n\twhile(1)\n\t{\n\t\t//storage classes...\n\t\tif (QCC_PR_CheckKeyword (keyword_typedef, \"typedef\"))\t\t//not storage but defines a type instead.\n\t\t\tistypedef=true;\n\t\telse if (QCC_PR_CheckKeyword(keyword_extern, \"extern\"))\t\t//came from some other unit (or at least outside of the current function)\n\t\t{\n\t\t\tif (!hadmodifier && pr_token_type == tt_immediate && pr_immediate_type==type_string)\n\t\t\t{\t//this C++ism is for parsing types, we don't currently change operator precedence, keywords, etc\n\t\t\t\tpbool old_qcfuncs = flag_qcfuncs;\n\t\t\t\tpbool old_cpriority = flag_cpriority;\n\t\t\t\tpbool old_assumeint = flag_assume_integer;\n\t\t\t\tpbool old_assumef64 = flag_assume_double;\n\t\t\t\tpbool old_assumevar = flag_assumevar;\n\t\t\t\tpbool old_subscope = pr_subscopedlocals;\n\t\t\t\tif (!strcasecmp(pr_token, \"C\"))\n\t\t\t\t{\n\t\t\t\t\tflag_qcfuncs = false;\t//ignore * on funcptrs, promote varg floats to doubles, some other quirks\n\t\t\t\t\tflag_cpriority = true;\n\t\t\t\t\tflag_assume_integer = true;\n\t\t\t\t\t//flag_assume_double = true;\t//not forced, too annoying, but is popped so you can explicitly enable it within the block for some reason.\n\t\t\t\t\tflag_assumevar = true;\n\t\t\t\t\tpr_subscopedlocals = true;\n\t\t\t\t}\n\t\t\t\telse if (!strcasecmp(pr_token, \"QC\"))\n\t\t\t\t{\n\t\t\t\t\tflag_qcfuncs = true;\t//no promotion, func references are simply references, etc\n\t\t\t\t\t//flag_cpriority = false; //refrain from changing it\n\t\t\t\t\t//flag_assume_integer = false;\n\t\t\t\t\t//flag_assume_double = false;\n\t\t\t\t\t//flag_assumevar = false;\n\t\t\t\t\t//pr_subscopedlocals = false;\n\t\t\t\t}\n\t\t\t\tQCC_PR_Lex();\n\n\t\t\t\tif (!QCC_PR_CheckToken (\"{\"))\n\t\t\t\t\tQCC_PR_ParseDefs(NULL, false);\n\t\t\t\telse for(;;)\n\t\t\t\t{\n\t\t\t\t\tif (QCC_PR_CheckToken(\"}\"))\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tif (pr_token_type == tt_eof)\n\t\t\t\t\t{\n\t\t\t\t\t\tQCC_PR_Expect (\"}\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tQCC_PR_ParseDefs(NULL, false);\n\t\t\t\t}\n\t\t\t\t//restore stuff to original mode.\n\t\t\t\t//FIXME: reset all flags+keywords?\n\t\t\t\tflag_qcfuncs = old_qcfuncs;\n\t\t\t\tflag_cpriority = old_cpriority;\n\t\t\t\tflag_assume_integer = old_assumeint;\n\t\t\t\tflag_assume_double = old_assumef64;\n\t\t\t\tflag_assumevar = old_assumevar;\n\t\t\t\tpr_subscopedlocals = old_subscope;\n\t\t\t\treturn;\n\t\t\t}\n\t\t\texternfnc=true;\n\t\t}\n\t\telse if (QCC_PR_CheckKeyword(keyword_auto, \"auto\"))\t\t\t//allocated on the stack like C's locals. may automatically/temporarily be promoted to register... default for C code.\n\t\t\tisauto=true;\n\t\telse if (QCC_PR_CheckKeyword(keyword_register, \"register\"))\t//allow a leading register keyword. technically EVERYTHING is a register in qc, so we can just ignore this.\n\t\t\tisauto=false;\n\t\telse if (QCC_PR_CheckKeyword(keyword_static, \"static\"))\t\t//comes from a global.\n\t\t\tisstatic = true;\n\t\t//else if (QCC_PR_CheckKeyword(keyword_static, \"register\"))\t//bans address-of FIXME: this is checked as part of the type as it needs to be valid for function args too.\n\t\t//\tisregister = true;\n\t\t//else if (QCC_PR_CheckKeyword(keyword_static, \"local\"))\t//QC weirdness. like register, but you can still take an address of it etc...\n\t\t//\tislocal = true;\n\n\t\telse if (QCC_PR_CheckKeyword(keyword_shared, \"shared\"))\n\t\t{\n\t\t\tshared=true;\n\t\t\tif (pr_scope)\n\t\t\t\tQCC_PR_ParseError (ERR_NOSHAREDLOCALS, \"Cannot have shared locals\");\n\t\t}\n\t\telse if (QCC_PR_CheckKeyword(keyword_const, \"const\"))\n\t\t\tisconstant = true;\n\t\telse if (QCC_PR_CheckKeyword(keyword_var, \"var\"))\n\t\t\tisvar = true;\n\t\telse if (!pr_scope && QCC_PR_CheckKeyword(keyword_nonstatic, \"nonstatic\"))\n\t\t\tisstatic = false;\n\t\telse if (QCC_PR_CheckKeyword(keyword_unused, \"unused\") || QCC_PR_CheckKeyword(keyword_noref, \"noref\"))\n\t\t\tnoref=true;\n\t\telse if (QCC_PR_CheckKeyword(keyword_used, \"used\"))\n\t\t\tforceused=true;\n\t\telse if (QCC_PR_CheckKeyword(keyword_nosave, \"nosave\"))\n\t\t\tnosave = true;\n\t\telse if (QCC_PR_CheckKeyword(keyword_strip, \"strip\") || QCC_PR_CheckKeyword(keyword_ignore, \"ignore\"))\n\t\t\tdostrip = true;\n\t\telse if (QCC_PR_CheckKeyword(keyword_inline, \"inline\"))\n\t\t\tallowinline = true;\n\t\telse if (QCC_PR_CheckKeyword(keyword_wrap, \"wrap\"))\n\t\t\tdowrap = true;\n\t\telse if (QCC_PR_CheckKeyword(keyword_weak, \"weak\"))\n\t\t\tdoweak = true;\n\t\telse if (QCC_PR_CheckKeyword(keyword_accumulate, \"accumulate\"))\n\t\t\taccumulate = true;\n\t\telse if (QCC_PR_CheckKeyword(false, \"deprecated\"))\n\t\t{\n\t\t\tif (!QCC_PR_CheckToken(\"(\"))\n\t\t\t\tdeprecated = \"\";\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (pr_token_type == tt_immediate && pr_immediate_type == type_string)\n\t\t\t\t{\n\t\t\t\t\tif (deprecated)\n\t\t\t\t\t{\n\t\t\t\t\t\tchar *n = qccHunkAlloc(strlen(deprecated)+2+strlen(pr_immediate_string)+1);\n\t\t\t\t\t\tsprintf(n, \"%s; %s\", deprecated, pr_immediate_string);\n\t\t\t\t\t\tdeprecated = n;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\tdeprecated = strcpy(qccHunkAlloc(strlen(pr_immediate_string)+1), pr_immediate_string);\n\t\t\t\t\tQCC_PR_Lex();\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tdeprecated = \"\";\n\t\t\t\tQCC_PR_Expect(\")\");\n\t\t\t}\n\t\t}\n\t\telse if ((hadmodifier||(flag_attributes && !pr_scope)) && QCC_PR_CheckToken(\"[\"))\n\t\t{\n\t\t\tQCC_PR_Expect(\"[\");\n\t\t\twhile(pr_token_type != tt_eof)\n\t\t\t{\n\t\t\t\tif (QCC_PR_CheckToken(\",\"))\n\t\t\t\t\tcontinue;\n\t\t\t\telse if (QCC_PR_CheckToken(\"]\"))\n\t\t\t\t\tbreak;\n\t\t\t\telse if (QCC_PR_CheckName(\"alias\"))\n\t\t\t\t{\n\t\t\t\t\tQCC_PR_Expect(\"(\");\n\t\t\t\t\tif (pr_token_type == tt_name)\n\t\t\t\t\t\taliasof = QCC_PR_ParseName();\n\t\t\t\t\telse if (pr_token_type == tt_immediate)\n\t\t\t\t\t{\n\t\t\t\t\t\taliasof = strcpy(qccHunkAlloc(strlen(pr_immediate_string)+1), pr_immediate_string);\n\t\t\t\t\t\tQCC_PR_Lex();\n\t\t\t\t\t}\n\t\t\t\t\tQCC_PR_Expect(\")\");\n\t\t\t\t}\n\t\t\t\telse if (QCC_PR_CheckName(\"accumulate\"))\n\t\t\t\t{\n\t\t\t\t\tif (accumulate)\n\t\t\t\t\t\tQCC_PR_ParseWarning(WARN_GMQCC_SPECIFIC, \"accumulating an accumulating accumulation\");\n\t\t\t\t\taccumulate = true;\n\t\t\t\t}\n\t\t\t\telse if (QCC_PR_CheckName(\"eraseable\"))\n\t\t\t\t\tnoref = true;\n\t\t\t\telse if (QCC_PR_CheckKeyword(false, \"deprecated\"))\n\t\t\t\t{\n\t\t\t\t\tif (!QCC_PR_CheckToken(\"(\"))\n\t\t\t\t\t\tdeprecated = \"\";\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tif (pr_token_type == tt_immediate && pr_immediate_type == type_string)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tdeprecated = strcpy(qccHunkAlloc(strlen(pr_immediate_string)+1), pr_immediate_string);\n\t\t\t\t\t\t\tQCC_PR_Lex();\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tQCC_PR_ParseError (ERR_EXPECTED, \"expected string\");\n\t\t\t\t\t\tQCC_PR_Expect(\")\");\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tQCC_PR_ParseWarning(WARN_GMQCC_SPECIFIC, \"Unknown attribute \\\"%s\\\"\", pr_token);\n\t\t\t\t\twhile(pr_token_type != tt_eof)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (QCC_PR_PeekToken(\"]\"))\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tif (QCC_PR_PeekToken(\",\"))\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tQCC_PR_Lex();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tQCC_PR_Expect(\"]\");\n\t\t}\n\t\telse\n\t\t\tbreak;\n\t\thadmodifier = true;\n\t}\n\n\tbasetype = QCC_PR_ParseType (false, hadmodifier, !flag_qcfuncs);\n\tif (basetype == NULL)\t//ignore\n\t{\n\t\tif (hadmodifier)\n\t\t{\t//banned since c99...\n\t\t\t//if (c23 && isauto) basetype=guess; else\n\t\t\tbasetype = type_integer;\n\t\t}\n\t\telse\n\t\t\treturn;\n\t}\n\n\tinlinefunction = type_inlinefunction;\n\n\tif (externfnc && !pr_scope && basetype->type != ev_function)\n\t{\n\t\tif (flag_qcfuncs)\n\t\t\tQCC_PR_ParseWarning(WARN_IGNOREDKEYWORD, \"extern keyword may only apply to qc functions.\");\n\t\texternfnc=false;\n\t}\n\n\tif (!pr_scope && QCC_PR_CheckKeyword(keyword_function, \"function\"))\t//reacc support.\n\t{\n\t\tname = QCC_PR_ParseName ();\n\t\tQCC_PR_Expect(\"(\");\n\t\ttype = QCC_PR_ParseFunctionTypeReacc(false, basetype);\n\t\tQCC_PR_Expect(\";\");\n\n\t\tif (istypedef)\n\t\t\treturn;\n\t\telse\n\t\t\tdef = QCC_PR_GetDef (basetype, name, NULL, true, 0, false);\n\n\t\tif (autoprototype || dostrip)\n\t\t{\t//ignore the code and stuff\n\n\t\t\tif (QCC_PR_CheckKeyword(keyword_external, \"external\"))\n\t\t\t{\t//builtin\n\t\t\t\tQCC_PR_Lex();\n\t\t\t\tQCC_PR_Expect(\";\");\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tint blev = 1;\n\n\t\t\t\twhile (!QCC_PR_CheckToken(\"{\"))\t//skip over the locals.\n\t\t\t\t{\n\t\t\t\t\tif (pr_token_type == tt_eof)\n\t\t\t\t\t{\n\t\t\t\t\t\tQCC_PR_ParseError(0, \"Unexpected EOF\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tQCC_PR_Lex();\n\t\t\t\t}\n\n\t\t\t\t//balance out the { and }\n\t\t\t\twhile(blev)\n\t\t\t\t{\n\t\t\t\t\tif (pr_token_type == tt_eof)\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tif (QCC_PR_CheckToken(\"{\"))\n\t\t\t\t\t\tblev++;\n\t\t\t\t\telse if (QCC_PR_CheckToken(\"}\"))\n\t\t\t\t\t\tblev--;\n\t\t\t\t\telse\n\t\t\t\t\t\tQCC_PR_Lex();\t//ignore it.\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tdef->referenced = true;\n\n\t\t\tf = QCC_PR_ParseImmediateStatements (def, basetype, false);\n\n\t\t\tdef->initialized = 1;\n\t\t\tdef->isstatic = isstatic;\n\t\t\tdef->symboldata[0].function = numfunctions;\n\t\t\tf->def = def;\n\t//\t\t\t\tif (pr_dumpasm)\n\t//\t\t\t\t\tPR_PrintFunction (def);\n\n\t\t\tif (numfunctions >= MAX_FUNCTIONS)\n\t\t\t\tQCC_Error(ERR_INTERNAL, \"Too many function defs\");\n\t\t}\n\t\treturn;\n\t}\n\n//\tif (pr_scope && (type->type == ev_field) )\n//\t\tQCC_PR_ParseError (\"Fields must be global\");\n\n\tdo\n\t{\n\t\tisinitialised = false;\n\t\ttype = basetype;\n\n\t\tif (QCC_PR_CheckToken (\";\"))\n\t\t{\n\t\t\tif (istypedef)\n\t\t\t{\n\t\t\t\tQCC_PR_ParseWarning(WARN_UNEXPECTEDPUNCT, \"typedef defines no types\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (type->type == ev_field && (type->aux_type->type == ev_union || type->aux_type->type == ev_struct))\n\t\t\t{\n\t\t\t\tQCC_PR_ExpandUnionToFields(type, &pr.size_fields);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (type->type == ev_struct && strcmp(type->name, \"struct\"))\n\t\t\t\treturn;\t//allow named structs\n\t\t\tif (type->type == ev_entity && type != type_entity)\n\t\t\t\treturn;\t//allow forward class definititions with or without a variable. \n\t\t\tif (type->type == ev_accessor)\t//accessors shouldn't trigger problems if they're just a type.\n\t\t\t\treturn;\n//\t\t\tif (type->type == ev_union)\n//\t\t\t{\n//\t\t\t\treturn;\n//\t\t\t}\n//\t\t\tQCC_PR_ParseError (WARN_TYPEWITHNONAME, \"type (%s) with no name\", type->name);\n\t\t\treturn;\n\t\t}\n\n\t\tif (!istypedef && !classname && type->typedefed && QCC_PR_CheckToken(\"::\"))\n\t\t{\n\t\t\tclassname = type->name;\t//FIXME: doesn't work with commas...\n\t\t\ttype = type_invalid;\n\t\t}\n\t\telse while (QCC_PR_CheckToken (\"*\"))\n\t\t\ttype = QCC_PointerTypeTo(type);\n\n\t\tarraysize = 0;\n\t\tname = NULL;\n\t\twhile(QCC_PR_CheckToken (\"(\"))\n\t\t{\n\t\t\tQCC_type_t *ftype;\n\t\t\tint isptr = 0;\n\t\t\tftype = QCC_PR_ParseFunctionType(false, type);\n\t\t\tif (ftype)\n\t\t\t{\t//qc-style function... possibly returning a function.\n\t\t\t\ttype = ftype;\n\t\t\t}\n\t\t\telse\n\t\t\t{\t//c-style function pointer\n\t\t\t\twhile (QCC_PR_CheckToken(\"*\"))\n\t\t\t\t\tisptr++;\n\t\t\t\tname = QCC_PR_ParseName ();\n\t\t\t\tif (QCC_PR_CheckToken (\"[\"))\n\t\t\t\t{\n\t\t\t\t\tif (QCC_PR_CheckToken (\"]\"))\n\t\t\t\t\t\tarraysize = -1;\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tarraysize = QCC_PR_IntConstExpr();\n\t\t\t\t\t\tQCC_PR_Expect (\"]\");\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tQCC_PR_Expect (\")\");\n\t\t\t\tif (!istypedef)\n\t\t\t\t\tisvar |= isptr>0;\n\t\t\t\tif (QCC_PR_CheckToken (\"(\"))\n\t\t\t\t{\n\t\t\t\t\ttype = QCC_PR_ParseFunctionType(false, type);\n\t\t\t\t\tif (!type)\n\t\t\t\t\t\tQCC_PR_ParseError(ERR_BADNOTTYPE, \"expected function arg list\");\n\t\t\t\t}\n\t\t\t\twhile(isptr-- > 1)\t//C's function pointers are basically the same as a qc function reference, though the definition is a bit different.\n\t\t\t\t\ttype = QCC_PointerTypeTo(type);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (!name)\n\t\t\tname = QCC_PR_ParseName ();\n\n\t\tif (!istypedef && !classname && QCC_PR_CheckToken(\"::\"))\n\t\t{\n\t\t\tclassname = name;\t//FIXME: doesn't work with commas...\n\t\t\tname = QCC_PR_ParseName();\n\t\t}\n\n\t\tif (type == type_invalid)\n\t\t{\n\t\t\ttype = type_void;\n\t\t\tif (!strcmp(classname, name))\n\t\t\t\t;\t//constructor. allowed.\n\t\t\telse\n\t\t\t\tQCC_PR_ParseWarning(ERR_NOTATYPE, \"no type specified for %s::%s. this is only allowed for constructors\", classname, name);\n\t\t}\n\n//check for an array\n\n\t\tdynlength = nullsref;\n\t\tif (!arraysize && QCC_PR_CheckToken (\"[\"))\n\t\t{\n\t\t\tdynlength = QCC_PR_ParseDefArray(&type, name, istypedef, istypedef||isstatic);\n\t\t\tif (dynlength.cast)\n\t\t\t\tarraysize = 0;\n\t\t\telse\n\t\t\t{\n\t\t\t\tarraysize = dynlength.ofs;\n\t\t\t\tif (!arraysize)\n\t\t\t\t\tarraysize = -1;\n\t\t\t}\n\t\t}\n\n\t\tif (QCC_PR_CheckToken(\"(\"))\n\t\t{\n\t\t\tif (inlinefunction)\n\t\t\t\tQCC_PR_ParseWarning(WARN_UNSAFEFUNCTIONRETURNTYPE, \"Function returning function. Is this what you meant? (suggestion: use typedefs)\");\n\t\t\tinlinefunction = false;\n\t\t\tif (!flag_qcfuncs && pr_scope)\n\t\t\t\tisconstant = externfnc = true;\t//in C, a locally defined function refers to an external one.\n\t\t\ttype = QCC_PR_ParseFunctionType(false, type);\n\t\t\tif (!type)\n\t\t\t\tQCC_PR_ParseError(ERR_BADNOTTYPE, \"expected function arg list\");\n\t\t}\n\n\t\tallocatenew = true;\n\t\tif (classname)\n\t\t{\n\t\t\tunsigned int ofs, bitofs;\n\t\t\tstruct QCC_typeparam_s *p;\n\t\t\tchar *membername = name;\n\t\t\tname = qccHunkAlloc(strlen(classname) + strlen(name) + 3);\n\t\t\tsprintf(name, \"%s::%s\", classname, membername);\n\t\t\tdefclass = QCC_TypeForName(classname);\n\t\t\tallocatenew = (dynlength.cast || aliasof)?false:2;\n\t\t\tif (defclass && defclass->type == ev_struct)\n\t\t\t\tallocatenew = false;\n\t\t\telse if (!defclass || !defclass->parentclass)\n\t\t\t\tQCC_PR_ParseError(ERR_NOTANAME, \"%s is not a class\\n\", classname);\n\n\t\t\tif (defclass->type == ev_struct)\n\t\t\t{\n\t\t\t\tp = QCC_PR_FindStructMember(defclass, membername, &ofs, &bitofs);\n\t\t\t\tif (p && p->isvirtual)\n\t\t\t\t\ttype = QCC_PR_MakeThiscall(type, defclass);\n\t\t\t}\n\t\t}\n\t\telse\n\t\t\tdefclass = NULL;\n\n\t\tif (istypedef+externfnc+isauto+isstatic+shared+(aliasof!=NULL)>1)\n\t\t\tQCC_PR_ParseWarning(WARN_IGNOREDKEYWORD, \"Conflicting storage classes.\");\n\t\tif (isconstant+isvar>1)\n\t\t\tQCC_PR_ParseWarning(WARN_IGNOREDKEYWORD, \"Conflicting constness.\");\n\t\tif ((allowinline || dowrap || doweak || accumulate) && type->type != ev_function)\n\t\t\tQCC_PR_ParseWarning(WARN_IGNOREDKEYWORD, \"function modifier on non-function.\");\n\n\t\tif (istypedef)\n\t\t{\n\t\t\tQCC_type_t *old;\n\t\t\tif (externfnc||shared||isconstant||isvar||forceused||dostrip||allowinline||dowrap||doweak||accumulate||aliasof||deprecated\n\t\t\t\t\t||(isstatic && !defaultstatic)\n\t\t\t\t\t||(noref && !defaultnoref)\n\t\t\t\t\t||(nosave && !defaultnosave) )\n\t\t\t\tQCC_PR_ParseWarning(ERR_BADEXTENSION, \"bad combination of modifiers with typedef (defining %s%s%s)\", col_type,name,col_none);\n\t\t\tif (arraysize)\n\t\t\t{\n\t\t\t\tstruct QCC_typeparam_s *param = qccHunkAlloc(sizeof(*param));\n//\t\t\t\tQCC_PR_ParseWarning(ERR_BADEXTENSION, \"unsupported typedefed array (defining %s%s%s[%i])\", col_type,name,col_none, arraysize);\n\t\t\t\tparam->type = type;\n\t\t\t\tparam->arraysize = arraysize;\n\t\t\t\tparam->paramname = NULL;\n\t\t\t\ttype = QCC_PR_NewType(name, ev_union, true);\n\t\t\t\ttype->params = param;\n\t\t\t\ttype->num_parms = 1;\n\t\t\t\ttype->size = param->type->size * param->arraysize;\n\t\t\t}\n\t\t\telse if (dynlength.cast)\n\t\t\t{\n\t\t\t\tQCC_PR_ParseWarning(ERR_BADEXTENSION, \"unsupported typedefed array (defining %s%s%s[])\", col_type,name,col_none);\n\t\t\t\ttype = QCC_PointerTypeTo(type);\n\t\t\t}\n\n\t\t\told = QCC_TypeForName(name);\n\t\t\tif (old && old->scope == pr_scope)\n\t\t\t{\n\t\t\t\tif (typecmp(old, type))\n\t\t\t\t{\n\t\t\t\t\tchar obuf[1024];\n\t\t\t\t\tchar nbuf[1024];\n\t\t\t\t\tQCC_PR_ParseWarning(ERR_NOTATYPE, \"Cannot redeclare typedef %s%s%s from %s%s%s to %s%s%s\", col_type,name,col_none, col_type,TypeName(old, obuf, sizeof(obuf)),col_none, col_type,TypeName(type, nbuf, sizeof(nbuf)),col_none);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\told = type;\n\t\t\t\ttype = QCC_PR_NewType(name, ev_typedef, true);\n\t\t\t\ttype->aux_type = old;\n\t\t\t\ttype->scope = pr_scope;\n\t\t\t}\n\n\t\t\tdef = NULL;\n\t\t\tcontinue;\n\t\t}\n\n\t\tisinitialised = QCC_PR_CheckToken (\"=\") || ((type->type == ev_function) && (pr_token[0] == '{' || pr_token[0] == '[' || pr_token[0] == ':'));\n\n\t\tgd_flags = 0;\n\t\tif (isstatic)\n\t\t\tgd_flags |= GDF_STATIC;\n\t\tif (isconstant || (!isvar && !pr_scope && ((isinitialised && !flag_assumevar) || type->type == ev_function || type->type == ev_field)))\n\t\t\tgd_flags |= GDF_CONST;\t//initialised things are assumed to be consts, unless assumevar is specified. functions and fields are always assumed to be consts even when not initialised. nothing is assumed const at local scope.\n\t\tif (!nosave)\n\t\t\tgd_flags |= GDF_SAVED;\n\t\tif (allowinline)\n\t\t\tgd_flags |= GDF_INLINE;\n\t\tif (dostrip)\n\t\t\tgd_flags |= GDF_STRIP;\n\t\telse if (forceused)\t//FIXME: make proper pragma(used) thingie\n\t\t\tgd_flags |= GDF_USED;\n\n\t\tif (!type->size)\n\t\t{\n//\t\t\tchar buf[1024];\n//\t\t\tQCC_PR_ParseError(ERR_BADEXTENSION, \"type %s%s%s not yet defined, cannot create %s%s%s\", col_type,TypeName(type,buf,sizeof(buf)),col_none, col_name,name,col_none);\n\t\t}\n\t\tif (dynlength.cast && !aliasof)\n\t\t{\n\t\t\tconst QCC_eval_t *eval;\n\t\t\tsize_t fixedlen;\n\t\t\tdynlength = QCC_SupplyConversion(dynlength, ev_uint, true);\n\t\t\teval = QCC_SRef_EvalConst(dynlength);\n\t\t\tif (eval)\n\t\t\t{\n\t\t\t\tgd_flags |= GDF_AUTODEREF;\n\t\t\t\tfixedlen = QCC_Eval_Int(eval, dynlength.cast);\n\t\t\t\tdef = QCC_PR_GetDef (QCC_PR_PointerType(QCC_GenArrayType(type, fixedlen)), name, pr_scope, allocatenew, 0, gd_flags);\n\t\t\t}\n\t\t\telse\n\t\t\t\tdef = QCC_PR_GetDef (QCC_PR_PointerType(type), name, pr_scope, allocatenew, 0, gd_flags);\n\n\t\t\tif (type->bits)\t//OP_PUSH takes words. we need to round up.\n\t\t\t{\n\t\t\t\tdynlength = QCC_PR_Statement(pr_opcodes+OP_MUL_I, dynlength, QCC_MakeUIntConst(type->bits/8), NULL);\t//convert to bytes\n\t\t\t\tdynlength = QCC_PR_Statement(pr_opcodes+OP_ADD_I, dynlength, QCC_MakeUIntConst(3), NULL);\t//extend by the worst case\n\t\t\t\tdynlength = QCC_PR_Statement(pr_opcodes+OP_DIV_I, dynlength, QCC_MakeUIntConst(4), NULL);\t//convert to words\n\t\t\t}\n\t\t\telse if (type->size != 1)\n\t\t\t\tdynlength = QCC_PR_Statement(pr_opcodes+OP_MUL_I, dynlength, QCC_MakeIntConst(type->size), NULL);\n\t\t\tQCC_PR_SimpleStatement(&pr_opcodes[OP_PUSH], dynlength, nullsref, QCC_MakeSRef(def, 0, def->type), false);\t//push *(int*)&a elements\n\t\t\tQCC_FreeTemp(dynlength);\n\t\t\tQCC_FreeDef(def);\n\t\t}\n\t\telse if (isauto)\n\t\t{\n\t\t\tgd_flags |= GDF_AUTODEREF;\n\t\t\tif (arraysize)\n\t\t\t\ttype = QCC_GenArrayType(type, arraysize);\n\t\t\tdef = QCC_PR_GetDef (QCC_PR_PointerType(type), name, pr_scope, allocatenew, 0, gd_flags);\n\t\t\tif (pr_scope && !isstatic)\n\t\t\t\tQCC_PR_SimpleStatement(&pr_opcodes[OP_PUSH], QCC_MakeIntConst(type->size), nullsref, QCC_MakeSRef(def, 0, def->type), false);\t//push *(int*)&a elements\n\t\t\telse\n\t\t\t{\n\t\t\t\tQCC_sref_t f = QCC_PR_EmulationFunc(memalloc);\n\t\t\t\tif (!f.cast) f = QCC_PR_EmulationFunc(malloc);\n\t\t\t\tif (!f.cast) QCC_PR_ParseError(ERR_BADEXTENSION, \"memalloc not yet defined, cannot define __auto %s at global scope\", name);\n\t\t\t\tif (QCC_OPCodeValid(&pr_opcodes[OP_CALL1H]))\n\t\t\t\t\tQCC_PR_SimpleInitStatement(&pr_opcodes[OP_CALL1H], f, QCC_MakeIntConst(type->size*4), nullsref);\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tQCC_PR_SimpleInitStatement(&pr_opcodes[OP_STORE_F], QCC_MakeIntConst(type->size*4), QCC_MakeSRef(&def_parms[0], 0, type_integer), nullsref);\n\t\t\t\t\tQCC_PR_SimpleInitStatement(&pr_opcodes[OP_CALL1], f, nullsref, nullsref);\n\t\t\t\t}\n\t\t\t\tQCC_PR_SimpleInitStatement(&pr_opcodes[QCC_OPCodeValid(&pr_opcodes[OP_STORE_P])?OP_STORE_P:OP_STORE_I], QCC_MakeSRef(&def_ret, 0, def->type), QCC_MakeSRef(def, 0, def->type), nullsref);\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (aliasof)\n\t\t\t{\n\t\t\t\tif (dynlength.cast)\n\t\t\t\t\tQCC_PR_ParseWarning(ERR_BADEXTENSION, \"array aliases are not supported\");\n\t\t\t\tdef = QCC_PR_GetDef (NULL, aliasof, externfnc?NULL:pr_scope, false, arraysize, gd_flags);\n\t\t\t\tif (!def)\n\t\t\t\t\tQCC_PR_ParseError(ERR_BADEXTENSION, \"%s not yet defined, cannot create %s as an alias\", aliasof, name);\n\t\t\t\tdef->referenced = true;\n\t\t\t\tdef = QCC_PR_DummyDef(type, name, externfnc?NULL:pr_scope, arraysize, def, 0, true, gd_flags);\n\t\t\t}\n\t\t\telse if (allocatenew != 1)\n\t\t\t{\t//we always allocate here, because it lets us handle syntax errors a little more gracefully, but an error is still an error and will be fatal later.\n\t\t\t\tdef = QCC_PR_GetDef (type, name, externfnc?NULL:pr_scope, false, arraysize, gd_flags);\n\t\t\t\tif (!def)\n\t\t\t\t{\n\t\t\t\t\tQCC_PR_ParseWarning(allocatenew?WARN_MEMBERNOTDEFINED:ERR_NOTDEFINED, \"%s is not part of class %s\", name, classname);\n\t\t\t\t\tdef = QCC_PR_GetDef (type, name, externfnc?NULL:pr_scope, true, arraysize, gd_flags);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t\tdef = QCC_PR_GetDef (type, name, externfnc?NULL:pr_scope, allocatenew, arraysize, gd_flags);\n\t\t}\n\n\t\tif (!def)\n\t\t\tQCC_PR_ParseError(ERR_NOTANAME, \"%s is not part of class %s\", name, classname);\n\n\t\tif (accumulate && (def->type->type != ev_function || def->arraysize != 0))\n\t\t\tQCC_PR_ParseError(ERR_NOTAFUNCTION, \"accumulate applies only to functions, not %s\", def->name);\n\n\t\tif (noref)\n\t\t{\n\t\t\tdef->unused = true;\n\t\t\tdef->referenced = true;\n\t\t}\n\n\t\tif (!def->initialized && shared)\t//shared count as initiialised\n\t\t{\n\t\t\tdef->shared = shared;\n\t\t\tdef->initialized = true;\n\t\t\tdef->nofold = true;\n\t\t}\n\t\tif (externfnc && !pr_scope)\n\t\t{\n\t\t\tdef->initialized = true;\n\t\t\tdef->isextern = true;\n\t\t}\n\n\t\tif (deprecated)\n\t\t\tdef->deprecated = deprecated;\n\n\t\tif (isstatic)\n\t\t{\n\t\t\tif (!strcmp(def->filen, s_filen))\n\t\t\t\tdef->isstatic = isstatic;\n\t\t\telse //if (type->type != ev_function && defaultstatic)\t//functions don't quite consitiute a definition\n\t\t\t\tQCC_PR_ParseErrorPrintDef (ERR_REDECLARATION, def, \"can't redefine non-static as static\");\n\t\t}\n\n// check for an initialization\n\t\t/*if (type->type == ev_function && (pr_scope))\n\t\t{\n\t\t\tif ( QCC_PR_CheckToken (\"=\") )\n\t\t\t{\n\t\t\t\tQCC_PR_ParseError (ERR_INITIALISEDLOCALFUNCTION, \"local functions may not be initialised\");\n\t\t\t}\n\n\t\t\td = def;\n\t\t\twhile (d != def->deftail)\n\t\t\t{\n\t\t\t\td = d->next;\n\t\t\t\td->initialized = 1;\t//fake function\n\t\t\t\td->symboldata[0].function = 0;\n\t\t\t}\n\n\t\t\tcontinue;\n\t\t}*/\n\n\t\tif (type->type == ev_field && QCC_PR_CheckName (\"alias\"))\n\t\t{\n\t\t\tQCC_PR_ParseError(ERR_INTERNAL, \"FTEQCC does not support this variant of decompiled hexenc\\nPlease obtain the original version released by Raven Software instead.\");\n\t\t\tname = QCC_PR_ParseName();\n\t\t}\n\t\telse if (isinitialised)\t//this is an initialisation (or a function)\n\t\t{\n\t\t\tpbool isconst;\n\t\t\tQCC_type_t *parentclass;\n\t\t\tif (aliasof)\n\t\t\t\tQCC_PR_ParseError (ERR_SHAREDINITIALISED, \"alias %s may not be initialised\", name);\n\t\t\tif (def->shared)\n\t\t\t\tQCC_PR_ParseError (ERR_SHAREDINITIALISED, \"shared values may not be assigned an initial value\");\n\n\t\t\t//if weak, only use the first non-weak version of the function\n\t\t\tif (autoprototype || dostrip || (def->initialized && doweak) || (!def->initialized && doweak && dowrap))\n\t\t\t{\t//ignore the code and stuff\n\t\t\t\tif ((dostrip || (doweak && dowrap)))\n\t\t\t\t\tdef->unused = true;\n\t\t\t\tif (dostrip)\n\t\t\t\t\tdef->referenced = true;\n\t\t\t\tif (QCC_PR_CheckToken(\"[\"))\n\t\t\t\t{\n\t\t\t\t\twhile (!QCC_PR_CheckToken(\"]\"))\n\t\t\t\t\t{\n\t\t\t\t\t\tif (pr_token_type == tt_eof)\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tQCC_PR_Lex();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (QCC_PR_CheckToken(\"{\"))\n\t\t\t\t{\n\t\t\t\t\tint blev = 1;\n\t\t\t\t\t//balance out the { and }\n\t\t\t\t\twhile(blev)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (pr_token_type == tt_eof)\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tif (QCC_PR_CheckToken(\"{\"))\n\t\t\t\t\t\t\tblev++;\n\t\t\t\t\t\telse if (QCC_PR_CheckToken(\"}\"))\n\t\t\t\t\t\t\tblev--;\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tQCC_PR_Lex();\t//ignore it.\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tif (type->type == ev_string && QCC_PR_CheckName(\"_\"))\n\t\t\t\t\t{\n\t\t\t\t\t\tQCC_PR_Expect(\"(\");\n\t\t\t\t\t\tQCC_PR_Lex();\n\t\t\t\t\t\tQCC_PR_Expect(\")\");\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tQCC_PR_CheckToken(\"#\");\n\t\t\t\t\t\tdo\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tQCC_PR_Lex();\n\t\t\t\t\t\t} while (*pr_token && strcmp(pr_token, \",\") && strcmp(pr_token, \";\"));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tQCC_FreeDef(def);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tparentclass = pr_classtype;\n\t\t\tpr_classtype = defclass?defclass:pr_classtype;\n\t\t\tif (flag_assumevar)\n\t\t\t\tisconst = isconstant || (!isvar && !pr_scope && (type->type == ev_function || type->type == ev_field));\n\t\t\telse\n\t\t\t\tisconst = (isconstant || (!isvar && !pr_scope));\n\t\t\tif (isconst != def->constant)\n\t\t\t{\t//we should only be optimising consts if its initialised, so it shouldn't have been read as 0 at any point so far.\n\t\t\t\tQCC_PR_ParseWarning(WARN_REDECLARATIONMISMATCH, \"Redeclaration of %s would change to %sconst.\", def->name, isconst?\"\":\"non-\");\n\t\t\t\tQCC_PR_ParsePrintDef(WARN_REDECLARATIONMISMATCH, def);\n//\t\t\t\tdef->constant = isconst;\n\t\t\t}\n\t\t\tif (accumulate || def->accumulate)\n\t\t\t{\n\t\t\t\tunsigned int pif_flags = PIF_ACCUMULATE;\n\t\t\t\tif (!def->initialized)\n\t\t\t\t\tdef->accumulate |= true;\t//first time\n\t\t\t\telse if (dowrap)\n\t\t\t\t\tpif_flags |= PIF_WRAP;\t\t//explicitly wrapping a prior accumulation...\n\t\t\t\telse if (!def->accumulate)\n\t\t\t\t{\n\t\t\t\t\tQCC_PR_ParseWarning(WARN_GMQCC_SPECIFIC, \"%s redeclared to accumulate after initial declaration\", def->name);\n\t\t\t\t\tpif_flags |= PIF_WRAP|PIF_AUTOWRAP;\t//wrap it automatically so its not so obvious\n\t\t\t\t}\n\t\t\t\tdef->accumulate |= true;\n\t\t\t\tdef->initialized = true;\n\t\t\t\tdef->symboldata[0].function = QCC_PR_ParseImmediateStatements (def, def->type, pif_flags) - functions;\n\t\t\t}\n\t\t\telse\n\t\t\t\tQCC_PR_ParseInitializerDef(def, (dowrap?PIF_WRAP:0)|(def->weak?PIF_STRONGER:0));\n\t\t\tif (doweak)\n\t\t\t\tdef->weak = true;\n\t\t\telse\n\t\t\t\tdef->weak = false;\n\t\t\tpr_classtype = parentclass;\n\n\t\t\tif (!def->nofold && def->constant && def->initialized && def->symbolheader == def && def->ofs == 0 && !def->arraysize)\n\t\t\t{\n\t\t\t\tQCC_def_t *base = NULL;\n\t\t\t\tconst QCC_eval_t *val = (const QCC_eval_t *)&def->symboldata[0];\n\t\t\t\tif (type->type == ev_float)\n\t\t\t\t\tbase = QCC_MakeFloatConst(val->_float).sym;\n\t\t\t\telse if (type->type == ev_integer)\n\t\t\t\t\tbase = QCC_MakeIntConst(val->_int).sym;\n\t\t\t\telse if (type->type == ev_vector)\n\t\t\t\t\tbase = QCC_MakeVectorConst(val->vector[0], val->vector[1], val->vector[2]).sym;\n\t\t\t\tif (base && !base->symbolheader->nofold)\n\t\t\t\t{\n\t\t\t\t\tdef->ofs = base->ofs;\n\t\t\t\t\tdef->symbolheader = base->symbolheader;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (1)//isconstant || isvar)\n\t\t\t{\n\t\t\t\tint c = isconstant;\n\t\t\t\tif (type->type == ev_function)\n\t\t\t\t\tc |= !isvar && !pr_scope;\n\t\t\t\tif (type->type == ev_field)\n\t\t\t\t{\n\t\t\t\t\tif (c)\n\t\t\t\t\t\tc = 2;\n\t\t\t\t\telse if (isvar || (pr_scope && !isstatic))\n\t\t\t\t\t\tc = 0;\n\t\t\t\t\telse\n\t\t\t\t\t\tc = 1;\n\t\t\t\t}\n\t\t\t\tif (def->constant != c)\n\t\t\t\t{\n\t\t\t\t\tQCC_PR_ParseWarning(WARN_REDECLARATIONMISMATCH, \"Redeclaration of %s changes const.\", def->name);\n\t\t\t\t\tQCC_PR_ParsePrintDef(WARN_REDECLARATIONMISMATCH, def);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (accumulate)\n\t\t\t{\n\t\t\t\tif (def->initialized)\n\t\t\t\t\tQCC_PR_ParseWarning(WARN_GMQCC_SPECIFIC, \"%s redeclared to accumulate after initial declaration\", def->name);\n\t\t\t\tdef->accumulate |= true;\n\t\t\t}\n\n\t\t\tif (dostrip)\n\t\t\t\tdef->referenced = true;\n\t\t\telse if (type->type == ev_field)\n\t\t\t{\n\t\t\t\t//fields are const by default, even when not initialised (as they are initialised behind the scenes)\n\t\t\t\tif (!def->initialized && def->constant)\n\t\t\t\t{\n\t\t\t\t\tunsigned int i;\n\t\t\t\t\tdef->initialized = true;\n\t\t\t\t\t//if the field already has a value, don't allocate new field space for it as that would confuse things.\n\t\t\t\t\t//otherwise allocate new space.\n\t\t\t\t\tif (def->symboldata[0]._int)\n\t\t\t\t\t{\n\t\t\t\t\t\tfor (i = 0; i < type->size*(arraysize?arraysize:1); i++)\t//make arrays of fields work.\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (def->symboldata[i]._int != i + def->symboldata[0]._int)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tQCC_PR_ParseWarning(0, \"Inconsistant field def:\");\n\t\t\t\t\t\t\t\tQCC_PR_ParsePrintDef(0, def);\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tfor (i = 0; i < type->size*(arraysize?arraysize:1); i++)\t//make arrays of fields work.\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (def->symboldata[i]._int)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tQCC_PR_ParseWarning(0, \"Field def already has a value:\");\n\t\t\t\t\t\t\t\tQCC_PR_ParsePrintDef(0, def);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tdef->symboldata[i]._int = pr.size_fields+i;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tpr.size_fields += i;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\td = def;\n\t\tQCC_FreeDef(d);\n\t\twhile (d != def->deftail)\n\t\t{\n\t\t\td = d->next;\n\t\t\td->constant = def->constant;\n\t\t\td->initialized = def->initialized;\n\t\t}\n\t} while (QCC_PR_CheckToken (\",\"));\n\n\tif (type->type == ev_function)\n\t\tQCC_PR_CheckTokenComment (\";\", def?&def->comment:NULL);\n\telse\n\t{\n\t\tif (!QCC_PR_CheckTokenComment (\";\", def?&def->comment:NULL))\n\t\t\tQCC_PR_ParseWarning(WARN_UNDESIRABLECONVENTION, \"Missing semicolon at end of definition\");\n\t}\n}\n\n/*\n============\nPR_CompileFile\n\ncompiles the 0 terminated text, adding defintions to the pr structure\n============\n*/\nvoid QCC_PR_LexWhitespace (pbool inhibitpreprocessor);\npbool\tQCC_PR_CompileFile (char *string, char *filename)\n{\n\tchar *tmp;\n\tjmp_buf oldjb;\n\tif (!pr.memory)\n\t\tQCC_Error (ERR_INTERNAL, \"PR_CompileFile: Didn't clear\");\n\n\tQCC_PR_ClearGrabMacros (true);\t// clear the frame macros\n\n\tcompilingfile = filename;\n\n\ts_unitn = s_filen = tmp = qccHunkAlloc(strlen(filename)+1);\n\tstrcpy(tmp, filename);\n\tif (opt_filenames)\n\t{\n\t\toptres_filenames += strlen(filename);\n\t\ts_filed = 0;\n\t}\n\telse\n\t\ts_filed = QCC_CopyString (filename);\n\n\tpr_file_p = string;\n\tpr_assumetermtype = NULL;\n\tpr_ignoredeprecation = false;\n\n\tpr_source_line = 0;\n\n\tmemcpy(&oldjb, &pr_parse_abort, sizeof(oldjb));\n\n\tif( setjmp( pr_parse_abort ) ) {\n\t\tpr_error_count++;\n\t\t// dont count it as error\n\t} else {\n\t\t//clock up the first line\n\t\tQCC_PR_NewLine (false);\n\n\t\tQCC_PR_Lex ();\t// read first token\n\t}\n\n\tif (preprocessonly)\n\t{\n\t\tpbool white = false;\n\t\tstatic int line = 1;//pr_source_line;\n\t\tstatic const char *fname = NULL;\n\t\tstatic QCC_string_t fnamed = 0;\n\t\twhile(pr_token_type != tt_eof)\n\t\t{\n//\t\t\twhite = (qcc_iswhite(*pr_file_p) || (*pr_file_p == '/' && (pr_file_p[1] == '/' || pr_file_p[1] == '*')));\n//\t\t\tQCC_PR_LexWhitespace (false);\n\n\t\t\tif (fnamed != s_filed)\n\t\t\t{\n\t\t\t\tline = pr_token_line;\n\t\t\t\tfname = s_filen;\n\t\t\t\tfnamed = s_filed;\n\t\t\t\texterns->Printf(\"\\n#pragma file(%s)\\n\",fname);\n\t\t\t\texterns->Printf(\"#pragma line(%i)\\n\",line);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t//if there's whitespace next, make sure we represent that\n\t\t\t\twhile(line < pr_token_line)\n\t\t\t\t{\t//keep line numbers correct by splurging multiple newlines.\n\t\t\t\t\texterns->Printf(\"\\n\");\n\t\t\t\t\twhite = false;\n\t\t\t\t\tline++;\n\t\t\t\t}\n\t\t\t\tif (white)\n\t\t\t\t\texterns->Printf(\" \");\n\t\t\t}\n\n\t\t\tif (pr_token_type == tt_immediate && pr_immediate_type == type_string)\n\t\t\t{\n\t\t\t\tconst char *s = pr_token;\n\t\t\t\texterns->Printf(\"\\\"\");\n\t\t\t\twhile (*s)\n\t\t\t\t{\n\t\t\t\t\tswitch(*s)\n\t\t\t\t\t{\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\texterns->Printf(\"%c\", 0);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase '\\\\':\n\t\t\t\t\t\texterns->Printf(\"\\\\\\\\\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase '\\\"':\n\t\t\t\t\t\texterns->Printf(\"\\\\\\\"\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase '\\r':\n\t\t\t\t\t\texterns->Printf(\"\\\\r\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase '\\n':\n\t\t\t\t\t\texterns->Printf(\"\\\\n\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase '\\t':\n\t\t\t\t\t\texterns->Printf(\"\\\\t\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\texterns->Printf(\"%c\", *s);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\ts++;\n\t\t\t\t}\n\t\t\t\texterns->Printf(\"\\\"\");\n\t\t\t}\n\t\t\telse\n\t\t\t\texterns->Printf(\"%s\", pr_token);\n\t\t\twhite = (qcc_iswhite(*pr_file_p) || (*pr_file_p == '/' && (pr_file_p[1] == '/' || pr_file_p[1] == '*')));\n\t\t\tQCC_PR_Lex();\n\t\t}\n\t}\n\telse while (pr_token_type != tt_eof)\n\t{\n\t\tif (setjmp(pr_parse_abort))\n\t\t{\n\t\t\tnum_continues = 0;\n\t\t\tnum_breaks = 0;\n\t\t\tnum_cases = 0;\n\t\t\tif (++pr_error_count > MAX_ERRORS)\n\t\t\t{\n\t\t\t\tmemcpy(&pr_parse_abort, &oldjb, sizeof(oldjb));\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tQCC_PR_SkipToSemicolon ();\n\t\t\tif (pr_token_type == tt_eof)\n\t\t\t{\n\t\t\t\tmemcpy(&pr_parse_abort, &oldjb, sizeof(oldjb));\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\tpr_scope = NULL;\t// outside all functions\n\n\t\tQCC_PR_ParseDefs (NULL, true);\n\n#if 0//def _DEBUG\n\t\tif (!pr_error_count)\n\t\t{\n\t\t\tQCC_def_t *d;\n\t\t\tunsigned int i;\n\t\t\tfor (i = 0; i < MAX_PARMS; i++)\n\t\t\t{\n\t\t\t\td = &def_parms[i];\n\t\t\t\tif (d->refcount)\n\t\t\t\t{\n\t\t\t\t\tQCC_sref_t sr;\n\t\t\t\t\tsr.sym = d;\n\t\t\t\t\tsr.cast = d->type;\n\t\t\t\t\tsr.ofs = 0;\n\t\t\t\t\tQCC_PR_ParseWarning(WARN_DEBUGGING, \"INTERNAL: %i references still held on %s (%s)\", d->refcount, d->name, QCC_VarAtOffset(sr));\n\t\t\t\t\td->refcount = 0;\n\t\t\t\t}\n\t\t\t}\n\t\t\tfor (d = pr.def_head.next; d; d = d->next)\n\t\t\t{\n\t\t\t\tif (d->refcount)\n\t\t\t\t{\n\t\t\t\t\tQCC_sref_t sr;\n\t\t\t\t\tsr.sym = d;\n\t\t\t\t\tsr.cast = d->type;\n\t\t\t\t\tsr.ofs = 0;\n\t\t\t\t\tQCC_PR_ParseWarning(WARN_DEBUGGING, \"INTERNAL: %i references still held on %s (%s)\", d->refcount, d->name, QCC_VarAtOffset(sr));\n\t\t\t\t\td->refcount = 0;\n\t\t\t\t}\n\t\t\t}\n\t\t\tfor (i = 0; i < tempsused; i++)\n\t\t\t{\n\t\t\t\td = tempsinfo[i].def;\n\t\t\t\tif (d->refcount)\n\t\t\t\t{\n\t\t\t\t\tQCC_sref_t sr;\n\t\t\t\t\tsr.sym = d;\n\t\t\t\t\tsr.cast = d->type;\n\t\t\t\t\tsr.ofs = 0;\n\t\t\t\t\tQCC_PR_ParseWarning(WARN_DEBUGGING, \"INTERNAL: %i references still held on %s (%s)\", d->refcount, d->name, QCC_VarAtOffset(sr));\n\t\t\t\t\td->refcount = 0;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n#endif\n\t}\n\tmemcpy(&pr_parse_abort, &oldjb, sizeof(oldjb));\n\n\treturn (pr_error_count == 0);\n}\n\npbool QCC_Include(const char *filename, pbool newunit)\n{\n\tchar *newfile;\n\tchar fname[512];\n\tchar *opr_file_p;\n\tconst char *os_unitn;\n\tconst char *os_filen;\n\tQCC_string_t os_filed;\n\tint opr_source_line;\n\tchar *ocompilingfile;\n\tstruct qcc_includechunk_s *oldcurrentchunk;\n\n\tocompilingfile = compilingfile;\n\tos_unitn = s_unitn;\n\tos_filen = s_filen;\n\tos_filed = s_filed;\n\topr_source_line = pr_source_line;\n\topr_file_p = pr_file_p;\n\toldcurrentchunk = currentchunk;\n\n\tstrcpy(fname, filename);\n\tQCC_LoadFile(fname, (void*)&newfile);\n\n\tif (!newunit)\n\t{\n\t\tQCC_PR_IncludeChunkEx(newfile, false, NULL, NULL);\n\t\ts_filen = strcpy(qccHunkAlloc(strlen(filename)+1), filename);\n\t\treturn true;\n\t}\n\n\tcurrentchunk = NULL;\n\tpr_file_p = newfile;\n\tQCC_PR_CompileFile(newfile, fname);\n\tcurrentchunk = oldcurrentchunk;\n\n\tcompilingfile = ocompilingfile;\n\ts_unitn = os_unitn;\n\ts_filen = os_filen;\n\ts_filed = os_filed;\n\tpr_source_line = opr_source_line;\n\tpr_file_p = opr_file_p;\n\n\tif (pr_error_count > MAX_ERRORS)\n\t\tlongjmp (pr_parse_abort, 1);\n\n//\tQCC_PR_IncludeChunk(newfile, false, fname);\n\n\treturn true;\n}\nvoid QCC_Cleanup(void)\n{\n\tfree(pr_breaks);\n\tfree(pr_continues);\n\tfree(pr_cases);\n\tfree(pr_casesref);\n\tfree(pr_casesref2);\n\tmax_breaks = max_continues = max_cases = num_continues = num_breaks = num_cases = 0;\n\tpr_breaks = NULL;\n\tpr_continues = NULL;\n\tpr_cases = NULL;\n\tpr_casesref = NULL;\n\tpr_casesref2 = NULL;\n\t*compilingrootfile = 0;\n\n#ifdef _DEBUG\n\tOpAssignsTo_Debug();\n#endif\n}\n#endif\n"
  },
  {
    "path": "engine/qclib/qcc_pr_lex.c",
    "content": "#if !defined(MINIMAL) && !defined(OMIT_QCC)\n\n#include \"qcc.h\"\n#include \"time.h\"\n\n#define MEMBERFIELDNAME \"__m%s\"\n\n#define STRCMP(s1,s2) (((*s1)!=(*s2)) || strcmp(s1,s2))\t//saves about 2-6 out of 120 - expansion of idea from fastqcc\n\nvoid QCC_PR_PreProcessor_Define(pbool append);\npbool QCC_PR_UndefineName(const char *name);\nconst char *QCC_PR_CheckCompConstString(const char *def);\nCompilerConstant_t *QCC_PR_CheckCompConstDefined(const char *def);\nint QCC_PR_CheckCompConst(void);\nvoid QCC_FreeDef(QCC_def_t *def);\nvoid QCC_PR_LexComment(char **comment);\n\nextern pbool\tdestfile_explicit;\nextern char\t\tdestfile[1024];\n//static const QCC_sref_t nullsref = {0};\n\n#define MAXINCLUDEDIRS 8\nchar\tqccincludedir[MAXINCLUDEDIRS][256];\t//the -src path, for #includes\nstruct qccincludeonced_s\n{\n\tstruct qccincludeonced_s *next;\n\tchar name[1];\n} *qccincludeonced;\t//the -src path, for #includes\n\nchar *compilingfile;\n\nint\t\t\tpr_source_line;\n\nchar\t\t*pr_file_p;\nchar\t\t*pr_line_start;\t\t// start of current source line\n\nint\t\t\tpr_bracelevel;\n\nchar\t\t*pr_token_precomment;\nchar\t\tpr_token[8192];\ntoken_type_t\tpr_token_type;\nint\t\t\t\tpr_token_line;\nint\t\t\t\tpr_token_line_last;\nQCC_type_t\t\t*pr_immediate_type;\nQCC_eval_t\t\tpr_immediate;\n\nchar\tpr_immediate_string[8192];\nsize_t\tpr_immediate_strlen;\n\nint\t\tpr_error_count;\nint\t\tpr_warning_count;\n\nextern pbool expandedemptymacro;\n\n//really these should not be in here\nextern unsigned int locals_end, locals_start;\nextern QCC_type_t *pr_classtype;\nQCC_function_t *QCC_PR_ParseImmediateStatements (QCC_def_t *def, QCC_type_t *type, pbool dowrap);\nQCC_type_t *QCC_PR_FieldType (QCC_type_t *pointsto);\n\nstatic void Q_strlcpy(char *dest, const char *src, int sizeofdest)\n{\n\tif (sizeofdest)\n\t{\n\t\tint slen = strlen(src);\n\t\tslen = min((sizeofdest-1), slen);\n\t\tmemcpy(dest, src, slen);\n\t\tdest[slen] = 0;\n\t}\n}\n\n\n\nchar\t*pr_punctuation[] =\n// longer symbols must be before a shorter partial match\n{\"&&\", \"||\", \"<=>\", \"<=\", \">=\",\"==\", \"!=\", \"/=\", \"*=\", \"+=\", \"-=\", \"(+)\", \"(-)\", \"|=\", \"&~=\", \"&=\", \"++\", \"--\", \"->\", \"^=\", \"::\", \";\", \",\", \"!\", \"*^\", \"*\", \"/\", \"(\", \")\", \"-\", \"+\", \"=\", \"[\", \"]\", \"{\", \"}\", \"...\", \"..\", \".\", \"><\", \"<<=\", \"<<\", \"<\", \">>=\", \">>\", \">\" , \"?\", \"#\" , \"@\", \"&\" , \"|\", \"%\", \"^^\", \"^\", \"~\", \":\", NULL};\n\nchar *pr_punctuationremap[] =\t//a nice bit of evilness.\n//(+) -> |=\n//-> -> .\n//(-) -> &~=\n{\"&&\", \"||\", \"<=>\", \"<=\", \">=\",\"==\", \"!=\", \"/=\", \"*=\", \"+=\", \"-=\", \"|=\",  \"&~=\", \"|=\", \"&~=\", \"&=\", \"++\", \"--\", \".\",  \"^=\", \"::\", \";\", \",\", \"!\", \"*^\", \"*\", \"/\", \"(\", \")\", \"-\", \"+\", \"=\", \"[\", \"]\", \"{\", \"}\", \"...\", \"..\", \".\", \"><\", \"<<=\", \"<<\", \"<\", \">>=\", \">>\", \">\" , \"?\", \"#\" , \"@\", \"&\" , \"|\", \"%\", \"^^\", \"^\", \"~\", \":\", NULL};\n\n// simple types.  function types are dynamically allocated\nQCC_type_t\t*type_void;\t\t\t\t//void\nQCC_type_t\t*type_string;\t\t\t//string\nQCC_type_t\t*type_float;\t\t\t//float\nQCC_type_t\t*type_double;\t\t\t//double\nQCC_type_t\t*type_vector;\t\t\t//vector\nQCC_type_t\t*type_entity;\t\t\t//entity\nQCC_type_t\t*type_field;\t\t\t//.void\nQCC_type_t\t*type_function;\t\t\t//void()\nQCC_type_t\t*type_floatfunction;\t//float()\nQCC_type_t\t*type_pointer;\t\t\t//??? * - careful with this one\nQCC_type_t\t*type_sint8;\t\t\t\t//char\t- these small types are technically ev_bitfield, but typedefed and aligned.\nQCC_type_t\t*type_uint8;\t\t\t//unsigned char\nQCC_type_t\t*type_sint16;\t\t\t//short\nQCC_type_t\t*type_uint16;\t\t\t//unsigned short\nQCC_type_t\t*type_integer;\t\t\t//int32\nQCC_type_t\t*type_uint;\t\t\t\t//uint32\nQCC_type_t\t*type_int64;\t\t\t//int64\nQCC_type_t\t*type_uint64;\t\t\t//uint64\nQCC_type_t\t*type_variant;\t\t\t//__variant\nQCC_type_t\t*type_invalid;\t\t\t//technically void, but shouldn't really ever be used.\nQCC_type_t\t*type_floatpointer;\t\t//float *\nQCC_type_t\t*type_intpointer;\t\t//int *\nQCC_type_t\t*type_bint;\t\t\t\t//int (0 or 1)\nQCC_type_t\t*type_bfloat;\t\t\t//float (0.0 or 1.0, and never -0.0)\n\nQCC_type_t\t*type_floatfield;// = {ev_field/*, &def_field*/, NULL, &type_float};\n\nQCC_def_t\tdef_ret, def_parms[MAX_PARMS];\n\n//QCC_def_t\t*def_for_type[9] = {&def_void, &def_string, &def_float, &def_vector, &def_entity, &def_field, &def_function, &def_pointer, &def_integer};\n\nvoid QCC_PR_LexWhitespace (pbool inhibitpreprocessor);\n\n\nQCC_type_t *QCC_PR_ParseEnum(pbool flags);\n\n//for compiler constants and file includes.\n\nqcc_includechunk_t *currentchunk;\nvoid QCC_PR_CloseProcessor(void)\n{\n\tint i;\n\tfor (i = 0; i < MAXINCLUDEDIRS; i++)\n\t\t*qccincludedir[i] = 0;\n\tcurrentchunk = NULL;\n\tqccincludeonced = NULL;\n}\nvoid QCC_PR_AddIncludePath(const char *newinc)\n{\n\tint i;\n\n\tif (!*newinc)\n\t{\n\t\tnewinc = \".\";\n//\t\tQCC_PR_ParseWarning(WARN_STRINGTOOLONG, \"Invalid include path.\");\n//\t\treturn;\n\t}\n\n\tfor (i = 0; i < MAXINCLUDEDIRS; i++)\n\t{\n\t\tif (!*qccincludedir[i])\n\t\t{\n\t\t\tpbool trunc;\n\t\t\tconst char *e = newinc + strlen(newinc)-1;\n\t\t\ttrunc = !QC_strlcpy(qccincludedir[i], newinc, sizeof(qccincludedir));\n\t\t\tif (*e != '/' && *e != '\\\\')\n\t\t\t\ttrunc |= !QC_strlcat(qccincludedir[i], \"/\", sizeof(qccincludedir));\n\t\t\tif (trunc)\n\t\t\t{\n\t\t\t\tQCC_PR_ParseWarning(WARN_STRINGTOOLONG, \"Include path too long.\");\n\t\t\t\t*qccincludedir[i] = 0;\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t\tif (!strcmp(qccincludedir[i], newinc))\n\t\t\tbreak;\n\t}\n\tif (i == MAXINCLUDEDIRS)\n\t{\n\t\tQCC_PR_ParseWarning(WARN_STRINGTOOLONG, \"Too many include dirs. Ignoring and hoping the stars align.\");\n\t}\n}\nvoid QCC_PR_IncludeChunkEx (char *data, pbool duplicate, char *filename, CompilerConstant_t *cnst)\n{\n\tqcc_includechunk_t *chunk = qccHunkAlloc(sizeof(qcc_includechunk_t));\n\tchunk->prev = currentchunk;\n\tcurrentchunk = chunk;\n\n\tchunk->currentdatapoint = pr_file_p;\n\tchunk->currentfilename = s_filen;\n\tchunk->currentlinenumber = pr_source_line;\n\tchunk->cnst = cnst;\n\tif( cnst )\n\t{\n#if 0\n\t\ts_filen = cnst->fromfile;\n\t\tpr_source_line = cnst->fromline;\n#else\n\t\tint b = strlen(s_filen)+1+8+strlen(cnst->name);\n\t\tchar *p;\n\t\tif (b > 128)\n\t\t\tb = 128;\n\t\ts_filen = p = qccHunkAlloc(b);\n\t\tQC_snprintfz(p, b, \"%s:%i:%s\", chunk->currentfilename, chunk->currentlinenumber, cnst->name);\n\t\tpr_source_line = 1;\n#endif\n\t\tcnst->inside++;\n\t}\n\telse\n\t\tpr_source_line = 1;\n\n\tif (duplicate)\n\t{\n\t\tpr_file_p = qccHunkAlloc(strlen(data)+1);\n\t\tstrcpy(pr_file_p, data);\n\t}\n\telse\n\t\tpr_file_p = data;\n\tchunk->datastart = pr_file_p;\n}\nvoid QCC_PR_IncludeChunk (char *data, pbool duplicate, char *filename)\n{\n\tQCC_PR_IncludeChunkEx(data, duplicate, filename, NULL);\n}\n\npbool QCC_PR_UnInclude(void)\n{\n\tif (!currentchunk)\n\t\treturn false;\n\n\tif( currentchunk->cnst )\n\t\tcurrentchunk->cnst->inside--;\n\n\tpr_file_p = currentchunk->currentdatapoint;\n\tpr_source_line = currentchunk->currentlinenumber;\n\ts_filen = currentchunk->currentfilename;\n\n\tcurrentchunk = currentchunk->prev;\n\n\treturn true;\n}\n\n//expresses a relative path relative to an existing FILEname. any directories in base must be / terminated properly.\nvoid QCC_JoinPaths(char *fullname, size_t fullnamesize, const char *newfile, const char *base)\n{\n\tchar *end;\n\n\tif (*newfile == '/' || *newfile == '\\\\')\n\t{\t//its an absolute path...\n\t\tQC_strlcpy(fullname, newfile, fullnamesize);\n\t\treturn;\n\t}\n\tQC_strlcpy(fullname, base, fullnamesize);\n\tend = fullname+strlen(fullname);\n\twhile (end > fullname)\n\t{\n\t\tend--;\n\t\tif (*end == '/' || *end == '\\\\')\n\t\t{\n\t\t\tend++;\n\t\t\tbreak;\n\t\t}\n\t}\n\tQC_strlcpy(end, newfile, fullnamesize - (end-fullname));\n\n\t//FIXME: do we want to convert /segment/../ into just / ?\n\t//fteqw might insist on it for its filesystem sandboxing.\n\t//but it breaks symlink weirdness (itself a possible security hole).\n\t//should probably be a separate function.\n}\n\nextern char qccmsourcedir[];\n//also meant to include it.\nvoid QCC_FindBestInclude(char *newfile, char *currentfile, pbool includetype)\n{\n\tstruct qccincludeonced_s *onced;\n\tint includepath = 0;\n\tchar fullname[1024];\n\n\tif (!*newfile)\n\t\treturn;\n\n\twhile(1)\n\t{\n\t\tif (includepath)\n\t\t{\n\t\t\tif (includepath > MAXINCLUDEDIRS || !*qccincludedir[includepath-1])\n\t\t\t\tQCC_Error(ERR_COULDNTOPENFILE, \"Couldn't open file %s\", newfile);\n\n\t\t\tcurrentfile = qccincludedir[includepath-1];\n\t\t}\n\n\t\tQCC_JoinPaths(fullname, sizeof(fullname), newfile, currentfile);\n\n\t\t{\n\t\t\textern progfuncs_t *qccprogfuncs;\n\t\t\tif (qccprogfuncs->funcs.parms->FileSize(fullname) == -1)\n\t\t\t{\n\t\t\t\tincludepath++;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\t\tbreak;\n\t}\n\n\tfor(onced = qccincludeonced; onced; onced = onced->next)\n\t{\n\t\tif (!strcmp(onced->name, fullname))\n\t\t\treturn;\n\t}\n\n\tif (includetype && verbose >= VERBOSE_PROGRESS)\n\t{\n\t\tif (includetype == 2)\n\t\t{\n\t\t\tif (autoprototype)\n\t\t\t\texterns->Printf(\"prototyping %s\\n\", fullname);\n\t\t\telse\n\t\t\t\texterns->Printf(\"compiling %s\\n\", fullname);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (autoprototype)\n\t\t\t\texterns->Printf(\"prototyping include %s\\n\", fullname);\n\t\t\telse\n\t\t\t\texterns->Printf(\"including %s\\n\", fullname);\n\t\t}\n\t}\n\tQCC_Include(fullname, includetype);\n}\n\npbool defaultnoref;\npbool defaultnosave;\npbool defaultstatic;\nint ForcedCRC;\nfloat qcc_framerate;\n\nint QCC_PR_LexInteger (void);\nvoid\tQCC_AddFile (char *filename);\nvoid QCC_PR_LexString (void);\npbool QCC_PR_SimpleGetToken (void);\npbool QCC_PR_SimpleGetString(void);\n\n#define PPI_VALUE 0\n#define PPI_NOT 1\n#define PPI_DEFINED 2\n#define PPI_COMPARISON 3\n#define PPI_LOGICAL 4\n#define PPI_TOPLEVEL 5\nstatic int ParsePrecompilerIf(int level)\n{\n\tCompilerConstant_t *c;\n\tint eval = 0;\n//\tpbool notted = false;\n\n\t//single term end-of-chain\n\tif (level == PPI_VALUE)\n\t{\n\t\t/*skip whitespace*/\n\t\twhile (*pr_file_p && qcc_iswhite(*pr_file_p) && *pr_file_p != '\\n')\n\t\t{\n\t\t\tpr_file_p++;\n\t\t}\n\n\t\tif (*pr_file_p == '(')\n\t\t{\t//try brackets\n\t\t\tpr_file_p++;\n\t\t\teval = ParsePrecompilerIf(PPI_TOPLEVEL);\n\t\t\twhile (*pr_file_p == ' ' || *pr_file_p == '\\t')\n\t\t\t\tpr_file_p++;\n\t\t\tif (*pr_file_p != ')')\n\t\t\t\tQCC_PR_ParseError(ERR_EXPECTED, \"unclosed bracket condition\\n\");\n\t\t\tpr_file_p++;\n\t\t}\n\t\telse if (*pr_file_p == '!')\n\t\t{\t//try brackets\n\t\t\tpr_file_p++;\n\t\t\teval = !ParsePrecompilerIf(PPI_NOT);\n\t\t}\n\t\telse\n\t\t{\t//simple token...\n\t\t\tif (!strncmp(pr_file_p, \"defined\", 7))\n\t\t\t{\n\t\t\t\tpbool brackets;\n\t\t\t\tpr_file_p+=7;\n\t\t\t\twhile (*pr_file_p == ' ' || *pr_file_p == '\\t')\n\t\t\t\t\tpr_file_p++;\n\t\t\t\tbrackets = *pr_file_p == '(';\n\t\t\t\tpr_file_p += brackets;\n\n\t\t\t\tQCC_PR_SimpleGetToken();\n\t\t\t\teval = !!QCC_PR_CheckCompConstDefined(pr_token);\n\n\t\t\t\tif (brackets)\n\t\t\t\t{\n\t\t\t\t\twhile (*pr_file_p == ' ' || *pr_file_p == '\\t')\n\t\t\t\t\t\tpr_file_p++;\n\t\t\t\t\tif (*pr_file_p != ')')\n\t\t\t\t\t\tQCC_PR_ParseError(ERR_EXPECTED, \"unclosed defined condition\\n\");\n\t\t\t\t\tpr_file_p++;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (!QCC_PR_SimpleGetToken())\n\t\t\t\t\tQCC_PR_ParseError(ERR_EXPECTED, \"unexpected end-of-line\\n\");\n\t\t\t\tc = QCC_PR_CheckCompConstDefined(pr_token);\n\t\t\t\tif (!c)\n\t\t\t\t\teval = atoi(pr_token);\n\t\t\t\telse\n\t\t\t\t\teval = atoi(c->value);\n\t\t\t}\n\t\t}\n\t\treturn eval;\n\t}\n\n\teval = ParsePrecompilerIf(level-1);\n\n\twhile (*pr_file_p && qcc_iswhite(*pr_file_p) && *pr_file_p != '\\n')\n\t{\n\t\tpr_file_p++;\n\t}\n\n\tswitch(level)\n\t{\n\tcase PPI_LOGICAL:\n\t\tif (!strncmp(pr_file_p, \"||\", 2))\n\t\t{\n\t\t\tpr_file_p+=2;\n\t\t\teval = ParsePrecompilerIf(level)||eval;\n\t\t}\n\t\telse if (!strncmp(pr_file_p, \"&&\", 2))\n\t\t{\n\t\t\tpr_file_p+=2;\n\t\t\teval = ParsePrecompilerIf(level)&&eval;\n\t\t}\n\t\tbreak;\n\tcase PPI_COMPARISON:\n\t\tif (!strncmp(pr_file_p, \"<=\", 2))\n\t\t{\n\t\t\tpr_file_p+=2;\n\t\t\teval = eval <= ParsePrecompilerIf(level);\n\t\t}\n\t\telse if (!strncmp(pr_file_p, \">=\", 2))\n\t\t{\n\t\t\tpr_file_p += 2;\n\t\t\teval = eval >= ParsePrecompilerIf(level);\n\t\t}\n\t\telse if (!strncmp(pr_file_p, \"<\", 1))\n\t\t{\n\t\t\tpr_file_p += 1;\n\t\t\teval = eval < ParsePrecompilerIf(level);\n\t\t}\n\t\telse if (!strncmp(pr_file_p, \">\", 1))\n\t\t{\n\t\t\tpr_file_p += 1;\n\t\t\teval = eval > ParsePrecompilerIf(level);\n\t\t}\n\t\telse if (!strncmp(pr_file_p, \"!=\", 2))\n\t\t{\n\t\t\tpr_file_p += 2;\n\t\t\teval = eval != ParsePrecompilerIf(level);\n\t\t}\n\t\telse if (!strncmp(pr_file_p, \"==\", 2))\n\t\t{\n\t\t\tpr_file_p += 2;\n\t\t\teval = eval == ParsePrecompilerIf(level);\n\t\t}\n\t\tbreak;\n\t}\n\treturn eval;\n}\n\nstruct deflist_s\n{\n\tchar *buffer;\n\tsize_t length;\n\tsize_t buffersize;\n};\nstatic void QCC_PR_GetDefinesListEnumerate(void *vctx, void *data)\n{\n\tstruct deflist_s *ctx = vctx;\n\tCompilerConstant_t *def = data;\n\tchar term[8192];\n\tsize_t termsize;\n\tpbool success = true;\n\n\tQC_snprintfz(term, sizeof(term), \"\\n%s\", def->name);\n\tif (def->numparams >= 0)\n\t{\n\t\tint i;\n\t\tsuccess &= QC_strlcat(term, \"(\", sizeof(term));\n\t\tfor (i = 0; i < def->numparams; i++)\n\t\t{\n\t\t\tif (i)\n\t\t\t\tsuccess &= QC_strlcat(term, \",\", sizeof(term));\n\t\t\tsuccess &= QC_strlcat(term, def->params[i], sizeof(term));\n\t\t}\n\t\tsuccess &= QC_strlcat(term, \")\", sizeof(term));\n\t}\n\tif (def->value && *def->value)\n\t{\n\t\tchar *o, *i;\n\t\tsuccess &= QC_strlcat(term, \"=\", sizeof(term));\n\n\t\t//annoying logic to skip whitespace... hopefully it won't fuck stuff up too much.\n\t\tfor (o = term+strlen(term), i = def->value; o < term + sizeof(term)-1 && *i; )\n\t\t{\n\t\t\tif (*i == ' ' || *i == '\\t' || *i == '\\n' || *i == '\\r')\n\t\t\t\ti++;\n\t\t\telse\n\t\t\t\t*o++ = *i++;\n\t\t}\n\t\t*o = 0;\n\t}\n\tif (!success)\t//this define was too long. don't show truncated stuff.\n\t\treturn;\n\n\ttermsize = strlen(term);\n\tif (ctx->length + termsize+1 > ctx->buffersize)\n\t{\n\t\tctx->buffersize = (ctx->length + termsize+1)*2;\n\t\tctx->buffer = realloc(ctx->buffer, ctx->buffersize);\n\t}\n\tmemcpy(ctx->buffer+ctx->length, term, termsize);\n\tctx->length += termsize;\n\tctx->buffer[ctx->length] = 0;\n}\nchar *QCC_PR_GetDefinesList(void)\n{\n\tstruct deflist_s ctx = {NULL};\n\tHash_Enumerate(&compconstantstable, QCC_PR_GetDefinesListEnumerate, &ctx);\n\treturn ctx.buffer;\n}\n\n//returns true if it was white/comments only. false if there was actual text that was skipped.\nstatic void QCC_PR_SkipToEndOfLine(pbool errorifnonwhite)\n{\n\tpbool handleccomments = true;\n\twhile(*pr_file_p != '\\n' && *pr_file_p != '\\0')\t//read on until the end of the line\n\t{\n\t\tif (*pr_file_p == '/' && pr_file_p[1] == '*' && handleccomments)\n\t\t{\n\t\t\tpr_file_p += 2;\n\t\t\twhile(*pr_file_p)\n\t\t\t{\n\t\t\t\tif (*pr_file_p == '*' && pr_file_p[1] == '/')\n\t\t\t\t{\n\t\t\t\t\tpr_file_p+=2;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tif (*pr_file_p == '\\n')\n\t\t\t\t\tpr_source_line++;\n\t\t\t\tpr_file_p++;\n\t\t\t}\n\t\t}\n\t\telse if (*pr_file_p == '/' && pr_file_p[1] == '/' && handleccomments)\n\t\t{\n\t\t\thandleccomments = false;\n\t\t\tpr_file_p += 2;\n\t\t\t/*while(*pr_file_p)\n\t\t\t{\n\t\t\t\tif (*pr_file_p == '\\n')\n\t\t\t\t\tbreak;\n\t\t\t\tpr_file_p++;\n\t\t\t}*/\n\t\t}\n\t\telse if (*pr_file_p == '\\\\' && pr_file_p[1] == '\\r' && pr_file_p[2] == '\\n')\n\t\t{\t/*windows endings*/\n\t\t\tpr_file_p+=3;\n\t\t\tpr_source_line++;\n\t\t}\n\t\telse if (*pr_file_p == '\\\"' && handleccomments)\n\t\t{\n\t\t\tif (errorifnonwhite)\n\t\t\t{\n\t\t\t\terrorifnonwhite = false;\n\t\t\t\tQCC_PR_ParseWarning (ERR_UNKNOWNPUCTUATION, \"unexpected tokens at end of line\");\n\t\t\t}\n\t\t\tpr_file_p++;\n\t\t\twhile (*pr_file_p)\n\t\t\t{\n\t\t\t\tif (*pr_file_p == '\\n')\n\t\t\t\t\tbreak;\t//this text is junk/ignored, so ignore the obvious error here.\n\t\t\t\telse if (*pr_file_p == '\\\"')\n\t\t\t\t{\n\t\t\t\t\tpr_file_p++;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\telse if (*pr_file_p == '\\\\' && pr_file_p[1] == '\\\"')\n\t\t\t\t\tpr_file_p+=2;\t//don't trip on \"\\\"/*\"\n\t\t\t\telse if (*pr_file_p == '\\\\' && pr_file_p[1] == '\\\\')\n\t\t\t\t\tpr_file_p+=2;\t//don't trip on \"\\\\\"//\"foo\n\t\t\t\telse\n\t\t\t\t\tpr_file_p++;\n\t\t\t\t//any other \\ should be part of the actual string, which we don't care about here\n\t\t\t}\n\t\t}\n\t\telse if (*pr_file_p == '\\\\' && pr_file_p[1] == '\\n')\n\t\t{\t/*linux endings*/\n\t\t\tpr_file_p+=2;\n\n\t\t\tpr_source_line++;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (errorifnonwhite && handleccomments && !qcc_iswhite(*pr_file_p))\n\t\t\t{\n\t\t\t\terrorifnonwhite = false;\n\t\t\t\tQCC_PR_ParseWarning(ERR_UNKNOWNPUCTUATION, \"unexpected tokens at end of line\");\n\t\t\t}\n\n\t\t\tpr_file_p++;\n\t\t}\n\t}\n}\n\n//if hadtrue, then we allow elses, otherwise we skip them.\nstatic pbool QCC_PR_FalsePreProcessorIf(pbool hadtrue, int originalline)\n{\n\tint eval;\n\tint level = 1;\n\twhile (1)\n\t{\n\t\twhile(*pr_file_p && (*pr_file_p==' ' || *pr_file_p == '\\t'))\n\t\t\tpr_file_p++;\n\n\t\tif (!*pr_file_p)\n\t\t{\n\t\t\tpr_source_line = originalline;\n\t\t\tQCC_PR_ParseError (ERR_NOENDIF, \"#if with no endif\");\n\t\t}\n\n\t\tif (*pr_file_p == '#')\n\t\t{\n\t\t\tpr_file_p++;\n\t\t\twhile(*pr_file_p==' ' || *pr_file_p == '\\t')\n\t\t\t\tpr_file_p++;\n\t\t\tif (!strncmp(pr_file_p, \"endif\", 5))\n\t\t\t\tlevel--;\n\t\t\tif (!strncmp(pr_file_p, \"if\", 2))\n\t\t\t\tlevel++;\n\t\t\tif (!hadtrue && !strncmp(pr_file_p, \"else\", 4) && level == 1)\n\t\t\t{\n\t\t\t\tpr_file_p+=4;\n\t\t\t\tQCC_PR_SkipToEndOfLine(true);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tif (!hadtrue && !strncmp(pr_file_p, \"elif\", 4) && level == 1)\n\t\t\t{\n//\t\t\t\tQCC_PR_ParseError(ERR_UNKNOWNPUCTUATION, \"#elif not supported\\n\");\n\n\t\t\t\tpr_file_p += 4;\n\t\t\t\tif (!strncmp(pr_file_p, \"def\", 3))\n\t\t\t\t{\n\t\t\t\t\teval = 1;\n\t\t\t\t\tpr_file_p += 3;\n\t\t\t\t}\n\t\t\t\telse if (!strncmp(pr_file_p, \"ndef\", 4))\n\t\t\t\t{\n\t\t\t\t\teval = 0;\n\t\t\t\t\tpr_file_p += 4;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\teval = 2;\n\t\t\t\tif (*pr_file_p != ' ' && *pr_file_p != '\\t')\n\t\t\t\t\tQCC_PR_ParseError(ERR_UNKNOWNPUCTUATION, \"malformed #elif\\n\");\n\t\t\t\tif (eval == 2)\n\t\t\t\t\teval = ParsePrecompilerIf(PPI_TOPLEVEL);\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tQCC_PR_SimpleGetToken ();\n\t\t\t\t\tif (eval)\n\t\t\t\t\t\teval = !!QCC_PR_CheckCompConstDefined(pr_token);\n\t\t\t\t\telse\n\t\t\t\t\t\teval = !QCC_PR_CheckCompConstDefined(pr_token);\n\t\t\t\t}\n\t\t\t\tif (eval)\n\t\t\t\t{\n\t\t\t\t\tQCC_PR_SkipToEndOfLine(true);\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tQCC_PR_SkipToEndOfLine(false);\n\n\t\tif (level <= 0)\n\t\t\treturn false;\n\t\tpr_file_p++;\t//next line\n\t\tpr_source_line++;\n\t}\n}\n\n#if 0\nstatic void QCC_PR_PackagerMessage(void *userctx, char *message, ...)\n{\n\tva_list\t\targptr;\n\tchar\t\tstring[1024];\n\n\tva_start (argptr,message);\n\tQC_vsnprintf (string,sizeof(string)-1,message,argptr);\n\tva_end (argptr);\n\n\texterns->Printf (\"%s\", string);\n}\n#endif\n\n/*\n==============\nQCC_PR_Precompiler\n==============\n\nRuns precompiler stage\n*/\nstatic pbool QCC_PR_Precompiler(void)\n{\n\tchar msg[1024];\n\tint ifmode;\n\tint a;\n\tstatic int ifs = 0;\n\tpbool eval = false;\n\n\tif (*pr_file_p == '#')\n\t{\n\t\tchar *directive;\n\t\tfor (directive = pr_file_p+1; *directive; directive++)\t//so #    define works\n\t\t{\n\t\t\tif (*directive == '\\r' || *directive == '\\n')\n\t\t\t\tQCC_PR_ParseError(ERR_UNKNOWNPUCTUATION, \"Hanging # with no directive\\n\");\n\t\t\tif (*directive > ' ')\n\t\t\t\tbreak;\n\t\t}\n\t\tif (!strncmp(directive, \"define\", 6))\n\t\t{\n\t\t\tpr_file_p = directive;\n\t\t\tQCC_PR_PreProcessor_Define(false);\n\t\t\tQCC_PR_SkipToEndOfLine(true);\n\t\t}\n\t\telse if (!strncmp(directive, \"append\", 6))\n\t\t{\n\t\t\tpr_file_p = directive;\n\t\t\tQCC_PR_PreProcessor_Define(true);\n\t\t\tQCC_PR_SkipToEndOfLine(true);\n\t\t}\n\t\telse if (!strncmp(directive, \"undef\", 5))\n\t\t{\n\t\t\tpr_file_p = directive+5;\n\t\t\twhile(qcc_iswhitesameline(*pr_file_p))\n\t\t\t\tpr_file_p++;\n\n\t\t\tQCC_PR_SimpleGetToken ();\n\t\t\tQCC_PR_UndefineName(pr_token);\n\n\t//\t\tQCC_PR_ConditionCompilation();\n\t\t\tQCC_PR_SkipToEndOfLine(true);\n\t\t}\n\t\telse if (!strncmp(directive, \"if\", 2))\n\t\t{\n\t\t\tint originalline = pr_source_line;\n \t\t\tpr_file_p = directive+2;\n\t\t\tif (!strncmp(pr_file_p, \"def\", 3))\n\t\t\t{\n\t\t\t\tifmode = 0;\n\t\t\t\tpr_file_p+=3;\n\t\t\t}\n\t\t\telse if (!strncmp(pr_file_p, \"ndef\", 4))\n\t\t\t{\n\t\t\t\tifmode = 1;\n\t\t\t\tpr_file_p+=4;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tifmode = 2;\n\t\t\t\tpr_file_p+=0;\n\t\t\t\t//QCC_PR_ParseError(\"bad \\\"#if\\\" type\");\n\t\t\t}\n\n\t\t\tif (!qcc_iswhite(*pr_file_p))\n\t\t\t{\n\t\t\t\tpr_file_p = directive;\n\t\t\t\tQCC_PR_SimpleGetToken ();\n\t\t\t\tQCC_PR_ParseWarning(WARN_BADPRAGMA, \"Unknown pragma \\'%s\\'\", qcc_token);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (ifmode == 2)\n\t\t\t\t{\n\t\t\t\t\teval = ParsePrecompilerIf(PPI_TOPLEVEL);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tQCC_PR_SimpleGetToken ();\n\n\t\t//\t\t\tif (!STRCMP(pr_token, \"COOP_MODE\"))\n\t\t//\t\t\t\teval = false;\n\t\t\t\t\tif (QCC_PR_CheckCompConstDefined(pr_token))\n\t\t\t\t\t\teval = true;\n\n\t\t\t\t\tif (ifmode == 1)\n\t\t\t\t\t\teval = eval?false:true;\n\t\t\t\t}\n\n\t\t\t\tQCC_PR_SkipToEndOfLine(true);\n\n\t\t\t\tif (eval)\n\t\t\t\t\tifs+=1;\n\t\t\t\telse\n\t\t\t\t\tifs += QCC_PR_FalsePreProcessorIf(false, originalline);\n\t\t\t}\n\t\t}\n\t\telse if (!strncmp(directive, \"else\", 4) || !strncmp(directive, \"elif\", 4))\n\t\t{\n\t\t\tint originalline = pr_source_line;\n\n\t\t\tif (!ifs)\n\t\t\t\tQCC_PR_ParseError(ERR_UNKNOWNPUCTUATION, \"#else outside of #if\\n\");\n\n\t\t\tifs -= 1;\n\n\t\t\tpr_file_p = directive+4;\n\t\t\tif (!strncmp(directive, \"elif\", 4))\n\t\t\t\tQCC_PR_SkipToEndOfLine(false);\n\t\t\telse\n\t\t\t\tQCC_PR_SkipToEndOfLine(true);\n\n\t\t\tifs += QCC_PR_FalsePreProcessorIf(true, originalline);\n\t\t}\n\t\telse if (!strncmp(directive, \"endif\", 5))\n\t\t{\n\t\t\tpr_file_p = directive+5;\n\t\t\tQCC_PR_SkipToEndOfLine(true);\n\t\t\tif (ifs <= 0)\n\t\t\t\tQCC_PR_ParseError(ERR_NOPRECOMPILERIF, \"unmatched #endif\");\n\t\t\telse\n\t\t\t\tifs-=1;\n\t\t}\n\t\telse if (!strncmp(directive, \"eof\", 3))\n\t\t{\n\t\t\tpr_file_p = NULL;\n\t\t\treturn true;\n\t\t}\n\t\telse if (!strncmp(directive, \"error\", 5))\n\t\t{\n\t\t\tpr_file_p = directive+5;\n\t\t\tfor (a = 0; a < sizeof(msg)-1 && pr_file_p[a] != '\\t' && pr_file_p[a] != '\\n' && pr_file_p[a] != '\\0'; a++)\n\t\t\t\tmsg[a] = pr_file_p[a];\n\t\t\tmsg[a] = '\\0';\n\n\t\t\tQCC_PR_SkipToEndOfLine(false);\n\n\t\t\tQCC_PR_ParseError(ERR_HASHERROR, \"#Error: %s\", msg);\n\t\t}\n\t\telse if (!strncmp(directive, \"warning\", 7))\n\t\t{\n\t\t\tpr_file_p = directive+7;\n\t\t\tfor (a = 0; a < 1023 && pr_file_p[a] != '\\r' && pr_file_p[a] != '\\n' && pr_file_p[a] != '\\0'; a++)\n\t\t\t\tmsg[a] = pr_file_p[a];\n\t\t\tmsg[a] = '\\0';\n\n\t\t\tQCC_PR_SkipToEndOfLine(false);\n\n\t\t\tQCC_PR_ParseWarning(WARN_PRECOMPILERMESSAGE, \"#warning: %s\", msg);\n\t\t}\n\t\telse if (!strncmp(directive, \"message\", 7))\n\t\t{\n\t\t\tpr_file_p = directive+7;\n\t\t\tfor (a = 0; a < sizeof(msg)-1 && pr_file_p[a] != '\\r' && pr_file_p[a] != '\\n' && pr_file_p[a] != '\\0'; a++)\n\t\t\t\tmsg[a] = pr_file_p[a];\n\t\t\tmsg[a] = '\\0';\n\n\t\t\tif (flag_msvcstyle)\n\t\t\t\texterns->Printf (\"%s(%i) : #message: %s\\n\", s_filen, pr_source_line, msg);\n\t\t\telse\n\t\t\t\texterns->Printf (\"%s:%i: #message: %s\\n\", s_filen, pr_source_line, msg);\n\t\t\tQCC_PR_SkipToEndOfLine(false);\n\t\t}\n\t\telse if (!strncmp(directive, \"copyright\", 9))\n\t\t{\n\t\t\tpr_file_p = directive+9;\n\t\t\tfor (a = 0; a < sizeof(msg)-1 && qcc_iswhite(pr_file_p[a]) && pr_file_p[a] != '\\0'; a++)\n\t\t\t\tmsg[a] = pr_file_p[a];\n\t\t\tmsg[a] = '\\0';\n\n\t\t\tQCC_PR_SkipToEndOfLine(false);\n\n\t\t\tif (strlen(msg) >= sizeof(QCC_copyright))\n\t\t\t\tQCC_PR_ParseWarning(WARN_STRINGTOOLONG, \"Copyright message is too long\\n\");\n\t\t\tQC_strlcpy(QCC_copyright, msg, sizeof(QCC_copyright)-1);\n\t\t}\n\t\telse if (!strncmp(directive, \"package\", 7))\n\t\t{\n\t\t\tpr_file_p=directive+7;\n\n\t\t\tQCC_PR_SkipToEndOfLine(true);\n\n#if 0\n\t\t\tif (!autoprototype)\n\t\t\t{\n\t\t\t\tstruct pkgctx_s *ctx = Packager_Create(QCC_PR_PackagerMessage, NULL);\n\t\t\t\tPackager_ParseText(ctx, pr_file_p);\n\t\t\t\tPackager_WriteDataset(ctx, NULL);\n\t\t\t\tPackager_Destroy(ctx);\n\t\t\t}\n#endif\n\n\t\t\tpr_file_p += strlen(pr_file_p);\n\t\t}\n\t\telse if (!strncmp(directive, \"pack\", 4))\n\t\t{\n\t\t\tifmode = 0;\n\t\t\tpr_file_p=directive+4;\n\t\t\tif (!strncmp(pr_file_p, \"id\", 2))\n\t\t\t\tpr_file_p+=3;\n\t\t\telse\n\t\t\t{\n\t\t\t\tifmode = QCC_PR_LexInteger();\n\t\t\t\tif (ifmode == 0)\n\t\t\t\t\tifmode = 1;\n\t\t\t\tpr_file_p++;\n\t\t\t}\n\t\t\tfor (a = 0; a < sizeof(msg)-1 && qcc_iswhite(pr_file_p[a]) && pr_file_p[a] != '\\0'; a++)\n\t\t\t\tmsg[a] = pr_file_p[a];\n\t\t\tmsg[a] = '\\0';\n\n\t\t\tQCC_PR_SkipToEndOfLine(true);\n\n\t\t\tif (ifmode == 0)\n\t\t\t\tQCC_packid = atoi(msg);\n\t\t\telse if (ifmode <= 5)\n\t\t\t\tstrcpy(QCC_Packname[ifmode-1], msg);\n\t\t\telse\n\t\t\t\tQCC_PR_ParseError(ERR_TOOMANYPACKFILES, \"No more than 5 packs are allowed\");\n\t\t}\n\t\telse if (!strncmp(directive, \"forcecrc\", 8))\n\t\t{\n\t\t\tpr_file_p=directive+8;\n\n\t\t\tForcedCRC = QCC_PR_LexInteger();\n\n\t\t\tQCC_PR_SkipToEndOfLine(true);\n\t\t}\n\t\telse if (!strncmp(directive, \"merge\", 5))\n\t\t{\n\t\t\tpr_file_p=directive+5;\n\n\t\t\twhile(qcc_iswhitesameline(*pr_file_p))\n\t\t\t\tpr_file_p++;\n\n\t\t\tQCC_PR_SimpleGetString();\n\t\t\texterns->Printf(\"Merging from %s\\n\", pr_token);\n\t\t\tQCC_ImportProgs(pr_token);\n\t\t\tif (!*destfile && !destfile_explicit)\n\t\t\t{\n\t\t\t\tQCC_JoinPaths(destfile, sizeof(destfile), pr_token, compilingfile);\n\t\t\t\texterns->Printf(\"Outputfile: %s\\n\", destfile);\n\t\t\t}\n\n\t\t\tQCC_PR_SkipToEndOfLine(true);\n\t\t}\n\t\telse if (!strncmp(directive, \"includelist\", 11))\n\t\t{\n\t\t\tint defines=0;\n\t\t\tpr_file_p=directive+11;\n\n\t\t\tQCC_PR_SkipToEndOfLine(true);\n\n\t\t\twhile(1)\n\t\t\t{\n\t\t\t\tQCC_PR_LexWhitespace(false);\n\t\t\t\tif (!flag_hashonly && QCC_PR_CheckCompConst())\n\t\t\t\t{\n\t\t\t\t\tdefines++;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (!QCC_PR_SimpleGetToken())\n\t\t\t\t{\n\t\t\t\t\tif (!*pr_file_p)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (defines>0)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tdefines--;\n\t\t\t\t\t\t\tQCC_PR_UnInclude();\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tQCC_Error(ERR_EOF, \"eof in includelist\");\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tpr_file_p++;\n\t\t\t\t\t\tpr_source_line++;\n\t\t\t\t\t}\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (!strcmp(pr_token, \"#endlist\"))\n\t\t\t\t{\n\t\t\t\t\tQCC_PR_SkipToEndOfLine(true);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tif (pr_error_count)\t//if we had an error, don't keep including more stuff that'll hide the actual error.\n\t\t\t\t{\n\t\t\t\t\tpr_file_p = \"\";\n\t\t\t\t\tQCC_PR_ParseError(0, NULL);\n\t\t\t\t}\n\n\t\t\t\tQCC_FindBestInclude(pr_token, compilingfile, true);\n\n\t\t\t\tif (*pr_file_p == '\\r')\n\t\t\t\t\tpr_file_p++;\n\n//\t\t\t\tQCC_PR_SkipToEndOfLine(true);\n\t\t\t}\n\t\t}\n\t\telse if (!strncmp(directive, \"include\", 7))\n\t\t{\n\t\t\tchar sm;\n\n\t\t\tpr_file_p=directive+7;\n\n\t\t\twhile(qcc_iswhitesameline(*pr_file_p))\n\t\t\t\tpr_file_p++;\n\n\t\t\tmsg[0] = '\\0';\n\t\t\tif (*pr_file_p == '\\\"')\n\t\t\t\tsm = '\\\"';\n\t\t\telse if (*pr_file_p == '<')\n\t\t\t\tsm = '>';\n\t\t\telse\n\t\t\t{\n\t\t\t\tQCC_PR_ParseError(0, \"Not a string literal (on a #include)\");\n\t\t\t\tsm = 0;\n\t\t\t}\n\t\t\tpr_file_p++;\n\t\t\ta=0;\n\t\t\twhile(*pr_file_p != sm)\n\t\t\t{\n\t\t\t\tif (*pr_file_p == '\\n')\n\t\t\t\t{\n\t\t\t\t\tQCC_PR_ParseError(0, \"#include continued over line boundry\\n\");\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tmsg[a++] = *pr_file_p;\n\t\t\t\tpr_file_p++;\n\t\t\t}\n\t\t\tmsg[a] = 0;\n\t\t\tpr_file_p++;\n\n\t\t\tif (pr_error_count)\t//if we had an error, don't keep including more stuff that'll hide the actual error.\n\t\t\t{\n\t\t\t\tpr_file_p = \"\";\n\t\t\t\tQCC_PR_ParseError(0, NULL);\n\t\t\t}\n\t\t\tQCC_PR_SkipToEndOfLine(true);\n\n\t\t\tQCC_FindBestInclude(msg, compilingfile, false);\n\t\t\tQCC_PR_Precompiler();\t//preprocessor directives normally require a leading newline to be considered a directive. we won't have one in the new file so lets just do it explicitly.\n\t\t}\n\t\telse if (!strncmp(directive, \"datafile\", 8))\n\t\t{\n\t\t\tpr_file_p=directive+8;\n\n\t\t\twhile(qcc_iswhitesameline(*pr_file_p))\n\t\t\t\tpr_file_p++;\n\n\t\t\tQCC_PR_SimpleGetString();\n\t\t\texterns->Printf(\"Including datafile: %s\\n\", pr_token);\n\t\t\tQCC_AddFile(pr_token);\n\n\t\t\tpr_file_p++;\n\n\t\t\tfor (a = 0; a < sizeof(msg)-1 && pr_file_p[a] != '\\n' && pr_file_p[a] != '\\0'; a++)\n\t\t\t\tmsg[a] = pr_file_p[a];\n\n\t\t\tmsg[a-1] = '\\0';\n\n\t\t\twhile(*pr_file_p != '\\n' && *pr_file_p != '\\0')\t//read on until the end of the line\n\t\t\t{\n\t\t\t\tpr_file_p++;\n\t\t\t}\n\t\t}\n\t\telse if (!strncmp(directive, \"output\", 6))\n\t\t{\n\t\t\tpr_file_p=directive+6;\n\n\t\t\twhile(qcc_iswhitesameline(*pr_file_p))\n\t\t\t\tpr_file_p++;\n\n\t\t\tQCC_PR_SimpleGetString();\n\t\t\tif (!destfile_explicit)\n\t\t\t{\n\t\t\t\tQCC_JoinPaths(destfile, sizeof(destfile), pr_token, compilingfile);\n\t\t\t\texterns->Printf(\"Outputfile: %s\\n\", pr_token);\n\t\t\t}\n\n\t\t\tQCC_PR_SkipToEndOfLine(true);\n\t\t}\n\t\telse if (!strncmp(directive, \"pragma\", 6))\n\t\t{\n\t\t\tpr_file_p=directive+6;\n\t\t\twhile(qcc_iswhitesameline(*pr_file_p))\n\t\t\t\tpr_file_p++;\n\n\t\t\tqcc_token[0] = '\\0';\n\t\t\tfor(a = 0; *pr_file_p != '\\n' && *pr_file_p != '\\0'; pr_file_p++)\t//read on until the end of the line\n\t\t\t{\n\t\t\t\tif ((*pr_file_p == ' ' || *pr_file_p == '\\t'|| *pr_file_p == '(') && !*qcc_token)\n\t\t\t\t{\n\t\t\t\t\tmsg[a] = '\\0';\n\t\t\t\t\tstrcpy(qcc_token, msg);\n\t\t\t\t\ta=0;\n\t\t\t\t\tif (*pr_file_p != '(')\n\t\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tmsg[a++] = *pr_file_p;\n\t\t\t}\n\n\t\t\tmsg[a] = '\\0';\n\t\t\t{\n\t\t\t\tchar *end;\n\t\t\t\tfor (end = msg + a-1; end>=msg && qcc_iswhite(*end); end--)\n\t\t\t\t\t*end = '\\0';\n\t\t\t}\n\n\t\t\tif (!*qcc_token)\n\t\t\t{\n\t\t\t\tstrcpy(qcc_token, msg);\n\t\t\t\tmsg[0] = '\\0';\n\t\t\t}\n\n\t\t\t{\n\t\t\t\tchar *end;\n\t\t\t\tfor (end = msg + a-1; end>=msg && qcc_iswhite(*end); end--)\n\t\t\t\t\t*end = '\\0';\n\t\t\t}\n\n\t\t\tif (!QC_strcasecmp(qcc_token, \"DONT_COMPILE_THIS_FILE\"))\n\t\t\t{\n\t\t\t\tQCC_PR_LexWhitespace(false);\n\t\t\t\twhile (*pr_file_p)\n\t\t\t\t{\n\t\t\t\t\tif (!qcc_iswhite(*pr_file_p))\n\t\t\t\t\t\tpr_file_p++;\n\t\t\t\t\tQCC_PR_LexWhitespace(false);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (!QC_strcasecmp(qcc_token, \"COPYRIGHT\"))\n\t\t\t{\n\t\t\t\tchar *e = strrchr(msg+1, '\\\"');\n\t\t\t\tif (*msg == '\\\"' && e && e != msg)\n\t\t\t\t{\t//FIXME: handle \\ns\n\t\t\t\t\tmemmove(msg, msg+1, e-(msg+1));\n\t\t\t\t\tmsg[e-(msg+1)] = 0;\n\t\t\t\t}\n\t\t\t\tif (!QC_strlcpy(QCC_copyright, msg, sizeof(QCC_copyright)))\n\t\t\t\t\tQCC_PR_ParseWarning(WARN_STRINGTOOLONG, \"Copyright message is too long\\n\");\n\t\t\t}\n\t\t\telse if (!QC_strcasecmp(qcc_token, \"compress\"))\n\t\t\t{\n\t\t\t\textern pbool compressoutput;\n\t\t\t\tcompressoutput = atoi(msg);\n\t\t\t}\n\t\t\telse if (!QC_strcasecmp(qcc_token, \"forcecrc\"))\n\t\t\t{\n\t\t\t\tForcedCRC = atoi(msg);\n\t\t\t}\n\t\t\telse if (!QC_strcasecmp(qcc_token, \"framerate\"))\n\t\t\t{\n\t\t\t\tqcc_framerate = atof(msg);\n\t\t\t\tif (qcc_framerate < 0)\n\t\t\t\t\tqcc_framerate = 0;\n\t\t\t}\n\t\t\telse if (!QC_strcasecmp(qcc_token, \"once\"))\n\t\t\t{\n\t\t\t\tstruct qccincludeonced_s *onced = qccHunkAlloc(sizeof(*onced) + strlen(compilingfile));\n\t\t\t\tstrcpy(onced->name, compilingfile);\n\t\t\t\tonced->next = qccincludeonced;\n\t\t\t\tqccincludeonced = onced;\n\t\t\t}\n\t\t\telse if (!QC_strcasecmp(qcc_token, \"file\"))\n\t\t\t{\t//#pragma file(foobar.qc)\n\t\t\t\tif (!flag_nopragmafileline)\n\t\t\t\t{\n\t\t\t\t\tchar *e;\n\t\t\t\t\tchar *m = msg;\n\t\t\t\t\tif (*m == '(')\n\t\t\t\t\t{\n\t\t\t\t\t\tm++;\n\t\t\t\t\t\te = strchr(m, ')');\n\t\t\t\t\t\tif (e)\n\t\t\t\t\t\t\t*e = 0;\n\t\t\t\t\t}\n\n\t\t\t\t\ts_filen = e = qccHunkAlloc(strlen(m)+1);\n\t\t\t\t\tstrcpy(e, m);\n\t\t\t\t\tif (opt_filenames)\n\t\t\t\t\t{\n\t\t\t\t\t\toptres_filenames += strlen(m);\n\t\t\t\t\t\ts_filed = 0;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\ts_filed = QCC_CopyString (m);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (!QC_strcasecmp(qcc_token, \"line\"))\n\t\t\t{\t//#pragma line(666)\n\t\t\t\tif (!flag_nopragmafileline)\n\t\t\t\t{\n\t\t\t\t\tchar *m = msg;\n\t\t\t\t\tif (*m == '(')\n\t\t\t\t\t\tm++;\n\t\t\t\t\tpr_source_line = strtoul(m, &m, 0)-1;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (!QC_strcasecmp(qcc_token, \"includedir\"))\n\t\t\t{\n\t\t\t\tchar newinc[1024];\n\t\t\t\tint i;\n\t\t\t\tQCC_COM_Parse(msg);\n\n\t\t\t\tif (*qcc_token)\n\t\t\t\t{\n\t\t\t\t\t i = qcc_token[strlen(qcc_token)-1];\n\t\t\t\t\t if (i != '/' && i != '\\\\')\n\t\t\t\t\t\t QC_strlcat(qcc_token, \"/\", sizeof(qcc_token));\n\t\t\t\t}\n\n\t\t\t\tQCC_JoinPaths(newinc, sizeof(newinc), qcc_token, compilingfile);\n\t\t\t\tQCC_PR_AddIncludePath(newinc);\n\t\t\t}\n\t\t\telse if (!QC_strcasecmp(qcc_token, \"noref\"))\n\t\t\t\tdefaultnoref = !!atoi(msg);\n\t\t\telse if (!QC_strcasecmp(qcc_token, \"nosave\"))\n\t\t\t\tdefaultnosave = !!atoi(msg);\n\t\t\telse if (!QC_strcasecmp(qcc_token, \"defaultstatic\"))\n\t\t\t\tdefaultstatic = !!atoi(msg);\n\t\t\telse if (!QC_strcasecmp(qcc_token, \"autoproto\"))\n\t\t\t{\n\t\t\t\tif (!autoprototyped)\n\t\t\t\t{\n\t\t\t\t\tif (numpr_globals != RESERVED_OFS)\n\t\t\t\t\t\tQCC_PR_ParseWarning(WARN_BADPRAGMA, \"#pragma autoproto must appear before any definitions\");\n\t\t\t\t\telse\n\t\t\t\t\t\tautoprototype = *msg?!!atoi(msg):true;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (!QC_strcasecmp(qcc_token, \"wrasm\"))\n\t\t\t{\n\t\t\t\tpbool on = atoi(msg);\n\n\t\t\t\tif (asmfile && !on)\n\t\t\t\t{\n\t\t\t\t\tfclose(asmfile);\n\t\t\t\t\tasmfile = NULL;\n\t\t\t\t}\n\t\t\t\tif (!asmfile && on)\n\t\t\t\t{\n\t\t\t\t\tif (asmfilebegun)\n\t\t\t\t\t\tasmfile = fopen(\"qc.asm\", \"ab\");\n\t\t\t\t\telse\n\t\t\t\t\t\tasmfile = fopen(\"qc.asm\", \"wb\");\n\t\t\t\t\tif (asmfile)\n\t\t\t\t\t\tasmfilebegun = true;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (!QC_strcasecmp(qcc_token, \"optimise\") || !QC_strcasecmp(qcc_token, \"optimize\"))\t//bloomin' americans.\n\t\t\t{\n\t\t\t\tint o;\n\t\t\t\textern pbool qcc_nopragmaoptimise;\n\t\t\t\tif (pr_scope)\n\t\t\t\t\tQCC_PR_ParseWarning(WARN_BADPRAGMA, \"pragma %s: unable to change optimisation options mid-function\", qcc_token);\n\t\t\t\telse if (*msg >= '0' && *msg <= '3')\n\t\t\t\t{\n\t\t\t\t\tint lev = atoi(msg);\n\t\t\t\t\tpbool state;\n\t\t\t\t\tint once = false;\n\t\t\t\t\tfor (o = 0; optimisations[o].enabled; o++)\n\t\t\t\t\t{\n\t\t\t\t\t\tstate = optimisations[o].optimisationlevel <= lev;\n\t\t\t\t\t\tif (qcc_nopragmaoptimise && *optimisations[o].enabled != state)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (!once++)\n\t\t\t\t\t\t\t\tQCC_PR_ParseWarning(WARN_BADPRAGMA, \"pragma %s %s: overriden by commandline\", qcc_token, msg);\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\t*optimisations[o].enabled = state;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse if (!strnicmp(msg, \"addon\", 5) || !strnicmp(msg, \"mutator\", 7))\n\t\t\t\t{\n\t\t\t\t\tint lev = 2;\n\t\t\t\t\tpbool state = false;\n\t\t\t\t\tfor (o = 0; optimisations[o].enabled; o++)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (optimisations[o].optimisationlevel > lev)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (qcc_nopragmaoptimise && *optimisations[o].enabled != state)\n\t\t\t\t\t\t\t\tQCC_PR_ParseWarning(WARN_IGNORECOMMANDLINE, \"pragma %s %s: disabling %s\", qcc_token, msg, optimisations[o].fullname);\n\t\t\t\t\t\t\t*optimisations[o].enabled = state;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tchar *opt = msg;\n\t\t\t\t\tpbool state = true;\n\t\t\t\t\tif (!strnicmp(msg, \"no-\", 3))\n\t\t\t\t\t{\n\t\t\t\t\t\tstate = false;\n\t\t\t\t\t\topt += 3;\n\t\t\t\t\t}\n\t\t\t\t\tfor (o = 0; optimisations[o].enabled; o++)\n\t\t\t\t\t\tif ((*optimisations[o].abbrev && !stricmp(opt, optimisations[o].abbrev)) || !stricmp(opt, optimisations[o].fullname))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (qcc_nopragmaoptimise && *optimisations[o].enabled != state)\n\t\t\t\t\t\t\t\tQCC_PR_ParseWarning(WARN_BADPRAGMA, \"pragma %s %s: overriden by commandline\", qcc_token, optimisations[o].fullname);\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t*optimisations[o].enabled = state;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\tif (!optimisations[o].enabled)\n\t\t\t\t\t\tQCC_PR_ParseWarning(WARN_BADPRAGMA, \"pragma %s: %s unsupported\", qcc_token, opt);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (!QC_strcasecmp(qcc_token, \"sourcefile\"))\n\t\t\t{\n\t\t\t\tchar *s = msg;\n\t\t\t\twhile ((s = QCC_COM_Parse(s)))\n\t\t\t\t\tQCC_RegisterSourceFile(qcc_token);\n\t\t\t}\n\t\t\telse if (!QC_strcasecmp(qcc_token, \"TARGET\"))\n\t\t\t{\n\t\t\t\tQCC_COM_Parse(msg);\n\t\t\t\tif (!QCC_OPCodeSetTargetName(qcc_token))\n\t\t\t\t\tQCC_PR_ParseWarning(WARN_BADTARGET, \"Unknown target \\'%s\\'. Ignored.\\nValid targets are: ID, HEXEN2, FTE, FTEH2, KK7, DP(patched)\", qcc_token);\n\t\t\t}\n\t\t\telse if (!QC_strcasecmp(qcc_token, \"PROGS_SRC\"))\n\t\t\t{\t//doesn't make sense, but silenced if you are switching between using a certain precompiler app used with CuTF.\n\t\t\t}\n\t\t\telse if (!QC_strcasecmp(qcc_token, \"PROGS_DAT\"))\n\t\t\t{\t//doesn't make sence, but silenced if you are switching between using a certain precompiler app used with CuTF.\n\t\t\t\tchar olddest[1024];\n\t\t\t\tQ_strlcpy(olddest, destfile, sizeof(olddest));\n\t\t\t\tQCC_COM_Parse(msg);\n\n\t\t\t\tif (!destfile_explicit) //if output file is named on the commandline, don't change it mid-compile\n\t\t\t\t\tQCC_JoinPaths(destfile, sizeof(destfile), qcc_token, compilingfile);\n\n\t\t\t\tif (strcmp(destfile, olddest))\n\t\t\t\t\texterns->Printf(\"Outputfile: %s\\n\", destfile);\n\t\t\t}\n\t\t\telse if (!QC_strcasecmp(qcc_token, \"opcode\"))\n\t\t\t{\n\t\t\t\tint st;\n\t\t\t\tchar *s = QCC_COM_Parse(msg);\n\t\t\t\tif (!QC_strcasecmp(qcc_token, \"enable\") || !QC_strcasecmp(qcc_token, \"on\"))\n\t\t\t\t\tst = 1;\n\t\t\t\telse if (!QC_strcasecmp(qcc_token, \"disable\") || !QC_strcasecmp(qcc_token, \"off\"))\n\t\t\t\t\tst = 0;\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tQCC_PR_ParseWarning(WARN_BADPRAGMA, \"opcode state not recognised\");\n\t\t\t\t\tst = -1;\n\t\t\t\t}\n\n\t\t\t\tif (st >= 0)\n\t\t\t\t{\n\t\t\t\t\tint f;\n\t\t\t\t\twhile ((s = QCC_COM_Parse(s)))\n\t\t\t\t\t{\n\t\t\t\t\t\tfor (f = 0; pr_opcodes[f].opname; f++)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (!QC_strcasecmp(pr_opcodes[f].opname, qcc_token))\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tif (f >= OP_NUMREALOPS)\n\t\t\t\t\t\t\t\t\tQCC_PR_ParseWarning(WARN_BADPRAGMA, \"opcode %s is internal\", qcc_token);\t//these will change with later opcodes, do not allow them to be written into the output.\n\t\t\t\t\t\t\t\telse if (st)\n\t\t\t\t\t\t\t\t\tpr_opcodes[f].flags |= OPF_VALID;\n\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t\tpr_opcodes[f].flags &= ~OPF_VALID;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (!pr_opcodes[f].opname)\n\t\t\t\t\t\t\tQCC_PR_ParseWarning(WARN_BADPRAGMA, \"opcode %s not recognised\", qcc_token);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (!QC_strcasecmp(qcc_token, \"keyword\") || !QC_strcasecmp(qcc_token, \"flag\"))\n\t\t\t{\n\t\t\t\tchar *s;\n\t\t\t\tint st;\n\t\t\t\ts = QCC_COM_Parse(msg);\n\t\t\t\tif (!QC_strcasecmp(qcc_token, \"enable\") || !QC_strcasecmp(qcc_token, \"on\"))\n\t\t\t\t\tst = 1;\n\t\t\t\telse if (!QC_strcasecmp(qcc_token, \"disable\") || !QC_strcasecmp(qcc_token, \"off\"))\n\t\t\t\t\tst = 0;\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tQCC_PR_ParseWarning(WARN_BADPRAGMA, \"compiler flag state not recognised\");\n\t\t\t\t\tst = -1;\n\t\t\t\t}\n\t\t\t\tif (st >= 0)\n\t\t\t\t{\n\t\t\t\t\tint f;\n\t\t\t\t\twhile ((s = QCC_COM_Parse(s)))\n\t\t\t\t\t{\n\t\t\t\t\t\tfor (f = 0; compiler_flag[f].enabled; f++)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (!QC_strcasecmp(compiler_flag[f].abbrev, qcc_token))\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tif (compiler_flag[f].flags & FLAG_MIDCOMPILE)\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t*compiler_flag[f].enabled = st;\n\t\t\t\t\t\t\t\t\tif (compiler_flag[f].enabled == &flag_cpriority)\n\t\t\t\t\t\t\t\t\t\tQCC_PrioritiseOpcodes();\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t\tQCC_PR_ParseWarning(WARN_BADPRAGMA, \"Cannot enable/disable keyword/flag via a pragma\");\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (!compiler_flag[f].enabled)\n\t\t\t\t\t\t\tQCC_PR_ParseWarning(WARN_BADPRAGMA, \"keyword/flag %s not recognised\", qcc_token);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (!QC_strcasecmp(qcc_token, \"warning\"))\n\t\t\t{\n\t\t\t\tint st;\n\t\t\t\tchar *s;\n\t\t\t\ts = QCC_COM_Parse(msg);\n\t\t\t\tif (!stricmp(qcc_token, \"enable\") || !stricmp(qcc_token, \"on\"))\n\t\t\t\t\tst = WA_WARN;\n\t\t\t\telse if (!stricmp(qcc_token, \"disable\") || !stricmp(qcc_token, \"off\") || !stricmp(qcc_token, \"ignore\"))\n\t\t\t\t\tst = WA_IGNORE;\n\t\t\t\telse if (!stricmp(qcc_token, \"error\"))\n\t\t\t\t\tst = WA_ERROR;\n\t\t\t\telse if (!stricmp(qcc_token, \"toggle\"))\n\t\t\t\t\tst = 3;\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tQCC_PR_ParseWarning(WARN_BADPRAGMA, \"warning state not recognised\");\n\t\t\t\t\tst = -1;\n\t\t\t\t}\n\t\t\t\tif (st>=0)\n\t\t\t\t{\n\t\t\t\t\tint wn;\n\t\t\t\t\twhile ((s = QCC_COM_Parse(s)))\n\t\t\t\t\t{\n\t\t\t\t\t\twn = QCC_WarningForName(qcc_token);\n\t\t\t\t\t\tif (wn < 0)\n\t\t\t\t\t\t\tQCC_PR_ParseWarning(WARN_BADPRAGMA, \"warning id not recognised\");\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (st == 3)\t//toggle\n\t\t\t\t\t\t\t\tqccwarningaction[wn] = !!qccwarningaction[wn];\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\tqccwarningaction[wn] = st;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tQCC_PR_SkipToEndOfLine(false);\n\t\t\t\tQCC_PR_ParseWarning(WARN_BADPRAGMA, \"Unknown pragma \\'%s\\'\", qcc_token);\n\t\t\t}\n\n\t\t\tQCC_PR_SkipToEndOfLine(true);\n\t\t}\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\n/*\n==============\nPR_NewLine\n\nCall at start of file and when *pr_file_p == '\\n'\n==============\n*/\nvoid QCC_PR_NewLine (pbool incomment)\n{\n\tpr_source_line++;\n\tpr_line_start = pr_file_p;\n\twhile(*pr_file_p==' ' || *pr_file_p == '\\t')\n\t\tpr_file_p++;\n\tif (incomment)\t//no constants if in a comment.\n\t{\n\t}\n\telse if (QCC_PR_Precompiler())\n\t{\n\t}\n\n//\tif (pr_dumpasm)\n//\t\tPR_PrintNextLine ();\n}\n\n/*\n==============\nPR_LexString\n\nParses a quoted string\n==============\n*/\nint QCC_PR_LexEscapedCodepoint(void)\n{\t//for \"\\foo\" or '\\foo' handling.\n\t//caller will have read the \\ already.\n\tint t;\n\tint c = *pr_file_p++;\n\tif (!c)\n\t\tQCC_PR_ParseError (ERR_EOF, \"EOF inside quote\");\n\tif (c == 'n')\n\t\tc = '\\n';\n\telse if (c == 'r')\n\t\tc = '\\r';\n\telse if (c == '#')\t//avoid preqcc expansion in strings.\n\t\tc = '#';\n\telse if (c == '\"')\n\t\tc = '\"';\n\telse if (c == 't')\n\t\tc = '\\t';\t//tab\n\telse if (c == 'a')\n\t\tc = '\\a';\t//bell\n\telse if (c == 'v')\n\t\tc = '\\v';\t//vertical tab\n\telse if (c == 'f')\n\t\tc = '\\f';\t//form feed\n//\telse if (c == 's' || c == 'b')\n//\t\tc = 0;\t//invalid...\n\t//else if (c == 'b')\n\t//\tc = '\\b';\n\telse if (c == '[')\n\t\tc = 0xe010;\t//quake specific\n\telse if (c == ']')\n\t\tc = 0xe011;\t//quake specific\n\telse if (c == '{')\n\t{\n\t\tint d;\n\t\tc = 0;\n\t\tif (*pr_file_p == 'x')\n\t\t{\n\t\t\tpr_file_p++;\n\t\t\twhile ((d = *pr_file_p++) != '}')\n\t\t\t{\n\t\t\t\tif (d >= '0' && d <= '9')\n\t\t\t\t\tc = c * 16 + d - '0';\n\t\t\t\telse if (d >= 'a' && d <= 'f')\n\t\t\t\t\tc = c * 16 + 10+d - 'a';\n\t\t\t\telse if (d >= 'A' && d <= 'F')\n\t\t\t\t\tc = c * 16 + 10+d - 'A';\n\t\t\t\telse\n\t\t\t\t\tQCC_PR_ParseError(ERR_BADCHARACTERCODE, \"Bad character code\");\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\twhile ((d = *pr_file_p++) != '}')\n\t\t\t{\n\t\t\t\tif (d >= '0' && d <= '9')\n\t\t\t\t\tc = c * 10 + d - '0';\n\t\t\t\telse\n\t\t\t\t\tQCC_PR_ParseError(ERR_BADCHARACTERCODE, \"Bad character code\");\n\t\t\t}\n\t\t}\n\t}\n\telse if (c == '.')\n\t\tc = 0xe01c;\n\telse if (c == '<')\n\t\tc = 0xe01d;\t//separator start\n\telse if (c == '-')\n\t\tc = 0xe01e;\t//separator middle\n\telse if (c == '>')\n\t\tc = 0xe01f;\t//separator end\n\telse if (c == '(')\n\t\tc = 0xe080;\t//slider start\n\telse if (c == '=')\n\t\tc = 0xe081;\t//slider middle\n\telse if (c == ')')\n\t\tc = 0xe082;\t//slider end\n\telse if (c == '+')\n\t\tc = 0xe083;\t//slider box\n\telse if (c == 'u' || c == 'U')\n\t{\n\t\t//lower case u specifies exactly 4 nibbles.\n\t\t//upper case U specifies exactly 8 nibbles.\n\t\tunsigned int nibbles = (c=='u')?4:8;\n\t\tc = 0;\n\t\twhile (nibbles --> 0)\n\t\t{\n\t\t\tt = (unsigned char)*pr_file_p;\n\t\t\tif (t >= '0' && t <= '9')\n\t\t\t\tc = (c*16) + (t - '0');\n\t\t\telse if (t >= 'A' && t <= 'F')\n\t\t\t\tc = (c*16) + (t - 'A') + 10;\n\t\t\telse if (t >= 'a' && t <= 'f')\n\t\t\t\tc = (c*16) + (t - 'a') + 10;\n\t\t\telse\n\t\t\t\tbreak;\n\t\t\tpr_file_p++;\n\t\t}\n\t\tif (nibbles)\n\t\t\tQCC_PR_ParseWarning(ERR_BADCHARACTERCODE, \"Unicode character terminated unexpectedly\");\n\t}\n\telse if (c == 'x' || c == 'X')\n\t{\n\t\tint d;\n\t\tc = 0;\n\n\t\td = (unsigned char)*pr_file_p++;\n\t\tif (d >= '0' && d <= '9')\n\t\t\tc += d - '0';\n\t\telse if (d >= 'A' && d <= 'F')\n\t\t\tc += d - 'A' + 10;\n\t\telse if (d >= 'a' && d <= 'f')\n\t\t\tc += d - 'a' + 10;\n\t\telse\n\t\t\tQCC_PR_ParseError(ERR_BADCHARACTERCODE, \"Bad character code\");\n\n\t\tc *= 16;\n\n\t\td = (unsigned char)*pr_file_p++;\n\t\tif (d >= '0' && d <= '9')\n\t\t\tc += d - '0';\n\t\telse if (d >= 'A' && d <= 'F')\n\t\t\tc += d - 'A' + 10;\n\t\telse if (d >= 'a' && d <= 'f')\n\t\t\tc += d - 'a' + 10;\n\t\telse\n\t\t{\t//oops. only one char valid...\n\t\t\tc >>= 4;\n\t\t\tpr_file_p --;\n\t\t}\n\t}\n\telse if (c == '\\\\')\n\t\tc = '\\\\';\n\telse if (c == '?')\t//triglyphs are dumb.\n\t\tc = '?';\n\telse if (c == '\\'')\n\t\tc = '\\'';\n\telse if (c >= '0' && c <= '9')\n\t{\n\t\tif (flag_qcfuncs)\n\t\t\tc = 0xe012 + c - '0';\t//WARNING: This is not an octal code, but uses 'yellow' numbers instead (as on hud).\n\t\telse\t//C compat\n\t\t{\n\t\t\tint d = c, base = 8;\n\t\t\tc = 0;\n\n\t\t\tif (d >= '0' && d < '0'+base)\n\t\t\t\tc = c*base + (d - '0');\n\t\t\telse\n\t\t\t\tQCC_PR_ParseError(ERR_BADCHARACTERCODE, \"Bad character code \\\\%c\", d);\n\n\t\t\td = (unsigned char)*pr_file_p++;\n\t\t\tif (d >= '0' && d < '0'+base)\n\t\t\t\tc = c*base + (d - '0');\n\t\t\telse\n\t\t\t\tpr_file_p --;\t//oops. only one char valid...\n\n\t\t\td = (unsigned char)*pr_file_p++;\n\t\t\tif (d >= '0' && d < '0'+base)\n\t\t\t\tc = c*base + (d - '0');\n\t\t\telse\n\t\t\t\tpr_file_p --;\t//oops. only twoish chars valid...\n\t\t}\n\t}\n\telse if (c == '\\r')\n\t{\t//sigh\n\t\tc = *pr_file_p++;\n\t\tif (c != '\\n')\n\t\t\tQCC_PR_ParseWarning(WARN_HANGINGSLASHR, \"Hanging \\\\\\\\\\r\");\n\t\tpr_source_line++;\n\t}\n\telse if (c == '\\n')\n\t{\t//sigh\n\t\tpr_source_line++;\n\t}\n\telse\n\t\tQCC_PR_ParseError (ERR_INVALIDSTRINGIMMEDIATE, \"Unknown escape char %c\", c);\n\n\treturn c;\n}\nvoid QCC_PR_LexString (void)\n{\n\tunsigned int\tc, t;\n\tint bytecount;\n\tint\t\tlen = 0;\n\tchar\t*end;\n\tconst char *cnst;\n\tint\t\traw;\n\tchar\trawdelim[64];\n\tint\t\tstringtype;\n\t\t\t//0 - quake output, input is 8bit. warnings when its not ascii. \\u will still give utf-8 text, other chars as-is. Expect \\s to screw everything up with utf-8 output.\n\t\t\t//1 - quake output, input is utf-8. due to editors not supporting it, that generally means the input (ab)uses markup.\n\t\t\t//2 - utf-8 output, input is utf-8. welcome to the future! unfortunately not the present.\n\n\tint texttype;\n\tpbool first = true;\n\n\tfor(;;)\n\t{\n\t\traw = 0;\n\t\ttexttype = 0;\n\n\t\tQCC_PR_LexComment(&pr_token_precomment);\n\n\t\tif (flag_qccx && *pr_file_p == ':')\n\t\t{\n\t\t\tpr_file_p++;\n\t\t\tpr_token[len++] = 0;\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (*pr_file_p == 'R' && pr_file_p[1] == '\\\"')\n\t\t{\n\t\t\t/*R\"delim(fo\n\t\t\to)delim\" -> \"fo\\no\"\n\t\t\tthe []\n\t\t\t*/\n\t\t\traw = 1;\n\t\t\tpr_file_p+=2;\n\n\t\t\twhile (1)\n\t\t\t{\n\t\t\t\tc = *pr_file_p++;\n\t\t\t\tif (c == '(')\n\t\t\t\t{\n\t\t\t\t\trawdelim[0] = ')';\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tif (!c || raw >= sizeof(rawdelim)-1)\n\t\t\t\t\tQCC_PR_ParseError (ERR_EOF, \"EOF while parsing raw string delimiter. Expected: R\\\"delim(string)delim\\\"\");\n\t\t\t\trawdelim[raw++] = c;\n\t\t\t}\n\t\t\trawdelim[raw++] = '\\\"';\n\n\t\t\t//these two conditions are generally part of the C preprocessor.\n\t\t\tif (!strncmp(pr_file_p, \"\\\\\\r\\n\", 3))\n\t\t\t{\t//dos format\n\t\t\t\tpr_file_p += 3;\n\t\t\t\tpr_source_line++;\n\t\t\t}\n\t\t\telse if (!strncmp(pr_file_p, \"\\\\\\r\", 2) || !strncmp(pr_file_p, \"\\\\\\n\", 2))\n\t\t\t{\t//mac + unix format\n\t\t\t\tpr_file_p += 2;\n\t\t\t\tpr_source_line++;\n\t\t\t}\n\t\t\tstringtype = 0;\n\t\t}\n\t\telse if (*pr_file_p == 'Q' && pr_file_p[1] == '\\\"')\n\t\t{\t//quake output with utf-8 input (expect to need markup).\n\t\t\tstringtype = 1;\n\t\t\tpr_file_p+=2;\n\t\t}\n\t\telse if ((*pr_file_p == 'U' || *pr_file_p == 'u' || *pr_file_p == 'L') && pr_file_p[1] == '\\\"')\n\t\t{\t//unicode string, char32_t, char16_t, wchar_t respectively. we spit out utf-8 regardless.\n\t\t\tQCC_PR_ParseWarning(WARN_NOTUTF8, \"char32_t/char16_t/wchar_t strings are not supported, treating as u8 prefix (as utf-8)\");\n\t\t\tstringtype = 2;\n\t\t\tpr_file_p+=2;\n\t\t}\n\t\telse if (*pr_file_p == 'u' && pr_file_p[1] == '8' && pr_file_p[2] == '\\\"')\n\t\t{\t//utf-8 string.\n\t\t\tstringtype = 2;\n\t\t\tpr_file_p+=3;\n\t\t}\n\t\telse if (*pr_file_p == '\\\"')\n\t\t{\n\t\t\tstringtype = flag_utf8strings?2:0;\n\t\t\tpr_file_p++;\n\t\t}\n\t\telse if (first)\n\t\t{\n\t\t\tstringtype = 0;\n\t\t\tQCC_PR_ParseError(ERR_BADCHARACTERCODE, \"Expected string constant\");\n\t\t}\n\t\telse\n\t\t\tbreak;\n\t\tfirst = false;\n\n\t\tfor(;;)\n\t\t{\n\t\t\tc = *pr_file_p++;\n\t\t\tif (!c)\n\t\t\t\tQCC_PR_ParseError (ERR_EOF, \"EOF inside quote\");\n\n\t\t\tif (raw)\n\t\t\t{\n\t\t\t\t//raw strings contain very little parsing. just delimiter and initial \\NL support.\n\t\t\t\tif (c == rawdelim[0] && !strncmp(pr_file_p, rawdelim+1, raw-1))\n\t\t\t\t{\n\t\t\t\t\tpr_file_p += raw-1;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\t//make sure line numbers are correct though.\n\t\t\t\tif (c == '\\r' && *pr_file_p != '\\n')\n\t\t\t\t\tpr_source_line++;\t//mac\n\t\t\t\tif (c == '\\n')\t//dos/unix\n\t\t\t\t\tpr_source_line++;\n\t\t\t\tgoto forcebyte;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (c=='\\n')\n\t\t\t\t\tQCC_PR_ParseError (ERR_INVALIDSTRINGIMMEDIATE, \"newline inside quote\");\n\t\t\t\tif (c=='\\\\')\n\t\t\t\t{\t// escape char\n\t\t\t\t\tc = *pr_file_p;\t//peek at it, for our hacks.\n\t\t\t\t\tif (c == 's' || c == 'b')\n\t\t\t\t\t{\n\t\t\t\t\t\tpr_file_p++;\n\t\t\t\t\t\ttexttype ^= 0xe080;\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\telse if (c == '.')\n\t\t\t\t\t{\n\t\t\t\t\t\tpr_file_p++;\n\t\t\t\t\t\tc = 0xe01c | texttype;\n\t\t\t\t\t}\n\t\t\t\t\telse if (c == 'u' || c == 'U')\n\t\t\t\t\t{\t//special hack, \\u is a utf-8 code regardless of output encoding...\n\t\t\t\t\t\tc = QCC_PR_LexEscapedCodepoint();\n\t\t\t\t\t\tgoto forceutf8;\n\t\t\t\t\t}\n\t\t\t\t\telse if (c == 'x' || c == 'X')\n\t\t\t\t\t{\t//special hack, \\xXX in a string is an explicit byte regardless of encoding.\n\t\t\t\t\t\tc = QCC_PR_LexEscapedCodepoint();\n//\t\t\t\t\t\tif (c > 0xff)\n//\t\t\t\t\t\t\tQCC_PR_ParseWarning(ERR_BADCHARACTERCODE, \"Bad unicode character code - codepoint %#x is above 0xFF\", c);\n\t\t\t\t\t\tgoto forcebyte;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tc = QCC_PR_LexEscapedCodepoint();\n//\t\t\t\t\t\tif (stringtype != 2 && c > 0xff)\n//\t\t\t\t\t\t\tQCC_PR_ParseWarning(ERR_BADCHARACTERCODE, \"Bad legacy character code - codepoint %#x is above 0xFF\", c);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse if (c=='\\\"')\n\t\t\t\t{\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\telse if (c == '#' && flag_macroinstrings)\n\t\t\t\t{\n\t\t\t\t\tfor (end = pr_file_p; ; end++)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (qcc_iswhite(*end))\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tif (*end == ')'\n\t\t\t\t\t\t\t||\t*end == '('\n\t\t\t\t\t\t\t||\t*end == '+'\n\t\t\t\t\t\t\t||\t*end == '-'\n\t\t\t\t\t\t\t||\t*end == '*'\n\t\t\t\t\t\t\t||\t*end == '/'\n\t\t\t\t\t\t\t||\t*end == '\\\\'\n\t\t\t\t\t\t\t||\t*end == '|'\n\t\t\t\t\t\t\t||\t*end == '&'\n\t\t\t\t\t\t\t||\t*end == '='\n\t\t\t\t\t\t\t||\t*end == '^'\n\t\t\t\t\t\t\t||\t*end == '~'\n\t\t\t\t\t\t\t||\t*end == '['\n\t\t\t\t\t\t\t||\t*end == ']'\n\t\t\t\t\t\t\t||\t*end == '\\\"'\n\t\t\t\t\t\t\t||\t*end == '{'\n\t\t\t\t\t\t\t||\t*end == '}'\n\t\t\t\t\t\t\t||\t*end == ';'\n\t\t\t\t\t\t\t||\t*end == ':'\n\t\t\t\t\t\t\t||\t*end == ','\n\t\t\t\t\t\t\t||\t*end == '.'\n\t\t\t\t\t\t\t||\t*end == '#')\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\tc = *end;\n\t\t\t\t\t*end = '\\0';\n\t\t\t\t\tcnst = QCC_PR_CheckCompConstString(pr_file_p);\n\t\t\t\t\tif (cnst==pr_file_p)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (*pr_file_p)\n\t\t\t\t\t\t\tQCC_PR_ParseWarning(WARN_MACROINSTRING, \"Unable to expand string macro %s\", pr_file_p);\n\n\t\t\t\t\t}\n\t\t\t\t\telse if (cnst)\n\t\t\t\t\t{\n\t\t\t\t\t\tQCC_PR_ParseWarning(WARN_MACROINSTRING, \"Macro %s expansion in string\", pr_file_p);\n\t\t\t\t\t\t*end = c;\n\n\t\t\t\t\t\tif (len+strlen(cnst) >= sizeof(pr_token)-1)\n\t\t\t\t\t\t\tQCC_Error(ERR_INVALIDSTRINGIMMEDIATE, \"String length exceeds %u\", (unsigned)sizeof(pr_token)-1);\n\n\t\t\t\t\t\tstrcpy(pr_token+len, cnst);\n\t\t\t\t\t\tbytecount=strlen(cnst);\n\t\t\t\t\t\twhile (bytecount > 0 && qcc_iswhitesameline(pr_token[len+bytecount-1]))\n\t\t\t\t\t\t\tbytecount--;\t//make sure there's no trailing whitespace on the end.\n\t\t\t\t\t\tlen+=bytecount;\n\t\t\t\t\t\tpr_file_p = end;\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\t*end = c;\n\t\t\t\t\tc = '#';\t//undo\n\t\t\t\t}\n\t\t\t\telse if (c == 0x7C && flag_acc)\t//reacc support... reacc is strange.\n\t\t\t\t\tc = '\\n';\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tunsigned int cp = c;\n\t\t\t\t\tunsigned int len = stringtype?utf8_check(pr_file_p-1, &cp):0;\n\t\t\t\t\tif (!len)\n\t\t\t\t\t{\t//invalid utf-8 encoding? don't treat it as utf-8!\n\t\t\t\t\t\tif (stringtype)\n\t\t\t\t\t\t\tQCC_PR_ParseWarning(ERR_BADCHARACTERCODE, \"Input string is not valid utf-8\");\n\t\t\t\t\t\tif (c >= ' ')\n\t\t\t\t\t\t\tc |= texttype;\n\t\t\t\t\t\tgoto forcebyte;\n\t\t\t\t\t}\n\t\t\t\t\tif (texttype)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (cp < ' ')\n\t\t\t\t\t\t\tc = cp;\t//don't mask C0 chars like \\t or \\n\n\t\t\t\t\t\telse if (cp < 0x80)\n\t\t\t\t\t\t\tc = cp|0xe080;\t//DO mask other ascii chars (and map to the private-use range at the same time, because this isn't standard unicode any more)\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tQCC_PR_ParseWarning(ERR_BADCHARACTERCODE, \"Unable to mask non-ascii chars. Attempting to mask bytes\");\n\t\t\t\t\t\t\tc |= texttype;\n\t\t\t\t\t\t\tgoto forcequake;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\tc = cp;\n\t\t\t\t\tpr_file_p += len-1;\n\t\t\t\t}\n\t\t\t}\n\n//\t\t\tif (c >= 0x20 && c < 0x80)\n//\t\t\t\tc |= 0xe000;\t//TEST\n\t\t\tif (stringtype == 2)\n\t\t\t{\t//we're outputting a utf-8 string.\nforceutf8:\n\t\t\t\tif (c > 0x10FFFFu)\t//RFC 3629 imposes the same limit as UTF-16 surrogate pairs.\n\t\t\t\t\tQCC_PR_ParseWarning(WARN_NOTUTF8, \"Bad unicode character code - codepoint is above 0x10FFFFu\");\n\n\t\t\t\t//figure out the count of bytes required to encode this char\n\t\t\t\tbytecount = 1;\n\t\t\t\tt = 0x80;\n\t\t\t\twhile (c >= t)\n\t\t\t\t{\n\t\t\t\t\tif (bytecount == 1)\n\t\t\t\t\t\tt <<= 4;\n\t\t\t\t\telse if (bytecount < 7)\n\t\t\t\t\t\tt <<= 5;\n\t\t\t\t\telse\n\t\t\t\t\t\tt <<= 6;\n\t\t\t\t\tbytecount++;\n\t\t\t\t}\n\n\t\t\t\t//error if needed\n\t\t\t\tif (len+bytecount >= sizeof(pr_token))\n\t\t\t\t\tQCC_Error(ERR_INVALIDSTRINGIMMEDIATE, \"String length exceeds %u\", (unsigned)sizeof(pr_token)-1);\n\n\t\t\t\t//output it.\n\t\t\t\tif (bytecount == 1)\n\t\t\t\t\tpr_token[len++] = (unsigned char)(c&0x7f);\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tt = bytecount*6;\n\t\t\t\t\tt = t-6;\n\t\t\t\t\tpr_token[len++] = (unsigned char)((c>>t)&(0x0000007f>>bytecount)) | (0xffffff00 >> bytecount);\n\t\t\t\t\tdo\n\t\t\t\t\t{\n\t\t\t\t\t\tt = t-6;\n\t\t\t\t\t\tpr_token[len++] = (unsigned char)((c>>t)&0x3f) | 0x80;\n\t\t\t\t\t} while(t);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\nforcequake:\n\t\t\t\t//we need to convert it to a quake char...\n\t\t\t\tif (c >= 0xe000 && c <= 0xe0ff)\n\t\t\t\t\tc = c & 0xff;\t//this private use range is commonly used for quake's glyphs.\n\t\t\t\telse if (c >= 0 && c <= 0x7f)\n\t\t\t\t\t; //FIXME: SOME c0 codes are known to quake, but many got reused for random glyphs. however I'm going to treat quake as full ascii.\n\t\t\t\telse if (c >= 0x80)\n\t\t\t\t{\n\t\t\t\t\t//FIXME: spit it out as ^{xxxxxx} instead\n\t\t\t\t\tQCC_PR_ParseWarning(WARN_NOTUTF8, \"Cannot convert codepoint %#x to quake's charset\", c);\n\t\t\t\t}\n\nforcebyte:\n\t\t\t\tif (len >= sizeof(pr_token)-1)\n\t\t\t\t\tQCC_Error(ERR_INVALIDSTRINGIMMEDIATE, \"String length exceeds %u\", (unsigned)sizeof(pr_token)-1);\n\t\t\t\tpr_token[len] = c;\n\t\t\t\tlen++;\n\t\t\t}\n\t\t}\n\t}\n\n\tif (len > sizeof(pr_immediate_string)-1)\n\t\tQCC_Error(ERR_INVALIDSTRINGIMMEDIATE, \"String length exceeds %u\", (unsigned)sizeof(pr_immediate_string)-1);\n\n\tpr_token[len] = 0;\n\tpr_token_type = tt_immediate;\n\tpr_immediate_type = type_string;\n\tmemcpy(pr_immediate_string, pr_token, len+1);\n\tpr_immediate_strlen = len;\n\n\t/*if (qccwarningaction[WARN_NOTUTF8] && stringtype != 1)\n\t{\n\t\tunsigned int\t\tcode;\n\t\tsize_t c;\n\t\tfor (c = 0; c < pr_immediate_strlen; )\n\t\t{\n\t\t\tlen = utf8_check(&pr_token[c], &code);\n\t\t\tif (!len || c+len>pr_immediate_strlen)\n\t\t\t{\n\t\t\t\tQCC_PR_ParseWarning(WARN_NOTUTF8, \"String literal is not valid utf-8\");\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tc += len;\n\t\t}\n\t}*/\n}\n\n/*\n==============\nPR_LexNumber\n==============\n*/\nint QCC_PR_LexInteger (void)\n{\n\tint\t\tc;\n\tint\t\tlen;\n\n\tlen = 0;\n\tc = *pr_file_p;\n\tif (pr_file_p[0] == '0' && (pr_file_p[1] == 'x' || pr_file_p[1] == 'X'))\n\t{\n\t\tpr_token[0] = '0';\n\t\tpr_token[1] = 'x';\n\t\tlen = 2;\n\t\tc = *(pr_file_p+=2);\n\t}\n\tdo\n\t{\n\t\tpr_token[len] = c;\n\t\tlen++;\n\t\tpr_file_p++;\n\t\tc = *pr_file_p;\n\t} while ((c >= '0' && c<= '9') || (c == '.'&&pr_file_p[1]!='.') || (c>='a' && c <= 'f'));\n\tpr_token[len] = 0;\n\treturn atoi (pr_token);\n}\n\n#ifdef _MSC_VER\n#define longlong __int64\n#define LL(x) x##i64\n#else\n#define longlong long long\n#define LL(x) x##ll\n#endif\n\nstatic void QCC_PR_LexNumber (void)\n{\n\tint tokenlen = 0;\n\tlonglong num=0;\n\tint base=0;\n\tint c;\n\tint sign=1;\n\tif (*pr_file_p == '-')\n\t{\n\t\tsign=-1;\n\t\tpr_file_p++;\n\n\t\tpr_token[tokenlen++] = '-';\n\t}\n\tif (pr_file_p[0] == '0' && (pr_file_p[1] == 'x' || pr_file_p[1] == 'X'))\n\t{\n\t\tpr_file_p+=2;\n\t\tbase = 16;\n\n\t\tpr_token[tokenlen++] = '0';\n\t\tpr_token[tokenlen++] = 'x';\n\t}\n\telse if (pr_file_p[0] == '0')\n\t{\n\t\tpr_file_p++;\n\t\tif (*pr_file_p >= '0' && *pr_file_p <= '9')\n\t\t\tQCC_PR_ParseWarning(WARN_OCTAL_IMMEDIATE, \"A leading 0 is interpreted as base-8.\");\n\t\tbase = 8;\n\n\t\tpr_token[tokenlen++] = '0';\n\t}\n\n\tpr_immediate_type = NULL;\n\t//assume base 10 if not stated\n\tif (!base)\n\t\tbase = 10;\n\n\twhile((c = *pr_file_p))\n\t{\n\t\tif (c >= '0' && c <= '9' && c < '0'+base)\n\t\t{\n\t\t\tpr_token[tokenlen++] = c;\n\t\t\tnum*=base;\n\t\t\tnum += c-'0';\n\t\t}\n\t\telse if (c >= 'a' && c <= 'z' && c < 'a'+base-10)\n\t\t{\n\t\t\tpr_token[tokenlen++] = c;\n\t\t\tnum*=base;\n\t\t\tnum += c -'a'+10;\n\t\t}\n\t\telse if (c >= 'A' && c <= 'Z' && c < 'A'+base-10)\n\t\t{\n\t\t\tpr_token[tokenlen++] = c;\n\t\t\tnum*=base;\n\t\t\tnum += c -'A'+10;\n\t\t}\n\t\telse if (c == '.' && pr_file_p[1]!='.')\n\t\t{\n\t\t\tpr_token[tokenlen++] = c;\n\t\t\tpr_file_p++;\n\t\t\tpr_immediate_type = flag_assume_double?type_double:type_float;\n\t\t\twhile(1)\n\t\t\t{\n\t\t\t\tc = *pr_file_p;\n\t\t\t\tif (c >= '0' && c <= '9')\n\t\t\t\t{\n\t\t\t\t\tpr_token[tokenlen++] = c;\n\t\t\t\t}\n\t\t\t\telse if (c == 'f' || c == 'F')\n\t\t\t\t{\n\t\t\t\t\tpr_immediate_type = type_float;\n\t\t\t\t\tpr_file_p++;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\telse if (c == 'd' || c == 'D')\n\t\t\t\t{\n\t\t\t\t\tpr_immediate_type = type_double;\n\t\t\t\t\tpr_file_p++;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tpr_file_p++;\n\t\t\t}\n\t\t\tpr_token[tokenlen++] = 0;\n\t\t\tif (pr_immediate_type == type_double)\n\t\t\t\tpr_immediate._double = atof(pr_token);\n\t\t\telse\n\t\t\t\tpr_immediate._float = (float)atof(pr_token);\n\t\t\tgoto checkjunk;\n\t\t}\n\t\telse if (c == 'f' || c == 'F')\n\t\t{\n\t\t\tpr_token[tokenlen++] = c;\n\t\t\tpr_token[tokenlen++] = 0;\n\t\t\tpr_file_p++;\n\t\t\tpr_immediate_type = type_float;\n\t\t\tpr_immediate._float = num*sign;\n\n\t\t\tnum*=sign;\n\t\t\tif ((longlong)pr_immediate._float != (longlong)num)\n\t\t\t\tQCC_PR_ParseWarning(WARN_OVERFLOW, \"numerical overflow\");\n\t\t\tgoto checkjunk;\n\t\t}\n\t\telse if (c == 'd' || c == 'D')\n\t\t{\t//note: conflicts with hex. add a dot before it or something.\n\t\t\tpr_token[tokenlen++] = c;\n\t\t\tpr_token[tokenlen++] = 0;\n\t\t\tpr_file_p++;\n\t\t\tpr_immediate_type = type_double;\n\t\t\tpr_immediate._double = num*sign;\n\n\t\t\tnum*=sign;\n\t\t\tif ((longlong)pr_immediate._double != (longlong)num)\n\t\t\t\tQCC_PR_ParseWarning(WARN_OVERFLOW, \"numerical overflow\");\n\t\t\tgoto checkjunk;\n\t\t}\n\t\telse if (c == 'i' || c == 'u' || c == 'l' || c == 'I' || c == 'U' || c == 'L')\n\t\t{\t//length and sign flags can be any order. LL suffix must have the same case (but not necessarily match the sign suffix)\n\t\t\tint isunsigned;\n\t\t\tint islong = (c == 'l')||(c == 'L');\n\t\t\tpr_token[tokenlen++] = c;\n\t\t\tpr_file_p++;\n\t\t\tif (islong)\n\t\t\t{\t//length suffix was first.\n\t\t\t\t//long-long?\n\t\t\t\tif (*pr_file_p == c)\n\t\t\t\t\tislong++, pr_token[tokenlen++] = *pr_file_p++;\n\t\t\t\t//check for signed suffix...\n\t\t\t\tc = *pr_file_p;\n\t\t\t\tisunsigned = (c == 'u')||(c=='U');\n\t\t\t\tif (c == 'i' || c == 'I' || isunsigned)\t//ignore an explicit redundant 'i' char, for not-a-float.\n\t\t\t\t\tpr_token[tokenlen++] = *pr_file_p++;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tisunsigned = (c == 'u')||(c=='U');\n\t\t\t\t//we already made sure it u or i, and its not an l\n\t\t\t\tc = *pr_file_p;\n\t\t\t\tif (c == 'l' || c == 'L')\n\t\t\t\t{\n\t\t\t\t\tpr_token[tokenlen++] = *pr_file_p++;\n\t\t\t\t\tislong = true;\n\t\t\t\t\t//long-long?\n\t\t\t\t\tif (*pr_file_p == c)\n\t\t\t\t\t\tislong++, pr_token[tokenlen++] = *pr_file_p++;\n\t\t\t\t}\n\t\t\t}\n\t\t\tpr_token[tokenlen++] = 0;\n\t\t\tnum *= sign;\n\t\t\tif (islong >= (flag_ILP32?2:1))\n\t\t\t{\t//enough longs for our 64bit type.\n\t\t\t\tpr_immediate_type = (isunsigned)?type_uint64:type_int64;\n\t\t\t\tpr_immediate.i64 = num;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tpr_immediate_type = (isunsigned)?type_uint:type_integer;\n\t\t\t\tpr_immediate._int = num;\n\n\t\t\t\tif ((longlong)pr_immediate._int != (longlong)num)\n\t\t\t\t{\n\t\t\t\t\tif (((longlong)pr_immediate._int & LL(0xffffffff80000000)) != LL(0xffffffff80000000))\n\t\t\t\t\t\tQCC_PR_ParseWarning(WARN_OVERFLOW, \"numerical overflow\");\n\t\t\t\t}\n\t\t\t}\n\t\t\tgoto checkjunk;\n\t\t}\n\t\telse\n\t\t\tbreak;\n\t\tpr_file_p++;\n\t}\n\tpr_token[tokenlen++] = 0;\n\n\tif (!pr_immediate_type)\n\t{\n\t\t//float f = num;\n\t\tif (flag_assume_integer)// || (base != 10 && sign > 0 && (long long)f != (long long)num))\n\t\t{\n\t\t\tif (num > UINT64_C(0x7fffffffffffffff))\n\t\t\t\tpr_immediate_type = type_uint64;\n\t\t\telse if (num > 0xffffffffu)\n\t\t\t\tpr_immediate_type = type_int64;\n\t\t\telse if (num > 0x7fffffffu)\n\t\t\t\tpr_immediate_type = type_uint;\n\t\t\telse\n\t\t\t\tpr_immediate_type = type_integer;\n\t\t}\n\t\telse if (flag_qccx && base == 16)\n\t\t{\n\t\t\tpr_immediate_type = type_float;\n\t\t\tgoto qccxhex;\n\t\t}\n\t\telse\n\t\t\tpr_immediate_type = type_float;\n\t}\n\n\tif (pr_immediate_type == type_int64 || pr_immediate_type == type_uint64)\n\t\tpr_immediate.i64 = num*sign;\n\telse if (pr_immediate_type == type_integer || pr_immediate_type == type_uint)\n\t{\nqccxhex:\n\t\tpr_immediate._int = num*sign;\n\n\t\tnum*=sign;\n\t\tif ((longlong)pr_immediate._int != (longlong)num)\n\t\t{\n\t\t\tif (((longlong)pr_immediate._int & LL(0xffffffff80000000)) != LL(0xffffffff80000000))\n\t\t\t\t\tQCC_PR_ParseWarning(WARN_OVERFLOW, \"numerical overflow\");\n\t\t}\n\t}\n\telse\n\t{\n\t\tpr_immediate_type = type_float;\n\t\t// at this point, we know there's no . in it, so the NaN bug shouldn't happen\n\t\t// and we cannot use atof on tokens like 0xabc, so use num*sign, it SHOULD be safe\n\t\t//pr_immediate._float = atof(pr_token);\n\t\tpr_immediate._float = (float)(num*sign);\n\n\t\tnum*=sign;\n\t\tif ((longlong)pr_immediate._float != (longlong)num && base == 16)\n\t\t\tQCC_PR_ParseWarning(WARN_OVERFLOW, \"numerical overflow %lld will be rounded to %f\", num, pr_immediate._float);\n\t}\n\ncheckjunk:\n\tc = *pr_file_p;\n\tif ( (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' || (c >= '0' && c <= '9') || (c & 0x80))\n\t\tQCC_PR_ParseWarning(ERR_NOTANUMBER, \"bad suffix on number %s\", pr_token);\n}\n\n\nstatic float QCC_PR_LexFloat (void)\n{\n\tint\t\tc;\n\tint\t\tlen;\n\n\tlen = 0;\n\tc = *pr_file_p;\n\tdo\n\t{\n\t\tpr_token[len] = c;\n\t\tlen++;\n\t\tpr_file_p++;\n\t\tc = *pr_file_p;\n\t} while ((c >= '0' && c<= '9') || (c == '.'&&pr_file_p[1]!='.'));\t//only allow a . if the next isn't too...\n\tif (*pr_file_p == 'f')\n\t\tpr_file_p++;\n\tpr_token[len] = 0;\n\treturn (float)atof (pr_token);\n}\n\n/*\n==============\nPR_LexVector\n\nParses a single quoted vector\n==============\n*/\nstatic void QCC_PR_LexVector (void)\n{\n\tint\t\ti;\n\n\tpr_file_p++;\t//skip the leading ' char\n\n\tif (*pr_file_p == '\\\\')\n\t{//extended character constant\n\t\tpr_file_p++;\n\t\tpr_token_type = tt_immediate;\n\t\tif (flag_assume_integer)\n\t\t{\n\t\t\tpr_immediate_type = type_integer;\n\t\t\tpr_immediate._int = QCC_PR_LexEscapedCodepoint();\n\t\t}\n\t\telse\n\t\t{\n\t\t\tpr_immediate_type = type_float;\n\t\t\tpr_immediate._float = QCC_PR_LexEscapedCodepoint();\n\t\t}\n\t\tif (*pr_file_p != '\\'')\n\t\t\tQCC_PR_ParseError (ERR_INVALIDVECTORIMMEDIATE, \"Bad character constant\");\n\t\tpr_file_p++;\n\t\treturn;\n\t}\n\tif ((unsigned char)*pr_file_p >= 0x80)\n\t{\n\t\tint b = utf8_check(pr_file_p, &pr_immediate._int);\t//utf-8 codepoint.\n\t\tpr_token_type = tt_immediate;\n\t\tif (flag_assume_integer)\n\t\t\tpr_immediate_type = type_integer;\n\t\telse\n\t\t{\n\t\t\tpr_immediate_type = type_float;\n\t\t\tif (flag_qccx)\n\t\t\t\tQCC_PR_ParseWarning(WARN_DENORMAL, \"char constant: denormal\");\n\t\t\telse\n\t\t\t\tpr_immediate._float = pr_immediate._int;\n\t\t}\n\t\tpr_file_p+=b+1;\n\t\treturn;\n\t}\n\telse if (pr_file_p[1] == '\\'')\n\t{//character constant\n\t\tpr_token_type = tt_immediate;\n\t\tif (flag_assume_integer)\n\t\t{\n\t\t\tpr_immediate_type = type_integer;\n\t\t\tpr_immediate._int = (unsigned char)pr_file_p[0];\n\t\t}\n\t\telse\n\t\t{\n\t\t\tpr_immediate_type = type_float;\n\t\t\tif (flag_qccx)\n\t\t\t{\n\t\t\t\tQCC_PR_ParseWarning(WARN_DENORMAL, \"char constant: denormal\");\n\t\t\t\tpr_immediate._int = pr_file_p[0];\n\t\t\t}\n\t\t\telse\n\t\t\t\tpr_immediate._float = pr_file_p[0];\n\t\t}\n\t\tpr_file_p+=2;\n\t\treturn;\n\t}\n\tpr_token_type = tt_immediate;\n\tpr_immediate_type = type_vector;\n\tQCC_PR_LexWhitespace (false);\n\tfor (i=0 ; i<3 ; i++)\n\t{\n\t\tpr_immediate.vector[i] = QCC_PR_LexFloat ();\n\t\tQCC_PR_LexWhitespace (false);\n\n\t\tif (*pr_file_p == '\\'' && i == 1)\n\t\t{\n\t\t\tif (i < 2)\n\t\t\t\tQCC_PR_ParseWarning (WARN_FTE_SPECIFIC, \"2d vector\");\n\n\t\t\tfor (i++ ; i<3 ; i++)\n\t\t\t\tpr_immediate.vector[i] = 0;\n\t\t\tbreak;\n\t\t}\n\t}\n\tif (*pr_file_p != '\\'')\n\t\tQCC_PR_ParseError (ERR_INVALIDVECTORIMMEDIATE, \"Bad vector\");\n\tpr_file_p++;\n}\n\n/*\n==============\nPR_LexName\n\nParses an identifier\n==============\n*/\nstatic void QCC_PR_LexName (void)\n{\n\tunsigned int\t\tc;\n\tint\t\tlen;\n\n\tlen = 0;\n\tdo\n\t{\n\t\tint b = utf8_check(pr_file_p, &c);\n\t\tif (!b)\n\t\t{\n\t\t\tunsigned char lead = *pr_file_p++;\n\t\t\tchar *o;\n\t\t\twhile(*pr_file_p && !utf8_check(pr_file_p, &c))\n\t\t\t\tpr_file_p++;\n\t\t\to = pr_file_p;\n\t\t\twhile (qcc_iswhite(*pr_file_p))\n\t\t\t{\n\t\t\t\tif (*pr_file_p == '\\n')\n\t\t\t\t\tbreak;\n\t\t\t\tpr_file_p++;\n\t\t\t}\n\t\t\tif (*pr_file_p == '\\n')\n\t\t\t\tQCC_PR_ParseError(ERR_NOTANAME, \"Invalid UTF-8 code sequence at end of line. Lead byte was %#2x\", lead);\n\t\t\telse\n\t\t\t{\n\t\t\t\tlen = 0;\n\t\t\t\twhile (*pr_file_p && !qcc_iswhite(*pr_file_p))\n\t\t\t\t\tpr_token[len++] = *pr_file_p++;\n\t\t\t\tpr_token[len++] = 0;\n\t\t\t\tpr_file_p = o;\n\t\t\t\tQCC_PR_ParseError(ERR_NOTANAME, \"Invalid UTF-8 code sequence before %s. Lead byte was %#2x\", pr_token, lead);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\twhile(b-->0)\n\t\t{\n\t\t\tpr_token[len] = *pr_file_p++;\n\t\t\tlen++;\n\t\t}\n\t\tc = *pr_file_p;\n\t} while ( (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_'\n\t|| (c >= '0' && c <= '9') || (c & 0x80));\n\n\tpr_token[len] = 0;\n\tpr_token_type = tt_name;\n}\n\n/*\n==============\nPR_LexPunctuation\n==============\n*/\nstatic void QCC_PR_LexPunctuation (void)\n{\n\tint\t\ti;\n\tint\t\tlen;\n\tchar\t*p;\n\n\tpr_token_type = tt_punct;\n\n\tif (pr_file_p[0] == '*' && pr_file_p[1] == '*' && flag_dblstarexp)\n\t{\t//for compat with gmqcc. fteqcc uses *^ internally (which does not conflict with multiplying by dereferenced pointers - sucks for MSCLR c++ syntax)\n\t\tQCC_PR_ParseWarning(WARN_GMQCC_SPECIFIC, \"** is unsafe around pointers, use *^ instead.\");\n\t\tstrcpy (pr_token, \"*^\");\n\t\tpr_file_p += 2;\n\t\treturn;\n\t}\n\n\tfor (i=0 ; (p = pr_punctuation[i]) != NULL ; i++)\n\t{\n\t\tlen = strlen(p);\n\t\tif (!strncmp(p, pr_file_p, len) )\n\t\t{\n\t\t\tstrcpy (pr_token, pr_punctuationremap[i]);\n\t\t\tif (p[0] == '{')\n\t\t\t\tpr_bracelevel++;\n\t\t\telse if (p[0] == '}')\n\t\t\t\tpr_bracelevel--;\n\t\t\tpr_file_p += len;\n\t\t\treturn;\n\t\t}\n\t}\n\n\tif ((unsigned char)*pr_file_p == (unsigned char)'\\\\' && pr_file_p[1] == '\\r' && pr_file_p[2] == '\\n')\n\t\tpr_file_p+=3;\n\telse if ((unsigned char)*pr_file_p == (unsigned char)'\\\\' && (pr_file_p[1] == '\\r' || pr_file_p[1] == '\\n'))\n\t\tpr_file_p+=2;\n\telse\n\t{\n\t\tif ((unsigned char)*pr_file_p == (unsigned char)0xa0)\n\t\t\tQCC_PR_ParseWarning (ERR_UNKNOWNPUCTUATION, \"Unsupported punctuation: '\\\\x%x' - non-breaking space\", (unsigned char)*pr_file_p);\n\t\telse\n\t\t\tQCC_PR_ParseWarning (ERR_UNKNOWNPUCTUATION, \"Unknown punctuation: '\\\\x%x'\", *pr_file_p);\n\t\tpr_file_p++;\n\t}\n\n\tQCC_PR_Lex();\n}\n\n\n/*\n==============\nPR_LexWhitespace\n==============\n*/\nvoid QCC_PR_LexWhitespace (pbool inhibitpreprocessor)\n{\n\tint\t\tc;\n\n\twhile (1)\n\t{\n\t// skip whitespace\n\t\twhile ((c = *pr_file_p) && qcc_iswhite(c))\n\t\t{\n\t\t\tif (qcc_islineending(c, pr_file_p[1]))\n\t\t\t{\n\t\t\t\tpr_file_p++;\n\t\t\t\tif (!inhibitpreprocessor)\n\t\t\t\t\tQCC_PR_NewLine (false);\n\t\t\t\telse\n\t\t\t\t\tpr_source_line++;\n\t\t\t\tif (!pr_file_p)\n\t\t\t\t\treturn;\n\t\t\t}\n\t\t\telse\n\t\t\t\tpr_file_p++;\n\t\t}\n\t\tif (c == 0)\n\t\t\treturn;\t\t// end of file\n\n\t// skip // comments\n\t\tif (c=='/' && pr_file_p[1] == '/')\n\t\t{\n\t\t\twhile (*pr_file_p && !qcc_islineending(pr_file_p[0], pr_file_p[1]))\n\t\t\t\tpr_file_p++;\n\n\t\t\tif (*pr_file_p)\n\t\t\t\tpr_file_p++;\t//don't break on eof.\n\t\t\tif (!inhibitpreprocessor)\n\t\t\t\tQCC_PR_NewLine(false);\n\t\t\telse\n\t\t\t\tpr_source_line++;\n\t\t\tcontinue;\n\t\t}\n\n\t// skip /* */ comments\n\t\tif (c=='/' && pr_file_p[1] == '*')\n\t\t{\n\t\t\tpr_file_p+=1;\n\t\t\tdo\n\t\t\t{\n\t\t\t\tpr_file_p++;\n\t\t\t\tif (qcc_islineending(pr_file_p[0], pr_file_p[1]))\n\t\t\t\t{\n\t\t\t\t\tif (!inhibitpreprocessor)\n\t\t\t\t\t\tQCC_PR_NewLine(true);\n\t\t\t\t\telse\n\t\t\t\t\t\tpr_source_line++;\n\t\t\t\t}\n\t\t\t\tif (pr_file_p[1] == 0)\n\t\t\t\t{\n\t\t\t\t\tQCC_PR_ParseError(0, \"EOF inside comment\\n\");\n\t\t\t\t\tpr_file_p++;\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (pr_file_p[0] == '/' && pr_file_p[1] == '*')\n\t\t\t\t\tQCC_PR_ParseWarning(WARN_NESTEDCOMMENT, \"\\\"/*\\\" inside comment\");\n\t\t\t} while (pr_file_p[0] != '*' || pr_file_p[1] != '/');\n\t\t\tpr_file_p+=2;\n\t\t\tcontinue;\n\t\t}\n\n\t\tbreak;\t\t// a real character has been found\n\t}\n}\n\n//============================================================================\n\n#define\tMAX_FRAMES\t8192\nchar\tpr_framemodelname[64];\nstruct\n{\n\tchar name[64];\n\tint value;\n\tconst char *file;\t//compare to s_filen to see if its current or not\n} pr_framemacro[MAX_FRAMES];\nint\t\tpr_nummacros;\nint\t\tpr_macrovalue;\t//next value to use\nint\t\tpr_savedmacro;\t//for sub-groups.\n\nvoid QCC_PR_ClearGrabMacros (pbool newfile)\n{\n\tif (!newfile)\n\t\tpr_nummacros = 0;\n\tpr_macrovalue = 0;\n\tpr_savedmacro = -1;\n}\n\nstatic int QCC_PR_FindMacro (char *name)\n{\n\tint\t\ti;\n\n\tfor (i=pr_nummacros-1 ; i>=0 ; i--)\n\t{\n\t\tif (!STRCMP (name, pr_framemacro[i].name))\n\t\t{\n\t\t\tif (pr_framemacro[i].file != s_filen)\n\t\t\t\tQCC_PR_ParseWarning(WARN_STALEMACRO, \"Stale macro used (%s, defined in %s)\", pr_token, pr_framemacro[i].file);\n\t\t\treturn pr_framemacro[i].value;\n\t\t}\n\t}\n\tfor (i=pr_nummacros-1 ; i>=0 ; i--)\n\t{\n\t\tif (!stricmp (name, pr_framemacro[i].name))\n\t\t{\n\t\t\tQCC_PR_ParseWarning(WARN_CASEINSENSITIVEFRAMEMACRO, \"Case insensitive frame macro (using %s)\", pr_framemacro[i].name);\n\t\t\tif (pr_framemacro[i].file != s_filen)\n\t\t\t\tQCC_PR_ParseWarning(WARN_STALEMACRO, \"Stale macro used (%s, defined in %s)\", pr_token, pr_framemacro[i].file);\n\t\t\treturn pr_framemacro[i].value;\n\t\t}\n\t}\n\treturn -1;\n}\n\nstatic void QCC_PR_ExpandMacro(void)\n{\n\tint\t\ti = QCC_PR_FindMacro(pr_token);\n\n\tif (i < 0)\n\t\tQCC_PR_ParseError (ERR_BADFRAMEMACRO, \"Unknown frame macro $%s\", pr_token);\n\n\tQC_snprintfz(pr_token, sizeof(pr_token),\"%d\", i);\n\tpr_token_type = tt_immediate;\n\tpr_immediate_type = type_float;\n\tpr_immediate._float = (float)i;\n}\n\npbool QCC_PR_SimpleGetString(void)\n{\n\tint\t\tc;\n\tint\t\ti = 0;\n\tchar *f;\n\n\tpr_token[0] = 0;\n\n// skip whitespace\n\twhile ((c = *pr_file_p) && qcc_iswhite(c))\n\t{\n\t\tif (c=='\\n')\n\t\t\treturn false;\n\t\tpr_file_p++;\n\t}\n\tif (c == 0)\t//eof\n\t\treturn false;\n//abort if there's a comment.\n\tif (pr_file_p[0] == '/')\n\t{\n\t\tif (pr_file_p[1] == '/')\n\t\t{\t//comment alert\n\t\t\twhile(*pr_file_p && *pr_file_p != '\\n')\n\t\t\t\tpr_file_p++;\n\t\t\treturn false;\n\t\t}\n\t\tif (pr_file_p[1] == '*')\n\t\t\treturn false;\n\t}\n\n\tif (*pr_file_p != '\\\"')\n\t\treturn false;\t//nope, not a string.\n\tf = pr_file_p+1;\n\twhile (*f)\n\t{\n\t\tif (*f == '\\n' || !*f)\n\t\t{\t//bad string\n\t\t\tQCC_Error (ERR_INTERNAL, \"new line inside string\");\n\t\t\tpr_token[0] = 0;\n\t\t\treturn false;\n\t\t}\n\t\tif (*f == '\\\"')\n\t\t{\t//end-of-string\n\t\t\tpr_token[i] = 0;\n\t\t\tpr_file_p = f+1;\n\t\t\treturn false;\n\t\t}\n\t\tif (i == sizeof(qcc_token)-1)\n\t\t\tQCC_Error (ERR_INTERNAL, \"token exceeds %i chars\", i);\n\t\tif (*f == '\\\\')\n\t\t{\n\t\t\tf++;\n\t\t\tif (!*f)\n\t\t\t\tf = \"\";\n\t\t\telse if (*f == 'n')\n\t\t\t{\n\t\t\t\tpr_token[i++] = '\\n';\n\t\t\t\tf++;\n\t\t\t}\n\t\t\telse if (*f == 'r')\n\t\t\t{\n\t\t\t\tpr_token[i++] = '\\r';\n\t\t\t\tf++;\n\t\t\t}\n\t\t\telse if (*f == 't')\n\t\t\t{\n\t\t\t\tpr_token[i++] = '\\t';\n\t\t\t\tf++;\n\t\t\t}\n\t\t\telse\t\t\n\t\t\t\tpr_token[i++] = *f++;\n\t\t}\n\t\telse\n\t\t\tpr_token[i++] = *f++;\n\t}\n\treturn true;\n}\n// just parses text, returning false if an eol is reached\npbool QCC_PR_SimpleGetToken (void)\n{\n\tint\t\tc;\n\tint\t\ti;\n\n\tpr_token[0] = 0;\n\n// skip whitespace\n\twhile ((c = *pr_file_p) && qcc_iswhite(c))\n\t{\n\t\tif (c=='\\n')\n\t\t\treturn false;\n\t\tpr_file_p++;\n\t}\n\tif (c == 0)\t//eof\n\t\treturn false;\n\tif (pr_file_p[0] == '/')\n\t{\n\t\tif (pr_file_p[1] == '/')\n\t\t{\t//comment alert\n\t\t\twhile(*pr_file_p && *pr_file_p != '\\n')\n\t\t\t\tpr_file_p++;\n\t\t\treturn false;\n\t\t}\n\t\tif (pr_file_p[1] == '*')\n\t\t\treturn false;\n\t}\n\n\ti = 0;\n\twhile ((c = *pr_file_p) && !qcc_iswhite(c) && c != ',' && c != ';' && c != ')' && c != '(' && c != ']' && !(c == '/' && pr_file_p[1] == '/'))\n\t{\n\t\tif (i == sizeof(qcc_token)-1)\n\t\t\tQCC_Error (ERR_INTERNAL, \"token exceeds %i chars\", i);\n\t\tpr_token[i] = c;\n\t\ti++;\n\t\tpr_file_p++;\n\t}\n\tpr_token[i] = 0;\n\treturn i!=0;\n}\n\nstatic pbool QCC_PR_LexMacroName(void)\n{\n\tint\t\tc;\n\tint\t\ti;\n\n\tpr_token[0] = 0;\n\n// skip whitespace\n\twhile ((c = *pr_file_p) && qcc_iswhite(c))\n\t{\n\t\tif (c=='\\n')\n\t\t\treturn false;\n\t\tpr_file_p++;\n\t}\n\tif (!c)\n\t\treturn false;\n\tif (pr_file_p[0] == '/')\n\t{\n\t\tif (pr_file_p[1] == '/')\n\t\t{\t//comment alert\n\t\t\twhile(*pr_file_p && *pr_file_p != '\\n')\n\t\t\t\tpr_file_p++;\n\t\t\treturn false;\n\t\t}\n\t\tif (pr_file_p[1] == '*')\n\t\t\treturn false;\n\t}\n\n\ti = 0;\n\twhile ( (c = *pr_file_p) > ' ' && c != '\\n' && c != ',' && c != ';' && c != '&' && c != '|' && c != ')' && c != '(' && c != ']' && !(pr_file_p[0] == '.' && pr_file_p[1] == '.'))\n\t{\n\t\tif (i == sizeof(qcc_token)-1)\n\t\t\tQCC_Error (ERR_INTERNAL, \"token exceeds %i chars\", i);\n\t\tpr_token[i] = c;\n\t\ti++;\n\t\tpr_file_p++;\n\t}\n\tpr_token[i] = 0;\n\treturn i!=0;\n}\n\nstatic void QCC_PR_MacroFrame(char *name, int value, pbool force)\n{\n\tint i;\n\tfor (i=pr_nummacros-1 ; i>=0 ; i--)\n\t{\n\t\tif (!STRCMP (name, pr_framemacro[i].name))\n\t\t{\n\t\t\t//vanilla macro behaviour is to not realise that there's dupes. lookups find the first, so dupes end up as dead gaps.\n\t\t\t//our caller incremented the value externally\n\t\t\t//so warn+ignore if its from the same file\n\t\t\tif (pr_framemacro[i].file == s_filen && !force)\n\t\t\t\tQCC_PR_ParseWarning(WARN_DUPLICATEMACRO, \"Duplicate macro defined (%s). Rename it.\", pr_token);\n\t\t\telse\n\t\t\t{\n\t\t\t\tpr_framemacro[i].value = value;\t//old file, override it, whatever the old value was is redundant now\n\t\t\t\tpr_framemacro[i].file = s_filen;\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t}\n\n\tif (strlen(name)+1 > sizeof(pr_framemacro[0].name))\n\t\tQCC_PR_ParseWarning(ERR_TOOMANYFRAMEMACROS, \"Name for frame macro %s is too long\", name);\n\telse\n\t{\n\t\tstrcpy (pr_framemacro[pr_nummacros].name, name);\n\t\tpr_framemacro[pr_nummacros].value = value;\n\t\tpr_framemacro[pr_nummacros].file = s_filen;\n\t\tpr_nummacros++;\n\t\tif (pr_nummacros >= MAX_FRAMES)\n\t\t\tQCC_PR_ParseError(ERR_TOOMANYFRAMEMACROS, \"Too many frame macros defined\");\n\t}\n}\n\nstatic void QCC_PR_ParseFrame (void)\n{\n\twhile (QCC_PR_LexMacroName ())\n\t{\n\t\tQCC_PR_MacroFrame(pr_token, pr_macrovalue++, false);\n\t}\n}\n\n/*\n==============\nPR_LexGrab\n\nDeals with counting sequence numbers and replacing frame macros\n==============\n*/\nstatic void QCC_PR_LexGrab (void)\n{\n\tpr_file_p++;\t// skip the $\n//\tif (!QCC_PR_SimpleGetToken ())\n//\t\tQCC_PR_ParseError (\"hanging $\");\n\tif (qcc_iswhite(*pr_file_p))\n\t\tQCC_PR_ParseError (ERR_BADFRAMEMACRO, \"hanging $\");\n\tQCC_PR_LexMacroName();\n\tif (!*pr_token)\n\t\tQCC_PR_ParseError (ERR_BADFRAMEMACRO, \"hanging $\");\n\n// check for $frame\n\tif (!STRCMP (pr_token, \"frame\") || !STRCMP (pr_token, \"framesave\"))\n\t{\n\t\tQCC_PR_ParseFrame ();\n\t\tQCC_PR_Lex ();\n\t}\n// ignore other known $commands - just for model/spritegen\n\telse if (!STRCMP (pr_token, \"cd\")\n\t|| !STRCMP (pr_token, \"origin\")\n\t|| !STRCMP (pr_token, \"base\")\n\t|| !STRCMP (pr_token, \"flags\")\n\t|| !STRCMP (pr_token, \"scale\")\n\t|| !STRCMP (pr_token, \"skin\") )\n\t{\t// skip to end of line\n\t\twhile (QCC_PR_LexMacroName ())\n\t\t;\n\t\tQCC_PR_Lex ();\n\t}\n\telse if (!STRCMP (pr_token, \"flush\"))\n\t{\n\t\tQCC_PR_ClearGrabMacros(true);\n\t\twhile (QCC_PR_LexMacroName ())\n\t\t;\n\t\tQCC_PR_Lex ();\n\t}\n\telse if (!STRCMP (pr_token, \"frame_reset\"))\n\t{\t//for compat with qfcc. full reset of all frame macros.\n\t\tQCC_PR_ClearGrabMacros(false);\n\t\twhile (QCC_PR_LexMacroName ())\n\t\t;\n\t\tQCC_PR_Lex ();\n\t}\n\telse if (!STRCMP (pr_token, \"framevalue\"))\n\t{\n\t\tQCC_PR_LexMacroName ();\n\t\tpr_macrovalue = atoi(pr_token);\n\n\t\tQCC_PR_Lex ();\n\t}\n\telse if (!STRCMP (pr_token, \"framerestore\"))\n\t{\n\t\tQCC_PR_LexMacroName ();\n\t\tQCC_PR_ExpandMacro();\n\t\tpr_macrovalue = (int)pr_immediate._float;\n\n\t\tQCC_PR_Lex ();\n\t}\n\telse if (!STRCMP (pr_token, \"modelname\"))\n\t{\n\t\tint i;\n\t\tQCC_PR_LexMacroName ();\n\n\t\tif (*pr_framemodelname)\n\t\t\tQCC_PR_MacroFrame(pr_framemodelname, pr_macrovalue, true);\n\n\t\tif (!QC_strlcpy(pr_framemodelname, pr_token, sizeof(pr_framemodelname)))\n\t\t\tQCC_PR_ParseWarning (WARN_STRINGTOOLONG, \"$modelname name too long\");\n\n\t\ti = QCC_PR_FindMacro(pr_framemodelname);\n\t\tif (i)\n\t\t\tpr_macrovalue = i;\n\t\telse\n\t\t\ti = 0;\n\n\t\tQCC_PR_Lex ();\n\t}\n// look for a frame name macro\n\telse\n\t\tQCC_PR_ExpandMacro ();\n}\n\n//===========================\n//compiler constants\t- dmw\n\npbool QCC_PR_UndefineName(const char *name)\n{\n//\tint a;\n\tCompilerConstant_t *c;\n\tc = pHash_Get(&compconstantstable, name);\n\tif (!c)\n\t{\n\t\tQCC_PR_ParseWarning(WARN_UNDEFNOTDEFINED, \"Precompiler constant %s was not defined\", name);\n\t\treturn false;\n\t}\n\n\tHash_Remove(&compconstantstable, name);\n\treturn true;\n}\n\nCompilerConstant_t *QCC_PR_DefineName(const char *name, const char *value)\n{\n\tint i;\n\tCompilerConstant_t *cnst;\n\n\tif (strlen(name) >= MAXCONSTANTNAMELENGTH || !*name)\n\t\tQCC_PR_ParseError(ERR_NAMETOOLONG, \"Compiler constant name length is too long or short\");\n\n\tcnst = pHash_Get(&compconstantstable, name);\n\tif (cnst)\n\t{\n\t\tif (strcmp(cnst->value, value?value:\"\") || cnst->numparams!=-1 || cnst->varg)\n\t\t\tQCC_PR_ParseWarning(WARN_DUPLICATEDEFINITION, \"Duplicate definition for Precompiler constant %s\", name);\n\t\tHash_Remove(&compconstantstable, name);\n\t}\n\n\tcnst = qccHunkAlloc(sizeof(CompilerConstant_t));\n\n\tcnst->used = false;\n\tcnst->numparams = -1;\n\tcnst->evil = false;\n\tstrcpy(cnst->name, name);\n\tcnst->namelen = strlen(name);\n\tcnst->value = cnst->name + strlen(cnst->name);\n\tfor (i = 0; i < MAXCONSTANTPARAMS; i++)\n\t\tcnst->params[i][0] = '\\0';\n\n\tpHash_Add(&compconstantstable, cnst->name, cnst, qccHunkAlloc(sizeof(bucket_t)));\n\n\tif (value && *value)\n\t\tcnst->value = strcpy(qccHunkAlloc(strlen(value)+1), value);\n\n\treturn cnst;\n}\n\nvoid QCC_PR_PreProcessor_Define(pbool append)\n{\n\tchar *d;\n\tchar *dbuf;\n\tint dbuflen;\n\tchar *s;\n\tint quote=false;\n\tpbool preprocessorhack = false;\n\tCompilerConstant_t *cnst, *oldcnst;\n\n\tQCC_PR_SimpleGetToken ();\n\n\tif (!QCC_PR_SimpleGetToken ())\n\t\tQCC_PR_ParseError(ERR_NONAME, \"No name defined for compiler constant\");\n\n\toldcnst = pHash_Get(&compconstantstable, pr_token);\n\tif (oldcnst)\n\t\tHash_Remove(&compconstantstable, oldcnst->name);\n\n\tcnst = QCC_PR_DefineName(pr_token, NULL);\n\n\tif (*pr_file_p == '(')\n\t{\n\t\tcnst->numparams = 0;\n\t\tpr_file_p++;\n\t\twhile(qcc_iswhitesameline(*pr_file_p))\n\t\t\tpr_file_p++;\n\t\ts = pr_file_p;\n\t\tfor (;;)\n\t\t{\n\t\t\tif (*pr_file_p == ',' || *pr_file_p == ')')\n\t\t\t{\n\t\t\t\tint nl;\n\t\t\t\tnl = pr_file_p-s;\n\t\t\t\twhile(nl > 0 && qcc_iswhitesameline(s[nl-1]))\n\t\t\t\t\tnl--;\n\t\t\t\tif (cnst->numparams >= MAXCONSTANTPARAMS)\n\t\t\t\t\tQCC_PR_ParseError(ERR_MACROTOOMANYPARMS, \"May not have more than %i parameters to a macro\", MAXCONSTANTPARAMS);\n\t\t\t\tif (nl >= MAXCONSTANTPARAMLENGTH)\n\t\t\t\t\tQCC_PR_ParseError(ERR_MACROTOOMANYPARMS, \"parameter name is too long (max %i)\", MAXCONSTANTPARAMLENGTH);\n\t\t\t\tif (nl == 3 && s[0] == '.' && s[1] == '.' && s[2] == '.')\n\t\t\t\t{\n\t\t\t\t\tcnst->varg = true;\n\t\t\t\t\tif (*pr_file_p != ')')\n\t\t\t\t\t\tQCC_PR_ParseError(ERR_MACROTOOMANYPARMS, \"varadic argument must be last\");\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tmemcpy(cnst->params[cnst->numparams], s, nl);\n\t\t\t\t\tcnst->params[cnst->numparams][nl] = '\\0';\n\t\t\t\t\tfor (nl = 0; nl < cnst->numparams; nl++)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (!strcmp(cnst->params[nl], cnst->params[cnst->numparams]))\n\t\t\t\t\t\t\tQCC_PR_ParseError(ERR_MACROTOOMANYPARMS, \"duplicate macro paramter name '%s'\", cnst->params[nl]);\n\t\t\t\t\t}\n\t\t\t\t\tcnst->numparams++;\n\t\t\t\t}\n\t\t\t\tif (*pr_file_p++ == ')')\n\t\t\t\t\tbreak;\n\t\t\t\twhile(qcc_iswhitesameline(*pr_file_p))\n\t\t\t\t\tpr_file_p++;\n\t\t\t\ts = pr_file_p;\n\t\t\t}\n\t\t\tif(!*pr_file_p++)\n\t\t\t{\n\t\t\t\tQCC_PR_ParseError(ERR_EXPECTED, \"missing ) in macro parameter list\");\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\telse cnst->numparams = -1;\n\n\t//disable append mode if they're trying to do something stupid\n\tif (append)\n\t{\n\t\tif (!oldcnst)\n\t\t\tappend = false;\t//append with no previous define is treated as just a regular define. huzzah.\n\t\telse if (cnst->numparams != oldcnst->numparams || cnst->varg != oldcnst->varg)\n\t\t{\n\t\t\tQCC_PR_ParseWarning(WARN_DUPLICATEPRECOMPILER, \"different number of macro arguments in macro append\");\n\t\t\tappend = false;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tint i;\n\t\t\t//arguments need to be specified, if only so that appends with arguments are still vaugely readable.\n\t\t\t//argument names need to match because the expansion is too lame to cope if they're different.\n\t\t\tfor (i = 0; i < cnst->numparams; i++)\n\t\t\t{\n\t\t\t\tif (strcmp(cnst->params[i], oldcnst->params[i]))\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (i < cnst->numparams)\n\t\t\t{\n\t\t\t\tQCC_PR_ParseWarning(WARN_DUPLICATEPRECOMPILER, \"arguments differ in macro append\");\n\t\t\t\tappend = false;\n\t\t\t}\n\t\t\telse\n\t\t\t\tappend = true;\n\t\t}\n\t}\n\n\ts = pr_file_p;\n\td = dbuf = NULL;\n\tdbuflen = 0;\n\n\tif (append)\n\t{\n\t\t//start with the old value\n\t\tint olen = strlen(oldcnst->value);\n\t\tdbuflen = olen + 128;\n\t\tdbuf = qccHunkAlloc(dbuflen);\n\t\tmemcpy(dbuf, oldcnst->value, olen);\n\t\td = dbuf + olen;\n\t\t*d++ = ' ';\n\t}\n\n\tcnst->fromfile = s_filen;\n\tcnst->fromline = pr_source_line;\n\n\twhile(*s == ' ' || *s == '\\t')\n\t\ts++;\n\twhile(1)\n\t{\n\t\tif ((d - dbuf) + 2 >= dbuflen)\n\t\t{\n\t\t\tint len = d - dbuf;\n\t\t\tdbuflen = (len+128) * 2;\n\t\t\tdbuf = qccHunkAlloc(dbuflen);\n\t\t\tmemcpy(dbuf, d - len, len);\n\t\t\td = dbuf + len;\n\t\t}\n\n\t\tif( *s == '\\\\' )\n\t\t{\n\t\t\t// read over a newline if necessary\n\t\t\tif( s[1] == '\\n' || s[1] == '\\r' )\n\t\t\t{\n\t\t\t\tchar *exploitcheck;\n\t\t\t\ts++;\t//skip the \\ char\n\t\t\t\tif (*s == '\\r' && s[1] == '\\n')\n\t\t\t\t\ts++;\t//skip the \\r. the \\n will become part of the macro.\n\t\t\t\ts++;\t//skip the \\n\n\t\t\t\tQCC_PR_NewLine(true);\n\n/*\nThis began as a bug. It is still evil, but its oh so useful.\n\nIn C,\n#define foobar \\\nfoo\\\nbar\\\nmoo\n\nbecomes foobarmoo, not foo\\nbar\\nmoo\n\n#define hacks however, require that it becomes\nfoo\\nbar\\nmoo\n\n# cannot be used on the first line of the macro, and then is only valid as the first non-white char of the following lines\nso if present, the preceeding \\\\\\n and following \\\\\\n must become an actual \\n instead of being stripped.\n*/\n\n\t\t\t\tfor (exploitcheck = s; *exploitcheck && qcc_iswhitesameline(*exploitcheck); exploitcheck++)\n\t\t\t\t\t;\n\t\t\t\tif (*exploitcheck == '#')\n\t\t\t\t{\n\t\t\t\t\t*d++ = '\\n';\n\t\t\t\t\tif (!cnst->evil)\n\t\t\t\t\t\tQCC_PR_ParseWarning(WARN_EVILPREPROCESSOR, \"preprocessor directive within preprocessor macro %s\", cnst->name);\n\t\t\t\t\tcnst->evil = true;\n\t\t\t\t\tpreprocessorhack = true;\n\t\t\t\t}\n\t\t\t\telse if (preprocessorhack)\n\t\t\t\t{\n\t\t\t\t\t*d++ = '\\n';\n\t\t\t\t\tpreprocessorhack = false;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse if(*s == '\\r' || *s == '\\n' || *s == '\\0')\n\t\t{\n\t\t\tbreak;\n\t\t}\n\t\tif (!quote && s[0]=='/'&&s[1]=='/')\n\t\t\tbreak;\t//c++ style comments can just be ignored\n\t\tif (!quote && s[0]=='/'&&s[1]=='*')\n\t\t{\t//multi-line c style comments become part of the define itself. this also negates the need for \\ at the end of lines.\n\t\t\t//although we don't bother embedding.\n\t\t\ts+=2;\n\t\t\tfor(;;)\n\t\t\t{\n\t\t\t\tif (!s[0])\n\t\t\t\t{\n\t\t\t\t\tQCC_PR_ParseWarning(WARN_DUPLICATEPRECOMPILER, \"EOF inside quote in define %s\", cnst->name);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tif (s[0]=='*'&&s[1]=='/')\n\t\t\t\t{\n\t\t\t\t\ts+=2;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tif (s[0] == '\\n')\n\t\t\t\t\tpr_source_line++;\n\t\t\t\ts++;\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\tif (*s == '\\\"')\n\t\t\tquote=!quote;\n\n\t\t*d = *s;\n\t\td++;\n\t\ts++;\n\t}\n\n\twhile (d>dbuf && qcc_iswhitesameline(d[-1]))\n\t\td--;\n\t*d = '\\0';\n\n\tcnst->value = dbuf;\n\n\tif (oldcnst && !append)\n\t{\t//we always warn if it was already defined\n\t\t//we use different warning codes so that -Wno-mundane can be used to ignore identical redefinitions.\n\t\tif (strcmp(oldcnst->value, cnst->value))\n\t\t\tQCC_PR_ParseWarning(WARN_DUPLICATEPRECOMPILER, \"Alternate precompiler definition of %s (%s -> %s)\", pr_token, oldcnst->value, cnst->value);\n\t\telse\n\t\t\tQCC_PR_ParseWarning(WARN_IDENTICALPRECOMPILER, \"Identical precompiler definition of %s\", pr_token);\n\t}\n\n\tpr_file_p = s;\n}\n\n/* *buffer, *bufferlen and *buffermax should be NULL/0 at the start */\nstatic void QCC_PR_ExpandStrCat(char **buffer, size_t *bufferlen, size_t *buffermax,   char *newdata, size_t newlen)\n{\n\tsize_t newmax = *bufferlen + newlen;\n\n\tif (newmax < *bufferlen)//check for overflow\n\t{\n\t\tQCC_PR_ParseWarning(ERR_INTERNAL, \"exceeds 4gb\");\n\t\treturn;\n\t}\n\tif (newmax > *buffermax)\n\t{\n\t\tchar *newbuf;\n\t\tif (newmax < 64)\n\t\t\tnewmax = 64;\n\t\tif (newmax < *bufferlen * 2)\n\t\t{\n\t\t\tnewmax = *bufferlen * 2;\n\t\t\tif (newmax < *bufferlen) /*overflowed?*/\n\t\t\t{\n\t\t\t\tQCC_PR_ParseWarning(ERR_INTERNAL, \"exceeds 4gb\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tnewbuf = realloc(*buffer, newmax);\n\t\tif (!newbuf)\n\t\t{\n\t\t\tQCC_PR_ParseWarning(ERR_INTERNAL, \"out of memory\");\n\t\t\treturn; /*OOM*/\n\t\t}\n\t\t*buffer = newbuf;\n\t\t*buffermax = newmax;\n\t}\n\tmemcpy(*buffer + *bufferlen, newdata, newlen);\n\t*bufferlen += newlen;\n\t/*no null terminator, remember to cat one if required*/\n}\n/* *buffer, *bufferlen and *buffermax should be NULL/0 at the start */\nstatic void QCC_PR_ExpandStrCatMarkup(char **buffer, size_t *bufferlen, size_t *buffermax,   char *newdata, size_t newlen)\n{\n\tsize_t newmax = *bufferlen + newlen*2;\n\n\tif (newmax < *bufferlen)//check for overflow\n\t{\n\t\tQCC_PR_ParseWarning(ERR_INTERNAL, \"exceeds 4gb\");\n\t\treturn;\n\t}\n\tif (newmax > *buffermax)\n\t{\n\t\tchar *newbuf;\n\t\tif (newmax < 64)\n\t\t\tnewmax = 64;\n\t\tif (newmax < *bufferlen * 2)\n\t\t{\n\t\t\tnewmax = *bufferlen * 2;\n\t\t\tif (newmax < *bufferlen) /*overflowed?*/\n\t\t\t{\n\t\t\t\tQCC_PR_ParseWarning(ERR_INTERNAL, \"exceeds 4gb\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tnewbuf = realloc(*buffer, newmax);\n\t\tif (!newbuf)\n\t\t{\n\t\t\tQCC_PR_ParseWarning(ERR_INTERNAL, \"out of memory\");\n\t\t\treturn; /*OOM*/\n\t\t}\n\t\t*buffer = newbuf;\n\t\t*buffermax = newmax;\n\t}\n\n\twhile (newlen--)\n\t{\n\t\tif (*newdata == '\\n')\n\t\t{\n\t\t\t(*buffer)[*bufferlen+0] = '\\\\';\n\t\t\t(*buffer)[*bufferlen+1] = '\\n';\n\t\t\t*bufferlen += 2;\n\t\t}\n\t\telse if (*newdata == '\\\\')\n\t\t{\n\t\t\t(*buffer)[*bufferlen+0] = '\\\\';\n\t\t\t(*buffer)[*bufferlen+1] = '\\\\';\n\t\t\t*bufferlen += 2;\n\t\t}\n\t\telse if (*newdata == '\\0')\n\t\t{\n\t\t\t(*buffer)[*bufferlen+0] = '\\\\';\n\t\t\t(*buffer)[*bufferlen+1] = '0';\n\t\t\t*bufferlen += 2;\n\t\t}\n\t\telse if (*newdata == '\\\"')\n\t\t{\n\t\t\t(*buffer)[*bufferlen+0] = '\\\\';\n\t\t\t(*buffer)[*bufferlen+1] = '\\\"';\n\t\t\t*bufferlen += 2;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t(*buffer)[*bufferlen] = *newdata;\n\t\t\t*bufferlen += 1;\n\t\t}\n\t\tnewdata++;\n\t}\n\t/*no null terminator, remember to cat one if required*/\n}\n\nstatic const struct tm *QCC_CurrentTime(void)\n{\n\t//if SOURCE_DATE_EPOCH environment is defined, use that as seconds from epoch (and show utc)\n\t//this helps give reproducable builds (which is for some debian project, demonstrating that noone is hacking binaries).\n\tconst char *env = getenv(\"SOURCE_DATE_EPOCH\");\n\ttime_t t;\n\tif (env && *env)\n\t{\n\t\tt = strtoull(env, NULL, 0);\n\t\tif (t)\n\t\t\treturn gmtime(&t);\n\t}\n\n\tt = time(NULL);\n\treturn localtime(&t);\n}\n\n#if _POSIX_C_SOURCE >= 2 || defined(_WIN32)\n#define HAVE_POPEN\n#endif\n#ifdef HAVE_POPEN\nstatic char *QCC_PR_PopenMacro(const char *macroname, const char *cmd, char *retbuf, size_t retbufsize)\n{\n\tchar *ret = retbuf;\n\tchar temp[65536], *t = temp;\n#ifdef _WIN32\n\tFILE *f = _popen(cmd, \"rt\");\n#else\n\tFILE *f = popen(cmd, \"r\");\n#endif\n\tint len;\n\tif (!f)\n\t{\n\t\tQCC_PR_ParseWarning(ERR_CONSTANTNOTDEFINED, \"%s: Unable to execute \\\"%s\\\" for value\", macroname, cmd);\n\t\treturn NULL;\n\t}\n\tretbufsize-=3;\t// '\"\"\\0'\n\t*retbuf++ = '\\\"';\n\tfor (;;)\n\t{\n\t\tlen = fread(temp, 1, sizeof(temp), f);\n\t\tif (len <= 0)\n\t\t\tbreak;\n\t\telse for (t=temp; len --> 0 && *t && retbufsize > 1; t++)\n\t\t{\n\t\t\tif      (*t == '\\\"')\tretbuf[1] = '\\\"';\n\t\t\telse if (*t == '\\n')\tretbuf[1] = 'n';\n\t\t\telse if (*t == '\\r')\tretbuf[1] = 'r';\n\t\t\telse if (*t == '#')\t\tretbuf[1] = '#';\t//so we don't get preqcc-style expansion in strings.\n\t\t\telse\n\t\t\t{\n\t\t\t\t*retbuf++ = *t;\n\t\t\t\tretbufsize--;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tretbuf[0] = '\\\\';\n\t\t\tretbuf+=2;\n\t\t\tretbufsize-=2;\n\t\t}\n\t}\n\tif (retbuf[-1] == 'n' && retbuf[-2] == '\\\\')\n\t\tretbuf -= 2;\n\tif (retbuf[-1] == 'r' && retbuf[-2] == '\\\\')\n\t\tretbuf -= 2;\n\t*retbuf++ = '\\\"';\n\t*retbuf++ = 0;\n#ifdef _WIN32\n\t_pclose(f);\n#else\n\tpclose(f);\n#endif\n\treturn ret;\n}\n#endif\n\nstatic char *QCC_PR_CheckBuiltinCompConst(char *constname, char *retbuf, size_t retbufsize)\n{\n\tif (constname[0] != '_' || constname[1] != '_')\n\t\treturn NULL;\n\tif (!strcmp(constname, \"__TIME__\"))\n\t{\n\t\tstrftime( retbuf, retbufsize,\t\"\\\"%H:%M\\\"\", QCC_CurrentTime());\n\t\treturn retbuf;\n\t}\n\tif (!strcmp(constname, \"__DATE__\"))\n\t{\n\t\tstrftime( retbuf, retbufsize,\t\"\\\"%a %d %b %Y\\\"\", QCC_CurrentTime());\n\t\treturn retbuf;\n\t}\n#ifdef HAVE_POPEN\n\tif (!strcmp(constname, \"__GITURL__\"))\n\t\treturn QCC_PR_PopenMacro(constname, \"git remote get-url origin\", retbuf, retbufsize);\t//some git url...\n\tif (!strcmp(constname, \"__GITHASH__\"))\n\t\treturn QCC_PR_PopenMacro(constname, \"git log -1 --format=%H\", retbuf, retbufsize);\t//just a hash\n\tif (!strcmp(constname, \"__GITDATE__\"))\n\t\treturn QCC_PR_PopenMacro(constname, \"git log -1 --format=%cs\", retbuf, retbufsize);\t//YYYY-MM-DD\n\tif (!strcmp(constname, \"__GITDATETIME__\"))\n\t\treturn QCC_PR_PopenMacro(constname, \"git log -1 --format=%ci\", retbuf, retbufsize);\t//YYYY-MM-DD HH:MM:SS +TZ\n\tif (!strcmp(constname, \"__GITDESC__\"))\n\t\treturn QCC_PR_PopenMacro(constname, \"git describe\", retbuf, retbufsize);\n#endif\n\tif (!strcmp(constname, \"__RAND__\"))\n\t{\n\t\tQC_snprintfz(retbuf, retbufsize, \"%i\", rand());\n\t\treturn retbuf;\n\t}\n#if defined(SVNREVISION) && defined(SVNDATE)\n\tif (!strcmp(constname, \"__QCCREV__\"))\n\t\treturn STRINGIFY(SVNREVISION); //no M or anything, so you can compare revisions properly\n#endif\n\tif (!strcmp(constname, \"__QCCVER__\"))\n\t{\n#if defined(SVNREVISION) && defined(SVNDATE)\n\t\treturn \"\\\"FTEQCC \" STRINGIFY(SVNREVISION) \" \"STRINGIFY(SVNDATE)\"\\\"\";\n#elif defined(SVNREVISION)\n\t\treturn \"\\\"FTEQCC \" STRINGIFY(SVNREVISION) \" \"__DATE__\"\\\"\";\n#else\n\t\treturn \"\\\"\"__DATE__\"\\\"\";\n#endif\n\t}\n\tif (!strcmp(constname, \"__FILE__\"))\n\t{\n\t\tchar *erk;\n\t\tQC_snprintfz(retbuf, retbufsize, \"\\\"%s\\\"\", s_filen);\n\t\terk = strchr(retbuf, ':');\n\t\tif (erk)\n\t\t{\n\t\t\terk[0] = '\\\"';\n\t\t\terk[1] = 0;\n\t\t}\n\t\treturn retbuf;\n\t}\n\tif (!strcmp(constname, \"__LINE__\"))\n\t{\n\t\tint line = pr_source_line;\n\t\tif (currentchunk && currentchunk->cnst)\t//if we're in a macro, use the line the macro was on.\n\t\t\tline = currentchunk->currentlinenumber;\n\t\tQC_snprintfz(retbuf, retbufsize, \"%i\", line);\n\t\treturn retbuf;\n\t}\n\tif (!strcmp(constname, \"__LINESTR__\"))\n\t{\n\t\tint line = pr_source_line;\n\t\tif (currentchunk && currentchunk->cnst)\t//if we're in a macro, use the line the macro was on.\n\t\t\tline = currentchunk->currentlinenumber;\n\t\tQC_snprintfz(retbuf, retbufsize, \"\\\"%i\\\"\", line);\n\t\treturn retbuf;\n\t}\n\tif (!strcmp(constname, \"__FUNC__\") || !strcmp(constname, \"__func__\"))\n\t{\n\t\tQC_snprintfz(retbuf, retbufsize, \"\\\"%s\\\"\",pr_scope?pr_scope->name:\"<NO FUNCTION>\");\n\t\treturn retbuf;\n\t}\n\tif (!strcmp(constname, \"__NULL__\"))\n\t{\n\t\treturn \"0i\";\n\t}\n\treturn NULL;\t//didn't match\n}\n\nstatic pbool QCC_PR_ExpandPreProcessorMacro(CompilerConstant_t *c, char **buffer, size_t *bufferlen, size_t *buffermax)\n{\n\tint p;\n\tchar *start;\n\tchar *starttok;\n\tchar *argsend;\n\tint argsendline;\n\tsize_t whitestart;\n\tchar *paramoffset[MAXCONSTANTPARAMS+1];\n\tint param=0, extraparam=0;\n\tint plevel=0;\n\tpbool noargexpand;\n\n\tchar *end;\n\tchar retbuf[256];\n\n\tpr_file_p++;\n\tQCC_PR_LexWhitespace(false);\n\tstart = pr_file_p;\n\twhile(1)\n\t{\n\t\t// handle strings correctly by ignoring them\n\t\tif (*pr_file_p == '\\\"')\n\t\t{\n\t\t\tdo {\n\t\t\t\tpr_file_p++;\n\t\t\t} while( (pr_file_p[-1] == '\\\\' || pr_file_p[0] != '\\\"') && *pr_file_p && *pr_file_p != '\\n' );\n\t\t}\n\t\tif (*pr_file_p == '(')\n\t\t\tplevel++;\n\t\telse if (!plevel && (*pr_file_p == ',' || *pr_file_p == ')'))\n\t\t{\n\t\t\tif (*pr_file_p == ',' && c->varg && param >= c->numparams)\n\t\t\t\textraparam++;\t//skip extra trailing , arguments if we're varging.\n\t\t\telse\n\t\t\t{\n\t\t\t\tparamoffset[param++] = start;\n\t\t\t\tstart = pr_file_p+1;\n\t\t\t\tif (*pr_file_p == ')')\n\t\t\t\t{\n\t\t\t\t\t*pr_file_p = '\\0';\n\t\t\t\t\tpr_file_p++;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\t*pr_file_p = '\\0';\n\t\t\t\tpr_file_p++;\n\t\t\t\tQCC_PR_LexWhitespace(false);\n\t\t\t\tstart = pr_file_p;\n\t\t\t\t// move back by one char because we move forward by one at the end of the loop\n\t\t\t\tpr_file_p--;\n\t\t\t\tif (param == MAXCONSTANTPARAMS || param > c->numparams)\n\t\t\t\t\tQCC_PR_ParseError(ERR_TOOMANYPARAMS, \"Too many parameters in macro call\");\n\t\t\t}\n\t\t} else if (*pr_file_p == ')' )\n\t\t\tplevel--;\n\t\telse if(*pr_file_p == '\\n')\n\t\t\tQCC_PR_NewLine(false);\n\n\t\t// see that *pr_file_p = '\\0' up there? Must ++ BEFORE checking for !*pr_file_p\n\t\tpr_file_p++;\n\t\tif (!*pr_file_p)\n\t\t\tQCC_PR_ParseError(ERR_EOF, \"EOF on macro call\");\n\t}\n\tif (param < c->numparams)\n\t\tQCC_PR_ParseError(ERR_TOOFEWPARAMS, \"Not enough macro parameters\");\n\tparamoffset[param] = start;\n\n\t*buffer = NULL;\n\t*bufferlen = 0;\n\t*buffermax = 0;\n\n//\t\t\t\tQCC_PR_LexWhitespace(false);\n\targsend = pr_file_p;\n\targsendline = pr_source_line;\n\tpr_file_p = c->value;\n\tfor(;;)\n\t{\n\t\tnoargexpand = false;\n\t\twhitestart = *bufferlen;\n\t\tstarttok = pr_file_p;\n\t\t/*while(qcc_iswhite(*pr_file_p))\t//copy across whitespace\n\t\t{\n\t\t\tif (!*pr_file_p)\n\t\t\t\tbreak;\n\t\t\tpr_file_p++;\n\t\t}*/\n\t\tQCC_PR_LexWhitespace(true);\n\t\tif (starttok != pr_file_p)\n\t\t{\n\t\t\tQCC_PR_ExpandStrCat(buffer,bufferlen,buffermax,   starttok, pr_file_p - starttok);\n\t\t}\n\n\t\tif(*pr_file_p == '\\\"')\n\t\t{\n\t\t\tstarttok = pr_file_p;\n\t\t\tdo\n\t\t\t{\n\t\t\t\tpr_file_p++;\n\t\t\t} while( (pr_file_p[-1] == '\\\\' || pr_file_p[0] != '\\\"') && *pr_file_p && *pr_file_p != '\\n' );\n\t\t\tif(*pr_file_p == '\\\"')\n\t\t\t\tpr_file_p++;\n\n\t\t\tQCC_PR_ExpandStrCat(buffer,bufferlen,buffermax,   starttok, pr_file_p - starttok);\n\t\t\tcontinue;\n\t\t}\n\t\telse if (*pr_file_p == '#')\t//if you ask for #a##b you will be shot. use #a #b instead, or chain macros.\n\t\t{\n\t\t\tif (pr_file_p[1] == '#')\n\t\t\t{\t//concatinate (strip out whitespace before the token)\n\t\t\t\t*bufferlen = whitestart;\n\t\t\t\tpr_file_p+=2;\n\t\t\t\tnoargexpand = true;\n\t\t\t}\n\t\t\telse\n\t\t\t{\t//stringify\n\t\t\t\tpr_file_p++;\n\t\t\t\tpr_file_p = QCC_COM_Parse2(pr_file_p);\n\t\t\t\tif (!pr_file_p)\n\t\t\t\t\tbreak;\n\n\t\t\t\tfor (p = 0; p < param; p++)\n\t\t\t\t{\n\t\t\t\t\tif (!STRCMP(qcc_token, c->params[p]))\n\t\t\t\t\t{\n\t\t\t\t\t\tQCC_PR_ExpandStrCat(buffer,bufferlen,buffermax,   \"\\\"\", 1);\n\t\t\t\t\t\tQCC_PR_ExpandStrCatMarkup(buffer,bufferlen,buffermax,   paramoffset[p], strlen(paramoffset[p]));\n\t\t\t\t\t\tQCC_PR_ExpandStrCat(buffer,bufferlen,buffermax,   \"\\\"\", 1);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (p == param)\n\t\t\t\t{\n\t\t\t\t\tQCC_PR_ExpandStrCat(buffer,bufferlen,buffermax,   \"#\", 1);\n\t\t\t\t\tQCC_PR_ExpandStrCat(buffer,bufferlen,buffermax,   qcc_token, strlen(qcc_token));\n\t\t\t\t\tif (!c->evil)\n\t\t\t\t\t\tQCC_PR_ParseWarning(0, \"'#%s' is not a macro parameter in %s\", qcc_token, c->name);\n\t\t\t\t}\n\t\t\t\tcontinue;\t//already did this one\n\t\t\t}\n\t\t}\n\n\t\tend = qcc_token;\n\t\tpr_file_p = QCC_COM_Parse2(pr_file_p);\n\t\tif (!pr_file_p)\n\t\t\tbreak;\n\n\t\tfor (p = 0; p < c->numparams; p++)\n\t\t{\n\t\t\tif (!STRCMP(qcc_token, c->params[p]))\n\t\t\t{\n\t\t\t\tchar *argstart, *argend;\n\n\t\t\t\tfor (start = pr_file_p; qcc_iswhite(*start); start++)\n\t\t\t\t\t;\n\t\t\t\tif (noargexpand || (start[0] == '#' && start[1] == '#'))\n\t\t\t\t\tQCC_PR_ExpandStrCat(buffer,bufferlen,buffermax,   paramoffset[p], strlen(paramoffset[p]));\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tfor (argstart = paramoffset[p]; *argstart; argstart = argend)\n\t\t\t\t\t{\n\t\t\t\t\t\targend = argstart;\n\t\t\t\t\t\twhile (qcc_iswhite(*argend))\n\t\t\t\t\t\t\targend++;\n\t\t\t\t\t\tif (*argend == '\\\"')\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tdo\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\targend++;\n\t\t\t\t\t\t\t} while( (argend[-1] == '\\\\' || argend[0] != '\\\"') && *argend && *argend != '\\n' );\n\t\t\t\t\t\t\tif(*argend == '\\\"')\n\t\t\t\t\t\t\t\targend++;\n\t\t\t\t\t\t\tend = NULL;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\targend = QCC_COM_Parse2(argend);\n\t\t\t\t\t\t\tif (!argend)\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tend = QCC_PR_CheckBuiltinCompConst(qcc_token, retbuf, sizeof(retbuf));\n\t\t\t\t\t\t}\n\t\t\t\t\t\t//FIXME: we should be testing all defines instead of just built-in ones.\n\t\t\t\t\t\tif (end)\n\t\t\t\t\t\t\tQCC_PR_ExpandStrCat(buffer,bufferlen,buffermax,   end, strlen(end));\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tQCC_PR_ExpandStrCat(buffer,bufferlen,buffermax,   argstart, argend-argstart);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (c->varg && !STRCMP(qcc_token, \"__VA_ARGS__\"))\n\t\t{\t//c99\n\t\t\tif (param-1 == c->numparams)\n\t\t\t\tQCC_PR_ExpandStrCat(buffer,bufferlen,buffermax,   paramoffset[c->numparams], strlen(paramoffset[c->numparams]));\n\t\t\telse if (noargexpand)\n\t\t\t{\n\t\t\t\tif(*bufferlen>0 && (*buffer)[*bufferlen-1] == ',')\n\t\t\t\t\t*bufferlen-=1;\n\t\t\t}\n\t\t}\n\t\telse if (c->varg && !STRCMP(qcc_token, \"__VA_COUNT__\"))\n\t\t{\t//not c99\n\t\t\tchar tmp[64];\n\t\t\tif (param < c->numparams)\n\t\t\t\tQCC_PR_ParseError(ERR_TOOFEWPARAMS, \"__VA_COUNT__ without any variable args\");\n\t\t\tQC_snprintfz(tmp, sizeof(tmp), \"%i\", param-1+extraparam);\n\t\t\tQCC_PR_ExpandStrCat(buffer,bufferlen,buffermax,   tmp, strlen(tmp));\n\t\t}\n\t\telse if (p == c->numparams)\n\t\t{\n\t\t\t/*CompilerConstant_t *c2 = pHash_Get(&compconstantstable, qcc_token);\n\t\t\tif (c2 && c2->numparams >= 0 && *pr_file_p == '(')\n\t\t\t{\t//oh dear god this is vile bullshit\n\t\t\t\tchar *sub = NULL;\n\t\t\t\tsize_t sublen;\n\t\t\t\tsize_t submax;\n\t\t\t\tpr_file_p++;\n\t\t\t\tif (QCC_PR_ExpandPreProcessorMacro(c, &sub,&sublen,&submax))\n\t\t\t\t{\n\t\t\t\t\tQCC_PR_ExpandStrCat(buffer,bufferlen,buffermax,   sub, sublen);\n\t\t\t\t}\n\t\t\t\tfree(sub);\n\t\t\t}\n\t\t\telse*/\n\t\t\t\tQCC_PR_ExpandStrCat(buffer,bufferlen,buffermax,   qcc_token, strlen(qcc_token));\n\t\t}\n\t}\n\n\tfor (p = 0; p < param-1; p++)\n\t\tparamoffset[p][strlen(paramoffset[p])] = ',';\n\tparamoffset[p][strlen(paramoffset[p])] = ')';\n\n\tif (c->inside>8)\n\t\treturn false;\n\n\tpr_file_p = argsend;\n\tpr_source_line = argsendline;\n\n\tif (flag_debugmacros)\n\t{\n\t\tif (flag_msvcstyle)\n\t\t\texterns->Printf (\"%s(%i) : macro %s: %s\\n\", s_filen, pr_source_line, c->name, pr_file_p);\n\t\telse\n\t\t\texterns->Printf (\"%s:%i: macro %s: %s\\n\", s_filen, pr_source_line, c->name, pr_file_p);\n\t}\n\treturn true;\n}\n\nint QCC_PR_CheckCompConst(void)\n{\n\t//FIXME: FOO(SPLAT(FOO(BAR)))\n\t//the above should expand the inner foo before expanding the outer foo\n\n\tchar\t\t*initial_file_p = pr_file_p;\n\tint\t\t\tinitial_line = pr_source_line;\n\n\tCompilerConstant_t *c;\n\n\tchar *end, *tok;\n\tchar retbuf[256];\n\n\tfor (end = pr_file_p; ; end++)\n\t{\n\t\tif (!*end || qcc_iswhite(*end))\n\t\t\tbreak;\n\n\t\tif (*end == ')'\n\t\t\t||\t*end == '('\n\t\t\t||\t*end == '+'\n\t\t\t||\t*end == '-'\n\t\t\t||\t*end == '*'\n\t\t\t||\t*end == '/'\n\t\t\t||\t*end == '|'\n\t\t\t||\t*end == '&'\n\t\t\t||\t*end == '='\n\t\t\t||\t*end == '^'\n\t\t\t||\t*end == '~'\n\t\t\t||\t*end == '['\n\t\t\t||\t*end == ']'\n\t\t\t||\t*end == '\\\"'\n\t\t\t||\t*end == '{'\n\t\t\t||\t*end == '}'\n\t\t\t||\t*end == ';'\n\t\t\t||\t*end == ':'\n\t\t\t||\t*end == '<'\n\t\t\t||\t*end == '>'\n\t\t\t||\t*end == ','\n\t\t\t||\t*end == '.'\n\t\t\t||\t*end == '#')\n\t\t\t\tbreak;\n\t}\n\tif (!QC_strnlcpy(pr_token, pr_file_p, end-pr_file_p, sizeof(pr_token)))\n\t\treturn false;\t//name too long to be a valid macro\n\n//\texterns->Printf(\"%s\\n\", pr_token);\n\tc = pHash_Get(&compconstantstable, pr_token);\n\n\tif (c && (!currentchunk || currentchunk->cnst != c))\t//macros don't expand themselves\n\t{\n\t\tpr_file_p = initial_file_p+strlen(c->name);\n\t\twhile(*pr_file_p == ' ' || *pr_file_p == '\\t')\n\t\t\tpr_file_p++;\n\t\tif (c->numparams>=0)\n\t\t{\n\t\t\tif (*pr_file_p == '(')\n\t\t\t{\n\t\t\t\tchar *buffer = NULL;\n\t\t\t\tsize_t bufferlen, buffermax;\n\t\t\t\tif (!QCC_PR_ExpandPreProcessorMacro(c, &buffer, &bufferlen, &buffermax))\n\t\t\t\t{\n\t\t\t\t\tfree(buffer);\n\t\t\t\t\tpr_file_p = initial_file_p;\n\t\t\t\t\tpr_source_line = initial_line;\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\n\t\t\t\tif (!bufferlen)\n\t\t\t\t\texpandedemptymacro = true;\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tQCC_PR_ExpandStrCat(&buffer, &bufferlen, &buffermax,   \"\\0\", 1);\n\t\t\t\t\tQCC_PR_IncludeChunkEx(buffer, true, NULL, c);\n\t\t\t\t}\n\t\t\t\texpandedemptymacro = true;\n\t\t\t\tfree(buffer);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t//QCC_PR_ParseError(ERR_TOOFEWPARAMS, \"Macro without argument list\");\n\t\t\t\t//such macros don't get expanded.\n\t\t\t\tpr_file_p = initial_file_p;\n\t\t\t\tpr_source_line = initial_line;\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (c->inside >= 8)\n\t\t\t{\n\t\t\t\tpr_file_p = initial_file_p;\n\t\t\t\tpr_source_line = initial_line;\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tif (*c->value)\n\t\t\t\tQCC_PR_IncludeChunkEx(c->value, false, NULL, c);\n\t\t\texpandedemptymacro = true;\n\t\t}\n\t\treturn true;\n\t}\n\n\ttok = QCC_PR_CheckBuiltinCompConst(pr_token, retbuf, sizeof(retbuf));\n\tif (tok)\n\t{\n\t\tpr_file_p = end;\n\t\tQCC_PR_IncludeChunkEx(tok, true, NULL, NULL);\n\t\treturn true;\n\t}\n\treturn false;\n}\n\nconst char *QCC_PR_CheckCompConstString(const char *def)\n{\n\tconst char *s;\n\n\tCompilerConstant_t *c;\n\n\tc = pHash_Get(&compconstantstable, def);\n\n\tif (c)\n\t{\n\t\ts = QCC_PR_CheckCompConstString(c->value);\n\t\treturn s;\n\t}\n\treturn def;\n}\n\nCompilerConstant_t *QCC_PR_CheckCompConstDefined(const char *def)\n{\n\tCompilerConstant_t *c = pHash_Get(&compconstantstable, def);\n\treturn c;\n}\n\nchar *QCC_PR_CheckCompConstTooltip(char *word, char *outstart, char *outend)\n{\n\tint i;\n\tCompilerConstant_t *c = QCC_PR_CheckCompConstDefined(word);\n\tif (c)\n\t{\n\t\tchar *out = outstart;\n\t\tif (c->numparams >= 0)\n\t\t{\n\t\t\tQC_snprintfz(out, outend-out, \"#define %s(\", c->name);\n\t\t\tout += strlen(out);\n\t\t\tfor (i = 0; i < c->numparams-1; i++)\n\t\t\t{\n\t\t\t\tQC_snprintfz(out, outend-out, \"%s,\", c->params[i]);\n\t\t\t\tout += strlen(out);\n\t\t\t}\n\t\t\tif (i < c->numparams)\n\t\t\t{\n\t\t\t\tQC_snprintfz(out, outend-out, \"%s\", c->params[i]);\n\t\t\t\tout += strlen(out);\n\t\t\t}\n\t\t\tQC_snprintfz(out, outend-out, \")\");\n\t\t}\n\t\telse\n\t\t\tQC_snprintfz(out, outend-out, \"#define %s\", c->name);\n\t\tout += strlen(out);\n\t\tif (c->value && *c->value)\n\t\t\tQC_snprintfz(out, outend-out, \"\\n%s\", c->value);\n\n\t\treturn outstart;\n\t}\n\treturn NULL;\n}\n\n//============================================================================\n\n/*\n==============\nPR_Lex\n\nSets pr_token, pr_token_type, and possibly pr_immediate and pr_immediate_type\n==============\n*/\nvoid QCC_PR_Lex (void)\n{\n\tint\t\tc;\n\n\tpr_token[0] = 0;\n\n\tif (!pr_file_p)\n\t{\n\t\tif (QCC_PR_UnInclude())\n\t\t{\n\t\t\tQCC_PR_Lex();\n\t\t\treturn;\n\t\t}\n\t\tpr_token_type = tt_eof;\n\t\treturn;\n\t}\n\n\tpr_token_precomment = NULL;\n\tQCC_PR_LexComment(&pr_token_precomment);\n//\tQCC_PR_LexWhitespace (false);\n\n\tpr_token_line_last = pr_token_line;\n\tpr_token_line = pr_source_line;\n\tif (currentchunk)\n\t\tpr_token_line += currentchunk->currentlinenumber-1;\n\n\tif (!pr_file_p)\n\t{\n\t\tif (QCC_PR_UnInclude())\n\t\t{\n\t\t\tQCC_PR_Lex();\n\t\t\treturn;\n\t\t}\n\t\tpr_token_type = tt_eof;\n\t\treturn;\n\t}\n\n\tc = *pr_file_p;\n\n\tif (!c)\n\t{\n\t\tif (QCC_PR_UnInclude())\n\t\t{\n\t\t\tQCC_PR_Lex();\n\t\t\treturn;\n\t\t}\n\t\tpr_token_type = tt_eof;\n\t\treturn;\n\t}\n\n// handle quoted strings as a unit\n\tif (c == '\\\"' || ((c == 'R' || c == 'Q' || c == 'u' || c == 'U') && pr_file_p[1] == '\\\"') || (c == 'u' && pr_file_p[1] == '8' && pr_file_p[2] == '\\\"'))\n\t{\n\t\tQCC_PR_LexString ();\n\t\treturn;\n\t}\n\n// handle quoted vectors as a unit\n\tif (c == '\\'')\n\t{\n\t\tQCC_PR_LexVector ();\n\t\treturn;\n\t}\n\n// if the first character is a valid identifier, parse until a non-id\n// character is reached\n\tif ((c == '%') && flag_qccx && (pr_file_p[1] == '-' || (pr_file_p[1] >= '0' && pr_file_p[1] <= '9')))\n\t{\n\t\t//with qccx, %5 is a denormalized float.\n\t\tpr_file_p++;\n\t\tpr_token_type = tt_immediate;\n\t\tpr_immediate_type = type_float;\n\t\tQCC_PR_ParseWarning(WARN_DENORMAL, \"denormalized immediate\");\n\t\tpr_immediate._int = QCC_PR_LexInteger ();\n\t\treturn;\n\t}\n\tif ( c == '0' && pr_file_p[1] == 'x')\n\t{\n\t\tpr_token_type = tt_immediate;\n\t\tQCC_PR_LexNumber();\n\t\treturn;\n\t}\n\tif ( (c == '.'&&pr_file_p[1]!='.'&&pr_file_p[1] >='0' && pr_file_p[1] <= '9') || (c >= '0' && c <= '9') || ( c=='-' && pr_file_p[1]>='0' && pr_file_p[1] <='9') )\n\t{\n\t\tpr_token_type = tt_immediate;\n\t\tQCC_PR_LexNumber ();\n\t\treturn;\n\t}\n\n\tif (/*!flag_qccx &&*/ c == '#' && !(pr_file_p[1]==')' || pr_file_p[1]==',' || pr_file_p[1]=='\\\"' || pr_file_p[1]=='-' || (pr_file_p[1]>='0' && pr_file_p[1] <='9')))\t//hash and not number\n\t{\n\t\tpr_file_p++;\n\t\tif (!QCC_PR_CheckCompConst())\n\t\t{\n\t\t\tif (!QCC_PR_SimpleGetToken())\n\t\t\t\tstrcpy(pr_token, \"unknown\");\n\t\t\tQCC_PR_ParseError(ERR_CONSTANTNOTDEFINED, \"Explicit precompiler usage when not defined %s\", pr_token);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tQCC_PR_Lex();\n\t\t\tif (pr_token_type == tt_eof)\n\t\t\t\tQCC_PR_Lex();\n\t\t}\n\n\t\treturn;\n\t}\n\n\tif ( (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' || (c & 0x80))\n\t{\n\t\tif (flag_hashonly || !QCC_PR_CheckCompConst())\t//look for a macro.\n\t\t\tQCC_PR_LexName ();\n\t\telse\n\t\t{\n\t\t\t//we expanded a macro. we need to read the tokens out of it now though\n\t\t\tQCC_PR_Lex();\n\t\t\tif (pr_token_type == tt_eof)\n\t\t\t{\n\t\t\t\tif (QCC_PR_UnInclude())\n\t\t\t\t{\n\t\t\t\t\tQCC_PR_Lex();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tpr_token_type = tt_eof;\n\t\t\t}\n\t\t}\n\t\treturn;\n\t}\n\n\tif (c == '$')\n\t{\n\t\tQCC_PR_LexGrab ();\n\t\treturn;\n\t}\n\n// parse symbol strings until a non-symbol is found\n\tQCC_PR_LexPunctuation ();\n}\n\n//=============================================================================\n\npbool QCC_Temp_Describe(QCC_def_t *def, char *buffer, int buffersize);\nvoid QCC_PR_ParsePrintDef (int type, QCC_def_t *def)\n{\n\tif (!qccwarningaction[type])\n\t\treturn;\n\tif (def->filen)\n\t{\n\t\tchar tybuffer[512];\n\t\tchar tmbuffer[512];\n\t\tchar vlbuffer[512];\n\t\tchar *modifiers;\n\t\tif (QCC_Temp_Describe(def, tmbuffer, sizeof(tmbuffer)))\n\t\t{\n\t\t\texterns->Printf (\"%s:%i:    (%s)(%s)\\n\", def->filen, def->s_line, TypeName(def->type, tybuffer, sizeof(tybuffer)), tmbuffer);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tmodifiers = \"\";\n\t\t\tif (def->constant)\n\t\t\t\tmodifiers = \"const \";\n\t\t\telse if (def->isstatic)\n\t\t\t\tmodifiers = \"static \";\n\n\n\t\t\tif (def && def->initialized && def->constant && !def->arraysize && def->symboldata)\n\t\t\t{\n\t\t\t\tconst QCC_eval_t *ev = (const QCC_eval_t*)&def->symboldata[0];\n\t\t\t\tswitch(def->type->type)\n\t\t\t\t{\n\t\t\t\tcase ev_float:\tQC_snprintfz(vlbuffer, sizeof(vlbuffer), \" = %g\", ev->_float);\tbreak;\n\t\t\t\tcase ev_double:\tQC_snprintfz(vlbuffer, sizeof(vlbuffer), \" = %g\", ev->_double);\tbreak;\n\t\t\t\tcase ev_integer:QC_snprintfz(vlbuffer, sizeof(vlbuffer), \" = %i\", ev->_int);\tbreak;\n\t\t\t\tcase ev_uint:\tQC_snprintfz(vlbuffer, sizeof(vlbuffer), \" = %u\", ev->_uint);\tbreak;\n\t\t\t\tcase ev_int64:\tQC_snprintfz(vlbuffer, sizeof(vlbuffer), \" = %\"pPRIi64, ev->i64);\tbreak;\n\t\t\t\tcase ev_uint64:\tQC_snprintfz(vlbuffer, sizeof(vlbuffer), \" = %\"pPRIu64, ev->u64);\tbreak;\n\t\t\t\tdefault:\t\t*vlbuffer = 0;\t\t\t\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t\t*vlbuffer = 0;\n\t\t\tif (flag_msvcstyle)\n\t\t\t\texterns->Printf (\"%s%s(%i) :    %s%s%s %s%s%s%s%s is defined here\\n\", col_location, def->filen, def->s_line, col_type, modifiers, TypeName(def->type, tybuffer, sizeof(tybuffer)), col_symbol, def->name, col_type, vlbuffer, col_none);\n\t\t\telse\n\t\t\t\texterns->Printf (\"%s%s:%i:    %s%s%s %s%s%s%s%s is defined here\\n\", col_location, def->filen, def->s_line, col_type, modifiers, TypeName(def->type, tybuffer, sizeof(tybuffer)), col_symbol, def->name, col_type, vlbuffer, col_none);\n\t\t}\n\t}\n}\nvoid QCC_PR_ParsePrintSRef (int type, QCC_sref_t def)\n{\n\tQCC_PR_ParsePrintDef(type, def.sym);\n}\n\nvoid *errorscope;\nstatic void QCC_PR_PrintMacro (qcc_includechunk_t *chunk)\n{\n\tif (chunk)\n\t{\n\t\tQCC_PR_PrintMacro(chunk->prev);\n\t\tif (chunk->cnst)\n\t\t{\n#if 1\n\t\t\texterns->Printf (\"%s%s:%i: macro %s%s%s is defined here\\n\", col_location, chunk->cnst->fromfile, chunk->cnst->fromline, col_symbol, chunk->cnst->name, col_none);\n#else\n\t\t\texterns->Printf (\"%s:%i: expanding %s\\n\", chunk->currentfilename, chunk->currentlinenumber, chunk->cnst->name);\n#endif\n\t\t\tif (verbose >= VERBOSE_STANDARD)\n\t\t\t\texterns->Printf (\"%s\\n\", chunk->datastart);\n\t\t}\n\t\telse\n\t\t\texterns->Printf (\"%s:%i:\\n\", chunk->currentfilename, chunk->currentlinenumber);\n\t}\n}\nstatic void QCC_PR_PrintScope (void)\n{\n\tQCC_PR_PrintMacro(currentchunk);\n\tif (pr_scope)\n\t{\n\t\tif (errorscope != pr_scope)\n\t\t\texterns->Printf (\"in function %s%s%s (line %i),\\n\", col_symbol, pr_scope->name, col_none, pr_scope->line);\n\t\terrorscope = pr_scope;\n\t}\n\telse\n\t{\n\t\tif (errorscope)\n\t\t\texterns->Printf (\"at global scope,\\n\");\n\t\terrorscope = NULL;\n\t}\n}\nvoid QCC_PR_ResetErrorScope(void)\n{\n\terrorscope = NULL;\n}\n/*\n============\nPR_ParseError\n\nAborts the current file load\n============\n*/\n#ifndef QCC\nvoid editbadfile(const char *file, int line);\n#endif\n//will abort.\nNORETURN void VARGS QCC_PR_ParseError (int errortype, const char *error, ...)\n{\n\tva_list\t\targptr;\n\tchar\t\tstring[1024];\n\n\tva_start (argptr,error);\n\tQC_vsnprintf (string,sizeof(string)-1, error,argptr);\n\tva_end (argptr);\n\n#ifndef QCC\n\teditbadfile(s_filen, pr_source_line);\n#endif\n\n\tif (error)\n\t{\n\t\tQCC_PR_PrintScope();\n\t\tif (flag_msvcstyle)\n\t\t\texterns->Printf (\"%s%s(%i) : %serror%s: %s\\n\", col_location, s_filen, pr_source_line, col_error, col_none, string);\n\t\telse\n\t\t\texterns->Printf (\"%s%s:%i: %serror%s: %s\\n\", col_location, s_filen, pr_source_line, col_error, col_none, string);\n\t}\n\n\tlongjmp (pr_parse_abort, 1);\n}\n//will abort.\nNORETURN void VARGS QCC_PR_ParseErrorPrintDef (int errortype, QCC_def_t *def, const char *error, ...)\n{\n\tva_list\t\targptr;\n\tchar\t\tstring[1024];\n\n\tva_start (argptr,error);\n\tQC_vsnprintf (string,sizeof(string)-1, error,argptr);\n\tva_end (argptr);\n\n#ifndef QCC\n\teditbadfile(s_filen, pr_source_line);\n#endif\n\tQCC_PR_PrintScope();\n\tif (flag_msvcstyle)\n\t\texterns->Printf (\"%s%s(%i) : %serror%s: %s\\n\", col_location, s_filen, pr_source_line, col_error, col_none, string);\n\telse\n\t\texterns->Printf (\"%s%s:%i: %serror%s: %s\\n\", col_location, s_filen, pr_source_line, col_error, col_none, string);\n\n\tQCC_PR_ParsePrintDef(WARN_ERROR, def);\n\n\tlongjmp (pr_parse_abort, 1);\n}\n\nNORETURN void VARGS QCC_PR_ParseErrorPrintSRef (int errortype, QCC_sref_t def, const char *error, ...)\n{\n\tva_list\t\targptr;\n\tchar\t\tstring[1024];\n\n\tva_start (argptr,error);\n\tQC_vsnprintf (string,sizeof(string)-1, error,argptr);\n\tva_end (argptr);\n\n#ifndef QCC\n\teditbadfile(s_filen, pr_source_line);\n#endif\n\tQCC_PR_PrintScope();\n\tif (flag_msvcstyle)\n\t\texterns->Printf (\"%s%s(%i) : %serror%s: %s\\n\", col_location, s_filen, pr_source_line, col_error, col_none, string);\n\telse\n\t\texterns->Printf (\"%s%s:%i: %serror%s: %s\\n\", col_location, s_filen, pr_source_line, col_error, col_none, string);\n\n\tQCC_PR_ParsePrintSRef(WARN_ERROR, def);\n\n\tlongjmp (pr_parse_abort, 1);\n}\n\nstatic pbool VARGS QCC_PR_PrintWarning (int type, const char *file, int line, const char *string)\n{\n\tchar *wnam = QCC_NameForWarning(type);\n\tif (!wnam)\n\t\twnam = \"\";\n\n\tif (string)\n\t\tQCC_PR_PrintScope();\n\n\tif (type >= ERR_PARSEERRORS)\n\t{\n\t\tif (!string)\n\t\t\t;\n\t\telse if (!file || !*file)\n\t\t\texterns->Printf (\":: %serror%s%s: %s\\n\", col_error, wnam, col_none, string);\n\t\telse if (flag_msvcstyle)\n\t\t\texterns->Printf (\"%s%s(%i) : %serror%s%s: %s\\n\", col_location, file, line, col_error, wnam, col_none, string);\n\t\telse\n\t\t\texterns->Printf (\"%s%s:%i: %serror%s%s: %s\\n\", col_location, file, line, col_error, wnam, col_none, string);\n\t\tpr_error_count++;\n\t}\n\telse if (qccwarningaction[type] == 2)\n\t{\t//-werror\n\t\tif (!string)\n\t\t\t;\n\t\telse if (!file || !*file)\n\t\t\texterns->Printf (\":: %swerror%s%s: %s\\n\", col_error, wnam, col_none, string);\n\t\telse if (flag_msvcstyle)\n\t\t\texterns->Printf (\"%s%s(%i) : %swerror%s%s: %s\\n\", col_location, file, line, col_error, wnam, col_none, string);\n\t\telse\n\t\t\texterns->Printf (\"%s%s:%i: %swerror%s%s: %s\\n\", col_location, file, line, col_error, wnam, col_none, string);\n\t\tpr_error_count++;\n\t}\n\telse\n\t{\n\t\tif (!string)\n\t\t\t;\n\t\telse if (!file || !*file)\n\t\t\texterns->Printf (\":: %swarning%s%s: %s\\n\", col_warning, wnam, col_none, string);\n\t\telse if (flag_msvcstyle)\n\t\t\texterns->Printf (\"%s%s(%i) : %swarning%s%s: %s\\n\", col_location, file, line, col_warning, wnam, col_none, string);\n\t\telse\n\t\t\texterns->Printf (\"%s%s:%i: %swarning%s%s: %s\\n\", col_location, file, line, col_warning, wnam, col_none, string);\n\t\tpr_warning_count++;\n\t}\n\treturn true;\n}\npbool VARGS QCC_PR_Warning (int type, const char *file, int line, const char *error, ...)\n{\n\tva_list\t\targptr;\n\tchar\t\tstring[1024];\n\n\tif (!qccwarningaction[type])\n\t\treturn false;\n\n\tif (!error)\n\t\treturn QCC_PR_PrintWarning(type, file, line, NULL);\n\n\tva_start (argptr,error);\n\tQC_vsnprintf (string,sizeof(string)-1, error,argptr);\n\tva_end (argptr);\n\n\treturn QCC_PR_PrintWarning(type, file, line, string);\n}\n\n//can be used for errors, qcc execution will continue.\npbool VARGS QCC_PR_ParseWarning (int type, const char *error, ...)\n{\n\tva_list\t\targptr;\n\tchar\t\tstring[1024];\n\n\tif (!qccwarningaction[type])\n\t\treturn false;\n\n\tva_start (argptr,error);\n\tQC_vsnprintf (string,sizeof(string)-1, error,argptr);\n\tva_end (argptr);\n\n\treturn QCC_PR_PrintWarning(type, s_filen, pr_source_line, string);\n}\n\nvoid VARGS QCC_PR_Note (int type, const char *file, int line, const char *error, ...)\n{\n\tva_list\t\targptr;\n\tchar\t\tstring[1024];\n\n\tif (!qccwarningaction[type])\n\t\treturn;\n\n\tva_start (argptr,error);\n\tQC_vsnprintf (string,sizeof(string)-1, error,argptr);\n\tva_end (argptr);\n\n\tQCC_PR_PrintScope();\n\tif (!file)\n\t\texterns->Printf (\"note: %s\\n\", string);\n\telse if (flag_msvcstyle)\n\t\texterns->Printf (\"%s(%i) : note: %s\\n\", file, line, string);\n\telse\n\t\texterns->Printf (\"%s:%i: note: %s\\n\", file, line, string);\n}\n\n\n/*\n=============\nPR_Expect\n\nIssues an error if the current token isn't equal to string\nGets the next token\n=============\n*/\n#ifndef COMMONINLINES\nvoid QCC_PR_Expect (const char *string)\n{\n\tif (STRCMP (string, pr_token))\n\t{\n\t\tif (pr_token_type == tt_immediate && pr_immediate_type == type_string)\n\t\t{\n\t\t\tif (pr_immediate_strlen > 32)\n\t\t\t\tQCC_PR_ParseError (ERR_EXPECTED, \"expected %s%s%s, found string immediate\", col_location, string, col_none);\n\t\t\telse\n\t\t\t\tQCC_PR_ParseError (ERR_EXPECTED, \"expected %s%s%s, found %s\\\"%s\\\"%s\", col_location, string, col_none, col_name, pr_token, col_none);\n\t\t}\n\t\telse if (pr_token_type == tt_eof)\n\t\t\tQCC_PR_ParseError (ERR_EXPECTED, \"expected %s%s%s, found %s%s%s\", col_location, string, col_none, col_name, \"<EOF>\", col_none);\n\t\telse\n\t\t\tQCC_PR_ParseError (ERR_EXPECTED, \"expected %s%s%s, found %s%s%s\", col_location, string, col_none, col_name, pr_token, col_none);\n\t}\n\tQCC_PR_Lex ();\n}\n#endif\n\nvoid QCC_PR_LexComment(char **comment)\n{\n\tchar c;\n\tchar *start;\n\tint nl;\n\tchar *old;\n\tint oldlen;\n\tpbool replace = true;\n\tpbool nextcomment = true;\n\n\t// skip whitespace\n\tnl = false;\n\twhile(nextcomment)\n\t{\n\t\tnextcomment = false;\n\n\t\twhile ((c = *pr_file_p) && qcc_iswhite(c))\n\t\t{\n\t\t\tif (qcc_islineending(c, pr_file_p[1]))\t//allow new lines, but only if there's whitespace before any tokens, and no double newlines.\n\t\t\t{\n\t\t\t\tif (nl)\n\t\t\t\t{\n\t\t\t\t\tpr_file_p++;\n\t\t\t\t\tQCC_PR_NewLine(false);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tnl = true;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tpr_file_p++;\n\t\t\t\tnl = false;\n\t\t\t}\n\t\t}\n\t\tif (nl)\n\t\t\tbreak;\n\n\t\t// parse // comments\n\t\tif (c=='/' && pr_file_p[1] == '/')\n\t\t{\n\t\t\tpr_file_p += 2;\n\t\t\twhile (*pr_file_p == ' ' || *pr_file_p == '\\t')\n\t\t\t\tpr_file_p++;\n\t\t\tstart = pr_file_p;\n\t\t\twhile (*pr_file_p && *pr_file_p != '\\n')\n\t\t\t\tpr_file_p++;\n\n\t\t\tif (*pr_file_p == '\\n')\n\t\t\t{\n\t\t\t\tpr_file_p++;\n\t\t\t\tQCC_PR_NewLine(false);\n\t\t\t}\n\n\t\t\told = replace?NULL:*comment;\n\t\t\treplace = false;\n\t\t\toldlen = old?strlen(old)+1:0;\n\t\t\t*comment = qccHunkAlloc(oldlen + (pr_file_p-start)+1);\n\t\t\tif (oldlen)\n\t\t\t{\n\t\t\t\tmemcpy(*comment, old, oldlen-1);\n\t\t\t\tmemcpy(*comment+oldlen-1, \"\\n\", 1);\n\t\t\t}\n\t\t\tmemcpy(*comment + oldlen, start, pr_file_p - start);\n\t\t\toldlen = oldlen+pr_file_p - start;\n\t\t\twhile(oldlen > 0 && ((*comment)[oldlen-1] == '\\r' || (*comment)[oldlen-1] == '\\n' || (*comment)[oldlen-1] == '\\t' || (*comment)[oldlen-1] == ' '))\n\t\t\t\toldlen--;\n\t\t\t(*comment)[oldlen] = 0;\n\t\t\tnextcomment = true;\t//include the next // too\n\t\t\tnl = true;\n\t\t}\n\t\t// parse /* comments\n\t\telse if (c=='/' && pr_file_p[1] == '*' && replace)\n\t\t{\n\t\t\tpr_file_p+=1;\n\t\t\tstart = pr_file_p+1;\n\n\t\t\tdo\n\t\t\t{\n\t\t\t\tpr_file_p++;\n\t\t\t\tif (pr_file_p[0]=='\\n')\n\t\t\t\t{\n\t\t\t\t\tQCC_PR_NewLine(true);\n\t\t\t\t}\n\t\t\t\telse if (pr_file_p[1] == 0)\n\t\t\t\t{\n\t\t\t\t\tQCC_PR_ParseError(0, \"EOF inside comment\\n\");\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tif (pr_file_p[0] == '/' && pr_file_p[1] == '*')\n\t\t\t\t\tQCC_PR_ParseWarning(WARN_NESTEDCOMMENT, \"\\\"/*\\\" inside comment\");\n\t\t\t} while (pr_file_p[0] != '*' || pr_file_p[1] != '/');\n\n\t\t\tif (pr_file_p[1] == 0)\n\t\t\t\tbreak;\n\n\t\t\told = replace?NULL:*comment;\n\t\t\treplace = false;\n\t\t\toldlen = old?strlen(old):0;\n\t\t\t*comment = qccHunkAlloc(oldlen + (pr_file_p-start)+1);\n\t\t\tmemcpy(*comment, old, oldlen);\n\t\t\tmemcpy(*comment + oldlen, start, pr_file_p - start);\n\t\t\t(*comment)[oldlen+pr_file_p - start] = 0;\n\n\t\t\tpr_file_p+=2;\n\t\t}\n\t}\n\n\tQCC_PR_LexWhitespace(false);\n}\n\npbool QCC_PR_CheckTokenComment(const char *string, char **comment)\n{\n\tif (pr_token_type != tt_punct)\n\t\treturn false;\n\n\tif (STRCMP (string, pr_token))\n\t\treturn false;\n\n\tif (comment)\n\t\tQCC_PR_LexComment(comment);\n\n\t//and then do the rest properly.\n\tQCC_PR_Lex ();\n\treturn true;\n}\n/*\n=============\nPR_Check\n\nReturns true and gets the next token if the current token equals string\nReturns false and does nothing otherwise\n=============\n*/\n#ifndef COMMONINLINES\npbool QCC_PR_CheckToken (const char *string)\n{\n\tif (pr_token_type != tt_punct)\n\t\treturn false;\n\n\tif (STRCMP (string, pr_token))\n\t\treturn false;\n\n\tQCC_PR_Lex ();\n\treturn true;\n}\n\npbool QCC_PR_PeekToken (const char *string)\n{\n\tif (pr_token_type != tt_punct)\n\t\treturn false;\n\tif (STRCMP (string, pr_token))\n\t\treturn false;\n\treturn true;\n}\n\npbool QCC_PR_CheckImmediate (const char *string)\n{\n\tif (pr_token_type != tt_immediate)\n\t\treturn false;\n\n\tif (STRCMP (string, pr_token))\n\t\treturn false;\n\n\tQCC_PR_Lex ();\n\treturn true;\n}\n\npbool QCC_PR_CheckName(const char *string)\n{\n\tif (pr_token_type != tt_name)\n\t\treturn false;\n\tif (flag_caseinsensitive)\n\t{\n\t\tif (stricmp (string, pr_token))\n\t\t\treturn false;\n\t}\n\telse\n\t{\n\t\tif (STRCMP(string, pr_token))\n\t\t\treturn false;\n\t}\n\tQCC_PR_Lex ();\n\treturn true;\n}\n\npbool QCC_PR_CheckKeyword(int keywordenabled, const char *string)\n{\n\tif (pr_token[0] == '_' && pr_token[1] == '_')\n\t{\n\t\t//lets just always go insensitive with a leading underscore pair.\n\t\tif (stricmp(string, pr_token+2))\n\t\t\treturn false;\n\t\tQCC_PR_Lex ();\n\t\treturn true;\n\t}\n\telse\n\t{\n\t\tif (!keywordenabled)\n\t\t\treturn false;\n\t\tif (flag_caseinsensitive)\n\t\t{\n\t\t\tif (stricmp (string, pr_token))\n\t\t\t\treturn false;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (STRCMP(string, pr_token))\n\t\t\t\treturn false;\n\t\t}\n\t}\n\tQCC_PR_Lex ();\n\treturn true;\n}\n#endif\n\n\n/*\n============\nPR_ParseName\n\nChecks to see if the current token is a valid name\n============\n*/\nchar *QCC_PR_ParseName (void)\n{\n\tchar\tident[MAX_NAME];\n\tchar *ret;\n\n\tif (pr_token_type != tt_name)\n\t{\n\t\tif (pr_token_type == tt_eof)\n\t\t\tQCC_PR_ParseError (ERR_EOF, \"unexpected EOF\");\n\t\telse if (strcmp(pr_token, \"...\"))\t//seriously? someone used '...' as an intrinsic NAME?\n\t\t\tQCC_PR_ParseError (ERR_NOTANAME, \"\\\"%s%s%s\\\" - not a name\", col_name, pr_token, col_none);\n\t}\n\tif (strlen(pr_token) >= MAX_NAME-1)\n\t\tQCC_PR_ParseError (ERR_NAMETOOLONG, \"name too long\");\n\tstrcpy (ident, pr_token);\n\tQCC_PR_Lex ();\n\n\tret = qccHunkAlloc(strlen(ident)+1);\n\tstrcpy(ret, ident);\n\treturn ret;\n//\treturn ident;\n}\n\n/*\n============\nPR_FindType\n\nReturns a preexisting complex type that matches the parm, or allocates\na new one and copies it out.\n============\n*/\n\n//requires EVERYTHING to be the same\nstatic int typecmp_strict(QCC_type_t *a, QCC_type_t *b)\n{\n\tint i;\n\tif (a == b)\n\t\treturn 0;\n\tif (!a || !b)\n\t\treturn 1;\t//different (^ and not both null)\n\n\tif (a->type != b->type)\n\t\treturn 1;\n\tif (a->num_parms != b->num_parms)\n\t\treturn 1;\n\tif (a->vargs != b->vargs)\n\t\treturn 1;\n\tif (a->vargcount != b->vargcount)\n\t\treturn 1;\n\n\tif (a->size != b->size || a->bits != b->bits || a->align != b->align)\n\t\treturn 1;\n\n\tif (a->accessors != b->accessors)\n\t\treturn 1;\n\n\tif (STRCMP(a->name, b->name))\n\t\treturn 1;\n\n\tif (typecmp_strict(a->aux_type, b->aux_type))\n\t\treturn 1;\n\n\ti = a->num_parms;\n\twhile(i-- > 0)\n\t{\n\t\tif (!a->params[i].paramname || !b->params[i].paramname)\n\t\t{\n\t\t\tif (a->params[i].paramname || b->params[i].paramname)\n\t\t\t\treturn 1;\t//two array types getting compared?\n\t\t}\n\t\telse if (STRCMP(a->params[i].paramname, b->params[i].paramname))\n\t\t\treturn 1;\n\t\tif (typecmp_strict(a->params[i].type, b->params[i].type))\n\t\t\treturn 1;\n\t\tif (a->params[i].defltvalue.cast || b->params[i].defltvalue.cast)\n\t\t{\n\t\t\tif (typecmp_strict(a->params[i].defltvalue.cast, b->params[i].defltvalue.cast) ||\n\t\t\t\ta->params[i].defltvalue.sym != b->params[i].defltvalue.sym ||\n\t\t\t\ta->params[i].defltvalue.ofs != b->params[i].defltvalue.ofs)\n\t\t\t\treturn 1;\n\t\t}\n\t}\n\n\treturn 0;\n}\n\n//reports if they're functionally equivelent (allows assignments) \nint typecmp(QCC_type_t *a, QCC_type_t *b)\n{\n\tint i;\n\tif (a == b)\n\t\treturn 0;\n\tif (!a || !b)\n\t\treturn 1;\t//different (^ and not both null)\n\n\tif (a->type != b->type)\n\t\treturn 1;\n\tif (a->num_parms != b->num_parms)\n\t\treturn 1;\n\tif (a->vargs != b->vargs)\n\t\treturn 1;\n\tif (a->vargcount != b->vargcount)\n\t\treturn 1;\n\n\tif (a->size != b->size || a->bits != b->bits)\n\t\treturn 1;\n\n\tif ((a->type == ev_entity && a->parentclass) || a->type == ev_struct || a->type == ev_union)\n\t{\n\t\tif (STRCMP(a->name, b->name))\n\t\t\treturn 1;\n\t}\n\n\tif (typecmp(a->aux_type, b->aux_type))\n\t\treturn 1;\n\n\ti = a->num_parms;\n\twhile(i-- > 0)\n\t{\n\t\tif (a->type == ev_union && (!a->params[i].paramname || !b->params[i].paramname))\t//special array weirdness.\n\t\t{\n\t\t\tif (a->params[i].paramname || b->params[i].paramname)\n\t\t\t\treturn 1;\n\t\t}\n\t\telse if (a->type != ev_function && STRCMP(a->params[i].paramname, b->params[i].paramname))\n\t\t\treturn 1;\n\t\tif (typecmp(a->params[i].type, b->params[i].type))\n\t\t\treturn 1;\n\t\tif ((a->type != ev_function && (a->params[i].defltvalue.cast || b->params[i].defltvalue.cast)) ||\n\t\t\t(a->type == ev_function && (a->params[i].defltvalue.cast && b->params[i].defltvalue.cast)))\n\t\t{\n\t\t\tif (typecmp(a->params[i].defltvalue.cast, b->params[i].defltvalue.cast) ||\n\t\t\t\ta->params[i].defltvalue.sym != b->params[i].defltvalue.sym ||\n\t\t\t\ta->params[i].defltvalue.ofs != b->params[i].defltvalue.ofs)\n\t\t\t\treturn 1;\n\t\t}\n\t}\n\n\treturn 0;\n}\n\n//compares the types, but doesn't complain if there are optional arguments which differ\nint typecmp_lax(QCC_type_t *a, QCC_type_t *b)\n{\n\tunsigned int minargs = 0;\n\tunsigned int t;\n\n\tif (a == b)\n\t\treturn 0;\n\tif (!a || !b)\n\t\treturn 1;\t//different (^ and not both null)\n\n\tif (a->type != b->type)\n\t{\n\t\tif (a->type == ev_accessor && a->parentclass)\n\t\t\tif (!typecmp_lax(a->parentclass, b))\n\t\t\t\treturn 0;\n\t\tif (b->type == ev_accessor && b->parentclass)\n\t\t\tif (!typecmp_lax(a, b->parentclass))\n\t\t\t\treturn 0;\n\t\tif (a->type != ev_variant && b->type != ev_variant)\n\t\t\treturn 1;\n\t}\n\telse if (a->size != b->size)\n\t\treturn 1;\n\n\tif (a->vargcount != b->vargcount)\n\t\treturn 1;\n\n\tt = a->num_parms;\n\tminargs = t;\n\tt = b->num_parms;\n\tif (minargs > t)\n\t\tminargs = t;\n\n//\tif (STRCMP(a->name, b->name))\t//This isn't 100% clean.\n//\t\treturn 1;\n\n\tif (typecmp_lax(a->aux_type, b->aux_type))\n\t\treturn 1;\n\n\t//variants and args don't make sense, and are considered equivelent(ish).\n\tif (a->type == ev_variant || b->type == ev_variant)\n\t\treturn 0;\n\n\t//optional arg types must match, even if they're not specified in one.\n\tfor (t = 0; t < minargs; t++)\n\t{\n\t\tif (a->params[t].type->type != b->params[t].type->type)\n\t\t\treturn 1;\n\t\tif (a->params[t].out != b->params[t].out)\n\t\t\treturn 1;\n\t\t//classes/structs/unions are matched on class names rather than the contents of the class\n\t\t//it gets too painful otherwise, with recursive definitions.\n\t\tif (a->params[t].type->type == ev_entity || a->params[t].type->type == ev_struct || a->params[t].type->type == ev_union)\n\t\t{\n\t\t\tif (STRCMP(a->params[t].type->name, b->params[t].type->name))\n\t\t\t\treturn 1;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (typecmp_lax(a->params[t].type, b->params[t].type))\n\t\t\t\treturn 1;\n\t\t}\n\n\t\tif ((a->type != ev_function && (a->params[t].defltvalue.cast || b->params[t].defltvalue.cast)) ||\n\t\t\t(a->type == ev_function && (a->params[t].defltvalue.cast && b->params[t].defltvalue.cast)))\n\t\t{\n\t\t\tif (typecmp(a->params[t].defltvalue.cast, b->params[t].defltvalue.cast) ||\n\t\t\t\ta->params[t].defltvalue.sym != b->params[t].defltvalue.sym ||\n\t\t\t\ta->params[t].defltvalue.ofs != b->params[t].defltvalue.ofs)\n\t\t\t\treturn 1;\n\t\t}\n\t}\n\tif (a->num_parms > minargs)\n\t{\n\t\tfor (t = minargs; t < a->num_parms; t++)\n\t\t{\n\t\t\tif (!a->params[t].optional)\n\t\t\t\treturn 1;\n\t\t}\n\t}\n\tif (b->num_parms > minargs)\n\t{\n\t\tfor (t = minargs; t < b->num_parms; t++)\n\t\t{\n\t\t\tif (!b->params[t].optional)\n\t\t\t\treturn 1;\n\t\t}\n\t}\n\n\treturn 0;\n}\n\n\nQCC_type_t *QCC_PR_DuplicateType(QCC_type_t *in, pbool recurse)\n{\n\tQCC_type_t *out;\n\tif (!in)\n\t\treturn NULL;\n\n\tout = QCC_PR_NewType(in->name, in->type, false);\n\tout->aux_type = recurse?QCC_PR_DuplicateType(in->aux_type, recurse):in->aux_type;\n\tout->num_parms = in->num_parms;\n\tout->params = qccHunkAlloc(sizeof(*out->params) * out->num_parms);\n\tmemcpy(out->params, in->params, sizeof(*out->params) * out->num_parms);\n\tout->accessors = in->accessors;\n\tout->size = in->size;\n\tout->bits = in->bits;\n\tout->align = in->align;\n\tout->num_parms = in->num_parms;\n\tout->name = in->name;\n\tout->parentclass = in->parentclass;\n\tout->vargs = in->vargs;\n\tout->vargtodouble = in->vargtodouble;\n\tout->vargcount = in->vargcount;\n\n\treturn out;\n}\n\nstatic void Q_strlcat(char *dest, const char *src, int sizeofdest)\n{\n\tif (sizeofdest)\n\t{\n\t\tint dlen = strlen(dest);\n\t\tint slen = strlen(src)+1;\n\t\tmemcpy(dest+dlen, src, min((sizeofdest-1)-dlen, slen));\n\t\tdest[sizeofdest - 1] = 0;\n\t}\n}\n\nchar *TypeName(QCC_type_t *type, char *buffer, int buffersize)\n{\n\tchar *ret;\n\t/*if (type->typedefed)\n\t{\n\t\tif (buffersize < 0)\n\t\t\treturn buffer;\n\t\t*buffer = 0;\n\t\tQ_strlcat(buffer, type->name, buffersize);\n\t\treturn buffer;\n\t}*/\n\n\tif (type->type == ev_void)\n\t{\n\t\tif (buffersize < 0)\n\t\t\treturn buffer;\n\t\t*buffer = 0;\n\t\tQ_strlcat(buffer, \"void\", buffersize);\n\t\treturn buffer;\n\t}\n\tif (type->type == ev_enum)\n\t{\n\t\tif (buffersize < 0)\n\t\t\treturn buffer;\n\t\t*buffer = 0;\n\t\tQ_strlcat(buffer, \"enum \", buffersize);\n\t\tQ_strlcat(buffer, type->name, buffersize);\n\t\tQ_strlcat(buffer, \":\", buffersize);\n\t\tTypeName(type->aux_type, buffer+strlen(buffer), buffersize-strlen(buffer));\n\t\treturn buffer;\n\t}\n\tif (type->type == ev_struct)\n\t{\n\t\tif (buffersize < 0)\n\t\t\treturn buffer;\n\t\t*buffer = 0;\n\t\tQ_strlcat(buffer, \"struct \", buffersize);\n\t\tQ_strlcat(buffer, type->name, buffersize);\n\t\treturn buffer;\n\t}\n\tif (type->type == ev_union)\n\t{\n\t\tif (buffersize < 0)\n\t\t\treturn buffer;\n\t\t*buffer = 0;\n\t\tQ_strlcat(buffer, \"union \", buffersize);\n\t\tQ_strlcat(buffer, type->name, buffersize);\n\t\treturn buffer;\n\t}\n\n\tif (type->type == ev_pointer)\n\t{\n\t\tif (buffersize < 0)\n\t\t\treturn buffer;\n\t\tTypeName(type->aux_type, buffer, buffersize-2);\n\t\tQ_strlcat(buffer, \"*\", buffersize);\n\t\treturn buffer;\n\t}\n\n\tret = buffer;\n\tif (type->type == ev_field)\n\t{\n\t\ttype = type->aux_type;\n\t\t*ret++ = '.';\n\t}\n\t*ret = 0;\n\n\tif (type->type == ev_function)\n\t{\n\t\tint args = type->num_parms;\n\t\tsize_t l;\n\t\tpbool vargs = type->vargs;\n\t\tunsigned int i;\n\t\tQ_strlcat(buffer, type->aux_type->name, buffersize);\n\t\tif (type->vargtodouble)\n\t\t\tQ_strlcat(buffer, \"(*)\", buffersize);\t//make it distinctly C-ey\n\t\tQ_strlcat(buffer, \"(\", buffersize);\n\t\tfor (i = 0; i < type->num_parms; )\n\t\t{\n\t\t\tif (type->params[i].out)\n\t\t\t\tQ_strlcat(buffer, \"inout \", buffersize-1);\n\t\t\tif (type->params[i].optional)\n\t\t\t\tQ_strlcat(buffer, \"optional \", buffersize-1);\n\t\t\targs--;\n\n\t\t\tl = strlen(buffer);\n\t\t\tTypeName(type->params[i].type, buffer+l, buffersize-1-l);\n\t\t\tif (type->params[i].paramname && *type->params[i].paramname)\n\t\t\t{\n\t\t\t\tQ_strlcat(buffer, \" \", buffersize-1);\n\t\t\t\tQ_strlcat(buffer, type->params[i].paramname, buffersize-1);\n\t\t\t}\n\n\t\t\tif (type->params[i].defltvalue.cast)\n\t\t\t{\n\t\t\t\tQ_strlcat(buffer, \" = \", buffersize-1);\n\t\t\t\tQ_strlcat(buffer, QCC_VarAtOffset(type->params[i].defltvalue), buffersize-1);\n\t\t\t}\n\n\t\t\tif (++i < type->num_parms || vargs)\n\t\t\t\tQ_strlcat(buffer, \", \", buffersize-1);\n\t\t}\n\t\tif (vargs)\n\t\t\tQ_strlcat(buffer, \"...\", buffersize-1);\n\t\tQ_strlcat(buffer, \")\", buffersize);\n\t}\n\telse if (type->type == ev_entity && type->parentclass)\n\t{\n\t\tret = buffer;\n\t\t*ret = 0;\n\t\tQ_strlcat(buffer, \"class \", buffersize);\n\t\tQ_strlcat(buffer, type->name, buffersize);\n/*\t\tstrcat(ret, \" {\");\n\t\ttype = type->param;\n\t\twhile(type)\n\t\t{\n\t\t\tstrcat(ret, type->name);\n\t\t\ttype = type->next;\n\n\t\t\tif (type)\n\t\t\t\tstrcat(ret, \", \");\n\t\t}\n\t\tstrcat(ret, \"}\");\n*/\n\t}\n\telse\n\t\tQ_strlcat(buffer, type->name, buffersize);\n\n\treturn buffer;\n}\n//#define typecmp(a, b) (a && ((a)->type==(b)->type) && !STRCMP((a)->name, (b)->name))\n\nstatic QCC_type_t *QCC_PR_FindType (QCC_type_t *type)\n{\n\tint t;\n\tfor (t = 0; t < numtypeinfos; t++)\n\t{\n//\t\tcheck = &qcc_typeinfo[t];\n\t\tif (typecmp_strict(&qcc_typeinfo[t], type))\n\t\t\tcontinue;\n\n//\t\tc2 = check->next;\n//\t\tn2 = type->next;\n//\t\tfor (i=0 ; n2&&c2 ; i++)\n//\t\t{\n//\t\t\tif (!typecmp((c2), (n2)))\n//\t\t\t\tbreak;\n//\t\t\tc2=c2->next;\n//\t\t\tn2=n2->next;\n//\t\t}\n\n//\t\tif (n2==NULL&&c2==NULL)\n\t\t{\n\t\t\treturn &qcc_typeinfo[t];\n\t\t}\n\t}\n\n\ttype = QCC_PR_DuplicateType(type, false);\n\treturn type;\n}\n/*\nQCC_type_t *QCC_PR_NextSubType(QCC_type_t *type, QCC_type_t *prev)\n{\n\tint p;\n\tif (!prev)\n\t\treturn type->next;\n\n\tfor (p = prev->num_parms; p; p--)\n\t\tprev = QCC_PR_NextSubType(prev, NULL);\n\tif (prev->num_parms)\n\n\tswitch(prev->type)\n\t{\n\tcase ev_function:\n\n\t}\n\n\treturn prev->next;\n}\n*/\n\nQCC_type_t *QCC_TypeForName(const char *name)\n{\n\tQCC_type_t *t = pHash_Get(&typedeftable, name);\n\twhile (t && t->scope)\n\t{\n\t\tif (t->scope == pr_scope)\n\t\t\tbreak;\t//its okay after all.\n\t\tt = pHash_GetNext(&typedeftable, name, t);\n\t}\n\tif (t && t->type == ev_typedef)\n\t\treturn t->aux_type;\t//just use its real type.\n\treturn t;\n}\n\n/*\n============\nPR_SkipToSemicolon\n\nFor error recovery, also pops out of nested braces\n============\n*/\nvoid QCC_PR_SkipToSemicolon (void)\n{\n\t//escape out of any #define\n\twhile (currentchunk && currentchunk->cnst)\n\t\tQCC_PR_UnInclude();\n\n\tdo\n\t{\n\t\tif (!pr_bracelevel && QCC_PR_CheckToken (\";\"))\n\t\t\treturn;\n\t\tQCC_PR_Lex ();\n\t} while (pr_token_type != tt_eof);\n}\n\nqc_inlinestatic QCC_eval_t *QCC_SRef_Data(QCC_sref_t ref)\n{\n\treturn (QCC_eval_t*)&ref.sym->symboldata[ref.ofs];\n}\n\n/*\n============\nPR_ParseType\n\nParses a variable type, including field and functions types\n============\n*/\n#ifdef MAX_EXTRA_PARMS\nchar\tpr_parm_names[MAX_PARMS+MAX_EXTRA_PARMS][MAX_NAME];\n#else\nchar\tpr_parm_names[MAX_PARMS][MAX_NAME];\n#endif\nchar *pr_parm_argcount_name;\n\nint recursivefunctiontype;\n\nQCC_type_t *QCC_PR_MakeThiscall(QCC_type_t *orig, QCC_type_t *thistype)\n{\n\tQCC_type_t\tftype = *orig;\n\n\tftype.ptrto = NULL;\n\tftype.typedefed = false;\n\tftype.num_parms++;\n\tftype.params = qccHunkAlloc(sizeof(*ftype.params) * ftype.num_parms);\n\tmemcpy(ftype.params+1, orig->params, sizeof(*ftype.params) * orig->num_parms);\n\tftype.params[0].paramname = \"this\";\n\tftype.params[0].type = QCC_PR_PointerType(thistype);\n\tftype.params[0].isvirtual = true;\n\n\tmemmove(&pr_parm_names[1], &pr_parm_names[0], sizeof(*pr_parm_names)*orig->num_parms);\n\tstrcpy(pr_parm_names[0], \"this\");\n\n\torig = QCC_PR_FindType (&ftype);\n\tif (!orig)\n\t{\n\t\torig = QCC_PR_NewType(ftype.name, ftype.type, false);\n\t\t*orig = ftype;\n\t}\n\treturn orig;\n}\n\nQCC_type_t *QCC_PR_ParseArrayType(QCC_type_t *basetype, int *arraysize)\n{\n\tint dims = 0;\n\tunsigned long dim[64];\n\tdim[0] = 0;\n\tdo\n\t{\n\t\tif (dims == sizeof(dim)/sizeof(dim[0]))\n\t\t\tQCC_PR_ParseError(ERR_NOTANAME, \"too many dimensions\");\n\t\tdim[dims] = QCC_PR_IntConstExpr();\n\t\tif (!dim[dims])\n\t\t\tQCC_PR_ParseError(ERR_NOTANAME, \"cannot cope with 0-sized arrays\");\n\t\tdims++;\n\t\tQCC_PR_Expect(\"]\");\n\t} while (QCC_PR_CheckToken(\"[\"));\n\twhile(dims-- > 1)\n\t\tbasetype = QCC_GenArrayType(basetype, dim[dims]);\n\t*arraysize = dim[0];\n\treturn basetype;\n}\n\n//expects a ( to have already been parsed.\nQCC_type_t *QCC_PR_ParseFunctionType (int newtype, QCC_type_t *returntype)\n{\n\tQCC_type_t\t*ftype = NULL, *t;\n\tchar\t*name;\n\tint definenames = !recursivefunctiontype;\n\tint numparms = 0;\n\tstruct QCC_typeparam_s paramlist[MAX_PARMS+MAX_EXTRA_PARMS];\n\n\tif (QCC_PR_PeekToken(\"*\"))\n\t\treturn NULL;\t//C pointer-to-function. this ain't the args.\n\n\trecursivefunctiontype++;\n\n\tif (definenames)\n\t\tpr_parm_argcount_name = NULL;\n\n\tif (QCC_PR_CheckToken (\")\"))\n\t{\n\t\tif (!ftype)\n\t\t{\n\t\t\tftype = QCC_PR_NewType(type_function->name, ev_function, false);\n\t\t\tftype->aux_type = returntype;\t// return type\n\t\t\tftype->num_parms = 0;\n\t\t}\n\t\tif (!flag_qcfuncs)\n\t\t\tftype->vargs = true;\t//'void name()' is vargs/undefined in C89 (disallowed in c99, interpreted as void in c23).\n\t}\n\telse\n\t{\n\t\tdo\n\t\t{\n\t\t\tint inout = -1;\n\t\t\tpbool isopt = false;\n\t\t\tif (numparms>=MAX_PARMS+MAX_EXTRA_PARMS)\n\t\t\t\tQCC_PR_ParseError(ERR_TOOMANYTOTALPARAMETERS, \"Too many parameters. Sorry. (limit is %i)\\n\", MAX_PARMS+MAX_EXTRA_PARMS);\n\n\t\t\tif (QCC_PR_CheckToken (\"...\"))\n\t\t\t{\n\t\t\t\tt = QCC_PR_ParseType(false, true, false);\t//the evil things I do...\n\t\t\t\tif (!t)\n\t\t\t\t{\n\t\t\t\t\tif (!ftype)\n\t\t\t\t\t{\n\t\t\t\t\t\tftype = QCC_PR_NewType(type_function->name, ev_function, false);\n\t\t\t\t\t\tftype->aux_type = returntype;\t// return type\n\t\t\t\t\t\tftype->num_parms = 0;\n\t\t\t\t\t}\n\t\t\t\t\tftype->vargs = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\t//its a ... followed by a type...\n\t\t\t\t\t//undo the damage from having parsed the ... already.\n\t\t\t\t\tt = QCC_PR_FieldType(t);\n\t\t\t\t\tt = QCC_PR_FieldType(t);\n\t\t\t\t\tt = QCC_PR_FieldType(t);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tpbool maybename = numparms==0;\n\t\t\t\twhile(1)\n\t\t\t\t{\n\t\t\t\t\tif (!isopt && QCC_PR_CheckKeyword(keyword_optional, \"optional\"))\n\t\t\t\t\t\tisopt = true;\n\t\t\t\t\telse if (inout<0 && QCC_PR_CheckKeyword(keyword_inout, \"inout\"))\n\t\t\t\t\t\tinout = true;\n\t\t\t\t\telse if (inout<0 && QCC_PR_CheckKeyword(keyword_inout, \"out\"))\n\t\t\t\t\t\tinout = 2;\n\t\t\t\t\telse if (inout<0 && QCC_PR_CheckKeyword(keyword_inout, \"in\"))\n\t\t\t\t\t\tinout = false;\n\t\t\t\t\telse\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tmaybename=false;\t//if we parsed something meaningful then its not a `void(name)(type)` thing\n\t\t\t\t}\n\n\t\t\t\tt = QCC_PR_ParseType(false, maybename, false);\n\t\t\t\tif (!t)\n\t\t\t\t{\n\t\t\t\t\tif (!flag_qcfuncs)\n\t\t\t\t\t\tt = type_integer;\t//c89 assumes int.\n\t\t\t\t\telse\n\t\t\t\t\t\treturn NULL;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (!ftype)\n\t\t\t{\n\t\t\t\tftype = QCC_PR_NewType(type_function->name, ev_function, false);\n\t\t\t\tftype->aux_type = returntype;\t// return type\n\t\t\t\tftype->num_parms = 0;\n\t\t\t}\n\n\t\t\tparamlist[numparms].optional = isopt;\n\t\t\tparamlist[numparms].isvirtual = false;\n\t\t\tparamlist[numparms].out = inout>=0?inout:0;\n\t\t\tparamlist[numparms].defltvalue.cast = NULL;\n\t\t\tparamlist[numparms].ofs = 0;\n\t\t\tparamlist[numparms].arraysize = 0;\n\t\t\tparamlist[numparms].type = t;\n\t\t\tif (!paramlist[numparms].type)\n\t\t\t\tQCC_PR_ParseError(0, \"Expected type\\n\");\n\n\t\t\twhile (QCC_PR_CheckToken(\"*\"))\n\t\t\t\tparamlist[numparms].type = QCC_PointerTypeTo(paramlist[numparms].type);\n\n\t\t\tif (inout < 0 && QCC_PR_CheckToken(\"&\"))\n\t\t\t{\t//accept c++ syntax, at least on arguments. its not quite the same, but it'll do.\n\t\t\t\tparamlist[numparms].out = true;\n\t\t\t}\n\n//\t\t\ttype->name = \"FUNC PARAMETER\";\n\n\t\t\tparamlist[numparms].paramname = \"\";\n\t\t\tif (QCC_PR_CheckToken (\"...\"))\n\t\t\t{\n\t\t\t\tftype->vargs = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tname = \"\";\n\t\t\tif (QCC_PR_CheckToken(\"(\"))\n\t\t\t{\n\t\t\t\tQCC_PR_CheckToken(\"*\");\t//one is normal for a function pointer/ref. non-ptr makes no sense here.\n\t\t\t\tname = QCC_PR_ParseName ();\n\t\t\t\tif (QCC_PR_CheckToken(\"[\"))\n\t\t\t\t{\n\t\t\t\t\tif (!QCC_PR_CheckToken(\"]\"))\n\t\t\t\t\t{\t//don't really care, doesn't matter\n\t\t\t\t\t\tparamlist[numparms].arraysize = QCC_PR_IntConstExpr();\n\t\t\t\t\t\tQCC_PR_Expect(\"]\");\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tQCC_PR_Expect(\")\");\n\n\t\t\t\tQCC_PR_Expect(\"(\");\n\t\t\t\tparamlist[numparms].type = QCC_PR_ParseFunctionType(false, paramlist[numparms].type);\n\t\t\t\tif (!paramlist[numparms].type)\n\t\t\t\t\tQCC_PR_ParseError(ERR_BADNOTTYPE, \"expected function arg list\");\n\t\t\t}\n\t\t\telse if (pr_token_type == tt_name)\n\t\t\t\tname = QCC_PR_ParseName ();\n\t\t\tparamlist[numparms].paramname = name;\n\t\t\tif (definenames)\n\t\t\t\tstrcpy (pr_parm_names[numparms], name);\n\t\t\tif (*name)\n\t\t\t\tnewtype = true;\n\t\t\telse if (paramlist[numparms].type->type == ev_void)\n\t\t\t\tbreak; //float(void) has no actual args\n\n\t\t\tif (!paramlist[numparms].arraysize && QCC_PR_CheckToken(\"[\"))\n\t\t\t{\n\t\t\t\tif (QCC_PR_CheckToken(\"]\"))\t//length omitted. just treat it as a pointer...?\n\t\t\t\t{\n\t\t\t\t\t//QCC_PR_ParseWarning(ERR_BADARRAYSIZE, \"unsized array argument\");\n\t\t\t\t\tparamlist[numparms].type = QCC_PointerTypeTo(paramlist[numparms].type);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\t//proper array\n\t\t\t\t\tint arraysize;\n\t\t\t\t\tparamlist[numparms].type = QCC_PR_ParseArrayType(paramlist[numparms].type, &arraysize);\n\n\t\t\t\t\tif (flag_qcfuncs)\n\t\t\t\t\t\tparamlist[numparms].arraysize = arraysize;\n\t\t\t\t\telse\n\t\t\t\t\t\tparamlist[numparms].type = QCC_PointerTypeTo(paramlist[numparms].type);\t//just turn it into a pointer and ditch the top-level size. C style.\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (!flag_qcfuncs)\n\t\t\t{\t//if its an array type, promote it to pointer here.\n\t\t\t\tif (t->type == ev_union && t->num_parms == 1 && !t->params[0].paramname)\n\t\t\t\t\tparamlist[numparms].type = QCC_PointerTypeTo(t->params[0].type);\n\t\t\t}\n\n\t\t\tif (QCC_PR_CheckToken(\"=\"))\n\t\t\t{\n\t\t\t\tparamlist[numparms].defltvalue = QCC_PR_ParseDefaultInitialiser(paramlist[numparms].type);\n\t\t\t\tQCC_FreeTemp(paramlist[numparms].defltvalue);\n\t\t\t}\n\t\t\tnumparms++;\n\t\t} while (QCC_PR_CheckToken (\",\"));\n\n\t\tif (ftype->vargs)\n\t\t{\n\t\t\tif (!QCC_PR_CheckToken (\")\"))\n\t\t\t{\n\t\t\t\tname = QCC_PR_ParseName();\n\t\t\t\tif (definenames)\n\t\t\t\t{\n\t\t\t\t\tpr_parm_argcount_name = qccHunkAlloc(strlen(name)+1);\n\t\t\t\t\tstrcpy(pr_parm_argcount_name, name);\n\t\t\t\t}\n\t\t\t\tftype->vargcount = true;\n\t\t\t\tQCC_PR_Expect (\")\");\n\t\t\t}\n\t\t}\n\t\telse\n\t\t\tQCC_PR_Expect (\")\");\n\t}\n\tftype->vargtodouble = !flag_qcfuncs && flag_assume_double;\n\tftype->num_parms = numparms;\n\tftype->params = qccHunkAlloc(sizeof(*ftype->params) * numparms);\n\tmemcpy(ftype->params, paramlist, sizeof(*ftype->params) * numparms);\n\trecursivefunctiontype--;\n\n\tif (newtype)\n\t\treturn ftype;\n\treturn QCC_PR_FindType (ftype);\n}\nQCC_type_t *QCC_PR_ParseFunctionTypeReacc (int newtype, QCC_type_t *returntype)\n{\n\tQCC_type_t\t*ftype, *nptype;\n\tchar\t*name;\n\tint definenames = !recursivefunctiontype;\n\tint numparms = 0;\n\tstruct QCC_typeparam_s paramlist[MAX_PARMS+MAX_EXTRA_PARMS];\n\n\trecursivefunctiontype++;\n\n\tftype = QCC_PR_NewType(type_function->name, ev_function, false);\n\n\tftype->aux_type = returntype;\t// return type\n\tftype->num_parms = 0;\n\n\tpr_parm_argcount_name = NULL;\n\n\tif (!QCC_PR_CheckToken (\")\"))\n\t{\n\t\tdo\n\t\t{\n\t\t\tif (numparms>=MAX_PARMS+MAX_EXTRA_PARMS)\n\t\t\t\tQCC_PR_ParseError(ERR_TOOMANYTOTALPARAMETERS, \"Too many parameters. Sorry. (limit is %i)\\n\", MAX_PARMS+MAX_EXTRA_PARMS);\n\n\t\t\tif (QCC_PR_CheckToken (\"...\"))\n\t\t\t{\n\t\t\t\tftype->vargs = true;\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tif (QCC_PR_CheckName(\"arg\"))\n\t\t\t{\n\t\t\t\tname = \"\";\n\t\t\t\tnptype = QCC_PR_NewType(\"Variant\", ev_variant, false);\n\t\t\t}\n\t\t\telse if (QCC_PR_CheckName(\"vect\"))\t//this can only be of vector sizes, so...\n\t\t\t{\n\t\t\t\tname = \"\";\n\t\t\t\tnptype = QCC_PR_NewType(\"Vector\", ev_vector, false);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tname = QCC_PR_ParseName();\n\t\t\t\tQCC_PR_Expect(\":\");\n\t\t\t\tnptype = QCC_PR_ParseType(true, false, false);\n\t\t\t}\n\n\t\t\tif (!nptype)\n\t\t\t\tQCC_PR_ParseError(0, \"Expected type\\n\");\n\t\t\tif (nptype->type == ev_void)\n\t\t\t\tbreak;\n//\t\t\ttype->name = \"FUNC PARAMETER\";\n\n\t\t\tparamlist[numparms].out = false;\n\t\t\tparamlist[numparms].optional = false;\n\t\t\tparamlist[numparms].isvirtual = false;\n\t\t\tparamlist[numparms].ofs = 0;\n\t\t\tparamlist[numparms].bitofs = 0;\n\t\t\tparamlist[numparms].arraysize = 0;\n\t\t\tparamlist[numparms].type = nptype;\n\n\t\t\tif (!*name)\n\t\t\t\tparamlist[numparms].paramname = \"\";\n\t\t\telse\n\t\t\t{\n\t\t\t\tparamlist[numparms].paramname = qccHunkAlloc(strlen(name)+1);\n\t\t\t\tstrcpy(paramlist[numparms].paramname, name);\n\t\t\t}\n\t\t\tif (definenames)\n\t\t\t\tQC_snprintfz(pr_parm_names[numparms], MAX_NAME, \"%s\", name);\n\n\t\t\tnumparms++;\n\t\t} while (QCC_PR_CheckToken (\";\"));\n\t\tQCC_PR_Expect (\")\");\n\t}\n\tftype->num_parms = numparms;\n\tftype->params = qccHunkAlloc(sizeof(*ftype->params) * numparms);\n\tmemcpy(ftype->params, paramlist, sizeof(*ftype->params) * numparms);\n\trecursivefunctiontype--;\n\tif (newtype)\n\t\treturn ftype;\n\treturn QCC_PR_FindType (ftype);\n}\nQCC_type_t *QCC_PR_PointerType (QCC_type_t *pointsto)\n{\n\tQCC_type_t\t*ptype;\n\tchar name[128];\n\tif (pointsto->ptrto)\n\t\treturn pointsto->ptrto;\n\tQC_snprintfz(name, sizeof(name), \"%s*\", pointsto->name);\n\tptype = QCC_PR_NewType(strcpy(qccHunkAlloc(strlen(name)+1), name), ev_pointer, false);\n\tptype->aux_type = pointsto;\n\treturn pointsto->ptrto = QCC_PR_FindType (ptype);\n}\nQCC_type_t *QCC_PR_FieldType (QCC_type_t *pointsto)\n{\n\tQCC_type_t\t*ptype;\n\tchar name[128];\n\tif (pointsto->fldto)\n\t\treturn pointsto->fldto;\n\tQC_snprintfz(name, sizeof(name), \".%s\", pointsto->name);\n\tptype = QCC_PR_NewType(strcpy(qccHunkAlloc(strlen(name)+1), name), ev_field, false);\n\tptype->aux_type = pointsto;\n\tptype->size = ptype->aux_type->size;\n\treturn pointsto->fldto = QCC_PR_FindType (ptype);\n}\nQCC_type_t *QCC_PR_GenFunctionType (QCC_type_t *rettype, struct QCC_typeparam_s *args, int numargs)\n{\n\tint i;\n\tstruct QCC_typeparam_s *p;\n\tQCC_type_t *ftype;\n\tftype = QCC_PR_NewType(\"$func\", ev_function, false);\n\tftype->aux_type = rettype;\n\tftype->num_parms = numargs;\n\tftype->params = p = qccHunkAlloc(sizeof(*ftype->params)*numargs);\n\tftype->vargs = false;\n\tftype->vargcount = false;\n\tfor (i = 0; i < numargs; i++, p++)\n\t{\n\t\tif (args[i].paramname)\n\t\t{\n\t\t\tp->paramname = qccHunkAlloc(strlen(args[i].paramname)+1);\n\t\t\tstrcpy(p->paramname, args[i].paramname);\n\t\t}\n\t\telse\n\t\t\tp->paramname = \"\";\n\t\tp->type = args[i].type;\n\t\tp->out = args[i].out;\n\t\tp->optional = args[i].optional;\n\t\tp->isvirtual = args[i].isvirtual;\n\t\tp->ofs = args[i].ofs;\n\t\tp->arraysize = args[i].arraysize;\n\t\tp->defltvalue.cast = NULL;\n\t}\n\treturn QCC_PR_FindType (ftype);\n}\n\n\nstruct accessor_s *QCC_PR_ParseAccessorMember(QCC_type_t *classtype, pbool isinline, pbool setnotget)\n{\n\tstruct accessor_s  *acc, *pacc;\n\tchar *fieldtypename;\n\tQCC_type_t *fieldtype;\n\tQCC_type_t *indextype;\n\tQCC_sref_t def;\n\tQCC_type_t *functype;\n\tQCC_type_t *parenttype;\n\tstruct QCC_typeparam_s arg[3];\n\tint args;\n\tchar *indexname;\n\tpbool isref;\n\tchar *accessorname;\n\n\tif (QCC_PR_CheckToken(\"&\"))\n\t\tisref = 2;\n\telse\n\t\tisref = QCC_PR_CheckToken(\"*\");\n\n\tfieldtypename = QCC_PR_ParseName();\n\tfieldtype = QCC_TypeForName(fieldtypename);\n\tif (!fieldtype)\n\t\tQCC_PR_ParseError(ERR_NOTATYPE, \"Invalid type: %s\", fieldtypename);\n\twhile(QCC_PR_CheckToken(\"*\"))\n\t\tfieldtype = QCC_PR_PointerType(fieldtype);\n\n\tif (pr_token_type != tt_punct)\n\t\taccessorname = QCC_PR_ParseName();\n\telse\n\t\taccessorname = \"\";\n\n\tindextype = NULL;\n\tindexname = \"index\";\n\tif (QCC_PR_CheckToken(\"[\"))\n\t{\n\t\tfieldtypename = QCC_PR_ParseName();\n\t\tindextype = QCC_TypeForName(fieldtypename);\n\n\t\tif (!QCC_PR_CheckToken(\"]\"))\n\t\t{\n\t\t\tindexname = QCC_PR_ParseName();\n\t\t\tQCC_PR_Expect(\"]\");\n\t\t}\n\t}\n\n\tQCC_PR_Expect(\"=\");\n\n\targs = 0;\n\tmemset(arg, 0, sizeof(arg));\n\tstrcpy (pr_parm_names[args], \"this\");\n\targ[args].paramname = \"this\";\n\tif (isref == 2)\n\t{\n\t\targ[args].type = classtype;\n\t\targ[args].out = 1;\t//inout\n\t}\n\telse if (isref)\n\t\targ[args].type = QCC_PointerTypeTo(classtype);\n\telse\n\t\targ[args].type = classtype;\n\targs++;\n\tif (indextype)\n\t{\n\t\tstrcpy (pr_parm_names[args], indexname);\n\t\targ[args].paramname = indexname;\n\t\targ[args++].type = indextype;\n\t}\n\tif (setnotget)\n\t{\n\t\tstrcpy (pr_parm_names[args], \"value\");\n\t\targ[args].paramname = \"value\";\n\t\targ[args++].type = fieldtype;\n\t}\n\tfunctype = QCC_PR_GenFunctionType(setnotget?type_void:fieldtype, arg, args);\n\n\tif (pr_token_type != tt_name)\n\t{\n\t\tQCC_function_t *f;\n\t\tchar funcname[256];\n\t\tQC_snprintfz(funcname, sizeof(funcname), \"%s::%s_%s\", classtype->name, setnotget?\"set\":\"get\", accessorname);\n\n\t\tdef = QCC_PR_GetSRef(functype, funcname, NULL, true, 0, GDF_CONST | (isinline?GDF_INLINE:0));\n\n\t\tif (autoprototype)\n\t\t{\n\t\t\tif (QCC_PR_CheckToken(\"[\"))\n\t\t\t{\n\t\t\t\twhile (!QCC_PR_CheckToken(\"]\"))\n\t\t\t\t{\n\t\t\t\t\tif (pr_token_type == tt_eof)\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tQCC_PR_Lex();\n\t\t\t\t}\n\t\t\t}\n\t\t\tQCC_PR_Expect(\"{\");\n\n\t\t\t{\n\t\t\t\tint blev = 1;\n\t\t\t\t//balance out the { and }\n\t\t\t\twhile(blev)\n\t\t\t\t{\n\t\t\t\t\tif (pr_token_type == tt_eof)\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tif (QCC_PR_CheckToken(\"{\"))\n\t\t\t\t\t\tblev++;\n\t\t\t\t\telse if (QCC_PR_CheckToken(\"}\"))\n\t\t\t\t\t\tblev--;\n\t\t\t\t\telse\n\t\t\t\t\t\tQCC_PR_Lex();\t//ignore it.\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tpr_classtype = ((classtype->type==ev_entity)?classtype:NULL);\n\t\t\tf = QCC_PR_ParseImmediateStatements (def.sym, functype, false);\n\t\t\tpr_classtype = NULL;\n\t\t\tpr_scope = NULL;\n\t\t\tQCC_SRef_Data(def)->function = f - functions;\n\t\t\tf->def = def.sym;\n\t\t\tdef.sym->initialized = 1;\n\t\t}\n\t}\n\telse\n\t{\n\t\tconst char *funcname = QCC_PR_ParseName();\n\t\tdef = QCC_PR_GetSRef(functype, funcname, NULL, true, 0, GDF_CONST|(isinline?GDF_INLINE:0));\n\t\tif (!def.cast)\n\t\t\tQCC_Error(ERR_NOFUNC, \"%s::set_%s: %s was not defined\", classtype->name, accessorname, funcname);\n\t}\n\tif (!def.cast || !def.sym || def.sym->temp)\n\t\tQCC_Error(ERR_NOFUNC, \"%s::%s_%s function invalid\", classtype->name, setnotget?\"set\":\"get\", accessorname);\n\n\tfor (acc = classtype->accessors; acc; acc = acc->next)\n\t\tif (!strcmp(acc->fieldname, accessorname))\n\t\t\tbreak;\n\tif (!acc)\n\t{\n\t\tacc = qccHunkAlloc(sizeof(*acc));\n\t\tacc->fieldname = accessorname;\n\t\tacc->next = classtype->accessors;\n\t\tacc->type = fieldtype;\n\t\tacc->indexertype = indextype;\n\t\tclasstype->accessors = acc;\n\t}\n\n\tif (acc->getset_func[setnotget].cast && (\n\t\tacc->getset_func[setnotget].sym != def.sym ||\n\t\tacc->getset_func[setnotget].cast != def.cast ||\n\t\tacc->getset_func[setnotget].ofs != def.ofs))\n\t\tQCC_Error(ERR_TOOMANYINITIALISERS, \"%s::%s_%s already declared\", classtype->name, setnotget?\"set\":\"get\", accessorname);\n\tacc->getset_func[setnotget] = def;\n\tacc->getset_isref[setnotget] = isref;\n\tQCC_FreeTemp(def);\n\n\tfor (parenttype = classtype->parentclass; parenttype; parenttype = parenttype->parentclass)\n\t{\n\t\tif (!parenttype->accessors)\n\t\t\tcontinue;\n\t\tfor (pacc = parenttype->accessors; pacc; pacc = pacc->next)\n\t\t{\n\t\t\tif (!strcmp(acc->fieldname, pacc->fieldname))\n\t\t\t\tQCC_PR_ParseWarning(WARN_DUPLICATEDEFINITION, \"%s::%s shadows parent %s\", classtype->name, acc->fieldname?acc->fieldname:\"<anon>\", parenttype->name);\n\t\t}\n\t}\n\n\treturn acc;\n}\n\nextern char *basictypenames[];\nextern QCC_type_t **basictypes[];\nstatic QCC_type_t *QCC_PR_ParseStruct(etype_t structtype)\n{\n\tQCC_type_t *newt, *type, *newparm, *oldtype = NULL;\n\tstruct QCC_typeparam_s *parms = NULL, *oldparm;\n\tint numparms = 0;\n\tint ofs, bitofs;\n\tunsigned int arraysize;\n\tchar *parmname;\n\n\tpbool isnonvirt = false;\n\tpbool isstatic = false;\n\tpbool isvirt = false;\n\tpbool definedsomething = false;\n\tunsigned int bitsize;\n\tunsigned int bitalign = 0;\t//alignment of largest element...\n\n\tif (QCC_PR_CheckToken(\"{\"))\n\t{\n\t\t//nameless struct\n\t\tnewt = QCC_PR_NewType(structtype==ev_union?\"<union>\":\"<struct>\", structtype, false);\n\t}\n\telse\n\t{\n\t\tQCC_type_t *parenttype;\n\t\tchar *tname = QCC_PR_ParseName();\n\n\t\tif (structtype == ev_struct && QCC_PR_CheckToken(\":\"))\n\t\t{\n\t\t\tchar *parentname = QCC_PR_ParseName();\n\t\t\tparenttype = QCC_TypeForName(parentname);\n\t\t\tif (!parenttype)\n\t\t\t\tQCC_PR_ParseError(ERR_NOTANAME, \"Parent type %s was not yet defined\", parentname);\n\t\t\tif (parenttype->type != ev_struct)\n\t\t\t\tQCC_PR_ParseError(ERR_NOTANAME, \"Parent type %s is not a struct\", parentname);\n\t\t}\n\t\telse\n\t\t\tparenttype = NULL;\n\n\t\tnewt = QCC_TypeForName(tname);\n\t\tif (!newt)\n\t\t{\n\t\t\tnewt = QCC_PR_NewType(tname, ev_struct, true);\n\t\t\tnewt->parentclass = parenttype;\n\t\t}\n\t\telse if (!newt->size && !newt->parentclass)\n\t\t\tnewt->parentclass = parenttype;\n\t\telse if (parenttype && newt->parentclass != parenttype)\n\t\t\tQCC_PR_ParseError(ERR_NOTANAME, \"Redeclaration of struct with different parent type\");\n\n\t\t//struct declaration only, not definition.\n\t\tif (parenttype)\n\t\t\tQCC_PR_Expect(\"{\");\n\t\telse if (!QCC_PR_CheckToken(\"{\"))\n\t\t\treturn newt;\n\n\t\tif (newt->size)\n\t\t{\n//\t\t\tQCC_PR_ParseError(ERR_NOTANAME, \"%s %s is already defined\", structtype==ev_union?\"union\":\"struct\", newt->name);\n\t\t\toldtype = newt;\n\t\t\tnewt = QCC_PR_NewType(tname, ev_struct, false);\n\t\t\tnewt->parentclass = parenttype;\n\t\t}\n\t}\n\tif (newt->parentclass)\n\t\tnewt->size = newt->parentclass->size;\n\telse\n\t\tnewt->size=0;\n\tbitsize = newt->size<<5;\n\n\ttype = NULL;\n\n\tnewparm = NULL;\n\tfor (;;)\n\t{\n\t\t//in qc, functions are assignable references like anything else, so no modifiers means the qc will need to assign to it somewhere.\n\t\t//virtual functions are still references, but we initialise them somewhere\n\t\t//nonvirtual functions and static functions are kinda the same thing\n\t\tQCC_sref_t defaultval;\n\t\tif (QCC_PR_CheckToken(\"}\"))\n\t\t{\n\t\t\tif (newparm)\n\t\t\t\tQCC_PR_ParseError(ERR_EXPECTED, \"missing semi-colon\");\n\t\t\tbreak;\n\t\t}\n\t\telse if (QCC_PR_CheckToken(\";\"))\n\t\t{\n\t\t\tnewparm = NULL;\n\t\t\tcontinue;\n\t\t}\n\t\telse if (QCC_PR_CheckToken(\",\"))\n\t\t{\t//same as last type, unless initial/after-semicolon\n\t\t\tif (!newparm)\n\t\t\t\tQCC_PR_ParseError(ERR_EXPECTED, \"element missing type\");\n\t\t}\n\t\telse\n\t\t{\t//new type!\n\t\t\tif (newparm)\n\t\t\t\tQCC_PR_ParseError(ERR_EXPECTED, \"missing semi-colon\");\t//allow a missing semi-colon on functions, for mixed-style functions.\n\n\t\t\t//reset these...\n\t\t\tisnonvirt = false;\n\t\t\tisstatic = false;\n\t\t\tisvirt = false;\n\n\t\t\t//parse field modifiers\n\t\t\tif (QCC_PR_CheckKeyword(1, \"public\"))\n\t\t\t{\n\t\t\t\t/*ispublic = true;*/\n\t\t\t\tQCC_PR_Expect(\":\");\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\telse if (QCC_PR_CheckKeyword(1, \"private\"))\n\t\t\t{\n\t\t\t\t/*isprivate = true;*/\n\t\t\t\tQCC_PR_Expect(\":\");\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\telse if (QCC_PR_CheckKeyword(1, \"protected\"))\n\t\t\t{\n\t\t\t\t/*isprotected = true;*/\n\t\t\t\tQCC_PR_Expect(\":\");\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t//if (QCC_PR_CheckKeyword(1, \"nonvirtual\"))\n\t\t\t//\tisnonvirt = true;\n\t\t\t//else\n\t\t\tif (QCC_PR_CheckKeyword(1, \"static\"))\n\t\t\t\tisstatic = true;\n\t\t\telse if (QCC_PR_CheckKeyword(1, \"virtual\"))\n\t\t\t\tisvirt = true;\n//\t\t\t\telse if (QCC_PR_CheckKeyword(1, \"ignore\"))\n//\t\t\t\t\tisignored = true;\n//\t\t\t\telse if (QCC_PR_CheckKeyword(1, \"strip\"))\n//\t\t\t\t\tisignored = true;\n\n\t\t\t//now parse the actual type.\n\t\t\tnewparm = QCC_PR_ParseType(false, false, true);\n\t\t\tdefinedsomething = false;\n\t\t}\n\t\ttype = newparm;\n\n\t\twhile (QCC_PR_CheckToken(\"*\"))\n\t\t\ttype = QCC_PointerTypeTo(type);\n\n\t\tarraysize = 0;\n\t\tif (QCC_PR_CheckToken(\";\"))\n\t\t{\t//annonymous structs do weird scope stuff.\n\t\t\tif (!definedsomething && (type->type != ev_struct && type->type != ev_union))\n\t\t\t{\n\t\t\t\tif (flag_qcfuncs && type->type == ev_function && type->aux_type == newt && QCC_PR_PeekToken(\";\"))\n\t\t\t\t\tQCC_PR_ParseWarning(WARN_POINTLESSSTATEMENT, \"constructors are not supported in structs at this time\");\n\t\t\t\telse\n\t\t\t\t\tQCC_PR_ParseWarning(WARN_POINTLESSSTATEMENT, \"declaration does not declare anything (%s)\", newparm->name);\n\t\t\t}\n\t\t\tnewparm = NULL;\n\t\t\tparmname = \"\";\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (QCC_PR_CheckToken(\"(\"))\n\t\t\t{\n\t\t\t\tQCC_PR_CheckToken(\"*\");\t//this is fine...\n\t\t\t\tparmname = QCC_PR_ParseName();\n\t\t\t\tQCC_PR_Expect(\")\");\n\t\t\t}\n\t\t\telse\n\t\t\t\tparmname = QCC_PR_ParseName();\n\t\t\tdefinedsomething = true;\n\t\t\tif (QCC_PR_CheckToken(\"[\"))\n\t\t\t\ttype = QCC_PR_ParseArrayType(type, &arraysize);\t//Checks for [][y][x] arrays.\n\n\t\t\tif (QCC_PR_CheckToken(\"(\"))\n\t\t\t{\n\t\t\t\ttype = QCC_PR_ParseFunctionType(false, type);\n\t\t\t\tif (!type)\n\t\t\t\t\tQCC_PR_ParseError(ERR_BADNOTTYPE, \"expected function arg list\");\n\t\t\t}\n\t\t}\n\n\t\tif (type == newt || ((type->type == ev_struct || type->type == ev_union) && !type->size))\n\t\t{\n\t\t\tQCC_PR_ParseWarning(ERR_NOTANAME, \"type %s not fully defined yet\", type->name);\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (QCC_PR_CheckToken(\":\"))\n\t\t{\n\t\t\tint bits = QCC_PR_IntConstExpr();\n\t\t\tif (bits > 64)\n\t\t\t\tQCC_PR_ParseWarning(ERR_BADNOTTYPE, \"too many bits\");\n\t\t\telse if (type->type == ev_integer || type->type == ev_uint || type->type == ev_int64 || type->type == ev_uint64)\n\t\t\t{\n\t\t\t\tQCC_type_t *bitfld;\n\t\t\t\t//prmote to bigger if big.\n\t\t\t\tif (bits > 32 && type->type == ev_integer)\n\t\t\t\t\ttype = type_int64;\n\t\t\t\tif (bits > 32 && type->type == ev_uint)\n\t\t\t\t\ttype = type_uint64;\n\t\t\t\tbitfld = QCC_PR_NewType(\"bitfld\", ev_bitfld, false);\n\t\t\t\tbitfld->bits = bits;\n\t\t\t\tbitfld->size = type->size;\n\t\t\t\tbitfld->parentclass = type;\n\t\t\t\tbitfld->align = type->align;\t//use the parent's alignment, for some reason.\n\t\t\t\ttype = bitfld;\n\t\t\t}\n\t\t\telse\n\t\t\t\tQCC_PR_ParseWarning(ERR_BADNOTTYPE, \"bitfields must be integer types\");\n\t\t}\n\n\t\tif ((isnonvirt || isvirt) && type->type != ev_function)\n\t\t\tQCC_PR_ParseWarning(ERR_INTERNAL, \"[non]virtual members must be functions\");\n\n\t\t//static members are technically just funny-named globals, and do not generate fields.\n\t\tif (isnonvirt || isstatic || isvirt)\n\t\t{\t//either way its a regular global. the difference being static has no implicit this/self argument.\n\t\t\tQCC_def_t *d;\n\t\t\tchar membername[2048];\n\t\t\tif (!isstatic)\n\t\t\t\ttype = QCC_PR_MakeThiscall(type, newt);\n\n\t\t\tQC_snprintfz(membername, sizeof(membername), \"%s::%s\", newt->name, parmname);\n\t\t\td = QCC_PR_GetDef(type, membername, NULL, true, 0, (type->type==ev_function)?GDF_CONST:0);\n\t\t\tif (QCC_PR_CheckToken(\"=\") || (type->type == ev_function && QCC_PR_PeekToken(\"{\")))\n\t\t\t{\n//FIXME: methods cannot be compiled yet, as none of the fields are not actually defined yet.\n\t\t\t\tpr_classtype = newt;\n\t\t\t\tQCC_PR_ParseInitializerDef(d, 0);\n\t\t\t\tpr_classtype = NULL;\n\t\t\t}\n\t\t\tQCC_FreeDef(d);\n\t\t\tif (!QCC_PR_PeekToken(\",\"))\n\t\t\t\tnewparm = NULL;\n\n\t\t\tif (isvirt)\n\t\t\t{\n\t\t\t\tdefaultval.ofs = 0;\n\t\t\t\tdefaultval.cast = d->type;\n\t\t\t\tdefaultval.sym = d;\n\t\t\t}\n\t\t\telse\n\t\t\t\tcontinue;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tdefaultval.cast = NULL;\n\t\t\tif (QCC_PR_CheckToken(\"=\"))\n\t\t\t{\n\t\t\t\tdefaultval = QCC_PR_ParseDefaultInitialiser(type);\n\t\t\t\tQCC_PR_ParseWarning(ERR_INTERNAL, \"TODO: pre-initialised struct members are not implemented yet\");\n\t\t\t}\n\t\t}\n\n\t\tparms = realloc(parms, sizeof(*parms) * (numparms+4));\n\t\toldparm = QCC_PR_FindStructMember(newt, parmname, &ofs, &bitofs);\n\t\tif (type->align)\n\t\t{\n\t\t\tif (bitalign < type->align)\n\t\t\t\tbitalign = type->align;\t//bigger than that...\n\t\t}\n\t\telse\n\t\t\tbitalign = 32;\t//struct must be word aligned.\n\t\tif (oldparm && oldparm->arraysize == arraysize && !typecmp_lax(oldparm->type, type))\n\t\t{\n\t\t\tofs <<= 5;\n\t\t\tofs += bitofs;\n\t\t\tif (!isvirt)\n\t\t\t\tcontinue;\n\t\t}\n\t\telse if (structtype == ev_union)\n\t\t{\n\t\t\tofs = 0;\n\t\t\tif (type->bits)\n\t\t\t{\n\t\t\t\tif (bitsize < type->bits*(arraysize?arraysize:1))\n\t\t\t\t\tbitsize = type->bits*(arraysize?arraysize:1);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (bitsize < 32*type->size*(arraysize?arraysize:1))\n\t\t\t\t\tbitsize = 32*type->size*(arraysize?arraysize:1);\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (type->bits)\n\t\t\t{\n\t\t\t\tif (type->bits < type->align && type->type == ev_bitfld)\n\t\t\t\t{\t//only realigns when it cross its parent's alignment boundary.\n\t\t\t\t\tif ((bitsize&(type->align-1)) + type->bits > type->align)\n\t\t\t\t\t\tbitsize = (bitsize+type->align-1)&~(type->align-1);\t//these must be aligned, so we can take pointers etc.\n\t\t\t\t}\n\t\t\t\telse if (type->align)\n\t\t\t\t\tbitsize = (bitsize+type->align-1)&~(type->align-1);\t//these must be aligned, so we can take pointers etc.\n\t\t\t\tofs = bitsize;\n\t\t\t\tbitsize += type->bits*(arraysize?arraysize:1);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tbitsize = (bitsize+31)&~31;\t//provide padding to keep non-bitfield stuff word aligned\n\t\t\t\tofs = bitsize;\n\t\t\t\tbitsize += 32 * type->size*(arraysize?arraysize:1);\n\t\t\t}\n\t\t}\n\n\t\tparms[numparms].ofs = ofs>>5;\n\t\tparms[numparms].bitofs = ofs - (parms[numparms].ofs<<5);\n\t\tparms[numparms].arraysize = arraysize;\n\t\tparms[numparms].out = false;\n\t\tparms[numparms].optional = false;\n\t\tparms[numparms].isvirtual = isvirt;\n\t\tparms[numparms].paramname = parmname;\n\t\tparms[numparms].type = type;\n\t\tparms[numparms].defltvalue = defaultval;\n\t\tnumparms++;\n\n\t\t/*if (type->type == ev_vector && arraysize == 0)\n\t\t{\t//add in vec_x/y/z members too...?\n\t\t\tint c;\n\t\t\tfor (c = 0; c < 3; c++)\n\t\t\t{\n\t\t\t\tparms[numparms].ofs = ofs + c;\n\t\t\t\tparms[numparms].arraysize = arraysize;\n\t\t\t\tparms[numparms].out = false;\n\t\t\t\tparms[numparms].optional = true;\n\t\t\t\tparms[numparms].isvirtual = isvirt;\n\t\t\t\tparms[numparms].paramname = qccHunkAlloc(strlen(parmname)+3);\n\t\t\t\tsprintf(parms[numparms].paramname, \"%s_%c\", parmname, 'x'+c);\n\t\t\t\tparms[numparms].type = type_float;\n\t\t\t\tparms[numparms].defltvalue = nullsref;\n\t\t\t\tnumparms++;\n\t\t\t}\n\t\t}*/\n\t}\n\n\tif (bitalign)\t//compute tail padding.\n\t\tbitsize = (bitsize+bitalign-1)&~(bitalign-1);\n\n\tnewt->size = (bitsize+31)>>5;\t//FIXME: change to bytes, for pointers.\n\tnewt->align = bitalign;\n\tif (bitalign&31)\n\t\tnewt->bits = bitsize;\n\telse\n\t\tnewt->bits = 0;\t//all word aligned. nothing special going on sizewise (maybe inside though).\n\n\tif (!numparms)\n\t\tQCC_PR_ParseError(ERR_NOTANAME, \"%s %s has no members\", structtype==ev_union?\"union\":\"struct\", newt->name);\n\n\tnewt->num_parms = numparms;\n\tnewt->params = qccHunkAlloc(sizeof(*type->params) * numparms);\n\tmemcpy(newt->params, parms, sizeof(*type->params) * numparms);\n\tfree(parms);\n\n\tif (oldtype)\n\t{\n\t\tif (typecmp_strict(newt, oldtype))\n\t\t\tQCC_PR_ParseError(ERR_NOTANAME, \"%s %s redeclared differently\", structtype==ev_union?\"union\":\"struct\", newt->name);\n\t\tnewt = oldtype;\n\t}\n\n\treturn newt;\n}\n\nQCC_type_t *QCC_PR_ParseEntClass(void)\n{\n\tQCC_type_t *newt, *type, *fieldtype, *newparm;\n\tchar membername[2048];\n\tchar *classname;\n\tint forwarddeclaration;\n\tint numparms = 0;\n\tstruct QCC_typeparam_s *parms = NULL;\n\tchar *parmname;\n\tint arraysize;\n\tpbool redeclaration;\n\tint basicindex;\n\tQCC_def_t *d;\n\tQCC_type_t *pc;\n\tQCC_type_t *basetype;\n\tpbool found = false;\n\tint assumevirtual = 0;\t//0=erk, 1=yes, -1=no\n\n\tparmname = QCC_PR_ParseName();\n\tclassname = qccHunkAlloc(strlen(parmname)+1);\n\tstrcpy(classname, parmname);\n\n\tnewt = 0;\n\n\tif (QCC_PR_CheckToken(\":\"))\n\t{\n\t\tchar *parentname = QCC_PR_ParseName();\n\t\tfieldtype = QCC_TypeForName(parentname);\n\t\tif (!fieldtype)\n\t\t\tQCC_PR_ParseError(ERR_NOTANAME, \"Parent class %s was not yet defined\", parentname);\n\n\t\t//FIXME: we should allow inheriting from 'object' too.\n\t\tif (fieldtype->type != ev_entity)\n\t\t\tQCC_PR_ParseError(ERR_NOTANAME, \"Parent type %s is not a class/entity\", parentname);\n\t\tforwarddeclaration = false;\n\n\t\tQCC_PR_Expect(\"{\");\n\t}\n\telse\n\t{\n\t\tfieldtype = type_entity;\n\t\tforwarddeclaration = !QCC_PR_CheckToken(\"{\");\n\t}\n\n\tnewt = QCC_TypeForName(classname);\n\tif (newt && newt->num_parms != 0)\n\t\tredeclaration = true;\n\telse\n\t\tredeclaration = false;\n\n\tif (!newt)\n\t{\n\t\tnewt = QCC_PR_NewType(classname, fieldtype->type, true);\n\t\tnewt->size=type_entity->size;\n\t}\n\n\ttype = NULL;\n\n\tif (forwarddeclaration)\n\t\treturn newt;\n\n\tif (pr_scope)\n\t\tQCC_PR_ParseError(ERR_REDECLARATION, \"Declaration of class %s%s%s within function\", col_type,classname,col_none);\n\tif (redeclaration && fieldtype != newt->parentclass)\n\t\tQCC_PR_ParseError(ERR_REDECLARATION, \"Parent class changed on redeclaration of %s%s%s\", col_type,classname,col_none);\n\tnewt->parentclass = fieldtype;\n\tnewt->filen = s_filen;\n\tnewt->line = pr_source_line;\n\n\tif (QCC_PR_CheckToken(\",\"))\n\t\tQCC_PR_ParseError(ERR_NOTANAME, \"member missing name\");\n\twhile (!QCC_PR_CheckToken(\"}\"))\n\t{\n\t\tunsigned int gdf_flags;\n\n\t\tpbool wasvirt = false;\n\t\tpbool wasnonvirt = false;\n\t\tpbool wasstatic = false;\n\t\tpbool isconst = false;\n\t\tpbool isvar = false;\n\t\tpbool isignored = false;\n\t\tpbool isinline = false;\n\t\tpbool isget = false;\n\t\tpbool isset = false;\n//\t\tpbool ispublic = false;\n//\t\tpbool isprivate = false;\n//\t\tpbool isprotected = false;\n\t\tpbool firstkeyword = true;\n\n\t\twhile(1)\n\t\t{\n\t\t\tif (QCC_PR_CheckKeyword(1, \"nonvirtual\"))\n\t\t\t{\n\t\t\t\tif (firstkeyword && QCC_PR_CheckToken(\":\"))\n\t\t\t\t{\t//fteqcc-specific\n\t\t\t\t\tassumevirtual = -1;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\twasnonvirt = true;\n\t\t\t}\n\t\t\telse if (QCC_PR_CheckKeyword(1, \"static\"))\n\t\t\t\twasstatic = true;\n\t\t\telse if (QCC_PR_CheckKeyword(1, \"const\"))\n\t\t\t\tisconst = wasstatic = true;\n\t\t\telse if (QCC_PR_CheckKeyword(1, \"var\"))\n\t\t\t\tisvar = true;\n\t\t\telse if (QCC_PR_CheckKeyword(1, \"virtual\"))\n\t\t\t{\n\t\t\t\tif (firstkeyword && QCC_PR_CheckToken(\":\"))\n\t\t\t\t{\t//fteqcc-specific\n\t\t\t\t\tassumevirtual = 1;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\twasvirt = true;\n\t\t\t}\n\t\t\telse if (QCC_PR_CheckKeyword(1, \"ignore\"))\n\t\t\t\tisignored = true;\n\t\t\telse if (QCC_PR_CheckKeyword(1, \"strip\"))\n\t\t\t\tisignored = true;\n\t\t\telse if (QCC_PR_CheckKeyword(1, \"inline\"))\n\t\t\t\tisinline = true;\n\t\t\telse if (QCC_PR_CheckKeyword(1, \"get\"))\n\t\t\t\tisget = true;\n\t\t\telse if (QCC_PR_CheckKeyword(1, \"set\"))\n\t\t\t\tisset = true;\n\t\t\telse if (firstkeyword && QCC_PR_CheckKeyword(1, \"public\"))\n\t\t\t{\n\t\t\t\t/*ispublic = true;*/\n\t\t\t\tQCC_PR_Expect(\":\");\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\telse if (firstkeyword && QCC_PR_CheckKeyword(1, \"private\"))\n\t\t\t{\n\t\t\t\tQCC_PR_Expect(\":\");\n\t\t\t\t/*isprivate = true; */\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\telse if (firstkeyword && QCC_PR_CheckKeyword(1, \"protected\"))\n\t\t\t{\n\t\t\t\tQCC_PR_Expect(\":\");\n\t\t\t\t/*isprotected = true;*/ QCC_PR_Expect(\":\");\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\telse\n\t\t\t\tbreak;\n\t\t\tfirstkeyword = false;\n\t\t}\n\t\tif (isget || isset)\n\t\t{\n\t\t\tif (wasvirt)\n\t\t\t\tQCC_PR_ParseWarning(ERR_INTERNAL, \"virtual accessors are not supported at this time\");\n\t\t\tif (wasstatic)\n\t\t\t\tQCC_PR_ParseError(ERR_INTERNAL, \"static accessors are not supported\");\n\t\t\tQCC_PR_ParseAccessorMember(newt, isinline, isset);\n\t\t\tQCC_PR_CheckToken(\";\");\n\t\t\tcontinue;\n\t\t}\n\n\t\tbasetype = QCC_PR_ParseType(false, false, false);\n\n\t\tif (!basetype)\n\t\t\tQCC_PR_ParseError(ERR_INTERNAL, \"In class %s, expected type, found %s\", classname, pr_token);\n\n\t\tif (basetype->type == ev_struct || basetype->type == ev_union)\t//we wouldn't be able to handle it.\n\t\t\tQCC_PR_ParseError(ERR_INTERNAL, \"Struct or union in class %s\", classname);\n\n\n\t\tfor (;basetype;)\n\t\t{\n\t\t\tpbool havebody = false;\n\t\t\tpbool isnull = false;\n\t\t\tpbool isstatic = wasstatic;\n\t\t\tpbool isvirt = wasvirt;\n\t\t\tpbool isnonvirt = wasnonvirt;\n\n\t\t\tif (flag_qcfuncs && basetype->type == ev_function && basetype->aux_type == newt && QCC_PR_PeekToken(\";\"))\n\t\t\t{\t//C++ style constructor (ie: missing return type followed by class name)\n\t\t\t\t//swap the class out for the appropriate function type...\n\t\t\t\tnewparm = QCC_PR_GenFunctionType(type_void, basetype->params, basetype->num_parms);\n\t\t\t\tparmname = classname;\n\t\t\t\tarraysize = 0;\n\t\t\t}\n\t\t\telse if (!flag_qcfuncs && basetype == newt && QCC_PR_CheckToken(\"(\"))\n\t\t\t{\n\t\t\t\tnewparm = QCC_PR_ParseFunctionType(false, type_void);\n\t\t\t\tif (!newparm)\n\t\t\t\t\tQCC_PR_ParseError(ERR_BADNOTTYPE, \"expected function arg list\");\n\t\t\t\tparmname = classname;\n\t\t\t\tarraysize = 0;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tparmname = QCC_PR_ParseName();\n\t\t\t\tif (QCC_PR_CheckToken(\"[\"))\n\t\t\t\t{\n\t\t\t\t\tarraysize = QCC_PR_IntConstExpr();\n\t\t\t\t\tQCC_PR_Expect(\"]\");\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tarraysize = 0;\n\n\t\t\t\tif (QCC_PR_CheckToken(\"(\"))\n\t\t\t\t{\n\t\t\t\t\t//int fnc(), fld; is valid.\n\t\t\t\t\tnewparm = QCC_PR_ParseFunctionType(false, basetype);\n\t\t\t\t\tif (!newparm)\n\t\t\t\t\t\tQCC_PR_ParseError(ERR_BADNOTTYPE, \"expected function arg list\");\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tnewparm = basetype;\n\t\t\t}\n\n\t\t\tif (newparm->type == ev_function)\n\t\t\t{\n\t\t\t\tif (!strcmp(classname, parmname))\n\t\t\t\t{\n\t\t\t\t\tif (isstatic)\n\t\t\t\t\t\tQCC_PR_ParseWarning(WARN_MISSINGMEMBERQUALIFIER, \"Constructor %s::%s should not be static.\", classname, pr_token);\n\t\t\t\t\tif (!isvirt)\n\t\t\t\t\t\tisnonvirt = true;//silently promote constructors to static\n\t\t\t\t\tif (newparm->aux_type->type != ev_void)\n\t\t\t\t\t\tQCC_PR_ParseWarning(WARN_MISSINGMEMBERQUALIFIER, \"Constructor %s::%s does not return void.\", classname, pr_token);\n\t\t\t\t\tif (newparm->num_parms != 0 || newparm->vargs)\n\t\t\t\t\t\tQCC_PR_ParseWarning(WARN_MISSINGMEMBERQUALIFIER, \"Constructor arguments are not supported at this time %s::%s. Use named fields instead.\", classname, pr_token);\n\t\t\t\t}\n\t\t\t\telse if (!isvirt && !isnonvirt && !isstatic)\n\t\t\t\t{\n\t\t\t\t\tif (assumevirtual == 1)\n\t\t\t\t\t\tisvirt = true;\n\t\t\t\t\telse if (assumevirtual == -1)\n\t\t\t\t\t\tisnonvirt = true;\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tQCC_PR_ParseWarning(WARN_MISSINGMEMBERQUALIFIER, \"%s::%s was not qualified. Assuming non-virtual.\", classname, parmname);\n\t\t\t\t\t\tisnonvirt = true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse if (isvirt+isnonvirt+isstatic != 1)\n\t\t\t\t\tQCC_PR_ParseError(ERR_INTERNAL, \"Multiple conflicting qualifiers on %s::%s.\", classname, pr_token);\n\n\t\t\t\tif (isstatic)\n\t\t\t\t{\t//static and non-virtual functions differ only in that static should not have a 'this' available. however there's no hiding 'self' for calling in to other functions.\n\t\t\t\t\tisstatic = false;\n\t\t\t\t\tisnonvirt = true;\n//\t\t\t\t\tQCC_PR_ParseError(ERR_INTERNAL, \"%s::%s static member functions are not supported at this time.\", classname, parmname);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (isvirt||isnonvirt)\n\t\t\t\t\tQCC_Error(ERR_INTERNAL, \"virtual keyword on member that is not a function\");\n\t\t\t\tif (isinline)\n\t\t\t\t\tQCC_Error(ERR_INTERNAL, \"inline keyword on member that is not a function\");\n\t\t\t}\n\n\t\t\tif (QCC_PR_CheckToken(\"=\"))\n\t\t\t\thavebody = true;\n\t\t\telse if (newparm->type == ev_function && QCC_PR_PeekToken(\"{\"))\n\t\t\t\thavebody = true;\n\t\t\tif (isinline && (!havebody || isvirt))\n\t\t\t\tQCC_Error(ERR_INTERNAL, \"inline keyword on function prototype or virtual function\");\n\n\t\t\tgdf_flags = 0;\n\t\t\tif ((newparm->type == ev_function && !arraysize && !isvar) || isconst)\n\t\t\t\tgdf_flags = GDF_CONST;\n\n\t\t\tif (havebody)\n\t\t\t{\n\t\t\t\tQCC_def_t *def;\n\t\t\t\tif (pr_scope)\n\t\t\t\t\tQCC_Error(ERR_INTERNAL, \"Nested function declaration\");\n\n\t\t\t\tisnull = (QCC_PR_CheckImmediate(\"0\") || QCC_PR_CheckImmediate(\"0i\"));\n\t\t\t\tQC_snprintfz(membername, sizeof(membername), \"%s::%s\", classname, parmname);\n\t\t\t\tif (isnull)\n\t\t\t\t{\n\t\t\t\t\tif (isignored)\n\t\t\t\t\t\tdef = NULL;\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tdef = QCC_PR_GetDef(newparm, membername, NULL, true, 0, gdf_flags);\n\t\t\t\t\t\tdef->symboldata[def->ofs].function = 0;\n\t\t\t\t\t\tdef->initialized = 1;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tif (isignored)\n\t\t\t\t\t\tdef = NULL;\n\t\t\t\t\telse\n\t\t\t\t\t\tdef = QCC_PR_GetDef(newparm, membername, NULL, true, 0, gdf_flags);\n\n\t\t\t\t\tif (newparm->type != ev_function && !isstatic)\n\t\t\t\t\t\tQCC_Error(ERR_INTERNAL, \"Can only initialise member functions\");\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tif (autoprototype || isignored)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (QCC_PR_CheckToken(\"[\"))\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\twhile (!QCC_PR_CheckToken(\"]\"))\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tif (pr_token_type == tt_eof)\n\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t\tQCC_PR_Lex();\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tQCC_PR_Expect(\"{\");\n\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tint blev = 1;\n\t\t\t\t\t\t\t\t//balance out the { and }\n\t\t\t\t\t\t\t\twhile(blev)\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tif (pr_token_type == tt_eof)\n\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t\tif (QCC_PR_CheckToken(\"{\"))\n\t\t\t\t\t\t\t\t\t\tblev++;\n\t\t\t\t\t\t\t\t\telse if (QCC_PR_CheckToken(\"}\"))\n\t\t\t\t\t\t\t\t\t\tblev--;\n\t\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t\t\tQCC_PR_Lex();\t//ignore it.\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tpr_classtype = newt;\n\t\t\t\t\t\t\tQCC_PR_ParseInitializerDef(def, 0);\n\t\t\t\t\t\t\tpr_classtype = NULL;\n\t\t\t\t\t\t\t/*\n\t\t\t\t\t\t\tf = QCC_PR_ParseImmediateStatements (def, newparm);\n\t\t\t\t\t\t\tpr_classtype = NULL;\n\t\t\t\t\t\t\tpr_scope = NULL;\n\t\t\t\t\t\t\tdef->symboldata[def->ofs].function = f - functions;\n\t\t\t\t\t\t\tf->def = def;\n\t\t\t\t\t\t\tdef->initialized = 1;*/\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (def)\n\t\t\t\t\tQCC_FreeDef(def);\n\n\t\t\t\tif (!isvirt && !isignored)\n\t\t\t\t{\n\t\t\t\t\tQCC_def_t *fdef;\n\t\t\t\t\tQCC_type_t *pc;\n\t\t\t\t\tunsigned int i;\n\n\t\t\t\t\tfor (pc = newt->parentclass; pc; pc = pc->parentclass)\n\t\t\t\t\t{\n\t\t\t\t\t\tfor (i = 0; i < pc->num_parms; i++)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (!strcmp(pc->params[i].paramname, parmname))\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tQCC_PR_ParseWarning(WARN_DUPLICATEDEFINITION, \"%s::%s is virtual inside parent class '%s'. Did you forget the 'virtual' keyword?\", newt->name, parmname, pc->name);\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (i < pc->num_parms)\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tif (!pc)\n\t\t\t\t\t{\n\t\t\t\t\t\tfdef = QCC_PR_GetDef(NULL, parmname, NULL, false, 0, GDF_CONST);\n\t\t\t\t\t\tif (fdef && fdef->type->type == ev_field)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tQCC_PR_ParseWarning(WARN_DUPLICATEDEFINITION, \"%s::%s is virtual inside parent class 'entity'. Did you forget the 'virtual' keyword?\", newt->name, parmname);\n\t\t\t\t\t\t\tQCC_PR_ParsePrintDef(0, fdef);\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (fdef)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tQCC_PR_ParseWarning(WARN_DUPLICATEDEFINITION, \"%s::%s shadows a global\", newt->name, parmname);\n\t\t\t\t\t\t\tQCC_PR_ParsePrintDef(0, fdef);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (fdef)\n\t\t\t\t\t\t\tQCC_FreeDef(fdef);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (!QCC_PR_CheckToken(\",\"))\n\t\t\t{\n\t\t\t\tQCC_PR_Expect(\";\");\n\t\t\t\tbasetype = NULL;\n\t\t\t}\n\n\t\t\tif (isignored)\t//member doesn't really exist\n\t\t\t\tcontinue;\n\n\t\t\t//static members are technically just funny-named globals, and do not generate fields.\n\t\t\tif (isnonvirt || isstatic || (newparm->type == ev_function && !arraysize))\n\t\t\t{\n\t\t\t\tQC_snprintfz(membername, sizeof(membername), \"%s::%s\", classname, parmname);\n\t\t\t\tQCC_FreeDef(QCC_PR_GetDef(newparm, membername, NULL, true, 0, gdf_flags));\n\n\t\t\t\tif (isnonvirt || isstatic)\n\t\t\t\t\tcontinue;\n\t\t\t}\n\n\n\t\t\tfieldtype = QCC_PR_NewType(parmname, ev_field, false);\n\t\t\tfieldtype->aux_type = newparm;\n\t\t\tfieldtype->size = newparm->size;\n\n\t\t\tparms = realloc(parms, sizeof(*parms) * (numparms+1));\n\t\t\tparms[numparms].ofs = 0;\n\t\t\tparms[numparms].bitofs = 0;\n\t\t\tparms[numparms].out = false;\n\t\t\tparms[numparms].optional = false;\n\t\t\tparms[numparms].isvirtual = isvirt;\n\t\t\tparms[numparms].paramname = parmname;\n\t\t\tparms[numparms].arraysize = arraysize;\n\t\t\tparms[numparms].type = newparm;\n\t\t\tparms[numparms].defltvalue.cast = NULL;\n\n\t\t\tbasicindex = 0;\n\t\t\tfound = false;\n\t\t\tfor(pc = newt; pc && !found; pc = pc->parentclass)\n\t\t\t{\n\t\t\t\tstruct QCC_typeparam_s *pp;\n\t\t\t\tint numpc;\n\t\t\t\tint i;\n\t\t\t\tif (pc == newt)\n\t\t\t\t{\n\t\t\t\t\tpp = parms;\n\t\t\t\t\tnumpc = numparms;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tpp = pc->params;\n\t\t\t\t\tnumpc = pc->num_parms;\n\t\t\t\t}\n\t\t\t\tfor (i = 0; i < numpc; i++)\n\t\t\t\t{\n\t\t\t\t\tif (pp[i].type->type == newparm->type)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (!strcmp(pp[i].paramname, parmname))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (typecmp(pp[i].type, newparm))\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tchar bufc[256];\n\t\t\t\t\t\t\t\tchar bufp[256];\n\t\t\t\t\t\t\t\tTypeName(pp[i].type, bufp, sizeof(bufp));\n\t\t\t\t\t\t\t\tTypeName(newparm, bufc, sizeof(bufc));\n\t\t\t\t\t\t\t\tQCC_PR_ParseError(0, \"%s defined as %s in %s, but %s in %s\\n\", parmname, bufc, newt->name, bufp, pc->name);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbasicindex = pp[i].ofs;\n\t\t\t\t\t\t\tfound = true;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif ((unsigned int)basicindex < pp[i].ofs+pp[i].type->size*(pp[i].arraysize?pp[i].arraysize:1))\t//if we found one with the index\n\t\t\t\t\t\t\tbasicindex = pp[i].ofs+pp[i].type->size*(pp[i].arraysize?pp[i].arraysize:1);\t//make sure we don't union it.\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\n\t\t\t/* iterate over every parent-class and see if our method already exists in conflicting nonvirtual type form */\n\t\t\tif (isvirt)\n\t\t\t{\n\t\t\t\tfor(pc = newt; pc && !found; pc = pc->parentclass)\n\t\t\t\t{\n\t\t\t\t\tstruct QCC_typeparam_s *pp;\n\t\t\t\t\tint numpc;\n\t\t\t\t\tint i;\n\t\t\t\t\tfound = false;\n\n\t\t\t\t\tif (pc == newt)\n\t\t\t\t\t\tcontinue;\n\n\t\t\t\t\tpp = parms;\n\t\t\t\t\tnumpc = numparms;\n\n\t\t\t\t\t/* iterate over all of the virtual methods */\n\t\t\t\t\tfor (i = 0; i < numpc; i++)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (pp[i].type->type == newparm->type)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t/* if we found it, abandon this loop - we still have to check the other classes however */\n\t\t\t\t\t\t\tif (!strcmp(pp[i].paramname, parmname))\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tfound = true;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t/* we didn't find it as a field... so check if it exists as a nonvirtual method */\n\t\t\t\t\tif (found == false)\n\t\t\t\t\t{\n\t\t\t\t\t\tQC_snprintfz(membername, sizeof(membername), \"%s::%s\", pc->name, parmname);\n\n\t\t\t\t\t\t/* if we found it, game over */\n\t\t\t\t\t\tif (QCC_PR_GetDef(NULL, membername, NULL, false, 0, 0))\n\t\t\t\t\t\t\tQCC_PR_ParseError(0, \"%s defined as virtual in %s, but nonvirtual in %s\\n\", parmname, newt->name, pc->name);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tparms[numparms].ofs = basicindex;\t//ulp, its new\n\t\t\tnumparms++;\n\n\t\t\tif (found)\n\t\t\t\tcontinue;\n\n\t\t\tif (!*basictypes[newparm->type])\n\t\t\t\tQCC_PR_ParseError(0, \"members of type %s are not supported (%s::%s)\\n\", basictypenames[newparm->type], classname, parmname);\n\n\t\t\t//make sure the union is okay\n\t\t\td = QCC_PR_GetDef(NULL, parmname, NULL, 0, 0, GDF_CONST);\n\t\t\tif (d)\n\t\t\t\tbasicindex = 0;\n\t\t\telse\n\t\t\t{\t//don't go all weird with unioning generic fields\n\t\t\t\tQC_snprintfz(membername, sizeof(membername), \"::*%s\", basictypenames[newparm->type]);\n\t\t\t\td = QCC_PR_GetDef(NULL, membername, NULL, 0, 0, GDF_CONST);\n\t\t\t\tif (!d)\n\t\t\t\t{\n\t\t\t\t\td = QCC_PR_GetDef(QCC_PR_FieldType(*basictypes[newparm->type]), membername, NULL, 2, 0, GDF_CONST|GDF_POSTINIT|GDF_USED);\n//\t\t\t\t\tfor (i = 0; (unsigned int)i < newparm->size*(arraysize?arraysize:1); i++)\n//\t\t\t\t\t\td->symboldata[i]._int = pr.size_fields+i;\n//\t\t\t\t\tpr.size_fields += i;\n\n\t\t\t\t\td->used = true;\n\t\t\t\t\td->referenced = true;\t//always referenced, so you can inherit safely.\n\t\t\t\t}\n\t\t\t\tif (d->arraysize < basicindex+(arraysize?arraysize:1))\n\t\t\t\t{\n\t\t\t\t\tif (d->symboldata)\n\t\t\t\t\t\tQCC_PR_ParseError(ERR_INTERNAL, \"array members are kinda limited, sorry. try rearranging them or adding padding for alignment\\n\");\t//FIXME: add relocs to cope with this all of a type can then be contiguous and thus allow arrays.\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tint newsize = basicindex+(arraysize?arraysize:1);\n\t\t\t\t\t\tif (d->type->type == ev_union || d->type->type == ev_struct)\n\t\t\t\t\t\t\td->arraysize = newsize;\n\t\t\t\t\t\telse while(d->arraysize < newsize)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tQC_snprintfz(membername, sizeof(membername), \"::%s[%i]\", basictypenames[newparm->type], d->arraysize/d->type->size);\n\t\t\t\t\t\t\tQCC_PR_DummyDef(d->type, membername, d->scope, 0, d, d->arraysize, true, GDF_CONST);\n\t\t\t\t\t\t\td->arraysize+=d->type->size;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tQCC_FreeDef(d);\n\n\t\t\t//and make sure we can do member::__fname\n\t\t\t//actually, that seems pointless.\n\t\t\tQC_snprintfz(membername, sizeof(membername), \"%s::\"MEMBERFIELDNAME, classname, parmname);\n//\t\t\t\texterns->Printf(\"define %s -> %s\\n\", membername, d->name);\n\t\t\td = QCC_PR_DummyDef(fieldtype, membername, pr_scope, arraysize, d, basicindex, true, (isnull?0:GDF_CONST)|(opt_classfields?GDF_STRIP:0));\n\t\t\td->referenced = true;\t//always referenced, so you can inherit safely.\n\t\t}\n\t}\n\n\tif (redeclaration)\n\t{\n\t\tint i;\n\t\tredeclaration = newt->num_parms != numparms;\n\n\t\tfor (i = 0; i < numparms && (unsigned int)i < newt->num_parms; i++)\n\t\t{\n\t\t\tif (newt->params[i].arraysize != parms[i].arraysize || typecmp(newt->params[i].type, parms[i].type) || strcmp(newt->params[i].paramname, parms[i].paramname))\n\t\t\t{\n\t\t\t\tQCC_PR_ParseError(ERR_REDECLARATION, \"Incompatible redeclaration of class %s. %s differs.\", classname, parms[i].paramname);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (newt->num_parms != numparms)\n\t\t\tQCC_PR_ParseError(ERR_REDECLARATION, \"Incompatible redeclaration of class %s.\", classname);\n\t}\n\telse\n\t{\n\t\tnewt->num_parms = numparms;\n\t\tnewt->params = qccHunkAlloc(sizeof(*type->params) * numparms);\n\t\tmemcpy(newt->params, parms, sizeof(*type->params) * numparms);\n\t}\n\tfree(parms);\n\n\t{\n\t\tQCC_def_t *d;\n\t\t//if there's a constructor, make sure the spawnfunc_ function is defined so that its available to maps.\n\t\tQC_snprintfz(membername, sizeof(membername), \"spawnfunc_%s\", classname);\n\t\td = QCC_PR_GetDef(type_function, membername, NULL, true, 0, GDF_CONST);\n\t\td->funccalled = true;\n\t\td->referenced = true;\n\t\tQCC_FreeDef(d);\n\t}\n\n\treturn newt;\n}\n\npbool type_inlinefunction;\n/*newtype=true: creates a new type always\n  silentfail=true: function is permitted to return NULL if it was not given a type, otherwise never returns NULL\n*/\nQCC_type_t *QCC_PR_ParseType (int newtype, pbool silentfail, pbool ignoreptr)\n{\n\tQCC_type_t\t*newt;\n\tQCC_type_t\t*type;\n\tchar\t*name;\n\n\ttype_inlinefunction = false;\t//doesn't really matter so long as its not from an inline function type\n\n//\tint ofs;\n\n\tif (QCC_PR_CheckKeyword(keyword_const, \"const\"))\n\t{\n\t\tQCC_PR_ParseWarning (WARN_IGNOREDKEYWORD, \"ignoring unsupported const keyword\");\n\t\tsilentfail = false;\t//FIXME\n\t}\n\tif (QCC_PR_CheckKeyword(keyword_volatile, \"volatile\"))\t//we don't really support this - everything is volatile.\n\t\tsilentfail = false;\n\n\tif (QCC_PR_PeekToken (\"...\") )\t//this is getting stupid\n\t{\n\t\tQCC_PR_LexWhitespace (false);\n\t\tif (*pr_file_p == '(')\n\t\t{\t//work around gmqcc's \"(...(\" being misinterpreted as a cast syntax error, instead abort here so it can be treated as an intrinsic (with args) instead.\n\t\t\tif (silentfail)\n\t\t\t\treturn NULL;\n\t\t\tQCC_PR_ParseError (ERR_NOTATYPE, \"\\\"%s\\\" is not a type\", pr_token);\n\t\t}\n\t\tQCC_PR_Lex ();\n\n\t\ttype = QCC_PR_NewType(\"FIELD_TYPE\", ev_field, false);\n\t\ttype->aux_type = QCC_PR_ParseType (false, false, ignoreptr);\n\t\ttype->size = type->aux_type->size;\n\n\t\tnewt = QCC_PR_FindType (type);\n\t\ttype = QCC_PR_NewType(\"FIELD_TYPE\", ev_field, false);\n\t\ttype->aux_type = newt;\n\t\ttype->size = type->aux_type->size;\n\n\t\tnewt = QCC_PR_FindType (type);\n\t\ttype = QCC_PR_NewType(\"FIELD_TYPE\", ev_field, false);\n\t\ttype->aux_type = newt;\n\t\ttype->size = type->aux_type->size;\n\n\t\tif (newtype)\n\t\t\treturn type;\n\t\treturn QCC_PR_FindType (type);\n\t}\n\tif (QCC_PR_CheckToken (\"..\"))\t//so we don't end up with the user specifying '. .vector blah' (hexen2 added the .. token for array ranges)\n\t{\n\t\tnewt = QCC_PR_NewType(\"FIELD_TYPE\", ev_field, false);\n\t\tnewt->aux_type = QCC_PR_ParseType (false, false, ignoreptr);\n\n\t\tnewt->size = newt->aux_type->size;\n\n\t\tnewt = QCC_PR_FindType (newt);\n\n\t\ttype = QCC_PR_NewType(\"FIELD_TYPE\", ev_field, false);\n\t\ttype->aux_type = newt;\n\n\t\ttype->size = type->aux_type->size;\n\n\t\tif (newtype)\n\t\t\treturn type;\n\t\treturn QCC_PR_FindType (type);\n\t}\n\tif (QCC_PR_CheckToken (\".\"))\n\t{\n\t\t//.float *foo; is annoying.\n\t\t//technically it is a pointer to a .float\n\t\t//most people will want a .(float*) foo;\n\t\t//so .*float will give you that.\n\t\t//however, we can't cope parsing that with regular types, so we support that ONLY when . was already specified.\n\t\t//this is pretty much an evil syntax hack.\n\t\tpbool ptr = QCC_PR_CheckToken (\"*\");\n\t\ttype = QCC_PR_ParseType(false, false, ignoreptr);\n\t\tif (!type)\n\t\t\tQCC_PR_ParseError(0, \"Expected type\\n\");\n\t\tif (ptr)\n\t\t\ttype = QCC_PointerTypeTo(type);\n\n\t\tname = qccHunkAlloc(strlen(type->name)+2);\n\t\t*name = '.';\n\t\tstrcpy(name+1, type->name);\n\n\t\tnewt = QCC_PR_NewType(name, ev_field, false);\n\t\tnewt->aux_type = type;\n\t\tnewt->size = newt->aux_type->size;\n\n\t\tif (newtype)\n\t\t\treturn newt;\n\t\treturn QCC_PR_FindType (newt);\n\t}\n\n\tname = pr_token;\n\tif (pr_token_type != tt_name)\n\t{\n\t\tif (silentfail)\n\t\t\treturn NULL;\n\t\tQCC_PR_ParseError (ERR_NOTATYPE, \"\\\"%s\\\" is not a type\", name);\n\t}\n//\tname = QCC_PR_CheckCompConstString(name);\n\n\t//accessors\n\tif (QCC_PR_CheckKeyword (keyword_accessor, \"accessor\"))\n\t{\n\t\tchar parentname[256];\n\t\tchar *accessorname;\n\t\tchar *funcname;\n\n\t\tnewt = NULL;\n\n\t\tfuncname = QCC_PR_ParseName();\n\t\taccessorname = qccHunkAlloc(strlen(funcname)+1);\n\t\tstrcpy(accessorname, funcname);\n\n\t\t/* Look to see if this type is already defined */\n\t\tnewt = QCC_TypeForName(accessorname);\n\t\tif (newt && newt->type != ev_accessor)\n\t\t\tQCC_PR_ParseError(ERR_NOTANAME, \"Type %s cannot be redefined as an accessor\", accessorname);\n\n\t\tif (QCC_PR_CheckToken(\":\"))\n\t\t{\n\t\t\ttype = QCC_PR_ParseType(false, false, false);\n\t\t\tif (type)\n\t\t\t\tTypeName(type, parentname, sizeof(parentname));\n\t\t\telse\n\t\t\t\tstrcpy(parentname, \"??\");\n\n\t\t\tif (!type || type->type == ev_struct || type->type == ev_union)\n\t\t\t\tQCC_PR_ParseError(ERR_NOTANAME, \"Accessor %s cannot be based upon %s\", accessorname, parentname);\n\t\t}\n\t\telse\n\t\t\ttype = NULL;\n\n\t\tif (!newt)\n\t\t{\n\t\t\tnewt = QCC_PR_NewType(accessorname, ev_accessor, true);\n\t\t\tnewt->size=type->size;\n\t\t}\n\t\tif (!newt->parentclass)\n\t\t{\n\t\t\tnewt->parentclass = type;\n\t\t\tif (!newt->parentclass || newt->parentclass->type == ev_struct || newt->parentclass->type == ev_union || newt->size != newt->parentclass->size)\n\t\t\t\tQCC_PR_ParseError(ERR_NOTANAME, \"Accessor %s cannot be based upon %s\", accessorname, parentname);\n\t\t}\n\t\telse if (type != newt->parentclass)\n\t\t{\n\t\t\tchar bufe[256];\n\t\t\tchar bufn[256];\n\t\t\tQCC_PR_ParseError(ERR_NOTANAME, \"Accessor %s basic type mismatch (%s, expected %s)\", accessorname,\n\t\t\t\tTypeName(type, bufn, sizeof(bufn)), TypeName(newt->parentclass, bufe, sizeof(bufe)));\n\t\t}\n\n\t\tif (QCC_PR_CheckToken(\"{\"))\n\t\t{\n\t\t\tpbool setnotget;\n\t\t\tpbool isinline;\n\n\t\t\tnewt->filen = s_filen;\n\t\t\tnewt->line = pr_source_line;\n\n\t\t\tdo\n\t\t\t{\n\t\t\t\tisinline = QCC_PR_CheckName(\"inline\");\n\n\t\t\t\tif (QCC_PR_CheckName(\"set\"))\n\t\t\t\t\tsetnotget = true;\n\t\t\t\telse if (QCC_PR_CheckName(\"get\"))\n\t\t\t\t\tsetnotget = false;\n\t\t\t\telse\n\t\t\t\t\tbreak;\n\n\t\t\t\tQCC_PR_ParseAccessorMember(newt, isinline, setnotget);\n\t\t\t} while (QCC_PR_CheckToken(\",\") || QCC_PR_CheckToken(\";\"));\n\t\t\tQCC_PR_Expect(\"}\");\n\t\t}\n\n\t\tif (newtype)\n\t\t\tnewt = QCC_PR_DuplicateType(newt, false);\n\t\treturn newt;\n\t}\n\n\tif (flag_qcfuncs && QCC_PR_CheckKeyword (keyword_class, \"class\"))\n\t\ttype = QCC_PR_ParseEntClass();\n\telse if (QCC_PR_CheckKeyword(keyword_enum, \"enum\"))\n\t\ttype = QCC_PR_ParseEnum(false);\n\telse if (QCC_PR_CheckKeyword(keyword_enumflags, \"enumflags\"))\n\t\ttype = QCC_PR_ParseEnum(true);\n\telse if (QCC_PR_CheckKeyword (keyword_union, \"union\"))\n\t\ttype = QCC_PR_ParseStruct(ev_union);\n\telse if (QCC_PR_CheckKeyword (keyword_struct, \"struct\"))\n\t\ttype = QCC_PR_ParseStruct(ev_struct);\t//defaults to public\n//\telse if (QCC_PR_CheckKeyword (keyword_class, \"class\"))\n//\t\tstructtype = ev_struct;\t//defaults to private. no other difference.\n\telse\n\t{\n\t\ttype = QCC_TypeForName(name);\n\t\tif (type)\n\t\t\tQCC_PR_Lex ();\n\t\telse\n\t\t{\n\t\t\tif (!*name)\n\t\t\t{\n\t\t\t\tQCC_PR_ParseError(ERR_NOTANAME, \"type missing name\");\n\t\t\t\treturn NULL;\n\t\t\t}\n\n\t\t\t//some reacc types...\n\t\t\tif (flag_acc && !stricmp(\"Void\", name))\n\t\t\t\ttype = type_void;\n\t\t\telse if (flag_acc && !stricmp(\"Real\", name))\n\t\t\t\ttype = type_float;\n\t\t\telse if (flag_acc && !stricmp(\"Vector\", name))\n\t\t\t\ttype = type_vector;\n\t\t\telse if (flag_acc && !stricmp(\"Object\", name))\n\t\t\t\ttype = type_entity;\n\t\t\telse if (flag_acc && !stricmp(\"String\", name))\n\t\t\t\ttype = type_string;\n\t\t\telse if (flag_acc && !stricmp(\"PFunc\", name))\n\t\t\t\ttype = type_function;\n\t\t\telse\n\t\t\t{\n\t\t\t\t//try and handle C's types, which have weird and obtuse combinations (like long long, long int, short int).\n\t\t\t\tpbool isokay = false;\n\t\t\t\tpbool issigned = false;\n\t\t\t\tpbool isunsigned = false;\n\t\t\t\tpbool islong = false;\n\t\t\t\tpbool isfloat = false;\n\t\t\t\tint bits = 0;\n\n\t#define longlongbits 64\n\t#define longbits (flag_ILP32?32:64)\n\n\t\t\t\tif (QCC_PR_CheckKeyword(keyword_register, \"register\"))\t//allow a leading register keyword. technically EVERYTHING is a register in qc, so we can just ignore this.\n\t\t\t\t\tisokay = true;\n\t\t\t\twhile(true)\n\t\t\t\t{\n\t\t\t\t\tif (!isunsigned && !issigned && QCC_PR_CheckKeyword(keyword_signed, \"signed\"))\n\t\t\t\t\t\tissigned = isokay = true;\n\t\t\t\t\telse if (!issigned && !isunsigned && QCC_PR_CheckKeyword(keyword_unsigned, \"unsigned\"))\n\t\t\t\t\t\tisunsigned = isokay = true;\n\t\t\t\t\telse if (!bits && QCC_PR_CheckKeyword(keyword_long, \"long\"))\n\t\t\t\t\t{\n\t\t\t\t\t\tif (islong)\n\t\t\t\t\t\t\tbits = longlongbits;\n\t\t\t\t\t\tislong = isokay = true;\n\t\t\t\t\t}\n\t\t\t\t\telse if ((!bits || bits==16) && (QCC_PR_CheckKeyword(keyword_int, \"int\") || QCC_PR_CheckKeyword(keyword_integer, \"integer\")))\n\t\t\t\t\t{\t//long int, short int, etc are allowed\n\t\t\t\t\t\tif (!bits)\n\t\t\t\t\t\t\tbits = 32;\n\t\t\t\t\t\tisokay = true;\n\t\t\t\t\t}\n\t\t\t\t\telse if (!bits && QCC_PR_CheckKeyword(keyword_short, \"short\"))\n\t\t\t\t\t\tbits = 16, isokay = true;\n\t\t\t\t\telse if (!bits && QCC_PR_CheckKeyword(keyword_char, \"char\"))\n\t\t\t\t\t\tbits = 8, isokay = true;\n\t\t\t\t\telse if (!bits && !issigned && !isunsigned && QCC_PR_CheckKeyword(true, \"_Bool\"))\t//c99\n\t\t\t\t\t{\n\t\t\t\t\t\tif (keyword_int)\n\t\t\t\t\t\t\ttype = type_bint;\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\ttype = type_bfloat;\n\t\t\t\t\t\tgoto wasctype;\n\t\t\t\t\t}\n\n\t\t\t\t\telse if (!bits && !islong && QCC_PR_CheckKeyword(keyword_float, \"float\"))\n\t\t\t\t\t\tbits = 32, isfloat = isokay = true;\n\t\t\t\t\telse if ((!bits||islong) && QCC_PR_CheckKeyword(keyword_double, \"double\"))\n\t\t\t\t\t\tbits = islong?128:64, islong=false, isfloat = isokay = true;\n\t\t\t\t\telse\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tif (isokay)\n\t\t\t\t{\n\t\t\t\t\tif (!bits)\n\t\t\t\t\t\tbits = islong?longbits:32;\t//<signed|unsigned|long> [int]\n\t\t\t\t\tif (isfloat)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (isunsigned)\n\t\t\t\t\t\t\tQCC_PR_ParseWarning (WARN_IGNOREDKEYWORD, \"ignoring unsupported unsigned keyword, type will be signed\");\n\t\t\t\t\t\tif (bits > 64)\n\t\t\t\t\t\t\ttype = type_double, QCC_PR_ParseWarning (WARN_IGNOREDKEYWORD, \"long doubles are not supported, using double\");\t//permitted\n\t\t\t\t\t\telse if (bits == 64)\n\t\t\t\t\t\t\ttype = type_double;\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\ttype = type_float;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tif (bits > 64)\n\t\t\t\t\t\t\ttype = (isunsigned?type_uint64:type_int64), QCC_PR_ParseWarning (WARN_IGNOREDKEYWORD, \"long longs are not supported, using long\");\t//permitted\n\t\t\t\t\t\telse if (bits > 32)\n\t\t\t\t\t\t\ttype = (isunsigned?type_uint64:type_int64);\n\t\t\t\t\t\telse if (bits <= 8)\n\t\t\t\t\t\t\ttype = (isunsigned?type_uint8:type_sint8);\n\t\t\t\t\t\telse if (bits <= 16)\n\t\t\t\t\t\t\ttype = (isunsigned?type_uint16:type_sint16);\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\ttype = (isunsigned?type_uint:type_integer);\n\t\t\t\t\t}\n\t\t\t\t\tgoto wasctype;\n\t\t\t\t}\n\n\n\t\t\t\tif (silentfail)\n\t\t\t\t\treturn NULL;\n\n\t\t\t\tQCC_PR_ParseError (ERR_NOTATYPE, \"\\\"%s\\\" is not a type\", name);\n\t\t\t\ttype = type_float;\t// shut up compiler warning\n\t\t\t}\n\t\t}\n\t}\nwasctype:\n\tif (!type)\n\t\treturn NULL;\n\n\tif (!ignoreptr)\n\t{\n\t\twhile (QCC_PR_CheckToken(\"*\"))\n\t\t{\n\t\t\tif (QCC_PR_CheckKeyword(keyword_const, \"const\"))\n\t\t\t\tQCC_PR_ParseWarning (WARN_IGNOREDKEYWORD, \"ignoring unsupported const keyword\");\n\t\t\ttype = QCC_PointerTypeTo(type);\n\t\t}\n\t}\n\n\tif (flag_qcfuncs && QCC_PR_CheckToken (\"(\"))\t//this is followed by parameters. Must be a function.\n\t{\n\t\ttype_inlinefunction = true;\n\t\ttype = QCC_PR_ParseFunctionType(newtype, type);\n\t\tif (!type)\n\t\t\tQCC_PR_ParseError(ERR_BADNOTTYPE, \"expected function arg list\");\n\t}\n\telse\n\t{\n\t\tif (newtype)\n\t\t{\n\t\t\ttype = QCC_PR_DuplicateType(type, false);\n\t\t}\n\t}\n\treturn type;\n}\n\n#endif\n\n\n\n"
  },
  {
    "path": "engine/qclib/qccgui.c",
    "content": "\n#include <windows.h>\n#include <windowsx.h>\n#include <commctrl.h>\n#include <commdlg.h>\n#include <richedit.h>\n#include <stdio.h>\n#include <sys/stat.h>\n#include <shlobj.h>\n#include <shlwapi.h>\n\n#include \"qcc.h\"\n#include \"gui.h\"\n\n//#define AVAIL_PNGLIB\n//#define AVAIL_ZLIB\n\n#define EMBEDDEBUG\n\n#define IDI_ICON_FTEQCC MAKEINTRESOURCE(101)\n\nvoid OptionsDialog(void);\nstatic void GUI_CreateInstaller_Windows(void);\nstatic void GUI_CreateInstaller_Android(void);\nstatic void SetProgsSrcFileAndPath(char *filename);\nstatic void CreateOutputWindow(pbool doannoates);\nvoid AddSourceFile(const char *parentsrc, const char *filename);\n\n#ifndef TVM_SETBKCOLOR\n#define TVM_SETBKCOLOR              (TV_FIRST + 29)\n#endif\n#ifndef TreeView_SetBkColor\n#define TreeView_SetBkColor(hwnd, clr) \\\n    (COLORREF)SNDMSG((hwnd), TVM_SETBKCOLOR, 0, (LPARAM)(clr))\n#endif\n\n\n#ifndef TTF_TRACK\n#define TTF_TRACK\t\t\t0x0020\n#endif\n#ifndef TTF_ABSOLUTE\n#define TTF_ABSOLUTE\t\t0x0080\n#endif\n#ifndef TTM_SETMAXTIPWIDTH\n#define TTM_SETMAXTIPWIDTH\t(WM_USER + 24)\n#endif\n#ifndef TTM_TRACKACTIVATE\n#define TTM_TRACKACTIVATE\t(WM_USER + 17)\n#endif\n#ifndef TTM_TRACKPOSITION\n#define TTM_TRACKPOSITION\t(WM_USER + 18)\n#endif\n\n\n//scintilla stuff\n#define SCI_GETLENGTH 2006\n#define SCI_GETCHARAT 2007\n#define SCI_GETCURRENTPOS 2008\n#define SCI_GETANCHOR 2009\n#define SCI_REDO 2011\n#define SCI_SETSAVEPOINT 2014\n#define SCI_CANREDO 2016\n#define SCI_GETCURLINE 2027\n#define SCI_CONVERTEOLS 2029\n#define SC_EOL_CRLF 0\n#define SC_EOL_CR 1\n#define SC_EOL_LF 2\n#define SCI_SETEOLMODE 2031\n#define SCI_SETTABWIDTH 2036\n#define SCI_SETCODEPAGE 2037\n#define SCI_MARKERDEFINE 2040\n#define SCI_MARKERSETFORE 2041\n#define SCI_MARKERSETBACK 2042\n#define SCI_MARKERADD 2043\n#define SCI_MARKERDELETE 2044\n#define SCI_MARKERDELETEALL 2045\n#define SCI_MARKERSETALPHA 2476\n#define SCI_MARKERGET 2046\n#define SCI_MARKERNEXT 2047\n#define SCI_STYLECLEARALL 2050\n#define SCI_STYLESETFORE 2051\n#define SCI_STYLESETBACK 2052\n#define SCI_STYLESETBOLD 2053\n#define SCI_STYLESETITALIC 2054\n#define SCI_STYLESETSIZE 2055\n#define SCI_STYLESETFONT 2056\n#define SCI_STYLERESETDEFAULT 2058\n#define SCI_STYLESETUNDERLINE 2059\n#define SCI_STYLESETCASE 2060\n#define SCI_AUTOCSHOW 2100\n#define SCI_AUTOCCANCEL 2101\n#define SCI_AUTOCACTIVE 2102\n#define SCI_AUTOCSETFILLUPS 2112\n#define SCI_GETLINE 2153\n#define SCI_SETSEL 2160\n#define SCI_GETSELTEXT 2161\n#define SCI_LINEFROMPOSITION 2166\n#define SCI_POSITIONFROMLINE 2167\n#define SCI_REPLACESEL 2170\n#define SCI_CANUNDO 2174\n#define SCI_UNDO 2176\n#define SCI_CUT 2177\n#define SCI_COPY 2178\n#define SCI_PASTE 2179\n#define SCI_SETTEXT 2181\n#define SCI_GETTEXT 2182\n#define SCI_CALLTIPSHOW 2200\n#define SCI_CALLTIPCANCEL 2201\n#define SCI_TOGGLEFOLD 2231\n#define SCI_SETMARGINWIDTHN 2242\n#define SCI_SETMARGINMASKN 2244\n#define SCI_SETMARGINSENSITIVEN 2246\n#define SCI_SETMOUSEDWELLTIME 2264\n#define SCI_CHARLEFT 2304\n#define SCI_CHARRIGHT 2306\n#define SCI_BACKTAB 2328 \n#define SCI_SEARCHANCHOR 2366\n#define SCI_SEARCHNEXT 2367\n#define SCI_SEARCHPREV 2368\n#define SCI_STYLEGETFORE 2481\n#define SCI_STYLEGETBACK 2482\n#define SCI_STYLEGETBOLD 2483\n#define SCI_STYLEGETITALIC 2484\n#define SCI_STYLEGETSIZE 2485\n#define SCI_STYLEGETFONT 2486\n#define SCI_STYLEGETUNDERLINE 2488\n#define SCI_STYLEGETCASE 2489\n#define SCI_BRACEHIGHLIGHTINDICATOR 2498\n#define SCI_BRACEBADLIGHTINDICATOR 2499\n#define SCI_LINELENGTH 2350\n#define SCI_BRACEHIGHLIGHT 2351\n#define SCI_BRACEBADLIGHT 2352\n#define SCI_BRACEMATCH 2353\n#define SCI_SETVIEWEOL 2356\n#define SCI_USEPOPUP 2371\n#define SCI_ANNOTATIONSETTEXT 2540\n#define SCI_ANNOTATIONGETTEXT 2541\n#define SCI_ANNOTATIONSETSTYLE 2542\n#define SCI_ANNOTATIONGETSTYLE 2543\n#define SCI_ANNOTATIONSETSTYLES 2544\n#define SCI_ANNOTATIONGETSTYLES 2545\n#define SCI_ANNOTATIONGETLINES 2546\n#define SCI_ANNOTATIONCLEARALL 2547\n#define ANNOTATION_HIDDEN 0\n#define ANNOTATION_STANDARD 1\n#define ANNOTATION_BOXED 2\n#define ANNOTATION_INDENTED 3\n#define SCI_ANNOTATIONSETVISIBLE 2548\n#define SCI_ANNOTATIONGETVISIBLE 2549\n#define SCI_ANNOTATIONSETSTYLEOFFSET 2550\n#define SCI_ANNOTATIONGETSTYLEOFFSET 2551\n#define SCI_AUTOCSETORDER 2660\n#define SCI_SETREPRESENTATION 2665\n#define SCI_SETLEXER 4001\n#define SCI_SETPROPERTY 4004\n#define SCI_SETKEYWORDS 4005\n\n#define SC_ORDER_PERFORMSORT 1\n\n#define SC_CP_UTF8 65001\n#define SCLEX_CPP 3\n\n#define SCE_C_DEFAULT 0\n#define SCE_C_COMMENT 1\n#define SCE_C_COMMENTLINE 2\n#define SCE_C_COMMENTDOC 3\n#define SCE_C_NUMBER 4\n#define SCE_C_WORD 5\n#define SCE_C_STRING 6\n#define SCE_C_CHARACTER 7\n#define SCE_C_UUID 8\n#define SCE_C_PREPROCESSOR 9\n#define SCE_C_OPERATOR 10\n#define SCE_C_IDENTIFIER 11\n#define SCE_C_STRINGEOL 12\n#define SCE_C_VERBATIM 13\n#define SCE_C_REGEX 14\n#define SCE_C_COMMENTLINEDOC 15\n#define SCE_C_WORD2 16\n#define SCE_C_COMMENTDOCKEYWORD 17\n#define SCE_C_COMMENTDOCKEYWORDERROR 18\n#define SCE_C_GLOBALCLASS 19\n#define SCE_C_STRINGRAW 20\n#define SCE_C_TRIPLEVERBATIM 21\n#define SCE_C_HASHQUOTEDSTRING 22\n#define SCE_C_PREPROCESSORCOMMENT 23\n#define SCE_C_PREPROCESSORCOMMENTDOC 24\n#define SCE_C_USERLITERAL 25\n#define SCE_C_TASKMARKER 26\n#define SCE_C_ESCAPESEQUENCE 27\n\n#define STYLE_DEFAULT 32\n#define STYLE_BRACELIGHT 34\n#define STYLE_BRACEBAD 35\n#define STYLE_LASTPREDEFINED 39\n\n#define SC_MARKNUM_FOLDEREND 25\n#define SC_MARKNUM_FOLDEROPENMID 26\n#define SC_MARKNUM_FOLDERMIDTAIL 27\n#define SC_MARKNUM_FOLDERTAIL 28\n#define SC_MARKNUM_FOLDERSUB 29\n#define SC_MARKNUM_FOLDER 30\n#define SC_MARKNUM_FOLDEROPEN 31\n#define SC_MASK_FOLDERS 0xFE000000\n#define SC_MARK_CIRCLE 0\n#define SC_MARK_ROUNDRECT 1\n#define SC_MARK_ARROW 2\n#define SC_MARK_SMALLRECT 3\n#define SC_MARK_SHORTARROW 4\n#define SC_MARK_EMPTY 5\n#define SC_MARK_ARROWDOWN 6\n#define SC_MARK_MINUS 7\n#define SC_MARK_PLUS 8\n#define SC_MARK_VLINE 9\n#define SC_MARK_LCORNER 10\n#define SC_MARK_TCORNER 11\n#define SC_MARK_BOXPLUS 12\n#define SC_MARK_BOXPLUSCONNECTED 13\n#define SC_MARK_BOXMINUS 14\n#define SC_MARK_BOXMINUSCONNECTED 15\n#define SC_MARK_LCORNERCURVE 16\n#define SC_MARK_TCORNERCURVE 17\n#define SC_MARK_CIRCLEPLUS 18\n#define SC_MARK_CIRCLEPLUSCONNECTED 19\n#define SC_MARK_CIRCLEMINUS 20\n#define SC_MARK_CIRCLEMINUSCONNECTED 21\n#define SC_MARK_BACKGROUND 22\n#define SC_MARK_DOTDOTDOT 23\n#define SC_MARK_ARROWS 24\n#define SC_MARK_PIXMAP 25\n#define SC_MARK_FULLRECT 26\n#define SC_MARK_LEFTRECT 27\n#define SC_MARK_AVAILABLE 28\n#define SC_MARK_UNDERLINE 29\n#define SC_MARK_RGBAIMAGE 30\n#define SC_MARK_BOOKMARK 31\n\n#define SCN_CHARADDED 2001\n#define SCN_SAVEPOINTREACHED 2002\n#define SCN_SAVEPOINTLEFT 2003\n#define SCN_UPDATEUI 2007\n#define SCN_MARGINCLICK 2010\n#define SCN_DWELLSTART 2016\n#define SCN_DWELLEND 2017\n#define SCN_FOCUSOUT 2029\n\nstruct SCNotification {\n\tNMHDR nmhdr;\n\tint position;\n\tint ch;\n\tint modifiers;\n\tint modificationType;\n\tconst char *text;\n\tint length;\n\tint linesAdded;\n\tint message;\n\tDWORD_PTR wParam;\n\tLONG_PTR lParam;\n\tint line;\n\tint foldLevelNow;\n\tint foldLevelPrev;\n\tint margin;\n\tint listType;\n\tint x;\n\tint y;\n\tint token;\n\tint annotationLinesAdded;\n\tint updated;\n};\n\n\n//these all run on the main thread\ntypedef struct editor_s {\n\tchar filename[MAX_PATH];\t//abs\n\tHWND window;\n\tHWND editpane;\n\tHWND tooltip;\n\tchar tooltiptext[1024];\n\tint curline;\n\tpbool modified;\n\tpbool scintilla;\n\tint savefmt;\n\ttime_t filemodifiedtime;\n\tstruct editor_s *next;\n\n\t//for avoiding silly redraws etc when titles don't actually change...\n\tint oldsavefmt;\n\tint oldline;\n} editor_t;\neditor_t *editors;\n\ntypedef struct\n{\n\teditor_t *editor;\t//will need to be validated\n\tunsigned int selpos;\n\tunsigned int anchorpos;\n} navhistory_t;\nnavhistory_t navhistory[8];\nconst unsigned int navhistory_size = sizeof(navhistory)/sizeof(navhistory[0]);\nunsigned int navhistory_first;\t//don't allow rewinding past this.\nunsigned int navhistory_pos;\n\n//the engine thread simply sits waiting for responses from the engine\ntypedef struct\n{\n\tint pipeclosed;\n\tDWORD tid;\n\tHWND window;\n\tHWND refocuswindow;\n\tHANDLE thread;\n\tHANDLE pipefromengine;\n\tHANDLE pipetoengine;\n\tsize_t embedtype;\t//0 = not. 1 = separate. 2 = mdi child\n} enginewindow_t;\nstatic pbool EngineCommandf(char *message, ...);\nstatic void EngineGiveFocus(void);\n\n/*\nstatic pbool QCC_RegGetStringValue(HKEY base, char *keyname, char *valuename, void *data, int datalen)\n{\n\tpbool result = false;\n\tHKEY subkey;\n\tDWORD type = REG_NONE;\n\tif (RegOpenKeyEx(base, keyname, 0, KEY_READ, &subkey) == ERROR_SUCCESS)\n\t{\n\t\tDWORD dwlen = datalen-1;\n\t\tresult = ERROR_SUCCESS == RegQueryValueEx(subkey, valuename, NULL, &type, data, &dwlen);\n\t\tdatalen = dwlen;\n\t\tRegCloseKey (subkey);\n\t}\n\n\tif (type == REG_SZ || type == REG_EXPAND_SZ)\n\t\t((char*)data)[datalen] = 0;\n\telse\n\t\t((char*)data)[0] = 0;\n\treturn result;\n}\nstatic pbool QCC_RegSetValue(HKEY base, char *keyname, char *valuename, int type, void *data, int datalen)\n{\n\tpbool result = false;\n\tHKEY subkey;\n\n\tif (RegCreateKeyEx(base, keyname, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &subkey, NULL) == ERROR_SUCCESS)\n\t{\n\t\tif (ERROR_SUCCESS == RegSetValueEx(subkey, valuename, 0, type, data, datalen))\n\t\t\tresult = true;\n\t\tRegCloseKey (subkey);\n\t}\n\treturn result;\n}\n*/\n\n#undef printf\n#undef Sys_Error\nvoid Sys_Error(const char *text, ...);\n\nextern pbool qcc_vfiles_changed;\nextern vfile_t *qcc_vfiles;\nHWND mainwindow;\nHINSTANCE ghInstance;\nstatic INT CALLBACK StupidBrowseCallbackProc(HWND hwnd, UINT uMsg, LPARAM lp, LPARAM pData) ;\nvoid QCC_SaveVFiles(void)\n{\n\tvfile_t *f;\n\tif (qcc_vfiles_changed)\n\t{\n\t\tswitch (MessageBox(mainwindow, \"Save files as archive?\", \"FTEQCCGUI\", MB_YESNOCANCEL))\n\t\t{\n\t\tcase IDYES:\n\t\t\t{\n\t\t\t\tchar filename[MAX_PATH];\n\t\t\t\tchar oldpath[MAX_PATH+10];\n\t\t\t\tOPENFILENAME ofn;\n\t\t\t\tmemset(&ofn, 0, sizeof(ofn));\n\t\t\t\tofn.lStructSize = sizeof(ofn);\n\t\t\t\tofn.hInstance = ghInstance;\n\t\t\t\tofn.lpstrFile = filename;\n\t\t\t\tofn.lpstrTitle = \"Output archive\";\n\t\t\t\tofn.nMaxFile = sizeof(filename)-1;\n\t\t\t\tofn.lpstrFilter = \"QuakeC Projects\\0*.zip\\0All files\\0*.*\\0\";\n\t\t\t\tmemset(filename, 0, sizeof(filename));\n\t\t\t\tGetCurrentDirectory(sizeof(oldpath)-1, oldpath);\n\t\t\t\tofn.lpstrInitialDir = oldpath;\n\t\t\t\tif (GetSaveFileName(&ofn))\n\t\t\t\t{\n\t\t\t\t\tint h = SafeOpenWrite(ofn.lpstrFile, -1);\n\n\t\t\t\t\tprogfuncs_t funcs;\n\t\t\t\t\tprogexterns_t ext;\n\t\t\t\t\tmemset(&funcs, 0, sizeof(funcs));\n\t\t\t\t\tfuncs.funcs.parms = &ext;\n\t\t\t\t\tmemset(&ext, 0, sizeof(ext));\n\t\t\t\t\text.ReadFile = GUIReadFile;\n\t\t\t\t\text.FileSize = GUIFileSize;\n\t\t\t\t\text.WriteFile = QCC_WriteFile;\n\t\t\t\t\text.Sys_Error = Sys_Error;\n\t\t\t\t\text.Printf = GUIprintf;\n\n\t\t\t\t\tqccprogfuncs = &funcs;\n\t\t\t\t\tWriteSourceFiles(qcc_vfiles, h, true, false);\n\t\t\t\t\tqccprogfuncs = NULL;\n\n\t\t\t\t\tSafeClose(h);\n\n\t\t\t\t\tqcc_vfiles_changed = false;\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\t\tcase IDNO:\n\t\t\t{\n\t\t\t\tchar oldworkingdir[MAX_PATH], newdir[MAX_PATH+10], workingdir[MAX_PATH];\n\t\t\t\tBROWSEINFO bi;\n\t\t\t\tLPITEMIDLIST il;\n\t\t\t\tmemset(&bi, 0, sizeof(bi));\n\t\t\t\tbi.hwndOwner = mainwindow;\n\t\t\t\tbi.pidlRoot = NULL;\n\t\t\t\tGetCurrentDirectory(sizeof(oldworkingdir)-1, oldworkingdir);\n\t\t\t\tGetCurrentDirectory(sizeof(workingdir)-1, workingdir);\n\t\t\t\tbi.pszDisplayName = workingdir;\n\t\t\t\tbi.lpszTitle = \"Where do you want the source?\";\n\t\t\t\tbi.ulFlags = BIF_RETURNONLYFSDIRS|BIF_STATUSTEXT;\n\t\t\t\tbi.lpfn = StupidBrowseCallbackProc;\n\t\t\t\tbi.lParam = 0;\n\t\t\t\tbi.iImage = 0;\n\t\t\t\til = SHBrowseForFolder(&bi);\n\t\t\t\tif (il)\n\t\t\t\t{\n\t\t\t\t\tSHGetPathFromIDList(il, newdir);\n\t\t\t\t\tCoTaskMemFree(il);\n\n\t\t\t\t\tfor (f = qcc_vfiles; f; f = f->next)\n\t\t\t\t\t{\n\t\t\t\t\t\tchar nname[MAX_PATH];\n\t\t\t\t\t\tint h;\n\t\t\t\t\t\tQC_snprintfz(nname, sizeof(nname), \"%s\\\\%s\", newdir, f->filename);\n\t\t\t\t\t\th = SafeOpenWrite(f->filename, -1);\n\t\t\t\t\t\tif (h >= 0)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tSafeWrite(h, f->file, f->size);\n\t\t\t\t\t\t\tSafeClose(h);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tSetCurrentDirectory(oldworkingdir);\t//revert microsoft stupidity.\n\t\t\t}\n\t\t\tbreak;\n\t\tdefault:\n\t\t\treturn;\n\t\t}\n\t}\n}\n\nvoid QCC_EnumerateFilesResult(const char *name, const void *compdata, size_t compsize, int method, size_t plainsize)\n{\n\tvoid *buffer = malloc(plainsize);\n\tif (QC_decode(NULL, compsize, plainsize, method, compdata, buffer))\n\t\tQCC_AddVFile(name, buffer, plainsize);\n\n\tfree(buffer);\n}\n\n/*\n==============\nLoadFile\n==============\n*/\nstatic void *QCC_ReadFile(const char *fname, unsigned char *(*buf_get)(void *ctx, size_t len), void *buf_ctx, size_t *out_size)\n//unsigned char *PDECL QCC_ReadFile (const char *fname, void *buffer, int len, size_t *sz)\n{\n\tsize_t len;\n\tFILE *f;\n\tchar *buffer;\n\tvfile_t *v = QCC_FindVFile(fname);\n\tif (v)\n\t{\n\t\tlen = v->size;\n\t\tif (buf_get)\n\t\t\tbuffer = buf_get(buf_ctx, len+1);\n\t\telse\n\t\t\tbuffer = malloc(len+1);\n\t\tif (!buffer)\n\t\t\treturn NULL;\n\t\t((char*)buffer)[len] = 0;\n\t\tif (len > v->size)\n\t\t\tlen = v->size;\n\t\tmemcpy(buffer, v->file, len);\n\t\tif (out_size)\n\t\t\t*out_size = len;\n\t\treturn buffer;\n\t}\n\n\tf = fopen(fname, \"rb\");\n\tif (!f)\n\t{\n\t\tif (out_size)\n\t\t\t*out_size = 0;\n\t\treturn NULL;\n\t}\n\n\tfseek(f, 0, SEEK_END);\n\tlen = ftell(f);\n\tfseek(f, 0, SEEK_SET);\n\tif (buf_get)\n\t\tbuffer = buf_get(buf_ctx, len+1);\n\telse\n\t\tbuffer = malloc(len+1);\n\t((char*)buffer)[len] = 0;\n\tif (len != fread(buffer, 1, len, f))\n\t{\n\t\tif (!buf_get)\n\t\t\tfree(buffer);\n\t\tbuffer = NULL;\n\t}\n\tfclose(f);\n\n\tif (out_size)\n\t\t*out_size = len;\n\treturn buffer;\n}\nint PDECL QCC_RawFileSize (const char *fname)\n{\n\tlong    length;\n\tFILE *f;\n\n\tvfile_t *v = QCC_FindVFile(fname);\n\tif (v)\n\t\treturn v->size;\n\n\tf = fopen(fname, \"rb\");\n\tif (!f)\n\t\treturn -1;\n\tfseek(f, 0, SEEK_END);\n\tlength = ftell(f);\n\tfclose(f);\n\treturn length;\n}\nint PDECL QCC_PopFileSize (const char *fname)\n{\n\textern int qcc_compileactive;\n\tint len = QCC_RawFileSize(fname);\n\tif (len >= 0 && qcc_compileactive)\n\t{\n\t\tAddSourceFile(compilingrootfile,\tfname);\n\t}\n\treturn len;\n}\n\n#ifdef AVAIL_ZLIB\n#include \"../libs/zlib.h\"\n#endif\n\npbool PDECL QCC_WriteFileW (const char *name, wchar_t *data, int maxchars)\n{\n\tchar *u8start = malloc(3+maxchars*4+1);\n\tchar *u8 = u8start;\n\tint offset;\n\tpbool result = false;\n\tunsigned int inc;\n\tFILE *f;\n\n\t//start with the bom\n\t//lets just always write a BOM when the file contains something outside ascii. it'll just be more robust when microsoft refuse to use utf8 by default.\n\t//its just much less likely to fuck up when people use notepad/wordpad. :s\n\tinc = 0xfeff;\n\t*u8++ = ((inc>>12) & 0xf) | 0xe0;\n\t*u8++ = ((inc>>6) & 0x3f) | 0x80;\n\t*u8++ = ((inc>>0) & 0x3f) | 0x80;\n\toffset = u8-u8start;\t//assume its not needed. will set to 0 if it is.\n\n\twhile(*data)\n\t{\n\t\tinc = *data++;\n\t\t//handle surrogates\n\t\tif (inc >= 0xd800u && inc < 0xdc00u)\n\t\t{\n\t\t\tunsigned int l = *data;\n\t\t\tif (l >= 0xdc00u && l < 0xe000u)\n\t\t\t{\n\t\t\t\tdata++;\n\t\t\t\tinc = (((inc & 0x3ffu)<<10) | (l & 0x3ffu)) + 0x10000;\n\t\t\t}\n\t\t}\n\t\tif (inc <= 127)\n\t\t\t*u8++ = inc;\n\t\telse \n\t\t{\n\t\t\toffset = 0;\n\t\t\tif (inc <= 0x7ff)\n\t\t\t{\n\t\t\t\t*u8++ = ((inc>>6) & 0x1f) | 0xc0;\n\t\t\t\t*u8++ = ((inc>>0) & 0x3f) | 0x80;\n\t\t\t}\n\t\t\telse if (inc <= 0xffff)\n\t\t\t{\n\t\t\t\t*u8++ = ((inc>>12) & 0xf) | 0xe0;\n\t\t\t\t*u8++ = ((inc>>6) & 0x3f) | 0x80;\n\t\t\t\t*u8++ = ((inc>>0) & 0x3f) | 0x80;\n\t\t\t}\n\t\t\telse if (inc <= 0x1fffff)\n\t\t\t{\n\t\t\t\t*u8++ = ((inc>>18) & 0x07) | 0xf0;\n\t\t\t\t*u8++ = ((inc>>12) & 0x3f) | 0x80;\n\t\t\t\t*u8++ = ((inc>> 6) & 0x3f) | 0x80;\n\t\t\t\t*u8++ = ((inc>> 0) & 0x3f) | 0x80;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tinc = 0xFFFD;\n\t\t\t\t*u8++ = ((inc>>12) & 0xf) | 0xe0;\n\t\t\t\t*u8++ = ((inc>>6) & 0x3f) | 0x80;\n\t\t\t\t*u8++ = ((inc>>0) & 0x3f) | 0x80;\n\t\t\t}\n\t\t}\n\t}\n\n\tf = fopen(name, \"wb\");\n\tif (f)\n\t{\n\t\tresult = fwrite(u8start+offset, 1, u8-(u8start+offset), f) == (u8-(u8start+offset));\n\t\tfclose(f);\n\t}\n\tfree(u8start);\n\treturn result;\n}\n\npbool PDECL QCC_WriteFile (const char *name, void *data, int len)\n{\n\tlong    length;\n\tFILE *f;\n\n\tchar *ext = strrchr(name, '.');\n\tif (ext && !stricmp(ext, \".gz\"))\n\t{\n#ifdef AVAIL_ZLIB\n\t\tpbool okay = true;\n\t\tchar out[1024*8];\n\n\t\tz_stream strm = {\n\t\t\tdata,\n\t\t\tlen,\n\t\t\t0,\n\n\t\t\tout,\n\t\t\tsizeof(out),\n\t\t\t0,\n\n\t\t\tNULL,\n\t\t\tNULL,\n\n\t\t\tNULL,\n\t\t\tNULL,\n\t\t\tNULL,\n\n\t\t\tZ_BINARY,\n\t\t\t0,\n\t\t\t0\n\t\t};\n\n\t\tf = fopen(name, \"wb\");\n\t\tif (!f)\n\t\t\treturn false;\n\t\tdeflateInit2(&strm, Z_BEST_COMPRESSION, Z_DEFLATED, MAX_WBITS|16, MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY);\n\t\twhile(okay && deflate(&strm, Z_FINISH) == Z_OK)\n\t\t{\n\t\t\tif (sizeof(out) - strm.avail_out != fwrite(out, 1, sizeof(out) - strm.avail_out, f))\n\t\t\t\tokay = false;\n\t\t\tstrm.next_out = out;\n\t\t\tstrm.avail_out = sizeof(out);\n\t\t}\n\t\tif (sizeof(out) - strm.avail_out != fwrite(out, 1, sizeof(out) - strm.avail_out, f))\n\t\t\tokay = false;\n\t\tdeflateEnd(&strm);\n\t\tfclose(f);\n\t\tif (!okay)\n\t\t\tunlink(name);\n\t\treturn okay;\n#else\n\t\treturn false;\n#endif\n\t}\n\n\tif (QCC_FindVFile(name))\n\t\treturn !!QCC_AddVFile(name, data, len);\n\n\tf = fopen(name, \"wb\");\n\tif (!f)\n\t\treturn false;\n\tlength = fwrite(data, 1, len, f);\n\tfclose(f);\n\n\tif (length != len)\n\t\treturn false;\n\n\treturn true;\n}\n\n#undef printf\n#undef Sys_Error\n\nvoid Sys_Error(const char *text, ...)\n{\n\tva_list argptr;\n\tstatic char msg[2048];\t\n\n\tva_start (argptr,text);\n\tQC_vsnprintf (msg,sizeof(msg)-1, text,argptr);\n\tva_end (argptr);\n\n\tQCC_Error(ERR_INTERNAL, \"%s\", msg);\n}\n\n\nFILE *logfile;\nint logprintf(const char *format, ...)\n{\n\tva_list\t\targptr;\n\tstatic char\t\tstring[1024];\n\n\tva_start (argptr, format);\n#ifdef _WIN32\n\t_vsnprintf (string,sizeof(string)-1, format,argptr);\n#else\n\tvsnprintf (string,sizeof(string), format,argptr);\n#endif\n\tva_end (argptr);\n\n\tprintf(\"%s\", string);\n\tif (logfile)\n\t\tfputs(string, logfile);\n\n\treturn 0;\n}\n\n\n\n\n\n\n\n\n\n\n\n#define Edit_Redo(hwndCtl)                      ((BOOL)(DWORD)SNDMSG((hwndCtl), EM_REDO, 0L, 0L))\n\n\n#define MAIN_WINDOW_CLASS_NAME \"FTEMAINWINDOW\"\n#define MDI_WINDOW_CLASS_NAME \"FTEMDIWINDOW\"\n#define EDIT_WINDOW_CLASS_NAME \"FTEEDITWINDOW\"\n#define OPTIONS_WINDOW_CLASS_NAME \"FTEOPTIONSWINDOW\"\n#define ENGINE_WINDOW_CLASS_NAME \"FTEEMBEDDEDWINDOW\"\n\n#define EM_GETSCROLLPOS  (WM_USER + 221)\n#define EM_SETSCROLLPOS  (WM_USER + 222)\n\n\n\nint GUIprintf(const char *msg, ...);\nvoid GUIPrint(HWND wnd, char *msg);\n\nchar finddef[256];\nchar greptext[256];\nextern pbool fl_extramargins;\nextern int fl_tabsize;\nextern char enginebinary[MAX_OSPATH];\nextern char enginebasedir[MAX_OSPATH];\nextern char enginecommandline[8192];\nextern QCC_def_t *sourcefilesdefs[];\nextern int sourcefilesnumdefs;\n\nvoid RunCompiler(char *args, pbool quick);\nvoid RunEngine(void);\n\nHINSTANCE ghInstance;\nHMODULE richedit;\nHMODULE scintilla;\n\npbool resetprogssrc;\t//progs.src was changed, reload project info.\n\nHWND mainwindow;\nHWND gamewindow;\nHWND mdibox;\nHWND watches;\nHWND optionsmenu;\nHWND outputbox;\nHWND projecttree;\nHWND search_name;\nHACCEL accelerators;\n\n\n//our splitter...\n#define SPLITTER_SIZE 4\nstatic struct splits_s\n{\n\tHWND wnd;\n\tHWND splitter;\n\tint minsize;\n\tint cury;\n\tint cursize;\n\tfloat frac;\n\n} *splits;\nstatic size_t numsplits;\nstatic RECT splitterrect;\n\nstatic struct splits_s *SplitterGet(HWND id)\n{\n\tsize_t s;\n\tfor (s = 0; s < numsplits; s++)\n\t{\n\t\tif (splits[s].wnd == id)\n\t\t\treturn &splits[s];\n\t}\n\treturn NULL;\n}\nstatic int SplitterShrinkPrior(size_t s, int px)\n{\n\tint found = 0;\n\tint avail;\n\tfor (; px && s > 0; s--)\n\t{\n\t\tavail = splits[s].cursize - splits[s].minsize;\n\t\tif (avail > px)\n\t\t\tavail = px;\n\n\t\tsplits[s].cursize -= avail;\n\t\tfound += avail;\n\t\tpx -= avail;\n\t}\n\n\tif (px)\n\t{\n\t\tavail = splits[0].cursize - splits[0].minsize;\n\t\tif (avail > px)\n\t\t\tavail = px;\n\n\t\tsplits[0].cursize -= avail;\n\t\tfound += avail;\n\t\tpx -= avail;\n\t}\n\n\treturn found;\n}\nstatic int SplitterShrinkNext(size_t s, int px)\n{\n\tint found = 0;\n\tint avail;\n\tfor (; px && s < numsplits; s++)\n\t{\n\t\tavail = splits[s].cursize - splits[s].minsize;\n\t\tif (avail > px)\n\t\t\tavail = px;\n\n\t\tsplits[s].cursize -= avail;\n\t\tfound += avail;\n\t\tpx -= avail;\n\t}\n\treturn found;\n}\nstatic void SplitterUpdate(void);\nstatic LRESULT CALLBACK SplitterWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)\n{\n\tsize_t s;\n\tPAINTSTRUCT ps;\n\tRECT rect;\n\tint y;\n\tswitch(message)\n\t{\n\tcase WM_LBUTTONDOWN:\n\t\tSetCapture(hWnd);\n\t\treturn TRUE;\n\tcase WM_MOUSEMOVE:\n\t\tif (wParam & MK_LBUTTON)\n\t\t\tif (GetCapture() == hWnd)\n\t\t\t\tgoto doresize;\n\t\treturn true;\n\tcase WM_LBUTTONUP:\n\t\tReleaseCapture();\n\tdoresize:\n\t\ty = GET_Y_LPARAM(lParam);\n\t\tGetClientRect(hWnd, &rect);\n\t\ty = y - rect.top - SPLITTER_SIZE/2;\n\t\tfor (s = 1; s < numsplits; s++)\n\t\t{\n\t\t\tif (splits[s].splitter == hWnd)\n\t\t\t{\n\t\t\t\tif (y < 0)\n\t\t\t\t\tsplits[s].cursize += SplitterShrinkPrior(s-1, -y);\n\t\t\t\telse\n\t\t\t\t\tsplits[s-1].cursize += SplitterShrinkNext(s, y);\n\t\t\t\tSplitterUpdate();\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\treturn TRUE;\n\tcase WM_PAINT:\n\t\tBeginPaint(hWnd,(LPPAINTSTRUCT)&ps);\n\t\tEndPaint(hWnd,(LPPAINTSTRUCT)&ps);\n\t\treturn TRUE;\n\tdefault:\n\t\treturn DefWindowProc(hWnd,message,wParam,lParam);\n\t}\n}\nstatic void SplitterUpdate(void)\n{\n\tint y = 0;\n\tsize_t s;\n\tif (!numsplits)\n\t\treturn;\n\n\ty = splitterrect.bottom-splitterrect.top;\n\n\t//now figure out their positions relative to that\n\tfor (s = numsplits; s-- > 1; )\n\t{\n\t\ty -= splits[s].cursize;\n\t\tsplits[s].cury = y;\n\t\ty -= SPLITTER_SIZE;\n\t}\n\n\tsplits[0].cursize = y;\n\tsplits[0].cury = 0;\n\tif (splits[0].cursize < splits[0].minsize)\n\t\tsplits[0].cursize += SplitterShrinkNext(1, splits[0].minsize-splits[0].cursize);\n\n\tfor (s = 0; s < numsplits; s++)\n\t{\n\t\tif (s)\n\t\t{\n\t\t\tif (!splits[s].splitter)\n\t\t\t{\n\t\t\t\tWNDCLASSA wclass;\n\t\t\t\twclass.style = 0;\n\t\t\t\twclass.lpfnWndProc = SplitterWndProc;\n\t\t\t\twclass.cbClsExtra = 0;\n\t\t\t\twclass.cbWndExtra = 0;\n\t\t\t\twclass.hInstance = ghInstance;\n\t\t\t\twclass.hIcon = NULL;\n\t\t\t\twclass.hCursor = LoadCursor(0, IDC_SIZENS);\n\t\t\t\twclass.hbrBackground = (HBRUSH)(COLOR_3DFACE + 1);\n\t\t\t\twclass.lpszMenuName = NULL;\n\t\t\t\twclass.lpszClassName = \"splitter\";\n\t\t\t\tRegisterClassA(&wclass);\n\t\t\t\tsplits[s].splitter = CreateWindowExA(0, wclass.lpszClassName, \"\", WS_CHILD|WS_VISIBLE, splitterrect.left, splitterrect.top+splits[s].cury-SPLITTER_SIZE, splitterrect.right-splitterrect.left, SPLITTER_SIZE, mainwindow, NULL, ghInstance, NULL);\n\t\t\t}\n\t\t\telse\n\t\t\t\tSetWindowPos(splits[s].splitter, HWND_TOP, splitterrect.left, splitterrect.top+splits[s].cury-SPLITTER_SIZE, splitterrect.right-splitterrect.left, SPLITTER_SIZE, SWP_NOZORDER);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (splits[s].splitter)\n\t\t\t{\n\t\t\t\tDestroyWindow(splits[s].splitter);\n\t\t\t\tsplits[s].splitter = NULL;\n\t\t\t}\n\t\t}\n\t\tSetWindowPos(splits[s].wnd, HWND_TOP, splitterrect.left, splitterrect.top+splits[s].cury, splitterrect.right-splitterrect.left, splits[s].cursize, SWP_NOZORDER);\n\t}\n}\nstatic void SplitterAdd(HWND w, int minsize, int newsize)\n{\n\tstruct splits_s *n = malloc(sizeof(*n)*(numsplits+1));\n\tmemcpy(n, splits, sizeof(*n)*numsplits);\n\tfree(splits);\n\tsplits = n;\n\tn += numsplits;\n\n\tn->wnd = w;\n\tn->splitter = NULL;\n\tn->minsize = minsize;\n\tn->cursize = newsize;\n\tn->cury = 0;\n\n\tnumsplits++;\n\n\tSplitterUpdate();\n\tShowWindow(w, SW_SHOW);\n}\n//adds if needed.\nstatic void SplitterFocus(HWND w, int minsize, int newsize)\n{\n\tstruct splits_s *s = SplitterGet(w);\n\tif (s)\n\t{\n\t\tif (s->cursize < newsize)\n\t\t{\n\t\t\ts->cursize += SplitterShrinkPrior(s-splits-1, (newsize-s->cursize)/2);\n\t\t\tif (s->cursize < newsize)\n\t\t\t\ts->cursize += SplitterShrinkNext(s-splits+1, newsize-s->cursize);\n\t\t\tif (s->cursize < newsize)\n\t\t\t\ts->cursize += SplitterShrinkPrior(s-splits-1, newsize-s->cursize);\n\t\t\tSplitterUpdate();\n\t\t}\n\t}\n\telse\n\t\tSplitterAdd(w, minsize, newsize);\n\n\tSetFocus(w);\n}\nstatic void SplitterRemove(HWND w)\n{\n\tstruct splits_s *s = SplitterGet(w);\n\tsize_t idx;\n\tif (!s)\n\t\treturn;\n\tif (s->splitter)\n\t\tDestroyWindow(s->splitter);\n\tidx = s-splits;\n\tnumsplits--;\n\tmemmove(splits+idx, splits+idx+1, sizeof(*s)*(numsplits-idx));\n\n\tShowWindow(w, SW_HIDE);\n\n\tSplitterUpdate();\n}\n\n\n\n\nFILE *logfile;\n\nvoid GrepAllFiles(char *string);\n\nstruct{\n\tchar *text;\n\tHWND hwnd;\n\tint washit;\n} buttons[] = {\n\t{\"Compile\"},\n#ifdef EMBEDDEBUG\n\t{NULL},\n\t{\"Debug\"},\n#endif\n\t{\"Options\"},\n\t{\"Def\"},\n\t{\"Grep\"}\n};\n\nenum\n{\n\tID_COMPILE = 0,\n#ifdef EMBEDDEBUG\n\tID_NULL,\n\tID_RUN,\n#endif\n\tID_OPTIONS,\n\tID_DEF,\n\tID_GREP\n};\n\n#define NUMBUTTONS sizeof(buttons)/sizeof(buttons[0])\n\n\n\nvoid GUI_DialogPrint(const char *title, const char *text)\n{\n\tMessageBox(mainwindow, text, title, 0);\n}\n\nstatic void FindNextScintilla(editor_t *editor, char *findtext, pbool next)\n{\n\tint pos = SendMessage(editor->editpane, SCI_GETCURRENTPOS, 0, 0);\n\tEdit_SetSel(editor->editpane, pos+1, pos+1);\n\tSendMessage(editor->editpane, SCI_SEARCHANCHOR, 0, 0);\n\tif (SendMessage(editor->editpane, next?SCI_SEARCHNEXT:SCI_SEARCHPREV, 0, (LPARAM)findtext) != -1)\n\t\tEdit_ScrollCaret(editor->editpane);\t//make sure its focused\n\telse\n\t{\n\t\tEdit_SetSel(editor->editpane, pos, pos);\t//revert the selection change as nothing was found\n\t\tMessageBox(editor->editpane, \"No more occurences found\", \"FTE Editor\", 0);\n\t}\n}\nstatic char *WordUnderCursor(editor_t *editor, char *word, int wordsize, char *term, int termsize, int position);\npbool GenAutoCompleteList(char *prefix, char *buffer, int buffersize);\n\n//available in xp+\ntypedef LRESULT (CALLBACK *SUBCLASSPROC)(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData);\nBOOL (WINAPI * pSetWindowSubclass)(HWND hWnd, SUBCLASSPROC pfnSubclass, UINT_PTR uIdSubclass, DWORD_PTR dwRefData);\nLRESULT (WINAPI *pDefSubclassProc)(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);\nLRESULT CALLBACK MySubclassWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)\n{\n\teditor_t *editor;\n\tif (uMsg == WM_CHAR || uMsg == WM_UNICHAR)\n\t{\n\t\tswitch(wParam)\n\t\t{\n\t\tcase VK_ESCAPE:\n\t\t\tSplitterRemove(outputbox);\n\t\t\tbreak;\n\t\tcase VK_SPACE:\n\t\t\t{\n\t\t\t\tBYTE keystate[256];\n\t\t\t\tGetKeyboardState(keystate);\n\t\t\t\tif ((keystate[VK_CONTROL] | keystate[VK_LCONTROL]) & 128)\n\t\t\t\t{\n\t\t\t\t\tfor (editor = editors; editor; editor = editor->next)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (editor->editpane == hWnd)\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tif (editor->scintilla)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (!SendMessage(editor->editpane, SCI_AUTOCACTIVE, 0, 0))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tstatic char buffer[65536];\n\t\t\t\t\t\t\tchar prefixbuffer[128];\n\t\t\t\t\t\t\tchar *pre = WordUnderCursor(editor, prefixbuffer, sizeof(prefixbuffer), NULL, 0, SendMessage(editor->editpane, SCI_GETCURRENTPOS, 0, 0));\n\t\t\t\t\t\t\tif (pre && *pre)\n\t\t\t\t\t\t\t\tif (GenAutoCompleteList(pre, buffer, sizeof(buffer)))\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tSendMessage(editor->editpane, SCI_AUTOCSETFILLUPS, 0, (LPARAM)\".,[<>(*/+-=\\t\\n\");\n\t\t\t\t\t\t\t\t\tSendMessage(editor->editpane, SCI_AUTOCSHOW, strlen(pre), (LPARAM)buffer);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\treturn FALSE;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t}\n\tif (uMsg == WM_LBUTTONDBLCLK && hWnd == outputbox)\n\t{\n\t\tCHARRANGE selrange = {0};\n\t\tSendMessage(hWnd, EM_EXGETSEL, 0, (LPARAM)&selrange);\n\t\tif (1)\t//some text is selected.\n\t\t{\n\t\t\tunsigned int bytes;\n\t\t\tchar line[1024];\n\t\t\tchar *colon1, *colon2 = NULL;\n\n\t\t\tint l1;\n\t\t\tint l2;\n\n\t\t\tl1 = Edit_LineFromChar(hWnd, selrange.cpMin);\n\t\t\tl2 = Edit_LineFromChar(hWnd, selrange.cpMax);\n\t\t\tif (l1 == l2)\n\t\t\t{\n\t\t\t\tbytes = Edit_GetLine(hWnd, Edit_LineFromChar(outputbox, selrange.cpMin), line, sizeof(line)-1);\n\t\t\t\tline[bytes] = 0;\n\n\t\t\t\tfor (colon1 = line+strlen(line)-1; *colon1 <= ' ' && colon1>=line; colon1--)\n\t\t\t\t\t*colon1 = '\\0';\n\t\t\t\tif (!strncmp(line, \"warning: \", 9))\n\t\t\t\t\tmemmove(line, line+9, sizeof(line)-9);\n\t\t\t\tcolon1=line;\n\t\t\t\tdo\n\t\t\t\t{\n\t\t\t\t\tcolon1 = strchr(colon1+1, ':');\n\t\t\t\t} while (colon1 && colon1[1] == '\\\\');\n\n\t\t\t\tif (colon1)\n\t\t\t\t{\n\t\t\t\t\tcolon2 = strchr(colon1+1, ':');\n\t\t\t\t\twhile (colon2 && colon2[1] == '\\\\')\n\t\t\t\t\t{\n\t\t\t\t\t\tcolon2 = strchr(colon2+1, ':');\n\t\t\t\t\t}\n\t\t\t\t\tif (colon2)\n\t\t\t\t\t{\n\t\t\t\t\t\t*colon1 = '\\0';\n\t\t\t\t\t\t*colon2 = '\\0';\n\t\t\t\t\t\tEditFile(line, atoi(colon1+1)-1, 2);\n\t\t\t\t\t}\n\t\t\t\t\telse if (!strncmp(line, \"Source file: \", 13))\n\t\t\t\t\t\tEditFile(line+13, -1, 2);\n\t\t\t\t\telse if (!strncmp(line, \"Including: \", 11))\n\t\t\t\t\t\tEditFile(line+11, -1, 2);\n\t\t\t\t}\n\t\t\t\telse if (!strncmp(line, \"including \", 10))\n\t\t\t\t\tEditFile(line+10, -1, 2);\n\t\t\t\telse if (!strncmp(line, \"compiling \", 10))\n\t\t\t\t\tEditFile(line+10, -1, 2);\n\t\t\t\telse if (!strncmp(line, \"prototyping \", 12))\n\t\t\t\t\tEditFile(line+12, -1, 2);\n\t\t\t\telse if (!strncmp(line, \"Couldn't open file \", 19))\n\t\t\t\t\tEditFile(line+19, -1, 2);\n\t\t\t\tEdit_SetSel(hWnd, selrange.cpMin, selrange.cpMin);\t//deselect it.\n\t\t\t}\n\t\t}\n\t\treturn 0;\n\t}\n\treturn pDefSubclassProc(hWnd, uMsg, wParam, lParam);\n}\n\nHWND CreateAnEditControl(HWND parent, pbool *scintillaokay)\n{\n\tHWND newc = NULL;\n\n#ifdef SCISTATIC\n\textern int Scintilla_RegisterClasses(void *hinst);\n\tscintilla = ghInstance;\n\tScintilla_RegisterClasses(scintilla);\n#else\n\tif (!scintilla && scintillaokay)\n\t{\n#ifdef _WIN64\n\t\tscintilla = LoadLibrary(\"SciLexer_64.dll\");\n\t\tif (!scintilla)\n#endif\n\t\tscintilla = LoadLibrary(\"SciLexer.dll\");\n\t}\n#endif\n\n\tif (!richedit)\n\t\trichedit = LoadLibrary(\"RICHED32.DLL\");\n\n\tif (!newc && scintilla && scintillaokay)\n\t{\n\t\tnewc=CreateWindowEx(WS_EX_CLIENTEDGE,\n\t\t\t\"Scintilla\",\n\t\t\t\"\",\n\t\t\tWS_CHILD /*| ES_READONLY*/ | WS_VISIBLE | \n\t\t\tWS_HSCROLL | WS_VSCROLL | ES_LEFT | ES_WANTRETURN |\n\t\t\tES_MULTILINE | ES_AUTOVSCROLL,\n\t\t\t0, 0, 0, 0,\n\t\t\tparent,\n\t\t\tNULL,\n\t\t\tghInstance,\n\t\t\tNULL);\n\t}\n\tif (newc)\n\t\t*scintillaokay = true;\n\telse if (scintillaokay)\n\t{\n\t\t*scintillaokay = false;\n\t\tscintillaokay = NULL;\n\t}\n\n\tif (!newc)\n\t\tnewc=CreateWindowExW(WS_EX_CLIENTEDGE,\n\t\t\trichedit?RICHEDIT_CLASSW:L\"EDIT\",\n\t\t\tL\"\",\n\t\t\tWS_CHILD /*| ES_READONLY*/ | WS_VISIBLE | \n\t\t\tWS_HSCROLL | WS_VSCROLL | ES_LEFT | ES_WANTRETURN |\n\t\t\tES_MULTILINE | ES_AUTOVSCROLL,\n\t\t\t0, 0, 0, 0,\n\t\t\tparent,\n\t\t\tNULL,\n\t\t\tghInstance,\n\t\t\tNULL);\n\n\tif (!newc)\n\t\tnewc=CreateWindowEx(WS_EX_CLIENTEDGE,\n\t\t\trichedit?RICHEDIT_CLASS10A:\"EDIT\",\t//fall back to the earlier version\n\t\t\t\"\",\n\t\t\tWS_CHILD /*| ES_READONLY*/ | WS_VISIBLE | \n\t\t\tWS_HSCROLL | WS_VSCROLL | ES_LEFT | ES_WANTRETURN |\n\t\t\tES_MULTILINE | ES_AUTOVSCROLL,\n\t\t\t0, 0, 0, 0,\n\t\t\tparent,\n\t\t\tNULL,\n\t\t\tghInstance,\n\t\t\tNULL);\n\n\tif (!newc)\n\t{\t//you've not got RICHEDIT installed properly, I guess\n\t\tFreeLibrary(richedit);\n\t\trichedit = NULL;\n\t\tnewc=CreateWindowEx(WS_EX_CLIENTEDGE,\n\t\t\t\"EDIT\",\n\t\t\t\"\",\n\t\t\tWS_CHILD /*| ES_READONLY*/ | WS_VISIBLE | \n\t\t\tWS_HSCROLL | WS_VSCROLL | ES_LEFT | ES_WANTRETURN |\n\t\t\tES_MULTILINE | ES_AUTOVSCROLL,\n\t\t\t0, 0, 0, 0,\n\t\t\tparent,\n\t\t\tNULL,\n\t\t\tghInstance,\n\t\t\tNULL);\n\t}\n\tif (!newc)\n\t\treturn NULL;\n\n\tif (scintillaokay)\n\t{\n\t\tFILE *f;\n\t\tint i;\n\n\t\tSendMessage(newc, SCI_STYLERESETDEFAULT, 0, 0);\n\t\tSendMessage(newc, SCI_STYLESETFONT, STYLE_DEFAULT, (LPARAM)\"Consolas\");\n\t\tSendMessage(newc, SCI_STYLECLEARALL, 0, 0);\n\n\t\tSendMessage(newc, SCI_SETCODEPAGE, SC_CP_UTF8, 0);\n\t\tSendMessage(newc, SCI_SETLEXER,\t\tSCLEX_CPP,\t\t\t\t\t\t0);\n\t\tSendMessage(newc, SCI_STYLESETFORE, SCE_C_DEFAULT,\t\t\t\t\tRGB(0x00, 0x00, 0x00));\n\t\tSendMessage(newc, SCI_STYLECLEARALL,0,\t\t\t\t\t\t\t\t0);\n\t\tSendMessage(newc, SCI_STYLESETFORE, SCE_C_COMMENT,\t\t\t\t\tRGB(0x00, 0x80, 0x00));\n\t\tSendMessage(newc, SCI_STYLESETFORE, SCE_C_COMMENTLINE,\t\t\t\tRGB(0x00, 0x80, 0x00));\n\t\tSendMessage(newc, SCI_STYLESETFORE, SCE_C_COMMENTDOC,\t\t\t\tRGB(0x00, 0x80, 0x00));\n\t\tSendMessage(newc, SCI_STYLESETFORE, SCE_C_NUMBER,\t\t\t\t\tRGB(0xA0, 0x10, 0x10));\n\t\tSendMessage(newc, SCI_STYLESETFORE, SCE_C_WORD,\t\t\t\t\t\tRGB(0x00, 0x00, 0xFF));\n\t\tSendMessage(newc, SCI_STYLESETFORE, SCE_C_STRING,\t\t\t\t\tRGB(0xA0, 0x10, 0x10));\n\t\tSendMessage(newc, SCI_STYLESETFORE, SCE_C_CHARACTER,\t\t\t\tRGB(0xA0, 0x10, 0x10));\n//\t\tSendMessage(newc, SCI_STYLESETFORE, SCE_C_UUID,\t\t\t\t\t\tRGB(0xA0, 0x10, 0x10));\n\t\tSendMessage(newc, SCI_STYLESETFORE, SCE_C_PREPROCESSOR,\t\t\t\tRGB(0x00, 0x00, 0xFF));\n//\t\tSendMessage(newc, SCI_STYLESETFORE, SCE_C_OPERATOR,\t\t\t\t\tRGB(0x00, 0x00, 0x00));\n//\t\tSendMessage(newc, SCI_STYLESETFORE, SCE_C_IDENTIFIER,\t\t\t\tRGB(0x00, 0x00, 0x00));\n\t\tSendMessage(newc, SCI_STYLESETFORE, SCE_C_STRINGEOL,\t\t\t\tRGB(0xA0, 0x10, 0x10));\n\t\tSendMessage(newc, SCI_STYLESETFORE, SCE_C_VERBATIM,\t\t\t\t\tRGB(0xA0, 0x10, 0x10));\n\t\tSendMessage(newc, SCI_STYLESETFORE, SCE_C_REGEX,\t\t\t\t\tRGB(0xA0, 0x10, 0x10));\n\t\tSendMessage(newc, SCI_STYLESETFORE, SCE_C_COMMENTLINEDOC,\t\t\tRGB(0xA0, 0x10, 0x10));\n\t\tSendMessage(newc, SCI_STYLESETFORE, SCE_C_WORD2,\t\t\t\t\tRGB(0xA0, 0x10, 0x10));\n\t\tSendMessage(newc, SCI_STYLESETFORE, SCE_C_COMMENTDOCKEYWORD,\t\tRGB(0xA0, 0x10, 0x10));\n\t\tSendMessage(newc, SCI_STYLESETFORE, SCE_C_COMMENTDOCKEYWORDERROR,\tRGB(0xA0, 0x10, 0x10));\n\t\tSendMessage(newc, SCI_STYLESETFORE, SCE_C_GLOBALCLASS,\t\t\t\tRGB(0xA0, 0x10, 0x10));\n\t\tSendMessage(newc, SCI_STYLESETFORE, SCE_C_STRINGRAW,\t\t\t\tRGB(0xA0, 0x00, 0x00));\n\t\tSendMessage(newc, SCI_STYLESETFORE, SCE_C_TRIPLEVERBATIM,\t\t\tRGB(0xA0, 0x10, 0x10));\n\t\tSendMessage(newc, SCI_STYLESETFORE, SCE_C_HASHQUOTEDSTRING,\t\t\tRGB(0xA0, 0x10, 0x10));\n\t\tSendMessage(newc, SCI_STYLESETFORE, SCE_C_PREPROCESSORCOMMENT,\t\tRGB(0xA0, 0x10, 0x10));\n\t\tSendMessage(newc, SCI_STYLESETFORE, SCE_C_PREPROCESSORCOMMENTDOC,\tRGB(0xA0, 0x10, 0x10));\n\t\tSendMessage(newc, SCI_STYLESETFORE, SCE_C_USERLITERAL,\t\t\t\tRGB(0xA0, 0x10, 0x10));\n\t\tSendMessage(newc, SCI_STYLESETFORE, SCE_C_TASKMARKER,\t\t\t\tRGB(0xA0, 0x10, 0x10));\n\t\tSendMessage(newc, SCI_STYLESETFORE, SCE_C_ESCAPESEQUENCE,\t\t\tRGB(0xA0, 0x10, 0x10));\n\n\t\tSendMessage(newc, SCI_STYLESETFORE, STYLE_BRACELIGHT,\t\t\t\tRGB(0x00, 0x00, 0x3F));\n\t\tSendMessage(newc, SCI_STYLESETBACK, STYLE_BRACELIGHT,\t\t\t\tRGB(0xef, 0xaf, 0xaf));\n\t\tSendMessage(newc, SCI_STYLESETBOLD, STYLE_BRACELIGHT,\t\t\t\tTRUE);\n\t\tSendMessage(newc, SCI_STYLESETFORE, STYLE_BRACEBAD,\t\t\t\t\tRGB(0x3F, 0x00, 0x00));\n\t\tSendMessage(newc, SCI_STYLESETBACK, STYLE_BRACEBAD,\t\t\t\t\tRGB(0xff, 0xaf, 0xaf));\n\n\t\t//SCE_C_WORD\n\t\tSendMessage(newc, SCI_SETKEYWORDS,\t0,\t(LPARAM)\n\t\t\t\t\t\"if else for do not while asm break case const continue \"\n\t\t\t\t\t\"default enum enumflags extern \"\n\t\t\t\t\t\"float goto __in __out __inout noref \"\n\t\t\t\t\t\"nosave shared state optional string \"\n\t\t\t\t\t\"struct switch thinktime until loop \"\n\t\t\t\t\t\"typedef union var \"\n\t\t\t\t\t\"accessor get set inline \"\n\t\t\t\t\t\"virtual nonvirtual class static nonstatic local return \"\n\t\t\t\t\t\"string float vector void int integer __variant entity\"\n\t\t\t\t\t);\n\n\t\t//SCE_C_WORD2\n\t\t{\n\t\t\tchar buffer[65536];\n\t\t\tGenBuiltinsList(buffer, sizeof(buffer));\n\t\t\tSendMessage(newc, SCI_SETKEYWORDS,\t1,\t(LPARAM)buffer);\n\t\t}\n\t\t//SCE_C_COMMENTDOCKEYWORDERROR\n\t\t//SCE_C_GLOBALCLASS\n\t\tSendMessage(newc, SCI_SETKEYWORDS,\t3,\t(LPARAM)\n\t\t\t\t\t\"\"\n\t\t\t\t\t);\n\t\t//preprocessor listing\n\t\t{\n\t\t\tchar *deflist = QCC_PR_GetDefinesList();\n\t\t\tSendMessage(newc, SCI_SETKEYWORDS,\t4,\t(LPARAM)deflist);\n\t\t\tfree(deflist);\n\t\t}\n\t\t//task markers (in comments only)\n\t\tSendMessage(newc, SCI_SETKEYWORDS,\t5,\t(LPARAM)\n\t\t\t\t\t\"TODO FIXME BUG\"\n\t\t\t\t\t);\n\n\t\tSendMessage(newc, SCI_USEPOPUP, 0/*SC_POPUP_NEVER*/, 0);\t//so we can do right-click menus ourselves.\n\n\t\tSendMessage(newc, SCI_SETMOUSEDWELLTIME, 1000, 0);\n\t\tSendMessage(newc, SCI_AUTOCSETORDER, SC_ORDER_PERFORMSORT, 0);\n\t\tSendMessage(newc, SCI_AUTOCSETFILLUPS, 0, (LPARAM)\".,[<>(*/+-=\\t\\n\");\n\n\t\t//Set up gui options.\n\t\tSendMessage(newc, SCI_SETMARGINWIDTHN,\t\t0, (LPARAM)fl_extramargins?40:0);\t//line numbers+folding\n\t\tSendMessage(newc, SCI_SETTABWIDTH,\t\t\tfl_tabsize, 0);\t\t//tab size\n\n\t\t//add margin for breakpoints\n\t\tSendMessage(newc, SCI_SETMARGINMASKN,\t\t1, (LPARAM)~SC_MASK_FOLDERS);\n\t\tSendMessage(newc, SCI_SETMARGINWIDTHN,\t\t1, (LPARAM)16);\n\t\tSendMessage(newc, SCI_SETMARGINSENSITIVEN,\t1, (LPARAM)true);\n\t\t//give breakpoints a nice red circle.\n\t\tSendMessage(newc, SCI_MARKERDEFINE,\t\t\t0,\tSC_MARK_CIRCLE);\n\t\tSendMessage(newc, SCI_MARKERSETFORE,\t\t0,\tRGB(0x7F, 0x00, 0x00));\n\t\tSendMessage(newc, SCI_MARKERSETBACK,\t\t0,\tRGB(0xFF, 0x00, 0x00));\n\t\t//give current line a yellow arrow\n\t\tSendMessage(newc, SCI_MARKERDEFINE,\t\t\t1,\tSC_MARK_SHORTARROW);\n\t\tSendMessage(newc, SCI_MARKERSETFORE,\t\t1,\tRGB(0xFF, 0xFF, 0x00));\n\t\tSendMessage(newc, SCI_MARKERSETBACK,\t\t1,\tRGB(0x7F, 0x7F, 0x00));\n\t\tSendMessage(newc, SCI_MARKERDEFINE,\t\t\t2,\tSC_MARK_BACKGROUND);\n\t\tSendMessage(newc, SCI_MARKERSETFORE,\t\t2,\tRGB(0x00, 0x00, 0x00));\n\t\tSendMessage(newc, SCI_MARKERSETBACK,\t\t2,\tRGB(0xFF, 0xFF, 0x00));\n\t\tSendMessage(newc, SCI_MARKERSETALPHA,\t\t2,\t0x40);\n\n\t\t//add margin for folding\n\t\tSendMessage(newc, SCI_SETPROPERTY,  (WPARAM)\"fold\", (LPARAM)\"1\");\n\t\tSendMessage(newc, SCI_SETMARGINWIDTHN,\t\t2, (LPARAM)fl_extramargins?16:0);\n\t\tSendMessage(newc, SCI_SETMARGINMASKN,\t\t2, (LPARAM)SC_MASK_FOLDERS);\n\t\tSendMessage(newc, SCI_SETMARGINSENSITIVEN,\t2, (LPARAM)true);\n\t\t//stop the images from being stupid\n\t\tSendMessage(newc, SCI_MARKERDEFINE,\t\tSC_MARKNUM_FOLDEROPEN,\t\tSC_MARK_BOXMINUS);\n\t\tSendMessage(newc, SCI_MARKERDEFINE,\t\tSC_MARKNUM_FOLDER,\t\t\tSC_MARK_BOXPLUS);\n\t\tSendMessage(newc, SCI_MARKERDEFINE,\t\tSC_MARKNUM_FOLDERSUB,\t\tSC_MARK_VLINE);\n\t\tSendMessage(newc, SCI_MARKERDEFINE,\t\tSC_MARKNUM_FOLDERTAIL,\t\tSC_MARK_LCORNERCURVE);\n\t\tSendMessage(newc, SCI_MARKERDEFINE,\t\tSC_MARKNUM_FOLDEREND,\t\tSC_MARK_BOXPLUSCONNECTED);\n\t\tSendMessage(newc, SCI_MARKERDEFINE,\t\tSC_MARKNUM_FOLDEROPENMID,\tSC_MARK_BOXMINUSCONNECTED);\n\t\tSendMessage(newc, SCI_MARKERDEFINE,\t\tSC_MARKNUM_FOLDERMIDTAIL,\tSC_MARK_TCORNERCURVE);\n\t\t//and fuck with colours so that its visible.\n#define FOLDBACK RGB(0x50, 0x50, 0x50)\n\t\tSendMessage(newc, SCI_MARKERSETFORE,\tSC_MARKNUM_FOLDER,\t\t\tRGB(0xFF, 0xFF, 0xFF));\n\t\tSendMessage(newc, SCI_MARKERSETBACK,\tSC_MARKNUM_FOLDER,\t\t\tFOLDBACK);\n\t\tSendMessage(newc, SCI_MARKERSETFORE,\tSC_MARKNUM_FOLDEROPEN,\t\tRGB(0xFF, 0xFF, 0xFF));\n\t\tSendMessage(newc, SCI_MARKERSETBACK,\tSC_MARKNUM_FOLDEROPEN,\t\tFOLDBACK);\n\t\tSendMessage(newc, SCI_MARKERSETFORE,\tSC_MARKNUM_FOLDEROPENMID,\tRGB(0xFF, 0xFF, 0xFF));\n\t\tSendMessage(newc, SCI_MARKERSETBACK,\tSC_MARKNUM_FOLDEROPENMID,\tFOLDBACK);\n\t\tSendMessage(newc, SCI_MARKERSETBACK,\tSC_MARKNUM_FOLDERSUB,\t\tFOLDBACK);\n\t\tSendMessage(newc, SCI_MARKERSETFORE,\tSC_MARKNUM_FOLDEREND,\t\tRGB(0xFF, 0xFF, 0xFF));\n\t\tSendMessage(newc, SCI_MARKERSETBACK,\tSC_MARKNUM_FOLDEREND,\t\tFOLDBACK);\n\t\tSendMessage(newc, SCI_MARKERSETBACK,\tSC_MARKNUM_FOLDERTAIL,\t\tFOLDBACK);\n\t\tSendMessage(newc, SCI_MARKERSETBACK,\tSC_MARKNUM_FOLDERMIDTAIL,\tFOLDBACK);\n\n\t\t//disable preprocessor tracking, because QC preprocessor is not specific to an individual file, and even if it was, includes would be messy.\n//\t\tSendMessage(newc, SCI_SETPROPERTY,  (WPARAM)\"lexer.cpp.track.preprocessor\", (LPARAM)\"0\");\n\n\t\tfor (i = 0; i < 0x100; i++)\n\t\t{\n\t\t\tchar *lowtab[32] = {\"QNUL\",NULL,NULL,NULL,NULL,\".\",NULL,NULL,NULL,NULL,NULL,\"#\",NULL,\">\",\".\",\".\",\n\t\t\t\t\t\t\t\t\"[\",\"]\",\"0\",\"1\",\"2\",\"3\",\"4\",\"5\",\"6\",\"7\",\"8\",\"9\",\".\",\"<-\",\"-\",\"->\"};\n\t\t\tchar *hightab[32] = {\"(=\",\"=\",\"=)\",\"=#=\",\"White\",\".\",\"Green\",\"Red\",\"Yellow\",\"Blue\",NULL,\"Purple\",NULL,\">\",\".\",\".\",\n\t\t\t\t\t\t\t\t\"[\",\"]\",\"0\",\"1\",\"2\",\"3\",\"4\",\"5\",\"6\",\"7\",\"8\",\"9\",\".\",\"<-\",\"-\",\"->\"};\n\t\t\tchar foo[4];\n\t\t\tchar bar[4];\n\t\t\tunsigned char c = i;\n\t\t\tfoo[0] = i;\t//these are invalid encodings or control chars.\n\t\t\tfoo[1] = 0;\n\n\t\t\tif (c >= 0 && c < 32)\n\t\t\t{\n\t\t\t\tif (lowtab[c])\n\t\t\t\t\tSendMessage(newc, SCI_SETREPRESENTATION,\t(WPARAM)foo,\t(LPARAM)lowtab[c]);\n\t\t\t}\n\t\t\telse if (c >= (128|0) && c < (128|32))\n\t\t\t{\n\t\t\t\tif (hightab[c-128])\n\t\t\t\t\tSendMessage(newc, SCI_SETREPRESENTATION,\t(WPARAM)foo,\t(LPARAM)hightab[c-128]);\n\t\t\t}\n\t\t\telse if (c < 128)\n\t\t\t\tcontinue;\t//don't do anything weird for ascii (other than control chars)\n\t\t\telse\n\t\t\t{\n\t\t\t\tint b = 0;\n\t\t\t\tbar[b++] = c&0x7f;\n\t\t\t\tbar[b++] = 0;\n\t\t\t\tSendMessage(newc, SCI_SETREPRESENTATION,\t(WPARAM)foo,\t(LPARAM)bar);\n\t\t\t}\n\t\t}\n\n\t\tfor (i = 0xe000; i < 0xe100; i++)\n\t\t{\n\t\t\tchar *lowtab[32] = {\"QNUL\",NULL,NULL,NULL,NULL,\".\",NULL,NULL,NULL,NULL,NULL,\"#\",NULL,\">\",\".\",\".\",\n\t\t\t\t\t\t\t\t\"[\",\"]\",\"0\",\"1\",\"2\",\"3\",\"4\",\"5\",\"6\",\"7\",\"8\",\"9\",\".\",\"<-\",\"-\",\"->\"};\n\t\t\tchar *hightab[32] = {\"(=\",\"=\",\"=)\",\"=#=\",\"White\",\".\",\"Green\",\"Red\",\"Yellow\",\"Blue\",NULL,\"Purple\",NULL,\">\",\".\",\".\",\n\t\t\t\t\t\t\t\t\"[\",\"]\",\"^0\",\"^1\",\"^2\",\"^3\",\"^4\",\"^5\",\"^6\",\"^7\",\"^8\",\"^9\",\".\",\"^<-\",\"^-\",\"^->\"};\n\t\t\tchar foo[4];\n\t\t\tchar bar[4];\n\t\t\tunsigned char c = i;\n\t\t\tfoo[0] = ((i>>12) & 0xf) | 0xe0;\n\t\t\tfoo[1] = ((i>>6) & 0x3f) | 0x80;\n\t\t\tfoo[2] = ((i>>0) & 0x3f) | 0x80;\n\t\t\tfoo[3] = 0;\n\n\t\t\tif (c >= 0 && c < 32)\n\t\t\t{\n\t\t\t\tif (lowtab[c])\n\t\t\t\t\tSendMessage(newc, SCI_SETREPRESENTATION,\t(WPARAM)foo,\t(LPARAM)lowtab[c]);\n\t\t\t}\n\t\t\telse if (c >= (128|0) && c < (128|32))\n\t\t\t{\n\t\t\t\tif (hightab[c-128])\n\t\t\t\t\tSendMessage(newc, SCI_SETREPRESENTATION,\t(WPARAM)foo,\t(LPARAM)hightab[c-128]);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tint b = 0;\n\t\t\t\tif (c >= 128)\n\t\t\t\t\tbar[b++] = '^';\n\t\t\t\tbar[b++] = c&0x7f;\n\t\t\t\tbar[b++] = 0;\n\t\t\t\tSendMessage(newc, SCI_SETREPRESENTATION,\t(WPARAM)foo,\t(LPARAM)bar);\n\t\t\t}\n\t\t}\n\n\n\t\tf = fopen(\"scintilla.cfg\", \"rt\");\n\t\tif (f)\n\t\t{\n\t\t\tchar buf[256];\n\t\t\twhile(fgets(buf, sizeof(buf)-1, f))\n\t\t\t{\n\t\t\t\tint msg;\n\t\t\t\tLPARAM lparam;\n\t\t\t\tWPARAM wparam;\n\t\t\t\tchar *c;\n\t\t\t\tbuf[sizeof(buf)-1] = 0;\n\t\t\t\tc = buf;\n\t\t\t\twhile(*c == ' ' || *c == '\\t')\n\t\t\t\t\tc++;\n\t\t\t\tif (c[0] == '#')\n\t\t\t\t\tcontinue;\n\t\t\t\tif (c[0] == '/' && c[1] == '/')\n\t\t\t\t\tcontinue;\n\t\t\t\tif (c[0] == '\\r' || c[0] == '\\n' || !c[0])\n\t\t\t\t\tcontinue;\n\t\t\t\tmsg = strtoul(c, &c, 0);\n\t\t\t\twhile(*c == ' ' || *c == '\\t')\n\t\t\t\t\tc++;\n\t\t\t\tif (*c == '\\\"')\n\t\t\t\t{\n\t\t\t\t\tc++;\n\t\t\t\t\twparam = (LPARAM)c;\n\t\t\t\t\tc = strrchr(c, '\\\"');\n\t\t\t\t\tif (c)\n\t\t\t\t\t\t*c++ = 0;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\twparam = strtoul(c, &c, 0);\n\t\t\t\twhile(*c == ' ' || *c == '\\t')\n\t\t\t\t\tc++;\n\t\t\t\tif (*c == '\\\"')\n\t\t\t\t{\n\t\t\t\t\tc++;\n\t\t\t\t\tlparam = (LPARAM)c;\n\t\t\t\t\tc = strrchr(c, '\\\"');\n\t\t\t\t\tif (c)\n\t\t\t\t\t\t*c++ = 0;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tlparam = strtoul(c, &c, 0);\n\n\t\t\t\tSendMessage(newc, msg,\twparam,\tlparam);\n\t\t\t}\n\t\t\tif (!ftell(f))\n\t\t\t{\n\t\t\t\tfclose(f);\n\t\t\t\tf = fopen(\"scintilla.cfg\", \"wt\");\n\t\t\t\tif (f)\n\t\t\t\t{\n\t\t\t\t\tint i;\n\t\t\t\t\tint val;\n\t\t\t\t\tfor (i = 0; i < STYLE_LASTPREDEFINED; i++)\n\t\t\t\t\t{\n\t\t\t\t\t\tval = SendMessage(newc, SCI_STYLEGETFORE,\ti,\t0);\n\t\t\t\t\t\tfprintf(f, \"%i\\t%i\\t%#x\\n\", SCI_STYLESETFORE, i, val);\n\t\t\t\t\t\tval = SendMessage(newc, SCI_STYLEGETBACK,\ti,\t0);\n\t\t\t\t\t\tfprintf(f, \"%i\\t%i\\t%#x\\n\", SCI_STYLESETBACK, i, val);\n\t\t\t\t\t\tval = SendMessage(newc, SCI_STYLEGETBOLD,\ti,\t0);\n\t\t\t\t\t\tfprintf(f, \"%i\\t%i\\t%#x\\n\", SCI_STYLESETBOLD, i, val);\n\t\t\t\t\t\tval = SendMessage(newc, SCI_STYLEGETITALIC,\ti,\t0);\n\t\t\t\t\t\tfprintf(f, \"%i\\t%i\\t%#x\\n\", SCI_STYLESETITALIC, i, val);\n\t\t\t\t\t\tval = SendMessage(newc, SCI_STYLEGETSIZE,\ti,\t0);\n\t\t\t\t\t\tfprintf(f, \"%i\\t%i\\t%#x\\n\", SCI_STYLESETSIZE, i, val);\n\t\t\t\t\t\tval = SendMessage(newc, SCI_STYLEGETFONT,\ti,\t(LPARAM)buf);\n\t\t\t\t\t\tfprintf(f, \"%i\\t%i\\t\\\"%s\\\"\\n\", SCI_STYLESETFONT, i, buf);\n\t\t\t\t\t\tval = SendMessage(newc, SCI_STYLEGETUNDERLINE,\ti,\t0);\n\t\t\t\t\t\tfprintf(f, \"%i\\t%i\\t%#x\\n\", SCI_STYLESETUNDERLINE, i, val);\n\t\t\t\t\t\tval = SendMessage(newc, SCI_STYLEGETCASE,\ti,\t0);\n\t\t\t\t\t\tfprintf(f, \"%i\\t%i\\t%#x\\n\", SCI_STYLESETCASE, i, val);\n\t\t\t\t\t}\n\t\t\t\t\tfclose(f);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t\tfclose(f);\n\t\t}\n\t}\n\telse\n\t{\n\t\t//go to lucidia console, 10pt\n\t\tCHARFORMAT cf;\n\t\tmemset(&cf, 0, sizeof(cf));\n\t\tcf.cbSize = sizeof(cf);\n\t\tcf.dwMask = CFM_BOLD | CFM_FACE;// | CFM_SIZE;\n\t\tstrcpy(cf.szFaceName, \"Lucida Console\");\n\t\tcf.yHeight = 5;\n\n\t\tSendMessage(newc, EM_SETCHARFORMAT, SCF_ALL, (WPARAM)&cf);\n\t\n\t\tif (richedit)\n\t\t{\n\t\t\tSendMessage(newc, EM_EXLIMITTEXT, 0, 1<<20);\n\t\t}\n\t}\n\n\tif (!pDefSubclassProc || !pSetWindowSubclass)\n\t{\n\t\tHMODULE lib = LoadLibrary(\"comctl32.dll\");\n\t\tif (lib)\n\t\t{\n\t\t\tpDefSubclassProc = (void*)GetProcAddress(lib, \"DefSubclassProc\");\n\t\t\tpSetWindowSubclass = (void*)GetProcAddress(lib, \"SetWindowSubclass\");\n\t\t}\n\t}\n\tif (pDefSubclassProc && pSetWindowSubclass)\n\t\tpSetWindowSubclass(newc, MySubclassWndProc, 0, (DWORD_PTR)parent);\n\n\tShowWindow(newc, SW_SHOW);\n\n\treturn newc;\n}\n\n\n\n\nenum {\n\tIDM_OPENDOCU=32,\n\tIDM_OPENNEW,\n\tIDM_OPENPROJECT,\n\tIDM_GREP,\n\tIDM_GOTODEF,\n\tIDM_RETURNDEF,\n\tIDM_OUTPUT_WINDOW,\n\tIDM_UI_SHOWLINENUMBERS,\n\tIDM_UI_TABSIZE,\n\tIDM_SAVE,\n\tIDM_RECOMPILE,\n\tIDM_FIND,\n\tIDM_FINDNEXT,\n\tIDM_FINDPREV,\n\tIDM_QUIT,\n\tIDM_UNDO,\n\tIDM_REDO,\n\tIDM_CUT,\n\tIDM_COPY,\n\tIDM_PASTE,\n\tIDM_ABOUT,\n\tIDM_CASCADE,\n\tIDM_TILE_HORIZ,\n\tIDM_TILE_VERT,\n\tIDM_DEBUG_REBUILD,\n\tIDM_DEBUG_BUILD_OPTIONS,\n\tIDM_DEBUG_SETNEXT,\n\tIDM_DEBUG_RUN,\n\tIDM_DEBUG_STEPOVER,\n\tIDM_DEBUG_STEPINTO,\n\tIDM_DEBUG_STEPOUT,\n\tIDM_DEBUG_TOGGLEBREAK,\n\tIDM_ENCODING_PRIVATEUSE,\n\tIDM_ENCODING_DEPRIVATEUSE,\n\tIDM_ENCODING_UNIX,\n\tIDM_ENCODING_WINDOWS,\n\tIDM_CREATEINSTALLER_WINDOWS,\n\tIDM_CREATEINSTALLER_ANDROID,\n\tIDM_CREATEINSTALLER_PACKAGES,\n\n\tIDI_O_LEVEL0,\n\tIDI_O_LEVEL1,\n\tIDI_O_LEVEL2,\n\tIDI_O_LEVEL3,\n\tIDI_O_DEFAULT,\n\tIDI_O_DEBUG,\n//\tIDI_O_CHANGE_PROGS_SRC,\n\tIDI_O_ADDITIONALPARAMETERS,\n\tIDI_O_OPTIMISATION,\n\tIDI_O_COMPILER_FLAG,\n\tIDI_O_APPLYSAVE,\n\tIDI_O_APPLY,\n\tIDI_O_TARGETH2,\n\tIDI_O_TARGETFTE,\n\tIDI_O_ENGINE,\n\tIDI_O_ENGINEBASEDIR,\n\tIDI_O_ENGINECOMMANDLINE,\n\n\tIDM_FIRSTCHILD\n};\n\nstatic void EditorReload(editor_t *editor);\nint EditorSave(editor_t *edit);\nvoid EditFile(const char *name, int line, pbool setcontrol);\npbool EditorModified(editor_t *e);\n\nvoid QueryOpenFile(void)\n{\n\tchar filename[MAX_PATH];\n\tchar oldpath[MAX_PATH+10];\n\tOPENFILENAME ofn;\n\tmemset(&ofn, 0, sizeof(ofn));\n\tofn.lStructSize = sizeof(ofn);\n\tofn.hInstance = ghInstance;\n\tofn.lpstrFile = filename;\n\tofn.nMaxFile = sizeof(filename)-1;\n\tmemset(filename, 0, sizeof(filename));\n\tGetCurrentDirectory(sizeof(oldpath)-1, oldpath);\n\tif (GetOpenFileName(&ofn))\n\t\tEditFile(filename, -1, false);\n\tSetCurrentDirectory(oldpath);\n}\n\nstatic void Packager_MessageCallback(void *ctx, const char *fmt, ...);\n\n//IDM_ stuff that needs no active window\nvoid GenericMenu(WPARAM wParam)\n{\n\tswitch(LOWORD(wParam))\n\t{\n\tcase IDM_OPENPROJECT:\n\t\t{\n\t\t\tchar filename[MAX_PATH];\n\t\t\tchar oldpath[MAX_PATH+10];\n\t\t\tOPENFILENAME ofn;\n\t\t\tmemset(&ofn, 0, sizeof(ofn));\n\t\t\tofn.lStructSize = sizeof(ofn);\n\t\t\tofn.hInstance = ghInstance;\n\t\t\tofn.lpstrFile = filename;\n\t\t\tofn.lpstrTitle = \"Please find progs.src or progs.dat\";\n\t\t\tofn.nMaxFile = sizeof(filename)-1;\n\t\t\tofn.lpstrFilter = \"QuakeC Projects\\0*.src;*.dat\\0All files\\0*.*\\0\";\n\t\t\tmemset(filename, 0, sizeof(filename));\n\t\t\tGetCurrentDirectory(sizeof(oldpath)-1, oldpath);\n\t\t\tofn.lpstrInitialDir = oldpath;\n\t\t\tif (GetOpenFileName(&ofn))\n\t\t\t{\n\t\t\t\tSetProgsSrcFileAndPath(filename);\n\t\t\t}\n\t\t\tresetprogssrc = true;\n\t\t}\n\t\tbreak;\n\n\tcase IDM_OPENNEW:\n\t\tQueryOpenFile();\n\t\tbreak;\n\n\tcase IDM_QUIT:\n\t\tPostQuitMessage(0);\n\t\tbreak;\n\n\tcase IDM_RECOMPILE:\n\t\tbuttons[ID_COMPILE].washit = true;\n\t\tbreak;\n\n\tcase IDM_CREATEINSTALLER_WINDOWS:\n\t\tGUI_CreateInstaller_Windows();\n\t\tbreak;\n\tcase IDM_CREATEINSTALLER_ANDROID:\n\t\tGUI_CreateInstaller_Android();\n\t\tbreak;\n\tcase IDM_CREATEINSTALLER_PACKAGES:\n\t\t{\n\t\t\tstruct pkgctx_s *ctx;\n\n\t\t\tCreateOutputWindow(false);\n\t\t\tGUIprintf(\"\");\n\n\t\t\tctx = Packager_Create(Packager_MessageCallback, NULL);\n\t\t\tPackager_ParseFile(ctx, \"packages.src\");\n\t\t\tPackager_WriteDataset(ctx, NULL);\n\t\t\tPackager_Destroy(ctx);\n\t\t}\n\t\tbreak;\n\n\tcase IDM_ABOUT:\n#if defined(SVNREVISION) && defined(SVNDATE)\n\t\tMessageBox(NULL, \"FTE QuakeC Compiler \"STRINGIFY(SVNREVISION)\" (\"STRINGIFY(SVNDATE)\")\\nWritten by Forethought Entertainment, whoever that is.\\n\\nIf you have problems with wordpad corrupting your qc files, try saving them using utf-16 encoding via notepad.\\nDecompiler component derived from frikdec.\", \"About\", 0);\n#elif defined(SVNREVISION)\n\t\tMessageBox(NULL, \"FTE QuakeC Compiler \"STRINGIFY(SVNREVISION)\" (\"__DATE__\" \"__TIME__\")\\nWritten by Forethought Entertainment, whoever that is.\\n\\nIf you have problems with wordpad corrupting your qc files, try saving them using utf-16 encoding via notepad.\\nDecompiler component derived from frikdec.\", \"About\", 0);\n#else\n\t\tMessageBox(NULL, \"FTE QuakeC Compiler (\"__DATE__\")\\nWritten by Forethought Entertainment, whoever that is.\\n\\nIf you have problems with wordpad corrupting your qc files, try saving them using utf-16 encoding via notepad.\\nDecompiler component derived from frikdec.\", \"About\", 0);\n#endif\n\t\tbreak;\n\n\tcase IDM_CASCADE:\n\t\tSendMessage(mdibox, WM_MDICASCADE, 0, 0);\n\t\tbreak;\n\tcase IDM_TILE_HORIZ:\n\t\tSendMessage(mdibox, WM_MDITILE, MDITILE_HORIZONTAL, 0);\n\t\tbreak;\n\tcase IDM_TILE_VERT:\n\t\tSendMessage(mdibox, WM_MDITILE, MDITILE_VERTICAL, 0);\n\t\tbreak;\n\n\n\tcase IDM_OUTPUT_WINDOW:\n\t\tif (GetFocus() == outputbox)\n\t\t\tSplitterRemove(outputbox);\n\t\telse\n\t\t\tSplitterFocus(outputbox, 64, 128);\n\t\tbreak;\n\tcase IDM_UI_SHOWLINENUMBERS:\n\t\t{\n\t\t\teditor_t *ed;\n\t\t\tMENUITEMINFO mii = {sizeof(mii)};\n\t\t\tfl_extramargins = !fl_extramargins;\n\t\t\tmii.fMask = MIIM_STATE;\n\t\t\tmii.fState = fl_extramargins?MFS_CHECKED:MFS_UNCHECKED;\n\t\t\tSetMenuItemInfo(GetMenu(mainwindow), IDM_UI_SHOWLINENUMBERS, FALSE, &mii);\n\n\t\t\tfor (ed = editors; ed; ed = ed->next)\n\t\t\t{\n\t\t\t\tif (ed->scintilla)\n\t\t\t\t{\n\t\t\t\t\tSendMessage(ed->editpane, SCI_SETMARGINWIDTHN,\t\t0, (LPARAM)fl_extramargins?40:0);\n\t\t\t\t\tSendMessage(ed->editpane, SCI_SETMARGINWIDTHN,\t\t2, (LPARAM)fl_extramargins?16:0);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tbreak;\n\tcase IDM_UI_TABSIZE:\n\t\t{\n\t\t\teditor_t *ed;\n\t\t\tMENUITEMINFO mii = {sizeof(mii)};\n\t\t\tfl_tabsize = (fl_tabsize>4)?4:8;\n\t\t\tmii.fMask = MIIM_STATE;\n\t\t\tmii.fState = (fl_tabsize>4)?MFS_CHECKED:MFS_UNCHECKED;\n\t\t\tSetMenuItemInfo(GetMenu(mainwindow), IDM_UI_TABSIZE, FALSE, &mii);\n\n\t\t\tfor (ed = editors; ed; ed = ed->next)\n\t\t\t{\n\t\t\t\tif (ed->scintilla)\n\t\t\t\t{\n\t\t\t\t\tSendMessage(ed->editpane, SCI_SETTABWIDTH,\t\tfl_tabsize, 0);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tbreak;\n\tcase IDM_DEBUG_RUN:\n\t\tEditFile(NULL, -1, true);\n\t\tEngineGiveFocus();\n\t\tif (!EngineCommandf(\"qcresume\\n\"))\n\t\t\tRunEngine();\n\t\treturn;\n\tcase IDM_DEBUG_REBUILD:\n\t\tbuttons[ID_COMPILE].washit = true;\n\t\treturn;\n\tcase IDM_DEBUG_BUILD_OPTIONS:\n\t\tOptionsDialog();\n\t\treturn;\n\tcase IDM_DEBUG_STEPOVER:\n\t\tEditFile(NULL, -1, true);\n\t\tEngineCommandf(\"qcstep over\\n\");\n\t\treturn;\n\tcase IDM_DEBUG_STEPINTO:\n\t\tEditFile(NULL, -1, true);\n\t\tEngineCommandf(\"qcstep into\\n\");\n\t\treturn;\n\tcase IDM_DEBUG_STEPOUT:\n\t\tEditFile(NULL, -1, true);\n\t\tEngineCommandf(\"qcstep out\\n\");\n\t\treturn;\n\t}\n}\n\nstatic char *WordUnderCursor(editor_t *editor, char *word, int wordsize, char *term, int termsize, int position)\n{\n\tunsigned char linebuf[1024];\n\tDWORD charidx;\n\tDWORD lineidx;\n\tPOINT pos;\n\tRECT rect;\n\tif (editor->scintilla)\n\t{\n\t\tDWORD len;\n\n\t\tlineidx = SendMessage(editor->editpane, SCI_LINEFROMPOSITION, position, 0);\n\t\tcharidx = position - SendMessage(editor->editpane, SCI_POSITIONFROMLINE, lineidx, 0);\n\n\t\tlen = SendMessage(editor->editpane, SCI_LINELENGTH, lineidx, 0);\n\t\tif (len >= sizeof(linebuf))\n\t\t\treturn \"\";\n\t\tlen = SendMessage(editor->editpane, SCI_GETLINE, lineidx, (LPARAM)linebuf);\n\t\tlinebuf[len] = 0;\n\t\tif (charidx >= len)\n\t\t\tcharidx = len-1;\n\t}\n\telse\n\t{\n\t\tGetCursorPos(&pos);\n\t\tGetWindowRect(editor->editpane, &rect);\n\t\tpos.x -= rect.left;\n\t\tpos.y -= rect.top;\n\t\tcharidx = SendMessage(editor->editpane, EM_CHARFROMPOS, 0, (LPARAM)&pos);\n\t\tlineidx = SendMessage(editor->editpane, EM_LINEFROMCHAR, charidx, 0);\n\t\tcharidx -= SendMessage(editor->editpane, EM_LINEINDEX, lineidx, 0);\n\t\tEdit_GetLine(editor->editpane, lineidx, linebuf, sizeof(linebuf));\n\t}\n\n\tif (word)\n\t{\n\t\t//skip back to the start of the word\n\t\twhile(charidx > 0 && (\n\t\t\t(linebuf[charidx-1] >= 'a' && linebuf[charidx-1] <= 'z') ||\n\t\t\t(linebuf[charidx-1] >= 'A' && linebuf[charidx-1] <= 'Z') ||\n\t\t\t(linebuf[charidx-1] >= '0' && linebuf[charidx-1] <= '9') ||\n\t\t\tlinebuf[charidx-1] == '_' || linebuf[charidx-1] == ':' ||\n\t\t\tlinebuf[charidx-1] >= 128\n\t\t\t))\n\t\t{\n\t\t\tcharidx--;\n\t\t}\n\t\t//copy the result out\n\t\tlineidx = 0;\n\t\twordsize--;\n\t\twhile (wordsize && (\n\t\t\t(linebuf[charidx] >= 'a' && linebuf[charidx] <= 'z') ||\n\t\t\t(linebuf[charidx] >= 'A' && linebuf[charidx] <= 'Z') ||\n\t\t\t(linebuf[charidx] >= '0' && linebuf[charidx] <= '9') ||\n\t\t\tlinebuf[charidx] == '_' || linebuf[charidx] == ':' ||\n\t\t\tlinebuf[charidx] >= 128\n\t\t\t))\n\t\t{\n\t\t\tword[lineidx++] = linebuf[charidx++];\n\t\t\twordsize--;\n\t\t}\n\t\tword[lineidx++] = 0;\n\t}\n\n\tif (term)\n\t{\n\t\t//skip back to the start of the word\n\t\twhile(charidx > 0 && (\n\t\t\t(linebuf[charidx-1] >= 'a' && linebuf[charidx-1] <= 'z') ||\n\t\t\t(linebuf[charidx-1] >= 'A' && linebuf[charidx-1] <= 'Z') ||\n\t\t\t(linebuf[charidx-1] >= '0' && linebuf[charidx-1] <= '9') ||\n\t\t\tlinebuf[charidx-1] == '_' || linebuf[charidx-1] == ':' || linebuf[charidx-1] == '.' ||\n\t\t\tlinebuf[charidx-1] == '[' || linebuf[charidx-1] == ']' ||\n\t\t\tlinebuf[charidx-1] >= 128\n\t\t\t))\n\t\t{\n\t\t\tcharidx--;\n\t\t}\n\t\t//copy the result out\n\t\tlineidx = 0;\n\t\ttermsize--;\n\t\twhile (termsize && (\n\t\t\t(linebuf[charidx] >= 'a' && linebuf[charidx] <= 'z') ||\n\t\t\t(linebuf[charidx] >= 'A' && linebuf[charidx] <= 'Z') ||\n\t\t\t(linebuf[charidx] >= '0' && linebuf[charidx] <= '9') ||\n\t\t\tlinebuf[charidx] == '_' || linebuf[charidx] == ':' || linebuf[charidx] == '.' ||\n\t\t\tlinebuf[charidx] == '[' || linebuf[charidx] == ']' ||\n\t\t\tlinebuf[charidx] >= 128\n\t\t\t))\n\t\t{\n\t\t\tterm[lineidx++] = linebuf[charidx++];\n\t\t\ttermsize--;\n\t\t}\n\t\tterm[lineidx++] = 0;\n\t}\n\treturn word;\n}\nstatic char *ReadTextSelection(editor_t *editor, char *word, int wordsize)\n{\n\tint total;\n\tif (editor->scintilla)\n\t{\n\t\ttotal = SendMessage(editor->editpane, SCI_GETSELTEXT, 0, (LPARAM)NULL);\n\t\tif (total < wordsize)\n\t\t\ttotal = SendMessage(editor->editpane, SCI_GETSELTEXT, 0, (LPARAM)word);\n\t\telse\n\t\t\ttotal = 0;\n\t}\n\telse\n\t{\n\t\tCHARRANGE ffs;\n\t\tSendMessage(editor->editpane, EM_EXGETSEL, 0, (LPARAM)&ffs);\n\t\tif (ffs.cpMax-ffs.cpMin > wordsize-1)\n\t\t\ttotal = 0;\t//don't crash through the use of a crappy API.\n\t\telse\n\t\t\ttotal = SendMessage(editor->editpane, EM_GETSELTEXT, (WPARAM)0, (LPARAM)word);\n\t}\n\tif (total)\n\t\tword[total]='\\0';\n\telse\n\t{\n\t\tif (*WordUnderCursor(editor, word, wordsize, NULL, 0, SendMessage(editor->editpane, SCI_GETCURRENTPOS, 0, 0)))\n\t\t\treturn word;\n\t\treturn NULL;\n\t}\n\treturn word;\n}\n\nstatic void GUI_Recode(editor_t *editor, int target)\n{\n\tif (target == UTF8_BOM && editor->savefmt == UTF_ANSI)\n\t{\t//we're currently using some ansi-like format. convert it to quake's format.\n\t\tpbool errors = false;\n\t\tint len;\n\t\tchar *wfile, *in;\n\n\t\tif (IDCANCEL==MessageBox(editor->window, \"Really convert?\", editor->filename, MB_OKCANCEL))\n\t\t\treturn;\n\n\t\tif (editor->scintilla)\n\t\t{\n\t\t\tchar *afile, *out;\n\t\t\tlen = SendMessage(editor->editpane, SCI_GETLENGTH, 0, 0);\n\t\t\twfile = malloc(len+1);\n\t\t\tSendMessage(editor->editpane, SCI_GETTEXT, len, (LPARAM)wfile);\n\t\t\twfile[len] = 0;\n\n\t\t\tafile = malloc((len+1)*3);\n\n\t\t\tin = wfile;\n\t\t\tout = afile;\n\t\t\twhile(*in)\n\t\t\t{\n\t\t\t\tunsigned int c = (unsigned char)*in++;\n\t\t\t\t//fixme: do we care about ascii control codes? quake tends not to, but also abuses them...\n\t\t\t\tif ((c >= 32 && c < 0x80) || c == '\\n' || c == '\\r' || c == '\\t')\n\t\t\t\t\t*out++ = c;\t//ascii chars are still ascii\n\t\t\t\telse if (c >= 0 && c < 0xff) //controll chars and high-value chars are not considered ascii and thus not safe\n\t\t\t\t{\n\t\t\t\t\tc |= 0xe000;\t//maps to private use\n\n//\t\t\t\t\t*out++ = ((c>>6) & 0x1f) | 0xc0;\n//\t\t\t\t\t*out++ = ((c>>0) & 0x3f) | 0x80;\n\n\t\t\t\t\t*out++ = ((c>>12) & 0xf) | 0xe0;\n\t\t\t\t\t*out++ = ((c>>6) & 0x3f) | 0x80;\n\t\t\t\t\t*out++ = ((c>>0) & 0x3f) | 0x80;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t*out++ = c;\n\t\t\t\t\terrors = true;\n\t\t\t\t}\n\t\t\t}\n\t\t\t*out++ = 0;\n\n\t\t\tif (errors)\n\t\t\t\terrors = IDCANCEL==MessageBox(editor->window, \"Encoding quake's char set to utf-8 will corrupt some characters (and cannot be displayed correctly in this editor). continue anyway?\", editor->filename, MB_OKCANCEL);\n\n\t\t\tif (!errors)\n\t\t\t{\n\t\t\t\teditor->savefmt = UTF8_BOM;\t//always use a bom, because notepad is shite.\n\t\t\t\tSendMessage(editor->editpane, SCI_SETTEXT, 0, (LPARAM)afile);\n\t\t\t\tSendMessage(editor->editpane, SCI_SETCODEPAGE, SC_CP_UTF8, 0);\n\t\t\t}\n\t\t\tfree(afile);\n\t\t}\n\t\telse\n\t\t{\n\t\t\twchar_t *afile, *out;\n\n\t\t\tlen = GetWindowTextLengthA(editor->editpane);\n\t\t\twfile = malloc(len+1);\n\t\t\tGetWindowTextA(editor->editpane, wfile, len+1);\n\t\t\n\t\t\tafile = malloc((len+1)*2);\n\n\t\t\tin = wfile;\n\t\t\tout = afile;\n\t\t\twhile(*in)\n\t\t\t{\n\t\t\t\tunsigned char c = *in++;\n\t\t\t\t//fixme: do we care about ascii control codes? quake tends not to, but also abuses them...\n\t\t\t\tif ((c >= 32 && c < 0x80) || c == '\\n' || c == '\\r' || c == '\\t')\n\t\t\t\t\t*out++ = c;\t//ascii chars are still ascii\n\t\t\t\telse if (c >= 0 && c < 0xff) //controll chars and high-value chars are not considered ascii and thus not safe\n\t\t\t\t\t*out++ = c | 0xe000;\t//maps to private use\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t*out++ = c;\n\t\t\t\t\terrors = true;\n\t\t\t\t}\n\t\t\t}\n\t\t\t*out++ = 0;\n\n\t\t\tif (errors)\n\t\t\t\terrors = IDCANCEL==MessageBox(editor->window, \"Encoding quake's char set to utf-8 will corrupt some characters (and cannot be displayed correctly in this editor). continue anyway?\", editor->filename, MB_OKCANCEL);\n\n\t\t\tif (!errors)\n\t\t\t{\n\t\t\t\teditor->savefmt = UTF8_BOM;\t//always use a bom, because notepad is shite.\n\t\t\t\tSetWindowTextW(editor->editpane, afile);\n\t\t\t}\n\t\t\tfree(afile);\n\t\t}\n\t\t\n\t\tfree(wfile);\n\t}\n\telse if (target == UTF_ANSI && editor->savefmt != UTF_ANSI)\n\t{\t//we're currently using some unicode format. convert it to quake's format.\n\t\tpbool errors = false;\n\t\tint len;\n\t\twchar_t *wfile, *in;\n\t\tchar *afile, *out;\n\n\t\tif (IDCANCEL==MessageBox(editor->window, \"Really convert?\", editor->filename, MB_OKCANCEL))\n\t\t\treturn;\n\n\t\tlen = GetWindowTextLengthW(editor->editpane);\n\t\twfile = malloc((len+1)*2);\n\t\tafile = malloc(len+1);\n\t\tGetWindowTextW(editor->editpane, wfile, len+1);\n\n\t\tin = wfile;\n\t\tout = afile;\n\t\twhile(*in)\n\t\t{\n\t\t\t//fixme: do we care about ascii control codes? quake tends not to, but also abuses them...\n\t\t\tif (*in >= 0 && *in < 0x80)\n\t\t\t\t*out++ = *in++;\t//ascii is ascii\n\t\t\telse if (*in >= 0xe000 && *in < 0xe100)\n\t\t\t\t*out++ = *in++;\t//quake's charset is quake's charset\n\t\t\t//FIXME: no utf-16 surrogates\n\t\t\telse\n\t\t\t{\n\t\t\t\t*out++ = *in++;\n\t\t\t\terrors = true;\n\t\t\t}\n\t\t}\n\t\t*out++ = 0;\n\n\t\tif (errors)\n\t\t\terrors = IDCANCEL==MessageBox(editor->window, \"Encoding to quake's char set will corrupt some characters (and cannot be displayed correctly in this editor). continue anyway?\", editor->filename, MB_OKCANCEL);\n\n\t\tif (!errors)\n\t\t{\n\t\t\teditor->savefmt = UTF_ANSI;\n\t\t\tif (editor->scintilla)\n\t\t\t{\n\t\t\t\tSendMessage(editor->editpane, SCI_SETCODEPAGE, 28591, 0);\n\t\t\t\tSendMessage(editor->editpane, SCI_SETTEXT, 0, (LPARAM)afile);\n\t\t\t}\n\t\t\telse\n\t\t\t\tSetWindowTextA(editor->editpane, afile);\n\t\t}\n\t\t\n\t\tfree(wfile);\n\t\tfree(afile);\n\t}\n}\n\nvoid EditorMenu(editor_t *editor, WPARAM wParam)\n{\n\tswitch(LOWORD(wParam))\n\t{\n\tcase IDM_OPENDOCU:\n\t\t{\n\t\t\tchar buffer[1024];\n\t\t\tif (!ReadTextSelection(editor, buffer, sizeof(buffer)))\n\t\t\t{\n\t\t\t\tMessageBox(NULL, \"There is no name currently selected.\", \"Whoops\", 0);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\telse\n\t\t\t\tEditFile(buffer, -1, false);\n\t\t}\n\t\tbreak;\n\tcase IDM_SAVE:\n\t\tEditorSave(editor);\n\t\tbreak;\n\tcase IDM_FIND:\n\t\tSetFocus(search_name);\n\t\tbreak;\n\tcase IDM_FINDNEXT:\n\tcase IDM_FINDPREV:\n\t\t{\n\t\t\tchar buffer[128];\n\t\t\tGetWindowText(search_name, buffer, sizeof(buffer));\n\t\t\tif (*buffer != 0)\n\t\t\t{\n\t\t\t\tHWND ew = (HWND)SendMessage(mdibox, WM_MDIGETACTIVE, 0, 0);\n\t\t\t\teditor_t *editor;\n\t\t\t\tfor (editor = editors; editor; editor = editor->next)\n\t\t\t\t{\n\t\t\t\t\tif (editor->window == ew)\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tif (editor && editor->scintilla)\n\t\t\t\t{\n\t\t\t\t\tFindNextScintilla(editor, buffer, LOWORD(wParam) == IDM_FINDNEXT);\n\t\t\t\t\tSetFocus(editor->window);\n\t\t\t\t\tSetFocus(editor->editpane);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tbreak;\n\tcase IDM_GREP:\n\t\t{\n\t\t\tchar buffer[1024];\n\t\t\tif (!ReadTextSelection(editor, buffer, sizeof(buffer)))\n\t\t\t{\n\t\t\t\tMessageBox(NULL, \"There is no search text specified.\", \"Whoops\", 0);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\telse\n\t\t\t\tGrepAllFiles(buffer);\n\t\t}\n\t\tbreak;\n\tcase IDM_RETURNDEF:\n\t\tif (navhistory_pos > navhistory_first)\n\t\t{\n\t\t\teditor_t *ed;\n\t\t\tnavhistory_pos--;\n\t\t\t//search for the editor to make sure its still open\n\t\t\tfor (ed = editors; ed; ed = ed->next)\n\t\t\t{\n\t\t\t\tif (ed == navhistory[navhistory_pos&navhistory_size].editor)\n\t\t\t\t{\n\t\t\t\t\tSetFocus(ed->window);\n\t\t\t\t\tSetFocus(ed->editpane);\n\t\t\t\t\tSendMessage(ed->editpane, SCI_SETSEL, navhistory[navhistory_pos&navhistory_size].selpos, navhistory[navhistory_pos&navhistory_size].anchorpos);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tbreak;\n\tcase IDM_GOTODEF:\n\t\t{\n\t\t\tchar buffer[1024];\n\n\t\t\t{\n\t\t\t\tnavhistory[navhistory_pos&navhistory_size].editor = editor;\n\t\t\t\tnavhistory[navhistory_pos&navhistory_size].selpos = SendMessage(editor->editpane, SCI_GETANCHOR, 0, 0);\n\t\t\t\tnavhistory[navhistory_pos&navhistory_size].anchorpos = SendMessage(editor->editpane, SCI_GETCURRENTPOS, 0, 0);\n\t\t\t\tnavhistory_pos++;\n\t\t\t\tif (navhistory_pos > navhistory_first + navhistory_size)\n\t\t\t\t\tnavhistory_first = navhistory_pos - navhistory_size;\n\t\t\t}\n\n\t\t\tif (!ReadTextSelection(editor, buffer, sizeof(buffer)))\n\t\t\t{\n\t\t\t\tMessageBox(NULL, \"There is no name currently selected.\", \"Whoops\", 0);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\telse\n\t\t\t\tGoToDefinition(buffer);\n\t\t}\n\t\tbreak;\n\n\tcase IDM_UNDO:\n\t\tif (editor->scintilla)\n\t\t\tSendMessage(editor->editpane, SCI_UNDO, 0, 0);\n\t\telse\n\t\t\tEdit_Undo(editor->editpane);\n\t\tbreak;\n\tcase IDM_REDO:\n\t\tif (editor->scintilla)\n\t\t\tSendMessage(editor->editpane, SCI_REDO, 0, 0);\n\t\telse\n\t\t\tEdit_Redo(editor->editpane);\n\t\tbreak;\n\n\tcase IDM_CUT:\n\t\tif (editor->scintilla)\n\t\t\tSendMessage(editor->editpane, SCI_CUT, 0, 0);\n\t\tbreak;\n\tcase IDM_COPY:\n\t\tif (editor->scintilla)\n\t\t\tSendMessage(editor->editpane, SCI_COPY, 0, 0);\n\t\tbreak;\n\tcase IDM_PASTE:\n\t\tif (editor->scintilla)\n\t\t\tSendMessage(editor->editpane, SCI_PASTE, 0, 0);\n\t\tbreak;\n\n\tcase IDM_DEBUG_TOGGLEBREAK:\n\t\t{\n\t\t\tint mode;\n\t\t\tif (editor->scintilla)\n\t\t\t{\n\t\t\t\tmode = !(SendMessage(editor->editpane, SCI_MARKERGET, editor->curline, 0) & 1);\n\t\t\t\tSendMessage(editor->editpane, mode?SCI_MARKERADD:SCI_MARKERDELETE, editor->curline, 0);\n\t\t\t}\n\t\t\telse\n\t\t\t\tmode = 2;\n\n\t\t\tEngineCommandf(\"qcbreakpoint %i \\\"%s\\\" %i\\n\", mode, editor->filename, editor->curline+1);\n\t\t}\n\t\treturn;\n\tcase IDM_DEBUG_SETNEXT:\n\t\tEngineCommandf(\"qcjump \\\"%s\\\" %i\\n\", editor->filename, editor->curline+1);\n\t\treturn;\n\n\tcase IDM_ENCODING_PRIVATEUSE:\n\t\tGUI_Recode(editor, UTF8_BOM);\n\t\tbreak;\n\tcase IDM_ENCODING_DEPRIVATEUSE:\n\t\tGUI_Recode(editor, UTF_ANSI);\n\t\tbreak;\n\n\tcase IDM_ENCODING_UNIX:\n\t\tSendMessage(editor->editpane, SCI_CONVERTEOLS, SC_EOL_LF, 0);\n\t\tSendMessage(editor->editpane, SCI_SETVIEWEOL, false, 0);\n\t\tbreak;\n\tcase IDM_ENCODING_WINDOWS:\n\t\tSendMessage(editor->editpane, SCI_CONVERTEOLS, SC_EOL_CRLF, 0);\n\t\tSendMessage(editor->editpane, SCI_SETVIEWEOL, false, 0);\n\t\tbreak;\n\n\tdefault:\n\t\tGenericMenu(wParam);\n\t\tbreak;\n\t}\n}\n\neditor_t *tooltip_editor = NULL;\nchar tooltip_variable[256];\nchar tooltip_type[256];\nchar tooltip_comment[2048];\nsize_t tooltip_position;\n\nchar *GetTooltipText(editor_t *editor, int pos, pbool dwell)\n{\n\tstatic char buffer[1024];\n\tchar wordbuf[256], *text;\n\tchar term[256];\n\tchar *defname;\n\tdefname = WordUnderCursor(editor, wordbuf, sizeof(wordbuf), term, sizeof(term), pos);\n\tif (!*defname)\n\t\treturn NULL;\n\telse if (globalstable.numbuckets)\n\t{\n\t\tQCC_def_t *def;\n\t\tint fno;\n\t\tint line;\n\t\tint best, bestline;\n\t\tchar *macro = QCC_PR_CheckCompConstTooltip(defname, buffer, buffer + sizeof(buffer));\n\t\tif (macro && *macro)\n\t\t\treturn macro;\n\n\t\tif (dwell)\n\t\t{\n\t\t\ttooltip_editor = NULL;\n\t\t\t*tooltip_variable = 0;\n\t\t\ttooltip_position = 0;\n\t\t\t*tooltip_type = 0;\n\t\t\t*tooltip_comment = 0;\n\t\t}\n\n\t\tline = SendMessage(editor->editpane, SCI_LINEFROMPOSITION, pos, 0);\n\n\t\tfor (best = 0,bestline=0, fno = 1; fno < numfunctions; fno++)\n\t\t{\n\t\t\tif (line > functions[fno].line && bestline < functions[fno].line)\n\t\t\t{\n\t\t\t\tif (!strcmp(editor->filename, functions[fno].filen))\n\t\t\t\t{\n\t\t\t\t\tbest = fno;\n\t\t\t\t\tbestline = functions[fno].line;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (best)\n\t\t{\n\t\t\tif (strstr(functions[best].name, \"::\"))\n\t\t\t{\n\t\t\t\tQCC_type_t *type;\n\t\t\t\tchar tmp[256];\n\t\t\t\tchar *c;\n\t\t\t\tQC_strlcpy(tmp, functions[best].name, sizeof(tmp));\n\t\t\t\tc = strstr(tmp, \"::\");\n\t\t\t\tif (c)\n\t\t\t\t\t*c = 0;\n\t\t\t\ttype = QCC_TypeForName(tmp);\n\n\t\t\t\tif (type->type == ev_entity)\n\t\t\t\t{\n\t\t\t\t\tQCC_def_t *def;\n\t\t\t\t\tQC_snprintfz(tmp, sizeof(tmp), \"%s::__m%s\", type->name, term);\n\n\t\t\t\t\tfor (fno = 0, def = NULL; fno < sourcefilesnumdefs && !def; fno++)\n\t\t\t\t\t{\n\t\t\t\t\t\tfor (def = sourcefilesdefs[fno]; def; def = def->next)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (def->scope && def->scope != &functions[best])\n\t\t\t\t\t\t\t\tcontinue;\n//\t\t\t\t\t\t\tOutputDebugString(def->name);\n//\t\t\t\t\t\t\tOutputDebugString(\"\\n\");\n\t\t\t\t\t\t\tif (!strcmp(def->name, tmp))\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t//FIXME: look at the scope's function to find the start+end of the function and filter based upon that, to show locals\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif (def && def->type->type == ev_field)\n\t\t\t\t\t{\n//\t\t\t\t\t\tQC_strlcpy(tmp, term, sizeof(tmp));\n\t\t\t\t\t\tQC_snprintfz(term, sizeof(term), \"self.%s\", tmp);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tfor (fno = 0, def = NULL; fno < sourcefilesnumdefs && !def; fno++)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tfor (def = sourcefilesdefs[fno]; def; def = def->next)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tif (def->scope && def->scope != &functions[best])\n\t\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t\tif (!strcmp(def->name, term))\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t//FIXME: look at the scope's function to find the start+end of the function and filter based upon that, to show locals\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (def && def->type->type == ev_field)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tQC_strlcpy(tmp, term, sizeof(tmp));\n\t\t\t\t\t\t\tQC_snprintfz(term, sizeof(term), \"self.%s\", tmp);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t//FIXME: we may need to display types too\n\t\tfor (fno = 0, def = NULL; fno < sourcefilesnumdefs && !def; fno++)\n\t\t{\n\t\t\tfor (def = sourcefilesdefs[fno]; def; def = def->next)\n\t\t\t{\n\t\t\t\tif (def->scope)\n\t\t\t\t\tcontinue;\n\t\t\t\tif (!strcmp(def->name, defname))\n\t\t\t\t{\n\t\t\t\t\t//FIXME: look at the scope's function to find the start+end of the function and filter based upon that, to show locals\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (def)\n\t\t{\n\t\t\tchar typebuf[1024];\n\t\t\tchar valuebuf[1024];\n\t\t\tchar *value = \"\";\n\t\t\tif (def->constant && def->type->type == ev_float)\n\t\t\t\tQC_snprintfz(value=valuebuf, sizeof(valuebuf), \" = %g\", def->symboldata[def->ofs]._float);\n\t\t\telse if (def->constant && def->type->type == ev_integer)\n\t\t\t\tQC_snprintfz(value=valuebuf, sizeof(valuebuf), \" = %i\", def->symboldata[def->ofs]._int);\n\t\t\telse if (def->constant && def->type->type == ev_vector)\n\t\t\t\tQC_snprintfz(value=valuebuf, sizeof(valuebuf), \" = '%g %g %g'\", def->symboldata[def->ofs].vector[0], def->symboldata[def->ofs].vector[1], def->symboldata[def->ofs].vector[2]);\n\t\t\t//note function argument names do not persist beyond the function def. we might be able to read the function's localdefs for them, but that's unreliable/broken with builtins where they're most needed.\n\t\t\tif (def->comment)\n\t\t\t\tQC_snprintfz(buffer, sizeof(buffer)-1, \"%s %s%s\\r\\n%s\", TypeName(def->type, typebuf, sizeof(typebuf)), def->name, value, def->comment);\n\t\t\telse\n\t\t\t\tQC_snprintfz(buffer, sizeof(buffer)-1, \"%s %s%s\", TypeName(def->type, typebuf, sizeof(typebuf)), def->name, value);\n\n\t\t\tif (dwell)\n\t\t\t{\n\t\t\t\tstrncpy(tooltip_type, TypeName(def->type, typebuf, sizeof(typebuf)), sizeof(tooltip_type)-1);\n\t\t\t\tif (def->comment)\n\t\t\t\t\tstrncpy(tooltip_comment, def->comment, sizeof(tooltip_comment)-1);\n\t\t\t}\n\n\t\t\ttext = buffer;\n\t\t}\n\t\telse\n\t\t\ttext = NULL;\n\n\t\tif (dwell)\n\t\t{\n\t\t\tstrncpy(tooltip_variable, term, sizeof(tooltip_variable));\n\t\t\ttooltip_variable[sizeof(tooltip_variable)-1] = 0;\n\t\t\ttooltip_position = pos;\n\t\t\ttooltip_editor = editor;\n\n\t\t\tEngineCommandf(\"qcinspect \\\"%s\\\" \\\"%s\\\"\\n\", term, (def && def->scope)?def->scope->name:\"\");\n\n\t\t\tif (text)\n\t\t\t\tSendMessage(editor->editpane, SCI_CALLTIPSHOW, (WPARAM)pos, (LPARAM)text);\n\t\t}\n\n\t\treturn text;\n\t}\n\telse\n\t\treturn NULL;//\"Type info not available. Compile first.\";\n}\n\n//scans the preceeding line(s) to find the ideal indentation for the highlighted line\n//indentbuf may contain spaces or tabs. preferably tabs.\nstatic void scin_get_line_indent(HWND editpane, int lineidx, char *indentbuf, size_t sizeofbuf)\n{\n\tsize_t i, len;\n\twhile (lineidx --> 0)\n\t{\n\t\tlen = SendMessage(editpane, SCI_LINELENGTH, lineidx, 0);\n\t\t*indentbuf = 0;\n\t\tif (len+2 < sizeofbuf)\n\t\t{\n\t\t\t//FIXME: ignore whitespace\n\t\t\tlen = SendMessage(editpane, SCI_GETLINE, lineidx, (LPARAM)indentbuf);\n\t\t\tfor (i = 0; i < len; i++)\n\t\t\t{\n\t\t\t\tif (indentbuf[i] == ' ' || indentbuf[i] == '\\t')\n\t\t\t\t\tcontinue;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (i == len)\n\t\t\t\tcontinue;\n\n\t\t\tif (len >= 3 && indentbuf[len-3] == '{')\n\t\t\t\tindentbuf[i++] = '\\t';\t//add an indent\n\t\t\tindentbuf[i] = 0;\n\t\t\treturn;\n\t\t}\n\t}\n\t*indentbuf = 0;\t//failed\n}\n\nvoid Scin_HandleCharAdded(editor_t *editor, struct SCNotification *not, int pos)\n{\n\tif (not->ch == '(')\n\t{\n\t\tchar *s = GetTooltipText(editor, pos-1, FALSE);\n\t\ttooltip_editor = NULL;\n\t\tif (s)\n\t\t\tSendMessage(editor->editpane, SCI_CALLTIPSHOW, (WPARAM)pos, (LPARAM)s);\n\t}\n\telse if (not->ch == '}')\n\t{\t//if the first char on the line, fix up indents to match previous-1\n\t\tchar prevline[65536];\n\t\tchar newline[4096];\n\t\tint pos = SendMessage(editor->editpane, SCI_GETCURRENTPOS, 0, 0);\n\t\tint lineidx = SendMessage(editor->editpane, SCI_LINEFROMPOSITION, pos, 0);\n\t\tint linestart = SendMessage(editor->editpane, SCI_POSITIONFROMLINE, lineidx, 0);\n\t\tint plen;\n\t\tint nlen = SendMessage(editor->editpane, SCI_LINELENGTH, lineidx, 0);\n\t\tif (nlen >= sizeof(newline))\n\t\t\treturn;\n\t\tnlen = SendMessage(editor->editpane, SCI_GETLINE, lineidx, (LPARAM)newline);\n\t\tif (linestart > 2)\n\t\t{\n\t\t\tscin_get_line_indent(editor->editpane, lineidx, prevline, sizeof(prevline));\n\t\t\tplen = strlen(prevline);\n\t\t\tif (plen > nlen)\n\t\t\t\treturn;\t//already indented a bit or something\n\t\t\tif (!strncmp(prevline, newline, plen))\t//same indent\n\t\t\t{\n\t\t\t\tSendMessage(editor->editpane, SCI_CHARLEFT, 0, 0);\t//move to the indent\n\t\t\t\tSendMessage(editor->editpane, SCI_BACKTAB, 0, 0);\t//do shift-tab to un-indent the current selection (one line supposedly)\n\t\t\t\tSendMessage(editor->editpane, SCI_CHARRIGHT, 0, 0);\t//and move back to the right of the }\n\t\t\t}\n\t\t}\n\t}\n\telse if (not->ch == '\\r' || not->ch == '\\n')\n\t{\n\t\tchar linebuf[65536];\n\t\tint pos = SendMessage(editor->editpane, SCI_GETCURRENTPOS, 0, 0);\n\t\tint lineidx = SendMessage(editor->editpane, SCI_LINEFROMPOSITION, pos, 0);\n\t\tint linestart = SendMessage(editor->editpane, SCI_POSITIONFROMLINE, lineidx, 0);\n\t\t//int len = SendMessage(editor->editpane, SCI_LINELENGTH, lineidx, 0);\n\t\tif (pos == linestart)\n\t\t{\n\t\t\tscin_get_line_indent(editor->editpane, lineidx, linebuf, sizeof(linebuf));\n\t\t\tSendMessage(editor->editpane, SCI_REPLACESEL, 0, (LPARAM)linebuf);\n\t\t}\n\t}\n/*\n\telse if (0)//(!SendMessage(editor->editpane, SCI_AUTOCACTIVE, 0, 0))\n\t{\n\t\tchar buffer[65536];\n\t\tchar prefixbuffer[128];\n\t\tchar *pre = WordUnderCursor(editor, prefixbuffer, sizeof(prefixbuffer), NULL, 0, SendMessage(editor->editpane, SCI_GETCURRENTPOS, 0, 0));\n\t\tif (pre && *pre)\n\t\t\tif (GenAutoCompleteList(pre, buffer, sizeof(buffer)))\n\t\t\t{\n\t\t\t\tSendMessage(editor->editpane, SCI_AUTOCSETFILLUPS, 0, (LPARAM)\"\\t\\n\");\n\t\t\t\tSendMessage(editor->editpane, SCI_AUTOCSHOW, strlen(pre), (LPARAM)buffer);\n\t\t\t}\n\t}\n*/\n}\n\nstatic void UpdateEditorTitle(editor_t *editor)\n{\n\tchar title[2048];\n\tchar *encoding = \"unknown\";\n\tif (editor->oldsavefmt == editor->savefmt && editor->oldline == editor->curline)\n\t\treturn;\t//nothing changed.\n\teditor->oldsavefmt = editor->savefmt;\n\teditor->oldline = editor->curline;\n\n\tswitch(editor->savefmt)\n\t{\n\tcase UTF8_RAW:\n\t\tencoding = \"utf-8\";\n\t\tbreak;\n\tcase UTF8_BOM:\n\t\tencoding = \"utf-8(bom)\";\n\t\tbreak;\n\tcase UTF_ANSI:\n\t\tencoding = \"unspecified\";\n\t\tbreak;\n\tcase UTF16LE:\n\t\tencoding = \"utf-16(le)\";\n\t\tbreak;\n\tcase UTF16BE:\n\t\tencoding = \"utf-16(be)\";\n\t\tbreak;\n\tcase UTF32LE:\n\t\tencoding = \"utf-32(le)\";\n\t\tbreak;\n\tcase UTF32BE:\n\t\tencoding = \"utf-32(be)\";\n\t\tbreak;\n\tdefault:\n\t\tencoding = \"unknown\";\n\t\tbreak;\n\t}\n\tif (QCC_FindVFile(editor->filename))\n\t\tsprintf(title, \"%s:%i - Virtual\", editor->filename, 1+editor->curline);\n\telse if (editor->modified)\n\t\tsprintf(title, \"*%s:%i - %s\", editor->filename, 1+editor->curline, encoding);\n\telse\n\t\tsprintf(title, \"%s:%i - %s\", editor->filename, 1+editor->curline, encoding);\n\tSetWindowText(editor->window, title);\n}\n\nstatic LRESULT CALLBACK EditorWndProc(HWND hWnd,UINT message,\n\t\t\t\t     WPARAM wParam,LPARAM lParam)\n{\n\tRECT rect;\n\tPAINTSTRUCT ps;\n\n\teditor_t *editor;\n\tfor (editor = editors; editor; editor = editor->next)\n\t{\n\t\tif (editor->window == hWnd)\n\t\t\tbreak;\n\t\tif (editor->window == NULL)\n\t\t\tbreak;\t//we're actually creating it now.\n\t}\n\tif (!editor)\n\t\tgoto gdefault;\n\n\tswitch (message)\n\t{\n\tcase WM_CLOSE:\n\tcase WM_QUIT:\n\t\tif (editor->modified)\n\t\t{\n\t\t\tswitch (MessageBox(hWnd, \"Would you like to save?\", editor->filename, MB_YESNOCANCEL))\n\t\t\t{\n\t\t\tcase IDCANCEL:\n\t\t\t\treturn false;\n\t\t\tcase IDYES:\n\t\t\t\tif (!EditorSave(editor))\n\t\t\t\t\treturn false;\n\t\t\tcase IDNO:\n\t\t\tdefault:\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tgoto gdefault;\n\tcase WM_DESTROY:\n\t\t{\n\t\t\teditor_t *e;\n\t\t\tif (editor == editors)\n\t\t\t{\n\t\t\t\teditors = editor->next;\n\t\t\t\tfree(editor);\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t\tfor (e = editors; e; e = e->next)\n\t\t\t{\n\t\t\t\tif (e->next == editor)\n\t\t\t\t{\n\t\t\t\t\te->next = editor->next;\n\t\t\t\t\tfree(editor);\n\t\t\t\t\treturn 0;\n\t\t\t\t}\n\t\t\t}\n\t\t\tMessageBox(0, \"Couldn't destroy file reference\", \"WARNING\", 0);\n\t\t}\n\t\tgoto gdefault;\n\tcase WM_CREATE:\n\t\teditor->editpane = CreateAnEditControl(hWnd, &editor->scintilla);\n\t\tif (richedit)\n\t\t{\n\t\t\tSendMessage(editor->editpane, EM_EXLIMITTEXT, 0, 1<<31);\n\t\t\tSendMessage(editor->editpane, EM_SETUNDOLIMIT, 256, 256);\n\t\t}\n\n\t\teditor->tooltip = CreateWindowEx(0, TOOLTIPS_CLASS, NULL, WS_POPUP|TTS_ALWAYSTIP|TTS_NOPREFIX, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, hWnd, NULL, ghInstance, NULL);\n\t\tif (editor->tooltip)\n\t\t{\n\t\t\tTOOLINFO toolInfo = { 0 };\n\t\t\ttoolInfo.cbSize = sizeof(toolInfo);\n\t\t\ttoolInfo.hwnd = hWnd;\n\t\t\ttoolInfo.uFlags = TTF_IDISHWND | TTF_SUBCLASS | TTF_TRACK | TTF_ABSOLUTE;\n\t\t\ttoolInfo.uId = (UINT_PTR)editor->editpane;\n\t\t\ttoolInfo.lpszText = \"\";\n\t\t\tSendMessage(editor->tooltip, TTM_ADDTOOL, 0, (LPARAM)&toolInfo);\n\t\t\tSendMessage(editor->tooltip, TTM_SETMAXTIPWIDTH, 0, 500);\n\t\t}\n\t\tgoto gdefault;\n\tcase WM_SETFOCUS:\n\t\tSetFocus(editor->editpane);\n\t\tgoto gdefault;\n\tcase WM_SIZE:\n\t\tGetClientRect(hWnd, &rect);\n\t\tSetWindowPos(editor->editpane, NULL, 0, 0, rect.right-rect.left, rect.bottom-rect.top, 0);\n\t\tgoto gdefault;\n\tcase WM_ERASEBKGND:\n\t\treturn TRUE;\n\tcase WM_PAINT:\n\t\tBeginPaint(hWnd,(LPPAINTSTRUCT)&ps);\n\n\t\tEndPaint(hWnd,(LPPAINTSTRUCT)&ps);\n\t\treturn TRUE;\n\t\tbreak;\n\tcase WM_SETCURSOR:\n\t\tif (!editor->scintilla)\n\t\t{\n\t\t\tPOINT pos;\n\t\t\tchar *newtext;\n\t\t\tTOOLINFO toolInfo = { 0 };\n\t\t\ttoolInfo.cbSize = sizeof(toolInfo);\n\t\t\ttoolInfo.hwnd = hWnd;\n\t\t\ttoolInfo.uFlags = TTF_IDISHWND | TTF_SUBCLASS | TTF_TRACK | TTF_ABSOLUTE;\n\t\t\ttoolInfo.uId = (UINT_PTR)editor->editpane;\n\t\t\tnewtext = GetTooltipText(editor, -1, FALSE);\n\t\t\ttoolInfo.lpszText = editor->tooltiptext;\n\t\t\tif (!newtext)\n\t\t\t\tnewtext = \"\";\n\t\t\tif (strcmp(editor->tooltiptext, newtext))\n\t\t\t{\n\t\t\t\tstrncpy(editor->tooltiptext, newtext, sizeof(editor->tooltiptext)-1);\n\t\t\t\tSendMessage(editor->tooltip, TTM_UPDATETIPTEXT, (WPARAM)0, (LPARAM)&toolInfo);\n\t\t\t\tif (*editor->tooltiptext)\n\t\t\t\t\tSendMessage(editor->tooltip, TTM_TRACKACTIVATE, (WPARAM)TRUE, (LPARAM)&toolInfo);\n\t\t\t\telse\n\t\t\t\t\tSendMessage(editor->tooltip, TTM_TRACKACTIVATE, (WPARAM)FALSE, (LPARAM)&toolInfo);\n\t\t\t}\n\n\t\t\tGetCursorPos(&pos);\n\t\t\tif (pos.x >= 60)\n\t\t\t\tpos.x -= 60;\n\t\t\telse\n\t\t\t\tpos.x = 0;\n\t\t\tpos.y += 30;\n\t\t\tSendMessage(editor->tooltip, TTM_TRACKPOSITION, (WPARAM)0, MAKELONG(pos.x, pos.y));\n\t\t}\n\t\tgoto gdefault;\n\tcase WM_COMMAND:\n\t\tif (HIWORD(wParam) == EN_CHANGE && (HWND)lParam == editor->editpane)\n\t\t{\n\t\t\tif (!editor->modified && !editor->scintilla)\n\t\t\t{\n\t\t\t\tCHARRANGE chrg;\n\n\t\t\t\tif (!editor->modified)\n\t\t\t\t\teditor->oldline=~0;\n\t\t\t\teditor->modified = true;\n\t\t\t\tif (EditorModified(editor))\n\t\t\t\t\tif (MessageBox(NULL, \"warning: file was modified externally. reload?\", \"Modified!\", MB_YESNO) == IDYES)\n\t\t\t\t\t\tEditorReload(editor);\n\n\n\t\t\t\tSendMessage(editor->editpane, EM_EXGETSEL, 0, (LPARAM) &chrg);\n\t\t\t\teditor->curline = Edit_LineFromChar(editor->editpane, chrg.cpMin);\n\t\t\t\tUpdateEditorTitle(editor);\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n//\t\t\tif (mdibox)\n//\t\t\t\tgoto gdefault;\n\t\t\tEditorMenu(editor, wParam);\n\t\t}\n\t\tbreak;\n\tcase WM_CONTEXTMENU:\n\t\t{\n\t\t\tchar buffer[1024];\n\t\t\tint x = GET_X_LPARAM(lParam), y = GET_Y_LPARAM(lParam);\n\t\t\tHMENU menu = CreatePopupMenu();\n\t\t\tif (x == -1 && y == -1)\n\t\t\t{\n\t\t\t\tPOINT p;\n\t\t\t\tGetCursorPos(&p);\t//not the best. but too lazy to work out scintilla/richedit.\n\t\t\t\tx = p.x;\n\t\t\t\ty = p.y;\n\t\t\t}\n\n\t\t\tif (ReadTextSelection(editor, buffer, sizeof(buffer)))\n\t\t\t{\n\t\t\t\tchar tmp[1024];\n\t\t\t\tQC_snprintfz(tmp, sizeof(tmp), \"Go to definition: %s\", buffer);\n\t\t\t\tAppendMenuA(menu, MF_ENABLED,\n\t\t\t\t\tIDM_GOTODEF, tmp);\n\n\t\t\t\tQC_snprintfz(tmp, sizeof(tmp), \"Grep for %s\", buffer);\n\t\t\t\tAppendMenuA(menu, MF_ENABLED,\n\t\t\t\t\tIDM_GREP, tmp);\n\n\t\t\t\tAppendMenuA(menu, MF_SEPARATOR, 0, NULL);\n\t\t\t}\n\n\t\t\tAppendMenuA(menu, MF_ENABLED,\n\t\t\t\tIDM_DEBUG_TOGGLEBREAK,\t\t\"Toggle Breakpoint\");\n\n\t\t\tif (gamewindow)\n\t\t\t{\n\t\t\t\tAppendMenuA(menu, MF_ENABLED, IDM_DEBUG_SETNEXT, \"Set next statement\");\n\t\t\t\tAppendMenuA(menu, MF_ENABLED, IDM_DEBUG_RUN, \"Resume\");\n\t\t\t}\n\t\t\telse\n\t\t\t\tAppendMenuA(menu, MF_ENABLED, IDM_DEBUG_RUN, \"Begin Debugging\");\n\n\t\t\tAppendMenuA(menu, MF_SEPARATOR, 0, NULL);\n\n\t\t\tAppendMenuA(menu, editor->modified?MF_ENABLED:(MF_DISABLED|MF_GRAYED),\n\t\t\t\tIDM_SAVE,\t\t\"Save File\");\n\t\t//\tAppendMenuA(menu, MF_ENABLED, IDM_FIND,\t\t\"&Find\");\n\t\t\tAppendMenuA(menu, (editor->scintilla&&!SendMessage(editor->editpane, SCI_CANUNDO,0,0))?(MF_DISABLED|MF_GRAYED):MF_ENABLED,\n\t\t\t\tIDM_UNDO,\t\t\"Undo\");\n\t\t\tAppendMenuA(menu, (editor->scintilla&&!SendMessage(editor->editpane, SCI_CANREDO,0,0))?(MF_DISABLED|MF_GRAYED):MF_ENABLED,\n\t\t\t\tIDM_REDO,\t\t\"Redo\");\n\t\t\tAppendMenuA(menu, MF_ENABLED,\n\t\t\t\tIDM_CUT,\t\t\"Cut\");\n\t\t\tAppendMenuA(menu, MF_ENABLED,\n\t\t\t\tIDM_COPY,\t\t\"Copy\");\n\t\t\tAppendMenuA(menu, MF_ENABLED,\n\t\t\t\tIDM_PASTE,\t\t\"Paste\");\n\t\t\t\n\t\t\tTrackPopupMenu(menu, TPM_LEFTBUTTON|TPM_RIGHTBUTTON, x, y, 0, hWnd, NULL);\n\t\t\tDestroyMenu(menu);\n\t\t}\n\t\tbreak;\n\tcase WM_NOTIFY:\n\t\t{\n\t\t\tNMHDR *nmhdr;\n\t\t\tnmhdr = (NMHDR *)lParam;\n\t\t\tif (editor->scintilla)\n\t\t\t{\n\t\t\t\tstruct SCNotification *not = (struct SCNotification*)nmhdr;\n\t\t\t\tint pos = SendMessage(editor->editpane, SCI_GETCURRENTPOS, 0, 0);\n\t\t\t\tint l = SendMessage(editor->editpane, SCI_LINEFROMPOSITION, pos, 0);\n\t\t\t\tint mode;\n\t\t\t\tif (editor->curline != l)\n\t\t\t\t\teditor->curline = l;\n\t\t\t\tswitch(nmhdr->code)\n\t\t\t\t{\n\t\t\t\tcase SCN_MARGINCLICK:\n\t\t\t\t\tl = SendMessage(editor->editpane, SCI_LINEFROMPOSITION, not->position, 0);\n\t\t\t\t\tif (not->margin == 1)\n\t\t\t\t\t{\n\t\t\t\t\t\t/*fixme: should we scan the statements to ensure the line is valid? this applies to the f9 key too*/\n\t\t\t\t\t\tmode = !(SendMessage(editor->editpane, SCI_MARKERGET, l, 0) & 1);\n\t\t\t\t\t\tSendMessage(editor->editpane, mode?SCI_MARKERADD:SCI_MARKERDELETE, l, 0);\n\t\t\t\t\t\tEngineCommandf(\"qcbreakpoint %i \\\"%s\\\" %i\\n\", mode, editor->filename, l+1);\n\t\t\t\t\t}\n\t\t\t\t\telse if (not->margin == 2)\n\t\t\t\t\t{\n\t\t\t\t\t\tSendMessage(editor->editpane, SCI_TOGGLEFOLD, l, 0);\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase SCN_CHARADDED:\n\t\t\t\t\tScin_HandleCharAdded(editor, not, pos);\n\t\t\t\t\tbreak;\n\t\t\t\tcase SCN_SAVEPOINTREACHED:\n\t\t\t\t\teditor->oldline=~0;\n\t\t\t\t\teditor->modified = false;\n\t\t\t\t\tbreak;\n\t\t\t\tcase SCN_SAVEPOINTLEFT:\n\t\t\t\t\teditor->oldline=~0;\n\t\t\t\t\teditor->modified = true;\n\n\t\t\t\t\tif (EditorModified(editor))\n\t\t\t\t\t\tif (MessageBox(NULL, \"warning: file was modified externally. reload?\", \"Modified!\", MB_YESNO) == IDYES)\n\t\t\t\t\t\t\tEditorReload(editor);\n\t\t\t\t\tbreak;\n\t\t\t\tcase SCN_UPDATEUI:\n\t\t\t\t\t{\n\t\t\t\t\t\tint pos1, pos2;\n\t\t\t\t\t\tif (strchr(\"{}[]()\", SendMessage(editor->editpane, SCI_GETCHARAT, pos, 0)))\n\t\t\t\t\t\t\tpos1 = pos;\n\t\t\t\t\t\telse if (strchr(\"{}[]()\", SendMessage(editor->editpane, SCI_GETCHARAT, pos-1, 0)))\n\t\t\t\t\t\t\tpos1 = pos-1;\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tpos1 = -1;\n\t\t\t\t\t\tif (pos1 != -1)\n\t\t\t\t\t\t\tpos2 = SendMessage(editor->editpane, SCI_BRACEMATCH, pos1, 0);\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tpos2 = -1;\n\t\t\t\t\t\tif (pos2 == -1)\n\t\t\t\t\t\t\tSendMessage(editor->editpane, SCI_BRACEBADLIGHT, pos1, 0);\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tSendMessage(editor->editpane, SCI_BRACEHIGHLIGHT, pos1, pos2);\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase SCN_DWELLSTART:\n\t\t\t\t\tGetTooltipText(editor, not->position, TRUE);\n\t\t\t\t\tbreak;\n\t\t\t\tcase SCN_DWELLEND:\n\t\t\t\tcase SCN_FOCUSOUT:\n\t\t\t\t\ttooltip_editor = NULL;\n\t\t\t\t\tSendMessage(editor->editpane, SCI_CALLTIPCANCEL, 0, 0);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tUpdateEditorTitle(editor);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tSELCHANGE *sel;\n\t\t\t\tswitch(nmhdr->code)\n\t\t\t\t{\n\t\t\t\tcase EN_SELCHANGE:\n\t\t\t\t\tsel = (SELCHANGE *)nmhdr;\n\t\t\t\t\teditor->curline = Edit_LineFromChar(editor->editpane, sel->chrg.cpMin);\n\t\t\t\t\tUpdateEditorTitle(editor);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\tdefault:\n\tgdefault:\n\t\tif (mdibox)\n\t\t\treturn DefMDIChildProc(hWnd,message,wParam,lParam);\n\t\telse\n\t\t\treturn DefWindowProc(hWnd,message,wParam,lParam);\n\t}\n\treturn 0;\n}\n\nstatic void EditorReload(editor_t *editor)\n{\n\tstruct stat sbuf;\n\tsize_t flensz;\n\tchar *rawfile;\n\tchar *file;\n\tsize_t flen;\n\tpbool dofree;\n\trawfile = QCC_ReadFile(editor->filename, NULL, NULL, &flensz);\n\tflen = flensz;\n\n\tfile = QCC_SanitizeCharSet(rawfile, &flen, &dofree, &editor->savefmt);\n\n\tstat(editor->filename, &sbuf);\n\teditor->filemodifiedtime = sbuf.st_mtime;\n\n\tif (editor->scintilla)\n\t{\n\t\tint endings = 0;\n\t\tchar *e, *stop;\n\t\tfor (e = file, stop=file+flen; e < stop; )\n\t\t{\n\t\t\tif (*e == '\\r')\n\t\t\t{\n\t\t\t\te++;\n\t\t\t\tif (*e == '\\n')\n\t\t\t\t{\n\t\t\t\t\te++;\n\t\t\t\t\tendings |= 4;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tendings |= 2;\n\t\t\t}\n\t\t\telse if (*e == '\\n')\n\t\t\t{\n\t\t\t\te++;\n\t\t\t\tendings |= 1;\n\t\t\t}\n\t\t\telse\n\t\t\t\te++;\n\t\t}\n\t\tswitch(endings)\n\t\t{\n\t\tcase 0:\t//new file with no endings, default to windows on windows.\n\t\tcase 4:\t//windows\n\t\t\tSendMessage(editor->editpane, SCI_SETEOLMODE, SC_EOL_CRLF, 0);\n\t\t\tSendMessage(editor->editpane, SCI_SETVIEWEOL, false, 0);\n\t\t\tbreak;\n\t\tcase 1:\t//unix\n\t\t\tSendMessage(editor->editpane, SCI_SETEOLMODE, SC_EOL_LF, 0);\n\t\t\tSendMessage(editor->editpane, SCI_SETVIEWEOL, false, 0);\n\t\t\tbreak;\n\t\tcase 2:\t//mac. traditionally qccs have never supported this. one of the mission packs has a \\r in the middle of some single-line comment.\n\t\t\tSendMessage(editor->editpane, SCI_SETEOLMODE, SC_EOL_CR, 0);\n\t\t\tSendMessage(editor->editpane, SCI_SETVIEWEOL, false, 0);\n\t\t\tbreak;\n\t\tdefault:\t//panic! everyone panic!\n\t\t\tSendMessage(editor->editpane, SCI_SETEOLMODE, SC_EOL_LF, 0);\n\t\t\tSendMessage(editor->editpane, SCI_SETVIEWEOL, true, 0);\n\t\t\tbreak;\n\t\t}\n\n//\t\tSendMessage(editor->editpane, SCI_SETTEXT, 0, (LPARAM)file);\n//\t\tSendMessage(editor->editpane, SCI_SETUNDOCOLLECTION, 0, 0);\n\t\tSendMessage(editor->editpane, SCI_SETTEXT, 0, (LPARAM)file);\n//\t\tSendMessage(editor->editpane, SCI_SETUNDOCOLLECTION, 1, 0);\n\t\tSendMessage(editor->editpane, EM_EMPTYUNDOBUFFER, 0, 0);\n\t\tSendMessage(editor->editpane, SCI_SETSAVEPOINT, 0, 0);\n\n\t\tif (editor->savefmt == UTF_ANSI)\n\t\t\tSendMessage(editor->editpane, SCI_SETCODEPAGE, 28591, 0);\n\t\telse\n\t\t\tSendMessage(editor->editpane, SCI_SETCODEPAGE, SC_CP_UTF8, 0);\n\t}\n\telse\n\t{\n\t\tSendMessage(editor->editpane, EM_SETEVENTMASK, 0, 0);\n\n\t\t/*clear it out*/\n\t\tEdit_SetSel(editor->editpane,0,Edit_GetTextLength(editor->editpane));\n\t\tEdit_ReplaceSel(editor->editpane,\"\");\n\n\t\tif (file)\n\t\t{\n\t\t\tpbool errors;\n\t\t\twchar_t *ch = QCC_makeutf16(file, flen, NULL, &errors);\n\t\t\tEdit_SetSel(editor->editpane,0,0);\n\t\t\tSetWindowTextW(editor->editpane, ch);\n\t\t\tif (errors)\n\t\t\t{\n//\t\t\t\tchar msg[1024];\n\t\t\t\teditor->savefmt = UTF_ANSI;\n\t\t\t\tSetWindowTextA(editor->editpane, file);\n\n//\t\t\t\tQC_snprintfz(msg, sizeof(msg), \"%s contains unicode encoding errors. File will be interpreted as ansi.\", editor->filename);\n//\t\t\t\tMessageBox(editor->editpane, msg, \"Encoding errors.\", MB_ICONWARNING);\n\t\t\t}\n\t\t\tfree(ch);\n\t\t}\n\t\tSendMessage(editor->editpane, EM_SETEVENTMASK, 0, ENM_SELCHANGE|ENM_CHANGE);\n\t}\n\n\tif (dofree)\n\t\tfree(file);\n\tfree(rawfile);\n\n\teditor->modified = false;\n\n\n\tif (editor->scintilla)\n\t{\n\t}\n\telse\n\t{\n\t\tCHARRANGE chrg;\n\t\tSendMessage(editor->editpane, EM_EXGETSEL, 0, (LPARAM) &chrg);\n\t\teditor->curline = Edit_LineFromChar(editor->editpane, chrg.cpMin);\n\t}\n\tUpdateEditorTitle(editor);\n}\n\n//line is 0-based. use -1 for no reselection\n//setcontrol is the reason we're opening it.\n//0: just load and go to the line.\n//1: show the line as the executing one\n//2: draw extra focus to it\nvoid EditFile(const char *name, int line, pbool setcontrol)\n{\n\tconst char *ext;\n\tchar title[1024];\n\teditor_t *neweditor;\n\tWNDCLASS wndclass;\n\tHMENU menu, menufile, menuhelp, menunavig;\n\n\tif (setcontrol)\n\t{\n\t\tfor (neweditor = editors; neweditor; neweditor = neweditor->next)\n\t\t{\n\t\t\tif (neweditor->scintilla)\n\t\t\t{\n\t\t\t\tSendMessage(neweditor->editpane, SCI_MARKERDELETEALL, 1, 0);\n\t\t\t\tSendMessage(neweditor->editpane, SCI_MARKERDELETEALL, 2, 0);\n\t\t\t}\n\t\t}\n\t}\n\tif (!name)\n\t\treturn;\n\n\tfor (neweditor = editors; neweditor; neweditor = neweditor->next)\n\t{\n\t\tif (neweditor->window && !strcmp(neweditor->filename, name))\n\t\t{\n\t\t\tif (line >= 0)\n\t\t\t{\n\t\t\t\tif (setcontrol)\n\t\t\t\t\tEdit_SetSel(neweditor->editpane, Edit_LineIndex(neweditor->editpane, line+1)-1, Edit_LineIndex(neweditor->editpane, line+1)-1);\n\t\t\t\telse\n\t\t\t\t\tEdit_SetSel(neweditor->editpane, Edit_LineIndex(neweditor->editpane, line), Edit_LineIndex(neweditor->editpane, line+1)-1);\n\t\t\t\tEdit_ScrollCaret(neweditor->editpane);\n\n\t\t\t\tif (setcontrol && neweditor->scintilla)\n\t\t\t\t{\n\t\t\t\t\tSendMessage(neweditor->editpane, SCI_MARKERADD, line, 1);\n\t\t\t\t\tSendMessage(neweditor->editpane, SCI_MARKERADD, line, 2);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (mdibox)\n\t\t\t\tSendMessage(mdibox, WM_MDIACTIVATE, (WPARAM)neweditor->window, 0);\n\t\t\tSetFocus(neweditor->window);\n\t\t\tSetFocus(neweditor->editpane);\n\t\t\treturn;\n\t\t}\n\t}\n\n\tif (QCC_RawFileSize(name) == -1)\n\t{\n\t\tQC_snprintfz(title, sizeof(title), \"File not found:\\n%s\\nCreate it?\", name);\n\t\tif (MessageBox(NULL, title, \"Error\", MB_ICONWARNING|MB_YESNO|MB_DEFBUTTON2) != IDYES)\n\t\t\treturn;\n\t}\n\n\text = strrchr(name, '.');\n\tif (ext)\n\t{\n\t\tif (!QC_strcasecmp(ext, \".wav\"))\n\t\t{\n\t\t\tsize_t flensz;\n\t\t\tchar *rawfile = QCC_ReadFile(name, NULL, NULL, &flensz);\n\t\t\t//fixme: thread this...\n\t\t\tBOOL (WINAPI *pPlaySound)(LPCSTR pszSound, HMODULE hmod, DWORD fdwSound);\n\t\t\tHMODULE winmm = LoadLibrary(\"winmm.dll\");\n\t\t\tpPlaySound = (void*)GetProcAddress(winmm, \"PlaySoundA\");\n\t\t\tif (pPlaySound)\n\t\t\t\tpPlaySound(rawfile, NULL, SND_MEMORY|SND_SYNC|SND_NODEFAULT);\n\t\t\tfree(rawfile);\n\t\t\treturn;\n\t\t}\n\t\telse if (!QC_strcasecmp(ext, \".ogg\") || !QC_strcasecmp(ext, \".mp3\") || !QC_strcasecmp(ext, \".opus\") ||\n\t\t\t\t!QC_strcasecmp(ext, \".bsp\") || !QC_strcasecmp(ext, \".mdl\") || !QC_strcasecmp(ext, \".md2\") || !QC_strcasecmp(ext, \".md3\") || !QC_strcasecmp(ext, \".iqm\") ||\n\t\t\t\t!QC_strcasecmp(ext, \".wad\") || !QC_strcasecmp(ext, \".lmp\") || !QC_strcasecmp(ext, \".png\") || !QC_strcasecmp(ext, \".tga\") || !QC_strcasecmp(ext, \".jpeg\") ||\n\t\t\t\t!QC_strcasecmp(ext, \".jpg\") || !QC_strcasecmp(ext, \".dds\") || !QC_strcasecmp(ext, \".ktx\") || !QC_strcasecmp(ext, \".bmp\") || !QC_strcasecmp(ext, \".pcx\") ||\n\t\t\t\t!QC_strcasecmp(ext, \".bin\") || !QC_strcasecmp(ext, \".dat\") || !QC_strcasecmp(ext, \".pak\") || !QC_strcasecmp(ext, \".pk3\") || !QC_strcasecmp(ext, \".dem\") || !QC_strcasecmp(ext, \".spr\"))\n\t\t{\n\t\t\tif (IDOK != MessageBox(NULL, \"The file extension implies that it is a binary file. Open as text anyway?\", \"FTEQCCGUI\", MB_OKCANCEL))\n\t\t\t\treturn;\n\t\t}\n\t}\n\n\tneweditor = malloc(sizeof(editor_t));\n\tif (!neweditor)\n\t{\n\t\tMessageBox(NULL, \"Low memory\", \"Error\", 0);\n\t\treturn;\n\t}\n\n\tneweditor->next = editors;\n\teditors = neweditor;\n\n\tneweditor->savefmt = UTF8_RAW;\n\tstrncpy(neweditor->filename, name, sizeof(neweditor->filename)-1);\n\n\tif (!mdibox)\n\t{\n\t\tmenu = CreateMenu();\n\t\tmenufile = CreateMenu();\n\t\tmenuhelp = CreateMenu();\n\t\tmenunavig = CreateMenu();\n\t\tAppendMenu(menu, MF_POPUP, (UINT_PTR)menufile,\t\"&File\");\n\t\tAppendMenu(menu, MF_POPUP, (UINT_PTR)menunavig,\t\"&Navigation\");\n\t\tAppendMenu(menu, MF_POPUP, (UINT_PTR)menuhelp,\t\"&Help\");\n\t\tAppendMenu(menufile, 0, IDM_OPENNEW,\t\"Open new file \");\n\t\tAppendMenu(menufile, 0, IDM_SAVE,\t\t\"&Save          \");\n\t//\tAppendMenu(menufile, 0, IDM_FIND,\t\t\"&Find\");\n\t\tAppendMenu(menufile, 0, IDM_UNDO,\t\t\"Undo          Ctrl+Z\");\n\t\tAppendMenu(menufile, 0, IDM_REDO,\t\t\"Redo          Ctrl+Y\");\n\t\tAppendMenu(menunavig, 0, IDM_GOTODEF, \"Go to definition\");\n\t\tAppendMenu(menunavig, 0, IDM_OPENDOCU, \"Open selected file\");\n\t\tAppendMenu(menuhelp, 0, IDM_ABOUT, \"About\");\n\t}\n\telse\n\t\tmenu = NULL;\n\n\n\t\n\twndclass.style      = 0;\n\twndclass.lpfnWndProc   = EditorWndProc;\n\twndclass.cbClsExtra    = 0;\n\twndclass.cbWndExtra    = 0;\n\twndclass.hInstance     = ghInstance;\n\twndclass.hIcon         = LoadIcon(ghInstance, IDI_ICON_FTEQCC);\n\twndclass.hCursor       = LoadCursor (NULL,IDC_ARROW);\n\twndclass.hbrBackground = (void *)COLOR_WINDOW;\n\twndclass.lpszMenuName  = 0;\n\twndclass.lpszClassName = EDIT_WINDOW_CLASS_NAME;\n\tRegisterClass(&wndclass);\n\n\tneweditor->window = NULL;\n\tif (mdibox)\n\t{\n\t\tMDICREATESTRUCT mcs;\n\n\t\tsprintf(title, \"%s\", name);\n\n\t\tmcs.szClass = EDIT_WINDOW_CLASS_NAME;\n\t\tmcs.szTitle = name;\n\t\tmcs.hOwner = ghInstance;\n\t\tmcs.x = mcs.cx = CW_USEDEFAULT;\n\t\tmcs.y = mcs.cy = CW_USEDEFAULT;\n\t\tmcs.style = WS_OVERLAPPEDWINDOW|WS_MAXIMIZE;\n\t\tmcs.lParam = 0;\n\n\t\tneweditor->window = (HWND) SendMessage (mdibox, WM_MDICREATE, 0, \n\t\t\t(LONG_PTR) (LPMDICREATESTRUCT) &mcs); \n\t}\n\telse\n\t{\n\t\tsprintf(title, \"%s - FTEEditor\", name);\n\n\t\tneweditor->window=CreateWindow(EDIT_WINDOW_CLASS_NAME, title, WS_OVERLAPPEDWINDOW,\n\t\t\t0, 0, 640, 480, NULL, NULL, ghInstance, NULL);\n\t}\n\n\tif (menu)\n\t\tSetMenu(neweditor->window, menu);\n\n\tif (!neweditor->window)\n\t{\n\t\tMessageBox(NULL, \"Failed to create editor window\", \"Error\", 0);\n\t\treturn;\n\t}\n\tSetWindowLongPtr(neweditor->window, GWLP_USERDATA, (LONG_PTR)neweditor);\n\n\tEditorReload(neweditor);\n\n\tif (line >= 0)\n\t{\n\t\tif (setcontrol)\n\t\t\tEdit_SetSel(neweditor->editpane, Edit_LineIndex(neweditor->editpane, line+1)-1, Edit_LineIndex(neweditor->editpane, line+1)-1);\n\t\telse\n\t\t\tEdit_SetSel(neweditor->editpane, Edit_LineIndex(neweditor->editpane, line), Edit_LineIndex(neweditor->editpane, line+1)-1);\n\t}\n\telse\n\t\tEdit_SetSel(neweditor->editpane, Edit_LineIndex(neweditor->editpane, 0), Edit_LineIndex(neweditor->editpane, 0));\n\n\tEdit_ScrollCaret(neweditor->editpane);\n\n\tShowWindow(neweditor->window, SW_SHOW);\n\tSetFocus(mainwindow);\n\tSetFocus(neweditor->window);\n\tSetFocus(neweditor->editpane);\n\n\tif (setcontrol && neweditor->scintilla)\n\t{\n\t\tSendMessage(neweditor->editpane, SCI_MARKERADD, line, 1);\n\t\tSendMessage(neweditor->editpane, SCI_MARKERADD, line, 2);\n\t}\n}\n\nint EditorSave(editor_t *edit)\n{\n\tstruct stat sbuf;\n\tint len;\n\twchar_t *wfile;\n\tchar *afile;\n\tBOOL failed = TRUE;\n\tint saved = false;\n\tif (edit->scintilla)\n\t{\n\t\t//wordpad will corrupt any embedded quake chars if we force a bom, because it'll re-save using the wrong char encoding by default.\n\t\tint bomlen = 0;\n\t\tchar *bom = \"\";\n\t\tif (edit->savefmt == UTF32BE || edit->savefmt == UTF32LE || edit->savefmt == UTF16BE)\n\t\t\tedit->savefmt = UTF16LE;\n\n\t\tif (edit->savefmt == UTF8_BOM)\n\t\t{\n\t\t\tbomlen = 3;\n\t\t\tbom = \"\\xEF\\xBB\\xBF\";\n\t\t}\n\t\telse if (edit->savefmt == UTF16BE)\n\t\t{\n\t\t\tbomlen = 2;\n\t\t\tbom = \"\\xFE\\xFF\";\n\t\t}\n\t\telse if (edit->savefmt == UTF16LE)\n\t\t{\n\t\t\tbomlen = 2;\n\t\t\tbom = \"\\xFF\\xFE\";\n\t\t}\n\t\telse if (edit->savefmt == UTF32BE)\n\t\t{\n\t\t\tbomlen = 4;\n\t\t\tbom = \"\\x00\\x00\\xFE\\xFF\";\n\t\t}\n\t\telse if (edit->savefmt == UTF32LE)\n\t\t{\n\t\t\tbomlen = 4;\n\t\t\tbom = \"\\xFF\\xFE\\x00\\x00\";\n\t\t}\n\t\tlen = SendMessage(edit->editpane, SCI_GETLENGTH, 0, 0);\n\t\tafile = malloc(bomlen+len+1);\n\t\tif (!afile)\n\t\t{\n\t\t\tMessageBox(NULL, \"Save failed - not enough mem\", \"Error\", 0);\n\t\t\treturn false;\n\t\t}\n\t\tmemcpy(afile, bom, bomlen);\n\t\tSendMessage(edit->editpane, SCI_GETTEXT, len+1, bomlen+(LPARAM)afile);\n\n\t\t//because wordpad saves in ansi by default instead of the format the file was originally saved in, we HAVE to use ansi without\n\t\tif (edit->savefmt != UTF8_BOM && edit->savefmt != UTF8_RAW)\n\t\t{\n\t\t\tint mchars;\n\t\t\tchar *mc;\n\t\t\tint wchars = MultiByteToWideChar(CP_UTF8, 0, afile, len, NULL, 0);\n\t\t\tif (wchars)\n\t\t\t{\n\t\t\t\twchar_t *wc = malloc(wchars * sizeof(wchar_t));\n\t\t\t\tMultiByteToWideChar(CP_UTF8, 0, afile, len, wc, wchars);\n\n\t\t\t\tif (edit->savefmt == UTF_ANSI)\n\t\t\t\t{\n\t\t\t\t\tmchars = WideCharToMultiByte(CP_ACP, 0, wc, wchars, NULL, 0, \"\", &failed);\n\t\t\t\t\tif (mchars)\n\t\t\t\t\t{\n\t\t\t\t\t\tmc = malloc(mchars);\n\t\t\t\t\t\tWideCharToMultiByte(CP_ACP, 0, wc, wchars, mc, mchars, \"\", &failed);\n\t\t\t\t\t\tif (!failed)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (!QCC_WriteFile(edit->filename, mc, mchars))\n\t\t\t\t\t\t\t\tsaved = -1;\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\tsaved = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tfree(mc);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tif (!QCC_WriteFile(edit->filename, wc, wchars))\n\t\t\t\t\t\tsaved = -1;\n\t\t\t\t\telse\n\t\t\t\t\t\tsaved = true;\n\t\t\t\t}\n\t\t\t\tfree(wc);\n\t\t\t}\n\t\t}\n\n\t\tif (!saved)\n\t\t{\n\t\t\tif (!QCC_WriteFile(edit->filename, afile, bomlen+len))\n\t\t\t\tsaved = -1;\n\t\t\telse\n\t\t\t\tsaved = true;\n\t\t}\n\t\tfree(afile);\n\t\tif (saved < 0)\n\t\t{\n\t\t\tMessageBox(NULL, \"Save failed\\nCheck path and ReadOnly flags\", \"Failure\", 0);\n\t\t\treturn false;\n\t\t}\n\t\tSendMessage(edit->editpane, SCI_SETSAVEPOINT, 0, 0);\n\t}\n\telse\n\t{\n\t\tif (edit->savefmt == UTF_ANSI)\n\t\t{\n\t\t\tlen = GetWindowTextLengthA(edit->editpane);\n\t\t\tafile = malloc(len+1);\n\t\t\tif (!afile)\n\t\t\t{\n\t\t\t\tMessageBox(NULL, \"Save failed - not enough mem\", \"Error\", 0);\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tGetWindowText(edit->editpane, afile, len+1);\n\t\t\tif (!QCC_WriteFile(edit->filename, afile, len))\n\t\t\t{\n\t\t\t\tfree(afile);\n\t\t\t\tMessageBox(NULL, \"Save failed\\nCheck path and ReadOnly flags\", \"Failure\", 0);\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tfree(afile);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tlen = GetWindowTextLengthW(edit->editpane);\n\t\t\twfile = malloc((len+1)*2);\n\t\t\tif (!wfile)\n\t\t\t{\n\t\t\t\tMessageBox(NULL, \"Save failed - not enough mem\", \"Error\", 0);\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tGetWindowTextW(edit->editpane, wfile, len+1);\n\t\t\tif (!QCC_WriteFileW(edit->filename, wfile, len))\n\t\t\t{\n\t\t\t\tfree(wfile);\n\t\t\t\tMessageBox(NULL, \"Save failed\\nCheck path and ReadOnly flags\", \"Failure\", 0);\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tfree(wfile);\n\t\t}\n\t}\n\n\t/*now whatever is on disk should have the current time*/\n\tedit->modified = false;\n\tstat(edit->filename, &sbuf);\n\tedit->filemodifiedtime = sbuf.st_mtime;\n\n\t//remove the * in a silly way.\n\tedit->oldline=~0;\n\tUpdateEditorTitle(edit);\n\n\treturn true;\n}\nvoid EditorsRun(void)\n{\n}\n\nstatic unsigned char *buf_get_malloc(void *ctx, size_t len)\n{\n\treturn malloc(len);\n}\nvoid *GUIReadFile(const char *fname, unsigned char *(*buf_get)(void *ctx, size_t len), void *buf_ctx, size_t *out_size, pbool issourcefile)\n{\n\teditor_t *e;\n\tsize_t blen;\n\tunsigned char *buffer;\n\tif (!buf_get)\n\t\tbuf_get = buf_get_malloc;\n\tfor (e = editors; e; e = e->next)\n\t{\n\t\tif (e->window && !strcmp(e->filename, fname))\n\t\t{\n\t\t\t//our qcc itself is fine with utf-16, so long as it has a BOM.\n\t\t\tif (e->scintilla)\n\t\t\t{\t//take the opportunity to grab a predefined preprocessor list for this file\n\t\t\t\tchar *deflist = QCC_PR_GetDefinesList();\n\t\t\t\tSendMessage(e->editpane, SCI_SETKEYWORDS,\t4,\t(LPARAM)deflist);\n\t\t\t\tfree(deflist);\n\n\t\t\t\t{\t//and just in case some system defs changed.\n\t\t\t\t\tchar buffer[65536];\n\t\t\t\t\tGenBuiltinsList(buffer, sizeof(buffer));\n\t\t\t\t\tSendMessage(e->editpane, SCI_SETKEYWORDS,\t1,\t(LPARAM)buffer);\n\t\t\t\t}\n\n\t\t\t\tblen = SendMessage(e->editpane, SCI_GETLENGTH, 0, 0);\n\t\t\t\tbuffer = buf_get(buf_ctx, blen+1);\n\t\t\t\tblen = SendMessage(e->editpane, SCI_GETTEXT, blen+1, (LPARAM)buffer);\n\t\t\t}\n\t\t\telse if (e->savefmt == UTF_ANSI)\n\t\t\t{\n\t\t\t\tblen = GetWindowTextLengthA(e->editpane);\n\t\t\t\tbuffer = buf_get(buf_ctx, blen);\n\t\t\t\tGetWindowTextA(e->editpane, buffer, blen);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tblen = (GetWindowTextLengthW(e->editpane)+1)*2;\n\t\t\t\tbuffer = buf_get(buf_ctx, blen);\n\t\t\t\t*(wchar_t*)buffer = 0xfeff;\n\t\t\t\tGetWindowTextW(e->editpane, (wchar_t*)buffer+1, blen-sizeof(wchar_t));\n\t\t\t}\n\n\t\t\tif (e->modified)\n\t\t\t{\n\t\t\t\tif (EditorModified(e))\n\t\t\t\t{\n\t\t\t\t\tif (MessageBox(e->window, \"File was modified on disk. Overwrite?\", e->filename, MB_YESNO) == IDYES)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (e->scintilla)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tQCC_WriteFile(e->filename, buffer, blen);\n\t\t\t\t\t\t\tSendMessage(e->editpane, SCI_SETSAVEPOINT, 0, 0);\t//tell the control that it was saved.\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tQCC_WriteFileW(e->filename, (wchar_t*)buffer+1, blen);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t*out_size = blen;\n\t\t\treturn buffer;\n\t\t}\n\t}\n\n\tif (issourcefile)\n\t\tAddSourceFile(compilingrootfile,\tfname);\n\n\treturn QCC_ReadFile(fname, buf_get, buf_ctx, out_size);\n}\n\nint GUIFileSize(const char *fname)\n{\n\teditor_t *e;\n\tfor (e = editors; e; e = e->next)\n\t{\n\t\tif (e->window && !strcmp(e->filename, fname))\n\t\t{\n\t\t\tint len;\n\t\t\tif (e->scintilla)\n\t\t\t\tlen = SendMessage(e->editpane, SCI_GETLENGTH, 0, 0);\n\t\t\telse if (e->savefmt == UTF_ANSI)\n\t\t\t\tlen = GetWindowTextLengthA(e->editpane);\n\t\t\telse\n\t\t\t\tlen = (GetWindowTextLengthW(e->editpane)+1)*2;\n\t\t\treturn len;\n\t\t}\n\t}\n\treturn QCC_PopFileSize(fname);\n}\n\n/*checks if the file has been modified externally*/\npbool EditorModified(editor_t *e)\n{\n\tstruct stat sbuf;\n\tstat(e->filename, &sbuf);\n\tif (e->filemodifiedtime != sbuf.st_mtime)\n\t\treturn true;\n\n\treturn false;\n}\n\nchar *COM_ParseOut (const char *data, char *out, int outlen)\n{\n\tint\t\tc;\n\tint\t\tlen;\n\n\tlen = 0;\n\tout[0] = 0;\n\n\tif (!data)\n\t\treturn NULL;\n\n// skip whitespace\nskipwhite:\n\twhile ( (c = *data) <= ' ')\n\t{\n\t\tif (c == 0)\n\t\t\treturn NULL;\t\t\t// end of file;\n\t\tdata++;\n\t}\n\n// skip // comments\n\tif (c=='/')\n\t{\n\t\tif (data[1] == '/')\n\t\t{\n\t\t\twhile (*data && *data != '\\n')\n\t\t\t\tdata++;\n\t\t\tgoto skipwhite;\n\t\t}\n\t}\n\n//skip / * comments\n\tif (c == '/' && data[1] == '*')\n\t{\n\t\tdata+=2;\n\t\twhile(*data)\n\t\t{\n\t\t\tif (*data == '*' && data[1] == '/')\n\t\t\t{\n\t\t\t\tdata+=2;\n\t\t\t\tgoto skipwhite;\n\t\t\t}\n\t\t\tdata++;\n\t\t}\n\t\tgoto skipwhite;\n\t}\n\n// handle marked up quoted strings specially (c-style, but with leading \\ before normal opening \")\n\tif (c == '\\\\' && data[1] == '\\\"')\n\t{\n\t\tdata+=2;\n\t\twhile (1)\n\t\t{\n\t\t\tif (len >= outlen-2)\n\t\t\t{\n\t\t\t\tout[len] = '\\0';\n\t\t\t\treturn (char*)data;\n\t\t\t}\n\n\t\t\tc = *data++;\n\t\t\tif (!c)\n\t\t\t{\n\t\t\t\tout[len] = 0;\n\t\t\t\treturn (char*)data-1;\n\t\t\t}\n\t\t\tif (c == '\\\\')\n\t\t\t{\n\t\t\t\tc = *data++;\n\t\t\t\tswitch(c)\n\t\t\t\t{\n\t\t\t\tcase '\\r':\n\t\t\t\t\tif (*data == '\\n')\n\t\t\t\t\t\tdata++;\n\t\t\t\tcase '\\n':\n\t\t\t\t\tcontinue;\n\t\t\t\tcase 'n':\n\t\t\t\t\tc = '\\n';\n\t\t\t\t\tbreak;\n\t\t\t\tcase 't':\n\t\t\t\t\tc = '\\t';\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'r':\n\t\t\t\t\tc = '\\r';\n\t\t\t\t\tbreak;\n\t\t\t\tcase '$':\n\t\t\t\tcase '\\\\':\n\t\t\t\tcase '\\'':\n\t\t\t\t\tbreak;\n\t\t\t\tcase '\"':\n\t\t\t\t\tc = '\"';\n\t\t\t\t\tout[len] = c;\n\t\t\t\t\tlen++;\n\t\t\t\t\tcontinue;\n\t\t\t\tdefault:\n\t\t\t\t\tc = '?';\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (c=='\\\"' || !c)\n\t\t\t{\n\t\t\t\tout[len] = 0;\n\t\t\t\treturn (char*)data;\n\t\t\t}\n\t\t\tout[len] = c;\n\t\t\tlen++;\n\t\t}\n\t}\n\n// handle legacy quoted strings specially\n\tif (c == '\\\"')\n\t{\n\t\tdata++;\n\t\twhile (1)\n\t\t{\n\t\t\tif (len >= outlen-1)\n\t\t\t{\n\t\t\t\tout[len] = 0;\n\t\t\t\treturn (char*)data;\n\t\t\t}\n\n\t\t\tc = *data++;\n\t\t\tif (c=='\\\"' || !c)\n\t\t\t{\n\t\t\t\tout[len] = 0;\n\t\t\t\treturn (char*)data;\n\t\t\t}\n\t\t\tout[len] = c;\n\t\t\tlen++;\n\t\t}\n\t}\n\n// parse a regular word\n\tdo\n\t{\n\t\tif (len >= outlen-1)\n\t\t{\n\t\t\tout[len] = 0;\n\t\t\treturn (char*)data;\n\t\t}\n\n\t\tout[len] = c;\n\t\tdata++;\n\t\tlen++;\n\t\tc = *data;\n\t} while (c>32);\n\n\tout[len] = 0;\n\treturn (char*)data;\n}\n\nstatic void EngineGiveFocus(void)\n{\n\tHWND game;\n\tif (gamewindow)\n\t{\n\t\tenginewindow_t *ctx = (enginewindow_t*)(LONG_PTR)GetWindowLongPtr(gamewindow, GWLP_USERDATA);\n\t\tif (ctx)\n\t\t{\n\t\t\tif (ctx->refocuswindow)\n\t\t\t{\n\t\t\t\tSetForegroundWindow(ctx->refocuswindow);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\tSetFocus(gamewindow);\n\t\tgame = GetWindow(gamewindow, GW_CHILD);\n\t\tif (game)\n\t\t\tSetForegroundWindow(game);\t//make sure the game itself has focus\n\t}\n}\n\nstatic pbool EngineCommandWnd(HWND wnd, char *message)\n{\n\t//qcresume\t\t\t- resume running\n\t//qcinto\t\t\t- singlestep. execute-with-debugging child functions\n\t//qcover\t\t\t- singlestep. execute-without-debugging child functions\n\t//qcout\t\t\t\t- singlestep. leave current function and enter parent.\n\t//qcbreak \"$loc\"\t- set breakpoint\n\t//qcwatch \"$var\"\t- set watchpoint\n\t//qcstack\t\t\t- force-report stack trace\n\tenginewindow_t *ctx;\n\tif (wnd)\n\t{\n\t\tctx = (enginewindow_t*)(LONG_PTR)GetWindowLongPtr(gamewindow, GWLP_USERDATA);\n\t\tif (ctx)\n\t\t{\n\t\t\tif (ctx->pipetoengine)\n\t\t\t{\n\t\t\t\tDWORD written = 0;\n\t\t\t\tWriteFile(ctx->pipetoengine, message, strlen(message), &written, NULL);\n\t\t\t\treturn TRUE;\n\t\t\t}\n\t\t}\n\t}\n\treturn FALSE;\n}\nstatic pbool EngineCommandf(char *message, ...)\n{\n\tva_list\t\tva;\n\tchar\t\tfinalmessage[1024];\n\tva_start (va, message);\n\tvsnprintf (finalmessage, sizeof(finalmessage)-1, message, va);\n\tva_end (va);\n\treturn EngineCommandWnd(gamewindow, finalmessage);\n}\nstatic pbool EngineCommandWndf(HWND wnd, char *message, ...)\n{\n\tva_list\t\tva;\n\tchar\t\tfinalmessage[1024];\n\tva_start (va, message);\n\tvsnprintf (finalmessage, sizeof(finalmessage)-1, message, va);\n\tva_end (va);\n\treturn EngineCommandWnd(wnd, finalmessage);\n}\n\nDWORD WINAPI threadwrapper(void *args)\n{\n\tpbool hadstatus = false;\n\tenginewindow_t *ctx = args;\n\t{\n\t\tchar workingdir[MAX_PATH+10];\n\t\tchar absexe[MAX_PATH+10];\n\t\tchar absbase[MAX_PATH+10];\n\t\tchar mssucks[MAX_PATH+10];\n\t\tchar *gah;\n\t\tPROCESS_INFORMATION childinfo;\n\t\tSTARTUPINFO startinfo;\n\t\tSECURITY_ATTRIBUTES pipesec = {sizeof(pipesec), NULL, TRUE};\n\t\tchar cmdline[8192];\n\t\t_snprintf(cmdline, sizeof(cmdline), \"\\\"%s\\\" %s -qcdebug\", enginebinary, enginecommandline);\n\n\t\tmemset(&startinfo, 0, sizeof(startinfo));\n\t\tstartinfo.cb = sizeof(startinfo);\n\t\tstartinfo.hStdInput = NULL;\n\t\tstartinfo.hStdError = NULL;\n\t\tstartinfo.hStdOutput = NULL;\n\t\tstartinfo.dwFlags |= STARTF_USESTDHANDLES;\n\n\t\t//create pipes for the stdin/stdout.\n\t\tCreatePipe(&ctx->pipefromengine, &startinfo.hStdOutput, &pipesec, 0);\n\t\tCreatePipe(&startinfo.hStdInput, &ctx->pipetoengine, &pipesec, 0);\n\n\t\tSetHandleInformation(ctx->pipefromengine, HANDLE_FLAG_INHERIT, 0);\n\t\tSetHandleInformation(ctx->pipetoengine, HANDLE_FLAG_INHERIT, 0);\n\n\t\t//let the engine know who to give focus to \n\t\t{\n\t\t\tchar message[256];\n\t\t\tDWORD written;\n\t\t\t_snprintf(message, sizeof(message)-1, \"debuggerwnd %#\"PRIxPTR\"\\n\",  (uintptr_t)(void*)mainwindow);\n\t\t\tWriteFile(ctx->pipetoengine, message, strlen(message), &written, NULL);\n\t\t}\n\n\t\t//let the engine know which window to embed itself in\n\t\tif (ctx->embedtype)\n\t\t{\n\t\t\tchar message[256];\n\t\t\tDWORD written;\n\t\t\tRECT rect;\n\t\t\tGetClientRect(ctx->window, &rect);\n\t\t\t_snprintf(message, sizeof(message)-1, \"vid_recenter %i %i %i %i %#\"PRIxPTR\"\\n\", 0, 0, (int)(rect.right - rect.left), (int)(rect.bottom-rect.top), (uintptr_t)(void*)ctx->window);\n\t\t\tWriteFile(ctx->pipetoengine, message, strlen(message), &written, NULL);\n\t\t}\n\n\t\tGetCurrentDirectory(sizeof(workingdir)-1, workingdir);\n\t\tstrcpy(mssucks, enginebasedir);\n\t\twhile ((gah = strchr(mssucks, '/')))\n\t\t\t*gah = '\\\\';\n\t\tPathCombine(absbase, workingdir, mssucks);\n\t\tstrcpy(mssucks, enginebinary);\n\t\twhile ((gah = strchr(mssucks, '/')))\n\t\t\t*gah = '\\\\';\n\t\tPathCombine(absexe, absbase, mssucks);\n\t\tif (!CreateProcess(absexe, cmdline, NULL, NULL, TRUE, 0, NULL, absbase, &startinfo, &childinfo))\n\t\t{\n\t\t\tHRESULT hr = GetLastError();\n\t\t\tswitch(hr)\n\t\t\t{\n\t\t\tcase ERROR_FILE_NOT_FOUND:\n\t\t\t\tMessageBox(mainwindow, \"File Not Found\", \"Cannot Start Engine\", 0);\n\t\t\t\tbreak;\n\t\t\tcase ERROR_PATH_NOT_FOUND:\n\t\t\t\tMessageBox(mainwindow, \"Path Not Found\", \"Cannot Start Engine\", 0);\n\t\t\t\tbreak;\n\t\t\tcase ERROR_ACCESS_DENIED:\n\t\t\t\tMessageBox(mainwindow, \"Access Denied\", \"Cannot Start Engine\", 0);\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tMessageBox(mainwindow, qcva(\"gla: %x\", (unsigned)hr), \"Cannot Start Engine\", 0);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\thadstatus = true;\t//don't warn about other stuff\n\t\t}\n\n\t\t//these ends of the pipes were inherited by now, so we can discard them in the caller.\n\t\tCloseHandle(startinfo.hStdOutput);\n\t\tCloseHandle(startinfo.hStdInput);\n\t}\n\n\t{\n\t\tchar buffer[8192];\n\t\tunsigned int bufoffs = 0;\n\t\tchar *nl;\n\t\twhile(1)\n\t\t{\n\t\t\tDWORD avail;\n\t\t\t//use Peek so we can read exactly how much there is without blocking, so we don't have to read byte-by-byte.\n\t\t\tPeekNamedPipe(ctx->pipefromengine, NULL, 0, NULL, &avail, NULL);\n\t\t\tif (!avail)\n\t\t\t\tavail = 1;\t//so we do actually sleep.\n\t\t\tif (avail > sizeof(buffer)-1 - bufoffs)\n\t\t\t\tavail = sizeof(buffer)-1 - bufoffs;\n\t\t\tif (!ReadFile(ctx->pipefromengine, buffer + bufoffs, avail, &avail, NULL) || !avail)\n\t\t\t{\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tbufoffs += avail;\n\t\t\twhile(1)\n\t\t\t{\n\t\t\t\tbuffer[bufoffs] = 0;\n\t\t\t\tnl = strchr(buffer, '\\n');\n\t\t\t\tif (nl)\n\t\t\t\t{\n\t\t\t\t\t*nl = 0;\n\t\t\t\t\tif (!strncmp(buffer, \"status \", 7))\n\t\t\t\t\t{\n\t\t\t\t\t\t//SetWindowText(ctx->window, buffer+7);\n\t\t\t\t\t\thadstatus = true;\n\t\t\t\t\t}\n\t\t\t\t\telse if (!strcmp(buffer, \"status\"))\n\t\t\t\t\t{\n\t\t\t\t\t\t//SetWindowText(ctx->window, \"Engine\");\n\t\t\t\t\t\thadstatus = true;\n\t\t\t\t\t}\n\t\t\t\t\telse if (!strcmp(buffer, \"curserver\"))\n\t\t\t\t\t{\n\t\t\t\t\t\t//not interesting\n\t\t\t\t\t}\n\t\t\t\t\telse if (!strncmp(buffer, \"qcstack \", 6))\n\t\t\t\t\t{\n\t\t\t\t\t\t//qcvm is giving a stack trace\n\t\t\t\t\t\t//stack reset\n\t\t\t\t\t\t//stack \"$func\" \"$loc\"\n\t\t\t\t\t\t//local $depth\n\t\t\t\t\t}\n\t\t\t\t\telse if (!strncmp(buffer, \"qcstep \", 7) || !strncmp(buffer, \"qcfault \", 8))\n\t\t\t\t\t{\n\t\t\t\t\t\t//post it, because of thread ownership issues.\n\t\t\t\t\t\tstatic char filenamebuffer[256];\n\t\t\t\t\t\tchar line[16];\n\t\t\t\t\t\tchar error[256];\n\t\t\t\t\t\tchar *l = COM_ParseOut(buffer+7, filenamebuffer, sizeof(filenamebuffer));\n\t\t\t\t\t\twhile (*l == ' ')\n\t\t\t\t\t\t\tl++;\n\t\t\t\t\t\tif (*l == ':')\n\t\t\t\t\t\t\tl++;\n\t\t\t\t\t\tl = COM_ParseOut(l, line, sizeof(line));\n\t\t\t\t\t\tl = COM_ParseOut(l, error, sizeof(error));\n\t\t\t\t\t\tPostMessage(ctx->window, WM_USER, atoi(line), (LPARAM)filenamebuffer);\t//and tell the owning window to try to close it again\n\t\t\t\t\t\tif (*error)\n\t\t\t\t\t\t\tPostMessage(ctx->window, WM_USER+3, 0, (LPARAM)strdup(error));\t//and tell the owning window to try to close it again\n\t\t\t\t\t}\n\t\t\t\t\telse if (!strncmp(buffer, \"qcvalue \", 8))\n\t\t\t\t\t{\n\t\t\t\t\t\t//qcvalue \"$variableformula\" \"$value\"\n\t\t\t\t\t\t//update tooltip to show engine's current value\n\t\t\t\t\t\tPostMessage(ctx->window, WM_USER+2, 0, (LPARAM)strdup(buffer+8));\t//and tell the owning window to try to close it again\n\t\t\t\t\t}\n\t\t\t\t\telse if (!strncmp(buffer, \"qcreloaded \", 10))\n\t\t\t\t\t{\n\t\t\t\t\t\t//so we can resend any breakpoint commands\n\t\t\t\t\t\t//qcreloaded \"$vmname\" \"$progsname\"\n\t\t\t\t\t\tchar caption[256];\n\t\t\t\t\t\tHWND gw = GetWindow(ctx->window, GW_CHILD);\n\t\t\t\t\t\tif (gw)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tGetWindowText(gw, caption, sizeof(caption));\n\t\t\t\t\t\t\tSetWindowText(ctx->window, caption);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tPostMessage(ctx->window, WM_USER+1, 0, 0);\t//and tell the owning window to try to close it again\n\t\t\t\t\t\thadstatus = true;\n\t\t\t\t\t}\n\t\t\t\t\telse if (!strncmp(buffer, \"refocuswindow\", 13) && (buffer[13] == ' ' || !buffer[13]))\n\t\t\t\t\t{\n\t\t\t\t\t\tchar *l = buffer+13;\n\t\t\t\t\t\twhile(*l == ' ')\n\t\t\t\t\t\t\tl++;\n\t\t\t\t\t\tctx->refocuswindow = (HWND)(size_t)strtoull(l, &l, 0);\n\t\t\t\t\t\tShowWindow(ctx->window, SW_HIDE);\n\t\t\t\t\t\thadstatus = true;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\t//handle anything else we need to handle here\n\t\t\t\t\t\tprintf(\"Unknown command from engine \\\"%s\\\"\\n\", buffer);\n\t\t\t\t\t}\n\t\t\t\t\tnl++;\n\t\t\t\t\tbufoffs -= (nl-buffer);\n\t\t\t\t\tmemmove(buffer, nl, bufoffs);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tCloseHandle(ctx->pipefromengine);\n\t\tctx->pipefromengine = NULL;\n\t\tCloseHandle(ctx->pipetoengine);\n\t\tctx->pipetoengine = NULL;\n\n\t\tif (!hadstatus)\n\t\t\tMessageBox(mainwindow, \"Engine terminated without acknowledging debug session.\\nCurrently only FTE supports debugging.\", \"Debugging Failed\", MB_OK);\n\t}\n\n\tctx->pipeclosed = true;\n\tPostMessage(ctx->window, WM_CLOSE, 0, 0);\t//and tell the owning window to try to close it again\n\treturn 0;\n}\n\nstatic LRESULT CALLBACK EngineWndProc(HWND hWnd,UINT message,\n\t\t\t\t     WPARAM wParam,LPARAM lParam)\n{\n\tenginewindow_t *ctx;\n\teditor_t *editor;\n\tswitch (message)\n\t{\n\tcase WM_CREATE:\n\t\tctx = malloc(sizeof(*ctx));\n\t\tmemset(ctx, 0, sizeof(*ctx));\n\t\tSetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR)ctx);\n\t\tctx->window = hWnd;\n\t\tctx->embedtype = (size_t)((CREATESTRUCT*)lParam)->lpCreateParams;\n\t\tctx->thread = (HANDLE)CreateThread(NULL, 0, threadwrapper, ctx, 0, &ctx->tid);\n\t\tbreak;\n\tcase WM_SIZE:\n\t\tctx = (enginewindow_t*)(LONG_PTR)GetWindowLongPtr(gamewindow, GWLP_USERDATA);\n\t\tif (ctx && ctx->embedtype)\n\t\t{\n\t\t\tRECT r;\n\t\t\tGetClientRect(hWnd, &r);\n\t\t\tEngineCommandWndf(hWnd, \"vid_recenter %i %i %i %i %#p\\n\", r.left, r.top, r.right-r.left, r.bottom - r.top, (void*)ctx->window);\n\t\t}\n\t\tgoto gdefault;\n\tcase WM_CLOSE:\n\t\t//ask the engine to quit\n\t\tctx = (enginewindow_t*)(LONG_PTR)GetWindowLongPtr(gamewindow, GWLP_USERDATA);\n\t\tif (ctx && !ctx->pipeclosed)\n\t\t{\n\t\t\tEngineCommandWnd(hWnd, \"quit force\\n\");\n\t\t\tbreak;\n\t\t}\n\t\tgoto gdefault;\n\tcase WM_DESTROY:\n\t\tEngineCommandWnd(hWnd, \"quit force\\n\");\t//just in case\n\t\tctx = (enginewindow_t*)(LONG_PTR)GetWindowLongPtr(gamewindow, GWLP_USERDATA);\n\t\tif (ctx)\n\t\t{\n\t\t\tWaitForSingleObject(ctx->thread, INFINITE);\n\t\t\tCloseHandle(ctx->thread);\n\t\t\tfree(ctx);\n\t\t}\n\t\tif (hWnd == gamewindow)\n\t\t{\n\t\t\tSplitterRemove(watches);\n\t\t\tgamewindow = NULL;\n\t\t}\n\t\tbreak;\n\tcase WM_USER:\n\t\t//engine broke. show code.\n\t\tif (lParam)\n\t\t\tSetForegroundWindow(mainwindow);\n\t\tEditFile((char*)lParam, wParam-1, true);\n\n\t\tif (watches)\n\t\t{\n\t\t\tchar text[MAX_PATH];\n\t\t\tint i, lim = ListView_GetItemCount(watches);\n\t\t\tfor (i = 0; i < lim; i++)\n\t\t\t{\n\t\t\t\tListView_GetItemText(watches, i, 0, text, sizeof(text));\n\t\t\t\tEngineCommandWndf(hWnd, \"qcinspect \\\"%s\\\" \\\"%s\\\"\\n\", text, \"\"); //term, scope\n\t\t\t}\n\t\t}\n\t\tbreak;\n\tcase WM_USER+1:\n\t\t//engine loaded a progs, reset breakpoints.\n\t\tfor (editor = editors; editor; editor = editor->next)\n\t\t{\n\t\t\tint line = -1;\n\t\t\tif (!editor->scintilla)\n\t\t\t\tcontinue;\n\n\t\t\tfor (;;)\n\t\t\t{\n\t\t\t\tline = SendMessage(editor->editpane, SCI_MARKERNEXT, line, 1);\n\t\t\t\tif (line == -1)\n\t\t\t\t\tbreak;\t//no more.\n\t\t\t\tline++;\n\n\t\t\t\tEngineCommandWndf(hWnd, \"qcbreakpoint 1 \\\"%s\\\" %i\\n\", editor->filename, line);\n\t\t\t}\n\t\t}\n\t\t//and now let the engine continue\n\t\tSetFocus(hWnd);\n\t\tEngineCommandWnd(hWnd, \"qcresume\\n\");\n\t\tbreak;\n\tcase WM_USER+2:\n\t\t{\n\t\t\tchar varname[1024];\n\t\t\tchar varvalue[1024];\n\t\t\tchar *line = (char*)lParam;\n\t\t\tline = COM_ParseOut(line, varname, sizeof(varname));\n\t\t\tline = COM_ParseOut(line, varvalue, sizeof(varvalue));\n\t\t\tif (tooltip_editor && !strcmp(varname, tooltip_variable))\n\t\t\t{\n\t\t\t\tchar tip[2048];\n\t\t\t\tif (*tooltip_comment)\n\t\t\t\t\t_snprintf(tip, sizeof(tip)-1, \"%s %s = %s\\r\\n%s\", tooltip_type, tooltip_variable, varvalue, tooltip_comment);\n\t\t\t\telse\n\t\t\t\t\t_snprintf(tip, sizeof(tip)-1, \"%s %s = %s\", tooltip_type, tooltip_variable, varvalue);\n\n\t\t\t\tSendMessage(tooltip_editor->editpane, SCI_CALLTIPSHOW, (WPARAM)tooltip_position, (LPARAM)tip);\n\t\t\t}\n\t\t\tif (watches)\n\t\t\t{\n\t\t\t\tchar text[MAX_PATH];\n\t\t\t\tint i, lim = ListView_GetItemCount(watches);\n\t\t\t\tfor (i = 0; i < lim; i++)\n\t\t\t\t{\n\t\t\t\t\tListView_GetItemText(watches, i, 0, text, sizeof(text));\n\t\t\t\t\tif (!strcmp(text, varname))\n\t\t\t\t\t\tListView_SetItemText(watches, i, 1, varvalue);\n\t\t\t\t}\n\t\t\t}\n\t\t\tfree((char*)lParam);\n\t\t}\n\t\tbreak;\n\tcase WM_USER+3:\n\t\t{\n\t\t\tchar *msg = (char*)lParam;\n\t\t\tMessageBox(mainwindow, msg, \"QC Fault\", 0);\n\t\t\tfree(msg);\n\t\t}\n\t\tbreak;\n\n\tdefault:\n\tgdefault:\n\t\treturn DefMDIChildProc(hWnd,message,wParam,lParam);\n\t}\n\treturn 0;\n}\nstatic INT CALLBACK StupidBrowseCallbackProc(HWND hwnd, UINT uMsg, LPARAM lp, LPARAM pData) \n{\t//'stolen' from microsoft's knowledge base.\n\t//required to work around microsoft being annoying.\n\tTCHAR szDir[MAX_PATH];\n\tchar *foo;\n\tswitch(uMsg) \n\t{\n\tcase BFFM_INITIALIZED: \n\t\tif (GetCurrentDirectory(sizeof(szDir)/sizeof(TCHAR), szDir))\n\t\t{\n\t\t\tfoo = strrchr(szDir, '\\\\');\n\t\t\tif (foo)\n\t\t\t\t*foo = 0;\n\t\t\tfoo = strrchr(szDir, '\\\\');\n\t\t\tif (foo)\n\t\t\t\t*foo = 0;\n\t\t\tSendMessage(hwnd, BFFM_SETSELECTION, TRUE, (LPARAM)szDir);\n\t\t}\n\t\tbreak;\n\tcase BFFM_SELCHANGED: \n\t\tif (SHGetPathFromIDList((LPITEMIDLIST) lp ,szDir))\n\t\t{\n\t\t\twhile((foo = strchr(szDir, '\\\\')))\n\t\t\t\t*foo = '/';\n\t\t\t//fixme: verify that id1 is a subdir perhaps?\n\t\t\tSendMessage(hwnd,BFFM_SETSTATUSTEXT,0,(LPARAM)szDir);\n\t\t}\n\t\tbreak;\n\t}\n\treturn 0;\n}\n\npbool PromptForFile(const char *prompt, const char *filter, const char *basepath, const char *defaultfile, char outname[], size_t outsize, pbool create)\n{\n\tchar oldworkingdir[MAX_PATH+10];\t//cmdlg changes it...\n\tchar workingdir[MAX_PATH+10];\n\n#ifndef OFN_DONTADDTORECENT\n#define OFN_DONTADDTORECENT 0x02000000\n#endif\n\n\tchar *s;\n\tchar initialdir[MAX_PATH+10];\n\tchar absengine[MAX_PATH+10];\n\tOPENFILENAME ofn;\n\tpbool okay;\n\tmemset(&ofn, 0, sizeof(ofn));\n\tofn.lStructSize = sizeof(ofn);\n\tofn.hwndOwner = mainwindow;\n\tofn.hInstance = ghInstance;\n\tofn.lpstrFile = absengine;\n\tofn.Flags = OFN_EXPLORER|(create?OFN_PATHMUSTEXIST|OFN_CREATEPROMPT:OFN_FILEMUSTEXIST)|OFN_DONTADDTORECENT;\n\tofn.lpstrTitle = prompt;\n\tofn.nMaxFile = outsize-1;\n\tofn.lpstrFilter = filter;\n\tGetCurrentDirectory(sizeof(oldworkingdir)-1, oldworkingdir);\n\tmemcpy(workingdir, oldworkingdir, sizeof(workingdir));\n\t_snprintf(absengine, sizeof(absengine), \"%s/%s\", basepath, defaultfile);\n\tfor (s = absengine; *s; s++)\n\t\tif (*s == '/')\n\t\t\t*s = '\\\\';\n\tstrrchr(absengine, '\\\\')[1] = 0;\n\tPathCombine(initialdir, workingdir, absengine);\n\tif (strchr(defaultfile, '/'))\n\t\tstrcpy(absengine, strrchr(defaultfile, '/')+1);\n\telse\n\t\tstrcpy(absengine, defaultfile);\n\t//and the fuck-you-microsoft loop\n\tfor (s = initialdir; *s; s++)\n\t\tif (*s == '/')\n\t\t\t*s = '\\\\';\n\tofn.lpstrInitialDir = initialdir;\n\tokay = GetOpenFileName(&ofn);\n\twhile (!okay)\n\t{\n\t\tswitch(CommDlgExtendedError())\n\t\t{\n\t\tcase FNERR_INVALIDFILENAME:\n\t\t\t*outname = 0;\n\t\t\tokay = GetOpenFileName(&ofn);\n\t\t\tcontinue;\n\t\t}\n\t\tbreak;\n\t}\n\tif (!PathRelativePathToA(outname, initialdir, FILE_ATTRIBUTE_DIRECTORY, absengine, FILE_ATTRIBUTE_DIRECTORY))\n\t\tQC_strlcpy(outname, absengine, sizeof(outsize));\n\tif (!strncmp(outname, \".\\\\\", 2))\n\t\tmemmove(outname, outname+2, strlen(outname+2)+1);\n\t//undo any damage caused by microsoft's stupidity\n\tSetCurrentDirectory(oldworkingdir);\n\treturn okay;\n}\nvoid PromptForEngine(int force)\n{\n\tchar oldworkingdir[MAX_PATH+10];\t//cmdlg changes it...\n\tchar workingdir[MAX_PATH+10];\n\tGetCurrentDirectory(sizeof(oldworkingdir)-1, oldworkingdir);\n\tif (!*enginebasedir || force==1)\n\t{\n\t\tBROWSEINFO bi;\n\t\tLPITEMIDLIST il;\n\t\tmemset(&bi, 0, sizeof(bi));\n\t\tbi.hwndOwner = mainwindow;\n\t\tbi.pidlRoot = NULL;\n\t\tGetCurrentDirectory(sizeof(workingdir)-1, workingdir);\n\t\tbi.pszDisplayName = workingdir;\n\t\tbi.lpszTitle = \"Please locate your base directory\";\n\t\tbi.ulFlags = BIF_RETURNONLYFSDIRS|BIF_STATUSTEXT;\n\t\tbi.lpfn = StupidBrowseCallbackProc;\n\t\tbi.lParam = 0;\n\t\tbi.iImage = 0;\n\t\til = SHBrowseForFolder(&bi);\n\t\tSetCurrentDirectory(oldworkingdir);\t//revert microsoft stupidity.\n\t\tif (il)\n\t\t{\n\t\t\tchar *foo;\n\t\t\tchar absbase[MAX_PATH+10];\n\t\t\tSHGetPathFromIDList(il, absbase);\n\t\t\tCoTaskMemFree(il);\n\t\t\tGetCurrentDirectory(sizeof(workingdir)-1, workingdir);\n\t\t\t//use the relative path instead. this'll be stored in a file, and I expect people will zip+email without thinking.\n\t\t\tif (!PathRelativePathToA(enginebasedir, workingdir, FILE_ATTRIBUTE_DIRECTORY, absbase, FILE_ATTRIBUTE_DIRECTORY))\n\t\t\t\tQC_strlcpy(enginebasedir, absbase, sizeof(enginebasedir));\n\t\t\twhile((foo = strchr(enginebasedir, '\\\\')))\n\t\t\t\t*foo = '/';\n\t\t}\n\t\telse\n\t\t\treturn;\n\n\t\tif (optionsmenu)\n\t\t\tDestroyWindow(optionsmenu);\n\t\tbuttons[ID_OPTIONS].washit = true;\n\t}\n\n\tif (!*enginebinary || force==2)\n\t{\n\t\tif (!PromptForFile(\"Please choose an engine\", \"Executables\\0*.exe\\0All files\\0*.*\\0\", enginebasedir, \"fteglqw.exe\", enginebinary, sizeof(enginebinary), false))\n\t\t\treturn;\n\n\t\tif (optionsmenu)\n\t\t\tDestroyWindow(optionsmenu);\n\t\tbuttons[ID_OPTIONS].washit = true;\n\n\t\tif (*enginebinary && (!*enginecommandline || force==2))\n\t\t{\n\t\t\tchar absbase[MAX_PATH+10];\n\t\t\tchar guessdir[MAX_PATH+10];\n\t\t\tchar *slash;\n\t\t\tGetCurrentDirectory(sizeof(workingdir)-1, workingdir);\n\t\t\t_snprintf(guessdir, sizeof(guessdir), \"%s/\", enginebasedir);\n\t\t\tfor (slash = guessdir; *slash; slash++)\n\t\t\t\tif (*slash == '/')\n\t\t\t\t\t*slash = '\\\\';\n\t\t\tPathCombine(absbase, workingdir, guessdir);\n\t\t\tif (PathRelativePathToA(guessdir, absbase, FILE_ATTRIBUTE_DIRECTORY, workingdir, FILE_ATTRIBUTE_DIRECTORY))\n\t\t\t{\n\t\t\t\tif (!strncmp(guessdir, \".\\\\\", 2))\n\t\t\t\t\tmemmove(guessdir, guessdir+2, strlen(guessdir+2)+1);\n\t\t\t\tslash = strchr(guessdir, '/');\n\t\t\t\tif (slash)\n\t\t\t\t\t*slash = 0;\n\t\t\t\tslash = strchr(guessdir, '\\\\');\n\t\t\t\tif (slash)\n\t\t\t\t\t*slash = 0;\n\t\t\t\tif (!*guessdir)\n\t\t\t\t\tQC_snprintfz(enginecommandline, sizeof(enginecommandline), \"-window -nohome\");\n\t\t\t\telse if (!strchr(guessdir, ' '))\n\t\t\t\t\tQC_snprintfz(enginecommandline, sizeof(enginecommandline), \"-window -nohome -game %s\", guessdir);\n\t\t\t\telse\n\t\t\t\t\tQC_snprintfz(enginecommandline, sizeof(enginecommandline), \"-window -nohome -game \\\"%s\\\"\", guessdir);\n\t\t\t}\n\t\t}\n\n\t}\n}\nvoid RunEngine(void)\n{\n\tsize_t embedtype = 0;\t//0 has focus issues.\n\tif (!gamewindow)\n\t{\n\t\tWNDCLASS wndclass;\n\t\tMDICREATESTRUCT mcs;\n\n\t\tPromptForEngine(0);\n\n\t\tmemset(&wndclass, 0, sizeof(wndclass));\n\t\twndclass.style      = 0;\n\t\twndclass.lpfnWndProc   = EngineWndProc;\n\t\twndclass.cbClsExtra    = 0;\n\t\twndclass.cbWndExtra    = 0;\n\t\twndclass.hInstance     = ghInstance;\n\t\twndclass.hIcon         = 0;\n\t\twndclass.hCursor       = LoadCursor (NULL,IDC_ARROW);\n\t\twndclass.hbrBackground = (void *)COLOR_WINDOW;\n\t\twndclass.lpszMenuName  = 0;\n\t\twndclass.lpszClassName = ENGINE_WINDOW_CLASS_NAME;\n\t\tRegisterClass(&wndclass);\n\n\t\tif (embedtype != 2)\n\t\t{\n\t\t\tgamewindow = CreateWindowA(ENGINE_WINDOW_CLASS_NAME, \"Debug\", WS_OVERLAPPEDWINDOW, 0, 0, 640, 480, NULL, NULL, ghInstance, (void*)embedtype);\n\t\t\tif (embedtype)\n\t\t\t\tShowWindow(gamewindow, SW_SHOW);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tmemset(&mcs, 0, sizeof(mcs));\n\t\t\tmcs.szClass = ENGINE_WINDOW_CLASS_NAME;\n\t\t\tmcs.szTitle = \"Debug\";\n\t\t\tmcs.hOwner = ghInstance;\n\t\t\tmcs.x = CW_USEDEFAULT;\n\t\t\tmcs.y = CW_USEDEFAULT;\n\t\t\tmcs.cx = 640;\n\t\t\tmcs.cy = 480;\n\t\t\tmcs.style = WS_OVERLAPPEDWINDOW;\n\t\t\tmcs.lParam = embedtype;\n\n\t\t\tgamewindow = (HWND) SendMessage (mdibox, WM_MDICREATE, 0, (LONG_PTR) (LPMDICREATESTRUCT) &mcs); \n\t\t}\n\t\tSplitterFocus(watches, 64, 64);\n\t}\n\telse\n\t{\n//\t\tenginewindow_t *e = (enginewindow_t*)(LONG_PTR)GetWindowLongPtr(gamewindow, GWLP_USERDATA);\n\t}\n//\tSendMessage(mdibox, WM_MDIACTIVATE, (WPARAM)gamewindow, 0);\n\tPostMessage(mainwindow, WM_SIZE, 0, 0);\n}\n\nstatic void SetProgsSrcFileAndPath(char *filename)\n{\n\tchar *s, *s2;\n\tstrcpy(progssrcdir, filename);\n\tfor(s = progssrcdir; s; s = s2)\n\t{\n\t\ts2 = strchr(s+1, '\\\\');\n\t\tif (!s2)\n\t\t\tbreak;\n\t\ts = s2;\n\t}\n\tif (s)\n\t{\n\t\t*s = '\\0';\n\t\tstrcpy(progssrcname, s+1);\n\t}\n\telse\n\t\tstrcpy(progssrcname, filename);\n\n\tSetCurrentDirectory(progssrcdir);\n\t*progssrcdir = '\\0';\n}\n\nqcc_cachedsourcefile_t *androidfiles;\nstatic void Android_FreeFiles(void)\n{\n\tqcc_cachedsourcefile_t *f;\n\twhile((f = androidfiles))\n\t{\n\t\tandroidfiles = f->next;\n\t\tfree(f);\n\t}\n}\nstatic void Android_CopyFile(const char *name, const void *compdata, size_t compsize, int method, size_t plainsize)\n{\n\tqcc_cachedsourcefile_t *nf, **link;\n\tif (!strncmp(name, \"META-INF\", 8))\n\t\treturn;\t//ignore any existing signatures.\n\tfor (link = &androidfiles; *link; link = &(*link)->next)\n\t{\n\t\tnf = *link;\n\t\tif (!stricmp(name, nf->filename))\n\t\t{\t//nuke the old file if we have a dupe.\n\t\t\t*link = nf->next;\n\t\t\tfree(nf);\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tnf = malloc(sizeof(*nf) + plainsize);\n\tif (!nf)\n\t{\n\t\tGUIprintf(\"Error: out of memory\\n\", name, plainsize);\n\t\treturn;\n\t}\n\tQC_strlcpy(nf->filename, name, sizeof(nf->filename));\n\tnf->file = (char*)(nf+1);\n\tnf->size = plainsize;\n\tnf->type = FT_DATA;\n\n\tif (QC_decode(NULL, compsize, nf->size, method, compdata, nf->file))\n\t{\n\t\tGUIprintf(\"Android: Including %s (%i bytes)\\n\", name, plainsize);\n\t\tnf->next = androidfiles;\n\t\tandroidfiles = nf;\n\t}\n\telse\n\t{\n\t\tGUIprintf(\"Android: Unable to read %s from source apk\\n\", name, plainsize);\n\t\tfree(nf);\n\t}\n}\nstatic pbool Android_PrepareAPK(FILE *f)\n{\n\tif (f)\n\t{\n\t\tchar *buf;\n\t\tsize_t size;\n\n\t\tfseek(f, 0, SEEK_END);\n\t\tsize = ftell(f);\n\t\tfseek(f, 0, SEEK_SET);\n\t\tbuf = malloc(size);\n\t\tfread(buf, 1, size, f);\n\t\tfclose(f);\n\t\tQC_EnumerateFilesFromBlob(buf, size, Android_CopyFile);\n\t\tfree(buf);\n\t\treturn true;\n\t}\n\treturn false;\n}\nvoid GUI_CreateInstaller_Android(void)\n{\n\tFILE *f;\n\tchar inputapkname[MAX_PATH];\n\t//files\n\tchar *keystore = \"my-release-key.keystore\";\n\tchar targetapk[MAX_PATH];\n\t//other stuff\n\tchar *storepass = \"fte123\";\n\tchar *alias = \"FTEDroid\";\n\tchar tmp[MAX_PATH];\n\tchar *mandata = NULL;\n\tchar *pngdata = NULL;\n\tchar *modname = \"my_application\";\n\tsize_t manlen, pnglen;\n\tint h;\n\tchar cmdline[2048];\n\tFILE *inputapk;\n\n\tif (MessageBox(mainwindow, \"The 'Create Installer' option is still experimental.\\nIt's probably still defective.\\nSo be sure to test stuff extensively.\", \"Create Installer\", MB_OKCANCEL|MB_DEFBUTTON2) != IDOK)\n\t\treturn;\n\n\tif (!mandata)\n\t{\n\t\tQC_snprintfz(tmp, sizeof(tmp), \"default.fmf\");\n\t\tmandata = QCC_ReadFile (tmp, NULL, 0, &manlen);\n\t}\n\tif (!mandata)\n\t{\n\t\tQC_snprintfz(tmp, sizeof(tmp), \"%s.fmf\", modname);\n\t\tmandata = QCC_ReadFile (tmp, NULL, 0, &manlen);\n\t}\n\tif (!mandata)\n\t{\n\t\tQC_snprintfz(tmp, sizeof(tmp), \"../default.fmf\");\n\t\tmandata = QCC_ReadFile (tmp, NULL, 0, &manlen);\n\t}\n\tif (!mandata)\n\t{\n\t\tQC_snprintfz(tmp, sizeof(tmp), \"../%s.fmf\", modname);\n\t\tmandata = QCC_ReadFile (tmp, NULL, 0, &manlen);\n\t}\n\tif (!mandata)\n\t{\n\t\tQC_snprintfz(tmp, sizeof(tmp), \"%s.fmf\", modname);\n\t\tif (PromptForFile(\"Please Select Manifest File\", \"FTE Manifests\\0*.fmf\\0All files\\0*.*\\0\", \".\", tmp, tmp, sizeof(tmp), false))\n\t\t\tmandata = QCC_ReadFile (tmp, NULL, 0, &manlen);\n\t}\n\tif (!mandata)\n\t{\n\t\tMessageBox(mainwindow, \"No manifest selected.\\n\", \"Create Installer\", MB_OK|MB_ICONERROR);\n\t\treturn;\n\t}\n\n\tPathCombine(inputapkname, enginebasedir, \"FTEDroid.apk\");\n\tinputapk = fopen(inputapkname, \"rb\");\n\tif (!inputapk)\n\t{\n\t\tif (PromptForFile(\"Please find base FTE android package\", \"The FTE Android Package\\0*.apk\\0All files\\0*.*\\0\", enginebasedir, \"FTEDroid.apk\", tmp, sizeof(tmp), false))\n\t\t{\n\t\t\tPathCombine(inputapkname, enginebasedir, tmp);\n\t\t\tinputapk = fopen(inputapkname, \"rb\");\n\t\t}\n\t}\n\n\tif (inputapk)\n\t{\n\t\tQC_snprintfz(tmp, sizeof(tmp), \"%s.apk\", modname);\n\t\tif (PromptForFile(\"Please output apk\", \"Android Packages\\0*.apk\\0All files\\0*.*\\0\", \".\", tmp, tmp, sizeof(tmp), true))\n\t\t\tPathCombine(targetapk, enginebasedir, tmp);\n\n\t\tGUIprintf(\"\");\n\n\t\t//read the files from an existing apk\n\t\tif (!Android_PrepareAPK(inputapk))\n\t\t\tMessageBox(mainwindow, \"Unable to read source package\", \"Create Installer\", MB_OK|MB_ICONERROR);\n\t\telse\n\t\t{\n\t\t\t//add/replace some existing files\n\t\t\tpngdata = QCC_ReadFile (\"../droid_72.png\", NULL, 0, &pnglen);\n\t\t\tif (!pngdata)\n\t\t\t\tGUIprintf(\"Could not open ../droid_72.png launcher icon\\n\");\n\t\t\tAndroid_CopyFile(\"res/drawable-hdpi/icon.png\", pngdata, pnglen, 0, pnglen);\n\t\t\tfree(pngdata);\n\n\t\t\tpngdata = QCC_ReadFile (\"../droid_48.png\", NULL, 0, &pnglen);\n\t\t\tif (!pngdata)\n\t\t\t\tGUIprintf(\"Could not open ../droid_48.png launcher icon\\n\");\n\t\t\tAndroid_CopyFile(\"res/drawable-mdpi/icon.png\", NULL, 0, 0, 0);\n\t\t\tfree(pngdata);\n\n\t\t\tAndroid_CopyFile(\"default.fmf\", mandata, manlen, 0, manlen);\n\n\t\t\t//write out the new zip... err... apk... :)\n\t\t\th = SafeOpenWrite (targetapk, 2*1024*1024);\n\t\t\tif (h < 0)\n\t\t\t{\n\t\t\t\tGUIprintf(\"Unable to open %s\\n\", targetapk);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tprogfuncs_t funcs;\n\t\t\t\tprogexterns_t ext;\n\t\t\t\tmemset(&funcs, 0, sizeof(funcs));\n\t\t\t\tfuncs.funcs.parms = &ext;\n\t\t\t\tmemset(&ext, 0, sizeof(ext));\n\t\t\t\text.ReadFile = GUIReadFile;\n\t\t\t\text.FileSize = GUIFileSize;\n\t\t\t\text.WriteFile = QCC_WriteFile;\n\t\t\t\text.Sys_Error = Sys_Error;\n\t\t\t\text.Printf = GUIprintf;\n\t\t\t\tqccprogfuncs = &funcs;\n\t\t\t\tWriteSourceFiles(androidfiles, h, true, false);\n\t\t\t\tif (!SafeClose(h))\n\t\t\t\t\tGUIprintf(\"Error: Unable to write output android package %s\\n\", targetapk);\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tf = fopen(keystore, \"rb\");\n\t\t\t\t\tif (f)\n\t\t\t\t\t\tfclose(f);\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tGUIprintf(\"Key store does not exist. Trying to create\\n\");\n\t\t\t\t\t\t//try to create a keystore for them, as this is their first time.\n\t\t\t\t\t\tQC_snprintfz(cmdline, sizeof(cmdline), \"keytool -genkey -keystore %s -storepass %s -keypass %s -alias %s -keyalg RSA -keysize 2048 -validity 10000\", keystore, storepass, storepass, alias);\n\t\t\t\t\t\tsystem(cmdline);\n\t\t\t\t\t}\n\n\t\t\t\t\tf = fopen(keystore, \"rb\");\n\t\t\t\t\tif (f)\n\t\t\t\t\t{\n\t\t\t\t\t\tfclose(f);\n\t\t\t\t\t\t//we now need to invoke the jarsigner program, so I hope you have java installed.\n\t\t\t\t\t\tQC_snprintfz(cmdline, sizeof(cmdline), \"jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore %s -storepass %s %s %s\", keystore, storepass, targetapk, alias);\n\t\t\t\t\t\tif (EXIT_SUCCESS == system(cmdline))\n\t\t\t\t\t\t\tGUIprintf(\"Android Package Complete. Go ahead and test it!\\n\");\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tGUIprintf(\"Failed to sign package.\\n\");\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\tGUIprintf(\"Keystore creation failed or was aborted.\\n\");\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tAndroid_FreeFiles();\n\n\tfree(mandata);\n}\n\n#ifdef AVAIL_PNGLIB\n//size info that microsoft recommends\nstatic const struct\n{\n\tint width;\n\tint height;\n\tint bpp;\n} icosizes[] = {\n//\t{96, 96, 32},\n\t{48, 48, 32},\n\t{32, 32, 32},\n\t{16, 16, 32},\n//\t{16, 16, 4},\n//\t{48, 48, 4},\n//\t{32, 32, 4},\n//\t{16, 16, 1},\n//\t{48, 48, 1},\n//\t{32, 32, 1},\n\t{256, 256, 32}\t//vista!\n};\n#endif\n\n//dates back to 16bit windows. bah.\n#pragma pack(push)\n#pragma pack(2)\ntypedef struct\n{\n\tWORD idReserved;\n\tWORD idType;\n\tWORD idCount;\n\tstruct\n\t{\n\t\tBYTE  bWidth;\n\t\tBYTE  bHeight;\n\t\tBYTE  bColorCount;\n\t\tBYTE  bReserved;\n\t\tWORD  wPlanes;\n\t\tWORD  wBitCount;\n\t\tDWORD dwBytesInRes;\n\t\tWORD  nId;\n\t} idEntries[256];\n} icon_group_t;\n#pragma pack(pop)\n\n\n#ifdef AVAIL_PNGLIB\nstatic void Image_ResampleTexture (unsigned *in, int inwidth, int inheight, unsigned *out,  int outwidth, int outheight)\n{\n\tint\t\ti, j;\n\tunsigned\t*inrow;\n\tunsigned\tfrac, fracstep;\n\n\t/*if (gl_lerpimages.ival)\n\t{\n\t\tImage_Resample32Lerp(in, inwidth, inheight, out, outwidth, outheight);\n\t\treturn;\n\t}*/\n\n\tfracstep = inwidth*0x10000/outwidth;\n\tfor (i=0 ; i<outheight ; i++, out += outwidth)\n\t{\n\t\tinrow = in + inwidth*(i*inheight/outheight);\n\t\tfrac = outwidth*fracstep;\n\t\tj=outwidth;\n\t\twhile ((j)&3)\n\t\t{\n\t\t\tj--;\n\t\t\tfrac -= fracstep;\n\t\t\tout[j] = inrow[frac>>16];\n\t\t}\n\t\tfor ( ; j>=4 ;)\n\t\t{\n\t\t\tj-=4;\n\t\t\tfrac -= fracstep;\n\t\t\tout[j+3] = inrow[frac>>16];\n\t\t\tfrac -= fracstep;\n\t\t\tout[j+2] = inrow[frac>>16];\n\t\t\tfrac -= fracstep;\n\t\t\tout[j+1] = inrow[frac>>16];\n\t\t\tfrac -= fracstep;\n\t\t\tout[j+0] = inrow[frac>>16];\n\t\t}\n\t}\n}\n#endif\n\n#ifndef MSVCLIBSPATH\n#ifdef MSVCLIBPATH\n\t#define MSVCLIBSPATH STRINGIFY(MSVCLIBPATH)\n#elif _MSC_VER == 1200\n\t#define MSVCLIBSPATH \"../\" \"../libs/vc6-libs/\"\n#else\n\t#define MSVCLIBSPATH \"../\" \"../libs/\"\n#endif\n#endif\n\n#ifdef AVAIL_PNGLIB\n\t#ifndef AVAIL_ZLIB\n\t\t#error PNGLIB requires ZLIB\n\t#endif\n\n\t#undef channels\n\n\t#ifndef PNG_SUCKS_WITH_SETJMP\n\t\t#if defined(MINGW)\n\t\t\t#include \"./mingw-libs/png.h\"\n\t\t#elif defined(_WIN32)\n\t\t\t#include \"../png.h\"\n\t\t#else\n\t\t\t#include <png.h>\n\t\t#endif\n\t#endif\n\n\t#ifdef DYNAMIC_LIBPNG\n\t\t#define PSTATIC(n)\n\t\tstatic dllhandle_t *libpng_handle;\n\t\t#define LIBPNG_LOADED() (libpng_handle != NULL)\n\t#else\n\t\t#define LIBPNG_LOADED() 1\n\t\t#define PSTATIC(n) = &n\n\t\t#ifdef _MSC_VER\n\t\t\t#ifdef _WIN64\n\t\t\t\t#pragma comment(lib, MSVCLIBSPATH \"libpng64.lib\")\n\t\t\t#else\n\t\t\t\t#pragma comment(lib, MSVCLIBSPATH \"libpng.lib\")\n\t\t\t#endif\n\t\t#endif\n\t#endif\n\n#ifndef PNG_NORETURN\n#define PNG_NORETURN\n#endif\n#ifndef PNG_ALLOCATED\n#define PNG_ALLOCATED\n#endif\n\n#if PNG_LIBPNG_VER < 10500\n\t#define png_const_infop png_infop\n\t#define png_const_structp png_structp\n\t#define png_const_bytep png_bytep\n\t#define png_const_unknown_chunkp png_unknown_chunkp\n#endif\n#if PNG_LIBPNG_VER < 10600\n\t#define png_inforp png_infop\n\t#define png_const_inforp png_const_infop\n\t#define png_structrp png_structp\n\t#define png_const_structrp png_const_structp\n#endif\n\nvoid (PNGAPI *qpng_error) PNGARG((png_const_structrp png_ptr, png_const_charp error_message)) PSTATIC(png_error);\nvoid (PNGAPI *qpng_read_end) PNGARG((png_structp png_ptr, png_infop info_ptr)) PSTATIC(png_read_end);\nvoid (PNGAPI *qpng_read_image) PNGARG((png_structp png_ptr, png_bytepp image)) PSTATIC(png_read_image);\npng_byte (PNGAPI *qpng_get_bit_depth) PNGARG((png_const_structp png_ptr, png_const_inforp info_ptr)) PSTATIC(png_get_bit_depth);\npng_byte (PNGAPI *qpng_get_channels) PNGARG((png_const_structp png_ptr, png_const_inforp info_ptr)) PSTATIC(png_get_channels);\n#if PNG_LIBPNG_VER < 10400\n\tpng_uint_32 (PNGAPI *qpng_get_rowbytes) PNGARG((png_const_structp png_ptr, png_const_inforp info_ptr)) PSTATIC(png_get_rowbytes);\n#else\n\tpng_size_t (PNGAPI *qpng_get_rowbytes) PNGARG((png_const_structp png_ptr, png_const_inforp info_ptr)) PSTATIC(png_get_rowbytes);\n#endif\nvoid (PNGAPI *qpng_read_update_info) PNGARG((png_structp png_ptr, png_infop info_ptr)) PSTATIC(png_read_update_info);\nvoid (PNGAPI *qpng_set_strip_16) PNGARG((png_structp png_ptr)) PSTATIC(png_set_strip_16);\nvoid (PNGAPI *qpng_set_expand) PNGARG((png_structp png_ptr)) PSTATIC(png_set_expand);\nvoid (PNGAPI *qpng_set_gray_to_rgb) PNGARG((png_structp png_ptr)) PSTATIC(png_set_gray_to_rgb);\nvoid (PNGAPI *qpng_set_tRNS_to_alpha) PNGARG((png_structp png_ptr)) PSTATIC(png_set_tRNS_to_alpha);\npng_uint_32 (PNGAPI *qpng_get_valid) PNGARG((png_const_structp png_ptr, png_const_infop info_ptr, png_uint_32 flag)) PSTATIC(png_get_valid);\n#if PNG_LIBPNG_VER >= 10400\nvoid (PNGAPI *qpng_set_expand_gray_1_2_4_to_8) PNGARG((png_structp png_ptr)) PSTATIC(png_set_expand_gray_1_2_4_to_8);\n#else\nvoid (PNGAPI *qpng_set_gray_1_2_4_to_8) PNGARG((png_structp png_ptr)) PSTATIC(png_set_gray_1_2_4_to_8);\n#endif\nvoid (PNGAPI *qpng_set_bgr) PNGARG((png_structp png_ptr)) PSTATIC(png_set_bgr);\nvoid (PNGAPI *qpng_set_filler) PNGARG((png_structp png_ptr, png_uint_32 filler, int flags)) PSTATIC(png_set_filler);\nvoid (PNGAPI *qpng_set_palette_to_rgb) PNGARG((png_structp png_ptr)) PSTATIC(png_set_palette_to_rgb);\npng_uint_32 (PNGAPI *qpng_get_IHDR) PNGARG((png_const_structrp png_ptr, png_const_inforp info_ptr, png_uint_32 *width, png_uint_32 *height,\n\t\t\tint *bit_depth, int *color_type, int *interlace_method, int *compression_method, int *filter_method)) PSTATIC(png_get_IHDR);\nvoid (PNGAPI *qpng_read_info) PNGARG((png_structp png_ptr, png_infop info_ptr)) PSTATIC(png_read_info);\nvoid (PNGAPI *qpng_set_sig_bytes) PNGARG((png_structp png_ptr, int num_bytes)) PSTATIC(png_set_sig_bytes);\nvoid (PNGAPI *qpng_set_read_fn) PNGARG((png_structp png_ptr, png_voidp io_ptr, png_rw_ptr read_data_fn)) PSTATIC(png_set_read_fn);\nvoid (PNGAPI *qpng_destroy_read_struct) PNGARG((png_structpp png_ptr_ptr, png_infopp info_ptr_ptr, png_infopp end_info_ptr_ptr)) PSTATIC(png_destroy_read_struct);\npng_infop (PNGAPI *qpng_create_info_struct) PNGARG((png_const_structrp png_ptr)) PSTATIC(png_create_info_struct);\npng_structp (PNGAPI *qpng_create_read_struct) PNGARG((png_const_charp user_png_ver, png_voidp error_ptr, png_error_ptr error_fn, png_error_ptr warn_fn)) PSTATIC(png_create_read_struct);\nint (PNGAPI *qpng_sig_cmp) PNGARG((png_const_bytep sig, png_size_t start, png_size_t num_to_check)) PSTATIC(png_sig_cmp);\n\nvoid (PNGAPI *qpng_write_end) PNGARG((png_structrp png_ptr, png_inforp info_ptr)) PSTATIC(png_write_end);\nvoid (PNGAPI *qpng_write_image) PNGARG((png_structrp png_ptr, png_bytepp image)) PSTATIC(png_write_image);\nvoid (PNGAPI *qpng_write_info) PNGARG((png_structrp png_ptr, png_const_inforp info_ptr)) PSTATIC(png_write_info);\nvoid (PNGAPI *qpng_set_IHDR) PNGARG((png_const_structrp png_ptr, png_infop info_ptr, png_uint_32 width, png_uint_32 height,\n\t\t\tint bit_depth, int color_type, int interlace_method, int compression_method, int filter_method)) PSTATIC(png_set_IHDR);\nvoid (PNGAPI *qpng_set_compression_level) PNGARG((png_structrp png_ptr, int level)) PSTATIC(png_set_compression_level);\nvoid (PNGAPI *qpng_init_io) PNGARG((png_structp png_ptr, png_FILE_p fp)) PSTATIC(png_init_io);\npng_voidp (PNGAPI *qpng_get_io_ptr) PNGARG((png_const_structrp png_ptr)) PSTATIC(png_get_io_ptr);\nvoid (PNGAPI *qpng_destroy_write_struct) PNGARG((png_structpp png_ptr_ptr, png_infopp info_ptr_ptr)) PSTATIC(png_destroy_write_struct);\npng_structp (PNGAPI *qpng_create_write_struct) PNGARG((png_const_charp user_png_ver, png_voidp error_ptr, png_error_ptr error_fn, png_error_ptr warn_fn)) PSTATIC(png_create_write_struct);\nvoid (PNGAPI *qpng_set_unknown_chunks) PNGARG((png_const_structrp png_ptr, png_inforp info_ptr, png_const_unknown_chunkp unknowns, int num_unknowns)) PSTATIC(png_set_unknown_chunks);\n\npng_voidp (PNGAPI *qpng_get_error_ptr) PNGARG((png_const_structrp png_ptr)) PSTATIC(png_get_error_ptr);\n\npbool LibPNG_Init(void)\n{\n#ifdef DYNAMIC_LIBPNG\n\tstatic dllfunction_t pngfuncs[] =\n\t{\n\t\t{(void **) &qpng_error,\t\t\t\t\t\t\t\"png_error\"},\n\t\t{(void **) &qpng_read_end,\t\t\t\t\t\t\"png_read_end\"},\n\t\t{(void **) &qpng_read_image,\t\t\t\t\t\"png_read_image\"},\n\t\t{(void **) &qpng_get_bit_depth,\t\t\t\t\t\"png_get_bit_depth\"},\n\t\t{(void **) &qpng_get_channels,\t\t\t\t\t\"png_get_channels\"},\n\t\t{(void **) &qpng_get_rowbytes,\t\t\t\t\t\"png_get_rowbytes\"},\n\t\t{(void **) &qpng_read_update_info,\t\t\t\t\"png_read_update_info\"},\n\t\t{(void **) &qpng_set_strip_16,\t\t\t\t\t\"png_set_strip_16\"},\n\t\t{(void **) &qpng_set_expand,\t\t\t\t\t\"png_set_expand\"},\n\t\t{(void **) &qpng_set_gray_to_rgb,\t\t\t\t\"png_set_gray_to_rgb\"},\n\t\t{(void **) &qpng_set_tRNS_to_alpha,\t\t\t\t\"png_set_tRNS_to_alpha\"},\n\t\t{(void **) &qpng_get_valid,\t\t\t\t\t\t\"png_get_valid\"},\n#if PNG_LIBPNG_VER > 10400\n\t\t{(void **) &qpng_set_expand_gray_1_2_4_to_8,\t\"png_set_expand_gray_1_2_4_to_8\"},\n#else\n\t\t{(void **) &qpng_set_gray_1_2_4_to_8,\t\"png_set_gray_1_2_4_to_8\"},\n#endif\n\t\t{(void **) &qpng_set_bgr,\t\t\t\t\t\t\"png_set_bgr\"},\n\t\t{(void **) &qpng_set_filler,\t\t\t\t\t\"png_set_filler\"},\n\t\t{(void **) &qpng_set_palette_to_rgb,\t\t\t\"png_set_palette_to_rgb\"},\n\t\t{(void **) &qpng_get_IHDR,\t\t\t\t\t\t\"png_get_IHDR\"},\n\t\t{(void **) &qpng_read_info,\t\t\t\t\t\t\"png_read_info\"},\n\t\t{(void **) &qpng_set_sig_bytes,\t\t\t\t\t\"png_set_sig_bytes\"},\n\t\t{(void **) &qpng_set_read_fn,\t\t\t\t\t\"png_set_read_fn\"},\n\t\t{(void **) &qpng_destroy_read_struct,\t\t\t\"png_destroy_read_struct\"},\n\t\t{(void **) &qpng_create_info_struct,\t\t\t\"png_create_info_struct\"},\n\t\t{(void **) &qpng_create_read_struct,\t\t\t\"png_create_read_struct\"},\n\t\t{(void **) &qpng_sig_cmp,\t\t\t\t\t\t\"png_sig_cmp\"},\n\n\t\t{(void **) &qpng_write_end,\t\t\t\t\t\t\"png_write_end\"},\n\t\t{(void **) &qpng_write_image,\t\t\t\t\t\"png_write_image\"},\n\t\t{(void **) &qpng_write_info,\t\t\t\t\t\"png_write_info\"},\n\t\t{(void **) &qpng_set_IHDR,\t\t\t\t\t\t\"png_set_IHDR\"},\n\t\t{(void **) &qpng_set_compression_level,\t\t\t\"png_set_compression_level\"},\n\t\t{(void **) &qpng_init_io,\t\t\t\t\t\t\"png_init_io\"},\n\t\t{(void **) &qpng_get_io_ptr,\t\t\t\t\t\"png_get_io_ptr\"},\n\t\t{(void **) &qpng_destroy_write_struct,\t\t\t\"png_destroy_write_struct\"},\n\t\t{(void **) &qpng_create_write_struct,\t\t\t\"png_create_write_struct\"},\n\t\t{(void **) &qpng_set_unknown_chunks,\t\t\t\"png_set_unknown_chunks\"},\n\n\t\t{(void **) &qpng_get_error_ptr,\t\t\t\t\t\"png_get_error_ptr\"},\n\t\t{NULL, NULL}\n\t};\n\tstatic qboolean tried;\n\tif (!tried)\n\t{\n\t\ttried = true;\n\n\t\tif (!LIBPNG_LOADED())\n\t\t{\n\t\t\tchar *libnames[] =\n\t\t\t{\n\t\t\t#ifdef _WIN32\n\t\t\t\tva(\"libpng%i\", PNG_LIBPNG_VER_DLLNUM);\n\t\t\t#else\n\t\t\t\t//linux...\n\t\t\t\t//lsb uses 'libpng12.so' specifically, so make sure that works.\n\t\t\t\t\"libpng\" STRINGIFY(PNG_LIBPNG_VER_MAJOR) STRINGIFY(PNG_LIBPNG_VER_MINOR) \".so.\" STRINGIFY(PNG_LIBPNG_VER_SONUM),\n\t\t\t\t\"libpng\" STRINGIFY(PNG_LIBPNG_VER_MAJOR) STRINGIFY(PNG_LIBPNG_VER_MINOR) \".so\",\n\t\t\t\t\"libpng.so.\" STRINGIFY(PNG_LIBPNG_VER_SONUM)\n\t\t\t\t\"libpng.so\",\n\t\t\t#endif\n\t\t\t};\n\t\t\tsize_t i;\n\t\t\tfor (i = 0; i < countof(libnames); i++)\n\t\t\t{\n\t\t\t\tlibpng_handle = Sys_LoadLibrary(libnames[i], pngfuncs);\n\t\t\t\tif (libpng_handle)\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (!libpng_handle)\n\t\t\t\tCon_Printf(\"Unable to load %s\\n\", libnames[0]);\n\t\t}\n\n//\t\tif (!LIBPNG_LOADED())\n//\t\t\tlibpng_handle = Sys_LoadLibrary(\"libpng\", pngfuncs);\n\t}\n#endif\n\treturn LIBPNG_LOADED();\n}\n\ntypedef struct {\n\tchar *data;\n\tint readposition;\n\tint filelen;\n} pngreadinfo_t;\n\nstatic void VARGS readpngdata(png_structp png_ptr,png_bytep data,png_size_t len)\n{\n\tpngreadinfo_t *ri = (pngreadinfo_t*)qpng_get_io_ptr(png_ptr);\n\tif (ri->readposition+len > ri->filelen)\n\t{\n\t\tqpng_error(png_ptr, \"unexpected eof\");\n\t\treturn;\n\t}\n\tmemcpy(data, &ri->data[ri->readposition], len);\n\tri->readposition+=len;\n}\n\nstruct pngerr\n{\n\tconst char *fname;\n\tjmp_buf jbuf;\n};\nstatic void VARGS png_onerror(png_structp png_ptr, png_const_charp error_msg)\n{\n\tstruct pngerr *err = qpng_get_error_ptr(png_ptr);\n//\tCon_Printf(\"libpng %s: %s\\n\", err->fname, error_msg);\n\tlongjmp(err->jbuf, 1);\n\tabort();\n}\n\nstatic void VARGS png_onwarning(png_structp png_ptr, png_const_charp warning_msg)\n{\n\tstruct pngerr *err = qpng_get_error_ptr(png_ptr);\n//\tCon_DPrintf(\"libpng %s: %s\\n\", err->fname, warning_msg);\n}\n\nqbyte *ReadPNGFile(qbyte *buf, int length, int *width, int *height, const char *fname)\n{\n\tqbyte header[8], **rowpointers = NULL, *data = NULL;\n\tpng_structp png;\n\tpng_infop pnginfo;\n\tint y, bitdepth, colortype, interlace, compression, filter, bytesperpixel;\n\tunsigned long rowbytes;\n\tpngreadinfo_t ri;\n\tpng_uint_32 pngwidth, pngheight;\n\tstruct pngerr errctx;\n\n\tif (!LibPNG_Init())\n\t\treturn NULL;\n\n\tmemcpy(header, buf, 8);\n\n\terrctx.fname = fname;\n\tif (setjmp(errctx.jbuf))\n\t{\nerror:\n\t\tif (data)\n\t\t\tfree(data);\n\t\tif (rowpointers)\n\t\t\tfree(rowpointers);\n\t\tqpng_destroy_read_struct(&png, &pnginfo, NULL);\n\t\treturn NULL;\n\t}\n\n\tif (qpng_sig_cmp(header, 0, 8))\n\t{\n\t\treturn NULL;\n\t}\n\n\tif (!(png = qpng_create_read_struct(PNG_LIBPNG_VER_STRING, &errctx, png_onerror, png_onwarning)))\n\t{\n\t\treturn NULL;\n\t}\n\n\tif (!(pnginfo = qpng_create_info_struct(png)))\n\t{\n\t\tqpng_destroy_read_struct(&png, &pnginfo, NULL);\n\t\treturn NULL;\n\t}\n\n\tri.data=buf;\n\tri.readposition=8;\n\tri.filelen=length;\n\tqpng_set_read_fn(png, &ri, readpngdata);\n\n\tqpng_set_sig_bytes(png, 8);\n\tqpng_read_info(png, pnginfo);\n\tqpng_get_IHDR(png, pnginfo, &pngwidth, &pngheight, &bitdepth, &colortype, &interlace, &compression, &filter);\n\n\t*width = pngwidth;\n\t*height = pngheight;\n\n\tif (colortype == PNG_COLOR_TYPE_PALETTE)\n\t{\n\t\tqpng_set_palette_to_rgb(png);\n\t\tqpng_set_filler(png, 255, PNG_FILLER_AFTER);\n\t}\n\n\tif (colortype == PNG_COLOR_TYPE_GRAY && bitdepth < 8)\n\t{\n\t\t#if PNG_LIBPNG_VER > 10400\n\t\t\tqpng_set_expand_gray_1_2_4_to_8(png);\n\t\t#else\n\t\t\tqpng_set_gray_1_2_4_to_8(png);\n\t\t#endif\n\t}\n\n\tif (qpng_get_valid( png, pnginfo, PNG_INFO_tRNS))\n\t\tqpng_set_tRNS_to_alpha(png);\n\n\tif (bitdepth >= 8 && colortype == PNG_COLOR_TYPE_RGB)\n\t\tqpng_set_filler(png, 255, PNG_FILLER_AFTER);\n\n\tif (colortype == PNG_COLOR_TYPE_GRAY || colortype == PNG_COLOR_TYPE_GRAY_ALPHA)\n\t{\n\t\tqpng_set_gray_to_rgb( png );\n\t\tqpng_set_filler(png, 255, PNG_FILLER_AFTER);\n\t}\n\n\tif (bitdepth < 8)\n\t\tqpng_set_expand (png);\n\telse if (bitdepth == 16)\n\t\tqpng_set_strip_16(png);\n\n\n\tqpng_read_update_info(png, pnginfo);\n\trowbytes = qpng_get_rowbytes(png, pnginfo);\n\tbytesperpixel = qpng_get_channels(png, pnginfo);\n\tbitdepth = qpng_get_bit_depth(png, pnginfo);\n\n\tif (bitdepth != 8 || bytesperpixel != 4)\n\t{\n//\t\tCon_Printf (\"Bad PNG color depth and/or bpp (%s)\\n\", fname);\n\t\tqpng_destroy_read_struct(&png, &pnginfo, NULL);\n\t\treturn NULL;\n\t}\n\n\tdata = malloc(*height * rowbytes);\n\trowpointers = malloc(*height * sizeof(*rowpointers));\n\n\tif (!data || !rowpointers)\n\t\tgoto error;\n\n\tfor (y = 0; y < *height; y++)\n\t\trowpointers[y] = data + y * rowbytes;\n\n\tqpng_read_image(png, rowpointers);\n\tqpng_read_end(png, NULL);\n\n\tqpng_destroy_read_struct(&png, &pnginfo, NULL);\n\tfree(rowpointers);\n\treturn data;\n}\n#endif\n\nstatic void GUI_CreateInstaller_Windows(void)\n{\n#define RESLANG MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_UK)\n\tunsigned char *mandata = NULL;\n\tsize_t manlen;\n\tunsigned char *pngdata = NULL;\n\tsize_t pnglen;\n\tchar *error = NULL;\n\tHANDLE bin;\n\tchar ourname[MAX_PATH];\n\tchar *basedir = enginebasedir;\n\tchar newname[MAX_PATH];\n\tchar modname[MAX_PATH+10] = \"unknownmod\";\n\tchar tmpname[MAX_PATH];\n\tchar tmp[MAX_PATH];\n\n\tif (MessageBox(mainwindow, \"The 'Create Installer' option is still experimental.\\nIt's probably still defective.\\nSo be sure to test stuff extensively.\", \"Create Installer\", MB_OKCANCEL|MB_DEFBUTTON2) != IDOK)\n\t\treturn;\n\n\tPromptForEngine(0);\n\n\n\t{\n\t\tchar workingdir[MAX_PATH];\n\t\tchar absbase[MAX_PATH];\n\t\tchar *slash;\n\t\tGetCurrentDirectory(sizeof(workingdir)-1, workingdir);\n\t\t_snprintf(modname, sizeof(modname), \"%s/\", enginebasedir);\n\t\tfor (slash = modname; *slash; slash++)\n\t\t\tif (*slash == '/')\n\t\t\t\t*slash = '\\\\';\n\t\tPathCombine(absbase, workingdir, modname);\n\t\tif (PathRelativePathToA(modname, absbase, FILE_ATTRIBUTE_DIRECTORY, workingdir, FILE_ATTRIBUTE_DIRECTORY))\n\t\t{\n\t\t\tif (!strncmp(modname, \".\\\\\", 2))\n\t\t\t\tmemmove(modname, modname+2, strlen(modname+2)+1);\n\t\t\tslash = strchr(modname, '/');\n\t\t\tif (slash)\n\t\t\t\t*slash = 0;\n\t\t\tslash = strchr(modname, '\\\\');\n\t\t\tif (slash)\n\t\t\t\t*slash = 0;\n\t\t\tif (!*modname)\n\t\t\t\t_snprintf(modname, sizeof(modname), \"unknownmod\");\n\t\t}\n\t}\n\n\tQC_snprintfz(tmp, sizeof(tmp), \"default.fmf\");\n\tmandata = QCC_ReadFile (tmp, NULL, 0, &manlen);\n\tif (!mandata)\n\t{\n\t\tQC_snprintfz(tmp, sizeof(tmp), \"%s.fmf\", modname);\n\t\tmandata = QCC_ReadFile (tmp, NULL, 0, &manlen);\n\t}\n\tif (!mandata)\n\t{\n\t\tQC_snprintfz(tmp, sizeof(tmp), \"../default.fmf\");\n\t\tmandata = QCC_ReadFile (tmp, NULL, 0, &manlen);\n\t}\n\tif (!mandata)\n\t{\n\t\tQC_snprintfz(tmp, sizeof(tmp), \"../%s.fmf\", modname);\n\t\tmandata = QCC_ReadFile (tmp, NULL, 0, &manlen);\n\t}\n\tif (!mandata)\n\t{\n\t\tQC_snprintfz(tmp, sizeof(tmp), \"%s.fmf\", modname);\n\t\tif (!PromptForFile(\"Please Select Manifest File\", \"FTE Manifests\\0*.fmf\\0All files\\0*.*\\0\", \".\", tmp, tmp, sizeof(tmp), true))\n\t\t\tmandata = QCC_ReadFile (tmp, NULL, 0, &manlen);\n\t}\n\t\n\tif (!mandata)\n\t{\n\t\tFILE *f;\n\t\tif (MessageBox(mainwindow, \"Creating an installer requires a manifest.\\nCreate+edit one now?\", \"Create Installer\", MB_OKCANCEL) != IDOK)\n\t\t\treturn;\n\n\t\tf = fopen(tmp, \"wb\");\n\t\tfprintf(f, \"FTEManifestVer 1\\n\");\n\t\tfprintf(f, \"///basic information\\n\");\n\t\tfprintf(f, \"game \\\"quake\\\" ///change this to isolate your mod from quake. if the game is known to the engine itself then other settings will receive default values unless overriden. Should be safe to use as a filename, so should have no spaces or full stops.\\n\");\n\t\tfprintf(f, \"name \\\"Quake\\\" ///this is the full name of your game that you wish the engine to display. Use spaces.\\n\");\n\t\tfprintf(f, \"//protocolname \\\"FTE-Quake\\\" ///allows isolation from other games using the same engine. Should only be changed for standalone total conversions.\\n\");\n\t\tfprintf(f, \"///filesystem\\n\");\n\t\tfprintf(f, \"//basegame \\\"id1\\\"\\n\");\n\t\tfprintf(f, \"//basegame \\\"qw\\\"\\n\");\n\t\tfprintf(f, \"//basegame \\\"*fte\\\" ///* prefix means its never networked\\n\");\n\t\tfprintf(f, \"gamedir \\\"%s\\\"\\n\", modname);\n\t\tfprintf(f, \"//disablehomedir 0\\n\");\n\t\tfprintf(f, \"///required packages. add more as needed. these will be downloaded+installed from the get-go\\n\");\n\t\tfprintf(f, \"///the engine will tell you the correct crc if you get it wrong/don't know it. Its not mandatory, but allows for autoupdates if the fmf changes.\\n\");\n\t\tfprintf(f, \"//package id1/example.pk3\\tmirror \\\"https://example.com/example.pak\\\"\\t//crc 0xdeadbeef\\n\");\n\t\tfprintf(f, \"///updateurl points to an (updated) copy of this manifest file, so you can update basic stuff easily. should always be https\\n\");\n\t\tfprintf(f, \"//updateurl \\\"https://example.com/example.fmf\\\"\\n\");\n\t\tfprintf(f, \"///downloadsurl is a list of optional updates, including engine updates, displayed via the updates menu. should always be https\\n\");\n\t\tfprintf(f, \"//downloadsurl \\\"https://fte.triptohell.info/downloadables.php\\\"\\n\");\n\t\tfprintf(f, \"///eula displayed when first installing\\n\");\n\t\tfprintf(f, \"//eula \\\"By using this game software, you assign your eternal soul to me for me to do as I wish, including but not limited to trading it for a pint of beer. This example is not legally binding.\\\"\\n\");\n\t\tfclose(f);\n\t\tEditFile(tmp, -1, false);\n\t\treturn;\n\t}\n\telse\n\t{\n\n\n\t\t\n\n\t\tQC_snprintfz(newname, sizeof(newname), \"%s_setup.exe\", modname);\n\n\t\tif (!PromptForFile(\"Please Select Output Executable\", \"Executables\\0*.exe\\0All files\\0*.*\\0\", basedir, newname, tmpname, sizeof(tmpname), true))\n\t\t\treturn;\n\n\t\tPathCombine(newname, basedir, tmpname);\n\t\tPathCombine(tmpname, basedir, \"tmp.exe\");\n\t\tPathCombine(ourname, enginebasedir, enginebinary);\n\n\t\tif (!CopyFile(ourname, tmpname, FALSE))\n\t\t\terror = \"output already exists or cannot be written\";\n\n\t\tif (!(bin = BeginUpdateResource(tmpname, FALSE)))\n\t\t\terror = \"BeginUpdateResource failed\";\n\t\telse\n\t\t{\n\t\t\tQC_snprintfz(tmp, sizeof(tmp), \"%s.ico\", modname);\n\t\t\tpngdata = QCC_ReadFile (tmp, NULL, 0, &pnglen);\n#ifdef AVAIL_PNGLIB\n\t\t\tif (!pngdata)\n\t\t\t{\n\t\t\t\tQC_snprintfz(tmp, sizeof(tmp), \"%s.png\", modname);\n\t\t\t\tpngdata = QCC_ReadFile (tmp, NULL, 0, &pnglen);\n\t\t\t}\n\t\t\tif (!pngdata)\n\t\t\t\tpngdata = QCC_ReadFile (\"default.png\", NULL, 0, &pnglen);\n#endif\n\n\t\t\tif (!pngdata)\n\t\t\t\tif (PromptForFile(\"Please Select Icon\", \"Icons\\0*.ico\"\n#ifdef AVAIL_PNGLIB\n\t\t\t\t\t\";*.png\"\n#endif\n\t\t\t\t\t\"\\0All files\\0*.*\\0\", \".\", tmp, tmp, sizeof(tmp), false))\n\t\t\t\t\tpngdata = QCC_ReadFile (tmp, NULL, 0, &pnglen);\n\n\t\t\tif (pngdata && pngdata[0] == 0 && pngdata[1] == 0 && pngdata[2] == 1 && pngdata[3] == 0)\n\t\t\t{\n\t\t\t\tunsigned int iconid = 1, img;\n\t\t\t\tunsigned short images;\n\t\t\t\tstruct {\n\t\t\t\t\tBYTE  bWidth;\n\t\t\t\t\tBYTE  bHeight;\n\t\t\t\t\tBYTE  bColorCount;\n\t\t\t\t\tBYTE  bReserved;\n\t\t\t\t\tWORD  wPlanes;\n\t\t\t\t\tWORD  wBitCount;\n\t\t\t\t\tDWORD dwBytesInRes;\n\t\t\t\t\tDWORD dwOffset;\n\t\t\t\t} *iconinfo;\n\t\t\t\ticon_group_t icondata;\n\t\t\t\tmemset(&icondata, 0, sizeof(icondata));\n\t\t\t\ticondata.idType = 1;\n\n\t\t\t\timages = pngdata[4] | (pngdata[5]<<8);\n\n\t\t\t\tUpdateResource(bin, RT_GROUP_ICON, MAKEINTRESOURCE(1), RESLANG, NULL, 0);\n\t\t\t\tUpdateResource(bin, RT_GROUP_ICON, MAKEINTRESOURCE(2), RESLANG, NULL, 0);\n\n\t\t\t\tfor (iconinfo = (void*)(pngdata+6), img = 0; img < images; iconinfo++, img++)\n\t\t\t\t{\n\t\t\t\t\tif (!error && !UpdateResource(bin, RT_ICON, MAKEINTRESOURCE(iconid), 0, pngdata+iconinfo->dwOffset, iconinfo->dwBytesInRes))\n\t\t\t\t\t\terror = \"UpdateResource failed (icon data)\";\n\n\t\t\t\t\t//and make a copy of it in the icon list\n\t\t\t\t\ticondata.idEntries[icondata.idCount].bWidth = iconinfo->bWidth;\n\t\t\t\t\ticondata.idEntries[icondata.idCount].bHeight = iconinfo->bHeight;\n\t\t\t\t\ticondata.idEntries[icondata.idCount].wBitCount = iconinfo->wBitCount;\n\t\t\t\t\ticondata.idEntries[icondata.idCount].wPlanes = iconinfo->wPlanes;\n\t\t\t\t\ticondata.idEntries[icondata.idCount].bColorCount = iconinfo->bColorCount;\n\t\t\t\t\ticondata.idEntries[icondata.idCount].dwBytesInRes = iconinfo->dwBytesInRes;\n\t\t\t\t\ticondata.idEntries[icondata.idCount].nId = iconid++;\n\t\t\t\t\ticondata.idCount++;\n\t\t\t\t}\n\n\t\t\t\tif (!error && !UpdateResource(bin, RT_GROUP_ICON, MAKEINTRESOURCE(1), RESLANG, &icondata, (pbyte*)&icondata.idEntries[icondata.idCount] - (pbyte*)&icondata))\n\t\t\t\t\terror = \"UpdateResource failed (icon group)\";\n\t\t\t}\n#ifdef AVAIL_PNGLIB\n\t\t\telse if (pngdata)\n\t\t\t{\n\t\t\t\ticon_group_t icondata;\n\t\t\t\tqbyte *rgbadata;\n\t\t\t\tint imgwidth, imgheight;\n\t\t\t\tint iconid = 1;\n\t\t\t\tmemset(&icondata, 0, sizeof(icondata));\n\t\t\t\ticondata.idType = 1;\n\n\t\t\t\tif (MessageBox(mainwindow, error, \"Embedding PNGs is probably buggy/suboptimal. You should consider using an .ico instead.\\nContinue anyway?\", MB_OKCANCEL) != IDOK)\n\t\t\t\t\terror = \"User aborted\";\n\n\t\t\t\tUpdateResource(bin, RT_GROUP_ICON, MAKEINTRESOURCE(1), RESLANG, NULL, 0);\n\t\t\t\tUpdateResource(bin, RT_GROUP_ICON, MAKEINTRESOURCE(2), RESLANG, NULL, 0);\n\t//\t\t\tUpdateResource(bin, RT_GROUP_ICON, MAKEINTRESOURCE(3), RESLANG, NULL, 0);\n\n\t\t\t\trgbadata = ReadPNGFile(pngdata, pnglen, &imgwidth, &imgheight, \"default.png\");\n\t\t\t\tif (!rgbadata)\n\t\t\t\t\terror = \"unable to read icon image\";\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tvoid *data = NULL;\n\t\t\t\t\tunsigned int datalen = 0;\n\t\t\t\t\tunsigned int i;\n\n//\t\t\t\t\textern cvar_t gl_lerpimages;\n//\t\t\t\t\tgl_lerpimages.ival = 1;\n\t\t\t\t\tfor (i = 0; i < sizeof(icosizes)/sizeof(icosizes[0]); i++)\n\t\t\t\t\t{\n\t\t\t\t\t\tunsigned int x,y;\n\t\t\t\t\t\tunsigned int pixels;\n\t\t\t\t\t\tif (icosizes[i].width > imgwidth || icosizes[i].height > imgheight)\n\t\t\t\t\t\t\tcontinue;\t//ignore icons if they're bigger than the original icon.\n\n\t\t\t\t\t\tif (icosizes[i].bpp == 32 && icosizes[i].width >= 128 && icosizes[i].height >= 128 && icosizes[i].width == imgwidth && icosizes[i].height == imgheight)\n\t\t\t\t\t\t{\t//png compression. oh look. we originally loaded a png!\n\t\t\t\t\t\t\tdata = pngdata;\n\t\t\t\t\t\t\tdatalen = pnglen;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t//generate the bitmap info\n\t\t\t\t\t\t\tBITMAPV4HEADER *bi;\n\t\t\t\t\t\t\tqbyte *out, *outmask;\n\t\t\t\t\t\t\tqbyte *in, *inrow;\n\t\t\t\t\t\t\tunsigned int outidx;\n\n\t\t\t\t\t\t\tpixels = icosizes[i].width * icosizes[i].height;\n\n\t\t\t\t\t\t\tbi = data = malloc(sizeof(*bi) + icosizes[i].width * icosizes[i].height * 5 + icosizes[i].height*4);\n\t\t\t\t\t\t\tmemset(bi,0, sizeof(BITMAPINFOHEADER));\n\t\t\t\t\t\t\tbi->bV4Size\t\t\t\t= sizeof(BITMAPINFOHEADER);\n\t\t\t\t\t\t\tbi->bV4Width\t\t\t= icosizes[i].width;\n\t\t\t\t\t\t\tbi->bV4Height\t\t\t= icosizes[i].height * 2;\t//icons are logically double-height, with the second half being a silly alpha mask.\n\t\t\t\t\t\t\tbi->bV4Planes\t\t\t= 1;\n\t\t\t\t\t\t\tbi->bV4BitCount\t\t\t= icosizes[i].bpp;\n\t\t\t\t\t\t\tbi->bV4V4Compression\t= BI_RGB;\n\t\t\t\t\t\t\tbi->bV4ClrUsed\t\t\t= (icosizes[i].bpp>=32?0:(1u<<icosizes[i].bpp));\n\n\t\t\t\t\t\t\tdatalen = bi->bV4Size;\n\t\t\t\t\t\t\tout = (qbyte*)data + datalen;\n\t\t\t\t\t\t\tdatalen += ((icosizes[i].width*icosizes[i].bpp/8+3)&~3) * icosizes[i].height;\n\t\t\t\t\t\t\toutmask = (qbyte*)data + datalen;\n\t\t\t\t\t\t\tdatalen += ((icosizes[i].width+31)&~31)/8 * icosizes[i].height;\n\n\t\t\t\t\t\t\tin = malloc(pixels*4);\n\t\t\t\t\t\t\tImage_ResampleTexture((unsigned int*)rgbadata, imgwidth, imgheight, (unsigned int*)in, icosizes[i].width, icosizes[i].height);\n\n\t\t\t\t\t\t\tinrow = in;\n\t\t\t\t\t\t\toutidx = 0;\n\t\t\t\t\t\t\tif (icosizes[i].bpp == 32)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tfor (y = 0; y < icosizes[i].height; y++)\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tinrow = in + 4*icosizes[i].width*(icosizes[i].height-1-y);\n\t\t\t\t\t\t\t\t\tfor (x = 0; x < icosizes[i].width; x++)\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tif (inrow[3] == 0)\t//transparent\n\t\t\t\t\t\t\t\t\t\t\toutmask[outidx>>3] |= 1u<<(outidx&7);\n\t\t\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\tout[0] = inrow[2];\n\t\t\t\t\t\t\t\t\t\t\tout[1] = inrow[1];\n\t\t\t\t\t\t\t\t\t\t\tout[2] = inrow[0];\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\tout += 4;\n\t\t\t\t\t\t\t\t\t\toutidx++;\n\t\t\t\t\t\t\t\t\t\tinrow += 4;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tif (x & 3)\n\t\t\t\t\t\t\t\t\t\tout += 4 - (x&3);\n\t\t\t\t\t\t\t\t\toutidx = (outidx + 31)&~31;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (!error && !UpdateResource(bin, RT_ICON, MAKEINTRESOURCE(iconid), 0, data, datalen))\n\t\t\t\t\t\t\terror = \"UpdateResource failed (icon data)\";\n\n\t\t\t\t\t\t//and make a copy of it in the icon list\n\t\t\t\t\t\ticondata.idEntries[icondata.idCount].bWidth = (icosizes[i].width<256)?icosizes[i].width:0;\n\t\t\t\t\t\ticondata.idEntries[icondata.idCount].bHeight = (icosizes[i].height<256)?icosizes[i].height:0;\n\t\t\t\t\t\ticondata.idEntries[icondata.idCount].wBitCount = icosizes[i].bpp;\n\t\t\t\t\t\ticondata.idEntries[icondata.idCount].wPlanes = 1;\n\t\t\t\t\t\ticondata.idEntries[icondata.idCount].bColorCount = (icosizes[i].bpp>=8)?0:(1u<<icosizes[i].bpp);\n\t\t\t\t\t\ticondata.idEntries[icondata.idCount].dwBytesInRes = datalen;\n\t\t\t\t\t\ticondata.idEntries[icondata.idCount].nId = iconid++;\n\t\t\t\t\t\ticondata.idCount++;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (!error && !UpdateResource(bin, RT_GROUP_ICON, MAKEINTRESOURCE(1), RESLANG, &icondata, (qbyte*)&icondata.idEntries[icondata.idCount] - (qbyte*)&icondata))\n\t\t\t\t\terror = \"UpdateResource failed (icon group)\";\n\t\t\t}\n#endif\n\t\t\telse\n\t\t\t\terror = \"icon format not supported\";\n\n\n\t\t\tif (mandata)\n\t\t\t{\n\t\t\t\tif (!error && !UpdateResource(bin, RT_RCDATA, MAKEINTRESOURCE(1), 0, mandata, manlen+1))\n\t\t\t\t\terror = \"UpdateResource failed (manicfest)\";\n\t\t\t}\n\t\t\telse\n\t\t\t\terror = \"fmf not found in working directory\";\n\n\t\t\tif (!EndUpdateResource(bin, !!error) && !error)\n\t\t\t\terror = \"EndUpdateResource failed. Check access permissions.\";\n\n\t\t\tDeleteFile(newname);\n\t\t\tMoveFile(tmpname, newname);\n\t\t}\n\t}\n\n\tfree(pngdata);\n\tfree(mandata);\n\n\tif (error)\n\t\tMessageBox(mainwindow, error, \"Create Installer Error\", MB_ICONERROR);\n\telse\n\t\tMessageBox(mainwindow, \"Installer Created!\\nNow go and upload it somewhere!\\n\\nBe sure to flush windows' icon cache if you changed the icon.\", \"Create Installer\", MB_OK);\n}\n\nHWND targitem_hexen2;\nHWND targitem_fte;\nHWND nokeywords_coexistitem;\nHWND autoprototype_item;\n//HWND autohighlight_item;\nHWND extraparmsitem;\n#ifdef EMBEDDEBUG\nHWND w_enginebinary;\nHWND w_enginebasedir;\nHWND w_enginecommandline;\n#endif\nstatic LRESULT CALLBACK OptionsWndProc(HWND hWnd,UINT message,\n\t\t\t\t     WPARAM wParam,LPARAM lParam)\n{\n\tint i;\n\tswitch (message)\n\t{\n\tcase WM_DESTROY:\n\t\toptionsmenu = NULL;\n\t\tbreak;\n\n\tcase WM_COMMAND:\n\t\tswitch(wParam)\n\t\t{\n\t\tcase IDI_O_APPLYSAVE:\n\t\tcase IDI_O_APPLY:\n\t\t\tfor (i = 0; optimisations[i].enabled; i++)\n\t\t\t{\n\t\t\t\tif (optimisations[i].flags & FLAG_HIDDENINGUI)\n\t\t\t\t\tcontinue;\n\n\t\t\t\tif (Button_GetCheck(optimisations[i].guiinfo))\n\t\t\t\t\toptimisations[i].flags |= FLAG_SETINGUI;\n\t\t\t\telse\n\t\t\t\t\toptimisations[i].flags &= ~FLAG_SETINGUI;\n\t\t\t}\n\t\t\tfl_hexen2 = Button_GetCheck(targitem_hexen2);\n\t\t\tfl_ftetarg = Button_GetCheck(targitem_fte);\n\t\t\tfor (i = 0; compiler_flag[i].enabled; i++)\n\t\t\t{\n\t\t\t\tif (compiler_flag[i].flags & FLAG_HIDDENINGUI)\n\t\t\t\t\tcontinue;\n\t\t\t\tif (Button_GetCheck(compiler_flag[i].guiinfo))\n\t\t\t\t\tcompiler_flag[i].flags |= FLAG_SETINGUI;\n\t\t\t\telse\n\t\t\t\t\tcompiler_flag[i].flags &= ~FLAG_SETINGUI;\n\t\t\t}\n\t\t\tEdit_GetText(extraparmsitem, parameters, sizeof(parameters)-1);\n#ifdef EMBEDDEBUG\n\t\t\tEdit_GetText(w_enginebinary, enginebinary, sizeof(enginebinary)-1);\n\t\t\tEdit_GetText(w_enginebasedir, enginebasedir, sizeof(enginebasedir)-1);\n\t\t\tEdit_GetText(w_enginecommandline, enginecommandline, sizeof(enginecommandline)-1);\n#endif\n\n\t\t\tif (wParam == IDI_O_APPLYSAVE)\n\t\t\t\tGUI_SaveConfig();\n\t\t\tDestroyWindow(hWnd);\n\t\t\tbreak;\n/*\t\tcase IDI_O_CHANGE_PROGS_SRC:\n\t\t\t{\n\t\t\t\tchar filename[MAX_PATH];\n\t\t\t\tchar oldpath[MAX_PATH+10];\n\t\t\t\tOPENFILENAME ofn;\n\t\t\t\tmemset(&ofn, 0, sizeof(ofn));\n\t\t\t\tofn.lStructSize = sizeof(ofn);\n\t\t\t\tofn.hInstance = ghInstance;\n\t\t\t\tofn.lpstrFile = filename;\n\t\t\t\tofn.lpstrTitle = \"Please find progs.src\";\n\t\t\t\tofn.nMaxFile = sizeof(filename)-1;\n\t\t\t\tofn.lpstrFilter = \"QuakeC source\\0*.src\\0All files\\0*.*\\0\";\n\t\t\t\tmemset(filename, 0, sizeof(filename));\n\t\t\t\tGetCurrentDirectory(sizeof(oldpath)-1, oldpath);\n\t\t\t\tofn.lpstrInitialDir = oldpath;\n\t\t\t\tif (GetOpenFileName(&ofn))\n\t\t\t\t{\n\t\t\t\t\tSetProgsSrcFileAndPath(filename);\n\t\t\t\t\tresetprogssrc = true;\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;*/\n\t\tcase IDI_O_LEVEL0:\n\t\tcase IDI_O_LEVEL1:\n\t\tcase IDI_O_LEVEL2:\n\t\tcase IDI_O_LEVEL3:\n\t\t\tfor (i = 0; optimisations[i].enabled; i++)\n\t\t\t{\n\t\t\t\tif (optimisations[i].flags & FLAG_HIDDENINGUI)\n\t\t\t\t\tcontinue;\n\n\t\t\t\tif (optimisations[i].optimisationlevel<=(int)wParam-IDI_O_LEVEL0)\n\t\t\t\t\tButton_SetCheck(optimisations[i].guiinfo, 1);\n\t\t\t\telse\n\t\t\t\t\tButton_SetCheck(optimisations[i].guiinfo, 0);\n\t\t\t}\n\t\t\tif (!fl_nondfltopts)\n\t\t\t{\n\t\t\t\tfor (i = 0; optimisations[i].enabled; i++)\n\t\t\t\t{\n\t\t\t\t\tif (optimisations[i].guiinfo)\n\t\t\t\t\t\tEnableWindow(optimisations[i].guiinfo, TRUE);\n\t\t\t\t}\n\t\t\t\tfl_nondfltopts = true;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase IDI_O_DEBUG:\n\t\t\tfor (i = 0; optimisations[i].enabled; i++)\n\t\t\t{\n\t\t\t\tif (optimisations[i].flags & FLAG_HIDDENINGUI)\n\t\t\t\t\tcontinue;\n\n\t\t\t\tif (optimisations[i].flags&FLAG_KILLSDEBUGGERS)\n\t\t\t\t\tButton_SetCheck(optimisations[i].guiinfo, 0);\n\t\t\t}\n\t\t\tif (!fl_nondfltopts)\n\t\t\t{\n\t\t\t\tfor (i = 0; optimisations[i].enabled; i++)\n\t\t\t\t{\n\t\t\t\t\tif (optimisations[i].guiinfo)\n\t\t\t\t\t\tEnableWindow(optimisations[i].guiinfo, TRUE);\n\t\t\t\t}\n\t\t\t\tfl_nondfltopts = true;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase IDI_O_DEFAULT:\n\t\t\tfor (i = 0; optimisations[i].enabled; i++)\n\t\t\t{\n\t\t\t\tif (optimisations[i].flags & FLAG_HIDDENINGUI)\n\t\t\t\t\tcontinue;\n\n\t\t\t\tif (optimisations[i].flags & FLAG_ASDEFAULT)\n\t\t\t\t\tButton_SetCheck(optimisations[i].guiinfo, 1);\n\t\t\t\telse\n\t\t\t\t\tButton_SetCheck(optimisations[i].guiinfo, 0);\n\t\t\t}\n\t\t\tif (fl_nondfltopts)\n\t\t\t{\n\t\t\t\tfor (i = 0; optimisations[i].enabled; i++)\n\t\t\t\t{\n\t\t\t\t\tif (optimisations[i].guiinfo)\n\t\t\t\t\t\tEnableWindow(optimisations[i].guiinfo, FALSE);\n\t\t\t\t}\n\t\t\t\tfl_nondfltopts = false;\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t\tbreak;\n\tcase WM_HELP:\n\t\t{\n\t\t\tHELPINFO *hi;\n\t\t\thi = (HELPINFO *)lParam;\n\t\t\tswitch(hi->iCtrlId) \n\t\t\t{\n\t\t\tcase IDI_O_DEFAULT:\n\t\t\t\tMessageBox(hWnd, \"Sets the default optimisations\", \"Help\", MB_OK|MB_ICONINFORMATION);\n\t\t\t\tbreak;\n\t\t\tcase IDI_O_DEBUG:\n\t\t\t\tMessageBox(hWnd, \"Clears all optimisations which can make your progs harder to debug\", \"Help\", MB_OK|MB_ICONINFORMATION);\n\t\t\t\tbreak;\n\t\t\tcase IDI_O_LEVEL0:\n\t\t\tcase IDI_O_LEVEL1:\n\t\t\tcase IDI_O_LEVEL2:\n\t\t\tcase IDI_O_LEVEL3:\n\t\t\t\tMessageBox(hWnd, \"Sets a specific optimisation level\", \"Help\", MB_OK|MB_ICONINFORMATION);\n\t\t\t\tbreak;\n//\t\t\tcase IDI_O_CHANGE_PROGS_SRC:\n//\t\t\t\tMessageBox(hWnd, \"Use this button to change your root source file.\\nNote that fteqcc compiles sourcefiles from editors first, rather than saving. This means that changes are saved ONLY when you save them, but means that switching project mid-compile can result in problems.\", \"Help\", MB_OK|MB_ICONINFORMATION);\n//\t\t\t\tbreak;\n\t\t\tcase IDI_O_ADDITIONALPARAMETERS:\n\t\t\t\tMessageBox(hWnd, \"Type in additional commandline parameters here. Use -Dname to define a named precompiler constant before compiling.\", \"Help\", MB_OK|MB_ICONINFORMATION);\n\t\t\t\tbreak;\n\t\t\tcase IDI_O_APPLY:\n\t\t\t\tMessageBox(hWnd, \"Apply changes shown.\", \"Help\", MB_OK|MB_ICONINFORMATION);\n\t\t\t\tbreak;\n\t\t\tcase IDI_O_APPLYSAVE:\n\t\t\t\tMessageBox(hWnd, \"Apply changes shown and save the settings for next time.\", \"Help\", MB_OK|MB_ICONINFORMATION);\n\t\t\t\tbreak;\n\t\t\tcase IDI_O_OPTIMISATION:\n\t\t\t\tfor (i = 0; optimisations[i].enabled; i++)\n\t\t\t\t{\n\t\t\t\t\tif (optimisations[i].guiinfo == hi->hItemHandle)\n\t\t\t\t\t{\n\t\t\t\t\t\tMessageBox(hWnd, optimisations[i].description, \"Help\", MB_OK|MB_ICONINFORMATION);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase IDI_O_COMPILER_FLAG:\n\t\t\t\tfor (i = 0; compiler_flag[i].enabled; i++)\n\t\t\t\t{\n\t\t\t\t\tif (compiler_flag[i].guiinfo == hi->hItemHandle)\n\t\t\t\t\t{\n\t\t\t\t\t\tMessageBox(hWnd, compiler_flag[i].description, \"Help\", MB_OK|MB_ICONINFORMATION);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase IDI_O_TARGETH2:\n\t\t\t\tMessageBox(hWnd, \"Click here to compile a hexen2 compatible progs, as well as enable all hexen2 keywords. Note that this uses the -Thexen2. There are other targets available.\", \"Help\", MB_OK|MB_ICONINFORMATION);\n\t\t\t\tbreak;\n\t\t\tcase IDI_O_TARGETFTE:\n\t\t\t\tMessageBox(hWnd, \"Click here to allow the use of extended instructions not found in the original instruction set.\", \"Help\", MB_OK|MB_ICONINFORMATION);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tbreak;\n\tdefault:\n\t\treturn DefWindowProc(hWnd,message,wParam,lParam);\n\t}\n\treturn 0;\n}\nstatic void AddTip(HWND tipwnd, HWND tool, char *message)\n{\n\tTOOLINFO toolInfo = { 0 };\n\ttoolInfo.cbSize = sizeof(toolInfo);\n\ttoolInfo.hwnd = tool;\n\ttoolInfo.uFlags = TTF_IDISHWND | TTF_SUBCLASS;\n\ttoolInfo.uId = (UINT_PTR)tool;\n\ttoolInfo.lpszText = message;\n\tSendMessage(tipwnd, TTM_ADDTOOL, 0, (LPARAM)&toolInfo);\n}\nvoid OptionsDialog(void)\n{\n\tchar nicername[256], *us;\n\tHWND subsection;\n\tRECT r;\n\tWNDCLASS wndclass;\n\tHWND wnd, tipwnd;\n\tint i;\n\tint flagcolums=1;\n\n\tint x;\n\tint y;\n\tint my;\n\tint lheight;\n\tint rheight;\n\tint num;\n\tint cflagsshown;\n\n\tif (optionsmenu)\n\t{\n\t\tBringWindowToTop(optionsmenu);\n\t\treturn;\n\t}\n\n\n\tmemset(&wndclass, 0, sizeof(wndclass));\n\twndclass.style      = 0;\n\twndclass.lpfnWndProc   = OptionsWndProc;\n\twndclass.cbClsExtra    = 0;\n\twndclass.cbWndExtra    = 0;\n\twndclass.hInstance     = ghInstance;\n\twndclass.hIcon         = LoadIcon(ghInstance, IDI_ICON_FTEQCC);\n\twndclass.hCursor       = LoadCursor (NULL,IDC_ARROW);\n\twndclass.hbrBackground = (void *)COLOR_WINDOW;\n\twndclass.lpszMenuName  = 0;\n\twndclass.lpszClassName = OPTIONS_WINDOW_CLASS_NAME;\n\tRegisterClass(&wndclass);\n\n\tlheight = 0;\n\tfor (i = 0; optimisations[i].enabled; i++)\n\t{\n\t\tif (optimisations[i].flags & FLAG_HIDDENINGUI)\n\t\t\tcontinue;\n\n\t\tlheight++;\n\t}\n\tlheight = (lheight+1)/2;\t//double columns for optimisations\n\tlheight *= 16;\n\tlheight += 112;\n\tlheight += 88;\n\n\tcflagsshown = 0;\n\tcflagsshown += 2; //hexenc, extended opcodes\n\tfor (i = 0; compiler_flag[i].enabled; i++)\n\t{\n\t\tif (compiler_flag[i].flags & FLAG_HIDDENINGUI)\n\t\t\tcontinue;\n\n\t\tcflagsshown++;\n\t}\n\n\tdo\n\t{\n\t\tflagcolums++;\n\t\tcflagsshown += flagcolums-1;\t//round up\n\t\trheight = (cflagsshown/flagcolums)*16;\n\n\t\trheight += 16+4+20;\t//extra parms cap,gap,parmsbox(min)\n\t}while (rheight > lheight*flagcolums);\n\n\tr.right = 408 + flagcolums*168;\n\tif (r.right < 640)\n\t\tr.right = 640;\n\n\tr.left = GetSystemMetrics(SM_CXSCREEN)/2-320;\n\tr.top = GetSystemMetrics(SM_CYSCREEN)/2-240;\n\tif (rheight > lheight)\n\t\tr.bottom = r.top + rheight;\n\telse\n\t{\n\t\tr.bottom = r.top + lheight;\n\t\trheight = lheight;\n\t}\n\tr.right  += r.left;\n\n\n\n\tAdjustWindowRectEx (&r, WS_CAPTION|WS_SYSMENU, FALSE, 0);\n\n\toptionsmenu=CreateWindowEx(WS_EX_CONTEXTHELP, OPTIONS_WINDOW_CLASS_NAME, \"Options - FTE QuakeC compiler\", WS_CAPTION|WS_SYSMENU,\n\t\tr.left, r.top, r.right-r.left, r.bottom-r.top, NULL, NULL, ghInstance, NULL);\n\n\ttipwnd = CreateWindow(TOOLTIPS_CLASS, NULL, WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP,\n\t\tCW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, optionsmenu, NULL, ghInstance, NULL);\n\tSetWindowPos(tipwnd, HWND_TOPMOST,0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);\n\tSendMessage(tipwnd, TTM_SETMAXTIPWIDTH, 0, 500);\n\n\tsubsection = CreateWindow(\"BUTTON\", \"Optimisations\", WS_CHILD|WS_VISIBLE|BS_GROUPBOX,\n\t\t0, 0, 400, lheight-40*4+24, optionsmenu, NULL, ghInstance, NULL);\n\n\tnum = 0;\n\tfor (i = 0; optimisations[i].enabled; i++)\n\t{\n\t\tif (optimisations[i].flags & FLAG_HIDDENINGUI)\n\t\t{\n\t\t\toptimisations[i].guiinfo = NULL;\n\t\t\tcontinue;\n\t\t}\n\n\t\tQC_strlcpy(nicername, optimisations[i].fullname, sizeof(nicername));\n\t\twhile((us = strchr(nicername, '_')))\n\t\t\t*us = ' ';\n\n\t\toptimisations[i].guiinfo = wnd = CreateWindow(\"BUTTON\",nicername,\n\t\t\t   WS_CHILD | WS_VISIBLE | BS_AUTOCHECKBOX,\n\t\t\t   8+200*(num&1),16+16*(num/2),200-16,16,\n\t\t\t   subsection,\n\t\t\t   (HMENU)IDI_O_OPTIMISATION,\n\t\t\t   ghInstance,\n\t\t\t   NULL);\n\n\t\tif (optimisations[i].flags&FLAG_SETINGUI)\n\t\t\tButton_SetCheck(wnd, 1);\n\t\telse\n\t\t\tButton_SetCheck(wnd, 0);\n\n\t\tif (!fl_nondfltopts)\n\t\t\tEnableWindow(wnd, FALSE);\n\n\t\tAddTip(tipwnd, wnd,\toptimisations[i].description);\n\n\t\tnum++;\n\t}\n\n\twnd = CreateWindow(\"BUTTON\",\"O0\",\n\t\t   WS_CHILD | WS_VISIBLE,\n\t\t   8,lheight-40*5+24,64,32,\n\t\t   optionsmenu,\n\t\t   (HMENU)IDI_O_LEVEL0,\n\t\t   ghInstance,\n\t\t   NULL);\n\tAddTip(tipwnd, wnd,\t\"Disable optimisations completely, giving code more similar to vanilla.\");\n\twnd = CreateWindow(\"BUTTON\",\"O1\",\n\t\t   WS_CHILD | WS_VISIBLE,\n\t\t   8+64,lheight-40*5+24,64,32,\n\t\t   optionsmenu,\n\t\t   (HMENU)IDI_O_LEVEL1,\n\t\t   ghInstance,\n\t\t   NULL);\n\tAddTip(tipwnd, wnd,\t\"Enable simple optimisations (primarily size). Probably still breaks decompilers.\");\n\twnd = CreateWindow(\"BUTTON\",\"O2\",\n\t\t   WS_CHILD | WS_VISIBLE,\n\t\t   8+64*2,lheight-40*5+24,64,32,\n\t\t   optionsmenu,\n\t\t   (HMENU)IDI_O_LEVEL2,\n\t\t   ghInstance,\n\t\t   NULL);\n\tAddTip(tipwnd, wnd,\t\"Enable most optimisations. Does not optimise anything that is likely to break any engines.\");\n\twnd = CreateWindow(\"BUTTON\",\"O3\",\n\t\t   WS_CHILD | WS_VISIBLE,\n\t\t   8+64*3,lheight-40*5+24,64,32,\n\t\t   optionsmenu,\n\t\t   (HMENU)IDI_O_LEVEL3,\n\t\t   ghInstance,\n\t\t   NULL);\n\tAddTip(tipwnd, wnd,\t\"Enable unsafe optimisations. The extra optimisations may cause the progs to fail in certain cases, especially if used to compile addon modules.\");\n\twnd = CreateWindow(\"BUTTON\",\"Debug\",\n\t\t   WS_CHILD | WS_VISIBLE,\n\t\t   8+64*4,lheight-40*5+24,64,32,\n\t\t   optionsmenu,\n\t\t   (HMENU)IDI_O_DEBUG,\n\t\t   ghInstance,\n\t\t   NULL);\n\tAddTip(tipwnd, wnd,\t\"Disable any optimisations that might interfere with debugging somehow.\");\n\twnd = CreateWindow(\"BUTTON\",\"Default\",\n\t\t   WS_CHILD | WS_VISIBLE,\n\t\t   8+64*5,lheight-40*5+24,64,32,\n\t\t   optionsmenu,\n\t\t   (HMENU)IDI_O_DEFAULT,\n\t\t   ghInstance,\n\t\t   NULL);\n\tAddTip(tipwnd, wnd,\t\"Default optimsations are aimed at increasing capacity without breaking debuggers or common decompilers (although gotos, switches, arrays, etc, will still result in issues).\");\n\n#ifdef EMBEDDEBUG\n\tw_enginebinary = CreateWindowEx(WS_EX_CLIENTEDGE,\n\t\t\"EDIT\",\n\t\tenginebinary,\n\t\tWS_CHILD /*| ES_READONLY*/ | WS_VISIBLE | ES_LEFT | ES_AUTOHSCROLL,\n\t\t8, lheight-40-30*3, 400-16, 22,\n\t\toptionsmenu,\n\t\t(HMENU)IDI_O_ENGINE,\n\t\tghInstance,\n\t\tNULL);\n\tw_enginebasedir = CreateWindowEx(WS_EX_CLIENTEDGE,\n\t\t\"EDIT\",\n\t\tenginebasedir,\n\t\tWS_CHILD /*| ES_READONLY*/ | WS_VISIBLE | ES_LEFT | ES_AUTOHSCROLL,\n\t\t8, lheight-40-30*2, 400-16, 22,\n\t\toptionsmenu,\n\t\t(HMENU)IDI_O_ENGINEBASEDIR,\n\t\tghInstance,\n\t\tNULL);\n\tw_enginecommandline = CreateWindowEx(WS_EX_CLIENTEDGE,\n\t\t\"EDIT\",\n\t\tenginecommandline,\n\t\tWS_CHILD /*| ES_READONLY*/ | WS_VISIBLE | ES_LEFT | ES_AUTOHSCROLL,\n\t\t8, lheight-40-30, 400-16, 22,\n\t\toptionsmenu,\n\t\t(HMENU)IDI_O_ENGINECOMMANDLINE,\n\t\tghInstance,\n\t\tNULL);\n\n\tAddTip(tipwnd, w_enginebinary,\t\t\"This is the engine that you wish to debug with.\\nCurrently only FTEQW supports actual debugging, while specifying other engines here merely provides you with a quick way to start them up\");\n\tAddTip(tipwnd, w_enginebasedir,\t\t\"This is your base directory (typically the directory your engine executable is in)\");\n\tAddTip(tipwnd, w_enginecommandline,\t\"This is the commandline to use to invoke your mod.\\nYou'll likely want -game here.\\n-window is also handy.\\n-nohome can be used to inhibit the use of home directories.\\nYou may also want to add '+map start' or some such.\");\n#endif\n\n\twnd = CreateWindow(\"BUTTON\",\"Apply\",\n\t\t   WS_CHILD | WS_VISIBLE,\n\t\t   8,lheight-40,64,32,\n\t\t   optionsmenu,\n\t\t   (HMENU)IDI_O_APPLY,\n\t\t   ghInstance,\n\t\t   NULL);\n\tAddTip(tipwnd, wnd,\t\t\"Use selected settings without saving them to disk.\");\n\twnd = CreateWindow(\"BUTTON\",\"Save\",\n\t\t   WS_CHILD | WS_VISIBLE,\n\t\t   8+64,lheight-40,64,32,\n\t\t   optionsmenu,\n\t\t   (HMENU)IDI_O_APPLYSAVE,\n\t\t   ghInstance,\n\t\t   NULL);\n\tAddTip(tipwnd, wnd,\t\t\"Use selected settings and save them to disk so that they're also used the next time you start fteqccgui.\");\n\t/*wnd = CreateWindow(\"BUTTON\",\"progs.src\",\n\t\t   WS_CHILD | WS_VISIBLE,\n\t\t   8+64*2,lheight-40,64,32,\n\t\t   optionsmenu,\n\t\t   (HMENU)IDI_O_CHANGE_PROGS_SRC,\n\t\t   ghInstance,\n\t\t   NULL);\n\tAddTip(tipwnd, wnd,\t\t\"Change the initial src file.\");*/\n\n\n\n\t\ty=4;\n\ttargitem_hexen2 = wnd = CreateWindow(\"BUTTON\",\"HexenC\",\n\t\t   WS_CHILD | WS_VISIBLE | BS_AUTOCHECKBOX,\n\t\t   408,y,200-16,16,\n\t\t   optionsmenu,\n\t\t   (HMENU)IDI_O_TARGETH2,\n\t\t   ghInstance,\n\t\t   NULL);\n\ty+=16;\n\tif (fl_hexen2)\n\t\tButton_SetCheck(wnd, 1);\n\telse\n\t\tButton_SetCheck(wnd, 0);\n\tAddTip(tipwnd, wnd,\t\"Compile for hexen2.\\nThis changes the opcodes slightly, the progs crc, and enables some additional keywords.\");\n\n\ttargitem_fte = wnd = CreateWindow(\"BUTTON\",\"Extended Instructions\",\n\t\t   WS_CHILD | WS_VISIBLE | BS_AUTOCHECKBOX,\n\t\t   408,y,200-16,16,\n\t\t   optionsmenu,\n\t\t   (HMENU)IDI_O_TARGETFTE,\n\t\t   ghInstance,\n\t\t   NULL);\n\ty+=16;\n\tif (fl_ftetarg)\n\t\tButton_SetCheck(wnd, 1);\n\telse\n\t\tButton_SetCheck(wnd, 0);\n\tAddTip(tipwnd, wnd,\t\"Enables the use of additional opcodes, which only FTE supports at this time.\\nThis gives both smaller and faster code, as well as allowing pointers, ints, and other extensions not possible with the vanilla QCVM.\");\n\n/*\tautohighlight_item = wnd = CreateWindow(\"BUTTON\",\"Syntax Highlighting\",\n\t\t   WS_CHILD | WS_VISIBLE | BS_AUTOCHECKBOX,\n\t\t   408,y,200-16,16,\n\t\t   optionsmenu,\n\t\t   (HMENU)IDI_O_SYNTAX_HIGHLIGHTING,\n\t\t   ghInstance,\n\t\t   NULL);\n\ty+=16;\n\tif (fl_autohighlight)\n\t\tButton_SetCheck(wnd, 1);\n\telse\n\t\tButton_SetCheck(wnd, 0);\n*/\n\tx = 408;\n\tmy = y;\n\tfor (i = 0; compiler_flag[i].enabled; i++)\n\t{\n\t\tif (compiler_flag[i].flags & FLAG_HIDDENINGUI)\n\t\t{\n\t\t\tcompiler_flag[i].guiinfo = NULL;\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (y > (cflagsshown/flagcolums)*16)\n\t\t{\n\t\t\ty = 4;\n\t\t\tx += 168;\n\t\t}\n\n\t\tcompiler_flag[i].guiinfo = wnd = CreateWindow(\"BUTTON\",compiler_flag[i].fullname,\n\t\t\t   WS_CHILD | WS_VISIBLE | BS_AUTOCHECKBOX,\n\t\t\t   x,y,168,16,\n\t\t\t   optionsmenu,\n\t\t\t   (HMENU)IDI_O_COMPILER_FLAG,\n\t\t\t   ghInstance,\n\t\t\t   NULL);\n\t\ty+=16;\n\n\t\tif (my < y)\n\t\t\tmy = y;\n\n\t\tif (compiler_flag[i].flags & FLAG_SETINGUI)\n\t\t\tButton_SetCheck(wnd, 1);\n\t\telse\n\t\t\tButton_SetCheck(wnd, 0);\n\t\tAddTip(tipwnd, wnd,\tcompiler_flag[i].description);\n\t}\n\n\tCreateWindow(\"STATIC\",\"Extra Parameters:\",\n\t\t   WS_CHILD | WS_VISIBLE,\n\t\t   408,my,200-16,16,\n\t\t   optionsmenu,\n\t\t   (HMENU)0,\n\t\t   ghInstance,\n\t\t   NULL);\n\tmy+=16;\n\textraparmsitem = CreateWindowEx(WS_EX_CLIENTEDGE, \"EDIT\",parameters,\n\t\t   WS_CHILD | WS_VISIBLE|ES_LEFT | ES_WANTRETURN |\n\t\tES_MULTILINE | ES_AUTOVSCROLL,\n\t\t   408,my,r.right-r.left - 408 - 8,rheight-my-4,\n\t\t   optionsmenu,\n\t\t   (HMENU)IDI_O_ADDITIONALPARAMETERS,\n\t\t   ghInstance,\n\t\t   NULL);\n\tAddTip(tipwnd, extraparmsitem,\t\"You can specify any additional commandline arguments here.\\nAdd -DFOO=bar to define the FOO preprocessor constant as bar.\");\n\n\tShowWindow(optionsmenu, SW_SHOWDEFAULT);\n}\n\n\n\n\n\n\n\n\n\n\n\n#undef printf\n\nWNDPROC combosubclassproc;\nstatic LRESULT CALLBACK SearchComboSubClass(HWND hWnd,UINT message,\n\t\t\t\t     WPARAM wParam,LPARAM lParam)\n{\n\tswitch (message) \n\t{ \n\tcase WM_KEYDOWN: \n\t\tswitch (wParam) \n\t\t{\n\t\tcase VK_RETURN:\n\t\t\tPostMessage(mainwindow, WM_COMMAND, ID_DEF, (LPARAM)buttons[ID_DEF].hwnd);\n\t\t\treturn true;\n\t\t}\n\t}\n\treturn CallWindowProc(combosubclassproc, hWnd, message, wParam, lParam); \n}\n\nstatic LRESULT CALLBACK MainWndProc(HWND hWnd,UINT message,\n\t\t\t\t     WPARAM wParam,LPARAM lParam)\n{\n\tint i;\n\tRECT rect;\n\tPAINTSTRUCT ps;\n\teditor_t *editor;\n\tswitch (message)\n\t{\n\tcase WM_CLOSE:\n\t\t//if any child editors are still open, send close requests to them first.\n\t\t//this allows them to display prompts, instead of silently losing changes.\n\t\tfor (editor = editors; editor;)\n\t\t{\n\t\t\teditor_t *n = editor->next;\n\t\t\tif (editor->window)\n\t\t\t\tSendMessage(editor->window, WM_CLOSE, 0, 0);\n\t\t\teditor = n;\n\t\t}\n\t\t//okay, they're all dead. we can kill ourselves now.\n\t\tif (!editors)\n\t\t\tDestroyWindow(hWnd);\n\t\treturn 0;\n\tcase WM_CREATE:\n\t\t{\n\t\t\tCLIENTCREATESTRUCT ccs;\n\n\t\t\tHMENU rootmenu, windowmenu, m;\n\n\t\t\tDragAcceptFiles(hWnd, TRUE);\n\n\t\t\trootmenu = CreateMenu();\n\t\t\t\n\t\t\t\tAppendMenu(rootmenu, MF_POPUP, (UINT_PTR)(m = CreateMenu()),\t\"&File\");\n\t\t\t\t\tAppendMenu(m, 0, IDM_OPENPROJECT,\t\t\t\t\t\t\t\"Open Project / Decompile\");\n\t\t\t\t\tAppendMenu(m, 0, IDM_OPENNEW,\t\t\t\t\t\t\t\t\"Open File\");\n\t\t\t\t\tAppendMenu(m, 0, IDM_SAVE,\t\t\t\t\t\t\t\t\t\"&Save\\tCtrl+S\");\n\t\t\t\t//\tAppendMenu(m, 0, IDM_FIND,\t\t\t\t\t\t\t\t\t\"&Find\");\n\t\t\t\t\tAppendMenu(m, 0, IDM_UNDO,\t\t\t\t\t\t\t\t\t\"Undo\\tCtrl+Z\");\n\t\t\t\t\tAppendMenu(m, 0, IDM_REDO,\t\t\t\t\t\t\t\t\t\"Redo\\tCtrl+Y\");\n\t\t\t\t\tAppendMenu(m, MF_SEPARATOR, 0, NULL);\n\t\t\t\t\tAppendMenu(m, 0, IDM_CREATEINSTALLER_WINDOWS,\t\t\t\t\"Create Windows Installer\");\n\t\t\t\t\tAppendMenu(m, 0, IDM_CREATEINSTALLER_ANDROID,\t\t\t\t\"Create Android Installer\");\n\t\t\t\t\tAppendMenu(m, 0, IDM_CREATEINSTALLER_PACKAGES,\t\t\t\t\"Create Packages\");\n\t\t\t\t\tAppendMenu(m, MF_SEPARATOR, 0, NULL);\n\t\t\t\t\tAppendMenu(m, 0, IDM_QUIT,\t\t\t\t\t\t\t\t\t\"Exit\");\n\t\t\t\tAppendMenu(rootmenu, MF_POPUP, (UINT_PTR)(m = CreateMenu()),\t\"&Navigation\");\n\t\t\t\t\tAppendMenu(m, 0, IDM_GOTODEF,\t\t\t\t\t\t\t\t\"Go To Definition\\tF12\");\n\t\t\t\t\tAppendMenu(m, 0, IDM_RETURNDEF,\t\t\t\t\t\t\t\t\"Return From Definition\\tShift+F12\");\n\t\t\t\t\tAppendMenu(m, 0, IDM_GREP,\t\t\t\t\t\t\t\t\t\"Grep For Selection\\tCtrl+G\");\n\t\t\t\t\tAppendMenu(m, 0, IDM_OPENDOCU,\t\t\t\t\t\t\t\t\"Open Selected File\");\n\t\t\t\t\tAppendMenu(m, 0, IDM_OUTPUT_WINDOW,\t\t\t\t\t\t\t\"Show Output Window\\tF6\");\n\t\t\t\t\tAppendMenu(m, (fl_extramargins?MF_CHECKED:MF_UNCHECKED), IDM_UI_SHOWLINENUMBERS, \"Show Line Numbers\");\n\t\t\t\t\tAppendMenu(m, ((fl_tabsize>4)?MF_CHECKED:MF_UNCHECKED), IDM_UI_TABSIZE, \"Large Tabs\");\n\t\t\t\t\tAppendMenu(m, MF_SEPARATOR, 0, NULL);\n\t\t\t\t\tAppendMenu(m, 0, IDM_ENCODING_PRIVATEUSE,\t\t\t\t\t\"Convert to UTF-8\");\n\t\t\t\t\tAppendMenu(m, 0, IDM_ENCODING_DEPRIVATEUSE,\t\t\t\t\t\"Convert to Quake encoding\");\n\t\t\t\t\tAppendMenu(m, 0, IDM_ENCODING_UNIX,\t\t\t\t\t\t\t\"Convert to Unix Endings\");\n\t\t\t\t\tAppendMenu(m, 0, IDM_ENCODING_WINDOWS,\t\t\t\t\t\t\"Convert to Dos Endings\");\n\n\t\t\t\tAppendMenu(rootmenu, MF_POPUP, (UINT_PTR)(m = windowmenu = CreateMenu()),\t\"&Window\");\n\t\t\t\t\tAppendMenu(m, 0, IDM_CASCADE,\t\t\t\t\t\t\t\t\"Cascade\");\n\t\t\t\t\tAppendMenu(m, 0, IDM_TILE_HORIZ,\t\t\t\t\t\t\t\"Tile Horizontally\");\n\t\t\t\t\tAppendMenu(m, 0, IDM_TILE_VERT,\t\t\t\t\t\t\t\t\"Tile Vertically\");\n\t\t\t\tAppendMenu(rootmenu, MF_POPUP, (UINT_PTR)(m = CreateMenu()),\t\"&Debug\");\n\t\t\t\t\tAppendMenu(m, 0, IDM_DEBUG_REBUILD,\t\t\t\t\t\t\t\"Rebuild\\tF7\");\n\t\t\t\t\tAppendMenu(m, 0, IDM_DEBUG_BUILD_OPTIONS,\t\t\t\t\t\"Build Options\");\n\t\t\t\t\tAppendMenu(m, MF_SEPARATOR, 0, NULL);\n\t\t\t\t\tAppendMenu(m, 0, IDM_DEBUG_SETNEXT,\t\t\t\t\t\t\t\"Set Next Statement\\tF8\");\n\t\t\t\t\tAppendMenu(m, 0, IDM_DEBUG_RUN,\t\t\t\t\t\t\t\t\"Run/Resume\\tF5\");\n\t\t\t\t\tAppendMenu(m, 0, IDM_DEBUG_STEPOVER,\t\t\t\t\t\t\"Step Over\\tF10\");\n\t\t\t\t\tAppendMenu(m, 0, IDM_DEBUG_STEPINTO,\t\t\t\t\t\t\"Step Into\\tF11\");\n\t\t\t\t\tAppendMenu(m, 0, IDM_DEBUG_STEPOUT,\t\t\t\t\t\t\t\"Step Out\\tShift-F11\");\n\t\t\t\t\tAppendMenu(m, 0, IDM_DEBUG_TOGGLEBREAK,\t\t\t\t\t\t\"Set Breakpoint\\tF9\");\n\t\t\t\tAppendMenu(rootmenu, MF_POPUP, (UINT_PTR)(m = CreateMenu()),\t\"&Help\");\n\t\t\t\t\tAppendMenu(m, 0, IDM_ABOUT,\t\t\t\t\t\t\t\t\t\"About\");\n\n\t\t\tSetMenu(hWnd, rootmenu);\n\n\t\t\t// Retrieve the handle to the window menu and assign the\n\t\t\t// first child window identifier.\n\n\t\t\tmemset(&ccs, 0, sizeof(ccs));\n\t\t\tccs.hWindowMenu = windowmenu;\n\t\t\tccs.idFirstChild = IDM_FIRSTCHILD;\n\n\t\t\t// Create the MDI client window.\n\n\t\t\tmdibox = CreateWindow( \"MDICLIENT\", (LPCTSTR) NULL,\n\t\t\t\t\tWS_CHILD | WS_CLIPCHILDREN | WS_VSCROLL | WS_HSCROLL,\n\t\t\t\t\t0, 0, 320, 200, hWnd, (HMENU) 0xCAC, ghInstance, (LPSTR) &ccs);\n\t\t\tShowWindow(mdibox, SW_SHOW);\n\n\t\t\twatches = CreateWindow(WC_LISTVIEW, (LPCTSTR) NULL,\n\t\t\t\t\tWS_CHILD | WS_VSCROLL | WS_HSCROLL | LVS_REPORT | LVS_EDITLABELS,\n\t\t\t\t\t0, 0, 320, 200, hWnd, (HMENU) 0xCAD, ghInstance, NULL);\n\n\t\t\tSplitterAdd(mdibox, 32, 32);\n\n\t\t\tif (watches)\n\t\t\t{\n\t\t\t\tLVCOLUMN col;\n\t\t\t\tLVITEM newi;\n\n//\t\t\t\tListView_SetUnicodeFormat(watches, TRUE);\n\t\t\t\tListView_SetExtendedListViewStyle(watches, LVS_EX_GRIDLINES);\n\t\t\t\tmemset(&col, 0, sizeof(col));\n\t\t\t\tcol.mask = LVCF_FMT | LVCF_TEXT | LVCF_WIDTH;\n\t\t\t\tcol.fmt = LVCFMT_LEFT;\n\t\t\t\tcol.cx = 320;\n\t\t\t\tcol.pszText = \"Variable\";\n\t\t\t\tListView_InsertColumn(watches, 0, &col);\n\t\t\t\tcol.pszText = \"Value\";\n\t\t\t\tListView_InsertColumn(watches, 1, &col);\n\n\n\n\t\t\t\tmemset(&newi, 0, sizeof(newi));                      \n\n\t\t\t\tnewi.pszText = \"<click to add>\";\n\t\t\t\tnewi.mask = LVIF_TEXT | LVIF_PARAM;\n\t\t\t\tnewi.lParam = ~0;\n\t\t\t\tnewi.iSubItem = 0;\n\t\t\t\tListView_InsertItem(watches, &newi); \n\t\t\t}\n\n\t\t\tprojecttree = CreateWindow(WC_TREEVIEW, (LPCTSTR) NULL,\n\t\t\t\t\tWS_CHILD | WS_CLIPCHILDREN | WS_VSCROLL | WS_HSCROLL\n\t\t\t\t\t|\tTVS_HASBUTTONS |TVS_LINESATROOT|TVS_HASLINES,\n\t\t\t0, 0, 320, 200, hWnd, (HMENU) 0xCAC, ghInstance, (LPSTR) &ccs);\n\t\t\tShowWindow(projecttree, SW_SHOW);\n\n\t\t\tif (projecttree)\n\t\t\t{\n\t\t\t\tsearch_name = CreateWindowEx(WS_EX_CLIENTEDGE, \"COMBOBOX\", (LPCTSTR) NULL,\n\t\t\t\t\t\tWS_CHILD | WS_CLIPCHILDREN|CBS_DROPDOWN|CBS_SORT,\n\t\t\t\t\t\t0, 0, 320, 200, hWnd, (HMENU) 0x4403, ghInstance, (LPSTR) NULL);\n\t\t\t\t{\n\t\t\t\t\t//microsoft suck big hairy donkey balls.\n\t\t\t\t\t//this tries to get the edit box of the combo control.\n\t\t\t\t\tHWND comboedit = GetWindow(search_name, GW_CHILD);\n\t\t\t\t\tcombosubclassproc = (WNDPROC) SetWindowLongPtr(comboedit, GWLP_WNDPROC, (DWORD_PTR) SearchComboSubClass);\n\t\t\t\t}\n\t\t\t\tShowWindow(search_name, SW_SHOW);\n\t\t\t}\n\t\t}\n\t\tbreak;\n\tcase WM_CTLCOLORBTN:\n\t\treturn (LRESULT)GetSysColorBrush(COLOR_HIGHLIGHT);//COLOR_BACKGROUND;\n\tcase WM_DESTROY:\n\t\tDragAcceptFiles(hWnd, FALSE);\n\t\tmainwindow = NULL;\n\t\tbreak;\n\n\tcase WM_DROPFILES:\n\t\t{\n\t\t\tHDROP p = (HDROP)wParam;\n\t\t\tchar fname[MAX_PATH];\n\t\t\tif (DragQueryFile(p, ~0, (LPSTR) NULL, 0) == 1)\n\t\t\t{\n\t\t\t\tDragQueryFile(p, 0, fname, sizeof(fname));\n\t\t\t\tSetProgsSrcFileAndPath(fname);\n\t\t\t\tresetprogssrc = true;\n\t\t\t}\n\t\t\tDragFinish(p);\n\t\t}\n\t\tbreak;\n\n\tcase WM_SIZE:\n\t\t{\n\t\t\tint y;\n\t\t\tGetClientRect(mainwindow, &rect);\n\t\t\ty = rect.bottom;\n\n\t\t\tfor (i = 0; i < NUMBUTTONS; i+=2)\n\t\t\t{\n\t\t\t\ty -= 24;\n\t\t\t\tif (!buttons[i+1].hwnd)\n\t\t\t\t\tSetWindowPos(buttons[i].hwnd, NULL, 0, y, 192, 24, SWP_NOZORDER);\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tSetWindowPos(buttons[i].hwnd, NULL, 0, y, 192/2, 24, SWP_NOZORDER);\n\t\t\t\t\tSetWindowPos(buttons[i+1].hwnd, NULL, 192/2, y, 192-192/2, 24, SWP_NOZORDER);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\ty -= 24;\n\t\t\tSetWindowPos(search_name, NULL, 0, y, 192, 24, SWP_NOZORDER);\n\n\t\t\tif (projecttree)\n\t\t\t\tSetWindowPos(projecttree, NULL, 0, 0, 192, y, SWP_NOZORDER);\n\n\t\t\tsplitterrect.left = 192;\n\t\t\tsplitterrect.right = rect.right-rect.left;\n\t\t\tsplitterrect.bottom = rect.bottom-rect.top;\n\t\t\tSplitterUpdate();\n\t\t}\n\t\tbreak;\n//\t\tgoto gdefault;\n\tcase WM_ERASEBKGND:\n\t\treturn TRUE;\t//background is clear... or doesn't need clearing (if its fully obscured)\n\tcase WM_PAINT:\n\t\tBeginPaint(hWnd,(LPPAINTSTRUCT)&ps);\n\n\t\tEndPaint(hWnd,(LPPAINTSTRUCT)&ps);\n\t\treturn TRUE;\n\tcase WM_COMMAND:\n\t\ti = LOWORD(wParam);\n\t\tif (i == 0x4403)\n\t\t{\n\t\t\tchar buffer[65536];\n\t\t\tchar text[128];\n\t\t\tswitch(HIWORD(wParam))\n\t\t\t{\n\t\t\tcase CBN_EDITUPDATE:\n\t\t\t\tGetWindowText(search_name, text, sizeof(text)-1);\n\t\t\t\tif (GenAutoCompleteList(text, buffer, sizeof(buffer)))\n\t\t\t\t{\n\t\t\t\t\tchar token[128];\n\t\t\t\t\tchar *list;\n\t\t\t\t\tDWORD start=0,end=0;\n\t\t\t\t\tSendMessage(search_name, CB_GETEDITSEL, (WPARAM)&start, (LPARAM)&end);\n\t\t\t\t\tComboBox_ResetContent(search_name);\t//windows is shit. this clears the text too.\n\t\t\t\t\tSetWindowText(search_name, text);\n\t\t\t\t\tComboBox_SetEditSel(search_name, start, end);\n\t\t\t\t\tfor (list = buffer; ; )\n\t\t\t\t\t{\n\t\t\t\t\t\tlist = COM_ParseOut(list, token, sizeof(token));\n\t\t\t\t\t\tif (!*token)\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tComboBox_AddString(search_name, token);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tgoto gdefault;\n\t\t}\n\t\tif (i>=20 && i < 20+NUMBUTTONS)\n\t\t{\n\t\t\ti -= 20;\n\t\t\tif (i == ID_DEF)\n\t\t\t{\n\t\t\t\tGetWindowText(search_name, finddef, sizeof(finddef)-1);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tif (i == ID_GREP)\n\t\t\t{\n\t\t\t\tGetWindowText(search_name, greptext, sizeof(greptext)-1);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tbuttons[i].washit = 1;\n\t\t\tbreak;\n\t\t}\n\t\tif (i < IDM_FIRSTCHILD)\n\t\t{\n\t\t\tHWND ew;\n\t\t\teditor_t *editor;\n\t\n\t\t\tew = (HWND)SendMessage(mdibox, WM_MDIGETACTIVE, 0, 0);\n\n\t\t\tfor (editor = editors; editor; editor = editor->next)\n\t\t\t{\n\t\t\t\tif (editor->window == ew)\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (editor)\n\t\t\t\tEditorMenu(editor, wParam);\n\t\t\telse\n\t\t\t\tGenericMenu(wParam);\n\t\t\tbreak;\n\t\t}\n\t\tgoto gdefault;\n\tcase WM_NOTIFY:\n\t\tif (lParam)\n\t\t{\n\t\t\tNMHDR *nm;\n\t\t\tHANDLE item;\n\t\t\tTVITEM i;\n\t\t\tchar filename[256];\n\t\t\tchar itemtext[256];\n\t\t\tint oldlen;\n\t\t\tint newlen;\n\t\t\tnm = (NMHDR*)lParam;\n\t\t\tif (nm->hwndFrom == watches)\n\t\t\t{\n\t\t\t\tswitch(nm->code)\n\t\t\t\t{\n\t\t\t\tcase LVN_BEGINLABELEDITA:\n\t\t\t\t\treturn FALSE;\t//false to allow...\n\t\t\t\tcase LVN_BEGINLABELEDITW:\n//\t\t\t\t\tOutputDebugString(\"Begin EditW\\n\");\n\t\t\t\t\treturn FALSE;\t//false to allow...\n\t\t\t\tcase LVN_ENDLABELEDITA:\n\t\t\t\t\tif (((NMLVDISPINFOA*)nm)->item.iItem == ListView_GetItemCount(watches)-1)\n\t\t\t\t\t{\n\t\t\t\t\t\tLVITEM newi;\n\t\t\t\t\t\tmemset(&newi, 0, sizeof(newi));\n\t\t\t\t\t\tnewi.iItem = ListView_GetItemCount(watches);\n\t\t\t\t\t\tnewi.pszText = \"<click to add>\";\n\t\t\t\t\t\tnewi.mask = LVIF_TEXT | LVIF_PARAM;\n\t\t\t\t\t\tnewi.lParam = ~0;\n\t\t\t\t\t\tnewi.iSubItem = 0;\n\t\t\t\t\t\tListView_InsertItem(watches, &newi);\n\t\t\t\t\t}\n\t\t\t\t\tEngineCommandf(\"qcinspect \\\"%s\\\" \\\"%s\\\"\\n\", ((NMLVDISPINFOA*)nm)->item.pszText, \"\"); //term, scope\n\t\t\t\t\tPostMessage(mainwindow, WM_SIZE, 0, 0);\n\t\t\t\t\treturn TRUE;\t//true to allow...\n/*\t\t\t\tcase LVN_ENDLABELEDITW:\n//\t\t\t\t\tOutputDebugString(\"End EditW\\n\");\n\t\t\t\t\tif (((NMLVDISPINFOW*)nm)->item.iItem == ListView_GetItemCount(watches)-1)\n\t\t\t\t\t{\n\t\t\t\t\t\tLVITEM newi;\n\t\t\t\t\t\tmemset(&newi, 0, sizeof(newi));\n\t\t\t\t\t\tnewi.iItem = ListView_GetItemCount(watches);\n\t\t\t\t\t\tnewi.pszText = \"<click to add>\";\n\t\t\t\t\t\tnewi.mask = LVIF_TEXT | LVIF_PARAM;\n\t\t\t\t\t\tnewi.lParam = ~0;\n\t\t\t\t\t\tnewi.iSubItem = 0;\n\t\t\t\t\t\tListView_InsertItem(watches, &newi);\n\t\t\t\t\t}\n\t\t\t\t\tEngineCommandf(\"qcinspect \\\"%s\\\" \\\"%s\\\"\\n\", ((NMLVDISPINFOW*)nm)->item.pszText, \"\"); //term, scope\n\t\t\t\t\treturn TRUE;\t//true to allow...\n*/\n\t\t\t\tcase LVN_ITEMCHANGING:\n//\t\t\t\t\tOutputDebugString(\"Changing\\n\");\n\t\t\t\t\treturn FALSE;\t//false to allow...\n\t\t\t\tcase LVN_ITEMCHANGED:\n//\t\t\t\t\tOutputDebugString(\"Changed\\n\");\n\t\t\t\t\treturn FALSE;\n\t\t\t\tcase LVN_GETDISPINFOA:\n//\t\t\t\t\tOutputDebugString(\"LVN_GETDISPINFOA\\n\");\n\t\t\t\t\treturn FALSE;\n//\t\t\t\tcase LVN_GETDISPINFOW:\n//\t\t\t\t\tOutputDebugString(\"LVN_GETDISPINFOW\\n\");\n//\t\t\t\t\treturn FALSE;\n\t\t\t\tcase NM_DBLCLK:\n//\t\t\t\t\tOutputDebugString(\"NM_DBLCLK\\n\");\n\t\t\t\t\t{\n\t\t\t\t\t\tNMITEMACTIVATE *ia = (NMITEMACTIVATE*)nm;\n\t\t\t\t\t\tLVHITTESTINFO ht;\n\t\t\t\t\t\tmemset(&ht, 0, sizeof(ht));\n\t\t\t\t\t\tht.pt = ia->ptAction;\n\t\t\t\t\t\tListView_SubItemHitTest(watches, &ht);\n\t\t\t\t\t\tListView_EditLabel(watches, ht.iItem);\n\t\t\t\t\t}\n\t\t\t\t\treturn TRUE;\n\t\t\t\tcase LVN_ITEMACTIVATE:\n//\t\t\t\t\tOutputDebugString(\"LVN_ITEMACTIVATE\\n\");\n\t\t\t\t\treturn FALSE;\t//must return false\n\t\t\t\tcase LVN_COLUMNCLICK:\n//\t\t\t\t\tOutputDebugString(\"LVN_COLUMNCLICK\\n\");\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n//\t\t\t\t\tsprintf(filename, \"%i\\n\", nm->code);\n//\t\t\t\t\tOutputDebugString(filename);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\treturn FALSE;\n\t\t\t}\n\t\t\telse if (nm->hwndFrom == projecttree)\n\t\t\t{\n\t\t\t\tswitch(nm->code)\n\t\t\t\t{\n\t\t\t\tcase NM_DBLCLK:\n\t\t\t\t\titem = TreeView_GetSelection(projecttree);\n\t\t\t\t\tmemset(&i, 0, sizeof(i));\n\t\t\t\t\ti.hItem = item;\n\t\t\t\t\ti.mask = TVIF_TEXT|TVIF_PARAM;\n\t\t\t\t\ti.pszText = itemtext;\n\t\t\t\t\ti.cchTextMax = sizeof(itemtext)-1;\n\t\t\t\t\tif (!TreeView_GetItem(projecttree, &i))\n\t\t\t\t\t\treturn 0;\n\t\t\t\t\tif (!i.lParam)\n\t\t\t\t\t\treturn 0;\n\t\t\t\t\tstrcpy(filename, i.pszText);\n\t\t\t\t\twhile(item)\n\t\t\t\t\t{\n\t\t\t\t\t\titem = TreeView_GetParent(projecttree, item);\n\t\t\t\t\t\ti.hItem = item;\n\t\t\t\t\t\tif (!TreeView_GetItem(projecttree, &i))\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tif (!TreeView_GetParent(projecttree, item))\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\toldlen = strlen(filename);\n\t\t\t\t\t\tnewlen = strlen(i.pszText);\n\t\t\t\t\t\tif (oldlen + newlen + 2 > sizeof(filename))\n\t\t\t\t\t\t\tbreak;\t//don't overflow.\n\t\t\t\t\t\tmemmove(filename+newlen+1, filename, oldlen+1);\n\t\t\t\t\t\tfilename[newlen] = '/';\n\t\t\t\t\t\tmemcpy(filename, i.pszText, newlen);\n\t\t\t\t\t}\n\t\t\t\t\tEditFile(filename, -1, false);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\tdefault:\n\tgdefault:\n\t\tif (mdibox)\n\t\t\treturn DefFrameProc(hWnd,mdibox,message,wParam,lParam);\n\t\telse\n\t\t\treturn DefWindowProc(hWnd,message,wParam,lParam);\n\t}\n\treturn 0;\n}\n\nstatic void DoTranslateMessage(MSG *msg)\n{\n\tif (!TranslateAccelerator(mainwindow, accelerators, msg))\n\t{\n\t\tTranslateMessage(msg);\n\t\tDispatchMessage(msg);\n\t}\n}\n\nvoid GUIPrint(HWND wnd, char *msg)\n{\n//\tMSG        wmsg;\n\tint len;\n\tstatic int writing;\n\n\tif (writing)\n\t\treturn;\n\tif (!mainwindow)\n\t{\n\t\tprintf(\"%s\", msg);\n\t\treturn;\n\t}\n\twriting=true;\n\tlen=Edit_GetTextLength(wnd);\n/*\tif ((unsigned)len>(32767-strlen(msg)))\n\t\tEdit_SetSel(wnd,0,len);\n\telse*/\n\t\tEdit_SetSel(wnd,len,len);\n\tEdit_ReplaceSel(wnd,msg);\n\n\t/*\n\twhile (PeekMessage (&wmsg, NULL, 0, 0, PM_NOREMOVE))\n\t{\n\t\tif (!GetMessage (&wmsg, NULL, 0, 0))\n\t\t\tbreak;\n\t\tDoTranslateMessage(&wmsg);\n\t}\n\t*/\n\twriting=false;\n}\n\nunsigned int utf8_decode(int *error, const void *in, char **out)\n{\n\t//uc is the output unicode char\n\tunsigned int uc = 0xfffdu;\t//replacement character\n\t//l is the length\n\tunsigned int l = 1;\n\tconst unsigned char *str = in;\n\n\tif ((*str & 0xe0) == 0xc0)\n\t{\n\t\tif ((str[1] & 0xc0) == 0x80)\n\t\t{\n\t\t\tl = 2;\n\t\t\tuc = ((str[0] & 0x1f)<<6) | (str[1] & 0x3f);\n\t\t\tif (!uc || uc >= (1u<<7))\t//allow modified utf-8\n\t\t\t\t*error = 0;\n\t\t\telse\n\t\t\t\t*error = 2;\n\t\t}\n\t\telse *error = 1;\n\t}\n\telse if ((*str & 0xf0) == 0xe0)\n\t{\n\t\tif ((str[1] & 0xc0) == 0x80 && (str[2] & 0xc0) == 0x80)\n\t\t{\n\t\t\tl = 3;\n\t\t\tuc = ((str[0] & 0x0f)<<12) | ((str[1] & 0x3f)<<6) | ((str[2] & 0x3f)<<0);\n\t\t\tif (uc >= (1u<<11))\n\t\t\t\t*error = 0;\n\t\t\telse\n\t\t\t\t*error = 2;\n\t\t}\n\t\telse *error = 1;\n\t}\n\telse if ((*str & 0xf8) == 0xf0)\n\t{\n\t\tif ((str[1] & 0xc0) == 0x80 && (str[2] & 0xc0) == 0x80 && (str[3] & 0xc0) == 0x80)\n\t\t{\n\t\t\tl = 4;\n\t\t\tuc = ((str[0] & 0x07)<<18) | ((str[1] & 0x3f)<<12) | ((str[2] & 0x3f)<<6) | ((str[3] & 0x3f)<<0);\n\t\t\tif (uc >= (1u<<16))\n\t\t\t\t*error = 0;\n\t\t\telse\n\t\t\t\t*error = 2;\n\t\t}\n\t\telse *error = 1;\n\t}\n\telse if ((*str & 0xfc) == 0xf8)\n\t{\n\t\tif ((str[1] & 0xc0) == 0x80 && (str[2] & 0xc0) == 0x80 && (str[3] & 0xc0) == 0x80 && (str[4] & 0xc0) == 0x80)\n\t\t{\n\t\t\tl = 5;\n\t\t\tuc = ((str[0] & 0x03)<<24) | ((str[1] & 0x3f)<<18) | ((str[2] & 0x3f)<<12) | ((str[3] & 0x3f)<<6) | ((str[4] & 0x3f)<<0);\n\t\t\tif (uc >= (1u<<21))\n\t\t\t\t*error = 0;\n\t\t\telse\n\t\t\t\t*error = 2;\n\t\t}\n\t\telse *error = 1;\n\t}\n\telse if ((*str & 0xfe) == 0xfc)\n\t{\n\t\t//six bytes\n\t\tif ((str[1] & 0xc0) == 0x80 && (str[2] & 0xc0) == 0x80 && (str[3] & 0xc0) == 0x80 && (str[4] & 0xc0) == 0x80)\n\t\t{\n\t\t\tl = 6;\n\t\t\tuc = ((str[0] & 0x01)<<30) | ((str[1] & 0x3f)<<24) | ((str[2] & 0x3f)<<18) | ((str[3] & 0x3f)<<12) | ((str[4] & 0x3f)<<6) | ((str[5] & 0x3f)<<0);\n\t\t\tif (uc >= (1u<<26))\n\t\t\t\t*error = 0;\n\t\t\telse\n\t\t\t\t*error = 2;\n\t\t}\n\t\telse *error = 1;\n\t}\n\t//0xfe and 0xff, while plausable leading bytes, are not permitted.\n#if 0\n\telse if ((*str & 0xff) == 0xfe)\n\t{\n\t\tif ((str[1] & 0xc0) == 0x80 && (str[2] & 0xc0) == 0x80 && (str[3] & 0xc0) == 0x80 && (str[4] & 0xc0) == 0x80)\n\t\t{\n\t\t\tl = 7;\n\t\t\tuc = 0 | ((str[1] & 0x3f)<<30) | ((str[2] & 0x3f)<<24) | ((str[3] & 0x3f)<<18) | ((str[4] & 0x3f)<<12) | ((str[5] & 0x3f)<<6) | ((str[6] & 0x3f)<<0);\n\t\t\tif (uc >= (1u<<31))\n\t\t\t\t*error = 0;\n\t\t\telse\n\t\t\t\t*error = 2;\n\t\t}\n\t\telse *error = 1;\n\t}\n\telse if ((*str & 0xff) == 0xff)\n\t{\n\t\tif ((str[1] & 0xc0) == 0x80 && (str[2] & 0xc0) == 0x80 && (str[3] & 0xc0) == 0x80 && (str[4] & 0xc0) == 0x80)\n\t\t{\n\t\t\tl = 8;\n\t\t\tuc = 0 | ((str[1] & 0x3f)<<36) | ((str[2] & 0x3f)<<30) | ((str[3] & 0x3f)<<24) | ((str[4] & 0x3f)<<18) | ((str[5] & 0x3f)<<12) | ((str[6] & 0x3f)<<6) | ((str[7] & 0x3f)<<0);\n\t\t\tif (uc >= (1llu<<36))\n\t\t\t\t*error = false;\n\t\t\telse\n\t\t\t\t*error = 2;\n\t\t}\n\t\telse *error = 1;\n\t}\n#endif\n\telse if (*str & 0x80)\n\t{\n\t\t//sequence error\n\t\t*error = 1;\n\t\tuc = 0xe000u + *str;\n\t}\n\telse \n\t{\n\t\t//ascii char\n\t\t*error = 0;\n\t\tuc = *str;\n\t}\n\n\t*out = (void*)(str + l);\n\n\tif (!*error)\n\t{\n\t\t//try to deal with surrogates by decoding the low if we see a high.\n\t\tif (uc >= 0xd800u && uc < 0xdc00u)\n\t\t{\n#if 1\n\t\t\t//cesu-8\n\t\t\tchar *lowend;\n\t\t\tunsigned int lowsur = utf8_decode(error, str + l, &lowend);\n\t\t\tif (*error == 4)\n\t\t\t{\n\t\t\t\t*out = lowend;\n\t\t\t\tuc = (((uc&0x3ffu) << 10) | (lowsur&0x3ffu)) + 0x10000;\n\t\t\t\t*error = false;\n\t\t\t}\n\t\t\telse\n#endif\n\t\t\t{\n\t\t\t\t*error = 3;\t//bad - lead surrogate without tail.\n\t\t\t}\n\t\t}\n\t\tif (uc >= 0xdc00u && uc < 0xe000u)\n\t\t\t*error = 4;\t//bad - tail surrogate\n\n\t\t//these are meant to be illegal too\n\t\tif (uc == 0xfffeu || uc == 0xffffu || uc > 0x10ffffu)\n\t\t\t*error = 2;\t//illegal code\n\t}\n\n\treturn uc;\n}\n//outlen is the size of out in _BYTES_.\nwchar_t *widen(wchar_t *out, size_t outbytes, const char *utf8, const char *stripchars)\n{\n\tsize_t outlen;\n\twchar_t *ret = out;\n\t//utf-8 to utf-16, not ucs-2.\n\tunsigned int codepoint;\n\tint error;\n\toutlen = outbytes/sizeof(wchar_t);\n\tif (!outlen)\n\t\treturn L\"\";\n\toutlen--;\n\twhile (*utf8)\n\t{\n\t\tif (stripchars && strchr(stripchars, *utf8))\n\t\t{\t//skip certain ascii chars\n\t\t\tutf8++;\n\t\t\tcontinue;\n\t\t}\n\t\tcodepoint = utf8_decode(&error, utf8, (void*)&utf8);\n\t\tif (error || codepoint > 0x10FFFFu)\n\t\t\tcodepoint = 0xFFFDu;\n\t\tif (codepoint > 0xffff)\n\t\t{\n\t\t\tif (outlen < 2)\n\t\t\t\tbreak;\n\t\t\toutlen -= 2;\n\t\t\tcodepoint -= 0x10000u;\n\t\t\t*out++ = 0xD800 | (codepoint>>10);\n\t\t\t*out++ = 0xDC00 | (codepoint&0x3ff);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (outlen < 1)\n\t\t\t\tbreak;\n\t\t\toutlen -= 1;\n\t\t\t*out++ = codepoint;\n\t\t}\n\t}\n\t*out = 0;\n\treturn ret;\n}\nint GUIEmitOutputText(HWND wnd, int start, char *text, int len, DWORD colour)\n{\n\twchar_t wc[2048];\n\tint c;\n\tCHARFORMAT cf;\n\n\tif (!len)\n\t\treturn start;\n\n\tc = text[len];\n\ttext[len] = '\\0';\n//\twc = QCC_makeutf16(text, len, &ol);\n\twiden(wc, sizeof(wc), text, \"\\r\");\n\ttext[len] = c;\n\tEdit_SetSel(wnd,start,start);\n\tSendMessageW(wnd, EM_REPLACESEL, 0L, (LPARAM)wc);\n\tlen = wcslen(wc);\n\n\tEdit_SetSel(wnd,start,start+len);\n\tmemset(&cf, 0, sizeof(cf));\n\tcf.cbSize = sizeof(cf);\n\tcf.dwMask = CFM_COLOR;\n\tcf.crTextColor = colour;\n\tSendMessage(wnd, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf);\n\tEdit_SetSel(wnd,start+len,start+len);\n\tEdit_ScrollCaret(wnd);\n\n\treturn start + len;\n}\nint outlen;\nint outstatus;\npbool gui_doannotates;\nint GUIprintf(const char *msg, ...)\n{\n\tva_list\t\targptr;\n\tchar\t\tbuf[1024];\n\tchar rn[3] = \"\\n\";\n\tchar *st, *s;\n\tint args;\n//\tMSG        wmsg;\n\n\tDWORD col;\n\n\tva_start (argptr,msg);\n\targs = QC_vsnprintf (buf,sizeof(buf)-1, msg,argptr);\n\tva_end (argptr);\n\tbuf[sizeof(buf)-5] = '.';\n\tbuf[sizeof(buf)-4] = '.';\n\tbuf[sizeof(buf)-3] = '.';\n\tbuf[sizeof(buf)-2] = '\\n';\n\tbuf[sizeof(buf)-1] = 0;\n\n\tprintf(\"%s\", buf);\n\t//OutputDebugStringA(buf);\n\tif (logfile)\n\t\tfprintf(logfile, \"%s\", buf);\n\n\tif (!*buf)\n\t{\n\t\teditor_t *ed;\n\t\t/*clear text*/\n\t\tSetWindowText(outputbox,\"\");\n\t\toutlen = 0;\n\n\t\t/*make sure its active so we can actually scroll. stupid windows*/\n\t\tSplitterFocus(outputbox, 64, 0);\n\n\t\t/*colour background to default*/\n\t\tTreeView_SetBkColor(projecttree, -1);\n\t\toutstatus = 0;\n\n\n\t\tif (gui_doannotates)\n\t\t{\n\t\t\tfor (ed = editors; ed; ed = ed->next)\n\t\t\t{\n\t\t\t\tif (ed->scintilla)\n\t\t\t\t\tSendMessage(ed->editpane, SCI_ANNOTATIONCLEARALL, 0, 0);\n\t\t\t}\n\t\t}\n\t\treturn 0;\n\t}\n\n\tif (strstr(buf, \": error\") || strstr(buf, \": werror\"))\n\t{\n\t\tif (outstatus < 2)\n\t\t{\n\t\t\tTreeView_SetBkColor(projecttree, RGB(255, 0, 0));\n\t\t\toutstatus = 2;\n\t\t}\n\t\tcol = RGB(255, 0, 0);\n\t}\n\telse if (strstr(buf, \": warning\"))\n\t{\n\t\tif (outstatus < 1)\n\t\t{\n\t\t\tTreeView_SetBkColor(projecttree, RGB(255, 255, 0));\n\t\t\toutstatus = 1;\n\t\t}\n\t\tcol = RGB(128, 128, 0);\n\t}\n\telse\n\t\tcol = RGB(0, 0, 0);\n\n\ts = st = buf;\n\twhile(*s)\n\t{\n\t\tif (*s == '\\n')\n\t\t{\n\t\t\t*s = '\\0';\n\t\t\tif (!strncmp(st, \"code: \", 6)) \n\t\t\t\tst+=6;\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (*st)\n\t\t\t\t\toutlen = GUIEmitOutputText(outputbox, outlen, st, strlen(st), col);\n\t\t\t\toutlen = GUIEmitOutputText(outputbox, outlen, rn, 1, col);\n\t\t\t}\n\n\t\t\tif (gui_doannotates)\n\t\t\t{\n\t\t\t\tchar *colon1 = strchr(st, ':');\n\t\t\t\tif (colon1)\n\t\t\t\t{\n\t\t\t\t\tchar *colon2 = strchr(colon1+1, ':');\n\t\t\t\t\tif (colon2)\n\t\t\t\t\t{\n\t\t\t\t\t\tunsigned int line;\n\t\t\t\t\t\tchar *validation;\n\t\t\t\t\t\t*colon1 = 0;\n\t\t\t\t\t\tline = strtoul(colon1+1, &validation, 10);\n\t\t\t\t\t\tif (validation == colon2)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\teditor_t *ed;\n\t\t\t\t\t\t\tcolon2++;\n\t\t\t\t\t\t\twhile(*colon2 == ' ' || *colon2 == '\\t')\n\t\t\t\t\t\t\t\tcolon2++;\n\t\t\t\t\t\t\tfor (ed = editors; ed; ed = ed->next)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tif (!stricmp(ed->filename, st))\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tif (ed->scintilla)\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tif (!SendMessage(ed->editpane, SCI_ANNOTATIONGETLINES, line-1, 0))\n\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\tSendMessage(ed->editpane, SCI_ANNOTATIONSETVISIBLE, ANNOTATION_BOXED, 0);\n\t\t\t\t\t\t\t\t\t\t\tSendMessage(ed->editpane, SCI_ANNOTATIONSETTEXT, line-1, (LPARAM)colon2);\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\tchar buf[8192];\n\t\t\t\t\t\t\t\t\t\t\tint clen = SendMessage(ed->editpane, SCI_ANNOTATIONGETTEXT, line-1, (LPARAM)NULL);\n\t\t\t\t\t\t\t\t\t\t\tif (clen+1+strlen(colon2) < sizeof(buf))\n\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\tclen = SendMessage(ed->editpane, SCI_ANNOTATIONGETTEXT, line-1, (LPARAM)buf);\n\t\t\t\t\t\t\t\t\t\t\t\tbuf[clen++] = '\\n';\n\t\t\t\t\t\t\t\t\t\t\t\tmemcpy(buf+clen, colon2, strlen(colon2)+1);\n//\t\t\t\t\t\t\t\t\t\t\t\tSendMessage(ed->editpane, SCI_ANNOTATIONSETVISIBLE, ANNOTATION_BOXED, 0);\n\t\t\t\t\t\t\t\t\t\t\t\tSendMessage(ed->editpane, SCI_ANNOTATIONSETTEXT, line-1, (LPARAM)buf);\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tst = s+1;\n\t\t}\n\n\t\ts++;\n\t}\n\tif (*st)\n\t\toutlen = GUIEmitOutputText(outputbox, outlen, st, strlen(st), col);\n\n/*\n\ts = st = buf;\n\twhile(*s)\n\t{\n\t\tif (*s == '\\n')\n\t\t{\n\t\t\t*s = '\\0';\n\t\t\tif (*st)\n\t\t\t\tGUIPrint(outputbox, st);\n\t\t\tGUIPrint(outputbox, \"\\r\\n\");\n\t\t\tst = s+1;\n\t\t}\n\n\t\ts++;\n\t}\n\tif (*st)\n\t\tGUIPrint(outputbox, st);\n*/\n\treturn args;\n}\nint Dummyprintf(const char *msg, ...){return 0;}\n\n#undef Sys_Error\n\nvoid compilecb(void)\n{\n\t//used to repaint the output window periodically instead of letting it redraw as stuff gets sent to it. this can save significant time on mods with boatloads of warnings.\n\tMSG wmsg;\n\tif (!SplitterGet(outputbox))\n\t\treturn;\n\tSendMessage(outputbox, WM_SETREDRAW, TRUE, 0);\n\tRedrawWindow(outputbox, NULL, NULL, RDW_ERASE | RDW_FRAME | RDW_INVALIDATE | RDW_ALLCHILDREN);\n\twhile (PeekMessage (&wmsg, NULL, 0, 0, PM_REMOVE))\n\t\tDoTranslateMessage(&wmsg);\n\tSendMessage(outputbox, WM_SETREDRAW, FALSE, 0);\n}\n\nvoid Sys_Error(const char *text, ...);\nvoid RunCompiler(char *args, pbool quick)\n{\n\tconst char *argv[256];\n\tint argc;\n\tprogexterns_t ext;\n\tprogfuncs_t funcs;\n\n\teditor_t *editor;\n\tfor (editor = editors; editor; editor = editor->next)\n\t{\n\t\tif (editor->modified)\n\t\t{\n\t\t\tif (EditorModified(editor))\n\t\t\t{\n\t\t\t\tchar msg[1024];\n\t\t\t\tsprintf(msg, \"%s is modified in both memory and on disk. Overwrite external modification? (saying no will reload from disk)\", editor->filename);\n\t\t\t\tswitch(MessageBox(NULL, msg, \"Modification conflict\", MB_YESNOCANCEL))\n\t\t\t\t{\n\t\t\t\tcase IDYES:\n\t\t\t\t\tEditorSave(editor);\n\t\t\t\t\tbreak;\n\t\t\t\tcase IDNO:\n\t\t\t\t\tEditorReload(editor);\n\t\t\t\t\tbreak;\n\t\t\t\tcase IDCANCEL:\n\t\t\t\t\tbreak; /*compiling will use whatever is in memory*/\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t/*not modified on disk, but modified in memory? try and save it, cos we might as well*/\n\t\t\t\tEditorSave(editor);\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\t/*modified on disk but not in memory? just reload it off disk*/\n\t\t\tif (EditorModified(editor))\n\t\t\t\tEditorReload(editor);\n\t\t}\n\t}\n\n\tmemset(&funcs, 0, sizeof(funcs));\n\tfuncs.funcs.parms = &ext;\n\tmemset(&ext, 0, sizeof(ext));\n\text.ReadFile = GUIReadFile;\n\text.FileSize = GUIFileSize;\n\text.WriteFile = QCC_WriteFile;\n\text.Sys_Error = Sys_Error;\n\n\tif (quick)\n\t\text.Printf = Dummyprintf;\n\telse\n\t{\n\t\text.Printf = GUIprintf;\n\t\tGUIprintf(\"\");\n\t}\n\text.DPrintf = ext.Printf;\n\t\n\tif (logfile)\n\t\tfclose(logfile);\n\tif (fl_log && !quick)\n\t\tlogfile = fopen(\"fteqcc.log\", \"wb\");\n\telse\n\t\tlogfile = NULL;\n\n\tif (SplitterGet(outputbox))\n\t\tSendMessage(outputbox, WM_SETREDRAW, FALSE, 0);\n\n\targc = GUI_BuildParms(args, argv, sizeof(argv)/sizeof(argv[0]), quick);\n\tif (!argc)\n\t\text.Printf(\"Too many args\\n\");\n\telse if (CompileParams(&funcs, outputbox?compilecb:NULL, argc, argv))\n\t{\n\t\tif (!quick)\n\t\t{\n\t\t\tEngineGiveFocus();\n\t\t\tEngineCommandf(\"qcresume\\nqcreload\\n\");\n//\t\t\tEngineCommandf(\"qcresume\\nmenu_restart\\nrestart\\n\");\n\t\t}\n\t}\n\n\tif (SplitterGet(outputbox))\n\t{\n\t\tSendMessage(outputbox, WM_SETREDRAW, TRUE, 0);\n\t\tRedrawWindow(outputbox, NULL, NULL, RDW_ERASE | RDW_FRAME | RDW_INVALIDATE | RDW_ALLCHILDREN);\n\t}\n\n\tif (logfile)\n\t\tfclose(logfile);\n}\n\n\nstatic void CreateOutputWindow(pbool doannoates)\n{\n\tgui_doannotates = doannoates;\n\n\tif (!outputbox)\n\t{\n\t\toutputbox = CreateAnEditControl(mainwindow, NULL);\n\t}\n\tSplitterFocus(outputbox, 64, 128);\n}\n\n\nint GrepSubFiles(HTREEITEM node, char *string)\n{\n\tHTREEITEM ch, p;\n\tchar fullname[1024];\n\tchar parentstring[256];\n\tint pl, nl;\n\tTV_ITEM parent;\n\tint found = 0;\n\n\tif (!node)\n\t\treturn found;\n\n\tmemset(&parent, 0, sizeof(parent));\n\t*fullname = 0;\n\tp = node;\n\twhile (p)\n\t{\n\t\tparent.hItem = p;\n\t\tparent.mask = TVIF_TEXT;\n\t\tparent.pszText = parentstring;\n\t\tparent.cchTextMax = sizeof(parentstring)-1;\n\t\tif (!TreeView_GetItem(projecttree, &parent))\n\t\t\tbreak;\n\t\tnl = strlen(fullname);\n\t\tpl = strlen(parent.pszText);\n\t\tif (nl + 1 + pl + 1 > sizeof(fullname))\n\t\t\treturn found;\n\t\tp = TreeView_GetParent(projecttree, p);\n\t\tif (!p && *fullname)\n\t\t\tbreak;\n\n\t\t//ignore the root node, unless we're actually querying that root node.\n\t\tmemmove(fullname+pl+1, fullname, nl+1);\n\t\tmemcpy(fullname, parent.pszText, pl);\n\t\tfullname[pl] = nl?'/':'\\0';\n\t}\n\t//skip the leading progs.src/ if its there, because that's an abstraction and does not match the filesystem.\n\tfound += Grep(fullname, string);\n\n\tch = TreeView_GetChild(projecttree, node);\n\tfound += GrepSubFiles(ch, string);\n\n\tch = TreeView_GetNextSibling(projecttree, node);\n\tfound += GrepSubFiles(ch, string);\n\n\treturn found;\n}\nvoid GrepAllFiles(char *string)\n{\n\tint found;\n\tCreateOutputWindow(false);\n\tGUIprintf(\"\");\n\tfound = GrepSubFiles(TreeView_GetChild(projecttree, TVI_ROOT), string);\n\tif (found)\n\t\tGUIprintf(\"grep found %i occurences\\n\", found);\n\telse\n\t\tGUIprintf(\"grep found nothing\\n\");\n}\nvoid AddSourceFile(const char *parentpath, const char *filename)\n{\n\tchar\t\tstring[1024];\n\n\tHANDLE pi;\n\tTVINSERTSTRUCT item;\n\tTV_ITEM parent;\n\tchar parentstring[256];\n\tchar *slash;\n\n\twhile (!strncmp(filename, \"./\", 2))\n\t\tfilename += 2;\n\n\tQC_strlcpy(string, filename, sizeof(string));\n\n\n\tmemset(&item, 0, sizeof(item));\n\tmemset(&parent, 0, sizeof(parent));\n\n\tpi = item.hParent = TVI_ROOT;\n\titem.hInsertAfter = TVI_LAST;//TVI_SORT;\n\titem.item.pszText = string;\n\titem.item.state = TVIS_EXPANDED;\n\titem.item.stateMask = TVIS_EXPANDED;\n\titem.item.mask = TVIF_TEXT|TVIF_STATE|TVIF_PARAM;\n\n\tif (parentpath && stricmp(parentpath, filename))\n\t{\n\t\titem.hParent = TreeView_GetChild(projecttree, item.hParent);\n\t\tdo\n\t\t{\n\t\t\tparent.hItem = item.hParent;\n\t\t\tparent.mask = TVIF_TEXT;\n\t\t\tparent.pszText = parentstring;\n\t\t\tparent.cchTextMax = sizeof(parentstring)-1;\n\t\t\tif (TreeView_GetItem(projecttree, &parent))\n\t\t\t{\n\t\t\t\tif (!stricmp(parent.pszText, parentpath))\n\t\t\t\t{\n\t\t\t\t\tpi = item.hParent;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t} while((item.hParent=TreeView_GetNextSibling(projecttree, item.hParent)));\n\t}\n\telse\n\t\tparentpath = NULL;\n\n\twhile(item.item.pszText)\n\t{\n\t\tif (parentpath)\n\t\t{\n\t\t\tslash = strchr(item.item.pszText, '/');\n\t\t\tif (slash)\n\t\t\t\t*slash++ = '\\0';\n\t\t}\n\t\telse\n\t\t\tslash = NULL;\n\n\t\titem.hParent = TreeView_GetChild(projecttree, pi); \n\t\tdo\n\t\t{\n\t\t\tparent.hItem = item.hParent;\n\t\t\tparent.mask = TVIF_TEXT;\n\t\t\tparent.pszText = parentstring;\n\t\t\tparent.cchTextMax = sizeof(parentstring)-1;\n\t\t\tif (TreeView_GetItem(projecttree, &parent))\n\t\t\t{\n\t\t\t\tif (!stricmp(parent.pszText, item.item.pszText))\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t} while((item.hParent=TreeView_GetNextSibling(projecttree, item.hParent)));\n\n\t\tif (!item.hParent)\n\t\t{\t//add a directory.\n\t\t\titem.hParent = pi;\n\t\t\titem.item.lParam = !slash;\t//lparam = false if we're only adding this node to get at a child.\n\t\t\titem.item.state = ((*item.item.pszText!='.')?TVIS_EXPANDED:0);\t//directories with a leading . should not be expanded by default\n\t\t\tpi = (HANDLE)SendMessage(projecttree,TVM_INSERTITEM,0,(LPARAM)&item);\n\t\t\titem.hParent = pi;\n\t\t}\n\t\telse pi = item.hParent;\n\n\t\titem.item.pszText = slash;\n\t}\n}\n\n//called when progssrcname has changed.\n//progssrcname should already have been set.\nvoid UpdateFileList(void)\n{\n\tTVINSERTSTRUCT item;\n\tTV_ITEM parent;\n\tmemset(&item, 0, sizeof(item));\n\tmemset(&parent, 0, sizeof(parent));\n\n\tif (projecttree)\n\t{\n\t\tsize_t size;\n\t\tchar *buffer;\n\n\t\tAddSourceFile(NULL, progssrcname);\n\n\t\tbuffer = QCC_ReadFile(progssrcname, NULL, 0, &size);\n\n\t\tpr_file_p = QCC_COM_Parse(buffer);\n\t\tif (*qcc_token == '#')\n\t\t{\n\t\t\t//aaaahhh! newstyle!\n\t\t}\n\t\telse\n\t\t{\n\t\t\tpr_file_p = QCC_COM_Parse(pr_file_p);\t//we dont care about the produced progs.dat\n\t\t\twhile(pr_file_p)\n\t\t\t{\n\t\t\t\tif (*qcc_token == '#')\t//panic if there's preprocessor in there.\n\t\t\t\t\tbreak;\n\n\t\t\t\tAddSourceFile(progssrcname, qcc_token);\n\t\t\t\tpr_file_p = QCC_COM_Parse(pr_file_p);\t//we dont care about the produced progs.dat\n\t\t\t}\n\t\t}\n\t\tfree(buffer);\n\n\t\tRunCompiler(parameters, true);\n\t}\n}\n\nstatic void Packager_MessageCallback(void *ctx, const char *fmt, ...)\n{\n\tva_list\t\tva;\n\tchar\t\tmessage[1024];\n\tva_start (va, fmt);\n\tvsnprintf (message, sizeof(message)-1, fmt, va);\n\tva_end (va);\n\n\toutlen = GUIEmitOutputText(outputbox, outlen, message, strlen(message), RGB(0, 0, 0));\n}\n\nvoid GUI_DoDecompile(void *buf, size_t size)\n{\n\tchar *c = ReadProgsCopyright(buf, size);\n\tif (!c || !*c)\n\t\tc = \"COPYRIGHT OWNER NOT KNOWN\";\t//all work is AUTOMATICALLY copyrighted under the terms of the Berne Convention in all major nations. It _IS_ copyrighted, even if there's no license etc included. Good luck guessing what rights you have.\n\tif (MessageBox(mainwindow, qcva(\"The copyright message from this progs is\\n%s\\n\\nPlease respect the wishes and legal rights of the person who created this.\", c), \"Copyright\", MB_OKCANCEL|MB_DEFBUTTON2|MB_ICONSTOP) == IDOK)\n\t{\n\t\tCreateOutputWindow(true);\n\t\tcompilecb();\n\t\tDecompileProgsDat(progssrcname, buf, size);\n\t\tif (SplitterGet(outputbox))\n\t\t{\n\t\t\tSendMessage(outputbox, WM_SETREDRAW, TRUE, 0);\n\t\t\tRedrawWindow(outputbox, NULL, NULL, RDW_ERASE | RDW_FRAME | RDW_INVALIDATE | RDW_ALLCHILDREN);\n\t\t}\n\n\t\tQCC_SaveVFiles();\n\t}\n}\n\nint WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)\n{\n\tpbool fl_acc;\n\tunsigned int i;\n\tWNDCLASS wndclass;\n\tstatic ACCEL acceleratorlist[] =\n\t{\n\t\t{FCONTROL|FVIRTKEY, 'S', IDM_SAVE},\n\t\t{FCONTROL|FVIRTKEY, 'F', IDM_FIND},\n\t\t{FCONTROL|FVIRTKEY, 'G', IDM_GREP},\n\t\t{FVIRTKEY,\t\t\tVK_F3, IDM_FINDNEXT},\n\t\t{FSHIFT|FVIRTKEY,\tVK_F3, IDM_FINDPREV},\n//\t\t{FVIRTKEY,\t\t\tVK_F4, IDM_NEXTERROR},\n\t\t{FVIRTKEY,\t\t\tVK_F5, IDM_DEBUG_RUN},\n\t\t{FVIRTKEY,\t\t\tVK_F6, IDM_OUTPUT_WINDOW},\n\t\t{FVIRTKEY,\t\t\tVK_F7, IDM_DEBUG_REBUILD},\n\t\t{FVIRTKEY,\t\t\tVK_F8, IDM_DEBUG_SETNEXT},\n\t\t{FVIRTKEY,\t\t\tVK_F9, IDM_DEBUG_TOGGLEBREAK},\n\t\t{FVIRTKEY,\t\t\tVK_F10, IDM_DEBUG_STEPOVER},\n\t\t{FVIRTKEY,\t\t\tVK_F11, IDM_DEBUG_STEPINTO},\n\t\t{FSHIFT|FVIRTKEY,\tVK_F11, IDM_DEBUG_STEPOUT},\n\t\t{FVIRTKEY,\t\t\tVK_F12, IDM_GOTODEF},\n\t\t{FSHIFT|FVIRTKEY,\tVK_F12, IDM_RETURNDEF}\n\t};\n\tint mode;\n\tghInstance= hInstance;\n\n\tstrcpy(enginebinary, \"\");\n\tstrcpy(enginebasedir, \"\");\n\tstrcpy(enginecommandline, \"\");\n\n\tGUI_SetDefaultOpts();\n\tmode = GUI_ParseCommandLine(lpCmdLine, false);\n\n\tif(mode == 1)\n\t{\n\t\tRunCompiler(lpCmdLine, false);\n\t\treturn 0;\n\t}\n\n\tfor (i = 0, fl_acc = false; compiler_flag[i].enabled; i++)\n\t{\n\t\tif (!strcmp(\"acc\", compiler_flag[i].abbrev))\n\t\t{\n\t\t\tfl_acc = !!(compiler_flag[i].flags & FLAG_SETINGUI);\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tInitCommonControls();\n\n\tif (!fl_acc && !*progssrcname)\n\t{\n\t\tstrcpy(progssrcname, \"preprogs.src\");\n\t\tif (QCC_RawFileSize(progssrcname)==-1)\n\t\t\tstrcpy(progssrcname, \"progs.src\");\n\t\tif (QCC_RawFileSize(progssrcname)==-1)\n\t\t{\n\t\t\tchar filename[MAX_PATH];\n\t\t\tchar oldpath[MAX_PATH+10];\n\t\t\tOPENFILENAME ofn;\n\t\t\tmemset(&ofn, 0, sizeof(ofn));\n\t\t\tofn.lStructSize = sizeof(ofn);\n\t\t\tofn.hInstance = ghInstance;\n\t\t\tofn.lpstrFile = filename;\n\t\t\tofn.lpstrTitle = \"Please find progs.src or progs.dat\";\n\t\t\tofn.nMaxFile = sizeof(filename)-1;\n\t\t\tofn.lpstrFilter = \"QuakeC Projects\\0*.src;*.dat\\0All files\\0*.*\\0\";\n\t\t\tmemset(filename, 0, sizeof(filename));\n\t\t\tGetCurrentDirectory(sizeof(oldpath)-1, oldpath);\n\t\t\tofn.lpstrInitialDir = oldpath;\n\t\t\tif (GetOpenFileName(&ofn))\n\t\t\t\tstrcpy(progssrcname, filename);\n\t\t\telse\n\t\t\t{\n\t\t\t\tMessageBox(NULL, \"You didn't select a file\", \"Error\", 0);\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t}\n\t}\n\n\tresetprogssrc = true;\n\n\twndclass.style      = 0;\n\twndclass.lpfnWndProc   = MainWndProc;\n\twndclass.cbClsExtra    = 0;\n\twndclass.cbWndExtra    = 0;\n\twndclass.hInstance     = ghInstance;\n\twndclass.hIcon         = LoadIcon(ghInstance, IDI_ICON_FTEQCC);\n\twndclass.hCursor       = LoadCursor (NULL,IDC_ARROW);\n\twndclass.hbrBackground = (void *)COLOR_WINDOW;\n\twndclass.lpszMenuName  = 0;\n\twndclass.lpszClassName = MDI_WINDOW_CLASS_NAME;\n\tRegisterClass(&wndclass);\n\n\taccelerators = CreateAcceleratorTable(acceleratorlist, sizeof(acceleratorlist)/sizeof(acceleratorlist[0])); \n\n\tmainwindow = CreateWindow(MDI_WINDOW_CLASS_NAME, \"FTE QuakeC compiler\", WS_OVERLAPPEDWINDOW,\n\t\t0, 0, 640, 480, NULL, NULL, ghInstance, NULL);\n\n\tif (mdibox)\n\t{\n\t\tSetWindowText(mainwindow, \"FTE QuakeC Development Suite\");\n\t}\n\n\tif (!mainwindow)\n\t{\n\t\tMessageBox(NULL, \"Failed to create main window\", \"Error\", 0);\n\t\treturn 0;\n\t}\n\n/*\n\toutputbox=CreateWindowEx(WS_EX_CLIENTEDGE,\n\t\t\"EDIT\",\n\t\t\"\",\n\t\tWS_CHILD | ES_READONLY | WS_VISIBLE | \n\t\tWS_VSCROLL | ES_LEFT | ES_WANTRETURN |\n\t\tES_MULTILINE | ES_AUTOVSCROLL,\n\t\t0, 0, 0, 0,\n\t\tmainwindow,\n\t\tNULL,\n\t\tghInstance,\n\t\tNULL);\n*/\n\n\tif (!mdibox)\n\t\toutputbox = CreateAnEditControl(mainwindow, NULL);\n\n\tfor (i = 0; i < NUMBUTTONS; i++)\n\t{\n\t\tif (!buttons[i].text)\n\t\t\tbuttons[i].hwnd = NULL;\n\t\telse\n\t\t\tbuttons[i].hwnd = CreateWindowEx(WS_EX_CLIENTEDGE,\n\t\t\t\t\"BUTTON\",\n\t\t\t\tbuttons[i].text,\n\t\t\t\tWS_CHILD | WS_VISIBLE,\n\t\t\t\t0, 0, 5, 5,\n\t\t\t\tmainwindow,\n\t\t\t\t(HMENU)(LONG_PTR)(i+20),\n\t\t\t\tghInstance,\n\t\t\t\tNULL); \n\t}\n\n\tShowWindow(mainwindow, SW_SHOWDEFAULT);\n\n\tresetprogssrc = true;\n\n\twhile(mainwindow || editors)\n\t{\n\t\tMSG        msg;\n\n\t\tif (resetprogssrc)\n\t\t{\t//this here, with the compiler below, means that we don't run recursivly.\n\t\t\tif (projecttree)\n\t\t\t\tTreeView_DeleteAllItems(projecttree);\n\n\t\t\t//if progssrcname is a path, then change working directory now.\n\t\t\t//this shouldn't affect that much, but should ensure well-defined behaviour.\n\t\t\t{\n\t\t\t\tchar *s, *s2;\n\t\t\t\tstrcpy(progssrcdir, progssrcname);\n\t\t\t\tfor(s = NULL, s2 = progssrcdir; s2;)\n\t\t\t\t{\n\t\t\t\t\tchar *bs = strchr(s2, '\\\\');\n\t\t\t\t\tchar *sl = strchr(s2, '/');\n\t\t\t\t\tif (bs)\n\t\t\t\t\t\ts2 = bs;\n\t\t\t\t\telse if (sl)\n\t\t\t\t\t\ts2 = sl;\n\t\t\t\t\telse\n\t\t\t\t\t\tbreak;\n\t\t\t\t\ts = s2++;\n\t\t\t\t}\n\t\t\t\tif (s)\n\t\t\t\t{\n\t\t\t\t\t*s = '\\0';\n\t\t\t\t\tstrcpy(progssrcname, s+1);\n\t\t\t\t\tSetCurrentDirectory(progssrcdir);\n\t\t\t\t}\n\t\t\t\t*progssrcdir = '\\0';\n\t\t\t}\n\n\t\t\t//reset project/directory options\n\t\t\tGUI_SetDefaultOpts();\n\t\t\tGUI_ParseCommandLine(lpCmdLine, true);\n\t\t\tGUI_RevealOptions();\n\n\t\t\t//if the project is a .dat or .zip then decompile it now (so we can access the 'source')\n\t\t\t{\n\t\t\t\tchar *ext = strrchr(progssrcname, '.');\n\t\t\t\tif (ext && (!QC_strcasecmp(ext, \".dat\") || !QC_strcasecmp(ext, \".pak\") || !QC_strcasecmp(ext, \".zip\") || !QC_strcasecmp(ext, \".pk3\")))\n\t\t\t\t{\n\t\t\t\t\tFILE *f = fopen(progssrcname, \"rb\");\n\t\t\t\t\tif (f)\n\t\t\t\t\t{\n\t\t\t\t\t\tchar *buf;\n\t\t\t\t\t\tsize_t size;\n\n\t\t\t\t\t\tfseek(f, 0, SEEK_END);\n\t\t\t\t\t\tsize = ftell(f);\n\t\t\t\t\t\tfseek(f, 0, SEEK_SET);\n\t\t\t\t\t\tbuf = malloc(size);\n\t\t\t\t\t\tfread(buf, 1, size, f);\n\t\t\t\t\t\tfclose(f);\n\t\t\t\t\t\tQCC_CloseAllVFiles();\n\t\t\t\t\t\tif (!QC_EnumerateFilesFromBlob(buf, size, QCC_EnumerateFilesResult) && !QC_strcasecmp(ext, \".dat\"))\n\t\t\t\t\t\t{\t//its a .dat and contains no .src files\n\t\t\t\t\t\t\tGUI_DoDecompile(buf, size);\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (!QCC_FindVFile(\"progs.src\"))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tvfile_t *f;\n\t\t\t\t\t\t\tchar *archivename = progssrcname;\n\t\t\t\t\t\t\twhile(strchr(archivename, '\\\\'))\n\t\t\t\t\t\t\t\t archivename = strchr(archivename, '\\\\')+1;\n\t\t\t\t\t\t\tAddSourceFile(NULL, archivename);\n\t\t\t\t\t\t\tfor (f = qcc_vfiles; f; f = f->next)\n\t\t\t\t\t\t\t\tAddSourceFile(archivename,\tf->filename);\n\n\t\t\t\t\t\t\tf = QCC_FindVFile(\"progs.dat\");\n\t\t\t\t\t\t\tif (f)\n\t\t\t\t\t\t\t\tGUI_DoDecompile(f->file, f->size);\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\tresetprogssrc = false;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tfree(buf);\n\t\t\t\t\t\tstrcpy(progssrcname, \"progs.src\");\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\tstrcpy(progssrcname, \"progs.src\");\n\n\t\t\t\t\tfor (i = 0; ; i++)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (!strcmp(\"embedsrc\", compiler_flag[i].abbrev))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcompiler_flag[i].flags |= FLAG_SETINGUI;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (fl_compileonstart)\n\t\t\t{\n\t\t\t\tif (resetprogssrc)\n\t\t\t\t{\n\t\t\t\t\tCreateOutputWindow(false);\n\t\t\t\t\tRunCompiler(lpCmdLine, false);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (!mdibox)\n\t\t\t\t{\n\t\t\t\t\tGUIprintf(\"Welcome to FTE QCC\\n\");\n\t\t\t\t\tGUIprintf(\"Source file: \");\n\t\t\t\t\tGUIprintf(progssrcname);\n\t\t\t\t\tGUIprintf(\"\\n\");\n\n\t\t\t\t\tRunCompiler(\"-?\", false);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (resetprogssrc)\n\t\t\t\tUpdateFileList();\n\t\t\tresetprogssrc = false;\n\t\t}\n\n\t\tEditorsRun();\n\n\t\twhile (PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE))\n\t\t{\n\t\t\tif (!GetMessage (&msg, NULL, 0, 0))\n\t\t\t\tbreak;\n\t\t\tif (!mdibox || !TranslateMDISysAccel(mdibox, &msg))\n\t\t\t\tDoTranslateMessage(&msg);\n\t\t}\n\n\t\tif (mainwindow)\n\t\t{\n\t\t\tif (buttons[ID_COMPILE].washit)\n\t\t\t{\n\t\t\t\tCreateOutputWindow(true);\n\t\t\t\tRunCompiler(parameters, false);\n\n\t\t\t\tbuttons[ID_COMPILE].washit = false;\n\t\t\t}\n#ifdef EMBEDDEBUG\n\t\t\tif (buttons[ID_RUN].washit)\n\t\t\t{\n\t\t\t\tbuttons[ID_RUN].washit = false;\n\t\t\t\tRunEngine();\n\t\t\t}\n#endif\n\t\t\tif (buttons[ID_OPTIONS].washit)\n\t\t\t{\n\t\t\t\tbuttons[ID_OPTIONS].washit = false;\n\t\t\t\tOptionsDialog();\n\t\t\t}\n\t\t}\n\n\t\tif (*finddef)\n\t\t{\n\t\t\tGoToDefinition(finddef);\n\t\t\t*finddef = '\\0';\n\t\t}\n\t\tif (*greptext)\n\t\t{\n\t\t\tGrepAllFiles(greptext);\n\t\t\t*greptext = '\\0';\n\t\t}\n\n\t\tSleep(10);\n\t}\n\n\treturn 0;\n}\n"
  },
  {
    "path": "engine/qclib/qccguiqt.cpp",
    "content": "/*Copyright Spoike, license is GPLv2+ */\n\n/*Todo (in no particular order):\n\ttooltips for inspecting variables/types.\n\tvariables/watch list.\n\tinitial open project prompt\n\tshpuld's styling\n\tdecompiler output saving\n\tright-click popup\n\t\tgoto-def\n\t\tgrep-for\n\t\ttoggle-breakpoint\n\t\tset-next\n\tbracket/brace highlights\n\tautoindentation on enter, etc\n\tdifferent displays for non-text files?\n\tutf-16? mneh, who gives a shit\n\tgive focus back to the engine on resume\n*/\n\n\n#ifdef __PIC__\n\t#undef __PIE__\t//QT is being annoying.\n#endif\n#include <QtWidgets>\n#include <Qsci/qsciscintilla.h>\n#include <Qsci/qscilexercpp.h>\n#include <unistd.h>\n#include <sys/stat.h>\n\nextern \"C\"\n{\n#include \"qcc.h\"\n#include \"gui.h\"\n\nextern pbool fl_nondfltopts;\nextern pbool fl_hexen2;\nextern pbool fl_ftetarg;\nextern pbool fl_compileonstart;\nextern pbool fl_showall;\nextern pbool fl_log;\nextern pbool fl_extramargins;\nextern int fl_tabsize;\n\nextern char enginebinary[MAX_OSPATH];\nextern char enginebasedir[MAX_OSPATH];\nextern char enginecommandline[8192];\n\nextern QCC_def_t *sourcefilesdefs[];\nextern int sourcefilesnumdefs;\n};\nstatic char *cmdlineargs;\n\nstatic progfuncs_t guiprogfuncs;\nstatic progexterns_t guiprogexterns;\n\n#undef NULL\n#define NULL nullptr\n\n#undef Sys_Error\n\n//c++ sucks and just pisses me off with its lack of support for void*\ntemplate<class T> inline T cpprealloc(T p, size_t s) {return static_cast<T>(realloc(static_cast<void*>(p),s));};\n#define STRINGIFY2(s) #s\n#define STRINGIFY(s) STRINGIFY2(s)\n\nstatic QProcess *qcdebugger;\nstatic void DebuggerStop(void);\nstatic bool DebuggerSendCommand(const char *msg, ...);\nstatic void DebuggerStart(void);\n\n\nvoid Sys_Error(const char *text, ...)\n{\n\tva_list argptr;\n\tstatic char msg[2048];\n\n\tva_start (argptr,text);\n\tQC_vsnprintf (msg,sizeof(msg)-1, text,argptr);\n\tva_end (argptr);\n\n\tQCC_Error(ERR_INTERNAL, \"%s\", msg);\n}\n\nvoid RunCompiler(const char *args, pbool quick);\nstatic void *QCC_ReadFile(const char *fname, unsigned char *(*buf_get)(void *ctx, size_t len), void *buf_ctx, size_t *out_size)\n{\n\tsize_t len;\n\tunsigned char *buffer;\n\n\tvfile_t *v = QCC_FindVFile(fname);\n\tif (v)\n\t{\n\t\tlen = v->size;\n\t\tif (buf_get)\n\t\t\tbuffer = buf_get(buf_ctx, len+1);\n\t\telse\n\t\t\tbuffer = static_cast<unsigned char *>(malloc(len+1));\n\t\tif (!buffer)\n\t\t\treturn NULL;\n\t\tbuffer[len] = 0;\n\t\tif (len > v->size)\n\t\t\tlen = v->size;\n\t\tmemcpy(buffer, v->file, len);\n\t\tif (out_size)\n\t\t\t*out_size = len;\n\t\treturn buffer;\n\t}\n\n\tauto f = fopen(fname, \"rb\");\n\tif (!f)\n\t{\n\t\tif (out_size)\n\t\t\t*out_size = 0;\n\t\treturn nullptr;\n\t}\n\n\tfseek(f, 0, SEEK_END);\n\tlen = ftell(f);\n\tfseek(f, 0, SEEK_SET);\n\tif (buf_get)\n\t\tbuffer = buf_get(buf_ctx, len+1);\n\telse\n\t\tbuffer = static_cast<unsigned char *>(malloc(len+1));\n\tbuffer[len] = 0;\n\tif (len != fread(buffer, 1, len, f))\n\t{\n\t\tif (!buf_get)\n\t\t\tfree(buffer);\n\t\tbuffer = nullptr;\n\t}\n\tfclose(f);\n\n\tif (out_size)\n\t\t*out_size = len;\n\treturn buffer;\n}\nstatic int PDECL QCC_FileSize (const char *fname)\n{\n\tvfile_t *v = QCC_FindVFile(fname);\n\tif (v)\n\t\treturn v->size;\n\n\tlong    length;\n\tauto f = fopen(fname, \"rb\");\n\tif (!f)\n\t\treturn -1;\n\tfseek(f, 0, SEEK_END);\n\tlength = ftell(f);\n\tfclose(f);\n\treturn length;\n}\nstatic int PDECL QCC_PopFileSize (const char *fname)\n{\t//populates the file list, as well as returning the size\n\textern int qcc_compileactive;\n\tint len = QCC_FileSize(fname);\n\tif (len >= 0 && qcc_compileactive)\n\t{\n\t\tAddSourceFile(compilingrootfile,    fname);\n\t}\n\treturn len;\n}\n\nstatic int PDECL QCC_StatFile (const char *fname, struct stat *sbuf)\n{\n\tvfile_t *v = QCC_FindVFile(fname);\n\tif (v)\n\t{\n\t\tmemset(sbuf, 0, sizeof(*sbuf));\n\t\tsbuf->st_size = v->size;\n\t\treturn 0;\n\t}\n\treturn stat(fname, sbuf);\n}\n\npbool PDECL QCC_WriteFile (const char *name, void *data, int len)\n{\n\tlong    length;\n\tFILE *f;\n\n\tauto *ext = strrchr(name, '.');\n\tif (ext && !stricmp(ext, \".gz\"))\n\t{\n#ifdef AVAIL_ZLIB\n\t\tpbool okay = true;\n\t\tchar out[1024*8];\n\n\t\tz_stream strm = {\n\t\t\tdata,\n\t\t\tlen,\n\t\t\t0,\n\n\t\t\tout,\n\t\t\tsizeof(out),\n\t\t\t0,\n\n\t\t\tNULL,\n\t\t\tNULL,\n\n\t\t\tNULL,\n\t\t\tNULL,\n\t\t\tNULL,\n\n\t\t\tZ_BINARY,\n\t\t\t0,\n\t\t\t0\n\t\t};\n\n\t\tf = fopen(name, \"wb\");\n\t\tif (!f)\n\t\t\treturn false;\n\t\tdeflateInit2(&strm, Z_BEST_COMPRESSION, Z_DEFLATED, MAX_WBITS|16, MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY);\n\t\twhile(okay && deflate(&strm, Z_FINISH) == Z_OK)\n\t\t{\n\t\t\tif (sizeof(out) - strm.avail_out != fwrite(out, 1, sizeof(out) - strm.avail_out, f))\n\t\t\t\tokay = false;\n\t\t\tstrm.next_out = out;\n\t\t\tstrm.avail_out = sizeof(out);\n\t\t}\n\t\tif (sizeof(out) - strm.avail_out != fwrite(out, 1, sizeof(out) - strm.avail_out, f))\n\t\t\tokay = false;\n\t\tdeflateEnd(&strm);\n\t\tfclose(f);\n\t\tif (!okay)\n\t\t\tunlink(name);\n\t\treturn okay;\n#else\n\t\treturn false;\n#endif\n\t}\n\n\tif (QCC_FindVFile(name))\n\t\treturn !!QCC_AddVFile(name, data, len);\n\n\tf = fopen(name, \"wb\");\n\tif (!f)\n\t\treturn false;\n\tlength = fwrite(data, 1, len, f);\n\tfclose(f);\n\n\tif (length != len)\n\t\treturn false;\n\n\treturn true;\n}\n\n//for the project's treeview to work, we need a subclass to provide the info to be displayed\nclass filelist : public QAbstractItemModel\n{\npublic:\n\tstruct filenode_s\n\t{\n\t\tfilenode_s *parent = nullptr;\n\t\tint numchildren;\n\t\tfilenode_s **children;\n\n\t\tchar *name;\n\n\t\t~filenode_s()\n\t\t{\n\t\t\twhile(numchildren)\n\t\t\t{\n\t\t\t\tdelete(children[--numchildren]);\n\t\t\t}\n\t\t\tfree(children);\n\t\t\tfree(name);\n\t\t}\n\t} *root;\n\n\tfilenode_s *getItem(const QModelIndex &idx) const\n\t{\n\t\tif (idx.isValid())\n\t\t\treturn static_cast<filenode_s*>(idx.internalPointer());\n\t\treturn root;\n\t}\n\tvirtual int \trowCount(const QModelIndex &parent = QModelIndex()) const\n\t{\n\t\tconst filenode_s *n = getItem(parent);\n\t\treturn n->numchildren;\n\t}\n\tvirtual int\t\tcolumnCount(const QModelIndex &parent = QModelIndex()) const\n\t{\n\t\treturn 1;\n\t}\n\tvirtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const\n\t{\n\t\tconst filenode_s *n = getItem(index);\n\t\tswitch(role)\n\t\t{\n\t\tcase Qt::DisplayRole:\n\t\t\treturn QVariant(n->name);\n\t\t}\n\t\treturn QVariant();\n\t}\n\tvirtual QModelIndex index(int row, int column, const QModelIndex &parent) const\n\t{\n\t\tfilenode_s *n;\n\t\tif (column)\n\t\t\treturn QModelIndex();\n\t\tn = getItem(parent);\n\t\tif (row >= 0 && row < n->numchildren)\n\t\t\treturn createIndex(row, column, n->children[row]);\n\t\treturn QModelIndex();\n\t}\n\tvirtual QModelIndex parent(const QModelIndex &index) const\n\t{\n\t\tif (!index.isValid())\n\t\t\treturn QModelIndex();\n\n\t\tfilenode_s *n = getItem(index);\n\t\tfilenode_s *p = n->parent;\n\n\t\tif (p == root)\n\t\t\treturn QModelIndex();\n\t\telse\n\t\t{\n\t\t\tint parentrow = 0;\n\t\t\tif (n->parent)\n\t\t\t\twhile (n != n->parent->children[parentrow])\n\t\t\t\t\tparentrow++;\n\t\t\treturn createIndex(parentrow, 0, p);\n\t\t}\n\t}\n\n\tvirtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const\n\t{\n\t\tswitch(role)\n\t\t{\n\t\tcase Qt::DisplayRole:\n\t\t\treturn QVariant(\"Files\");\n\t\t}\n\t\treturn QVariant();\n\t}\n\npublic:\n\tfilelist()\n\t{\n\t\troot = new filenode_s();\n\t}\n\n\tvoid GrepAll(const char *search, filenode_s *n = nullptr)\n\t{\n\t\tif (!n)\n\t\t{\n\t\t\tGUIprintf(\"\");\n\t\t\tGUIprintf(\"Grep for %s\\n\", search);\n\t\t\tn = root;\n\t\t}\n\n\t\tGrep(n->name, search);\n\t\tfor (int i = 0; i < n->numchildren; i++)\n\t\t{\n\t\t\tauto c = n->children[i];\n\t\t\tGrepAll(search, c);\n\t\t}\n\t}\n\n\tfilenode_s *FindChild(filenode_s *p, const char *filename)\n\t{\n\t\tfor (int i = 0; i < p->numchildren; i++)\n\t\t{\n\t\t\tauto c = p->children[i];\n\t\t\tif (!strcasecmp(c->name, filename))\n\t\t\t\treturn c;\n\t\t}\n\t\treturn nullptr;\n\t}\n\tvoid AddFile(const char *parentpath, const char *filename)\n\t{\n\t\tfilenode_s *p = root, *c;\n\n\t\tif (!filename)\n\t\t{\n\t\t\tdelete root;\n\t\t\troot = new filenode_s();\n\t\t\treturn;\n\t\t}\n\n\t\twhile (parentpath && *parentpath)\n\t\t{\n\t\t\tauto sl = strchr(parentpath, '/');\n\n\t\t\tc = FindChild(p, parentpath);\n\t\t\tif (!c)\n\t\t\t\tbreak;\n\t\t\tp = c;\n\n\t\t\tif (sl)\n\t\t\t\tsl++;\n\t\t\tparentpath = sl;\n\t\t}\n\n\t\twhile(!strncmp(filename, \"./\", 2))\n\t\t\tfilename+=2;\n\n\t\tif (p->parent && FindChild(p->parent, filename) == p)\n\t\t\treturn;\t//matches its parent. probably the .src file itself\n\t\tc = FindChild(p, filename);\n\t\tif (c)\n\t\t\treturn;\t//already in there.\n\n\t\tbeginResetModel();\n\t\tc = new filenode_s();\n\t\tc->name = strdup(filename);\n\t\tc->parent = p;\n\n\t\tp->children = cpprealloc(p->children, sizeof(*p->children)*(p->numchildren+1));\n\t\tp->children[p->numchildren] = c;\n\t\tp->numchildren++;\n\t\tendResetModel();\n\t}\n};\n\nclass WrappedQsciScintilla:public QsciScintilla\n{\npublic:\n\tWrappedQsciScintilla(QWidget *parent):QsciScintilla(parent){}\n\n\tchar *WordUnderCursor(char *word, int wordsize, char *term, int termsize, int position)\n\t{\n\t\tunsigned char linebuf[1024];\n\t\tlong charidx;\n\t\tlong lineidx;\n\t\tlong len;\n\t\tpbool noafter = (position==-2);\n\n\t\tif (position == -3)\n\t\t{\n\t\t\tlen = SendScintilla(QsciScintillaBase::SCI_GETSELTEXT, NULL);\n\t\t\tif (len > 0 && len < wordsize)\n\t\t\t{\n\t\t\t\tlen = SendScintilla(QsciScintillaBase::SCI_GETSELTEXT, word);\n\t\t\t\tif (len>0)\n\t\t\t\t\treturn word;\n\t\t\t}\n\t\t}\n\n\t\tif (position < 0)\n\t\t\tposition = SendScintilla(QsciScintillaBase::SCI_GETCURRENTPOS);\n\n\t\tlineidx = SendScintilla(QsciScintillaBase::SCI_LINEFROMPOSITION, position);\n\t\tcharidx = position - SendScintilla(QsciScintillaBase::SCI_POSITIONFROMLINE, lineidx);\n\n\t\tlen = SendScintilla(QsciScintillaBase::SCI_LINELENGTH, lineidx);\n\t\tif (len >= sizeof(linebuf))\n\t\t{\n\t\t\t*word = 0;\n\t\t\treturn word;\n\t\t}\n\t\tlen = SendScintilla(QsciScintillaBase::SCI_GETLINE, lineidx, linebuf);\n\t\tlinebuf[len] = 0;\n\t\tif (charidx >= len)\n\t\t\tcharidx = len-1;\n\n\t\tif (noafter)\t//truncate it here if we're not meant to be reading anything after.\n\t\t\tlinebuf[charidx] = 0;\n\n\t\tif (word)\n\t\t{\n\t\t\t//skip back to the start of the word\n\t\t\twhile(charidx > 0 && (\n\t\t\t\t(linebuf[charidx-1] >= 'a' && linebuf[charidx-1] <= 'z') ||\n\t\t\t\t(linebuf[charidx-1] >= 'A' && linebuf[charidx-1] <= 'Z') ||\n\t\t\t\t(linebuf[charidx-1] >= '0' && linebuf[charidx-1] <= '9') ||\n\t\t\t\tlinebuf[charidx-1] == '_' || linebuf[charidx-1] == ':' ||\n\t\t\t\tlinebuf[charidx-1] >= 128\n\t\t\t\t))\n\t\t\t{\n\t\t\t\tcharidx--;\n\t\t\t}\n\t\t\t//copy the result out\n\t\t\tlineidx = 0;\n\t\t\twordsize--;\n\t\t\twhile (wordsize && (\n\t\t\t\t(linebuf[charidx] >= 'a' && linebuf[charidx] <= 'z') ||\n\t\t\t\t(linebuf[charidx] >= 'A' && linebuf[charidx] <= 'Z') ||\n\t\t\t\t(linebuf[charidx] >= '0' && linebuf[charidx] <= '9') ||\n\t\t\t\tlinebuf[charidx] == '_' || linebuf[charidx] == ':' ||\n\t\t\t\tlinebuf[charidx] >= 128\n\t\t\t\t))\n\t\t\t{\n\t\t\t\tword[lineidx++] = linebuf[charidx++];\n\t\t\t\twordsize--;\n\t\t\t}\n\t\t\tword[lineidx++] = 0;\n\t\t}\n\n\t\tif (term)\n\t\t{\n\t\t\t//skip back to the start of the word\n\t\t\twhile(charidx > 0 && (\n\t\t\t\t(linebuf[charidx-1] >= 'a' && linebuf[charidx-1] <= 'z') ||\n\t\t\t\t(linebuf[charidx-1] >= 'A' && linebuf[charidx-1] <= 'Z') ||\n\t\t\t\t(linebuf[charidx-1] >= '0' && linebuf[charidx-1] <= '9') ||\n\t\t\t\tlinebuf[charidx-1] == '_' || linebuf[charidx-1] == ':' || linebuf[charidx-1] == '.' ||\n\t\t\t\tlinebuf[charidx-1] == '[' || linebuf[charidx-1] == ']' ||\n\t\t\t\tlinebuf[charidx-1] >= 128\n\t\t\t\t))\n\t\t\t{\n\t\t\t\tcharidx--;\n\t\t\t}\n\t\t\t//copy the result out\n\t\t\tlineidx = 0;\n\t\t\ttermsize--;\n\t\t\twhile (termsize && (\n\t\t\t\t(linebuf[charidx] >= 'a' && linebuf[charidx] <= 'z') ||\n\t\t\t\t(linebuf[charidx] >= 'A' && linebuf[charidx] <= 'Z') ||\n\t\t\t\t(linebuf[charidx] >= '0' && linebuf[charidx] <= '9') ||\n\t\t\t\tlinebuf[charidx] == '_' || linebuf[charidx] == ':' || linebuf[charidx] == '.' ||\n\t\t\t\tlinebuf[charidx] == '[' || linebuf[charidx] == ']' ||\n\t\t\t\tlinebuf[charidx] >= 128\n\t\t\t\t))\n\t\t\t{\n\t\t\t\tterm[lineidx++] = linebuf[charidx++];\n\t\t\t\ttermsize--;\n\t\t\t}\n\t\t\tterm[lineidx++] = 0;\n\t\t}\n\t\treturn word;\n\t}\nprotected:\n\tvoid contextMenuEvent(QContextMenuEvent *event);\n};\n\nclass documentlist : public QAbstractListModel\n{\npublic: enum endings_e\n\t{\n\t\tNONE\t= 0,\t//no endings at all yet\n\t\tUNIX\t= 1,\n\t\tMAC\t\t= 2,\n\t\tMIXED\t= 3,\n\t\tWINDOWS = 4,\n\t};\nprivate:\n\n\tWrappedQsciScintilla *s;\t//this is the widget that we load our documents into\n\n\tint numdocuments;\n\tstruct document_s\n\t{\t//these are swapped in/out of the scintilla widget\n\t\tconst char *fname;\n\t\tconst char *shortname;\n\t\ttime_t filemodifiedtime;\n\t\tbool modified;\n\t\tint cursorline;\n\t\tint cursorindex;\n\t\tenum endings_e endings;\t//line endings for this file.\n\t\tint savefmt;\t//encoding to save as\n\t\tQsciDocument doc;\n\t\tQsciLexer *l;\n\t} **docs, *curdoc = nullptr;\n\n\tclass docstacklock\n\t{\n\t\tstruct document_s *oldval;\n\t\tdocumentlist &dl;\n\tpublic:\n\t\tdocstacklock(documentlist *ptr_, struct document_s *newval) : dl(*ptr_)\n\t\t{\t//pick new stuff\n\t\t\toldval = dl.curdoc;\n\t\t\tdl.curdoc = newval;\n\t\t\tdl.s->setDocument(dl.curdoc->doc);\n\t\t}\n\t\t~docstacklock()\n\t\t{\t//restore state to how it used to be\n\t\t\tif (!oldval)\n\t\t\t\treturn;\n\t\t\tdl.curdoc = oldval;\n\t\t\tdl.s->setDocument(dl.curdoc->doc);\n\n\t\t\t//annoying, but it completely loses your position otherwise.\n\t\t\tdl.s->setCursorPosition(dl.curdoc->cursorline-1, dl.curdoc->cursorindex);\n\t\t\tdl.s->ensureCursorVisible();\n\t\t}\n\t};\n\n\tdocument_s *getItem(const QModelIndex &idx) const\n\t{\n\t\tif (idx.isValid())\n\t\t\treturn docs[idx.row()];\n\t\treturn nullptr;\n\t}\n\tvirtual int \trowCount(const QModelIndex &parent = QModelIndex()) const\n\t{\n\t\treturn numdocuments;\n\t}\n\tvirtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const\n\t{\n\t\tconst document_s *n = getItem(index);\n\t\tif(n)\n\t\tswitch(role)\n\t\t{\n\t\tcase Qt::DisplayRole:\n\t\t\tif (n->modified)\n\t\t\t\treturn QVariant(QString::asprintf(\"%s*\", n->fname));\n\t\t\telse\n\t\t\t\treturn QVariant(n->fname);\n\t\t}\n\t\treturn QVariant();\n\t}\n\n\tvirtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const\n\t{\n\t\tswitch(role)\n\t\t{\n\t\tcase Qt::DisplayRole:\n\t\t\treturn QVariant(\"Files\");\n\t\t}\n\t\treturn QVariant();\n\t}\n\tvoid UpdateTitle(void);\npublic:\n\tdocumentlist(WrappedQsciScintilla *editor)\n\t{\n\t\ts = editor;\n\t\tnumdocuments = 0;\n\t\tdocs = nullptr;\n\n\t\tconnect(s, &QsciScintilla::cursorPositionChanged,\n\t\t\t[=](int line, int index)\n\t\t\t{\n\t\t\t\tif (curdoc)\n\t\t\t\t{\n\t\t\t\t\tcurdoc->cursorline = line+1;\n\t\t\t\t\tcurdoc->cursorindex = index;\n\t\t\t\t\tUpdateTitle();\n\t\t\t\t}\n\t\t\t});\n\n\t\tconnect(s, &QsciScintilla::modificationChanged,\n\t\t\t[=](bool m)\n\t\t\t{\n\t\t\t\tif (curdoc)\n\t\t\t\t{\n\t\t\t\t\tcurdoc->modified = m;\n\n\t\t\t\t\tfor(int row = 0; row < numdocuments; row++)\n\t\t\t\t\t{\n\t\t\t\t\t\tif(docs[row] == curdoc)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tauto i = index(row);\n\t\t\t\t\t\t\tthis->dataChanged(i, i);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t});\n\t}\n\n\tvoid SetupScintilla(document_s *ed)\n\t{\n//\t\ted->l = new QsciLexerCPP (s);\n\t\ts->SendScintilla(QsciScintillaBase::SCI_STYLERESETDEFAULT);\n//\t\ts->SendScintilla(QsciScintillaBase::SCI_STYLESETFONT, QsciScintillaBase::STYLE_DEFAULT, \"Consolas\");\n\t\ts->setFont(QFont(QString(\"Consolas\"), 8));\n\t\ts->SendScintilla(QsciScintillaBase::SCI_STYLECLEARALL);\n\n\t\ts->SendScintilla(QsciScintillaBase::SCI_SETCODEPAGE, QsciScintillaBase::SC_CP_UTF8);\n\t\ts->SendScintilla(QsciScintillaBase::SCI_SETLEXER,     QsciScintillaBase::SCLEX_CPP);\n\t\ts->SendScintilla(QsciScintillaBase::SCI_STYLESETFORE, QsciLexerCPP::Default,\t\t\t\t\tQColor(0x00, 0x00, 0x00));\n\t\ts->SendScintilla(QsciScintillaBase::SCI_STYLECLEARALL);\n\t\ts->SendScintilla(QsciScintillaBase::SCI_STYLESETFORE, QsciLexerCPP::Comment,\t\t\t\t\tQColor(0x00, 0x80, 0x00));\n\t\ts->SendScintilla(QsciScintillaBase::SCI_STYLESETFORE, QsciLexerCPP::CommentLine,\t\t\t\tQColor(0x00, 0x80, 0x00));\n\t\ts->SendScintilla(QsciScintillaBase::SCI_STYLESETFORE, QsciLexerCPP::CommentDoc,\t\t\t\t\tQColor(0x00, 0x80, 0x00));\n\t\ts->SendScintilla(QsciScintillaBase::SCI_STYLESETFORE, QsciLexerCPP::Number,\t\t\t\t\t\tQColor(0xA0, 0x10, 0x10));\n\t\ts->SendScintilla(QsciScintillaBase::SCI_STYLESETFORE, QsciLexerCPP::Keyword,\t\t\t\t\t\tQColor(0x00, 0x00, 0xFF));\n\t\ts->SendScintilla(QsciScintillaBase::SCI_STYLESETFORE, QsciLexerCPP::DoubleQuotedString,\t\t\tQColor(0xA0, 0x10, 0x10));\n\t\ts->SendScintilla(QsciScintillaBase::SCI_STYLESETFORE, QsciLexerCPP::SingleQuotedString,\t\t\tQColor(0xA0, 0x10, 0x10));\n//      s->SendScintilla(QsciScintillaBase::SCI_STYLESETFORE, QsciLexerCPP::UUID,\t\t\t\t\t\tQColor(0xA0, 0x10, 0x10));\n\t\ts->SendScintilla(QsciScintillaBase::SCI_STYLESETFORE, QsciLexerCPP::PreProcessor,\t\t\t\tQColor(0x00, 0x00, 0xFF));\n//      s->SendScintilla(QsciScintillaBase::SCI_STYLESETFORE, QsciLexerCPP::Operator,\t\t\t\t\tQColor(0x00, 0x00, 0x00));\n//      s->SendScintilla(QsciScintillaBase::SCI_STYLESETFORE, QsciLexerCPP::Identifier,\t\t\t\t\tQColor(0x00, 0x00, 0x00));\n\t\ts->SendScintilla(QsciScintillaBase::SCI_STYLESETFORE, QsciLexerCPP::UnclosedString,\t\t\t\tQColor(0xA0, 0x10, 0x10));\n\t\ts->SendScintilla(QsciScintillaBase::SCI_STYLESETFORE, QsciLexerCPP::VerbatimString,\t\t\t\tQColor(0xA0, 0x10, 0x10));\n\t\ts->SendScintilla(QsciScintillaBase::SCI_STYLESETFORE, QsciLexerCPP::Regex,\t\t\t\t\t\tQColor(0xA0, 0x10, 0x10));\n\t\ts->SendScintilla(QsciScintillaBase::SCI_STYLESETFORE, QsciLexerCPP::CommentLineDoc,\t\t\t\tQColor(0xA0, 0x10, 0x10));\n\t\ts->SendScintilla(QsciScintillaBase::SCI_STYLESETFORE, QsciLexerCPP::KeywordSet2,\t\t\t\tQColor(0xA0, 0x10, 0x10));\n\t\ts->SendScintilla(QsciScintillaBase::SCI_STYLESETFORE, QsciLexerCPP::CommentDocKeyword,\t\t\tQColor(0xA0, 0x10, 0x10));\n\t\ts->SendScintilla(QsciScintillaBase::SCI_STYLESETFORE, QsciLexerCPP::CommentDocKeywordError,\t\tQColor(0xA0, 0x10, 0x10));\n\t\ts->SendScintilla(QsciScintillaBase::SCI_STYLESETFORE, QsciLexerCPP::GlobalClass,\t\t\t\tQColor(0xA0, 0x10, 0x10));\n\t\ts->SendScintilla(QsciScintillaBase::SCI_STYLESETFORE, QsciLexerCPP::RawString,\t\t\t\t\tQColor(0xA0, 0x00, 0x00));\n\t\ts->SendScintilla(QsciScintillaBase::SCI_STYLESETFORE, QsciLexerCPP::TripleQuotedVerbatimString,\tQColor(0xA0, 0x10, 0x10));\n\t\ts->SendScintilla(QsciScintillaBase::SCI_STYLESETFORE, QsciLexerCPP::HashQuotedString,\t\t\tQColor(0xA0, 0x10, 0x10));\n\t\ts->SendScintilla(QsciScintillaBase::SCI_STYLESETFORE, QsciLexerCPP::PreProcessorComment,\t\tQColor(0xA0, 0x10, 0x10));\n\t\ts->SendScintilla(QsciScintillaBase::SCI_STYLESETFORE, QsciLexerCPP::PreProcessorCommentLineDoc,\tQColor(0xA0, 0x10, 0x10));\n\t\ts->SendScintilla(QsciScintillaBase::SCI_STYLESETFORE, QsciLexerCPP::UserLiteral,\t\t\t\tQColor(0xA0, 0x10, 0x10));\n\t\ts->SendScintilla(QsciScintillaBase::SCI_STYLESETFORE, QsciLexerCPP::TaskMarker,\t\t\t\t\tQColor(0xA0, 0x10, 0x10));\n\t\ts->SendScintilla(QsciScintillaBase::SCI_STYLESETFORE, QsciLexerCPP::EscapeSequence,\t\t\t\tQColor(0xA0, 0x10, 0x10));\n\n\t\ts->SendScintilla(QsciScintillaBase::SCI_STYLESETFORE, QsciScintillaBase::STYLE_BRACELIGHT,\t\tQColor(0x00, 0x00, 0x3F));\n\t\ts->SendScintilla(QsciScintillaBase::SCI_STYLESETBACK, QsciScintillaBase::STYLE_BRACELIGHT,\t\tQColor(0xef, 0xaf, 0xaf));\n\t\ts->SendScintilla(QsciScintillaBase::SCI_STYLESETBOLD, QsciScintillaBase::STYLE_BRACELIGHT,\t\ttrue);\n\t\ts->SendScintilla(QsciScintillaBase::SCI_STYLESETFORE, QsciScintillaBase::STYLE_BRACEBAD,\t\tQColor(0x3F, 0x00, 0x00));\n\t\ts->SendScintilla(QsciScintillaBase::SCI_STYLESETBACK, QsciScintillaBase::STYLE_BRACEBAD,\t\tQColor(0xff, 0xaf, 0xaf));\n\n\t\t//SCE_C_WORD\n\t\ts->SendScintilla(QsciScintillaBase::SCI_SETKEYWORDS,  0ul,\n\t\t\t\t\t\"if else for do not while asm break case const continue \"\n\t\t\t\t\t\"default enum enumflags extern \"\n\t\t\t\t\t\"float goto __in __out __inout noref \"\n\t\t\t\t\t\"nosave shared __state optional string \"\n\t\t\t\t\t\"struct switch thinktime until loop \"\n\t\t\t\t\t\"typedef union var \"\n\t\t\t\t\t\"accessor get set inline \"\n\t\t\t\t\t\"virtual nonvirtual class static nonstatic local return \"\n\t\t\t\t\t\"string float vector void int integer __variant entity\"\n\t\t\t\t\t);\n\n\t\t//SCE_C_WORD2\n\t\t{\n\t\t\tchar buffer[65536];\n\t\t\tGenBuiltinsList(buffer, sizeof(buffer));\n\t\t\ts->SendScintilla(QsciScintillaBase::SCI_SETKEYWORDS,  1ul,  buffer);\n\t\t}\n\t\t//SCE_C_COMMENTDOCKEYWORDERROR\n\t\t//SCE_C_GLOBALCLASS\n\t\ts->SendScintilla(QsciScintillaBase::SCI_SETKEYWORDS,  3ul,\n\t\t\t\t\t\"\"\n\t\t\t\t\t);\n//preprocessor listing\n\t\t{\n\t\t\tchar *deflist = QCC_PR_GetDefinesList();\n\t\t\tif (!deflist)\n\t\t\t\tdeflist = strdup(\"\");\n\t\t\ts->SendScintilla(QsciScintillaBase::SCI_SETKEYWORDS,  4ul,  deflist);\n\t\t\tfree(deflist);\n\t\t}\n\t\t//task markers (in comments only)\n\t\ts->SendScintilla(QsciScintillaBase::SCI_SETKEYWORDS,  5ul,\n\t\t\t\t\t\"TODO FIXME BUG\"\n\t\t\t\t\t);\n\n\t\ts->SendScintilla(QsciScintillaBase::SCI_USEPOPUP, QsciScintillaBase::SC_POPUP_NEVER);    //so we can do right-click menus ourselves.\n\n\t\ts->SendScintilla(QsciScintillaBase::SCI_SETMOUSEDWELLTIME, 1000);\n\t\ts->SendScintilla(QsciScintillaBase::SCI_AUTOCSETORDER, QsciScintillaBase::SC_ORDER_PERFORMSORT);\n\t\ts->SendScintilla(QsciScintillaBase::SCI_AUTOCSETFILLUPS, nullptr, \".,[<>(*/+-=\\t\\n\");\n\n\t\t//Set up gui options.\n\t\ts->SendScintilla(QsciScintillaBase::SCI_SETMARGINWIDTHN,      0, fl_extramargins?40:0);   //line numbers+folding\n\t\ts->SendScintilla(QsciScintillaBase::SCI_SETTABWIDTH,          fl_tabsize);     //tab size\n\n\t\t//add margin for breakpoints\n\t\ts->SendScintilla(QsciScintillaBase::SCI_SETMARGINMASKN,       1, ~QsciScintillaBase::SC_MASK_FOLDERS);\n\t\ts->SendScintilla(QsciScintillaBase::SCI_SETMARGINWIDTHN,      1, 16);\n\t\ts->SendScintilla(QsciScintillaBase::SCI_SETMARGINSENSITIVEN,  1, true);\n\t\t//give breakpoints a nice red circle.\n\t\ts->SendScintilla(QsciScintillaBase::SCI_MARKERDEFINE,         0,  QsciScintillaBase::SC_MARK_CIRCLE);\n\t\ts->SendScintilla(QsciScintillaBase::SCI_MARKERSETFORE,        0,  QColor(0x7F, 0x00, 0x00));\n\t\ts->SendScintilla(QsciScintillaBase::SCI_MARKERSETBACK,        0,  QColor(0xFF, 0x00, 0x00));\n\t\t//give current line a yellow arrow\n\t\ts->SendScintilla(QsciScintillaBase::SCI_MARKERDEFINE,         1,  QsciScintillaBase::SC_MARK_SHORTARROW);\n\t\ts->SendScintilla(QsciScintillaBase::SCI_MARKERSETFORE,        1,  QColor(0xFF, 0xFF, 0x00));\n\t\ts->SendScintilla(QsciScintillaBase::SCI_MARKERSETBACK,        1,  QColor(0x7F, 0x7F, 0x00));\n\t\ts->SendScintilla(QsciScintillaBase::SCI_MARKERDEFINE,         2,  QsciScintillaBase::SC_MARK_BACKGROUND);\n\t\ts->SendScintilla(QsciScintillaBase::SCI_MARKERSETFORE,        2,  QColor(0x00, 0x00, 0x00));\n\t\ts->SendScintilla(QsciScintillaBase::SCI_MARKERSETBACK,        2,  QColor(0xFF, 0xFF, 0x00));\n\t\ts->SendScintilla(QsciScintillaBase::SCI_MARKERSETALPHA,       2,  0x40);\n\n\t\t//add margin for folding\n\n\t\ts->SendScintilla(QsciScintillaBase::SCI_SETPROPERTY,  \"fold\", \"1\");\n\t\ts->SendScintilla(QsciScintillaBase::SCI_SETMARGINWIDTHN,      2, fl_extramargins?16:0);\n\t\ts->SendScintilla(QsciScintillaBase::SCI_SETMARGINMASKN,       2, QsciScintillaBase::SC_MASK_FOLDERS);\n\t\ts->SendScintilla(QsciScintillaBase::SCI_SETMARGINSENSITIVEN,  2, true);\n\t\t//stop the images from being stupid\n\t\ts->SendScintilla(QsciScintillaBase::SCI_MARKERDEFINE,\t\tQsciScintillaBase::SC_MARKNUM_FOLDEROPEN,\t\tQsciScintillaBase::SC_MARK_BOXMINUS);\n\t\ts->SendScintilla(QsciScintillaBase::SCI_MARKERDEFINE,\t\tQsciScintillaBase::SC_MARKNUM_FOLDER,\t\t\tQsciScintillaBase::SC_MARK_BOXPLUS);\n\t\ts->SendScintilla(QsciScintillaBase::SCI_MARKERDEFINE,\t\tQsciScintillaBase::SC_MARKNUM_FOLDERSUB,\t\tQsciScintillaBase::SC_MARK_VLINE);\n\t\ts->SendScintilla(QsciScintillaBase::SCI_MARKERDEFINE,\t\tQsciScintillaBase::SC_MARKNUM_FOLDERTAIL,\t\tQsciScintillaBase::SC_MARK_LCORNERCURVE);\n\t\ts->SendScintilla(QsciScintillaBase::SCI_MARKERDEFINE,\t\tQsciScintillaBase::SC_MARKNUM_FOLDEREND,\t\tQsciScintillaBase::SC_MARK_BOXPLUSCONNECTED);\n\t\ts->SendScintilla(QsciScintillaBase::SCI_MARKERDEFINE,\t\tQsciScintillaBase::SC_MARKNUM_FOLDEROPENMID,\tQsciScintillaBase::SC_MARK_BOXMINUSCONNECTED);\n\t\ts->SendScintilla(QsciScintillaBase::SCI_MARKERDEFINE,\t\tQsciScintillaBase::SC_MARKNUM_FOLDERMIDTAIL,\tQsciScintillaBase::SC_MARK_TCORNERCURVE);\n\t\t//and fuck with colours so that its visible.\n#define FOLDBACK QColor(0x50, 0x50, 0x50)\n\t\ts->SendScintilla(QsciScintillaBase::SCI_MARKERSETFORE,    QsciScintillaBase::SC_MARKNUM_FOLDER,          QColor(0xFF, 0xFF, 0xFF));\n\t\ts->SendScintilla(QsciScintillaBase::SCI_MARKERSETBACK,    QsciScintillaBase::SC_MARKNUM_FOLDER,          FOLDBACK);\n\t\ts->SendScintilla(QsciScintillaBase::SCI_MARKERSETFORE,    QsciScintillaBase::SC_MARKNUM_FOLDEROPEN,      QColor(0xFF, 0xFF, 0xFF));\n\t\ts->SendScintilla(QsciScintillaBase::SCI_MARKERSETBACK,    QsciScintillaBase::SC_MARKNUM_FOLDEROPEN,      FOLDBACK);\n\t\ts->SendScintilla(QsciScintillaBase::SCI_MARKERSETFORE,    QsciScintillaBase::SC_MARKNUM_FOLDEROPENMID,   QColor(0xFF, 0xFF, 0xFF));\n\t\ts->SendScintilla(QsciScintillaBase::SCI_MARKERSETBACK,    QsciScintillaBase::SC_MARKNUM_FOLDEROPENMID,   FOLDBACK);\n\t\ts->SendScintilla(QsciScintillaBase::SCI_MARKERSETBACK,    QsciScintillaBase::SC_MARKNUM_FOLDERSUB,       FOLDBACK);\n\t\ts->SendScintilla(QsciScintillaBase::SCI_MARKERSETFORE,    QsciScintillaBase::SC_MARKNUM_FOLDEREND,       QColor(0xFF, 0xFF, 0xFF));\n\t\ts->SendScintilla(QsciScintillaBase::SCI_MARKERSETBACK,    QsciScintillaBase::SC_MARKNUM_FOLDEREND,       FOLDBACK);\n\t\ts->SendScintilla(QsciScintillaBase::SCI_MARKERSETBACK,    QsciScintillaBase::SC_MARKNUM_FOLDERTAIL,      FOLDBACK);\n\t\ts->SendScintilla(QsciScintillaBase::SCI_MARKERSETBACK,    QsciScintillaBase::SC_MARKNUM_FOLDERMIDTAIL,   FOLDBACK);\n\n\t\t//disable preprocessor tracking, because QC preprocessor is not specific to an individual file, and even if it was, includes would be messy.\n//      s->SendScintilla(QsciScintillaBase::SCI_SETPROPERTY,  (WPARAM)\"lexer.cpp.track.preprocessor\", (LPARAM)\"0\");\n\n\t\tfor (int i = 0; i < 0x100; i++)\n\t\t{\n\t\t\tconst char *lowtab[32] = {\"QNUL\",NULL,NULL,NULL,NULL,\".\",NULL,NULL,NULL,NULL,NULL,\"#\",NULL,\">\",\".\",\".\",\n\t\t\t\t\t\t\t\t\"[\",\"]\",\"0\",\"1\",\"2\",\"3\",\"4\",\"5\",\"6\",\"7\",\"8\",\"9\",\".\",\"<-\",\"-\",\"->\"};\n\t\t\tconst char *hightab[32] = {\"(=\",\"=\",\"=)\",\"=#=\",\"White\",\".\",\"Green\",\"Red\",\"Yellow\",\"Blue\",NULL,\"Purple\",NULL,\">\",\".\",\".\",\n\t\t\t\t\t\t\t\t\"[\",\"]\",\"0\",\"1\",\"2\",\"3\",\"4\",\"5\",\"6\",\"7\",\"8\",\"9\",\".\",\"<-\",\"-\",\"->\"};\n\t\t\tchar foo[4];\n\t\t\tchar bar[4];\n\t\t\tunsigned char c = i&0xff;\n\t\t\tfoo[0] = i; //these are invalid encodings or control chars.\n\t\t\tfoo[1] = 0;\n\n\t\t\tif (c < 32)\n\t\t\t{\n\t\t\t\tif (lowtab[c])\n\t\t\t\t\ts->SendScintilla(QsciScintillaBase::SCI_SETREPRESENTATION,    foo,    lowtab[c]);\n\t\t\t}\n\t\t\telse if (c >= (128|0) && c < (128|32))\n\t\t\t{\n\t\t\t\tif (hightab[c-128])\n\t\t\t\t\ts->SendScintilla(QsciScintillaBase::SCI_SETREPRESENTATION,    foo,    hightab[c-128]);\n\t\t\t}\n\t\t\telse if (c < 128)\n\t\t\t\tcontinue;   //don't do anything weird for ascii (other than control chars)\n\t\t\telse\n\t\t\t{\n\t\t\t\tint b = 0;\n\t\t\t\tbar[b++] = c&0x7f;\n\t\t\t\tbar[b++] = 0;\n\t\t\t\ts->SendScintilla(QsciScintillaBase::SCI_SETREPRESENTATION,    foo,    bar);\n\t\t\t}\n\t\t}\n\n\t\tfor (int i = 0xe000; i < 0xe100; i++)\n\t\t{\n\t\t\tconst char *lowtab[32] = {\"QNUL\",NULL,NULL,NULL,NULL,\".\",NULL,NULL,NULL,NULL,NULL,\"#\",NULL,\">\",\".\",\".\",\n\t\t\t\t\t\t\t\t\"[\",\"]\",\"0\",\"1\",\"2\",\"3\",\"4\",\"5\",\"6\",\"7\",\"8\",\"9\",\".\",\"<-\",\"-\",\"->\"};\n\t\t\tconst char *hightab[32] = {\"(=\",\"=\",\"=)\",\"=#=\",\"White\",\".\",\"Green\",\"Red\",\"Yellow\",\"Blue\",NULL,\"Purple\",NULL,\">\",\".\",\".\",\n\t\t\t\t\t\t\t\t\"[\",\"]\",\"^0\",\"^1\",\"^2\",\"^3\",\"^4\",\"^5\",\"^6\",\"^7\",\"^8\",\"^9\",\".\",\"^<-\",\"^-\",\"^->\"};\n\t\t\tchar foo[4];\n\t\t\tchar bar[4];\n\t\t\tunsigned char c = i&0xff;\n\t\t\tfoo[0] = ((i>>12) & 0xf) | 0xe0;\n\t\t\tfoo[1] = ((i>>6) & 0x3f) | 0x80;\n\t\t\tfoo[2] = ((i>>0) & 0x3f) | 0x80;\n\t\t\tfoo[3] = 0;\n\n\t\t\tif (c < 32)\n\t\t\t{\n\t\t\t\tif (lowtab[c])\n\t\t\t\t\ts->SendScintilla(QsciScintillaBase::SCI_SETREPRESENTATION,    foo,    lowtab[c]);\n\t\t\t}\n\t\t\telse if (c >= (128|0) && c < (128|32))\n\t\t\t{\n\t\t\t\tif (hightab[c-128])\n\t\t\t\t\ts->SendScintilla(QsciScintillaBase::SCI_SETREPRESENTATION,    foo,    hightab[c-128]);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tint b = 0;\n\t\t\t\tif (c >= 128)\n\t\t\t\t\tbar[b++] = '^';\n\t\t\t\tbar[b++] = c&0x7f;\n\t\t\t\tbar[b++] = 0;\n\t\t\t\ts->SendScintilla(QsciScintillaBase::SCI_SETREPRESENTATION,    foo,    bar);\n\t\t\t}\n\t\t}\n\n/*\t\tauto f = fopen(\"scintilla.cfg\", \"rt\");\n\t\tif (f)\n\t\t{\n\t\t\tchar buf[256];\n\t\t\twhile(fgets(buf, sizeof(buf)-1, f))\n\t\t\t{\n\t\t\t\tint msg;\n\t\t\t\tlong lparam;\n\t\t\t\tlong wparam;\n\t\t\t\tchar *c;\n\t\t\t\tbuf[sizeof(buf)-1] = 0;\n\t\t\t\tc = buf;\n\t\t\t\twhile(*c == ' ' || *c == '\\t')\n\t\t\t\t\tc++;\n\t\t\t\tif (c[0] == '#')\n\t\t\t\t\tcontinue;\n\t\t\t\tif (c[0] == '/' && c[1] == '/')\n\t\t\t\t\tcontinue;\n\t\t\t\tif (c[0] == '\\r' || c[0] == '\\n' || !c[0])\n\t\t\t\t\tcontinue;\n\t\t\t\tmsg = strtoul(c, &c, 0);\n\t\t\t\twhile(*c == ' ' || *c == '\\t')\n\t\t\t\t\tc++;\n\t\t\t\tif (*c == '\\\"')\n\t\t\t\t{\n\t\t\t\t\tc++;\n\t\t\t\t\twparam = c;\n\t\t\t\t\tc = strrchr(c, '\\\"');\n\t\t\t\t\tif (c)\n\t\t\t\t\t\t*c++ = 0;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\twparam = strtoul(c, &c, 0);\n\t\t\t\twhile(*c == ' ' || *c == '\\t')\n\t\t\t\t\tc++;\n\t\t\t\tif (*c == '\\\"')\n\t\t\t\t{\n\t\t\t\t\tc++;\n\t\t\t\t\tlparam = c;\n\t\t\t\t\tc = strrchr(c, '\\\"');\n\t\t\t\t\tif (c)\n\t\t\t\t\t\t*c++ = 0;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tlparam = strtoul(c, &c, 0);\n\n\t\t\t\ts->SendScintilla(QsciScintillaBase::msg,  wparam, lparam);\n\t\t\t}\n\t\t\tif (!ftell(f))\n\t\t\t{\n\t\t\t\tfclose(f);\n\t\t\t\tf = fopen(\"scintilla.cfg\", \"wt\");\n\t\t\t\tif (f)\n\t\t\t\t{\n\t\t\t\t\tint i;\n\t\t\t\t\tint val;\n\t\t\t\t\tfor (i = 0; i < STYLE_LASTPREDEFINED; i++)\n\t\t\t\t\t{\n\t\t\t\t\t\tval = s->SendScintilla(QsciScintillaBase::SCI_STYLEGETFORE,   i,  0);\n\t\t\t\t\t\tfprintf(f, \"%i\\t%i\\t%#x\\n\", SCI_STYLESETFORE, i, val);\n\t\t\t\t\t\tval = s->SendScintilla(QsciScintillaBase::SCI_STYLEGETBACK,   i,  0);\n\t\t\t\t\t\tfprintf(f, \"%i\\t%i\\t%#x\\n\", SCI_STYLESETBACK, i, val);\n\t\t\t\t\t\tval = s->SendScintilla(QsciScintillaBase::SCI_STYLEGETBOLD,   i,  0);\n\t\t\t\t\t\tfprintf(f, \"%i\\t%i\\t%#x\\n\", SCI_STYLESETBOLD, i, val);\n\t\t\t\t\t\tval = s->SendScintilla(QsciScintillaBase::SCI_STYLEGETITALIC, i,  0);\n\t\t\t\t\t\tfprintf(f, \"%i\\t%i\\t%#x\\n\", SCI_STYLESETITALIC, i, val);\n\t\t\t\t\t\tval = s->SendScintilla(QsciScintillaBase::SCI_STYLEGETSIZE,   i,  0);\n\t\t\t\t\t\tfprintf(f, \"%i\\t%i\\t%#x\\n\", SCI_STYLESETSIZE, i, val);\n\t\t\t\t\t\tval = s->SendScintilla(QsciScintillaBase::SCI_STYLEGETFONT,   i,  (LPARAM)buf);\n\t\t\t\t\t\tfprintf(f, \"%i\\t%i\\t\\\"%s\\\"\\n\", SCI_STYLESETFONT, i, buf);\n\t\t\t\t\t\tval = s->SendScintilla(QsciScintillaBase::SCI_STYLEGETUNDERLINE,  i,  0);\n\t\t\t\t\t\tfprintf(f, \"%i\\t%i\\t%#x\\n\", SCI_STYLESETUNDERLINE, i, val);\n\t\t\t\t\t\tval = s->SendScintilla(QsciScintillaBase::SCI_STYLEGETCASE,   i,  0);\n\t\t\t\t\t\tfprintf(f, \"%i\\t%i\\t%#x\\n\", SCI_STYLESETCASE, i, val);\n\t\t\t\t\t}\n\t\t\t\t\tfclose(f);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t\tfclose(f);\n\t\t}\n*/\t}\n\n\tvoid SwitchToDocument_Internal(document_s *ed)\n\t{\n\t\tcurdoc = ed;\n\t\ts->setDocument(ed->doc);\n\n\t\tswitch(ed->endings)\n\t\t{\n#ifdef _WIN32\n\t\tcase endings_e::NONE: //new file with no endings, default to windows on windows.\n#endif\n\t\tcase endings_e::WINDOWS: //windows\n\t\t\ts->setEolMode(QsciScintilla::EolMode::EolWindows);\n\t\t\ts->setEolVisibility(false);\n\t\t\tbreak;\n#ifndef _WIN32\n\t\tcase endings_e::NONE: //new file with no endings, default to unix on non-windows.\n#endif\n\t\tcase endings_e::UNIX: //unix\n\t\t\ts->setEolMode(QsciScintilla::EolMode::EolUnix);\n\t\t\ts->setEolVisibility(false);\n\t\t\tbreak;\n\t\tcase endings_e::MAC: //mac. traditionally qccs have never supported this. one of the mission packs has a \\r in the middle of some single-line comment.\n\t\t\ts->setEolMode(QsciScintilla::EolMode::EolMac);\n\t\t\ts->setEolVisibility(false);\n\t\t\tbreak;\n\t\tdefault: //panic! everyone panic!\n\t\t\ts->setEolMode(QsciScintilla::EolMode::EolUnix);\n\t\t\ts->setEolVisibility(true);\n\t\t\tbreak;\n\t\t}\n\n\t\ts->setUtf8(ed->savefmt != UTF_ANSI);\n\t}\n\tvoid ConvertEndings(endings_e newendings)\n\t{\n\t\tcurdoc->endings = newendings;\n\n\t\tswitch(newendings)\n\t\t{\n\t\tcase endings_e::WINDOWS: //windows\n\t\t\ts->convertEols(QsciScintilla::EolWindows);\n\t\t\tbreak;\n\t\tcase endings_e::UNIX: //unix\n\t\t\ts->convertEols(QsciScintilla::EolUnix);\n\t\t\tbreak;\n\t\tcase endings_e::MAC: //mac. traditionally qccs have never supported this. one of the mission packs has a \\r in the middle of some single-line comment.\n\t\t\ts->convertEols(QsciScintilla::EolMac);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tbreak;\t//not real endings.\n\t\t}\n\n\t\tSwitchToDocument_Internal(curdoc);\n\t}\n\tbool CreateDocument(document_s *ed)\n\t{\n\t\tsize_t flensz;\n\t\tauto rawfile = QCC_ReadFile(ed->fname, nullptr, nullptr, &flensz);\n\t\tif (!rawfile)\n\t\t\treturn false;\n\t\tauto flen = flensz;\n\t\tpbool dofree;\n\t\tauto file = QCC_SanitizeCharSet(static_cast<char*>(rawfile), &flen, &dofree, &ed->savefmt);\n\t\tstruct stat sbuf;\n\t\tQCC_StatFile(ed->fname, &sbuf);\n\t\ted->filemodifiedtime = sbuf.st_mtime;\n\n\t\tendings_e endings = endings_e::NONE;\n\t\tchar *e, *stop;\n\t\tfor (e = file, stop=file+flen; e < stop; )\n\t\t{\n\t\t\tif (*e == '\\r')\n\t\t\t{\n\t\t\t\te++;\n\t\t\t\tif (*e == '\\n')\n\t\t\t\t{\n\t\t\t\t\te++;\n\t\t\t\t\tif (endings != endings_e::WINDOWS)\n\t\t\t\t\t\tendings = endings?(endings_e::MIXED):endings_e::WINDOWS;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tif (endings != endings_e::MAC)\n\t\t\t\t\t\tendings = endings?(endings_e::MIXED):endings_e::MAC;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (*e == '\\n')\n\t\t\t{\n\t\t\t\te++;\n\t\t\t\tif (endings != endings_e::UNIX)\n\t\t\t\t\tendings = endings?(endings_e::MIXED):endings_e::UNIX;\n\t\t\t}\n\t\t\telse\n\t\t\t\te++;\n\t\t}\n\t\ted->endings = endings;\n\n\t\tSwitchToDocument_Internal(ed);\n\n\t\tconnect(s, &QsciScintillaBase::SCN_CHARADDED, [=](int charadded)\n\t\t{\n\t\t\tif (charadded == '(')\n\t\t\t{\n\t\t\t\tint pos = s->SendScintilla(QsciScintillaBase::SCI_GETCURRENTPOS);\n\t\t\t\tchar *tooltext = GetCalltipForLine(pos-1);\n\t\t\t\t//tooltip_editor = NULL;\n\t\t\t\tif (tooltext)\n\t\t\t\t\ts->SendScintilla(QsciScintillaBase::SCI_CALLTIPSHOW, pos, tooltext);\n\t\t\t}\n\t\t});\n\n\t\ts->setText(QString(file));\n\n\t\tSetupScintilla(ed);\n\n\t\ts->SendScintilla(QsciScintillaBase::SCI_SETSAVEPOINT);\n\t\ted->modified = false;\n\t\treturn true;\n\t}\n\n\tvoid SwitchToDocument(document_s *ed)\n\t{\n\t\tstruct stat sbuf;\n\t\tQCC_StatFile(ed->fname, &sbuf);\n\t\tif (ed->filemodifiedtime < sbuf.st_mtime)\n\t\t{\n\t\t\tCreateDocument(ed);\n\t\t\treturn;\n\t\t}\n\n\t\tSwitchToDocument_Internal(ed);\n\t}\n\n\tdocument_s *FindFile(const char *filename)\n\t{\n\t\tif (!filename)\n\t\t\treturn curdoc;\n\t\tfor (int i = 0; i < numdocuments; i++)\n\t\t{\n\t\t\tif (!strcasecmp(filename, docs[i]->fname))\n\t\t\t\treturn docs[i];\n\t\t}\n\t\treturn nullptr;\n\t}\n\n\tvoid *getFileData(document_s *d, unsigned char *(*buf_get)(void *ctx, size_t len), void *buf_ctx, size_t *out_size)\n\t{\n\t\tdocstacklock lock(this, d);\n\t\tunsigned char *ret = NULL;\n\t\tauto text = s->text().toUtf8();\n\t\t*out_size = text.length();\n\t\tif (buf_get)\n\t\t\tret = buf_get(buf_ctx, *out_size+1);\n\t\telse\n\t\t\tret = static_cast<unsigned char*>(malloc(*out_size+1));\n\t\tmemcpy(ret, text.data(), *out_size);\n\t\tret[*out_size] = 0;\n\t\treturn ret;\n\t}\n\tint getFileSize(document_s *d)\n\t{\n\t\tdocstacklock lock(this,d);\n\t\tint ret = -1;\n\t\tauto text = s->text().toUtf8();\n\t\tret = text.length();\n\t\treturn ret;\n\t}\n\n\tchar *GetCalltipForLine(int cursorpos)\t//calltips are used for tooltips too, so there's some extra weirdness in here\n\t{\n\t\tstatic char buffer[1024];\n\t\tchar wordbuf[256], *text;\n\t\tchar term[256];\n\t\tchar *defname;\n\t\tdefname = s->WordUnderCursor(wordbuf, sizeof(wordbuf), term, sizeof(term), cursorpos);\n\t\tif (!*defname)\n\t\t\treturn NULL;\n\t\telse if (globalstable.numbuckets)\n\t\t{\n\t\t\tQCC_def_t *def;\n\t\t\tint fno;\n\t\t\tint line;\n\t\t\tint best, bestline;\n\t\t\tchar *macro = QCC_PR_CheckCompConstTooltip(defname, buffer, buffer + sizeof(buffer));\n\t\t\tif (macro && *macro)\n\t\t\t\treturn macro;\n\n\t\t\t/*if (dwell)\n\t\t\t{\n\t\t\t\ttooltip_editor = NULL;\n\t\t\t\t*tooltip_variable = 0;\n\t\t\t\ttooltip_position = 0;\n\t\t\t\t*tooltip_type = 0;\n\t\t\t\t*tooltip_comment = 0;\n\t\t\t}*/\n\n\t\t\tline = s->SendScintilla(QsciScintillaBase::SCI_LINEFROMPOSITION, cursorpos);\n\n\t\t\tfor (best = 0,bestline=0, fno = 1; fno < numfunctions; fno++)\n\t\t\t{\n\t\t\t\tif (line > functions[fno].line && bestline < functions[fno].line)\n\t\t\t\t{\n\t\t\t\t\tif (!strcmp(curdoc->fname, functions[fno].filen))\n\t\t\t\t\t{\n\t\t\t\t\t\tbest = fno;\n\t\t\t\t\t\tbestline = functions[fno].line;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (best)\n\t\t\t{\n\t\t\t\tif (strstr(functions[best].name, \"::\"))\n\t\t\t\t{\n\t\t\t\t\tQCC_type_t *type;\n\t\t\t\t\tchar tmp[256];\n\t\t\t\t\tchar *c;\n\t\t\t\t\tQC_strlcpy(tmp, functions[best].name, sizeof(tmp));\n\t\t\t\t\tc = strstr(tmp, \"::\");\n\t\t\t\t\tif (c)\n\t\t\t\t\t\t*c = 0;\n\t\t\t\t\ttype = QCC_TypeForName(tmp);\n\n\t\t\t\t\tif (type->type == ev_entity)\n\t\t\t\t\t{\n\t\t\t\t\t\tQCC_def_t *def;\n\t\t\t\t\t\tQC_snprintfz(tmp, sizeof(tmp), \"%s::__m%s\", type->name, term);\n\n\t\t\t\t\t\tfor (fno = 0, def = NULL; fno < sourcefilesnumdefs && !def; fno++)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tfor (def = sourcefilesdefs[fno]; def; def = def->next)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tif (def->scope && def->scope != &functions[best])\n\t\t\t\t\t\t\t\t\tcontinue;\n\t//\t\t\t\t\t\t\tOutputDebugString(def->name);\n\t//\t\t\t\t\t\t\tOutputDebugString(\"\\n\");\n\t\t\t\t\t\t\t\tif (!strcmp(def->name, tmp))\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t//FIXME: look at the scope's function to find the start+end of the function and filter based upon that, to show locals\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (def && def->type->type == ev_field)\n\t\t\t\t\t\t{\n\t//\t\t\t\t\t\tQC_strlcpy(tmp, term, sizeof(tmp));\n\t\t\t\t\t\t\tQC_snprintfz(term, sizeof(term), \"self.%s\", tmp);\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tfor (fno = 0, def = NULL; fno < sourcefilesnumdefs && !def; fno++)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tfor (def = sourcefilesdefs[fno]; def; def = def->next)\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tif (def->scope && def->scope != &functions[best])\n\t\t\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t\t\tif (!strcmp(def->name, term))\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t//FIXME: look at the scope's function to find the start+end of the function and filter based upon that, to show locals\n\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (def && def->type->type == ev_field)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tQC_strlcpy(tmp, term, sizeof(tmp));\n\t\t\t\t\t\t\t\tQC_snprintfz(term, sizeof(term), \"self.%s\", tmp);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t//FIXME: we may need to display types too\n\t\t\tfor (fno = 0, def = NULL; fno < sourcefilesnumdefs && !def; fno++)\n\t\t\t{\n\t\t\t\tfor (def = sourcefilesdefs[fno]; def; def = def->next)\n\t\t\t\t{\n\t\t\t\t\tif (def->scope)\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\tif (!strcmp(def->name, defname))\n\t\t\t\t\t{\n\t\t\t\t\t\t//FIXME: look at the scope's function to find the start+end of the function and filter based upon that, to show locals\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (def)\n\t\t\t{\n\t\t\t\tchar typebuf[1024];\n\t\t\t\tchar valuebuf[1024];\n\t\t\t\tif (def->constant && def->type->type == ev_float)\n\t\t\t\t\tQC_snprintfz(valuebuf, sizeof(valuebuf), \" = %g\", def->symboldata[def->ofs]._float);\n\t\t\t\telse if (def->constant && def->type->type == ev_integer)\n\t\t\t\t\tQC_snprintfz(valuebuf, sizeof(valuebuf), \" = %i\", def->symboldata[def->ofs]._int);\n\t\t\t\telse if (def->constant && def->type->type == ev_vector)\n\t\t\t\t\tQC_snprintfz(valuebuf, sizeof(valuebuf), \" = '%g %g %g'\", def->symboldata[def->ofs].vector[0], def->symboldata[def->ofs].vector[1], def->symboldata[def->ofs].vector[2]);\n\t\t\t\telse\n\t\t\t\t\t*valuebuf = 0;\n\t\t\t\t//note function argument names do not persist beyond the function def. we might be able to read the function's localdefs for them, but that's unreliable/broken with builtins where they're most needed.\n\t\t\t\tif (def->comment)\n\t\t\t\t\tQC_snprintfz(buffer, sizeof(buffer)-1, \"%s %s%s\\r\\n%s\", TypeName(def->type, typebuf, sizeof(typebuf)), def->name, valuebuf, def->comment);\n\t\t\t\telse\n\t\t\t\t\tQC_snprintfz(buffer, sizeof(buffer)-1, \"%s %s%s\", TypeName(def->type, typebuf, sizeof(typebuf)), def->name, valuebuf);\n\n\t\t\t\ttext = buffer;\n\t\t\t}\n\t\t\telse\n\t\t\t\ttext = NULL;\n\n\t\t\treturn text;\n\t\t}\n\t\telse\n\t\t\treturn NULL;//\"Type info not available. Compile first.\";\n\t}\n\n\tvoid clearannotates(void)\n\t{\n\t\tfor (int i = 0; i < numdocuments; i++)\n\t\t{\n\t\t\tdocument_s *d = docs[i];\n\t\t\ts->setDocument(d->doc);\n\t\t\ts->clearAnnotations();\n\t\t}\n\t\tif (curdoc)\n\t\t\ts->setDocument(curdoc->doc);\n\t}\n\n\tbool annotate(const char *line)\n\t{\n\t\tauto filename = line+6;\n\t\tauto filenameend = strchr(filename, ':');\n\t\tif (!filenameend) return false;\n\t\tauto linenum = atoi(filenameend+1);\n\t\tline = strchr(filenameend+1, ':')+1;\n\t\tif (!line) return false;\n\t\tif (strncmp(curdoc->fname, filename, filenameend-filename) || curdoc->fname[filenameend-filename])\n\t\t{\n\t\t\tauto d = FindFile(filename);\n\t\t\tif (d)\n\t\t\t{\n\t\t\t\tcurdoc = d;\n\t\t\t\ts->setDocument(d->doc);\n\t\t\t}\n\t\t\telse\n\t\t\t\treturn false;\t//some other file that we're not interested in\n\t\t}\n\n\t\ts->annotate(linenum-1, s->annotation(linenum-1) + line + \"\\n\", 0);\n\t\treturn true;\n\t}\n\n\tbool saveDocument(document_s *d)\n\t{\n\t\tstruct stat sbuf;\n\t\tbool saved = false;\n\n\t\t//wordpad will corrupt any embedded quake chars if we force a bom, because it'll re-save using the wrong char encoding by default.\n\t\tint bomlen = 0;\n\t\tconst char *bom = \"\";\n\t\tif (!d)\n\t\t\td = curdoc;\n\t\tif (!d)\n\t\t\treturn false;\n\n\t\tif (d->savefmt == UTF32BE || d->savefmt == UTF32LE || d->savefmt == UTF16BE)\n\t\t\td->savefmt = UTF16LE;\n\n\t\tif (d->savefmt == UTF8_BOM)\n\t\t{\n\t\t\tbomlen = 3;\n\t\t\tbom = \"\\xEF\\xBB\\xBF\";\n\t\t}\n\t\telse if (d->savefmt == UTF16BE)\n\t\t{\n\t\t\tbomlen = 2;\n\t\t\tbom = \"\\xFE\\xFF\";\n\t\t}\n\t\telse if (d->savefmt == UTF16LE)\n\t\t{\n\t\t\tbomlen = 2;\n\t\t\tbom = \"\\xFF\\xFE\";\n\t\t}\n\t\telse if (d->savefmt == UTF32BE)\n\t\t{\n\t\t\tbomlen = 4;\n\t\t\tbom = \"\\x00\\x00\\xFE\\xFF\";\n\t\t}\n\t\telse if (d->savefmt == UTF32LE)\n\t\t{\n\t\t\tbomlen = 4;\n\t\t\tbom = \"\\xFF\\xFE\\x00\\x00\";\n\t\t}\n\n\t\tdocstacklock lock(this, d);\n\n\t\tauto text = s->text().toUtf8();\n\t\ttext.prepend(bom, bomlen);\n\n\t\t//because wordpad saves in ansi by default instead of the format the file was originally saved in, we HAVE to use ansi without\n\t\tif (d->savefmt != UTF8_BOM && d->savefmt != UTF8_RAW)\n\t\t{\n\t\t\t/*int mchars;\n\t\t\tchar *mc;\n\t\t\tint wchars = MultiByteToWideChar(CP_UTF8, 0, text.data(), text.length(), NULL, 0);\n\t\t\tif (wchars)\n\t\t\t{\n\t\t\t\twchar_t *wc = malloc(wchars * sizeof(wchar_t));\n\t\t\t\tMultiByteToWideChar(CP_UTF8, 0, text.data(), text.length(), wc, wchars);\n\n\t\t\t\tif (d->savefmt == UTF_ANSI)\n\t\t\t\t{\n\t\t\t\t\tmchars = WideCharToMultiByte(CP_ACP, 0, wc, wchars, NULL, 0, \"\", &failed);\n\t\t\t\t\tif (mchars)\n\t\t\t\t\t{\n\t\t\t\t\t\tmc = malloc(mchars);\n\t\t\t\t\t\tWideCharToMultiByte(CP_ACP, 0, wc, wchars, mc, mchars, \"\", &failed);\n\t\t\t\t\t\tif (!failed)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tsaved = QCC_WriteFile(d->filename, mc, mchars))\n\t\t\t\t\t\t}\n\t\t\t\t\t\tfree(mc);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tsaved = QCC_WriteFile(d->filename, wc, wchars);\n\t\t\t\tfree(wc);\n\t\t\t}*/\n\t\t}\n\t\telse\n\t\t\tsaved = QCC_WriteFile(d->fname, text.data(), bomlen+text.length());\n\t\tif (!saved)\n\t\t{\n\t\t\tQMessageBox::critical(NULL, \"Failure\", \"Save failed\\nCheck path and ReadOnly flags\");\n\t\t\treturn false;\n\t\t}\n\t\telse\n\t\t{\n\t\t\ts->SendScintilla(QsciScintillaBase::SCI_SETSAVEPOINT);\n\n\t\t\t/*now whatever is on disk should have the current time*/\n\t\t\t//d->modified = false;\n\t\t\tQCC_StatFile(d->fname, &sbuf);\n\t\t\td->filemodifiedtime = sbuf.st_mtime;\n\n\t\t\t//remove the * in a silly way.\n\t\t\t//d->oldline=~0;\n\t\t\t//UpdateEditorTitle(d);\n\t\t}\n\n\t\tUpdateTitle();\n\t\treturn true;\n\t}\n\n\tbool saveAll(void)\n\t{\n\t\tdocument_s *d;\n\t\tstruct stat sbuf;\n\t\tfor (int i = 0; i < numdocuments; i++)\n\t\t{\n\t\t\td = docs[i];\n\t\t\tQCC_StatFile(d->fname, &sbuf);\n\t\t\tif (d->modified)\n\t\t\t{\n\t\t\t\tif (d->filemodifiedtime != sbuf.st_mtime)\n\t\t\t\t{\n\t\t\t\t\tswitch(QMessageBox::question(nullptr, \"Modification conflict\", QString::asprintf(\"%s is modified in both memory and on disk. Overwrite external modification? (saying no will reload from disk)\", d->fname), QMessageBox::Save|QMessageBox::Reset|QMessageBox::Ignore, QMessageBox::Ignore))\n\t\t\t\t\t{\n\t\t\t\t\tcase QMessageBox::Save:\n\t\t\t\t\t\tif (!saveDocument(d))\n\t\t\t\t\t\t\tQMessageBox::critical(nullptr, \"Error\", QString::asprintf(\"Unable to write %s, file was not saved\", d->fname));\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase QMessageBox::Reset:\n\t\t\t\t\t\tCreateDocument(d);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\tcase QMessageBox::Ignore:\n\t\t\t\t\t\tbreak; /*compiling will use whatever is in memory*/\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t/*not modified on disk, but modified in memory? try and save it, cos we might as well*/\n\t\t\t\t\tif (!saveDocument(d))\n\t\t\t\t\t\tQMessageBox::critical(nullptr, \"Error\", QString::asprintf(\"Unable to write %s, file was not saved\", d->fname));\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t/*modified on disk but not in memory? just reload it off disk*/\n\t\t\t\tif (d->filemodifiedtime != sbuf.st_mtime)\n\t\t\t\t\tCreateDocument(d);\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\tvoid reapplyAllBreakpoints(void)\n\t{\n\t\tfor (int i = 0; i < numdocuments; i++)\n\t\t{\n\t\t\tdocument_s *d = docs[i];\n\t\t\tint line = -1;\n\t\t\ts->setDocument(d->doc);\n\t\t\tfor (;;)\n            {\n                line = s->SendScintilla(QsciScintillaBase::SCI_MARKERNEXT, line, 1);\n                if (line == -1)\n                    break;  //no more.\n                line++;\n\n\t\t\t\tDebuggerSendCommand(\"qcbreakpoint 1 \\\"%s\\\" %i\\n\", d->fname, line);\n            }\n\t\t}\n\t\tif (curdoc)\n\t\t\ts->setDocument(curdoc->doc);\n\t}\n\tvoid toggleBreak(void)\n\t{\n\t\tif (!curdoc)\n\t\t\treturn;\n\t\tint mode = !(s->SendScintilla(QsciScintillaBase::SCI_MARKERGET, curdoc->cursorline-1) & 1);\n\t\ts->SendScintilla(mode?QsciScintillaBase::SCI_MARKERADD:QsciScintillaBase::SCI_MARKERDELETE, curdoc->cursorline-1);\n\n\t\tDebuggerSendCommand(\"qcbreakpoint %i \\\"%s\\\" %i\\n\", mode, curdoc->fname, curdoc->cursorline);\n\t}\n\t/*void setMarkerLine(int linenum)\n\t{\t//moves the 'current-line' marker to the active document and current line\n\t\tfor (int i = 0; i < numdocuments; i++)\n\t\t{\n\t\t\tdocstacklock lock(this, docs[i]);\n\t\t\ts->SendScintilla(QsciScintillaBase::SCI_MARKERDELETEALL, 1);\n\t\t\ts->SendScintilla(QsciScintillaBase::SCI_MARKERDELETEALL, 2);\n\t\t}\n\n\t\tif (linenum <= 0)\n\t\t\treturn;\n\t\ts->SendScintilla(QsciScintillaBase::SCI_MARKERADD, linenum-1, 1);\n\t\ts->SendScintilla(QsciScintillaBase::SCI_MARKERADD, linenum-1, 2);\n\t}*/\n\tvoid setNextStatement(void)\n\t{\n\t\tif (!curdoc)\n\t\t\treturn;\n\t\tif (!qcdebugger)\n\t\t\treturn;\t//can't move it if we're not running.\n\n\t\tDebuggerSendCommand(\"qcjump \\\"%s\\\" %i\\n\", curdoc->fname, curdoc->cursorline);\n\t}\n\tvoid autoComplete(void)\n\t{\n\t\tif (!curdoc)\n\t\t\treturn;\n\n\t\tchar word[256];\n\t\tchar suggestions[65536];\n\t\ts->WordUnderCursor(word, sizeof(word), NULL, 0, -2);\n\t\tif (*word && GenAutoCompleteList(word, suggestions, sizeof(suggestions)))\n\t\t{\n\t\t\ts->SendScintilla(QsciScintillaBase::SCI_AUTOCSETFILLUPS, \".,[<>(*/+-=\\t\\n\");\n\t\t\ts->SendScintilla(QsciScintillaBase::SCI_AUTOCSHOW, strlen(word), suggestions);\n\t\t}\n\n\t\t//s->SendScintilla(QsciScintillaBase::SCI_CALLTIPSHOW, (int)0, \"hello world\");\n\n\t\t//SCI_CALLTIPSHOW pos, \"text\"\n\t}\n\tvoid setNext(void)\n\t{\n\t\tif (!curdoc)\n\t\t\treturn;\n\t\tDebuggerSendCommand(\"qcjump \\\"%s\\\" %i\\n\", curdoc->fname, curdoc->cursorline);\n\t}\n\n\tvoid EditFile(document_s *doc, const char *filename, int linenum=-1, bool setcontrol=false);\n\tvoid EditFile(const char *filename, int linenum=-1, bool setcontrol=false)\n\t{\n\t\tEditFile(NULL, filename, linenum, setcontrol);\n\t}\n\tvoid EditFile(QModelIndex idx, int linenum=-1, bool setcontrol=false)\n\t{\n\t\tauto d = getItem(idx);\n\t\tif (d)\n\t\t\tEditFile(d, d->fname, linenum, setcontrol);\n\t}\n};\n\ntemplate<class T>\nclass keyEnterReceiver : public QObject\n{\n\tvoid(*cb)(T &ctx);\n\tT ctx;\npublic:\n\tkeyEnterReceiver(void(*cb_)(T &ctx), T &ctx_) : cb(cb_), ctx(T(ctx_))\n\t{\n\t}\nprotected:\n\tbool eventFilter(QObject* obj, QEvent* event)\n\t{\n\t\tif (event->type()==QEvent::KeyPress)\n\t\t{\n\t\t\tQKeyEvent* key = static_cast<QKeyEvent*>(event);\n\t\t\tif ( (key->key()==Qt::Key_Enter) || (key->key()==Qt::Key_Return) )\n\t\t\t\tcb(ctx);\n\t\t\telse\n\t\t\t\treturn QObject::eventFilter(obj, event);\n\t\t\treturn true;\n\t\t}\n\t\telse\n\t\t\treturn QObject::eventFilter(obj, event);\n\t\treturn false;\n\t}\n};\n\nclass optionswindow : public QDialog\n{\n\tQWidget *newlineedit(const char *initial, void(*changed)(const QString&))\n\t{\n\t\tauto w = new QLineEdit(initial);\n\t\tconnect(w, &QLineEdit::textEdited, changed);\n\t\treturn w;\n\t}\n\tvoid SetOptsEnabled(QGridLayout *layout, int lev = -1)\n\t{\n\t\tfor(int i = 0; i < layout->count(); i++)\n\t\t{\n\t\t\tauto w = static_cast<QCheckBox*>(layout->itemAt(i)->widget());\n\t\t\tif (!w)\n\t\t\t\tbreak;\n\t\t\tauto id = w->property(\"optnum\").toInt();\n\t\t\tif (lev >= 0 && lev <= 3)\n\t\t\t\tw->setCheckState((lev >= optimisations[id].optimisationlevel)?Qt::Checked:Qt::Unchecked);\n\t\t\telse if (lev == 4)\n\t\t\t{\n\t\t\t\tif (optimisations[id].flags & FLAG_KILLSDEBUGGERS)\n\t\t\t\t\tw->setCheckState(Qt::Unchecked);\n\t\t\t}\n\t\t\telse if (lev == 5)\n\t\t\t\tw->setCheckState((optimisations[id].flags&FLAG_ASDEFAULT)?Qt::Checked:Qt::Unchecked);\n\t\t\tw->setEnabled(fl_nondfltopts);\t//FIXME: should use tristate on a per-setting basis\n\t\t}\n\t}\npublic:\n\t~optionswindow()\n\t{\n\t\tGUI_SaveConfig();\n\t}\n\n\toptionswindow()\n\t{\n\t\tsetModal(true);\n\n\t\tauto toplayout = new QHBoxLayout;\n\t\tauto leftlayout = new QVBoxLayout;\n\t\tauto rightlayout = new QVBoxLayout;\n\n\t\t{\n\t\t\tauto layout = new QGridLayout;\n\t\t\tfor (int i = 0,n=0; optimisations[i].fullname; i++)\n\t\t\t{\n\t\t\t\tif (optimisations[i].flags & FLAG_HIDDENINGUI)\n\t\t\t\t\tcontinue;\n\t\t\t\tauto w = new QCheckBox(optimisations[i].fullname);\n\t\t\t\tw->setProperty(\"optnum\", i);\n\t\t\t\tw->setCheckState((optimisations[i].flags & FLAG_SETINGUI)?Qt::Checked:Qt::Unchecked);\n\t\t\t\tw->setEnabled(!fl_nondfltopts);\n\t\t\t\tconnect(w, &QCheckBox::stateChanged, [=](int v){if (v)optimisations[i].flags |= FLAG_SETINGUI;else optimisations[i].flags &= ~FLAG_SETINGUI;});\n\t\t\t\tlayout->addWidget(w, n>>1, n&1);\n\t\t\t\tn++;\n\t\t\t}\n\t\t\tSetOptsEnabled(layout, -1);\n\t\t\tauto gb = new QGroupBox(\"Optimisations\");\n\t\t\tauto opts = new QVBoxLayout;\n\t\t\topts->addLayout(layout);\n\t\t\tauto optlevels = new QHBoxLayout;\n\t\t\tfor (int i=0; i<6; i++)\n\t\t\t{\n\t\t\t\tconst char *buttons[] = {\"O0\", \"O1\", \"O2\", \"O3\", \"Debug\", \"Default\"};\n\t\t\t\tauto w = new QPushButton(buttons[i]);\n\t\t\t\tif (i == 5)\n\t\t\t\t{\n\t\t\t\t\tw->setCheckable(true);\n\t\t\t\t\tw->setChecked(fl_nondfltopts);\n\t\t\t\t}\n\t\t\t\tconnect(w, &QPushButton::clicked, [=](bool checked)\n\t\t\t\t{\n\t\t\t\t\tif (i == 5)\n\t\t\t\t\t\tfl_nondfltopts = !checked;\n\t\t\t\t\telse\n\t\t\t\t\t\tfl_nondfltopts = true;\n\t\t\t\t\tSetOptsEnabled(layout, i);\n\t\t\t\t});\n\t\t\t\toptlevels->addWidget(w);\n\t\t\t}\n\t\t\topts->addLayout(optlevels);\n\t\t\tgb->setLayout(opts);\n\t\t\tleftlayout->addWidget(gb);\n\t\t}\n\t\t{\n\t\t\tauto layout = new QFormLayout;\n\t\t\tlayout->addRow(\"Engine:\", newlineedit(enginebinary, [](const QString&str){QC_strlcpy(enginebinary, str.toUtf8().data(), sizeof(enginebinary));}));\n\t\t\tlayout->addRow(\"Basedir:\", newlineedit(enginebasedir, [](const QString&str){QC_strlcpy(enginebasedir, str.toUtf8().data(), sizeof(enginebasedir));}));\n\t\t\tlayout->addRow(\"Cmdline:\", newlineedit(enginecommandline, [](const QString&str){QC_strlcpy(enginecommandline, str.toUtf8().data(), sizeof(enginecommandline));}));\n\t\t\tleftlayout->addLayout(layout);\n\t\t}\n\n\t\t{\n\t\t\tauto layout = new QGridLayout;\n\t\t\tint n = 0;\n\n\t\t\tauto w = new QCheckBox(\"HexenC\");\n\t\t\tw->setStatusTip(\"Compile for hexen2.\\nThis changes the opcodes slightly, the progs crc, and enables some additional keywords.\");\n\t\t\tw->setCheckState((fl_hexen2)?Qt::Checked:Qt::Unchecked);\n\t\t\tconnect(w, &QCheckBox::stateChanged, [=](int v){fl_hexen2=!!v;});\n\t\t\tlayout->addWidget(w, n/3, n%3);\n\t\t\tn++;\n\n\t\t\tw = new QCheckBox(\"Extended Instructions\");\n\t\t\tw->setStatusTip(\"Enables the use of additional opcodes, which only FTE supports at this time.\\nThis gives both smaller and faster code, as well as allowing pointers, ints, and other extensions not possible with the vanilla QCVM\");\n\t\t\tw->setCheckState((fl_ftetarg)?Qt::Checked:Qt::Unchecked);\n\t\t\tconnect(w, &QCheckBox::stateChanged, [=](int v){fl_ftetarg=!!v;});\n\t\t\tlayout->addWidget(w, n/3, n%3);\n\t\t\tn++;\n\n\t\t\tfor (int i = 0; compiler_flag[i].fullname; i++)\n\t\t\t{\n\t\t\t\tif (compiler_flag[i].flags & FLAG_HIDDENINGUI)\n\t\t\t\t\tcontinue;\n\t\t\t\tauto w = new QCheckBox(compiler_flag[i].fullname);\n\t\t\t\tw->setCheckState((compiler_flag[i].flags & FLAG_SETINGUI)?Qt::Checked:Qt::Unchecked);\n\t\t\t\tconnect(w, &QCheckBox::stateChanged, [=](int v){if (v)compiler_flag[i].flags |= FLAG_SETINGUI;else compiler_flag[i].flags &= ~FLAG_SETINGUI;});\n\t\t\t\tlayout->addWidget(w, n/3, n%3);\n\t\t\t\tn++;\n\t\t\t}\n\n\t\t\tauto gb = new QGroupBox(\"Compiler Flags\");\n\t\t\tgb->setLayout(layout);\n\t\t\trightlayout->addWidget(gb);\n\n\t\t\tauto argbox = new QTextEdit();\n\t\t\targbox->setText(parameters);\n\t\t\trightlayout->addWidget(argbox);\n\t\t}\n\t\ttoplayout->addLayout(leftlayout);\n\t\ttoplayout->addLayout(rightlayout);\n\n\t\tsetLayout(toplayout);\n\t}\n};\n\nstatic class guimainwindow : public QMainWindow\n{\npublic:\n\tWrappedQsciScintilla s;\n\tQSplitter leftrightsplit;\n\tQSplitter logsplit;\n\tQSplitter leftsplit;\n\tQTreeView files_w;\n\tQListView docs_w;\n\n\tQTextEdit log;\n\tfilelist files;\n\tdocumentlist docs;\n\nprivate:\n\tvoid CreateMenus(void)\n\t{\n\t\t{\n\t\t\tauto fileMenu = menuBar()->addMenu(tr(\"&File\"));\n\n\t\t\t//editor UI things\n\t\t\tauto fileopen = new QAction(tr(\"Open\"), this);\n\t\t\tfileMenu->addAction(fileopen);\n\t\t\tfileopen->setShortcuts(QKeySequence::listFromString(\"Ctrl+O\"));\n\t\t\tconnect(fileopen, &QAction::triggered, [=]()\n\t\t\t\t{\n\t\t\t\t\tGUIprintf(\"Ctrl+O hit\\n\");\n\t\t\t\t\tQMessageBox::critical(nullptr, \"Error\", QString::asprintf(\"Not yet implemented\"));\n\t\t\t\t});\n\t\t\tauto filesave = new QAction(tr(\"Save\"), this);\n\t\t\tfileMenu->addAction(filesave);\n\t\t\tfilesave->setShortcuts(QKeySequence::listFromString(\"Ctrl+S\"));\n\t\t\tconnect(filesave, &QAction::triggered, [=]()\n\t\t\t\t{\n\t\t\t\t\tif (!docs.saveDocument(NULL))\n\t\t\t\t\t\tQMessageBox::critical(nullptr, \"Error\", QString::asprintf(\"Unable to save\"));\n\t\t\t\t});\n\n\t\t\tauto prefs = new QAction(tr(\"&Preferences\"), this);\n\t\t\tfileMenu->addAction(prefs);\n\t\t\tprefs->setShortcuts(QKeySequence::Preferences);\n\t\t\tprefs->setStatusTip(tr(\"Reconfigure stuff\"));\n\t\t\tconnect(prefs, &QAction::triggered, [](){auto w = new optionswindow();w->setAttribute(Qt::WidgetAttribute::WA_DeleteOnClose, true); w->show();});\n\n\t\t\tauto goline = new QAction(tr(\"Go to line\"), this);\n\t\t\tfileMenu->addAction(goline);\n\t\t\tgoline->setShortcuts(QKeySequence::listFromString(\"Ctrl+G\"));\n\t\t\tgoline->setStatusTip(tr(\"Jump to line\"));\n\t\t\tconnect(goline, &QAction::triggered, [=]()\n\t\t\t\t{\n\t\t\t\t\tstruct grepargs_s\n\t\t\t\t\t{\n\t\t\t\t\t\tguimainwindow *mw;\n\t\t\t\t\t\tQDialog *d;\n\t\t\t\t\t\tQLineEdit *t;\n\t\t\t\t\t} args = {this, new QDialog()};\n\t\t\t\t\targs.t = new QLineEdit(QString(\"\"));\n\t\t\t\t\tauto l = new QVBoxLayout;\n\t\t\t\t\tl->addWidget(args.t);\n\t\t\t\t\targs.d->setLayout(l);\n\t\t\t\t\targs.d->setWindowTitle(\"FTEQCC Go To Line\");\n\t\t\t\t\targs.t->installEventFilter(new keyEnterReceiver<grepargs_s>([](grepargs_s &ctx)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tctx.mw->docs.EditFile(NULL, atoi(ctx.t->text().toUtf8().data()));\n\t\t\t\t\t\t\tctx.d->done(0);\n\t\t\t\t\t\t}, args));\n\t\t\t\t\targs.d->show();\n\t\t\t\t});\n\n\t\t\tauto find = new QAction(tr(\"Find In File\"), this);\n\t\t\tfileMenu->addAction(find);\n\t\t\tfind->setShortcuts(QKeySequence::listFromString(\"Ctrl+F\"));\n\t\t\tfind->setStatusTip(tr(\"Search for a token in the current file\"));\n\t\t\tconnect(find, &QAction::triggered, [=]()\n\t\t\t\t{\n\t\t\t\t\tstruct grepargs_s\n\t\t\t\t\t{\n\t\t\t\t\t\tguimainwindow *mw;\n\t\t\t\t\t\tQDialog *d;\n\t\t\t\t\t\tQLineEdit *t;\n\t\t\t\t\t} args = {this, new QDialog()};\n\t\t\t\t\targs.t = new QLineEdit(this->s.selectedText());\n\t\t\t\t\tauto l = new QVBoxLayout;\n\t\t\t\t\tl->addWidget(args.t);\n\t\t\t\t\targs.d->setLayout(l);\n\t\t\t\t\targs.d->setWindowTitle(\"Find In File\");\n\t\t\t\t\targs.t->installEventFilter(new keyEnterReceiver<grepargs_s>([](grepargs_s &ctx)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tctx.mw->s.findFirst(ctx.t->text(), false, false, false, true);\n\t\t\t\t\t\t\tctx.d->done(0);\n\t\t\t\t\t\t}, args));\n\t\t\t\t\targs.d->show();\n\t\t\t\t});\n\t\t\tconnect(new QShortcut(QKeySequence(tr(\"F3\", \"File|FindNext\")), this), &QShortcut::activated,\n\t\t\t\t[=]()\n\t\t\t\t{\n\t\t\t\t\ts.findNext();\n\t\t\t\t});\n\n\t\t\tauto grep = new QAction(tr(\"Find In Project\"), this);\n\t\t\tfileMenu->addAction(grep);\n\t\t\tgrep->setShortcuts(QKeySequence::listFromString(\"Ctrl+Shift+F\"));\n\t\t\tgrep->setStatusTip(tr(\"Search through all project files\"));\n\t\t\tconnect(grep, &QAction::triggered, [=]()\n\t\t\t\t{\n\t\t\t\t\tstruct grepargs_s\n\t\t\t\t\t{\n\t\t\t\t\t\tguimainwindow *mw;\n\t\t\t\t\t\tQDialog *d;\n\t\t\t\t\t\tQLineEdit *t;\n\t\t\t\t\t} args = {this, new QDialog()};\n\t\t\t\t\targs.t = new QLineEdit(this->s.selectedText());\n\t\t\t\t\tauto l = new QVBoxLayout;\n\t\t\t\t\tl->addWidget(args.t);\n\t\t\t\t\targs.d->setLayout(l);\n\t\t\t\t\targs.d->setWindowTitle(\"Find In Project\");\n\t\t\t\t\targs.t->installEventFilter(new keyEnterReceiver<grepargs_s>([](grepargs_s &ctx)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tctx.mw->files.GrepAll(ctx.t->text().toUtf8().data());\n\t\t\t\t\t\t\tctx.d->done(0);\n\t\t\t\t\t\t}, args));\n\t\t\t\t\targs.d->show();\n\t\t\t\t});\n\n\t\t\tauto quit = new QAction(tr(\"Quit\"), this);\n\t\t\tfileMenu->addAction(quit);\n\t\t\tconnect(quit, &QAction::triggered, [=]()\n\t\t\t\t{\n\t\t\t\t\tthis->close();\n\t\t\t\t});\n\t\t}\n\t\t{\n\t\t\tauto editMenu = menuBar()->addMenu(tr(\"&Edit\"));\n\n\t\t\t//undo\n\t\t\t//redo\n\n\t\t\tauto godef = new QAction(tr(\"Go To Definition\"), this);\n\t\t\teditMenu->addAction(godef);\n\t\t\tgodef->setShortcuts(QKeySequence::listFromString(\"F12\"));\n\t\t\tconnect(godef, &QAction::triggered, [=]()\n\t\t\t\t{\n\t\t\t\t\tstruct grepargs_s\n\t\t\t\t\t{\n\t\t\t\t\t\tguimainwindow *mw;\n\t\t\t\t\t\tQDialog *d;\n\t\t\t\t\t\tQLineEdit *t;\n\t\t\t\t\t} args = {this, new QDialog()};\n\t\t\t\t\targs.t = new QLineEdit(this->s.selectedText());\n\t\t\t\t\tauto l = new QVBoxLayout;\n\t\t\t\t\tl->addWidget(args.t);\n\t\t\t\t\targs.d->setLayout(l);\n\t\t\t\t\targs.d->setWindowTitle(\"Go To Definition\");\n\t\t\t\t\targs.t->installEventFilter(new keyEnterReceiver<grepargs_s>([](grepargs_s &ctx)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tGoToDefinition(ctx.t->text().toUtf8().data());\n\t\t\t\t\t\t\tctx.d->done(0);\n\t\t\t\t\t\t}, args));\n\t\t\t\t\targs.d->show();\n\t\t\t\t});\n\n\t\t\tauto autocomplete = new QAction(tr(\"AutoComplete\"), this);\n\t\t\teditMenu->addAction(autocomplete);\n\t\t\tautocomplete->setShortcuts(QKeySequence::listFromString(\"Ctrl+Space\"));\n\t\t\tconnect(autocomplete, &QAction::triggered, [=]()\n\t\t\t\t{\n\t\t\t\t\tdocs.autoComplete();\n\t\t\t\t});\n\t\t\tauto convertunix = new QAction(tr(\"Convert to Unix Endings\"), this);\n\t\t\teditMenu->addAction(convertunix);\n\t\t\tconnect(convertunix, &QAction::triggered, [=]()\n\t\t\t\t{\n\t\t\t\t\tdocs.ConvertEndings(documentlist::endings_e::UNIX);\n\t\t\t\t});\n\t\t\tauto convertdos = new QAction(tr(\"Convert to DOS Endings\"), this);\n\t\t\teditMenu->addAction(convertdos);\n\t\t\tconnect(convertdos, &QAction::triggered, [=]()\n\t\t\t\t{\n\t\t\t\t\tdocs.ConvertEndings(documentlist::endings_e::WINDOWS);\n\t\t\t\t});\n\n\t\t\t//convert to utf-8 chars\n\t\t\t//convert to Quake chars\n\t\t}\n\t\t{\n\t\t\tauto debugMenu = menuBar()->addMenu(tr(\"&Debug\"));\n\n\t\t\tauto debugrebuild = new QAction(tr(\"Rebuild\"), this);\n\t\t\tdebugMenu->addAction(debugrebuild);\n\t\t\tdebugrebuild->setShortcuts(QKeySequence::listFromString(\"F7\"));\n\t\t\tconnect(debugrebuild, &QAction::triggered, [=]()\n\t\t\t\t{\n\t\t\t\t\tRunCompiler(parameters, false);\n\t\t\t\t});\n\t\t\tauto debugsetnext = new QAction(tr(\"Set Next Statement\"), this);\n\t\t\tdebugMenu->addAction(debugsetnext);\n\t\t\tdebugsetnext->setShortcuts(QKeySequence::listFromString(\"F8\"));\n\t\t\tconnect(debugsetnext, &QAction::triggered, [=]()\n\t\t\t\t{\n\t\t\t\t\tdocs.setNextStatement();\n\t\t\t\t});\n\t\t\tauto debugresume = new QAction(tr(\"Resume\"), this);\n\t\t\tdebugMenu->addAction(debugresume);\n\t\t\tdebugresume->setShortcuts(QKeySequence::listFromString(\"F5\"));\n\t\t\tconnect(debugresume, &QAction::triggered, [=]()\n\t\t\t\t{\n\t\t\t\t\tif (!DebuggerSendCommand(\"qcresume\\n\"))\n\t\t\t\t\t\tDebuggerStart();\t//unable to send? assume its not running.\n\t\t\t\t});\n\t\t\tauto debugover = new QAction(tr(\"Step Over\"), this);\n\t\t\tdebugMenu->addAction(debugover);\n\t\t\tdebugover->setShortcuts(QKeySequence::listFromString(\"F10\"));\n\t\t\tconnect(debugover, &QAction::triggered, [=]()\n\t\t\t\t{\n\t\t\t\t\tif (!DebuggerSendCommand(\"qcstep over\\n\"))\n\t\t\t\t\t\tDebuggerStart();\n\t\t\t\t});\n\t\t\tauto debuginto = new QAction(tr(\"Step Into\"), this);\n\t\t\tdebugMenu->addAction(debuginto);\n\t\t\tdebuginto->setShortcuts(QKeySequence::listFromString(\"F11\"));\n\t\t\tconnect(debuginto, &QAction::triggered, [=]()\n\t\t\t\t{\n\t\t\t\t\tif (!DebuggerSendCommand(\"qcstep into\\n\"))\n\t\t\t\t\t\tDebuggerStart();\n\t\t\t\t});\n\t\t\tauto debugout = new QAction(tr(\"Step Out\"), this);\n\t\t\tdebugMenu->addAction(debugout);\n\t\t\tdebugout->setShortcuts(QKeySequence::listFromString(\"Shift+F11\"));\n\t\t\tconnect(debugout, &QAction::triggered, [=]()\n\t\t\t\t{\n\t\t\t\t\tif (!DebuggerSendCommand(\"qcstep out\\n\"))\n\t\t\t\t\t\tDebuggerStart();\n\t\t\t\t});\n\t\t\tauto debugbreak = new QAction(tr(\"Toggle Breakpoint\"), this);\n\t\t\tdebugMenu->addAction(debugbreak);\n\t\t\tdebugbreak->setShortcuts(QKeySequence::listFromString(\"F9\"));\n\t\t\tconnect(debugbreak, &QAction::triggered, [=]()\n\t\t\t\t{\n\t\t\t\t\tdocs.toggleBreak();\n\t\t\t\t});\n\t\t}\n\t}\n\npublic:\n\t~guimainwindow()\n\t{\t//if we're dying, make sure there's no engine waiting for us\n\t\tDebuggerStop();\n\t}\n\tguimainwindow() : s(this), leftrightsplit(Qt::Horizontal, this), logsplit(Qt::Vertical, this), leftsplit(Qt::Vertical, this), files_w(this), docs_w(this), docs(&s)\n\t{\n\t\tsetWindowTitle(QString(\"FTEQCC Gui\"));\n\n\t\ts.setReadOnly(true);\n\n\t\tfiles_w.setModel(&files);\n\t\tconnect(&files_w, &QTreeView::clicked,\n\t\t\t[=](const QModelIndex &index)\n\t\t\t{\n\t\t\t\tdocs.EditFile(files.getItem(index)->name);\n\t\t\t});\n\n\t\tleftrightsplit.addWidget(&leftsplit);\n\t\tleftrightsplit.addWidget(&logsplit);\n\t\tleftsplit.addWidget(&files_w);\n\t\tleftsplit.addWidget(&docs_w);\n\t\tdocs_w.setModel(&docs);\n\t\tlogsplit.addWidget(&s);\n\t\tlogsplit.addWidget(&log);\n\t\tQList<int> sizes;\n\t\tsizes.append(64);\n\t\tsizes.append(256);\n\t\tleftrightsplit.setSizes(sizes);\n\t\tQList<int> sizes2;\n\t\tsizes.append(1);\n\t\tsizes.append(0);\n\t\tlogsplit.setSizes(sizes2);\n\t\tsetCentralWidget(&leftrightsplit);\n\n\t\tlog.setReadOnly(true);\n\t\tlog.clear();\n\n\t\tconnect(&docs_w, &QAbstractItemView::clicked,\n\t\t\t[=](const QModelIndex &index)\n\t\t\t{\n\t\t\t\tdocs.EditFile(index);\n\t\t\t});\n\n\t\tconnect(&log, &QTextEdit::selectionChanged,\n\t\t\t[=]()\n\t\t\t{\n\t\t\t\tauto foo = log.textCursor();\n\t\t\t\tfoo.select(QTextCursor::LineUnderCursor);\n\t\t\t\tauto txt = foo.selectedText();\n\t\t\t\tauto colon = txt.indexOf(':');\n\t\t\t\tif (colon>0)\n\t\t\t\t{\n\t\t\t\t\tauto colon2 = txt.indexOf(':', colon+1);\n\t\t\t\t\tif (colon2>0)\n\t\t\t\t\t{\n\t\t\t\t\t\tauto line = txt.mid(colon+1, colon2-colon-1).toInt();\n\t\t\t\t\t\tEditFile(txt.mid(0, colon).toUtf8().data(), line, true);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\tEditFile(txt.mid(0, colon).toUtf8().data(), -1, true);\n\t\t\t\t}\n\t\t\t});\n\n\t\tCreateMenus();\n\t}\n} *mainwnd;\n\n\nvoid WrappedQsciScintilla::contextMenuEvent(QContextMenuEvent *event)\n{\n\tQMenu *menu = createStandardContextMenu();\n\tstatic char blah[256];\n\n\tif (*WordUnderCursor(blah, sizeof(blah), NULL, 0, -3))\n\t{\n\t\tmenu->addSeparator();\n\t\tconnect(menu->addAction(tr(\"Go to definition\")), &QAction::triggered, [=]()\n\t\t{\n\t\t\tGoToDefinition(blah);\n\t\t});\n\t\tconnect(menu->addAction(tr(\"Find Usages\")), &QAction::triggered, [=]()\n\t\t{\n\t\t\tmainwnd->files.GrepAll(blah);\n\t\t});\n\t}\n\n\tmenu->addSeparator();\n\tconnect(menu->addAction(tr(\"Toggle Breakpoint\")), &QAction::triggered, [=]()\n\t{\n\t\tmainwnd->docs.toggleBreak();\n\t});\n\tif (qcdebugger)\n\t{\n\t\tconnect(menu->addAction(tr(\"Set Next Statement\")), &QAction::triggered, [=]()\n\t\t{\n\t\t\tmainwnd->docs.setNextStatement();\n\t\t});\n\t\tconnect(menu->addAction(tr(\"Resume\")), &QAction::triggered, [=]()\n\t\t{\n\t\t});\n\t}\n\telse\n\t{\n\t\tconnect(menu->addAction(tr(\"Start Debugging\")), &QAction::triggered, [=]()\n\t\t{\n\t\t\tDebuggerStart();\n\t\t});\n\t}\n\n\tmenu->exec(event->globalPos());\n\tdelete menu;\n}\n\n//called when progssrcname has changed.\n//progssrcname should already have been set.\nvoid UpdateFileList(void)\n{\n\tchar *buffer;\n\n\tAddSourceFile(nullptr, progssrcname);\n\n\tsize_t size;\n\tbuffer = static_cast<char*>(QCC_ReadFile(progssrcname, nullptr, nullptr, &size));\n\n\tpr_file_p = QCC_COM_Parse(buffer);\n\tif (*qcc_token == '#')\n\t{\n\t\t//aaaahhh! newstyle!\n\t}\n\telse\n\t{\n\t\tpr_file_p = QCC_COM_Parse(pr_file_p);   //we dont care about the produced progs.dat\n\t\twhile(pr_file_p)\n\t\t{\n\t\t\tif (*qcc_token == '#')  //panic if there's preprocessor in there.\n\t\t\t\tbreak;\n\n\t\t\tAddSourceFile(progssrcname, qcc_token);\n\t\t\tpr_file_p = QCC_COM_Parse(pr_file_p);   //we dont care about the produced progs.dat\n\t\t}\n\t}\n\tfree(buffer);\n\n\t//handle any #includes in there\n\tRunCompiler(parameters, true);\n\n\t//expand everything, so the user doesn't get annoyed.\n\tmainwnd->files_w.expandAll();\n}\nvoid AddSourceFile(const char *parentpath, const char *filename)\n{\n\tmainwnd->files.AddFile(parentpath, filename);\n}\n\nstatic int Dummyprintf(const char *msg, ...){return 0;}\n\nvoid RunCompiler(const char *args, pbool quick)\n{\n\tstatic FILE *logfile;\n\tconst char *argv[256];\n\tint argc;\n\n\tmainwnd->docs.saveAll();\n\n\tmemset(&guiprogfuncs, 0, sizeof(guiprogfuncs));\n\tguiprogfuncs.funcs.parms = &guiprogexterns;\n\tmemset(&guiprogexterns, 0, sizeof(guiprogexterns));\n\tguiprogexterns.ReadFile = GUIReadFile;\n\tguiprogexterns.FileSize = GUIFileSize;\n\tguiprogexterns.WriteFile = QCC_WriteFile;\n\tguiprogexterns.Sys_Error = Sys_Error;\n\n\tif (quick)\n\t\tguiprogexterns.Printf = Dummyprintf;\n\telse\n\t{\n\t\tguiprogexterns.Printf = GUIprintf;\n\t\tGUIprintf(\"\");\n\t}\n\tguiprogexterns.DPrintf = guiprogexterns.Printf;\n\n\tif (logfile)\n\t\tfclose(logfile);\n\tif (fl_log && !quick)\n\t\tlogfile = fopen(\"fteqcc.log\", \"wb\");\n\telse\n\t\tlogfile = NULL;\n\n\targc = GUI_BuildParms(args, argv, sizeof(argv)/sizeof(argv[0]), quick);\n\tif (!argc)\n\t\tguiprogexterns.Printf(\"Too many args\\n\");\n\telse if (CompileParams(&guiprogfuncs, NULL, argc, argv))\n\t{\n\t\tif (!quick)\n\t\t{\n\t\t\t//DebuggerGiveFocus();\n\t\t\tDebuggerSendCommand(\"qcresume\\nqcreload\\n\");\n\t\t}\n\t}\n\n\tif (logfile)\n\t{\n\t\tfclose(logfile);\n\t\tlogfile = NULL;\n\t}\n}\n\nvoid GUI_DoDecompile(void *buf, size_t size)\n{\n\tconst char *c = ReadProgsCopyright((char*)buf, size);\n\tif (!c || !*c)\n\t\tc = \"COPYRIGHT OWNER NOT KNOWN\";\t//all work is AUTOMATICALLY copyrighted under the terms of the Berne Convention in all major nations. It _IS_ copyrighted, even if there's no license etc included. Good luck guessing what rights you have.\n\tif (QMessageBox::Open == QMessageBox::question(mainwnd, \"Copyright\", QString::asprintf(\"The copyright message from this progs is\\n%s\\n\\nPlease respect the wishes and legal rights of the person who created this.\", c), QMessageBox::Open|QMessageBox::Cancel, QMessageBox::Cancel))\n\t{\n\t\textern pbool qcc_vfiles_changed;\n\t\textern vfile_t *qcc_vfiles;\n\n\t\tGUIprintf(\"\");\n\n\t\tDecompileProgsDat(progssrcname, buf, size);\n\n\t\tif (qcc_vfiles_changed)\n\t\t{\n\t\t\tswitch (QMessageBox::question(mainwnd, \"Decompile\", \"Save as archive?\", QMessageBox::Yes|QMessageBox::SaveAll|QMessageBox::Ignore, QMessageBox::Ignore))\n\t\t\t{\n\t\t\tcase QMessageBox::Yes:\n\t\t\t\t{\n\t\t\t\t\tQString fname = QFileDialog::getSaveFileName(mainwnd, \"Output Archive\", QString(), \"Zips (*.zip)\");\n\t\t\t\t\tif (!fname.isNull())\n\t\t\t\t\t{\n\t\t\t\t\t\tint h = SafeOpenWrite(fname.toUtf8().data(), -1);\n\n\t\t\t\t\t\tmemset(&guiprogfuncs, 0, sizeof(guiprogfuncs));\n\t\t\t\t\t\tguiprogfuncs.funcs.parms = &guiprogexterns;\n\t\t\t\t\t\tmemset(&guiprogexterns, 0, sizeof(guiprogexterns));\n\t\t\t\t\t\tguiprogexterns.ReadFile = GUIReadFile;\n\t\t\t\t\t\tguiprogexterns.FileSize = GUIFileSize;\n\t\t\t\t\t\tguiprogexterns.WriteFile = QCC_WriteFile;\n\t\t\t\t\t\tguiprogexterns.Sys_Error = Sys_Error;\n\t\t\t\t\t\tguiprogexterns.Printf = GUIprintf;\n\n\t\t\t\t\t\tqccprogfuncs = &guiprogfuncs;\n\t\t\t\t\t\tWriteSourceFiles(qcc_vfiles, h, true, false);\n\t\t\t\t\t\tqccprogfuncs = NULL;\n\n\t\t\t\t\t\tSafeClose(h);\n\n\t\t\t\t\t\tqcc_vfiles_changed = false;\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase QMessageBox::SaveAll:\n\t\t\t\t{\n\t\t\t\t\tQString path = QFileDialog::getExistingDirectory(mainwnd, \"Where do you want to save the decompiled code?\", QString());\n\t\t\t\t\tfor (vfile_t *f = qcc_vfiles; f; f = f->next)\n\t\t\t\t\t{\n\t\t\t\t\t\tchar nname[MAX_OSPATH];\n\t\t\t\t\t\tint h;\n\t\t\t\t\t\tQC_snprintfz(nname, sizeof(nname), \"%s/%s\", path.toUtf8().data(), f->filename);\n\t\t\t\t\t\th = SafeOpenWrite(f->filename, -1);\n\n\t\t\t\t\t\tif (h >= 0)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tSafeWrite(h, f->file, f->size);\n\t\t\t\t\t\t\tSafeClose(h);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n}\n\nstatic void QCC_EnumerateFilesResult(const char *name, const void *compdata, size_t compsize, int method, size_t plainsize)\n{\n\tauto buffer = new char[plainsize];\n\tif (QC_decode(nullptr, compsize, plainsize, method, compdata, buffer))\n\t\tQCC_AddVFile(name, buffer, plainsize);\n\n\tdelete [] buffer;\n}\n\nstatic void SetMainSrcFile(const char *src)\n{\n\t//if its a path, chdir to it instead\n\tconst char *sl = strrchr(src, '/');\n\tif (sl)\n\t{\n\t\tsl++;\n\t\tauto gah = static_cast<char*>(malloc(sl-src+1));\n\t\tmemcpy(gah, src, sl-src);\n\t\tgah[sl-src] = 0;\n\t\tchdir(gah);\n\t\tfree(gah);\n\t\tsrc = sl;\n\t}\n\n\tstrcpy(progssrcname, src);\n\n\tQCC_CloseAllVFiles();\n\n\tGUI_SetDefaultOpts();\n\tGUI_ParseCommandLine(cmdlineargs, true);\n\tGUI_RevealOptions();\n\n\t//if the project is a .dat or .zip then decompile it now (so we can access the 'source')\n\t{\n\t\tchar *ext = strrchr(progssrcname, '.');\n\t\tif (ext && (!QC_strcasecmp(ext, \".dat\") || !QC_strcasecmp(ext, \".pak\") || !QC_strcasecmp(ext, \".zip\") || !QC_strcasecmp(ext, \".pk3\")))\n\t\t{\n\t\t\tFILE *f = fopen(progssrcname, \"rb\");\n\t\t\tif (f)\n\t\t\t{\n\t\t\t\tsize_t size;\n\n\t\t\t\tfseek(f, 0, SEEK_END);\n\t\t\t\tsize = ftell(f);\n\t\t\t\tfseek(f, 0, SEEK_SET);\n\t\t\t\tauto buf = new char[size];\n\t\t\t\tfread(buf, 1, size, f);\n\t\t\t\tfclose(f);\n\t\t\t\tif (!QC_EnumerateFilesFromBlob(buf, size, QCC_EnumerateFilesResult) && !QC_strcasecmp(ext, \".dat\"))\n\t\t\t\t{   //its a .dat and contains no .src files\n\t\t\t\t\tGUI_DoDecompile(buf, size);\n\t\t\t\t}\n\t\t\t\telse if (!QCC_FindVFile(\"progs.src\"))\n\t\t\t\t{\n\t\t\t\t\tvfile_t *f;\n\t\t\t\t\tchar *archivename = progssrcname;\n\t\t\t\t\twhile(strchr(archivename, '\\\\'))\n\t\t\t\t\t\t archivename = strchr(archivename, '\\\\')+1;\n\t\t\t\t\tAddSourceFile(NULL, archivename);\n//\t\t\t\t\tfor (f = qcc_vfiles; f; f = f->next)\n//\t\t\t\t\t\tAddSourceFile(archivename,  f->filename);\n\n\t\t\t\t\tf = QCC_FindVFile(\"progs.dat\");\n\t\t\t\t\tif (f)\n\t\t\t\t\t\tGUI_DoDecompile(f->file, f->size);\n\t\t\t\t}\n\t\t\t\tdelete [] buf;\n\t\t\t\tstrcpy(progssrcname, \"progs.src\");\n\t\t\t}\n\t\t\telse\n\t\t\t\tstrcpy(progssrcname, \"progs.src\");\n\n\t\t\tfor (int i = 0; ; i++)\n\t\t\t{\n\t\t\t\tif (!strcmp(\"embedsrc\", compiler_flag[i].abbrev))\n\t\t\t\t{\n\t\t\t\t\tcompiler_flag[i].flags |= FLAG_SETINGUI;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t//then populate the file list.\n\tUpdateFileList();\n\tEditFile(progssrcname, -1, false);\n}\n\nint main(int argc, char* argv[])\n{\n\t//handle initial commandline args\n\tGUI_SetDefaultOpts();\n\t{\n\t\tsize_t argl = 1;\n\t\tfor (int i = 1; i < argc; i++)\n\t\t\targl += strlen(argv[i])+3;\n\t\tcmdlineargs = (char*)malloc(argl);\n\t\targl = 0;\n\t\tfor (int i = 1; i < argc; i++)\n\t\t{\n\t\t\tif (strchr(argv[i], ' '))\n\t\t\t{\n\t\t\t\tcmdlineargs[argl++] = '\\\"';\n\t\t\t\tmemcpy(cmdlineargs+argl, argv[i], strlen(argv[i]));\n\t\t\t\targl += strlen(argv[i]);\n\t\t\t\tcmdlineargs[argl++] = '\\\"';\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tmemcpy(cmdlineargs+argl, argv[i], strlen(argv[i]));\n\t\t\t\targl += strlen(argv[i]);\n\t\t\t}\n\t\t\tcmdlineargs[argl++] = ' ';\n\t\t}\n\t\tcmdlineargs[argl] = 0;\n\t\tint mode = GUI_ParseCommandLine(cmdlineargs, false);\n\t\tif (mode == 1)\n\t\t{\t//compile-only\n\t\t\tRunCompiler(cmdlineargs, false);\n\t\t\treturn EXIT_SUCCESS;\n\t\t}\n\t}\n\n\t//start up the gui parts\n\tQCoreApplication *app = new QApplication(argc, argv);\n\tmainwnd = new guimainwindow();\n\tmainwnd->show();\n\tGUIprintf(\"Welcome to FTEQCC!\\n(QT edition)\\n\");\n#ifdef SVNREVISION\n\tif (strcmp(STRINGIFY(SVNREVISION), \"-\"))\n\t\tGUIprintf(\"FTE SVN Revision: %s\\n\",STRINGIFY(SVNREVISION));\n#endif\n\n\t//and now set up our project...\n\tif (*progssrcname)\n\t\tSetMainSrcFile(progssrcname);\n\telse\n\t\tSetMainSrcFile(\"progs.src\");\n\n\t//done, run the gui's main loop.\n\treturn app->exec();\n}\nvoid compilecb(void)\n{\n}\n\nint GUIprintf(const char *msg, ...)\n{\n\tstatic QString l;\n\tva_list va;\n\n\tif (!*msg)\n\t{\t//starting a compile or something.\n\t\t//clear the text and make sure the log is visible.\n\t\tmainwnd->log.setText(QString(\"\"));\n\t\tif (!mainwnd->logsplit.sizes()[1])\n\t\t{\t//force it visible\n\t\t\tQList<int> sizes;\n\t\t\tsizes.append(2);\n\t\t\tsizes.append(1);\n\t\t\tmainwnd->logsplit.setSizes(sizes);\n\t\t}\n\n\t\tmainwnd->docs.clearannotates();\n\t\treturn 0;\n\t}\n\n\tva_start(va, msg);\n\tl.append(QString::vasprintf(msg, va));\n\tva_end(va);\n\tfor (;;)\n\t{\n\t\tauto idx = l.indexOf('\\n');\n\t\tif (idx >= 0)\n\t\t{\n\t\t\tQString s = l.mid(0, idx);\n\t\t\tl = l.mid(idx+1);\n\n\t\t\tif (!s.mid(0, 6).compare(\"code: \"))\n\t\t\t{\n\t\t\t\tmainwnd->docs.annotate(s.toUtf8().data());\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\telse if (s.contains(\": error\") || s.contains(\": werror\") || !s.mid(0,5).compare(\"error\", Qt::CaseInsensitive))\n\t\t\t\tmainwnd->log.setTextColor(QColor(255, 0, 0));\n\t\t\telse if (s.contains(\": warning\"))\n\t\t\t\tmainwnd->log.setTextColor(QColor(128, 128, 0));\n\t\t\telse\n\t\t\t\tmainwnd->log.setTextColor(QColor(0, 0, 0));\n\t\t\tmainwnd->log.append(s);\n\t\t}\n\t\telse\n\t\t\tbreak;\n\t}\n\treturn 0;\n}\nvoid documentlist::UpdateTitle(void)\n{\n\tif (curdoc)\n\t\tmainwnd->setWindowTitle(QString::asprintf(\"%s%s:%i\", curdoc->fname, curdoc->modified?\"*\":\"\", curdoc->cursorline));\n\telse\n\t\tmainwnd->setWindowTitle(\"FTEQCC\");\n}\nvoid documentlist::EditFile(document_s *c, const char *filename, int linenum, bool setcontrol)\n{\n\tif (setcontrol)\n\t{\n\t\tfor (int i = 0; i < numdocuments; i++)\n\t\t{\n\t\t\tdocstacklock lock(this, docs[i]);\n\t\t\ts->SendScintilla(QsciScintillaBase::SCI_MARKERDELETEALL, 1);\n\t\t\ts->SendScintilla(QsciScintillaBase::SCI_MARKERDELETEALL, 2);\n\t\t}\n\t}\n\n\tif (!c)\n\t\tc = FindFile(filename);\n\tif (!c)\n\t{\n\t\tc = new document_s();\n\t\tc->fname = strdup(filename);\n\n\t\tdocs = cpprealloc(docs, sizeof(*docs)*(numdocuments+1));\n\n\t\tif (!CreateDocument(c))\n\t\t{\n\t\t\tdelete(c);\n\t\t\treturn;\n\t\t}\n\n\t\tbeginInsertRows(QModelIndex(), numdocuments, numdocuments);\n\t\tdocs[numdocuments] = c;\n\t\tnumdocuments++;\n\t\tendInsertRows();\n\t}\n\telse\n\t{\n\t\tSwitchToDocument(c);\n\t}\n\n\tUpdateTitle();\n\n\tif (linenum >= 1)\n\t{\n\t\tlinenum--;\t//scintilla is 0-based, apparently.\n\t\ts->ensureLineVisible(max(1, linenum - 3));\n\t\ts->ensureLineVisible(linenum + 3);\n\t\ts->setCursorPosition(linenum, 0);\n\t\ts->ensureCursorVisible();\n\t\ts->setFocus();\n\n\t\tif (setcontrol)\n\t\t{\n\t\t\ts->SendScintilla(QsciScintillaBase::SCI_MARKERADD, linenum, 1);\n\t\t\ts->SendScintilla(QsciScintillaBase::SCI_MARKERADD, linenum, 2);\n\t\t}\n\t}\n}\nvoid EditFile(const char *name, int line, pbool setcontrol)\n{\n\tmainwnd->docs.EditFile(name, line, setcontrol);\n}\nvoid GUI_DialogPrint(const char *title, const char *text)\n{\n\tQMessageBox::information(mainwnd, title, text);\n}\nvoid *GUIReadFile(const char *fname, unsigned char *(*buf_get)(void *ctx, size_t len), void *buf_ctx, size_t *out_size, pbool issourcefile)\n{\n\tauto d = mainwnd->docs.FindFile(fname);\n\tif (d)\n\t\treturn mainwnd->docs.getFileData(d, buf_get, buf_ctx, out_size);\n\n\tif (issourcefile)\n\t\tAddSourceFile(compilingrootfile,    fname);\n\n\treturn QCC_ReadFile(fname, buf_get, buf_ctx, out_size);\n}\nint GUIFileSize(const char *fname)\n{\n\tauto d = mainwnd->docs.FindFile(fname);\n\tif (d)\n\t\treturn mainwnd->docs.getFileSize(d);\n\n\treturn QCC_PopFileSize(fname);\n}\n\n\n\n\n\n\nstatic void DebuggerStop(void)\n{\n\tif (qcdebugger)\n\t{\n\t\t//GUIprintf(\"Detatching from debuggee\\n\");\n\t\tqcdebugger->closeWriteChannel();\n\t\tqcdebugger->closeReadChannel(QProcess::StandardOutput);\n\t\tqcdebugger->closeReadChannel(QProcess::StandardError);\n\t\tqcdebugger->waitForFinished();\n\t\tdelete qcdebugger;\n\t\tqcdebugger = NULL;\n\t}\n}\nstatic bool DebuggerSendCommand(const char *msg, ...)\n{\n\tva_list va;\n\t//qcresume\n\t//qcstep out|over|into\n\t//qcjump \"file\" line\n\t//debuggerwnd windowid\n\t//qcinspect \"variable\"\n\t//qcreload\n\t//qcbreakpoint off=0|on=1|toggle=2 \"file\" line\n\n\tif (!qcdebugger || qcdebugger->state() == QProcess::NotRunning)\n\t\treturn false;\t//not running, can't send.\n\n\tva_start(va, msg);\n\tqcdebugger->write(QString::vasprintf(msg, va).toUtf8().data());\n\tva_end(va);\n\treturn true;\n}\nextern \"C\" pbool QCC_PR_SimpleGetToken (void);\nstatic void DebuggerStart(void)\n{\n\tDebuggerStop();\n\n\tconst char *engine = enginebinary;\n\tchar *cmdline = enginecommandline;\n\tif (!*enginebinary)\n\t{\n\t\tengine = \"fteqw\";\n\t\tif(!*cmdline)\n\t\t\tcmdline = (char*)\"-window\";\n\t}\n\tqcdebugger = new QProcess(mainwnd);\n\tqcdebugger->setProgram(engine);\n\tqcdebugger->setWorkingDirectory(enginebasedir);\n\n\tQStringList args;\n\tpr_file_p = cmdline;\n\twhile (QCC_PR_SimpleGetToken())\n\t\targs.append(pr_token);\n\tqcdebugger->setArguments(args);\n\n\tQObject::connect(qcdebugger, static_cast<void(QProcess::*)(int,QProcess::ExitStatus)>(&QProcess::finished),\n\t\t[=](int exitcode,QProcess::ExitStatus status)\n\t\t{\n//\t\t\tGUIprintf(\"Debuggee finished\\n\");\n//\t\t\tDebuggerStop();\t//can't kill it here, there's still code running inside it\n\t\t\tmainwnd->activateWindow();\t//try and grab the user's attention\n\t\t});\n\tQObject::connect(qcdebugger, &QProcess::readyReadStandardOutput,\n\t\t[=]()\n\t\t{\n\t\t\twhile(qcdebugger->canReadLine())\n\t\t\t{\n\t\t\t\tauto l = qcdebugger->readLine();\n\t\t\t\tconst char *line = l.data();\n\t\t\t\tif (!strncmp(line, \"qcstep \", 7) || !strncmp(line, \"qcfault \", 8))\n\t\t\t\t{\t//engine hit a breakpoint or some such.\n\t\t\t\t\t//file, linenum, message\n\t\t\t\t\tline = QCC_COM_Parse (line+7);\n\t\t\t\t\tQString s(qcc_token);\n\t\t\t\t\tif (*line == ':')\n\t\t\t\t\t\tline++;\t//grr\n\t\t\t\t\tline = QCC_COM_Parse (line);\n\t\t\t\t\tEditFile(s.toUtf8().data(), atoi(qcc_token), true);\n\t\t\t\t\tline = QCC_COM_Parse (line);\n\t\t\t\t\tmainwnd->activateWindow();\n\t\t\t\t\tif (*qcc_token)\n\t\t\t\t\t\tQMessageBox::critical(mainwnd, \"Debugger Fault\", qcc_token);\n\t\t\t\t}\n\t\t\t\telse if (!strncmp(line, \"qcreloaded \", 10))\n\t\t\t\t{\t//vmname, progsname\n\t\t\t\t\tmainwnd->docs.reapplyAllBreakpoints(); //send all breakpoints...\n\t\t\t\t\tDebuggerSendCommand(\"qcresume\\n\");\t//and let it run\n\t\t\t\t}\n\t\t\t\t//status\n\t\t\t\t//curserver\n\t\t\t\t//qcstack\n\t\t\t\t//qcvalue\n\t\t\t\t//refocuswindow\n\t\t\t\telse\n\t\t\t\t\tGUIprintf(line);\n\t\t\t}\n\t\t});\n\n\tqcdebugger->start(QProcess::ReadWrite|QProcess::Unbuffered);\n\tqcdebugger->waitForStarted();\n\tswitch (qcdebugger->state())\n\t{\n\tcase QProcess::NotRunning:\n\t\tGUIprintf(\"Child process not running\\n\");\n\t\tbreak;\n\tcase QProcess::Starting:\n\t\tGUIprintf(\"Child starting up\\n\");\t//still forking?\n\t\tbreak;\n\tcase QProcess::Running:\n//\t\tGUIprintf(\"Child is running\\n\");\n\t\tbreak;\n\t}\n}\n"
  },
  {
    "path": "engine/qclib/qccguistuff.c",
    "content": "#include \"qcc.h\"\n#include \"gui.h\"\n\n#ifndef _WIN32\n#include <unistd.h>\n#endif\n\n#if defined(_WIN32) || defined(__DJGPP__)\n#include <malloc.h>\n#elif defined(__unix__) && !defined(__linux__) // quick hack for the bsds and other unix systems\n#include<stdlib.h>\n#else\n#include <alloca.h>\n#endif\n\n//common gui things\n\npbool fl_nondfltopts;\npbool fl_hexen2;\npbool fl_ftetarg;\npbool fl_compileonstart;\npbool fl_showall;\npbool fl_log;\npbool fl_extramargins;\nint fl_tabsize;\n\nchar parameters[16384];\nchar progssrcname[256];\nchar progssrcdir[256];\nchar enginebinary[MAX_OSPATH];\nchar enginebasedir[MAX_OSPATH];\nchar enginecommandline[8192];\n\n//for finding symbol keywords\nextern QCC_def_t *sourcefilesdefs[];\nextern int sourcefilesnumdefs;\n\nint Grep(const char *filename, const char *string)\n{\n\tint foundcount = 0;\n\tchar *last, *found, *linestart;\n\tint line = 1;\n\tsize_t sz;\n\tchar *raw, *buf;\n\tpbool dofree;\n\tint origfmt;\n\tif (!filename)\n\t\treturn foundcount;\n\n\traw = GUIReadFile(filename, NULL, NULL, &sz, false);\n\tif (!raw)\n\t\treturn foundcount;\n\tif (raw[sz] != 0)\n\t\treturn foundcount;\t//error....\n\n\tbuf = QCC_SanitizeCharSet(raw, &sz, &dofree, &origfmt);\n\n\tlinestart = last = found = buf;\n\twhile ((found = QC_strcasestr(found, string)))\n\t{\n\t\twhile (last < found)\n\t\t{\n\t\t\tif (*last++ == '\\n')\n\t\t\t{\n\t\t\t\tline++;\n\t\t\t\tlinestart = last;\n\t\t\t}\n\t\t}\n\n\t\twhile (*found && *found != '\\n')\n\t\t\tfound++;\n\t\tif (*found)\n\t\t\t*found++ = '\\0';\n\n\t\tGUIprintf(\"%s:%i: %s\\n\", filename, line, linestart);\n\t\tline++;\n\t\tlinestart = found;\n\t\tfoundcount++;\n\t}\n\tif (dofree)\n\t\tfree(buf);\n\tfree(raw);\n\n\treturn foundcount;\n}\nvoid GoToDefinition(const char *name)\n{\n\t#define MAXSOURCEFILESLIST 8\n\textern QCC_def_t *sourcefilesdefs[MAXSOURCEFILESLIST];\n\textern int sourcefilesnumdefs;\n\tint fno;\n\n\tQCC_def_t *def, *guess;\n\tQCC_function_t *fnc;\n\n\tconst char *strip;\t//trim whitespace (for convieniance).\n\twhile (*name <= ' ' && *name)\n\t\tname++;\n\tfor (strip = name + strlen(name)-1; strip > name; strip--)\n\t{\n\t\tif (*strip <= ' ')\n\t\t\tcontinue;\n\t\telse\t//got some part of a word\n\t\t\tbreak;\n\t}\n\tif (*strip <= ' ')\n\t{\n\t\tchar *t = alloca(strip-name+1);\n\t\tmemcpy(t, name, strip-t);\n\t\tt[strip-t] = 0;\n\t\tname = t;\n\t}\n\n\tif (!globalstable.numbuckets)\n\t{\n\t\tGUI_DialogPrint(\"Not found\", \"You need to compile first.\");\n\t\treturn;\n\t}\n\n\n\tdef = QCC_PR_GetDef(NULL, name, NULL, false, 0, false);\n\n\t//no exact match, see if we can get a case-insensitive match\n\tif (!def && *name)\n\t{\n\t\tfor (fno = 0; fno < sourcefilesnumdefs; fno++)\n\t\t{\n\t\t\tfor (def = sourcefilesdefs[fno]; def; def = def->next)\n\t\t\t{\n\t\t\t\tif (def->scope)\n\t\t\t\t\tcontinue;\t//ignore locals, because we don't know where we are, and they're probably irrelevent.\n\t\t\t\tif (!QC_strcasecmp(def->name, name))\n\t\t\t\t{\n\t\t\t\t\tfno = sourcefilesnumdefs;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t//no exact match, see if we can get a partial\n\tif (!def && *name)\n\t{\n\t\tint prefixlen = strlen(name);\n\t\tfor (fno = 0; fno < sourcefilesnumdefs; fno++)\n\t\t{\n\t\t\tfor (guess = sourcefilesdefs[fno]; guess; guess = guess->next)\n\t\t\t{\n\t\t\t\tif (guess->scope)\n\t\t\t\t\tcontinue;\t//ignore locals, because we don't know where we are, and they're probably irrelevent.\n\n\t\t\t\t//make sure it has the right prefix\n\t\t\t\tif (!QC_strncasecmp(guess->name, name, prefixlen))\n\t\t\t\t{\n\t\t\t\t\tif (guess->type->type == ev_function && guess->constant && !guess->arraysize)\n\t\t\t\t\t{\t//if we found a function, use that one above all others.\n\t\t\t\t\t\tdef = guess;\n\t\t\t\t\t\tfno = sourcefilesnumdefs;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\telse if (!def)\n\t\t\t\t\t\tdef = guess;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tif (def)\n\t{\n\t\t//with functions, the def is the prototype.\n\t\t//we want the body, so zoom to the first statement of the function instead\n\t\tif (def->type->type == ev_function && def->constant && !def->arraysize)\n\t\t{\n\t\t\tint fnum = def->symboldata[0].function;\n\t\t\tif (fnum > 0 && fnum < numfunctions)\n\t\t\t{\n\t\t\t\tfnc = &functions[fnum];\n\t\t\t\tif (fnc->code>=0 && fnc->filen)\n\t\t\t\t{\n\t\t\t\t\tEditFile(fnc->filen, fnc->line, false);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (!def->filen)\n\t\t{\n\t\t\tchar msgbuffer[2048];\n\t\t\tQC_snprintfz(msgbuffer, sizeof(msgbuffer), \"Global definition of \\\"%s\\\" was not specified.\", name);\t\t\n\t\t\tGUI_DialogPrint(\"Not found\", msgbuffer);\n\t\t}\n\t\telse\n\t\t\tEditFile(def->filen, def->s_line-1, false);\n\t}\n\telse\n\t{\n\t\tchar msgbuffer[2048];\n\t\tQC_snprintfz(msgbuffer, sizeof(msgbuffer), \"Global instance of \\\"%s\\\" was not found.\", name); \n\t\tGUI_DialogPrint(\"Not found\", msgbuffer);\n\t}\n}\n\npbool GenAutoCompleteList(char *prefix, char *buffer, int buffersize)\n{\n\tQCC_def_t *def;\n\tint prefixlen = strlen(prefix);\n\tint usedbuffer = 0;\n\tint l;\n\tint fno;\n\tfor (fno = 0; fno < sourcefilesnumdefs; fno++)\n\t{\n\t\tfor (def = sourcefilesdefs[fno]; def; def = def->next)\n\t\t{\n\t\t\tif (def->scope)\n\t\t\t\tcontinue;\t//ignore locals, because we don't know where we are, and they're probably irrelevent.\n\n\t\t\t//make sure it has the right prefix\n\t\t\tif (!strncmp(def->name, prefix, prefixlen))\n\t\t\t//but ignore it if its one of those special things that you're not meant to know about.\n\t\t\tif (strcmp(def->name, \"IMMEDIATE\") && !strchr(def->name, ':') && !strchr(def->name, '.') && !strchr(def->name, '*') && !strchr(def->name, '['))\n\t\t\t{\n\t\t\t\tl = strlen(def->name);\n\t\t\t\tif (l && usedbuffer+2+l < buffersize)\n\t\t\t\t{\n\t\t\t\t\tif (usedbuffer)\n\t\t\t\t\t\tbuffer[usedbuffer++] = ' ';\n\t\t\t\t\tmemcpy(buffer+usedbuffer, def->name, l);\n\t\t\t\t\tusedbuffer += l;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tbuffer[usedbuffer] = 0;\n\treturn usedbuffer>0;\n}\n\nstatic pbool GUI_NeedsQuotes(const char *str)\n{\n\t//true if an empty string.\n\tif (!*str)\n\t\treturn true;\n\tfor(; *str; str++)\n\t{\t//true if any char is not alpha-numeric\n\t\tif (*str >= 'a' && *str <= 'z')\n\t\t\t;\n\t\telse if (*str >= 'A' && *str <= 'Z')\n\t\t\t;\n\t\telse if (*str >= '0' && *str <= '9')\n\t\t\t;\n\t\telse if (*str == '_')\n\t\t\t;\n\t\telse\n\t\t\treturn true;\n\t}\n\treturn false;\n}\n\nstatic void GUI_WriteConfigLine(FILE *file, char *part1, char *part2, char *part3, char *desc)\n{\n\tint align = 0;\n\tif (part1)\n\t{\n\t\tif (GUI_NeedsQuotes(part1))\n\t\t\talign += fprintf(file, \"\\\"%s\\\" \", part1);\n\t\telse\n\t\t\talign += fprintf(file, \"%s \", part1);\n\t\tfor (; align < 14; align++)\n\t\t\tfputc(' ', file);\n\t}\n\tif (part2)\n\t{\n\t\tif (GUI_NeedsQuotes(part2))\n\t\t\talign += fprintf(file, \"\\\"%s\\\" \", part2);\n\t\telse\n\t\t\talign += fprintf(file, \"%s \", part2);\n\t\tfor (; align < 28; align++)\n\t\t\tfputc(' ', file);\n\t}\n\tif (part3)\n\t{\n\t\tif (GUI_NeedsQuotes(part3))\n\t\t\talign += fprintf(file, \"\\\"%s\\\" \", part3);\n\t\telse\n\t\t\talign += fprintf(file, \"%s \", part3);\n\t\tfor (; align < 40; align++)\n\t\t\tfputc(' ', file);\n\t}\n\n\tif (desc)\n\t{\n\t\tif (align > 40)\n\t\t{\n\t\t\tfputc('\\n', file);\n\t\t\talign = 0;\n\t\t}\n\t\tfor (; align < 40; align++)\n\t\t\tfputc(' ', file);\n\t\tfputs(\"# \", file);\n\t\talign -= 40;\n\t\tif (align < 0)\n\t\t\talign = 0;\n\t\twhile(*desc)\n\t\t{\n\t\t\tif (*desc == '\\n' || (*desc == ' ' && align > 60))\n\t\t\t{\n\t\t\t\tfputs(\"\\n\", file);\n\t\t\t\tfor (align = 0; align < 40; align++)\n\t\t\t\t\tfputc(' ', file);\n\t\t\t\tfputs(\"# \", file);\n\t\t\t\talign = 0;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tfputc(*desc, file);\n\t\t\t\talign++;\n\t\t\t}\n\t\t\tdesc++;\n\t\t}\n\t}\n\tfputs(\"\\n\", file);\n}\nstatic void GUI_WriteConfigInt(FILE *file, char *part1, int part2, char *desc)\n{\n\tchar buf[64];\n\tQC_snprintfz(buf, sizeof(buf), \"%i\", part2);\n\tGUI_WriteConfigLine(file, part1, buf, NULL, desc);\n}\nvoid GUI_SaveConfig(void)\n{\n\tFILE *file = fopen(\"fteqcc.ini\", \"wt\");\n\tint p;\n\tif (!file)\n\t\treturn;\n\tfor (p = 0; optimisations[p].enabled; p++)\n\t{\n\t\tif ((!(optimisations[p].flags&FLAG_SETINGUI)) == (!(optimisations[p].flags&FLAG_ASDEFAULT)))\n\t\t\tGUI_WriteConfigLine(file, \"optimisation\",\toptimisations[p].abbrev,\t\"default\",\t\toptimisations[p].description);\n\t\telse\n\t\t\tGUI_WriteConfigLine(file, \"optimisation\",\toptimisations[p].abbrev,\t(optimisations[p].flags&FLAG_SETINGUI)?\"on\":\"off\",\t\toptimisations[p].description);\n\t}\n\n\tfor (p = 0; compiler_flag[p].enabled; p++)\n\t{\n\t\tif (p>0 && compiler_flag[p].enabled == compiler_flag[p-1].enabled)\n\t\t\tcontinue;\t//don't list dupe names.\n\t\tif (!strncmp(compiler_flag[p].fullname, \"Keyword: \", 9))\n\t\t\tGUI_WriteConfigLine(file, \"keyword\",\tcompiler_flag[p].abbrev,\t(compiler_flag[p].flags&FLAG_SETINGUI)?\"true\":\"false\",\tcompiler_flag[p].description);\n\t\telse\n\t\t\tGUI_WriteConfigLine(file, \"flag\",\t\tcompiler_flag[p].abbrev,\t(compiler_flag[p].flags&FLAG_SETINGUI)?\"true\":\"false\",\tcompiler_flag[p].description);\n\t}\n\n\tGUI_WriteConfigLine(file, \"showall\",\t\t\tfl_showall?\"on\":\"off\",\t\t\tNULL, \"Show all keyword options in the gui\");\n\tGUI_WriteConfigLine(file, \"compileonstart\",\t\tfl_compileonstart?\"on\":\"off\",\tNULL, \"Recompile on GUI startup\");\n\tGUI_WriteConfigLine(file, \"log\",\t\t\t\tfl_log?\"on\":\"off\",\t\t\t\tNULL, \"Write out a compile log\");\n\n\tGUI_WriteConfigLine(file, \"enginebinary\",\t\tenginebinary,\t\t\t\t\tNULL, \"Location of the engine binary to run. Change this to something else to run a different engine, but not all support debugging.\");\n\tGUI_WriteConfigLine(file, \"basedir\",\t\t\tenginebasedir,\t\t\t\t\tNULL, \"The base directory of the game that contains your sub directory\");\n\tGUI_WriteConfigLine(file, \"engineargs\",\t\t\tenginecommandline,\t\t\t\tNULL, \"The engine commandline to use when debugging. You'll likely want to ensure this contains -window as well as the appropriate -game argument.\");\n\tGUI_WriteConfigLine(file, \"srcfile\",\t\t\tprogssrcname,\t\t\t\t\tNULL, \"The progs.src file to load to find ordering of other qc files.\");\n\tGUI_WriteConfigLine(file, \"src\",\t\t\t\tprogssrcdir,\t\t\t\t\tNULL, \"Additional subdir to read qc files from. Typically blank (ie: the working directory).\");\n\n\tGUI_WriteConfigInt (file, \"tabsize\",\t\t\tfl_tabsize,\t\t\t\t\t\t\t  \"Specifies the size of tabs in scintilla windows.\");\n\tGUI_WriteConfigLine(file, \"extramargins\",\t\tfl_extramargins?\"on\":\"off\",\t\tNULL, \"Enables line number and folding margins.\");\n\tGUI_WriteConfigLine(file, \"hexen2\",\t\t\t\tfl_hexen2?\"on\":\"off\",\t\t\tNULL, \"Enable the extra tweaks needed for compatibility with hexen2 engines.\");\n\tGUI_WriteConfigLine(file, \"extendedopcodes\",\tfl_ftetarg?\"on\":\"off\",\t\t\tNULL, \"Utilise an extended instruction set, providing support for pointers and faster arrays and other speedups.\");\n\n\tGUI_WriteConfigLine(file, \"parameters\",\t\t\tparameters,\t\t\t\t\t\tNULL, \"Other additional parameters that are not supported by the gui. Likely including -DFOO\");\n\n\tfclose(file);\n}\n//grabs a token. modifies original string.\nstatic char *GUI_ParseInPlace(char **state)\n{\n\tchar *str = *state, *end;\n\twhile(*str == ' ' || *str == '\\t' || *str == '\\r' || *str == '\\n')\n\t\tstr++;\n\tif (*str == '\\\"')\n\t{\n\t\tchar *fmt;\n\t\tstr++;\n\t\tfor (end = str, fmt = str; *end; )\n\t\t{\n\t\t\tif (*end == '\\\"')\n\t\t\t{\n\t\t\t\t*end++ = 0;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\telse if (*end == '\\'' && end[1] == '\\\\')\n\t\t\t\t*fmt = '\\\\';\n\t\t\telse if (*end == '\\'' && end[1] == '\\\"')\n\t\t\t\t*fmt = '\\\"';\n\t\t\telse if (*end == '\\'' && end[1] == '\\n')\n\t\t\t\t*fmt = '\\n';\n\t\t\telse if (*end == '\\'' && end[1] == '\\r')\n\t\t\t\t*fmt = '\\r';\n\t\t\telse if (*end == '\\'' && end[1] == '\\t')\n\t\t\t\t*fmt = '\\t';\n\t\t\telse\n\t\t\t{\n\t\t\t\t*fmt++ = *end++;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tfmt+=1;\n\t\t\tend+=2;\n\t\t}\n\t}\n\telse\n\t{\n\t\tfor (end = str; *end; end++)\n\t\t{\n\t\t\tif (*end == '#')\n\t\t\t{\n\t\t\t\t*end = 0;\n\t\t\t\twhile (*end && *end != '\\n')\n\t\t\t\t\tend++;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (*end==' ' || *end =='\\t' || *end == '\\n' || *end == '\\r')\n\t\t\t\tbreak;\n\t\t}\n\t}\n\tif (end && *end)\n\t{\n\t\t*end = 0;\n\t\t*state = end+1;\n\t}\n\telse *state = end;\n\treturn str;\n}\nstatic int GUI_ParseIntInPlace(char **state, int defaultval)\n{\n\tchar *token = GUI_ParseInPlace(state);\n\tif (!stricmp(token, \"default\"))\n\t\treturn defaultval;\n\telse if (!stricmp(token, \"on\") || !stricmp(token, \"true\") || !stricmp(token, \"yes\"))\n\t\treturn 1;\n\telse if (!stricmp(token, \"off\") || !stricmp(token, \"false\") || !stricmp(token, \"no\"))\n\t\treturn 0;\n\telse\n\t\treturn atoi(token);\n}\nstatic int GUI_ParseBooleanInPlace(char **state, int defaultval)\n{\n\tchar *token = GUI_ParseInPlace(state);\n\tif (!stricmp(token, \"default\"))\n\t\treturn defaultval;\n\telse if (!stricmp(token, \"on\") || !stricmp(token, \"true\") || !stricmp(token, \"yes\"))\n\t\treturn 1;\n\telse if (!stricmp(token, \"off\") || !stricmp(token, \"false\") || !stricmp(token, \"no\"))\n\t\treturn 0;\n\telse\n\t\treturn !!atoi(token);\n}\nvoid GUI_LoadConfig(void)\n{\n\tchar buf[2048];\n\tchar *token, *str;\n\tFILE *file = fopen(\"fteqcc.ini\", \"rb\");\n\tint p;\n\t//initialise gui-only stuff.\n\tfl_compileonstart = false;\n\tfl_extramargins = false;\n\tfl_tabsize = 8;\n\tif (!file)\n\t\treturn;\n\tfl_nondfltopts = false;\n\twhile (fgets(buf, sizeof(buf), file))\n\t{\n\t\tstr = buf;\n\n\t\ttoken = GUI_ParseInPlace(&str);\n\t\tif (!stricmp(token, \"optimisation\") || !stricmp(token, \"opt\"))\n\t\t{\n\t\t\tchar *item = GUI_ParseInPlace(&str);\n\t\t\tint value = GUI_ParseBooleanInPlace(&str, -1);\n\t\t\tfor (p = 0; optimisations[p].enabled; p++)\n\t\t\t\tif (!stricmp(item, optimisations[p].abbrev))\n\t\t\t\t{\n\t\t\t\t\tif (value == -1)\n\t\t\t\t\t\tvalue = !!(optimisations[p].flags & FLAG_ASDEFAULT);\n\t\t\t\t\telse\n\t\t\t\t\t\tfl_nondfltopts = true;\n\t\t\t\t\tif (value)\n\t\t\t\t\t\toptimisations[p].flags |= FLAG_SETINGUI;\n\t\t\t\t\telse\n\t\t\t\t\t\toptimisations[p].flags &= ~FLAG_SETINGUI;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t//don't worry too much if its not known\n\t\t\tif (!optimisations[p].enabled)\n\t\t\t\tprintf(\"Unknown flag: \\\"%s\\\"\\n\", item);\n\t\t}\n\t\telse if (!stricmp(token, \"flag\") || !stricmp(token, \"fl\") || !stricmp(token, \"keyword\"))\n\t\t{\n\t\t\tchar *item = GUI_ParseInPlace(&str);\n\t\t\tint value = GUI_ParseBooleanInPlace(&str, -1);\n\t\t\tfor (p = 0; compiler_flag[p].enabled; p++)\n\t\t\t\tif (!stricmp(item, compiler_flag[p].abbrev))\n\t\t\t\t{\n\t\t\t\t\tif (value == -1)\n\t\t\t\t\t\tvalue = !!(compiler_flag[p].flags & FLAG_ASDEFAULT);\n\t\t\t\t\tif (value)\n\t\t\t\t\t\tcompiler_flag[p].flags |= FLAG_SETINGUI;\n\t\t\t\t\telse\n\t\t\t\t\t\tcompiler_flag[p].flags &= ~FLAG_SETINGUI;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\tif (!compiler_flag[p].enabled)\n\t\t\t\tprintf(\"Unknown flag/keyword: \\\"%s\\\"\\n\", item);\n\t\t\t//don't worry if its not known\n\t\t}\n\t\telse if (!stricmp(token, \"enginebinary\"))\n\t\t\tQC_strlcpy(enginebinary, GUI_ParseInPlace(&str), sizeof(enginebinary));\n\t\telse if (!stricmp(token, \"basedir\"))\n\t\t\tQC_strlcpy(enginebasedir, GUI_ParseInPlace(&str), sizeof(enginebasedir));\n\t\telse if (!stricmp(token, \"engineargs\"))\n\t\t\tQC_strlcpy(enginecommandline, GUI_ParseInPlace(&str), sizeof(enginecommandline));\n\t\telse if (!stricmp(token, \"srcfile\"))\n\t\t\tQC_strlcpy(progssrcname, GUI_ParseInPlace(&str), sizeof(progssrcname));\n\t\telse if (!stricmp(token, \"src\"))\n\t\t\tQC_strlcpy(progssrcdir, GUI_ParseInPlace(&str), sizeof(progssrcdir));\n\t\telse if (!stricmp(token, \"parameters\"))\n\t\t\tQC_strlcpy(parameters, GUI_ParseInPlace(&str), sizeof(parameters));\n\n\t\telse if (!stricmp(token, \"log\"))\n\t\t\tfl_log = GUI_ParseBooleanInPlace(&str, false);\n\t\telse if (!stricmp(token, \"compileonstart\"))\n\t\t\tfl_compileonstart = GUI_ParseBooleanInPlace(&str, false);\n\t\telse if (!stricmp(token, \"showall\"))\n\t\t\tfl_showall = GUI_ParseBooleanInPlace(&str, false);\n\n\t\telse if (!stricmp(token, \"tabsize\"))\n\t\t\tfl_tabsize = GUI_ParseIntInPlace(&str, false);\n\t\telse if (!stricmp(token, \"extramargins\"))\n\t\t\tfl_extramargins = GUI_ParseBooleanInPlace(&str, false);\n\t\telse if (!stricmp(token, \"hexen2\"))\n\t\t\tfl_hexen2 = GUI_ParseBooleanInPlace(&str, false);\n\t\telse if (!stricmp(token, \"extendedopcodes\"))\n\t\t\tfl_ftetarg = GUI_ParseBooleanInPlace(&str, false);\n\t\telse if (*token)\n\t\t{\n\t\t\tprintf(\"Unknown setting: \\\"%s\\\"\\n\", token);\n\t\t}\n\t}\n\tfclose(file);\n}\n\n\n//this function takes the windows specified commandline and strips out all the options menu items.\nint GUI_ParseCommandLine(const char *args, pbool keepsrcanddir)\n{\n\tint paramlen=0;\n\tint l, p;\n\tconst char *next;\n\tint mode = 0;\n\textern int qccpersisthunk;\n\n\tif (!*args)\n\t{\n\t\tint len;\n\t\tFILE *f;\n\t\tchar *s;\n\n\t\tf = fopen(\"fteqcc.arg\", \"rb\");\n\t\tif (f)\n\t\t{\n\t\t\tfseek(f, 0, SEEK_END);\n\t\t\tlen = ftell(f);\n\t\t\tfseek(f, 0, SEEK_SET);\n\t\t\targs = alloca(len+1);\n\t\t\tfread((char*)args, 1, len, f);\n\t\t\t((char*)args)[len] = '\\0';\n\t\t\tfclose(f);\n\n\t\t\twhile((s = strchr(args, '\\r')))\n\t\t\t\t*s = ' ';\n\t\t\twhile((s = strchr(args, '\\n')))\n\t\t\t\t*s = ' ';\n\t\t}\n\t}\n\n\t//find the first argument\n\twhile (*args == ' ' || *args == '\\t')\n\t\targs++;\n\tfor (next = args; *next&&*next!=' '&&*next !='\\t'; next++)\n\t\t;\n\n\n\tif (*args != '-')\n\t{\n\t\tpbool qt = *args == '\\\"';\n\t\tl = 0;\n\t\tif (qt)\n\t\t\targs++;\n\t\twhile ((*args != ' ' || qt) && *args)\n\t\t{\n\t\t\tif (qt && *args == '\\\"')\n\t\t\t{\n\t\t\t\targs++;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (!keepsrcanddir)\n\t\t\t\tprogssrcname[l++] = *args;\n\t\t\targs++;\n\t\t}\n\t\tif (!keepsrcanddir)\n\t\t\tprogssrcname[l] = 0;\n\n\t\tnext = args;\n\n\t\tif (!keepsrcanddir)\n\t\t{\n\t\t\targs = strrchr(progssrcname, '\\\\');\n\t\t\twhile(args && strchr(args, '/'))\n\t\t\t\targs = strchr(args, '/');\n\t\t\tif (args)\n\t\t\t{\n\t\t\t\tmemcpy(progssrcdir, progssrcname, args-progssrcname);\n\t\t\t\tprogssrcdir[args-progssrcname] = 0;\n\t\t\t\targs++;\n\t\t\t\tmemmove(progssrcname, args, strlen(args)+1);\n\n#ifdef _WIN32\n\t\t\t\tSetCurrentDirectoryA(progssrcdir);\n#else\n\t\t\t\tchdir(progssrcdir);\n#endif\n\t\t\t\t*progssrcdir = 0;\n\t\t\t}\n\t\t}\n\t\targs = next;\n\t}\n\n\tGUI_LoadConfig();\n\n\tparamlen = strlen(parameters);\n\tif (paramlen)\n\t\tparameters[paramlen++] = ' ';\n\twhile(*args)\n\t{\n\t\twhile (*args == ' ' || *args == '\\t')\n\t\t\targs++;\n\t\tfor (next = args; *next&&*next!=' '&&*next !='\\t'; next++)\n\t\t\t;\n\n\t\tstrncpy(parameters+paramlen, args, next-args);\n\t\tparameters[paramlen+next-args] = '\\0';\n\t\tl = strlen(parameters+paramlen)+1;\n\n\t\tif (!strnicmp(parameters+paramlen, \"-stdout\", 7) || !strnicmp(parameters+paramlen, \"--version\", 9))\n\t\t{\n\t\t\tmode = 1;\n\t\t}\n\t\t/*else if (!strnicmp(parameters+paramlen, \"-zippatch\", 9))\n\t\t{\n\t\t\tmode = 2;\n\t\t}*/\n\t\telse if (!strnicmp(parameters+paramlen, \"-O\", 2) || !strnicmp(parameters+paramlen, \"/O\", 2))\n\t\t{\t//strip out all -O\n\t\t\tfl_nondfltopts = true;\n\t\t\tif (parameters[paramlen+2])\n\t\t\t{\n\t\t\t\tif (parameters[paramlen+2] >= '0' && parameters[paramlen+2] <= '3')\n\t\t\t\t{\n\t\t\t\t\tp = parameters[paramlen+2]-'0';\n\t\t\t\t\tfor (l = 0; optimisations[l].enabled; l++)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (optimisations[l].optimisationlevel<=p)\n\t\t\t\t\t\t\toptimisations[l].flags |= FLAG_SETINGUI;\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\toptimisations[l].flags &= ~FLAG_SETINGUI;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse if (!strncmp(parameters+paramlen+2, \"no-\", 3))\n\t\t\t\t{\n\t\t\t\t\tif (parameters[paramlen+5])\n\t\t\t\t\t{\n\t\t\t\t\t\tfor (p = 0; optimisations[p].enabled; p++)\n\t\t\t\t\t\t\tif ((*optimisations[p].abbrev && !strcmp(parameters+paramlen+5, optimisations[p].abbrev)) || !strcmp(parameters+paramlen+5, optimisations[p].fullname))\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\toptimisations[p].flags &= ~FLAG_SETINGUI;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (!optimisations[p].enabled)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tparameters[paramlen+next-args] = ' ';\n\t\t\t\t\t\t\tparamlen += l;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tfor (p = 0; optimisations[p].enabled; p++)\n\t\t\t\t\t\tif ((*optimisations[p].abbrev && !strcmp(parameters+paramlen+2, optimisations[p].abbrev)) || !strcmp(parameters+paramlen+2, optimisations[p].fullname))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\toptimisations[p].flags |= FLAG_SETINGUI;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\n\t\t\t\t\tif (!optimisations[p].enabled)\n\t\t\t\t\t{\n\t\t\t\t\t\tparameters[paramlen+next-args] = ' ';\n\t\t\t\t\t\tparamlen += l;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse if (!strnicmp(parameters+paramlen, \"-F\", 2) || !strnicmp(parameters+paramlen, \"/F\", 2) || !strnicmp(parameters+paramlen, \"-K\", 2) || !strnicmp(parameters+paramlen, \"/K\", 2))\n\t\t{\n\t\t\tif (parameters[paramlen+2])\n\t\t\t{\n\t\t\t\tif (!strncmp(parameters+paramlen+2, \"no-\", 3))\n\t\t\t\t{\n\t\t\t\t\tif (parameters[paramlen+5])\n\t\t\t\t\t{\n\t\t\t\t\t\tfor (p = 0; compiler_flag[p].enabled; p++)\n\t\t\t\t\t\t\tif ((*compiler_flag[p].abbrev && !strcmp(parameters+paramlen+5, compiler_flag[p].abbrev)) || !strcmp(parameters+paramlen+5, compiler_flag[p].fullname))\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tcompiler_flag[p].flags &= ~FLAG_SETINGUI;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (!compiler_flag[p].enabled)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tparameters[paramlen+next-args] = ' ';\n\t\t\t\t\t\t\tparamlen += l;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tfor (p = 0; compiler_flag[p].enabled; p++)\n\t\t\t\t\t\tif ((*compiler_flag[p].abbrev && !strcmp(parameters+paramlen+2, compiler_flag[p].abbrev)) || !strcmp(parameters+paramlen+2, compiler_flag[p].fullname))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcompiler_flag[p].flags |= FLAG_SETINGUI;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\n\t\t\t\t\tif (!compiler_flag[p].enabled)\n\t\t\t\t\t{\n\t\t\t\t\t\tparameters[paramlen+next-args] = ' ';\n\t\t\t\t\t\tparamlen += l;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n/*\n\t\telse if (!strnicmp(parameters+paramlen, \"-Fno-kce\", 8) || !strnicmp(parameters+paramlen, \"/Fno-kce\", 8))\t//keywords stuph\n\t\t{\n\t\t\tfl_nokeywords_coexist = true;\n\t\t}\n\t\telse if (!strnicmp(parameters+paramlen, \"-Fkce\", 5) || !strnicmp(parameters+paramlen, \"/Fkce\", 5))\n\t\t{\n\t\t\tfl_nokeywords_coexist = false;\n\t\t}\n\t\telse if (!strnicmp(parameters+paramlen, \"-Facc\", 5) || !strnicmp(parameters+paramlen, \"/Facc\", 5))\n\t\t{\n\t\t\tfl_acc = true;\n\t\t}\n\t\telse if (!strnicmp(parameters+paramlen, \"-autoproto\", 10) || !strnicmp(parameters+paramlen, \"/autoproto\", 10))\n\t\t{\n\t\t\tfl_autoprototype = true;\n\t\t}\n*/\n\t\telse if (!strnicmp(parameters+paramlen, \"-showall\", 8) || !strnicmp(parameters+paramlen, \"/showall\", 8))\n\t\t{\n\t\t\tfl_showall = true;\n\t\t}\n\t\telse if (!strnicmp(parameters+paramlen, \"-ac\", 3) || !strnicmp(parameters+paramlen, \"/ac\", 3))\n\t\t{\n\t\t\tfl_compileonstart = true;\n\t\t}\n\t\telse if (!strnicmp(parameters+paramlen, \"-log\", 4) || !strnicmp(parameters+paramlen, \"/log\", 4))\n\t\t{\n\t\t\tfl_log = true;\n\t\t}\n\t\telse if (!strnicmp(parameters+paramlen, \"-nolog\", 4) || !strnicmp(parameters+paramlen, \"/nolog\", 4))\n\t\t{\n\t\t\tfl_log = false;\n\t\t}\n\t\telse if (!strnicmp(parameters+paramlen, \"-engine\", 7) || !strnicmp(parameters+paramlen, \"/engine\", 7))\n\t\t{\n\t\t\twhile (*next == ' ')\n\t\t\t\tnext++;\n\t\t\t\n\t\t\tl = 0;\n\t\t\twhile (*next != ' ' && *next)\n\t\t\t\tenginebinary[l++] = *next++;\n\t\t\tenginebinary[l] = 0;\n\t\t}\n\t\telse if (!strnicmp(parameters+paramlen, \"-basedir\", 8) || !strnicmp(parameters+paramlen, \"/basedir\", 8))\n\t\t{\n\t\t\twhile (*next == ' ')\n\t\t\t\tnext++;\n\t\t\t\n\t\t\tl = 0;\n\t\t\twhile (*next != ' ' && *next)\n\t\t\t\tenginebasedir[l++] = *next++;\n\t\t\tenginebasedir[l] = 0;\n\t\t}\n\t\t//strcpy(enginecommandline, \"-window +map start -nohome\");\n\t\telse if (!strnicmp(parameters+paramlen, \"-srcfile\", 8) || !strnicmp(parameters+paramlen, \"/srcfile\", 8))\n\t\t{\n\t\t\twhile (*next == ' ')\n\t\t\t\tnext++;\n\t\t\t\n\t\t\tif (keepsrcanddir)\n\t\t\t{\t//ignore it\n\t\t\t\twhile (*next != ' ' && *next)\n\t\t\t\t\tnext++;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tl = 0;\n\t\t\t\twhile (*next != ' ' && *next)\n\t\t\t\t\tprogssrcname[l++] = *next++;\n\t\t\t\tprogssrcname[l] = 0;\n\t\t\t}\n\t\t}\n\t\telse if (!strnicmp(parameters+paramlen, \"-src \", 5) || !strnicmp(parameters+paramlen, \"/src \", 5))\n\t\t{\n\t\t\twhile (*next == ' ')\n\t\t\t\tnext++;\n\n\t\t\tif (keepsrcanddir)\n\t\t\t{\t//ignore it\n\t\t\t\twhile (*next != ' ' && *next)\n\t\t\t\t\tnext++;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tl = 0;\n\t\t\t\twhile (*next != ' ' && *next)\n\t\t\t\t\tprogssrcdir[l++] = *next++;\n\t\t\t\tprogssrcdir[l] = 0;\n\t\t\t}\n\t\t}\n\t\telse if (!strnicmp(parameters+paramlen, \"-T\", 2) || !strnicmp(parameters+paramlen, \"/T\", 2))\t//the target\n\t\t{\n\t\t\tif (!strnicmp(parameters+paramlen+2, \"h2\", 2))\n\t\t\t{\n\t\t\t\tfl_hexen2 = true;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tfl_hexen2 = false;\n\t\t\t\tparameters[paramlen+next-args] = ' ';\n\t\t\t\tparamlen += l;\n\t\t\t}\n\t\t}\n\t\t/*\n\t\telse if (isfirst && *args != '-' && *args != '/')\n\t\t{\n\t\t\tpbool qt = *args == '\\\"';\n\t\t\tl = 0;\n\t\t\tif (qt)\n\t\t\t\targs++;\n\t\t\twhile (*args != ' ' && *args)\n\t\t\t{\n\t\t\t\tif (qt && *args == '\\\"')\n\t\t\t\t{\n\t\t\t\t\targs++;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tprogssrcname[l++] = *args++;\n\t\t\t}\n\t\t\tprogssrcname[l] = 0;\n\n\t\t\targs = strrchr(progssrcname, '\\\\');\n\t\t\twhile(args && strchr(args, '/'))\n\t\t\t\targs = strchr(args, '/');\n\t\t\tif (args)\n\t\t\t{\n\t\t\t\tmemcpy(progssrcdir, progssrcname, args-progssrcname);\n\t\t\t\tprogssrcdir[args-progssrcname] = 0;\n\t\t\t\targs++;\n\t\t\t\tmemmove(progssrcname, args, strlen(args)+1);\n\n\t\t\t\tSetCurrentDirectoryA(progssrcdir);\n\t\t\t\t*progssrcdir = 0;\n\t\t\t}\n\t\t}\n\t\t*/\n\t\telse\n\t\t{\n\t\t\tparameters[paramlen+next-args] = ' ';\n\t\t\tparamlen += l;\n\t\t}\n\n\t\targs=next;\n\t}\n\n\twhile (paramlen>0 && (parameters[paramlen-1] == ' ' || parameters[paramlen-1] == '\\t'))\n\t\tparamlen--;\n\tparameters[paramlen] = '\\0';\n\n\tqccpersisthunk = (mode!=1);\n\treturn mode;\n}\n\nvoid GUI_SetDefaultOpts(void)\n{\n\tint i;\n\tfor (i = 0; compiler_flag[i].enabled; i++)\t//enabled is a pointer\n\t{\n\t\tif (compiler_flag[i].flags & FLAG_ASDEFAULT)\n\t\t\tcompiler_flag[i].flags |= FLAG_SETINGUI;\n\t\telse\n\t\t\tcompiler_flag[i].flags &= ~FLAG_SETINGUI;\n\t}\n\tfor (i = 0; optimisations[i].enabled; i++)\t//enabled is a pointer\n\t{\n\t\tif (optimisations[i].flags & FLAG_ASDEFAULT)\n\t\t\toptimisations[i].flags |= FLAG_SETINGUI;\n\t\telse\n\t\t\toptimisations[i].flags &= ~FLAG_SETINGUI;\n\t}\n}\n\nvoid GUI_RevealOptions(void)\n{\n\tint i;\n\tfor (i = 0; compiler_flag[i].enabled; i++)\t//enabled is a pointer\n\t{\n\t\tif (fl_showall && compiler_flag[i].flags & FLAG_HIDDENINGUI)\n\t\t\tcompiler_flag[i].flags &= ~FLAG_HIDDENINGUI;\n\t}\n\tfor (i = 0; optimisations[i].enabled; i++)\t//enabled is a pointer\n\t{\n\t\tif (fl_showall && optimisations[i].flags & FLAG_HIDDENINGUI)\n\t\t\toptimisations[i].flags &= ~FLAG_HIDDENINGUI;\n\n\t\tif (optimisations[i].flags & FLAG_HIDDENINGUI)\t//hidden optimisations are disabled as default\n\t\t\toptimisations[i].optimisationlevel = 4;\n\t}\n}\n\n\n\n\nint GUI_BuildParms(const char *args, const char **argv, int argv_size, pbool quick)//, char *forceoutputfile)\n{\n\tstatic char param[2048];\n\tint paramlen = 0;\n\tint argc;\n\tconst char *next;\n\tint i;\n\tint targ;\n\tchar *targs[] = {\"\", \"-Th2\", \"-Tfte\", \"-Tfteh2\"};\n\n\n\targc = 1;\n\targv[0] = \"fteqcc\";\n\n\tif (quick)\n\t{\n\t\tstrcpy(param+paramlen, \"-Tparse\");\n\t\targv[argc++] = param+paramlen;\n\t\tparamlen += strlen(param+paramlen)+1;\n\t}\n\n\ttarg = 0;\n\ttarg |= fl_hexen2?1:0;\n\ttarg |= fl_ftetarg?2:0;\n\tif (*targs[targ])\n\t{\n\t\tstrcpy(param+paramlen, targs[targ]);\n\t\targv[argc++] = param+paramlen;\n\t\tparamlen += strlen(param+paramlen)+1;\n\t}\n\n\tif (fl_nondfltopts)\n\t{\n\t\tfor (i = 0; optimisations[i].enabled; i++)\t//enabled is a pointer\n\t\t{\n\t\t\tif (optimisations[i].flags & FLAG_SETINGUI)\n\t\t\t\tsprintf(param+paramlen, \"-O%s\", optimisations[i].abbrev);\n\t\t\telse\n\t\t\t\tsprintf(param+paramlen, \"-Ono-%s\", optimisations[i].abbrev);\n\t\t\targv[argc++] = param+paramlen;\n\t\t\tparamlen += strlen(param+paramlen)+1;\n\t\t}\n\t}\n\n\tfor (i = 0; compiler_flag[i].enabled; i++)\t//enabled is a pointer\n\t{\n\t\tif (compiler_flag[i].flags & FLAG_SETINGUI)\n\t\t\tsprintf(param+paramlen, \"-F%s\", compiler_flag[i].abbrev);\n\t\telse\n\t\t\tsprintf(param+paramlen, \"-Fno-%s\", compiler_flag[i].abbrev);\n\t\targv[argc++] = param+paramlen;\n\t\tparamlen += strlen(param+paramlen)+1;\n\t}\n\n\n/*\twhile(*args)\n\t{\n\t\twhile (*args <= ' '&& *args)\n\t\t\targs++;\n\n\t\tfor (next = args; *next>' '; next++)\n\t\t\t;\n\t\tstrncpy(param+paramlen, args, next-args);\n\t\tparam[paramlen+next-args] = '\\0';\n\t\targv[argc++] = param+paramlen;\n\t\tparamlen += strlen(param+paramlen)+1;\n\t\targs=next;\n\t}*/\n\n//\tif (*forceoutputfile)\n//\t{\n//\t\targv[argc++] = \"-destfile\";\n//\t\targv[argc++] = forceoutputfile;\n//\t}\n\n\tif (*progssrcname)\n\t{\n\t\targv[argc++] = \"-srcfile\";\n\t\targv[argc++] = progssrcname;\n\t}\n\tif (*progssrcdir)\n\t{\n\t\targv[argc++] = \"-src\";\n\t\targv[argc++] = progssrcdir;\n\t}\n\n\twhile(*args)\n\t{\n\t\twhile (*args <= ' '&& *args)\n\t\t\targs++;\n\n\t\tif (argc >= argv_size)\n\t\t\treturn 0;\n\n\t\tfor (next = args; *next>' '; next++)\n\t\t\t;\n\t\tstrncpy(param+paramlen, args, next-args);\n\t\tparam[paramlen+next-args] = '\\0';\n\t\targv[argc++] = param+paramlen;\n\t\tparamlen += strlen(param+paramlen)+1;\n\n\t\targs=next;\n\t}\n\n\treturn argc;\n}\n\npbool GenBuiltinsList(char *buffer, int buffersize)\n{\n\tQCC_def_t *def;\n\tint usedbuffer = 0;\n\tint l;\n\tint fno;\n\tfor (fno = 0; fno < sourcefilesnumdefs; fno++)\n\t{\n\t\tfor (def = sourcefilesdefs[fno]; def; def = def->next)\n\t\t{\n\t\t\tif (def->scope)\n\t\t\t\tcontinue;   //ignore locals, because we don't know where we are, and they're probably irrelevent.\n\n\t\t\t//if its a builtin function...\n\t\t\tif (def->type->type == ev_function && def->symboldata->function && functions[def->symboldata->function].code<0)\n\t\t\t\t;\n\t\t\telse if (def->filen && strstr(def->filen, \"extensions\"))\n\t\t\t\t;\n\t\t\telse\n\t\t\t\tcontinue;\n\n\t\t\t//but ignore it if its one of those special things that you're not meant to know about.\n\t\t\tif (strcmp(def->name, \"IMMEDIATE\") && !strchr(def->name, ':') && !strchr(def->name, '.') && !strchr(def->name, '*') && !strchr(def->name, '['))\n\t\t\t{\n\t\t\t\tl = strlen(def->name);\n\t\t\t\tif (l && usedbuffer+2+l < buffersize)\n\t\t\t\t{\n\t\t\t\t\tif (usedbuffer)\n\t\t\t\t\t\tbuffer[usedbuffer++] = ' ';\n\t\t\t\t\tmemcpy(buffer+usedbuffer, def->name, l);\n\t\t\t\t\tusedbuffer += l;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tbuffer[usedbuffer] = 0;\n\treturn usedbuffer>0;\n}\n"
  },
  {
    "path": "engine/qclib/qccmain.c",
    "content": "#if !defined(MINIMAL) && !defined(OMIT_QCC)\n\n#define PROGSUSED\n#include \"qcc.h\"\n#include <sys/stat.h>\n#ifdef _WIN32\n#include <direct.h>\n#endif\n#include <time.h>\n\n#include \"errno.h\"\n\n#define countof(array) (sizeof(array)/sizeof(array[0]))\n\n//#define TODO_READWRITETRACK\n\n//#define DEBUG_DUMP\n//#define DISASM \"M_Preset\"\n\nextern QCC_def_t tempsdef;\n\nchar QCC_copyright[1024];\nint QCC_packid;\nchar QCC_Packname[5][128];\n\nextern int optres_test1;\nextern int optres_test2;\n\npbool writeasm;\nint verbose;\n#define VERBOSE_WARNINGSONLY -1\n#define VERBOSE_PROGRESS 0\n#define VERBOSE_STANDARD 1\n#define VERBOSE_DEBUG 2\n#define VERBOSE_DEBUGSTATEMENTS 3\t//figuring out the files can be expensive.\npbool qcc_nopragmaoptimise;\npbool opt_stripunusedfields;\nextern unsigned int locals_marshalled;\nextern int qccpersisthunk;\n\npbool QCC_PR_SimpleGetToken (void);\nvoid QCC_PR_LexWhitespace (pbool inhibitpreprocessor);\nstatic char *QCC_PR_String (char *string);\n\nvoid *FS_ReadToMem(char *fname, size_t *len);\nvoid FS_CloseFromMem(void *mem);\n\nunsigned int MAX_REGS;\nunsigned int MAX_LOCALS = 0x10000;\nunsigned int MAX_TEMPS = 0x10000;\n\nint\tMAX_STRINGS;\nint\tMAX_GLOBALS;\nint\tMAX_FIELDS;\nint\tMAX_STATEMENTS;\nint\tMAX_FUNCTIONS;\nint MAX_CONSTANTS;\nint max_temps;\n\nint *qcc_tempofs;\nint tempsstart;\n\n#define MAXSOURCEFILESLIST 8\nchar sourcefileslist[MAXSOURCEFILESLIST][1024];\nQCC_def_t *sourcefilesdefs[MAXSOURCEFILESLIST];\t//for the gui to peek at.\nint sourcefilesnumdefs;\t//maximum used...\nint currentsourcefile;\t//currently compiling file.\nint numsourcefiles;\t\t//count pending.\nextern char *compilingfile;\t\t//file currently being compiled\nchar compilingrootfile[1024];\t//the .src file we started from (the current one, not original)\n\nchar\tqccmsourcedir[1024];\t//the -src path, for #includes\n\nvoid QCC_PR_ResetErrorScope(void);\nstatic void StartNewStyleCompile(void);\n\npbool\tcompressoutput;\n\npbool newstylesource;\nchar\t\tdestfile[1024];\t\t//the file we're going to output to\npbool\t\tdestfile_explicit;\t\t//destfile was override on the commandline, don't let qc change it.\n\nQCC_eval_basic_t\t\t*qcc_pr_globals;\nunsigned int\tnumpr_globals;\n\nchar\t\t*strings;\nint\t\t\tstrofs;\n\nQCC_statement_t\t*statements;\nint\t\t\tnumstatements;\n\n\nQCC_function_t\t*functions;\n//dfunction_t\t*dfunctions;\nint\t\t\tnumfunctions;\n\nQCC_ddef_t\t\t*qcc_globals;\nint\t\t\tnumglobaldefs;\n\nQCC_ddef_t\t\t*fields;\nint\t\t\tnumfielddefs;\n\n//typedef char PATHSTRING[MAX_DATA_PATH];\n\nprecache_t\t*precache_sound;\nint\t\t\tnumsounds;\n\nprecache_t\t*precache_texture;\nint\t\t\tnumtextures;\n\nprecache_t\t*precache_model;\nint\t\t\tnummodels;\n\nprecache_t\t*precache_file;\nint\t\t\tnumfiles;\n\nextern int numCompilerConstants;\nhashtable_t compconstantstable;\nhashtable_t globalstable;\nhashtable_t localstable;\nhashtable_t typedeftable;\n#ifdef WRITEASM\nFILE *asmfile;\npbool asmfilebegun;\n#endif\nhashtable_t floatconstdefstable;\nhashtable_t stringconstdefstable;\nhashtable_t stringconstdefstable_trans;\nextern int dotranslate_count;\n\nunsigned char qccwarningaction[WARN_MAX];\t//0 = disabled, 1 = warn, 2 = error.\n\nunsigned int qcc_targetversion;\nqcc_targetformat_t qcc_targetformat;\n\npbool bodylessfuncs;\n\nQCC_type_t *qcc_typeinfo;\nint numtypeinfos;\nint maxtypeinfos;\n\npbool preprocessonly;\n\nstatic pbool flag_dumpfilenames;\nstatic pbool flag_dumpfields;\nstatic pbool flag_dumpsymbols;\nstatic pbool flag_dumpautocvars;\nstatic pbool flag_dumplocalisation;\nstatic pbool flag_dumptags;\nstatic pbool flag_dumpopcodes;\n\n\nstruct {\n\tchar *name;\n\tint index;\n} warningnames[] =\n{\n//\t{\"\", WARN_NOTREFERENCEDCONST},\n//\t{\"\", WARN_CONFLICTINGRETURNS},\n\t{\" Q100\", WARN_PRECOMPILERMESSAGE},\n\t{\" Q101\", WARN_TOOMANYPARAMS},\n\t//102: Indirect function: too many parameters\n\t//103: vararg func cannot have more than \n\t//104: type mismatch on parm %i\n\t{\" Q105\", WARN_TOOFEWPARAMS},\n//\t{\"\", WARN_UNEXPECTEDPUNCT},\n\t{\" Q106\", WARN_ASSIGNMENTTOCONSTANT},\n\t//107: Array index should be type int\n\t//108: Mixed float and int types\n\t//109: Expecting int, float a parameter found\n\t//110: Expecting int, float b parameter found\n\t//112: Null 'if' statement\n\t//113: Null 'else' statement\n\t//114: Type mismatch on redeclaration \n\t//115: redeclared with different number of parms \n\t//116: Local %s redeclared\n\t//117: too many initializers\n\t//118: Too many closing braces\n\t//119: Too many #endifs\n\t{\" Q120\", WARN_BADPRAGMA},\n\t//121: unknown directive\n\t//122: strofs exceeds limit\n\t//123: numstatements exceeds limit \n\t//124: numfunctions exceeds limit \n\t//125: numglobaldefs exceeds limit \n\t//126: numfielddefs exceeds limit \n\t//127: numpr_globals exceeds limit \n\t//128: rededeclared with different parms \n\t{\" F129\", WARN_COMPATIBILITYHACK},\t//multiple errors are replaced by this, for compat purposes.\n\n\t{\" Q203\", WARN_MISSINGRETURNVALUE},\n\t{\" Q204\", WARN_WRONGRETURNTYPE},\n\t{\" Q205\", WARN_POINTLESSSTATEMENT},\n\t{\" Q206\", WARN_MISSINGRETURN},\n\t{\" Q207\", WARN_DUPLICATEDEFINITION},\t//redeclared different scope\n\t{\" Q208\", WARN_SYSTEMCRC},\n//\t{\"\", WARN_STRINGTOOLONG},\n//\t{\"\", WARN_BADTARGET},\n\t//301: %s defined as local in %s\n\t{\" Q302\", WARN_NOTREFERENCED},\t\t\t//302: Unreferenced local variable %s from line %i\n\t//401: In function %s parameter %s is unused\n//\t{\"\", WARN_HANGINGSLASHR},\n//\t{\"\", WARN_NOTDEFINED},\n//\t{\"\", WARN_SWITCHTYPEMISMATCH},\n//\t{\"\", WARN_CONFLICTINGUNIONMEMBER},\n//\t{\"\", WARN_KEYWORDDISABLED},\n//\t{\"\", WARN_ENUMFLAGS_NOTINTEGER},\n//\t{\"\", WARN_ENUMFLAGS_NOTBINARY},\n\t{\" Q111\", WARN_DUPLICATELABEL},\n\t{\" Q201\", WARN_ASSIGNMENTINCONDITIONAL},\n\t{\" F300\", WARN_DEADCODE},\n\t{\" F301\", WARN_NOTUTF8},\n\t{\" F302\", WARN_UNINITIALIZED},\n\t{\" F303\", WARN_EVILPREPROCESSOR},\n\t{\" F304\", WARN_UNARYNOTSCOPE},\n\t{\" F305\", WARN_CASEINSENSITIVEFRAMEMACRO},\n\t{\" F306\", WARN_SAMENAMEASGLOBAL},\n\t{\" F307\", WARN_STRICTTYPEMISMATCH},\n\t{\" F308\", WARN_TYPEMISMATCHREDECOPTIONAL},\n\t{\" F309\", WARN_IGNORECOMMANDLINE},\n\t{\" F310\", WARN_MISUSEDAUTOCVAR},\n\t{\" F311\", WARN_FTE_SPECIFIC},\n\t{\" F312\", WARN_OVERFLOW},\n\t{\" F313\", WARN_DENORMAL},\n\t{\" F314\", WARN_LAXCAST},\n\t{\" F315\", WARN_DUPLICATEPRECOMPILER},\n\t{\" F316\", WARN_IDENTICALPRECOMPILER},\n\t{\" F317\", WARN_STALEMACRO},\n\t{\" F318\", WARN_DUPLICATEMACRO},\n\t{\" F319\", WARN_CONSTANTCOMPARISON},\n\t{\" F320\", WARN_PARAMWITHNONAME},\n\t{\" F321\", WARN_GMQCC_SPECIFIC},\n\t{\" F322\", WARN_IFSTRING_USED},\n\t{\" F323\", WARN_UNREACHABLECODE},\n\t{\" F324\", WARN_FORMATSTRING},\n\t{\" F325\", WARN_NESTEDCOMMENT},\n\t{\" F326\", WARN_DEPRECATEDVARIABLE},\n\t{\" F327\", WARN_ENUMFLAGS_NOTINTEGER},\n\t{\" F328\", WARN_DEPRECACTEDSYNTAX},\n\t{\" F329\", WARN_REDECLARATIONMISMATCH},\n\t{\" F330\", WARN_MUTEDEPRECATEDVARIABLE},\n\t{\" F331\", WARN_SELFNOTTHIS},\n\t{\" F332\", WARN_DIVISIONBY0},\n\t{\" F333\", WARN_ARGUMENTCHECK},\n\t{\" F334\", WARN_MISSINGMEMBERQUALIFIER},\n\t{\" F335\", WARN_MEMBERNOTDEFINED},\t\t//defining a new member function inside a class that didn't list it.\n\n\t{\" F207\", WARN_NOTREFERENCEDFIELD},\n\t{\" F208\", WARN_NOTREFERENCEDCONST},\n\t{\" F209\", WARN_EXTRAPRECACHE},\t\n\t{\" F210\", WARN_NOTPRECACHED},\n\t{\" F211\", WARN_SYSTEMCRC2},\n\n\t//frikqcc errors\n\t//Q608: PrecacheSound: numsounds\n\t//Q609: PrecacheModels: nummodels\n\t//Q610: PrecacheFile: numfiles\n\t//Q611: Bad parm order\n\t//Q612: PR_CompileFile: Didn't clear (internal error)\n\t//Q614: PR_DefForFieldOfs: couldn't find\n\t//Q613: Error writing error.log\n\t//Q615: Error writing\n\t//Q616: No function named\n\t//Q617: Malloc failure\n\t//Q618: Ran out of mem pointer space (malloc failure again)\n\n\t//we can put longer alternative names here...\n\t{\" field-redeclared\",\tWARN_REMOVEDWARNING},\n\t{\" deprecated\",\t\tWARN_DEPRECATEDVARIABLE},\n\t{\" bounds\",\t\t\tWARN_BOUNDS},\n\n\t{\" octal\",\t\t\tWARN_OCTAL_IMMEDIATE},\n\t{\" unimplemented\",\tWARN_IGNOREDKEYWORD},\n\t{\" localptr\",\t\tWARN_UNSAFELOCALPOINTER},\n\t{\" largereturn\",\tWARN_SLOW_LARGERETURN},\n\t{NULL}\n};\n\nchar *QCC_NameForWarning(int idx)\n{\n\tint i;\n\tfor (i = 0; warningnames[i].name; i++)\n\t{\n\t\tif (warningnames[i].index == idx)\n\t\t\treturn warningnames[i].name;\n\t}\n\treturn NULL;\n}\nint QCC_WarningForName(const char *name)\n{\n\tint i;\n\tfor (i = 0; warningnames[i].name; i++)\n\t{\n\t\tif (!stricmp(name, warningnames[i].name+1))\n\t\t\treturn warningnames[i].index;\n\t}\n\treturn -1;\n}\n\noptimisations_t optimisations[] =\n{\n\t//level 0 = no optimisations\n\t//level 1 = size optimisations\n\t//level 2 = speed optimisations\n\t//level 3 = unsafe optimisations (they break multiprogs).\n\t//level 4 = experimental or extreeme features... must be used explicitly\n\n\t{&opt_assignments,\t\t\t\t\"t\",\t1,\tFLAG_ASDEFAULT,\t\t\t\"assignments\",\t\t\"c = a*b is performed in one operation rather than two, and can cause older decompilers to fail.\"},\n\t{&opt_shortenifnots,\t\t\t\"i\",\t1,\tFLAG_ASDEFAULT,\t\t\t\"shortenifs\",\t\t\"if (!a) was traditionally compiled in two statements. This optimisation does it in one, but can cause some decompilers to get confused.\"},\n\t{&opt_nonvec_parms,\t\t\t\t\"p\",\t1,\tFLAG_ASDEFAULT,\t\t\t\"nonvec_parms\",\t\t\"In the original qcc, function parameters were specified as a vector store even for floats. This fixes that.\"},\n\t{&opt_constant_names,\t\t\t\"c\",\t2,\tFLAG_KILLSDEBUGGERS,\t\"constant_names\",\t\"This optimisation strips out the names of constants (but not strings) from your progs, resulting in smaller files. It makes decompilers leave out names or fabricate numerical ones.\"},\n\t{&opt_constant_names_strings,\t\"cs\",\t3,\tFLAG_KILLSDEBUGGERS,\t\"constant_names_strings\", \"This optimisation strips out the names of string constants from your progs. However, this can break addons, so don't use it in those cases.\"},\n\t{&opt_dupconstdefs,\t\t\t\t\"d\",\t1,\tFLAG_ASDEFAULT,\t\t\t\"dupconstdefs\",\t\t\"This will merge definitions of constants which are the same value. Pay extra attention to assignment to constant warnings.\"},\n\t{&opt_noduplicatestrings,\t\t\"s\",\t1,\tFLAG_ASDEFAULT,\t\t\t\"noduplicatestrings\", \"This will compact the string table that is stored in the progs. It will be considerably smaller with this.\"},\n\t{&opt_locals,\t\t\t\t\t\"l\",\t1,\tFLAG_KILLSDEBUGGERS,\t\"locals\",\t\t\t\"Strips out local names and definitions. Most decompiles will break on this.\"},\n\t{&opt_function_names,\t\t\t\"n\",\t1,\tFLAG_KILLSDEBUGGERS,\t\"function_names\",\t\"This strips out the names of functions which are never called. Doesn't make much of an impact though.\"},\n\t{&opt_filenames,\t\t\t\t\"f\",\t1,\tFLAG_KILLSDEBUGGERS,\t\"filenames\",\t\t\"This strips out the filenames of the progs. This can confuse the really old decompilers, but is nothing to the more recent ones.\"},\n//\t{&opt_unreferenced,\t\t\t\t\"u\",\t1,\tFLAG_ASDEFAULT,\t\t\t\"unreferenced\",\t\t\"Removes the entries of unreferenced variables. Doesn't make a difference in well maintained code.\"},\n\t{&opt_overlaptemps,\t\t\t\t\"r\",\t1,\tFLAG_ASDEFAULT,\t\t\t\"overlaptemps\",\t\t\"Optimises the pr_globals count by overlapping temporaries. In QC, every multiplication, division or operation in general produces a temporary variable. This optimisation prevents excess, and in the case of Hexen2's gamecode, reduces the count by 50k. This is the most important optimisation, ever.\"},\n\t{&opt_constantarithmatic,\t\t\"a\",\t1,\tFLAG_ASDEFAULT,\t\t\t\"constantarithmatic\", \"5*6 actually emits an operation into the progs. This prevents that happening, effectivly making the compiler see 30\"},\n\t{&opt_precache_file,\t\t\t\"pf\",\t2,\t0,\t\t\t\t\t\t\"precache_file\",\t\"Strip out stuff wasted used in function calls and strings to the precache_file builtin (which is actually a stub in quake).\"},\n\t{&opt_return_only,\t\t\t\t\"ro\",\t2,\tFLAG_KILLSDEBUGGERS,\t\"return_only\",\t\t\"Functions ending in a return statement do not need a done statement at the end of the function. This can confuse some decompilers, making functions appear larger than they were.\"},\n\t{&opt_compound_jumps,\t\t\t\"cj\",\t2,\tFLAG_KILLSDEBUGGERS,\t\"compound_jumps\",\t\"This optimisation plays an effect mostly with nested if/else statements, instead of jumping to an unconditional jump statement, it'll jump to the final destination instead. This will bewilder decompilers.\"},\n//\t{&opt_comexprremoval,\t\t\t\"cer\",\t4,\t0,\t\t\t\t\t\t\"expression_removal\",\t\"Eliminate common sub-expressions\"},\t//this would be too hard...\n\t{&opt_stripfunctions,\t\t\t\"sf\",\t3,\tFLAG_KILLSDEBUGGERS,\t\"strip_functions\",\t\"Strips out the 'defs' of functions that were only ever called directly. This does not affect saved games, but will prevent FTE_MULTIPROGS/mutators from being able to hook functions.\"},\n\t{&opt_locals_overlapping,\t\t\"lo\",\t2,\tFLAG_KILLSDEBUGGERS,\t\"locals_overlapping\", \"Store all locals in a single section of the pr_globals. Vastly reducing it. This effectivly does the job of overlaptemps.\\nHowever, locals are no longer automatically initialised to 0 (and never were in the case of recursion, but at least then its the same type).\\nIf locals appear uninitialised, fteqcc will disable this optimisation for the affected functions, you can optionally get a warning about these locals using: #pragma warning enable F302\"},\n\t{&opt_vectorcalls,\t\t\t\t\"vc\",\t4,\tFLAG_KILLSDEBUGGERS,\t\"vectorcalls\",\t\t\"Where a function is called with just a vector, this causes the function call to store three floats instead of one vector. This can save a good number of pr_globals where those vectors contain many duplicate coordinates but do not match entirly.\"},\n\t{&opt_classfields,\t\t\t\t\"cf\",\t2,\tFLAG_KILLSDEBUGGERS,\t\"class_fields\",\t\t\"Strip class field names. This will harm debugging and can result in 'gibberish' names appearing in saved games. Has no effect on engines other than FTEQW, which will not recognise these anyway.\"},\n\t{&opt_stripunusedfields,\t\t\"uf\",\t4,\tFLAG_KILLSDEBUGGERS,\t\"strip_unused_fields\",\"Strips any fields that have no references. This may result in extra warnings at load time, or disabling support for mapper-specified alpha in engines that do not provide that. FIXME: this is a little pointless until relocs are properly implemented.\"},\n\t{NULL}\n};\n\n#define defaultkeyword\t\tFLAG_HIDDENINGUI|FLAG_ASDEFAULT|FLAG_MIDCOMPILE\n#define typekeyword\t\t\tFLAG_HIDDENINGUI|FLAG_ASDEFAULT\n#define nondefaultkeyword\tFLAG_HIDDENINGUI|0|FLAG_MIDCOMPILE\n#define hideflag\t\t\tFLAG_HIDDENINGUI|FLAG_MIDCOMPILE\n#define defaultflag\t\t\tFLAG_ASDEFAULT|FLAG_MIDCOMPILE\n#define hidedefaultflag\t\tFLAG_HIDDENINGUI|FLAG_ASDEFAULT|FLAG_MIDCOMPILE\n//global to store useage to, flags, codename, human-readable name, help text\ncompiler_flag_t compiler_flag[] = {\n\t//keywords\n\t{&keyword_asm,\t\t\tdefaultkeyword, \"asm\",\t\t\t\"Keyword: asm\",\t\t\t\"Disables the 'asm' keyword. Use the writeasm flag to see an example of the asm.\"},\n\t{&keyword_break,\t\tdefaultkeyword, \"break\",\t\t\"Keyword: break\",\t\t\"Disables the 'break' keyword.\"},\n\t{&keyword_case,\t\t\tdefaultkeyword, \"case\",\t\t\t\"Keyword: case\",\t\t\"Disables the 'case' keyword.\"},\n\t{&keyword_class,\t\tdefaultkeyword, \"class\",\t\t\"Keyword: class\",\t\t\"Disables the 'class' keyword.\"},\n\t{&keyword_accessor,\t\tdefaultkeyword, \"accessor\",\t\t\"Keyword: accessor\",\t\"Disables the 'accessor' keyword.\"},\n\t{&keyword_const,\t\tdefaultkeyword, \"const\",\t\t\"Keyword: const\",\t\t\"Disables the 'const' keyword.\"},\n\t{&keyword_continue,\t\tdefaultkeyword, \"continue\",\t\t\"Keyword: continue\",\t\"Disables the 'continue' keyword.\"},\n\t{&keyword_default,\t\tdefaultkeyword, \"default\",\t\t\"Keyword: default\",\t\t\"Disables the 'default' keyword.\"},\n\t{&keyword_entity,\t\tdefaultkeyword, \"entity\",\t\t\"Keyword: entity\",\t\t\"Disables the 'entity' keyword.\"},\n\t{&keyword_enum,\t\t\tdefaultkeyword, \"enum\",\t\t\t\"Keyword: enum\",\t\t\"Disables the 'enum' keyword.\"},\t//kinda like in c, but typedef not supported.\n\t{&keyword_enumflags,\tdefaultkeyword, \"enumflags\",\t\"Keyword: enumflags\",\t\"Disables the 'enumflags' keyword.\"},\t//like enum, but doubles instead of adds 1.\n\t{&keyword_extern,\t\tdefaultkeyword, \"extern\",\t\t\"Keyword: extern\",\t\t\"Disables the 'extern' keyword. Use only on functions inside addons.\"},\t//function is external, don't error or warn if the body was not found\n\t{&keyword_float,\t\tdefaultkeyword, \"float\",\t\t\"Keyword: float\",\t\t\"Disables the 'float' keyword. (Disables the float keyword without 'local' preceeding it)\"},\n\t{&keyword_for,\t\t\tdefaultkeyword, \"for\",\t\t\t\"Keyword: for\",\t\t\t\"Disables the 'for' keyword. Syntax: for(assignment; while; increment) {codeblock;}\"},\n\t{&keyword_goto,\t\t\tdefaultkeyword, \"goto\",\t\t\t\"Keyword: goto\",\t\t\"Disables the 'goto' keyword.\"},\n\t{&keyword_int,\t\t\ttypekeyword,\t\"int\",\t\t\t\"Keyword: int\",\t\t\t\"Disables the 'int' keyword.\"},\n\t{&keyword_integer,\t\ttypekeyword,\t\"integer\",\t\t\"Keyword: integer\",\t\t\"Disables the 'integer' keyword.\"},\n\t{&keyword_double,\t\tnondefaultkeyword, \"double\",\t\"Keyword: double\",\t\t\"Disables the 'double' keyword.\"},\n\t{&keyword_long,\t\t\tnondefaultkeyword, \"long\",\t\t\"Keyword: long\",\t\t\"Disables the 'long' keyword.\"},\n\t{&keyword_short,\t\tnondefaultkeyword, \"short\",\t\t\"Keyword: short\",\t\t\"Disables the 'short' keyword.\"},\n\t{&keyword_char,\t\t\tnondefaultkeyword, \"char\",\t\t\"Keyword: char\",\t\t\"Disables the 'char' keyword.\"},\n\t{&keyword_signed,\t\tnondefaultkeyword, \"signed\",\t\"Keyword: signed\",\t\t\"Disables the 'signed' keyword.\"},\n\t{&keyword_unsigned,\t\tdefaultkeyword, \"unsigned\",\t\t\"Keyword: unsigned\",\t\"Disables the 'unsigned' keyword.\"},\n\t{&keyword_register,\t\tnondefaultkeyword, \"register\",\t\"Keyword: register\",\t\"Disables the 'register' keyword.\"},\n\t{&keyword_volatile,\t\tnondefaultkeyword, \"volatile\",\t\"Keyword: volatile\",\t\"Disables the 'volatile' keyword.\"},\n\t{&keyword_noref,\t\tdefaultkeyword, \"noref\",\t\t\"Keyword: noref\",\t\t\"Disables the 'noref' keyword.\"},\t//nowhere else references this, don't warn about it.\n\t{&keyword_unused,\t\tnondefaultkeyword, \"unused\",\t\"Keyword: unused\",\t\t\"Disables the 'unused' keyword. 'unused' means that the variable is unused, you're aware that its unused, and you'd rather not know about all the warnings this results in.\"},\n\t{&keyword_used,\t\t\tnondefaultkeyword, \"used\",\t\t\"Keyword: used\",\t\t\"Disables the 'used' keyword. 'used' means that the variable is used even if the qcc can't see how - thus preventing it from ever being stripped.\"},\n\t{&keyword_local,\t\tdefaultkeyword, \"local\",\t\t\"Keyword: local\",\t\t\"Disables the 'local' keyword.\"},\n\t{&keyword_static,\t\tdefaultkeyword, \"static\",\t\t\"Keyword: static\",\t\t\"Disables the 'static' keyword. 'static' means that a variable has altered scope. On globals, the variable is visible only to the current .qc file. On locals, the variable's value does not change between calls to the function. On class variables, specifies that the field is a scoped global instead of a local. On class functions, specifies that 'this' is expected to be invalid and that the function will access any memembers via it.\"},\n\t{&keyword_nonstatic,\tdefaultkeyword, \"nonstatic\",\t\"Keyword: nonstatic\",\t\"Disables the 'nonstatic' keyword. 'nonstatic' acts upon globals+functions, reverting the defaultstatic pragma on a per-variable basis. For use by people who prefer to keep their APIs explicit.\"},\n\t{&keyword_ignore,\t\tnondefaultkeyword, \"ignore\",\t\"Keyword: ignore\",\t\t\"Disables the 'ignore' keyword. 'ignore' is expected to typically be hidden behind a 'csqconly' define, and in such a context can be used to conditionally compile functions a little more gracefully. The opposite of the 'used' keyword. These variables/functions/members are ALWAYS stripped, and effectively ignored.\"},\n\t{&keyword_auto,\t\t\tnondefaultkeyword, \"auto\",\t\t\"Keyword: auto\",\t\t\"Disables the 'auto' keyword. This keyword denotes the variable as one that acts like C does, allowing locals to be passed recursively without screwing up the value. Can also be used on uninitialised globals to allocate at loadtime to reduce the size of the dat.\"},\n\n\t{&keyword_nosave,\t\tdefaultkeyword, \"nosave\",\t\t\"Keyword: nosave\",\t\t\"Disables the 'nosave' keyword.\"},\t//don't write the def to the output.\n\t{&keyword_inline,\t\tdefaultkeyword, \"inline\",\t\t\"Keyword: inline\",\t\t\"Disables the 'inline' keyword.\"},\t//don't write the def to the output.\n\t{&keyword_strip,\t\tdefaultkeyword, \"strip\",\t\t\"Keyword: strip\",\t\t\"Disables the 'strip' keyword.\"},\t//don't write the def to the output.\n\t{&keyword_shared,\t\tdefaultkeyword, \"shared\",\t\t\"Keyword: shared\",\t\t\"Disables the 'shared' keyword.\"},\t//mark global to be copied over when progs changes (part of FTE_MULTIPROGS)\n\t{&keyword_state,\t\tnondefaultkeyword,\"state\",\t\t\"Keyword: state\",\t\t\"Disables the 'state' keyword.\"},\n\t{&keyword_optional,\t\tdefaultkeyword,\"optional\",\t\t\"Keyword: optional\",\t\"Disables the 'optional' keyword.\"},\n\t{&keyword_inout,\t\tnondefaultkeyword,\"inout\",\t\t\"Keyword: inout\",\t\t\"Disables the 'inout' keyword.\"},\n\t{&keyword_string,\t\tdefaultkeyword, \"string\",\t\t\"Keyword: string\",\t\t\"Disables the 'string' keyword.\"},\n\t{&keyword_struct,\t\tdefaultkeyword, \"struct\",\t\t\"Keyword: struct\",\t\t\"Disables the 'struct' keyword.\"},\n\t{&keyword_switch,\t\tdefaultkeyword, \"switch\",\t\t\"Keyword: switch\",\t\t\"Disables the 'switch' keyword.\"},\n\t{&keyword_thinktime,\tnondefaultkeyword,\"thinktime\",\t\"Keyword: thinktime\",\t\"Disables the 'thinktime' keyword which is used in HexenC\"},\n\t{&keyword_until,\t\tnondefaultkeyword,\"until\",\t\t\"Keyword: until\",\t\t\"Disables the 'until' keyword which is used in HexenC\"},\n\t{&keyword_loop,\t\t\tnondefaultkeyword,\"loop\",\t\t\"Keyword: loop\",\t\t\"Disables the 'loop' keyword which is used in HexenC\"},\n\t{&keyword_typedef,\t\tdefaultkeyword, \"typedef\",\t\t\"Keyword: typedef\",\t\t\"Disables the 'typedef' keyword.\"},\t//fixme\n\t{&keyword_union,\t\tdefaultkeyword, \"union\",\t\t\"Keyword: union\",\t\t\"Disables the 'union' keyword.\"},\t//you surly know what a union is!\n\t{&keyword_var,\t\t\tdefaultkeyword, \"var\",\t\t\t\"Keyword: var\",\t\t\t\"Disables the 'var' keyword.\"},\n\t{&keyword_vector,\t\tdefaultkeyword, \"vector\",\t\t\"Keyword: vector\",\t\t\"Disables the 'vector' keyword.\"},\n\t{&keyword_wrap,\t\t\tdefaultkeyword, \"wrap\",\t\t\t\"Keyword: wrap\",\t\t\"Disables the 'wrap' keyword.\"},\n\t{&keyword_weak,\t\t\tdefaultkeyword, \"weak\",\t\t\t\"Keyword: weak\",\t\t\"Disables the 'weak' keyword.\"},\n\t{&keyword_accumulate,\tnondefaultkeyword,\"accumulate\",\t\"Keyword: accumulate\",\t\"Disables the 'accumulate' keyword.\"},\n\t{&keyword_using,\t\tnondefaultkeyword,\"using\",\t\t\"Keyword: using\",\t\t\"Disables the 'using' keyword.\"},\n\n\t//options\n\t{&flag_acc,\t\t\t\t0,\t\t\t\t\"acc\",\t\t\t\"Reacc support\",\t\t\"Reacc is a pascall like compiler. It was released before the Quake source was released. This flag has a few effects. It sorts all qc files in the current directory into alphabetical order to compile them. It also allows Reacc global/field distinctions, as well as allows | for linebreaks. Whilst case insensitivity and lax type checking are supported by reacc, they are seperate compiler flags in fteqcc.\"},\t\t//reacc like behaviour of src files.\n\t{&flag_qccx,\t\t\tFLAG_MIDCOMPILE,\"qccx\",\t\t\t\"QCCX syntax\",\t\t\t\"WARNING: This syntax makes mods inherantly engine specific.\\nDo NOT use unless you know what you're doing.This is provided for compatibility only\\nAny entity hacks will be unsupported in FTEQW, DP, and others, resulting in engine crashes if the code in question is executed.\"},\n\t{&keywords_coexist,\t\tdefaultflag,\t\"kce\",\t\t\t\"Keywords Coexist\",\t\t\"If you want keywords to NOT be disabled when they a variable by the same name is defined, check here.\"},\n//\t{&flag_lno,\t\t\t\tdefaultflag,\t\"lno\",\t\t\t\"Write Line Numbers\",\t\"Writes line number information. This is required for any real kind of debugging. Will be ignored if filenames were stripped.\"},\n\t{&output_parms,\t\t\t0,\t\t\t\t\"parms\",\t\t\"Define offset parms\",\t\"if PARM0 PARM1 etc should be defined by the compiler. These are useful if you make use of the asm keyword for function calls, or you wish to create your own variable arguments. This is an easy way to break decompilers.\"},\t//controls weather to define PARMx for the parms (note - this can screw over some decompilers)\n\t{&autoprototype,\t\t0,\t\t\t\t\"autoproto\",\t\"Automatic Prototyping\",\"Causes compilation to take two passes instead of one. The first pass, only the definitions are read. The second pass actually compiles your code. This means you never have to remember to prototype functions again.\"},\t//so you no longer need to prototype functions and things in advance.\n\t{&writeasm,\t\t\t\t0,\t\t\t\t\"wasm\",\t\t\t\"Dump Assembler\",\t\t\"Writes out a qc.asm which contains all your functions but in assembler. This is a great way to look for bugs in fteqcc, but can also be used to see exactly what your functions turn into, and thus how to optimise statements better.\"},\t\t\t//spit out a qc.asm file, containing an assembler dump of the ENTIRE progs. (Doesn't include initialisation of constants)\n\t{&flag_guiannotate,\t\tFLAG_MIDCOMPILE,\"annotate\",\t\t\"Annotate Sourcecode\",\t\"Annotate source code with assembler statements on compile (requires gui).\"},\n\t{&flag_nullemptystr,\tFLAG_MIDCOMPILE,\"nullemptystr\",\t\"Null String Immediates\",\t\t\"Empty string immediates will have the raw value 0 instead of 1.\"},\n\t{&flag_ifstring,\t\tFLAG_MIDCOMPILE,\"ifstring\",\t\t\"if(string) fix\",\t\t\"Causes if(string) to behave identically to if(string!=\"\") This is most useful with addons of course, but also has adverse effects with FRIK_FILE's fgets, where it becomes impossible to determin the end of the file. In such a case, you can still use asm {IF string 2;RETURN} to detect eof and leave the function.\"},\t\t//correction for if(string) no-ifstring to get the standard behaviour.\n\t{&flag_iffloat,\t\t\tFLAG_MIDCOMPILE,\"iffloat\",\t\t\"if(-0.0) fix\",\"Fixes certain floating point logic.\"},\n\t{&flag_ifvector,\t\tdefaultflag,\t\"ifvector\",\t\t\"if('0 1 0') fix\",\"Fixes conditional vector logic.\"},\n\t{&flag_vectorlogic,\t\tdefaultflag,\t\"vectorlogic\",\t\"v&&v||v fix\",\t\"Fixes conditional vector logic.\"},\n\t{&flag_brokenarrays,\tFLAG_MIDCOMPILE,\"brokenarray\",\t\"array[0] omission\",\t\"Treat references to arrays as references to the first index of said array, to replicate an old fteqcc bug.\"},\n\t{&flag_rootconstructor,\tFLAG_MIDCOMPILE,\"rootconstructor\",\"root constructor first\",\t\"When enabled, the root constructor should be called first like in c++.\"},\n\t{&flag_caseinsensitive,\t0,\t\t\t\t\"caseinsens\",\t\"Case insensitivity\",\t\"Causes fteqcc to become case insensitive whilst compiling names. It's generally not advised to use this as it compiles a little more slowly and provides little benefit. However, it is required for full reacc support.\"},\t//symbols will be matched to an insensitive case if the specified case doesn't exist. This should b usable for any mod\n\t{&flag_laxcasts,\t\tFLAG_MIDCOMPILE,\"lax\",\t\t\t\"Lax type checks\",\t\t\"Disables many errors (generating warnings instead) when function calls or operations refer to two normally incompatible types. This is required for reacc support, and can also allow certain (evil) mods to compile that were originally written for frikqcc.\"},\t\t//Allow lax casting. This'll produce loadsa warnings of course. But allows compilation of certain dodgy code.\n\t{&flag_hashonly,\t\tFLAG_MIDCOMPILE,\"hashonly\",\t\t\"Hash-only constants\",\t\"Allows use of only #constant for precompiler constants, allows certain preqcc using mods to compile\"},\n\t{&flag_macroinstrings,\tFLAG_MIDCOMPILE,\"macroinstrings\",\"Expand macros in strings\",\t\"Allows the use #constant inside string immediates. This is a feature of preqcc, but should otherwise probably be left disabled.\"},\n\t{&opt_logicops,\t\t\tFLAG_MIDCOMPILE,\"lo\",\t\t\t\"Logic ops\",\t\t\t\"This changes the behaviour of your code. It generates additional if operations to early-out in if statements. With this flag, the line if (0 && somefunction()) will never call the function. It can thus be considered an optimisation. However, due to the change of behaviour, it is not considered so by fteqcc. Note that due to inprecisions with floats, this flag can cause runaway loop errors within the player walk and run functions (without iffloat also enabled). This code is advised:\\nplayer_stand1:\\n    if (self.velocity_x || self.velocity_y)\\nplayer_run\\n    if (!(self.velocity_x || self.velocity_y))\"},\n\t{&flag_msvcstyle,\t\tFLAG_MIDCOMPILE,\"msvcstyle\",\t\"MSVC-style errors\",\t\"Generates warning and error messages in a format that msvc understands, to facilitate ide integration.\"},\n\t{&flag_debugmacros,\t\tFLAG_MIDCOMPILE,\"debugmacros\",\t\"Verbose Macro Expansion\",\t\"Print out the contents of macros that are expanded. This can help look inside macros that are expanded and is especially handy if people are using preprocessor hacks.\"},\n\t{&flag_filetimes,\t\thideflag,\t\t\"filetimes\",\t\"Check Filetimes\",\t\t\"Recompiles the progs only if the file times are modified.\"},\n\t{&flag_fasttrackarrays,\tFLAG_MIDCOMPILE,\"fastarrays\",\t\"fast arrays where possible\",\t\"Generates extra instructions inside array handling functions to detect engine and use extension opcodes only in supporting engines.\\nAdds a global which is set by the engine if the engine supports the extra opcodes. Note that this applies to all arrays or none.\"},\n\t{&flag_assume_integer,\tFLAG_MIDCOMPILE,\"assumeint\",\t\"Assume Integers\",\t\t\"Numerical constants are assumed to be integers, instead of floats.\"},\n\t{&pr_subscopedlocals,\tFLAG_MIDCOMPILE,\"subscope\",\t\t\"Subscoped Locals\",\t\t\"Restrict the scope of locals to the block they are actually defined within, as in C.\"},\n\t{&verbose,\t\t\t\tFLAG_MIDCOMPILE,\"verbose\",\t\t\"Verbose\",\t\t\t\t\"Lots of extra compiler messages.\"},\n\t{&flag_typeexplicit,\tFLAG_MIDCOMPILE,\"typeexplicit\",\t\"Explicit types\",\t\t\"All type conversions must be explicit or directly supported by instruction set.\"},\n\t{&flag_boundchecks,\t\tdefaultflag,\t\"boundchecks\",\t\"Enforce Bound Checks\",\t\"Enforce array index checks to avoid accessing arrays out of bounds. This can be disabled for a speedup (the qcvm will still verify that the access is within the qcvm's memory, but it can't verify that its within the intended array).\"},\n\t{&flag_attributes,\t\thideflag,\t\t\"attributes\",\t\"[[attributes]]\",\t\t\"WARNING: This syntax conflicts with vector constructors.\"},\n\t{&flag_assumevar,\t\thideflag,\t\t\"assumevar\",\t\"explicit consts\",\t\t\"Initialised globals will be considered non-const by default.\"},\n\t{&flag_dblstarexp,\t\thideflag,\t\t\"ssp\",\t\t\t\"** exponent\",\t\t\t\"Treat ** as an operator for exponents, instead of multiplying by a dereferenced pointer.\"},\n\t{&flag_cpriority,\t\thideflag,\t\t\"cpriority\",\t\"C Operator Priority\",\t\"QC treats !a&&b as equivelent to !(a&&b). When this is set, behaviour will be (!a)&&b as in C. Other operators are also affected in similar ways.\"},\n\t{&flag_assume_double,\thideflag,\t\t\"assumedouble\",\t\"Assume Doubles\",\t\t\"Floating point immediates will be treated as doubles, for C compat.\"},\n\t{&flag_qcfuncs,\t\t\thidedefaultflag,\"qcfuncs\",\t\t\"Parse QC-style funcs\",\t\"Recognise void() as a function type. Required for QC compat.\"},\n\t{&flag_allowuninit,\t\thideflag,\t\t\"allowuninit\",\t\"Uninitialised Locals\",\t\"Permit optimisations that may result in locals being uninitialised. This may allow for greater reductions in temps.\"},\n\t{&flag_nopragmafileline,FLAG_MIDCOMPILE,\"nofileline\",\t\"Ignore #pragma file\",\t\"Ignores #pragma file(foo) and #pragma line(foo), so that errors and symbols reflect the actual lines, instead of the original source.\"},\n//\t{&flag_lno,\t\t\t\thidedefaultflag,\"lno\",\t\t\t\"Gen Debugging Info\",\t\"Writes debugging info.\"},\n\t{&flag_utf8strings,\t\tFLAG_MIDCOMPILE,\"utf8\",\t\t\t\"Unicode\",\t\t\t\t\"String immediates will use utf-8 encoding, instead of quake's encoding.\"},\n\t{&flag_reciprocalmaths,\tFLAG_MIDCOMPILE,\"reciprocal-math\",\"Reciprocal Maths\",\t\"Optimise x/const as x*(1/const).\"},\n\t{&flag_ILP32,\t\t\tFLAG_MIDCOMPILE,\"ILP32\",\t\t\"ILP32 Data Model\",\t\t\"Restricts the size of `long` to 32bits, consistent with pointers.\"},\n\t{&flag_undefwordsize,\thideflag,\t\t\"undefwordsize\",\"Undefined Word Size\",\t\"Do not make assumptions about pointer types and word sizes. Block functionality that depends upon specific word sizes. Changed as part of target.\"},\n\t{&flag_pointerrelocs,\thideflag,\t\t\"pointerrelocs\",\"Initialised Pointers\",\t\"Allow pointer types to be preinitialised at compile time, both at global scope and optimising local scope a little.\"},\n\t{&flag_noreflection,\tFLAG_MIDCOMPILE,\"omitinternals\",\"Omit Reflection Info\",\t\"Keeps internal symbols private (equivelent to unix's hidden visibility). This has the effect of reducing filesize, thwarting debuggers, and breaking saved games. This allows you to use arrays without massively bloating the size of your progs.\\nWARNING: The bit about breaking saved games was NOT a joke, but does not apply to menuqc or csqc. It also interferes with FTE_MULTIPROGS.\"},\n\n\t{&flag_embedsrc,\t\tFLAG_MIDCOMPILE,\"embedsrc\",\t\t\"Embed Sources\",\t\t\"Write the sourcecode into the output file. The resulting .dat can be opened as a standard zip archive (or by fteqccgui).\\nGood for GPL compliance!\"},\n\t{&flag_dumpfilenames,\tFLAG_MIDCOMPILE,\"dumpfilenames\",\"Write a .lst file\",\t\"Writes a .lst file which contains a list of all file names that we can detect from the qc. This file list can then be passed into external compression tools.\"},\n\t{&flag_dumpfields,\t\tFLAG_MIDCOMPILE,\"dumpfields\",\t\"Write a .fld file\",\t\"Writes a .fld file that shows which fields are defined, along with their offsets etc, for weird debugging.\"},\n\t{&flag_dumpsymbols,\t\tFLAG_MIDCOMPILE,\"dumpsymbols\",\t\"Write a .sym file\",\t\"Writes a .sym file alongside the dat which contains a list of all global symbols defined in the code (before stripping)\"},\n\t{&flag_dumpautocvars,\tFLAG_MIDCOMPILE,\"dumpautocvars\",\"Write a .cfg file\",\t\"Writes a .cfg file that contains a default value for each autocvar listed in the code\"},\n\t{&flag_dumplocalisation,FLAG_MIDCOMPILE,\"dumplocalisation\",\"Write a .pot file\",\t\"Writes a .po template file from your _(\"\") strings that can be edited (with eg gettext's tools) for translations, resulting in eg csprogs.en_US.po vs csprogs.en.po and other various other dialects vs languages.\"},\n\t{&flag_dumptags,\t\tFLAG_MIDCOMPILE,\"dumptags\",\t\t\"Write vi's tags file\",\t\"Writes a .tags file for text editors compatible with vi to locate symbols by name. This file is unsorted and per-module, so you will need to 'cat ../*.tags|LC_COLLATE=C sort>tags' for it to be used.\"},\n\t{&flag_dumptags,\t\tFLAG_MIDCOMPILE|FLAG_HIDDENINGUI,\"tags\",\"aka dumptags\",\t\"See dumptags\"},\n\t{&flag_dumpopcodes,\t\tFLAG_MIDCOMPILE,\"dumpopcodes\",\t\t\"Write a .op.inc file with the supported opcodes in C syntax\",\t\"Writes a .inc file for game engines to include to have opcodes defined. Executes at the end of the compile, and thus respects opcodes added via #pragma.\"},\n\t{NULL}\n};\n\nstatic const char *QCC_VersionString(void)\n{\n#if defined(SVNREVISION) && defined(SVNDATE)\n\treturn \"FTEQCC: \" STRINGIFY(SVNREVISION) \" (\" STRINGIFY(SVNDATE) \")\";\n#elif defined(SVNREVISION)\n\treturn \"FTEQCC: \" STRINGIFY(SVNREVISION) \" (\" __DATE__\")\";\n#else\n\treturn \"FTEQCC: \" __DATE__;\n#endif\n}\n\n/*\n=================\nBspModels\n\nRuns qbsp and light on all of the models with a .bsp extension\n=================\n*/\n\nstatic void QCC_BspModels (void)\n{\n/*\n\tint\t\tp;\n\tchar\t*gamedir;\n\tint\t\ti;\n\tchar\t*m;\n\tchar\tcmd[1024];\n\tchar\tname[256];\n\tsize_t result;\n\n\tp = QCC_CheckParm (\"-bspmodels\");\n\tif (!p)\n\t\treturn;\n\tif (p == myargc-1)\n\t\tQCC_Error (ERR_BADPARMS, \"-bspmodels must preceed a game directory\");\n\tgamedir = myargv[p+1];\n\n\tfor (i=0 ; i<nummodels ; i++)\n\t{\n\t\tm = precache_models[i];\n\t\tif (strcmp(m+strlen(m)-4, \".bsp\"))\n\t\t\tcontinue;\n\t\tstrcpy (name, m);\n\t\tname[strlen(m)-4] = 0;\n\t\tsprintf (cmd, \"qbsp %s/%s ; light -extra %s/%s\", gamedir, name, gamedir, name);\n\t\tresult = system (cmd); // do something with the result\n\n\t\tif (result != 0)\n\t\t\tQCC_Error(ERR_INTERNAL, \"QCC_BspModels() system returned non zero (failure) with: qbsp %s/%s ; light -extra %s/%s (%i)\\n\", gamedir, name, gamedir, name, errno);\n\t}\n*/\n}\n\ntypedef struct stringtab_s\n{\n\tstruct stringtab_s *prev;\n\tchar *ofs;\n\tint len;\n} stringtab_t;\nstringtab_t *stringtablist[256];\n// CopyString returns an offset from the string heap\nint\tQCC_CopyString (const char *str)\n{\n\tint\t\told;\n\tsize_t len;\n\n\tif (!str)\n\t\treturn 0;\n\tif (!*str)\n\t\treturn !flag_nullemptystr;\n\n\tif (opt_noduplicatestrings || !strcmp(str, \"IMMEDIATE\"))\n\t{\n#if 1\n\t\t//more scalable (faster) version.\n\t\tstringtab_t *t;\n\t\tint len = strlen(str);\n\t\tunsigned char key = str[len-1];\n\t\tfor (t = stringtablist[key]; t; t = t->prev)\n\t\t{\n\t\t\tif (t->len >= len)\n\t\t\t\tif (!strcmp(t->ofs + t->len - len, str))\n\t\t\t\t{\n\t\t\t\t\toptres_noduplicatestrings += len;\n\t\t\t\t\treturn (t->ofs + t->len - len)-strings;\n\t\t\t\t}\n\t\t}\n\t\tt = qccHunkAlloc(sizeof(*t));\n\t\tt->prev = stringtablist[key];\n\t\tstringtablist[key] = t;\n\t\tt->ofs = strings+strofs;\n\t\tt->len = len;\n#elif 1\n\t\t//bruteforce\n\t\tchar *s;\n\t\tfor (s = strings; s < strings+strofs; s++)\n\t\t\tif (!strcmp(s, str))\n\t\t\t{\n\t\t\t\toptres_noduplicatestrings += strlen(str);\n\t\t\t\treturn s-strings;\n\t\t\t}\n#endif\n\t}\n\n\told = strofs;\n\tlen = strlen(str)+1;\n\tif ( (strofs + len) > MAX_STRINGS)\n\t\tQCC_Error(ERR_INTERNAL, \"QCC_CopyString: -max_strings %i limit exceeded\\n\", MAX_STRINGS);\n\tmemcpy (strings+strofs, str, len);\n\tstrofs += len;\n\treturn old;\n}\n\nint\tQCC_CopyStringLength (const char *str, size_t length)\n{\n\tint\t\told;\n\n\tif (!str)\n\t\treturn 0;\n\tif (!*str && length == 1)\n\t\treturn !flag_nullemptystr;\n\n\told = strofs;\n\tif ( (strofs + length) > MAX_STRINGS)\n\t\tQCC_Error(ERR_INTERNAL, \"QCC_CopyString: -max_strings %i limit exceeded\\n\", MAX_STRINGS);\n\tmemcpy (strings+strofs, str, length);\n\tstrings[strofs+length] = 0;\n\tstrofs += length+1;\n\treturn old;\n}\n\n/*static int\tQCC_CopyDupBackString (const char *str)\n{\n\tsize_t length;\n\tint\t\told;\n\tchar *s;\n\n\tfor (s = strings+strofs-1; s>strings ; s--)\n\t\tif (!strcmp(s, str))\n\t\t\treturn s-strings;\n\n\told = strofs;\n\tlength = strlen(str)+1;\n\tif ( (strofs + length) > MAX_STRINGS)\n\t\tQCC_Error(ERR_INTERNAL, \"QCC_CopyString: stringtable size limit exceeded\\n\");\n\tstrcpy (strings+strofs, str);\n\tstrofs += length;\n\treturn old;\n}\n\nstatic void QCC_PrintStrings (void)\n{\n\tint\t\ti, l, j;\n\n\tfor (i=0 ; i<strofs ; i += l)\n\t{\n\t\tl = strlen(strings+i) + 1;\n\t\texterns->Printf (\"%5i : \",i);\n\t\tfor (j=0 ; j<l ; j++)\n\t\t{\n\t\t\tif (strings[i+j] == '\\n')\n\t\t\t{\n\t\t\t\tputchar ('\\\\');\n\t\t\t\tputchar ('n');\n\t\t\t}\n\t\t\telse\n\t\t\t\tputchar (strings[i+j]);\n\t\t}\n\t\texterns->Printf (\"\\n\");\n\t}\n}\n\n\nvoid QCC_PrintFunctions (void)\n{\n\tint\t\ti,j;\n\tQCC_dfunction_t\t*d;\n\n\tfor (i=0 ; i<numfunctions ; i++)\n\t{\n\t\td = &functions[i];\n\t\texterns->Printf (\"%s : %s : %i %i (\", strings + d->s_file, strings + d->s_name, d->first_statement, d->parm_start);\n\t\tfor (j=0 ; j<d->numparms ; j++)\n\t\t\texterns->Printf (\"%i \",d->parm_size[j]);\n\t\texterns->Printf (\")\\n\");\n\t}\n}*/\n\nstatic void QCC_SortFields (void)\n{\n\tint\t\t\t\ti, j;\n\tQCC_ddef32_t\tt;\n\n\t//insertion sort, of sorts.\n\t//(qsort doesn't guarentee ordering)\n\tfor (i = 1; i < numfielddefs; i++)\n\t{\n\t\tif (fields[i].ofs < fields[i-1].ofs)\n\t\t{\t//this entry is out of order\n\t\t\tfor (j = i-1; j > 0; j--)\n\t\t\t{\n\t\t\t\tif (fields[j].ofs <= fields[i].ofs)\n\t\t\t\t{\t//okay, so we're putting it after this element.\n\t\t\t\t\tj++;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tt = fields[i];\n\t\t\tmemmove(&fields[j+1], &fields[j], (i-j)*sizeof(t));\n\t\t\tfields[j] = t;\n\t\t}\n\t}\n}\n\nstatic void QCC_DumpFields (const char *outputname)\n{\n\tchar line[1024];\n\textern char *basictypenames[];\n\tint\t\ti;\n\tQCC_ddef_t\t*d;\n\tint h;\n\n\tsnprintf(line, sizeof(line), \"%s.fld\", outputname);\n\th = SafeOpenWrite (line, 2*1024*1024);\n\tif (h >= 0)\n\t{\n\t\tfor (i=0 ; i<numfielddefs ; i++)\n\t\t{\n\t\t\td = &fields[i];\n\t\t\tsnprintf(line, sizeof(line), \"%5i : (%s) %s\\n\", d->ofs, basictypenames[d->type], strings + d->s_name);\n\t\t\tSafeWrite(h, line, strlen(line));\n\t\t}\n\n\t\tSafeClose(h);\n\t}\n}\n\nstatic void QCC_DumpSymbolNames (const char *outputname)\n{\n\tchar line[1024];\n\tQCC_def_t *def;\n\tint h;\n\n\tsnprintf(line, sizeof(line), \"%s.sym\", outputname);\n\th = SafeOpenWrite (line, 2*1024*1024);\n\tif (h >= 0)\n\t{\n\t\tfor (def = pr.def_head.next ; def ; def = def->next)\n\t\t{\n\t\t\tif ((def->scope && !def->isstatic) || !strcmp(def->name, \"IMMEDIATE\"))\n\t\t\t\tcontinue;\n\t\t\tif (def->symbolheader != def /*&& def->symbolheader->type != def->type*/)\n\t\t\t\tcontinue;\t//try to exclude vector components.\n\n\t\t\tsnprintf(line, sizeof(line), \"%s  %10i %s\\n\", def->initialized?\"data\":\"bss \", def->symbolsize*(int)sizeof(float), def->name);\n\t\t\tSafeWrite(h, line, strlen(line));\n\t\t}\n\t\tSafeClose(h);\n\t}\n}\n/*static void QCC_DumpSymbolInfo (const char *outputname)\n{\n\tchar line[1024];\n\tchar tname[512];\n\tQCC_def_t *def;\n\tint h;\n\n\tsnprintf(line, sizeof(line), \"%s.sym\", outputname);\n\th = SafeOpenWrite (line, 2*1024*1024);\n\tif (h >= 0)\n\t{\n\t\tfor (def = pr.def_head.next ; def ; def = def->next)\n\t\t{\n//\t\t\tif ((def->scope && !def->isstatic) || !strcmp(def->name, \"IMMEDIATE\"))\n//\t\t\t\tcontinue;\n//\t\t\tif (def->symbolheader != def && def->symbolheader->type != def->type)\n//\t\t\t\tcontinue;\t//try to exclude vector components.\n\n\t\t\tif (def->arraysize)\n\t\t\t\tsnprintf(line, sizeof(line), \"%s%i: %s[%i] %s = \", def->used?\"\":\"(unused)\", def->ofs, TypeName(def->type, tname, sizeof(tname)), def->arraysize, def->name);\n\t\t\telse\n\t\t\t\tsnprintf(line, sizeof(line), \"%s%i: %s %s = \", def->used?\"\":\"(unused)\", def->ofs, TypeName(def->type, tname, sizeof(tname)), def->name);\n\t\t\tSafeWrite(h, line, strlen(line));\n\n\t\t\tswitch(def->type->type)\n\t\t\t{\n\t\t\tcase ev_vector:\n\t\t\t\tsnprintf(line, sizeof(line), \"%g %g %g\\n\", def->symboldata[0]._float, def->symboldata[1]._float, def->symboldata[2]._float);\n\t\t\t\tbreak;\n\t\t\tcase ev_float:\n\t\t\t\tsnprintf(line, sizeof(line), \"%g\\n\", def->symboldata[0]._float);\n\t\t\t\tbreak;\n\t\t\tcase ev_double:\n\t\t\t\tsnprintf(line, sizeof(line), \"%g\\n\", def->symboldata[0]._float);\n\t\t\t\tbreak;\n\t\t\tcase ev_string:\n\t\t\t\tsnprintf(line, sizeof(line), \"%+i, %s\\n\", def->symboldata[0].string, QCC_PR_String(strings + def->symboldata[0].string));\n\t\t\t\tbreak;\n\t\t\tcase ev_function:\n\t\t\t\tsnprintf(line, sizeof(line), \"%+i, %s\\n\", def->symboldata[0]._int, functions[def->symboldata[0]._int].name);\n\t\t\t\tbreak;\n\t\t\tcase ev_int64:\n\t\t\tcase ev_uint64:\n\t\t\t\tsnprintf(line, sizeof(line), \"%i\\n\", def->symboldata[0]._int);\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tsnprintf(line, sizeof(line), \"%i\\n\", def->symboldata[0]._int);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tSafeWrite(h, line, strlen(line));\n\t\t}\n\t\tSafeClose(h);\n\t}\n}*/\n\n/*\nstatic void QCC_PrintGlobals (void)\n{\n\tint\t\ti;\n\tQCC_ddef_t\t*d;\n\n\texterns->Printf(\"Globals Listing:\\n\");\n\n\tfor (i=0 ; i<numglobaldefs ; i++)\n\t{\n\t\td = &qcc_globals[i];\n\t\texterns->Printf (\"%5i : (%i) %s\\n\", d->ofs, d->type, strings + d->s_name);\n\t}\n}*/\n\nstatic void QCC_DumpAutoCvars (const char *outputname)\n{\n\tchar line[1024];\n\tint\t\ti, h;\n\tQCC_ddef_t\t*d;\n\tchar *n;\n\n\tsnprintf(line, sizeof(line), \"%s.cfg\", outputname);\n\th = SafeOpenWrite (line, 2*1024*1024);\n\tif (h >= 0)\n\t{\n\t\tfor (i=0 ; i<numglobaldefs ; i++)\n\t\t{\n\t\t\td = &qcc_globals[i];\n\t\t\tn = strings + d->s_name;\n\t\t\tif (!strncmp(n, \"autocvar_\", 9))\n\t\t\t{\n\t\t\t\tchar *desc;\n\t\t\t\tconst QCC_eval_t *val = (const QCC_eval_t*)&qcc_pr_globals[d->ofs];\n\t\t\t\tQCC_def_t *def = QCC_PR_GetDef(NULL, n, NULL, false, 0, 0);\n\t\t\t\tn += 9;\n\n\t\t\t\tif (!def)\n\t\t\t\t\tcontinue;\t//erk?\n\n\t\t\t\tif (def->comment)\n\t\t\t\t\tdesc = def->comment;\n\t\t\t\telse\n\t\t\t\t\tdesc = NULL;\n\n\t\t\t\tswitch(d->type & ~(DEF_SAVEGLOBAL|DEF_SHARED))\n\t\t\t\t{\n\t\t\t\tcase ev_float:\n\t\t\t\t\tsnprintf(line, sizeof(line), \"set %s\\t%g%s%s\\n\",\t\t\t\tn, val->_float,\t\t\t\t\t\t\t\t\t\tdesc?\"\\t//\":\"\", desc?desc:\"\");\n\t\t\t\t\tbreak;\n\t\t\t\tcase ev_double:\n\t\t\t\t\tsnprintf(line, sizeof(line), \"set %s\\t%g%s%s\\n\",\t\t\t\tn, val->_double,\t\t\t\t\t\t\t\t\tdesc?\"\\t//\":\"\", desc?desc:\"\");\n\t\t\t\t\tbreak;\n\t\t\t\tcase ev_vector:\n\t\t\t\t\tsnprintf(line, sizeof(line), \"set %s\\t\\\"%g %g %g\\\"%s%s\\n\",\t\tn, val->vector[0], val->vector[1], val->vector[2],\tdesc?\"\\t//\":\"\", desc?desc:\"\");\n\t\t\t\t\tbreak;\n\t\t\t\tcase ev_integer:\n\t\t\t\t\tsnprintf(line, sizeof(line), \"set %s\\t%\"pPRIi\"%s%s\\n\",\t\t\tn, val->_int,\t\t\t\t\t\t\t\t\t\tdesc?\"\\t//\":\"\", desc?desc:\"\");\n\t\t\t\t\tbreak;\n\t\t\t\tcase ev_uint:\n\t\t\t\t\tsnprintf(line, sizeof(line), \"set %s\\t%\"pPRIu\"%s%s\\n\",\t\t\tn, val->_uint,\t\t\t\t\t\t\t\t\t\tdesc?\"\\t//\":\"\", desc?desc:\"\");\n\t\t\t\t\tbreak;\n\t\t\t\tcase ev_int64:\n\t\t\t\t\tsnprintf(line, sizeof(line), \"set %s\\t%\"pPRIi64\"%s%s\\n\",\t\tn, val->i64,\t\t\t\t\t\t\t\t\t\tdesc?\"\\t//\":\"\", desc?desc:\"\");\n\t\t\t\t\tbreak;\n\t\t\t\tcase ev_uint64:\n\t\t\t\t\tsnprintf(line, sizeof(line), \"set %s\\t%\"pPRIu64\"%s%s\\n\",\t\tn, val->u64,\t\t\t\t\t\t\t\t\t\tdesc?\"\\t//\":\"\", desc?desc:\"\");\n\t\t\t\t\tbreak;\n\t\t\t\tcase ev_string:\n\t\t\t\t\tsnprintf(line, sizeof(line), \"set %s\\t\\\"%s\\\"%s%s\\n\",\t\t\tn, strings + val->_int,\t\t\t\t\t\t\t\tdesc?\"\\t//\":\"\", desc?desc:\"\");\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tsnprintf(line, sizeof(line), \"//set %s\\t ?%s%s\\n\",\t\t\t\tn,\t\t\t\t\t\t\t\t\t\t\t\t\tdesc?\"\\t//\":\"\", desc?desc:\"\");\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tSafeWrite(h, line, strlen(line));\n\t\t\t}\n\t\t}\n\t}\n}\n\nstatic void QCC_DumpLocalisation (const char *outputname)\n{\n\tchar line[65536];\n\tint\t\th, o;\n\tQCC_def_t *def;\n\tchar *n;\n\n\tsnprintf(line, sizeof(line), \"%s.pot\", outputname);\n\th = SafeOpenWrite (line, 2*1024*1024);\n\tif (h >= 0)\n\t{\n\t\tfor (def = pr.def_head.next ; def ; def = def->next)\n\t\t{\n\t\t\tif (!strncmp(def->name, \"dotranslate_\", 12))\n\t\t\t{\n\t\t\t\tconst QCC_eval_t *val = (const QCC_eval_t*)def->symboldata;\n\t\t\t\tif (def->type->type != ev_string)\n\t\t\t\t\tcontinue;\n\n\t\t\t\tif (def->comment)\n\t\t\t\t{\n\t\t\t\t\tn = def->comment;\n\t\t\t\t\tsnprintf(line, sizeof(line), \"#. \");\n\t\t\t\t\tfor (o = strlen(line); *n && o < countof(line)-10; n++)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (*n == '\\n')\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (n[1])\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tline[o++] = '\\n';\n\t\t\t\t\t\t\t\tline[o++] = '#';\n\t\t\t\t\t\t\t\tline[o++] = '.';\n\t\t\t\t\t\t\t\tline[o++] = ' ';\n\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse\t\t\t\tline[++o] = 'n';\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (*n == '\\\\')\tline[++o] = '\\\\';\n\t\t\t\t\t\telse if (*n == '\\\"')\tline[++o] = '\\\"';\n\t\t\t\t\t\telse if (*n == '\\n')\tline[++o] = 'n';\n\t\t\t\t\t\telse if (*n == '\\r')\tline[++o] = 'r';\n\t\t\t\t\t\telse if (*n == '\\t')\tline[++o] = 't';\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\t//hopefully the programmer used utf-8...\n\t\t\t\t\t\t\tline[o++] = *n;\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tline[o++-1] = '\\\\';\n\t\t\t\t\t}\n\t\t\t\t\tline[o++] = '\\n';\n\t\t\t\t\tline[o++] = 0;\n\t\t\t\t\tSafeWrite(h, line, strlen(line));\n\t\t\t\t}\n\n\t\t\t\tif (def->filen)\n\t\t\t\t{\t//strip any extra macro info there...\n\t\t\t\t\tchar *c = strchr(def->filen, ':');\n\t\t\t\t\tif (c && (c[1] < '0' || c[1] > '9'))\t//don't get fooled by windows paths...\n\t\t\t\t\t\tc = strchr(c+1, ':');\n\t\t\t\t\tif (c)\n\t\t\t\t\t{\n\t\t\t\t\t\t*c = 0;\n\t\t\t\t\t\tsnprintf(line, sizeof(line), \"#: %s:%i\\n\", def->filen, def->s_line);\n\t\t\t\t\t\t*c = ':';\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\tsnprintf(line, sizeof(line), \"#: %s:%i\\n\", def->filen, def->s_line);\n\t\t\t\t\tSafeWrite(h, line, strlen(line));\n\t\t\t\t}\n\n\t\t\t\tn = strings + val->_int;\n\t\t\t\tsnprintf(line, sizeof(line), \"msgid \\\"\");\n\t\t\t\tfor (o = strlen(line); *n && o < countof(line)-5; n++)\n\t\t\t\t{\n\t\t\t\t\tif (*n == '\\n' && n[1] && o < countof(line)-10)\n\t\t\t\t\t{\t//split multi-line stuff onto multiple lines, becase we can.\n\t\t\t\t\t\tline[o++] = '\\\\';\n\t\t\t\t\t\tline[o++] = 'n';\n\t\t\t\t\t\tline[o++] = '\\\"';\n\t\t\t\t\t\tline[o++] = '\\n';\n\t\t\t\t\t\tline[o++] = '\\\"';\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\telse if (*n == '\\\\')\tline[++o] = '\\\\';\n\t\t\t\t\telse if (*n == '\\\"')\tline[++o] = '\\\"';\n\t\t\t\t\telse if (*n == '\\n')\tline[++o] = 'n';\n\t\t\t\t\telse if (*n == '\\r')\tline[++o] = 'r';\n\t\t\t\t\telse if (*n == '\\t')\tline[++o] = 't';\n\t\t\t\t\telse\n\t\t\t\t\t{\t//hopefully the programmer used utf-8...\n\t\t\t\t\t\tline[o++] = *n;\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tline[o++-1] = '\\\\';\n\t\t\t\t}\n\t\t\t\tline[o++] = '\\\"';\n\t\t\t\tline[o++] = '\\n';\n\t\t\t\tline[o++] = 0;\n\t\t\t\tSafeWrite(h, line, strlen(line));\n\n\t\t\t\tsnprintf(line, sizeof(line), \"msgstr \\\"\\\"\\n\\n\");\n\t\t\t\tSafeWrite(h, line, strlen(line));\n\t\t\t}\n\t\t}\n\t}\n\tSafeClose(h);\n}\n\nstatic void QCC_DumpFiles (const char *outputname)\n{\n\tstruct\n\t{\n\t\tprecache_t *list;\n\t\tint count;\n\t} precaches[] =\n\t{\n\t\t{ precache_sound, numsounds},\n\t\t{ precache_texture, numtextures},\n\t\t{ precache_model, nummodels},\n\t\t{ precache_file, numfiles}\n\t};\n\n\tint\t\tg, i, b;\n\tpbool header;\n\n\texterns->Printf(\"\\nFile lists:\\n\");\n\tfor (b = 0; b < 64; b++)\n\t{\n\t\tfor (header = false, g = 0; g < sizeof(precaches)/sizeof(precaches[0]); g++)\n\t\t{\n\t\t\tfor (i = 0; i < precaches[g].count; i++)\n\t\t\t{\n\t\t\t\tif (precaches[g].list[i].block == b)\n\t\t\t\t{\n\t\t\t\t\tif (*precaches[g].list[i].name == '*')\n\t\t\t\t\t\tcontinue;\t//*-prefixed models are not real, and shouldn't be included in file lists.\n\t\t\t\t\tif (!header)\n\t\t\t\t\t{\n\t\t\t\t\t\texterns->Printf(\"pak%i:\\n\", b-1);\n\t\t\t\t\t\theader=true;\n\t\t\t\t\t}\n\t\t\t\t\texterns->Printf(\"%s\\n\", precaches[g].list[i].name);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (header)\n\t\t\texterns->Printf(\"\\n\");\n\t}\n}\n\nvoid QCC_DumpPreProcTags(void *ctx, void *data)\n{\n\tchar line[2048];\n\tint h = *(int*)ctx;\n\tCompilerConstant_t *def = data;\n\tif (def->fromfile)\n\t{\n\t\tsnprintf(line, sizeof(line), \"%s\\t%s\\t%i;\\\"\\td\\n\", def->name, def->fromfile, def->fromline);\n\t\tSafeWrite(h, line, strlen(line));\n\t}\n}\nstatic void QCC_DumpTags(const char *outputname)\n{\n\tchar line[65536];\n\tchar scope[2048];\n\tint\t\th;\n\tQCC_def_t *def;\n\tQCC_function_t\t*fnc;\n\tint i;\n\tQCC_type_t *t;\n\n\tsnprintf(line, sizeof(line), \"%s.tags\", outputname);\n\th = SafeOpenWrite (line, 2*1024*1024);\n\tif (h >= 0)\n\t{\n\t\tHash_Enumerate(&compconstantstable, QCC_DumpPreProcTags, &h);\n\n\t\tfor (i = 0; i < numtypeinfos; i++)\n\t\t{\n\t\t\tt = &qcc_typeinfo[i];\n\t\t\tif (!t->line || !t->filen)\n\t\t\t\tcontinue;\t//predefined type, not from any file.\n\t\t\tif (!*t->name || strchr(t->name, '<'))\n\t\t\t\tcontinue;\t//anonymous stuff happens.\n\t\t\tif (t->typedefed)\n\t\t\t\tsnprintf(line, sizeof(line), \"%s\\t%s\\t%i;\\\"\\tt\\n\", t->name, t->filen, t->line);\n\t\t\telse if (t->type == ev_struct)\n\t\t\t\tsnprintf(line, sizeof(line), \"%s\\t%s\\t%i;\\\"\\ts\\n\", t->name, t->filen, t->line);\n\t\t\telse if (t->type == ev_union)\n\t\t\t\tsnprintf(line, sizeof(line), \"%s\\t%s\\t%i;\\\"\\tu\\n\", t->name, t->filen, t->line);\n\t\t\telse if (t->type == ev_enum)\n\t\t\t\tsnprintf(line, sizeof(line), \"%s\\t%s\\t%i;\\\"\\tg\\n\", t->name, t->filen, t->line);\n\t\t\telse if ((t->type == ev_entity||t->type == ev_accessor) && t->parentclass)\n\t\t\t\tsnprintf(line, sizeof(line), \"%s\\t%s\\t%i;\\\"\\tc\\n\", t->name, t->filen, t->line);\n\t\t\telse\n\t\t\t\tcontinue;\n\t\t\tSafeWrite(h, line, strlen(line));\n\t\t}\n\n\t\tfor (def = pr.def_head.next; def; def = def->next)\n\t\t{\n\t\t\t//immediates are uninteresting...\n\t\t\tif (!strcmp(def->name, \"IMMEDIATE\"))\n\t\t\t\tcontinue;\n\t\t\tif (strchr(def->name, '.') || strchr(def->name, '[') || strchr(def->name, '*'))\n\t\t\t\tcontinue;\t//weird symbols, listed for savedgames only...\n\t\t\tif (def->scope && !strchr(def->scope->name, ':'))\n\t\t\t\tsnprintf(scope, sizeof(scope), \"\\tfunction:%s\\n\", def->scope->name);\n\t\t\telse if (def->isstatic)\n\t\t\t\tsnprintf(scope, sizeof(scope), \"\\file:\\n\");\t//local scope\n\t\t\telse\n\t\t\t\t*scope = 0;\n\n\t\t\tif (def->type->type == ev_function && def->constant && !def->arraysize)\n\t\t\t{\n\t\t\t\tint fnum = def->symboldata[0].function;\n\n\t\t\t\tif (fnum > 0 && fnum < numfunctions)\n\t\t\t\t{\n\t\t\t\t\tfnc = &functions[fnum];\n\t\t\t\t\tif (fnc->code>=0 && fnc->filen)\n\t\t\t\t\t{\n\t\t\t\t\t\tsnprintf(line, sizeof(line), \"%s\\t%s\\t%i;\\\"\\tf%s\\n\", def->name, fnc->filen, fnc->line, scope);\n\t\t\t\t\t\tSafeWrite(h, line, strlen(line));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (def->filen)\n\t\t\t\t{\n\t\t\t\t\tsnprintf(line, sizeof(line), \"%s\\t%s\\t%i;\\\"\\tp%s\\n\", def->name, def->filen, def->s_line, scope);\n\t\t\t\t\tSafeWrite(h, line, strlen(line));\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (def->filen)\n\t\t\t{\n\t\t\t\tsnprintf(line, sizeof(line), \"%s\\t%s\\t%i;\\\"\\tv%s\\n\", def->name, def->filen, def->s_line, scope);\n\t\t\t\tSafeWrite(h, line, strlen(line));\n\t\t\t}\n\t\t}\n\t}\n\tSafeClose(h);\n}\n\nstatic void QCC_DumpOpcodes (const char *outputname)\n{\n\tchar line[65536];\n\tint h;\n\n\tsnprintf(line, sizeof(line), \"%s.op.inc\", outputname);\n\th = SafeOpenWrite (line, 2*1024*1024);\n\tif (h >= 0)\n\t{\n\t\tsnprintf(line, sizeof(line), \"enum qcop_e {\\n\");\n\t\tSafeWrite(h, line, strlen(line));\n\t\tfor (enum qcop_e opcode = 0; opcode < OP_NUMREALOPS; ++opcode)\n\t\t{\n\t\t\tsnprintf(line, sizeof(line), \"\\t%sOP_%s = %d,\\n\",\n\t\t\t\t\tQCC_OPCodeValid(&pr_opcodes[opcode]) ? \"\" : \"// \",\n\t\t\t\t\tpr_opcodes[opcode].opname, (int) opcode);\n\t\t\tSafeWrite(h, line, strlen(line));\n\t\t}\n\t\tsnprintf(line, sizeof(line), \"\\tOP_NUMREALOPS = %d\\n\", (int) OP_NUMREALOPS);\n\t\tSafeWrite(h, line, strlen(line));\n\t\tsnprintf(line, sizeof(line), \"};\\n\");\n\t\tSafeWrite(h, line, strlen(line));\n\t}\n\tSafeClose(h);\n}\n\nint WriteSourceFiles(qcc_cachedsourcefile_t *filelist, int h, pbool sourceaswell, pbool legacyembed)\n{\n\t//helpers to deal with misaligned data. writes little-endian.\n#define misbyte(ptr,ofs,data) ((unsigned char*)(ptr))[ofs] = (data)&0xff;\n#define misshort(ptr,ofs,data) misbyte((ptr),(ofs),(data));misbyte((ptr),(ofs)+1,(data)>>8);\n#define misint(ptr,ofs,data) misshort((ptr),(ofs),(data));misshort((ptr),(ofs)+2,(data)>>16);\n\tincludeddatafile_t *idf;\n\tqcc_cachedsourcefile_t *f;\n\tint num=0;\n\tint ofs;\n\tpbool zipembed = true;\n\tint startofs;\n\tsourceaswell |= flag_embedsrc;\n\n\tfor (f = filelist,num=0; f ; f=f->next)\n\t{\n\t\tif (f->type == FT_CODE && !sourceaswell)\n\t\t\tcontinue;\n\t\tnum++;\n\t}\n\tif (!num)\n\t{\n\t\tif (zipembed)\n\t\t{\t//zips are found by scanning. so make sure something can be found so noone will erroneously find something\n\t\t\tchar centralheader[22];\n\t\t\tint centraldirofs = SafeSeek(h, 0, SEEK_CUR);\n\t\t\tmisint  (centralheader, 0, 0x06054b50);\n\t\t\tmisshort(centralheader, 4, 0);\t//this disk number\n\t\t\tmisshort(centralheader, 6, 0);\t//centraldir first disk\n\t\t\tmisshort(centralheader, 8, 0);\t//centraldir entries\n\t\t\tmisshort(centralheader, 10, 0);\t//total centraldir entries\n\t\t\tmisint  (centralheader, 12, 0);\t//centraldir size\n\t\t\tmisint  (centralheader, 16, centraldirofs);\t//centraldir offset\n\t\t\tmisshort(centralheader, 20, 0);\t//comment length\n\t\t\tSafeWrite(h, centralheader, 22);\n\t\t}\n\t\treturn 0;\n\t}\n\tstartofs = SafeSeek(h, 0, SEEK_CUR);\n\tidf = qccHunkAlloc(sizeof(includeddatafile_t)*num);\n\tfor (f = filelist,num=0; f ; f=f->next)\n\t{\n\t\tif (f->type == FT_CODE && !sourceaswell)\n\t\t\tcontinue;\n\n\t\tif (zipembed)\n\t\t{\n\t\t\tsize_t end;\n\t\t\tchar header[32];\n\t\t\tsize_t fnamelen = strlen(f->filename);\n\t\t\tf->zcrc = QC_encodecrc(f->size, f->file);\n\t\t\tmisint  (header, 0, 0x04034b50);\n\t\t\tmisshort(header, 4, 0);//minver\n\t\t\tmisshort(header, 6, 0);//general purpose flags\n\t\t\tmisshort(header, 8, 0);//compression method, 0=store, 8=deflate\n\t\t\tmisshort(header, 10, 0);//lastmodfiletime\n\t\t\tmisshort(header, 12, 0);//lastmodfiledate\n\t\t\tmisint  (header, 14, f->zcrc);//crc32\n\t\t\tmisint  (header, 18, f->size);//compressed size\n\t\t\tmisint  (header, 22, f->size);//uncompressed size\n\t\t\tmisshort(header, 26, fnamelen);//filename length\n\t\t\tmisshort(header, 28, 0);//extradata length\n\n\t\t\tf->zhdrofs = SafeSeek(h, 0, SEEK_CUR);\n\t\t\tSafeWrite(h, header, 30);\n\t\t\tSafeWrite(h, f->filename, fnamelen);\n\n\t\t\tstrcpy(idf[num].filename, f->filename);\n\t\t\tidf[num].size = f->size;\n\t\t\tidf[num].compmethod = 8;\t//must be 0(raw) or 8(raw deflate) for zips to work\n\t\t\tidf[num].ofs = SafeSeek(h, 0, SEEK_CUR);\n\t\t\tif (idf[num].compmethod==0)\n\t\t\t\tSafeWrite(h, f->file, f->size);\n\t\t\telse\n\t\t\t{\n\t\t\t\tidf[num].compsize = QC_encode(progfuncs, f->size, idf[num].compmethod, f->file, h);\n\n\t\t\t\tmisshort(header, 8, idf[num].compmethod);//compression method, 0=store, 8=deflate\n\t\t\t\tmisint  (header, 18, idf[num].compsize);\n\n\t\t\t\tend = SafeSeek(h, 0, SEEK_CUR);\n\t\t\t\tSafeSeek(h, f->zhdrofs, SEEK_SET);\n\t\t\t\tSafeWrite(h, header, 30);\n\t\t\t\tSafeSeek(h, end, SEEK_SET);\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (strlen(f->filename) >= sizeof(idf[num].filename))\n\t\t\t\tcontinue;\n\n\t\t\tstrcpy(idf[num].filename, f->filename);\n\t\t\tidf[num].size = f->size;\n\t#ifdef AVAIL_ZLIB\n\t\t\tidf[num].compmethod = 2;\n\t#else\n\t\t\tidf[num].compmethod = 0;\n\t#endif\n\t\t\tidf[num].ofs = SafeSeek(h, 0, SEEK_CUR);\n\t\t\tidf[num].compsize = QC_encode(progfuncs, f->size, idf[num].compmethod, f->file, h);\n\t\t}\n\t\tnum++;\n\t}\n\n\tif (zipembed)\n\t{\n\t\tchar centralheader[46];\n\t\tint centraldirsize;\n\t\tofs = SafeSeek(h, 0, SEEK_CUR);\n\t\tfor (f = filelist,num=0; f ; f=f->next)\n\t\t{\n\t\t\tsize_t fnamelen;\n\t\t\tif (f->type == FT_CODE && !sourceaswell)\n\t\t\t\tcontinue;\n\t\t\tfnamelen = strlen(f->filename);\n\t\t\tmisint  (centralheader, 0, 0x02014b50);\n\t\t\tmisshort(centralheader, 4, 0);//ourver\n\t\t\tmisshort(centralheader, 6, 0);//minver\n\t\t\tmisshort(centralheader, 8, 0);//general purpose flags\n\t\t\tmisshort(centralheader, 10, idf[num].compmethod);//compression method, 0=store, 8=deflate\n\t\t\tmisshort(centralheader, 12, 0);//lastmodfiletime\n\t\t\tmisshort(centralheader, 14, 0);//lastmodfiledate\n\t\t\tmisint  (centralheader, 16, f->zcrc);//crc32\n\t\t\tmisint  (centralheader, 20, idf[num].compsize);//compressed size\n\t\t\tmisint  (centralheader, 24, f->size);//uncompressed size\n\t\t\tmisshort(centralheader, 28, fnamelen);//filename length\n\t\t\tmisshort(centralheader, 30, 0);//extradata length\n\t\t\tmisshort(centralheader, 32, 0);//comment length\n\t\t\tmisshort(centralheader, 34, 0);//first disk number\n\t\t\tmisshort(centralheader, 36, 0);//internal file attribs\n\t\t\tmisint  (centralheader, 38, 0);//external file attribs\n\t\t\tmisint  (centralheader, 42, f->zhdrofs);//local header offset\n\t\t\tSafeWrite(h, centralheader, 46);\n\t\t\tSafeWrite(h, f->filename, fnamelen);\n\t\t\tnum++;\n\t\t}\n\n\t\tcentraldirsize = SafeSeek(h, 0, SEEK_CUR)-ofs;\n\t\tmisint  (centralheader, 0, 0x06054b50);\n\t\tmisshort(centralheader, 4, 0);\t//this disk number\n\t\tmisshort(centralheader, 6, 0);\t//centraldir first disk\n\t\tmisshort(centralheader, 8, num);\t//centraldir entries\n\t\tmisshort(centralheader, 10, num);\t//total centraldir entries\n\t\tmisint  (centralheader, 12, centraldirsize);\t//centraldir size\n\t\tmisint  (centralheader, 16, ofs);\t//centraldir offset\n\t\tmisshort(centralheader, 20, 0);\t//comment length\n\t\tSafeWrite(h, centralheader, 22);\n\n\t\tofs = 0;\n\t}\n\telse if (legacyembed)\n\t{\n\t\tofs = SafeSeek(h, 0, SEEK_CUR);\n\t\tSafeWrite(h, &num, sizeof(int));\n\t\tSafeWrite(h, idf, sizeof(includeddatafile_t)*num);\n\t}\n\telse\n\t\tofs = 0;\n\n\texterns->Printf(\"Embedded files take %u bytes\\n\",  SafeSeek(h, 0, SEEK_CUR) - startofs);\n\n\treturn ofs;\n}\n\nstatic void QCC_InitData (void)\n{\n\tstatic char parmname[12][MAX_PARMS];\n\tint\t\ti;\n\n\tqcc_sourcefile = NULL;\n\n\tmemset(stringtablist, 0, sizeof(stringtablist));\n\tnumstatements = 1;\t//first statement should be an OP_DONE, matching the null function(ish), in case it somehow doesn't get caught by the vm.\n\tstrofs = 2;\t\t\t//null, empty, *other stuff*\n\tnumfunctions = 1;\t//first function is a null function.\n\tnumglobaldefs = 1;\t//first globaldef is a null def. just because. doesn't include parms+ret. is there any point to this?\n\n\tnumfielddefs = 0;\n\tfields[numfielddefs].type = ev_void;\n\tfields[numfielddefs].s_name = 0;\t//should map to null\n\tfields[numfielddefs].ofs = 0;\n\tnumfielddefs++;\t//FIXME: do we actually need a null field? is there any point to this at all?\n\n\tmemset(&def_ret, 0, sizeof(def_ret));\n\tdef_ret.ofs = OFS_RETURN;\n\tdef_ret.name = \"return\";\n\tdef_ret.constant = false;\n\tdef_ret.type\t= NULL;\n\tdef_ret.symbolheader = &def_ret;\n\tdef_ret.symbolsize = type_size[ev_vector];\n\tfor (i=0 ; i<MAX_PARMS ; i++)\n\t{\n\t\tdef_parms[i].symbolheader = &def_parms[i];\n\t\tdef_parms[i].symbolsize = type_size[ev_vector];\n\t\tdef_parms[i].temp = NULL;\n\t\tdef_parms[i].type = NULL;\n\t\tdef_parms[i].ofs = OFS_PARM0 + 3*i;\n\t\tdef_parms[i].name = parmname[i];\n\t\tsprintf(parmname[i], \"parm%i\", i);\n\t}\n}\n\nstatic int WriteBodylessFuncs (int handle)\n{\n\tQCC_def_t\t\t*d;\n\tint ret=0;\n\tfor (d=pr.def_head.next ; d ; d=d->next)\n\t{\n\t\tif (!d->used || !d->constant || d->symbolheader != d)\n\t\t\tcontinue;\n\n\t\tif (d->type->type == ev_function && !d->scope)// function parms are ok\n\t\t{\n\t\t\tif (d->isextern)\n\t\t\t{\n\t\t\t\tSafeWrite(handle, d->name, strlen(d->name)+1);\n\t\t\t\tret++;\n\t\t\t}\n\t\t\tif (d->initialized == 0)\n\t\t\t{\n\t\t\t\tQCC_PR_Warning(ERR_NOFUNC, d->filen, d->s_line, \"function %s has no body\", d->name);\n\t\t\t\tQCC_PR_ParsePrintDef(ERR_NOFUNC, d);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn ret;\n}\n\nstatic void QCC_DetermineNeededSymbols(QCC_def_t *endsyssym)\n{\n\tQCC_def_t *sym = pr.def_head.next;\n\tsize_t i;\n\t//make sure system defs are not hurt by this.\n\tif (endsyssym)\n\t{\n\t\tfor (; sym; sym = sym->next)\n\t\t{\n\t\t\tif (sym->unused && !sym->initialized)\n\t\t\t\tsym->initialized = 1;\t//even if its not.\n\t\t\tsym->used = true;\n\t\t\tsym->referenced = true;\t//silence warnings about unreferenced things that can't be stripped\n\t\t\tif (sym == endsyssym)\n\t\t\t\tbreak;\n\t\t}\n\t}\n\t//non-system fields should maybe be present too.\n\tif (!opt_stripunusedfields)\n\t{\n\t\tfor (; sym; sym = sym->next)\n\t\t{\n\t\t\tif (sym->constant && sym->type->type == ev_field)\n\t\t\t{\n\t\t\t\tsym->used = true;\n\t\t\t\tsym->referenced = true;\n\t\t\t}\n\t\t}\n\t}\n\n\n\tfor (i=0 ; i<numstatements ; i++)\n\t{\n\t\tif ((sym = statements[i].a.sym))\n\t\t\tif (sym->symbolheader)\n\t\t\t\tsym->used = true;\n\t\tif ((sym = statements[i].b.sym))\n\t\t\tif (sym->symbolheader)\n\t\t\t\tsym->used = true;\n\t\tif ((sym = statements[i].c.sym))\n\t\t\tif (sym->symbolheader)\n\t\t\t\tsym->used = true;\n\t}\n}\n\n//allocates final space for the def, making it a true def\nstatic void QCC_FinaliseDef(QCC_def_t *def)\n{\n//#define DEBUG_DUMP_GLOBALMAP\n#if defined(DEBUG_DUMP) || defined(DEBUG_DUMP_GLOBALMAP)\n\tint ssize = def->symbolsize;\n\tconst QCC_eval_t *v;\n\tQCC_sref_t sr;\n#endif\n\n\tif (def->symboldata == qcc_pr_globals + def->ofs)\n\t{\n#ifdef DEBUG_DUMP_GLOBALMAP\n\t\texterns->Printf(\"Prefinalised %s @ %i+%i\\n\", def->name, def->ofs, ssize);\n#endif\n\t\treturn;\t//was already finalised.\n\t}\n\n\tif (def->symbolheader != def)\n\t{\t//finalise the parent/root symbol first.\n\t\tdef->symbolheader->used |= def->used;\n\t\tQCC_FinaliseDef(def->symbolheader);\n\t\tdef->referenced = true;\n\t}\n\tif (def->symbolheader == def && def->deftail)\n\t{\n\t\t//for head symbols, we go through and touch all of their children\n\t\tQCC_def_t *prev, *sub;\n\t\tif (def->used && !def->referenced)\n\t\t{\n\t\t\tpbool ignoreone = true;\n\t\t\t//touch all but one child\n\t\t\tfor (prev = def, sub = prev->next; prev != def->deftail; sub = (prev=sub)->next)\n\t\t\t{\n\t\t\t\tif (sub->referenced)\n\t\t\t\t\tdef->referenced=true;\t//if one child is referenced, the composite is referenced\n\t\t\t\telse if (!sub->referenced && ignoreone)\n\t\t\t\t\tignoreone = false;\t//this is the one we're going to warn about\n\t\t\t}\n//\t\t\tif (def->referenced)\t//no child defs were referenced at all. if we're going to be warning about this then at least mute warnings for any other fields\n//\t\t\t\tfor (prev = def, sub = prev->next; prev != def->deftail; sub = (prev=sub)->next)\n//\t\t\t\t\tsub->referenced = true;\n\t\t}\n\t\telse if (def->used)\n\t\t{\n\t\t\t//touch children to silence annoying warnings.\n\t\t\tfor (prev = def, sub = prev->next; prev != def->deftail; sub = (prev=sub)->next)\n\t\t\t\tsub->referenced |= true;\n\t\t}\n\n#ifdef TODO_READWRITETRACK\n\t\tif (!def->read)\n\t\t{\n\t\t\tpbool ignoreone = true;\n\t\t\t//touch all but one child\n\t\t\tfor (prev = def, sub = prev->next; prev != def->deftail; sub = (prev=sub)->next)\n\t\t\t{\n\t\t\t\tif (sub->read)\n\t\t\t\t\tdef->read=true;\t//if one child is referenced, the composite is referenced\n\t\t\t\telse if (!sub->read && ignoreone)\n\t\t\t\t\tignoreone = false;\t//this is the one we're going to warn about\n\t\t\t\telse\n\t\t\t\t\tsub->read |= def->read;\t\t\t\n\t\t\t}\n\t\t\tif (!def->read)\t//no child defs were referenced at all. if we're going to be warning about this then at least mute warnings for any other fields\n\t\t\t\tfor (prev = def, sub = prev->next; prev != def->deftail; sub = (prev=sub)->next)\n\t\t\t\t\tsub->read = true;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t//touch children\n\t\t\tfor (prev = def, sub = prev->next; prev != def->deftail; sub = (prev=sub)->next)\n\t\t\t\tsub->read |= def->read;\n\t\t}\n\n\t\tif (!def->written)\n\t\t{\n\t\t\tpbool ignoreone = true;\n\t\t\t//touch all but one child\n\t\t\tfor (prev = def, sub = prev->next; prev != def->deftail; sub = (prev=sub)->next)\n\t\t\t{\n\t\t\t\tif (sub->written)\n\t\t\t\t\tdef->written=true;\t//if one child is referenced, the composite is referenced\n\t\t\t\telse if (!sub->written && ignoreone)\n\t\t\t\t\tignoreone = false;\t//this is the one we're going to warn about\n\t\t\t\telse\n\t\t\t\t\tsub->written |= def->written;\t\t\t\n\t\t\t}\n\t\t\tif (!def->written)\t//no child defs were referenced at all. if we're going to be warning about this then at least mute warnings for any other fields\n\t\t\t\tfor (prev = def, sub = prev->next; prev != def->deftail; sub = (prev=sub)->next)\n\t\t\t\t\tsub->written = true;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t//touch children\n\t\t\tfor (prev = def, sub = prev->next; prev != def->deftail; sub = (prev=sub)->next)\n\t\t\t\tsub->written |= def->written;\n\t\t}\n#endif\n\t}\n//\telse if (def->symbolheader)\t//if a child symbol is referenced, mark the entire parent as referenced too. this avoids vec_x+vec_y with no vec or vec_z from generating warnings about vec being unreferenced\n//\t\tdef->symbolheader->referenced |= def->referenced;\n\n\tif (!def->symbolheader->used)\n\t{\n\t\tif (def->symboldata != qcc_pr_globals+def->ofs && def->symbolheader != def && def->symbolheader->symboldata == qcc_pr_globals + def->symbolheader->ofs)\n\t\t{\n\t\t\tdef->ofs += def->symbolheader->ofs;\n\t\t\tdef->symboldata = qcc_pr_globals + def->ofs;\n\t\t}\n\t\tif (verbose >= VERBOSE_DEBUG)\n\t\t\texterns->Printf(\"not needed: %s\\n\", def->name);\n\t\treturn;\n\t}\n\n\telse if (def->symbolheader != def && def->symbolheader->symboldata == qcc_pr_globals + def->symbolheader->ofs)\n\t{\n\t\tdef->ofs += def->symbolheader->ofs;\n\t}\n\telse\n\t{\n\t\tif (def->ofs)\n\t\t{\n\t\t\tif (def->symbolheader == def)\n\t\t\t\tQCC_Error(ERR_INTERNAL, \"root symbol %s has an offset\", def->name);\n\t\t\telse\n\t\t\t\tdef->ofs = 0;\n\t\t}\n\n\t\tif (def->arraylengthprefix)\n\t\t{\n\t\t\t//for hexen2 arrays, we need to emit a length first\n\t\t\tif (numpr_globals+1+def->symbolsize >= MAX_REGS)\n\t\t\t{\n\t\t\t\tif (!opt_overlaptemps || !opt_locals_overlapping)\n\t\t\t\t\tQCC_Error(ERR_TOOMANYGLOBALS, \"numpr_globals exceeded MAX_REGS - you'll need to use more optimisations\");\n\t\t\t\telse\n\t\t\t\t\tQCC_Error(ERR_TOOMANYGLOBALS, \"numpr_globals exceeded MAX_REGS of %u. Increase with eg: -max_regs %u\", MAX_REGS, MAX_REGS*2);\n\t\t\t}\n\t\t\tif (def->type->type == ev_vector)\n\t\t\t\t((int *)qcc_pr_globals)[numpr_globals] = def->arraysize-1;\n\t\t\telse\n\t\t\t\t((int *)qcc_pr_globals)[numpr_globals] = (def->arraysize*def->type->size)-1;\t//using float arrays for structs.\n\t\t\tnumpr_globals+=1;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (numpr_globals+def->symbolsize >= MAX_REGS)\n\t\t\t{\n\t\t\t\tif (!opt_overlaptemps || !opt_locals_overlapping)\n\t\t\t\t\tQCC_Error(ERR_TOOMANYGLOBALS, \"numpr_globals exceeded MAX_REGS - you'll need to use more optimisations\");\n\t\t\t\telse\n\t\t\t\t\tQCC_Error(ERR_TOOMANYGLOBALS, \"numpr_globals exceeded MAX_REGS of %u. Increase with eg: -max_regs %u\", MAX_REGS, MAX_REGS*2);\n\t\t\t}\n\t\t}\n\t\tdef->ofs += numpr_globals;\n\t\tnumpr_globals += def->symbolsize;\n\n\t\tif (def->symboldata)\n\t\t\tmemcpy(qcc_pr_globals+def->ofs, def->symboldata, def->symbolsize*sizeof(float));\n\t\telse\n\t\t\tmemset(qcc_pr_globals+def->ofs, 0, def->symbolsize*sizeof(float));\n\t}\n\tdef->symboldata = qcc_pr_globals + def->ofs;\n\tdef->symbolsize = numpr_globals - def->ofs;\n\n\tif (def->reloc)\n\t{\n\t\tdef->reloc->used = true;\n\t\tQCC_FinaliseDef(def->reloc);\n\t\tif (def->type->type == ev_function/*misordered inits/copies*/ || def->type->type == ev_integer/*dp-style global index*/ || def->type->type == ev_string/*immediates...*/)\n\t\t{\n//printf(\"func Reloc %s %s@%i==%x -> %s@%i==%x==%s\\n\", def->symbolheader->name, def->name,def->ofs, def->symboldata->_int, def->reloc->name,def->reloc->ofs, def->reloc->symboldata->_int, functions[def->reloc->symboldata->_int].name);\n\t\t\tdef->symboldata->_int += def->reloc->symboldata->_int;\n\t\t}\n\t\telse if (def->type->type == ev_pointer/*signal to the engine to fix up the offset*/)\n\t\t{\n//printf(\"Reloc %s %s@%i==%x -> %s@%i==%x\\n\", def->symbolheader->name, def->name,def->ofs, def->symboldata->_int, def->reloc->name,def->reloc->ofs, def->symboldata->_int+def->reloc->ofs*VMWORDSIZE);\n\t\t\tif (def->symboldata->_int & 0x80000000)\n\t\t\t\tQCC_PR_ParseWarning(0, \"dupe reloc, %s\", def->type->name);\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (flag_undefwordsize)\n\t\t\t\t\tQCC_PR_ParseWarning(ERR_BADEXTENSION, \"pointer relocs are disabled for this target.\");\n\t\t\t\tdef->symboldata->_int += def->reloc->ofs*VMWORDSIZE;\n\n\t\t\t\tdef->symboldata->_int |= 0x80000000;\t//we're using this as a hint to the engine to let it know that there's no dupes.\n\t\t\t}\n\t\t}\n\t\telse\n\t\t\tQCC_Error(ERR_INTERNAL, \"unknown reloc type... %s\", def->type->name);\n\t}\n\n\tif (def->deftail)\n\t{\n\t\tQCC_def_t *prev, *sub;\n\t\tfor (prev = def, sub = prev->next; prev != def->deftail; sub = (prev=sub)->next)\n\t\t{\n\t\t\tif (sub->reloc && !sub->used)\n\t\t\t{\t//make sure any children are finalised properly if they're relocs.\n\t\t\t\tsub->used = true;\n\t\t\t\tsub->referenced = true;\n\t\t\t\tQCC_FinaliseDef(sub);\n\t\t\t}\n\t\t}\n\t}\n\n#ifdef DEBUG_DUMP_GLOBALMAP\n\tif (!def->referenced)\n\t\texterns->Printf(\"Unreferenced \");\n\tsr.sym = def;\n\tsr.ofs = 0;\n\tsr.cast = def->type;\n\tv = (QCC_eval_t*)&sr.sym->symboldata[sr.ofs];\n\tif (v && def->type->type == ev_float)\n\t\texterns->Printf(\"Finalise %s(%f) @ %i+%i\\n\", def->name, v->_float, def->ofs, ssize);\n\telse if (v && def->type->type == ev_vector)\n\t\texterns->Printf(\"Finalise %s(%f %f %f) @ %i+%i\\n\", def->name, v->vector[0], v->vector[1], v->vector[2], def->ofs, ssize);\n\telse if (v && def->type->type == ev_integer)\n\t\texterns->Printf(\"Finalise %s(%i) @ %i+%i\\n\", def->name, v->_int, def->ofs, ssize);\n\telse if (v && def->type->type == ev_function)\n\t\texterns->Printf(\"Finalise %s(@%i) @ %i+%i\\n\", def->name, v->_int, def->ofs, ssize);\n\telse if (v && def->type->type == ev_field)\n\t\texterns->Printf(\"Finalise %s(.%i) @ %i+%i\\n\", def->name, v->_int, def->ofs, ssize);\n\telse if (v && def->type->type == ev_string)\n\t\texterns->Printf(\"Finalise %s(\\\"%s\\\") @ %i+%i\\n\", def->name, strings+v->_int, def->ofs, ssize);\n\telse\n\t\texterns->Printf(\"Finalise %s(?) @ %i+%i\\n\", def->name, def->ofs, ssize);\n#endif\n}\n\n//marshalled locals still point to the FIRST_LOCAL range.\n//this function remaps all the locals back into actual usable defs.\nstatic void QCC_UnmarshalLocals(void)\n{\n\tQCC_def_t *d;\n\tunsigned int onum, biggest, eog;\n\tsize_t i;\n\n\tfor (d = pr.def_head.next ; d ; d = d->next)\n\t\t;\n\n\t//finalise all the globals that we've seen so far\n\tfor (d = pr.def_head.next ; d ; d = d->next)\n\t{\n\t\tif (!d->localscope || d->isstatic)\n\t\t\tQCC_FinaliseDef(d);\n\t}\n\n\t//first, finalize all static locals that shouldn't form part of the local defs.\n\tfor (i=0 ; i<numfunctions ; i++)\n\t{\n//\t\tif (functions[i].privatelocals)\n\t\t{\n\t\t\tfor (d = functions[i].firstlocal; d; d = d->nextlocal)\n\t\t\t\tif (d->isstatic || (d->constant && d->initialized))\n\t\t\t\t\tQCC_FinaliseDef(d);\n\t\t}\n\t}\n\n\teog = numpr_globals;\n\t//next, finalize non-static non-shared locals.\n\tfor (i=0 ; i<numfunctions ; i++)\n\t{\n\t\tif (functions[i].privatelocals)\n\t\t{\n\t\t\tonum = numpr_globals;\n#ifdef DEBUG_DUMP\n\t\t\texterns->Printf(\"function %s locals:\\n\", functions[i].name);\n#endif\n\t\t\tfor (d = functions[i].firstlocal; d; d = d->nextlocal)\n\t\t\t\tif (!d->isstatic && !(d->constant && d->initialized))\n\t\t\t\t\tQCC_FinaliseDef(d);\n\t\t\tif (verbose >= VERBOSE_DEBUG)\n\t\t\t{\n\t\t\t\tif (onum == numpr_globals)\n\t\t\t\t\texterns->Printf(\"code: %s:%i: function %s no private locals\\n\", functions[i].filen, functions[i].line, functions[i].name);\n\t\t\t\telse \n\t\t\t\t\texterns->Printf(\"code: %s:%i: function %s private locals %i-%i\\n\", functions[i].filen, functions[i].line, functions[i].name, onum, numpr_globals);\n\t\t\t}\n\t\t}\n\t}\n\n\t//these functions don't have any initialisation issues, allowing us to merge them\n\tonum = biggest = numpr_globals;\n\tfor (i=0 ; i<numfunctions ; i++)\n\t{\n\t\tif (!functions[i].privatelocals)\n\t\t{\n\t\t\tnumpr_globals = onum;\n\t\t\tfor (d = functions[i].firstlocal; d; d = d->nextlocal)\n\t\t\t\tif (!d->isstatic && !(d->constant && d->initialized))\n\t\t\t\t\tQCC_FinaliseDef(d);\n\t\t\tif (biggest < numpr_globals)\n\t\t\t\tbiggest = numpr_globals;\n\n\t\t\tif (verbose >= VERBOSE_DEBUG)\n\t\t\t{\n\t\t\t\tif (onum == numpr_globals)\n\t\t\t\t\texterns->Printf(\"code: %s:%i: function %s no locals\\n\", functions[i].filen, functions[i].line, functions[i].name);\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\texterns->Printf(\"code: %s:%i: function %s overlapped locals %i-%i\\n\", functions[i].filen, functions[i].line, functions[i].name, onum, numpr_globals);\n\n\t\t\t\t\tfor (d = functions[i].firstlocal; d; d = d->nextlocal)\n\t\t\t\t\t{\n\t\t\t\t\t\texterns->Printf(\"code: %s:%i: %s @%i\\n\", functions[i].filen, functions[i].line, d->name, d->ofs);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tnumpr_globals = biggest;\n\tif (verbose >= VERBOSE_STANDARD)\n\t\texterns->Printf(\"%i shared locals, %i private, %i total\\n\", biggest - onum, onum - eog, numpr_globals-eog);\n}\nstatic void QCC_GenerateFieldDefs(QCC_def_t *def, char *fieldname, int ofs, QCC_type_t *type)\n{\n\tstring_t sname;\n\tQCC_ddef32_t *dd;\n\tif (type->type == ev_struct || type->type == ev_union)\n\t{\t//the qcvm cannot cope with struct fields. so we need to generate lots of fake ones.\n\t\tchar sub[256];\n\t\tunsigned int p, a;\n\t\tunsigned int parms = type->num_parms;\n\t\tif (type->type == ev_union)\n\t\t\tparms = 1;\t//unions only generate the first element. it simplifies things (should really just be the biggest).\n\t\tfor (p = 0; p < parms; p++)\n\t\t{\n\t\t\tif (type->params[p].arraysize)\n\t\t\t{\n\t\t\t\tfor (a = 0; a < type->params[p].arraysize; a++)\n\t\t\t\t{\n\t\t\t\t\tQC_snprintfz(sub, sizeof(sub), \"%s.%s[%u]\", fieldname, type->params[p].paramname, a);\n\t\t\t\t\tQCC_GenerateFieldDefs(def, sub, ofs + type->params[p].ofs + a * type->params[p].type->size, type->params[p].type);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tQC_snprintfz(sub, sizeof(sub), \"%s.%s\", fieldname, type->params[p].paramname);\n\t\t\t\tQCC_GenerateFieldDefs(def, sub, ofs + type->params[p].ofs, type->params[p].type);\n\t\t\t}\n\t\t}\n\t\treturn;\n\t}\n\tif (numfielddefs >= MAX_FIELDS)\n\t\tQCC_PR_ParseError(0, \"Too many fields. Limit is %u\\n\", MAX_FIELDS);\n\tdd = &fields[numfielddefs];\n\tnumfielddefs++;\n\tdd->type = type->type;\n\tdd->s_name = sname = QCC_CopyString (fieldname);\n\tdd->ofs = def->symboldata[ofs]._int;\n\n\tif (numglobaldefs >= MAX_GLOBALS)\n\t\tQCC_PR_ParseError(0, \"Too many globals. Limit is %u\\n\", MAX_GLOBALS);\n\t//and make sure that there's a global defined too, so field remapping isn't screwed.\n\tdd = &qcc_globals[numglobaldefs];\n\tnumglobaldefs++;\n\tdd->type = ev_field;\n\tdd->ofs = def->ofs+ofs;\n\tdd->s_name = sname;\n}\n\nstatic const char *QCC_FileForStatement(int st)\n{\n\tconst char *ret = \"???\";\n\tint i;\n\tfor (i = 0; i < numfunctions; i++) \n\t{\n\t\tif (functions[i].code > 0)\n\t\t{\n\t\t\tif (st < functions[i].code)\n\t\t\t\tbreak;\n\t\t\tret = functions[i].filen;\n\t\t}\n\t}\n\treturn ret;\n}\nstatic const char *QCC_FunctionForStatement(int st)\n{\n\tconst char *ret = \"???\";\n\tint i;\n\tfor (i = 0; i < numfunctions; i++) \n\t{\n\t\tif (functions[i].code > 0)\n\t\t{\n\t\t\tif (st < functions[i].code)\n\t\t\t\tbreak;\n\t\t\tret = functions[i].name;\n\t\t}\n\t}\n\treturn ret;\n}\n\n\nstatic void QCC_PR_CRCMessages(unsigned short crc);\nCompilerConstant_t *QCC_PR_CheckCompConstDefined(char *def);\nstatic pbool QCC_WriteData (int crc)\n{\n\tQCC_def_t\t\t*def;\n\tQCC_ddef_t\t\t*dd;\n\tdprograms_t\tprogs;\n\tint\t\t\th;\n\tint\t\t\ti, len;\n\tpbool debugtarget = false;\n\tpbool types = false;\n\tint outputsttype = PST_DEFAULT;\n\tint dupewarncount = 0, extwarncount = 0;\n\tint\t\t\t*statement_linenums;\n\tvoid\t\t*funcdata;\n\tsize_t\t\tfuncdatasize;\n\tconst char *bigjumps = NULL;\n\n\n\textern char *basictypenames[];\n\n\tmemset(&progs, 0, sizeof(progs));\n\n\tif (numstatements==1 && numfunctions==1 && numglobaldefs==1 && numfielddefs==1)\n\t{\n\t\texterns->Printf(\"nothing to write\\n\");\n\t\treturn false;\n\t}\n\n\tprogs.blockscompressed=0;\n\n\tif (numstatements > MAX_STATEMENTS)\n\t\tQCC_Error(ERR_TOOMANYSTATEMENTS, \"Too many statements - %i\\nAdd '-max_statements %i' to the commandline\", numstatements, (numstatements+32768)&~32767);\n\n\tif (strofs > MAX_STRINGS)\n\t\tQCC_Error(ERR_TOOMANYSTRINGS, \"Too many strings - %i\\nAdd '-max_strings %i' to the commandline\", strofs, (strofs+32768)&~32767);\n\n\t//part of how compilation works. This def is always present, and never used.\n\tdef = QCC_PR_GetDef(NULL, \"end_sys_globals\", NULL, false, 0, false);\n\tif (def)\n\t\tdef->referenced = true;\n\n\tdef = QCC_PR_GetDef(NULL, \"end_sys_fields\", NULL, false, 0, false);\n\tif (def)\n\t\tdef->referenced = true;\n\n\tQCC_PR_FinaliseFunctions();\n\tQCC_DetermineNeededSymbols(def);\n\n\tQCC_UnmarshalLocals();\n\tQCC_FinaliseTemps();\n\n\tfor (i=0 ; i<numstatements ; i++)\n\t{\n\t\tif (!statements[i].a.sym && ((int)statements[i].a.ofs > 0x7fff || (int)statements[i].a.ofs < -0x7fff))\n\t\t\tbreak;\n\t\tif (!statements[i].a.sym && ((int)statements[i].a.ofs > 0x7fff || (int)statements[i].a.ofs < -0x7fff))\n\t\t\tbreak;\n\t\tif (!statements[i].a.sym && ((int)statements[i].a.ofs > 0x7fff || (int)statements[i].a.ofs < -0x7fff))\n\t\t\tbreak;\n\t}\n\tif (i < numstatements)\n\t\tbigjumps = QCC_FunctionForStatement(i);\n\n\tif (!def)\n\t{\n\t\tQCC_PR_Warning(WARN_SYSTEMCRC, NULL, 0, \"no end_sys_fields defined. system headers missing.\");\n\t}\n\telse\n\t\tQCC_PR_CRCMessages(crc);\n\tswitch (qcc_targetformat)\n\t{\n\tcase QCF_HEXEN2:\n\tcase QCF_STANDARD:\n\tcase QCF_DARKPLACES:\t//grr.\n\t\tif (bodylessfuncs)\n\t\t\texterns->Printf(\"Warning: There are some functions without bodies.\\n\");\n\n\t\tif (bigjumps)\n\t\t{\n\t\t\texterns->Printf(\"Forcing target to FTE32 due to large function %s\\n\", bigjumps);\n\t\t\toutputsttype = PST_FTE32;\n\t\t}\n\t\telse if (numpr_globals > 65530)\n\t\t{\n\t\t\tif (qcc_targetformat == QCF_HEXEN2)\n\t\t\t{\n\t\t\t\texterns->Printf(\"Forcing target to uHexen2 due to numpr_globals\\n\");\n\t\t\t\toutputsttype = PST_UHEXEN2;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\texterns->Printf(\"Forcing target to FTE32 due to numpr_globals\\n\");\n\t\t\t\toutputsttype = PST_FTE32;\n\t\t\t}\n\t\t}\n\t\telse if (qcc_targetformat == QCF_FTEH2)\n\t\t{\n\t\t\texterns->Printf(\"Progs execution will require FTE\\n\");\n\t\t\tbreak;\n\t\t}\n\t\telse if (qcc_targetformat == QCF_HEXEN2)\n\t\t{\n\t\t\texterns->Printf(\"Progs execution requires a Hexen2 compatible HCVM\\n\");\n\t\t\tbreak;\n\t\t}\n\t\telse if (qcc_targetformat == QCF_DARKPLACES)\n\t\t{\n\t\t\texterns->Printf(\"Progs execution uses extended opcodes.\\n\");\n\t\t\tbreak;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (numpr_globals >= 32768)\t//not much of a different format. Rewrite output to get it working on original executors?\n\t\t\t\texterns->Printf(\"Globals exceeds 32k - an enhanced QCVM will be required\\n\");\n\t\t\telse if (verbose >= VERBOSE_STANDARD)\n\t\t\t\texterns->Printf(\"Progs should run on any QuakeC VM\\n\");\n\t\t\tbreak;\n\t\t}\n\t\tQCC_OPCodeSetTarget((qcc_targetformat==QCF_HEXEN2)?QCF_FTEH2:QCF_FTE, 0);\n\t\t//intentional fallthrough\n\tcase QCF_FTEDEBUG:\n\tcase QCF_FTE:\n\tcase QCF_FTEH2:\n\tcase QCF_QSS:\n\t\tif (qcc_targetformat == QCF_FTEDEBUG)\n\t\t\tdebugtarget = true;\n\n\t\tif (outputsttype != PST_FTE32 && outputsttype != PST_UHEXEN2)\n\t\t{\n\t\t\tif (bigjumps)\n\t\t\t{\n\t\t\t\texterns->Printf(\"Using 32 bit target due to large function %s\\n\", bigjumps);\n\t\t\t\toutputsttype = PST_FTE32;\n\t\t\t}\n\t\t\telse if (numpr_globals > 65530)\n\t\t\t{\n\t\t\t\texterns->Printf(\"Using 32 bit target due to numpr_globals\\n\");\n\t\t\t\toutputsttype = PST_FTE32;\n\t\t\t}\n\t\t}\n\n\t\tif (qcc_targetformat == QCF_QSS || qcc_targetformat == QCF_DARKPLACES)\n\t\t\tcompressoutput = 0;\n\n\n\t\t//compression of blocks?\n\t\tif (compressoutput)\t\tprogs.blockscompressed |=1;\t\t//statements\n\t\tif (compressoutput)\t\tprogs.blockscompressed |=2;\t\t//defs\n\t\tif (compressoutput)\t\tprogs.blockscompressed |=4;\t\t//fields\n\t\tif (compressoutput)\t\tprogs.blockscompressed |=8;\t\t//functions\n\t\tif (compressoutput)\t\tprogs.blockscompressed |=16;\t//strings\n\t\tif (compressoutput)\t\tprogs.blockscompressed |=32;\t//globals\n\t\tif (compressoutput)\t\tprogs.blockscompressed |=64;\t//line numbers\n\t\tif (compressoutput)\t\tprogs.blockscompressed |=128;\t//types\n\t\t//include a type block?\n\t\t//types = debugtarget;\n//\t\tif (types && sizeof(char *) != sizeof(string_t))\n\t\t{\n\t\t\t//qcc_typeinfo_t has a char* inside it, which changes size\n//\t\t\texterns->Printf(\"64bit builds cannot write typeinfo structures\\n\");\n\t\t\ttypes = false;\n\t\t}\n\n\t\tif (verbose >= VERBOSE_STANDARD)\n\t\t{\n\t\t\tif (qcc_targetformat == QCF_QSS)\n\t\t\t\texterns->Printf(\"QSS or FTE will be required\\n\");\n\t\t\telse if (qcc_targetformat == QCF_DARKPLACES)\n\t\t\t\texterns->Printf(\"DarkPlaces or FTE will be required\\n\");\n\t\t\telse if (outputsttype == PST_UHEXEN2)\n\t\t\t\texterns->Printf(\"FTE or uHexen2 will be required\\n\");\n\t\t\telse\n\t\t\t\texterns->Printf(\"FTE's QCLib will be required\\n\");\n\t\t}\n\t\tbreak;\n\tcase QCF_UHEXEN2:\n\t\tdebugtarget = false;\n\t\toutputsttype = PST_UHEXEN2;\n\t\tif (verbose >= VERBOSE_STANDARD)\n\t\t\texterns->Printf(\"uHexen2 will be required\\n\");\n\t\tif (numpr_globals < 65535)\n\t\t\texterns->Printf(\"Warning: outputting 32 uHexen2 format when 16bit would suffice\\n\");\n\t\tbreak;\n\tcase QCF_KK7:\n\t\tif (bodylessfuncs)\n\t\t\texterns->Printf(\"Warning: There are some functions without bodies.\\n\");\n\t\tif (numpr_globals > 65530)\n\t\t\texterns->Printf(\"Warning: Saving is not fully supported. Ensure all engine read fields and globals are defined early on.\\n\");\n\n\t\texterns->Printf(\"A KK compatible executor will be required (FTE/KK)\\n\");\n\t\toutputsttype = PST_KKQWSV;\n\t\tbreak;\n\tcase QCF_QTEST:\n\t\texterns->Printf(\"Compiled QTest progs will most likely not work at all. YOU'VE BEEN WARNED!\\n\");\n\t\toutputsttype = PST_QTEST;\n\t\tbreak;\n\tdefault:\n\t\texterns->Sys_Error(\"invalid progs type chosen!\");\n\t}\n\n\n\tswitch (outputsttype)\n\t{\n\tcase PST_QTEST:\n\t\t{\n\t\t\t// this sucks but the structures are just too different\n\t\t\tqtest_function_t *funcs = (qtest_function_t *)qccHunkAlloc(sizeof(qtest_function_t)*numfunctions);\n\n\t\t\tfor (i=0 ; i<numfunctions ; i++)\n\t\t\t{\n\t\t\t\tint j;\n\n\t\t\t\tfuncs[i].unused1 = 0;\n\t\t\t\tfuncs[i].profile = 0;\n\t\t\t\tif (functions[i].code == -1)\n\t\t\t\t\tfuncs[i].first_statement = PRLittleLong (-functions[i].builtin);\n\t\t\t\telse\n\t\t\t\t\tfuncs[i].first_statement = PRLittleLong (functions[i].code);\n\t\t\t\tfuncs[i].first_statement = PRLittleLong (functions[i].code);\n\t\t\t\tfuncs[i].parm_start = 0;//PRLittleLong (functions[i].parm_start);\n\t\t\t\tfuncs[i].s_name = PRLittleLong (QCC_CopyString(functions[i].name));\n\t\t\t\tfuncs[i].s_file = PRLittleLong (functions[i].s_filed);\n\t\t\t\tfuncs[i].numparms = 0;//PRLittleLong ((functions[i].numparms>MAX_PARMS)?MAX_PARMS:functions[i].numparms);\n\t\t\t\tfuncs[i].locals = 0;//PRLittleLong (functions[i].locals);\n\t\t\t\tfor (j = 0; j < MAX_PARMS; j++)\n\t\t\t\t\tfuncs[i].parm_size[j] = 0;//PRLittleLong((int)functions[i].parm_size[j]);\n\t\t\t}\n\t\t\tfuncdata = funcs;\n\t\t\tfuncdatasize = numfunctions*sizeof(*funcs);\n\t\t}\n\t\tbreak;\n\tcase PST_UHEXEN2:\n\tcase PST_DEFAULT:\n\tcase PST_KKQWSV:\n\tcase PST_FTE32:\n\t\t{\n\t\t\tdfunction_t *funcs = (dfunction_t *)qccHunkAlloc(sizeof(dfunction_t)*numfunctions);\n\t\t\tfor (i=0 ; i<numfunctions ; i++)\n\t\t\t{\n\t\t\t\tQCC_def_t *local;\n\t\t\t\tunsigned int p, a;\n\t\t\t\tif (functions[i].code == -1)\n\t\t\t\t\tfuncs[i].first_statement = PRLittleLong (-functions[i].builtin);\n\t\t\t\telse\n\t\t\t\t\tfuncs[i].first_statement = PRLittleLong (functions[i].code);\n\t\t\t\tif (opt_function_names && functions[i].builtin)\n\t\t\t\t{\n\t\t\t\t\t//builtins don't need a name entry. the def is constant, so no string need be emitted at all.\n\t\t\t\t\toptres_function_names += strlen(functions[i].name)+1;\n\t\t\t\t\tfuncs[i].s_name = 0;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tfuncs[i].s_name = PRLittleLong (QCC_CopyString(functions[i].name));\n\t\t\t\tfuncs[i].s_file = PRLittleLong (functions[i].s_filed);\n\n\t\t\t\tif (functions[i].merged)\n\t\t\t\t{\n\t\t\t\t\tfuncs[i].parm_start = functions[i].merged->parm_start;\n\t\t\t\t\tfuncs[i].locals = functions[i].merged->locals;\n\t\t\t\t\tfuncs[i].numparms = functions[i].merged->numparms;\n\t\t\t\t\tfor(p = 0; p < (unsigned)funcs[i].numparms; p++)\n\t\t\t\t\t\tfuncs[i].parm_size[p] = functions[i].merged->parm_size[p];\n\t\t\t\t}\n\t\t\t\telse if (functions[i].code == -1)\n\t\t\t\t{\n\t\t\t\t\tfuncs[i].parm_start = 0;\n\t\t\t\t\tfuncs[i].locals = 0;\n\t\t\t\t\tfuncs[i].numparms = functions[i].type->num_parms;\n\t\t\t\t\tif (funcs[i].numparms > MAX_PARMS)\n\t\t\t\t\t\tfuncs[i].numparms = MAX_PARMS;\t//shouldn't happen for builtins.\n\t\t\t\t\tp = 0;\n\t\t\t\t}\n\t\t\t\telse if (functions[i].firstlocal)\n\t\t\t\t{\n\t\t\t\t\tunsigned int size;\n\t\t\t\t\tfuncs[i].parm_start = 0;\n\t\t\t\t\tfor (local = functions[i].firstlocal, a = 0, p = 0; local && a < MAX_PARMS && a < functions[i].type->num_parms; local = local->deftail->nextlocal)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (!local->used)\n\t\t\t\t\t\t{\t//all params should have been assigned space. logically we could have safely omitted the last ones, but blurgh.\n\t\t\t\t\t\t\tQCC_PR_Warning(ERR_INTERNAL, local->filen, local->s_line, \"Argument %s was not marked used.\", local->name);\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (!p)\n\t\t\t\t\t\t\tfuncs[i].parm_start = local->ofs;\n\n\t\t\t\t\t\tsize = local->type->size;\n\t\t\t\t\t\tif (local->arraysize)\t//arrays are annoying\n\t\t\t\t\t\t\tsize = local->arraylengthprefix+size*local->arraysize;\n\n\t\t\t\t\t\tfuncs[i].locals += size;\n\t\t\t\t\t\tfor (; size > 3 && p < MAX_PARMS; size -= 3)\n\t\t\t\t\t\t\tfuncs[i].parm_size[p++] = 3;\t//the engine will copy PARMi over, it can't cope with larger types, the following args would be wrong.\n\t\t\t\t\t\tif (p < MAX_PARMS)\n\t\t\t\t\t\t\tfuncs[i].parm_size[p++] = size;\n\t\t\t\t\t\ta++;\n\t\t\t\t\t}\n\t\t\t\t\tfor (; local && (!local->used || local->isstatic || (local->constant && local->initialized)); local = local->nextlocal)\n\t\t\t\t\t\t;\n\t\t\t\t\tif (!p && local)\n\t\t\t\t\t\tfuncs[i].parm_start = local->ofs;\n\t\t\t\t\tfor (; local; local = local->nextlocal)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (!local->used || local->isstatic || (local->constant && local->initialized))\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\tsize = local->type->size;\n\t\t\t\t\t\tif (local->arraysize)\t//arrays are annoying\n\t\t\t\t\t\t\tsize = local->arraylengthprefix+size*local->arraysize;\n\t\t\t\t\t\tfuncs[i].locals += size;\n\t\t\t\t\t}\n\t\t\t\t\tfuncs[i].numparms = p;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t//fucked up functions with no params.\n\t\t\t\t\tfuncs[i].parm_start = 0;\n\t\t\t\t\tfuncs[i].locals = 0;\n\t\t\t\t\tfuncs[i].numparms = 0;\n\t\t\t\t\tp = 0;\n\t\t\t\t}\n\t\t\t\twhile(p < MAX_PARMS)\n\t\t\t\t\tfuncs[i].parm_size[p++] = 0;\n\t\t\t\tfuncs[i].parm_start = PRLittleLong(funcs[i].parm_start);\n\t\t\t\tfuncs[i].locals = PRLittleLong(funcs[i].locals);\n\t\t\t\tfuncs[i].numparms = PRLittleLong(funcs[i].numparms);\n\n\t\t\t\tif (funcs[i].locals && !funcs[i].parm_start)\n\t\t\t\t\tQCC_PR_Warning(0, strings + funcs[i].s_file, functions[i].line, \"%s:%i: func %s @%i locals@%i+%i, %i parms\\n\", functions[i].filen, functions[i].line, strings+funcs[i].s_name, funcs[i].first_statement, funcs[i].parm_start, funcs[i].locals, funcs[i].numparms);\n\n\t\t\t\tif (verbose >= VERBOSE_DEBUG)\n\t\t\t\t{\n\t\t\t\t\texterns->Printf(\"code: %s:%i: func %s @%i locals@%i+%i, %i parms\\n\", functions[i].filen, functions[i].line, strings+funcs[i].s_name, funcs[i].first_statement, funcs[i].parm_start, funcs[i].locals, funcs[i].numparms);\n\t\t\t\t\texterns->Printf(\"code: %s:%i: (%i,%i,%i,%i,%i,%i,%i,%i)\\n\", functions[i].filen, functions[i].line, funcs[i].parm_size[0], funcs[i].parm_size[1], funcs[i].parm_size[2], funcs[i].parm_size[3], funcs[i].parm_size[4], funcs[i].parm_size[5], funcs[i].parm_size[6], funcs[i].parm_size[7]);\n\t\t\t\t}\n\t\t\t}\n\t\t\tfuncdata = funcs;\n\t\t\tfuncdatasize = numfunctions*sizeof(*funcs);\n\t\t}\n\t\tbreak;\n\tdefault:\n\t\texterns->Sys_Error(\"structtype error\");\n\t\tfuncdata = NULL;\n\t\tfuncdatasize = 0;\n\t}\n\n\tfor (dupewarncount = 0, def = pr.def_head.next ; def ; def = def->next)\n\t{\n\t\tif (def->scope && !def->isstatic && !def->scope->privatelocals)\n\t\t{\t//if we're merging locals, then we shouldn't ever bother writing those globals. it just confuses debuggers etc. they're utterly pointless.\n\t\t\t//which may be a problem if they're things that the engine is going to be swapping around... which they shouldn't be.\n\t\t\tcontinue;\n\t\t}\n\n/*\t\tif (def->type->type == ev_vector || (def->type->type == ev_field && def->type->aux_type->type == ev_vector))\n\t\t{\t//do the references, so we don't get loadsa not referenced VEC_HULL_MINS_x\n\t\t\ts_file = def->s_file;\n\t\t\tQC_snprintfz (element, sizeof(element), \"%s_x\", def->name);\n\t\t\tcomp_x = QCC_PR_GetDef(NULL, element, def->scope, false, 0, false);\n\t\t\tQC_snprintfz (element, sizeof(element), \"%s_y\", def->name);\n\t\t\tcomp_y = QCC_PR_GetDef(NULL, element, def->scope, false, 0, false);\n\t\t\tQC_snprintfz (element, sizeof(element), \"%s_z\", def->name);\n\t\t\tcomp_z = QCC_PR_GetDef(NULL, element, def->scope, false, 0, false);\n\n\t\t\th = def->references;\n\t\t\tif (comp_x && comp_y && comp_z)\n\t\t\t{\n\t\t\t\th += comp_x->references;\n\t\t\t\th += comp_y->references;\n\t\t\t\th += comp_z->references;\n\n\t\t\t\tif (!def->references)\n\t\t\t\t\tif (!comp_x->references || !comp_y->references || !comp_z->references)\t//one of these vars is useless...\n\t\t\t\t\t\th=0;\n\n\t\t\t\tdef->references = h;\n\n\n\t\t\t\tif (!h)\n\t\t\t\t\th = 1;\n\t\t\t\tif (comp_x)\n\t\t\t\t\tcomp_x->references = h;\n\t\t\t\tif (comp_y)\n\t\t\t\t\tcomp_y->references = h;\n\t\t\t\tif (comp_z)\n\t\t\t\t\tcomp_z->references = h;\n\t\t\t}\n\t\t}\n*/\n#ifdef TODO_READWRITETRACK\n\t\tif (def->symbolheader->read && !def->symbolheader->written && !def->symbolheader->referenced)\n\t\t{\n\t\t\tchar typestr[256];\n\t\t\tQCC_sref_t sr = {def, 0, def->type};\n\t\t\tQCC_PR_Warning(WARN_READNOTWRITTEN, def->filen, def->s_line, \"%s %s = %s read, but not writte.\", TypeName(def->type, typestr, sizeof(typestr)), def->name, QCC_VarAtOffset(sr));\n\t\t}\n\t\tif (def->symbolheader->written && !def->symbolheader->read && !def->symbolheader->referenced)\n\t\t{\n\t\t\tchar typestr[256];\n\t\t\tQCC_sref_t sr = {def, 0, def->type};\n\t\t\tQCC_PR_Warning(WARN_WRITTENNOTREAD, def->filen, def->s_line, \"%s %s = %s written, but not read.\", TypeName(def->type, typestr, sizeof(typestr)), def->name, QCC_VarAtOffset(sr));\n\t\t}\n#endif\n\t\tif (!def->symbolheader->read && !def->symbolheader->written && !def->referenced)\n\t\t{\n\t\t\tint wt = def->constant?WARN_NOTREFERENCEDCONST:WARN_NOTREFERENCED;\n\t\t\tif (def->type->type == ev_field && def->constant)\n\t\t\t\twt = WARN_NOTREFERENCEDFIELD;\n\t\t\tpr_scope = def->scope;\n\t\t\tif (!strncmp(def->name, \"spawnfunc_\", 10))\n\t\t\t\t;\t//no warnings from unreferenced entry points.\n\t\t\telse if (strcmp(def->name, \"IMMEDIATE\") && qccwarningaction[wt] && !(def->type->type == ev_function && def->symbolheader->timescalled) && !def->symbolheader->used)\n\t\t\t{\n\t\t\t\tchar typestr[256];\n\t\t\t\tif (QC_strcasestr(def->filen, \"extensions\") && verbose < VERBOSE_STANDARD)\n\t\t\t\t{\t//try to avoid annoying warnings from dpextensions.qc\n\t\t\t\t\textwarncount++;\n\t\t\t\t\tQCC_PR_Warning(wt, def->filen, def->s_line, NULL);\n\t\t\t\t}\n\t\t\t\telse if (def->arraysize)\n\t\t\t\t\tQCC_PR_Warning(wt, def->filen, def->s_line, (dupewarncount++ >= 10 && verbose < VERBOSE_STANDARD)?NULL:\"%s %s%s%s[%i]  no references.\", TypeName(def->type, typestr, sizeof(typestr)), col_symbol, def->name, col_none, def->arraysize);\n\t\t\t\telse\n\t\t\t\t\tQCC_PR_Warning(wt, def->filen, def->s_line, (dupewarncount++ >= 10 && verbose < VERBOSE_STANDARD)?NULL:\"%s %s%s%s  no references.\", TypeName(def->type, typestr, sizeof(typestr)), col_symbol, def->name, col_none);\n\t\t\t}\n\t\t\tpr_scope = NULL;\n\n\t\t\tif (def->symbolheader->used)\n\t\t\t{\n\t\t\t\tchar typestr[256];\n\t\t\t\tQCC_sref_t sr = {def, {0}, def->type};\n\t\t\t\tQCC_PR_Warning(WARN_NOTREFERENCED, def->filen, def->s_line, \"%s %s%s%s = %s used, but not referenced.\", TypeName(def->type, typestr, sizeof(typestr)), col_symbol, def->name, col_none, QCC_VarAtOffset(sr));\n\t\t\t}\n\t\t\t/*if (opt_unreferenced && def->type->type != ev_field)\n\t\t\t{\n\t\t\t\toptres_unreferenced++;\n#ifdef DEBUG_DUMP\n\t\t\t\texterns->Printf(\"code: %s:%i: strip noref %s %s@%i;\\n\", def->filen, def->s_line, def->type->name, def->name, def->ofs);\n#endif\n\t\t\t\tcontinue;\n\t\t\t}*/\n\t\t}\n\t\tif ((def->type->type == ev_struct || def->type->type == ev_union || def->arraysize) && def->deftail)\n\t\t{\n#ifdef DEBUG_DUMP\n\t\t\texterns->Printf(\"code: %s:%i: strip struct %s %s@%i;\\n\", def->filen, def->s_line, def->type->name, def->name, def->ofs);\n#endif\n\t\t\t//the head of an array/struct is never written. only, its member fields are.\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (def->strip || !def->symbolheader->used)\n\t\t{\n\t\t\toptres_unreferenced++;\n#ifdef DEBUG_DUMP\n\t\t\texterns->Printf(\"code: %s:%i: strip %s %s@%i;\\n\", def->filen, def->s_line, def->type->name, def->name, def->ofs);\n#endif\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (def->type->type == ev_function)\n\t\t{\n\t\t\tif (opt_function_names && def->initialized && functions[def->symboldata[0].function].code<0)\n\t\t\t{\n\t\t\t\toptres_function_names++;\n\t\t\t\tdef->name = \"\";\n\t\t\t}\n#if IAMNOTLAZY\n\t\t\tif (!def->timescalled)\n\t\t\t{\n\t\t\t\tif (def->references<=1 && strncmp(def->name, \"spawnfunc_\", 10))\n\t\t\t\t\tQCC_PR_Warning(WARN_DEADCODE, strings + def->s_file, def->s_line, \"%s is never directly called or referenced (spawn function or dead code)\", def->name);\n//\t\t\t\telse\n//\t\t\t\t\tQCC_PR_Warning(WARN_DEADCODE, strings + def->s_file, def->s_line, \"%s is never directly called\", def->name);\n\t\t\t}\n\t\t\tif (opt_stripfunctions && def->constant && def->timescalled >= def->references-1)\t//make sure it's not copied into a different var.\n\t\t\t{\t\t\t\t\t\t\t\t//if it ever does self.think then it could be needed for saves.\n\t\t\t\toptres_stripfunctions++;\t//if it's only ever called explicitly, the engine doesn't need to know.\n#ifdef DEBUG_DUMP\n\t\t\t\texterns->Printf(\"code: %s:%i: strip const %s %s@%i;\\n\", strings+def->s_file, def->s_line, def->type->name, def->name, def->ofs);\n#endif\n\t\t\t\tcontinue;\n\t\t\t}\n#endif\n\t\t}\n\t\telse if (def->type->type == ev_field && def->constant && (!def->scope || def->isstatic || def->initialized))\n\t\t{\n\t\t\tQCC_GenerateFieldDefs(def, def->name, 0, def->type->aux_type);\n\t\t\tcontinue;\n\t\t}\n\t\telse if (def->type->type == ev_pointer && (def->symboldata[0]._int & 0x80000000))\n\t\t{\t//pointer relocs must never be stripped as we don't know the final addresses in advance. would screw stuff up.\n\t\t\tif (opt_constant_names && !def->nostrip)\n\t\t\t\tdef->name = \"\";\t//reloc, can't strip it (engine needs to fix em up), but can clear its name.\n\t\t}\n\t\telse if (def->scope && !def->scope->privatelocals && !def->isstatic)\n\t\t\tcontinue;\t//def is a local, which got shared and should be 0...\n\t\telse if ((def->type->type == ev_pointer || def->type->type == ev_string) && def->initialized && (def->symboldata[0]._int || !strncmp(def->name, \"dotranslate_\", 12)))\n\t\t{\t//string types in addons cannot be stripped - the engine needs to update offset to the new string table.\n\t\t\tif (!def->nostrip && (def->scope||def->constant||flag_noreflection))\n\t\t\tif (strncmp(def->name, \"dotranslate_\", 12) && strncmp(def->name, \"autocvar_\", 9))\t//special crap.\n\t\t\t{\t//we can at least strip the name\n\t\t\t\tif (opt_constant_names_strings)\n\t\t\t\t\tcontinue; //drop entirely.\n\t\t\t\tif (opt_constant_names)\n\t\t\t\t{\n\t\t\t\t\toptres_constant_names_strings += strlen(def->name);\n\t\t\t\t\t/*char *n = qccHunkAlloc(7+strlen(def->name)+1);\n\t\t\t\t\tsprintf(n, \"STRIP_%s\", def->name);\n\t\t\t\t\tdef->name = n;*/\n\t\t\t\t\tdef->name = \"\";\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse if ((opt_constant_names&&(def->scope||def->constant))||(flag_noreflection&&strncmp(def->name, \"autocvar_\", 9)))// && (def->type->type != ev_string || (opt_constant_names_strings && strncmp(def->name, \"dotranslate_\", 12))))\n\t\t{\n\t\t\tif (!def->nostrip)\n\t\t\t{\n\t\t\t\tif (def->type->type == ev_string)\n\t\t\t\t\toptres_constant_names_strings += strlen(def->name);\n\t\t\t\telse\n\t\t\t\t\toptres_constant_names += strlen(def->name);\n\n#ifdef DEBUG_DUMP\n\t\t\t\tif (def->scope)\n\t\t\t\t\texterns->Printf(\"code: %s:%i: strip local %s %s @%i;\\n\", def->filen, def->s_line, def->type->name, def->name, def->ofs);\n\t\t\t\telse if (def->constant)\n\t\t\t\t\texterns->Printf(\"code: %s:%i: strip const %s %s @%i;\\n\", def->filen, def->s_line, def->type->name, def->name, def->ofs);\n\t\t\t\telse\n\t\t\t\t\texterns->Printf(\"code: %s:%i: strip globl %s %s @%i;\\n\", def->filen, def->s_line, def->type->name, def->name, def->ofs);\n#endif\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\n//\t\tif (!def->saved && def->type->type != ev_string)\n//\t\t\tcontinue;\n\t\tdd = &qcc_globals[numglobaldefs];\n\t\tnumglobaldefs++;\n\n\t\tif (types)\n\t\t\tdd->type = def->type-qcc_typeinfo;\n\t\telse\n\t\t\tdd->type = def->type->type;\n#ifdef DEF_SAVEGLOBAL\n\t\tif ( def->saved && !def->constant// || def->type->type == ev_function)\n//\t\t&& def->type->type != ev_function\n\t\t&& def->type->type != ev_field\n\t\t&& def->scope == NULL)\n\t\t{\n\t\t\tdd->type |= DEF_SAVEGLOBAL;\n\t\t}\n#endif\n\t\tif (def->shared)\n\t\t\tdd->type |= DEF_SHARED;\n\n\t\tif (opt_locals && ((def->scope&&!def->isstatic) || !strcmp(def->name, \"IMMEDIATE\")))\n\t\t{\n\t\t\tdd->s_name = 0;\n\t\t\toptres_locals += strlen(def->name);\n\t\t}\n\t\telse\n\t\t\tdd->s_name = QCC_CopyString (def->name);\n\t\tdd->ofs = def->ofs;\n\n#ifdef DEBUG_DUMP\n\t\tif ((dd->type&~(DEF_SHARED|DEF_SAVEGLOBAL)) == ev_string)\n\t\t\texterns->Printf(\"code: %s:%i: %s%s%s %s@%i = \\\"%s\\\"\\n\", def->filen, def->s_line, dd->type&DEF_SAVEGLOBAL?\"save \":\"nosave \", dd->type&DEF_SHARED?\"shared \":\"\", basictypenames[dd->type&~(DEF_SHARED|DEF_SAVEGLOBAL)], strings+dd->s_name, dd->ofs, ((unsigned)def->symboldata[0].string>=(unsigned)strofs)?\"???\":(strings + def->symboldata[0].string));\n\t\telse if ((dd->type&~(DEF_SHARED|DEF_SAVEGLOBAL)) == ev_float)\n\t\t\texterns->Printf(\"code: %s:%i: %s%s%s %s@%i = %g\\n\", def->filen, def->s_line, dd->type&DEF_SAVEGLOBAL?\"save \":\"nosave \", dd->type&DEF_SHARED?\"shared \":\"\", basictypenames[dd->type&~(DEF_SHARED|DEF_SAVEGLOBAL)], strings+dd->s_name, dd->ofs, def->symboldata[0]._float);\n\t\telse if ((dd->type&~(DEF_SHARED|DEF_SAVEGLOBAL)) == ev_integer)\n\t\t\texterns->Printf(\"code: %s:%i: %s%s%s %s@%i = %i\\n\", def->filen, def->s_line, dd->type&DEF_SAVEGLOBAL?\"save \":\"nosave \", dd->type&DEF_SHARED?\"shared \":\"\", basictypenames[dd->type&~(DEF_SHARED|DEF_SAVEGLOBAL)], strings+dd->s_name, dd->ofs, def->symboldata[0]._int);\n\t\telse if ((dd->type&~(DEF_SHARED|DEF_SAVEGLOBAL)) == ev_vector)\n\t\t\texterns->Printf(\"code: %s:%i: %s%s%s %s@%i = '%g %g %g'\\n\", def->filen, def->s_line, dd->type&DEF_SAVEGLOBAL?\"save \":\"nosave \", dd->type&DEF_SHARED?\"shared \":\"\", basictypenames[dd->type&~(DEF_SHARED|DEF_SAVEGLOBAL)], strings+dd->s_name, dd->ofs, def->symboldata[0].vector[0], def->symboldata[0].vector[1], def->symboldata[0].vector[2]);\n\t\telse if ((dd->type&~(DEF_SHARED|DEF_SAVEGLOBAL)) == ev_function)\n\t\t\texterns->Printf(\"code: %s:%i: %s%s%s %s@%i = %i(%s)\\n\", def->filen, def->s_line, dd->type&DEF_SAVEGLOBAL?\"save \":\"nosave \", dd->type&DEF_SHARED?\"shared \":\"\", basictypenames[dd->type&~(DEF_SHARED|DEF_SAVEGLOBAL)], strings+dd->s_name, dd->ofs, def->symboldata[0].function, def->symboldata[0].function >= numfunctions?\"???\":functions[def->symboldata[0].function].name);\n\t\telse if ((dd->type&~(DEF_SHARED|DEF_SAVEGLOBAL)) == ev_field)\n\t\t\texterns->Printf(\"code: %s:%i: %s%s%s %s@%i = @%i\\n\", def->filen, def->s_line, dd->type&DEF_SAVEGLOBAL?\"save \":\"nosave \", dd->type&DEF_SHARED?\"shared \":\"\", basictypenames[dd->type&~(DEF_SHARED|DEF_SAVEGLOBAL)], strings+dd->s_name, dd->ofs, def->symboldata[0]._int);\n\t\telse\n\t\t\texterns->Printf(\"code: %s:%i: %s%s%s %s@%i\\n\", def->filen, def->s_line, dd->type&DEF_SAVEGLOBAL?\"save \":\"nosave \", dd->type&DEF_SHARED?\"shared \":\"\", basictypenames[dd->type&~(DEF_SHARED|DEF_SAVEGLOBAL)], strings+dd->s_name, dd->ofs);\n#endif\n\t}\n\tQCC_SortFields();\n\n\tif (dupewarncount > 10 && verbose < VERBOSE_STANDARD)\n\t\tQCC_PR_Note(WARN_NOTREFERENCED, NULL, 0, \"suppressed %i more warnings about unreferenced variables, as you clearly don't care about the first 10.\", dupewarncount-10);\n\tif (extwarncount)\n\t\tQCC_PR_Note(WARN_NOTREFERENCED, NULL, 0, \"suppressed %i warnings about unused extensions.\", extwarncount);\n\n\tfor (i = 0; i < numglobaldefs; i++)\n\t{\n\t\tdd = &qcc_globals[i];\n\t\tif (!(dd->type & DEF_SAVEGLOBAL))\t//only warn about saved ones.\n\t\t\tcontinue;\n\n\t\tfor (h = 0; h < numglobaldefs; h++)\n\t\t{\n\t\t\tif (i == h || !(qcc_globals[h].type & DEF_SAVEGLOBAL))\n\t\t\t\tcontinue;\n\t\t\tif (dd->ofs == qcc_globals[h].ofs)\n\t\t\t{\n\t\t\t\tif ((dd->type&~DEF_SAVEGLOBAL) != (qcc_globals[h].type&~DEF_SAVEGLOBAL))\n\t\t\t\t{\n\t\t\t\t\tif (!(((dd->type&~DEF_SAVEGLOBAL) == ev_vector && (qcc_globals[h].type&~DEF_SAVEGLOBAL) == ev_float) ||\n\t\t\t\t\t\t((dd->type&~DEF_SAVEGLOBAL) == ev_struct || (dd->type&~DEF_SAVEGLOBAL) == ev_union)))\n\t\t\t\t\t\tQCC_PR_Warning(0, NULL, 0, \"Mismatched union global types (%s and %s)\", strings+dd->s_name, strings+qcc_globals[h].s_name);\n\t\t\t\t}\n\t\t\t\t//remove the saveglobal flag on the duplicate globals.\n\t\t\t\tqcc_globals[h].type &= ~DEF_SAVEGLOBAL;\n\t\t\t}\n\t\t}\n\t}\n\tfor (i = 1; i < numfielddefs; i++)\n\t{\n\t\tdd = &fields[i];\n\n\t\tif (dd->type == ev_vector || dd->type == ev_struct || dd->type == ev_union)\t//just ignore vectors, structs, and unions.\n\t\t\tcontinue;\n\n\t\tfor (h = 1; h < numfielddefs; h++)\n\t\t{\n\t\t\tif (i == h)\n\t\t\t\tcontinue;\n\t\t\tif (dd->ofs == fields[h].ofs)\n\t\t\t{\n\t\t\t\tif (dd->type != fields[h].type)\n\t\t\t\t{\n\t\t\t\t\tif (fields[h].type != ev_vector && fields[h].type != ev_struct && fields[h].type != ev_union)\n\t\t\t\t\t{\n\t\t\t\t\t\tQCC_PR_Warning(0, NULL, 0, \"Mismatched union field types (%s and %s @%i)\", strings+dd->s_name, strings+fields[h].s_name, dd->ofs);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tif (numglobaldefs > MAX_GLOBALS)\n\t\tQCC_Error(ERR_TOOMANYGLOBALS, \"Too many globals - %i\\nAdd '-max_globals %i' to the commandline\", numglobaldefs, (numglobaldefs+32768)&~32767);\n\n\n\tdupewarncount = 0;\n\tfor (i = 0; i < nummodels; i++)\n\t{\n\t\tif (!precache_model[i].used)\n\t\t\tdupewarncount+=QCC_PR_Warning(WARN_EXTRAPRECACHE, precache_model[i].filename, precache_model[i].fileline, (dupewarncount>10&&verbose < VERBOSE_STANDARD)?NULL:\"Model \\\"%s\\\" was precached but not directly used%s\", precache_model[i].name, dupewarncount?\"\":\" (annotate the usage with the used_model intrinsic to silence this warning)\");\n\t\telse if (!precache_model[i].block)\n\t\t\tdupewarncount+=QCC_PR_Warning(WARN_NOTPRECACHED, precache_model[i].filename, precache_model[i].fileline, (dupewarncount>10&&verbose < VERBOSE_STANDARD)?NULL:\"Model \\\"%s\\\" was used but not directly precached\", precache_model[i].name);\n\t}\n\n\tfor (i = 0; i < numsounds; i++)\n\t{\n\t\tif (!precache_sound[i].used)\n\t\t\tdupewarncount+=QCC_PR_Warning(WARN_EXTRAPRECACHE, precache_sound[i].filename, precache_sound[i].fileline, (dupewarncount>10&&verbose < VERBOSE_STANDARD)?NULL:\"Sound \\\"%s\\\" was precached but not directly used%s\", precache_sound[i].name, dupewarncount?\"\":\" (annotate the usage with the used_sound intrinsic to silence this warning)\");\n\t\telse if (!precache_sound[i].block)\n\t\t\tdupewarncount+=QCC_PR_Warning(WARN_NOTPRECACHED, precache_sound[i].filename, precache_sound[i].fileline, (dupewarncount>10&&verbose < VERBOSE_STANDARD)?NULL:\"Sound \\\"%s\\\" was used but not directly precached\", precache_sound[i].name);\n\t}\n\n\tif (dupewarncount > 10 && verbose < VERBOSE_STANDARD)\n\t\tQCC_PR_Note(WARN_NOTREFERENCED, NULL, 0, \"suppressed %i more %swarnings%s about precaches.\", dupewarncount-10, col_warning, col_none);\n\n//PrintStrings ();\n//PrintFunctions ();\n//PrintFields ();\n//PrintGlobals ();\nstrofs = (strofs+3)&~3;\n\n\tif (verbose >= VERBOSE_STANDARD)\n\t{\n\t\texterns->Printf (\"%6i strofs (of %i)\\n\", strofs, MAX_STRINGS);\n\t\texterns->Printf (\"%6i numstatements (of %i)\\n\", numstatements, MAX_STATEMENTS);\n\t\texterns->Printf (\"%6i numfunctions (of %i)\\n\", numfunctions, MAX_FUNCTIONS);\n\t\texterns->Printf (\"%6i numglobaldefs (of %i)\\n\", numglobaldefs, MAX_GLOBALS);\n\t\texterns->Printf (\"%6i numfielddefs (%i unique) (of %i)\\n\", numfielddefs, pr.size_fields, MAX_FIELDS);\n\t\texterns->Printf (\"%6i numpr_globals (of %i)\\n\", numpr_globals, MAX_REGS);\n\t\texterns->Printf (\"%6i nummodels\\n\", nummodels);\n\t\texterns->Printf (\"%6i numsounds\\n\", numsounds);\n\t}\n\n\tif (!*destfile)\n\t\tstrcpy(destfile, \"progs.dat\");\n\tif (verbose >= VERBOSE_PROGRESS)\n\t\texterns->Printf(\"Writing %s\\n\", destfile);\n\th = SafeOpenWrite (destfile, 2*1024*1024);\n\tSafeWrite (h, &progs, sizeof(progs));\n\tSafeWrite (h, \"\\r\\n\\r\\n\", 4);\n\tSafeWrite (h, QCC_copyright, strlen(QCC_copyright)+1);\n\tSafeWrite (h, \"\\r\\n\\r\\n\", 4);\n\twhile(SafeSeek (h, 0, SEEK_CUR) & 3)//this is a lame way to do it\n\t{\n\t\tSafeWrite (h, \"\\0\", 1);\n\t}\n\n\tprogs.ofs_strings = SafeSeek (h, 0, SEEK_CUR);\n\tprogs.numstrings = strofs;\n\n\tif (progs.blockscompressed&16)\n\t{\n\t\tSafeWrite (h, &len, sizeof(int));\t//save for later\n\t\tlen = QC_encode(progfuncs, strofs*sizeof(char), 2, (char *)strings, h);\t//write\n\t\ti = SafeSeek (h, 0, SEEK_CUR);\n\t\tSafeSeek(h, progs.ofs_strings, SEEK_SET);//seek back\n\t\tlen = PRLittleLong(len);\n\t\tSafeWrite (h, &len, sizeof(int));\t//write size.\n\t\tSafeSeek(h, i, SEEK_SET);\n\t}\n\telse\n\t\tSafeWrite (h, strings, strofs);\n\n\tprogs.ofs_statements = SafeSeek (h, 0, SEEK_CUR);\n\tprogs.numstatements = numstatements;\n\n\tif (qcc_targetformat == QCF_HEXEN2 || qcc_targetformat == QCF_UHEXEN2 || qcc_targetformat == QCF_FTEH2)\n\t{\n\t\tfor (i=0 ; i<numstatements ; i++)\n\t\t{\n\t\t\tif (statements[i].op >= OP_CALL1 && statements[i].op <= OP_CALL8)\n\t\t\t\tQCC_Error(ERR_BADTARGETSWITCH, \"Target switching produced incompatible instructions\");\n\t\t\telse if (statements[i].op >= OP_CALL1H && statements[i].op <= OP_CALL8H)\n\t\t\t\tstatements[i].op = statements[i].op - OP_CALL1H + OP_CALL1;\n\t\t}\n\t}\n\n\tif (!opt_filenames)\n\t{\n\t\tstatement_linenums = qccHunkAlloc(sizeof(statement_linenums) * numstatements);\n\t\tfor (i = 0; i < numstatements; i++)\n\t\t\tstatement_linenums[i] = statements[i].linenum;\n\t}\n\telse\n\t\tstatement_linenums = NULL;\n\n\n\tswitch(outputsttype)\n\t{\n\tcase PST_UHEXEN2:\n\t\t{\n\t\t\tQCC_dstatement32_t *statements32 = qccHunkAlloc(sizeof(*statements32) * numstatements);\n\t\t\tfor (i=0 ; i<numstatements ; i++)\n\t\t\t{\n\t\t\t\tstatements32[i].op = PRLittleLong(statements[i].op<<16);\n\t\t\t\tstatements32[i].a = PRLittleLong((statements[i].a.sym?statements[i].a.sym->ofs:0) + statements[i].a.ofs);\n\t\t\t\tstatements32[i].b = PRLittleLong((statements[i].b.sym?statements[i].b.sym->ofs:0) + statements[i].b.ofs);\n\t\t\t\tstatements32[i].c = PRLittleLong((statements[i].c.sym?statements[i].c.sym->ofs:0) + statements[i].c.ofs);\n\n\t\t\t\tif (verbose >= VERBOSE_DEBUGSTATEMENTS)\n\t\t\t\t\texterns->Printf(\"code: %s:%i: @%i %s %i %i %i\\n\", QCC_FileForStatement(i), statements[i].linenum, i, pr_opcodes[statements[i].op].name, statements32[i].a, statements32[i].b, statements32[i].c);\n\t\t\t}\n\n\t\t\tSafeWrite (h, statements32, numstatements*sizeof(QCC_dstatement32_t));\n\t\t}\n\t\tbreak;\n\tcase PST_KKQWSV:\n\tcase PST_FTE32:\n\t\t{\n\t\t\tQCC_dstatement32_t *statements32 = qccHunkAlloc(sizeof(*statements32) * numstatements);\n\t\t\tfor (i=0 ; i<numstatements ; i++)\n\t\t\t{\n\t\t\t\tstatements32[i].op = PRLittleLong(statements[i].op);\n\t\t\t\tstatements32[i].a = PRLittleLong((statements[i].a.sym?statements[i].a.sym->ofs:0) + statements[i].a.ofs);\n\t\t\t\tstatements32[i].b = PRLittleLong((statements[i].b.sym?statements[i].b.sym->ofs:0) + statements[i].b.ofs);\n\t\t\t\tstatements32[i].c = PRLittleLong((statements[i].c.sym?statements[i].c.sym->ofs:0) + statements[i].c.ofs);\n\n\t\t\t\tif (verbose >= VERBOSE_DEBUGSTATEMENTS)\n\t\t\t\t\texterns->Printf(\"code: %s:%i: @%i %s %i %i %i\\n\", QCC_FileForStatement(i), statements[i].linenum, i, pr_opcodes[statements[i].op].name, statements32[i].a, statements32[i].b, statements32[i].c);\n\t\t\t}\n\n\t\t\tif (progs.blockscompressed&1)\n\t\t\t{\n\t\t\t\tSafeWrite (h, &len, sizeof(int));\t//save for later\n\t\t\t\tlen = QC_encode(progfuncs, numstatements*sizeof(QCC_dstatement32_t), 2, (char *)statements, h);\t//write\n\t\t\t\ti = SafeSeek (h, 0, SEEK_CUR);\n\t\t\t\tSafeSeek(h, progs.ofs_statements, SEEK_SET);//seek back\n\t\t\t\tlen = PRLittleLong(len);\n\t\t\t\tSafeWrite (h, &len, sizeof(int));\t//write size.\n\t\t\t\tSafeSeek(h, i, SEEK_SET);\n\t\t\t}\n\t\t\telse\n\t\t\t\tSafeWrite (h, statements32, numstatements*sizeof(QCC_dstatement32_t));\n\t\t}\n\t\tbreak;\n\tcase PST_QTEST:\n\t\t{\n\t\t\tqtest_statement_t *qtst = qccHunkAlloc(sizeof(*qtst) * numstatements);\n\t\t\tfor (i=0 ; i<numstatements ; i++) // scale down from 16-byte internal to 12-byte qtest\n\t\t\t{\n\t\t\t\tunsigned int a = (statements[i].a.sym?statements[i].a.sym->ofs:0) + statements[i].a.ofs;\n\t\t\t\tunsigned int b = (statements[i].b.sym?statements[i].b.sym->ofs:0) + statements[i].b.ofs;\n\t\t\t\tunsigned int c = (statements[i].c.sym?statements[i].c.sym->ofs:0) + statements[i].c.ofs;\n\n\t\t\t\tqtst[i].line = statements[i].linenum;\n\t\t\t\tqtst[i].op = PRLittleShort((unsigned short)statements[i].op);\n\t\t\t\tqtst[i].a = (unsigned short)PRLittleShort((unsigned short)a);\n\t\t\t\tqtst[i].b = (unsigned short)PRLittleShort((unsigned short)b);\n\t\t\t\tqtst[i].c = (unsigned short)PRLittleShort((unsigned short)c);\n\t\t\t}\n\n\t\t\t// no compression\n\t\t\tSafeWrite (h, qtst, numstatements*sizeof(qtest_statement_t));\n\t\t}\n\t\tbreak;\n\tcase PST_DEFAULT:\n\t\t{\n\t\t\tQCC_dstatement16_t *statements16 = qccHunkAlloc(sizeof(*statements16) * numstatements);\n#ifdef DISASM\n\t\t\tint start, end;\n\t\t\tfor (i = 1; i < numfunctions; i++)\n\t\t\t{\n\t\t\t\tif (!strcmp(functions[i].name, DISASM))\n\t\t\t\t{\n\t\t\t\t\tstart = functions[i].code;\n\t\t\t\t\tend = functions[i+1].code;\n\t\t\t\t}\n\t\t\t}\n#endif\n\t\t\tfor (i=0 ; i<numstatements ; i++)\t//resize as we go - scaling down\n\t\t\t{\n\t\t\t\tunsigned int a = (statements[i].a.sym?statements[i].a.sym->ofs:0) + statements[i].a.ofs;\n\t\t\t\tunsigned int b = (statements[i].b.sym?statements[i].b.sym->ofs:0) + statements[i].b.ofs;\n\t\t\t\tunsigned int c = (statements[i].c.sym?statements[i].c.sym->ofs:0) + statements[i].c.ofs;\n\t\t\t\tstatements16[i].op = PRLittleShort((unsigned short)statements[i].op);\n\n\t\t\t\tif (\n#if defined(DISASM)\n\t\t\t\t\t(i >= start && i < end) ||\n#endif\n\t\t\t\t\tverbose >= VERBOSE_DEBUGSTATEMENTS)\n\t\t\t\t{\n\t\t\t\t\tchar line[2048];\n\n\t\t\t\t\tQC_snprintfz(line, sizeof(line), \"code: %s:%i: @%i  %s  %i  %i  %i  (%s\", QCC_FileForStatement(i), statements[i].linenum, i, pr_opcodes[statements[i].op].opname, a, b, c, QCC_VarAtOffset(statements[i].a));\n\t\t\t\t\tQC_snprintfz(line+strlen(line), sizeof(line)-strlen(line), \" %s\", QCC_VarAtOffset(statements[i].b));\n\t\t\t\t\tQC_snprintfz(line+strlen(line), sizeof(line)-strlen(line), \" %s)\\n\", QCC_VarAtOffset(statements[i].c));\n\t\t\t\t\texterns->Printf(\"%s\", line);\n\t\t\t\t}\n#ifdef _DEBUG\n\t\t\t\tif (((signed)a >= (signed)numpr_globals && statements[i].a.sym) || ((signed)b >= (signed)numpr_globals && statements[i].b.sym) || ((signed)c >= (signed)numpr_globals && statements[i].c.sym))\n\t\t\t\t\texterns->Printf(\"invalid offset on %s instruction (from line %i)\\n\", pr_opcodes[statements[i].op].opname, statements[i].linenum);\n#endif\n\t\t\t\t//truncate to 16bit. should probably warn if the high bits are not 0x0000 or 0xffff\n\t\t\t\tstatements16[i].a = (unsigned short)PRLittleShort((unsigned short)a);\n\t\t\t\tstatements16[i].b = (unsigned short)PRLittleShort((unsigned short)b);\n\t\t\t\tstatements16[i].c = (unsigned short)PRLittleShort((unsigned short)c);\n\t\t\t}\n\n\t\t\tif (progs.blockscompressed&1)\n\t\t\t{\n\t\t\t\tSafeWrite (h, &len, sizeof(int));\t//save for later\n\t\t\t\tlen = QC_encode(progfuncs, numstatements*sizeof(QCC_dstatement16_t), 2, (char *)statements16, h);\t//write\n\t\t\t\ti = SafeSeek (h, 0, SEEK_CUR);\n\t\t\t\tSafeSeek(h, progs.ofs_statements, SEEK_SET);//seek back\n\t\t\t\tlen = PRLittleLong(len);\n\t\t\t\tSafeWrite (h, &len, sizeof(int));\t//write size.\n\t\t\t\tSafeSeek(h, i, SEEK_SET);\n\t\t\t}\n\t\t\telse\n\t\t\t\tSafeWrite (h, statements16, numstatements*sizeof(QCC_dstatement16_t));\n\t\t}\n\t\tbreak;\n\tdefault:\n\t\texterns->Sys_Error(\"structtype error\");\n\t}\n\n\tprogs.ofs_functions = SafeSeek (h, 0, SEEK_CUR);\n\tprogs.numfunctions = numfunctions;\n\tif (progs.blockscompressed&8)\n\t{\n\t\tSafeWrite (h, &len, sizeof(int));\t//save for later\n\t\tlen = QC_encode(progfuncs, funcdatasize, 2, (char *)funcdata, h);\t//write\n\t\ti = SafeSeek (h, 0, SEEK_CUR);\n\t\tSafeSeek(h, progs.ofs_functions, SEEK_SET);//seek back\n\t\tlen = PRLittleLong(len);\n\t\tSafeWrite (h, &len, sizeof(int));\t//write size.\n\t\tSafeSeek(h, i, SEEK_SET);\n\t}\n\telse\n\t\tSafeWrite (h, funcdata, funcdatasize);\n\n\tif (flag_dumpfilenames)\n\t\tQCC_DumpFiles(destfile);\n\n\tif (flag_dumpfields)\n\t\tQCC_DumpFields(destfile);\n\n\tif (flag_dumpsymbols)\n\t\tQCC_DumpSymbolNames(destfile);\n//\tQCC_DumpSymbolInfo(destfile);\n\tif (flag_dumpautocvars)\n\t\tQCC_DumpAutoCvars(destfile);\n\tif (flag_dumplocalisation)\n\t\tQCC_DumpLocalisation(destfile);\n\tif (flag_dumptags)\n\t\tQCC_DumpTags(destfile);\n\tif (flag_dumpopcodes)\n\t\tQCC_DumpOpcodes(destfile);\n\n\tswitch(outputsttype)\n\t{\n\tcase PST_QTEST:\n\t\t// qtest needs a struct remap but should be able to get away with a simple swap here\n\t\tfor (i=0 ; i<numglobaldefs ; i++)\n\t\t{\n\t\t\tqtest_def_t qtdef = ((qtest_def_t *)qcc_globals)[i];\n\t\t\tqcc_globals[i].type = qtdef.type;\n\t\t\tqcc_globals[i].ofs = qtdef.ofs;\n\t\t\tqcc_globals[i].s_name = qtdef.s_name;\n\t\t}\n\t\tfor (i=0 ; i<numfielddefs ; i++)\n\t\t{\n\t\t\tqtest_def_t qtdef = ((qtest_def_t *)fields)[i];\n\t\t\tfields[i].type = qtdef.type;\n\t\t\tfields[i].ofs = qtdef.ofs;\n\t\t\tfields[i].s_name = qtdef.s_name;\n\t\t}\n\t\t// passthrough.. reuse FTE32 code\n\tcase PST_FTE32:\n\t\tprogs.ofs_globaldefs = SafeSeek (h, 0, SEEK_CUR);\n\t\tprogs.numglobaldefs = numglobaldefs;\n\t\tfor (i=0 ; i<numglobaldefs ; i++)\n\t\t{\n\t\t\tqcc_globals[i].type = PRLittleLong/*PRLittleShort*/ (qcc_globals[i].type);\n\t\t\tqcc_globals[i].ofs = PRLittleLong/*PRLittleShort*/ (qcc_globals[i].ofs);\n\t\t\tqcc_globals[i].s_name = PRLittleLong (qcc_globals[i].s_name);\n\t\t}\n\n\t\tif (progs.blockscompressed&2)\n\t\t{\n\t\t\tSafeWrite (h, &len, sizeof(int));\t//save for later\n\t\t\tlen = QC_encode(progfuncs, numglobaldefs*sizeof(QCC_ddef_t), 2, (char *)qcc_globals, h);\t//write\n\t\t\ti = SafeSeek (h, 0, SEEK_CUR);\n\t\t\tSafeSeek(h, progs.ofs_globaldefs, SEEK_SET);//seek back\n\t\t\tlen = PRLittleLong(len);\n\t\t\tSafeWrite (h, &len, sizeof(int));\t//write size.\n\t\t\tSafeSeek(h, i, SEEK_SET);\n\t\t}\n\t\telse\n\t\t\tSafeWrite (h, qcc_globals, numglobaldefs*sizeof(QCC_ddef_t));\n\n\t\tprogs.ofs_fielddefs = SafeSeek (h, 0, SEEK_CUR);\n\t\tprogs.numfielddefs = numfielddefs;\n\n\t\tfor (i=0 ; i<numfielddefs ; i++)\n\t\t{\n\t\t\tfields[i].type = PRLittleLong/*PRLittleShort*/ (fields[i].type);\n\t\t\tfields[i].ofs = PRLittleLong/*PRLittleShort*/ (fields[i].ofs);\n\t\t\tfields[i].s_name = PRLittleLong (fields[i].s_name);\n\t\t}\n\n\t\tif (progs.blockscompressed&4)\n\t\t{\n\t\t\tSafeWrite (h, &len, sizeof(int));\t//save for later\n\t\t\tlen = QC_encode(progfuncs, numfielddefs*sizeof(QCC_ddef_t), 2, (char *)fields, h);\t//write\n\t\t\ti = SafeSeek (h, 0, SEEK_CUR);\n\t\t\tSafeSeek(h, progs.ofs_fielddefs, SEEK_SET);//seek back\n\t\t\tlen = PRLittleLong(len);\n\t\t\tSafeWrite (h, &len, sizeof(int));\t//write size.\n\t\t\tSafeSeek(h, i, SEEK_SET);\n\t\t}\n\t\telse\n\t\t\tSafeWrite (h, fields, numfielddefs*sizeof(QCC_ddef_t));\n\t\tbreak;\n\tcase PST_UHEXEN2:\n\t\tprogs.ofs_globaldefs = SafeSeek (h, 0, SEEK_CUR);\n\t\tprogs.numglobaldefs = numglobaldefs;\n\t\tfor (i=0 ; i<numglobaldefs ; i++)\n\t\t{\n\t\t\tqcc_globals[i].type = PRLittleLong (qcc_globals[i].type<<16);\n\t\t\tqcc_globals[i].ofs = PRLittleLong (qcc_globals[i].ofs);\n\t\t\tqcc_globals[i].s_name = PRLittleLong (qcc_globals[i].s_name);\n\t\t}\n\n\t\tif (progs.blockscompressed&2)\n\t\t{\n\t\t\tSafeWrite (h, &len, sizeof(int));\t//save for later\n\t\t\tlen = QC_encode(progfuncs, numglobaldefs*sizeof(QCC_ddef_t), 2, (char *)qcc_globals, h);\t//write\n\t\t\ti = SafeSeek (h, 0, SEEK_CUR);\n\t\t\tSafeSeek(h, progs.ofs_globaldefs, SEEK_SET);//seek back\n\t\t\tlen = PRLittleLong(len);\n\t\t\tSafeWrite (h, &len, sizeof(int));\t//write size.\n\t\t\tSafeSeek(h, i, SEEK_SET);\n\t\t}\n\t\telse\n\t\t\tSafeWrite (h, qcc_globals, numglobaldefs*sizeof(QCC_ddef_t));\n\n\t\tprogs.ofs_fielddefs = SafeSeek (h, 0, SEEK_CUR);\n\t\tprogs.numfielddefs = numfielddefs;\n\n\t\tfor (i=0 ; i<numfielddefs ; i++)\n\t\t{\n\t\t\tfields[i].type = PRLittleLong/*PRLittleShort*/ (fields[i].type<<16);\n\t\t\tfields[i].ofs = PRLittleLong/*PRLittleShort*/ (fields[i].ofs);\n\t\t\tfields[i].s_name = PRLittleLong (fields[i].s_name);\n\t\t}\n\n\t\tif (progs.blockscompressed&4)\n\t\t{\n\t\t\tSafeWrite (h, &len, sizeof(int));\t//save for later\n\t\t\tlen = QC_encode(progfuncs, numfielddefs*sizeof(QCC_ddef_t), 2, (char *)fields, h);\t//write\n\t\t\ti = SafeSeek (h, 0, SEEK_CUR);\n\t\t\tSafeSeek(h, progs.ofs_fielddefs, SEEK_SET);//seek back\n\t\t\tlen = PRLittleLong(len);\n\t\t\tSafeWrite (h, &len, sizeof(int));\t//write size.\n\t\t\tSafeSeek(h, i, SEEK_SET);\n\t\t}\n\t\telse\n\t\t\tSafeWrite (h, fields, numfielddefs*sizeof(QCC_ddef_t));\n\t\tbreak;\n\tcase PST_KKQWSV:\n\tcase PST_DEFAULT:\n#define qcc_globals16 ((QCC_ddef16_t*)qcc_globals)\n#define fields16 ((QCC_ddef16_t*)fields)\n\t\tprogs.ofs_globaldefs = SafeSeek (h, 0, SEEK_CUR);\n\t\tprogs.numglobaldefs = numglobaldefs;\n\t\tfor (i=0 ; i<numglobaldefs ; i++)\n\t\t{\n\t\t\tqcc_globals16[i].type = (unsigned short)PRLittleShort ((unsigned short)qcc_globals[i].type);\n\t\t\tqcc_globals16[i].ofs = (unsigned short)PRLittleShort ((unsigned short)qcc_globals[i].ofs);\n\t\t\tqcc_globals16[i].s_name = PRLittleLong (qcc_globals[i].s_name);\n\t\t}\n\n\t\tif (progs.blockscompressed&2)\n\t\t{\n\t\t\tSafeWrite (h, &len, sizeof(int));\t//save for later\n\t\t\tlen = QC_encode(progfuncs, numglobaldefs*sizeof(QCC_ddef16_t), 2, (char *)qcc_globals16, h);\t//write\n\t\t\ti = SafeSeek (h, 0, SEEK_CUR);\n\t\t\tSafeSeek(h, progs.ofs_globaldefs, SEEK_SET);//seek back\n\t\t\tlen = PRLittleLong(len);\n\t\t\tSafeWrite (h, &len, sizeof(int));\t//write size.\n\t\t\tSafeSeek(h, i, SEEK_SET);\n\t\t}\n\t\telse\n\t\t\tSafeWrite (h, qcc_globals16, numglobaldefs*sizeof(QCC_ddef16_t));\n\n\t\tprogs.ofs_fielddefs = SafeSeek (h, 0, SEEK_CUR);\n\t\tprogs.numfielddefs = numfielddefs;\n\n\t\tfor (i=0 ; i<numfielddefs ; i++)\n\t\t{\n\t\t\tfields16[i].type = (unsigned short)PRLittleShort ((unsigned short)fields[i].type);\n\t\t\tif (fields[i].ofs > 0xffff)\n\t\t\t{\n\t\t\t\texterns->Printf(\"Offset for field %s overflowed 16 bits.\\n\", strings+fields[i].s_name);\n\t\t\t\tfields16[i].ofs = 0;\n\t\t\t}\n\t\t\telse\n\t\t\t\tfields16[i].ofs = (unsigned short)PRLittleShort ((unsigned short)fields[i].ofs);\n\t\t\tfields16[i].s_name = PRLittleLong (fields[i].s_name);\n\t\t}\n\n\t\tif (progs.blockscompressed&4)\n\t\t{\n\t\t\tSafeWrite (h, &len, sizeof(int));\t//save for later\n\t\t\tlen = QC_encode(progfuncs, numfielddefs*sizeof(QCC_ddef16_t), 2, (char *)fields16, h);\t//write\n\t\t\ti = SafeSeek (h, 0, SEEK_CUR);\n\t\t\tSafeSeek(h, progs.ofs_fielddefs, SEEK_SET);//seek back\n\t\t\tlen = PRLittleLong(len);\n\t\t\tSafeWrite (h, &len, sizeof(int));\t//write size.\n\t\t\tSafeSeek(h, i, SEEK_SET);\n\t\t}\n\t\telse\n\t\t\tSafeWrite (h, fields16, numfielddefs*sizeof(QCC_ddef16_t));\n\t\tbreak;\n\tdefault:\n\t\texterns->Sys_Error(\"structtype error\");\n\t}\n\n\tprogs.ofs_globals = SafeSeek (h, 0, SEEK_CUR);\n\tprogs.numglobals = numpr_globals;\n\n\tfor (i=0 ; (unsigned)i<numpr_globals ; i++)\n\t\t((int *)qcc_pr_globals)[i] = PRLittleLong (((int *)qcc_pr_globals)[i]);\n\n\tif (progs.blockscompressed&32)\n\t{\n\t\tSafeWrite (h, &len, sizeof(int));\t//save for later\n\t\tlen = QC_encode(progfuncs, numpr_globals*4, 2, (char *)qcc_pr_globals, h);\t//write\n\t\ti = SafeSeek (h, 0, SEEK_CUR);\n\t\tSafeSeek(h, progs.ofs_globals, SEEK_SET);//seek back\n\t\tlen = PRLittleLong(len);\n\t\tSafeWrite (h, &len, sizeof(int));\t//write size.\n\t\tSafeSeek(h, i, SEEK_SET);\n\t}\n\telse\n\t\tSafeWrite (h, qcc_pr_globals, numpr_globals*4);\n\n/*\n\tif (types)\n\tfor (i=0 ; i<numtypeinfos ; i++)\n\t{\n\t\tif (qcc_typeinfo[i].aux_type)\n\t\t\tqcc_typeinfo[i].aux_type = (QCC_type_t*)(qcc_typeinfo[i].aux_type - qcc_typeinfo);\n\t\tif (qcc_typeinfo[i].next)\n\t\t\tqcc_typeinfo[i].next = (QCC_type_t*)(qcc_typeinfo[i].next - qcc_typeinfo);\n\t\tqcc_typeinfo[i].name = (char*)QCC_CopyDupBackString(qcc_typeinfo[i].name);\n\t}\n*/\n\tprogs.ofsfiles = 0;\n\tprogs.ofslinenums = 0;\n\tprogs.secondaryversion = 0;\n\tprogs.ofsbodylessfuncs = 0;\n\tprogs.numbodylessfuncs = 0;\n\tprogs.ofs_types = 0;\n\tprogs.numtypes = 0;\n\n\t\n\tprogs.ofsbodylessfuncs = SafeSeek (h, 0, SEEK_CUR);\n\tprogs.numbodylessfuncs = WriteBodylessFuncs(h);\n\n\tswitch(qcc_targetformat)\n\t{\n\tcase QCF_QTEST:\n\t\tprogs.version = PROG_QTESTVERSION;\n\t\tprogs.ofsfiles = WriteSourceFiles(qcc_sourcefile, h, debugtarget, false);\n\t\tbreak;\n\tdefault:\n\tcase QCF_STANDARD:\n\tcase QCF_HEXEN2:\t//urgh\n\t\tprogs.version = PROG_VERSION;\n\t\tprogs.ofsfiles = WriteSourceFiles(qcc_sourcefile, h, debugtarget, false);\n\t\tbreak;\n\tcase QCF_DARKPLACES:\n\tcase QCF_FTE:\n\tcase QCF_FTEH2:\n\tcase QCF_FTEDEBUG:\n\tcase QCF_QSS:\n\tcase QCF_UHEXEN2:\n\tcase QCF_KK7:\n\n\t\tprogs.version = PROG_EXTENDEDVERSION;\n\t\tif (outputsttype == PST_UHEXEN2)\n\t\t\tprogs.secondaryversion = PROG_SECONDARYUHEXEN2;\t//prepadded...\n\t\telse if (outputsttype == PST_KKQWSV)\n\t\t\tprogs.secondaryversion = PROG_SECONDARYKKQWSV;\t//messed up\n\t\telse if (outputsttype == PST_FTE32)\n\t\t\tprogs.secondaryversion = PROG_SECONDARYVERSION32;\t//post-extended.\n\t\telse\n\t\t{\n\t\t\tprogs.secondaryversion = PROG_SECONDARYVERSION16;\n\t\t\tif (qcc_targetformat == QCF_DARKPLACES)\n\t\t\t\tprogs.version = PROG_VERSION;\n\t\t}\n\n\t\tif (debugtarget && statement_linenums)\n\t\t{\n\t\t\tprogs.ofslinenums = SafeSeek (h, 0, SEEK_CUR);\n\t\t\tif (progs.blockscompressed&64)\n\t\t\t{\n\t\t\t\tSafeWrite (h, &len, sizeof(int));\t//save for later\n\t\t\t\tlen = QC_encode(progfuncs, numstatements*sizeof(int), 2, (char *)statement_linenums, h);\t//write\n\t\t\t\ti = SafeSeek (h, 0, SEEK_CUR);\n\t\t\t\tSafeSeek(h, progs.ofslinenums, SEEK_SET);//seek back\n\t\t\t\tlen = PRLittleLong(len);\n\t\t\t\tSafeWrite (h, &len, sizeof(int));\t//write size.\n\t\t\t\tSafeSeek(h, i, SEEK_SET);\n\t\t\t}\n\t\t\telse\n\t\t\t\tSafeWrite (h, statement_linenums, numstatements*sizeof(int));\n\t\t\tstatement_linenums = NULL;\n\t\t}\n\t\telse\n\t\t\tprogs.ofslinenums = 0;\n\n\t\tif (types)\n\t\t{\n\t\t\tprogs.ofs_types = SafeSeek (h, 0, SEEK_CUR);\n\t\t\tif (progs.blockscompressed&128)\n\t\t\t{\n\t\t\t\tSafeWrite (h, &len, sizeof(int));\t//save for later\n\t\t\t\tlen = QC_encode(progfuncs, sizeof(QCC_type_t)*numtypeinfos, 2, (char *)qcc_typeinfo, h);\t//write\n\t\t\t\ti = SafeSeek (h, 0, SEEK_CUR);\n\t\t\t\tSafeSeek(h, progs.ofs_types, SEEK_SET);//seek back#\n\t\t\t\tlen = PRLittleLong(len);\n\t\t\t\tSafeWrite (h, &len, sizeof(int));\t//write size.\n\t\t\t\tSafeSeek(h, i, SEEK_SET);\n\t\t\t}\n\t\t\telse\n\t\t\t\tSafeWrite (h, qcc_typeinfo, sizeof(QCC_type_t)*numtypeinfos);\n\t\t\tprogs.numtypes = numtypeinfos;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tprogs.ofs_types = 0;\n\t\t\tprogs.numtypes = 0;\n\t\t}\n\n\t\tprogs.ofsfiles = WriteSourceFiles(qcc_sourcefile, h, debugtarget, true);\n\t\tbreak;\n\t}\n\tqcc_sourcefile = NULL;\n\n\tif (progs.version != PROG_EXTENDEDVERSION && progs.numbodylessfuncs)\n\t\texterns->Printf (\"WARNING: progs format cannot handle extern functions\\n\");\n\n\n\tif (verbose >= VERBOSE_STANDARD)\n\t\texterns->Printf (\"%6i TOTAL SIZE\\n\", (int)SafeSeek (h, 0, SEEK_CUR));\n\n\tprogs.entityfields = pr.size_fields;\n\n\tprogs.crc = crc;\n\n\tif (flag_qccx)\n\t{\n\t\tdef = QCC_PR_GetDef(NULL, \"progs\", NULL, false, 0, 0);\t//this is for qccx support\n\t\tif (def && (def->type->type == ev_entity || (def->type->type == ev_accessor && def->type->parentclass->type == ev_entity)))\n\t\t{\n\t\t\tint size = SafeSeek (h, 0, SEEK_CUR);\n\t\t\tsize += 1;\t//the engine will add a null terminator\n\t\t\tsize = (size+15)&(~15);\t//and will allocate it on the hunk with 16-byte alignment\n\n\t\t\t//this global receives the offset from world to the start of the progs def _IN VANILLA QUAKE_.\n\t\t\t//this is a negative index due to allocation ordering with the assumption that the progs.dat was loaded on the heap directly followed by the entities.\n\t\t\t//this will NOT work in FTE, DP, QuakeForge due to entity indexes. Various other engines will likely mess up too, if they change the allocation order or sizes etc. 64bit is screwed.\n\t\t\tif (progs.blockscompressed&32)\n\t\t\t\texterns->Printf(\"unable to write value for 'entity progs'\\n\");\t//would not work anyway\n\t\t\telse\n\t\t\t{\n\t\t\t\tQCC_PR_Warning(WARN_DENORMAL, def->filen, def->s_line, \"'entity progs' is non-portable and will not work across engines nor cpus.\");\n\n\t\t\t\tif (def->initialized)\n\t\t\t\t\ti = PRLittleLong(qcc_pr_globals[def->ofs]._int);\n\t\t\t\telse\n\t\t\t\t{\t//entsize(=96)+hunk header size(=32)\n\t\t\t\t\tif (verbose >= VERBOSE_STANDARD)\n\t\t\t\t\t\texterns->Printf(\"qccx hack - 'entity progs' uninitialised. Assuming 112.\\n\");\n\t\t\t\t\ti = 112;\t//match qccx.\n\t\t\t\t}\n\t\t\t\ti = -(size + i);\n\t\t\t\ti = PRLittleLong(i);\n\t\t\t\tSafeSeek (h, progs.ofs_globals + 4 * def->ofs, SEEK_SET);\n\t\t\t\tSafeWrite (h, &i, 4);\n\t\t\t}\n\t\t}\n\t}\n\n\t// qbyte swap the header and write it out\n\tfor (i=0 ; i<sizeof(progs)/4 ; i++)\n\t\t((int *)&progs)[i] = PRLittleLong ( ((int *)&progs)[i] );\n\tSafeSeek (h, 0, SEEK_SET);\n\tSafeWrite (h, &progs, sizeof(progs));\n\n\n\tif (!SafeClose (h))\n\t{\n\t\texterns->Printf(\"%sError%s while writing output %s\\n\", col_error, col_none, destfile);\n\t\treturn false;\n\t}\n\n\n\n\n\tif (verbose >= VERBOSE_PROGRESS)\n\tswitch(qcc_targetformat)\n\t{\n\tcase QCF_QTEST:\n\t\texterns->Printf(\"Compile finished: %s (qtest format)\\n\", destfile);\n\t\tbreak;\n\tcase QCF_KK7:\n\t\texterns->Printf(\"Compile finished: %s (kk7 format)\\n\", destfile);\n\t\tbreak;\n\tcase QCF_STANDARD:\n\t\texterns->Printf(\"Compile finished: %s (id format)\\n\", destfile);\n\t\tbreak;\n\tcase QCF_HEXEN2:\n\tcase QCF_UHEXEN2:\n\t\tif (progs.version == PROG_VERSION)\n\t\t\texterns->Printf(\"Compile finished: %s (hexen2 format)\\n\", destfile);\n\t\telse\n\t\t\texterns->Printf(\"Compile finished: %s (uhexen2 format)\\n\", destfile);\n\t\tbreak;\n\tcase QCF_DARKPLACES:\n\t\texterns->Printf(\"Compile finished: %s (fte+dp format)\\n\", destfile);\n\t\tbreak;\n\tcase QCF_QSS:\n\t\texterns->Printf(\"Compile finished: %s (fte+qss format)\\n\", destfile);\n\t\tbreak;\n\tcase QCF_FTE:\n\t\texterns->Printf(\"Compile finished: %s (fte format)\\n\", destfile);\n\t\tbreak;\n\tcase QCF_FTEH2:\n\t\texterns->Printf(\"Compile finished: %s (fteh2 format)\\n\", destfile);\n\t\tbreak;\n\tcase QCF_FTEDEBUG:\n\t\texterns->Printf(\"Compile finished: %s (ftedbg format)\\n\", destfile);\n\t\tbreak;\n\tdefault:\n\t\texterns->Printf(\"Compile finished: %s\\n\", destfile);\n\t\tbreak;\n\t}\n\n\tif (statement_linenums)\n\t{\n\t\tunsigned int lnotype = *(unsigned int*)\"LNOF\";\n\t\tunsigned int version = 1;\n\t\tpbool gz = false;\n\t\twhile(1)\n\t\t{\n\t\t\tchar *ext;\n\t\t\text = strrchr(destfile, '.');\n\t\t\tif (!ext || strchr(ext, '/') || strchr(ext, '\\\\'))\n\t\t\t\tbreak;\n\t\t\tif (!stricmp(ext, \".gz\"))\n\t\t\t{\n\t\t\t\t*ext = 0;\n\t\t\t\tgz = true;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t*ext = 0;\n\t\t\tbreak;\n\t\t}\n\t\tif (strlen(destfile) < sizeof(destfile)-(4+3))\n\t\t{\n\t\t\tstrcat(destfile, \".lno\");\n\t\t\tif (gz)\n\t\t\t\tstrcat(destfile, \".gz\");\n\t\t\tif (verbose >= VERBOSE_STANDARD)\n\t\t\t\texterns->Printf(\"Writing %s for debugging\\n\", destfile);\n\t\t\th = SafeOpenWrite (destfile, 2*1024*1024);\n\t\t\tSafeWrite (h, &lnotype, sizeof(int));\n\t\t\tSafeWrite (h, &version, sizeof(int));\n\t\t\tSafeWrite (h, &numglobaldefs, sizeof(int));\n\t\t\tSafeWrite (h, &numpr_globals, sizeof(int));\n\t\t\tSafeWrite (h, &numfielddefs, sizeof(int));\n\t\t\tSafeWrite (h, &numstatements, sizeof(int));\n\t\t\tSafeWrite (h, statement_linenums, numstatements*sizeof(int));\n\t\t\tSafeClose (h);\n\t\t}\n\t}\n\n\treturn true;\n}\n\n/*\n#merge \"oldprogs\"\nwrap void() worldspawn =\n{\n\tprint(\"hello world\\n\");\n\tprior();\n};\n\nProgs merging is done by loading in an existing progs.dat and essentially appending new stuff on the end.\nThe resulting output should be the same, other than wraps (which replaces the previous function global with the new one).\n*/\nstatic void QCC_MergeStrings(char *in, unsigned int num)\n{\n\tmemcpy(strings, in, num);\n\tstrofs = num;\n}\nstatic int QCC_MergeValidateString(int str)\n{\n\tif (str < 0 || str >= strofs)\n\t\tstr = 0;\n\treturn str;\n}\nstatic void QCC_MergeFunctions(dfunction_t *in, unsigned int num)\n{\n\tnumfunctions = 0;\n\twhile(num --> 0)\n\t{\n\t\tif (in->first_statement <= 0)\n\t\t{\n\t\t\tfunctions[numfunctions].builtin = -in->first_statement;\n\t\t\tfunctions[numfunctions].code = -1;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tfunctions[numfunctions].builtin = 0;\n\t\t\tfunctions[numfunctions].code = in->first_statement;\n\t\t}\n\t\tfunctions[numfunctions].s_filed = QCC_MergeValidateString(in->s_file);\n\t\tfunctions[numfunctions].filen = strings+functions[numfunctions].s_filed;\n\t\tfunctions[numfunctions].line = 0;\n\t\tfunctions[numfunctions].name = strings+QCC_MergeValidateString(in->s_name);\n\t\tfunctions[numfunctions].parentscope = NULL;\n\t\tfunctions[numfunctions].type = NULL;\n\t\tfunctions[numfunctions].def = NULL;\n\t\tfunctions[numfunctions].firstlocal = NULL;\n\t\tfunctions[numfunctions].privatelocals = true;\n\t\tfunctions[numfunctions].merged = in;\n\t\tnumfunctions++;\n\t\tin++;\n\t}\n}\nstatic void QCC_MergeStatements16(dstatement16_t *in, unsigned int num)\n{\n\tQCC_statement_t *out = statements;\n\tnumstatements = num;\n\tfor (; num --> 0; out++, in++)\n\t{\n\t\tout->op = in->op;\n\t\tout->a.sym = NULL;\n\t\tout->a.cast = NULL;\n\t\tout->a.ofs = in->a;\n\t\tout->b.sym = NULL;\n\t\tout->b.cast = NULL;\n\t\tout->b.ofs = in->b;\n\t\tout->c.sym = NULL;\n\t\tout->c.cast = NULL;\n\t\tout->c.ofs = in->c;\n\t\tout->linenum = 0;\n\t\tif (in->op < OP_NUMREALOPS)\n\t\t{\n\t\t\tif (!pr_opcodes[in->op].type_a)\n\t\t\t\tout->a.ofs = (short)in->a;\n\t\t\tif (!pr_opcodes[in->op].type_b)\n\t\t\t\tout->b.ofs = (short)in->b;\n\t\t\tif (!pr_opcodes[in->op].type_c)\n\t\t\t\tout->c.ofs = (short)in->c;\n\t\t}\n\t}\n\t\n\tout->op = OP_DONE;\n\tout->a.ofs = 0;\n\tout->b.ofs = 0;\n\tout->c.ofs = 0;\n\tout->linenum = 0;\n\tnumstatements++;\n}\nstatic etype_t QCC_MergeFindFieldType(unsigned int ofs, const char *fldname, ddef16_t *fields, size_t numfields)\n{\n\tsize_t i;\n\tetype_t best = ev_void;\n\tfor (i = 0; i < numfields; i++)\n\t{\n\t\tif (fields[i].ofs == ofs)\n\t\t{\t//sometimes we have field unions. go for the exact name match if we can so we don't get confused over vectors/floats. otherwise just go with the first (and hope they're correctly ordered)\n\t\t\tchar *name = strings+QCC_MergeValidateString(fields[i].s_name);\n\t\t\tif (!strcmp(name, fldname))\n\t\t\t\treturn fields[i].type;\n\t\t\tif (best == ev_void)\n\t\t\t\tbest = fields[i].type;\n\t\t}\n\t}\n\treturn best;\n}\nstatic void QCC_MergeUnstrip(dfunction_t *in, unsigned int num)\n{\n\tsize_t i;\n\tchar *name;\n\tQCC_def_t *def;\n\n\t//functions may have been stripped. this results in an annoying lack of errors, and will likely confuse function wrapping...\n\t//generate a new def for each function, if it doesn't already exist.\n\t//these are probably going to be wasteful dupes, but they'll just get stripped again if they're still not used.\n\tfor (i = 0; i < num; i++)\n\t{\n\t\tif (!in[i].s_name)\n\t\t\tcontinue;\n\n\t\tname = strings+QCC_MergeValidateString(in[i].s_name);\n\n\t\tdef = QCC_PR_GetDef(NULL, name, NULL, false, 0, GDF_BASICTYPE);\n\t\tif (!def)\n\t\t{\n\t\t\tdef = QCC_PR_GetDef(type_function, name, NULL, true, 0, GDF_BASICTYPE);\n\t\t\tdef->symboldata[0].function = i;\n\t\t\tdef->initialized = true;\n\t\t\tdef->referenced = true;\n\t\t\tdef->assumedtype = true;\n\t\t}\n\t\tQCC_FreeDef(def);\n\t}\n}\nQCC_type_t *QCC_PR_FieldType (QCC_type_t *pointsto);\nstatic void QCC_MergeGlobalDefs16(ddef16_t *in, size_t num, void *values, size_t defscount, ddef16_t *fields, size_t numfields)\n{\n\tQCC_def_t *root, *def;\n\tQCC_type_t *type;\n\tetype_t evt;\n\n\tchar *name;\n\tunsigned int flags;\n\tpbool referrable;\n\n\tnumpr_globals = 0;\t//that root object will replace the normal reserved globals.\n\troot = QCC_PR_GetDef(type_void, \"\", NULL, true, 0, GDF_USED);\n\troot->symboldata = values;\n\troot->symbolsize = defscount;\n\n\tfor (; num --> 0; in++)\n\t{\n\t\tname = strings+QCC_MergeValidateString(in->s_name);\n\n\t\tflags = GDF_USED;\n\t\tif (in->type & DEF_SAVEGLOBAL)\n\t\t\tflags |= GDF_SAVED;\n\n\t\tevt = in->type&~DEF_SAVEGLOBAL;\n\t\tif (evt == ev_field)\n\t\t\tevt = QCC_MergeFindFieldType(root->symboldata[in->ofs]._int, name, fields, numfields);\n\t\tswitch(evt)\n\t\t{\n\t\tcase ev_void:\n\t\t\ttype = type_void;\n\t\t\tbreak;\n\t\tcase ev_vector:\n\t\t\ttype = type_vector;\n\t\t\tbreak;\n\t\tcase ev_float:\n\t\t\ttype = type_float;\n\t\t\tbreak;\n\t\tcase ev_string:\n\t\t\ttype = type_string;\n\t\t\tbreak;\n\t\tcase ev_entity:\n\t\t\ttype = type_entity;\n\t\t\tbreak;\n\t\tcase ev_integer:\n\t\t\ttype = type_integer;\n\t\t\tbreak;\n\t\tcase ev_function:\n\t\t\ttype = type_function;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\ttype = type_variant;\n\t\t\tbreak;\n\t\t}\n\t\tif ((in->type&~DEF_SAVEGLOBAL) == ev_field)\n\t\t{\n\t\t\ttype = QCC_PR_FieldType(type);\n\t\t\tflags |= GDF_CONST;\n\t\t}\n\n\t\treferrable = true;\t//fixme: disable if this appears to be within a function's local storage\n\t\t\n\t\tdef = QCC_PR_DummyDef(type, name, NULL, 0, root, in->ofs, referrable, flags);\n\t\tdef->initialized = 1;\n\t\tdef->referenced = true;\n\t\tdef->assumedtype = true;\n\n\t\tif (evt == ev_vector)\n\t\t{\n\t\t\tint j = 3;\n\t\t\tif ((in->type&~DEF_SAVEGLOBAL) == ev_field)\n\t\t\t{\n\t\t\t\tfor (j = 0; j < 3; j++)\n\t\t\t\t{\n\t\t\t\t\tif (in[j+1].ofs == in->ofs+j && (in[j+1].type&~DEF_SAVEGLOBAL) == ev_field && QCC_MergeFindFieldType(root->symboldata[in[j+1].ofs]._int, strings+QCC_MergeValidateString(in[j+1].s_name), fields, numfields) == ev_float)\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tfor (j = 0; j < 3; j++)\n\t\t\t\t{\n\t\t\t\t\tif (in[j+1].ofs == in->ofs+j && (in[j+1].type&~DEF_SAVEGLOBAL) == ev_float)\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tin += j;\n\t\t\tnum -= j;\n\t\t}\n\t}\n\tQCC_FreeDef(root);\n}\n\nstatic unsigned char *PDECL QCC_LoadFileHunkAlloc(void *ctx, size_t size)\n{\n\treturn (unsigned char*)qccHunkAlloc(size+1);\n}\n\n/*load a progs into the current compile state.*/\nvoid QCC_ImportProgs(const char *filename)\n{\n\tsize_t flen;\n\tdprograms_t *prog;\n\n\t//these keywords are implicitly enabled by #merge\n\tkeyword_weak = true;\n\tkeyword_wrap = true;\n\n//\tif (strofs != 0)\t\t//could be fixed with relocs\n//\t\tQCC_Error(ERR_BADEXTENSION, \"#merge used too late. It must be used before any other definitions.\");\n\tif (numstatements != 1)\t//should be easy to deal with.\n\t\tQCC_Error(ERR_BADEXTENSION, \"#merge used too late. It must be used before any other definitions.\");\n\tif (numfunctions != 1)\t//could be fixed with relocs\n\t\tQCC_Error(ERR_BADEXTENSION, \"#merge used too late. It must be used before any other definitions.\");\n\tif (numglobaldefs != 1)\t//could be fixed by inserting it properly. any already-defined defs must have their parentdef changed to union them with imported ones.\n\t\tQCC_Error(ERR_BADEXTENSION, \"#merge used too late. It must be used before any other definitions (globals).\");\n\tif (numfielddefs != 1)\t//could be fixed with relocs\n\t\tQCC_Error(ERR_BADEXTENSION, \"#merge used too late. It must be used before any other definitions (fields).\");\n\tif (numpr_globals != RESERVED_OFS)\t//not normally changed until after compiling\n\t\tQCC_Error(ERR_BADEXTENSION, \"#merge used too late. It must be used before any other definitions (regs).\");\n\n\texterns->Printf (\"\\nnote: The #merge feature is still experimental\\n\\n\");\n\t//FIXME: find overlapped locals. strip them. merge with new ones.\n\t//FIXME: find temps. strip them. you get the idea.\n\t//FIXME: find immediates. set up hash tables for them for reuse. HAH!\n\n\tprog = externs->ReadFile(filename, QCC_LoadFileHunkAlloc, NULL, &flen, false);\n\tif (!prog)\n\t{\n\t\tQCC_Error(ERR_COULDNTOPENFILE, \"Couldn't open file %s\", filename);\n\t\treturn;\n\t}\n\n\tif (prog->version == 7 && prog->secondaryversion == PROG_SECONDARYVERSION16 && !prog->blockscompressed && !prog->numtypes)\n\t\t;\n\telse if (prog->version == 7 && prog->secondaryversion == PROG_SECONDARYVERSION32 && !prog->blockscompressed && !prog->numtypes)\n\t\t;\n\telse if (prog->version != 6)\n\t{\n\t\tQCC_Error(ERR_COULDNTOPENFILE, \"Unsupported version: %s\", filename);\n\t\treturn;\n\t}\n\n\tQCC_MergeStrings(((char*)prog+prog->ofs_strings), prog->numstrings);\n\tQCC_MergeFunctions((dfunction_t*)((char*)prog+prog->ofs_functions), prog->numfunctions);\n\tpr.size_fields = prog->entityfields;\n\tif (prog->version == 7 && prog->secondaryversion == PROG_SECONDARYVERSION32)\n\t{\n//\t\tQCC_MergeStatements32((dstatement32_t*)((char*)prog+prog->ofs_statements), prog->numstatements);\n//\t\tQCC_MergeGlobalDefs32((ddef32_t*)((char*)prog+prog->ofs_globaldefs), prog->numglobaldefs, ((char*)prog)+prog->ofs_globals, prog->numglobals, (ddef16_t*)((char*)prog+prog->ofs_fielddefs), prog->numfielddefs);\n\t\tQCC_Error(ERR_COULDNTOPENFILE, \"32bit versions not supported: %s\", filename);\n\t}\n\telse\n\t{\n\t\tQCC_MergeStatements16((dstatement16_t*)((char*)prog+prog->ofs_statements), prog->numstatements);\n\t\tQCC_MergeGlobalDefs16((ddef16_t*)((char*)prog+prog->ofs_globaldefs), prog->numglobaldefs, ((char*)prog)+prog->ofs_globals, prog->numglobals, (ddef16_t*)((char*)prog+prog->ofs_fielddefs), prog->numfielddefs);\n\t}\n\tQCC_MergeUnstrip((dfunction_t*)((char*)prog+prog->ofs_functions), prog->numfunctions);\n}\n\n\n/*\n===============\nPR_String\n\nReturns a string suitable for printing (no newlines, max 60 chars length)\n===============\n*/\nstatic char *QCC_PR_String (char *string)\n{\n\tstatic char buf[80];\n\tchar\t*s;\n\n\ts = buf;\n\t*s++ = '\"';\n\twhile (string && *string)\n\t{\n\t\tif (s == buf + sizeof(buf) - 2)\n\t\t\tbreak;\n\t\tif (*string == '\\n')\n\t\t{\n\t\t\t*s++ = '\\\\';\n\t\t\t*s++ = 'n';\n\t\t}\n\t\telse if (*string == '\"')\n\t\t{\n\t\t\t*s++ = '\\\\';\n\t\t\t*s++ = '\"';\n\t\t}\n\t\telse\n\t\t\t*s++ = *string;\n\t\tstring++;\n\t\tif (s - buf > 60)\n\t\t{\n\t\t\t*s++ = '.';\n\t\t\t*s++ = '.';\n\t\t\t*s++ = '.';\n\t\t\tbreak;\n\t\t}\n\t}\n\t*s++ = '\"';\n\t*s++ = 0;\n\treturn buf;\n}\n\n\n\nstatic QCC_def_t\t*QCC_PR_DefForFieldOfs (gofs_t ofs)\n{\n\tQCC_def_t\t*d;\n\n\tfor (d=pr.def_head.next ; d ; d=d->next)\n\t{\n\t\tif (d->type->type != ev_field)\n\t\t\tcontinue;\n\t\tif (*((unsigned int *)&qcc_pr_globals[d->ofs]) == ofs)\n\t\t\treturn d;\n\t}\n\tQCC_Error (ERR_NOTDEFINED, \"PR_DefForFieldOfs: couldn't find %i\",ofs);\n\treturn NULL;\n}\n\n/*\n============\nPR_ValueString\n\nReturns a string describing *data in a type specific manner\n=============\n*/\nchar *QCC_PR_ValueString (etype_t type, void *val)\n{\n\tstatic char\tline[256];\n\tQCC_def_t\t\t*def;\n\tQCC_function_t\t*f;\n\n\tswitch (type)\n\t{\n\tcase ev_string:\n\t\tQC_snprintfz (line, sizeof(line), \"%s\", QCC_PR_String(strings + *(int *)val));\n\t\tbreak;\n\tcase ev_entity:\n\t\tQC_snprintfz (line, sizeof(line), \"entity %i\", *(int *)val);\n\t\tbreak;\n\tcase ev_function:\n\t\tf = functions + *(int *)val;\n\t\tif (!f)\n\t\t\tQC_snprintfz (line, sizeof(line), \"undefined function\");\n\t\telse\n\t\t\tQC_snprintfz (line, sizeof(line), \"%s()\", f->name);\n\t\tbreak;\n\tcase ev_field:\n\t\tdef = QCC_PR_DefForFieldOfs ( *(int *)val );\n\t\tQC_snprintfz (line, sizeof(line), \".%s\", def->name);\n\t\tbreak;\n\tcase ev_void:\n\t\tQC_snprintfz (line, sizeof(line), \"void\");\n\t\tbreak;\n\tcase ev_float:\n\t\tQC_snprintfz (line, sizeof(line), \"%5.1f\", *(float *)val);\n\t\tbreak;\n\tcase ev_integer:\n\t\tQC_snprintfz (line, sizeof(line), \"%i\", *(int *)val);\n\t\tbreak;\n\tcase ev_vector:\n\t\tQC_snprintfz (line, sizeof(line), \"'%5.1f %5.1f %5.1f'\", ((float *)val)[0], ((float *)val)[1], ((float *)val)[2]);\n\t\tbreak;\n\tcase ev_pointer:\n\t\tQC_snprintfz (line, sizeof(line), \"pointer\");\n\t\tbreak;\n\tdefault:\n\t\tQC_snprintfz (line, sizeof(line), \"bad type %i\", type);\n\t\tbreak;\n\t}\n\n\treturn line;\n}\n\n/*\n============\nPR_GlobalString\n\nReturns a string with a description and the contents of a global,\npadded to 20 field width\n============\n*/\n/*char *QCC_PR_GlobalStringNoContents (gofs_t ofs)\n{\n\tint\t\ti;\n\tQCC_def_t\t*def;\n\tvoid\t*val;\n\tstatic char\tline[128];\n\n\tval = (void *)&qcc_pr_globals[ofs];\n\tdef = pr_global_defs[ofs];\n\tif (!def)\n//\t\tError (\"PR_GlobalString: no def for %i\", ofs);\n\t\tQC_snprintfz (line, sizeof(line), \"%i(?\"\"?\"\"?)\", ofs);\n\telse\n\t\tQC_snprintfz (line, sizeof(line), \"%i(%s)\", ofs, def->name);\n\n\ti = strlen(line);\n\tfor ( ; i<16 ; i++)\n\t\tQ_strlcat (line,\" \", sizeof(line));\n\tQ_strlcat (line,\" \", sizeof(line));\n\n\treturn line;\n}\n\nchar *QCC_PR_GlobalString (gofs_t ofs)\n{\n\tchar\t*s;\n\tint\t\ti;\n\tQCC_def_t\t*def;\n\tvoid\t*val;\n\tstatic char\tline[128];\n\n\tval = (void *)&qcc_pr_globals[ofs];\n\tdef = pr_global_defs[ofs];\n\tif (!def)\n\t\treturn QCC_PR_GlobalStringNoContents(ofs);\n\tif (def->initialized && def->type->type != ev_function)\n\t{\n\t\ts = QCC_PR_ValueString (def->type->type, &qcc_pr_globals[ofs]);\n\t\tQC_snprintfz (line, sizeof(line), \"%i(%s)\", ofs, s);\n\t}\n\telse\n\t\tQC_snprintfz (line, sizeof(line), \"%i(%s)\", ofs, def->name);\n\n\ti = strlen(line);\n\tfor ( ; i<16 ; i++)\n\t\tstrcat (line,\" \");\n\tstrcat (line,\" \");\n\n\treturn line;\n}*/\n\n/*\n============\nPR_PrintOfs\n============\n*/\n/*void QCC_PR_PrintOfs (gofs_t ofs)\n{\n\texterns->Printf (\"%s\\n\",QCC_PR_GlobalString(ofs));\n}*/\n\n/*\n=================\nPR_PrintStatement\n=================\n*/\n/*void QCC_PR_PrintStatement (QCC_dstatement_t *s)\n{\n\tint\t\ti;\n\n\texterns->Printf (\"%4i : %4i : %s \", (int)(s - statements), statement_linenums[s-statements], pr_opcodes[s->op].opname);\n\ti = strlen(pr_opcodes[s->op].opname);\n\tfor ( ; i<10 ; i++)\n\t\texterns->Printf (\" \");\n\n\tif (s->op == OP_IF || s->op == OP_IFNOT)\n\t\texterns->Printf (\"%sbranch %i\",QCC_PR_GlobalString(s->a),s->b);\n\telse if (s->op == OP_GOTO)\n\t{\n\t\texterns->Printf (\"branch %i\",s->a);\n\t}\n\telse if ( (unsigned)(s->op - OP_STORE_F) < 6)\n\t{\n\t\texterns->Printf (\"%s\",QCC_PR_GlobalString(s->a));\n\t\texterns->Printf (\"%s\", QCC_PR_GlobalStringNoContents(s->b));\n\t}\n\telse\n\t{\n\t\tif (s->a)\n\t\t\texterns->Printf (\"%s\",QCC_PR_GlobalString(s->a));\n\t\tif (s->b)\n\t\t\texterns->Printf (\"%s\",QCC_PR_GlobalString(s->b));\n\t\tif (s->c)\n\t\t\texterns->Printf (\"%s\", QCC_PR_GlobalStringNoContents(s->c));\n\t}\n\texterns->Printf (\"\\n\");\n}*/\n\n\n/*\n============\nPR_PrintDefs\n============\n*/\n/*void QCC_PR_PrintDefs (void)\n{\n\tQCC_def_t\t*d;\n\n\tfor (d=pr.def_head.next ; d ; d=d->next)\n\t\tQCC_PR_PrintOfs (d->ofs);\n}*/\n\nQCC_type_t *QCC_PR_NewType (const char *name, int basictype, pbool typedefed)\n{\n\tif (numtypeinfos>= maxtypeinfos)\n\t\tQCC_Error(ERR_TOOMANYTYPES, \"Too many types\");\n\tmemset(&qcc_typeinfo[numtypeinfos], 0, sizeof(QCC_type_t));\n\tqcc_typeinfo[numtypeinfos].type = basictype;\n\tqcc_typeinfo[numtypeinfos].name = name;\n\tqcc_typeinfo[numtypeinfos].num_parms = 0;\n\tqcc_typeinfo[numtypeinfos].params = NULL;\n\tqcc_typeinfo[numtypeinfos].size = type_size[basictype];\n\tqcc_typeinfo[numtypeinfos].typedefed = typedefed;\n\tqcc_typeinfo[numtypeinfos].align = 32;\t//assume this alignment for now. some types allow tighter alignment, though mostly only in structs.\n\n\tqcc_typeinfo[numtypeinfos].filen = s_filen;\n\tqcc_typeinfo[numtypeinfos].line = pr_source_line;\n\n\tif (typedefed)\n\t\tpHash_Add(&typedeftable, name, &qcc_typeinfo[numtypeinfos], qccHunkAlloc(sizeof(bucket_t)));\n\n\tnumtypeinfos++;\n\n\treturn &qcc_typeinfo[numtypeinfos-1];\n}\n\n/*\n==============\nPR_BeginCompilation\n\ncalled before compiling a batch of files, clears the pr struct\n==============\n*/\nstatic void\tQCC_PR_BeginCompilation (void *memory, int memsize)\n{\n\textern int recursivefunctiontype;\n\tint\t\ti;\n\tchar name[16];\n\n\tpr.memory = memory;\n\tpr.max_memory = memsize;\n\n\tpr.def_tail = &pr.def_head;\n\tpr.local_tail = &pr.local_head;\n\n\tQCC_PR_ResetErrorScope();\n\tpr_scope = NULL;\n\n/*\tnumpr_globals = RESERVED_OFS;\n\n\tfor (i=0 ; i<RESERVED_OFS ; i++)\n\t\tpr_global_defs[i] = &def_void;\n*/\n\n\ttype_void = QCC_PR_NewType(\"void\", ev_void, true);\n\ttype_string = QCC_PR_NewType(flag_qcfuncs?\"string\":\"__string\", ev_string, true);\n\ttype_float = QCC_PR_NewType(\"float\", ev_float, true);\n\ttype_double = QCC_PR_NewType(\"__double\", ev_double, true);\n\ttype_vector = QCC_PR_NewType(flag_qcfuncs?\"vector\":\"__vector\", ev_vector, true);\n\ttype_entity = QCC_PR_NewType(flag_qcfuncs?\"entity\":\"__entity\", ev_entity, true);\n\ttype_field = QCC_PR_NewType(\"__field\", ev_field, false);\n\ttype_field->aux_type = type_void;\n\ttype_function = QCC_PR_NewType(\"__function\", ev_function, false);\n\ttype_function->aux_type = type_void;\n\ttype_pointer = QCC_PR_NewType(\"__pointer\", ev_pointer, false);\n\ttype_integer = QCC_PR_NewType(\"__int32\", ev_integer, true);\n\ttype_uint = QCC_PR_NewType(\"__uint32\", ev_uint, true);\n\ttype_int64 = QCC_PR_NewType(\"__int64\", ev_int64, true);\n\ttype_uint64 = QCC_PR_NewType(\"__uint64\", ev_uint64, true);\n\ttype_variant = QCC_PR_NewType(\"__variant\", ev_variant, true);\n\n\ttype_sint8 = QCC_PR_NewType(\"__int8\", ev_bitfld, true);\t\ttype_sint8 ->parentclass = type_integer;\ttype_sint8 ->size = type_sint8 ->parentclass->size; type_sint8 ->align = type_sint8 ->bits = 8;\n\ttype_uint8 = QCC_PR_NewType(\"__uint8\", ev_bitfld, true);\ttype_uint8 ->parentclass = type_uint;\t\ttype_uint8 ->size = type_uint8 ->parentclass->size; type_uint8 ->align = type_uint8 ->bits = 8;\n\ttype_sint16 = QCC_PR_NewType(\"__int16\", ev_bitfld, true);\ttype_sint16->parentclass = type_integer;\ttype_sint16->size = type_sint16->parentclass->size; type_sint16->align = type_sint16->bits = 16;\n\ttype_uint16 = QCC_PR_NewType(\"__uint16\", ev_bitfld, true);\ttype_uint16->parentclass = type_uint;\t\ttype_uint16->size = type_uint16->parentclass->size; type_uint16->align = type_uint16->bits = 16;\n\n\ttype_invalid = QCC_PR_NewType(\"invalid\", ev_void, false);\n\n\ttype_floatfield = QCC_PR_NewType(\"__fieldfloat\", ev_field, false);\n\ttype_floatfield->aux_type = type_float;\n\ttype_pointer->aux_type = QCC_PR_NewType(\"__pointeraux\", ev_float, false);\n\n\ttype_intpointer = QCC_PR_NewType(\"__intpointer\", ev_pointer, false);\n\ttype_intpointer->aux_type = type_integer;\n\ttype_floatpointer = QCC_PR_NewType(\"__floatpointer\", ev_pointer, false);\n\ttype_floatpointer->aux_type = type_float;\n\ttype_floatfunction = QCC_PR_NewType(\"__floatfunction\", ev_function, false);\n\ttype_floatfunction->aux_type = type_float;\n\n\ttype_bfloat = QCC_PR_NewType(\"__bfloat\", ev_boolean, true);\ttype_bfloat->parentclass = type_float;\t//has value 0.0 or 1.0\n\ttype_bint = QCC_PR_NewType(\"__bint\", ev_boolean, true);\t\ttype_bint->parentclass = type_integer;\t\t//has value 0   or 1\n\n\t//type_field->aux_type = type_float;\n\n//\tQCC_PR_NewType(\"_Bool\", ev_boolean, true);\n//\tQCC_PR_NewType(\"bool\", ev_boolean, true);\n//\tQCC_PR_NewType(\"__int\", ev_integer, keyword_integer?true:false);\n\n\tQCC_PR_NewType(\"variant\", ev_variant, true);\n\tif (*type_string->name != '_')\tQCC_PR_NewType(\"__string\", ev_string, true);\t//make sure some core types __string always work with a double-underscore prefix\n\tif (*type_vector->name != '_')\tQCC_PR_NewType(\"__vector\", ev_vector, true);\t//make sure some core types __string always work with a double-underscore prefix\n\tif (*type_entity->name != '_')\tQCC_PR_NewType(\"__entity\", ev_entity, true);\t//make sure some core types __string always work with a double-underscore prefix\n\tQCC_PR_NewType(\"__int\", ev_integer, true);\n\tQCC_PR_NewType(\"__uint\", ev_uint, true);\n\n\n\n\tif (output_parms)\n\t{\t//this tends to confuse the brains out of decompilers. :)\n\t\tnumpr_globals = 1;\n\t\tQCC_PR_GetDef(type_vector, \"RETURN\", NULL, true, 0, false)->referenced=true;\n\t\tfor (i = 0; i < MAX_PARMS; i++)\n\t\t{\n\t\t\tQC_snprintfz (name, sizeof(name), \"PARM%i\", i);\n\t\t\tQCC_PR_GetDef(type_vector, name, NULL, true, 0, false)->referenced=true;\n\t\t}\n\t}\n\telse\n\t{\n\t\tnumpr_globals = RESERVED_OFS;\n//\t\tfor (i=0 ; i<RESERVED_OFS ; i++)\n//\t\t\tpr_global_defs[i] = NULL;\n\t}\n\n// link the function type in so state forward declarations match proper type\n\tpr.types = NULL;\n//\ttype_function->next = NULL;\n\tpr_error_count = 0;\n\tpr_warning_count = 0;\n\trecursivefunctiontype = 0;\n\n\tQCC_PrioritiseOpcodes();\n}\n\nstatic void QCC_PR_FinishFieldDef(QCC_def_t *d)\n{\n\tint i;\n\tif (d->symboldata)\n\t\treturn;\t//nothing to finish\n\n\td->symbolsize = (d->arraysize?d->arraysize:1) * d->type->size;\n\n\tif (d->symbolheader != d)\n\t{\n\t\tQCC_PR_FinishFieldDef(d->symbolheader);\n\t\td->symboldata = d->symbolheader->symboldata + d->ofs;\n\t}\n\telse\n\t{\n\t\td->symboldata = qccHunkAlloc (d->symbolsize * sizeof(float));\n\t\tfor (i = 0; i < d->symbolsize; i++)\n\t\t\td->symboldata[i]._int = pr.size_fields++;\n\t}\n}\n\n/*\n==============\nPR_FinishCompilation\n\ncalled after all files are compiled to check for errors\nReturns false if errors were detected.\n==============\n*/\nstatic int QCC_PR_FinishCompilation (void)\n{\n\tQCC_def_t\t\t*d;\n\tQCC_type_t\t\t*t;\n\tint\terrors;\n\n\tpbool externokay = false;\n\n\terrors = false;\n\n\tif (pr_error_count)\n\t\treturn false;\n\n\tif (qcc_targetformat == QCF_FTE || qcc_targetformat == QCF_FTEDEBUG || qcc_targetformat == QCF_FTEH2)\n\t\texternokay = true;\n\n// check to make sure all functions prototyped have code\n\tfor (d=pr.def_head.next ; d ; d=d->next)\n\t{\n\t\tif (d->type->type == ev_field && !d->symboldata)\n\t\t\tQCC_PR_FinishFieldDef(d);\n\t\tif (d->type->type == ev_function && d->constant && d->symbolheader == d)// function parms are ok\n\t\t{\n\t\t\tif (d->isextern)\n\t\t\t{\n\t\t\t\tif (!externokay)\n\t\t\t\t{\n\t\t\t\t\tQCC_PR_Warning(ERR_NOFUNC, d->filen, d->s_line, \"extern is not supported with this target format\");\n\t\t\t\t\tQCC_PR_ParsePrintDef(ERR_NOFUNC, d);\n\t\t\t\t\terrors = true;\n\t\t\t\t}\n\t\t\t\tbodylessfuncs = true;\n\t\t\t}\n\t\t\tif (!d->initialized)\n\t\t\t{\n\t\t\t\tif (!strncmp(d->name, \"ArrayGet*\", 9))\n\t\t\t\t{\n\t\t\t\t\tQCC_PR_EmitArrayGetFunction(d, d->generatedfor, d->name+9);\n\t\t\t\t\tpr_scope = NULL;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (!strncmp(d->name, \"ArraySet*\", 9))\n\t\t\t\t{\n\t\t\t\t\tQCC_PR_EmitArraySetFunction(d, d->generatedfor, d->name+9);\n\t\t\t\t\tpr_scope = NULL;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (!strncmp(d->name, \"spawnfunc_\", 10))\n\t\t\t\t{\n\t\t\t\t\t//not all of these will have a class defined, as some will be regular spawn functions, so don't error on that\n\t\t\t\t\tt = QCC_TypeForName(d->name+10);\n\t\t\t\t\tif (t && t->type == ev_entity)\n\t\t\t\t\t{\n\t\t\t\t\t\tQCC_PR_EmitClassFromFunction(d, t);\n\t\t\t\t\t\tpr_scope = NULL;\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (d->unused && !d->used)\n\t\t\t\t{\n\t\t\t\t\t//d->initialized = 1;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tQCC_PR_Warning(ERR_NOFUNC, d->filen, d->s_line, \"function %s has no body\",d->name);\n\t\t\t\tQCC_PR_ParsePrintDef(ERR_NOFUNC, d);\n\t\t\t\tbodylessfuncs = true;\n\t\t\t\terrors = true;\n\t\t\t}\n\t\t}\n\t}\n\tpr_scope = NULL;\n\n\treturn !errors;\n}\n\n//=============================================================================\n\n// FIXME: byte swap?\n\n// this is a 16 bit, non-reflected CRC using the polynomial 0x1021\n// and the initial and final xor values shown below...  in other words, the\n// CCITT standard CRC used by XMODEM\n\n\n#define CRC_INIT_VALUE\t0xffff\n#define CRC_XOR_VALUE\t0x0000\n\nstatic unsigned short QCC_crctable[256] =\n{\n\t0x0000,\t0x1021,\t0x2042,\t0x3063,\t0x4084,\t0x50a5,\t0x60c6,\t0x70e7,\n\t0x8108,\t0x9129,\t0xa14a,\t0xb16b,\t0xc18c,\t0xd1ad,\t0xe1ce,\t0xf1ef,\n\t0x1231,\t0x0210,\t0x3273,\t0x2252,\t0x52b5,\t0x4294,\t0x72f7,\t0x62d6,\n\t0x9339,\t0x8318,\t0xb37b,\t0xa35a,\t0xd3bd,\t0xc39c,\t0xf3ff,\t0xe3de,\n\t0x2462,\t0x3443,\t0x0420,\t0x1401,\t0x64e6,\t0x74c7,\t0x44a4,\t0x5485,\n\t0xa56a,\t0xb54b,\t0x8528,\t0x9509,\t0xe5ee,\t0xf5cf,\t0xc5ac,\t0xd58d,\n\t0x3653,\t0x2672,\t0x1611,\t0x0630,\t0x76d7,\t0x66f6,\t0x5695,\t0x46b4,\n\t0xb75b,\t0xa77a,\t0x9719,\t0x8738,\t0xf7df,\t0xe7fe,\t0xd79d,\t0xc7bc,\n\t0x48c4,\t0x58e5,\t0x6886,\t0x78a7,\t0x0840,\t0x1861,\t0x2802,\t0x3823,\n\t0xc9cc,\t0xd9ed,\t0xe98e,\t0xf9af,\t0x8948,\t0x9969,\t0xa90a,\t0xb92b,\n\t0x5af5,\t0x4ad4,\t0x7ab7,\t0x6a96,\t0x1a71,\t0x0a50,\t0x3a33,\t0x2a12,\n\t0xdbfd,\t0xcbdc,\t0xfbbf,\t0xeb9e,\t0x9b79,\t0x8b58,\t0xbb3b,\t0xab1a,\n\t0x6ca6,\t0x7c87,\t0x4ce4,\t0x5cc5,\t0x2c22,\t0x3c03,\t0x0c60,\t0x1c41,\n\t0xedae,\t0xfd8f,\t0xcdec,\t0xddcd,\t0xad2a,\t0xbd0b,\t0x8d68,\t0x9d49,\n\t0x7e97,\t0x6eb6,\t0x5ed5,\t0x4ef4,\t0x3e13,\t0x2e32,\t0x1e51,\t0x0e70,\n\t0xff9f,\t0xefbe,\t0xdfdd,\t0xcffc,\t0xbf1b,\t0xaf3a,\t0x9f59,\t0x8f78,\n\t0x9188,\t0x81a9,\t0xb1ca,\t0xa1eb,\t0xd10c,\t0xc12d,\t0xf14e,\t0xe16f,\n\t0x1080,\t0x00a1,\t0x30c2,\t0x20e3,\t0x5004,\t0x4025,\t0x7046,\t0x6067,\n\t0x83b9,\t0x9398,\t0xa3fb,\t0xb3da,\t0xc33d,\t0xd31c,\t0xe37f,\t0xf35e,\n\t0x02b1,\t0x1290,\t0x22f3,\t0x32d2,\t0x4235,\t0x5214,\t0x6277,\t0x7256,\n\t0xb5ea,\t0xa5cb,\t0x95a8,\t0x8589,\t0xf56e,\t0xe54f,\t0xd52c,\t0xc50d,\n\t0x34e2,\t0x24c3,\t0x14a0,\t0x0481,\t0x7466,\t0x6447,\t0x5424,\t0x4405,\n\t0xa7db,\t0xb7fa,\t0x8799,\t0x97b8,\t0xe75f,\t0xf77e,\t0xc71d,\t0xd73c,\n\t0x26d3,\t0x36f2,\t0x0691,\t0x16b0,\t0x6657,\t0x7676,\t0x4615,\t0x5634,\n\t0xd94c,\t0xc96d,\t0xf90e,\t0xe92f,\t0x99c8,\t0x89e9,\t0xb98a,\t0xa9ab,\n\t0x5844,\t0x4865,\t0x7806,\t0x6827,\t0x18c0,\t0x08e1,\t0x3882,\t0x28a3,\n\t0xcb7d,\t0xdb5c,\t0xeb3f,\t0xfb1e,\t0x8bf9,\t0x9bd8,\t0xabbb,\t0xbb9a,\n\t0x4a75,\t0x5a54,\t0x6a37,\t0x7a16,\t0x0af1,\t0x1ad0,\t0x2ab3,\t0x3a92,\n\t0xfd2e,\t0xed0f,\t0xdd6c,\t0xcd4d,\t0xbdaa,\t0xad8b,\t0x9de8,\t0x8dc9,\n\t0x7c26,\t0x6c07,\t0x5c64,\t0x4c45,\t0x3ca2,\t0x2c83,\t0x1ce0,\t0x0cc1,\n\t0xef1f,\t0xff3e,\t0xcf5d,\t0xdf7c,\t0xaf9b,\t0xbfba,\t0x8fd9,\t0x9ff8,\n\t0x6e17,\t0x7e36,\t0x4e55,\t0x5e74,\t0x2e93,\t0x3eb2,\t0x0ed1,\t0x1ef0\n};\n\nstatic void QCC_CRC_Init(unsigned short *crcvalue)\n{\n\t*crcvalue = CRC_INIT_VALUE;\n}\n\nstatic void QCC_CRC_ProcessByte(unsigned short *crcvalue, pbyte data)\n{\n\t*crcvalue = ((*crcvalue << 8) ^ QCC_crctable[(*crcvalue >> 8) ^ data]) & 0xffff;\n}\n\n/*static unsigned short QCC_CRC_Value(unsigned short crcvalue)\n{\n\treturn crcvalue ^ CRC_XOR_VALUE;\n}*/\n//=============================================================================\n\n\n/*\n============\nPR_WriteProgdefs\n\nWrites the global and entity structures out\nReturns a crc of the header, to be stored in the progs file for comparison\nat load time.\n============\n*/\n/*\nchar *Sva(char *msg, ...)\n{\n\tva_list l;\n\tstatic char buf[1024];\n\n\tva_start(l, msg);\n\tQC_vsnprintf (buf,sizeof(buf)-1, msg, l);\n\tva_end(l);\n\n\treturn buf;\n}\n*/\n\n#define PROGDEFS_MAX_SIZE 16384\n//write (to file buf) and add to the crc\nstatic void Add_WithCRC(char *p, unsigned short *crc, char *file)\n{\n\tchar *s;\n\tint i = strlen(file);\n\tif (i + strlen(p)+1 >= PROGDEFS_MAX_SIZE)\n\t\treturn;\n\tfor(s=p;*s;s++,i++)\n\t{\n\t\tQCC_CRC_ProcessByte(crc, *s);\n\t\tfile[i] = *s;\n\t}\n\tfile[i]='\\0';\n}\n#define ADD_CRC(p) Add_WithCRC(p, &crc, file)\n//#define ADD(p) {char *s;int i = strlen(p);for(s=p;*s;s++,i++){QCC_CRC_ProcessByte(&crc, *s);file[i] = *s;}file[i]='\\0';}\n\nstatic void Add_CrcOnly(char *p, unsigned short *crc, char *file)\n{\n\tchar *s;\n\tfor(s=p;*s;s++)\n\t\tQCC_CRC_ProcessByte(crc, *s);\n}\n#define EAT_CRC(p) Add_CrcOnly(p, &crc, file)\n\nstatic void QCC_PR_CRCMessages(unsigned short crc)\n{\n\tswitch (crc)\n\t{\n\tcase 12923:\t//#pragma sourcefile usage\n\t\tbreak;\n\tcase 54730:\n\t\tif (verbose >= VERBOSE_STANDARD)\n\t\t\texterns->Printf(\"Recognised progs as QuakeWorld\\n\");\n\t\tbreak;\n\tcase 5927:\n\t\tif (verbose >= VERBOSE_STANDARD)\n\t\t\texterns->Printf(\"Recognised progs as NetQuake server gamecode\\n\");\n\t\tbreak;\n\n\tcase 26940:\n\t\tif (verbose >= VERBOSE_STANDARD)\n\t\t\texterns->Printf(\"Recognised progs as Quake pre-release...\\n\");\n\t\tbreak;\n\n\tcase 38488:\n\t\tif (verbose >= VERBOSE_STANDARD)\n\t\t\texterns->Printf(\"Recognised progs as original Hexen2\\n\");\n\t\tbreak;\n\tcase 26905:\n\t\tif (verbose >= VERBOSE_STANDARD)\n\t\t\texterns->Printf(\"Recognised progs as Hexen2 Mission Pack\\n\");\n\t\tbreak;\n\tcase 14046:\n\t\tif (verbose >= VERBOSE_STANDARD)\n\t\t\texterns->Printf(\"Recognised progs as Hexen2 (demo)\\n\");\n\t\tbreak;\n\n\tcase 22390: //EXT_CSQC_1\n\t\tif (verbose >= VERBOSE_STANDARD)\n\t\t\texterns->Printf(\"Recognised progs as an EXT_CSQC_1 module\\n\");\n\t\tbreak;\n\tcase 17105:\n\tcase 32199:\t//outdated ext_csqc\n\t\tQCC_PR_Warning(WARN_SYSTEMCRC2, NULL, 0, \"Recognised progs as outdated CSQC module\");\n\t\tbreak;\n\tcase 52195:\t//this is what DP requires. don't print it as the warning that it is as that would royally piss off xonotic and their use of -Werror.\n\t\tif (verbose >= VERBOSE_PROGRESS)\n\t\texterns->Printf(\"Recognised progs as DP-specific CSQC module\\n\");\n\t\tbreak;\n\tcase 10020:\n\t\tif (verbose >= VERBOSE_STANDARD)\n\t\t\texterns->Printf(\"Recognised progs as a MenuQC module\\n\");\n\t\tbreak;\n\n\tcase 32401:\n\t\tQCC_PR_Warning(WARN_SYSTEMCRC, NULL, 0, \"please update your tenebrae system defs.\");\n\t\tbreak;\n\tdefault:\n\t\tQCC_PR_Warning(WARN_SYSTEMCRC, NULL, 0, \"system defs not recognised from quake nor clones, probably buggy (sys)defs.qc\");\n\t\tbreak;\n\t}\n}\n\nstatic unsigned short QCC_PR_WriteProgdefs (char *filename)\n{\n#define ADD_ONLY(p) QC_strlcat(file, p, sizeof(file))\t//no crc (later changes)\n\tchar file[PROGDEFS_MAX_SIZE];\n\tQCC_def_t\t*d;\n\tint\tf;\n\tunsigned short\t\tcrc;\n//\tint\t\tc;\n\tpbool hassystemfield = false;\n\n\tfile[0] = '\\0';\n\n\tQCC_CRC_Init (&crc);\n\n// print global vars until the first field is defined\n\n\tADD_CRC(\"\\n/* \");\n\tif (qcc_targetformat == QCF_HEXEN2 || qcc_targetformat == QCF_UHEXEN2 || qcc_targetformat == QCF_FTEH2)\n\t\tEAT_CRC(\"generated by hcc, do not modify\");\n\telse\n\t\tEAT_CRC(\"file generated by qcc, do not modify\");\n\tADD_ONLY(\"File generated by FTEQCC, relevent for engine modding only, the generated crc must be the same as your engine expects.\");\n\tADD_CRC(\" */\\n\\ntypedef struct\");\n\tADD_ONLY(\" globalvars_s\");\n\tADD_CRC(qcva(\"\\n{\"));\n\tADD_ONLY(\"\\n\\tint ofs_null;\\n\"\n\t\t\"\\tint ofs_return[3];\\n\"\t//makes it easier with the get globals func\n\t\t\"\\tint ofs_parm0[3];\\n\"\n\t\t\"\\tint ofs_parm1[3];\\n\"\n\t\t\"\\tint ofs_parm2[3];\\n\"\n\t\t\"\\tint ofs_parm3[3];\\n\"\n\t\t\"\\tint ofs_parm4[3];\\n\"\n\t\t\"\\tint ofs_parm5[3];\\n\"\n\t\t\"\\tint ofs_parm6[3];\\n\"\n\t\t\"\\tint ofs_parm7[3];\\n\");\n\tEAT_CRC(qcva(\"\\tint\\tpad[%i];\\n\", RESERVED_OFS));\n\tfor (d=pr.def_head.next ; d ; d=d->next)\n\t{\n\t\tif (!strcmp (d->name, \"end_sys_globals\"))\n\t\t\tbreak;\n\t\tif (!*d->name)\n\t\t\tcontinue;\n//\t\tif (d->symbolheader->ofs<RESERVED_OFS)\n//\t\t\tcontinue;\n\t\tif (d->symbolheader != d)\n\t\t\tcontinue;\n\n\t\tswitch (d->type->type)\n\t\t{\n\t\tcase ev_float:\n\t\t\tADD_CRC(qcva(\"\\tfloat\\t%s;\\n\",d->name));\n\t\t\tbreak;\n\t\tcase ev_vector:\n\t\t\tADD_CRC(qcva(\"\\tvec3_t\\t%s;\\n\",d->name));\n\t\t\tif (d->deftail)\n\t\t\t\td=d->deftail;\t// skip the elements\n\t\t\tbreak;\n\t\tcase ev_string:\n\t\t\tADD_CRC(qcva(\"\\tstring_t\\t%s;\\n\",d->name));\n\t\t\tbreak;\n\t\tcase ev_function:\n\t\t\tADD_CRC(qcva(\"\\tfunc_t\\t%s;\\n\",d->name));\n\t\t\tbreak;\n\t\tcase ev_entity:\n\t\t\tADD_CRC(qcva(\"\\tint\\t%s;\\n\",d->name));\n\t\t\tbreak;\n\t\tcase ev_integer:\n\t\t\tADD_CRC(qcva(\"\\tint\\t%s;\\n\",d->name));\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tADD_CRC(qcva(\"\\tint\\t%s;\\n\",d->name));\n\t\t\tbreak;\n\t\t}\n\t}\n\tADD_CRC(\"} globalvars_t;\\n\\n\");\n\n// print all fields\n\tADD_CRC(\"typedef struct\");\n\tADD_ONLY(\" entvars_s\");\n\tADD_CRC(\"\\n{\\n\");\n\tfor (d=pr.def_head.next ; d ; d=d->next)\n\t{\n\t\tif (!strcmp (d->name, \"end_sys_fields\"))\n\t\t\tbreak;\n\n\t\tif (d->type->type != ev_field)\n\t\t\tcontinue;\n\n\t\tif (d->symbolheader != d)\n\t\t\tcontinue;\n\n\t\tswitch (d->type->aux_type->type)\n\t\t{\n\t\tcase ev_float:\n\t\t\tADD_CRC(qcva(\"\\tfloat\\t%s;\\n\",d->name));\n\t\t\tbreak;\n\t\tcase ev_vector:\n\t\t\tADD_CRC(qcva(\"\\tvec3_t\\t%s;\\n\",d->name));\n\t\t\tif (d->deftail)\n\t\t\t\td=d->deftail;\t// skip the elements\n\t\t\tbreak;\n\t\tcase ev_string:\n\t\t\tADD_CRC(qcva(\"\\tstring_t\\t%s;\\n\",d->name));\n\t\t\tbreak;\n\t\tcase ev_function:\n\t\t\tADD_CRC(qcva(\"\\tfunc_t\\t%s;\\n\",d->name));\n\t\t\tbreak;\n\t\tcase ev_entity:\n\t\t\tADD_CRC(qcva(\"\\tint\\t%s;\\n\",d->name));\n\t\t\tbreak;\n\t\tcase ev_integer:\n\t\t\tADD_CRC(qcva(\"\\tint\\t%s;\\n\",d->name));\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tADD_CRC(qcva(\"\\tint\\t%s;\\n\",d->name));\n\t\t\tbreak;\n\t\t}\n\t\thassystemfield = true;\n\t}\n\tif (!hassystemfield)\n\t\tADD_ONLY(qcva(\"\\tint\\tplaceholder_; //no system fields\\n\"));\n\tADD_CRC(\"} entvars_t;\\n\\n\");\n\n/*\n\t///temp\n\tADD_ONLY(\"//with this the crc isn't needed for fields.\\n#ifdef FIELDSSTRUCT\\nstruct fieldvars_s {\\n\\tint ofs;\\n\\tint type;\\n\\tchar *name;\\n} fieldvars[] = {\\n\");\n\tf=0;\n\tfor (d=pr.def_head.next ; d ; d=d->next)\n\t{\n\t\tif (!strcmp (d->name, \"end_sys_fields\"))\n\t\t\tbreak;\n\n\t\tif (d->type->type != ev_field)\n\t\t\tcontinue;\n\t\tif (f)\n\t\t\tADD_ONLY(\",\\n\");\n\t\tADD_ONLY(qcva(\"\\t{%i,\\t%i,\\t\\\"%s\\\"}\",G_INT(d->ofs), d->type->aux_type->type, d->name));\n\t\tf = 1;\n\t}\n\tADD_ONLY(\"\\n};\\n#endif\\n\\n\");\n\t//end temp\n*/\n\n\tADD_ONLY(qcva(\"#define PROGHEADER_CRC %i\\n\", crc));\n\n\tif (QCC_CheckParm(\"-progdefs\"))\n\t{\n\t\texterns->Printf (\"writing %s\\n\", filename);\n\t\tf = SafeOpenWrite(filename, 16384);\n\t\tSafeWrite(f, file, strlen(file));\n\t\tSafeClose(f);\n\t}\n\n\n\tif (ForcedCRC)\n\t\tcrc = ForcedCRC;\n\n\treturn crc;\n}\n\n\n/*void QCC_PrintFunction (char *name)\n{\n\tint\t\ti;\n\tQCC_dstatement_t\t*ds;\n\tQCC_dfunction_t\t\t*df;\n\n\tfor (i=0 ; i<numfunctions ; i++)\n\t\tif (!strcmp (name, strings + functions[i].s_name))\n\t\t\tbreak;\n\tif (i==numfunctions)\n\t\tQCC_Error (ERR_NOFUNC, \"No function named \\\"%s\\\"\", name);\n\tdf = functions + i;\n\n\texterns->Printf (\"Statements for function %s:\\n\", name);\n\tds = statements + df->first_statement;\n\twhile (1)\n\t{\n\t\tQCC_PR_PrintStatement (ds);\n\t\tif (!ds->op)\n\t\t\tbreak;\n\t\tds++;\n\t}\n}*/\n/*\nvoid QCC_PrintOfs(unsigned int ofs)\n{\n\tint i;\n\tbool printfunc;\n\tQCC_dstatement_t\t*ds;\n\tQCC_dfunction_t\t\t*df;\n\n\tfor (i=0 ; i<numfunctions ; i++)\n\t{\n\t\tdf = functions + i;\n\t\tds = statements + df->first_statement;\n\t\tprintfunc = false;\n\t\twhile (1)\n\t\t{\n\t\t\tif (!ds->op)\n\t\t\t\tbreak;\n\t\t\tif (ds->a == ofs || ds->b == ofs || ds->c == ofs)\n\t\t\t{\n\t\t\t\tQCC_PR_PrintStatement (ds);\n\t\t\t\tprintfunc = true;\n\t\t\t}\n\t\t\tds++;\n\t\t}\n\t\tif (printfunc)\n\t\t{\n\t\t\tQCC_PrintFunction(strings + functions[i].s_name);\n\t\t\texterns->Printf(\" \\n \\n\");\n\t\t}\n\t}\n\n}\n*/\n/*\n==============================================================================\n\nDIRECTORY COPYING / PACKFILE CREATION\n\n==============================================================================\n*/\n\nstatic packfile_t\tpfiles[4096], *pf;\nstatic int\t\t\tpackhandle;\nstatic int\t\t\tpackbytes;\n\n/*\n===========\nPackFile\n\nCopy a file into the pak file\n===========\n*/\nstatic void QCC_PackFile (char *src, char *name)\n{\n\tsize_t\tremaining;\n#if 1\n\tchar\t*f;\n#else\n\tint\t\tin;\n\tint\t\tcount;\n\tchar\tbuf[4096];\n#endif\n\n\n\tif ( (pbyte *)pf - (pbyte *)pfiles > sizeof(pfiles) )\n\t\tQCC_Error (ERR_TOOMANYPAKFILES, \"Too many files in pak file\");\n\n#if 1\n\tf = FS_ReadToMem(src, &remaining);\n\tif (!f)\n\t{\n\t\texterns->Printf (\"%64s : %7s\\n\", name, \"\");\n//\t\tQCC_Error(\"Failed to open file %s\", src);\n\t\treturn;\n\t}\n\n\tpf->filepos = PRLittleLong (SafeSeek (packhandle, 0, SEEK_CUR));\n\tpf->filelen = PRLittleLong (remaining);\n\tstrcpy (pf->name, name);\n\texterns->Printf (\"%64s : %7u\\n\", pf->name, (unsigned int)remaining);\n\n\tpackbytes += remaining;\n\n\tSafeWrite (packhandle, f, remaining);\n\n\tFS_CloseFromMem(f);\n#else\n\tin = SafeOpenRead (src);\n\tremaining = filelength (in);\n\n\tpf->filepos = PRLittleLong (lseek (packhandle, 0, SEEK_CUR));\n\tpf->filelen = PRLittleLong (remaining);\n\tstrcpy (pf->name, name);\n\texterns->Printf (\"%64s : %7u\\n\", pf->name, (unsigned int)remaining);\n\n\tpackbytes += remaining;\n\n\twhile (remaining)\n\t{\n\t\tif (remaining < sizeof(buf))\n\t\t\tcount = remaining;\n\t\telse\n\t\t\tcount = sizeof(buf);\n\t\tSafeRead (in, buf, count);\n\t\tSafeWrite (packhandle, buf, count);\n\t\tremaining -= count;\n\t}\n\n\tclose (in);\n#endif\n\tpf++;\n}\n\n\n/*\n===========\nCopyFile\n\nCopies a file, creating any directories needed\n===========\n*/\nstatic void QCC_CopyFile (char *src, char *dest)\n{\n\t/*\n\tint\t\tin, out;\n\tint\t\tremaining, count;\n\tchar\tbuf[4096];\n\n\tprint (\"%s to %s\\n\", src, dest);\n\n\tin = SafeOpenRead (src);\n\tremaining = filelength (in);\n\n\tQCC_CreatePath (dest);\n\tout = SafeOpenWrite (dest, remaining+10);\n\n\twhile (remaining)\n\t{\n\t\tif (remaining < sizeof(buf))\n\t\t\tcount = remaining;\n\t\telse\n\t\t\tcount = sizeof(buf);\n\t\tSafeRead (in, buf, count);\n\t\tSafeWrite (out, buf, count);\n\t\tremaining -= count;\n\t}\n\n\tclose (in);\n\tSafeClose (out);\n\t*/\n}\n\n\n/*\n===========\nCopyFiles\n===========\n*/\n\nstatic void _QCC_CopyFiles (int blocknum, int copytype, char *srcdir, char *destdir)\n{\n\tint i;\n\tint\t\tdirlen;\n\tunsigned short\t\tcrc;\n\tpackheader_t\theader;\n\tchar\tname[1024];\n\tchar\tsrcfile[1024], destfile[1024];\n\n\tpackbytes = 0;\n\n\tif (copytype == 2)\n\t{\n\t\tpf = pfiles;\n\t\tpackhandle = SafeOpenWrite (destdir, 1024*1024);\n\t\tSafeWrite (packhandle, &header, sizeof(header));\n\t}\n\n\tfor (i=0 ; i<numsounds ; i++)\n\t{\n\t\tif (precache_sound[i].block != blocknum)\n\t\t\tcontinue;\n\t\tsprintf (srcfile,\"%s%s\",srcdir, precache_sound[i].name);\n\t\tsprintf (destfile,\"%s%s\",destdir, precache_sound[i].name);\n\t\tif (copytype == 1)\n\t\t\tQCC_CopyFile (srcfile, destfile);\n\t\telse\n\t\t\tQCC_PackFile (srcfile, precache_sound[i].name);\n\t}\n\tfor (i=0 ; i<nummodels ; i++)\n\t{\n\t\tif (precache_model[i].block != blocknum)\n\t\t\tcontinue;\n\t\tsprintf (srcfile,\"%s%s\",srcdir, precache_model[i].name);\n\t\tsprintf (destfile,\"%s%s\",destdir, precache_model[i].name);\n\t\tif (copytype == 1)\n\t\t\tQCC_CopyFile (srcfile, destfile);\n\t\telse\n\t\t\tQCC_PackFile (srcfile, precache_model[i].name);\n\t}\n\tfor (i=0 ; i<numtextures ; i++)\n\t{\n\t\tif (precache_texture[i].block != blocknum)\n\t\t\tcontinue;\n\n\t\t{\n\t\t\tsprintf (name, \"%s\", precache_texture[i].name);\n\t\t\tsprintf (srcfile,\"%s%s\",srcdir, name);\n\t\t\tsprintf (destfile,\"%s%s\",destdir, name);\n\t\t\tif (copytype == 1)\n\t\t\t\tQCC_CopyFile (srcfile, destfile);\n\t\t\telse\n\t\t\t\tQCC_PackFile (srcfile, name);\n\t\t}\n\t\t{\n\t\t\tsprintf (name, \"%s.bmp\", precache_texture[i].name);\n\t\t\tsprintf (srcfile,\"%s%s\",srcdir, name);\n\t\t\tsprintf (destfile,\"%s%s\",destdir, name);\n\t\t\tif (copytype == 1)\n\t\t\t\tQCC_CopyFile (srcfile, destfile);\n\t\t\telse\n\t\t\t\tQCC_PackFile (srcfile, name);\n\t\t}\n\t\t{\n\t\t\tsprintf (name, \"%s.tga\", precache_texture[i].name);\n\t\t\tsprintf (srcfile,\"%s%s\",srcdir, name);\n\t\t\tsprintf (destfile,\"%s%s\",destdir, name);\n\t\t\tif (copytype == 1)\n\t\t\t\tQCC_CopyFile (srcfile, destfile);\n\t\t\telse\n\t\t\t\tQCC_PackFile (srcfile, name);\n\t\t}\n\t}\n\tfor (i=0 ; i<numfiles ; i++)\n\t{\n\t\tif (precache_file[i].block != blocknum)\n\t\t\tcontinue;\n\t\tsprintf (srcfile,\"%s%s\",srcdir, precache_file[i].name);\n\t\tsprintf (destfile,\"%s%s\",destdir, precache_file[i].name);\n\t\tif (copytype == 1)\n\t\t\tQCC_CopyFile (srcfile, destfile);\n\t\telse\n\t\t\tQCC_PackFile (srcfile, precache_file[i].name);\n\t}\n\n\tif (copytype == 2)\n\t{\n\t\theader.id[0] = 'P';\n\t\theader.id[1] = 'A';\n\t\theader.id[2] = 'C';\n\t\theader.id[3] = 'K';\n\t\tdirlen = (pbyte *)pf - (pbyte *)pfiles;\n\t\theader.dirofs = PRLittleLong(SafeSeek (packhandle, 0, SEEK_CUR));\n\t\theader.dirlen = PRLittleLong(dirlen);\n\n\t\tSafeWrite (packhandle, pfiles, dirlen);\n\n\t\tSafeSeek (packhandle, 0, SEEK_SET);\n\t\tSafeWrite (packhandle, &header, sizeof(header));\n\t\tSafeClose (packhandle);\n\n\t// do a crc of the file\n\t\tQCC_CRC_Init (&crc);\n\t\tfor (i=0 ; i<dirlen ; i++)\n\t\t\tQCC_CRC_ProcessByte (&crc, ((pbyte *)pfiles)[i]);\n\n\t\ti = pf - pfiles;\n\t\texterns->Printf (\"%i files packed in %i bytes (%i crc)\\n\",i, packbytes, crc);\n\t}\n}\n\nstatic void QCC_CopyFiles (void)\n{\n\tchar *s;\n\tchar\tsrcdir[1024], destdir[1024];\n\tint\t\tp;\n\n\tif (verbose)\n\t{\n\t\tif (numsounds > 0)\n\t\t\texterns->Printf (\"%3i unique precache_sounds\\n\", numsounds);\n\t\tif (nummodels > 0)\n\t\t\texterns->Printf (\"%3i unique precache_models\\n\", nummodels);\n\t\tif (numtextures > 0)\n\t\t\texterns->Printf (\"%3i unique precache_textures\\n\", numtextures);\n\t\tif (numfiles > 0)\n\t\t\texterns->Printf (\"%3i unique precache_files\\n\", numfiles);\n\t}\n\n\tp = QCC_CheckParm (\"-copy\");\n\tif (p && p < myargc-2)\n\t{\t// create a new directory tree\n\n\t\tstrcpy (srcdir, myargv[p+1]);\n\t\tstrcpy (destdir, myargv[p+2]);\n\t\tif (srcdir[strlen(srcdir)-1] != '/')\n\t\t\tstrcat (srcdir, \"/\");\n\t\tif (destdir[strlen(destdir)-1] != '/')\n\t\t\tstrcat (destdir, \"/\");\n\n\t\t_QCC_CopyFiles(0, 1, srcdir, destdir);\n\t\treturn;\n\t}\n\n\tfor ( p = 0; p < countof(QCC_Packname); p++)\n\t{\n\t\ts = QCC_Packname[p];\n\t\tif (!*s)\n\t\t\tcontinue;\n\t\tstrcpy(destdir, s);\n\t\tstrcpy(srcdir, \"\");\n\t\t_QCC_CopyFiles(p+1, 2, srcdir, destdir);\n\t}\n\treturn;\n\t/*\n\n\tblocknum = 1;\n\tp = QCC_CheckParm (\"-pak2\");\n\tif (p && p <myargc-2)\n\t\tblocknum = 2;\n\telse\n\t\tp = QCC_CheckParm (\"-pak\");\n\tif (p && p < myargc-2)\n\t{\t// create a pak file\n\t\tstrcpy (srcdir, myargv[p+1]);\n\t\tstrcpy (destdir, myargv[p+2]);\n\t\tif (srcdir[strlen(srcdir)-1] != '/')\n\t\t\tstrcat (srcdir, \"/\");\n\t\tDefaultExtension (destdir, \".pak\");\n\n\n\t\tcopytype = 2;\n\n\t\t_QCC_CopyFiles(blocknum, copytype, srcdir, destdir);\n\t}\n\t*/\n}\n\n//============================================================================\n\n#ifdef _WIN32\n#define WINDOWSARG(x) x\n#else\n#define WINDOWSARG(x) false\n#endif\n\npbool QCC_RegisterSourceFile(const char *filename)\n{\n\tint i;\n\tfor (i = 0; i < numsourcefiles; i++)\n\t{\n\t\tif (!strcmp(sourcefileslist[i], filename))\n\t\t\treturn true;\n\t}\n\tif (numsourcefiles < MAXSOURCEFILESLIST)\n\t{\n\t\tstrcpy(sourcefileslist[numsourcefiles++], filename);\n\t\treturn true;\n\t}\n\treturn false;\n}\n\nstatic void QCC_PR_CommandLinePrecompilerOptions (void)\n{\n\tint             i, j, p;\n\tconst char *name, *val;\n\tpbool werror = false;\n\tqcc_nopragmaoptimise = false;\n\n\tfor (i = 1;i<myargc;i++)\n\t{\n\t\tif ( !strcmp(myargv[i], \"-v\") )\n\t\t\tverbose++;\t//verbose\n\t\telse if ( !strcmp(myargv[i], \"-srcfile\") )\n\t\t{\n\t\t\tif (++i == myargc)\n\t\t\t\tbreak;\n\t\t\tif (!QCC_RegisterSourceFile(myargv[i]))\n\t\t\t\tQCC_PR_Warning(WARN_BADPARAMS, \"cmdline\", 0, \"too many -srcfile arguments\");\n\t\t}\n\t\telse if ( !strcmp(myargv[i], \"-src\") )\n\t\t{\n\t\t\ti++;\n\t\t\tstrcpy (qccmsourcedir, myargv[i]);\n\t\t\tstrcat (qccmsourcedir, \"/\");\n\t\t}\n\t\telse if ( !strcmp(myargv[i], \"-o\") )\n\t\t{\t//explicit output file\n\t\t\ti++;\n\t\t\tstrcpy(destfile, myargv[i]);\n\t\t\tif (!destfile_explicit)\n\t\t\t\tverbose--;\n\t\t\tdestfile_explicit = true;\n\t\t}\n\t\telse if ( !strncmp(myargv[i], \"-o\", 2) )\n\t\t{\t//explicit output file\n\t\t\tstrcpy(destfile, myargv[i]+2);\n\t\t\tif (!destfile_explicit)\n\t\t\t\tverbose--;\n\t\t\tdestfile_explicit = true;\n\t\t}\n\t\telse if ( !strcmp(myargv[i], \"-qc\") )\n\t\t\tQCC_PR_Warning(WARN_BADPARAMS, \"cmdline\", 0, \"Argument %s is experimental\", myargv[i]);\t//compile without linking. output cannot be read by engines.\n\t\telse if ( !strcmp(myargv[i], \"-E\") )\n\t\t\tQCC_PR_Warning(WARN_BADPARAMS, \"cmdline\", 0, \"Argument %s is experimental\", myargv[i]);\t//preprocess only\n\t\telse if ( !strcmp(myargv[i], \"-progdefs\") )\n\t\t\t;\t//write progdefs.h\n\t\telse if ( !strcmp(myargv[i], \"-copy\") )\n\t\t\t;\t//copy files / write pak files\n\t\telse if ( !strcmp(myargv[i], \"-bspmodels\") )\n\t\t\tQCC_PR_Warning(WARN_BADPARAMS, \"cmdline\", 0, \"Argument %s is not supported\", myargv[i]);\n\t\telse if ( !strcmp(myargv[i], \"-h2\") || !strcmp(myargv[i], \"-fteh2\")  || !strcmp(myargv[i], \"-fte\") || !strcmp(myargv[i], \"-dp\")  )\n\t\t\t;\t//various targets\n\t\telse if ( !strcmp(myargv[i], \"-pak\") || !strcmp(myargv[i], \"-pak2\") )\n\t\t\tQCC_PR_Warning(WARN_BADPARAMS, \"cmdline\", 0, \"Argument %s is not supported\", myargv[i]);\n\t\telse\n\n\t\t//compiler constant\n\t\tif ( !strncmp(myargv[i], \"-D\", 2) )\n\t\t{\n\t\t\tname = myargv[i] + 2;\n\t\t\tval = strchr(name, '=');\n\t\t\tif (val)\n\t\t\t{\n\t\t\t\tchar *t = malloc(val-name+1);\n\t\t\t\tmemcpy(t, name, val-name);\n\t\t\t\tt[val-name] = 0;\n\t\t\t\tval++;\n\t\t\t\tQCC_PR_DefineName(t, val);\n\t\t\t\tfree(t);\n\t\t\t}\n\t\t\telse\n\t\t\t\tQCC_PR_DefineName(name, NULL);\n\t\t}\n\t\telse if ( !strncmp(myargv[i], \"-I\", 2) )\n\t\t{\n\t\t\tname = myargv[i] + 2;\n\t\t\tif (!*name && i+1<myargc)\n\t\t\t\tname = myargv[++i];\n\n\t\t\tQCC_PR_AddIncludePath(name);\n\t\t}\n\n\t\t//optimisations.\n\t\telse if ( !strnicmp(myargv[i], \"-O\", 2) || WINDOWSARG(!strnicmp(myargv[i], \"/O\", 2)) )\n\t\t{\n\t\t\tqcc_nopragmaoptimise = true;\n\t\t\tp = 0;\n\t\t\tif (myargv[i][2] >= '0' && myargv[i][2] <= '3')\n\t\t\t{\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tconst char *a = myargv[i]+2;\n\t\t\t\tpbool state = true;\n\t\t\t\tif (!strnicmp(a, \"no-\", 3))\n\t\t\t\t{\n\t\t\t\t\ta+=3;\n\t\t\t\t\tstate = false;\n\t\t\t\t}\n\n\t\t\t\tfor (p = 0; optimisations[p].enabled; p++)\n\t\t\t\t{\n\t\t\t\t\tif ((*optimisations[p].abbrev && !stricmp(a, optimisations[p].abbrev)) || !stricmp(a, optimisations[p].fullname))\n\t\t\t\t\t{\n\t\t\t\t\t\t*optimisations[p].enabled = state;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (!optimisations[p].enabled)\n\t\t\t\t{\n\t\t\t\t\tif (!stricmp(a, \"overlap-locals\"))\n\t\t\t\t\t\topt_locals_overlapping = state;\n\t\t\t\t\telse\n\t\t\t\t\t\tQCC_PR_Warning(WARN_BADPARAMS, \"cmdline\", 0, \"Unrecognised optimisation parameter (%s)\", myargv[i]);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\telse if ( !strnicmp(myargv[i], \"-K\", 2) || WINDOWSARG(!strnicmp(myargv[i], \"/K\", 2)) )\n\t\t{\n\t\t\tp = 0;\n\t\t\tif (!strnicmp(myargv[i]+2, \"no-\", 3))\n\t\t\t{\n\t\t\t\tfor (p = 0; compiler_flag[p].enabled; p++)\n\t\t\t\t\tif (!stricmp(myargv[i]+5, compiler_flag[p].abbrev))\n\t\t\t\t\t{\n\t\t\t\t\t\t*compiler_flag[p].enabled = false;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tfor (p = 0; compiler_flag[p].enabled; p++)\n\t\t\t\t\tif (!stricmp(myargv[i]+2, compiler_flag[p].abbrev))\n\t\t\t\t\t{\n\t\t\t\t\t\t*compiler_flag[p].enabled = true;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (!compiler_flag[p].enabled)\n\t\t\t\tQCC_PR_Warning(WARN_BADPARAMS, \"cmdline\", 0, \"Unrecognised keyword parameter (%s)\", myargv[i]);\n\t\t}\n\t\telse if ( !strnicmp(myargv[i], \"-std=\", 5))\n\t\t{\n\t\t\tfor (p = 0; compiler_flag[p].enabled; p++)\n\t\t\t{\n\t\t\t\tif (compiler_flag[p].flags & FLAG_ASDEFAULT)\n\t\t\t\t\t*compiler_flag[p].enabled = true;\n\t\t\t\telse\n\t\t\t\t\t*compiler_flag[p].enabled = false;\n\t\t\t}\n\t\t\tif (!stricmp(myargv[i]+5, \"C\") || !stricmp(myargv[i]+5, \"c++\") ||!stricmp(myargv[i]+5, \"c89\") || !stricmp(myargv[i]+5, \"c90\") || !stricmp(myargv[i]+5, \"c99\") || !stricmp(myargv[i]+5, \"c11\") || !stricmp(myargv[i]+5, \"c17\"))\n\t\t\t{\t//set up for greatest C compatibility... variations from C are bugs, not features.\n\t\t\t\tkeyword_asm = false;\n\t\t\t\tkeyword_break = keyword_continue = keyword_for = keyword_goto = keyword_const = keyword_extern = keyword_static = keyword_auto = true;\n\t\t\t\tkeyword_switch = keyword_case = keyword_default = true;\n\t\t\t\tkeyword_accessor = keyword_class = keyword_var = keyword_inout = keyword_optional = keyword_state = keyword_inline = keyword_nosave = keyword_shared = keyword_noref = keyword_unused = keyword_used = keyword_local = keyword_nonstatic = keyword_ignore = keyword_strip = false;\n\n\t\t\t\tkeyword_vector = keyword_entity = keyword_float = keyword_string = false;\t//not to be confused with actual types, but rather the absence of the keyword local.\n\t\t\t\tkeyword_integer = keyword_enumflags = false;\n\t\t\t\tkeyword_float = keyword_int = keyword_typedef = keyword_struct = keyword_union = keyword_enum = true;\n\t\t\t\tkeyword_double = keyword_long = keyword_short = keyword_char = keyword_signed = keyword_unsigned = keyword_register = keyword_volatile = true;\n\t\t\t\tkeyword_thinktime = keyword_until = keyword_loop = false;\n\n\t\t\t\tflag_ILP32 = true;\t\t\t//C code generally expects intptr_t==size_t==long, we'll get better compat if we don't give it surprises.\n\t\t\t\topt_logicops = true;\t\t//early out like C.\n\t\t\t\tflag_assumevar = true;\t\t//const only if explicitly const.\n\t\t\t\tpr_subscopedlocals = true;\t//locals shadow other locals rather than being the same one.\n\t\t\t\tflag_cpriority = true;\t\t//fiddle with operator precedence.\n\t\t\t\tflag_assume_integer = true;\t//unqualified numeric constants are assumed to be ints, consistent with C.\n\t\t\t\tflag_assume_double = true;\t//and any immediates with a decimal points are assumed to be doubles, consistent with C.\n\t\t\t\tflag_qcfuncs = false;\t\t//there's a few parsing quirks where our attempt to parse qc functions will misparse valid C.\n\t\t\t\tflag_macroinstrings = false;//hacky preqcc hack.\n\t\t\t\tflag_boundchecks = false;\t//nope... not C's style.\n\t\t\t\tflag_iffloat = true;\t\t//C code won't like this bug (and is mostly ints anyway, so emulation shouldn't be quite so expensive).\n\n\t\t\t\tqccwarningaction[WARN_UNINITIALIZED] = WA_WARN;\t\t//C doesn't like that, might as well warn here too.\n\t\t\t\tqccwarningaction[WARN_TOOMANYPARAMS] = WA_ERROR;\t//too many args to function is weeeeird.\n\t\t\t\tqccwarningaction[WARN_TOOFEWPARAMS] = WA_ERROR;\t\t//missing args should be fatal.\n\t\t\t\tqccwarningaction[WARN_ASSIGNMENTTOCONSTANT] = WA_ERROR;\t\t//const is const. at least its not const by default.\n\t\t\t\tqccwarningaction[WARN_SAMENAMEASGLOBAL] = WA_IGNORE;\t\t//shadowing of globals.\n\t\t\t\tqccwarningaction[WARN_OCTAL_IMMEDIATE] = WA_IGNORE;\t\t\t//0400!=400 is normal for C code.\n\t\t\t\tqccwarningaction[WARN_POINTLESSSTATEMENT] = WA_IGNORE;\t\t//common in C.\n\n\n\t\t\t\tQCC_PR_DefineName(\"__STDC_HOSTED__\", \"0\");\n\n\t\t\t\tif (!stricmp(myargv[i]+5, \"c++\"))\n\t\t\t\t{\n\t\t\t\t\tkeyword_class = /*keyword_new =*/ keyword_inline = true;\n\t\t\t\t\tQCC_PR_DefineName(\"__cplusplus\", NULL);\n\t\t\t\t\tval = NULL;\n\t\t\t\t}\n\t\t\t\telse if (!stricmp(myargv[i]+5, \"c89\") || !stricmp(myargv[i]+5, \"c90\"))\n\t\t\t\t\tval = \"199409L\";\t//it was ammended, apparently.\n\t\t\t\telse if (!stricmp(myargv[i]+5, \"c99\"))\n\t\t\t\t\tval = \"199901L\";\n\t\t\t\telse if (!stricmp(myargv[i]+5, \"c11\"))\n\t\t\t\t\tval = \"201112L\";\n\t\t\t\telse if (!stricmp(myargv[i]+5, \"c17\"))\n\t\t\t\t\tval = \"201710L\";\n\t\t\t\telse if (!stricmp(myargv[i]+5, \"c23\"))\n\t\t\t\t\tval = \"202311L\";\n\t\t\t\telse\n\t\t\t\t\tval = NULL;\n\t\t\t\tif (val)\n\t\t\t\t\tQCC_PR_DefineName(\"__STDC_VERSION__\", val);\n\t\t\t\tQCC_PR_DefineName(\"__STDC_NO_THREADS__\", val);\t//added c11\n\t\t\t\tQCC_PR_DefineName(\"__STDC_NO_ATOMICS__\", val);\t//added c11\n\t\t\t\tQCC_PR_DefineName(\"__STDC_NO_COMPLEX__\", val);\t//optional in c11 (complex mandatory in c99).\n\t\t\t\tQCC_PR_DefineName(\"__STDC_NO_VLA__\", val);\t\t//optional in c11 (vla mandatory in c99). we support vla only for the outer array.\n\t\t\t}\n\t\t\telse if (!strcmp(myargv[i]+5, \"qccx\"))\n\t\t\t{\n\t\t\t\tflag_qccx = true;\t//fixme: extra stuff\n\t\t\t\tqccwarningaction[WARN_DENORMAL] = WA_IGNORE;\t//this is just too spammy\n\t\t\t\tqccwarningaction[WARN_LAXCAST] = WA_IGNORE;\t//more plausable, but still too spammy. easier to fix at least.\n\t\t\t}\n\t\t\telse if (!strcmp(myargv[i]+5, \"preqcc\"))\n\t\t\t{\n\t\t\t\tflag_hashonly = true;\n\t\t\t\tflag_macroinstrings = true;\n\t\t\t}\n\t\t\telse if (!strcmp(myargv[i]+5, \"reacc\"))\n\t\t\t{\n\t\t\t\tflag_acc = true;\n\t\t\t\tflag_macroinstrings = false;\n\t\t\t}\n\t\t\telse if (!strcmp(myargv[i]+5, \"frikqcc\"))\n\t\t\t{\n\t\t\t\tkeyword_state = true;\n\t\t\t\tflag_macroinstrings = false;\n\t\t\t}\n\t\t\telse if (!strcmp(myargv[i]+5, \"fteqcc\"))\n\t\t\t\t;\t//as above, its the default.\n\t\t\telse if (!strcmp(myargv[i]+5, \"qcc\") || !strcmp(myargv[i]+5, \"id\"))\n\t\t\t{\n\t\t\t\tflag_ifvector = flag_vectorlogic = false;\n\n\t\t\t\tkeyword_asm = keyword_break = keyword_continue = keyword_for = keyword_goto = false;\n\t\t\t\tkeyword_const = keyword_var = keyword_inout = keyword_optional = keyword_state = keyword_inline = keyword_nosave = keyword_extern = keyword_shared = keyword_noref = keyword_unused = keyword_used = keyword_static = keyword_nonstatic = keyword_ignore = keyword_strip = false;\n\t\t\t\tkeyword_switch = keyword_case = keyword_default = keyword_accessor = keyword_class = keyword_const = false;\n\n\t\t\t\tkeyword_vector = keyword_entity = keyword_float = keyword_string = false;\t//not to be confused with actual types, but rather the absence of the keyword local.\n\t\t\t\tkeyword_int = keyword_integer = keyword_typedef = keyword_struct = keyword_union = keyword_enum = keyword_enumflags = false;\n\t\t\t\tkeyword_thinktime = keyword_until = keyword_loop = false;\n\t\t\t\tkeyword_wrap = keyword_weak = false;\n\n\t\t\t\tqccwarningaction[WARN_PARAMWITHNONAME] = WA_ERROR;\n\t\t\t}\n\t\t\telse if (!strcmp(myargv[i]+5, \"hcc\") || !strcmp(myargv[i]+5, \"hexenc\"))\n\t\t\t{\n\t\t\t\tqcc_framerate = 20;\n\t\t\t\tflag_ifvector = flag_vectorlogic = false;\n\t\t\t\tflag_macroinstrings = false;\n\n\t\t\t\tkeyword_asm = keyword_continue = keyword_for = keyword_goto = false;\n\t\t\t\tkeyword_const = keyword_var = keyword_inout = keyword_optional = keyword_state = keyword_inline = keyword_nosave = keyword_extern = keyword_shared = keyword_noref = keyword_unused = keyword_used = keyword_static = keyword_nonstatic = keyword_ignore = keyword_strip = false;\n\t\t\t\tkeyword_accessor = keyword_class = keyword_const = false;\n\n\t\t\t\tkeyword_vector = keyword_entity = keyword_float = keyword_string = false;\t//not to be confused with actual types, but rather the absence of the keyword local.\n\t\t\t\tkeyword_int = keyword_integer = keyword_typedef = keyword_struct = keyword_union = keyword_enum = keyword_enumflags = false;\n\t\t\t\tkeyword_wrap = keyword_weak = false;\n\n\t\t\t\tkeyword_thinktime = keyword_until = keyword_loop = true;\n\t\t\t\tkeyword_switch = keyword_case = keyword_default = keyword_break = true;\n\t\t\t}\n\t\t\telse if (!strcmp(myargv[i]+5, \"gmqcc\"))\n\t\t\t{\n\t\t\t\tflag_ifvector = flag_vectorlogic = true;\n\t\t\t\tflag_dblstarexp = flag_attributes = flag_assumevar = pr_subscopedlocals = flag_cpriority = flag_allowuninit = true;\n\t\t\t\tflag_boundchecks = false;\t//gmqcc doesn't support dynamic bound checks, so xonotic is buggy shite. we don't want to generate code that will crash.\n\t\t\t\tflag_macroinstrings = false;\n\t\t\t\tflag_reciprocalmaths = true; //optimise x/y to x*(1/y) in constants.\n\t\t\t\tflag_undefwordsize = true;\t//assume we're targetting DP and go into lame mode.\n\t\t\t\topt_logicops = true;\n\n\t\t\t\t//we have to disable some of these warnings, because xonotic insists on using -Werror. use -Wextra to override.\n\t\t\t\tqccwarningaction[WARN_NOTREFERENCEDCONST] = WA_IGNORE;\t//gmqcc doesn't warn about function prototypes without any names\n\t\t\t\tqccwarningaction[WARN_CONSTANTCOMPARISON] = WA_IGNORE;\t//xonotic abuses this, and gmqcc doesn't warn.\n\t\t\t\tqccwarningaction[WARN_POINTLESSSTATEMENT] = WA_IGNORE;\t//so many macro expansions that we can't mute because of xonotic using an external preprocessor.\n\t\t\t\tqccwarningaction[WARN_OVERFLOW] = WA_IGNORE;\t\t\t//xonotic has data loss from implicit conversions too.\n\t\t\t\tqccwarningaction[WARN_STRICTTYPEMISMATCH] = WA_IGNORE;\t//gmqcc doesn't enforce checks on auxilliary types.\n\t\t\t\tqccwarningaction[WARN_PARAMWITHNONAME] = WA_IGNORE;\t\t//nor does it care if a parameter isn't named.\n\t\t\t\tqccwarningaction[WARN_IFSTRING_USED] = WA_IGNORE;\t\t//and many people would argue that this was a feature rather than a bug\n\t\t\t\tqccwarningaction[WARN_UNINITIALIZED] = WA_IGNORE;\t\t//all locals get 0-initialised anyway, and our checks are not quite up to scratch.\n\t\t\t\tqccwarningaction[WARN_GMQCC_SPECIFIC] = WA_IGNORE;\t\t//we shouldn't warn about gmqcc syntax when we're trying to be compatible with it. there's always -Wextra.\n\t\t\t\tqccwarningaction[WARN_OCTAL_IMMEDIATE] = WA_IGNORE;\t\t//we shouldn't warn about gmqcc syntax when we're trying to be compatible with it. there's always -Wextra.\n\t\t\t\tqccwarningaction[WARN_SYSTEMCRC] = WA_IGNORE;\t\t\t//lameness\n\t\t\t\tqccwarningaction[WARN_SYSTEMCRC2] = WA_IGNORE;\t\t\t//extra lameness\n\t\t\t\tqccwarningaction[WARN_ARGUMENTCHECK] = WA_IGNORE;\t\t//gmqcc is often used on DP mods, and DP is just too horrible to fix its problems. Also there's a good chance its using some undocumented/new thing.\n\n\t\t\t\tqccwarningaction[WARN_ASSIGNMENTTOCONSTANT] = WA_ERROR;\t//some sanity.\n\n\t\t\t\tkeyword_asm = false;\n\t\t\t\tkeyword_inout = keyword_optional = keyword_state = keyword_inline = keyword_nosave = keyword_extern = keyword_shared = keyword_unused = keyword_used = keyword_nonstatic = keyword_ignore = keyword_strip = false;\n\t\t\t\tkeyword_accessor = keyword_class = false;\n\n\t\t\t\tkeyword_vector = keyword_entity = keyword_float = keyword_string = false;\t//not to be confused with actual types, but rather the absence of the keyword local.\n\t\t\t\tkeyword_int = keyword_integer = keyword_struct = keyword_union = keyword_enum = keyword_enumflags = false;\n\t\t\t\tkeyword_thinktime = keyword_until = keyword_loop = false;\n\n\t\t\t\tkeyword_wrap = keyword_weak = false;\n\n\t\t\t\tkeyword_enum = true;\n\t\t\t\tkeyword_break = keyword_continue = keyword_for = keyword_goto = true;\n\t\t\t\tkeyword_typedef = true;\n\t\t\t\tkeyword_switch = keyword_case = keyword_default = true;\n\t\t\t\tkeyword_const = keyword_var = keyword_static = keyword_noref = true;\n\t\t\t}\n\t\t\telse\n\t\t\t\tQCC_PR_Warning(WARN_BADPARAMS, \"cmdline\", 0, \"Unrecognised std parameter (%s)\", myargv[i]);\n\t\t}\n\t\telse if (!strnicmp(myargv[i], \"-state-fps=\", 11))\n\t\t{\n\t\t\tqcc_framerate = atof(myargv[i]+11);\n\t\t}\n\t\telse if ( !strnicmp(myargv[i], \"-F\", 2) || WINDOWSARG(!strnicmp(myargv[i], \"/F\", 2)) )\n\t\t{\n\t\t\tpbool state;\n\t\t\tconst char *arg;\n\t\t\tp = 0;\n\t\t\tif (!strnicmp(myargv[i]+2, \"no-\", 3))\n\t\t\t{\n\t\t\t\targ = myargv[i]+5;\n\t\t\t\tstate = false;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\targ = myargv[i]+2;\n\t\t\t\tstate = true;\n\t\t\t}\n\n\t\t\tfor (p = 0; compiler_flag[p].enabled; p++)\n\t\t\t\tif (!stricmp(arg, compiler_flag[p].abbrev))\n\t\t\t\t{\n\t\t\t\t\t*compiler_flag[p].enabled = state;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\tif (!compiler_flag[p].enabled)\n\t\t\t{\n\t\t\t\t//compat flags.\n\t\t\t\tif (!stricmp(arg, \"short-logic\"))\n\t\t\t\t\topt_logicops = state;\n\t\t\t\telse if (!stricmp(arg, \"correct-logic\"))\n\t\t\t\t\tflag_ifvector = flag_vectorlogic = state;\n\t\t\t\telse if (!stricmp(arg, \"false-empty-strings\"))\n\t\t\t\t\tflag_ifstring = state;\n\t\t\t\telse if (!stricmp(arg, \"true-empty-strings\"))\n\t\t\t\t\tflag_brokenifstring = state;\n\t\t\t\telse if (!stricmp(arg, \"emulate-state\"))\n\t\t\t\t{\n\t\t\t\t\tif (qcc_framerate>0 && state)\n\t\t\t\t\t\t;//already on, don't force if they already gave it an actual rate.\n\t\t\t\t\telse\n\t\t\t\t\t\tqcc_framerate = state?10:0;\n\t\t\t\t}\n\t\t\t\telse if (!stricmp(arg, \"arithmetic-exceptions\"))\n\t\t\t\t\tqccwarningaction[WARN_DIVISIONBY0] = state?WA_ERROR:WA_IGNORE;\n\t\t\t\telse if (!stricmp(arg, \"lno\"))\n\t\t\t\t{\n\t\t\t\t\t//currently we always try to write lno files, when filename info isn't stripped\n\t\t\t\t\tif (opt_filenames)\n\t\t\t\t\t{\n\t\t\t\t\t\tQCC_PR_Warning(WARN_BADPARAMS, \"cmdline\", 0, \"Disabling -Ofilenames to satisfy -flno request\");\n\t\t\t\t\t\topt_filenames = false;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse if (!stricmp(arg, \"return-assignments\"))\n\t\t\t\t\t;\t//should really be a warning instead\n\t\t\t\telse if (!stricmp(arg, \"relaxed-switch\"))\n\t\t\t\t\t;\t//again should be a warning/werror\n\t\t\t\telse if (!stricmp(arg, \"bail-on-werror\"))\n\t\t\t\t\t;\n\t\t\t\telse\n\t\t\t\t\tQCC_PR_Warning(WARN_BADPARAMS, \"cmdline\", 0, \"Unrecognised flag parameter (%s)\", myargv[i]);\n\t\t\t}\n\t\t}\n\n\n\t\telse if ( !strncmp(myargv[i], \"-T\", 2) || WINDOWSARG(!strncmp(myargv[i], \"/T\", 2)) )\n\t\t{\n\t\t\tp = 0;\n\t\t\tif (!strcmp(\"parse\", myargv[i]+2))\n\t\t\t\tparseonly = true;\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (!QCC_OPCodeSetTargetName(myargv[i]+2))\n\t\t\t\t\tQCC_PR_Warning(WARN_BADPARAMS, \"cmdline\", 0, \"Unrecognised target parameter (%s)\", myargv[i]);\n\t\t\t}\n\t\t}\n\n\t\telse if ( !strnicmp(myargv[i], \"-W\", 2) || WINDOWSARG(!strnicmp(myargv[i], \"/W\", 2)) )\n\t\t{\n\t\t\tconst char *a = myargv[i]+2;\n\t\t\tif (!stricmp(a, \"all\"))\n\t\t\t{\n\t\t\t\tfor (j = 0; j < ERR_PARSEERRORS; j++)\n\t\t\t\t\tif (qccwarningaction[j] == WA_IGNORE)\n\t\t\t\t\t{\n\t\t\t\t\t\tswitch(j)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t//these warnings do not get switched on with -Wall when using -std=gmqcc, because mods that use -Werror would screw up too much\n\t\t\t\t\t\tcase WARN_CONSTANTCOMPARISON:\n\t\t\t\t\t\tcase WARN_POINTLESSSTATEMENT:\n\t\t\t\t\t\tcase WARN_OVERFLOW:\n\t\t\t\t\t\tcase WARN_STRICTTYPEMISMATCH:\n\t\t\t\t\t\tcase WARN_PARAMWITHNONAME:\n\t\t\t\t\t\tcase WARN_IFSTRING_USED:\n//\t\t\t\t\t\tcase WARN_UNINITIALIZED:\n\t\t\t\t\t\tcase WARN_GMQCC_SPECIFIC:\n\t\t\t\t\t\tcase WARN_SYSTEMCRC:\n\t\t\t\t\t\tcase WARN_SYSTEMCRC2:\n\t\t\t\t\t\t\tif (qccwarningaction[WARN_GMQCC_SPECIFIC])\n\t\t\t\t\t\t\t\tqccwarningaction[j] = WA_WARN;\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\t//these warnings require -Wextra to enable, as they're too annoying to have to fix\n\t\t\t\t\t\tcase WARN_NOTREFERENCEDCONST:\t\t//warning about every single constant is annoying as heck. note that this includes both stuff like MOVETYPE_ and builtins.\n\t\t\t\t\t\tcase WARN_EXTRAPRECACHE:\t\t\t//we can't guarentee that we can parse this correctly. this warning is thus a common false positive. its available with -Wextra, and there's intrinsics to reduce false positives.\n\t\t\t\t\t\tcase WARN_FTE_SPECIFIC:\t\t\t\t//kinda annoying when its actually valid code.\n\t\t\t\t\t\tcase WARN_MUTEDEPRECATEDVARIABLE:\t//these were explicitly muted by the user using checkbuiltin/etc to mute specific symbols.\n\t\t\t\t\t\tcase WARN_DIVISIONBY0:\t\t\t\t//breaks xonotic, which seems to want nans.\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\tqccwarningaction[j] = WA_WARN;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t}\n\t\t\telse if (!stricmp(a, \"extra\"))\n\t\t\t{\n\t\t\t\tfor (j = 0; j < ERR_PARSEERRORS; j++)\n\t\t\t\t\tif (qccwarningaction[j] == WA_IGNORE)\n\t\t\t\t\t\tqccwarningaction[j] = WA_WARN;\n\t\t\t}\n\t\t\telse if (!stricmp(a, \"none\"))\n\t\t\t{\n\t\t\t\tfor (j = 0; j < ERR_PARSEERRORS; j++)\n\t\t\t\t\tqccwarningaction[j] = WA_IGNORE;\n\t\t\t}\n\t\t\telse if(!stricmp(a, \"error\"))\n\t\t\t{\n\t\t\t\twerror = true;\n\t\t\t}\n\t\t\telse if (!stricmp(a, \"no-mundane\"))\n\t\t\t{\t//disable mundane performance/efficiency/blah warnings that don't affect code.\n\t\t\t\tqccwarningaction[WARN_SAMENAMEASGLOBAL] = WA_IGNORE;\n\t\t\t\tqccwarningaction[WARN_DUPLICATEDEFINITION] = WA_IGNORE;\n\t\t\t\tqccwarningaction[WARN_CONSTANTCOMPARISON] = WA_IGNORE;\n\t\t\t\tqccwarningaction[WARN_ASSIGNMENTINCONDITIONAL] = WA_IGNORE;\n\t\t\t\tqccwarningaction[WARN_DEADCODE] = WA_IGNORE;\n\t\t\t\tqccwarningaction[WARN_NOTREFERENCEDCONST] = WA_IGNORE;\n\t\t\t\tqccwarningaction[WARN_NOTREFERENCED] = WA_IGNORE;\n\t\t\t\tqccwarningaction[WARN_POINTLESSSTATEMENT] = WA_IGNORE;\n\t\t\t\tqccwarningaction[WARN_ASSIGNMENTTOCONSTANTFUNC] = WA_IGNORE;\n\t\t\t\tqccwarningaction[WARN_BADPRAGMA] = WA_IGNORE;\t//C specs say that these should be ignored. We're close enough to C that I consider that a valid statement.\n\t\t\t\tqccwarningaction[WARN_IDENTICALPRECOMPILER] = WA_IGNORE;\n\t\t\t\tqccwarningaction[WARN_UNDEFNOTDEFINED] = WA_IGNORE;\n\t\t\t\tqccwarningaction[WARN_EXTRAPRECACHE] = WA_IGNORE;\n\t\t\t\tqccwarningaction[WARN_CORRECTEDRETURNTYPE] = WA_IGNORE;\n\t\t\t\tqccwarningaction[WARN_NOTUTF8] = WA_IGNORE;\n\t\t\t\tqccwarningaction[WARN_SELFNOTTHIS] = WA_IGNORE;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tunsigned char action = WA_WARN;\n\t\t\t\tp = -1;\n\t\t\t\tif (!strnicmp(a, \"error-\", 6))\n\t\t\t\t{\n\t\t\t\t\ta+= 6;\n\t\t\t\t\taction = WA_ERROR;\n\t\t\t\t}\n\t\t\t\telse if (!strnicmp(a, \"no-\", 3))\n\t\t\t\t{\n\t\t\t\t\ta+=3;\n\t\t\t\t\taction = WA_IGNORE;\n\t\t\t\t}\n\t\t\t\tp = QCC_WarningForName(a);\n\t\t\t\tif (p >= 0)\n\t\t\t\t\tqccwarningaction[p] = action;\n\t\t\t\telse\n\t\t\t\t\tQCC_PR_Warning(WARN_BADPARAMS, \"cmdline\", 0, \"Unrecognised warning parameter (%s)\", myargv[i]);\n\t\t\t}\n\t\t}\n\t\telse if ( !strcmp(myargv[i], \"-stdout\") )\n\t\t{\n\t\t}\n\t\telse if ( !strcmp(myargv[i], \"-log\") || !strcmp(myargv[i], \"-nolog\") )\n\t\t{\n\t\t}\n\t\telse if ( !strcmp(myargv[i], \"-max_regs\") || !strcmp(myargv[i], \"-max_strings\") || !strcmp(myargv[i], \"-max_globals\")\n\t\t || !strcmp(myargv[i], \"-max_fields\") || !strcmp(myargv[i], \"-max_statements\") || !strcmp(myargv[i], \"-max_functions\")\n\t\t  || !strcmp(myargv[i], \"-max_types\") || !strcmp(myargv[i], \"-max_temps\") || !strcmp(myargv[i], \"-max_macros\") )\n\t\t{\n\t\t\tif (++i == myargc)\n\t\t\t\tQCC_PR_Warning(WARN_BADPARAMS, \"cmdline\", 0, \"Missing value for %s arg\", myargv[--i]);\n\t\t}\n\t\telse if ( !strcmp(myargv[i], \"--version\") )\n\t\t{\n\t\t\texterns->Printf(\"%s\\n\", QCC_VersionString());\n\t\t\texit(EXIT_SUCCESS);\n\t\t}\n\t\telse if ( !strcmp(myargv[i], \"--help\") || !strcmp(myargv[i], \"-help\") )\n\t\t\t;\t//hacks... checked later. *sigh*\n\t\telse if (*myargv[i] == '-' || WINDOWSARG(*myargv[i] == '/'))\n\t\t\tQCC_PR_Warning(WARN_BADPARAMS, \"cmdline\", 0, \"Unrecognised parameter (%s)\", myargv[i]);\n\t\telse\n\t\t{\n\t\t\tif (!QCC_RegisterSourceFile(myargv[i]))\n\t\t\t\tQCC_PR_Warning(WARN_BADPARAMS, \"cmdline\", 0, \"too many source filename arguments\");\n\t\t}\n\t}\n\n\tif (werror)\n\t{\n\t\tfor (j = 0; j < ERR_PARSEERRORS; j++)\n\t\t\tif (qccwarningaction[j])\n\t\t\t\tqccwarningaction[j] = WA_ERROR;\n\t}\n}\n\n/*\n============\nmain\n============\n*/\n\nint\t\tqccmline;\nchar\t*qccmsrc;\n//char\t*qccmsrc2;\nchar\tqccmfilename[1024];\nchar\tqccmprogsdat[1024*2];\n\nvoid QCC_FinishCompile(void);\n\n\nvoid SetEndian(void);\n\n\n\nstatic void QCC_SetDefaultProperties (void)\n{\n\tint level;\n\tint i;\n#ifdef _WIN32\n#define FWDSLASHARGS 1\n#else\n#define FWDSLASHARGS 0\n#endif\n\n\tHash_InitTable(&compconstantstable, MAX_CONSTANTS, qccHunkAlloc(Hash_BytesForBuckets(MAX_CONSTANTS)));\n\n\tqcc_framerate = 0;\t//depends on target (engine's OP_STATE)\n\tForcedCRC = 0;\n\tdefaultstatic = 0;\n\tverbose = VERBOSE_PROGRESS;\n\t*qccmsourcedir = 0;\n\tQCC_PR_CloseProcessor();\n\n\tQCC_PR_DefineName(\"FTEQCC\", NULL);\n\tQCC_PR_DefineName(\"__FTEQCC__\", NULL);\n\n\tif ((FWDSLASHARGS && QCC_CheckParm(\"/O0\")) || QCC_CheckParm(\"-O0\"))\n\t\tlevel = 0;\n\telse if ((FWDSLASHARGS && QCC_CheckParm(\"/O1\")) || QCC_CheckParm(\"-O1\"))\n\t\tlevel = 1;\n\telse if ((FWDSLASHARGS && QCC_CheckParm(\"/O2\")) || QCC_CheckParm(\"-O2\"))\n\t\tlevel = 2;\n\telse if ((FWDSLASHARGS && QCC_CheckParm(\"/O3\")) || QCC_CheckParm(\"-O3\"))\n\t\tlevel = 3;\n\telse\n\t\tlevel = -1;\n\n\tif (level == -1)\n\t{\n\t\tfor (i = 0; optimisations[i].enabled; i++)\n\t\t{\n\t\t\tif (optimisations[i].flags & FLAG_ASDEFAULT)\n\t\t\t\t*optimisations[i].enabled = true;\n\t\t\telse\n\t\t\t\t*optimisations[i].enabled = false;\n\t\t}\n\t}\n\telse\n\t{\n\t\tfor (i = 0; optimisations[i].enabled; i++)\n\t\t{\n\t\t\tif (level >= optimisations[i].optimisationlevel)\n\t\t\t\t*optimisations[i].enabled = true;\n\t\t\telse\n\t\t\t\t*optimisations[i].enabled = false;\n\t\t}\n\t}\n\n\t{\t//FIXME: outdated, should be using -Tfte\n\t\tqcc_targetformat_t targ;\n\t\tif (QCC_CheckParm (\"-h2\"))\n\t\t\ttarg =\tQCF_HEXEN2;\n\t\telse if (QCC_CheckParm (\"-fte\"))\n\t\t\ttarg = QCF_FTE;\n\t\telse if (QCC_CheckParm (\"-fteh2\"))\n\t\t\ttarg = QCF_FTEH2;\n\t\telse if (QCC_CheckParm (\"-dp\"))\n\t\t\ttarg = QCF_DARKPLACES;\n\t\telse\n\t\t\ttarg = QCF_STANDARD;\n\t\tQCC_OPCodeSetTarget(targ, 0);\n\t}\n\n\n\t//enable all warnings\n\tfor (i = 0; i < ERR_PARSEERRORS; i++)\n\t\tqccwarningaction[i] = WA_WARN;\n\tfor (; i < WARN_MAX; i++)\n\t\tqccwarningaction[i] = WA_ERROR;\n\n\t//play with default warnings.\n\tqccwarningaction[WARN_NOTREFERENCEDCONST]\t\t= WA_IGNORE;\n\tqccwarningaction[WARN_MACROINSTRING]\t\t\t= WA_IGNORE;\n//\tqccwarningaction[WARN_ASSIGNMENTTOCONSTANT]\t\t= WA_IGNORE;\n\tqccwarningaction[WARN_EXTRAPRECACHE]\t\t\t= WA_IGNORE;\n\tqccwarningaction[WARN_DEADCODE]\t\t\t\t\t= WA_IGNORE;\n\tqccwarningaction[WARN_FTE_SPECIFIC]\t\t\t\t= WA_IGNORE;\n\tqccwarningaction[WARN_DIVISIONBY0]\t\t\t\t= WA_IGNORE;\n\tqccwarningaction[WARN_MUTEDEPRECATEDVARIABLE]\t= WA_IGNORE;\n\tqccwarningaction[WARN_EXTENSION_USED]\t\t\t= WA_IGNORE;\n\tqccwarningaction[WARN_IFSTRING_USED]\t\t\t= WA_IGNORE;\n\tqccwarningaction[WARN_CORRECTEDRETURNTYPE]\t\t= WA_IGNORE;\n\tqccwarningaction[WARN_NOTUTF8]\t\t\t\t\t= WA_IGNORE;\n\tqccwarningaction[WARN_UNINITIALIZED]\t\t\t= WA_IGNORE;\t//not sure about this being ignored by default.\n\tqccwarningaction[WARN_SELFNOTTHIS]\t\t\t\t= WA_IGNORE;\n\tqccwarningaction[WARN_UNSAFELOCALPOINTER]\t\t= WA_IGNORE;\t//only an issue with recursion. and annoying.\n\tqccwarningaction[WARN_EVILPREPROCESSOR]\t\t\t= WA_ERROR;\t\t//evil people do evil things. evil must be thwarted!\n\tqccwarningaction[WARN_IDENTICALPRECOMPILER]\t\t= WA_IGNORE;\n\tqccwarningaction[WARN_DENORMAL]\t\t\t\t\t= WA_ERROR;\t\t//DAZ provides a speedup on modern machines, so any engine compiled for sse2+ will have problems with denormals, so make their use look serious.\n\n\tif (qcc_targetformat == QCF_HEXEN2 || qcc_targetformat == QCF_UHEXEN2 || qcc_targetformat == QCF_FTEH2)\n\t\tqccwarningaction[WARN_CASEINSENSITIVEFRAMEMACRO] = WA_IGNORE;\t//hexenc consides these fair game.\n\n\tif (QCC_CheckParm (\"-Fqccx\"))\n\t{\n\t\tqccwarningaction[WARN_DENORMAL] = WA_IGNORE;\t//this is just too spammy\n\t\tqccwarningaction[WARN_LAXCAST] = WA_IGNORE;\t//more plausable, but still too spammy. easier to fix at least.\n\t}\n\n\t//Check the command line\n\tQCC_PR_CommandLinePrecompilerOptions();\n\n\n\tif (qcc_targetformat == QCF_HEXEN2 || qcc_targetformat == QCF_UHEXEN2 || qcc_targetformat == QCF_FTEH2)\t//force on the thinktime keyword if hexen2 progs.\n\t{\n\t\tkeyword_thinktime = true;\t//thinktime self : 0.1;\n\t\tkeyword_until = true;\t\t//until(cond) {code}; or do{code}until(cond);\n\t\tkeyword_loop = true;\t\t//loop {code};\n\t}\n\n\tif ((FWDSLASHARGS && QCC_CheckParm(\"/Debug\")))\t//disable any debug optimisations\n\t{\n\t\tfor (i = 0; optimisations[i].enabled; i++)\n\t\t{\n\t\t\tif (optimisations[i].flags & FLAG_KILLSDEBUGGERS)\n\t\t\t\t*optimisations[i].enabled = false;\n\t\t}\n\t}\n}\n\n//builds a list of files, pretends that they came from a progs.src\n//FIXME: use sourcedir!\nstatic int QCC_FindQCFiles(const char *sourcedir)\n{\n#ifdef _WIN32\n\tWIN32_FIND_DATA fd;\n\tHANDLE h;\n#endif\n\n\tint numfiles = 0, i, j;\n\tchar *filelist[256], *temp;\n\n\n\tqccmsrc = qccHunkAlloc(8192);\n\tstrcat(qccmsrc, \"progs.dat\\n\");//\"#pragma PROGS_DAT progs.dat\\n\");\n\n#if defined(_WIN32) && !defined(WINRT)\n\th = FindFirstFile(\"*.qc\", &fd);\n\tif (h == INVALID_HANDLE_VALUE)\n\t\treturn 0;\n\n\tdo\n\t{\n\t\tfilelist[numfiles] = qccHunkAlloc (strlen(fd.cFileName)+1);\n\t\tstrcpy(filelist[numfiles], fd.cFileName);\n\t\tnumfiles++;\n\t} while(FindNextFile(h, &fd)!=0);\n\tFindClose(h);\n#else\n\texterns->Printf(\"-Facc is not supported on this platform. Please make a progs.src file instead\\n\");\n#endif\n\n\t//Sort alphabetically.\n\t//bubble. :(\n\n\tfor (i = 0; i < numfiles-1; i++)\n\t{\n\t\tfor (j = i+1; j < numfiles; j++)\n\t\t{\n\t\t\tif (stricmp(filelist[i], filelist[j]) > 0)\n\t\t\t{\n\t\t\t\ttemp = filelist[j];\n\t\t\t\tfilelist[j] = filelist[i];\n\t\t\t\tfilelist[i] = temp;\n\t\t\t}\n\t\t}\n\t}\n\tfor (i = 0; i < numfiles; i++)\n\t{\n\t\tstrcat(qccmsrc, filelist[i]);\n\t\tstrcat(qccmsrc, \"\\n\");\n//\t\tstrcat(qccmsrc, \"#include \\\"\");\n//\t\tstrcat(qccmsrc, filelist[i]);\n//\t\tstrcat(qccmsrc, \"\\\"\\n\");\n\t}\n\n\treturn numfiles;\n}\n\n\nstatic pbool QCC_GenerateRelativePath(char *dest, size_t destsize, char *base, char *relative)\n{\n\tint p;\n\tchar *s1, *s2;\n\tif (!QC_strlcpy (dest, base, destsize))\n\t\treturn false;\n\ts1 = strchr(dest, '\\\\');\n\ts2 = strchr(dest, '/');\n\tif (s2 > s1)\n\t\ts1 = s2;\n\tif (s1)\n\t\t*s1 = 0;\n\telse\n\t\t*dest = 0;\n\n\tp=0;\n\ts2 = relative;\n\tfor (;;)\n\t{\n\t\tif (!strncmp(s2, \"./\", 2))\n\t\t\ts2+=2;\n\t\telse if(!strncmp(s2, \"../\", 3))\n\t\t{\n\t\t\ts2+=3;\n\t\t\tp++;\n\t\t}\n\t\telse\n\t\t\tbreak;\n\t}\n\tfor (s1=dest+strlen(dest)-1;p && s1>=dest; s1--)\n\t{\n\t\tif (*s1 == '/' || *s1 == '\\\\')\n\t\t{\n\t\t\t*s1 = '\\0';\n\t\t\tp--;\n\t\t}\n\t}\n\tif (*dest)\n\t{\n\t\tif (p)\n\t\t{\t//we were still looking for a separator, but didn't find one, so kill the entire path.\n\t\t\t(void)QC_strlcpy(dest, \"\", destsize);\n\t\t\tp--;\n\t\t}\n\t\telse if (!QC_strlcat(dest, \"/\", destsize))\n\t\t\treturn false;\n\t}\n\tif (!QC_strlcat(dest, s2, destsize))\n\t\treturn false;\n\n\twhile (p>0)\n\t{\n\t\tif (strlen(dest)+3 >= destsize)\n\t\t\treturn false;\n\t\tmemmove(dest+3, dest, strlen(dest)+1);\n\t\tdest[0] = '.';\n\t\tdest[1] = '.';\n\t\tdest[2] = '/';\n\t\tp--;\n\t}\n\n\treturn true;\n}\n\nconst char *qcccol[COL_MAX];\n\nint qcc_compileactive = false;\nextern int accglobalsblock;\nchar *originalqccmsrc;\t//for autoprototype.\npbool QCC_main (int argc, const char **argv)\t//as part of the quake engine\n{\n\textern int\t\t\tpr_bracelevel;\n\ttime_t long_time;\n\textern QCC_type_t *pr_classtype;\n\n\tsize_t\t\tp;\n\textern int qccpersisthunk;\n\tconst char *arg;\n\n\tchar *s;\n\n\t//make sure any print colours are set up properly.\n\tfor (p = 0; p < COL_MAX; p++)\n\t\tif (!qcccol[p])\n\t\t\tqcccol[p] = \"\";\n\n\ts_filen = \"cmdline\";\n\ts_unitn = \"\";\n\ts_filed = 0;\n\tpr_source_line = 0;\n\n\tif (numsourcefiles && currentsourcefile == numsourcefiles)\n\t{\n\t\tnumsourcefiles = 0;\n\t\treturn false;\n\t}\n\telse if (!numsourcefiles)\n\t\tcurrentsourcefile = 0;\n\n\tif (currentsourcefile && qccpersisthunk && numsourcefiles)\n\t\tQCC_PR_ResetErrorScope();\t//don't clear the ram if we're retaining def info\n\telse\n\t{\n\t\tmemset(sourcefilesdefs, 0, sizeof(sourcefilesdefs));\n\t\tsourcefilesnumdefs = 0;\n\t\tif (!PreCompile())\n\t\t\treturn false;\n\t}\n\n\tSetEndian();\n\n\tmyargc = argc;\n\tmyargv = argv;\n\tpr_scope = NULL;\n\tpr_classtype = NULL;\n\tlocals_marshalled = 0;\n\n\tqcc_compileactive = true;\n\n\tpHash_Get = &Hash_Get;\n\tpHash_GetNext = &Hash_GetNext;\n\tpHash_Add = &Hash_Add;\n\tpHash_RemoveData = &Hash_RemoveData;\n\n\tMAX_REGS\t\t= 1<<21;\n\tMAX_STRINGS\t\t= 1<<21;\n\tMAX_GLOBALS\t\t= 1<<17;\n\tMAX_FIELDS\t\t= 1<<13;\n\tMAX_STATEMENTS\t= 1<<20;\n\tMAX_FUNCTIONS\t= 1<<15;\n\tmaxtypeinfos\t= 1<<16;\n\tMAX_CONSTANTS\t= 1<<12;\n\n\tstrcpy(destfile, \"\");\n\tcompressoutput = 0;\n\n\tif ((arg = QCC_ReadParm(\"-max_regs\")))\n\t\tMAX_REGS = max(100, atoi(arg));\n\tif ((arg = QCC_ReadParm(\"-max_strings\")))\n\t\tMAX_STRINGS = max(100, atoi(arg));\n\tif ((arg = QCC_ReadParm(\"-max_globals\")))\n\t\tMAX_GLOBALS = max(64, atoi(arg));\n\tif ((arg = QCC_ReadParm(\"-max_fields\")))\n\t\tMAX_FIELDS = max(0, atoi(arg));\n\tif ((arg = QCC_ReadParm(\"-max_statements\")))\n\t\tMAX_STATEMENTS = max(1, atoi(arg));\n\tif ((arg = QCC_ReadParm(\"-max_functions\")))\n\t\tMAX_FUNCTIONS = max(1, atoi(arg));\n\tif ((arg = QCC_ReadParm(\"-max_types\")))\n\t\tmaxtypeinfos = max(100, atoi(arg));\n\tif ((arg = QCC_ReadParm(\"-max_temps\")))\n\t\tmax_temps = max(100, atoi(arg));\n\tif ((arg = QCC_ReadParm(\"-max_macros\")))\n\t\tMAX_CONSTANTS = max(100, atoi(arg));\n\n\t//FIXME: strip this.\n\ts = externs->ReadFile(\"qcc.cfg\", QCC_LoadFileHunkAlloc, NULL, &p, false);\n\tif (s)\n\t{\n\t\twhile(1)\n\t\t{\n\t\t\ts = QCC_COM_Parse(s);\n\t\t\tif (!strcmp(qcc_token, \"MAX_REGS\"))\n\t\t\t{\n\t\t\t\ts = QCC_COM_Parse(s);\n\t\t\t\tMAX_REGS = atoi(qcc_token);\n\t\t\t} else if (!strcmp(qcc_token, \"MAX_STRINGS\")) {\n\t\t\t\ts = QCC_COM_Parse(s);\n\t\t\t\tMAX_STRINGS = atoi(qcc_token);\n\t\t\t} else if (!strcmp(qcc_token, \"MAX_GLOBALS\")) {\n\t\t\t\ts = QCC_COM_Parse(s);\n\t\t\t\tMAX_GLOBALS = atoi(qcc_token);\n\t\t\t} else if (!strcmp(qcc_token, \"MAX_FIELDS\")) {\n\t\t\t\ts = QCC_COM_Parse(s);\n\t\t\t\tMAX_FIELDS = atoi(qcc_token);\n\t\t\t} else if (!strcmp(qcc_token, \"MAX_STATEMENTS\")) {\n\t\t\t\ts = QCC_COM_Parse(s);\n\t\t\t\tMAX_STATEMENTS = atoi(qcc_token);\n\t\t\t} else if (!strcmp(qcc_token, \"MAX_FUNCTIONS\")) {\n\t\t\t\ts = QCC_COM_Parse(s);\n\t\t\t\tMAX_FUNCTIONS = atoi(qcc_token);\n\t\t\t} else if (!strcmp(qcc_token, \"MAX_TYPES\")) {\n\t\t\t\ts = QCC_COM_Parse(s);\n\t\t\t\tmaxtypeinfos = atoi(qcc_token);\n\t\t\t} else if (!strcmp(qcc_token, \"MAX_TEMPS\")) {\n\t\t\t\ts = QCC_COM_Parse(s);\n\t\t\t\tmax_temps = atoi(qcc_token);\n\t\t\t} else if (!strcmp(qcc_token, \"CONSTANTS\")) {\n\t\t\t\ts = QCC_COM_Parse(s);\n\t\t\t\tMAX_CONSTANTS = atoi(qcc_token);\n\t\t\t}\n\t\t\telse if (!s)\n\t\t\t\tbreak;\n\t\t\telse\n\t\t\t\texterns->Printf(\"Bad token in qcc.cfg file\\n\");\n\t\t}\n\t}\n\t/* don't try to be clever\n\telse if (p < 0)\n\t{\n\t\ts = qccHunkAlloc(8192);\n\t\tsprintf(s, \"MAX_REGS\\t%i\\r\\nMAX_STRINGS\\t%i\\r\\nMAX_GLOBALS\\t%i\\r\\nMAX_FIELDS\\t%i\\r\\nMAX_STATEMENTS\\t%i\\r\\nMAX_FUNCTIONS\\t%i\\r\\nMAX_TYPES\\t%i\\r\\n\",\n\t\t\t\t\tMAX_REGS,    MAX_STRINGS,    MAX_GLOBALS,    MAX_FIELDS,    MAX_STATEMENTS,    MAX_FUNCTIONS,    maxtypeinfos);\n\t\texterns->WriteFile(\"qcc.cfg\", s, strlen(s));\n\t}\n\t*/\n\n\ttime(&long_time);\n\tstrftime(QCC_copyright, sizeof(QCC_copyright),  \"Compiled [%Y/%m/%d]\"\n#ifdef SVNREVISION\n\t\t\t\", by fteqcc \"STRINGIFY(SVNREVISION)\n#endif\n\t\t\t\". \", localtime( &long_time ));\n\t(void)QC_strlcat(QCC_copyright, QCC_VersionString(), sizeof(QCC_copyright));\n\tfor (p = 0; p < 5; p++)\n\t\tstrcpy(QCC_Packname[p], \"\");\n\n\tfor (p = 0; compiler_flag[p].enabled; p++)\n\t{\n\t\t*compiler_flag[p].enabled = !!(compiler_flag[p].flags & FLAG_ASDEFAULT);\n\t}\n\n\tparseonly = autoprototyped = autoprototype = false;\n\tQCC_SetDefaultProperties();\n\tautoprototype |= parseonly;\n\n\toptres_shortenifnots = 0;\n\toptres_overlaptemps = 0;\n\toptres_noduplicatestrings = 0;\n\toptres_constantarithmatic = 0;\n\toptres_nonvec_parms = 0;\n\toptres_constant_names = 0;\n\toptres_constant_names_strings = 0;\n\toptres_precache_file = 0;\n\toptres_filenames = 0;\n\toptres_assignments = 0;\n\toptres_unreferenced = 0;\n\toptres_function_names = 0;\n\toptres_locals = 0;\n\toptres_dupconstdefs = 0;\n\toptres_return_only = 0;\n\toptres_compound_jumps = 0;\n//\toptres_comexprremoval = 0;\n\toptres_stripfunctions = 0;\n\toptres_locals_overlapping = 0;\n\toptres_logicops = 0;\n\toptres_inlines = 0;\n\n\toptres_test1 = 0;\n\toptres_test2 = 0;\n\n\taccglobalsblock = 0;\n\n\n\ttempsused = 0;\n\n\tQCC_PurgeTemps();\n\n\tstrings = (void *)qccHunkAlloc(sizeof(char) * MAX_STRINGS);\n\tstrofs = 2;\n\n\tstatements = (void *)qccHunkAlloc(sizeof(QCC_statement_t) * MAX_STATEMENTS);\n\tnumstatements = 0;\n\n\tfunctions = (void *)qccHunkAlloc(sizeof(QCC_function_t) * MAX_FUNCTIONS);\n\tnumfunctions=0;\n\n\tpr_bracelevel = 0;\n\n\tqcc_pr_globals = (void *)qccHunkAlloc(sizeof(float) * (MAX_REGS + MAX_LOCALS + MAX_TEMPS));\n\tnumpr_globals=0;\n\n\tHash_InitTable(&typedeftable, 1024, qccHunkAlloc(Hash_BytesForBuckets(1024)));\n\tHash_InitTable(&globalstable, MAX_REGS/2, qccHunkAlloc(Hash_BytesForBuckets(MAX_REGS/2)));\n\tHash_InitTable(&localstable, 128, qccHunkAlloc(Hash_BytesForBuckets(128)));\n\tHash_InitTable(&floatconstdefstable, MAX_REGS/2+1, qccHunkAlloc(Hash_BytesForBuckets(MAX_REGS/2+1)));\n\tHash_InitTable(&stringconstdefstable, MAX_REGS/2, qccHunkAlloc(Hash_BytesForBuckets(MAX_REGS/2)));\n\tHash_InitTable(&stringconstdefstable_trans, 1000, qccHunkAlloc(Hash_BytesForBuckets(1000)));\n\tdotranslate_count = 0;\n\n//\tpr_global_defs = (QCC_def_t **)qccHunkAlloc(sizeof(QCC_def_t *) * MAX_REGS);\n\n\tqcc_globals = (void *)qccHunkAlloc(sizeof(QCC_ddef_t) * MAX_GLOBALS);\n\tnumglobaldefs=0;\n\n\tfields = (void *)qccHunkAlloc(sizeof(QCC_ddef_t) * MAX_FIELDS);\n\tnumfielddefs=0;\n\nmemset(pr_immediate_string, 0, sizeof(pr_immediate_string));\n\n\tprecache_sound = (void *)qccHunkAlloc(sizeof(*precache_sound)*QCC_MAX_SOUNDS);\n\tnumsounds=0;\n\tprecache_texture = (void *)qccHunkAlloc(sizeof(*precache_texture)*QCC_MAX_TEXTURES);\n\tnumtextures=0;\n\tprecache_model = (void *)qccHunkAlloc(sizeof(*precache_model)*QCC_MAX_MODELS);\n\tnummodels=0;\n\tprecache_file = (void *)qccHunkAlloc(sizeof(*precache_file)*QCC_MAX_FILES);\n\tnumfiles = 0;\n\n\tqcc_typeinfo = (void *)qccHunkAlloc(sizeof(QCC_type_t)*maxtypeinfos);\n\tnumtypeinfos = 0;\n\n\tqcc_tempofs = qccHunkAlloc(sizeof(int) * max_temps);\n\ttempsstart = 0;\n\n\tbodylessfuncs=0;\n\n\tmemset(&pr, 0, sizeof(pr));\n#ifdef MAX_EXTRA_PARMS\n\tmemset(&extra_parms, 0, sizeof(extra_parms));\n#endif\n\n\tif ( QCC_CheckParm (\"/?\") || QCC_CheckParm (\"?\") || QCC_CheckParm (\"-?\") || QCC_CheckParm (\"-help\") || QCC_CheckParm (\"--help\"))\n\t{\n\t\texterns->Printf (\"Compile args:\\n\");\n//\t\texterns->Printf (\"to build a clean data tree: qcc -copy <srcdir> <destdir>\\n\");\n//\t\texterns->Printf (\"to build a clean pak file: qcc -pak <srcdir> <packfile>\\n\");\n//\t\texterns->Printf (\"to bsp all bmodels: qcc -bspmodels <gamedir>\\n\");\n\t\texterns->Printf (\" -src <DIRECTORY> : look for the progs.src and qc files in a different directory\\n\");\n\t\texterns->Printf (\" -srcfile <PATH> : explicit path for your starting .src file\\n\");\n\t\texterns->Printf (\" -O0 : disable optimisations\\n\");\n\t\texterns->Printf (\" -O1 : optimise for size\\n\");\n\t\texterns->Printf (\" -O2 : optimise more - some behaviours may change\\n\");\n\t\texterns->Printf (\" -O3 : optimise lots - experimental or non-future-proof\\n\");\n\t\texterns->Printf (\" -O<NAME> : enable an optimisation\\n\");\n\t\texterns->Printf (\" -Ono-<NAME> : disable optimisations\\n\");\n\t\texterns->Printf (\" -K[no-]<KEYWORD> : activate or deactivate a keyword\\n\");\n\t\texterns->Printf (\"     note that inactive keywords can still be used via __keyword\\n\");\n\t\texterns->Printf (\" -Wall : give a stupid number of warnings\\n\");\n\t\texterns->Printf (\" -T<TARGET> : set an output format\\n\");\n\t\texterns->Printf (\"     q1, h2, qtest, fte_5768, dp, kk7\\n\");\n\t\texterns->Printf (\" -F[no-]<FLAG> : Enable or disable some flagged setting\\n\");\n\t\texterns->Printf (\"     wasm : causes FTEQCC to dump all asm to qc.asm\\n\");\n\t\texterns->Printf (\"     autoproto : enable automatic prototyping\\n\");\n\t\texterns->Printf (\"     subscope : make locals specific to their subscope\\n\");\n\t\texterns->Printf (\"     assumeint : don't force immediates to floats, preserving precision\\n\");\n\t\texterns->Printf (\"     dumpautocvars : write a .cfg containing all autocvars, to be inserted into your mod's default.cfg file\\n\");\n\t\texterns->Printf (\"     dumplocalisation : write a localisation template file\\n\");\n\t\texterns->Printf (\"     dumpopcodes : write an opcodes file\\n\");\n\t\texterns->Printf (\" -D<MACRO>=<VALUE> : define a preprocessor macro via the commandline\\n\");\n\t\texterns->Printf (\" -I<PATH> : specify an alternative path to search for includes\\n\");\n\t\texterns->Printf (\" -std=<STD> : change default settings to be more accepting of code for other compilers\\n\");\n\t\texterns->Printf (\"     qcc(vanilla), hcc(hexen2), C, qccx, reacc(for nehahra)\\n\");\n\n\t\tqcc_compileactive = false;\n\t\treturn true;\n\t}\n\n\tif (flag_caseinsensitive)\n\t{\n\t\texterns->Printf(\"Compiling without case sensitivity\\n\");\n\t\tpHash_Get = &Hash_GetInsensitive;\n\t\tpHash_GetNext = &Hash_GetNextInsensitive;\n\t\tpHash_Add = &Hash_AddInsensitive;\n\t\tpHash_RemoveData = &Hash_RemoveDataInsensitive;\n\t}\n\n\tif (*qccmsourcedir)\n\t\texterns->Printf (\"Source directory: %s\\n\", qccmsourcedir);\n\n\tQCC_InitData ();\n\n\tQCC_PR_BeginCompilation ((void *)qccHunkAlloc (0x100000), 0x100000);\n\n\tQCC_PR_ClearGrabMacros (false);\n\n\tqccmsrc = NULL;\t\n\tif (destfile_explicit && numsourcefiles && !currentsourcefile)\n\t{\t//generate an internal .src file from the argument list\n\t\tint i;\n\t\tqccmsrc = qccHunkAlloc(8192);\n\t\t*qccmsrc = 0;\n\t\tfor (i = 0;i<numsourcefiles;i++)\n\t\t\tQC_snprintfz(qccmsrc+strlen(qccmsrc), 8192-strlen(qccmsrc), \"#include \\\"%s\\\"\\n\", sourcefileslist[i]);\n\t\tcurrentsourcefile = i;\n\t}\n\n\tif (qccmsrc)\n\t\t;\n\telse if (flag_acc && !numsourcefiles)\n\t{\n\t\tif (!QCC_FindQCFiles(qccmsourcedir))\n\t\t\tQCC_Error (ERR_COULDNTOPENFILE, \"Couldn't find any qc files.\");\n\t}\n\telse\n\t{\n\t\tif (!numsourcefiles)\n\t\t{\n\t\t\tp = QCC_CheckParm (\"-qc\");\n\t\t\tif (p && p < argc-1 )\n\t\t\t\tQC_strlcpy(qccmprogsdat, argv[p+1], sizeof(qccmprogsdat));\n\t\t\telse\n\t\t\t{\t//look for a preprogs.src... :o)\n\t\t\t\tchar tmp[sizeof(qccmsourcedir)+16];\n\t\t\t\tQC_snprintfz (tmp, sizeof(tmp), \"%spreprogs.src\", qccmsourcedir);\n\t\t\t\tif (externs->FileSize(tmp) <= 0)\n\t\t\t\t\tQC_snprintfz (qccmprogsdat, sizeof(qccmprogsdat), \"progs.src\");\n\t\t\t\telse\n\t\t\t\t\tQC_snprintfz (qccmprogsdat, sizeof(qccmprogsdat), \"preprogs.src\");\n\t\t\t}\n\n\t\t\tnumsourcefiles = 0;\n\t\t\tstrcpy(sourcefileslist[numsourcefiles++], qccmprogsdat);\n\t\t\tcurrentsourcefile = 0;\n\t\t}\n\t\telse if (currentsourcefile == numsourcefiles || (currentsourcefile && destfile_explicit))\n\t\t{\n\t\t\t//no more.\n\t\t\tqcc_compileactive = false;\n\t\t\tnumsourcefiles = 0;\n\t\t\tcurrentsourcefile = 0;\n\t\t\treturn true;\n\t\t}\n\n\t\tif (currentsourcefile)\n\t\t\texterns->Printf(\"-------------------------------------\\n\");\n\t\telse\n\t\t\texterns->Printf(\"%s\\n\", QCC_VersionString());\n\n\t\tQC_snprintfz (qccmprogsdat, sizeof(qccmprogsdat), \"%s%s\", qccmsourcedir, sourcefileslist[currentsourcefile++]);\n\t\texterns->Printf (\"Source file: %s\\n\", qccmprogsdat);\n\n\t\tQC_strlcpy(compilingrootfile, qccmprogsdat, sizeof(compilingrootfile));\n\t\tif (QCC_LoadFile (qccmprogsdat, (void *)&qccmsrc) == -1)\n\t\t{\n\t\t\treturn true;\n\t\t}\n\t}\n\n#ifdef WRITEASM\n\tif (writeasm)\n\t{\n\t\tasmfile = fopen(\"qc.asm\", \"wb\");\n\t\tif (!asmfile)\n\t\t\tQCC_Error (ERR_INTERNAL, \"Couldn't open file for asm output.\");\n\t}\n\tasmfilebegun = !!asmfile;\n#endif\n\n\tnewstylesource = false;\n\tif (qccmsrc[0] == '#' && qccmsrc[1] == '!')\n\t\tqccmsrc = strchr(qccmsrc, '\\n');\t//ignore the first line if it starts with a #! for unix scripts. because we can.\n\n\tcompilingfile = qccmprogsdat;\n\tpreprocessonly = false;\n\tif (QCC_CheckParm (\"-E\"))\n\t{\n\t\tpr_file_p = qccmsrc;\n\t\tpreprocessonly = true;\n\t\tgoto newstyle;\n\t}\n\n\tpr_file_p = QCC_COM_Parse(qccmsrc);\n\tif (QCC_CheckParm (\"-qc\"))\n\t{\n\t\tstrcpy(destfile, qccmprogsdat);\n\t\tStripExtension(destfile);\n\t\tstrcat(destfile, \".qco\");\n\n\t\tp = QCC_CheckParm (\"-o\");\n\t\tif (!p || p >= argc-1 || argv[p+1][0] == '-')\n\t\tif (p && p < argc-1 )\n\t\t\tsprintf (destfile, \"%s%s\", qccmsourcedir, argv[p+1]);\n\t\tgoto newstyle;\n\t}\n\n\tif (*qcc_token == '#')\n\t{\nnewstyle:\n\t\tif (flag_filetimes)\n\t\t\tQCC_PR_Warning(0, qccmsrc, 0, \"-ffiletimes unsupported with this input\");\n\t\tnewstylesource = true;\n\t\toriginalqccmsrc = qccmsrc;\n\t\tpr_source_line = qccmline = 1;\n\t\tStartNewStyleCompile();\n\t\treturn true;\n\t}\n\n\tpr_source_line = qccmline = 1;\n\tpr_file_p = qccmsrc;\n\tQCC_PR_LexWhitespace(false);\n\tqccmsrc = pr_file_p;\n\n\ts = qccmsrc;\n\tpr_file_p = qccmsrc;\n\tQCC_PR_SimpleGetToken ();\n\tstrcpy(qcc_token, pr_token);\n\tqccmsrc = pr_file_p;\n\tqccmline = pr_source_line;\n\n\tif (!qccmsrc)\n\t\tQCC_Error (ERR_NOOUTPUT, \"No destination filename.  qcc -help for info.\");\n\n\tQCC_GenerateRelativePath(destfile, sizeof(destfile), qccmprogsdat, qcc_token);\n\n\tp = QCC_CheckParm (\"-o\");\n\tif (p > 0 && p < argc-1 && argv[p+1][0] != '-')\n\t\tsprintf (destfile, \"%s\", argv[p+1]);\n\n\tif (flag_filetimes)\n\t{\n\t\tstruct stat s, os;\n\t\tpbool modified = false;\n\n\t\tif (stat(destfile, &os) != -1)\n\t\t{\n\t\t\twhile ((pr_file_p=QCC_COM_Parse(pr_file_p)))\n\t\t\t{\n\t\t\t\tif (stat(qcc_token, &s) == -1 || s.st_mtime > os.st_mtime)\n\t\t\t\t{\n\t\t\t\t\texterns->Printf(\"%s changed\\n\", qcc_token);\n\t\t\t\t\tmodified = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!modified)\n\t\t\t{\n\t\t\t\texterns->Printf(\"No changes\\n\");\n\t\t\t\tqcc_compileactive = false;\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tpr_file_p = qccmsrc;\n\t\t\t}\n\t\t}\n\t}\n\n\texterns->Printf (\"outputfile: %s\\n\", destfile);\n\n\tpr_dumpasm = false;\n\n\tcurrentchunk = NULL;\n\n\toriginalqccmsrc = qccmsrc;\n\treturn true;\n}\n\nvoid new_QCC_ContinueCompile(void);\n//called between exe frames - won't loose net connection (is the theory)...\nvoid QCC_ContinueCompile(void)\n{\n\tif (!qcc_compileactive)\n\t\t//HEY!\n\t\treturn;\n\n\tif (newstylesource)\n\t{\n\t\tchar *ofp = pr_file_p;\n\t\tdo\n\t\t{\n\t\t\tnew_QCC_ContinueCompile();\n\t\t} while(currentchunk);\t//while parsing through preprocessor, make sure nothing gets hurt.\n\t\tif (ofp == pr_file_p && qcc_compileactive && pr_token_type != tt_eof)\n\t\t\tQCC_Error (ERR_INTERNAL, \"Syntax error\\n\");\n\n\t\treturn;\n\t}\n\n\tpr_file_p = qccmsrc;\n\ts_filen = compilingrootfile;\n\ts_filed = 0;\n\tpr_source_line = qccmline;\n\tQCC_PR_LexWhitespace(false);\n\tqccmsrc = pr_file_p;\n\tqccmline = pr_source_line;\n\n\tqccmsrc = QCC_COM_Parse(qccmsrc);\n\tif (!qccmsrc)\n\t{\n\t\tif (parseonly)\n\t\t{\n\t\t\tqcc_compileactive = false;\n\t\t\tif (sourcefilesnumdefs < countof(sourcefilesdefs) && qccpersisthunk)\n\t\t\t\tsourcefilesdefs[currentsourcefile++] = pr.def_head.next;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (autoprototype)\n\t\t\t{\n\t\t\t\tqccmsrc = originalqccmsrc;\n\t\t\t\tautoprototyped = autoprototype;\n\t\t\t\tQCC_SetDefaultProperties();\n\t\t\t\tautoprototype = false;\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tQCC_FinishCompile();\n\t\t}\n\t\tPostCompile();\n\n\t\tif (currentsourcefile < numsourcefiles)\n\t\t{\n\t\t\tif (!QCC_main(myargc, myargv))\n\t\t\t\treturn;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tqcc_compileactive = false;\n\t\t\tnumsourcefiles = 0;\n\t\t\tcurrentsourcefile = 0;\n\t\t}\n\t\treturn;\n\t}\n\n\tif(setjmp(pr_parse_abort))\n\t{\n\t\tif (++pr_error_count > MAX_ERRORS)\n\t\t\tQCC_Error (ERR_PARSEERRORS, \"%i errors have occured\\n\", pr_error_count);\n\t\treturn;\t//just try move onto the next file, gather errors.\n\t}\n\telse\n\t\tQCC_FindBestInclude(qcc_token, compilingrootfile, 2);\n/*\n\t{\n\t\tint includepath = 0;\n\t\twhile(1)\n\t\t{\n\t\t\tif (includepath)\n\t\t\t{\n\t\t\t\tif (includepath > MAXINCLUDEDIRS || !*qccincludedir[includepath-1])\n\t\t\t\t{\n\t\t\t\t\tQCC_GenerateRelativePath(qccmfilename, sizeof(qccmfilename), compilingrootfile, qcc_token);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tcurrentfile = qccincludedir[includepath-1];\n\t\t\t}\n\n\t\t\tQCC_Canonicalize(qccmfilename, sizeof(fullname), qcc_token, compilingrootfile);\n\n\t\t\t{\n\t\t\t\textern progfuncs_t *qccprogfuncs;\n\t\t\t\tif (qccprogfuncs->funcs.parms->FileSize(qccmfilename) == -1)\n\t\t\t\t{\n\t\t\t\t\tincludepath++;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tQCC_GenerateRelativePath(qccmfilename, sizeof(qccmfilename), compilingrootfile, qcc_token);\n\tif (autoprototype)\n\t\texterns->Printf (\"prototyping %s\\n\", qccmfilename);\n\telse\n\t{\n\t\texterns->Printf (\"compiling %s\\n\", qccmfilename);\n\t}\n\n\tQCC_LoadFile (qccmfilename, (void *)&qccmsrc2);\n\n\tif (!QCC_PR_CompileFile (qccmsrc2, qccmfilename) )\n\t\tQCC_Error (ERR_PARSEERRORS, \"%i errors have occured\\n\", pr_error_count);\n\t*/\n}\nvoid QCC_FinishCompile(void)\n{\n\tpbool donesomething;\n\tint crc;\n//\tint p;\n\tcurrentchunk = NULL;\n\n\tif (setjmp(pr_parse_abort))\n\t\tQCC_Error(ERR_INTERNAL, \"%s\", \"\");\n\n\ts_filen = \"\";\n\ts_filed = 0;\n\tpr_source_line = 0;\n\n\tif (!QCC_PR_FinishCompilation ())\n\t{\n\t\tQCC_Error (ERR_PARSEERRORS, \"compilation errors\");\n\t}\n\n/*\tp = QCC_CheckParm (\"-asm\");\n\tif (p)\n\t{\n\t\tfor (p++ ; p<myargc ; p++)\n\t\t{\n\t\t\tif (myargv[p][0] == '-')\n\t\t\t\tbreak;\n\t\t\tQCC_PrintFunction (myargv[p]);\n\t\t}\n\t}*/\n\n\t/*p = QCC_CheckParm (\"-ofs\");\n\tif (p)\n\t{\n\t\tfor (p++ ; p<myargc ; p++)\n\t\t{\n\t\t\tif (myargv[p][0] == '-')\n\t\t\t\tbreak;\n\t\t\tQCC_PrintOfs (atoi(myargv[p]));\n\t\t}\n\t}*/\n\n// write progdefs.h\n\tcrc = QCC_PR_WriteProgdefs (\"progdefs.h\");\n\n\tif (pr_error_count)\n\t\tQCC_Error (ERR_PARSEERRORS, \"compilation errors\");\n\n// write data file\n\tdonesomething = QCC_WriteData (crc);\n\n// regenerate bmodels if -bspmodels\n\tQCC_BspModels ();\n\n// report / copy the data files\n\tQCC_CopyFiles ();\n\n\tif (sourcefilesnumdefs < countof(sourcefilesdefs) && qccpersisthunk)\n\t\tsourcefilesdefs[sourcefilesnumdefs++] = pr.def_head.next;\n\n\tif (donesomething)\n\t{\n\t\tif (verbose >= VERBOSE_STANDARD)\n\t\t{\n\t\t\texterns->Printf (\"Compile Complete\\n\\n\");\n\n\t\t\tif (optres_shortenifnots)\n\t\t\t\texterns->Printf(\"optres_shortenifnots %i\\n\", optres_shortenifnots);\n\t\t\tif (optres_overlaptemps)\n\t\t\t\texterns->Printf(\"optres_overlaptemps %i\\n\", optres_overlaptemps);\n\t\t\tif (optres_noduplicatestrings)\n\t\t\t\texterns->Printf(\"optres_noduplicatestrings %i\\n\", optres_noduplicatestrings);\n\t\t\tif (optres_constantarithmatic)\n\t\t\t\texterns->Printf(\"optres_constantarithmatic %i\\n\", optres_constantarithmatic);\n\t\t\tif (optres_nonvec_parms)\n\t\t\t\texterns->Printf(\"optres_nonvec_parms %i\\n\", optres_nonvec_parms);\n\t\t\tif (optres_constant_names)\n\t\t\t\texterns->Printf(\"optres_constant_names %i\\n\", optres_constant_names);\n\t\t\tif (optres_constant_names_strings)\n\t\t\t\texterns->Printf(\"optres_constant_names_strings %i\\n\", optres_constant_names_strings);\n\t\t\tif (optres_precache_file)\n\t\t\t\texterns->Printf(\"optres_precache_file %i\\n\", optres_precache_file);\n\t\t\tif (optres_filenames)\n\t\t\t\texterns->Printf(\"optres_filenames %i\\n\", optres_filenames);\n\t\t\tif (optres_assignments)\n\t\t\t\texterns->Printf(\"optres_assignments %i\\n\", optres_assignments);\n\t\t\tif (optres_unreferenced)\n\t\t\t\texterns->Printf(\"optres_unreferenced %i\\n\", optres_unreferenced);\n\t\t\tif (optres_locals)\n\t\t\t\texterns->Printf(\"optres_locals %i\\n\", optres_locals);\n\t\t\tif (optres_function_names)\n\t\t\t\texterns->Printf(\"optres_function_names %i\\n\", optres_function_names);\n\t\t\tif (optres_dupconstdefs)\n\t\t\t\texterns->Printf(\"optres_dupconstdefs %i\\n\", optres_dupconstdefs);\n\t\t\tif (optres_return_only)\n\t\t\t\texterns->Printf(\"optres_return_only %i\\n\", optres_return_only);\n\t\t\tif (optres_compound_jumps)\n\t\t\t\texterns->Printf(\"optres_compound_jumps %i\\n\", optres_compound_jumps);\n\t\t//\tif (optres_comexprremoval)\n\t\t//\t\texterns->Printf(\"optres_comexprremoval %i\\n\", optres_comexprremoval);\n\t\t\tif (optres_stripfunctions)\n\t\t\t\texterns->Printf(\"optres_stripfunctions %i\\n\", optres_stripfunctions);\n\t\t\tif (optres_locals_overlapping)\n\t\t\t\texterns->Printf(\"optres_locals_overlapping %i\\n\", optres_locals_overlapping);\n\t\t\tif (optres_logicops)\n\t\t\t\texterns->Printf(\"optres_logicops %i\\n\", optres_logicops);\n\t\t\tif (optres_inlines)\n\t\t\t\texterns->Printf(\"optres_inlines %i\\n\", optres_inlines);\n\n\n\t\t\tif (optres_test1)\n\t\t\t\texterns->Printf(\"optres_test1 %i\\n\", optres_test1);\n\t\t\tif (optres_test2)\n\t\t\t\texterns->Printf(\"optres_test2 %i\\n\", optres_test2);\n\n\t\t\texterns->Printf(\"numtemps %u\\n\", (unsigned)tempsused);\n\t\t}\n\t\tif (!flag_msvcstyle && verbose >= VERBOSE_PROGRESS)\n\t\t\texterns->Printf(\"Done. %i warnings\\n\", pr_warning_count);\n\t}\n\n\tqcc_compileactive = false;\n}\n\n\n\n\n\nextern char\t\t*pr_file_p;\nextern int\t\t\tpr_source_line;\n\n\n\n\nstatic void StartNewStyleCompile(void)\n{\n\tchar *tmp;\n\tif (setjmp(pr_parse_abort))\n\t{\n\t\tif (++pr_error_count > MAX_ERRORS)\n\t\t\treturn;\n\t\tif (setjmp(pr_parse_abort))\n\t\t\treturn;\n\n\t\tQCC_PR_SkipToSemicolon ();\n\t\tif (pr_token_type == tt_eof)\n\t\t\treturn;\n\t}\n\n\tcompilingfile = qccmprogsdat;\n\n\ts_filen = tmp = qccHunkAlloc(strlen(compilingfile)+1);\n\tstrcpy(tmp, compilingfile);\n\tif (opt_filenames)\n\t{\n\t\toptres_filenames += strlen(compilingfile)+1;\n\t\ts_filed = 0;\n\t}\n\telse\n\t\ts_filed = QCC_CopyString (compilingfile);\n\n\tpr_file_p = qccmsrc;\n\n\tpr_source_line = 0;\n\n\tQCC_PR_NewLine (false);\n\n\tQCC_PR_Lex ();\t// read first token\n}\nvoid new_QCC_ContinueCompile(void)\n{\n\tif (setjmp(pr_parse_abort))\n\t{\n//\t\tif (pr_error_count != 0)\n\t\t{\n\t\t\tQCC_Error (ERR_PARSEERRORS, \"%i errors have occured\\n\", pr_error_count);\n\t\t\treturn;\n\t\t}\n\t\tQCC_PR_SkipToSemicolon ();\n\t\tif (pr_token_type == tt_eof)\n\t\t\treturn;\n\t}\n\n\tif (pr_token_type == tt_eof)\n\t{\n\t\tif (pr_error_count)\n\t\t\tQCC_Error (ERR_PARSEERRORS, \"%i errors have occured\\n\", pr_error_count);\n\n\t\tif (autoprototype && !parseonly)\n\t\t{\n\t\t\tchar *tmp;\n\t\t\tqccmsrc = originalqccmsrc;\n\n\t\t\ts_filen = tmp = qccHunkAlloc(strlen(compilingfile)+1);\n\t\t\tstrcpy(tmp, compilingfile);\n\t\t\tif (opt_filenames)\n\t\t\t{\n\t\t\t\toptres_filenames += strlen(compilingfile)+1;\n\t\t\t\ts_filed = 0;\n\t\t\t}\n\t\t\telse\n\t\t\t\ts_filed = QCC_CopyString (compilingfile);\n\t\t\tpr_file_p = qccmsrc;\n\n\t\t\tautoprototyped = autoprototype;\n\t\t\tQCC_SetDefaultProperties();\n\t\t\tautoprototype = false;\n\t\t\tQCC_PR_NewLine(false);\n\t\t\tQCC_PR_Lex();\n\t\t\treturn;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (!parseonly)\n\t\t\t\tQCC_FinishCompile();\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (sourcefilesnumdefs < countof(sourcefilesdefs) && qccpersisthunk)\n\t\t\t\t\tsourcefilesdefs[currentsourcefile++] = pr.def_head.next;\n\t\t\t}\n\t\t\tPostCompile();\n\t\t\tif (!QCC_main(myargc, myargv))\n\t\t\t{\n\t\t\t\tqcc_compileactive = false;\n\t\t\t\treturn;\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t}\n\n\tpr_scope = NULL;\t// outside all functions\n\n\tif (preprocessonly)\n\t{\n\t\tpbool white = false;\n\t\tstatic int line = 1;\n\t\twhile(pr_token_type != tt_eof)\n\t\t{\n\t\t\t//if there's whitespace next, make sure we represent that\n\t\t\tif (line < pr_token_line)\n\t\t\t{\t//keep line numbers correct by splurging multiple newlines.\n\t\t\t\twhile(line++ < pr_source_line)\n\t\t\t\t\texterns->Printf(\"\\n\");\n\t\t\t}\n\t\t\telse if (white)\n\t\t\t\texterns->Printf(\" \");\n\n\t\t\texterns->Printf(\"%s\", pr_token);\n\t\t\twhite = (qcc_iswhite(*pr_file_p) || (*pr_file_p == '/' && (pr_file_p[1] == '/' || pr_file_p[1] == '*')));\n\t\t\tQCC_PR_Lex();\n\t\t}\n\t\tQCC_PR_Lex();\n\t\treturn;\n\t}\n\n\tQCC_PR_ParseDefs (NULL, false);\n}\n\n/*void new_QCC_ContinueCompile(void)\n{\n\tchar *s, *s2;\n\tif (!qcc_compileactive)\n\t\t//HEY!\n\t\treturn;\n\n// compile all the files\n\n\t\tqccmsrc = QCC_COM_Parse(qccmsrc);\n\t\tif (!qccmsrc)\n\t\t{\n\t\t\tQCC_FinishCompile();\n\t\t\treturn;\n\t\t}\n\t\ts = qcc_token;\n\n\t\tstrcpy (qccmfilename, qccmsourcedir);\n\t\twhile(1)\n\t\t{\n\t\t\tif (!strncmp(s, \"..\\\\\", 3))\n\t\t\t{\n\t\t\t\ts2 = qccmfilename + strlen(qccmfilename)-2;\n\t\t\t\twhile (s2>=qccmfilename)\n\t\t\t\t{\n\t\t\t\t\tif (*s2 == '/' || *s2 == '\\\\')\n\t\t\t\t\t{\n\t\t\t\t\t\ts2[1] = '\\0';\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\ts2--;\n\t\t\t\t}\n\t\t\t\ts+=3;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (!strncmp(s, \".\\\\\", 2))\n\t\t\t{\n\t\t\t\ts+=2;\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tbreak;\n\t\t}\n//\t\tstrcat (qccmfilename, s);\n//\t\texterns->Printf (\"compiling %s\\n\", qccmfilename);\n//\t\tQCC_LoadFile (qccmfilename, (void *)&qccmsrc2);\n\n//\t\tif (!new_QCC_PR_CompileFile (qccmsrc2, qccmfilename) )\n//\t\t\tQCC_Error (\"Errors have occured\\n\");\n\n\n\t\t{\n\n\t\t\tif (!pr.memory)\n\t\t\t\tQCC_Error (\"PR_CompileFile: Didn't clear\");\n\n\t\t\tQCC_PR_ClearGrabMacros ();\t// clear the frame macros\n\n\t\t\tcompilingfile = filename;\n\n\t\t\tpr_file_p = qccmsrc2;\n\t\t\ts_file = QCC_CopyString (filename);\n\n\t\t\tpr_source_line = 0;\n\n\t\t\tQCC_PR_NewLine ();\n\n\t\t\tQCC_PR_Lex ();\t// read first token\n\n\t\t\twhile (pr_token_type != tt_eof)\n\t\t\t{\n\t\t\t\tif (setjmp(pr_parse_abort))\n\t\t\t\t{\n\t\t\t\t\tif (++pr_error_count > MAX_ERRORS)\n\t\t\t\t\t\treturn false;\n\t\t\t\t\tQCC_PR_SkipToSemicolon ();\n\t\t\t\t\tif (pr_token_type == tt_eof)\n\t\t\t\t\t\treturn false;\n\t\t\t\t}\n\n\t\t\t\tpr_scope = NULL;\t// outside all functions\n\n\t\t\t\tQCC_PR_ParseDefs ();\n\t\t\t}\n\t\t}\n\treturn (pr_error_count == 0);\n\n}*/\n\n\n#endif\n"
  },
  {
    "path": "engine/qclib/qcctui.c",
    "content": "#include \"qcc.h\"\r\n\r\n#include <stdarg.h>\r\n#include <stdio.h>\r\n\r\n#if defined(__linux__) || defined(__unix__)\r\n#include <unistd.h>\r\n#endif\r\n\r\n/*\r\n==============\r\nLoadFile\r\n==============\r\n*/\r\nstatic void *QCC_ReadFile(const char *fname, unsigned char *(*buf_get)(void *ctx, size_t len), void *buf_ctx, size_t *out_size, pbool issourcefile)\r\n//unsigned char *PDECL QCC_ReadFile (const char *fname, void *buffer, int len, size_t *sz)\r\n{\r\n\tsize_t len;\r\n\tFILE *f;\r\n\tchar *buffer;\r\n\r\n\tf = fopen(fname, \"rb\");\r\n\tif (!f)\r\n\t{\r\n\t\tif (out_size)\r\n\t\t\t*out_size = 0;\r\n\t\treturn NULL;\r\n\t}\r\n\r\n\tfseek(f, 0, SEEK_END);\r\n\tlen = ftell(f);\r\n\tfseek(f, 0, SEEK_SET);\r\n\tif (buf_get)\r\n\t\tbuffer = buf_get(buf_ctx, len+1);\r\n\telse\r\n\t\tbuffer = malloc(len+1);\r\n\t((char*)buffer)[len] = 0;\r\n\tif (len != fread(buffer, 1, len, f))\r\n\t{\r\n\t\tif (!buf_get)\r\n\t\t\tfree(buffer);\r\n\t\tbuffer = NULL;\r\n\t}\r\n\tfclose(f);\r\n\r\n\tif (out_size)\r\n\t\t*out_size = len;\r\n\treturn buffer;\r\n}\r\nstatic int PDECL QCC_FileSize (const char *fname)\r\n{\r\n\tlong    length;\r\n\tFILE *f;\r\n\tf = fopen(fname, \"rb\");\r\n\tif (!f)\r\n\t\treturn -1;\r\n\tfseek(f, 0, SEEK_END);\r\n\tlength = ftell(f);\r\n\tfclose(f);\r\n\r\n\treturn length;\r\n}\r\n\r\nstatic pbool PDECL QCC_WriteFile (const char *name, void *data, int len)\r\n{\r\n\tlong    length;\r\n\tFILE *f;\r\n\tf = fopen(name, \"wb\");\r\n\tif (!f)\r\n\t\treturn false;\r\n\tlength = fwrite(data, 1, len, f);\r\n\tfclose(f);\r\n\r\n\tif (length != len)\r\n\t\treturn false;\r\n\r\n\treturn true;\r\n}\r\n\r\n#undef printf\r\n#undef Sys_Error\r\n\r\nstatic void PDECL Sys_Error(const char *text, ...)\r\n{\r\n\tva_list argptr;\r\n\tstatic char msg[2048];\t\r\n\r\n\tva_start (argptr,text);\r\n\tQC_vsnprintf (msg,sizeof(msg)-1, text,argptr);\r\n\tva_end (argptr);\r\n\r\n\tQCC_Error(ERR_INTERNAL, \"%s\", msg);\r\n}\r\n\r\n\r\nstatic FILE *logfile;\r\nstatic int logprintf(const char *format, ...)\r\n{\r\n\tva_list\t\targptr;\r\n\tstatic char\t\tstring[1024];\r\n\r\n\tva_start (argptr, format);\r\n#ifdef _WIN32\r\n\t_vsnprintf (string,sizeof(string)-1, format,argptr);\r\n#else\r\n\tvsnprintf (string,sizeof(string), format,argptr);\r\n#endif\r\n\tva_end (argptr);\r\n\r\n\tfprintf(stderr, \"%s\", string);\r\n//\tfputs(string, stderr);\r\n\tif (logfile)\r\n\t\tfputs(string, logfile);\r\n\r\n\treturn 0;\r\n}\r\n\r\nstatic size_t totalsize, filecount;\r\nstatic void QCC_FileList(const char *name, const void *compdata, size_t compsize, int method, size_t plainsize)\r\n{\r\n\ttotalsize += plainsize;\r\n\tfilecount += 1;\r\n\tif (method < 0)\r\n\t{\r\n\t\tif (method == -1-9)\r\n\t\t\texterns->Printf(\"%s%8u DF64 %s%s\\n\", col_error, (unsigned)plainsize, name, col_none);\r\n\t\telse if (method == -1)\t//general error\r\n\t\t\texterns->Printf(\"%s%8u ERR %s%s\\n\", col_error, (unsigned)plainsize, name, col_none);\r\n\t\telse\r\n\t\t\texterns->Printf(\"%s%8u m%-3i %s%s\\n\", col_error, (unsigned)plainsize, -1-method, name, col_none);\r\n\t}\r\n\telse if (!method && compsize==plainsize)\r\n\t\texterns->Printf(\"%8u      %s\\n\", (unsigned)plainsize, name);\r\n\telse\r\n\t\texterns->Printf(\"%8u %3u%% %s\\n\", (unsigned)plainsize, plainsize?(unsigned)((100*compsize)/plainsize):100u, name);\r\n}\r\n#include <limits.h>\r\n#ifdef __unix__\r\n#include <sys/stat.h>\r\nvoid QCC_Mkdir(const char *path)\r\n{\r\n\tchar buf[MAX_OSPATH], *sl;\r\n\tif (!strchr(path, '/'))\r\n\t\treturn;\t//no need to create anything\r\n\tmemcpy(buf, path, MAX_OSPATH);\r\n\twhile((sl=strrchr(buf, '/')))\r\n\t{\r\n\t\t*sl = 0;\r\n\t\tmkdir(buf, 0777);\r\n\t}\r\n}\r\n#else\r\nvoid QCC_Mkdir(const char *path)\r\n{\r\n\t//unsupported.\r\n}\r\n#endif\r\n\r\nstatic char qcc_tolower(char c)\r\n{\r\n\tif (c >= 'A' && c <= 'Z')\r\n\t\treturn c-'A'+'a';\r\n\treturn c;\r\n}\r\nint qcc_wildcmp(const char *wild, const char *string)\r\n{\r\n\twhile (*string)\r\n\t{\r\n\t\tif (*wild == '*')\r\n\t\t{\r\n\t\t\tif (*string == '/' || *string == '\\\\')\r\n\t\t\t{\r\n\t\t\t\t//* terminates if we get a match on the char following it, or if its a \\ or / char\r\n\t\t\t\twild++;\r\n\t\t\t\tcontinue;\r\n\t\t\t}\r\n\t\t\tif (qcc_wildcmp(wild+1, string))\r\n\t\t\t\treturn true;\r\n\t\t\tstring++;\r\n\t\t}\r\n\t\telse if ((qcc_tolower(*wild) == qcc_tolower(*string)) || (*wild == '?'))\r\n\t\t{\r\n\t\t\t//this char matches\r\n\t\t\twild++;\r\n\t\t\tstring++;\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\t//failure\r\n\t\t\treturn false;\r\n\t\t}\r\n\t}\r\n\r\n\twhile (*wild == '*')\r\n\t{\r\n\t\twild++;\r\n\t}\r\n\treturn !*wild;\r\n}\r\n\r\nvoid AddSourceFile(const char *parentpath, const char *filename){}\t//used in the gui to insert extra stuff into the file list. irrelevant here.\r\nvoid compilecb(void){}\t//... used in the gui to repaint things while busy, so a pointless stub here.\r\nstatic void DoDecompileProgsDat(const char *name, void *blob, size_t blobsize)\r\n{\r\n//\textern pbool qcc_vfiles_changed;\r\n\textern vfile_t *qcc_vfiles;\r\n\tvfile_t *f;\r\n\tDecompileProgsDat(name, blob, blobsize);\r\n\r\n\tfor (f = qcc_vfiles; f; f = f->next)\r\n\t{\r\n\t\tint h;\r\n\t\th = SafeOpenWrite(f->filename, -1);\r\n\t\tif (h >= 0)\r\n\t\t{\r\n\t\t\tSafeWrite(h, f->file, f->size);\r\n\t\t\tSafeClose(h);\r\n\t\t}\r\n\t\telse\r\n\t\t\texterns->Printf(\"%s: write failure\\n\", f->filename);\r\n\t}\r\n}\r\n\r\nstatic const char *extractonly;\t//the file we're looking for\r\nstatic pbool extractonlyfound;\t//for errors.\r\nstatic pbool extractecho;\t//print the file to stdout instead of writing it.\r\nstatic pbool extractdecomp;\t//print the file to stdout instead of writing it.\r\nstatic void QCC_FileExtract(const char *name, const void *compdata, size_t compsize, int method, size_t plainsize)\r\n{\r\n\tif (method < 0)\r\n\t\treturn;\t//QC_decode will fail. provided for enumeration reasons.\r\n\tif (extractonly)\r\n\t{\r\n\t\tconst char *sl = strrchr(extractonly, '/');\r\n\t\tif (sl && !sl[1])\r\n\t\t{\t//trailing / - extract the entire dir.\r\n\t\t\tif (!strcmp(name, extractonly))\r\n\t\t\t\treturn;\t//ignore the dir itself...\r\n\t\t\tif (strncmp(name, extractonly, strlen(extractonly)))\r\n\t\t\t\treturn;\r\n\t\t}\r\n\t\telse\r\n\t\t\tif (!qcc_wildcmp(extractonly, name))\r\n\t\t\t\treturn;\t//ignore it if its not the one we're going for.\r\n\t}\r\n\textractonlyfound = true;\r\n\texterns->Printf(\"Extracting %s...\", name);\r\n\tif (plainsize <= INT_MAX)\r\n\t{\r\n\t\tvoid *buffer = malloc(plainsize);\r\n\t\tif (buffer && QC_decode(progfuncs, compsize, plainsize, method, compdata, buffer))\r\n\t\t{\r\n\t\t\tif (extractdecomp)\r\n\t\t\t\tDoDecompileProgsDat(name, buffer, plainsize);\r\n\t\t\telse if (extractecho)\r\n\t\t\t{\r\n\t\t\t\texterns->Printf(\"\\n\");\r\n\t\t\t\tfwrite(buffer, 1, plainsize, stdout);\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tQCC_Mkdir(name);\r\n\t\t\t\tif (!QCC_WriteFile(name, buffer, plainsize))\r\n\t\t\t\t\texterns->Printf(\" write failure\\n\");\r\n\t\t\t\telse\r\n\t\t\t\t\texterns->Printf(\" done\\n\");\r\n\t\t\t}\r\n\t\t}\r\n\t\telse\r\n\t\t\texterns->Printf(\" read failure\\n\");\r\n\r\n\t\tfree(buffer);\r\n\t}\r\n\telse\r\n\t\texterns->Printf(\" too large\\n\");\r\n}\r\n\r\nstatic void QCC_PR_PackagerMessage(void *userctx, const char *message, ...)\r\n{\r\n\tva_list\t\targptr;\r\n\tchar\t\tstring[1024];\r\n\r\n\tva_start (argptr,message);\r\n\tQC_vsnprintf (string,sizeof(string)-1,message,argptr);\r\n\tva_end (argptr);\r\n\r\n\texterns->Printf (\"%s\", string);\r\n}\r\n\r\nint main (int argc, const char **argv)\r\n{\r\n\tunsigned int i;\r\n\tpbool sucess;\r\n#if 0//def _WIN32\r\n\tpbool writelog = true;\t//spew log files on windows. windows often closes the window as soon as the program ends making its output otherwise unreadable.\r\n#else\r\n\tpbool writelog = false;\t//other systems are sane.\r\n#endif\r\n\tpbool halp = false;\r\n\tint colours = 2;\t//auto\r\n\tint ziparg = -1;\r\n\tprogexterns_t ext;\r\n\tprogfuncs_t funcs;\r\n\tprogfuncs = &funcs;\r\n\tmemset(&funcs, 0, sizeof(funcs));\r\n\tfuncs.funcs.parms = &ext;\r\n\tmemset(&ext, 0, sizeof(progexterns_t));\r\n\tfuncs.funcs.parms->ReadFile = QCC_ReadFile;\r\n\tfuncs.funcs.parms->FileSize = QCC_FileSize;\r\n\tfuncs.funcs.parms->WriteFile = QCC_WriteFile;\r\n\tfuncs.funcs.parms->Printf = logprintf;\r\n\tfuncs.funcs.parms->Sys_Error = Sys_Error;\r\n\r\n\tfor (i = 0; i < argc; i++)\r\n\t{\r\n\t\tif (!argv[i])\r\n\t\t\tcontinue;\r\n\t\tif (!strcmp(argv[i], \"-log\"))\r\n\t\t\twritelog = true;\r\n\t\telse if (!strcmp(argv[i], \"-nolog\"))\r\n\t\t\twritelog = false;\r\n\t\telse if (!strcmp(argv[i], \"-help\") || !strcmp(argv[i], \"--help\"))\r\n\t\t\thalp = true;\r\n\r\n\t\t//arg consistency with ls\r\n\t\telse if (!strcmp(argv[i], \"--color=always\") || !strcmp(argv[i], \"--color\"))\r\n\t\t\tcolours = 1;\r\n\t\telse if (!strcmp(argv[i], \"--color=never\"))\r\n\t\t\tcolours = 0;\r\n\t\telse if (!strcmp(argv[i], \"--color=auto\"))\r\n\t\t\tcolours = 2;\r\n\t\telse if (!strcmp(argv[i], \"-d\") || //o.O\r\n\t\t\t\t !strcmp(argv[i], \"-l\") ||\r\n\t\t\t\t !strcmp(argv[i], \"-x\") ||\r\n\t\t\t\t !strcmp(argv[i], \"-p\") ||\r\n\t\t\t\t !strcmp(argv[i], \"-z\") ||\r\n\t\t\t\t !strcmp(argv[i], \"-0\") ||\r\n\t\t\t\t !strcmp(argv[i], \"-9\"))\r\n\t\t{\r\n\t\t\tziparg = i;\r\n\t\t\tbreak;\t//other args are all filenames. don't misinterpret stuff.\r\n\t\t}\r\n\t}\r\n\r\n\tfor (i = 0; i < COL_MAX; i++)\r\n\t\tqcccol[i] = \"\";\r\n#if defined(__linux__) || defined(__unix__)\r\n\tif (colours == 2)\r\n\t\tcolours = isatty(STDOUT_FILENO);\r\n\tif (colours)\r\n\t{\t//only use colours if its a tty, and not if we're redirected.\r\n\t\tcol_none = \"\\e[0;m\";\t\t\t//reset to white\r\n\t\tcol_error = \"\\e[0;31m\";\t\t\t//red\r\n\t\tcol_symbol = \"\\e[0;32m\";\t\t//green\r\n\t\tcol_warning = \"\\e[0;33m\";\t\t//yellow\r\n\t\t//col_ = \"\\e[0;34m\";\t\t\t//blue\r\n\t\tcol_name = \"\\e[0;35m\";\t\t\t//magenta\r\n\t\tcol_type = \"\\e[0;36m\";\t\t\t//cyan\r\n\t\tcol_location = \"\\e[0;1;37m\";\t//bright white\r\n\t}\r\n#else\r\n\t(void)colours;\r\n#endif\r\n\r\n\tif (halp)\r\n\t{\r\n\t\tlogprintf(\"Archiving args:\\n\");\r\n\t\tlogprintf(\" -l PACKAGE : List files within a pak or pk3\\n\");\r\n\t\tlogprintf(\" -x PACKAGE [FILENAMES]: Extract files from pak or pk3\\n\");\r\n\t\tlogprintf(\" -p PACKAGE FILENAME: Pipe files from a pak or pk3 to the stdout\\n\");\r\n\t\tlogprintf(\" -z DIRECTORY : Create a spanned pk3 from a 'foo.pk3dir' subdir.\\n\");\r\n\t\tlogprintf(\"     the pk3 itself contains just the file table, actual data will reside in external .p## files which will NOT be overwritten and can be referenced by future revisions to reduce redundancy on future updates\\n\");\r\n\t\tlogprintf(\" -0 DIRECTORY : Create a hybrid pak (uncompressed)\\n\");\r\n\t\tlogprintf(\"     such pak files can also be read with any zip tool without needing special tools to extract (but should not be edited)\\n\");\r\n\t\tlogprintf(\" -9 DIRECTORY : Create a standard pk3\\n\");\r\n\t\tlogprintf(\"     regular compressed zip with limited feature set for greater engine compat\\n\");\r\n\t\tlogprintf(\"Decompiling args:\\n\");\r\n\t\tlogprintf(\" -d FILENAME : decompile a progs (into working directory)\\n\");\r\n\t}\r\n\telse if (ziparg >= 0)\r\n\t{\r\n\t\tif (ziparg+1 >= argc)\r\n\t\t{\r\n\t\t\tlogprintf(\"archive name not specified\\n\");\r\n\t\t\treturn EXIT_FAILURE;\r\n\t\t}\r\n\t\tswitch(argv[ziparg][1])\r\n\t\t{\r\n\t\tcase 'd':\t//decompile...\r\n\t\t\t{\r\n\t\t\t\tsize_t blobsize;\r\n\t\t\t\tvoid *blob = QCC_ReadFile(argv[ziparg+1], NULL, NULL, &blobsize, false);\r\n\t\t\t\tif (!blob)\r\n\t\t\t\t\tlogprintf(\"Unable to read %s\\n\", argv[ziparg+1]);\r\n\t\t\t\telse if (!strncmp(blob, \"PACK\", 4) || !strncmp(blob, \"PK\", 2))\r\n\t\t\t\t{\t//.pak or .pk3... probably.\r\n\t\t\t\t\textractonly = (ziparg+2 < argc)?argv[ziparg+2]:\"progs.dat\";\r\n\t\t\t\t\textractdecomp = true;\r\n\t\t\t\t\textractonlyfound = false;\r\n\t\t\t\t\tQC_EnumerateFilesFromBlob(blob, blobsize, QCC_FileExtract);\r\n\t\t\t\t\tif (!extractonlyfound)\r\n\t\t\t\t\t\texterns->Printf(\"Unable to find file %s inside %s\\n\", extractonly, argv[ziparg+1]);\r\n\t\t\t\t\telse\r\n\t\t\t\t\t\treturn EXIT_SUCCESS;\r\n\t\t\t\t\textractonly = NULL;\r\n\t\t\t\t}\r\n\t\t\t\telse if (blob)\r\n\t\t\t\t{\t//directly a .dat\r\n\t\t\t\t\tDoDecompileProgsDat(argv[ziparg+1], blob, blobsize);\r\n\t\t\t\t\tfree(blob);\r\n\t\t\t\t\treturn EXIT_SUCCESS;\r\n\t\t\t\t}\r\n\t\t\t\treturn EXIT_FAILURE;\r\n\t\t\t}\r\n\t\t\tbreak;\r\n\t\tcase 'l':\t//list all files.\r\n\t\t\t{\r\n\t\t\t\tsize_t blobsize;\r\n\t\t\t\tvoid *blob = QCC_ReadFile(argv[ziparg+1], NULL, NULL, &blobsize, false);\r\n\t\t\t\tif (blob)\r\n\t\t\t\t{\r\n\t\t\t\t\tQC_EnumerateFilesFromBlob(blob, blobsize, QCC_FileList);\r\n\t\t\t\t\texterns->Printf(\"Total size %lu bytes, %u files\\n\", (unsigned long)totalsize, (unsigned)filecount);\r\n\t\t\t\t\tfree(blob);\r\n\t\t\t\t\treturn EXIT_SUCCESS;\r\n\t\t\t\t}\r\n\t\t\t\tlogprintf(\"Unable to read %s\\n\", argv[ziparg+1]);\r\n\t\t\t}\r\n\t\t\tbreak;\r\n\t\tcase 'p':\t//print (named) files to stdout.\r\n\t\t\textractecho = true;\r\n\t\t\t//fall through\r\n\t\tcase 'x':\t//extract (named) files to working directory.\r\n\t\t\t{\t//list/extract/view\r\n\t\t\t\tsize_t blobsize;\r\n\t\t\t\tvoid *blob = QCC_ReadFile(argv[ziparg+1], NULL, NULL, &blobsize, false);\r\n\t\t\t\tint ret = EXIT_FAILURE;\r\n\t\t\t\tif (!blob)\r\n\t\t\t\t\tlogprintf(\"Unable to read %s\\n\", argv[ziparg+1]);\r\n\t\t\t\telse if (ziparg+2 < argc)\r\n\t\t\t\t{\r\n\t\t\t\t\tfor (i = ziparg+2; i < argc; i++)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\textractonly = argv[i];\r\n\t\t\t\t\t\textractonlyfound = false;\r\n\t\t\t\t\t\tQC_EnumerateFilesFromBlob(blob, blobsize, QCC_FileExtract);\r\n\t\t\t\t\t\tif (!extractonlyfound)\r\n\t\t\t\t\t\t\texterns->Printf(\"Unable to find file %s\\n\", extractonly);\r\n\t\t\t\t\t\telse\r\n\t\t\t\t\t\t\tret = EXIT_SUCCESS;\r\n\t\t\t\t\t}\r\n\t\t\t\t\textractonly = NULL;\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\tQC_EnumerateFilesFromBlob(blob, blobsize, QCC_FileExtract);\r\n\t\t\t\t\tret = EXIT_SUCCESS;\r\n\t\t\t\t}\r\n\t\t\t\tfree(blob);\r\n\t\t\t\treturn ret;\r\n\t\t\t}\r\n\t\tcase 'z':\t//fancy spanned stuff\r\n\t\tcase '0':\t//store-only (pak)\r\n\t\tcase '9':\t//best compression (pk3)\r\n\r\n\t\t\t{\t//exe -0 foo.pk3dir\r\n\t\t\t\tenum pkgtype_e t;\r\n\t\t\t\tif (argv[ziparg][1] == '9')\r\n\t\t\t\t\tt = PACKAGER_PK3;\r\n\t\t\t\telse if (argv[ziparg][1] == '0')\r\n\t\t\t\t\tt = PACKAGER_PAK;\t//not really any difference but oh well\r\n\t\t\t\telse\r\n\t\t\t\t\tt = PACKAGER_PK3_SPANNED;\r\n\r\n\t\t\t\tif (Packager_CompressDir(argv[ziparg+1], t, QCC_PR_PackagerMessage, NULL))\r\n\t\t\t\t\treturn EXIT_SUCCESS;\r\n\t\t\t}\r\n\t\t\tbreak;\r\n\t\tdefault:\r\n\t\t\t//should be unreachable.\r\n\t\t\tbreak;\r\n\t\t}\r\n\t\treturn EXIT_FAILURE;\r\n\t}\r\n\r\n\r\n\tlogfile = writelog?fopen(\"fteqcc.log\", \"wt\"):false;\r\n\r\n\tif (logfile)\r\n\t{\r\n\t\tfputs(\"Args:\", logfile);\r\n\t\tfor (i = 0; i < argc; i++)\r\n\t\t{\r\n\t\t\tif (!argv[i])\r\n\t\t\t\tcontinue;\r\n\t\t\tif (strchr(argv[i], ' '))\r\n\t\t\t\tfprintf(logfile, \" \\\"%s\\\"\", argv[i]);\r\n\t\t\telse\r\n\t\t\t\tfprintf(logfile, \" %s\", argv[i]);\r\n\t\t}\r\n\t\tfprintf(logfile, \"\\n\");\r\n\t}\r\n\tsucess = CompileParams(&funcs, NULL, argc, argv);\r\n\tqccClearHunk();\r\n\tif (logfile)\r\n\t\tfclose(logfile);\r\n\r\n#ifdef _WIN32\r\n//\tfgetc(stdin);\t//wait for keypress\r\n#endif\r\n\treturn sucess?EXIT_SUCCESS:EXIT_FAILURE;\r\n}\r\n"
  },
  {
    "path": "engine/qclib/qcd.h",
    "content": "pbool QC_decodeMethodSupported(int method);\nchar *QC_decode(progfuncs_t *progfuncs, int complen, int len, int method, const void *info, char *buffer);\nint QC_encode(progfuncs_t *progfuncs, int len, int method, const char *in, int handle);\nint QC_EnumerateFilesFromBlob(const void *blob, size_t blobsize, void (*cb)(const char *name, const void *compdata, size_t compsize, int method, size_t plainsize));\nint QC_encodecrc(int len, char *in);\n\nchar *PDECL filefromprogs(pubprogfuncs_t *progfuncs, progsnum_t prnum, const char *fname, size_t *size, char *buffer);\nchar *filefromnewprogs(pubprogfuncs_t *progfuncs, const char *prname, const char *fname, size_t *size, char *buffer);//fixme - remove parm 1\n\nvoid DecompileProgsDat(const char *name, void *buf, size_t bufsize);\nchar *ReadProgsCopyright(char *buf, size_t bufsize);\nint GUIprintf(const char *msg, ...);\nvoid compilecb(void);\nvoid AddSourceFile(const char *parentpath, const char *filename);\n"
  },
  {
    "path": "engine/qclib/qcd_main.c",
    "content": "#include \"progsint.h\"\n#include \"qcc.h\"\n\n#if !defined(FTE_TARGET_WEB) && !defined(_XBOX)\n#ifndef AVAIL_ZLIB\n#define AVAIL_ZLIB\n#endif\n#endif\n\n#ifdef NO_ZLIB\n#undef AVAIL_ZLIB\n#endif\n\n#ifdef AVAIL_ZLIB\n#include <zlib.h>\n#endif\n\npbool QC_decodeMethodSupported(int method)\n{\n\tif (method == 0)\n\t\treturn true;\n\tif (method == 1)\n\t\treturn true;\n\tif (method == 2)\n\t{\n#ifdef AVAIL_ZLIB\n\t\treturn false;\n#endif\n\t}\n\treturn false;\n}\n\n\n#ifdef ZLIB_DEFLATE64\n#include \"infback9.h\"\t//an obscure compile-your-own part of zlib.\nstruct def64ctx\n{\n\tconst char *in;\n\tchar *out;\n\tsize_t csize;\n\tsize_t usize;\n\n\tchar window[65536];\n};\nstatic unsigned int QC_Deflate64_Grab(void *vctx, unsigned char **bufptr)\n{\n\tstruct def64ctx *ctx = vctx;\n\n\tunsigned int avail = ctx->csize;\n\t*bufptr = (unsigned char *)ctx->in;\n\tctx->csize = 0;\n\tctx->in += avail;\n\n\treturn avail;\n}\nstatic int QC_Deflate64_Spew(void *vctx, unsigned char *buf, unsigned int buflen)\n{\n\tstruct def64ctx *ctx = vctx;\n\n\tif (buflen > ctx->usize)\n\t\treturn 1;\t//over the size of our buffer...\n\tmemcpy(ctx->out, buf, buflen);\n\tctx->out += buflen;\n\tctx->usize -= buflen;\n\treturn 0;\n}\n#endif\n\n\n\nchar *QC_decode(progfuncs_t *progfuncs, int complen, int len, int method, const void *info, char *buffer)\n{\n\tint i;\n\tif (method == 0)\t//copy\n\t{\n\t\tif (complen != len) externs->Sys_Error(\"lengths do not match\");\n\t\tmemcpy(buffer, info, len);\t\t\n\t}\n\telse if (method == 1)\t//xor encryption\n\t{\n\t\tif (complen != len) externs->Sys_Error(\"lengths do not match\");\n\t\tfor (i = 0; i < len; i++)\n\t\t\tbuffer[i] = ((const char*)info)[i] ^ 0xA5;\n\t}\n#ifdef AVAIL_ZLIB\n\telse if (method == 2 || method == 8)\t//compression (ZLIB)\n\t{\n\t\tz_stream strm = {\n\t\t\t(char*)info,\n\t\t\tcomplen,\n\t\t\t0,\n\n\t\t\tbuffer,\n\t\t\tlen,\n\t\t\t0,\n\n\t\t\tNULL,\n\t\t\tNULL,\n\n\t\t\tNULL,\n\t\t\tNULL,\n\t\t\tNULL,\n\n\t\t\tZ_BINARY,\n\t\t\t0,\n\t\t\t0\n\t\t};\n\n\t\tif (method == 8)\n\t\t\tinflateInit2(&strm, -MAX_WBITS);\n\t\telse\n\t\t\tinflateInit(&strm);\n\t\tif (Z_STREAM_END != inflate(&strm, Z_FINISH))\t//decompress it in one go.\n\t\t\texterns->Sys_Error(\"Failed block decompression\\n\");\n\t\tinflateEnd(&strm);\n\t}\n#endif\n#ifdef ZLIB_DEFLATE64\n\telse if (method == 9)\n\t{\n\t\tz_stream strm = {NULL};\n\t\tstruct def64ctx ctx;\n\t\tctx.in = info;\n\t\tctx.csize = complen;\n\t\tctx.out = buffer;\n\t\tctx.usize = len;\n\n\t\tstrm.data_type = Z_UNKNOWN;\n\t\tinflateBack9Init(&strm, ctx.window);\n\t\t//getting inflateBack9 to\n\t\tif (Z_STREAM_END != inflateBack9(&strm, QC_Deflate64_Grab, &ctx, QC_Deflate64_Spew, &ctx))\n\t\t{\t//some stream error?\n\t\t\texterns->Printf(\"Decompression error\\n\");\n\t\t\tbuffer = NULL;\n\t\t}\n\t\telse if (ctx.csize != 0 || ctx.usize != 0)\n\t\t{\t//corrupt file table?\n\t\t\texterns->Printf(\"Decompression size error\\n\");\n\t\t\texterns->Printf(\"read %i of %i bytes\\n\", (unsigned)ctx.csize, (unsigned)complen);\n\t\t\texterns->Printf(\"wrote %i of %i bytes\\n\", (unsigned)ctx.usize, (unsigned)len);\n\t\t\tbuffer = NULL;\n\t\t}\n\t\tinflateBack9End(&strm);\n\t\treturn buffer;\n\t}\n#endif\n\t//add your decryption/decompression routine here.\n\telse\n\t\texterns->Sys_Error(\"Bad file encryption routine\\n\");\n\n\n\treturn buffer;\n}\n\n#if !defined(MINIMAL) && !defined(OMIT_QCC)\nint QC_encodecrc(int len, char *in)\n{\n#ifdef AVAIL_ZLIB\n\treturn crc32(0, in, len);\n#else\n\treturn 0;\n#endif\n}\nvoid SafeWrite(int hand, const void *buf, long count);\nint SafeSeek(int hand, int ofs, int mode);\n//we are allowed to trash our input here.\nint QC_encode(progfuncs_t *progfuncs, int len, int method, const char *in, int handle)\n{\n\tif (method == 0) //copy, allows a lame pass-through.\n\t{\t\t\n\t\tSafeWrite(handle, in, len);\n\t\treturn len;\n\t}\n\t/*else if (method == 1)\t//xor encryption, not secure. maybe useful for the string table.\n\t{\n\t\tfor (i = 0; i < len; i++)\n\t\t\tin[i] = in[i] ^ 0xA5;\n\t\tSafeWrite(handle, in, len);\n\t\treturn len;\n\t}*/\n\telse if (method == 2 || method == 8)\t//compression (ZLIB)\n\t{\n#ifdef AVAIL_ZLIB\n\t\tchar out[8192];\n\t\tint i=0;\n\n\t\tz_stream strm = {\n\t\t\t(char *)in,\n\t\t\tlen,\n\t\t\t0,\n\n\t\t\tout,\n\t\t\tsizeof(out),\n\t\t\t0,\n\n\t\t\tNULL,\n\t\t\tNULL,\n\n\t\t\tNULL,\n\t\t\tNULL,\n\t\t\tNULL,\n\n\t\t\tZ_BINARY,\n\t\t\t0,\n\t\t\t0\n\t\t};\n\n\t\tif (method == 8)\n\t\t\tdeflateInit2(&strm, 9, Z_DEFLATED, -MAX_WBITS, 9, Z_DEFAULT_STRATEGY);\t\t//zip deflate compression\n\t\telse\n\t\t\tdeflateInit(&strm, Z_BEST_COMPRESSION);\t//zlib compression\n\t\twhile(deflate(&strm, Z_FINISH) == Z_OK)\n\t\t{\n\t\t\tSafeWrite(handle, out, sizeof(out) - strm.avail_out);\t//compress in chunks of 8192. Saves having to allocate a huge-mega-big buffer\n\t\t\ti+=sizeof(out) - strm.avail_out;\n\t\t\tstrm.next_out = out;\n\t\t\tstrm.avail_out = sizeof(out);\n\t\t}\n\t\tSafeWrite(handle, out, sizeof(out) - strm.avail_out);\n\t\ti+=sizeof(out) - strm.avail_out;\n\t\tdeflateEnd(&strm);\n\t\treturn i;\n#endif\n\t\texterns->Sys_Error(\"ZLIB compression not supported in this build\");\n\t\treturn 0;\n\t}\n\t//add your compression/decryption routine here.\n\telse\n\t{\n\t\texterns->Sys_Error(\"Wierd method\");\n\t\treturn 0;\n\t}\n}\n#endif\n\nstatic int QC_ReadRawInt(const unsigned char *blob)\n{\n\treturn (blob[0]<<0) | (blob[1]<<8) | (blob[2]<<16) | (blob[3]<<24);\n}\nstatic int QC_ReadRawShort(const unsigned char *blob)\n{\n\treturn (blob[0]<<0) | (blob[1]<<8);\n}\nint QC_EnumerateFilesFromBlob(const void *blob, size_t blobsize, void (*cb)(const char *name, const void *compdata, size_t compsize, int method, size_t plainsize))\n{\n\tunsigned int cdentries;\n\tunsigned int cdlen;\n\tunsigned int cdstart;\n\tunsigned int zipoffset = 0;\n\tconst unsigned char *eocd;\n\tconst unsigned char *cd;\n\tunsigned int ofs_le;\n\tunsigned int cd_nl,cd_el,cd_cl;\n\tint ret = 0;\n\n\tconst unsigned char *le;\n\tunsigned int csize, usize;\n\tint method; //negatives for errors.\n\tunsigned int le_nl,le_el;\n\tchar name[256];\n\n\tconst void *data;\n\n\tif (blobsize < 22)\n\t\treturn ret;\n\tif (!strncmp(blob, \"PACK\", 4))\n\t{\n\t\tconst packheader_t *head = blob;\n\t\tconst packfile_t *f = (packfile_t*)((char*)blob + head->dirofs);\n\t\tfor (ret = 0; ret < head->dirlen/sizeof(*f); ret++, f++)\n\t\t{\n\t\t\tcb(f->name, (const char*)blob+f->filepos, f->filelen, 0, f->filelen);\n\t\t}\n\t\treturn ret;\n\t}\n\n\t//treat it as a zip (with no comment, too lazy to scan)\n\teocd = blob;\n\teocd += blobsize-22;\n\tfor (cdlen = 0; ; eocd--, cdlen++)\n\t{\n\t\tif (cdlen > 65535 || eocd < (const unsigned char*)blob)\n\t\t{\n\t\t\tprintf(\"No zip EOCD\\n\");\n\t\t\treturn ret;\n\t\t}\n\t\tif (QC_ReadRawInt(eocd+0) == 0x06054b50)\n\t\t\tbreak;\n\t}\n\n\tif (QC_ReadRawShort(eocd+4) || QC_ReadRawShort(eocd+6) || QC_ReadRawShort(eocd+20)!=cdlen || QC_ReadRawShort(eocd+8) != QC_ReadRawShort(eocd+10))\n\t{\t\t//this-disk\t\t\t\t\tstart-disk\t\t\t\t\tcomment-length\t\t\tnumfiles_thisdisk\t\t\tnumfiles_all\n\t\tprintf(\"Unsupported zip\\n\");\n\t\treturn ret;\n\t}\n\tcdstart = QC_ReadRawInt(eocd+16);\n\tcdlen = QC_ReadRawInt(eocd+12);\n\tcdentries = QC_ReadRawShort(eocd+10);\n\tcd = blob;\n\tcd += cdstart + zipoffset;\n\tif (cdlen < 46 || cd+cdlen>=(const unsigned char*)blob+blobsize || cd[0]!='P'||cd[1]!='K'||cd[2]!=1||cd[3]!=2)\n\t{\t//cd looks corrupt? assume eocd starts right after the cd and use that as an offset at the start of the zip (concatenated onto a binary or w/e)\n\t\tzipoffset += (eocd-(const unsigned char*)blob) - (cdstart+cdlen);\n\n\t\tcd = blob;\n\t\tcd += cdstart + zipoffset;\n\n\t\tif (cdlen < 46 || zipoffset > blobsize || cd+cdlen>=(const unsigned char*)blob+blobsize || cd[0]!='P'||cd[1]!='K'||cd[2]!=1||cd[3]!=2)\n\t\t{\n\t\t\tprintf(\"Zip CentralDir not found: %s\\n\", cd);\n\t\t\treturn ret;\n\t\t}\n\t\t//okay, we're at an offset.\n\t\tprintf(\"Zip offset is %u\\n\", zipoffset);\n\t}\n\n\n\tfor(; cdentries --> 0 && (QC_ReadRawInt(cd+0) == 0x02014b50); cd += 46 + cd_nl+cd_el+cd_cl)\n\t{\n\t\tdata = NULL, csize=usize=0, method=-1;\n\n\t\tcd_nl = QC_ReadRawShort(cd+28);\t//name length\n\t\tcd_el = QC_ReadRawShort(cd+30);\t//extras length\n\t\tcd_cl = QC_ReadRawShort(cd+32);\t//comment length\n\n\t\tofs_le = QC_ReadRawInt(cd+42);\n\n\t\tif (cd_nl < sizeof(name))\t//make can't be too long...\n\t\t\tQC_strlcpy(name, cd+46, (cd_nl+1<sizeof(name))?cd_nl+1:sizeof(name));\n\t\telse\n\t\t\tQC_strlcpy(name, \"?\", sizeof(name));\n\n\t\t//1=encrypted\n\t\t//2,4=encoder flags\n\t\t//8=crc etc info is dodgy\n\t\t//10=enhanced deflate\n\t\t//20=patchdata\n\t\t//40=strong encryption\n\t\t//80,100,200,400=unused\n\t\t//800=utf-8\n\t\t//1000=enh comp\n\t\t//2000=masked localheader\n\t\t//4000,8000=reserved\n\t\tif (!(QC_ReadRawShort(cd+8) & ~0x80e))\t//only accept known cd general purpose flags\n\t\t{\n\t\t\tif (ofs_le+46 < blobsize)\n\t\t\t{\n\t\t\t\tle = (const unsigned char*)blob + zipoffset + QC_ReadRawInt(cd+42);\n\n\t\t\t\tif (QC_ReadRawInt(le+0) == 0x04034b50)\t//needs proper local entry tag\n\t\t\t\tif (!(QC_ReadRawShort(le+6) & ~0x80e))\t//ignore unsupported general purpose flags\n\t\t\t\t{\n\t\t\t\t\tle_nl = QC_ReadRawShort(le+26);\n\t\t\t\t\tle_el = QC_ReadRawShort(le+28);\n\t\t\t\t\tif (cd_nl == le_nl)\t//name (length) must match...\n//\t\t\t\t\tif (cd_el != le_el) //extras does NOT match\n\t\t\t\t\t{\n\t\t\t\t\t\tunsigned short gpflags = QC_ReadRawShort(le+6);\n\t\t\t\t\t\tif (gpflags & (1u<<3))\n\t\t\t\t\t\t{\t//stream-compressed (csize+usize+crc are AFTER the data... just fall back to the central directory instead)\n\t\t\t\t\t\t\tcsize = QC_ReadRawInt(cd+20);\n\t\t\t\t\t\t\tusize = QC_ReadRawInt(cd+24);\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcsize = QC_ReadRawInt(le+18);\n\t\t\t\t\t\t\tusize = QC_ReadRawInt(le+22);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t//parse extra\n\t\t\t\t\t\tif (le_el)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tconst pbyte *extra = le + 30 + le_nl, *extraend = extra + le_el;\n\t\t\t\t\t\t\tunsigned short extrachunk_tag;\n\t\t\t\t\t\t\tunsigned short extrachunk_len;\n\n\t\t\t\t\t\t\twhile(extra+4 < extraend)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\textrachunk_tag = QC_ReadRawShort(extra+0);\n\t\t\t\t\t\t\t\textrachunk_len = QC_ReadRawShort(extra+2);\n\t\t\t\t\t\t\t\tif (extra + extrachunk_len > extraend)\n\t\t\t\t\t\t\t\t\tbreak;\t//error\n\t\t\t\t\t\t\t\textra += 4;\n\n\t\t\t\t\t\t\t\tswitch(extrachunk_tag)\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tcase 1:\t//zip64 extended information extra field. the attributes are only present if the reegular file info is nulled out with a -1\n\t\t\t\t\t\t\t\t\tif (usize == 0xffffffffu)\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tusize = QC_ReadRawInt/*64*/(extra);\n\t\t\t\t\t\t\t\t\t\tif (QC_ReadRawInt(extra+4))\n\t\t\t\t\t\t\t\t\t\t\tmethod=-1-method;\n\t\t\t\t\t\t\t\t\t\textra += 8;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tif (csize == 0xffffffffu)\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tcsize = QC_ReadRawInt/*64*/(extra);\n\t\t\t\t\t\t\t\t\t\tif (QC_ReadRawInt(extra+4))\n\t\t\t\t\t\t\t\t\t\t\tmethod=-1-method;\n\t\t\t\t\t\t\t\t\t\textra += 8;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\tdefault:\n/*\t\t\t\t\t\t\t\t\tprintf(\"Unknown chunk %x\\n\", extrachunk_tag);\n\t\t\t\t\t\t\t\tcase 0x000a:\t//NTFS (timestamps)\n\t\t\t\t\t\t\t\tcase 0x5455:\t//extended timestamp\n\t\t\t\t\t\t\t\tcase 0x7875:\t//unix uid/gid\n*/\t\t\t\t\t\t\t\t\textra += extrachunk_len;\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tdata = le+30+le_nl+le_el;\n\n\t\t\t\t\t\tmethod = QC_ReadRawShort(le+8);\n\t\t\t\t\t\tif (method >= 0 && (method != 0\n#ifdef AVAIL_ZLIB\n\t\t\t\t\t\t\t&& method != 8\n#endif\n#ifdef ZLIB_DEFLATE64\n\t\t\t\t\t\t\t&& method != 9\n#endif\n\t\t\t\t\t\t ))\n\t\t\t\t\t\t\tmethod=-1-method;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tcb(name, data, csize, method, usize);\n\t\tret++;\n\t}\n\treturn ret;\n}\n\nchar *PDECL filefromprogs(pubprogfuncs_t *ppf, progsnum_t prnum, const char *fname, size_t *size, char *buffer)\n{\n\tprogfuncs_t *progfuncs = (progfuncs_t*)ppf;\n\tint num;\n\tincludeddatafile_t *s;\n\tif (size)\n\t\t*size = 0;\n\tif (!pr_progstate[prnum].progs)\n\t\treturn NULL;\n\tif (pr_progstate[prnum].progs->version != PROG_EXTENDEDVERSION)\n\t\treturn NULL;\n\tif (pr_progstate[prnum].progs->secondaryversion != PROG_SECONDARYVERSION16 &&\n\t\tpr_progstate[prnum].progs->secondaryversion != PROG_SECONDARYVERSION32)\n\t\treturn NULL;\n\n\tnum = *(int*)((char *)pr_progstate[prnum].progs + pr_progstate[prnum].progs->ofsfiles);\n\ts = (includeddatafile_t *)((char *)pr_progstate[prnum].progs + pr_progstate[prnum].progs->ofsfiles+4);\t\n\twhile(num>0)\n\t{\n\t\tif (!strcmp(s->filename, fname))\n\t\t{\n\t\t\tif (size)\n\t\t\t\t*size = s->size;\n\t\t\tif (!buffer)\n\t\t\t\treturn NULL;\n\t\t\treturn QC_decode(progfuncs, s->compsize, s->size, s->compmethod, (char *)pr_progstate[prnum].progs+s->ofs, buffer);\n\t\t}\n\n\t\ts++;\n\t\tnum--;\n\t}\t\n\n\tif (size)\n\t\t*size = 0;\n\treturn NULL;\n}\n\n/*\nchar *filefromnewprogs(progfuncs_t *progfuncs, const char *prname, const char *fname, int *size, char *buffer)\n{\n\tint num;\n\tincludeddatafile_t *s;\t\n\tprogstate_t progs;\n\tif (!PR_ReallyLoadProgs(progfuncs, prname, -1, &progs, false))\n\t{\n\t\tif (size)\n\t\t\t*size = 0;\n\t\treturn NULL;\n\t}\n\n\tif (progs.progs->version < PROG_EXTENDEDVERSION)\n\t\treturn NULL;\n\tif (!progs.progs->ofsfiles)\n\t\treturn NULL;\n\n\tnum = *(int*)((char *)progs.progs + progs.progs->ofsfiles);\n\ts = (includeddatafile_t *)((char *)progs.progs + progs.progs->ofsfiles+4);\t\n\twhile(num>0)\n\t{\n\t\tif (!strcmp(s->filename, fname))\n\t\t{\n\t\t\tif (size)\n\t\t\t\t*size = s->size;\n\t\t\tif (!buffer)\n\t\t\t\treturn (char *)0xffffffff;\n\t\t\treturn QC_decode(progfuncs, s->compsize, s->size, s->compmethod, (char *)progs.progs+s->ofs, buffer);\n\t\t}\n\n\t\ts++;\n\t\tnum--;\n\t}\t\n\n\tif (size)\n\t\t*size = 0;\n\treturn NULL;\n}\n*/\n"
  },
  {
    "path": "engine/qclib/qcdecomp.c",
    "content": "#if !defined(MINIMAL) && !defined(OMIT_QCC)\n\n//decompiling a progs should normally be done by walking the function table and emitting each def leading up to the one that refers to the function in question.\n//this of course assumes strict ordering\n\n//#include \"qcc.h\"\n#include \"progsint.h\"\n#include \"setjmp.h\"\n\n#define\tMAX_PARMS\t8\n\n// I put the following here to resolve \"undefined reference to `__imp__vsnprintf'\" with MinGW64 ~ Moodles\n#if 0//def _WIN32\n\t#if (_MSC_VER >= 1400)\n\t\t//with MSVC 8, use MS extensions\n\t\t#define snprintf linuxlike_snprintf_vc8\n\t\tint VARGS linuxlike_snprintf_vc8(char *buffer, int size, const char *format, ...) LIKEPRINTF(3);\n\t\t#define vsnprintf(a, b, c, d) vsnprintf_s(a, b, _TRUNCATE, c, d)\n\t#else\n\t\t//msvc crap\n\t\t#define snprintf linuxlike_snprintf\n\t\tint VARGS linuxlike_snprintf(char *buffer, int size, const char *format, ...) LIKEPRINTF(3);\n\t\t#define vsnprintf linuxlike_vsnprintf\n\t\tint VARGS linuxlike_vsnprintf(char *buffer, int size, const char *format, va_list argptr);\n\t#endif\n#endif\n\ntypedef struct QCC_type_s\n{\n\tetype_t\t\t\ttype;\n\n\tstruct QCC_type_s\t*next;\n// function types are more complex\n\tstruct QCC_type_s\t*aux_type;\t// return type or field type\n\tint\t\t\t\tnum_parms;\t// -1 = variable args\n//\tstruct QCC_type_s\t*parm_types[MAX_PARMS];\t// only [num_parms] allocated\n\n\tint ofs;\t//inside a structure.\n\tint size;\n\tchar *name;\n\n} QCC_type_t;\n\n\nextern QCC_type_t\t*qcc_typeinfo;\nextern int numtypeinfos;\nextern int maxtypeinfos;\nextern QCC_type_t\t*type_void;// = {ev_void/*, &def_void*/};\nextern QCC_type_t\t*type_string;// = {ev_string/*, &def_string*/};\nextern QCC_type_t\t*type_float;// = {ev_float/*, &def_float*/};\nextern QCC_type_t\t*type_vector;// = {ev_vector/*, &def_vector*/};\nextern QCC_type_t\t*type_entity;// = {ev_entity/*, &def_entity*/};\nextern QCC_type_t\t*type_field;// = {ev_field/*, &def_field*/};\nextern QCC_type_t\t*type_function;// = {ev_function/*, &def_function*/,NULL,&type_void};\n// type_function is a void() function used for state defs\nextern QCC_type_t\t*type_pointer;// = {ev_pointer/*, &def_pointer*/};\nextern QCC_type_t\t*type_integer;// = {ev_integer/*, &def_integer*/};\nextern QCC_type_t\t*type_floatpointer;\nextern QCC_type_t\t*type_intpointer;\n\nextern QCC_type_t\t*type_floatfield;// = {ev_field/*, &def_field*/, NULL, &type_float};\nQCC_type_t *QCC_PR_NewType (char *name, int basictype, pbool typedefed);\n\n#if 0\n\njmp_buf decompilestatementfailure;\n\nQCC_type_t **ofstype;\nqbyte *ofsflags;\n\nint SafeOpenWrite (char *filename, int maxsize);\nvoid SafeWrite(int hand, void *buf, long count);\nint SafeSeek(int hand, int ofs, int mode);\nvoid SafeClose(int hand);\nvoid VARGS writes(int hand, char *msg, ...)\n{\n\tva_list va;\n\tchar buf[4192];\n\n\tva_start(va, msg);\n\tQ_vsnprintf (buf,sizeof(buf)-1, msg, va);\n\tva_end(va);\n\n\tSafeWrite(hand, buf, strlen(buf));\n};\n\nddef16_t *ED_GlobalAtOfs16 (progfuncs_t *progfuncs, int ofs);\nchar *VarAtOfs(progfuncs_t *progfuncs, int ofs)\n{\n\tstatic char buf [4192];\n\tddef16_t *def;\n\tint typen;\n\n\tif (ofsflags[ofs]&8)\n\t\tdef = ED_GlobalAtOfs16(progfuncs, ofs);\n\telse\n\t\tdef = NULL;\n\tif (!def)\n\t{\n\t\tif (ofsflags[ofs]&3)\n\t\t{\n\t\t\tif (ofstype[ofs])\n\t\t\t\tsprintf(buf, \"_v_%s_%i\", ofstype[ofs]->name, ofs);\n\t\t\telse\n\t\t\t\tsprintf(buf, \"_v_%i\", ofs);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (ofstype[ofs])\n\t\t\t{\n\t\t\t\ttypen = ofstype[ofs]->type;\n\t\t\t\tgoto evaluateimmediate;\n\t\t\t}\n\t\t\telse\n\t\t\t\tsprintf(buf, \"_c_%i\", ofs);\n\t\t}\n\t\treturn buf;\n\t}\n\tif (!def->s_name[progfuncs->funcs.stringtable] || !strcmp(progfuncs->funcs.stringtable+def->s_name, \"IMMEDIATE\"))\n\t{\n\t\tif (current_progstate->types)\n\t\t\ttypen = current_progstate->types[def->type & ~DEF_SHARED].type;\n\t\telse\n\t\t\ttypen = def->type & ~(DEF_SHARED|DEF_SAVEGLOBAL);\n\nevaluateimmediate:\n//\t\treturn PR_UglyValueString(def->type, (eval_t *)&current_progstate->globals[def->ofs]);\n\t\tswitch(typen)\n\t\t{\n\t\tcase ev_float:\n\t\t\tsprintf(buf, \"%f\", G_FLOAT(ofs));\n\t\t\treturn buf;\n\t\tcase ev_vector:\n\t\t\tsprintf(buf, \"\\'%f %f %f\\'\", G_FLOAT(ofs), G_FLOAT(ofs+1), G_FLOAT(ofs+2));\n\t\t\treturn buf;\n\t\tcase ev_string:\n\t\t\t{\n\t\t\t\tchar *s, *s2;\n\t\t\t\ts = buf;\n\t\t\t\t*s++ = '\\\"';\n\t\t\t\ts2 = pr_strings+G_INT(ofs);\n\n\n\t\t\t\tif (s2)\n\t\t\t\twhile(*s2)\n\t\t\t\t{\n\t\t\t\t\tif (*s2 == '\\n')\n\t\t\t\t\t{\n\t\t\t\t\t\t*s++ = '\\\\';\n\t\t\t\t\t\t*s++ = 'n';\n\t\t\t\t\t\ts2++;\n\t\t\t\t\t}\n\t\t\t\t\telse if (*s2 == '\\\"')\n\t\t\t\t\t{\n\t\t\t\t\t\t*s++ = '\\\\';\n\t\t\t\t\t\t*s++ = '\\\"';\n\t\t\t\t\t\ts2++;\n\t\t\t\t\t}\n\t\t\t\t\telse if (*s2 == '\\t')\n\t\t\t\t\t{\n\t\t\t\t\t\t*s++ = '\\\\';\n\t\t\t\t\t\t*s++ = 't';\n\t\t\t\t\t\ts2++;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\t*s++=*s2++;\n\t\t\t\t}\n\t\t\t\t*s++ = '\\\"';\n\t\t\t\t*s++ = '\\0';\n\t\t\t}\n\t\t\treturn buf;\n\t\tcase ev_pointer:\n\t\t\tsprintf(buf, \"_c_pointer_%i\", ofs);\n\t\t\treturn buf;\n\t\tdefault:\n\t\t\tsprintf(buf, \"_c_%i\", ofs);\n\t\t\treturn buf;\n\t\t}\n\t}\n\treturn def->s_name+progfuncs->funcs.stringtable;\n}\n\n\nint file;\n\nint ImmediateReadLater(progfuncs_t *progfuncs, progstate_t *progs, unsigned int ofs, int firstst)\n{\n\tdstatement16_t *st;\n\tif (ofsflags[ofs] & 8)\n\t\treturn false;\t//this is a global/local/pramater, not a temp\n\tif (!(ofsflags[ofs] & 3))\n\t\treturn false;\t//this is a constant.\n\tfor (st = &((dstatement16_t*)progs->statements)[firstst]; ; st++,firstst++)\n\t{\t//if written, return false, if read, return true.\n\t\tif (st->op >= OP_CALL0 && st->op <= OP_CALL8)\n\t\t{\n\t\t\tif (ofs == OFS_RETURN)\n\t\t\t\treturn false;\n\t\t\tif (ofs < OFS_PARM0 + 3*((unsigned int)st->op - OP_CALL0))\n\t\t\t\treturn true;\n\t\t}\n\t\telse if (pr_opcodes[st->op].associative == ASSOC_RIGHT)\n\t\t{\n\t\t\tif (ofs == st->b)\n\t\t\t\treturn false;\n\t\t\tif (ofs == st->a)\n\t\t\t\treturn true;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (st->a == ofs)\n\t\t\t\treturn true;\n\t\t\tif (st->b == ofs)\n\t\t\t\treturn true;\n\t\t\tif (st->c == ofs)\n\t\t\t\treturn false;\n\t\t}\n\n\t\tif (st->op == OP_DONE || st->op == OP_RETURN)\t//we missed our chance. (return/done ends any code coherancy).\n\t\t\treturn false;\n\t}\n\treturn false;\n}\nint ProductReadLater(progfuncs_t *progfuncs, progstate_t *progs, int stnum)\n{\n\tdstatement16_t *st;\n\tst = &((dstatement16_t*)progs->statements)[stnum];\n\tif (pr_opcodes[st->op].priority == -1)\n\t{\n\t\tif (st->op >= OP_CALL0 && st->op <= OP_CALL7)\n\t\t\treturn ImmediateReadLater(progfuncs, progs, OFS_RETURN, stnum+1);\n\t\treturn false;//these don't have products...\n\t}\n\n\tif (pr_opcodes[st->op].associative == ASSOC_RIGHT)\n\t\treturn ImmediateReadLater(progfuncs, progs, st->b, stnum+1);\n\telse\n\t\treturn ImmediateReadLater(progfuncs, progs, st->c, stnum+1);\n}\n\nvoid WriteStatementProducingOfs(progfuncs_t *progfuncs, progstate_t *progs, int lastnum, int firstpossible, int ofs)\t//recursive, works backwards\n{\n\tint i;\n\tdstatement16_t *st;\n\tddef16_t *def;\n\tif (ofs == 0)\n\t\tlongjmp(decompilestatementfailure, 1);\n\tfor (; lastnum >= firstpossible; lastnum--)\n\t{\n\t\tst = &((dstatement16_t*)progs->statements)[lastnum];\n\t\tif (st->op >= OP_CALL0 && st->op < OP_CALL7)\n\t\t{\n\t\t\tif (ofs != OFS_RETURN)\n\t\t\t\tcontinue;\n\t\t\tWriteStatementProducingOfs(progfuncs, progs, lastnum-1, firstpossible, st->a);\n\t\t\twrites(file, \"(\");\n\t\t\tfor (i = 0; i < st->op - OP_CALL0; i++)\n\t\t\t{\n\t\t\t\tWriteStatementProducingOfs(progfuncs, progs, lastnum-1, firstpossible, OFS_PARM0 + i*3);\n\t\t\t\tif (i != st->op - OP_CALL0-1)\n\t\t\t\t\twrites(file, \", \");\n\t\t\t}\n\t\t\twrites(file, \")\");\n\t\t\treturn;\n\t\t}\n\t\telse if (pr_opcodes[st->op].associative == ASSOC_RIGHT)\n\t\t{\n\t\t\tif (st->b != ofs)\n\t\t\t\tcontinue;\n\t\t\tif (!ImmediateReadLater(progfuncs, progs, st->b, lastnum+1))\n\t\t\t{\n\t\t\t\twrites(file, \"(\");\n\t\t\t\tWriteStatementProducingOfs(progfuncs, progs, lastnum-1, firstpossible, st->b);\n\t\t\t\twrites(file, \" \");\n\t\t\t\twrites(file, pr_opcodes[st->op].name);\n\t\t\t\twrites(file, \" \");\n\t\t\t\tWriteStatementProducingOfs(progfuncs, progs, lastnum-1, firstpossible, st->a);\n\t\t\t\twrites(file, \")\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tWriteStatementProducingOfs(progfuncs, progs, lastnum-1, firstpossible, st->a);\n\t\t\treturn;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (st->c != ofs)\n\t\t\t\tcontinue;\n\n\t\t\tif (!ImmediateReadLater(progfuncs, progs, st->c, lastnum+1))\n\t\t\t{\n\t\t\t\tWriteStatementProducingOfs(progfuncs, progs, lastnum-1, firstpossible, st->c);\n\t\t\t\twrites(file, \" = \");\n\t\t\t}\n\t\t\twrites(file, \"(\");\n\t\t\tWriteStatementProducingOfs(progfuncs, progs, lastnum-1, firstpossible, st->a);\n\n\t\t\tif (!strcmp(pr_opcodes[st->op].name, \".\"))\n\t\t\t\twrites(file, pr_opcodes[st->op].name);\t//extra spaces around .s are ugly.\n\t\t\telse\n\t\t\t{\n\t\t\t\twrites(file, \" \");\n\t\t\t\twrites(file, pr_opcodes[st->op].name);\n\t\t\t\twrites(file, \" \");\n\t\t\t}\n\t\t\tWriteStatementProducingOfs(progfuncs, progs, lastnum-1, firstpossible, st->b);\n\t\t\twrites(file, \")\");\n\t\t\treturn;\n\t\t}\n\t}\n\n\tdef = ED_GlobalAtOfs16(progfuncs, ofs);\n\tif (def)\n\t{\n\t\tif (!strcmp(def->s_name+progfuncs->funcs.stringtable, \"IMMEDIATE\"))\n\t\t\twrites(file, \"%s\", VarAtOfs(progfuncs, ofs));\n\t\telse\n\t\t\twrites(file, \"%s\", progfuncs->funcs.stringtable+def->s_name);\n\t}\n\telse\n\t\twrites(file, \"%s\", VarAtOfs(progfuncs, ofs));\n//\t\tlongjmp(decompilestatementfailure, 1);\n}\n\nint WriteStatement(progfuncs_t *progfuncs, progstate_t *progs, int stnum, int firstpossible)\n{\n\tint count, skip;\n\tdstatement16_t *st;\n\tst = &((dstatement16_t*)progs->statements)[stnum];\n\tswitch(st->op)\n\t{\n\tcase OP_IFNOT_I:\n\t\tcount = (signed short)st->b;\n\t\twrites(file, \"if (\");\n\t\tWriteStatementProducingOfs(progfuncs, progs, stnum, firstpossible, st->a);\n\t\twrites(file, \")\\r\\n\");\n\t\twrites(file, \"{\\r\\n\");\n\t\tfirstpossible = stnum+1;\n\t\tcount--;\n\t\tstnum++;\n\t\twhile(count)\n\t\t{\n\t\t\tif (ProductReadLater(progfuncs, progs, stnum))\n\t\t\t{\n\t\t\t\tcount--;\n\t\t\t\tstnum++;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tskip = WriteStatement(progfuncs, progs, stnum, firstpossible);\n\t\t\tcount-=skip;\n\t\t\tstnum+=skip;\n\t\t}\n\t\twrites(file, \"}\\r\\n\");\n\t\tst = &((dstatement16_t*)progs->statements)[stnum];\n\t\tif (st->op == OP_GOTO)\n\t\t{\n\t\t\tcount = (signed short)st->b;\n\t\t\tcount--;\n\t\t\tstnum++;\n\n\t\t\twrites(file, \"else\\r\\n\");\n\t\t\twrites(file, \"{\\r\\n\");\n\t\t\twhile(count)\n\t\t\t{\n\t\t\t\tif (ProductReadLater(progfuncs, progs, stnum))\n\t\t\t\t{\n\t\t\t\t\tcount--;\n\t\t\t\t\tstnum++;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tskip = WriteStatement(progfuncs, progs, stnum, firstpossible);\n\t\t\t\tcount-=skip;\n\t\t\t\tstnum+=skip;\n\t\t\t}\n\t\t\twrites(file, \"}\\r\\n\");\n\t\t}\n\t\tbreak;\n\tcase OP_IF_I:\n\t\tlongjmp(decompilestatementfailure, 1);\n\t\tbreak;\n\tcase OP_GOTO:\n\t\tlongjmp(decompilestatementfailure, 1);\n\t\tbreak;\n\tcase OP_RETURN:\n\tcase OP_DONE:\n\t\tif (st->a)\n\t\t\tWriteStatementProducingOfs(progfuncs, progs, stnum-1, firstpossible, st->a);\n\t\tbreak;\n\tcase OP_CALL0:\n\tcase OP_CALL1:\n\tcase OP_CALL2:\n\tcase OP_CALL3:\n\tcase OP_CALL4:\n\tcase OP_CALL5:\n\tcase OP_CALL6:\n\tcase OP_CALL7:\n\t\tWriteStatementProducingOfs(progfuncs, progs, stnum, firstpossible, OFS_RETURN);\n\t\twrites(file, \";\\r\\n\");\n\t\tbreak;\n\tdefault:\n\t\tif (pr_opcodes[st->op].associative == ASSOC_RIGHT)\n\t\t\tWriteStatementProducingOfs(progfuncs, progs, stnum, firstpossible, st->b);\n\t\telse\n\t\t\tWriteStatementProducingOfs(progfuncs, progs, stnum, firstpossible, st->c);\n\t\twrites(file, \";\\r\\n\");\n\t\tbreak;\n\t}\n\n\treturn 1;\n}\n\nvoid WriteAsmStatements(progfuncs_t *progfuncs, progstate_t *progs, int num, int f, char *functionname)\n{\n\tint stn = progs->functions[num].first_statement;\n\tQCC_opcode_t *op;\n\tdstatement16_t *st = NULL;\n\teval_t *v;\n\n\tddef16_t *def;\n\tint ofs,i;\n\n\tif (!functionname && stn<0)\n\t{\n\t\t//we wrote this one...\n\t\treturn;\n\t}\n\n\tif (stn>=0)\n\t{\n\t\tfor (stn = progs->functions[num].first_statement; stn < (signed int)pr_progs->numstatements; stn++)\n\t\t{\n\t\t\tst = &((dstatement16_t*)progs->statements)[stn];\n\t\t\tif (st->op == OP_DONE || st->op == OP_RETURN)\n\t\t\t{\n\t\t\t\tif (!st->a)\n\t\t\t\t\twrites(f, \"void(\");\n\t\t\t\telse if (ofstype[st->a])\n\t\t\t\t{\n\t\t\t\t\twrites(f, \"%s\", ofstype[st->a]->name);\n\t\t\t\t\twrites(f, \"(\");\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\twrites(f, \"function(\");\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tst=NULL;\n\t\tstn = progs->functions[num].first_statement;\n\t}\n\telse\n\t\twrites(f, \"function(\");\n\tfor (ofs = progs->functions[num].parm_start, i = 0; i < progs->functions[num].numparms; i++, ofs+=progs->functions[num].parm_size[i])\n\t{\n\t\tofsflags[ofs] |= 4;\n\n\t\tdef = ED_GlobalAtOfs16(progfuncs, ofs);\n\t\tif (def && stn>=0)\n\t\t{\n\t\t\tif (st)\n\t\t\t\twrites(f, \", \");\n\t\t\tst = (void *)0xffff;\n\n\t\t\tif (!def->s_name[progfuncs->funcs.stringtable])\n\t\t\t{\n\t\t\t\tchar mem[64];\n\t\t\t\tsprintf(mem, \"_p_%i\", def->ofs);\n\t\t\t\tdef->s_name = (char*)malloc(strlen(mem)+1)-progfuncs->funcs.stringtable;\n\t\t\t\tstrcpy(def->s_name+progfuncs->funcs.stringtable, mem);\n\t\t\t}\n\n\t\t\tif (current_progstate->types)\n\t\t\t\twrites(f, \"%s %s\", current_progstate->types[def->type&~(DEF_SHARED|DEF_SAVEGLOBAL)].name, def->s_name);\n\t\t\telse\n\t\t\t\tswitch(def->type&~(DEF_SHARED|DEF_SAVEGLOBAL))\n\t\t\t\t{\n\t\t\t\tcase ev_string:\n\t\t\t\t\twrites(f, \"%s %s\", \"string\", progfuncs->funcs.stringtable+def->s_name);\n\t\t\t\t\tbreak;\n\t\t\t\tcase ev_float:\n\t\t\t\t\twrites(f, \"%s %s\", \"float\", progfuncs->funcs.stringtable+def->s_name);\n\t\t\t\t\tbreak;\n\t\t\t\tcase ev_entity:\n\t\t\t\t\twrites(f, \"%s %s\", \"entity\", progfuncs->funcs.stringtable+def->s_name);\n\t\t\t\t\tbreak;\n\t\t\t\tcase ev_vector:\n\t\t\t\t\twrites(f, \"%s %s\", \"vector\", progfuncs->funcs.stringtable+def->s_name);\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\twrites(f, \"%s %s\", \"randomtype\", progfuncs->funcs.stringtable+def->s_name);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t}\n\t}\n\tfor (ofs = progs->functions[num].parm_start+progs->functions[num].numparms, i = progs->functions[num].numparms; i < progs->functions[num].locals; i++, ofs+=1)\n\t\tofsflags[ofs] |= 4;\n\n\tif (!progfuncs->funcs.stringtable[progs->functions[num].s_name])\n\t{\n\t\tchar mem[64];\n\t\tif (!functionname)\n\t\t{\n\t\t\tsprintf(mem, \"_bi_%i\", num);\n\t\t\tprogs->functions[num].s_name = (char*)malloc(strlen(mem)+1)-progfuncs->funcs.stringtable;\n\t\t\tstrcpy(progs->functions[num].s_name+progfuncs->funcs.stringtable, mem);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tprogs->functions[num].s_name = (char*)malloc(strlen(functionname)+1)-progfuncs->funcs.stringtable;\n\t\t\tstrcpy(progs->functions[num].s_name+progfuncs->funcs.stringtable, functionname);\n\t\t}\n\t}\n\n\twrites(f, \") %s\", progfuncs->funcs.stringtable+progs->functions[num].s_name);\n\n\tif (stn < 0)\n\t{\n\t\tstn*=-1;\n\t\twrites(f, \" = #%i;\\r\\n\", stn);\n/*\n\t\tfor (ofs = progs->functions[num].parm_start, i = 0; i < progs->functions[num].numparms; i++, ofs+=progs->functions[num].parm_size[i])\n\t\t{\n\t\t\tdef = ED_GlobalAtOfs16(progfuncs, ofs);\n\t\t\tif (def)\n\t\t\t{\n\t\t\t\tdef->ofs = 0xffff;\n\n\t\t\t\tif (progs->types)\n\t\t\t\t{\n\t\t\t\t\tif (progs->types[def->type & ~(DEF_SHARED|DEF_SAVEGLOBAL)].type == ev_vector)\n\t\t\t\t\t{\n\t\t\t\t\t\tdef = ED_GlobalAtOfs16(progfuncs, ofs);\n\t\t\t\t\t\tdef->ofs = 0xffff;\n\t\t\t\t\t\tdef = ED_GlobalAtOfs16(progfuncs, ofs+1);\n\t\t\t\t\t\tdef->ofs = 0xffff;\n\t\t\t\t\t\tdef = ED_GlobalAtOfs16(progfuncs, ofs+2);\n\t\t\t\t\t\tdef->ofs = 0xffff;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse if ((def->type & (~(DEF_SHARED|DEF_SAVEGLOBAL))) == ev_vector)\n\t\t\t\t{\n\t\t\t\t\tdef = ED_GlobalAtOfs16(progfuncs, ofs);\n\t\t\t\t\tdef->ofs = 0xffff;\n\t\t\t\t\tdef = ED_GlobalAtOfs16(progfuncs, ofs+1);\n\t\t\t\t\tdef->ofs = 0xffff;\n\t\t\t\t\tdef = ED_GlobalAtOfs16(progfuncs, ofs+2);\n\t\t\t\t\tdef->ofs = 0xffff;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t*/\n\t\treturn;\n\t}\n\n\tif (functionname)\t//parsing defs\n\t{\n\t\twrites(f, \";\\r\\n\");\n\t\treturn;\n\t}\n\n\tif (setjmp(decompilestatementfailure))\n\t{\n\t\twrites(f, \"*/\\r\\n\");\n\t\twrites(f, \" = asm {\\r\\n\");\n\n\t\tstn = progs->functions[num].first_statement;\n\t\tfor (ofs = progs->functions[num].parm_start+progs->functions[num].numparms, i = progs->functions[num].numparms; i < progs->functions[num].locals; i++, ofs+=1)\n\t\t{\n\t\t\tdef = ED_GlobalAtOfs16(progfuncs, ofs);\n\t\t\tif (def)\n\t\t\t{\n\t\t\t\tv = (eval_t *)&((int *)progs->globals)[def->ofs];\n\t\t\t\tif (current_progstate->types)\n\t\t\t\t\twrites(f, \"\\tlocal %s %s;\\r\\n\", current_progstate->types[def->type&~(DEF_SHARED|DEF_SAVEGLOBAL)].name, def->s_name);\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tif (!progfuncs->funcs.stringtable[def->s_name])\n\t\t\t\t\t{\n\t\t\t\t\t\tchar mem[64];\n\t\t\t\t\t\tsprintf(mem, \"_l_%i\", def->ofs);\n\t\t\t\t\t\tdef->s_name = (char*)malloc(strlen(mem)+1)-progfuncs->funcs.stringtable;\n\t\t\t\t\t\tstrcpy(def->s_name+progfuncs->funcs.stringtable, mem);\n\t\t\t\t\t}\n\n\t\t\t\t\tswitch(def->type&~(DEF_SHARED|DEF_SAVEGLOBAL))\n\t\t\t\t\t{\n\t\t\t\t\tcase ev_string:\n\t\t\t\t\t\twrites(f, \"\\tlocal %s %s;\\r\\n\", \"string\", progfuncs->funcs.stringtable+def->s_name);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase ev_float:\n\t\t\t\t\t\twrites(f, \"\\tlocal %s %s;\\r\\n\", \"float\", progfuncs->funcs.stringtable+def->s_name);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase ev_entity:\n\t\t\t\t\t\twrites(f, \"\\tlocal %s %s;\\r\\n\", \"entity\", progfuncs->funcs.stringtable+def->s_name);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase ev_vector:\n\t\t\t\t\t\tif (v->_vector[0] || v->_vector[1] || v->_vector[2])\n\t\t\t\t\t\t\twrites(f, \"\\tlocal vector %s = '%f %f %f';\\r\\n\", progfuncs->funcs.stringtable+def->s_name, v->_vector[0], v->_vector[1], v->_vector[2]);\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\twrites(f, \"\\tlocal %s %s;\\r\\n\", \"vector\", progfuncs->funcs.stringtable+def->s_name);\n\t\t\t\t\t\tofs+=2;\t//skip floats;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\twrites(f, \"\\tlocal %s %s;\\r\\n\", \"randomtype\", progfuncs->funcs.stringtable+def->s_name);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\twhile(1)\n\t\t{\n\t\t\tst = &((dstatement16_t*)progs->statements)[stn];\n\t\t\tif (!st->op)\t//end of function statement!\n\t\t\t\tbreak;\n\t\t\top = &pr_opcodes[st->op];\n\t\t\twrites(f, \"\\t%s\", op->opname);\n\n\t\t\tif (op->priority==-1&&op->associative==ASSOC_RIGHT)\t//last param is a goto\n\t\t\t{\n\t\t\t\tif (op->type_b == &type_void)\n\t\t\t\t{\n\t\t\t\t\tif (st->a)\n\t\t\t\t\t\twrites(f, \" %i\", (signed short)st->a);\n\t\t\t\t}\n\t\t\t\telse if (op->type_c == &type_void)\n\t\t\t\t{\n\t\t\t\t\tif (st->a)\n\t\t\t\t\t\twrites(f, \" %s\", VarAtOfs(progfuncs, st->a));\n\t\t\t\t\tif (st->b)\n\t\t\t\t\t\twrites(f, \" %i\", (signed short)st->b);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tif (st->a)\n\t\t\t\t\t\twrites(f, \" %s\", VarAtOfs(progfuncs, st->a));\n\t\t\t\t\tif (st->b)\n\t\t\t\t\t\twrites(f, \" %s\", VarAtOfs(progfuncs, st->b));\n\t\t\t\t\tif (st->c)\t//rightness means it uses a as c\n\t\t\t\t\t\twrites(f, \" %i\", (signed short)st->c);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (st->a)\n\t\t\t\t{\n\t\t\t\t\tif (op->type_a == NULL)\n\t\t\t\t\t\twrites(f, \" %i\", (signed short)st->a);\n\t\t\t\t\telse\n\t\t\t\t\t\twrites(f, \" %s\", VarAtOfs(progfuncs, st->a));\n\t\t\t\t}\n\t\t\t\tif (st->b)\n\t\t\t\t{\n\t\t\t\t\tif (op->type_b == NULL)\n\t\t\t\t\t\twrites(f, \" %i\", (signed short)st->b);\n\t\t\t\t\telse\n\t\t\t\t\t\twrites(f, \" %s\", VarAtOfs(progfuncs, st->b));\n\t\t\t\t}\n\t\t\t\tif (st->c && op->associative != ASSOC_RIGHT)\t//rightness means it uses a as c\n\t\t\t\t{\n\t\t\t\t\tif (op->type_c == NULL)\n\t\t\t\t\t\twrites(f, \" %i\", (signed short)st->c);\n\t\t\t\t\telse\n\t\t\t\t\t\twrites(f, \" %s\", VarAtOfs(progfuncs, st->c));\n\t\t\t\t}\n\t\t\t}\n\n\t\t\twrites(f, \";\\r\\n\");\n\n\t\t\tstn++;\n\t\t}\n\t}\n\telse\n\t{\n\t\tif (!strcmp(progfuncs->funcs.stringtable+progs->functions[num].s_name, \"SUB_Remove\"))\n\t\t\tfile = 0;\n\t\tfile = f;\n\n\t\twrites(f, \"/*\\r\\n\");\n\n\t\twrites(f, \" =\\r\\n{\\r\\n\");\n\n\t\tfor (ofs = progs->functions[num].parm_start+progs->functions[num].numparms, i = progs->functions[num].numparms; i < progs->functions[num].locals; i++, ofs+=1)\n\t\t{\n\t\t\tdef = ED_GlobalAtOfs16(progfuncs, ofs);\n\t\t\tif (def)\n\t\t\t{\n\t\t\t\tv = (eval_t *)&((int *)progs->globals)[def->ofs];\n\t\t\t\tif (current_progstate->types)\n\t\t\t\t\twrites(f, \"\\tlocal %s %s;\\r\\n\", current_progstate->types[def->type&~(DEF_SHARED|DEF_SAVEGLOBAL)].name, def->s_name);\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tif (!def->s_name[progfuncs->funcs.stringtable])\n\t\t\t\t\t{\n\t\t\t\t\t\tchar mem[64];\n\t\t\t\t\t\tsprintf(mem, \"_l_%i\", def->ofs);\n\t\t\t\t\t\tdef->s_name = (char*)malloc(strlen(mem)+1)-progfuncs->funcs.stringtable;\n\t\t\t\t\t\tstrcpy(def->s_name+progfuncs->funcs.stringtable, mem);\n\t\t\t\t\t}\n\n\t\t\t\t\tswitch(def->type&~(DEF_SHARED|DEF_SAVEGLOBAL))\n\t\t\t\t\t{\n\t\t\t\t\tcase ev_string:\n\t\t\t\t\t\twrites(f, \"\\tlocal %s %s;\\r\\n\", \"string\", progfuncs->funcs.stringtable+def->s_name);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase ev_float:\n\t\t\t\t\t\twrites(f, \"\\tlocal %s %s;\\r\\n\", \"float\", progfuncs->funcs.stringtable+def->s_name);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase ev_entity:\n\t\t\t\t\t\twrites(f, \"\\tlocal %s %s;\\r\\n\", \"entity\", progfuncs->funcs.stringtable+def->s_name);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase ev_vector:\n\t\t\t\t\t\tif (v->_vector[0] || v->_vector[1] || v->_vector[2])\n\t\t\t\t\t\t\twrites(f, \"\\tlocal vector %s = '%f %f %f';\\r\\n\", progfuncs->funcs.stringtable+def->s_name, v->_vector[0], v->_vector[1], v->_vector[2]);\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\twrites(f, \"\\tlocal %s %s;\\r\\n\", \"vector\",progfuncs->funcs.stringtable+def->s_name);\n\t\t\t\t\t\tofs+=2;\t//skip floats;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\twrites(f, \"\\tlocal %s %s;\\r\\n\", \"randomtype\", progfuncs->funcs.stringtable+def->s_name);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\n\t\tfor (stn = progs->functions[num].first_statement; stn < (signed int)pr_progs->numstatements; stn++)\n\t\t{\n\t\t\tif (ProductReadLater(progfuncs, progs, stn))\n\t\t\t\tcontinue;\n\n\t\t\tst = &((dstatement16_t*)progs->statements)[stn];\n\t\t\tif (!st->op)\n\t\t\t\tbreak;\n\t\t\tWriteStatement(progfuncs, progs, stn, progs->functions[num].first_statement);\n\t\t}\n\n\t\tlongjmp(decompilestatementfailure, 1);\n\t}\n\twrites(f, \"};\\r\\n\");\n}\n\nvoid FigureOutTypes(progfuncs_t *progfuncs)\n{\n\tddef16_t\t\t*def;\n\tQCC_opcode_t *op;\n\tunsigned int i,p;\n\tdstatement16_t *st;\n\n\tint parmofs[8];\n\n\tofstype\t\t= realloc(ofstype,\t\tsizeof(*ofstype)*65535);\n\tofsflags\t= realloc(ofsflags,\tsizeof(*ofsflags)*65535);\n\n\tmaxtypeinfos=256;\n\tqcc_typeinfo = (void *)realloc(qcc_typeinfo, sizeof(QCC_type_t)*maxtypeinfos);\n\tnumtypeinfos = 0;\n\n\tmemset(ofstype,\t\t0, sizeof(*ofstype)*65535);\n\tmemset(ofsflags,\t0, sizeof(*ofsflags)*65535);\n\n\ttype_void = QCC_PR_NewType(\"void\", ev_void, true);\n\ttype_string = QCC_PR_NewType(\"string\", ev_string, true);\n\ttype_float = QCC_PR_NewType(\"float\", ev_float, true);\n\ttype_vector = QCC_PR_NewType(\"vector\", ev_vector, true);\n\ttype_entity = QCC_PR_NewType(\"entity\", ev_entity, true);\n\ttype_field = QCC_PR_NewType(\"field\", ev_field, false);\n\ttype_function = QCC_PR_NewType(\"function\", ev_function, false);\n\ttype_pointer = QCC_PR_NewType(\"pointer\", ev_pointer, false);\n\ttype_integer = QCC_PR_NewType(\"integer\", ev_integer, true);\n\n//\ttype_variant = QCC_PR_NewType(\"__variant\", ev_variant);\n\n\ttype_floatfield = QCC_PR_NewType(\"fieldfloat\", ev_field, false);\n\ttype_floatfield->aux_type = type_float;\n\ttype_pointer->aux_type = QCC_PR_NewType(\"pointeraux\", ev_float, false);\n\n\ttype_function->aux_type = type_void;\n\n\tfor (i = 0,st = pr_statements16; i < pr_progs->numstatements; i++,st++)\n\t{\n\t\top = &pr_opcodes[st->op];\n\t\tif (st->op >= OP_CALL1 && st->op <= OP_CALL8)\n\t\t{\n\t\t\tfor (p = 0; p < (unsigned int)st->op-OP_CALL0; p++)\n\t\t\t{\n\t\t\t\tofstype[parmofs[p]] = ofstype[OFS_PARM0+p*3];\n\t\t\t}\n\t\t}\n\t\telse if (op->associative == ASSOC_RIGHT)\n\t\t{\t//assignment\n\t\t\tofsflags[st->b] |= 1;\n\t\t\tif (st->b >= OFS_PARM0 && st->b < RESERVED_OFS)\n\t\t\t\tparmofs[(st->b-OFS_PARM0)/3] = st->a;\n\n//\t\t\tif (st->op != OP_STORE_F || st->b>RESERVED_OFS)\t//optimising compilers fix the OP_STORE_V, it's the storef that becomes meaningless (this is the only time that we need this sort of info anyway)\n\t\t\t{\n\t\t\t\tif (op->type_c && op->type_c != &type_void)\n\t\t\t\t\tofstype[st->a] = *op->type_c;\n\t\t\t\tif (op->type_b && op->type_b != &type_void)\n\t\t\t\t\tofstype[st->b] = *op->type_b;\n\t\t\t}\n\t\t}\n\t\telse if (op->type_c)\n\t\t{\n\t\t\tofsflags[st->c] |= 2;\n\n\t\t\tif (st->c >= OFS_PARM0 && st->b < RESERVED_OFS)\t//too complicated\n\t\t\t\tparmofs[(st->b-OFS_PARM0)/3] = 0;\n\n//\t\t\tif (st->op != OP_STORE_F || st->b>RESERVED_OFS)\t//optimising compilers fix the OP_STORE_V, it's the storef that becomes meaningless (this is the only time that we need this sort of info anyway)\n\t\t\t{\n\t\t\t\tif (op->type_a && op->type_a != &type_void)\n\t\t\t\t\tofstype[st->a] = *op->type_a;\n\t\t\t\tif (op->type_b && op->type_b != &type_void)\n\t\t\t\t\tofstype[st->b] = *op->type_b;\n\t\t\t\tif (op->type_c && op->type_c != &type_void)\n\t\t\t\t\tofstype[st->c] = *op->type_c;\n\t\t\t}\n\t\t}\n\t}\n\n\n\tfor (i=0 ; i<pr_progs->numglobaldefs ; i++)\n\t{\n\t\tdef = &pr_globaldefs16[i];\n\t\tofsflags[def->ofs] |= 8;\n\t\tswitch(def->type)\n\t\t{\n\t\tcase ev_float:\n\t\t\tofstype[def->ofs] = type_float;\n\t\t\tbreak;\n\t\tcase ev_string:\n\t\t\tofstype[def->ofs] = type_string;\n\t\t\tbreak;\n\t\tcase ev_vector:\n\t\t\tofstype[def->ofs] = type_vector;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tbreak;\n\t\t}\n\t}\n}\n\npbool PDECL QC_Decompile(pubprogfuncs_t *ppf, char *fname)\n{\n\tprogfuncs_t *progfuncs = (progfuncs_t*)ppf;\n\textern progfuncs_t *qccprogfuncs;\n\tunsigned int i;\n\tunsigned int fld=0;\n\teval_t *v;\n//\tchar *filename;\n\tint f, type;\n\n\tprogstate_t progs, *op;\n\n\tqccprogfuncs = progfuncs;\n\top=current_progstate;\n\n\tif (!PR_ReallyLoadProgs(progfuncs, fname, &progs, false))\n\t{\n\t\treturn false;\n\t}\n\n\tf=SafeOpenWrite(\"qcdtest/defs.qc\", 1024*512);\n\n\twrites(f, \"//Decompiled code can contain little type info.\\r\\n\");\n\n\tFigureOutTypes(progfuncs);\n\n\tfor (i = 1; i < progs.progs->numglobaldefs; i++)\n\t{\n\t\tif (!strcmp(progfuncs->funcs.stringtable+pr_globaldefs16[i].s_name, \"IMMEDIATE\"))\n\t\t\tcontinue;\n\n\t\tif (ofsflags[pr_globaldefs16[i].ofs] & 4)\n\t\t\tcontinue;\t//this is a local.\n\n\t\tif (current_progstate->types)\n\t\t\ttype = progs.types[pr_globaldefs16[i].type & ~(DEF_SHARED|DEF_SAVEGLOBAL)].type;\n\t\telse\n\t\t\ttype = pr_globaldefs16[i].type & ~(DEF_SHARED|DEF_SAVEGLOBAL);\n\t\tv = (eval_t *)&((int *)progs.globals)[pr_globaldefs16[i].ofs];\n\n\t\tif (!progfuncs->funcs.stringtable[pr_globaldefs16[i].s_name])\n\t\t{\n\t\t\tchar mem[64];\n\t\t\tif (ofsflags[pr_globaldefs16[i].ofs] & 3)\n\t\t\t{\n\t\t\t\tofsflags[pr_globaldefs16[i].ofs] &= ~8;\n\t\t\t\tcontinue;\t//this is a constant...\n\t\t\t}\n\n\t\t\tsprintf(mem, \"_g_%i\", pr_globaldefs16[i].ofs);\n\t\t\tpr_globaldefs16[i].s_name = (char*)malloc(strlen(mem)+1)-progfuncs->funcs.stringtable;\n\t\t\tstrcpy(pr_globaldefs16[i].s_name+progfuncs->funcs.stringtable, mem);\n\t\t}\n\n\t\tswitch(type)\n\t\t{\n\t\tcase ev_void:\n\t\t\twrites(f, \"void %s;\\r\\n\", progfuncs->funcs.stringtable+pr_globaldefs16[i].s_name);\n\t\t\tbreak;\n\t\tcase ev_string:\n\t\t\tif (v->string && *(pr_strings+v->_int))\n\t\t\t\twrites(f, \"string %s = \\\"%s\\\";\\r\\n\", progfuncs->funcs.stringtable+pr_globaldefs16[i].s_name, pr_strings+v->_int);\n\t\t\telse\n\t\t\t\twrites(f, \"string %s;\\r\\n\", progfuncs->funcs.stringtable+pr_globaldefs16[i].s_name);\n\t\t\tbreak;\n\t\tcase ev_float:\n\t\t\tif (v->_float)\n\t\t\t\twrites(f, \"float %s = %f;\\r\\n\", progfuncs->funcs.stringtable+pr_globaldefs16[i].s_name, v->_float);\n\t\t\telse\n\t\t\t\twrites(f, \"float %s;\\r\\n\", progfuncs->funcs.stringtable+pr_globaldefs16[i].s_name);\n\t\t\tbreak;\n\t\tcase ev_vector:\n\t\t\tif (v->_vector[0] || v->_vector[1] || v->_vector[2])\n\t\t\t\twrites(f, \"vector %s = '%f %f %f';\\r\\n\", progfuncs->funcs.stringtable+pr_globaldefs16[i].s_name, v->_vector[0], v->_vector[1], v->_vector[2]);\n\t\t\telse\n\t\t\t\twrites(f, \"vector %s;\\r\\n\", progfuncs->funcs.stringtable+pr_globaldefs16[i].s_name);\n\t\t\ti+=3;//skip the floats\n\t\t\tbreak;\n\t\tcase ev_entity:\n\t\t\twrites(f, \"entity %s;\\r\\n\", progfuncs->funcs.stringtable+pr_globaldefs16[i].s_name);\n\t\t\tbreak;\n\t\tcase ev_field:\n//wierd\n\t\t\tfld++;\n\t\t\tif (!v->_int)\n\t\t\t\twrites(f, \"var \");\n\t\t\tswitch(pr_fielddefs16[fld].type)\n\t\t\t{\n\t\t\tcase ev_string:\n\t\t\t\twrites(f, \".string %s;\", progfuncs->funcs.stringtable+pr_globaldefs16[i].s_name);\n\t\t\t\tbreak;\n\n\t\t\tcase ev_float:\n\t\t\t\twrites(f, \".float %s;\", progfuncs->funcs.stringtable+pr_globaldefs16[i].s_name);\n\t\t\t\tbreak;\n\n\t\t\tcase ev_vector:\n\t\t\t\twrites(f, \".float %s;\", progfuncs->funcs.stringtable+pr_globaldefs16[i].s_name);\n\t\t\t\tbreak;\n\n\t\t\tcase ev_entity:\n\t\t\t\twrites(f, \".float %s;\", progfuncs->funcs.stringtable+pr_globaldefs16[i].s_name);\n\t\t\t\tbreak;\n\n\t\t\tcase ev_function:\n\t\t\t\twrites(f, \".void() %s;\", progfuncs->funcs.stringtable+pr_globaldefs16[i].s_name);\n\t\t\t\tbreak;\n\n\t\t\tdefault:\n\t\t\t\twrites(f, \"field %s;\", progfuncs->funcs.stringtable+pr_globaldefs16[i].s_name);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (v->_int)\n\t\t\t\twrites(f, \"/* %i */\", v->_int);\n\t\t\twrites(f, \"\\r\\n\");\n\t\t\tbreak;\n\n\t\tcase ev_function:\n//wierd\n\t\t\tWriteAsmStatements(progfuncs, &progs, ((int *)progs.globals)[pr_globaldefs16[i].ofs], f, pr_globaldefs16[i].s_name+progfuncs->funcs.stringtable);\n\t\t\tbreak;\n\n\t\tcase ev_pointer:\n\t\t\twrites(f, \"pointer %s;\\r\\n\", progfuncs->funcs.stringtable+pr_globaldefs16[i].s_name);\n\t\t\tbreak;\n\t\tcase ev_integer:\n\t\t\twrites(f, \"integer %s;\\r\\n\", progfuncs->funcs.stringtable+pr_globaldefs16[i].s_name);\n\t\t\tbreak;\n\n\t\tcase ev_union:\n\t\t\twrites(f, \"union %s;\\r\\n\", progfuncs->funcs.stringtable+pr_globaldefs16[i].s_name);\n\t\t\tbreak;\n\t\tcase ev_struct:\n\t\t\twrites(f, \"struct %s;\\r\\n\", progfuncs->funcs.stringtable+pr_globaldefs16[i].s_name);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tbreak;\n\n\t\t}\n\t}\n\n\tfor (i = 0; i < progs.progs->numfunctions; i++)\n\t{\n\t\tWriteAsmStatements(progfuncs, &progs, i, f, NULL);\n\t}\n\n\tSafeClose(f);\n\n\tcurrent_progstate=op;\n\n\treturn true;\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "engine/qclib/readme.txt",
    "content": "Readme for the FTE QCLib\n\nThis library is a library for running QuakeC gamecode. It does not provide any builtins itself.\n\nFeatures:\n\t* Multiple library instances, enabling server qc, client qc, and menu qc. There is no maximum instance limit other than memory.\n\n\t* Addons, for running multiple progs in any individual instance.\n\n\t* Field reassignment, allowing a single engine to support multiple subtly different QC APIs. Also makes additional fields easier.\n\n\t* Step-by-step debugging. Requires a text editor of some form, however. A printout of the current line is also useful of course.\n\n\t* 64bit support. All strings, globals, and fields are allocated in a consecutive addressable section of memory. This also allows pointers and secure access (not implemented yet, but should be relativly easy bar builtins, which are your responsability).\n\n\t* Multiple 'threads'. The library allows a builtin to make a duplicate of the current execution state, or to wipe the current state. This allows sleep commands and fork commands. How handy.\n\n\t* Integrated QC compiler. FTEQCC comes as part of qclib. By setting up an interface with a specific value, you can cause it to always run, or run only if it detects a source change.\n\n\t* Support for different sorts of progs. Namly Hexen2's, kkqwsv's bigprogs, and FTE's extended format with extra opcodes and possibly fully 32bit offsets. The use of kkqwsv's progs is not recommended - this might be removed at some point.\n\n\n\n\nQuirks:\n\t* don't use multiple instances of fteqcc at the same time. Compilation will fail.\n\t* 64bit support requires all strings to be allocated by qclib itself, achivable via a method call. Compatability requires a certain ammount of caution.\n\t* a fair number of methods are obsolete.\n\t* An overuse of pointers in the API. There are some macros which you can use to hide some of the dereferences.\n\t* kkqwsv progs are not reliable. Do not try saving the game. Avoid letting your users know of support.\n\t\n\t* Builtin structures are different from original quake. You'll need to convert the arguments to qclib style. This change was required for both multiple instances as well as addon support. It should be straightforward enough.\n\t* Entity fields are accessed via a pointer from the edict_t structure. This was required to place entity fields within the 64bit accessable section. Changing a . to a -> is not a major issue though. However, there are a lot. do a find and replace of ->v. to ->v->\n\t* FTE's entities are numbers not pointers. This fact is not made into a big feature as it's kinda incompatable with standard quake. Please do not use numbers directly to refer to ents but instead use the EDICT_TO_PROGS macro which will give protection. This is consistant with standard quake.\n\n\nBasic usage:\n\t* refer to test.c for a sample on how to set up the library.\n\t* refer to progslib.h for the things that I've forgotten to mention.\n\t* Call the InitProgs function to get a handle to the instance. It takes a parameter which should be set up with some fields. You'll require ReadFile, FileSize, Abort and printf for basic execution.\n\t* Call the configure function to say how much memory to use, and how many progs/addons to support.\n\t* Load your progs via LoadProgs. Use a crc of 0 to use any. Otherwise progs will be rejected if it doesn't match. Give it a list of progs-specific builtins too. :)\n\t* Before calling the spawn builtin, call the InitEnts method. It's parameter stating how many maximum entities to spawn. Using a really large quantity is not much of an issue, as they are allocated as required.\n\t* Before calling InitEnts, you can tell the VM which fields your engine uses (state all basic ones or none). This will place the entity fields in the same order as your engine expects for entvars_t.\n\t* Obtain pointers to globals, or just use the globals structure directly.\n\t* Call the ExecuteProgram method to start execution.\n\t* Call the FindFunction method to find a function to run in the first place.\n\t* Call the 'globals' method to retrieve a pointer to the globals (you should always use PR_CURRENT here). Set the parameters with the G_INT/G_FLOAT macros and friends. Use OFS_PARM0 - OFS_PARM7 to set params before calling or read inside a builtin. Use OFS_RETURN to read the return value. These macros are hard coded to use a 'pr_globals' symbol, so avoid renaming builtin parameter names.\n\t* Ask me on IRC when it all starts keeling over.\n\t* These are the C files that form qclib: pr_edict.c pr_exec.c pr_multi.c initlib.c qcc_pr_comp.c qcc_pr_lex.c qccmain.c qcc_cmdlib.c comprout.c hash.c qcd_main.c qcdecomp.c\n"
  },
  {
    "path": "engine/qclib/test.c",
    "content": "//This is basically a sample program.\n//It deomnstrates the code required to get qclib up and running.\n//This code does not demonstrate entities, however.\n//It does demonstrate the built in qc compiler, and does demonstrate a globals-only progs interface.\n//It also demonstrates basic builtin(s).\n\n\n\n#include \"progtype.h\"\n#include \"progslib.h\"\n\n#include <stdlib.h>\n#include <stdio.h>\n#include <string.h>\n#include <stdarg.h>\n#include <math.h>\n\nenum{false,true};\n\n\n//builtins and builtin management.\nvoid PF_puts (pubprogfuncs_t *prinst, struct globalvars_s *gvars)\n{\n\tchar *s;\n\ts = prinst->VarString(prinst, 0);\n\n\tprintf(\"%s\", s);\n}\nvoid PF_strcat (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tG_INT(OFS_RETURN) = prinst->TempString(prinst, prinst->VarString(prinst, 0));\n}\nvoid PF_ftos (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tchar temp[64];\n\tsprintf(temp, \"%g\", G_FLOAT(OFS_PARM0));\n\tG_INT(OFS_RETURN) = prinst->TempString(prinst, temp);\n}\nvoid PF_vtos (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tchar temp[64];\n\tsprintf(temp, \"'%g %g %g'\", G_VECTOR(OFS_PARM0)[0], G_VECTOR(OFS_PARM0)[1], G_VECTOR(OFS_PARM0)[2]);\n\tG_INT(OFS_RETURN) = prinst->TempString(prinst, temp);\n}\nvoid PF_etos (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tchar temp[64];\n\tsprintf(temp, \"%i\", G_INT(OFS_PARM0));\n\tG_INT(OFS_RETURN) = prinst->TempString(prinst, temp);\n}\nvoid PF_itos (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tchar temp[64];\n\tsprintf(temp, \"%x\", G_INT(OFS_PARM0));\n\tG_INT(OFS_RETURN) = prinst->TempString(prinst, temp);\n}\nvoid PF_ltos (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tchar temp[64];\n\tsprintf(temp, \"%\"PRIx64, G_INT64(OFS_PARM0));\n\tG_INT(OFS_RETURN) = prinst->TempString(prinst, temp);\n}\nvoid PF_dtos (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tchar temp[64];\n\tsprintf(temp, \"%g\", G_DOUBLE(OFS_PARM0));\n\tG_INT(OFS_RETURN) = prinst->TempString(prinst, temp);\n}\nvoid PF_stof (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tG_FLOAT(OFS_RETURN) = strtod(PR_GetStringOfs(prinst, OFS_PARM0), NULL);\n}\nvoid PF_stov (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tsscanf(PR_GetStringOfs(prinst, OFS_PARM0), \" ' %f %f %f ' \",\n\t\t&G_FLOAT(OFS_RETURN+0),\n\t\t&G_FLOAT(OFS_RETURN+1),\n\t\t&G_FLOAT(OFS_RETURN+2));\n}\nvoid PF_strcmp (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tif (prinst->callargc >= 3)\n\t\tG_FLOAT(OFS_RETURN) = strncmp(PR_GetStringOfs(prinst, OFS_PARM0), PR_GetStringOfs(prinst, OFS_PARM1), G_FLOAT(OFS_PARM2));\n\telse\n\t\tG_FLOAT(OFS_RETURN) = strcmp(PR_GetStringOfs(prinst, OFS_PARM0), PR_GetStringOfs(prinst, OFS_PARM1));\n}\nvoid PF_vlen (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tfloat *v = G_VECTOR(OFS_PARM0);\n\tG_FLOAT(OFS_RETURN) = sqrt(v[0]*v[0]+v[1]*v[1]+v[2]*v[2]);\n}\nvoid PF_normalize (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tfloat *v = G_VECTOR(OFS_PARM0);\n\tdouble l = sqrt(v[0]*v[0]+v[1]*v[1]+v[2]*v[2]);\n\tif (l)\n\t\tl = 1./l;\n\telse\n\t\tl = 0;\n\tG_VECTOR(OFS_RETURN)[0] = v[0] * l;\n\tG_VECTOR(OFS_RETURN)[1] = v[1] * l;\n\tG_VECTOR(OFS_RETURN)[2] = v[2] * l;\n}\nvoid PF_floor (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tG_FLOAT(OFS_RETURN) = floor(G_FLOAT(OFS_PARM0));\n}\nvoid PF_pow (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tG_FLOAT(OFS_RETURN) = pow(G_FLOAT(OFS_PARM0), G_FLOAT(OFS_PARM1));\n}\nvoid PF_sqrt (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tG_FLOAT(OFS_RETURN) = sqrt(G_FLOAT(OFS_PARM0));\n}\n\nvoid PF_putv (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tprintf(\"%f %f %f\\n\", G_FLOAT(OFS_PARM0+0), G_FLOAT(OFS_PARM0+1), G_FLOAT(OFS_PARM0+2));\n}\n\nvoid PF_putf (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tprintf(\"%f\\n\", G_FLOAT(OFS_PARM0));\n}\n\n#ifdef _WIN32\n\t#define Q_snprintfz _snprintf\n\t#define Q_vsnprintf _vsnprintf\n#else\n\t#define Q_snprintfz snprintf\n\t#define Q_vsnprintf vsnprintf\n#endif\nchar\t*va(char *format, ...)\n{\n\tva_list\t\targptr;\n\tstatic char\t\tstring[1024];\n\tva_start (argptr, format);\n\tQ_vsnprintf (string, sizeof(string), format,argptr);\n\tva_end (argptr);\n\treturn string;\t\n}\n\nvoid QCBUILTIN PF_sprintf_internal (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals, const char *s, int firstarg, char *outbuf, int outbuflen)\n{\n\tconst char *s0;\n\tchar *o = outbuf, *end = outbuf + outbuflen, *err;\n\tint width, precision, thisarg, flags;\n\tchar formatbuf[16];\n\tchar *f;\n\tint argpos = firstarg;\n\tint isfloat;\n\tstatic int dummyivec[3] = {0, 0, 0};\n\tstatic float dummyvec[3] = {0, 0, 0};\n\n#define PRINTF_ALTERNATE 1\n#define PRINTF_ZEROPAD 2\n#define PRINTF_LEFT 4\n#define PRINTF_SPACEPOSITIVE 8\n#define PRINTF_SIGNPOSITIVE 16\n\n\tformatbuf[0] = '%';\n\n#define GETARG_FLOAT(a) (((a)>=firstarg && (a)<prinst->callargc) ? (G_FLOAT(OFS_PARM0 + 3 * (a))) : 0)\n#define GETARG_VECTOR(a) (((a)>=firstarg && (a)<prinst->callargc) ? (G_VECTOR(OFS_PARM0 + 3 * (a))) : dummyvec)\n#define GETARG_INT(a) (((a)>=firstarg && (a)<prinst->callargc) ? (G_INT(OFS_PARM0 + 3 * (a))) : 0)\n#define GETARG_INTVECTOR(a) (((a)>=firstarg && (a)<prinst->callargc) ? ((int*) G_VECTOR(OFS_PARM0 + 3 * (a))) : dummyivec)\n#define GETARG_STRING(a) (((a)>=firstarg && (a)<prinst->callargc) ? (PR_GetStringOfs(prinst, OFS_PARM0 + 3 * (a))) : \"\")\n\n\tfor(;;)\n\t{\n\t\ts0 = s;\n\t\tswitch(*s)\n\t\t{\n\t\t\tcase 0:\n\t\t\t\tgoto finished;\n\t\t\tcase '%':\n\t\t\t\t++s;\n\n\t\t\t\tif(*s == '%')\n\t\t\t\t\tgoto verbatim;\n\n\t\t\t\t// complete directive format:\n\t\t\t\t// %3$*1$.*2$ld\n\t\t\t\t\n\t\t\t\twidth = -1;\n\t\t\t\tprecision = -1;\n\t\t\t\tthisarg = -1;\n\t\t\t\tflags = 0;\n\t\t\t\tisfloat = -1;\n\n\t\t\t\t// is number following?\n\t\t\t\tif(*s >= '0' && *s <= '9')\n\t\t\t\t{\n\t\t\t\t\twidth = strtol(s, &err, 10);\n\t\t\t\t\tif(!err)\n\t\t\t\t\t{\n\t\t\t\t\t\tprintf(\"PF_sprintf: bad format string: %s\\n\", s0);\n\t\t\t\t\t\tgoto finished;\n\t\t\t\t\t}\n\t\t\t\t\tif(*err == '$')\n\t\t\t\t\t{\n\t\t\t\t\t\tthisarg = width + (firstarg-1);\n\t\t\t\t\t\twidth = -1;\n\t\t\t\t\t\ts = err + 1;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tif(*s == '0')\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tflags |= PRINTF_ZEROPAD;\n\t\t\t\t\t\t\tif(width == 0)\n\t\t\t\t\t\t\t\twidth = -1; // it was just a flag\n\t\t\t\t\t\t}\n\t\t\t\t\t\ts = err;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif(width < 0)\n\t\t\t\t{\n\t\t\t\t\tfor(;;)\n\t\t\t\t\t{\n\t\t\t\t\t\tswitch(*s)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase '#': flags |= PRINTF_ALTERNATE; break;\n\t\t\t\t\t\t\tcase '0': flags |= PRINTF_ZEROPAD; break;\n\t\t\t\t\t\t\tcase '-': flags |= PRINTF_LEFT; break;\n\t\t\t\t\t\t\tcase ' ': flags |= PRINTF_SPACEPOSITIVE; break;\n\t\t\t\t\t\t\tcase '+': flags |= PRINTF_SIGNPOSITIVE; break;\n\t\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\t\tgoto noflags;\n\t\t\t\t\t\t}\n\t\t\t\t\t\t++s;\n\t\t\t\t\t}\nnoflags:\n\t\t\t\t\tif(*s == '*')\n\t\t\t\t\t{\n\t\t\t\t\t\t++s;\n\t\t\t\t\t\tif(*s >= '0' && *s <= '9')\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\twidth = strtol(s, &err, 10);\n\t\t\t\t\t\t\tif(!err || *err != '$')\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tprintf(\"PF_sprintf: invalid format string: %s\\n\", s0);\n\t\t\t\t\t\t\t\tgoto finished;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\ts = err + 1;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\twidth = argpos++;\n\t\t\t\t\t\twidth = GETARG_FLOAT(width);\n\t\t\t\t\t\tif(width < 0)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tflags |= PRINTF_LEFT;\n\t\t\t\t\t\t\twidth = -width;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\telse if(*s >= '0' && *s <= '9')\n\t\t\t\t\t{\n\t\t\t\t\t\twidth = strtol(s, &err, 10);\n\t\t\t\t\t\tif(!err)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tprintf(\"PF_sprintf: invalid format string: %s\\n\", s0);\n\t\t\t\t\t\t\tgoto finished;\n\t\t\t\t\t\t}\n\t\t\t\t\t\ts = err;\n\t\t\t\t\t\tif(width < 0)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tflags |= PRINTF_LEFT;\n\t\t\t\t\t\t\twidth = -width;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t// otherwise width stays -1\n\t\t\t\t}\n\n\t\t\t\tif(*s == '.')\n\t\t\t\t{\n\t\t\t\t\t++s;\n\t\t\t\t\tif(*s == '*')\n\t\t\t\t\t{\n\t\t\t\t\t\t++s;\n\t\t\t\t\t\tif(*s >= '0' && *s <= '9')\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tprecision = strtol(s, &err, 10);\n\t\t\t\t\t\t\tif(!err || *err != '$')\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tprintf(\"PF_sprintf: invalid format string: %s\\n\", s0);\n\t\t\t\t\t\t\t\tgoto finished;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\ts = err + 1;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tprecision = argpos++;\n\t\t\t\t\t\tprecision = GETARG_FLOAT(precision);\n\t\t\t\t\t}\n\t\t\t\t\telse if(*s >= '0' && *s <= '9')\n\t\t\t\t\t{\n\t\t\t\t\t\tprecision = strtol(s, &err, 10);\n\t\t\t\t\t\tif(!err)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tprintf(\"PF_sprintf: invalid format string: %s\\n\", s0);\n\t\t\t\t\t\t\tgoto finished;\n\t\t\t\t\t\t}\n\t\t\t\t\t\ts = err;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tprintf(\"PF_sprintf: invalid format string: %s\\n\", s0);\n\t\t\t\t\t\tgoto finished;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tfor(;;)\n\t\t\t\t{\n\t\t\t\t\tswitch(*s)\n\t\t\t\t\t{\n\t\t\t\t\t\tcase 'h': isfloat = 1; break;\n\t\t\t\t\t\tcase 'l': isfloat = 0; break;\n\t\t\t\t\t\tcase 'L': isfloat = 0; break;\n\t\t\t\t\t\tcase 'j': break;\n\t\t\t\t\t\tcase 'z': break;\n\t\t\t\t\t\tcase 't': break;\n\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\tgoto nolength;\n\t\t\t\t\t}\n\t\t\t\t\t++s;\n\t\t\t\t}\nnolength:\n\n\t\t\t\t// now s points to the final directive char and is no longer changed\n\t\t\t\tif (*s == 'p' || *s == 'P')\n\t\t\t\t{\n\t\t\t\t\t//%p is slightly different from %x.\n\t\t\t\t\t//always 8-bytes wide with 0 padding, always ints.\n\t\t\t\t\tflags |= PRINTF_ZEROPAD;\n\t\t\t\t\tif (width < 0) width = 8;\n\t\t\t\t\tif (isfloat < 0) isfloat = 0;\n\t\t\t\t}\n\t\t\t\telse if (*s == 'i')\n\t\t\t\t{\n\t\t\t\t\t//%i defaults to ints, not floats.\n\t\t\t\t\tif(isfloat < 0) isfloat = 0;\n\t\t\t\t}\n\n\t\t\t\t//assume floats, not ints.\n\t\t\t\tif(isfloat < 0)\n\t\t\t\t\tisfloat = 1;\n\n\t\t\t\tif(thisarg < 0)\n\t\t\t\t\tthisarg = argpos++;\n\n\t\t\t\tif(o < end - 1)\n\t\t\t\t{\n\t\t\t\t\tf = &formatbuf[1];\n\t\t\t\t\tif(*s != 's' && *s != 'c')\n\t\t\t\t\t\tif(flags & PRINTF_ALTERNATE) *f++ = '#';\n\t\t\t\t\tif(flags & PRINTF_ZEROPAD) *f++ = '0';\n\t\t\t\t\tif(flags & PRINTF_LEFT) *f++ = '-';\n\t\t\t\t\tif(flags & PRINTF_SPACEPOSITIVE) *f++ = ' ';\n\t\t\t\t\tif(flags & PRINTF_SIGNPOSITIVE) *f++ = '+';\n\t\t\t\t\t*f++ = '*';\n\t\t\t\t\tif(precision >= 0)\n\t\t\t\t\t{\n\t\t\t\t\t\t*f++ = '.';\n\t\t\t\t\t\t*f++ = '*';\n\t\t\t\t\t}\n\t\t\t\t\tif (*s == 'p')\n\t\t\t\t\t\t*f++ = 'x';\n\t\t\t\t\telse if (*s == 'P')\n\t\t\t\t\t\t*f++ = 'X';\n\t\t\t\t\telse\n\t\t\t\t\t\t*f++ = *s;\n\t\t\t\t\t*f++ = 0;\n\n\t\t\t\t\tif(width < 0) // not set\n\t\t\t\t\t\twidth = 0;\n\n\t\t\t\t\tswitch(*s)\n\t\t\t\t\t{\n\t\t\t\t\t\tcase 'd': case 'i':\n\t\t\t\t\t\t\tif(precision < 0) // not set\n\t\t\t\t\t\t\t\tQ_snprintfz(o, end - o, formatbuf, width, (isfloat ? (int) GETARG_FLOAT(thisarg) : (int) GETARG_INT(thisarg)));\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\tQ_snprintfz(o, end - o, formatbuf, width, precision, (isfloat ? (int) GETARG_FLOAT(thisarg) : (int) GETARG_INT(thisarg)));\n\t\t\t\t\t\t\to += strlen(o);\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 'o': case 'u': case 'x': case 'X': case 'p': case 'P':\n\t\t\t\t\t\t\tif(precision < 0) // not set\n\t\t\t\t\t\t\t\tQ_snprintfz(o, end - o, formatbuf, width, (isfloat ? (unsigned int) GETARG_FLOAT(thisarg) : (unsigned int) GETARG_INT(thisarg)));\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\tQ_snprintfz(o, end - o, formatbuf, width, precision, (isfloat ? (unsigned int) GETARG_FLOAT(thisarg) : (unsigned int) GETARG_INT(thisarg)));\n\t\t\t\t\t\t\to += strlen(o);\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 'e': case 'E': case 'f': case 'F': case 'g': case 'G':\n\t\t\t\t\t\t\tif(precision < 0) // not set\n\t\t\t\t\t\t\t\tQ_snprintfz(o, end - o, formatbuf, width, (isfloat ? (double) GETARG_FLOAT(thisarg) : (double) GETARG_INT(thisarg)));\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\tQ_snprintfz(o, end - o, formatbuf, width, precision, (isfloat ? (double) GETARG_FLOAT(thisarg) : (double) GETARG_INT(thisarg)));\n\t\t\t\t\t\t\to += strlen(o);\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 'v': case 'V':\n\t\t\t\t\t\t\tf[-2] += 'g' - 'v';\n\t\t\t\t\t\t\tif(precision < 0) // not set\n\t\t\t\t\t\t\t\tQ_snprintfz(o, end - o, va(\"%s %s %s\", /* NESTED SPRINTF IS NESTED */ formatbuf, formatbuf, formatbuf),\n\t\t\t\t\t\t\t\t\twidth, (isfloat ? (double) GETARG_VECTOR(thisarg)[0] : (double) GETARG_INTVECTOR(thisarg)[0]),\n\t\t\t\t\t\t\t\t\twidth, (isfloat ? (double) GETARG_VECTOR(thisarg)[1] : (double) GETARG_INTVECTOR(thisarg)[1]),\n\t\t\t\t\t\t\t\t\twidth, (isfloat ? (double) GETARG_VECTOR(thisarg)[2] : (double) GETARG_INTVECTOR(thisarg)[2])\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\tQ_snprintfz(o, end - o, va(\"%s %s %s\", /* NESTED SPRINTF IS NESTED */ formatbuf, formatbuf, formatbuf),\n\t\t\t\t\t\t\t\t\twidth, precision, (isfloat ? (double) GETARG_VECTOR(thisarg)[0] : (double) GETARG_INTVECTOR(thisarg)[0]),\n\t\t\t\t\t\t\t\t\twidth, precision, (isfloat ? (double) GETARG_VECTOR(thisarg)[1] : (double) GETARG_INTVECTOR(thisarg)[1]),\n\t\t\t\t\t\t\t\t\twidth, precision, (isfloat ? (double) GETARG_VECTOR(thisarg)[2] : (double) GETARG_INTVECTOR(thisarg)[2])\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\to += strlen(o);\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 'c':\n\t\t\t\t\t\t\t//UTF-8-FIXME: figure it out yourself\n//\t\t\t\t\t\t\tif(flags & PRINTF_ALTERNATE)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tif(precision < 0) // not set\n\t\t\t\t\t\t\t\t\tQ_snprintfz(o, end - o, formatbuf, width, (isfloat ? (unsigned int) GETARG_FLOAT(thisarg) : (unsigned int) GETARG_INT(thisarg)));\n\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t\tQ_snprintfz(o, end - o, formatbuf, width, precision, (isfloat ? (unsigned int) GETARG_FLOAT(thisarg) : (unsigned int) GETARG_INT(thisarg)));\n\t\t\t\t\t\t\t\to += strlen(o);\n\t\t\t\t\t\t\t}\n/*\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tunsigned int c = (isfloat ? (unsigned int) GETARG_FLOAT(thisarg) : (unsigned int) GETARG_INT(thisarg));\n\t\t\t\t\t\t\t\tchar charbuf16[16];\n\t\t\t\t\t\t\t\tconst char *buf = u8_encodech(c, NULL, charbuf16);\n\t\t\t\t\t\t\t\tif(!buf)\n\t\t\t\t\t\t\t\t\tbuf = \"\";\n\t\t\t\t\t\t\t\tif(precision < 0) // not set\n\t\t\t\t\t\t\t\t\tprecision = end - o - 1;\n\t\t\t\t\t\t\t\to += u8_strpad(o, end - o, buf, (flags & PRINTF_LEFT) != 0, width, precision);\n\t\t\t\t\t\t\t}\n*/\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 's':\n\t\t\t\t\t\t\t//UTF-8-FIXME: figure it out yourself\n//\t\t\t\t\t\t\tif(flags & PRINTF_ALTERNATE)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tif(precision < 0) // not set\n\t\t\t\t\t\t\t\t\tQ_snprintfz(o, end - o, formatbuf, width, GETARG_STRING(thisarg));\n\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t\tQ_snprintfz(o, end - o, formatbuf, width, precision, GETARG_STRING(thisarg));\n\t\t\t\t\t\t\t\to += strlen(o);\n\t\t\t\t\t\t\t}\n/*\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tif(precision < 0) // not set\n\t\t\t\t\t\t\t\t\tprecision = end - o - 1;\n\t\t\t\t\t\t\t\to += u8_strpad(o, end - o, GETARG_STRING(thisarg), (flags & PRINTF_LEFT) != 0, width, precision);\n\t\t\t\t\t\t\t}\n*/\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\tprintf(\"PF_sprintf: invalid format string: %s\\n\", s0);\n\t\t\t\t\t\t\tgoto finished;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t++s;\n\t\t\t\tbreak;\n\t\t\tdefault:\nverbatim:\n\t\t\t\tif(o < end - 1)\n\t\t\t\t\t*o++ = *s;\n\t\t\t\ts++;\n\t\t\t\tbreak;\n\t\t}\n\t}\nfinished:\n\t*o = 0;\n}\n\nvoid PF_printf (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tchar outbuf[4096];\n\tPF_sprintf_internal(prinst, pr_globals, PR_GetStringOfs(prinst, OFS_PARM0), 1, outbuf, sizeof(outbuf));\n\tprintf(\"%s\", outbuf);\n}\n\nstruct edict_s\n{\n\tenum ereftype_e\tereftype;\n\tfloat\t\t\tfreetime;\t\t\t// realtime when the object was freed\n\tunsigned int\tentnum;\n\tunsigned int\tfieldsize;\n\tpbool\t\t\treadonly;\t//causes error when QC tries writing to it. (quake's world entity)\n\tvoid\t\t\t*fields;\n};\nvoid PF_spawn (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tstruct edict_s\t*ed;\n\ted = ED_Alloc(prinst, false, 0);\n\tpr_globals = PR_globals(prinst, PR_CURRENT);\n\tRETURN_EDICT(prinst, ed);\n}\nvoid PF_remove (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tstruct edict_s *ed = (void*)G_EDICT(prinst, OFS_PARM0);\n\tif (ed->ereftype == ER_FREE)\n\t{\n\t\tprintf(\"Tried removing free entity\\n\");\n\t\tPR_StackTrace(prinst, false);\n\t\treturn;\n\t}\n\tED_Free (prinst, (void*)ed);\n}\n\nvoid PF_error(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tPF_puts(prinst, pr_globals);\n}\n\nvoid PF_bad (pubprogfuncs_t *prinst, struct globalvars_s *gvars)\n{\n\tprintf(\"bad builtin\\n\");\n}\n\nbuiltin_t builtins[] = {\n\tPF_bad,\n\tPF_puts,\n\tPF_ftos,\n\tPF_spawn,\n\tPF_remove,\n\tPF_vtos,\n\tPF_error,\n\tPF_vlen,\n\tPF_etos,\n\tPF_stof,\n\tPF_strcat,\n\tPF_strcmp,\n\tPF_normalize,\n\tPF_sqrt,\n\tPF_floor,\n\tPF_pow,\n\tPF_stov,\n\tPF_itos,\n\tPF_ltos,\n\tPF_dtos,\n\tPF_puts,\n\tPF_putv,\n\tPF_putf,\n\tPF_printf,\n};\n\n\n\n\n//Called when the qc library has some sort of serious error.\nvoid Sys_Abort(const char *s, ...)\n{\t//quake handles this with a longjmp.\n\tva_list ap;\n\tva_start(ap, s);\n\tvprintf(s, ap);\n\tva_end(ap);\n\texit(1);\n}\n//Called when the library has something to say.\n//Kinda required for the compiler...\n//Not really that useful for the normal vm.\nint Sys_Printf(char *s, ...)\n{\t//look up quake's va function to find out how to deal with variable arguments properly.\n\treturn printf(\"%s\", s);\n}\n\n#include <stdio.h>\n//copy file into buffer. note that the buffer will have been sized to fit the file (obtained via FileSize)\nvoid *PDECL Sys_ReadFile(const char *fname, unsigned char *(PDECL *buf_get)(void *ctx, size_t len), void *buf_ctx, size_t *out_size, pbool issourcefile)\n{\n\tvoid *buffer;\n\tint len;\n\tFILE *f;\n\tif (!strncmp(fname, \"src/\", 4))\n\t\tfname+=4;\t//skip the src part\n\tf = fopen(fname, \"rb\");\n\tif (!f)\n\t\treturn NULL;\n\tfseek(f, 0, SEEK_END);\n\tlen = ftell(f);\n\n\tbuffer = buf_get(buf_ctx, len);\n\tfseek(f, 0, SEEK_SET);\n\tfread(buffer, 1, len, f);\n\tfclose(f);\n\n\t*out_size = len;\n\treturn buffer;\n}\n//Finds the size of a file.\nint Sys_FileSize (const char *fname)\n{\n\tint len;\n\tFILE *f;\n\tif (!strncmp(fname, \"src/\", 4))\n\t\tfname+=4;\t//skip the src part\n\tf = fopen(fname, \"rb\");\n\tif (!f)\n\t\treturn -1;\n\tfseek(f, 0, SEEK_END);\n\tlen = ftell(f);\n\tfclose(f);\n\treturn len;\n}\n//Writes a file.\npbool Sys_WriteFile (const char *fname, void *data, int len)\n{\n\tFILE *f;\n\tf = fopen(fname, \"wb\");\n\tif (!f)\n\t\treturn 0;\n\tfwrite(data, 1, len, f);\n\tfclose(f);\n\treturn 1;\n}\n\n\nvoid ASMCALL StateOp(pubprogfuncs_t *prinst, float var, func_t func)\n{\t//note: inefficient. stupid globals abuse and not making assumptions about fields\n\tint *selfg = (int*)PR_FindGlobal(prinst, \"self\", 0, NULL);\n\tstruct edict_s *ed = PROG_TO_EDICT(prinst, selfg?*selfg:0);\n\tfloat *time = (float*)PR_FindGlobal(prinst, \"time\", 0, NULL);\n\teval_t *think = prinst->GetEdictFieldValue(prinst, ed, \"think\", ev_function, NULL);\n\teval_t *nextthink = prinst->GetEdictFieldValue(prinst, ed, \"nextthink\", ev_float, NULL);\n\teval_t *frame = prinst->GetEdictFieldValue(prinst, ed, \"frame\", ev_float, NULL);\n\tif (time && nextthink)\n\t\tnextthink->_float = *time+0.1;\n\tif (think)\n\t\tthink->function = func;\n\tif (frame)\n\t\tframe->_float = var;\n}\nvoid ASMCALL CStateOp(pubprogfuncs_t *progs, float first, float last, func_t currentfunc)\n{\n/*\n\tfloat min, max;\n\tfloat step;\n\tfloat *vars = PROG_TO_WEDICT(progs, *w->g.self)->fields;\n\tfloat frame = e->v->frame;\n\n//\tif (progstype == PROG_H2)\n//\t\te->v->nextthink = *w->g.time+0.05;\n//\telse\n\t\te->v->nextthink = *w->g.time+0.1;\n\te->v->think = currentfunc;\n\n\tif (csqcg.cycle_wrapped)\n\t\t*csqcg.cycle_wrapped = false;\n\n\tif (first > last)\n\t{\t//going backwards\n\t\tmin = last;\n\t\tmax = first;\n\t\tstep = -1.0;\n\t}\n\telse\n\t{\t//forwards\n\t\tmin = first;\n\t\tmax = last;\n\t\tstep = 1.0;\n\t}\n\tif (frame < min || frame > max)\n\t\tframe = first;\t//started out of range, must have been a different animation\n\telse\n\t{\n\t\tframe += step;\n\t\tif (frame < min || frame > max)\n\t\t{\t//became out of range, must have wrapped\n\t\t\tif (csqcg.cycle_wrapped)\n\t\t\t\t*csqcg.cycle_wrapped = true;\n\t\t\tframe = first;\n\t\t}\n\t}\n\te->v->frame = frame;\n*/\n}\nstatic void ASMCALL CWStateOp (pubprogfuncs_t *prinst, float first, float last, func_t currentfunc)\n{\n/*\n\tfloat min, max;\n\tfloat step;\n\tworld_t *w = prinst->parms->user;\n\twedict_t *e = PROG_TO_WEDICT(prinst, *w->g.self);\n\tfloat frame = e->v->weaponframe;\n\n\te->v->nextthink = *w->g.time+0.1;\n\te->v->think = currentfunc;\n\n\tif (csqcg.cycle_wrapped)\n\t\t*csqcg.cycle_wrapped = false;\n\n\tif (first > last)\n\t{\t//going backwards\n\t\tmin = last;\n\t\tmax = first;\n\t\tstep = -1.0;\n\t}\n\telse\n\t{\t//forwards\n\t\tmin = first;\n\t\tmax = last;\n\t\tstep = 1.0;\n\t}\n\tif (frame < min || frame > max)\n\t\tframe = first;\t//started out of range, must have been a different animation\n\telse\n\t{\n\t\tframe += step;\n\t\tif (frame < min || frame > max)\n\t\t{\t//became out of range, must have wrapped\n\t\t\tif (csqcg.cycle_wrapped)\n\t\t\t\t*csqcg.cycle_wrapped = true;\n\t\t\tframe = first;\n\t\t}\n\t}\n\te->v->weaponframe = frame;\n*/\n}\nvoid ASMCALL ThinkTimeOp(pubprogfuncs_t *prinst, struct edict_s *ed, float var)\n{\n\tfloat *self = (float*)PR_FindGlobal(prinst, \"self\", 0, NULL);\n\tfloat *time = (float*)PR_FindGlobal(prinst, \"time\", 0, NULL);\n\tint *nextthink = (int*)PR_FindGlobal(prinst, \"nextthink\", 0, NULL);\n\tfloat *vars = PROG_TO_EDICT(prinst, self?*self:0)->fields;\n\tif (time && nextthink)\n\t\tvars[*nextthink] = *time+0.1;\n}\n\n\nvoid runtest(const char *progsname, const char **args)\n{\n\tpubprogfuncs_t *pf;\n\tfunc_t func;\n\tprogsnum_t pn;\n\n\tprogparms_t ext;\n\tmemset(&ext, 0, sizeof(ext));\n\n\text.progsversion = PROGSTRUCT_VERSION;\n\text.ReadFile = Sys_ReadFile;\n\text.FileSize= Sys_FileSize;\n\text.Sys_Error = Sys_Abort;\n\text.Abort = Sys_Abort;\n\text.Printf = printf;\n\text.stateop = StateOp;\n\text.cstateop = CStateOp;\n\text.cwstateop = CWStateOp;\n\text.thinktimeop = ThinkTimeOp;\n\n\text.numglobalbuiltins = sizeof(builtins)/sizeof(builtins[0]);\n\text.globalbuiltins = builtins;\n\n\tpf = InitProgs(&ext);\n\tpf->Configure(pf, 1024*1024*64, 1, false);\t//memory quantity of 1mb. Maximum progs loadable into the instance of 1\n//If you support multiple progs types, you should tell the VM the offsets here, via RegisterFieldVar\n\tpn = pf->LoadProgs(pf, progsname);\t//load the progs.\n\tif (pn < 0)\n\t\tprintf(\"test: Failed to load progs \\\"%s\\\"\\n\", progsname);\n\telse\n\t{\n//allocate qc-acessable strings here for 64bit cpus. (allocate via AddString, tempstringbase is a holding area not used by the actual vm)\n//you can call functions before InitEnts if you want. it's not really advised for anything except naming additional progs. This sample only allows one max.\n\n\t\tpf->InitEnts(pf, 10);\t\t//Now we know how many fields required, we can say how many maximum ents we want to allow. 10 in this case. This can be huge without too many problems.\n\n//now it's safe to ED_Alloc.\n\n\t\tfunc = pf->FindFunction(pf, \"main\", PR_ANY);\t//find the function 'main' in the first progs that has it.\n\t\tif (!func)\n\t\t\tprintf(\"Couldn't find function\\n\");\n\t\telse\n\t\t{\t//feed it some complex args.\n\t\t\tvoid *pr_globals = PR_globals(pf, PR_CURRENT);\n\t\t\tint i;\n\t\t\tconst char *atypes = *args++;\n\t\t\tfor (i = 0; atypes[i]; i++) switch(atypes[i])\n\t\t\t{\n\t\t\tcase 'f':\n\t\t\t\tG_FLOAT(OFS_PARM0+i*3) = atof(*args++);\n\t\t\t\tbreak;\n\t\t\tcase 'v':\n\t\t\t\tsscanf(*args++, \" %f %f %f \", &G_VECTOR(OFS_PARM0+i*3)[0], &G_VECTOR(OFS_PARM0+i*3)[1], &G_VECTOR(OFS_PARM0+i*3)[2]);\n\t\t\t\tbreak;\n\t\t\tcase 's':\n\t\t\t\tG_INT(OFS_PARM0+i*3) = pf->TempString(pf, *args++);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tpf->ExecuteProgram(pf, func);\t\t\t//call the function\n\t\t}\n\t}\n\tpf->Shutdown(pf);\n}\n\n//Run a compiler and nothing else.\n//Note that this could be done with an autocompile of PR_COMPILEALWAYS.\npbool compile(int argc, const char **argv)\n{\n\tpbool success = false;\n\tpubprogfuncs_t *pf;\n\n\tprogparms_t ext;\n\n\tif (0)\n\t{\n\t\tchar *testsrcfile =\t//newstyle progs.src must start with a #.\n\t\t\t\t\t\t//it's newstyle to avoid using multiple source files.\n\t\t\t\t \t\"#pragma PROGS_DAT \\\"testprogs.dat\\\"\\r\\n\"\n\t\t\t\t\t\"//INTERMEDIATE FILE - EDIT TEST.C INSTEAD\\r\\n\"\n\t\t\t\t\t\"\\r\\n\"\n\t\t\t\t\t\"void(...) print = #1;\\r\\n\"\n\t\t\t\t\t\"void() main =\\r\\n\"\n\t\t\t\t\t\"{\\r\\n\"\n\t\t\t\t\t\"\tprint(\\\"hello world\\\\n\\\");\\r\\n\"\n\t\t\t\t\t\"};\\r\\n\";\n\n\t\t//so that the file exists. We could insert it via the callbacks instead\n\t\tSys_WriteFile(\"progs.src\", testsrcfile, strlen(testsrcfile));\n\t}\n\n\tmemset(&ext, 0, sizeof(ext));\n\text.progsversion = PROGSTRUCT_VERSION;\n\text.ReadFile = Sys_ReadFile;\n\text.FileSize= Sys_FileSize;\n\text.WriteFile= Sys_WriteFile;\n\text.Abort = Sys_Abort;\n\text.Printf = printf;\n\n\tpf = InitProgs(&ext);\n\tif (pf->StartCompile)\n\t{\n\t\tif (pf->StartCompile(pf, argc, argv))\n\t\t{\n\t\t\twhile(pf->ContinueCompile(pf) == 1)\n\t\t\t\t;\n\t\t\tsuccess = true;\n\t\t}\n\t\telse\n\t\t\tprintf(\"compilation failed to start\\n\");\n\t}\n\telse\n\t\tprintf(\"no compiler in this qcvm build\\n\");\n\n\tpf->Shutdown(pf);\n\treturn success;\n}\n\nint main(int argc, const char **argv)\n{\n\tint i, a=0;\n\tchar atypes[9];\n\tconst char *args[9] = {atypes};\n\tconst char *dat = NULL;\n\tif (argc < 2)\n\t{\n\t\tprintf(\"Invalid arguments!\\nPlease run as, for example:\\n%s testprogs.dat -srcfile progs.src\\nThe first argument is the name of the progs.dat to run, the remaining arguments are the qcc args to use\\n\", argv[0]);\n\t\treturn 0;\n\t}\n\n\tfor (i = 1; i < argc; i++)\n\t{\n\t\tif (!strcmp(argv[i], \"-float\"))\t\t\t{atypes[a] = 'f'; args[++a] = argv[++i];}\n\t\telse if (!strcmp(argv[i], \"-vector\"))\t{atypes[a] = 'v'; args[++a] = argv[++i];}\n\t\telse if (!strcmp(argv[i], \"-string\"))\t{atypes[a] = 's'; args[++a] = argv[++i];}\n\t\telse if (!strcmp(argv[i], \"-srcfile\"))\t{if (!compile(argc-i, argv+i))return EXIT_FAILURE; break;}\t//compile it, woo. consume the rest of the args, too\n\t\telse if (!dat && argv[i][0] != '-')\t\t{dat = argv[i];}\n\t\telse\t\t\t\t\t\t\t\t\t{printf(\"unknown arg %s\\n\", argv[i]); return EXIT_FAILURE;}\n\t}\n\tatypes[a] = 0;\n\tif (dat)\n\t\truntest(dat, args);\n\telse\n\t\tprintf(\"Nothing to run\\n\");\n\n\treturn EXIT_SUCCESS;\n}\n"
  },
  {
    "path": "engine/server/net_preparse.c",
    "content": "#include \"quakedef.h\"\n\n#if !defined(CLIENTONLY) && defined(NETPREPARSE)\n/*Testing this code should typically be done with the three following mods:\nPrydon gate\nNexuiz\nFrikBots (both NQ+QW).\n\nIf those 3 mods work, then pretty much everything else will\n*/\n\n//#define NEWPREPARSE\n\n/*I want to rewrite this\nto use something like\n*/\n\n#ifdef NEWPREPARSE\nenum protocol_type\n{\n\tPPT_FLOAT,\n\tPPT_ENT,\n\tPPT_COORD,\n\tPPT_ANGLE,\n\tPPT_BYTE,\n\tPPT_SHORT,\n\tPPT_LONG,\n\tPPT_STRING\n};\n\n#define PPT_POS PPT_COORD,PPT_COORD,PPT_COORD\n\nunion protocol_data\n{\n\tfloat\tfd;\n\tint\t\tid;\n\tunsigned char\t*str;\n};\n\nstatic union protocol_data pp_data[1024];\nstatic enum protocol_type pp_temptypes[1024], *pp_types;\nstatic unsigned char pp_sdata[4096];\nstatic unsigned int pp_sdata_offset;\nstatic int pp_dest;\nstatic qboolean pp_fault;\n\nstatic unsigned int pp_expectedelements;\nstatic unsigned int pp_receivedelements;\nstatic qboolean (*pp_curdecision) (enum protocol_type *pt, union protocol_data *pd);\n\nstatic enum protocol_type pp_root[] = {PPT_BYTE};\nstatic qboolean pp_root_decide(enum protocol_type *pt, union protocol_data *pd);\n\nstatic void decide(enum protocol_type *types, unsigned int numtypes, qboolean (*newdecision) (enum protocol_type *pt, union protocol_data *pd))\n{\n\tpp_types = types;\n\tpp_expectedelements = numtypes;\n\tpp_curdecision = newdecision;\n}\n\nstatic void pp_flush(multicast_t to, vec3_t origin, void (*flushfunc)(client_t *cl, sizebuf_t *msg, enum protocol_type *pt, union protocol_data *pd), enum protocol_type *pt, union protocol_data *pd)\n{\n\n\tclient_t\t*client;\n\tqbyte\t\t*mask;\n\tint\t\t\tleafnum;\n\tint\t\t\tj;\n\tqboolean\treliable;\n\n\tdecide(pp_root, 1, pp_root_decide);\n\n\n\n\t{\n\t\treliable = false;\n\n\t\tswitch (to)\n\t\t{\n\t\tcase MULTICAST_ALL_R:\n\t\t\treliable = true;\t// intentional fallthrough\n\t\tcase MULTICAST_ALL:\n\t\t\tmask = NULL;\n\t\t\tbreak;\n\n\t\tcase MULTICAST_PHS_R:\n\t\t\treliable = true;\t// intentional fallthrough\n\t\tcase MULTICAST_PHS:\n\t\t\tif (!sv.phs)\n\t\t\t\tmask = NULL;\n\t\t\telse\n\t\t\t{\n\t\t\t\tcluster = sv.world.worldmodel->funcs.LeafnumForPoint(sv.world.worldmodel, origin);\n\t\t\t\tif (cluster >= 0)\n\t\t\t\t\tmask = sv.phs + cluster * 4*((sv.world.worldmodel->numclusters+31)>>5);\n\t\t\t\telse\n\t\t\t\t\tmask = NULL;\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase MULTICAST_PVS_R:\n\t\t\treliable = true;\t// intentional fallthrough\n\t\tcase MULTICAST_PVS:\n\t\t\tcluster = sv.world.worldmodel->funcs.LeafnumForPoint(sv.world.worldmodel, origin);\n\t\t\tif (cluster >= 0)\n\t\t\t\tmask = sv.pvs + cluster * 4*((sv.world.worldmodel->numclusters+31)>>5);\n\t\t\telse\n\t\t\t\tmask = NULL;\n\t\t\tbreak;\n\n\t\tdefault:\n\t\t\treturn;\n\t\t}\n\n\t\t// send the data to all relevent clients\n\t\tfor (j = 0, client = svs.clients; j < sv.allocated_client_slots; j++, client++)\n\t\t{\n\t\t\tif (client->state != cs_spawned)\n\t\t\t\tcontinue;\n\n\t\t\tif (client->controller)\n\t\t\t\tcontinue;\t//FIXME: send if at least one of the players is near enough.\n\n\t\t\tif (!((int)client->edict->xv->dimension_see & (int)pr_global_struct->dimension_send))\n\t\t\t\tcontinue;\n\n\t\t\tif (to == MULTICAST_PHS_R || to == MULTICAST_PHS)\n\t\t\t{\n\t\t\t\tvec3_t delta;\n\t\t\t\tVectorSubtract(origin, client->edict->v->origin, delta);\n\t\t\t\tif (Length(delta) <= 1024)\n\t\t\t\t\tgoto inrange;\n\t\t\t}\n\n\t\t\tif (mask)\n\t\t\t{\n\t\t\t\tcluster = sv.world.worldmodel->funcs.ClusterForPoint (sv.world.worldmodel, client->edict->v->origin);\n\t\t\t\tif (cluster >= 0 && !(mask[leafnum>>3] & (1<<(leafnum&7)) ) )\n\t\t\t\t{\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\ninrange:\n\t\t\tif (client->protocol == SCP_BAD)\n\t\t\t{\n\t\t\t\t/*bot*/\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (reliable)\n\t\t\t\tflushfunc(client, &client->netchan.message, pt, pd);\n\t\t\telse\n\t\t\t\tflushfunc(client, &client->datagram, pt, pd);\n\t\t}\n\t}\n/*\n\tif (sv.mvdrecording && !with)\t//mvds don't get the pext stuff\n\t{\n\t\tflushfunc(&dem.recorder,\n\t\tif (reliable)\n\t\t{\n\t\t\tMVDWrite_Begin(dem_all, 0, sv.multicast.cursize);\n\t\t\tSZ_Write(&demo.dbuf->sb, sv.multicast.data, sv.multicast.cursize);\n\t\t} else\n\t\t\tSZ_Write(&demo.datagram, sv.multicast.data, sv.multicast.cursize);\n\t}*/\n\n\tpp_sdata_offset = 0;\n\tpp_receivedelements = 0;\n}\n\n#define DECIDE(t) decide(t, sizeof(t)/sizeof(*t), t##_decide)\n#define DECIDE2(t,f) decide(t, sizeof(t)/sizeof(*t), f)\n\nstatic void pp_identity_flush(client_t *cl, sizebuf_t *msg, enum protocol_type *pt, union protocol_data *pd)\n{\n\tunsigned int i;\n\tfor (i = 0; i < pp_receivedelements; i++)\n\t{\n\t\tswitch(pt[i])\n\t\t{\n\t\tcase PPT_BYTE:\n\t\t\tMSG_WriteByte(msg, pd[i].id);\n\t\t\tbreak;\n\t\tcase PPT_ENT:\n\t\t\tMSG_WriteEntity(msg, pd[i].id);\n\t\t\tbreak;\n\t\tcase PPT_SHORT:\n\t\t\tMSG_WriteShort(msg, pd[i].id);\n\t\t\tbreak;\n\t\tcase PPT_COORD:\n\t\t\tMSG_WriteCoord(msg, pd[i].fd);\n\t\t\tbreak;\n\t\tcase PPT_ANGLE:\n\t\t\tMSG_WriteAngle(msg, pd[i].fd);\n\t\t\tbreak;\n\t\tcase PPT_STRING:\n\t\t\tMSG_WriteString(msg, pd[i].str);\n\t\t\tbreak;\n\t\t}\n\t}\n}\n\n/*flush is our last attempt to cope with unrecognised/invalid messages, it will send stuff though as it was, and most likely get things wrong*/\nvoid NPP_Flush(void)\n{\n\tpp_fault = false;\n\tpp_flush(MULTICAST_ALL_R, NULL, pp_identity_flush, pp_types, pp_data);\n}\n\nstatic void pp_entry(int dest, enum protocol_type pt, union protocol_data pd)\n{\n\tif (pp_receivedelements)\n\t{\n\t\tif (pp_dest != dest)\n\t\t{\n\t\t\tif (!pp_fault)\n\t\t\t\tCon_Printf(\"Preparse: MSG destination changed in the middle of a packet 0x%x.\\n\", pp_data[0].id);\n\t\t\tNPP_Flush();\n\t\t}\n\t}\n\tpp_dest = dest;\n\n\tif (pp_fault)\n\t{\n\t\tpp_types[pp_receivedelements] = pt;\n\t\tpp_data[pp_receivedelements] = pd;\n\t\tpp_receivedelements++;\n\t}\n\telse if (pp_types[pp_receivedelements] != pt)\n\t{\n\t\tCon_Printf(\"Preparse: Unmatched expectation at entry %i in svc 0x%x.\\n\", pp_receivedelements+1, pp_data[0].id);\n\n\t\tpp_types[pp_receivedelements] = pt;\n\t\tpp_data[pp_receivedelements] = pd;\n\t\tpp_receivedelements++;\n\nfaulted:\n\t\tpp_fault = true;\n\t\tif (pp_temptypes != pp_types)\n\t\t{\n\t\t\tmemcpy(pp_temptypes, pp_types, sizeof(*pp_temptypes)*pp_receivedelements);\n\t\t\tpp_types = pp_temptypes;\n\t\t}\n\t}\n\telse\n\t{\n\t\tpp_data[pp_receivedelements++] = pd;\n\n\t\tif (pp_expectedelements == pp_receivedelements)\n\t\t{\n\t\t\tif (!pp_curdecision(pp_types, pp_data))\n\t\t\t{\n\t\t\t\tif (pp_types[pp_receivedelements-1] == PPT_BYTE)\n\t\t\t\t\tCon_Printf(\"Preparse: Unhandled byte %i@%i in svc%i.\\n\", pd.id, pp_receivedelements, pp_data[0].id);\n\t\t\t\telse\n\t\t\t\t\tCon_Printf(\"Preparse: Unhandled data @%i in svc%i.\\n\", pp_receivedelements, pp_data[0].id);\n\t\t\t\tgoto faulted;\n\t\t\t}\n\t\t}\n\t}\n}\n\nvoid NPP_NQWriteByte(int dest, qbyte data)\n{\n\tunion protocol_data pd;\n\tpd.id = data;\n\tpp_entry(dest, PPT_BYTE, pd);\n}\nvoid NPP_NQWriteChar(int dest, char data)\n{\n\tunion protocol_data pd;\n\tpd.id = (unsigned char)data;\n\tpp_entry(dest, PPT_BYTE, pd);\n}\nvoid NPP_NQWriteShort(int dest, short data)\n{\n\tunion protocol_data pd;\n\tpd.id = (unsigned char)data;\n\tpp_entry(dest, PPT_SHORT, pd);\n}\nvoid NPP_NQWriteLong(int dest, long data)\n{\n\tunion protocol_data pd;\n\tpd.id = data;\n\tpp_entry(dest, PPT_LONG, pd);\n}\nvoid NPP_NQWriteAngle(int dest, float data)\n{\n\tunion protocol_data pd;\n\tpd.fd = data;\n\tpp_entry(dest, PPT_ANGLE, pd);\n}\nvoid NPP_NQWriteCoord(int dest, float data)\n{\n\tunion protocol_data pd;\n\tpd.fd = data;\n\tpp_entry(dest, PPT_COORD, pd);\n}\nvoid NPP_NQWriteString(int dest, char *data)\n{\n\tunsigned int l;\n\tunion protocol_data pd;\n\tl = strlen(data)+1;\n\tif (pp_sdata_offset + l > sizeof(pp_sdata))\n\t\tSV_Error(\"preparse string overflow\\n\");\n\tpd.str = pp_sdata + pp_sdata_offset;\n\tmemcpy(pd.str, data, l);\n\tpp_entry(dest, PPT_STRING, pd);\n}\nvoid NPP_NQWriteEntity(int dest, short data)\n{\n\tunion protocol_data pd;\n\tpd.id = (unsigned short)data;\n\tpp_entry(dest, PPT_ENTITY, pd);\n}\n\nvoid NPP_QWWriteByte(int dest, qbyte data)\n{\n\tNPP_NQWriteByte(dest, data);\n}\nvoid NPP_QWWriteChar(int dest, char data)\n{\n\tNPP_NQWriteChar(dest, data);\n}\nvoid NPP_QWWriteShort(int dest, short data)\n{\n\tNPP_NQWriteShort(dest, data);\n}\nvoid NPP_QWWriteLong(int dest, long data)\n{\n\tNPP_NQWriteLong(dest, data);\n}\nvoid NPP_QWWriteAngle(int dest, float data)\n{\n\tNPP_NQWriteAngle(dest, data);\n}\nvoid NPP_QWWriteCoord(int dest, float data)\n{\n\tNPP_NQWriteCoord(dest, data);\n}\nvoid NPP_QWWriteString(int dest, char *data)\n{\n\tNPP_NQWriteString(dest, data);\n}\nvoid NPP_QWWriteEntity(int dest, int data)\n{\n\tNPP_NQWriteEntity(dest, data);\n}\n\n\n\n\n\n\n\n\n\n\n\nstatic enum protocol_type pp_svc_temp_entity_beam[] = {PPT_BYTE, PPT_BYTE, PPT_ENT, PPT_POS, PPT_POS};\nstatic void pp_svc_temp_entity_beam_flush(client_t *cl, sizebuf_t *msg, enum protocol_type *pt, union protocol_data *pd)\n{\n\tMSG_WriteByte(msg, pd[0].id);\n\tMSG_WriteByte(msg, pd[1].id);\n\tMSG_WriteEntity(msg, pd[2].id);\n\tMSG_WriteCoord(msg, pd[3].fd);\n\tMSG_WriteCoord(msg, pd[4].fd);\n\tMSG_WriteCoord(msg, pd[5].fd);\n\tMSG_WriteCoord(msg, pd[6].fd);\n\tMSG_WriteCoord(msg, pd[7].fd);\n\tMSG_WriteCoord(msg, pd[8].fd);\n}\nstatic qboolean pp_svc_temp_entity_beam_decide(enum protocol_type *pt, union protocol_data *pd)\n{\n\tvec3_t org;\n\torg[0] = pd[3].fd;\n\torg[1] = pd[4].fd;\n\torg[2] = pd[5].fd;\n\tpp_flush(MULTICAST_PHS, org, pp_svc_temp_entity_beam_flush, pt, pd);\n\treturn true;\n}\n\nstatic void pp_svc_temp_entity_gunshot_flush(client_t *cl, sizebuf_t *msg, enum protocol_type *pt, union protocol_data *pd)\n{\n\tint offset = (progstype == PROG_QW)?3:2;\n\tint count = (offset == 3)?pd[2].id:1;\n\n\twhile (count > 0)\n\t{\n\t\tMSG_WriteByte(msg, pd[0].id);\n\t\tMSG_WriteByte(msg, pd[1].id);\n\t\tif (cl->protocol == SCP_QUAKEWORLD)\n\t\t{\n\t\t\tif (count > 255)\n\t\t\t{\n\t\t\t\tMSG_WriteByte(msg, 255);\n\t\t\t\tcount-=255;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tMSG_WriteByte(msg, count);\n\t\t\t\tcount = 0;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t\tcount--;\n\t\tMSG_WriteCoord(msg, pd[offset+0].fd);\n\t\tMSG_WriteCoord(msg, pd[offset+1].fd);\n\t\tMSG_WriteCoord(msg, pd[offset+2].fd);\n\t}\n}\nstatic qboolean pp_svc_temp_entity_gunshot(enum protocol_type *pt, union protocol_data *pd)\n{\n\tvec3_t org;\n\tint offset = (progstype == PROG_QW)?3:2;\n\torg[0] = pd[offset+0].fd;\n\torg[1] = pd[offset+1].fd;\n\torg[2] = pd[offset+2].fd;\n\tpp_flush(MULTICAST_PHS, org, pp_svc_temp_entity_gunshot_flush, pt, pd);\n\treturn true;\n}\n\nstatic qboolean pp_decide_pvs_2(enum protocol_type *pt, union protocol_data *pd)\n{\n\tvec3_t org;\n\torg[0] = pd[2].fd;\n\torg[1] = pd[3].fd;\n\torg[2] = pd[4].fd;\n\tpp_flush(MULTICAST_PVS, org, pp_identity_flush, pt, pd);\n\treturn true;\n}\nstatic qboolean pp_decide_phs_2(enum protocol_type *pt, union protocol_data *pd)\n{\n\tvec3_t org;\n\torg[0] = pd[2].fd;\n\torg[1] = pd[3].fd;\n\torg[2] = pd[4].fd;\n\tpp_flush(MULTICAST_PHS, org, pp_identity_flush, pt, pd);\n\treturn true;\n}\n\nstatic enum protocol_type pp_svc_temp_entity[] = {PPT_BYTE, PPT_BYTE};\nstatic qboolean pp_svc_temp_entity_decide(enum protocol_type *pt, union protocol_data *pd)\n{\n\tswitch(pd[1].id)\n\t{\n\tcase TE_LIGHTNING1:\n\tcase TE_LIGHTNING2:\n\tcase TE_LIGHTNING3:\n\t\tDECIDE(pp_svc_temp_entity_beam);\n\t\treturn true;\n\tcase TE_EXPLOSION:\n\tcase TEDP_EXPLOSIONQUAD:\n\tcase TE_SPIKE:\n\tcase TE_SUPERSPIKE:\n\tcase TEDP_SPIKEQUAD:\n\tcase TEDP_SUPERSPIKEQUAD:\n\tcase TEDP_SMALLFLASH:\n\t\t{\n\t\t\tstatic enum protocol_type fmt[] = {PPT_BYTE, PPT_BYTE, PPT_POS};\n\t\t\tDECIDE2(fmt, pp_decide_phs_2);\n\t\t}\n\t\treturn true;\n\n\tcase TEDP_GUNSHOTQUAD:\n\tcase TE_TAREXPLOSION:\n\tcase TE_WIZSPIKE:\n\tcase TE_KNIGHTSPIKE:\n\tcase TE_LAVASPLASH:\n\tcase TE_TELEPORT:\n\t\t{\n\t\t\tstatic enum protocol_type fmt[] = {PPT_BYTE, PPT_BYTE, PPT_POS};\n\t\t\tDECIDE2(fmt, pp_decide_pvs_2);\n\t\t}\n\t\treturn true;\n\n\tcase TE_GUNSHOT:\n\t\tif (progstype == PROG_QW)\n\t\t{\n\t\t\tstatic enum protocol_type fmt[] = {PPT_BYTE, PPT_BYTE, PPT_BYTE, PPT_POS};\n\t\t\tDECIDE2(fmt, pp_svc_temp_entity_gunshot);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tstatic enum protocol_type fmt[] = {PPT_BYTE, PPT_BYTE, PPT_POS};\n\t\t\tDECIDE2(fmt, pp_svc_temp_entity_gunshot);\n\t\t}\n\t\treturn true;\n\n\tcase 12:\n\t\tif (progstype == PROG_QW)\n\t\t{\n\t\t\t/*TEQW_BLOOD*/\n\t\t}\n\t\telse\n\t\t{\n\t\t\t/*TENQ_EXPLOSION2*/\n\t\t}\n\t\treturn false;\n\n\tcase 13:\n\t\tif (progstype == PROG_QW)\n\t\t{\n\t\t\t/*TEQW_LIGHTNINGBLOOD*/\n\t\t}\n\t\telse\n\t\t{\n\t\t\t/*TENQ_BEAM*/\n\t\t}\n\t\treturn false;\n\n\tcase TEDP_BLOOD:\n\tcase TEDP_SPARK:\n\t\t{\n\t\t\tstatic enum protocol_type fmt[] = {PPT_BYTE, PPT_BYTE, PPT_POS, PPT_BYTE,PPT_BYTE,PPT_BYTE, PPT_BYTE};\n\t\t\tDECIDE2(fmt, pp_decide_pvs_2);\n\t\t}\n\t\treturn true;\n\n\tcase TE_BULLET:\n\tcase TE_SUPERBULLET:\n\n\tcase TE_RAILTRAIL:\n\n\t\t// hexen 2\n\tcase TEH2_STREAM_CHAIN:\n\tcase TEH2_STREAM_SUNSTAFF1:\n\tcase TEH2_STREAM_SUNSTAFF2:\n\tcase TEH2_STREAM_LIGHTNING:\n\tcase TEH2_STREAM_COLORBEAM:\n\tcase TEH2_STREAM_ICECHUNKS:\n\tcase TEH2_STREAM_GAZE:\n\tcase TEH2_STREAM_FAMINE:\n\n\tcase TEDP_BLOODSHOWER:\n\tcase TEDP_EXPLOSIONRGB:\n\tcase TEDP_PARTICLECUBE:\n\tcase TEDP_PARTICLERAIN: // [vector] min [vector] max [vector] dir [short] count [byte] color\n\tcase TEDP_PARTICLESNOW: // [vector] min [vector] max [vector] dir [short] count [byte] color\n\tcase TEDP_CUSTOMFLASH:\n\tcase TEDP_FLAMEJET:\n\tcase TEDP_PLASMABURN:\n\tcase TEDP_TEI_G3:\n\tcase TEDP_SMOKE:\n\tcase TEDP_TEI_BIGEXPLOSION:\n\tcase TEDP_TEI_PLASMAHIT:\n\tdefault:\n\t\treturn false;\n\t}\n}\n\nqboolean pp_root_decide(enum protocol_type *pt, union protocol_data *pd)\n{\n\tswitch (pd[0].id)\n\t{\n\tcase svc_temp_entity:\n\t\tDECIDE(pp_svc_temp_entity);\n\t\treturn true;\n\tdefault:\n\t\treturn false;\n\t}\n}\n\n\n\n\n\n#else\n\nstatic sizebuf_t\t*writedest;\nstatic client_t\t\t*cldest;\nstruct netprim_s *destprim;\nstatic int majortype;\n#ifdef NQPROT\nstatic int minortype;\n#endif\nstatic int protocollen;\n\nstatic qbyte buffer[MAX_OVERALLMSGLEN];\nstatic int bufferlen;\nstatic int nullterms;\n\nstatic int multicastpos;\t//writecoord*3 offset\nstatic int multicasttype;\nstatic int requireextension;\nstatic qboolean ignoreprotocol;\nstatic int te_515sevilhackworkaround;\n\n#define svc_setfrags 14\n#define svc_updatecolors 17\n\n#ifdef HEXEN2\n#define svch2_setviewflags\t\t40\t//hexen2.\n#define svch2_clearviewflags\t41\t//hexen2.\n#define svch2_setviewtint\t\t46\t//hexen2.\n#define svch2_setangles_lerp\t50\n#endif\n\n//these are present in the darkplaces engine.\n//I wanna knick their mods.\n#define svcdp_skybox\t37\n\n#define\tsvcdp_showlmp\t\t\t35\t\t// [string] slotname [string] lmpfilename [short] x [short] y\n#define\tsvcdp_hidelmp\t\t\t36\t\t// [string] slotname\n\n//#define\tTE_RAILTRAIL_NEH\t\t15 // [vector] origin [coord] red [coord] green [coord] blue\t(fixme: ignored)\n#define\tTE_EXPLOSION3_NEH\t\t16 // [vector] origin [coord] red [coord] green [coord] blue\t(fixme: ignored)\n#define TE_LIGHTNING4_NEH\t\t17 // [string] model [entity] entity [vector] start [vector] end\n#define TE_SMOKE_NEH\t\t\t18\n#define TE_EXPLOSIONSMALL2\t\t20\t//\torg.\n\nclient_t *Write_GetClient(void);\nsizebuf_t *QWWriteDest (int dest);\n#ifdef NQPROT\nsizebuf_t *NQWriteDest (int dest);\n#endif\n\nvoid NPP_SetInfo(client_t *cl, char *key, char *value)\n{\n\tint i;\n\n\t//its common for bots to be set up by renaming players randomly etc\n\t//such bots should appear that way in server browsers.\n\t//on the other hand, players might end up with postfixes on their names.\n\t//this will break clientside name/stats parsing of course... not much we can do about that other than fixing the mods.\n\t\n\tif (cl->netchan.remote_address.type != NA_INVALID)\n\t{\n\t\tint vlen = strlen(value);\n\t\tif (!strcmp(key, \"name\") && 7+vlen+1 <= sizeof(buffer))\n\t\t{\n\t\t\tif (progstype != PROG_QW)\n\t\t\t{\n\t\t\t\tmemmove(buffer+7, value, vlen+1);\n\t\t\t\tbuffer[0] = svc_setinfo;\n\t\t\t\tbuffer[1] = cl - svs.clients;\n\t\t\t\tbuffer[2] = 'n';\n\t\t\t\tbuffer[3] = 'a';\n\t\t\t\tbuffer[4] = 'm';\n\t\t\t\tbuffer[5] = 'e';\n\t\t\t\tbuffer[6] = 0;\n\t\t\t\tbufferlen = 7+ vlen+1;\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n\n\tbufferlen = 0;\n\n\tif (!strcmp(key, \"colours\"))\n\t{\n\t\ti = atoi(value);\n\t\tInfoBuf_SetKey (&cl->userinfo, \"bottomcolor\", va(\"%i\", i&15));\n\t\tInfoBuf_SetKey (&cl->userinfo, \"topcolor\", va(\"%i\", i>>4));\n\t}\n\tInfoBuf_SetKey (&cl->userinfo, key, value);\n\tif (!*InfoBuf_ValueForKey (&cl->userinfo, \"name\"))\n\t\tcl->name[0] = '\\0';\n\telse // process any changed values\n\t\tSV_ExtractFromUserinfo (cl, false);\n\n\ti = cl - svs.clients;\n\tif (!strcmp(key, \"colours\"))\n\t{\n\t\tMSG_WriteByte (&sv.reliable_datagram, svc_setinfo);\n\t\tMSG_WriteByte (&sv.reliable_datagram, i);\n\t\tMSG_WriteString (&sv.reliable_datagram, \"bottomcolor\");\n\t\tMSG_WriteString (&sv.reliable_datagram, InfoBuf_ValueForKey(&cl->userinfo, \"bottomcolor\"));\n\n\t\tMSG_WriteByte (&sv.reliable_datagram, svc_setinfo);\n\t\tMSG_WriteByte (&sv.reliable_datagram, i);\n\t\tMSG_WriteString (&sv.reliable_datagram, \"topcolor\");\n\t\tMSG_WriteString (&sv.reliable_datagram, InfoBuf_ValueForKey(&cl->userinfo, \"topcolor\"));\n\t}\n\telse\n\t{\n\t\tMSG_WriteByte (&sv.reliable_datagram, svc_setinfo);\n\t\tMSG_WriteByte (&sv.reliable_datagram, i);\n\t\tMSG_WriteString (&sv.reliable_datagram, key);\n\t\tMSG_WriteString (&sv.reliable_datagram, InfoBuf_ValueForKey(&cl->userinfo, key));\n\t}\n}\n\nvoid NPP_NQFlush(void)\n{\n\tif (!bufferlen)\n\t\treturn;\n\n\tswitch(majortype)\n\t{\n\tcase svc_cdtrack:\n\t\tif (bufferlen!=protocollen)\n\t\t\tCon_Printf(\"NQFlush: svc_cdtrack wasn't the right length\\n\");\n\t\telse\n\t\t\tbufferlen-=1;\n\t\tbreak;\n\tcase svc_updatefrags:\n\t\tbreak;\n\t\t//ignore these.\n\tcase svc_print:\n\tcase svcdp_skybox:\n\t\tbufferlen = 0;\n\t\tbreak;\n\tcase svc_updatename:\n\t\tNPP_SetInfo(&svs.clients[buffer[1]], \"name\", buffer+2);\n\t\tbreak;\n\tcase svc_updatecolors:\n\t\tNPP_SetInfo(&svs.clients[buffer[1]], \"colours\", va(\"%i\", buffer[2]));\n\t\tbreak;\n\tcase svc_intermission:\n//\t\tif (writedest == &sv.reliable_datagram)\n\t\t{\n\t\t\tsizebuf_t *msg;\n\t\t\tclient_t *cl;\n\t\t\tint i;\n#ifdef HEXEN2\n\t\t\tchar *h2finale = NULL;\n\t\t\tchar *h2title = NULL;\n\t\t\tif (progstype == PROG_H2)\n\t\t\t{\n\t\t\t\t/*FIXME: hexen2 intermission+finale includes the viewheight. NQ does not, and QW has explicit position with scores but no finale*/\n\t\t\t\t/*hexen2 does something like this in the client, but we don't support those protocols, so translate to something usable*/\n\t\t\t\tchar *title[13] = {\"gfx/finale.lmp\", \"gfx/meso.lmp\", \"gfx/egypt.lmp\", \"gfx/roman.lmp\", \"gfx/castle.lmp\", \"gfx/castle.lmp\", \"gfx/end-1.lmp\", \"gfx/end-2.lmp\", \"gfx/end-3.lmp\", \"gfx/castle.lmp\", \"gfx/mpend.lmp\", \"gfx/mpmid.lmp\", \"gfx/end-3.lmp\"};\n\t\t\t\tint lookup[13] = {394, 395, 396, 397, 358, strcmp(T_GetString(400+5*2+1), \"BAD STRING\")?400+5*2+1:400+4*2, 386+6, 386+7, 386+8, 391, 538, 545, 561};\n\t\t\t\t//5 is the demo sell screen, which changes depending on hexen2 vs portals.\n\t\t\t\tif (buffer[1] < 13)\n\t\t\t\t{\n\t\t\t\t\th2title = title[buffer[1]];\n\t\t\t\t\th2finale = T_GetString(lookup[buffer[1]]);\n\t\t\t\t}\n\t\t\t}\n#endif\n\n#if defined(HAVE_CLIENT)\n\t\t\tLog_MapNowCompleted();\n#endif\n\n\t\t\tfor (i = 0, cl = svs.clients; i < sv.allocated_client_slots; i++, cl++)\n\t\t\t{\n\t\t\t\tif (cl->state == cs_spawned && ISQWCLIENT(cl))\n\t\t\t\t{\n#ifdef HEXEN2\n\t\t\t\t\tif (h2finale)\n\t\t\t\t\t{\n\t\t\t\t\t\tmsg = ClientReliable_StartWrite(cl, 6 + strlen(h2title) + 3 + strlen(h2finale) + 1);\n\t\t\t\t\t\tMSG_WriteByte(msg, svc_finale);\n\n\t\t\t\t\t\tMSG_WriteByte(msg, '/');\n\t\t\t\t\t\tMSG_WriteByte(msg, 'F');\n\t\t\t\t\t\tMSG_WriteByte(msg, 'f');\t//hexen2-style finale\n\n\t\t\t\t\t\tMSG_WriteByte(msg, '/');\n\t\t\t\t\t\tMSG_WriteByte(msg, 'I');\n\t\t\t\t\t\tSZ_Write(msg, h2title, strlen(h2title));\n\t\t\t\t\t\tMSG_WriteByte(msg, ':');\t//image\n\n\t\t\t\t\t\tMSG_WriteByte(msg, '/');\n\t\t\t\t\t\tMSG_WriteByte(msg, 'P');\t//image should be a background.\n\n\t\t\t\t\t\tMSG_WriteString(msg, h2finale);\n\t\t\t\t\t}\n\t\t\t\t\telse\n#endif\n\t\t\t\t\t\tif (cl->fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS)\n\t\t\t\t\t{\t//special intermission mode to leave the view attached to the viewentity (as required for nq - especially rogue's finale) instead of hacking it to some specific point\n\t\t\t\t\t\tmsg = ClientReliable_StartWrite(cl, 5);\n\t\t\t\t\t\tMSG_WriteByte(msg, svc_finale);\n\t\t\t\t\t\tMSG_WriteString(msg, \"/FI\");\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tmsg = ClientReliable_StartWrite(cl, 23);\n\t\t\t\t\t\tMSG_WriteByte(msg, svc_intermission);\n\t\t\t\t\t\tMSG_WriteCoord(msg, cl->edict->v->origin[0]);\n\t\t\t\t\t\tMSG_WriteCoord(msg, cl->edict->v->origin[1]);\n\t\t\t\t\t\tMSG_WriteCoord(msg, cl->edict->v->origin[2]+cl->edict->v->view_ofs[2]);\n\t\t\t\t\t\tMSG_WriteAngle(msg, cl->edict->v->angles[0]);\n\t\t\t\t\t\tMSG_WriteAngle(msg, cl->edict->v->angles[1]);\n\t\t\t\t\t\tMSG_WriteAngle(msg, cl->edict->v->angles[2]);\n\t\t\t\t\t}\n\t\t\t\t\tClientReliable_FinishWrite(cl);\n\t\t\t\t}\n\t\t\t}\n\t\t\tbufferlen = 0;\n\t\t\tprotocollen=0;\n\t\t\twritedest = NULL;\n\t\t}\n\t\tbreak;\n//\tcase svc_finale:\t//finale does exist in vanilla qw. apparently. hurrah.\n//\t\tbufferlen = 0;\n//\t\tbreak;\n\tcase svc_cutscene:\n\t\t//finale with no text. and explicitly no flags (which mostly prevents the 'completed' banner appearing), should be equivelent.\n\t\tbuffer[0] = svc_finale;\n\t\tbuffer[1] = '/';\n\t\tbuffer[2] = '.';\n\t\tbuffer[3] = 0;\n\t\tbufferlen = 4;\n\t\tbreak;\n\tcase svc_setview:\n\t\trequireextension = PEXT_SETVIEW;\n\n\t\tif (cldest)\t//catch it to work with all clients\n\t\t{\n\t\t\tcldest->viewent = *(unsigned short*)&buffer[1];\n//\t\t\tbufferlen = 0;\n\t\t\tif (cldest->viewent == (cldest - svs.clients)+1)\n\t\t\t\tcldest->viewent = 0;\t//self is the same as none\n\t\t}\n//\t\tbufferlen = 0;\n\t\tbreak;\n\tcase svcdp_hidelmp:\n\t\tif (progstype == PROG_TENEBRAE)\n\t\t{\n\t\t\tbufferlen = 0;\n\t\t\tbreak;\n\t\t}\n\t\trequireextension = PEXT_SHOWPIC;\n\t\tbuffer[0] = svcfte_hidepic;\n\t\tbreak;\n\tcase svcdp_showlmp:\n\t\tif (progstype == PROG_TENEBRAE)\n\t\t{\n\t\t\tbufferlen = 0;\n\t\t\tbreak;\n\t\t}\n\t\trequireextension = PEXT_SHOWPIC;\n\t\tmemmove(buffer+2, buffer+1, bufferlen-1);\n\t\tbufferlen++;\n\t\tbuffer[0] = svcfte_showpic;\n\t\tbuffer[1] = 0;\t//top left\n\t\t//pad the bytes to shorts.\n\t\tbuffer[bufferlen] = buffer[bufferlen-1];\n\t\tbuffer[bufferlen-1] = 0;\n\t\tbuffer[bufferlen+1] = 0;\n\t\tbufferlen+=2;\n\t\tbreak;\n\n\tcase svcfte_cgamepacket:\n\t\trequireextension = PEXT_CSQC;\n\t\tif (sv_csqcdebug.ival || writedest != &sv.multicast)\n\t\t{\n\t\t\tif (writedest != &sv.multicast)\n\t\t\t\tCon_Printf(CON_WARNING\"Warning: svc_cgamepacket used outside of a multicast\\n\");\n\t\t\t/*shift the data up by two bytes*/\n\t\t\tmemmove(buffer+3, buffer+1, bufferlen-1);\n\n\t\t\tbuffer[0] = svcfte_cgamepacket_sized;\n\t\t\t/*add a length in the 2nd/3rd bytes*/\n\t\t\tbuffer[1] = (bufferlen-1);\n\t\t\tbuffer[2] = (bufferlen-1) >> 8;\n\n\t\t\tbufferlen += 2;\n\t\t\tif(multicastpos) multicastpos += 2;\n\t\t}\n\t\tbreak;\n\tcase svc_temp_entity:\n\t\tif (sv_csqcdebug.ival)\n\t\t{\n\t\t\tif (te_515sevilhackworkaround && writedest != &sv.multicast)\n\t\t\t\tCon_Printf(CON_WARNING\"Warning: unknown svc_temp_entity used outside of a multicast\\n\");\n\t\t\t/*shift the data up by two bytes, but don't care about the first byte*/\n\t\t\tmemmove(buffer+3, buffer+1, bufferlen-1);\n\n\t\t\tbuffer[0] = svcfte_temp_entity_sized;\n\t\t\t/*add a length in the 2nd/3rd bytes, if needed*/\n\t\t\tbuffer[1] =  (bufferlen-1) & 0xff;\n\t\t\tbuffer[2] = ((bufferlen-1) >> 8);\n\t\t\tif (te_515sevilhackworkaround)\t//we don't know how to translate it, so the qw receiver must be told to interpret as the nq version\n\t\t\t\tbuffer[2] |= 0x80;\n\n\t\t\tbufferlen += 2;\n\t\t\tif(multicastpos) multicastpos += 2;\n\t\t\tbreak;\n\t\t}\n\t\tswitch (buffer[1])\n\t\t{\n\t\tdefault:\n\t\t\tif (te_515sevilhackworkaround)\n\t\t\t{\n\t\t\t\tif (writedest != &sv.multicast)\n\t\t\t\t\tCon_Printf(CON_WARNING\"Warning: unknown svc_temp_entity used outside of a multicast\\n\");\n\t\t\t\t/*shift the data up by two bytes, but don't care about the first byte*/\n\t\t\t\tmemmove(buffer+3, buffer+1, bufferlen-1);\n\n\t\t\t\tbuffer[0] = svcfte_temp_entity_sized;\n\t\t\t\t/*add a length in the 2nd/3rd bytes, if needed*/\n\t\t\t\tbuffer[1] =  (bufferlen-1) & 0xff;\n\t\t\t\tbuffer[2] = ((bufferlen-1) >> 8);\n\t\t\t\tif (te_515sevilhackworkaround)\t//we don't know how to translate it, so the qw receiver must be told to interpret as the nq version\n\t\t\t\t\tbuffer[2] |= 0x80;\n\n\t\t\t\tbufferlen += 2;\n\t\t\t\tif(multicastpos) multicastpos += 2;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase TENQ_NQEXPLOSION:\n\t\t\tif (writedest == &sv.datagram)\n\t\t\t{\t//for old clients, use a te_explosion.\n\t\t\t\t//for clients that support it, use a TEQW_EXPLOSIONNOSPRITE\n\t\t\t\tvec3_t org;\n\t\t\t\tint coordsize = (destprim->coordtype&0xf);\n\t\t\t\tcoorddata cd;\n\t\t\t\tif (sv.multicast.cursize + bufferlen > sv.multicast.maxsize)\n\t\t\t\t\tSV_FlushBroadcasts();\n\t\t\t\tSZ_Write(&sv.multicast, buffer, bufferlen);\n\n\t\t\t\tmemcpy(&cd, &buffer[2+coordsize*0], coordsize);\n\t\t\t\torg[0] = MSG_FromCoord(cd, coordsize);\n\t\t\t\tmemcpy(&cd, &buffer[2+coordsize*1], coordsize);\n\t\t\t\torg[1] = MSG_FromCoord(cd, coordsize);\n\t\t\t\tmemcpy(&cd, &buffer[2+coordsize*2], coordsize);\n\t\t\t\torg[2] = MSG_FromCoord(cd, coordsize);\n\n\t\t\t\trequireextension = PEXT_TE_BULLET;\n\t\t\t\tSV_MulticastProtExt(org, multicasttype, pr_global_struct->dimension_send, 0, requireextension);\n\t\t\t\tbuffer[1] = TEQW_NQEXPLOSION;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase TENQ_BEAM:\n\t\t\trequireextension = PEXT_TE_BULLET;\n\t\t\t//should translate it to lightning or something for old clients\n\t\t\tbuffer[1] = TEQW_BEAM;\n\t\t\tbreak;\n\t\tcase TENQ_EXPLOSION2:\t//happens with rogue.\n\t\t\trequireextension = PEXT_TE_BULLET;\n\t\t\tif (writedest == &sv.datagram || writedest == &sv.multicast)\n\t\t\t{\n\t\t\t\tvec3_t org;\n\t\t\t\tint coordsize = (destprim->coordtype&0xf);\n\t\t\t\tcoorddata cd;\n\t\t\t\tmemcpy(&cd, &buffer[2+coordsize*0], coordsize);\n\t\t\t\torg[0] = MSG_FromCoord(cd, coordsize);\n\t\t\t\tmemcpy(&cd, &buffer[2+coordsize*1], coordsize);\n\t\t\t\torg[1] = MSG_FromCoord(cd, coordsize);\n\t\t\t\tmemcpy(&cd, &buffer[2+coordsize*2], coordsize);\n\t\t\t\torg[2] = MSG_FromCoord(cd, coordsize);\n\n\t\t\t\tbuffer[1] = TEQW_QWEXPLOSION;\t\t\t\t\t//use a generic crappy explosion\n\t\t\t\tSZ_Write(&sv.multicast, buffer, bufferlen-2);\t//trim the two trailing colour bytes\n\t\t\t\tSV_MulticastProtExt(org, multicasttype, pr_global_struct->dimension_send, 0, requireextension);\n\t\t\t}\n\t\t\tbuffer[1] = TEQW_EXPLOSION2;\t//TENQ_EXPLOSION2 conflicts with TEQW_BLOOD\n\t\t\tbreak;\n\t\t}\n\t\tbreak;\n\t}\n\tif (ignoreprotocol)\n\t{\n\t\tignoreprotocol=false;\n\t\tbufferlen = 0;\n\t}\n\n\n\n\n\tif (cldest)\n\t{\n\t\tif (!requireextension || cldest->fteprotocolextensions & requireextension)\n\t\tif (bufferlen && ISQWCLIENT(cldest))\n\t\t{\n\t\t\tsizebuf_t *msg = ClientReliable_StartWrite(cldest, bufferlen);\n\t\t\tSZ_Write(msg, buffer, bufferlen);\n\t\t\tClientReliable_FinishWrite(cldest);\n\t\t}\n\t\tcldest = NULL;\n\t}\n\telse\n\t{\n\t\tif (multicastpos && (writedest == &sv.datagram || writedest == &sv.multicast))\n\t\t\twritedest = &sv.multicast;\n\t\telse\n\t\t\tmulticastpos = 0;\n\t\tif (bufferlen)\n\t\t{\n\t\t\tif (writedest->cursize + bufferlen > writedest->maxsize)\n\t\t\t{\n\t\t\t\tSV_FlushBroadcasts();\n\t\t\t}\n\t\t\tSZ_Write(writedest, buffer, bufferlen);\n\t\t}\n\n\t\tif (multicastpos)\n\t\t{\n\t\t\tvec3_t org;\n\t\t\tint coordsize = (destprim->coordtype&0xf);\n\t\t\tcoorddata cd;\n\n\t\t\tmemcpy(&cd, &buffer[multicastpos+coordsize*0], coordsize);\n\t\t\torg[0] = MSG_FromCoord(cd, coordsize);\n\t\t\tmemcpy(&cd, &buffer[multicastpos+coordsize*1], coordsize);\n\t\t\torg[1] = MSG_FromCoord(cd, coordsize);\n\t\t\tmemcpy(&cd, &buffer[multicastpos+coordsize*2], coordsize);\n\t\t\torg[2] = MSG_FromCoord(cd, coordsize);\n\n\t\t\tSV_MulticastProtExt(org, multicasttype, pr_global_struct->dimension_send, requireextension, 0);\n\t\t}\n\t\twritedest = NULL;\n\t}\n\tbufferlen = 0;\n\tprotocollen=0;\n\tnullterms = 0;\n\tmulticastpos=0;\n\trequireextension=0;\n}\nvoid NPP_NQCheckFlush(void)\n{\n\tif (bufferlen >= protocollen && protocollen && !nullterms)\n\t\tNPP_NQFlush();\n}\n\nvoid NPP_NQCheckDest(int dest)\n{\n\tif (dest == MSG_ONE)\n\t{\n\t\tclient_t *cl = Write_GetClient();\n\t\tif (!cl)\n\t\t{\n\t\t\tCon_Printf(\"Not a client\\n\");\n\t\t\treturn;\n\t\t}\n\t\tif (bufferlen && ((cldest && cldest != cl) || writedest))\n\t\t{\n\t\t\tCon_Printf(\"MSG destination changed in the middle of a packet %i.\\n\", (int)*buffer);\n\t\t\tNPP_NQFlush();\n\t\t}\n\t\twritedest = NULL;\n\t\tcldest = cl;\n\t\tdestprim = &cldest->netchan.message.prim;\n\t}\n\telse\n\t{\n\t\tsizebuf_t\t*ndest = QWWriteDest(dest);\n\t\tif (bufferlen && (cldest || (writedest && writedest != ndest)))\n\t\t{\n\t\t\tCon_DPrintf(\"NQCheckDest: MSG destination changed in the middle of a packet %i.\\n\", (int)*buffer);\n\t\t\tNPP_NQFlush();\n\t\t}\n\t\tcldest = NULL;\n\t\twritedest = ndest;\n\t\tdestprim = &writedest->prim;\n\t}\n}\nvoid NPP_AddData(const void *data, int len)\n{\n\tif (bufferlen+len > sizeof(buffer))\n\t{\n\t\tbufferlen = 0;\n\t\tSV_Error(\"Preparse buffer was filled\\n\");\n\t}\n\tmemcpy(buffer+bufferlen, data, len);\n\tbufferlen+=len;\n}\n\nvoid NPP_NQWriteByte(int dest, qbyte data)\t//replacement write func (nq to qw)\n{\n\tNPP_NQCheckDest(dest);\n\n#ifdef NQPROT\n\tif (dest == MSG_ONE)\n\t{\n\t\tclient_t *cl = Write_GetClient();\n\t\tif (!cl)\n\t\t{\n\t\t\tCon_Printf(\"msg_entity: not a client\\n\");\n\t\t\treturn;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (cl->protocol == SCP_BAD) // is a bot\n\t\t\t\treturn;\n\t\t\telse if (!ISQWCLIENT(cl))\n\t\t\t{\n\t\t\t\tClientReliableCheckBlock(cl, sizeof(qbyte));\n\t\t\t\tClientReliableWrite_Byte(cl, data);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n\telse\n\t\tMSG_WriteByte (NQWriteDest(dest), data);\n#endif\n\n\tif (!bufferlen)\t//new message section\n\t{\n\t\tmajortype = data;\n\t\tswitch(data)\n\t\t{\n\t\tcase svcdp_showlmp:\n\t\tcase svcdp_hidelmp:\n\t\tcase svc_sound:\n\t\t\tbreak;\n\t\tcase svc_temp_entity:\n\t\t\tte_515sevilhackworkaround = false;\n\t\t\tbreak;\n\t\tcase svc_setangle:\n\t\t\tprotocollen = sizeof(qbyte) + destprim->anglesize*3;\n\t\t\tbreak;\n\t\tcase svc_setview:\n\t\t\tprotocollen = sizeof(qbyte)*1 + sizeof(short);\n\t\t\tbreak;\n\t\tcase svc_updatename:\n\t\t\tnullterms = 1;\n\t\t\tbreak;\n\t\tcase svc_setfrags:\n\t\t\tprotocollen = 4;\n\t\t\tbreak;\n\t\tcase svc_updatecolors:\n\t\t\tprotocollen = 3;\n\t\t\tbreak;\n\t\tcase svc_print:\n\t\t\tprotocollen = 3;\n\t\t\tnullterms = 1;\n\t\t\tbreak;\n\t\tcase svc_cdtrack:\n\t\t\tif (progstype == PROG_QW)\n\t\t\t\tprotocollen = 2;\n\t\t\telse\n\t\t\t\tprotocollen = 3;\n\t\t\tbreak;\n\t\tcase svc_killedmonster:\n\t\t\tprotocollen = 1;\n\t\t\tbreak;\n\t\tcase svc_foundsecret:\n\t\t\tprotocollen = 1;\n\t\t\tbreak;\n\t\tcase svc_intermission:\n\t\t\tif (progstype == PROG_H2)\n\t\t\t\tprotocollen = 2;\n\t\t\telse\n\t\t\t\tprotocollen = 1;\n\t\t\tbreak;\n\t\tcase svc_finale:\n\t\t\tnullterms = 1;\n\t\t\tprotocollen = 2;\n\t\t\tbreak;\n\t\tcase svcdp_skybox:\n\t\t\tprotocollen = 2;//it's just a string\n\t\t\tbreak;\n\t\tcase svcnq_updatestatlong:\t//insta fixup\n\t\t\tdata = svcqw_updatestatlong;\t//ho hum... let it through (should check size later.)\n\t\t\tprotocollen = 6;\n\t\t\tbreak;\n\t\tcase svcqex_achievement:\n\t\t\tignoreprotocol = true;\n\t\t\tnullterms = 1;\n\t\t\tbreak;\n\t\tcase svc_stufftext:\n\t\tcase svc_centerprint:\n\t\t\tnullterms = 1;\n\t\t\tbreak;\n#ifdef HEXEN2\n\t\tcase svch2_setviewflags:\t//sets some viewmodel drawflag\n\t\tcase svch2_clearviewflags:\t//undoes svch2_setviewflags\n\t\tcase svch2_setviewtint:\t\t//tints the viewmodel (tied to hexen2's weird colormap stuff)\n\t\t\tif (progstype == PROG_H2)\n\t\t\t{\n\t\t\t\tprotocollen = 2;\n\t\t\t\tignoreprotocol = true;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tCon_DPrintf(\"NQWriteByte: bad protocol %i\\n\", (int)data);\n\t\t\t\tprotocollen = sizeof(buffer);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase svch2_setangles_lerp:\n\t\t\tif (progstype == PROG_H2)\n\t\t\t{\n\t\t\t\tmajortype = data = svc_setangle;\n\t\t\t\tprotocollen = sizeof(qbyte) + destprim->anglesize*3;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tCon_DPrintf(\"NQWriteByte: bad protocol %i\\n\", (int)data);\n\t\t\t\tprotocollen = sizeof(buffer);\n\t\t\t}\n\t\t\tbreak;\n#endif\n\t\tcase svc_cutscene:\n\t\t\tnullterms = 1;\n\t\t\tbreak;\n\t\tcase svcdp_updatestatbyte:\n\t\t\tprotocollen = 3;\n\t\t\tignoreprotocol = true;\n\t\t\tbreak;\n\t\tcase svcfte_cgamepacket:\n\t\t\tprotocollen = sizeof(buffer);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tif (data & 128)\n\t\t\t{\n\t\t\t\tmajortype = 128;\t//nq 'fast update'\n\t\t\t\tignoreprotocol = true;\n\t\t\t\t//this handling is explicitly for hipnotic.\n\t\t\t\t//hipnotic sends a camera entity this way so that svc_setview knows where to position itself.\n\t\t\t\t//we emulate this with quakeworld protocols, so we don't necessarily need to support it other than to strip it.\n\t\t\t\t//we're probably screwed if we're acting as an nq server, but few people will use that.\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tCon_DPrintf(\"NQWriteByte: bad protocol %i\\n\", (int)data);\n\t\t\t\tprotocollen = sizeof(buffer);\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t}\n\tif (bufferlen == 1 && !protocollen)\t//some of them depend on the following bytes for size.\n\t{\n\t\tswitch(majortype)\n\t\t{\n\t\tcase 128:\n#ifdef NQPROT\n\t\t\t{\n\t\t\t\tunsigned int bits = buffer[0];\n\t\t\t\tprotocollen = 1;\n\t\t\t\tif (bits & NQU_MOREBITS)\n\t\t\t\t{\n\t\t\t\t\tbits |= data<<8;\n\t\t\t\t\tprotocollen+=1;\n\t\t\t\t}\n\n\t\t\t\tif (bits != (NQU_SIGNAL|NQU_MOREBITS|NQU_ORIGIN1|NQU_ORIGIN2|NQU_ORIGIN3|NQU_LONGENTITY))\n\t\t\t\t{\t//this mask is what hipnotic uses. warn if anyone else tries it.\n\t\t\t\t\tCon_Printf(\"fast update will be stripped\\n\");\n\t\t\t\t\tPR_StackTrace(svprogfuncs, false);\n\t\t\t\t}\n\t\t\t\tif (bits & NQU_ORIGIN1)\n\t\t\t\t\tprotocollen+=2;\n\t\t\t\tif (bits & NQU_ORIGIN2)\n\t\t\t\t\tprotocollen+=2;\n\t\t\t\tif (bits & NQU_ORIGIN3)\n\t\t\t\t\tprotocollen+=2;\n\t\t\t\tif (bits & NQU_ANGLE2)\n\t\t\t\t\tprotocollen+=1;\n\t\t\t\t//nolerp no data\n\t\t\t\tif (bits & NQU_FRAME)\n\t\t\t\t\tprotocollen+=1;\n\n\t\t\t\tif (bits & NQU_ANGLE1)\n\t\t\t\t\tprotocollen+=1;\n\t\t\t\tif (bits & NQU_ANGLE3)\n\t\t\t\t\tprotocollen+=1;\n\t\t\t\tif (bits & NQU_MODEL)\n\t\t\t\t\tprotocollen+=1;\n\t\t\t\tif (bits & NQU_COLORMAP)\n\t\t\t\t\tprotocollen+=1;\n\t\t\t\tif (bits & NQU_SKIN)\n\t\t\t\t\tprotocollen+=1;\n\t\t\t\tif (bits & NQU_EFFECTS)\n\t\t\t\t\tprotocollen+=1;\n\t\t\t\tif (bits & NQU_LONGENTITY)\n\t\t\t\t\tprotocollen+=2;\n\t\t\t\telse\n\t\t\t\t\tprotocollen+=1;\n\t\t\t\tif (bits & DPU_EXTEND1)\n\t\t\t\t\tCon_DPrintf(\"NQWriteByte: fast update with extension bits is not supported\\n\");\n\t\t\t}\n#else\n\t\t\tignoreprotocol = true;\n#endif\n\t\t\tbreak;\n\t\tcase svc_sound:\n\t\t\tprotocollen = 5+(destprim->coordtype&0xf)*3;\n\t\t\tif (data & NQSND_VOLUME)\n\t\t\t\tprotocollen++;\n\t\t\tif (data & NQSND_ATTENUATION)\n\t\t\t\tprotocollen++;\n\t\t\tif (data & NQSND_LARGEENTITY)//extension\n\t\t\t\tprotocollen++;\n\t\t\tif (data & NQSND_LARGESOUND)//extension\n\t\t\t\tprotocollen++;\n#ifdef warningmsg\n#pragma warningmsg(\"NPP_NQWriteByte: this ignores SVC_SOUND from nq mods (nexuiz)\")\n#endif\n\t\t\tignoreprotocol = true;\n\t\t\tbreak;\n\t\tcase svc_temp_entity:\n\t\t\tswitch(data)\n\t\t\t{\n\t\t\tcase TENQ_BEAM:\n\t\t\tcase TE_LIGHTNING1:\n\t\t\tcase TE_LIGHTNING2:\n\t\t\tcase TE_LIGHTNING3:\n\t\t\t\tmulticastpos=4;\n\t\t\t\tmulticasttype=MULTICAST_PHS;\n\t\t\t\tprotocollen = (destprim->coordtype&0xf)*6+sizeof(short)+sizeof(qbyte)*2;\n\t\t\t\tbreak;\n\t\t\tcase TENQ_NQGUNSHOT:\n\t\t\t\tmulticastpos=3;\n\t\t\t\tmulticasttype=MULTICAST_PVS;\n\t\t\t\t//we need to emit another qbyte here. QuakeWorld has a number of particles.\n\t\t\t\t//emit it here and we don't need to remember to play with temp_entity later\n\t\t\t\tNPP_AddData(&data, sizeof(qbyte));\n\t\t\t\tdata = 1;\n\t\t\t\tprotocollen = (destprim->coordtype&0xf)*3+sizeof(qbyte)*3;\n\t\t\t\tbreak;\n\t\t\tcase TENQ_NQEXPLOSION:\n\t\t\tcase TE_SPIKE:\n\t\t\tcase TE_SUPERSPIKE:\n\t\t\t\tmulticastpos=2;\n\t\t\t\tmulticasttype=MULTICAST_PHS;\n\t\t\t\tprotocollen = (destprim->coordtype&0xf)*3+sizeof(qbyte)*2;\n\t\t\t\tbreak;\n\t\t\tcase TE_LAVASPLASH:\n\t\t\t\tmulticastpos=2;\n\t\t\t\tmulticasttype=MULTICAST_ALL;\n\t\t\t\tprotocollen = (destprim->coordtype&0xf)*3+sizeof(qbyte)*2;\n\t\t\t\tbreak;\n\t\t\tcase TE_TAREXPLOSION:\n\t\t\tcase TE_WIZSPIKE:\n\t\t\tcase TE_KNIGHTSPIKE:\n\t\t\tcase TE_TELEPORT:\n\t\t\t\tmulticastpos=2;\n\t\t\t\tmulticasttype=MULTICAST_PVS;\n\t\t\t\tprotocollen = (destprim->coordtype&0xf)*3+sizeof(qbyte)*2;\n\t\t\t\tbreak;\n\t\t\tcase TE_EXPLOSION3_NEH:\n\t\t\t\tprotocollen = sizeof(qbyte) + (destprim->coordtype&0xf)*6;\n\t\t\t\tignoreprotocol = true;\n\t\t\t\tbreak;\n\t\t\tcase TENQ_EXPLOSION2:\n\t\t\t\tprotocollen = sizeof(qbyte)*4 + (destprim->coordtype&0xf)*3;\n\t\t\t\tmulticastpos=2;\n\t\t\t\tmulticasttype=MULTICAST_PHS;\n\t\t\t\tbreak;\n\t\t\tcase TE_EXPLOSIONSMALL2:\n\t\t\t\tdata = TEQW_QWEXPLOSION;\n\t\t\t\tprotocollen = sizeof(qbyte)*2 + (destprim->coordtype&0xf)*3;\n\t\t\t\tmulticastpos=2;\n\t\t\t\tmulticasttype=MULTICAST_PHS;\n\t\t\t\tbreak;\n\t\t\tcase TEQW_RAILTRAIL:\n\t\t\t\tprotocollen = (destprim->coordtype&0xf)*6+sizeof(qbyte)*1;\n\t\t\t\tmulticastpos=2;\n\t\t\t\tmulticasttype=MULTICAST_PHS;\n\t\t\t\tbreak;\n\t\t\tcase TEH2_STREAM_LIGHTNING_SMALL:\n\t\t\tcase TEH2_STREAM_CHAIN:\n\t\t\tcase TEH2_STREAM_SUNSTAFF1:\n\t\t\tcase TEH2_STREAM_SUNSTAFF2:\n\t\t\tcase TEH2_STREAM_LIGHTNING:\n\t\t\tcase TEH2_STREAM_ICECHUNKS:\n\t\t\tcase TEH2_STREAM_GAZE:\n\t\t\tcase TEH2_STREAM_FAMINE:\n\t\t\t\tprotocollen = (destprim->coordtype&0xf)*6+sizeof(short)+sizeof(qbyte)*(2+2);\n\t\t\t\tmulticastpos = 8;\n\t\t\t\tmulticasttype=MULTICAST_PHS;\n\t\t\t\tbreak;\n\t\t\tcase TEH2_STREAM_COLORBEAM:\n\t\t\t\tprotocollen = (destprim->coordtype&0xf)*6+sizeof(short)+sizeof(qbyte)*(3+2);\n\t\t\t\tmulticastpos = 8;\n\t\t\t\tmulticasttype=MULTICAST_PHS;\n\t\t\t\tbreak;\n\n\t\t\tcase TEDP_FLAMEJET:\t//TE_FLAMEJET\n\t\t\t\tprotocollen = (destprim->coordtype&0xf)*6 +sizeof(qbyte)*3;\n\t\t\t\tmulticastpos = 2;\n\t\t\t\tmulticasttype=MULTICAST_PVS;\n\t\t\t\tbreak;\n\n\t\t\tcase TEDP_TEI_G3:\n\t\t\t\tprotocollen = (destprim->coordtype&0xf)*9+sizeof(qbyte)*2;\n\t\t\t\tmulticastpos = 2;\n\t\t\t\tmulticasttype=MULTICAST_PHS;\n\t\t\t\tbreak;\n\n\t\t\tcase TEDP_SMOKE:\n\t\t\t\tprotocollen = (destprim->coordtype&0xf)*6+sizeof(qbyte)*3;\n\t\t\t\tmulticastpos = 2;\n\t\t\t\tmulticasttype=MULTICAST_PHS;\n\t\t\t\tbreak;\n\n\t\t\tcase TEDP_TEI_BIGEXPLOSION:\n\t\t\t\tprotocollen = (destprim->coordtype&0xf)*3+sizeof(qbyte)*2;\n\t\t\t\tmulticastpos = 2;\n\t\t\t\tmulticasttype=MULTICAST_PHS;\n\t\t\t\tbreak;\n\n\t\t\tcase TEDP_TEI_PLASMAHIT:\n\t\t\t\tprotocollen = (destprim->coordtype&0xf)*6+sizeof(qbyte)*3;\n\t\t\t\tmulticastpos = 2;\n\t\t\t\tmulticasttype=MULTICAST_PHS;\n\t\t\t\tbreak;\n\n\t\t\tdefault:\n\t\t\t\tprotocollen = sizeof(buffer);\n\t\t\t\tif (dest == MSG_MULTICAST)\n\t\t\t\t{\n\t\t\t\t\tCon_DPrintf(\"NQWriteByte: unknown tempentity %i\\n\", data);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tte_515sevilhackworkaround = true;\n\t\t\t\t\tif (!ssqc_deprecated_warned)\n\t\t\t\t\t{\n\t\t\t\t\t\tssqc_deprecated_warned = true;\n\t\t\t\t\t\tCon_Printf(\"NQWriteByte: invalid tempentity %i. Future errors will be dprinted. You may need to enable sv_csqcdebug.\\n\", data);\n\t\t\t\t\t\tPR_StackTrace(svprogfuncs, false);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\tCon_DPrintf(\"NQWriteByte: unknown tempentity %i\\n\", data);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase svcqex_achievement:\n\t\tcase svc_updatename:\n\t\tcase svc_stufftext:\n\t\tcase svc_centerprint:\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tCon_Printf(\"NQWriteByte: Non-Implemented svc\\n\");\n\t\t\tprotocollen = sizeof(buffer);\n\t\t\tbreak;\n\t\t}\n\t}\n\tif (!protocollen)\t//these protocols take strings, and are thus dynamically sized.\n\t{\n\t\tswitch(majortype)\n\t\t{\n\t\tcase svc_updatename:\n\t\t\tif (bufferlen < 2)\n\t\t\t\tbreak;\t//don't truncate the name if the mod is sending the slot number\n\t\tcase svcqex_achievement:\n\t\tcase svc_stufftext:\n\t\tcase svc_centerprint:\n\t\tcase svc_cutscene:\n\t\t\tif (!data)\n\t\t\t\tprotocollen = bufferlen;\n\t\t\tbreak;\n\t\tcase svcdp_hidelmp:\n\t\t\t//tenebrae compat:\n\t\t\tif (progstype == PROG_TENEBRAE)\n\t\t\t{\n\t\t\t\t//svc, coord6, byte, long, long, effectname\n\t\t\t\tif (bufferlen >= sizeof(qbyte)*2+(destprim->coordtype&0xf)*6+sizeof(int)*2 && !data)\n\t\t\t\t\tprotocollen = bufferlen;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\t//nehahra/dp compat\n\t\t\tif (!data)\n\t\t\t\tprotocollen = bufferlen;\n\t\t\tbreak;\n\t\tcase svcdp_showlmp:\n\t\t\t//tenebrae compat:\n\t\t\tif (progstype == PROG_TENEBRAE)\n\t\t\t{\n\t\t\t\t//svc, coord3, byte, effectname\n\t\t\t\tif (bufferlen >= sizeof(qbyte)*2+(destprim->coordtype&0xf)*3 && !data)\n\t\t\t\t\tprotocollen = bufferlen;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\t// [string] slotname [string] lmpfilename [byte] x [byte] y\n\t\t\t//note: nehara uses bytes!\n\t\t\t//and the rest of dp uses shorts. how nasty is that?\n\t\t\tif (!data)\n\t\t\t{\t//second string, plus 2 bytes.\n\t\t\t\tint i;\n\t\t\t\tfor (i = 0; i < bufferlen; i++)\n\t\t\t\t\tif (!buffer[i])\n\t\t\t\t\t\tprotocollen = bufferlen+2;\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tNPP_AddData(&data, sizeof(qbyte));\n\tif (!data && bufferlen>=protocollen)\n\t\tif (nullterms)\n\t\t\tnullterms--;\n\tNPP_NQCheckFlush();\n}\n\nvoid NPP_NQWriteChar(int dest, char data)\t//replacement write func (nq to qw)\n{\n\tNPP_NQWriteByte(dest, (qbyte)data);\n\treturn;\n\t/*\n\tNPP_NQCheckDest(dest);\n\tif (!bufferlen)\n\t{\n\t\tNPP_NQWriteByte(dest, (qbyte)data);\n\t\treturn;\n\t}\n\n#ifdef NQPROT\n\tif (dest == MSG_ONE) {\n\t\tclient_t *cl = Write_GetClient();\n\t\tif (cl && cl->nqprot)\n\t\t{\n\t\t\tClientReliableCheckBlock(cl, sizeof(char));\n\t\t\tClientReliableWrite_Char(cl, data);\n\t\t}\n\t} else\n\t\tMSG_WriteChar (NQWriteDest(dest), data);\n#endif\n\n\tNPP_AddData(&data, sizeof(char));\n\tNPP_NQCheckFlush();*/\n}\n\nvoid NPP_NQWriteShort(int dest, short data)\t//replacement write func (nq to qw)\n{\n\tunion {\n\t\tqbyte b[2];\n\t\tshort s;\n\t} u;\n\tu.s = LittleShort(data);\n\tNPP_NQWriteByte(dest, u.b[0]);\n\tNPP_NQWriteByte(dest, u.b[1]);\n}\n\nvoid NPP_NQWriteLong(int dest, long data)\t//replacement write func (nq to qw)\n{\n\tunion {\n\t\tqbyte b[4];\n\t\tint l;\n\t} u;\n\tu.l = LittleLong(data);\n\tNPP_NQWriteByte(dest, u.b[0]);\n\tNPP_NQWriteByte(dest, u.b[1]);\n\tNPP_NQWriteByte(dest, u.b[2]);\n\tNPP_NQWriteByte(dest, u.b[3]);\n}\nvoid NPP_NQWriteFloat(int dest, float data)\t//replacement write func (nq to qw)\n{\n\tunion {\n\t\tqbyte b[4];\n\t\tfloat f;\n\t} u;\n\tu.f = LittleFloat(data);\n\tNPP_NQWriteByte(dest, u.b[0]);\n\tNPP_NQWriteByte(dest, u.b[1]);\n\tNPP_NQWriteByte(dest, u.b[2]);\n\tNPP_NQWriteByte(dest, u.b[3]);\n}\nvoid NPP_NQWriteAngle(int dest, float in)\t//replacement write func (nq to qw)\n{\n\tchar data = (int)(in*256/360) & 255;\n\tNPP_NQCheckDest(dest);\n\n#ifdef NQPROT\n\tif (cldest)\n\t{\n\t\tif (cldest->protocol == SCP_BAD)\n\t\t\treturn;\n\t\telse if (!ISQWCLIENT(cldest))\n\t\t{\n\t\t\tClientReliableCheckBlock(cldest, sizeof(char));\n\t\t\tClientReliableWrite_Angle(cldest, in);\n\t\t\treturn;\n\t\t}\n\t}\n\telse\n\t\tMSG_WriteAngle (NQWriteDest(dest), in);\n#endif\n\n\tif (!bufferlen)\n\t{\n\t\tCon_Printf(\"NQWriteAngle: Messages should start with WriteByte (last was %i)\\n\", majortype);\n\t\tPR_StackTrace(svprogfuncs, false);\n\t}\n\n\tif (destprim->anglesize==2)\n\t{\n\t\tcoorddata cd = MSG_ToAngle(in, destprim->anglesize);\n\t\tNPP_AddData(&cd.b2, sizeof(cd.b2));\n\t}\n\telse\n\t\tNPP_AddData(&data, sizeof(char));\n\tNPP_NQCheckFlush();\n}\nvoid NPP_NQWriteCoord(int dest, float in)\t//replacement write func (nq to qw)\n{\n\tNPP_NQCheckDest(dest);\n\n#ifdef NQPROT\n\tif (dest == MSG_ONE)\n\t{\n\t\tclient_t *cl = Write_GetClient();\n\t\tif (!cl)\n\t\t{\n\t\t\tCon_Printf(\"msg_entity: not a client\\n\");\n\t\t\treturn;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (cl->protocol == SCP_BAD)\n\t\t\t\treturn;\n\t\t\telse if (!ISQWCLIENT(cl))\n\t\t\t{\n\t\t\t\tClientReliableCheckBlock(cl, sizeof(float));\n\t\t\t\tClientReliableWrite_Coord(cl, in);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n\telse\n\t\tMSG_WriteCoord (NQWriteDest(dest), in);\n#endif\n\n\tif (!bufferlen)\n\t{\n\t\tCon_Printf(\"NQWriteCoord: Messages should start with WriteByte\\n\");\n\t\tPR_StackTrace(svprogfuncs, false);\n\t}\n\n\tif (destprim->coordtype==COORDTYPE_FLOAT_32)\n\t{\n\t\tfloat dataf = in;\n\n\t\tdataf = LittleFloat(dataf);\n\t\tNPP_AddData(&dataf, sizeof(float));\n\t}\n\telse\n\t{\n\t\tshort datas = (int)(in*8)&0xffff;\n\n\t\tdatas = LittleShort(datas);\n\t\tNPP_AddData(&datas, sizeof(short));\n\t}\n\tNPP_NQCheckFlush();\n}\nvoid NPP_NQWriteString(int dest, const char *data)\t//replacement write func (nq to qw)\n{\n\tNPP_NQCheckDest(dest);\n\n#ifdef NQPROT\n\tif (dest == MSG_ONE)\n\t{\n\t\tclient_t *cl = Write_GetClient();\n\t\tif (!cl)\n\t\t{\n\t\t\tCon_Printf(\"msg_entity: not a client\\n\");\n\t\t\treturn;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (cl->protocol == SCP_BAD)\n\t\t\t\treturn;\n\t\t\telse if (!ISQWCLIENT(cl))\n\t\t\t{\n\t\t\t\tClientReliableCheckBlock(cl, strlen(data)+1);\n\t\t\t\tClientReliableWrite_String(cl, data);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n\telse\n\t\tMSG_WriteString (NQWriteDest(dest), data);\n#endif\n\n\tif (!bufferlen)\n\t{\n\t\tCon_Printf(\"NQWriteString: Messages should start with WriteByte\\n\");\n\t\tPR_StackTrace(svprogfuncs, false);\n\t}\n\n\tNPP_AddData(data, strlen(data)+1);\n\n\tif (!protocollen)\t//these protocols take strings, and are thus dynamically sized.\n\t{\n\t\tswitch(majortype)\n\t\t{\n\t\tcase svc_updatename:\n\t\tcase svc_stufftext:\n\t\tcase svc_centerprint:\n\t\tcase svc_cutscene:\n\t\t\tprotocollen = bufferlen;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tif (nullterms)\n\t\tnullterms--;\n\tNPP_NQCheckFlush();\n}\nvoid NPP_NQWriteEntity(int dest, int data)\t//replacement write func (nq to qw)\n{\n\tNPP_NQCheckDest(dest);\n\n\tif (majortype == svc_temp_entity && data > 0 && data <= sv.allocated_client_slots)\n\t\tif (svs.clients[data-1].viewent)\n\t\t\tdata = svs.clients[data-1].viewent;\n\n#ifdef NQPROT\n\tif (dest == MSG_ONE)\n\t{\n\t\tclient_t *cl = Write_GetClient();\n\t\tif (!cl)\n\t\t{\n\t\t\tCon_Printf(\"msg_entity: not a client\\n\");\n\t\t\treturn;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (cl->protocol == SCP_BAD)\n\t\t\t\treturn;\n\t\t\telse if (!ISQWCLIENT(cl))\n\t\t\t{\n\t\t\t\tClientReliableCheckBlock(cl, sizeof(short));\n\t\t\t\tClientReliableWrite_Entity(cl, data);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n\telse\n\t\tMSG_WriteEntity (NQWriteDest(dest), data);\n#endif\n\n\tif (!bufferlen)\n\t{\n\t\tCon_Printf(\"NQWriteEntity: Messages should start with WriteByte\\n\");\n\t\tPR_StackTrace(svprogfuncs, false);\n\t}\n\n\n\tNPP_AddData(&data, sizeof(short));\n\tNPP_NQCheckFlush();\n}\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n#ifdef NQPROT\n\nfloat NPP_ReadFloat(qbyte *buf)\n{\n\tunion\n\t{\n\t\tfloat f;\n\t\tqbyte b[4];\n\t} u;\n\tmemcpy(u.b, buf, sizeof(u.f));\n\treturn LittleFloat(u.f);\n}\nshort NPP_ReadShort(qbyte *buf)\n{\n\tunion\n\t{\n\t\tshort s;\n\t\tqbyte b[2];\n\t} u;\n\tmemcpy(u.b, buf, sizeof(u.s));\n\treturn LittleShort(u.s);\n}\nunsigned short NPP_ReadUShort(qbyte *buf)\n{\n\tunion\n\t{\n\t\tunsigned short s;\n\t\tqbyte b[2];\n\t} u;\n\tmemcpy(u.b, buf, sizeof(u.s));\n\treturn LittleShort(u.s);\n}\n\n//qw to nq translation is only useful if we allow nq clients to connect.\n\nvoid NPP_QWFlush(void)\n{\n\tqbyte b;\n\tif (!bufferlen)\n\t\treturn;\n\n\tswitch(majortype)\n\t{\n\tcase svc_updatename:\t//not a standard feature, but hey, if a progs wants bots.\n\t\tNPP_SetInfo(&svs.clients[buffer[1]], \"name\", buffer+2);\n\t\tbreak;\n\tcase svc_updatecolors:\n\t\tNPP_SetInfo(&svs.clients[buffer[1]], \"colours\", va(\"%i\", buffer[2]));\n\t\tbreak;\n\tcase svc_cdtrack:\n\t\tif (bufferlen!=protocollen)\n\t\t\tCon_Printf(\"QWFlush: svc_cdtrack wasn't the right length\\n\");\n\t\telse\n\t\t{\n\t\t\t//qw cdtracks have only a loop byte.\n\t\t\t//nq has initial+loop values.\n\t\t\tb = (bufferlen==2)?buffer[1]:0;\n\t\t\tNPP_AddData(&b, sizeof(qbyte));\n\t\t}\n\t\tbreak;\n\t\t//ignore these.\n\tcase svc_intermission:\n#if defined(HAVE_CLIENT)\n\t\tLog_MapNowCompleted();\n#endif\n//\t\tif (writedest == &sv.reliable_datagram)\n\t\t{\n\t\t\tclient_t *cl;\n\t\t\tint i;\n\t\t\tfor (i = 0, cl = svs.clients; i < sv.allocated_client_slots; i++, cl++)\n\t\t\t{\n\t\t\t\tif (cl->state == cs_spawned && ISNQCLIENT(cl))\n\t\t\t\t{\n\t\t\t\t\tvec3_t org, ang;\n\n\t\t\t\t\tif (cl->zquake_extensions & Z_EXT_SERVERTIME)\n\t\t\t\t\t{\n\t\t\t\t\t\tClientReliableCheckBlock(cl, 6);\n\t\t\t\t\t\tClientReliableWrite_Byte(cl, svcqw_updatestatlong);\n\t\t\t\t\t\tClientReliableWrite_Byte(cl, STAT_TIME);\n\t\t\t\t\t\tClientReliableWrite_Long(cl, (int)(sv.world.physicstime * 1000));\n\t\t\t\t\t\tcl->nextservertimeupdate = sv.world.physicstime+10;\n\t\t\t\t\t}\n\n\t\t\t\t\tClientReliableCheckBlock(cl, 1);\n\t\t\t\t\tClientReliableWrite_Byte(cl, svc_intermission);\n\n\t\t\t\t\ti = 1;\n\t\t\t\t\tif (destprim->coordtype==COORDTYPE_FLOAT_32)\n\t\t\t\t\t{\n\t\t\t\t\t\torg[0] = NPP_ReadFloat(buffer+i+0);\n\t\t\t\t\t\torg[1] = NPP_ReadFloat(buffer+i+4);\n\t\t\t\t\t\torg[2] = NPP_ReadFloat(buffer+i+8);\n\t\t\t\t\t\ti += 12;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\torg[0] = NPP_ReadShort(buffer+i+0) / 8.0;\n\t\t\t\t\t\torg[1] = NPP_ReadShort(buffer+i+2) / 8.0;\n\t\t\t\t\t\torg[2] = NPP_ReadShort(buffer+i+4) / 8.0;\n\t\t\t\t\t\ti += 6;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (destprim->anglesize == 2)\n\t\t\t\t\t{\n\t\t\t\t\t\tang[0] = NPP_ReadUShort(buffer+i+0)*360.0/0xffff;\n\t\t\t\t\t\tang[1] = NPP_ReadUShort(buffer+i+2)*360.0/0xffff;\n\t\t\t\t\t\tang[2] = NPP_ReadUShort(buffer+i+4)*360.0/0xffff;\n\t\t\t\t\t\ti += 6;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tang[0] = (*(qbyte*)&buffer[i+0])*360.0/0xff;\n\t\t\t\t\t\tang[1] = (*(qbyte*)&buffer[i+1])*360.0/0xff;\n\t\t\t\t\t\tang[2] = (*(qbyte*)&buffer[i+2])*360.0/0xff;\n\t\t\t\t\t\ti += 3;\n\t\t\t\t\t}\n\n\t\t\t\t\t//move nq players to origin + angle\n\t\t\t\t\tVectorCopy(org, cl->edict->v->origin);\n\t\t\t\t\tVectorCopy(ang, cl->edict->v->angles);\n\t\t\t\t\tcl->edict->v->angles[0]*=r_meshpitch.value;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tbufferlen = 0;\n\t\tprotocollen=0;\n\t\twritedest = NULL;\n//\tcase svc_finale:\n//\t\tbufferlen = 0;\n\t\tbreak;\n\tcase svc_setview:\n\t\trequireextension = PEXT_SETVIEW;\n//\t\tbufferlen = 0;\n\t\tbreak;\n\tcase svc_muzzleflash:\n\t\tif (bufferlen < 3)\n\t\t\tCon_Printf(\"Dodgy muzzleflash\\n\");\n\t\telse\n\t\t{\n\t\t\tshort data;\n\t\t\tfloat org[3];\n\t\t\tedict_t *ent = EDICT_NUM_UB(svprogfuncs, LittleShort((*(short*)&buffer[1])));\n\t\t\tent->muzzletime = sv.world.physicstime+host_frametime;\t//flag the entity as needing an EF_MUZZLEFLASH\n\t\t\tVectorCopy(ent->v->origin, org);\n\n\t\t\t//we need to make a fake muzzleflash position for multicast to work properly.\n\t\t\tmulticastpos = 4;\n\t\t\tdata = LittleShort((short)(org[0]*8));\n\t\t\tNPP_AddData(&data, sizeof(short));\n\t\t\tdata = LittleShort((short)(org[1]*8));\n\t\t\tNPP_AddData(&data, sizeof(short));\n\t\t\tdata = LittleShort((short)(org[2]*8));\n\t\t\tNPP_AddData(&data, sizeof(short));\n\t\t}\n\t\tbufferlen = 0;\t//can't send this to nq. :(\n\t\tbreak;\n\tcase svc_smallkick:\n\tcase svc_bigkick:\n\t\tbufferlen = 0;\n\t\tbreak;\n\tcase svcqw_updatestatlong:\n\t\tbuffer[0] = svcnq_updatestatlong;\n\t\tbreak;\n\tcase svc_updateuserinfo:\n\t\tif (buffer[6])\n\t\t{\n\t\t\tunsigned int j = buffer[1];\n\t\t\tif (j < sv.allocated_client_slots)\n\t\t\t{\n\t\t\t\tInfoBuf_FromString(&svs.clients[j].userinfo, buffer+6, false);\n\t\t\t\tif (*InfoBuf_ValueForKey(&svs.clients[j].userinfo, \"name\"))\n\t\t\t\t\tSV_ExtractFromUserinfo(&svs.clients[j], false);\n\t\t\t\telse\n\t\t\t\t\t*svs.clients[j].name = '\\0';\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tunsigned int j = buffer[1];\n\t\t\tif (j < sv.allocated_client_slots)\n\t\t\t{\n\t\t\t\t*svs.clients[j].name = '\\0';\n\t\t\t\tInfoBuf_Clear(&svs.clients[j].userinfo, true);\n\t\t\t}\n\t\t}\n\n\t\tbreak;\n\tcase svcfte_cgamepacket:\n\t\trequireextension = PEXT_CSQC;\n\t\tif (sv_csqcdebug.ival || writedest != &sv.nqmulticast)\n\t\t{\n\t\t\tif (writedest != &sv.nqmulticast)\n\t\t\t\tCon_Printf(CON_WARNING\"Warning: svc_cgamepacket used outside of a multicast\\n\");\n\t\t\t/*shift the data up by two bytes*/\n\t\t\tmemmove(buffer+3, buffer+1, bufferlen-1);\n\n\t\t\tbuffer[0] = svcfte_cgamepacket_sized;\n\t\t\t/*add a length in the 2nd/3rd bytes*/\n\t\t\tbuffer[1] = (bufferlen-1);\n\t\t\tbuffer[2] = (bufferlen-1) >> 8;\n\n\t\t\tbufferlen += 2;\n\t\t\tif(multicastpos) multicastpos += 2;\n\t\t}\n\t\tbreak;\n\tcase svc_temp_entity:\n\t\tif (sv_csqcdebug.ival)\n\t\t{\n\t\t\tif (te_515sevilhackworkaround && writedest != &sv.multicast)\n\t\t\t\tCon_Printf(CON_WARNING\"Warning: unknown svc_temp_entity used outside of a multicast\\n\");\n\t\t\t/*shift the data up by two bytes, but don't care about the first byte*/\n\t\t\tmemmove(buffer+3, buffer+1, bufferlen-1);\n\n\t\t\tbuffer[0] = svcfte_temp_entity_sized;\n\t\t\t/*add a length in the 2nd/3rd bytes, if needed*/\n\t\t\tbuffer[1] = (bufferlen-1) & 0xff;\n\t\t\tbuffer[2] = (bufferlen-1) >> 8;\n\t\t\tif (!te_515sevilhackworkaround)\t//we translated it from qw, let the client know that its now the nq version\n\t\t\t\tbuffer[2] |= 0x80;\n\n\t\t\tbufferlen += 2;\n\t\t\tif(multicastpos) multicastpos += 2;\n\t\t\tbreak;\n\t\t}\n\t\tswitch(minortype)\n\t\t{\n\t\tdefault:\n\t\t\tif (te_515sevilhackworkaround)\n\t\t\t{\n\t\t\t\tCon_Printf(CON_WARNING\"Warning: unknown svc_temp_entity used outside of a multicast\\n\");\n\t\t\t\t/*shift the data up by two bytes*/\n\t\t\t\tmemmove(buffer+3, buffer+1, bufferlen-1);\n\n\t\t\t\tbuffer[0] = svcfte_temp_entity_sized;\n\t\t\t\t/*add a length in the 2nd/3rd bytes*/\n\t\t\t\tbuffer[1] = (bufferlen-1);\n\t\t\t\tbuffer[2] = (bufferlen-1) >> 8;\n\t\t\t\tif (!te_515sevilhackworkaround)\t//we translated it from qw, let the client know that its now the nq version\n\t\t\t\t\tbuffer[2] |= 0x80;\n\n\t\t\t\tbufferlen += 2;\n\t\t\t\tif(multicastpos) multicastpos += 2;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase TEQW_LIGHTNINGBLOOD:\n\t\tcase TEQW_QWBLOOD:\t\t//needs to be converted to an svc_particle\n\t\t\t{\n\t\t\t\tvec3_t org;\n\t\t\t\tqbyte count;\n\t\t\t\tqbyte colour;\n\t\t\t\tchar dir[3];\n\t\t\t\tshort s;\n\t\t\t\tint v;\n\t\t\t\tint i;\n\t\t\t\tqbyte svc;\n\t\t\t\tsvc = svc_particle;\n\t\t\t\torg[0] = (*(short*)&buffer[multicastpos])/8.0f;\n\t\t\t\torg[1] = (*(short*)&buffer[multicastpos+2])/8.0f;\n\t\t\t\torg[2] = (*(short*)&buffer[multicastpos+4])/8.0f;\n\t\t\t\tcount = bound(0, buffer[2]*20, 254);\t//255 is taken to mean an explosion, for some reason.\n\t\t\t\tif (minortype == TEQW_LIGHTNINGBLOOD)\n\t\t\t\t\tcolour = 225;\n\t\t\t\telse\n\t\t\t\t\tcolour = 73;\n\n\t\t\t\tfor (i=0 ; i<3 ; i++)\n\t\t\t\t{\n\t\t\t\t\tv = 0*16;\n\t\t\t\t\tif (v > 127)\n\t\t\t\t\t\tv = 127;\n\t\t\t\t\telse if (v < -128)\n\t\t\t\t\t\tv = -128;\n\t\t\t\t\tdir[i] = v;\n\t\t\t\t}\n\n\t\t\t\tbufferlen = 0;\t\t//restart\n\t\t\t\tprotocollen = 1000;\n\n\t\t\t\tmulticastpos = 1;\n\n\t\t\t\tNPP_AddData(&svc, sizeof(qbyte));\n\t\t\t\tfor (i = 0; i < 3; i++)\n\t\t\t\t{\n\t\t\t\t\tif (destprim->coordtype==COORDTYPE_FLOAT_32)\n\t\t\t\t\t\tNPP_AddData(&org[i], sizeof(float));\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\ts = org[i]*8;\n\t\t\t\t\t\tNPP_AddData(&s, sizeof(short));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tNPP_AddData(&dir[0], sizeof(char));\n\t\t\t\tNPP_AddData(&dir[1], sizeof(char));\n\t\t\t\tNPP_AddData(&dir[2], sizeof(char));\n\t\t\t\tNPP_AddData(&count, sizeof(qbyte));\n\t\t\t\tNPP_AddData(&colour, sizeof(qbyte));\n\t\t\t}\n\t\t\tbreak;\n\t\tcase TEQW_QWGUNSHOT:\t//needs byte 3 removed\n\t\t\tif (bufferlen >= 3)\n\t\t\t{\n\t\t\t\tmemmove(buffer+2, buffer+3, bufferlen-3);\n\t\t\t\tbufferlen--;\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t}\n\tif (ignoreprotocol)\n\t{\n\t\tignoreprotocol=false;\n\t\tbufferlen = 0;\n\t}\n\n\n\n\n\tif (cldest)\n\t{\n\t\tif (!requireextension || cldest->fteprotocolextensions & requireextension)\n\t\tif (bufferlen && !ISQWCLIENT(cldest))\n\t\t{\n\t\t\tsizebuf_t *msg = ClientReliable_StartWrite(cldest, bufferlen);\n\t\t\tSZ_Write(msg, buffer, bufferlen);\n\t\t\tClientReliable_FinishWrite(cldest);\n\t\t}\n\t\tcldest = NULL;\n\t}\n\telse\n\t{\n\t\tif (multicastpos && (writedest == &sv.nqdatagram || writedest == &sv.nqmulticast))\n\t\t\twritedest = &sv.nqmulticast;\n\t\telse\n\t\t\tmulticastpos = 0;\n\t\tif (bufferlen)\n\t\t\tSZ_Write(writedest, buffer, bufferlen);\n\n\t\tif (multicastpos)\n\t\t{\n\t\t\tint qwsize;\n\t\t\tvec3_t org;\n\t\t\tint coordsize = (destprim->coordtype&0xf);\n\t\t\tcoorddata cd;\n\n\t\t\tmemcpy(&cd, &buffer[multicastpos+coordsize*0], coordsize);\n\t\t\torg[0] = MSG_FromCoord(cd, coordsize);\n\t\t\tmemcpy(&cd, &buffer[multicastpos+coordsize*1], coordsize);\n\t\t\torg[1] = MSG_FromCoord(cd, coordsize);\n\t\t\tmemcpy(&cd, &buffer[multicastpos+coordsize*2], coordsize);\n\t\t\torg[2] = MSG_FromCoord(cd, coordsize);\n\n\t\t\tqwsize = sv.multicast.cursize;\n\t\t\tsv.multicast.cursize = 0;\n\t\t\tSV_MulticastProtExt(org, multicasttype, pr_global_struct->dimension_send, requireextension, 0);\n\t\t\tsv.multicast.cursize = qwsize;\n\t\t}\n\t\twritedest = NULL;\n\t}\n\tbufferlen = 0;\n\tnullterms=0;\n\tprotocollen=0;\n\tmulticastpos=0;\n\trequireextension=0;\n}\nvoid NPP_QWCheckFlush(void)\n{\n\tif (bufferlen >= protocollen && protocollen && !nullterms)\n\t\tNPP_QWFlush();\n}\n\nvoid NPP_QWCheckDest(int dest)\n{\n\tif (dest == MSG_ONE)\n\t{\n\t\tclient_t *cl = Write_GetClient();\n\t\tif (!cl)\n\t\t{\n\t\t\tCon_Printf(\"Not a client\\n\");\n\t\t\treturn;\n\t\t}\n\t\tif (bufferlen && ((cldest && cldest != cl) || writedest))\n\t\t{\n\t\t\tCon_Printf(\"MSG destination changed in the middle of a packet %i.\\n\", (int)*buffer);\n\t\t\tNPP_QWFlush();\n\t\t}\n\t\twritedest = NULL;\n\t\tcldest = cl;\n\t\tdestprim = &cldest->netchan.message.prim;\n\t}\n\telse\n\t{\n\t\tsizebuf_t\t*ndest = NQWriteDest(dest);\n\t\tif (bufferlen && (cldest || (writedest && writedest != ndest)))\n\t\t{\n\t\t\tCon_DPrintf(\"QWCheckDest: MSG destination changed in the middle of a packet %i.\\n\", (int)*buffer);\n\t\t\tNPP_QWFlush();\n\t\t}\n\t\tcldest = NULL;\n\t\twritedest = ndest;\n\t\tdestprim = &writedest->prim;\n\t}\n}\n\n\n\n\nvoid NPP_QWWriteByte(int dest, qbyte data)\t//replacement write func (nq to qw)\n{\n\tNPP_QWCheckDest(dest);\n\n#ifdef NQPROT\n\tif (dest == MSG_ONE)\n\t{\n\t\tclient_t *cl = Write_GetClient();\n\t\tif (!cl)\n\t\t{\n\t\t\tCon_Printf(\"msg_entity: not a client\\n\");\n\t\t\treturn;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (cl->protocol == SCP_BAD) // is a bot\n\t\t\t\treturn;\n\t\t\telse if (ISQWCLIENT(cl))\n\t\t\t{\n\t\t\t\tClientReliableCheckBlock(cl, sizeof(qbyte));\n\t\t\t\tClientReliableWrite_Byte(cl, data);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n\telse\n\t\tMSG_WriteByte (QWWriteDest(dest), data);\n#endif\n\tif (!bufferlen)\t//new message section\n\t{\n\t\tswitch(data)\n\t\t{\n\t\tcase svc_temp_entity:\n\t\t\tbreak;\n\t\tcase svc_setangle:\n\t\t\tprotocollen = sizeof(qbyte) + destprim->anglesize*3;\n\t\t\tbreak;\n\t\tcase svc_setview:\n\t\t\tprotocollen = sizeof(qbyte)*1 + sizeof(short);\n\t\t\tbreak;\n\t\tcase svc_cdtrack:\n\t\t\tprotocollen = sizeof(qbyte)*2;\n\t\t\tbreak;\n\t\tcase svc_killedmonster:\n\t\t\tprotocollen = 1;\n\t\t\tbreak;\n\t\tcase svc_foundsecret:\n\t\t\tprotocollen = 1;\n\t\t\tbreak;\n\t\tcase svc_intermission:\n\t\t\tprotocollen = 1 + (destprim->coordtype&0xf)*3 + destprim->anglesize*3;\n\t\t\tbreak;\n\t\tcase svc_finale:\n\t\t\tnullterms = 1;\n\t\t\tprotocollen = 2;\n\t\t\tbreak;\n\t\tcase svc_updatepl:\n\t\tcase svc_muzzleflash:\n\t\t\tprotocollen = 3;\n\t\t\tbreak;\n\t\tcase svc_smallkick:\n\t\tcase svc_bigkick:\n\t\t\tprotocollen = 1;\n\t\t\tbreak;\n\t\tcase svc_print:\n\t\t\tprotocollen = 2;\n\t\t\tnullterms=1;\n\t\t\tbreak;\n\t\tcase svc_setinfo:\n\t\t\tprotocollen = 2;\n\t\t\tnullterms = 2;\n\t\t\tbreak;\n\t\tcase svc_centerprint:\n\t\tcase svc_stufftext:\n\t\t\tprotocollen = 1;\n\t\t\tnullterms=1;\n\t\t\tbreak;\n\t\tcase svcqw_updatestatbyte:\n\t\t\tprotocollen = 3;\n\t\t\tbreak;\n\t\tcase svc_updateping:\n\t\tcase svc_updatefrags:\n\t\t\tprotocollen = 4;\n\t\t\tbreak;\n\t\tcase svc_updateentertime:\n\t\t\tprotocollen = 6;\n\t\t\tbreak;\n\t\tcase svc_updateuserinfo:\n\t\t\tnullterms = 1;\n\t\t\tprotocollen = 6+nullterms;\n\t\t\tbreak;\n\t\tcase svcqw_updatestatlong:\n\t\t\tprotocollen = 6;\n\t\t\tbreak;\n\t\tcase svc_setpause:\n\t\t\tprotocollen = 2;\n\t\t\tbreak;\n\t\tcase svcfte_cgamepacket:\n\t\t\tprotocollen = sizeof(buffer);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tCon_DPrintf(\"QWWriteByte: bad protocol %i\\n\", (int)data);\n\t\t\tprotocollen = sizeof(buffer);\n\t\t\tbreak;\n\t\t}\n\t\tmajortype = data;\n\t}\n\tif (bufferlen == 1 && !protocollen)\t//some of them depend on the following bytes for size.\n\t{\n\t\tswitch(majortype)\n\t\t{\n\t\tcase svc_temp_entity:\n\t\t\tminortype = data;\n\t\t\tswitch(data)\n\t\t\t{\n\t\t\tcase TE_LIGHTNING1:\n\t\t\tcase TE_LIGHTNING2:\n\t\t\tcase TE_LIGHTNING3:\n\t\t\t\tmulticastpos=4;\n\t\t\t\tmulticasttype=MULTICAST_PHS;\n\t\t\t\tprotocollen = (destprim->coordtype&0xf)*6+sizeof(short)+sizeof(qbyte)*2;\n\t\t\t\tbreak;\n\t\t\tcase TEQW_QWBLOOD:\t\t//needs to be converted to a particle\n\t\t\tcase TEQW_QWGUNSHOT:\t//needs qbyte 2 removed\n\t\t\t\tmulticastpos=3;\n\t\t\t\tmulticasttype=MULTICAST_PVS;\n\t\t\t\tprotocollen = (destprim->coordtype&0xf)*3+sizeof(qbyte)*3;\n\t\t\t\tbreak;\n\t\t\tcase TEQW_LIGHTNINGBLOOD:\n\t\t\tcase TEQW_QWEXPLOSION:\n\t\t\tcase TE_SPIKE:\n\t\t\tcase TE_SUPERSPIKE:\n\t\t\t\tmulticastpos=2;\n\t\t\t\tmulticasttype=MULTICAST_PHS_R;\n\t\t\t\tprotocollen = (destprim->coordtype&0xf)*3+sizeof(qbyte)*2;\n\t\t\t\tbreak;\n\t\t\tcase TE_TAREXPLOSION:\n\t\t\tcase TE_WIZSPIKE:\n\t\t\tcase TE_KNIGHTSPIKE:\n\t\t\tcase TE_LAVASPLASH:\n\t\t\tcase TE_TELEPORT:\n\t\t\t\tmulticastpos=2;\n\t\t\t\tmulticasttype=MULTICAST_PVS;\n\t\t\t\tprotocollen = (destprim->coordtype&0xf)*3+sizeof(qbyte)*2;\n\t\t\t\tbreak;\n\t\t\tcase TEQW_RAILTRAIL:\n\t\t\t\tmulticastpos=1;\n\t\t\t\tmulticasttype=MULTICAST_PVS;\n\t\t\t\tprotocollen = (destprim->coordtype&0xf)*3+sizeof(qbyte)*1;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tprotocollen = sizeof(buffer);\n\t\t\t\tif (writedest != &sv.nqmulticast)\n\t\t\t\t\tCon_Printf(\"QWWriteByte: bad tempentity - %i\\n\", data);\n\t\t\t\tte_515sevilhackworkaround = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tCon_Printf(\"QWWriteByte: Non-Implemented svc\\n\");\n\t\t\tprotocollen = sizeof(buffer);\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tNPP_AddData(&data, sizeof(qbyte));\n\tif (!data && bufferlen>=protocollen)\n\t\tif (nullterms)\n\t\t\tnullterms--;\n\tNPP_QWCheckFlush();\n}\n\nvoid NPP_QWWriteChar(int dest, char data)\t//replacement write func (nq to qw)\n{\n\tNPP_QWWriteByte(dest, (qbyte)data);\n}\n\nvoid NPP_QWWriteShort(int dest, short data)\t//replacement write func (nq to qw)\n{\n\tunion {\n\t\tqbyte b[2];\n\t\tshort s;\n\t} u;\n\tif (bufferlen == 2 && majortype == svc_temp_entity && (minortype == TE_LIGHTNING1 || minortype == TE_LIGHTNING2 || minortype == TE_LIGHTNING3))\n\t\tNPP_QWWriteEntity(dest, data);\n\telse\n\t{\n\t\tu.s = LittleShort(data);\n\t\tNPP_QWWriteByte(dest, u.b[0]);\n\t\tNPP_QWWriteByte(dest, u.b[1]);\n\t}\n}\n\nvoid NPP_QWWriteFloat(int dest, float data)\t//replacement write func (nq to qw)\n{\n\tunion {\n\t\tqbyte b[4];\n\t\tfloat f;\n\t} u;\n\tu.f = LittleFloat(data);\n\tNPP_QWWriteByte(dest, u.b[0]);\n\tNPP_QWWriteByte(dest, u.b[1]);\n\tNPP_QWWriteByte(dest, u.b[2]);\n\tNPP_QWWriteByte(dest, u.b[3]);\n}\n\nvoid NPP_QWWriteLong(int dest, long data)\t//replacement write func (nq to qw)\n{\n\tunion {\n\t\tqbyte b[4];\n\t\tint l;\n\t} u;\n\tu.l = LittleLong(data);\n\tNPP_QWWriteByte(dest, u.b[0]);\n\tNPP_QWWriteByte(dest, u.b[1]);\n\tNPP_QWWriteByte(dest, u.b[2]);\n\tNPP_QWWriteByte(dest, u.b[3]);\n}\nvoid NPP_QWWriteAngle(int dest, float in)\t//replacement write func (nq to qw)\n{\n\tif (destprim->anglesize==1)\n\t{\n\t\tchar data = (int)(in*256/360) & 255;\n\t\tNPP_QWWriteChar(dest, data);\n\t}\n\telse\n\t{\n\t\tshort data = (int)(in*0xffff/360) & 0xffff;\n\t\tNPP_QWWriteShort(dest, data);\n\t}\n}\nvoid NPP_QWWriteCoord(int dest, float in)\t//replacement write func (nq to qw)\n{\n\tif (destprim->coordtype==COORDTYPE_FLOAT_32)\n\t{\n\t\tNPP_QWWriteFloat(dest, in);\n\t}\n\telse\n\t{\n\t\tshort datas = (int)(in*8);\n\t\tNPP_QWWriteShort(dest, datas);\n\t}\n}\nvoid NPP_QWWriteString(int dest, const char *data)\t//replacement write func (nq to qw)\n{\n#if 0\n\t//the slow but guarenteed routine\n\twhile(*data)\n\t\tNPP_QWWriteByte(dest, *data++);\n\tNPP_QWWriteByte(dest, 0);\t//and the null terminator\n#else\n\t//the fast-track, less reliable routine\n\n\n\tNPP_QWCheckDest(dest);\n\n#ifdef NQPROT\n\tif (dest == MSG_ONE)\n\t{\n\t\tclient_t *cl = Write_GetClient();\n\t\tif (!cl)\n\t\t{\n\t\t\tCon_Printf(\"msg_entity: not a client\\n\");\n\t\t\treturn;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (cl->protocol == SCP_BAD)\n\t\t\t\treturn;\n\t\t\telse if (ISQWCLIENT(cl))\n\t\t\t{\n\t\t\t\tClientReliableCheckBlock(cl, strlen(data)+1);\n\t\t\t\tClientReliableWrite_String(cl, data);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n\telse\n\t\tMSG_WriteString (QWWriteDest(dest), data);\n#endif\n\n\tif (!bufferlen)\n\t{\n\t\tCon_Printf(\"QWWriteString: Messages should start with WriteByte (last was %i)\\n\", majortype);\n\t\tPR_StackTrace(svprogfuncs, false);\n\t}\n\n\tNPP_AddData(data, strlen(data)+1);\n\tif (nullterms)\n\t\tnullterms--;\n\tNPP_QWCheckFlush();\n#endif\n}\nvoid NPP_QWWriteEntity(int dest, int data)\t//replacement write func (nq to qw)\n{\n\tif (data >= 0x8000)\n\t{\n\t\tNPP_QWWriteByte(dest, ((data>> 0) & 0xff));\n\t\tNPP_QWWriteByte(dest, ((data>> 8) & 0x7f) | 0x80);\n\t\tNPP_QWWriteByte(dest, ((data>>15) & 0xff));\n\t}\n\telse\n\t{\n\t\tNPP_QWWriteByte(dest, (data>>0) & 0xff);\n\t\tNPP_QWWriteByte(dest, (data>>8) & 0x7f);\n\t}\n}\n#endif\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n#ifdef SERVER_DEMO_PLAYBACK\n\n\n\n\n\n#define DF_ORIGIN\t1\n#define DF_ANGLES\t(1<<3)\n#define DF_EFFECTS\t(1<<6)\n#define DF_SKINNUM\t(1<<7)\n#define DF_DEAD\t\t(1<<8)\n#define DF_GIB\t\t(1<<9)\n#define DF_WEAPONFRAME (1<<10)\n#define DF_MODEL\t(1<<11)\n\n#define\tPF_MSEC\t\t\t(1<<0)\n#define\tPF_COMMAND\t\t(1<<1)\n#define\tPF_VELOCITY1\t(1<<2)\n#define\tPF_VELOCITY2\t(1<<3)\n#define\tPF_VELOCITY3\t(1<<4)\n#define\tPF_MODEL\t\t(1<<5)\n#define\tPF_SKINNUM\t\t(1<<6)\n#define\tPF_EFFECTS\t\t(1<<7)\n#define\tPF_WEAPONFRAME\t(1<<8)\t\t// only sent for view player\n#define\tPF_DEAD\t\t\t(1<<9)\t\t// don't block movement any more\n#define\tPF_GIB\t\t\t(1<<10)\t\t// offset the view height differently\n\n\n\n\nint sv_demo_spikeindex;\n\n\n\n\nvoid NPP_MVDFlush(void)\n{\n\tif (!bufferlen)\n\t\treturn;\n\n\tswitch(majortype)\n\t{\n\tcase svc_spawnbaseline:\n\t\tSV_FlushDemoSignon();\n\t\tcldest = NULL;\n\t\twritedest = &sv.demosignon;\n\n\t\tif (1)\n\t\t{\n\t\t\tint entnum, i;\n\t\t\tmvdentity_state_t *ent;\n\n\t\t\tif (!sv.demobaselines)\n\t\t\t{\n\t\t\t\tsv.demobaselines = (mvdentity_state_t*)BZ_Malloc(sizeof(mvdentity_state_t)*MAX_EDICTS);\n\t\t\t\tsv.demostatevalid = true;\n\t\t\t}\n\t\t\tentnum = buffer[1] + (buffer[2]<<8);\n//\t\t\tif (entnum < svs.allocated_client_slots)\n//\t\t\t\tbreak;\n\t\t\tent = &sv.demobaselines[entnum];\n\n\t\t\tent->modelindex = buffer[3];\n\t\t\tent->frame = buffer[4];\n\t\t\tent->colormap = buffer[5];\n\t\t\tent->skinnum = buffer[6];\n\n\t\t\tfor (i=0 ; i<3 ; i++)\n\t\t\t{\n\t\t\t\tent->origin[i] = (short)(buffer[7+i*3] + (buffer[8+i*3]<<8))/8.0f;\n\t\t\t\tent->angles[i] = buffer[9+i*3];\n\t\t\t}\n\t\t}\n\t\tbreak;\n\tcase svc_spawnstatic:\n\t\tSV_FlushDemoSignon();\n\t\tcldest = NULL;\n\t\twritedest = &sv.demosignon;\n\t\tbreak;\n\tcase svc_modellist:\n\t\tignoreprotocol=true;\n\t\t{\n\t\t\tint i;\n\t\t\tint rpos, s;\n\t\t\ti = buffer[1];\n\t\t\trpos = 2;\n\t\t\twhile (1)\n\t\t\t{\n\t\t\t\ti++;\n\t\t\t\ts = rpos;\n\t\t\t\twhile(buffer[rpos])\n\t\t\t\t\trpos++;\n\n\t\t\t\tif (rpos == s)\t//end\n\t\t\t\t\tbreak;\n\n\t\t\t\tstrcpy(sv.demmodel_precache[i], buffer+s);\n\t\t\t\tif (!strcmp(sv.demmodel_precache[i], \"progs/spike.mdl\"))\n\t\t\t\t\tsv_demo_spikeindex = i;\n\t\t\t\trpos++;\n\t\t\t}\n\t\t}\n\t\tbreak;\n\tcase svc_soundlist:\n\t\tignoreprotocol=true;\n\t\t{\n\t\t\tint i;\n\t\t\tint rpos, s;\n\t\t\ti = buffer[1];\n\t\t\trpos = 2;\n\t\t\twhile (1)\n\t\t\t{\n\t\t\t\ti++;\n\t\t\t\ts = rpos;\n\t\t\t\twhile(buffer[rpos])\n\t\t\t\t\trpos++;\n\n\t\t\t\tif (rpos == s)\t//end\n\t\t\t\t\tbreak;\n\n\t\t\t\tstrcpy(sv.demsound_precache[i], buffer+s);\n\t\t\t\trpos++;\n\t\t\t}\n\t\t}\n\t\tbreak;\n\tcase svc_serverdata:\n\t\t{\n\t\t\tint i;\n\t\t\tsv_demo_spikeindex = 0;\t//new map, new precaches.\n\n\t\t\ti = 9;\n\t\t\tstrcpy(sv.demgamedir, buffer+i);\n\t\t\tfor(;i < bufferlen && buffer[i];i++)\n\t\t\t\t;\n\t\t\ti++;\n\t\t\ti+=4;\n\t\t\tQ_strncpyz(sv.demfullmapname, buffer+i, sizeof(sv.demfullmapname));\n\t\t\tfor(;i < bufferlen && buffer[i];i++)\n\t\t\t\t;\n\t\t\ti+=4*10;\n\t\t}\n\t\tignoreprotocol=true;\n\t\tbreak;\n\tcase svc_lightstyle:\n\t\tsv.demolightstyles[buffer[1]] = Hunk_Alloc(strlen(buffer+2)+1);\n\t\tstrcpy(sv.demolightstyles[buffer[1]], buffer+2);\n\t\tbreak;\n\n\tcase svc_updatestat:\n\tcase svc_updatestatlong:\t//make sure we update the running players stats properly.\n\t\t{\n\t\t\tint v, s;\n\t\t\tif (majortype == svc_updatestat)\n\t\t\t\tv = buffer[2];\n\t\t\telse\n\t\t\t\tv = buffer[2] | (buffer[3]<<8) | (buffer[4]<<16) | (buffer[5]<<24);\n\t\t\ts = buffer[1];\n\n\t\t\tif (sv.lastto < 32)\t//dem_multicast could be used at the wrong time...\n\t\t\t\tsv.recordedplayer[sv.lastto].stats[s] = v;\n\n\t\t\tignoreprotocol=true;\n\t\t}\n\t\tbreak;\n\tcase svc_packetentities:\n\tcase svc_deltapacketentities:\t//read the delta in to the array.\n\t\tignoreprotocol=true;\t\t//a bug exists in that the delta MUST have been reliably recorded.\n\t\t{\n\t\t\tint i;\n\t\t\tint entnum;\n\t\t\tmvdentity_state_t *ents;\n\t\t\tunsigned short s;\n\t\t\tif (!sv.demostate)\n\t\t\t{\n\t\t\t\tsv.demostate = BZ_Malloc(sizeof(mvdentity_state_t)*MAX_EDICTS);\n\t\t\t\tsv.demostatevalid = true;\n\t\t\t}\n\t\t\ti = majortype-svc_packetentities+1;\n\t\t\twhile (1)\n\t\t\t{\n\t\t\t\ts = buffer[i] + buffer[i+1]*256;\n\t\t\t\ti+=2;\n\t\t\t\tif (!s)\n\t\t\t\t{\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tentnum = s&511;\n\t\t\t\t\ts &= ~511;\n\n\t\t\t\t\tif (entnum > sv.demomaxents)\n\t\t\t\t\t\tsv.demomaxents = entnum;\n\n\t\t\t\t\tents = &sv.demostate[entnum];\n\t\t\t\t\tif (s & U_REMOVE)\n\t\t\t\t\t{\t//this entity went from the last packet\n\t\t\t\t\t\tents->modelindex = 0;\n\t\t\t\t\t\tents->effects = 0;\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (!ents->modelindex && !ents->effects && sv.demobaselines)\n\t\t\t\t\t{\t//new entity, reset to baseline\n\t\t\t\t\t\tmemcpy(ents, &sv.demobaselines[entnum], sizeof(mvdentity_state_t));\n\t\t\t\t\t}\n\n\t\t\t\t\tif (s & U_MOREBITS)\n\t\t\t\t\t{\n\t\t\t\t\t\ts |= buffer[i];\n\t\t\t\t\t\ti++;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (s & U_MODEL)\n\t\t\t\t\t{\n\t\t\t\t\t\tents->modelindex = buffer[i];\n\t\t\t\t\t\ti++;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (s & U_FRAME)\n\t\t\t\t\t{\n\t\t\t\t\t\tents->frame = buffer[i];\n\t\t\t\t\t\ti++;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (s & U_COLORMAP)\n\t\t\t\t\t{\n\t\t\t\t\t\tents->colormap = buffer[i];\n\t\t\t\t\t\ti++;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (s & U_SKIN)\n\t\t\t\t\t{\n\t\t\t\t\t\tents->skinnum = buffer[i];\n\t\t\t\t\t\ti++;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (s & U_EFFECTS)\n\t\t\t\t\t{\n\t\t\t\t\t\tents->effects = buffer[i];\n\t\t\t\t\t\ti++;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (s & U_ORIGIN1)\n\t\t\t\t\t{\n\t\t\t\t\t\tents->origin[0] = (short)(buffer[i]+buffer[i+1]*256) /8.0f;\n\t\t\t\t\t\ti+=2;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (s & U_ANGLE1)\n\t\t\t\t\t{\n\t\t\t\t\t\tents->angles[0] = (unsigned char)(buffer[i]);//\t* (360.0/256);\n\t\t\t\t\t\ti++;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (s & U_ORIGIN2)\n\t\t\t\t\t{\n\t\t\t\t\t\tents->origin[1] = (short)(buffer[i]+buffer[i+1]*256) /8.0f;\n\t\t\t\t\t\ti+=2;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (s & U_ANGLE2)\n\t\t\t\t\t{\n\t\t\t\t\t\tents->angles[1] = (unsigned char)(buffer[i]);//\t* (360.0/256);\n\t\t\t\t\t\ti++;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (s & U_ORIGIN3)\n\t\t\t\t\t{\n\t\t\t\t\t\tents->origin[2] = (short)(buffer[i]+buffer[i+1]*256) /8.0f;\n\t\t\t\t\t\ti+=2;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (s & U_ANGLE3)\n\t\t\t\t\t{\n\t\t\t\t\t\tents->angles[2] = (unsigned char)(buffer[i]);//\t* (360.0/256);\n\t\t\t\t\t\ti++;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tbreak;\n\tcase svc_playerinfo:\n\t\tignoreprotocol=true;\n\t\t{\n\t\t\tint i, j;\n\t\t\tunsigned short flags;\n\t\t\tmvdentity_state_t *ents;\n\t\t\tint playernum;\n\t\t\tvec3_t oldang;\n\n\t\t\tif (!sv.demostate)\n\t\t\t\tsv.demostate = BZ_Malloc(sizeof(entity_state_t)*MAX_EDICTS);\n\n\t\t\tsv.demostatevalid = true;\n\n\t\t\tflags = buffer[2] + buffer[3]*256;\n\n\t\t\tplayernum = buffer[1];\n\t\t\tents = &sv.demostate[playernum+1];\n\t\t\tents->frame = buffer[4];\n\n//\t\t\tents->colormap=playernum+1;\n\n\t\t\tVectorCopy(ents->origin, sv.recordedplayer[playernum].oldorg);\n\t\t\tVectorCopy(ents->angles, sv.recordedplayer[playernum].oldang);\n\n\t\t\ti = 5;\n\t\t\tfor (j=0 ; j<3 ; j++)\n\t\t\t\tif (flags & (DF_ORIGIN << j))\n\t\t\t\t{\n\t\t\t\t\tents->origin[j] = (signed short)(buffer[i] + (buffer[i+1]<<8))/8.0f;\n\t\t\t\t\ti+=2;\n\t\t\t\t}\n\n\t\t\tVectorCopy(ents->angles, oldang);\n\t\t\tfor (j=0 ; j<3 ; j++)\n\t\t\t\tif (flags & (DF_ANGLES << j))\n\t\t\t\t{\n\t\t\t\t\t//FIXME: angle truncation here.\n\t\t\t\t\tents->angles[j] = (char)((int)(buffer[i] + (buffer[i+1]<<8))/256.0f);\n\t\t\t\t\ti+=2;\n\t\t\t\t}\n\n\t\t\tif (flags & (DF_ANGLES << 0))\t//'stupid quake bug' I believe is the correct quote...\n\t\t\t\tents->angles[0] = ents->angles[0]*-1/3.0f; //also scale pitch down as well as invert\n\n\t\t\tif (flags & DF_MODEL)\n\t\t\t{\n\t\t\t\tents->modelindex = buffer[i];\n\t\t\t\ti+=1;\n\t\t\t}\n\t\t\tif (flags & DF_SKINNUM)\n\t\t\t{\n\t\t\t\tents->skinnum = buffer[i];\n\t\t\t\ti+=1;\n\t\t\t}\n\t\t\tif (flags & DF_EFFECTS)\n\t\t\t{\n\t\t\t\tents->effects = buffer[i];\n\t\t\t\ti+=1;\n\t\t\t}\n\t\t\tif (flags & DF_WEAPONFRAME)\n\t\t\t{\t//mvds are deltas remember, this is really the only place where that fact is all that important.\n\t\t\t\tsv.recordedplayer[playernum].weaponframe = buffer[i];\n\t\t\t\ti+=1;\n\t\t\t}\n\n\t\t\tsv.recordedplayer[playernum].updatetime = realtime;\n\n\t\t\tignoreprotocol=true;\n\t\t}\n\t\tbreak;\n\n\tcase svc_nails:\n\tcase svc_nails2:\n\t\tsv.numdemospikes = buffer[1];\n\t\t{\n\t\t\tqboolean hasid = (majortype==svc_nails2);\n\t\t\tchar *bits;\n\t\t\tint i;\n\t\t\tbits = buffer+2;\n\t\t\tfor (i = 0; i < sv.numdemospikes; i++)\n\t\t\t{\n\t\t\t\tif (hasid)\n\t\t\t\t{\n\t\t\t\t\tsv.demospikes[i].id = *bits;\n\t\t\t\t\tbits++;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tsv.demospikes[i].id = 0;\n\t\t\t\tsv.demospikes[i].modelindex = sv_demo_spikeindex;\n\t\t\t\tsv.demospikes[i].org[0] = ( ( bits[0] + ((bits[1]&15)<<8) ) <<1) - 4096;\n\t\t\t\tsv.demospikes[i].org[1] = ( ( (bits[1]>>4) + (bits[2]<<4) ) <<1) - 4096;\n\t\t\t\tsv.demospikes[i].org[2] = ( ( bits[3] + ((bits[4]&15)<<8) ) <<1) - 4096;\n\t\t\t\tsv.demospikes[i].pitch = (bits[4]>>4);\n\t\t\t\tsv.demospikes[i].yaw = bits[5];\n\n\t\t\t\tbits+=6;\n\t\t\t}\n\t\t}\n\t\tignoreprotocol=true;\n\t\tbreak;\n\n\tcase svc_stufftext:\n\t\tignoreprotocol = true;\n\t\tCmd_TokenizeString(buffer+1, false, false);\n\t\tif (!stricmp(Cmd_Argv(0), \"fullserverinfo\"))\n\t\t{\n\t\t\tQ_strncpyz(sv.demoinfo, Cmd_Argv(1), sizeof(sv.demoinfo));\n\t\t\tbreak;\n\t\t}\n\t\tbreak;\n\tcase svc_updateping:\n\t\t{\n\t\t\tint j;\n\t\t\tj = buffer[1];\n\t\t\tsv.recordedplayer[j].ping = buffer[2] | (buffer[3]<<8);\n\t\t}\n\t\tbreak;\n\tcase svc_updatepl:\n\t\t{\n\t\t\tint j;\n\t\t\tj = buffer[1];\n\t\t\tsv.recordedplayer[j].pl = buffer[2] | (buffer[3]<<8);\n\t\t}\n\t\tbreak;\n\tcase svc_updatefrags:\n\t\t{\n\t\t\tint j;\n\t\t\tj = buffer[1];\n\t\t\tsv.recordedplayer[j].frags = buffer[2] | (buffer[3]<<8);\n\t\t}\n\t\tbreak;\n\tcase svc_setinfo:\n//\t\tignoreprotocol = true;\n\t\t{\n\t\t\tint j;\n\t\t\tj = buffer[1];\n\t\t\tInfo_SetValueForStarKey(sv.recordedplayer[j].userinfo, buffer+2, buffer+2+strlen(buffer+2)+1, sizeof(sv.recordedplayer[j].userinfo));\n\t\t}\n\t\tbreak;\n\tcase svc_updateuserinfo:\n//\t\tignoreprotocol = true;\n\t\t{\n\t\t\tunsigned int j;\n\t\t\tj = buffer[1];\n\t\t\tif (j < sv.allocated_client_slots)\n\t\t\t{\n\t\t\t\tsv.recordedplayer[j].userid = buffer[2] | (buffer[3]<<8) | (buffer[4]<<16) | (buffer[5]<<24);\n\t\t\t\tQ_strncpyz(sv.recordedplayer[j].userinfo, buffer+6, sizeof(sv.recordedplayer[j].userinfo));\n\t\t\t}\n\t\t}\n\t\tbreak;\n\tcase svc_setangle:\t//FIXME: forward on to trackers.\n\t\tignoreprotocol = true;\n\t\tbreak;\n\n\tcase svc_disconnect:\n\t\tsvs.spawncount++;\n\t\tSV_BroadcastCommand(\"changing\\n\");\n\t\tSV_BroadcastCommand (\"reconnect\\n\");\n\t\tignoreprotocol = true;\n\t\tbreak;\n\t}\n\tif (ignoreprotocol)\n\t{\n\t\tignoreprotocol=false;\n\t\tbufferlen = 0;\n\t}\n\n\n\n\n\tif (cldest)\n\t{\n\t\tif (!requireextension || cldest->fteprotocolextensions & requireextension)\n\t\tif (bufferlen)\n\t\t{\n\t\t\tClientReliableCheckBlock(cldest, bufferlen);\n\t\t\tClientReliableWrite_SZ(cldest, buffer, bufferlen);\n\t\t}\n\t\tcldest = NULL;\n\t}\n\telse\n\t{\n\t\tif (multicastpos && (writedest == &sv.datagram || writedest == &sv.multicast))\n\t\t\twritedest = &sv.multicast;\n\t\telse\n\t\t\tmulticastpos = 0;\n\n\t\tif (writedest == &sv.reliable_datagram)\n\t\t{\n\t\t\twritedest = &sv.multicast;\n\t\t\tmulticasttype = MULTICAST_ALL_R;\n\t\t}\n\n\t\tif (bufferlen)\n\t\t\tSZ_Write(writedest, buffer, bufferlen);\n\n\t\tif (multicastpos)\n\t\t{\n\t\t\tvec3_t org;\n\t\t\tcoorddata cd;\n\n\t\t\tmemcpy(&cd, &buffer[multicastpos+sizeofcoord*0], sizeofcoord);\n\t\t\torg[0] = MSG_FromCoord(cd, sizeofcoord);\n\t\t\tmemcpy(&cd, &buffer[multicastpos+sizeofcoord*1], sizeofcoord);\n\t\t\torg[1] = MSG_FromCoord(cd, sizeofcoord);\n\t\t\tmemcpy(&cd, &buffer[multicastpos+sizeofcoord*2], sizeofcoord);\n\t\t\torg[2] = MSG_FromCoord(cd, sizeofcoord);\n\n\t\t\tSV_MulticastProtExt(org, multicasttype, FULLDIMENSIONMASK, requireextension, 0);\n\t\t}\n\t\telse if (writedest == &sv.multicast)\n\t\t\tSV_MulticastProtExt(vec3_origin, multicasttype, FULLDIMENSIONMASK, requireextension, 0);\n\t\twritedest = NULL;\n\t}\n\tbufferlen = 0;\n\tprotocollen=0;\n\tmulticastpos=0;\n\trequireextension=0;\n}\nvoid NPP_MVDForceFlush(void)\n{\n\tif (bufferlen)\n\t{\n\t\tCon_Printf(\"Forcing flush mvd->qw prot\\n\");\n\t\tCon_Printf(\"(last was %i)\\n\", (int)majortype);\n\t\tNPP_MVDFlush();\n\t}\n}\nvoid NPP_MVDCheckFlush(void)\n{\n\tif (bufferlen >= protocollen && protocollen)\n\t\tNPP_MVDFlush();\n}\n\nvoid NPP_MVDCheckDest(client_t *cl, int broadcast)\n{\n\tif (!broadcast)\n\t{\n\t\tif (!cl)\n\t\t{\n\t\t\tCon_Printf(\"Not a client\\n\");\n\t\t\treturn;\n\t\t}\n\t\tif ((cldest && cldest != cl) || writedest)\n\t\t{\n\t\t\tCon_Printf(\"MSG destination changed in the middle of a packet.\\n\");\n\t\t\tNPP_MVDFlush();\n\t\t}\n\t\tcldest = cl;\n\t}\n\telse\n\t{\n\t\tsizebuf_t\t*ndest = &sv.reliable_datagram;\n\t\tif (cldest || (writedest && writedest != ndest))\n\t\t{\n\t\t\tCon_Printf(\"MSG destination changed in the middle of a packet.\\n\");\n\t\t\tNPP_MVDFlush();\n\t\t}\n\t\twritedest = ndest;\n\t}\n}\n\nvoid NPP_MVDWriteByte(qbyte data, client_t *to, int broadcast)\t//replacement write func (nq to qw)\n{\n\tint i;\n\tNPP_MVDCheckDest(to, broadcast);\n\n\tif (!bufferlen)\t//new message section\n\t{\n\t\tswitch(data)\n\t\t{\n\t\tcase svc_temp_entity://depends on following bytes\n\t\t\tbreak;\n\t\tcase svc_serverinfo:\n\t\tcase svc_print:\n\t\tcase svc_sound:\n\t\tcase svc_serverdata:\n\t\tcase svc_stufftext:\n\t\tcase svc_modellist:\n\t\tcase svc_soundlist:\n\t\tcase svc_updateuserinfo:\n\t\tcase svc_playerinfo:\n\t\tcase svc_packetentities:\n\t\tcase svc_deltapacketentities:\n\t\tcase svc_lightstyle:\n\t\tcase svc_nails:\n\t\tcase svc_nails2:\n\t\tcase svc_centerprint:\n\t\tcase svc_setinfo:\n\t\t\tbreak;\n\t\tcase svc_setangle:\n\t\t\tif (sv.mvdplayback)\n\t\t\t\tprotocollen = sizeof(qbyte)*5;\t//MVDSV writes an extra client num too.\n\t\t\telse\n\t\t\t\tprotocollen = sizeof(qbyte)*4;\t//MVDSV writes an extra client num too.\n\t\t\tbreak;\n\t\tcase svc_setview:\n\t\t\tprotocollen = sizeof(qbyte)*1 + sizeof(short);\n\t\t\tbreak;\n\t\tcase svc_cdtrack:\n\t\t\tprotocollen = sizeof(qbyte)*2;\n\t\t\tbreak;\n\t\tcase svc_killedmonster:\n\t\t\tprotocollen = 1;\n\t\t\tbreak;\n\t\tcase svc_foundsecret:\n\t\t\tprotocollen = 1;\n\t\t\tbreak;\n//\t\tcase svc_intermission:\n//\t\t\tprotocollen = 1;\n//\t\t\tbreak;\n//\t\tcase svc_finale:\n//\t\t\tprotocollen = 2;\n//\t\t\tbreak;\n\t\tcase svc_muzzleflash:\n\t\t\tprotocollen = 3;\n\t\t\tbreak;\n\t\tcase svc_smallkick:\n\t\tcase svc_bigkick:\n\t\t\tprotocollen = 1;\n\t\t\tbreak;\n\n\t\tcase svc_spawnstaticsound:\n\t\t\tprotocollen = 10;\n\t\t\tbreak;\n\t\tcase svc_spawnstatic:\n\t\t\tprotocollen = 14;\n\t\t\tbreak;\n\t\tcase svc_spawnbaseline:\n\t\t\tprotocollen = 16;\n\t\t\tbreak;\n\t\tcase svc_updateping:\n\t\tcase svc_updatefrags:\n\t\t\tprotocollen = 4;\n\t\t\tbreak;\n\t\tcase svc_updatestat:\n\t\tcase svc_updatepl:\n\t\t\tprotocollen = 3;\n\t\t\tbreak;\n\t\tcase svc_updatestatlong:\n\t\t\tprotocollen = 6;\n\t\t\tbreak;\n\t\tcase svc_updateentertime:\n\t\t\tprotocollen = 6;\n\t\t\tbreak;\n\t\tcase svc_intermission:\n\t\t\tprotocollen = 10;\n\t\t\tbreak;\n\t\tcase svc_disconnect:\n\t\t\tprotocollen = 2+strlen(\"EndOfDemo\");\n\t\t\tbreak;\n\t\tcase svc_chokecount:\n\t\t\tprotocollen = 2;\n\t\t\tbreak;\n\t\tcase svc_damage:\n\t\t\tprotocollen = 9;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tCon_Printf(\"mvd: bad protocol %i\\n\", (int)data);\n\t\t\tCon_Printf(\"(last was %i)\\n\", (int)majortype);\n\t\t\tprotocollen = sizeof(buffer);\n\t\t\tnet_message.cursize=0;\n\t\t\tdata = svc_nop;\n\t\t\tprotocollen = 1;\n\t\t\tbreak;\n\t\t}\n\t\tmajortype = data;\n\t}\n\telse if (!protocollen)\n\t{\n\t\tswitch(majortype)\n\t\t{\n\t\tcase svc_temp_entity:\n\t\t\tif (bufferlen == 1)\n\t\t\t{\n\t\t\t\tminortype = data;\n\t\t\t\tswitch(data)\n\t\t\t\t{\n\t\t\t\tcase TE_LIGHTNING1:\n\t\t\t\tcase TE_LIGHTNING2:\n\t\t\t\tcase TE_LIGHTNING3:\n\t\t\t\t\tmulticastpos=4;\n\t\t\t\t\tmulticasttype=MULTICAST_PHS;\n\t\t\t\t\tprotocollen = sizeof(short)*6+sizeof(short)+sizeof(qbyte)*2;\n\t\t\t\t\tbreak;\n\t\t\t\tcase TE_BLOOD:\t\t//needs to be converted to a particle\n\t\t\t\tcase TE_GUNSHOT:\t//needs qbyte 2 removed\n\t\t\t\t\tmulticastpos=3;\n\t\t\t\t\tmulticasttype=MULTICAST_PVS;\n\t\t\t\t\tprotocollen = sizeof(short)*3+sizeof(qbyte)*3;\n\t\t\t\t\tbreak;\n\t\t\t\tcase TE_LIGHTNINGBLOOD:\n\t\t\t\tcase TE_EXPLOSION:\n\t\t\t\tcase TE_SPIKE:\n\t\t\t\tcase TE_SUPERSPIKE:\n\t\t\t\t\tmulticastpos=2;\n\t\t\t\t\tmulticasttype=MULTICAST_PHS_R;\n\t\t\t\t\tprotocollen = sizeof(short)*3+sizeof(qbyte)*2;\n\t\t\t\t\tbreak;\n\t\t\t\tcase TE_TAREXPLOSION:\n\t\t\t\tcase TE_WIZSPIKE:\n\t\t\t\tcase TE_KNIGHTSPIKE:\n\t\t\t\tcase TE_LAVASPLASH:\n\t\t\t\tcase TE_TELEPORT:\n\t\t\t\t\tmulticastpos=2;\n\t\t\t\t\tmulticasttype=MULTICAST_PVS;\n\t\t\t\t\tprotocollen = sizeof(short)*3+sizeof(qbyte)*2;\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tprotocollen = sizeof(buffer);\n\t\t\t\t\tCon_Printf(\"bad tempentity\\n\");\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\t\tcase svc_serverdata:\n\t\t\tif (bufferlen > 9)\n\t\t\t{\n\t\t\t\ti = 9;\n\t\t\t\tfor(;i < bufferlen && buffer[i];i++)\n\t\t\t\t\t;\n\t\t\t\ti++;\n\t\t\t\ti+=4;\n\t\t\t\tfor(;i < bufferlen && buffer[i];i++)\n\t\t\t\t\t;\n\t\t\t\ti+=4*10;\n\t\t\t\tif (i <= bufferlen)\n\t\t\t\t\tprotocollen = i;\n\t\t\t}\n\n\t\t\tbreak;\n\t\tcase svc_stufftext:\n\t\t\tif (!data)\t//terminated by a null term\n\t\t\t\tprotocollen = bufferlen;\n\t\t\tbreak;\n\n\t\tcase svc_modellist:\n\t\tcase svc_soundlist:\n\t\t\tif (!data)\n\t\t\t\tif (!buffer[bufferlen-1])\t//two null bytes marks the last string\n\t\t\t\t\tprotocollen = bufferlen+2;\n\t\t\tbreak;\n\n\t\tcase svc_updateuserinfo:\t//6 bytes then a string\n\t\t\tif (!data && bufferlen>=6)\n\t\t\t\tprotocollen = bufferlen+1;\n\t\t\tbreak;\n\n\t\tcase svc_playerinfo:\n\t\t\tif (bufferlen==4)\n\t\t\t{\n\t\t\t\tunsigned short pflags;\n\t\t\t\tint j;\n\t\t\t\tignoreprotocol=true;\n\t\t\t\tpflags = buffer[2] + (buffer[3]*256);\t//little endian\n\n\t\t\t\tprotocollen = 4+1;\n\n\t\t\t\tfor (j=0 ; j<3 ; j++)\n\t\t\t\t\tif (pflags & (DF_ORIGIN << j))\n\t\t\t\t\t\tprotocollen += 2;\n\n\t\t\t\tfor (j=0 ; j<3 ; j++)\n\t\t\t\t\tif (pflags & (DF_ANGLES << j))\n\t\t\t\t\t\tprotocollen += 2;\n\n\n\t\t\t\tif (pflags & DF_MODEL)\n\t\t\t\t\tprotocollen += 1;\n\n\t\t\t\tif (pflags & DF_SKINNUM)\n\t\t\t\t\tprotocollen += 1;\n\n\t\t\t\tif (pflags & DF_EFFECTS)\n\t\t\t\t\tprotocollen += 1;\n\n\t\t\t\tif (pflags & DF_WEAPONFRAME)\n\t\t\t\t\tprotocollen += 1;\n\n/*\n\t\t\t\tif (pflags & PF_MSEC)\n\t\t\t\t\tprotocollen+=1;\n\n\t\t\t\tif (pflags & PF_COMMAND)\n\t\t\t\t{\n\t\t\t\t\tCon_Printf(\"svc_playerinfo PF_COMMAND not expected in mvd\\n\");\n\t\t\t\t\tprotocollen+=500;\n\t\t\t\t}\n\n\t\t\t\tfor (i=0 ; i<3 ; i++)\n\t\t\t\t\tif (pflags & (PF_VELOCITY1<<i) )\n\t\t\t\t\t\tprotocollen+=2;\n\n\t\t\t\tif (pflags & PF_MODEL)\n\t\t\t\t\tprotocollen+=1;\n\n\t\t\t\tif (pflags & PF_SKINNUM)\n\t\t\t\t\tprotocollen+=1;\n\n\t\t\t\tif (pflags & PF_EFFECTS)\n\t\t\t\t\tprotocollen+=1;\n\n\t\t\t\tif (pflags & PF_WEAPONFRAME)\n\t\t\t\t\tprotocollen+=1;\n*/\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase svc_packetentities:\n\t\tcase svc_deltapacketentities:\n\t\t\tif (!data)\t//two last bytes are 0.\n\t\t\t{\n\t\t\t\tunsigned short s;\n\t\t\t\ti = majortype-svc_packetentities+1;\n\t\t\t\tbuffer[bufferlen] = data;\n\t\t\t\tbufferlen++;\n\t\t\t\twhile (1)\n\t\t\t\t{\n\t\t\t\t\ts = buffer[i] + buffer[i+1]*256;\n\t\t\t\t\ti+=2;\n\t\t\t\t\tif (i > bufferlen)\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tif (!s)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (i <= bufferlen)\n\t\t\t\t\t\t\tprotocollen = bufferlen;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\ts &= ~511;\n\t\t\t\t\t\tif (s & U_MOREBITS)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ts |= buffer[i];\n\t\t\t\t\t\t\ti++;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (s & U_MODEL)\n\t\t\t\t\t\t\ti++;\n\n\t\t\t\t\t\tif (s & U_FRAME)\n\t\t\t\t\t\t\ti++;\n\n\t\t\t\t\t\tif (s & U_COLORMAP)\n\t\t\t\t\t\t\ti++;\n\n\t\t\t\t\t\tif (s & U_SKIN)\n\t\t\t\t\t\t\ti++;\n\n\t\t\t\t\t\tif (s & U_EFFECTS)\n\t\t\t\t\t\t\ti++;\n\n\t\t\t\t\t\tif (s & U_ORIGIN1)\n\t\t\t\t\t\t\ti+=2;\n\n\t\t\t\t\t\tif (s & U_ANGLE1)\n\t\t\t\t\t\t\ti++;\n\n\t\t\t\t\t\tif (s & U_ORIGIN2)\n\t\t\t\t\t\t\ti+=2;\n\n\t\t\t\t\t\tif (s & U_ANGLE2)\n\t\t\t\t\t\t\ti++;\n\n\t\t\t\t\t\tif (s & U_ORIGIN3)\n\t\t\t\t\t\t\ti+=2;\n\n\t\t\t\t\t\tif (s & U_ANGLE3)\n\t\t\t\t\t\t\ti++;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbufferlen--;\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase svc_lightstyle:\n\t\t\tif (!data && bufferlen>=2)\n\t\t\t\tprotocollen = bufferlen+1;\n\t\t\tbreak;\n\n\t\tcase svc_serverinfo:\t//looking for two null terminators\n\t\t\tif (!data)\t//gotta be the second.\n\t\t\t{\n\t\t\t\tfor (i = 0; i < bufferlen; i++)\n\t\t\t\t{\n\t\t\t\t\tif (!buffer[i])\t//we already wrote one.\n\t\t\t\t\t{\n\t\t\t\t\t\tprotocollen = bufferlen+1;\t//so this is the last qbyte\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase svc_sound:\n\t\t\tif (bufferlen == 3) //decide after 3\n\t\t\t{\n\t\t\t\tunsigned short s;\n\t\t\t\ti = 10;\n\t\t\t\ts = buffer[1] + buffer[2]*256;\n\t\t\t\tif (s & SND_VOLUME)\n\t\t\t\t\ti++;\n\t\t\t\tif (s & SND_ATTENUATION)\n\t\t\t\t\ti++;\n\t\t\t\tprotocollen = i;\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase svc_setinfo:\n\t\t\tif (!data && bufferlen>2)\n\t\t\t{\n\t\t\t\tfor (i = 2; i < bufferlen; i++)\n\t\t\t\t{\n\t\t\t\t\tif (!buffer[i])\n\t\t\t\t\t{\n\t\t\t\t\t\tprotocollen = bufferlen+1;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase svc_centerprint:\n\t\t\tif (!data && bufferlen>=1)\n\t\t\t\tprotocollen = bufferlen+1;\n\t\t\tbreak;\n\t\tcase svc_print:\n\t\t\tif (!data && bufferlen>=2)\n\t\t\t\tprotocollen = bufferlen+1;\n\t\t\tbreak;\n\n\t\tcase svc_nails:\n\t\t\tif (bufferlen == 1)\n\t\t\t{\n\t\t\t\ti = data;\t//first qbyte is id, second, (the current, about to be written) is number of nails.\n\t\t\t\tprotocollen = (i * 6) + 2;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase svc_nails2:\n\t\t\tif (bufferlen == 1)\n\t\t\t{\n\t\t\t\ti = data;\t//first qbyte is id, second, (the current, about to be written) is number of nails.\n\t\t\t\tprotocollen = (i * 7) + 2;\n\t\t\t}\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tCon_Printf(\"mvd: bad protocol %i\\n\", (int)data);\n\t\t\tprotocollen = sizeof(buffer);\n\t\t\tnet_message.cursize=0;\n\t\t\tdata = svc_nop;\n\t\t\tprotocollen = 1;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\n\tNPP_AddData(&data, sizeof(qbyte));\n\tNPP_MVDCheckFlush();\n}\n#endif //SERVER_DEMO_PLAYBACK\n\nvoid NPP_Flush(void)\n{\n\tif (progstype != PROG_QW)\n\t\tNPP_NQFlush();\n#ifdef NQPROT\n\telse\n\t\tNPP_QWFlush();\n#endif\n}\n#endif\n#endif\n"
  },
  {
    "path": "engine/server/pr_cmds.c",
    "content": "/*\nCopyright (C) 1996-1997 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n*/\n\n#include \"quakedef.h\"\n\n#ifdef SQL\n#include \"sv_sql.h\"\n#endif\n\n#ifndef SQL\n#define PF_sqlconnect\t\tPF_Fixme\n#define PF_sqldisconnect\tPF_Fixme\n#define PF_sqlopenquery\t\tPF_Fixme\n#define PF_sqlclosequery\tPF_Fixme\n#define PF_sqlreadfield\t\tPF_Fixme\n#define PF_sqlerror\t\t\tPF_Fixme\n#define PF_sqlescape\t\tPF_Fixme\n#define PF_sqlversion\t\tPF_Fixme\n#define PF_sqlreadfloat\t\tPF_Fixme\n#define PF_sqlreadblob\t\tPF_Fixme\n#define PF_sqlescapeblob\tPF_Fixme\n#endif\n\n#ifndef CLIENTONLY\n\n#include \"pr_common.h\"\n\n//okay, so these are a quick but easy hack\nint PR_EnableEBFSBuiltin(const char *name, int binum);\nint PR_CSQC_BuiltinValid(const char *name, int num);\n\n/*cvars for the gamecode only*/\ncvar_t\tnomonsters = CVAR(\"nomonsters\", \"0\");\ncvar_t\tgamecfg = CVAR(\"gamecfg\", \"0\");\ncvar_t\tscratch1 = CVAR(\"scratch1\", \"0\");\ncvar_t\tscratch2 = CVAR(\"scratch2\", \"0\");\ncvar_t\tscratch3 = CVAR(\"scratch3\", \"0\");\ncvar_t\tscratch4 = CVAR(\"scratch4\", \"0\");\ncvar_t\tsavedgamecfg = CVARF(\"savedgamecfg\", \"0\", CVAR_ARCHIVE);\ncvar_t\tsaved1 = CVARF(\"saved1\", \"0\", CVAR_ARCHIVE);\ncvar_t\tsaved2 = CVARF(\"saved2\", \"0\", CVAR_ARCHIVE);\ncvar_t\tsaved3 = CVARF(\"saved3\", \"0\", CVAR_ARCHIVE);\ncvar_t\tsaved4 = CVARF(\"saved4\", \"0\", CVAR_ARCHIVE);\ncvar_t\ttemp1 = CVARF(\"temp1\", \"0\", CVAR_ARCHIVE);\ncvar_t\tnoexit = CVAR(\"noexit\", \"0\");\nextern cvar_t sv_specprint;\n\n//cvar_t\tsv_aim = {\"sv_aim\", \"0.93\"};\ncvar_t\tsv_aim = CVARD(\"sv_aim\", \"2\", \"The value should be cos(angle), where angle is the greatest allowed angle from which to deviate from the direction the shot would have been along. Values >1 thus disable auto-aim. This can be overridden with setinfo aim 0.73.\");\ncvar_t\tsv_maxaim = CVARD(\"sv_maxaim\", \"22\", \"The maximum acceptable angle for autoaiming.\");\n\nextern cvar_t pr_autocreatecvars;\ncvar_t\tpr_ssqc_memsize = CVARD(\"pr_ssqc_memsize\", \"-1\", \"The ammount of memory available to the QC vm. This has a theoretical maximum of 1gb, but that value can only really be used in 64bit builds. -1 will attempt to use some conservative default, but you may need to increase it. Consider also clearing pr_fixbrokenqccarrays if you need to change this cvar.\");\n\n/*cvars purely for compat with others*/\ncvar_t\tpr_imitatemvdsv = CVARFD(\"pr_imitatemvdsv\", \"0\", CVAR_MAPLATCH, \"Enables mvdsv-specific builtins, and fakes identifiers so that mods made for mvdsv can run properly and with the full feature set.\");\n\n/*other stuff*/\ncvar_t\tpr_maxedicts = CVARAFD(\"pr_maxedicts\", \"131072\", \"max_edicts\", CVAR_MAPLATCH, \"Maximum number of entities spawnable on the map at once. Low values will crash the server on some maps/mods. High values will result in excessive memory useage (see pr_ssqc_memsize). Illegible server messages may occur with old/other clients above 32k. FTE's network protocols have a maximum at a little over 4 million. Please don't ever make a mod that actually uses that many...\");\n\n#ifndef HAVE_LEGACY\ncvar_t\tpr_no_playerphysics = CVARFD(\"pr_no_playerphysics\", \"1\", CVAR_MAPLATCH, \"Prevents support of the 'SV_PlayerPhysics' QC function. This allows servers to prevent needless breakage of player prediction.\");\n#else\nstatic cvar_t\tpr_no_playerphysics = CVARFD(\"pr_no_playerphysics\", \"0\", CVAR_MAPLATCH, \"Prevents support of the 'SV_PlayerPhysics' QC function. This allows servers to prevent needless breakage of player prediction.\");\n#endif\nstatic cvar_t\tpr_no_parsecommand = CVARFD(\"pr_no_parsecommand\", \"0\", 0, \"Provides a way around invalid mod usage of SV_ParseClientCommand, eg xonotic.\");\n\nextern cvar_t pr_sourcedir;\ncvar_t\tpr_ssqc_progs = CVARAFD(\"sv_progs\", \"\", \"progs\", CVAR_ARCHIVE | CVAR_SERVERINFO | CVAR_NOTFROMSERVER, \"Specifies the filename of the gamecode to use serverside. Empty means autodetect - typically loading either progs or qwprogs depending on gamedir priority, then based upon deathmatch settings.\");\nstatic cvar_t\tpr_nonetaccess = CVARD(\"pr_nonetaccess\", \"0\", \"Block all direct access to network buffers (the writebyte builtin and friends will ignore the call).\");\t//prevent write_... builtins from doing anything. This means we can run any mod, specific to any engine, on the condition that it also has a qw or nq crc.\n\nstatic cvar_t pr_overridebuiltins = CVAR(\"pr_overridebuiltins\", \"1\");\n\nstatic cvar_t pr_compatabilitytest = CVARFD(\"pr_compatabilitytest\", \"0\", CVAR_MAPLATCH, \"Only enables builtins if the extension they are part of was queried.\");\n\ncvar_t pr_ssqc_coreonerror = CVAR(\"pr_coreonerror\", \"1\");\n\nstatic cvar_t sv_gameplayfix_honest_tracelines = CVAR(\"sv_gameplayfix_honest_tracelines\", \"1\");\n#ifndef HAVE_LEGACY\nstatic cvar_t sv_gameplayfix_setmodelrealbox = CVARD(\"sv_gameplayfix_setmodelrealbox\", \"1\", \"Vanilla setmodel will setsize the entity to a hardcoded size for non-bsp models. This cvar will always use the real size of the model instead, but will require that the server actually loads the model.\");\n#else\nstatic cvar_t sv_gameplayfix_setmodelrealbox = CVARD(\"sv_gameplayfix_setmodelrealbox\", \"0\", \"Vanilla setmodel will setsize the entity to a hardcoded size for non-bsp models. This cvar will always use the real size of the model instead, but will require that the server actually loads the model.\");\n#endif\nstatic cvar_t sv_gameplayfix_setmodelsize_qw = CVARD(\"sv_gameplayfix_setmodelsize_qw\", \"0\", \"The setmodel builtin will act as a setsize for QuakeWorld mods also.\");\ncvar_t dpcompat_nopreparse = CVARD(\"dpcompat_nopreparse\", \"0\", \"Xonotic uses svc_tempentity with unknowable lengths mixed with other data that needs to be translated. This cvar disables any attempt to translate or pre-parse network messages, including disabling nq/qw cross compatibility. NOTE: because preparsing will be disabled, messages might not get backbuffered correctly if too much reliable data is written.\");\nstatic cvar_t dpcompat_precachesoundhack = CVARD(\"dpcompat_precachesoundhack\", \"0\", \"Changes the behaviour of only the ssqc's precache_sound to return a precache index. precache_model and csqc's precaches are unchanged.\");\n//static cvar_t dpcompat_traceontouch = CVARD(\"dpcompat_traceontouch\", \"0\", \"Report trace plane etc when an entity touches another.\");\nextern cvar_t sv_listen_dp;\n\nstatic cvar_t sv_addon[MAXADDONS];\nstatic char cvargroup_progs[] = \"Progs variables\";\n\nstatic evalc_t evalc_idealpitch, evalc_pitch_speed;\nstatic void PR_Lightstyle_f(void);\n\nqboolean ssqc_deprecated_warned;\nint pr_teamfield;\n#ifdef HEXEN2\nstatic unsigned int h2infoplaque[2];\t/*hexen2 stat*/\n#endif\n\nvoid PR_fclose_progs(pubprogfuncs_t*);\nvoid PF_InitTempStrings(pubprogfuncs_t *prinst);\nstatic int PDECL PR_SSQC_MapNamedBuiltin(pubprogfuncs_t *progfuncs, int headercrc, const char *builtinname);\nstatic void QCBUILTIN PF_Fixme (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\n\nvoid PR_DumpPlatform_f(void);\n\ntypedef struct {\n\t//for func finding and swapping.\n\tchar *name;\n\t//c function to call\n\tbuiltin_t bifunc;\n\n\t//most of the next two are the same, but they follow different family trees.\n\t//It depends on the progs type being loaded as to which is used.\n\tint nqnum;\t\t//standard nq.\n\tint qwnum;\t\t//standard qw.\n\tint h2num;\t\t//standard hexen2\n\tint ebfsnum;\t//extra functions, these exist ONLY after being checked for.\n\n\tchar *prototype;\n\tchar *biglongdesc;\n\tqboolean obsolete;\n} BuiltinList_t;\nbuiltin_t pr_builtin[1024];\n\nstatic struct {\n\tfunc_t ChatMessage;\t//mvdsv parsing of 'say' commands\n\tfunc_t UserCmd;\t//mvdsv\n\tfunc_t ConsoleCmd; //mvdsv\n\tfunc_t UserInfo_Changed;\n\tfunc_t localinfoChanged;\n\n\tfunc_t ParseClusterEvent;\t//FTE_SV_CLUSTER\n\tfunc_t ParseClientCommand;\t//KRIMZON_SV_PARSECLIENTCOMMAND\n\tfunc_t ParseConnectionlessPacket;\t//FTE_QC_SENDPACKET\n\tfunc_t SV_Shutdown;\n\n\tfunc_t PausedTic;\n\tfunc_t ShouldPause;\n\n\tfunc_t RunClientCommand;\t//EXT_CSQC_1\n\n#ifdef HEXEN2\n\tfunc_t ClassChangeWeapon;//hexen2 support\n#endif\n\tfunc_t AddDebugPolygons;\n\tfunc_t CheckRejectConnection;\n\n\tfunc_t ZQ_ClientCommand;\n} gfuncs;\nfunc_t SpectatorConnect;\t//QW\nfunc_t SpectatorThink;\t//QW\nfunc_t SpectatorDisconnect;\t//QW\n\nfunc_t SV_PlayerPhysicsQC;\t//DP's DP_SV_PLAYERPHYSICS extension\nfunc_t EndFrameQC;\t//a common extension\n\nstatic globalptrs_t realpr_global_ptrs;\nglobalptrs_t *pr_global_ptrs = &realpr_global_ptrs;\n\npubprogfuncs_t *svprogfuncs;\nprogparms_t svprogparms;\n\nprogstype_t progstype;\n\nstatic struct\n{\n\tconst char *fieldname;\n\tunsigned int bit;\n\tint offset;\t//((float*)ent->v)[offset] = buttonpressed; or -1\n} buttonfields[] =\n{\n\t//0,1,2 are handled specially, because they're always defined in qc (and button1 is hacky).\n\t{\"button3\",  2},\n\t{\"button4\",  3},\n\t{\"button5\",  4},\n\t{\"button6\",  5},\n\t{\"button7\",  6},\n\t{\"button8\",  7},\n\n\t//and for dp compat (these names are fucked, yes)\n\t{\"buttonuse\",    8},\n\t{\"buttonchat\",   9},\n\t{\"cursor_active\", 10},\n\t{\"button9\",  11},\n\t{\"button10\", 12},\n\t{\"button11\", 13},\n\t{\"button12\", 14},\n\t{\"button13\", 15},\n\n\t{\"button14\", 16},\n\t{\"button15\", 17},\n\t{\"button16\", 18},\n/*\t{\"button17\", 19},\n\t{\"button18\", 20},\n\t{\"button19\", 21},\n\t{\"button20\", 22},\n\t{\"button21\", 23},\n\n\t{\"button22\", 24},\n\t{\"button23\", 25},\n\t{\"button24\", 26},\n\t{\"button25\", 27},\n\t{\"button26\", 28},\n\t{\"button27\", 29},\n\t{\"button28\", 30},\n\t{\"button29\", 31},\n*/\n};\n\nvoid PR_RegisterFields(void);\nvoid PR_ResetBuiltins(progstype_t type);\n\nvoid PDECL ED_Spawned (struct edict_s *ent, int loading)\n{\n#ifdef VM_Q1\n\tif (!ent->xv)\n\t\tent->xv = (extentvars_t *)(ent->v+1);\n#endif\n\n\tif (!loading || !ent->xv->uniquespawnid)\n\t{\n\t\tent->xv->dimension_see = pr_global_struct->dimension_default;\n\t\tent->xv->dimension_seen = pr_global_struct->dimension_default;\n\t\tent->xv->dimension_ghost = 0;\n\t\tent->xv->dimension_solid = pr_global_struct->dimension_default;\n\t\tent->xv->dimension_hit = pr_global_struct->dimension_default;\n#ifdef HEXEN2\n\t\tif (progstype != PROG_H2)\n\t\t\tent->xv->drawflags = SCALE_ORIGIN_ORIGIN;\t//if not running hexen2, default the scale origin to the actual origin.\n#endif\n\n#ifdef HAVE_LEGACY\n\t\tent->xv->Version = sv.csqcentversion[ent->entnum];\n#endif\n\t\tent->xv->uniquespawnid = sv.csqcentversion[ent->entnum];\n\n\t\tif (!ent->baseline.number)\n\t\t{\t//make sure it has a valid baseline\n\t\t\textern entity_state_t nullentitystate;\n\t\t\tmemcpy(&ent->baseline, &nullentitystate, sizeof(ent->baseline));\n\t\t\tent->baseline.number = ent->entnum;\n\t\t}\n\t}\n}\n\npbool PDECL ED_CanFree (edict_t *ed)\n{\n\tif (ed == (edict_t*)sv.world.edicts)\n\t{\n\t\tif (developer.value)\n\t\t\tPR_RunWarning(svprogfuncs, \"cannot free world entity\\n\");\n\t\treturn false;\n\t}\n\tif (NUM_FOR_EDICT(svprogfuncs, ed) <= sv.allocated_client_slots)\n\t{\n\t\tPR_RunWarning(svprogfuncs, \"cannot free player entities\\n\");\n\t\treturn false;\n\t}\n\tWorld_UnlinkEdict ((wedict_t*)ed);\t\t// unlink from world bsp\n\n\ted->v->model = 0;\n\ted->v->takedamage = 0;\n\ted->v->modelindex = 0;\n\ted->v->colormap = 0;\n\ted->v->skin = 0;\n\ted->v->frame = 0;\n\tVectorClear (ed->v->origin);\n\tVectorClear (ed->v->angles);\n\ted->v->solid = 0;\n\ted->xv->pvsflags = 0;\n\n#ifndef HAVE_LEGACY\n\t//ideal world...\n\t\ted->v->nextthink = 0;\n\t\ted->v->think = 0;\n\t\ted->v->classname = 0;\n\t\ted->v->health = 0;\n#else\n\t//yay compat\n\t\ted->v->nextthink = -1;\n\t\tif (progstype == PROG_QW)\n\t\t{\n\t\t\ted->v->classname = 0;\t//this should be below, but 0 is a nicer choice, and matches all qw servers that we actually care about, even if wrong.\n\t\t\tif (pr_imitatemvdsv.value)\n\t\t\t{\n\n\t\t\t\ted->v->health = 0;\n\t\t\t\ted->v->impulse = 0;\t//this is not true imitation, but it seems we need this line to get out of some ktpro infinate loops.\n\t\t\t}\n\t\t}\n#endif\n\n\ted->xv->SendEntity = 0;\n\tsv.csqcentversion[ed->entnum] += 1;\n\n#ifdef USERBE\n\tif (sv.world.rbe)\n\t{\n\t\tsv.world.rbe->RemoveFromEntity(&sv.world, (wedict_t*)ed);\n\t\tsv.world.rbe->RemoveJointFromEntity(&sv.world, (wedict_t*)ed);\n\t}\n#endif\n\n\n\treturn true;\n}\n\nstatic void ASMCALL StateOp (pubprogfuncs_t *prinst, float var, func_t func)\n{\n\tstdentvars_t *vars = PROG_TO_EDICT(prinst, pr_global_struct->self)->v;\n#ifdef HEXEN2\n\tif (progstype == PROG_H2)\n\t\tvars->nextthink = pr_global_struct->time+0.05;\n\telse\n#endif\n\t\tvars->nextthink = pr_global_struct->time+0.1;\n\tvars->think = func;\n\tvars->frame = var;\n}\n#ifdef HEXEN2\nstatic void ASMCALL CStateOp (pubprogfuncs_t *prinst, float first, float last, func_t currentfunc)\n{\n\tfloat min, max;\n\tfloat step;\n\twedict_t *e = PROG_TO_WEDICT(prinst, pr_global_struct->self);\n\tfloat frame = e->v->frame;\n\n\tif (progstype == PROG_H2)\n\t\te->v->nextthink = pr_global_struct->time+0.05;\n\telse\n\t\te->v->nextthink = pr_global_struct->time+0.1;\n\te->v->think = currentfunc;\n\n\tif (pr_global_ptrs->cycle_wrapped)\n\t\tpr_global_struct->cycle_wrapped = false;\n\n\tif (first > last)\n\t{\t//going backwards\n\t\tmin = last;\n\t\tmax = first;\n\t\tstep = -1.0;\n\t}\n\telse\n\t{\t//forwards\n\t\tmin = first;\n\t\tmax = last;\n\t\tstep = 1.0;\n\t}\n\tif (frame < min || frame > max)\n\t\tframe = first;\t//started out of range, must have been a different animation\n\telse\n\t{\n\t\tframe += step;\n\t\tif (frame < min || frame > max)\n\t\t{\t//became out of range, must have wrapped\n\t\t\tif (pr_global_ptrs->cycle_wrapped)\n\t\t\t\tpr_global_struct->cycle_wrapped = true;\n\t\t\tframe = first;\n\t\t}\n\t}\n\te->v->frame = frame;\n}\nstatic void ASMCALL CWStateOp (pubprogfuncs_t *prinst, float first, float last, func_t currentfunc)\n{\n\tfloat min, max;\n\tfloat step;\n\twedict_t *e = PROG_TO_WEDICT(prinst, pr_global_struct->self);\n\tfloat frame = e->v->weaponframe;\n\n\tif (progstype == PROG_H2)\n\t\te->v->nextthink = pr_global_struct->time+0.05;\n\telse\n\t\te->v->nextthink = pr_global_struct->time+0.1;\n\te->v->think = currentfunc;\n\n\tif (pr_global_ptrs->cycle_wrapped)\n\t\tpr_global_struct->cycle_wrapped = false;\n\n\tif (first > last)\n\t{\t//going backwards\n\t\tmin = last;\n\t\tmax = first;\n\t\tstep = -1.0;\n\t}\n\telse\n\t{\t//forwards\n\t\tmin = first;\n\t\tmax = last;\n\t\tstep = 1.0;\n\t}\n\tif (frame < min || frame > max)\n\t\tframe = first;\t//started out of range, must have been a different animation\n\telse\n\t{\n\t\tframe += step;\n\t\tif (frame < min || frame > max)\n\t\t{\t//became out of range, must have wrapped\n\t\t\tif (pr_global_ptrs->cycle_wrapped)\n\t\t\t\tpr_global_struct->cycle_wrapped = true;\n\t\t\tframe = first;\n\t\t}\n\t}\n\te->v->weaponframe = frame;\n}\n\nstatic void ASMCALL ThinkTimeOp (pubprogfuncs_t *prinst, edict_t *ed, float var)\n{\n\tstdentvars_t *vars = ed->v;\n\tvars->nextthink = pr_global_struct->time+var;\n}\n#endif\n\nstatic int SV_ParticlePrecache_Add(const char *pname);\nstatic pbool PDECL SV_BadField(pubprogfuncs_t *inst, edict_t *foo, const char *keyname, const char *value)\n{\n\tif (!strcmp(keyname, \"traileffect\"))\n\t{\n\t\tfoo->xv->traileffectnum = SV_ParticlePrecache_Add(value);\n\t\treturn true;\n\t}\n\tif (!strcmp(keyname, \"emiteffect\"))\n\t{\n\t\tfoo->xv->emiteffectnum = SV_ParticlePrecache_Add(value);\n\t\treturn true;\n\t}\n\n\tif (*keyname == '_')\n\t\tkeyname++;\n\n#ifdef HEXEN2\n\t/*Worldspawn only fields...*/\n\tif (NUM_FOR_EDICT(inst, foo) == 0)\n\t{\n\t\t/*hexen2 midi - just mute it, we don't support it*/\n\t\tif (!stricmp(keyname, \"MIDI\"))\n\t\t{\n\t\t\tQ_strncpyz(sv.h2miditrack, value, sizeof(sv.h2miditrack));\n\t\t\treturn true;\n\t\t}\n\t\t/*hexen2 does cd tracks slightly differently too, so there's consistancy for you*/\n\t\tif (!stricmp(keyname, \"CD\"))\n\t\t{\n\t\t\tsv.h2cdtrack = atoi(value);\n\t\t\treturn true;\n\t\t}\n\t}\n#endif\n\n\tif (!strcmp(keyname, \"sky\") || !strcmp(keyname, \"fog\"))\n\t\treturn true;\t//these things are handled in the client, so don't warn if they're used.\n\n\tif (!strcmp(keyname, \"skyroom\"))\n\t{\n\t\tvalue = COM_Parse(value);sv.skyroom_pos[0] = atof(com_token);\n\t\tvalue = COM_Parse(value);sv.skyroom_pos[1] = atof(com_token);\n\t\tvalue = COM_Parse(value);sv.skyroom_pos[2] = atof(com_token);\n\t\tsv.skyroom_pos_known = true;\n\t\treturn true;\n\t}\n\n\t//don't spam warnings about missing fields if we failed to load the progs.\n\tif (!svs.numprogs)\n\t\treturn true;\n\n\treturn false;\n}\n\nvoid PR_SV_FillWorldGlobals(world_t *w)\n{\n\tunsigned int i;\n\tfor (i = 0; i < countof(buttonfields); i++)\n\t\tbuttonfields[i].offset = -1;\n\tw->g.self = pr_global_ptrs->self;\n\tw->g.other = pr_global_ptrs->other;\n\tw->g.force_retouch = pr_global_ptrs->force_retouch;\n\tw->g.physics_mode = pr_global_ptrs->physics_mode;\n\tw->g.frametime = pr_global_ptrs->frametime;\n\tw->g.newmis = pr_global_ptrs->newmis;\n\tw->g.time = pr_global_ptrs->time;\n\tw->g.v_forward = *pr_global_ptrs->v_forward;\n\tw->g.v_right = *pr_global_ptrs->v_right;\n\tw->g.v_up = *pr_global_ptrs->v_up;\n\tw->g.defaultgravitydir = *pr_global_ptrs->global_gravitydir;\n}\n\nstatic void PDECL PR_SSQC_Relocated(pubprogfuncs_t *pr, char *oldb, char *newb, int oldlen)\n{\n#ifdef VM_Q1\n\tedict_t *ent;\n#endif\n\tint i;\n\tunion {\n\t\tglobalptrs_t *g;\n\t\tchar **c;\n\t} b;\n\tb.g = pr_global_ptrs;\n\tfor (i = 0; i < sizeof(*b.g)/sizeof(*b.c); i++)\n\t{\n\t\tif (b.c[i] >= oldb && b.c[i] < oldb+oldlen)\n\t\t\tb.c[i] += newb - oldb;\n\t}\n\tPR_SV_FillWorldGlobals(&sv.world);\n\n#ifdef VM_Q1\n\tfor (i = 0; i < sv.world.num_edicts; i++)\n\t{\n\t\tent = EDICT_NUM_PB(pr, i);\n\t\tif ((char*)ent->xv >= oldb && (char*)ent->xv < oldb+oldlen)\n\t\t\tent->xv = (extentvars_t*)((char*)ent->xv - oldb + newb);\n\t}\n#endif\n\n\tfor (i = 0; sv.strings.model_precache[i]; i++)\n\t{\n\t\tif (sv.strings.model_precache[i] >= oldb && sv.strings.model_precache[i] < oldb+oldlen)\n\t\t\tsv.strings.model_precache[i] += newb - oldb;\n\t}\n\tfor (i = 0; i < svs.allocated_client_slots; i++)\n\t{\n\t\tif (svs.clients[i].name >= oldb && svs.clients[i].name < oldb+oldlen)\n\t\t\tsvs.clients[i].name += newb - oldb;\n\t\tif (svs.clients[i].team >= oldb && svs.clients[i].team < oldb+oldlen)\n\t\t\tsvs.clients[i].team += newb - oldb;\n\t}\n}\n\n//int QCEditor (char *filename, int line, int nump, char **parms);\nvoid QC_Clear(void);\nbuiltin_t pr_builtin[];\nextern int pr_numbuiltins;\n\nmodel_t *QDECL SVPR_GetCModel(world_t *w, int modelindex)\n{\n\tif ((unsigned int)modelindex < MAX_PRECACHE_MODELS)\n\t{\n\t\tmodel_t *mod;\n\t\tif (!sv.models[modelindex] && sv.strings.model_precache[modelindex])\n\t\t\tsv.models[modelindex] = Mod_ForName(Mod_FixName(sv.strings.model_precache[modelindex], sv.modelname), MLV_WARN);\n\t\tmod = sv.models[modelindex];\n\t\tif (mod && mod->loadstate != MLS_LOADED)\n\t\t{\n\t\t\tif (mod->loadstate == MLS_NOTLOADED)\n\t\t\t\tMod_LoadModel(mod, MLV_SILENT);\n\t\t\tif (mod->loadstate == MLS_LOADING)\n\t\t\t\tCOM_WorkerPartialSync(mod, &mod->loadstate, MLS_LOADING);\n\t\t\tif (mod->loadstate != MLS_LOADED)\n\t\t\t\tmod = NULL;\t//gah, it failed!\n\t\t}\n\t\treturn mod;\n\t}\n\telse\n\t\treturn NULL;\n}\nstatic void QDECL SVPR_Get_FrameState(world_t *w, wedict_t *ent, framestate_t *fstate)\n{\n\tmemset(fstate, 0, sizeof(*fstate));\n\tfstate->g[FS_REG].frame[0] = ent->v->frame;\n\tfstate->g[FS_REG].frametime[0] = ent->xv->frame1time;\n\tfstate->g[FS_REG].lerpweight[0] = 1;\n\tfstate->g[FS_REG].endbone = 0x7fffffff;\n\n\tfstate->g[FST_BASE].frame[0] = ent->xv->baseframe;\n\tfstate->g[FST_BASE].frametime[0] = ent->xv->/*base*/frame1time;\n\tfstate->g[FST_BASE].lerpweight[0] = 1;\n\tfstate->g[FST_BASE].endbone = ent->xv->basebone;\n\n#if defined(SKELETALOBJECTS) || defined(RAGDOLL)\n\tif (ent->xv->skeletonindex)\n\t\tskel_lookup(w, ent->xv->skeletonindex, fstate);\n#endif\n}\n\nstatic void set_trace_globals(pubprogfuncs_t *prinst, trace_t *trace);\nstatic void QDECL SVPR_Event_Touch(world_t *w, wedict_t *s, wedict_t *o, trace_t *trace)\n{\n\tint oself = pr_global_struct->self;\n\tint oother = pr_global_struct->other;\n\n\tif (trace)\n\t\tset_trace_globals(w->progs, trace);\n\n\tpr_global_struct->self = EDICT_TO_PROG(w->progs, s);\n\tpr_global_struct->other = EDICT_TO_PROG(w->progs, o);\n\tpr_global_struct->time = w->physicstime;\n\tPR_ExecuteProgram (w->progs, s->v->touch);\n\n\tpr_global_struct->self = oself;\n\tpr_global_struct->other = oother;\n}\n\nstatic void QDECL SVPR_Event_Think(world_t *w, wedict_t *s)\n{\n\tpr_global_struct->self = EDICT_TO_PROG(w->progs, s);\n\tpr_global_struct->other = EDICT_TO_PROG(w->progs, w->edicts);\n\tif (!s->v->think)\n\t\tCon_Printf(\"SSQC entity \\\"%s\\\" has nextthink with no think function\\n\", PR_GetString(w->progs, s->v->classname));\n\telse\n\t\tPR_ExecuteProgram (w->progs, s->v->think);\n}\n\nstatic qboolean QDECL SVPR_Event_ContentsTransition(world_t *w, wedict_t *ent, int oldwatertype, int newwatertype)\n{\n\tif (ent->xv->contentstransition)\n\t{\n\t\tvoid *pr_globals = PR_globals(w->progs, PR_CURRENT);\n\t\tpr_global_struct->self = EDICT_TO_PROG(w->progs, ent);\n\t\tpr_global_struct->time = w->physicstime;\n\t\tG_FLOAT(OFS_PARM0) = oldwatertype;\n\t\tG_FLOAT(OFS_PARM1) = newwatertype;\n\t\tPR_ExecuteProgram (w->progs, ent->xv->contentstransition);\n\t\treturn true;\n\t}\n\treturn false;\t//do legacy behaviour\n}\n\n#define QW_PROGHEADER_CRC\t54730\n#define NQ_PROGHEADER_CRC\t5927\n#define PREREL_PROGHEADER_CRC\t26940\t//prerelease\n#define TENEBRAE_PROGHEADER_CRC\t32401\t//tenebrae\n#define H2_PROGHEADER_CRC\t38488\t//basic hexen2\n#define H2MP_PROGHEADER_CRC\t26905\t//hexen2 mission pack uses slightly different defs... *sigh*...\n#define H2DEMO_PROGHEADER_CRC\t14046\t//I'm guessing this is from the original release or something\n\npbool PDECL PR_SSQC_CheckHeaderCrc(pubprogfuncs_t *inst, progsnum_t idx, int crc, const char *filename)\n{\n\tprogstype_t modtype;\n\tif (crc == QW_PROGHEADER_CRC)\n\t\tmodtype = PROG_QW;\n\telse if (crc == NQ_PROGHEADER_CRC)\n\t\tmodtype = PROG_NQ;\n#ifdef HEXEN2\n\telse if (crc == H2_PROGHEADER_CRC || crc == H2MP_PROGHEADER_CRC || crc == H2DEMO_PROGHEADER_CRC)\n\t\tmodtype = PROG_H2;\n#endif\n\telse if (crc == PREREL_PROGHEADER_CRC)\n\t\tmodtype = PROG_PREREL;\n\telse if (crc == TENEBRAE_PROGHEADER_CRC)\n\t\tmodtype = PROG_TENEBRAE;\n\telse\n\t\tmodtype = PROG_UNKNOWN;\n\n\t//if we didn't see one yet, use the one that just got loaded.\n\tif (progstype == PROG_NONE)\n\t\tprogstype = modtype;\n\t//if the new one differs from the main module, reject it, unless it has crc 0, which we'll allow as a universal mutator (good luck guessing the correct arguments, but hey).\n\tif (progstype != modtype && crc != 0)\n\t{\n\t\tCon_Printf(\"Unable to load \\\"%s\\\" due to mismatched gametype/progdefs\\n\", filename);\n\t\treturn false;\n\t}\n\treturn true;\n}\nstatic void *PDECL SSQC_PRReadFile (const char *path, qbyte *(PDECL *buf_get)(void *buf_ctx, size_t size), void *buf_ctx, size_t *size, pbool issource)\n{\n\tflocation_t loc;\n\tif (FS_FLocateFile(path, FSLF_IFFOUND, &loc))\n\t{\n\t\tqbyte *buffer = NULL;\n\t\tvfsfile_t *file = FS_OpenReadLocation(path, &loc);\n\t\tif (file)\n\t\t{\n\t\t\t*size = loc.len;\n\t\t\tbuffer = buf_get(buf_ctx, *size);\n\t\t\tif (buffer)\n\t\t\t\tVFS_READ(file, buffer, *size);\n\t\t\tVFS_CLOSE(file);\n\t\t}\n\t\treturn buffer;\n\t}\n\telse\n\t\treturn NULL;\n}\nstatic int PDECL SSQC_PRFileSize (const char *path)\n{\n\tflocation_t loc;\n\tif (FS_FLocateFile(path, FSLF_IFFOUND, &loc))\n\t\treturn loc.len;\n\telse\n\t\treturn -1;\n}\n\nvoid Q_SetProgsParms(qboolean forcompiler)\n{\n\tprogstype = PROG_NONE;\n\tsvprogparms.progsversion = PROGSTRUCT_VERSION;\n\tsvprogparms.ReadFile = SSQC_PRReadFile;//char *(*ReadFile) (char *fname, void *buffer, int *len);\n\tsvprogparms.FileSize = SSQC_PRFileSize;//int (*FileSize) (char *fname);\t//-1 if file does not exist\n\tsvprogparms.WriteFile = QC_WriteFile;//bool (*WriteFile) (char *name, void *data, int len);\n\tsvprogparms.Printf = PR_Printf;//Con_Printf;//void (*printf) (char *, ...);\n\tsvprogparms.DPrintf = PR_DPrintf;//Con_Printf;//void (*printf) (char *, ...);\n\tsvprogparms.CheckHeaderCrc = PR_SSQC_CheckHeaderCrc;\n\tsvprogparms.Sys_Error = Sys_Error;\n\tsvprogparms.Abort = SV_Error;\n\tsvprogparms.edictsize = sizeof(edict_t);\n\n\tsvprogparms.entspawn = ED_Spawned;//void (*entspawn) (struct edict_s *ent);\t//ent has been spawned, but may not have all the extra variables (that may need to be set) set\n\tsvprogparms.entcanfree = ED_CanFree;//bool (*entcanfree) (struct edict_s *ent);\t//return true to stop ent from being freed\n\tsvprogparms.stateop = StateOp;//void (*stateop) (float var, func_t func);\n#ifdef HEXEN2\n\tsvprogparms.cstateop = CStateOp;\n\tsvprogparms.cwstateop = CWStateOp;\n\tsvprogparms.thinktimeop = ThinkTimeOp;\n#endif\n\n\tsvprogparms.MapNamedBuiltin = PR_SSQC_MapNamedBuiltin;\n\tsvprogparms.loadcompleate = NULL;//void (*loadcompleate) (int edictsize);\t//notification to reset any pointers.\n\tsvprogparms.badfield = SV_BadField;\n\n\tsvprogparms.memalloc = PR_CB_Malloc;//void *(*memalloc) (int size);\t//small string allocation\tmalloced and freed randomly\n\tsvprogparms.memfree = PR_CB_Free;//void (*memfree) (void * mem);\n\n\n\tsvprogparms.globalbuiltins = pr_builtin;//builtin_t *globalbuiltins;\t//these are available to all progs\n\tsvprogparms.numglobalbuiltins = pr_numbuiltins;\n\n\tsvprogparms.autocompile = PR_COMPILEIGNORE;//PR_COMPILECHANGED;//enum {PR_NOCOMPILE, PR_COMPILENEXIST, PR_COMPILECHANGED, PR_COMPILEALWAYS} autocompile;\n\n\tsvprogparms.gametime = &sv.time;\n#ifdef MULTITHREAD\n\tsvprogparms.usethreadedgc = pr_gc_threaded.ival;\n#endif\n\n\tsvprogparms.edicts = (edict_t**)&sv.world.edicts;\n\tsvprogparms.num_edicts = &sv.world.num_edicts;\n\n\tsvprogparms.useeditor = QCEditor;\n\n\t//until its properly tested\n\tif (pr_ssqc_memsize.ival == -2)\n\t\tsvprogparms.addressablerelocated = PR_SSQC_Relocated;\n\n\tsvprogparms.user = &sv.world;\n\tif (!svprogfuncs)\n\t{\n\t\tsv.world.progs = svprogfuncs = InitProgs(&svprogparms);\n\t}\n\tsv.world.Event_Touch = SVPR_Event_Touch;\n\tsv.world.Event_Think = SVPR_Event_Think;\n\tsv.world.Event_Sound = SVQ1_StartSound;\n\tsv.world.Event_ContentsTransition = SVPR_Event_ContentsTransition;\n\tsv.world.Get_CModel = SVPR_GetCModel;\n\tsv.world.Get_FrameState = SVPR_Get_FrameState;\n\tPR_ClearThreads(svprogfuncs);\n\tPR_fclose_progs(svprogfuncs);\n\n//\tsvs.numprogs = 0;\n\n}\n\nvoid PR_Deinit(void)\n{\n\tint i;\n\n\tPR_ClearThreads(svprogfuncs);\n#ifdef VM_Q1\n\tQ1QVM_Shutdown(true);\n#endif\n\tif (svprogfuncs)\n\t{\n\t\tfor (i = 0; i < svs.allocated_client_slots; i++)\n\t\t{\n\t\t\tif (svs.clients[i].name != svs.clients[i].namebuf)\n\t\t\t\tQ_strncpyz(svs.clients[i].namebuf, svs.clients[i].name, sizeof(svs.clients[i].namebuf));\n\t\t\tsvs.clients[i].name = svs.clients[i].namebuf;\n\t\t\tsvs.clients[i].team = svs.clients[i].teambuf;\n\t\t}\n\n\t\tPR_Common_Shutdown(svprogfuncs, false);\n\t\tWorld_Destroy(&sv.world);\n\t\tif (svprogfuncs->Shutdown)\n\t\t\tsvprogfuncs->Shutdown(svprogfuncs);\n\t\tsv.world.progs = NULL;\n\t\tmemset(&gfuncs, 0, sizeof(gfuncs));\n\t\tsvprogfuncs=NULL;\n\t}\n\n\tfor (i = 0; i < sv.maxlightstyles; i++)\n\t{\n\t\tBZ_Free((void*)sv.lightstyles[i].str);\n\t\tsv.lightstyles[i].str = NULL;\n\t}\n\tBZ_Free(sv.lightstyles);\n\tsv.lightstyles = NULL;\n\tsv.maxlightstyles = 0;\n\n\tWorld_Destroy(&sv.world);\n\n\t//clear out function pointers (so changing game modes cannot lead to confusions)\n\tmemset(&gfuncs, 0, sizeof(gfuncs));\n\tSpectatorConnect = 0;\n\tSpectatorThink = 0;\n\tSpectatorDisconnect = 0;\n}\n\nvoid PR_LoadGlabalStruct(qboolean muted)\n{\n\tstatic pvec_t svphysicsmode = 2;\n\tstatic pvec_t writeonly;\n\tstatic pint_t writeonly_int;\n\tstatic pint_t endcontentsi, surfaceflagsi;\n#ifdef HAVE_LEGACY\n\tstatic pvec_t endcontentsf, surfaceflagsf;\n#endif\n\tstatic pvec_t dimension_send_default;\n\tstatic pvec_t dimension_default = 255;\n\tstatic pvec_t zero_default;\n\tstatic pvec_t input_buttons_default;\n\tstatic pvec_t input_timelength_default;\n\tstatic pvec_t input_sequence_default;\n\tstatic pvec_t input_impulse_default;\n\tstatic pvec3_t input_angles_default;\n\tstatic pvec3_t input_movevalues_default;\n\tstatic pvec3_t global_gravitydir_default = {0,0,-1};\n\tint i;\n\tpint_t *v;\n\tetype_t typ;\n\tglobalptrs_t *pr_globals = pr_global_ptrs;\n\tmemset(pr_global_ptrs, 0, sizeof(*pr_global_ptrs));\n\n\t//we need the qw ones, but any in standard quake and not quakeworld, we don't really care about.\n#ifdef HAVE_LEGACY\n#define ssqcglobals_legacy\t\\\n\tglobalfloat\t\t(false, trace_endcontentsf)\t\\\n\tglobalfloat\t\t(false, trace_surfaceflagsf)\t\\\n\tglobalstring\t(false, trace_dphittexturename)\t\\\n\tglobalfloat\t\t(false, trace_dpstartcontents)\t\\\n\tglobalfloat\t\t(false, trace_dphitcontents)\t\\\n\tglobalfloat\t\t(false, trace_dphitq3surfaceflags)\n#else\n\t#define ssqcglobals_legacy\n#endif\n#define ssqcglobals\t\\\n\tglobalentity\t(true, self)\t\t\t\\\n\tglobalentity\t(true, other)\t\t\t\\\n\tglobalentity\t(true, world)\t\t\t\\\n\tglobalfloat\t\t(true, time)\t\t\t\\\n\tglobalfloat\t\t(true, frametime)\t\t\\\n\tglobalentity\t(false, newmis)\t\t\t\\\n\tglobalfloat\t\t(false, force_retouch)\t\\\n\tglobalstring\t(true, mapname)\t\t\t\\\n\tglobalfloat\t\t(false, deathmatch)\t\t\\\n\tglobalfloat\t\t(false, coop)\t\t\t\\\n\tglobalfloat\t\t(false, teamplay)\t\t\\\n\tglobalfloat\t\t(false, serverflags)\t\\\n\tglobalfloat\t\t(false, total_secrets)\t\\\n\tglobalfloat\t\t(false, total_monsters)\t\\\n\tglobalfloat\t\t(false, found_secrets)\t\\\n\tglobalfloat\t\t(false, killed_monsters)\t\\\n\tglobalvec\t\t(true, v_forward)\t\\\n\tglobalvec\t\t(true, v_up)\t\\\n\tglobalvec\t\t(true, v_right)\t\\\n\tglobalfloat\t\t(false, trace_allsolid)\t\\\n\tglobalfloat\t\t(false, trace_startsolid)\t\\\n\tglobalfloat\t\t(false, trace_fraction)\t\\\n\tglobalvec\t\t(false, trace_endpos)\t\\\n\tglobalvec\t\t(false, trace_plane_normal)\t\\\n\tglobalfloat\t\t(false, trace_plane_dist)\t\\\n\tglobalentity\t(true, trace_ent)\t\\\n\tglobalfloat\t\t(false, trace_inopen)\t\\\n\tglobalfloat\t\t(false, trace_inwater)\t\\\n\tglobalint\t\t(false, trace_endcontentsi)\t\\\n\tglobalint\t\t(false, trace_surfaceflagsi)\t\\\n\tglobalstring\t(false, trace_surfacename)\t\\\n\tglobalint\t\t(false, trace_brush_id)\t\\\n\tglobalint\t\t(false, trace_brush_faceid)\t\\\n\tglobalint\t\t(false, trace_surface_id)\t\\\n\tglobalint\t\t(false, trace_bone_id)\t\\\n\tglobalint\t\t(false, trace_triangle_id)\t\\\n\t\\\n\tglobalfloat\t\t(false, cycle_wrapped)\t\\\n\tglobalentity\t(false, msg_entity)\t\\\n\tglobalfunc\t\t(false, main, \"void()\")\t\\\n\tglobalfunc\t\t(true, StartFrame, \"void()\")\t\\\n\tglobalfunc\t\t(true, PlayerPreThink, \"void()\")\t\\\n\tglobalfunc\t\t(true, PlayerPostThink, \"void()\")\t\\\n\tglobalfunc\t\t(true, ClientKill, \"void()\")\t\\\n\tglobalfunc\t\t(true, ClientConnect, \"void()\")\t\\\n\tglobalfunc\t\t(true, PutClientInServer, \"void()\")\t\\\n\tglobalfunc\t\t(true, ClientDisconnect, \"void()\")\t\\\n\tglobalfunc\t\t(false, SetNewParms, \"void()\")\t\\\n\tglobalfunc\t\t(false, SetChangeParms, \"void()\")\t\\\n\tglobalfloat\t\t(false, cycle_wrapped)\t\\\n\tglobalfloat\t\t(false, dimension_send)\t\\\n\tglobalfloat\t\t(false, dimension_default)\t\\\n\t\\\n\tglobalfloat\t\t(false, input_sequence)\t\\\n\tglobalfloat\t\t(false, input_timelength)\t\\\n\tglobalfloat\t\t(false, input_impulse)\t\\\n\tglobalvec\t\t(false, input_angles)\t\\\n\tglobalvec\t\t(false, input_movevalues)\t\\\n\tglobalfloat\t\t(false, input_buttons)\t\\\n\tglobaluint64\t(false, input_weapon)\t\\\n\tglobalfloat\t\t(false, input_lightlevel)\t\\\n\tglobalvec\t\t(false, input_cursor_screen)\t\\\n\tglobalvec\t\t(false, input_cursor_trace_start)\t\\\n\tglobalvec\t\t(false, input_cursor_trace_endpos)\t\\\n\tglobalfloat\t\t(false, input_cursor_entitynumber)\t\\\n\tglobaluint64\t(false, input_head_status)\t\\\n\tglobalvec\t\t(false, input_head_origin)\t\\\n\tglobalvec\t\t(false, input_head_angles)\t\\\n\tglobalvec\t\t(false, input_head_velocity)\t\\\n\tglobalvec\t\t(false, input_head_avelocity)\t\\\n\tglobaluint64\t(false, input_head_weapon)\t\\\n\tglobaluint64\t(false, input_left_status)\t\\\n\tglobalvec\t\t(false, input_left_origin)\t\\\n\tglobalvec\t\t(false, input_left_angles)\t\\\n\tglobalvec\t\t(false, input_left_velocity)\t\\\n\tglobalvec\t\t(false, input_left_avelocity)\t\\\n\tglobaluint64\t(false, input_left_weapon)\t\\\n\tglobaluint64\t(false, input_right_status)\t\\\n\tglobalvec\t\t(false, input_right_origin)\t\\\n\tglobalvec\t\t(false, input_right_angles)\t\\\n\tglobalvec\t\t(false, input_right_velocity)\t\\\n\tglobalvec\t\t(false, input_right_avelocity)\t\\\n\tglobaluint64\t(false, input_right_weapon)\t\\\n\tglobalfloat\t\t(false, input_servertime)\t\\\n\t\\\n\tglobalint\t\t(false, serverid)\t\\\n\tglobalvec\t\t(false, global_gravitydir)\t\\\n\tglobalstring\t(false, parm_string)\t\\\n\tssqcglobals_legacy\n\n#define globalfloat(need,name)\t\t\t(pr_globals)->name = (pvec_t\t*)PR_FindGlobal(svprogfuncs, #name, 0, &typ); /*if ((pr_globals)->name && (typ&0x0fff) != ev_float)    (pr_globals)->name = NULL;*/ if (need && !(pr_globals)->name) {static pvec_t    fallback##name; (pr_globals)->name = &fallback##name; if (!muted) Con_DPrintf(typ!=ev_void?CON_WARNING\"ssqc: global \"#name\" defined as unexpected type %i\\n\":\"Could not find \\\"\"#name\"\\\" export in progs\\n\", typ&0xfff);}\n#define globalentity(need,name)\t\t\t(pr_globals)->name = (pint_t\t*)PR_FindGlobal(svprogfuncs, #name, 0, &typ); /*if ((pr_globals)->name && (typ&0x0fff) != ev_entity)   (pr_globals)->name = NULL;*/ if (need && !(pr_globals)->name) {static pint_t    fallback##name; (pr_globals)->name = &fallback##name; if (!muted) Con_DPrintf(typ!=ev_void?CON_WARNING\"ssqc: global \"#name\" defined as unexpected type %i\\n\":\"Could not find \\\"\"#name\"\\\" export in progs\\n\", typ&0xfff);}\n#define globalint(need,name)\t\t\t(pr_globals)->name = (pint_t\t*)PR_FindGlobal(svprogfuncs, #name, 0, &typ); /*if ((pr_globals)->name && (typ&0x0fff) != ev_integer)  (pr_globals)->name = NULL;*/ if (need && !(pr_globals)->name) {static pint_t    fallback##name; (pr_globals)->name = &fallback##name; if (!muted) Con_DPrintf(typ!=ev_void?CON_WARNING\"ssqc: global \"#name\" defined as unexpected type %i\\n\":\"Could not find \\\"\"#name\"\\\" export in progs\\n\", typ&0xfff);}\n#define globaluint(need,name)\t\t\t(pr_globals)->name = (puint_t\t*)PR_FindGlobal(svprogfuncs, #name, 0, &typ); /*if ((pr_globals)->name && (typ&0x0fff) != ev_uint)     (pr_globals)->name = NULL;*/ if (need && !(pr_globals)->name) {static puint_t   fallback##name; (pr_globals)->name = &fallback##name; if (!muted) Con_DPrintf(typ!=ev_void?CON_WARNING\"ssqc: global \"#name\" defined as unexpected type %i\\n\":\"Could not find \\\"\"#name\"\\\" export in progs\\n\", typ&0xfff);}\n#define globaluint64(need,name)\t\t\t(pr_globals)->name = (puint64_t\t*)PR_FindGlobal(svprogfuncs, #name, 0, &typ);   if ((pr_globals)->name && (typ&0x0fff) != ev_uint64)   (pr_globals)->name = NULL;   if (need && !(pr_globals)->name) {static puint64_t fallback##name; (pr_globals)->name = &fallback##name; if (!muted) Con_DPrintf(typ!=ev_void?CON_WARNING\"ssqc: global \"#name\" defined as unexpected type %i\\n\":\"Could not find \\\"\"#name\"\\\" export in progs\\n\", typ&0xfff);}\n#define globalstring(need,name)\t\t\t(pr_globals)->name = (string_t\t*)PR_FindGlobal(svprogfuncs, #name, 0, &typ); /*if ((pr_globals)->name && (typ&0x0fff) != ev_string)   (pr_globals)->name = NULL;*/ if (need && !(pr_globals)->name) {static string_t  fallback##name; (pr_globals)->name = &fallback##name; if (!muted) Con_DPrintf(typ!=ev_void?CON_WARNING\"ssqc: global \"#name\" defined as unexpected type %i\\n\":\"Could not find \\\"\"#name\"\\\" export in progs\\n\", typ&0xfff);}\n#define globalvec(need,name)\t\t\t(pr_globals)->name = (pvec3_t\t*)PR_FindGlobal(svprogfuncs, #name, 0, &typ);   if ((pr_globals)->name && (typ&0x0fff) != ev_vector)   (pr_globals)->name = NULL;   if (need && !(pr_globals)->name) {static pvec3_t   fallback##name; (pr_globals)->name = &fallback##name; if (!muted) Con_DPrintf(typ!=ev_void?CON_WARNING\"ssqc: global \"#name\" defined as unexpected type %i\\n\":\"Could not find \\\"\"#name\"\\\" export in progs\\n\", typ&0xfff);}\n#define globalfunc(need,name,typestr)\t(pr_globals)->name = (func_t\t*)PR_FindGlobal(svprogfuncs, #name, 0, &typ); /*if ((pr_globals)->name && (typ&0x0fff) != ev_function) (pr_globals)->name = NULL;*/ if (\t\t!(pr_globals)->name) {static func_t    stripped##name; stripped##name = PR_FindFunction(svprogfuncs, #name, 0); if (stripped##name) (pr_globals)->name = &stripped##name; else if (need && !muted) Con_DPrintf(\"Could not find function \\\"\"#name\"\\\" in progs\\n\"); }\n\tssqcglobals\n#undef globalfloat\n#undef globalentity\n#undef globalint\n#undef globaluint64\n#undef globaluint\n#undef globalstring\n#undef globalvec\n#undef globalfunc\n\n\tmemset(&evalc_idealpitch, 0, sizeof(evalc_idealpitch));\n\tmemset(&evalc_pitch_speed, 0, sizeof(evalc_pitch_speed));\n\n\tif (pr_global_ptrs->serverid)\n\t\t*pr_global_ptrs->serverid = svs.clusterserverid;\n\tfor (i = 0; i < NUM_SPAWN_PARMS; i++)\n\t\tpr_global_ptrs->spawnparamglobals[i] = (pvec_t *)PR_FindGlobal(svprogfuncs, va(\"parm%i\", i+1), 0, NULL);\n\n#define ensureglobal(name,var) if (!(pr_globals)->name) (pr_globals)->name = &var;\n\n#ifndef HAVE_LEGACY\n\tif (!(pr_globals)->trace_surfaceflagsi)\n\t\t(pr_globals)->trace_surfaceflagsi = (int*)PR_FindGlobal(svprogfuncs, \"trace_surfaceflags\", 0, NULL);\n\tif (!(pr_globals)->trace_endcontentsi)\n\t\t(pr_globals)->trace_endcontentsi = (int*)PR_FindGlobal(svprogfuncs, \"trace_endcontents\", 0, NULL);\n#else\n\tif (!(pr_globals)->trace_surfaceflagsf && !(pr_globals)->trace_surfaceflagsi)\n\t{\n\t\tetype_t etype;\n\t\teval_t *v = PR_FindGlobal(svprogfuncs, \"trace_surfaceflags\", 0, &etype);\n\t\tif (etype == ev_float)\n\t\t\t(pr_globals)->trace_surfaceflagsf = (pvec_t*)v;\n\t\telse if (etype == ev_integer)\n\t\t\t(pr_globals)->trace_surfaceflagsi = (pint_t*)v;\n\t}\n\tif (!(pr_globals)->trace_endcontentsf && !(pr_globals)->trace_endcontentsi)\n\t{\n\t\tetype_t etype;\n\t\teval_t *v = PR_FindGlobal(svprogfuncs, \"trace_endcontents\", 0, &etype);\n\t\tif (etype == ev_float)\n\t\t\t(pr_globals)->trace_endcontentsf = (pvec_t*)v;\n\t\telse if (etype == ev_integer)\n\t\t\t(pr_globals)->trace_endcontentsi = (pint_t*)v;\n\t}\n\tensureglobal(trace_endcontentsf, endcontentsf);\n\tensureglobal(trace_surfaceflagsf, surfaceflagsf);\n#endif\n\n\t// make sure these entries are always valid pointers\n\tensureglobal(dimension_send, dimension_send_default);\n\tensureglobal(dimension_default, dimension_default);\n\tensureglobal(trace_endcontentsi, endcontentsi);\n\tensureglobal(trace_surfaceflagsi, surfaceflagsi);\n\tensureglobal(trace_brush_id, writeonly_int);\n\tensureglobal(trace_brush_faceid, writeonly_int);\n\tensureglobal(trace_surface_id, writeonly_int);\n\tensureglobal(trace_bone_id, writeonly_int);\n\tensureglobal(trace_triangle_id, writeonly_int);\n\n\tensureglobal(input_sequence, input_sequence_default);\n\tensureglobal(input_timelength, input_timelength_default);\n\tensureglobal(input_impulse, input_impulse_default);\n\tensureglobal(input_angles, input_angles_default);\n\tensureglobal(input_movevalues, input_movevalues_default);\n\tensureglobal(input_buttons, input_buttons_default);\n\tensureglobal(global_gravitydir, global_gravitydir_default);\n\n\t// qtest renames and missing variables\n\tif (!(pr_globals)->trace_plane_normal)\n\t{\n\t\t(pr_globals)->trace_plane_normal = (pvec3_t *)PR_FindGlobal(svprogfuncs, \"trace_normal\", 0, NULL);\n\t\tif (!(pr_globals)->trace_plane_normal)\n\t\t{\n\t\t\tstatic pvec3_t fallback_trace_plane_normal;\n\t\t\t(pr_globals)->trace_plane_normal = &fallback_trace_plane_normal;\n\t\t\tif (!muted)\n\t\t\t\tCon_DPrintf(\"Could not find trace_plane_normal export in progs\\n\");\n\t\t}\n\t}\n\tif (!(pr_globals)->trace_endpos)\n\t{\n\t\t(pr_globals)->trace_endpos = (pvec3_t *)PR_FindGlobal(svprogfuncs, \"trace_impact\", 0, NULL);\n\t\tif (!(pr_globals)->trace_endpos)\n\t\t{\n\t\t\tstatic pvec3_t fallback_trace_endpos;\n\t\t\t(pr_globals)->trace_endpos = &fallback_trace_endpos;\n\t\t\tif (!muted)\n\t\t\t\tCon_DPrintf(\"Could not find trace_endpos export in progs\\n\");\n\t\t}\n\t}\n\tif (!(pr_globals)->trace_fraction)\n\t{\n\t\t(pr_globals)->trace_fraction = (pvec_t *)PR_FindGlobal(svprogfuncs, \"trace_frac\", 0, NULL);\n\t\tif (!(pr_globals)->trace_fraction)\n\t\t{\n\t\t\tstatic pvec_t fallback_trace_fraction;\n\t\t\t(pr_globals)->trace_fraction = &fallback_trace_fraction;\n\t\t\tif (!muted)\n\t\t\t\tCon_DPrintf(\"Could not find trace_fraction export in progs\\n\");\n\t\t}\n\t}\n\tensureglobal(serverflags, zero_default);\n\tensureglobal(total_secrets, zero_default);\n\tensureglobal(total_monsters, zero_default);\n\tensureglobal(found_secrets, zero_default);\n\tensureglobal(killed_monsters, zero_default);\n\tensureglobal(trace_allsolid, writeonly);\n\tensureglobal(trace_startsolid, writeonly);\n\tensureglobal(trace_plane_dist, writeonly);\n\tensureglobal(trace_inopen, writeonly);\n\tensureglobal(trace_inwater, writeonly);\n\tensureglobal(physics_mode, svphysicsmode);\n\n\t//this can be a map start or a loadgame. don't hurt stuff.\n\tif (!pr_global_struct->dimension_send)\n\t\tpr_global_struct->dimension_send = pr_global_struct->dimension_default;\n/*\n\tpr_global_struct->serverflags = 0;\n\tpr_global_struct->total_secrets = 0;\n\tpr_global_struct->total_monsters = 0;\n\tpr_global_struct->found_secrets = 0;\n\tpr_global_struct->killed_monsters = 0;\n*/\n\tpr_teamfield = 0;\n\n\tSpectatorConnect = PR_FindFunction(svprogfuncs, \"SpectatorConnect\", PR_ANY);\n\tSpectatorDisconnect = PR_FindFunction(svprogfuncs, \"SpectatorDisconnect\", PR_ANY);\n\tSpectatorThink = PR_FindFunction(svprogfuncs, \"SpectatorThink\", PR_ANY);\n\n\tgfuncs.ParseClusterEvent = PR_FindFunction(svprogfuncs, \"SV_ParseClusterEvent\", PR_ANY);\n\tgfuncs.ParseClientCommand = PR_FindFunction(svprogfuncs, \"SV_ParseClientCommand\", PR_ANY);\n\tgfuncs.ParseConnectionlessPacket = PR_FindFunction(svprogfuncs, \"SV_ParseConnectionlessPacket\", PR_ANY);\n\tgfuncs.SV_Shutdown = PR_FindFunction(svprogfuncs, \"SV_Shutdown\", PR_ANY);\n\n\tgfuncs.UserInfo_Changed = PR_FindFunction(svprogfuncs, \"UserInfo_Changed\", PR_ANY);\n\tgfuncs.localinfoChanged = PR_FindFunction(svprogfuncs, \"localinfoChanged\", PR_ANY);\n\tgfuncs.ChatMessage = PR_FindFunction(svprogfuncs, \"ChatMessage\", PR_ANY);\n\tgfuncs.UserCmd = PR_FindFunction(svprogfuncs, \"UserCmd\", PR_ANY);\n\tgfuncs.ConsoleCmd = PR_FindFunction(svprogfuncs, \"ConsoleCmd\", PR_ANY);\n\tgfuncs.ZQ_ClientCommand = PR_FindFunction(svprogfuncs, \"GE_ClientCommand\", PR_ANY);\n\n\tgfuncs.PausedTic = PR_FindFunction(svprogfuncs, \"SV_PausedTic\", PR_ANY);\n\tgfuncs.ShouldPause = PR_FindFunction(svprogfuncs, \"SV_ShouldPause\", PR_ANY);\n#ifdef HEXEN2\n\tgfuncs.ClassChangeWeapon = PR_FindFunction(svprogfuncs, \"ClassChangeWeapon\", PR_ANY);\n#endif\n\tgfuncs.RunClientCommand = PR_FindFunction(svprogfuncs, \"SV_RunClientCommand\", PR_ANY);\n\tgfuncs.AddDebugPolygons = PR_FindFunction(svprogfuncs, \"SV_AddDebugPolygons\", PR_ANY);\n\tgfuncs.CheckRejectConnection = PR_FindFunction(svprogfuncs, \"SV_CheckRejectConnection\", PR_ANY);\n\n\tif (pr_no_playerphysics.ival)\n\t\tSV_PlayerPhysicsQC = 0;\n\telse\n\t\tSV_PlayerPhysicsQC = PR_FindFunction(svprogfuncs, \"SV_PlayerPhysics\", PR_ANY);\n\tEndFrameQC = PR_FindFunction (svprogfuncs, \"EndFrame\", PR_ANY);\n\n\tv = (pint_t *)PR_globals(svprogfuncs, PR_CURRENT);\n\tsvprogfuncs->AddSharedVar(svprogfuncs, (pint_t *)(pr_global_ptrs)->self-v, 1);\n\tsvprogfuncs->AddSharedVar(svprogfuncs, (pint_t *)(pr_global_ptrs)->other-v, 1);\n\tsvprogfuncs->AddSharedVar(svprogfuncs, (pint_t *)(pr_global_ptrs)->time-v, 1);\n\n#ifdef QUAKESTATS\n\t//test the global rather than the field - fte internally always has the field.\n\tsv.haveitems2 = !!PR_FindGlobal(svprogfuncs, \"items2\", 0, NULL);\n#endif\n\n\tSV_ClearQCStats();\n\n\tPR_SV_FillWorldGlobals(&sv.world);\n\n\tfor (i = 0; i < countof(buttonfields); i++)\n\t{\n\t\teval_t *val = svprogfuncs->GetEdictFieldValue(svprogfuncs, (edict_t*)sv.world.edicts, buttonfields[i].fieldname, ev_float, NULL);\n\t\tif (val)\n\t\t\tbuttonfields[i].offset = (float*)val - (float*)sv.world.edicts->v;\n\t\telse\n\t\t\tbuttonfields[i].offset = -1;\n\t}\n\n#if defined(HEXEN2) && defined(QUAKESTATS)\n\t/*Hexen2 has lots of extra stats, which I don't want special support for, so list them here and send them as for csqc*/\n\tif (progstype == PROG_H2)\n\t{\n\t\tSV_QCStatName(ev_float, \"level\", STAT_H2_LEVEL);\n\t\tSV_QCStatName(ev_float, \"intelligence\", STAT_H2_INTELLIGENCE);\n\t\tSV_QCStatName(ev_float, \"wisdom\", STAT_H2_WISDOM);\n\t\tSV_QCStatName(ev_float, \"strength\", STAT_H2_STRENGTH);\n\t\tSV_QCStatName(ev_float, \"dexterity\", STAT_H2_DEXTERITY);\n\t\tSV_QCStatName(ev_float, \"bluemana\", STAT_H2_BLUEMANA);\n\t\tSV_QCStatName(ev_float, \"greenmana\", STAT_H2_GREENMANA);\n\t\tSV_QCStatName(ev_float, \"experience\", STAT_H2_EXPERIENCE);\n\t\tSV_QCStatName(ev_float, \"cnt_torch\", STAT_H2_CNT_TORCH);\n\t\tSV_QCStatName(ev_float, \"cnt_h_boost\", STAT_H2_CNT_H_BOOST);\n\t\tSV_QCStatName(ev_float, \"cnt_sh_boost\", STAT_H2_CNT_SH_BOOST);\n\t\tSV_QCStatName(ev_float, \"cnt_mana_boost\", STAT_H2_CNT_MANA_BOOST);\n\t\tSV_QCStatName(ev_float, \"cnt_teleport\", STAT_H2_CNT_TELEPORT);\n\t\tSV_QCStatName(ev_float, \"cnt_tome\", STAT_H2_CNT_TOME);\n\t\tSV_QCStatName(ev_float, \"cnt_summon\", STAT_H2_CNT_SUMMON);\n\t\tSV_QCStatName(ev_float, \"cnt_invisibility\", STAT_H2_CNT_INVISIBILITY);\n\t\tSV_QCStatName(ev_float, \"cnt_glyph\", STAT_H2_CNT_GLYPH);\n\t\tSV_QCStatName(ev_float, \"cnt_haste\", STAT_H2_CNT_HASTE);\n\t\tSV_QCStatName(ev_float, \"cnt_blast\", STAT_H2_CNT_BLAST);\n\t\tSV_QCStatName(ev_float, \"cnt_polymorph\", STAT_H2_CNT_POLYMORPH);\n\t\tSV_QCStatName(ev_float, \"cnt_flight\", STAT_H2_CNT_FLIGHT);\n\t\tSV_QCStatName(ev_float, \"cnt_cubeofforce\", STAT_H2_CNT_CUBEOFFORCE);\n\t\tSV_QCStatName(ev_float, \"cnt_invincibility\", STAT_H2_CNT_INVINCIBILITY);\n\t\tSV_QCStatName(ev_float, \"artifact_active\", STAT_H2_ARTIFACT_ACTIVE);\n\t\tSV_QCStatName(ev_float, \"artifact_low\", STAT_H2_ARTIFACT_LOW);\n\t\tSV_QCStatName(ev_float, \"movetype\", STAT_H2_MOVETYPE);\t\t/*normally used to change the roll when flying*/\n\t\tSV_QCStatName(ev_entity, \"cameramode\", STAT_H2_CAMERAMODE);\t/*locks view in place when set*/\n\t\tSV_QCStatName(ev_float, \"hasted\", STAT_H2_HASTED);\n\t\tSV_QCStatName(ev_float, \"inventory\", STAT_H2_INVENTORY);\n\t\tSV_QCStatName(ev_float, \"rings_active\", STAT_H2_RINGS_ACTIVE);\n\n\t\tSV_QCStatName(ev_float, \"rings_low\", STAT_H2_RINGS_LOW);\n\t\tSV_QCStatName(ev_float, \"armor_amulet\", STAT_H2_ARMOUR2);\n\t\tSV_QCStatName(ev_float, \"armor_bracer\", STAT_H2_ARMOUR4);\n\t\tSV_QCStatName(ev_float, \"armor_breastplate\", STAT_H2_ARMOUR3);\n\t\tSV_QCStatName(ev_float, \"armor_helmet\", STAT_H2_ARMOUR1);\n\t\tSV_QCStatName(ev_float, \"ring_flight\", STAT_H2_FLIGHT_T);\n\t\tSV_QCStatName(ev_float, \"ring_water\", STAT_H2_WATER_T);\n\t\tSV_QCStatName(ev_float, \"ring_turning\", STAT_H2_TURNING_T);\n\t\tSV_QCStatName(ev_float, \"ring_regeneration\", STAT_H2_REGEN_T);\n\t\tSV_QCStatName(ev_string, \"puzzle_inv1\", STAT_H2_PUZZLE1);\n\t\tSV_QCStatName(ev_string, \"puzzle_inv2\", STAT_H2_PUZZLE2);\n\t\tSV_QCStatName(ev_string, \"puzzle_inv3\", STAT_H2_PUZZLE3);\n\t\tSV_QCStatName(ev_string, \"puzzle_inv4\", STAT_H2_PUZZLE4);\n\t\tSV_QCStatName(ev_string, \"puzzle_inv5\", STAT_H2_PUZZLE5);\n\t\tSV_QCStatName(ev_string, \"puzzle_inv6\", STAT_H2_PUZZLE6);\n\t\tSV_QCStatName(ev_string, \"puzzle_inv7\", STAT_H2_PUZZLE7);\n\t\tSV_QCStatName(ev_string, \"puzzle_inv8\", STAT_H2_PUZZLE8);\n\t\tSV_QCStatName(ev_float, \"max_health\", STAT_H2_MAXHEALTH);\n\t\tSV_QCStatName(ev_float, \"max_mana\", STAT_H2_MAXMANA);\n\t\tSV_QCStatName(ev_float, \"flags\", STAT_H2_FLAGS);\t\t\t\t/*to show the special abilities on the sbar*/\n\n\t\tSV_QCStatPtr(ev_integer, &h2infoplaque[0], STAT_H2_OBJECTIVE1);\n\t\tSV_QCStatPtr(ev_integer, &h2infoplaque[1], STAT_H2_OBJECTIVE2);\n\t}\n#endif\n}\n\nstatic pbool PR_CheckForQEx(pubprogfuncs_t *pf, const char *name, void *ctx)\n{\t//if #90 == centerprint then its the october revision of the rerelease and thus incompatible with everything else.\n\t//(fteextensions specifies it as #0:centerprint_qex instead of #90 so don't worry about that)\n\tif (Q_strcasestr(name, \"centerprint\"))\n\t{\n\t\tsv.world.remasterlogic = true;\n\t\treturn true;\n\t}\n\n\t//something else... like tracebox.\n\tsv.world.remasterlogic = false;\n\treturn false;\t//stop now, so we can't unintentionally set remasterlogic to true. this allows smart mods to target both (favouring our own builtin mappings and effect flags etc).\n}\n\nstatic progsnum_t AddProgs(const char *name)\n{\n\tfloat fl;\n\tfunc_t f;\n\tglobalvars_t *pr_globals;\n\tprogsnum_t num;\n\tflocation_t loc;\n\tchar gamedir[MAX_QPATH];\n\tint i;\n\tif (strlen(name) >= sizeof(svs.progsnames[0]))\n\t\treturn -1;\n\n\tfor (i = 0; i < svs.numprogs; i++)\n\t{\n\t\tif (!strcmp(svs.progsnames[i], name))\n\t\t{\n\t\t\treturn svs.progsnum[i];\n\t\t}\n\t}\n\n\tif (svs.numprogs >= MAX_PROGS)\n\t\treturn -1;\n\n//\tsvprogparms.autocompile = PR_COMPILECHANGED;\n\n\tnum = PR_LoadProgs (svprogfuncs, name);\n\tsv.world.usesolidcorpse = (progstype != PROG_H2);\n\tif (!i && num != -1)\n\t{\n\t\tswitch(progstype)\n\t\t{\n\t\tcase PROG_QW:\n\t\t\tCon_DPrintf(\"Using QW progs\\n\");\n\t\t\tbreak;\n\t\tcase PROG_NQ:\n\t\t\tCon_DPrintf(\"Using NQ progs\\n\");\n\t\t\tbreak;\n\t\tcase PROG_H2:\n\t\t\tCon_DPrintf(\"Using H2 progs\\n\");\n\t\t\tbreak;\n\t\tcase PROG_PREREL:\n\t\t\tCon_DPrintf(\"Using prerelease progs\\n\");\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tCon_DPrintf(\"Using unknown progs\\n\");\n\t\t\tbreak;\n\t\t}\n\t}\n//\tsvprogparms.autocompile = PR_COMPILECHANGED;\n\tif (num == -1)\n\t{\n\t\tCon_Printf(\"Failed to load %s\\n\", name);\n\t\treturn -1;\n\t}\n\n\tif (num == 0)\n\t\tPR_LoadGlabalStruct(false);\n\n\t*gamedir = 0;\n\tif (FS_FLocateFile(name, FSLF_IFFOUND, &loc))\n\t{\n\t\tQ_strncpyz(gamedir, loc.search->purepath, sizeof(gamedir));\n\t\t*COM_SkipPath(gamedir) = 0;\n\t}\n\tCon_DPrintf(\"Loaded progs %s%s\\n\", gamedir, name);\n\n\tPR_ProgsAdded(svprogfuncs, num, name);\n\n\tif (!svs.numprogs)\n\t{\n\t\tif (progstype == PROG_NQ)\n\t\t{\n\t\t\tif (PR_FindFunction(svprogfuncs, \"ex_centerprint\", num) && !PR_FindFunction(svprogfuncs, \"centerprint\", num))\n\t\t\t\tsv.world.remasterlogic = true;\n\t\t\telse\n\t\t\t\tsvprogfuncs->FindBuiltins (svprogfuncs, num, 90, PR_CheckForQEx, NULL);\t//older check\n\t\t}\n\n\t\tPF_InitTempStrings(svprogfuncs);\n\t\tPR_ResetBuiltins(progstype);\n\t}\n\n\tfor (i = 0; i < countof(buttonfields); i++)\n\t{\n\t\teval_t *val = svprogfuncs->GetEdictFieldValue(svprogfuncs, (edict_t*)sv.world.edicts, buttonfields[i].fieldname, ev_float, NULL);\n\t\tif (val)\n\t\t\tbuttonfields[i].offset = (float*)val - (float*)sv.world.edicts->v;\n\t\telse\n\t\t\tbuttonfields[i].offset = -1;\n\t}\n\n\tif ((f = PR_FindFunction (svprogfuncs, \"VersionChat\", num )))\n\t{\n\t\tpr_globals = PR_globals(svprogfuncs, num);\n\t\tG_FLOAT(OFS_PARM0) = version_number();\n\t\tPR_ExecuteProgram (svprogfuncs, f);\n\n\t\tfl = G_FLOAT(OFS_RETURN);\n\t\tif (fl < 0)\n\t\t\tSV_Error (\"PR_LoadProgs: progs.dat is not compatible with EXE version\");\n\t\telse if ((int) (fl) != (int) (version_number()))\n\t\t\tCon_DPrintf(\"Warning: Progs may not be fully compatible\\n (%4.2f != %i)\\n\", fl, version_number());\n\t}\n\n\tif ((f = PR_FindFunction (svprogfuncs, \"FTE_init\", num )))\n\t{\n\t\tpr_globals = PR_globals(svprogfuncs, num);\n\t\tG_FLOAT(OFS_PARM0) = version_number();\n\t\tPR_ExecuteProgram (svprogfuncs, f);\n\t}\n\n\n\tstrcpy(svs.progsnames[svs.numprogs], name);\n\tsvs.progsnum[svs.numprogs] = num;\n\tsvs.numprogs++;\n\n\treturn num;\n}\n\nstatic void PR_Decompile_f(void)\n{\n\tif (!svprogfuncs)\n\t{\n\t\tQ_SetProgsParms(false);\n\t\tPR_Configure(svprogfuncs, PR_ReadBytesString(pr_ssqc_memsize.string), MAX_PROGS, 0);\n\t}\n\n\n\tif (Cmd_Argc() == 1)\n\t\tsvprogfuncs->Decompile(svprogfuncs, \"qwprogs.dat\");\n\telse\n\t\tsvprogfuncs->Decompile(svprogfuncs, Cmd_Argv(1));\n}\nstatic void PR_Compile_f(void)\n{\n\tqboolean killondone = false;\n\tint argc=3;\n\tdouble time = Sys_DoubleTime();\n\tconst char *argv[64] = {\"\", \"-src\", pr_sourcedir.string, \"-srcfile\", \"progs.src\"};\n\n\tif (Cmd_Argc()>2)\n\t{\n\t\tfor (argc = 0; argc < Cmd_Argc(); argc++)\n\t\t\targv[argc] = Cmd_Argv(argc);\n\t}\n\telse\n\t{\n\t\t//override the source name\n\t\tif (Cmd_Argc() == 2)\n\t\t{\n\t\t\targv[4] = Cmd_Argv(1);\n\t\t\targc = 5;\n\t\t}\n\t\tif (!FS_FLocateFile(va(\"%s/%s\", argv[2], argv[4]), FSLF_IFFOUND, NULL))\n\t\t{\n\t\t\t//try the qc path\n\t\t\targv[2] = \"qc\";\n\t\t}\n\t\tif (!FS_FLocateFile(va(\"%s/%s\", argv[2], argv[4]), FSLF_IFFOUND, NULL))\n\t\t{\n\t\t\t//try the progs path (yeah... gah)\n\t\t\targv[2] = \"progs\";\n\t\t}\n\t\tif (!FS_FLocateFile(va(\"%s/%s\", argv[2], argv[4]), FSLF_IFFOUND, NULL))\n\t\t{\n\t\t\t//try the gamedir path\n\t\t\targv[1] = argv[3];\n\t\t\targv[2] = argv[4];\n\t\t\targc -= 2;\n\t\t}\n\t}\n\n\tif (!svprogfuncs)\n\t{\n\t\tQ_SetProgsParms(true);\n\t\tkillondone = true;\n\t}\n\tif (svprogfuncs->StartCompile && PR_StartCompile(svprogfuncs, argc, argv))\n\t\twhile(PR_ContinueCompile(svprogfuncs));\n\n\tif (killondone)\n\t\tPR_Deinit();\n\n\ttime = Sys_DoubleTime() - time;\n\n\tCon_TPrintf(\"Compile took %f secs\\n\", time);\n}\n\nstatic void PR_ApplyCompilation_f (void)\n{\n\tedict_t *ent;\n\tchar *s;\n\tsize_t len;\n\tint i;\n\tif (sv.state < ss_active)\n\t{\n\t\tCon_Printf(\"Can't apply: Server isn't running or is still loading\\n\");\n\t\treturn;\n\t}\n\n\tCon_Printf(\"Saving state\\n\");\n\ts = PR_SaveEnts(svprogfuncs, NULL, &len, 0, 1);\n\n\n\tPR_Configure(svprogfuncs, PR_ReadBytesString(pr_ssqc_memsize.string), MAX_PROGS, pr_enable_profiling.ival);\n\tPR_RegisterFields();\n\tsv.world.edict_size=PR_InitEnts(svprogfuncs, sv.world.max_edicts);\n\n\tsv.world.edict_size=svprogfuncs->load_ents(svprogfuncs, s, NULL, NULL, NULL, NULL);\n\n\n\tPR_LoadGlabalStruct(false);\n\n\tpr_global_struct->time = sv.world.physicstime;\n\n\tfor (i=0 ; i<sv.allocated_client_slots ; i++)\n\t{\n\t\tent = EDICT_NUM_PB(svprogfuncs, i+1);\n\n\t\tsvs.clients[i].edict = ent;\n\t}\n\n\tWorld_ClearWorld (&sv.world, true);\n\n\tsvprogfuncs->parms->memfree(s);\n}\n\nstatic void PR_BreakPoint_f(void)\n{\n\tint wasset;\n\tint isset;\n\tchar *filename = Cmd_Argv(1);\n\tint line = atoi(Cmd_Argv(2));\n\n\tif (!svprogfuncs)\n\t{\n\t\tCon_Printf(\"Start the server first\\n\");\n\t\treturn;\n\t}\n\twasset = svprogfuncs->ToggleBreak(svprogfuncs, filename, line, 3);\n\tisset = svprogfuncs->ToggleBreak(svprogfuncs, filename, line, 2);\n\n\tif (wasset == isset)\n\t\tCon_Printf(\"Breakpoint was not valid\\n\");\n\telse if (isset)\n\t\tCon_Printf(\"Breakpoint has been set\\n\");\n\telse\n\t\tCon_Printf(\"Breakpoint has been cleared\\n\");\n\n//\tCvar_Set(Cvar_FindVar(\"debugger\"), \"1\");\n}\nstatic void PR_WatchPoint_f(void)\n{\n\tchar *variable = Cmd_Argv(1);\n\tint oldself;\n\tif (!*variable)\n\t\tvariable = NULL;\n\n\tif (!svprogfuncs)\n\t{\n\t\tCon_Printf(\"Start the server first\\n\");\n\t\treturn;\n\t}\n\toldself = pr_global_struct->self;\n\tif (oldself == 0)\n\t{\t//if self is world, set it to something sensible.\n\t\tint i;\n\t\tfor (i = 0; i < sv.allocated_client_slots; i++)\n\t\t{\n\t\t\tif (svs.clients[i].state && svs.clients[i].netchan.remote_address.type == NA_LOOPBACK)\n\t\t\t{\n\t\t\t\t//always use first local client, if available.\n\t\t\t\tpr_global_struct->self = EDICT_TO_PROG(svprogfuncs, svs.clients[i].edict);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\t//failing that, just use the first client.\n\t\t\tif (svs.clients[i].state == cs_spawned && !pr_global_struct->self)\n\t\t\t\tpr_global_struct->self = EDICT_TO_PROG(svprogfuncs, svs.clients[i].edict);\n\t\t}\n\t}\n\tif (svprogfuncs->SetWatchPoint(svprogfuncs, variable, variable))\n\t\tCon_Printf(\"Watchpoint set\\n\");\n\telse\n\t\tCon_Printf(\"Watchpoint cleared\\n\");\n\tpr_global_struct->self = oldself;\n\n//\tCvar_Set(Cvar_FindVar(\"debugger\"), \"1\");\n}\n\nstatic void PR_SSProfile_f(void)\n{\n\tif (svprogfuncs && svprogfuncs->DumpProfile)\n\t\tif (!svprogfuncs->DumpProfile(svprogfuncs, !atof(Cmd_Argv(1))))\n\t\t\tCon_Printf(\"Enabled ssqc profiling. Re-execute %s to see the results.\\n\", Cmd_Argv(0));\n}\n\nstatic void PR_SSPoke_f(void)\n{\n\tif (MSV_ForwardToAutoServer())\n\t\t;\n\telse if (!SV_MayCheat())\n\t\tCon_TPrintf (\"Please set sv_cheats 1 and restart the map first.\\n\");\n\telse if (svprogfuncs && svprogfuncs->EvaluateDebugString)\n\t\tCon_TPrintf(\"Result: %s\\n\", svprogfuncs->EvaluateDebugString(svprogfuncs, Cmd_Args()));\n\telse\n\t\tCon_TPrintf (\"not supported.\\n\");\n}\n\nstatic void PR_SSCoreDump_f(void)\n{\n\tif (!svprogfuncs)\n\t{\n\t\tCon_Printf(\"Progs not running, you need to start a server first\\n\");\n\t\treturn;\n\t}\n\n\t{\n\t\tsize_t size = 1024*1024*8;\n\t\tchar *buffer = BZ_Malloc(size);\n\t\tsvprogfuncs->save_ents(svprogfuncs, buffer, &size, size, 3);\n\t\tCOM_WriteFile(\"ssqccore.txt\", FS_GAMEONLY, buffer, size);\n\t\tBZ_Free(buffer);\n\t}\n}\n\nstatic void PR_SVExtensionList_f(void);\n\n/*\n#ifdef _DEBUG\nvoid QCLibTest(void)\n{\n\tint size = 1024*1024*8;\n\tchar *buffer = BZ_Malloc(size);\n\tsvprogfuncs->save_ents(svprogfuncs, buffer, &size, 3);\n\tCOM_WriteFile(\"ssqccore.txt\", buffer, size);\n\tBZ_Free(buffer);\n\n\tPR_TestForWierdness(svprogfuncs);\n}\n#endif\n*/\ntypedef char char32[32];\nstatic char32 sv_addonname[MAXADDONS];\nvoid PR_Init(void)\n{\n\tint i;\n\n\tCmd_AddCommand (\"breakpoint\", PR_BreakPoint_f);\n\tCmd_AddCommand (\"watchpoint\", PR_WatchPoint_f);\n\tCmd_AddCommand (\"watchpoint_ssqc\", PR_WatchPoint_f);\n\tCmd_AddCommand (\"decompile\", PR_Decompile_f);\n\tCmd_AddCommand (\"compile\", PR_Compile_f);\n\tCmd_AddCommand (\"applycompile\", PR_ApplyCompilation_f);\n\tCmd_AddCommand (\"coredump_ssqc\", PR_SSCoreDump_f);\n\tCmd_AddCommand (\"poke_ssqc\", PR_SSPoke_f);\n\tCmd_AddCommandD (\"profile_ssqc\", PR_SSProfile_f, \"Displays how much time has been spent in various QC functions since this command was last used.\\nIf pr_enable_profiling is set, profiling will be enabled automatically, and can be used to list spawn functions.\\nAdd an arg with value 1 if you wish to avoid purging timing information.\");\n\n\tCmd_AddCommand (\"extensionlist_ssqc\", PR_SVExtensionList_f);\n\tCmd_AddCommand (\"pr_dumpplatform\", PR_DumpPlatform_f);\n\n\tCmd_AddCommandD (\"sv_lightstyle\", PR_Lightstyle_f, \"Overrides lightstyles from the server's console, mostly for debug use.\");\n\n/*\n#ifdef _DEBUG\n\tCmd_AddCommand (\"svtestprogs\", QCLibTest);\n#endif\n*/\n\tCvar_Register(&pr_imitatemvdsv, cvargroup_progs);\n\n\tCvar_Register(&pr_maxedicts, cvargroup_progs);\n\tCvar_Register(&pr_no_playerphysics, cvargroup_progs);\n\tCvar_Register(&pr_no_parsecommand, cvargroup_progs);\n\n\tfor (i = 0; i < MAXADDONS; i++)\n\t{\n\t\tsprintf(sv_addonname[i], \"addon%i\", i);\n\t\tsv_addon[i].name = sv_addonname[i];\n\t\tsv_addon[i].string = \"\";\n\t\tsv_addon[i].flags = CVAR_NOTFROMSERVER;\n\t\tCvar_Register(&sv_addon[i], cvargroup_progs);\n\t}\n\n\tCvar_Register (&nomonsters, cvargroup_progs);\n\tCvar_Register (&gamecfg, cvargroup_progs);\n\tCvar_Register (&scratch1, cvargroup_progs);\n\tCvar_Register (&scratch2, cvargroup_progs);\n\tCvar_Register (&scratch3, cvargroup_progs);\n\tCvar_Register (&scratch4, cvargroup_progs);\n\tCvar_Register (&savedgamecfg, cvargroup_progs);\n\tCvar_Register (&saved1, cvargroup_progs);\n\tCvar_Register (&saved2, cvargroup_progs);\n\tCvar_Register (&saved3, cvargroup_progs);\n\tCvar_Register (&saved4, cvargroup_progs);\n\tCvar_Register (&temp1, cvargroup_progs);\n\tCvar_Register (&noexit, cvargroup_progs);\n\n\tCvar_Register (&pr_ssqc_progs, cvargroup_progs);\n\tCvar_Register (&pr_compatabilitytest, cvargroup_progs);\n\n\tCvar_Register (&dpcompat_precachesoundhack, cvargroup_progs);\n\tCvar_Register (&dpcompat_nopreparse, cvargroup_progs);\n\tCvar_Register (&pr_nonetaccess, cvargroup_progs);\n\tCvar_Register (&pr_overridebuiltins, cvargroup_progs);\n\n\tCvar_Register (&pr_ssqc_coreonerror, cvargroup_progs);\n\tCvar_Register (&pr_ssqc_memsize, cvargroup_progs);\n\n\tCvar_Register (&sv_gameplayfix_honest_tracelines, cvargroup_progs);\n\tCvar_Register (&sv_gameplayfix_setmodelrealbox, cvargroup_progs);\n\tCvar_Register (&sv_gameplayfix_setmodelsize_qw, cvargroup_progs);\n\n#ifdef SQL\n\tSQL_Init();\n#endif\n\n\t{\t//hide this extension, because AD maps often fail to signal that its not needed, resulting in horrendous stalls when people fire shotguns.\n\t\tstatic cvar_t var = CVARFD(\"pr_ext_dp_qc_getsurface\", \"\", CVAR_ARCHIVE, \"Set to 0 to block detection of the extension\");\n\t\tCvar_Register (&var, \"QC Extensions\");\n\t}\n}\n\nvoid SVQ1_CvarChanged(cvar_t *var)\n{\n\tif (svprogfuncs)\n\t{\n\t\tPR_AutoCvar(svprogfuncs, var);\n\t}\n}\n\nstatic void QCBUILTIN PF_precache_model (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nstatic void QCBUILTIN PF_setmodel (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nvoid QCBUILTIN PF_makestatic (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\nstatic void PR_FallbackSpawn_Misc_Model(pubprogfuncs_t *progfuncs, edict_t *self, qboolean force)\n{\n\tvoid *pr_globals;\n\teval_t *val;\n\n\tif (sv.world.worldmodel && sv.world.worldmodel->type==mod_brush && sv.world.worldmodel->fromgame == fg_quake3 && !force)\n\t{\t//on q3bsp, these are expected to be handled directly by q3map2, but it doesn't always strip it.\n\t\tED_Free(progfuncs, self);\n\t\treturn;\n\t}\n\n\tif (!self->v->model && (val = progfuncs->GetEdictFieldValue(progfuncs, self, \"mdl\", ev_string, NULL)))\n\t\tself->v->model = val->string;\n\tif (!*PR_GetString(progfuncs, self->v->model)) //must have a model, because otherwise various things will assume its not valid at all.\n\t\tprogfuncs->SetStringField(progfuncs, self, &self->v->model, \"*null\", true);\n\n\tif (self->v->angles[1] < 0)\t//mimic AD. shame there's no avelocity clientside.\n\t\tself->v->angles[1] = (rand()*(360.0/RAND_MAX));\n\n\t//make sure the model is precached, to avoid errors.\n\tpr_globals = PR_globals(progfuncs, PR_CURRENT);\n\tG_INT(OFS_PARM0) = self->v->model;\n\tPF_precache_model(progfuncs, pr_globals);\n\n\tpr_globals = PR_globals(progfuncs, PR_CURRENT);\n\tG_INT(OFS_PARM0) = EDICT_TO_PROG(progfuncs, self);\n\tG_INT(OFS_PARM1) = self->v->model;\n\tPF_setmodel(progfuncs, pr_globals);\n\n\t//and lets just call makestatic instead of worrying if it'll interfere with the rest of the qc.\n\tpr_globals = PR_globals(progfuncs, PR_CURRENT);\n\tG_INT(OFS_PARM0) = EDICT_TO_PROG(progfuncs, self);\n\tPF_makestatic(progfuncs, pr_globals);\n}\nstatic void PR_FallbackSpawn_Func_Detail(pubprogfuncs_t *progfuncs, edict_t *self)\n{\n\tvoid *pr_globals;\n\teval_t *val;\n\n\tif (!self->v->model && (val = progfuncs->GetEdictFieldValue(progfuncs, self, \"mdl\", ev_string, NULL)))\n\t\tself->v->model = val->string;\n\tif (!*PR_GetString(progfuncs, self->v->model)) //must have a model, because otherwise various things will assume its not valid at all.\n\t\tprogfuncs->SetStringField(progfuncs, self, &self->v->model, \"*null\", true);\n\n\tself->v->solid = SOLID_BSP;\n\tself->v->movetype = MOVETYPE_PUSH;\n\n\t//make sure the model is precached, to avoid errors.\n\tpr_globals = PR_globals(progfuncs, PR_CURRENT);\n\tG_INT(OFS_PARM0) = self->v->model;\n\tPF_precache_model(progfuncs, pr_globals);\n\n\tpr_globals = PR_globals(progfuncs, PR_CURRENT);\n\tG_INT(OFS_PARM0) = EDICT_TO_PROG(progfuncs, self);\n\tG_INT(OFS_PARM1) = self->v->model;\n\tPF_setmodel(progfuncs, pr_globals);\n}\nstruct spawnents_s\n{\n\tint killonspawnflags;\n\teval_t *fulldata;\n\tqboolean foundfuncs;\n\tfunc_t CheckSpawn;\t//fte's alternative to spawn functions.\n\tfunc_t SV_OnEntityPreSpawnFunction;\t\t//dp's more clumsy crap\n\tfunc_t SV_OnEntityNoSpawnFunction;\t//dp's more clumsy crap\n\tfunc_t SV_OnEntityPostSpawnFunction;\t//dp's more clumsy crap\n\tconst char *spawnwarned[32];\n};\nstatic void PDECL PR_DoSpawnInitialEntity(pubprogfuncs_t *progfuncs, struct edict_s *ed, void *vctx, const char *start, const char *end)\n{\n\tstruct spawnents_s *ctx = vctx;\n\tconst char *eclassname;\n\tfunc_t f;\n\tchar spawnfuncname[256];\n\n\tif (!ctx->foundfuncs)\n\t{\n\t\tctx->foundfuncs = true;\n\t\tctx->CheckSpawn = PR_FindFunction(progfuncs, \"CheckSpawn\", -2);\n\t\tctx->SV_OnEntityPreSpawnFunction = PR_FindFunction(progfuncs, \"SV_OnEntityPreSpawnFunction\", -2);\n\t\tctx->SV_OnEntityNoSpawnFunction = PR_FindFunction(progfuncs, \"SV_OnEntityNoSpawnFunction\", -2);\n\t\tctx->SV_OnEntityPostSpawnFunction = PR_FindFunction(progfuncs, \"SV_OnEntityPostSpawnFunction\", -2);\n\t}\n\n\t//remove the entity if its spawnflags matches the ones we're killing on\n\t//we skip this check if the mod has the CheckSpawn function defined. Such mods can do their own filtering easily enough.\n\t//dpcompat: SV_OnEntityPreSpawnFunction does not inhibit this legacy behaviour (even though it really ought to).\n\tif (!ctx->CheckSpawn && (int)ed->v->spawnflags & ctx->killonspawnflags)\n\t{\n\t\tED_Free(progfuncs, (struct edict_s *)ed);\n\t\treturn;\n\t}\n\n\tif (ctx->SV_OnEntityPreSpawnFunction)\n\t{\n//\t\tvoid *pr_globals = PR_globals(progfuncs, PR_CURRENT);\n\t\t*sv.world.g.self = EDICT_TO_PROG(progfuncs, ed);\n\t\tPR_ExecuteProgram(progfuncs, ctx->SV_OnEntityPreSpawnFunction);\n\t\tif (ed->ereftype == ER_FREE)\n\t\t\treturn;\n\t}\n\n\teclassname = PR_GetString(progfuncs, ed->v->classname);\n\tif (!*eclassname)\n\t{\n\t\tCon_Printf(\"No classname\\n\");\n\t\tED_Free(progfuncs, ed);\n\t}\n\telse\n\t{\n\t\t//added by request of Mercury.\n\t\tif (ctx->fulldata)\t//this is a vital part of HL map support!!!\n\t\t{\t//essentually, it passes the ent's spawn info to the ent.\n\t\t\tchar *spawndata;//a standard quake ent.\n#ifdef QCGC\n\t\t\tconst char *in;\n\t\t\tctx->fulldata->string = progfuncs->AllocTempString(progfuncs, &spawndata, end-start+1);\n\t\t\tfor (in = start; in < end; )\n\t\t\t{\n\t\t\t\tchar c = *in++;\n\t\t\t\tif (c == '\\n')\n\t\t\t\t\t*spawndata++ = '\\t';\n\t\t\t\telse\n\t\t\t\t\t*spawndata++ = c;\n\t\t\t}\n\t\t\t*spawndata = 0;\n#else\n\t\t\tchar *nl;\t//otherwise it sees only the named fields of\n\t\t\tspawndata = PRHunkAlloc(progfuncs, file - datastart +1, \"fullspawndata\");\n\t\t\tstrncpy(spawndata, datastart, file - datastart);\n\t\t\tspawndata[file - datastart] = '\\0';\n\t\t\tfor (nl = spawndata; *nl; nl++)\n\t\t\t\tif (*nl == '\\n')\n\t\t\t\t\t*nl = '\\t';\n\t\t\tctx->fulldata->string = PR_StringToProgs(&progfuncs->funcs, spawndata);\n#endif\n\t\t}\n\n\t\t*sv.world.g.self = EDICT_TO_PROG(progfuncs, ed);\n\n\t\t//DP_SV_SPAWNFUNC_PREFIX support\n\t\tQ_snprintfz(spawnfuncname, sizeof(spawnfuncname), \"spawnfunc_%s\", eclassname);\n\t\tf = PR_FindFunction(progfuncs, spawnfuncname, PR_ANYBACK);\n\t\tif (!f)\n\t\t\tf = PR_FindFunction(progfuncs, eclassname, PR_ANYBACK);\n\t\tif (!f)\n\t\t\tf = ctx->SV_OnEntityNoSpawnFunction;\n\t\tif (f)\n\t\t{\n\t\t\tif (ctx->CheckSpawn)\n\t\t\t{\n\t\t\t\tvoid *pr_globals = PR_globals(progfuncs, PR_CURRENT);\n\t\t\t\tG_INT(OFS_PARM0) = f;\n\t\t\t\tPR_ExecuteProgram(progfuncs, ctx->CheckSpawn);\n\t\t\t\t//call the spawn func or remove.\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (developer.value)\n\t\t\t\t{\n\t\t\t\t\tint argcount;\n\t\t\t\t\tprogfuncs->GetFunctionInfo(progfuncs, f, &argcount, NULL, NULL, spawnfuncname, sizeof(spawnfuncname));\n\t\t\t\t\tif (argcount != 0)\n\t\t\t\t\t\tCon_Printf(\"Spawn function %s defined with unsatisfied arguments\\n\", spawnfuncname);\n\t\t\t\t}\n\t\t\t\tPR_ExecuteProgram(progfuncs, f);\n\t\t\t}\n\t\t}\n\t\telse if (ctx->CheckSpawn)\n\t\t{\n\t\t\tvoid *pr_globals = PR_globals(progfuncs, PR_CURRENT);\n\t\t\tG_INT(OFS_PARM0) = 0;\n\t\t\tPR_ExecuteProgram(progfuncs, ctx->CheckSpawn);\n\t\t\t//the mod is responsible for freeing unrecognised ents.\n\t\t}\n\t\telse if (!strcmp(eclassname, \"misc_model\"))\n\t\t\tPR_FallbackSpawn_Misc_Model(progfuncs, ed, false);\n\t\t//func_detail+func_group are for compat with ericw-tools, etc.\n\t\telse if (!strcmp(eclassname, \"func_detail_illusionary\"))\n\t\t\tPR_FallbackSpawn_Misc_Model(progfuncs, ed, true);\n\t\telse if (!strcmp(eclassname, \"func_detail\") || !strcmp(eclassname, \"func_detail_wall\") || !strcmp(eclassname, \"func_detail_fence\"))\n\t\t\tPR_FallbackSpawn_Func_Detail(progfuncs, ed);\n\t\telse if (!strcmp(eclassname, \"func_group\"))\n\t\t\tPR_FallbackSpawn_Func_Detail(progfuncs, ed);\n\t\telse\n\t\t{\n\t\t\t//only warn on the first occurence of the classname, don't spam.\n\t\t\tint i;\n\t\t\tif (svs.numprogs)\n\t\t\t\tfor (i = 0; i < countof(ctx->spawnwarned); i++)\n\t\t\t\t{\n\t\t\t\t\tif (!ctx->spawnwarned[i])\n\t\t\t\t\t{\n\t\t\t\t\t\tCon_Printf(\"Couldn't find spawn function for %s\\n\", eclassname);\n\t\t\t\t\t\tctx->spawnwarned[i] = eclassname;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\telse if (!strcmp(ctx->spawnwarned[i], eclassname))\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\tED_Free(progfuncs, ed);\n\t\t}\n\t}\n\n\tif (ctx->SV_OnEntityPostSpawnFunction)\n\t{\n//\t\tvoid *pr_globals = PR_globals(progfuncs, PR_CURRENT);\n\t\tif (ed->ereftype == ER_FREE)\n\t\t\treturn;\n\t\t*sv.world.g.self = EDICT_TO_PROG(progfuncs, ed);\n\t\tPR_ExecuteProgram(progfuncs, ctx->SV_OnEntityPreSpawnFunction);\n\t}\n}\nvoid PR_SpawnInitialEntities(const char *file)\n{\n\textern cvar_t skill;\n\tstruct spawnents_s ctx;\n\tmemset(&ctx, 0, sizeof(ctx));\n\n#ifdef HEXEN2\n\tif (progstype == PROG_H2)\n\t{\n\t\textern cvar_t coop;\n\t\tif (deathmatch.value)\n\t\t\tctx.killonspawnflags = SPAWNFLAG_NOT_H2DEATHMATCH;\n\t\telse if (coop.value)\n\t\t\tctx.killonspawnflags = SPAWNFLAG_NOT_H2COOP;\n\t\telse\n\t\t{\n\t\t\tcvar_t *cl_playerclass = Cvar_Get(\"cl_playerclass\", \"0\", CVAR_USERINFO, 0);\n\t\t\tctx.killonspawnflags = SPAWNFLAG_NOT_H2SINGLE;\n\n\t\t\tif (cl_playerclass && cl_playerclass->ival == 1)\n\t\t\t\tctx.killonspawnflags |= SPAWNFLAG_NOT_H2PALADIN;\n\t\t\telse if (cl_playerclass && cl_playerclass->ival == 2)\n\t\t\t\tctx.killonspawnflags |= SPAWNFLAG_NOT_H2CLERIC;\n\t\t\telse if (cl_playerclass && cl_playerclass->ival == 3)\n\t\t\t\tctx.killonspawnflags |= SPAWNFLAG_NOT_H2NECROMANCER;\n\t\t\telse if (cl_playerclass && cl_playerclass->ival == 4)\n\t\t\t\tctx.killonspawnflags |= SPAWNFLAG_NOT_H2THEIF;\n\t\t\telse if (cl_playerclass && cl_playerclass->ival == 5)\n\t\t\t\tctx.killonspawnflags |= SPAWNFLAG_NOT_H2NECROMANCER;\t/*yes, I know.,. makes no sense*/\n\t\t}\n\t\tif (skill.value < 0.5)\n\t\t\tctx.killonspawnflags |= SPAWNFLAG_NOT_H2EASY;\n\t\telse if (skill.value > 1.5)\n\t\t\tctx.killonspawnflags |= SPAWNFLAG_NOT_H2HARD;\n\t\telse\n\t\t\tctx.killonspawnflags |= SPAWNFLAG_NOT_H2MEDIUM;\n\n\t\t//don't filter based on player class. we're lame and don't have any real concept of player classes.\n\t}\n\telse\n#endif\n\t\tif (!deathmatch.value)\t//decide if we are to inhibit single player game ents instead\n\t{\n\t\tif (skill.value < 0.5)\n\t\t\tctx.killonspawnflags = SPAWNFLAG_NOT_EASY;\n\t\telse if (skill.value > 1.5)\n\t\t\tctx.killonspawnflags = SPAWNFLAG_NOT_HARD;\n\t\telse\n\t\t\tctx.killonspawnflags = SPAWNFLAG_NOT_MEDIUM;\n\t}\n\telse\n\t\tctx.killonspawnflags = SPAWNFLAG_NOT_DEATHMATCH;\n\n\tctx.fulldata = PR_FindGlobal(svprogfuncs, \"__fullspawndata\", PR_ANY, NULL);\n\n\tif (svprogfuncs)\n\t\tsv.world.edict_size = PR_LoadEnts(svprogfuncs, file, &ctx, NULL, PR_DoSpawnInitialEntity, NULL);\n\telse\n\t\tsv.world.edict_size = 0;\n}\n\nvoid SV_RegisterH2CustomTents(void);\nvoid Q_InitProgs(enum initprogs_e flags)\n{\n\tint i, i2;\n\tfunc_t f, f2;\n\tglobalvars_t *pr_globals;\n\tstatic char addons[2048];\n\tchar *as, *a;\n\tprogsnum_t prnum, oldprnum=-1;\n\tint d1, d2;\n\n\tssqc_deprecated_warned = false;\n\n\tQC_Clear();\n\n\tQ_SetProgsParms(false);\n\n\tmemset(pr_builtin, 0, sizeof(pr_builtin));\n\n// load progs to get entity field count\n\tPR_Configure(svprogfuncs, PR_ReadBytesString(pr_ssqc_memsize.string), MAX_PROGS, pr_enable_profiling.ival);\n\n\tPR_RegisterFields();\n\n\tsvs.numprogs=0;\n\n\tif (flags & INITPROGS_EDITOR)\n\t{\n\t\toldprnum = AddProgs(\"sseditor.dat\");\n\t\tPR_LoadGlabalStruct(true);\n\t}\n\telse\n\t{\n\t\td1 = FS_FLocateFile(\"progs.dat\",\tFSLF_DONTREFERENCE|FSLF_DEEPONFAILURE|FSLF_IGNOREBASEDEPTH, NULL);\n\t\td2 = FS_FLocateFile(\"qwprogs.dat\",\tFSLF_DONTREFERENCE|FSLF_DEEPONFAILURE|FSLF_IGNOREBASEDEPTH, NULL);\n\t\t//FIXME id1/progs.dat vs qw/qwprogs.dat - these should be considered to have the same priority.\n\t\tif (d1 < d2)\t//progs.dat is closer to the gamedir\n\t\t\tstrcpy(addons, \"progs.dat\");\n\t\telse if (d1 > d2)\t//qwprogs.dat is closest\n\t\t{\n\t\t\tstrcpy(addons, \"qwprogs.dat\");\n\t\t\td1 = d2;\n\t\t}\n\t\t//both are an equal depth - same path.\n\t\telse if (deathmatch.value && !COM_CheckParm(\"-game\"))\t//if deathmatch, default to qw\n\t\t{\n\t\t\tstrcpy(addons, \"qwprogs.dat\");\n\t\t\td1 = d2;\n\t\t}\n\t\telse\t\t\t\t\t//single player/coop is better done with nq.\n\t\t{\n\t\t\tstrcpy(addons, \"progs.dat\");\n\t\t}\n\t\t\t\t\t\t\t\t//if progs cvar is left blank and a q2 map is loaded, the server will use the q2 game dll.\n\t\t\t\t\t\t\t\t//if you do set a value here, q2 dll is not used.\n\n\n\t\t//hexen2 - maplist contains a list of maps that we need to use an alternate progs.dat for.\n\t\td1 = COM_FDepthFile(addons, true);\n\t\td2 = COM_FDepthFile(\"maplist.txt\", true);\n\t\tif (d2 <= d1)//Use it if the maplist.txt file is within a more or equal important gamedir.\n\t\t{\n\t\t\tint j, maps;\n\t\t\tchar *f;\n\n\t\t\tf = COM_LoadTempFile(\"maplist.txt\", 0, NULL);\n\t\t\tf = COM_Parse(f);\n\t\t\tmaps = atoi(com_token);\n\t\t\tfor (j = 0; j < maps; j++)\n\t\t\t{\n\t\t\t\tf = COM_Parse(f);\n\t\t\t\tif (!Q_strcasecmp(svs.name, com_token))\n\t\t\t\t{\n\t\t\t\t\tf = COM_Parse(f);\n\t\t\t\t\tstrcpy(addons, com_token);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tf = strchr(f, '\\n');\t//skip to the end of the line.\n\t\t\t}\n\t\t}\n\n\t\t/*if pr_ssqc_progs cvar is set, override the default*/\n\t\tif (*pr_ssqc_progs.string && strlen(pr_ssqc_progs.string)<64 && *pr_ssqc_progs.string != '*')\t//a * is a special case to not load a q2 dll.\n\t\t{\n\t\t\tQ_strncpyz(addons, pr_ssqc_progs.string, MAX_QPATH);\n\t\t\tCOM_DefaultExtension(addons, \".dat\", sizeof(addons));\n\t\t}\n\t\toldprnum= AddProgs(addons);\n\n\t\t/*try to load qwprogs.dat if we didn't manage to load one yet*/\n\t\tif (oldprnum < 0 && strcmp(addons, \"qwprogs.dat\"))\n\t\t{\n#ifndef SERVERONLY\n\t\t\tif (SCR_UpdateScreen)\n\t\t\t\tSCR_UpdateScreen();\n#endif\n\t\t\toldprnum= AddProgs(\"qwprogs.dat\");\n\t\t}\n\n\t\t/*try to load qwprogs.dat if we didn't manage to load one yet*/\n\t\tif (oldprnum < 0 && strcmp(addons, \"progs.dat\"))\n\t\t{\n#ifndef SERVERONLY\n\t\t\tif (SCR_UpdateScreen)\n\t\t\t\tSCR_UpdateScreen();\n#endif\n\t\t\toldprnum= AddProgs(\"progs.dat\");\n\t\t}\n\n\t\tif (oldprnum < 0)\n\t\t{\n\t\t\tPR_LoadGlabalStruct(true);\n\t\t\tif (flags & INITPROGS_REQUIRE)\n\t\t\t\tSV_Error(\"No gamecode available. Try using the downloads menu.\\n\");\n\t\t\tCon_Printf(CON_ERROR\"Running without gamecode\\n\");\n\t\t}\n\n\t\tif (oldprnum >= 0)\n\t\t\tf = PR_FindFunction (svprogfuncs, \"AddAddonProgs\", oldprnum);\n\t\telse\n\t\t\tf = 0;\n\t/*\tif (num)\n\t\t{\n\t\t\t//restore progs\n\t\t\tfor (i = 1; i < num; i++)\n\t\t\t{\n\t\t\t\tif (f)\n\t\t\t\t{\n\t\t\t\t\tpr_globals = PR_globals(PR_CURRENT);\n\t\t\t\t\tG_SETSTRING(OFS_PARM0, svs.progsnames[i]);\n\t\t\t\t\tPR_ExecuteProgram (f);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tprnum = AddProgs(svs.progsnames[i]);\n\t\t\t\t\tf2 = PR_FindFunction ( \"init\", prnum);\n\n\t\t\t\t\tif (f2)\n\t\t\t\t\t{\n\t\t\t\t\t\tpr_globals = PR_globals(PR_CURRENT);\n\t\t\t\t\t\tG_PROG(OFS_PARM0) = oldprnum;\n\t\t\t\t\t\tPR_ExecuteProgram(f2);\n\t\t\t\t\t}\n\t\t\t\t\toldprnum=prnum;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t*/\n\t\t//additional (always) progs\n\t\tas = NULL;\n\t\ta = COM_LoadStackFile(\"mod.gam\", addons, 2048, NULL);\n\n\t\tif (a)\n\t\t{\n\t\t\tif (progstype == PROG_QW)\n\t\t\t\tas = strstr(a, \"extraqwprogs=\");\n\t\t\telse\n\t\t\t\tas = strstr(a, \"extraprogs=\");\n\t\t\tif (as)\n\t\t\t{\n\t\t\tfor (a = as+13; *a; a++)\n\t\t\t{\n\t\t\t\tif (*a < ' ')\n\t\t\t\t{\n\t\t\t\t\t*a = '\\0';\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\ta = (as+=13);\n\t\t\t}\n\t\t}\n\t\tif (as)\n\t\t{\n\t\t\twhile(*a)\n\t\t\t{\n\t\t\t\tif (*a == ';')\n\t\t\t\t{\n\t\t\t\t\t*a = '\\0';\n\t\t\t\t\tfor (i = 0; i < svs.numprogs; i++)\t//don't add if already added\n\t\t\t\t\t{\n\t\t\t\t\t\tif (!strcmp(svs.progsnames[i], as))\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tif (i == svs.numprogs)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (f)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tpr_globals = PR_globals(svprogfuncs, PR_CURRENT);\n\t\t\t\t\t\t\tG_INT(OFS_PARM0) = (int)PR_TempString(svprogfuncs, as);\n\t\t\t\t\t\t\tPR_ExecuteProgram (svprogfuncs, f);\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tprnum = AddProgs(as);\n\t\t\t\t\t\t\tif (prnum>=0)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tf2 = PR_FindFunction (svprogfuncs, \"init\", prnum);\n\n\t\t\t\t\t\t\t\tif (f2)\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tpr_globals = PR_globals(svprogfuncs, PR_CURRENT);\n\t\t\t\t\t\t\t\t\tG_PROG(OFS_PARM0) = oldprnum;\n\t\t\t\t\t\t\t\t\tPR_ExecuteProgram(svprogfuncs, f2);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\toldprnum=prnum;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t*a = ';';\n\t\t\t\t\tas = a+1;\n\t\t\t\t}\n\t\t\t\ta++;\n\t\t\t}\n\t\t}\n\n\t\tif (COM_FDepthFile(\"fteadd.dat\", true)!=FDEPTH_MISSING)\n\t\t{\n\t\t\tprnum = AddProgs(\"fteadd.dat\");\n\t\t\tif (prnum>=0)\n\t\t\t{\n\t\t\t\tf2 = PR_FindFunction (svprogfuncs, \"init\", prnum);\n\n\t\t\t\tif (f2)\n\t\t\t\t{\n\t\t\t\t\tpr_globals = PR_globals(svprogfuncs, PR_CURRENT);\n\t\t\t\t\tG_PROG(OFS_PARM0) = oldprnum;\n\t\t\t\t\tPR_ExecuteProgram(svprogfuncs, f2);\n\t\t\t\t}\n\t\t\t\toldprnum=prnum;\n\t\t\t}\n\t\t}\n\t\tprnum = 0;\n\n\t\tswitch (sv.world.worldmodel->fromgame)\t//spawn functions for - spawn funcs still come from the first progs found.\n\t\t{\n\t\tcase fg_quake2:\n\t\t\tif (COM_FDepthFile(\"q2bsp.dat\", true)!=FDEPTH_MISSING)\n\t\t\t\tprnum = AddProgs(\"q2bsp.dat\");\n\t\t\tbreak;\n\t\tcase fg_quake3:\n\t\t\tif (COM_FDepthFile(\"q3bsp.dat\", true)!=FDEPTH_MISSING)\n\t\t\t\tprnum = AddProgs(\"q3bsp.dat\");\n\t\t\telse if (COM_FDepthFile(\"q2bsp.dat\", true)!=FDEPTH_MISSING)\t//fallback\n\t\t\t\tprnum = AddProgs(\"q2bsp.dat\");\n\t\t\tbreak;\n\t\tcase fg_doom:\n\t\t\tif (COM_FDepthFile(\"doombsp.dat\", true)!=FDEPTH_MISSING)\n\t\t\t\tprnum = AddProgs(\"doombsp.dat\");\n\t\t\tbreak;\n\t\tcase fg_halflife:\n\t\t\tif (COM_FDepthFile(\"hlbsp.dat\", true)!=FDEPTH_MISSING)\n\t\t\t\tprnum = AddProgs(\"hlbsp.dat\");\n\t\t\tbreak;\n\n\t\tdefault:\n\t\t\tbreak;\n\t\t}\n\n\t\tif (prnum>=0)\n\t\t{\n\t\t\tf2 = PR_FindFunction (svprogfuncs, \"init\", prnum);\n\n\t\t\tif (f2)\n\t\t\t{\n\t\t\t\tpr_globals = PR_globals(svprogfuncs, PR_CURRENT);\n\t\t\t\tG_PROG(OFS_PARM0) = oldprnum;\n\t\t\t\tPR_ExecuteProgram(svprogfuncs, f2);\n\t\t\t}\n\t\t\toldprnum=prnum;\n\t\t}\n\n/*\t\t//progs depended on by maps.\n\t\ta = as = COM_LoadStackFile(va(\"%s.inf\", sv.modelname), addons, sizeof(addons), NULL);\n\t\tif (a)\n\t\t{\n\t\t\tif (progstype == PROG_QW)\n\t\t\t\tas = strstr(a, \"qwprogs=\");\n\t\t\telse\n\t\t\t\tas = strstr(a, \"progs=\");\n\t\t\tif (as)\n\t\t\t{\n\t\t\tfor (a = as+11; *a; a++)\n\t\t\t{\n\t\t\t\tif (*a < ' ')\n\t\t\t\t{\n\t\t\t\t\t*a = '\\0';\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\ta = (as+=11);\n\t\t\t}\n\t\t}\n\t\tif (as)\n\t\t{\n\t\t\twhile(*a)\n\t\t\t{\n\t\t\t\tif (*a == ';')\n\t\t\t\t{\n\t\t\t\t\t*a = '\\0';\n\t\t\t\t\tfor (i = 0; i < svs.numprogs; i++)\t//don't add if already added\n\t\t\t\t\t{\n\t\t\t\t\t\tif (!strcmp(svs.progsnames[i], as))\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tif (i == svs.numprogs)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (f)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tpr_globals = PR_globals(svprogfuncs, PR_CURRENT);\n\t\t\t\t\t\t\tG_INT(OFS_PARM0) = (int)PR_TempString(svprogfuncs, as);\n\t\t\t\t\t\t\tPR_ExecuteProgram (svprogfuncs, f);\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tprnum = AddProgs(as);\n\t\t\t\t\t\t\tif (prnum>=0)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tf2 = PR_FindFunction (svprogfuncs, \"init\", prnum);\n\n\t\t\t\t\t\t\t\tif (f2)\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tpr_globals = PR_globals(svprogfuncs, PR_CURRENT);\n\t\t\t\t\t\t\t\t\tG_PROG(OFS_PARM0) = oldprnum;\n\t\t\t\t\t\t\t\t\tPR_ExecuteProgram(svprogfuncs, f2);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\toldprnum=prnum;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t*a = ';';\n\t\t\t\t\tas = a+1;\n\t\t\t\t}\n\t\t\t\ta++;\n\t\t\t}\n\t\t}\n*/\n\t\t//add any addons specified\n\t\tfor (i2 = 0; i2 < MAXADDONS; i2++)\n\t\t{\n\t\t\tif (*sv_addon[i2].string)\n\t\t\t{\n\t\t\t\tfor (i = 0; i < svs.numprogs; i++)\t//don't add if already added\n\t\t\t\t{\n\t\t\t\t\tif (!strcmp(svs.progsnames[i], sv_addon[i2].string))\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tif (i == svs.numprogs)\t//Not added yet. Add it.\n\t\t\t\t{\n\t\t\t\t\tif (f)\n\t\t\t\t\t{\n\t\t\t\t\t\tpr_globals = PR_globals(svprogfuncs, PR_CURRENT);\n\t\t\t\t\t\tG_INT(OFS_PARM0) = (int)PR_TempString(svprogfuncs, sv_addon[i2].string);\n\t\t\t\t\t\tPR_ExecuteProgram (svprogfuncs, f);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tprnum = AddProgs(sv_addon[i2].string);\n\t\t\t\t\t\tif (prnum >= 0)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tf2 = PR_FindFunction (svprogfuncs, \"init\", prnum);\n\t\t\t\t\t\t\tif (f2)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tpr_globals = PR_globals(svprogfuncs, PR_CURRENT);\n\t\t\t\t\t\t\t\tG_PROG(OFS_PARM0) = oldprnum;\n\t\t\t\t\t\t\t\tPR_ExecuteProgram(svprogfuncs, f2);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\toldprnum=prnum;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tsv.world.edict_size = PR_InitEnts(svprogfuncs, sv.world.max_edicts);\n\n/*\t{\n\t\tchar watch[] = \"something\";\n\t\tsvprogfuncs->SetWatchPoint(svprogfuncs, watch);\n\t}\n*/\n//\t\tsvprogfuncs->ToggleBreak(svprogfuncs, \"something\", 0, 2);\n\n#ifdef HEXEN2\n\tSV_RegisterH2CustomTents();\n#endif\n\n\tWorld_RBE_Start(&sv.world);\n}\n\nqboolean PR_QCChat(char *text, int say_type)\n{\n\tglobalvars_t *pr_globals;\n\n\tif (!gfuncs.ChatMessage || pr_imitatemvdsv.value >= 0)\n\t\treturn false;\n\n\tpr_globals = PR_globals(svprogfuncs, PR_CURRENT);\n\n\tG_INT(OFS_PARM0) = (int)PR_SetString(svprogfuncs, text);\n\tG_FLOAT(OFS_PARM1) = say_type;\n\tPR_ExecuteProgram (svprogfuncs, gfuncs.ChatMessage);\n\n\tif (G_FLOAT(OFS_RETURN))\n\t\treturn true;\n\treturn false;\n}\n\nqboolean PR_GameCodePausedTic(float pausedtime)\n{\t//notications to the gamecode that the server is paused.\n\tglobalvars_t *pr_globals;\n\n\tif (!svprogfuncs || !gfuncs.PausedTic)\n\t\treturn false;\n\n\tpr_globals = PR_globals(svprogfuncs, PR_CURRENT);\n\n\tG_FLOAT(OFS_PARM0) = pausedtime;\n\tPR_ExecuteProgram (svprogfuncs, gfuncs.PausedTic);\n\n\tif (G_FLOAT(OFS_RETURN))\n\t\treturn true;\n\treturn false;\n}\nqboolean PR_ShouldTogglePause(client_t *initiator, qboolean newpaused)\n{\n\tglobalvars_t *pr_globals;\n\n\tif (!svprogfuncs || !gfuncs.ShouldPause)\n\t\treturn true;\n\n\tpr_globals = PR_globals(svprogfuncs, PR_CURRENT);\n\n\tif (initiator)\n\t\tpr_global_struct->self = EDICT_TO_PROG(svprogfuncs, initiator->edict);\n\telse\n\t\tpr_global_struct->self = 0;\n\tG_FLOAT(OFS_PARM0) = newpaused;\n\tPR_ExecuteProgram (svprogfuncs, gfuncs.ShouldPause);\n\n\treturn G_FLOAT(OFS_RETURN);\n}\n\nqboolean PR_GameCodePacket(char *s)\n{\n\tglobalvars_t *pr_globals;\n\tint i;\n\tclient_t *cl;\n\tchar adr[MAX_ADR_SIZE];\n\n\tif (!gfuncs.ParseConnectionlessPacket)\n\t\treturn false;\n\tif (!svprogfuncs)\n\t\treturn false;\n\n\tpr_globals = PR_globals(svprogfuncs, PR_CURRENT);\n\tpr_global_struct->time = sv.world.physicstime;\n\n\t// check for packets from connected clients\n\tpr_global_struct->self = 0;\n\tfor (i=0, cl=svs.clients ; i<sv.allocated_client_slots ; i++,cl++)\n\t{\n\t\tif (cl->state == cs_free)\n\t\t\tcontinue;\n\t\tif (!NET_CompareAdr (&net_from, &cl->netchan.remote_address))\n\t\t\tcontinue;\n\t\tpr_global_struct->self = EDICT_TO_PROG(svprogfuncs, cl->edict);\n\t\tbreak;\n\t}\n\n\n\n\tG_INT(OFS_PARM0) = PR_TempString(svprogfuncs, NET_AdrToString (adr, sizeof(adr), &net_from));\n\n\tG_INT(OFS_PARM1) = PR_TempString(svprogfuncs, s);\n\tPR_ExecuteProgram (svprogfuncs, gfuncs.ParseConnectionlessPacket);\n\treturn G_FLOAT(OFS_RETURN);\n}\n\nqboolean PR_ParseClusterEvent(const char *dest, const char *source, const char *cmd, const char *info)\n{\n\tglobalvars_t *pr_globals;\n\n\tif (svprogfuncs && gfuncs.ParseClusterEvent)\n\t{\n\t\tpr_globals = PR_globals(svprogfuncs, PR_CURRENT);\n\t\tpr_global_struct->time = sv.world.physicstime;\n\t\tpr_global_struct->self = 0;\n\n\t\tG_INT(OFS_PARM0) = (int)PR_TempString(svprogfuncs, dest);\n\t\tG_INT(OFS_PARM1) = (int)PR_TempString(svprogfuncs, source);\n\t\tG_INT(OFS_PARM2) = (int)PR_TempString(svprogfuncs, cmd);\n\t\tG_INT(OFS_PARM3) = (int)PR_TempString(svprogfuncs, info);\n\t\tPR_ExecuteProgram (svprogfuncs, gfuncs.ParseClusterEvent);\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\nvoid SSQC_MapEntityEdited(int modelidx, int idx, const char *newdata)\n{\n}\n\nqboolean PR_KrimzonParseCommand(const char *s)\n{\n\tglobalvars_t *pr_globals;\n\n#ifdef Q2SERVER\n\tif (ge)\n\t\treturn false;\n#endif\n\tif (!svprogfuncs)\n\t\treturn false;\n\n\t/*some people are irresponsible*/\n\tif (pr_no_parsecommand.ival)\n\t\treturn false;\n\n\tif (gfuncs.ParseClientCommand)\n\t{\t//the QC is expected to send it back to use via a builtin.\n\n\t\tpr_globals = PR_globals(svprogfuncs, PR_CURRENT);\n\t\tpr_global_struct->time = sv.world.physicstime;\n\t\tpr_global_struct->self = EDICT_TO_PROG(svprogfuncs, sv_player);\n\n\t\tG_INT(OFS_PARM0) = (int)PR_TempString(svprogfuncs, s);\n\t\tPR_ExecuteProgram (svprogfuncs, gfuncs.ParseClientCommand);\n\t\treturn true;\n\t}\n\n\treturn false;\n}\nint tokenizeqc(const char *str, qboolean dpfuckage);\nqboolean PR_UserCmd(const char *s)\n{\n\tglobalvars_t *pr_globals;\n#ifdef Q2SERVER\n\tif (ge)\n\t{\n\t\tSV_BeginRedirect (RD_CLIENT, host_client->language);\n\t\tge->ClientCommand(host_client->q2edict);\n\t\tSV_EndRedirect ();\n\t\treturn true;\t//the dll will convert it to chat.\n\t}\n#endif\n\tif (!svprogfuncs)\n\t\treturn false;\n\n\tif (gfuncs.ZQ_ClientCommand)\n\t{\n\t\tpr_globals = PR_globals(svprogfuncs, PR_CURRENT);\n\t\ttokenizeqc(s, true);\t//I don't know the spec, I'm making assumptions here. Of course, not having the original string means the tokens will be reordered if they tokenize parm1. sucks to be them if they do that.\n\n\t\tpr_global_struct->time = sv.world.physicstime;\n\t\tpr_global_struct->self = EDICT_TO_PROG(svprogfuncs, sv_player);\n\t\t//originally, UserCmd took no arguments. ezquake passes only the arg (the command's name). We pass the entire argument string.\n\t\tG_INT(OFS_PARM0) = PR_TempString(svprogfuncs, Cmd_Argv(0));\n\t\tG_INT(OFS_PARM1) = PR_TempString(svprogfuncs, Cmd_Args());\n\t\tPR_ExecuteProgram (svprogfuncs, gfuncs.ZQ_ClientCommand);\n\t\treturn !!G_FLOAT(OFS_RETURN);\n\t}\n\n\tif (gfuncs.ParseClientCommand)\n\t{\t//the QC is expected to send it back to use via a builtin.\n\n\t\tpr_globals = PR_globals(svprogfuncs, PR_CURRENT);\n\t\tpr_global_struct->time = sv.world.physicstime;\n\t\tpr_global_struct->self = EDICT_TO_PROG(svprogfuncs, sv_player);\n\n\t\tG_INT(OFS_PARM0) = PR_TempString(svprogfuncs, s);\n\t\tPR_ExecuteProgram (svprogfuncs, gfuncs.ParseClientCommand);\n\t\treturn true;\n\t}\n\n#ifdef VM_Q1\n\tif (svs.gametype == GT_Q1QVM)\n\t{\n\t\tpr_global_struct->time = sv.world.physicstime;\n\t\tpr_global_struct->self = EDICT_TO_PROG(svprogfuncs, sv_player);\n\t\treturn Q1QVM_ClientCommand();\n\t}\n#endif\n\n\tif (gfuncs.UserCmd && progstype == PROG_QW)\n\t{\t//we didn't recognise it. see if the mod does.\n\t\tpr_globals = PR_globals(svprogfuncs, PR_CURRENT);\n\n\t\ttokenizeqc(s, true);\n\n\t\t{\t//ktpro bug warning:\n\t\t\t//admin + judge. I don't know the exact rules behind this bug, so I just ban the entire command\n\t\t\t//I can't be arsed detecting ktpro specifically, so assume we're always running ktpro\n\n\t\t\tconst char *arg0;\n\n\t\t\t//make sure we use the same logic that the qc will use. specifically that we check for \" and leading spaces etc\n\t\t\tG_FLOAT(OFS_PARM0) = 0;\n\t\t\tPF_ArgV(svprogfuncs, pr_globals);\n\t\t\targ0 = PR_GetStringOfs(svprogfuncs, OFS_RETURN);\n\t\t\tif (!strcmp(arg0, \"admin\") || !strcmp(arg0, \"judge\"))\n\t\t\t{\n\t\t\t\tCon_Printf(\"Blocking potentially unsafe ktpro command: %s\\n\", s);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\n\t\tpr_global_struct->time = sv.world.physicstime;\n\t\tpr_global_struct->self = EDICT_TO_PROG(svprogfuncs, sv_player);\n\t\t//originally, UserCmd took no arguments. ezquake passes only the arg (the command's name). We pass the entire argument string.\n\t\tG_INT(OFS_PARM0) = PR_TempString(svprogfuncs, s);\n\t\tPR_ExecuteProgram (svprogfuncs, gfuncs.UserCmd);\n\t\treturn !!G_FLOAT(OFS_RETURN);\n\t}\n\n\treturn false;\n}\nqboolean PR_ConsoleCmd(const char *command)\n{\n\tglobalvars_t *pr_globals;\n\textern redirect_t sv_redirected;\n\n\tif (Cmd_ExecLevel < cmd_gamecodelevel.value)\n\t\treturn false;\n\n\tif (svprogfuncs)\n\t{\n\t\tpr_globals = PR_globals(svprogfuncs, PR_CURRENT);\n\t\tif (gfuncs.ConsoleCmd)\n\t\t{\n\t\t\tint oself = pr_global_struct->self;\n\t\t\tpr_global_struct->time = sv.world.physicstime;\n\t\t\tif (sv_redirected == RD_CLIENT)\n\t\t\t\tpr_global_struct->self = EDICT_TO_PROG(svprogfuncs, host_client->edict);\n\t\t\telse\n\t\t\t\tpr_global_struct->self = EDICT_TO_PROG(svprogfuncs, sv.world.edicts);\n\n\t\t\tG_INT(OFS_PARM0) = PR_TempString(svprogfuncs, command);\n\t\t\tPR_ExecuteProgram (svprogfuncs, gfuncs.ConsoleCmd);\n\t\t\tpr_global_struct->self = oself;\n\t\t\treturn (int) G_FLOAT(OFS_RETURN);\n\t\t}\n\t}\n\n\treturn false;\n}\n\nvoid PR_ClientUserInfoChanged(char *name, char *oldivalue, char *newvalue)\n{\n\tif (gfuncs.UserInfo_Changed && pr_imitatemvdsv.value >= 0)\n\t{\n\t\tglobalvars_t *pr_globals;\n\t\tpr_globals = PR_globals(svprogfuncs, PR_CURRENT);\n\n\t\tpr_global_struct->time = sv.world.physicstime;\n\t\tpr_global_struct->self = EDICT_TO_PROG(svprogfuncs, sv_player);\n\n\t\tG_INT(OFS_PARM0) = PR_TempString(svprogfuncs, name);\n\t\tG_INT(OFS_PARM1) = PR_TempString(svprogfuncs, oldivalue);\n\t\tG_INT(OFS_PARM2) = PR_TempString(svprogfuncs, newvalue);\n\n\t\tPR_ExecuteProgram (svprogfuncs, gfuncs.UserInfo_Changed);\n\t}\n}\n\nvoid PR_LocalInfoChanged(char *name, char *oldivalue, char *newvalue)\n{\n\tif (gfuncs.localinfoChanged && sv.state && pr_imitatemvdsv.value >= 0)\n\t{\n\t\tglobalvars_t *pr_globals;\n\t\tpr_globals = PR_globals(svprogfuncs, PR_CURRENT);\n\n\t\tpr_global_struct->time = sv.world.physicstime;\n\t\tpr_global_struct->self = EDICT_TO_PROG(svprogfuncs, sv.world.edicts);\n\n\t\tG_INT(OFS_PARM0) = PR_TempString(svprogfuncs, name);\n\t\tG_INT(OFS_PARM1) = PR_TempString(svprogfuncs, oldivalue);\n\t\tG_INT(OFS_PARM2) = PR_TempString(svprogfuncs, newvalue);\n\n\t\tPR_ExecuteProgram (svprogfuncs, gfuncs.localinfoChanged);\n\t}\n}\nvoid PR_PreShutdown(void)\n{\n\tsv.mapchangelocked = true;\t//don't let the mod fuck over stuff like `disconnect`. its meant to be shutting down, not switching maps.\n\tif (svprogfuncs && gfuncs.SV_Shutdown && sv.state)\n\t{\n\t\tfunc_t f = gfuncs.SV_Shutdown;\n\t\tglobalvars_t *pr_globals;\n\t\tgfuncs.SV_Shutdown = 0;\t//clear it early, to avoid (severe) recursion/whatever problems.\n\t\tpr_globals = PR_globals(svprogfuncs, PR_CURRENT);\n\n\t\tpr_global_struct->time = sv.world.physicstime;\n\t\tpr_global_struct->self = EDICT_TO_PROG(svprogfuncs, sv.world.edicts);\n\n\t\tG_INT(OFS_PARM0) = 0;\n\t\tG_INT(OFS_PARM1) = 0;\n\t\tG_INT(OFS_PARM2) = 0;\n\t\tPR_ExecuteProgram (svprogfuncs, f);\n\t}\n}\n\nvoid QC_Clear(void)\n{\n}\n\n\n//#define\tRETURN_EDICT(pf, e) (((int *)pr_globals)[OFS_RETURN] = EDICT_TO_PROG(pf, e))\n#define\tRETURN_SSTRING(s) (((int *)pr_globals)[OFS_RETURN] = PR_SetString(prinst, s))\t//static - exe will not change it.\n#define\tRETURN_TSTRING(s) (((int *)pr_globals)[OFS_RETURN] = PR_TempString(prinst, s))\t//temp (static but cycle buffers)\n#define\tRETURN_CSTRING(s) (((int *)pr_globals)[OFS_RETURN] = PR_SetString(prinst, s))\t//semi-permanant. (hash tables?)\n#define\tRETURN_PSTRING(s) (((int *)pr_globals)[OFS_RETURN] = PR_NewString(prinst, s))\t//permanant\n\n/*\n===============================================================================\n\n\t\t\t\t\t\tBUILT-IN FUNCTIONS\n\n===============================================================================\n*/\n\nstatic void PR_ConsoleCommand_f(void)\n{\n\tchar cmd[2048];\n\tQ_snprintfz(cmd, sizeof(cmd), \"%s %s\", Cmd_Argv(0), Cmd_Args());\n\tPR_ConsoleCmd(cmd);\n}\nstatic void QCBUILTIN PF_sv_registercommand (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char *str = PR_GetStringOfs(prinst, OFS_PARM0);\n\tconst char *desc = (prinst->callargc>1)?PR_GetStringOfs(prinst, OFS_PARM1):NULL;\n\tif (desc && !*desc)\n\t\tdesc = NULL;\n\tif (!Cmd_Exists(str))\n\t{\n\t\tCmd_AddCommandD(str, PR_ConsoleCommand_f, desc);\n\t\tif (!gfuncs.ConsoleCmd) //wut? maybe we're an addon calling this...\n\t\t\tgfuncs.ConsoleCmd = PR_FindFunction(svprogfuncs, \"ConsoleCmd\", PR_ANY);\n\t}\n}\n\n\nstatic void SV_Effect(vec3_t org, int mdlidx, int startframe, int endframe, int framerate)\n{\n\tif (startframe>255 || mdlidx>255)\n\t{\n\t\tMSG_WriteByte (&sv.multicast, svcfte_effect2);\n\t\tMSG_WriteCoord (&sv.multicast, org[0]);\n\t\tMSG_WriteCoord (&sv.multicast, org[1]);\n\t\tMSG_WriteCoord (&sv.multicast, org[2]);\n\t\tMSG_WriteShort (&sv.multicast, mdlidx);\n\t\tMSG_WriteShort (&sv.multicast, startframe);\n\t\tMSG_WriteByte (&sv.multicast, endframe);\n\t\tMSG_WriteByte (&sv.multicast, framerate);\n\n#ifdef NQPROT\n\t\tMSG_WriteByte (&sv.nqmulticast, svcnq_effect2);\n\t\tMSG_WriteCoord (&sv.nqmulticast, org[0]);\n\t\tMSG_WriteCoord (&sv.nqmulticast, org[1]);\n\t\tMSG_WriteCoord (&sv.nqmulticast, org[2]);\n\t\tMSG_WriteShort (&sv.nqmulticast, mdlidx);\n\t\tMSG_WriteShort (&sv.nqmulticast, startframe);\n\t\tMSG_WriteByte (&sv.nqmulticast, endframe);\n\t\tMSG_WriteByte (&sv.nqmulticast, framerate);\n#endif\n\t}\n\telse\n\t{\n\t\tMSG_WriteByte (&sv.multicast, svcfte_effect);\n\t\tMSG_WriteCoord (&sv.multicast, org[0]);\n\t\tMSG_WriteCoord (&sv.multicast, org[1]);\n\t\tMSG_WriteCoord (&sv.multicast, org[2]);\n\t\tMSG_WriteByte (&sv.multicast, mdlidx);\n\t\tMSG_WriteByte (&sv.multicast, startframe);\n\t\tMSG_WriteByte (&sv.multicast, endframe);\n\t\tMSG_WriteByte (&sv.multicast, framerate);\n\n#ifdef NQPROT\n\t\tMSG_WriteByte (&sv.nqmulticast, svcnq_effect);\n\t\tMSG_WriteCoord (&sv.nqmulticast, org[0]);\n\t\tMSG_WriteCoord (&sv.nqmulticast, org[1]);\n\t\tMSG_WriteCoord (&sv.nqmulticast, org[2]);\n\t\tMSG_WriteByte (&sv.nqmulticast, mdlidx);\n\t\tMSG_WriteByte (&sv.nqmulticast, startframe);\n\t\tMSG_WriteByte (&sv.nqmulticast, endframe);\n\t\tMSG_WriteByte (&sv.nqmulticast, framerate);\n#endif\n\t}\n\n\tSV_MulticastProtExt (org, MULTICAST_PVS, pr_global_struct->dimension_send, 0, 0);\n}\n\n#ifdef HEXEN2\nstatic int SV_CustomTEnt_Register(char *effectname, int nettype, float *stain_rgb, float stain_radius, float *dl_rgb, float dl_radius, float dl_time, float *dl_fade)\n{\n\tint i;\n\tfor (i = 0; i < 255; i++)\n\t{\n\t\tif (!*sv.customtents[i].particleeffecttype)\n\t\t\tbreak;\n\t\tif (!strcmp(effectname, sv.customtents[i].particleeffecttype))\n\t\t\tbreak;\n\t}\n\tif (i == 255)\n\t{\n\t\tCon_Printf(\"Too many custom effects\\n\");\n\t\treturn -1;\n\t}\n\n\tQ_strncpyz(sv.customtents[i].particleeffecttype, effectname, sizeof(sv.customtents[i].particleeffecttype));\n\tsv.customtents[i].netstyle = nettype;\n\n\tif (nettype & CTE_STAINS)\n\t{\n\t\tVectorCopy(stain_rgb, sv.customtents[i].stain);\n\t\tsv.customtents[i].radius = stain_radius;\n\t}\n\tif (nettype & CTE_GLOWS)\n\t{\n\t\tsv.customtents[i].dlightrgb[0] = dl_rgb[0]*255;\n\t\tsv.customtents[i].dlightrgb[1] = dl_rgb[1]*255;\n\t\tsv.customtents[i].dlightrgb[2] = dl_rgb[2]*255;\n\t\tsv.customtents[i].dlightradius = dl_radius/4;\n\t\tsv.customtents[i].dlighttime = dl_time*16;\n\t\tif (nettype & CTE_CHANNELFADE)\n\t\t{\n\t\t\tsv.customtents[i].dlightcfade[0] = dl_fade[0]*64;\n\t\t\tsv.customtents[i].dlightcfade[1] = dl_fade[1]*64;\n\t\t\tsv.customtents[i].dlightcfade[2] = dl_fade[2]*64;\n\t\t}\n\t}\n\n\treturn i;\n}\n\nstatic int SV_CustomTEnt_Spawn(int index, float *org, float *org2, int count, float *dir)\n{\n\tstatic int persist_id;\n\tint type;\n\tmulticast_t mct = MULTICAST_PVS;\n\tif (index < 0 || index >= 255)\n\t\treturn -1;\n\ttype = sv.customtents[index].netstyle;\n\n\tMSG_WriteByte(&sv.multicast, svcfte_customtempent);\n\tMSG_WriteByte(&sv.multicast, index);\n\n\tif (type & CTE_PERSISTANT)\n\t{\n\t\tpersist_id++;\n\t\tif (persist_id >= 0x8000)\n\t\t\tpersist_id = 1;\n\t\tif (sv.state == ss_loading)\n\t\t\tmct = MULTICAST_INIT;\n\t\telse\n\t\t\tmct = MULTICAST_ALL;\n\t\tMSG_WriteShort(&sv.multicast, persist_id);\n\t}\n\tMSG_WriteCoord(&sv.multicast, org[0]);\n\tMSG_WriteCoord(&sv.multicast, org[1]);\n\tMSG_WriteCoord(&sv.multicast, org[2]);\n\n\tif (type & CTE_ISBEAM)\n\t{\n\t\tMSG_WriteCoord(&sv.multicast, org2[0]);\n\t\tMSG_WriteCoord(&sv.multicast, org2[1]);\n\t\tMSG_WriteCoord(&sv.multicast, org2[2]);\n\t}\n\tif (type & CTE_CUSTOMCOUNT)\n\t{\n\t\tMSG_WriteByte(&sv.multicast, count);\n\t}\n\tif (type & CTE_CUSTOMVELOCITY)\n\t{\n\t\tMSG_WriteCoord(&sv.multicast, dir[0]);\n\t\tMSG_WriteCoord(&sv.multicast, dir[1]);\n\t\tMSG_WriteCoord(&sv.multicast, dir[2]);\n\t}\n\telse if (type & CTE_CUSTOMDIRECTION)\n\t{\n\t\tvec3_t norm;\n\t\tVectorNormalize2(dir, norm);\n\t\tMSG_WriteDir(&sv.multicast, norm);\n\t}\n\n\tSV_MulticastProtExt (org, mct, pr_global_struct->dimension_send, PEXT_CUSTOMTEMPEFFECTS, 0);\t//now send the new multicast to all that will.\n\n\treturn persist_id;\n}\n#endif\n\n\n\nfloat PR_LoadAditionalProgs(char *s);\nstatic void QCBUILTIN PF_addprogs(pubprogfuncs_t *prinst, globalvars_t *pr_globals)\n{\n\tconst char *s = PR_GetStringOfs(prinst, OFS_PARM0);\n\tif (!s || !*s)\n\t{\n\t\tG_PROG(OFS_RETURN)=-1;\n\t\treturn;\n\t}\n\tG_PROG(OFS_RETURN) = AddProgs(s);\n}\n\n\n\n\n\n\n/*\nchar *PF_VarString (int\tfirst)\n{\n\tint\t\ti;\n\tstatic char out[256];\n\n\tout[0] = 0;\n\tfor (i=first ; i<pr_argc ; i++)\n\t{\n\t\tstrcat (out, G_STRING((OFS_PARM0+i*3)));\n\t}\n\treturn out;\n}\n*/\n\n/*\n=================\nPF_objerror\n\nDumps out self, then an error message.  The program is aborted and self is\nremoved, but the level can continue.\n\nobjerror(value)\n=================\n*/\nstatic void QCBUILTIN PF_objerror (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char\t*s;\n\tedict_t\t*ed;\n\n\ts = PF_VarString(prinst, 0, pr_globals);\n/*\tCon_Printf (\"======OBJECT ERROR in %s:\\n%s\\n\", PR_GetString(pr_xfunction->s_name),s);\n*/\ted = PROG_TO_EDICT(prinst, pr_global_struct->self);\n/*\tED_Print (ed);\n*/\n\tprinst->ED_Print(prinst, ed);\n\n\tPR_RunWarning(prinst, \"Program error: %s\\n\", s);\n\n\tED_Free (prinst, ed);\n\tPR_AbortStack(prinst);\n\n//\tif (sv.time > 10)\n//\t\tCbuf_AddText(\"restart\\n\", RESTRICT_LOCAL);\n}\n\n\n\n/*\n==============\nPF_makevectors\n\nWrites new values for v_forward, v_up, and v_right based on angles\nmakevectors(vector)\n==============\n*/\nstatic void QCBUILTIN PF_makevectors (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tAngleVectors (G_VECTOR(OFS_PARM0), P_VEC(v_forward), P_VEC(v_right), P_VEC(v_up));\n}\n\n/*\n=================\nPF_setorigin\n\nThis is the only valid way to move an object without using the physics of the world (setting velocity and waiting).  Directly changing origin will not set internal links correctly, so clipping would be messed up.  This should be called when an object is spawned, and then only if it is teleported.\n\nsetorigin (entity, origin)\n=================\n*/\nstatic void QCBUILTIN PF_setorigin (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tedict_t\t*e;\n\tpvec_t\t*org;\n\n\te = G_EDICT(prinst, OFS_PARM0);\n\tif (e->readonly)\n\t{\n\t\tCon_Printf(\"setorigin on entity %i\\n\", e->entnum);\n\t\treturn;\n\t}\n\torg = G_VECTOR(OFS_PARM1);\n\tVectorCopy (org, e->v->origin);\n\tWorld_LinkEdict (&sv.world, (wedict_t*)e, false);\n}\n\n\n/*\n=================\nPF_setsize\n\nthe size box is rotated by the current angle\n\nsetsize (entity, minvector, maxvector)\n=================\n*/\nstatic void QCBUILTIN PF_setsize (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tedict_t\t*e;\n\tpvec_t\t*min, *max;\n\n\te = G_EDICT(prinst, OFS_PARM0);\n\tif (ED_ISFREE(e))\n\t{\n\t\tif (progstype != PROG_H2 || developer.ival)\n\t\t\tPR_RunWarning(prinst, \"%s edict %i was free\\n\", \"setsize\", e->entnum);\n\t\treturn;\n\t}\n\tif (e->readonly)\n\t{\n\t\tCon_TPrintf(\"setsize on readonly entity %i\\n\", e->entnum);\n\t\treturn;\n\t}\n\tmin = G_VECTOR(OFS_PARM1);\n\tmax = G_VECTOR(OFS_PARM2);\n\tVectorCopy (min, e->v->mins);\n\tVectorCopy (max, e->v->maxs);\n\tVectorSubtract (max, min, e->v->size);\n\tWorld_LinkEdict (&sv.world, (wedict_t*)e, false);\n}\n\n\n/*\n=================\nPF_setmodel\n\nsetmodel(entity, model)\nAlso sets size, mins, and maxs for inline bmodels\n=================\n*/\nvoid PF_setmodel_Internal (pubprogfuncs_t *prinst, edict_t *e, const char *m)\n{\n\tint\t\ti;\n\tmodel_t\t*mod;\n\n\tif (!e)\n\t{\n\t\tPR_RunWarning(prinst, \"%s on invalid entity\\n\", \"setmodel\");\n\t\treturn;\n\t}\n\tif (e->readonly)\n\t{\n\t\tPR_RunWarning(prinst, \"%s edict %i is read-only\\n\", \"setmodel\", e->entnum);\n\t\treturn;\n\t}\n\tif (ED_ISFREE(e))\n\t{\n\t\tPR_RunWarning(prinst, \"%s edict %i was free\\n\", \"setmodel\", e->entnum);\n\t\treturn;\n\t}\n\n// check to see if model was properly precached\n\tif (!m || !*m)\n\t\ti = 0;\n\telse\n\t{\n\t\tfor (i=1; sv.strings.model_precache[i] ; i++)\n\t\t{\n\t\t\tif (!strcmp(sv.strings.model_precache[i], m))\n\t\t\t{\n#ifdef VM_Q1\n\t\t\t\tif (svs.gametype != GT_Q1QVM)\n#endif\n\t\t\t\t\tm = sv.strings.model_precache[i];\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (i==MAX_PRECACHE_MODELS || !sv.strings.model_precache[i])\n\t\t{\n\t\t\tif (i!=MAX_PRECACHE_MODELS)\n\t\t\t{\n#ifdef VM_Q1\n\t\t\t\tif (svs.gametype == GT_Q1QVM)\n\t\t\t\t\tsv.strings.model_precache[i] = m;\t//in a qvm, we expect the caller to have used a static location.\n\t\t\t\telse\n#endif\n\t\t\t\t\tm = sv.strings.model_precache[i] = PR_AddString(prinst, m, 0, false);\n\t\t\t\tif (!strcmp(m + strlen(m) - 4, \".bsp\"))\t//always precache bsps\n\t\t\t\t\tsv.models[i] = Mod_FindName(Mod_FixName(m, sv.strings.model_precache[1]));\n\t\t\t\tCon_Printf(\"WARNING: SV_ModelIndex: model %s not precached\\n\", m);\n\n\t\t\t\tif (sv.state != ss_loading)\n\t\t\t\t{\n\t\t\t\t\tint j;\n\t\t\t\t\tCon_DPrintf(\"Delayed model precache: %s\\n\", m);\n\n\t\t\t\t\tfor (j = 0; j < sv.allocated_client_slots; j++)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (svs.clients[j].state < cs_connected)\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\tif (ISDPCLIENT(&svs.clients[j]) || (svs.clients[j].fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tClientReliableWrite_Begin (&svs.clients[j], ISNQCLIENT(&svs.clients[j])?svcdp_precache:svcfte_precache, strlen(m)+4);\n\t\t\t\t\t\t\tClientReliableWrite_Short (&svs.clients[j], i);\n\t\t\t\t\t\t\tClientReliableWrite_String (&svs.clients[j], m);\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t//client doesn't support this... reset the connection so they're forced to reload everything.\n\t\t\t\t\t\t\t//SV_StuffcmdToClient(&svs.clients[j], \"cmd new\\n\");\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tPR_BIError (prinst, \"no precache: %s\\n\", m);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n\n\tprinst->SetStringField(prinst, e, &e->v->model, m, true);\n\te->v->modelindex = i;\n\n\t// if it is an inline model, get the size information for it\n\tif (m && (m[0] == '*' || (*m&&progstype == PROG_H2)))\n\t{\n\t\tmod = SVPR_GetCModel(&sv.world, i);\n//\t\tmod = Mod_ForName (Mod_FixName(m, sv.modelname), MLV_WARN);\n\t\tif (mod)\n\t\t{\n\t\t\twhile(mod->loadstate == MLS_LOADING)\n\t\t\t\tCOM_WorkerPartialSync(mod, &mod->loadstate, MLS_LOADING);\n\n\t\t\tVectorCopy (mod->mins, e->v->mins);\n\t\t\tVectorCopy (mod->maxs, e->v->maxs);\n#ifdef HEXEN2\n\t\t\tif (progstype == PROG_H2 && mod->type == mod_alias && !sv_gameplayfix_setmodelrealbox.ival)\n\t\t\t{\t//hexen2 expands its mdls by 10\n\t\t\t\tvec3_t hexen2expansion = {10,10,10};\n\t\t\t\tVectorSubtract (mod->mins, hexen2expansion, e->v->mins);\n\t\t\t\tVectorAdd (mod->maxs, hexen2expansion, e->v->maxs);\n\t\t\t}\n#endif\n\t\t\tVectorSubtract (e->v->maxs, e->v->mins, e->v->size);\n\t\t\tWorld_LinkEdict (&sv.world, (wedict_t*)e, false);\n\t\t}\n\n\t\treturn;\n\t}\n\n\t/*if (progstype == PROG_H2)\n\t{\n\t\te->v->mins[0] = 0;\n\t\te->v->mins[1] = 0;\n\t\te->v->mins[2] = 0;\n\n\t\te->v->maxs[0] = 0;\n\t\te->v->maxs[1] = 0;\n\t\te->v->maxs[2] = 0;\n\n\t\tVectorSubtract (e->v->maxs, e->v->mins, e->v->size);\n\t}\n\telse*/\n\t{\n\t\tif (sv_gameplayfix_setmodelrealbox.ival)\n\t\t\tmod = SVPR_GetCModel(&sv.world, i);\n\t\telse\n\t\t\tmod = sv.models[i];\n\n\t\tif (progstype != PROG_QW || sv_gameplayfix_setmodelsize_qw.ival)\n\t\t{\t//also sets size.\n\n\t\t\t//nq dedicated servers load bsps and mdls\n\t\t\t//qw dedicated servers only load bsps (better)\n\t\t\tif (mod)\n\t\t\t{\n\t\t\t\tmod = Mod_ForName (Mod_FixName(m, sv.modelname), MLV_WARNSYNC);\n\t\t\t\tif (mod)\n\t\t\t\t{\n\t\t\t\t\twhile(mod->loadstate == MLS_LOADING)\n\t\t\t\t\t\tCOM_WorkerPartialSync(mod, &mod->loadstate, MLS_LOADING);\n\n\t\t\t\t\tVectorCopy (mod->mins, e->v->mins);\n\t\t\t\t\tVectorCopy (mod->maxs, e->v->maxs);\n\t\t\t\t\tVectorSubtract (mod->maxs, mod->mins, e->v->size);\n\t\t\t\t\tWorld_LinkEdict (&sv.world, (wedict_t*)e, false);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t//it's an interesting fact that nq pretended that it's models were all +/- 16 (causing culling issues).\n\t\t\t\t//seing as dedicated servers don't want to load mdls,\n\t\t\t\t//imitate the behaviour of setting the size (which nq can only have as +/- 16)\n\t\t\t\t//hell, this works with quakerally so why not use it.\n\t\t\t\te->v->mins[0] =\n\t\t\t\te->v->mins[1] =\n\t\t\t\te->v->mins[2] = -16;\n\t\t\t\te->v->maxs[0] =\n\t\t\t\te->v->maxs[1] =\n\t\t\t\te->v->maxs[2] = 16;\n\t\t\t\tVectorSubtract (e->v->maxs, e->v->mins, e->v->size);\n\t\t\t\tWorld_LinkEdict (&sv.world, (wedict_t*)e, false);\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\t//qw was fixed - it never sets the size of an alias model, mostly because it doesn't know it.\n\t\t\t//this of course means that precache_model+setmodel doesn't stall.\n\t\t\tif (mod && mod->type != mod_alias)\n\t\t\t{\n\t\t\t\twhile(mod->loadstate == MLS_LOADING)\n\t\t\t\t\tCOM_WorkerPartialSync(mod, &mod->loadstate, MLS_LOADING);\n\n\t\t\t\tVectorCopy (mod->mins, e->v->mins);\n\t\t\t\tVectorCopy (mod->maxs, e->v->maxs);\n\t\t\t\tVectorSubtract (mod->maxs, mod->mins, e->v->size);\n\t\t\t}\n\t\t\t//but do relink, because stuff bugs out otherwise.\n\t\t\tWorld_LinkEdict (&sv.world, (wedict_t*)e, false);\n\t\t}\n\t}\n}\n\nstatic void QCBUILTIN PF_setmodel (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tedict_t\t*e;\n\tconst char\t*m;\n\n\te = G_EDICT(prinst, OFS_PARM0);\n\tm = PR_GetStringOfs(prinst, OFS_PARM1);\n\n\tPF_setmodel_Internal(prinst, e, m);\n}\n\n#ifdef HEXEN2\nstatic void QCBUILTIN PF_h2set_puzzle_model (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\t//qc/hc lacks string manipulation.\n\tedict_t\t*e;\n\tconst char *shortname;\n\tchar fullname[MAX_QPATH];\n\te = G_EDICT(prinst, OFS_PARM0);\n\tshortname = PR_GetStringOfs(prinst, OFS_PARM1);\n\n\tsnprintf(fullname, sizeof(fullname)-1, \"models/puzzle/%s.mdl\", shortname);\n\tPF_setmodel_Internal(prinst, e, fullname);\n}\n#endif\n\n/*\n=================\nPF_bprint\n\nbroadcast print to everyone on server\n\nbprint(value)\n=================\n*/\nstatic void QCBUILTIN PF_bprint (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char\t\t*s;\n\tint\t\t\tlevel;\n\n#ifdef SERVER_DEMO_PLAYBACK\n\tif (sv.demofile)\n\t\treturn;\n#endif\n\n\tif (progstype != PROG_QW)\n\t{\n\t\tlevel = PRINT_HIGH;\n\n\t\ts = PF_VarString(prinst, 0, pr_globals);\n\t}\n\telse\n\t{\n\t\tlevel = G_FLOAT(OFS_PARM0);\n\n\t\ts = PF_VarString(prinst, 1, pr_globals);\n\t}\n\tSV_BroadcastPrintf (level, \"%s\", s);\n}\n\n/*\n=================\nPF_sprint\n\nsingle print to a specific client\n\nsprint(clientent, value)\n=================\n*/\nstatic void QCBUILTIN PF_sprint (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char\t\t*s;\n\tclient_t\t*client;\n\tint\t\t\tentnum;\n\tint\t\t\tlevel;\n\n#ifdef SERVER_DEMO_PLAYBACK\n\tif (sv.demofile)\n\t\treturn;\n#endif\n\n\tentnum = G_EDICTNUM(prinst, OFS_PARM0);\n\n\tif (progstype == PROG_NQ || progstype == PROG_H2)\n\t{\n\t\tlevel = PRINT_HIGH;\n\n\t\ts = PF_VarString(prinst, 1, pr_globals);\n\t}\n\telse\n\t{\n\t\tlevel = G_FLOAT(OFS_PARM1);\n\n\t\ts = PF_VarString(prinst, 2, pr_globals);\n\t}\n\n\tif (entnum < 1 || entnum > sv.allocated_client_slots)\n\t{\n\t\tCon_TPrintf (\"tried to sprint to a non-client\\n\");\n\t\treturn;\n\t}\n\n\tclient = &svs.clients[entnum-1];\n\n\tSV_ClientPrintf (client, level, \"%s\", s);\n\n\tif (sv_specprint.ival & SPECPRINT_SPRINT)\n\t{\n\t\tclient_t *spec;\n\t\tunsigned int i;\n\t\tfor (i = 0, spec = svs.clients; i < sv.allocated_client_slots; i++, spec++)\n\t\t{\n\t\t\tif (spec->state != cs_spawned || !spec->spectator)\n\t\t\t\tcontinue;\n\t\t\tif (spec->spec_track == entnum && (spec->spec_print & SPECPRINT_SPRINT))\n\t\t\t{\n\t\t\t\tif (level < spec->messagelevel)\n\t\t\t\t\tcontinue;\n\t\t\t\tif (spec->controller)\n\t\t\t\t\tSV_PrintToClient(spec->controller, level, s);\n\t\t\t\telse\n\t\t\t\t\tSV_PrintToClient(spec, level, s);\n\t\t\t}\n\t\t}\n\t}\n}\n\n//When a client is backbuffered, it's generally not a brilliant plan to send a bazillion stuffcmds. You have been warned.\n//This handy function will let the mod know when it shouldn't send more. (use instead of a timer, and you'll never get clients overflowing. yay.)\nstatic void QCBUILTIN PF_isbackbuffered (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint\t\t\tentnum;\n\tclient_t\t*client;\n\n\tentnum = G_EDICTNUM(prinst, OFS_PARM0);\n\tif (entnum < 1 || entnum > sv.allocated_client_slots)\n\t{\n\t\tCon_Printf (\"PF_isbackbuffered: Not a client\\n\");\n\t\treturn;\n\t}\n\tclient = &svs.clients[entnum-1];\n\n\tG_FLOAT(OFS_RETURN) = client->num_backbuf>0;\n}\n\n\n/*\n=================\nPF_centerprint\n\nsingle print to a specific client\n\ncenterprint(clientent, value)\n=================\n*/\nvoid PF_centerprint_Internal (int entnum, qboolean plaque, const char *s)\n{\n\tclient_t\t*cl;\n\tint\t\t\tslen;\n\n#ifdef SERVER_DEMO_PLAYBACK\n\tif (sv.demofile)\n\t\treturn;\n#endif\n\n\tif (entnum < 1 || entnum > sv.allocated_client_slots)\n\t{\n\t\tPR_RunWarning(sv.world.progs, \"tried to centerprint to a non-client\\n\");\n\t\treturn;\n\t}\n\n\tcl = &svs.clients[entnum-1];\n\tif (cl->centerprintstring)\n\t\tZ_Free(cl->centerprintstring);\n\tcl->centerprintstring = NULL;\n\n\tslen = strlen(s);\n\tif (plaque && *s)\n\t{\n\t\tcl->centerprintstring = Z_Malloc(slen+3);\n\t\tcl->centerprintstring[0] = '/';\n\t\tcl->centerprintstring[1] = 'P';\n\t\tstrcpy(cl->centerprintstring+2, s);\n\t}\n#ifdef HEXEN2\n\telse if (progstype == PROG_H2)\n\t{\n\t\tchar *at;\n\t\tcl->centerprintstring = Z_Malloc(slen+2);\n\t\tcl->centerprintstring[0] = 2;\t//hexen2's centerprints use the alternate charset.\n\t\tstrcpy(cl->centerprintstring+1, s);\n\n\t\t//and @ chars are used for new-lines.\n\t\tfor (at = cl->centerprintstring+1; *at; at++)\n\t\t\tif (*at == '@')\n\t\t\t\t*at = '\\n';\n\t}\n#endif\n\telse\n\t{\n\t\tcl->centerprintstring = Z_Malloc(slen+1);\n\t\tstrcpy(cl->centerprintstring, s);\n\t}\n\n\tif (sv_specprint.ival & SPECPRINT_CENTERPRINT)\n\t{\n\t\tclient_t *spec;\n\t\tunsigned int i;\n\t\tfor (i = 0, spec = svs.clients; i < sv.allocated_client_slots; i++, spec++)\n\t\t{\n\t\t\tif (spec->state != cs_spawned || !spec->spectator || spec == cl)\n\t\t\t\tcontinue;\n\t\t\tif (spec->spec_track == entnum && (spec->spec_print & SPECPRINT_CENTERPRINT))\n\t\t\t{\n\t\t\t\tZ_Free(spec->centerprintstring);\n\t\t\t\tspec->centerprintstring = Z_StrDup(cl->centerprintstring);\n\t\t\t}\n\t\t}\n\t}\n}\n\nstatic void QCBUILTIN PF_centerprint (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char\t\t*s;\n\tint\t\t\tentnum;\n\n\tentnum = G_EDICTNUM(prinst, OFS_PARM0);\n\ts = PF_VarString(prinst, 1, pr_globals);\n\tPF_centerprint_Internal(entnum, false, s);\n}\n\n/*\n=================\nPF_particle\n\nparticle(origin, color, count)\n=================\n*/\nvoid QCBUILTIN PF_particle (pubprogfuncs_t *prinst, globalvars_t *pr_globals)\t//I said it was for compatability only.\n{\n\tint i, v;\n\n\tVM_VECTORARG(org, OFS_PARM0);\n\tVM_VECTORARG(dir, OFS_PARM1);\n\tint color = G_FLOAT(OFS_PARM2);\n\tint count = G_FLOAT(OFS_PARM3);\n\n\tcount = bound(0, count, 255);\n\tcolor &= 0xff;\n\n#ifdef NQPROT\n\tMSG_WriteByte (&sv.nqmulticast, svc_particle);\n\tMSG_WriteCoord (&sv.nqmulticast, org[0]);\n\tMSG_WriteCoord (&sv.nqmulticast, org[1]);\n\tMSG_WriteCoord (&sv.nqmulticast, org[2]);\n\tfor (i=0 ; i<3 ; i++)\n\t{\n\t\tv = dir[i]*16;\n\t\tif (v > 127)\n\t\t\tv = 127;\n\t\telse if (v < -128)\n\t\t\tv = -128;\n\t\tMSG_WriteChar (&sv.nqmulticast, v);\n\t}\n\tMSG_WriteByte (&sv.nqmulticast, count);\n\tMSG_WriteByte (&sv.nqmulticast, color);\n\tSV_MulticastProtExt(org, MULTICAST_PVS, pr_global_struct->dimension_send, 0, 0);\n#endif\n\t//for qw users (and not fte)\n/*\tif (*prinst->callargc >= 5)\n\t{\n\tPARM4 = te_\n\toptional PARM5 = count\n\t\tMSG_WriteByte (&sv.multicast, svc_temp_entity);\n\t\tMSG_WriteByte (&sv.multicast, TE_BLOOD);\n\t\tMSG_WriteByte (&sv.multicast, count<10?1:(count+10)/20);\n\t\tMSG_WriteCoord (&sv.multicast, org[0]);\n\t\tMSG_WriteCoord (&sv.multicast, org[1]);\n\t\tMSG_WriteCoord (&sv.multicast, org[2]);\n\t\tSV_MulticastProtExt(org, MULTICAST_PVS, pr_global_struct->dimension_send, 0, PEXT_HEXEN2);\n\t}\n\telse\n*/\n\t\tif (color == 73)\n\t{\n\t\tMSG_WriteByte (&sv.multicast, svc_temp_entity);\n\t\tMSG_WriteByte (&sv.multicast, TEQW_QWBLOOD);\n\t\tMSG_WriteByte (&sv.multicast, count<10?1:(count+10)/20);\n\t\tMSG_WriteCoord (&sv.multicast, org[0]);\n\t\tMSG_WriteCoord (&sv.multicast, org[1]);\n\t\tMSG_WriteCoord (&sv.multicast, org[2]);\n\t\tSV_MulticastProtExt(org, MULTICAST_PVS, pr_global_struct->dimension_send, 0, PEXT_HEXEN2);\n\t}\n\telse if (color == 225)\n\t{\n\t\tMSG_WriteByte (&sv.multicast, svc_temp_entity);\n\t\tMSG_WriteByte (&sv.multicast, TEQW_LIGHTNINGBLOOD);\n\t\tMSG_WriteCoord (&sv.multicast, org[0]);\n\t\tMSG_WriteCoord (&sv.multicast, org[1]);\n\t\tMSG_WriteCoord (&sv.multicast, org[2]);\n\t\tSV_MulticastProtExt(org, MULTICAST_PVS, pr_global_struct->dimension_send, 0, PEXT_HEXEN2);\n\t}\n\t//now we can start fte svc_particle stuff..\n\tMSG_WriteByte (&sv.multicast, svc_particle);\n\tMSG_WriteCoord (&sv.multicast, org[0]);\n\tMSG_WriteCoord (&sv.multicast, org[1]);\n\tMSG_WriteCoord (&sv.multicast, org[2]);\n\tfor (i=0 ; i<3 ; i++)\n\t{\n\t\tv = dir[i]*16;\n\t\tif (v > 127)\n\t\t\tv = 127;\n\t\telse if (v < -128)\n\t\t\tv = -128;\n\t\tMSG_WriteChar (&sv.multicast, v);\n\t}\n\tMSG_WriteByte (&sv.multicast, count);\n\tMSG_WriteByte (&sv.multicast, color);\n\tSV_MulticastProtExt(org, MULTICAST_PVS, pr_global_struct->dimension_send, PEXT_HEXEN2, 0);\n}\n\nstatic void QCBUILTIN PF_te_blooddp (pubprogfuncs_t *prinst, globalvars_t *pr_globals)\n{\n#ifdef NQPROT\n\tint i, v;\n#endif\n\tVM_VECTORARG(org, OFS_PARM0);\n\tVM_VECTORARG(dir, OFS_PARM1);\n\tint count = G_FLOAT(OFS_PARM2);\n\n#ifdef NQPROT\n\tMSG_WriteByte (&sv.nqmulticast, svc_particle);\n\tMSG_WriteCoord (&sv.nqmulticast, org[0]);\n\tMSG_WriteCoord (&sv.nqmulticast, org[1]);\n\tMSG_WriteCoord (&sv.nqmulticast, org[2]);\n\tfor (i=0 ; i<3 ; i++)\n\t{\n\t\tv = dir[i]*16;\n\t\tif (v > 127)\n\t\t\tv = 127;\n\t\telse if (v < -128)\n\t\t\tv = -128;\n\t\tMSG_WriteChar (&sv.nqmulticast, v);\n\t}\n\tMSG_WriteByte (&sv.nqmulticast, count);\n\tMSG_WriteByte (&sv.nqmulticast, 73);\n#endif\n\n\t(void)dir; //FIXME: sould be sending TEDP_BLOOD\n\tMSG_WriteByte (&sv.multicast, svc_temp_entity);\n\tMSG_WriteByte (&sv.multicast, TEQW_QWBLOOD);\n\tMSG_WriteByte (&sv.multicast, (count>0&&count<10)?1:(count+10)/20);\n\tMSG_WriteCoord (&sv.multicast, org[0]);\n\tMSG_WriteCoord (&sv.multicast, org[1]);\n\tMSG_WriteCoord (&sv.multicast, org[2]);\n\tSV_MulticastProtExt (org, MULTICAST_PVS, pr_global_struct->dimension_send, 0, 0);\n}\n\n#ifdef HEXEN2\n/*\n=================\nPF_particle2 - hexen2\n\nparticle(origin, dmin, dmax, color, effect, count)\n=================\n*/\nstatic void QCBUILTIN PF_particle2 (pubprogfuncs_t *prinst, globalvars_t *pr_globals)\n{\n\tVM_VECTORARG(org, OFS_PARM0);\n\tVM_VECTORARG(dmin, OFS_PARM1);\n\tVM_VECTORARG(dmax, OFS_PARM2);\n\tfloat color = G_FLOAT(OFS_PARM3);\n\tfloat effect = G_FLOAT(OFS_PARM4);\n\tfloat count = G_FLOAT(OFS_PARM5);\n\n\tMSG_WriteByte (&sv.multicast, svcfte_particle2);\n\tMSG_WriteCoord (&sv.multicast, org[0]);\n\tMSG_WriteCoord (&sv.multicast, org[1]);\n\tMSG_WriteCoord (&sv.multicast, org[2]);\n\tMSG_WriteFloat (&sv.multicast, dmin[0]);\n\tMSG_WriteFloat (&sv.multicast, dmin[1]);\n\tMSG_WriteFloat (&sv.multicast, dmin[2]);\n\tMSG_WriteFloat (&sv.multicast, dmax[0]);\n\tMSG_WriteFloat (&sv.multicast, dmax[1]);\n\tMSG_WriteFloat (&sv.multicast, dmax[2]);\n\n\tMSG_WriteShort (&sv.multicast, color);\n\tMSG_WriteByte (&sv.multicast, bound(0, count, 255));\n\tMSG_WriteByte (&sv.multicast, effect);\n\n\tSV_MulticastProtExt (org, MULTICAST_PVS, pr_global_struct->dimension_send, PEXT_HEXEN2, 0);\n}\n\n\n/*\n=================\nPF_particle3 - hexen2\n\nparticle(origin, box, color, effect, count)\n=================\n*/\nstatic void QCBUILTIN PF_particle3 (pubprogfuncs_t *prinst, globalvars_t *pr_globals)\n{\n\tVM_VECTORARG(org, OFS_PARM0);\n\tVM_VECTORARG(box, OFS_PARM1);\n\tfloat color = G_FLOAT(OFS_PARM2);\n\tfloat effect = G_FLOAT(OFS_PARM3);\n\tfloat count = G_FLOAT(OFS_PARM4);\n\n\tMSG_WriteByte (&sv.multicast, svcfte_particle3);\n\tMSG_WriteCoord (&sv.multicast, org[0]);\n\tMSG_WriteCoord (&sv.multicast, org[1]);\n\tMSG_WriteCoord (&sv.multicast, org[2]);\n\tMSG_WriteByte (&sv.multicast, box[0]);\n\tMSG_WriteByte (&sv.multicast, box[1]);\n\tMSG_WriteByte (&sv.multicast, box[2]);\n\n\tMSG_WriteShort (&sv.multicast, color);\n\tMSG_WriteByte (&sv.multicast, count);\n\tMSG_WriteByte (&sv.multicast, effect);\n\n\tSV_MulticastProtExt (org, MULTICAST_PVS, pr_global_struct->dimension_send, PEXT_HEXEN2, 0);\n}\n\n/*\n=================\nPF_particle4 - hexen2\n\nparticle(origin, radius, color, effect, count)\n=================\n*/\nstatic void QCBUILTIN PF_particle4 (pubprogfuncs_t *prinst, globalvars_t *pr_globals)\n{\n\tVM_VECTORARG(org, OFS_PARM0);\n\tint radius = G_FLOAT(OFS_PARM1);\n\tint color = G_FLOAT(OFS_PARM2);\n\tint effect = G_FLOAT(OFS_PARM3);\n\tint count = G_FLOAT(OFS_PARM4);\n\n\tMSG_WriteByte (&sv.multicast, svcfte_particle4);\n\tMSG_WriteCoord (&sv.multicast, org[0]);\n\tMSG_WriteCoord (&sv.multicast, org[1]);\n\tMSG_WriteCoord (&sv.multicast, org[2]);\n\tMSG_WriteByte (&sv.multicast, bound(0, radius, 255));\n\n\tMSG_WriteShort (&sv.multicast, color);\n\tMSG_WriteByte (&sv.multicast, bound(0, count, 255));\n\tMSG_WriteByte (&sv.multicast, effect);\n\n\tSV_MulticastProtExt (org, MULTICAST_PVS, pr_global_struct->dimension_send, PEXT_HEXEN2, 0);\n}\n\nstatic void QCBUILTIN PF_h2particleexplosion(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\t//used by the (regular) ice staff, and multiple other things.\n\tVM_VECTORARG(org, OFS_PARM0);\n\tint radius = G_FLOAT(OFS_PARM1);\n\tint color = G_FLOAT(OFS_PARM2);\n\tint effect = 255;\t//special explosion thing\n\tint count = G_FLOAT(OFS_PARM3);\n\n\tMSG_WriteByte (&sv.multicast, svcfte_particle4);\n\tMSG_WriteCoord (&sv.multicast, org[0]);\n\tMSG_WriteCoord (&sv.multicast, org[1]);\n\tMSG_WriteCoord (&sv.multicast, org[2]);\n\tMSG_WriteByte (&sv.multicast, bound(0, radius, 255));\n\n\tMSG_WriteShort (&sv.multicast, color);\n\tMSG_WriteByte (&sv.multicast, count);\n\tMSG_WriteByte (&sv.multicast, effect);\n\n\tSV_MulticastProtExt (org, MULTICAST_PVS, pr_global_struct->dimension_send, PEXT_HEXEN2, 0);\n}\n#endif\n\n/*\n=================\nPF_ambientsound\n\n=================\n*/\nvoid PF_ambientsound_Internal (float *pos, const char *samp, float vol, float attenuation)\n{\n\tstaticsound_state_t *state;\n\tint\t\tsoundnum;\n\n// check to see if samp was properly precached\n\tfor (soundnum=1 ; ; soundnum++)\n\t{\n\t\tif (soundnum == MAX_PRECACHE_SOUNDS || !sv.strings.sound_precache[soundnum])\n\t\t{\n\t\t\tCon_TPrintf (\"no precache: %s\\n\", samp);\n\t\t\treturn;\n\t\t}\n\t\tif (!strcmp(sv.strings.sound_precache[soundnum],samp))\n\t\t\tbreak;\n\t}\n\n\tif (sv.num_static_sounds == sv_max_staticsounds)\n\t{\n\t\tsv_max_staticsounds += 16;\n\t\tsv_staticsounds = BZ_Realloc(sv_staticsounds, sizeof(*sv_staticsounds) * sv_max_staticsounds);\n\t}\n\n\tstate = &sv_staticsounds[sv.num_static_sounds++];\n\tmemset(state, 0, sizeof(*state));\n\tVectorCopy(pos, state->position);\n\tstate->soundnum = soundnum;\n\tstate->volume = bound(0, (int)(vol*255), 255);\n\tstate->attenuation = bound(0,attenuation*64,255);\n}\n\nstatic void QCBUILTIN PF_ambientsound (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tVM_VECTORARG(pos, OFS_PARM0);\n\tconst char *samp = PR_GetStringOfs(prinst, OFS_PARM1);\n\tfloat vol = G_FLOAT(OFS_PARM2);\n\tfloat attenuation = G_FLOAT(OFS_PARM3);\n\n\tPF_ambientsound_Internal(pos, samp, vol, attenuation);\n}\n\n/*\n=================\nPF_sound\n\nEach entity can have eight independant sound sources, like voice,\nweapon, feet, etc.\n\nChannel 0 is an auto-allocate channel, the others override anything\nalready running on that entity/channel pair.\n\nAn attenuation of 0 will play full volume everywhere in the level.\nLarger attenuations will drop off.\npitchadj is a percent. values greater than 100 will result in a lower pitch, less than 100 gives a higher pitch.\n\n=================\n*/\nstatic void QCBUILTIN PF_sound (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char\t\t*sample;\n\tint\t\t\tchannel;\n\tedict_t\t\t*entity;\n\tint \t\tvolume;\n\tfloat attenuation;\n\tfloat\t\t\tpitchadj;\n\tunsigned int chflags;\n\tfloat\ttimeofs;\n\n\tentity = G_EDICT(prinst, OFS_PARM0);\n\tchannel = G_FLOAT(OFS_PARM1);\n\tsample = PR_GetStringOfs(prinst, OFS_PARM2);\n\tvolume = G_FLOAT(OFS_PARM3) * 255;\n\tattenuation = G_FLOAT(OFS_PARM4);\n\tif (svprogfuncs->callargc > 5)\n\t\tpitchadj = G_FLOAT(OFS_PARM5)*0.01;\n\telse\n\t\tpitchadj = 0;\n\tif (svprogfuncs->callargc > 6)\n\t{\n\t\tchflags = G_FLOAT(OFS_PARM6);\n\t\tif (channel < 0)\n\t\t\tchannel = 0;\n\t}\n\telse if (progstype == PROG_QW)\n\t{\n\t\t//QW uses channel&8 to mean reliable.\n\t\tchflags = (channel & 8)?CF_SV_RELIABLE:0;\n\t\t//strip out that unused bit.\n\t\tchannel = (channel & 7) | ((channel & ~15) >> 1);\n\t\tif (channel > 7 && !ssqc_deprecated_warned && sv.csqcchecksum)\n\t\t{\t//warn for extended channels (inconsistencies with csqc).\n\t\t\tPR_RunWarning(prinst, \"PF_sound: recommended to pass the flags arg when using extended channel numbers.\");\n\t\t\tssqc_deprecated_warned = true;\n\t\t}\n\t}\n\telse\n\t\tchflags = 0;\n\ttimeofs = (svprogfuncs->callargc>7)?G_FLOAT(OFS_PARM7):0;\n\n\tif (volume < 0)\t//erm...\n\t\treturn;\n\n\tif (volume > 255)\n\t\tvolume = 255;\n\n\tSVQ1_StartSound (NULL, (wedict_t*)entity, channel, sample, volume, attenuation, pitchadj, timeofs, chflags);\n}\n\nstatic void QCBUILTIN PF_pointsound(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tVM_VECTORARG(origin, OFS_PARM0);\n\tconst char *sample = PR_GetStringOfs(prinst, OFS_PARM1);\n\tfloat volume = G_FLOAT(OFS_PARM2)*255;\n\tfloat attenuation = G_FLOAT(OFS_PARM3);\n\tfloat pitchpct = (prinst->callargc > 4)?G_FLOAT(OFS_PARM4)*0.01:0;\n\n\tif (volume > 255)\n\t\tvolume = 255;\n\n\tSVQ1_StartSound (origin, sv.world.edicts, 0, sample, volume, attenuation, pitchpct, 0, 0);\n}\n\n//an evil one from telejano.\n#if defined(HAVE_CLIENT) && !defined(NOLEGACY)\nstatic void QCBUILTIN PF_ss_LocalSound(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tsfx_t\t*sfx;\n\n\tconst char * s = PR_GetStringOfs(prinst, OFS_PARM0);\n\tfloat chan = (prinst->callargc>1)?G_FLOAT(OFS_PARM1):0;\n\tfloat vol = (prinst->callargc>2)?G_FLOAT(OFS_PARM2):1;\n\n\tif (!isDedicated)\n\t{\n\t\tif ((sfx = S_PrecacheSound(s)))\n\t\t\tS_StartSound(cl.playerview[0].playernum, chan, sfx, cl.playerview[0].simorg, NULL, vol, 0.0, 0, 0, CF_NOSPACIALISE);\n\t}\n};\n#else\n#define PF_ss_LocalSound PF_Fixme\n#endif\n\nstatic void set_trace_globals(pubprogfuncs_t *prinst, /*struct globalvars_s *pr_globals,*/ trace_t *trace)\n{\n\tpr_global_struct->trace_allsolid = trace->allsolid;\n\tpr_global_struct->trace_startsolid = trace->startsolid;\n\tpr_global_struct->trace_fraction = trace->fraction;\n\tpr_global_struct->trace_inwater = trace->inwater;\n\tpr_global_struct->trace_inopen = trace->inopen;\n\tpr_global_struct->trace_surfaceflagsi = trace->surface?trace->surface->flags:0;\n\tif (pr_global_ptrs->trace_surfacename)\n\t\tprinst->SetStringField(prinst, NULL, &pr_global_struct->trace_surfacename, trace->surface?trace->surface->name:NULL, true);\n\tpr_global_struct->trace_endcontentsi = trace->contents;\n\tpr_global_struct->trace_brush_id = trace->brush_id;\n\tpr_global_struct->trace_brush_faceid = trace->brush_face;\n\tpr_global_struct->trace_surface_id = trace->surface_id;\n\tpr_global_struct->trace_bone_id = trace->bone_id;\n\tpr_global_struct->trace_triangle_id = trace->triangle_id;\n\n#ifdef HAVE_LEGACY\n\tpr_global_struct->trace_surfaceflagsf = trace->surface?trace->surface->flags:0;\n\tpr_global_struct->trace_endcontentsf = trace->contents;\n\n\tif (pr_global_ptrs->trace_dphittexturename)\n\t\tprinst->SetStringField(prinst, NULL, &pr_global_struct->trace_dphittexturename, trace->surface?trace->surface->name:NULL, true);\n\tif (pr_global_ptrs->trace_dpstartcontents)\n\t\tpr_global_struct->trace_dpstartcontents = FTEToDPContents(0);\t//fixme, maybe\n\tif (pr_global_ptrs->trace_dphitcontents)\n\t\tpr_global_struct->trace_dphitcontents = FTEToDPContents(pr_global_struct->trace_endcontentsi);\n\tif (pr_global_ptrs->trace_dphitq3surfaceflags)\n\t\tpr_global_struct->trace_dphitq3surfaceflags = pr_global_struct->trace_surfaceflagsf;\n#endif\n\n//\tif (trace.fraction != 1)\n//\t\tVectorMA (trace->endpos, 4, trace->plane.normal, P_VEC(trace_endpos));\n//\telse\n\t\tVectorCopy (trace->endpos, P_VEC(trace_endpos));\n\tVectorCopy (trace->plane.normal, P_VEC(trace_plane_normal));\n\tpr_global_struct->trace_plane_dist =  trace->plane.dist;\n\tif (trace->ent)\n\t\tpr_global_struct->trace_ent = EDICT_TO_PROG(svprogfuncs, trace->ent);\n\telse\n\t\tpr_global_struct->trace_ent = EDICT_TO_PROG(svprogfuncs, sv.world.edicts);\n\n\tif (trace->startsolid)\n\t\tif (!sv_gameplayfix_honest_tracelines.ival)\n\t\t\tpr_global_struct->trace_fraction = 1;\n}\n\n/*\n=================\nPF_traceline\n\nUsed for use tracing and shot targeting\nTraces are blocked by bbox and exact bsp entityes, and also slide box entities\nif the tryents flag is set.\n\ntraceline (vector1, vector2, tryents)\n=================\n*/\nvoid QCBUILTIN PF_svtraceline (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tvec_t\t*v1, *v2, *mins, *maxs;\n\ttrace_t\ttrace;\n\tint\t\tnomonsters;\n\tedict_t\t*ent;\n\n\tv1 = G_VECTOR(OFS_PARM0);\n\tv2 = G_VECTOR(OFS_PARM1);\n\tnomonsters = G_FLOAT(OFS_PARM2);\n\tif (svprogfuncs->callargc == 3) // QTEST\n\t\tent = PROG_TO_EDICT(prinst, pr_global_struct->self);\n\telse\n\t\tent = G_EDICT(prinst, OFS_PARM3);\n\n\tif (sv_antilag.ival == 2)\n\t\tnomonsters |= MOVE_LAGGED;\n\n\tif (svprogfuncs->callargc == 6)\n\t{\n\t\tmins = G_VECTOR(OFS_PARM4);\n\t\tmaxs = G_VECTOR(OFS_PARM5);\n\t}\n\telse\n\t{\n\t\tmins = vec3_origin;\n\t\tmaxs = vec3_origin;\n\t}\n\n\ttrace = World_Move (&sv.world, v1, mins, maxs, v2, nomonsters|MOVE_IGNOREHULL, (wedict_t*)ent);\n\n\tset_trace_globals(prinst, &trace);\n}\n\n#ifdef HEXEN2\nstatic void QCBUILTIN PF_traceboxh2 (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tVM_VECTORARG(v1, OFS_PARM0);\n\tVM_VECTORARG(v2, OFS_PARM1);\n\tVM_VECTORARG(mins, OFS_PARM2);\n\tVM_VECTORARG(maxs, OFS_PARM3);\n\tint nomonsters = G_FLOAT(OFS_PARM4);\n\tedict_t *ent = G_EDICT(prinst, OFS_PARM5);\n\n\ttrace_t\ttrace = World_Move (&sv.world, v1, mins, maxs, v2, nomonsters|MOVE_IGNOREHULL, (wedict_t*)ent);\n\tset_trace_globals(prinst, &trace);\n}\n#endif\n\nstatic void QCBUILTIN PF_traceboxdp (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tVM_VECTORARG(v1, OFS_PARM0);\n\tVM_VECTORARG(mins, OFS_PARM1);\n\tVM_VECTORARG(maxs, OFS_PARM2);\n\tVM_VECTORARG(v2, OFS_PARM3);\n\tint\t\tnomonsters = G_FLOAT(OFS_PARM4);\n\tedict_t\t*ent = G_EDICT(prinst, OFS_PARM5);\n\n//\tPR_StackTrace(prinst, 2);\n\n\ttrace_t trace = World_Move (&sv.world, v1, mins, maxs, v2, nomonsters|MOVE_IGNOREHULL, (wedict_t*)ent);\n\tset_trace_globals(prinst, &trace);\n}\n\nstatic void QCBUILTIN PF_TraceToss (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\ttrace_t\ttrace;\n\tedict_t\t*ent;\n\tedict_t\t*ignore;\n\n\tent = G_EDICT(prinst, OFS_PARM0);\n\tif (ent == (edict_t*)sv.world.edicts)\n\t\tCon_DPrintf(\"tracetoss: can not use world entity\\n\");\n\tignore = G_EDICT(prinst, OFS_PARM1);\n\n\ttrace = WPhys_Trace_Toss (&sv.world, (wedict_t*)ent, (wedict_t*)ignore);\n\n\tset_trace_globals(prinst, &trace);\n}\n\n//============================================================================\n\nstatic pvsbuffer_t\tcheckpvsbuffer;\nstatic vec3_t\tcheckorg;\nextern cvar_t sv_nopvs;\n\nvoid PF_newcheckclient (pubprogfuncs_t *prinst, world_t *w)\n{\n\tint\t\ti;\n//\tqbyte\t*pvs;\n\tedict_t\t*ent;\n\tint\t\tcluster;\n\n// cycle to the next one\n\n\tif (w->lastcheck < 1)\n\t\tw->lastcheck = 1;\n\tif (w->lastcheck > sv.allocated_client_slots)\n\t\tw->lastcheck = sv.allocated_client_slots;\n\n\tif (w->lastcheck == sv.allocated_client_slots)\n\t\ti = 1;\n\telse\n\t\ti = w->lastcheck + 1;\n\n\tfor ( ;  ; i++)\n\t{\n\t\tif (i >= sv.allocated_client_slots+1)\n\t\t\ti = 1;\n\n\t\tent = EDICT_NUM_UB(prinst, i);\n\n\t\tif (i == w->lastcheck)\n\t\t\tbreak;\t// didn't find anything else\n\n\t\tif (ED_ISFREE(ent))\n\t\t\tcontinue;\n\t\tif (ent->v->health <= 0)\n\t\t\tcontinue;\n\t\tif ((int)ent->v->flags & FL_NOTARGET)\n\t\t\tcontinue;\n\n\t// anything that is a client, or has a client as an enemy\n\t\tbreak;\n\t}\n\n// get the PVS for the entity\n\tVectorAdd (ent->v->origin, ent->v->view_ofs, checkorg);\n\tif (sv.world.worldmodel->type == mod_heightmap || sv_nopvs.ival)\n\t\tw->lastcheckpvs = NULL;\n\telse\n\t{\n\t\tcluster = sv.world.worldmodel->funcs.ClusterForPoint(sv.world.worldmodel, checkorg, NULL);\n\t\tw->lastcheckpvs = sv.world.worldmodel->funcs.ClusterPVS (sv.world.worldmodel, cluster, &checkpvsbuffer, PVM_FAST);\n\t}\n\n\tw->lastcheck = i;\n}\n\n/*\n=================\nPF_checkclient\n\nReturns a client (or object that has a client enemy) that would be a\nvalid target.\n\nIf there are more than one valid options, they are cycled each frame\n\nIf (self.origin + self.viewofs) is not in the PVS of the current target,\nit is not returned at all.\n\nname checkclient ()\n=================\n*/\n#define\tMAX_CHECK\t16\nint PF_checkclient_Internal (pubprogfuncs_t *prinst)\n{\n\tedict_t\t*ent, *self;\n\tint\t\tclust;\n\tvec3_t\tview;\n\tvec3_t\tdist;\n\tworld_t *w = &sv.world;\n\n// find a new check if on a new frame\n\tif (w->physicstime - w->lastchecktime >= 0.1)\n\t{\n\t\tPF_newcheckclient (prinst, w);\n\t\tw->lastchecktime = w->physicstime;\n\t}\n\n// return check if it might be visible\n\tent = EDICT_NUM_PB(prinst, w->lastcheck);\n\tif (ED_ISFREE(ent) || ent->v->health <= 0)\n\t{\n\t\treturn 0;\n\t}\n\n// if current entity can't possibly see the check entity, return 0\n\tself = PROG_TO_EDICT(prinst, pr_global_struct->self);\n\tVectorAdd (self->v->origin, self->v->view_ofs, view);\n\n\tVectorSubtract(view, checkorg, dist);\n\tif (DotProduct(dist, dist) > 2048*2048)\n\t\treturn 0;\n\n\tif (w->lastcheckpvs)\n\t{\n\t\tclust = w->worldmodel->funcs.ClusterForPoint(w->worldmodel, view, NULL);\n\t\tif ( (clust<0) || !(w->lastcheckpvs[clust>>3] & (1<<(clust&7)) ) )\n\t\t{\n\t\t\treturn 0;\n\t\t}\n\t}\n\n// might be able to see it. the qc will do some tracelines to ensure that it can.\n\treturn w->lastcheck;\n}\n\nstatic void QCBUILTIN PF_checkclient (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tRETURN_EDICT(prinst, EDICT_NUM_PB(prinst, PF_checkclient_Internal(prinst)));\n}\n\n//============================================================================\n\nvoid PF_stuffcmd_Internal(int entnum, const char *str, unsigned int flags)\n{\n\tclient_t\t*cl;\n\tunsigned int i;\n\n\tif (flags & STUFFCMD_BROADCAST)\n\t{\n\t\tif (!(flags & STUFFCMD_DEMOONLY))\n\t\t\tfor (i = 0, cl = svs.clients; i < sv.allocated_client_slots; i++, cl++)\n\t\t\t{\n\t\t\t\tif (cl->state != cs_spawned || cl->controller != cl)\n\t\t\t\t\tcontinue;\n\t\t\t\tif (flags & STUFFCMD_UNRELIABLE)\n\t\t\t\t\tSV_StuffcmdToClient_Unreliable(cl, str);\n\t\t\t\telse\n\t\t\t\t\tSV_StuffcmdToClient(cl, str);\n\t\t\t}\n#ifdef MVD_RECORDING\n\t\tif (!(flags & STUFFCMD_IGNOREINDEMO))\n\t\t\tif (sv.mvdrecording)\n\t\t\t{\n\t\t\t\tsizebuf_t *msg = MVDWrite_Begin (dem_all, 0, 2 + strlen(str));\n\t\t\t\tMSG_WriteByte (msg, svc_stufftext);\n\t\t\t\tMSG_WriteString (msg, str);\n\t\t\t}\n#endif\n\t\treturn;\n\t}\n\n\tif (entnum < 1 || entnum > sv.allocated_client_slots)\n\t\treturn;\n\n\tcl = &svs.clients[entnum-1];\n\n\tif (strcmp(str, \"disconnect\\n\") == 0)\n\t{\n\t\t// so long and thanks for all the fish\n\t\tif (cl->netchan.remote_address.type != NA_LOOPBACK)\t//don't drop the local client. It looks wrong.\n\t\t\tcl->drop = true;\n\t\treturn;\n\t}\n\n\tif (!(flags & STUFFCMD_DEMOONLY))\n\t{\n\t\tif (flags & STUFFCMD_UNRELIABLE)\n\t\t\tSV_StuffcmdToClient_Unreliable(cl, str);\n\t\telse\n\t\t\tSV_StuffcmdToClient(cl, str);\n\t}\n\n#ifdef MVD_RECORDING\n\tif (!(flags & STUFFCMD_IGNOREINDEMO))\n\tif (sv.mvdrecording)\n\t{\n\t\tsizebuf_t *msg = MVDWrite_Begin (dem_single, entnum - 1, 2 + strlen(str));\n\t\tMSG_WriteByte (msg, svc_stufftext);\n\t\tMSG_WriteString (msg, str);\n\t}\n#endif\n\n\t//this seems a little dangerous. v_cshift could leave a spectator's machine unusable if they switch players at unfortunate times.\n\tif (!(flags & STUFFCMD_DEMOONLY))\n\tif (sv_specprint.ival & SPECPRINT_STUFFCMD)\n\t{\n\t\tclient_t *spec;\n\t\tunsigned int i;\n\t\tfor (i = 0, spec = svs.clients; i < sv.allocated_client_slots; i++, spec++)\n\t\t{\n\t\t\tif (spec->state != cs_spawned || !spec->spectator)\n\t\t\t\tcontinue;\n\t\t\tif (spec->spec_track == entnum && (spec->spec_print & SPECPRINT_STUFFCMD))\n\t\t\t{\n\t\t\t\tif (flags & STUFFCMD_UNRELIABLE)\n\t\t\t\t\tSV_StuffcmdToClient_Unreliable(spec, str);\n\t\t\t\telse\n\t\t\t\t\tSV_StuffcmdToClient(spec, str);\n\t\t\t}\n\t\t}\n\t}\n}\n\n/*\n=================\nPF_stuffcmd\n\nSends text over to the client's execution buffer\n\nstuffcmd (clientent, value)\n=================\n*/\nstatic void QCBUILTIN PF_stuffcmd (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tPF_stuffcmd_Internal(G_EDICTNUM(prinst, OFS_PARM0), PF_VarString(prinst, 1, pr_globals), 0);\n}\n\nstatic void QCBUILTIN PF_stuffcmdflags (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tPF_stuffcmd_Internal(G_EDICTNUM(prinst, OFS_PARM0), PF_VarString(prinst, 2, pr_globals), G_FLOAT(OFS_PARM1));\n}\n\n//DP_QC_DROPCLIENT\nstatic void QCBUILTIN PF_dropclient (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint\t\tentnum;\n\tclient_t\t*cl;\n\n\tentnum = G_EDICTNUM(prinst, OFS_PARM0);\n\tif (entnum < 1 || entnum > sv.allocated_client_slots)\n\t\treturn;\n\n\tcl = &svs.clients[entnum-1];\n\n\t// so long and thanks for all the fish\n\tif (cl->netchan.remote_address.type == NA_LOOPBACK)\n\t{\n\t\tCbuf_AddText (\"disconnect\\n\", RESTRICT_INSECURE);\n\t\treturn;\t//don't drop the local client. It looks wrong.\n\t}\n\tcl->drop = true;\n\treturn;\n}\n\n\n\n//DP_SV_BOTCLIENT\n//entity() spawnclient = #454;\nstatic void QCBUILTIN PF_spawnclient (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\textern int nextuserid;\n\tint i;\n\tfor (i = 0; i < sv.allocated_client_slots; i++)\n\t{\n\t\tif (!*svs.clients[i].name && !svs.clients[i].protocol && svs.clients[i].state == cs_free)\n\t\t{\n\t\t\tsvs.clients[i].userid = ++nextuserid;\n\t\t\tsvs.clients[i].protocol = SCP_BAD;\t//marker for bots\n\t\t\tsvs.clients[i].state = cs_spawned;\n\t\t\tsvs.clients[i].connection_started = realtime;\n\t\t\tsvs.clients[i].spawned = true;\n\t\t\tsv.spawned_client_slots++;\n\t\t\tsvs.clients[i].netchan.message.allowoverflow = true;\n\t\t\tsvs.clients[i].netchan.message.maxsize = 0;\n\t\t\tsvs.clients[i].datagram.allowoverflow = true;\n\t\t\tsvs.clients[i].datagram.maxsize = 0;\n\n\t\t\tsvs.clients[i].edict = EDICT_NUM_PB(prinst, i+1);\n\n\t\t\tSV_SetUpClientEdict (&svs.clients[i], svs.clients[i].edict);\n\n\t\t\tRETURN_EDICT(prinst, svs.clients[i].edict);\n\t\t\treturn;\n\t\t}\n\t}\n\tRETURN_EDICT(prinst, sv.world.edicts);\n}\n\n//DP_SV_BOTCLIENT\n//float(entity client) clienttype = #455;\nstatic void QCBUILTIN PF_clienttype (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint entnum = G_EDICTNUM(prinst, OFS_PARM0);\n\tif (entnum < 1 || entnum > sv.allocated_client_slots)\n\t{\n\t\tG_FLOAT(OFS_RETURN) = CLIENTTYPE_NOTACLIENT;\t//not a client slot\n\t\treturn;\n\t}\n\tentnum--;\n\tif (svs.clients[entnum].state < cs_connected)\n\t{\n\t\tG_FLOAT(OFS_RETURN) = CLIENTTYPE_DISCONNECTED;\t//disconnected\n\t\treturn;\n\t}\n\tif (svs.clients[entnum].protocol == SCP_BAD)\n\t\tG_FLOAT(OFS_RETURN) = CLIENTTYPE_BOT;\t//an active, bot client.\n\telse\n\t\tG_FLOAT(OFS_RETURN) = CLIENTTYPE_REAL;\t//an active, not-bot client.\n}\n\n/*\n=================\nPF_cvar\n\nfloat cvar (string)\n=================\n*/\nstatic void QCBUILTIN PF_cvar (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char\t*str;\n\n\tstr = PR_GetStringOfs(prinst, OFS_PARM0);\n\n\tif (!strcmp(str, \"pr_checkextension\"))\t//no console changing\n\t{\n\t\tcvar_t *var = Cvar_FindVar(str);\n\t\tif (var && !var->ival)\n\t\t\tG_FLOAT(OFS_RETURN) = false;\n\t\telse\n\t\t\tG_FLOAT(OFS_RETURN) = PR_EnableEBFSBuiltin(\"checkextension\", 0);\n\t}\n\telse if (!strcmp(str, \"pr_builtin_find\"))\n\t\tG_FLOAT(OFS_RETURN) = PR_EnableEBFSBuiltin(\"builtin_find\", 0);\n\telse if (!strcmp(str, \"pr_map_builtin\"))\n\t\tG_FLOAT(OFS_RETURN) = PR_EnableEBFSBuiltin(\"map_builtin\", 0);\n\telse if (!strcmp(str, \"halflifebsp\"))\n\t\tG_FLOAT(OFS_RETURN) = sv.world.worldmodel->fromgame == fg_halflife;\n\telse\n\t{\n\t\tcvar_t *cv = PF_Cvar_FindOrGet(str);\n\t\tif (!cv || (cv->flags & CVAR_NOUNSAFEEXPAND))\n\t\t\tG_FLOAT(OFS_RETURN) = 0;\n\t\telse\n\t\t\tG_FLOAT(OFS_RETURN) = cv->value;\n\t}\n}\n\nstatic void QCBUILTIN PF_sv_getlight (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n#ifdef HAVE_CLIENT\n\t/*not shared with client - clients get more lights*/\n\tfloat *point = G_VECTOR(OFS_PARM0);\n\tvec3_t diffuse, ambient, dir;\n\tmodel_t *wm = sv.world.worldmodel;\n\n\tif (wm && wm->loadstate == MLS_LOADED && wm->funcs.LightPointValues && wm->lightmaps.maxstyle<cl_max_lightstyles)\n\t{\n\t\twm->funcs.LightPointValues(wm, point, diffuse, ambient, dir);\n\t\tVectorMA(ambient, 0.5, diffuse, G_VECTOR(OFS_RETURN));\n\t\treturn;\n\t}\n#endif\n\n\t//something failed.\n\tG_FLOAT(OFS_RETURN+0) = 128;\n\tG_FLOAT(OFS_RETURN+1) = 128;\n\tG_FLOAT(OFS_RETURN+2) = 128;\n}\n\n#ifdef HAVE_LEGACY\n/*\n=========\nPF_conprint\n=========\n*/\nstatic void QCBUILTIN PF_conprint (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tSys_Printf (\"%s\",PF_VarString(prinst, 0, pr_globals));\n}\n#endif\n\n#ifdef HEXEN2\n//dprintf(\"foo %s\\n\", 5.0) - its stupid and potentially unsafe\nstatic void QCBUILTIN PF_h2dprintf (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tchar temp[256];\n\tchar printable[2048];\n\tchar *pct;\n\n\tsprintf (temp, \"%g\", G_FLOAT(OFS_PARM1));\n\n\tQ_strncpyz(printable, PR_GetStringOfs(prinst, OFS_PARM0), sizeof(printable));\n\twhile((pct = strstr(printable, \"%s\")))\n\t{\n\t\tif ((pct-printable) + strlen(temp) + strlen(pct) > sizeof(printable))\n\t\t\tbreak;\n\t\tmemmove(pct + strlen(temp), pct+2, strlen(pct+2)+1);\n\t\tmemcpy(pct, temp, strlen(temp));\n\t}\n\tCon_DPrintf (\"%s\", printable);\n}\n\nstatic void QCBUILTIN PF_h2dprintv (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tchar temp[256];\n\tchar printable[2048];\n\tchar *pct;\n\n\tsprintf (temp, \"'%g %g %g'\", G_VECTOR(OFS_PARM1)[0], G_VECTOR(OFS_PARM1)[1], G_VECTOR(OFS_PARM1)[2]);\n\n\tQ_strncpyz(printable, PR_GetStringOfs(prinst, OFS_PARM0), sizeof(printable));\n\twhile((pct = strstr(printable, \"%s\")))\n\t{\n\t\tif ((pct-printable) + strlen(temp) + strlen(pct) > sizeof(printable))\n\t\t\tbreak;\n\t\tmemmove(pct + strlen(temp), pct+2, strlen(pct+2)+1);\n\t\tmemcpy(pct, temp, strlen(temp));\n\t}\n\tCon_DPrintf (\"%s\", printable);\n}\n\nstatic void QCBUILTIN PF_h2spawn_temp (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tedict_t\t*ed;\n\ted = ED_Alloc(prinst, false, 0);\n\tRETURN_EDICT(prinst, ed);\n}\n#endif\n\nstatic void QCBUILTIN PF_Remove (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tedict_t\t*ed;\n\n\ted = G_EDICT(prinst, OFS_PARM0);\n\tif (!ed)\n\t{\n\t\tCon_Printf(\"Tried removing invald entity at:\\n\");\n\t\tPR_StackTrace(prinst, false);\n\t}\n\tif (ED_ISFREE(ed))\n\t{\n\t\tED_CanFree(ed);\t//fake it\n\t\tif (developer.value)\n\t\t{\n\t\t\tCon_Printf(\"Tried removing free entity at:\\n\");\n\t\t\tPR_StackTrace(prinst, false);\n\t\t}\n\t\treturn;\t//yeah, alright, so this is hacky.\n\t}\n\n\tprinst->EntFree (prinst, ed, false);\n}\nstatic void QCBUILTIN PF_RemoveInstant (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tedict_t\t*ed;\n\n\ted = G_EDICT(prinst, OFS_PARM0);\n\n\tif (ED_ISFREE(ed))\n\t{\n\t\tED_CanFree(ed);\t//fake it\n\t\tif (developer.value)\n\t\t{\n\t\t\tCon_Printf(\"Tried removing free entity at:\\n\");\n\t\t\tPR_StackTrace(prinst, false);\n\t\t}\n\t\treturn;\t//yeah, alright, so this is hacky.\n\t}\n\n\tprinst->EntFree (prinst, ed, true);\n}\n\n\n/*\nvoid PR_CheckEmptyString (char *s)\n{\n\tif (s[0] <= ' ')\n\t\tPR_RunError (\"Bad string\");\n}\n*/\n\nstatic int SV_ParticlePrecache_Add(const char *pname)\n{\n\tint i;\n\tfor (i=1 ; i<MAX_SSPARTICLESPRE ; i++)\n\t{\n\t\tif (!sv.strings.particle_precache[i])\n\t\t{\n\t\t\tsv.strings.particle_precache[i] = PR_AddString(sv.world.progs, pname, 0, false);\n\n\t\t\tif (sv.state != ss_loading)\n\t\t\t{\n\t\t\t\tCon_DPrintf(\"Delayed particle precache: %s\\n\", pname);\n\t\t\t\tMSG_WriteByte(&sv.multicast, svcfte_precache);\n\t\t\t\tMSG_WriteShort(&sv.multicast, i|PC_PARTICLE);\n\t\t\t\tMSG_WriteString(&sv.multicast, pname);\n#ifdef NQPROT\n\t\t\t\tMSG_WriteByte(&sv.nqmulticast, svcdp_precache);\n\t\t\t\tMSG_WriteShort(&sv.nqmulticast, i|PC_PARTICLE);\n\t\t\t\tMSG_WriteString(&sv.nqmulticast, pname);\n#endif\n\n\t\t\t\tSV_MulticastProtExt(vec3_origin, MULTICAST_ALL_R, pr_global_struct->dimension_send, PEXT_CSQC, 0);\n\t\t\t}\n\t\t}\n\t\tif (!strcmp(sv.strings.particle_precache[i], pname))\n\t\t{\n\t\t\treturn i;\n\t\t}\n\t}\n\treturn 0;\n}\n\n//float(string effectname) particleeffectnum (EXT_CSQC)\nvoid QCBUILTIN PF_sv_particleeffectnum(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char *s = PR_GetStringOfs(prinst, OFS_PARM0);\n\tint\t\ti;\n\n\tG_FLOAT(OFS_RETURN) = 0;\n\n\tif (s[0] <= ' ')\n\t{\n\t\t/*if (!ssqc_deprecated_warned)\n\t\t{\n\t\t\tPR_RunWarning(prinst, \"PF_precache_particles: Bad string\");\n\t\t\tssqc_deprecated_warned = true;\n\t\t}*/\n\t\treturn;\n\t}\n\n#ifdef NQPROT\n\t//DPP7's network protocol depends upon the ordering of these from an external file. unreliable, but if we're meant to be compatible then we need to at least pretend.\n\tif (!sv.strings.particle_precache[1] && (sv_listen_dp.ival || !strncmp(s, \"effectinfo.\", 11)))\n\t\tCOM_Effectinfo_Enumerate(SV_ParticlePrecache_Add);\n#endif\n\n\ti = SV_ParticlePrecache_Add(s);\n\tG_FLOAT(OFS_RETURN) = i;\n\tif (!i)\n\t\tPR_BIError (prinst, \"PF_precache_particles: overflow\");\n}\n\nstatic void QCBUILTIN PF_precache_file (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\t// precache_file is only used to copy files with qcc, it does nothing\n\tconst char\t*s = PR_GetStringOfs(prinst, OFS_PARM0);\n\n\tG_INT(OFS_RETURN) = G_INT(OFS_PARM0);\n\n\t/*touch the file, so any packs will be referenced. this is fte-specific behaviour.*/\n\tFS_FLocateFile(s, FSLF_IFFOUND, NULL);\n}\n\nint PF_precache_sound_Internal (pubprogfuncs_t *prinst, const char *s, qboolean queryonly)\n{\n\tint\t\ti;\n\n\tif (s[0] <= ' ')\n\t{\n\t\tif (*s)\n\t\t\tPR_BIError (prinst, \"PF_precache_sound: Bad string\");\n\t\treturn 0;\n\t}\n\n\tfor (i=1 ; i<MAX_PRECACHE_SOUNDS ; i++)\n\t{\n\t\tif (!sv.strings.sound_precache[i])\n\t\t{\n\t\t\tif (queryonly)\n\t\t\t\treturn 0;\n#ifdef VM_Q1\n\t\t\tif (svs.gametype == GT_Q1QVM)\n\t\t\t\tsv.strings.sound_precache[i] = s;\n\t\t\telse\n#endif\n\t\t\t\tsv.strings.sound_precache[i] = PR_AddString(prinst, s, 0, false);\n\n\t\t\t/*touch the file, so any packs will be referenced*/\n\t\t\tFS_FLocateFile(s, FSLF_IFFOUND, NULL);\n\n\t\t\tif (sv.state != ss_loading)\n\t\t\t{\n\t\t\t\tCon_DPrintf(\"Delayed sound precache: %s\\n\", s);\n\t\t\t\tMSG_WriteByte(&sv.reliable_datagram, svcfte_precache);\n\t\t\t\tMSG_WriteShort(&sv.reliable_datagram, i+PC_SOUND);\n\t\t\t\tMSG_WriteString(&sv.reliable_datagram, s);\n#ifdef NQPROT\n\t\t\t\tMSG_WriteByte(&sv.nqreliable_datagram, svcdp_precache);\n\t\t\t\tMSG_WriteShort(&sv.nqreliable_datagram, i+PC_SOUND);\n\t\t\t\tMSG_WriteString(&sv.nqreliable_datagram, s);\n#endif\n\t\t\t}\n\t\t\treturn i;\n\t\t}\n\t\tif (!strcmp(sv.strings.sound_precache[i], s))\n\t\t\treturn i;\n\t}\n\tif (!queryonly)\n\t\tPR_BIError (prinst, \"PF_precache_sound: overflow\");\n\treturn 0;\n}\nstatic void QCBUILTIN PF_precache_sound (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char\t*s;\n\tint idx;\n\n\ts = PR_GetStringOfs(prinst, OFS_PARM0);\n\n\tidx = PF_precache_sound_Internal(prinst, s, false);\n\n\tif (dpcompat_precachesoundhack.ival)\n\t\tG_FLOAT(OFS_RETURN) = idx;\t//returns the index as a float.\n\telse\n\t\tG_INT(OFS_RETURN) = G_INT(OFS_PARM0);\t//returns the filename as a string.\n}\nstatic void QCBUILTIN PF_getsoundindex (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char\t*s = PR_GetStringOfs(prinst, OFS_PARM0);\n\tqboolean queryonly = (svprogfuncs->callargc >= 2)?G_FLOAT(OFS_PARM1):false;\n\n\tG_FLOAT(OFS_RETURN) = PF_precache_sound_Internal(prinst, s, queryonly);\n}\nstatic void QCBUILTIN PF_soundnameforindex (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint idx = G_FLOAT(OFS_PARM0);\n\tif (idx >= 0 && idx < MAX_PRECACHE_SOUNDS && sv.strings.sound_precache[idx])\n\t\tRETURN_TSTRING(sv.strings.sound_precache[idx]);\n\telse\n\t\tG_INT(OFS_RETURN) = 0;\n}\n\nint PF_precache_model_Internal (pubprogfuncs_t *prinst, const char *s, qboolean queryonly)\n{\n\tint\t\ti;\n\n\tif (s[0] <= ' ')\n\t{\n\t\tCon_DPrintf (\"precache_model: empty string\\n\");\n\t\treturn 0;\n\t}\n\n\tfor (i=1 ; i<MAX_PRECACHE_MODELS ; i++)\n\t{\n\t\tif (!sv.strings.model_precache[i])\n\t\t{\n\t\t\tif (strlen(s)>=MAX_QPATH-1)\t//probably safest to keep this.\n\t\t\t{\n\t\t\t\tPR_BIError (prinst, \"Precache name too long\");\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t\tif (queryonly)\n\t\t\t\treturn 0;\n#ifdef VM_Q1\n\t\t\tif (svs.gametype == GT_Q1QVM)\n\t\t\t\tsv.strings.model_precache[i] = s;\n\t\t\telse\n#endif\n\t\t\t\tsv.strings.model_precache[i] = PR_AddString(prinst, s, 0, false);\n\t\t\ts = sv.strings.model_precache[i];\n\t\t\tif (!strcmp(s + strlen(s) - 4, \".bsp\") || sv_gameplayfix_setmodelrealbox.ival)\n\t\t\t\tsv.models[i] = Mod_ForName(Mod_FixName(s, sv.modelname), MLV_WARNSYNC);\n\t\t\telse\n\t\t\t{\n\t\t\t\t/*touch the file, so any packs will be referenced*/\n\t\t\t\tFS_FLocateFile(s, FSLF_IFFOUND, NULL);\n\t\t\t}\n\n\t\t\tif (sv.state != ss_loading)\n\t\t\t{\n\t\t\t\tCon_DPrintf(\"Delayed model precache: %s\\n\", s);\n\t\t\t\tMSG_WriteByte(&sv.reliable_datagram, svcfte_precache);\n\t\t\t\tMSG_WriteShort(&sv.reliable_datagram, i);\n\t\t\t\tMSG_WriteString(&sv.reliable_datagram, s);\n#ifdef NQPROT\n\t\t\t\tMSG_WriteByte(&sv.nqreliable_datagram, svcdp_precache);\n\t\t\t\tMSG_WriteShort(&sv.nqreliable_datagram, i);\n\t\t\t\tMSG_WriteString(&sv.nqreliable_datagram, s);\n#endif\n\t\t\t}\n\n\t\t\treturn i;\n\t\t}\n\t\tif (!strcmp(sv.strings.model_precache[i], s))\n\t\t{\n\t\t\treturn i;\n\t\t}\n\t}\n\tif (!queryonly)\n\t\tPR_BIError (prinst, \"PF_precache_model: overflow\");\n\treturn 0;\n}\nstatic void QCBUILTIN PF_precache_model (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char\t*s;\n\n\ts = PR_GetStringOfs(prinst, OFS_PARM0);\n\n\tG_INT(OFS_RETURN) = G_INT(OFS_PARM0);\n\n\tPF_precache_model_Internal(prinst, s, false);\n}\n\n#ifdef HEXEN2\nstatic void QCBUILTIN PF_h2precache_puzzle_model (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\t//qc/hc lacks string manipulation.\n\tconst char *shortname;\n\tchar fullname[MAX_QPATH];\n\tshortname = PR_GetStringOfs(prinst, OFS_PARM0);\n\tsnprintf(fullname, sizeof(fullname)-1, \"models/puzzle/%s.mdl\", shortname);\n\n\tPF_precache_model_Internal(prinst, fullname, false);\n}\n#endif\n\nstatic void QCBUILTIN PF_getmodelindex (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char\t*s = PR_GetStringOfs(prinst, OFS_PARM0);\n\tqboolean queryonly = (svprogfuncs->callargc >= 2)?G_FLOAT(OFS_PARM1):false;\n\n\tG_FLOAT(OFS_RETURN) = PF_precache_model_Internal(prinst, s, queryonly);\n}\nstatic void QCBUILTIN PF_modelnameforindex (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint idx = G_FLOAT(OFS_PARM0);\n\tif (idx >= 0 && idx < MAX_PRECACHE_MODELS && sv.strings.model_precache[idx])\n\t\tRETURN_TSTRING(sv.strings.model_precache[idx]);\n\telse\n\t\tG_INT(OFS_RETURN) = 0;\n}\n#ifdef HAVE_LEGACY\nvoid QCBUILTIN PF_precache_vwep_model (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint i;\n\tconst char\t*s;\n\n\ts = PR_GetStringOfs(prinst, OFS_PARM0);\n\tif (!*s || strchr(s, '\\\"') || strchr(s, ';') || strchr(s, '\\t') || strchr(s, '\\n'))\n\t{\n\t\tCon_Printf(\"PF_precache_vwep_model: bad string\\n\");\n\t\tG_FLOAT(OFS_RETURN) = 0;\n\t}\n\telse\n\t{\n\t\tfor (i = 0; i < sizeof(sv.strings.vw_model_precache)/sizeof(sv.strings.vw_model_precache[0]); i++)\n\t\t{\n\t\t\tif (!sv.strings.vw_model_precache[i])\n\t\t\t{\n\t\t\t\tif (sv.state != ss_loading)\n\t\t\t\t{\n\t\t\t\t\tCon_Printf(\"PF_precache_vwep_model: not spawn-time\\n\");\n\t\t\t\t\tG_FLOAT(OFS_RETURN) = 0;\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tsv.strings.vw_model_precache[i] = PR_AddString(prinst, s, 0, false);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (!strcmp(sv.strings.vw_model_precache[i], s))\n\t\t\t{\n\t\t\t\tG_FLOAT(OFS_RETURN) = i;\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tCon_Printf(\"PF_precache_vwep_model: overflow\\n\");\n\t\tG_FLOAT(OFS_RETURN) = 0;\n\t}\n}\n#endif\n\n/*\nstatic void QCBUILTIN PF_svcoredump (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint size = 1024*1024*8;\n\tchar *buffer = BZ_Malloc(size);\n\tprinst->save_ents(prinst, buffer, &size, 3);\n\tCOM_WriteFile(\"ssqccore.txt\", buffer, size);\n\tBZ_Free(buffer);\n}\n*/\n\nstatic void QCBUILTIN PF_sv_movetogoal (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\twedict_t\t*ent;\n\tfloat dist;\n\tent = (wedict_t*)PROG_TO_EDICT(prinst, pr_global_struct->self);\n\tdist = G_FLOAT(OFS_PARM0);\n\tWorld_MoveToGoal (&sv.world, ent, dist);\n}\n\n/*\n===============\nPF_walkmove\n\nfloat(float yaw, float dist) walkmove\n===============\n*/\nstatic void QCBUILTIN PF_walkmove (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\twedict_t\t*ent;\n\tfloat\tyaw, dist;\n\tvec3_t\tmove;\n//\tdfunction_t\t*oldf;\n\tint \toldself;\n\tqboolean settrace;\n\tvec3_t\taxis[3];\n\tfloat s;\n\n\tent = PROG_TO_WEDICT(prinst, pr_global_struct->self);\n\tyaw = G_FLOAT(OFS_PARM0);\n\tdist = G_FLOAT(OFS_PARM1);\n\tif (svprogfuncs->callargc >= 3 && G_FLOAT(OFS_PARM2))\n\t\tsettrace = true;\n\telse\n\t\tsettrace = false;\n\n\tif ( !( (int)ent->v->flags & (FL_ONGROUND|FL_FLY|FL_SWIM) ) )\n\t{\n\t\tG_FLOAT(OFS_RETURN) = 0;\n\t\treturn;\n\t}\n\n\tyaw = yaw*M_PI*2 / 360;\n\tWorld_GetEntGravityAxis(ent, axis);\n\n\ts = cos(yaw)*dist;\n\tVectorScale(axis[0], s, move);\n\ts = sin(yaw)*dist;\n\tVectorMA(move, s, axis[1], move);\n\n// save program state, because SV_movestep may call other progs\n//\toldf = pr_xfunction;\n\toldself = pr_global_struct->self;\n\n//\tif (!dist)\n//\t{\n//\t\tG_FLOAT(OFS_RETURN) = !SV_TestEntityPosition(ent);\n//\t}\n//\telse if (!SV_TestEntityPosition(ent))\n//\t{\n\t\tG_FLOAT(OFS_RETURN) = World_movestep(&sv.world, (wedict_t*)ent, move, axis, true, false, settrace?set_trace_globals:NULL);\n//\t\tif (SV_TestEntityPosition(ent))\n//\t\t\tCon_Printf(\"Entity became stuck\\n\");\n//\t}\n\n\n// restore program state\n//\tpr_xfunction = oldf;\n\tpr_global_struct->self = oldself;\n}\nstatic void QCBUILTIN PF_walkmovedist (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\t//for wrath, doesn't actually provide anything useful other than to stop crashes.\n\twedict_t *ent = PROG_TO_WEDICT(prinst, pr_global_struct->self);\n\tvec3_t start;\n\tVectorCopy(ent->v->origin, start);\n\tPF_walkmove(prinst, pr_globals);\n\tVectorSubtract(ent->v->origin, start, start);\n\tG_FLOAT(OFS_RETURN) = VectorLength(start);\n}\n\nvoid QCBUILTIN PF_applylightstyle(int style, const char *val, vec3_t rgb)\n{\n\tclient_t\t*client;\n\tint\t\t\tj;\n\n\tif (style < 0 || style >= MAX_NET_LIGHTSTYLES)\n\t{\n\t\tCon_Printf(\"WARNING: Bad lightstyle %i.\\n\", style);\n\t\treturn;\n\t}\n\tif (strlen(val) >= 64)\n\t\tCon_Printf(\"WARNING: Style string is longer than standard (%i). Some clients could crash.\\n\", 63);\n\n\tif (style+1 > sv.maxlightstyles)\n\t\tZ_ReallocElements((void**)&sv.lightstyles, &sv.maxlightstyles, style+1, sizeof(*sv.lightstyles));\n\n// change the string in sv\n\tif (sv.lightstyles[style].str)\n\t\tBZ_Free((void*)sv.lightstyles[style].str);\n\tsv.lightstyles[style].str = Z_StrDup(val);\n\n#ifdef PEXT_LIGHTSTYLECOL\n\tVectorCopy(rgb, sv.lightstyles[style].colours);\n#endif\n\n// send message to all clients on this server\n\tif (sv.state != ss_active)\n\t\treturn;\n\n\tfor (j=0, client = svs.clients ; j<sv.allocated_client_slots ; j++, client++)\n\t{\n\t\tif (client->protocol == SCP_BAD)\n\t\t\tcontinue;\n\t\tif (client->controller)\n\t\t\tcontinue;\n\t\tif (client->state == cs_spawned)\n\t\t\tSV_SendLightstyle(client, NULL, style, false);\n\t}\n\n#ifdef MVD_RECORDING\n\tif (sv.mvdrecording)\n\t\tSV_SendLightstyle(&demo.recorder, NULL, style, true);\n#endif\n}\n\n/*\n===============\nPF_lightstyle\n\nvoid(float style, string value [, float colour]) lightstyle\n===============\n*/\nstatic void QCBUILTIN PF_lightstyle (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint\t\tstyle;\n\tconst char\t*val;\n\tvec3_t rgb = {1,1,1};\n\n#ifdef PEXT_LIGHTSTYLECOL\n\tif (svprogfuncs->callargc >= 3)\n\t\tVectorCopy(G_VECTOR(OFS_PARM2), rgb);\n#endif\n\n\tstyle = G_FLOAT(OFS_PARM0);\n\tval = PR_GetStringOfs(prinst, OFS_PARM1);\n\n\tPF_applylightstyle(style, val, rgb);\n}\n\nstatic void PR_Lightstyle_f(void)\n{\n\tint style = atoi(Cmd_Argv(1));\n\n\tif (!SV_MayCheat())\n\t\tCon_TPrintf (\"Please set sv_cheats 1 and restart the map first.\\n\");\n\telse switch(svs.gametype)\n\t{\n\tdefault:\n\t\tCon_TPrintf (\"not supported in the current game mode.\\n\");\n\t\tbreak;\n#ifdef Q2SERVER\n\tcase GT_QUAKE2:\n\t\tif (Cmd_Argc() <= 2)\n\t\t{\n\t\t\tif ((unsigned)style < (unsigned)Q2MAX_LIGHTSTYLES && Cmd_Argc() >= 2)\n\t\t\t\tCon_Printf (\"Style %i: %s\\n\", style, sv.strings.configstring[Q2CS_LIGHTS+style]);\n\t\t\telse for (style = 0; style < Q2MAX_LIGHTSTYLES; style++)\n\t\t\t\tif (sv.strings.configstring[Q2CS_LIGHTS+style])\n\t\t\t\t\tCon_Printf(\"Style %i: %s\\n\", style, sv.strings.configstring[Q2CS_LIGHTS+style]);\n\t\t}\n\t\telse if ((unsigned)style < (unsigned)Q2MAX_LIGHTSTYLES)\n\t\t\tPFQ2_Configstring (Q2CS_LIGHTS+style, Cmd_Argv(2));\n\t\tbreak;\n#endif\n\tcase GT_PROGS:\n\tcase GT_Q1QVM:\n\t\tif (Cmd_Argc() <= 2)\n\t\t{\n\t\t\tif (style >= 0 && style < sv.maxlightstyles && Cmd_Argc() >= 2)\n\t\t\t\tCon_Printf(\"Style %i: %s %g %g %g\\n\", style, sv.lightstyles[style].str, sv.lightstyles[style].colours[0], sv.lightstyles[style].colours[1], sv.lightstyles[style].colours[2]);\n\t\t\telse for (style = 0; style < sv.maxlightstyles; style++)\n\t\t\t\tif (sv.lightstyles[style].str)\n\t\t\t\t\tCon_Printf(\"Style %i: %s %g %g %g\\n\", style, sv.lightstyles[style].str, sv.lightstyles[style].colours[0], sv.lightstyles[style].colours[1], sv.lightstyles[style].colours[2]);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tvec3_t rgb = {1,1,1};\n\t\t\tif (Cmd_Argc() > 5)\n\t\t\t{\n\t\t\t\trgb[0] = atof(Cmd_Argv(3));\n\t\t\t\trgb[1] = atof(Cmd_Argv(4));\n\t\t\t\trgb[2] = atof(Cmd_Argv(5));\n\t\t\t}\n\t\t\telse if (Cmd_Argc() > 3)\n\t\t\t\trgb[0] = rgb[1] = rgb[2] = atof(Cmd_Argv(3));\n\t\t\tPF_applylightstyle(style, Cmd_Argv(2), rgb);\n\t\t}\n\t\tbreak;\n\t}\n}\n\nstatic void QCBUILTIN PF_getlightstyle (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tunsigned int\t\tstyle = G_FLOAT(OFS_PARM0);\n\n\tif (style >= sv.maxlightstyles)\n\t{\n\t\tVectorSet(G_VECTOR(OFS_PARM1), 0, 0, 0);\n\t\tG_INT(OFS_RETURN) = 0;\n\t}\n\telse\n\t{\n\t\tVectorCopy(sv.lightstyles[style].colours, G_VECTOR(OFS_PARM1));\n\t\tif (!sv.lightstyles[style].str)\n\t\t\tG_INT(OFS_RETURN) = 0;\n\t\telse\n\t\t\tRETURN_TSTRING(sv.lightstyles[style].str);\n\t}\n}\nstatic void QCBUILTIN PF_getlightstylergb (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tunsigned int\t\tstyle = G_FLOAT(OFS_PARM0);\n\tint value;\n\tif (style >= sv.maxlightstyles || !sv.lightstyles[style].str)\n\t\tvalue = ('m'-'a')*22;\n\telse if (sv.lightstyles[style].str[0] == '=')\n\t\tvalue = atof(sv.lightstyles[style].str+1)*256;\n\telse\n\t\tvalue = sv.lightstyles[style].str[max(0,(int)(sv.time*10)) % strlen(sv.lightstyles[style].str)] - 'a';\n\tVectorScale(sv.lightstyles[style].colours, value*(1.0/256), G_VECTOR(OFS_RETURN));\n}\n\n#ifdef HEXEN2\nstatic void QCBUILTIN PF_lightstylevalue (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint style;\n\tstyle = G_FLOAT(OFS_PARM0);\n\tif(style < 0 || style >= sv.maxlightstyles || !sv.lightstyles[style].str || !*sv.lightstyles[style].str)\n\t{\n\t\tG_FLOAT(OFS_RETURN) = 0;\n\t\treturn;\n\t}\n\tG_FLOAT(OFS_RETURN) = *sv.lightstyles[style].str - 'a';\n}\n\nstatic void QCBUILTIN PF_lightstylestatic (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint\t\tstyle;\n\tint\tnum;\n\tchar\tval[2];\n\tvec3_t rgb = {1,1,1};\n\n\tstyle = G_FLOAT(OFS_PARM0);\n\tnum = G_FLOAT(OFS_PARM1);\n#ifdef PEXT_LIGHTSTYLECOL\n\tif (svprogfuncs->callargc >= 3)\n\t\tVectorCopy(G_VECTOR(OFS_PARM2), rgb);\n#endif\n\n\t//with fte+dp, va(\"=%g\", (num*2.0)/26) should work\n\t//but will break other clients. so that's a problem.\n\n\tval[0] = 'a' + bound(0, num, ('z'-'a')-1);\n\tval[1] = 0;\n\tPF_applylightstyle(style, val, rgb);\n}\n#endif\n\n/*\n=============\nPF_pointcontents\n=============\n*/\nstatic void QCBUILTIN PF_pointcontents (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tworld_t *w = prinst->parms->user;\n\n\tint cont;\n\n\tVM_VECTORARG(v, OFS_PARM0);\n\n\tcont = World_PointContentsWorldOnly(w, v);\n\tif (cont & FTECONTENTS_SOLID)\n\t\tG_FLOAT(OFS_RETURN) = Q1CONTENTS_SOLID;\n\telse if (cont & FTECONTENTS_SKY)\n\t\tG_FLOAT(OFS_RETURN) = Q1CONTENTS_SKY;\n\telse if (cont & FTECONTENTS_LAVA)\n\t\tG_FLOAT(OFS_RETURN) = Q1CONTENTS_LAVA;\n\telse if (cont & FTECONTENTS_SLIME)\n\t\tG_FLOAT(OFS_RETURN) = Q1CONTENTS_SLIME;\n\telse if (cont & FTECONTENTS_WATER)\n\t\tG_FLOAT(OFS_RETURN) = Q1CONTENTS_WATER;\n\telse if (cont & FTECONTENTS_LADDER)\n\t\tG_FLOAT(OFS_RETURN) = Q1CONTENTS_LADDER;\n\telse\n\t\tG_FLOAT(OFS_RETURN) = Q1CONTENTS_EMPTY;\n}\nstatic void QCBUILTIN PF_pointcontentsmask (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tworld_t *w = prinst->parms->user;\n\tVM_VECTORARG(v, OFS_PARM0);\n\tif (prinst->callargc < 1 || G_FLOAT(OFS_PARM1))\n\t\tG_UINT(OFS_RETURN) = World_PointContentsWorldOnly(w, v);\n\telse\n\t\tG_UINT(OFS_RETURN) = World_PointContentsAllBSPs(w, v);\n}\n\n/*\n=============\nPF_aim\n\nPick a vector for the player to shoot along\nvector aim(entity, missilespeed)\n=============\n*/\nvoid QCBUILTIN PF_aim (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tedict_t\t*ent, *check, *bestent;\n\tvec3_t\tstart, dir, end, bestdir;\n\tint\t\ti, j;\n\ttrace_t\ttr;\n\tfloat\tdist, bestdist;\n\n\tent = G_EDICT(prinst, OFS_PARM0);\n//\tspeed = G_FLOAT(OFS_PARM1);\n\n\n\t//figure out the angle we're allowed to aim at\n\ti = NUM_FOR_EDICT(prinst, ent);\n\tif (i>0 && i<sv.allocated_client_slots)\n\t\tbestdist = svs.clients[i-1].autoaimdot;\n\telse\n\t\tbestdist = sv_aim.value;\n\tif (bestdist >= 1)\n\t{\t//autoaim disabled, early out and just aim straight\n\t\tVectorCopy (P_VEC(v_forward), G_VECTOR(OFS_RETURN));\n\t\treturn;\n\t}\n\tdist = cos(sv_maxaim.value);\n\tbestdist = max(bestdist, dist);\n\n\n\tVectorCopy (ent->v->origin, start);\n\tstart[2] += ent->v->view_ofs[2]; //the view position is at 22, but the gun position is normally at 16.\n\n// try sending a trace straight\n\tVectorCopy (P_VEC(v_forward), dir);\n\tVectorMA (start, 2048, dir, end);\n\ttr = World_Move (&sv.world, start, vec3_origin, vec3_origin, end, false, (wedict_t*)ent);\n\tif (tr.ent && ((edict_t *)tr.ent)->v->takedamage == DAMAGE_AIM\n\t&& (!teamplay.value || ent->v->team <=0 || ent->v->team != ((edict_t *)tr.ent)->v->team) )\n\t{\n\t\tVectorCopy (P_VEC(v_forward), G_VECTOR(OFS_RETURN));\n\t\treturn;\n\t}\n\n\n// try all possible entities\n\tVectorCopy (dir, bestdir);\n\tbestdist = sv_aim.value;\n\tbestent = NULL;\n\n\tfor (i=1 ; i<sv.world.num_edicts ; i++ )\n\t{\n\t\tcheck = EDICT_NUM_PB(prinst, i);\n\t\tif (check->v->takedamage != DAMAGE_AIM)\n\t\t\tcontinue;\n\t\tif (check == ent)\n\t\t\tcontinue;\n\t\tif (teamplay.value && ent->v->team > 0 && ent->v->team == check->v->team)\n\t\t\tcontinue;\t// don't aim at teammate\n\t\tfor (j=0 ; j<3 ; j++)\n\t\t\tend[j] = check->v->origin[j]\n\t\t\t+ 0.5*(check->v->mins[j] + check->v->maxs[j]);\n\t\tVectorSubtract (end, start, dir);\n\t\tVectorNormalize (dir);\n\t\tdist = DotProduct (dir, P_VEC(v_forward));\n\t\tif (dist < bestdist)\n\t\t\tcontinue;\t// to far to turn\n\t\ttr = World_Move (&sv.world, start, vec3_origin, vec3_origin, end, false, (wedict_t*)ent);\n\t\tif (tr.ent == check)\n\t\t{\t// can shoot at this one\n\t\t\tbestdist = dist;\n\t\t\tbestent = check;\n\t\t}\n\t}\n\n\tif (bestent)\n\t{\n\t\tVectorSubtract (bestent->v->origin, ent->v->origin, dir);\n\t\tdist = DotProduct (dir, P_VEC(v_forward));\n\t\tVectorScale (P_VEC(v_forward), dist, end);\n\t\tend[2] = dir[2];\n\t\tVectorNormalize (end);\n\t\tVectorCopy (end, G_VECTOR(OFS_RETURN));\n\t}\n\telse\n\t{\n\t\tVectorCopy (bestdir, G_VECTOR(OFS_RETURN));\n\t}\n}\n\n/*\n===============================================================================\n\nMESSAGE WRITING\n\n===============================================================================\n*/\n\n#define\tMSG_BROADCAST\t0\t\t// unreliable to all\n#define\tMSG_ONE\t\t\t1\t\t// reliable to one (msg_entity)\n#define\tMSG_ALL\t\t\t2\t\t// reliable to all\n#define\tMSG_INIT\t\t3\t\t// write to the init string\n#define\tMSG_MULTICAST\t4\t\t// for multicast()\n\nsizebuf_t *QWWriteDest (int\t\tdest)\n{\n\tswitch (dest)\n\t{\n\tcase MSG_PRERELONE:\n\t\t{\n\t\tint entnum;\n\t\tentnum = PR_globals(svprogfuncs, PR_CURRENT)->param[0].i;\n\t\treturn &svs.clients[entnum-1].netchan.message;\n\t\t}\n\tcase MSG_BROADCAST:\n\t\treturn &sv.datagram;\n\n\tcase MSG_ONE:\n\t\tSV_Error(\"Shouldn't be at MSG_ONE\");\n#if 0\n\t\tent = PROG_TO_EDICT(pr_global_struct->msg_entity);\n\t\tentnum = NUM_FOR_EDICT(ent);\n\t\tif (entnum < 1 || entnum > sv.allocated_client_slots)\n\t\t{\n\t\t\tPR_BIError (\"WriteDest: not a client\");\n\t\t\treturn &sv.reliable_datagram;\n\t\t}\n\t\treturn &svs.clients[entnum-1].netchan.message;\n#endif\n\n\tcase MSG_ALL:\n\t\treturn &sv.reliable_datagram;\n\n\tcase MSG_INIT:\n\t\tif (sv.state != ss_loading)\n\t\t{\n\t\t\tPR_BIError (svprogfuncs, \"PF_Write_*: MSG_INIT can only be written in spawn functions\");\n\t\t\treturn NULL;\n\t\t}\n\t\treturn &sv.signon;\n\n\tcase MSG_MULTICAST:\n\t\treturn &sv.multicast;\n\n\tdefault:\n\t\tPR_BIError (svprogfuncs, \"WriteDest: bad destination\");\n\t\tbreak;\n\t}\n\n\treturn NULL;\n}\n#ifdef NQPROT\nsizebuf_t *NQWriteDest (int dest)\n{\n\tswitch (dest)\n\t{\n\tcase MSG_PRERELONE:\n\t\t{\n\t\tint entnum;\n\t\tentnum = PR_globals(svprogfuncs, PR_CURRENT)->param[0].i;\n\t\treturn &svs.clients[entnum-1].netchan.message;\n\t\t}\n\n\tcase MSG_BROADCAST:\n\t\treturn &sv.nqdatagram;\n\n\tcase MSG_ONE:\n\t\tSV_Error(\"Shouldn't be at MSG_ONE\");\n#if 0\n\t\tent = PROG_TO_EDICT(pr_global_struct->msg_entity);\n\t\tentnum = NUM_FOR_EDICT(ent);\n\t\tif (entnum < 1 || entnum > sv.allocated_client_slots)\n\t\t{\n\t\t\tPR_BIError (prinst, \"WriteDest: not a client\");\n\t\t\treturn &sv.nqreliable_datagram;\n\t\t}\n\t\treturn &svs.clients[entnum-1].netchan.message;\n#endif\n\n\tcase MSG_ALL:\n\t\treturn &sv.nqreliable_datagram;\n\n\tcase MSG_INIT:\n\t\tif (sv.state != ss_loading)\n\t\t{\n\t\t\tPR_BIError (svprogfuncs, \"PF_Write_*: MSG_INIT can only be written in spawn functions\");\n\t\t\treturn NULL;\n\t\t}\n\t\treturn &sv.signon;\n\n\tcase MSG_MULTICAST:\n\t\treturn &sv.nqmulticast;\n\n\tdefault:\n\t\tPR_BIError (svprogfuncs, \"WriteDest: bad destination\");\n\t\tbreak;\n\t}\n\n\treturn NULL;\n}\n#else\nstatic sizebuf_t *NQWriteDest (int dest)\n{\n\treturn QWWriteDest(dest);\n}\n#endif\n\nclient_t *Write_GetClient(void)\n{\n\tint\t\tentnum;\n\tedict_t\t*ent;\n\n\tent = PROG_TO_EDICT(svprogfuncs, pr_global_struct->msg_entity);\n\tentnum = NUM_FOR_EDICT(svprogfuncs, ent);\n\tif (entnum < 1 || entnum > sv.allocated_client_slots)\n\t\treturn NULL;//PR_RunError (\"WriteDest: not a client\");\n\tif (svs.clients[entnum-1].protocol == SCP_BAD)\n\t\treturn NULL;\t//don't try writing to bots... we don't want the overflows.\n\treturn &svs.clients[entnum-1];\n}\n\nstatic int PF_Write_BoundForNetwork(pubprogfuncs_t *prinst, int minv, int v, int maxv)\n{\n\tif (v > maxv)\n\t{\n\t\tint mask = (minv<0)?maxv*2+1:maxv;\n\t\tint n = (v&mask);\n\t\tif (minv < 0 && n > maxv)\n\t\t\tn |= ~mask;\t//sign-extend it\n\t\tif (developer.ival)\n\t\t{\n\t\t\tCon_Printf(\"Write*: value %i is outside of the required %i to %i range, truncating to %i\\n\", v, minv, maxv, n);\n\t\t\tPR_StackTrace(prinst, false);\n\t\t}\n\t\treturn n;\n\t}\n\tif (v < minv)\n\t{\n\t\tint mask = (minv<0)?maxv*2+1:maxv;\n\t\tint n = (v&mask);\n\t\tif (minv < 0 && n > maxv)\n\t\t\tn |= ~mask;\t//sign-extend it\n\t\tif (developer.ival)\n\t\t{\n\t\t\tCon_Printf(\"Write*: value %i is outside of the required %i to %i range, truncating to %i\\n\", v, minv, maxv, n);\n\t\t\tPR_StackTrace(prinst, false);\n\t\t}\n\t\treturn n;\n\t}\n\treturn v;\n}\n\nextern sizebuf_t csqcmsgbuffer;\nvoid QCBUILTIN PF_WriteByte (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint dest = G_FLOAT(OFS_PARM0);\n\tqbyte val = PF_Write_BoundForNetwork(prinst, 0, G_FLOAT(OFS_PARM1), 255);\n\tif (dest == MSG_CSQC)\n\t{\t//csqc buffers are always written.\n\t\tif (!csqcmsgbuffer.maxsize)\n\t\t{\n\t\t\tPR_StackTrace(prinst, false);\n\t\t\tprinst->parms->Abort (\"MSG_CSQC outside of SendEntity method\");\n\t\t}\n\n\t\tMSG_WriteByte(&csqcmsgbuffer, val);\n\t\treturn;\n\t}\n\n\tif (pr_nonetaccess.value)\n\t\treturn;\n\n#ifdef SERVER_DEMO_PLAYBACK\n\tif (sv.demofile)\n\t\treturn;\n#endif\n\n#ifdef NETPREPARSE\n\tif (dpcompat_nopreparse.ival)\n\t\t;\n\telse if (progstype != PROG_QW)\n\t{\n\t\tNPP_NQWriteByte(dest, val);\n\t\treturn;\n\t}\n#ifdef NQPROT\n\telse\n\t{\n\t\tNPP_QWWriteByte(dest, val);\n\t\treturn;\n\t}\n#endif\n#endif\n\tif (dest == MSG_ONE)\n\t{\t//WARNING: THIS IS BUGGY. DO NOT MAKE MODS THAT TAKE THIS PATH\n\t\tclient_t *cl = Write_GetClient();\n\t\tif (!cl)\n\t\t\treturn;\n\t\tClientReliableCheckBlock(cl, 1);\n\t\tClientReliableWrite_Byte(cl, val);\n\t}\n\telse\n\t{\n\t\tif (progstype != PROG_QW)\n\t\t\tMSG_WriteByte (NQWriteDest(dest), val);\n\t\telse\n\t\t\tMSG_WriteByte (QWWriteDest(dest), val);\n\t}\n}\n\nvoid QCBUILTIN PF_WriteChar (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint dest = G_FLOAT(OFS_PARM0);\n\tchar val = PF_Write_BoundForNetwork(prinst, -128, G_FLOAT(OFS_PARM1), 127);\n\tif (dest == MSG_CSQC)\n\t{\t//csqc buffers are always written.\n\t\tMSG_WriteChar(&csqcmsgbuffer, val);\n\t\treturn;\n\t}\n\n\tif (pr_nonetaccess.value)\n\t\treturn;\n#ifdef SERVER_DEMO_PLAYBACK\n\tif (sv.demofile)\n\t\treturn;\n#endif\n\n#ifdef NETPREPARSE\n\tif (dpcompat_nopreparse.ival)\n\t\t;\n\telse if (progstype != PROG_QW)\n\t{\n\t\tNPP_NQWriteChar(dest, val);\n\t\treturn;\n\t}\n#ifdef NQPROT\n\telse\n\t{\n\t\tNPP_QWWriteChar(dest, val);\n\t\treturn;\n\t}\n#endif\n#endif\n\tif (dest == MSG_ONE)\n\t{\n\t\tclient_t *cl = Write_GetClient();\n\t\tif (!cl)\n\t\t\treturn;\n\t\tClientReliableCheckBlock(cl, 1);\n\t\tClientReliableWrite_Char(cl, val);\n\t}\n\telse\n\t{\n\t\tif (progstype != PROG_QW)\n\t\t\tMSG_WriteChar (NQWriteDest(dest), val);\n\t\telse\n\t\t\tMSG_WriteChar (QWWriteDest(dest), val);\n\t}\n}\n\nvoid QCBUILTIN PF_WriteShort (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint dest = G_FLOAT(OFS_PARM0);\n\tshort val = PF_Write_BoundForNetwork(prinst, -32768, G_FLOAT(OFS_PARM1), 32767);\n\tif (dest == MSG_CSQC)\n\t{\t//csqc buffers are always written.\n\t\tMSG_WriteShort(&csqcmsgbuffer, val);\n\t\treturn;\n\t}\n\n\tif (pr_nonetaccess.value)\n\t\treturn;\n#ifdef SERVER_DEMO_PLAYBACK\n\tif (sv.demofile)\n\t\treturn;\n#endif\n\n#ifdef NETPREPARSE\n\tif (dpcompat_nopreparse.ival)\n\t\t;\n\telse if (progstype != PROG_QW)\n\t{\n\t\tNPP_NQWriteShort(dest, val);\n\t\treturn;\n\t}\n#ifdef NQPROT\n\telse\n\t{\n\t\tNPP_QWWriteShort(dest, val);\n\t\treturn;\n\t}\n#endif\n#endif\n\n\tif (dest == MSG_ONE)\n\t{\n\t\tclient_t *cl = Write_GetClient();\n\t\tif (!cl)\n\t\t\treturn;\n\t\tClientReliableCheckBlock(cl, 2);\n\t\tClientReliableWrite_Short(cl, val);\n\t}\n\telse\n\t{\n\t\tif (progstype != PROG_QW)\n\t\t\tMSG_WriteShort (NQWriteDest(dest), val);\n\t\telse\n\t\t\tMSG_WriteShort (QWWriteDest(dest), val);\n\t}\n}\n\nvoid QCBUILTIN PF_WriteLong (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint dest = G_FLOAT(OFS_PARM0);\n\tif (dest == MSG_CSQC)\n\t{\t//csqc buffers are always written.\n\t\tMSG_WriteLong(&csqcmsgbuffer, G_FLOAT(OFS_PARM1));\n\t\treturn;\n\t}\n\n\tif (pr_nonetaccess.value)\n\t\treturn;\n#ifdef SERVER_DEMO_PLAYBACK\n\tif (sv.demofile)\n\t\treturn;\n#endif\n\n#ifdef NETPREPARSE\n\tif (dpcompat_nopreparse.ival)\n\t\t;\n\telse if (progstype != PROG_QW)\n\t{\n\t\tNPP_NQWriteLong(dest, G_FLOAT(OFS_PARM1));\n\t\treturn;\n\t}\n#ifdef NQPROT\n\telse\n\t{\n\t\tNPP_QWWriteLong(dest, G_FLOAT(OFS_PARM1));\n\t\treturn;\n\t}\n#endif\n#endif\n\n\tif (dest == MSG_ONE)\n\t{\n\t\tclient_t *cl = Write_GetClient();\n\t\tif (!cl)\n\t\t\treturn;\n\t\tClientReliableCheckBlock(cl, 4);\n\t\tClientReliableWrite_Long(cl, G_FLOAT(OFS_PARM1));\n\t}\n\telse\n\t{\n\t\tif (progstype != PROG_QW)\n\t\t\tMSG_WriteLong (NQWriteDest(dest), G_FLOAT(OFS_PARM1));\n\t\telse\n\t\t\tMSG_WriteLong (QWWriteDest(dest), G_FLOAT(OFS_PARM1));\n\t}\n}\n\nvoid QCBUILTIN PF_WriteInt (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint dest = G_FLOAT(OFS_PARM0);\n\tif (dest == MSG_CSQC)\n\t{\t//csqc buffers are always written.\n\t\tMSG_WriteLong(&csqcmsgbuffer, G_INT(OFS_PARM1));\n\t\treturn;\n\t}\n\n\tif (pr_nonetaccess.value)\n\t\treturn;\n#ifdef SERVER_DEMO_PLAYBACK\n\tif (sv.demofile)\n\t\treturn;\n#endif\n\n#ifdef NETPREPARSE\n\tif (dpcompat_nopreparse.ival)\n\t\t;\n\telse if (progstype != PROG_QW)\n\t{\n\t\tNPP_NQWriteLong(dest, G_INT(OFS_PARM1));\n\t\treturn;\n\t}\n#ifdef NQPROT\n\telse\n\t{\n\t\tNPP_QWWriteLong(dest, G_INT(OFS_PARM1));\n\t\treturn;\n\t}\n#endif\n#endif\n\n\tif (dest == MSG_ONE)\n\t{\n\t\tclient_t *cl = Write_GetClient();\n\t\tif (!cl)\n\t\t\treturn;\n\t\tClientReliableCheckBlock(cl, 4);\n\t\tClientReliableWrite_Long(cl, G_INT(OFS_PARM1));\n\t}\n\telse\n\t{\n\t\tif (progstype != PROG_QW)\n\t\t\tMSG_WriteLong (NQWriteDest(dest), G_INT(OFS_PARM1));\n\t\telse\n\t\t\tMSG_WriteLong (QWWriteDest(dest), G_INT(OFS_PARM1));\n\t}\n}\n\nstatic void PF_WriteUInt64_Val (int dest, puint64_t val)\n{\n\tif (dest == MSG_CSQC)\n\t{\t//csqc buffers are always written.\n\t\tMSG_WriteInt64(&csqcmsgbuffer, val);\n\t\treturn;\n\t}\n\n\tif (pr_nonetaccess.value)\n\t\treturn;\n#ifdef SERVER_DEMO_PLAYBACK\n\tif (sv.demofile)\n\t\treturn;\n#endif\n\n#ifdef NETPREPARSE\n\tif (dpcompat_nopreparse.ival)\n\t\t;\n\telse if (progstype != PROG_QW)\n\t{\n\t\tint b = 0;\n\t\tquint64_t l = 128;\n\t\twhile (val > l-1u && b < 8)\n\t\t{\t//count the extra bytes we need\n\t\t\tb++;\n\t\t\tl <<= 7;\t//each byte we add gains 8 bits, but we spend one on length.\n\t\t}\n\t\tNPP_NQWriteByte(dest, 0xffu<<(8-b) | (val >> (b*8)));\n\t\twhile(b --> 0)\n\t\t\tNPP_NQWriteByte(dest, val >> (b*8));\n\t\treturn;\n\t}\n#ifdef NQPROT\n\telse\n\t{\n\t\tint b = 0;\n\t\tquint64_t l = 128;\n\t\twhile (val > l-1u && b < 8)\n\t\t{\t//count the extra bytes we need\n\t\t\tb++;\n\t\t\tl <<= 7;\t//each byte we add gains 8 bits, but we spend one on length.\n\t\t}\n\t\tNPP_QWWriteByte(dest, 0xffu<<(8-b) | (val >> (b*8)));\n\t\twhile(b --> 0)\n\t\t\tNPP_QWWriteByte(dest, val >> (b*8));\n\t\treturn;\n\t}\n#endif\n#endif\n\n\tif (dest == MSG_ONE)\n\t{\n\t\tclient_t *cl = Write_GetClient();\n\t\tif (!cl)\n\t\t\treturn;\n\t\tClientReliableCheckBlock(cl, 8);\n\t\tClientReliableWrite_Int64(cl, val);\n\t}\n\telse\n\t{\n\t\tif (progstype != PROG_QW)\n\t\t\tMSG_WriteUInt64 (NQWriteDest(dest), val);\n\t\telse\n\t\t\tMSG_WriteUInt64 (QWWriteDest(dest), val);\n\t}\n}\nvoid QCBUILTIN PF_WriteUInt64 (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint dest = G_FLOAT(OFS_PARM0);\n\tpuint64_t val = G_UINT64(OFS_PARM1);\n\tPF_WriteUInt64_Val(dest, val);\n}\nvoid QCBUILTIN PF_WriteInt64 (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint dest = G_FLOAT(OFS_PARM0);\n\tpint64_t val = G_INT64(OFS_PARM1);\n\t//move the sign bit into the low bit and avoid sign extension for more efficient length coding. otherwise just reuse our uint64 logic\n\tif (val < 0)\n\t\tPF_WriteUInt64_Val(dest, ((quint64_t)(-1-val)<<1)|1);\n\telse\n\t\tPF_WriteUInt64_Val(dest, val<<1);\n}\n\nvoid QCBUILTIN PF_WriteAngle (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint dest = G_FLOAT(OFS_PARM0);\n\tif (dest == MSG_CSQC)\n\t{\t//csqc buffers are always written.\n\t\tMSG_WriteAngle(&csqcmsgbuffer, G_FLOAT(OFS_PARM1));\n\t\treturn;\n\t}\n\n\tif (pr_nonetaccess.value)\n\t\treturn;\n#ifdef SERVER_DEMO_PLAYBACK\n\tif (sv.demofile)\n\t\treturn;\n#endif\n\n#ifdef NETPREPARSE\n\tif (dpcompat_nopreparse.ival)\n\t\t;\n\telse if (progstype != PROG_QW)\n\t{\n\t\tNPP_NQWriteAngle(dest, G_FLOAT(OFS_PARM1));\n\t\treturn;\n\t}\n#ifdef NQPROT\n\telse\n\t{\n\t\tNPP_QWWriteAngle(dest, G_FLOAT(OFS_PARM1));\n\t\treturn;\n\t}\n#endif\n#endif\n\n\tif (dest == MSG_ONE)\n\t{\n\t\tclient_t *cl = Write_GetClient();\n\t\tif (!cl)\n\t\t\treturn;\n\t\tClientReliableCheckBlock(cl, 4);\n\t\tClientReliableWrite_Angle(cl, G_FLOAT(OFS_PARM1));\n\t}\n\telse\n\t{\n\t\tif (progstype != PROG_QW)\n\t\t\tMSG_WriteAngle (NQWriteDest(dest), G_FLOAT(OFS_PARM1));\n\t\telse\n\t\t\tMSG_WriteAngle (QWWriteDest(dest), G_FLOAT(OFS_PARM1));\n\t}\n}\n\nvoid QCBUILTIN PF_WriteCoord (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint dest = G_FLOAT(OFS_PARM0);\n\tif (developer.ival && sv.reliable_datagram.prim.coordtype == COORDTYPE_FIXED_13_3)\n\t{\n\t\tint v = G_FLOAT(OFS_PARM1)*8;\n\t\tif (v > 32767)\n\t\t{\n\t\t\tif (developer.ival)\n\t\t\t\tPR_RunWarning(prinst, \"WriteCoord: value %g is outside of the required -4096 to 4095.875 range\\n\", G_FLOAT(OFS_PARM1));\n\t\t}\n\t\tif (v < -32768)\n\t\t{\n\t\t\tif (developer.ival)\n\t\t\t\tPR_RunWarning(prinst, \"WriteCoord: value %g is outside of the required 4096 to 4095.875 range\\n\", G_FLOAT(OFS_PARM1));\n\t\t}\n\t}\n\tif (dest == MSG_CSQC)\n\t{\t//csqc buffers are always written.\n\t\tMSG_WriteCoord(&csqcmsgbuffer, G_FLOAT(OFS_PARM1));\n\t\treturn;\n\t}\n\n\tif (pr_nonetaccess.value)\n\t\treturn;\n#ifdef SERVER_DEMO_PLAYBACK\n\tif (sv.demofile)\n\t\treturn;\n#endif\n\n#ifdef NETPREPARSE\n\tif (dpcompat_nopreparse.ival)\n\t\t;\n\telse if (progstype != PROG_QW)\n\t{\n\t\tNPP_NQWriteCoord(dest, G_FLOAT(OFS_PARM1));\n\t\treturn;\n\t}\n#ifdef NQPROT\n\telse\n\t{\n\t\tNPP_QWWriteCoord(dest, G_FLOAT(OFS_PARM1));\n\t\treturn;\n\t}\n#endif\n#endif\n\n\tif (dest == MSG_ONE)\n\t{\n\t\tclient_t *cl = Write_GetClient();\n\t\tif (!cl)\n\t\t\treturn;\n\t\tClientReliableCheckBlock(cl, 2);\n\t\tClientReliableWrite_Coord(cl, G_FLOAT(OFS_PARM1));\n\t}\n\telse\n\t{\n\t\tif (progstype != PROG_QW)\n\t\t\tMSG_WriteCoord (NQWriteDest(dest), G_FLOAT(OFS_PARM1));\n\t\telse\n\t\t\tMSG_WriteCoord (QWWriteDest(dest), G_FLOAT(OFS_PARM1));\n\t}\n}\n\nvoid QCBUILTIN PF_WriteFloat (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint dest = G_FLOAT(OFS_PARM0);\n\tif (dest == MSG_CSQC)\n\t{\t//csqc buffers are always written.\n\t\tMSG_WriteFloat(&csqcmsgbuffer, G_FLOAT(OFS_PARM1));\n\t\treturn;\n\t}\n\n\tif (pr_nonetaccess.value)\n\t\treturn;\n#ifdef SERVER_DEMO_PLAYBACK\n\tif (sv.demofile)\n\t\treturn;\n#endif\n\n#ifdef NETPREPARSE\n\tif (dpcompat_nopreparse.ival)\n\t\t;\n\telse if (progstype != PROG_QW)\n\t{\n\t\tNPP_NQWriteFloat(dest, G_FLOAT(OFS_PARM1));\n\t\treturn;\n\t}\n#ifdef NQPROT\n\telse\n\t{\n\t\tNPP_QWWriteFloat(dest, G_FLOAT(OFS_PARM1));\n\t\treturn;\n\t}\n#endif\n#endif\n\n\tif (dest == MSG_ONE)\n\t{\n\t\tclient_t *cl = Write_GetClient();\n\t\tif (!cl)\n\t\t\treturn;\n\t\tClientReliableCheckBlock(cl, 4);\n\t\tClientReliableWrite_Float(cl, G_FLOAT(OFS_PARM1));\n\t}\n\telse\n\t{\n\t\tif (progstype != PROG_QW)\n\t\t\tMSG_WriteFloat (NQWriteDest(dest), G_FLOAT(OFS_PARM1));\n\t\telse\n\t\t\tMSG_WriteFloat (QWWriteDest(dest), G_FLOAT(OFS_PARM1));\n\t}\n}\n\nvoid QCBUILTIN PF_WriteDouble (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint dest = G_FLOAT(OFS_PARM0);\n\tdouble val = G_DOUBLE(OFS_PARM1);\n#ifdef NETPREPARSE\n\tunion {\n\t\tdouble val;\n\t\tquint64_t ival;\n\t} u = {val};\n#endif\n\tif (dest == MSG_CSQC)\n\t{\t//csqc buffers are always written.\n\t\tMSG_WriteDouble(&csqcmsgbuffer, val);\n\t\treturn;\n\t}\n\n\tif (pr_nonetaccess.value)\n\t\treturn;\n#ifdef SERVER_DEMO_PLAYBACK\n\tif (sv.demofile)\n\t\treturn;\n#endif\n\n#ifdef NETPREPARSE\n\tif (dpcompat_nopreparse.ival)\n\t\t;\n\telse if (progstype != PROG_QW)\n\t{\n\t\tNPP_NQWriteLong(dest, u.ival&0xffffffff);\n\t\tNPP_NQWriteLong(dest, (u.ival>>32)&0xffffffff);\n\t\treturn;\n\t}\n#ifdef NQPROT\n\telse\n\t{\n\t\tNPP_QWWriteLong(dest, u.ival&0xffffffff);\n\t\tNPP_QWWriteLong(dest, (u.ival>>32)&0xffffffff);\n\t\treturn;\n\t}\n#endif\n#endif\n\n\tif (dest == MSG_ONE)\n\t{\n\t\tclient_t *cl = Write_GetClient();\n\t\tif (!cl)\n\t\t\treturn;\n\t\tClientReliableCheckBlock(cl, 8);\n\t\tClientReliableWrite_Double(cl, val);\n\t}\n\telse\n\t{\n\t\tif (progstype != PROG_QW)\n\t\t\tMSG_WriteDouble (NQWriteDest(dest), val);\n\t\telse\n\t\t\tMSG_WriteDouble (QWWriteDest(dest), val);\n\t}\n}\n\nvoid PF_WriteString_Internal (int target, const char *str)\n{\n\tif (target == MSG_CSQC)\n\t{\t//csqc buffers are always written.\n\t\tMSG_WriteString(&csqcmsgbuffer, str);\n\t\treturn;\n\t}\n\n\tif (pr_nonetaccess.value\n#ifdef SERVER_DEMO_PLAYBACK\n\t\t|| sv.demofile\n#endif\n\t\t)\n\t\treturn;\n\n#ifdef NETPREPARSE\n\tif (dpcompat_nopreparse.ival)\n\t\t;\n\telse if (progstype != PROG_QW)\n\t{\n\t\tNPP_NQWriteString(target, str);\n\t\treturn;\n\t}\n#ifdef NQPROT\n\telse\n\t{\n\t\tNPP_QWWriteString(target, str);\n\t\treturn;\n\t}\n#endif\n#endif\n\n\tif (target == MSG_ONE)\n\t{\n\t\tclient_t *cl = Write_GetClient();\n\t\tif (!cl)\n\t\t\treturn;\n\t\tClientReliableCheckBlock(cl, 1+strlen(str));\n\t\tClientReliableWrite_String(cl, str);\n\t}\n\telse\n\t{\n\t\tif (progstype != PROG_QW)\n\t\t\tMSG_WriteString (NQWriteDest(target), str);\n\t\telse\n\t\t\tMSG_WriteString (QWWriteDest(target), str);\n\t}\n}\n\nstatic void QCBUILTIN PF_WriteString (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char *str = PF_VarString(prinst, 1, pr_globals);\n\tPF_WriteString_Internal(G_FLOAT(OFS_PARM0), str);\n}\n\nstatic void QCBUILTIN PF_WritePicture (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\t//name size data\n\t//this is basically a stub, so we write a size of 0. the client will have to just deal with it.\n\tint target = G_FLOAT(OFS_PARM0);\n\tstring_t o = G_INT(OFS_PARM1);\n\tfloat sizelimit = G_INT(OFS_PARM2);\n\n\t(void)sizelimit;\t//we don't use this, because we don't bother trying to re-compress the thing here.\n\n\tPF_WriteString_Internal(target, PR_GetString(prinst, o));\n\tG_FLOAT(OFS_PARM1) = 0;\n\tPF_WriteShort(prinst, pr_globals);\n\n\tG_INT(OFS_PARM1) = o;\n}\n\nvoid QCBUILTIN PF_WriteEntity (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint dest = G_FLOAT(OFS_PARM0);\n\tif (dest == MSG_CSQC)\n\t{\t//csqc buffers are always written.\n\t\tMSG_WriteEntity(&csqcmsgbuffer, G_EDICTNUM(prinst, OFS_PARM1));\n\t\treturn;\n\t}\n\n\tif (pr_nonetaccess.value\n#ifdef SERVER_DEMO_PLAYBACK\n\t\t|| sv.demofile\n#endif\n\t\t)\n\t\treturn;\n\n#ifdef NETPREPARSE\n\tif (dpcompat_nopreparse.ival)\n\t\t;\n\telse if (progstype != PROG_QW)\n\t{\n\t\tNPP_NQWriteEntity(dest, G_EDICTNUM(prinst, OFS_PARM1));\n\t\treturn;\n\t}\n#ifdef NQPROT\n\telse\n\t{\n\t\tNPP_QWWriteEntity(dest, G_EDICTNUM(prinst, OFS_PARM1));\n\t\treturn;\n\t}\n#endif\n#endif\n\n\tif (dest == MSG_ONE)\n\t{\n\t\tclient_t *cl = Write_GetClient();\n\t\tif (!cl)\n\t\t\treturn;\n\t\tClientReliableCheckBlock(cl, 2);\n\t\tClientReliableWrite_Entity(cl, G_EDICTNUM(prinst, OFS_PARM1));\n\t}\n\telse\n\t{\n\t\tif (progstype != PROG_QW)\n\t\t\tMSG_WriteEntity (NQWriteDest(dest), G_EDICTNUM(prinst, OFS_PARM1));\n\t\telse\n\t\t\tMSG_WriteEntity (QWWriteDest(dest), G_EDICTNUM(prinst, OFS_PARM1));\n\t}\n}\n\n//small wrapper function.\n//void(float target, string str, ...) WriteString2 = #33;\nstatic void QCBUILTIN PF_WriteString2 (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint old;\n\tconst char *str;\n\n\tif (G_FLOAT(OFS_PARM0) != MSG_CSQC && (pr_nonetaccess.value\n#ifdef SERVER_DEMO_PLAYBACK\n\t\t|| sv.demofile\n#endif\n\t\t))\n\t\treturn;\n\n\tstr = PF_VarString(prinst, 1, pr_globals);\n\n\told = G_FLOAT(OFS_PARM1);\n\twhile(*str)\n\t{\n\t\tG_FLOAT(OFS_PARM1) = *str++;\n\t\tPF_WriteChar(prinst, pr_globals);\n\t}\n\tG_FLOAT(OFS_PARM1) = old;\n}\n\n#if defined(HAVE_LEGACY) && defined(NETPREPARSE)\n//qtest-only builtins.\nstatic void QCBUILTIN PF_qtSingle_WriteByte (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tNPP_NQWriteByte(MSG_PRERELONE, (qbyte)G_FLOAT(OFS_PARM1));\n}\nstatic void QCBUILTIN PF_qtSingle_WriteChar (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tNPP_NQWriteChar(MSG_PRERELONE, (char)G_FLOAT(OFS_PARM1));\n}\nstatic void QCBUILTIN PF_qtSingle_WriteShort (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tNPP_NQWriteShort(MSG_PRERELONE, (short)G_FLOAT(OFS_PARM1));\n}\nstatic void QCBUILTIN PF_qtSingle_WriteLong (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tNPP_NQWriteLong(MSG_PRERELONE, G_FLOAT(OFS_PARM1));\n}\nstatic void QCBUILTIN PF_qtSingle_WriteAngle (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tNPP_NQWriteAngle(MSG_PRERELONE, G_FLOAT(OFS_PARM1));\n}\nstatic void QCBUILTIN PF_qtSingle_WriteCoord (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tNPP_NQWriteCoord(MSG_PRERELONE, G_FLOAT(OFS_PARM1));\n}\nstatic void QCBUILTIN PF_qtSingle_WriteString (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tNPP_NQWriteString(MSG_PRERELONE, PF_VarString(prinst, 1, pr_globals));\n}\nstatic void QCBUILTIN PF_qtSingle_WriteEntity (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tNPP_NQWriteEntity(MSG_PRERELONE, (short)G_EDICTNUM(prinst, OFS_PARM1));\n}\n\nstatic void QCBUILTIN PF_qtBroadcast_WriteByte (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tNPP_NQWriteByte(MSG_BROADCAST, (qbyte)G_FLOAT(OFS_PARM0));\n}\nstatic void QCBUILTIN PF_qtBroadcast_WriteChar (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tNPP_NQWriteChar(MSG_BROADCAST, (char)G_FLOAT(OFS_PARM0));\n}\nstatic void QCBUILTIN PF_qtBroadcast_WriteShort (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tNPP_NQWriteShort(MSG_BROADCAST, (short)G_FLOAT(OFS_PARM0));\n}\nstatic void QCBUILTIN PF_qtBroadcast_WriteLong (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tNPP_NQWriteLong(MSG_BROADCAST, G_FLOAT(OFS_PARM0));\n}\nstatic void QCBUILTIN PF_qtBroadcast_WriteAngle (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tNPP_NQWriteAngle(MSG_BROADCAST, G_FLOAT(OFS_PARM0));\n}\nstatic void QCBUILTIN PF_qtBroadcast_WriteCoord (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tNPP_NQWriteCoord(MSG_BROADCAST, G_FLOAT(OFS_PARM0));\n}\nstatic void QCBUILTIN PF_qtBroadcast_WriteString (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tNPP_NQWriteString(MSG_BROADCAST, PF_VarString(prinst, 0, pr_globals));\n}\nstatic void QCBUILTIN PF_qtBroadcast_WriteEntity (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tNPP_NQWriteEntity(MSG_BROADCAST, (short)G_EDICTNUM(prinst, OFS_PARM0));\n}\n#endif\n\n//======================================================\n\n//copes with any qw point entities.\nvoid SV_point_tempentity (vec3_t o, int type, int count)\t//count (usually 1) is available for some tent types.\n{\n\tint split=0;\n\tint qwtype[2] = {type,type};\n#ifdef NQPROT\n\tint nqtype[2] = {type,type};\n#endif\n\tint i;\n\n#ifdef SERVER_DEMO_PLAYBACK\n\tif (sv.demofile)\n\t\treturn;\n#endif\n\n\tswitch(type)\t//uses the QW types...\n\t{\n\tcase TE_BULLET:\n\t\tqwtype[1] = TE_SPIKE;\n#ifdef NQPROT\n\t\tnqtype[1] = nqtype[0] = TE_SPIKE;\n#endif\n\t\tsplit = PEXT_TE_BULLET;\n\t\tbreak;\n\tcase TEQW_SUPERBULLET:\n\t\tqwtype[1] = TE_SUPERSPIKE;\n#ifdef NQPROT\n\t\tnqtype[1] = nqtype[0] = TE_SUPERSPIKE;\n#endif\n\t\tsplit = PEXT_TE_BULLET;\n\t\tbreak;\n\tcase TEQW_QWBLOOD:\n#ifdef NQPROT\n\t\tnqtype[0] = nqtype[1] = 73|256;\n#endif\n\t\tbreak;\n\tcase TEQW_LIGHTNINGBLOOD:\n#ifdef NQPROT\n\t\tnqtype[0] = nqtype[1] = 225|256;\n#endif\n\t\tbreak;\n\tcase TEQW_QWGUNSHOT:\n#ifdef NQPROT\n\t\tnqtype[0] = TENQ_QWGUNSHOT;\n\t\tnqtype[1] = TENQ_NQGUNSHOT;\n\t\tsplit = PEXT_TE_BULLET;\n#endif\n\t\tbreak;\n\tcase TEQW_NQGUNSHOT:\n#ifdef NQPROT\n\t\tnqtype[0] = TENQ_NQGUNSHOT;\n\t\tnqtype[1] = TENQ_NQGUNSHOT;\n#endif\n\t\tqwtype[1] = TEQW_QWGUNSHOT;\n\t\tsplit = PEXT_TE_BULLET;\n\t\tbreak;\n\tcase TEQW_QWEXPLOSION:\n#ifdef NQPROT\n\t\tnqtype[0] = TENQ_QWEXPLOSION;\n\t\tnqtype[1] = TENQ_NQEXPLOSION;\n\t\tsplit = PEXT_TE_BULLET;\n#endif\n\t\tbreak;\n\tcase TEQW_NQEXPLOSION:\n\t\tqwtype[0] = TEQW_NQEXPLOSION;\n\t\tqwtype[1] = TEQW_QWEXPLOSION;\n#ifdef NQPROT\n\t\tnqtype[0] = TENQ_NQEXPLOSION;\n\t\tnqtype[1] = TENQ_NQEXPLOSION;\n#endif\n\t\tsplit = PEXT_TE_BULLET;\n\t\tbreak;\n\tcase TE_LIGHTNING1:\n\tcase TE_LIGHTNING2:\n\tcase TE_LIGHTNING3:\n\t\tSV_Error(\"SV_point_tempentity - type is a beam\\n\");\n\t\tbreak;\n\tdefault:\n\t\tbreak;\n\t}\n\tfor (i = 0; i < 2; i++)\n\t{\n\t\tif (qwtype[i] >= 0)\n\t\t{\n\t\t\tMSG_WriteByte (&sv.multicast, svc_temp_entity);\n\t\t\tMSG_WriteByte (&sv.multicast, qwtype[i]);\n\t\t\tif (qwtype[i] == TEQW_QWBLOOD || qwtype[i] == TEQW_QWGUNSHOT)\n\t\t\t\tMSG_WriteByte (&sv.multicast, count);\n\t\t\tMSG_WriteCoord (&sv.multicast, o[0]);\n\t\t\tMSG_WriteCoord (&sv.multicast, o[1]);\n\t\t\tMSG_WriteCoord (&sv.multicast, o[2]);\n\t\t}\n#ifdef NQPROT\n\t\tif (nqtype[i] >= 256)\n\t\t{\n\t\t\t//send a particle instead\n\t\t\tMSG_WriteByte (&sv.nqmulticast, svc_particle);\n\t\t\tMSG_WriteCoord (&sv.nqmulticast, o[0]);\n\t\t\tMSG_WriteCoord (&sv.nqmulticast, o[1]);\n\t\t\tMSG_WriteCoord (&sv.nqmulticast, o[2]);\n\t\t\t//no direction.\n\t\t\tMSG_WriteChar (&sv.nqmulticast, 0);\n\t\t\tMSG_WriteChar (&sv.nqmulticast, 0);\n\t\t\tMSG_WriteChar (&sv.nqmulticast, 0);\n\t\t\tMSG_WriteByte (&sv.nqmulticast, bound(0, count*20, 254));\t//255=explosion.\n\t\t\tMSG_WriteByte (&sv.nqmulticast, nqtype[i]&0xff);\n\t\t}\n\t\telse if (nqtype[i] >= 0)\n\t\t{\n\t\t\t//messy - TENQ_NQGUNSHOT loops until we reach our counter. should probably randomize positions a little\n\t\t\tint nqcount = (nqtype[i] == TENQ_NQGUNSHOT)?min(3,count):1;\n\t\t\twhile(nqcount-->0)\n\t\t\t{\n\t\t\t\tMSG_WriteByte (&sv.nqmulticast, svc_temp_entity);\n\t\t\t\tMSG_WriteByte (&sv.nqmulticast, nqtype[i]);\n\t\t\t\tif (nqtype[i] == TEDP_BLOOD)\n\t\t\t\t{\n\t\t\t\t\tMSG_WriteChar(&sv.nqmulticast, 0);\n\t\t\t\t\tMSG_WriteChar(&sv.nqmulticast, 0);\n\t\t\t\t\tMSG_WriteChar(&sv.nqmulticast, 0);\n\t\t\t\t}\n\t\t\t\telse if (nqtype[i] == TENQ_QWGUNSHOT)\n\t\t\t\t\tMSG_WriteByte (&sv.nqmulticast, count);\n\t\t\t\tMSG_WriteCoord (&sv.nqmulticast, o[0]);\n\t\t\t\tMSG_WriteCoord (&sv.nqmulticast, o[1]);\n\t\t\t\tMSG_WriteCoord (&sv.nqmulticast, o[2]);\n\t\t\t}\n\t\t}\n#endif\n\t\tif (i)\n\t\t{\n\t\t\tSV_MulticastProtExt (o, MULTICAST_PHS, pr_global_struct->dimension_send, 0, split);\n\t\t\tbreak;\n\t\t}\n\t\tSV_MulticastProtExt (o, MULTICAST_PHS, pr_global_struct->dimension_send, split, 0);\n\t\tif (!split)\n\t\t\tbreak;\n\t}\n}\n\nvoid SV_beam_tempentity (int ownerent, vec3_t start, vec3_t end, int type)\n{\n\tMSG_WriteByte (&sv.multicast, svc_temp_entity);\n\tMSG_WriteByte (&sv.multicast, type);\n\tMSG_WriteEntity (&sv.multicast, ownerent);\n\tMSG_WriteCoord (&sv.multicast, start[0]);\n\tMSG_WriteCoord (&sv.multicast, start[1]);\n\tMSG_WriteCoord (&sv.multicast, start[2]);\n\tMSG_WriteCoord (&sv.multicast, end[0]);\n\tMSG_WriteCoord (&sv.multicast, end[1]);\n\tMSG_WriteCoord (&sv.multicast, end[2]);\n#ifdef NQPROT\n\tMSG_WriteByte (&sv.nqmulticast, svc_temp_entity);\n\tMSG_WriteByte (&sv.nqmulticast, (type==TEQW_BEAM)?TENQ_BEAM:type);\n\tMSG_WriteEntity (&sv.nqmulticast, ownerent);\n\tMSG_WriteCoord (&sv.nqmulticast, start[0]);\n\tMSG_WriteCoord (&sv.nqmulticast, start[1]);\n\tMSG_WriteCoord (&sv.nqmulticast, start[2]);\n\tMSG_WriteCoord (&sv.nqmulticast, end[0]);\n\tMSG_WriteCoord (&sv.nqmulticast, end[1]);\n\tMSG_WriteCoord (&sv.nqmulticast, end[2]);\n#endif\n\tSV_MulticastProtExt (start, MULTICAST_PHS, pr_global_struct->dimension_send, 0, 0);\n}\n\n/*\nvoid PF_tempentity (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tSV_point_tempentity(G_VECTOR(OFS_PARM0), G_FLOAT(OFS_PARM1), 1);\n}\n*/\n\n//=============================================================================\n\nint SV_ModelIndex (const char *name);\n\nvoid QCBUILTIN PF_makestatic (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tedict_t\t*ent;\n\tentity_state_t *state;\n\n\tent = G_EDICT(prinst, OFS_PARM0);\n\n\tif (sv.num_static_entities == sv_max_staticentities)\n\t{\n\t\tsv_max_staticentities += 16;\n\t\tsv_staticentities = BZ_Realloc(sv_staticentities, sizeof(*sv_staticentities) * sv_max_staticentities);\n\t}\n\n\tstate = &sv_staticentities[sv.num_static_entities++];\n\tmemset(state, 0, sizeof(*state));\n\n\tSV_Snapshot_BuildStateQ1(state, ent, NULL, NULL);\n\tstate->number = sv.num_static_entities;\n\n// throw the entity away now\n\tED_Free (svprogfuncs, ent);\n}\n\n//=============================================================================\n\n/*\n==============\nPF_setspawnparms\n==============\n*/\nvoid QCBUILTIN PF_setspawnparms (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tedict_t\t*ent;\n\tint\t\ti;\n\tclient_t\t*client;\n\n\tent = G_EDICT(prinst, OFS_PARM0);\n\ti = NUM_FOR_EDICT(prinst, ent);\n\tif (i < 1 || i > sv.allocated_client_slots)\n\t{\n\t\tPR_BIError (prinst, \"Entity is not a client\");\n\t\treturn;\n\t}\n\n\t// copy spawn parms out of the client_t\n\tclient = svs.clients + (i-1);\n\n\tSV_SpawnParmsToQC(client);\n}\n\n/*\n==============\nPF_changelevel\n==============\n*/\nvoid QCBUILTIN PF_changelevel (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tchar newmap[MAX_QPATH];\n\tchar startspot[MAX_QPATH];\n\n// make sure we don't issue two changelevels (unless the last one failed)\n\tif (sv.mapchangelocked)\n\t\treturn;\n\tsv.mapchangelocked = true;\n\n#ifdef HAVE_CLIENT\n\tLog_MapNowCompleted();\n#endif\n\n#ifdef HEXEN2\n\tif (progstype == PROG_H2)\n\t{\n\t\tCOM_QuotedString(PR_GetStringOfs(prinst, OFS_PARM1), startspot, sizeof(startspot), false);\n\t\t//these flags disable the whole levelcache thing. the startspot is meant to still and always be specified.\n\t\t//hexen2 ALWAYS specifies two arguments, and it seems that raven left it blank in some single-player maps too.\n\t\t//if we don't want to be stupid/broken in deathmatch, we might as well do the fully compatible thing\n\t\tif ((int)pr_global_struct->serverflags & (16|32))\n\t\t{\n\t\t\tCOM_QuotedString(va(\"*%s\", PR_GetStringOfs(prinst, OFS_PARM0)), newmap, sizeof(newmap), false);\n\t\t\tCbuf_AddText (va(\"\\nchangelevel %s %s\\n\", newmap, startspot), RESTRICT_LOCAL);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tCOM_QuotedString(PR_GetStringOfs(prinst, OFS_PARM0), newmap, sizeof(newmap), false);\n\t\t\tCbuf_AddText (va(\"\\nchangelevel %s %s\\n\", newmap, startspot), RESTRICT_LOCAL);\n\t\t}\n\t}\n\telse\n#endif\n\t{\n\t\tCOM_QuotedString(PR_GetStringOfs(prinst, OFS_PARM0), newmap, sizeof(newmap), false);\n\t\tif (svprogfuncs->callargc == 2)\n\t\t{\n\t\t\tCOM_QuotedString(PR_GetStringOfs(prinst, OFS_PARM1), startspot, sizeof(startspot), false);\n\t\t\tCbuf_AddText (va(\"\\nchangelevel %s %s\\n\", newmap, startspot), RESTRICT_LOCAL);\n\t\t}\n\t\telse\n\t\t\tCbuf_AddText (va(\"\\nchangelevel %s\\n\", newmap), RESTRICT_LOCAL);\n\t}\n}\n\n\n/*\n==============\nPF_logfrag\n\nlogfrag (killer, killee)\n==============\n*/\nvoid QCBUILTIN PF_logfrag (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\textern cvar_t fraglog_details;\n\tedict_t\t*ent1, *ent2;\n\tint\t\te1, e2;\n\tchar\ts[2048];\n\tint slen;\n\tsizebuf_t *sz;\n\n\tent1 = G_EDICT(prinst, OFS_PARM0);\n\tent2 = G_EDICT(prinst, OFS_PARM1);\n\n\te1 = NUM_FOR_EDICT(prinst, ent1)-1;\n\te2 = NUM_FOR_EDICT(prinst, ent2)-1;\n\n\tif (e1 < 0 || e1 >= sv.allocated_client_slots\n\t|| e2 < 0 || e2 >= sv.allocated_client_slots)\n\t\treturn;\n\n#ifdef SVRANKING\n\tif (e1 != e2)\t//don't get a point for suicide.\n\t\tsvs.clients[e1].kills += 1;\n\tsvs.clients[e2].deaths += 1;\n#endif\n\n\tif (!fraglog_details.ival)\n\t\treturn;\t//not logging any details\n\telse if (fraglog_details.ival == 7)\n\t\tQ_snprintfz(s, sizeof(s)-2, \"\\\\frag\\\\\");\t//compat with mvdsv.\n\telse if (fraglog_details.ival == 1)\n\t\tstrcpy(s, \"\\\\\");//vanilla compat\n\telse\t//specify what info is actually in there\n\t\tQ_snprintfz(s, sizeof(s)-2, \"\\\\\\\\%u\\\\\", fraglog_details.ival);\n\tslen = strlen(s);\n\tif (fraglog_details.ival & 1)\n\t{\n\t\tQ_snprintfz(s+slen, sizeof(s)-2-slen, \"%s\\\\%s\\\\\", svs.clients[e1].name, svs.clients[e2].name);\n\t\tslen += strlen(s+slen);\n\t}\n\tif (fraglog_details.ival & 2)\n\t{\n\t\tQ_snprintfz(s+slen, sizeof(s)-2-slen, \"%s\\\\%s\\\\\", svs.clients[e1].team, svs.clients[e2].team);\n\t\tslen += strlen(s+slen);\n\t}\n\tif (fraglog_details.ival & 4)\n\t{\n\t\ttime_t t = time (NULL);\n\t\tstruct tm *tm = gmtime(&t);\n\t\tQ_snprintfz(s+slen, sizeof(s)-2-slen, \"%d-%d-%d %d:%d:%d\\\\\", tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec);\n\t\tslen += strlen(s+slen);\n\t}\n\tif (fraglog_details.ival & 8)\n\t{\n\t\tQ_snprintfz(s+slen, sizeof(s)-2-slen, \"%g\\\\\", ent1->v->weapon);\n\t\tslen += strlen(s+slen);\n\t}\n\tif (fraglog_details.ival & 16)\n\t{\n\t\tQ_snprintfz(s+slen, sizeof(s)-2-slen, \"%s\\\\%s\\\\\", svs.clients[e1].guid, svs.clients[e2].guid);\n\t\tslen += strlen(s+slen);\n\t}\n\ts[slen++] = '\\n';\n\n\t//print it to the fraglog buffer for masters/etc to query\n\tsz = &svs.log[svs.logsequence&(FRAGLOG_BUFFERS-1)];\n\tif (sz->cursize && sz->cursize+slen+1 >= sz->maxsize)\n\t{\n\t\t// swap buffers and bump sequence\n\t\tsvs.logtime = realtime;\n\t\tsvs.logsequence++;\n\t\tsz = &svs.log[svs.logsequence&(FRAGLOG_BUFFERS-1)];\n\t\tsz->cursize = 0;\n\t\tCon_TPrintf (\"beginning fraglog sequence %i\\n\", svs.logsequence);\n\t}\n\tSZ_Write(sz, s, slen);\n\n\t//print it to our local fraglog file.\n\tif (sv_fraglogfile)\n\t{\n\t\tVFS_WRITE(sv_fraglogfile, s, strlen(s));\n\t\tVFS_FLUSH (sv_fraglogfile);\n\t}\n}\n\n\n/*\n==============\nPF_infokey\n\nstring(entity e, string key) infokey\n==============\n*/\nchar *PF_infokey_Internal (int entnum, const char *key)\n{\n\tchar\t*value;\n\tstatic char ov[256];\n\n\tif (entnum == 0)\n\t{\n\t\tif (pr_imitatemvdsv.value && !strcmp(key, \"*version\"))\n\t\t\tvalue = \"2.40\";\n\t\telse if (!strcmp(key, \"modelname\"))\t//for compat with mvdsv.\n\t\t\tvalue = sv.modelname;\n\t\telse\n\t\t{\n\t\t\tif ((value = InfoBuf_ValueForKey(&svs.info, key)) == NULL || !*value)\n\t\t\t\tvalue = InfoBuf_ValueForKey(&svs.localinfo, key);\n\t\t}\n\t}\n\telse if (entnum <= sv.allocated_client_slots)\n\t{\n\t\tclient_t *pl = &svs.clients[entnum-1];\n\t\tclient_t *controller = pl->controller?pl->controller:pl;\n\t\tvalue = ov;\n\t\tif (!strcmp(key, \"ip\"))\n\t\t{\n\t\t\tif (controller->state > cs_zombie && controller->protocol == SCP_BAD)\n\t\t\t\tsprintf(ov, \"bot\");\t//bots don't have valid ips\n\t\t\telse if (controller->netchan.remote_address.type == NA_INVALID)\n\t\t\t\tsprintf(ov, \"\");\t//bots don't have valid ips\n\t\t\telse\n\t\t\t\tNET_BaseAdrToString (ov, sizeof(ov), &controller->netchan.remote_address);\n\t\t}\n\t\telse if (!strcmp(key, \"realip\"))\n\t\t{\n\t\t\tif (pl->state > cs_zombie && controller->protocol == SCP_BAD)\n\t\t\t\tsprintf(ov, \"bot\");\t//bots don't have valid ips\n\t\t\telse if (controller->realip_status)\n\t\t\t\tNET_BaseAdrToString (ov, sizeof(ov), &controller->realip);\n\t\t\telse if (controller->netchan.remote_address.type == NA_INVALID)\n\t\t\t\tsprintf(ov, \"\");\t//bots don't have valid ips\n\t\t\telse\t//FIXME: should we report the spoofable/proxy address if the real ip is not known?\n\t\t\t\tNET_BaseAdrToString (ov, sizeof(ov), &controller->netchan.remote_address);\n\t\t}\n\t\telse if (!strcmp(key, \"csqcactive\") || !strcmp(key, \"*csqcactive\"))\n\t\t\tsprintf(ov, \"%d\", controller->csqcactive);\n\t\telse if (!strcmp(key, \"ping\"))\n\t\t\tsprintf(ov, \"%d\", SV_CalcPing (&svs.clients[entnum-1], false));\n\t\telse if (!strcmp(key, \"svping\"))\n\t\t\tsprintf(ov, \"%d\", SV_CalcPing (&svs.clients[entnum-1], true));\n\t\telse if (!strcmp(key, \"guid\"))\n\t\t\tsprintf(ov, \"%s\", pl->guid);\n\t\telse if (!strcmp(key, \"*cert_dn\"))\n\t\t\tNET_GetConnectionCertificate(svs.sockets, &controller->netchan.remote_address, QCERT_PEERSUBJECT, ov, sizeof(ov));\n\t\telse if (!strncmp(key, \"*cert_\", 6))\n\t\t{\n\t\t\tstatic struct\n\t\t\t{\n\t\t\t\tconst char *name;\n\t\t\t\thashfunc_t *func;\n\t\t\t} funcs[] = {{\"sha1\",&hash_sha1}, {\"sha2_256\", &hash_sha2_256}, {\"sha2_512\", &hash_sha2_512}};\n\t\t\tint i;\n\t\t\tchar buf[8192];\n\t\t\tchar digest[DIGEST_MAXSIZE];\n\t\t\tint certsize;\n\t\t\t*ov = 0;\n\t\t\tfor (i = 0; i < countof(funcs); i++)\n\t\t\t{\n\t\t\t\tif (!strcmp(key+6, funcs[i].name))\n\t\t\t\t{\n\t\t\t\t\tcertsize = NET_GetConnectionCertificate(svs.sockets, &controller->netchan.remote_address, QCERT_PEERCERTIFICATE, buf, sizeof(buf));\n\t\t\t\t\tif (certsize > 0)\n\t\t\t\t\t\tBase64_EncodeBlockURI(digest,CalcHash(funcs[i].func, digest, sizeof(digest), buf, certsize), ov, sizeof(ov));\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse if (!strcmp(key, \"challenge\"))\n\t\t\tsprintf(ov, \"%u\", pl->challenge);\n\t\telse if (!strcmp(key, \"*userid\"))\n\t\t\tsprintf(ov, \"%d\", pl->userid);\n\t\telse if (!strcmp(key, \"download\"))\n\t\t\tsprintf(ov, \"%d\", pl->download != NULL ? (int)(100*pl->downloadcount/pl->downloadsize) : -1);\n//\t\telse if (!strcmp(key, \"login\"))\t//mvdsv\n//\t\t\tvalue = \"\";\n\t\telse if (!strcmp(key, \"protocol\"))\n\t\t{\n\t\t\tvalue = \"\";\n\t\t\tswitch(controller->protocol)\n\t\t\t{\n\t\t\tcase SCP_BAD:\n\t\t\t\tvalue = \"\";\t//could be a writebyted bot...\n\t\t\t\tbreak;\n\t\t\tcase SCP_QUAKEWORLD:\n\t\t\t\tif (!controller->fteprotocolextensions && !controller->fteprotocolextensions2)\n\t\t\t\t\tvalue = \"quakeworld\";\n\t\t\t\telse\n\t\t\t\t\tvalue = \"quakeworld+\";\n\t\t\t\tbreak;\n\t\t\tcase SCP_QUAKE2EX:\n\t\t\t\tvalue = \"q2e\";\t//shouldn't happen\n\t\t\t\tbreak;\n\t\t\tcase SCP_QUAKE2:\n\t\t\t\tvalue = \"quake2\";\t//shouldn't happen\n\t\t\t\tbreak;\n\t\t\tcase SCP_QUAKE3:\n\t\t\t\tvalue = \"quake3\";\t//can actually happen.\n\t\t\t\tbreak;\n\t\t\tcase SCP_NETQUAKE:\n\t\t\t\tif (controller->qex)\n\t\t\t\t\tvalue = \"qex15\";\n\t\t\t\telse\n\t\t\t\t\tvalue = \"quake\";\n\t\t\t\tbreak;\n\t\t\tcase SCP_BJP3:\n\t\t\t\tvalue = \"bjp3\";\n\t\t\t\tbreak;\n\t\t\tcase SCP_FITZ666:\n\t\t\t\tif (controller->qex)\n\t\t\t\t{\n\t\t\t\t\tif (controller->netchan.netprim.coordtype != COORDTYPE_FIXED_13_3)\n\t\t\t\t\t\tvalue = \"qex999\";\n\t\t\t\t\telse\n\t\t\t\t\t\tvalue = \"qex666\";\n\t\t\t\t}\n\t\t\t\telse if (controller->netchan.netprim.coordtype != COORDTYPE_FIXED_13_3)\n\t\t\t\t\tvalue = controller->fteprotocolextensions2?\"rmq999+fte2\":\"rmq999\";\n\t\t\t\telse\n\t\t\t\t\tvalue = controller->fteprotocolextensions2?\"fitz666+fte2\":\"fitz666\";\n\t\t\t\tbreak;\n\t\t\tcase SCP_DARKPLACES6:\n\t\t\t\tvalue = \"dpp6\";\n\t\t\t\tbreak;\n\t\t\tcase SCP_DARKPLACES7:\n\t\t\t\tvalue = \"dpp7\";\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\telse if (!strcmp(key, \"trustlevel\"))\t//info for progs.\n\t\t{\n#ifdef SVRANKING\n\t\t\trankstats_t rs;\n\t\t\tif (!pl->rankid)\n\t\t\t\tvalue = \"\";\n\t\t\telse if (Rank_GetPlayerStats(pl->rankid, &rs))\n\t\t\t\tsprintf(ov, \"%d\", rs.trustlevel);\n\t\t\telse\n#endif\n\t\t\t\tvalue = \"\";\n\t\t}\n\t\telse if (!strcmp(key, \"*VIP\"))\n\t\t\tvalue = (pl->penalties & BAN_VIP)?\"1\":\"\";\n\t\telse if (!strcmp(key, \"*ismuted\"))\n\t\t\tvalue = (pl->penalties & BAN_MUTE)?\"1\":\"\";\n\t\telse if (!strcmp(key, \"*isdeaf\"))\n\t\t\tvalue = (pl->penalties & BAN_DEAF)?\"1\":\"\";\n\t\telse if (!strcmp(key, \"*iscrippled\"))\n\t\t\tvalue = (pl->penalties & BAN_CRIPPLED)?\"1\":\"\";\n\t\telse if (!strcmp(key, \"*iscuffed\"))\n\t\t\tvalue = (pl->penalties & BAN_CUFF)?\"1\":\"\";\n\t\telse if (!strcmp(key, \"*islagged\"))\n\t\t\tvalue = (pl->penalties & BAN_LAGGED)?\"1\":\"\";\n\t\telse\n\t\t\tvalue = InfoBuf_ValueForKey (&pl->userinfo, key);\n\t} else\n\t\tvalue = \"\";\n\n\treturn value;\n}\n\nstatic void QCBUILTIN PF_infokey_s (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tedict_t\t\t*e = G_EDICT(prinst, OFS_PARM0);\n\tint\t\t\te1 = NUM_FOR_EDICT(prinst, e);\n\tconst char\t*key = PR_GetStringOfs(prinst, OFS_PARM1);\n\tconst char\t*value = PF_infokey_Internal (e1, key);\n\n\tG_INT(OFS_RETURN) = PR_TempString(prinst, value);\n}\nstatic void QCBUILTIN PF_infokey_f (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tedict_t\t*e;\n\tint\t\te1;\n\tchar\t*value;\n\tconst char\t*key;\n\n\te = G_EDICT(prinst, OFS_PARM0);\n\te1 = NUM_FOR_EDICT(prinst, e);\n\tkey = PR_GetStringOfs(prinst, OFS_PARM1);\n\n\tvalue = PF_infokey_Internal (e1, key);\n\n\tG_FLOAT(OFS_RETURN) = atof(value);\n}\n\nstatic void QCBUILTIN PF_infokey_blob (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tedict_t\t\t*e = G_EDICT(prinst, OFS_PARM0);\n\tconst char\t*key = PR_GetStringOfs(prinst, OFS_PARM1);\n\tint\t\t\tqcptr = (prinst->callargc>2)?G_INT(OFS_PARM2):0;\n\tint\t\t\tqcsize = (prinst->callargc>3)?G_INT(OFS_PARM3):0;\n\n\tunsigned\te1 = NUM_FOR_EDICT(prinst, e);\n\tsize_t\t\tblobsize = 0;\n\tconst char\t*value;\n\n\t//raw blob info, so no query hacks\n\tif (e1 == 0)\n\t{\n\t\tif ((value = InfoBuf_BlobForKey(&svs.info, key, &blobsize, NULL)) == NULL)\n\t\t\tvalue = InfoBuf_BlobForKey(&svs.localinfo, key, &blobsize, NULL);\n\t}\n\telse if (e1 <= (unsigned)sv.allocated_client_slots)\n\t\tvalue = InfoBuf_BlobForKey (&svs.clients[e1-1].userinfo, key, &blobsize, NULL);\n\telse\n\t\tvalue = NULL;\n\n\tif (qcptr)\n\t{\t//we were told somewhere to store it\n\t\tvoid *ptr = PR_GetWriteQCPtr(prinst, qcptr, qcsize);\n\t\tif (!ptr)\t//but the place was invalid?\n\t\t\tPR_BIError(prinst, \"PF_infokey_blob: invalid pointer/size\\n\");\n\t\telse\n\t\t{\n\t\t\tblobsize = min(blobsize, qcsize);\n\t\t\tmemcpy(ptr, value, blobsize);\n\t\t\tG_INT(OFS_RETURN) = blobsize;\n\t\t}\n\t}\n\telse //no output, so just return the size\n\t\tG_INT(OFS_RETURN) = blobsize;\n}\n\n\nstatic void QCBUILTIN PF_sv_serverkeystring (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char\t*key = PR_GetStringOfs(prinst, OFS_PARM0);\n\tconst char\t*value = InfoBuf_ValueForKey (&svs.info, key);\n\tG_INT(OFS_RETURN) = *value?PR_TempString(prinst, value):0;\n}\nstatic void QCBUILTIN PF_sv_serverkeyfloat (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char\t*key = PR_GetStringOfs(prinst, OFS_PARM0);\n\tconst char\t*value = InfoBuf_ValueForKey (&svs.info, key);\n\tif (*value)\n\t\tG_FLOAT(OFS_RETURN) = strtod(value, NULL);\n\telse\n\t\tG_FLOAT(OFS_RETURN) = (prinst->callargc>=2)?G_FLOAT(OFS_PARM1):0;\n}\nstatic void QCBUILTIN PF_sv_serverkeyblob (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tsize_t blobsize = 0;\n\tconst char\t*key = PR_GetStringOfs(prinst, OFS_PARM0);\n\tconst char\t*blobvalue = InfoBuf_BlobForKey(&svs.info, key, &blobsize, NULL);\n\n\tif ((prinst->callargc<2) || G_INT(OFS_PARM1) == 0)\n\t\tG_INT(OFS_RETURN) = blobsize;\t//no pointer to write to, just return the length.\n\telse\n\t{\n\t\tsize_t size = min(blobsize, G_INT(OFS_PARM2));\n\t\tvoid *ptr = PR_GetWriteQCPtr(prinst, G_INT(OFS_PARM1), size);\n\t\tif (!ptr)\n\t\t\tPR_BIError(prinst, \"PF_sv_serverkeyblob: invalid pointer/size\\n\");\n\t\tG_INT(OFS_RETURN) = size;\n\t\tmemcpy(ptr, blobvalue, size);\n\t}\n}\nstatic void QCBUILTIN PF_setserverkey (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char\t*key = PR_GetStringOfs(prinst, OFS_PARM0);\n\tconst char\t*value;\n\tsize_t size;\n\tif (prinst->callargc > 2)\n\t{\n\t\tsize = G_INT(OFS_PARM2);\n\t\tvalue = PR_GetReadQCPtr(prinst, G_INT(OFS_PARM1), size);\n\t}\n\telse\n\t{\n\t\tvalue = PR_GetStringOfs(prinst, OFS_PARM1);\n\t\tsize = strlen(value);\n\t}\n\tif (!value)\n\t\tPR_BIError(prinst, \"PF_setserverkey: invalid pointer/size\\n\");\n\n\tInfoBuf_SetStarBlobKey(&svs.info, key, value, size);\n}\n\n\nstatic void QCBUILTIN PF_getlocalinfo (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tsize_t blobsize = 0;\n\tconst char\t*key = PR_GetStringOfs(prinst, OFS_PARM0);\n\tconst char\t*blobvalue = InfoBuf_BlobForKey(&svs.localinfo, key, &blobsize, NULL);\n\n\tif ((prinst->callargc<2) || G_INT(OFS_PARM1) == 0)\n\t\tG_INT(OFS_RETURN) = blobsize;\t//no pointer to write to, just return the length.\n\telse\n\t{\n\t\tsize_t size = min(blobsize, G_INT(OFS_PARM2));\n\t\tvoid *ptr = PR_GetWriteQCPtr(prinst, G_INT(OFS_PARM1), size);\n\t\tif (!ptr)\n\t\t\tPR_BIError(prinst, \"PF_getlocalinfo: invalid pointer/size\\n\");\n\t\tG_INT(OFS_RETURN) = size;\n\t\tmemcpy(ptr, blobvalue, size);\n\t}\n}\nstatic void QCBUILTIN PF_setlocalinfo (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char\t*key = PR_GetStringOfs(prinst, OFS_PARM0);\n\tconst char\t*value;\n\tsize_t size;\n\tif (prinst->callargc > 2)\n\t{\n\t\tsize = G_INT(OFS_PARM2);\n\t\tvalue = PR_GetReadQCPtr(prinst, G_INT(OFS_PARM1), size);\n\t}\n\telse\n\t{\n\t\tvalue = PR_GetStringOfs(prinst, OFS_PARM1);\n\t\tsize = strlen(value);\n\t}\n\tif (!value)\n\t\tPR_BIError(prinst, \"PF_setlocalinfo: invalid pointer/size\\n\");\n\n\tInfoBuf_SetStarBlobKey(&svs.localinfo, key, value, size);\n}\n\n/*\n==============\nPF_multicast\n\nvoid(vector where, float set) multicast\n==============\n*/\nvoid QCBUILTIN PF_multicast (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tVM_VECTORARG(o, OFS_PARM0);\n\tint to = G_FLOAT(OFS_PARM1);\n\n#ifdef NETPREPARSE\n\tNPP_Flush();\n#endif\n\n\tif (sv_csqcdebug.ival)\n\t{\n#ifdef NQPROT\n\t\tif (sv.nqmulticast.cursize && (sv.nqmulticast.data[0] == svcfte_cgamepacket||sv.nqmulticast.data[0] == svc_temp_entity))\n\t\t{\n\t\t\tif (sv.nqmulticast.cursize + 2 > sv.nqmulticast.maxsize)\n\t\t\t\tsv.nqmulticast.cursize = 0;\n\t\t\telse\n\t\t\t{\n\t\t\t\t/*shift the data up by two bytes*/\n\t\t\t\tmemmove(sv.nqmulticast.data+3, sv.nqmulticast.data+1, sv.nqmulticast.cursize-1);\n\n\t\t\t\t/*add a length in the 2nd/3rd bytes*/\n\t\t\t\tif (sv.nqmulticast.data[0] == svc_temp_entity)\n\t\t\t\t\tsv.nqmulticast.data[0] = svcfte_temp_entity_sized;\n\t\t\t\telse\n\t\t\t\t\tsv.nqmulticast.data[0] = svcfte_cgamepacket_sized;\n\t\t\t\tsv.nqmulticast.data[1] = (sv.nqmulticast.cursize-1);\n\t\t\t\tsv.nqmulticast.data[2] = (sv.nqmulticast.cursize-1) >> 8;\n\n\t\t\t\tsv.nqmulticast.cursize += 2;\n\t\t\t}\n\t\t}\n#endif\n\t\tif (sv.multicast.cursize && (sv.multicast.data[0] == svcfte_cgamepacket||sv.multicast.data[0] == svc_temp_entity))\n\t\t{\n\t\t\tif (sv.multicast.cursize + 2 > sv.multicast.maxsize)\n\t\t\t\tsv.multicast.cursize = 0;\n\t\t\telse\n\t\t\t{\n\t\t\t\t/*shift the data up by two bytes*/\n\t\t\t\tmemmove(sv.multicast.data+3, sv.multicast.data+1, sv.multicast.cursize-1);\n\n\t\t\t\t/*add a length in the 2nd/3rd bytes*/\n\t\t\t\tif (sv.multicast.data[0] == svc_temp_entity)\n\t\t\t\t\tsv.multicast.data[0] = svcfte_temp_entity_sized;\n\t\t\t\telse\n\t\t\t\t\tsv.multicast.data[0] = svcfte_cgamepacket_sized;\n\t\t\t\tsv.multicast.data[1] = (sv.multicast.cursize-1);\n\t\t\t\tsv.multicast.data[2] = (sv.multicast.cursize-1) >> 8;\n\n\t\t\t\tsv.multicast.cursize += 2;\n\t\t\t}\n\t\t}\n\t}\n\n\tSV_MulticastProtExt(o, to, pr_global_struct->dimension_send, 0, 0);\n}\n\nstatic void QCBUILTIN PF_Ignore(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tG_INT(OFS_RETURN) = 0;\n}\n\n#ifdef HAVE_LEGACY\nstatic void QCBUILTIN PF_mvdsv_newstring(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\t//mvdsv\n{\n\tchar *s;\n\tint len;\n\n\tconst char *in = PR_GetStringOfs(prinst, OFS_PARM0);\n\n\tlen = strlen(in)+1;\n\tif (prinst->callargc == 2 && G_FLOAT(OFS_PARM1) > len)\n\t\tlen = G_FLOAT(OFS_PARM1);\n\n\ts = prinst->AddressableAlloc(prinst, len);\n\tG_INT(OFS_RETURN) = (char*)s - prinst->stringtable;\n\tstrcpy(s, in);\n}\nstatic void QCBUILTIN PF_mvdsv_freestring(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\t//mvdsv\n{\n\tprinst->AddressableFree(prinst, prinst->stringtable + G_INT(OFS_PARM0));\n}\n#endif\n\n/*\nstatic void QCBUILTIN PF_strcatp(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tchar *buf = PR_GetStringOfs(prinst, OFS_PARM0); char *add = PR_GetStringOfs(prinst, OFS_PARM1);\n\tint wantedlen = G_FLOAT(OFS_PARM2);\n\tint len;\n\tif (((int *)(buf-8))[0] != PRSTR)\n\t{\n\t\tCon_Printf(\"QC tried to add to a non allocated string\\n\");\n\t\t(*prinst->pr_trace) = 1;\n\t\tG_FLOAT(OFS_RETURN) = 0;\n\t\treturn;\n\t}\n\tlen = strlen(add);\n\tbuf+=strlen(buf);\n\tstrcat(buf, add);\n\tbuf+=len;\n\twhile(len++ < wantedlen)\n\t\t*buf++ = ' ';\n\t*buf = '\\0';\n\n\tG_INT(OFS_RETURN) = G_INT(OFS_PARM0);\n}\n*/\n\n/*\nstatic void QCBUILTIN PF_redstring(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tchar *string = PR_GetStringOfs(prinst, OFS_PARM0), *s;\n\tstatic char buf[1024];\n\n\tfor (s = buf; *string; s++, string++)\n\t\t*s=*string|CON_HIGHCHARSMASK;\n\t*s = '\\0';\n\n\tRETURN_TSTRING(buf);\n}\n*/\n\n#ifdef SVCHAT\nvoid SV_Chat(const char *filename, float starttag, edict_t *edict);\nstatic void QCBUILTIN PF_chat (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tSV_Chat(PR_GetStringOfs(prinst, OFS_PARM0), G_FLOAT(OFS_PARM1), G_EDICT(prinst, OFS_PARM2));\n}\n#endif\n\n\n\n\n\n\n\n\n/* FTE_SQL builtins */\n#ifdef SQL\nvoid QCBUILTIN PF_sqlconnect (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char *paramstr[SQL_CONNECT_PARAMS];\n\tconst char *driver;\n\tint i;\n\n\tif (!SQL_Available())\n\t{\n\t\tG_FLOAT(OFS_RETURN) = -1;\n\t\treturn;\n\t}\n\n\t// check and fit connection parameters\n\tfor (i = 0; i < SQL_CONNECT_PARAMS; i++)\n\t{\n\t\tif (svprogfuncs->callargc <= (i + 1))\n\t\t\tparamstr[i] = \"\";\n\t\telse\n\t\t\tparamstr[i] = PR_GetStringOfs(prinst, OFS_PARM0+i*3);\n\t}\n\n\tif (!paramstr[0][0])\n\t\tparamstr[0] = sql_host.string;\n\tif (!paramstr[1][0])\n\t\tparamstr[1] = sql_username.string;\n\tif (!paramstr[2][0])\n\t\tparamstr[2] = sql_password.string;\n\tif (!paramstr[3][0])\n\t\tparamstr[3] = sql_defaultdb.string;\n\n\t// verify/switch driver choice\n\tif (svprogfuncs->callargc >= (SQL_CONNECT_PARAMS + 1))\n\t\tdriver = PR_GetStringOfs(prinst, OFS_PARM0 + SQL_CONNECT_PARAMS * 3);\n\telse\n\t\tdriver = \"\";\n\n\tif (!driver[0])\n\t\tdriver = sql_driver.string;\n\n\tG_FLOAT(OFS_RETURN) = SQL_NewServer(prinst, driver, paramstr);\n}\n\nvoid QCBUILTIN PF_sqldisconnect (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tsqlserver_t *server;\n\n\tif (SQL_Available())\n\t{\n\t\tserver = SQL_GetServer(prinst, G_FLOAT(OFS_PARM0), false);\n\t\tif (server)\n\t\t{\n\t\t\tSQL_Disconnect(server);\n\t\t\treturn;\n\t\t}\n\t}\n}\n\nstatic qboolean PR_SQLResultAvailable(queryrequest_t *req, int firstrow, int numrows, int numcols, qboolean eof)\n{\n\tedict_t *ent;\n\tpubprogfuncs_t *prinst = svprogfuncs;\n\tstruct globalvars_s *pr_globals = PR_globals(prinst, PR_CURRENT);\n\n\tif (req->user.qccallback)\n\t{\n\t\tG_FLOAT(OFS_PARM0) = req->srvid;\n\t\tG_FLOAT(OFS_PARM1) = req->num;\n\t\tG_FLOAT(OFS_PARM2) = numrows;\n\t\tG_FLOAT(OFS_PARM3) = numcols;\n\t\tG_FLOAT(OFS_PARM4) = eof;\n\t\tG_FLOAT(OFS_PARM5) = firstrow;\n\n\t\t// recall self and other references\n\t\tent = PROG_TO_EDICT(prinst, req->user.selfent);\n\t\tif (ED_ISFREE(ent) || ent->xv->uniquespawnid != req->user.selfid)\n\t\t\tpr_global_struct->self = pr_global_struct->world;\n\t\telse\n\t\t\tpr_global_struct->self = req->user.selfent;\n\t\tent = PROG_TO_EDICT(prinst, req->user.otherent);\n\t\tif (ED_ISFREE(ent) || ent->xv->uniquespawnid != req->user.otherid)\n\t\t\tpr_global_struct->other = pr_global_struct->world;\n\t\telse\n\t\t\tpr_global_struct->other = req->user.otherent;\n\n\t\tPR_ExecuteProgram(svprogfuncs, req->user.qccallback);\n\t}\n\tif (eof && req->user.thread)\n\t{\n\t\tqcstate_t *thread = req->user.thread;\n\t\treq->user.thread = NULL;\n\t\tif (thread)\n\t\t\tthread->waiting = false;\n\t}\n\n\treturn req->user.persistant;\n}\n\nvoid QCBUILTIN PF_sqlopenquery (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tsqlserver_t *server;\n\tint callfunc = G_INT(OFS_PARM1);\n\tint querytype = G_FLOAT(OFS_PARM2);\n\tconst char *querystr = PF_VarString(prinst, 3, pr_globals);\n\n\tif (SQL_Available())\n\t{\n\t\tserver = SQL_GetServer(prinst, G_FLOAT(OFS_PARM0), false);\n\t\tif (server)\n\t\t{\n\t\t\tqueryrequest_t *qreq;\n\n\t\t\tCon_DPrintf(\"SQL Query: %s\\n\", querystr);\n\n\t\t\tG_FLOAT(OFS_RETURN) = SQL_NewQuery(server, PR_SQLResultAvailable, querystr, &qreq);\n\n\t\t\tif (qreq)\n\t\t\t{\n\t\t\t\t//so our C callback knows what to do\n\t\t\t\tqreq->user.persistant = querytype > 0;\n\t\t\t\tqreq->user.qccallback = callfunc;\n\n\t\t\t\t// save self and other references\n\t\t\t\tqreq->user.selfent = ED_ISFREE(PROG_TO_EDICT(prinst, pr_global_struct->self))?pr_global_struct->world:pr_global_struct->self;\n\t\t\t\tqreq->user.selfid = PROG_TO_EDICT(prinst, qreq->user.selfent)->xv->uniquespawnid;\n\t\t\t\tqreq->user.otherent = ED_ISFREE(PROG_TO_EDICT(prinst, pr_global_struct->other))?pr_global_struct->world:pr_global_struct->other;\n\t\t\t\tqreq->user.otherid = PROG_TO_EDICT(prinst, qreq->user.otherent)->xv->uniquespawnid;\n\n\t\t\t\tif (querytype & 2)\n\t\t\t\t{\n\t\t\t\t\tqreq->user.thread = PR_CreateThread(prinst, G_FLOAT(OFS_RETURN), 0, true);\n\n\t\t\t\t\tsvprogfuncs->AbortStack(prinst);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t}\n\t// else we failed so return the error\n\tG_FLOAT(OFS_RETURN) = -1;\n}\n\nvoid QCBUILTIN PF_sqlclosequery (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tsqlserver_t *server;\n\tqueryrequest_t *qreq;\n\n\tif (SQL_Available())\n\t{\n\t\tserver = SQL_GetServer(prinst, G_FLOAT(OFS_PARM0), false);\n\t\tif (server)\n\t\t{\n\t\t\tqreq = SQL_GetQueryRequest(server, G_FLOAT(OFS_PARM1));\n\t\t\tif (qreq)\n\t\t\t{\n\t\t\t\tSQL_CloseRequest(server, qreq, false);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\telse \n\t\t\t\tCon_Printf(\"Invalid sql request\\n\");\n\t\t}\n\t}\n\t// else nothing to close\n}\n\nvoid QCBUILTIN PF_sqlreadfield (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tsqlserver_t *server;\n\tqueryresult_t *qres;\n\tchar *data;\n\n\tif (SQL_Available())\n\t{\n\t\tserver = SQL_GetServer(prinst, G_FLOAT(OFS_PARM0), false);\n\t\tif (server)\n\t\t{\n\t\t\tqres = SQL_GetQueryResult(server, G_FLOAT(OFS_PARM1), G_FLOAT(OFS_PARM2));\n\t\t\tif (qres)\n\t\t\t{\n\t\t\t\tdata = SQL_ReadField(server, qres, G_FLOAT(OFS_PARM2), G_FLOAT(OFS_PARM3), true, NULL);\n\t\t\t\tif (data)\n\t\t\t\t{\n\t\t\t\t\tRETURN_TSTRING(data);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tCon_Printf(\"Invalid sql request/row\\n\");\n\t\t\t}\n\t\t}\n\t}\n\t// else we failed to get anything\n\tG_INT(OFS_RETURN) = 0;\n}\n\nvoid QCBUILTIN PF_sqlreadfloat (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tsqlserver_t *server;\n\tqueryrequest_t *qreq;\n\tqueryresult_t *qres;\n\tchar *data;\n\n\tif (SQL_Available())\n\t{\n\t\tserver = SQL_GetServer(prinst, G_FLOAT(OFS_PARM0), false);\n\t\tif (server)\n\t\t{\n\t\t\tif (G_FLOAT(OFS_PARM2) < 0)\n\t\t\t{\n\t\t\t\tqreq = SQL_GetQueryRequest(server, G_FLOAT(OFS_PARM1));\n\t\t\t\tif (qreq->results)\n\t\t\t\t{\n\t\t\t\t\tif (G_FLOAT(OFS_PARM2) == -2)\n\t\t\t\t\t\tG_FLOAT(OFS_RETURN) = qreq->results->columns;\n\t\t\t\t\telse if (G_FLOAT(OFS_PARM2) == -3)\n\t\t\t\t\t\tG_FLOAT(OFS_RETURN) = qreq->results->rows + qreq->results->firstrow;\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tCon_Printf(\"Invalid sql row\\n\");\n\t\t\t\t\t\tG_FLOAT(OFS_RETURN) = 0;\n\t\t\t\t\t}\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tqres = SQL_GetQueryResult(server, G_FLOAT(OFS_PARM1), G_FLOAT(OFS_PARM2));\n\t\t\t\tif (qres)\n\t\t\t\t{\n\t\t\t\t\tif (G_FLOAT(OFS_PARM2) == -1)\n\t\t\t\t\t{\n\t\t\t\t\t\tG_FLOAT(OFS_RETURN) = qres->columns;\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tif (G_FLOAT(OFS_PARM2) == -2)\n\t\t\t\t\t{\n\t\t\t\t\t\tG_FLOAT(OFS_RETURN) = qres->rows;\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tdata = SQL_ReadField(server, qres, G_FLOAT(OFS_PARM2), G_FLOAT(OFS_PARM3), true, NULL);\n\t\t\t\t\tif (data)\n\t\t\t\t\t{\n\t\t\t\t\t\tG_FLOAT(OFS_RETURN) = Q_atof(data);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tCon_Printf(\"Invalid sql request/row\\n\");\n\t\t\t\t\tPR_StackTrace(prinst, false);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\t// else we failed to get anything\n\tG_FLOAT(OFS_RETURN) = 0;\n}\n\nvoid QCBUILTIN PF_sqlreadblob (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tsqlserver_t *server;\n\tqueryresult_t *qres;\n\tchar *data;\n\n\tint serveridx = G_FLOAT(OFS_PARM0);\n\tint queryidx = G_FLOAT(OFS_PARM1);\n\tint row = G_FLOAT(OFS_PARM2);\n\tint column = G_FLOAT(OFS_PARM3);\n\tint dst = G_INT(OFS_PARM4);\n\tint dstsize = G_INT(OFS_PARM5);\n\n\tif (dst <= 0 || dst+dstsize >= prinst->stringtablesize)\n\t{\t//FIXME: this check should be some utility function.\n\t\tPR_BIError(prinst, \"PF_sqlreadblob: invalid dest\\n\");\n\t\treturn;\n\t}\n\n\tif (SQL_Available())\n\t{\n\t\tserver = SQL_GetServer(prinst, serveridx, false);\n\t\tif (server)\n\t\t{\n\t\t\tqres = SQL_GetQueryResult(server, queryidx, row);\n\t\t\tif (qres)\n\t\t\t{\n\t\t\t\tsize_t blobsize;\n\t\t\t\tdata = SQL_ReadField(server, qres, row, column, true, &blobsize);\n\t\t\t\tif (data)\n\t\t\t\t{\t//unsure how to handle overflows. we truncate for now.\n\t\t\t\t\tblobsize = min(blobsize, dstsize);\n\t\t\t\t\tG_INT(OFS_RETURN) = blobsize;\n\t\t\t\t\tmemcpy(prinst->stringtable + dst, data, blobsize);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tCon_Printf(\"Invalid sql request/row\\n\");\n\t\t\t\tPR_StackTrace(prinst, false);\n\t\t\t}\n\t\t}\n\t}\n\t// else we failed to get anything\n\tG_INT(OFS_RETURN) = 0;\n}\nvoid QCBUILTIN PF_sqlescapeblob (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tchar hex[16] = \"0123456789abcdef\";\n//\tint serveridx = G_FLOAT(OFS_PARM0);\t//fixme\n\tint qcptr = G_INT(OFS_PARM1);\n\tint size = G_INT(OFS_PARM2);\n\tchar *out;\n\n\tqbyte *blob = prinst->stringtable + qcptr;\n\t\n\tif (qcptr <= 0 || qcptr+size >= prinst->stringtablesize)\n\t{\t//FIXME: this check should be some utility function.\n\t\tPR_BIError(prinst, \"PF_sqlescapeblob: invalid blob\\n\");\n\t\treturn;\n\t}\n\n\tG_INT(OFS_RETURN) = prinst->AllocTempString(prinst, &out, size*2+4);\n\n\t//\"x'DEADBEEF'\"\n\t*out++ = 'x';\n\t*out++ = '\\'';\n\tfor (; size > 0; size--, blob++)\n\t{\n\t\t*out++ = hex[*blob>>4];\n\t\t*out++ = hex[*blob&15];\n\t}\n\t*out++ = '\\'';\n\t*out++ = 0;\n}\n\nvoid QCBUILTIN PF_sqlerror (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tsqlserver_t *server;\n\n\tif (SQL_Available())\n\t{\n\t\tserver = SQL_GetServer(prinst, G_FLOAT(OFS_PARM0), true);\n\t\tif (server)\n\t\t{\n\t\t\tif (svprogfuncs->callargc == 2)\n\t\t\t{ // query-specific error request\n\t\t\t\tif (server->active) // didn't check this earlier so check it now\n\t\t\t\t{\n\t\t\t\t\tqueryresult_t *qres = SQL_GetQueryResult(server, G_FLOAT(OFS_PARM1), G_FLOAT(OFS_PARM2));\n\t\t\t\t\tif (qres)\n\t\t\t\t\t{\n\t\t\t\t\t\tRETURN_TSTRING(qres->error);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (server->serverresult)\n\t\t\t{ // server-specific error request\n\t\t\t\tRETURN_TSTRING(server->serverresult->error);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n\t// else we didn't get a server or query\n\tRETURN_TSTRING(\"\");\n}\n\nvoid QCBUILTIN PF_sqlescape (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tsqlserver_t *server;\n\tconst char *toescape;\n\tchar escaped[4096];\n\n\tif (SQL_Available())\n\t{\n\t\tserver = SQL_GetServer(prinst, G_FLOAT(OFS_PARM0), false);\n\t\tif (server)\n\t\t{\n\t\t\ttoescape = PR_GetStringOfs(prinst, OFS_PARM1);\n\t\t\tif (toescape)\n\t\t\t{\n\t\t\t\tSQL_Escape(server, toescape, escaped, sizeof(escaped));\n\t\t\t\tRETURN_TSTRING(escaped);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n\t// else invalid string or server reference\n\tRETURN_TSTRING(\"\");\n}\n\nvoid QCBUILTIN PF_sqlversion (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tsqlserver_t *server;\n\n\tif (SQL_Available())\n\t{\n\t\tserver = SQL_GetServer(prinst, G_FLOAT(OFS_PARM0), false);\n\t\tif (server)\n\t\t{\n\t\t\tRETURN_TSTRING(SQL_Info(server));\n\t\t\treturn;\n\t\t}\n\t}\n\t// else invalid string or server reference\n\tRETURN_TSTRING(\"\");\n}\n\nvoid PR_SQLCycle(void)\n{\n\tSQL_ServerCycle();\n}\n#endif\n\n\n\n/*\nstatic qc_extension_t *checkfteextensioncl(int mask, const char *name)\t//true if the cient extension mask matches an extension name\n{\n\tint i;\n\tfor (i = 0; i < 32; i++)\n\t{\n\t\tif (mask & (1<<i))\t//suported\n\t\t{\n\t\t\tif (QSG_Extensions[i].name)\t//some were removed\n\t\t\t\tif (!stricmp(name, QSG_Extensions[i].name))\t//name matches\n\t\t\t\t\treturn &QSG_Extensions[i];\n\t\t}\n\t}\n\treturn NULL;\n}\n\nstatic qc_extension_t *checkfteextensionsv(const char *name)\t//true if the server supports an protocol extension.\n{\n\treturn checkfteextensioncl(Net_PextMask(PROTOCOL_VERSION_FTE1, false), name);\n}\n\nstatic qc_extension_t *checkextension(const char *name)\n{\n\tint i;\n\tfor (i = 0; i < QSG_Extensions_count; i++)\n\t{\n\t\tif (!QSG_Extensions[i].name)\n\t\t\tcontinue;\n\t\tif (!stricmp(name, QSG_Extensions[i].name))\n\t\t\treturn &QSG_Extensions[i];\n\t}\n\treturn NULL;\n}*/\n\n/*\n=================\nPF_checkextension\n\nreturns true if the extension is supported by the server\n\ncheckextension(string extensionname, [entity client])\n=================\n*/\nstatic void QCBUILTIN PF_checkextension (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tqc_extension_t *ext;\n\tconst char *s = PR_GetStringOfs(prinst, OFS_PARM0);\n\tint clnum = (svprogfuncs->callargc == 2)?NUM_FOR_EDICT(prinst, G_EDICT(prinst, OFS_PARM1)):0;\n\tint i;\n\tcvar_t *v;\n\tchar *cvn;\n\n\tG_FLOAT(OFS_RETURN) = false;\n\n\tfor (i = 0; i < QSG_Extensions_count; i++)\n\t{\n\t\tif (!stricmp(s, QSG_Extensions[i].name))\n\t\t{\n\t\t\text = &QSG_Extensions[i];\n\n\t\t\tif (ext->extensioncheck && clnum >= 1 && clnum <= sv.allocated_client_slots)\n\t\t\t{\n\t\t\t\tclient_t *cl = &svs.clients[clnum-1];\n\t\t\t\textcheck_t check = {prinst->parms->user, cl->fteprotocolextensions, cl->fteprotocolextensions2};\n\t\t\t\tif (!ext->extensioncheck(&check))\n\t\t\t\t\treturn;\t//blocked by some setting somewhere, somehow.\n\t\t\t}\n\t\t\telse if (ext->extensioncheck)\n\t\t\t{\n\t\t\t\textcheck_t check = {prinst->parms->user, Net_PextMask(PROTOCOL_VERSION_FTE1, false)&PEXT_SERVERADVERTISE, Net_PextMask(PROTOCOL_VERSION_FTE2, false)&PEXT2_SERVERADVERTISE};\n\t\t\t\tif (!ext->extensioncheck(&check))\n\t\t\t\t\treturn;\t//blocked by some setting somewhere, somehow.\n\t\t\t}\n\n\t\t\t//user cvar blocks. note that the qc isn't meant to be aware of them, we use CVAR_NOUNSAFEEXPAND so mods don't can't bypass checkextension in a non-portable way.\n\t\t\tcvn = va(\"pr_ext_%s\", ext->name);\n\t\t\tfor (i = 0; cvn[i]; i++)\n\t\t\t\tif (cvn[i] >= 'A' && cvn[i] <= 'Z')\n\t\t\t\t\tcvn[i] = 'a' + (cvn[i]-'A');\n\t\t\tv = Cvar_Get2(cvn, \"1\", CVAR_ARCHIVE|CVAR_NOUNSAFEEXPAND, \"Set to 0 to block detection of the extension, or 1 to enable it.\", \"QC Extensions\");\n\t\t\tif (v && !v->ival)\n\t\t\t{\n\t\t\t\tif (!*v->string && (v->flags&CVAR_POINTER))\n\t\t\t\t\t;\t//user-defined cvars arn't explicitly defined elsewhere. interpret them as 1 when empty, because they're probably old ones that we're no longer trying to block...\n\t\t\t\telse\n\t\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tfor (i = 0; i < countof(ext->builtinnames) && ext->builtinnames[i]; i++)\n\t\t\t{\n\t\t\t\tif (*ext->builtinnames[i] == '.' || *ext->builtinnames[i] == '#')\n\t\t\t\t{\n\t\t\t\t\t/*field or global*/\n\t\t\t\t}\n\t\t\t\telse if (!PR_EnableEBFSBuiltin(ext->builtinnames[i], 0))\n\t\t\t\t{\n\t\t\t\t\tCon_Printf(\"Failed to initialise builtin \\\"%s\\\" for extension \\\"%s\\\"\\n\", ext->builtinnames[i], s);\n\t\t\t\t\treturn;\t//whoops, we failed.\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tG_FLOAT(OFS_RETURN) = true;\n\t\t\tCon_DPrintf(\"Extension %s is supported\\n\", s);\n\t\t\tbreak;\n\t\t}\n\t}\n\n}\n\nstatic void QCBUILTIN PF_checkbuiltin (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tfunc_t funcref = G_INT(OFS_PARM0);\n\tchar *funcname = NULL;\n\tint args;\n\tint builtinno;\n\tif (prinst->GetFunctionInfo(prinst, funcref, &args, NULL, &builtinno, funcname, sizeof(funcname)))\n\t{\t//qc defines the function at least. nothing weird there...\n\t\tif (builtinno > 0 && builtinno < prinst->parms->numglobalbuiltins)\n\t\t{\n\t\t\tif (!prinst->parms->globalbuiltins[builtinno] || prinst->parms->globalbuiltins[builtinno] == PF_Fixme || prinst->parms->globalbuiltins[builtinno] == PF_Ignore)\n\t\t\t\tG_FLOAT(OFS_RETURN) = false;\t//the builtin with that number isn't defined.\n\t\t\telse\n\t\t\t{\n\t\t\t\tG_FLOAT(OFS_RETURN) = true;\t\t//its defined, within the sane range, mapped, everything. all looks good.\n\t\t\t\t//we should probably go through the available builtins and validate that the qc's name matches what would be expected\n\t\t\t\t//this is really intended more for builtins defined as #0 though, in such cases, mismatched assumptions are impossible.\n\t\t\t}\n\t\t}\n\t\telse\n\t\t\tG_FLOAT(OFS_RETURN) = false;\t//not a valid builtin (#0 builtins get remapped according to the function name)\n\t}\n\telse\n\t{\t//not valid somehow.\n\t\tG_FLOAT(OFS_RETURN) = false;\n\t}\n}\n\nstatic void QCBUILTIN PF_builtinsupported (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char *s = PR_GetStringOfs(prinst, OFS_PARM0);\n\tint binum = (prinst->callargc < 2)?0:G_FLOAT(OFS_PARM1);\n\n\tG_FLOAT(OFS_RETURN) = !!PR_EnableEBFSBuiltin(s, binum);\n}\n\n\n\n\n//mvdsv builtins.\nvoid QCBUILTIN PF_ExecuteCommand  (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\t//83\t\t//void() exec;\n{\n\tint osc = svs.spawncount;\n\tint old_other, old_self; // mod_consolecmd will be executed, so we need to store this\n\n\told_self = pr_global_struct->self;\n\told_other = pr_global_struct->other;\n\n\tCbuf_Execute();\n\n\tif (osc != svs.spawncount)\n\t\tHost_EndGame(\"PF_ExecuteCommand: gamecode pulled out from under us\");\n\n\tpr_global_struct->self = old_self;\n\tpr_global_struct->other = old_other;\n}\n\n#ifdef HAVE_LEGACY\n/*\n=================\nPF_teamfield\n\nstring teamfield(.string field)\n=================\n*/\n\nstatic void QCBUILTIN PF_teamfield (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tpr_teamfield = G_INT(OFS_PARM0)+prinst->fieldadjust;\n}\n\n/*\n=================\nPF_substr\n\nstring substr(string str, float start, float len)\n=================\n*/\n\nstatic void QCBUILTIN PF_substr (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tchar dest[4096];\n\tconst char *s;\n\tint start, len, l;\n\n\ts = PR_GetStringOfs(prinst, OFS_PARM0);\n\tstart = (int) G_FLOAT(OFS_PARM1);\n\tlen = (int) G_FLOAT(OFS_PARM2);\n\tl = strlen(s);\n\n\tif (start >= l || len<=0 || !*s)\n\t{\n\t\tRETURN_TSTRING(\"\");\n\t\treturn;\n\t}\n\n\ts += start;\n\tl -= start;\n\n\tif (len > l + 1)\n\t\tlen = l + 1;\n\n\tif (len > sizeof(dest)-1)\n\t\tlen = sizeof(dest)-1;\n\n\tQ_strncpyz(dest, s, len + 1);\n\n\tRETURN_TSTRING(dest);\n}\n\n\n\n/*\n=================\nPF_str2byte\n\nfloat str2byte (string str)\n=================\n*/\n\nstatic void QCBUILTIN PF_str2byte (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tG_FLOAT(OFS_RETURN) = (float) *PR_GetStringOfs(prinst, OFS_PARM0);\n}\n\n/*\n=================\nPF_str2short\n\nfloat str2short (string str)\n=================\n*/\n\nstatic void QCBUILTIN PF_str2short (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tG_FLOAT(OFS_RETURN) = LittleShort(*(const short*)PR_GetStringOfs(prinst, OFS_PARM0));\n}\n\n/*\n=================\nPF_readcmd\n\nstring readmcmd (string str)\n=================\n*/\n\nstatic void QCBUILTIN PF_readcmd (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char *s;\n\textern char sv_redirected_buf[];\n\textern redirect_t sv_redirected;\n\textern int sv_redirectedlang;\n\tredirect_t old;\n\tint oldl;\n\tint spawncount = svs.spawncount;\n\n\ts = PR_GetStringOfs(prinst, OFS_PARM0);\n\n\tCbuf_Execute();\n\tif (svs.spawncount != spawncount || sv.state < ss_loading)\n\t\tHost_EndGame(\"PF_readcmd: map changed before reading\\n\");\n\tCbuf_AddText (s, RESTRICT_LOCAL);\n\n\told = sv_redirected;\n\toldl = sv_redirectedlang;\n\tif (old != RD_NONE)\n\t\tSV_EndRedirect();\n\n\tSV_BeginRedirect(RD_OBLIVION, TL_FindLanguage(\"\"));\n\tCbuf_Execute();\n\tCon_Printf(\"PF_readcmd: %s\\n\", s);\n\tG_INT(OFS_RETURN) = (int)PR_TempString(prinst, sv_redirected_buf);\n\tSV_EndRedirect();\n\n\tif (svs.spawncount != spawncount || sv.state < ss_loading || prinst != sv.world.progs)\n\t\tHost_EndGame(\"PF_readcmd: map changed during reading\\n\");\n\n\tif (old != RD_NONE)\n\t\tSV_BeginRedirect(old, oldl);\n}\n\n#ifdef MVD_RECORDING\n/*\n=================\nPF_forcedemoframe\n\nvoid PF_forcedemoframe(float now)\nForces demo frame\nif argument 'now' is set, frame is written instantly\n=================\n*/\n\nstatic void QCBUILTIN PF_forcedemoframe (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tdemo.forceFrame = 1;\n//\tif (G_FLOAT(OFS_PARM0) == 1)\n//        SV_SendDemoMessage();\n}\n#endif\n\n/*\n=================\nPF_strcpy\n\nvoid strcpy(string dst, string src)\nFIXME: check for null pointers first?\n=================\n*/\n\nstatic void QCBUILTIN PF_MVDSV_strcpy (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tunsigned int dst = G_INT(OFS_PARM0);\n\tconst char *src = PR_GetStringOfs(prinst, OFS_PARM1);\n\tunsigned int size = strlen(src)+1;\n\n\tif (dst <= 0 || dst+size >= prinst->stringtablesize)\n\t{\n\t\tPR_BIError(prinst, \"PF_strcpy: invalid dest\\n\");\n\t\treturn;\n\t}\n\n\tstrcpy(prinst->stringtable+dst, src);\n}\n\n/*\n=================\nPF_strncpy\n\nvoid strcpy(string dst, string src, float count)\nFIXME: check for null pointers first?\n=================\n*/\n\nstatic void QCBUILTIN PF_MVDSV_strncpy (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tunsigned int dst = G_INT(OFS_PARM0);\n\tconst char *src = PR_GetStringOfs(prinst, OFS_PARM1);\n\tunsigned int size = G_FLOAT(OFS_PARM2);\n\n\tif (dst <= 0 || dst+size >= prinst->stringtablesize)\n\t{\n\t\tPR_BIError(prinst, \"PF_strncpy: invalid dest\\n\");\n\t\treturn;\n\t}\n\n\tstrncpy(prinst->stringtable+dst, src, size);\n}\n\n\n/*\n=================\nPF_strstr\n\nstring strstr(string str, string sub)\n=================\n*/\n\nstatic void QCBUILTIN PF_strstr (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char *str, *sub, *p;\n\n\tstr = PR_GetStringOfs(prinst, OFS_PARM0);\n\tsub = PR_GetStringOfs(prinst, OFS_PARM1);\n\n\tif ((p = strstr(str, sub)) == NULL)\n\t{\n\t\tG_INT(OFS_RETURN) = 0;\n\t\treturn;\n\t}\n\n\tif (p > prinst->stringtable && p-prinst->stringtable < prinst->stringtablesize)\n\t\tG_INT(OFS_RETURN) = p-prinst->stringtable;\n\telse\n\t\tRETURN_TSTRING(p);\n}\n\nstatic char readable2[256] =\n{\n\t'.', '_', '_', '_', '_', '.', '_', '_',\n\t'_', '_', '\\n', '_', '\\n', '>', '.', '.',\n\t'[', ']', '0', '1', '2', '3', '4', '5',\n\t'6', '7', '8', '9', '.', '_', '_', '_',\n\t' ', '!', '\\\"', '#', '$', '%', '&', '\\'',\n\t'(', ')', '*', '+', ',', '-', '.', '/',\n\t'0', '1', '2', '3', '4', '5', '6', '7',\n\t'8', '9', ':', ';', '<', '=', '>', '?',\n\t'@', 'A', 'B', 'C', 'D', 'E', 'F', 'G',\n\t'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',\n\t'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',\n\t'X', 'Y', 'Z', '[', '\\\\', ']', '^', '_',\n\t'`', 'a', 'b', 'c', 'd', 'e', 'f', 'g',\n\t'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',\n\t'p', 'q', 'r', 's', 't', 'u', 'v', 'w',\n\t'x', 'y', 'z', '{', '|', '}', '~', '_',\n\t'_', '_', '_', '_', '_', '.', '_', '_',\n\t'_', '_', '_', '_', '_', '>', '.', '.',\n\t'[', ']', '0', '1', '2', '3', '4', '5',\n\t'6', '7', '8', '9', '.', '_', '_', '_',\n\t' ', '!', '\\\"', '#', '$', '%', '&', '\\'',\n\t'(', ')', '*', '+', ',', '-', '.', '/',\n\t'0', '1', '2', '3', '4', '5', '6', '7',\n\t'8', '9', ':', ';', '<', '=', '>', '?',\n\t'@', 'A', 'B', 'C', 'D', 'E', 'F', 'G',\n\t'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',\n\t'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',\n\t'X', 'Y', 'Z', '[', '\\\\', ']', '^', '_',\n\t'`', 'a', 'b', 'c', 'd', 'e', 'f', 'g',\n\t'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',\n\t'p', 'q', 'r', 's', 't', 'u', 'v', 'w',\n\t'x', 'y', 'z', '{', '|', '}', '~', '_'\n};\n/*\nstatic void PR_CleanText(unsigned char *text)\n{\n\tfor ( ; *text; text++)\n\t\t*text = readable2[*text];\n}*/\n\n/*\n================\nPF_log\n\nvoid log(string name, float console, string text)\n=================\n*/\n\nstatic void QCBUILTIN PF_logtext(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tchar name[MAX_OSPATH];\n\tconst char *otext, *text;\n\tvfsfile_t *file;\n\tchar unitext[8192], *out = unitext;\n\tint err;\n\n\tsnprintf(name, MAX_OSPATH, \"%s.log\", PR_GetStringOfs(prinst, OFS_PARM0));\n\totext = text = PF_VarString(prinst, 2, pr_globals);\n\twhile (*text)\n\t{\n\t\tint cp = unicode_decode(&err, text, &text, false);\n\t\tif ((cp >= 0xe000 && cp < 0xe100) || cp == '\\r')\n\t\t\tcp = readable2[cp&0xff];\t//dequake it\n\t\tout += utf8_encode(out, cp, sizeof(unitext)-1-(out-unitext));\n\t}\n\t*out++ = 0;\n\n\tfile = FS_OpenVFS(name, \"ab\", FS_GAME);\n\tif (file == NULL)\n\t{\n\t\tSys_Printf(\"coldn't open log file %s\\n\", name);\n\t}\n\telse\n\t{\n\t\tVFS_WRITE(file, unitext, out-unitext);\n\t\tVFS_CLOSE (file);\n\t}\n\n\tif (G_FLOAT(OFS_PARM1))\n\t\tCon_Printf(\"%s\", otext);\n}\n#endif\n\n/*\n=================\nPF_redirectcmd\n\nvoid redirectcmd (entity to, string str)\n\nthe mvdsv implementation executes it now. we delay till the end of the frame, to avoid issues with map changes etc.\n=================\n*/\nstatic void PF_Redirectcmdcallback(struct frameendtasks_s *task)\n{\t//called at the end of the frame when there's no qc running\n\thost_client = svs.clients + task->ctxint;\n\tif (host_client->state >= cs_connected)\n\t{\n\t\tSV_BeginRedirect(RD_CLIENT, host_client->language);\n\t\tCmd_ExecuteString(task->data, RESTRICT_INSECURE);\n\t\tSV_EndRedirect();\n\t}\n}\nstatic void QCBUILTIN PF_redirectcmd (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tstruct frameendtasks_s *task, **link;\n\tint entnum = G_EDICTNUM(prinst, OFS_PARM0);\n\tconst char *s = PR_GetStringOfs(prinst, OFS_PARM1);\n\tif (entnum < 1 || entnum > sv.allocated_client_slots)\n\t\tPR_RunError (prinst, \"Parm 0 not a client\");\n\n\ttask = Z_Malloc(sizeof(*task)+strlen(s));\n\ttask->callback = PF_Redirectcmdcallback;\n\tstrcpy(task->data, s);\n\ttask->ctxint = entnum-1;\n\tfor(link = &svs.frameendtasks; *link; link = &(*link)->next)\n\t\t;\t//add them on the end, so they're execed in order\n\t*link = task;\n}\n\nstatic void QCBUILTIN PF_OpenPortal\t(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint i;\n\tint state\t= G_FLOAT(OFS_PARM1)!=0;\n\tclient_t *client;\n\tedict_t *ent = G_EDICT(prinst, OFS_PARM0);\n\tint portal = G_EDICT(prinst, OFS_PARM0)->xv->style;\t//read the func_areaportal's style field (q2ism).\n\tint area1 = ent->pvsinfo.areanum, area2 = ent->pvsinfo.areanum2;\n\tfor (client = svs.clients, i = 0; i < sv.allocated_client_slots; i++, client++)\n\t{\n\t\tif (client->state >= cs_connected)\n\t\t{\n\t\t\tClientReliableWrite_Begin(client, svc_setportalstate, 4);\n\t\t\tif (portal > 0xff || area1 > 0xff || area2 > 0xff)\n\t\t\t{\n\t\t\t\tClientReliableWrite_Byte(client, 0xe0 | 2 | state);\n\t\t\t\tClientReliableWrite_Short(client, portal);\n\t\t\t\tClientReliableWrite_Short(client, area1);\n\t\t\t\tClientReliableWrite_Short(client, area2);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tClientReliableWrite_Byte(client, 0xe0 | 0 | state);\n\t\t\t\tClientReliableWrite_Byte(client, portal);\n\t\t\t\tClientReliableWrite_Byte(client, area1);\n\t\t\t\tClientReliableWrite_Byte(client, area2);\n\t\t\t}\n\t\t}\n\t}\n\tif (sv.world.worldmodel->funcs.SetAreaPortalState)\n\t\tsv.world.worldmodel->funcs.SetAreaPortalState(sv.world.worldmodel, portal, area1, area2, state);\n}\n\n//EXTENSION: KRIMZON_SV_PARSECLIENTCOMMAND\n\n//void(entity e, string s) clientcommand = #440\n//executes a command string as if it came from the specified client\nstatic void QCBUILTIN PF_clientcommand (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tclient_t *oldhostclient = host_client;\n\tedict_t *oldsvplayer = sv_player;\n\tunsigned int i;\n\n\t//find client for this entity\n\ti = NUM_FOR_EDICT(prinst, G_EDICT(prinst, OFS_PARM0));\n\tif (i <= 0 || i > sv.allocated_client_slots)\n\t\tPR_BIError(prinst, \"PF_clientcommand: entity is not a client\");\n\telse\n\t{\n\t\thost_client = &svs.clients[i-1];\n\t\tsv_player = host_client->edict;\n\t\tif (host_client->state == cs_connected || host_client->state == cs_spawned)\n\t\t{\n\t\t\tSV_ExecuteUserCommand (PF_VarString(prinst, 1, pr_globals), true);\n\t\t}\n\t\telse\n\t\t\tCon_Printf(\"PF_clientcommand: client is not active\\n\");\n\t}\n\n\thost_client = oldhostclient;\n\tsv_player = oldsvplayer;\n}\n\n\n\n\n\n\nconst char *SV_CheckRejectConnection(netadr_t *adr, const char *uinfo, unsigned int protocol, unsigned int pext1, unsigned int pext2, unsigned int ezpext1, char *guid)\n{\n\tchar addrstr[256];\n\tchar clfeatures[4096], *bp;\n\tconst char *ret = NULL;\n\tif (gfuncs.CheckRejectConnection)\n\t{\n\t\tglobalvars_t *pr_globals = PR_globals(svprogfuncs, PR_CURRENT);\n\n\t\tNET_AdrToString(addrstr, sizeof(addrstr), adr);\n\n\t\t*clfeatures = 0;\n\t\tsafeswitch(protocol)\n\t\t{\n\t\tcase SCP_QUAKEWORLD:\tbp = \"qw\";\t\t\tbreak;\n\t\tcase SCP_QUAKE2:\t\tbp = \"q2\";\t\t\tbreak;\n\t\tcase SCP_QUAKE2EX:\t\tbp = \"q2ex\";\t\tbreak;\n\t\tcase SCP_QUAKE3:\t\tbp = \"q3\";\t\t\tbreak;\n\t\tcase SCP_NETQUAKE:\t\tbp = \"nq\";\t\t\tbreak;\n\t\tcase SCP_BJP3:\t\t\tbp = \"bjp3\";\t\tbreak;\n\t\tcase SCP_FITZ666:\t\tbp = \"fitz666\";\t\tbreak;\n\t\tcase SCP_DARKPLACES6:\tbp = \"dpp6\";\t\tbreak;\n\t\tcase SCP_DARKPLACES7:\tbp = \"dpp7\";\t\tbreak;\n\t\tsafedefault:\t\t\tbp = \"unknown\";\t\tbreak;\n\t\t}\n\t\tInfo_SetValueForKey(clfeatures, \"basicprotocol\", bp, sizeof(clfeatures));\n\t\tInfo_SetValueForKey(clfeatures, \"guid\", guid, sizeof(clfeatures));\n\n\t\t//this is not the limits of the client itself, but the limits that the server is able and willing to send to them.\n\n\t\tif ((pext1 & PEXT_SOUNDDBL) || (pext2 & PEXT2_REPLACEMENTDELTAS) || (protocol == SCP_BJP3 || protocol == SCP_FITZ666 || protocol == SCP_DARKPLACES6) || (protocol == SCP_DARKPLACES7))\n\t\t\tInfo_SetValueForKey(clfeatures, \"maxsounds\", va(\"%i\", MAX_PRECACHE_SOUNDS), sizeof(clfeatures));\n\t\telse\n\t\t\tInfo_SetValueForKey(clfeatures, \"maxsounds\", \"256\", sizeof(clfeatures));\n\n\t\tif ((pext1 & PEXT_MODELDBL) || (protocol == SCP_BJP3 || protocol == SCP_FITZ666 || protocol == SCP_DARKPLACES6) || (protocol == SCP_DARKPLACES7))\n\t\t\tInfo_SetValueForKey(clfeatures, \"maxmodels\", va(\"%i\", MAX_PRECACHE_MODELS), sizeof(clfeatures));\n\t\telse\n\t\t\tInfo_SetValueForKey(clfeatures, \"maxmodels\", \"256\", sizeof(clfeatures));\n\n\t\tif (pext2 & PEXT2_REPLACEMENTDELTAS)\n\t\t\tInfo_SetValueForKey(clfeatures, \"maxentities\", va(\"%i\", MAX_EDICTS), sizeof(clfeatures));\n\t\telse if (protocol == SCP_BJP3 || protocol == SCP_FITZ666)\n//\t\t\tInfo_SetValueForKey(clfeatures, \"maxentities\", \"65535\", sizeof(clfeatures));\n\t\t\tInfo_SetValueForKey(clfeatures, \"maxentities\", \"32767\", sizeof(clfeatures));\n\t\telse if (protocol == SCP_DARKPLACES6 || protocol == SCP_DARKPLACES7)\n\t\t\tInfo_SetValueForKey(clfeatures, \"maxentities\", \"32767\", sizeof(clfeatures));\n\t\telse if (pext1 & PEXT_ENTITYDBL2)\n\t\t\tInfo_SetValueForKey(clfeatures, \"maxentities\", \"2048\", sizeof(clfeatures));\n\t\telse if (pext1 & PEXT_ENTITYDBL)\n\t\t\tInfo_SetValueForKey(clfeatures, \"maxentities\", \"1024\", sizeof(clfeatures));\n\t\telse if (protocol == SCP_NETQUAKE)\n\t\t\tInfo_SetValueForKey(clfeatures, \"maxentities\", \"600\", sizeof(clfeatures));\n\t\telse //if (protocol == SCP_QUAKEWORLD)\n\t\t\tInfo_SetValueForKey(clfeatures, \"maxentities\", \"512\", sizeof(clfeatures));\n\n\t\tif (pext2 & PEXT2_REPLACEMENTDELTAS)\t\t\t//limited by packetlog/size, but server can track the whole lot, assuming they're not all sent in a single packet.\n\t\t\tInfo_SetValueForKey(clfeatures, \"maxvisentities\", va(\"%i\", MAX_EDICTS), sizeof(clfeatures));\n\t\telse if (protocol == SCP_DARKPLACES6 || protocol == SCP_DARKPLACES7)\t//deltaing protocol allows all ents to be visible at once\n\t\t\tInfo_SetValueForKey(clfeatures, \"maxvisentities\", \"32767\", sizeof(clfeatures));\n\t\telse if (pext1 & PEXT_256PACKETENTITIES)\n\t\t\tInfo_SetValueForKey(clfeatures, \"maxvisentities\", \"256\", sizeof(clfeatures));\n\t\telse if (protocol == SCP_QUAKEWORLD)\n\t\t\tInfo_SetValueForKey(clfeatures, \"maxvisentities\", \"64\", sizeof(clfeatures));\n\t\t//others are limited by packet sizes, so the count can vary...\n\n\t\t//features\n#ifdef PEXT_VIEW2\n\t\tif (pext1 & PEXT_VIEW2)\n\t\t\tInfo_SetValueForKey(clfeatures, \"PEXT_VIEW2\", \"1\", sizeof(clfeatures));\n#endif\n\t\tif (pext1 & PEXT_LIGHTSTYLECOL)\n\t\t\tInfo_SetValueForKey(clfeatures, \"PEXT_LIGHTSTYLECOL\", \"1\", sizeof(clfeatures));\n\t\tif ((pext1 & PEXT_CSQC) || (protocol == SCP_DARKPLACES6) || (protocol == SCP_DARKPLACES7))\n\t\t\tInfo_SetValueForKey(clfeatures, \"PEXT_CSQC\", \"1\", sizeof(clfeatures));\n\t\tif ((pext1 & PEXT_FLOATCOORDS) || (protocol == SCP_DARKPLACES6) || (protocol == SCP_DARKPLACES7))\n\t\t\tInfo_SetValueForKey(clfeatures, \"PEXT_FLOATCOORDS\", \"1\", sizeof(clfeatures));\n\t\tif ((pext1 & PEXT_ENTITYDBL) || (pext2 & PEXT2_REPLACEMENTDELTAS) || (protocol == SCP_FITZ666 || protocol == SCP_DARKPLACES6) || (protocol == SCP_DARKPLACES7))\n\t\t\tInfo_SetValueForKey(clfeatures, \"PEXT_ENTITYDBL\", \"1\", sizeof(clfeatures));\n\t\tif (pext1 & PEXT_HEXEN2)\n\t\t\tInfo_SetValueForKey(clfeatures, \"PEXT_HEXEN2\", \"1\", sizeof(clfeatures));\n\t\tif ((pext1 & PEXT_SETATTACHMENT) || (protocol == SCP_DARKPLACES6) || (protocol == SCP_DARKPLACES7))\n\t\t\tInfo_SetValueForKey(clfeatures, \"PEXT_SETATTACHMENT\", \"1\", sizeof(clfeatures));\n\t\tif (pext1 & PEXT_CUSTOMTEMPEFFECTS)\n\t\t\tInfo_SetValueForKey(clfeatures, \"PEXT_CUSTOMTEMPEFFECTS\", \"1\", sizeof(clfeatures));\n\t\tif ((pext2 & PEXT2_PRYDONCURSOR) || (protocol == SCP_DARKPLACES6) || (protocol == SCP_DARKPLACES7))\n\t\t\tInfo_SetValueForKey(clfeatures, \"PEXT2_PRYDONCURSOR\", \"1\", sizeof(clfeatures));\n\t\tif (pext2 & PEXT2_VOICECHAT)\n\t\t\tInfo_SetValueForKey(clfeatures, \"PEXT2_VOICECHAT\", \"1\", sizeof(clfeatures));\n\t\tif (pext2 & PEXT2_REPLACEMENTDELTAS)\n\t\t\tInfo_SetValueForKey(clfeatures, \"PEXT2_REPLACEMENTDELTAS\", \"1\", sizeof(clfeatures));\n\t\tif (pext2 & PEXT2_MAXPLAYERS)\n\t\t\tInfo_SetValueForKey(clfeatures, \"PEXT2_MAXPLAYERS\", \"1\", sizeof(clfeatures));\n\t\tif (pext2 & PEXT2_PREDINFO)\n\t\t\tInfo_SetValueForKey(clfeatures, \"PEXT2_PREDINFO\", \"1\", sizeof(clfeatures));\n\t\tif (pext2 & PEXT2_NEWSIZEENCODING)\n\t\t\tInfo_SetValueForKey(clfeatures, \"PEXT2_NEWSIZEENCODING\", \"1\", sizeof(clfeatures));\n\t\tif (pext2 & PEXT2_INFOBLOBS)\n\t\t\tInfo_SetValueForKey(clfeatures, \"PEXT2_INFOBLOBS\", \"1\", sizeof(clfeatures));\n\t\tif (pext2 & PEXT2_VRINPUTS)\n\t\t\tInfo_SetValueForKey(clfeatures, \"PEXT2_VRINPUTS\", \"1\", sizeof(clfeatures));\n\n\t\tif (ezpext1 & EZPEXT1_FLOATENTCOORDS)\n\t\t\tInfo_SetValueForKey(clfeatures, \"EZPEXT1_FLOATENTCOORDS\", \"1\", sizeof(clfeatures));\n\t\tif (ezpext1 & EZPEXT1_SETANGLEREASON)\n\t\t\tInfo_SetValueForKey(clfeatures, \"EZPEXT1_SETANGLEREASON\", \"1\", sizeof(clfeatures));\n\n\t\tpr_global_struct->self = EDICT_TO_PROG(svprogfuncs, sv.world.edicts);\n\t\tG_INT(OFS_PARM0) = (int)PR_TempString(svprogfuncs, addrstr);\n\t\tG_INT(OFS_PARM1) = (int)PR_TempString(svprogfuncs, uinfo);\n\t\tG_INT(OFS_PARM2) = (int)PR_TempString(svprogfuncs, clfeatures);\n\t\tPR_ExecuteProgram (svprogfuncs, gfuncs.CheckRejectConnection);\n\t\tret = PR_GetStringOfs(svprogfuncs, OFS_RETURN);\n\t\tif (!*ret)\n\t\t\tret = NULL;\n\t}\n\treturn ret;\n}\n#ifndef SERVERONLY\nvoid SV_AddDebugPolygons(void)\n{\n\tint i;\n\tif (gfuncs.AddDebugPolygons)\n\t{\n#ifdef PROGS_DAT\n\t\textern qboolean csqc_dp_lastwas3d;\n\t\tcsqc_dp_lastwas3d = true;\n#endif\n\t\tpr_global_struct->self = EDICT_TO_PROG(svprogfuncs, sv.world.edicts);\n\t\tfor (i = 0; i < sv.allocated_client_slots; i++)\n\t\t\tif (svs.clients[i].netchan.remote_address.type == NA_LOOPBACK)\n\t\t\t\tpr_global_struct->self = EDICT_TO_PROG(svprogfuncs, svs.clients[i].edict);\n\t\tPR_ExecuteProgram (svprogfuncs, gfuncs.AddDebugPolygons);\n\t\tif (R2D_Flush)\n\t\t\tR2D_Flush();\n#ifdef PROGS_DAT\n\t\tcsqc_dp_lastwas3d = false;\n#endif\n\t}\n}\n#endif\n\n#ifdef HEXEN2\nstatic void QCBUILTIN PF_h2AdvanceFrame(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tedict_t *Ent;\n\tfloat Start,End,Result;\n\n\tEnt = PROG_TO_EDICT(prinst, pr_global_struct->self);\n\tStart = G_FLOAT(OFS_PARM0);\n\tEnd = G_FLOAT(OFS_PARM1);\n\n\tif ((Start<End&&(Ent->v->frame < Start || Ent->v->frame > End))||\n\t\t(Start>End&&(Ent->v->frame > Start || Ent->v->frame < End)))\n\t{ // Didn't start in the range\n\t\tEnt->v->frame = Start;\n\t\tResult = 0;\n\t}\n\telse if(Ent->v->frame == End)\n\t{  // Wrapping\n\t\tEnt->v->frame = Start;\n\t\tResult = 1;\n\t}\n\telse if(End>Start)\n\t{  // Regular Advance\n\t\tEnt->v->frame++;\n\t\tif (Ent->v->frame == End)\n\t\t\tResult = 2;\n\t\telse\n\t\t\tResult = 0;\n\t}\n\telse if(End<Start)\n\t{  // Reverse Advance\n\t\tEnt->v->frame--;\n\t\tif (Ent->v->frame == End)\n\t\t\tResult = 2;\n\t\telse\n\t\t\tResult = 0;\n\t}\n\telse\n\t{\n\t\tEnt->v->frame=End;\n\t\tResult = 1;\n\t}\n\n\tG_FLOAT(OFS_RETURN) = Result;\n}\n\nstatic void QCBUILTIN PF_h2RewindFrame(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tedict_t *Ent;\n\tfloat Start,End,Result;\n\n\tEnt = PROG_TO_EDICT(prinst, pr_global_struct->self);\n\tStart = G_FLOAT(OFS_PARM0);\n\tEnd = G_FLOAT(OFS_PARM1);\n\n\tif (Ent->v->frame > Start || Ent->v->frame < End)\n\t{ // Didn't start in the range\n\t\tEnt->v->frame = Start;\n\t\tResult = 0;\n\t}\n\telse if(Ent->v->frame == End)\n\t{  // Wrapping\n\t\tEnt->v->frame = Start;\n\t\tResult = 1;\n\t}\n\telse\n\t{  // Regular Advance\n\t\tEnt->v->frame--;\n\t\tif (Ent->v->frame == End) Result = 2;\n\t\telse Result = 0;\n\t}\n\n\tG_FLOAT(OFS_RETURN) = Result;\n}\n\n#define WF_NORMAL_ADVANCE 0\n#define WF_CYCLE_STARTED 1\n#define WF_CYCLE_WRAPPED 2\n#define WF_LAST_FRAME 3\n\nstatic void QCBUILTIN PF_h2advanceweaponframe (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tedict_t *ent;\n\tfloat startframe,endframe;\n\tfloat state;\n\n\tent = PROG_TO_EDICT(prinst, pr_global_struct->self);\n\tstartframe = G_FLOAT(OFS_PARM0);\n\tendframe = G_FLOAT(OFS_PARM1);\n\n\tif ((endframe > startframe && (ent->v->weaponframe > endframe || ent->v->weaponframe < startframe)) ||\n\t(endframe < startframe && (ent->v->weaponframe < endframe || ent->v->weaponframe > startframe)) )\n\t{\n\t\tent->v->weaponframe=startframe;\n\t\tstate = WF_CYCLE_STARTED;\n\t}\n\telse if(ent->v->weaponframe==endframe)\n\t{\n\t\tent->v->weaponframe=startframe;\n\t\tstate = WF_CYCLE_WRAPPED;\n\t}\n\telse\n\t{\n\t\tif (startframe > endframe)\n\t\t\tent->v->weaponframe = ent->v->weaponframe - 1;\n\t\telse if (startframe < endframe)\n\t\t\tent->v->weaponframe = ent->v->weaponframe + 1;\n\n\t\tif (ent->v->weaponframe==endframe)\n\t\t\tstate = WF_LAST_FRAME;\n\t\telse\n\t\t\tstate = WF_NORMAL_ADVANCE;\n\t}\n\n\tG_FLOAT(OFS_RETURN) = state;\n}\n\nvoid PRH2_SetPlayerClass(client_t *cl, int classnum, qboolean fromqc)\n{\n\tchar\t\ttemp[16];\n\tif (classnum < 1)\n\t\treturn; //reject it (it would crash the (standard hexen2) mod)\n\tif (classnum > 5)\n\t\treturn;\n\twhile (classnum>1 && !COM_FCheckExists(va(\"gfx/menu/netp%i.lmp\", classnum)))\n\t\tclassnum--;\n\n\tif (!fromqc)\n\t{\n\t\tif (progstype != PROG_H2)\n\t\t\treturn;\n\n\t\t/*if they already have a class, only switch to the class already set by the gamecode\n\t\tthis is awkward, but matches hexen2*/\n\t\tif (cl->playerclass)\n\t\t{\n\t\t\tif (cl->edict->xv->playerclass)\n\t\t\t\tclassnum = cl->edict->xv->playerclass;\n\t\t\telse if (cl->playerclass)\n\t\t\t\tclassnum = cl->playerclass;\n\t\t}\n\t}\n\n\tif (classnum)\n\t\tsprintf(temp,\"%i\",(int)classnum);\n\telse\n\t\t*temp = 0;\n\tInfoBuf_SetKey (&cl->userinfo, \"cl_playerclass\", temp);\n\tif (cl->playerclass != classnum)\n\t{\n\t\tcl->edict->xv->playerclass = classnum;\n\t\tcl->playerclass = classnum;\n\n\t\tif (!fromqc)\n\t\t{\n\t\t\tcl->sendinfo = true;\n\t\t\tif (cl->state == cs_spawned && gfuncs.ClassChangeWeapon)\n\t\t\t{\n\t\t\t\tpr_global_struct->self = EDICT_TO_PROG(svprogfuncs, cl->edict);\n\t\t\t\tPR_ExecuteProgram (svprogfuncs, gfuncs.ClassChangeWeapon);\n\t\t\t}\n\t\t}\n\t}\n}\n\nstatic void QCBUILTIN PF_h2setclass (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tfloat\t\tNewClass;\n\tint\t\t\tentnum;\n\tedict_t\t\t*e;\n\tclient_t\t*client;\n//\tclient_t\t*old;\n\tchar\t\ttemp[1024];\n\n\tentnum = G_EDICTNUM(prinst, OFS_PARM0);\n\te = G_EDICT(prinst, OFS_PARM0);\n\tNewClass = G_FLOAT(OFS_PARM1);\n\n\tif (entnum < 1 || entnum > sv.allocated_client_slots)\n\t{\n\t\tCon_Printf (\"tried to change class of a non-client\\n\");\n\t\treturn;\n\t}\n\n\tclient = &svs.clients[entnum-1];\n\n\te->xv->playerclass = NewClass;\n\tclient->playerclass = NewClass;\n\n\tsprintf(temp,\"%d\",(int)NewClass);\n\tInfoBuf_SetValueForKey (&client->userinfo, \"cl_playerclass\", temp);\n\tclient->sendinfo = true;\n}\n\nstatic void QCBUILTIN PF_h2v_factor(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n// returns (v_right * factor_x) + (v_forward * factor_y) + (v_up * factor_z)\n{\n\tfloat *range;\n\tvec3_t result;\n\n\trange = G_VECTOR(OFS_PARM0);\n\n\tresult[0] = (P_VEC(v_right)[0] * range[0]) +\n\t\t\t\t(P_VEC(v_forward)[0] * range[1]) +\n\t\t\t\t(P_VEC(v_up)[0] * range[2]);\n\n\tresult[1] = (P_VEC(v_right)[1] * range[0]) +\n\t\t\t\t(P_VEC(v_forward)[1] * range[1]) +\n\t\t\t\t(P_VEC(v_up)[1] * range[2]);\n\n\tresult[2] = (P_VEC(v_right)[2] * range[0]) +\n\t\t\t\t(P_VEC(v_forward)[2] * range[1]) +\n\t\t\t\t(P_VEC(v_up)[2] * range[2]);\n\n\tVectorCopy (result, G_VECTOR(OFS_RETURN));\n}\n\nstatic void QCBUILTIN PF_h2v_factorrange(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n// returns (v_right * factor_x) + (v_forward * factor_y) + (v_up * factor_z)\n{\n\tfloat num;\n\tvec3_t result,r2;\n\n\tVM_VECTORARG(minv, OFS_PARM0);\n\tVM_VECTORARG(maxv, OFS_PARM1);\n\n\tnum = (rand ()&0x7fff) / ((float)0x7fff);\n\tresult[0] = ((maxv[0]-minv[0]) * num) + minv[0];\n\tnum = (rand ()&0x7fff) / ((float)0x7fff);\n\tresult[1] = ((maxv[1]-minv[1]) * num) + minv[1];\n\tnum = (rand ()&0x7fff) / ((float)0x7fff);\n\tresult[2] = ((maxv[2]-minv[2]) * num) + minv[2];\n\n\tr2[0] = (P_VEC(v_right)[0] * result[0]) +\n\t\t\t(P_VEC(v_forward)[0] * result[1]) +\n\t\t\t(P_VEC(v_up)[0] * result[2]);\n\n\tr2[1] = (P_VEC(v_right)[1] * result[0]) +\n\t\t\t(P_VEC(v_forward)[1] * result[1]) +\n\t\t\t(P_VEC(v_up)[1] * result[2]);\n\n\tr2[2] = (P_VEC(v_right)[2] * result[0]) +\n\t\t\t(P_VEC(v_forward)[2] * result[1]) +\n\t\t\t(P_VEC(v_up)[2] * result[2]);\n\n\tVectorCopy (r2, G_VECTOR(OFS_RETURN));\n}\n\nchar *T_GetString(int num);\nstatic void QCBUILTIN PF_h2plaque_draw(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tchar\t\t*s;\n\n#ifdef SERVER_DEMO_PLAYBACK\n\tif (sv.demofile)\n\t\treturn;\n#endif\n\n\tif (G_FLOAT(OFS_PARM1) == 0)\n\t\ts = \"\";\n\telse\n\t{\n\t\ts = T_GetString(G_FLOAT(OFS_PARM1)-1);\n\t}\n\n\tif (G_FLOAT(OFS_PARM0) == MSG_ONE)\n\t{\n\t\tedict_t *ent;\n\t\tent = PROG_TO_EDICT(svprogfuncs, pr_global_struct->msg_entity);\n\t\tPF_centerprint_Internal(NUM_FOR_EDICT(svprogfuncs, ent), true, s);\n\t}\n\telse\n\t{\n\t\tMSG_WriteByte (QWWriteDest(G_FLOAT(OFS_PARM0)), svc_centerprint);\n\t\tif (*s)\n\t\t{\n\t\t\tMSG_WriteByte (QWWriteDest(G_FLOAT(OFS_PARM0)), '/');\n\t\t\tMSG_WriteByte (QWWriteDest(G_FLOAT(OFS_PARM0)), 'P');\n\t\t}\n\t\tMSG_WriteString (QWWriteDest(G_FLOAT(OFS_PARM0)), s);\n#ifdef NQPROT\n\t\tMSG_WriteByte (NQWriteDest(G_FLOAT(OFS_PARM0)), svc_centerprint);\n\t\tif (*s)\n\t\t{\n\t\t\tMSG_WriteByte (NQWriteDest(G_FLOAT(OFS_PARM0)), '/');\n\t\t\tMSG_WriteByte (NQWriteDest(G_FLOAT(OFS_PARM0)), 'P');\n\t\t}\n\t\tMSG_WriteString (NQWriteDest(G_FLOAT(OFS_PARM0)), s);\n#endif\n\t}\n}\n\nstatic void QCBUILTIN PF_h2movestep (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tvec3_t v;\n\tedict_t\t*ent;\n\tint \toldself;\n\tqboolean set_trace;\n\n\tent = PROG_TO_EDICT(prinst, pr_global_struct->self);\n\n\tv[0] = G_FLOAT(OFS_PARM0);\n\tv[1] = G_FLOAT(OFS_PARM1);\n\tv[2] = G_FLOAT(OFS_PARM2);\n\tset_trace = G_FLOAT(OFS_PARM3);\n\n// save program state, because SV_movestep may call other progs\n\toldself = pr_global_struct->self;\n\n\tG_INT(OFS_RETURN) = World_movestep (&sv.world, (wedict_t*)ent, v, NULL, false, true, set_trace?set_trace_globals:NULL);\n\n// restore program state\n\tpr_global_struct->self = oldself;\n}\n\nstatic void QCBUILTIN PF_h2concatv(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tfloat *in,*range;\n\tvec3_t result;\n\n\tin = G_VECTOR(OFS_PARM0);\n\trange = G_VECTOR(OFS_PARM1);\n\n\tVectorCopy (in, result);\n\tif (result[0] < -range[0]) result[0] = -range[0];\n\tif (result[0] > range[0]) result[0] = range[0];\n\tif (result[1] < -range[1]) result[1] = -range[1];\n\tif (result[1] > range[1]) result[1] = range[1];\n\tif (result[2] < -range[2]) result[2] = -range[2];\n\tif (result[2] > range[2]) result[2] = range[2];\n\n\tVectorCopy (result, G_VECTOR(OFS_RETURN));\n}\n\nstatic void QCBUILTIN PF_h2matchAngleToSlope(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tedict_t\t*actor;\n\tvec3_t v_forward, old_forward, old_right, new_angles2 = { 0, 0, 0 };\n\tfloat pitch, mod, dot;\n\n\n\t// OFS_PARM0 is used by PF_vectoangles below\n\tactor = G_EDICT(prinst, OFS_PARM1);\n\n\tAngleVectors(actor->v->angles, old_forward, old_right, P_VEC(v_up));\n\n\tVectorAngles(G_VECTOR(OFS_PARM0), NULL, G_VECTOR(OFS_RETURN), true/*FIXME*/);\n\n\tpitch = G_FLOAT(OFS_RETURN) - 90;\n\n\tnew_angles2[1] = G_FLOAT(OFS_RETURN+1);\n\n\tAngleVectors(new_angles2, v_forward, P_VEC(v_right), P_VEC(v_up));\n\n\tmod = DotProduct(v_forward, old_right);\n\n\tif(mod<0)\n\t\tmod=1;\n\telse\n\t\tmod=-1;\n\n\tdot = DotProduct(v_forward, old_forward);\n\n\tactor->v->angles[0] = dot*pitch;\n\tactor->v->angles[2] = (1-fabs(dot))*pitch*mod;\n}\n/*objective type stuff, this goes into a stat*/\nstatic void QCBUILTIN PF_h2updateinfoplaque(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tunsigned int idx = G_FLOAT(OFS_PARM0);\n\tint mode = G_FLOAT(OFS_PARM1);\t/*0=toggle, 1=force, 2=clear*/\n\n\tif (idx >= sizeof(h2infoplaque)*8)\n\t\treturn;\n\n\tif ((mode & 3) == 3)\n\t\t/*idiot*/;\n\telse if (mode & 1)\n\t\th2infoplaque[idx/32] |= 1<<(idx&31);\n\telse if (mode & 2)\n\t\th2infoplaque[idx/32] &=~(1<<(idx&31));\n\telse\n\t\th2infoplaque[idx/32] ^= 1<<(idx&31);\n}\n\nenum\n{\n\tce_none,\n\tce_rain,\n\tce_fountain,\n\tce_quake,\n\tce_white_smoke,\n\tce_bluespark,\n\tce_yellowspark,\n\tce_sm_circle_exp,\n\tce_bg_circle_exp,\n\tce_sm_white_flash,\n\tce_white_flash\t\t= 10,\n\tce_yellowred_flash,\n\tce_blue_flash,\n\tce_sm_blue_flash,\n\tce_red_flash,\n\tce_sm_explosion,\n\tce_lg_explosion,\n\tce_floor_explosion,\n\tce_rider_death,\n\tce_blue_explosion,\n\tce_green_smoke\t\t= 20,\n\tce_grey_smoke,\n\tce_red_smoke,\n\tce_slow_white_smoke,\n\tce_redspark,\n\tce_greenspark,\n\tce_telesmk1,\n\tce_telesmk2,\n\tce_icehit,\n\tce_medusa_hit,\n\tce_mezzo_reflect\t= 30,\n\tce_floor_explosion2,\n\tce_xbow_explosion,\n\tce_new_explosion,\n\tce_magic_missile_explosion,\n\tce_ghost,\n\tce_bone_explosion,\n\tce_redcloud,\n\tce_teleporterpuffs,\n\tce_teleporterbody,\n\tce_boneshard\t\t= 40,\n\tce_boneshrapnel,\n\tce_flamestream,\n\tce_snow,\n\tce_gravitywell,\n\tce_bldrn_expl,\n\tce_acid_muzzfl,\n\tce_acid_hit,\n\tce_firewall_small,\n\tce_firewall_medium,\n\tce_firewall_large\t= 50,\n\tce_lball_expl,\n\tce_acid_splat,\n\tce_acid_expl,\n\tce_fboom,\n\tce_chunk,\n\tce_bomb,\n\tce_brn_bounce,\n\tce_lshock,\n\tce_flamewall,\n\tce_flamewall2\t\t= 60,\n\tce_floor_explosion3,\n\tce_onfire,\n\n\n\t/*internal effects, here for indexes*/\n\n\tce_teleporterbody_1,\t/*de-sheeped*/\n\tce_white_smoke_05,\n\tce_white_smoke_10,\n\tce_white_smoke_15,\n\tce_white_smoke_20,\n\tce_white_smoke_50,\n\tce_green_smoke_05,\n\tce_green_smoke_10,\n\tce_green_smoke_15,\n\tce_green_smoke_20,\n\tce_grey_smoke_15,\n\tce_grey_smoke_100,\n\n\tce_chunk_1,\n\tce_chunk_2,\n\tce_chunk_3,\n\tce_chunk_4,\n\tce_chunk_5,\n\tce_chunk_6,\n\tce_chunk_7,\n\tce_chunk_8,\n\tce_chunk_9,\n\tce_chunk_10,\n\tce_chunk_11,\n\tce_chunk_12,\n\tce_chunk_13,\n\tce_chunk_14,\n\tce_chunk_15,\n\tce_chunk_16,\n\tce_chunk_17,\n\tce_chunk_18,\n\tce_chunk_19,\n\tce_chunk_20,\n\tce_chunk_21,\n\tce_chunk_22,\n\tce_chunk_23,\n\tce_chunk_24,\n\n\tce_max\n};\nstatic int h2customtents[ce_max];\nvoid SV_RegisterH2CustomTents(void)\n{\n\tint i;\n\tfor (i = 0; i < ce_max; i++)\n\t\th2customtents[i] = -1;\n\n\tif (progstype == PROG_H2)\n\t{\n\t\th2customtents[ce_rain]\t\t\t\t= SV_CustomTEnt_Register(\"h2part.ce_rain\",\t\t\t\tCTE_PERSISTANT|CTE_CUSTOMVELOCITY|CTE_ISBEAM|CTE_CUSTOMCOUNT, NULL, 0, NULL, 0, 0, NULL);\n\t\th2customtents[ce_fountain]\t\t\t= SV_CustomTEnt_Register(\"h2part.ce_fountain\",\t\t\tCTE_PERSISTANT|CTE_CUSTOMVELOCITY|CTE_CUSTOMCOUNT, NULL, 0, NULL, 0, 0, NULL);\n\t\th2customtents[ce_quake]\t\t\t\t= SV_CustomTEnt_Register(\"h2part.ce_quake\",\t\t\t0, NULL, 0, NULL, 0, 0, NULL);\n//\tce_white_smoke (special)\n\t\th2customtents[ce_bluespark]\t\t\t= SV_CustomTEnt_Register(\"h2part.ce_bluespark\",\t\t0, NULL, 0, NULL, 0, 0, NULL);\n\t\th2customtents[ce_yellowspark]\t\t= SV_CustomTEnt_Register(\"h2part.ce_yellowspark\",\t\t0, NULL, 0, NULL, 0, 0, NULL);\n\t\th2customtents[ce_sm_circle_exp]\t\t= SV_CustomTEnt_Register(\"h2part.ce_sm_circle_exp\",\t0, NULL, 0, NULL, 0, 0, NULL);\n\t\th2customtents[ce_bg_circle_exp]\t\t= SV_CustomTEnt_Register(\"h2part.ce_bg_circle_exp\",\t0, NULL, 0, NULL, 0, 0, NULL);\n\t\th2customtents[ce_sm_white_flash]\t= SV_CustomTEnt_Register(\"h2part.ce_sm_white_flash\",\t0, NULL, 0, NULL, 0, 0, NULL);\n\t\th2customtents[ce_white_flash]\t\t= SV_CustomTEnt_Register(\"h2part.ce_white_flash\",\t\t0, NULL, 0, NULL, 0, 0, NULL);\n\t\th2customtents[ce_yellowred_flash]\t= SV_CustomTEnt_Register(\"h2part.ce_yellowred_flash\",\t0, NULL, 0, NULL, 0, 0, NULL);\n\t\th2customtents[ce_blue_flash]\t\t= SV_CustomTEnt_Register(\"h2part.ce_blue_flash\",\t\t0, NULL, 0, NULL, 0, 0, NULL);\n\t\th2customtents[ce_sm_blue_flash]\t\t= SV_CustomTEnt_Register(\"h2part.ce_sm_blue_flash\",\t0, NULL, 0, NULL, 0, 0, NULL);\n\t\th2customtents[ce_red_flash]\t\t\t= SV_CustomTEnt_Register(\"h2part.ce_red_flash\",\t\t0, NULL, 0, NULL, 0, 0, NULL);\n\t\th2customtents[ce_sm_explosion]\t\t= SV_CustomTEnt_Register(\"h2part.ce_sm_explosion\",\t\t0, NULL, 0, NULL, 0, 0, NULL);\n\t\th2customtents[ce_lg_explosion]\t\t= SV_CustomTEnt_Register(\"h2part.ce_lg_explosion\",\t\t0, NULL, 0, NULL, 0, 0, NULL);\n\t\th2customtents[ce_floor_explosion]\t= SV_CustomTEnt_Register(\"h2part.ce_floor_explosion\",\t0, NULL, 0, NULL, 0, 0, NULL);\n\t\th2customtents[ce_rider_death]\t\t= SV_CustomTEnt_Register(\"h2part.ce_rider_death\",\t\t0, NULL, 0, NULL, 0, 0, NULL);\n\t\th2customtents[ce_blue_explosion]\t= SV_CustomTEnt_Register(\"h2part.ce_blue_explosion\",\t0, NULL, 0, NULL, 0, 0, NULL);\n//\tce_green_smoke (special)\n//\tce_grey_smoke (special)\n\t\th2customtents[ce_red_smoke]\t\t\t= SV_CustomTEnt_Register(\"h2part.ce_red_smoke\",\t\t0, NULL, 0, NULL, 0, 0, NULL);\n\t\th2customtents[ce_slow_white_smoke]\t= SV_CustomTEnt_Register(\"h2part.ce_slow_white_smoke\",\t0, NULL, 0, NULL, 0, 0, NULL);\n\t\th2customtents[ce_redspark]\t\t\t= SV_CustomTEnt_Register(\"h2part.ce_redspark\",\t\t\t0, NULL, 0, NULL, 0, 0, NULL);\n\t\th2customtents[ce_greenspark]\t\t= SV_CustomTEnt_Register(\"h2part.ce_greenspark\",\t\t0, NULL, 0, NULL, 0, 0, NULL);\n\t\th2customtents[ce_telesmk1]\t\t\t= SV_CustomTEnt_Register(\"h2part.ce_telesmk1\",\t\t\tCTE_CUSTOMVELOCITY, NULL, 0, NULL, 0, 0, NULL);\n\t\th2customtents[ce_telesmk2]\t\t\t= SV_CustomTEnt_Register(\"h2part.ce_telesmk2\",\t\t\tCTE_CUSTOMVELOCITY, NULL, 0, NULL, 0, 0, NULL);\n\t\th2customtents[ce_icehit]\t\t\t= SV_CustomTEnt_Register(\"h2part.ce_icehit\",\t\t\t0, NULL, 0, NULL, 0, 0, NULL);\n\t\th2customtents[ce_medusa_hit]\t\t= SV_CustomTEnt_Register(\"h2part.ce_medusa_hit\",\t\t0, NULL, 0, NULL, 0, 0, NULL);\n\t\th2customtents[ce_mezzo_reflect]\t\t= SV_CustomTEnt_Register(\"h2part.ce_mezzo_reflect\",\t0, NULL, 0, NULL, 0, 0, NULL);\n\t\th2customtents[ce_floor_explosion2]\t= SV_CustomTEnt_Register(\"h2part.ce_floor_explosion2\",\t0, NULL, 0, NULL, 0, 0, NULL);\n\t\th2customtents[ce_xbow_explosion]\t= SV_CustomTEnt_Register(\"h2part.ce_xbow_explosion\",\t0, NULL, 0, NULL, 0, 0, NULL);\n\t\th2customtents[ce_new_explosion]\t\t= SV_CustomTEnt_Register(\"h2part.ce_new_explosion\",\t0, NULL, 0, NULL, 0, 0, NULL);\n\t\th2customtents[ce_magic_missile_explosion]\t= SV_CustomTEnt_Register(\"h2part.ce_magic_missile_explosion\", 0, NULL, 0, NULL, 0, 0, NULL);\n\t\th2customtents[ce_ghost]\t\t\t\t= SV_CustomTEnt_Register(\"h2part.ce_ghost\",\t\t\tCTE_CUSTOMVELOCITY, NULL, 0, NULL, 0, 0, NULL);\n\t\th2customtents[ce_bone_explosion]\t= SV_CustomTEnt_Register(\"h2part.ce_bone_explosion\",\t0, NULL, 0, NULL, 0, 0, NULL);\n\t\th2customtents[ce_redcloud]\t\t\t= SV_CustomTEnt_Register(\"h2part.ce_redcloud\",\t\t\tCTE_CUSTOMVELOCITY, NULL, 0, NULL, 0, 0, NULL);\n\t\th2customtents[ce_teleporterpuffs]\t= SV_CustomTEnt_Register(\"h2part.ce_teleporterpuffs\",\t0, NULL, 0, NULL, 0, 0, NULL);\n\t\th2customtents[ce_teleporterbody]\t= SV_CustomTEnt_Register(\"h2part.ce_teleporterbody\",\t0, NULL, 0, NULL, 0, 0, NULL);\n\t\th2customtents[ce_boneshard]\t\t\t= SV_CustomTEnt_Register(\"h2part.ce_boneshard\",\t\tCTE_CUSTOMVELOCITY, NULL, 0, NULL, 0, 0, NULL);\n\t\th2customtents[ce_boneshrapnel]\t\t= SV_CustomTEnt_Register(\"h2part.ce_boneshrapnel\",\t\tCTE_CUSTOMVELOCITY, NULL, 0, NULL, 0, 0, NULL);\n\t\th2customtents[ce_flamestream]\t\t= SV_CustomTEnt_Register(\"h2part.ce_flamestream\",\t\tCTE_CUSTOMVELOCITY, NULL, 0, NULL, 0, 0, NULL);\n\t\th2customtents[ce_snow]\t\t\t\t= SV_CustomTEnt_Register(\"h2part.ce_snow\",\t\t\t\tCTE_PERSISTANT|CTE_CUSTOMVELOCITY|CTE_ISBEAM|CTE_CUSTOMCOUNT, NULL, 0, NULL, 0, 0, NULL);\n\t\th2customtents[ce_gravitywell]\t\t= SV_CustomTEnt_Register(\"h2part.ce_gravitywell\",\t\t0, NULL, 0, NULL, 0, 0, NULL);\n\t\th2customtents[ce_bldrn_expl]\t\t= SV_CustomTEnt_Register(\"h2part.ce_bldrn_expl\",\t\t0, NULL, 0, NULL, 0, 0, NULL);\n\t\th2customtents[ce_acid_muzzfl]\t\t= SV_CustomTEnt_Register(\"h2part.ce_acid_muzzfl\",\t\tCTE_CUSTOMVELOCITY, NULL, 0, NULL, 0, 0, NULL);\n\t\th2customtents[ce_acid_hit]\t\t\t= SV_CustomTEnt_Register(\"h2part.ce_acid_hit\",\t\t\t0, NULL, 0, NULL, 0, 0, NULL);\n\t\th2customtents[ce_firewall_small]\t= SV_CustomTEnt_Register(\"h2part.ce_firewall_small\",\t0, NULL, 0, NULL, 0, 0, NULL);\n\t\th2customtents[ce_firewall_medium]\t= SV_CustomTEnt_Register(\"h2part.ce_firewall_medium\",\t0, NULL, 0, NULL, 0, 0, NULL);\n\t\th2customtents[ce_firewall_large]\t= SV_CustomTEnt_Register(\"h2part.ce_firewall_large\",\t0, NULL, 0, NULL, 0, 0, NULL);\n\t\th2customtents[ce_lball_expl]\t\t= SV_CustomTEnt_Register(\"h2part.ce_lball_expl\",\t\t0, NULL, 0, NULL, 0, 0, NULL);\n\t\th2customtents[ce_acid_splat]\t\t= SV_CustomTEnt_Register(\"h2part.ce_acid_splat\",\t\t0, NULL, 0, NULL, 0, 0, NULL);\n\t\th2customtents[ce_acid_expl]\t\t\t= SV_CustomTEnt_Register(\"h2part.ce_acid_expl\",\t\t0, NULL, 0, NULL, 0, 0, NULL);\n\t\th2customtents[ce_fboom]\t\t\t\t= SV_CustomTEnt_Register(\"h2part.ce_fboom\",\t\t\t0, NULL, 0, NULL, 0, 0, NULL);\n//\tce_chunk (special)\n\t\th2customtents[ce_bomb]\t\t\t\t= SV_CustomTEnt_Register(\"h2part.ce_bomb\",\t\t\t\t0, NULL, 0, NULL, 0, 0, NULL);\n\t\th2customtents[ce_brn_bounce]\t\t= SV_CustomTEnt_Register(\"h2part.ce_brn_bounce\",\t\t0, NULL, 0, NULL, 0, 0, NULL);\n\t\th2customtents[ce_lshock]\t\t\t= SV_CustomTEnt_Register(\"h2part.ce_lshock\",\t\t\t0, NULL, 0, NULL, 0, 0, NULL);\n\t\th2customtents[ce_flamewall]\t\t\t= SV_CustomTEnt_Register(\"h2part.ce_flamewall\",\t\tCTE_CUSTOMVELOCITY, NULL, 0, NULL, 0, 0, NULL);\n\t\th2customtents[ce_flamewall2]\t\t= SV_CustomTEnt_Register(\"h2part.ce_flamewall2\",\t\tCTE_CUSTOMVELOCITY, NULL, 0, NULL, 0, 0, NULL);\n\t\th2customtents[ce_floor_explosion3]\t= SV_CustomTEnt_Register(\"h2part.ce_floor_explosion3\",\t0, NULL, 0, NULL, 0, 0, NULL);\n\t\th2customtents[ce_onfire]\t\t\t= SV_CustomTEnt_Register(\"h2part.ce_onfire\",\t\t\tCTE_CUSTOMVELOCITY, NULL, 0, NULL, 0, 0, NULL);\n\n\n\n\t\th2customtents[ce_teleporterbody_1]\t= SV_CustomTEnt_Register(\"h2part.ce_teleporterbody_1\",\tCTE_CUSTOMVELOCITY, NULL, 0, NULL, 0, 0, NULL);\n\t\th2customtents[ce_white_smoke_05]\t= SV_CustomTEnt_Register(\"h2part.ce_white_smoke_05\",\tCTE_CUSTOMVELOCITY, NULL, 0, NULL, 0, 0, NULL);\n\t\th2customtents[ce_white_smoke_10]\t= SV_CustomTEnt_Register(\"h2part.ce_white_smoke_10\",\tCTE_CUSTOMVELOCITY, NULL, 0, NULL, 0, 0, NULL);\n\t\th2customtents[ce_white_smoke_15]\t= SV_CustomTEnt_Register(\"h2part.ce_white_smoke_15\",\tCTE_CUSTOMVELOCITY, NULL, 0, NULL, 0, 0, NULL);\n\t\th2customtents[ce_white_smoke_20]\t= SV_CustomTEnt_Register(\"h2part.ce_white_smoke_20\",\tCTE_CUSTOMVELOCITY, NULL, 0, NULL, 0, 0, NULL);\n\t\th2customtents[ce_white_smoke_50]\t= SV_CustomTEnt_Register(\"h2part.ce_white_smoke_50\",\tCTE_CUSTOMVELOCITY, NULL, 0, NULL, 0, 0, NULL);\n\t\th2customtents[ce_green_smoke_05]\t= SV_CustomTEnt_Register(\"h2part.ce_green_smoke_05\",\tCTE_CUSTOMVELOCITY, NULL, 0, NULL, 0, 0, NULL);\n\t\th2customtents[ce_green_smoke_10]\t= SV_CustomTEnt_Register(\"h2part.ce_green_smoke_10\",\tCTE_CUSTOMVELOCITY, NULL, 0, NULL, 0, 0, NULL);\n\t\th2customtents[ce_green_smoke_15]\t= SV_CustomTEnt_Register(\"h2part.ce_green_smoke_15\",\tCTE_CUSTOMVELOCITY, NULL, 0, NULL, 0, 0, NULL);\n\t\th2customtents[ce_green_smoke_20]\t= SV_CustomTEnt_Register(\"h2part.ce_green_smoke_20\",\tCTE_CUSTOMVELOCITY, NULL, 0, NULL, 0, 0, NULL);\n\t\th2customtents[ce_grey_smoke_15]\t\t= SV_CustomTEnt_Register(\"h2part.ce_grey_smoke_15\",\tCTE_CUSTOMVELOCITY, NULL, 0, NULL, 0, 0, NULL);\n\t\th2customtents[ce_grey_smoke_100]\t= SV_CustomTEnt_Register(\"h2part.ce_grey_smoke_100\",\tCTE_CUSTOMVELOCITY, NULL, 0, NULL, 0, 0, NULL);\n\n\t\th2customtents[ce_chunk_1]\t= SV_CustomTEnt_Register(\"h2part.ce_chunk_greystone\",\t\tCTE_CUSTOMVELOCITY|CTE_CUSTOMCOUNT, NULL, 0, NULL, 0, 0, NULL);\n\t\th2customtents[ce_chunk_2]\t= SV_CustomTEnt_Register(\"h2part.ce_chunk_wood\",\t\t\tCTE_CUSTOMVELOCITY|CTE_CUSTOMCOUNT, NULL, 0, NULL, 0, 0, NULL);\n\t\th2customtents[ce_chunk_3]\t= SV_CustomTEnt_Register(\"h2part.ce_chunk_metal\",\t\t\tCTE_CUSTOMVELOCITY|CTE_CUSTOMCOUNT, NULL, 0, NULL, 0, 0, NULL);\n\t\th2customtents[ce_chunk_4]\t= SV_CustomTEnt_Register(\"h2part.ce_chunk_flesh\",\t\t\tCTE_CUSTOMVELOCITY|CTE_CUSTOMCOUNT, NULL, 0, NULL, 0, 0, NULL);\n\t\th2customtents[ce_chunk_5]\t= SV_CustomTEnt_Register(\"h2part.ce_chunk_fire\",\t\t\tCTE_CUSTOMVELOCITY|CTE_CUSTOMCOUNT, NULL, 0, NULL, 0, 0, NULL);\n\t\th2customtents[ce_chunk_6]\t= SV_CustomTEnt_Register(\"h2part.ce_chunk_clay\",\t\t\tCTE_CUSTOMVELOCITY|CTE_CUSTOMCOUNT, NULL, 0, NULL, 0, 0, NULL);\n\t\th2customtents[ce_chunk_7]\t= SV_CustomTEnt_Register(\"h2part.ce_chunk_leaves\",\t\t\tCTE_CUSTOMVELOCITY|CTE_CUSTOMCOUNT, NULL, 0, NULL, 0, 0, NULL);\n\t\th2customtents[ce_chunk_8]\t= SV_CustomTEnt_Register(\"h2part.ce_chunk_hay\",\t\t\tCTE_CUSTOMVELOCITY|CTE_CUSTOMCOUNT, NULL, 0, NULL, 0, 0, NULL);\n\t\th2customtents[ce_chunk_9]\t= SV_CustomTEnt_Register(\"h2part.ce_chunk_brownstone\",\t\tCTE_CUSTOMVELOCITY|CTE_CUSTOMCOUNT, NULL, 0, NULL, 0, 0, NULL);\n\t\th2customtents[ce_chunk_10]\t= SV_CustomTEnt_Register(\"h2part.ce_chunk_cloth\",\t\t\tCTE_CUSTOMVELOCITY|CTE_CUSTOMCOUNT, NULL, 0, NULL, 0, 0, NULL);\n\t\th2customtents[ce_chunk_11]\t= SV_CustomTEnt_Register(\"h2part.ce_chunk_wood_leaf\",\t\tCTE_CUSTOMVELOCITY|CTE_CUSTOMCOUNT, NULL, 0, NULL, 0, 0, NULL);\n\t\th2customtents[ce_chunk_12]\t= SV_CustomTEnt_Register(\"h2part.ce_chunk_wood_metal\",\t\tCTE_CUSTOMVELOCITY|CTE_CUSTOMCOUNT, NULL, 0, NULL, 0, 0, NULL);\n\t\th2customtents[ce_chunk_13]\t= SV_CustomTEnt_Register(\"h2part.ce_chunk_wood_stone\",\t\tCTE_CUSTOMVELOCITY|CTE_CUSTOMCOUNT, NULL, 0, NULL, 0, 0, NULL);\n\t\th2customtents[ce_chunk_14]\t= SV_CustomTEnt_Register(\"h2part.ce_chunk_metal_stone\",\tCTE_CUSTOMVELOCITY|CTE_CUSTOMCOUNT, NULL, 0, NULL, 0, 0, NULL);\n\t\th2customtents[ce_chunk_15]\t= SV_CustomTEnt_Register(\"h2part.ce_chunk_metal_cloth\",\tCTE_CUSTOMVELOCITY|CTE_CUSTOMCOUNT, NULL, 0, NULL, 0, 0, NULL);\n\t\th2customtents[ce_chunk_16]\t= SV_CustomTEnt_Register(\"h2part.ce_chunk_webs\",\t\t\tCTE_CUSTOMVELOCITY|CTE_CUSTOMCOUNT, NULL, 0, NULL, 0, 0, NULL);\n\t\th2customtents[ce_chunk_17]\t= SV_CustomTEnt_Register(\"h2part.ce_chunk_glass\",\t\t\tCTE_CUSTOMVELOCITY|CTE_CUSTOMCOUNT, NULL, 0, NULL, 0, 0, NULL);\n\t\th2customtents[ce_chunk_18]\t= SV_CustomTEnt_Register(\"h2part.ce_chunk_ice\",\t\t\tCTE_CUSTOMVELOCITY|CTE_CUSTOMCOUNT, NULL, 0, NULL, 0, 0, NULL);\n\t\th2customtents[ce_chunk_19]\t= SV_CustomTEnt_Register(\"h2part.ce_chunk_clearglass\",\t\tCTE_CUSTOMVELOCITY|CTE_CUSTOMCOUNT, NULL, 0, NULL, 0, 0, NULL);\n\t\th2customtents[ce_chunk_20]\t= SV_CustomTEnt_Register(\"h2part.ce_chunk_redglass\",\t\tCTE_CUSTOMVELOCITY|CTE_CUSTOMCOUNT, NULL, 0, NULL, 0, 0, NULL);\n\t\th2customtents[ce_chunk_21]\t= SV_CustomTEnt_Register(\"h2part.ce_chunk_acid\",\t\t\tCTE_CUSTOMVELOCITY|CTE_CUSTOMCOUNT, NULL, 0, NULL, 0, 0, NULL);\n\t\th2customtents[ce_chunk_22]\t= SV_CustomTEnt_Register(\"h2part.ce_chunk_meteor\",\t\t\tCTE_CUSTOMVELOCITY|CTE_CUSTOMCOUNT, NULL, 0, NULL, 0, 0, NULL);\n\t\th2customtents[ce_chunk_23]\t= SV_CustomTEnt_Register(\"h2part.ce_chunk_greenflesh\",\t\tCTE_CUSTOMVELOCITY|CTE_CUSTOMCOUNT, NULL, 0, NULL, 0, 0, NULL);\n\t\th2customtents[ce_chunk_24]\t= SV_CustomTEnt_Register(\"h2part.ce_chunk_bone\",\t\t\tCTE_CUSTOMVELOCITY|CTE_CUSTOMCOUNT, NULL, 0, NULL, 0, 0, NULL);\n\t}\n}\nstatic void QCBUILTIN PF_h2starteffect(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tfloat *min, *max;//, *size;\n//\tfloat *angle;\n\tfloat colour;\n//\tfloat wait, radius, frame, framelength, duration;\n//\tint flags, skin;\n\tint type;\n\tfloat *org, *dir;\n\tint count;\n\n\tint efnum = G_FLOAT(OFS_PARM0);\n\tG_FLOAT(OFS_RETURN) = 0;\n\tswitch(efnum)\n\t{\n\tcase ce_rain:\n\t\t/*this effect is meant to be persistant (endeffect is never used)*/\n\t\tmin = G_VECTOR(OFS_PARM1);\n\t\tmax = G_VECTOR(OFS_PARM2);\n\t\t//size = G_VECTOR(OFS_PARM3);\t/*maxs-min, so useless*/\n\t\tdir = G_VECTOR(OFS_PARM4);\t\t/*[125,100] or [0,0]*/\n\t\tcolour = G_FLOAT(OFS_PARM5);\t/*414 default*/\n\t\tcount = G_FLOAT(OFS_PARM6);\t\t/*300 default*/\n\t\tcount *= /*wait =*/ G_FLOAT(OFS_PARM7);\t/*0.1 default*/\n\n\t\t/*FIXME: not spawned - this persistant effect is created by a map object, all attributes are custom.*/\n\n\t\tif (colour == 414)\n\t\t\tefnum = h2customtents[efnum];\n\t\telse\n\t\t\tefnum = SV_CustomTEnt_Register(va(\"h2part.ce_rain_%i\", (int)colour),\t\t\t\tCTE_PERSISTANT|CTE_CUSTOMVELOCITY|CTE_ISBEAM|CTE_CUSTOMCOUNT, NULL, 0, NULL, 0, 0, NULL);\n\n\t\t{\t//align to the top of the rain volume\n\t\t\t//particles should be removed once they reach the bottom\n\t\t\tvec3_t nmin;\n\t\t\tvec3_t ndir;\n\t\t\tnmin[0] = min[0];\n\t\t\tnmin[1] = min[1];\n\t\t\tnmin[2] = max[2];\n\t\t\tVectorSet(ndir, dir[0], dir[1], (min[2]-max[2])/1);\t//divide by expected lifetime.\n\t\t\tSV_CustomTEnt_Spawn(efnum, nmin, max, count, ndir);\n\t\t}\n\t\treturn;\n\tcase ce_snow:\n\t\t/*this effect is meant to be persistant (endeffect is never used)*/\n\t\tmin = G_VECTOR(OFS_PARM1);\n\t\tmax = G_VECTOR(OFS_PARM2);\n\t\t//flags = G_FLOAT(OFS_PARM3);\n\t\tdir = G_VECTOR(OFS_PARM4);\n\t\tcount = G_FLOAT(OFS_PARM5);\n\n\t\t/*FIXME: we ignore any happy/fluffy/mixed snow types*/\n\t\tSV_CustomTEnt_Spawn(h2customtents[efnum], min, max, count, dir);\n//\t\tCon_Printf(\"FTE-H2 FIXME: ce_snow not supported!\\n\");\n\t\treturn;\n\tcase ce_fountain:\n\t\t/*this effect is meant to be persistant (endeffect is never used)*/\n\t\torg = G_VECTOR(OFS_PARM1);\n//\t\tangle = G_VECTOR(OFS_PARM2);\n\t\tdir = G_VECTOR(OFS_PARM3);\n\t\tcolour = G_FLOAT(OFS_PARM4);\n\t\tcount = G_FLOAT(OFS_PARM5);\n\n\t\t/*FIXME: not spawned - this persistant effect is created by a map object, all attributes are custom.*/\n\t\tif (colour == 407)\n\t\t{\n\t\t\tdir[2] *= 2;\n\t\t\tSV_CustomTEnt_Spawn(h2customtents[efnum], org, NULL, count, dir);\n\t\t}\n\t\telse\n\t\t\tCon_Printf(\"FTE-H2 FIXME: ce_fountain not supported!\\n\");\n\t\treturn;\n\tcase ce_quake:\n\t\t/*this effect is meant to be persistant*/\n\t\torg = G_VECTOR(OFS_PARM1);\n\t\t//radius = G_FLOAT(OFS_PARM2);\t/*discard: always 500/3 */\n\n\t\tif (h2customtents[efnum] != -1)\n\t\t{\n\t\t\tG_FLOAT(OFS_RETURN) = SV_CustomTEnt_Spawn(h2customtents[efnum], org, NULL, 1, NULL);\n\t\t\treturn;\n\t\t}\n\t\tbreak;\n\tcase ce_white_smoke:\t\t//(1,2,3,4,10)*0.05\n\tcase ce_green_smoke:\t\t//(1,2,3,4)*0.05\n\tcase ce_grey_smoke:\t\t\t//(3,20)*0.05\n\t\t/*transform them to the correct durations*/\n\t\tif (efnum == ce_white_smoke)\n\t\t{\n\t\t\tint ifl = G_FLOAT(OFS_PARM3) * 100;\n\t\t\tif (ifl == 5)\n\t\t\t\tefnum = ce_white_smoke_05;\n\t\t\telse if (ifl == 10)\n\t\t\t\tefnum = ce_white_smoke_10;\n\t\t\telse if (ifl == 15)\n\t\t\t\tefnum = ce_white_smoke_15;\n\t\t\telse if (ifl == 20)\n\t\t\t\tefnum = ce_white_smoke_20;\n\t\t\telse if (ifl == 50)\n\t\t\t\tefnum = ce_white_smoke_50;\n\t\t}\n\t\telse if (efnum == ce_green_smoke)\n\t\t{\n\t\t\tint ifl = G_FLOAT(OFS_PARM3) * 100;\n\t\t\tif (ifl == 05)\n\t\t\t\tefnum = ce_green_smoke_05;\n\t\t\telse if (ifl == 10)\n\t\t\t\tefnum = ce_green_smoke_10;\n\t\t\telse if (ifl == 15)\n\t\t\t\tefnum = ce_green_smoke_15;\n\t\t\telse if (ifl == 20)\n\t\t\t\tefnum = ce_green_smoke_20;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tint ifl = G_FLOAT(OFS_PARM3) * 100;\n\t\t\tif (ifl == 15)\n\t\t\t\tefnum = ce_grey_smoke_15;\n\t\t\tif (ifl == 100)\n\t\t\t\tefnum = ce_grey_smoke_100;\n\t\t}\n\t\t/*fallthrough*/\n\tcase ce_red_smoke:\t\t\t//0.15\n\tcase ce_slow_white_smoke:\t//0\n\tcase ce_telesmk1:\t\t\t//0.15\n\tcase ce_telesmk2:\t\t\t//not used\n\tcase ce_ghost:\t\t\t\t//0.1\n\tcase ce_redcloud:\t\t\t//0.05\n\t\torg = G_VECTOR(OFS_PARM1);\n\t\tdir = G_VECTOR(OFS_PARM2);\n\t\t//framelength = G_FLOAT(OFS_PARM3);\t/*FIXME: validate for the other effects?*/\n\n\t\tif (h2customtents[efnum] != -1)\n\t\t{\n\t\t\tSV_CustomTEnt_Spawn(h2customtents[efnum], org, NULL, 1, dir);\n\t\t\treturn;\n\t\t}\n\t\tbreak;\n\tcase ce_acid_muzzfl:\n\tcase ce_flamestream:\n\tcase ce_flamewall:\n\tcase ce_flamewall2:\n\tcase ce_onfire:\n\t\torg = G_VECTOR(OFS_PARM1);\n\t\tdir = G_VECTOR(OFS_PARM2);\n\t\t//frame = G_FLOAT(OFS_PARM3);\t\t\t/*discard: h2 uses a fixed value for each effect (0, except for acid_muzzfl which is 0.05)*/\n\n\t\tif (h2customtents[efnum] != -1)\n\t\t{\n\t\t\tSV_CustomTEnt_Spawn(h2customtents[efnum], org, NULL, 1, dir);\n\t\t\treturn;\n\t\t}\n\t\tbreak;\n\tcase ce_sm_white_flash:\n\tcase ce_yellowred_flash:\n\tcase ce_bluespark:\n\tcase ce_yellowspark:\n\tcase ce_sm_circle_exp:\n\tcase ce_bg_circle_exp:\n\tcase ce_sm_explosion:\n\tcase ce_lg_explosion:\n\tcase ce_floor_explosion:\n\tcase ce_floor_explosion3:\n\tcase ce_blue_explosion:\n\tcase ce_redspark:\n\tcase ce_greenspark:\n\tcase ce_icehit:\n\tcase ce_medusa_hit:\n\tcase ce_mezzo_reflect:\n\tcase ce_floor_explosion2:\n\tcase ce_xbow_explosion:\n\tcase ce_new_explosion:\n\tcase ce_magic_missile_explosion:\n\tcase ce_bone_explosion:\n\tcase ce_bldrn_expl:\n\tcase ce_acid_hit:\n\tcase ce_acid_splat:\n\tcase ce_acid_expl:\n\tcase ce_lball_expl:\n\tcase ce_firewall_small:\n\tcase ce_firewall_medium:\n\tcase ce_firewall_large:\n\tcase ce_fboom:\n\tcase ce_bomb:\n\tcase ce_brn_bounce:\n\tcase ce_lshock:\n\tcase ce_white_flash:\n\tcase ce_blue_flash:\n\tcase ce_sm_blue_flash:\n\tcase ce_red_flash:\n\tcase ce_rider_death:\n\tcase ce_teleporterpuffs:\n\t\torg = G_VECTOR(OFS_PARM1);\n\t\tif (h2customtents[efnum] != -1)\n\t\t{\n\t\t\tSV_CustomTEnt_Spawn(h2customtents[efnum], org, NULL, 1, NULL);\n\t\t\treturn;\n\t\t}\n\t\tbreak;\n\tcase ce_gravitywell:\n\t\torg = G_VECTOR(OFS_PARM1);\n\t\t//colour = G_FLOAT(OFS_PARM2);\t\t/*discard: h2 uses a fixed random range*/\n\t\t//duration = G_FLOAT(OFS_PARM3);\t/*discard: h2 uses a fixed time limit*/\n\n\t\tif (h2customtents[efnum] != -1)\n\t\t{\n\t\t\tSV_CustomTEnt_Spawn(h2customtents[efnum], org, NULL, 1, NULL);\n\t\t\treturn;\n\t\t}\n\t\tbreak;\n\tcase ce_teleporterbody:\n\t\torg = G_VECTOR(OFS_PARM1);\n\t\tdir = G_VECTOR(OFS_PARM2);\n\t\tif (G_FLOAT(OFS_PARM3))\t/*alternate*/\n\t\t\tefnum = ce_teleporterbody_1;\n\n\t\tif (h2customtents[efnum] != -1)\n\t\t{\n\t\t\tSV_CustomTEnt_Spawn(h2customtents[efnum], org, NULL, 1, dir);\n\t\t\treturn;\n\t\t}\n\t\tbreak;\n\tcase ce_boneshard:\n\tcase ce_boneshrapnel:\n\t\torg = G_VECTOR(OFS_PARM1);\n\t\tdir = G_VECTOR(OFS_PARM2);\n\t\t//angle = G_VECTOR(OFS_PARM3);\t/*discard: angle is a function of the dir*/\n\t\t//avelocity = G_VECTOR(OFS_PARM4);/*discard: avelocity is a function of the dir*/\n\n\t\t/*FIXME: meant to be persistant until removed*/\n\t\tif (h2customtents[efnum] != -1)\n\t\t{\n\t\t\tG_FLOAT(OFS_RETURN) = SV_CustomTEnt_Spawn(h2customtents[efnum], org, NULL, 1, dir);\n\t\t\treturn;\n\t\t}\n\t\tbreak;\n\tcase ce_chunk:\n\t\torg = G_VECTOR(OFS_PARM1);\n\t\ttype = G_FLOAT(OFS_PARM2);\n\t\tdir = G_VECTOR(OFS_PARM3);\n\t\tcount = G_FLOAT(OFS_PARM4);\n\n\t\t/*convert it to the requested chunk type*/\n\t\tefnum = ce_chunk_1 + type - 1;\n\t\tif (efnum < ce_chunk_1 || efnum > ce_chunk_24)\n\t\t\tefnum = ce_chunk;\n\n\t\tif (h2customtents[efnum] != -1)\n\t\t{\n\t\t\tSV_CustomTEnt_Spawn(h2customtents[efnum], org, NULL, count, dir);\n\t\t\treturn;\n\t\t}\n\n\t\tCon_Printf(\"FTE-H2 FIXME: ce_chunk type=%i not supported!\\n\", type);\n\t\treturn;\n\n\n\t\tbreak;\n\tdefault:\n\t\tPR_BIError (prinst, \"PF_h2starteffect: bad effect\");\n\t\tbreak;\n\t}\n\n\tCon_Printf(\"FTE-H2 FIXME: Effect %i doesn't have an effect registered\\nTell Spike!\\n\", efnum);\n}\n\nstatic void QCBUILTIN PF_h2endeffect(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n//\tint ign = G_FLOAT(OFS_PARM0);\n\tint index = G_FLOAT(OFS_PARM1);\n\n\tCon_DPrintf(\"Stop effect %i\\n\", index);\n}\n\nstatic void QCBUILTIN PF_h2rain_go(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\t//used by (tomed) icemace.hc \n\tfloat *min = G_VECTOR(OFS_PARM0);\n\tfloat *max = G_VECTOR(OFS_PARM1);\n//unused\tfloat *size = G_VECTOR(OFS_PARM2);\n\tfloat *dir = G_VECTOR(OFS_PARM3);\n\tfloat colour = G_FLOAT(OFS_PARM4);\n\tfloat count = G_FLOAT(OFS_PARM5);\n\n\t//this is some hacky alternative.\n\t//fixme: the effect isn't bounded to the box\n\tMSG_WriteByte(&sv.multicast, svc_temp_entity);\n\tMSG_WriteByte(&sv.multicast, TEDP_PARTICLERAIN);\n\t// min\n\tMSG_WriteCoord(&sv.multicast, min[0]);\n\tMSG_WriteCoord(&sv.multicast, min[1]);\n\tMSG_WriteCoord(&sv.multicast, max[2]);\n\t// max\n\tMSG_WriteCoord(&sv.multicast, max[0]);\n\tMSG_WriteCoord(&sv.multicast, max[1]);\n\tMSG_WriteCoord(&sv.multicast, max[2]);\n\t// velocity\n\tMSG_WriteCoord(&sv.multicast, dir[0]);\n\tMSG_WriteCoord(&sv.multicast, dir[1]);\n\tMSG_WriteCoord(&sv.multicast, -(frandom()*700+256));\t//dir not valid. fill in a default downwards direction\n\t// count\n\tMSG_WriteShort(&sv.multicast, min(count, 65535));\n\t// colour\n\tMSG_WriteByte(&sv.multicast, (int)colour&0xff);\n\tSV_MulticastProtExt (NULL, MULTICAST_ALL, pr_global_struct->dimension_send, 0, 0);\n}\n\nstatic void QCBUILTIN PF_h2updatesoundpos(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tCon_DPrintf(\"FTE-H2 FIXME: updatesoundpos not implemented\\n\");\n}\n\nstatic void QCBUILTIN PF_h2whiteflash(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\t/*\n\tbroadcast a stuffcmd, I guess, to flash the screen white\n\tOnly seen this occur once: after killing pravus.\n\t*/\n\tMSG_WriteByte(&sv.multicast, svc_stufftext);\n\tMSG_WriteString(&sv.multicast, \"wf\\n\");\n\tSV_MulticastProtExt (vec3_origin, MULTICAST_ALL_R, pr_global_struct->dimension_send, PEXT_CUSTOMTEMPEFFECTS, 0);\t//now send the new multicast to all that will.\n}\n\nstatic void QCBUILTIN PF_h2getstring(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tchar *s = T_GetString(G_FLOAT(OFS_PARM0)-1);\n\tRETURN_PSTRING(s);\n}\n\nvoid QCBUILTIN PF_sj_strhash (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\t//not quite the same, but oh well\n\tconst char *str = PF_VarString(prinst, 0, pr_globals);\n\tint len = strlen(str);\n\tG_FLOAT(OFS_RETURN) = CalcHashInt(&hash_md4, str, len);\n}\n#endif\nstatic void QCBUILTIN PF_StopSound(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tedict_t\t\t*entity = G_EDICT(prinst, OFS_PARM0);\n\tint\t\t\tchannel = G_FLOAT(OFS_PARM1);\n\n\tSVQ1_StartSound (NULL, (wedict_t*)entity, channel, NULL, 1, 0, 0, 0, CF_SV_RELIABLE);\n}\n\nstatic void QCBUILTIN PF_RegisterTEnt(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint arg;\n\tint i;\n\tint nettype = G_FLOAT(OFS_PARM0);\n\tconst char *effectname = PR_GetStringOfs(prinst, OFS_PARM1);\n\tif (sv.state != ss_loading)\n\t{\n\t\tPR_BIError (prinst, \"PF_RegisterTEnt: Registration can only be done in spawn functions\");\n\t\tG_FLOAT(OFS_RETURN) = -1;\n\t\treturn;\n\t}\n\n\tfor (i = 0; i < 255; i++)\n\t{\n\t\tif (!*sv.customtents[i].particleeffecttype)\n\t\t\tbreak;\n\t\tif (!strcmp(effectname, sv.customtents[i].particleeffecttype))\n\t\t\tbreak;\n\t}\n\tif (i == 255)\n\t{\n\t\tCon_Printf(\"Too many custom effects\\n\");\n\t\tG_FLOAT(OFS_RETURN) = -1;\n\t\treturn;\n\t}\n\n\tQ_strncpyz(sv.customtents[i].particleeffecttype, effectname, sizeof(sv.customtents[i].particleeffecttype));\n\tsv.customtents[i].netstyle = nettype;\n\n\targ = 2;\n\tif (nettype & CTE_STAINS)\n\t{\n\t\tVectorCopy(G_VECTOR(OFS_PARM0+arg*3), sv.customtents[i].stain);\n\t\tsv.customtents[i].radius = G_FLOAT(OFS_PARM1+arg*3);\n\t\targ += 2;\n\t}\n\tif (nettype & CTE_GLOWS)\n\t{\n\t\tsv.customtents[i].dlightrgb[0] = G_FLOAT(OFS_PARM0+arg*3+0)*255;\n\t\tsv.customtents[i].dlightrgb[1] = G_FLOAT(OFS_PARM0+arg*3+1)*255;\n\t\tsv.customtents[i].dlightrgb[2] = G_FLOAT(OFS_PARM0+arg*3+2)*255;\n\t\tsv.customtents[i].dlightradius = G_FLOAT(OFS_PARM1+arg*3)/4;\n\t\tsv.customtents[i].dlighttime = G_FLOAT(OFS_PARM2+arg*3)*16;\n\t\targ += 3;\n\t\tif (nettype & CTE_CHANNELFADE)\n\t\t{\n\t\t\tsv.customtents[i].dlightcfade[0] = G_FLOAT(OFS_PARM0+arg*3+0)*64;\n\t\t\tsv.customtents[i].dlightcfade[1] = G_FLOAT(OFS_PARM0+arg*3+1)*64;\n\t\t\tsv.customtents[i].dlightcfade[2] = G_FLOAT(OFS_PARM0+arg*3+2)*64;\n\t\t\targ++; // we're out now..\n\t\t}\n\t}\n\n\tif (arg != prinst->callargc)\n\t\tCon_Printf(\"Bad argument count\\n\");\n\n\n\tG_FLOAT(OFS_RETURN) = i;\n}\n\nstatic void QCBUILTIN PF_CustomTEnt(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint type;\n\tint arg;\n\tfloat *org;\n\tmulticast_t mcd = MULTICAST_PVS;\n\n\tif (sv.multicast.cursize)\n\t\tSV_MulticastProtExt (vec3_origin, MULTICAST_ALL, pr_global_struct->dimension_send, 0, PEXT_CUSTOMTEMPEFFECTS);\t//do a multicast with the current buffer to all players who won't get the new effect.\n\n\ttype = G_FLOAT(OFS_PARM0);\n\tif (type < 0 || type >= 255)\n\t\treturn;\n\n\tMSG_WriteByte(&sv.multicast, svcfte_customtempent);\n\tMSG_WriteByte(&sv.multicast, type);\n\ttype = sv.customtents[type].netstyle;\n\targ = 1;\n\n\tif (type & CTE_PERSISTANT)\n\t{\n\t\tint id = G_EDICTNUM(prinst, OFS_PARM0+arg*3);\n\t\targ++;\n\t\tmcd = MULTICAST_ALL_R;\n\n\t\tif (arg == prinst->callargc)\n\t\t{\n\t\t\tMSG_WriteShort(&sv.multicast, id | 0x8000);\n\t\t\tSV_MulticastProtExt (vec3_origin, mcd, pr_global_struct->dimension_send, PEXT_CUSTOMTEMPEFFECTS, 0);\t//now send the new multicast to all that will.\n\t\t\treturn;\n\t\t}\n\t\tMSG_WriteShort(&sv.multicast, id);\n\t}\n\n\torg = G_VECTOR(OFS_PARM0+arg*3);\n\tMSG_WriteCoord(&sv.multicast, org[0]);\n\tMSG_WriteCoord(&sv.multicast, org[1]);\n\tMSG_WriteCoord(&sv.multicast, org[2]);\n\n\tif (type & CTE_ISBEAM)\n\t{\n\t\tMSG_WriteCoord(&sv.multicast, G_VECTOR(OFS_PARM0+arg*3)[0]);\n\t\tMSG_WriteCoord(&sv.multicast, G_VECTOR(OFS_PARM0+arg*3)[1]);\n\t\tMSG_WriteCoord(&sv.multicast, G_VECTOR(OFS_PARM0+arg*3)[2]);\n\t\targ++;\n\t}\n\telse\n\t{\n\t\tif (type & CTE_CUSTOMCOUNT)\n\t\t{\n\t\t\tMSG_WriteByte(&sv.multicast, G_FLOAT(OFS_PARM0+arg*3));\n\t\t\targ++;\n\t\t}\n\t\tif (type & CTE_CUSTOMVELOCITY)\n\t\t{\n\t\t\tfloat *vel = G_VECTOR(OFS_PARM0+arg*3);\n\t\t\tMSG_WriteCoord(&sv.multicast, vel[0]);\n\t\t\tMSG_WriteCoord(&sv.multicast, vel[1]);\n\t\t\tMSG_WriteCoord(&sv.multicast, vel[2]);\n\t\t\targ++;\n\t\t}\n\t\telse if (type & CTE_CUSTOMDIRECTION)\n\t\t{\n\t\t\tvec3_t norm;\n\t\t\tVectorNormalize2(G_VECTOR(OFS_PARM0+arg*3), norm);\n\t\t\tMSG_WriteDir(&sv.multicast, norm);\n\t\t\targ++;\n\t\t}\n\t}\n\tif (arg != prinst->callargc)\n\t\tCon_Printf(\"PF_CusromTEnt: bad number of arguments for particle type\\n\");\n\n\tSV_MulticastProtExt (org, mcd, pr_global_struct->dimension_send, PEXT_CUSTOMTEMPEFFECTS, 0);\t//now send the new multicast to all that will.\n}\n\n//void(float effectnum, entity ent, vector start, vector end) trailparticles (EXT_CSQC),\nvoid QCBUILTIN PF_sv_trailparticles(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n#ifdef PEXT_CSQC\n\tint efnum;\n\tint ednum;\n\tfloat *start = G_VECTOR(OFS_PARM2);\n\tfloat *end = G_VECTOR(OFS_PARM3);\n\n\t/*DP gets this wrong*/\n\tif (G_INT(OFS_PARM1) >= MAX_EDICTS)\n\t{\n\t\tednum = G_EDICTNUM(prinst, OFS_PARM0);\n\t\tefnum = G_FLOAT(OFS_PARM1);\n\t}\n\telse\n\t{\n\t\tefnum = G_FLOAT(OFS_PARM0);\n\t\tednum = G_EDICTNUM(prinst, OFS_PARM1);\n\t}\n\n\tif (efnum <= 0)\n\t{\n//\t\tif (!ssqc_deprecated_warned)\n//\t\t{\n//\t\t\tPR_RunWarning(prinst, \"PF_sv_trailparticles: invalid effect\");\n//\t\t\tssqc_deprecated_warned = true;\n//\t\t}\n\t\treturn;\n\t}\n\n\tMSG_WriteByte(&sv.multicast, svcfte_trailparticles);\n\tMSG_WriteEntity(&sv.multicast, ednum);\n\tMSG_WriteShort(&sv.multicast, efnum);\n\tMSG_WriteCoord(&sv.multicast, start[0]);\n\tMSG_WriteCoord(&sv.multicast, start[1]);\n\tMSG_WriteCoord(&sv.multicast, start[2]);\n\tMSG_WriteCoord(&sv.multicast, end[0]);\n\tMSG_WriteCoord(&sv.multicast, end[1]);\n\tMSG_WriteCoord(&sv.multicast, end[2]);\n\n#ifdef NQPROT\n\tMSG_WriteByte(&sv.nqmulticast, svcdp_trailparticles);\n\tMSG_WriteEntity(&sv.nqmulticast, ednum);\n\tMSG_WriteShort(&sv.nqmulticast, efnum);\n\tMSG_WriteCoord(&sv.nqmulticast, start[0]);\n\tMSG_WriteCoord(&sv.nqmulticast, start[1]);\n\tMSG_WriteCoord(&sv.nqmulticast, start[2]);\n\tMSG_WriteCoord(&sv.nqmulticast, end[0]);\n\tMSG_WriteCoord(&sv.nqmulticast, end[1]);\n\tMSG_WriteCoord(&sv.nqmulticast, end[2]);\n#endif\n\n\tSV_MulticastProtExt(start, MULTICAST_PHS, pr_global_struct->dimension_send, PEXT_CSQC, 0);\n#endif\n}\n//void(float effectnum, vector origin [, vector dir, float count]) pointparticles (EXT_CSQC)\nvoid QCBUILTIN PF_sv_pointparticles(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n#ifdef PEXT_CSQC\n\tint efnum = G_FLOAT(OFS_PARM0);\n\tfloat *org = G_VECTOR(OFS_PARM1);\n\tfloat *vel = (prinst->callargc < 3)?vec3_origin:G_VECTOR(OFS_PARM2);\n\tint count = (prinst->callargc < 4)?1:G_FLOAT(OFS_PARM3);\n\n\tif (efnum <= 0)\n\t{\n//\t\tif (!ssqc_deprecated_warned)\n//\t\t{\n//\t\t\tPR_RunWarning(prinst, \"PF_sv_pointparticles: invalid effect\");\n//\t\t\tssqc_deprecated_warned = true;\n//\t\t}\n\t\treturn;\n\t}\n\n\tif (count > 65535)\n\t\tcount = 65535;\n\n\tif (count == 1 && DotProduct(vel, vel) == 0)\n\t{\n\t\tMSG_WriteByte(&sv.multicast, svcfte_pointparticles1);\n\t\tMSG_WriteShort(&sv.multicast, efnum);\n\t\tMSG_WriteCoord(&sv.multicast, org[0]);\n\t\tMSG_WriteCoord(&sv.multicast, org[1]);\n\t\tMSG_WriteCoord(&sv.multicast, org[2]);\n\n#ifdef NQPROT\n\t\tMSG_WriteByte(&sv.nqmulticast, svcdp_pointparticles1);\n\t\tMSG_WriteShort(&sv.nqmulticast, efnum);\n\t\tMSG_WriteCoord(&sv.nqmulticast, org[0]);\n\t\tMSG_WriteCoord(&sv.nqmulticast, org[1]);\n\t\tMSG_WriteCoord(&sv.nqmulticast, org[2]);\n#endif\n\t}\n\telse\n\t{\n\t\tMSG_WriteByte(&sv.multicast, svcfte_pointparticles);\n\t\tMSG_WriteShort(&sv.multicast, efnum);\n\t\tMSG_WriteCoord(&sv.multicast, org[0]);\n\t\tMSG_WriteCoord(&sv.multicast, org[1]);\n\t\tMSG_WriteCoord(&sv.multicast, org[2]);\n\t\tMSG_WriteCoord(&sv.multicast, vel[0]);\n\t\tMSG_WriteCoord(&sv.multicast, vel[1]);\n\t\tMSG_WriteCoord(&sv.multicast, vel[2]);\n\t\tMSG_WriteShort(&sv.multicast, count);\n\n#ifdef NQPROT\n\t\tMSG_WriteByte(&sv.nqmulticast, svcdp_pointparticles);\n\t\tMSG_WriteShort(&sv.nqmulticast, efnum);\n\t\tMSG_WriteCoord(&sv.nqmulticast, org[0]);\n\t\tMSG_WriteCoord(&sv.nqmulticast, org[1]);\n\t\tMSG_WriteCoord(&sv.nqmulticast, org[2]);\n\t\tMSG_WriteCoord(&sv.nqmulticast, vel[0]);\n\t\tMSG_WriteCoord(&sv.nqmulticast, vel[1]);\n\t\tMSG_WriteCoord(&sv.nqmulticast, vel[2]);\n\t\tMSG_WriteShort(&sv.nqmulticast, count);\n#endif\n\t}\n\tSV_MulticastProtExt(org, MULTICAST_PHS, pr_global_struct->dimension_send, PEXT_CSQC, 0);\n#endif\n}\n\n//DP_TE_STANDARDEFFECTBUILTINS\n//void(vector org) te_gunshot = #418;\nstatic void QCBUILTIN PF_te_gunshot(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint count;\n\tif (svprogfuncs->callargc >= 2)\n\t{\n\t\tcount = G_FLOAT(OFS_PARM1);\n\t\tSV_point_tempentity(G_VECTOR(OFS_PARM0), TEQW_QWGUNSHOT, count);\n\t}\n\telse\n\t{\n\t\tcount = 1;\n\t\tSV_point_tempentity(G_VECTOR(OFS_PARM0), TEQW_NQGUNSHOT, count);\n\t}\n}\n//DP_TE_QUADEFFECTS1\nstatic void QCBUILTIN PF_te_gunshotquad(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint count;\n\tif (svprogfuncs->callargc >= 2)\n\t\tcount = G_FLOAT(OFS_PARM1);\n\telse\n\t\tcount = 1;\n\tSV_point_tempentity(G_VECTOR(OFS_PARM0), TEDP_GUNSHOTQUAD, count);\n}\n\n//DP_TE_STANDARDEFFECTBUILTINS\n//void(vector org) te_spike = #419;\nstatic void QCBUILTIN PF_te_spike(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tSV_point_tempentity(G_VECTOR(OFS_PARM0), TE_SPIKE, 1);\n}\n\n//DP_TE_QUADEFFECTS1\nstatic void QCBUILTIN PF_te_spikequad(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tSV_point_tempentity(G_VECTOR(OFS_PARM0), TEDP_SPIKEQUAD, 1);\n}\n\n// FTE_TE_STANDARDEFFECTBUILTINS\nstatic void QCBUILTIN PF_te_lightningblood(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tSV_point_tempentity(G_VECTOR(OFS_PARM0), TEQW_LIGHTNINGBLOOD, 1);\n}\n\n// FTE_TE_STANDARDEFFECTBUILTINS\nstatic void QCBUILTIN PF_te_bloodqw(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint count;\n\tif (svprogfuncs->callargc >= 2)\n\t\tcount = G_FLOAT(OFS_PARM1);\n\telse\n\t\tcount = 1;\n\tSV_point_tempentity(G_VECTOR(OFS_PARM0), TEQW_QWBLOOD, count);\n}\n\n//DP_TE_STANDARDEFFECTBUILTINS\n//void(vector org) te_superspike = #420;\nstatic void QCBUILTIN PF_te_superspike(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tSV_point_tempentity(G_VECTOR(OFS_PARM0), TE_SUPERSPIKE, 1);\n}\n//DP_TE_QUADEFFECTS1\nstatic void QCBUILTIN PF_te_superspikequad(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tSV_point_tempentity(G_VECTOR(OFS_PARM0), TEDP_SUPERSPIKEQUAD, 1);\n}\n\n//DP_TE_STANDARDEFFECTBUILTINS\n//void(vector org) te_explosion = #421;\nstatic void QCBUILTIN PF_te_explosion(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tif (progstype == PROG_QW)\n\t\tSV_point_tempentity(G_VECTOR(OFS_PARM0), TEQW_QWEXPLOSION, 1);\n\telse\n\t\tSV_point_tempentity(G_VECTOR(OFS_PARM0), TEQW_NQEXPLOSION, 1);\n}\n//DP_TE_QUADEFFECTS1\nstatic void QCBUILTIN PF_te_explosionquad(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tSV_point_tempentity(G_VECTOR(OFS_PARM0), TEDP_EXPLOSIONQUAD, 1);\n}\n\n//DP_TE_STANDARDEFFECTBUILTINS\n//void(vector org) te_tarexplosion = #422;\nstatic void QCBUILTIN PF_te_tarexplosion(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tSV_point_tempentity(G_VECTOR(OFS_PARM0), TE_TAREXPLOSION, 1);\n}\n\n//DP_TE_STANDARDEFFECTBUILTINS\n//void(vector org) te_wizspike = #423;\nstatic void QCBUILTIN PF_te_wizspike(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tSV_point_tempentity(G_VECTOR(OFS_PARM0), TE_WIZSPIKE, 1);\n}\n\n//DP_TE_STANDARDEFFECTBUILTINS\n//void(vector org) te_knightspike = #424;\nstatic void QCBUILTIN PF_te_knightspike(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tSV_point_tempentity(G_VECTOR(OFS_PARM0), TE_KNIGHTSPIKE, 1);\n}\n\n//DP_TE_STANDARDEFFECTBUILTINS\n//void(vector org) te_lavasplash = #425;\nstatic void QCBUILTIN PF_te_lavasplash(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tSV_point_tempentity(G_VECTOR(OFS_PARM0), TE_LAVASPLASH, 1);\n}\n\n//DP_TE_STANDARDEFFECTBUILTINS\n//void(vector org) te_teleport = #426;\nstatic void QCBUILTIN PF_te_teleport(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tSV_point_tempentity(G_VECTOR(OFS_PARM0), TE_TELEPORT, 1);\n}\n\n//DP_TE_STANDARDEFFECTBUILTINS\n//void(vector org, float color, float length) te_explosion2 = #427;\nstatic void QCBUILTIN PF_te_explosion2(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tqboolean old = false;\n\tfloat *org = G_VECTOR(OFS_PARM0);\n\tint start = G_FLOAT(OFS_PARM1);\n\tint length = G_FLOAT(OFS_PARM2);\n\tstart = bound(0, start, 255);\n\tlength = bound(0, length, 255-start);\n\n\tfor(;;)\n\t{\n\t\tMSG_WriteByte (&sv.multicast, svc_temp_entity);\n\t\tMSG_WriteByte (&sv.multicast, old?TEQW_QWEXPLOSION:TEQW_EXPLOSION2);\n\t\tMSG_WriteCoord (&sv.multicast, org[0]);\n\t\tMSG_WriteCoord (&sv.multicast, org[1]);\n\t\tMSG_WriteCoord (&sv.multicast, org[2]);\n\t\tif (!old)\n\t\t{\n\t\t\tMSG_WriteByte (&sv.multicast, start);\n\t\t\tMSG_WriteByte (&sv.multicast, length);\n\t\t}\n\t#ifdef NQPROT\n\t\tif (!old)\n\t\t{\n\t\t\tMSG_WriteByte (&sv.nqmulticast, svc_temp_entity);\n\t\t\tMSG_WriteByte (&sv.nqmulticast, TENQ_EXPLOSION2);\n\t\t\tMSG_WriteCoord (&sv.nqmulticast, org[0]);\n\t\t\tMSG_WriteCoord (&sv.nqmulticast, org[1]);\n\t\t\tMSG_WriteCoord (&sv.nqmulticast, org[2]);\n\t\t\tMSG_WriteByte (&sv.nqmulticast, start);\n\t\t\tMSG_WriteByte (&sv.nqmulticast, length);\n\t\t}\n\t#endif\n\n\t\tif (old)\n\t\t{\n\t\t\tSV_MulticastProtExt(org, MULTICAST_PHS, pr_global_struct->dimension_send, 0, PEXT_TE_BULLET);\n\t\t\tbreak;\n\t\t}\n\t\telse\n\t\t\tSV_MulticastProtExt(org, MULTICAST_PHS, pr_global_struct->dimension_send, PEXT_TE_BULLET, 0);\n\t\told = true;\n\t}\n}\n\n//DP_TE_FLAMEJET\nstatic void QCBUILTIN PF_te_flamejet(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tfloat *org = G_VECTOR(OFS_PARM0);\n\tfloat *vel = G_VECTOR(OFS_PARM1);\n\tfloat howmany = bound(0,G_FLOAT(OFS_PARM2),255);\n\n\tMSG_WriteByte (&sv.multicast, svc_temp_entity);\n\tMSG_WriteByte (&sv.multicast, TEDP_FLAMEJET);\n\tMSG_WriteCoord (&sv.multicast, org[0]);\n\tMSG_WriteCoord (&sv.multicast, org[1]);\n\tMSG_WriteCoord (&sv.multicast, org[2]);\n\tMSG_WriteCoord (&sv.multicast, vel[0]);\n\tMSG_WriteCoord (&sv.multicast, vel[1]);\n\tMSG_WriteCoord (&sv.multicast, vel[2]);\n\tMSG_WriteByte (&sv.multicast, howmany);\n#ifdef NQPROT\n\tMSG_WriteByte (&sv.nqmulticast, svc_temp_entity);\n\tMSG_WriteByte (&sv.nqmulticast, TEDP_FLAMEJET);\n\tMSG_WriteCoord (&sv.nqmulticast, org[0]);\n\tMSG_WriteCoord (&sv.nqmulticast, org[1]);\n\tMSG_WriteCoord (&sv.nqmulticast, org[2]);\n\tMSG_WriteCoord (&sv.nqmulticast, vel[0]);\n\tMSG_WriteCoord (&sv.nqmulticast, vel[1]);\n\tMSG_WriteCoord (&sv.nqmulticast, vel[2]);\n\tMSG_WriteByte (&sv.nqmulticast, howmany);\n#endif\n\tSV_MulticastProtExt(org, MULTICAST_PHS, pr_global_struct->dimension_send, 0, 0);\n}\n\n//DP_TE_STANDARDEFFECTBUILTINS\n//void(entity own, vector start, vector end) te_lightning1 = #428;\nstatic void QCBUILTIN PF_te_lightning1(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tSV_beam_tempentity(G_EDICTNUM(prinst, OFS_PARM0), G_VECTOR(OFS_PARM1), G_VECTOR(OFS_PARM2), TE_LIGHTNING1);\n}\n\n//DP_TE_STANDARDEFFECTBUILTINS\n//void(entity own, vector start, vector end) te_lightning2 = #429;\nstatic void QCBUILTIN PF_te_lightning2(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tSV_beam_tempentity(G_EDICTNUM(prinst, OFS_PARM0), G_VECTOR(OFS_PARM1), G_VECTOR(OFS_PARM2), TE_LIGHTNING2);\n}\n\n//DP_TE_STANDARDEFFECTBUILTINS\n//void(entity own, vector start, vector end) te_lightning3 = #430;\nstatic void QCBUILTIN PF_te_lightning3(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tSV_beam_tempentity(G_EDICTNUM(prinst, OFS_PARM0), G_VECTOR(OFS_PARM1), G_VECTOR(OFS_PARM2), TE_LIGHTNING3);\n}\n\n//DP_TE_STANDARDEFFECTBUILTINS\n//void(entity own, vector start, vector end) te_beam = #431;\nstatic void QCBUILTIN PF_te_beam(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tSV_beam_tempentity(G_EDICTNUM(prinst, OFS_PARM0), G_VECTOR(OFS_PARM1), G_VECTOR(OFS_PARM2), TEQW_BEAM);\n}\n\nstatic void QCBUILTIN PF_te_muzzleflash(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tedict_t *e = G_EDICT(prinst, OFS_PARM0);\n//this is for lamers with old (or unsupported) clients\n\tif (progstype == PROG_QW)\n\t{\n\t\tMSG_WriteByte (&sv.multicast, svc_muzzleflash);\n\t\tMSG_WriteEntity (&sv.multicast, NUM_FOR_EDICT(prinst, e));\n\n\t\tSV_MulticastProtExt (e->v->origin, MULTICAST_PHS, pr_global_struct->dimension_send, 0, 0);\n\t}\n\telse\n\t{\t//consistent with nq, this'll be cleaned up elsewhere.\n\t\te->v->effects = (int)e->v->effects | EF_MUZZLEFLASH;\n\t}\n}\n\n//DP_TE_SPARK\nstatic void QCBUILTIN PF_te_spark(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tfloat *org = G_VECTOR(OFS_PARM0);\n\n\tif (G_FLOAT(OFS_PARM2) < 1)\n\t\treturn;\n\n\tMSG_WriteByte(&sv.multicast, svc_temp_entity);\n\tMSG_WriteByte(&sv.multicast, TEDP_SPARK);\n\t// origin\n\tMSG_WriteCoord(&sv.multicast, org[0]);\n\tMSG_WriteCoord(&sv.multicast, org[1]);\n\tMSG_WriteCoord(&sv.multicast, org[2]);\n\t// velocity\n\tMSG_WriteByte(&sv.multicast, bound(-128, (int) G_VECTOR(OFS_PARM1)[0], 127));\n\tMSG_WriteByte(&sv.multicast, bound(-128, (int) G_VECTOR(OFS_PARM1)[1], 127));\n\tMSG_WriteByte(&sv.multicast, bound(-128, (int) G_VECTOR(OFS_PARM1)[2], 127));\n\t// count\n\tMSG_WriteByte(&sv.multicast, bound(0, (int) G_FLOAT(OFS_PARM2), 255));\n\n//the nq version\n#ifdef NQPROT\n\tMSG_WriteByte(&sv.nqmulticast, svc_temp_entity);\n\tMSG_WriteByte(&sv.nqmulticast, TEDP_SPARK);\n\t// origin\n\tMSG_WriteCoord(&sv.nqmulticast, org[0]);\n\tMSG_WriteCoord(&sv.nqmulticast, org[1]);\n\tMSG_WriteCoord(&sv.nqmulticast, org[2]);\n\t// velocity\n\tMSG_WriteByte(&sv.nqmulticast, bound(-128, (int) G_VECTOR(OFS_PARM1)[0], 127));\n\tMSG_WriteByte(&sv.nqmulticast, bound(-128, (int) G_VECTOR(OFS_PARM1)[1], 127));\n\tMSG_WriteByte(&sv.nqmulticast, bound(-128, (int) G_VECTOR(OFS_PARM1)[2], 127));\n\t// count\n\tMSG_WriteByte(&sv.nqmulticast, bound(0, (int) G_FLOAT(OFS_PARM2), 255));\n#endif\n\tSV_MulticastProtExt (org, MULTICAST_PVS, pr_global_struct->dimension_send, 0, 0);\n}\n\n// #416 void(vector org) te_smallflash (DP_TE_SMALLFLASH)\nstatic void QCBUILTIN PF_te_smallflash(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tfloat *org = G_VECTOR(OFS_PARM0);\n\tSV_point_tempentity(org, TEDP_SMALLFLASH, 0);\n}\n\n// #417 void(vector org, float radius, float lifetime, vector color) te_customflash (DP_TE_CUSTOMFLASH)\nstatic void QCBUILTIN PF_te_customflash(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tfloat *org = G_VECTOR(OFS_PARM0);\n\n\tif (G_FLOAT(OFS_PARM1) <= 0 || G_FLOAT(OFS_PARM2) < (1.0 / 256.0))\n\t\treturn;\n\tMSG_WriteByte(&sv.multicast, svc_temp_entity);\n\tMSG_WriteByte(&sv.multicast, TEDP_CUSTOMFLASH);\n\t// origin\n\tMSG_WriteCoord(&sv.multicast, G_VECTOR(OFS_PARM0)[0]);\n\tMSG_WriteCoord(&sv.multicast, G_VECTOR(OFS_PARM0)[1]);\n\tMSG_WriteCoord(&sv.multicast, G_VECTOR(OFS_PARM0)[2]);\n\t// radius\n\tMSG_WriteByte(&sv.multicast, bound(1, G_FLOAT(OFS_PARM1) / 8 - 1, 255));\n\t// lifetime\n\tMSG_WriteByte(&sv.multicast, bound(0, G_FLOAT(OFS_PARM2) * 256 - 1, 255));\n\t// color\n\tMSG_WriteByte(&sv.multicast, bound(0, G_VECTOR(OFS_PARM3)[0] * 255, 255));\n\tMSG_WriteByte(&sv.multicast, bound(0, G_VECTOR(OFS_PARM3)[1] * 255, 255));\n\tMSG_WriteByte(&sv.multicast, bound(0, G_VECTOR(OFS_PARM3)[2] * 255, 255));\n\n#ifdef NQPROT\n\tMSG_WriteByte(&sv.nqmulticast, svc_temp_entity);\n\tMSG_WriteByte(&sv.nqmulticast, TEDP_CUSTOMFLASH);\n\t// origin\n\tMSG_WriteCoord(&sv.nqmulticast, G_VECTOR(OFS_PARM0)[0]);\n\tMSG_WriteCoord(&sv.nqmulticast, G_VECTOR(OFS_PARM0)[1]);\n\tMSG_WriteCoord(&sv.nqmulticast, G_VECTOR(OFS_PARM0)[2]);\n\t// radius\n\tMSG_WriteByte(&sv.nqmulticast, bound(0, G_FLOAT(OFS_PARM1) / 8 - 1, 255));\n\t// lifetime\n\tMSG_WriteByte(&sv.nqmulticast, bound(0, G_FLOAT(OFS_PARM2) * 256 - 1, 255));\n\t// color\n\tMSG_WriteByte(&sv.nqmulticast, bound(0, G_VECTOR(OFS_PARM3)[0] * 255, 255));\n\tMSG_WriteByte(&sv.nqmulticast, bound(0, G_VECTOR(OFS_PARM3)[1] * 255, 255));\n\tMSG_WriteByte(&sv.nqmulticast, bound(0, G_VECTOR(OFS_PARM3)[2] * 255, 255));\n#endif\n\tSV_MulticastProtExt (org, MULTICAST_PVS, pr_global_struct->dimension_send, 0, 0);\n}\n\n//#408 void(vector mincorner, vector maxcorner, vector vel, float howmany, float color, float gravityflag, float randomveljitter) te_particlecube (DP_TE_PARTICLECUBE)\nstatic void QCBUILTIN PF_te_particlecube(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tfloat *min = G_VECTOR(OFS_PARM0);\n\tfloat *max = G_VECTOR(OFS_PARM1);\n\tfloat *dir = G_VECTOR(OFS_PARM2);\n\tfloat count = G_FLOAT(OFS_PARM3);\n\tfloat color = G_FLOAT(OFS_PARM4);\n\tfloat gravityflag = G_FLOAT(OFS_PARM5);\n\tfloat randomveljitter = G_FLOAT(OFS_PARM6);\n\n\tvec3_t org;\n\n\tif (count < 1)\n\t\treturn;\n\n\t// [vector] min [vector] max [vector] dir [short] count [byte] color [byte] gravity [coord] randomvel\n\n\tMSG_WriteByte (&sv.multicast, svc_temp_entity);\n\tMSG_WriteByte (&sv.multicast, TEDP_PARTICLECUBE);\n\tMSG_WriteCoord(&sv.multicast, min[0]);\n\tMSG_WriteCoord(&sv.multicast, min[1]);\n\tMSG_WriteCoord(&sv.multicast, min[2]);\n\tMSG_WriteCoord(&sv.multicast, max[0]);\n\tMSG_WriteCoord(&sv.multicast, max[1]);\n\tMSG_WriteCoord(&sv.multicast, max[2]);\n\tMSG_WriteCoord(&sv.multicast, dir[0]);\n\tMSG_WriteCoord(&sv.multicast, dir[1]);\n\tMSG_WriteCoord(&sv.multicast, dir[2]);\n\tMSG_WriteShort(&sv.multicast, bound(0, count, 65535));\n\tMSG_WriteByte (&sv.multicast, bound(0, color, 255));\n\tMSG_WriteByte (&sv.multicast, (int)gravityflag!=0);\n\tMSG_WriteCoord(&sv.multicast, randomveljitter);\n\n#ifdef NQPROT\n\tMSG_WriteByte (&sv.nqmulticast, svc_temp_entity);\n\tMSG_WriteByte (&sv.nqmulticast, TEDP_PARTICLECUBE);\n\tMSG_WriteCoord(&sv.nqmulticast, min[0]);\n\tMSG_WriteCoord(&sv.nqmulticast, min[1]);\n\tMSG_WriteCoord(&sv.nqmulticast, min[2]);\n\tMSG_WriteCoord(&sv.nqmulticast, max[0]);\n\tMSG_WriteCoord(&sv.nqmulticast, max[1]);\n\tMSG_WriteCoord(&sv.nqmulticast, max[2]);\n\tMSG_WriteCoord(&sv.nqmulticast, dir[0]);\n\tMSG_WriteCoord(&sv.nqmulticast, dir[1]);\n\tMSG_WriteCoord(&sv.nqmulticast, dir[2]);\n\tMSG_WriteShort(&sv.nqmulticast, bound(0, count, 65535));\n\tMSG_WriteByte (&sv.nqmulticast, bound(0, color, 255));\n\tMSG_WriteByte (&sv.nqmulticast, (int)gravityflag!=0);\n\tMSG_WriteCoord(&sv.nqmulticast, randomveljitter);\n#endif\n\n\tVectorAdd(min, max, org);\n\tVectorScale(org, 0.5, org);\n\tSV_MulticastProtExt (org, MULTICAST_PVS, pr_global_struct->dimension_send, 0, 0);\n}\n\nstatic void QCBUILTIN PF_te_explosionrgb(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tfloat *org = G_VECTOR(OFS_PARM0);\n\tfloat *colour = G_VECTOR(OFS_PARM0);\n\n\tMSG_WriteByte(&sv.multicast, svc_temp_entity);\n\tMSG_WriteByte(&sv.multicast, TEDP_EXPLOSIONRGB);\n\t// origin\n\tMSG_WriteCoord(&sv.multicast, org[0]);\n\tMSG_WriteCoord(&sv.multicast, org[1]);\n\tMSG_WriteCoord(&sv.multicast, org[2]);\n\t// color\n\tMSG_WriteByte(&sv.multicast, bound(0, (int) (colour[0] * 255), 255));\n\tMSG_WriteByte(&sv.multicast, bound(0, (int) (colour[1] * 255), 255));\n\tMSG_WriteByte(&sv.multicast, bound(0, (int) (colour[2] * 255), 255));\n#ifdef NQPROT\n\tMSG_WriteByte(&sv.nqmulticast, svc_temp_entity);\n\tMSG_WriteByte(&sv.nqmulticast, TEDP_EXPLOSIONRGB);\n\t// origin\n\tMSG_WriteCoord(&sv.nqmulticast, org[0]);\n\tMSG_WriteCoord(&sv.nqmulticast, org[1]);\n\tMSG_WriteCoord(&sv.nqmulticast, org[2]);\n\t// color\n\tMSG_WriteByte(&sv.nqmulticast, bound(0, (int) (colour[0] * 255), 255));\n\tMSG_WriteByte(&sv.nqmulticast, bound(0, (int) (colour[1] * 255), 255));\n\tMSG_WriteByte(&sv.nqmulticast, bound(0, (int) (colour[2] * 255), 255));\n#endif\n\tSV_MulticastProtExt (org, MULTICAST_PVS, pr_global_struct->dimension_send, 0, 0);\n}\n\nstatic void QCBUILTIN PF_te_particlerain(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tfloat *min = G_VECTOR(OFS_PARM0);\n\tfloat *max = G_VECTOR(OFS_PARM1);\n\tfloat *velocity = G_VECTOR(OFS_PARM2);\n\tfloat count = G_FLOAT(OFS_PARM3);\n\tfloat colour = G_FLOAT(OFS_PARM4);\n\n\tif (count < 1)\n\t\treturn;\n\n\tMSG_WriteByte(&sv.multicast, svc_temp_entity);\n\tMSG_WriteByte(&sv.multicast, TEDP_PARTICLERAIN);\n\t// min\n\tMSG_WriteCoord(&sv.multicast, min[0]);\n\tMSG_WriteCoord(&sv.multicast, min[1]);\n\tMSG_WriteCoord(&sv.multicast, min[2]);\n\t// max\n\tMSG_WriteCoord(&sv.multicast, max[0]);\n\tMSG_WriteCoord(&sv.multicast, max[1]);\n\tMSG_WriteCoord(&sv.multicast, max[2]);\n\t// velocity\n\tMSG_WriteCoord(&sv.multicast, velocity[0]);\n\tMSG_WriteCoord(&sv.multicast, velocity[1]);\n\tMSG_WriteCoord(&sv.multicast, velocity[2]);\n\t// count\n\tMSG_WriteShort(&sv.multicast, min(count, 65535));\n\t// colour\n\tMSG_WriteByte(&sv.multicast, colour);\n\n#ifdef NQPROT\n\tMSG_WriteByte(&sv.nqmulticast, svc_temp_entity);\n\tMSG_WriteByte(&sv.nqmulticast, TEDP_PARTICLERAIN);\n\t// min\n\tMSG_WriteCoord(&sv.nqmulticast, min[0]);\n\tMSG_WriteCoord(&sv.nqmulticast, min[1]);\n\tMSG_WriteCoord(&sv.nqmulticast, min[2]);\n\t// max\n\tMSG_WriteCoord(&sv.nqmulticast, max[0]);\n\tMSG_WriteCoord(&sv.nqmulticast, max[1]);\n\tMSG_WriteCoord(&sv.nqmulticast, max[2]);\n\t// velocity\n\tMSG_WriteCoord(&sv.nqmulticast, velocity[0]);\n\tMSG_WriteCoord(&sv.nqmulticast, velocity[1]);\n\tMSG_WriteCoord(&sv.nqmulticast, velocity[2]);\n\t// count\n\tMSG_WriteShort(&sv.nqmulticast, min(count, 65535));\n\t// colour\n\tMSG_WriteByte(&sv.nqmulticast, colour);\n#endif\n\n\tSV_MulticastProtExt (NULL, MULTICAST_ALL, pr_global_struct->dimension_send, 0, 0);\n}\n\nstatic void QCBUILTIN PF_te_particlesnow(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tfloat *min = G_VECTOR(OFS_PARM0);\n\tfloat *max = G_VECTOR(OFS_PARM1);\n\tfloat *velocity = G_VECTOR(OFS_PARM2);\n\tfloat count = G_FLOAT(OFS_PARM3);\n\tfloat colour = G_FLOAT(OFS_PARM4);\n\n\tif (count < 1)\n\t\treturn;\n\n\tMSG_WriteByte(&sv.multicast, svc_temp_entity);\n\tMSG_WriteByte(&sv.multicast, TEDP_PARTICLESNOW);\n\t// min\n\tMSG_WriteCoord(&sv.multicast, min[0]);\n\tMSG_WriteCoord(&sv.multicast, min[1]);\n\tMSG_WriteCoord(&sv.multicast, min[2]);\n\t// max\n\tMSG_WriteCoord(&sv.multicast, max[0]);\n\tMSG_WriteCoord(&sv.multicast, max[1]);\n\tMSG_WriteCoord(&sv.multicast, max[2]);\n\t// velocity\n\tMSG_WriteCoord(&sv.multicast, velocity[0]);\n\tMSG_WriteCoord(&sv.multicast, velocity[1]);\n\tMSG_WriteCoord(&sv.multicast, velocity[2]);\n\t// count\n\tMSG_WriteShort(&sv.multicast, min(count, 65535));\n\t// colour\n\tMSG_WriteByte(&sv.multicast, colour);\n\n#ifdef NQPROT\n\tMSG_WriteByte(&sv.nqmulticast, svc_temp_entity);\n\tMSG_WriteByte(&sv.nqmulticast, TEDP_PARTICLESNOW);\n\t// min\n\tMSG_WriteCoord(&sv.nqmulticast, min[0]);\n\tMSG_WriteCoord(&sv.nqmulticast, min[1]);\n\tMSG_WriteCoord(&sv.nqmulticast, min[2]);\n\t// max\n\tMSG_WriteCoord(&sv.nqmulticast, max[0]);\n\tMSG_WriteCoord(&sv.nqmulticast, max[1]);\n\tMSG_WriteCoord(&sv.nqmulticast, max[2]);\n\t// velocity\n\tMSG_WriteCoord(&sv.nqmulticast, velocity[0]);\n\tMSG_WriteCoord(&sv.nqmulticast, velocity[1]);\n\tMSG_WriteCoord(&sv.nqmulticast, velocity[2]);\n\t// count\n\tMSG_WriteShort(&sv.nqmulticast, min(count, 65535));\n\t// colour\n\tMSG_WriteByte(&sv.nqmulticast, colour);\n#endif\n\n\tSV_MulticastProtExt (NULL, MULTICAST_ALL, pr_global_struct->dimension_send, 0, 0);\n}\n\n// #406 void(vector mincorner, vector maxcorner, float explosionspeed, float howmany) te_bloodshower (DP_TE_BLOODSHOWER)\nstatic void QCBUILTIN PF_te_bloodshower(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\t// [vector] min [vector] max [coord] explosionspeed [short] count\n\tfloat *min = G_VECTOR(OFS_PARM0);\n\tfloat *max = G_VECTOR(OFS_PARM1);\n\tfloat speed = G_FLOAT(OFS_PARM2);\n\tfloat count = G_FLOAT(OFS_PARM3);\n\tvec3_t org;\n\n\tMSG_WriteByte(&sv.multicast, svc_temp_entity);\n\tMSG_WriteByte(&sv.multicast, TEDP_BLOODSHOWER);\n\t// min\n\tMSG_WriteCoord(&sv.multicast, min[0]);\n\tMSG_WriteCoord(&sv.multicast, min[1]);\n\tMSG_WriteCoord(&sv.multicast, min[2]);\n\t// max\n\tMSG_WriteCoord(&sv.multicast, max[0]);\n\tMSG_WriteCoord(&sv.multicast, max[1]);\n\tMSG_WriteCoord(&sv.multicast, max[2]);\n\t// speed\n\tMSG_WriteCoord(&sv.multicast, speed);\n\t// count\n\tMSG_WriteShort(&sv.multicast, bound(0, count, 65535));\n\n#ifdef NQPROT\n\tMSG_WriteByte(&sv.nqmulticast, svc_temp_entity);\n\tMSG_WriteByte(&sv.nqmulticast, TEDP_BLOODSHOWER);\n\t// min\n\tMSG_WriteCoord(&sv.nqmulticast, min[0]);\n\tMSG_WriteCoord(&sv.nqmulticast, min[1]);\n\tMSG_WriteCoord(&sv.nqmulticast, min[2]);\n\t// max\n\tMSG_WriteCoord(&sv.nqmulticast, max[0]);\n\tMSG_WriteCoord(&sv.nqmulticast, max[1]);\n\tMSG_WriteCoord(&sv.nqmulticast, max[2]);\n\t// speed\n\tMSG_WriteCoord(&sv.nqmulticast, speed);\n\t// count\n\tMSG_WriteShort(&sv.nqmulticast, bound(0, count, 65535));\n#endif\n\n\tVectorAdd(min, max, org);\n\tVectorScale(org, 0.5, org);\n\tSV_MulticastProtExt (org, MULTICAST_PVS, pr_global_struct->dimension_send, 0, 0);\t//fixme: should this be phs instead?\n}\n\n//DP_SV_EFFECT\n//void(vector org, string modelname, float startframe, float endframe, float framerate) effect = #404;\nstatic void QCBUILTIN PF_effect(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tfloat *org = G_VECTOR(OFS_PARM0);\n\tconst char *name = PR_GetStringOfs(prinst, OFS_PARM1);\n\tfloat startframe = G_FLOAT(OFS_PARM2);\n\tfloat endframe = G_FLOAT(OFS_PARM3);\n\tfloat framerate = G_FLOAT(OFS_PARM4);\n\tint index = SV_ModelIndex(name);\n\n\tSV_Effect(org, index, startframe, endframe, framerate);\n}\n\n//DP_TE_PLASMABURN\n//void(vector org) te_plasmaburn = #433;\nstatic void QCBUILTIN PF_te_plasmaburn(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tfloat *org = G_VECTOR(OFS_PARM0);\n\tSV_point_tempentity(org, TEDP_PLASMABURN, 0);\n}\n\n\nint PF_ForceInfoKey_Internal(unsigned int entnum, const char *key, const char *value, size_t valsize)\n{\n\tconst char *oldval;\n\tsize_t oldsize;\n\tif (entnum == 0)\n\t{\t//serverinfo\n\t\toldval = InfoBuf_BlobForKey(&svs.info, key, &oldsize, NULL);\n\t\tif (oldsize == valsize && !memcmp(oldval, value, valsize))\n\t\t\treturn 2;\t//unchanged\n\t\tInfoBuf_SetStarBlobKey(&svs.info, key, value, valsize);\n\t\treturn 2;\n\t}\n\telse if (entnum <= sv.allocated_client_slots)\n\t{\t//woo. we found a client.\n\t\tif (svs.clients[entnum-1].state == cs_free)\n\t\t{\n\t\t\tCon_DPrintf(\"PF_ForceInfoKey: inactive client\\n\");\n\t\t\treturn 0;\n\t\t}\n\t\toldval = InfoBuf_BlobForKey(&svs.clients[entnum-1].userinfo, key, &oldsize, NULL);\n\t\tif (oldsize == valsize && !memcmp(oldval, value, valsize))\n\t\t\treturn 1;\t//unchanged\n\n\t\tif (InfoBuf_SetStarBlobKey(&svs.clients[entnum-1].userinfo, key, value, valsize))\n\t\t{\n\t\t\tSV_ExtractFromUserinfo (&svs.clients[entnum-1], false);\n\n\t\t\tvalue = InfoBuf_ValueForKey(&svs.clients[entnum-1].userinfo, key);\n\n\t\t\tif (!strcmp(key, \"*spectator\"))\n\t\t\t{\n\t\t\t\tint ns = !!atoi(value);\n\n\t\t\t\tif (svs.clients[entnum-1].spawned && svs.clients[entnum-1].state == cs_spawned)\n\t\t\t\t{\n\t\t\t\t\tif (svs.clients[entnum-1].spectator)\n\t\t\t\t\t\tsv.spawned_observer_slots--;\n\t\t\t\t\telse\n\t\t\t\t\t\tsv.spawned_client_slots--;\n\t\t\t\t\tsvs.clients[entnum-1].spectator = ns;\n\t\t\t\t\tif (ns)\n\t\t\t\t\t\tsv.spawned_observer_slots++;\n\t\t\t\t\telse\n\t\t\t\t\t\tsv.spawned_client_slots++;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tsvs.clients[entnum-1].spectator = ns;\n\t\t\t}\n\n\t\t\tSV_BroadcastUserinfoChange(&svs.clients[entnum-1], SV_UserInfoIsBasic(key), key, value);\n\t\t}\n\n\t\treturn 1;\n\t}\n\telse\n\t\tCon_DPrintf(\"PF_ForceInfoKey: not world or client\\n\");\n\treturn 0;\n}\n\nstatic void QCBUILTIN PF_ForceInfoKey(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tedict_t\t*e;\n\tconst char\t*value;\n\tconst char\t*key;\n\n\te = G_EDICT(prinst, OFS_PARM0);\n\tkey = PR_GetStringOfs(prinst, OFS_PARM1);\n\tvalue = PR_GetStringOfs(prinst, OFS_PARM2);\n\n\tG_FLOAT(OFS_RETURN) = PF_ForceInfoKey_Internal(e->entnum, key, value, strlen(value));\n}\nstatic void QCBUILTIN PF_ForceInfoKeyBlob(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tedict_t\t*e;\n\tconst char\t*value;\n\tconst char\t*key;\n\tint size;\n\n\te = G_EDICT(prinst, OFS_PARM0);\n\tkey = PR_GetStringOfs(prinst, OFS_PARM1);\n\tvalue = PR_GetStringOfs(prinst, OFS_PARM2);\n\tsize = G_INT(OFS_PARM3);\n\tif (size < 0)\n\t\tsize = 0;\n\tif (G_INT(OFS_PARM2) >= 0 && G_INT(OFS_PARM2) < prinst->stringtablesize)\n\t{\n\t\tif (value+size > prinst->stringtable+prinst->stringtablesize)\n\t\t\treturn;\n\t}\n\telse\n\t{\n\t\tif (size > strlen(value)+1)\n\t\t\treturn;\n\t}\n\n\tG_FLOAT(OFS_RETURN) = PF_ForceInfoKey_Internal(e->entnum, key, value, size);\n}\n\n/*\n=================\nPF_setcolors\n\nsets the color of a client and broadcasts the update to all connected clients\n\nsetcolors(clientent, value)\n=================\n*/\n\n//from lh\nstatic void QCBUILTIN PF_setcolors (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tclient_t\t*client;\n\tint\t\t\tentnum, i;\n\tchar number[8];\n\tchar *key = NULL;\n\n\tentnum = G_EDICTNUM(prinst, OFS_PARM0);\n\ti = G_FLOAT(OFS_PARM1);\n\n\tif (entnum < 1 || entnum > sv.allocated_client_slots)\n\t{\n\t\tCon_Printf (\"tried to setcolor a non-client\\n\");\n\t\treturn;\n\t}\n\n\tclient = &svs.clients[entnum-1];\n\tclient->edict->v->team = (i & 15) + 1;\n\n\tQ_snprintfz(number, sizeof(number), \"%i\", i>>4);\n\tif (strcmp(number, InfoBuf_ValueForKey(&client->userinfo, \"topcolor\")))\n\t{\n\t\tInfoBuf_SetValueForKey(&client->userinfo, \"topcolor\", number);\n\t\tkey = \"topcolor\";\n\t}\n\n\tQ_snprintfz(number, sizeof(number), \"%i\", i&15);\n\tif (strcmp(number, InfoBuf_ValueForKey(&client->userinfo, \"bottomcolor\")))\n\t{\n\t\tInfoBuf_SetValueForKey(&client->userinfo, \"bottomcolor\", number);\n\t\tkey = key?\"*bothcolours\":\"bottomcolor\";\n\t}\n\n\tSV_ExtractFromUserinfo (client, true);\n\tif (key)\n\t{\t//something changed at least.\n\t\tSV_BroadcastUserinfoChange(client, true, key, NULL);\n\t}\n}\n\nstatic void ParamNegateFix ( float * xx, float * yy, int Zone )\n{\n\tfloat x,y;\n\tx = xx[0];\n\ty = yy[0];\n\n\tif (Zone == SL_ORG_CC || SL_ORG_CW == Zone || SL_ORG_CE == Zone )\n\t\ty = y + 8000;\n\n\tif (Zone == SL_ORG_CC || SL_ORG_CN == Zone || SL_ORG_CS == Zone  )\n\t\tx = x + 8000;\n\n\txx[0] = x;\n\tyy[0] = y;\n}\nstatic void QCBUILTIN PF_ShowPic(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char *slot\t= PR_GetStringOfs(prinst, OFS_PARM0);\n\tconst char *picname\t= PR_GetStringOfs(prinst, OFS_PARM1);\n\tfloat x\t\t\t\t= G_FLOAT(OFS_PARM2);\n\tfloat y\t\t\t\t= G_FLOAT(OFS_PARM3);\n\tunsigned int zone\t= G_FLOAT(OFS_PARM4);\n\tint entnum;\n\n\tclient_t *cl;\n\n\tParamNegateFix( &x, &y, zone );\n\n\tif (prinst->callargc==6)\n\t{\t//to a single client\n\t\tentnum = G_EDICTNUM(prinst, OFS_PARM5)-1;\n\t\tif (entnum < 0 || entnum >= sv.allocated_client_slots)\n\t\t\tPR_RunError (prinst, \"PF_ShowPic: not a client\");\n\n\t\tif (!(svs.clients[entnum].fteprotocolextensions & PEXT_SHOWPIC))\n\t\t\treturn;\t//need an extension for this. duh.\n\n\t\tcl = ClientReliableWrite_BeginSplit(&svs.clients[entnum], svcfte_showpic, 8 + strlen(slot)+strlen(picname));\n\t\tClientReliableWrite_Byte(cl, zone);\n\t\tClientReliableWrite_String(cl, slot);\n\t\tClientReliableWrite_String(cl, picname);\n\t\tClientReliableWrite_Short(cl, x);\n\t\tClientReliableWrite_Short(cl, y);\n\t}\n\telse\n\t{\n\t\tprinst->callargc = 6;\n\t\tfor (entnum = 0; entnum < sv.allocated_client_slots; entnum++)\n\t\t{\n\t\t\tG_INT(OFS_PARM5) = EDICT_TO_PROG(prinst, EDICT_NUM_PB(prinst, entnum+1));\n\t\t\tPF_ShowPic(prinst, pr_globals);\n\t\t}\n\t}\n};\n\nstatic void QCBUILTIN PF_HidePic(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tclient_t *cl;\n\tconst char *slot\t= PR_GetStringOfs(prinst, OFS_PARM0);\n\tint entnum;\n\n\tif (prinst->callargc==2)\n\t{\t//to a single client\n\t\tentnum = G_EDICTNUM(prinst, OFS_PARM1)-1;\n\t\tif (entnum < 0 || entnum >= sv.allocated_client_slots)\n\t\t\tPR_RunError (prinst, \"PF_HidePic: not a client\");\n\n\t\tif (!(svs.clients[entnum].fteprotocolextensions & PEXT_SHOWPIC))\n\t\t\treturn;\t//need an extension for this. duh.\n\n\t\tcl = ClientReliableWrite_BeginSplit(&svs.clients[entnum], svcfte_hidepic, 2 + strlen(slot));\n\t\tClientReliableWrite_String(cl, slot);\n\t}\n\telse\n\t{\n\t\tprinst->callargc = 2;\n\t\tfor (entnum = 0; entnum < sv.allocated_client_slots; entnum++)\n\t\t{\n\t\t\tG_INT(OFS_PARM1) = EDICT_TO_PROG(prinst, EDICT_NUM_PB(prinst, entnum+1));\n\t\t\tPF_HidePic(prinst, pr_globals);\n\t\t}\n\t}\n};\n\n\nstatic void QCBUILTIN PF_MovePic(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char *slot\t= PR_GetStringOfs(prinst, OFS_PARM0);\n\tfloat x\t\t= G_FLOAT(OFS_PARM1);\n\tfloat y\t\t= G_FLOAT(OFS_PARM2);\n\tfloat zone\t= G_FLOAT(OFS_PARM3);\n\tint entnum;\n\tclient_t *cl;\n\n\tParamNegateFix( &x, &y, zone );\n\n\tif (prinst->callargc==5)\n\t{\t//to a single client\n\t\tentnum = G_EDICTNUM(prinst, OFS_PARM4)-1;\n\t\tif (entnum < 0 || entnum >= sv.allocated_client_slots)\n\t\t\tPR_RunError (prinst, \"PF_MovePic: not a client\");\n\n\t\tif (!(svs.clients[entnum].fteprotocolextensions & PEXT_SHOWPIC))\n\t\t\treturn;\t//need an extension for this. duh.\n\n\t\tcl = ClientReliableWrite_BeginSplit(&svs.clients[entnum], svcfte_movepic, 6 + strlen(slot));\n\t\tClientReliableWrite_String(cl, slot);\n\t\tClientReliableWrite_Byte(cl, zone);\n\t\tClientReliableWrite_Short(cl, x);\n\t\tClientReliableWrite_Short(cl, y);\n\t}\n\telse\n\t{\n\t\tprinst->callargc = 5;\n\t\tfor (entnum = 0; entnum < sv.allocated_client_slots; entnum++)\n\t\t{\n\t\t\tG_INT(OFS_PARM4) = EDICT_TO_PROG(prinst, EDICT_NUM_PB(prinst, entnum+1));\n\t\t\tPF_MovePic(prinst, pr_globals);\n\t\t}\n\t}\n};\n\nstatic void QCBUILTIN PF_ChangePic(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char *slot\t= PR_GetStringOfs(prinst, OFS_PARM0);\n\tconst char *newpic= PR_GetStringOfs(prinst, OFS_PARM1);\n\tint entnum;\n\tclient_t *cl;\n\n\tif (prinst->callargc==3)\n\t{\t//to a single client\n\t\tentnum = G_EDICTNUM(prinst, OFS_PARM2)-1;\n\t\tif (entnum < 0 || entnum >= sv.allocated_client_slots)\n\t\t\tPR_RunError (prinst, \"PF_ChangePic: not a client\");\n\n\t\tif (!(svs.clients[entnum].fteprotocolextensions & PEXT_SHOWPIC))\n\t\t\treturn;\t//need an extension for this. duh.\n\n\t\tcl = ClientReliableWrite_BeginSplit(&svs.clients[entnum], svcfte_updatepic, 3 + strlen(slot)+strlen(newpic));\n\t\tClientReliableWrite_String(cl, slot);\n\t\tClientReliableWrite_String(cl, newpic);\n\t}\n\telse\n\t{\n\t\tprinst->callargc = 3;\n\t\tfor (entnum = 0; entnum < sv.allocated_client_slots; entnum++)\n\t\t{\n\t\t\tG_INT(OFS_PARM2) = EDICT_TO_PROG(prinst, EDICT_NUM_PB(prinst, entnum+1));\n\t\t\tPF_ChangePic(prinst, pr_globals);\n\t\t}\n\t}\n}\n\n//the first implementation of this function was (float type, float num, string name)\n//it is now float num, float type, .field\n//EXT_CSQC_1\nstatic void QCBUILTIN PF_clientstat(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n#if 0 //this is the old code\n\tchar *name = PF_VarString(prinst, 2, pr_globals);\n\tSV_QCStatName(G_FLOAT(OFS_PARM0), name, G_FLOAT(OFS_PARM1));\n#else\n\tSV_QCStatFieldIdx(G_FLOAT(OFS_PARM1), G_INT(OFS_PARM2)+prinst->fieldadjust, G_FLOAT(OFS_PARM0));\n#endif\n}\n//EXT_CSQC_1\n//void(float num, float type, string name) globalstat\nstatic void QCBUILTIN PF_globalstat(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char *name = PF_VarString(prinst, 2, pr_globals);\n#if 0 //this is the old code\n\tSV_QCStatName(G_FLOAT(OFS_PARM0), name, G_FLOAT(OFS_PARM1));\n#else\n\tSV_QCStatGlobal(G_FLOAT(OFS_PARM1), name, G_FLOAT(OFS_PARM0));\n#endif\n}\n\n//void(float num, float type, void *ptr) pointerstat\nstatic void QCBUILTIN PF_pointerstat(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint num = G_FLOAT(OFS_PARM0);\n\tint type = G_FLOAT(OFS_PARM1);\n\tint addr = G_INT(OFS_PARM2);\n\tint size = (type == ev_vector)?sizeof(vec3_t):sizeof(float);\n\tif (addr < 0 || addr+size >= prinst->stringtablesize)\n\t\tprinst->RunError(prinst, \"QCVM address %#x is not valid.\", addr);\n\telse\n\t\tSV_QCStatPtr(type, prinst->stringtable+addr, num);\n}\n\n//void(entity e, vector flags, entity target) setsendneeded\nstatic void QCBUILTIN PF_setsendneeded(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tunsigned int subject = G_EDICTNUM(svprogfuncs, OFS_PARM0);\n\tquint64_t fl = (((quint64_t)G_FLOAT(OFS_PARM1+0)&0xffffff)<<(SENDFLAGS_SHIFT+ 0))\n\t\t\t\t | (((quint64_t)G_FLOAT(OFS_PARM1+1)&0xffffff)<<(SENDFLAGS_SHIFT+24))\n\t\t\t\t | (((quint64_t)G_FLOAT(OFS_PARM1+2)&0xffffff)<<(SENDFLAGS_SHIFT+48));\n\tunsigned int to = G_EDICTNUM(svprogfuncs, OFS_PARM2);\n\tif (!to)\n\t{\t//broadcast\n\t\tfor (to = 0; to < sv.allocated_client_slots; to++)\n\t\t\tif (svs.clients[to].pendingcsqcbits && subject < svs.clients[to].max_net_ents)\n\t\t\t\tsvs.clients[to].pendingcsqcbits[subject] |= fl;\n\t}\n\telse\n\t{\n\t\tto--;\n\t\tif (to >= sv.allocated_client_slots || !svs.clients[to].pendingcsqcbits || subject >= svs.clients[to].max_net_ents)\n\t\t\treturn;\t//some kind of error.\n\t\telse\n\t\t\tsvs.clients[to].pendingcsqcbits[subject] |= fl;\n\t}\n}\n\n//EXT_CSQC_1\nstatic void QCBUILTIN PF_runclientphys(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tunsigned int i, n;\n\textern qbyte *playertouch;\n\textern size_t playertouchmax;\n\tunsigned int msecs;\n\textern cvar_t sv_gravity;\n\tedict_t *ent = G_EDICT(prinst, OFS_PARM0);\n\tedict_t *touched;\n\tclient_t *client;\n\n\tif (!ent || ent->readonly)\n\t{\n\t\tCon_Printf(\"runplayerphysics called on read-only entity\\n\");\n\t\treturn;\n\t}\n\n\tVALGRIND_MAKE_MEM_UNDEFINED(&pmove, sizeof(pmove));\n\n\tif (ent->entnum >= 1 && ent->entnum <= sv.allocated_client_slots)\n\t\tclient = &svs.clients[ent->entnum-1];\n\telse\n\t\tclient = NULL;\n\tpmove.pm_type = SV_PMTypeForClient(client, ent);\n\n\tpmove.jump_msec = 0;\n\n\tpmove.jump_held = ((int)ent->xv->pmove_flags)&PMF_JUMP_HELD;\n\tif (progstype != PROG_QW)\t//this is just annoying.\n\t\tpmove.waterjumptime = ent->v->teleport_time - sv.time;\n\telse\n\t\tpmove.waterjumptime = ent->v->teleport_time;\n\n//set up the movement command\n\tpmove.cmd.sequence = pr_global_struct->input_sequence;\n\tmsecs = pr_global_struct->input_timelength*1000 + 0.5f;\n\t//precision inaccuracies. :(\n\tpmove.cmd.angles[0] = ANGLE2SHORT((pr_global_struct->input_angles)[0]);\n\tpmove.cmd.angles[1] = ANGLE2SHORT((pr_global_struct->input_angles)[1]);\n\tpmove.cmd.angles[2] = ANGLE2SHORT((pr_global_struct->input_angles)[2]);\n\tVectorCopy(pr_global_struct->input_angles, pmove.angles);\n\n\tpmove.cmd.forwardmove = bound(-32767, (pr_global_struct->input_movevalues)[0], 32767);\n\tpmove.cmd.sidemove = bound(-32767, (pr_global_struct->input_movevalues)[1], 32767);\n\tpmove.cmd.upmove = bound(-32767, (pr_global_struct->input_movevalues)[2], 32767);\n\tpmove.cmd.buttons = pr_global_struct->input_buttons;\n\n\tpmove.safeorigin_known = progstype != PROG_QW;\n\tVectorCopy(ent->v->oldorigin, pmove.safeorigin);\n\tVectorCopy(ent->v->origin, pmove.origin);\n\tVectorCopy(ent->v->velocity, pmove.velocity);\n\tVectorCopy(ent->v->maxs, pmove.player_maxs);\n\tVectorCopy(ent->v->mins, pmove.player_mins);\n\tVectorCopy(ent->xv->gravitydir, pmove.gravitydir);\n\n\t//update entity-specific stuff\n\tmovevars.entgravity = sv_gravity.value/movevars.gravity;\n\tif (ent->xv->gravity)\n\t\tmovevars.entgravity *= ent->xv->gravity;\n\tmovevars.maxspeed = ent->xv->maxspeed?ent->xv->maxspeed:sv_maxspeed.value;\n#ifdef HEXEN2\n\tif (ent->xv->hasted)\n\t\tmovevars.maxspeed *= ent->xv->hasted;\n#endif\n\tif (client)\n\t{\n\t\tmovevars.coordtype = client->netchan.netprim.coordtype;\n\t\tclient->lastruncmd = sv.time*1000;\n\t}\n\telse\n\t\tmovevars.coordtype = svs.netprim.coordtype;\n\n\tpmove.numtouch = 0;\n\tpmove.world = &sv.world;\n\tpmove.skipent = -1;\n\tpmove.numphysent = 1;\n\tpmove.physents[0].model = sv.world.worldmodel;\n\n\tpmove.onladder = false;\n\tpmove.onground = ((int)ent->v->flags & FL_ONGROUND) != 0;\n\tpmove.groundent = 0;\n\tpmove.waterlevel = 0;\n\tpmove.watertype = 0;\n\tpmove.capsule = (ent->xv->geomtype == GEOMTYPE_CAPSULE);\n\n\tfor (i=0 ; i<3 ; i++)\n\t{\n\t\tpmove_mins[i] = pmove.origin[i] - 256;\n\t\tpmove_maxs[i] = pmove.origin[i] + 256;\n\t}\n\tAddAllLinksToPmove(&sv.world, (wedict_t*)ent);\n\n\tSV_PreRunCmd();\n\n\twhile(msecs)\t//break up longer commands\n\t{\n\t\tif (msecs > 50)\n\t\t\tpmove.cmd.msec = 50;\n\t\telse\n\t\t\tpmove.cmd.msec = msecs;\n\t\tmsecs -= pmove.cmd.msec;\n\t\tPM_PlayerMove(1);\n\n\t\tif (client)\n\t\t\tclient->jump_held = pmove.jump_held;\n\t\tent->xv->pmove_flags = 0;\n\t\tent->xv->pmove_flags += ((int)pmove.jump_held?PMF_JUMP_HELD:0);\n\t\tent->xv->pmove_flags += ((int)pmove.onladder?PMF_LADDER:0);\n\t\tif (progstype != PROG_QW)\t//this is just annoying.\n\t\t\tent->v->teleport_time = sv.time + pmove.waterjumptime;\n\t\telse\n\t\t\tent->v->teleport_time = pmove.waterjumptime;\n\t\tVectorCopy(pmove.origin, ent->v->origin);\n\t\tVectorCopy(pmove.safeorigin, ent->v->oldorigin);\n\t\tVectorCopy(pmove.velocity, ent->v->velocity);\n\n\n\t\tVectorCopy(pmove.angles, ent->v->v_angle);\n\n\n\t\tent->v->waterlevel = pmove.waterlevel;\n\n\t\tif (pmove.watertype & FTECONTENTS_SOLID)\n\t\t\tent->v->watertype = Q1CONTENTS_SOLID;\n\t\telse if (pmove.watertype & FTECONTENTS_SKY)\n\t\t\tent->v->watertype = Q1CONTENTS_SKY;\n\t\telse if (pmove.watertype & FTECONTENTS_LAVA)\n\t\t\tent->v->watertype = Q1CONTENTS_LAVA;\n\t\telse if (pmove.watertype & FTECONTENTS_SLIME)\n\t\t\tent->v->watertype = Q1CONTENTS_SLIME;\n\t\telse if (pmove.watertype & FTECONTENTS_WATER)\n\t\t\tent->v->watertype = Q1CONTENTS_WATER;\n\t\telse\n\t\t\tent->v->watertype = Q1CONTENTS_EMPTY;\n\n\t\tif (pmove.onground)\n\t\t{\n\t\t\tent->v->flags = (int)ent->v->flags | FL_ONGROUND;\n\t\t\tent->v->groundentity = EDICT_TO_PROG(svprogfuncs, EDICT_NUM_PB(svprogfuncs, pmove.physents[pmove.groundent].info));\n\t\t}\n\t\telse\n\t\t\tent->v->flags = (int)ent->v->flags & ~FL_ONGROUND;\n\n\n\n\t\tWorld_LinkEdict(&sv.world, (wedict_t*)ent, true);\n\t\tfor (i=0 ; i<pmove.numtouch ; i++)\n\t\t{\n\t\t\tif (pmove.physents[pmove.touchindex[i]].notouch)\n\t\t\t\tcontinue;\n\t\t\tn = pmove.physents[pmove.touchindex[i]].info;\n\t\t\ttouched = EDICT_NUM_PB(svprogfuncs, n);\n\t\t\tif (!touched->v->touch || n >= playertouchmax || (playertouch[n/8]&(1<<(n%8))))\n\t\t\t\tcontinue;\n\n\t\t\tsv.world.Event_Touch(&sv.world, (wedict_t*)touched, (wedict_t*)ent, NULL);\n\t\t\tplayertouch[n/8] |= 1 << (n%8);\n\t\t}\n\t\tpmove.numtouch = 0;\n\t}\n}\n\nvoid SV_SetEntityButtons(edict_t *ent, unsigned int buttonbits)\n{\n\tunsigned int i;\n\textern cvar_t pr_allowbutton1;\n\n\t//set vanilla buttons\n\tent->v->button0 = buttonbits & 1;\n\tent->v->button2 = (buttonbits >> 1) & 1;\n\tif (pr_allowbutton1.ival && progstype == PROG_QW)\t//many mods use button1 - it's just a wasted field to many mods. So only work it if the cvar allows.\n\t\tent->v->button1 = ((buttonbits >> 2) & 1);\n\n\t//set extended buttons\n\tfor (i = 0; i < countof(buttonfields); i++)\n\t{\n\t\tif (buttonfields[i].offset >= 0)\n\t\t\t((float*)ent->v)[buttonfields[i].offset] = ((buttonbits >> buttonfields[i].bit)&1);\n\t}\n}\n\nvoid SV_SetSSQCInputs(usercmd_t *ucmd)\n{\n\tif (pr_global_ptrs->input_sequence)\n\t\tpr_global_struct->input_sequence = ucmd->sequence;\n\tif (pr_global_ptrs->input_timelength)\n\t\tpr_global_struct->input_timelength = ucmd->msec/1000.0f * sv.gamespeed;\n\tif (pr_global_ptrs->input_impulse)\n\t\tpr_global_struct->input_impulse = ucmd->impulse;\n//precision inaccuracies. :(\n#define ANGLE2SHORT(x) (x) * (65536/360.0)\n\tif (pr_global_ptrs->input_angles)\n\t{\n\t\t(pr_global_struct->input_angles)[0] = SHORT2ANGLE(ucmd->angles[0]);\n\t\t(pr_global_struct->input_angles)[1] = SHORT2ANGLE(ucmd->angles[1]);\n\t\t(pr_global_struct->input_angles)[2] = SHORT2ANGLE(ucmd->angles[2]);\n\t}\n\n\tif (pr_global_ptrs->input_movevalues)\n\t{\n\t\t(pr_global_struct->input_movevalues)[0] = ucmd->forwardmove;\n\t\t(pr_global_struct->input_movevalues)[1] = ucmd->sidemove;\n\t\t(pr_global_struct->input_movevalues)[2] = ucmd->upmove;\n\t}\n\tif (pr_global_ptrs->input_servertime)\n\t\tpr_global_struct->input_servertime = ucmd->fservertime;\n\tif (pr_global_ptrs->input_clienttime)\n\t\tpr_global_struct->input_clienttime = ucmd->fclienttime;\n\tif (pr_global_ptrs->input_buttons)\n\t\tpr_global_struct->input_buttons = ucmd->buttons;\n\tif (pr_global_ptrs->input_weapon)\n\t\tpr_global_struct->input_weapon = ucmd->weapon;\n\tif (pr_global_ptrs->input_lightlevel)\n\t\tpr_global_struct->input_lightlevel = ucmd->lightlevel;\n\n\tif (pr_global_ptrs->input_cursor_screen)\n\t\tVectorSet(pr_global_struct->input_cursor_screen, ucmd->cursor_screen[0], ucmd->cursor_screen[1], 0);\n\tif (pr_global_ptrs->input_cursor_trace_start)\n\t\tVectorCopy(ucmd->cursor_start, pr_global_struct->input_cursor_trace_start);\n\tif (pr_global_ptrs->input_cursor_trace_endpos)\n\t\tVectorCopy(ucmd->cursor_impact, pr_global_struct->input_cursor_trace_endpos);\n\tif (pr_global_ptrs->input_cursor_entitynumber)\n\t\tpr_global_struct->input_cursor_entitynumber = ucmd->cursor_entitynumber;\n\n\tif (pr_global_ptrs->input_head_status)\n\t\tpr_global_struct->input_head_status = ucmd->vr[VRDEV_HEAD].status;\n\tif (pr_global_ptrs->input_head_origin)\n\t\tVectorCopy(ucmd->vr[VRDEV_HEAD].origin, pr_global_struct->input_head_origin);\n\tif (pr_global_ptrs->input_head_velocity)\n\t\tVectorCopy(ucmd->vr[VRDEV_HEAD].velocity, pr_global_struct->input_head_velocity);\n\tif (pr_global_ptrs->input_head_angles)\n\t{\n\t\t(pr_global_struct->input_head_angles)[0] = SHORT2ANGLE(ucmd->vr[VRDEV_HEAD].angles[0]);\n\t\t(pr_global_struct->input_head_angles)[1] = SHORT2ANGLE(ucmd->vr[VRDEV_HEAD].angles[1]);\n\t\t(pr_global_struct->input_head_angles)[2] = SHORT2ANGLE(ucmd->vr[VRDEV_HEAD].angles[2]);\n\t}\n\tif (pr_global_ptrs->input_head_avelocity)\n\t{\n\t\t(pr_global_struct->input_head_avelocity)[0] = SHORT2ANGLE(ucmd->vr[VRDEV_HEAD].avelocity[0]);\n\t\t(pr_global_struct->input_head_avelocity)[1] = SHORT2ANGLE(ucmd->vr[VRDEV_HEAD].avelocity[1]);\n\t\t(pr_global_struct->input_head_avelocity)[2] = SHORT2ANGLE(ucmd->vr[VRDEV_HEAD].avelocity[2]);\n\t}\n\tif (pr_global_ptrs->input_head_weapon)\n\t\tpr_global_struct->input_head_weapon = ucmd->vr[VRDEV_HEAD].weapon;\n\n\tif (pr_global_ptrs->input_left_status)\n\t\tpr_global_struct->input_left_status = ucmd->vr[VRDEV_LEFT].status;\n\tif (pr_global_ptrs->input_left_origin)\n\t\tVectorCopy(ucmd->vr[VRDEV_LEFT].origin, pr_global_struct->input_left_origin);\n\tif (pr_global_ptrs->input_left_velocity)\n\t\tVectorCopy(ucmd->vr[VRDEV_LEFT].velocity, pr_global_struct->input_left_velocity);\n\tif (pr_global_ptrs->input_left_angles)\n\t{\n\t\t(pr_global_struct->input_left_angles)[0] = SHORT2ANGLE(ucmd->vr[VRDEV_LEFT].angles[0]);\n\t\t(pr_global_struct->input_left_angles)[1] = SHORT2ANGLE(ucmd->vr[VRDEV_LEFT].angles[1]);\n\t\t(pr_global_struct->input_left_angles)[2] = SHORT2ANGLE(ucmd->vr[VRDEV_LEFT].angles[2]);\n\t}\n\tif (pr_global_ptrs->input_left_avelocity)\n\t{\n\t\t(pr_global_struct->input_left_avelocity)[0] = SHORT2ANGLE(ucmd->vr[VRDEV_LEFT].avelocity[0]);\n\t\t(pr_global_struct->input_left_avelocity)[1] = SHORT2ANGLE(ucmd->vr[VRDEV_LEFT].avelocity[1]);\n\t\t(pr_global_struct->input_left_avelocity)[2] = SHORT2ANGLE(ucmd->vr[VRDEV_LEFT].avelocity[2]);\n\t}\n\tif (pr_global_ptrs->input_left_weapon)\n\t\tpr_global_struct->input_left_weapon = ucmd->vr[VRDEV_LEFT].weapon;\n\n\tif (pr_global_ptrs->input_right_status)\n\t\tpr_global_struct->input_right_status = ucmd->vr[VRDEV_RIGHT].status;\n\tif (pr_global_ptrs->input_right_origin)\n\t\tVectorCopy(ucmd->vr[VRDEV_RIGHT].origin, pr_global_struct->input_right_origin);\n\tif (pr_global_ptrs->input_right_velocity)\n\t\tVectorCopy(ucmd->vr[VRDEV_RIGHT].velocity, pr_global_struct->input_right_velocity);\n\tif (pr_global_ptrs->input_right_angles)\n\t{\n\t\t(pr_global_struct->input_right_angles)[0] = SHORT2ANGLE(ucmd->vr[VRDEV_RIGHT].angles[0]);\n\t\t(pr_global_struct->input_right_angles)[1] = SHORT2ANGLE(ucmd->vr[VRDEV_RIGHT].angles[1]);\n\t\t(pr_global_struct->input_right_angles)[2] = SHORT2ANGLE(ucmd->vr[VRDEV_RIGHT].angles[2]);\n\t}\n\tif (pr_global_ptrs->input_right_avelocity)\n\t{\n\t\t(pr_global_struct->input_right_avelocity)[0] = SHORT2ANGLE(ucmd->vr[VRDEV_RIGHT].avelocity[0]);\n\t\t(pr_global_struct->input_right_avelocity)[1] = SHORT2ANGLE(ucmd->vr[VRDEV_RIGHT].avelocity[1]);\n\t\t(pr_global_struct->input_right_avelocity)[2] = SHORT2ANGLE(ucmd->vr[VRDEV_RIGHT].avelocity[2]);\n\t}\n\tif (pr_global_ptrs->input_right_weapon)\n\t\tpr_global_struct->input_right_weapon = ucmd->vr[VRDEV_RIGHT].weapon;\n}\n\n//EXT_CSQC_1 (called when a movement command is received. runs full acceleration + movement)\nqboolean SV_RunFullQCMovement(client_t *client, usercmd_t *ucmd)\n{\n\tif (ucmd->vr[VRDEV_HEAD].status & VRSTATUS_ANG)\n\t\tsv_player->xv->idealpitch = SHORT2ANGLE(ucmd->vr[VRDEV_HEAD].angles[0]);\n\telse\n\t\tsv_player->xv->idealpitch = 0;\n\tSV_SetSSQCInputs(ucmd);\t//make sure its available for PlayerPreThink.\n\tif (gfuncs.RunClientCommand)\n\t{\n\t\tvec3_t startangle;\n#ifdef SVCHAT\n\t\tif (SV_ChatMove(ucmd->impulse))\n\t\t{\n\t\t\tucmd->buttons = 0;\n\t\t\tucmd->impulse = 0;\n\t\t\tucmd->forwardmove = ucmd->sidemove = ucmd->upmove = 0;\n\t\t}\n#endif\n\n\t\tif (host_client->state && host_client->protocol != SCP_BAD)\n\t\t{\n\t\t\tif (!sv_player->v->fixangle)\n\t\t\t{\n\t\t\t\tsv_player->v->v_angle[0] = SHORT2ANGLE(ucmd->angles[0]);\n\t\t\t\tsv_player->v->v_angle[1] = SHORT2ANGLE(ucmd->angles[1]);\n\t\t\t\tsv_player->v->v_angle[2] = SHORT2ANGLE(ucmd->angles[2]);\n\t\t\t}\n\t\t\tsv_player->xv->movement[0] = ucmd->forwardmove;\n\t\t\tsv_player->xv->movement[1] = ucmd->sidemove;\n\t\t\tsv_player->xv->movement[2] = ucmd->upmove;\n\t\t}\n\t\tVectorCopy(sv_player->v->v_angle, startangle);\n\n#ifdef HEXEN2\n\t\tif (progstype == PROG_H2)\n\t\t\tsv_player->xv->light_level = 128;\t//hmm... HACK!!!\n#endif\n\n\t\tSV_SetEntityButtons(sv_player, ucmd->buttons);\n\t\tif (ucmd->impulse && SV_FilterImpulse(ucmd->impulse, host_client->trustlevel))\n\t\t\tsv_player->v->impulse = ucmd->impulse;\n\n\t\tif (host_client->penalties & BAN_CUFF)\n\t\t{\n\t\t\tsv_player->v->impulse = 0;\n\t\t\tsv_player->v->button0 = 0;\n\t\t}\n\n\t\tWPhys_CheckVelocity(&sv.world, (wedict_t*)sv_player);\n\n\t//\n\t// angles\n\t// show 1/3 the pitch angle and all the roll angle\n\t\tif (sv_player->v->health > 0)\n\t\t{\n\t\t\tif (!sv_player->v->fixangle)\n\t\t\t{\n\t\t\t\tsv_player->v->angles[PITCH] = r_meshpitch.value * sv_player->v->v_angle[PITCH]/3;\n\t\t\t\tsv_player->v->angles[YAW] = sv_player->v->v_angle[YAW];\n\t\t\t}\n\t\t\tsv_player->v->angles[ROLL] =\n\t\t\t\tV_CalcRoll (sv_player->v->angles, sv_player->v->velocity)*4;\n\t\t}\n\n\t\t//prethink should be consistant with what the engine normally does\n\t\tpr_global_struct->time = sv.time;\n\t\tpr_global_struct->self = EDICT_TO_PROG(svprogfuncs, client->edict);\n\t\tPR_ExecuteProgram (svprogfuncs, pr_global_struct->PlayerPreThink);\n\t\tWPhys_RunThink (&sv.world, (wedict_t*)client->edict);\n\n\t\tpr_global_struct->self = EDICT_TO_PROG(svprogfuncs, client->edict);\n\t\tPR_ExecuteProgram(svprogfuncs, gfuncs.RunClientCommand);\n\n\n\t\tif (!sv_player->v->fixangle && client->protocol != SCP_BAD)\n\t\t{\n\t\t\tint i;\n\t\t\tvec3_t delta;\n\t\t\tVectorSubtract (sv_player->v->v_angle, startangle, delta);\n\n\t\t\tif (delta[0] || delta[1] || delta[2])\n\t\t\t{\n\t\t\t\t//eular angle changes suck\n\t\t\t\tif (client->fteprotocolextensions2 & PEXT2_SETANGLEDELTA)\n\t\t\t\t{\n\t\t\t\t\tclient_t *cl = ClientReliableWrite_BeginSplit(client, svcfte_setangledelta, 7);\n\t\t\t\t\tfor (i=0 ; i < 3 ; i++)\n\t\t\t\t\t\tClientReliableWrite_Angle16 (cl, delta[i]);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tclient_t *cl = ClientReliableWrite_BeginSplit(client, svc_setangle, 7);\n\t\t\t\t\tfor (i=0 ; i < 3 ; i++)\n\t\t\t\t\t\tClientReliableWrite_Angle (cl, sv_player->v->v_angle[i]);\n\t\t\t\t}\n\t\t\t}\n\n\t\t}\n \t\treturn true;\n\t}\n\treturn false;\n}\n\n//entity(string match [, float matchnum]) matchclient = #241;\nstatic void QCBUILTIN PF_matchclient(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint clnum=-1;\n\tconst char *name = PR_GetStringOfs(prinst, OFS_PARM0);\n\tint matchnum = G_FLOAT(OFS_PARM1);\n\tclient_t *cl;\n\n\tif (prinst->callargc < 2)\n\t{\n\t\tcl = SV_GetClientForString(name, &clnum);\n\t\tif (!cl)\n\t\t\tG_INT(OFS_RETURN) = 0;\n\t\telse\n\t\t\tG_INT(OFS_RETURN) = (cl - svs.clients) + 1;\n\n\t\tif ((cl = SV_GetClientForString(name, &clnum)))\n\t\t\tG_INT(OFS_RETURN) = 0;\t//prevent multiple matches.\n\t\treturn;\n\t}\n\n\twhile((cl = SV_GetClientForString(name, &clnum)))\n\t{\n\t\tif (!matchnum)\n\t\t{\t//this is the one that matches\n\t\t\tG_INT(OFS_RETURN) = (cl - svs.clients) + 1;\n\t\t\treturn;\n\t\t}\n\t\tmatchnum--;\n\t}\n\n\tG_INT(OFS_RETURN) = 0;\t//world\n}\n\nstatic void QCBUILTIN PF_SendPacket(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tnetadr_t to;\n\tconst char *address = PR_GetStringOfs(prinst, OFS_PARM0);\n\tconst char *contents = PF_VarString(prinst, 1, pr_globals);\n\n\tif (NET_StringToAdr(address, 0, &to))\n\t{\n\t\tchar *send = Z_Malloc(4+strlen(contents));\n\t\tsend[0] = send[1] = send[2] = send[3] = 0xff;\n\t\tmemcpy(send+4, contents, strlen(contents));\n\t\tG_FLOAT(OFS_RETURN) = NET_SendPacket(svs.sockets, 4+strlen(contents), send, &to);\n\t\tZ_Free(send);\n\t}\n}\n\n//be careful to not touch the resource unless we're meant to, to avoid stalling\nstatic void QCBUILTIN PF_resourcestatus(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint restype = G_FLOAT(OFS_PARM0);\n\tint doload = G_FLOAT(OFS_PARM1);\n\tconst char *resname = PR_GetStringOfs(prinst, OFS_PARM2);\n\tint idx;\n\tG_FLOAT(OFS_RETURN) = RESSTATE_NOTKNOWN;\n\tswitch(restype)\n\t{\n\tcase RESTYPE_MODEL:\n\t\tfor (idx=1 ; idx<MAX_PRECACHE_MODELS && sv.strings.model_precache[idx] ; idx++)\n\t\t{\n\t\t\tif (!strcmp(sv.strings.model_precache[idx], resname))\n\t\t\t{\n\t\t\t\tmodel_t *mod = sv.models[idx];\n\t\t\t\tif (!mod && doload)\n\t\t\t\t\tmod = sv.models[idx] = Mod_FindName(Mod_FixName(resname, sv.modelname));\n\t\t\t\tif (!mod)\n\t\t\t\t\tG_FLOAT(OFS_RETURN) = RESSTATE_NOTLOADED;\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tif (doload && mod->loadstate == MLS_NOTLOADED)\n\t\t\t\t\t\tMod_LoadModel (mod, MLV_SILENT);\t//should avoid blocking.\n\t\t\t\t\tswitch(mod->loadstate)\n\t\t\t\t\t{\n\t\t\t\t\tdefault:\n\t\t\t\t\tcase MLS_NOTLOADED:\n\t\t\t\t\t\tG_FLOAT(OFS_RETURN) = RESSTATE_NOTLOADED;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase MLS_LOADING:\n\t\t\t\t\t\tG_FLOAT(OFS_RETURN) = RESSTATE_LOADING;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase MLS_LOADED:\n\t\t\t\t\t\tG_FLOAT(OFS_RETURN) = RESSTATE_LOADED;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase MLS_FAILED:\n\t\t\t\t\t\tG_FLOAT(OFS_RETURN) = RESSTATE_FAILED;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tbreak;\n\tcase RESTYPE_SOUND:\n\t\tfor (idx=1 ; idx<MAX_PRECACHE_SOUNDS && sv.strings.sound_precache[idx] ; idx++)\n\t\t{\n\t\t\tif (!strcmp(sv.strings.sound_precache[idx], resname))\n\t\t\t{\n\t\t\t\tG_FLOAT(OFS_RETURN) = RESSTATE_NOTLOADED;\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tbreak;\n\tcase RESTYPE_PARTICLE:\n\tcase RESTYPE_SHADER:\n\tcase RESTYPE_SKIN:\n\tcase RESTYPE_TEXTURE:\n\t\t//FIXME\n\tdefault:\n\t\tG_FLOAT(OFS_RETURN) = -1;\n\t\tbreak;\n\t}\n}\n\nstatic void QCBUILTIN PF_clusterevent(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n#ifdef SUBSERVERS\n\tconst char *dest = PR_GetStringOfs(prinst, OFS_PARM0);\n\tconst char *src = PR_GetStringOfs(prinst, OFS_PARM1);\n\tconst char *cmd = PR_GetStringOfs(prinst, OFS_PARM2);\n\tconst char *info = PF_VarString(prinst, 3, pr_globals);\n\tSSV_Send(dest, src, cmd, info);\n#endif\n}\nstatic void QCBUILTIN PF_clustertransfer(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n#ifdef SUBSERVERS\n\tint p = G_EDICT(prinst, OFS_PARM0)->entnum - 1;\n\tconst char *dest = (prinst->callargc >= 2)?PR_GetStringOfs(prinst, OFS_PARM1):NULL;\n\n\tG_INT(OFS_RETURN) = 0;\n\n\tif (p < 0 || p >= sv.allocated_client_slots)\n\t{\n\t\tPR_BIError (prinst, \"PF_clustertransfer: not a player\\n\");\n\t\treturn;\n\t}\n\n\tif (dest)\n\t{\n\t\t//FIXME: allow cluster transfers even when not a cluster node ourselves. \n\t\tif (!SSV_IsSubServer() && !isDedicated)\n\t\t{\n\t\t\tCon_DPrintf(\"PF_clustertransfer: not running in mapcluster mode, ignoring transfer to %s\\n\", dest);\n\t\t\treturn;\n\t\t}\n\t\tif (svs.clients[p].transfer)\n\t\t{\n\t\t\tCon_DPrintf(\"PF_clustertransfer: Already transferring to %s, ignoring transfer to %s\\n\", svs.clients[p].transfer, dest);\n\t\t\treturn;\n\t\t}\n\t\tsvs.clients[p].transfer = Z_StrDup(dest);\n\t\tSSV_InitiatePlayerTransfer(&svs.clients[p], svs.clients[p].transfer);\n\t}\n\n\tif (svs.clients[p].transfer)\n\t\tRETURN_TSTRING(svs.clients[p].transfer);\n#else\n\tG_INT(OFS_RETURN) = 0;\n#endif\n}\n\nstatic void QCBUILTIN PF_setpause(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint pause = (G_FLOAT(OFS_PARM0)?PAUSE_EXPLICIT:0) | (sv.paused&~PAUSE_EXPLICIT);\n\tG_FLOAT(OFS_RETURN) = !!(sv.paused&PAUSE_EXPLICIT);\n\tif (sv.paused != pause)\n\t{\n//\t\tif (pause&PAUSE_EXPLICIT)\n//\t\t\tSV_BroadcastTPrintf (PRINT_HIGH, \"game paused\\n\");\n//\t\telse\n//\t\t\tSV_BroadcastTPrintf (PRINT_HIGH, \"game unpaused\\n\");\n\n\t\tsv.paused = pause;\n\t\tsv.pausedstart = Sys_DoubleTime();\n\t}\n}\n\n/*builtins to work around the remastered edition of quake.*/\nstatic void QCBUILTIN PF_finaleFinished_qex(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\t//undocumented. implement some quicky hack that just waits till some user has tried attacking. should probably be at least half players or something. silly afkers.\n\tunsigned int i;\n\tG_FLOAT(OFS_RETURN) = false;\n\tfor (i = 0; i < svs.allocated_client_slots; i++)\n\t{\n\t\tif (svs.clients[i].lastcmd.buttons & 1)\n\t\t\tG_FLOAT(OFS_RETURN) = true;\n\t}\n}\nstatic void QCBUILTIN PF_localsound_qex(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\t//localsound(player, \"foo.wav\") - plays the sound with no attenuation to only that specific player. to be paired with centerprints.\n\tint oldmsgent = pr_global_struct->msg_entity;\n\tedict_t *ent = G_EDICT(prinst, OFS_PARM0);\n\tconst char *sample = PR_GetStringOfs(prinst, OFS_PARM1);\n\tint entnum = NUM_FOR_EDICT(prinst, ent);\n\tif (entnum < 1 || entnum > sv.allocated_client_slots)\n\t{\n\t\tPR_RunWarning(sv.world.progs, \"tried to localsound to a non-client\\n\");\n\t\treturn;\n\t}\n\tif (svs.clients[entnum-1].state < cs_connected)\n\t\treturn;\t//bad caller!\n\n\tpr_global_struct->msg_entity = G_INT(OFS_PARM0);\t//required for CF_SV_UNICAST\n\tSV_StartSound(NUM_FOR_EDICT(svprogfuncs, ent), ent->v->origin, vec3_origin, ent->xv->dimension_seen, CHAN_AUTO, sample, 1/*vol*/, 0/*attn*/, 1/*rate*/, 0/*ofs*/, CF_SV_UNICAST|CF_SV_RELIABLE|CF_NOREVERB|CF_NOSPACIALISE);\n\tpr_global_struct->msg_entity = oldmsgent; //don't break stuff.\n\n}\nstatic void QCBUILTIN PF_CheckPlayerEXFlags_qex(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint entnum = G_EDICTNUM(prinst, OFS_PARM0);\n\tunsigned int flags = 0;\n\n\tif (entnum >= 1 && entnum <= sv.allocated_client_slots)\n\t{\n\t\tclient_t *pl = &svs.clients[entnum-1];\n\t\tint ival;\n\t\tchar *value = InfoBuf_ValueForKey (&pl->userinfo, \"w_switch\");\n\t\tif (!*value)\n\t\t\tvalue = InfoBuf_ValueForKey (&pl->userinfo, \"b_switch\");\n\t\tival = atoi(value);\n\n\t\t//if (!ival) ival = 8; //QW's logic...\n\n\t\t/*this is ugly. QW has a 'w_switch' userinfo that says the user's best weapon. As a general rule, setting w_switch to 1(axe) will disable switching up on weapon grabs.\n\t\t  QE does stuff differently though, so all we can really go by is w_switch 1 (vs >1).\n\t\t  */\n\t\tif (!ival)\t//unspecified\n\t\t\tflags |= 1/*PEF_CHANGEONLYNEW*/;\n\t\telse if (ival == 1)\t//user will not 'upgrade' past axe.\n\t\t\tflags |= 2/*PEF_CHANGENEVER*/;\n\t\t//else //\n\t}\n\n\tG_FLOAT(OFS_RETURN) = flags;\n}\nstatic void QCBUILTIN PF_walkpathtogoal_qex(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\t//this builtin is supposed to provide more advanced routing.\n\t//the possible return values include an 'inprogress' which is kinda vague and implies persistent state.\n\t//we can get away with returning false as a failure here, equivelent to if the map has no waypoints defined. the gamecode is expected to then fall back on the really lame movetogoal builtin.\n\tG_FLOAT(OFS_RETURN) = false;\n}\nstatic void QCBUILTIN PF_centerprint_qex(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\t//TODO: send the strings to the client for localisation+reordering\n\tconst char *arg[8];\n\tint args;\n\tchar s[1024];\n\tint\t\t\tentnum;\n\tint lang = com_language;\n\n\tentnum = G_EDICTNUM(prinst, OFS_PARM0);\n\tfor (args = 0; args+1 < prinst->callargc; args++)\n\t\targ[args] = PR_GetStringOfs(prinst, OFS_PARM1+args*(OFS_PARM1-OFS_PARM0));\n\n\tif (entnum >= 1 && entnum <= sv.allocated_client_slots)\n\t\tlang = svs.clients[entnum-1].language;\n\tTL_Reformat(lang, s, sizeof(s), args, arg);\n\n\tPF_centerprint_Internal(entnum, false, s);\n}\nstatic void QCBUILTIN PF_sprint_qex(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tconst char *arg[8];\n\tint args;\n\tchar s[1024];\n\tclient_t\t*client;\n\tint\t\t\tentnum;\n\tint\t\t\tlevel;\n\n#ifdef SERVER_DEMO_PLAYBACK\n\tif (sv.demofile)\n\t\treturn;\n#endif\n\n\tentnum = G_EDICTNUM(prinst, OFS_PARM0);\n\n\tif (progstype == PROG_NQ || progstype == PROG_H2)\n\t{\n\t\tlevel = PRINT_HIGH;\n\t\tfor (args = 0; args+1 < prinst->callargc; args++)\n\t\t\targ[args] = PR_GetStringOfs(prinst, OFS_PARM1+args*(OFS_PARM1-OFS_PARM0));\n\t}\n\telse\n\t{\n\t\tlevel = G_FLOAT(OFS_PARM1);\n\t\tfor (args = 0; args+2 < prinst->callargc; args++)\n\t\t\targ[args] = PR_GetStringOfs(prinst, OFS_PARM2+args*(OFS_PARM1-OFS_PARM0));\n\t}\n\n\tif (entnum < 1 || entnum > sv.allocated_client_slots)\n\t{\n\t\tCon_TPrintf (\"tried to sprint to a non-client\\n\");\n\t\treturn;\n\t}\n\n\tclient = &svs.clients[entnum-1];\n\n\tTL_Reformat(client->language, s, sizeof(s), args, arg);\n\tSV_ClientPrintf (client, level, \"%s\", s);\n\n\tif (sv_specprint.ival & SPECPRINT_SPRINT)\n\t{\n\t\tclient_t *spec;\n\t\tunsigned int i;\n\t\tfor (i = 0, spec = svs.clients; i < sv.allocated_client_slots; i++, spec++)\n\t\t{\n\t\t\tif (spec->state != cs_spawned || !spec->spectator)\n\t\t\t\tcontinue;\n\t\t\tif (spec->spec_track == entnum && (spec->spec_print & SPECPRINT_SPRINT))\n\t\t\t{\n\t\t\t\tif (level < spec->messagelevel)\n\t\t\t\t\tcontinue;\n\t\t\t\tif (spec->controller)\n\t\t\t\t\tSV_PrintToClient(spec->controller, level, s);\n\t\t\t\telse\n\t\t\t\t\tSV_PrintToClient(spec, level, s);\n\t\t\t}\n\t\t}\n\t}\n}\nstatic void QCBUILTIN PF_bprint_qex(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\t//TODO: send the strings to the client for localisation+reordering\n\tconst char *arg[8];\n\tint args;\n\tint level;\n\n\tif (progstype == PROG_QW)\n\t{\n\t\tlevel = G_FLOAT(OFS_PARM0);\n\t\tfor (args = 0; args+1 < prinst->callargc; args++)\n\t\t\targ[args] = PR_GetStringOfs(prinst, OFS_PARM1+args*(OFS_PARM1-OFS_PARM0));\n\t}\n\telse\n\t{\n\t\tlevel = PRINT_HIGH;\n\t\tfor (args = 0; args < prinst->callargc; args++)\n\t\t\targ[args] = PR_GetStringOfs(prinst, OFS_PARM0+args*(OFS_PARM1-OFS_PARM0));\n\t}\n\n\tSV_BroadcastPrint_QexLoc (0, level, arg, args);\n}\n\nvoid SV_Prompt_Input(client_t *cl, usercmd_t *ucmd)\n{\n\tif (cl->prompt.active && !cl->qex)\n\t{\t//with this serverside, we don't get autorepeat etc.\n\t\t//we also can't prevent movement beyond the menu closure.\n\t\tif (ucmd->forwardmove >= 100 && cl->prompt.oldmove[0] < 100)\n\t\t{\t//up\n\t\t\tif (cl->prompt.selected > 0)\n\t\t\t\tcl->prompt.selected--;\n\t\t\telse if (cl->prompt.numopts)\n\t\t\t\tcl->prompt.selected = cl->prompt.numopts-1;\t//wrap.\n\n\t\t\tcl->prompt.nextsend = realtime;\n\t\t}\n\t\telse if (ucmd->forwardmove <= -100 && cl->prompt.oldmove[0] > -100)\n\t\t{\t//down\n\t\t\tcl->prompt.selected++;\n\t\t\tif (cl->prompt.selected >= cl->prompt.numopts)\n\t\t\t\tcl->prompt.selected = 0;\t//wrap.\n\t\t\tcl->prompt.nextsend = realtime;\n\t\t}\n\t\telse if (ucmd->sidemove >= 100 && cl->prompt.oldmove[1] < 100)\n\t\t{\t//right (use)\n\t\t\tif (cl->prompt.selected < cl->prompt.numopts)\n\t\t\t\tcl->edict->v->impulse = cl->prompt.opt[cl->prompt.selected].impulse;\n\t\t}\n\t\tcl->prompt.oldmove[0] = ucmd->forwardmove;\n\t\tcl->prompt.oldmove[1] = ucmd->sidemove;\n\t\tucmd->forwardmove = 0;\n\t\tucmd->sidemove = 0;\n\t}\n\telse\n\t{\n\t\tcl->prompt.oldmove[0] = ucmd->forwardmove;\n\t\tcl->prompt.oldmove[1] = ucmd->sidemove;\n\t}\n}\nvoid SV_Prompt_Resend(client_t *client)\n{\n\tZ_Free(client->centerprintstring);\n\tclient->centerprintstring = NULL;\n\n\tif (client->prompt.nextsend > realtime)\n\t\treturn;\t//still good.\n\n\tif (client->qex)\n\t{\t//can depend on the client doing any translation stuff.\n\t\tsize_t sz;\n\t\tsizebuf_t *msg;\n\t\tsize_t i;\n\n\t\tsz = 2;\n\t\tif (client->prompt.numopts)\n\t\t{\n\t\t\tsz += strlen(client->prompt.header)+1;\n\t\t\tfor (i = 0; i < client->prompt.numopts; i++)\n\t\t\t\tsz += strlen(client->prompt.opt[i].text)+2;\n\t\t}\n\n\t\tmsg = ClientReliable_StartWrite(client, sz);\n\n\t\tMSG_WriteByte(msg, svcqex_prompt);\n\t\tMSG_WriteByte(msg, client->prompt.numopts);\n\t\tif (client->prompt.numopts)\n\t\t{\n\t\t\tMSG_WriteString(msg, client->prompt.header);\n\t\t\tfor (i = 0; i < client->prompt.numopts; i++)\n\t\t\t{\n\t\t\t\tMSG_WriteString(msg, client->prompt.opt[i].text);\n\t\t\t\tMSG_WriteByte(msg, client->prompt.opt[i].impulse);\n\t\t\t}\n\t\t}\n\n\t\tClientReliable_FinishWrite(client);\n\n\t\tclient->prompt.nextsend = DBL_MAX;\t//don't let it time out.\n\t}\n\telse\n\t{\t//emulate via centerprints\n\t\tconst char *txt[4];\n\t\tsize_t i;\n\t\tclient->prompt.nextsend = realtime + 1;\t//don't let it time out.\n\n\t\tif (client->prompt.numopts)\n\t\t{\n\t\t\ttxt[0] = client->prompt.header?client->prompt.header:\"\";\n\t\t\tfor (i = 0; i < 3; i++)\n\t\t\t{\n\t\t\t\tif (client->prompt.selected + i - 1u < client->prompt.numopts)\n\t\t\t\t\ttxt[1+i] = client->prompt.opt[client->prompt.selected + i - 1u].text;\n\t\t\t\telse\n\t\t\t\t\ttxt[1+i] = \" \";\n\t\t\t}\n\n\t\t\t//need to translate it too.\n\t\t\tfor (i = 0; i < countof(txt); i++)\n\t\t\t\ttxt[i] = TL_Translate(client->language, txt[i]);\n\n\t\t\tclient->centerprintstring = va(\"%s\\n%s\\n^a[[ %s ]]^a\\n%s\", txt[0], txt[1], txt[2], txt[3]);\n\t\t}\n\t\telse\n\t\t\tclient->centerprintstring = client->prompt.header?client->prompt.header:\"\";\n\t\tclient->centerprintstring = Z_StrDup(client->centerprintstring);\n\t}\n}\nvoid SV_Prompt_Clear(client_t *cl)\n{\n\tint i;\n\tcl->prompt.active = false;\n\tZ_Free(cl->prompt.header);\n\tcl->prompt.header = NULL;\n\tcl->prompt.selected = 0;\n\tcl->prompt.nextsend = realtime;\n\n\tfor (i = 0; i < cl->prompt.numopts; i++)\n\t\tZ_Free(cl->prompt.opt[i].text);\n\tcl->prompt.numopts = cl->prompt.maxopts = 0;\n\tZ_Free(cl->prompt.opt);\n\tcl->prompt.opt = NULL;\n}\nstatic void QCBUILTIN PF_prompt_qex(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint p = G_EDICT(prinst, OFS_PARM0)->entnum - 1;\n\tclient_t *cl;\n\tconst char *text = (prinst->callargc >= 2)?PR_GetStringOfs(prinst, OFS_PARM1):NULL;\n\tunsigned int opts = (prinst->callargc >= 3)?G_FLOAT(OFS_PARM2):0;\n\tif (p < 0 || p >= sv.allocated_client_slots)\n\t{\n\t\tPR_BIError (prinst, \"PF_clearprompt_qex: not a player\\n\");\n\t\treturn;\n\t}\n\tcl = &svs.clients[p];\n\n\tSV_Prompt_Clear(cl);\n\tif (!text)\n\t{\n\t\tSV_Prompt_Resend(cl);\n\t\treturn;\n\t}\n\tcl->prompt.active = true;\n\tcl->prompt.maxopts = opts;\n\tcl->prompt.numopts = 0;\n\tZ_StrDupPtr(&cl->prompt.header, text);\n\tcl->prompt.opt = Z_Malloc(sizeof(*cl->prompt.opt) * cl->prompt.maxopts);\n}\nstatic void QCBUILTIN PF_promptchoice_qex(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint p = G_EDICT(prinst, OFS_PARM0)->entnum - 1;\n\tclient_t *cl;\n\tconst char *text = PR_GetStringOfs(prinst, OFS_PARM1);\n\tint impulse = G_FLOAT(OFS_PARM2);\n\tsize_t opt;\n\tif (p < 0 || p >= sv.allocated_client_slots)\n\t{\n\t\tPR_BIError (prinst, \"PF_clearprompt_qex: not a player\\n\");\n\t\treturn;\n\t}\n\tcl = &svs.clients[p];\n\tif (!cl->prompt.active)\n\t{\n\t\tPR_BIError (prinst, \"PF_clearprompt_qex: too many options\\n\");\n\t\treturn;\n\t}\n\n\topt = cl->prompt.numopts++;\n\tif (opt >= cl->prompt.maxopts)\n\t\tZ_ReallocElements((void**)&cl->prompt.opt, &cl->prompt.maxopts, opt+1, sizeof(*cl->prompt.opt));\n\tZ_StrDupPtr(&cl->prompt.opt[opt].text, text);\n\tcl->prompt.opt[opt].impulse = impulse;\n}\n\n#define STUB ,NULL,true\n#if defined(DEBUG) || defined(_DEBUG)\n#define NYI\n#else\n#define NYI ,true\n#endif\n#ifdef NOQCDESCRIPTIONS\n\t#define D(p,d) NULL,NULL\n#else\n\t#define D(p,d) p,d\n#endif\nstatic BuiltinList_t BuiltinList[] = {\t\t\t\t//nq\tqw\t\th2\t\tebfs\n\t{\"fixme\",\t\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t0,\tD(\"void()\", \"Some builtin that should never be called. Ends the game with some weird message.\")},\n\n#ifndef SERVERONLY\n\t//begin menu-only 'standard'\n\t{\"checkextension\",\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t1,\tD(\"float(string ext)\", \"Checks if the named extension is supported by the running engine.\")},\n\t{\"error\",\t\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t2,\tD(\"void(string err,...)\", \"Fatal error that will trigger a crash-to-console that users will actually notice.\")},\n\t{\"objerror\",\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t3,\tD(\"void(string err,...)\", \"For some reason this has been redefined as non-fatal, and as it won't force the user to look at the console it'll generally be ignored completely so really what's the point? Other than as a convoluted way to remove(self) that is.\")},\n\t{\"print\",\t\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t4,\tD(\"void(string text,...)\", \"Hello, world. Shoves junk on the console. Hopefully people will bother to read it, maybe.\")},\n\t{\"bprint\",\t\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t5,\t\"DEP void(string text,...)\"},\n\t{\"msprint\",\t\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t6,\t\"DEP void(float clientnum, string text,...)\"},\n\t{\"cprint\",\t\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t7,\tD(\"void(string text,...)\", \"Tries to show the given message in the centre of the screen, assuming that its not obscured by menus. Oh hey look, you're calling it in menuqc!\")},\n\t{\"normalize\",\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t8,\t\"vector(vector)\"},\n\t{\"vlen\",\t\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t9,\t\"float(vector)\"},\n\t{\"vectoyaw\",\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t10,\t\"float(vector)\"},\n\t{\"vectoangles\",\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t11,\t\"vector(vector)\"},\n\t{\"random\",\t\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t12,\t\"float()\"},\n\t{\"localcmd\",\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t13,\t\"void(string,...)\"},\n\t{\"cvar\",\t\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t14,\t\"float(string name)\"},\n\t{\"cvar_set\",\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t15,\t\"void(string name, string value)\"},\n\t{\"dprint\",\t\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t16,\t\"void(string text, ...)\"},\n\t{\"ftos\",\t\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t17,\t\"string(float)\"},\n\t{\"fabs\",\t\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t18,\t\"float(float)\"},\n\t{\"vtos\",\t\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t19,\t\"string(vector)\"},\n\t{\"etos\",\t\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t20,\t\"string(entity)\"},\n\t{\"stof\",\t\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t21,\t\"float(string)\"},\n\t{\"spawn\",\t\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t22,\t\"entity()\"},\n\t{\"remove\",\t\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t23,\t\"void(entity)\"},\n\t{\"find\",\t\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t24,\t\"entity(entity start, .string field, string match)\"},\n\t{\"findfloat\",\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t25,\t\"entity(entity start, .__variant field, __variant match)\"},\n\t{\"findchain\",\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t26,\t\"entity(.string field, string match)\"},\n\t{\"findchainfloat\",\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t27,\t\"entity(.__variant field, __variant match)\"},\n\t{\"precache_file\",\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t28,\tD(\"string(string file)\", \"Attempts to download the named file from the current server, if it isn't found locally. Not very useful as menuqc is normally meant to work before joining servers too.\")},\n\t{\"precache_sound\",\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t29,\t\"string(string sample)\"},\n\t{\"coredump\",\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t30,\tD(\"void()\", \"Takes a dump, writing the qcvm's state to disk. There are normally easier ways to debug, but I suppose this one still beats print spam.\")},\n\t{\"traceon\",\t\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t31,\tD(\"void()\", \"Enables single-stepping. Its generally easier to just set a breakpoint.\")},\n\t{\"traceoff\",\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t32,\tD(\"void()\", \"Disables single-stepping. Which sucks if you started said singlestepping outside of qc.\")},\n\t{\"eprint\",\t\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t33,\t\"void(entity)\"},\n\t{\"rint\",\t\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t34,\t\"float(float)\"},\n\t{\"floor\",\t\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t35,\t\"float(float)\"},\n\t{\"ceil\",\t\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t36,\t\"float(float)\"},\n\t{\"nextent\",\t\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t37,\t\"entity(entity)\"},\n\t{\"sin\",\t\t\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t38,\t\"float(float)\"},\n\t{\"cos\",\t\t\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t39,\t\"float(float)\"},\n\t{\"sqrt\",\t\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t40,\t\"float(float)\"},\n\t{\"randomvec\",\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t41,\tD(\"vector()\", \"Returns a random vector with length <= 1 (each axis may be negative).\")},\n\t{\"registercvar\",\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t42,\tD(\"float(string name, string value, optional float flags)\", \"Creates the cvar if it didn't already exist. This presents issues for setting those cvars via startup configs of course, and autocvars are easier but I suppose they don't get any flags (which are ignored anyway, of course).\")},\n\t{\"min\",\t\t\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t43,\t\"float(float,...)\"},\n\t{\"max\",\t\t\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t44,\t\"float(float,...)\"},\n\t{\"bound\",\t\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t45,\t\"float(float min,float value,float max)\"},\n\t{\"pow\",\t\t\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t46,\t\"float(float,float)\"},\n\t{\"copyentity\",\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t47,\tD(\"void(entity src, entity dst)\", \"Copies all entity fields from one entity into another (forgetting any that were previously set on the destination).\")},\n\t{\"fopen\",\t\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t48,\t\"filestream(string filename, float mode)\"},\n\t{\"fclose\",\t\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t49,\t\"void(filestream fhandle)\"},\n\t{\"fgets\",\t\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t50,\t\"string(filestream fhandle)\"},\n\t{\"fputs\",\t\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t51,\t\"void(filestream fhandle, string s)\"},\n\t{\"strlen\",\t\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t52,\t\"float(string)\"},\n\t{\"strcat\",\t\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t53,\t\"string(string, optional string, optional string, optional string, optional string, optional string, optional string, optional string)\"},\n\t{\"substring\",\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t54,\t\"string(string s, float start, float length)\"},\n\t{\"stov\",\t\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t55,\t\"vector(string)\"},\n\t{\"strzone\",\t\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t56,\tD(\"FTEDEP(\\\"Redundant\\\") string(string)\", \"Exists in FTE for compat only, no different from strcat.\")},\n\t{\"strunzone\",\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t57,\tD(\"FTEDEP(\\\"Redundant\\\") void(string)\", \"Exists in FTE for compat only, does nothing.\")},\n\t{\"tokenize\",\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t58,\tD(\"float(string)\", \"Splits up the given string into its different components (what constitutes a token separator is not well defined and has been hacked about with over the years so have fun with that), returning the number of tokens that were found. Call argv(0 through ret-1) to retrieve each individual token. Take care to not use this recursively.\")},\n\t{\"argv\",\t\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t59,\tD(\"string(float)\", \"Returns one of the tokens found via tokenize (and equivelent builtins).\")},\n\t{\"isserver\",\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t60,\tD(\"float()\", \"Returns true if the local engine is running a server, and thus cvars and localcmds are shared with said server.\")},\n\t{\"clientcount\",\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t61,\tD(\"float()\", \"Returns the maximum number of players on the server. Useless if its a remote server, so its a kinda useless builtin really.\")},\n\t{\"clientstate\",\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t62,\tD(\"float()\", \"Tells you whether the client is actually connected to anything. 0 for a dedicated server (but dedicated servers don't normally run menuqc anyway), 2 if connecting or connected to a server (but not necessarily spawned+active), 1 for sitting around idle without trying to connect to anything yet.\")},\n\t{\"clientcommand\",\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t63,\tD(\"void(float client, string s)\", \"Fakes a 'cmd foo' message from the specified player to the local server, and also bypasses ssqc handling of it greatly limiting the use of this builtin.\"), true},\n\t{\"changelevel\",\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t64,\tD(\"void(string map)\", \"Not really any different from a localcmd, but with proper string escapes.\")},\n\t{\"localsound\",\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t65,\tD(\"void(string sample, optional float channel, optional float volume)\", \"Plays a sound, locally. precaching is optional, but recommended.\")},\n\t{\"getmousepos\",\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t66,\tD(\"vector()\", \"Obsolete. Return values depend upon the current cursor mode. Implement Menu_InputEvent instead, so you can handle deltas as-is or absolutes if that's all the OS can provide.\")},\n\t{\"gettime\",\t\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t67,\t\"float(optional float timetype)\"},\n\t{\"gettimed\",\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t0,\t\"__double(optional int timetype)\"},\n\t{\"loadfromdata\",\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t68,\tD(\"void(string s)\", \"Reads a set of entities from the given string. This string should have the same format as a .ent file or a saved game. Entities will be spawned as required. If you need to see the entities that were created, you should use parseentitydata instead.\")},\n\t{\"loadfromfile\",\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t69,\tD(\"void(string s)\", \"Reads a set of entities from the named file. This file should have the same format as a .ent file or a saved game. Entities will be spawned as required. If you need to see the entities that were created, you should use parseentitydata instead.\")},\n\t{\"mod\",\t\t\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t70,\t\"float(float val, float m)\"},\n\t{\"cvar_string\",\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t71,\tD(\"string(string name)\", \"Returns the value of a cvar, as a string.\")},\n\t{\"crash\",\t\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t72,\tD(\"FTEDEP(\\\"Call 'error' instead\\\") void()\", \"Demonstrates that no program is bug free.\")},\n\t{\"stackdump\",\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t73,\tD(\"void()\", \"Prints out the QC's stack, for console-based error reports.\")},\n\t{\"search_begin\",\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t74,\t\"searchhandle(string pattern, enumflags:float{\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"SB_CASEINSENSITIVE=1<<0,\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"SB_FULLPACKAGEPATH=1<<1,\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"SB_ALLOWDUPES=1<<2,\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"SB_FORCESEARCH=1<<3,\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"SB_MULTISEARCH=1<<4,\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"SB_NAMESORT=1<<5\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"} flags, float quiet, optional string package)\"},\n\t{\"search_end\",\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t75,\t\"void(searchhandle handle)\"},\n\t{\"search_getsize\",\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t76,\t\"float(searchhandle handle)\"},\n\t{\"search_getfilename\",PF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t77,\t\"string(searchhandle handle, float num)\"},\n\t{\"etof\",\t\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t79,\t\"float(entity)\"},\n\t{\"ftoe\",\t\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t80,\t\"entity(float)\"},\n\t{\"validstring\",\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t81,\tD(\"float(string)\", \"Returns true if str isn't null. In case 'if [not](str)' was configured to test for empty instead of null.\")},\n\t{\"altstr_count\",\tPF_Fixme,\t\t\t0,\t\t0,\t\t0, \t\t82,\tD(\"DEP float(string str)\", \"Reports how many single-quotes there were in the string, divided by 2.\")},\n\t{\"altstr_prepare\",\tPF_Fixme,\t\t\t0,\t\t0,\t\t0, \t\t83,\tD(\"DEP string(string str)\", \"Adds markup to escape only single-quotes. Does not add any.\")},\n\t{\"altstr_get\",\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t84,\tD(\"DEP string(string str, float num)\", \"Gets the Nth single-quoted token in the input.\")},\n\t{\"altstr_set\",\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0, \t\t85,\tD(\"DEP string(string str, float num, string setval)\", \"Changes the Nth single-quoted token. The setval argument must not contain any single-quotes (use altstr_prepare to ensure this).\")},\n\t{\"altstr_ins\",\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t86,\tD(\"DEP string(string str, float num, string set)\", NULL), true},\n\t{\"findflags\",\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t87,\t\"entity(entity start, .float field, float match)\"},\n\t{\"findchainflags\",\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t88,\t\"entity(.float field, float match)\"},\n\t{\"cvar_defstring\",\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t89,\t\"string(string name)\"},\n\n\t{\"setmodel\",\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t90, D(\"void(entity ent, string mname)\",\t\"Menuqc-specific version.\")},\n\t{\"precache_model\",\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t91, D(\"void(string mname)\",\t\t\t\t\"Menuqc-specific version.\")},\n\t{\"setorigin\",\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t92, D(\"void(entity ent, vector neworg)\",\"Menuqc-specific version.\")},\n\t//end menu-only 'standard'\n#endif\n\t\t\t\t\t\t\t\t\t\t  //nq\t\tqw\t\th2\t\tebfs\n\t{\"ignore\",\t\t\tPF_Ignore,\t\t\t0,\t\t0,\t\t0,\t\t0,\tD(\"void()\",\"Ignored by the engine. Returns 0.\")},\n\t{\"makevectors\",\t\tPF_makevectors,\t\t1,\t\t1,\t\t1,\t\t0,\tD(\"void(vector vang)\",\"Takes an angle vector (pitch,yaw,roll) (+x=DOWN). Writes its results into v_forward, v_right, v_up vectors.\")},\n\t{\"setorigin\",\t\tPF_setorigin,\t\t2,\t\t2,\t\t2,\t\t0,\tD(\"void(entity e, vector o)\",\"Changes e's origin to be equal to o. Also relinks collision state (as well as setting absmin+absmax), which is required after changing .solid\")},\n\t{\"setmodel\",\t\tPF_setmodel,\t\t3,\t\t3,\t\t3,\t\t0,\tD(\"void(entity e, string m)\",\"Looks up m in the model precache list, and sets both e.model and e.modelindex to match. BSP models will set e.mins and e.maxs accordingly, other models depend upon the value of sv_gameplayfix_setmodelrealbox - for compatibility you should always call setsize after all pickups or non-bsp models. Also relinks collision state.\")},\n\t{\"setsize\",\t\t\tPF_setsize,\t\t\t4,\t\t4,\t\t4,\t\t0,\tD(\"void(entity e, vector min, vector max)\", \"Sets the e's mins and maxs fields. Also relinks collision state, which sets absmin and absmax too.\")},\n\t{\"qtest_setabssize\",PF_setsize,\t\t\t5,\t\t0,\t\t0,\t\t0,\tD(\"DEP void(entity e, vector min, vector max)\", \"qtest\"), true},\n\t{\"breakpoint\",\t\tPF_break,\t\t\t6,\t\t6,\t\t6,\t\t0,\tD(\"void()\", \"Trigger a debugging event. FTE will break into the qc debugger. Other engines may crash with a debug execption.\")},\n\t{\"random\",\t\t\tPF_random,\t\t\t7,\t\t7,\t\t7,\t\t0,\tD(\"float()\", \"Returns a random value between 0 and 1. Be warned, this builtin can return 1 in most engines, which can break arrays.\")},\n\t{\"sound\",\t\t\tPF_sound,\t\t\t8,\t\t8,\t\t8,\t\t0,\tD(\"void(entity e, float chan, string samp, float vol, float atten, optional float speedpct, optional float flags, optional float timeofs)\", \"Starts a sound centered upon the given entity.\\nchan is the entity sound channel to use, channel 0 will allow you to mix many samples at once, others will replace the old sample\\n'samp' must have been precached first\\nif specified, 'speedpct' should normally be around 100 (or =0), 200 for double speed or 50 for half speed.\\nIf flags is specified, the reliable flag in the channels argument is used for additional channels. Flags should be made from SOUNDFLAG_* constants\\ntimeofs should be negative in order to provide a delay before the sound actually starts.\")},\n\t{\"normalize\",\t\tPF_normalize,\t\t9,\t\t9,\t\t9,\t\t0,\tD(\"vector(vector v)\", \"Shorten or lengthen a direction vector such that it is only one quake unit long.\")},\n\t{\"error\",\t\t\tPF_error,\t\t\t10,\t\t10,\t\t10,\t\t0,\tD(\"void(string e)\", \"Ends the game with an easily readable error message.\")},\n\t{\"objerror\",\t\tPF_objerror,\t\t11,\t\t11,\t\t11,\t\t0,\tD(\"void(string e)\", \"Displays a non-fatal easily readable error message concerning the self entity, including a field dump. self will be removed!\")},\n\t{\"vlen\",\t\t\tPF_vlen,\t\t\t12,\t\t12,\t\t12,\t\t0,\tD(\"float(vector v)\", \"Returns the square root of the dotproduct of a vector with itself. Or in other words the length of a distance vector, in quake units.\")},\n\t{\"vectoyaw\",\t\tPF_vectoyaw,\t\t13,\t\t13,\t\t13,\t\t0,\tD(\"float(vector v, optional entity reference)\", \"Given a direction vector, returns the yaw angle in which that direction vector points. If an entity is passed, the yaw angle will be relative to that entity's gravity direction.\")},\n\t{\"spawn\",\t\t\tPF_Spawn,\t\t\t14,\t\t14,\t\t14,\t\t0,\tD(\"entity()\", \"Adds a brand new entity into the world! Hurrah, you're now a parent!\")},\n\t{\"remove\",\t\t\tPF_Remove,\t\t\t15,\t\t15,\t\t15,\t\t0,\tD(\"void(entity e)\", \"Destroys the given entity and clears some limited fields (including model, modelindex, solid, classname). Any references to the entity following the call are an error. After half a second the entity will be reused, in the interim you can unfortunatly still read its fields to see if the reference is no longer valid.\")},\n\t{\"removeinstant\",\tPF_RemoveInstant,\t0,\t\t0,\t\t0,\t\t0,\tD(\"void(entity e)\", \"Same thing as the regular remove builtin, but bypasses the half-second rule. The entity slot may be reused instantly. Be CERTAIN that you have no lingering references, because if they're followed they will end up poking an entirely different type of entity! So only use this where you're sure its safe.\")},\n\t{\"traceline\",\t\tPF_svtraceline,\t\t16,\t\t16,\t\t16,\t\t0,\tD(\"void(vector v1, vector v2, float flags, entity ent)\", \"Traces a thin line through the world from v1 towards v2.\\nWill not collide with ent, ent.owner, or any entity who's owner field refers to ent.\\nThe passed entity will also be used to determine whether to use a capsule trace, the contents that the trace should impact, and a couple of other extra fields that define the trace.\\nThere are no side effects beyond the trace_* globals being written.\\nflags&MOVE_NOMONSTERS will not impact on non-bsp entities.\\nflags&MOVE_MISSILE will impact with increased size.\\nflags&MOVE_HITMODEL will impact upon model meshes, instead of their bounding boxes.\\nflags&MOVE_TRIGGERS will also stop on triggers\\nflags&MOVE_EVERYTHING will stop if it hits anything, even non-solid entities.\\nflags&MOVE_LAGGED will backdate entity positions for the purposes of this builtin according to the indicated player ent's latency, to provide lag compensation.\")},\n\t{\"checkclient\",\t\tPF_checkclient,\t\t17,\t\t17,\t\t17,\t\t0,\tD(\"entity()\", \"Returns one of the player entities. The returned player will change periodically.\")},\n\t{\"find\",\t\t\tPF_FindString,\t\t18,\t\t18,\t\t18,\t\t0,\tD(\"entity(entity start, .string fld, string match)\", \"Scan for the next entity with a given field set to the given 'match' value. start should be either world, or the previous entity that was found. Returns world on failure/if there are no more.\\nIf you have many many entities then you may find that hashtables will give more performance (but requires extra upkeep).\")},\n#ifdef QCGC\n\t{\"find_list\",\t\tPF_FindList,\t\t0,\t\t0,\t\t0,\t\t0,\tD(\"entity*(.__variant fld, __variant match, int type=EV_STRING, __out int count)\", \"Scan for the next entity with a given field set to the given 'match' value. start should be either world, or the previous entity that was found. Returns world on failure/if there are no more.\\nIf you have many many entities then you may find that hashtables will give more performance (but requires extra upkeep).\")},\n#endif\n\t{\"precache_sound\",\tPF_precache_sound,\t19,\t\t19,\t\t19,\t\t0,\tD(\"string(string s)\", \"Precaches a sound, making it known to clients and loading it from disk. This builtin (strongly) should be called during spawn functions. This builtin must be called for the sound before the sound builtin is called, or it might not even be heard.\")},\n\t{\"precache_model\",\tPF_precache_model,\t20,\t\t20,\t\t20,\t\t0,\tD(\"string(string s)\", \"Precaches a model, making it known to clients and loading it from disk if it has a .bsp extension. This builtin (strongly) should be called during spawn functions. This must be called for each model name before setmodel may use that model name.\\nModelindicies precached in SSQC will always be positive. CSQC precaches will be negative if they are not also on the server.\")},\n\t{\"stuffcmd\",\t\tPF_stuffcmd,\t\t21,\t\t21,\t\t21,\t\t0,\tD(\"void(entity client, string s, optional string s2, optional string s3, optional string s4, optional string s5, optional string s6, optional string s7)\", \"Sends a console command (or cvar) to the client, where it will be executed. Different clients support different commands. Do NOT forget the final \\\\n.\\nThis builtin is generally considered evil.\")},\n\t{\"stuffcmdflags\",\tPF_stuffcmdflags,\t0,\t\t0,\t\t0,\t\t0,\tD(\"void(entity client, float flags, string s, optional string s2, optional string s3, optional string s4, optional string s5, optional string s6)\", \"Sends a console command (or cvar) to the client, where it will be executed. Different clients support different commands. Do NOT forget the final \\\\n.\\nThis (just as evil) variant allows specifying some flags too. See the STUFFCMD_* constants.\")},\n\t{\"findradius\",\t\tPF_findradius,\t\t22,\t\t22,\t\t22,\t\t0,\tD(/*\"FTEDEP(\\\"Has recursion issues. Use findradius_list.\\\") \"*/\"entity(vector org, float rad, optional .entity chainfield)\", \"Finds all entities within a distance of the 'org' specified. One entity is returned directly, while other entities are returned via that entity's .chain field. Use findradius_list for an updated alternative without reenterancy issues.\")},\n#ifdef QCGC\n\t{\"findradius_list\",\tPF_findradius_list,\t0,\t\t0,\t\t0,\t\t0,\tD(\"entity*(vector org, float rad, __out int foundcount, int sort=0)\", \"Finds all entities linked with a bbox within a distance of the 'org' specified, returning the list as a temp-array (world signifies the end). Unlike findradius, sv_gameplayfix_blowupfallenzombies is ignored (use FL_FINDABLE_NONSOLID instead), while sv_gameplayfix_findradiusdistancetobox and dpcompat_findradiusarealinks are force-enabled. The resulting buffer will automatically be cleaned up by the engine and does not need to be freed.\")},\n#endif\n\n\t//both bprint and sprint accept different arguments in QW vs NQ/H2\n\t{\"bprint\",\t\t\tPF_bprint,\t\t\t23,\t\t0,\t\t23,\t\t0,\tD(\"void(string s, optional string s2, optional string s3, optional string s4, optional string s5, optional string s6, optional string s7, optional string s8)\", \"NQ: Concatenates all arguments, and prints the messsage on the console of all connected clients.\")},\n\t{\"sprint\",\t\t\tPF_sprint,\t\t\t24,\t\t0,\t\t24,\t\t0,\tD(\"void(entity client, string s, optional string s2, optional string s3, optional string s4, optional string s5, optional string s6, optional string s7)\", \"NQ: Concatenates all string arguments, and prints the messsage on the named client's console\")},\n#ifdef HAVE_LEGACY\t//see below\n\t{\"dprint\",\t\t\tPF_dprint,\t\t\t25,\t\t0,\t\t25,\t\t0,\tD(\"void(string s, optional string s2, optional string s3, optional string s4, optional string s5, optional string s6, optional string s7, optional string s8)\", \"NQ: Prints the given message on the server's console, but only if the developer cvar is set. Arguments will be concatenated into a single message.\")},\n#endif\n\t{\"sprint\",\t\t\tPF_sprint,\t\t\t0,\t\t24,\t\t0,\t\t0,\tD(\"void(entity client, float msglvl, string s, optional string s2, optional string s3, optional string s4, optional string s5, optional string s6)\", \"QW: Concatenates all string arguments, and prints the messsage on the named client's console, but only if that client's 'msg' infokey is set lower or equal to the supplied 'msglvl' argument.\")},\n\t{\"bprint\",\t\t\tPF_bprint,\t\t\t0,\t\t23,\t\t0,\t\t0,\tD(\"void(float msglvl, string s, optional string s2, optional string s3, optional string s4, optional string s5, optional string s6, optional string s7)\", \"QW: Concatenates all string arguments, and prints the messsage on the console of only all clients who's 'msg' infokey is set lower or equal to the supplied 'msglvl' argument.\")},\n#ifdef HAVE_LEGACY //these have subtly different behaviour, and are implemented using different internal builtins, which is a bit weird in the extensions file. documentation is documentation.\n\t{\"dprint\",\t\t\tPF_print,\t\t\t0,\t\t25,\t\t0,\t\t0,\tD(\"void(string s, optional string s2, optional string s3, optional string s4, optional string s5, optional string s6, optional string s7, optional string s8)\", \"QW: Unconditionally prints the given message on the server's console.  Arguments will be concatenated into a single message.\")},\n#else\n\t//going forward, we have print and dprint\n\t{\"dprint\",\t\t\tPF_dprint,\t\t\t25,\t\t25,\t\t25,\t\t0,\tD(\"void(string s, optional string s2, optional string s3, optional string s4, optional string s5, optional string s6, optional string s7, optional string s8)\", \"Prints the given message on the server's console, but only if the developer cvar is set. Additional arguments will be concatenated into a single message. Use the print builtin if you want to print regardless of the developer builtin.\")},\n#endif\n\n\t{\"ftos\",\t\t\tPF_ftos,\t\t\t26,\t\t26,\t\t26,\t\t0,\tD(\"string(float val)\", \"Returns a tempstring containing a representation of the given float. Precision depends upon engine.\")},\n\t{\"vtos\",\t\t\tPF_vtos,\t\t\t27,\t\t27,\t\t27,\t\t0,\tD(\"string(vector val)\", \"Returns a tempstring containing a representation of the given vector. Precision depends upon engine.\")},\n\t{\"coredump\",\t\tPF_coredump,\t\t28,\t\t28,\t\t28,\t\t0,\tD(\"void()\", \"Writes out a coredump. This contains stack, globals, and field info for all ents. This can be handy for debugging.\")},\n\t{\"traceon\",\t\t\tPF_traceon,\t\t\t29,\t\t29,\t\t29,\t\t0,\tD(\"void()\", \"Enables tracing. This may be spammy, slow, and stuff. Set debugger 1 in order to use fte's qc debugger.\")},\n\t{\"traceoff\",\t\tPF_traceoff,\t\t30,\t\t30,\t\t30,\t\t0,\tD(\"void()\", \"Disables tracing again.\")},\n\t{\"eprint\",\t\t\tPF_eprint,\t\t\t31,\t\t31,\t\t31,\t\t0,\tD(\"void(entity e)\", \"Debugging builtin that prints all fields of the given entity to the console.\")},// debug print an entire entity\n\t{\"walkmove\",\t\tPF_walkmove,\t\t32,\t\t32,\t\t32,\t\t0,\tD(\"float(float yaw, float dist, optional float settraceglobals)\", \"Attempt to walk the entity at a given angle for a given distance.\\nif settraceglobals is set, the trace_* globals will be set, showing the results of the movement.\\nThis function will trigger touch events.\")},\n//\t{\"qtest_flymove\",\tNULL,\t33},\t// float(vector dir) flymove = #33;\n//qbism super8's 'private'sound #33\n\t{\"droptofloor\",\t\tPF_droptofloor,\t\t34,\t\t34,\t\t34,\t\t0,\tD(\"float()\", \"Instantly moves the entity downwards until it hits the ground. If the entity is in solid or would need to drop more than 'pr_droptofloorunits' quake units, its position will be considered invalid and the builtin will abort, returning FALSE, otherwise TRUE.\")},\n\t{\"lightstyle\",\t\tPF_lightstyle,\t\t35,\t\t35,\t\t35,\t\t0,\tD(\"void(float lightstyle, string stylestring, optional vector rgb)\", \"Specifies an auto-animating string that specifies the light intensity for entities using that lightstyle.\\na is off, z is fully lit. Should be lower case only.\\nrgb will recolour all lights using that lightstyle.\\n\")},\n\t{\"rint\",\t\t\tPF_rint,\t\t\t36,\t\t36,\t\t36,\t\t0,\tD(\"float(float)\", \"Rounds the given float up or down to the closest integeral value. X.5 rounds away from 0\")},\n\t{\"floor\",\t\t\tPF_floor,\t\t\t37,\t\t37,\t\t37,\t\t0,\tD(\"float(float)\", \"Rounds the given float downwards, even when negative.\")},\n\t{\"ceil\",\t\t\tPF_ceil,\t\t\t38,\t\t38,\t\t38,\t\t0,\tD(\"float(float)\", \"Rounds the given float upwards, even when negative.\")},\n\t{\"qtest_canreach\",\tPF_Ignore,\t\t\t39,\t\t0,\t\t0,\t\t0,\t\"DEP float(vector v)\"}, // QTest builtin called in effectless statement\n\t{\"checkbottom\",\t\tPF_checkbottom,\t\t40,\t\t40,\t\t40,\t\t0,\tD(\"float(entity ent)\", \"Expensive checks to ensure that the entity is actually sitting on something solid, returns true if it is.\")},\n\t{\"pointcontents\",\tPF_pointcontents,\t41,\t\t41,\t\t41,\t\t0,\tD(\"float(vector pos)\", \"Checks the given point to see what is there. Returns one of the CONTENTS_* constants. Just because a point is empty does not mean that the player can stand there due to the size of the player - use tracebox for such tests.\")},\n\t{\"pointcontentsmask\",PF_pointcontentsmask,0,\t0,\t\t0,\t\t0,\tD(\"__uint(vector pos, optional float worldonly=1)\", \"Checks the given point to see what is there. Returns a mask of the CONTENTBIT_* constants. Just because a point is empty does not mean that the player can stand there due to the size of the player - use tracebox for such tests.\")},\n//\t{\"qtest_stopsound\",\tNULL,\t\t\t\t42}, // defined QTest builtin that is never called\n\t{\"fabs\",\t\t\tPF_fabs,\t\t\t43,\t\t43,\t\t43,\t\t0,\tD(\"float(float)\", \"Removes the sign of the float, making it positive if it is negative.\")},\n\t{\"aim\",\t\t\t\tPF_aim,\t\t\t\t44,\t\t44,\t\t44,\t\t0,\tD(\"vector(entity player, float missilespeed)\", \"Returns a tweaked copy of the v_forward vector (must be set! ie: makevectors(player.v_angle) ). This is important for keyboard users (that don't want to have to look up/down the whole time), as well as joystick users (who's aim is otherwise annoyingly imprecise). Only the upwards component of the result will differ from the value of v_forward. The builtin will select the enemy closest to the crosshair within the angle of acos(sv_aim).\")},\t//44\n\t{\"cvar\",\t\t\tPF_cvar,\t\t\t45,\t\t45,\t\t45,\t\t0,\tD(\"float(string)\", \"Returns the numeric value of the named cvar\")},\n\t{\"localcmd\",\t\tPF_localcmd,\t\t46,\t\t46,\t\t46,\t\t0,\tD(\"void(string, ...)\", \"Adds the string to the console command queue. Commands will not be executed immediately, but rather at the start of the following frame.\")},\n\t{\"nextent\",\t\t\tPF_nextent,\t\t\t47,\t\t47,\t\t47,\t\t0,\tD(\"entity(entity)\", \"Returns the following entity. Skips over removed entities. Returns world when passed the last valid entity.\")},\n\t{\"particle\",\t\tPF_particle,\t\t48,\t\t0,\t\t48,\t\t48, D(\"void(vector pos, vector dir, float colour, float count)\", \"Spawn 'count' particles around 'pos' moving in the direction 'dir', with a palette colour index between 'colour' and 'colour+8'.\")}, //48 nq readded. This isn't present in QW protocol (fte added it back).\n\t{\"changeyaw\",\t\tPF_changeyaw,\t\t49,\t\t49,\t\t49,\t\t0,\tD(\"#define ChangeYaw changeyaw\\nvoid()\", \"Changes the self.angles_y field towards self.ideal_yaw by up to self.yaw_speed.\")},\n//\t{\"qtest_precacheitem\", NULL,\t\t\t50}, // defined QTest builtin that is never called\n\t{\"vectoangles\",\t\tPF_vectoangles,\t\t51,\t\t51,\t\t51,\t\t0,\tD(\"vector(vector fwd, optional vector up)\", \"Returns the angles (+x=UP) required to orient an entity to look in the given direction. The 'up' argument is required if you wish to set a roll angle, otherwise it will be limited to just monster-style turning.\")},\n\n\t{\"WriteByte\",\t\tPF_WriteByte,\t\t52,\t\t52,\t\t52,\t\t0,\tD(\"void(float to, float val)\", \"Writes a single byte into a network message buffer. Typically you will find a more correct alternative to writing arbitary data. 'to' should be one of the MSG_* constants. MSG_ONE must have msg_entity set first.\")},\t//52\n\t{\"WriteChar\",\t\tPF_WriteChar,\t\t53,\t\t53,\t\t53,\t\t0,\tD(\"void(float to, float val)\", \"Writes a signed value between -128 and 127.\")},\t//53\n\t{\"WriteShort\",\t\tPF_WriteShort,\t\t54,\t\t54,\t\t54,\t\t0,\tD(\"void(float to, float val)\", \"Writes a signed value between -32768 and 32767. As an exception, values up to 65535 will not trigger warnings (but readshort will read the result as negative!)\")},\t//54\n\t{\"WriteLong\",\t\tPF_WriteLong,\t\t55,\t\t55,\t\t55,\t\t0,\tD(\"void(float to, float val)\", \"Writes a signed 32bit integer. Note that the input argument being of float type limits the resulting integer to a mere 24 consecutive bits of validity. Use WriteInt if you want to write an entire 32bit int without data loss.\")},\t//55\n\t{\"WriteCoord\",\t\tPF_WriteCoord,\t\t56,\t\t56,\t\t56,\t\t0,\tD(\"void(float to, float val)\", \"Writes a single value suitable for a map coordinate axis. The precision is not strictly specified but is assumed to be of at least 13.3 fixed-point precision (ie: +/-4k with 1/8th precision).\")},\t//56\n\t{\"WriteAngle\",\t\tPF_WriteAngle,\t\t57,\t\t57,\t\t57,\t\t0,\tD(\"void(float to, float val)\", \"Writes a single value suitable for an angle axis. The precision is not strictly specified but is assumed to be 8bit, giving 256 notches instead of the assumed 360 range passed in.\")},\t//57\n\t{\"WriteString\",\t\tPF_WriteString,\t\t58,\t\t58,\t\t58,\t\t0,\tD(\"void(float to, string val)\", \"Writes a variable-length null terminated string. There are length limits. The codepage is not translated, so be sure that client+server agree on whether utf-8 is being used or not (or just stick to ascii+markup).\")},\t//58\n\t{\"WriteEntity\",\t\tPF_WriteEntity,\t\t59,\t\t59,\t\t59,\t\t0,\tD(\"void(float to, entity val)\", \"Writes the index of the specified entity (the network data size is not specified). This can be read clientside using the readentitynum builtin, with caveats.\")},\t//59\n\n#if defined(HAVE_LEGACY) && defined(NETPREPARSE)\n\t{\"swritebyte\",\t\tPF_qtSingle_WriteByte,\t\t\t0,\t\t0,\t\t0,\t\t0,\tD(\"void(entity to, float val)\", \"A legacy of qtest - like WriteByte, except writes explicitly to the MSG_ONE target.\"), true},\t//52\n\t{\"swritechar\",\t\tPF_qtSingle_WriteChar,\t\t\t0,\t\t0,\t\t0,\t\t0,\tD(\"void(entity to, float val)\", NULL), true},\t//53\n\t{\"swriteshort\",\t\tPF_qtSingle_WriteShort,\t\t\t0,\t\t0,\t\t0,\t\t0,\tD(\"void(entity to, float val)\", NULL), true},\t//54\n\t{\"swritelong\",\t\tPF_qtSingle_WriteLong,\t\t\t0,\t\t0,\t\t0,\t\t0,\tD(\"void(entity to, float val)\", NULL), true},\t//55\n\t{\"swritecoord\",\t\tPF_qtSingle_WriteCoord,\t\t\t0,\t\t0,\t\t0,\t\t0,\tD(\"void(entity to, float val)\", NULL), true},\t//56\n\t{\"swriteangle\",\t\tPF_qtSingle_WriteAngle,\t\t\t0,\t\t0,\t\t0,\t\t0,\tD(\"void(entity to, float val)\", NULL), true},\t//57\n\t{\"swritestring\",\tPF_qtSingle_WriteString,\t\t0,\t\t0,\t\t0,\t\t0,\tD(\"void(entity to, string val)\", NULL), true},\t//58\n\t{\"swriteentity\",\tPF_qtSingle_WriteEntity,\t\t0,\t\t0,\t\t0,\t\t0,\tD(\"void(entity to, entity val)\", NULL), true},\n\n\t{\"bwritebyte\",\t\tPF_qtBroadcast_WriteByte,\t\t0,\t\t0,\t\t0,\t\t0,\tD(\"void(float byte)\", \"A legacy of qtest - like WriteByte, except writes explicitly to the MSG_ALL target.\"), true},\t//59\n\t{\"bwritechar\",\t\tPF_qtBroadcast_WriteChar,\t\t0,\t\t0,\t\t0,\t\t0,\tD(\"void(float val)\", NULL), true},\t//60\n\t{\"bwriteshort\",\t\tPF_qtBroadcast_WriteShort,\t\t0,\t\t0,\t\t0,\t\t0,\tD(\"void(float val)\", NULL), true},\t//61\n\t{\"bwritelong\",\t\tPF_qtBroadcast_WriteLong,\t\t0,\t\t0,\t\t0,\t\t0,\tD(\"void(float val)\", NULL), true},\t//62\n\t{\"bwritecoord\",\t\tPF_qtBroadcast_WriteCoord,\t\t0,\t\t0,\t\t0,\t\t0,\tD(\"void(float val)\", NULL), true},\t//63\n\t{\"bwriteangle\",\t\tPF_qtBroadcast_WriteAngle,\t\t0,\t\t0,\t\t0,\t\t0,\tD(\"void(float val)\", NULL), true},\t//64\n\t{\"bwritestring\",\tPF_qtBroadcast_WriteString,\t\t0,\t\t0,\t\t0,\t\t0,\tD(\"void(string val)\", NULL), true},\t//65\n\t{\"bwriteentity\",\tPF_qtBroadcast_WriteEntity,\t\t0,\t\t0,\t\t0,\t\t0,\tD(\"void(entity val)\", NULL), true},\t//66\n#endif\n\n\t{\"sin\",\t\t\t\tPF_Sin,\t\t\t\t0,\t\t0,\t\t62,\t\t60,\tD(\"float(float angle)\", \"Forgive me father, for I have trigonometry homework. Uses radians.\")},\t//60\n\t{\"cos\",\t\t\t\tPF_Cos,\t\t\t\t0,\t\t0,\t\t61,\t\t61, D(\"float(float angle)\", \"Just cos. Uses radians.\")},\t//61\n\t{\"sqrt\",\t\t\tPF_Sqrt,\t\t\t0,\t\t0,\t\t84,\t\t62,\tD(\"float(float value)\", \"Square Root. Use pow for other exponents.\")},\t//62\n\t{\"modulo\",\t\t\tPF_mod,\t\t\t\t0,\t\t0,\t\t0,\t\t0,\tD(\"float(float dividend, float divisor)\", \"Returns the remainder of a division, so divisor must not be 0. fractional values will give different results... Or just use the % operator.\")},\n\n\t{\"changepitch\",\t\tPF_changepitch,\t\t0,\t\t0,\t\t0,\t\t63,\t\"void(entity ent)\"},\n\t{\"tracetoss\",\t\tPF_TraceToss,\t\t0,\t\t0,\t\t0,\t\t64,\tD(\"void(entity ent, entity ignore)\", \"Attempts to guess were a movetype_toss entity will end up\")},\n\t{\"etos\",\t\t\tPF_etos,\t\t\t0,\t\t0,\t\t0,\t\t65,\tD(\"string(entity ent)\", \"Returns some pointless 'entity %i' message for debugging. Use generateentitydata if you want its field data, or sprintf(\\\"%i\\\",ent) if you want just its number without the clumsy awkward prefix.\")},\n\n\t{\"movetogoal\",\t\tPF_sv_movetogoal,\t67,\t\t67,\t\t67,\t\t0,\tD(\"void(float step)\", \"Runs lots and lots of fancy logic in order to try to step the entity the specified distance towards its goalentity.\")},\t//67\n\t{\"precache_file\",\tPF_precache_file,\t68,\t\t68,\t\t68,\t\t0,\tD(\"string(string s)\", \"This builtin does nothing. It was used only as a hint for pak generation.\")},\t//68\n\t{\"makestatic\",\t\tPF_makestatic,\t\t69,\t\t69,\t\t69,\t\t0,\tD(\"void(entity e)\", \"Sends a copy of the entity's renderable fields to all clients, and REMOVES the entity, preventing further changes. This means it will be unmutable and non-solid.\")},\t//69\n\n\t{\"changelevel\",\t\tPF_changelevel,\t\t70,\t\t70,\t\t70,\t\t0,\tD(\"void(string mapname, optional string newmapstartspot)\", \"Attempts to change the map to the named map. If 'newmapstartspot' is specified, the state of the current map will be preserved, and the argument will be passed to the next map in the 'startspot' global, and the next map will be loaded from archived state if it was previously visited. If not specified, all archived map states will be purged.\")},\t//70\n\n\t{\"cvar_set\",\t\tPF_cvar_set,\t\t72,\t\t72,\t\t72,\t\t0,\tD(\"void(string cvarname, string valuetoset)\", \"Instantly sets a cvar to the given string value. Warning: the resulting string includes apostrophies surrounding the result. You may wish to use sprintf instead.\")},\t//72\n\t{\"centerprint\",\t\tPF_centerprint,\t\t73,\t\t73,\t\t73,\t\t0,\t\"void(entity ent, string text, optional string text2, optional string text3, optional string text4, optional string text5, optional string text6, optional string text7)\"},\t//73\n\n\t{\"ambientsound\",\tPF_ambientsound,\t74,\t\t74,\t\t74,\t\t0,\tD(\"void (vector pos, string samp, float vol, float atten)\", \"Plays a sound at the specified position when clients first connect. FTE will force the sound to loop if it lacks wav cue stuff (unlike vanilla quake). These sounds cannot be stopped/replaced later.\")},\t//74\n\n\t{\"precache_model2\",\tPF_precache_model,\t75,\t\t75,\t\t75,\t\t0,\tD(\"string(string str)\", \"Identical alternative to the non-2 precache. The different name allowed the vanilla qcc to know to bake these files into pak1.pak instead of pak0.pak - useful as a reminder for all those cheapskates hacking/using engines to use the shareware content with fancy mods.\")},\t//75\n\t{\"precache_sound2\",\tPF_precache_sound,\t76,\t\t76,\t\t76,\t\t0,\tD(\"string(string str)\", \"Identical alternative to the non-2 precache. The different name allowed the vanilla qcc to know to bake these files into pak1.pak instead of pak0.pak - useful as a reminder for all those cheapskates hacking/using engines to use the shareware content with fancy mods.\")},\t//76\t// precache_sound2 is different only for qcc\n\t{\"precache_file2\",\tPF_precache_file,\t77,\t\t77,\t\t0,\t\t0,\tD(\"string(string str)\", \"Identical alternative to the non-2 precache. The different name allowed the vanilla qcc to know to bake these files into pak1.pak instead of pak0.pak - useful as a reminder for all those cheapskates hacking/using engines to use the shareware content with fancy mods.\")},\t//77\n\n\t{\"setspawnparms\",\tPF_setspawnparms,\t78,\t\t78,\t\t78,\t\t0,\tD(\"void(entity player)\", \"Overwrites the parm* globals to match those of the specified client (restored from the last map's SetChangeParms, otherwise SetNewParms)\")},\t//78\n\n//QuakeEx (aka: quake rerelease). These conflict with core extensions so we don't register them by default (Update: they now link by name rather than number.\n\t{\"ex_finaleFinished\",PF_finaleFinished_qex,0,\t0,\t\t0,0/*79*/,\tD(\"float()\", \"Behaviour is undocumented.\")},\n\t{\"ex_localsound\",\tPF_localsound_qex,\t0,\t\t0,\t\t0,0/*80*/,\tD(\"void(entity client, string sample)\", \"Plays a sound only to the single specified player. The sound will be full volume, unattenuated, unaffected by reverb. Suitable for menus and things that are not part of the actual game world.\")},\n\t{\"ex_draw_point\",\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,0/*81*/,\tD(\"void(vector point, float colormap, float lifetime, float depthtest)\", \"Behaviour is undocumented.\")},\n\t{\"ex_draw_line\",\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,0/*82*/,\tD(\"void(vector start, vector end, float colormap, float lifetime, float depthtest)\", \"Behaviour is undocumented.\")},\n\t{\"ex_draw_arrow\",\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,0/*83*/,\tD(\"void(vector start, vector end, float colormap, float size, float lifetime, float depthtest)\", \"Behaviour is undocumented.\")},\n\t{\"ex_draw_ray\",\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,0/*84*/,\tD(\"void(vector start, vector direction, float length, float colormap, float size, float lifetime, float depthtest)\", \"Behaviour is undocumented.\")},\n\t{\"ex_draw_circle\",\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,0/*85*/,\tD(\"void(vector origin, float radius, float colormap, float lifetime, float depthtest)\", \"Behaviour is undocumented.\")},\n\t{\"ex_draw_bounds\",\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,0/*86*/,\tD(\"void(vector min, vector max, float colormap, float lifetime, float depthtest)\", \"Behaviour is undocumented.\")},\n\t{\"ex_draw_worldtext\",PF_Fixme,\t\t\t0,\t\t0,\t\t0,0/*87*/,\tD(\"void(string s, vector origin, float size, float lifetime, float depthtest)\", \"Behaviour is undocumented.\")},\n\t{\"ex_draw_sphere\",\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,0/*88*/,\tD(\"void(vector origin, float radius, float colormap, float lifetime, float depthtest)\", \"Behaviour is undocumented.\")},\n\t{\"ex_draw_cylinder\",PF_Fixme,\t\t\t0,\t\t0,\t\t0,0/*89*/,\tD(\"void(vector origin, float halfHeight, float radius, float colormap, float lifetime, float depthtest)\", \"Behaviour is undocumented.\")},\n\t{\"ex_centerprint\",\tPF_centerprint_qex,\t0,\t\t0,\t\t0,0/*90*/,\tD(\"void(entity ent, string text, optional string s0, optional string s1, optional string s2, optional string s3, optional string s4, optional string s5)\", \"Remaster: Sends the strings to the client, which will order according to {#}. Also substitutes localised strings for $NAME strings.\")},\n\t{\"ex_bprint\",\t\tPF_bprint_qex,\t\t0,\t\t0,\t\t0,0/*91*/,\tD(\"void(string s, optional string s0, optional string s1, optional string s2, optional string s3, optional string s4, optional string s5, optional string s6)\", \"Remaster: Sends the strings to all clients, which will order them according to {#}. Also substitutes localised strings for $NAME strings.\")},\n\t{\"ex_sprint\",\t\tPF_sprint_qex,\t\t0,\t\t0,\t\t0,0/*92*/,\tD(\"void(entity client, string s, optional string s0, optional string s1, optional string s2, optional string s3, optional string s4, optional string s5)\", \"Remaster: Sends the strings to the client, which will order according to {#}. Also substitutes localised strings for $NAME strings.\")},\n\t{\"ex_bot_movetopoint\",PF_Fixme,\t\t\t0,\t\t0,\t\t0,0,\tD(\"float(entity bot, vector point)\", \"Behaviour is undocumented.\")},\n\t{\"ex_bot_followentity\",PF_Fixme,\t\t0,\t\t0,\t\t0,0,\tD(\"float(entity bot, entity goal)\", \"Behaviour is undocumented.\")},\n\t{\"ex_CheckPlayerEXFlags\",PF_CheckPlayerEXFlags_qex,0,0,\t0,0,\tD(\"float(entity playerEnt)\", \"Behaviour is undocumented.\")},\n\t{\"ex_walkpathtogoal\",PF_walkpathtogoal_qex,0,\t0,\t\t0,0,\tD(\"float(float movedist, vector goal)\", \"Behaviour is undocumented.\")},\n\t{\"ex_prompt\",\t\tPF_prompt_qex,\t\t0,\t\t0,\t\t0,0,\tD(\"void(entity player, string text, float numchoices)\", \"Initiates a user prompt. You must call ex_promptchoice exactly once per choice, with no other networking between.\")},\n\t{\"ex_promptchoice\",\tPF_promptchoice_qex,0,\t\t0,\t\t0,0,\tD(\"void(entity player, string text, float impulse)\", \"Follows a call to ex_prompt.\")},\n\t{\"ex_clearprompt\",\tPF_prompt_qex,\t\t0,\t\t0,\t\t0,0,\tD(\"void(entity player)\", \"Hides a previously sent prompt.\")},\n//End QuakeEx, for now. :(\n\n// Tomaz - QuakeC String Manipulation Begin\n\t{\"tq_zone\",\t\t\tPF_strzone,\t\t\t0,\t\t0,\t\t0,\t\t79, D(\"DEP string(string s)\",NULL), true},\t//79\n\t{\"tq_unzone\",\t\tPF_strunzone,\t\t0,\t\t0,\t\t0,\t\t80, D(\"DEP void(string s)\",NULL), true},\t//80\n\t{\"tq_strlen\",\t\tPF_strlen,\t\t\t0,\t\t0,\t\t0,\t\t81, D(\"DEP float(string s)\",NULL), true},\t//81\n\t{\"tq_strcat\",\t\tPF_strcat,\t\t\t0,\t\t0,\t\t0,\t\t82, D(\"DEP string(string s1, optional string s2, optional string s3, optional string s4, optional string s5, optional string s6, optional string s7, optional string s8)\",NULL), true},\t//82\n\t{\"tq_substring\",\tPF_substring,\t\t0,\t\t0,\t\t0,\t\t83, D(\"DEP string(string str, float start, float len)\",NULL), true},\t//83\n\t{\"tq_stof\",\t\t\tPF_stof,\t\t\t0,\t\t0,\t\t0,\t\t84, D(\"DEP float(string s)\",NULL), true},\t//84\n\t{\"tq_stov\",\t\t\tPF_stov,\t\t\t0,\t\t0,\t\t0,\t\t85, D(\"DEP vector(string s)\",NULL), true},\t//85\n// Tomaz - QuakeC String Manipulation End\n\n// Tomaz - QuakeC File System Begin (new mods use frik_file instead)\n\t{\"tq_fopen\",\t\tPF_fopen,\t\t\t0,\t\t0,\t\t0,\t\t86, D(\"DEP filestream(string filename, float mode)\",NULL), true},// (QSG_FILE)\n\t{\"tq_fclose\",\t\tPF_fclose,\t\t\t0,\t\t0,\t\t0,\t\t87, D(\"DEP void(filestream fhandle)\",NULL), true},// (QSG_FILE)\n\t{\"tq_fgets\",\t\tPF_fgets,\t\t\t0,\t\t0,\t\t0,\t\t88, D(\"DEP string(filestream fhandle)\",NULL), true},// (QSG_FILE)\n\t{\"tq_fputs\",\t\tPF_fputs,\t\t\t0,\t\t0,\t\t0,\t\t89, D(\"DEP void(filestream fhandle, string s)\",NULL), true},// (QSG_FILE)\n// Tomaz - QuakeC File System End\n\n\t{\"logfrag\",\t\t\tPF_logfrag,\t\t\t0,\t\t79,\t\t0,\t\t79,\t\"void(entity killer, entity killee)\"},\t//79\n\t{\"infokey\",\t\t\tPF_infokey_s,\t\t0,\t\t80,\t\t0,\t\t80,\tD(\"string(entity e, string key)\", \"If e is world, returns the field 'key' from either the serverinfo or the localinfo. If e is a player, returns the value of 'key' from the player's userinfo string. There are a few special exceptions, like 'ip' which is not technically part of the userinfo.\")},\t//80\n\t{\"infokeyf\",\t\tPF_infokey_f,\t\t0,\t\t0,\t\t0,\t\t0,\tD(\"float(entity e, string key)\", \"Identical to regular infokey, except returns a float.\")},\t//80\n\t{\"infokey_blob\",\tPF_infokey_blob,\t0,\t\t0,\t\t0,\t\t0,\tD(\"int(entity e, string key, optional void *outbuf, int outbufsize)\", \"Retrieves a user's blob size, and optionally writes it to the specified buffer.\")},\n\t{\"stof\",\t\t\tPF_stof,\t\t\t0,\t\t81,\t\t0,\t\t81,\t\"float(string)\"},\t//81\n\t{\"multicast\",\t\tPF_multicast,\t\t0,\t\t82,\t\t0,\t\t82,\tD(\"#define unicast(pl,reli) do{msg_entity = pl; multicast('0 0 0', reli?MULTICAST_ONE_R:MULTICAST_ONE);}while(0)\\n\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"void(vector where, float set)\", \"Once the MSG_MULTICAST network message buffer has been filled with data, this builtin is used to dispatch it to the given target, filtering by pvs for reduced network bandwidth.\")},\t//82\n\n\n#ifdef HAVE_LEGACY\n//mvdsv (don't require ebfs usage in qw)\n\t{\"executecommand\",\tPF_ExecuteCommand,\t0,\t\t0,\t\t0,\t\t83, D(\"DEP void()\",\"Attempt to flush the localcmd buffer NOW. This is unsafe, as many events might cause the map to be purged while still executing qc code.\"),\t\t\t\ttrue},\n\t{\"mvdtokenize\",\t\tPF_tokenize_console,0,\t\t0,\t\t0,\t\t84, D(\"DEP void(string str)\",NULL),\t\ttrue},\n\t{\"mvdargc\",\t\t\tPF_ArgC,\t\t\t0,\t\t0,\t\t0,\t\t85, D(\"DEP float()\",NULL),\t\t\t\ttrue},\n\t{\"mvdargv\",\t\t\tPF_ArgV,\t\t\t0,\t\t0,\t\t0,\t\t86, D(\"DEP string(float num)\",NULL),\ttrue},\n\n//mvd commands\n//some of these are a little iffy.\n//we support them for mvdsv compatability but some of them look very hacky.\n//these ones are not honoured with numbers, but can be used via the proper means.\n\t{\"teamfield\",\t\tPF_teamfield,\t\t0,\t\t0,\t\t0,\t\t87, D(\"DEP void(.string teamfield)\",NULL), true},\n\t{\"substr\",\t\t\tPF_substr,\t\t\t0,\t\t0,\t\t0,\t\t88, D(\"DEP string(string str, float start, float len)\",\"Returns the theDoes not work on tempstrings nor zoned strings.\"), true},\n\t{\"mvdstrcat\",\t\tPF_strcat,\t\t\t0,\t\t0,\t\t0,\t\t89, D(\"DEP string(string s1, optional string s2, optional string s3, optional string s4, optional string s5, optional string s6, optional string s7, optional string s8)\",NULL), true},\n\t{\"mvdstrlen\",\t\tPF_strlen,\t\t\t0,\t\t0,\t\t0,\t\t90, D(\"DEP float(string s)\",NULL), true},\n\t{\"str2byte\",\t\tPF_str2byte,\t\t0,\t\t0,\t\t0,\t\t91, D(\"DEP float(string str)\",\"Returns the value of the first byte of the given string.\"), true},\n\t{\"str2short\",\t\tPF_str2short,\t\t0,\t\t0,\t\t0,\t\t92, D(\"DEP float(string str)\",\"Returns the value of the first two bytes of the given string, treated as a short.\"), true},\n\t{\"mvdnewstr\",\t\tPF_mvdsv_newstring,\t0,\t\t0,\t\t0,\t\t93, D(\"DEP string(string s, optional float bufsize)\",\"Allocs a copy of the string. If bufsize is longer than the string then there will be extra space available on the end. The resulting string can then be modified freely.\"), true},\n\t{\"mvdfreestr\",\t\tPF_mvdsv_freestring,0,\t\t0,\t\t0,\t\t94, D(\"DEP void(string s)\",\"Frees memory allocated by mvdnewstr.\"), true},\n\t{\"conprint\",\t\tPF_conprint,\t\t0,\t\t0,\t\t0,\t\t95, D(\"DEP void(string s, ...)\",\"Prints the string(s) onto the local console, bypassing redirects.\"), true},\n\t{\"readcmd\",\t\t\tPF_readcmd,\t\t\t0,\t\t0,\t\t0,\t\t0/*96*/, D(\"DEP string(string str)\",\"Executes the given command NOW. This is unsafe, as many events might cause the map to be purged while still executing qc code, so be careful about the commands you try reading, and avoid aliases.\"), true},\n\t{\"mvdstrcpy\",\t\tPF_MVDSV_strcpy,\t0,\t\t0,\t\t0,\t\t97, D(\"DEP void(string dst, string src)\",NULL), true},\n\t{\"strstr\",\t\t\tPF_strstr,\t\t\t0,\t\t0,\t\t0,\t\t98, D(\"DEP string(string str, string sub)\",NULL), true},\n\t{\"mvdstrncpy\",\t\tPF_MVDSV_strncpy,\t0,\t\t0,\t\t0,\t\t99, D(\"DEP void(string dst, string src, float count)\",NULL), true},\n\t{\"logtext\",\t\t\tPF_logtext,\t\t\t0,\t\t0,\t\t0,\t\t100, D(\"DEP void(string name, float console, string text)\",NULL), true},\n\t{\"mvdcalltimeofday\",PF_calltimeofday,\t0,\t\t0,\t\t0,\t\t102, D(\"__deprecated(\\\"Use strftime\\\") void()\",NULL), true},\n#ifdef MVD_RECORDING\n\t{\"forcedemoframe\",\tPF_forcedemoframe,\t0,\t\t0,\t\t0,\t\t103, D(\"DEP void(float now)\",NULL), true},\n#endif\n//end of mvdsv\n#endif\n\t{\"redirectcmd\",\t\tPF_redirectcmd,\t\t0,\t\t0,\t\t0,\t\t101, D(\"DEP void(entity to, string str)\",\"Executes a single console command, and sends the text generated by it to the specified player. The command will be executed at the end of the frame once QC is no longer running - you may wish to pre/postfix it with 'echo'.\")},\n\n\t{\"getlightstyle\",\tPF_getlightstyle,\t0,\t\t0,\t\t0,\t\t0,\tD(\"string(float style, optional __out vector rgb)\", \"Obtains the light style string for the given style.\")},\n\t{\"getlightstylergb\",PF_getlightstylergb,0,\t\t0,\t\t0,\t\t0,\tD(\"vector(float style)\", \"Obtains the current rgb value of the specified light style. In csqc, this is correct with regard to the current frame, while ssqc gives no guarentees about time and ignores client cvars. Note: use getlight if you want the actual light value at a point.\")},\n#ifdef HEXEN2\n\t{\"lightstylestatic\",PF_lightstylestatic,0,\t\t0,\t\t5,\t\t5,\tD(\"void(float style, float val, optional vector rgb)\", \"Sets the lightstyle to an explicit numerical level. From Hexen2.\")},\n\t{\"tracearea\",\t\tPF_traceboxh2,\t\t0,\t\t0,\t\t33,\t\t0,\tD(\"void(vector v1, vector v2, vector mins, vector maxs, float nomonsters, entity ent)\", \"For hexen2 compat\")},\n\t{\"vhlen\",\t\t\tPF_vhlen,\t\t\t0,\t\t0,\t\t50,\t\t0,\tD(\"float(vector)\", \"Returns the horizontal length of the given vector ignoring z dispalcement - specifically sqrt(x*x+y*y)\")},\n\t{\"printfloat\",\t\tPF_h2dprintf,\t\t0,\t\t0,\t\t60,\t\t0,\tD(\"FTEDEP(\\\"Use sprintf\\\") void(string fmt, float val)\", NULL)},\t//60\n\t{\"AdvanceFrame\",\tPF_h2AdvanceFrame,\t0,\t\t0,\t\t63,\t\t0,\tD(\"void(float start, float end)\", \"Advances self.frame by 1 and keeps it within the specified range. Return values are:\\n0: wasn't already in the range or advanced naturally.\\n1: Wrapped back to start.\\n2: Reached end of loop, next call will wrap.\\nIf end is lower than start then it'll simply play backwards.\")},\n\t{\"printvec\",\t\tPF_h2dprintv,\t\t0,\t\t0,\t\t64,\t\t0,\tD(\"FTEDEP(\\\"Use sprintf\\\") void(string fmt, vector val)\", NULL)},\t//64\n\t{\"RewindFrame\",\t\tPF_h2RewindFrame,\t0,\t\t0,\t\t65,\t\t0},\n\t{\"particleexplosion\",PF_h2particleexplosion,0,\t0,\t\t81,\t\t0},\n\t{\"movestep\",\t\tPF_h2movestep,\t\t0,\t\t0,\t\t82,\t\t0}, //more explicit walkmove\n\t{\"advanceweaponframe\",PF_h2advanceweaponframe,0,0,\t\t83,\t\t0},\n\n\t{\"setclass\",\t\tPF_h2setclass,\t\t0,\t\t0,\t\t66,\t\t0}, //forces the player's class (aka cl_playerclass and self.playerclass).\n\t{\"lightstylevalue\",\tPF_lightstylevalue,\t0,\t\t0,\t\t71,\t\t0,\tD(\"float(float lstyle)\", \"Returns the last value passed into the lightstylestatic builtin, or the first value specified by the style string passed to the lightstyle builtin\")},\t//70\n\n\t{\"plaque_draw\",\t\tPF_h2plaque_draw,\t0,\t\t0,\t\t79,\t\t0,\t\"void(entity targ, float stringno)\"},\t//79\n\t{\"rain_go\",\t\t\tPF_h2rain_go,\t\t0,\t\t0,\t\t80,\t\t0},\t//80\n\t{\"setpuzzlemodel\",\tPF_h2set_puzzle_model,0,\t0,\t\t87,\t\t0},\t//setmodel(arg1, sprintf(\"models/puzzle/%s.mdl\", arg2))\n\t{\"starteffect\",\t\tPF_h2starteffect,\t0,\t\t0,\t\t88,\t\t0},\t//FIXME\n\t{\"endeffect\",\t\tPF_h2endeffect,\t\t0,\t\t0,\t\t89,\t\t0},\t//FIXME\n\t{\"getstring\",\t\tPF_h2getstring,\t\t0,\t\t0,\t\t92,\t\t0},\t//localises an internationalised stringnumber, acording to the server's locale.\n\t{\"spawntemp\",\t\tPF_h2spawn_temp,\t0,\t\t0,\t\t93,\t\t0}, //Like spawn, but can be reclaimed when too many are spawned.\n\n\t{\"v_factor\",\t\tPF_h2v_factor,\t\t0,\t\t0,\t\t94,\t\t0}, //converts from modelspace to worldspace according to the v_forward etc 3x3 matrix (you'll need to add the modelspace's origin after).\n\t{\"v_factorrange\",\tPF_h2v_factorrange,\t0,\t\t0,\t\t95,\t\t0},\t//v_factor(randomv(arg1,arg2))\n\n\t{\"precache_puzzle_model\",PF_h2precache_puzzle_model,0,0,90,\t\t0},\t//precache_model(sprintf(\"models/puzzle/%s.mdl\", arg2))\n\t{\"concatv\",\t\t\tPF_h2concatv,\t\t0,\t\t0,\t\t91,\t\t0},//boxclamps arg1 to +/- arg2\n\t{\"precache_sound3\",\tPF_precache_sound,\t0,\t\t0,\t\t96,\t\t0},//hexen2 had more paks...\n\t{\"precache_model3\",\tPF_precache_model,\t0,\t\t0,\t\t97,\t\t0},//please don't use...\n\t{\"matchangletoslope\",PF_h2matchAngleToSlope,0,\t0,\t\t99,\t\t0},\n\t{\"updateinfoplaque\",PF_h2updateinfoplaque,0,\t0,\t\t100,\t0},//bitwise-updates the internal (global) storage for hexen2's STAT_H2_OBJECTIVE1+STAT_H2_OBJECTIVE2 stats.\n\t{\"precache_sound4\",\tPF_precache_sound,\t0,\t\t0,\t\t101,\t0},\n\t{\"precache_model4\",\tPF_precache_model,\t0,\t\t0,\t\t102,\t0},\n\t{\"precache_file4\",\tPF_precache_file,\t0,\t\t0,\t\t103,\t0},\n\t{\"dowhiteflash\",\tPF_h2whiteflash,\t0,\t\t0,\t\t104,\t0}, //localcmd(\"wf\\n\")\n\t{\"updatesoundpos\",\tPF_h2updatesoundpos,0,\t\t0,\t\t105,\t0},\n\t{\"stopsound\",\t\tPF_StopSound,\t\t0,\t\t0,\t\t106,\t0,\tD(\"void(entity ent, float channel)\", \"Terminates playback of sounds on the specified entity-channel. CHAN_AUTO should not be used.\")},\n\n\t//shanjaq's fork of uhexen2... listed here to avoid confusion with other builtins.\n\t{\"set_extra_flags\",\tPF_Ignore,\t\t\t0,\t\t0,\t\t107,\t0, D(\"void(string model, float flags)\", \"You should probably use .traileffectnum/r_effect instead.\"), true},\n\t{\"set_fx_color\",\tPF_Ignore,\t\t\t0,\t\t0,\t\t108,\t0, D(\"void(string model,float r,float g,float b,float a)\", \"You should probably use .traileffectnum/r_effect instead and/or .glowmod and/or tenebrae_gfx_dlights.\"), true},\n\t{\"strhash\",\t\t\tPF_sj_strhash,\t\t0,\t\t0,\t\t109,\t0, D(\"float(string)\", \"A poor-man's strzone, apparently. digest_hex should be used instead of you care about the hash function used.\"), true},\n\n\t{\"precache_model5\",\tPF_precache_model,\t0,\t\t0,\t\t116,\t0},//please don't use...\n\t{\"precache_sound5\",\tPF_precache_sound,\t0,\t\t0,\t\t117,\t0},\n#else\n\t{\"stopsound\",\t\tPF_StopSound,\t\t0,\t\t0,\t\t0,\t\t0,\tD(\"void(entity ent, float channel)\", \"Terminates playback of sounds on the specified entity-channel. CHAN_AUTO should not be used.\")},\n#endif\n\n\t{\"tracebox\",\t\tPF_traceboxdp,\t\t0,\t\t0,\t\t0,\t\t90,\tD(\"void(vector start, vector mins, vector maxs, vector end, float nomonsters, entity ent)\", \"Exactly like traceline, but a box instead of a uselessly thin point. Acceptable sizes are limited by bsp format, q1bsp has strict acceptable size values.\")},\n\n\t{\"randomvec\",\t\tPF_randomvector,\t0,\t\t0,\t\t0,\t\t91,\tD(\"vector()\", \"Returns a random vector with length <= 1 (each axis may be negative).\")},\n\t{\"getlight\",\t\tPF_sv_getlight,\t\t0,\t\t0,\t\t0,\t\t92, D(\"DEP_SSQC(\\\"Broken on dedicated servers, ignores rtlights/etc\\\") vector(vector org)\", \"Computes the RGB lighting at the specified position.\")},// (DP_QC_GETLIGHT),\n\t{\"registercvar\",\tPF_registercvar,\t0,\t\t0,\t\t0,\t\t93,\tD(\"float(string cvarname, string defaultvalue, optional float flags)\", \"Creates a new cvar on the fly. If it does not already exist, it will be given the specified value. If it does exist, this is a no-op.\\nThis builtin has the limitation that it does not apply to configs or commandlines. Such configs will need to use the set or seta command causing this builtin to be a noop.\\nIn engines that support it, you will generally find the autocvar feature easier and more efficient to use.\")},\n\t{\"min\",\t\t\t\tPF_min,\t\t\t\t0,\t\t0,\t\t0,\t\t94,\tD(\"float(float a, float b, ...)\", \"Returns the lowest value of its arguments.\")},// (DP_QC_MINMAXBOUND)\n\t{\"max\",\t\t\t\tPF_max,\t\t\t\t0,\t\t0,\t\t0,\t\t95,\tD(\"float(float a, float b, ...)\", \"Returns the highest value of its arguments.\")},// (DP_QC_MINMAXBOUND)\n\t{\"bound\",\t\t\tPF_bound,\t\t\t0,\t\t0,\t\t0,\t\t96,\tD(\"float(float minimum, float val, float maximum)\", \"Returns val, unless minimum is higher, or maximum is less.\")},// (DP_QC_MINMAXBOUND)\n\t{\"pow\",\t\t\t\tPF_pow,\t\t\t\t0,\t\t0,\t\t0,\t\t97,\tD(\"float(float value, float exp)\", \"Computes an exponent, or 'raises value to the power of exp', aka multiplying 'value' by itself 'exp' times. Equivelent to the C function, so fractional exponents are allowed, eg 0.5 to double up as a square root or 1.0/3 for cube root etc.\")},\n\t{\"logarithm\",\t\tPF_Logarithm,\t\t0,\t\t0,\t\t0,\t\t0,\tD(\"float(float v, optional float base)\", \"Determines the logarithm of the input value according to the specified base. This can be used to calculate how much something was shifted by. When base is omitted then this computes the 'natural log' (often called ln)\")},\n\t{\"tj_cvar_string\",\tPF_cvar_string,\t\t0,\t\t0,\t\t0,\t\t97, D(\"DEP string(string cvarname)\",NULL), true},\t//telejano\n//DP_QC_FINDFLOAT\n\t{\"findfloat\",\t\tPF_FindFloat,\t\t0,\t\t0,\t\t0,\t\t98, D(\"#define findentity findfloat\\nentity(entity start, .__variant fld, __variant match)\", \"Equivelent to the find builtin, but instead of comparing strings contents, this builtin compares the raw values. This builtin requires multiple calls in order to scan all entities - set start to the previous call's return value.\\nworld is returned when there are no more entities.\")},\t// #98 (DP_QC_FINDFLOAT)\n\n\t{\"checkextension\",\tPF_checkextension,\t99,\t\t99,\t\t0,\t\t99,\tD(\"float(string extname)\", \"Checks for an extension by its name (eg: checkextension(\\\"FRIK_FILE\\\") says that its okay to go ahead and use strcat).\\nUse cvar(\\\"pr_checkextension\\\") to see if this builtin exists.\")},\t// #99\t//darkplaces system - query a string to see if the mod supports X Y and Z.\n\t{\"checkbuiltin\",\tPF_checkbuiltin,\t0,\t\t0,\t\t0,\t\t0,\tD(\"float(__variant funcref)\", \"Checks to see if the specified builtin is supported/mapped. This is intended as a way to check for #0 functions, allowing for simple single-builtin functions. Warning, if two different engines map different builtins to the same number, then this function will not tell you which will be called, only that it won't crash (the exception being #0, which are remapped as available).\")},\n\t{\"builtin_find\",\tPF_builtinsupported,100,\t100,\t0,\t\t100,\tD(\"float(string builtinname)\", \"Looks to see if the named builtin is valid, and returns the builtin number it exists at.\")},\t// #100\t//per builtin system.\n\t{\"anglemod\",\t\tPF_anglemod,\t\t0,\t\t0,\t\t0,\t\t102,\t\"float(float value)\"},\n\t{\"anglesub\",\t\tPF_anglesub,\t\t0,\t\t0,\t\t0,\t\t0,\t\tD(\"float(float newangle, float oldangle)\",\"Returns newangle-oldangle, except returning the shortest route around a circle so yields a result between -180 and +180.\")},\n\t{\"qsg_cvar_string\",\tPF_cvar_string,\t\t0,\t\t0,\t\t0,\t\t103,\tD(\"DEP string(string cvarname)\",\"An old/legacy equivelent of more recent/common builtins in order to read a cvar's string value.\"), true},\n\n//TEI_SHOWLMP2\n\t{\"showpic\",\t\t\tPF_ShowPic,\t\t\t0,\t\t0,\t\t0,\t\t104,\tD(\"DEP_CSQC void(string slot, string picname, float x, float y, float zone, optional entity player)\", \"Instructs the client that it should display some image somewhere on the screen (relative to the specified zone, to deal with unknown video modes). This is an earlier builtin form of the showpic console command, and thus lacks support for touchscreen events.\")},\n\t{\"hidepic\",\t\t\tPF_HidePic,\t\t\t0,\t\t0,\t\t0,\t\t105,\t\"DEP_CSQC void(string slot, optional entity player)\"},\n\t{\"movepic\",\t\t\tPF_MovePic,\t\t\t0,\t\t0,\t\t0,\t\t106,\t\"DEP_CSQC void(string slot, float x, float y, float zone, optional entity player)\"},\n\t{\"changepic\",\t\tPF_ChangePic,\t\t0,\t\t0,\t\t0,\t\t107,\t\"DEP_CSQC void(string slot, string picname, optional entity player)\"},\n\t{\"showpicent\",\t\tPF_ShowPic,\t\t\t0,\t\t0,\t\t0,\t\t108,\tD(\"DEP_CSQC void(string slot, entity player)\",NULL), true},\n\t{\"hidepicent\",\t\tPF_HidePic,\t\t\t0,\t\t0,\t\t0,\t\t109,\tD(\"DEP_CSQC void(string slot, entity player)\",NULL), true},\n//\t{\"movepicent\",\t\tPF_MovePic,\t\t\t0,\t\t0,\t\t0,\t\t110,\t\"DEP_CSQC void(string slot, float x, float y, float zone, entity player)\", true},\n//\t{\"changepicent\",\tPF_ChangePic,\t\t0,\t\t0,\t\t0,\t\t111,\t\"DEP_CSQC void(string slot, string picname, entity player)\", true},\n//End TEI_SHOWLMP2\n\n//frik file\n\t{\"fopen\",\t\t\tPF_fopen,\t\t\t0,\t\t0,\t\t0,\t\t110, D(\"filestream(string filename, float mode, optional float mmapminsize)\", \"Opens a file within quake's filesystem for either read or write access. Returns a negative value on error, or >=0 for success. Due to sandboxing, all filenames should be prefixed with \\\"data/\\\" for maximum compatibility, otherwise writes may be redirected or reads may fail (sandboxing can be disabled in FTE with the `-unsafefopen` arg, but its use is strongly discouraged - hence why its not a cvar or w/e). tcp:// or tls:// schemes may be used in conjunction with FILE_STREAM. The file:/// scheme may be used when the `-allowfileuri` commandline arg was used (again strongly discouraged, but some people have weird requirements. You might just want to use a symlink instead.). Handles will not persist into loaded savegames, so be sure to not let handles linger (unless you're using eg SV_PerformLoad to handle it).\")},\t// (FRIK_FILE)\n\t{\"fclose\",\t\t\tPF_fclose,\t\t\t0,\t\t0,\t\t0,\t\t111, D(\"void(filestream fhandle)\", \"Closes a file handle returned from fopen. Should be called once for every successful call to fopen, files left open may result in warning messages, double closes are definitely bad too obviously.\")},\t// (FRIK_FILE)\n\t{\"fgets\",\t\t\tPF_fgets,\t\t\t0,\t\t0,\t\t0,\t\t112, D(\"string(filestream fhandle)\", \"Reads a single line out of the file. The new line character is not returned as part of the string. Returns the null string on EOF (use `if not(thereturnedstring)` to easily test for this, which distinguishes it from the empty string which is returned if the line being read is blank.\")},\t// (FRIK_FILE)\n\t{\"fputs\",\t\t\tPF_fputs,\t\t\t0,\t\t0,\t\t0,\t\t113, D(\"void(filestream fhandle, string s, optional string s2, optional string s3, optional string s4, optional string s5, optional string s6, optional string s7)\", \"Writes the given string(s) into the file. For compatibility with fgets, you should ensure that the string is terminated with a \\\\n - this will not otherwise be done for you. It is up to the engine whether dos or unix line endings are actually written.\")},\t// (FRIK_FILE)\n\t{\"fread\",\t\t\tPF_fread,\t\t\t0,\t\t0,\t\t0,\t\t0,\t D(\"int(filestream fhandle, void *ptr, int size, optional int offset)\", \"Reads binary data out of the file. Returns truncated lengths if the read exceeds the length of the file.\")},\n\t{\"fwrite\",\t\t\tPF_fwrite,\t\t\t0,\t\t0,\t\t0,\t\t0,\t D(\"int(filestream fhandle, void *ptr, int size, optional int offset)\", \"Writes binary data out of the file.\")},\n\t{\"fseek\",\t\t\tPF_fseek32,\t\t\t0,\t\t0,\t\t0,\t\t0,\t D(\"#define ftell fseek //c compat\\nint(filestream fhandle, optional int newoffset)\", \"Changes the current position of the file, if specified. Returns prior position, in bytes.\")},\n\t{\"fsize\",\t\t\tPF_fsize32,\t\t\t0,\t\t0,\t\t0,\t\t0,\t D(\"int(filestream fhandle, optional int newsize)\", \"Reports the total size of the file, in bytes. Can also be used to truncate/extend the file\")},\n\t{\"fseek64\",\t\t\tPF_fseek64,\t\t\t0,\t\t0,\t\t0,\t\t0,\t D(\"int(filestream fhandle, optional __int64 newoffset)\", \"Changes the current position of the file, if specified. Returns prior position, in bytes.\")},\n\t{\"fsize64\",\t\t\tPF_fsize64,\t\t\t0,\t\t0,\t\t0,\t\t0,\t D(\"__int64(filestream fhandle, optional __int64 newsize)\", \"Reports the total size of the file, in bytes. Can also be used to truncate/extend the file\")},\n\t{\"strlen\",\t\t\tPF_strlen,\t\t\t0,\t\t0,\t\t0,\t\t114, D(\"float(string s)\", \"Returns the number of bytes in the string not including the null terminator. If utf8_enable is set then returns codepoints instead.\")},\t// (FRIK_FILE)\n\t{\"strcat\",\t\t\tPF_strcat,\t\t\t0,\t\t0,\t\t0,\t\t115, D(\"string(string s1, optional string s2, optional string s3, optional string s4, optional string s5, optional string s6, optional string s7, optional string s8)\", \"Concatenate up to 8 strings. You should consider using sprintf instead - it may be more readable and need fewer args. Returns a tempstring, which may cause issues in other engines.\")},\t// (FRIK_FILE)\n\t{\"substring\",\t\tPF_substring,\t\t0,\t\t0,\t\t0,\t\t116, D(\"string(string s, float start, float length)\", \"Returns a portion of the inputt string. If start is negative then will be treated as relative to the end, if length is negative then it will be interpretted relative to the end of the null terminator (eg -5 to skip the a 3-char filename extension including its dot) [Portability Note: these negative values are part of FTE_STRINGS, not FRIK_FILE et al]. Returns a tempstring, which may cause issues in other engines. When utf8_enable is set then operates on codepoints, but otherwise typically on bytes.\")},\t// (FRIK_FILE)\n\t{\"stov\",\t\t\tPF_stov,\t\t\t0,\t\t0,\t\t0,\t\t117, D(\"vector(string s)\", \"parses 3 space+tab separated floats and returns their values as a vector. optional single-quotes are accepted.\")},\t// (FRIK_FILE)\n#ifdef QCGC\n\t{\"strzone\",\t\t\tPF_strzone,\t\t\t0,\t\t0,\t\t0,\t\t118,\tD(\"FTEDEP(\\\"Redundant\\\") string(string s, ...)\", \"Create a semi-permanent copy of a string that only becomes invalid once strunzone is called on the string (instead of when the engine assumes your string has left scope). This builtin has become redundant in FTEQW due to the FTE_QC_PERSISTENTTEMPSTRINGS extension and is now functionally identical to strcat for compatibility with old engines+mods.\")},\t// (FRIK_FILE)\n\t{\"strunzone\",\t\tPF_strunzone,\t\t0,\t\t0,\t\t0,\t\t119,\tD(\"FTEDEP(\\\"Redundant\\\") void(string s)\", \"Destroys a string that was allocated by strunzone. Further references to the string MAY crash the game. In FTE, this function became redundant and now does nothing.\")},\t// (FRIK_FILE)\n\t{\"createbuffer\",\tPF_createbuffer,\t0,\t\t0,\t\t0,\t\t0,\t\tD(\"void*(int bytes)\", \"Returns a temporary buffer that can be written to / read from. The buffer will be garbage collected and thus cannot be explicitly freed. Tempstrings and buffer references must not be stored into the buffer as the garbage collector will not scan these.\")},\n#else\n\t{\"strzone\",\t\t\tPF_strzone,\t\t\t0,\t\t0,\t\t0,\t\t118,\tD(\"string(string s, ...)\", \"Create a semi-permanent copy of a string that only becomes invalid once strunzone is called on the string (instead of when the engine assumes your string has left scope).\")},\t// (FRIK_FILE)\n\t{\"strunzone\",\t\tPF_strunzone,\t\t0,\t\t0,\t\t0,\t\t119,\tD(\"void(string s)\", \"Destroys a string that was allocated by strunzone. Further references to the string MAY crash the game.\")},\t// (FRIK_FILE)\n#endif\n//end frikfile\n\n//these are telejano's\n\t{\"cvar_setf\",\t\tPF_cvar_setf,\t\t0,\t\t0,\t\t0,\t\t176,\t\"void(string cvar, float val)\"},\n\t{\"localsound\",\t\tPF_ss_LocalSound,\t0,\t\t0,\t\t0,\t\t177,\tD(\"DEP_SSQC(\\\"This bypasses networking by design, so do NOT call this from ssqc. Use ex_localsound or sound(...,CF_UNICAST) instead.\\\") void(string soundname, optional float channel, optional float volume)\", \"Plays a sound... locally... Also disables reverb.\")},//\t#177\n//end telejano\n\n//fte extras\n\n//\t{\"findlist_string\",\tPF_FindListString,\t0,\t\t0,\t\t0,\t\t0,\t\tD(\"entity*(.string fld, string match)\", \"Return a list of entities with the given string field set to the given value.\")},\n//\t{\"findlist_float\",\tPF_FindListFloat,\t0,\t\t0,\t\t0,\t\t0,\t\tD(\"entity*(.__variant fld, __variant match)\", \"Return a list of entities with the given field set to the given value.\")},\n//\t{\"findlist_radius\",\tPF_FindListRadius,\t0,\t\t0,\t\t0,\t\t0,\t\tD(\"entity*(vector pos, float radius)\", \"Return a list of entities with the given string field set to the given value.\")},\n//\t{\"traceboxptr\",\t\tPF_TraceBox,\t\t0,\t\t0,\t\t0,\t\t0,\t\tD(\"typedef struct {\\nfloat allsolid;\\nfloat startsolid;\\nfloat fraction;\\nfloat truefraction;\\nentity ent;\\nvector endpos;\\nvector plane_normal;\\nfloat plane_dist;\\nint surfaceflags;\\nint contents;\\n} trace_t;\\nvoid(trace_t *trace, vector start, vector mins, vector maxs, vector end, float nomonsters, entity forent)\", \"Like regular tracebox, except doesn't doesn't use any evil globals.\")},\n\n\t{\"getmodelindex\",\tPF_getmodelindex,\t0,\t\t0,\t\t0,\t\t200,\tD(\"float(string modelname, optional float queryonly)\", \"Acts as an alternative to precache_model(foo);setmodel(bar, foo); return bar.modelindex;\\nIf queryonly is set and the model was not previously precached, the builtin will return 0 without needlessly precaching the model.\")},\n\t{\"getsoundindex\",\tPF_getsoundindex,\t0,\t\t0,\t\t0,\t\t0,\t\tD(\"float(string soundname, optional float queryonly)\", \"Provides a way to query if a sound is already precached or not. The return value can also be checked for <=255 to see if it'll work over any network protocol. The sound index can also be used for writebyte hacks, but this is discouraged - use SOUNDFLAG_UNICAST instead.\")},\n\t{\"externcall\",\t\tPF_externcall,\t\t0,\t\t0,\t\t0,\t\t201,\tD(\"__variant(float prnum, string funcname, ...)\", \"Directly call a function in a different/same progs by its name.\\nprnum=0 is the 'default' or 'main' progs.\\nprnum=-1 means current progs.\\nprnum=-2 will scan through the active progs and will use the first it finds.\")},\n\t{\"addprogs\",\t\tPF_addprogs,\t\t0,\t\t0,\t\t0,\t\t202,\tD(\"float(string progsname)\", \"Loads an additional .dat file into the current qcvm. The returned handle can be used with any of the externcall/externset/externvalue builtins.\\nThere are cvars that allow progs to be loaded automatically.\")},\n\t{\"externvalue\",\t\tPF_externvalue,\t\t0,\t\t0,\t\t0,\t\t203,\tD(\"__variant(float prnum, string varname)\", \"Reads a global in the named progs by the name of that global.\\nprnum=0 is the 'default' or 'main' progs.\\nprnum=-1 means current progs.\\nprnum=-2 will scan through the active progs and will use the first it finds.\")},\n\t{\"externset\",\t\tPF_externset,\t\t0,\t\t0,\t\t0,\t\t204,\tD(\"void(float prnum, __variant newval, string varname)\", \"Sets a global in the named progs by name.\\nprnum=0 is the 'default' or 'main' progs.\\nprnum=-1 means current progs.\\nprnum=-2 will scan through the active progs and will use the first it finds.\")},\n\t{\"externrefcall\",\tPF_externrefcall,\t0,\t\t0,\t\t0,\t\t205,\tD(\"__deprecated(\\\"Redundant\\\") __variant(float prnum, void() func, ...)\",\"Calls a function between progs by its reference. No longer needed as direct function calls now switch progs context automatically, and have done for a long time. There is no remaining merit for this function.\"), true},\n\t{\"instr\",\t\t\tPF_instr,\t\t\t0,\t\t0,\t\t0,\t\t206,\tD(\"float(string input, string token)\", \"Returns substring(input, strstrpos(input, token), -1), or the null string if token was not found in input. You're probably better off using strstrpos.\"), true},\n\t{\"openportal\",\t\tPF_OpenPortal,\t\t0,\t\t0,\t\t0,\t\t207,\tD(\"void(entity portal, float state)\", \"Opens or closes the portals associated with a door or some such on q2 or q3 maps. On Q2BSPs, the entity should be the 'func_areaportal' entity - its style field will say which portal to open. On Q3BSPs, the entity is the door itself, the portal will be determined by the two areas found from a preceding setorigin call.\")},\n\n\t{\"RegisterTempEnt\", PF_RegisterTEnt,\t0,\t\t0,\t\t0,\t\t208,\t\"float(float attributes, string effectname, ...)\"},\n\t{\"CustomTempEnt\",\tPF_CustomTEnt,\t\t0,\t\t0,\t\t0,\t\t209,\t\"void(float type, vector pos, ...)\"},\n\t{\"fork\",\t\t\tPF_Fork,\t\t\t0,\t\t0,\t\t0,\t\t210,\tD(\"float(optional float sleeptime)\", \"When called, this builtin simply returns. Twice.\\nThe current 'thread' will return instantly with a return value of 0. The new 'thread' will return after sleeptime seconds with a return value of 1. See documentation for the 'sleep' builtin for limitations/requirements concerning the new thread. Note that QC should probably call abort in the new thread, as otherwise the function will return to the calling qc function twice also.\")},\n\t{\"abort\",\t\t\tPF_Abort,\t\t\t0,\t\t0,\t\t0,\t\t211,\tD(\"void(optional __variant ret)\", \"QC execution is aborted. Parent QC functions on the stack will be skipped, effectively this forces all QC functions to 'return ret' until execution returns to the engine. If ret is ommited, it is assumed to be 0.\")},\n\t{\"sleep\",\t\t\tPF_Sleep,\t\t\t0,\t\t0,\t\t0,\t\t212,\tD(\"void(float sleeptime)\", \"Suspends the current QC execution thread for 'sleeptime' seconds.\\nOther QC functions can and will be executed in the interim, including changing globals and field state (but not simultaneously).\\nThe self and other globals will be restored when the thread wakes up (or set to world if they were removed since the thread started sleeping). Locals will be preserved, but will not be protected from remove calls.\\nIf the engine is expecting the QC to return a value (even in the parent/root function), the value 0 shall be used instead of waiting for the qc to resume.\")},\n\t{\"forceinfokey\",\tPF_ForceInfoKey,\t0,\t\t0,\t\t0,\t\t213,\tD(\"void(entity player, string key, string value)\", \"Directly changes a user's info without pinging off the client. Also allows explicitly setting * keys, including *spectator. Does not affect the user's config or other servers.\")},\n\t{\"forceinfokeyblob\",PF_ForceInfoKeyBlob,0,\t\t0,\t\t0,\t\t0,\t\tD(\"void(entity player, string key, void *data, int size)\", \"Directly changes a user's info without pinging off the client. Also allows explicitly setting * keys, including *spectator. Does not affect the user's config or other servers.\")},\n#ifdef SVCHAT\n\t{\"chat\",\t\t\tPF_chat,\t\t\t0,\t\t0,\t\t0,\t\t214,\t\"void(string filename, float starttag, entity edict)\"}, //(FTE_NPCCHAT)\n#endif\n\n#ifdef HEXEN2\n\t{\"particle2\",\t\tPF_particle2,\t\t0,\t\t0,\t\t42,\t\t215,\t\"void(vector org, vector dmin, vector dmax, float colour, float effect, float count)\"},\n\t{\"particle3\",\t\tPF_particle3,\t\t0,\t\t0,\t\t85,\t\t216,\t\"void(vector org, vector box, float colour, float effect, float count)\"},\n\t{\"particle4\",\t\tPF_particle4,\t\t0,\t\t0,\t\t86,\t\t217,\t\"void(vector org, float radius, float colour, float effect, float count)\"},\n#endif\n\n//EXT_DIMENSION_PLANES\n\t{\"bitshift\",\t\tPF_bitshift,\t\t0,\t\t0,\t\t0,\t\t218,\t\"float(float number, float quantity)\"},\n\n//I guess this should go under DP_TE_STANDARDEFFECTBUILTINS...\n\t{\"te_lightningblood\",PF_te_lightningblood,\t0,\t0,\t\t0,\t\t219,\t\"void(vector pos)\"},// #219 te_lightningblood\n\n\t{\"map_builtin\",\t\tPF_builtinsupported,0,\t\t0,\t\t0,\t\t220,\tD(\"float(string builtinname, float builtinnum)\",\"Attempts to map the named builtin at a non-standard builtin number. Returns 0 on failure.\"), true},\t//like #100 - takes 2 args. arg0 is builtinname, 1 is number to map to.\n\n//FTE_STRINGS\n\t{\"strstrofs\",\t\tPF_strstrofs,\t\t0,\t\t0,\t\t0,\t\t221,\tD(\"float(string s1, string sub, optional float startidx)\", \"Returns the 0-based offset of sub within the s1 string, or -1 if sub is not in s1.\\nIf startidx is set, this builtin will ignore matches before that 0-based offset.\")},\n\t{\"str2chr\",\t\t\tPF_str2chr,\t\t\t0,\t\t0,\t\t0,\t\t222,\tD(\"float(string str, float index)\", \"Retrieves the character value at offset 'index'.\")},\n\t{\"chr2str\",\t\t\tPF_chr2str,\t\t\t0,\t\t0,\t\t0,\t\t223,\tD(\"string(float chr, ...)\", \"The input floats are considered character values, and are concatenated.\")},\n\t{\"strconv\",\t\t\tPF_strconv,\t\t\t0,\t\t0,\t\t0,\t\t224,\tD(\"string(float ccase, float redalpha, float redchars, string str, ...)\", \"Converts quake chars in the input string amongst different representations.\\nccase specifies the new case for letters.\\n 0: not changed.\\n 1: forced to lower case.\\n 2: forced to upper case.\\nredalpha and redchars switch between colour ranges.\\n 0: no change.\\n 1: Forced white.\\n 2: Forced red.\\n 3: Forced gold(low) (numbers only).\\n 4: Forced gold (high) (numbers only).\\n 5+6: Forced to white and red alternately.\\nYou should not use this builtin in combination with UTF-8.\")},\n\t{\"strpad\",\t\t\tPF_strpad,\t\t\t0,\t\t0,\t\t0,\t\t225,\tD(\"string(float pad, string str1, ...)\", \"Pads the string with spaces, to ensure its a specific length (so long as a fixed-width font is used, anyway). If pad is negative, the spaces are added on the left. If positive the padding is on the right.\")},\t//will be moved\n\t{\"infoadd\",\t\t\tPF_infoadd,\t\t\t0,\t\t0,\t\t0,\t\t226,\tD(\"infostring(infostring old, string key, string value)\", \"Returns a new tempstring infostring with the named value changed (or added if it was previously unspecified). Key and value may not contain the \\\\ character.\")},\n\t{\"infoget\",\t\t\tPF_infoget,\t\t\t0,\t\t0,\t\t0,\t\t227,\tD(\"string(infostring info, string key)\", \"Reads a named value from an infostring. The returned value is a tempstring\")},\n//\t{\"strcmp\",\t\t\tPF_strncmp,\t\t\t0,\t\t0,\t\t0,\t\t228,\tD(\"float(string s1, string s2)\", \"Compares the two strings exactly. s1ofs allows you to treat s2 as a substring to compare against, or should be 0.\\nReturns 0 if the two strings are equal, a negative value if s1 appears numerically lower, and positive if s1 appears numerically higher.\")},\n\t{\"strncmp\",\t\t\tPF_strncmp,\t\t\t0,\t\t0,\t\t0,\t\t228,\tD(\"#define strcmp strncmp\\nfloat(string s1, string s2, optional float len, optional float s1ofs, optional float s2ofs)\", \"Compares up to 'len' chars in the two strings. s1ofs allows you to treat s2 as a substring to compare against, or should be 0.\\nReturns 0 if the two strings are equal, a negative value if s1 appears numerically lower, and positive if s1 appears numerically higher.\")},\n\t{\"strcasecmp\",\t\tPF_strncasecmp,\t\t0,\t\t0,\t\t0,\t\t229,\tD(\"float(string s1, string s2)\",  \"Compares the two strings without case sensitivity.\\nReturns 0 if they are equal. The sign of the return value may be significant, but should not be depended upon.\")},\n\t{\"strncasecmp\",\t\tPF_strncasecmp,\t\t0,\t\t0,\t\t0,\t\t230,\tD(\"float(string s1, string s2, float len, optional float s1ofs, optional float s2ofs)\", \"Compares up to 'len' chars in the two strings without case sensitivity. s1ofs allows you to treat s2 as a substring to compare against, or should be 0.\\nReturns 0 if they are equal. The sign of the return value may be significant, but should not be depended upon.\")},\n//END FTE_STRINGS\n\t{\"strtrim\",\t\t\tPF_strtrim,\t\t\t0,\t\t0,\t\t0,\t\t0,\t\tD(\"string(string s)\", \"Trims the whitespace from the start+end of the string.\")},\n\n//FTE_CALLTIMEOFDAY\n\t{\"calltimeofday\",\tPF_calltimeofday,\t0,\t\t0,\t\t0,\t\t231,\tD(\"__deprecated(\\\"Use strftime.\\\") void()\", \"Asks the engine to instantly call the qc's 'timeofday' function, before returning. For compatibility with mvdsv.\\ntimeofday should have the prototype: void(float secs, float mins, float hour, float day, float mon, float year, string strvalue)\\nThe strftime builtin is more versatile and less weird.\")},\n\n//EXT_CSQC\n\t{\"clientstat\",\t\tPF_clientstat,\t\t0,\t\t0,\t\t0,\t\t232,\tD(\"void(float num, float type, .__variant fld)\", \"Specifies what data to use in order to send various stats, in a client-specific way.\\n'num' should be a value between 32 and 127, other values are reserved.\\n'type' must be set to one of the EV_* constants, one of EV_FLOAT, EV_STRING, EV_INTEGER, EV_ENTITY.\\nfld must be a reference to the field used, each player will be sent only their own copy of these fields.\")},\t//EXT_CSQC\n\t{\"globalstat\",\t\tPF_globalstat,\t\t0,\t\t0,\t\t0,\t\t233,\tD(\"void(float num, float type, string name)\", \"Specifies what data to use in order to send various stats, in a non-client-specific way. num and type are as in clientstat, name however, is the name of the global to read in the form of a string (pass \\\"foo\\\").\")},\t//EXT_CSQC_1 actually\n\t{\"pointerstat\",\t\tPF_pointerstat,\t\t0,\t\t0,\t\t0,\t\t0,\t\tD(\"void(float num, float type, __variant *address)\", \"Specifies what data to use in order to send various stats, in a non-client-specific way. num and type are as in clientstat, address however, is the address of the variable you would like to use (pass &foo).\")},\n\t{\"setsendneeded\",\tPF_setsendneeded,\t0,\t\t0,\t\t0,\t\t0,\t\tD(\"void(entity ent, vector sendflags, entity unicastplayer)\", \"Flags the entity as needing to be resent. This builtin allows for more bits than supported by the SendEntity field, as well as allows flagging sends to specific players.\")},\n//END EXT_CSQC\n\t{\"isbackbuffered\",\tPF_isbackbuffered,\t0,\t\t0,\t\t0,\t\t234,\tD(\"float(entity player)\", \"Returns if the given player's network buffer will take multiple network frames in order to clear. If this builtin returns non-zero, you should delay or reduce the amount of reliable (and also unreliable) data that you are sending to that client.\")},\n\t{\"rotatevectorsbyangle\",PF_rotatevectorsbyangles,0,0,\t0,\t\t235,\tD(\"void(vector angle)\", \"rotates the v_forward,v_right,v_up matrix by the specified angles.\")}, // #235\n\t{\"rotatevectorsbyvectors\",PF_rotatevectorsbymatrix,0,0,\t0,\t\t236,\t\"void(vector fwd, vector right, vector up)\"}, // #236\n\t{\"skinforname\",\t\tPF_skinforname,\t\t0,\t\t0,\t\t0,\t\t237,\t\"float(float mdlindex, string skinname)\"},\t\t// #237\n\t{\"shaderforname\",\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t238,\tD(\"float(string shadername, optional string defaultshader, ...)\", \"Caches the named shader and returns a handle to it.\\nIf the shader could not be loaded from disk (missing file or ruleset_allow_shaders 0), it will be created from the 'defaultshader' string if specified, or a 'skin shader' default will be used.\\ndefaultshader if not empty should include the outer {} that you would ordinarily find in a shader.\")},\n\t{\"remapshader\",\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t0,\t\tD(\"void(string shadername, string replacement, float timeoffset)\", \"All surfaces drawn with the specified shader will instead be drawn using the specified replacement shader. Shaders can be remapped to something else later by using the same source shadername. This is mostly useful for worldmodel surfaces (eg showing which team is currently winning). Entities should generally use setcustomskin or forceshader instead. Remaps will be forgotten on vid_reload, but can be reapplied via CSQC_RendererRestarted.\")},\n\t{\"te_bloodqw\",\t\tPF_te_bloodqw,\t\t0,\t\t0,\t\t0,\t\t239,\t\"void(vector org, optional float count)\"},\n\t{\"te_muzzleflash\",\tPF_te_muzzleflash,\t0,\t\t0,\t\t0,\t\t0,\t\t\"void(entity ent)\"},\n\n\t{\"checkpvs\",\t\tPF_checkpvs,\t\t0,\t\t0,\t\t0,\t\t240,\t\"float(vector viewpos, entity entity)\"},\n\t{\"matchclientname\",\tPF_matchclient,\t\t0,\t\t0,\t\t0,\t\t241,\t\"entity(string match, optional float matchnum)\"},\n\t{\"sendpacket\",\t\tPF_SendPacket,\t\t0,\t\t0,\t\t0,\t\t242,\tD(\"float(string destaddress, string content)\", \"Sends a UDP packet to the specified destination. Note that the payload will be prefixed with four 255 bytes as a sort of security feature.\")},// (FTE_QC_SENDPACKET)\n\n//\t{\"bulleten\",\t\tPF_bulleten,\t\t0,\t\t0,\t\t0,\t\t243}, (removed builtin)\n\n\t{\"rotatevectorsbytag\",\tPF_Fixme,\t\t0,\t\t0,\t\t0,\t\t244,\t\"vector(entity ent, float tagnum)\"},\n\n\t{\"mod\",\t\t\t\tPF_mod,\t\t\t\t0,\t\t0,\t\t0,\t\t245,\tD(\"float(float dividend, float divisor)\", \"Returns the remainder of a division, so divisor must not be 0. fractional values will give different results. Or just use the % operator...\")},\n//\t{\"empty\",\t\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t245,\t\"void()\"},\n//\t{\"empty\",\t\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t246,\t\"void()\"},\n//\t{\"empty\",\t\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t247,\t\"void()\"},\n//\t{\"empty\",\t\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t248,\t\"void()\"},\n//\t{\"empty\",\t\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t249,\t\"void()\"},\n\n\t{\"sqlconnect\",\t\tPF_sqlconnect,\t\t0,\t\t0,\t\t0,\t\t250,\t\"float(optional string host, optional string user, optional string pass, optional string defaultdb, optional string driver)\"}, // sqlconnect (FTE_SQL)\n\t{\"sqldisconnect\",\tPF_sqldisconnect,\t0,\t\t0,\t\t0,\t\t251,\t\"void(float serveridx)\"}, // sqldisconnect (FTE_SQL)\n\t{\"sqlopenquery\",\tPF_sqlopenquery,\t0,\t\t0,\t\t0,\t\t252,\t\"float(float serveridx, void(float serveridx, float queryidx, float rows, float columns, float eof, float firstrow) callback, float querytype, string query)\"}, // sqlopenquery (FTE_SQL)\n\t{\"sqlclosequery\",\tPF_sqlclosequery,\t0,\t\t0,\t\t0,\t\t253,\t\"void(float serveridx, float queryidx)\"}, // sqlclosequery (FTE_SQL)\n\t{\"sqlreadfield\",\tPF_sqlreadfield,\t0,\t\t0,\t\t0,\t\t254,\t\"string(float serveridx, float queryidx, float row, float column)\"}, // sqlreadfield (FTE_SQL)\n\t{\"sqlerror\",\t\tPF_sqlerror,\t\t0,\t\t0,\t\t0,\t\t255,\t\"string(float serveridx, optional float queryidx)\"}, // sqlerror (FTE_SQL)\n\t{\"sqlescape\",\t\tPF_sqlescape,\t\t0,\t\t0,\t\t0,\t\t256,\t\"string(float serveridx, string data)\"}, // sqlescape (FTE_SQL)\n\t{\"sqlversion\",\t\tPF_sqlversion,\t\t0,\t\t0,\t\t0,\t\t257,\t\"string(float serveridx)\"}, // sqlversion (FTE_SQL)\n\t{\"sqlreadfloat\",\tPF_sqlreadfloat,\t0,\t\t0,\t\t0,\t\t258,\t\"float(float serveridx, float queryidx, float row, float column)\"}, // sqlreadfloat (FTE_SQL)\n\t{\"sqlreadblob\",\t\tPF_sqlreadblob,\t\t0,\t\t0,\t\t0,\t\t0,\t\t\"int(float serveridx, float queryidx, float row, float column, __variant *ptr, int maxsize)\"},\n\t{\"sqlescapeblob\",\tPF_sqlescapeblob,\t0,\t\t0,\t\t0,\t\t0,\t\t\"string(float serveridx, __variant *ptr, int maxsize)\"},\n\n\t//basic API is from Joshua Ashton, though uses FTE's json parser instead.\n\t{\"json_parse\",\t\tPF_json_parse,\t\t0,\t\t0,\t\t0,\t\t0,\t\tD(\"typedef struct json_s *json_t;\\n\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  \"accessor jsonnode : json_t;\\n\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  \"jsonnode(string)\", \"Parses the given JSON string.\")},\n\t{\"json_free\",\t\tPF_memfree,\t\t\t0,\t\t0,\t\t0,\t\t0,\t\tD(\"void(jsonnode)\", \"Frees a json tree and all of its children. Must only be called on the root node.\")},\n\t{\"json_get_value_type\",PF_json_get_value_type,0,0,\t\t0,\t\t0,\t\tD(\"enum json_type_e : int\\n{\\n\\tJSON_TYPE_STRING,\\n\\tJSON_TYPE_NUMBER,\\n\\tJSON_TYPE_OBJECT,\\n\\tJSON_TYPE_ARRAY,\\n\\tJSON_TYPE_TRUE,\\n\\tJSON_TYPE_FALSE,\\n\\tJSON_TYPE_NULL\\n};\\n\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  \"json_type_e(jsonnode node)\", \"Get type of a JSON value.\")},\n\t{\"json_get_integer\",PF_json_get_integer,0,\t\t0,\t\t0,\t\t0,\t\tD(\"int(jsonnode node)\", \"Get an integer from a json node.\")},\n\t{\"json_get_float\",\tPF_json_get_float,\t0,\t\t0,\t\t0,\t\t0,\t\tD(\"float(jsonnode node)\", \"Get a float from a json node.\")},\n\t{\"json_get_string\",\tPF_json_get_string,\t0,\t\t0,\t\t0,\t\t0,\t\tD(\"string(jsonnode node)\", \"Get a string from a value. Returns a null string if its not a string type.\")},\n\t{\"json_find_object_child\",PF_json_find_object_child,0,0,0,\t\t0,\t\tD(\"jsonnode(jsonnode node, string)\", \"Find a child of a json object by name. Returns NULL if the handle couldn't be found.\")},\n\t{\"json_get_length\",\tPF_json_get_length,\t0,\t\t0,\t\t0,\t\t0,\t\tD(\"int(jsonnode node)\", \"Get the length of a json array or object. Returns 0 if its not an array.\")},\n\t{\"json_get_child_at_index\",PF_json_get_child_at_index,0,0,0,\t0,\t\tD(\"jsonnode(jsonnode node, int childindex)\", \"Get the nth child of a json array or object. Returns NULL if the index is out of range.\")},\n\t{\"json_get_name\",\tPF_json_get_name,\t0,\t\t0,\t\t0,\t\t0,\t\tD(\"string(jsonnode node)\", \"Gets the object's name (useful if you're using json_get_child_at_index to walk an object's children for whatever reason).\")},\n\n\t{\"js_run_script\",\tPF_js_run_script,\t0,\t\t0,\t\t0,\t\t0,\t\tD(\"string(string javascript)\", \"Runs the provided javascript snippet. This builtin functions only in emscripten builds, returning a null string on other systems (or if the script evaluates to null).\")},\n\n\t{\"stoi\",\t\t\tPF_stoi,\t\t\t0,\t\t0,\t\t0,\t\t259,\tD(\"int(string)\", \"Converts the given string into a true integer. Base 8, 10, or 16 is determined based upon the format of the string.\")},\n\t{\"itos\",\t\t\tPF_itos,\t\t\t0,\t\t0,\t\t0,\t\t260,\tD(\"string(int)\", \"Converts the passed true integer into a base10 string.\")},\n\t{\"stoh\",\t\t\tPF_stoh,\t\t\t0,\t\t0,\t\t0,\t\t261,\tD(\"int(string)\", \"Reads a base-16 string (with or without 0x prefix) as an integer. Bugs out if given a base 8 or base 10 string. :P\")},\n\t{\"htos\",\t\t\tPF_htos,\t\t\t0,\t\t0,\t\t0,\t\t262,\tD(\"string(int)\", \"Formats an integer as a base16 string, with leading 0s and no prefix. Always returns 8 characters.\")},\n\t{\"ftoi\",\t\t\tPF_ftoi,\t\t\t0,\t\t0,\t\t0,\t\t0,\t\tD(\"int(float)\", \"Converts the given float into a true integer without depending on extended qcvm instructions.\")},\n\t{\"itof\",\t\t\tPF_itof,\t\t\t0,\t\t0,\t\t0,\t\t0,\t\tD(\"float(int, optional float shift, float mask=24)\", \"Converts the given true integer into a float without depending on extended qcvm instructions. If shift and mask are specified then only specific parts of the integer will be cast to float.\")},\n\t{\"ftou\",\t\t\tPF_ftou,\t\t\t0,\t\t0,\t\t0,\t\t0,\t\tD(\"__uint(float)\", \"Converts the given float into a true integer without depending on extended qcvm instructions.\")},\n\t{\"utof\",\t\t\tPF_utof,\t\t\t0,\t\t0,\t\t0,\t\t0,\t\tD(\"float(__uint, optional float shift, float mask=24)\", \"Converts the given true integer into a float without depending on extended qcvm instructions. If shift and mask are specified then only specific parts of the integer will be cast to float.\")},\n\n\t#define qcskelblend\t\t\t\t\\\n\t\"typedef struct\\n{\\n\"\t\t\t\\\n\t\t\"\\tint sourcemodelindex; /*frame data will be imported from this model, bones must be compatible*/\\n\"\t\\\n\t\t\"\\tint reserved;\\n\"\t\t\t\\\n\t\t\"\\tint firstbone;\\n\"\t\t\\\n\t\t\"\\tint lastbone;\\n\"\t\t\t\\\n\t\t\"\\tfloat prescale;\t/*0 destroys existing data, 1 retains it*/\\n\"\\\n\t\t\"\\tfloat scale[4];\t/*you'll need to do lerpfrac manually*/\\n\"\t\t\t\\\n\t\t\"\\tint animation[4];\\n\"\t\t\\\n\t\t\"\\tfloat animationtime[4];\\n\"\t\\\n\t\t\"\\t/*halflife models*/\\n\"\t\\\n\t\t\"\\tfloat subblend[2];\\n\"\t\\\n\t\t\"\\tfloat controllers[5];\\n\"\t\\\n\t\"} skelblend_t;\\n\"\n\n\t{\"skel_create\",\t\tPF_skel_create,\t\t0,\t\t0,\t\t0,\t\t263,\tD(\"float(float modlindex, optional float useabstransforms)\", \"Allocates a new uninitiaised skeletal object, with enough bone info to animate the given model.\\neg: self.skeletonobject = skel_create(self.modelindex);\")}, // (FTE_CSQC_SKELETONOBJECTS)\n\t{\"skel_build\",\t\tPF_skel_build,\t\t0,\t\t0,\t\t0,\t\t264,\tD(\"float(float skel, entity ent, float modelindex, float retainfrac, float firstbone, float lastbone, optional float addfrac)\", \"Animation data (according to the entity's frame info) is pulled from the specified model and blended into the specified skeletal object.\\nIf retainfrac is set to 0 on the first call and 1 on the others, you can blend multiple animations together according to the addfrac value. The final weight should be 1. Other values will result in scaling and/or other weirdness. You can use firstbone and lastbone to update only part of the skeletal object, to allow legs to animate separately from torso, use 0 for both arguments to specify all, as bones are 1-based.\")}, // (FTE_CSQC_SKELETONOBJECTS)\n\t{\"skel_build_ptr\",\tPF_skel_build_ptr,\t0,\t\t0,\t\t0,\t\t0,\t\tD(qcskelblend\"float(float skel, int numblends, skelblend_t *weights, int structsize)\", \"Like skel_build, but slightly simpler.\")},\n\t{\"skel_get_numbones\",PF_skel_get_numbones,0,\t0,\t\t0,\t\t265,\tD(\"float(float skel)\", \"Retrives the number of bones in the model. The valid range is 1<=bone<=numbones.\")}, // (FTE_CSQC_SKELETONOBJECTS)\n\t{\"skel_get_bonename\",PF_skel_get_bonename,0,\t0,\t\t0,\t\t266,\tD(\"string(float skel, float bonenum)\", \"Retrieves the name of the specified bone. Mostly only for debugging.\")}, // (FTE_CSQC_SKELETONOBJECTS) (returns tempstring)\n\t{\"skel_get_boneparent\",PF_skel_get_boneparent,0,0,\t\t0,\t\t267,\tD(\"float(float skel, float bonenum)\", \"Retrieves which bone this bone's position is relative to. Bone 0 refers to the entity's position rather than an actual bone\")}, // (FTE_CSQC_SKELETONOBJECTS)\n\t{\"skel_find_bone\",\tPF_skel_find_bone,\t0,\t\t0,\t\t0,\t\t268,\tD(\"float(float skel, string tagname)\", \"Finds a bone by its name, from the model that was used to create the skeletal object.\")}, // (FTE_CSQC_SKELETONOBJECTS)\n\t{\"skel_get_bonerel\",PF_skel_get_bonerel,0,\t\t0,\t\t0,\t\t269,\tD(\"vector(float skel, float bonenum)\", \"Gets the bone position and orientation relative to the bone's parent. Return value is the offset, and v_forward, v_right, v_up contain the orientation.\")}, // (FTE_CSQC_SKELETONOBJECTS) (sets v_forward etc)\n\t{\"skel_get_boneabs\",PF_skel_get_boneabs,0,\t\t0,\t\t0,\t\t270,\tD(\"vector(float skel, float bonenum)\", \"Gets the bone position and orientation relative to the entity. Return value is the offset, and v_forward, v_right, v_up contain the orientation.\\nUse gettaginfo for world coord+orientation.\")}, // (FTE_CSQC_SKELETONOBJECTS) (sets v_forward etc)\n\t{\"skel_set_bone\",\tPF_skel_set_bone,\t0,\t\t0,\t\t0,\t\t271,\tD(\"void(float skel, float bonenum, vector org, optional vector fwd, optional vector right, optional vector up)\", \"Sets a bone position relative to its parent. If the orientation arguments are not specified, v_forward+v_right+v_up are used instead.\")}, // (FTE_CSQC_SKELETONOBJECTS) (reads v_forward etc)\n\t{\"skel_premul_bone\",PF_skel_premul_bone,0,\t\t0,\t\t0,\t\t272,\tD(\"void(float skel, float bonenum, vector org, optional vector fwd, optional vector right, optional vector up)\", \"Transforms a single bone by a matrix. You can use makevectors to generate a rotation matrix from an angle.\")}, // (FTE_CSQC_SKELETONOBJECTS) (reads v_forward etc)\n\t{\"skel_premul_bones\",PF_skel_premul_bones,0,\t0,\t\t0,\t\t273,\tD(\"void(float skel, float startbone, float endbone, vector org, optional vector fwd, optional vector right, optional vector up)\", \"Transforms an entire consecutive range of bones by a matrix. You can use makevectors to generate a rotation matrix from an angle, but you'll probably want to divide the angle by the number of bones.\")}, // (FTE_CSQC_SKELETONOBJECTS) (reads v_forward etc)\n\t{\"skel_postmul_bone\",PF_skel_postmul_bone,0,\t0,\t\t0,\t\t0,\t\tD(\"void(float skel, float bonenum, vector org, optional vector fwd, optional vector right, optional vector up)\", \"Transforms a single bone by a matrix. You can use makevectors to generate a rotation matrix from an angle.\")}, // (FTE_CSQC_SKELETONOBJECTS) (reads v_forward etc)\n//\t{\"skel_postmul_bones\",PF_skel_postmul_bones,0,\t0,\t\t0,\t\t0,\t\tD(\"void(float skel, float startbone, float endbone, vector org, optional vector fwd, optional vector right, optional vector up)\", \"Transforms an entire consecutive range of bones by a matrix. You can use makevectors to generate a rotation matrix from an angle, but you'll probably want to divide the angle by the number of bones.\")}, // (FTE_CSQC_SKELETONOBJECTS) (reads v_forward etc)\n\t{\"skel_copybones\",\tPF_skel_copybones,\t0,\t\t0,\t\t0,\t\t274,\tD(\"void(float skeldst, float skelsrc, float startbone, float entbone)\", \"Copy bone data from one skeleton directly into another.\")}, // (FTE_CSQC_SKELETONOBJECTS)\n\t{\"skel_delete\",\t\tPF_skel_delete,\t\t0,\t\t0,\t\t0,\t\t275,\tD(\"void(float skel)\", \"Deletes a skeletal object. The actual delete is delayed, allowing the skeletal object to be deleted in an entity's predraw function yet still be valid by the time the addentity+renderscene builtins need it. Also uninstanciates any ragdoll currently in effect on the skeletal object.\")}, // (FTE_CSQC_SKELETONOBJECTS)\n\t{\"frameforname\",\tPF_frameforname,\t0,\t\t0,\t\t0,\t\t276,\tD(\"float(float modidx, string framename)\", \"Looks up a framegroup from a model by name, avoiding the need for hardcoding. Returns -1 on error.\")},// (FTE_CSQC_SKELETONOBJECTS)\n\t{\"frameduration\",\tPF_frameduration,\t0,\t\t0,\t\t0,\t\t277,\tD(\"float(float modidx, float framenum)\", \"Retrieves the duration (in seconds) of the specified framegroup.\")},// (FTE_CSQC_SKELETONOBJECTS)\n\t{\"frameforaction\",\tPF_frameforaction,\t0,\t\t0,\t\t0,\t\t0,\t\tD(\"float(float modidx, int actionid)\", \"Returns a random frame/animation for the specified mod-defined action, or -1 if no animations have the specified action.\")},\n\t{\"processmodelevents\",PF_processmodelevents,0,\t0,\t\t0,\t\t0,\t\tD(\"void(float modidx, float framenum, __inout float basetime, float targettime, void(float timestamp, int code, string data) callback)\", \"Calls a callback for each event that has been reached. Basetime is set to targettime.\")},\n\t{\"getnextmodelevent\",PF_getnextmodelevent,0,\t0,\t\t0,\t\t0,\t\tD(\"float(float modidx, float framenum, __inout float basetime, float targettime, __out int code, __out string data)\", \"Reports the next event within a model's animation. Returns a boolean if an event was found between basetime and targettime. Writes to basetime,code,data arguments (if an event was found, basetime is set to the event's time, otherwise to targettime).\\nWARNING: this builtin cannot deal with multiple events with the same timestamp (only the first will be reported).\")},\n\t{\"getmodeleventidx\",PF_getmodeleventidx,0,\t\t0,\t\t0,\t\t0,\t\tD(\"float(float modidx, float framenum, int eventidx, __out float timestamp, __out int code, __out string data)\", \"Reports an indexed event within a model's animation. Writes to timestamp,code,data arguments on success. Returns false if the animation/event/model was out of range/invalid. Does not consider looping animations (retry from index 0 if it fails and you know that its a looping animation). This builtin is more annoying to use than getnextmodelevent, but can be made to deal with multiple events with the exact same timestamp.\")},\n\t{\"crossproduct\",\tPF_crossproduct,\t0,\t\t0,\t\t0,\t\t0,\t\tD(\"#define dotproduct(v1,v2) ((vector)(v1)*(vector)(v2))\\nvector(vector v1, vector v2)\", \"Small helper function to calculate the crossproduct of two vectors.\")},\n\t{\"pushmove\", \t\tPF_pushmove, \t\t0, \t\t0,\t\t0, \t\t0, \t\t\"float(entity pusher, vector move, vector amove)\"},\n#ifdef TERRAIN\n\t{\"terrain_edit\",\tPF_terrain_edit,\t0,\t\t0,\t\t0,\t\t278,\tD(\"__variant(float action, optional vector pos, optional float radius, optional float quant, ...)\", \"Realtime terrain editing. Actions are the TEREDIT_ constants.\")},// (??FTE_TERRAIN_EDIT??\n\n#define qcbrushface\t\t\t\t\t\\\n\t\"typedef struct\\n{\\n\"\t\t\t\\\n\t\t\"\\tstring\\tshadername;\\n\"\t\\\n\t\t\"\\tvector\\tplanenormal;\\n\"\t\\\n\t\t\"\\tfloat\\tplanedist;\\n\"\t\t\\\n\t\t\"\\tvector\\tsdir;\\n\"\t\t\t\\\n\t\t\"\\tfloat\\tsbias;\\n\"\t\t\t\\\n\t\t\"\\tvector\\ttdir;\\n\"\t\t\t\\\n\t\t\"\\tfloat\\ttbias;\\n\"\t\t\t\\\n\t\"} brushface_t;\\n\"\n\t{\"brush_get\",\t\tPF_brush_get,\t\t0,\t\t0,\t\t0,\t\t0,\t\tD(qcbrushface \"int(float modelidx, int brushid, brushface_t *out_faces, int maxfaces, int *out_contents)\", \"Queries a brush's information. You must pre-allocate the face array for the builtin to write to. Return value is the number of faces retrieved, 0 on error.\")},\n\t{\"brush_create\",\tPF_brush_create,\t0,\t\t0,\t\t0,\t\t0,\t\tD(\"int(float modelidx, brushface_t *in_faces, int numfaces, int contents, optional int brushid)\", \"Inserts a new brush into the model. Return value is the new brush's id.\")},\n\t{\"brush_delete\",\tPF_brush_delete,\t0,\t\t0,\t\t0,\t\t0,\t\tD(\"void(float modelidx, int brushid)\", \"Destroys the specified brush.\")},\n\t{\"brush_selected\",\tPF_brush_selected,\t0,\t\t0,\t\t0,\t\t0,\t\tD(\"float(float modelid, int brushid, int faceid, float selectedstate)\", \"Allows you to easily set transient visual properties of a brush. returns old value. selectedstate=-1 changes nothing (called for its return value).\")},\n\t{\"brush_getfacepoints\",PF_brush_getfacepoints,0,0,\t\t0,\t\t0,\t\tD(\"int(float modelid, int brushid, int faceid, vector *points, int maxpoints)\", \"Returns the list of verticies surrounding the given face. If face is 0, returns the center of the brush (if space for 1 point) or the mins+maxs (if space for 2 points).\")},\n\t{\"brush_calcfacepoints\",PF_brush_calcfacepoints,0,0,\t0,\t\t0,\t\tD(\"int(int faceid, brushface_t *in_faces, int numfaces, vector *points, int maxpoints)\", \"Determines the points of the specified face, if the specified brush were to actually be created.\")},\n\t{\"brush_findinvolume\",PF_brush_findinvolume,0,\t0,\t\t0,\t\t0,\t\tD(\"int(float modelid, vector *planes, float *dists, int numplanes, int *out_brushes, int *out_faces, int maxresults)\", \"Allows you to easily obtain a list of brushes+faces within the given bounding region. If out_faces is not null, the same brush might be listed twice.\")},\n//\t{\"brush_editplane\",\tPF_brush_editplane,\t0,\t\t0,\t\t0,\t\t0,\t\tD(\"float(float modelid, int brushid, int faceid, in brushface *face)\", \"Changes a surface's texture info.\")},\n//\t{\"brush_transformselected\",PF_brush_transformselected,0,0,0,\t0,\t\tD(\"int(float modelid, int brushid, float *matrix)\", \"Transforms selected brushes by the given transform\")},\n\n#define qcpatchvert\t\t\t\t\t\\\n\t\"typedef struct\\n{\\n\"\t\t\t\\\n\t\t\"\\tstring shadername;\\n\"\t\\\n\t\t\"\\tint contents;\\n\"\t\t\t\\\n\t\t\"\\tint cpwidth;\\n\"\t\t\t\\\n\t\t\"\\tint cpheight;\\n\"\t\t\t\\\n\t\t\"\\tint tesswidth;\\n\"\t\t\\\n\t\t\"\\tint tessheight;\\n\"\t\t\\\n\t\t\"\\tvector texinfo;/*scalex,y,rot*/\\n\"\t\t\\\n\t\"} patchinfo_t;\\n\"\t\t\t\t\\\n\t\"typedef struct\\n{\\n\"\t\t\t\\\n\t\t\"\\tvector xyz;\\n\"\t\t\t\\\n\t\t\"\\tvector rgb; float a;\\n\"\t\\\n\t\t\"\\tfloat s, t;\\n\"\t\t\t\\\n\t\"} patchvert_t;\\n\"\t\t\t\t\\\n\t\"#define patch_delete(modelidx,patchidx) brush_delete(modelidx,patchidx)\\n\"\n\t{\"patch_getcp\",\t\tPF_patch_getcp,\t\t0,\t\t0,\t\t0,\t\t0,\t\tD(qcpatchvert \"int(float modelidx, int patchid, patchvert_t *out_controlverts, int maxcp, patchinfo_t *out_info)\", \"Queries a patch's information. You must pre-allocate the face array for the builtin to write to. Return value is the total number of control verts that were retrieved, 0 on error.\")},\n\t{\"patch_getmesh\",\tPF_patch_getmesh,\t0,\t\t0,\t\t0,\t\t0,\t\tD(\"int(float modelidx, int patchid, patchvert_t *out_verts, int maxverts, patchinfo_t *out_info)\", \"Queries a patch's information. You must pre-allocate the face array for the builtin to write to. Return value is the total number of control verts that were retrieved, 0 on error.\")},\n\t{\"patch_create\",\tPF_patch_create,\t0,\t\t0,\t\t0,\t\t0,\t\tD(\"int(float modelidx, int oldpatchid, patchvert_t *in_controlverts, patchinfo_t in_info)\", \"Inserts a new patch into the model. Return value is the new patch's id.\")},\n\t{\"patch_evaluate\",\tPF_patch_evaluate,\t0,\t\t0,\t\t0,\t\t0,\t\tD(\"int(patchvert_t *in_controlverts, patchvert_t *out_renderverts, int maxout, patchinfo_t *inout_info)\", \"Calculates the geometry of a hyperthetical patch.\")},\n#endif\n\n#ifdef ENGINE_ROUTING\n#define qcnodeslist\t\t\t\\\n\t\"typedef struct\\n{\\n\"\t\\\n\t\t\"\\tvector dest;\\n\"\t\\\n\t\t\"\\tint linkflags;\\n\"\\\n\t\t\"\\tfloat radius;\\n\"\\\n\t\"} nodeslist_t;\\n\"\n\t{\"route_calculate\",\tPF_route_calculate,0,\t\t0,\t\t0,\t\t0,\t\tD(qcnodeslist \"void(entity ent, vector dest, int denylinkflags, void(entity ent, vector dest, int numnodes, nodeslist_t *nodelist) callback)\", \"Begin calculating a route. The callback function will be called once the route has finished being calculated. The route must be memfreed once it is no longer needed. The route must be followed in reverse order (ie: the first node that must be reached is at index numnodes-1). If no route is available then the callback will be called with no nodes.\")},\n#endif\n\n\t{\"touchtriggers\",\tPF_touchtriggers,\t0,\t\t0,\t\t0,\t\t279,\tD(\"void(optional entity ent, optional vector neworigin)\", \"Triggers a touch events between self and every SOLID_TRIGGER entity that it is in contact with. This should typically just be the triggers touch functions. Also optionally updates the origin of the moved entity.\")},//\n\t{\"WriteFloat\",\t\tPF_WriteFloat,\t\t0,\t\t0,\t\t0,\t\t280,\tD(\"void(float buf, float fl)\", \"Writes a full 32bit float without any data conversions at all, for full precision.\")},//\n\t{\"WriteDouble\",\t\tPF_WriteDouble,\t\t0,\t\t0,\t\t0,\t\t0,\t\tD(\"void(float buf, __double dbl)\", \"Writes a full 64bit double-precision float without any data conversions at all, for excessive precision.\")},//\n\t{\"WriteInt\",\t\tPF_WriteInt,\t\t0,\t\t0,\t\t0,\t\t0,\t\tD(\"void(float buf, int fl)\", \"Writes all 4 bytes of a 32bit integer without truncating to a float first before converting back to an int (unlike WriteLong does, but otherwise equivelent).\")},//\n\t{\"WriteUInt\",\t\tPF_WriteInt,\t\t0,\t\t0,\t\t0,\t\t0,\t\tD(\"void(float buf, __uint fl)\", \"Writes all 4 bytes of a 32bit unsigned integer without truncating to a float first.\")},//\n\t{\"WriteInt64\",\t\tPF_WriteInt64,\t\t0,\t\t0,\t\t0,\t\t0,\t\tD(\"void(float buf, __int64 fl)\", \"Writes all 8 bytes of a 64bit integer. This uses variable-length coding and will send only a single byte for any value between -64 and 63.\")},//\n\t{\"WriteUInt64\",\t\tPF_WriteUInt64,\t\t0,\t\t0,\t\t0,\t\t0,\t\tD(\"void(float buf, __uint64 fl)\", \"Writes all 8 bytes of a 64bit unsigned integer. Values between 0-127 will be sent in a single byte.\")},//\n\t{\"skel_ragupdate\",\tPF_skel_ragedit,\t0,\t\t0,\t\t0,\t\t281,\tD(\"float(entity skelent, string dollcmd, float animskel)\", \"Updates the skeletal object attached to the entity according to its origin and other properties.\\nif animskel is non-zero, the ragdoll will animate towards the bone state in the animskel skeletal object, otherwise they will pick up the model's base pose which may not give nice results.\\nIf dollcmd is not set, the ragdoll will update (this should be done each frame).\\nIf the doll is updated without having a valid doll, the model's default .doll will be instanciated.\\ncommands:\\n doll foo.doll : sets up the entity to use the named doll file\\n dollstring TEXT : uses the doll file directly embedded within qc, with that extra prefix.\\n cleardoll : uninstanciates the doll without destroying the skeletal object.\\n animate 0.5 : specifies the strength of the ragdoll as a whole \\n animatebody somebody 0.5 : specifies the strength of the ragdoll on a specific body (0 will disable ragdoll animations on that body).\\n enablejoint somejoint 1 : enables (or disables) a joint. Disabling joints will allow the doll to shatter.\")}, // (FTE_CSQC_RAGDOLL)\n\t{\"skel_mmap\",\t\tPF_skel_mmap,\t\t0,\t\t0,\t\t0,\t\t282,\tD(\"float*(float skel)\", \"Map the bones in VM memory. They can then be accessed via pointers. Each bone is 12 floats, the four vectors interleaved (sadly).\")},// (FTE_QC_RAGDOLL)\n\t{\"skel_set_bone_world\",PF_skel_set_bone_world,0,0,\t\t0,\t\t283,\tD(\"void(entity ent, float bonenum, vector org, optional vector angorfwd, optional vector right, optional vector up)\", \"Sets the world position of a bone within the given entity's attached skeletal object. The world position is dependant upon the owning entity's position. If no orientation argument is specified, v_forward+v_right+v_up are used for the orientation instead. If 1 is specified, it is understood as angles. If 3 are specified, they are the forawrd/right/up vectors to use.\")},\n\t{\"frametoname\",\t\tPF_frametoname,\t\t0,\t\t0,\t\t0,\t\t284,\t\"string(float modidx, float framenum)\"},\n\t{\"skintoname\",\t\tPF_skintoname,\t\t0,\t\t0,\t\t0,\t\t285,\t\"string(float modidx, float skin)\"},\n\t{\"resourcestatus\",\tPF_resourcestatus,\t0,\t\t0,\t\t0,\t\t286,\tD(\"float(float resourcetype, float tryload, string resourcename)\", \"resourcetype must be one of the RESTYPE_ constants. Returns one of the RESSTATE_ constants. Tryload 0 is a query only. Tryload 1 will attempt to reload the content if it was flushed.\")},\n\t{\"hash_createtab\",\tPF_hash_createtab,\t0,\t\t0,\t\t0,\t\t287,\tD(\"hashtable(float tabsize, optional float defaulttype)\", \"Creates a hash table object.\\nThe tabsize argument is a performance hint and should generally be set to something similar to the number of entries expected, typically a power of two assumption. Too high simply wastes memory, too low results in extra string compares but no actual bugs.\\ndefaulttype must be one of the EV_* values, if specified.\\nThe hash table with index 0 is a game-persistant table and will NEVER be returned by this builtin (except as an error return).\")},\n\t{\"hash_destroytab\",\tPF_hash_destroytab,\t0,\t\t0,\t\t0,\t\t288,\tD(\"void(hashtable table)\", \"Destroys a hash table object.\")},\n\t{\"hash_add\",\t\tPF_hash_add,\t\t0,\t\t0,\t\t0,\t\t289,\tD(\"void(hashtable table, string name, __variant value, optional float typeandflags)\", \"Adds the given key with the given value to the table.\\nIf flags&HASH_REPLACE, the old value will be removed, otherwise if flags&HASH_ADD then a duplicate entry will be added with a second value (can be obtained via hash_get's index argument).\\nThe type argument describes how the value should be stored in saved games, as well as providing constraints with the hash_get function. While you can claim that all variables are just vectors, being more precise can result in less issues with tempstrings or saved games - be sure to be explicit with EV_STRING where appropriate because tempstrings may be reclaimed before the get (especially with saved games or table 0).\")},\n\t{\"hash_get\",\t\tPF_hash_get,\t\t0,\t\t0,\t\t0,\t\t290,\tD(\"__variant(hashtable table, string name, optional __variant deflt, optional float requiretype, optional float index)\",\"Looks up the specified key name in the hash table. Returns deflt if the key was not found.\\nIf requiretype is specified then the function will only consider entries of the matching type (allowing you to store both flags+strings under a single name without getting confused).\\nIf index is specified then the function will ignore the first N entries with the same key (applicable only with entries added using HASH_ADD, not HASH_REPLACE), allowing you to store multiple entries. Keep querying higher indexes starting from 0 until it returns the deflt value.\\nYou will usually need to cast the result of this function to a real datatype.\")},\n\t{\"hash_delete\",\t\tPF_hash_delete,\t\t0,\t\t0,\t\t0,\t\t291,\tD(\"__variant(hashtable table, string name)\", \"removes the named key. returns the value of the object that was destroyed, or 0 on error.\")},\n\t{\"hash_getkey\",\t\tPF_hash_getkey,\t\t0,\t\t0,\t\t0,\t\t292,\tD(\"string(hashtable table, float idx)\", \"gets some random key name. add+delete can change return values of this, so don't blindly increment the key index if you're removing all.\")},\n\t{\"hash_getcb\",\t\tPF_hash_getcb,\t\t0,\t\t0,\t\t0,\t\t293,\tD(\"void(hashtable table, void(string keyname, __variant val) callback, optional string name)\", \"For each item in the table that matches the name, call the callback. if name is omitted, will enumerate ALL keys.\"), true},\n\t{\"checkcommand\",\tPF_checkcommand,\t0,\t\t0,\t\t0,\t\t294,\tD(\"float(string name)\", \"Checks to see if the supplied name is a valid command, cvar, or alias. Returns 0 if it does not exist.\")},\n\t{\"argescape\",\t\tPF_argescape,\t\t0,\t\t0,\t\t0,\t\t295,\tD(\"string(string s)\", \"Marks up a string so that it can be reliably tokenized as a single argument later.\")},\n//\t{\"cvar_setlatch\",\tPF_cvar_setlatch,\t0,\t\t0,\t\t0,\t\t???,\t\"void(string cvarname, optional string value)\"},\n\t{\"clusterevent\",\tPF_clusterevent,\t0,\t\t0,\t\t0,\t\t0,\t\tD(\"void(string dest, string from, string cmd, string info)\", \"Only functions in mapcluster mode. Sends an event to whichever server the named player is on. The destination server can then dispatch the event to the client or handle it itself via the SV_ParseClusterEvent entrypoint. If dest is empty, the event is broadcast to ALL servers. If the named player can't be found, the event will be returned to this server with the cmd prefixed with 'error:'.\")},\n\t{\"clustertransfer\",\tPF_clustertransfer,\t0,\t\t0,\t\t0,\t\t0,\t\tD(\"string(entity player, optional string newnode)\", \"Only functions in mapcluster mode. Initiate transfer of the player to a different node. Can take some time. If dest is specified, returns null on error. Otherwise returns the current/new target node (or null if not transferring).\")},\n\t{\"modelframecount\", PF_modelframecount, 0,\t\t0,\t\t0,\t\t0,\t\tD(\"float(float mdlidx)\", \"Retrieves the number of frames in the specified model.\")},\n\n\t{\"clearscene\",\t\tPF_Fixme,\t0,\t\t0,\t\t0,\t\t300,\tD(\"void()\", \"Forgets all rentities, polygons, and temporary dlights. Resets all view properties to their default values.\")},// (EXT_CSQC)\n\t{\"addentities\",\t\tPF_Fixme,\t0,\t\t0,\t\t0,\t\t301,\tD(\"void(float mask)\", \"Walks through all entities effectively doing this:\\n if (ent.drawmask&mask){ if (!ent.predaw()) addentity(ent); }\\nIf mask&MASK_DELTA, non-csqc entities, particles, and related effects will also be added to the rentity list.\\n If mask&MASK_STDVIEWMODEL then the default view model will also be added.\")},// (EXT_CSQC)\n\t{\"addentity\",\t\tPF_Fixme,\t0,\t\t0,\t\t0,\t\t302,\tD(\"void(entity ent)\", \"Copies the entity fields into a new rentity for later rendering via addscene.\")},// (EXT_CSQC)\n\t{\"addentity_lighting\",PF_Fixme,\t0,\t\t0,\t\t0,\t\t0,\t\tD(\"void(entity ent, vector lightdir, vector lightavg, vector lightrange, int reserved1=0,void*reserved2=0)\", \"Copies the entity fields into a new rentity for later rendering via addscene, but with explicit lighting info.\")},\n\t{\"removeentity\",\tPF_Fixme,\t0,\t\t0,\t\t0,\t\t0,\t\tD(\"void(entity ent)\", \"Undoes all addentities added to the scene from the given entity, without removing ALL entities (useful for splitscreen/etc, readd modified versions as desired).\")},\n\t{\"addtrisoup_simple\",PF_Fixme,\t0,\t\t0,\t\t0,\t\t0,\t\tD(\"typedef float vec2[2];\\ntypedef float vec3[3];\\ntypedef float vec4[4];\\ntypedef struct trisoup_simple_vert_s {vec3 xyz;vec2 st;vec4 rgba;} trisoup_simple_vert_t;\\nvoid(string texturename, int flags, struct trisoup_simple_vert_s *verts, int *indexes, int numindexes)\", \"Adds the specified trisoup into the scene as additional geometry. This permits caching geometry to reduce builtin spam. Indexes are a triangle list (so eg quads will need 6 indicies to form two triangles). NOTE: this is not going to be a speedup over polygons if you're still generating lots of new data every frame.\")},\n\t{\"setproperty\",\t\tPF_Fixme,\t0,\t\t0,\t\t0,\t\t303,\tD(\"#define setviewprop setproperty\\nfloat(float property, ...)\", \"Allows you to override default view properties like viewport, fov, and whether the engine hud will be drawn. Different VF_ values have slightly different arguments, some are vectors, some floats.\")},// (EXT_CSQC)\n\t{\"renderscene\",\t\tPF_Fixme,\t0,\t\t0,\t\t0,\t\t304,\tD(\"void()\", \"Draws all entities, polygons, and particles on the rentity list (which were added via addentities or addentity), using the various view properties set via setproperty. There is no ordering dependancy.\\nThe scene must generally be cleared again before more entities are added, as entities will persist even over to the next frame.\\nYou may call this builtin multiple times per frame, but should only be called from CSQC_UpdateView.\")},// (EXT_CSQC)\n\n\t{\"dynamiclight_add\",PF_Fixme,\t0,\t\t0,\t\t0,\t\t305,\tD(\"float(vector org, float radius, vector lightcolours, optional float style, optional string cubemapname, optional float pflags)\", \"Adds a temporary dlight, ready to be drawn via addscene. Cubemap orientation will be read from v_forward/v_right/v_up.\")},// (EXT_CSQC)\n\n\t//gonna expose these to ssqc as a debugging extension\n\t{\"R_BeginPolygon\",\tPF_R_PolygonBegin,0,0,\t\t0,\t\t306,\tD(\"void(string texturename, optional float flags, optional float is2d)\", \"Specifies the shader to use for the following polygons, along with optional flags.\\nIf is2d, the polygon will be drawn as soon as the EndPolygon call is made, rather than waiting for renderscene. This allows complex 2d effects.\")},// (EXT_CSQC_???)\n\t{\"R_PolygonVertex\",\tPF_R_PolygonVertex,0,0,\t\t0,\t\t307,\tD(\"void(vector org, vector texcoords, vector rgb, float alpha)\", \"Specifies a polygon vertex with its various properties.\")},// (EXT_CSQC_???)\n\t{\"R_EndPolygon\",\tPF_R_PolygonEnd,0,\t0,\t\t0,\t\t308,\tD(\"void()\", \"Ends the current polygon. At least 3 verticies must have been specified. You do not need to call beginpolygon again if you wish to draw another polygon with the same shader.\")},\n\t{\"R_EndPolygonRibbon\",PF_Fixme,\t0,\t\t0,\t\t0,\t\t0,\t\tD(\"void(float radius, vector texcoordbias)\", \"Ends the current primitive and duplicates each vertex sideways into a ribbon. The texcoordbias will be added to each duplicated vertex allowing for regular 2d textures. At least 2 verticies must have been specified. You do not need to call beginpolygon again if you wish to draw another polygon with the same shader.\")},\n\n\t{\"getproperty\",\t\tPF_Fixme,\t0,\t\t0,\t\t0,\t\t309,\tD(\"#define getviewprop getproperty\\n__variant(float property)\", \"Retrieve a currently-set (typically view) property, allowing you to read the current viewport or other things. Due to cheat protection, certain values may be unretrievable.\")},// (EXT_CSQC_1)\n\n//310\n//maths stuff that uses the current view settings.\n\t{\"unproject\",\t\tPF_Fixme,\t0,\t\t0,\t\t0,\t\t310,\tD(\"vector (vector v)\", \"Transform a 2d screen-space point (with depth) into a 3d world-space point, according the various origin+angle+fov etc settings set via setproperty.\")},// (EXT_CSQC)\n\t{\"project\",\t\t\tPF_Fixme,\t0,\t\t0,\t\t0,\t\t311,\tD(\"vector (vector v)\", \"Transform a 3d world-space point into a 2d screen-space point, according the various origin+angle+fov etc settings set via setproperty.\")},// (EXT_CSQC)\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t//312\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t//313\n//2d (immediate) operations\n\t{\"drawtextfield\",\tPF_Fixme,\t0,\t\t0,\t\t0,\t 0/*314*/,\tD(\"float(vector pos, vector size, float alignflags, string text)\", \"Draws a multi-line block of text, including word wrapping and alignment. alignflags bits are RTLB, typically 3. Returns the total number of lines.\")},// (EXT_CSQC)\n\t{\"drawline\",\t\tPF_Fixme,\t0,\t\t0,\t\t0,\t\t315,\tD(\"void(float width, vector pos1, vector pos2, vector rgb, float alpha, optional float drawflag)\", \"Draws a 2d line between the two 2d points.\")},// (EXT_CSQC)\n\t{\"iscachedpic\",\t\tPF_Fixme,\t0,\t\t0,\t\t0,\t\t316,\tD(\"float(string name)\", \"Checks to see if the image is currently loaded. Engines might lie, or cache between maps.\")},// (EXT_CSQC)\n\t{\"precache_pic\",\tPF_Fixme,\t0,\t\t0,\t\t0,\t\t317,\tD(\"string(string name, optional float flags)\", \"Forces the engine to load the named image. Flags are a bitmask of the PRECACHE_PIC_* flags.\")},// (EXT_CSQC)\n\t{\"r_uploadimage\",\tPF_Fixme,\t0,\t\t0,\t\t0,\t\t0,\t\tD(\"void(string imagename, int width, int height, void *pixeldata, optional int datasize, optional int format)\", \"Updates a texture with the specified rgba data (uploading it to the gpu). Will be created if needed. If datasize is specified then the image is decoded (eg .ktx or .dds data) instead of being raw R8G8B8A data. You'll typically want shaderforname to also generate a shader to use the texture.\")},\n\t{\"r_readimage\",\t\tPF_Fixme,\t0,\t\t0,\t\t0,\t\t0,\t\tD(\"int*(string filename, __out int width, __out int height, __out int format)\", \"Reads and decodes an image from disk, providing raw R8G8B8A8 pixel data. Should not be used for dds or ktx etc formats. Returns __NULL__ if the image could not be read for any reason. Use memfree to free the data once you're done with it.\")},\n\t{\"drawgetimagesize\",PF_Fixme,\t0,\t\t0,\t\t0,\t\t318,\tD(\"#define draw_getimagesize drawgetimagesize\\nvector(string picname)\", \"Returns the dimensions of the named image. Images specified with .lmp should give the original .lmp's dimensions even if texture replacements use a different resolution. WARNING: this function may be slow if used without or directly after its initial precache_pic.\")},// (EXT_CSQC)\n\t{\"freepic\",\t\t\tPF_Fixme,\t0,\t\t0,\t\t0,\t\t319,\tD(\"void(string name)\", \"Tells the engine that the image is no longer needed. The image will appear to be new the next time its needed.\")},// (EXT_CSQC)\n\t{\"spriteframe\",\t\tPF_Fixme,\t0,\t\t0,\t\t0,\t\t0,\t\tD(\"string(string modelname, int frame, float frametime)\", \"Obtains a suitable shader name to draw a sprite's shader via drawpic/R_BeginPolygon/etc, instead of needing to create a scene.\")},\n//320\n\t{\"drawcharacter\",\tPF_Fixme,\t0,\t\t0,\t\t0,\t\t320,\tD(\"float(vector position, float character, vector size='8 8', vector rgb='1 1 1', float alpha=1, optional float drawflag=0)\", \"Draw the given quake character at the given position.\\nIf flag&4, the function will consider the char to be a unicode char instead (or display as a ? if outside the 32-127 range).\\nsize should normally be something like '8 8 0'.\\nrgb should normally be '1 1 1'\\nalpha normally 1.\\nSoftware engines may assume the named defaults.\\nNote that ALL text may be rescaled on the X axis due to variable width fonts. The X axis may even be ignored completely.\")},// (EXT_CSQC, [EXT_CSQC_???])\n\t{\"drawrawstring\",\tPF_Fixme,\t0,\t\t0,\t\t0,\t\t321,\tD(\"float(vector position, string text, vector size, vector rgb, float alpha, optional float drawflag)\", \"Draws the specified string without using any markup at all, even in engines that support it.\\nIf UTF-8 is globally enabled in the engine, then that encoding is used (without additional markup), otherwise it is raw quake chars.\\nSoftware engines may assume a size of '8 8 0', rgb='1 1 1', alpha=1, flag&3=0, but it is not an error to draw out of the screen.\")},// (EXT_CSQC, [EXT_CSQC_???])\n\t{\"drawpic\",\t\t\tPF_Fixme,\t0,\t\t0,\t\t0,\t\t322,\tD(\"float(vector position, string pic, vector size, vector rgb='1 1 1', float alpha=1, optional float drawflag=0)\", \"Draws an shader within the given 2d screen box. Software engines may omit support for rgb+alpha, but must support rescaling, and must clip to the screen without crashing.\")},// (EXT_CSQC, [EXT_CSQC_???])\n\t{\"drawfill\",\t\tPF_Fixme,\t0,\t\t0,\t\t0,\t\t323,\tD(\"float(vector position, vector size, vector rgb, float alpha, optional float drawflag)\", \"Draws a solid block over the given 2d box, with given colour, alpha, and blend mode (specified via flags).\\nflags&3=0 simple blend.\\nflags&3=1 additive blend\")},// (EXT_CSQC, [EXT_CSQC_???])\n\t{\"drawsetcliparea\",\tPF_Fixme,\t0,\t\t0,\t\t0,\t\t324,\tD(\"void(float x, float y, float width, float height)\", \"Specifies a 2d clipping region (aka: scissor test). 2d draw calls will all be clipped to this 2d box, the area outside will not be modified by any 2d draw call (even 2d polygons).\")},// (EXT_CSQC_???)\n\t{\"drawresetcliparea\",PF_Fixme,\t0,\t\t0,\t\t0,\t\t325,\tD(\"void(void)\", \"Reverts the scissor/clip area to the whole screen.\")},// (EXT_CSQC_???)\n\n\t{\"drawstring\",\t\tPF_Fixme,\t0,\t\t0,\t\t0,\t\t326,\tD(\"float(vector position, string text, vector size='8 8', vector rgb='1 1 1', float alpha=1, float drawflag=0)\", \"Draws a string, interpreting markup and recolouring as appropriate.\")},// #326\n\t{\"stringwidth\",\t\tPF_Fixme,\t0,\t\t0,\t\t0,\t\t327,\tD(\"float(string text, float usecolours, vector fontsize='8 8')\", \"Calculates the width of the screen in virtual pixels. If usecolours is 1, markup that does not affect the string width will be ignored. Will always be decoded as UTF-8 if UTF-8 is globally enabled.\\nIf the char size is not specified, '8 8 0' will be assumed.\")},// EXT_CSQC_'DARKPLACES'\n\t{\"drawsubpic\",\t\tPF_Fixme,\t0,\t\t0,\t\t0,\t\t328,\tD(\"void(vector pos, vector sz, string pic, vector srcpos, vector srcsz, vector rgb, float alpha, optional float drawflag)\", \"Draws a rescaled subsection of an image to the screen.\")},// #328 EXT_CSQC_'DARKPLACES'\n\t{\"drawrotpic\",\t\tPF_Fixme,\t0,\t\t0,\t\t0,\t\t0,\t\tD(\"void(vector pivot, vector mins, vector maxs, string pic, vector rgb, float alpha, float angle)\", \"Draws an image rotating at the pivot. To rotate in the center, use mins+maxs of half the size with mins negated. Angle is in degrees.\")},\n\t{\"drawrotsubpic\",\tPF_Fixme,\t0,\t\t0,\t\t0,\t\t0,\t\tD(\"void(vector pivot, vector mins, vector maxs, string pic, vector txmin, vector txsize, vector rgb, vector alphaandangles)\", \"Overcomplicated draw function for over complicated people. Positions follow drawrotpic, while texture coords follow drawsubpic. Due to argument count limitations in builtins, the alpha value and angles are combined into separate fields of a vector (tip: use fteqcc's [alpha, angle] feature.\")},\n\n//330\n\t//NOTE: DP misnamed these to match its common misuse of clientstat, swapping getstatf+getstati.\n\t{\"getstati\",\t\tPF_Fixme,\t0,\t\t0,\t\t0,\t\t330,\tD(\"#define getstati_punf(stnum) (float)(__variant)getstati(stnum)\\nint(float stnum)\", \"Retrieves the full precision of a stat registered as EV_INTEGER.\")},// (EXT_CSQC)\n\t{\"getstatf\",\t\tPF_Fixme,\t0,\t\t0,\t\t0,\t\t331,\tD(\"#define getstatbits getstatf\\nfloat(float stnum, optional float firstbit, optional float bitcount)\", \"Retrieves the numerical value of the given EV_FLOAT stat. If firstbit and bitcount are specified, then this builtin acts as getstati combined with itof, and which should be used for STAT_ITEMS (but not other stats).\")},// (EXT_CSQC)\n\t{\"getstats\",\t\tPF_Fixme,\t0,\t\t0,\t\t0,\t\t332,\tD(\"string(float stnum)\", \"Retrieves the value of the given EV_STRING stat, as a tempstring.\\nOlder engines may use 4 consecutive integer stats, with a limit of 15 chars (yes, really. 15.), but \"FULLENGINENAME\" uses a separate namespace for string stats and has a much higher length limit.\")},\n\t{\"getplayerstat\",\tPF_Fixme,\t0,\t\t0,\t\t0,\t\t0,\t\tD(\"__variant(float playernum, float statnum, float stattype)\", \"Retrieves a specific player's stat, matching the type specified on the server. This builtin is primarily intended for mvd playback where ALL players are known. Return value matches the specified EV_ stattype. For EV_ENTITY, world will be returned if the entity is not in the pvs, use type-punning with EV_INTEGER to get the entity number if you just want to see if its set. STAT_ITEMS should be queried as an EV_INTEGER on account of runes and items2 being packed into the upper bits.\")},\n\n//EXT_CSQC\n\t{\"setmodelindex\",\tPF_Fixme,\t0,\t\t0,\t\t0,\t\t333,\tD(\"void(entity e, float mdlindex)\", \"Sets a model by precache index instead of by name. Otherwise identical to setmodel.\")},//\n\t{\"modelnameforindex\",PF_modelnameforindex,0,0,\t0,\t\t334,\tD(\"string(float mdlindex)\", \"Retrieves the name of the model based upon a precache index. This can be used to reduce csqc network traffic by enabling model matching (with getmodelindex).\")},//\n\t{\"soundnameforindex\",PF_soundnameforindex,0,0,\t0,\t\t0,\t\tD(\"string(float sndindex)\", \"Retrieves the name of the sound based upon a precache index. This can be used to reduce csqc network traffic by enabling sound matching (with getsoundindex).\")},//\n\n\t{\"particleeffectnum\",PF_sv_particleeffectnum,0,0,0,\t\t335,\tD(\"float(string effectname)\", \"Precaches the named particle effect. If your effect name is of the form 'foo.bar' then particles/foo.cfg will be loaded by the client if foo.bar was not already defined.\\nDifferent engines will have different particle systems, this specifies the QC API only.\")},// (EXT_CSQC)\n\t{\"trailparticles\",\tPF_sv_trailparticles,0,0,\t0,\t\t336,\tD(\"void(float effectnum, entity ent, vector start, vector end)\", \"Draws the given effect between the two named points. If ent is not world, distances will be cached in the entity in order to avoid framerate dependancies. The entity is not otherwise used.\")},// (EXT_CSQC),\n//\t{\"trailparticles_dp\",PF_sv_trailparticles,0,0,\t0,\t\t336,\tD(\"void(entity ent, float effectnum, vector start, vector end)\", \"DarkPlaces got the argument order wrong, and failed to fix it due to apathy.\")},// (EXT_CSQC),\n\t{\"pointparticles\",\tPF_sv_pointparticles,0,0,\t0,\t\t337,\tD(\"void(float effectnum, vector origin, optional vector dir, optional float count)\", \"Spawn a load of particles from the given effect at the given point traveling or aiming along the direction specified. The number of particles are scaled by the count argument.\\nFor regular particles, the dir vector is multiplied by the 'veladd' property (while orgadd will push the particles along it). Decals will use it as a hint to align to the correct surface. In both cases, it should normally be a unit vector, but other lengths will still work. If it has length 0 then FTE will assume downwards.\")},// (EXT_CSQC)\n\n\t{\"cprint\",\t\t\tPF_Fixme,\t0,\t\t0,\t\t0,\t\t338,\tD(\"void(string s, ...)\", \"Print into the center of the screen just as ssqc's centerprint would appear.\")},//(EXT_CSQC)\n\t{\"print\",\t\t\tPF_print,\t0,\t\t0,\t\t0,\t\t339,\tD(\"void(string s, ...)\", \"Unconditionally print on the local system's console, even in ssqc (doesn't care about the value of the developer cvar).\")},//(EXT_CSQC)\n\t{\"setwatchpoint\",\tPF_setwatchpoint,0,\t0,\t\t0,\t\t0,\t\tD(\"void(string name, float evaltype, void *ptr)\", \"For debugging. Set a watchpoint to monitor an address for changes. This is equivelent to using the watchpoint_* console command, but done from qc can be used on abitrary addresses.\")},\n\n\t{\"keynumtostring\",\tPF_Fixme,\t0,\t\t0,\t\t0,\t\t340,\tD(\"string(float keynum)\", \"Returns a hunam-readable name for the given keycode, as a tempstring.\")},// (EXT_CSQC)\n\t{\"keynumtostring_csqc\",PF_Fixme,0,\t\t0,\t\t0,\t\t340,\tD(\"DEP string(float keynum)\", \"Returns a hunam-readable name for the given keycode, as a tempstring.\")},// (found in menuqc)\n\t{\"stringtokeynum\",\tPF_Fixme,\t0,\t\t0,\t\t0,\t\t341,\tD(\"float(string keyname)\", \"Looks up the key name in the same way that the bind command would, returning the keycode for that key.\")},// (EXT_CSQC)\n\t{\"stringtokeynum_csqc\",\tPF_Fixme,0,\t\t0,\t\t0,\t\t341,\tD(\"DEP float(string keyname)\", \"Looks up the key name in the same way that the bind command would, returning the keycode for that key.\")},// (found in menuqc)\n\t{\"getkeybind\",\t\tPF_Fixme,\t0,\t\t0,\t\t0,\t\t342,\tD(\"string(float keynum, optional float bindmap, optional float modifier)\", \"Returns the current binding for the given key (returning only the command executed when no modifiers are pressed).\")},// (EXT_CSQC)\n\n\t{\"setcursormode\",\tPF_Fixme,\t0,\t\t0,\t\t0,\t\t343,\tD(\"void(float usecursor, optional string cursorimage, optional vector hotspot, optional float scale)\", \"Pass TRUE if you want the engine to release the mouse cursor (absolute input events + touchscreen mode). Pass FALSE if you want the engine to grab the cursor (relative input events + standard looking). If the image name is specified, the engine will use that image for a cursor (use an empty string to clear it again), in a way that will not conflict with the console. Images specified this way will be hardware accelerated, if supported by the platform/port.\")},\n\t{\"getcursormode\",\tPF_Fixme,\t0,\t\t0,\t\t0,\t\t0,\t\tD(\"float(float effective)\", \"Reports the cursor mode this module previously attempted to use. If 'effective' is true, reports the cursor mode currently active (if was overriden by a different module which has precidence, for instance, or if there is only a touchscreen and no mouse).\")},\n\t{\"getmousepos\",\t\tPF_Fixme,\t0,\t\t0,\t\t0,\t\t344,\tD(\"vector()\", \"Nasty convoluted DP extension. Typically returns deltas instead of positions. Use CSQC_InputEvent instead for such things in csqc mods.\")},\t// #344 This is a DP extension\n\t{\"setmousepos\",\t\tPF_Fixme,\t0,\t\t0,\t\t0,\t\t0,\t\tD(\"void(vector newpos)\", \"Warps the mouse cursor to the given location. Should normally only be done following setcursormode(TRUE,...). The warp MAY be visible through *_InputEvent, but normally be seen as an IE_ABSMOUSE event anyway. Not all systems support cursor warping (or even cursors), so this is a hint only and you should not depend upon it.\")},\n\n\t{\"getinputstate\",\tPF_Fixme,\t0,\t\t0,\t\t0,\t\t345,\tD(\"float(float inputsequencenum)\", \"Looks up an input frame from the log, setting the input_* globals accordingly.\\nThe sequence number range used for prediction should normally be servercommandframe < sequence <= clientcommandframe.\\nThe sequence equal to clientcommandframe will change between input frames.\")},// (EXT_CSQC)\n\t{\"setsensitivityscaler\",PF_Fixme,0,\t\t0,\t\t0,\t\t346,\tD(\"void(float sens)\", \"Temporarily scales the player's mouse sensitivity based upon something like zoom, avoiding potential cvar saving and thus corruption.\")},// (EXT_CSQC)\n\n\n\t{\"runstandardplayerphysics\",PF_runclientphys,0,0,0,\t\t347,\tD(\"void(entity ent)\", \"Perform the engine's standard player movement prediction upon the given entity using the input_* globals to describe movement.\")},\n\t{\"getplayerkeyvalue\",\tPF_Fixme,0,\t\t0,\t\t0,\t\t348,\tD(\"string(float playernum, string keyname)\", \"Look up a player's userinfo, to discover things like their name, topcolor, bottomcolor, skin, team, *ver.\\nAlso includes scoreboard info like frags, ping, pl, userid, entertime, as well as voipspeaking and voiploudness.\")},// (EXT_CSQC)\n\t{\"getplayerkeyfloat\",\tPF_Fixme,0,\t\t0,\t\t0,\t\t0,\t\tD(\"float(float playernum, string keyname, optional float assumevalue)\", \"Cheaper version of getplayerkeyvalue that avoids the need for so many tempstrings.\")},\n\t{\"getplayerkeyblob\",\tPF_Fixme,0,\t\t0,\t\t0,\t\t0,\t\tD(\"int(float playernum, string keyname, optional void *outptr, int size)\", \"Obtains a copy of the full data blob. Will write up to size bytes but return the full size. Does not null terminate (but memalloc(ret+1) will, if you want to cast the buffer to a string), and the blob may contain embedded nulls. Ignores all special keys, returning only what is actually there.\")},\n\t{\"setlocaluserinfo\",\tPF_Fixme,0,\t\t0,\t\t0,\t\t0,\t\tD(\"void(float seat, string keyname, string newvalue)\", \"Change a userinfo key for the specified local player seat, equivelent to the setinfo console command. The server will normally forward the setting to other clients.\")},\n\t{\"getlocaluserinfo\",\tPF_Fixme,0,\t\t0,\t\t0,\t\t0,\t\tD(\"string(float seat, string keyname)\", \"Reads a local userinfo key for the specified local player seat. This is not quite the same as getplayerkeyvalue, due to latency and possible serverside filtering.\")},\n\t{\"setlocaluserinfoblob\",PF_Fixme,0,\t\t0,\t\t0,\t\t0,\t\tD(\"void(float seat, string keyname, void *outptr, int size)\", \"Sets the userinfo key to a blob that may contain nulls etc. Keys with a leading underscore will be visible to only the server (for user-specific binary settings).\")},\n\t{\"getlocaluserinfoblob\",PF_Fixme,0,\t\t0,\t\t0,\t\t0,\t\tD(\"int(float seat, string keyname, void *outptr, int maxsize)\", \"Obtains a copy of the full data blob. Will write up to size bytes but return the full size. Does not null terminate (but memalloc(ret+1) will, if you want to cast the buffer to a string), and the blob may contain embedded nulls. Ignores all special keys, returning only what is actually there.\")},\n\t{\"getlocalinfo\",\tPF_getlocalinfo,0,\t0,\t\t0,\t\t0,\t\tD(\"int(string keyname, optional void *outptr, int size)\", \"Obtains a copy of a data blob (with spaces) from the server's private localinfo. Will write up to size bytes and return the actual size. Does not null terminate (but memalloc(ret+1) will, if you want to cast the buffer to a string), and the blob may contain embedded nulls. Ignores all special keys, returning only what is actually there.\")},\n\t{\"setlocalinfo\",\tPF_setlocalinfo,0,\t0,\t\t0,\t\t0,\t\tD(\"void(string keyname, optional void *outptr, int size)\", \"Changes the server's private localinfo. This data will be available for the following map, and will *usually* reload with saved games.\")},\n\n\t{\"isdemo\",\t\t\tPF_Fixme,\t0,\t\t0,\t\t0,\t\t349,\tD(\"float()\", \"Returns if the client is currently playing a demo or not. Returns 2 when playing an mvd (where other player's stats can be queried, or the pov can be changed freely).\")},// (EXT_CSQC)\n\t{\"isserver\",\t\tPF_Fixme,\t0,\t\t0,\t\t0,\t\t350,\tD(\"float()\", \"Returns non-zero whenever the local console can directly affect the server (ie: listen servers or single-player). Compat note: DP returns 0 for single-player.\")},//(EXT_CSQC)\n\t{\"SetListener\",\t\tPF_Fixme, \t0,\t\t0,\t\t0,\t\t351,\tD(\"void(vector origin, vector forward, vector right, vector up, optional float reverbtype)\", \"Sets the position of the view, as far as the audio subsystem is concerned. This should be called once per CSQC_UpdateView as it will otherwise revert to default. For reverbtype, see setup_reverb or treat as 'underwater'.\")},// (EXT_CSQC)\n\t{\"setup_reverb\",\tPF_Fixme, \t0,\t\t0,\t\t0,\t\t0,\t\tD(\"typedef struct {\\n\\tfloat flDensity;\\n\\tfloat flDiffusion;\\n\\tfloat flGain;\\n\\tfloat flGainHF;\\n\\tfloat flGainLF;\\n\\tfloat flDecayTime;\\n\\tfloat flDecayHFRatio;\\n\\tfloat flDecayLFRatio;\\n\\tfloat flReflectionsGain;\\n\\tfloat flReflectionsDelay;\\n\\tvector flReflectionsPan;\\n\\tfloat flLateReverbGain;\\n\\tfloat flLateReverbDelay;\\n\\tvector flLateReverbPan;\\n\\tfloat flEchoTime;\\n\\tfloat flEchoDepth;\\n\\tfloat flModulationTime;\\n\\tfloat flModulationDepth;\\n\\tfloat flAirAbsorptionGainHF;\\n\\tfloat flHFReference;\\n\\tfloat flLFReference;\\n\\tfloat flRoomRolloffFactor;\\n\\tint   iDecayHFLimit;\\n} reverbinfo_t;\\nvoid(float reverbslot, reverbinfo_t *reverbinfo, int sizeofreverinfo_t)\", \"Reconfigures a reverb slot for weird effects. Slot 0 is reserved for no effects. Slot 1 is reserved for underwater effects. Reserved slots will be reinitialised on snd_restart, but can otherwise be changed. These reverb slots can be activated with SetListener. Note that reverb will currently only work when using OpenAL.\")},\n\t{\"queueaudio\",\t\tPF_Fixme,\t0,\t\t0,\t\t0,\t\t0,\t\tD(\"float(int hz, int channels, int type, void *data, unsigned int frames)\", \"Queues raw audio. For best results do not vary the hz/channels values. type: -8=__int8\"/*\"8=__uint8\"*/\", -16==__int16\"/*\", 16=__uint16\"*//*\", -32=__int32\"*//*\", 32=__uint32\"*//*\", 256|32==float\"*/)},\n\t{\"getqueuedaudio\",PF_Fixme,\t0,\t\t0,\t\t0,\t\t0,\t\tD(\"float()\", \"Returns the number of seconds of audio that is still pending.\")},\n\t{\"registercommand\",\tPF_sv_registercommand,0,0,\t0,\t\t352,\tD(\"void(string cmdname, optional string desc)\", \"Register the given console command, for easy console use.\\nConsole commands that are later used will invoke CSQC_ConsoleCommand/m_consolecommand/ConsoleCmd according to module.\")},//(EXT_CSQC)\n\t{\"wasfreed\",\t\tPF_WasFreed,0,\t\t0,\t\t0,\t\t353,\tD(\"float(entity ent)\", \"Quickly check to see if the entity is currently free. This function is only valid during the half-second non-reuse window, after that it may give bad results. Try one second to make it more robust.\")},//(EXT_CSQC) (should be availabe on server too)\n\t{\"serverkey\",\t\tPF_sv_serverkeystring,0,0,\t0,\t\t354,\tD(\"string(string key)\", \"Look up a key in the server's public serverinfo string. If the key contains binary data then it will be truncated at the first null.\")},//\n\t{\"serverkeyfloat\",\tPF_sv_serverkeyfloat,0,0,\t0,\t\t0,\t\tD(\"float(string key, optional float assumevalue)\", \"Version of serverkey that returns the value as a float (which avoids tempstrings).\")},//\n\t{\"serverkeyblob\",\tPF_sv_serverkeyblob,0,0,\t0,\t\t0,\t\tD(\"int(string key, optional void *ptr, int maxsize)\", \"Version of serverkey that returns data as a blob (ie: binary data that may contain nulls). Returns the full blob size, even if truncated (pass maxsize=0 to query required storage).\")},//\n\t{\"setserverkey\",\tPF_setserverkey,0,\t0,\t\t0,\t\t0,\t\tD(\"void(string key, void *ptr, optional int size)\", \"Changes the server's serverinfo.\")},//\n\t{\"getentitytoken\",\tPF_Fixme,\t0,\t\t0,\t\t0,\t\t355,\tD(\"string(optional string resetstring)\", \"Grab the next token in the map's entity lump.\\nIf resetstring is not specified, the next token will be returned with no other sideeffects.\\nIf empty, will reset from the map before returning the first token, probably {.\\nIf not empty, will tokenize from that string instead.\\nAlways returns tempstrings.\")},//;\n\t{\"findfont\",\t\tPF_Fixme,\t0,\t\t0,\t\t0,\t\t356,\tD(\"float(string s)\", \"Looks up a named font slot. Matches the actual font name as a last resort.\")},//;\n\t{\"loadfont\",\t\tPF_Fixme,\t0,\t\t0,\t\t0,\t\t357,\tD(\"float(string fontname, string fontmaps, string sizes, float slot, optional float fix_scale, optional float fix_voffset)\", \"too convoluted for me to even try to explain correct usage. Try drawfont = loadfont(\\\"\\\", \\\"cour\\\", \\\"16\\\", -1, 0, 0); to switch to the courier font (optimised for 16 virtual pixels high) ('cour' requires mscorefonts installed in linux). Additionally you can add \\\"outline=1\\\" as an extra token in the sizes string, to have more readable outlined fonts.\")},\n\n\t//358\n\t{\"sendevent\",\t\tPF_Fixme,\t0,\t\t0,\t\t0,\t\t359,\tD(\"void(string evname, string evargs, ...)\", \"Invoke CSEv_evname_evargs in ssqc. evargs must be a string of initials refering to the types of the arguments to pass. v=vector, e=entity(.entnum field is sent), f=float, i=int. 6 arguments max - you can get more if you pack your floats into vectors.\")},// (EXT_CSQC_1)\n\n\t{\"readbyte\",\t\tPF_Fixme,\t0,\t\t0,\t\t0,\t\t360,\tD(\"float()\", \"Reads an unsigned 8-bit value, pair with WriteByte.\")},// (EXT_CSQC)\n\t{\"readchar\",\t\tPF_Fixme,\t0,\t\t0,\t\t0,\t\t361,\tD(\"float()\", \"Reads a signed 8-bit value. Paired with WriteChar.\")},// (EXT_CSQC)\n\t{\"readshort\",\t\tPF_Fixme,\t0,\t\t0,\t\t0,\t\t362,\tD(\"float()\", \"Reads a signed 16-bit value. Paired with WriteShort.\")},// (EXT_CSQC)\n\t{\"readlong\",\t\tPF_Fixme,\t0,\t\t0,\t\t0,\t\t363,\tD(\"float()\", \"Reads a signed 32-bit value. Paired with WriteLong or WriteInt.\")},// (EXT_CSQC)\n\t{\"readcoord\",\t\tPF_Fixme,\t0,\t\t0,\t\t0,\t\t364,\tD(\"float()\", \"Reads a value matching the unspecified precision written ONLY by WriteCoord.\")},// (EXT_CSQC)\n\n\t{\"readangle\",\t\tPF_Fixme,\t0,\t\t0,\t\t0,\t\t365,\tD(\"float()\", \"Reads a value matching the unspecified precision written ONLY by WriteAngle.\")},// (EXT_CSQC)\n\t{\"readstring\",\t\tPF_Fixme,\t0,\t\t0,\t\t0,\t\t366,\tD(\"string()\", \"Reads a null-terminated string.\")},// (EXT_CSQC)\n\t{\"readfloat\",\t\tPF_Fixme,\t0,\t\t0,\t\t0,\t\t367,\tD(\"float()\", \"Reads a float without any truncation nor conversions. Data MUST have originally been written with WriteFloat.\")},// (EXT_CSQC)\n\t{\"readdouble\",\t\tPF_Fixme,\t0,\t\t0,\t\t0,\t\t0,\t\tD(\"__double()\", \"Reads a double-precision float without any truncation nor conversions. Data MUST have originally been written with WriteDouble.\")},\n\t{\"readint\",\t\t\tPF_Fixme,\t0,\t\t0,\t\t0,\t\t0,\t\tD(\"int()\", \"Reads a 32bit singled int without any conversions to float, otherwise interchangable with readlong.\")},// (EXT_CSQC)\n\t{\"readuint\",\t\tPF_Fixme,\t0,\t\t0,\t\t0,\t\t0,\t\tD(\"__uint()\", \"Reads a 32bit unsigned int. Paired with WriteUInt.\")},// (EXT_CSQC)\n\t{\"readint64\",\t\tPF_Fixme,\t0,\t\t0,\t\t0,\t\t0,\t\tD(\"__int64()\", \"Reads a 64bit signed int. Paired with WriteInt64.\")},\n\t{\"readuint64\",\t\tPF_Fixme,\t0,\t\t0,\t\t0,\t\t0,\t\tD(\"__uint64()\", \"Reads a 64bit unsigned int. Paired with WriteUInt64.\")},\n\t{\"readentitynum\",\tPF_Fixme,\t0,\t\t0,\t\t0,\t\t368,\tD(\"float()\", \"Reads the serverside index of an entity, paired with WriteEntity. There may be nothing else known about the entity yet, so the result typically needs to be saved as-is and re-looked up each frame. This can be done via getentity(NUM, GE_*) for non-csqc ents, or findentity(world,entnum,NUM) - both of which can fail due to latency.\")},// (EXT_CSQC)\n\n//\t{\"readserverentitystate\",PF_Fixme,0,\t0,\t\t0,\t\t369,\t\"void(float flags, float simtime)\"},// (EXT_CSQC_1)\n//\t{\"readsingleentitystate\",PF_Fixme,0,\t0,\t\t0,\t\t370},\n\t{\"deltalisten\",\t\tPF_Fixme,\t0,\t\t0,\t\t0,\t\t371,\tD(\"float(string modelname, float(float isnew) updatecallback, float flags)\", \"Specifies a per-modelindex callback to listen for engine-networking entity updates. Such entities are automatically interpolated by the engine (unless flags specifies not to).\\nThe various standard entity fields will be overwritten each frame before the updatecallback function is called.\")},//  (EXT_CSQC_1)\n\n\t{\"dynamiclight_spawnstatic\",PF_Fixme,0,\t0,\t\t0,\t\t0,\t\tD(\"float(vector org, float radius, vector rgb)\", \"Creates a static persistent light at the given position with the specified colour. Additional properties must be set via dynamiclight_set.\")},\n\t{\"dynamiclight_get\",PF_Fixme,\t0,\t\t0,\t\t0,\t\t372,\tD(\"__variant(float lno, float fld)\", \"Retrieves a property from the given dynamic/rt light. Return type depends upon the light field requested.\")},\n\t{\"dynamiclight_set\",PF_Fixme,\t0,\t\t0,\t\t0,\t\t373,\tD(\"void(float lno, float fld, __variant value)\", \"Changes a property on the given dynamic/rt light. Value type depends upon the light field to be changed.\")},\n\t{\"particleeffectquery\",PF_Fixme,0,\t\t0,\t\t0,\t\t374,\tD(\"string(float efnum, float body)\", \"Retrieves either the name or the body of the effect with the given number. The effect body is regenerated from internal state, and can be changed before being reapplied via the localcmd builtin.\")},\n\n\t{\"adddecal\",\t\tPF_Fixme,\t0,\t\t0,\t\t0,\t\t375,\tD(\"void(string shadername, vector origin, vector up, vector side, vector rgb, float alpha)\", \"Adds a temporary clipped decal shader to the scene, centered at the given point with given orientation. Will be drawn by the next renderscene call, and freed by the next clearscene call.\")},\n\t{\"setcustomskin\",\tPF_Fixme,\t0,\t\t0,\t\t0,\t\t376,\tD(\"void(entity e, string skinfilename, optional string skindata)\", \"Sets an entity's skin overrides to a new skin object. Releases the entities old skin (refcounted).\")},\n\t{\"loadcustomskin\",\tPF_Fixme,\t0,\t\t0,\t\t0,\t\t377,\tD(\"float(string skinfilename, optional string skindata)\", \"Creates a new skin object and returns it. These are custom per-entity surface->shader lookups. The skinfilename/data should be in .skin format:\\nsurfacename,shadername - makes the named surface use the named shader (legacy format for compat with q3)\\nreplace \\\"surfacename\\\" \\\"shadername\\\" - non-legacy equivalent.\\nqwskin \\\"foo\\\" - use an unmodified quakeworld player skin (including crop+repalette rules)\\nq1lower 0xff0000 - specify an override for the entity's lower colour, in this case to red\\nq1upper 0x0000ff - specify an override for the entity's lower colour, in this case to blue\\nh2class 0 - specifies which class to use for hexen2's hacky class-specific player colouring\\ncompose \\\"surfacename\\\" \\\"shader\\\" \\\"imagename@x,y:w,h$s,t,s2,t2?r,g,b,a\\\" - compose a skin texture from multiple images.\\n  The texture is determined to be sufficient to hold the first named image, additional images can be named as extra tokens on the same line.\\n  Use a + at the end of the line to continue reading image tokens from the next line also, the named shader must use 'map $diffuse' to read the composed texture (compatible with the defaultskin shader). Must be matched with a releasecustomskin call later, and is pointless without applycustomskin.\")},\n\t{\"applycustomskin\",\tPF_Fixme,\t0,\t\t0,\t\t0,\t\t378,\tD(\"void(entity e, float skinobj)\", \"Updates the entity's custom skin (refcounted).\")},\n\t{\"releasecustomskin\",PF_Fixme,\t0,\t\t0,\t\t0,\t\t379,\tD(\"void(float skinobj)\", \"Lets the engine know that the skin will no longer be needed. Thanks to refcounting any ents with the skin already applied will retain their skin until later changed. It is valid to destroy a skin just after applying it to an ent in the same function that it was created in, as the skin will only be destroyed once its refcount rops to 0.\")},\n\n\t{\"gp_getbuttontype\",PF_Fixme,\t0,\t\t0,\t\t0,\t\t0,\t\tD(\"enum controllertype : float\\n{\\n\\tCONTROLLER_NONE,\\n\\tCONTROLLER_UNKNOWN,\\n\\tCONTROLLER_XBOX,\\n\\tCONTROLLER_PLAYSTATION,\\n\\tCONTROLLER_NINTENDO,\\n\\tCONTROLLER_VIRTUAL};\\n\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"json_type_e(float devid)\", \"Sends a single rumble event to the game-pad specified in devid. Every time you call this, the previous effect is cancelled out.\")},\n\t{\"gp_rumble\",\t\tPF_Fixme,\t0,\t\t0,\t\t0,\t\t0,\t\tD(\"void(float devid, float amp_low, float amp_high, float duration)\", \"Sends a single rumble event to the game-pad specified in devid. Every time you call this, the previous effect is cancelled out.\")},\n\t{\"gp_rumbletriggers\",PF_Fixme,\t0,\t\t0,\t\t0,\t\t0,\t\tD(\"void(float devid, float left, float right, float duration)\", \"Makes the analog triggers rumble of the specified game-pad, like gp_rumble() one call cancels out the previous one on the device.\")},\n\t{\"gp_setledcolor\",\tPF_Fixme,\t0,\t\t0,\t\t0,\t\t0,\t\tD(\"void(float devid, vector color)\", \"Updates the game-pad LED color.\")},\n\t{\"gp_settriggerfx\",\tPF_Fixme,\t0,\t\t0,\t\t0,\t\t0,\t\tD(\"void(float devid, /*const*/ void *data, int size)\", \"Sends a specific effect packet to the controller. On the PlayStation 5's DualSense that can adjust the tension on the analog triggers.\")},\n//END EXT_CSQC\n\n\t{\"memalloc\",\t\tPF_memalloc,\t\t0,\t\t0,\t\t0,\t\t384,\tD(\"__variant*(int size)\", \"Allocate an arbitary block of memory\")},\n\t{\"memrealloc\",\t\tPF_memrealloc,\t\t0,\t\t0,\t\t0,\t\t0,\t\tD(\"__variant*(void *oldptr, int newsize)\", \"Allocate a new block of memory to replace an old one.\")},\n\t{\"memfree\",\t\t\tPF_memfree,\t\t\t0,\t\t0,\t\t0,\t\t385,\tD(\"void(__variant *ptr)\", \"Frees a block of memory that was allocated with memfree\")},\n\t{\"memcmp\",\t\t\tPF_memcmp,\t\t\t0,\t\t0,\t\t0,\t\t0,\t\tD(\"int(__variant *dst, __variant *src, int size, optional int dstoffset, int srcoffset)\", \"Compares two blocks of memory. Returns 0 if equal.\")},\n\t{\"memcpy\",\t\t\tPF_memcpy,\t\t\t0,\t\t0,\t\t0,\t\t386,\tD(\"void(__variant *dst, __variant *src, int size, optional int dstoffset, int srcoffset)\", \"Copys memory from one location to another\")},\n\t{\"memfill8\",\t\tPF_memfill8,\t\t0,\t\t0,\t\t0,\t\t387,\tD(\"void(__variant *dst, int val, int size, optional int offset)\", \"Sets an entire block of memory to a specified value. Pretty much always 0.\")},\n\t{\"memgetval\",\t\tPF_memgetval,\t\t0,\t\t0,\t\t0,\t\t388,\tD(\"__variant(__variant *dst, float ofs)\", \"Looks up the 32bit value stored at a pointer-with-offset.\")},\n\t{\"memsetval\",\t\tPF_memsetval,\t\t0,\t\t0,\t\t0,\t\t389,\tD(\"void(__variant *dst, float ofs, __variant val)\", \"Changes the 32bit value stored at the specified pointer-with-offset.\")},\n\t{\"memptradd\",\t\tPF_memptradd,\t\t0,\t\t0,\t\t0,\t\t390,\tD(\"__variant*(__variant *base, float ofs)\", \"Perform some pointer maths. Woo.\")},\n\t{\"memstrsize\",\t\tPF_memstrsize,\t\t0,\t\t0,\t\t0,\t\t0,\t\tD(\"float(string s)\", \"strlen, except ignores utf-8\")},\n\t{\"base64encode\",\tPF_base64encode,\t0,\t\t0,\t\t0,\t\t0,\t\tD(\"string(__variant *ptr, int bytes, optional int offset)\", \"Returns a copy of a binary blob encoded as a base64 temp-string (uses + and /, so be sure to include quotes).\")},\n\t{\"base64decode\",\tPF_base64decode,\t0,\t\t0,\t\t0,\t\t0,\t\tD(\"__variant*(string base64str, __out int bytes)\", \"Decodes a base64, returning a new block of memory that can must be freed with memfree.\")},\n\n\t{\"con_getset\",\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t391,\tD(\"string(string conname, string field, optional string newvalue)\", \"Reads or sets a property from a console object. The old value is returned. Iterrate through consoles with the 'next' field. Valid properties: \ttitle, name, next, unseen, markup, forceutf8, close, clear, hidden, linecount\")},\n\t{\"con_printf\",\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t392,\tD(\"void(string conname, string messagefmt, ...)\", \"Prints onto a named console.\")},\n\t{\"con_draw\",\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t393,\tD(\"void(string conname, vector pos, vector size, float fontsize)\", \"Draws the named console.\")},\n\t{\"con_input\",\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t394,\tD(\"float(string conname, float inevtype, float parama, float paramb, float paramc)\", \"Forwards input events to the named console. Mouse updates should be absolute only.\")},\n\t{\"setwindowcaption\",PF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t0,\t\tD(\"void(string newcaption)\", \"Replaces the title of the game window, as seen when task switching or just running in windowed mode.\")},\n\t{\"cvars_haveunsaved\",PF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t0,\t\tD(\"float()\", \"Returns true if any archived cvar has an unsaved value.\")},\n\n\t{\"entityprotection\",PF_entityprotection,0,\t\t0,\t\t0,\t\t0,\t\tD(\"float(entity e, float nowreadonly)\", \"Changes the protection on the specified entity to protect it from further edits from QC. The return value is the previous setting. Note that this can be used to unprotect the world, but doing so long term is not advised as you will no longer be able to detect invalid entity references. Also, world is not networked, so results might not be seen by clients (or in other words, world.avelocity_y=64 is a bad idea).\")},\n\n\t{\"getlocationname\",\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t0,\t\tD(\"string(vector pos)\", \"Looks up the specified position in the current map's .loc file and reports the nearest marked name.\")},\n\n\t{\"clipboard_get\",\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t0,\t\tD(\"void(int cliptype)\", \"Attempts to query the system clipboard. Any pasted text will be returned via Menu_InputEvent\")},\n\t{\"clipboard_set\",\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t0,\t\tD(\"void(int cliptype, string text)\", \"Changes the system clipboard to the specified text.\")},\n\t{\"respawnedict\",\tPF_respawnedict,\t0,\t\t0,\t\t0,\t\t0,\t\tD(\"entity(float entnum, optional __out float wasspawned)\", \"Acts like edict_num returning a specific entity number, but also marks it as spawned. If it was previously spawned then all of its prior field data will be LOST (you may wish to use wasfreed(edict_num(idx)) to check.\")},\n//\t{\"spawn_object\",\tPF_spawn_object,\t0,\t\t0,\t\t0,\t\t0,\t\tD(\"object(int objectsize, optional float entnum, optional __out float wasspawned)\", \"Spawns a new object with the specified size, normally called via qcc intrinsics.\")},\n//end fte extras\n\n//DP extras\n\n//DP_QC_COPYENTITY\n\t{\"copyentity\",\t\tPF_copyentity,\t\t0,\t\t0,\t\t0,\t\t400,\tD(\"entity(entity from, optional entity to)\", \"Copies all fields from one entity to another.\")},// (DP_QC_COPYENTITY)\n//DP_SV_SETCOLOR\n\t{\"setcolor\",\t\tPF_setcolors,\t\t0,\t\t0,\t\t0,\t\t401,\tD(\"__deprecated(\\\"No RGB support.\\\") void(entity ent, float colours)\", \"Changes a player's colours. The bits 0-3 are the lower/trouser colour, bits 4-7 are the upper/shirt colours.\")},//DP_SV_SETCOLOR\n//DP_QC_FINDCHAIN\n\t{\"findchain\",\t\tPF_findchain,\t0,\t\t0,\t\t0,\t\t402,\t\"entity(.string field, string match, optional .entity chainfield)\"},// (DP_QC_FINDCHAIN)\n//DP_QC_FINDCHAINFLOAT\n\t{\"findchainfloat\",\tPF_findchainfloat,0,\t\t0,\t\t0,\t\t403,\t\"entity(.float fld, float match, optional .entity chainfield)\"},// (DP_QC_FINDCHAINFLOAT)\n//DP_SV_EFFECT\n\t{\"effect\",\t\t\tPF_effect,\t\t\t0,\t\t0,\t\t0,\t\t404,\tD(\"void(vector org, string modelname, float startframe, float endframe, float framerate)\", \"Spawns a self-animating sprite\")},// (DP_SV_EFFECT)\n//DP_TE_BLOOD\n\t{\"te_blood\",\t\tPF_te_blooddp,\t\t0,\t\t0,\t\t0,\t\t405,\t\"void(vector org, vector dir, float count)\"},// #405 te_blood\n//DP_TE_BLOODSHOWER\n\t{\"te_bloodshower\",\tPF_te_bloodshower,\t0,\t\t0,\t\t0,\t\t406,\t\"void(vector mincorner, vector maxcorner, float explosionspeed, float howmany)\"},// (DP_TE_BLOODSHOWER)\n//DP_TE_EXPLOSIONRGB\n\t{\"te_explosionrgb\",\tPF_te_explosionrgb,\t0,\t\t0,\t\t0,\t\t407,\t\"void(vector org, vector color)\"},// (DP_TE_EXPLOSIONRGB)\n//DP_TE_PARTICLECUBE\n\t{\"te_particlecube\",\tPF_te_particlecube,\t0,\t\t0,\t\t0,\t\t408,\t\"void(vector mincorner, vector maxcorner, vector vel, float howmany, float color, float gravityflag, float randomveljitter)\"},// (DP_TE_PARTICLECUBE)\n//DP_TE_PARTICLERAIN\n\t{\"te_particlerain\",\tPF_te_particlerain,\t0,\t\t0,\t\t0,\t\t409,\t\"void(vector mincorner, vector maxcorner, vector vel, float howmany, float color)\"},// (DP_TE_PARTICLERAIN)\n//DP_TE_PARTICLESNOW\n\t{\"te_particlesnow\",\tPF_te_particlesnow,\t0,\t\t0,\t\t0,\t\t410,\t\"void(vector mincorner, vector maxcorner, vector vel, float howmany, float color)\"},// (DP_TE_PARTICLESNOW)\n//DP_TE_SPARK\n\t{\"te_spark\",\t\tPF_te_spark,\t\t0,\t\t0,\t\t0,\t\t411,\t\"void(vector org, vector vel, float howmany)\"},// (DP_TE_SPARK)\n//DP_TE_QUADEFFECTS1\n\t{\"te_gunshotquad\",\tPF_te_gunshotquad,\t0,\t\t0,\t\t0,\t\t412,\t\"void(vector org)\"},// (DP_TE_QUADEFFECTS1)\n\t{\"te_spikequad\",\tPF_te_spikequad,\t0,\t\t0,\t\t0,\t\t413,\t\"void(vector org)\"},// (DP_TE_QUADEFFECTS1)\n\t{\"te_superspikequad\",PF_te_superspikequad,0,\t0,\t\t0,\t\t414,\t\"void(vector org)\"},// (DP_TE_QUADEFFECTS1)\n\t{\"te_explosionquad\",PF_te_explosionquad,0,\t\t0,\t\t0,\t\t415,\t\"void(vector org)\"},// (DP_TE_QUADEFFECTS1)\n//DP_TE_SMALLFLASH\n\t{\"te_smallflash\",\tPF_te_smallflash,\t0,\t\t0,\t\t0,\t\t416,\t\"void(vector org)\"},// (DP_TE_SMALLFLASH)\n//DP_TE_CUSTOMFLASH\n\t{\"te_customflash\",\tPF_te_customflash,\t0,\t\t0,\t\t0,\t\t417,\t\"void(vector org, float radius, float lifetime, vector color)\"},// (DP_TE_CUSTOMFLASH)\n\n//DP_TE_STANDARDEFFECTBUILTINS\n\t{\"te_gunshot\",\t\tPF_te_gunshot,\t\t0,\t\t0,\t\t0,\t\t418,\t\"void(vector org, optional float count)\"},// #418 te_gunshot\n\t{\"te_spike\",\t\tPF_te_spike,\t\t0,\t\t0,\t\t0,\t\t419,\t\"void(vector org)\"},// #419 te_spike\n\t{\"te_superspike\",\tPF_te_superspike,\t0,\t\t0,\t\t0,\t\t420,\t\"void(vector org)\"},// #420 te_superspike\n\t{\"te_explosion\",\tPF_te_explosion,\t0,\t\t0,\t\t0,\t\t421,\t\"void(vector org)\"},// #421 te_explosion\n\t{\"te_tarexplosion\",\tPF_te_tarexplosion,\t0,\t\t0,\t\t0,\t\t422,\t\"void(vector org)\"},// #422 te_tarexplosion\n\t{\"te_wizspike\",\t\tPF_te_wizspike,\t\t0,\t\t0,\t\t0,\t\t423,\t\"void(vector org)\"},// #423 te_wizspike\n\t{\"te_knightspike\",\tPF_te_knightspike,\t0,\t\t0,\t\t0,\t\t424,\t\"void(vector org)\"},// #424 te_knightspike\n\t{\"te_lavasplash\",\tPF_te_lavasplash,\t0,\t\t0,\t\t0,\t\t425,\t\"void(vector org)\"},// #425 te_lavasplash\n\t{\"te_teleport\",\t\tPF_te_teleport,\t\t0,\t\t0,\t\t0,\t\t426,\t\"void(vector org)\"},// #426 te_teleport\n\t{\"te_explosion2\",\tPF_te_explosion2,\t0,\t\t0,\t\t0,\t\t427,\t\"void(vector org, float color, float colorlength)\"},// #427 te_explosion2\n\t{\"te_lightning1\",\tPF_te_lightning1,\t0,\t\t0,\t\t0,\t\t428,\t\"void(entity own, vector start, vector end)\"},// #428 te_lightning1\n\t{\"te_lightning2\",\tPF_te_lightning2,\t0,\t\t0,\t\t0,\t\t429,\t\"void(entity own, vector start, vector end)\"},// #429 te_lightning2\n\t{\"te_lightning3\",\tPF_te_lightning3,\t0,\t\t0,\t\t0,\t\t430,\t\"void(entity own, vector start, vector end)\"},// #430 te_lightning3\n\t{\"te_beam\",\t\t\tPF_te_beam,\t\t\t0,\t\t0,\t\t0,\t\t431,\t\"void(entity own, vector start, vector end)\"},// #431 te_beam\n\t{\"vectorvectors\",\tPF_vectorvectors,\t0,\t\t0,\t\t0,\t\t432,\t\"void(vector dir)\"},// (DP_QC_VECTORVECTORS)\n\t{\"te_plasmaburn\",\tPF_te_plasmaburn,\t0,\t\t0,\t\t0,\t\t433,\t\"void(vector org)\"},// (DP_TE_PLASMABURN)\n\t{\"getsurfacenumpoints\",PF_getsurfacenumpoints,0,0,\t\t0,\t\t434,\t\"float(entity e, float s)\"},// (DP_QC_GETSURFACE)\n\t{\"getsurfacepoint\",PF_getsurfacepoint,\t0,\t\t0,\t\t0,\t\t435,\t\"vector(entity e, float s, float n)\"},// (DP_QC_GETSURFACE)\n\t{\"getsurfacenormal\",PF_getsurfacenormal,0,\t\t0,\t\t0,\t\t436,\t\"vector(entity e, float s)\"},// (DP_QC_GETSURFACE)\n\t{\"getsurfacetexture\",PF_getsurfacetexture,0,\t0,\t\t0,\t\t437,\t\"string(entity e, float s)\"},// (DP_QC_GETSURFACE)\n\t{\"getsurfacenearpoint\",PF_getsurfacenearpoint,0,0,\t\t0,\t\t438,\t\"float(entity e, vector p)\"},// (DP_QC_GETSURFACE)\n\t{\"getsurfaceclippedpoint\",PF_getsurfaceclippedpoint,0,0,0,\t\t439,\t\"vector(entity e, float s, vector p)\"},// (DP_QC_GETSURFACE)\n\n#ifndef SERVERONLY\n\t//begin menu-only\n\t{\"buf_create\",\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t440,\t\"strbuf()\"},//DP_QC_STRINGBUFFERS\n\t{\"buf_del\",\t\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t441,\t\"void(strbuf bufhandle)\"},//DP_QC_STRINGBUFFERS\n\t{\"buf_getsize\",\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t442,\t\"float(strbuf bufhandle)\"},//DP_QC_STRINGBUFFERS\n\t{\"buf_copy\",\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t443,\t\"void(strbuf bufhandle_from, float bufhandle_to)\"},//DP_QC_STRINGBUFFERS\n\t{\"buf_sort\",\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t444,\t\"void(strbuf bufhandle, float sortprefixlen, float backward)\"},//DP_QC_STRINGBUFFERS\n\t{\"buf_implode\",\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t445,\t\"string(strbuf bufhandle, string glue)\"},//DP_QC_STRINGBUFFERS\n\t{\"bufstr_get\",\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t446,\t\"string(strbuf bufhandle, float string_index)\"},//DP_QC_STRINGBUFFERS\n\t{\"bufstr_set\",\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t447,\t\"void(strbuf bufhandle, float string_index, string str)\"},//DP_QC_STRINGBUFFERS\n\t{\"bufstr_add\",\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t448,\t\"float(strbuf bufhandle, string str, float ordered)\"},//DP_QC_STRINGBUFFERS\n\t{\"bufstr_free\",\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t449,\t\"void(strbuf bufhandle, float string_index)\"},//DP_QC_STRINGBUFFERS\n\t{\"iscachedpic\",\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t451,\t\"float(string name)\"},// (EXT_CSQC)\n\t{\"precache_pic\",\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t452,\t\"string(string name, optional float flags)\"},// (EXT_CSQC)\n\t{\"freepic\",\t\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t453,\t\"void(string name)\"},// (EXT_CSQC)\n\t{\"drawcharacter\",\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t454,\t\"float(vector position, float character, vector scale, vector rgb, float alpha, optional float flag)\"},// (EXT_CSQC, [EXT_CSQC_???])\n\t{\"drawrawstring\",\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t455,\t\"float(vector position, string text, vector scale, vector rgb, float alpha, optional float flag)\"},// (EXT_CSQC, [EXT_CSQC_???])\n\t{\"drawpic\",\t\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t456,\t\"float(vector position, string pic, vector size, vector rgb, float alpha, optional float flag)\"},// (EXT_CSQC, [EXT_CSQC_???])\n\t{\"drawfill\",\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t457,\t\"float(vector position, vector size, vector rgb, float alpha, optional float flag)\"},// (EXT_CSQC, [EXT_CSQC_???])\n\t{\"drawsetcliparea\",\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t458,\t\"void(float x, float y, float width, float height)\"},// (EXT_CSQC_???)\n\t{\"drawresetcliparea\",PF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t459,\t\"void(void)\"},// (EXT_CSQC_???)\n\t{\"drawgetimagesize\",PF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t460,\t\"vector(string picname)\"},// (EXT_CSQC)\n\t{\"cin_open\",\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t461,\t\"float(string file, string id)\" STUB},\n\t{\"cin_close\",\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t462,\t\"void(string id)\" STUB},\n\t{\"cin_setstate\",\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t463,\t\"void(string id, float newstate)\" STUB},\n\t{\"cin_getstate\",\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t464,\t\"float(string id)\" STUB},\n\t{\"cin_restart\",\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0, \t\t465,\t\"void(string file)\" STUB},\n\t{\"drawline\",\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t466,\t\"void(float width, vector pos1, vector pos2)\"},// (EXT_CSQC)\n\t{\"drawstring\",\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t467,\t\"float(vector position, string text, vector scale, vector rgb, float alpha, float flag=0)\"},// #326\n\t{\"stringwidth\",\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t468,\t\"float(string text, float usecolours, vector fontsize='8 8')\"},// EXT_CSQC_'DARKPLACES'\n\t{\"drawsubpic\",\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t469,\t\"void(vector pos, vector sz, string pic, vector srcpos, vector srcsz, vector rgb, float alpha, float flag)\"},// #328 EXT_CSQC_'DARKPLACES'\n\t//end menu-only\n#endif\n\t//begin non-menu\n\t{\"clientcommand\",\tPF_clientcommand,\t0,\t\t0,\t\t0,\t\t440,\t\"void(entity e, string s)\"},// (KRIMZON_SV_PARSECLIENTCOMMAND)\n\t{\"tokenize\",\t\tPF_Tokenize,\t\t0,\t\t0,\t\t0,\t\t441,\t\"float(string s)\"},// (KRIMZON_SV_PARSECLIENTCOMMAND)\n\t{\"argv\",\t\t\tPF_ArgV,\t\t\t0,\t\t0,\t\t0,\t\t442,\t\"string(float n)\"},// (KRIMZON_SV_PARSECLIENTCOMMAND\n\t{\"setattachment\",\tPF_setattachment,\t0,\t\t0,\t\t0,\t\t443,\t\"void(entity e, entity tagentity, string tagname)\"},// (DP_GFX_QUAKE3MODELTAGS)\n\t{\"search_begin\",\tPF_search_begin,\t0,\t\t0,\t\t0,\t\t444,\tD(\"searchhandle(string pattern, enumflags:float{\\n\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\\tSB_CASEINSENSITIVE=1<<0/*deprecated, ignored*/,\\n\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\\tSB_FULLPACKAGEPATH=1<<1/*in/out package names use gamedir prefix for extra info/specificity. see also: WP_FULLPACKAGEPATH*/,\\n\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\\tSB_ALLOWDUPES=1<<2/*don't filter out dupes, useful with search_getpackagename or search_fopen*/,\\n\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\\tSB_FORCESEARCH=1<<3/*open the named package if needed (possibly in other gamedirs)*/,\\n\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\\tSB_MULTISEARCH=1<<4/*use colons as a delimiter for multiple search patterns (instead of needing to somehow sort/combine multiple searches after, eg for ui displays)*/,\\n\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\\tSB_NAMESORT=1<<5/*sort results by filename, instead of by filesystem priority/randomness*/\\n\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"} flags, float quiet, optional string filterpackage)\", \"initiate a filesystem scan based upon filenames. Be sure to call search_end on the returned handle. Returns a negative value on error.\")},\n\t{\"search_end\",\t\tPF_search_end,\t\t0,\t\t0,\t\t0,\t\t445,\t\"void(searchhandle handle)\"},\n\t{\"search_getsize\",\tPF_search_getsize,\t0,\t\t0,\t\t0,\t\t446,\tD(\"float(searchhandle handle)\", \"Retrieves the number of files that were found.\")},\n\t{\"search_getfilename\", PF_search_getfilename,0,\t0,\t\t0,\t\t447,\tD(\"string(searchhandle handle, float num)\", \"Retrieves name of one of the files that was found by the initial search.\")},\n\t{\"search_getfilesize\", PF_search_getfilesize,0,\t0,\t\t0,\t\t0,\t\tD(\"float(searchhandle handle, float num)\", \"Retrieves the size of one of the files that was found by the initial search.\")},\n\t{\"search_getfilemtime\", PF_search_getfilemtime,0,0,\t\t0,\t\t0,\t\tD(\"string(searchhandle handle, float num)\", \"Retrieves modification time of one of the files.\")},\n\t{\"search_getpackagename\", PF_search_getpackagename,0,0,\t0,\t\t0,\t\tD(\"string(searchhandle handle, float num)\", \"Retrieves the name of the package containing the file. Search with SB_FULLPACKAGEPATH to see gamedir/package info\")},\n\t{\"search_fopen\",\tPF_search_fopen,\t0,\t\t0,\t\t0,\t\t0,\t\tD(\"filestream(searchhandle handle, float num)\", \"Opens the file directly, without getting confused about entries from other packages. Read access only.\")},\n\t{\"cvar_string\",\t\tPF_cvar_string,\t\t0,\t\t0,\t\t0,\t\t448,\t\"string(string cvarname)\"},//DP_QC_CVAR_STRING\n\t{\"findflags\",\t\tPF_FindFlags,\t\t0,\t\t0,\t\t0,\t\t449,\t\"entity(entity start, .float fld, float match)\"},//DP_QC_FINDFLAGS\n\t{\"findchainflags\",\tPF_findchainflags,0,\t\t0,\t\t0,\t\t450,\t\"entity(.float fld, float match, optional .entity chainfield)\"},//DP_QC_FINDCHAINFLAGS\n\t{\"gettagindex\",\t\tPF_gettagindex,\t\t0,\t\t0,\t\t0,\t\t451,\t\"float(entity ent, string tagname)\"},// (DP_MD3_TAGSINFO)\n\t{\"gettaginfo\",\t\tPF_gettaginfo,\t\t0,\t\t0,\t\t0,\t\t452,\tD(\"vector(entity ent, float tagindex)\", \"Obtains the current worldspace position+orientation of the bone or tag from the given entity. The return value is the world coord, v_forward, v_right, v_up are also set according to the bone/tag's orientation.\")},// (DP_MD3_TAGSINFO)\n\t{\"dropclient\",\t\tPF_dropclient,\t\t0,\t\t0,\t\t0,\t\t453,\t\"void(entity player)\"},//DP_SV_BOTCLIENT\n\t{\"spawnclient\",\t\tPF_spawnclient,\t\t0,\t\t0,\t\t0,\t\t454,\t\"entity()\"},//DP_SV_BOTCLIENT\n\t{\"clienttype\",\t\tPF_clienttype,\t\t0,\t\t0,\t\t0,\t\t455,\t\"float(entity client)\"},//botclient\n\t{\"WriteUnterminatedString\",PF_WriteString2,0,\t0,\t\t0,\t\t456,\t\"void(float target, string str)\"},\t//writestring but without the null terminator. makes things a little nicer.\n\t{\"te_flamejet\",\t\tPF_te_flamejet,\t\t0,\t\t0,\t\t0,\t\t457,\t\"void(vector org, vector vel, float howmany)\"},//DP_TE_FLAMEJET\n//\t{\"undefined\",\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t458,\t\"\"},\n\t{\"edict_num\",\t\tPF_edict_for_num,\t0,\t\t0,\t\t0,\t\t459,\t\"entity(float entnum)\"},//DP_QC_EDICT_NUM\n\t{\"buf_create\",\t\tPF_buf_create,\t\t0,\t\t0,\t\t0,\t\t460,\t\"strbuf()\"},//DP_QC_STRINGBUFFERS\n\t{\"buf_del\",\t\t\tPF_buf_del,\t\t\t0,\t\t0,\t\t0,\t\t461,\t\"void(strbuf bufhandle)\"},//DP_QC_STRINGBUFFERS\n\t{\"buf_getsize\",\t\tPF_buf_getsize,\t\t0,\t\t0,\t\t0,\t\t462,\t\"float(strbuf bufhandle)\"},//DP_QC_STRINGBUFFERS\n\t{\"buf_copy\",\t\tPF_buf_copy,\t\t0,\t\t0,\t\t0,\t\t463,\t\"void(strbuf bufhandle_from, strbuf bufhandle_to)\"},//DP_QC_STRINGBUFFERS\n\t{\"buf_sort\",\t\tPF_buf_sort,\t\t0,\t\t0,\t\t0,\t\t464,\t\"void(strbuf bufhandle, float sortprefixlen, float backward)\"},//DP_QC_STRINGBUFFERS\n\t{\"buf_implode\",\t\tPF_buf_implode,\t\t0,\t\t0,\t\t0,\t\t465,\t\"string(strbuf bufhandle, string glue)\"},//DP_QC_STRINGBUFFERS\n\t{\"bufstr_get\",\t\tPF_bufstr_get,\t\t0,\t\t0,\t\t0,\t\t466,\t\"string(strbuf bufhandle, float string_index)\"},//DP_QC_STRINGBUFFERS\n\t{\"bufstr_set\",\t\tPF_bufstr_set,\t\t0,\t\t0,\t\t0,\t\t467,\t\"void(strbuf bufhandle, float string_index, string str)\"},//DP_QC_STRINGBUFFERS\n\t{\"bufstr_add\",\t\tPF_bufstr_add,\t\t0,\t\t0,\t\t0,\t\t468,\t\"float(strbuf bufhandle, string str, float ordered)\"},//DP_QC_STRINGBUFFERS\n\t{\"bufstr_free\",\t\tPF_bufstr_free,\t\t0,\t\t0,\t\t0,\t\t469,\t\"void(strbuf bufhandle, float string_index)\"},//DP_QC_STRINGBUFFERS\n\t//end non-menu\n\n//\t{\"undefined\",\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t470,\t\"\"},\n//MENU VM BUILTINS SHARE THE BELOW BUILTINS\n\t{\"asin\",\t\t\tPF_asin,\t\t\t0,\t\t0,\t\t0,\t\t471,\t\"float(float s)\"},//DP_QC_ASINACOSATANATAN2TAN\n\t{\"acos\",\t\t\tPF_acos,\t\t\t0,\t\t0,\t\t0,\t\t472,\t\"float(float c)\"},//DP_QC_ASINACOSATANATAN2TAN\n\t{\"atan\",\t\t\tPF_atan,\t\t\t0,\t\t0,\t\t0,\t\t473,\t\"float(float t)\"},//DP_QC_ASINACOSATANATAN2TAN\n\t{\"atan2\",\t\t\tPF_atan2,\t\t\t0,\t\t0,\t\t0,\t\t474,\t\"float(float c, float s)\"},//DP_QC_ASINACOSATANATAN2TAN\n\t{\"tan\",\t\t\t\tPF_tan,\t\t\t\t0,\t\t0,\t\t0,\t\t475,\tD(\"float(float a)\", \"Forgive me father, for I have a sunbed and I'm not afraid to use it.\")},//DP_QC_ASINACOSATANATAN2TAN\n\t{\"strlennocol\",\t\tPF_strlennocol,\t\t0,\t\t0,\t\t0,\t\t476,\tD(\"float(string s)\", \"Returns the number of characters in the string after any colour codes or other markup has been parsed.\")},//DP_QC_STRINGCOLORFUNCTIONS\n\t{\"strdecolorize\",\tPF_strdecolorize,\t0,\t\t0,\t\t0,\t\t477,\tD(\"string(string s)\", \"Flattens any markup/colours, removing them from the string.\")},//DP_QC_STRINGCOLORFUNCTIONS\n\t{\"strftime\",\t\tPF_strftime,\t\t0,\t\t0,\t\t0,\t\t478,\t\"string(float uselocaltime, string format, ...)\"},\t//DP_QC_STRFTIME\n\t{\"tokenizebyseparator\",PF_tokenizebyseparator,0,0,\t\t0,\t\t479,\tD(\"float(string s, string separator1, ...)\", \"Splits up the string using only the specified delimiters/separators. Multiple delimiters can be given, they are each considered equivelent (though should start with the longest if you want to do weird subseparator stuff).\\nThe resulting tokens can be queried via argv (and argv_start|end_index builtins, if you want to determine which of the separators was present between two tokens).\\nNote that while an input string containing JUST a separator will return 2, a string with no delimiter will return 1, while (in FTE) an empty string will ALWAYS return 0.\")},\t//DP_QC_TOKENIZEBYSEPARATOR\n\t{\"strtolower\",\t\tPF_strtolower,\t\t0,\t\t0,\t\t0,\t\t480,\t\"string(string s)\"},\t//DP_QC_STRING_CASE_FUNCTIONS\n\t{\"strtoupper\",\t\tPF_strtoupper,\t\t0,\t\t0,\t\t0,\t\t481,\t\"string(string s)\"},\t//DP_QC_STRING_CASE_FUNCTIONS\n\t{\"cvar_defstring\",\tPF_cvar_defstring,\t0,\t\t0,\t\t0,\t\t482,\t\"string(string s)\"},\t//DP_QC_CVAR_DEFSTRING\n\t{\"pointsound\",\t\tPF_pointsound,\t\t0,\t\t0,\t\t0,\t\t483,\t\"void(vector origin, string sample, float volume, float attenuation)\"},//DP_SV_POINTSOUND\n\t{\"strreplace\",\t\tPF_strreplace,\t\t0,\t\t0,\t\t0,\t\t484,\t\"string(string search, string replace, string subject)\"},//DP_QC_STRREPLACE\n\t{\"strireplace\",\t\tPF_strireplace,\t\t0,\t\t0,\t\t0,\t\t485,\t\"string(string search, string replace, string subject)\"},//DP_QC_STRREPLACE\n\t{\"getsurfacepointattribute\",PF_getsurfacepointattribute,0,0,0,\t486,\t\"vector(entity e, float s, float n, float a)\"},//DP_QC_GETSURFACEPOINTATTRIBUTE\n\t{\"gecko_create\",\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t487,\tD(\"float(string name, optional string initialURI)\", \"Create a new 'browser tab' shader with the specified name that can then be drawn via drawpic (shader should not already exist - including from map/model textures or disk). In order to function correctly, this builtin depends upon external plugins being available. Use gecko_navigate to navigate it to a page of your choosing.\")},//DP_GECKO_SUPPORT\n\t{\"gecko_destroy\",\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t488,\tD(\"void(string name)\", \"Destroy a shader.\")},//DP_GECKO_SUPPORT\n\t{\"gecko_navigate\",\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t489,\tD(\"void(string name, string URI)\", \"Sends a command to the media decoder attached to the specified shader. In the case of a browser decoder, this changes the url that the browser displays. 'cmd:[un]focus' will tell the decoder that it has focus.\")},//DP_GECKO_SUPPORT\n\t{\"gecko_keyevent\",\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t490,\tD(\"float(string name, float key, float eventtype, optional float charcode)\", \"Send a key event to a media decoder. This applies only to interactive decoders like browsers.\")},//DP_GECKO_SUPPORT\n\t{\"gecko_mousemove\",\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t491,\tD(\"void(string name, float x, float y)\", \"Sets a media decoder shader's mouse position. Values should be 0-1.\")},//DP_GECKO_SUPPORT\n\t{\"gecko_resize\",\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t492,\tD(\"void(string name, float w, float h)\", \"Request to resize a media decoder.\")},//DP_GECKO_SUPPORT\n\t{\"gecko_get_texture_extent\",PF_Fixme,\t0,\t\t0,\t\t0,\t\t493,\tD(\"vector(string name)\", \"Retrieves a media decoder current image pixel sizes.\")},//DP_GECKO_SUPPORT\n\t{\"gecko_getproperty\",PF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t0,\t\tD(\"string(string shadname, string propname)\", \"Queries the media decoder (especially browser ones) for decoder-specific properties. The cef plugin recognises url, title, status.\")},\n\t{\"cin_open\",\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t0,\t\tD(\"float(string file, string id)\", NULL)},\n\t{\"cin_close\",\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t0,\t\tD(\"void(string id)\", NULL)},\n\t{\"cin_setstate\",\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t0,\t\tD(\"void(string id, float newstate)\", NULL)},\n\t{\"cin_getstate\",\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t0,\t\tD(\"float(string id)\", NULL)},\n\t{\"cin_restart\",\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0, \t\t0,\t\tD(\"void(string file)\", NULL)},\n\n\t{\"crc16\",\t\t\tPF_crc16,\t\t\t0,\t\t0,\t\t0,\t\t494,\t\"__deprecated(\\\"Use digest_hex\\\") float(float caseinsensitive, string s, ...)\"},//DP_QC_CRC16\n\t{\"cvar_type\",\t\tPF_cvar_type,\t\t0,\t\t0,\t\t0,\t\t495,\t\"float(string name)\"},//DP_QC_CVAR_TYPE\n\t{\"numentityfields\",\tPF_numentityfields,\t0,\t\t0,\t\t0,\t\t496,\tD(\"float()\", \"Gives the number of named entity fields. Note that this is not the size of an entity, but rather just the number of unique names (ie: vectors use 4 names rather than 3).\")},//DP_QC_ENTITYDATA\n\t{\"findentityfield\",\tPF_findentityfield,\t0,\t\t0,\t\t0,\t\t0,\t\tD(\"float(string fieldname)\", \"Find a field index by name.\")},\n\t{\"entityfieldref\",\tPF_entityfieldref,\t0,\t\t0,\t\t0,\t\t0,\t\tD(\"typedef .__variant field_t;\\nfield_t(float fieldnum)\", \"Returns a field value that can be directly used to read entity fields. Be sure to validate the type with entityfieldtype before using.\")},//DP_QC_ENTITYDATA\n\t{\"entityfieldname\",\tPF_entityfieldname,\t0,\t\t0,\t\t0,\t\t497,\tD(\"string(float fieldnum)\", \"Retrieves the name of the given entity field.\")},//DP_QC_ENTITYDATA\n\t{\"entityfieldtype\",\tPF_entityfieldtype,\t0,\t\t0,\t\t0,\t\t498,\tD(\"float(float fieldnum)\", \"Provides information about the type of the field specified by the field num. Returns one of the EV_ values.\")},//DP_QC_ENTITYDATA\n\t{\"getentityfieldstring\",PF_getentityfieldstring,0,0,\t0,\t\t499,\t\"string(float fieldnum, entity ent)\"},//DP_QC_ENTITYDATA\n\t{\"putentityfieldstring\",PF_putentityfieldstring,0,0,\t0,\t\t500,\t\"float(float fieldnum, entity ent, string s)\"},//DP_QC_ENTITYDATA\n\t{\"WritePicture\",\tPF_WritePicture,\t0,\t\t0,\t\t0,\t\t501,\tD(\"void(float to, string s, float sz)\", \"Encodes the named image across the network as-is adhering to some size limit. In FTE, this simply writes the string and is equivelent to writestring and sz is ignored. WritePicture should be paired with ReadPicture in csqc.\")},//DP_SV_WRITEPICTURE\n\t{\"ReadPicture\",\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t501,\tD(\"string()\", \"Reads a picture that was written by ReadPicture, and returns a name that can be used in drawpic and other 2d drawing functions. In FTE, this acts as a readstring-with-downloadcheck - the image will appear normally once it has been downloaded, but its size may be incorrect until then.\")},//DP_SV_WRITEPICTURE\n\t{\"boxparticles\",\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t502,\t\"void(float effectindex, entity own, vector org_from, vector org_to, vector dir_from, vector dir_to, float countmultiplier, optional float flags)\"},\n\t{\"whichpack\",\t\tPF_whichpack,\t\t0,\t\t0,\t\t0,\t\t503,\tD(\"string(string filename, optional enumflags:float{WP_REFERENCEPACKAGE,WP_FULLPACKAGEPATH} flags)\", \"Returns the pak file name that contains the file specified. progs/player.mdl will generally return something like 'pak0.pak'. If WP_REFERENCE, clients will automatically be told that the returned package should be pre-downloaded and used, even if allow_download_refpackages is not set.\")},//DP_QC_WHICHPACK\n\t{\"getentity\",\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t504,\tD(\"__variant(float entnum, float fieldnum)\", \"Looks up fields from non-csqc-visible entities. The entity will need to be within the player's pvs. fieldnum should be one of the GE_ constants.\")},//DP_CSQC_QUERYRENDERENTITY\n//\t{\"undefined\",\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t505,\t\"\"},\n//\t{\"undefined\",\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t506,\t\"\"},\n//\t{\"undefined\",\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t507,\t\"\"},\n//\t{\"undefined\",\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t508,\t\"\"},\n//\t{\"undefined\",\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t509,\t\"\"},\n\t{\"uri_escape\",\t\tPF_uri_escape,\t\t0,\t\t0,\t\t0,\t\t510,\tD(\"string(string in)\", \"Uses percent-encoding to encode any bytes in the input string which are not ascii alphanumeric, period, hyphen, or underscore. All other bytes will expand to eg '%20' for a single space char. This encoding scheme is compatible with http and other uris.\")},//DP_QC_URI_ESCAPE\n\t{\"uri_unescape\",\tPF_uri_unescape,\t0,\t\t0,\t\t0,\t\t511,\tD(\"string(string in)\", \"Undo any percent-encoding in the input string, hopefully resulting in the same original sequence of bytes (and thus chars too).\")},//DP_QC_URI_ESCAPE\n\t{\"num_for_edict\",\tPF_num_for_edict,\t0,\t\t0,\t\t0,\t\t512,\t\"float(entity ent)\"},//DP_QC_NUM_FOR_EDICT\n\t{\"uri_get\",\t\t\tPF_uri_get,\t\t\t0,\t\t0,\t\t0,\t\t513,\tD(\"#define uri_post uri_get\\nfloat(string uril, float id, optional string postmimetype, optional string postdata)\", \"uri_get() gets content from an URL and calls a callback \\\"uri_get_callback\\\" with it set as string; an unique ID of the transfer is returned\\nreturns 1 on success, and then calls the callback with the ID, 0 or the HTTP status code, and the received data in a string\\nFor a POST request, you will typically want the postmimetype set to application/x-www-form-urlencoded.\\nFor a GET request, omit the mime+data entirely.\\nConsult your webserver/php/etc documentation for best-practise.\")},//DP_QC_URI_GET\n\t{\"uri_post\",\t\tPF_uri_get,\t\t\t0,\t\t0,\t\t0,\t\t513,\tD(\"float(string uril, float id, optional string postmimetype, optional string postdata, optional float strbuf)\", \"uri_get() gets content from an URL and calls a callback \\\"uri_get_callback\\\" with it set as string; an unique ID of the transfer is returned\\nreturns 1 on success, and then calls the callback with the ID, 0 or the HTTP status code, and the received data in a string\"), true},//DP_QC_URI_POST\n\t{\"tokenize_console\",PF_tokenize_console,0,\t\t0,\t\t0,\t\t514,\tD(\"float(string str)\", \"Tokenize a string exactly as the console's tokenizer would do so. The regular tokenize builtin became bastardized for convienient string parsing, which resulted in a large disparity that can be exploited to bypass checks implemented in a naive SV_ParseClientCommand function, therefore you can use this builtin to make sure it exactly matches.\")},\n\t{\"argv_start_index\",PF_argv_start_index,0,\t\t0,\t\t0,\t\t515,\tD(\"float(float idx)\", \"Returns the character index that the tokenized arg started at.\")},\n\t{\"argv_end_index\",\tPF_argv_end_index,\t0,\t\t0,\t\t0,\t\t516,\tD(\"float(float idx)\", \"Returns the character index that the tokenized arg stopped at.\")},\n\t{\"buf_cvarlist\",\tPF_buf_cvarlist,\t0,\t\t0,\t\t0,\t\t517,\t\"void(strbuf strbuf, string pattern, string antipattern)\"},\n\t{\"cvar_description\",PF_cvar_description,0,\t\t0,\t\t0,\t\t518,\tD(\"string(string cvarname)\", \"Retrieves the description of a cvar, which might be useful for tooltips or help files. This may still not be useful.\")},\n\t{\"gettime\",\t\t\tPF_gettimef,\t\t0,\t\t0,\t\t0,\t\t519,\t\"float(optional float timetype)\"},\n\t{\"gettimed\",\t\tPF_gettimed,\t\t0,\t\t0,\t\t0,\t\t0,\t\t\"__double(optional int timetype)\"},\n\t{\"keynumtostring_omgwtf\",PF_Fixme,\t\t0,\t\t0,\t\t0,\t\t520,\t\"DEP string(float keynum)\"},\t//excessive third version in dp's csqc.\n\t{\"findkeysforcommand\",PF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t521,\tD(\"__deprecated(\\\"Does not support modifiers\\\") string(string command, optional float bindmap)\", \"Returns a list of keycodes that perform the given console command in a format that can only be parsed via tokenize (NOT tokenize_console). This only and always returns two values - if only one key is actually bound, -1 will be returned. The bindmap argument is listed for compatibility with dp-specific defs, but is ignored in FTE.\")},\n\t{\"findkeysforcommandex\",PF_Fixme,\t\t0,\t\t0,\t\t0,\t\t0,\t\tD(\"string(string command, optional float bindmap)\", \"Returns a list of key bindings in keyname format instead of keynums. Use tokenize to parse. This list may contain modifiers. May return large numbers of keys.\")},\n//\t{\"initparticlespawner\",PF_Fixme,\t\t0,\t\t0,\t\t0,\t\t522,\tD(\"void(float max_themes)\",\"\")},\n//\t{\"resetparticle\",\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t523,\tD(\"void()\",\"\")},\n//\t{\"particletheme\",\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t524,\tD(\"void(float theme)\",\"Copies the given theme's settings into the various QC globals.\")},\n//\t{\"particlethemesave\",PF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t525,\tD(\"float(optional float theme)\",\"Copies the various particle globals into a particle theme slot. If theme is omitted, a slot will be auto-allocated. DP BUG: if theme is specified, the return value is undefined.\")},\n//\t{\"particlethemefree\",PF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t526,\tD(\"void()\",\"Resets the particle theme slot to defaults, and marks it as uninitialised (so themesave might reallocate it)\")},\n//\t{\"particle\",\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t527,\tD(\"float(vector org, vector vel, optional float theme)\",\"Spawns a particle at the specified position+speed. If theme is specified the other properties come from a theme slot, otherwise they're read from globals.\")},\n//\t{\"delayedparticle\",\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t528,\tD(\"float(vector org, vector vel, float delay, float collisiondelay, optional float theme)\",\"Basically just extra args for 'particle'.\")},\n\t{\"loadfromdata\",\tPF_loadfromdata,\t0,\t\t0,\t\t0,\t\t529,\tD(\"void(string s)\", \"Reads a set of entities from the given string. This string should have the same format as a .ent file or a saved game. Entities will be spawned as required. If you need to see the entities that were created, you should use parseentitydata instead. No spawn functions will be called.\")},\n\t{\"loadfromfile\",\tPF_loadfromfile,\t0,\t\t0,\t\t0,\t\t530,\tD(\"void(string s)\", \"Reads a set of entities from the named file. This file should have the same format as a .ent file or a saved game. Entities will be spawned as required. If you need to see the entities that were created, you should use parseentitydata instead. No spawn functions will be called.\")},\n\t{\"setpause\",\t\tPF_setpause,\t\t0,\t\t0,\t\t0,\t\t531,\tD(\"void(float pause)\",\t\"SSQC: Sets whether the server should or should not be paused.\\n\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"CSQC: Only works in singleplayer, suitable for menu auto-pause. To pause in multiplayer use eg localcmd(\\\"cmd pause\\n\\\") to ask the server side to pause.\\n\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"Pause state between modules will be ORed, along with engine reasons for auto pausing.\")},\n\t//end dp extras\n\t//begin mvdsv extras\n#ifdef HAVE_LEGACY\n\t{\"precache_vwep_model\",PF_precache_vwep_model,0,0,\t\t0,\t\t532,\t\"float(string mname)\"},\n#endif\n\t//end mvdsv extras\n\t//restart dp extras\n\t{\"log\",\t\t\t\tPF_Logarithm,\t\t0,\t\t0,\t\t0,\t\t532,\tD(\"float(float v, optional float base)\", \"Determines the logarithm of the input value according to the specified base. This can be used to calculate how much something was shifted by.\")},\n\t{\"soundupdate\",\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t0,\t\tD(\"float(entity e, float channel, string newsample, float volume, float attenuation, float pitchpct, float flags, float timeoffset)\", \"Changes the properties of the current sound being played on the given entity channel. newsample may be empty, and will be ignored in this case. timeoffset is relative to the current position (subtract the result of getsoundtime for absolute positions). Negative volume can be used to stop the sound. Return value is a fractional value based upon the number of audio devices that could be updated - test against TRUE rather than non-zero.\")},\n\t{\"getsoundtime\",\tPF_Ignore,\t\t\t0,\t\t0,\t\t0,\t\t533,\tD(\"float(entity e, float channel)\", \"Returns the current playback time of the sample on the given entity's channel. Beware CHAN_AUTO (in csqc, channels are not limited by network protocol).\")},\n\t{\"getchannellevel\",\tPF_Ignore,\t\t\t0,\t\t0,\t\t0,\t\t0,\t\tD(\"float(entity e, float channel)\", \"Reports how load the sound's sample is at its current offset.\")},\n\t{\"soundlength\",\t\tPF_Ignore,\t\t\t0,\t\t0,\t\t0,\t\t534,\tD(\"float(string sample)\", \"Provides a way to query the duration of a sound sample, allowing you to set up a timer to chain samples.\")},\n\t{\"buf_loadfile\",\tPF_buf_loadfile,\t0,\t\t0,\t\t0,\t\t535,\tD(\"float(string filename, strbuf bufhandle)\", \"Appends the named file into a string buffer (which must have been created in advance). The return value merely says whether the file was readable.\")},\n\t{\"buf_writefile\",\tPF_buf_writefile,\t0,\t\t0,\t\t0,\t\t536,\tD(\"float(filestream filehandle, strbuf bufhandle, optional float startpos, optional float numstrings)\", \"Writes the contents of a string buffer onto the end of the supplied filehandle (you must have already used fopen). Additional optional arguments permit you to constrain the writes to a subsection of the stringbuffer.\")},\n\t{\"bufstr_find\",\t\tPF_bufstr_find,\t\t0,\t\t0,\t\t0,\t\t537,\tD(\"float(float bufhandle, string match, float matchrule, float startpos, float step)\", \"Looks for the first occurence of the specified string in the buffer, returning its index or -1 on failure.\")},\n//\t{\"matchpattern\",\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t538,\t\"float(string s, string pattern, float matchrule)\"},\n//\t{\"undefined\",\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t539,\t\"\"},\n\n\t{\"physics_supported\",PF_physics_supported,0,\t0,\t\t0,\t\t0,\t\tD(\"float(optional float forcestate)\", \"Queries whether rigid body physics is enabled or not. CSQC and SSQC may report different values. If the force argument is specified then the engine will try to activate or release physics (returning the new state, which may fail if plugins or dlls are missing). Note that restarting the physics engine is likely to result in hitches when collision trees get generated. The state may change if a plugin is disabled mid-map.\")},\n#ifdef USERBE\n\t{\"physics_enable\",\tPF_physics_enable,\t0,\t\t0,\t\t0,\t\t540,\tD(\"void(entity e, float physics_enabled)\", \"Enable or disable the physics attached to a MOVETYPE_PHYSICS entity. Entities which have been disabled in this way will stop taking so much cpu time.\")},\n\t{\"physics_addforce\",PF_physics_addforce,0,\t\t0,\t\t0,\t\t541,\tD(\"void(entity e, vector force, vector relative_ofs)\", \"Apply some impulse directional force upon a MOVETYPE_PHYSICS entity.\")},\n\t{\"physics_addtorque\",PF_physics_addtorque,0,\t0,\t\t0,\t\t542,\tD(\"void(entity e, vector torque)\", \"Apply some impulse rotational force upon a MOVETYPE_PHYSICS entity.\")},\n#endif\n\t\n\t{\"setkeydest\",\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t601,\t\"void(float dest)\"},\n\t{\"getkeydest\",\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t602,\t\"float()\"},\n\t{\"setmousetarget\",\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t603,\t\"void(float trg)\"},\n\t{\"getmousetarget\",\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t604,\t\"float()\"},\n\t{\"callfunction\",\tPF_callfunction,\t0,\t\t0,\t\t0,\t\t605,\tD(\"void(.../*, string funcname*/)\", \"Invokes the named function. The function name is always passed as the last parameter and must always be present. The others are passed to the named function as-is\")},\n\t{\"writetofile\",\t\tPF_writetofile,\t\t0,\t\t0,\t\t0,\t\t606,\tD(\"void(filestream fh, entity e)\", \"Writes an entity's fields to the named frik_file file handle.\")},\n\t{\"isfunction\",\t\tPF_isfunction,\t\t0,\t\t0,\t\t0,\t\t607,\tD(\"float(string s)\", \"Returns true if the named function exists and can be called with the callfunction builtin.\")},\n\t{\"getresolution\",\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t608,\tD(\"vector(float vidmode, optional float forfullscreen)\", \"Supposed to query the driver for supported video modes. FTE does not query drivers in this way, nor would it trust drivers anyway.\")},\n\t{\"keynumtostring_menu\",PF_Fixme,\t\t0,\t\t0,\t\t0,\t\t609,\t\"DEP string(float keynum)\"},\t//third copy of this builtin in dp's csqc.\n\t{\"findkeysforcommand_dp\",PF_Fixme,\t\t0,\t\t0,\t\t0,\t\t610,\t\"DEP string(string command, optional float bindmap)\"},\n\t{\"keynumtostring\",\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t609,\tD(\"string(float keynum)\", \"Converts a qscancode key number into a mostly-human-readable name, matching the bind command.\")},\t//normal name is for menuqc standard.\n\t{\"findkeysforcommand\",PF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t610,\t\"string(string command, optional float bindmap)\"},\n\t{\"gethostcachevalue\",PF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t611,\tD(\"float(float type)\", \"Obtains one of SLIST_ values.\")},\n\t{\"gethostcachestring\",PF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t612,\tD(\"string(float fld, float hostnr)\", \"Reads the value of a serverinfo key from the specified server (up to gethostcachevalue(SLIST_HOSTCACHEVIEWCOUNT)).\")},\n\t{\"parseentitydata\",\tPF_parseentitydata,\t0,\t\t0,\t\t0,\t\t613,\tD(\"float(entity e, string s, optional float offset)\", \"Reads a single entity's fields into an already-spawned entity. s should contain field pairs like in a saved game: {\\\"foo1\\\" \\\"bar\\\" \\\"foo2\\\" \\\"5\\\"}. Returns <=0 on failure, otherwise returns the offset in the string that was read to.\")},\n\t{\"generateentitydata\",PF_generateentitydata,0,\t0,\t\t0,\t\t0,\t\tD(\"string(entity e)\", \"Dumps the entities fields into a string which can later be parsed with parseentitydata.\")},\n\t{\"stringtokeynum\",\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t614,\tD(\"float(string key)\", \"Returns the qscancode of a key from its name. Names are identical to the bind command. ctrl/shift/alt modifiers are ignored.\")},\n\t{\"stringtokeynum_menu\",\tPF_Fixme,\t\t0,\t\t0,\t\t0,\t\t614,\tD(\"float(string key)\", \"Looks up a single key name in the same way that the bind command would, returning the keycode for that key\")},\n\t{\"resethostcachemasks\",PF_Fixme,\t\t0,\t\t0,\t\t0,\t\t615,\tD(\"void()\", \"Resets filters set by sethostcachemask*\")},\n\t{\"sethostcachemaskstring\",PF_Fixme,\t\t0,\t\t0,\t\t0,\t\t616,\tD(\"void(float mask, float fld, string str, float op)\", \"Adds a filter rule for the server queries.\")},\n\t{\"sethostcachemasknumber\",PF_Fixme,\t\t0,\t\t0,\t\t0,\t\t617,\tD(\"void(float mask, float fld, float num, float op)\", \"sethostcachemaskstring, but with a numeric reference value instead of using string comparisons\")},\n\t{\"resorthostcache\",\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t618,\tD(\"void()\", \"Reevalutes whether each server should be visible. Resorts servers. server ids passed to gethostcachestring are likely to change, and may become out of bounds.\")},\n\t{\"sethostcachesort\",PF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t619,\tD(\"void(float fld, float descending)\", \"Specifies which field to sort the server list by, and whether it should be ascending or descending. You will need to resorthostcache() for this to take effect.\")},\n\t{\"refreshhostcache\",PF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t620,\tD(\"void(optional float dopurge)\", \"Starts a new ping sequence\")},\n\t{\"gethostcachenumber\",PF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t621,\tD(\"float(float fld, float hostnr)\", \"An alternative to gethostcachestring, for reading numeric values\")},\n\t{\"gethostcacheindexforkey\",PF_Fixme,\t0,\t\t0,\t\t0,\t\t622,\tD(\"float(string key)\", \"Obtains a handle to a named key (to avoid string lookups). The return value can be used in place of the fld arg in related builtins\")},\n\t{\"addwantedhostcachekey\",PF_Fixme,\t\t0,\t\t0,\t\t0,\t\t623,\tD(\"void(string key)\", \"Asks the engine to track a custom serverinfo key.\")},\n\t{\"getextresponse\",\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t624,\tD(\"string()\", \"Stub.\")},\n\t{\"netaddress_resolve\",PF_netaddress_resolve,0,\t0,\t\t0,\t\t625,\tD(\"string(string dnsname, optional float defport)\", \"Performs a dna lookup and returns the first result as a string, which could be in a whole range of formats. Also blocks until completion.\")},\n\t{\"getgamedirinfo\",\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t626,\tD(\"string(float n, float prop)\", \"Queries properties about an indexed gamedir (or -1 for the current gamedir). Returns null strings when out of bounds. Use the GDDI_* constants for the prop arg.\")},\n\t{\"getpackagemanagerinfo\",PF_Fixme,\t\t0,\t\t0,\t\t0,\t\t0,\t\tD(\"string(int n, int prop)\", \"Queries information about a package from the engine's package manager subsystem. Actions can be taken via the pkg console command. prop must be one of the GPMI_ constants.\")},\n\t{\"sprintf\",\t\t\tPF_sprintf,\t\t\t0,\t\t0,\t\t0,\t\t627,\tD(\"string(string fmt, ...)\",\t\"'prints' to a formatted temp-string. Mostly acts as in C, however %d assumes floats (fteqcc has arg checking. Use it.).\\ntype conversions: l=arg is an int, h=arg is a float, and will work as a prefix for any float or int representation.\\nfloat representations: d=decimal, e,E=exponent-notation, f,F=floating-point notation, g,G=terse float, c=char code, x,X=hex\\nother representations: i=int, s=string, S=quoted and marked-up string, v=vector, p=pointer\\nso %ld will accept an int arg, while %hi will expect a float arg.\\nentities, fields, and functions will generally need to be printed as ints with %i.\")},\n\t{\"getsurfacenumtriangles\",PF_getsurfacenumtriangles,0,0,0,\t\t628,\t\"float(entity e, float s)\"},\n\t{\"getsurfacetriangle\",PF_getsurfacetriangle,0,\t0,\t\t0,\t\t629,\t\"vector(entity e, float s, float n)\"},\n\t{\"setkeybind\",\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t630,\t\"float(float key, string bind, optional float bindmap, optional float modifier)\"},\n\t{\"getbindmaps\",\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t631,\t\"vector()\"},\n\t{\"setbindmaps\",\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t632,\t\"float(vector bm)\"},\n\t{\"crypto_getkeyfp\",\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t633,\t\"DEP string(string addr)\"  STUB},\n\t{\"crypto_getidfp\",\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t634,\t\"DEP string(string addr)\"  STUB},\n\t{\"crypto_getencryptlevel\",PF_Fixme,\t\t0,\t\t0,\t\t0,\t\t635,\t\"DEP string(string addr)\"  STUB},\n\t{\"crypto_getmykeyfp\",PF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t636,\t\"DEP string(string addr)\"  STUB},\n\t{\"crypto_getmyidfp\",PF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t637,\t\"DEP string(float addr)\" STUB},\n//\t{\"CL_RotateMoves\",\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t638,\tD(\"void(vector anglechange)\", \"Rewrites the input log history to rotate all unacknowledged frames according to the angle delta specified.\")},\n\t{\"digest_hex\",\t\tPF_digest_hex,\t\t0,\t\t0,\t\t0,\t\t639,\t\"string(string digest, string data, ...)\"},\n\t{\"digest_ptr\",\t\tPF_digest_ptr,\t\t0,\t\t0,\t\t0,\t\t0,\t\tD(\"string(string digest, void *data, int length, optional int offset)\", \"Calculates the digest of a single contiguous block of memory (including nulls) using the specified hash function.\")},\n\t{\"V_CalcRefdef\",\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t640,\t\"DEP void(entity e, float flags)\"\tSTUB},\n\t{\"crypto_getmyidstatus\",PF_Fixme,\t\t0,\t\t0,\t\t0,\t\t641,\t\"DEP float(float i)\"\tSTUB},\n\t{\"coverage\",\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t642,\t\"DEP void()\"\tSTUB},\n\t{\"crypto_getidstatus\",PF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t643,\t\"DEP float(string addr)\"\tSTUB},\n\t//end dp extras\n\n\t//wrath extras...\n\t{\"fcopy\",\t\t\tPF_fcopy,\t\t\t0,\t\t0,\t\t0,\t\t650,\tD(\"float(string src, string dst)\",\t\"Equivelent to fopen+fread+fwrite+fclose from QC (ie: reads from $gamedir/data/ or $gamedir, but always writes to $gamedir/data/ )\")},\n\t{\"frename\",\t\t\tPF_frename,\t\t\t0,\t\t0,\t\t0,\t\t651,\tD(\"float(string src, string dst)\",\t\"Renames the file, returning 0 on success. Both paths are relative to the data/ subdir.\")},\n\t{\"fremove\",\t\t\tPF_fremove,\t\t\t0,\t\t0,\t\t0,\t\t652,\tD(\"float(string fname)\",\t\"Deletes the named file - path is relative to data/ subdir, like fopen's FILE_WRITE. Returns 0 on success.\")},\n\t{\"fexists\",\t\t\tPF_fexists,\t\t\t0,\t\t0,\t\t0,\t\t653,\tD(\"float(string fname)\",\t\"Returns true if it exists inside the default writable path. Use whichpack for greater portability.\")},\n\t{\"rmtree\",\t\t\tPF_rmtree,\t\t\t0,\t\t0,\t\t0,\t\t654,\tD(\"float(string path)\",\t\t\"Dangerous, but sandboxed to data/\")},\n\t{\"walkmovedist\",\tPF_walkmovedist,\t0,\t\t0,\t\t0,\t\t655,\tD(\"DEP float(float yaw, float dist, optional float settraceglobals)\", \"Attempt to walk the entity at a given angle for a given distance.\\nif settraceglobals is set, the trace_* globals will be set, showing the results of the movement.\\nThis function will trigger touch events.\"), true},\n\t{\"timescale\",\t\tPF_Fixme,\t\t\t0,\t\t0,\t\t0,\t\t656,\t\"DEP void(float f)\" STUB},\n\t{\"nodegraph_graphset_clear\",\t\t\t\t\t\t\tPF_Fixme,0,0,0,700,\t\"DEP float()\" STUB},\t//Wrath's EXT_NODEGRAPH\n\t{\"nodegraph_graphset_load\",\t\t\t\t\t\t\t\tPF_Fixme,0,0,0,701,\t\"DEP float()\" STUB},\n\t{\"nodegraph_graphset_save\",\t\t\t\t\t\t\t\tPF_Fixme,0,0,0,702,\t\"DEP float()\" STUB},\n\t{\"nodegraph_graph_clear\",\t\t\t\t\t\t\t\tPF_Fixme,0,0,0,703,\t\"DEP float(float graphid)\" STUB},\n\t{\"nodegraph_graph_nodes_count\",\t\t\t\t\t\t\tPF_Fixme,0,0,0,704,\t\"DEP float(float graphid)\" STUB},\n\t{\"nodegraph_graph_add_node\",\t\t\t\t\t\t\tPF_Fixme,0,0,0,705,\t\"DEP float(float graphid, vector node)\" STUB},\n\t{\"nodegraph_graph_remove_node\",\t\t\t\t\t\t\tPF_Fixme,0,0,0,706,\t\"DEP float(float graphid, float nodeid)\" STUB},\n\t{\"nodegraph_graph_is_node_valid\",\t\t\t\t\t\tPF_Fixme,0,0,0,707,\t\"DEP float(float graphid, float nodeid)\" STUB},\n\t{\"nodegraph_graph_get_node\",\t\t\t\t\t\t\tPF_Fixme,0,0,0,708,\t\"DEP vector(float graphid, float nodeid)\" STUB},\n\t{\"nodegraph_graph_add_link\",\t\t\t\t\t\t\tPF_Fixme,0,0,0,709,\t\"DEP float(float graphid, float nodeidfrom, float nodeidto)\" STUB},\n\t{\"nodegraph_graph_remove_link\",\t\t\t\t\t\t\tPF_Fixme,0,0,0,710,\t\"DEP float(float graphid, float nodeidfrom, float nodeidto)\" STUB},\n\t{\"nodegraph_graph_does_link_exist\",\t\t\t\t\t\tPF_Fixme,0,0,0,711,\t\"DEP float(float graphid, float nodeidfrom, float nodeidto)\" STUB},\n\t{\"nodegraph_graph_find_nearest_nodeid\",\t\t\t\t\tPF_Fixme,0,0,0,712,\t\"DEP float(float graphid, vector position)\" STUB},\n\t{\"nodegraph_graph_query_path\",\t\t\t\t\t\t\tPF_Fixme,0,0,0,713,\t\"DEP float(float graphid, float nodeidfrom, float nodeidto)\" STUB},\n\t{\"nodegraph_graph_query_nodes_linked\",\t\t\t\t\tPF_Fixme,0,0,0,714,\t\"DEP float(float graphid, float nodeid)\" STUB},\n\t{\"nodegraph_graph_query_nodes_in_radius\",\t\t\t\tPF_Fixme,0,0,0,715,\t\"DEP float(float graphid, vector position, float radius)\" STUB},\n\t{\"nodegraph_query_release\",\t\t\t\t\t\t\t\tPF_Fixme,0,0,0,716,\t\"DEP float(float graphid)\" STUB},\n\t{\"nodegraph_query_entries_count\",\t\t\t\t\t\tPF_Fixme,0,0,0,717,\t\"DEP float(float graphid)\" STUB},\n\t{\"nodegraph_query_is_valid\",\t\t\t\t\t\t\tPF_Fixme,0,0,0,718,\t\"DEP float(float graphid)\" STUB},\n\t{\"nodegraph_query_get_graphid\",\t\t\t\t\t\t\tPF_Fixme,0,0,0,719,\t\"DEP float(float graphid)\" STUB},\n\t{\"nodegraph_query_get_nodeid\",\t\t\t\t\t\t\tPF_Fixme,0,0,0,720,\t\"DEP float(float graphid, float entryid)\" STUB},\n\t{\"nodegraph_moveprobe_fly\",\t\t\t\t\t\t\t\tPF_Fixme,0,0,0,721,\t\"DEP float(vector nodefrom, vector nodeto, vector mins, vector maxs, float type)\" STUB},\n\t{\"nodegraph_moveprobe_walk\",\t\t\t\t\t\t\tPF_Fixme,0,0,0,722,\t\"DEP float(vector nodefrom, vector nodeto, vector mins, vector maxs, float stepheight, float dropheight)\" STUB},\n\t{\"nodegraph_graph_query_nodes_in_radius_fly_reachable\",\tPF_Fixme,0,0,0,723,\t\"DEP float(float graphid, vector position, float radius, vector mins, vector maxs, float type)\" STUB},\n\t{\"nodegraph_graph_query_nodes_in_radius_walk_reachable\",PF_Fixme,0,0,0,724,\t\"DEP float(float graphid, vector position, float radius, vector mins, vector maxs, float stepheight, float dropheight)\" STUB},\n\t{\"nodegraph_graphset_remove\",\t\t\t\t\t\t\tPF_Fixme,0,0,0,725,\t\"DEP float()\" STUB},\n\t{\"stachievement_unlock\",\t\t\t\t\t\t\t\tPF_Fixme,0,0,0,730,\t\"DEP void(string achievement_id)\" STUB},\t\t//(csqc) EXT_STEAM_REKI\n\t{\"stachievement_query\",\t\t\t\t\t\t\t\t\tPF_Fixme,0,0,0,731,\t\"DEP void(string achievement_id)\" STUB},\t\t//(csqc) EXT_STEAM_REKI\n\t{\"ststat_setvalue\",\t\t\t\t\t\t\t\t\t\tPF_Fixme,0,0,0,732,\t\"DEP void(string stat_id, float value)\" STUB},\t//(csqc) EXT_STEAM_REKI\n\t{\"ststat_increment\",\t\t\t\t\t\t\t\t\tPF_Fixme,0,0,0,733,\t\"DEP void(string stat_id, float value)\" STUB},\t//(csqc) EXT_STEAM_REKI\n\t{\"ststat_query\",\t\t\t\t\t\t\t\t\t\tPF_Fixme,0,0,0,734,\t\"DEP void(string stat_id)\" STUB},\t\t\t\t//(csqc) EXT_STEAM_REKI\n\t{\"stachievement_register\",\t\t\t\t\t\t\t\tPF_Fixme,0,0,0,735,\t\"DEP void(string achievement_id)\" STUB},\t\t//(csqc) EXT_STEAM_REKI\n\t{\"ststat_register\",\t\t\t\t\t\t\t\t\t\tPF_Fixme,0,0,0,736,\t\"DEP void(string stat_id)\" STUB},\t\t\t\t//(csqc) EXT_STEAM_REKI\n\t{\"controller_query\",\t\t\t\t\t\t\t\t\tPF_Fixme,0,0,0,740,\t\"DEP void(float index)\" STUB},\t\t\t\t\t//(csqc) EXT_CONTROLLER_REKI\n\t{\"controller_rumble\",\t\t\t\t\t\t\t\t\tPF_Fixme,0,0,0,741,\t\"DEP void(float index, float lowmult, float highmult, float )\" STUB},\t\t//(csqc) EXT_CONTROLLER_REKI\n\t{\"controller_rumbletriggers\",\t\t\t\t\t\t\tPF_Fixme,0,0,0,742,\t\"DEP void(float index, float leftmult, float rightmult, float msec)\" STUB},\t//(csqc) EXT_CONTROLLER_REKI\n\t//end wrath extras\n\n\t{\"getrmqeffectsversion\",PF_Ignore,\t\t0,\t\t0,\t\t0,\t\t666,\t\"DEP float()\" STUB},\n\t//don't exceed sizeof(pr_builtin)/sizeof(pr_builtin[0]) (currently 1024) without modifing the size of pr_builtin\n\t\n\t{NULL}\n};\n\nstatic void QCBUILTIN PF_Fixme (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tint binum;\n\tchar fname[MAX_QPATH];\n\tint i;\n\tqboolean printedheader = false;\n\n\tSV_EndRedirect();\n\tif (!prinst->GetBuiltinCallInfo(prinst, &binum, fname, sizeof(fname)))\n\t{\n\t\tbinum = 0;\n\t\tstrcpy(fname, \"?unknown?\");\n\t}\n\n\tif (binum)\n\tfor (i = 0; BuiltinList[i].bifunc; i++)\n\t{\n\t\tif (BuiltinList[i].ebfsnum == binum)\n\t\t{\n\t\t\tif (!printedheader)\n\t\t\t{\n\t\t\t\tCon_Printf( \"\\n\"\n\t\t\t\t\t\"Mod forgot to ensure support for builtin %i:%s\\n\"\n\t\t\t\t\t\t\t\"Please consult the extensionlist_ssqc command.\\n\"\n\t\t\t\t\t\t\t\"Possible builtins:\\n\", binum, fname);\n\t\t\t\tprintedheader = true;\n\t\t\t}\n\t\t\tCon_Printf(\"%s\\n\", BuiltinList[i].name);\n\t\t}\n\t}\n\n\tCon_Printf(\"\\n\");\n\n\tif (progstype == PROG_QW && (binum >= 83 && binum < 105))\n\t\tprinst->RunError(prinst, \"\\nBuiltin %i:%s not implemented.\\nMods designed for mvdsv may need pr_imitatemvdsv to be enabled.\", binum, fname);\n\telse\n\t\tprinst->RunError(prinst, \"\\nBuiltin %i:%s not implemented.\\nMod is not compatible.\", binum, fname);\n\tPR_BIError (prinst, \"builtin not implemented\");\n}\nint PR_EnableEBFSBuiltin(const char *name, int binum)\n{\n\tint i;\n\tfor (i = 0;BuiltinList[i].name;i++)\n\t{\n\t\tif (!strcmp(BuiltinList[i].name, name) && BuiltinList[i].bifunc != PF_Fixme)\n\t\t{\n\t\t\tif (!binum)\n\t\t\t\tbinum = BuiltinList[i].ebfsnum;\n\t\t\tif (!binum)\n\t\t\t\treturn -1;\n\t\t\tif (!pr_overridebuiltins.value)\n\t\t\t{\n\t\t\t\tif (pr_builtin[binum] != NULL && pr_builtin[binum] != PF_Fixme)\n\t\t\t\t{\n\t\t\t\t\tif (pr_builtin[binum] == BuiltinList[i].bifunc)\t//it is already this function.\n\t\t\t\t\t\treturn binum;\n\n\t\t\t\t\treturn 0;\t//already used... ?\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tpr_builtin[binum] = BuiltinList[i].bifunc;\n\n\t\t\treturn binum;\n\t\t}\n\t}\n\n\treturn 0;\t//not known\n}\n\nstatic int PDECL PR_SSQC_MapNamedBuiltin(pubprogfuncs_t *progfuncs, int headercrc, const char *builtinname)\n{\n\tint i, binum;\n\tfor (i = 0;BuiltinList[i].name;i++)\n\t{\n\t\tif (!strcmp(BuiltinList[i].name, builtinname) && BuiltinList[i].bifunc != PF_Fixme)\n\t\t{\n\t\t\tfor (binum = sizeof(pr_builtin)/sizeof(pr_builtin[0]); --binum; )\n\t\t\t{\n\t\t\t\tif (pr_builtin[binum] && pr_builtin[binum] != PF_Fixme && BuiltinList[i].bifunc)\n\t\t\t\t\tcontinue;\n\t\t\t\tpr_builtin[binum] = BuiltinList[i].bifunc;\n\t\t\t\treturn binum;\n\t\t\t}\n\t\t\tCon_Printf(\"No more builtin slots to allocate for %s\\n\", builtinname);\n\t\t\tbreak;\n\t\t}\n\t}\n\tCon_DPrintf(\"Unknown ssqc builtin: %s\\n\", builtinname);\n\treturn 0;\n}\n\nvoid PR_ResetBuiltins(progstype_t type)\t//fix all nulls to PF_FIXME and add any extras that have a big number.\n{\n\tint i;\n\n\tint builtincount[sizeof(pr_builtin)/sizeof(pr_builtin[0])];\n\n\t//map the core builtins for the relevant game type\n\tif (type == PROG_QW)\n\t{\n\t\tfor (i = 0; BuiltinList[i].name; i++)\n\t\t{\n\t\t\tif (BuiltinList[i].qwnum)\n\t\t\t{\n\t\t\t\tif (pr_builtin[BuiltinList[i].qwnum])\n\t\t\t\t\tSys_Error(\"Cannot assign builtin %s, already taken\\n\", BuiltinList[i].name);\n\t\t\t\tpr_builtin[BuiltinList[i].qwnum] = BuiltinList[i].bifunc;\n\t\t\t}\n\t\t}\n\t}\n#ifdef HEXEN2\n\telse if (type == PROG_H2)\n\t{\n\t\tfor (i = 0; BuiltinList[i].name; i++)\n\t\t{\n\t\t\tif (BuiltinList[i].h2num)\n\t\t\t{\n\t\t\t\tif (pr_builtin[BuiltinList[i].h2num])\n\t\t\t\t\tSys_Error(\"Cannot assign builtin %s, already taken\\n\", BuiltinList[i].name);\n\t\t\t\tpr_builtin[BuiltinList[i].h2num] = BuiltinList[i].bifunc;\n\t\t\t}\n\t\t}\n\t}\n#endif\n\telse\n\t{\n\t\tfor (i = 0; BuiltinList[i].name; i++)\n\t\t{\n\t\t\tif (BuiltinList[i].nqnum)\n\t\t\t{\n\t\t\t\tif (pr_builtin[BuiltinList[i].nqnum])\n\t\t\t\t\tSys_Error(\"Cannot assign builtin %s, already taken\\n\", BuiltinList[i].name);\n\t\t\t\tpr_builtin[BuiltinList[i].nqnum] = BuiltinList[i].bifunc;\n\t\t\t}\n\t\t}\n\t}\n\n#if defined(HAVE_LEGACY) && defined(NETPREPARSE)\n\tif (type == PROG_PREREL)\n\t{\n\t\tpr_builtin[52] = PF_qtSingle_WriteByte;\n\t\tpr_builtin[53] = PF_qtSingle_WriteChar;\n\t\tpr_builtin[54] = PF_qtSingle_WriteShort;\n\t\tpr_builtin[55] = PF_qtSingle_WriteLong;\n\t\tpr_builtin[56] = PF_qtSingle_WriteCoord;\n\t\tpr_builtin[57] = PF_qtSingle_WriteAngle;\n\t\tpr_builtin[58] = PF_qtSingle_WriteString;\n\t\t//lack of writeentity is intentional (prerel doesn't have it.\n\n\t\tpr_builtin[59] = PF_qtBroadcast_WriteByte;\n\t\tpr_builtin[60] = PF_qtBroadcast_WriteChar;\n\t\tpr_builtin[61] = PF_qtBroadcast_WriteShort;\n\t\tpr_builtin[62] = PF_qtBroadcast_WriteLong;\n\t\tpr_builtin[63] = PF_qtBroadcast_WriteCoord;\n\t\tpr_builtin[64] = PF_qtBroadcast_WriteAngle;\n\t\tpr_builtin[65] = PF_qtBroadcast_WriteString;\n\t\tpr_builtin[66] = PF_qtBroadcast_WriteEntity;\n\t}\n#endif\n\n\tif (type == PROG_QW)\n\t{\n\t\t//this conflicts with dp's logarithm builtin.\n\t\tPR_EnableEBFSBuiltin(\"precache_vwep_model\",\t532);\n\n\t\tif (pr_imitatemvdsv.value>0)\t//pretend to be mvdsv for a bit.\n\t\t{\n\t\t\tif (\n\t\t\t\tPR_EnableEBFSBuiltin(\"executecommand\",\t83) != 83 ||\n\t\t\t\tPR_EnableEBFSBuiltin(\"mvdtokenize\",\t\t84) != 84 ||\n\t\t\t\tPR_EnableEBFSBuiltin(\"mvdargc\",\t\t\t85) != 85 ||\n\t\t\t\tPR_EnableEBFSBuiltin(\"mvdargv\",\t\t\t86) != 86 ||\n\t\t\t\tPR_EnableEBFSBuiltin(\"teamfield\",\t\t87) != 87 ||\n\t\t\t\tPR_EnableEBFSBuiltin(\"substr\",\t\t\t88) != 88 ||\n\t\t\t\tPR_EnableEBFSBuiltin(\"mvdstrcat\",\t\t89) != 89 ||\n\t\t\t\tPR_EnableEBFSBuiltin(\"mvdstrlen\",\t\t90) != 90 ||\n\t\t\t\tPR_EnableEBFSBuiltin(\"str2byte\",\t\t91) != 91 ||\n\t\t\t\tPR_EnableEBFSBuiltin(\"str2short\",\t\t92) != 92 ||\n\t\t\t\tPR_EnableEBFSBuiltin(\"mvdnewstr\",\t\t93) != 93 ||\n\t\t\t\tPR_EnableEBFSBuiltin(\"mvdfreestr\",\t\t94) != 94 ||\n\t\t\t\tPR_EnableEBFSBuiltin(\"conprint\",\t\t95) != 95 ||\n\t\t\t\tPR_EnableEBFSBuiltin(\"readcmd\",\t\t\t96) != 96 ||\n\t\t\t\tPR_EnableEBFSBuiltin(\"mvdstrcpy\",\t\t97) != 97 ||\n\t\t\t\tPR_EnableEBFSBuiltin(\"strstr\",\t\t\t98) != 98 ||\n\t\t\t\tPR_EnableEBFSBuiltin(\"mvdstrncpy\",\t\t99) != 99 ||\n\t\t\t\tPR_EnableEBFSBuiltin(\"logtext\",\t\t\t100)!= 100 ||\n\t//\t\t\tPR_EnableEBFSBuiltin(\"redirectcmd\",\t\t101)!= 101 ||\n\t\t\t\tPR_EnableEBFSBuiltin(\"mvdcalltimeofday\",102)!= 102 ||\n\t\t\t\tPR_EnableEBFSBuiltin(\"forcedemoframe\",\t103)!= 103)\n\t\t\t\tCon_Printf(\"Failed to register all MVDSV builtins\\n\");\n\t\t\telse\n\t\t\t\tCon_Printf(\"Be aware that MVDSV does not follow standards. Please encourage mod developers to not require pr_imitatemvdsv to be set.\\n\");\n\t\t}\n\t}\n\n#ifdef HAVE_LEGACY\t//FIXME: this should no longer be needed, but there may be unmaintained mods...\n\tif (sv.world.remasterlogic)\n\t{\t//everyone needs their own set of incompatibile builtins... yay!...\n\t\tPR_EnableEBFSBuiltin(\"ex_finaleFinished\",\t79);//logfrag,\t(tq_strzone)\n\t\tPR_EnableEBFSBuiltin(\"ex_localsound\",\t\t80);//infokey,\t(tq_strunzone)\n\t\tPR_EnableEBFSBuiltin(\"ex_draw_point\",\t\t81);//stof,\t\t(tq_strlen)\n\t\tPR_EnableEBFSBuiltin(\"ex_draw_line\",\t\t82);//multicast,(tq_strcat)\n\t\tPR_EnableEBFSBuiltin(\"ex_draw_arrow\",\t\t83);//\t\t\t(tq_substring)\n\t\tPR_EnableEBFSBuiltin(\"ex_draw_ray\",\t\t\t84);//\t\t\t(tq_stof)\n\t\tPR_EnableEBFSBuiltin(\"ex_draw_circle\",\t\t85);//\t\t\t(tq_stov)\n\t\tPR_EnableEBFSBuiltin(\"ex_draw_bounds\",\t\t86);//\t\t\t(tq_fopen)\n\t\tPR_EnableEBFSBuiltin(\"ex_draw_worldtext\",\t87);//\t\t\t(tq_fclose)\n\t\tPR_EnableEBFSBuiltin(\"ex_draw_sphere\",\t\t88);//\t\t\t(tq_fgets)\n\t\tPR_EnableEBFSBuiltin(\"ex_draw_cylinder\",\t89);//\t\t\t(tq_fputs)\n\t\tPR_EnableEBFSBuiltin(\"ex_centerprint\",\t\t90);//tracebox\n\t\tPR_EnableEBFSBuiltin(\"ex_bprint\",\t\t\t91);//randomvec\n\t\tPR_EnableEBFSBuiltin(\"ex_sprint\",\t\t\t92);//getlight\n\n\t\tPR_EnableEBFSBuiltin(\"checkextension\",\t\t99);\n\t}\n#endif\n\n\tmemset(builtincount, 0, sizeof(builtincount));\n\tfor (i = 0; i < pr_numbuiltins; i++)\t//clean up nulls\n\t{\n\t\tif (!pr_builtin[i])\n\t\t{\n\t\t\tpr_builtin[i] = PF_Fixme;\n\t\t}\n\t\telse\n\t\t\tbuiltincount[i]=100;\n\t}\n\tif (!pr_compatabilitytest.value && !sv.world.remasterlogic)\n\t{\n\t\tfor (i = 0; BuiltinList[i].name; i++)\n\t\t{\n\t\t\tif (BuiltinList[i].ebfsnum && !BuiltinList[i].obsolete && BuiltinList[i].bifunc != PF_Fixme)\n\t\t\t\tbuiltincount[BuiltinList[i].ebfsnum]++;\n\t\t}\n\t\tfor (i = 0; BuiltinList[i].name; i++)\n\t\t{\n\t\t\tif (BuiltinList[i].ebfsnum)\n\t\t\t{\n\t\t\t\tif (pr_builtin[BuiltinList[i].ebfsnum] == PF_Fixme && builtincount[BuiltinList[i].ebfsnum] == (BuiltinList[i].obsolete?0:1))\n\t\t\t\t{\n\t\t\t\t\tpr_builtin[BuiltinList[i].ebfsnum] = BuiltinList[i].bifunc;\n//\t\t\t\t\tCon_DPrintf(\"Enabled %s (%i)\\n\", BuiltinList[i].name, BuiltinList[i].ebfsnum);\n\t\t\t\t}\n//\t\t\t\telse if (pr_builtin[i] != BuiltinList[i].bifunc)\n//\t\t\t\t\tCon_DPrintf(\"Not enabled %s (%i)\\n\", BuiltinList[i].name, BuiltinList[i].ebfsnum);\n\t\t\t}\n\t\t}\n\t}\n\n\t{\n\t\tchar *builtinmap;\n\t\tint binum;\n\t\tbuiltinmap = COM_LoadTempFile(\"fte_bimap.txt\", 0, NULL);\n\t\twhile(1)\n\t\t{\n\t\t\tbuiltinmap = COM_Parse(builtinmap);\n\t\t\tif (!builtinmap)\n\t\t\t\tbreak;\n\t\t\tbinum = atoi(com_token);\n\t\t\tbuiltinmap = COM_Parse(builtinmap);\n\n\t\t\tfor (i = 0; BuiltinList[i].name; i++)\n\t\t\t{\n\t\t\t\tif (!strcmp(BuiltinList[i].name, com_token) && (BuiltinList[i].bifunc != PF_Fixme||!i))\n\t\t\t\t{\n\t\t\t\t\tpr_builtin[binum] = BuiltinList[i].bifunc;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!BuiltinList[i].name)\n\t\t\t\tCon_Printf(\"Failed to map builtin %s to %i specified in fte_bimap.txt\\n\", com_token, binum);\n\t\t}\n\t}\n}\n\nvoid PR_SVExtensionList_f(void)\n{\n\tint i, j;\n\tint ebi;\n\tint bi;\n\tqc_extension_t *extlist;\n\tqboolean inactive, wrongmodule;\n\tchar *extname;\n\tint num;\n\tchar biissues[8192];\n\n\textcheck_t extcheck = {&sv.world, Net_PextMask(PROTOCOL_VERSION_FTE1, false)&PEXT_SERVERADVERTISE, Net_PextMask(PROTOCOL_VERSION_FTE2, false)&PEXT2_SERVERADVERTISE};\n\n#define SHOW_ACTIVEEXT 1\n#define SHOW_ACTIVEBI 2\n#define SHOW_NOTSUPPORTEDEXT 4\n#define SHOW_NOTACTIVEEXT 8\n#define SHOW_NOTACTIVEBI 16\n\n\tint showflags = atoi(Cmd_Argv(1));\n\tif (!showflags)\n\t\tshowflags = SHOW_ACTIVEEXT|SHOW_NOTACTIVEEXT;\n\n\tif (showflags & (SHOW_ACTIVEBI|SHOW_NOTACTIVEBI))\n\tfor (i = 0; BuiltinList[i].name; i++)\n\t{\n\t\tif (!BuiltinList[i].ebfsnum)\n\t\t\tcontinue;\t//a reserved builtin.\n\t\tif (BuiltinList[i].bifunc == PF_Fixme)\n\t\t{\t//can give a lot of false positives due to builtins that exist only in menuqc.\n\t\t\tif (showflags & SHOW_NOTACTIVEBI)\n\t\t\t\tCon_Printf(\"^1#%i:%s is not ssqc\\n\", BuiltinList[i].ebfsnum, BuiltinList[i].name);\n\t\t}\n\t\telse if (pr_builtin[BuiltinList[i].ebfsnum] == BuiltinList[i].bifunc)\n\t\t{\n\t\t\tif (showflags & SHOW_ACTIVEBI)\n\t\t\t\tCon_Printf(\"%s is active on %i\\n\", BuiltinList[i].name, BuiltinList[i].ebfsnum);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (showflags & SHOW_NOTACTIVEBI)\n\t\t\t\tCon_Printf(\"^4%s is NOT active (%i)\\n\", BuiltinList[i].name, BuiltinList[i].ebfsnum);\n\t\t}\n\t}\n\n\tif (showflags & (SHOW_NOTSUPPORTEDEXT|SHOW_NOTACTIVEEXT|SHOW_ACTIVEEXT))\n\t{\n\t\textlist = QSG_Extensions;\n\n\t\tfor (i = 0; i < QSG_Extensions_count; i++)\n\t\t{\n\t\t\t*biissues = 0;\n\t\t\tinactive = false;\n\t\t\tif (!extlist[i].name)\n\t\t\t\tcontinue;\n\n\t\t\tfor (ebi = 0; ebi < countof(extlist[i].builtinnames) && extlist[i].builtinnames[ebi]; ebi++)\n\t\t\t{\n\t\t\t\twrongmodule = false;\n\n\t\t\t\tfor (bi = 0; BuiltinList[bi].name; bi++)\n\t\t\t\t{\n\t\t\t\t\tif (!strcmp(BuiltinList[bi].name, extlist[i].builtinnames[ebi]))\n\t\t\t\t\t{\n\t\t\t\t\t\tif (BuiltinList[bi].bifunc == PF_Fixme)\n\t\t\t\t\t\t\twrongmodule=true;\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (!BuiltinList[bi].name)\n\t\t\t\t{\n\t\t\t\t\tif (wrongmodule)\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\tQ_strncatz2(biissues, va(CON_ERROR\"\\t^4#:%s is not known\\n\", extlist[i].builtinnames[ebi]));\n\t\t\t\t\tinactive = true;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\n\t\t\t\tif (progstype == PROG_QW)\n\t\t\t\t\tnum = BuiltinList[bi].qwnum;\n\t\t\t\telse if (progstype == PROG_H2)\n\t\t\t\t\tnum = BuiltinList[bi].h2num;\n\t\t\t\telse\n\t\t\t\t\tnum = BuiltinList[bi].nqnum;\n\t\t\t\tif (!num)\n\t\t\t\t\tnum = BuiltinList[bi].ebfsnum;\n\n\t\t\t\tif (!num)\n\t\t\t\t\t;//builtins with no number are handled at load time. there are no number conflicts so no way for the builtin to be inactive. if there is an error then its not because of the extension itself.\n\t\t\t\telse if (pr_builtin[num] != BuiltinList[bi].bifunc)\n\t\t\t\t{\n\t\t\t\t\tif (pr_builtin[num] == PF_Fixme)\n\t\t\t\t\t\tQ_strncatz2(biissues, va(\"\\t^4#%i:%s is not implemented\\n\", num, BuiltinList[bi].name));\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tfor (j = 0; BuiltinList[j].name; j++)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (BuiltinList[j].bifunc == pr_builtin[num])\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tQ_strncatz2(biissues, va(\"\\t#%i:%s is currently %s\\n\", num,BuiltinList[bi].name, BuiltinList[j].name));\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (!BuiltinList[j].name)\n\t\t\t\t\t\t\tQ_strncatz2(biissues, va(\"\\t^4#%i:%s is currently unknown\\n\", num, BuiltinList[bi].name));\n\t\t\t\t\t}\n\t\t\t\t\tinactive = true;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (extlist[i].description)\n\t\t\t\textname = va(\"^[%s\\\\tip\\\\%s^]\", extlist[i].name, extlist[i].description);\n\t\t\telse\n\t\t\t\textname = extlist[i].name;\n\n\t\t\tif (extlist[i].extensioncheck && !extlist[i].extensioncheck(&extcheck))\n\t\t\t{\n\t\t\t\tif (showflags & SHOW_NOTSUPPORTEDEXT)\n\t\t\t\t\tCon_Printf(CON_WARNING\"protocol %s is blocked\\n%s\", extname, biissues);\n\t\t\t}\n\t\t\telse if (inactive)\n\t\t\t{\n\t\t\t\tif (showflags & SHOW_NOTSUPPORTEDEXT)\n\t\t\t\t\tCon_Printf(CON_ERROR\"%s is inactive\\n%s\", extname, biissues);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (showflags & SHOW_ACTIVEEXT)\n\t\t\t\t{\n\t\t\t\t\tif (!extlist[i].builtinnames[0])\n\t\t\t\t\t\tCon_Printf(\"%s is supported\\n%s\", extname, biissues);\n\t\t\t\t\telse\n\t\t\t\t\t\tCon_Printf(\"%s is currently active\\n%s\", extname, biissues);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nint pr_numbuiltins = sizeof(pr_builtin)/sizeof(pr_builtin[0]);\n\nvoid PR_RegisterFields(void)\t//it's just easier to do it this way.\n{\n#define comfieldfloat(name,desc) PR_RegisterFieldVar(svprogfuncs, ev_float, #name, (size_t)&((stdentvars_t*)0)->name, -1);\n#define comfieldint(name,desc) PR_RegisterFieldVar(svprogfuncs, ev_integer, #name, (size_t)&((stdentvars_t*)0)->name, -1);\n#define comfieldvector(name,desc) PR_RegisterFieldVar(svprogfuncs, ev_vector, #name, (size_t)&((stdentvars_t*)0)->name, -1);\n#define comfieldentity(name,desc) PR_RegisterFieldVar(svprogfuncs, ev_entity, #name, (size_t)&((stdentvars_t*)0)->name, -1);\n#define comfieldstring(name,desc) PR_RegisterFieldVar(svprogfuncs, ev_string, (((size_t)&((stdentvars_t*)0)->name==(size_t)&((stdentvars_t*)0)->message)?\"_\"#name:#name), (size_t)&((stdentvars_t*)0)->name, -1);\n#define comfieldfunction(name, typestr,desc) PR_RegisterFieldVar(svprogfuncs, ev_function, #name, (size_t)&((stdentvars_t*)0)->name, -1);\ncomqcfields\n#undef comfieldfloat\n#undef comfieldint\n#undef comfieldvector\n#undef comfieldentity\n#undef comfieldstring\n#undef comfieldfunction\n#ifdef VM_Q1\n#define comfieldfloat(name,desc) PR_RegisterFieldVar(svprogfuncs, ev_float, #name, sizeof(stdentvars_t) + (size_t)&((extentvars_t*)0)->name, -1);\n#define comfieldint(name,desc) PR_RegisterFieldVar(svprogfuncs, ev_integer, #name, sizeof(stdentvars_t) + (size_t)&((extentvars_t*)0)->name, -1);\n#define comfieldvector(name,desc) PR_RegisterFieldVar(svprogfuncs, ev_vector, #name, sizeof(stdentvars_t) + (size_t)&((extentvars_t*)0)->name, -1);\n#define comfieldentity(name,desc) PR_RegisterFieldVar(svprogfuncs, ev_entity, #name, sizeof(stdentvars_t) + (size_t)&((extentvars_t*)0)->name, -1);\n#define comfieldstring(name,desc) PR_RegisterFieldVar(svprogfuncs, ev_string, #name, sizeof(stdentvars_t) + (size_t)&((extentvars_t*)0)->name, -1);\n#define comfieldfunction(name, typestr,desc) PR_RegisterFieldVar(svprogfuncs, ev_function, #name, sizeof(stdentvars_t) + (size_t)&((extentvars_t*)0)->name, -1);\n#else\n#define comfieldfloat(ssqcname,desc) PR_RegisterFieldVar(svprogfuncs, ev_float, #ssqcname, (size_t)&((stdentvars_t*)0)->ssqcname, -1);\n#define comfieldint(ssqcname,desc) PR_RegisterFieldVar(svprogfuncs, ev_integer, #ssqcname, (size_t)&((stdentvars_t*)0)->ssqcname, -1);\n#define comfieldvector(ssqcname,desc) PR_RegisterFieldVar(svprogfuncs, ev_vector, #ssqcname, (size_t)&((stdentvars_t*)0)->ssqcname, -1);\n#define comfieldentity(ssqcname,desc) PR_RegisterFieldVar(svprogfuncs, ev_entity, #ssqcname, (size_t)&((stdentvars_t*)0)->ssqcname, -1);\n#define comfieldstring(ssqcname,desc) PR_RegisterFieldVar(svprogfuncs, ev_string, #ssqcname, (size_t)&((stdentvars_t*)0)->ssqcname, -1);\n#define comfieldfunction(ssqcname, typestr,desc) PR_RegisterFieldVar(svprogfuncs, ev_function, #ssqcname, (size_t)&((stdentvars_t*)0)->ssqcname, -1);\n#endif\n\ncomextqcfields\nsvextqcfields\n\n#undef comfieldfloat\n#undef comfieldint\n#undef comfieldvector\n#undef comfieldentity\n#undef comfieldstring\n#undef comfieldfunction\n\n\t//Tell the qc library to split the entity fields each side.\n\t//the fields above become < 0, the remaining fields specified by the qc stay where the mod specified, as far as possible (with addons at least).\n\t//this means that custom array offsets still work in mods like ktpro.\n\tif (!pr_fixbrokenqccarrays.ival && pr_imitatemvdsv.ival)\n\t\tPR_RegisterFieldVar(svprogfuncs, 0, NULL, 1,0);\n\telse\n\t\tPR_RegisterFieldVar(svprogfuncs, 0, NULL, pr_fixbrokenqccarrays.ival,0);\n}\n\n//targets\n#define QW\t\t(1u<<0)\t//exists in qwssqc\n#define NQ\t\t(1u<<1)\t//exists in nqssqc\n#define FTE_CS\t(1u<<2)\t//fte's csqc globals (the original)\n#define QSS_CS\t(1u<<3)\t//qss's csqc globals (really nq, for simplicity)\n#define DP_CS\t(1u<<4)\t//dp's csqc globals (eww!)\n#define MENU\t(1u<<5)\t//exists in menuqc\n#define H2\t\t(1u<<6)\t//exists in h2ssqc\n//mere flag\n#define FTE\t\t(1u<<7)\t//use fte opcodes\n#define ID1\t\t(1u<<8)\t//symbol conflicts with vanilla defs.qc (so stripped, with exceptions)\n#define DPX\t\t(1u<<9)\t//symbol conflicts with dpextensions.qc\n\n#define CS\t\t(QSS_CS|FTE_CS|DP_CS)\t\t//exists in csqc\n#define GAME\t(QW|NQ|CS|H2)\t//all but menu\n#ifdef HEXEN2\n#define ALL (QW|NQ|H2|CS|MENU)\n#else\n#define ALL (QW|NQ|CS|MENU)\n#endif\n#define CORE\ntypedef struct\n{\n\tchar *name;\n\tchar *type;\n\n\tunsigned int module;\n\n\tchar *desc;\n\n\tfloat value;\n\tchar *valuestr;\n\tqboolean misc;\n} knowndef_t;\n#include \"cl_master.h\"\nvoid Key_PrintQCDefines(vfsfile_t *f, qboolean defines);\n\n#ifdef SERVERONLY\n\n#elif defined(NOQCDESCRIPTIONS) && NOQCDESCRIPTIONS > 1\n\n#else\nstatic qboolean PR_DumpPlatform_GenIfdef(vfsfile_t *f, unsigned int filetarg, unsigned int symboltarg, unsigned int *curtarg)\n{\n\tif (!(symboltarg & filetarg))\n\t\treturn false;\t\n\tif ((symboltarg&filetarg) != (*curtarg&filetarg))\n\t{\n\t\tif (*curtarg != (ALL & ~filetarg))\n\t\t\tVFS_PRINTF(f, \"#endif\\n\");\n\n\t\tif (((symboltarg | (~filetarg)) & ALL) == ALL)\n\t\t\t*curtarg = ALL & ~filetarg;\n\t\telse\n\t\t{\n\t\t\t*curtarg = symboltarg;\n\t\t\tsymboltarg = *curtarg & (ALL & filetarg);\n\t\t\tif (symboltarg & CS)\n\t\t\t\tsymboltarg |= CS;\n\t\t\tswitch(symboltarg)\n\t\t\t{\n\t\t\tcase 0:\n\t\t\t\treturn false;\n\t\t\tcase QW:\n\t\t\t\tVFS_PRINTF(f, \"#if defined(QWSSQC)\\n\");\n\t\t\t\tbreak;\n\t\t\tcase NQ:\n\t\t\t\tVFS_PRINTF(f, \"#if defined(NQSSQC)\\n\");\n\t\t\t\tbreak;\n\t\t\tcase QW|NQ:\n\t\t\t\tVFS_PRINTF(f, \"#ifdef SSQC\\n\");\n\t\t\t\tbreak;\n\t\t\tcase CS:\n\t\t\t\tVFS_PRINTF(f, \"#ifdef CSQC\\n\");\n\t\t\t\tbreak;\n\t\t\tcase QW|CS:\n\t\t\t\tVFS_PRINTF(f, \"#if defined(CSQC) || defined(QWSSQC)\\n\");\n\t\t\t\tbreak;\n\t\t\tcase NQ|CS:\n\t\t\t\tVFS_PRINTF(f, \"#if defined(CSQC) || defined(NQSSQC)\\n\");\n\t\t\t\tbreak;\n\t\t\tcase NQ|CS|QW:\n\t\t\t\tVFS_PRINTF(f, \"#if defined(CSQC) || defined(SSQC)\\n\");\n\t\t\t\tbreak;\n\t\t\tcase MENU:\n\t\t\t\tVFS_PRINTF(f, \"#ifdef MENU\\n\");\n\t\t\t\tbreak;\n\t\t\tcase QW|MENU:\n\t\t\t\tVFS_PRINTF(f, \"#if defined(QWSSQC) || defined(MENU)\\n\");\n\t\t\t\tbreak;\n\t\t\tcase NQ|MENU:\n\t\t\t\tVFS_PRINTF(f, \"#if defined(NQSSQC) || defined(MENU)\\n\");\n\t\t\t\tbreak;\n\t\t\tcase QW|NQ|MENU:\n\t\t\t\tVFS_PRINTF(f, \"#if defined(SSQC) || defined(MENU)\\n\");\n\t\t\t\tbreak;\n\t\t\tcase CS|MENU:\n\t\t\t\tVFS_PRINTF(f, \"#if defined(CSQC) || defined(MENU)\\n\");\n\t\t\t\tbreak;\n\t\t\tcase QW|CS|MENU:\n\t\t\t\tVFS_PRINTF(f, \"#if defined(CSQC) || defined(QWSSQC) || defined(MENU)\\n\");\n\t\t\t\tbreak;\n\t\t\tcase NQ|CS|MENU:\n\t\t\t\tVFS_PRINTF(f, \"#if defined(CSQC) || defined(NQSSQC) || defined(MENU)\\n\");\n\t\t\t\tbreak;\n\t\t\tcase H2:\n\t\t\t\tVFS_PRINTF(f, \"#ifdef H2\\n\");\n\t\t\t\tbreak;\n\t\t\tcase H2|QW:\n\t\t\t\tVFS_PRINTF(f, \"#if defined(H2) || defined(QWSSQC)\\n\");\n\t\t\t\tbreak;\n\t\t\tcase H2|NQ:\n\t\t\t\tVFS_PRINTF(f, \"#if defined(H2) || defined(NQSSQC)\\n\");\n\t\t\t\tbreak;\n\t\t\tcase H2|QW|NQ:\n\t\t\t\tVFS_PRINTF(f, \"#if defined(H2) || defined(SSQC)\\n\");\n\t\t\t\tbreak;\n\t\t\tcase H2|CS:\n\t\t\t\tVFS_PRINTF(f, \"#if defined(H2) || defined(CSQC)\\n\");\n\t\t\t\tbreak;\n\t\t\tcase H2|QW|CS:\n\t\t\t\tVFS_PRINTF(f, \"#if defined(H2) || defined(CSQC) || defined(QWSSQC)\\n\");\n\t\t\t\tbreak;\n\t\t\tcase H2|NQ|CS:\n\t\t\t\tVFS_PRINTF(f, \"#if defined(H2) || defined(CSQC) || defined(NQSSQC)\\n\");\n\t\t\t\tbreak;\n\t\t\tcase H2|NQ|CS|QW:\n\t\t\t\tVFS_PRINTF(f, \"#if defined(H2) || defined(CSQC) || defined(SSQC)\\n\");\n\t\t\t\tbreak;\n\t\t\tcase H2|MENU:\n\t\t\t\tVFS_PRINTF(f, \"#if defined(H2) || defined(MENU)\\n\");\n\t\t\t\tbreak;\n\t\t\tcase H2|QW|MENU:\n\t\t\t\tVFS_PRINTF(f, \"#if defined(H2) || defined(QWSSQC) || defined(MENU)\\n\");\n\t\t\t\tbreak;\n\t\t\tcase H2|NQ|MENU:\n\t\t\t\tVFS_PRINTF(f, \"#if defined(H2) || defined(NQSSQC) || defined(MENU)\\n\");\n\t\t\t\tbreak;\n\t\t\tcase H2|QW|NQ|MENU:\n\t\t\t\tVFS_PRINTF(f, \"#if defined(H2) || defined(SSQC) || defined(MENU)\\n\");\n\t\t\t\tbreak;\n\t\t\tcase H2|CS|MENU:\n\t\t\t\tVFS_PRINTF(f, \"#if defined(H2) || defined(CSQC) || defined(MENU)\\n\");\n\t\t\t\tbreak;\n\t\t\tcase H2|QW|CS|MENU:\n\t\t\t\tVFS_PRINTF(f, \"#if defined(H2) || defined(CSQC) || defined(QWSSQC) || defined(MENU)\\n\");\n\t\t\t\tbreak;\n\t\t\tcase H2|NQ|CS|MENU:\n\t\t\t\tVFS_PRINTF(f, \"#if defined(H2) || defined(CSQC) || defined(NQSSQC) || defined(MENU)\\n\");\n\t\t\t\tbreak;\n\t\t\tcase ALL:\n\t\t\t\tVFS_PRINTF(f, \"#if 1\\n\");\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tVFS_PRINTF(f, \"#if 0 //???\\n\");\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\treturn true;\n}\n\nstruct symtable_s\n{\n\tunsigned int targ;\n\tconst char *fname;\t\t//filename to read symbol list from.\n\tconst char *warning;\t//text inserted before the symbol name. generally terse.\n\tconst char *define;\t\t//optional text to insert at top-level (to match the warning).\n\tconst char *symbols;\n};\nstatic void PR_DumpPlatform_LoadSymbolTables(vfsfile_t *outfile, struct symtable_s *symtabs, unsigned int targ)\n{\n\tchar *i, *o;\n\tqofs_t sz;\n\tchar *f;\n\tfor (; symtabs->fname; symtabs++)\n\t{\n\t\tif (!(symtabs->targ & targ))\n\t\t\tcontinue;\n\t\tif (!symtabs->warning)\n\t\t\tcontinue;\n\t\tf = FS_MallocFile(va(\"src/%s\", symtabs->fname), FS_GAME, &sz);\n\t\tif (!f)\n\t\t\tf = FS_MallocFile(symtabs->fname, FS_GAME, &sz);\n\t\tif (f)\n\t\t{\n\t\t\ti = f;\n\t\t\tsymtabs->symbols = o = Z_Malloc(sz + 16);\n\n\t#define iswhite(c) ((c) == ' ' || (c) == '\\r' || (c) == '\\r' || (c) == '\\n')\n\n\t\t\t*o++ = '\\n';\n\t\t\twhile (*i)\n\t\t\t{\n\t\t\t\twhile (iswhite(*i))\n\t\t\t\t\ti++;\n\t\t\t\twhile (*i && !iswhite(*i))\n\t\t\t\t\t*o++ = *i++;\n\t\t\t\t*o++ = '\\n';\n\t\t\t}\n\t\t\t*o = 0;\n\t\t\tFS_FreeFile(f);\n\n\t\t\tif (symtabs->define)\n\t\t\t\tVFS_PRINTF(outfile, \"%s\", symtabs->define);\n\t\t}\n\t\telse\n\t\t\tCon_Printf(\"%s not found, not filtering\\n\", symtabs->fname);\n\t}\n}\nstatic void PR_DumpPlatform_SymbolType(vfsfile_t *f, const struct symtable_s *symtabs, const char *symtype, const char *symbol)\n{\t//prefixes any symbols with the engines that they're not in...\n\tint symstart = 0;\n\tconst char *colon;\n\n\t//skip over any \"typedef struct {...;};\\n\" or \"#define foo bar\\n\" blocks in there\n\twhile ((colon = strstr(symtype+symstart, \"\\n\")))\n\t{\n\t\tsymstart = (colon+1) - symtype;\n\t\tcontinue;\n\t}\n\n\t//write those prefixes we tried to skip...\n\tVFS_WRITE(f, symtype, symstart);\n\tsymtype += symstart;\n\n\t//now write our modifiers\n\tsymbol = va(\"\\n%s\\n\", symbol);\n\tfor (; symtabs->fname; symtabs++)\n\t{\n\t\tif (!symtabs->symbols)\n\t\t\tcontinue;\n\n\t\tif (strstr(symtabs->symbols, symbol))\n\t\t\tcontinue;\n\t\tVFS_PRINTF(f, \"%s \", symtabs->warning);\n\t}\n\n\t//and write the rest of the type.\n\tVFS_PRINTF(f, \"%s \", symtype);\n}\n#endif\n\nvoid PR_DumpPlatform_f(void)\n{\n#ifdef SERVERONLY\n\tCon_Printf(\"This command is not available in dedicated servers, sorry.\\n\");\n#elif defined(NOQCDESCRIPTIONS) && NOQCDESCRIPTIONS > 1\n\tCon_Printf(\"This command is not available in this build, sorry.\\n\");\n#else\n\t//eg: pr_dumpplatform -FFTE -TCS -O csplat\n\n\t/*const char *keywords[] =\n\t{\n\t\t\"ignore\"\t\t//0\n\t\t\"qwqc\",\t\t\t//qw\n\t\t\"nqqc\",\t\t\t//nq\n\t\t\"ssqc\"\t\t\t//qw|nq\n\t\t\"csqc\"\t\t\t//cs\n\t\t\"csqwqc\",\t\t//cs|qw\n\t\t\"csnqqc\",\t\t//cs|nq\n\t\t\"gameqc\"\t\t//cs|nq|qw\n\t\t\"menuonly\"\t\t\t//mn\n\t\t\"mnqwqc\",\t\t//mn|qw\n\t\t\"mnnqqc\",\t\t//mn|nq\n\t\t\"mnssqc\"\t\t//mn|qw|nq\n\t\t\"mncsqc\"\t\t//mn|cs\n\t\t\"mncsqwqc\",\t\t//mn|cs|qw\n\t\t\"mncsnqqc\",\t\t//mn|cs|nq\n\t\t\"\"\t\t//mn|cs|nq|qw\n\t};*/\n\n\tint idx;\n\tint i, j;\n\tint d = 0, nd, k;\n\tvfsfile_t *f;\n\tchar *fname = \"\";\n\tchar dbgfname[MAX_OSPATH];\n\tunsigned int targ = 0;\n\tqboolean defines = false;\n\tqboolean accessors = false;\n\tqboolean depfilter = false;\n\tchar *comment;\n\n\tstruct symtable_s symtab[] = {\n\t\t{NQ,\t\"qss_ss.sym\",\t\t\"NOT_QSS\",\t\"#ifndef NOT_QSS\\n#define NOT_QSS __deprecated(\\\"Not supported by QSS\\\")\\n#endif\\n\"},\n\t\t{NQ,\t\"dp_ss.sym\",\t\t\"NOT_DP\",\t\"#ifndef NOT_DP\\n#define NOT_DP __deprecated(\\\"Not supported by Darkplaces\\\")\\n#endif\\n\"},\n\t\t{CS,\t\"qss_cs.sym\",\t\t\"NOT_QSS\",\t\"#ifndef NOT_QSS\\n#define NOT_QSS __deprecated(\\\"Not supported by QSS\\\")\\n#endif\\n\"},\n\t\t{CS,\t\"dp_cs.sym\",\t\t\"NOT_DP\",\t\"#ifndef NOT_DP\\n#define NOT_DP __deprecated(\\\"Not supported by Darkplaces\\\")\\n#endif\\n\"},\n\t\t{MENU,\t\"qss_menu.sym\",\t\t\"NOT_QSS\",\t\"#ifndef NOT_QSS\\n#define NOT_QSS __deprecated(\\\"Not supported by QSS\\\")\\n#endif\\n\"},\n\t\t{MENU,\t\"dp_menu.sym\",\t\t\"NOT_DP\",\t\"#ifndef NOT_DP\\n#define NOT_DP __deprecated(\\\"Not supported by Darkplaces\\\")\\n#endif\\n\"},\n\t\t{0,\t\tNULL,\t\t\tNULL}\n\t};\n\n#undef D\n#ifdef NOQCDESCRIPTIONS\n\t#define D(d) NULL\n#else\n\t#define D(d) d\n#endif\n\n\t/*this list is here to ensure that the file can be used as a valid initial qc file (ignoring precompiler options)*/\n\tknowndef_t knowndefs[] =\n\t{\n\t\t{\"self\",\t\t\t\t\"entity\", QW|NQ|CS|MENU, D(\"The magic me\")},\n\t\t{\"other\",\t\t\t\t\"entity\", QW|NQ|CS,\tD(\"Valid in touch functions, this is the entity that we touched.\")},\n\t\t{\"world\",\t\t\t\t\"entity\", QW|NQ|CS,\tD(\"The null entity. Hurrah. Readonly after map spawn time.\")},\n\t\t{\"time\",\t\t\t\t\"float\", QW|NQ|CS,\tD(\"The current game time. Stops when paused.\")},\n\t\t{\"cltime\",\t\t\t\t\"float\", FTE_CS,\t\tD(\"A local timer that ticks relative to local time regardless of latency, packetloss, or pause.\")},\n\t\t{\"frametime\",\t\t\t\"float\", QW|NQ|CS,\tD(\"The time since the last physics/render/input frame.\")},\n\t\t{\"player_localentnum\",\t\"float\", FTE_CS|DP_CS,\t\tD(\"This is entity number the player is seeing from/spectating, or the player themself, can change mid-map.\")},\n\t\t{\"player_localnum\",\t\t\"float\", FTE_CS|DP_CS,\t\tD(\"The 0-based player index, valid for getplayerkeyvalue calls.\")},\n\t\t{\"maxclients\",\t\t\t\"float\", FTE_CS|DP_CS,\t\tD(\"Maximum number of player slots on the server.\")},\n\t\t{\"clientcommandframe\",\t\"float\", FTE_CS|DP_CS,\t\tD(\"This is the input-frame sequence. frames < clientcommandframe have been sent to the server. frame==clientcommandframe is still being generated and can still change.\")},\n\t\t{\"servercommandframe\",\t\"float\", FTE_CS|DP_CS,\t\tD(\"This is the input-frame that was last acknowledged by the server. Input frames greater than this should be applied to the player's entity.\")},\n\t\t{\"newmis\",\t\t\t\t\"entity\", QW,\t\tD(\"A named entity that should be run soon, to reduce the effects of latency.\")},\n\t\t{\"force_retouch\",\t\t\"float\", QW|NQ|QSS_CS,\t\tD(\"If positive, causes all entities to check for triggers.\")},\n\t\t{\"mapname\",\t\t\t\t\"string\", QW|NQ|CS,\tD(\"The short name of the map.\")},\n\t\t{\"deathmatch\",\t\t\t\"float\", NQ|QSS_CS},\n\t\t{\"coop\",\t\t\t\t\"float\", NQ|QSS_CS},\n\t\t{\"teamplay\",\t\t\t\"float\", NQ|QSS_CS},\n\t\t{\"serverflags\",\t\t\t\"float\", QW|NQ|QSS_CS},\n\t\t{\"total_secrets\",\t\t\"float\", QW|NQ|QSS_CS},\n\t\t{\"total_monsters\",\t\t\"float\", QW|NQ|QSS_CS},\n\t\t{\"found_secrets\",\t\t\"float\", QW|NQ|QSS_CS},\n\t\t{\"killed_monsters\",\t\t\"float\", QW|NQ|QSS_CS},\n\t\t{\"parm1\", \"float\", QW|NQ|QSS_CS, \"Player specific mod-defined values that are transferred from one map to the next. These are set by the qc inside calls to SetNewParms and SetChangeParms, and can then be read on a per-player basis after a call to the setspawnparms builtin. They are not otherwise valid.\"},\n\t\t{\"parm2\", \"float\", QW|NQ|QSS_CS, \"Player specific mod-defined values that are transferred from one map to the next. These are set by the qc inside calls to SetNewParms and SetChangeParms, and can then be read on a per-player basis after a call to the setspawnparms builtin. They are not otherwise valid.\"},\n\t\t{\"parm3\", \"float\", QW|NQ|QSS_CS, \"Player specific mod-defined values that are transferred from one map to the next. These are set by the qc inside calls to SetNewParms and SetChangeParms, and can then be read on a per-player basis after a call to the setspawnparms builtin. They are not otherwise valid.\"},\n\t\t{\"parm4\", \"float\", QW|NQ|QSS_CS, \"Player specific mod-defined values that are transferred from one map to the next. These are set by the qc inside calls to SetNewParms and SetChangeParms, and can then be read on a per-player basis after a call to the setspawnparms builtin. They are not otherwise valid.\"},\n\t\t{\"parm5\", \"float\", QW|NQ|QSS_CS, \"Player specific mod-defined values that are transferred from one map to the next. These are set by the qc inside calls to SetNewParms and SetChangeParms, and can then be read on a per-player basis after a call to the setspawnparms builtin. They are not otherwise valid.\"},\n\t\t{\"parm6\", \"float\", QW|NQ|QSS_CS, \"Player specific mod-defined values that are transferred from one map to the next. These are set by the qc inside calls to SetNewParms and SetChangeParms, and can then be read on a per-player basis after a call to the setspawnparms builtin. They are not otherwise valid.\"},\n\t\t{\"parm7\", \"float\", QW|NQ|QSS_CS, \"Player specific mod-defined values that are transferred from one map to the next. These are set by the qc inside calls to SetNewParms and SetChangeParms, and can then be read on a per-player basis after a call to the setspawnparms builtin. They are not otherwise valid.\"},\n\t\t{\"parm8\", \"float\", QW|NQ|QSS_CS, \"Player specific mod-defined values that are transferred from one map to the next. These are set by the qc inside calls to SetNewParms and SetChangeParms, and can then be read on a per-player basis after a call to the setspawnparms builtin. They are not otherwise valid.\"},\n\t\t{\"parm9\", \"float\", QW|NQ|QSS_CS, \"Player specific mod-defined values that are transferred from one map to the next. These are set by the qc inside calls to SetNewParms and SetChangeParms, and can then be read on a per-player basis after a call to the setspawnparms builtin. They are not otherwise valid.\"},\n\t\t{\"parm10\", \"float\", QW|NQ|QSS_CS, \"Player specific mod-defined values that are transferred from one map to the next. These are set by the qc inside calls to SetNewParms and SetChangeParms, and can then be read on a per-player basis after a call to the setspawnparms builtin. They are not otherwise valid.\"},\n\t\t{\"parm11\", \"float\", QW|NQ|QSS_CS, \"Player specific mod-defined values that are transferred from one map to the next. These are set by the qc inside calls to SetNewParms and SetChangeParms, and can then be read on a per-player basis after a call to the setspawnparms builtin. They are not otherwise valid.\"},\n\t\t{\"parm12\", \"float\", QW|NQ|QSS_CS, \"Player specific mod-defined values that are transferred from one map to the next. These are set by the qc inside calls to SetNewParms and SetChangeParms, and can then be read on a per-player basis after a call to the setspawnparms builtin. They are not otherwise valid.\"},\n\t\t{\"parm13\", \"float\", QW|NQ|QSS_CS, \"Player specific mod-defined values that are transferred from one map to the next. These are set by the qc inside calls to SetNewParms and SetChangeParms, and can then be read on a per-player basis after a call to the setspawnparms builtin. They are not otherwise valid.\"},\n\t\t{\"parm14\", \"float\", QW|NQ|QSS_CS, \"Player specific mod-defined values that are transferred from one map to the next. These are set by the qc inside calls to SetNewParms and SetChangeParms, and can then be read on a per-player basis after a call to the setspawnparms builtin. They are not otherwise valid.\"},\n\t\t{\"parm15\", \"float\", QW|NQ|QSS_CS, \"Player specific mod-defined values that are transferred from one map to the next. These are set by the qc inside calls to SetNewParms and SetChangeParms, and can then be read on a per-player basis after a call to the setspawnparms builtin. They are not otherwise valid.\"},\n\t\t{\"parm16\", \"float\", QW|NQ|QSS_CS, \"Player specific mod-defined values that are transferred from one map to the next. These are set by the qc inside calls to SetNewParms and SetChangeParms, and can then be read on a per-player basis after a call to the setspawnparms builtin. They are not otherwise valid.\"},\n\t\t{\"intermission\",\t\t\"float\", FTE_CS},\n\t\t{\"v_forward\",\t\t\t\"vector\", QW|NQ|CS},\n\t\t{\"v_up\",\t\t\t\t\"vector\", QW|NQ|CS},\n\t\t{\"v_right\",\t\t\t\t\"vector\", QW|NQ|CS},\n\t\t{\"view_angles\",\t\t\t\"vector\", FTE_CS,\t\tD(\"+x=DOWN\")},\n\t\t{\"trace_allsolid\",\t\t\"float\", QW|NQ|CS},\n\t\t{\"trace_startsolid\",\t\"float\", QW|NQ|CS},\n\t\t{\"trace_fraction\",\t\t\"float\", QW|NQ|CS},\n\t\t{\"trace_endpos\",\t\t\"vector\", QW|NQ|CS},\n\t\t{\"trace_plane_normal\",\t\"vector\", QW|NQ|CS},\n\t\t{\"trace_plane_dist\",\t\"float\", QW|NQ|CS},\n\t\t{\"trace_ent\",\t\t\t\"entity\", QW|NQ|CS},\n\t\t{\"trace_inopen\",\t\t\"float\", QW|NQ|CS},\n\t\t{\"trace_inwater\",\t\t\"float\", QW|NQ|CS},\n\t\t{\"input_timelength\",\t\"float\", FTE_CS},\n\t\t{\"input_angles\",\t\t\"vector\", FTE_CS,\tD(\"+x=DOWN\")},\n\t\t{\"input_movevalues\",\t\"vector\", FTE_CS},\n\t\t{\"input_buttons\",\t\t\"float\", FTE_CS},\n\t\t{\"input_impulse\",\t\t\"float\", FTE_CS},\n\t\t{\"msg_entity\",\t\t\t\"entity\", QW|NQ|QSS_CS},\n\t\t{\"main\",\t\t\t\t\"void()\", QW|NQ|QSS_CS, D(\"This function is never called, and is effectively dead code.\")},\n\t\t{\"StartFrame\",\t\t\t\"void()\", QW|NQ|QSS_CS, D(\"Called at the start of each new physics frame. Player entities may think out of sequence so try not to depend upon explicit ordering too much.\")},\n\t\t{\"PlayerPreThink\",\t\t\"void()\", QW|NQ|QSS_CS, D(\"With Prediction(QW compat/FTE default): Called before the player's input commands are processed.\\nNo Prediction(NQ compat): Called AFTER the player's movement intents have already been processed (ie: velocity will have already changed according to input_*, but before the actual position change.\")},\n\t\t{\"PlayerPostThink\",\t\t\"void()\", QW|NQ|QSS_CS, D(\"Called after the player's input commands are processed.\")},\n\t\t{\"ClientKill\",\t\t\t\"void()\", QW|NQ|QSS_CS, D(\"Called in response to 'cmd kill' (or just 'kill').\")},\n\t\t{\"ClientConnect\",\t\t\"void()\", QW|NQ|QSS_CS|ID1, D(\"Called after the connecting client has finished loading and is ready to receive active entities. Note that this is NOT the first place that a client might be referred to. To determine if the client has csqc active (and kick anyone that doesn't), you can use if(infokeyf(self,INFOKEY_P_CSQCACTIVE)) {sprint(self, \\\"CSQC is required for this server\\\\n\\\");dropclient(self);}\")},\n\t\t{\"PutClientInServer\",\t\"void()\", QW|NQ|QSS_CS, D(\"Enginewise, this is only ever called immediately after ClientConnect and is thus a little redundant. Modwise, this is also called for respawning a player etc.\")},\n\t\t{\"ClientDisconnect\",\t\"void()\", QW|NQ|QSS_CS, D(\"Called once a client disconnects or times out. Not guarenteed to be called on map changes.\")},\n\t\t{\"SetNewParms\",\t\t\t\"void()\", QW|NQ|QSS_CS, D(\"Called without context when a new client initially connects (before ClientConnect is even called). This function is expected to only set the parm* globals so that they can be decoded properly later. You should not rely on 'self' being set.\")},\n\t\t{\"SetChangeParms\",\t\t\"void()\", QW|NQ|QSS_CS, D(\"Called for each client on map changes. Should copy various entity fields to the parm* globals.\")},\n\n\t\t{\"CSQC_Init\",\t\t\t\"void(optional float apilevel, string enginename, float engineversion)\", DP_CS, D(\"Arg list is shorter than FTE+QSS\")},\n\t\t{\"CSQC_Shutdown\",\t\t\"void()\", DP_CS},\n\t\t{\"CSQC_InputEvent\",\t\t\"float(float eventtype, float xscan, float ychar, optional float zdev)\", DP_CS, D(\"Arg list is shorter than FTE+QSS\")},\n\t\t{\"CSQC_UpdateView\",\t\t\"void(float physicalwidth, float physicalheight, optional float notmenu)\", DP_CS, D(\"width+height are misinterpreted as physical sizes rather than the more useful virtual sizes.\")},\n\t\t{\"CSQC_ConsoleCommand\",\t\"float(string cmd)\", DP_CS},\n\t\t{\"pmove_org\",\t\t\t\"vector\", DP_CS},\n\t\t{\"pmove_vel\",\t\t\t\"vector\", DP_CS},\n\t\t{\"pmove_mins\",\t\t\t\"vector\", DP_CS},\n\t\t{\"pmove_maxs\",\t\t\t\"vector\", DP_CS},\n\t\t{\"input_timelength\",\t\"float\", DP_CS},\n\t\t{\"input_angles\",\t\t\"vector\", DP_CS},\n\t\t{\"input_movevalues\",\t\"vector\", DP_CS},\n\t\t{\"input_buttons\",\t\t\"float\", DP_CS},\n\t\t/*{\"movevar_gravity\",\t\t\t\t\"float\", DP_CS},\n\t\t{\"movevar_stopspeed\",\t\t\t\"float\", DP_CS},\n\t\t{\"movevar_maxspeed\",\t\t\t\"float\", DP_CS},\n\t\t{\"movevar_spectatormaxspeed\",\t\"float\", DP_CS},\n\t\t{\"movevar_accelerate\",\t\t\t\"float\", DP_CS},\n\t\t{\"movevar_airaccelerate\",\t\t\"float\", DP_CS},\n\t\t{\"movevar_wateraccelerate\",\t\t\"float\", DP_CS},\n\t\t{\"movevar_friction\",\t\t\t\"float\", DP_CS},\n\t\t{\"movevar_waterfriction\",\t\t\"float\", DP_CS},\n\t\t{\"movevar_entgravity\",\t\t\t\"float\", DP_CS},*/\n\n\t\t{\"end_sys_globals\",\t\t\"void\", QW|NQ|CS|MENU},\n\n\n\t\t{\"modelindex\",\t\t\t\".float\", QW|NQ|CS,\t\tD(\"This is the model precache index for the model that was set on the entity, instead of having to look up the model according to the .model field. Use setmodel to change it.\")},\n\t\t{\"absmin\",\t\t\t\t\".vector\", QW|NQ|CS,\tD(\"Set by the engine when the entity is relinked (by setorigin, setsize, or setmodel). This is in world coordinates.\")},\n\t\t{\"absmax\",\t\t\t\t\".vector\", QW|NQ|CS,\tD(\"Set by the engine when the entity is relinked (by setorigin, setsize, or setmodel). This is in world coordinates.\")},\n\t\t{\"ltime\",\t\t\t\t\".float\", QW|NQ|QSS_CS,\t\tD(\"On MOVETYPE_PUSH entities, this is used as an alternative to the 'time' global, and .nextthink is synced to this instead of time. This allows time to effectively freeze if the entity is blocked, ensuring the think happens when the entity reaches the target point instead of randomly.\")},\n\t\t{\"entnum\",\t\t\t\t\".float\", FTE_CS|DP_CS,\t\t\tD(\"The entity number as its known on the server.\")},\n\t\t{\"drawmask\",\t\t\t\".float\", FTE_CS|DP_CS,\t\t\tD(\"Acts as a filter in the addentities call.\")},\n\t\t{\"predraw\",\t\t\t\t\".float()\", FTE_CS|DP_CS,\t\t\tD(\"Called by addentities after the filter and before the entity is actually drawn. Do your interpolation and animation in here. Should return one of the PREDRAW_* constants.\")},\n\t\t{\"lastruntime\",\t\t\t\".float\", QW,\t\t\tD(\"This field used to be used to avoid running an entity multiple times in a single frame due to quakeworld's out-of-order thinks. It is no longer used by FTE due to precision issues, but may still be updated for compatibility reasons.\")},\n\t\t{\"movetype\",\t\t\t\".float\", QW|NQ|CS,\t\tD(\"Describes how the entity moves. One of the MOVETYPE_ constants.\")},\n\t\t{\"solid\",\t\t\t\t\".float\", QW|NQ|CS,\t\tD(\"Describes whether the entity is solid or not, and any special properties infered by that. Must be one of the SOLID_ constants\")},\n\t\t{\"origin\",\t\t\t\t\".vector\", QW|NQ|CS,\tD(\"The current location of the entity in world space. Inline bsp entities (ie: ones placed by a mapper) will typically have a value of '0 0 0' in their neutral pose, as the geometry is offset from that. It is the reference point of the entity rather than the center of its geometry, for non-bsp models, this is often not a significant distinction.\")},\n\t\t{\"oldorigin\",\t\t\t\".vector\", QW|NQ|CS,\tD(\"This is often used on players to reset the player back to where they were last frame if they somehow got stuck inside something due to fpu precision. Never change a player's oldorigin field to inside a solid, because that might cause them to become pemanently stuck.\")},\n\t\t{\"velocity\",\t\t\t\".vector\", QW|NQ|CS,\tD(\"The direction and speed that the entity is moving in world space.\")},\n\t\t{\"angles\",\t\t\t\t\".vector\", QW|NQ|CS,\tD(\"The eular angles the entity is facing in, in pitch, yaw, roll order. Due to a legacy bug, mdl/iqm/etc formats use +x=UP, bsp/spr/etc formats use +x=DOWN.\")},\n\t\t{\"avelocity\",\t\t\t\".vector\", QW|NQ|CS,\tD(\"The amount the entity's angles change by per second. Note that this is direct eular angles, and thus the angular change is non-linear and often just looks buggy if you're changing more than one angle at a time.\")},\n\t\t{\"pmove_flags\",\t\t\t\".float\", FTE_CS},\n\t\t{\"punchangle\",\t\t\t\".vector\", NQ|QSS_CS},\n\t\t{\"classname\",\t\t\t\".string\", QW|NQ|CS,\tD(\"Identifies the class/type of the entity. Useful for debugging, also used for loading, but its value is not otherwise significant to the engine, this leaves the mod free to set it to whatever it wants and randomly test strings for values in whatever inefficient way it chooses fit.\")},\n\t\t{\"renderflags\",\t\t\t\".float\", FTE_CS},\n\t\t{\"model\",\t\t\t\t\".string\", QW|NQ|CS,\tD(\"The model name that was set via setmodel, in theory. Often, this is cleared to null to prevent the engine from being seen by clients while not changing modelindex. This behaviour allows inline models to remain solid yet be invisible.\")},\n\t\t{\"frame\",\t\t\t\t\".float\", QW|NQ|CS,\t\tD(\"The current frame the entity is meant to be displayed in. In CSQC, note the lerpfrac and frame2 fields as well. if it specifies a framegroup, the framegroup will autoanimate in ssqc, but not in csqc.\")},\n\t\t{\"frame1time\",\t\t\t\".float\", FTE_CS,\t\t\tD(\"The absolute time into the animation/framegroup specified by .frame.\")},\n\t\t{\"frame2\",\t\t\t\t\".float\", FTE_CS,\t\t\tD(\"The alternative frame. Visible only when lerpfrac is set to 1.\")},\n\t\t{\"frame2time\",\t\t\t\".float\", FTE_CS,\t\t\tD(\"The absolute time into the animation/framegroup specified by .frame2.\")},\n\t\t{\"lerpfrac\",\t\t\t\".float\", FTE_CS,\t\t\tD(\"If 0, use frame1 only. If 1, use frame2 only. Mix them together for values between.\")},\n\t\t{\"skin\",\t\t\t\t\".float\", QW|NQ|CS,\t\tD(\"The skin index to use. on a bsp entity, setting this to 1 will switch to the 'activated' texture instead. A negative value will be understood as a replacement contents value, so setting it to CONTENTS_WATER will make a movable pool of water.\")},\n\t\t{\"effects\",\t\t\t\t\".float\", QW|NQ|CS,\t\tD(\"Lots of random flags that change random effects. See EF_* constants.\")},\n\t\t{\"mins\",\t\t\t\t\".vector\", QW|NQ|CS,\tD(\"The minimum extent of the model (ie: the bottom-left coordinate relative to the entity's origin). Change via setsize. May also be changed by setmodel.\")},\n\t\t{\"maxs\",\t\t\t\t\".vector\", QW|NQ|CS,\tD(\"like mins, but in the other direction.\")},\n\t\t{\"size\",\t\t\t\t\".vector\", QW|NQ|CS,\tD(\"maxs-mins. Updated when the entity is relinked (by setorigin, setsize, setmodel)\")},\n\t\t{\"touch\",\t\t\t\t\".void()\", QW|NQ|CS},\n\t\t{\"use\",\t\t\t\t\t\".void()\", QW|NQ|QSS_CS|DP_CS},\n\t\t{\"think\",\t\t\t\t\".void()\", QW|NQ|CS},\n\t\t{\"blocked\",\t\t\t\t\".void()\", QW|NQ|CS},\n\t\t{\"nextthink\",\t\t\t\".float\", QW|NQ|CS,\t\tD(\"The time at which the entity is next scheduled to fire its think event. For MOVETYPE_PUSH entities, this is relative to that entity's ltime field, for all other entities it is relative to the time gloal.\")},\n\n\t\t{\"groundentity\",\t\t\".entity\", QW|NQ|QSS_CS},\n\t\t{\"health\",\t\t\t\t\".float\", QW|NQ|QSS_CS},\n\t\t{\"frags\",\t\t\t\t\".float\", QW|NQ|QSS_CS},\n\t\t{\"weapon\",\t\t\t\t\".float\", QW|NQ|QSS_CS},\n\t\t{\"weaponmodel\",\t\t\t\".string\", QW|NQ|QSS_CS},\n\t\t{\"weaponframe\",\t\t\t\".float\", QW|NQ|QSS_CS},\n\t\t{\"currentammo\",\t\t\t\".float\", QW|NQ|QSS_CS},\n\t\t{\"ammo_shells\",\t\t\t\".float\", QW|NQ|QSS_CS},\n\t\t{\"ammo_nails\",\t\t\t\".float\", QW|NQ|QSS_CS},\n\t\t{\"ammo_rockets\",\t\t\".float\", QW|NQ|QSS_CS},\n\t\t{\"ammo_cells\",\t\t\t\".float\", QW|NQ|QSS_CS},\n\t\t{\"items\",\t\t\t\t\".float\", QW|NQ|QSS_CS},\n\t\t{\"takedamage\",\t\t\t\".float\", QW|NQ|QSS_CS},\n\n\t\t{\"chain\",\t\t\t\t\".entity\", QW|NQ|CS},\n\t\t{\"deadflag\",\t\t\t\".float\", QW|NQ|QSS_CS},\n\t\t{\"view_ofs\",\t\t\t\".vector\", QW|NQ|QSS_CS},\n\t\t{\"button0\",\t\t\t\t\".float\", QW|NQ|QSS_CS},\n\t\t{\"button1\",\t\t\t\t\".float\", QW|NQ|QSS_CS},\n\t\t{\"button2\",\t\t\t\t\".float\", QW|NQ|QSS_CS},\n\t\t{\"impulse\",\t\t\t\t\".float\", QW|NQ|QSS_CS},\n\t\t{\"fixangle\",\t\t\t\".float\", QW|NQ|QSS_CS,\t\"Forces the clientside view angles to change to the value of .angles (has some lag). If set to 1/TRUE, the server will guess whether to send a delta or an explicit angle. If 2, will always send a delta (due to lag between transmission and acknowledgement, this cannot be spammed reliably). If 3, will always send an explicit angle.\"},\n\t\t{\"v_angle\",\t\t\t\t\".vector\", QW|NQ|QSS_CS,\t\"The angles a player is viewing. +x is DOWN (pitch, yaw, roll)\"},\n\t\t{\"idealpitch\",\t\t\t\".float\", NQ|QSS_CS},\n\t\t{\"netname\",\t\t\t\t\".string\", QW|NQ|QSS_CS|DP_CS},\n\t\t{\"enemy\",\t\t\t\t\".entity\", QW|NQ|CS},\n\t\t{\"flags\",\t\t\t\t\".float\", QW|NQ|CS},\n\t\t{\"colormap\",\t\t\t\".float\", QW|NQ|CS},\n\t\t{\"team\",\t\t\t\t\".float\", QW|NQ|QSS_CS},\n\t\t{\"max_health\",\t\t\t\".float\", QW|NQ|QSS_CS},\n\t\t{\"teleport_time\",\t\t\".float\", QW|NQ|QSS_CS,\tD(\"While active, prevents the player from using the +back command, also blocks waterjumping.\")},\n\t\t{\"armortype\",\t\t\t\".float\", QW|NQ|QSS_CS},\n\t\t{\"armorvalue\",\t\t\t\".float\", QW|NQ|QSS_CS},\n\t\t{\"waterlevel\",\t\t\t\".float\", QW|NQ|QSS_CS},\n\t\t{\"watertype\",\t\t\t\".float\", QW|NQ|QSS_CS},\n\t\t{\"ideal_yaw\",\t\t\t\".float\", QW|NQ|QSS_CS},\n\t\t{\"yaw_speed\",\t\t\t\".float\", QW|NQ|QSS_CS},\n\t\t{\"aiment\",\t\t\t\t\".entity\", QW|NQ|QSS_CS},\n\t\t{\"goalentity\",\t\t\t\".entity\", QW|NQ|QSS_CS},\n\t\t{\"spawnflags\",\t\t\t\".float\", QW|NQ|QSS_CS},\n\t\t{\"target\",\t\t\t\t\".string\", QW|NQ|QSS_CS},\n\t\t{\"targetname\",\t\t\t\".string\", QW|NQ|QSS_CS},\n\t\t{\"dmg_take\",\t\t\t\".float\", QW|NQ|QSS_CS},\n\t\t{\"dmg_save\",\t\t\t\".float\", QW|NQ|QSS_CS},\n\t\t{\"dmg_inflictor\",\t\t\".entity\", QW|NQ|QSS_CS},\n\t\t{\"owner\",\t\t\t\t\".entity\", QW|NQ|CS},\n\t\t{\"movedir\",\t\t\t\t\".vector\", QW|NQ|QSS_CS},\n\t\t{\"message\",\t\t\t\t\".string\", QW|NQ|QSS_CS},\n\t\t{\"sounds\",\t\t\t\t\".float\", QW|NQ|QSS_CS},\n\t\t{\"noise\",\t\t\t\t\".string\", QW|NQ|QSS_CS},\n\t\t{\"noise1\",\t\t\t\t\".string\", QW|NQ|QSS_CS},\n\t\t{\"noise2\",\t\t\t\t\".string\", QW|NQ|QSS_CS},\n\t\t{\"noise3\",\t\t\t\t\".string\", QW|NQ|QSS_CS},\n\t\t{\"end_sys_fields\",\t\t\"void\", QW|NQ|CS|MENU},\n\n\t\t{\"time\",\t\t\t\t\"float\",\tMENU,\tD(\"The current local time. Increases while paused.\")},\n\t\t{\"input_sequence\",\t\t\"float\",\tQW|NQ|CS,\tD(\"This is the client-generated input sequence number. 0 for unsequenced movements.\")},\n\t\t{\"input_servertime\",\t\"float\",\tQW|NQ|CS,\tD(\"Server's timestamp of the client's interpolation state.\")},\n//\t\t{\"input_clienttime\",\t\"float\",\tQW|NQ|CS,\tD(\"This is the timestamp that player prediction is simulating.\")},\n\t\t{\"input_timelength\",\t\"float\",\tQW|NQ},\n\t\t{\"input_angles\",\t\t\"vector\",\tQW|NQ,\tD(\"+x=DOWN\")},\n\t\t{\"input_movevalues\",\t\"vector\",\tQW|NQ},\n\t\t{\"input_buttons\",\t\t\"float\",\tQW|NQ},\n\t\t{\"input_impulse\",\t\t\"float\",\tQW|NQ},\n\n\t\t{\"trace_endcontents\",\t\t\"int\", QW|NQ|CS},\n\t\t{\"trace_surfaceflags\",\t\t\"int\", QW|NQ|CS},\n//\t\t{\"trace_surfacename\",\t\t\"string\", QW|NQ|CS},\n\t\t{\"trace_brush_id\",\t\t\"int\", QW|NQ|CS},\n\t\t{\"trace_brush_faceid\",\t\"int\", QW|NQ|CS},\n\t\t{\"trace_surface_id\",\t\"int\", QW|NQ|CS, D(\"1-based. 0 if not known.\")},\n\t\t{\"trace_bone_id\",\t\t\"int\", QW|NQ|CS, D(\"1-based. 0 if not known. typically needs MOVE_HITMODEL.\")},\n\t\t{\"trace_triangle_id\",\t\"int\", QW|NQ|CS, D(\"1-based. 0 if not known.\")},\n\t\t{\"trace_networkentity\",\t\"float\", CS, D(\"Repots which ssqc entnum was hit when a csqc traceline impacts an ssqc-based brush entity.\")},\n\n\t\t{\"pmove_org\",\t\t\t\"vector\", CS, D(\"Reports the origin of the engineside player (after prediction). Does not work when the player is a csqc-owned entity.\")},\n\t\t{\"pmove_vel\",\t\t\t\"vector\", CS, D(\"Reports the velocity of the engineside player (after prediction). Does not work when the player is a csqc-owned entity.\")},\n\t\t{\"pmove_onground\",\t\t\"float\", CS, D(\"Reports the onground state of the engineside player (after prediction). Does not work when the player is a csqc-owned entity.\")},\n\n\t\t{\"global_gravitydir\",\t\"vector\", QW|NQ|CS,\tD(\"The direction gravity should act in if not otherwise specified per entity.\"), 0,\"'0 0 -1'\"},\n\t\t{\"serverid\",\t\t\t\"int\", QW|NQ|CS,\tD(\"The unique id of this server within the server cluster.\")},\n\t\t{\"message\",\t\t\t\t\".string\", CS,\t\tD(\"Allows the csqc to read the map description from the server.\")},\n\n\t\t{\"button3\",\t\t\t\t\".float\", QW|NQ},\n\t\t{\"button4\",\t\t\t\t\".float\", QW|NQ},\n\t\t{\"button5\",\t\t\t\t\".float\", QW|NQ},\n\t\t{\"button6\",\t\t\t\t\".float\", QW|NQ},\n\t\t{\"button7\",\t\t\t\t\".float\", QW|NQ},\n\t\t{\"button8\",\t\t\t\t\".float\", QW|NQ},\n\n\t\t//and for dp compat (these names are fucked, yes)\n\t\t{\"buttonuse\",\t\t\t\".float\", NQ,\t\tD(\"For DP compat only.\")},\n\t\t{\"buttonchat\",\t\t\t\".float\", NQ,\t\tD(\"For DP compat only.\")},\n\t\t{\"cursor_active\",\t\t\".float\", NQ,\t\tD(\"For DP compat only.\")},\n\t\t{\"button9\",\t\t\t\t\".float\", NQ,\t\tD(\"For DP compat only.\")},\n\t\t{\"button10\",\t\t\t\".float\", NQ,\t\tD(\"For DP compat only.\")},\n\t\t{\"button11\",\t\t\t\".float\", NQ,\t\tD(\"For DP compat only.\")},\n\t\t{\"button12\",\t\t\t\".float\", NQ,\t\tD(\"For DP compat only.\")},\n\t\t{\"button13\",\t\t\t\".float\", NQ,\t\tD(\"For DP compat only.\")},\n\n\t\t{\"button14\",\t\t\t\".float\", NQ,\t\tD(\"For DP compat only.\")},\n\t\t{\"button15\",\t\t\t\".float\", NQ,\t\tD(\"For DP compat only.\")},\n\t\t{\"button16\",\t\t\t\".float\", NQ,\t\tD(\"For DP compat only.\")},\n\n\t\t{\"cursor_screen\",\t\t\".vector\", NQ,\t\tD(\"For DP compat only.\")},\n\t\t{\"cursor_start\",\t\t\".vector\", NQ,\t\tD(\"For DP compat only.\")},\n\t\t{\"cursor_impact\",\t\t\".vector\", NQ,\t\tD(\"For DP compat only.\")},\n\t\t{\"cursor_entitynumber\",\t\".entity\", NQ,\t\tD(\"For DP compat only.\")},\n\n#undef comfieldfloatdep\n#undef comfieldintdep\n#undef comfieldvectordep\n#undef comfieldentitydep\n#undef comfieldstringdep\n#define comfieldfloatdep(name,desc,depreason) {#name, \"__deprecated(\\\"\"depreason\"\\\") .float\", FL, D(desc)},\n#define comfieldintdep(name,desc,depreason) {#name, \"__deprecated(\\\"\"depreason\"\\\") .int\", FL, D(desc)},\n#define comfieldvectordep(name,desc,depreason) {#name, \"__deprecated(\\\"\"depreason\"\\\") .vector\", FL, D(desc)},\n#define comfieldentitydep(name,desc,depreason) {#name, \"__deprecated(\\\"\"depreason\"\\\") .entity\", FL, D(desc)},\n#define comfieldstringdep(name,desc,depreason) {#name, \"__deprecated(\\\"\"depreason\"\\\") .string\", FL, D(desc)},\n//function types can bake their deprecation reason into their type string.\n\n#define comfieldfloat(name,desc) {#name, \".float\", FL, D(desc)},\n#define comfieldint(name,desc) {#name, \".int\", FL, D(desc)},\n#define comfieldvector(name,desc) {#name, \".vector\", FL, D(desc)},\n#define comfieldentity(name,desc) {#name, \".entity\", FL, D(desc)},\n#define comfieldstring(name,desc) {#name, \".string\", FL, D(desc)},\n#define comfieldfunction(name,typestr,desc) {#name, typestr, FL, D(desc)},\n#define FL QW|NQ\n\t\tcomqcfields\n#undef FL\n#define FL QW|NQ|CS\n\t\tcomextqcfields\n#undef FL\n#define FL QW|NQ\n\t\tsvextqcfields\n#undef FL\n#define FL CS\n\t\tcsqcextfields\n#undef FL\n#undef comfieldfloat\n#undef comfieldint\n#undef comfieldvector\n#undef comfieldentity\n#undef comfieldstring\n#undef comfieldfunction\n\n\t\t{\"URI_Get_Callback\",\t\t\"void(float reqid, float responsecode, string resourcebody, int resourcebytes)\",\tQW|NQ|CS|MENU, \"Called as an eventual result of the uri_get builtin.\"},\n\t\t{\"SpectatorConnect\",\t\t\"void()\", QW|NQ, \"Called when a spectator joins the game.\"},\n\t\t{\"SpectatorDisconnect\",\t\t\"void()\", QW|NQ, \"Called when a spectator disconnects from the game.\"},\n\t\t{\"SpectatorThink\",\t\t\t\"void()\", QW|NQ, \"Called each frame for each spectator.\"},\n\t\t{\"SV_ParseClientCommand\",\t\"void(string cmd)\", QW|NQ, \"Provides QC with a way to intercept 'cmd foo' commands from the client. Very handy. Self will be set to the sending client, while the 'cmd' argument can be tokenize()d and each element retrieved via argv(argno). Unrecognised cmds MUST be passed on to the clientcommand builtin.\"},\n\t\t{\"SV_ParseClusterEvent\",\t\"void(string dest, string from, string cmd, string info)\", QW|NQ, \"Part of cluster mode. Handles cross-node events that were sent via clusterevent, on behalf of the named client.\"},\n\t\t{\"SV_ParseConnectionlessPacket\", \"float(string sender, string body)\", QW|NQ, \"Provides QC with a way to communicate between servers, or with client server browsers. Sender is the sender's ip. Body is the body of the message. You'll need to add your own password/etc support as required. Self is not valid.\"},\n\t\t{\"SV_PausedTic\",\t\t\t\"void(float pauseduration)\", QW|NQ, \"For each frame that the server is paused, this function will be called to give the gamecode a chance to unpause the server again. the pauseduration argument says how long the server has been paused for (the time global is frozen and will not increment while paused). Self is not valid.\"},\n\t\t{\"SV_ShouldPause\",\t\t\t\"float(float newstatus)\", QW|NQ, \"Called to give the qc a change to block pause/unpause requests. Return false for the pause request to be ignored. newstatus is 1 if the user is trying to pause the game. For the duration of the call, self will be set to the player who tried to pause, or to world if it was triggered by a server-side event.\"},\n\t\t{\"SV_RunClientCommand\",\t\t\"void()\", QW|NQ, \"Called each time a player movement packet was received from a client. Self is set to the player entity which should be updated, while the input_* globals specify the various properties stored within the input packet. The contents of this function should be somewaht identical to the equivelent function in CSQC, or prediction misses will occur. If you're feeling lazy, you can simply call 'runstandardplayerphysics' after modifying the inputs.\"},\n\t\t{\"SV_AddDebugPolygons\",\t\t\"void()\", QW|NQ, \"Called each video frame. This is the only place where ssqc is allowed to call the R_BeginPolygon/R_PolygonVertex/R_EndPolygon builtins. This is exclusively for debugging, and will break in anything but single player as it will not be called if the engine is not running both a client and a server.\"},\n\t\t{\"SV_PlayerPhysics\",\t\t\"DEP_CSQC void()\", QW|NQ, \"Compatibility method to tweak player input that does not reliably work with prediction (prediction WILL break). Mods that care about prediction should use SV_RunClientCommand instead. If pr_no_playerphysics is set to 1, this function will never be called, which will either fix prediction or completely break player movement depending on whether the feature was even useful.\"},\n\t\t{\"SV_PerformSave\",\t\t\t\"void(float fd, float entcount, float playerslots)\", QW|NQ, \"Called by the engine as part of saved games. The QC is responsible for writing any data that will be required to restore the game. Save files are not limited to just text, you can use fwrite (and later fread) for binary data. Remember to close the passed file.\"},\n\t\t{\"SV_PerformLoad\",\t\t\t\"void(float fd, float entcount, float playerslots)\", QW|NQ, \"Called by the engine to restore a saved game that was saved via SV_PerformSave, the server will have already started the game to its inital state (for precaches+makestatic calls), so entities+globals will have their post-spawn values (you will likely want to remove most of them, you can use the two extra args as helpers for that). You can use respawn_edict to un-remove specific entities, with parseentitydata or putentityfieldstring(findentityfield(NAME), ent, value) to assign to fields by name strings. Don't expect bprints/etc to work - players are likely in a pending state. Remember to close the passed file.\"},\n\t\t{\"RestoreGame\",\t\t\t\t\"void()\", QW|NQ, \"Called for each reconnecting player as part of loading a saved game. Part of NEH_RESTOREGAME.\"},\n\t\t{\"EndFrame\",\t\t\t\t\"void()\", QW|NQ, \"Called after non-player entities have been run at the end of the physics frame. Player physics is performed out of order and can/will still occur between EndFrame and BeginFrame.\"},\n\t\t{\"SetTransferParms\",\t\t\"void()\", QW|NQ, \"Called as an alternative to SetChangeParms when a player is transferring to another server. Part of FTE_SV_CLUSTER.\"},\n\t\t{\"SV_CheckRejectConnection\",\"string(string addr, string uinfo, string features) \", QW|NQ, \"Called to give the mod a chance to ignore connection requests based upon client protocol support or other properties. Use infoget to read the uinfo and features arguments.\"},\n#ifdef HEXEN2\n\t\t{\"ClassChangeWeapon\",\t\t\"void()\", H2, \"Hexen2 support. Called when cl_playerclass changes. Self is set to the player who is changing class.\"},\n#endif\n\t\t/* //mvdsv compat\n\t\t{\"UserInfo_Changed\",\t\t\"//void()\", QW},\n\t\t{\"localinfoChanged\",\t\t\"//void()\", QW},\n\t\t{\"ChatMessage\",\t\t\t\t\"//void()\", QW},\n\t\t{\"UserCmd\",\t\t\t\t\t\"//void()\", QW},\n\t\t{\"ConsoleCmd\",\t\t\t\t\"//void()\", QW},\n\t\t*/\n\n\t\t{\"CSQC_Init\",\t\t\t\t\"void(float apilevel, string enginename, float engineversion)\", CS, \"Called at startup. enginename and engineversion are arbitary hints and can take any form. enginename should be consistant between revisions, but this cannot truely be relied upon.\"},\n\t\t{\"CSQC_WorldLoaded\",\t\t\"void()\", CS, \"Called after the server's model+sound precaches have been executed. Gives a chance for the qc to read the entity lump from the bsp (via getentitytoken).\"},\n\t\t{\"CSQC_Shutdown\",\t\t\t\"void()\", CS, \"Specifies that the csqc is going down. Save your persistant settings here.\"},\n\t\t{\"CSQC_UpdateView\",\t\t\t\"void(float vwidth, float vheight, float notmenu)\", CS, \"Called every single video frame. The CSQC is responsible for rendering the entire screen.\"},\n\t\t{\"CSQC_UpdateViewLoading\",\t\"void(float vwidth, float vheight, float notmenu)\", CS, \"Alternative to CSQC_UpdateView, called when the engine thinks there should be a loading screen. If present, will inhibit the engine's normal loading screen, deferring to qc to draw it.\"},\n\t\t{\"CSQC_DrawHud\",\t\t\t\"void(vector viewsize, float scoresshown)\", CS, \"Part of simple csqc, called after drawing the 3d view whenever CSQC_UpdateView is not defined.\"},\n\t\t{\"CSQC_DrawScores\",\t\t\t\"void(vector viewsize, float scoresshown)\", CS, \"Part of simple csqc, called after CSQC_DrawHud whenever CSQC_UpdateView is not defined, and when there are no menus/console active.\"},\n\t\t{\"CSQC_Parse_StuffCmd\",\t\t\"void(string msg)\", CS, \"Gives the CSQC a chance to intercept stuffcmds. Use the tokenize builtin to parse the message. Unrecognised commands would normally be localcmded, but its probably better to drop unrecognised stuffcmds completely.\"},\n\t\t{\"CSQC_Parse_CenterPrint\",\t\"float(string msg)\", CS, \"Gives the CSQC a chance to intercept centerprints. Return true if you wish the engine to otherwise ignore the centerprint.\"},\n\t\t{\"CSQC_Parse_Damage\",\t\t\"float(float save, float take, vector inflictororg)\", CS, \"Called as a result of player.dmg_save or player.dmg_take being set on the server.\\nReturn true to completely inhibit the engine's colour shift and damage rolls, allowing you to do your own thing.\\nYou can use punch_roll += (normalize(inflictororg-player.origin)*v_right)*(take+save)*autocvar_v_kickroll; as a modifier for the roll angle should the player be hit from the side, and slowly fade it away over time.\"},\n\t\t{\"CSQC_Parse_Print\",\t\t\"void(string printmsg, float printlvl)\", CS, \"Gives the CSQC a chance to intercept sprint/bprint builtin calls. CSQC should filter by the client's current msg setting and then pass the message on to the print command, or handle them itself.\"},\n\t\t{\"CSQC_Parse_Event\",\t\t\"void()\", CS, \"Called when the client receives an SVC_CGAMEPACKET. The csqc should read the data or call the error builtin if it does not recognise the message.\"},\n\t\t{\"CSQC_InputEvent\",\t\t\t\"float(float evtype, float scanx, float chary, float devid)\", CS, \"Called whenever a key is pressed, the mouse is moved, etc. evtype will be one of the IE_* constants. The other arguments vary depending on the evtype. Key presses are not guarenteed to have both scan and unichar values set at the same time.\"},\n\t\t{\"CSQC_Input_Frame\",\t\t\"__used void()\", CS, \"Called just before each time clientcommandframe is updated. You can edit the input_* globals in order to apply your own player inputs within csqc, which may allow you a convienient way to pass certain info to ssqc.\"},\n\t\t{\"CSQC_RendererRestarted\",\t\"void(string rendererdescription)\", CS, \"Called by the engine after the video was restarted. This serves to notify the CSQC that any render targets that it may have cached were purged, and will need to be regenerated.\"},\n\t\t{\"CSQC_GenerateMaterial\",\t\"string(string shadername)\", CS, \"Returns the material text to use for the named material, for automatically importing foreign materials.\"},\n\t\t{\"CSQC_ConsoleCommand\",\t\t\"float(string cmd)\", CS, \"Called if the user uses any console command registed via registercommand.\"},\n\t\t{\"CSQC_ConsoleLink\",\t\t\"float(string text, string info)\", CS, \"Called if the user clicks a ^[text\\\\infokey\\\\infovalue^] link. Use infoget to read/check each supported key. Return true if you wish the engine to not attempt to handle the link itself.\\nWARNING: link text can potentially come from other players, so be careful about what you allow to be changed.\"},\n\t\t{\"CSQC_Ent_Spawn\",\t\t\t\"void(float entnum)\", CS, \"Clumsily defined function for compat with DP. Should call spawn, set that ent's entnum field, and return the entity inside the 'self' global which will then be used for fllowing Ent_Updates. MUST NOT PARSE ANY NETWORK DATA (which makes it kinda useless).\"},\n\t\t{\"CSQC_Ent_Update\",\t\t\t\"void(float isnew)\", CS, \"Parses the data sent by ssqc's various SendEntity functions (must use the exact same reads as the ssqc used writes - to debug this rule more easily, you may wish to use sv_csqcdebug). 'self' provides context between frames, and self.entnum should normally report which ssqc entity . Be aware that interpolation will need to happen separately.\"},\n\t\t{\"CSQC_Ent_Remove\",\t\t\t\"void()\", CS},\n\t\t{\"CSQC_Event_Sound\",\t\t\"float(float entnum, float channel, string soundname, float vol, float attenuation, vector pos, float pitchmod, float flags\"/*\", float timeofs*/\")\", CS},\n//\t\t{\"CSQC_ServerSound\",\t\t\"//void()\", CS},\n//\t\t{\"CSQC_LoadResource\",\t\t\"float(string resname, string restype)\", CS, \"Called each time some resource is being loaded. CSQC can invoke various draw calls to provide a loading screen, until WorldLoaded is called.\"},\n\t\t{\"CSQC_Parse_TempEntity\",\t\"float()\", CS,\t\"Please don't use this. Use CSQC_Parse_Event and multicasts instead.\\nThe use of serverside protocol translation to handle QW vs NQ protocols mean that you're likely to end up reading slightly different data. Which is bad.\\nReturn true to say that you fully handled the tempentity. Return false to have the client attempt to rewind the network stream and parse the message itself.\"},\n\n\t\t{\"GameCommand\",\t\t\t\t\"void(string cmdtext)\", CS|MENU},\n\t\t{\"Cef_GeneratePage\",\t\t\"string(string uri, string method, string postdata, __in string requestheaders, __inout string responseheaders)\", QW|NQ|CS|MENU, \"Provides an entrypoint to generate pages for the CEF plugin from within QC. Headers are \\n-separated key/value pairs (use tokenizebyseparator).\"},\n\t\t{\"HTTP_GeneratePage\",\t\t\"string(string uri, string method, string postdata, __in string requestheaders, __inout string responseheaders)\", QW|NQ, \"Provides an entrypoint to generate pages for pages requested over http (sv_port_tcp+net_enable_http). Headers are \\r\\n-separated key/value pairs (use tokenizebyseparator). Return __NULL__ to let the engine handle it, an empty string for a 404, and any other text for a regular 200 response.\"},\n\n\t\t{\"init\",\t\t\t\t\t\"void(float prevprogs)\", QW|NQ|CS, \"Part of FTE_MULTIPROGS. Called as soon as a progs is loaded, called at a time when entities are not valid. This is the only time when it is safe to call addprogs without field assignment. As it is also called as part of addprogs, this also gives you a chance to hook functions in modules that are already loaded (via externget+externget).\"},\n\t\t{\"initents\",\t\t\t\t\"void()\", QW|NQ|CS, \"Part of FTE_MULTIPROGS. Called after fields have been finalized. This is the first point at which it is safe to call spawn(), and is called before any entity fields have been parsed. You can use this entrypoint to send notifications to other modules.\"},\n\n\t\t{\"m_init\",\t\t\t\t\t\"void()\", MENU},\n\t\t{\"m_shutdown\",\t\t\t\t\"void()\", MENU},\n\t\t{\"m_draw\",\t\t\t\t\t\"void(vector screensize)\", MENU, \"Provides the menuqc with a chance to draw. Will be called even if the menu does not have focus, so be sure to avoid that. COMPAT: screensize is not provided in DP.\"},\n\t\t{\"m_drawloading\",\t\t\t\"void(vector screensize, float opaque)\", MENU, \"Additional drawing function to draw loading screens. If opaque is set, then this function must ensure that the entire screen is overdrawn (even if just by a black drawfill).\"},\n\t\t{\"Menu_RendererRestarted\",\t\"void(string rendererdescription)\", MENU, \"Called by the engine after the video was restarted. This serves to notify the MenuQC that any render targets that it may have cached were purged, and will need to be regenerated.\"},\n\t\t{\"Menu_InputEvent\",\t\t\t\"float(float evtype, float scanx, float chary, float devid)\", MENU, \"If present, this is called instead of m_keydown and m_keyup\\nCalled whenever a key is pressed, the mouse is moved, etc. evtype will be one of the IE_* constants. The other arguments vary depending on the evtype. Key presses are not guarenteed to have both scan and unichar values set at the same time.\"},\n\t\t{\"m_keydown\",\t\t\t\t\"__deprecated(\\\"Use Menu_InputEvent\\\") void(float scan, float chr)\", MENU},\n\t\t{\"m_keyup\",\t\t\t\t\t\"__deprecated(\\\"Use Menu_InputEvent\\\") void(float scan, float chr)\", MENU},\n\t\t{\"m_toggle\",\t\t\t\t\"void(float wantmode)\", MENU},\n\t\t{\"m_consolecommand\",\t\t\"float(string cmd)\", MENU},\n\t\t{\"m_gethostcachecategory\",\t\"float(float idx)\", MENU},\n\n\t\t{\"parm17, parm18, parm19, parm20, parm21, parm22, parm23, parm24, parm25, parm26, parm27, parm28, parm29, parm30, parm31, parm32\", \"float\", QW|NQ, \"Additional spawn parms, following the same parmN theme.\"},\n\t\t{\"parm33, parm34, parm35, parm36, parm37, parm38, parm39, parm40, parm41, parm42, parm43, parm44, parm45, parm46, parm47, parm48\", \"float\", QW|NQ},\n\t\t{\"parm49, parm50, parm51, parm52, parm53, parm54, parm55, parm56, parm57, parm58, parm59, parm60, parm61, parm62, parm63, parm64\", \"float\", QW|NQ},\n\t\t{\"parm_string\",\t\t\t\t\"string\", QW|NQ, \"Like the regular parmN globals, but preserves string contents.\"},\n\t\t{\"startspot\",\t\t\t\t\"string\", QW|NQ, \"Receives the value of the second argument to changelevel from the previous map.\"},\n\t\t{\"dimension_send\",\t\t\t\"var float\", QW|NQ, \"Used by multicast functionality. Multicasts (and related builtins that multicast internally) will only be sent to players where (player.dimension_see & dimension_send) is non-zero.\"},\n\t\t{\"dimension_default\",\t\t\"//var float\", QW|NQ, \"Default dimension bitmask\", 255},\n\t\t{\"__fullspawndata\",\t\t\t\"__unused var string\", QW|NQ|H2, \"Set by the engine before calls to spawn functions, and is most easily parsed with the tokenize builtin. This allows you to handle halflife's multiple-fields-with-the-same-name (or target-specific fields).\"},\n\t\t{\"physics_mode\",\t\t\t\"__used var float\", QW|NQ|CS, \"0: original csqc - physics are not run\\n1: DP-compat. Thinks occur, but not true movetypes.\\n2: movetypes occur just as they do in ssqc.\", 2},\n\t\t{\"gamespeed\",\t\t\t\t\"float\", CS, \"Set by the engine, this is the value of the sv_gamespeed cvar\"},\n\t\t{\"numclientseats\",\t\t\t\"float\", CS, \"This is the number of splitscreen clients currently running on this client.\"},\n\t\t{\"drawfontscale\",\t\t\t\"var vector\", CS|MENU, \"Specifies a scaler for all text rendering. There are other ways to implement this.\", 0, \"'1 1 0'\"},\n\t\t{\"drawfont\",\t\t\t\t\"float\", CS|MENU, \"Allows you to choose exactly which font is to be used to draw text. Fonts can be registered/allocated with the loadfont builtin.\"},\n\t\t{\"FONT_DEFAULT\",\t\t\t\"const float\", CS|MENU, NULL, 0},\n\n#define globalfloat(need,name)\t\t\t{#name, \"float\", QW|NQ, \"ssqc global\"},\n#define globalentity(need,name)\t\t\t{#name, \"entity\", QW|NQ, \"ssqc global\"},\n#define globalint(need,name)\t\t\t{#name, \"int\", QW|NQ, \"ssqc global\"},\n#define globaluint(need,name)\t\t\t{#name, \"__uint\", QW|NQ, \"ssqc global\"},\n#define globaluint64(need,name)\t\t\t{#name, \"__uint64\", QW|NQ, \"ssqc global\"},\n#define globalstring(need,name)\t\t\t{#name, \"string\", QW|NQ, \"ssqc global\"},\n#define globalvec(need,name)\t\t\t{#name, \"vector\", QW|NQ, \"ssqc global\"},\n#define globalfunc(need,name,typestr)\t{#name, typestr, QW|NQ, \"ssqc global\"},\n\t\tssqcglobals\n#undef globalfloat\n#undef globalentity\n#undef globalint\n#undef globaluint\n#undef globaluint64\n#undef globalstring\n#undef globalvec\n#undef globalfunc\n\n#define globalfloatdep(name,depreason)\t{#name, \"DEP(\\\"\"depreason\"\\\") float\", CS},\n#define globalfunction(name,typestr)\t{#name, typestr, CS},\n#define globalfloat(name)\t\t\t\t{#name, \"float\", CS},\n#define globalentity(name)\t\t\t\t{#name, \"entity\", CS},\n#define globalvector(name)\t\t\t\t{#name, \"vector\", CS},\n#define globalstring(name)\t\t\t\t{#name, \"string\", CS},\n#define globalint(name)\t\t\t\t\t{#name, \"int\", CS},\n#define globaluint(name)\t\t\t\t{#name, \"__uint\", CS},\n#define globaluint64(name)\t\t\t\t{#name, \"__uint64\", CS},\n\t\tcsqcglobals\n#undef globalfunction\n#undef globalfloat\n#undef globalentity\n#undef globalvector\n#undef globalstring\n#undef globalint\n#undef globaluint\n#undef globaluint64\n#undef globalfloatdep\n\n\t\t{\"TRUE\",\t\t\t\t\t\"const float\", ALL, NULL, 1},\n\t\t{\"FALSE\",\t\t\t\t\t\"const float\", ALL, \"File not found...\", 0},\n\t\t{\"M_PI\",\t\t\t\t\t\"const float\", ALL, \"Mathematica Pi constant.\", M_PI},\n\n\t\t{\"MOVETYPE_NONE\",\t\t\t\"const float\", QW|NQ|CS, NULL, MOVETYPE_NONE},\n\t\t{\"MOVETYPE_WALK\",\t\t\t\"const float\", QW|NQ|CS, NULL, MOVETYPE_WALK},\n\t\t{\"MOVETYPE_STEP\",\t\t\t\"const float\", QW|NQ|CS, NULL, MOVETYPE_STEP},\n\t\t{\"MOVETYPE_FLY\",\t\t\t\"const float\", QW|NQ|CS, NULL, MOVETYPE_FLY},\n\t\t{\"MOVETYPE_TOSS\",\t\t\t\"const float\", QW|NQ|CS, NULL, MOVETYPE_TOSS},\n\t\t{\"MOVETYPE_PUSH\",\t\t\t\"const float\", QW|NQ|CS, NULL, MOVETYPE_PUSH},\n\t\t{\"MOVETYPE_NOCLIP\",\t\t\t\"const float\", QW|NQ|CS, NULL, MOVETYPE_NOCLIP},\n\t\t{\"MOVETYPE_FLYMISSILE\",\t\t\"const float\", QW|NQ|CS, NULL, MOVETYPE_FLYMISSILE},\n\t\t{\"MOVETYPE_BOUNCE\",\t\t\t\"const float\", QW|NQ|CS, NULL, MOVETYPE_BOUNCE},\n\t\t{\"MOVETYPE_BOUNCEMISSILE\",\t\"const float\", QW|NQ|CS, NULL, MOVETYPE_BOUNCEMISSILE},\n\t\t{\"MOVETYPE_FOLLOW\",\t\t\t\"const float\", QW|NQ|CS, NULL, MOVETYPE_FOLLOW},\n\t\t{\"MOVETYPE_PUSHPULL\",\t\t\"const float\", H2,\t\t D(\"Identical to MOVETYPE_STEP. QC may treat the entity differently (typically with movechains).\"), MOVETYPE_H2PUSHPULL},\n\t\t{\"MOVETYPE_SWIM\",\t\t\t\"const float\", H2,\t\t D(\"Equivelent to MOVETYPE_STEP, but additionally walkmove/movetogoal will not allow a movetype_swim entity to move out of water.\"), MOVETYPE_H2SWIM},\n\t\t{\"MOVETYPE_6DOF\",\t\t\t\"const float\", QW|NQ|CS, D(\"A glorified MOVETYPE_FLY. Players using this movetype will get some flightsim-like physics, with fully independant rotations (order-dependant transforms).\"), MOVETYPE_6DOF},\n\t\t{\"MOVETYPE_WALLWALK\",\t\t\"const float\", QW|NQ|CS, D(\"Players using this movetype will be able to orient themselves to walls, and then run up them.\"), MOVETYPE_WALLWALK},\n\t\t{\"MOVETYPE_PHYSICS\",\t\t\"const float\", QW|NQ|CS, D(\"Enable the use of ODE physics upon this entity.\"), MOVETYPE_PHYSICS},\n//\t\t{\"MOVETYPE_FLY_WORLDONLY\",\t\"const float\", QW|NQ|CS, D(\"A cross between noclip and fly. Basically, this prevents the player/spectator from being able to move into the void, which avoids pvs issues that are common with caulk brushes on q3bsp. ONLY the world model will be solid, all doors/etc will be non-solid.\"), MOVETYPE_FLY_WORLDONLY},\n\n\t\t{\"SOLID_NOT\",\t\t\t\t\"const float\", QW|NQ|CS, NULL, SOLID_NOT},\n\t\t{\"SOLID_TRIGGER\",\t\t\t\"const float\", QW|NQ|CS, NULL, SOLID_TRIGGER},\n\t\t{\"SOLID_BBOX\",\t\t\t\t\"const float\", QW|NQ|CS, NULL, SOLID_BBOX},\n\t\t{\"SOLID_SLIDEBOX\",\t\t\t\"const float\", QW|NQ|CS, NULL, SOLID_SLIDEBOX},\n\t\t{\"SOLID_BSP\",\t\t\t\t\"const float\", QW|NQ|CS, D(\"Does not collide against other SOLID_BSP entities. Normally paired with MOVETYPE_PUSH.\"), SOLID_BSP},\n\t\t{\"SOLID_CORPSE\",\t\t\t\"const float\", QW|NQ|CS, D(\"Non-solid to SOLID_SLIDEBOX or other SOLID_CORPSE entities. For hitscan weapons to hit corpses, change the player's .hitcontentsmaski value to include CONTENTBIT_CORPSE, perform the traceline, then revert the player's .solid value.\"), SOLID_CORPSE},\n\t\t{\"SOLID_LADDER\",\t\t\t\"__deprecated(\\\"Obsoleted by .skin=CONTENTS_LADDER\\\") const float\", QW|NQ|CS, D(\"Obsolete and may be removed at some point. Use skin=CONTENT_LADDER and solid_bsp or solid_trigger instead.\"), SOLID_LADDER},\n\t\t{\"SOLID_PORTAL\",\t\t\t\"const float\", QW|NQ|CS, D(\"CSG subtraction volume combined with entity transformations on impact.\"), SOLID_PORTAL},\n\t\t{\"SOLID_BSPTRIGGER\",\t\t\"const float\", QW|NQ|CS, D(\"For complex-shaped trigger volumes, instead of being a pure aabb.\"), SOLID_BSPTRIGGER},\n\t\t{\"SOLID_PHYSICS_BOX\",\t\t\"const float\", QW|NQ|CS, NULL, SOLID_PHYSICS_BOX},\n\t\t{\"SOLID_PHYSICS_SPHERE\",\t\"const float\", QW|NQ|CS, NULL, SOLID_PHYSICS_SPHERE},\n\t\t{\"SOLID_PHYSICS_CAPSULE\",\t\"const float\", QW|NQ|CS, NULL, SOLID_PHYSICS_CAPSULE},\n\t\t{\"SOLID_PHYSICS_TRIMESH\",\t\"const float\", QW|NQ|CS, NULL, SOLID_PHYSICS_TRIMESH},\n\t\t{\"SOLID_PHYSICS_CYLINDER\",\t\"const float\", QW|NQ|CS, NULL, SOLID_PHYSICS_CYLINDER},\n\n\t\t{\"GEOMTYPE_NONE\",\t\t\t\"const float\", QW|NQ|CS, NULL, GEOMTYPE_NONE},\n\t\t{\"GEOMTYPE_SOLID\",\t\t\t\"const float\", QW|NQ|CS, NULL, GEOMTYPE_SOLID},\n\t\t{\"GEOMTYPE_BOX\",\t\t\t\"const float\", QW|NQ|CS, NULL, GEOMTYPE_BOX},\n\t\t{\"GEOMTYPE_SPHERE\",\t\t\t\"const float\", QW|NQ|CS, NULL, GEOMTYPE_SPHERE},\n\t\t{\"GEOMTYPE_CAPSULE\",\t\t\"const float\", QW|NQ|CS, NULL, GEOMTYPE_CAPSULE},\n\t\t{\"GEOMTYPE_TRIMESH\",\t\t\"const float\", QW|NQ|CS, NULL, GEOMTYPE_TRIMESH},\n\t\t{\"GEOMTYPE_CYLINDER\",\t\t\"const float\", QW|NQ|CS, NULL, GEOMTYPE_CYLINDER},\n\t\t{\"GEOMTYPE_CAPSULE_X\",\t\t\"const float\", QW|NQ|CS, NULL, GEOMTYPE_CAPSULE_X},\n\t\t{\"GEOMTYPE_CAPSULE_Y\",\t\t\"const float\", QW|NQ|CS, NULL, GEOMTYPE_CAPSULE_Y},\n\t\t{\"GEOMTYPE_CAPSULE_Z\",\t\t\"const float\", QW|NQ|CS, NULL, GEOMTYPE_CAPSULE_Z},\n\t\t{\"GEOMTYPE_CYLINDER_X\",\t\t\"const float\", QW|NQ|CS, NULL, GEOMTYPE_CYLINDER_X},\n\t\t{\"GEOMTYPE_CYLINDER_Y\",\t\t\"const float\", QW|NQ|CS, NULL, GEOMTYPE_CYLINDER_Y},\n\t\t{\"GEOMTYPE_CYLINDER_Z\",\t\t\"const float\", QW|NQ|CS, NULL, GEOMTYPE_CYLINDER_Z},\n\n\t\t{\"JOINTTYPE_FIXED\",\t\t\t\"const float\", QW|NQ|CS, NULL, JOINTTYPE_FIXED},\n\t\t{\"JOINTTYPE_POINT\",\t\t\t\"const float\", QW|NQ|CS, NULL, JOINTTYPE_POINT},\n\t\t{\"JOINTTYPE_HINGE\",\t\t\t\"const float\", QW|NQ|CS, NULL, JOINTTYPE_HINGE},\n\t\t{\"JOINTTYPE_SLIDER\",\t\t\"const float\", QW|NQ|CS, NULL, JOINTTYPE_SLIDER},\n\t\t{\"JOINTTYPE_UNIVERSAL\",\t\t\"const float\", QW|NQ|CS, NULL, JOINTTYPE_UNIVERSAL},\n\t\t{\"JOINTTYPE_HINGE2\",\t\t\"const float\", QW|NQ|CS, NULL, JOINTTYPE_HINGE2},\n\n\t\t{\"GE_MAXENTS\",\t\t\"const float\", CS, \"Valid for getentity, ignores the entity argument. Returns the maximum number of entities which may be valid, to avoid having to poll 65k when only 100 are used.\", GE_MAXENTS},\n\t\t{\"GE_ACTIVE\",\t\t\"const float\", CS, \"Valid for getentity. Returns whether this entity is known to the client or not.\", GE_ACTIVE},\n\t\t{\"GE_ORIGIN\",\t\t\"const float\", CS, \"Valid for getentity. Returns the interpolated .origin.\", GE_ORIGIN},\n\t\t{\"GE_FORWARD\",\t\t\"const float\", CS, \"Valid for getentity. Returns the interpolated forward vector.\", GE_FORWARD},\n\t\t{\"GE_RIGHT\",\t\t\"const float\", CS, \"Valid for getentity. Returns the entity's right vector.\", GE_RIGHT},\n\t\t{\"GE_UP\",\t\t\t\"const float\", CS, \"Valid for getentity. Returns the entity's up vector.\", GE_UP},\n\t\t{\"GE_SCALE\",\t\t\"const float\", CS, \"Valid for getentity. Returns the entity .scale.\", GE_SCALE},\n\t\t{\"GE_ORIGINANDVECTORS\",\"const float\", CS, \"Valid for getentity. Returns interpolated .origin, but also sets v_forward, v_right, and v_up accordingly. Use vectoangles(v_forward,v_up) to determine the angles.\", GE_ORIGINANDVECTORS},\n\t\t{\"GE_ALPHA\",\t\t\"const float\", CS, \"Valid for getentity. Returns the entity alpha.\", GE_ALPHA},\n\t\t{\"GE_COLORMOD\",\t\t\"const float\", CS, \"Valid for getentity. Returns the colormod vector.\", GE_COLORMOD},\n\t\t{\"GE_PANTSCOLOR\",\t\"const float\", CS, \"Valid for getentity. Returns the entity's lower color (from .colormap), as a palette range value.\", GE_PANTSCOLOR},\n\t\t{\"GE_SHIRTCOLOR\",\t\"const float\", CS, \"Valid for getentity. Returns the entity's lower color (from .colormap), as a palette range value.\", GE_SHIRTCOLOR},\n\t\t{\"GE_SKIN\",\t\t\t\"const float\", CS, \"Valid for getentity. Returns the entity's .skin index.\", GE_SKIN},\n\t\t{\"GE_MINS\",\t\t\t\"const float\", CS, \"Valid for getentity. Guesses the entity's .min vector.\", GE_MINS},\n\t\t{\"GE_MAXS\",\t\t\t\"const float\", CS, \"Valid for getentity. Guesses the entity's .max vector.\", GE_MAXS},\n\t\t{\"GE_ABSMIN\",\t\t\"const float\", CS, \"Valid for getentity. Guesses the entity's .absmin vector.\", GE_ABSMIN},\n\t\t{\"GE_ABSMAX\",\t\t\"const float\", CS, \"Valid for getentity. Guesses the entity's .absmax vector.\", GE_ABSMAX},\n//\t\t{\"GE_LIGHT\",\t\t\"const float\", CS, NULL, GE_LIGHT},\n\n\t\t{\"GE_MODELINDEX\",\t\"const float\", CS, D(\"Valid for getentity. Guesses the entity's .modelindex float.\"), GE_MODELINDEX},\n\t\t{\"GE_MODELINDEX2\",\t\"const float\", CS, D(\"Valid for getentity. Guesses the entity's .vw_index float.\"), GE_MODELINDEX2},\n\t\t{\"GE_EFFECTS\",\t\t\"const float\", CS, D(\"Valid for getentity. Guesses the entity's .effects float.\"), GE_EFFECTS},\n\t\t{\"GE_FRAME\",\t\t\"const float\", CS, D(\"Valid for getentity. Guesses the entity's .frame float.\"), GE_FRAME},\n\t\t{\"GE_ANGLES\",\t\t\"const float\", CS, D(\"Valid for getentity. Guesses the entity's .angles vector.\"), GE_ANGLES},\n\t\t{\"GE_FATNESS\",\t\t\"const float\", CS, D(\"Valid for getentity. Guesses the entity's .fatness float.\"), GE_FATNESS},\n\t\t{\"GE_DRAWFLAGS\",\t\"const float\", CS, D(\"Valid for getentity. Guesses the entity's .drawflags float.\"), GE_DRAWFLAGS},\n\t\t{\"GE_ABSLIGHT\",\t\t\"const float\", CS, D(\"Valid for getentity. Guesses the entity's .abslight float.\"), GE_ABSLIGHT},\n\t\t{\"GE_GLOWMOD\",\t\t\"const float\", CS, D(\"Valid for getentity. Guesses the entity's .glowmod vector.\"), GE_GLOWMOD},\n\t\t{\"GE_GLOWSIZE\",\t\t\"const float\", CS, D(\"Valid for getentity. Guesses the entity's .glowsize float.\"), GE_GLOWSIZE},\n\t\t{\"GE_GLOWCOLOUR\",\t\"const float\", CS, D(\"Valid for getentity. Guesses the entity's .glowcolor float.\"), GE_GLOWCOLOUR},\n\t\t{\"GE_RTSTYLE\",\t\t\"const float\", CS, D(\"Valid for getentity. Guesses the entity's .style float.\"), GE_RTSTYLE},\n\t\t{\"GE_RTPFLAGS\",\t\t\"const float\", CS, D(\"Valid for getentity. Guesses the entity's .pflags float.\"), GE_RTPFLAGS},\n\t\t{\"GE_RTCOLOUR\",\t\t\"const float\", CS, D(\"Valid for getentity. Guesses the entity's .color vector.\"), GE_RTCOLOUR},\n\t\t{\"GE_RTRADIUS\",\t\t\"const float\", CS, D(\"Valid for getentity. Guesses the entity's .light_lev float.\"), GE_RTRADIUS},\n\t\t{\"GE_TAGENTITY\",\t\"const float\", CS, D(\"Valid for getentity. Guesses the entity's .tag_entity float.\"), GE_TAGENTITY},\n\t\t{\"GE_TAGINDEX\",\t\t\"const float\", CS, D(\"Valid for getentity. Guesses the entity's .tag_index float.\"), GE_TAGINDEX},\n\t\t{\"GE_GRAVITYDIR\",\t\"const float\", CS, D(\"Valid for getentity. Guesses the entity's .gravitydir vector.\"), GE_GRAVITYDIR},\n\t\t{\"GE_TRAILEFFECTNUM\",\"const float\",CS, D(\"Valid for getentity. Guesses the entity's .traileffectnum float.\"), GE_TRAILEFFECTNUM},\n\n\t\t{\"DAMAGE_NO\",\t\t\"const float\", QW|NQ, NULL, DAMAGE_NO},\n\t\t{\"DAMAGE_YES\",\t\t\"const float\", QW|NQ, NULL, DAMAGE_YES},\n\t\t{\"DAMAGE_AIM\",\t\t\"const float\", QW|NQ, NULL, DAMAGE_AIM},\n\n\t\t{\"CONTENT_EMPTY\",\t\"const float\", QW|NQ|CS, NULL, Q1CONTENTS_EMPTY},\n\t\t{\"CONTENT_SOLID\",\t\"const float\", QW|NQ|CS, NULL, Q1CONTENTS_SOLID},\n\t\t{\"CONTENT_WATER\",\t\"const float\", QW|NQ|CS, NULL, Q1CONTENTS_WATER},\n\t\t{\"CONTENT_SLIME\",\t\"const float\", QW|NQ|CS, NULL, Q1CONTENTS_SLIME},\n\t\t{\"CONTENT_LAVA\",\t\"const float\", QW|NQ|CS, NULL, Q1CONTENTS_LAVA},\n\t\t{\"CONTENT_SKY\",\t\t\"const float\", QW|NQ|CS, NULL, Q1CONTENTS_SKY},\n\t\t{\"CONTENT_LADDER\",\t\"const float\", QW|NQ|CS, D(\"If this value is assigned to a solid_bsp's .skin field, the entity will become a ladder volume.\"), Q1CONTENTS_LADDER},\n\n\t\t{\"CONTENTBIT_NONE\",\t\t\t\"const int\", QW|NQ|CS, NULL, 0,STRINGIFY(FTECONTENTS_EMPTY)\"i\"},\n\t\t{\"CONTENTBIT_SOLID\",\t\t\"const int\", QW|NQ|CS, NULL, 0,STRINGIFY(FTECONTENTS_SOLID)\"i\"},\n\t\t{\"CONTENTBIT_LAVA\",\t\t\t\"const int\", QW|NQ|CS, NULL, 0,STRINGIFY(FTECONTENTS_LAVA)\"i\"},\n\t\t{\"CONTENTBIT_SLIME\",\t\t\"const int\", QW|NQ|CS, NULL, 0,STRINGIFY(FTECONTENTS_SLIME)\"i\"},\n\t\t{\"CONTENTBIT_WATER\",\t\t\"const int\", QW|NQ|CS, NULL, 0,STRINGIFY(FTECONTENTS_WATER)\"i\"},\n\t\t{\"CONTENTBIT_FTELADDER\",\t\"const int\", QW|NQ|CS, D(\"Content bit used for .skin=CONTENT_LADDER entities.\"), 0,STRINGIFY(FTECONTENTS_LADDER)\"i\"},\n\t\t{\"CONTENTBIT_PLAYERCLIP\",\t\"const int\", QW|NQ|CS, NULL, 0,STRINGIFY(FTECONTENTS_PLAYERCLIP)\"i\"},\n\t\t{\"CONTENTBIT_MONSTERCLIP\",\t\"const int\", QW|NQ|CS, NULL, 0,STRINGIFY(FTECONTENTS_MONSTERCLIP)\"i\"},\n\t\t{\"CONTENTBIT_BODY\",\t\t\t\"const int\", QW|NQ|CS, D(\"Content bit that indicates collisions against SOLID_BBOX/SOLID_SLIDEBOX entities.\"), 0,STRINGIFY(FTECONTENTS_BODY)\"i\"},\n\t\t{\"CONTENTBIT_CORPSE\",\t\t\"const int\", QW|NQ|CS, D(\"Content bit that indicates collisions against SOLID_CORPSE entities.\"), 0,STRINGIFY(FTECONTENTS_CORPSE)\"i\"},\n\t\t{\"CONTENTBIT_Q2LADDER\",\t\t\"const int\", QW|NQ|CS, D(\"Content bit specific to q2bsp (conflicts with q3bsp contents so use with caution).\"), 0,STRINGIFY(Q2CONTENTS_LADDER)\"i\"},\n\t\t{\"CONTENTBIT_SKY\",\t\t\t\"const int\", QW|NQ|CS, D(\"Content bit somewhat specific to q1bsp (aliases to NODROP in q3bsp), but you should probably check surfaceflags&SURF_SKY as well for q2+q3bsp too.\"), 0,STRINGIFY(FTECONTENTS_SKY)\"i\"},\n\t\t{\"CONTENTBITS_POINTSOLID\",\t\"const int\", QW|NQ|CS, D(\"Bits that traceline would normally consider solid\"), 0,\"CONTENTBIT_SOLID|\"STRINGIFY(Q2CONTENTS_WINDOW)\"i|CONTENTBIT_BODY\"},\n\t\t{\"CONTENTBITS_BOXSOLID\",\t\"const int\", QW|NQ|CS, D(\"Bits that tracebox would normally consider solid\"), 0,\"CONTENTBIT_SOLID|\"STRINGIFY(Q2CONTENTS_WINDOW)\"i|CONTENTBIT_BODY|CONTENTBIT_PLAYERCLIP\"},\n\t\t{\"CONTENTBITS_FLUID\",\t\t\"const int\", QW|NQ|CS, NULL, 0,\"CONTENTBIT_WATER|CONTENTBIT_SLIME|CONTENTBIT_LAVA|CONTENTBIT_SKY\"},\n\n\t\t{\"SPA_POSITION\",\t\t\t\"const float\", QW|NQ|CS, D(\"These SPA_* constants are to specify which attribute is returned by the getsurfacepointattribute builtin\"), 0},\n\t\t{\"SPA_S_AXIS\",\t\t\t\t\"const float\", QW|NQ|CS, NULL, 1},\n\t\t{\"SPA_T_AXIS\",\t\t\t\t\"const float\", QW|NQ|CS, NULL, 2},\n\t\t{\"SPA_R_AXIS\",\t\t\t\t\"const float\", QW|NQ|CS, D(\"aka: SPA_NORMAL\"), 3},\n\t\t{\"SPA_TEXCOORDS0\",\t\t\t\"const float\", QW|NQ|CS, NULL, 4},\n\t\t{\"SPA_LIGHTMAP0_TEXCOORDS\",\t\"const float\", QW|NQ|CS, NULL, 5},\n\t\t{\"SPA_LIGHTMAP0_COLOR\",\t\t\"const float\", QW|NQ|CS, NULL, 6},\n\n\t\t{\"CHAN_AUTO\",\t\t\"const float\", QW|NQ|CS, D(\"The automatic channel, play as many sounds on this channel as you want, and they'll all play, however the other channels will replace each other.\"), CHAN_AUTO},\n\t\t{\"CHAN_WEAPON\",\t\t\"const float\", QW|NQ|CS, NULL, CHAN_WEAPON},\n\t\t{\"CHAN_VOICE\",\t\t\"const float\", QW|NQ|CS, NULL, CHAN_VOICE},\n\t\t{\"CHAN_ITEM\",\t\t\"const float\", QW|NQ|CS, NULL, CHAN_ITEM},\n\t\t{\"CHAN_BODY\",\t\t\"const float\", QW|NQ|CS, NULL, CHAN_BODY},\n\t\t{\"CHANF_RELIABLE\",\t\"const float\", QW,\t\t D(\"Only valid if the flags argument is not specified. The sound will be sent reliably, which is important if it is intended to replace looping sounds on doors etc.\"), 8},\n\n\t\t{\"SOUNDFLAG_RELIABLE\",\t\t\"const float\",\tQW|NQ,\t\tD(\"The sound will be sent reliably, and without regard to phs.\"), CF_SV_RELIABLE},\n\t\t{\"SOUNDFLAG_ABSVOLUME\",\t\t\"const float\",\tCS,\t\t\tD(\"The sample's volume is not scaled by the volume cvar. Use with caution\"), CF_CL_ABSVOLUME},\n\t\t{\"SOUNDFLAG_FORCELOOP\",\t\t\"const float\",\tQW|NQ|CS,\tD(\"The sound will restart once it reaches the end of the sample.\"), CF_FORCELOOP},\n\t\t{\"SOUNDFLAG_NOSPACIALISE\",\t\"const float\",\tQW|NQ|CS,\tD(\"The different audio channels are played at the same volume regardless of which way the player is facing, without needing to use 0 attenuation.\"), CF_NOSPACIALISE},\n\t\t{\"SOUNDFLAG_NOREVERB\",\t\t\"const float\",\tQW|NQ|CS,\tD(\"Disables the use of underwater/reverb effects on this sound effect.\"), CF_NOREVERB},\n\t\t{\"SOUNDFLAG_FOLLOW\",\t\t\"const float\",\tQW|NQ|CS,\tD(\"The sound's origin will updated to follow the emitting entity.\"), CF_FOLLOW},\n\t\t{\"SOUNDFLAG_NOREPLACE\",\t\t\"const float\",\tQW|NQ|CS,\tD(\"Sounds started with this flag will be ignored when there's already a sound playing on that same ent-channel. Such sounds can be safely 're-' started every single frame without harming anything. Tends not to make sense with CHAN_AUTO.\"), CF_NOREPLACE},\n\t\t{\"SOUNDFLAG_UNICAST\",\t\t\"const float\",\tQW|NQ,\t\tD(\"The sound will be sent only by the player specified by msg_entity. Spectators and related splitscreen players will also hear the sound.\"), CF_SV_UNICAST},\n\t\t{\"SOUNDFLAG_SENDVELOCITY\",\t\"const float\",\tQW|NQ,\t\tD(\"The entity's current velocity will be sent to the client, only useful if doppler is enabled.\"), CF_SV_SENDVELOCITY},\n\t\t{\"SOUNDFLAG_INACTIVE\",\t\t\"const float\",\tCS,\t\t\tD(\"The sound will ignore the value of the snd_inactive cvar.\"), CF_CLI_INACTIVE},\n\n\t\t{\"ATTN_NONE\",\t\t\"const float\", QW|NQ|CS, D(\"Sounds with this attenuation can be heard throughout the map\"), ATTN_NONE},\n\t\t{\"ATTN_NORM\",\t\t\"const float\", QW|NQ|CS, D(\"Standard attenuation\"), ATTN_NORM},\n\t\t{\"ATTN_IDLE\",\t\t\"const float\", QW|NQ|CS, D(\"Extra attenuation so that sounds don't travel too far.\"), 2},\t//including these for completeness, despite them being defined by the gamecode rather than the engine api.\n\t\t{\"ATTN_STATIC\",\t\t\"const float\", QW|NQ|CS, D(\"Even more attenuation to avoid torches drowing out everything else throughout the map.\"), 3},\n\n\t\t//not putting other svcs here, qc shouldn't otherwise need to generate svcs directly.\n\t\t{\"SVC_CGAMEPACKET\",\t\t\"const float\", QW|NQ, D(\"Direct ssqc->csqc message. Must only be multicast. The data triggers a CSQC_Parse_Event call in the csqc for the csqc to read the contents. The server *may* insert length information for clients connected via proxies which are not able to cope with custom csqc payloads. This should only ever be used in conjunction with the MSG_MULTICAST destination.\"), svcfte_cgamepacket},\n\n\t\t{\"TE_SPIKE\",\t\t\t\"const float\", QW|NQ|CS, NULL, TE_SPIKE},\n\t\t{\"TE_SUPERSPIKE\",\t\t\"const float\", QW|NQ|CS, NULL, TE_SUPERSPIKE},\n\t\t{\"TE_GUNSHOT\",\t\t\t\"const float\", QW|FTE_CS, NULL, TEQW_QWGUNSHOT},\n\t\t{\"TE_EXPLOSION\",\t\t\"const float\", QW|FTE_CS, NULL, TEQW_QWEXPLOSION},\n\t\t{\"TE_GUNSHOT\",\t\t\t\"const float\", NQ|QSS_CS|DP_CS, NULL, TENQ_NQGUNSHOT},\n\t\t{\"TE_EXPLOSION\",\t\t\"const float\", NQ|QSS_CS|DP_CS, NULL, TENQ_NQEXPLOSION},\n\t\t{\"TE_TAREXPLOSION\",\t\t\"const float\", QW|NQ|CS, NULL, TE_TAREXPLOSION},\n\t\t{\"TE_LIGHTNING1\",\t\t\"const float\", QW|NQ|CS, NULL, TE_LIGHTNING1},\n\t\t{\"TE_LIGHTNING2\",\t\t\"const float\", QW|NQ|CS, NULL, TE_LIGHTNING2},\n\t\t{\"TE_WIZSPIKE\",\t\t\t\"const float\", QW|NQ|CS, NULL, TE_WIZSPIKE},\n\t\t{\"TE_KNIGHTSPIKE\",\t\t\"const float\", QW|NQ|CS, NULL, TE_KNIGHTSPIKE},\n\t\t{\"TE_LIGHTNING3\",\t\t\"const float\", QW|NQ|CS, NULL, TE_LIGHTNING3},\n\t\t{\"TE_LAVASPLASH\",\t\t\"const float\", QW|NQ|CS, NULL, TE_LAVASPLASH},\n\t\t{\"TE_TELEPORT\",\t\t\t\"const float\", QW|NQ|CS, NULL, TE_TELEPORT},\n\t\t{\"TE_BLOOD\",\t\t\t\"const float\", QW|FTE_CS, NULL, TEQW_QWBLOOD},\n\t\t{\"TE_EXPLOSION2\",\t\t\"const float\", NQ|QSS_CS|DP_CS, NULL, TENQ_EXPLOSION2},\n\t\t{\"TE_LIGHTNINGBLOOD\",\t\"const float\", QW|FTE_CS, NULL, TEQW_LIGHTNINGBLOOD},\n\t\t{\"TE_BEAM\",\t\t\t\t\"const float\", NQ|QSS_CS|DP_CS, NULL, TENQ_BEAM},\n\n#ifdef HAVE_LEGACY\n\t\t{\"MSG_BROADCAST\",\t\t\"const float\", NQ, D(\"The byte(s) will be unreliably sent to all players. MSG_ constants are valid arguments to the Write* builtin family.\"), MSG_BROADCAST},\n\t\t{\"MSG_ONE\",\t\t\t\t\"const float\", NQ, D(\"The byte(s) will be reliably sent to the player specified in the msg_entity global. WARNING: in quakeworld servers without network preparsing enabled, this can result in illegible server messages (due to individual reliable messages being split between multiple backbuffers/packets). NQ has larger reliable buffers which avoids this issue, but still kicks the client.\"), MSG_ONE},\n\t\t{\"MSG_ALL\",\t\t\t\t\"const float\", NQ, D(\"The byte(s) will be reliably sent to all players.\"), MSG_ALL},\n\t\t{\"MSG_BROADCAST\",\t\t\"__deprecated(\\\"Use MSG_MULTICAST+multicast(MULTICAST_*)\\\") const float\", QW, D(\"The byte(s) will be unreliably sent to all players. MSG_ constants are valid arguments to the Write* builtin family.\"), MSG_BROADCAST},\n\t\t{\"MSG_ONE\",\t\t\t\t\"__deprecated(\\\"Use MSG_MULTICAST+multicast(MULTICAST_ONE_R)\\\") const float\", QW, D(\"The byte(s) will be reliably sent to the player specified in the msg_entity global. WARNING: in quakeworld servers without network preparsing enabled, this can result in illegible server messages (due to individual reliable messages being split between multiple backbuffers/packets). NQ has larger reliable buffers which avoids this issue, but still kicks the client.\"), MSG_ONE},\n\t\t{\"MSG_ALL\",\t\t\t\t\"__deprecated(\\\"Use MSG_MULTICAST+multicast(MULTICAST_ALL)\\\") const float\", QW, D(\"The byte(s) will be reliably sent to all players.\"), MSG_ALL},\n\t\t{\"MSG_INIT\",\t\t\t\"const float\", QW|NQ, D(\"The byte(s) will be written into the signon buffer. Clients will see these messages when they connect later. This buffer is only flushed on map changes, so spamming it _WILL_ result in overflows.\"), MSG_INIT},\n#endif\n\t\t{\"MSG_MULTICAST\",\t\t\"const float\", QW|NQ, D(\"The byte(s) will be written into the multicast buffer for more selective sending. Messages sent this way will never be split across packets, and using this for csqc-only messages will not break protocol translation.\"), MSG_MULTICAST},\n\t\t{\"MSG_ENTITY\",\t\t\t\"const float\", QW|NQ, D(\"The byte(s) will be written into the entity buffer. This is a special value used only inside 'SendEntity' functions.\"), MSG_CSQC},\n\n\t\t{\"MULTICAST_ALL\",\t\t\"const float\", QW|NQ, D(\"The multicast message is unreliably sent to all players. MULTICAST_ constants are valid arguments for the multicast builtin, which ignores the specified origin when given this constant.\"), MULTICAST_ALL},\n\t\t{\"MULTICAST_PHS\",\t\t\"const float\", QW|NQ, D(\"The multicast message is unreliably sent to only players that can potentially hear the specified origin. Its quite loose.\"), MULTICAST_PHS},\n\t\t{\"MULTICAST_PVS\",\t\t\"const float\", QW|NQ, D(\"The multicast message is unreliably sent to only players that can potentially see the specified origin.\"), MULTICAST_PVS},\n\t\t{\"MULTICAST_ONE\",\t\t\"const float\", QW|NQ, D(\"The multicast message is unreliably sent to the player (AND ALL TRACKING SPECTATORS) specified in the msg_entity global. The specified origin is ignored.\"), MULTICAST_ONE_SPECS},\n\t\t{\"MULTICAST_ONE_NOSPECS\",\"const float\", QW|NQ, D(\"The multicast message is unreliably sent to the player specified in the msg_entity global. The specified origin is ignored.\"), MULTICAST_ONE_NOSPECS},\n\t\t{\"MULTICAST_ALL_R\",\t\t\"const float\", QW|NQ, D(\"The multicast message is reliably sent to all players. The specified origin is ignored.\"), MULTICAST_ALL_R},\n\t\t{\"MULTICAST_PHS_R\",\t\t\"const float\", QW|NQ, D(\"The multicast message is reliably sent to only players that can potentially hear the specified origin. Players might still not receive it if they are out of range.\"), MULTICAST_PHS_R},\n\t\t{\"MULTICAST_PVS_R\",\t\t\"const float\", QW|NQ, D(\"The multicast message is reliably sent to only players that can potentially see the specified origin. Players might still not receive it if they cannot see the event.\"), MULTICAST_PVS_R},\n\t\t{\"MULTICAST_ONE_R\",\t\t\"const float\", QW|NQ, D(\"The multicast message is reliably sent to the player (AND ALL TRACKING SPECTATORS) specified in the msg_entity global. The specified origin is ignored\"), MULTICAST_ONE_R_SPECS},\n\t\t{\"MULTICAST_ONE_R_NOSPECS\",\"const float\", QW|NQ, D(\"The multicast message is reliably sent to the player specified in the msg_entity global. The specified origin is ignored\"), MULTICAST_ONE_R_NOSPECS},\n\n\t\t{\"PRINT_LOW\",\t\t\t\"const float\", QW, NULL, PRINT_LOW},\n\t\t{\"PRINT_MEDIUM\",\t\t\"const float\", QW, NULL, PRINT_MEDIUM},\n\t\t{\"PRINT_HIGH\",\t\t\t\"const float\", QW, NULL, PRINT_HIGH},\n\t\t{\"PRINT_CHAT\",\t\t\t\"const float\", QW, NULL, PRINT_CHAT},\n\n\t\t{\"PVSF_NORMALPVS\",\t\t\"const float\", QW|NQ, D(\"Filter first by PVS, then filter this entity using tracelines if sv_cullentities is enabled.\"), PVSF_NORMALPVS},\n\t\t{\"PVSF_NOTRACECHECK\",\t\"const float\", QW|NQ, D(\"Filter strictly by PVS.\"), PVSF_NOTRACECHECK},\n\t\t{\"PVSF_USEPHS\",\t\t\t\"const float\", QW|NQ, D(\"Send if we're close enough to be able to hear this entity.\"), PVSF_USEPHS},\n\t\t{\"PVSF_IGNOREPVS\",\t\t\"const float\", QW|NQ, D(\"Ignores pvs. This entity is visible whereever you are on the map. Updates will be sent regardless of pvs or phs\"), PVSF_IGNOREPVS},\n\t\t{\"PVSF_NOREMOVE\",\t\t\"const float\", QW|NQ, D(\"Once visible to a client, this entity will remain visible. This can be useful for csqc and corpses. While this flag is set, no CSQC_Remove events will be sent for the entity, but this does NOT mean that it will still receive further updates while outside of the pvs.\"), PVSF_NOREMOVE},\n\n\t\t//most of these are there for documentation rather than anything else.\n\t\t{\"INFOKEY_P_IP\",\t\t\"const string\", QW|NQ, D(\"The apparent ip address of the client. This may be a proxy's ip address.\"), 0, \"\\\"ip\\\"\"},\n\t\t{\"INFOKEY_P_REALIP\",\t\"const string\", QW|NQ, D(\"If sv_getrealip is set, this gives the ip as determine using that algorithm.\"), 0, \"\\\"realip\\\"\"},\n\t\t{\"INFOKEY_P_CSQCACTIVE\",\"const string\", QW|NQ, D(\"Client has csqc enabled. CSQC ents etc will be sent to this player.\"), 0, \"\\\"csqcactive\\\"\"},\n\t\t{\"INFOKEY_P_SVPING\",\t\"const string\", QW|NQ, NULL, 0, \"\\\"svping\\\"\"},\n\t\t{\"INFOKEY_P_GUID\",\t\t\"const string\", QW|NQ, D(\"Some hash string which should be reasonably unique to this player's quake installation.\"), 0, \"\\\"guid\\\"\"},\n\t\t{\"INFOKEY_P_CERT_SHA1\",\t\"const string\", QW|NQ, D(\"Obtains the client's (d)tls certificate's fingerprint.\"), 0, \"\\\"*cert_sha1\\\"\"},\n\t\t{\"INFOKEY_P_CERT_DN\",\t\"const string\", QW|NQ, D(\"Obtains the client's (d)tls certificate's Distinguished Name string.\"), 0, \"\\\"*cert_dn\\\"\"},\n\t\t{\"INFOKEY_P_CHALLENGE\",\t\"const string\", QW|NQ, NULL, 0, \"\\\"challenge\\\"\"},\n\t\t{\"INFOKEY_P_USERID\",\t\"const string\", QW|NQ, NULL, 0, \"\\\"*userid\\\"\"},\n\t\t{\"INFOKEY_P_DOWNLOADPCT\",\"const string\",QW|NQ, D(\"The client's download percentage for the current file. Additional files are not known.\"), 0, \"\\\"download\\\"\"},\n\t\t{\"INFOKEY_P_TRUSTLEVEL\",\"const string\", QW|NQ, NULL, 0, \"\\\"trustlevel\\\"\"},\n\t\t{\"INFOKEY_P_PROTOCOL\",\t\"const string\", QW|NQ, D(\"The network protocol the client is using to connect to the server.\"), 0, \"\\\"protocol\\\"\"},\n\t\t{\"INFOKEY_P_VIP\",\t\t\"const string\", QW|NQ, D(\"1 if the player has the VIP 'penalty'.\"), 0, \"\\\"*VIP\\\"\"},\n\t\t{\"INFOKEY_P_ISMUTED\",\t\"const string\", QW|NQ, D(\"1 if the player has the 'mute' penalty and is not allowed to use the say/say_team commands.\"), 0, \"\\\"*ismuted\\\"\"},\n\t\t{\"INFOKEY_P_ISDEAF\",\t\"const string\", QW|NQ, D(\"1 if the player has the 'deaf' penalty and cannot see other people's say/say_team commands.\"), 0, \"\\\"*isdeaf\\\"\"},\n\t\t{\"INFOKEY_P_ISCRIPPLED\",\"const string\", QW|NQ, D(\"1 if the player has the cripple penalty, and their movement values are ignored (.movement is locked to 0).\"), 0, \"\\\"*ismuted\\\"\"},\n\t\t{\"INFOKEY_P_ISCUFFED\",\t\"const string\", QW|NQ, D(\"1 if the player has the cuff penalty, and is unable to attack or use impulses(.button0 and .impulse fields are locked to 0).\"), 0, \"\\\"*ismuted\\\"\"},\n\t\t{\"INFOKEY_P_ISLAGGED\",\t\"const string\", QW|NQ, D(\"1 if the player has the fakelag penalty and has an extra 200ms of lag.\"), 0, \"\\\"*ismuted\\\"\"},\n\t\t{\"INFOKEY_P_PING\",\t\t\"const string\", CS|QW|NQ, D(\"The player's ping time, in milliseconds.\"), 0, \"\\\"ping\\\"\"},\n\t\t{\"INFOKEY_P_NAME\",\t\t\"const string\", CS|QW|NQ, D(\"The player's name.\"), 0, \"\\\"name\\\"\"},\n\t\t{\"INFOKEY_P_SPECTATOR\",\t\"const string\", CS|QW|NQ, D(\"Whether the player is a spectator or not.\"), 0, \"\\\"*spectator\\\"\"},\n\t\t{\"INFOKEY_P_TOPCOLOR\",\t\"const string\", CS|QW|NQ, D(\"The player's upper/shirt colour (palette index).\"), 0, \"\\\"topcolor\\\"\"},\n\t\t{\"INFOKEY_P_BOTTOMCOLOR\",\"const string\", CS|QW|NQ, D(\"The player's lower/pants/trouser colour (palette index).\"), 0, \"\\\"bottomcolor\\\"\"},\n\t\t{\"INFOKEY_P_TOPCOLOR_RGB\",\"const string\", CS, D(\"The player's upper/shirt colour as an rgb value in a format usable with stov.\"), 0, \"\\\"topcolor_rgb\\\"\"},\n\t\t{\"INFOKEY_P_BOTTOMCOLOR_RGB\",\"const string\", CS, D(\"The player's lower/pants/trouser colour as an rgb value in a format usable with stov.\"), 0, \"\\\"bottomcolor_rgb\\\"\"},\n\t\t{\"INFOKEY_P_MUTED\",\t\t\"const string\", CS, D(\"0: we can see the result of the player's say/say_team commands.   1: we see no say/say_team messages from this player. Use the ignore command to toggle this value.\"), 0, \"\\\"ignored\\\"\"},\n\t\t{\"INFOKEY_P_VOIP_MUTED\",\"const string\", CS, D(\"0: we can hear this player when they speak (assuming voip is generally enabled). 1: we ignore everything this player says. Use cl_voip_mute to change the values.\"), 0, \"\\\"vignored\\\"\"},\n\t\t{\"INFOKEY_P_ENTERTIME\",\t\"const string\", CS, D(\"Reads the timestamp at which the player entered the game, in terms of csqc's time global.\"), 0, \"\\\"entertime\\\"\"},\n\t\t{\"INFOKEY_P_FRAGS\",\t\t\"const string\", CS, D(\"Reads a player's frag count.\"), 0, \"\\\"frags\\\"\"},\n\t\t{\"INFOKEY_P_PACKETLOSS\",\"const string\", CS, D(\"Reads a player's packetloss, as a percentage.\"), 0, \"\\\"pl\\\"\"},\n\t\t{\"INFOKEY_P_VOIPSPEAKING\",\"const string\", CS, D(\"Boolean value that says whether the given player is currently sending voice information.\"), 0, \"\\\"voipspeaking\\\"\"},\n\t\t{\"INFOKEY_P_VOIPLOUDNESS\",\"const string\", CS, D(\"Only valid for the local player. Gives a value between 0 and 1 to indicate to the user how loud their mic is.\"), 0, \"\\\"voiploudness\\\"\"},\n\n\t\t{\"SERVERKEY_IP\",\t\t\"const string\",\tCS,D(\"The address of the server we connected to.\"), 0, \"\\\"ip\\\"\"},\n\t\t{\"SERVERKEY_SERVERNAME\",\"const string\",\tCS,D(\"The hostname that was last passed to the connect command.\"), 0, \"\\\"servername\\\"\"},\n\t\t{\"SERVERKEY_CONSTATE\",\t\"const string\",\tCS,D(\"The current connection state. Will be set to one of: disconnected (menu-only mode), active (gamestate received and loaded), connecting(connecting, downloading, or precaching content, aka: loading screen).\"), 0, \"\\\"constate\\\"\"},\n\t\t{\"SERVERKEY_TRANSFERRING\",\"const string\",CS,D(\"Set to the hostname of the server that we are attempting to connect or transfer to.\"), 0, \"\\\"transferring\\\"\"},\n\t\t{\"SERVERKEY_LOADSTATE\",\t\"const string\", CS, D(\"loadstage, loading image name, current step, max steps\\nStages are: 1=connecting, 2=serverside, 3=clientside\\nKey will be empty if we are not loading.\"), 0, \"\\\"loadstate\\\"\"},\n\t\t{\"SERVERKEY_PAUSESTATE\",\"const string\", CS, D(\"1 if the server claimed to be paused. 0 otherwise\"), 0, \"\\\"pausestate\\\"\"},\n\t\t{\"SERVERKEY_DLSTATE\",\t\"const string\", CS,\tD(\"The progress of any current downloads. Empty string if no download is active, otherwise a tokenizable string containing this info:\\nfiles-remaining, total-size, unknown-sizes-flag, file-localname, file-remotename, file-percent, file-rate, file-received-bytes, file-total-bytes\\nIf the current file info is omitted, then we are waiting for a download to start.\"), 0, \"\\\"dlstate\\\"\"},\n\t\t{\"SERVERKEY_PROTOCOL\",\t\"const string\", CS,\tD(\"The protocol we are connected to the server with.\"), 0, \"\\\"protocol\\\"\"},\n\t\t{\"SERVERKEY_MAXPLAYERS\",\"const string\", CS,\tD(\"The number of player/spectator slots allocated on the server.\"), 0, \"\\\"maxplayers\\\"\"},\n\n\t\t{\"STUFFCMD_IGNOREINDEMO\",\"const float\",\tQW|NQ,\tD(\"This stuffcmd will NOT be written to mvds/qtv.\"), STUFFCMD_IGNOREINDEMO},\n\t\t{\"STUFFCMD_DEMOONLY\",\t\"const float\",\tQW|NQ,\tD(\"This stuffcmd will ONLY be written into mvds/qtv streams.\"), STUFFCMD_DEMOONLY},\n\t\t{\"STUFFCMD_BROADCAST\",\t\"const float\",\tQW|NQ,\tD(\"The stuffcmd will be broadcast server-wide (according to the mvd filters).\"), STUFFCMD_BROADCAST},\n\t\t{\"STUFFCMD_UNRELIABLE\",\t\"const float\",\tQW|NQ,\tD(\"The stuffcmd might not arrive. It might also get there faster than ones sent over the reliable channel.\"), STUFFCMD_UNRELIABLE},\n\n/*\t\t{\"SOUND_RELIABLE\",\t\t\"const float\",\tQW|NQ,\tD(\"The sound will be sent reliably, and without regard to phs.\"), CF_RELIABLE},\n\t\t{\"SOUND_FORCELOOP\",\t\t\"const float\",\tQW|NQ|CS,D(\"The sound will restart once it reaches the end of the sample.\"), CF_FORCELOOP},\n\t\t{\"SOUND_NOSPACIALISE\",\t\"const float\",\tQW|NQ|CS,D(\"The different audio channels are played at the same volume regardless of which way the player is facing, without needing to use 0 attenuation.\"), CF_NOSPACIALISE},\n\t\t{\"SOUND_ABSVOLUME\",\t\t\"const float\",\tQW|NQ|CS,D(\"The sample's volume is not scaled by the volume cvar. Use with caution\"), CF_ABSVOLUME},\n*/\n\t\t// edict.flags\n\t\t{\"FL_FLY\",\t\t\t\t\"const float\", QW|NQ|CS, NULL, FL_FLY},\n\t\t{\"FL_SWIM\",\t\t\t\t\"const float\", QW|NQ|CS, NULL, FL_SWIM},\n\t\t{\"FL_CLIENT\",\t\t\t\"const float\", QW|NQ|CS, NULL, FL_CLIENT},\n\t\t{\"FL_INWATER\",\t\t\t\"const float\", QW|NQ|CS, NULL, FL_INWATER},\n\t\t{\"FL_MONSTER\",\t\t\t\"const float\", QW|NQ|CS, NULL, FL_MONSTER},\n\t\t{\"FL_GODMODE\",\t\t\t\"const float\", QW|NQ, NULL, FL_GODMODE},\n\t\t{\"FL_NOTARGET\",\t\t\t\"const float\", QW|NQ, NULL, FL_NOTARGET},\n\t\t{\"FL_ITEM\",\t\t\t\t\"const float\", QW|NQ|CS, NULL, FL_ITEM},\n\t\t{\"FL_ONGROUND\",\t\t\t\"const float\", QW|NQ|CS, NULL, FL_ONGROUND},\n\t\t{\"FL_PARTIALGROUND\",\t\"const float\", QW|NQ|CS, NULL, FL_PARTIALGROUND},\n\t\t{\"FL_WATERJUMP\",\t\t\"const float\", QW|NQ|CS, NULL, FL_WATERJUMP},\n\t\t{\"FL_JUMPRELEASED\",\t\t\"const float\",    NQ,    NULL, FL_JUMPRELEASED},\n\t\t{\"FL_FINDABLE_NONSOLID\",\"const float\", QW|NQ|CS, D(\"Allows this entity to be found with findradius\"), FL_FINDABLE_NONSOLID},\n\t\t{\"FL_MOVECHAIN_ANGLE\",\t\"const float\", H2, NULL, FL_MOVECHAIN_ANGLE},\n\t\t{\"FL_LAGGEDMOVE\",\t\t\"const float\", QW|NQ, D(\"Enables anti-lag on rockets etc.\"), FLQW_LAGGEDMOVE},\n\t\t{\"FL_CLASS_DEPENDENT\",\t\"const float\", H2, NULL, FL_CLASS_DEPENDENT},\n\n\t\t{\"MOVE_NORMAL\",\t\t\t\"const float\", QW|NQ|CS, NULL, MOVE_NORMAL},\n\t\t{\"MOVE_NOMONSTERS\",\t\t\"const float\", QW|NQ|CS, D(\"The trace will ignore all non-solid_bsp entities.\"), MOVE_NOMONSTERS},\n\t\t{\"MOVE_MISSILE\",\t\t\"const float\", QW|NQ|CS, D(\"The trace will use a bbox size of +/- 15 against entities with FL_MONSTER set.\"), MOVE_MISSILE},\n#ifdef HAVE_LEGACY\n\t\t{\"MOVE_WORLDONLY\",\t\t\"FTEDEP(\\\"use MOVE_OTHERONLY\\\") const float\", QW|NQ|CS, D(\"The trace will ignore everything but the worldmodel. This is useful for to prevent the q3bsp pvs+culling issues that come with spectator modes leaving the world .\"), MOVE_WORLDONLY},\n#endif\n\t\t{\"MOVE_HITMODEL\",\t\t\"const float\", QW|NQ|CS, D(\"Traces will impact the actual mesh of the model instead of merely their bounding box. Should generally only be used for tracelines. Note that this flag is unreliable as an object can animate through projectiles. The bounding box MUST be set to completely encompass the entity or those extra areas will be non-solid (leaving a hole for things to go through).\"), MOVE_HITMODEL},\n\t\t{\"MOVE_TRIGGERS\",\t\t\"const float\", QW|NQ|CS, D(\"This trace type will impact only triggers. It will ignore non-solid entities.\"), MOVE_TRIGGERS},\n\t\t{\"MOVE_EVERYTHING\",\t\t\"const float\", QW|NQ|CS, D(\"This type of trace will hit solids and triggers alike. Even non-solid entities.\"), MOVE_EVERYTHING},\n\t\t{\"MOVE_LAGGED\",\t\t\t\"const float\", QW|NQ, D(\"Will use antilag based upon the player's latency. Traces will be performed against old positions for entities instead of their current origin.\"), MOVE_LAGGED},\n\t\t{\"MOVE_ENTCHAIN\",\t\t\"const float\", QW|NQ|CS, D(\"Returns a list of entities impacted via the trace_ent.chain field\"), MOVE_ENTCHAIN},\n\t\t{\"MOVE_OTHERONLY\",\t\t\"const float\", QW|NQ|CS, D(\"Traces that use this trace type will collide against *only* the entity specified via the 'other' global, and will ignore all owner/solid_not/dimension etc rules, they will still adhere to contents and bsp/bbox rules though.\"), MOVE_OTHERONLY},\n\n\t\t{\"CVAR_TYPEFLAG_EXISTS\",\t\t\"const float\", ALL,\t\t D(\"Cvar name actually exists.\"), CVAR_TYPEFLAG_EXISTS},\n\t\t{\"CVAR_TYPEFLAG_SAVED\",\t\t\t\"const float\", ALL,\t\t D(\"Cvar is flaged for archival (might need cfg_save to actually save).\"), CVAR_TYPEFLAG_SAVED},\n\t\t{\"CVAR_TYPEFLAG_PRIVATE\",\t\t\"const float\", ALL,\t\t D(\"QC is not allowed to read.\"), CVAR_TYPEFLAG_PRIVATE},\n\t\t{\"CVAR_TYPEFLAG_ENGINE\",\t\t\"const float\", ALL,\t\t D(\"Cvar was created by the engine itself (not user/mod created).\"), CVAR_TYPEFLAG_ENGINE},\n\t\t{\"CVAR_TYPEFLAG_HASDESCRIPTION\",\"const float\", ALL,\t\t D(\"cvar_description will return something (hopefully) useful.\"), CVAR_TYPEFLAG_HASDESCRIPTION},\n\t\t{\"CVAR_TYPEFLAG_READONLY\",\t\t\"const float\", ALL,\t\t D(\"cvar may not be changed by qc.\"), CVAR_TYPEFLAG_READONLY},\n\n\t\t{\"RESTYPE_MODEL\",\t\t\"const float\", ALL,\t\t D(\"RESTYPE_* constants are used as arguments with the resourcestatus builtin.\"), RESTYPE_MODEL},\n\t\t{\"RESTYPE_SOUND\",\t\t\"const float\", ALL,\t\t D(\"precache_sound\"), RESTYPE_SOUND},\n\t\t{\"RESTYPE_PARTICLE\",\t\"const float\", ALL,\t\t D(\"particleeffectnum\"), RESTYPE_PARTICLE},\n\t\t{\"RESTYPE_PIC\",\t\t\t\"const float\", CS|MENU,\t D(\"precache_pic. Status results are an amalgomation of the textures used by the named shader.\"), RESTYPE_SHADER},\n\t\t{\"RESTYPE_SKIN\",\t\t\"const float\", CS|MENU,\t D(\"setcustomskin\"), RESTYPE_SKIN},\n\t\t{\"RESTYPE_TEXTURE\",\t\t\"const float\", CS|MENU,\t D(\"Individual textures within shaders. These are not directly usable, but may be named as part of a skin file, or a shader.\"), RESTYPE_TEXTURE},\n\t\t{\"RESSTATE_NOTKNOWN\",\t\"const float\", ALL,\t\t D(\"RESSTATE_* constants are return values from the resourcestatus builtin. The engine doesn't know about the resource if it is in this state. This means you will need to precache it. Attempting to use it anyway may result in warnings, errors, or silently succeed, depending on engine version and resource type.\"), RESSTATE_NOTKNOWN},\n\t\t{\"RESSTATE_NOTLOADED\",\t\"const float\", ALL,\t\t D(\"The resource was precached, but has been flushed and there has not been an attempt to reload it. If you use the resource normally, chances are it'll be loaded but at the cost of a stall.\"), RESSTATE_NOTLOADED},\n\t\t{\"RESSTATE_LOADING\",\t\"const float\", ALL,\t\t D(\"Resources in this this state are queued for loading, and will be loaded at the engine's convienience. If you attempt to query the resource now, the engine will stall until the result is available. sounds in this state may be delayed, while models/pics/shaders may be invisible.\"), RESSTATE_LOADING},\n\t\t{\"RESSTATE_FAILED\",\t\t\"const float\", ALL,\t\t D(\"Resources in this state are unusable/could not be loaded. You will get placeholders or dummy results. Queries will not stall the engine. The engine may display placeholder content.\"), RESSTATE_FAILED},\n\t\t{\"RESSTATE_LOADED\",\t\t\"const float\", ALL,\t\t D(\"Resources in this state are finally usable, everything will work okay. Hurrah. Queries will not stall the engine.\"), RESSTATE_LOADED},\n\t\t{\"EF_BRIGHTFIELD\",\t\t\"const float\", QW|NQ|CS, NULL, EF_BRIGHTFIELD},\n\t\t{\"EF_MUZZLEFLASH\",\t\t\"const float\",    NQ|CS, NULL, EF_MUZZLEFLASH},\n\t\t{\"EF_BRIGHTLIGHT\",\t\t\"const float\", QW|NQ|CS, NULL, EF_BRIGHTLIGHT},\n\t\t{\"EF_DIMLIGHT\",\t\t\t\"const float\", QW|NQ|CS, NULL, EF_DIMLIGHT},\n\t\t{\"EF_FLAG1\",\t\t\t\"const float\", QW      , NULL, QWEF_FLAG1},\n\t\t{\"EF_FLAG2\",\t\t\t\"const float\", QW      , NULL, QWEF_FLAG2},\n\t\t{\"EF_NODRAW\",\t\t\t\"const float\",    NQ|CS, D(\"Disables drawing of the model. Does NOT work on QW players.\"), NQEF_NODRAW},\n\t\t{\"EF_ADDITIVE\",\t\t\t\"const float\", QW|NQ|CS, D(\"The entity will be drawn with an additive blend. This is NOT supported on players in any quakeworld engine.\"), NQEF_ADDITIVE},\n\t\t{\"EF_BLUE\",\t\t\t\t\"const float\", QW|NQ|CS, D(\"A blue glow\"), EF_BLUE},\n\t\t{\"EF_RED\",\t\t\t\t\"const float\", QW|NQ|CS, D(\"A red glow\"), EF_RED},\n\t\t{\"EF_GREEN\",\t\t\t\"const float\", QW|NQ|CS, D(\"A green glow\"), EF_GREEN},\n\t\t{\"EF_FULLBRIGHT\",\t\t\"const float\", QW|NQ|CS, D(\"This entity will ignore lighting\"), EF_FULLBRIGHT},\n\t\t{\"EF_NOSHADOW\",\t\t\t\"const float\", QW|NQ|CS, D(\"This entity will not cast shadows\"), EF_NOSHADOW},\n\t\t{\"EF_NODEPTHTEST\",\t\t\"const float\", QW|NQ|CS, D(\"This entity will be drawn over the top of other things that are closer.\"), EF_NODEPTHTEST},\n\n\t\t{\"EF_NOMODELFLAGS\",\t\t\"const float\", QW|NQ, D(\"Surpresses the normal flags specified in the model.\"), EF_NOMODELFLAGS},\n\n\t\t{\"MF_ROCKET\",\t\t\t\"const float\", QW|NQ|CS, NULL, EF_MF_ROCKET>>24},\n\t\t{\"MF_GRENADE\",\t\t\t\"const float\", QW|NQ|CS, NULL, EF_MF_GRENADE>>24},\n\t\t{\"MF_GIB\",\t\t\t\t\"const float\", QW|NQ|CS, D(\"Regular blood trail\"), EF_MF_GIB>>24},\n\t\t{\"MF_ROTATE\",\t\t\t\"const float\", QW|NQ|CS, NULL, EF_MF_ROTATE>>24},\n\t\t{\"MF_TRACER\",\t\t\t\"const float\", QW|NQ|CS, D(\"AKA: green scrag trail\"), EF_MF_TRACER>>24},\n\t\t{\"MF_ZOMGIB\",\t\t\t\"const float\", QW|NQ|CS, D(\"Dark blood trail\"), EF_MF_ZOMGIB>>24},\n\t\t{\"MF_TRACER2\",\t\t\t\"const float\", QW|NQ|CS, D(\"AKA: hellknight projectile trail\"), EF_MF_TRACER2>>24},\n\t\t{\"MF_TRACER3\",\t\t\t\"const float\", QW|NQ|CS, D(\"AKA: purple vore trail\"), EF_MF_TRACER3>>24},\n\n\n\t\t{\"SL_ORG_TL\",\t\t\t\"DEP_CSQC const float\", QW|NQ, D(\"Used with showpic etc, specifies that the x+y values are relative to the top-left of the screen\"), SL_ORG_TL},\n\t\t{\"SL_ORG_TR\",\t\t\t\"DEP_CSQC const float\", QW|NQ, NULL, SL_ORG_TR},\n\t\t{\"SL_ORG_BL\",\t\t\t\"DEP_CSQC const float\", QW|NQ, NULL, SL_ORG_BL},\n\t\t{\"SL_ORG_BR\",\t\t\t\"DEP_CSQC const float\", QW|NQ, NULL, SL_ORG_BR},\n\t\t{\"SL_ORG_MM\",\t\t\t\"DEP_CSQC const float\", QW|NQ, NULL, SL_ORG_MM},\n\t\t{\"SL_ORG_TM\",\t\t\t\"DEP_CSQC const float\", QW|NQ, NULL, SL_ORG_TM},\n\t\t{\"SL_ORG_BM\",\t\t\t\"DEP_CSQC const float\", QW|NQ, NULL, SL_ORG_BM},\n\t\t{\"SL_ORG_ML\",\t\t\t\"DEP_CSQC const float\", QW|NQ, NULL, SL_ORG_ML},\n\t\t{\"SL_ORG_MR\",\t\t\t\"DEP_CSQC const float\", QW|NQ, NULL, SL_ORG_MR},\n\n\t\t{\"PFLAGS_NOSHADOW\",\t\t\"const float\", QW|NQ|CS, D(\"Associated RT lights attached will not cast shadows, making them significantly faster to draw.\"), PFLAGS_NOSHADOW},\n\t\t{\"PFLAGS_CORONA\",\t\t\"const float\", QW|NQ|CS, D(\"Enables support of coronas on the associated rtlights.\"), PFLAGS_CORONA},\n\t\t{\"PFLAGS_FULLDYNAMIC\",\t\"const float\", QW|NQ, D(\"When set in self.pflags, enables fully-customised dynamic lights. Custom rtlight information is not otherwise used.\"), PFLAGS_FULLDYNAMIC},\n\n\t\t//including these for csqc stat types, hash tables, etc.\n//\t\t{\"EV_VOID\",\t\t\t\t\"const float\", ALL, NULL, ev_void},\n\t\t{\"EV_STRING\",\t\t\t\"const float\", ALL, NULL, ev_string},\n\t\t{\"EV_FLOAT\",\t\t\t\"const float\", ALL, NULL, ev_float},\n\t\t{\"EV_VECTOR\",\t\t\t\"const float\", ALL, NULL, ev_vector},\n\t\t{\"EV_ENTITY\",\t\t\t\"const float\", ALL, NULL, ev_entity},\n\t\t{\"EV_FIELD\",\t\t\t\"const float\", ALL, NULL, ev_field},\n\t\t{\"EV_FUNCTION\",\t\t\t\"const float\", ALL, NULL, ev_function},\n\t\t{\"EV_POINTER\",\t\t\t\"const float\", ALL, NULL, ev_pointer},\n\t\t{\"EV_INTEGER\",\t\t\t\"const float\", ALL, NULL, ev_integer},\n\t\t{\"EV_UINT\",\t\t\t\t\"const float\", ALL, NULL, ev_uint},\n\t\t{\"EV_INT64\",\t\t\t\"const float\", ALL, NULL, ev_int64},\n\t\t{\"EV_UINT64\",\t\t\t\"const float\", ALL, NULL, ev_uint64},\n\t\t{\"EV_DOUBLE\",\t\t\t\"const float\", ALL, NULL, ev_double},\n\n\t\t{\"gamestate\",\t\t\t\"hashtable\",   ALL, D(\"Special hash table index for hash_add and hash_get. Entries in this table will persist over map changes (and doesn't need to be created/deleted).\"), 0},\n\t\t{\"HASH_REPLACE\",\t\t\"const float\", ALL, D(\"Used with hash_add. Attempts to remove the old value instead of adding two values for a single key.\"), 256},\n\t\t{\"HASH_ADD\",\t\t\t\"const float\", ALL, D(\"Used with hash_add. The new entry will be inserted in addition to the existing entry.\"), 512},\n\n#ifdef QUAKESTATS\n\t\t{\"STAT_HEALTH\",\t\t\t\"const float\", CS, D(\"Player's health.\"), STAT_HEALTH},\n\t\t{\"STAT_WEAPONMODELI\",\t\"const float\", CS, D(\"This is the modelindex of the current viewmodel (renamed from the original name 'STAT_WEAPON' due to confusions).\"), STAT_WEAPONMODELI},\n\t\t{\"STAT_AMMO\",\t\t\t\"const float\", CS, D(\"player.currentammo\"), STAT_AMMO},\n\t\t{\"STAT_ARMOR\",\t\t\t\"const float\", CS, NULL, STAT_ARMOR},\n\t\t{\"STAT_WEAPONFRAME\",\t\"const float\", CS, NULL, STAT_WEAPONFRAME},\n\t\t{\"STAT_SHELLS\",\t\t\t\"const float\", CS, NULL, STAT_SHELLS},\n\t\t{\"STAT_NAILS\",\t\t\t\"const float\", CS, NULL, STAT_NAILS},\n\t\t{\"STAT_ROCKETS\",\t\t\"const float\", CS, NULL, STAT_ROCKETS},\n\t\t{\"STAT_CELLS\",\t\t\t\"const float\", CS, NULL, STAT_CELLS},\n\t\t{\"STAT_ACTIVEWEAPON\",\t\"const float\", CS, D(\"player.weapon\"), STAT_ACTIVEWEAPON},\n\t\t{\"STAT_TOTALSECRETS\",\t\"const float\", CS, NULL, STAT_TOTALSECRETS},\n\t\t{\"STAT_TOTALMONSTERS\",\t\"const float\", CS, NULL, STAT_TOTALMONSTERS},\n\t\t{\"STAT_FOUNDSECRETS\",\t\"const float\", CS, NULL, STAT_SECRETS},\n\t\t{\"STAT_KILLEDMONSTERS\",\t\"const float\", CS, NULL, STAT_MONSTERS},\n\t\t{\"STAT_ITEMS\",\t\t\t\"const float\", CS, D(\"self.items | (self.items2<<23). In order to decode this stat properly, you need to use getstatbits(STAT_ITEMS,0,23) to read self.items, and getstatbits(STAT_ITEMS,23,11) to read self.items2 or getstatbits(STAT_ITEMS,28,4) to read the visible part of serverflags, whichever is applicable.\"), STAT_ITEMS},\n\t\t{\"STAT_VIEWHEIGHT\",\t\t\"const float\", CS, D(\"player.view_ofs_z\"), STAT_VIEWHEIGHT},\n#ifdef SIDEVIEWS\n\t\t{\"STAT_VIEW2\",\t\t\t\"const float\", CS, D(\"This stat contains the number of the entity in the server's .view2 field.\"), STAT_VIEW2},\n#endif\n\t\t{\"STAT_VIEWZOOM\",\t\t\"const float\", CS, D(\"Scales fov and sensitiity. Part of DP_VIEWZOOM.\"), STAT_VIEWZOOM},\n\n\t\t{\"STAT_USER\",\t\t\t\"const float\", QW|NQ|CS, D(\"Custom user stats start here (lower values are reserved for engine use).\"), 32},\n#endif\n\n\t\t{\"PRECACHE_PIC_FROMWAD\",\"const float\", CS|MENU, D(\"Attempt to load it from the legacy gfx.wad file (usually its better to just use a gfx/ prefix instead).\"), 1},\n\t\t{\"PRECACHE_PIC_NOCLAMP\",\"const float\", CS|MENU, D(\"Texture coords for the pic will not be clamped nor padded nor atlased.\"), 4},\n//\t\t{\"PRECACHE_PIC_MIPMAP\",\t\"const float\", CS|MENU, D(\"Force the image to be mipmapped. This might result in it being blurry, but will not be noisy.\"), 8},\n\t\t{\"PRECACHE_PIC_DOWNLOAD\",\"const float\", CS|MENU, D(\"If no image could be loaded then attempt to download one from the server. This flag can cause the function to block until completion. (Slow!)\"), 256},\n\t\t{\"PRECACHE_PIC_TEST\",\t\"const float\", CS|MENU, D(\"The precache will block until the image is fully loaded, returning a null string on failure. (Slow!)\"), 512},\n\n\t\t{\"VF_MIN\",\t\t\t\t\"const float\", CS|MENU, D(\"The top-left of the 3d viewport in screenspace. The VF_ values are used via the setviewprop/getviewprop builtins.\"), VF_MIN},\n\t\t{\"VF_MIN_X\",\t\t\t\"const float\", CS|MENU, NULL, VF_MIN_X},\n\t\t{\"VF_MIN_Y\",\t\t\t\"const float\", CS|MENU, NULL, VF_MIN_Y},\n\t\t{\"VF_SIZE\",\t\t\t\t\"const float\", CS|MENU, D(\"The width+height of the 3d viewport in screenspace.\"), VF_SIZE},\n\t\t{\"VF_SIZE_X\",\t\t\t\"const float\", CS|MENU, NULL, VF_SIZE_X},\n\t\t{\"VF_SIZE_Y\",\t\t\t\"const float\", CS|MENU, NULL, VF_SIZE_Y},\n\t\t{\"VF_VIEWPORT\",\t\t\t\"const float\", CS|MENU, D(\"vector+vector. Two argument shortcut for VF_MIN and VF_SIZE\"), VF_VIEWPORT},\n\t\t{\"VF_FOV\",\t\t\t\t\"const float\", CS|MENU, D(\"sets both fovx and fovy. consider using afov instead.\"), VF_FOV},\n\t\t{\"VF_FOV_X\",\t\t\t\"const float\", CS|MENU, D(\"horizontal field of view. does not consider aspect at all.\"), VF_FOV_X},\n\t\t{\"VF_FOV_Y\",\t\t\t\"const float\", CS|MENU, D(\"vertical field of view. does not consider aspect at all.\"), VF_FOV_Y},\n\t\t{\"VF_ORIGIN\",\t\t\t\"const float\", CS|MENU, D(\"The origin of the view. Not of the player.\"), VF_ORIGIN},\n\t\t{\"VF_ORIGIN_X\",\t\t\t\"const float\", CS|MENU, NULL, VF_ORIGIN_X},\n\t\t{\"VF_ORIGIN_Y\",\t\t\t\"const float\", CS|MENU, NULL, VF_ORIGIN_Y},\n\t\t{\"VF_ORIGIN_Z\",\t\t\t\"const float\", CS|MENU, NULL, VF_ORIGIN_Z},\n\t\t{\"VF_ANGLES\",\t\t\t\"const float\", CS|MENU, D(\"The angles the view will be drawn at. Not the angle the client reports to the server.\"), VF_ANGLES},\n\t\t{\"VF_ANGLES_X\",\t\t\t\"const float\", CS|MENU, NULL, VF_ANGLES_X},\n\t\t{\"VF_ANGLES_Y\",\t\t\t\"const float\", CS|MENU, NULL, VF_ANGLES_Y},\n\t\t{\"VF_ANGLES_Z\",\t\t\t\"const float\", CS|MENU, NULL, VF_ANGLES_Z},\n\t\t{\"VF_DRAWWORLD\",\t\t\"const float\", CS, D(\"boolean. If set to 1, the engine will draw the world and static/persistant rtlights. If 0, the world will be skipped and everything will be fullbright.\"), VF_DRAWWORLD},\n\t\t{\"VF_DRAWENGINESBAR\",\t\"const float\", CS, D(\"boolean. If set to 1, the sbar will be drawn, and viewsize will be honoured automatically.\"), VF_ENGINESBAR},\n\t\t{\"VF_DRAWCROSSHAIR\",\t\"const float\", CS, D(\"boolean. If set to 1, the engine will draw its default crosshair.\"), VF_DRAWCROSSHAIR},\n\n\t\t{\"VF_MINDIST\",\t\t\t\"const float\", CS|MENU, D(\"The distance of the near clip plane from the view position. Should generally not be <=0, as this would introduce NANs.\"), VF_MINDIST},\n\t\t{\"VF_MAXDIST\",\t\t\t\"const float\", CS|MENU, D(\"The distance of the far clip plane from the view position. If 0, will be considered infinite.\"), VF_MAXDIST},\n\n\t\t{\"VF_CL_VIEWANGLES\",\t\"const float\", CS, NULL, VF_CL_VIEWANGLES_V},\n\t\t{\"VF_CL_VIEWANGLES_X\",\t\"const float\", CS, NULL, VF_CL_VIEWANGLES_X},\n\t\t{\"VF_CL_VIEWANGLES_Y\",\t\"const float\", CS, NULL, VF_CL_VIEWANGLES_Y},\n\t\t{\"VF_CL_VIEWANGLES_Z\",\t\"const float\", CS, NULL, VF_CL_VIEWANGLES_Z},\n\n\t\t{\"VF_PERSPECTIVE\",\t\t\"const float\", CS|MENU, D(\"1: regular rendering. Fov specifies the angle. 0: isometric-style. Fov specifies the number of Quake Units each side of the viewport, and mindist restrictions are removed, pvs culling should be disabled.\"), VF_PERSPECTIVE},\n\t\t{\"VF_ACTIVESEAT\",\t\t\"#define VF_LPLAYER VF_ACTIVESEAT\\nconst float\", CS, D(\"The 'seat' number, used when running splitscreen.\"), VF_ACTIVESEAT},\n\t\t{\"VF_AFOV\",\t\t\t\t\"const float\", CS|MENU, D(\"Aproximate fov. Matches the 'fov' cvar. The engine handles the aspect ratio for you.\"), VF_AFOV},\n\t\t{\"VF_SCREENVSIZE\",\t\t\"const float\", CS|MENU, D(\"Provides a reliable way to retrieve the current virtual screen size (even if the screen is automatically scaled to retain aspect).\"), VF_SCREENVSIZE},\n\t\t{\"VF_SCREENPSIZE\",\t\t\"const float\", CS|MENU, D(\"Provides a reliable way to retrieve the current physical screen size (cvars need vid_restart for them to take effect).\"), VF_SCREENPSIZE},\n\t\t{\"VF_VIEWENTITY\",\t\t\"const float\", CS, D(\"Changes the RF_EXTERNALMODEL flag on entities to match the new selection, and removes entities flaged with RF_VIEWENTITY. Requires cunning use of .entnum and typically requires calling addentities(MASK_VIEWMODEL) too.\"), VF_VIEWENTITY},\n\n\t\t{\"VF_RT_DESTCOLOUR\",\t\"const float\", CS|MENU, D(\"The texture name to write colour info into, this includes both 3d and 2d drawing.\\nAdditional arguments are: format (IMGFMT_*), sizexy.\\nWritten to by both 3d and 2d rendering.\\nNote that any rendertarget textures may be destroyed on video mode changes or so. Shaders can name render targets by prefixing texture names with '$rt:', or $sourcecolour.\"), VF_RT_DESTCOLOUR0},\n\t\t{\"VF_RT_DESTCOLOUR1\",\t\"const float\", CS|MENU, D(\"Like VF_RT_DESTCOLOUR, for multiple render targets.\"), VF_RT_DESTCOLOUR1},\n\t\t{\"VF_RT_DESTCOLOUR2\",\t\"const float\", CS|MENU, D(\"Like VF_RT_DESTCOLOUR, for multiple render targets.\"), VF_RT_DESTCOLOUR2},\n\t\t{\"VF_RT_DESTCOLOUR3\",\t\"const float\", CS|MENU, D(\"Like VF_RT_DESTCOLOUR, for multiple render targets.\"), VF_RT_DESTCOLOUR3},\n//\t\t{\"VF_RT_DESTCOLOUR4\",\t\"const float\", CS|MENU, D(\"Like VF_RT_DESTCOLOUR, for multiple render targets.\"), VF_RT_DESTCOLOUR4},\n//\t\t{\"VF_RT_DESTCOLOUR5\",\t\"const float\", CS|MENU, D(\"Like VF_RT_DESTCOLOUR, for multiple render targets.\"), VF_RT_DESTCOLOUR5},\n//\t\t{\"VF_RT_DESTCOLOUR6\",\t\"const float\", CS|MENU, D(\"Like VF_RT_DESTCOLOUR, for multiple render targets.\"), VF_RT_DESTCOLOUR6},\n//\t\t{\"VF_RT_DESTCOLOUR7\",\t\"const float\", CS|MENU, D(\"Like VF_RT_DESTCOLOUR, for multiple render targets.\"), VF_RT_DESTCOLOUR7},\n\t\t{\"VF_RT_SOURCECOLOUR\",\t\"const float\", CS|MENU, D(\"The texture name to use with shaders that specify a $sourcecolour map.\"), VF_RT_SOURCECOLOUR},\n\t\t{\"VF_RT_DEPTH\",\t\t\t\"const float\", CS|MENU, D(\"The texture name to use as a depth buffer. Also used for shaders that specify $sourcedepth. 1-based. Additional arguments are: format (IMGFMT_D*), sizexy.\"), VF_RT_DEPTH},\n\t\t{\"VF_RT_RIPPLE\",\t\t\"const float\", CS|MENU, D(\"The texture name to use as a ripplemap (target for shaders with 'sort ripple'). Also used for shaders that specify $ripplemap. 1-based. Additional arguments are: format, sizexy.\"), VF_RT_RIPPLE},\n\t\t{\"VF_ENVMAP\",\t\t\t\"const float\", CS|MENU, D(\"The cubemap name to use as a fallback for $reflectcube, if a shader was unable to load one. Note that this doesn't automatically change shader permutations or anything.\"), VF_ENVMAP},\n\t\t{\"VF_USERDATA\",\t\t\t\"const float\", CS|MENU, D(\"Pointer (and byte size) to an array of vec4s. This data is then globally visible to all glsl via the w_user uniform.\"), VF_USERDATA},\n\t\t{\"VF_SKYROOM_CAMERA\",\t\"const float\", CS, D(\"Controls the camera position of the skyroom (which will be drawn underneath transparent sky surfaces). This should move slightly with the real camera, but not so much that the skycamera enters walls. Requires a skyshader with a blend mode on the first pass (or no passes).\"), VF_SKYROOM_CAMERA},\n\t\t{\"VF_PROJECTIONOFFSET\",\t\"const float\", CS|MENU, D(\"vec2 horizontal+vertical offset for the projection matrix, for weird off-centre rendering.\"), VF_PROJECTIONOFFSET},\n\n\t\t{\"DRAWFLAG_NORMAL\",\t\t\"const float\", CS|MENU, D(\"Args for drawpic/drawfill/beginpolygon. Not to be confused with the hexen2-compatibility feature.\"), DRAWFLAG_NORMAL},\n\t\t{\"DRAWFLAG_ADD\",\t\t\"const float\", CS|MENU, D(\"Forces additive blending, overriding any shader settings.\"), DRAWFLAG_ADD},\n\t\t{\"DRAWFLAG_MODULATE\",\t\"const float\", CS|MENU, D(\"Forces alpha blending, overriding any shader settings.\"), DRAWFLAG_MODULATE},\n//\t\t{\"DRAWFLAG_MODULATE2\",\t\"const float\", CS|MENU, D(\".\"), DRAWFLAG_MODULATE2},\n\t\t{\"DRAWFLAG_2D\",\t\t\t\"const float\", CS|MENU, D(\"For use with beginpolygon. The polygon will be drawn to the 2d screen, instead of being added to the 3d scene.\"), DRAWFLAG_2D},\n\t\t{\"DRAWFLAG_TWOSIDED\",\t\"const float\", CS|MENU, D(\"For use with beginpolygon. The polygon will be two-sided without any backface culling.\"), DRAWFLAG_TWOSIDED},\n\t\t{\"DRAWFLAG_LINES\",\t\t\"const float\", CS|MENU, D(\"For use with beginpolygon. The surface verticies should be interpreted as a line loop, instead of a triangle fan.\"), DRAWFLAG_LINES},\n\n\t\t{\"IMGFMT_R8G8B8A8\",\t\t\"const float\", CS|MENU, D(\"Typical 32bit rgba pixel format.\"), 1},\n\t\t{\"IMGFMT_R16G16B16A16F\",\"const float\", CS|MENU, D(\"Half-Float pixel format. Requires gl3 support.\"), 2},\n\t\t{\"IMGFMT_R32G32B32A32F\",\"const float\", CS|MENU, D(\"Regular Float pixel format. Requires gl3 support.\"), 3},\n\t\t{\"IMGFMT_D16\",\t\t\t\"const float\", CS|MENU, D(\"16-bit depth pixel format. Must not be used with VF_RT_DESTCOLOUR*.\"), 4},\n\t\t{\"IMGFMT_D24\",\t\t\t\"const float\", CS|MENU, D(\"24-bit depth pixel format. Must not be used with VF_RT_DESTCOLOUR*.\"), 5},\n\t\t{\"IMGFMT_D32\",\t\t\t\"const float\", CS|MENU, D(\"32-bit depth pixel format. Must not be used with VF_RT_DESTCOLOUR*.\"), 6},\n\t\t{\"IMGFMT_R8\",\t\t\t\"const float\", CS|MENU, D(\"Single channel red-only 8bit pixel format.\"), 7},\n\t\t{\"IMGFMT_R16F\",\t\t\t\"const float\", CS|MENU, D(\"Single channel red-only Half-Float pixel format. Requires gl3 support.\"), 8},\n\t\t{\"IMGFMT_R32F\",\t\t\t\"const float\", CS|MENU, D(\"Single channel red-only Float pixel format. Requires gl3 support.\"), 9},\n\t\t{\"IMGFMT_A2B10G10R10\",\t\"const float\", CS|MENU, D(\"Packed 32-bit packed 10-bit colour pixel format. Requires gl3 support.\"), 10},\n\t\t{\"IMGFMT_R5G6B5\",\t\t\"const float\", CS|MENU, D(\"Packed 16-bit colour pixel format.\"), 11},\n\t\t{\"IMGFMT_R4G4B4A4\",\t\t\"const float\", CS|MENU, D(\"Packed 16-bit colour pixel format, with alpha\"), 12},\n\t\t{\"IMGFMT_R8G8\",\t\t\t\"const float\", CS|MENU, D(\"16-bit two-channel pixel format.\"), 13},\n\t\t{\"IMGFMT_R32G32B32F\",\t\"const float\", CS|MENU, D(\"A pixel format that matches QC's vector type.\"), 14},\n\t\t{\"IMGFMT_P8_RGB8\",\t\t\"const float\", CS|MENU, D(\"8bit paletted format, with 768 bytes of RGB8 palette directly following the image data. Exclusively for r_uploadimage.\"), 15},\n\t\t{\"IMGFMT_P8_RGBA8\",\t\t\"const float\", CS|MENU, D(\"8bit paletted format, with 1024 bytes of RGBA8 palette directly following the image data. Exclusively for r_uploadimage.\"), 16},\n\n\t\t{\"RF_VIEWMODEL\",\t\t\"const float\", CS, D(\"Specifies that the entity is a view model, and that its origin is relative to the current view position. These entities are also subject to viewweapon bob.\"), CSQCRF_VIEWMODEL},\n\t\t{\"RF_EXTERNALMODEL\",\t\"const float\", CS, D(\"Specifies that this entity should be displayed in mirrors (and may still cast shadows), but will not otherwise be visible.\"), CSQCRF_EXTERNALMODEL},\n\t\t{\"RF_DEPTHHACK\",\t\t\"const float\", CS|MENU, D(\"Hacks the depth values such that the entity uses depth values as if it were closer to the screen. This is useful when combined with viewmodels to avoid weapons poking in to walls.\"), CSQCRF_DEPTHHACK},\n\t\t{\"RF_ADDITIVE\",\t\t\t\"const float\", CS|MENU, D(\"Shaders from this entity will temporarily be hacked to use an additive blend mode instead of their normal blend mode.\"), CSQCRF_ADDITIVE},\n\t\t{\"RF_USEAXIS\",\t\t\t\"const float\", CS, D(\"The entity will be oriented according to the current v_forward+v_right+v_up vector values instead of the entity's .angles field.\"), CSQCRF_USEAXIS},\n\t\t{\"RF_NOSHADOW\",\t\t\t\"const float\", CS, D(\"This entity will not cast shadows. Often useful on view models.\"), CSQCRF_NOSHADOW},\n\t\t{\"RF_FRAMETIMESARESTARTTIMES\",\"const float\", CS, D(\"Specifies that the frame1time, frame2time field are timestamps (denoting the start of the animation) rather than time into the animation.\"), CSQCRF_FRAMETIMESARESTARTTIMES},\n\t\t{\"RF_FIRSTPERSON\",\"const float\", CS, D(\"This is basically the opposite of RF_EXTERNALMODEL. Don't draw in third-person or mirrors.\"), CSQCRF_FIRSTPERSON},\n\n\t\t{\"IE_KEYDOWN\",\t\t\t\"const float\", CS|MENU, D(\"Specifies that a key was pressed. Second argument is the scan code. Third argument is the unicode (printable) char value. Fourth argument denotes which keyboard(or mouse, if its a mouse 'scan' key) the event came from. Note that some systems may completely separate scan codes and unicode values, with a 0 value for the unspecified argument.\"), CSIE_KEYDOWN},\n\t\t{\"IE_KEYUP\",\t\t\t\"const float\", CS|MENU, D(\"Specifies that a key was released. Arguments are the same as IE_KEYDOWN. On some systems, this may be fired instantly after IE_KEYDOWN was fired.\"), CSIE_KEYUP},\n\t\t{\"IE_MOUSEDELTA\",\t\t\"const float\", CS|MENU, D(\"Specifies that a mouse was moved (touch screens and tablets typically give IE_MOUSEABS events instead, use in_windowed_mouse 0 to test code to cope with either). Second argument is the X displacement, third argument is the Y displacement. Fourth argument is which mouse or touch event triggered the event.\"), CSIE_MOUSEDELTA},\n\t\t{\"IE_MOUSEABS\",\t\t\t\"const float\", CS|MENU, D(\"Specifies that a mouse cursor or touch event was moved to a specific location relative to the virtual screen space. Second argument is the new X position, third argument is the new Y position. Fourth argument is which mouse or touch event triggered the event.\"), CSIE_MOUSEABS},\n\t\t{\"IE_ACCELEROMETER\",\t\"const float\", CS|MENU, NULL, CSIE_ACCELEROMETER},\n\t\t{\"IE_FOCUS\",\t\t\t\"const float\", CS|MENU, D(\"Specifies that input focus was given. parama says mouse focus, paramb says keyboard focus. If either are -1, then it is unchanged.\"), CSIE_FOCUS},\n\t\t{\"IE_JOYAXIS\",\t\t\t\"const float\", CS|MENU, D(\"Specifies that what value a joystick/controller axis currently specifies. x=axis, y=value. Will be called multiple times, once for each axis of each active controller.\"), CSIE_JOYAXIS},\n\t\t{\"IE_GYROSCOPE\",\t\t\"const float\", CS|MENU, NULL, CSIE_GYROSCOPE},\n\n\t\t{\"GGDI_GAMEDIR\",\t\t\"const float\", CS|MENU, D(\"Used with getgamedirinfo to query the mod's public gamedir. There is often other info that cannot be expressed with just a gamedir name, resulting in dupes or other weirdness.\"), GGDI_GAMEDIR},\n\t\t{\"GGDI_DESCRIPTION\",\t\"const float\", CS|MENU, D(\"The human-readable title of the mod. Empty when no data is known (ie: the gamedir just contains some maps).\"), GGDI_DESCRIPTION},\n\t\t{\"GGDI_OVERRIDES\",\t\t\"const float\", CS|MENU, D(\"A list of settings overrides.\"), GGDI_OVERRIDES},\n\t\t{\"GGDI_LOADCOMMAND\",\t\"const float\", CS|MENU, D(\"The console command needed to actually load the mod.\"), GGDI_LOADCOMMAND},\n\t\t{\"GGDI_ICON\",\t\t\t\"const float\", CS|MENU, D(\"The mod's Icon path, ready for drawpic.\"), GGDI_ICON},\n\t\t{\"GGDI_GAMEDIRLIST\",\t\"const float\", CS|MENU, D(\"A semi-colon delimited list of gamedirs that the mod's content can be loaded through.\"), GGDI_ALLGAMEDIRS},\n\n\t\t{\"GPMI_NAME\",\t\t\t\"const int\", CS|MENU, D(\"Used with getpackagemanagerinfo. Refers to the package's name.\"), GPMI_NAME},\n\t\t{\"GPMI_CATEGORY\",\t\t\"const int\", CS|MENU, D(\"Refers to the package's category.\"), GPMI_CATEGORY},\n\t\t{\"GPMI_TITLE\",\t\t\t\"const int\", CS|MENU, D(\"The human-readable title of the mod with spaces and things.\"), GPMI_TITLE},\n\t\t{\"GPMI_VERSION\",\t\t\"const int\", CS|MENU, D(\"The package's version. There may be multiple packages with the same name.\"), GPMI_VERSION},\n\t\t{\"GPMI_DESCRIPTION\",\t\"const int\", CS|MENU, D(\"Fairly long textural description of the package which may include gotchas/warnings and other fairly important info.\"), GPMI_DESCRIPTION},\n\t\t{\"GPMI_LICENSE\",\t\t\"const int\", CS|MENU, D(\"The approximate name of the license the package is covered by, if known.\"), GPMI_LICENSE},\n\t\t{\"GPMI_AUTHOR\",\t\t\t\"const int\", CS|MENU, D(\"Who owns the respective (additional) copyrights.\"), GPMI_AUTHOR},\n\t\t{\"GPMI_WEBSITE\",\t\t\"const int\", CS|MENU, D(\"Website associated with the third-party package.\"), GPMI_WEBSITE},\n\t\t{\"GPMI_INSTALLED\",\t\t\"const int\", CS|MENU, D(\"Reports the package's installation status. May be enabled(fully installed and active), present(previously installed, but not currently in use), corrupt(enabling will delete+redownload), pending(queued for installation), or a percentage(currently downloading). If empty means the user does not have a copy nor do they currently intend to install it.\"), GPMI_INSTALLED},\n\t\t{\"GPMI_ACTION\",\t\t\t\"const int\", CS|MENU, D(\"The action that will be carried out when a 'pkg apply' command is issued(and confirmed).\"), GPMI_ACTION},\n\t\t{\"GPMI_AVAILABLE\",\t\t\"const int\", CS|MENU, D(\"Specifies whether the package may be downloaded.\"), GPMI_AVAILABLE},\n\t\t{\"GPMI_FILESIZE\",\t\t\"const int\", CS|MENU, D(\"The download size of the package.\"), GPMI_FILESIZE},\n\t\t{\"GPMI_GAMEDIR\",\t\t\"const int\", CS|MENU, D(\"Which gamedir this package will be installed into.\"), GPMI_GAMEDIR},\n\t\t{\"GPMI_MAPS\",\t\t\t\"const int\", CS|MENU, D(\"Retrieves a tokenisable list of map names provided in this package, loadable via `map pkgname:mapname`.\"), GPMI_MAPS},\n\t\t{\"GPMI_PREVIEWIMG\",\t\t\"const int\", CS|MENU, D(\"An image to drawpic for a preview.\"), GPMI_PREVIEWIMG},\n\n\t\t{\"CLIENTTYPE_DISCONNECTED\",\"const float\", QW|NQ, D(\"Return value from clienttype() builtin. This entity is a player slot that is currently empty.\"), CLIENTTYPE_DISCONNECTED},\n\t\t{\"CLIENTTYPE_REAL\",\t\t\"const float\", QW|NQ, D(\"This is a real player, and not a bot.\"), CLIENTTYPE_REAL},\n\t\t{\"CLIENTTYPE_BOT\",\t\t\"const float\", QW|NQ, D(\"This player slot does not correlate to a real player, any messages sent to this client will be ignored.\"), CLIENTTYPE_BOT},\n\t\t{\"CLIENTTYPE_NOTACLIENT\",\"const float\",QW|NQ, D(\"This entity is not even a player slot. This is typically an error condition.\"), CLIENTTYPE_NOTACLIENT},\n\n\t\t{\"FILE_STREAM\",\t\t\t\"const float\", ALL, D(\"The filename is a tcp:// or tls:// network scheme that should be used instead of direct file access. Such file handles should be read as binary with fread instead of using fgets, in order to distinguish between empty-line, eof, and would-block - the qc is responsible for periodically checking if new data is available while being aware that ANY response may be received a single byte at a time.\"), -1},\n\t\t{\"FILE_READ\",\t\t\t\"const float\", ALL, D(\"The file may be read via fgets to read a single line at a time.\"), FRIK_FILE_READ},\n\t\t{\"FILE_APPEND\",\t\t\t\"const float\", ALL, D(\"Like FILE_WRITE, but writing starts at the end of the file.\"), FRIK_FILE_APPEND},\n\t\t{\"FILE_WRITE\",\t\t\t\"const float\", ALL, D(\"fputs will be used to write to the file.\"), FRIK_FILE_WRITE},\n\t\t{\"FILE_READNL\",\t\t\t\"const float\", ALL, D(\"Like FILE_READ, except newlines are not special. fgets reads the entire file into a tempstring.\"), FRIK_FILE_READNL},\n\t\t{\"FILE_MMAP_READ\",\t\t\"const float\", ALL, D(\"The file will be loaded into memory. fgets returns a pointer to the first byte (and will always return the same value for this file). Cast this to your datatype.\"), FRIK_FILE_MMAP_READ},\n\t\t{\"FILE_MMAP_RW\",\t\t\"const float\", ALL, D(\"Like FILE_MMAP_READ, except any changes to the data will be written back to disk once the file is closed.\"), FRIK_FILE_MMAP_RW},\n\n\t\t{\"MASK_ENGINE\",\t\t\t\"const float\", CS, D(\"Valid as an argument for addentities. If specified, all non-csqc entities will be added to the scene.\"), MASK_DELTA},\n\t\t{\"MASK_VIEWMODEL\",\t\t\"const float\", CS, D(\"Valid as an argument for addentities. If specified, the regular engine viewmodel will be added to the scene.\"), MASK_STDVIEWMODEL},\n\t\t{\"PREDRAW_AUTOADD\",\t\t\"const float\", CS, D(\"Valid as a return value from the predraw function. Returning this will cause the engine to automatically invoke addentity(self) for you.\"), false},\n\t\t{\"PREDRAW_NEXT\",\t\t\"const float\", CS, D(\"Valid as a return value from the predraw function. Returning this will simply move on to the next entity without the autoadd behaviour, so can be used for particle/invisible/special entites, or entities that were explicitly drawn with addentity.\"), true},\n\n\t\t{\"LFIELD_ORIGIN\",\t\t\"const float\", CS, NULL, lfield_origin},\n\t\t{\"LFIELD_COLOUR\",\t\t\"const float\", CS, NULL, lfield_colour},\n\t\t{\"LFIELD_RADIUS\",\t\t\"const float\", CS, NULL, lfield_radius},\n\t\t{\"LFIELD_FLAGS\",\t\t\"const float\", CS, NULL, lfield_flags},\n\t\t{\"LFIELD_STYLE\",\t\t\"const float\", CS, NULL, lfield_style},\n\t\t{\"LFIELD_ANGLES\",\t\t\"const float\", CS, NULL, lfield_angles},\n\t\t{\"LFIELD_FOV\",\t\t\t\"const float\", CS, NULL, lfield_fov},\n\t\t{\"LFIELD_CORONA\",\t\t\"const float\", CS, NULL, lfield_corona},\n\t\t{\"LFIELD_CORONASCALE\",\t\"const float\", CS, NULL, lfield_coronascale},\n\t\t{\"LFIELD_CUBEMAPNAME\",\t\"const float\", CS, NULL, lfield_cubemapname},\n\t\t{\"LFIELD_AMBIENTSCALE\",\t\"const float\", CS, NULL, lfield_ambientscale},\n\t\t{\"LFIELD_DIFFUSESCALE\",\t\"const float\", CS, NULL, lfield_diffusescale},\n\t\t{\"LFIELD_SPECULARSCALE\",\"const float\", CS, NULL, lfield_specularscale},\n\t\t{\"LFIELD_ROTATION\",\t\t\"const float\", CS, NULL, lfield_rotation},\n\t\t{\"LFIELD_DIETIME\",\t\t\"const float\", CS, NULL, lfield_dietime},\n\t\t{\"LFIELD_RGBDECAY\",\t\t\"const float\", CS, NULL, lfield_rgbdecay},\n\t\t{\"LFIELD_RADIUSDECAY\",\t\"const float\", CS, NULL, lfield_radiusdecay},\n\t\t{\"LFIELD_STYLESTRING\",\t\"const float\", CS, NULL, lfield_stylestring},\n\t\t{\"LFIELD_NEARCLIP\",\t\t\"const float\", CS, NULL, lfield_nearclip},\n\t\t{\"LFIELD_OWNER\",\t\t\"const float\", CS, NULL, lfield_owner},\n\n\t\t{\"LFLAG_NORMALMODE\",\t\"const float\", CS, NULL, LFLAG_NORMALMODE},\n\t\t{\"LFLAG_REALTIMEMODE\",\t\"const float\", CS, NULL, LFLAG_REALTIMEMODE},\n\t\t{\"LFLAG_LIGHTMAP\",\t\t\"const float\", CS, NULL, LFLAG_LIGHTMAP},\n\t\t{\"LFLAG_FLASHBLEND\",\t\"const float\", CS, NULL, LFLAG_FLASHBLEND},\n\t\t{\"LFLAG_NOSHADOWS\",\t\t\"const float\", CS, NULL, LFLAG_NOSHADOWS},\n\t\t{\"LFLAG_SHADOWMAP\",\t\t\"const float\", CS, NULL, LFLAG_SHADOWMAP},\n\t\t{\"LFLAG_CREPUSCULAR\",\t\"const float\", CS, NULL, LFLAG_CREPUSCULAR},\n#ifdef LFLAG_ORTHO\n\t\t{\"LFLAG_ORTHOSUN\",\t\t\"const float\", CS, NULL, LFLAG_ORTHO},\n#else\n\t\t{\"LFLAG_ORTHOSUN\",\t\t\"const float\", CS, NULL, 0},\n#endif\n\n#ifdef TERRAIN\n\t\t{\"TEREDIT_RELOAD\",\t\t\"const float\", CS, NULL, ter_reload},\n\t\t{\"TEREDIT_SAVE\",\t\t\"const float\", CS, NULL, ter_save},\n\t\t{\"TEREDIT_SETHOLE\",\t\t\"const float\", CS, NULL, ter_sethole},\n\t\t{\"TEREDIT_HEIGHT_SET\",\t\"const float\", CS, NULL, ter_height_set},\n\t\t{\"TEREDIT_HEIGHT_SMOOTH\",\"const float\",CS, NULL, ter_height_smooth},\n\t\t{\"TEREDIT_HEIGHT_SPREAD\",\"const float\",CS, NULL, ter_height_spread},\n\t\t{\"TEREDIT_HEIGHT_RAISE\",\"const float\", CS, NULL, ter_raise},\n\t\t{\"TEREDIT_HEIGHT_FLATTEN\",\"const float\", CS, NULL, ter_height_flatten},\n\t\t{\"TEREDIT_HEIGHT_LOWER\",\"const float\", CS, NULL, ter_lower},\n\t\t{\"TEREDIT_TEX_KILL\",\t\"const float\", CS, NULL, ter_tex_kill},\n\t\t{\"TEREDIT_TEX_GET\",\t\t\"const float\", CS, NULL, ter_tex_get},\n\t\t{\"TEREDIT_TEX_BLEND\",\t\"const float\", CS, NULL, ter_tex_blend},\n\t\t{\"TEREDIT_TEX_UNIFY\",\t\"const float\", CS, NULL, ter_tex_concentrate},\n\t\t{\"TEREDIT_TEX_NOISE\",\t\"const float\", CS, NULL, ter_tex_noise},\n\t\t{\"TEREDIT_TEX_BLUR\",\t\"const float\", CS, NULL, ter_tex_blur},\n\t\t{\"TEREDIT_TEX_REPLACE\",\t\"const float\", CS, NULL, ter_tex_replace},\n\t\t{\"TEREDIT_TEX_SETMASK\",\t\"const float\", CS, NULL, ter_tex_mask},\n\t\t{\"TEREDIT_WATER_SET\",\t\"const float\", CS, NULL, ter_water_set},\n\t\t{\"TEREDIT_MESH_ADD\",\t\"const float\", CS, NULL, ter_mesh_add},\n\t\t{\"TEREDIT_MESH_KILL\",\t\"const float\", CS, NULL, ter_mesh_kill},\n\t\t{\"TEREDIT_TINT\",\t\t\"const float\", CS, NULL, ter_tint},\n\t\t{\"TEREDIT_RESET_SECT\",\t\"const float\", CS, NULL, ter_reset},\n\t\t{\"TEREDIT_RELOAD_SECT\",\t\"const float\", CS, NULL, ter_reloadsect},\n//\t\t{\"TEREDIT_ENTS_WIPE\",\t\"const float\", CS, NULL, ter_ents_wipe_deprecated},\n//\t\t{\"TEREDIT_ENTS_CONCAT\",\t\"const float\", CS, NULL, ter_ents_concat},\n//\t\t{\"TEREDIT_ENTS_GET\",\t\"const float\", CS, NULL, ter_ents_get},\n\t\t{\"TEREDIT_ENT_GET\",\t\t\"const float\", CS, NULL, ter_ent_get},\n\t\t{\"TEREDIT_ENT_SET\",\t\t\"const float\", CS, NULL, ter_ent_set},\n\t\t{\"TEREDIT_ENT_ADD\",\t\t\"const float\", CS, NULL, ter_ent_add},\n\t\t{\"TEREDIT_ENT_COUNT\",\t\"const float\", CS, NULL, ter_ent_count},\n#endif\n\n#ifdef CL_MASTER\n\t\t{\"SLIST_HOSTCACHEVIEWCOUNT\",\t\"const float\", CS|MENU, NULL, SLIST_HOSTCACHEVIEWCOUNT},\n\t\t{\"SLIST_HOSTCACHETOTALCOUNT\",\t\"const float\", CS|MENU, NULL, SLIST_HOSTCACHETOTALCOUNT},\n\t\t{\"SLIST_MASTERQUERYCOUNT\",\t\t\"const float\", CS|MENU, NULL, SLIST_MASTERQUERYCOUNT},\n\t\t{\"SLIST_MASTERREPLYCOUNT\",\t\t\"const float\", CS|MENU, NULL, SLIST_MASTERREPLYCOUNT},\n\t\t{\"SLIST_SERVERQUERYCOUNT\",\t\t\"const float\", CS|MENU, NULL, SLIST_SERVERQUERYCOUNT},\n\t\t{\"SLIST_SERVERREPLYCOUNT\",\t\t\"const float\", CS|MENU, NULL, SLIST_SERVERREPLYCOUNT},\n\t\t{\"SLIST_SORTFIELD\",\t\t\t\t\"const float\", CS|MENU, NULL, SLIST_SORTFIELD},\n\t\t{\"SLIST_SORTDESCENDING\",\t\t\"const float\", CS|MENU, NULL, SLIST_SORTDESCENDING},\n\t\t{\"SLIST_TEST_CONTAINS\",\t\t\t\"const float\", CS|MENU, NULL, SLIST_TEST_CONTAINS},\n\t\t{\"SLIST_TEST_NOTCONTAIN\",\t\t\"const float\", CS|MENU, NULL, SLIST_TEST_NOTCONTAIN},\n\t\t{\"SLIST_TEST_LESSEQUAL\",\t\t\"const float\", CS|MENU, NULL, SLIST_TEST_LESSEQUAL},\n\t\t{\"SLIST_TEST_LESS\",\t\t\t\t\"const float\", CS|MENU, NULL, SLIST_TEST_LESS},\n\t\t{\"SLIST_TEST_EQUAL\",\t\t\t\"const float\", CS|MENU, NULL, SLIST_TEST_EQUAL},\n\t\t{\"SLIST_TEST_GREATER\",\t\t\t\"const float\", CS|MENU, NULL, SLIST_TEST_GREATER},\n\t\t{\"SLIST_TEST_GREATEREQUAL\",\t\t\"const float\", CS|MENU, NULL, SLIST_TEST_GREATEREQUAL},\n\t\t{\"SLIST_TEST_NOTEQUAL\",\t\t\t\"const float\", CS|MENU, NULL, SLIST_TEST_NOTEQUAL},\n\t\t{\"SLIST_TEST_STARTSWITH\",\t\t\"const float\", CS|MENU, NULL, SLIST_TEST_STARTSWITH},\n\t\t{\"SLIST_TEST_NOTSTARTSWITH\",\t\"const float\", CS|MENU, NULL, SLIST_TEST_NOTSTARTSWITH},\n#endif\n\t\t{NULL}\n\t};\n\n#define DUMPHELP\t\\\n\t\t\t\t\t\"Available options:\\n\"\t\\\n\t\t\t\t\t\"-Ffte       - target only FTE (optimations and additional extensions)\\n\"\t\\\n\t\t\t\t\t\"-Tnq        - dump only NQ fields\\n\"\t\\\n\t\t\t\t\t\"-Tqw        - dump only QW fields\\n\"\t\\\n\t\t\t\t\t\"-Tcs        - dump only CSQC fields\\n\"\t\\\n\t\t\t\t\t\"-Tsimplecs  - dump only Simple-CSQC fields\\n\"\t\\\n\t\t\t\t\t\"-Tdpcs      - dump only DP-CSQC fields\\n\"\t\\\n\t\t\t\t\t\"-Tmenu      - dump only menuqc fields\\n\"\t\\\n\t\t\t\t\t\"-Tid1       - omits any symbols that conflict with vanilla defs.qc\\n\"\t\\\n\t\t\t\t\t\"-Tdp        - omits any symbols that conflict with dpextensions.qc\\n\"\t\\\n\t\t\t\t\t\"-Fdefines   - generate #defines instead of constants\\n\"\t\\\n\t\t\t\t\t\"-Faccessors - use accessors instead of basic types via defines\\n\"\t\\\n\t\t\t\t\t\"-Fdepfilter - use symbol tables to include deprecation warnings for symbols not supported by other engines\\n\"\t\\\n\t\t\t\t\t\"-O          - write to a different qc file\\n\"\n\ttarg = 0;\n\tfor (i = 1; i < Cmd_Argc(); i++)\n\t{\n\t\tchar *arg = Cmd_Argv(i);\n\t\tif (!stricmp(arg, \"-Ffte\"))\n\t\t\ttarg |= FTE;\n\t\telse if (!stricmp(arg, \"-Tnq\"))\n\t\t\ttarg |= NQ;\n\t\telse if (!stricmp(arg, \"-Tqw\"))\n\t\t\ttarg |= QW;\n\t\telse if (!stricmp(arg, \"-Tcs\"))\n\t\t\ttarg |= FTE_CS;\n\t\telse if (!stricmp(arg, \"-Tsimplecs\"))\n\t\t\ttarg |= QSS_CS;\n\t\telse if (!stricmp(arg, \"-Tdpcs\"))\n\t\t\ttarg |= DP_CS;\n\t\telse if (!stricmp(arg, \"-Tmenu\"))\n\t\t\ttarg |= MENU;\n\t\telse if (!stricmp(arg, \"-Th2\"))\n\t\t\ttarg |= H2;\t//TESTME\n\t\telse if (!stricmp(arg, \"-Tid1\"))\n\t\t\ttarg |= ID1;\n\t\telse if (!stricmp(arg, \"-Tdp\"))\n\t\t\ttarg |= DPX;\n\t\telse if (!stricmp(arg, \"-Fdefines\"))\n\t\t\tdefines = true;\n\t\telse if (!stricmp(arg, \"-Fnodefines\"))\n\t\t\tdefines = false;\n\t\telse if (!stricmp(arg, \"-Faccessors\"))\n\t\t\taccessors = true;\n\t\telse if (!stricmp(arg, \"-Fnoaccessors\"))\n\t\t\taccessors = false;\n\t\telse if (!stricmp(arg, \"-Fdepfilter\"))\n\t\t\tdepfilter = true;\n\t\telse if (!Q_strncasecmp(arg, \"-O\", 2))\n\t\t{\n\t\t\tif (arg[2])\n\t\t\t\tfname = arg+2;\n\t\t\telse\n\t\t\t\tfname = Cmd_Argv(++i);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tCon_Printf(\"Unknown argument \\\"%s\\\"\\n\" DUMPHELP, arg);\n\t\t\treturn;\n\t\t}\n\t}\n\tif (!(targ & ALL))\n\t\ttarg |= (QW|NQ|FTE_CS|MENU);\n\n\t//our #ifdefs can't cope with targetting more than one csqc type at a time.\n\tif ((targ & QSS_CS) && (targ & (FTE_CS|DP_CS)))\n\t\ttarg &= ~(FTE_CS|DP_CS);\t//QSS doesn't support any other layours.\n\tif ((targ & DP_CS) && (targ & FTE_CS))\n\t\ttarg &= ~(FTE_CS);\t//if you're trying to target both, then you generally want to favour the lowest common denominator. FTE has workarounds in place so this is the most compatible option (which sucks).\n\n\tif (!*fname)\n\t\tfname = \"fteextensions\";\n\tfname = va(\"%s/%s.qc\", pr_sourcedir.string, fname);\n\tFS_DisplayPath(fname, FS_GAMEONLY, dbgfname, sizeof(dbgfname));\n\tFS_CreatePath(fname, FS_GAMEONLY);\n\tf = FS_OpenVFS(fname, \"wb\", FS_GAMEONLY);\n\tif (!f)\n\t{\n\t\tCon_Printf(\"Unable to create \\\"%s\\\"\\n\", dbgfname);\n\t\treturn;\n\t}\n\n\tVFS_PRINTF(f,\t\"/*\\n\"\n\t\t\t\t\t\"This file was generated by %s %s, dated %s.\\n\"\n\t\t\t\t\t\"This file can be regenerated by issuing the following command:\\n\"\n\t\t\t\t\t\"%s %s\\n\"\n\t\t\t\t\t\"(Use the -help arg for a list of available args)\\n\"\n\t\t\t\t\t\"*/\\n\"\n\t\t\t\t\t, FULLENGINENAME, STRINGIFY(SVNREVISION),\n\t\t\t\t\t#ifdef SVNDATE\n\t\t\t\t\tSTRINGIFY(SVNDATE)\n\t\t\t\t\t#else\n\t\t\t\t\t__DATE__\n\t\t\t\t\t#endif\n\t\t\t\t\t, Cmd_Argv(0), Cmd_Args());\n\n\tVFS_PRINTF(f, \"#pragma noref 1\\n\");\n\tVFS_PRINTF(f, \"//#pragma flag enable logicops\\n\");\n\n\tVFS_PRINTF(f, \"#pragma warning %s Q101 /*too many parms. The vanilla qcc didn't validate properly, hence why fteqcc normally treats it as a warning.*/\\n\", (targ & ID1)?\"enable\":\"error\");\n\tVFS_PRINTF(f, \"#pragma warning %s Q105 /*too few parms. The vanilla qcc didn't validate properly, hence why fteqcc normally treats it as a warning.*/\\n\", (targ & ID1)?\"enable\":\"error\");\n\tVFS_PRINTF(f, \"#pragma warning %s Q106 /*assignment to constant/lvalue. Define them as var if you want to initialise something.*/\\n\", (targ & ID1)?\"enable\":\"error\");\n\tVFS_PRINTF(f, \"#pragma warning error Q208 /*system crc unknown. Compatibility goes out of the window if you disable this.*/\\n\");\n#ifndef HAVE_LEGACY\n\tVFS_PRINTF(f, \"#pragma warning error F211 /*system crc outdated (eg: dp's csqc). Such mods will not run properly in FTE.*/\\n\");\n#else\n//\tVFS_PRINTF(f, \"#pragma warning disable F211 /*system crc outdated (eg: dp's csqc). Note that this may trigger emulation.*/\\n\");\n#endif\n\tVFS_PRINTF(f, \"#pragma warning enable F301 /*non-utf-8 strings. Think of the foreigners! Also think of text editors that insist on screwing up your char encodings.*/\\n\");\n\tVFS_PRINTF(f, \"#pragma warning enable F302 /*uninitialised locals. They usually default to 0 in qc (except in recursive functions), but its still probably a bug*/\\n\");\n//\tVFS_PRINTF(f, \"#pragma warning %s F308 /*Optional arguments differ on redeclaration.*/\\n\", (targ & ID1)?\"disable\":\"enable\");\n\n#ifdef HEXEN2\n\tif ((targ&ALL) == H2)\n\t{\n\t\tif (targ&FTE)\n\t\t\tVFS_PRINTF(f, \"#pragma target FTEH2\\n#define FTEDEP DEP\\n\");\n\t\telse\n\t\t\tVFS_PRINTF(f, \"#pragma target H2\\n\");\n\t}\n\telse\n#endif\n\t{\n\t\tif (targ&FTE)\n\t\t\tVFS_PRINTF(f, \"#pragma target FTE\\n#define FTEDEP DEP\\n\");\n\t}\n\tif ((targ&ALL) == FTE_CS)\n\t\tVFS_PRINTF(f,\t\"#ifndef CSQC\\n\"\n\t\t\t\t\t\t\t\"\\t#define CSQC\\n\"\n\t\t\t\t\t\t\"#endif\\n\"\n\t\t\t\t\t\t);\n\telse if ((targ&ALL) == QSS_CS)\n\t\tVFS_PRINTF(f,\t\"#ifndef CSQC\\n\"\n\t\t\t\t\t\t\t\"\\t#define CSQC\\n\"\n\t\t\t\t\t\t\t\"\\t#define SIMPLE_CSQC\\n\"\n\t\t\t\t\t\t\"#endif\\n\"\n\t\t\t\t\t\t);\n\telse if ((targ&ALL) == DP_CS)\n\t\tVFS_PRINTF(f,\t\"#ifndef CSQC\\n\"\n\t\t\t\t\t\t\t\"\\t#define CSQC\\n\"\n\t\t\t\t\t\t\t\"\\t#define DP_CSQC\\n\"\n\t\t\t\t\t\t\"#endif\\n\"\n\t\t\t\t\t\t);\n\telse if ((targ&ALL) == NQ)\n\t\tVFS_PRINTF(f,\t\"#ifndef NETQUAKE\\n\"\n\t\t\t\t\t\t\t\"\\t#define NETQUAKE\\n\"\n\t\t\t\t\t\t\"#endif\\n\"\n\t\t\t\t\t\t\"#ifndef NQSSQC\\n\"\n\t\t\t\t\t\t\t\"\\t#define NQSSQC\\n\"\n\t\t\t\t\t\t\"#endif\\n\"\n\t\t\t\t\t\t\"#ifndef SSQC\\n\"\n\t\t\t\t\t\t\t\"\\t#define SSQC\\n\"\n\t\t\t\t\t\t\"#endif\\n\"\n\t\t\t\t\t\t);\n\telse if ((targ&ALL) == QW)\n\t\tVFS_PRINTF(f,\t\"#ifndef QUAKEWORLD\\n\"\n\t\t\t\t\t\t\t\"\\t#define QUAKEWORLD\\n\"\n\t\t\t\t\t\t\"#endif\\n\"\n\t\t\t\t\t\t\"#ifndef QWSSQC\\n\"\n\t\t\t\t\t\t\t\"\\t#define QWSSQC\\n\"\n\t\t\t\t\t\t\"#endif\\n\"\n\t\t\t\t\t\t\"#ifndef SSQC\\n\"\n\t\t\t\t\t\t\t\"\\t#define SSQC\\n\"\n\t\t\t\t\t\t\"#endif\\n\"\n\t\t\t\t\t\t);\n\telse if ((targ&ALL) == MENU)\n\t\tVFS_PRINTF(f,\t\"#ifndef MENU\\n\"\n\t\t\t\t\t\t\t\"\\t#define MENU\\n\"\n\t\t\t\t\t\t\"#endif\\n\"\n\t\t\t\t\t\t);\n\telse\n\t\tVFS_PRINTF(f,\t\"#if !defined(CSQC) && !defined(NQSSQC) && !defined(QWSSQC)&& !defined(MENU)\\n\"\n\t\t\t\t\t\t\t\"\\t#ifdef QUAKEWORLD\\n\"\n\t\t\t\t\t\t\t\t\"\\t\\t#define QWSSQC\\n\"\n\t\t\t\t\t\t\t\"\\t#else\\n\"\n\t\t\t\t\t\t\t\t\"\\t\\t#define NQSSQC\\n\"\n\t\t\t\t\t\t\t\"\\t#endif\\n\"\n\t\t\t\t\t\t\"#endif\\n\"\n\t\t\t\t\t\t\"#if !defined(SSQC) && (defined(QWSSQC) || defined(NQSSQC))\\n\"\n\t\t\t\t\t\t\t\"\\t#define SSQC\\n\"\n\t\t\t\t\t\t\"#endif\\n\"\n\t\t\t\t\t\t);\n\n\n\tif (targ&(NQ|QW|H2))\n\t{\t//DEP_CSQC means deprecated in ssqc in favour of doing something else in csqc.\n\t\tVFS_PRINTF(f,\t\"#if defined(CSQC) || defined(MENU)\\n\"\n\t\t\t\t\t\t\t\"\\t#define DEP_CSQC DEP\\n\"\n\t\t\t\t\t\t\"#else\\n\"\n\t\t\t\t\t\t\t\"\\t#define DEP_CSQC __deprecated(\\\"Use CSQC for this\\\")\\n\"\n\t\t\t\t\t\t\"#endif\\n\"\n\t\t\t\t\t\t);\n\t}\n\telse\n\t\tVFS_PRINTF(f, \"#define DEP_CSQC DEP\\n\");\n\tif (targ&(NQ|QW|H2))\n\t{\t//DEP_SSQC means deprecated in ssqc only, but NOT in the CSQC.\n\t\tVFS_PRINTF(f,\t\"#if defined(SSQC)\\n\"\n\t\t\t\t\t\t\t\"\\t#define DEP_SSQC DEP\\n\"\n\t\t\t\t\t\t\"#else\\n\"\n\t\t\t\t\t\t\t\"\\t#define DEP_SSQC(reason)\\n\"\n\t\t\t\t\t\t\"#endif\\n\"\n\t\t\t\t\t\t);\n\t}\n\telse\n\t\tVFS_PRINTF(f, \"#define DEP_SSQC(reason)\\n\");\n\tVFS_PRINTF(f,\t\"#ifndef DEP\\n\"\n\t\t\t\t\t\t\"\\t#define DEP __deprecated //predefine this if you want to avoid our deprecation warnings.\\n\"\n\t\t\t\t\t\"#endif\\n\"\n\t\t\t\t\t\"#ifndef FTEDEP\\n\"\n\t\t\t\t\t\t\"\\t#define FTEDEP(reason) //for symbols deprecated in FTE that may still be useful/required for other engines\\n\"\n\t\t\t\t\t\"#endif\\n\");\n\n\tif (depfilter)\n\t\tPR_DumpPlatform_LoadSymbolTables(f, symtab, targ);\n\n\tfor (i = 0; i < QSG_Extensions_count; i++)\n\t{\n\t\tif (!QSG_Extensions[i].name || *QSG_Extensions[i].name == '?' || *QSG_Extensions[i].name == '_')\n\t\t\tcontinue;\t//FIXME!\n\n\t\tif (QSG_Extensions[i].description)\n\t\t\tVFS_PRINTF(f, \"#define %s /* %s */\\n\", QSG_Extensions[i].name, QSG_Extensions[i].description);\n\t\telse\n\t\t\tVFS_PRINTF(f, \"#define %s\\n\", QSG_Extensions[i].name);\n\t}\n\n\tVFS_PRINTF(f, \"\\n\");\n\n\tif (accessors)\n\t\tVFS_PRINTF(f, \"#define _ACCESSORS;\\n\");\n\t\n\tVFS_PRINTF(f, \n\t\t\t\"#ifdef _ACCESSORS\\n\"\n\t\t\t\t\"accessor strbuf : float;\\n\"\n\t\t\t\t\"accessor searchhandle : float;\\n\"\n\t\t\t\t\"accessor hashtable : float;\\n\"\n\t\t\t\t\"accessor infostring : string;\\n\"\n\t\t\t\t\"accessor filestream : float;\\n\"\n\t\t\t\t\"accessor filestream : float;\\n\"\n\t\t\t\"#else\\n\"\n\t\t\t\t\"#define strbuf float\\n\"\n\t\t\t\t\"#define searchhandle float\\n\"\n\t\t\t\t\"#define hashtable float\\n\"\n\t\t\t\t\"#define infostring string\\n\"\n\t\t\t\t\"#define filestream float\\n\"\n\t\t\t\"#endif\\n\"\n\t\t);\n\tVFS_PRINTF(f, \"\\n\");\n\n\n\tfor (idx = 0; knowndefs[idx].name; idx++)\n\t{\t//system fields\n\t\tif (!strcmp(knowndefs[idx].name, \"end_sys_fields\"))\n\t\t\tbreak;\n\t}\n\tfor (i = 0; knowndefs[i].name; i++)\n\t{\n\t\tfor (j = 0; j < i; j++)\n\t\t{\n\t\t\tif (!strcmp(knowndefs[i].name, knowndefs[j].name))\n\t\t\t{\t//promote the first one to the relevant module...\n\t\t\t\tif (j >= idx)\n\t\t\t\tif (!strcmp(knowndefs[i].type, knowndefs[j].type))\n\t\t\t\tif (knowndefs[i].desc==knowndefs[j].desc||!knowndefs[i].desc||!knowndefs[j].desc||!strcmp(knowndefs[i].desc, knowndefs[j].desc))\n\t\t\t\tif (knowndefs[i].valuestr==knowndefs[j].valuestr||(knowndefs[i].valuestr&&knowndefs[j].valuestr&&!strcmp(knowndefs[i].valuestr, knowndefs[j].valuestr)))\n\t\t\t\tif (knowndefs[i].value==knowndefs[j].value)\n\t\t\t\t{\n\t\t\t\t\tknowndefs[j].module |= knowndefs[i].module; /*give the first def the later one's module flag*/\n\t\t\t\t}\n\t\t\t\tknowndefs[i].module &= ~knowndefs[j].module; /*clear the flag on the later dupe def*/\n\t\t\t}\n\t\t}\n\t}\n\n\td = ALL & ~targ;\n\tfor (i = 0; knowndefs[i].name; i++)\n\t{\n\t\tconst char *type = knowndefs[i].type;\n\t\tnd = (knowndefs[i].module & targ) | (~targ & ALL);\n\t\tif (!(nd & targ))\n\t\t\tcontinue;\n\t\tif ((knowndefs[i].module & targ) & ID1)\n\t\t{\n\t\t\tif (!strcmp(knowndefs[i].name, \"ClientConnect\"))\n\t\t\t\ttype = \"void()\";\n\t\t\telse\n\t\t\t\tcontinue;\n\t\t}\n\t\tif (!PR_DumpPlatform_GenIfdef(f, targ, nd, &d))\n\t\t\tcontinue;\n\t\tif (knowndefs[i].desc)\n\t\t{\n\t\t\tif (!strncmp(knowndefs[i].desc, \"stub. \", 6))\n\t\t\t\tcontinue;\t//mostly for items2. :(\n\t\t\tif (!strncmp(type, \"//\", 2))\n\t\t\t\tcomment = va(\"\\n/* %s */\", knowndefs[i].desc);\n\t\t\telse\n\t\t\t\tcomment = va(\"\\t/* %s */\", knowndefs[i].desc);\n\t\t}\n\t\telse\n\t\t\tcomment = \"\";\n\t\tif (!strcmp(type, \"const float\"))\n\t\t{\n\t\t\tif (knowndefs[i].value >= (1<<23))\n\t\t\t{\n\t\t\t\tif (defines)\n\t\t\t\t\tVFS_PRINTF(f, \"#define %s %i%s\\n\", knowndefs[i].name, (int)knowndefs[i].value, comment);\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tPR_DumpPlatform_SymbolType(f, symtab, type, knowndefs[i].name);\n\t\t\t\t\tVFS_PRINTF(f, \"%s = %i;%s\\n\", knowndefs[i].name, (int)knowndefs[i].value, comment);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (defines)\n\t\t\t\t\tVFS_PRINTF(f, \"#define %s %g%s\\n\", knowndefs[i].name, knowndefs[i].value, comment);\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tPR_DumpPlatform_SymbolType(f, symtab, type, knowndefs[i].name);\n\t\t\t\t\tVFS_PRINTF(f, \"%s = %g;%s\\n\", knowndefs[i].name, knowndefs[i].value, comment);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse if (!strcmp(type, \"const string\"))\n\t\t{\n\t\t\tif (defines)\n\t\t\t\tVFS_PRINTF(f, \"#define %s %s%s\\n\", knowndefs[i].name, knowndefs[i].valuestr, comment);\n\t\t\telse\n\t\t\t{\n\t\t\t\tPR_DumpPlatform_SymbolType(f, symtab, type, knowndefs[i].name);\n\t\t\t\tVFS_PRINTF(f, \"%s = %s;%s\\n\", knowndefs[i].name, knowndefs[i].valuestr, comment);\n\t\t\t}\n\t\t}\n\t\telse if (knowndefs[i].valuestr)\n\t\t{\n\t\t\tPR_DumpPlatform_SymbolType(f, symtab, type, knowndefs[i].name);\n\t\t\tVFS_PRINTF(f, \"%s = %s;%s\\n\", knowndefs[i].name, knowndefs[i].valuestr, comment);\n\t\t}\n\t\telse if (knowndefs[i].value)\n\t\t{\n\t\t\tPR_DumpPlatform_SymbolType(f, symtab, type, knowndefs[i].name);\n\t\t\tVFS_PRINTF(f, \"%s = %g;%s\\n\", knowndefs[i].name, knowndefs[i].value, comment);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tPR_DumpPlatform_SymbolType(f, symtab, type, knowndefs[i].name);\n\t\t\tVFS_PRINTF(f, \"%s;%s\\n\", knowndefs[i].name, comment);\n\t\t}\n\t}\n\tfor (i = 0; BuiltinList[i].name; i++)\n\t{\n\t\tif (BuiltinList[i].obsolete)\n\t\t\tcontinue;\n\t\tidx = 0;\n\t\tif (BuiltinList[i].ebfsnum)\n\t\t\tidx = BuiltinList[i].ebfsnum;\n\t\telse if (BuiltinList[i].nqnum)\n\t\t\tidx = BuiltinList[i].nqnum;\n\t\telse if (BuiltinList[i].qwnum)\n\t\t\tidx = BuiltinList[i].qwnum;\n\t\telse if (BuiltinList[i].h2num)\n\t\t\tidx = BuiltinList[i].h2num;\n\t\telse\n\t\t\tidx = 0;\n\n\t\tnd = 0;\n\n\t\tif (BuiltinList[i].bifunc != PF_Fixme && BuiltinList[i].bifunc != PF_Ignore)\n\t\t{\n\t\t\tif (!idx)\t//no index is a dynamically linked builtin, and can thus be usable in any gamecode mode (so long as its ssqc anyway)\n\t\t\t\tnd |= NQ|QW|H2;\n\t\t\tif (BuiltinList[i].ebfsnum == idx)\n\t\t\t\tnd |= NQ|QW;\n\t\t\tif (BuiltinList[i].nqnum == idx)\n\t\t\t\tnd |= NQ;\n\t\t\tif (BuiltinList[i].qwnum == idx)\n\t\t\t\tnd |= QW;\n\t\t\tif (BuiltinList[i].h2num == idx)\n\t\t\t\tnd |= H2;\n\t\t}\n\n#ifdef CSQC_DAT\n\t\tif (PR_CSQC_BuiltinValid(BuiltinList[i].name, idx))\n\t\t\tnd |= CS;\n#endif\n#ifdef MENU_DAT\n\t\tif (MP_BuiltinValid(BuiltinList[i].name, idx))\n\t\t\tnd |= MENU;\n#endif\n\n\t\tif (targ & ID1)\n\t\t{\n\t\t\t//both engines have these defs, but fte's version has extra args, or added non-void return types\n\t\t\t//so to avoid compile errors/warnings, we omit our versions.\n\t\t\tconst char *buggyvanillabuiltins[] = {\n\t\t\t\t\"sound\",\"vectoyaw\",\"findradius\",\"bprint\",\"sprint\",\n\t\t\t\t\"walkmove\",\"droptofloor\",\"lightstyle\",\"vectoangles\",\"changelevel\",\"centerprint\"\n\t\t\t};\n\t\t\tfor (j = 0; j < countof(buggyvanillabuiltins) && nd; j++)\n\t\t\t{\n\t\t\t\tif (!strcmp(buggyvanillabuiltins[j], BuiltinList[i].name))\n\t\t\t\t\tnd = 0;\n\t\t\t}\n\t\t}\n\t\tif (targ & DPX)\n\t\t{\n\t\t\t//both engines have these defs, but fte's version has extra args, or added non-void return types\n\t\t\t//so to avoid compile errors/warnings, we omit our versions.\n\t\t\tconst char *buggydpbuiltins[] = {\n\t\t\t\t\"copyentity\",\"callfunction\",\"parseentitydata\",\"findfloat\",\"trailparticles\",\n\t\t\t\t\"findchain\",\"findchainflags\",\"findchainfloat\",\"log\",\"whichpack\",\"uri_get\",\"te_gunshot\",\"fopen\",\"fputs\",\"skel_create\",\"skel_build\",\"skel_set_bone\",\n\t\t\t\t\"infoadd\",\"strcmp\",\"strncmp\",\"strncasecmp\",\"strcat\"\n\t\t\t};\n\t\t\tfor (j = 0; j < countof(buggydpbuiltins) && nd; j++)\n\t\t\t{\n\t\t\t\tif (!strcmp(buggydpbuiltins[j], BuiltinList[i].name))\n\t\t\t\t\tnd = 0;\n\t\t\t}\n\t\t}\n\t\t\n\t\tif (!nd)\t/*no idea what its for*/\n\t\t\tcontinue;\n\t\tnd |= (~targ & ALL);\n\t\tif (!PR_DumpPlatform_GenIfdef(f, targ, nd, &d))\n\t\t\tcontinue;\n\n\t\tif (BuiltinList[i].obsolete)\n\t\t\tVFS_PRINTF(f, \"//\");\n\t\tPR_DumpPlatform_SymbolType(f, symtab, BuiltinList[i].prototype, BuiltinList[i].name);\n\t\tif (idx)\n\t\t\tVFS_PRINTF(f, \"%s = #%u;\", BuiltinList[i].name, idx);\n\t\telse\n\t\t\tVFS_PRINTF(f, \"%s = #%u:%s;\", BuiltinList[i].name, idx, BuiltinList[i].name);\n\t\tnd = 0;\n\t\tfor (j = 0; j < QSG_Extensions_count; j++)\n\t\t{\n\t\t\tfor (k = 0; k < countof(QSG_Extensions[j].builtinnames) && QSG_Extensions[j].builtinnames[k]; k++)\n\t\t\t{\n\t\t\t\tif (!strcmp(QSG_Extensions[j].builtinnames[k], BuiltinList[i].name))\n\t\t\t\t{\n\t\t\t\t\tif (!nd)\n\t\t\t\t\t\tVFS_PRINTF(f, \" /* Part of \");\n\t\t\t\t\telse\n\t\t\t\t\t\tVFS_PRINTF(f, \", \");\n\t\t\t\t\tnd++;\n\t\t\t\t\tVFS_PRINTF(f, \"%s\", QSG_Extensions[j].name);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (BuiltinList[i].biglongdesc)\n\t\t{\n\t\t\tchar *line = BuiltinList[i].biglongdesc;\n\t\t\tchar *term;\n\t\t\tif (!nd)\n\t\t\t\tVFS_PRINTF(f, \" /*\");\n\t\t\twhile(*line)\n\t\t\t{\n\t\t\t\tVFS_PRINTF(f, \"\\n\\t\\t\");\n\t\t\t\tterm = line;\n\t\t\t\twhile(*term && *term != '\\n')\n\t\t\t\t\tterm++;\n\t\t\t\tVFS_WRITE(f, line, term - line);\n\t\t\t\tif (*term == '\\n')\n\t\t\t\t{\n\t\t\t\t\tterm++;\n\t\t\t\t}\n\t\t\t\tline = term;\n\t\t\t}\n\t\t\tVFS_PRINTF(f, \" */\\n\\n\");\n\t\t}\n\t\telse if (nd)\n\t\t\tVFS_PRINTF(f, \"*/\\n\");\n\t\telse\n\t\t\tVFS_PRINTF(f, \"\\n\");\n\t}\n\tPR_DumpPlatform_GenIfdef(f, targ, ALL, &d);\n\n#if defined(CSQC_DAT) || defined(MENU_DAT)\n\tif (targ & (CS|MENU))\n\t{\n\t\tVFS_PRINTF(f, \"#if defined(CSQC) || defined(MENU)\\n\");\n\t\tKey_PrintQCDefines(f, defines);\n\t\tVFS_PRINTF(f, \"#endif\\n\");\n\t}\n#endif\n\n\tVFS_PRINTF(f, \"#ifdef _ACCESSORS\\n\");\n\tVFS_PRINTF(f,\n\t\t\"accessor strbuf : float\\n{\\n\"\n\t\t\t\"\\tinline get float asfloat[float idx] = {return stof(bufstr_get(this, idx));};\\n\"\n\t\t\t\"\\tinline set float asfloat[float idx] = {bufstr_set(this, idx, ftos(value));};\\n\"\n\t\t\t\"\\tget string[float] = bufstr_get;\\n\"\n\t\t\t\"\\tset string[float] = bufstr_set;\\n\"\n\t\t\t\"\\tget float length = buf_getsize;\\n\"\n\t\t\"};\\n\");\n\tVFS_PRINTF(f,\n\t\t\"accessor searchhandle : float\\n{\\n\"\n\t\t\t\"\\tget string[float] = search_getfilename;\\n\"\n\t\t\t\"\\tget float length = search_getsize;\\n\"\n\t\t\"};\\n\");\n\tVFS_PRINTF(f,\n\t\t\"accessor hashtable : float\\n{\\n\"\n\t\t\t\"\\tinline get vector v[string key] = {return hash_get(this, key, '0 0 0', EV_VECTOR);};\\n\"\n\t\t\t\"\\tinline set vector v[string key] = {hash_add(this, key, value, HASH_REPLACE|EV_VECTOR);};\\n\"\n\t\t\t\"\\tinline get string s[string key] = {return hash_get(this, key, \\\"\\\", EV_STRING);};\\n\"\n\t\t\t\"\\tinline set string s[string key] = {hash_add(this, key, value, HASH_REPLACE|EV_STRING);};\\n\"\n\t\t\t\"\\tinline get float f[string key] = {return hash_get(this, key, 0.0, EV_FLOAT);};\\n\"\n\t\t\t\"\\tinline set float f[string key] = {hash_add(this, key, value, HASH_REPLACE|EV_FLOAT);};\\n\"\n\t\t\t\"\\tinline get __variant[string key] = {return hash_get(this, key, __NULL__);};\\n\"\n\t\t\t\"\\tinline set __variant[string key] = {hash_add(this, key, value, HASH_REPLACE);};\\n\"\n\t\t\"};\\n\");\n\tVFS_PRINTF(f,\n\t\t\"accessor infostring : string\\n{\\n\"\n\t\t\t\"\\tget string[string] = infoget;\\n\"\n#ifdef QCGC\n\t\t\t\"\\tinline set& string[string fld] = {this = infoadd(this, fld, value);};\\n\"\n#endif\n\t\t\"};\\n\");\n\tVFS_PRINTF(f,\n\t\t\"accessor filestream : float\\n{\\n\"\n\t\t\t\"\\tget string = fgets;\\n\"\n\t\t\t\"\\tinline set string = {fputs(this,value);};\\n\"\n\t\t\"};\\n\");\n\tVFS_PRINTF(f, \"#endif\\n\");\n\n\tVFS_PRINTF(f,\n\t\t\"accessor jsonnode : json_t\\n{\\n\"\n\t\t\t\"\\tinline get json_type_e type = json_get_value_type;\\n\"\n\t\t\t\"\\tinline get string s = json_get_string;\\n\"\n\t\t\t\"\\tinline get float f = json_get_float;\\n\"\n\t\t\t\"\\tinline get __int i = json_get_integer;\\n\"\n\t\t\t\"\\tinline get __int length = json_get_length;\\n\"\n\t\t\t\"\\tinline get jsonnode a[__int key] = json_get_child_at_index;\\n\"\t//FIXME: remove this name when fteqcc can cope with dupes, for array[idx]\n\t\t\t\"\\tinline get jsonnode[string key] = json_find_object_child;\\n\"\n\t\t\t\"\\tinline get string name = json_get_name;\\n\"\n\t\t\"};\\n\");\n\n\tVFS_PRINTF(f, \"#undef DEP_CSQC\\n\");\n\tVFS_PRINTF(f, \"#undef FTEDEP\\n\");\n\tVFS_PRINTF(f, \"#undef DEP\\n\");\n\tVFS_PRINTF(f, \"#pragma noref 0\\n\");\n\n\tVFS_CLOSE(f);\n\n\tCon_Printf(\"Written \\\"%s\\\"\\n\", dbgfname);\n#endif\n}\n\n#endif\n"
  },
  {
    "path": "engine/server/pr_lua.c",
    "content": "#include \"quakedef.h\"\r\n\r\n#ifdef VM_LUA\r\n\r\n#include \"pr_common.h\"\r\n#include \"hash.h\"\r\n\r\n#define LUA_MALLOC_TAG 0x55780128\r\n\r\n#define luagloballist\t\\\r\n\tglobalentity\t(true, self)\t\\\r\n\tglobalentity\t(true, other)\t\\\r\n\tglobalentity\t(true, world)\t\\\r\n\tglobalfloat\t\t(true, time)\t\\\r\n\tglobalfloat\t\t(true, frametime)\t\\\r\n\tglobalentity\t(false, newmis)\t\\\r\n\tglobalfloat\t\t(false, force_retouch)\t\\\r\n\tglobalstring\t(true, mapname)\t\\\r\n\tglobalfloat\t\t(false, deathmatch)\t\\\r\n\tglobalfloat\t\t(false, coop)\t\\\r\n\tglobalfloat\t\t(false, teamplay)\t\\\r\n\tglobalfloat\t\t(true, serverflags)\t\\\r\n\tglobalfloat\t\t(false, dimension_send)\t\\\r\n\tglobalfloat\t\t(false, physics_mode)\t\\\r\n\tglobalfloat\t\t(true, total_secrets)\t\\\r\n\tglobalfloat\t\t(true, total_monsters)\t\\\r\n\tglobalfloat\t\t(true, found_secrets)\t\\\r\n\tglobalfloat\t\t(true, killed_monsters)\t\\\r\n\tglobalvec\t\t(true, v_forward)\t\\\r\n\tglobalvec\t\t(true, v_up)\t\\\r\n\tglobalvec\t\t(true, v_right)\t\\\r\n\tglobalfloat\t\t(true, trace_allsolid)\t\\\r\n\tglobalfloat\t\t(true, trace_startsolid)\t\\\r\n\tglobalfloat\t\t(true, trace_fraction)\t\\\r\n\tglobalvec\t\t(true, trace_endpos)\t\\\r\n\tglobalvec\t\t(true, trace_plane_normal)\t\\\r\n\tglobalfloat\t\t(true, trace_plane_dist)\t\\\r\n\tglobalentity\t(true, trace_ent)\t\\\r\n\tglobalfloat\t\t(true, trace_inopen)\t\\\r\n\tglobalfloat\t\t(true, trace_inwater)\t\\\r\n\tglobalfloat\t\t(false, trace_endcontentsf)\t\\\r\n\tglobalint\t\t(false, trace_endcontentsi)\t\\\r\n\tglobalfloat\t\t(false, trace_surfaceflagsf)\t\\\r\n\tglobalint\t\t(false, trace_surfaceflagsi)\t\\\r\n\tglobalfloat\t\t(false, cycle_wrapped)\t\\\r\n\tglobalentity\t(false, msg_entity)\t\\\r\n\tglobalfunc\t\t(false, main)\t\\\r\n\tglobalfunc\t\t(true, StartFrame)\t\\\r\n\tglobalfunc\t\t(true, PlayerPreThink)\t\\\r\n\tglobalfunc\t\t(true, PlayerPostThink)\t\\\r\n\tglobalfunc\t\t(true, ClientKill)\t\\\r\n\tglobalfunc\t\t(true, ClientConnect)\t\\\r\n\tglobalfunc\t\t(true, PutClientInServer)\t\\\r\n\tglobalfunc\t\t(true, ClientDisconnect)\t\\\r\n\tglobalfunc\t\t(false, SetNewParms)\t\\\r\n\tglobalfunc\t\t(false, SetChangeParms)\t\\\r\n\tglobalfloat\t\t(false, dimension_default)\t\\\r\n\tglobalvec\t\t(false, global_gravitydir)\r\n\r\n//any globals or functions that the server might want access to need to be known also.\r\n#define luaextragloballist\t\\\r\n\tglobalstring\t(true, startspot)\t\\\r\n\tglobalfunc\t\t(true, ClientReEnter) \r\n\r\ntypedef struct\r\n{\r\n#define globalentity(required, name) int name;\r\n#define globalint(required, name) int name;\r\n#define globalfloat(required, name) float name;\r\n#define globalstring(required, name) string_t name;\r\n#define globalvec(required, name) vec3_t name;\r\n#define globalfunc(required, name) int name;\r\nluagloballist\r\nluaextragloballist\r\n#undef globalentity\r\n#undef globalint\r\n#undef globalfloat\r\n#undef globalstring\r\n#undef globalvec\r\n#undef globalfunc\r\n\r\n\tfloat parm[NUM_SPAWN_PARMS];\r\n} luaglobalvars_t;\r\n\r\ntypedef struct\r\n{\r\n\tint type;\r\n\tqintptr_t offset;\r\n\tchar *name;\r\n\tbucket_t buck;\r\n} luafld_t;\r\n\r\n//#define LIBLUA_STATIC\r\n\r\n#ifdef LIBLUA_STATIC\r\n\t#ifdef _MSC_VER\r\n\t\t#pragma comment(lib, \"liblua.lib\")\r\n\t#endif\r\n\t#include <lua.h>\r\n\t//#include <lualib.h>\r\n\t#include <lauxlib.h>\r\n#else\r\ntypedef struct lua_State lua_State;\r\ntypedef void *(QDECL *lua_Alloc) (void *ud, void *ptr, size_t osize, size_t nsize);\r\ntypedef const char *(QDECL *lua_Reader)(lua_State *L, void *data, size_t *size);\r\ntypedef int (QDECL *lua_CFunction) (lua_State *L);\r\ntypedef double lua_Number;\r\ntypedef long long lua_Integer;\r\n\r\n\r\n#define lua_pcall(L,n,r,f)\tlua_pcallk(L, (n), (r), (f), 0, NULL)\r\n#define lua_call(L,n,r)\t\tlua_callk(L, (n), (r), 0, NULL)\r\n#define lua_pop(L,n)\t\tlua_settop(L, -(n)-1)\r\n#define lua_pushstring(L,s)\tlua_pushfstring(L,\"%s\",s)\r\n\r\n//#define lua_remove(L,idx)\t(lua_rotate(L, (idx), -1), lua_pop(L, 1))\r\n#define lua_replace(L,idx)\t(lua_copy(L, -1, (idx)), lua_pop(L, 1))\r\n#define lua_pushglobaltable(L)  (lua_rawgeti(L, LUA_REGISTRYINDEX, LUA_RIDX_GLOBALS))\r\n#define luaL_getmetatable(L,n)\t(lua_getfield(L, LUA_REGISTRYINDEX, (n)))\r\n\r\n#define LUA_TNONE\t\t\t(-1)\r\n#define LUA_TNIL\t\t\t0\r\n#define LUA_TBOOLEAN\t\t1\r\n#define LUA_TLIGHTUSERDATA\t2\r\n#define LUA_TNUMBER\t\t\t3\r\n#define LUA_TSTRING\t\t\t4\r\n#define LUA_TTABLE\t\t\t5\r\n#define LUA_TFUNCTION\t\t6\r\n#define LUA_TUSERDATA\t\t7\r\n#define LUA_TTHREAD\t\t\t8\r\n#define LUA_NUMTAGS\t\t\t9\r\n\r\n#define LUA_RIDX_GLOBALS\t2\r\n\r\n#define LUA_REGISTRYINDEX (-1000000 - 1000)\r\n\r\n#define lua_newstate\t\tlua.newstate\r\n#define lua_atpanic\t\t\tlua.atpanic\r\n#define lua_close\t\t\tlua.close\r\n#define lua_load\t\t\tlua.load\r\n#define lua_pcallk\t\t\tlua.pcallk\r\n#define lua_callk\t\t\tlua.callk\r\n#define lua_getfield\t\tlua.getfield\r\n#define lua_setfield\t\tlua.setfield\r\n#define lua_gettable\t\tlua.gettable\r\n#define lua_settable\t\tlua.settable\r\n#define lua_getglobal\t\tlua.getglobal\r\n#define lua_setglobal\t\tlua.setglobal\t\r\n#define lua_error\t\t\tlua.error\r\n#define lua_type\t\t\tlua.type\r\n#define lua_typename\t\tlua.typename\r\n#define lua_rawget\t\t\tlua.rawget\r\n#define lua_rawgeti\t\t\tlua.rawgeti\r\n#define lua_rawgetp\t\t\tlua.rawgetp\r\n#define lua_rawset\t\t\tlua.rawset\r\n#define lua_createtable\t\tlua.createtable\r\n#define lua_setmetatable\tlua.setmetatable\r\n#define lua_newuserdata\t\tlua.newuserdata\r\n\r\n#define lua_copy\t\t\tlua.copy\r\n#define lua_gettop\t\t\tlua.gettop\r\n#define lua_settop\t\t\tlua.settop\r\n#define lua_pushboolean\t\tlua.pushboolean\r\n#define lua_pushnil\t\t\tlua.pushnil\r\n#define lua_pushnumber\t\tlua.pushnumber\r\n#define lua_pushinteger\t\tlua.pushinteger\r\n#define lua_pushvalue\t\tlua.pushvalue\r\n#define lua_pushcclosure\tlua.pushcclosure\r\n#define lua_pushfstring\t\tlua.pushfstring\r\n#define lua_pushliteral\t\tlua_pushstring\r\n#define lua_pushlightuserdata\tlua.pushlightuserdata\r\n#define lua_tolstring\t\tlua.tolstring\r\n#define lua_toboolean\t\tlua.toboolean\r\n#define lua_tonumberx\t\tlua.tonumberx\r\n#define lua_tointegerx\t\tlua.tointegerx\r\n#define lua_topointer\t\tlua.topointer\r\n#define lua_touserdata\t\tlua.touserdata\r\n#define lua_touserdata\t\tlua.touserdata\r\n#define lua_next\t\t\tlua.next\r\n//#define lua_remove\t\t\tlua.remove\r\n\r\n#define luaL_callmeta\t\tlua.Lcallmeta\r\n#define luaL_newmetatable\tlua.Lnewmetatable\r\n\r\n#define lua_newtable(L)\t\tlua_createtable(L,0,0)\r\n#define lua_pushcfunction(L,f) lua_pushcclosure(L,f,0)\r\n#define lua_isnil(L,i)\t\t(lua_type(L,i)==LUA_TNIL)\r\n#define lua_tostring(L,i)\tlua_tolstring(L,i,NULL)\r\n#define lua_tonumber(L,i)\tlua_tonumberx(L,i,NULL)\r\n#define lua_tointeger(L,i)\tlua_tointegerx(L,i,NULL)\r\n#endif\r\n\r\n//I'm using this struct for all the global stuff.\r\nstatic struct\r\n{\r\n\tlua_State *ctx;\r\n\tchar readbuf[1024];\r\n\tpubprogfuncs_t progfuncs;\r\n\tprogexterns_t progfuncsparms;\r\n\tedict_t **edicttable;\r\n\tunsigned int maxedicts;\r\n\r\n\tluaglobalvars_t globals;\t//internal global structure\r\n\thashtable_t globalfields;\t//name->luafld_t\r\n\tluafld_t globflds[1024];\t//fld->offset+type\r\n\r\n\thashtable_t entityfields;\t//name->luafld_t\r\n\tluafld_t entflds[1024];\t\t//fld->offset+type\r\n\tsize_t numflds;\r\n\r\n#ifndef LIBLUA_STATIC\r\n\tqboolean triedlib;\r\n\tdllhandle_t *lib;\r\n\r\n\tlua_State *\t\t(QDECL *newstate)\t\t(lua_Alloc f, void *ud);\r\n\tlua_CFunction\t(QDECL *atpanic)\t\t(lua_State *L, lua_CFunction panicf);\r\n\tvoid\t\t\t(QDECL *close)\t\t\t(lua_State *L);\r\n\tint\t\t\t\t(QDECL *load)\t\t\t(lua_State *L, lua_Reader reader, void *dt, const char *chunkname, const char *mode);\r\n\tint\t\t\t\t(QDECL *pcallk)\t\t\t(lua_State *L, int nargs, int nresults, int errfunc, int ctx, lua_CFunction k);\r\n\tvoid\t\t\t(QDECL *callk)\t\t\t(lua_State *L, int nargs, int nresults,\t\t\t\t int ctx, lua_CFunction k);\r\n\tvoid\t\t\t(QDECL *getfield)\t\t(lua_State *L, int idx, const char *k);\r\n\tvoid\t\t\t(QDECL *setfield)\t\t(lua_State *L, int idx, const char *k);\r\n\tvoid\t\t\t(QDECL *gettable)\t\t(lua_State *L, int idx);\r\n\tvoid\t\t\t(QDECL *settable)\t\t(lua_State *L, int idx);\r\n\tvoid\t\t\t(QDECL *getglobal)\t\t(lua_State *L, const char *var);\r\n\tvoid\t\t\t(QDECL *setglobal)\t\t(lua_State *L, const char *var);\r\n\tint\t\t\t\t(QDECL *error)\t\t\t(lua_State *L);\r\n\tint\t\t\t\t(QDECL *type)\t\t\t(lua_State *L, int idx);\r\n\tconst char     *(QDECL *typename)\t\t(lua_State *L, int tp);\r\n\tint\t\t\t\t(QDECL *rawget)\t\t\t(lua_State *L, int idx);\r\n\tint\t\t\t\t(QDECL *rawgeti)\t\t(lua_State *L, int idx, lua_Integer n);\r\n\tint\t\t\t\t(QDECL *rawgetp)\t\t(lua_State *L, int idx, const void *p);\r\n\tvoid\t\t\t(QDECL *rawset)\t\t\t(lua_State *L, int idx);\r\n\tvoid\t\t\t(QDECL *createtable)\t(lua_State *L, int narr, int nrec);\r\n\tint\t\t\t\t(QDECL *setmetatable)\t(lua_State *L, int objindex);\r\n\tvoid\t\t   *(QDECL *newuserdata)\t(lua_State *L, size_t usize);\r\n\r\n\tvoid\t\t\t(QDECL *copy)\t\t\t(lua_State *L, int fromidx, int toidx);\t//added in 5.3\r\n//\tvoid\t\t\t(QDECL *replace)\t\t(lua_State *L, int idx);\t\t\t\t//removed in 5.3\r\n\tint\t\t\t\t(QDECL *gettop)\t\t\t(lua_State *L);\r\n\tint\t\t\t\t(QDECL *settop)\t\t\t(lua_State *L, int idx);\r\n\tvoid\t\t\t(QDECL *pushboolean)\t(lua_State *L, int b);\r\n\tvoid\t\t\t(QDECL *pushnil)\t\t(lua_State *L);\r\n\tvoid\t\t\t(QDECL *pushnumber)\t\t(lua_State *L, lua_Number n);\r\n\tvoid\t\t\t(QDECL *pushinteger)\t(lua_State *L, lua_Integer n);\r\n\tvoid\t\t\t(QDECL *pushvalue)\t\t(lua_State *L, int idx);\r\n\tvoid\t\t\t(QDECL *pushcclosure)\t(lua_State *L, lua_CFunction fn, int n);\r\n\tconst char *\t(QDECL *pushfstring)\t(lua_State *L, const char *fmt, ...);\r\n\tvoid\t\t\t(QDECL *pushlightuserdata)\t(lua_State *L, void *p);\r\n\tconst char *\t(QDECL *tolstring)\t\t(lua_State *L, int idx, size_t *len);\r\n\tint             (QDECL *toboolean)\t\t(lua_State *L, int idx);\r\n\tlua_Number      (QDECL *tonumberx)\t\t(lua_State *L, int idx, int *isnum);\r\n\tlua_Integer     (QDECL *tointegerx)\t\t(lua_State *L, int idx, int *isnum);\r\n\tconst void     *(QDECL *topointer)\t\t(lua_State *L, int idx);\r\n\tvoid\t\t   *(QDECL *touserdata)\t\t(lua_State *L, int idx);\r\n\tint\t\t\t\t(QDECL *next)\t\t\t(lua_State *L, int idx);\r\n//\tvoid\t\t\t(QDECL *remove)\t\t\t(lua_State *L, int idx);\r\n\r\n\tint\t\t\t\t(QDECL *Lcallmeta)\t\t(lua_State *L, int obj, const char *e);\r\n\tint\t\t\t\t(QDECL *Lnewmetatable)\t(lua_State *L, const char *tname);\r\n#endif\r\n} lua;\r\n\r\nstatic qboolean init_lua(void)\r\n{\r\n#ifndef LIBLUA_STATIC\r\n\tif (!lua.triedlib)\r\n\t{\r\n\t\tdllfunction_t luafuncs[] =\r\n\t\t{\r\n\t\t\t{(void*)&lua.newstate,\t\t\"lua_newstate\"},\r\n\t\t\t{(void*)&lua.atpanic,\t\t\"lua_atpanic\"},\r\n\t\t\t{(void*)&lua.close,\t\t\t\"lua_close\"},\r\n\t\t\t{(void*)&lua.load,\t\t\t\"lua_load\"},\r\n\t\t\t{(void*)&lua.pcallk,\t\t\"lua_pcallk\"},\r\n\t\t\t{(void*)&lua.callk,\t\t\t\"lua_callk\"},\r\n\t\t\t{(void*)&lua.getfield,\t\t\"lua_getfield\"},\r\n\t\t\t{(void*)&lua.setfield,\t\t\"lua_setfield\"},\r\n\t\t\t{(void*)&lua.gettable,\t\t\"lua_gettable\"},\r\n\t\t\t{(void*)&lua.settable,\t\t\"lua_settable\"},\r\n\t\t\t{(void*)&lua.getglobal,\t\t\"lua_getglobal\"},\r\n\t\t\t{(void*)&lua.setglobal,\t\t\"lua_setglobal\"},\r\n\t\t\t{(void*)&lua.error,\t\t\t\"lua_error\"},\r\n\t\t\t{(void*)&lua.type,\t\t\t\"lua_type\"},\r\n\t\t\t{(void*)&lua.typename,\t\t\"lua_typename\"},\r\n\t\t\t{(void*)&lua.rawget,\t\t\"lua_rawget\"},\r\n\t\t\t{(void*)&lua.rawgeti,\t\t\"lua_rawgeti\"},\r\n\t\t\t{(void*)&lua.rawgetp,\t\t\"lua_rawgetp\"},\r\n\t\t\t{(void*)&lua.rawset,\t\t\"lua_rawset\"},\r\n\t\t\t{(void*)&lua.createtable,\t\"lua_createtable\"},\r\n\t\t\t{(void*)&lua.setmetatable,\t\"lua_setmetatable\"},\r\n\t\t\t{(void*)&lua.newuserdata,\t\"lua_newuserdata\"},\r\n\t\r\n\t\t\t{(void*)&lua.copy,\t\t\t\"lua_copy\"},\r\n//\t\t\t{(void*)&lua.replace,\t\t\"lua_replace\"},\r\n\t\t\t{(void*)&lua.gettop,\t\t\"lua_gettop\"},\r\n\t\t\t{(void*)&lua.settop,\t\t\"lua_settop\"},\r\n\t\t\t{(void*)&lua.pushboolean,\t\"lua_pushboolean\"},\r\n\t\t\t{(void*)&lua.pushnil,\t\t\"lua_pushnil\"},\r\n\t\t\t{(void*)&lua.pushnumber,\t\"lua_pushnumber\"},\r\n\t\t\t{(void*)&lua.pushinteger,\t\"lua_pushinteger\"},\r\n\t\t\t{(void*)&lua.pushvalue,\t\t\"lua_pushvalue\"},\r\n\t\t\t{(void*)&lua.pushcclosure,\t\"lua_pushcclosure\"},\r\n\t\t\t{(void*)&lua.pushfstring,\t\"lua_pushfstring\"},\r\n\t\t\t{(void*)&lua.pushlightuserdata,\t\"lua_pushlightuserdata\"},\r\n\t\t\t{(void*)&lua.tolstring,\t\t\"lua_tolstring\"},\r\n\t\t\t{(void*)&lua.toboolean,\t\t\"lua_toboolean\"},\r\n\t\t\t{(void*)&lua.tonumberx,\t\t\"lua_tonumberx\"},\r\n\t\t\t{(void*)&lua.tointegerx,\t\"lua_tointegerx\"},\r\n\t\t\t{(void*)&lua.topointer,\t\t\"lua_topointer\"},\r\n\t\t\t{(void*)&lua.touserdata,\t\"lua_touserdata\"},\r\n\t\t\t{(void*)&lua.next,\t\t\t\"lua_next\"},\r\n//\t\t\t{(void*)&lua.remove,\t\t\"lua_remove\"},\r\n\r\n\t\t\t{(void*)&lua.Lcallmeta,\t\t\"luaL_callmeta\"},\r\n\t\t\t{(void*)&lua.Lnewmetatable,\t\"luaL_newmetatable\"},\r\n\r\n\t\t\t{NULL, NULL}\r\n\t\t};\r\n\t\tlua.triedlib = true;\r\n\t\tlua.lib = Sys_LoadLibrary(\"lua53\", luafuncs);\r\n\t}\r\n\tif (!lua.lib)\r\n\t\treturn false;\r\n#endif\r\n\treturn true;\r\n}\r\nchar *QDECL Lua_AddString(pubprogfuncs_t *prinst, const char *val, int minlength, pbool demarkup);\r\n\r\nstatic void lua_pushedict(lua_State *L, struct edict_s *ent)\r\n{\r\n\tlua_rawgetp(L, LUA_REGISTRYINDEX, ent);\r\n}\r\n\r\n/*\r\nstatic void lua_debugstack(lua_State *L)\r\n{\r\n\tint idx;\r\n\tint top = lua_gettop(L);\r\n\tfor (idx = 1; idx <= top; idx++)\r\n\t{\r\n\t\tif (luaL_callmeta(L, idx, \"__tostring\"))\r\n\t\t{\r\n\t\t\tCon_Printf(\"Stack%i: %s\\n\", idx, lua_tolstring(L, -1, NULL));\r\n\t\t\tlua_pop(L, 1);\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tint t = lua_type(L, idx);\r\n\t\t\tswitch (t)\r\n\t\t\t{\r\n\t\t\tcase LUA_TNUMBER:\r\n\t\t\t\tCon_Printf(\"Stack%i: %g\\n\", idx, lua_tonumber(L, idx));\r\n\t\t\t\tbreak;\r\n\t\t\tcase LUA_TSTRING:\r\n\t\t\t\tCon_Printf(\"Stack%i: %s\\n\", idx, lua_tolstring(L, idx, NULL));\r\n\t\t\t\tbreak;\r\n\t\t\tcase LUA_TBOOLEAN:\r\n\t\t\t\tCon_Printf(\"Stack%i: %s\\n\", idx, (lua_toboolean(L, idx) ? \"true\" : \"false\"));\r\n\t\t\t\tbreak;\r\n\t\t\tcase LUA_TNIL:\r\n\t\t\t\tCon_Printf(\"Stack%i: %s\\n\", idx, \"nil\");\r\n\t\t\t\tbreak;\r\n\t\t\tcase LUA_TTABLE:\r\n\t\t\t\t//special check for things that look like vectors.\r\n\t\t\t\tlua_getfield(L, idx, \"x\");\r\n\t\t\t\tlua_getfield(L, idx, \"y\");\r\n\t\t\t\tlua_getfield(L, idx, \"z\");\r\n\t\t\t\tif (lua_type(L, -3) == LUA_TNUMBER && lua_type(L, -2) == LUA_TNUMBER && lua_type(L, -1) == LUA_TNUMBER)\r\n\t\t\t\t{\r\n\t\t\t\t\tCon_Printf(\"Stack%i: '%f %f %f'\\n\", idx, lua_tonumberx(L, -3, NULL), lua_tonumberx(L, -2, NULL), lua_tonumberx(L, -1, NULL));\r\n\t\t\t\t\tlua_pop(L, 3);\r\n\t\t\t\t\tbreak;\r\n\t\t\t\t}\r\n\t\t\t\tlua_pop(L, 3);\r\n\t\t\tdefault:\r\n\t\t\t\tCon_Printf(\"Stack%i: %s: %p\\n\", idx, lua_typename(L, lua_type(L, idx)), lua_topointer(L, idx));\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n}\r\n*/\r\n\r\nstatic void *my_lua_alloc (void *ud, void *ptr, size_t osize, size_t nsize)\r\n{\r\n\tif (nsize == 0)\r\n\t{\r\n\t\tfree(ptr);\r\n\t\treturn NULL;\r\n\t}\r\n\telse\r\n\t\treturn realloc(ptr, nsize);\r\n}\r\nconst char * my_lua_Reader(lua_State *L, void *data, size_t *size)\r\n{\r\n\tvfsfile_t *f = data;\r\n\t*size = VFS_READ(f, lua.readbuf, sizeof(lua.readbuf));\r\n\treturn lua.readbuf;\r\n}\r\n\r\n//replace lua's standard 'print' function to use the console instead of stdout. intended to use the same linebreak rules.\r\nstatic int bi_lua_print(lua_State *L)\r\n{\r\n\t//the problem is that we can only really accept strings here.\r\n\t//so lets just use the tostring function to make sure things are actually readable as strings.\r\n\tint args = lua_gettop(L);\r\n\tint i;\r\n\tconst char *s;\r\n\tlua_getglobal(L, \"tostring\");\r\n\t//args now start at 1\r\n\tfor(i = 1; i <= args; i++)\r\n\t{\r\n\t\tlua_pushvalue(L, -1);\r\n\t\tlua_pushvalue(L, i);\r\n\t\tlua_pcall(L, 1, 1, 0);\t//pops args+func\r\n\t\ts = lua_tolstring(L, -1, NULL);\r\n\t\tif(s == NULL)\r\n\t\t\ts = \"?\";\r\n\r\n\t\tif(i > 1) Con_Printf(\"\\t\");\r\n\t\tCon_Printf(\"%s\", s);\r\n\t\tlua_pop(L, 1);\t\t\t//pop our lstring\r\n\t};\r\n\tlua_pop(L, 1);\t\t\t//pop the cached tostring.\r\n\tCon_Printf(\"\\n\");\r\n\treturn 0;\r\n};\r\n//more like quakec's print\r\nstatic int bi_lua_conprint(lua_State *L)\r\n{\r\n\t//the problem is that we can only really accept strings here.\r\n\t//so lets just use the tostring function to make sure things are actually readable as strings.\r\n\tint args = lua_gettop(L);\r\n\tint i;\r\n\tconst char *s;\r\n\r\n\tlua_getglobal(L, \"tostring\");\r\n\t//args start at stack index 1\r\n\tfor(i = 1; i <= args; i++)\r\n\t{\r\n\t\tlua_pushvalue(L, -1);\t//dupe the tostring\r\n\t\tlua_pushvalue(L, i);\t//dupe the argument\r\n\t\tlua_pcall(L, 1, 1, 0);\t//pops args+func, pushes the string result\r\n\t\ts = lua_tolstring(L, -1, NULL);\r\n\t\tif(s == NULL)\r\n\t\t\ts = \"?\";\r\n\r\n\t\tCon_Printf(\"%s\", s);\r\n\t\tlua_pop(L, 1);\t\t\t//pop our lstring\r\n\t};\r\n\tlua_pop(L, 1);\t\t\t//pop the cached tostring.\r\n\treturn 0;\r\n};\r\nstatic int bi_lua_dprint(lua_State *L)\r\n{\r\n\tif (!developer.ival)\r\n\t\treturn 0;\r\n\treturn bi_lua_conprint(L);\r\n}\r\n\r\n//taken from lua's baselib.c, with dependancies reduced a little.\r\nstatic int bi_lua_tostring(lua_State *L)\r\n{\r\n//\tif (lua_type(L, 1) == LUA_TNONE)\r\n//\t\tluaL_argerror(L, narg, \"value expected\");\r\n\tif (luaL_callmeta(L, 1, \"__tostring\"))\r\n\t\treturn 1;\r\n\tswitch (lua_type(L, 1))\r\n\t{\r\n\tcase LUA_TNUMBER:\r\n\t\tlua_pushfstring(L, lua_tolstring(L, 1, NULL));\r\n\t\tbreak;\r\n\tcase LUA_TSTRING:\r\n\t\tlua_pushvalue(L, 1);\r\n\t\tbreak;\r\n\tcase LUA_TBOOLEAN:\r\n\t\tlua_pushstring(L, (lua_toboolean(L, 1) ? \"true\" : \"false\"));\r\n\t\tbreak;\r\n\tcase LUA_TNIL:\r\n\t\tlua_pushstring(L, \"nil\");\r\n\t\tbreak;\r\n\tcase LUA_TTABLE:\r\n\t\t//special check for things that look like vectors.\r\n\t\tlua_getfield(L, 1, \"x\");\r\n\t\tlua_getfield(L, 1, \"y\");\r\n\t\tlua_getfield(L, 1, \"z\");\r\n\t\tif (lua_type(L, -3) == LUA_TNUMBER && lua_type(L, -2) == LUA_TNUMBER && lua_type(L, -1) == LUA_TNUMBER)\r\n\t\t{\r\n\t\t\tlua_pushfstring(L, \"'%g %g %g'\", lua_tonumberx(L, -3, NULL), lua_tonumberx(L, -2, NULL), lua_tonumberx(L, -1, NULL));\r\n\t\t\treturn 1;\r\n\t\t}\r\n\t\t//fallthrough\r\n\tdefault:\r\n\t\tlua_pushfstring(L, \"%s: %p\", lua_typename(L, lua_type(L, 1)), lua_topointer(L, 1));\r\n\t\tbreak;\r\n\t}\r\n\treturn 1;\r\n}\r\n#define bi_lua_vtos bi_lua_tostring\r\n#define bi_lua_ftos bi_lua_tostring\r\n\r\n//taken from lua's baselib.c, with dependancies reduced a little.\r\nstatic int bi_lua_tonumber(lua_State *L)\r\n{\r\n//\tif (lua_type(L, 1) == LUA_TNONE)\r\n//\t\tluaL_argerror(L, narg, \"value expected\");\r\n\tif (luaL_callmeta(L, 1, \"__tonumber\"))\r\n\t\treturn 1;\r\n\tswitch (lua_type(L, 1))\r\n\t{\r\n\tcase LUA_TSTRING:\r\n\t\tlua_pushnumber(L, atof(lua_tostring(L, 1)));\r\n\t\tbreak;\r\n\tcase LUA_TNUMBER:\r\n\t\tlua_pushvalue(L, 1);\r\n\t\tbreak;\r\n\tcase LUA_TBOOLEAN:\r\n\t\tlua_pushnumber(L, lua_toboolean(L, 1));\r\n\t\tbreak;\r\n\tcase LUA_TNIL:\r\n\t\tlua_pushnumber(L, 0);\r\n\t\tbreak;\r\n\tcase LUA_TTABLE:\r\n\t\t//special check for things that look like vectors.\r\n\t\tlua_getfield(L, 1, \"x\");\r\n\t\tlua_getfield(L, 1, \"y\");\r\n\t\tlua_getfield(L, 1, \"z\");\r\n\t\tif (lua_type(L, -3) == LUA_TNUMBER && lua_type(L, -2) == LUA_TNUMBER && lua_type(L, -1) == LUA_TNUMBER)\r\n\t\t{\r\n\t\t\tvec3_t v = {lua_tonumberx(L, -3, NULL), lua_tonumberx(L, -2, NULL), lua_tonumberx(L, -1, NULL)};\r\n\t\t\tlua_pushnumber(L, DotProduct(v,v));\r\n\t\t\treturn 1;\r\n\t\t}\r\n\t\tlua_getfield(L, 1, \"entnum\");\r\n\t\tif (lua_type(L, -1) == LUA_TNUMBER)\r\n\t\t\treturn 1;\r\n\t\t//fallthrough\r\n\tdefault:\r\n\t\tlua_pushfstring(L, \"%s: %p\", lua_typename(L, lua_type(L, 1)), lua_topointer(L, 1));\r\n\t\tbreak;\r\n\t}\r\n\treturn 1;\r\n}\r\n#define bi_lua_stof bi_lua_tonumber\r\n\r\nstatic int my_lua_panic(lua_State *L)\r\n{\r\n\tconst char *s = lua_tolstring(L, -1, NULL);\r\n\tSys_Error(\"lua error: %s\", s);\r\n}\r\n\r\nstatic int my_lua_entity_eq(lua_State *L)\r\n{\r\n\t//table1=1\r\n\t//table2=2\r\n\tunsigned int entnum1, entnum2;\r\n\tlua_getfield(L, 1, \"entnum\");\r\n\tentnum1 = lua_tointegerx(L, -1, NULL);\r\n\tlua_getfield(L, 2, \"entnum\");\r\n\tentnum2 = lua_tointegerx(L, -1, NULL);\r\n\tlua_pop(L, 2);\r\n\r\n\tlua_pushboolean(L, entnum1 == entnum2);\r\n\treturn 1;\r\n}\r\n\r\nstatic int my_lua_entity_tostring(lua_State *L)\r\n{\r\n\t//table=1\r\n\tunsigned int entnum;\r\n\tlua_getfield(L, 1, \"entnum\");\r\n\tentnum = lua_tointegerx(L, -1, NULL);\r\n\tlua_pop(L, 1);\r\n\r\n\tlua_pushstring(L, va(\"entity: %u\", entnum));\r\n\treturn 1;\r\n}\r\n\r\nstatic int my_lua_vec3_tostring(lua_State *L)\r\n{\r\n\t//table=1\r\n\tlua_Number x,y,z;\r\n\tlua_getfield(L, 1, \"x\");\r\n\tx = lua_tonumber(L, -1);\r\n\tlua_getfield(L, 1, \"y\");\r\n\ty = lua_tonumber(L, -1);\r\n\tlua_getfield(L, 1, \"z\");\r\n\tz = lua_tonumber(L, -1);\r\n\tlua_pop(L, 3);\r\n\r\n\tlua_pushstring(L, va(\"'%g %g %g'\", x, y, z));\r\n\treturn 1;\r\n}\r\n\r\nstatic qboolean lua_readvector(lua_State *L, int idx, float *result)\r\n{\r\n\tswitch(lua_type(L, idx))\r\n\t{\r\n\tcase LUA_TSTRING:\r\n\t\t{\r\n\t\t\t//we parse strings primnarily for easy .ent(or bsp) loading support.\r\n\t\t\tconst char *str = lua_tolstring(L, idx, NULL);\r\n\t\t\tstr = COM_Parse(str);\r\n\t\t\tresult[0] = atof(com_token);\r\n\t\t\tstr = COM_Parse(str);\r\n\t\t\tresult[1] = atof(com_token);\r\n\t\t\tstr = COM_Parse(str);\r\n\t\t\tresult[2] = atof(com_token);\r\n\t\t}\r\n\t\treturn true;\r\n\tcase LUA_TTABLE:\r\n\t\tlua_getfield(L, idx, \"x\");\r\n\t\tresult[0] = lua_tonumberx(L, -1, NULL);\r\n\t\tlua_getfield(L, idx, \"y\");\r\n\t\tresult[1] = lua_tonumberx(L, -1, NULL);\r\n\t\tlua_getfield(L, idx, \"z\");\r\n\t\tresult[2] = lua_tonumberx(L, -1, NULL);\r\n\t\tlua_pop(L, 3);\r\n\t\treturn true;\r\n\tcase LUA_TNUMBER:\r\n\t\tresult[0] = \r\n\t\tresult[1] = \r\n\t\tresult[2] = lua_tonumber(L, idx);\r\n\t\tbreak;\r\n\tcase LUA_TNIL:\r\n\t\tresult[0] = 0;\r\n\t\tresult[1] = 0;\r\n\t\tresult[2] = 0;\r\n\t\tbreak;\r\n\tdefault:\r\n\t\tCon_Printf(\"Expected vector, got %s\\n\", lua_typename(L, lua_type(L, idx)));\r\n\t\tresult[0] = 0;\r\n\t\tresult[1] = 0;\r\n\t\tresult[2] = 0;\r\n\t\tbreak;\r\n\t}\r\n\treturn false;\r\n}\r\nstatic void lua_pushvector(lua_State *L, vec_t x, vec_t y, vec_t z)\r\n{\r\n\tlua_newtable(L);\r\n\t//FIXME: should provide a metatable with a __tostring\r\n\tlua_pushnumber(L, x);\r\n\tlua_setfield (L, -2, \"x\");\r\n\tlua_pushnumber(L, y);\r\n\tlua_setfield (L, -2, \"y\");\r\n\tlua_pushnumber(L, z);\r\n\tlua_setfield (L, -2, \"z\");\r\n\r\n\tluaL_getmetatable(L, \"vec3_t\");\r\n\tlua_setmetatable(L, -2); \r\n}\r\n\r\nstatic int my_lua_vec3_eq(lua_State *L)\r\n{\r\n\tvec3_t a, b;\r\n\tlua_readvector(L, 1, a);\r\n\tlua_readvector(L, 2, b);\r\n\tlua_pushboolean(L, a[0]==b[0] && a[1]==b[1] && a[2]==b[2]);\r\n\treturn 1;\r\n}\r\nstatic int my_lua_vec3_sub(lua_State *L)\r\n{\r\n\tvec3_t a, b;\r\n\tlua_readvector(L, 1, a);\r\n\tlua_readvector(L, 2, b);\r\n\tlua_pushvector(L, a[0]-b[0], a[1]-b[1], a[2]-b[2]);\r\n\treturn 1;\r\n}\r\nstatic int my_lua_vec3_mul(lua_State *L)\r\n{\r\n\tvec3_t a, b;\r\n\tif (lua_readvector(L, 1, a) + lua_readvector(L, 2, b) == 2)\r\n\t\tlua_pushnumber(L, DotProduct(a,b));\t//vec*vec is a dotproduct\r\n\telse\r\n\t\tlua_pushvector(L, a[0]*b[0], a[1]*b[1], a[2]*b[2]);\t//one arg is a scaler. woot.\r\n\treturn 1;\r\n}\r\nstatic int my_lua_vec3_add(lua_State *L)\r\n{\r\n\tvec3_t a, b;\r\n\tlua_readvector(L, 1, a);\r\n\tlua_readvector(L, 2, b);\r\n\tlua_pushvector(L, a[0]+b[0], a[1]+b[1], a[2]+b[2]);\r\n\treturn 1;\r\n}\r\nstatic int my_lua_vec3_len(lua_State *L)\r\n{\r\n\tvec3_t a;\r\n\tlua_readvector(L, 1, a);\r\n\tlua_pushnumber(L, VectorLength(a));\r\n\treturn 1;\r\n}\r\n\r\nstatic int my_lua_entity_set(lua_State *L)\t//__newindex\r\n{\r\n//\tCon_Printf(\"lua_entity_set: \");\r\n//\tmy_lua_print(L);\r\n\t//table=1\r\n\t//key=2\r\n\t//value=3\r\n\r\n\tif (lua_type(L, 2) == LUA_TSTRING)\r\n\t{\r\n\t\tconst char *s = lua_tolstring(L, 2, NULL);\r\n\t\tluafld_t *fld = Hash_GetInsensitive(&lua.entityfields, s);\r\n\t\teval_t *eval;\r\n\t\tunsigned int entnum;\r\n\t\tif (fld && fld->offset >= 0)\r\n\t\t{\r\n\t\t\tlua_getfield(L, 1, \"entnum\");\r\n\t\t\tentnum = lua_tointegerx(L, -1, NULL);\r\n\t\t\tlua_pop(L, 1);\r\n\t\t\tif (entnum < lua.maxedicts && lua.edicttable[entnum] && !ED_ISFREE(lua.edicttable[entnum]))\r\n\t\t\t{\r\n\t\t\t\teval = (eval_t*)((char*)lua.edicttable[entnum]->v + fld->offset);\r\n\t\t\t\tswitch(fld->type)\r\n\t\t\t\t{\r\n\t\t\t\tcase ev_float:\r\n\t\t\t\t\teval->_float = lua_tonumberx(L, 3, NULL);\r\n\t\t\t\t\treturn 0;\r\n\t\t\t\tcase ev_vector:\r\n\t\t\t\t\tlua_readvector(L, 3, eval->_vector);\r\n\t\t\t\t\treturn 0;\r\n\t\t\t\tcase ev_integer:\r\n\t\t\t\t\teval->_int = lua_tointegerx(L, 3, NULL);\r\n\t\t\t\t\treturn 0;\r\n\t\t\t\tcase ev_function:\r\n\t\t\t\t\tif (lua_type(L, 3) == LUA_TNIL)\r\n\t\t\t\t\t\teval->function = 0;\t//so the engine can distinguish between nil and not.\r\n\t\t\t\t\telse\r\n\t\t\t\t\t\teval->function = fld->offset | ((entnum+1)<<10);\r\n\t\t\t\t\tlua_pushlightuserdata(L, (void *)(qintptr_t)fld->offset);\t//execute only knows a function id, so we need to translate the store to match.\r\n\t\t\t\t\tlua_replace(L, 2);\r\n\t\t\t\t\tlua_rawset(L, 1);\r\n\t\t\t\t\treturn 0;\r\n\t\t\t\tcase ev_string:\r\n\t\t\t\t\tif (lua_type(L, 3) == LUA_TNIL)\r\n\t\t\t\t\t\teval->string = 0;\t//so the engine can distinguish between nil and not.\r\n\t\t\t\t\telse\r\n\t\t\t\t\t\teval->string = fld->offset | ((entnum+1)<<10);\r\n\t\t\t\t\tlua_pushlightuserdata(L, (void *)(qintptr_t)fld->offset);\t//execute only knows a string id, so we need to translate the store to match.\r\n\t\t\t\t\tlua_replace(L, 2);\r\n\t\t\t\t\tlua_rawset(L, 1);\r\n\t\t\t\t\treturn 0;\r\n\t\t\t\tcase ev_entity:\r\n\t\t\t\t\t//read the table's entnum field so we know which one its meant to be.\r\n\t\t\t\t\tlua_getfield(L, 3, \"entnum\");\r\n\t\t\t\t\teval->edict = lua_tointegerx(L, -1, NULL);\r\n\t\t\t\t\treturn 0;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\tlua_rawset(L, 1);\r\n\treturn 0;\r\n}\r\nstatic int my_lua_entity_get(lua_State *L)\t//__index\r\n{\r\n//\tCon_Printf(\"lua_entity_get: \");\r\n//\tmy_lua_print(L);\r\n\t//table=1\r\n\t//key=2\r\n\r\n\tif (lua_type(L, 2) == LUA_TSTRING)\r\n\t{\r\n\t\tconst char *s = lua_tolstring(L, 2, NULL);\r\n\t\tluafld_t *fld = Hash_GetInsensitive(&lua.entityfields, s);\r\n\t\teval_t *eval;\r\n\t\tint entnum;\r\n\t\tif (fld)\r\n\t\t{\r\n\t\t\tif (fld->offset < 0)\r\n\t\t\t{\t//negative offsets are not real fields, but are just there to ensure that no nils appear\r\n\t\t\t\tlua_rawget(L, 1);\r\n\t\t\t\tif (lua_isnil(L, -1))\r\n\t\t\t\t{\r\n\t\t\t\t\tlua_pop(L, 1);\r\n\t\t\t\t\tswitch(fld->type)\r\n\t\t\t\t\t{\r\n\t\t\t\t\tcase ev_float:\r\n\t\t\t\t\t\tlua_pushnumber(L, 0);\r\n\t\t\t\t\t\treturn 1;\r\n\t\t\t\t\tcase ev_integer:\r\n\t\t\t\t\t\tlua_pushinteger(L, 0);\r\n\t\t\t\t\t\treturn 1;\r\n\t\t\t\t\tcase ev_vector:\r\n\t\t\t\t\t\tlua_pushvector(L, 0, 0, 0);\r\n\t\t\t\t\t\tbreak;\r\n\t\t\t\t\tcase ev_function:\r\n\t\t\t\t\tdefault:\t//no idea\r\n\t\t\t\t\t\tlua_pushnil(L);\r\n\t\t\t\t\t\tbreak;\r\n\t\t\t\t\tcase ev_string:\r\n\t\t\t\t\t\tlua_pushliteral(L, \"\");\r\n\t\t\t\t\t\tbreak;\r\n\t\t\t\t\tcase ev_entity:\r\n\t\t\t\t\t\tlua_pushedict(L, lua.edicttable[0]);\r\n\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t\treturn 1;\r\n\t\t\t}\r\n\t\t\tlua_getfield(L, 1, \"entnum\");\r\n\t\t\tentnum = lua_tointegerx(L, -1, NULL);\r\n\t\t\tlua_pop(L, 1);\r\n\t\t\tif (entnum < lua.maxedicts && lua.edicttable[entnum])// && !lua.edicttable[entnum]->isfree)\r\n\t\t\t{\r\n\t\t\t\teval = (eval_t*)((char*)lua.edicttable[entnum]->v + fld->offset);\r\n\t\t\t\tswitch(fld->type)\r\n\t\t\t\t{\r\n\t\t\t\tcase ev_float:\r\n\t\t\t\t\tlua_pushnumber(L, eval->_float);\r\n\t\t\t\t\treturn 1;\r\n\t\t\t\tcase ev_integer:\r\n\t\t\t\t\tlua_pushinteger(L, eval->_int);\r\n\t\t\t\t\treturn 1;\r\n\t\t\t\tcase ev_vector:\r\n\t\t\t\t\tlua_pushvector(L, eval->_vector[0], eval->_vector[1], eval->_vector[2]);\r\n\t\t\t\t\treturn 1;\r\n\t\t\t\tcase ev_function:\r\n\t\t\t\tcase ev_string:\r\n\t\t\t\t\tlua_pushlightuserdata(L, (void *)(qintptr_t)(eval->function & 1023));\t//execute only knows a function id, so we need to translate the store to match.\r\n\t\t\t\t\tlua_replace(L, 2);\r\n\t\t\t\t\tlua_rawget(L, 1);\r\n\t\t\t\t\treturn 1;\r\n\t\t\t\tcase ev_entity:\r\n\t\t\t\t\t//return the table for the entity via the lua registry.\r\n\t\t\t\t\tlua_pushlightuserdata(lua.ctx, lua.edicttable[eval->edict]);\r\n\t\t\t\t\tlua_gettable(lua.ctx, LUA_REGISTRYINDEX);\r\n\t\t\t\t\treturn 1;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\t//make sure it exists so we don't get called constantly if code loops through stuff that wasn't set.\r\n//\tlua_pushstring(L, \"nil\");\r\n\tlua_rawget(L, 1);\r\n\treturn 1;\r\n}\r\nstatic int my_lua_global_set(lua_State *L)\t//__newindex\r\n{\r\n//\tCon_Printf(\"my_lua_global_set: \");\r\n//\tmy_lua_print(L);\r\n\t//table=1\r\n\t//key=2\r\n\t//value=3\r\n\r\n\tif (lua_type(L, 2) == LUA_TSTRING)\r\n\t{\r\n\t\tconst char *s = lua_tolstring(L, 2, NULL);\r\n\t\tluafld_t *fld = Hash_GetInsensitive(&lua.globalfields, s);\r\n\t\teval_t *eval;\r\n\t\tif (fld)\r\n\t\t{\r\n\t\t\teval = (eval_t*)((char*)&lua.globals + fld->offset);\r\n\t\t\tswitch(fld->type)\r\n\t\t\t{\r\n\t\t\tcase ev_float:\r\n\t\t\t\teval->_float = lua_tonumberx(L, 3, NULL);\r\n\t\t\t\treturn 0;\r\n\t\t\tcase ev_vector:\r\n\t\t\t\tlua_getfield(L, 3, \"x\");\r\n\t\t\t\teval->_vector[0] = lua_tonumberx(L, -1, NULL);\r\n\t\t\t\tlua_getfield(L, 3, \"y\");\r\n\t\t\t\teval->_vector[1] = lua_tonumberx(L, -1, NULL);\r\n\t\t\t\tlua_getfield(L, 3, \"z\");\r\n\t\t\t\teval->_vector[2] = lua_tonumberx(L, -1, NULL);\r\n\t\t\t\treturn 0;\r\n\t\t\tcase ev_integer:\r\n\t\t\t\teval->_int = lua_tointegerx(L, 3, NULL);\r\n\t\t\t\treturn 0;\r\n\t\t\tcase ev_function:\r\n\t\t\t\tif (lua_type(L, 3) == LUA_TNIL)\r\n\t\t\t\t\teval->function = 0;\t//so the engine can distinguish between nil and not.\r\n\t\t\t\telse\r\n\t\t\t\t\teval->function = fld->offset;\r\n\t\t\t\tlua_pushlightuserdata(L, (void *)fld->offset);\t//execute only knows a function id, so we need to translate the store to match.\r\n\t\t\t\tlua_replace(L, 2);\r\n\t\t\t\tlua_rawset(L, 1);\r\n\t\t\t\treturn 0;\r\n\t\t\tcase ev_string:\r\n\t\t\t\tif (lua_type(L, 3) == LUA_TNIL)\r\n\t\t\t\t\teval->string = 0;\t//so the engine can distinguish between nil and not.\r\n\t\t\t\telse\r\n\t\t\t\t\teval->string = fld->offset;\r\n\t\t\t\tlua_pushlightuserdata(L, (void *)fld->offset);\t//execute only knows a string id, so we need to translate the store to match.\r\n\t\t\t\tlua_replace(L, 2);\r\n\t\t\t\tlua_rawset(L, 1);\r\n\t\t\t\treturn 0;\r\n\t\t\tcase ev_entity:\r\n\t\t\t\t//read the table's entnum field so we know which one its meant to be.\r\n\t\t\t\tlua_getfield(L, 3, \"entnum\");\r\n\t\t\t\teval->edict = lua_tointegerx(L, -1, NULL);\r\n\t\t\t\treturn 0;\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\tlua_rawset(L, 1);\r\n\treturn 0;\r\n}\r\nstatic int my_lua_global_get(lua_State *L)\t//__index\r\n{\r\n//\tCon_Printf(\"my_lua_global_get: \");\r\n//\tmy_lua_print(L);\r\n\t//table=1\r\n\t//key=2\r\n\r\n\tif (lua_type(L, 2) == LUA_TSTRING)\r\n\t{\r\n\t\tconst char *s = lua_tolstring(L, 2, NULL);\r\n\t\tluafld_t *fld = Hash_GetInsensitive(&lua.globalfields, s);\r\n\t\teval_t *eval;\r\n\t\tif (fld)\r\n\t\t{\r\n\t\t\teval = (eval_t*)((char*)&lua.globals + fld->offset);\r\n\t\t\tswitch(fld->type)\r\n\t\t\t{\r\n\t\t\tcase ev_float:\r\n\t\t\t\tlua_pushnumber(L, eval->_float);\r\n\t\t\t\treturn 1;\r\n\t\t\tcase ev_integer:\r\n\t\t\t\tlua_pushinteger(L, eval->_int);\r\n\t\t\t\treturn 1;\r\n\t\t\tcase ev_vector:\r\n\t\t\t\tlua_pushvector(L, eval->_vector[0], eval->_vector[1], eval->_vector[2]);\r\n\t\t\t\treturn 1;\r\n\t\t\tcase ev_function:\r\n\t\t\t\tlua_pushlightuserdata(L, (void *)(qintptr_t)eval->function);\t//execute only knows a function id, so we need to translate the store to match.\r\n\t\t\t\tlua_replace(L, 2);\r\n\t\t\t\tlua_rawget(L, 1);\r\n\t\t\t\treturn 1;\r\n\t\t\tcase ev_entity:\r\n\t\t\t\tlua_pushedict(L, lua.edicttable[eval->edict]);\r\n\t\t\t\treturn 1;\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\t//make sure it exists so we don't get called constantly if code loops through stuff that wasn't set.\r\n//\tlua_pushstring(L, \"nil\");\r\n\tlua_rawget(L, 1);\r\n\treturn 1;\r\n}\r\n\r\nstatic int bi_lua_setmodel(lua_State *L)\r\n{\r\n\tint entnum;\r\n\tedict_t *e;\r\n\tlua_getfield(L, 1, \"entnum\");\r\n\tentnum = lua_tointegerx(L, -1, NULL);\r\n\te = (entnum>=lua.maxedicts)?NULL:lua.edicttable[entnum];\r\n\tPF_setmodel_Internal(&lua.progfuncs, e, lua_tolstring(L, 2, NULL));\r\n\treturn 0;\r\n}\r\nstatic int bi_lua_precache_model(lua_State *L)\r\n{\r\n\tPF_precache_model_Internal(&lua.progfuncs, lua_tolstring(L, 1, NULL), false);\r\n\treturn 0;\r\n}\r\n#define bi_lua_precache_model2 bi_lua_precache_model\r\nstatic int bi_lua_precache_sound(lua_State *L)\r\n{\r\n\tPF_precache_sound_Internal(&lua.progfuncs, lua_tolstring(L, 1, NULL), false);\r\n\treturn 0;\r\n}\r\n#define bi_lua_precache_sound2 bi_lua_precache_sound\r\nstatic int bi_lua_precache_file(lua_State *L)\r\n{\r\n\treturn 0;\r\n}\r\nstatic int bi_lua_lightstyle(lua_State *L)\r\n{\r\n\tvec3_t rgb;\r\n\tif (lua_gettop(L) >= 3)\r\n\t\tlua_readvector(L, 3, rgb);\r\n\telse\r\n\t\tVectorSet(rgb, 1, 1, 1);\r\n\tPF_applylightstyle(lua_tointegerx(L, 1, NULL), lua_tolstring(L, 2, NULL), rgb);\r\n\treturn 0;\r\n}\r\nstatic int bi_lua_spawn(lua_State *L)\r\n{\r\n\tedict_t *e = lua.progfuncs.EntAlloc(&lua.progfuncs, false, 0);\r\n\tif (e)\r\n\t\tlua_pushedict(L, e);\r\n\telse\r\n\t\tlua_pushnil(L);\r\n\treturn 1;\r\n}\r\nstatic int bi_lua_remove(lua_State *L)\r\n{\r\n\tint entnum;\r\n\tedict_t *e;\r\n\tlua_getfield(L, 1, \"entnum\");\r\n\tentnum = lua_tointegerx(L, -1, NULL);\r\n\te = (entnum>=lua.maxedicts)?NULL:lua.edicttable[entnum];\r\n\tif (e)\r\n\t\tlua.progfuncs.EntFree(&lua.progfuncs, e, false);\r\n\treturn 0;\r\n}\r\nstatic int bi_lua_setorigin(lua_State *L)\r\n{\r\n\tedict_t *e;\r\n\tlua_getfield(L, 1, \"entnum\");\r\n\te = EDICT_NUM_UB((&lua.progfuncs), lua_tointegerx(L, -1, NULL));\r\n\tlua_readvector(L, 2, e->v->origin);\r\n\tWorld_LinkEdict (&sv.world, (wedict_t*)e, false);\r\n\treturn 0;\r\n}\r\nstatic int bi_lua_setsize(lua_State *L)\r\n{\r\n\tedict_t *e;\r\n\tlua_getfield(L, 1, \"entnum\");\r\n\te = EDICT_NUM_UB((&lua.progfuncs), lua_tointegerx(L, -1, NULL));\r\n\tlua_readvector(L, 2, e->v->mins);\r\n\tlua_readvector(L, 3, e->v->maxs);\r\n\tVectorSubtract (e->v->maxs, e->v->mins, e->v->size);\r\n\tWorld_LinkEdict (&sv.world, (wedict_t*)e, false);\r\n\treturn 0;\r\n}\r\n\r\nstatic int bi_lua_localcmd(lua_State *L)\r\n{\r\n\tconst char\t*str = lua_tolstring(lua.ctx, 1, NULL);\r\n\tCbuf_AddText (str, RESTRICT_INSECURE);\r\n\treturn 0;\r\n}\r\nstatic int bi_lua_changelevel(lua_State *L)\r\n{\r\n\tconst char\t*s, *spot;\r\n\r\n// make sure we don't issue two changelevels (unless the last one failed)\r\n\tif (sv.mapchangelocked)\r\n\t\treturn 0;\r\n\tsv.mapchangelocked = true;\r\n\r\n\tif (lua_type(L, 2) == LUA_TSTRING)\t//and not nil or none\r\n\t{\r\n\t\ts = lua_tolstring(lua.ctx, 1, NULL);\r\n\t\tspot = lua_tolstring(lua.ctx, 2, NULL);\r\n\t\tCbuf_AddText (va(\"\\nchangelevel %s %s\\n\",s, spot), RESTRICT_LOCAL);\r\n\t}\r\n\telse\r\n\t{\r\n\t\ts = lua_tolstring(lua.ctx, 1, NULL);\r\n\t\tCbuf_AddText (va(\"\\nchangelevel %s\\n\",s), RESTRICT_LOCAL);\r\n\t}\r\n\treturn 0;\r\n}\r\n\r\nstatic int bi_lua_stuffcmd(lua_State *L)\r\n{\r\n\tint entnum;\r\n\tconst char *str;\r\n\tlua_getfield(L, 1, \"entnum\");\r\n\tentnum = lua_tointegerx(L, -1, NULL);\r\n\tstr = lua_tolstring(L, 2, NULL);\r\n\r\n\tPF_stuffcmd_Internal(entnum, str, 0);\r\n\treturn 0;\r\n}\r\nstatic int bi_lua_centerprint(lua_State *L)\r\n{\r\n\tint entnum;\r\n\tconst char *str;\r\n\tlua_getfield(L, 1, \"entnum\");\r\n\tentnum = lua_tointegerx(L, -1, NULL);\r\n\tstr = lua_tolstring(L, 2, NULL);\r\n\tif (!str)\r\n\t\tstr = \"\";\r\n\tPF_centerprint_Internal(entnum, false, str);\r\n\treturn 0;\r\n}\r\nstatic int bi_lua_getinfokey(lua_State *L)\r\n{\r\n\tint entnum;\r\n\tconst char *key;\r\n\tlua_getfield(L, 1, \"entnum\");\r\n\tentnum = lua_tointegerx(L, -1, NULL);\r\n\tkey = lua_tolstring(L, 2, NULL);\r\n\r\n\tkey = PF_infokey_Internal(entnum, key);\r\n\tlua_pushstring(L, key);\r\n\treturn 1;\r\n}\r\n#define bi_lua_infokey bi_lua_getinfokey\r\nstatic int bi_lua_setinfokey(lua_State *L)\r\n{\r\n\tint entnum;\r\n\tconst char *key;\r\n\tconst char *value;\r\n\tint result;\r\n\tlua_getfield(L, 1, \"entnum\");\r\n\tentnum = lua_tointegerx(L, -1, NULL);\r\n\tkey = lua_tolstring(L, 2, NULL);\r\n\tvalue = lua_tolstring(L, 3, NULL);\r\n\r\n\tresult = PF_ForceInfoKey_Internal(entnum, key, value, strlen(value));\r\n\tlua_pushinteger(L, result);\r\n\treturn 1;\r\n}\r\nstatic int bi_lua_ambientsound(lua_State *L)\r\n{\r\n\tvec3_t pos;\r\n\tconst char *samp = lua_tolstring(L, 2, NULL);\r\n\tfloat vol = lua_tonumberx(L, 3, NULL);\r\n\tfloat attenuation = lua_tonumberx(L, 4, NULL);\r\n\tlua_readvector(L, 1, pos);\r\n\r\n\tPF_ambientsound_Internal(pos, samp, vol, attenuation);\r\n\treturn 0;\r\n}\r\nstatic int bi_lua_sound(lua_State *L)\r\n{\r\n\tint entnum;\r\n\tfloat channel = lua_tonumberx(L, 2, NULL);\r\n\tconst char *samp = lua_tolstring(L, 3, NULL);\r\n\tfloat volume = lua_tonumberx(L, 4, NULL);\r\n\tfloat attenuation = lua_tonumberx(L, 5, NULL);\r\n\r\n\tlua_getfield(L, 1, \"entnum\");\r\n\tentnum = lua_tointegerx(L, -1, NULL);\r\n\r\n\t//note: channel & 256 == reliable\r\n\r\n\tSVQ1_StartSound (NULL, (wedict_t*)EDICT_NUM_UB((&lua.progfuncs), entnum), channel, samp, volume*255, attenuation, 0, 0, 0);\r\n\treturn 0;\r\n}\r\n\r\nstatic int bi_lua_pointcontents(lua_State *L)\r\n{\r\n\tvec3_t pos;\r\n\tlua_readvector(L, 1, pos);\r\n\tlua_pushinteger(L, sv.world.worldmodel->funcs.PointContents(sv.world.worldmodel, NULL, pos));\r\n\treturn 1;\r\n}\r\n\r\nstatic int bi_lua_setspawnparms(lua_State *L)\r\n{\r\n\tglobalvars_t pr_globals;\r\n\r\n\tlua_getfield(L, 1, \"entnum\");\r\n\tpr_globals.param[0].i = lua_tointegerx(L, -1, NULL);\r\n\tPF_setspawnparms(&lua.progfuncs, &pr_globals);\r\n\treturn 0;\r\n}\r\nstatic int bi_lua_makestatic(lua_State *L)\r\n{\r\n\tglobalvars_t pr_globals;\r\n\r\n\tlua_getfield(L, 1, \"entnum\");\r\n\tpr_globals.param[0].i = lua_tointegerx(L, -1, NULL);\r\n\tPF_makestatic(&lua.progfuncs, &pr_globals);\r\n\treturn 0;\r\n}\r\n\r\nstatic int bi_lua_droptofloor(lua_State *L)\r\n{\r\n\textern cvar_t pr_droptofloorunits;\r\n\twedict_t\t*ent;\r\n\tvec3_t\t\tend;\r\n\tvec3_t\t\tstart;\r\n\ttrace_t\t\ttrace;\r\n\tconst float *gravitydir;\r\n\tstatic const vec3_t standardgravity = {0,0,-1};\r\n\tpubprogfuncs_t *prinst = &lua.progfuncs;\r\n\tworld_t *world = prinst->parms->user;\r\n\r\n\tent = PROG_TO_WEDICT((&lua.progfuncs), pr_global_struct->self);\r\n\r\n\tif (ent->xv->gravitydir[2] || ent->xv->gravitydir[1] || ent->xv->gravitydir[0])\r\n\t\tgravitydir = ent->xv->gravitydir;\r\n\telse\r\n\t\tgravitydir = standardgravity;\r\n\r\n\tVectorCopy (ent->v->origin, end);\r\n\tif (pr_droptofloorunits.value > 0)\r\n\t\tVectorMA(end, pr_droptofloorunits.value, gravitydir, end);\r\n\telse\r\n\t\tVectorMA(end, 256, gravitydir, end);\r\n\r\n\tVectorCopy (ent->v->origin, start);\r\n\ttrace = World_Move (world, start, ent->v->mins, ent->v->maxs, end, MOVE_NORMAL, ent);\r\n\r\n\tif (trace.fraction == 1 || trace.allsolid)\r\n\t\tlua_pushboolean(L, false);\r\n\telse\r\n\t{\r\n\t\tVectorCopy (trace.endpos, ent->v->origin);\r\n\t\tWorld_LinkEdict (world, ent, false);\r\n\t\tent->v->flags = (int)ent->v->flags | FL_ONGROUND;\r\n\t\tent->v->groundentity = EDICT_TO_PROG(prinst, trace.ent);\r\n\t\tlua_pushboolean(L, true);\r\n\t}\r\n\treturn 1;\r\n}\r\n\r\nstatic int bi_lua_checkbottom(lua_State *L)\r\n{\r\n\tqboolean okay;\r\n\tint entnum;\r\n\tvec3_t up = {0,0,1};\r\n\tlua_getfield(L, 1, \"entnum\");\r\n\tentnum = lua_tointegerx(L, -1, NULL);\r\n\tokay = World_CheckBottom(&sv.world, (wedict_t*)EDICT_NUM_UB((&lua.progfuncs), entnum), up);\r\n\tlua_pushboolean(L, okay);\r\n\treturn 1;\r\n}\r\n\r\nstatic int bi_lua_bprint_qw(lua_State *L)\r\n{\r\n\tint level = lua_tointegerx(L, 1, NULL);\r\n\tconst char *str = lua_tolstring(L, 2, NULL);\r\n\tSV_BroadcastPrintf (level, \"%s\", str);\r\n\treturn 0;\r\n}\r\nstatic int bi_lua_bprint_nq(lua_State *L)\r\n{\r\n\tint level = PRINT_HIGH;\r\n\tconst char *str = lua_tolstring(L, 1, NULL);\r\n\tSV_BroadcastPrintf (level, \"%s\", str);\r\n\treturn 0;\r\n}\r\nstatic int bi_lua_sprint_qw(lua_State *L)\r\n{\r\n\tint entnum;\r\n\tint level = lua_tointegerx(L, 2, NULL);\r\n\tconst char *str = lua_tolstring(L, 3, NULL);\r\n\r\n\tlua_getfield(L, 1, \"entnum\");\r\n\tentnum = lua_tointegerx(L, -1, NULL);\r\n\r\n\tif (entnum < 1 || entnum > sv.allocated_client_slots)\r\n\t{\r\n\t\tCon_TPrintf (\"tried to sprint to a non-client\\n\");\r\n\t\treturn 0;\r\n\t}\r\n\tSV_ClientPrintf (&svs.clients[entnum-1], level, \"%s\", str);\r\n\treturn 0;\r\n}\r\nstatic int bi_lua_sprint_nq(lua_State *L)\r\n{\r\n\tint entnum;\r\n\tint level = PRINT_HIGH;\r\n\tconst char *str = lua_tolstring(L, 2, NULL);\r\n\r\n\tlua_getfield(L, 1, \"entnum\");\r\n\tentnum = lua_tointegerx(L, -1, NULL);\r\n\r\n\tif (entnum < 1 || entnum > sv.allocated_client_slots)\r\n\t{\r\n\t\tCon_TPrintf (\"tried to sprint to a non-client\\n\");\r\n\t\treturn 0;\r\n\t}\r\n\tSV_ClientPrintf (&svs.clients[entnum-1], level, \"%s\", str);\r\n\treturn 0;\r\n}\r\n\r\nstatic int bi_lua_cvar_set(lua_State *L)\r\n{\r\n\tconst char *name = lua_tolstring(L, 1, NULL);\r\n\tconst char *str = lua_tolstring(L, 2, NULL);\r\n\tcvar_t *var = Cvar_FindVar(name);\r\n\tif (var)\r\n\t\tCvar_Set(var, str);\r\n\treturn 0;\r\n}\r\nstatic int bi_lua_cvar_get(lua_State *L)\r\n{\r\n\tconst char *name = lua_tolstring(L, 1, NULL);\r\n\tcvar_t *var = Cvar_FindVar(name);\r\n\tif (var)\r\n\t\tlua_pushstring(L, var->string);\r\n\telse\r\n\t\tlua_pushnil(L);\r\n\treturn 1;\r\n}\r\nstatic int bi_lua_cvar(lua_State *L)\t//useless get[float]\r\n{\r\n\tconst char *name = lua_tolstring(L, 1, NULL);\r\n\tcvar_t *var = Cvar_FindVar(name);\r\n\tif (var)\r\n\t\tlua_pushnumber(L, var->value);\r\n\telse\r\n\t\tlua_pushnil(L);\r\n\treturn 1;\r\n}\r\n\r\nstatic int pushset_trace_globals(lua_State *L, trace_t *trace)\r\n{\r\n\tpr_global_struct->trace_allsolid = trace->allsolid;\r\n\tpr_global_struct->trace_startsolid = trace->startsolid;\r\n\tpr_global_struct->trace_fraction = trace->fraction;\r\n\tpr_global_struct->trace_inwater = trace->inwater;\r\n\tpr_global_struct->trace_inopen = trace->inopen;\r\n\tpr_global_struct->trace_surfaceflagsf = trace->surface?trace->surface->flags:0;\r\n\tpr_global_struct->trace_surfaceflagsi = trace->surface?trace->surface->flags:0;\r\n//\tif (pr_global_struct->trace_surfacename)\r\n//\t\tprinst->SetStringField(prinst, NULL, &pr_global_struct->trace_surfacename, tr->surface?tr->surface->name:\"\", true);\r\n\tpr_global_struct->trace_endcontentsf = trace->contents;\r\n\tpr_global_struct->trace_endcontentsi = trace->contents;\r\n//\tif (trace.fraction != 1)\r\n//\t\tVectorMA (trace->endpos, 4, trace->plane.normal, P_VEC(trace_endpos));\r\n//\telse\r\n\t\tVectorCopy (trace->endpos, P_VEC(trace_endpos));\r\n\tVectorCopy (trace->plane.normal, P_VEC(trace_plane_normal));\r\n\tpr_global_struct->trace_plane_dist =  trace->plane.dist;\r\n\tpr_global_struct->trace_ent = trace->ent?((wedict_t*)trace->ent)->entnum:0;\r\n\r\n\r\n\tlua_newtable(L);\r\n\t\tlua_pushnumber(L, trace->fraction);\r\n\t\tlua_setfield(L, -2, \"fraction\");\r\n\t\tlua_pushvector(L, trace->endpos[0], trace->endpos[1], trace->endpos[2]);\r\n\t\tlua_setfield(L, -2, \"endpos\");\r\n\t\tif (trace->ent)\r\n\t\t\tlua_pushedict(L, trace->ent);\r\n\t\telse\r\n\t\t\tlua_pushedict(L, lua.edicttable[0]);\r\n\t\tlua_setfield(L, -2, \"ent\");\r\n\r\n\t\tlua_pushboolean(L, trace->allsolid);\r\n\t\tlua_setfield(L, -2, \"allsolid\");\r\n\t\tlua_pushboolean(L, trace->startsolid);\r\n\t\tlua_setfield(L, -2, \"startsolid\");\r\n\t\tlua_pushboolean(L, trace->inwater);\r\n\t\tlua_setfield(L, -2, \"inwater\");\r\n\t\tlua_pushboolean(L, trace->inopen);\r\n\t\tlua_setfield(L, -2, \"inopen\");\r\n\r\n\t\tlua_newtable(L);\r\n\t\t\tlua_pushnumber(L, trace->plane.dist);\r\n\t\t\tlua_setfield(L, -2, \"dist\");\r\n\t\t\tlua_pushvector(L, trace->plane.normal[0], trace->plane.normal[1], trace->plane.normal[2]);\r\n\t\t\tlua_setfield(L, -2, \"normal\");\r\n\t\tlua_setfield(L, -2, \"plane\");\r\n\treturn 1;\r\n}\r\n\r\nstatic int bi_lua_traceline(lua_State *L)\r\n{\r\n\tvec3_t v1, v2;\r\n\ttrace_t\ttrace;\r\n\tint\t\tnomonsters;\r\n\twedict_t\t*ent;\r\n\r\n\tlua_readvector(L, 1, v1);\r\n\tlua_readvector(L, 2, v2);\r\n\tnomonsters = lua_tointegerx(L, 3, NULL);\r\n\tlua_getfield(L, 4, \"entnum\");\r\n\tent = (wedict_t*)EDICT_NUM_UB((&lua.progfuncs), lua_tointegerx(L, -1, NULL));\r\n\r\n\ttrace = World_Move (&sv.world, v1, vec3_origin, vec3_origin, v2, nomonsters|MOVE_IGNOREHULL, (wedict_t*)ent);\r\n\r\n\treturn pushset_trace_globals(L, &trace);\r\n}\r\n\r\nstatic int bi_lua_tracebox(lua_State *L)\r\n{\r\n\tvec3_t v1, v2, mins, maxs;\r\n\ttrace_t\ttrace;\r\n\tint\t\tnomonsters;\r\n\twedict_t\t*ent;\r\n\r\n\tlua_readvector(L, 1, v1);\r\n\tlua_readvector(L, 2, v2);\r\n\tlua_readvector(L, 3, mins);\r\n\tlua_readvector(L, 4, maxs);\r\n\tnomonsters = lua_tointegerx(L, 5, NULL);\r\n\tlua_getfield(L, 6, \"entnum\");\r\n\tent = (wedict_t*)EDICT_NUM_UB((&lua.progfuncs), lua_tointegerx(L, -1, NULL));\r\n\r\n\ttrace = World_Move (&sv.world, v1, mins, maxs, v2, nomonsters|MOVE_IGNOREHULL, (wedict_t*)ent);\r\n\r\n\treturn pushset_trace_globals(L, &trace);\r\n}\r\n\r\nstatic int bi_lua_walkmove(lua_State *L)\r\n{\r\n\tpubprogfuncs_t *prinst = &lua.progfuncs;\r\n\tworld_t *world = prinst->parms->user;\r\n\twedict_t\t*ent;\r\n\tfloat\tyaw, dist;\r\n\tvec3_t\tmove;\r\n\tint \toldself;\r\n\tvec3_t\taxis[3];\r\n\r\n\tent = PROG_TO_WEDICT(prinst, *world->g.self);\r\n\tyaw = lua_tonumberx(L, 1, NULL);\r\n\tdist = lua_tonumberx(L, 2, NULL);\r\n\r\n\tif ( !( (int)ent->v->flags & (FL_ONGROUND|FL_FLY|FL_SWIM) ) )\r\n\t{\r\n\t\tlua_pushboolean(L, false);\r\n\t\treturn 1;\r\n\t}\r\n\r\n\tWorld_GetEntGravityAxis(ent, axis);\r\n\r\n\tyaw = yaw*M_PI*2 / 360;\r\n\r\n\tVectorScale(axis[0], cos(yaw)*dist, move);\r\n\tVectorMA(move, sin(yaw)*dist, axis[1], move);\r\n\r\n// save program state, because World_movestep may call other progs\r\n\toldself = *world->g.self;\r\n\r\n\tlua_pushboolean(L, World_movestep(world, ent, move, axis, true, false, NULL));\r\n\r\n// restore program state\r\n\t*world->g.self = oldself;\r\n\r\n\treturn 1;\r\n}\r\nstatic int bi_lua_movetogoal(lua_State *L)\r\n{\r\n\tpubprogfuncs_t *prinst = &lua.progfuncs;\r\n\tworld_t *world = prinst->parms->user;\r\n\twedict_t\t*ent;\r\n\tfloat dist;\r\n\tent = PROG_TO_WEDICT(prinst, *world->g.self);\r\n\tdist = lua_tonumberx(L, 1, NULL);\r\n\tWorld_MoveToGoal (world, ent, dist);\r\n\treturn 0;\r\n}\r\n\r\nstatic int bi_lua_nextent(lua_State *L)\r\n{\r\n\tworld_t *world = &sv.world;\r\n\tint\t\ti;\r\n\twedict_t\t*ent;\r\n\r\n\tlua_getfield(L, 1, \"entnum\");\r\n\ti = lua_tointegerx(L, -1, NULL);\r\n\r\n\twhile (1)\r\n\t{\r\n\t\ti++;\r\n\t\tif (i == world->num_edicts)\r\n\t\t{\r\n\t\t\tent = world->edicts;\r\n\t\t\tbreak;\r\n\t\t}\r\n\t\tent = (wedict_t *)lua.edicttable[i];\r\n\t\tif (!ED_ISFREE(ent))\r\n\t\t{\r\n\t\t\tbreak;\r\n\t\t}\r\n\t}\r\n\tlua_pushedict(L, (struct edict_s *)ent);\r\n\treturn 1;\r\n}\r\n\r\nstatic int bi_lua_nextclient(lua_State *L)\r\n{\r\n\tworld_t *world = &sv.world;\r\n\tint\t\ti;\r\n\twedict_t\t*ent;\r\n\r\n\tlua_getfield(L, 1, \"entnum\");\r\n\ti = lua_tointegerx(L, -1, NULL);\r\n\r\n\twhile (1)\r\n\t{\r\n\t\ti++;\r\n\t\tif (i == sv.allocated_client_slots)\r\n\t\t{\r\n\t\t\tent = world->edicts;\r\n\t\t\tbreak;\r\n\t\t}\r\n\t\tent = WEDICT_NUM_UB(world->progs, i);\r\n\t\tif (!ED_ISFREE(ent))\r\n\t\t{\r\n\t\t\tbreak;\r\n\t\t}\r\n\t}\r\n\tlua_pushlightuserdata(L, ent);\r\n\tlua_gettable(L, LUA_REGISTRYINDEX);\r\n\treturn 1;\r\n}\r\n\r\nstatic int bi_lua_checkclient(lua_State *L)\r\n{\r\n\tpubprogfuncs_t *prinst = &lua.progfuncs;\r\n\twedict_t *ent;\r\n\tent = WEDICT_NUM_PB(prinst, PF_checkclient_Internal(prinst));\r\n\tlua_pushlightuserdata(L, ent);\r\n\tlua_gettable(L, LUA_REGISTRYINDEX);\r\n\treturn 1;\r\n}\r\n\r\nstatic int bi_lua_random(lua_State *L)\r\n{\r\n\tlua_pushnumber(L, (rand ()&0x7fff) / ((float)0x8000));\r\n\treturn 1;\r\n}\r\n\r\nstatic int bi_lua_makevectors(lua_State *L)\r\n{\r\n\tvec3_t angles;\r\n\t//this is annoying as fuck in lua, what with it writing globals and stuff.\r\n\t//perhaps we should support f,u,l=makevectors(ang)... meh, cba.\r\n\tlua_readvector(L, 1, angles);\r\n\tAngleVectors (angles, pr_global_struct->v_forward, pr_global_struct->v_right, pr_global_struct->v_up);\r\n\r\n#if 1\r\n\t//globals suck, and lua allows multi-return.\r\n\tlua_pushvector(L, (pr_global_struct->v_forward)[0], (pr_global_struct->v_forward)[1], (pr_global_struct->v_forward)[2]);\r\n\tlua_pushvector(L, (pr_global_struct->v_right)[0], (pr_global_struct->v_right)[1], (pr_global_struct->v_right)[2]);\r\n\tlua_pushvector(L, (pr_global_struct->v_up)[0], (pr_global_struct->v_up)[1], (pr_global_struct->v_up)[2]);\r\n\treturn 3;\r\n#else\r\n\treturn 0;\r\n#endif\r\n}\r\nstatic int bi_lua_normalize(lua_State *L)\r\n{\r\n\tvec3_t v;\r\n\tlua_readvector(L, 1, v);\r\n\r\n\tVectorNormalize(v);\r\n\r\n\tlua_pushvector(L, v[0], v[1], v[2]);\r\n\treturn 1;\r\n}\r\nstatic int bi_lua_vectoangles(lua_State *L)\r\n{\r\n\tvec3_t forward;\r\n\tvec3_t up;\r\n\tfloat *uv = NULL;\r\n\tvec3_t ret;\r\n\tlua_readvector(L, 1, forward);\r\n\tif (lua_type(L, 2) != LUA_TNONE)\r\n\t{\r\n\t\tlua_readvector(L, 1, up);\r\n\t\tuv = up;\r\n\t}\r\n\tVectorAngles(forward, uv, ret, true);\r\n\r\n\tlua_pushvector(L, ret[0], ret[1], ret[2]);\r\n\treturn 1;\r\n}\r\nstatic int bi_lua_vectoyaw(lua_State *L)\r\n{\r\n\tvec3_t forward;\r\n\tvec3_t up;\r\n\tfloat *uv = NULL;\r\n\tvec3_t ret;\r\n\tlua_readvector(L, 1, forward);\r\n\tif (lua_type(L, 2) != LUA_TNONE)\r\n\t{\r\n\t\tlua_readvector(L, 1, up);\r\n\t\tuv = up;\r\n\t}\r\n\tVectorAngles(forward, uv, ret, true);\r\n\r\n\tlua_pushnumber(L, ret[1]);\r\n\treturn 1;\r\n}\r\n\r\nvoid QCBUILTIN PF_aim (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\r\nstatic int bi_lua_aim(lua_State *L)\r\n{\t//aim builtin is for keyboard users, if they still exist.\r\n\tglobalvars_t pr_globals;\r\n\tlua_getfield(L, 1, \"entnum\");\r\n\tpr_globals.param[0].i = lua_tointeger(L, -1);\r\n\tpr_globals.param[1].f = lua_tonumber(L, 2);\t//speed\r\n\tPF_aim(&lua.progfuncs, &pr_globals);\r\n\tlua_pushvector(L, pr_globals.ret.vec[0], pr_globals.ret.vec[1], pr_globals.ret.vec[2]);\r\n\treturn 1;\r\n}\r\n\r\nstatic int bi_lua_tokenize(lua_State *L)\r\n{\r\n\tconst char *instring = lua_tolstring(L, 1, NULL);\r\n\tint argc = 0;\r\n\tlua_newtable(L);\r\n\twhile(NULL != (instring = COM_Parse(instring)))\r\n\t{\r\n\t\t//lua is traditionally 1-based\r\n\t\t//for i=1,t.argc do\r\n\t\tlua_pushinteger(L, ++argc);\r\n\t\tlua_pushstring(L, com_token);\r\n\t\tlua_settable(L, -3);\r\n\r\n\t\tif (argc == 1)\r\n\t\t{\r\n\t\t\twhile (*instring == ' ' || *instring == '\\t')\r\n\t\t\t\tinstring++;\r\n\t\t\tlua_pushstring(L, instring);\r\n\t\t\tlua_setfield(L, -2, \"args\");\t//args is all-but-the-first\r\n\t\t}\r\n\t}\r\n\tlua_pushinteger(L, argc);\r\n\tlua_setfield(L, -2, \"argc\");\t//argc is the count.\r\n\treturn 1;\r\n}\r\n\r\nstatic int bi_lua_findradiuschain(lua_State *L)\r\n{\r\n\textern cvar_t sv_gameplayfix_blowupfallenzombies;\r\n\tworld_t *world = &sv.world;\r\n\tedict_t\t*ent, *chain;\r\n\tfloat\trad;\r\n\tvec3_t\torg;\r\n\tvec3_t\teorg;\r\n\tint\t\ti, j;\r\n\r\n\tchain = (edict_t *)world->edicts;\r\n\r\n\tlua_readvector(L, 1, org);\r\n\trad = lua_tonumberx(L, 2, NULL);\r\n\trad = rad*rad;\r\n\r\n\tfor (i=1 ; i<world->num_edicts ; i++)\r\n\t{\r\n\t\tent = EDICT_NUM_PB(world->progs, i);\r\n\t\tif (ED_ISFREE(ent))\r\n\t\t\tcontinue;\r\n\t\tif (ent->v->solid == SOLID_NOT && (progstype != PROG_QW || !((int)ent->v->flags & FL_FINDABLE_NONSOLID)) && !sv_gameplayfix_blowupfallenzombies.value)\r\n\t\t\tcontinue;\r\n\t\tfor (j=0 ; j<3 ; j++)\r\n\t\t\teorg[j] = org[j] - (ent->v->origin[j] + (ent->v->mins[j] + ent->v->maxs[j])*0.5);\r\n\t\tif (DotProduct(eorg,eorg) > rad)\r\n\t\t\tcontinue;\r\n\r\n\t\tent->v->chain = EDICT_TO_PROG(world->progs, chain);\r\n\t\tchain = ent;\r\n\t}\r\n\r\n\tlua_pushlightuserdata(L, chain);\r\n\tlua_gettable(L, LUA_REGISTRYINDEX);\r\n\treturn 1;\r\n}\r\nstatic int bi_lua_findradiustable(lua_State *L)\r\n{\r\n\textern cvar_t sv_gameplayfix_blowupfallenzombies;\r\n\tworld_t *world = &sv.world;\r\n\tedict_t\t*ent, *chain;\r\n\tfloat\trad;\r\n\tvec3_t\torg;\r\n\tvec3_t\teorg;\r\n\tint\t\ti, j;\r\n\tint results = 1;\t//lua arrays are 1-based\r\n\r\n\tchain = (edict_t *)world->edicts;\r\n\r\n\tlua_readvector(L, 1, org);\r\n\trad = lua_tonumberx(L, 2, NULL);\r\n\trad = rad*rad;\r\n\r\n\tlua_newtable(L);\t//our return value.\r\n\r\n\tfor (i=1 ; i<world->num_edicts ; i++)\r\n\t{\r\n\t\tent = EDICT_NUM_PB(world->progs, i);\r\n\t\tif (ED_ISFREE(ent))\r\n\t\t\tcontinue;\r\n\t\tif (ent->v->solid == SOLID_NOT && (progstype != PROG_QW || !((int)ent->v->flags & FL_FINDABLE_NONSOLID)) && !sv_gameplayfix_blowupfallenzombies.value)\r\n\t\t\tcontinue;\r\n\t\tfor (j=0 ; j<3 ; j++)\r\n\t\t\teorg[j] = org[j] - (ent->v->origin[j] + (ent->v->mins[j] + ent->v->maxs[j])*0.5);\r\n\t\tif (DotProduct(eorg,eorg) > rad)\r\n\t\t\tcontinue;\r\n\r\n\t\tlua_pushinteger(L, ++results);\r\n\t\tlua_pushlightuserdata(L, ent);\r\n\t\tlua_gettable(L, LUA_REGISTRYINDEX);\r\n\t\tlua_settable(L, -3);\r\n\t}\r\n\r\n\tlua_pushlightuserdata(L, chain);\r\n\tlua_gettable(L, LUA_REGISTRYINDEX);\r\n\treturn 1;\r\n}\r\n#define bi_lua_findradius bi_lua_findradiuschain\r\n\r\n\r\nstatic int bi_lua_find(lua_State *L)\r\n{\r\n\tworld_t *world = &sv.world;\r\n\tedict_t\t*ent;\r\n\tsize_t\ti;\r\n\tconst char *s;\r\n\tconst char *match;\r\n\r\n\tlua_getfield(L, 1, \"entnum\");\r\n\ti = lua_tointegerx(L, -1, NULL)+1;\r\n\tmatch = lua_tostring(L, 3);\r\n\r\n\tfor ( ; i<world->num_edicts ; i++)\r\n\t{\r\n\t\tent = EDICT_NUM_PB(world->progs, i);\r\n\t\tif (ED_ISFREE(ent))\r\n\t\t\tcontinue;\r\n\t\tlua_pushedict(L, ent);\r\n\t\tlua_pushvalue(L, 2);\r\n\t\tlua_gettable(L, -2);\r\n\t\ts = lua_tostring(L, -1);\r\n\t\tif (!s)\r\n\t\t\ts = \"\";\r\n\t\tif (!strcmp(s, match))\t//should probably do a lua comparison, but nils suck\r\n\t\t{\r\n\t\t\tlua_pop(L, 2);\r\n\t\t\tlua_pushedict(L, ent);\r\n\t\t\treturn 1;\r\n\t\t}\r\n\t\tlua_pop(L, 2);\r\n\t}\r\n\r\n\tlua_pushedict(L, EDICT_NUM_PB(world->progs, 0));\r\n\treturn 1;\r\n}\r\n\r\nstatic int bi_lua_multicast(lua_State *L)\r\n{\r\n\tint dest;\r\n\tvec3_t org;\r\n\r\n\tdest = lua_tointegerx(L, 1, NULL);\r\n\tlua_readvector(L, 2, org);\r\n\r\n\tNPP_Flush();\r\n\tSV_Multicast (org, dest);\r\n\r\n\treturn 0;\r\n}\r\nextern sizebuf_t csqcmsgbuffer;\r\nstatic int bi_lua_writechar(lua_State *L)\r\n{\r\n\tglobalvars_t pr_globals;\r\n\tpr_globals.param[0].f = (csqcmsgbuffer.maxsize?MSG_CSQC:MSG_MULTICAST);\r\n\tpr_globals.param[1].f = lua_tonumberx(L, 1, NULL);\r\n\tPF_WriteChar(&lua.progfuncs, &pr_globals);\r\n\treturn 0;\r\n}\r\nstatic int bi_lua_writebyte(lua_State *L)\r\n{\r\n\tglobalvars_t pr_globals;\r\n\tpr_globals.param[0].f = (csqcmsgbuffer.maxsize?MSG_CSQC:MSG_MULTICAST);\r\n\tpr_globals.param[1].f = lua_tonumberx(L, 1, NULL);\r\n\tPF_WriteByte(&lua.progfuncs, &pr_globals);\r\n\treturn 0;\r\n}\r\nstatic int bi_lua_writeshort(lua_State *L)\r\n{\r\n\tglobalvars_t pr_globals;\r\n\tpr_globals.param[0].f = (csqcmsgbuffer.maxsize?MSG_CSQC:MSG_MULTICAST);\r\n\tpr_globals.param[1].f = lua_tonumberx(L, 1, NULL);\r\n\tPF_WriteShort(&lua.progfuncs, &pr_globals);\r\n\treturn 0;\r\n}\r\nstatic int bi_lua_writelong(lua_State *L)\r\n{\r\n\tglobalvars_t pr_globals;\r\n\tpr_globals.param[0].f = (csqcmsgbuffer.maxsize?MSG_CSQC:MSG_MULTICAST);\r\n\tpr_globals.param[1].f = lua_tonumberx(L, 1, NULL);\r\n\tPF_WriteLong(&lua.progfuncs, &pr_globals);\r\n\treturn 0;\r\n}\r\nstatic int bi_lua_writeangle(lua_State *L)\r\n{\r\n\tglobalvars_t pr_globals;\r\n\tpr_globals.param[0].f = (csqcmsgbuffer.maxsize?MSG_CSQC:MSG_MULTICAST);\r\n\tpr_globals.param[1].f = lua_tonumberx(L, 1, NULL);\r\n\tPF_WriteAngle(&lua.progfuncs, &pr_globals);\r\n\treturn 0;\r\n}\r\nstatic int bi_lua_writecoord(lua_State *L)\r\n{\r\n\tglobalvars_t pr_globals;\r\n\tpr_globals.param[0].f = (csqcmsgbuffer.maxsize?MSG_CSQC:MSG_MULTICAST);\r\n\tpr_globals.param[1].f = lua_tonumberx(L, 1, NULL);\r\n\tPF_WriteCoord(&lua.progfuncs, &pr_globals);\r\n\treturn 0;\r\n}\r\nstatic int bi_lua_writestring(lua_State *L)\r\n{\r\n\tPF_WriteString_Internal((csqcmsgbuffer.maxsize?MSG_CSQC:MSG_MULTICAST),lua_tolstring(L, 1, NULL));\r\n\treturn 0;\r\n}\r\nstatic int bi_lua_writeentity(lua_State *L)\r\n{\r\n\tglobalvars_t pr_globals;\r\n\tlua_getfield(L, 1, \"entnum\");\r\n\tpr_globals.param[0].f = (csqcmsgbuffer.maxsize?MSG_CSQC:MSG_MULTICAST);\r\n\tpr_globals.param[1].i = lua_tointegerx(L, -1, NULL);\r\n\tPF_WriteEntity(&lua.progfuncs, &pr_globals);\r\n\treturn 0;\r\n}\r\n\r\nstatic int bi_lua_WriteChar(lua_State *L)\r\n{\r\n\tglobalvars_t pr_globals;\r\n\tpr_globals.param[0].f = lua_tonumberx(L, 1, NULL);\r\n\tpr_globals.param[1].f = lua_tonumberx(L, 2, NULL);\r\n\tPF_WriteChar(&lua.progfuncs, &pr_globals);\r\n\treturn 0;\r\n}\r\nstatic int bi_lua_WriteByte(lua_State *L)\r\n{\r\n\tglobalvars_t pr_globals;\r\n\tpr_globals.param[0].f = lua_tonumberx(L, 1, NULL);\r\n\tpr_globals.param[1].f = lua_tonumberx(L, 2, NULL);\r\n\tPF_WriteByte(&lua.progfuncs, &pr_globals);\r\n\treturn 0;\r\n}\r\nstatic int bi_lua_WriteShort(lua_State *L)\r\n{\r\n\tglobalvars_t pr_globals;\r\n\tpr_globals.param[0].f = lua_tonumberx(L, 1, NULL);\r\n\tpr_globals.param[1].f = lua_tonumberx(L, 2, NULL);\r\n\tPF_WriteShort(&lua.progfuncs, &pr_globals);\r\n\treturn 0;\r\n}\r\nstatic int bi_lua_WriteLong(lua_State *L)\r\n{\r\n\tglobalvars_t pr_globals;\r\n\tpr_globals.param[0].f = lua_tonumberx(L, 1, NULL);\r\n\tpr_globals.param[1].f = lua_tonumberx(L, 2, NULL);\r\n\tPF_WriteLong(&lua.progfuncs, &pr_globals);\r\n\treturn 0;\r\n}\r\nstatic int bi_lua_WriteAngle(lua_State *L)\r\n{\r\n\tglobalvars_t pr_globals;\r\n\tpr_globals.param[0].f = lua_tonumberx(L, 1, NULL);\r\n\tpr_globals.param[1].f = lua_tonumberx(L, 2, NULL);\r\n\tPF_WriteAngle(&lua.progfuncs, &pr_globals);\r\n\treturn 0;\r\n}\r\nstatic int bi_lua_WriteCoord(lua_State *L)\r\n{\r\n\tglobalvars_t pr_globals;\r\n\tpr_globals.param[0].f = lua_tonumberx(L, 1, NULL);\r\n\tpr_globals.param[1].f = lua_tonumberx(L, 2, NULL);\r\n\tPF_WriteCoord(&lua.progfuncs, &pr_globals);\r\n\treturn 0;\r\n}\r\nstatic int bi_lua_WriteString(lua_State *L)\r\n{\r\n\tPF_WriteString_Internal(lua_tonumberx(L, 1, NULL),lua_tolstring(L, 2, NULL));\r\n\treturn 0;\r\n}\r\nstatic int bi_lua_WriteEntity(lua_State *L)\r\n{\r\n\tglobalvars_t pr_globals;\r\n\tlua_getfield(L, 2, \"entnum\");\r\n\tpr_globals.param[0].f = lua_tonumberx(L, 1, NULL);\r\n\tpr_globals.param[1].i = lua_tointegerx(L, -1, NULL);\r\n\tPF_WriteEntity(&lua.progfuncs, &pr_globals);\r\n\treturn 0;\r\n}\r\n\r\nvoid QCBUILTIN PF_particle (pubprogfuncs_t *prinst, globalvars_t *pr_globals);\r\nstatic int bi_lua_particle(lua_State *L)\r\n{\r\n\tglobalvars_t pr_globals;\r\n\tlua_readvector(L, 1, pr_globals.param[0].vec);\r\n\tlua_readvector(L, 2, pr_globals.param[1].vec);\r\n\tpr_globals.param[2].f = lua_tonumberx(L, 3, NULL);\r\n\tpr_globals.param[3].f = lua_tonumberx(L, 4, NULL);\r\n\tPF_particle(&lua.progfuncs, &pr_globals);\r\n\treturn 0;\r\n}\r\nstatic int bi_lua_ChangeYaw(lua_State *L)\r\n{\r\n\tPF_changeyaw(&lua.progfuncs, NULL);\r\n\treturn 0;\r\n}\r\n\r\n\r\nstatic int bi_lua_bitnot(lua_State *L)\r\n{\r\n\tlua_pushinteger(L, ~lua_tointegerx(L, 1, NULL));\r\n\treturn 1;\r\n}\r\nstatic int bi_lua_bitclear(lua_State *L)\r\n{\r\n\tlua_pushinteger(L, lua_tointegerx(L, 1, NULL)&~lua_tointegerx(L, 2, NULL));\r\n\treturn 1;\r\n}\r\nstatic int bi_lua_bitset(lua_State *L)\r\n{\r\n\tlua_pushnumber(L, lua_tointegerx(L, 1, NULL)|lua_tointegerx(L, 2, NULL));\r\n\treturn 1;\r\n}\r\n#define bi_lua_bitor bi_lua_bitset\r\nstatic int bi_lua_bitand(lua_State *L)\r\n{\r\n\tlua_pushnumber(L, lua_tointegerx(L, 1, NULL)&lua_tointegerx(L, 2, NULL));\r\n\treturn 1;\r\n}\r\nstatic int bi_lua_bitxor(lua_State *L)\r\n{\r\n\tlua_pushnumber(L, lua_tointegerx(L, 1, NULL)^lua_tointegerx(L, 2, NULL));\r\n\treturn 1;\r\n}\r\n\r\nstatic int bi_lua_sin(lua_State *L)\r\n{\r\n\tlua_pushnumber(L, sin(lua_tonumberx(L, 1, NULL)));\r\n\treturn 1;\r\n}\r\nstatic int bi_lua_cos(lua_State *L)\r\n{\r\n\tlua_pushnumber(L, cos(lua_tonumberx(L, 1, NULL)));\r\n\treturn 1;\r\n}\r\nstatic int bi_lua_tan(lua_State *L)\r\n{\r\n\tlua_pushnumber(L, tan(lua_tonumberx(L, 1, NULL)));\r\n\treturn 1;\r\n}\r\nstatic int bi_lua_atan2(lua_State *L)\r\n{\r\n\tlua_pushnumber(L, atan2(lua_tonumberx(L, 1, NULL), lua_tonumberx(L, 2, NULL)));\r\n\treturn 1;\r\n}\r\nstatic int bi_lua_sqrt(lua_State *L)\r\n{\r\n\tlua_pushnumber(L, sin(lua_tonumberx(L, 1, NULL)));\r\n\treturn 1;\r\n}\r\nstatic int bi_lua_pow(lua_State *L)\r\n{\r\n\tlua_pushnumber(L, pow(lua_tonumberx(L, 1, NULL), lua_tonumberx(L, 2, NULL)));\r\n\treturn 1;\r\n}\r\nstatic int bi_lua_floor(lua_State *L)\r\n{\r\n\tlua_pushnumber(L, floor(lua_tonumberx(L, 1, NULL)));\r\n\treturn 1;\r\n}\r\nstatic int bi_lua_rint(lua_State *L)\r\n{\t//C rounds towards 0, so bias away from 0 by 0.5 and we'll get the right rounded value.\r\n\tlua_Number f = lua_tonumberx(L, 1, NULL);\r\n\tif (f < 0)\r\n\t\tlua_pushinteger(L, f - 0.5);\r\n\telse\r\n\t\tlua_pushinteger(L, f + 0.5);\r\n\treturn 1;\r\n}\r\nstatic int bi_lua_ceil(lua_State *L)\r\n{\r\n\tlua_pushnumber(L, ceil(lua_tonumberx(L, 1, NULL)));\r\n\treturn 1;\r\n}\r\nstatic int bi_lua_fabs(lua_State *L)\r\n{\r\n\tlua_pushnumber(L, fabs(lua_tonumberx(L, 1, NULL)));\r\n\treturn 1;\r\n}\r\nstatic int bi_lua_acos(lua_State *L)\r\n{\r\n\tlua_pushnumber(L, acos(lua_tonumberx(L, 1, NULL)));\r\n\treturn 1;\r\n}\r\nstatic int bi_lua_asin(lua_State *L)\r\n{\r\n\tlua_pushnumber(L, asin(lua_tonumberx(L, 1, NULL)));\r\n\treturn 1;\r\n}\r\nstatic int bi_lua_atan(lua_State *L)\r\n{\r\n\tlua_pushnumber(L, atan(lua_tonumberx(L, 1, NULL)));\r\n\treturn 1;\r\n}\r\n\r\ntypedef struct\r\n{\r\n\tlua_State *L;\r\n\tint idx;\r\n} luafsenum_t;\r\nstatic int QDECL lua_file_enumerate(const char *fname, qofs_t fsize, time_t mtime, void *param, searchpathfuncs_t *spath)\r\n{\r\n\tluafsenum_t *e = param;\r\n\tlua_pushinteger(e->L, e->idx++);\r\n\tlua_pushfstring(e->L, \"%s\", fname);\r\n\tlua_settable(e->L, -3);\r\n\treturn true;\r\n}\r\nstatic int bi_lua_getfilelist(lua_State *L)\r\n{\r\n\tluafsenum_t e;\r\n\tconst char *path = lua_tolstring(L, 1, NULL);\r\n\te.L = L;\r\n\te.idx = 1;\t//lua arrays are 1-based.\r\n\r\n\tlua_newtable(L);\t//our return value.\r\n\tCOM_EnumerateFiles(path, lua_file_enumerate, &e);\r\n\treturn 1;\r\n}\r\n\r\nstatic int bi_lua_fclose(lua_State *L)\r\n{\r\n\t//both fclose and __gc.\r\n\t//should we use a different function so that we can warn on dupe fcloses without bugging out on fclose+gc?\r\n\t//meh, cba\r\n\tvfsfile_t **f = lua_touserdata(L, 1);\r\n\tif (f && *f != NULL)\r\n\t{\r\n\t\tVFS_CLOSE(*f);\r\n\t\t*f = NULL;\r\n\t}\r\n\treturn 0;\r\n}\r\nstatic int bi_lua_fopen(lua_State *L)\r\n{\r\n\tvfsfile_t *f;\r\n\tconst char *fname = lua_tolstring(L, 1, NULL);\r\n\tqboolean read = true;\r\n\tvfsfile_t **ud;\r\n\tif (read)\r\n\t\tf = FS_OpenVFS(fname, \"rb\", FS_GAME);\r\n\telse\r\n\t\tf = FS_OpenVFS(fname, \"wb\", FS_GAMEONLY);\r\n\tif (!f)\r\n\t{\r\n\t\tlua_pushnil(L);\r\n\t\treturn 1;\r\n\t}\r\n\tud = lua_newuserdata(L, sizeof(vfsfile_t*));\r\n\t*ud = f;\r\n\t\r\n\tlua_newtable(L);\r\n\tlua_pushcclosure(L, bi_lua_fclose, 0);\r\n\tlua_setfield(L, -2, \"__gc\");\r\n\tlua_setmetatable(L, -2);\r\n\treturn 1;\r\n}\r\nstatic int bi_lua_fgets(lua_State *L)\r\n{\r\n\tvfsfile_t **f = lua_touserdata(L, 1);\r\n\tchar line[8192];\r\n\tchar *r = NULL;\r\n\tif (f && *f)\r\n\t\tr = VFS_GETS(*f, line, sizeof(line));\r\n\r\n\tif (r)\r\n\t\tlua_pushfstring(L, \"%s\", r);\r\n\telse\r\n\t\tlua_pushnil(L);\r\n\treturn 1;\r\n}\r\nstatic int bi_lua_fputs(lua_State *L)\r\n{\r\n\tvfsfile_t **f = lua_touserdata(L, 1);\r\n\tsize_t l;\r\n\tconst char *str = lua_tolstring(L, 2, &l);\r\n\tif (f && *f != NULL)\r\n\t\tVFS_WRITE(*f, str, l);\r\n\treturn 0;\r\n}\r\n\r\nstatic int bi_lua_loadlua(lua_State *L)\r\n{\r\n\tconst char *fname = lua_tolstring(L, 1, NULL);\r\n\tvfsfile_t *sourcefile = FS_OpenVFS(fname, \"rb\", FS_GAME);\r\n\tif (!sourcefile)\r\n\t{\r\n\t\tCon_Printf(\"Error trying to load %s\\n\", fname);\r\n\t\tlua_pushnil(L);\r\n\t}\r\n\telse if (0 != lua_load(L, my_lua_Reader, sourcefile, va(\"@%s\", fname), \"bt\"))\t//load the file, embed it within a function and push it\r\n\t{\r\n\t\tCon_Printf(\"Error trying to parse %s: %s\\n\", fname, lua_tolstring(L, -1, NULL));\r\n\t\tlua_pushnil(L);\r\n\t}\r\n\tVFS_CLOSE(sourcefile);\r\n\treturn 1;\r\n}\r\n\r\nstatic int bi_lua_require(lua_State *L)\r\n{\r\n\tconst char *fname = lua_tolstring(L, 1, NULL);\r\n\tvfsfile_t *sourcefile;\r\n\tconst char *usename;\r\n\tif ((sourcefile = FS_OpenVFS(usename=fname, \"rb\", FS_GAME)))\r\n\t\t;\r\n\telse if ((sourcefile = FS_OpenVFS(usename=va(\"%s.lua\", fname), \"rb\", FS_GAME)))\r\n\t\t;\r\n\telse\r\n\t{\r\n\t\tCon_Printf(\"Error trying to load %s\\n\", fname);\r\n\t\treturn 1;\r\n\t}\r\n\r\n\tif (0 != lua_load(lua.ctx, my_lua_Reader, sourcefile, va(\"@%s\", usename), \"bt\"))\t//load the file, embed it within a function and push it\r\n\t{\r\n\t\t//failed - it pushed an error code instead\r\n\t\tCon_Printf(\"Error trying to parse %s: %s\\n\", fname, lua_tolstring(lua.ctx, -1, NULL));\r\n\t\tlua_pop(lua.ctx, 1);\r\n\t}\r\n\telse\r\n\t{\r\n\t\tif (lua_pcall(lua.ctx, 0, 0, 0) != 0)\t//now call it, so its actually run.\r\n\t\t{\r\n\t\t\tconst char *s = lua_tolstring(lua.ctx, -1, NULL);\r\n\t\t\tCon_Printf(CON_WARNING \"%s\\n\", s);\r\n\t\t\tlua_pop(lua.ctx, 1);\r\n\t\t}\r\n\t}\r\n\tVFS_CLOSE(sourcefile);\r\n\treturn 0;\r\n}\r\n\r\nstatic int bi_lua_vec3(lua_State *L)\r\n{\r\n\tfloat x = lua_tonumberx(L, 1, NULL);\r\n\tfloat y = lua_tonumberx(L, 2, NULL);\r\n\tfloat z = lua_tonumberx(L, 3, NULL);\r\n\r\n\tlua_pushvector(L, x, y, z);\r\n\treturn 1;\r\n}\r\nstatic int bi_lua_field(lua_State *L)\r\n{\r\n\tconst char *fname = lua_tostring(L, 1);\r\n\tconst char *ftype = lua_tostring(L, 2);\r\n\tint t;\r\n\tsize_t u;\r\n\r\n\tif (!strcmp(ftype, \"string\"))\r\n\t\tt = ev_string;\r\n\telse if (!strcmp(ftype, \"float\"))\r\n\t\tt = ev_float;\r\n\telse if (!strcmp(ftype, \"integer\"))\r\n\t\tt = ev_integer;\r\n\telse if (!strcmp(ftype, \"vector\"))\r\n\t\tt = ev_vector;\r\n\telse if (!strcmp(ftype, \"function\"))\r\n\t\tt = ev_function;\r\n\telse if (!strcmp(ftype, \"entity\"))\r\n\t\tt = ev_entity;\r\n\telse\r\n\t{\r\n\t\tCon_Printf(\"Unknown field type\\n\");\r\n\t\treturn 0;\r\n\t}\r\n\r\n\tif (lua.numflds == countof(lua.entflds))\r\n\t{\r\n\t\tCon_Printf(\"Too many ent fields\\n\");\r\n\t\treturn 0;\r\n\t}\r\n\r\n\t//no dupes please.\r\n\tfor (u = 0; u < lua.numflds; u++)\r\n\t{\r\n\t\tif (!strcmp(lua.entflds[u].name, fname))\r\n\t\t\treturn 0;\r\n\t}\r\n\r\n\tlua.entflds[lua.numflds].offset = -1;\t//if we don't know about it yet, then its an artificial field, and present just so that self.whatever returns something other than nil.\r\n\tlua.entflds[lua.numflds].name = Lua_AddString(NULL, fname, 0, false);\r\n\tlua.entflds[lua.numflds].type = t;\r\n\tHash_AddInsensitive(&lua.entityfields, lua.entflds[lua.numflds].name, &lua.entflds[lua.numflds], &lua.entflds[lua.numflds].buck);\r\n\tlua.numflds++;\r\n\r\n\treturn 0;\r\n}\r\nstatic int bi_lua_type(lua_State *L)\r\n{\r\n\tconst char *tn;\r\n\tint t = lua_type(L, 1);\r\n//\tluaL_argcheck(L, t != LUA_TNONE, 1, \"value expected\");\r\n\ttn = lua_typename(L, t);\r\n\tlua_pushstring(L, tn);\r\n\treturn 1;\r\n}\r\n\r\nstatic int bi_lua_logfrag(lua_State *L)\r\n{\r\n\t//stub... noone cares\r\n\treturn 0;\r\n}\r\n\r\n/*static int bi_lua_entities(lua_State *L)\r\n{\r\n\t//stub... noone cares\r\n\treturn 0;\r\n}*/\r\n\r\n#ifdef LIBLUA_STATIC\r\nstatic int pairsmeta (lua_State *L, const char *method, int iszero,\r\n                      lua_CFunction iter) {\r\n  luaL_checkany(L, 1);\r\n  if (luaL_getmetafield(L, 1, method) == LUA_TNIL) {  /* no metamethod? */\r\n    lua_pushcfunction(L, iter);  /* will return generator, */\r\n    lua_pushvalue(L, 1);  /* state, */\r\n    if (iszero) lua_pushinteger(L, 0);  /* and initial value */\r\n    else lua_pushnil(L);\r\n  }\r\n  else {\r\n    lua_pushvalue(L, 1);  /* argument 'self' to metamethod */\r\n    lua_call(L, 1, 3);  /* get 3 values from metamethod */\r\n  }\r\n  return 3;\r\n}\r\nstatic int luaB_next (lua_State *L) {\r\n  luaL_checktype(L, 1, LUA_TTABLE);\r\n  lua_settop(L, 2);  /* create a 2nd argument if there isn't one */\r\n  if (lua_next(L, 1))\r\n    return 2;\r\n  else {\r\n    lua_pushnil(L);\r\n    return 1;\r\n  }\r\n}\r\nstatic int bi_lua_pairs (lua_State *L) {\r\n  return pairsmeta(L, \"__pairs\", 0, luaB_next);\r\n}\r\n#endif\r\n\r\nstatic int bi_lua_objerror (lua_State *L)\r\n{\r\n\tCon_Printf(\"\\n\");\r\n\tlua_pushedict(L, lua.edicttable[lua.globals.self]);\r\n\r\n\tlua_pushnil(L);\r\n\twhile (lua_next(L, -2))\r\n\t{\t//FIXME: this doesn't find vector fields\r\n\t\tif (lua_type(L, -2) == LUA_TSTRING)\r\n\t\t\tCon_Printf(\"%21s:\", lua_tostring(L, -2));\r\n\t\telse if (lua_type(L, -2) == LUA_TLIGHTUSERDATA)\r\n\t\t{\r\n\t\t\tint i;\r\n\t\t\tconst void *ud = lua_topointer(L, -2);\r\n\t\t\tfor (i = 0; i < lua.numflds; i++)\r\n\t\t\t\tif (lua.entflds[i].offset == (qintptr_t)ud)\r\n\t\t\t\t\tbreak;\r\n\r\n\t\t\tif (i == lua.numflds)\r\n\t\t\t\tCon_Printf(\"%21s:\", lua_typename(L, lua_type(L, -2)));\r\n\t\t\telse\r\n\t\t\t\tCon_Printf(\"%21s:\", lua.entflds[i].name);\r\n\t\t}\r\n\t\telse\r\n\t\t\tCon_Printf(\"%21s:\", lua_typename(L, lua_type(L, -2)));\r\n\t\tif (lua_type(L, -1) == LUA_TSTRING)\r\n\t\t\tCon_Printf(\" \\\"%s\\\"\\n\", lua_tostring(L, -1));\r\n\t\telse if (lua_type(L, -1) == LUA_TNUMBER)\r\n\t\t\tCon_Printf(\" \\\"%g\\\"\\n\", lua_tonumber(L, -1));\r\n#ifdef LIBLUA_STATIC\r\n\t\telse if (lua_type(L, -1) == LUA_TFUNCTION)\r\n\t\t{\r\n\t\t\tlua_Debug ar = {0};\r\n\t\t\tlua_pushvalue(L, -1);\r\n\t\t\tlua_getinfo(L, \">nS\", &ar);\r\n\t\t\tCon_Printf(\" %s %s:%i\\n\", ar.name, ar.source, ar.linedefined);\r\n\t\t}\r\n#endif\r\n\t\telse\r\n\t\t\tCon_Printf(\" %s\\n\", lua_typename(L, lua_type(L, -1)));\r\n\t\tlua_pop(L, 1);\r\n\t}\r\n\r\n\tlua_pop(L, 1);\r\n\r\n\tCon_Printf(\"objerror: %s\\n\", lua_tostring(L, 1));\r\n\treturn 0;\r\n}\r\nstatic int bi_lua_error (lua_State *L)\r\n{\r\n\tSV_Error(\"Error: %s\\n\", lua_tostring(L, 1));\r\n\treturn 0;\r\n}\r\nstatic int bi_lua_qtrue (lua_State *L)\r\n{\r\n\t//the truth is what you make it...\r\n\r\n\tconst char *s;\r\n\r\n\tswitch(lua_type(L, 1))\r\n\t{\r\n\tcase LUA_TSTRING:\r\n\t\ts = lua_tostring(L, 1);\r\n\t\tlua_pushboolean(L, *s != 0);\t//empty string is considered false. WARNING: vanilla QC considered empty-but-not-null strings to be true\r\n\t\tbreak;\r\n\r\n\tdefault:\r\n//\tcase LUA_TUSERDATA:\r\n//\tcase LUA_TTHREAD:\r\n//\tcase LUA_TLIGHTUSERDATA:\r\n//\tcase LUA_TNONE:\r\n//\tcase LUA_TNIL:\r\n\t\tlua_pushboolean(L, false);\r\n\t\tbreak;\r\n\tcase LUA_TBOOLEAN:\r\n\t\tlua_pushvalue(L, 1);\r\n\t\tbreak;\r\n\r\n\tcase LUA_TNUMBER:\r\n\t\tlua_pushboolean(L, lua_tonumber(L, 1) != 0);\r\n\t\tbreak;\r\n\r\n\tcase LUA_TFUNCTION:\t//functions are always considered true. otherwise they're nil and not functions.\r\n\t\tlua_pushboolean(L, true);\r\n\t\tbreak;\r\n\tcase LUA_TTABLE:\r\n\t\t//might be a vector or an entity.\r\n\t\tlua_getfield(L, 1, \"entnum\");\r\n\t\tif (!lua_isnil(L, -1))\r\n\t\t{\t//okay, so its a table with a valid entnum field. must be an entity.\r\n\t\t\tlua_pushboolean(L, 0!=lua_tointegerx(L, -1, NULL));\t//true if its not 0/world\r\n\t\t}\r\n\t\telse\r\n\t\t{\t//assume that its a vector.\r\n\t\t\t//note that this means that any table without x|y|z fields will be considered false.\r\n\t\t\tvec3_t v;\r\n\t\t\tlua_readvector(L, 1, v);\r\n\t\t\tlua_pushboolean(L, v[0] || v[1] || v[2]);\r\n\t\t}\r\n\t\tbreak;\r\n\t}\r\n\treturn 1;\r\n}\r\n\r\n#define registerfunc(n) lua_pushcclosure(L, bi_lua_##n, 0); lua_setglobal(L, #n);\r\n#define registerfuncn(n) registerfunc(n)\t//new crap\r\n#define registerfuncd(n) registerfunc(n)\t//deprecated crap\r\nstatic void my_lua_registerbuiltins(lua_State *L)\r\n{\r\n\tlua_atpanic (L, my_lua_panic);\r\n\r\n\tlua_pushglobaltable(L);\r\n\tlua_setfield(L, -1, \"_G\");\r\n\r\n\t//standard lua library replacement\r\n\t//this avoids the risk of including any way to access os.execute etc, or other file access.\r\n\tregisterfuncn(tostring);\t//standardish\r\n\tregisterfuncn(tonumber);\t//standardish\r\n\tregisterfuncn(type);\t\t//standardish\r\n\tregisterfuncn(print);\t\t//'standard' lua print, except prints to console. WARNING: this adds an implicit \\n. Use conprint for the quake-style version.\r\n\tregisterfuncn(require);\t\t//'standard'ish, except uses quake's filesystem instead of reading random system paths.\r\n#ifdef LIBLUA_STATIC\r\n\tregisterfuncn(pairs);\r\n#endif\r\n\tlua_pushnil(L); lua_setglobal(L, \"dofile\");\t//violates our sandbox\r\n\tlua_pushnil(L); lua_setglobal(L, \"loadfile\"); //violates our sandbox\r\n\r\n\tlua_newtable(L);\r\n\t\tlua_pushcclosure(L, bi_lua_fabs, 0); lua_setfield(L, -2, \"abs\");\r\n\t\tlua_pushcclosure(L, bi_lua_rint, 0); lua_setfield(L, -2, \"rint\");\r\n\t\tlua_pushcclosure(L, bi_lua_ceil, 0); lua_setfield(L, -2, \"ceil\");\r\n\t\tlua_pushcclosure(L, bi_lua_floor, 0); lua_setfield(L, -2, \"floor\");\r\n\t\tlua_pushcclosure(L, bi_lua_sin, 0); lua_setfield(L, -2, \"sin\");\r\n\t\tlua_pushcclosure(L, bi_lua_cos, 0); lua_setfield(L, -2, \"cos\");\r\n\t\tlua_pushcclosure(L, bi_lua_tan, 0); lua_setfield(L, -2, \"tan\");\r\n\t\tlua_pushcclosure(L, bi_lua_asin, 0); lua_setfield(L, -2, \"asin\");\r\n\t\tlua_pushcclosure(L, bi_lua_acos, 0); lua_setfield(L, -2, \"acos\");\r\n\t\tlua_pushcclosure(L, bi_lua_atan, 0); lua_setfield(L, -2, \"atan\");\r\n\t\tlua_pushcclosure(L, bi_lua_atan2, 0); lua_setfield(L, -2, \"atan2\");\r\n\t\tlua_pushcclosure(L, bi_lua_sqrt, 0); lua_setfield(L, -2, \"sqrt\");\r\n\t\tlua_pushcclosure(L, bi_lua_pow, 0); lua_setfield(L, -2, \"pow\");\r\n\t\tlua_pushnumber  (L, M_PI);\t\t\tlua_setfield(L, -2, \"pi\");\r\n\tlua_setglobal(L, \"math\");\r\n\r\n//\tregisterfuncd(entities);\r\n\r\n\tregisterfuncd(loadlua);\t//should probably use 'require' instead.\r\n\tregisterfuncn(vec3);\r\n\tregisterfuncn(field);\r\n#undef qtrue\r\n\tregisterfuncn(qtrue);\t//for auto-converted code that tests for truth amongst a myriad of different custom types...\r\n\r\n\tregisterfunc(setmodel);\r\n\tregisterfunc(precache_model);\r\n\tregisterfuncd(precache_model2);\t//pointless alternative name\r\n\tregisterfunc(precache_sound);\r\n\tregisterfuncd(precache_sound2);\t//pointless alternative name\r\n\tregisterfuncd(precache_file);\t//empty pointless function...\r\n\tregisterfunc(lightstyle);\r\n\tregisterfunc(spawn);\r\n\tregisterfunc(remove);\r\n\tregisterfunc(nextent);\r\n\tregisterfunc(nextclient);\r\n\tregisterfunc(makestatic);\r\n\tregisterfunc(setorigin);\r\n\tregisterfunc(setsize);\r\n\r\n\tregisterfuncn(conprint);\t//dprint, without the developer\r\n\r\n\tregisterfunc(bprint_qw);\r\n\tregisterfunc(sprint_qw);\r\n\tif (progstype == PROG_QW)\r\n\t{\r\n\t\tlua_pushcclosure(L, bi_lua_conprint, 0); lua_setglobal(L, \"dprint\");\t//ignores developer\r\n\t\tlua_pushcclosure(L, bi_lua_bprint_qw, 0); lua_setglobal(L, \"bprint\");\t//an extra level arg\r\n\t\tlua_pushcclosure(L, bi_lua_sprint_qw, 0); lua_setglobal(L, \"sprint\");\t//an extra level arg\r\n\t}\r\n\telse\r\n\t{\r\n\t\tlua_pushcclosure(L, bi_lua_dprint, 0); lua_setglobal(L, \"dprint\");\t//responds to developer\r\n\t\tlua_pushcclosure(L, bi_lua_bprint_nq, 0); lua_setglobal(L, \"bprint\");\t//no level arg\r\n\t\tlua_pushcclosure(L, bi_lua_sprint_nq, 0); lua_setglobal(L, \"sprint\");\t//no level arg\r\n\t}\r\n\tregisterfunc(centerprint);\r\n\tregisterfunc(ambientsound);\r\n\tregisterfunc(sound);\r\n\tregisterfunc(random);\r\n\tregisterfunc(checkclient);\r\n\tregisterfunc(stuffcmd);\r\n\tregisterfunc(localcmd);\r\n\tregisterfuncd(cvar);\t\t\t//gets a float value, never a string.\r\n\tregisterfuncn(cvar_get);\r\n\tregisterfunc(cvar_set);\r\n\tregisterfuncd(findradius);\t\t//qc legacy compat. should probably warn when its called or sommit.\r\n\tregisterfuncn(findradiuschain);\t//renamed from qc. because qc's behaviour is annoying enough that we want to discourage its use.\r\n\tregisterfuncn(findradiustable);\t//findradius, but returns an array/table instead.\r\n\r\n\tregisterfunc(objerror);\r\n\tregisterfunc(error);\r\n//\tregisterfuncd(break);\t//won't implement\r\n\r\n\tregisterfunc(traceline);\r\n\tregisterfuncn(tracebox);\r\n\tregisterfunc(walkmove);\r\n\tregisterfunc(movetogoal);\r\n\tregisterfunc(droptofloor);\r\n\tregisterfunc(checkbottom);\r\n\tregisterfunc(pointcontents);\r\n\tregisterfuncd(ChangeYaw);\r\n\r\n\tregisterfunc(setspawnparms);\r\n\tregisterfunc(changelevel);\r\n\tregisterfunc(logfrag);\r\n\tregisterfuncd(infokey);\r\n\tregisterfuncn(getinfokey);\r\n\tregisterfuncn(setinfokey);\r\n\tregisterfunc(multicast);\r\n\tregisterfuncn(writebyte);\r\n\tregisterfuncn(writechar);\r\n\tregisterfuncn(writeshort);\r\n\tregisterfuncn(writelong);\r\n\tregisterfuncn(writeangle);\r\n\tregisterfuncn(writecoord);\r\n\tregisterfuncn(writestring);\r\n\tregisterfuncn(writeentity);\r\n\tregisterfuncd(WriteByte);\r\n\tregisterfuncd(WriteChar);\r\n\tregisterfuncd(WriteShort);\r\n\tregisterfuncd(WriteLong);\r\n\tregisterfuncd(WriteAngle);\r\n\tregisterfuncd(WriteCoord);\r\n\tregisterfuncd(WriteString);\r\n\tregisterfuncd(WriteEntity);\r\n\tregisterfuncd(particle);\r\n\tregisterfuncn(bitnot);\r\n\tregisterfuncn(bitclear);\r\n\tregisterfuncn(bitset);\r\n\tregisterfuncn(bitor);\r\n\tregisterfuncn(bitand);\r\n\tregisterfuncn(bitxor);\r\n\tregisterfuncn(sin);\r\n\tregisterfuncn(cos);\r\n\tregisterfuncn(acos);\r\n\tregisterfuncn(asin);\r\n\tregisterfuncn(atan);\r\n\tregisterfuncn(atan2);\r\n\tregisterfuncn(sqrt);\r\n\tregisterfuncn(pow);\r\n\tregisterfunc(floor);\r\n\tregisterfunc(ceil);\r\n\tregisterfunc(rint);\r\n\tregisterfunc(fabs);\r\n\tregisterfuncn(fopen);\r\n\tregisterfuncn(fclose);\r\n\tregisterfuncn(fgets);\r\n\tregisterfuncn(fputs);\r\n\tregisterfuncn(getfilelist);\r\n\tregisterfuncd(find);\r\n\r\n\t//registerfunc(strftime);\r\n\tregisterfunc(tokenize);\r\n\tregisterfunc(makevectors);\r\n\tregisterfunc(normalize);\r\n\tregisterfunc(vectoangles);\r\n\tregisterfunc(vectoyaw);\r\n\tregisterfuncd(aim);\t//original implementation nudges v_forward up or down so that keyboard players can still play.\r\n\tregisterfunc(vtos);\r\n\tregisterfunc(ftos);\r\n\tregisterfunc(stof);\r\n\r\n\t//registerfunc(PRECACHE_VWEP_MODEL);\r\n\t//registerfunc(SETPAUSE);\r\n\r\n\r\n\t//set a metatable on the globals table\r\n\t//this means that we can just directly use self.foo instead of blob.self.foo\r\n\tlua_pushglobaltable(L);\r\n\tif (luaL_newmetatable(L, \"globals\"))\r\n\t{\r\n\t\tlua_pushcclosure(L, my_lua_global_set, 0);\t//for the luls.\r\n\t\tlua_setfield (L, -2, \"__newindex\");\r\n\r\n\t\tlua_pushcclosure(L, my_lua_global_get, 0);\t//for the luls.\r\n\t\tlua_setfield (L, -2, \"__index\");\r\n\t}\r\n\tlua_setmetatable(L, -2);\r\n\tlua_pop(L, 1);\r\n\r\n\tif (luaL_newmetatable(L, \"vec3_t\"))\r\n\t{\r\n//\t\tlua_pushcclosure(L, my_lua_vec3_set, 0);\t//known writes should change the internal info so the engine can use the information.\r\n//\t\tlua_setfield (L, -2, \"__newindex\");\r\n\r\n//\t\tlua_pushcclosure(L, my_lua_vec3_get, 0);\t//we need to de-translate the engine's fields too.\r\n//\t\tlua_setfield (L, -2, \"__index\");\r\n\r\n\t\tlua_pushcclosure(L, my_lua_vec3_tostring, 0);\t//cos its prettier than seeing 'table 0x5425729' all over the place\r\n\t\tlua_setfield (L, -2, \"__tostring\");\r\n\r\n\t\tlua_pushcclosure(L, my_lua_vec3_eq, 0);\t//for comparisons, you know?\r\n\t\tlua_setfield (L, -2, \"__eq\");\r\n\r\n\t\tlua_pushcclosure(L, my_lua_vec3_add, 0);\t//for comparisons, you know?\r\n\t\tlua_setfield (L, -2, \"__add\");\r\n\r\n\t\tlua_pushcclosure(L, my_lua_vec3_sub, 0);\t//for comparisons, you know?\r\n\t\tlua_setfield (L, -2, \"__sub\");\r\n\r\n\t\tlua_pushcclosure(L, my_lua_vec3_mul, 0);\t//for comparisons, you know?\r\n\t\tlua_setfield (L, -2, \"__mul\");\r\n\r\n\t\tlua_pushcclosure(L, my_lua_vec3_len, 0);\t//for comparisons, you know?\r\n\t\tlua_setfield (L, -2, \"__len\");\r\n\r\n\t\tlua_pop(L, 1);\r\n\t}\r\n}\r\n\r\n\r\n\r\n\r\n\r\n\r\nstatic edict_t *QDECL Lua_EdictNum(pubprogfuncs_t *pf, unsigned int num)\r\n{\r\n\tint newcount;\r\n\tif (num >= lua.maxedicts)\r\n\t{\r\n\t\tnewcount = num + 64;\r\n\t\tlua.edicttable = realloc(lua.edicttable, newcount*sizeof(*lua.edicttable));\r\n\t\twhile(lua.maxedicts < newcount)\r\n\t\t\tlua.edicttable[lua.maxedicts++] = NULL;\r\n\r\n\t\tpf->edicttable_length = lua.maxedicts;\r\n\t\tpf->edicttable = lua.edicttable;\r\n\t}\r\n\treturn lua.edicttable[num];\r\n}\r\nstatic unsigned int QDECL Lua_NumForEdict(pubprogfuncs_t *pf, edict_t *e)\r\n{\r\n\treturn e->entnum;\r\n}\r\nstatic int QDECL Lua_EdictToProgs(pubprogfuncs_t *pf, edict_t *e)\r\n{\r\n\treturn e->entnum;\r\n}\r\nstatic edict_t *QDECL Lua_ProgsToEdict(pubprogfuncs_t *pf, int num)\r\n{\r\n\treturn Lua_EdictNum(pf, num);\r\n}\r\nvoid Lua_EntClear (pubprogfuncs_t *pf, edict_t *e)\r\n{\r\n\tint num = e->entnum;\r\n\tmemset (e->v, 0, sv.world.edict_size);\r\n\te->ereftype = ER_ENTITY;\r\n\te->entnum = num;\r\n}\r\nedict_t *Lua_CreateEdict(unsigned int num)\r\n{\r\n\tedict_t *e;\r\n\te = lua.edicttable[num] = Z_Malloc(sizeof(edict_t) + sv.world.edict_size);\r\n\te->v = (stdentvars_t*)(e+1);\r\n#ifdef VM_Q1\r\n\te->xv = (extentvars_t*)(e->v + 1);\r\n#endif\r\n\te->entnum = num;\r\n\treturn e;\r\n}\r\nstatic void QDECL Lua_EntRemove(pubprogfuncs_t *pf, edict_t *e, qboolean instant)\r\n{\r\n\tlua_State *L = lua.ctx;\r\n\r\n\tif (!ED_CanFree(e))\r\n\t\treturn;\r\n\te->ereftype = ER_FREE;\r\n\te->freetime = (instant?0:sv.time); //can respawn instantly when asked.\r\n\r\n\t//clear out the lua version of the entity, so that it can be garbage collected.\r\n\t//should probably clear out its entnum field too, just in case.\r\n\tlua_pushlightuserdata(L, e);\r\n\tlua_pushnil(L);\r\n\tlua_settable(L, LUA_REGISTRYINDEX);\r\n}\r\nstatic edict_t *Lua_DoRespawn(pubprogfuncs_t *pf, edict_t *e, int num)\r\n{\r\n\tlua_State *L = lua.ctx;\r\n\tif (!e)\r\n\t\te = Lua_CreateEdict(num);\r\n\telse\r\n\t\tLua_EntClear (pf, e);\r\n\r\n\tED_Spawned((struct edict_s *) e, false);\r\n\r\n\t//create a new table for the entity, give it a suitable metatable, and store it into the registry (avoiding GC and allowing us to actually hold on to it).\r\n\tlua_pushlightuserdata(L, lua.edicttable[num]);\r\n\tlua_newtable(L);\r\n\tif (luaL_newmetatable(L, \"entity\"))\r\n\t{\r\n\t\tlua_pushcclosure(L, my_lua_entity_set, 0);\t//known writes should change the internal info so the engine can use the information.\r\n\t\tlua_setfield (L, -2, \"__newindex\");\r\n\r\n\t\tlua_pushcclosure(L, my_lua_entity_get, 0);\t//we need to de-translate the engine's fields too.\r\n\t\tlua_setfield (L, -2, \"__index\");\r\n\r\n\t\tlua_pushcclosure(L, my_lua_entity_tostring, 0);\t//cos its prettier than seeing 'table 0x5425729' all over the place\r\n\t\tlua_setfield (L, -2, \"__tostring\");\r\n\r\n\t\tlua_pushcclosure(L, my_lua_entity_eq, 0);\t//for comparisons, you know?\r\n\t\tlua_setfield (L, -2, \"__eq\");\r\n\t}\r\n\tlua_setmetatable(L, -2);\r\n\tlua_pushinteger(L, num);\r\n\tlua_setfield (L, -2, \"entnum\");\t//so we know which entity it is.\r\n\tlua_settable(L, LUA_REGISTRYINDEX);\r\n\treturn e;\r\n}\r\nstatic edict_t *QDECL Lua_EntAlloc(pubprogfuncs_t *pf, pbool isobject, size_t extrasize)\r\n{\r\n\tint i;\r\n\tedict_t *e;\r\n\tfor ( i=0 ; i<sv.world.num_edicts ; i++)\r\n\t{\r\n\t\te = (edict_t*)EDICT_NUM_PB(pf, i);\r\n\t\t// the first couple seconds of server time can involve a lot of\r\n\t\t// freeing and allocating, so relax the replacement policy\r\n\t\tif (!e || (ED_ISFREE(e) && ( e->freetime < 2 || sv.time - e->freetime > 0.5 ) ))\r\n\t\t{\r\n\t\t\te = Lua_DoRespawn(pf, e, i);\r\n\t\t\treturn (struct edict_s *)e;\r\n\t\t}\r\n\t}\r\n\r\n\tif (i >= sv.world.max_edicts-1)\t//try again, but use timed out ents.\r\n\t{\r\n\t\tfor ( i=0 ; i<sv.world.num_edicts ; i++)\r\n\t\t{\r\n\t\t\te = (edict_t*)EDICT_NUM_PB(pf, i);\r\n\t\t\t// the first couple seconds of server time can involve a lot of\r\n\t\t\t// freeing and allocating, so relax the replacement policy\r\n\t\t\tif (!e || ED_ISFREE(e))\r\n\t\t\t{\r\n\t\t\t\te = Lua_DoRespawn(pf, e, i);\r\n\t\t\t\treturn (struct edict_s *)e;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tif (i >= sv.world.max_edicts-1)\r\n\t\t{\r\n\t\t\tSys_Error (\"ED_Alloc: no free edicts\");\r\n\t\t}\r\n\t}\r\n\r\n\tsv.world.num_edicts++;\r\n\te = Lua_EdictNum(pf, i);\r\n\r\n\te = Lua_DoRespawn(pf, e, i);\r\n\r\n\treturn (struct edict_s *)e;\r\n}\r\n\r\n/*static int QDECL Lua_LoadEnts(pubprogfuncs_t *pf, const char *mapstring, void *ctx, void (PDECL *callback) (pubprogfuncs_t *progfuncs, struct edict_s *ed, void *ctx, const char *entstart, const char *entend))\r\n{\r\n\tlua_State *L = lua.ctx;\r\n\tint i = 0;\r\n\tlua_getglobal(L, \"LoadEnts\");\r\n\tlua_newtable(L);\r\n\twhile(NULL != (mapstring = COM_Parse(mapstring)))\r\n\t{\r\n\t\tlua_pushinteger(L, i++);\r\n\t\tlua_pushstring(L, com_token);\r\n\t\tlua_settable(L, -3);\r\n\t}\r\n//\tlua_pushinteger(L, spawnflags);\r\n\r\n\tif (lua_pcall(L, 2, 0, 0) != 0)\r\n\t{\r\n\t\tconst char *s = lua_tolstring(L, -1, NULL);\r\n\t\tCon_Printf(CON_WARNING \"%s\\n\", s);\r\n\t\tlua_pop(L, 1);\r\n\t}\r\n\r\n\treturn sv.world.edict_size;\r\n}*/\r\n\r\nstatic const char *Lua_ParseEdict (pubprogfuncs_t *progfuncs, const char *data, struct edict_s *ent)\r\n{\r\n\tlua_State *L = lua.ctx;\r\n\r\n//\tfdef_t\t\t*key;\r\n\tpbool\tinit;\r\n\tchar\t\tkeyname[256];\r\n\tint\t\t\tn;\r\n\tint\t\t\tnest = 1;\r\n\tchar\t\ttoken[8192];\r\n\tluafld_t *fld;\r\n\r\n//\teval_t\t\t*val;\r\n\r\n\tinit = false;\r\n\r\n// go through all the dictionary pairs\r\n\twhile (1)\r\n\t{\r\n\t// parse key\r\n\t\tdata = COM_ParseOut(data, token, sizeof(token));\r\n\t\tif (token[0] == '}')\r\n\t\t{\r\n\t\t\tif (--nest)\r\n\t\t\t\tcontinue;\r\n\t\t\tbreak;\r\n\t\t}\r\n\t\tif (token[0] == '{' && !token[1])\r\n\t\t\tnest++;\r\n\t\tif (!data)\r\n\t\t{\r\n\t\t\tCon_Printf (\"Lua_ParseEdict: EOF without closing brace\\n\");\r\n\t\t\treturn NULL;\r\n\t\t}\r\n\t\tif (nest > 1)\r\n\t\t\tcontinue;\r\n\r\n\t\tstrncpy (keyname, token, sizeof(keyname)-1);\r\n\t\tkeyname[sizeof(keyname)-1] = 0;\r\n\r\n\t\t// another hack to fix heynames with trailing spaces\r\n\t\tn = strlen(keyname);\r\n\t\twhile (n && keyname[n-1] == ' ')\r\n\t\t{\r\n\t\t\tkeyname[n-1] = 0;\r\n\t\t\tn--;\r\n\t\t}\r\n\r\n\t// parse value\r\n\t\tdata = COM_ParseOut(data, token, sizeof(token));\r\n\t\tif (!data)\r\n\t\t{\r\n\t\t\tCon_Printf (\"Lua_ParseEdict: EOF without closing brace\\n\");\r\n\t\t\treturn NULL;\r\n\t\t}\r\n\r\n\t\tif (token[0] == '}')\r\n\t\t{\r\n\t\t\tCon_Printf (\"Lua_ParseEdict: closing brace without data\\n\");\r\n\t\t\treturn NULL;\r\n\t\t}\r\n\r\n\t\tinit = true;\r\n\r\n// keynames with a leading underscore are used for utility comments,\r\n// and are immediately discarded by quake\r\n\t\tif (keyname[0] == '_')\r\n\t\t\tcontinue;\r\n\r\n\r\n\r\n\t\tif (!strcmp(keyname, \"angle\"))\t//Quake anglehack - we've got to leave it in cos it doesn't work for quake otherwise, and this is a QuakeC lib!\r\n\t\t{\r\n\t\t\tQ_snprintfz (keyname, sizeof(keyname), \"angles\");\t//change it from yaw to 3d angle\r\n\t\t\tQ_snprintfz (token, sizeof(token), \"0 %f 0\", atof(token));\t//change it from yaw to 3d angle\r\n\t\t\tgoto cont;\r\n\t\t}\r\n/*\r\n\t\tkey = ED_FindField (progfuncs, keyname);\r\n\t\tif (!key)\r\n\t\t{\r\n\t\t\tif (!strcmp(keyname, \"light\"))\t//Quake lighthack - allows a field name and a classname to go by the same thing in the level editor\r\n\t\t\t\tif ((key = ED_FindField (progfuncs, \"light_lev\")))\r\n\t\t\t\t\tgoto cont;\r\n\t\t\tif (externs->badfield && externs->badfield(&progfuncs->funcs, (struct edict_s*)ent, keyname, qcc_token))\r\n\t\t\t\tcontinue;\r\n\t\t\tCon_DPrintf (\"'%s' is not a field\\n\", keyname);\r\n\t\t\tcontinue;\r\n\t\t}\r\n*/\r\ncont:\r\n\t\tfld = Hash_GetInsensitive(&lua.entityfields, keyname);\r\n\t\tif (fld && fld->type == ev_float)\r\n\t\t\tlua_pushnumber(L, atof(token));\r\n\t\telse if (fld && fld->type == ev_integer)\r\n\t\t\tlua_pushinteger(L, atoi(token));\r\n\t\telse if (fld && fld->type == ev_vector)\r\n\t\t{\r\n\t\t\tchar *e;\r\n\t\t\tfloat x,y,z;\r\n\t\t\tx = strtod(token, &e);\r\n\t\t\tif (*e == ' ')\r\n\t\t\t\te++;\r\n\t\t\ty = strtod(e, &e);\r\n\t\t\tif (*e == ' ')\r\n\t\t\t\te++;\r\n\t\t\tz = strtod(e, &e);\r\n\t\t\tif (*e == ' ')\r\n\t\t\t\te++;\r\n\t\t\tlua_pushvector(L, x, y, z);\r\n\t\t}\r\n\t\telse if (fld && fld->type == ev_function)\r\n\t\t\tlua_getglobal(L, token);\t//functions are nameless, except for how they're invoked. so this is only for evil mods...\r\n\t\telse if (fld && fld->type == ev_entity)\r\n\t\t{\r\n\t\t\tint num = atoi(token);\r\n\t\t\tlua_pushedict(L, EDICT_NUM_UB((&lua.progfuncs), num));\r\n\t\t}\r\n\t\telse\r\n\t\t\tlua_pushstring(L, token);\r\n\t\tlua_setfield(L, -2, keyname);\r\n\r\n\t\t/*if (!ED_ParseEpair (progfuncs, (char*)ent->fields - progfuncs->funcs.stringtable, key->ofs, key->type, qcc_token))\r\n\t\t{\r\n\t\t\tcontinue;\r\n//\t\t\tSys_Error (\"ED_ParseEdict: parse error on entities\");\r\n\t\t}*/\r\n\t}\r\n\r\n\tif (!init)\r\n\t\tent->ereftype = ER_FREE;\r\n\r\n\treturn data;\r\n}\r\n\r\nstatic int QDECL Lua_LoadEnts(pubprogfuncs_t *pf, const char *mapstring, void *ctx,\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tvoid (PDECL *memoryreset) (pubprogfuncs_t *progfuncs, void *ctx),\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tvoid (PDECL *entspawned) (pubprogfuncs_t *progfuncs, struct edict_s *ed, void *ctx, const char *entstart, const char *entend),\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tpbool(PDECL *extendedterm)(pubprogfuncs_t *progfuncs, void *ctx, const char **extline)\r\n\t\t\t\t\t\t\t\t\t\t\t\t) //restore the entire progs state (or just add some more ents) (returns edicts ize)\r\n{\r\n\tlua_State *L = lua.ctx;\r\n\tstruct edict_s *ed = NULL;\r\n\tconst char *datastart = mapstring;\r\n\r\n\tlua_pushglobaltable(L);\r\n\twhile (1)\r\n\t{\r\n\t\tdatastart = mapstring;\r\n\r\n\t\tif (extendedterm)\r\n\t\t{\r\n\t\t\t//skip simple leading whitespace\r\n\t\t\twhile (*mapstring == ' ' || *mapstring == '\\t' || *mapstring == '\\r' || *mapstring == '\\n')\r\n\t\t\t\tmapstring++;\r\n\t\t\tif (mapstring[0] == '/' && mapstring[1] == '*')\t//we are not reading lua here, so C-style comments are the proper form (otherwise ignored by COM_Parse, giving extensibility).\r\n\t\t\t{\t//looks like we have a hidden extension.\r\n\t\t\t\tmapstring+=2;\r\n\t\t\t\tfor(;;)\r\n\t\t\t\t{\r\n\t\t\t\t\t//skip to end of line\r\n\t\t\t\t\tif (!*mapstring)\r\n\t\t\t\t\t\tbreak;\t//unexpected EOF\r\n\t\t\t\t\telse if (mapstring[0] == '*' && mapstring[1] == '/')\r\n\t\t\t\t\t{\t//end of comment\r\n\t\t\t\t\t\tmapstring+=2;\r\n\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t}\r\n\t\t\t\t\telse if (*mapstring != '\\n')\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tmapstring++;\r\n\t\t\t\t\t\tcontinue;\r\n\t\t\t\t\t}\r\n\t\t\t\t\tmapstring++;\t//skip past the \\n\r\n\t\t\t\t\twhile (*mapstring == ' ' || *mapstring == '\\t')\r\n\t\t\t\t\t\tmapstring++;\t//skip leading indentation\r\n\r\n\t\t\t\t\tif (mapstring[0] == '*' && mapstring[1] == '/')\r\n\t\t\t\t\t{\t//end of comment\r\n\t\t\t\t\t\tmapstring+=2;\r\n\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t}\r\n\t\t\t\t\telse if (*mapstring == '/')\r\n\t\t\t\t\t\tcontinue;\t//embedded comment. ignore the line. not going to do nested comments, because those are not normally valid anyway, just C++-style inside C-style.\r\n\t\t\t\t\telse if (extendedterm(pf, ctx, &mapstring))\r\n\t\t\t\t\t\t;\t//found a term we recognised\r\n\t\t\t\t\telse\r\n\t\t\t\t\t\t;\t//unknown line, but this is a comment so whatever\r\n\r\n\t\t\t\t}\r\n\t\t\t\tcontinue;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tmapstring = COM_Parse(mapstring);\r\n\t\tif (!strcmp(com_token, \"{\"))\r\n\t\t{\r\n\t\t\tif (!ed)\t//first entity\r\n\t\t\t\ted = EDICT_NUM_PB(pf, 0);\r\n\t\t\telse\r\n\t\t\t\ted = ED_Alloc(pf, false, 0);\r\n\t\t\ted->ereftype = ER_ENTITY;\r\n\t\t\tif (pf->parms->entspawn)\r\n\t\t\t\tpf->parms->entspawn(ed, true);\r\n\r\n\t\t\tlua_pushedict(L, ed);\r\n\t\t\tmapstring = Lua_ParseEdict(pf, mapstring, ed);\r\n\r\n\t\t\tif (1)\r\n\t\t\t{\t//we can't call the callback, as it would be unable to represent the function references.\r\n\t\t\t\tint spawnflags, killonspawnflags = *(int*)ctx;//lame. really lame\r\n\t\t\t\tlua_getfield(L, -1, \"spawnflags\");\t//push -1[\"classname\"]...\r\n\t\t\t\tspawnflags = lua_tointeger(L, -1);\r\n\t\t\t\tlua_pop(L, 1);\r\n\t\t\t\tif (spawnflags & killonspawnflags)\r\n\t\t\t\t\tlua.progfuncs.EntFree(&lua.progfuncs, ed, true);\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\tlua_getfield(L, -1, \"classname\");\t//push -1[\"classname\"]...\r\n\t\t\t\t\t//-3:globaltable, -2:enttable, -1:classname string\r\n\t\t\t\t\tlua_gettable(L, -3);\t//pop the classname and look it up inside the global table (to find the function in question)\r\n\t\t\t\t\tlua.globals.self = ed->entnum;\r\n\t\t\t\t\tif (lua_pcall(L, 0, 0, 0) != 0)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tconst char *s = lua_tolstring(L, -1, NULL);\r\n\t\t\t\t\t\tlua_getfield(L, -2, \"classname\");\t//push -1[\"classname\"]...\r\n\t\t\t\t\t\tCon_Printf(CON_WARNING \"spawn func %s: %s\\n\", lua_tolstring(L, -1, NULL), s);\r\n\t\t\t\t\t\tlua_pop(L, 2);\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tlua_pop(L, 1);\r\n\t\t\t\tentspawned(pf, ed, ctx, datastart, mapstring);\r\n\t\t\t}\r\n\t\t\tlua_pop(L, 1);\t//pop ent table\r\n\t\t}\r\n\t\telse\r\n\t\t\tbreak;\t//unexpected token...\r\n\t}\r\n\tlua_pop(L, 1);\r\n\treturn sv.world.edict_size;\r\n}\r\n\r\nstatic eval_t *QDECL Lua_GetEdictFieldValue(pubprogfuncs_t *pf, edict_t *e, const char *fieldname, etype_t type, evalc_t *cache)\r\n{\r\n\teval_t *val;\r\n\tluafld_t *fld;\r\n\tfld = Hash_GetInsensitive(&lua.entityfields, fieldname);\r\n\tif (fld)\r\n\t{\r\n\t\tval = (eval_t*)((char*)e->v + fld->offset);\r\n\t\treturn val;\r\n\t}\r\n\treturn NULL;\r\n}\r\n\r\nstatic eval_t\t*QDECL Lua_FindGlobal\t\t(pubprogfuncs_t *prinst, const char *name, progsnum_t num, etype_t *type)\r\n{\r\n\teval_t *val;\r\n\tluafld_t *fld;\r\n\tfld = Hash_GetInsensitive(&lua.globalfields, name);\r\n\tif (fld)\r\n\t{\r\n\t\tval = (eval_t*)((char*)&lua.globals + fld->offset);\r\n\t\treturn val;\r\n\t}\r\n\r\n\tCon_Printf(\"Lua_FindGlobal: %s\\n\", name);\r\n\treturn NULL;\r\n}\r\nstatic func_t QDECL Lua_FindFunction\t\t(pubprogfuncs_t *prinst, const char *name, progsnum_t num)\r\n{\r\n\teval_t *val;\r\n\tluafld_t *fld;\r\n\tfld = Hash_GetInsensitive(&lua.globalfields, name);\r\n\tif (fld)\r\n\t{\r\n\t\tval = (eval_t*)((char*)&lua.globals + fld->offset);\r\n\t\treturn val->function;\r\n\t}\r\n\r\n\tCon_Printf(\"Lua_FindFunction: %s\\n\", name);\r\n\treturn 0;\r\n}\r\n\r\nstatic globalvars_t *QDECL Lua_Globals(pubprogfuncs_t *prinst, int prnum)\r\n{\r\n//\tCon_Printf(\"Lua_Globals: called\\n\");\r\n\treturn NULL;\r\n}\r\n\r\nchar *QDECL Lua_AddString(pubprogfuncs_t *prinst, const char *val, int minlength, pbool demarkup)\r\n{\r\n\tchar *ptr;\r\n\tint len = strlen(val)+1;\r\n\tif (len < minlength)\r\n\t\tlen = minlength;\r\n\tptr = Z_TagMalloc(len, LUA_MALLOC_TAG);\r\n\tstrcpy(ptr, val);\r\n\treturn ptr;\r\n}\r\nstatic string_t QDECL Lua_StringToProgs(pubprogfuncs_t *prinst, const char *str)\r\n{\r\n\tCon_Printf(\"Lua_StringToProgs called instead of Lua_SetStringField\\n\");\r\n\treturn 0;\r\n}\r\n\r\n//passing NULL for ed means its setting a global.\r\nstatic void QDECL Lua_SetStringField(pubprogfuncs_t *prinst, edict_t *ed, string_t *fld, const char *str, pbool str_is_static)\r\n{\r\n\tlua_State *L = lua.ctx;\r\n\tstring_t val;\r\n\tstring_t base;\r\n\tif (ed)\r\n\t{\r\n\t\tbase = (ed->entnum+1)<<10;\r\n\t\tval = (char*)fld-(char*)ed->v;\r\n\r\n\t\tlua_pushedict(lua.ctx, lua.edicttable[ed->entnum]);\r\n\t}\r\n\telse\r\n\t{\r\n\t\tbase = 0;\r\n\t\tval = (char*)fld-(char*)&lua.globals;\r\n\r\n\t\t//push the globals list\r\n\t\tlua_pushglobaltable(L);\r\n\t}\r\n\t*fld = base | val;\t//set the engine's value\r\n\r\n\t//set the stuff so that lua can read it properly.\r\n\tlua_pushlightuserdata(L, (void *)(qintptr_t)val);\r\n\tlua_pushstring(L, str);\r\n\tlua_rawset(L, -3);\r\n\r\n\t//and pop the table\r\n\tlua_pop(L, 1);\r\n}\r\n\r\nstatic const char *ASMCALL QDECL Lua_StringToNative(pubprogfuncs_t *prinst, string_t str)\r\n{\r\n\tconst char *ret = \"\";\r\n\tunsigned int entnum = str >> 10;\r\n\tif (str)\r\n\t{\r\n\t\tstr &= 1023;\r\n\t\tif (!entnum)\r\n\t\t{\r\n\t\t\t//okay, its the global table.\r\n\t\t\tlua_pushglobaltable(lua.ctx);\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tentnum-=1;\r\n\t\t\tif (entnum >= lua.maxedicts)\r\n\t\t\t\treturn ret;\t//erk...\r\n\t\t\t//get the entity's table\r\n\t\t\tlua_pushlightuserdata(lua.ctx, lua.edicttable[entnum]);\r\n\t\t\tlua_gettable(lua.ctx, LUA_REGISTRYINDEX);\r\n\t\t}\r\n\r\n\t\t//read the function from the table\r\n\t\tlua_pushlightuserdata(lua.ctx, (void *)(qintptr_t)str);\r\n\t\tlua_rawget(lua.ctx, -2);\r\n\t\tret = lua_tolstring(lua.ctx, -1, NULL);\r\n\t\tlua_pop(lua.ctx, 2);\t//pop the table+string.\r\n\t\t//popping the string is 'safe' on the understanding that the string reference is still held by its containing table, so don't store the string anywhere.\r\n\t}\r\n\r\n\treturn ret;\r\n}\r\n\r\nstatic void Lua_Event_Touch(world_t *w, wedict_t *s, wedict_t *o, trace_t *trace)\r\n{\r\n\tint oself = pr_global_struct->self;\r\n\tint oother = pr_global_struct->other;\r\n\r\n\tpr_global_struct->self = EDICT_TO_PROG(w->progs, s);\r\n\tpr_global_struct->other = EDICT_TO_PROG(w->progs, o);\r\n\tpr_global_struct->time = w->physicstime;\r\n\r\n#if 1\r\n\tPR_ExecuteProgram (w->progs, s->v->touch);\r\n#else\r\n\tlua_pushedict(lua.ctx, s);\r\n\t//lua_pushliteral(lua.ctx, \"touch\");\r\n\tlua_pushlightuserdata(lua.ctx, (void*)((char*)&s->v->touch-(char*)s->v));\r\n\tlua_rawget(lua.ctx, -2);\r\n\tlua_replace(lua.ctx, -2);\r\n\tif (lua_pcall(lua.ctx, 0, 0, 0) != 0)\r\n\t{\r\n\t\tconst char *e = lua_tolstring(lua.ctx, -1, NULL);\r\n\t\tlua_pushedict(lua.ctx, (struct edict_s*)s);\r\n\t\tlua_getfield(lua.ctx, -1, \"classname\");\r\n\t\tCon_Printf(CON_WARNING \"%s touch: %s\\n\", lua_tostring(lua.ctx, -1), e);\r\n\t\tlua_pop(lua.ctx, 3);\t//error, enttable, classname\r\n\t}\r\n#endif\r\n\r\n\tpr_global_struct->self = oself;\r\n\tpr_global_struct->other = oother;\r\n}\r\n\r\nstatic void Lua_Event_Think(world_t *w, wedict_t *s)\r\n{\r\n\tpr_global_struct->self = EDICT_TO_PROG(w->progs, s);\r\n\tpr_global_struct->other = EDICT_TO_PROG(w->progs, w->edicts);\r\n\r\n#if 0\r\n\tPR_ExecuteProgram (w->progs, s->v->think);\r\n#else\r\n\tlua_pushedict(lua.ctx, (struct edict_s*)s);\r\n//\tlua_pushliteral(lua.ctx, \"think\");\r\n\tlua_pushlightuserdata(lua.ctx, (void*)((char*)&s->v->think-(char*)s->v));\r\n\tlua_rawget(lua.ctx, -2);\r\n\tlua_replace(lua.ctx, -2);\r\n\tif (lua_pcall(lua.ctx, 0, 0, 0) != 0)\r\n\t{\r\n\t\tconst char *e = lua_tolstring(lua.ctx, -1, NULL);\r\n\t\tlua_pushedict(lua.ctx, (struct edict_s*)s);\r\n\t\tlua_getfield(lua.ctx, -1, \"classname\");\r\n\t\tCon_Printf(CON_WARNING \"%s think: %s\\n\", lua_tostring(lua.ctx, -1), e);\r\n\t\tlua_pop(lua.ctx, 3);\t//error, enttable, classname\r\n\t}\r\n#endif\r\n}\r\n\r\nstatic qboolean Lua_Event_ContentsTransition(world_t *w, wedict_t *ent, int oldwatertype, int newwatertype)\r\n{\r\n\treturn false;\t//always do legacy behaviour, because I cba implementing anything else.\r\n}\r\n\r\nstatic void Lua_SetupGlobals(world_t *world)\r\n{\r\n\tint flds;\r\n\tint bucks;\r\n\tcomentvars_t\t*v = NULL;\r\n\textentvars_t\t*xv = (extentvars_t*)(v+1);\r\n\r\n\tmemset(&lua.globals, 0, sizeof(lua.globals));\r\n\tlua.globals.physics_mode = 2;\r\n\tlua.globals.dimension_send = 255;\r\n\tlua.globals.dimension_default = 255;\r\n\tlua.globals.global_gravitydir[2] = -1;\r\n\r\n\tflds = 0;\r\n\tbucks = 64;\r\n\tHash_InitTable(&lua.globalfields, bucks, Z_Malloc(Hash_BytesForBuckets(bucks)));\r\n\r\n//WARNING: global is not remapped yet...\r\n//This code is written evilly, but works well enough\r\n#define doglobal(n, t)\t\\\r\n\t\tpr_global_ptrs->n = &lua.globals.n;\t\\\r\n\t\tlua.globflds[flds].offset = (char*)&lua.globals.n - (char*)&lua.globals;\t\\\r\n\t\tlua.globflds[flds].name = #n;\t\t\\\r\n\t\tlua.globflds[flds].type = t;\t\t\\\r\n\t\tHash_AddInsensitive(&lua.globalfields, lua.globflds[flds].name, &lua.globflds[flds], &lua.globflds[flds].buck);\t\\\r\n\t\tflds++;\r\n#define doglobal_v(o, f, t)\t\\\r\n\t\tlua.globflds[flds].offset = (char*)&lua.globals.o - (char*)&lua.globals;\t\\\r\n\t\tlua.globflds[flds].name = #f;\t\t\\\r\n\t\tlua.globflds[flds].type = t;\t\t\\\r\n\t\tHash_AddInsensitive(&lua.globalfields, lua.globflds[flds].name, &lua.globflds[flds], &lua.globflds[flds].buck);\t\\\r\n\t\tflds++;\r\n#define globalentity(required, name) doglobal(name, ev_entity)\r\n#define globalint(required, name) doglobal(name, ev_integer)\r\n#define globalfloat(required, name) doglobal(name, ev_float)\r\n#define globalstring(required, name) doglobal(name, ev_string)\r\n#define globalvec(required, name) doglobal(name, ev_vector) doglobal_v(name[0], name##_x, ev_float) doglobal_v(name[1], name##_y, ev_float) doglobal_v(name[2], name##_z, ev_float)\r\n#define globalfunc(required, name) doglobal(name, ev_function)\r\n\tluagloballist\r\n#undef doglobal\r\n#define doglobal(n, t) doglobal_v(n,n,t)\r\n\tluaextragloballist\r\n\r\n#define parm(n)\\\r\n\t\tpr_global_ptrs->spawnparamglobals[n] = &lua.globals.parm[n];\t\\\r\n\t\tlua.globflds[flds].offset = (char*)&lua.globals.parm[n] - (char*)&lua.globals;\t\\\r\n\t\tlua.globflds[flds].name = \"parm\"#n;\t\t\\\r\n\t\tlua.globflds[flds].type = ev_float;\t\t\\\r\n\t\tHash_AddInsensitive(&lua.globalfields, lua.globflds[flds].name, &lua.globflds[flds], &lua.globflds[flds].buck);\t\\\r\n\t\tflds++\r\n\tparm( 0);parm( 1);parm( 2);parm( 3);parm( 4);parm( 5);parm( 6);parm( 7);\r\n\tparm( 8);parm( 9);parm(10);parm(11);parm(12);parm(13);parm(14);parm(15);\r\n#undef parm\r\n\r\n\tlua.numflds = 0;\r\n\tbucks = 256;\r\n\tHash_InitTable(&lua.entityfields, bucks, Z_Malloc(Hash_BytesForBuckets(bucks)));\r\n\r\n\r\n#define doefield(n, t)\t\\\r\n\t\tlua.entflds[lua.numflds].offset = (char*)&v->n - (char*)v;\t\\\r\n\t\tlua.entflds[lua.numflds].name = #n;\t\t\\\r\n\t\tlua.entflds[lua.numflds].type = t;\t\t\\\r\n\t\tHash_AddInsensitive(&lua.entityfields, lua.entflds[lua.numflds].name, &lua.entflds[lua.numflds], &lua.entflds[lua.numflds].buck);\t\\\r\n\t\tlua.numflds++;\r\n#define doefield_v(o, f, t)\t\\\r\n\t\tlua.entflds[lua.numflds].offset = (char*)&v->o - (char*)v;\t\\\r\n\t\tlua.entflds[lua.numflds].name = #f;\t\t\\\r\n\t\tlua.entflds[lua.numflds].type = t;\t\t\\\r\n\t\tHash_AddInsensitive(&lua.entityfields, lua.entflds[lua.numflds].name, &lua.entflds[lua.numflds], &lua.entflds[lua.numflds].buck);\t\\\r\n\t\tlua.numflds++;\r\n#define comfieldentity(name,desc) doefield(name, ev_entity)\r\n#define comfieldint(name,desc) doefield(name, ev_integer)\r\n#define comfieldfloat(name,desc) doefield(name, ev_float)\r\n#define comfieldstring(name,desc) doefield(name, ev_string)\r\n#define comfieldvector(name,desc) doefield(name, ev_vector) doefield_v(name[0], name##_x, ev_float) doefield_v(name[1], name##_y, ev_float) doefield_v(name[2], name##_z, ev_float)\r\n#define comfieldfunction(name,typestr,desc) doefield(name, ev_function)\r\n\tcomqcfields\r\n#undef doefield\r\n#undef doefield_v\r\n#define doefield(n, t)\t\\\r\n\t\tlua.entflds[lua.numflds].offset = (char*)&xv->n - (char*)v;\t\\\r\n\t\tlua.entflds[lua.numflds].name = #n;\t\t\\\r\n\t\tlua.entflds[lua.numflds].type = t;\t\t\\\r\n\t\tHash_AddInsensitive(&lua.entityfields, lua.entflds[lua.numflds].name, &lua.entflds[lua.numflds], &lua.entflds[lua.numflds].buck);\t\\\r\n\t\tlua.numflds++;\r\n#define doefield_v(o, f, t)\t\\\r\n\t\tlua.entflds[lua.numflds].offset = (char*)&xv->o - (char*)v;\t\\\r\n\t\tlua.entflds[lua.numflds].name = #f;\t\t\\\r\n\t\tlua.entflds[lua.numflds].type = t;\t\t\\\r\n\t\tHash_AddInsensitive(&lua.entityfields, lua.entflds[lua.numflds].name, &lua.entflds[lua.numflds], &lua.entflds[lua.numflds].buck);\t\\\r\n\t\tlua.numflds++;\r\n\tcomextqcfields\r\n\tsvextqcfields\r\n\r\n\tPR_SV_FillWorldGlobals(world);\r\n}\r\n\r\nvoid QDECL Lua_ExecuteProgram(pubprogfuncs_t *funcs, func_t func)\r\n{\r\n\tunsigned int entnum = func >> 10;\r\n\tfunc &= 1023;\r\n\tif (!entnum)\r\n\t{\r\n\t\t//okay, its the global table.\r\n\t\tlua_pushglobaltable(lua.ctx);\r\n\t}\r\n\telse\r\n\t{\r\n\t\tentnum-=1;\r\n\t\tif (entnum >= lua.maxedicts)\r\n\t\t\treturn;\t//erk...\r\n\t\t//get the entity's table\r\n\t\tlua_pushlightuserdata(lua.ctx, lua.edicttable[entnum]);\r\n\t\tlua_gettable(lua.ctx, LUA_REGISTRYINDEX);\r\n\t}\r\n\r\n\t//read the function from the table\r\n\tlua_pushlightuserdata(lua.ctx, (void *)(qintptr_t)func);\r\n\tlua_rawget(lua.ctx, -2);\r\n\r\n\t//and now invoke it.\r\n\tif (lua_pcall(lua.ctx, 0, 0, 0) != 0)\r\n\t{\r\n\t\tconst char *s = lua_tolstring(lua.ctx, -1, NULL);\r\n\t\tCon_Printf(CON_WARNING \"%s\\n\", s);\r\n\t\tlua_pop(lua.ctx, 1);\r\n\t}\r\n}\r\n\r\nvoid PDECL Lua_CloseProgs(pubprogfuncs_t *inst)\r\n{\r\n\tlua_close(lua.ctx);\r\n\tfree(lua.edicttable);\r\n\tlua.edicttable = NULL;\r\n\tlua.maxedicts = 0;\r\n\r\n\tmemset(&lua, 0, sizeof(lua));\r\n\r\n\tZ_FreeTags(LUA_MALLOC_TAG);\r\n}\r\n\r\nstatic void QDECL Lua_Get_FrameState(world_t *w, wedict_t *ent, framestate_t *fstate)\r\n{\r\n\tmemset(fstate, 0, sizeof(*fstate));\r\n\tfstate->g[FS_REG].frame[0] = ent->v->frame;\r\n\tfstate->g[FS_REG].frametime[0] = ent->xv->frame1time;\r\n\tfstate->g[FS_REG].lerpweight[0] = 1;\r\n\tfstate->g[FS_REG].endbone = 0x7fffffff;\r\n\r\n\tfstate->g[FST_BASE].frame[0] = ent->xv->baseframe;\r\n\tfstate->g[FST_BASE].frametime[0] = ent->xv->/*base*/frame1time;\r\n\tfstate->g[FST_BASE].lerpweight[0] = 1;\r\n\tfstate->g[FST_BASE].endbone = ent->xv->basebone;\r\n\r\n#if defined(SKELETALOBJECTS) || defined(RAGDOLL)\r\n\tif (ent->xv->skeletonindex)\r\n\t\tskel_lookup(w, ent->xv->skeletonindex, fstate);\r\n#endif\r\n}\r\n\r\nqboolean PR_LoadLua(void)\r\n{\r\n\tworld_t *world = &sv.world;\r\n\tpubprogfuncs_t *pf;\r\n\tvfsfile_t *sourcefile = NULL;\r\n\tif ((sourcefile = FS_OpenVFS(\"qwprogs.lua\", \"rb\", FS_GAME)))\r\n\t\tprogstype = PROG_QW;\r\n\telse if ((sourcefile = FS_OpenVFS(\"progs.lua\", \"rb\", FS_GAME)))\r\n\t\tprogstype = PROG_NQ;\r\n\telse\r\n\t\treturn false;\r\n\r\n\tif (!init_lua())\r\n\t{\r\n\t\tVFS_CLOSE(sourcefile);\r\n\t\tCon_Printf(\"WARNING: Found progs.lua, but could load load lua library\\n\");\r\n\t\treturn false;\r\n\t}\r\n\r\n\tpf = svprogfuncs = &lua.progfuncs;\r\n\r\n\tpf->Shutdown = Lua_CloseProgs;\r\n\tpf->AddString = Lua_AddString;\r\n\tpf->EdictNum = Lua_EdictNum;\r\n\tpf->NumForEdict = Lua_NumForEdict;\r\n\tpf->EdictToProgs = Lua_EdictToProgs;\r\n\tpf->ProgsToEdict = Lua_ProgsToEdict;\r\n\tpf->EntAlloc = Lua_EntAlloc;\r\n\tpf->EntFree = Lua_EntRemove;\r\n\tpf->EntClear = Lua_EntClear;\r\n\tpf->FindGlobal = Lua_FindGlobal;\r\n\tpf->load_ents = Lua_LoadEnts;\r\n\tpf->globals = Lua_Globals;\r\n\tpf->GetEdictFieldValue = Lua_GetEdictFieldValue;\r\n\tpf->SetStringField = Lua_SetStringField;\r\n\tpf->StringToProgs = Lua_StringToProgs;\r\n\tpf->StringToNative = Lua_StringToNative;\r\n\tpf->ExecuteProgram = Lua_ExecuteProgram;\r\n\tpf->FindFunction = Lua_FindFunction;\r\n\r\n\tworld->Event_Touch = Lua_Event_Touch;\r\n\tworld->Event_Think = Lua_Event_Think;\r\n\tworld->Event_Sound = SVQ1_StartSound;\r\n\tworld->Event_ContentsTransition = Lua_Event_ContentsTransition;\r\n\tworld->Get_CModel = SVPR_GetCModel;\r\n\tworld->Get_FrameState = Lua_Get_FrameState;\r\n\r\n\tworld->progs = pf;\r\n\tworld->progs->parms = &lua.progfuncsparms;\r\n\tworld->progs->parms->user = world;\r\n\tworld->progs->parms->Printf = PR_Printf;\r\n\tworld->progs->parms->DPrintf = PR_DPrintf;\r\n\tworld->usesolidcorpse = true;\r\n\r\n\tLua_SetupGlobals(world);\r\n\r\n\tsvs.numprogs = 0;\t//Why is this svs?\r\n#ifdef VM_Q1\r\n\tworld->edict_size = sizeof(stdentvars_t) + sizeof(extentvars_t);\r\n#else\r\n\tworld->edict_size = sizeof(stdentvars_t);\r\n#endif\r\n\r\n\t//force items2 instead of serverflags\r\n\tsv.haveitems2 = true;\r\n\r\n\t//initalise basic lua context\r\n\tlua.ctx = lua_newstate(my_lua_alloc, NULL);\t\t\t\t\t//create our lua state\r\n//\tluaL_openlibs(lua.ctx); \r\n\tmy_lua_registerbuiltins(lua.ctx);\r\n\r\n\t//spawn the world, woo.\r\n\tworld->edicts = (wedict_t*)pf->EntAlloc(pf,false,0);\r\n\r\n\t//load the gamecode now. it should be safe for it to call various builtins.\r\n\tif (0 != lua_load(lua.ctx, my_lua_Reader, sourcefile, \"progs.lua\", \"bt\"))\t//load the file, embed it within a function and push it\r\n\t{\r\n\t\tCon_Printf(\"Error trying to parse %s: %s\\n\", \"progs.lua\", lua_tolstring(lua.ctx, -1, NULL));\r\n\t\tlua_pop(lua.ctx, 1);\r\n\t}\r\n\telse\r\n\t{\r\n\t\tif (lua_pcall(lua.ctx, 0, 0, 0) != 0)\r\n\t\t{\r\n\t\t\tconst char *s = lua_tolstring(lua.ctx, -1, NULL);\r\n\t\t\tCon_Printf(CON_WARNING \"%s\\n\", s);\r\n\t\t\tlua_pop(lua.ctx, 1);\r\n\t\t}\r\n\t}\r\n\tVFS_CLOSE(sourcefile);\r\n\r\n\treturn true;\r\n}\r\n#endif\t//VM_LUA\r\n"
  },
  {
    "path": "engine/server/pr_q1qvm.c",
    "content": "/*\r\nCopyright (C) 1996-1997 Id Software, Inc.\r\n\r\nThis program is free software; you can redistribute it and/or\r\nmodify it under the terms of the GNU General Public License\r\nas published by the Free Software Foundation; either version 2\r\nof the License, or (at your option) any later version.\r\n\r\nThis program is distributed in the hope that it will be useful,\r\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\r\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\r\n\r\nSee the GNU General Public License for more details.\r\n\r\nYou should have received a copy of the GNU General Public License\r\nalong with this program; if not, write to the Free Software\r\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\r\n\r\n*/\r\n\r\n/*64bit cpu notes:\r\nstring_t is a 32bit quantity.\r\nthis datatype needs to have enough bits to express any address that contains a string.\r\nin a 32bit build, this is fine. with a qvm, the offset between the vm base and the string is always less than 32bits so this is fine too.\r\nHOWEVER...\r\nnative code uses a base address of 0. this needs a 48bit datatype for any userland address. 32 bits just ain't enough.\r\neven worse: ktx defines string_t as a 'char*'. okay, its 64bit at last... but it means that the entire entity field structure is now the wrong size with the wrong offsets.\r\nthis means CRASH!\r\nhow to fix? good luck with that. seriously.\r\n\tthe only sane way to fix it is to either define a better base address (say the dll base,\r\n\t\tand require that all string_t values are bss or data and not from malloc, which is problematic when loading dynamic stuff from a map)\r\n\talternatively, you could create some string_t->pointer lookup. messy.\r\n\teither way, string_t cannot be a pointer.\r\n\tprobably the best solution is to stop using string_t stuff completely. move all those string values somewhere else.\r\n\tnetnames will still mess things up.\r\nso just use qvms.\r\noh, wait, ktx no longer supports those properly.\r\n*/\r\n\r\n#include \"quakedef.h\"\r\n\r\n#ifdef VM_Q1\r\n\r\n#include \"pr_common.h\"\r\n\r\n/*version changes:\r\n13: 2009/june gamecode no longer aware of edict_t data (just 'qc' fields).\r\n14: 2017/march gamedata_t.maxentities added\r\n15: 2017/june for-64bit string indirection changes. added GAME_CLEAR_EDICT.\r\n16: wasted_edict_t_size is finally 0, mod is responsible for querying all strings.\r\n*/\r\n#define\tGAME_API_VERSION\t\t16\r\n#define\tGAME_API_VERSION_MIN\t8\r\n#define MAX_Q1QVM_EDICTS\t768 //according to ktx at api version 12 (fte's protocols go to 2048). removed in v14.\r\n#define MAPNAME_LEN 64\r\n\r\nvoid PR_SV_FillWorldGlobals(world_t *w);\r\n\r\nstatic int qvm_api_version;\r\nstatic size_t wasted_edict_t_size;\r\n\r\n//===============================================================\r\n\r\n//\r\n// system traps provided by the main engine\r\n//\r\ntypedef enum\r\n{\r\n\t//============== general Quake services ==================\r\n\r\n\tG_GETAPIVERSION,\t\t// ( void);\t//0\r\n\r\n\tG_DPRINT,\t\t// ( const char *string );\t//1\r\n\t// print message on the local console\r\n\r\n\tG_ERROR,\t\t// ( const char *string );\t//2\r\n\t// abort the game\r\n\tG_GetEntityToken,\t\t//3\r\n\r\n\tG_SPAWN_ENT,\t\t//4\r\n\tG_REMOVE_ENT,\t\t//5\r\n\tG_PRECACHE_SOUND,\r\n\tG_PRECACHE_MODEL,\r\n\tG_LIGHTSTYLE,\r\n\tG_SETORIGIN,\r\n\tG_SETSIZE,\t\t\t//10\r\n\tG_SETMODEL,\r\n\tG_BPRINT,\r\n\tG_SPRINT,\r\n\tG_CENTERPRINT,\r\n\tG_AMBIENTSOUND,\t\t//15\r\n\tG_SOUND,\r\n\tG_TRACELINE,\r\n\tG_CHECKCLIENT,\r\n\tG_STUFFCMD,\r\n\tG_LOCALCMD,\t\t\t//20\r\n\tG_CVAR,\r\n\tG_CVAR_SET,\r\n\tG_FINDRADIUS,\r\n\tG_WALKMOVE,\r\n\tG_DROPTOFLOOR,\t\t//25\r\n\tG_CHECKBOTTOM,\r\n\tG_POINTCONTENTS,\r\n\tG_NEXTENT,\r\n\tG_AIM,\r\n\tG_MAKESTATIC,\t\t//30\r\n\tG_SETSPAWNPARAMS,\r\n\tG_CHANGELEVEL,\r\n\tG_LOGFRAG,\r\n\tG_GETINFOKEY,\r\n\tG_MULTICAST,\t\t//35\r\n\tG_DISABLEUPDATES,\r\n\tG_WRITEBYTE,\r\n\tG_WRITECHAR,\r\n\tG_WRITESHORT,\r\n\tG_WRITELONG,\t\t//40\r\n\tG_WRITEANGLE,\r\n\tG_WRITECOORD,\r\n\tG_WRITESTRING,\r\n\tG_WRITEENTITY,\r\n\tG_FLUSHSIGNON,\t\t//45\r\n\tg_memset,\r\n\tg_memcpy,\r\n\tg_strncpy,\r\n\tg_sin,\r\n\tg_cos,\t\t\t\t//50\r\n\tg_atan2,\r\n\tg_sqrt,\r\n\tg_floor,\r\n\tg_ceil,\r\n\tg_acos,\t\t\t\t//55\r\n\tG_CMD_ARGC,\r\n\tG_CMD_ARGV,\r\n\tG_TraceBox,\t\t\t//was G_TraceCapsule, which is a misnomer.\r\n\tG_FS_OpenFile,\r\n\tG_FS_CloseFile,\t\t//60\r\n\tG_FS_ReadFile,\r\n\tG_FS_WriteFile,\r\n\tG_FS_SeekFile,\r\n\tG_FS_TellFile,\r\n\tG_FS_GetFileList,\t//65\r\n\tG_CVAR_SET_FLOAT,\r\n\tG_CVAR_STRING,\r\n\tG_Map_Extension,\r\n\tG_strcmp,\r\n\tG_strncmp,\t\t\t//70\r\n\tG_stricmp,\r\n\tG_strnicmp,\r\n\tG_Find,\r\n\tG_executecmd,\r\n\tG_conprint,\t\t\t//75\r\n\tG_readcmd,\r\n\tG_redirectcmd,\r\n\tG_Add_Bot,\r\n\tG_Remove_Bot,\r\n\tG_SetBotUserInfo,\t//80\r\n\tG_SetBotCMD,\r\n\r\n\tG_strftime,\r\n\tG_CMD_ARGS,\r\n\tG_CMD_TOKENIZE,\r\n\tG_strlcpy,\t\t//85\r\n\tG_strlcat,\r\n\tG_MAKEVECTORS,\r\n\tG_NEXTCLIENT,\r\n\r\n\tG_PRECACHE_VWEP_MODEL,\r\n\tG_SETPAUSE,\r\n\tG_SETUSERINFO,\r\n\tG_MOVETOGOAL,\r\n\tG_VISIBLETO,\r\n\r\n\r\n\tG_MAX\r\n} gameImport_t;\r\n\r\n\r\n//\r\n// functions exported by the game subsystem\r\n//\r\ntypedef enum\r\n{\r\n\tGAME_INIT,\t// ( int levelTime, int randomSeed, int restart );\r\n\t// init and shutdown will be called every single level\r\n\t// The game should call G_GET_ENTITY_TOKEN to parse through all the\r\n\t// entity configuration text and spawn gentities.\r\n\tGAME_LOADENTS,\r\n\tGAME_SHUTDOWN,\t// (void);\r\n\r\n\tGAME_CLIENT_CONNECT,\t \t// ( int clientNum ,int isSpectator);\r\n\tGAME_PUT_CLIENT_IN_SERVER,\r\n\r\n\tGAME_CLIENT_USERINFO_CHANGED,\t// ( int clientNum,int isSpectator );\r\n\r\n\tGAME_CLIENT_DISCONNECT,\t\t\t// ( int clientNum,int isSpectator );\r\n\r\n\tGAME_CLIENT_COMMAND,\t\t\t// ( int clientNum,int isSpectator );\r\n\r\n\tGAME_CLIENT_PRETHINK,\r\n\tGAME_CLIENT_THINK,\t\t\t\t// ( int clientNum,int isSpectator );\r\n\tGAME_CLIENT_POSTTHINK,\r\n\r\n\tGAME_START_FRAME,\t\t\t\t\t// ( int levelTime );\r\n\tGAME_SETCHANGEPARMS, //self\r\n\tGAME_SETNEWPARMS,\r\n\tGAME_CONSOLE_COMMAND,\t\t\t// ( void );\r\n\tGAME_EDICT_TOUCH,                      //(self,other)\r\n\tGAME_EDICT_THINK,                      //(self,other=world,time)\r\n\tGAME_EDICT_BLOCKED,                     //(self,other)\r\n\tGAME_CLIENT_SAY, \t\t//(int isteam)\r\n\tGAME_PAUSED_TIC,\t\t//(int milliseconds)\r\n\r\n\tGAME_CLEAR_EDICT,\t\t//v15 (sets self.fields to safe values after they're cleared)\r\n\r\n\tGAME_EDICT_CSQCSEND=200,\t//fte entrypoint, called when using SendEntity.\r\n} q1qvmgameExport_t;\r\n\r\n\r\ntypedef enum\r\n{\r\n\tF_INT,\r\n\tF_FLOAT,\r\n\tF_LSTRING,\t\t\t// string on disk, pointer in memory, TAG_LEVEL\r\n//\tF_GSTRING,\t\t\t// string on disk, pointer in memory, TAG_GAME\r\n\tF_VECTOR,\r\n\tF_ANGLEHACK,\r\n//\tF_ENTITY,\t\t\t// index on disk, pointer in memory\r\n//\tF_ITEM,\t\t\t\t// index on disk, pointer in memory\r\n//\tF_CLIENT,\t\t\t// index on disk, pointer in memory\r\n\tF_IGNORE\r\n} fieldtype_t;\r\n\r\ntypedef struct\r\n{\r\n\tquintptr_t\tname;\r\n\tint\t\t\tofs;\r\n\tfieldtype_t\ttype;\r\n//\tint\t\t\tflags;\r\n} fieldN_t;\r\n\r\ntypedef struct\r\n{\r\n\tunsigned int\t\tname;\r\n\tint\t\t\t\t\tofs;\r\n\tfieldtype_t\t\t\ttype;\r\n//\tint\t\t\t\t\tflags;\r\n} field32_t;\r\n\r\n\r\n\r\ntypedef struct {\r\n\tint\tpad[28];\r\n\tint\tself;\r\n\tint\tother;\r\n\tint\tworld;\r\n\tfloat\ttime;\r\n\tfloat\tframetime;\r\n\tint\tnewmis;\r\n\tfloat\tforce_retouch;\r\n\tstring_t\tmapname;\r\n\tfloat\tserverflags;\r\n\tfloat\ttotal_secrets;\r\n\tfloat\ttotal_monsters;\r\n\tfloat\tfound_secrets;\r\n\tfloat\tkilled_monsters;\r\n\tfloat\tparm1;\r\n\tfloat\tparm2;\r\n\tfloat\tparm3;\r\n\tfloat\tparm4;\r\n\tfloat\tparm5;\r\n\tfloat\tparm6;\r\n\tfloat\tparm7;\r\n\tfloat\tparm8;\r\n\tfloat\tparm9;\r\n\tfloat\tparm10;\r\n\tfloat\tparm11;\r\n\tfloat\tparm12;\r\n\tfloat\tparm13;\r\n\tfloat\tparm14;\r\n\tfloat\tparm15;\r\n\tfloat\tparm16;\r\n\tvec3_t\tv_forward;\r\n\tvec3_t\tv_up;\r\n\tvec3_t\tv_right;\r\n\tfloat\ttrace_allsolid;\r\n\tfloat\ttrace_startsolid;\r\n\tfloat\ttrace_fraction;\r\n\tvec3_t\ttrace_endpos;\r\n\tvec3_t\ttrace_plane_normal;\r\n\tfloat\ttrace_plane_dist;\r\n\tint\ttrace_ent;\r\n\tfloat\ttrace_inopen;\r\n\tfloat\ttrace_inwater;\r\n\tint\tmsg_entity;\r\n\tfunc_t\tmain;\r\n\tfunc_t\tStartFrame;\r\n\tfunc_t\tPlayerPreThink;\r\n\tfunc_t\tPlayerPostThink;\r\n\tfunc_t\tClientKill;\r\n\tfunc_t\tClientConnect;\r\n\tfunc_t\tPutClientInServer;\r\n\tfunc_t\tClientDisconnect;\r\n\tfunc_t\tSetNewParms;\r\n\tfunc_t\tSetChangeParms;\r\n} q1qvmglobalvars_t;\r\n\r\n\r\n//this is not directly usable in 64bit to refer to a 32bit qvm (hence why we have two versions).\r\ntypedef struct\r\n{\r\n\tunsigned int\t\tAPIversion;\r\n\tunsigned int\t\tsizeofent;\r\n\tunsigned int\t\tmaxedicts;\r\n\r\n\tquintptr_t\t\t\tglobal;\r\n\tquintptr_t\t\t\tfields;\r\n\tquintptr_t\t\t\tents;\r\n} gameDataPrivate_t;\r\n\r\ntypedef struct\r\n{\r\n\tunsigned int\tents;\r\n\tint\t\t\t\tsizeofent;\r\n\tunsigned int\tglobal;\r\n\tunsigned int\tfields;\r\n\tint\t\t\t\tAPIversion;\r\n#if GAME_API_VERSION >= 14\r\n\tunsigned int\tmaxentities;\r\n#endif\r\n} gameData32_t;\r\n\r\ntypedef struct\r\n{\r\n\tquintptr_t\t\tents;\r\n\tint\t\t\t\tsizeofent;\r\n\tquintptr_t\t\tglobal;\r\n\tquintptr_t\t\tfields;\r\n\tint\t\t\t\tAPIversion;\r\n#if GAME_API_VERSION >= 14\r\n\tunsigned int\tmaxentities;\r\n#endif\r\n} gameDataN_t;\r\n\r\ntypedef enum {\r\n\tFS_READ_BIN,\r\n\tFS_READ_TXT,\r\n\tFS_WRITE_BIN,\r\n\tFS_WRITE_TXT,\r\n\tFS_APPEND_BIN,\r\n\tFS_APPEND_TXT\r\n} q1qvmfsMode_t;\r\n\r\ntypedef enum {\r\n\tFS_SEEK_CUR,\r\n\tFS_SEEK_END,\r\n\tFS_SEEK_SET\r\n} fsOrigin_t;\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n#define emufields \\\r\n\t\temufield(gravity,\t\tF_FLOAT)\t\\\r\n\t\temufield(maxspeed,\t\tF_FLOAT)\t\\\r\n\t\temufield(movement,\t\tF_VECTOR)\t\\\r\n\t\temufield(vw_index,\t\tF_FLOAT)\t\\\r\n\t\temufield(isBot,\t\t\tF_INT)\t\t\\\r\n\t\temufield(items2,\t\tF_FLOAT)\t\\\r\n\t\temufield(trackent,\t\tF_INT)\t\t/*network another player instead, but not entity because of an mvdsv bug. used during bloodfest.*/\t\\\r\n\t\temufield(hideentity,\tF_INT)\t\t/*backward nodrawtoclient, used by race mode spectators*/\t\t\t\t\t\t\t\t\t\t\t\\\r\n\t\temufield(hideplayers,\tF_INT)\t\t/*force other clients as invisible, for race mode*/\r\n//\t\temufield(visclients,\tF_INT)\t/*bitfield of clients that can see this entity (borked with playerslots>32). used for 'cmd tpmsg foo', and bots.*/\r\n//\t\temufield(teleported,\tF_INT)\t/*teleport angle twisting*/\r\n//\t\temufield(brokenankle,\tF_FLOAT) /*not actually in mvdsv after all*/\r\n//\t\temufield(mod_admin,\t\tF_INT)\t/*enable 'cmd ban' etc when &2*/\r\n\r\n\r\nstatic struct\r\n{\r\n#define emufield(n,t) int n;\r\n\temufields\r\n#undef emufield\r\n} fofs;\r\n\r\n\r\nstatic const char *q1qvmentstring;\r\nstatic vm_t *q1qvm;\r\nstatic pubprogfuncs_t q1qvmprogfuncs;\r\n\r\nstatic q1qvmglobalvars_t *gvars;\r\nstatic void *evars;\t//pointer to the gamecodes idea of an edict_t\r\nstatic quintptr_t vevars;\t//offset into the vm base of evars\r\n\r\n/*\r\nstatic char *Q1QVMPF_AddString(pubprogfuncs_t *pf, char *base, int minlength)\r\n{\r\n\tchar *n;\r\n\tint l = strlen(base);\r\n\tCon_Printf(\"warning: string %s will not be readable from the qvm\\n\", base);\r\n\tl = l<minlength?minlength:l;\r\n\tn = Z_TagMalloc(l+1, VMFSID_Q1QVM);\r\n\tstrcpy(n, base);\r\n\treturn n;\r\n}\r\n*/\r\n\r\nstatic edict_t *QDECL Q1QVMPF_EdictNum(pubprogfuncs_t *pf, unsigned int num)\r\n{\r\n\tedict_t *e;\r\n\r\n\tif (/*num < 0 ||*/ num >= sv.world.max_edicts)\r\n\t\treturn NULL;\r\n\r\n\te = q1qvmprogfuncs.edicttable[num];\r\n\tif (!e)\r\n\t{\r\n\t\te = q1qvmprogfuncs.edicttable[num] = Z_TagMalloc(sizeof(edict_t)+sizeof(extentvars_t), VMFSID_Q1QVM);\r\n\t\te->v = (stdentvars_t*)((char*)evars + (num * sv.world.edict_size) + wasted_edict_t_size);\r\n\t\te->xv = (extentvars_t*)(e+1);\r\n\t\te->entnum = num;\r\n\t}\r\n\treturn e;\r\n}\r\n\r\nstatic unsigned int QDECL Q1QVMPF_NumForEdict(pubprogfuncs_t *pf, edict_t *e)\r\n{\r\n\treturn e->entnum;\r\n}\r\n\r\nstatic int QDECL Q1QVMPF_EdictToProgs(pubprogfuncs_t *pf, edict_t *e)\r\n{\t//sadly ktx still uses byte-offset-from-world :(\r\n\treturn e->entnum*sv.world.edict_size;\r\n}\r\nstatic edict_t *QDECL Q1QVMPF_ProgsToEdict(pubprogfuncs_t *pf, int num)\r\n{\r\n\t//sadly ktx still uses byte-offset-from-world :(\r\n\tif (num % sv.world.edict_size)\r\n\t\tCon_Printf(\"Edict To Progs with remainder\\n\");\r\n\tnum /= sv.world.edict_size;\r\n\r\n\treturn Q1QVMPF_EdictNum(pf, num);\r\n}\r\n\r\nstatic void Q1QVMED_ClearEdict (edict_t *e, qboolean wipe)\r\n{\r\n\tint num = e->entnum;\r\n\tif (wipe)\r\n\t{\r\n\t\tmemset (e->v, 0, sv.world.edict_size - wasted_edict_t_size);\r\n\t\tmemset (e->xv, 0, sizeof(*e->xv));\r\n\t}\r\n\tif (qvm_api_version >= 15)\r\n\t{\r\n\t\tint oself = pr_global_struct->self;\r\n\t\tpr_global_struct->self = Q1QVMPF_EdictToProgs(svprogfuncs, e);\r\n\t\tVM_Call(q1qvm, GAME_CLEAR_EDICT, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);\r\n\t\tpr_global_struct->self = oself;\r\n\t}\r\n\te->ereftype = ER_ENTITY;\r\n\te->entnum = num;\r\n}\r\nstatic void QDECL Q1QVMPF_ClearEdict(pubprogfuncs_t *pf, edict_t *e)\r\n{\r\n\tQ1QVMED_ClearEdict(e, true);\r\n}\r\n\r\nstatic void QDECL Q1QVMPF_EntRemove(pubprogfuncs_t *pf, edict_t *e, pbool instant)\r\n{\r\n\tif (!ED_CanFree(e))\r\n\t\treturn;\r\n\te->ereftype = ER_FREE;\r\n\te->freetime = instant?0:sv.time;\r\n}\r\n\r\nstatic edict_t *QDECL Q1QVMPF_EntAlloc(pubprogfuncs_t *pf, pbool object, size_t extrasize)\r\n{\r\n\tint i;\r\n\tedict_t *e;\r\n\tfor ( i=0 ; i<sv.world.num_edicts ; i++)\r\n\t{\r\n\t\te = (edict_t*)EDICT_NUM_PB(pf, i);\r\n\t\t// the first couple seconds of server time can involve a lot of\r\n\t\t// freeing and allocating, so relax the replacement policy\r\n\t\tif (!e || (ED_ISFREE(e) && ( e->freetime < 2 || sv.time - e->freetime > 0.5 ) ))\r\n\t\t{\r\n\t\t\tQ1QVMED_ClearEdict (e, true);\r\n\r\n\t\t\tED_Spawned((struct edict_s *) e, false);\r\n\t\t\treturn (struct edict_s *)e;\r\n\t\t}\r\n\t}\r\n\r\n\tif (i >= sv.world.max_edicts-1)\t//try again, but use timed out ents.\r\n\t{\r\n\t\tfor ( i=0 ; i<sv.world.num_edicts ; i++)\r\n\t\t{\r\n\t\t\te = (edict_t*)EDICT_NUM_PB(pf, i);\r\n\t\t\t// the first couple seconds of server time can involve a lot of\r\n\t\t\t// freeing and allocating, so relax the replacement policy\r\n\t\t\tif (!e || ED_ISFREE(e))\r\n\t\t\t{\r\n\t\t\t\tQ1QVMED_ClearEdict (e, true);\r\n\r\n\t\t\t\tED_Spawned((struct edict_s *) e, false);\r\n\t\t\t\treturn (struct edict_s *)e;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tif (i >= sv.world.max_edicts-1)\r\n\t\t{\r\n\t\t\tSys_Error (\"ED_Alloc: no free edicts\");\r\n\t\t}\r\n\t}\r\n\r\n\tsv.world.num_edicts++;\r\n\te = (edict_t*)Q1QVMPF_EdictNum(pf, i);\r\n\r\n// new ents come ready wiped (unless 15 in which case we need to give the gamecode a chance to set safe defaults)\r\n\tif (qvm_api_version >= 15)\r\n\t\tQ1QVMED_ClearEdict (e, false);\r\n\r\n\tED_Spawned((struct edict_s *) e, false);\r\n\r\n\treturn (struct edict_s *)e;\r\n}\r\n\r\nstatic int QDECL Q1QVMPF_LoadEnts(pubprogfuncs_t *pf, const char *mapstring, void *ctx,\r\n\t\t\t\t\t\t\t\t  void (PDECL *memoryreset) (pubprogfuncs_t *progfuncs, void *ctx),\r\n\t\t\t\t\t\t\t\t  void (PDECL *ent_callback) (pubprogfuncs_t *progfuncs, struct edict_s *ed, void *ctx, const char *entstart, const char *entend),\r\n\t\t\t\t\t\t\t\t  pbool (PDECL *ext_callback)(pubprogfuncs_t *pf, void *ctx, const char **str))\r\n{\r\n\t//the qvm calls the spawn functions itself.\r\n\t//no saved-games.\r\n\tq1qvmentstring = mapstring;\r\n\tVM_Call(q1qvm, GAME_LOADENTS, 0, 0, 0);\r\n\tq1qvmentstring = NULL;\r\n\treturn sv.world.edict_size;\r\n}\r\n\r\nstatic int QDECL Q1QVMPF_QueryField(pubprogfuncs_t *prinst, unsigned int fieldoffset, etype_t *type, char const**name, evalc_t *fieldcache)\r\n{\r\n\t*type = ev_void;\r\n\t*name = \"?\";\r\n\r\n\tfieldcache->varname = NULL;\r\n\tfieldcache->spare[0] = fieldoffset;\r\n\treturn true;\r\n}\r\n\r\nstatic eval_t *QDECL Q1QVMPF_GetEdictFieldValue(pubprogfuncs_t *pf, edict_t *e, const char *fieldname, etype_t type, evalc_t *cache)\r\n{\r\n\tif (cache && !cache->varname)\r\n\t{\r\n\t\treturn (eval_t*)((char*)e->v + cache->spare[0]-wasted_edict_t_size);\r\n\t}\r\n\tif (!strcmp(fieldname, \"message\"))\r\n\t{\r\n\t\treturn (eval_t*)&e->v->message;\r\n\t}\r\n\treturn NULL;\r\n}\r\n\r\nstatic eval_t\t*QDECL Q1QVMPF_FindGlobal\t\t(pubprogfuncs_t *prinst, const char *name, progsnum_t num, etype_t *type)\r\n{\r\n\treturn NULL;\r\n}\r\n\r\nstatic globalvars_t *QDECL Q1QVMPF_Globals(pubprogfuncs_t *prinst, progsnum_t prnum)\r\n{\r\n\treturn NULL;\r\n}\r\n\r\nstatic string_t QDECL Q1QVMPF_StringToProgs(pubprogfuncs_t *prinst, const char *str)\r\n{\r\n\tquintptr_t ret = (str - (char*)VM_MemoryBase(q1qvm));\r\n\tif (ret >= VM_MemoryMask(q1qvm))\r\n\t\treturn 0;\r\n\tif (ret >= 0xffffffff)\r\n\t\treturn 0;\t//invalid string! blame 64bit.\r\n\treturn ret;\r\n}\r\n\r\nstatic void *ASMCALL QDECL Q1QVMPF_PointerToNative(pubprogfuncs_t *prinst, quintptr_t str)\r\n{\r\n\tvoid *ret;\r\n\tif (!str || (quintptr_t)str >= VM_MemoryMask(q1qvm))\r\n\t\treturn NULL;\t//null or invalid pointers.\r\n\tret = (char*)VM_MemoryBase(q1qvm) + str;\r\n\treturn ret;\r\n}\r\n\r\nstatic const char *ASMCALL QDECL Q1QVMPF_StringToNative(pubprogfuncs_t *prinst, string_t str)\r\n{\r\n\tquintptr_t ref;\r\n\tif (str == ~0)\r\n\t\treturn \" \";\t//models are weird. yes, this is a hack.\r\n\tif (qvm_api_version >= 15)\r\n\t{\r\n\t\tqboolean stringishacky = sizeof(quintptr_t) != sizeof(string_t) && qvm_api_version >= 15 && !VM_NonNative(q1qvm);\t//silly bullshit. Really, native gamecode should have its own implementation of this builtin or something, especially as its pretty much only ever used for classnames.\r\n\t\tif (!str)\r\n\t\t\treturn \"\";\t//invalid...\r\n\t\telse if (stringishacky)\r\n\t\t{\r\n\t\t\tif (str >= 0 && str < sv.world.edict_size*sv.world.max_edicts - sizeof(quintptr_t))\r\n\t\t\t\tref = *(quintptr_t*)((char*)evars + (int)str);\t//extra indirection added in api 15.\r\n\t\t\telse\r\n\t\t\t\treturn \"\"; //error\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tif (str >= 0 && str < sv.world.edict_size*sv.world.max_edicts - sizeof(quintptr_t))\r\n\t\t\t\tref = *(string_t*)((char*)evars + (int)str);\t//extra indirection added in api 15.\r\n\t\t\telse\r\n\t\t\t\treturn \"\"; //error\r\n\t\t}\r\n\t}\r\n\telse\r\n\t\tref = str;\r\n\tif (!ref || (quintptr_t)ref >= VM_MemoryMask(q1qvm))\r\n\t\treturn \"\";\t//null or invalid pointers.\r\n\treturn (char*)VM_MemoryBase(q1qvm) + ref;\r\n}\r\nstatic void QDECL Q1QVMPF_SetStringField(pubprogfuncs_t *progfuncs, struct edict_s *ed, string_t *fld, const char *str, pbool str_is_static)\r\n{\r\n\tif (!str_is_static)\r\n\t\treturn;\r\n\tif (qvm_api_version >= 15)\r\n\t{\r\n\t\tqboolean stringishacky = sizeof(quintptr_t) != sizeof(string_t) && qvm_api_version >= 15 && !VM_NonNative(q1qvm);\t//silly bullshit. Really, native gamecode should have its own implementation of this builtin or something, especially as its pretty much only ever used for classnames.\r\n\t\tquintptr_t nval = (str - (char*)VM_MemoryBase(q1qvm));\r\n\t\tif (nval >= VM_MemoryMask(q1qvm))\r\n\t\t\treturn;\r\n\r\n\t\tif (!*fld)\r\n\t\t\tCon_DPrintf(\"Ignoring string set. mod pointer not set.\\n\");\r\n\t\telse if (stringishacky)\r\n\t\t{\r\n\t\t\tif (*fld >= 0 && *fld < sv.world.edict_size*sv.world.max_edicts - sizeof(quintptr_t))\r\n\t\t\t\t*(quintptr_t*)((char*)evars + *fld) = nval;\r\n\t\t\telse\r\n\t\t\t\tCon_DPrintf(\"Ignoring string set outside of progs VM\\n\");\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tif (nval >= 0xffffffff)\r\n\t\t\t\treturn;\t//invalid string! blame 64bit.\r\n\t\t\tif (*fld >= 0 && *fld < sv.world.edict_size*sv.world.max_edicts - sizeof(string_t))\r\n\t\t\t\t*(string_t*)((char*)evars + *fld) = (string_t)nval;\r\n\t\t\telse\r\n\t\t\t\tCon_DPrintf(\"Ignoring string set outside of progs VM\\n\");\r\n\t\t}\r\n\t}\r\n\telse\r\n\t{\r\n\t\tstring_t newval = progfuncs->StringToProgs(progfuncs, str);\r\n\t\tif (newval || !str)\r\n\t\t\t*fld = newval;\r\n\t\telse if (!str)\r\n\t\t\t*fld = 0;\r\n\t\telse\r\n\t\t{\r\n\t\t\t*fld = ~0;\r\n\t\t\t//Con_DPrintf(\"Ignoring string set outside of progs VM\\n\");\r\n\t\t}\r\n\t}\r\n}\r\n\r\nstatic void Q1QVMPF_SetStringGlobal(pubprogfuncs_t *progfuncs, string_t *fld, const char *str, size_t copysize)\r\n{\r\n\tif (qvm_api_version >= 15)\r\n\t{\r\n\t\tqboolean stringishacky = sizeof(quintptr_t) != sizeof(string_t) && qvm_api_version >= 15 && !VM_NonNative(q1qvm);\t//silly bullshit. Really, native gamecode should have its own implementation of this builtin or something, especially as its pretty much only ever used for classnames.\r\n\t\tif (!*fld)\r\n\t\t\tCon_Printf(\"Q1QVM: string reference not set. Fix the mod.\\n\");\r\n\t\telse if (stringishacky)\r\n\t\t{\t//quintptr_t\r\n//\t\t\tif (*fld >= 0 && *fld < sv.world.edict_size*sv.world.max_edicts - sizeof(intptr_t))\r\n\t\t\t{\r\n\t\t\t\tif (!*(quintptr_t*)((char*)gvars + *fld))\r\n\t\t\t\t{\r\n\t\t\t\t\tCon_DPrintf(\"String buffer not set. Hacking the data in instead.\\n\");\r\n\t\t\t\t\t*(quintptr_t*)((char*)gvars + *fld) = (str - (char*)VM_MemoryBase(q1qvm));\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\tchar *ptr = (char*)*(quintptr_t*)((char*)gvars + *fld);\r\n\t\t\t\t\tQ_strncpyz(ptr, str, copysize);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t\telse\r\n\t\t{\t//string_t\r\n//\t\t\tif (*fld >= 0 && *fld < sv.world.edict_size*sv.world.max_edicts - sizeof(string_t))\r\n\t\t\t{\r\n\t\t\t\tif (!*(quintptr_t*)((char*)gvars + *fld))\r\n\t\t\t\t{\r\n\t\t\t\t\tquintptr_t nval = (str - (char*)VM_MemoryBase(q1qvm));;\r\n\t\t\t\t\tif (nval > VM_MemoryMask(q1qvm))\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tCon_Printf(\"Q1QVM: String buffer not set. Data out of QVM memory space. Fix the mod.\\n\");\r\n\t\t\t\t\t}\r\n\t\t\t\t\telse\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tCon_DPrintf(\"String buffer not set. Hacking the data in instead.\\n\");\r\n\t\t\t\t\t\t*(quintptr_t*)((char*)gvars + *fld) = (str - (char*)VM_MemoryBase(q1qvm));;\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\tchar *ptr = (char*)*(quintptr_t*)((char*)gvars + *fld);\r\n\t\t\t\t\tQ_strncpyz(ptr, str, copysize);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\telse\r\n\t{\r\n\t\tif (!*fld)\r\n\t\t{\r\n\t\t\tquintptr_t nval = (str - (char*)VM_MemoryBase(q1qvm));\r\n\t\t\tif (nval > VM_MemoryMask(q1qvm))\r\n\t\t\t{\r\n\t\t\t\tCon_Printf(\"Q1QVM: String buffer not set. Data out of QVM memory space. Fix the mod.\\n\");\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tCon_DPrintf(\"String buffer not set. Hacking the data in instead.\\n\");\r\n\t\t\t\t*fld = (str - (char*)VM_MemoryBase(q1qvm));\r\n\t\t\t}\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tchar *ptr = (char*)VM_MemoryBase(q1qvm) + *fld;\r\n\t\t\tQ_strncpyz(ptr, str, copysize);\r\n\t\t}\r\n\t}\r\n}\r\n\r\nstatic int WrapQCBuiltin(builtin_t func, void *offset, quintptr_t mask, const qintptr_t *arg, char *argtypes)\r\n{\r\n\tglobalvars_t gv;\r\n\tint argnum=0;\r\n\twhile(*argtypes)\r\n\t{\r\n\t\tswitch(*argtypes++)\r\n\t\t{\r\n\t\tcase 'f':\r\n\t\t\tgv.param[argnum++].f = VM_FLOAT(*arg++);\r\n\t\t\tbreak;\r\n\t\tcase 'i':\r\n\t\t\tgv.param[argnum++].f = VM_LONG(*arg++);\t//vanilla qc does not support ints, but qvms do. this means ints need to be converted to floats for the builtin to understand them properly.\r\n\t\t\tbreak;\r\n\t\tcase 'n':\t//ent num\r\n\t\t\tgv.param[argnum++].i = EDICT_TO_PROG(svprogfuncs, Q1QVMPF_EdictNum(svprogfuncs, VM_LONG(*arg++)));\r\n\t\t\tbreak;\r\n\t\tcase 'v':\t//three seperate args -> 1 vector\r\n\t\t\tgv.param[argnum].vec[0] = VM_FLOAT(*arg++);\r\n\t\t\tgv.param[argnum].vec[1] = VM_FLOAT(*arg++);\r\n\t\t\tgv.param[argnum].vec[2] = VM_FLOAT(*arg++);\r\n\t\t\targnum++;\r\n\t\t\tbreak;\r\n\t\tcase 's':\r\n\t\t\tgv.param[argnum].i = VM_LONG(*arg++);\r\n\t\t\targnum++;\r\n\t\t\tbreak;\r\n\t\t}\r\n\t}\r\n\tsvprogfuncs->callargc = argnum;\r\n\tgv.ret.i = 0;\r\n\tfunc(svprogfuncs, &gv);\r\n\treturn gv.ret.i;\r\n}\r\n\r\n#define VALIDATEPOINTER(o,l) if ((qintptr_t)o + l >= mask || VM_POINTER(o) < offset) SV_Error(\"Call to game trap passes invalid pointer\\n\");\t//out of bounds.\r\nstatic qintptr_t QVM_GetAPIVersion (void *offset, quintptr_t mask, const qintptr_t *arg)\r\n{\r\n\treturn qvm_api_version;\r\n}\r\n\r\nstatic qintptr_t QVM_DPrint (void *offset, quintptr_t mask, const qintptr_t *arg)\r\n{\r\n\tCon_DPrintf(\"%s\", (char*)VM_POINTER(arg[0]));\r\n\treturn 0;\r\n}\r\n\r\nstatic qintptr_t QVM_Error (void *offset, quintptr_t mask, const qintptr_t *arg)\r\n{\r\n\tSV_Error(\"Q1QVM: %s\", (char*)VM_POINTER(arg[0]));\r\n\treturn 0;\r\n}\r\n\r\nstatic qintptr_t QVM_GetEntityToken (void *offset, quintptr_t mask, const qintptr_t *arg)\r\n{\r\n\tif (VM_OOB(arg[0], arg[1]) || !arg[1])\r\n\t\treturn false;\r\n\tif (q1qvmentstring)\r\n\t{\r\n\t\tchar *ret = VM_POINTER(arg[0]);\r\n\t\tq1qvmentstring = COM_Parse(q1qvmentstring);\r\n\t\tQ_strncpyz(ret, com_token, VM_LONG(arg[1]));\r\n\t\treturn q1qvmentstring != NULL;\r\n\t}\r\n\telse\r\n\t{\r\n\t\tchar *ret = VM_POINTER(arg[0]);\r\n\t\t*ret = '\\0';\r\n\t\treturn false;\r\n\t}\r\n}\r\n\r\nstatic qintptr_t QVM_Spawn_Ent (void *offset, quintptr_t mask, const qintptr_t *arg)\r\n{\r\n\treturn Q1QVMPF_EntAlloc(svprogfuncs, false, 0)->entnum;\r\n}\r\n\r\nstatic qintptr_t QVM_Remove_Ent (void *offset, quintptr_t mask, const qintptr_t *arg)\r\n{\r\n\tif (arg[0] >= sv.world.max_edicts)\r\n\t\treturn false;\r\n\tQ1QVMPF_EntRemove(svprogfuncs, q1qvmprogfuncs.edicttable[arg[0]], false);\r\n\treturn true;\r\n}\r\n\r\nstatic qintptr_t QVM_Precache_Sound (void *offset, quintptr_t mask, const qintptr_t *arg)\r\n{\r\n\treturn PF_precache_sound_Internal(svprogfuncs, VM_POINTER(arg[0]), false);\r\n}\r\nstatic qintptr_t QVM_Precache_Model (void *offset, quintptr_t mask, const qintptr_t *arg)\r\n{\r\n\treturn PF_precache_model_Internal(svprogfuncs, VM_POINTER(arg[0]), false);\r\n}\r\nstatic qintptr_t QVM_LightStyle (void *offset, quintptr_t mask, const qintptr_t *arg)\r\n{\r\n\tvec3_t rgb = {1,1,1};\r\n\tPF_applylightstyle(VM_LONG(arg[0]), VM_POINTER(arg[1]), rgb);\r\n\treturn 0;\r\n}\r\nstatic qintptr_t QVM_SetOrigin (void *offset, quintptr_t mask, const qintptr_t *arg)\r\n{\r\n\tedict_t *e = Q1QVMPF_EdictNum(svprogfuncs, VM_LONG(arg[0]));\r\n\tif (!e || ED_ISFREE(e))\r\n\t\treturn false;\r\n\r\n\te->v->origin[0] = VM_FLOAT(arg[1]);\r\n\te->v->origin[1] = VM_FLOAT(arg[2]);\r\n\te->v->origin[2] = VM_FLOAT(arg[3]);\r\n\tWorld_LinkEdict (&sv.world, (wedict_t*)e, false);\r\n\treturn true;\r\n}\r\nstatic qintptr_t QVM_SetSize (void *offset, quintptr_t mask, const qintptr_t *arg)\r\n{\r\n\tedict_t *e = Q1QVMPF_EdictNum(svprogfuncs, arg[0]);\r\n\tif (!e || ED_ISFREE(e))\r\n\t\treturn false;\r\n\r\n\te->v->mins[0] = VM_FLOAT(arg[1]);\r\n\te->v->mins[1] = VM_FLOAT(arg[2]);\r\n\te->v->mins[2] = VM_FLOAT(arg[3]);\r\n\r\n\te->v->maxs[0] = VM_FLOAT(arg[4]);\r\n\te->v->maxs[1] = VM_FLOAT(arg[5]);\r\n\te->v->maxs[2] = VM_FLOAT(arg[6]);\r\n\r\n\tVectorSubtract (e->v->maxs, e->v->mins, e->v->size);\r\n\tWorld_LinkEdict (&sv.world, (wedict_t*)e, false);\r\n\treturn true;\r\n}\r\nstatic qintptr_t QVM_SetModel (void *offset, quintptr_t mask, const qintptr_t *arg)\r\n{\r\n\tedict_t *e = Q1QVMPF_EdictNum(svprogfuncs, arg[0]);\r\n\tPF_setmodel_Internal(svprogfuncs, e, VM_POINTER(arg[1]));\r\n\treturn 0;\r\n}\r\nstatic qintptr_t QVM_BPrint (void *offset, quintptr_t mask, const qintptr_t *arg)\r\n{\r\n\tunsigned int flags = VM_LONG(arg[2]);\r\n\tif (qvm_api_version < 13)\r\n\t\tflags = 0;\t//added mid-v12, resulting in undefined values with early-12 mods.\r\n\tSV_BroadcastPrint(flags, arg[0], (char*)VM_POINTER(arg[1]));\r\n\treturn 0;\r\n}\r\nstatic qintptr_t QVM_SPrint (void *offset, quintptr_t mask, const qintptr_t *arg)\r\n{\r\n\tunsigned int clnum = VM_LONG(arg[0])-1;\r\n\tint level = VM_LONG(arg[1]);\r\n\tconst char *text = VM_POINTER(arg[2]);\r\n\tunsigned int flags = VM_LONG(arg[3]);\r\n#define SPRINT_IGNOREINDEMO   (   1<<0) // do not put such message in mvd demo\r\n\tclient_t *cl = &svs.clients[clnum];\r\n\tif (clnum >= sv.allocated_client_slots)\r\n\t\treturn 0;\r\n\tif (qvm_api_version < 13)\r\n\t\tflags = 0;\t//added mid-v12, resulting in undefined values with early-12 mods.\r\n\r\n\tif (flags & SPRINT_IGNOREINDEMO)\r\n\t{\r\n\t\tif (level >= cl->messagelevel)\r\n\t\t\tSV_PrintToClient(cl, level, text);\r\n\t}\r\n\telse\r\n\t\tSV_ClientPrintf(cl, level, \"%s\", text);\r\n\treturn 0;\r\n}\r\nstatic qintptr_t QVM_CenterPrint (void *offset, quintptr_t mask, const qintptr_t *arg)\r\n{\r\n\tPF_centerprint_Internal(VM_LONG(arg[0]), false, VM_POINTER(arg[1]));\r\n\treturn 0;\r\n}\r\nstatic qintptr_t QVM_AmbientSound (void *offset, quintptr_t mask, const qintptr_t *arg)\r\n{\r\n\tvec3_t pos;\r\n\tpos[0] = VM_FLOAT(arg[0]);\r\n\tpos[1] = VM_FLOAT(arg[1]);\r\n\tpos[2] = VM_FLOAT(arg[2]);\r\n\tPF_ambientsound_Internal(pos, VM_POINTER(arg[3]), VM_FLOAT(arg[4]), VM_FLOAT(arg[5]));\r\n\treturn 0;\r\n}\r\nstatic qintptr_t QVM_Sound (void *offset, quintptr_t mask, const qintptr_t *arg)\r\n{\r\n//\t( int edn, int channel, char *samp, float vol, float att )\r\n\tint channel = VM_LONG(arg[1]);\r\n\tint flags = 0;\r\n\tif (channel & 8)\r\n\t{\t//based on quakeworld, remember\r\n\t\tchannel = (channel & 7) | ((channel&~15)>>1);\r\n\t\tflags |= CF_SV_RELIABLE;\r\n\t}\r\n\tSVQ1_StartSound (NULL, (wedict_t*)Q1QVMPF_EdictNum(svprogfuncs, VM_LONG(arg[0])), channel, VM_POINTER(arg[2]), VM_FLOAT(arg[3])*255, VM_FLOAT(arg[4]), 0, 0, flags);\r\n\treturn 0;\r\n}\r\nstatic qintptr_t QVM_TraceLine (void *offset, quintptr_t mask, const qintptr_t *arg)\r\n{\r\n\tWrapQCBuiltin(PF_svtraceline, offset, mask, arg, \"vvin\");\r\n\treturn 0;\r\n}\r\nstatic qintptr_t QVM_CheckClient (void *offset, quintptr_t mask, const qintptr_t *arg)\r\n{\r\n\treturn PF_checkclient_Internal(svprogfuncs);\r\n}\r\nstatic qintptr_t QVM_StuffCmd (void *offset, quintptr_t mask, const qintptr_t *arg)\r\n{\r\n\tPF_stuffcmd_Internal(VM_LONG(arg[0]), VM_POINTER(arg[1]), VM_LONG(arg[2]));\r\n\treturn 0;\r\n}\r\nstatic qintptr_t QVM_LocalCmd (void *offset, quintptr_t mask, const qintptr_t *arg)\r\n{\r\n\tCbuf_AddText (VM_POINTER(arg[0]), RESTRICT_INSECURE);\r\n\treturn 0;\r\n}\r\nstatic qintptr_t QVM_CVar (void *offset, quintptr_t mask, const qintptr_t *arg)\r\n{\r\n\tcvar_t *c;\r\n\tchar *vname = VM_POINTER(arg[0]);\r\n\r\n\t//paused state is not a cvar in fte.\r\n\tif (!strcmp(vname, \"sv_paused\"))\r\n\t\treturn VM_LONG(sv.paused);\r\n\r\n\tc = Cvar_Get(vname, \"\", 0, \"Gamecode\");\r\n\treturn VM_LONG(c->value);\r\n}\r\nstatic qintptr_t QVM_CVar_Set (void *offset, quintptr_t mask, const qintptr_t *arg)\r\n{\r\n\tcvar_t *var;\r\n\tvar = Cvar_Get(VM_POINTER(arg[0]), VM_POINTER(arg[1]), 0, \"Gamecode variables\");\r\n\tif (!var)\r\n\t\treturn -1;\r\n\tCvar_Set (var, VM_POINTER(arg[1]));\r\n\treturn 0;\r\n}\r\n\r\nstatic qintptr_t QVM_FindRadius (void *offset, quintptr_t mask, const qintptr_t *arg)\r\n{\r\n\tint start = ((char*)VM_POINTER(arg[0]) - (char*)evars) / sv.world.edict_size;\r\n\tedict_t *ed;\r\n\tvec3_t diff;\r\n\tfloat *org = VM_POINTER(arg[1]);\r\n\tfloat rad = VM_FLOAT(arg[2]);\r\n\trad *= rad;\r\n\tfor(start++; start < sv.world.num_edicts; start++)\r\n\t{\r\n\t\ted = EDICT_NUM_PB(svprogfuncs, start);\r\n\t\tif (ED_ISFREE(ed))\r\n\t\t\tcontinue;\r\n\t\tVectorSubtract(ed->v->origin, org, diff);\r\n\t\tif (rad > DotProduct(diff, diff))\r\n\t\t\treturn (qintptr_t)(vevars + start*sv.world.edict_size);\r\n\t}\r\n\treturn 0;\r\n}\r\nstatic qintptr_t QVM_WalkMove (void *offset, quintptr_t mask, const qintptr_t *arg)\r\n{\r\n\twedict_t *ed = WEDICT_NUM_UB(svprogfuncs, arg[0]);\r\n\tfloat yaw = VM_FLOAT(arg[1]);\r\n\tfloat dist = VM_FLOAT(arg[2]);\r\n\tvec3_t move;\r\n\tvec3_t axis[3];\r\n\r\n\tWorld_GetEntGravityAxis(ed, axis);\r\n\r\n\tyaw = yaw*M_PI*2 / 360;\r\n\tmove[0] = cos(yaw)*dist;\r\n\tmove[1] = sin(yaw)*dist;\r\n\tmove[2] = 0;\r\n\r\n\treturn World_movestep(&sv.world, (wedict_t*)ed, move, axis, true, false, NULL);\r\n}\r\nstatic qintptr_t QVM_DropToFloor (void *offset, quintptr_t mask, const qintptr_t *arg)\r\n{\r\n\tedict_t\t\t*ent;\r\n\tpvec3_t\t\tend;\r\n\tpvec3_t\t\tstart;\r\n\ttrace_t\t\ttrace;\r\n\textern cvar_t pr_droptofloorunits;\r\n\r\n\tent = EDICT_NUM_UB(svprogfuncs, arg[0]);\r\n\r\n\tVectorCopy (ent->v->origin, end);\r\n\tif (pr_droptofloorunits.value > 0)\r\n\t\tend[2] -= pr_droptofloorunits.value;\r\n\telse\r\n\t\tend[2] -= 256;\r\n\r\n\tVectorCopy (ent->v->origin, start);\r\n\ttrace = World_Move (&sv.world, start, ent->v->mins, ent->v->maxs, end, MOVE_NORMAL, (wedict_t*)ent);\r\n\r\n\tif (trace.fraction == 1 || trace.allsolid)\r\n\t\treturn false;\r\n\telse\r\n\t{\r\n\t\tVectorCopy (trace.endpos, ent->v->origin);\r\n\t\tWorld_LinkEdict (&sv.world, (wedict_t*)ent, false);\r\n\t\tent->v->flags = (int)ent->v->flags | FL_ONGROUND;\r\n\t\tent->v->groundentity = EDICT_TO_PROG(svprogfuncs, trace.ent);\r\n\t\treturn true;\r\n\t}\r\n}\r\nstatic qintptr_t QVM_CheckBottom (void *offset, quintptr_t mask, const qintptr_t *arg)\r\n{\r\n\tvec3_t up = {0,0,1};\r\n\treturn World_CheckBottom(&sv.world, (wedict_t*)EDICT_NUM_UB(svprogfuncs, VM_LONG(arg[0])), up);\r\n}\r\nstatic qintptr_t QVM_PointContents (void *offset, quintptr_t mask, const qintptr_t *arg)\r\n{\r\n\tvec3_t v;\r\n\tv[0] = VM_FLOAT(arg[0]);\r\n\tv[1] = VM_FLOAT(arg[1]);\r\n\tv[2] = VM_FLOAT(arg[2]);\r\n\treturn sv.world.worldmodel->funcs.PointContents(sv.world.worldmodel, NULL, v);\r\n}\r\nstatic qintptr_t QVM_NextEnt (void *offset, quintptr_t mask, const qintptr_t *arg)\r\n{\t//input output are entity numbers\r\n\tunsigned int i;\r\n\tedict_t\t*ent;\r\n\r\n\ti = VM_LONG(arg[0]);\r\n\twhile (1)\r\n\t{\r\n\t\ti++;\r\n\t\tif (i >= sv.world.num_edicts)\r\n\t\t{\r\n\t\t\treturn 0;\r\n\t\t}\r\n\t\tent = EDICT_NUM_PB(svprogfuncs, i);\r\n\t\tif (!ED_ISFREE(ent))\r\n\t\t{\r\n\t\t\treturn i;\r\n\t\t}\r\n\t}\r\n}\r\nstatic qintptr_t QVM_Aim (void *offset, quintptr_t mask, const qintptr_t *arg)\r\n{\r\n\tCon_DPrintf(\"QVM_Aim: not implemented\\n\");\r\n\treturn 0;\t//not in mvdsv anyway\r\n}\r\nstatic qintptr_t QVM_MakeStatic (void *offset, quintptr_t mask, const qintptr_t *arg)\r\n{\r\n\tWrapQCBuiltin(PF_makestatic, offset, mask, arg, \"n\");\r\n\treturn 0;\r\n}\r\nstatic qintptr_t QVM_SetSpawnParams (void *offset, quintptr_t mask, const qintptr_t *arg)\r\n{\r\n\tWrapQCBuiltin(PF_setspawnparms, offset, mask, arg, \"n\");\r\n\treturn 0;\r\n}\r\nstatic qintptr_t QVM_ChangeLevel (void *offset, quintptr_t mask, const qintptr_t *arg)\r\n{\r\n\tconst char *arg_mapname = VM_POINTER(arg[0]);\r\n\tconst char *arg_entfilename = (qvm_api_version > 13)?(VM_POINTER(arg[1])):\"\";\r\n\r\n\tchar newmap[MAX_QPATH];\r\n\tif (sv.mapchangelocked)\r\n\t\treturn 0;\r\n\r\n\tif (arg_entfilename && *arg_entfilename)\r\n\t{\r\n\t\tint nl = strlen(arg_mapname);\r\n\t\tif (!strncmp(arg_mapname, arg_entfilename, nl) && arg_mapname[nl]=='#')\r\n\t\t\targ_mapname = arg_entfilename;\r\n\t\telse\r\n\t\t\tCon_Printf(CON_ERROR\"%s: named ent file does not match map\\n\", \"QVM_ChangeLevel\");\r\n\t}\r\n\r\n\tsv.mapchangelocked = true;\r\n\tCOM_QuotedString(arg_mapname, newmap, sizeof(newmap), false);\r\n\tCbuf_AddText (va(\"\\nchangelevel %s\\n\", newmap), RESTRICT_LOCAL);\r\n\treturn 1;\r\n}\r\nstatic qintptr_t QVM_ChangeLevelHub (void *offset, quintptr_t mask, const qintptr_t *arg)\r\n{\r\n\tconst char *arg_mapname = VM_POINTER(arg[0]);\r\n\tconst char *arg_entfilename = VM_POINTER(arg[1]);\r\n\tconst char *arg_startspot = VM_POINTER(arg[2]);\r\n\r\n\tchar newmap[MAX_QPATH];\r\n\tchar startspot[MAX_QPATH];\r\n\tif (sv.mapchangelocked)\r\n\t\treturn 0;\r\n\r\n\tif (arg_entfilename && *arg_entfilename)\r\n\t{\r\n\t\tint nl = strlen(arg_mapname);\r\n\t\tif (!strncmp(arg_mapname, arg_entfilename, nl) && arg_mapname[nl]=='#')\r\n\t\t\targ_mapname = arg_entfilename;\r\n\t\telse\r\n\t\t\tCon_Printf(CON_ERROR\"%s: named ent file does not match map\\n\", \"QVM_ChangeLevelHub\");\r\n\t}\r\n\r\n\tsv.mapchangelocked = true;\r\n\tCOM_QuotedString(arg_mapname, newmap, sizeof(newmap), false);\r\n\tCOM_QuotedString(arg_startspot, startspot, sizeof(startspot), false);\r\n\tCbuf_AddText (va(\"\\nchangelevel %s %s\\n\", newmap, startspot), RESTRICT_LOCAL);\r\n\treturn 1;\r\n}\r\nstatic qintptr_t QVM_LogFrag (void *offset, quintptr_t mask, const qintptr_t *arg)\r\n{\r\n\tWrapQCBuiltin(PF_logfrag, offset, mask, arg, \"nn\");\r\n\treturn 0;\r\n}\r\nstatic qintptr_t QVM_Precache_VWep_Model (void *offset, quintptr_t mask, const qintptr_t *arg)\r\n{\r\n\tconst char *s = VM_POINTER(arg[0]);\r\n\tint i;\r\n\r\n\tif (!*s || strchr(s, '\\\"') || strchr(s, ';') || strchr(s, '\\t') || strchr(s, '\\n'))\r\n\t\tCon_Printf(\"QVM_Precache_VWep_Model: bad string\\n\");\r\n\telse\r\n\t{\r\n\t\tfor (i = 0; i < sizeof(sv.strings.vw_model_precache)/sizeof(sv.strings.vw_model_precache[0]); i++)\r\n\t\t{\r\n\t\t\tif (!sv.strings.vw_model_precache[i])\r\n\t\t\t{\r\n\t\t\t\tif (sv.state != ss_loading)\r\n\t\t\t\t{\r\n\t\t\t\t\tCon_Printf(\"QVM_Precache_VWep_Model: not spawning\\n\");\r\n\t\t\t\t\treturn 0;\r\n\t\t\t\t}\r\n\t\t\t\tsv.strings.vw_model_precache[i] = s;\r\n\t\t\t\treturn i;\r\n\t\t\t}\r\n\t\t\tif (!strcmp(sv.strings.vw_model_precache[i], s))\r\n\t\t\t\treturn i;\r\n\t\t}\r\n\t\tCon_Printf(\"QVM_Precache_VWep_Model: overflow\\n\");\r\n\t}\r\n\treturn 0;\r\n}\r\nstatic qintptr_t QVM_GetInfoKey (void *offset, quintptr_t mask, const qintptr_t *arg)\r\n{\r\n\tchar *v;\r\n\tif (VM_OOB(arg[2], arg[3]))\r\n\t\treturn -1;\r\n\tv = PF_infokey_Internal(VM_LONG(arg[0]), VM_POINTER(arg[1]));\r\n\tQ_strncpyz(VM_POINTER(arg[2]), v, VM_LONG(arg[3]));\r\n\treturn 0;\r\n}\r\nstatic qintptr_t QVM_Multicast (void *offset, quintptr_t mask, const qintptr_t *arg)\r\n{\r\n\tWrapQCBuiltin(PF_multicast, offset, mask, arg, \"vi\");\r\n\treturn 0;\r\n}\r\nstatic qintptr_t QVM_DisableUpdates (void *offset, quintptr_t mask, const qintptr_t *arg)\r\n{\r\n\t//FIXME: remember to ask mvdsv people why this is useful\r\n\tCon_Printf(\"G_DISABLEUPDATES: not supported\\n\");\r\n\treturn 0;\r\n}\r\nstatic qintptr_t QVM_WriteByte (void *offset, quintptr_t mask, const qintptr_t *arg)\r\n{\r\n\tWrapQCBuiltin(PF_WriteByte, offset, mask, arg, \"ii\");\r\n\treturn 0;\r\n}\r\nstatic qintptr_t QVM_WriteChar (void *offset, quintptr_t mask, const qintptr_t *arg)\r\n{\r\n\tWrapQCBuiltin(PF_WriteChar, offset, mask, arg, \"ii\");\r\n\treturn 0;\r\n}\r\nstatic qintptr_t QVM_WriteShort (void *offset, quintptr_t mask, const qintptr_t *arg)\r\n{\r\n\tWrapQCBuiltin(PF_WriteShort, offset, mask, arg, \"ii\");\r\n\treturn 0;\r\n}\r\nstatic qintptr_t QVM_WriteLong (void *offset, quintptr_t mask, const qintptr_t *arg)\r\n{\r\n\tWrapQCBuiltin(PF_WriteLong, offset, mask, arg, \"ii\");\r\n\treturn 0;\r\n}\r\nstatic qintptr_t QVM_WriteAngle (void *offset, quintptr_t mask, const qintptr_t *arg)\r\n{\r\n\tWrapQCBuiltin(PF_WriteAngle, offset, mask, arg, \"if\");\r\n\treturn 0;\r\n}\r\nstatic qintptr_t QVM_WriteCoord (void *offset, quintptr_t mask, const qintptr_t *arg)\r\n{\r\n\tWrapQCBuiltin(PF_WriteCoord, offset, mask, arg, \"if\");\r\n\treturn 0;\r\n}\r\nstatic qintptr_t QVM_WriteString (void *offset, quintptr_t mask, const qintptr_t *arg)\r\n{\r\n\tPF_WriteString_Internal(VM_LONG(arg[0]), VM_POINTER(arg[1]));\r\n\treturn 0;\r\n}\r\nstatic qintptr_t QVM_WriteEntity (void *offset, quintptr_t mask, const qintptr_t *arg)\r\n{\r\n\tWrapQCBuiltin(PF_WriteEntity, offset, mask, arg, \"in\");\r\n\treturn 0;\r\n}\r\nstatic qintptr_t QVM_FlushSignon (void *offset, quintptr_t mask, const qintptr_t *arg)\r\n{\r\n\tSV_FlushSignon (false);\r\n\treturn 0;\r\n}\r\nstatic qintptr_t QVM_memset (void *offset, quintptr_t mask, const qintptr_t *arg)\r\n{\r\n\tvoid *dst = VM_POINTER(arg[0]);\r\n\tVALIDATEPOINTER(arg[0], arg[2]);\r\n\tmemset(dst, arg[1], arg[2]);\r\n\treturn arg[0];\r\n}\r\nstatic qintptr_t QVM_memcpy (void *offset, quintptr_t mask, const qintptr_t *arg)\r\n{\r\n\tvoid *dst = VM_POINTER(arg[0]);\r\n\tvoid *src = VM_POINTER(arg[1]);\r\n\tVALIDATEPOINTER(arg[0], arg[2]);\r\n\tmemmove(dst, src, arg[2]);\r\n\treturn arg[0];\r\n}\r\nstatic qintptr_t QVM_strncpy (void *offset, quintptr_t mask, const qintptr_t *arg)\r\n{\r\n\tVALIDATEPOINTER(arg[0], arg[2]);\r\n\tQ_strncpyS(VM_POINTER(arg[0]), VM_POINTER(arg[1]), arg[2]);\r\n\treturn arg[0];\r\n}\r\nstatic qintptr_t QVM_sin (void *offset, quintptr_t mask, const qintptr_t *arg)\r\n{\r\n\tunion\r\n\t{\r\n\t\tqintptr_t r;\r\n\t\tfloat f;\r\n\t} u = {0};\r\n\tu.f = sin(VM_FLOAT(arg[0]));\r\n\treturn u.r;\r\n}\r\nstatic qintptr_t QVM_cos (void *offset, quintptr_t mask, const qintptr_t *arg)\r\n{\r\n\tunion\r\n\t{\r\n\t\tqintptr_t r;\r\n\t\tfloat f;\r\n\t} u = {0};\r\n\tu.f = cos(VM_FLOAT(arg[0]));\r\n\treturn u.r;\r\n}\r\nstatic qintptr_t QVM_atan2 (void *offset, quintptr_t mask, const qintptr_t *arg)\r\n{\r\n\tunion\r\n\t{\r\n\t\tqintptr_t r;\r\n\t\tfloat f;\r\n\t} u = {0};\r\n\tu.f = atan2(VM_FLOAT(arg[0]), VM_FLOAT(arg[1]));\r\n\treturn u.r;\r\n}\r\nstatic qintptr_t QVM_sqrt (void *offset, quintptr_t mask, const qintptr_t *arg)\r\n{\r\n\tunion\r\n\t{\r\n\t\tqintptr_t r;\r\n\t\tfloat f;\r\n\t} u = {0};\r\n\tu.f = sqrt(VM_FLOAT(arg[0]));\r\n\treturn u.r;\r\n}\r\nstatic qintptr_t QVM_floor (void *offset, quintptr_t mask, const qintptr_t *arg)\r\n{\r\n\tunion\r\n\t{\r\n\t\tqintptr_t r;\r\n\t\tfloat f;\r\n\t} u = {0};\r\n\tu.f = floor(VM_FLOAT(arg[0]));\r\n\treturn u.r;\r\n}\r\nstatic qintptr_t QVM_ceil (void *offset, quintptr_t mask, const qintptr_t *arg)\r\n{\r\n\tunion\r\n\t{\r\n\t\tqintptr_t r;\r\n\t\tfloat f;\r\n\t} u = {0};\r\n\tu.f = ceil(VM_FLOAT(arg[0]));\r\n\treturn u.r;\r\n}\r\nstatic qintptr_t QVM_acos (void *offset, quintptr_t mask, const qintptr_t *arg)\r\n{\r\n\tunion\r\n\t{\r\n\t\tqintptr_t r;\r\n\t\tfloat f;\r\n\t} u = {0};\r\n\tu.f = acos(VM_FLOAT(arg[0]));\r\n\treturn u.r;\r\n}\r\nstatic qintptr_t QVM_Cmd_ArgC (void *offset, quintptr_t mask, const qintptr_t *arg)\r\n{\r\n\treturn Cmd_Argc();\r\n}\r\nstatic qintptr_t QVM_Cmd_ArgV (void *offset, quintptr_t mask, const qintptr_t *arg)\r\n{\r\n\tchar *c;\r\n\tc = Cmd_Argv(VM_LONG(arg[0]));\r\n\tif (VM_OOB(arg[1], arg[2]))\r\n\t\treturn -1;\r\n\tQ_strncpyz(VM_POINTER(arg[1]), c, VM_LONG(arg[2]));\r\n\treturn 0;\r\n}\r\nstatic qintptr_t QVM_TraceBox (void *offset, quintptr_t mask, const qintptr_t *arg)\r\n{\r\n\tWrapQCBuiltin(PF_svtraceline, offset, mask, arg, \"vvinvv\");\r\n\treturn 0;\r\n}\r\n\r\n\r\n\r\ntypedef struct {\r\n\tvfsfile_t *file;\r\n} vm_fopen_files_t;\r\nstatic vm_fopen_files_t vm_fopen_files[64];\r\n\r\nstatic qintptr_t QVM_FS_OpenFile (void *offset, quintptr_t mask, const qintptr_t *arg)\r\n{\r\n\tconst char *name = VM_POINTER(arg[0]);\r\n\tint *handle = VM_POINTER(arg[1]);\r\n\tint fmode = VM_LONG(arg[2]);\r\n\tint fnum;\r\n\tstatic struct {\r\n\t\tconst char *mode;\r\n\t\tenum fs_relative root;\r\n\t} mode[] = {\r\n\t\t/*FS_READ_BIN*/{\"rb\",FS_GAME},\r\n\t\t/*FS_READ_TXT*/{\"rt\",FS_GAME},\r\n\t\t/*FS_WRITE_BIN*/{\"wb\",FS_GAMEONLY},\r\n\t\t/*FS_WRITE_TXT*/{\"wt\",FS_GAMEONLY},\r\n\t\t/*FS_APPEND_BIN*/{\"ab\",FS_GAMEONLY},\r\n\t\t/*FS_APPEND_TXT*/{\"at\",FS_GAMEONLY},\r\n\t};\r\n\tif (fmode < 0 || fmode >= countof(mode))\r\n\t\treturn -1;\r\n\tfor (fnum = 0; fnum < countof(vm_fopen_files); fnum++)\r\n\t\tif (!vm_fopen_files[fnum].file)\r\n\t\t\tbreak;\r\n\tif (fnum == countof(vm_fopen_files))\t//too many already open\r\n\t\treturn -1;\r\n\r\n\tvm_fopen_files[fnum].file = FS_OpenVFS(name, mode[fmode].mode, mode[fmode].root);\r\n\tif (!vm_fopen_files[fnum].file)\r\n\t\treturn -1;\r\n\t*handle = fnum+1;\r\n\treturn VFS_GETLEN(vm_fopen_files[fnum].file);\r\n}\r\nstatic qintptr_t QVM_FS_CloseFile (void *offset, quintptr_t mask, const qintptr_t *arg)\r\n{\r\n\tint fnum = VM_LONG(arg[0])-1;\r\n\tif (fnum >= 0 && fnum < countof(vm_fopen_files) && vm_fopen_files[fnum].file)\r\n\t{\r\n\t\tVFS_CLOSE(vm_fopen_files[fnum].file);\r\n\t\tvm_fopen_files[fnum].file = NULL;\r\n\t\treturn 0;\r\n\t}\r\n\treturn -1;\r\n}\r\nstatic void QVM_FS_CloseFileAll (void)\r\n{\r\n\tsize_t fnum;\r\n\tfor (fnum = 0; fnum < countof(vm_fopen_files); fnum++)\r\n\t\tif (vm_fopen_files[fnum].file)\r\n\t\t{\r\n\t\t\tVFS_CLOSE(vm_fopen_files[fnum].file);\r\n\t\t\tvm_fopen_files[fnum].file = NULL;\r\n\t\t}\r\n}\r\nstatic qintptr_t QVM_FS_ReadFile (void *offset, quintptr_t mask, const qintptr_t *arg)\r\n{\r\n\tvoid *dest = VM_POINTER(arg[0]);\r\n\tint size = VM_LONG(arg[1]);\r\n\tint fnum = VM_LONG(arg[2])-1;\r\n\tif (VM_OOB(arg[0], size))\r\n\t\treturn 0;\r\n\tif (fnum >= 0 && fnum < countof(vm_fopen_files) && vm_fopen_files[fnum].file && vm_fopen_files[fnum].file->ReadBytes)\r\n\t\treturn VFS_READ(vm_fopen_files[fnum].file, dest, size);\r\n\treturn 0;\r\n}\r\nstatic qintptr_t QVM_FS_WriteFile (void *offset, quintptr_t mask, const qintptr_t *arg)\r\n{\r\n\tvoid *dest = VM_POINTER(arg[0]);\r\n\tint size = VM_LONG(arg[1]);\r\n\tint fnum = VM_LONG(arg[2])-1;\r\n\tif (VM_OOB(arg[0], size))\r\n\t\treturn 0;\r\n\tif (fnum >= 0 && fnum < countof(vm_fopen_files) && vm_fopen_files[fnum].file && vm_fopen_files[fnum].file->ReadBytes)\r\n\t\treturn VFS_WRITE(vm_fopen_files[fnum].file, dest, size);\r\n\treturn 0;\r\n}\r\nstatic qintptr_t QVM_FS_SeekFile (void *offset, quintptr_t mask, const qintptr_t *arg)\r\n{\r\n\tint fnum = VM_LONG(arg[0])-1;\r\n\tquintptr_t foffset = arg[1];\r\n\tint seektype = VM_LONG(arg[2]);\r\n\tif (fnum >= 0 && fnum < countof(vm_fopen_files) && vm_fopen_files[fnum].file && vm_fopen_files[fnum].file->seekstyle != SS_UNSEEKABLE)\r\n\t{\r\n\t\tif (seektype == 0)\t//cur\r\n\t\t\tfoffset += VFS_TELL(vm_fopen_files[fnum].file);\r\n\t\telse if (seektype == 2)\t//end\r\n\t\t\tfoffset = VFS_GETLEN(vm_fopen_files[fnum].file) + (qintptr_t)foffset;\r\n\t\treturn VFS_SEEK(vm_fopen_files[fnum].file, foffset);\r\n\t}\r\n\treturn 0;\r\n}\r\nstatic qintptr_t QVM_FS_TellFile (void *offset, quintptr_t mask, const qintptr_t *arg)\r\n{\r\n\tint fnum = VM_LONG(arg[0])-1;\r\n\tif (fnum >= 0 && fnum < countof(vm_fopen_files) && vm_fopen_files[fnum].file)\r\n\t{\r\n\t\treturn VFS_TELL(vm_fopen_files[fnum].file);\r\n\t}\r\n\treturn -1;\r\n}\r\n\r\n\r\n\r\n\r\n//filesystem searches result in a tightly-packed blob of null-terminated filenames (along with a count for how many entries)\r\ntypedef struct {\r\n\tchar *initialbuffer;\r\n\tchar *buffer;\r\n\tint found;\r\n\tint bufferleft;\r\n\tint skip;\r\n} vmsearch_t;\r\nstatic int QDECL VMEnum(const char *match, qofs_t size, time_t mtime, void *args, searchpathfuncs_t *spath)\r\n{\r\n\tchar *check;\r\n\tint newlen;\r\n\tmatch += ((vmsearch_t *)args)->skip;\r\n\tnewlen = strlen(match)+1;\r\n\tif (newlen > ((vmsearch_t *)args)->bufferleft)\r\n\t\treturn false;\t//too many files for the buffer\r\n\r\n\tcheck = ((vmsearch_t *)args)->initialbuffer;\r\n\twhile(check < ((vmsearch_t *)args)->buffer)\r\n\t{\r\n\t\tif (!Q_strcasecmp(check, match))\r\n\t\t\treturn true;\t//we found this one already\r\n\t\tcheck += strlen(check)+1;\r\n\t}\r\n\r\n\tmemcpy(((vmsearch_t *)args)->buffer, match, newlen);\r\n\t((vmsearch_t *)args)->buffer+=newlen;\r\n\t((vmsearch_t *)args)->bufferleft-=newlen;\r\n\t((vmsearch_t *)args)->found++;\r\n\treturn true;\r\n}\r\nstatic qintptr_t QVM_FS_GetFileList (void *offset, quintptr_t mask, const qintptr_t *arg)\r\n{\r\n\tvmsearch_t vms;\r\n\tconst char *path = VM_POINTER(arg[0]);\r\n\tconst char *ext = VM_POINTER(arg[1]);\r\n\tchar *output = VM_POINTER(arg[2]);\r\n\tsize_t buffersize = VM_LONG(arg[3]);\r\n\tif (VM_OOB(arg[2], buffersize))\r\n\t\treturn 0;\r\n\r\n\tvms.initialbuffer = vms.buffer = output;\r\n\tvms.skip = strlen(path)+1;\r\n\tvms.bufferleft = buffersize;\r\n\tvms.found=0;\r\n\tif (*(const char *)ext == '.' || *(const char *)ext == '/')\r\n\t\tCOM_EnumerateFiles(va(\"%s/*%s\", path, ext), VMEnum, &vms);\r\n\telse\r\n\t\tCOM_EnumerateFiles(va(\"%s/*.%s\", path, ext), VMEnum, &vms);\r\n\treturn vms.found;\r\n}\r\nstatic qintptr_t QVM_CVar_Set_Float (void *offset, quintptr_t mask, const qintptr_t *arg)\r\n{\r\n\tcvar_t *var;\r\n\tvar = Cvar_Get(VM_POINTER(arg[0]), va(\"%f\", VM_FLOAT(arg[1])), 0, \"Gamecode variables\");\r\n\tif (!var)\r\n\t\treturn -1;\r\n\tCvar_SetValue (var, VM_FLOAT(arg[1]));\r\n\treturn 0;\r\n}\r\nstatic qintptr_t QVM_CVar_String (void *offset, quintptr_t mask, const qintptr_t *arg)\r\n{\r\n\tchar *n = VM_POINTER(arg[0]);\r\n\tcvar_t *cv;\r\n\tif (VM_OOB(arg[1], arg[2]))\r\n\t\treturn -1;\r\n\tif (!strcmp(n, \"version\"))\r\n\t{\r\n\t\tn = version_string();\r\n\t\tQ_strncpyz(VM_POINTER(arg[1]), n, VM_LONG(arg[2]));\r\n\t}\r\n\telse\r\n\t{\r\n\t\tcv = Cvar_Get(n, \"\", 0, \"QC variables\");\r\n\t\tif (cv)\r\n\t\t\tQ_strncpyz(VM_POINTER(arg[1]), cv->string, VM_LONG(arg[2]));\r\n\t\telse\r\n\t\t\tQ_strncpyz(VM_POINTER(arg[1]), \"\", VM_LONG(arg[2]));\r\n\t}\r\n\treturn 0;\r\n}\r\nstatic qintptr_t QVM_strcmp (void *offset, quintptr_t mask, const qintptr_t *arg)\r\n{\r\n\tchar *a = VM_POINTER(arg[0]);\r\n\tchar *b = VM_POINTER(arg[1]);\r\n\treturn strcmp(a, b);\r\n}\r\nstatic qintptr_t QVM_strncmp (void *offset, quintptr_t mask, const qintptr_t *arg)\r\n{\r\n\tchar *a = VM_POINTER(arg[0]);\r\n\tchar *b = VM_POINTER(arg[1]);\r\n\treturn strncmp(a, b, VM_LONG(arg[2]));\r\n}\r\nstatic qintptr_t QVM_stricmp (void *offset, quintptr_t mask, const qintptr_t *arg)\r\n{\r\n\tchar *a = VM_POINTER(arg[0]);\r\n\tchar *b = VM_POINTER(arg[1]);\r\n\treturn stricmp(a, b);\r\n}\r\nstatic qintptr_t QVM_strnicmp (void *offset, quintptr_t mask, const qintptr_t *arg)\r\n{\r\n\tchar *a = VM_POINTER(arg[0]);\r\n\tchar *b = VM_POINTER(arg[1]);\r\n\treturn strnicmp(a, b, VM_LONG(arg[2]));\r\n}\r\nstatic qintptr_t QVM_Find (void *offset, quintptr_t mask, const qintptr_t *arg)\r\n{\r\n\tedict_t *e = VM_POINTER(arg[0]);\r\n\tint ofs = (VM_LONG(arg[1]) - wasted_edict_t_size);\r\n\tchar *match = VM_POINTER(arg[2]);\r\n\tchar *field;\r\n\tint first = e?((char*)e - (char*)evars)/sv.world.edict_size:0;\r\n\tint i;\r\n\tqboolean stringishacky = sizeof(quintptr_t) != sizeof(string_t) && qvm_api_version >= 15 && !VM_NonNative(q1qvm);\t//silly bullshit. Really, native gamecode should have its own implementation of this builtin or something, especially as its pretty much only ever used for classnames.\r\n\tif (!match)\r\n\t\tmatch = \"\";\r\n\tfor (i = first+1; i < sv.world.num_edicts; i++)\r\n\t{\r\n\t\te = q1qvmprogfuncs.edicttable[i];\r\n\t\tif (stringishacky)\r\n\t\t\tfield = VM_POINTER(*(quintptr_t*)((char*)e->v + ofs));\r\n\t\telse\r\n\t\t\tfield = VM_POINTER(*(string_t*)((char*)e->v + ofs));\r\n\t\tif (field == NULL)\r\n\t\t{\r\n\t\t\tif (*match == '\\0')\r\n\t\t\t\treturn ((char*)e->v - (char*)offset)-wasted_edict_t_size;\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tif (!strcmp(field, match))\r\n\t\t\t\treturn ((char*)e->v - (char*)offset)-wasted_edict_t_size;\r\n\t\t}\r\n\t}\r\n\treturn 0;\r\n}\r\nstatic qintptr_t QVM_ExecuteCmd (void *offset, quintptr_t mask, const qintptr_t *arg)\r\n{\r\n\tWrapQCBuiltin(PF_ExecuteCommand, offset, mask, arg, \"\");\r\n\treturn 0;\r\n}\r\nstatic qintptr_t QVM_ConPrint (void *offset, quintptr_t mask, const qintptr_t *arg)\r\n{\r\n\tCon_Printf(\"%s\", (char*)VM_POINTER(arg[0]));\r\n\treturn 0;\r\n}\r\nstatic qintptr_t QVM_ReadCmd (void *offset, quintptr_t mask, const qintptr_t *arg)\r\n{\r\n\textern char sv_redirected_buf[];\r\n\textern redirect_t sv_redirected;\r\n\textern int sv_redirectedlang;\r\n\tredirect_t old;\r\n\tint oldl;\r\n\tint spawncount = svs.spawncount;\r\n\r\n\tchar *s = VM_POINTER(arg[0]);\r\n\tchar *output = VM_POINTER(arg[1]);\r\n\tint outputlen = VM_LONG(arg[2]);\r\n\r\n\tif (VM_OOB(arg[1], arg[2]))\r\n\t\treturn -1;\r\n\r\n\tCbuf_Execute();\t//FIXME: this code is flawed\r\n\tif (svs.spawncount != spawncount || sv.state < ss_loading)\r\n\t\tHost_EndGame(\"QVM_ReadCmd: Map changed before reading\");\r\n\tCbuf_AddText (s, RESTRICT_LOCAL);\r\n\r\n\told = sv_redirected;\r\n\toldl = sv_redirectedlang;\r\n\tif (old != RD_NONE)\r\n\t\tSV_EndRedirect();\r\n\r\n\tSV_BeginRedirect(RD_OBLIVION, TL_FindLanguage(\"\"));\r\n\tCbuf_Execute();\r\n\tif (svs.spawncount != spawncount || sv.state < ss_loading)\r\n\t{\r\n\t\tSV_EndRedirect();\r\n\t\tHost_EndGame(\"QVM_ReadCmd: Map changed after reading\");\r\n\t}\r\n\tQ_strncpyz(output, sv_redirected_buf, outputlen);\r\n\tSV_EndRedirect();\r\n\r\n\tif (old != RD_NONE)\r\n\t\tSV_BeginRedirect(old, oldl);\r\n\r\nCon_DPrintf(\"PF_readcmd: %s\\n%s\", s, output);\r\n\r\n\treturn 0;\r\n}\r\n\r\n\r\n\r\nstatic void QVM_RedirectCmdCallback(struct frameendtasks_s *task)\r\n{\t//called at the end of the frame when there's no gamecode running\r\n\thost_client = svs.clients + task->ctxint;\r\n\tif (host_client->state >= cs_connected)\r\n\t{\r\n\t\tSV_BeginRedirect(RD_CLIENT, host_client->language);\r\n\t\tCmd_ExecuteString(task->data, RESTRICT_INSECURE);\r\n\t\tSV_EndRedirect();\r\n\t}\r\n}\r\nstatic qintptr_t QVM_RedirectCmd (void *offset, quintptr_t mask, const qintptr_t *arg)\r\n{\r\n\tstruct frameendtasks_s *task, **link;\r\n\tunsigned int entnum = ((char*)VM_POINTER(arg[0]) - (char*)evars) / sv.world.edict_size;\r\n\tconst char *s = VM_POINTER(arg[1]);\r\n\tif (entnum < 1 || entnum > sv.allocated_client_slots)\r\n\t\tSV_Error (\"QVM_RedirectCmd: Parm 0 not a client\");\r\n\r\n\ttask = Z_Malloc(sizeof(*task)+strlen(s));\r\n\ttask->callback = QVM_RedirectCmdCallback;\r\n\tstrcpy(task->data, s);\r\n\ttask->ctxint = entnum-1;\r\n\tfor(link = &svs.frameendtasks; *link; link = &(*link)->next)\r\n\t\t;\t//add them on the end, so they're execed in order\r\n\t*link = task;\r\n\treturn 0;\r\n}\r\nstatic qintptr_t QVM_Add_Bot (void *offset, quintptr_t mask, const qintptr_t *arg)\r\n{\r\n\tchar *name = VM_POINTER(arg[0]);\r\n\tint bottom = VM_LONG(arg[1]);\r\n\tint top = VM_LONG(arg[2]);\r\n\tchar *skin = VM_POINTER(arg[3]);\r\n\r\n#if 1\r\n\textern int nextuserid;\r\n\tint i;\r\n\tfor (i = 0; i < sv.allocated_client_slots; i++)\r\n\t{\r\n\t\tclient_t *cl = &svs.clients[i];\r\n\t\tif (!*cl->name && !cl->protocol && cl->state == cs_free)\r\n\t\t{\r\n\t\t\tcl->userid = ++nextuserid;\r\n\t\t\tcl->protocol = SCP_BAD;\t//marker for bots\r\n\t\t\tcl->state = cs_spawned;\r\n\t\t\tcl->connection_started = realtime;\r\n\t\t\tcl->spawned = true;\r\n\t\t\tsv.spawned_client_slots++;\r\n\t\t\tcl->netchan.message.allowoverflow = true;\r\n\t\t\tcl->netchan.message.maxsize = 0;\r\n\t\t\tcl->datagram.allowoverflow = true;\r\n\t\t\tcl->datagram.maxsize = 0;\r\n\r\n\t\t\tcl->edict = EDICT_NUM_PB(sv.world.progs, i+1);\r\n\r\n\t\t\tInfoBuf_SetKey(&cl->userinfo, \"name\", name);\r\n\t\t\tInfoBuf_SetKey(&cl->userinfo, \"topcolor\", va(\"%i\", top));\r\n\t\t\tInfoBuf_SetKey(&cl->userinfo, \"bottomcolor\", va(\"%i\", bottom));\r\n\t\t\tInfoBuf_SetKey(&cl->userinfo, \"skin\", skin);\r\n\t\t\tInfoBuf_SetStarKey(&cl->userinfo, \"*bot\", \"1\");\r\n\t\t\tSV_ExtractFromUserinfo(cl, true);\r\n\t\t\tSV_SetUpClientEdict (cl, cl->edict);\r\n\r\n\t\t\tSV_FullClientUpdate(cl, NULL);\r\n\t\t\tQ1QVM_ClientConnect(cl);\r\n\r\n\t\t\treturn cl->edict->entnum;\r\n\t\t}\r\n\t}\r\n#else\r\n\t//FIXME: not implemented, always returns failure.\r\n\t//the other bot functions only ever work on bots anyway, so don't need to be implemented until this one is\r\n\r\n\t//return WrapQCBuiltin(PF_spawnclient, offset, mask, arg, \"\");\r\n\tCon_DPrintf(\"QVM_Add_Bot: not implemented\\n\");\r\n#endif\r\n\treturn 0;\r\n}\r\nstatic qintptr_t QVM_Remove_Bot (void *offset, quintptr_t mask, const qintptr_t *arg)\r\n{\r\n\t//kicks NOW. which generally makes it unsafe for players (calling from StartFrame should be okay).\r\n\tint entnum = VM_LONG(arg[0]);\r\n\tif (entnum >= 1 && entnum <= svs.allocated_client_slots)\r\n\t{\r\n\t\tclient_t *cl = &svs.clients[entnum-1];\r\n\t\tSV_DropClient(cl);\r\n\t}\r\n\r\n\treturn 0;\r\n}\r\nstatic qintptr_t QVM_SetBotCMD (void *offset, quintptr_t mask, const qintptr_t *arg)\r\n{\r\n\t//this just queues the command for later, even in mvdsv. ignore the msecs value because its basically pointless\r\n\tint edn\t\t= VM_LONG(arg[0]);\r\n\tint msec\t= VM_LONG(arg[1]);\r\n\tfloat angles_x = VM_FLOAT(arg[2]);\r\n\tfloat angles_y = VM_FLOAT(arg[3]);\r\n\tfloat angles_z = VM_FLOAT(arg[4]);\r\n\tint forwardmove = VM_LONG(arg[5]);\r\n\tint sidemove = VM_LONG(arg[6]);\r\n\tint upmove = VM_LONG(arg[7]);\r\n\tint buttons = VM_LONG(arg[8]);\r\n\tint impulse = VM_LONG(arg[9]);\r\n\r\n\tif (edn >= 1 && edn <= svs.allocated_client_slots)\r\n\t{\r\n\t\tclient_t *cl = &svs.clients[edn-1];\r\n\t\tcl->lastcmd.msec = msec;\r\n\t\tcl->lastcmd.angles[0] = ANGLE2SHORT(angles_x);\r\n\t\tcl->lastcmd.angles[1] = ANGLE2SHORT(angles_y);\r\n\t\tcl->lastcmd.angles[2] = ANGLE2SHORT(angles_z);\r\n\t\tcl->lastcmd.forwardmove = forwardmove;\r\n\t\tcl->lastcmd.sidemove = sidemove;\r\n\t\tcl->lastcmd.upmove = upmove;\r\n\t\tcl->lastcmd.buttons = buttons;\r\n\t\tcl->lastcmd.impulse = impulse;\r\n\t}\r\n\treturn 0;\r\n}\r\nstatic qintptr_t QVM_SetUserInfo (void *offset, quintptr_t mask, const qintptr_t *arg)\r\n{\r\n\tint ent = VM_LONG(arg[0]);\r\n\tconst char *key = VM_POINTER(arg[1]);\r\n\tconst char *val = VM_POINTER(arg[2]);\r\n\tif (*key == '*' && (VM_LONG(arg[3])&1))\r\n\t\treturn -1;\t//denied!\r\n\treturn PF_ForceInfoKey_Internal(ent, key, val, strlen(val));\r\n}\r\nstatic qintptr_t QVM_SetBotUserInfo (void *offset, quintptr_t mask, const qintptr_t *arg)\r\n{\r\n\tint ent = VM_LONG(arg[0]);\r\n\tconst char *key = VM_POINTER(arg[1]);\r\n\tconst char *val = VM_POINTER(arg[2]);\r\n\r\n\treturn PF_ForceInfoKey_Internal(ent, key, val,  strlen(val));\r\n}\r\nstatic qintptr_t QVM_MoveToGoal (void *offset, quintptr_t mask, const qintptr_t *arg)\r\n{\r\n\treturn World_MoveToGoal(&sv.world, (wedict_t*)Q1QVMPF_ProgsToEdict(svprogfuncs, pr_global_struct->self), VM_FLOAT(arg[0]));\r\n}\r\nstatic qintptr_t QVM_strftime (void *offset, quintptr_t mask, const qintptr_t *arg)\r\n{\r\n\tchar *out = VM_POINTER(arg[0]);\r\n\tchar *fmt = VM_POINTER(arg[2]);\r\n\ttime_t curtime;\r\n\tstruct tm *local;\r\n\tif (VM_OOB(arg[0], arg[1]) || !out)\r\n\t\treturn -1;\t//please don't corrupt me\r\n\ttime(&curtime);\r\n\tcurtime += VM_LONG(arg[3]);\r\n\tlocal = localtime(&curtime);\r\n\treturn strftime(out, VM_LONG(arg[1]), fmt, local);\r\n}\r\nstatic qintptr_t QVM_Cmd_ArgS (void *offset, quintptr_t mask, const qintptr_t *arg)\r\n{\r\n\tchar *c;\r\n\tc = Cmd_Args();\r\n\tif (VM_OOB(arg[0], arg[1]))\r\n\t\treturn -1;\r\n\tQ_strncpyz(VM_POINTER(arg[0]), c, VM_LONG(arg[1]));\r\n\treturn arg[0];\r\n}\r\nstatic qintptr_t QVM_Cmd_Tokenize (void *offset, quintptr_t mask, const qintptr_t *arg)\r\n{\r\n\tchar *str = VM_POINTER(arg[0]);\r\n\tCmd_TokenizeString(str, false, false);\r\n\treturn Cmd_Argc();\r\n}\r\nstatic qintptr_t QVM_strlcpy (void *offset, quintptr_t mask, const qintptr_t *arg)\r\n{\r\n\tchar *dst = VM_POINTER(arg[0]);\r\n\tchar *src = VM_POINTER(arg[1]);\r\n\tif (VM_OOB(arg[0], arg[2]) || VM_LONG(arg[2]) < 1)\r\n\t\treturn -1;\r\n\telse if (!src)\r\n\t{\r\n\t\t*dst = 0;\r\n\t\treturn 0;\r\n\t}\r\n\telse\r\n\t{\r\n\t\tQ_strncpyz(dst, src, VM_LONG(arg[2]));\r\n\t\treturn strlen(src);\r\n\t}\r\n}\r\nstatic qintptr_t QVM_strlcat (void *offset, quintptr_t mask, const qintptr_t *arg)\r\n{\r\n\tchar *dst = VM_POINTER(arg[0]);\r\n\tchar *src = VM_POINTER(arg[1]);\r\n\tif (VM_OOB(arg[0], arg[2]))\r\n\t\treturn -1;\r\n\tQ_strncatz(dst, src, VM_LONG(arg[2]));\r\n\t//WARNING: no return value\r\n\treturn 0;\r\n}\r\nstatic qintptr_t QVM_MakeVectors (void *offset, quintptr_t mask, const qintptr_t *arg)\r\n{\r\n\tAngleVectors(VM_POINTER(arg[0]), P_VEC(v_forward), P_VEC(v_right), P_VEC(v_up));\r\n\treturn 0;\r\n}\r\nstatic qintptr_t QVM_NextClient (void *offset, quintptr_t mask, const qintptr_t *arg)\r\n{\r\n\tunsigned int start = ((char*)VM_POINTER(arg[0]) - (char*)evars) / sv.world.edict_size;\r\n\twhile (start < sv.allocated_client_slots)\r\n\t{\r\n\t\tif (svs.clients[start].state == cs_spawned)\r\n\t\t\treturn (qintptr_t)(vevars + (start+1) * sv.world.edict_size);\r\n\t\tstart++;\r\n\t}\r\n\treturn 0;\r\n}\r\nstatic qintptr_t QVM_SetPause (void *offset, quintptr_t mask, const qintptr_t *arg)\r\n{\r\n\tint pause = VM_LONG(arg[0]);\r\n\tif ((sv.paused&PAUSE_EXPLICIT) == (pause&PAUSE_EXPLICIT))\r\n\t\treturn !!(sv.paused&PAUSE_EXPLICIT);\t//nothing changed, ignore it.\r\n\tsv.paused = pause;\r\n\tsv.pausedstart = Sys_DoubleTime();\r\n\treturn !!(sv.paused&PAUSE_EXPLICIT);\r\n}\r\n\r\nstatic qintptr_t QVM_VisibleTo (void *offset, quintptr_t mask, const qintptr_t *arg)\r\n{\r\n\tunsigned int viewernum = VM_LONG(arg[0]);\r\n\tunsigned int first = VM_LONG(arg[1]);\r\n\tunsigned int count = VM_LONG(arg[2]);\r\n\tqbyte\t\t *results = VM_POINTER(arg[3]);\r\n\tunsigned int e, last = first + count;\r\n\tunsigned int ret = 0;\r\n\r\n\tmemset(results, 0, count);\t//assume the worst...\r\n\tif (viewernum < sv.world.num_edicts && !VM_OOB(arg[3], count) && first < last && last <= sv.world.num_edicts)\r\n\t{\r\n\t\tedict_t *viewer = q1qvmprogfuncs.edicttable[viewernum];\r\n\t\tedict_t *ed;\r\n\t\tstatic pvsbuffer_t pvs;\t//bit of a leak. but only one allocation.\r\n\t\tvec3_t org;\r\n\t\tint areas[] = {2,viewer->pvsinfo.areanum, viewer->pvsinfo.areanum2};\r\n\r\n\t\tif (ED_ISFREE(viewer))\r\n\t\t\treturn ret;\r\n\r\n\t\t//FIXME: make a FatPVS that uses a pvscache_t\r\n\t\tVectorAdd(viewer->v->origin, viewer->v->view_ofs, org);\r\n\t\tsv.world.worldmodel->funcs.FatPVS(sv.world.worldmodel, org, &pvs, false);\r\n\t\tfor (e = first; e < last; e++)\r\n\t\t{\r\n\t\t\ted = q1qvmprogfuncs.edicttable[e];\r\n\t\t\tif (ED_ISFREE(ed))\r\n\t\t\t\tcontinue;\t//free ents can't be visible (should not be linked, but oh well)\r\n\t\t\tif (e >= 1 && e <= sv.allocated_client_slots && svs.clients[e - 1].state != cs_spawned)\r\n\t\t\t\tcontinue;\t//player ents are weird, skip them for mods that do weird stuff.\r\n\r\n\t\t\tif (sv.world.worldmodel->funcs.EdictInFatPVS(sv.world.worldmodel, &ed->pvsinfo, pvs.buffer, areas))\r\n\t\t\t{\t//one of the viewer's clusters can see the viewee.\r\n\t\t\t\tresults[e - first] = true;\r\n\t\t\t\tret+=1;\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\treturn ret;\r\n}\r\nstatic qintptr_t QVM_NotYetImplemented (void *offset, quintptr_t mask, const qintptr_t *arg)\r\n{\r\n\tSV_Error(\"Q1QVM: Trap not implemented\\n\");\r\n\treturn 0;\r\n}\r\n\r\nstatic int QVM_FindExtField(char *fname)\r\n{\r\n\textentvars_t *xv = NULL;\r\n#define comfieldfloat(name,desc) if (!strcmp(fname, #name)) return ((int*)&xv->name - (int*)xv);\r\n#define comfieldint(name,desc) if (!strcmp(fname, #name)) return ((int*)&xv->name - (int*)xv);\r\n#define comfieldvector(name,desc) if (!strcmp(fname, #name)) return ((int*)&xv->name - (int*)xv);\r\n#define comfieldentity(name,desc) if (!strcmp(fname, #name)) return ((int*)&xv->name - (int*)xv);\r\n#define comfieldstring(name,desc) if (!strcmp(fname, #name)) return ((int*)&xv->name - (int*)xv);\r\n#define comfieldfunction(name, typestr,desc) if (!strcmp(fname, #name)) return ((int*)&xv->name - (int*)xv);\r\ncomextqcfields\r\nsvextqcfields\r\n#undef comfieldfloat\r\n#undef comfieldint\r\n#undef comfieldvector\r\n#undef comfieldentity\r\n#undef comfieldstring\r\n#undef comfieldfunction\r\n\treturn -1;\t//unsupported\r\n}\r\nstatic qintptr_t QVM_SetExtField (void *offset, quintptr_t mask, const qintptr_t *arg)\r\n{\r\n\tunsigned int entnum = ((char*)VM_POINTER(arg[0]) - (char*)evars)/sv.world.edict_size;\r\n\tint i = QVM_FindExtField(VM_POINTER(arg[1]));\r\n\tint value = VM_LONG(arg[2]);\r\n\r\n\tif (i >= 0 && entnum < q1qvmprogfuncs.edicttable_length && q1qvmprogfuncs.edicttable[entnum])\r\n\t{\r\n\t\t((int*)q1qvmprogfuncs.edicttable[entnum]->xv)[i] = value;\r\n\t\treturn value;\r\n\t}\r\n\treturn 0;\r\n}\r\nstatic qintptr_t QVM_GetExtField (void *offset, quintptr_t mask, const qintptr_t *arg)\r\n{\r\n\tunsigned int entnum = ((char*)VM_POINTER(arg[0]) - (char*)evars)/sv.world.edict_size;\r\n\tint i = QVM_FindExtField(VM_POINTER(arg[1]));\r\n\r\n\tif (i >= 0 && entnum < q1qvmprogfuncs.edicttable_length && q1qvmprogfuncs.edicttable[entnum])\r\n\t{\r\n\t\treturn ((int*)q1qvmprogfuncs.edicttable[entnum]->xv)[i];\r\n\t}\r\n\treturn 0;\r\n}\r\n\r\n#ifdef WEBCLIENT\r\nstatic void QVM_uri_query_callback(struct dl_download *dl)\r\n{\r\n\tvoid *cb_context = dl->user_ctx;\r\n\tint cb_entry = dl->user_float;\r\n\tint selfnum = dl->user_num;\r\n\r\n\tif (svs.gametype != GT_Q1QVM || svs.spawncount != dl->user_sequence)\r\n\t\treturn;\t//the world moved on.\r\n\t//fixme: pointers might not still be valid if the map changed.\r\n\r\n\t*sv.world.g.self = selfnum;\r\n\tif (dl->file)\r\n\t{\r\n\t\tsize_t len = VFS_GETLEN(dl->file);\r\n\t\tchar *buffer = malloc(len+1);\r\n\t\tbuffer[len] = 0;\r\n\t\tVFS_READ(dl->file, buffer, len);\r\n\t\tCmd_Args_Set(buffer, strlen(buffer));\r\n\t\tfree(buffer);\r\n\t}\r\n\telse\r\n\t\tCmd_Args_Set(NULL, 0);\r\n\tVM_Call(q1qvm, cb_entry, cb_context, dl->replycode, 0, 0, 0);\r\n}\r\n\r\n//bool uri_get(char *uri, int cb_entry, void *cb_ctx, char *mime, void *data, unsigned datasize)\r\nstatic qintptr_t QVM_uri_query (void *offset, quintptr_t mask, const qintptr_t *arg)\r\n{\r\n\tstruct dl_download *dl;\r\n\r\n\tconst unsigned char *url = VM_POINTER(arg[0]);\r\n\tint cb_entry = VM_LONG(arg[1]);\r\n\tvoid *cb_context = VM_POINTER(arg[2]);\r\n\tconst char *mimetype = VM_POINTER(arg[3]);\r\n\tconst char *data = VM_POINTER(arg[4]);\r\n\tsize_t datasize = VM_LONG(arg[5]);\r\n\textern cvar_t pr_enable_uriget;\r\n\r\n\tif (!pr_enable_uriget.ival)\r\n\t{\r\n\t\tCon_Printf(\"QVM_uri_query(\\\"%s\\\"): %s disabled\\n\", url, pr_enable_uriget.name);\r\n\t\treturn 0;\r\n\t}\r\n\r\n\tif (mimetype && *mimetype)\r\n\t{\r\n\t\tVALIDATEPOINTER(arg[4],datasize);\r\n\t\tCon_DPrintf(\"QVM_uri_query(%s)\\n\", url);\r\n\t\tdl = HTTP_CL_Put(url, mimetype, data, datasize, QVM_uri_query_callback);\r\n\t}\r\n\telse\r\n\t{\r\n\t\tCon_DPrintf(\"QVM_uri_query(%s)\\n\", url);\r\n\t\tdl = HTTP_CL_Get(url, NULL, QVM_uri_query_callback);\r\n\t}\r\n\tif (dl)\r\n\t{\r\n\t\tdl->user_ctx = cb_context;\r\n\t\tdl->user_float = cb_entry;\r\n\t\tdl->user_num = *sv.world.g.self;\r\n\t\tdl->user_sequence = svs.spawncount;\r\n\t\tdl->isquery = true;\r\n\t\treturn 1;\r\n\t}\r\n\telse\r\n\t\treturn 0;\r\n}\r\n#endif\r\n\r\nvoid QCBUILTIN PF_sv_trailparticles(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\r\nvoid QCBUILTIN PF_sv_pointparticles(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\r\nvoid QCBUILTIN PF_sv_particleeffectnum(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);\r\nstatic qintptr_t QVM_particleeffectnum (void *offset, quintptr_t mask, const qintptr_t *arg)\r\n{\r\n\tint i = WrapQCBuiltin(PF_sv_particleeffectnum, offset, mask, arg, \"s\");\r\n\treturn VM_FLOAT(i);\r\n}\r\nstatic qintptr_t QVM_trailparticles (void *offset, quintptr_t mask, const qintptr_t *arg)\r\n{\r\n\treturn WrapQCBuiltin(PF_sv_trailparticles, offset, mask, arg, \"invv\");\r\n}\r\nstatic qintptr_t QVM_pointparticles (void *offset, quintptr_t mask, const qintptr_t *arg)\r\n{\r\n\treturn WrapQCBuiltin(PF_sv_pointparticles, offset, mask, arg, \"ivvi\");\r\n}\r\n\r\nstatic qintptr_t QVM_clientstat (void *offset, quintptr_t mask, const qintptr_t *arg)\r\n{\r\n\tint num = VM_LONG(arg[0]);\r\n\tint type = VM_LONG(arg[1]);\r\n\tint fieldofs = VM_LONG(arg[2]);\r\n\r\n\r\n//\tSV_QCStatEval(type, \"\", &cache, NULL, num);\r\n\r\n\tSV_QCStatFieldIdx(type, fieldofs, num);\r\n\treturn 0;\r\n}\r\nstatic qintptr_t QVM_pointerstat (void *offset, quintptr_t mask, const qintptr_t *arg)\r\n{\r\n\tint num = VM_LONG(arg[0]);\r\n\tint type = VM_LONG(arg[1]);\r\n\tvoid *ptr = VM_POINTER(arg[2]);\r\n\tSV_QCStatPtr(type, ptr, num);\r\n\treturn 0;\r\n}\r\n\r\n//void(entity e, vector flags, entity target) setsendneeded\r\nstatic qintptr_t QVM_SetSendNeeded(void *offset, quintptr_t mask, const qintptr_t *arg)\r\n{\r\n\tunsigned int subject = VM_LONG(arg[0]);\r\n\tquint64_t fl = arg[1] << SENDFLAGS_SHIFT;\r\n\tunsigned int to = VM_LONG(arg[2]);\r\n\tif (!to)\r\n\t{\t//broadcast\r\n\t\tfor (to = 0; to < sv.allocated_client_slots; to++)\r\n\t\t\tif (svs.clients[to].pendingcsqcbits && subject < svs.clients[to].max_net_ents)\r\n\t\t\t\tsvs.clients[to].pendingcsqcbits[subject] |= fl;\r\n\t}\r\n\telse\r\n\t{\r\n\t\tto--;\r\n\t\tif (to >= sv.allocated_client_slots || !svs.clients[to].pendingcsqcbits || subject >= svs.clients[to].max_net_ents)\r\n\t\t\t;\t//some kind of error.\r\n\t\telse\r\n\t\t\tsvs.clients[to].pendingcsqcbits[subject] |= fl;\r\n\t}\r\n\treturn 0;\r\n}\r\n\r\nstatic qintptr_t QVM_VisibleTo_FTE (void *offset, quintptr_t mask, const qintptr_t *arg)\r\n{\r\n\tunsigned int a0 = VM_LONG(arg[0]);\r\n\tunsigned int a1 = VM_LONG(arg[1]);\r\n\tif (a0 < sv.world.num_edicts && a1 < sv.world.num_edicts)\r\n\t{\r\n\t\tpvscache_t *viewer = &q1qvmprogfuncs.edicttable[a0]->pvsinfo;\r\n\t\tpvscache_t *viewee = &q1qvmprogfuncs.edicttable[a1]->pvsinfo;\r\n\t\tif (viewer->num_leafs && viewee->num_leafs)\r\n\t\t{\r\n\t\t\tunsigned int i;\r\n\t\t\tint areas[] = {2,viewer->areanum, viewer->areanum2};\r\n\t\t\tfor (i = 0; i < viewer->num_leafs; i++)\r\n\t\t\t{\r\n\t\t\t\tqbyte *pvs = sv.world.worldmodel->funcs.ClusterPVS(sv.world.worldmodel, viewer->leafnums[i], NULL, PVM_FAST);\r\n\t\t\t\tif (sv.world.worldmodel->funcs.EdictInFatPVS(sv.world.worldmodel, viewee, pvs, areas))\r\n\t\t\t\t\treturn true;\t//viewer can see viewee\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\treturn 0;\r\n}\r\n\r\nstatic qintptr_t QVM_Map_Extension (void *offset, quintptr_t mask, const qintptr_t *arg);\r\n\r\ntypedef qintptr_t (*traps_t) (void *offset, quintptr_t mask, const qintptr_t *arg);\r\ntraps_t bitraps[G_MAX] =\r\n{\r\n\tQVM_GetAPIVersion,\r\n\tQVM_DPrint,\r\n\tQVM_Error,\r\n\tQVM_GetEntityToken,\r\n\tQVM_Spawn_Ent,\r\n\tQVM_Remove_Ent,\r\n\tQVM_Precache_Sound,\r\n\tQVM_Precache_Model,\r\n\tQVM_LightStyle,\r\n\tQVM_SetOrigin,\r\n\tQVM_SetSize,\t\t\t//10\r\n\tQVM_SetModel,\r\n\tQVM_BPrint,\r\n\tQVM_SPrint,\r\n\tQVM_CenterPrint,\r\n\tQVM_AmbientSound,\t\t//15\r\n\tQVM_Sound,\r\n\tQVM_TraceLine,\r\n\tQVM_CheckClient,\r\n\tQVM_StuffCmd,\r\n\tQVM_LocalCmd,\t\t\t//20\r\n\tQVM_CVar,\r\n\tQVM_CVar_Set,\r\n\tQVM_FindRadius,\r\n\tQVM_WalkMove,\r\n\tQVM_DropToFloor,\t\t//25\r\n\tQVM_CheckBottom,\r\n\tQVM_PointContents,\r\n\tQVM_NextEnt,\r\n\tQVM_Aim,\r\n\tQVM_MakeStatic,\t\t//30\r\n\tQVM_SetSpawnParams,\r\n\tQVM_ChangeLevel,\r\n\tQVM_LogFrag,\r\n\tQVM_GetInfoKey,\r\n\tQVM_Multicast,\t\t//35\r\n\tQVM_DisableUpdates,\r\n\tQVM_WriteByte,\r\n\tQVM_WriteChar,\r\n\tQVM_WriteShort,\r\n\tQVM_WriteLong,\t\t//40\r\n\tQVM_WriteAngle,\r\n\tQVM_WriteCoord,\r\n\tQVM_WriteString,\r\n\tQVM_WriteEntity,\r\n\tQVM_FlushSignon,\t\t//45\r\n\tQVM_memset,\r\n\tQVM_memcpy,\r\n\tQVM_strncpy,\r\n\tQVM_sin,\r\n\tQVM_cos,\t\t\t\t//50\r\n\tQVM_atan2,\r\n\tQVM_sqrt,\r\n\tQVM_floor,\r\n\tQVM_ceil,\r\n\tQVM_acos,\t\t\t\t//55\r\n\tQVM_Cmd_ArgC,\r\n\tQVM_Cmd_ArgV,\r\n\tQVM_TraceBox,\t\t\t//was G_TraceCapsule\r\n\tQVM_FS_OpenFile,\r\n\tQVM_FS_CloseFile,\t\t//60\r\n\tQVM_FS_ReadFile,\r\n\tQVM_FS_WriteFile,\r\n\tQVM_FS_SeekFile,\r\n\tQVM_FS_TellFile,\r\n\tQVM_FS_GetFileList,\t//65\r\n\tQVM_CVar_Set_Float,\r\n\tQVM_CVar_String,\r\n\tQVM_Map_Extension,\r\n\tQVM_strcmp,\r\n\tQVM_strncmp,\t\t\t//70\r\n\tQVM_stricmp,\r\n\tQVM_strnicmp,\r\n\tQVM_Find,\r\n\tQVM_ExecuteCmd,\r\n\tQVM_ConPrint,\t\t\t//75\r\n\tQVM_ReadCmd,\r\n\tQVM_RedirectCmd,\r\n\tQVM_Add_Bot,\r\n\tQVM_Remove_Bot,\r\n\tQVM_SetBotUserInfo,\t//80\r\n\tQVM_SetBotCMD,\r\n\r\n\tQVM_strftime,\r\n\tQVM_Cmd_ArgS,\r\n\tQVM_Cmd_Tokenize,\r\n\tQVM_strlcpy,\t\t//85\r\n\tQVM_strlcat,\r\n\tQVM_MakeVectors,\r\n\tQVM_NextClient,\r\n\r\n\tQVM_Precache_VWep_Model,\r\n\tQVM_SetPause,\r\n\tQVM_SetUserInfo,\r\n\tQVM_MoveToGoal,\r\n\tQVM_VisibleTo,\r\n};\r\n\r\nstruct\r\n{\r\n\tchar *extname;\r\n\ttraps_t trap;\r\n} qvmextensions[] =\r\n{\r\n\t{\"SetExtField\",\t\t\tQVM_SetExtField},\r\n\t{\"GetExtField\",\t\t\tQVM_GetExtField},\r\n\t{\"ChangeLevelHub\",\t\tQVM_ChangeLevelHub},\t//with start spot\r\n#ifdef WEBCLIENT\r\n\t{\"URI_Query\",\t\t\tQVM_uri_query},\r\n#endif\r\n\t{\"particleeffectnum\",\tQVM_particleeffectnum},\r\n\t{\"trailparticles\",\t\tQVM_trailparticles},\r\n\t{\"pointparticles\",\t\tQVM_pointparticles},\r\n\t{\"clientstat\",\t\t\tQVM_clientstat},\t//csqc extension\r\n\t{\"pointerstat\",\t\t\tQVM_pointerstat},\t//csqc extension\r\n\t{\"setsendneeded\",\t\tQVM_SetSendNeeded},\t\t//csqc extension\r\n\t{\"VisibleTo\",\t\t\tQVM_VisibleTo_FTE},\t\t//alternative to mvdsv's visclients hack. redundant now. FIXME: Remove.\r\n\r\n\t//sql?\r\n\t//model querying?\r\n\t//skeletal objects/tags?\r\n\t//heightmap / brush editing?\r\n\t//csqc ents\r\n\t{NULL, NULL}\r\n};\r\n\r\ntraps_t traps[512];\r\n\r\nstatic qintptr_t QVM_Map_Extension (void *offset, quintptr_t mask, const qintptr_t *arg)\r\n{\r\n\tchar *extname = VM_POINTER(arg[0]);\r\n\tunsigned int slot = VM_LONG(arg[1]);\r\n\tint i;\r\n\r\n\tif (slot >= countof(traps))\r\n\t\treturn -2;\t//invalid slot.\r\n\r\n\tif (!extname)\r\n\t{\t//special handling for vauge compat with mvdsv, for testing how many 'known' builtins are implemented.\r\n\t\tif (slot < G_MAX)\r\n\t\t\treturn -2;\r\n\t\treturn -1;\r\n\t}\r\n\r\n\t//find the extension and map it to the slot if found.\r\n\tfor (i = 0; qvmextensions[i].extname; i++)\r\n\t{\r\n\t\tif (!Q_strcasecmp(extname, qvmextensions[i].extname))\r\n\t\t{\r\n\t\t\ttraps[slot] = qvmextensions[i].trap;\r\n\t\t\treturn slot;\r\n\t\t}\r\n\t}\r\n\treturn -1;\t//extension not known\r\n}\r\n\r\n//============== general Quake services ==================\r\nstatic int syscallqvm (void *offset, quintptr_t mask, int fn, const int *arg)\r\n{\r\n\tif (sizeof(int) == sizeof(qintptr_t))\r\n\t{\t//should allow the slow copy below to be optimised out\r\n\t\tif (fn >= countof(traps))\r\n\t\t\treturn QVM_NotYetImplemented(offset, mask, (qintptr_t*)arg);\r\n\t\treturn traps[fn](offset, mask, (qintptr_t*)arg);\r\n\t}\r\n\telse\r\n\t{\r\n\t\tqintptr_t args[13];\r\n\t\tint i;\r\n\t\tfor (i = 0; i < countof(args); i++)\r\n\t\t\targs[i] = arg[i];\r\n\t\tif (fn >= countof(traps))\r\n\t\t\treturn QVM_NotYetImplemented(offset, mask, args);\r\n\t\treturn traps[fn](offset, mask, args);\r\n\t}\r\n}\r\n\r\nstatic qintptr_t EXPORT_FN syscallnative (qintptr_t arg, ...)\r\n{\r\n\tqintptr_t args[13];\r\n\tva_list argptr;\r\n\r\n\tva_start(argptr, arg);\r\n\targs[0]=va_arg(argptr, qintptr_t);\r\n\targs[1]=va_arg(argptr, qintptr_t);\r\n\targs[2]=va_arg(argptr, qintptr_t);\r\n\targs[3]=va_arg(argptr, qintptr_t);\r\n\targs[4]=va_arg(argptr, qintptr_t);\r\n\targs[5]=va_arg(argptr, qintptr_t);\r\n\targs[6]=va_arg(argptr, qintptr_t);\r\n\targs[7]=va_arg(argptr, qintptr_t);\r\n\targs[8]=va_arg(argptr, qintptr_t);\r\n\targs[9]=va_arg(argptr, qintptr_t);\r\n\targs[10]=va_arg(argptr, qintptr_t);\r\n\targs[11]=va_arg(argptr, qintptr_t);\r\n\targs[12]=va_arg(argptr, qintptr_t);\r\n\tva_end(argptr);\r\n\r\n\tif (arg >= countof(traps))\r\n\t\treturn QVM_NotYetImplemented(NULL, ~(quintptr_t)0, args);\r\n\treturn traps[arg](NULL, ~(quintptr_t)0, args);\r\n}\r\n\r\nvoid Q1QVM_Shutdown(qboolean notifygame)\r\n{\r\n\tint i;\r\n\tif (q1qvm)\r\n\t{\r\n\t\tfor (i = 0; i < sv.allocated_client_slots; i++)\r\n\t\t{\r\n\t\t\tif (svs.clients[i].name)\r\n\t\t\t\tQ_strncpyz(svs.clients[i].namebuf, svs.clients[i].name, sizeof(svs.clients[i].namebuf));\r\n\t\t\tsvs.clients[i].name = svs.clients[i].namebuf;\r\n\t\t}\r\n\t\tif (notifygame && gvars)\r\n\t\t\tVM_Call(q1qvm, GAME_SHUTDOWN, 0, 0, 0);\r\n\t\tVM_Destroy(q1qvm);\r\n\t\tq1qvm = NULL;\r\n\t\tQVM_FS_CloseFileAll();\r\n\t\tif (svprogfuncs == &q1qvmprogfuncs)\r\n\t\t\tsv.world.progs = svprogfuncs = NULL;\r\n\t\tZ_FreeTags(VMFSID_Q1QVM);\r\n\t\tif (q1qvmprogfuncs.edicttable)\r\n\t\t{\r\n\t\t\tZ_Free(q1qvmprogfuncs.edicttable);\r\n\t\t\tq1qvmprogfuncs.edicttable = NULL;\r\n\t\t}\r\n\t\tvevars = 0;\r\n\t}\r\n}\r\n\r\nstatic void QDECL Q1QVM_Get_FrameState(world_t *w, wedict_t *ent, framestate_t *fstate)\r\n{\r\n\tmemset(fstate, 0, sizeof(*fstate));\r\n\tfstate->g[FS_REG].frame[0] = ent->v->frame;\r\n\tfstate->g[FS_REG].frametime[0] = ent->xv->frame1time;\r\n\tfstate->g[FS_REG].lerpweight[0] = 1;\r\n\tfstate->g[FS_REG].endbone = 0x7fffffff;\r\n\r\n\tfstate->g[FST_BASE].frame[0] = ent->xv->baseframe;\r\n\tfstate->g[FST_BASE].frametime[0] = ent->xv->/*base*/frame1time;\r\n\tfstate->g[FST_BASE].lerpweight[0] = 1;\r\n\tfstate->g[FST_BASE].endbone = ent->xv->basebone;\r\n\r\n#if defined(SKELETALOBJECTS) || defined(RAGDOLL)\r\n\tif (ent->xv->skeletonindex)\r\n\t\tskel_lookup(w, ent->xv->skeletonindex, fstate);\r\n#endif\r\n}\r\n\r\nstatic void QDECL Q1QVM_Event_Touch(world_t *w, wedict_t *s, wedict_t *o, trace_t *trace)\r\n{\r\n\tint oself = pr_global_struct->self;\r\n\tint oother = pr_global_struct->other;\r\n\r\n\tpr_global_struct->self = EDICT_TO_PROG(w->progs, s);\r\n\tpr_global_struct->other = EDICT_TO_PROG(w->progs, o);\r\n\tpr_global_struct->time = w->physicstime;\r\n\tVM_Call(q1qvm, GAME_EDICT_TOUCH, 0, 0, 0);\r\n\r\n\tpr_global_struct->self = oself;\r\n\tpr_global_struct->other = oother;\r\n}\r\n\r\nstatic void QDECL Q1QVM_Event_Think(world_t *w, wedict_t *s)\r\n{\r\n\tpr_global_struct->self = EDICT_TO_PROG(w->progs, s);\r\n\tpr_global_struct->other = EDICT_TO_PROG(w->progs, w->edicts);\r\n\tVM_Call(q1qvm, GAME_EDICT_THINK, 0, 0, 0);\r\n}\r\n\r\nstatic qboolean QDECL Q1QVM_Event_ContentsTransition(world_t *w, wedict_t *ent, int oldwatertype, int newwatertype)\r\n{\r\n\treturn false;\t//always do legacy behaviour\r\n}\r\n\r\nqboolean PR_LoadQ1QVM(void)\r\n{\r\n\tstatic pint_t writable_int;\r\n\tstatic pvec_t writable;\r\n\tstatic pvec_t dimensionsend = 255;\r\n\tstatic pvec_t dimensiondefault = 255;\r\n\tstatic pvec_t physics_mode = 2;\r\n\tstatic pvec3_t defaultgravity = {0,0,-1};\r\n\tint i;\r\n\tgameDataPrivate_t gd;\r\n\tgameDataN_t *gdn;\r\n\tgameData32_t *gd32;\r\n\tqintptr_t ret;\r\n\tqintptr_t limit;\r\n\textern cvar_t\tpr_maxedicts;\r\n\r\n\tconst char *fname = pr_ssqc_progs.string;\r\n\tif (!*fname)\r\n\t\tfname = \"qwprogs\";\r\n\r\n\tQ1QVM_Shutdown(true);\r\n\r\n\tq1qvm = VM_Create(fname, com_gamedirnativecode.ival?syscallnative:NULL, fname, syscallqvm);\r\n\tif (!q1qvm)\r\n\t\tq1qvm = VM_Create(fname, syscallnative, fname, NULL);\r\n\tif (!q1qvm)\r\n\t{\r\n\t\tif (!com_gamedirnativecode.ival && COM_FCheckExists(va(\"%s\"ARCH_DL_POSTFIX, fname)))\r\n\t\t\tCon_Printf(CON_WARNING\"%s\"ARCH_DL_POSTFIX\" exists, but is blocked from loading due to known bugs in other engines. If this is from a safe source then either ^aset com_gamedirnativecode 1^a or rename to eg %s%s_%s\"ARCH_DL_POSTFIX\"\\n\", fname, ((host_parms.binarydir && *host_parms.binarydir)?host_parms.binarydir:host_parms.basedir), fname, FS_GetGamedir(false));\r\n\t\tif (svprogfuncs == &q1qvmprogfuncs)\r\n\t\t\tsv.world.progs = svprogfuncs = NULL;\r\n\t\treturn false;\r\n\t}\r\n\r\n\tfor(i = 0; i < G_MAX; i++)\r\n\t\ttraps[i] = bitraps[i];\r\n\tfor(; i < countof(traps); i++)\r\n\t\ttraps[i] = QVM_NotYetImplemented;\r\n\r\n\r\n\tmemset(&fofs, 0, sizeof(fofs));\r\n\r\n\tprogstype = PROG_QW;\r\n\r\n\r\n\tsvprogfuncs = &q1qvmprogfuncs;\r\n\r\n\r\n//\tq1qvmprogfuncs.AddString = Q1QVMPF_AddString;\t//using this breaks 64bit support, and is a 'bad plan' elsewhere too,\r\n\tq1qvmprogfuncs.EdictNum = Q1QVMPF_EdictNum;\r\n\tq1qvmprogfuncs.NumForEdict = Q1QVMPF_NumForEdict;\r\n\tq1qvmprogfuncs.EdictToProgs = Q1QVMPF_EdictToProgs;\r\n\tq1qvmprogfuncs.ProgsToEdict = Q1QVMPF_ProgsToEdict;\r\n\tq1qvmprogfuncs.EntAlloc = Q1QVMPF_EntAlloc;\r\n\tq1qvmprogfuncs.EntFree = Q1QVMPF_EntRemove;\r\n\tq1qvmprogfuncs.FindGlobal = Q1QVMPF_FindGlobal;\r\n\tq1qvmprogfuncs.load_ents = Q1QVMPF_LoadEnts;\r\n\tq1qvmprogfuncs.globals = Q1QVMPF_Globals;\r\n\tq1qvmprogfuncs.GetEdictFieldValue = Q1QVMPF_GetEdictFieldValue;\r\n\tq1qvmprogfuncs.QueryField = Q1QVMPF_QueryField;\r\n\tq1qvmprogfuncs.StringToProgs = Q1QVMPF_StringToProgs;\r\n\tq1qvmprogfuncs.StringToNative = Q1QVMPF_StringToNative;\r\n\tq1qvmprogfuncs.SetStringField = Q1QVMPF_SetStringField;\r\n\tq1qvmprogfuncs.EntClear = Q1QVMPF_ClearEdict;\r\n\r\n\tsv.world.Event_Touch = Q1QVM_Event_Touch;\r\n\tsv.world.Event_Think = Q1QVM_Event_Think;\r\n\tsv.world.Event_Sound = SVQ1_StartSound;\r\n\tsv.world.Event_ContentsTransition = Q1QVM_Event_ContentsTransition;\r\n\tsv.world.Get_CModel = SVPR_GetCModel;\r\n\tsv.world.Get_FrameState = Q1QVM_Get_FrameState;\r\n\r\n\tsv.world.num_edicts = 0;\t//we're not ready for most of the builtins yet\r\n\tsv.world.max_edicts = 0;\t//so clear these out, just in case\r\n\tsv.world.edict_size = 0;\t//if we get a division by zero, then at least its a safe crash\r\n\r\n\tq1qvmprogfuncs.edicttable = NULL;\r\n\r\n\tq1qvmprogfuncs.stringtable = VM_MemoryBase(q1qvm);\r\n\r\n\tqvm_api_version = GAME_API_VERSION;\r\n\r\n\tret = VM_Call(q1qvm, GAME_INIT, (qintptr_t)(sv.time*1000), rand(), 0, 0, 0);\r\n\tif (!ret)\r\n\t{\r\n\t\tQ1QVM_Shutdown(false);\r\n\t\treturn false;\r\n\t}\r\n\r\n\tif (VM_NonNative(q1qvm))\r\n\t{\t//when non native, this can only be a 32bit qvm in a 64bit server.\r\n\t\tgd32 = (gameData32_t*)((char*)VM_MemoryBase(q1qvm) + ret);\t//qvm is 32bit\r\n\r\n\t\tgd.APIversion = gd32->APIversion;\r\n\t\tgd.sizeofent = gd32->sizeofent;\r\n\r\n\t\tgd.ents = gd32->ents;\r\n\t\tgd.global = gd32->global;\r\n\t\tgd.fields = gd32->fields;\r\n\r\n\t\tif (qvm_api_version >= 14)\r\n\t\t\tgd.maxedicts = gd32->maxentities;\r\n\t\telse\r\n\t\t\tgd.maxedicts = MAX_Q1QVM_EDICTS;\r\n\t}\r\n\telse\r\n\t{\r\n\t\tgdn = (gameDataN_t*)((char*)VM_MemoryBase(q1qvm) + ret);\r\n\t\tgd.APIversion = gdn->APIversion;\r\n\t\tgd.sizeofent = gdn->sizeofent;\r\n\r\n\t\tgd.ents = gdn->ents;\r\n\t\tgd.global = gdn->global;\r\n\t\tgd.fields = gdn->fields;\r\n\r\n\t\tif (qvm_api_version >= 14)\r\n\t\t\tgd.maxedicts = gdn->maxentities;\r\n\t\telse\r\n\t\t\tgd.maxedicts = MAX_Q1QVM_EDICTS;\r\n\t}\r\n\tgd.maxedicts = bound(1, pr_maxedicts.ival, gd.maxedicts);\r\n\tgd.maxedicts = bound(1, gd.maxedicts, MAX_EDICTS);\r\n\r\n\tqvm_api_version = gd.APIversion;\r\n\tif (!(GAME_API_VERSION_MIN <= qvm_api_version && qvm_api_version <= GAME_API_VERSION))\r\n\t{\r\n\t\tCon_Printf(\"QVM-API version %i not supported\\n\", qvm_api_version);\r\n\t\tQ1QVM_Shutdown(false);\r\n\t\treturn false;\r\n\t}\r\n\r\n\tif (qvm_api_version >= 16)\r\n\t{\t//version 16 finally removed the last remnant of the server's state from the qvm.\r\n\t\twasted_edict_t_size = 0;\r\n\t}\r\n\telse if (qvm_api_version >= 13)\r\n\t{\r\n\t\t//in version 13, the actual edict_t struct is gone, and there's a pointer to it in its place (which is unusable, and changes depending on modes).\r\n\t\twasted_edict_t_size = (VM_NonNative(q1qvm)?sizeof(int):sizeof(void*));\r\n\t}\r\n\telse\r\n\t{\r\n\t\t//fte/qclib has split edict_t and entvars_t.\r\n\t\t//older versions of the qvm api has them in one lump\r\n\t\t//so we need to bias the mod's entvars_t offsets a little.\r\n\t\twasted_edict_t_size = 114;\r\n\t}\r\n\r\n\tsv.world.num_edicts = 1;\r\n\tsv.world.max_edicts = bound(64, gd.maxedicts, MAX_EDICTS);\r\n\tq1qvmprogfuncs.edicttable = Z_Malloc(sizeof(*q1qvmprogfuncs.edicttable) * sv.world.max_edicts);\r\n\tq1qvmprogfuncs.edicttable_length = sv.world.max_edicts;\r\n\r\n\tlimit = VM_MemoryMask(q1qvm);\r\n\tif (gd.sizeofent > 0xffffffff / gd.maxedicts)\r\n\t\tgd.sizeofent = 0xffffffff / gd.maxedicts;\r\n\tif ((quintptr_t)gd.ents+(gd.sizeofent*gd.maxedicts) < (quintptr_t)gd.ents || (quintptr_t)gd.ents > (quintptr_t)limit)\r\n\t\tgd.ents = 0;\r\n\tif ((quintptr_t)(gd.global+1) < (quintptr_t)gd.global || (quintptr_t)gd.global > (quintptr_t)limit)\r\n\t\tgd.global = 0;\r\n\tif (/*(quintptr_t)gd.fields < (quintptr_t)gd.fields ||*/ (quintptr_t)gd.fields > limit)\r\n\t\tgd.fields = 0;\r\n\r\n\tsv.world.edict_size = gd.sizeofent;\r\n\tvevars = gd.ents;\r\n\tevars = Q1QVMPF_PointerToNative(&q1qvmprogfuncs, vevars);\r\n\tgvars = Q1QVMPF_PointerToNative(&q1qvmprogfuncs, gd.global);\r\n\r\n\tif (!evars || !gvars)\r\n\t{\r\n\t\tQ1QVM_Shutdown(false);\r\n\t\treturn false;\r\n\t}\r\n\r\n//WARNING: global is not remapped yet...\r\n//This code is written evilly, but works well enough\r\n#define globalint(required, name) pr_global_ptrs->name = Q1QVMPF_PointerToNative(&q1qvmprogfuncs, (qintptr_t)&gvars->name - (qintptr_t)q1qvmprogfuncs.stringtable)\t//the logic of this is somewhat crazy\r\n#define globalfloat(required, name) pr_global_ptrs->name = Q1QVMPF_PointerToNative(&q1qvmprogfuncs, (qintptr_t)&gvars->name - (qintptr_t)q1qvmprogfuncs.stringtable)\r\n#define globalstring(required, name) pr_global_ptrs->name = Q1QVMPF_PointerToNative(&q1qvmprogfuncs, (qintptr_t)&gvars->name - (qintptr_t)q1qvmprogfuncs.stringtable)\r\n#define globalvec(required, name) pr_global_ptrs->name = Q1QVMPF_PointerToNative(&q1qvmprogfuncs, (qintptr_t)&gvars->name - (qintptr_t)q1qvmprogfuncs.stringtable)\r\n#define globalfunc(required, name) pr_global_ptrs->name = Q1QVMPF_PointerToNative(&q1qvmprogfuncs, (qintptr_t)&gvars->name - (qintptr_t)q1qvmprogfuncs.stringtable)\r\n#define globalnull(required, name) pr_global_ptrs->name = NULL\r\n\tglobalint\t\t(true, self);\t//we need the qw ones, but any in standard quake and not quakeworld, we don't really care about.\r\n\tglobalint\t\t(true, other);\r\n\tglobalint\t\t(true, world);\r\n\tglobalfloat\t\t(true, time);\r\n\tglobalfloat\t\t(true, frametime);\r\n\tglobalint\t\t(false, newmis);\t//not always in nq.\r\n\tglobalfloat\t\t(false, force_retouch);\r\n\tglobalstring\t(true, mapname);\r\n\tglobalnull\t\t(false, deathmatch);\r\n\tglobalnull\t\t(false, coop);\r\n\tglobalnull\t\t(false, teamplay);\r\n\tglobalfloat\t\t(true, serverflags);\r\n\tglobalfloat\t\t(true, total_secrets);\r\n\tglobalfloat\t\t(true, total_monsters);\r\n\tglobalfloat\t\t(true, found_secrets);\r\n\tglobalfloat\t\t(true, killed_monsters);\r\n\tglobalvec\t\t(true, v_forward);\r\n\tglobalvec\t\t(true, v_up);\r\n\tglobalvec\t\t(true, v_right);\r\n\tglobalfloat\t\t(true, trace_allsolid);\r\n\tglobalfloat\t\t(true, trace_startsolid);\r\n\tglobalfloat\t\t(true, trace_fraction);\r\n\tglobalvec\t\t(true, trace_endpos);\r\n\tglobalvec\t\t(true, trace_plane_normal);\r\n\tglobalfloat\t\t(true, trace_plane_dist);\r\n\tglobalint\t\t(true, trace_ent);\r\n\tglobalfloat\t\t(true, trace_inopen);\r\n\tglobalfloat\t\t(true, trace_inwater);\r\n\tglobalnull\t\t(false, trace_endcontentsf);\r\n\tglobalnull\t\t(false, trace_endcontentsi);\r\n\tglobalnull\t\t(false, trace_surfaceflagsf);\r\n\tglobalnull\t\t(false, trace_surfaceflagsi);\r\n\tglobalnull\t\t(false, cycle_wrapped);\r\n\tglobalint\t\t(false, msg_entity);\r\n\tglobalfunc\t\t(false, main);\r\n\tglobalfunc\t\t(true, StartFrame);\r\n\tglobalfunc\t\t(true, PlayerPreThink);\r\n\tglobalfunc\t\t(true, PlayerPostThink);\r\n\tglobalfunc\t\t(true, ClientKill);\r\n\tglobalfunc\t\t(true, ClientConnect);\r\n\tglobalfunc\t\t(true, PutClientInServer);\r\n\tglobalfunc\t\t(true, ClientDisconnect);\r\n\tglobalfunc\t\t(false, SetNewParms);\r\n\tglobalfunc\t\t(false, SetChangeParms);\r\n\r\n\tpr_global_ptrs->trace_surfaceflagsf = &writable;\r\n\tpr_global_ptrs->trace_surfaceflagsi = &writable_int;\r\n\tpr_global_ptrs->trace_endcontentsf = &writable;\r\n\tpr_global_ptrs->trace_endcontentsi = &writable_int;\r\n\tpr_global_ptrs->dimension_default = &dimensiondefault;\r\n\tpr_global_ptrs->dimension_send = &dimensionsend;\r\n\tpr_global_ptrs->physics_mode = &physics_mode;\r\n\tpr_global_ptrs->trace_brush_id = &writable_int;\r\n\tpr_global_ptrs->trace_brush_faceid = &writable_int;\r\n\tpr_global_ptrs->trace_surface_id = &writable_int;\r\n\tpr_global_ptrs->trace_bone_id = &writable_int;\r\n\tpr_global_ptrs->trace_triangle_id = &writable_int;\r\n\tpr_global_ptrs->global_gravitydir = &defaultgravity;\r\n\r\n//\tensureglobal(input_timelength, input_timelength_default);\r\n//\tensureglobal(input_impulse, input_impulse_default);\r\n//\tensureglobal(input_angles, input_angles_default);\r\n//\tensureglobal(input_movevalues, input_movevalues_default);\r\n//\tensureglobal(input_buttons, input_buttons_default);\r\n\r\n\tdimensionsend = dimensiondefault = 255;\r\n\tfor (i = 0; i < 16; i++)\r\n\t\tpr_global_ptrs->spawnparamglobals[i] = (float*)(&gvars->parm1 + i);\r\n\tfor (; i < NUM_SPAWN_PARMS; i++)\r\n\t\tpr_global_ptrs->spawnparamglobals[i] = NULL;\r\n\tpr_global_ptrs->parm_string = NULL;\r\n\r\n#define emufield(n,t) if (field[i].type == t && !strcmp(#n, fname)) {fofs.n = (field[i].ofs - wasted_edict_t_size)/sizeof(float); continue;}\r\n\tif (VM_NonNative(q1qvm))\r\n\t{\r\n\t\tfield32_t *field = Q1QVMPF_PointerToNative(&q1qvmprogfuncs, gd.fields);\r\n\t\tif (field)\r\n\t\tfor (i = 0; field[i].name; i++)\r\n\t\t{\r\n\t\t\tconst char *fname = Q1QVMPF_PointerToNative(&q1qvmprogfuncs, field[i].name);\r\n\t\t\temufields\r\n\t\t\tCon_DPrintf(\"Extension field %s is not supported\\n\", fname);\r\n\t\t}\r\n\t}\r\n\telse\r\n\t{\r\n\t\tfieldN_t *field = Q1QVMPF_PointerToNative(&q1qvmprogfuncs, gd.fields);\r\n\t\tif (field)\r\n\t\tfor (i = 0; field[i].name; i++)\r\n\t\t{\r\n\t\t\tconst char *fname = Q1QVMPF_PointerToNative(&q1qvmprogfuncs, field[i].name);\r\n\t\t\temufields\r\n\t\t\tCon_DPrintf(\"Extension field %s is not supported\\n\", fname);\r\n\t\t}\r\n\t}\r\n#undef emufield\r\n\r\n\r\n\tsv.world.progs = &q1qvmprogfuncs;\r\n\tsv.world.edicts = (wedict_t*)Q1QVMPF_EdictNum(svprogfuncs, 0);\r\n\tsv.world.usesolidcorpse = true;\r\n\r\n\tif (qvm_api_version >= 15)\r\n\t{\r\n\t\tint e;\r\n\t\tfor (e = 0; e <= 32; e++)\r\n\t\t{\r\n\t\t\tpr_global_struct->self = Q1QVMPF_EdictToProgs(svprogfuncs, Q1QVMPF_EdictNum(svprogfuncs, e));\r\n\t\t\tVM_Call(q1qvm, GAME_CLEAR_EDICT, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);\r\n\t\t}\r\n\t\tpr_global_struct->self = 0;\r\n\r\n\t\tif (qvm_api_version == 15)\r\n\t\t\tQ1QVMPF_SetStringGlobal(sv.world.progs, &gvars->mapname, svs.name, MAPNAME_LEN);\r\n\t}\r\n\telse\r\n\t{\r\n\t\tif ((quintptr_t)gvars->mapname && (quintptr_t)gvars->mapname+MAPNAME_LEN < VM_MemoryMask(q1qvm))\r\n\t\t\tQ_strncpyz((char*)VM_MemoryBase(q1qvm) + gvars->mapname, svs.name, MAPNAME_LEN);\r\n\t\telse\r\n\t\t\tgvars->mapname = Q1QVMPF_StringToProgs(sv.world.progs, svs.name);\r\n\t}\r\n\r\n\tPR_SV_FillWorldGlobals(&sv.world);\r\n\treturn true;\r\n}\r\n\r\n\r\n\r\n\r\nvoid Q1QVM_ClientConnect(client_t *cl)\r\n{\r\n\tif (qvm_api_version >= 16)\r\n\t{\r\n\t\tQ_strncpyz(cl->namebuf, cl->name, sizeof(cl->namebuf));\r\n\t\tcl->name = cl->namebuf;\r\n\t}\r\n\telse if (qvm_api_version >= 15 && !VM_NonNative(q1qvm))\r\n\t{\r\n\t\tQ_strncpyz(cl->namebuf, cl->name, sizeof(cl->namebuf));\r\n\t\tQ1QVMPF_SetStringField(sv.world.progs, cl->edict, &cl->edict->v->netname, cl->namebuf, true);\r\n\t}\r\n\telse if (cl->edict->v->netname)\r\n\t{\r\n\t\tchar *name;\r\n\t\tchar *base = VM_MemoryBase(q1qvm);\r\n\t\tquintptr_t mask = VM_MemoryMask(q1qvm);\r\n\t\tname = (char*)Q1QVMPF_StringToNative(svprogfuncs, cl->edict->v->netname);\r\n\t\tif (cl->name > base && cl->name < base+mask)\r\n\t\t{\r\n\t\t\tQ_strncpyz(cl->namebuf, name, sizeof(cl->namebuf));\r\n\t\t\tstrcpy(name, cl->namebuf);\r\n\t\t\tcl->name = name;\t//so the gamecode can do changes if it wants.\r\n\t\t}\r\n\t\telse\r\n\t\t\tCon_Printf(\"WARNING: Mod provided no netname buffer. Player names will not be set properly.\\n\");\r\n\t}\r\n\telse if (!VM_NonNative(q1qvm))\r\n\t{\r\n\t\tQ_strncpyz(cl->namebuf, cl->name, sizeof(cl->namebuf));\r\n\t\tcl->name = cl->namebuf;\r\n\t\tcl->edict->v->netname = Q1QVMPF_StringToProgs(svprogfuncs, cl->namebuf);\r\n\r\n//\t\tCon_DPrintf(\"WARNING: Mod provided no netname buffer and will not function correctly when compiled as a qvm.\\n\");\r\n\t}\r\n\telse\r\n\t\tCon_Printf(\"WARNING: Mod provided no netname buffer. Player names will not be set properly.\\n\");\r\n\r\n\tif (fofs.gravity)\r\n\t\t((float*)cl->edict->v)[fofs.gravity] = cl->edict->xv->gravity;\r\n\tif (fofs.maxspeed)\r\n\t\t((float*)cl->edict->v)[fofs.maxspeed] = cl->edict->xv->maxspeed;\r\n\tif (fofs.isBot)\r\n\t\t((float*)cl->edict->v)[fofs.isBot] = !cl->protocol;\r\n\r\n\t// call the spawn function\r\n\tpr_global_struct->time = sv.world.physicstime;\r\n\tpr_global_struct->self = EDICT_TO_PROG(svprogfuncs, cl->edict);\r\n\tVM_Call(q1qvm, GAME_CLIENT_CONNECT, cl->spectator, 0, 0, 0);\r\n\r\n\t// actually spawn the player\r\n\tpr_global_struct->time = sv.world.physicstime;\r\n\tpr_global_struct->self = EDICT_TO_PROG(svprogfuncs, cl->edict);\r\n\tVM_Call(q1qvm, GAME_PUT_CLIENT_IN_SERVER, cl->spectator, 0, 0, 0);\r\n}\r\n\r\nqboolean Q1QVM_GameConsoleCommand(void)\r\n{\r\n\tint oldself, oldother;\r\n\tif (!q1qvm)\r\n\t\treturn false;\r\n\r\n\t//FIXME: if an rcon command from someone on the server, mvdsv sets self to match the ip of that player\r\n\t//this is not required (broken by proxies anyway) but is a nice handy feature\r\n\r\n\tpr_global_struct->time = sv.world.physicstime;\r\n\toldself = pr_global_struct->self;\t//these are usually useless\r\n\toldother = pr_global_struct->other;\t//but its possible that someone makes a mod that depends on the 'mod' command working via redirectcmd+co\r\n\t\t\t\t\t\t//this at least matches mvdsv\r\n\tpr_global_struct->self = 0;\r\n\tpr_global_struct->other = 0;\r\n\r\n\tVM_Call(q1qvm, GAME_CONSOLE_COMMAND, 0, 0, 0);\t//mod uses Cmd_Argv+co to get args\r\n\r\n\tpr_global_struct->self = oldself;\r\n\tpr_global_struct->other = oldother;\r\n\treturn true;\r\n}\r\n\r\nqboolean Q1QVM_ClientSay(edict_t *player, qboolean team)\r\n{\r\n\tqboolean washandled;\r\n\tif (!q1qvm)\r\n\t\treturn false;\r\n\r\n\tpr_global_struct->time = sv.world.physicstime;\r\n\tpr_global_struct->self = Q1QVMPF_EdictToProgs(svprogfuncs, player);\r\n\twashandled = VM_Call(q1qvm, GAME_CLIENT_SAY, team, 0, 0, 0);\r\n\r\n\treturn washandled;\r\n}\r\n\r\nqboolean Q1QVM_UserInfoChanged(edict_t *player, qboolean after)\r\n{\t//mod will use G_CMD_ARGV to get argv1+argv2 to read the info that is changing.\r\n\tif (!q1qvm)\r\n\t\treturn false;\r\n\r\n\tpr_global_struct->time = sv.world.physicstime;\r\n\tpr_global_struct->self = Q1QVMPF_EdictToProgs(svprogfuncs, player);\r\n\treturn VM_Call(q1qvm, GAME_CLIENT_USERINFO_CHANGED, after, 0, 0);\r\n}\r\n\r\nvoid Q1QVM_PlayerPreThink(void)\r\n{\r\n\tif (fofs.movement)\r\n\t{\r\n\t\tsv_player->xv->movement[0] = ((float*)sv_player->v)[fofs.movement+0];\r\n\t\tsv_player->xv->movement[1] = ((float*)sv_player->v)[fofs.movement+1];\r\n\t\tsv_player->xv->movement[2] = ((float*)sv_player->v)[fofs.movement+2];\r\n\t}\r\n\tif (fofs.gravity)\r\n\t\tsv_player->xv->gravity = ((float*)sv_player->v)[fofs.gravity];\r\n\tif (fofs.maxspeed)\r\n\t\tsv_player->xv->maxspeed = ((float*)sv_player->v)[fofs.maxspeed];\r\n\r\n\tVM_Call(q1qvm, GAME_CLIENT_PRETHINK, host_client->spectator, 0, 0, 0);\r\n}\r\n\r\nvoid Q1QVM_RunPlayerThink(void)\r\n{\r\n\tVM_Call(q1qvm, GAME_EDICT_THINK, 0, 0, 0);\r\n\tVM_Call(q1qvm, GAME_CLIENT_THINK, host_client->spectator, 0, 0, 0);\r\n}\r\n\r\nvoid Q1QVM_PostThink(void)\r\n{\r\n\tVM_Call(q1qvm, GAME_CLIENT_POSTTHINK, host_client->spectator, 0, 0, 0);\r\n\r\n\tif (fofs.vw_index)\r\n\t\tsv_player->xv->vw_index = ((float*)sv_player->v)[fofs.vw_index];\r\n\tif (fofs.items2)\r\n\t\tsv_player->xv->items2 = ((float*)sv_player->v)[fofs.items2];\r\n\tif (fofs.trackent)\r\n\t\thost_client->viewent = ((int*)sv_player->v)[fofs.trackent];\r\n\tif (fofs.hideplayers)\r\n\t\thost_client->hideplayers = ((int*)sv_player->v)[fofs.hideplayers];\r\n\tif (fofs.hideentity)\r\n\t\thost_client->hideentity = ((int*)sv_player->v)[fofs.hideentity];\r\n}\r\n\r\nvoid Q1QVM_StartFrame(qboolean botsarespecialsnowflakes)\r\n{\r\n\tif (botsarespecialsnowflakes && qvm_api_version < 15)\r\n\t\treturn; //this stupidity brought to you with api 15!\r\n\tVM_Call(q1qvm, GAME_START_FRAME, (qintptr_t)(sv.time*1000), botsarespecialsnowflakes, 0, 0);\r\n}\r\n\r\nqboolean Q1QVM_SendEntity(quint64_t sendflags)\r\n{\r\n\treturn VM_Call(q1qvm, GAME_EDICT_CSQCSEND, sendflags, 0, 0, 0) > 0;\r\n}\r\n\r\nvoid Q1QVM_Blocked(void)\r\n{\r\n\tVM_Call(q1qvm, GAME_EDICT_BLOCKED, 0, 0, 0);\r\n}\r\n\r\nvoid Q1QVM_SetNewParms(void)\r\n{\r\n\tVM_Call(q1qvm, GAME_SETNEWPARMS, 0, 0, 0);\r\n}\r\n\r\nvoid Q1QVM_SetChangeParms(void)\r\n{\r\n\tVM_Call(q1qvm, GAME_SETCHANGEPARMS, 0, 0, 0);\r\n}\r\n\r\nqboolean Q1QVM_ClientCommand(void)\r\n{\r\n\treturn VM_Call(q1qvm, GAME_CLIENT_COMMAND, 0, 0, 0);\r\n}\r\n\r\nvoid Q1QVM_GameCodePausedTic(float pausedduration)\r\n{\r\n\tVM_Call(q1qvm, GAME_PAUSED_TIC, (qintptr_t)(pausedduration*1000), 0, 0, 0);\r\n}\r\n\r\nvoid Q1QVM_DropClient(client_t *cl)\r\n{\r\n\tif (cl->name)\r\n\t\tQ_strncpyz(cl->namebuf, cl->name, sizeof(cl->namebuf));\r\n\r\n\tpr_global_struct->self = EDICT_TO_PROG(svprogfuncs, cl->edict);\r\n\tVM_Call(q1qvm, GAME_CLIENT_DISCONNECT, 0, 0, 0);\r\n\tcl->name = cl->namebuf;\r\n}\r\n\r\nvoid Q1QVM_ChainMoved(void)\r\n{\r\n}\r\nvoid Q1QVM_EndFrame(void)\r\n{\r\n}\r\n\r\n#endif\r\n"
  },
  {
    "path": "engine/server/progdefs.h",
    "content": "/*\nCopyright (C) 1996-1997 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  \n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n*/\n\n/* file generated by qcc, do not modify */\n\ntypedef struct globalvars_s\n{\n\tpint_t null;\n\tunion {\n\t\tpvec3_t vec;\n\t\tpvec_t f;\n\t\tpint_t i;\n\t} ret;\n\tunion {\n\t\tpvec3_t vec;\n\t\tpvec_t f;\n\t\tpint_t i;\n\t} param[8];\n} globalvars_t;\n\n#define\tNUM_SPAWN_PARMS\t\t\t64\ntypedef struct nqglobalvars_s\n{\n\tpint_t\t*self;\n\tpint_t\t*other;\n\tpint_t\t*world;\n\tpvec_t\t*time;\n\tpvec_t\t*frametime;\n\tpint_t\t\t*newmis;\n\tpvec_t\t*force_retouch;\n\tstring_t\t*mapname;\n\tpvec_t\t*deathmatch;\n\tpvec_t\t*coop;\n\tpvec_t\t*teamplay;\n\tpvec_t\t*serverflags;\n\tpvec_t\t*total_secrets;\n\tpvec_t\t*total_monsters;\n\tpvec_t\t*found_secrets;\n\tpvec_t\t*killed_monsters;\n\tpvec3_t\t*v_forward;\n\tpvec3_t\t*v_up;\n\tpvec3_t\t*v_right;\n\tpvec_t\t*trace_allsolid;\n\tpvec_t\t*trace_startsolid;\n\tpvec_t\t*trace_fraction;\n#ifdef HAVE_LEGACY\n\tpvec_t\t*trace_surfaceflagsf;\n#endif\n\tpint_t\t\t*trace_surfaceflagsi;\n\tstring_t*trace_surfacename;\n#ifdef HAVE_LEGACY\n\tpvec_t\t*trace_endcontentsf;\n#endif\n\tpint_t\t\t*trace_endcontentsi;\n\tpint_t\t\t*trace_brush_id;\n\tpint_t\t\t*trace_brush_faceid;\n\tpint_t\t\t*trace_surface_id;\n\tpint_t\t\t*trace_bone_id;\n\tpint_t\t\t*trace_triangle_id;\n\tpvec3_t\t*trace_endpos;\n\tpvec3_t\t*trace_plane_normal;\n\tpvec_t\t*trace_plane_dist;\n\tpint_t\t*trace_ent;\n\tpvec_t\t*trace_inopen;\n\tpvec_t\t*trace_inwater;\n#ifdef HAVE_LEGACY\n\tstring_t*trace_dphittexturename;\n\tpvec_t *trace_dpstartcontents;\n\tpvec_t *trace_dphitcontents;\n\tpvec_t *trace_dphitq3surfaceflags;\n#endif\n\tpint_t\t*msg_entity;\n\tfunc_t\t*main;\n\tfunc_t\t*StartFrame;\n\tfunc_t\t*PlayerPreThink;\n\tfunc_t\t*PlayerPostThink;\n\tfunc_t\t*ClientKill;\n\tfunc_t\t*ClientConnect;\n\tfunc_t\t*PutClientInServer;\n\tfunc_t\t*ClientDisconnect;\n\tfunc_t\t*SetNewParms;\n\tfunc_t\t*SetChangeParms;\n\tpvec_t *cycle_wrapped;\n\tpvec_t *dimension_send;\n\tpvec_t *dimension_default;\n\n\tpvec_t *physics_mode;\n\n\tpvec_t *input_sequence;\n\tpvec_t *input_servertime;\n\tpvec_t *input_clienttime;\n\tpvec_t *input_timelength;\n\tpvec_t *input_impulse;\n\tpvec3_t *input_angles;\n\tpvec3_t *input_movevalues;\n\tpvec_t *input_buttons;\n\tpuint64_t *input_weapon;\n\tpvec_t *input_lightlevel;\n\tpvec3_t *input_cursor_screen;\n\tpvec3_t *input_cursor_trace_start;\n\tpvec3_t *input_cursor_trace_endpos;\n\tpvec_t *input_cursor_entitynumber;\n\tpuint64_t *input_head_status;\n\tpvec3_t *input_head_origin;\n\tpvec3_t *input_head_angles;\n\tpvec3_t *input_head_velocity;\n\tpvec3_t *input_head_avelocity;\n\tpuint64_t *input_head_weapon;\n\tpuint64_t *input_left_status;\n\tpvec3_t *input_left_origin;\n\tpvec3_t *input_left_angles;\n\tpvec3_t *input_left_velocity;\n\tpvec3_t *input_left_avelocity;\n\tpuint64_t *input_left_weapon;\n\tpuint64_t *input_right_status;\n\tpvec3_t *input_right_origin;\n\tpvec3_t *input_right_angles;\n\tpvec3_t *input_right_velocity;\n\tpvec3_t *input_right_avelocity;\n\tpuint64_t *input_right_weapon;\n\n\tpvec3_t *global_gravitydir;\n\tpvec_t *spawnparamglobals[NUM_SPAWN_PARMS];\n\tstring_t *parm_string;\n\tpint_t *serverid;\n} globalptrs_t;\n\n#define P_VEC(v) (pr_global_struct->v)\n\n\n#ifndef HAVE_LEGACY\n#define comfieldfloat_legacy(n,desc)\n#define comfieldfloatdep_legacy(n,desc,depreason)\n#else\n#define comfieldfloat_legacy comfieldfloat\n#define comfieldfloatdep_legacy comfieldfloatdep\n#endif\n\n#ifndef QUAKESTATS\n#define comfieldfloat_legacystat(n,desc)\n#define comfieldfloatdep_legacystat(n,desc,depreason)\n#else\n#define comfieldfloat_legacystat comfieldfloat\n#define comfieldfloatdep_legacystat comfieldfloatdep\n#endif\n\n/*my hands are tied when it comes to the layout of this structure\nOn the server side, the structure *must* match original quakeworld, or we break compatibility with mvdsv's qvm api\nOn the client, it really doesn't matter what order fields are in, qclib will remap.\nBut fields that are actually useful on both sides need to be in the same locations.\nBut if we include all, that's a waste for csqc...\nBut we can overlap useful csqc-only ones with ssqc ones that are not going to be used on the client, so long as the types match.\nThis list isn't shared with the menu.\n\nso the base fields are a fixed size\nand the extension fields are added on the end and can have extra vm-specific stuff added on the end\n*/\n/*DO NOT ADD TO THIS STRUCTURE (base-qw-compat for q1qvm)*/\n#define comqcfields\t\\\n\tcomfieldfloat(modelindex,\"This is the model precache index for the model that was set on the entity, instead of having to look up the model according to the .model field. Use setmodel to change it.\")\\\n\tcomfieldvector(absmin,\"Set by the engine when the entity is relinked (by setorigin, setsize, or setmodel). This is in world coordinates.\")\\\n\tcomfieldvector(absmax,\"Set by the engine when the entity is relinked (by setorigin, setsize, or setmodel). This is in world coordinates.\")\\\n\tcomfieldfloat(ltime,\"On MOVETYPE_PUSH entities, this is used as an alternative to the 'time' global, and .nextthink is synced to this instead of time. This allows time to effectively freeze if the entity is blocked, ensuring the think happens when the entity reaches the target point instead of randomly.\")\\\n\tcomfieldfloat(lastruntime,\"This field used to be used to avoid running an entity multiple times in a single frame due to quakeworld's out-of-order thinks. It is no longer used by FTE due to precision issues, but may still be updated for compatibility reasons.\")\t/*type doesn't match the qc, we use a hidden double instead. this is dead.*/ \t\\\n\tcomfieldfloat(movetype,\"Describes how the entity moves. One of the MOVETYPE_ constants.\")\\\n\tcomfieldfloat(solid,\"Describes whether the entity is solid or not, and any special properties infered by that. Must be one of the SOLID_ constants\")\\\n\tcomfieldvector(origin,\"The current location of the entity in world space. Inline bsp entities (ie: ones placed by a mapper) will typically have a value of '0 0 0' in their neutral pose, as the geometry is offset from that. It is the reference point of the entity rather than the center of its geometry, for non-bsp models, this is often not a significant distinction.\")\\\n\tcomfieldvector(oldorigin,\"This is often used on players to reset the player back to where they were last frame if they somehow got stuck inside something due to fpu precision. Never change a player's oldorigin field to inside a solid, because that might cause them to become pemanently stuck.\")\\\n\tcomfieldvector(velocity,\"The direction and speed that the entity is moving in world space.\")\\\n\tcomfieldvector(angles,\"The eular angles the entity is facing in, in pitch, yaw, roll order. Due to a legacy bug, mdl/iqm/etc formats use +x=UP, bsp/spr/etc formats use +x=DOWN.\")\\\n\tcomfieldvector(avelocity,\"The amount the entity's angles change by each frame. Note that this is direct eular angles, and thus the angular change is non-linear and often just looks buggy.\")\\\n\tcomfieldstring(classname,\"Identifies the class/type of the entity. Useful for debugging, also used for loading, but its value is not otherwise significant to the engine, this leaves the mod free to set it to whatever it wants and randomly test strings for values in whatever inefficient way it chooses fit.\")\\\n\tcomfieldstring(model,\"The model name that was set via setmodel, in theory. Often, this is cleared to null to prevent the engine from being seen by clients while not changing modelindex. This behaviour allows inline models to remain solid yet be invisible.\")\\\n\tcomfieldfloat(frame,\"The current frame the entity is meant to be displayed in. In CSQC, note the lerpfrac and frame2 fields as well. if it specifies a framegroup, the framegroup will autoanimate in ssqc, but not in csqc.\")\\\n\tcomfieldfloat(skin,\"The skin index to use. on a bsp entity, setting this to 1 will switch to the 'activated' texture instead. A negative value will be understood as a replacement contents value, so setting it to CONTENTS_WATER will make a movable pool of water.\")\\\n\tcomfieldfloat(effects,\"Lots of random flags that change random effects.\")\\\n\tcomfieldvector(mins,\"The minimum extent of the model (ie: the bottom-left coordinate relative to the entity's origin). Change via setsize. May also be changed by setmodel.\")\\\n\tcomfieldvector(maxs,\"like mins, but in the other direction.\")\\\n\tcomfieldvector(size,\"maxs-mins. Updated when the entity is relinked (by setorigin, setsize, setmodel)\")\\\n\tcomfieldfunction(touch, \".void()\",NULL)\\\n\tcomfieldfunction(use, \".void()\",NULL)\\\n\tcomfieldfunction(think, \".void()\",NULL)\\\n\tcomfieldfunction(blocked, \".void()\",NULL)\\\n\tcomfieldfloat(nextthink,\"The time at which the entity is next scheduled to fire its think event. For MOVETYPE_PUSH entities, this is relative to that entity's ltime field, for all other entities it is relative to the time gloal.\")\\\n\tcomfieldentity(groundentity,NULL)\\\n\tcomfieldfloat(health,NULL)\\\n\tcomfieldfloat(frags,NULL)\\\n\tcomfieldfloat(weapon,NULL)\\\n\tcomfieldstring(weaponmodel,NULL)\\\n\tcomfieldfloat(weaponframe,NULL)\\\n\tcomfieldfloat(currentammo,NULL)\\\n\tcomfieldfloat(ammo_shells,NULL)\\\n\tcomfieldfloat(ammo_nails,NULL)\\\n\tcomfieldfloat(ammo_rockets,NULL)\\\n\tcomfieldfloat(ammo_cells,NULL)\\\n\tcomfieldfloat(items,NULL)\\\n\tcomfieldfloat(takedamage,NULL)\\\n\tcomfieldentity(chain,NULL)\\\n\tcomfieldfloat(deadflag,NULL)\\\n\tcomfieldvector(view_ofs,NULL)\\\n\tcomfieldfloat(button0,NULL)\\\n\tcomfieldfloat(button1,NULL)\t/*dead field in nq mode*/\t\\\n\tcomfieldfloat(button2,NULL)\\\n\tcomfieldfloat(impulse,NULL)\\\n\tcomfieldfloat(fixangle,NULL)\\\n\tcomfieldvector(v_angle,\"The angles a player is viewing. +x is DOWN (pitch, yaw, roll)\")\\\n\tcomfieldstring(netname,NULL)\\\n\tcomfieldentity(enemy,NULL)\\\n\tcomfieldfloat(flags,NULL)\\\n\tcomfieldfloat(colormap,NULL)\\\n\tcomfieldfloat(team,NULL)\\\n\tcomfieldfloat(max_health,NULL)\\\n\tcomfieldfloat(teleport_time,NULL)\\\n\tcomfieldfloat(armortype,NULL)\\\n\tcomfieldfloat(armorvalue,NULL)\\\n\tcomfieldfloat(waterlevel,NULL)\\\n\tcomfieldfloat(watertype,NULL)\\\n\tcomfieldfloat(ideal_yaw,NULL)\\\n\tcomfieldfloat(yaw_speed,NULL)\\\n\tcomfieldentity(aiment,NULL)\\\n\tcomfieldentity(goalentity,NULL)\\\n\tcomfieldfloat(spawnflags,NULL)\\\n\tcomfieldstring(target,NULL)\\\n\tcomfieldstring(targetname,NULL)\\\n\tcomfieldfloat(dmg_take,NULL)\\\n\tcomfieldfloat(dmg_save,NULL)\\\n\tcomfieldentity(dmg_inflictor,NULL)\\\n\tcomfieldentity(owner,NULL)\\\n\tcomfieldvector(movedir,NULL)\\\n\tcomfieldstring(message,NULL)\t/*don't use directly, hexen2 uses floats, so we go via qclib for message*/\\\n\tcomfieldfloat(sounds,NULL)\\\n\tcomfieldstring(noise,NULL)\\\n\tcomfieldstring(noise1,NULL)\\\n\tcomfieldstring(noise2,NULL)\\\n\tcomfieldstring(noise3,NULL)\n/*DO NOT ADD TO THE ABOVE STRUCTURE (unless you want to break qvms)*/\n\n#ifdef HEXEN2\n#define comextqcfieldshexen2\t\\\n\tcomfieldfloat(drawflags,\"Various flags that affect lighting values and scaling. Typically set to 96 in quake for proper compatibility with DP_QC_SCALE.\")/*hexen2*/\\\n\tcomfieldfloat(abslight,\"Allows overriding light levels. Use drawflags to state that this field should actually be used.\")/*hexen2's force a lightlevel*/\\\n\n#define svextqcfieldshexen2\t\\\n\tcomfieldfloat(playerclass,NULL)/*hexen2 requirements*/\\\n\tcomfieldfloat(hasted,NULL)/*hexen2 uses this AS WELL as maxspeed*/\\\n\tcomfieldfloat(light_level,\"Used by hexen2 to indicate the light level where the player is standing.\")\\\n\n#else\n#define comextqcfieldshexen2\n#define svextqcfieldshexen2\n#endif\n\n#define comextqcfields\t\\\n\tcomfieldvector(punchangle,NULL) /*std in nq*/\\\n\tcomfieldfloat(gravity,\"Multiplier applied in addition to sv_gravity (not absolute units), to control the gravity affecting this entity specifically.\")\t/*added in quake 1.09 (for hipnotic)*/\\\n\tcomfieldfloat(hull,\"Overrides the hull used by the entity for walkmove/movetogoal and not traceline/tracebox.\")/*PEXT_HEXEN2*/\\\n\tcomfieldentity(movechain,\"This is a linked list of entities which will be moved whenever this entity moves, logically they are attached to this entity.\")/*hexen2*/\\\n\tcomfieldfunction(chainmoved, \".void()\",\"Called when the entity is moved as a result of being part of another entity's .movechain\")/*hexen2*/\\\n\tcomfieldfunction(contentstransition, \".void(float old, float new)\",\"This function is called when the entity moves between water and air. If specified, default splash sounds will be disabled allowing you to provide your own.\")/*ENTITYCONTENTSTRANSITION*/\\\n\tcomfieldfloat(dimension_solid,\"This is the bitmask of dimensions which the entity is solid within. This is not networked, instead csqc traces impacting ssqc entities assumes the ssqc entity to have a dimension_solid of 1.\")/*EXT_DIMENSION_PHYSICS*/\\\n\tcomfieldfloat(dimension_hit,\"This is the bitmask of dimensions which the entity will be blocked by. If other.dimension_solid & self.dimension_hit, our traces will impact and not proceed. If its false, the traces will NOT impact, allowing self to pass straight through.\")/*EXT_DIMENSION_PHYSICS*/\\\n\t/*comfieldfloat_legacy(hitcontentsmask,\"Traces performed for this entity will impact against surfaces that match this contents mask.\")*/ \\\n\tcomfieldint(hitcontentsmaski,\"Traces performed for this entity will impact against surfaces that match this contents mask (CONTENTBITS_* constants).\")\\\n\tcomfieldfloatdep_legacy(dphitcontentsmask, \"Some crappy field that inefficiently requires translating to the native contents flags. Ditch the 'dp', do it properly.\", \"Does not support mod-specific contents.\")\\\n\tcomfieldfloat(scale,\"Multiplier that resizes the entity. 1 is normal sized, 2 is double sized. scale 0 is remapped to 1. In SSQC, this is limited to 1/16th precision, with a maximum just shy of 16.\")/*DP_ENT_SCALE*/\\\n\tcomfieldfloat(fatness,\"How many QuakeUnits to push the entity's verticies along their normals by.\")/*FTE_PEXT_FATNESS*/\\\n\tcomfieldfloat(alpha,\"The transparency of the entity. 1 means opaque, 0.0001 means virtually invisible. 0 is remapped to 1, for compatibility.\")/*DP_ENT_ALPHA*/\\\n\tcomfieldfloat(modelflags,\"Used to override the flags set in the entity's model. Should be set according to the MF_ constants. Use effects|=EF_NOMODELFLAGS to ignore the model's flags completely. The traileffectnum field is more versatile.\")\\\n\tcomfieldfloat(frame1time,\"This controls the time into the framegroup/animation named by .frame, you should increment this value according to frametime or to distance moved, depending on the sort of animation you're attempting. You may wish to avoid incrementing this while lerpfrac is still changing, to avoid wasting parts of the animation.\")\t/*EXT_CSQC_1*/\\\n\tcomfieldfloat(basebone,\"The base* frame animations are equivelent to their non-base versions, except that they only affect bone numbers below the 'basebone' value. This means that the base* animation can affect the legs of a skeletal model independantly of the normal animation fields affecting the torso area. For more complex animation than this, use skeletal objects.\")\t/*FTE_QC_BASEFRAME*/\\\n\tcomfieldfloat(baseframe,\"See basebone\")\t/*FTE_QC_BASEFRAME*/\\\n\tcomfieldfunction(customphysics,\".void()\", \"Called once each physics frame, overriding the entity's .movetype field and associated logic. You'll probably want to use tracebox to move it through the world. Be sure to call .think as appropriate.\")\\\n\tcomfieldentity(tag_entity,\"Specifies which entity this entity's origin+angles is 'attached' to.\")\\\n\tcomfieldfloat(tag_index,\"Specifies the tag or bone on the parent entity that we're attached to. If this is -1 then the entity is instead a q3-like camera portal, with the tag_entity saying the entity to display for. If tag_entity is world then this is a q3-like portal surface marker with a separate camera (with a tag_entity referring to the portal surface).\")\\\n\tcomfieldfloat(skeletonindex,\"This object serves as a container for the skeletal bone states used to override the animation data.\")\t\t/*FTE_CSQC_SKELETONOBJECTS*/\\\n\tcomfieldvector(colormod,\"Provides a colour tint for the entity (does not affect fullbrights).\")\\\n\tcomfieldvector(glowmod,\"Scaler for an entity's fullbright textures.\")\\\n\tcomfieldvector(gravitydir,\"Specifies the direction in which gravity acts. Must be normalised. '0 0 0' also means down. Use '0 0 1' if you want the player to be able to run on ceilings.\")\\\n\tcomfieldfunction(camera_transform,\".vector(vector org, vector ang)\", \"A callback that provides portal transform information for portal surfaces attached to this entity. Also used to open up pvs in ssqc.\")\\\n\tcomfieldfloat(pmove_flags,NULL)/*EXT_CSQC_1*/\\\n\tcomfieldfloat(geomtype,NULL)/*DP_...PHYSICS*/\\\n\tcomfieldfloat(friction,NULL)/*DP_...PHYSICS*/\\\n\tcomfieldfloat(erp,NULL)/*DP_...PHYSICS*/\\\n\tcomfieldfloat(jointtype,NULL)/*DP_...PHYSICS*/\\\n\tcomfieldfloat(mass,NULL)/*DP_...PHYSICS*/\\\n\tcomfieldfloat(bouncefactor,NULL)/*DP_...PHYSICS*/\\\n\tcomfieldfloat(bouncestop,NULL)/*DP_...PHYSICS*/\\\n\tcomfieldfloat(damp_linear,NULL)/*FTE_...PHYSICS*/\\\n\tcomfieldfloat(damp_angular,NULL)/*FTE_...PHYSICS*/\\\n\tcomfieldfloat(max_angular,NULL)/*FTE_...PHYSICS*/\\\n\tcomfieldfloat(jointgroup,NULL)/*FTE_...PHYSICS*/\\\n\tcomfieldfloat(idealpitch,NULL)/*DP_QC_CHANGEPITCH (inconsistant naming)*/\\\n\tcomfieldfloat(pitch_speed,NULL)/*DP_QC_CHANGEPITCH*/\\\n\tcomextqcfieldshexen2\t\\\n\tcomfieldvector(color,\"This affects the colour of realtime lights that were enabled via the pflags field.\")/*Hexen2 has a .float color, the warnings should be benign but does mean updated hexen2 mods may need to use color_x for map compat*/ \\\n\tcomfieldfloat(light_lev,\"This is the radius of an entity's light. This is not normally used by the engine, but is used for realtime lights (ones that are enabled with the pflags field).\")\\\n\tcomfieldfloat(style,\"Used by the light util to decide how an entity's light should animate. On an entity with pflags set, this also affects realtime lights.\")\\\n\tcomfieldfloat(pflags,\"Realtime lighting flags\")\n\n#ifdef HEXEN2\n#else\n#define svextqcfieldshexen2\n#endif\n\n#ifdef PEXT_VIEW2\n#define svextqcfield_clientcamera comfieldentity(clientcamera,\"Controls which entity to use for this client's camera.\")\n#else\n#define svextqcfield_clientcamera\n#endif\n\n#define svextqcfields \\\n\tcomfieldfloat(maxspeed,NULL)/*added in quake 1.09*/\\\n\tcomfieldentity(view2,\"defines a second viewpoint, typically displayed in a corner of the screen (also punches open pvs).\")/*FTE_PEXT_VIEW2*/\\\n\tcomfieldvector(movement,\"These are the directions that the player is currently trying to move in (ie: which +forward/+moveright/+moveup etc buttons they have held), expressed relative to that player's angles. Order is forward, right, up.\")\\\n\tcomfieldfloat(vw_index,\"This acts as a second modelindex, using the same frames etc.\")\\\n\tcomfieldentitydep(nodrawtoclient,\"This entity will not be sent to the player named by this field. They will be invisible and not emit dlights/particles. Does not work in MVD-recorded game.\", \"Cannot be recorded in MVDs, nor work properly with splitscreen. Use CSQC instead.\")\\\n\tcomfieldentitydep(drawonlytoclient,\"This entity will be sent *only* to the player named by this field. To other players they will be invisible and not emit dlights/particles. Does not work in MVD-recorded game.\", \"Cannot be recorded in MVDs, nor work properly with splitscreen. Use CSQC instead.\")\\\n\tcomfieldentitydep(viewmodelforclient,\"This entity will be sent only to the player named by this field, and this entity will be attached to the player's view as an additional weapon model.\", \"Redundant. Cannot be recorded in MVDs, nor work properly with splitscreen. Use CSQC instead.\")/*DP_ENT_VIEWMODEL*/\\\n\tcomfieldentitydep(exteriormodeltoclient,\"This entity will be invisible to the player named by this field, except in mirrors or mirror-like surfaces, where it will be visible as normal. It may still cast shadows as normal, and generate lights+particles, depending on client settings. Does not affect how other players see the entity.\", \"Cannot be recorded in MVDs, nor work properly with splitscreen. Use CSQC instead.\")\\\n\tsvextqcfield_clientcamera\\\n\tcomfieldfloat(glow_size,\"Some outdated particle trail thing.\")\\\n\tcomfieldfloat(glow_color,\"Some outdated particle trail thing.\")\\\n\tcomfieldfloat(glow_trail,\"Some outdated particle trail thing.\")\\\n\tcomfieldfloat(traileffectnum,\"This should be set to the result of particleeffectnum, in order to attach a custom trail effect to an entity as it moves.\")/*DP_ENT_TRAILEFFECTNUM*/\\\n\tcomfieldfloat(emiteffectnum,\"This should be set to the result of particleeffectnum, in order to continually spawn particles in the direction that this entity faces.\")/*DP_ENT_TRAILEFFECTNUM*/\\\n\t/*comfieldfloat(baseframe,\"Specifies the current frame(group) to use for the lower (numerically) bones of a skeletal model. The basebone field specifies the bone where the regular frame field takes over.\")*/\t/*FTESS_QC_BASEFRAME*/\\\n\t/*comfieldfloat(basebone,\"Specifies the bone at which the baseframe* fields stop being effective.\")*/\t/*FTE_SSQC_BASEFRAME*/\\\n\tcomfieldfloatdep(dimension_see,\"This is the dimension mask (bitfield) that the client is allowed to see. Entities and events not in this dimension mask will be invisible.\", \"Does not work with MVDs nor splitscreen.\")/*EXT_DIMENSION_VISIBLE*/\\\n\tcomfieldfloatdep(dimension_seen,\"This is the dimension mask (bitfield) that the client is visible within. Clients that cannot see this dimension mask will not see this entity.\", \"Does not work with MVDs nor splitscreen.\")/*EXT_DIMENSION_VISIBLE*/\\\n\tcomfieldfloatdep(dimension_ghost,\"If this entity is visible only within these dimensions, it will become transparent, as if a ghost.\", \"Does not work with MVDs nor splitscreen.\")/*EXT_DIMENSION_GHOST*/\\\n\tcomfieldfloatdep(dimension_ghost_alpha,\"If this entity is subject to dimension_ghost, this is the scaler for its alpha value. If 0, 0.5 will be used instead.\", \"Does not work with MVDs nor splitscreen.\")/*EXT_DIMENSION_GHOST*/\\\n\tcomfieldfunction(SendEntity, \".float(entity playerent, float changedflags)\",\"Called by the engine whenever an entity needs to be (re)sent to a client's csprogs, either because SendFlags was set or because data was lost. Must write its data to the MSG_ENTITY buffer. Will be called at the engine's leasure.\")/*EXT_CSQC*/\\\n\tcomfieldfloat(SendFlags,\"Indicates that something in the entity has been changed, and that it needs to be updated to all players that can see it. The engine will clear it at some point, with the cleared bits appearing in the 'changedflags' argument of the SendEntity method.\")/*EXT_CSQC_1 (one of the DP guys came up with it)*/\\\n\tcomfieldfloatdep_legacy(Version,\"Obsolete\", \"Use SendFlags instead.\")/*EXT_CSQC (obsolete)*/\\\n\tcomfieldfloatdep_legacy(clientcolors,NULL, \"Doesn't support RGB player colours.\")\\\n\tcomfieldfloat_legacystat(viewzoom,NULL)/*DP_VIEWZOOM, stats*/\\\n\tcomfieldfloat_legacystat(items2,\"stub. commented by default, to prevent items2 being networked instead of runes.\")\t\t/*added in quake 1.09 (for hipnotic). legacy because of stats*/\\\n\tsvextqcfieldshexen2 \\\n\tcomfieldfloat(pvsflags,\"Reconfigures when the entity is visible to clients\")/*EXT_CSQC_1*/\\\n\tcomfieldfloat(uniquespawnid,\"Incremented by 1 whenever the entity is respawned. Persists across remove calls, for when the two-second grace period is insufficient.\")/*FTE_ENT_UNIQUESPAWNID*/\\\n\tcomfieldfunction(customizeentityforclient, \"DEP_CSQC .float()\",\"Called just before an entity is sent to a client (non-csqc protocol). This gives you a chance to tailor 'self' according to what 'other' should see.\")\n\n#ifdef HALFLIFEMODELS\n#define HALFLIFEMODEL_FIELDS\t\\\n  \tcomfieldfloat(bonecontrol1,\"Halflife model format bone controller. On player models, this typically affects the spine's yaw.\")\t/*FTE_CSQC_HALFLIFE_MODELS*/\\\n\tcomfieldfloat(bonecontrol2,\"Halflife model format bone controller. On player models, this typically affects the spine's yaw.\")\t/*FTE_CSQC_HALFLIFE_MODELS*/\\\n\tcomfieldfloat(bonecontrol3,\"Halflife model format bone controller. On player models, this typically affects the spine's yaw.\")\t/*FTE_CSQC_HALFLIFE_MODELS*/\\\n\tcomfieldfloat(bonecontrol4,\"Halflife model format bone controller. On player models, this typically affects the spine's yaw.\")\t/*FTE_CSQC_HALFLIFE_MODELS*/\\\n\tcomfieldfloat(bonecontrol5,\"Halflife model format bone controller. This typically affects the mouth.\")\t/*FTE_CSQC_HALFLIFE_MODELS*/\\\n\tcomfieldfloat(subblendfrac,\"Weird animation value specific to halflife models. On player models, this typically affects the spine's pitch, or yaw, or...\")\t/*FTE_CSQC_HALFLIFE_MODELS*/\\\n\tcomfieldfloat(subblend2frac,\"Weird animation value specific to halflife models. I've no idea what this does, probably nothing for most models.\")\t/*FTE_CSQC_HALFLIFE_MODELS*/\\\n\tcomfieldfloat(basesubblendfrac,\"See basebone\")\t/*FTE_CSQC_HALFLIFE_MODELS+FTE_CSQC_BASEFRAME*/\\\n\tcomfieldfloat(basesubblend2frac,\"See basebone\")\t/*FTE_CSQC_HALFLIFE_MODELS+FTE_CSQC_BASEFRAME*/\n#else\n#define HALFLIFEMODEL_FIELDS\n#endif\n\n#if FRAME_BLENDS >= 4\n#define frame34fields \\\n\tcomfieldfloat(frame3,\"Some people just don't understand how to use framegroups...\")\t\t/**/\\\n\tcomfieldfloat(frame3time,\".frame3 equivelent of frame1time.\")\t/*EXT_CSQC_1*/\\\n\tcomfieldfloat(lerpfrac3,\"Weight of .frame3 - .frame's weight is automatically calculated as 1-(lerpfrac+lerpfrac3+lerpfrac4), as a result these fields should NEVER add to above 1.\")\t/**/\\\n\tcomfieldfloat(frame4,NULL)\t\t/**/\\\n\tcomfieldfloat(frame4time,\".frame4 equivelent of frame1time.\")\t/*EXT_CSQC_1*/\\\n\tcomfieldfloat(lerpfrac4,NULL)\t/**/\\\n\n#else\n#define frame34fields\n#endif\n\n//this is the list for all the csqc fields.\n//(the #define is so the list always matches the ones pulled out)\n#define csqcextfields\t\\\n\tcomfieldfloat(entnum,\"This is the number of the entity that the ssqc is using.\")\t\t\\\n\tcomfieldfloat(frame2,\"This is typically the old frame of the entity. if lerpfrac is 1, .frame will be ignored and .frame2 will be used solely. lerpfrac 0.5 will give an even 50/50 blend.\")\t\t/*EXT_CSQC_1*/\\\n\tcomfieldfloat(frame2time,\".frame2 equivelent of frame1time.\")\t/*EXT_CSQC_1*/\\\n\tcomfieldfloat(lerpfrac,\"The weight of .frame2 (with the weight of .frame being inferred). A value of 0 normally means the entity will animate using only .frame, while 1 would exclusively be .frame2. As this value is incremented, more of frame2 will be used. If you wish to use .frame2 as the 'old' frame, it is generally recommended to start this field with the value 1, to decrement it by frametime, and when it drops below 0 add 1 to it and update .frame2 and .frame to lerp into the new frame.\")\t/*EXT_CSQC_1*/\\\n\tframe34fields\t\\\n\tcomfieldfloat(renderflags,\"Matches to the RF_* flags, read during addentity/addentities.\")\\\n\tcomfieldfloat(forceshader,\"Contains a shader handle used to replace all surfaces upon the entity.\")/*FTE_CSQC_SHADERS*/\\\n\t\t\t\t\t\t\t\\\n\tcomfieldfloat(baseframe2,\"See basebone\")\t/*FTE_CSQC_BASEFRAME*/\\\n\tcomfieldfloat(baseframe1time,\"See basebone\")\t/*FTE_CSQC_BASEFRAME*/\\\n\tcomfieldfloat(baseframe2time,\"See basebone\")\t/*FTE_CSQC_BASEFRAME*/\\\n\tcomfieldfloat(baselerpfrac,\"See basebone\")\t/*FTE_CSQC_BASEFRAME*/\\\n\tHALFLIFEMODEL_FIELDS\t\\\n\tcomfieldfloat(drawmask, \"Matces the bitmask passed to the addentities builtin, to easily submit entities to the renderer. Not otherwise meaningful.\")\t/*So that the qc can specify all rockets at once or all bannanas at once*/\t\\\n\tcomfieldfunction(predraw, \".float()\",\"Called as part of the addentities builtin. Returns one of the PREDRAW_ constants. This gives you a chance to interpolate or animate entities as desired.\")\t/*If present, is called just before it's drawn.*/\t\n\n\n#define comfieldentitydep(nam,desc,depreason) comfieldentity(nam,desc)\n#define comfieldfloatdep(nam,desc,depreason) comfieldfloat(nam,desc)\n\ntypedef struct stdentvars_s //standard = standard for qw\n{\n#define comfieldfloat(sharedname,desc) pvec_t sharedname;\n#define comfieldvector(sharedname,desc) pvec3_t sharedname;\n#define comfieldentity(sharedname,desc) pint_t sharedname;\n#define comfieldstring(sharedname,desc) string_t sharedname;\n#define comfieldfunction(sharedname, typestr,desc) func_t sharedname;\ncomqcfields\n#undef comfieldfloat\n#undef comfieldvector\n#undef comfieldentity\n#undef comfieldstring\n#undef comfieldfunction\n#ifdef VM_Q1\n} stdentvars_t;\n\ntypedef struct extentvars_s\n{\n#endif\n#define comfieldfloat(name,desc) pvec_t name;\n#define comfieldint(name,desc) pint_t name;\n#define comfieldvector(name,desc) pvec3_t name;\n#define comfieldentity(name,desc) pint_t name;\n#define comfieldstring(name,desc) string_t name;\n#define comfieldfunction(name, typestr,desc) func_t name;\ncomextqcfields\nsvextqcfields\n#undef comfieldfloat\n#undef comfieldint\n#undef comfieldvector\n#undef comfieldentity\n#undef comfieldstring\n#undef comfieldfunction\n\n#ifdef VM_Q1\n} extentvars_t;\n#else\n} stdentvars_t;\n#endif\n\ntypedef struct {\n#define comfieldfloat(sharedname,desc) pvec_t sharedname;\n#define comfieldvector(sharedname,desc) pvec3_t sharedname;\n#define comfieldentity(sharedname,desc) pint_t sharedname;\n#define comfieldstring(sharedname,desc) string_t sharedname;\n#define comfieldfunction(sharedname, typestr,desc) func_t sharedname;\ncomqcfields\n#undef comfieldfloat\n#undef comfieldvector\n#undef comfieldentity\n#undef comfieldstring\n#undef comfieldfunction\n\n#ifdef VM_Q1\n} comentvars_t;\ntypedef struct {\n#endif\n\n#define comfieldfloat(name,desc) pvec_t name;\n#define comfieldint(name,desc) pint_t name;\n#define comfieldvector(name,desc) pvec3_t name;\n#define comfieldentity(name,desc) pint_t name;\n#define comfieldstring(name,desc) string_t name;\n#define comfieldfunction(name, typestr,desc) func_t name;\ncomextqcfields\n#undef comfieldfloat\n#undef comfieldint\n#undef comfieldvector\n#undef comfieldentity\n#undef comfieldstring\n#undef comfieldfunction\n\n#ifdef VM_Q1\n} comextentvars_t;\n#else\n} comentvars_t;\n#endif\n\n#ifdef USEAREAGRID\n#define AREAGRIDPERENT 16\n#endif\n\n#ifdef USERBE\ntypedef struct\n{\n\tvoid *body;\n\tvoid *geom;\n} rbebody_t;\ntypedef struct\n{\n\t//doll info\n\tchar name[32];\n\tint bone;\n\tfloat animate;\n\tqboolean draw:1;\n\tqboolean orient:1;\n\tqboolean isoffset:1;\n\tint orientpeer;\n\n\t//physics engine info\n\tint geomshape;\n\tfloat relmatrix[12];\n\tfloat inverserelmatrix[12];\n\tvec3_t dimensions;\n\tfloat mass;\n} rbebodyinfo_t;\n\ntypedef struct\n{\n\tvoid *joint;\n} rbejoint_t;\ntypedef struct\n{\n\t//doll info\n\tchar name[32];\n//\tunsigned int disablebits;\n\tqboolean draw:1;\n\tqboolean startenabled:1;\n\n\t//ode info\n\tint type;\n\tint body1;\t//handled by the ragdoll code, rather than the physics library.\n\tint body2;\t//handled by the ragdoll code.\n\tint bonepivot;\t//pivot is specified relative to this bone.\n\n\tfloat FMax,\t\tFMax2;\n\tfloat HiStop,\tHiStop2;\n\tfloat LoStop,\tLoStop2;\n\tfloat CFM,\t\tCFM2;\n\tfloat ERP,\t\tERP2;\n\tfloat Vel,\t\tVel2;\n\tvec3_t offset,\toffset2;\n\tvec3_t axis,\taxis2;\n} rbejointinfo_t;\n\nenum rbecommands_e\n{\n\tRBECMD_ENABLE,\n\tRBECMD_DISABLE,\n\tRBECMD_FORCE,\n\tRBECMD_TORQUE,\n};\n\ntypedef struct rbecommandqueue_s\n{\n\tstruct rbecommandqueue_s *next;\n\tenum rbecommands_e command;\n\tstruct wedict_s *edict;\n\tvec3_t v1;\n\tvec3_t v2;\n} rbecommandqueue_t;\n\ntypedef struct\n{\n\t// physics parameters\n\tqboolean physics;\n\trbebody_t body;\n\trbejoint_t joint;\n\tint jointgroup;\n\tfloat *vertex3f;\n\tint *element3i;\n\tint numvertices;\n\tint numtriangles;\n\tvec3_t mins;\n\tvec3_t maxs;\n\tvec_t mass;\n\tvec3_t origin;\n\tvec3_t velocity;\n\tvec3_t angles;\n\tvec3_t avelocity;\n\tqboolean gravity;\n\tint modelindex;\n\tvec_t movelimit; // smallest component of (maxs[]-mins[])\n\tfloat offsetmatrix[16];\n\tfloat offsetimatrix[16];\n\tint joint_type;\n\tint joint_enemy;\n\tint joint_aiment;\n\tvec3_t joint_origin; // joint anchor\n\tvec3_t joint_angles; // joint axis\n\tvec3_t joint_velocity; // second joint axis\n\tvec3_t joint_movedir; // parameters\n\tvoid *massbuf;\n} entityrbe_t;\n#endif\n"
  },
  {
    "path": "engine/server/progs.h",
    "content": "/*\nCopyright (C) 1996-1997 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  \n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n*/\n\nstruct client_s;\nstruct edict_s;\n\n#define MAX_PROGS 64\n#define MAXADDONS 16\n\nvoid SVQ1_CvarChanged(cvar_t *var);\n#define NewGetEdictFieldValue GetEdictFieldValue\nvoid Q_SetProgsParms(qboolean forcompiler);\nvoid PR_Deinit(void);\t//server shutting down\nvoid PR_LoadGlabalStruct(qboolean muted);\nenum initprogs_e\n{\n\tINITPROGS_NORMAL\t= 0,\n\tINITPROGS_EDITOR\t= 1<<0,\n\tINITPROGS_REQUIRE\t= 1<<1,\n};\nvoid Q_InitProgs(enum initprogs_e flags);\nvoid PR_SpawnInitialEntities(const char *file);\nvoid PR_RegisterFields(void);\nvoid PR_Init(void);\nvoid QDECL ED_Spawned (struct edict_s *ent, int loading);\nvoid SSQC_MapEntityEdited(int modelidx, int idx, const char *newdata);\nvoid SV_SetEntityButtons(struct edict_s *ent, unsigned int buttonbits);\nqboolean SV_RunFullQCMovement(struct client_s *client, usercmd_t *ucmd);\nqboolean PR_KrimzonParseCommand(const char *s);\nqboolean PR_ParseClusterEvent(const char *dest, const char *source, const char *cmd, const char *info);\nqboolean PR_UserCmd(const char *cmd);\nqboolean PR_ConsoleCmd(const char *cmd);\n\n\n#define PR_MAINPROGS 0\t//this is a constant that should really be phased out. But seeing as QCLIB requires some sort of master progs due to extern funcs...\n\t//maybe go through looking for extern funcs, and remember which were not allocated. It would then be a first come gets priority. Not too bad I supppose.\n\nextern int compileactive;\n\ntypedef enum {PROG_NONE, PROG_QW, PROG_NQ, PROG_H2, PROG_PREREL, PROG_TENEBRAE, PROG_UNKNOWN} progstype_t;\t//unknown obtains NQ behaviour\nextern progstype_t progstype;\n\n#include \"../qclib/progslib.h\"\n\ntypedef struct edict_s\n{\n\t//these 5 shared with qclib\n\tenum ereftype_e\tereftype;\n\tfloat\t\t\tfreetime; // sv.time when the object was freed\n\tint\t\t\t\tentnum;\n\tunsigned int\tfieldsize;\n\tpbool\t\t\treadonly;\t//world\n#ifdef VM_Q1\n\tstdentvars_t\t*v;\n\textentvars_t\t*xv;\n#else\n\tunion {\n\t\tstdentvars_t\t*v;\n\t\tstdentvars_t\t*xv;\n\t};\n#endif\n\t/*qc lib doesn't care about the rest*/\n\n\t/*these are shared with csqc*/\n#ifdef USEAREAGRID\n\tareagridlink_t\tgridareas[AREAGRIDPERENT];\t//on overflow, use the inefficient overflow list.\n\tsize_t\t\t\tgridareasequence;\t//used to avoid iterrating the same ent twice.\n#else\n\tlink_t\tarea;\n#endif\n\tpvscache_t pvsinfo;\n\tint lastruntime;\n\tint solidsize;\n#ifdef USERBE\n\tentityrbe_t rbe;\n#endif\n\t/*csqc doesn't reference the rest*/\n\n#ifdef NQPROT\n\tfloat muzzletime;\t//nq clients need special handling to retain EF_MUZZLEFLASH while not breaking qw clients running nq mods.\n#endif\n\tentity_state_t\tbaseline;\n// other fields from progs come immediately after\n} edict_t;\n\n\n\n\n\n#undef pr_global_struct\n#define pr_global_struct *pr_global_ptrs\n\nextern globalptrs_t *pr_global_ptrs;\n\nextern pubprogfuncs_t *svprogfuncs;\t//instance\nextern progparms_t svprogparms;\nextern progsnum_t svmainprogs;\nextern progsnum_t clmainprogs;\n#define\tHLEDICT_FROM_AREA(l) STRUCT_FROM_LINK(l,hledict_t,area)\n#define\tQ2EDICT_FROM_AREA(l) STRUCT_FROM_LINK(l,q2edict_t,area)\n#define\tQ3EDICT_FROM_AREA(l) STRUCT_FROM_LINK(l,q3serverEntity_t,area)\n\nextern func_t SpectatorConnect;\nextern func_t SpectatorThink;\nextern func_t SpectatorDisconnect;\n\nextern func_t SV_PlayerPhysicsQC;\nextern func_t EndFrameQC;\n\nextern qboolean ssqc_deprecated_warned;\nextern cvar_t pr_maxedicts; //used in too many places...\nextern cvar_t noexit, temp1, saved1, saved2, saved3, saved4, savedgamecfg, scratch1, scratch2, scratch3, scratch4, gamecfg, nomonsters; //read by savegame.c\nextern cvar_t pr_ssqc_memsize, pr_imitatemvdsv, sv_aim, sv_maxaim, pr_ssqc_coreonerror, dpcompat_nopreparse;\nextern int pr_teamfield;\n\nqboolean PR_QCChat(char *text, int say_type);\n\nvoid PR_ClientUserInfoChanged(char *name, char *oldivalue, char *newvalue);\nvoid PR_PreShutdown(void);\nvoid PR_LocalInfoChanged(char *name, char *oldivalue, char *newvalue);\nvoid PF_InitTempStrings(pubprogfuncs_t *inst);\n\n#ifdef VM_LUA\nqboolean PR_LoadLua(void);\n#endif\n#ifdef VM_Q1\n#define VMFSID_Q1QVM 57235\t//the q1qvm zone tag that is freed when the module is purged.\nstruct client_s;\nvoid Q1QVM_Shutdown(qboolean notifygame);\nqboolean PR_LoadQ1QVM(void);\nvoid Q1QVM_ClientConnect(struct client_s *cl);\nqboolean Q1QVM_GameConsoleCommand(void);\nqboolean Q1QVM_ClientSay(edict_t *player, qboolean team);\nqboolean Q1QVM_UserInfoChanged(edict_t *player, qboolean after);\nvoid Q1QVM_PlayerPreThink(void);\nvoid Q1QVM_RunPlayerThink(void);\nvoid Q1QVM_PostThink(void);\nvoid Q1QVM_StartFrame(qboolean botsarespecialsnowflakes);\nvoid Q1QVM_Blocked(void);\nqboolean Q1QVM_SendEntity(quint64_t sendflags);\nvoid Q1QVM_SetNewParms(void);\nvoid Q1QVM_SetChangeParms(void);\nqboolean Q1QVM_ClientCommand(void);\nvoid Q1QVM_GameCodePausedTic(float pausedduration);\nvoid Q1QVM_DropClient(struct client_s *cl);\nvoid Q1QVM_ChainMoved(void);\nvoid Q1QVM_EndFrame(void);\n#endif\n\n"
  },
  {
    "path": "engine/server/q2game.h",
    "content": "/*\nCopyright (C) 1997-2001 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  \n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n*/\n\n// game.h -- game dll information visible to server\n\ntypedef enum multicast_e\n{\n\tMULTICAST_ALL,\n\tMULTICAST_PHS,\n\tMULTICAST_PVS,\n\tMULTICAST_ALL_R,\n\tMULTICAST_PHS_R,\n\tMULTICAST_PVS_R,\n\n\tMULTICAST_ONE_SPECS,\n\tMULTICAST_ONE_R_SPECS,\n\tMULTICAST_INIT,\n\tMULTICAST_ONE_NOSPECS,\n\tMULTICAST_ONE_R_NOSPECS,\n} multicast_t;\n\nextern float\tpm_q2stepheight;\n\n#if defined(Q2SERVER) || defined(Q2CLIENT)\n\nstruct trace_s;\nstruct q2trace_s;\nstruct q2pmove_s;\n\n\n#define\tMAXTOUCH\t32\ntypedef struct q2pmove_s\n{\n\t// state (in / out)\n\tq2pmove_state_t\ts;\n\n\t// command (in)\n\tq2usercmd_t\t\tcmd;\n\tqboolean\t\tsnapinitial;\t// if s has been changed outside pmove\n\n\t// results (out)\n\tint\t\t\tnumtouch;\n\tstruct edict_s\t*touchents[MAXTOUCH];\n\n\tvec3_t\t\tviewangles;\t\t\t// clamped\n\tfloat\t\tviewheight;\n\n\tvec3_t\t\tmins, maxs;\t\t\t// bounding box size\n\n\tstruct edict_s\t*groundentity;\n\tint\t\t\twatertype;\n\tint\t\t\twaterlevel;\n\n\t// callbacks to test the world\n\tq2trace_t\t\t(VARGS *trace) (vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end);\n\tint\t\t\t(VARGS *pointcontents) (vec3_t point);\n} q2pmove_t;\n\nvoid VARGS Q2_Pmove (q2pmove_t *pmove);\n\n#endif\n#ifdef Q2SERVER\n\n#define\tQ2GAME_API_VERSION\t3\n\n// edict->svflags\n\n#define\tSVF_NOCLIENT\t\t\t0x00000001\t// don't send entity to clients, even if it has effects\n#define\tSVF_DEADMONSTER\t\t\t0x00000002\t// treat as CONTENTS_DEADMONSTER for collision\n#define\tSVF_MONSTER\t\t\t\t0x00000004\t// treat as CONTENTS_MONSTER for collision\n\n// edict->solid values\n\ntypedef enum\n{\nQ2SOLID_NOT,\t\t\t// no interaction with other objects\nQ2SOLID_TRIGGER,\t\t// only touch when inside, after moving\nQ2SOLID_BBOX,\t\t\t// touch on edge\nQ2SOLID_BSP\t\t\t// bsp clip, touch on edge\n} q2solid_t;\n\n\n//===============================================================\n\n// link_t is only used for entity area links now\n/*typedef struct link_s\n{\n\tstruct link_s\t*prev, *next;\n} link_t;*/\n\n#define\tQ2MAX_ENT_CLUSTERS\t16\n\n\n//typedef struct edict_s edict_t;\ntypedef struct gclient_s gclient_t;\n\n\n#ifndef GAME_INCLUDE\n\nstruct q2gclient_s\n{\n\tq2player_state_t\tps;\t\t// communicated by server to clients\n\tint\t\t\t\tping;\n\t// the game dll can add anything it wants after\n\t// this point in the structure\n};\n\n#endif\n#if defined(Q2SERVER) || defined(Q2CLIENT)\ntypedef struct q2entity_state_s\n{\n\tint\t\tnumber;\t\t\t// edict index\n\n\tvec3_t\torigin;\n\tvec3_t\tangles;\n\tvec3_t\told_origin;\t\t// for lerping\n\tint\t\tmodelindex;\n\tint\t\tmodelindex2, modelindex3, modelindex4;\t// weapons, CTF flags, etc\n\tint\t\tframe;\n\tint\t\tskinnum;\n\tunsigned int\t\teffects;\t\t// PGM - we're filling it, so it needs to be unsigned\n\tint\t\trenderfx;\n\tint\t\tsolid;\t\t\t// for client side prediction, 8*(bits 0-4) is x/y radius\n\t\t\t\t\t\t\t// 8*(bits 5-9) is z down distance, 8(bits10-15) is z up\n\t\t\t\t\t\t\t// gi.linkentity sets this properly\n\tint\t\tsound;\t\t\t// for looping sounds, to guarantee shutoff\n\tint\t\tevent;\t\t\t// impulse events -- muzzle flashes, footsteps, etc\n\t\t\t\t\t\t\t// events only go out for a single frame, they\n\t\t\t\t\t\t\t// are automatically cleared each frame\n} q2entity_state_t;\n#endif\n#if defined(Q2SERVER)\n\n\nstruct q2edict_s\n{\n\tq2entity_state_t\ts;\n\tstruct q2gclient_s\t*client;\n\tqboolean\tinuse;\n\tint\t\t\tlinkcount;\n\n\t// FIXME: move these fields to a server private sv_entity_t\n\tlink_t\t\tarea;\t\t\t\t// linked to a division node or leaf\n\t\n\tint\t\t\tnum_clusters;\t\t// if -1, use headnode instead\n\tint\t\t\tclusternums[Q2MAX_ENT_CLUSTERS];\n\tint\t\t\theadnode;\t\t\t// unused if num_clusters != -1\n\tint\t\t\tareanum, areanum2;\n\n\t//================================\n\n\tint\t\t\tsvflags;\t\t\t// SVF_NOCLIENT, SVF_DEADMONSTER, SVF_MONSTER, etc\n\tvec3_t\t\tmins, maxs;\n\tvec3_t\t\tabsmin, absmax, size;\n\tq2solid_t\t\tsolid;\n\tint\t\t\tclipmask;\n\tq2edict_t\t\t*owner;\n\n\t// the game dll can add anything it wants after\n\t// this point in the structure\n};\n\n#endif\t\t// GAME_INCLUDE\n\n//===============================================================\n\n//\n// functions provided by the main engine\n//\n//yes, these are all VARGS, for the calling convention rather than actually being varargs.\ntypedef struct\n{\n\t// special messages\n\tvoid\t(VARGS *bprintf) (int printlevel, const char *fmt, ...) LIKEPRINTF(2);\n\tvoid\t(VARGS *dprintf) (const char *fmt, ...) LIKEPRINTF(1);\n\tvoid\t(VARGS *cprintf) (q2edict_t *ent, int printlevel, const char *fmt, ...) LIKEPRINTF(3);\n\tvoid\t(VARGS *centerprintf) (q2edict_t *ent, const char *fmt, ...) LIKEPRINTF(2);\n\tvoid\t(VARGS *sound) (q2edict_t *ent, int channel, int soundindex, float volume, float attenuation, float timeofs);\n\tvoid\t(VARGS *positioned_sound) (vec3_t origin, q2edict_t *ent, int channel, int soundinedex, float volume, float attenuation, float timeofs);\n\n\t// config strings hold all the index strings, the lightstyles,\n\t// and misc data like the sky definition and cdtrack.\n\t// All of the current configstrings are sent to clients when\n\t// they connect, and changes are sent to all connected clients.\n\tvoid\t(VARGS *configstring) (int num, const char *string);\n\n\tvoid\t(VARGS *error) (const char *fmt, ...) LIKEPRINTF(1);\n\n\t// the *index functions create configstrings and some internal server state\n\tint\t\t(VARGS *modelindex) (const char *name);\n\tint\t\t(VARGS *soundindex) (const char *name);\n\tint\t\t(VARGS *imageindex) (const char *name);\n\n\tvoid\t(VARGS *setmodel) (q2edict_t *ent, const char *name);\n\n\t// collision detection\n\tq2trace_t\t(VARGS *trace) (vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, q2edict_t *passent, int contentmask);\n\tint\t\t(VARGS *pointcontents) (vec3_t point);\n\tqboolean\t(VARGS *inPVS) (vec3_t p1, vec3_t p2);\n\tqboolean\t(VARGS *inPHS) (vec3_t p1, vec3_t p2);\n\tvoid\t\t(VARGS *SetAreaPortalState) (unsigned int portalnum, qboolean open);\n\tqboolean\t(VARGS *AreasConnected) (unsigned int area1, unsigned int area2);\n\n\t// an entity will never be sent to a client or used for collision\n\t// if it is not passed to linkentity.  If the size, position, or\n\t// solidity changes, it must be relinked.\n\tvoid\t(VARGS *linkentity) (q2edict_t *ent);\n\tvoid\t(VARGS *unlinkentity) (q2edict_t *ent);\t\t// call before removing an interactive edict\n\tint\t\t(VARGS *BoxEdicts) (vec3_t mins, vec3_t maxs, q2edict_t **list,\tint maxcount, int areatype);\n\tvoid\t(VARGS *Pmove) (q2pmove_t *pmove);\t\t// player movement code common with client prediction\n\n\t// network messaging\n\tvoid\t(VARGS *multicast) (vec3_t origin, multicast_t to);\n\tvoid\t(VARGS *unicast) (q2edict_t *ent, qboolean reliable);\n\tvoid\t(VARGS *WriteChar) (int c);\n\tvoid\t(VARGS *WriteByte) (int c);\n\tvoid\t(VARGS *WriteShort) (int c);\n\tvoid\t(VARGS *WriteLong) (int c);\n\tvoid\t(VARGS *WriteFloat) (float f);\n\tvoid\t(VARGS *WriteString) (const char *s);\n\tvoid\t(VARGS *WritePosition) (vec3_t pos);\t// some fractional bits\n\tvoid\t(VARGS *WriteDir) (vec3_t pos);\t\t// single byte encoded, very coarse\n\tvoid\t(VARGS *WriteAngle) (float f);\n\n\t// managed memory allocation\n\tvoid\t*(VARGS *TagMalloc) (int size, int tag);\n\tvoid\t(VARGS *TagFree) (void *block);\n\tvoid\t(VARGS *FreeTags) (int tag);\n\n\t// console variable interaction\n\tcvar_t\t*(VARGS *cvar) (const char *var_name, const char *value, int flags);\n\tcvar_t\t*(VARGS *cvar_set) (const char *var_name, const char *value);\n\tcvar_t\t*(VARGS *cvar_forceset) (const char *var_name, const char *value);\n\n\t// ClientCommand and ServerCommand parameter access\n\tint\t\t(VARGS *argc) (void);\n\tchar\t*(VARGS *argv) (int n);\n\tchar\t*(VARGS *args) (void);\t// concatenation of all argv >= 1\n\n\t// add commands to the server console as if they were typed in\n\t// for map changing, etc\n\tvoid\t(VARGS *AddCommandString) (const char *text);\n\n\tvoid\t(VARGS *DebugGraph) (float value, int color);\n\n\n\t//kmq2 adds pak file stuff, which is certainly useful, but is only half the solution when homedirs are involved.\n} game_import_t;\n\n//\n// functions exported by the game subsystem\n//\ntypedef struct\n{\n\tint\t\t\tapiversion;\n\n\t// the init function will only be called when a game starts,\n\t// not each time a level is loaded.  Persistant data for clients\n\t// and the server can be allocated in init\n\tvoid\t\t(VARGS *Init) (void);\n\tvoid\t\t(VARGS *Shutdown) (void);\n\n\t// each new level entered will cause a call to SpawnEntities\n\tvoid\t\t(VARGS *SpawnEntities) (const char *mapname, const char *entstring, const char *spawnpoint);\n\n\t// Read/Write Game is for storing persistant cross level information\n\t// about the world state and the clients.\n\t// WriteGame is called every time a level is exited.\n\t// ReadGame is called on a loadgame.\n\tvoid\t\t(VARGS *WriteGame) (const char *filename, qboolean autosave);\n\tvoid\t\t(VARGS *ReadGame) (const char *filename);\n\n\t// ReadLevel is called after the default map information has been\n\t// loaded with SpawnEntities\n\tvoid\t\t(VARGS *WriteLevel) (const char *filename);\n\tvoid\t\t(VARGS *ReadLevel) (const char *filename);\n\n\tqboolean\t(VARGS *ClientConnect) (q2edict_t *ent, char *userinfo);\n\tvoid\t\t(VARGS *ClientBegin) (q2edict_t *ent);\n\tvoid\t\t(VARGS *ClientUserinfoChanged) (q2edict_t *ent, char *userinfo);\n\tvoid\t\t(VARGS *ClientDisconnect) (q2edict_t *ent);\n\tvoid\t\t(VARGS *ClientCommand) (q2edict_t *ent);\n\tvoid\t\t(VARGS *ClientThink) (q2edict_t *ent, q2usercmd_t *cmd);\n\n\tvoid\t\t(VARGS *RunFrame) (void);\n\n\t// ServerCommand will be called when an \"sv <command>\" command is issued on the\n\t// server console.\n\t// The game can issue gi.argc() / gi.argv() commands to get the rest\n\t// of the parameters\n\tvoid\t\t(VARGS *ServerCommand) (void);\n\n\t//\n\t// global variables shared between game and server\n\t//\n\n\t// The edict array is allocated in the game dll so it\n\t// can vary in size from one game to another.\n\t// \n\t// The size will be fixed when ge->Init() is called\n\tstruct q2edict_s\t*edicts;\n\tint\t\t\tedict_size;\n\tint\t\t\tnum_edicts;\t\t// current number, <= max_edicts\n\tint\t\t\tmax_edicts;\n} game_export_t;\n\ngame_export_t *GetGameApi (game_import_t *import);\n\n\n\nextern game_export_t\t*ge;\nextern int svq2_maxclients;\n\n#endif\n"
  },
  {
    "path": "engine/server/savegame.c",
    "content": "#include \"quakedef.h\"\n#include \"pr_common.h\"\n\n#if !defined(CLIENTONLY) && defined(SAVEDGAMES)\n#define CACHEGAME_VERSION_DEFAULT CACHEGAME_VERSION_VERBOSE\n\nextern cvar_t skill;\nextern cvar_t deathmatch;\nextern cvar_t coop;\nextern cvar_t teamplay;\nextern cvar_t pr_enable_profiling;\n\ncvar_t sv_savefmt = CVARFD(\"sv_savefmt\", \"\", CVAR_SAVE, \"Specifies the format used for the saved game.\\n0=legacy.\\n1=fte\\n2=binary\");\ncvar_t sv_autosave = CVARFD(\"sv_autosave\", \"5\", CVAR_SAVE, \"Interval for autosaves, in minutes. Set to 0 to disable autosave.\");\nextern cvar_t pr_ssqc_memsize;\n\nvoid SV_Savegame_f (void);\n\n\ntypedef struct\n{\n\tchar name[32];\n\tunion\n\t{\n\t\tint i;\n\t\tfloat f;\n\t} parm[NUM_SPAWN_PARMS];\n\tchar *parmstr;\n\n\tclient_t *source;\n} loadplayer_t;\n\nstruct loadinfo_s\n{\n\tsize_t numplayers;\n\tloadplayer_t *players;\n};\n\n//Writes a SAVEGAME_COMMENT_LENGTH character comment describing the current\nvoid SV_SavegameComment (char *text, size_t textsize)\n{\n\tint\t\ti;\n\tchar\tkills[20];\n\tchar\tdatetime[64];\n\ttime_t\ttimeval;\n\n\tchar *mapname = sv.mapname;\n\n\tfor (i=0 ; i<textsize-1 ; i++)\n\t\ttext[i] = ' ';\n\ttext[textsize-1] = '\\0';\n\tif (!mapname)\n\t\tstrcpy( text, \"Unnamed_Level\");\n\telse\n\t{\n\t\ti = strlen(mapname);\n\t\tif (i > 22)\n\t\t\ti = 22;\n\t\tmemcpy (text, mapname, i);\n\t}\n\n\tkills[0] = '\\0';\n#ifdef Q2SERVER\n\tif (ge)\t//q2\n\t{\n\t\tkills[0] = '\\0';\n\t}\n\telse\n#endif\n#ifdef HLSERVER\n\tif (svs.gametype == GT_HALFLIFE)\n\t{\n\t\tsprintf (kills,\"\");\n\t}\n\telse\n#endif\n\t{\n\t\tif ((int)pr_global_struct->killed_monsters || (int)pr_global_struct->total_monsters)\n\t\t\tsprintf (kills,\"kills:%3i/%3i\", (int)pr_global_struct->killed_monsters, (int)pr_global_struct->total_monsters);\n\t}\n\n\ttime(&timeval);\n\tstrftime(datetime, sizeof(datetime), \"%Y-%m-%d %H:%M:%S\", localtime( &timeval ));\n\n\tmemcpy (text+22, kills, strlen(kills));\n\tif (textsize > 39)\n\t{\n\t\tQ_strncpyz(text+39, datetime, textsize-39);\n\t}\n// convert space to _ to make stdio happy\n\tfor (i=0 ; i<textsize-1 ; i++)\n\t{\n\t\tif (text[i] == ' ')\n\t\t\ttext[i] = '_';\n\t\telse if (text[i] == '\\n')\n\t\t\ttext[i] = '\\0';\n\t}\n\ttext[textsize-1] = '\\0';\n}\n\npbool PDECL SV_ExtendedSaveData(pubprogfuncs_t *progfuncs, void *loadctx, const char **ptr)\n{\n\tstruct loadinfo_s *loadinfo = loadctx;\n\tchar token[65536];\n\tcom_tokentype_t tt;\n\tconst char *l = *ptr;\n\tsize_t idx;\n\tif (l[0] == 's' && l[1] == 'v' && l[2] == '.')\n\t\tl += 3;\t//DPism\n\n\tdo\n\t{\n\t\tl = COM_ParseTokenOut(l, NULL, token, sizeof(token), &tt);\n\t} while(tt == TTP_LINEENDING);\n\tif (tt != TTP_RAWTOKEN)return false;\n\n\tif (!strcmp(token, \"lightstyle\") || !strcmp(token, \"lightstyles\"))\n\t{\t//lightstyle N \"STYLESTRING\" 1 1 1\n\t\tl = COM_ParseTokenOut(l, NULL, token, sizeof(token), &tt);if (tt != TTP_RAWTOKEN)return false;\n\t\tidx = atoi(token);\n\t\tif (idx >= sv.maxlightstyles)\n\t\t{\n\t\t\tif (idx >= MAX_NET_LIGHTSTYLES)\n\t\t\t\treturn false; //unsupported index.\n\t\t\tZ_ReallocElements((void**)&sv.lightstyles, &sv.maxlightstyles, idx+1, sizeof(*sv.lightstyles));\n\t\t}\n\t\tl = COM_ParseTokenOut(l, NULL, token, sizeof(token), &tt);if (tt != TTP_STRING)return false;\n\n\t\tif (sv.lightstyles[idx].str)\n\t\t\tZ_Free((char*)sv.lightstyles[idx].str);\n\t\tsv.lightstyles[idx].str = Z_StrDup(token);\n\t\tsv.lightstyles[idx].colours[0] = sv.lightstyles[idx].colours[1] = sv.lightstyles[idx].colours[2] = 1.0;\n\t\tl = COM_ParseTokenOut(l, NULL, token, sizeof(token), &tt);if (tt != TTP_RAWTOKEN)return false;\n\t\tsv.lightstyles[idx].colours[0] = atof(token);\n\t\tl = COM_ParseTokenOut(l, NULL, token, sizeof(token), &tt);if (tt != TTP_RAWTOKEN)return false;\n\t\tsv.lightstyles[idx].colours[1] = atof(token);\n\t\tl = COM_ParseTokenOut(l, NULL, token, sizeof(token), &tt);if (tt != TTP_RAWTOKEN)return false;\n\t\tsv.lightstyles[idx].colours[2] = atof(token);\n\t}\n\telse if (!strcmp(token, \"model_precache\") || !strcmp(token, \"model\"))\n\t{\t//model_precache N \"MODELNAME\"\n\t\tl = COM_ParseTokenOut(l, NULL, token, sizeof(token), &tt);if (tt != TTP_RAWTOKEN)return false;\n\t\tidx = atoi(token);\n\t\tif (!idx || idx >= countof(sv.strings.model_precache))\n\t\t\treturn false;\t//unsupported index.\n\t\tl = COM_ParseTokenOut(l, NULL, token, sizeof(token), &tt);if (tt != TTP_STRING)return false;\n\t\tsv.strings.model_precache[idx] = PR_AddString(svprogfuncs, token, 0, false);\n\t}\n#ifdef HAVE_LEGACY\n\telse if (!strcmp(token, \"vwep\"))\n\t{\t//vwep N \"MODELNAME\"\n\t\t//0 IS valid, and frankly this stuff sucks.\n\t\tl = COM_ParseTokenOut(l, NULL, token, sizeof(token), &tt);if (tt != TTP_RAWTOKEN)return false;\n\t\tidx = atoi(token);\n\t\tif (idx >= countof(sv.strings.vw_model_precache))\n\t\t\treturn false;\t//unsupported index.\n\t\tl = COM_ParseTokenOut(l, NULL, token, sizeof(token), &tt);if (tt != TTP_STRING)return false;\n\t\tsv.strings.vw_model_precache[idx] = PR_AddString(svprogfuncs, token, 0, false);\n\t}\n#endif\n\telse if (!strcmp(token, \"sound_precache\") || !strcmp(token, \"sound\"))\n\t{\t//sound_precache N \"MODELNAME\"\n\t\tl = COM_ParseTokenOut(l, NULL, token, sizeof(token), &tt);if (tt != TTP_RAWTOKEN)return false;\n\t\tidx = atoi(token);\n\t\tif (!idx || idx >= countof(sv.strings.sound_precache))\n\t\t\treturn false;\t//unsupported index.\n\t\tl = COM_ParseTokenOut(l, NULL, token, sizeof(token), &tt);if (tt != TTP_STRING)return false;\n\t\tsv.strings.sound_precache[idx] = PR_AddString(svprogfuncs, token, 0, false);\n\t}\n\telse if (!strcmp(token, \"particle_precache\") || !strcmp(token, \"particle\"))\n\t{\t//particle_precache N \"MODELNAME\"\n\t\tl = COM_ParseTokenOut(l, NULL, token, sizeof(token), &tt);if (tt != TTP_RAWTOKEN)return false;\n\t\tidx = atoi(token);\n\t\tif (!idx || idx >= countof(sv.strings.particle_precache))\n\t\t\treturn false;\t//unsupported index.\n\t\tl = COM_ParseTokenOut(l, NULL, token, sizeof(token), &tt);if (tt != TTP_STRING)return false;\n\t\tsv.strings.particle_precache[idx] = PR_AddString(svprogfuncs, token, 0, false);\n\t}\n\telse if (!strcmp(token, \"serverflags\"))\n\t{\t//serverflags N  (for map_restart to work properly)\n\t\tl = COM_ParseTokenOut(l, NULL, token, sizeof(token), &tt);if (tt != TTP_RAWTOKEN)return false;\n\t\tidx = atoi(token);\n\t\tsvs.serverflags = idx;\n\t}\n\telse if (!strcmp(token, \"startspot\"))\n\t{\t//startspot \"foo\"  (for map_restart to work properly)\n\t\tl = COM_ParseTokenOut(l, NULL, token, sizeof(token), &tt);if (tt != TTP_RAWTOKEN)return false;\n\t\tInfoBuf_SetStarKey(&svs.info, \"*startspot\", token);\n\t}\n\telse if (loadinfo && !strcmp(token, \"spawnparm\"))\n\t{\t//spawnparm idx val  (for map_restart to work properly)\n\t\tl = COM_ParseTokenOut(l, NULL, token, sizeof(token), &tt);if (tt != TTP_RAWTOKEN)return false;\n\t\tidx = atoi(token);\n\t\tif (idx == 0)\n\t\t{\t//the parmstr...\n\t\t\tl = COM_ParseTokenOut(l, NULL, token, sizeof(token), &tt);if (tt != TTP_STRING)return false;\n\t\t\tloadinfo->players[0].parmstr = Z_StrDup(token);\n\t\t}\n\t\telse if (idx >= 1 && idx <= countof(loadinfo->players->parm))\n\t\t{\t//regular parm\n\t\t\tl = COM_ParseTokenOut(l, NULL, token, sizeof(token), &tt);if (tt != TTP_RAWTOKEN)return false;\n\t\t\tloadinfo->players[0].parm[idx-1].f = atof(token);\n\t\t}\n\t}\n\t//strbuffer+hashtable+etc junk\n\telse if (PR_Common_LoadGame(svprogfuncs, token, &l))\n\t\t;\n\telse\n\t\treturn false;\n\t*ptr = l;\n\treturn true;\n}\n\n#ifndef QUAKETC\nstatic qboolean SV_LegacySavegame (const char *savename, qboolean verbose)\n{\n\tsize_t len;\n\tchar *s = NULL;\n\tclient_t *cl;\n\tint clnum;\n\n\tint version = SAVEGAME_VERSION_FTE_LEG;\n\n\tchar\tnative[MAX_OSPATH];\n\tchar\tname[MAX_QPATH];\n\tvfsfile_t\t*f;\n\tint\t\ti;\n\tchar\tcomment[SAVEGAME_COMMENT_LENGTH+1];\n\n\tif (sv.state != ss_active)\n\t{\n\t\tif (verbose)\n\t\t\tCon_TPrintf(\"Can't apply: Server isn't running or is still loading\\n\");\n\t\treturn false;\n\t}\n\n\tif (sv.allocated_client_slots != 1 || svs.clients->state != cs_spawned)\n\t{\n\t\t//we don't care about fte-format legacy.\n\t\tif (verbose)\n\t\t\tCon_TPrintf(\"Unable to use legacy savegame format to save multiplayer games\\n\");\n\t\treturn false;\n\t}\n\n\tsprintf (name, \"%s\", savename);\n\tCOM_RequireExtension (name, \".sav\", sizeof(name));\t//do NOT allow .pak etc\n\tif (!FS_DisplayPath(name, FS_GAMEONLY, native, sizeof(native)))\n\t\treturn false;\n\tCon_TPrintf (U8(\"Saving game to %s...\\n\"), native);\n\tf = FS_OpenVFS(name, \"wbp\", FS_GAMEONLY);\n\tif (!f)\n\t{\n\t\tif (verbose)\n\t\t\tCon_TPrintf (\"ERROR: couldn't open %s.\\n\", name);\n\t\treturn false;\n\t}\n\n\t//if there are 1 of 1 players connected\n\tif (sv.allocated_client_slots == 1 && svs.clients->state == cs_spawned)\n\t{//try to go for nq/zq compatability as this is a single player game.\n\t\ts = PR_SaveEnts(svprogfuncs, NULL, &len, 0, 2);\t//get the entity state now, so that we know if we can get the full state in a q1 format.\n\t\tif (s)\n\t\t{\n\t\t\tif (progstype == PROG_QW)\n\t\t\t\tversion = SAVEGAME_VERSION_QW;\n\t\t\telse\n\t\t\t\tversion = SAVEGAME_VERSION_NQ;\n\t\t}\n\t}\n\n\n\tVFS_PRINTF(f, \"%i\\n\", version);\n\tSV_SavegameComment (comment, sizeof(comment));\n\tVFS_PRINTF(f, \"%s\\n\", comment);\n\n\tif (version == SAVEGAME_VERSION_NQ || version == SAVEGAME_VERSION_QW)\n\t{\n\t\t//only 16 spawn parms.\n\t\tfor (i=0; i < 16; i++)\n\t\t\tVFS_PRINTF(f, \"%f\\n\", svs.clients->spawn_parms[i]);\t//client 1.\n\t\tVFS_PRINTF(f, \"%f\\n\", skill.value);\n\t}\n\telse\n\t{\n\t\tVFS_PRINTF(f, \"%i\\n\", sv.allocated_client_slots);\n\t\tfor (cl = svs.clients, clnum=0; clnum < sv.allocated_client_slots; cl++,clnum++)\n\t\t{\n\t\t\tif (cl->state < cs_loadzombie || !cl->spawned)\t//don't save if they are still connecting\n\t\t\t{\n\t\t\t\tVFS_PRINTF(f, \"\\\"\\\"\\n\");\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tVFS_PRINTF(f, \"\\\"%s\\\"\\n\", cl->name);\n\t\t\tfor (i=0; i<NUM_SPAWN_PARMS ; i++)\n\t\t\t\tVFS_PRINTF(f, \"%f\\n\", cl->spawn_parms[i]);\n\t\t}\n\t\tVFS_PRINTF(f, \"%i\\n\", progstype);\n\t\tVFS_PRINTF(f, \"%f\\n\", skill.value);\n\t\tVFS_PRINTF(f, \"%f\\n\", deathmatch.value);\n\t\tVFS_PRINTF(f, \"%f\\n\", coop.value);\n\t\tVFS_PRINTF(f, \"%f\\n\", teamplay.value);\n\t}\n\tVFS_PRINTF(f, \"%s\\n\", svs.name);\n\tVFS_PRINTF(f, \"%f\\n\",sv.time);\n\n// write the light styles (only 64 are saved in legacy saved games)\n\tfor (i=0 ; i < 64; i++)\n\t{\n\t\tif (i < sv.maxlightstyles && sv.lightstyles[i].str && *sv.lightstyles[i].str)\n\t\t\tVFS_PRINTF(f, \"%s\\n\", sv.lightstyles[i].str);\n\t\telse\n\t\t\tVFS_PRINTF(f,\"m\\n\");\n\t}\n\n\tif (!s)\n\t\ts = PR_SaveEnts(svprogfuncs, NULL, &len, 0, 1);\n\tVFS_PUTS(f, s);\n\tVFS_PUTS(f, \"\\n\");\n\n#if 1\n\t/* Extended save info\n\t** This should also be compatible with both DP and QSS.\n\t** WARNING: this does NOT protect against models/sounds being precached in different/random orders (statics/baselines/ambients will be wrong).\n\t**          the only protection we get is from late precaches.\n\t**          theoretically the loader could make it work by rewriting the various tables, but that would not necessarily be reliable.\n\t*/\n\tVFS_PUTS(f, \"/*\\n\");\n\tVFS_PUTS(f, \"// FTE extended savegame\\n\");\n\tfor (i=0 ; i < sv.maxlightstyles; i++)\n\t{\t//yes, repeat styles 0-63 again, but only list ones that are not empty.\n\t\tif (sv.lightstyles[i].str)\n\t\t\tVFS_PRINTF(f, \"sv.lightstyles %i %s\\n\", i, sv.lightstyles[i].str);\n\t}\n\tfor (i=1 ; i < countof(sv.strings.model_precache); i++)\n\t{\n\t\tif (sv.strings.model_precache[i])\n\t\t\tVFS_PRINTF(f, \"sv.model_precache %i %s\\n\", i, sv.strings.model_precache[i]);\n\t}\n\tfor (i=1 ; i < countof(sv.strings.sound_precache); i++)\n\t{\n\t\tif (sv.strings.sound_precache[i])\n\t\t\tVFS_PRINTF(f, \"sv.sound_precache %i %s\\n\", i, sv.strings.sound_precache[i]);\n\t}\n\tfor (i=1 ; i < countof(sv.strings.particle_precache); i++)\n\t{\n\t\tif (sv.strings.particle_precache[i])\n\t\t\tVFS_PRINTF(f, \"sv.particle_precache %i %s\\n\", i, sv.strings.particle_precache[i]);\n\t}\n\tVFS_PRINTF(f, \"sv.serverflags %i\\n\", svs.serverflags);\t//zomg! a fix for losing runes on load;restart!\n//\tVFS_PRINTF(f, \"sv.startspot %s\\n\", InfoBuf_ValueForKey(&svs.info, \"*startspot\")); //startspot, for restarts.\n\tif (svs.clients->spawn_parmstring)\n\t{\n\t\tsize_t maxlen = strlen(svs.clients->spawn_parmstring)*2+4 + 1;\n\t\tchar *buffer = BZ_Malloc(maxlen);\n\t\tVFS_PRINTF(f, \"spawnparm 0 %s\\n\", COM_QuotedString(svs.clients->spawn_parmstring, buffer, sizeof(maxlen), false));\n\t\tBZ_Free(buffer);\n\t}\n\tif (version == SAVEGAME_VERSION_NQ || version == SAVEGAME_VERSION_QW)\n\t\tfor (i=16 ; i < countof(svs.clients->spawn_parms); i++)\n\t\t\tVFS_PRINTF(f, \"spawnparm %i %g\\n\", i+1, svs.clients->spawn_parms[i]);\n//\tsv.buffer %i %i \"string\"\n//\tsv.bufstr %i %i \"%s\"\n\tVFS_PUTS(f, \"*/\\n\");\n#endif\n\tsvprogfuncs->parms->memfree(s);\n\n\tVFS_CLOSE(f);\n\n\tFS_FlushFSHashWritten(name);\n\n\tQ_strncpyz(sv.loadgame_on_restart, savename, sizeof(sv.loadgame_on_restart));\n\treturn true;\n}\n#endif\n\nvoid SV_FlushLevelCache(void)\n{\n\tlevelcache_t *cache;\n\n\twhile(svs.levcache)\n\t{\n\t\tcache = svs.levcache->next;\n\t\tZ_Free(svs.levcache);\n\t\tsvs.levcache = cache;\n\t}\n}\n\nvoid LoadModelsAndSounds(vfsfile_t *f)\n{\n\tchar\tstr[32768];\n\tint i;\n\n\tsv.strings.model_precache[0] = PR_AddString(svprogfuncs, \"\", 0, false);\n\tfor (i=1; i < MAX_PRECACHE_MODELS; i++)\n\t{\n\t\tVFS_GETS(f, str, sizeof(str));\n\t\tif (!*str)\n\t\t\tbreak;\n\n\t\tsv.strings.model_precache[i] = PR_AddString(svprogfuncs, str, 0, false);\n\t}\n\tif (i == MAX_PRECACHE_MODELS)\n\t{\n\t\tVFS_GETS(f, str, sizeof(str));\n\t\tif (*str)\n\t\t\tSV_Error(\"Too many model precaches in loadgame cache\");\n\t}\n\tfor (; i < MAX_PRECACHE_MODELS; i++)\n\t\tsv.strings.model_precache[i] = NULL;\n\n\tsv.strings.sound_precache[0] = PR_AddString(svprogfuncs, \"\", 0, false);\n\tfor (i=1; i < MAX_PRECACHE_SOUNDS; i++)\n\t{\n\t\tVFS_GETS(f, str, sizeof(str));\n\t\tif (!*str)\n\t\t\tbreak;\n\n\t\tsv.strings.sound_precache[i] = PR_AddString(svprogfuncs, str, 0, false);\n\t}\n\tif (i == MAX_PRECACHE_SOUNDS)\n\t{\n\t\tVFS_GETS(f, str, sizeof(str));\n\t\tif (*str)\n\t\t\tSV_Error(\"Too many sound precaches in loadgame cache\");\n\t}\n\tfor (; i < MAX_PRECACHE_SOUNDS; i++)\n\t\tsv.strings.sound_precache[i] = NULL;\n}\n\nstatic void PDECL SV_SaveMemoryReset (pubprogfuncs_t *progfuncs, void *ctx)\n{\n\tsize_t i;\n\t//model names are pointers to vm-accessible memory. as that memory is going away, we need to destroy and recreate, which requires preserving them.\n\tfor (i = 1; i < MAX_PRECACHE_MODELS; i++)\n\t{\n\t\tif (!sv.strings.model_precache[i])\n\t\t\tbreak;\n\t\tsv.strings.model_precache[i] = PR_AddString(svprogfuncs, sv.strings.model_precache[i], 0, false);\n\t}\n\tfor (i = 1; i < MAX_PRECACHE_SOUNDS; i++)\n\t{\n\t\tif (!sv.strings.sound_precache[i])\n\t\t\tbreak;\n\t\tsv.strings.sound_precache[i] = PR_AddString(svprogfuncs, sv.strings.sound_precache[i], 0, false);\n\t}\n}\n\n/*ignoreplayers - says to not tell gamecode (a loadgame rather than a level change)*/\nqboolean SV_LoadLevelCache(const char *savename, const char *level, const char *startspot, qboolean isloadgame)\n{\n\teval_t *eval, *e2;\n\n\tchar\tname[MAX_OSPATH];\n\tvfsfile_t\t*f;\n\tchar\tmapname[MAX_QPATH];\n\tfloat\ttime;\n\tchar\tstr[32768];\n\tint\t\ti;\n\tsize_t\tj;\n\tedict_t\t*ent;\n\tint\t\tversion;\n\n\tint current_skill;\n\n\tint pt;\n\n\tint modelpos;\n\n\tqofs_t filelen, filepos;\n\tchar *file;\n\tgametype_e gametype;\n\n\tlevelcache_t *cache;\n\tint numstyles;\n\n\tif (isloadgame)\n\t{\n\t\tgametype = svs.gametype;\n\t}\n\telse\n\t{\n\t\tcache = svs.levcache;\n\t\twhile(cache)\n\t\t{\n\t\t\tif (!strcmp(cache->mapname, level))\n\t\t\t\tbreak;\n\n\t\t\tcache = cache->next;\n\t\t}\n\t\tif (!cache)\n\t\t\treturn false;\t//not visited yet. Ignore the existing caches as fakes.\n\n\t\tgametype = cache->gametype;\n\t}\n\n\tif (savename)\n\t\tQ_snprintfz (name, sizeof(name), \"saves/%s/%s.lvc\", savename, level);\n\telse\n\t\tQ_snprintfz (name, sizeof(name), \"saves/%s.lvc\", level);\n\n//\tCon_TPrintf (\"Loading game from %s...\\n\", name);\n\n#ifdef Q2SERVER\n\tif (gametype == GT_QUAKE2)\n\t{\n\t\tchar *s;\n\t\tflocation_t loc;\n\t\tSV_SpawnServer (level, startspot, false, false, 0);\n\n\t\tWorld_ClearWorld(&sv.world, false);\n\t\tif (!ge)\n\t\t{\n\t\t\tCon_Printf(\"Incorrect gamecode type.\\n\");\n\t\t\treturn false;\n\t\t}\n\n\t\tif (!FS_FLocateFile(name, FSLF_IFFOUND, &loc))\n\t\t{\n\t\t\tCon_Printf(\"Couldn't find %s.\\n\", name);\n\t\t\treturn false;\n\t\t}\n\t\tif (!*loc.rawname || loc.offset)\n\t\t{\n\t\t\tCon_Printf(\"%s is inside a package and cannot be used by the quake2 gamecode.\\n\", name);\n\t\t\treturn false;\n\t\t}\n\n\t\tif (savename)\n\t\t\tQ_snprintfz (name, sizeof(name), \"saves/%s/%s.lvx\", savename, level);\n\t\telse\n\t\t\tQ_snprintfz (name, sizeof(name), \"saves/%s.lvx\", level);\n\t\tfile = FS_MallocFile(name, FS_GAME, &filelen);\n\t\tif (file)\n\t\t{\n\t\t\ts = file;\n\t\t\t//Read config strings\n\t\t\tfor (i = 0; i < countof(sv.strings.configstring) && s < file+filelen; i++)\n\t\t\t{\n\t\t\t\tZ_Free((char*)sv.strings.configstring[i]);\n\t\t\t\tsv.strings.configstring[i] = Z_StrDup(s);\n\t\t\t\ts += strlen(s)+1;\n\t\t\t}\n\t\t\tfor (i = 0; s < file+filelen; i++)\n\t\t\t{\n\t\t\t\tif (!*s)\n\t\t\t\t\tbreak;\n\t\t\t\tif (i < countof(sv.strings.q2_extramodels))\n\t\t\t\t{\n\t\t\t\t\tZ_Free((char*)sv.strings.q2_extramodels[i]);\n\t\t\t\t\tsv.strings.q2_extramodels[i] = Z_StrDup(s);\n\t\t\t\t}\n\t\t\t\ts += strlen(s)+1;\n\t\t\t}\n\t\t\tfor (; i < countof(sv.strings.q2_extramodels); i++)\n\t\t\t{\n\t\t\t\tZ_Free((char*)sv.strings.q2_extramodels[i]);\n\t\t\t\tsv.strings.q2_extramodels[i] = NULL;\n\t\t\t}\n\t\t\tfor (i = 0; s < file+filelen; i++)\n\t\t\t{\n\t\t\t\tif (!*s)\n\t\t\t\t\tbreak;\n\t\t\t\tif (i < countof(sv.strings.q2_extrasounds))\n\t\t\t\t{\n\t\t\t\t\tZ_Free((char*)sv.strings.q2_extrasounds[i]);\n\t\t\t\t\tsv.strings.q2_extrasounds[i] = Z_StrDup(s);\n\t\t\t\t}\n\t\t\t\ts += strlen(s)+1;\n\t\t\t}\n\t\t\tfor (; i < countof(sv.strings.q2_extrasounds); i++)\n\t\t\t{\n\t\t\t\tZ_Free((char*)sv.strings.q2_extrasounds[i]);\n\t\t\t\tsv.strings.q2_extrasounds[i] = NULL;\n\t\t\t}\n\t\t\t//Read portal state\n\t\t\tsv.world.worldmodel->funcs.LoadAreaPortalBlob(sv.world.worldmodel, s, (file+filelen)-s);\n\t\t\tFS_FreeFile(file);\n\t\t}\n\n\t\tge->ReadLevel(loc.rawname);\n\n\t\tfor (i=0 ; i<100 ; i++)\t//run for 10 secs to iron out a few bugs.\n\t\t\tge->RunFrame ();\n\t\treturn true;\n\t}\n#endif\n\n// we can't call SCR_BeginLoadingPlaque, because too much stack space has\n// been used.  The menu calls it before stuffing loadgame command\n//\tSCR_BeginLoadingPlaque ();\n\n\tf = FS_OpenVFS(name, \"rb\", FS_GAME);\n\tif (!f)\n\t{\n\t\tif (isloadgame)\n\t\t\tCon_Printf (\"ERROR: Couldn't load \\\"%s\\\"\\n\", name);\n\t\treturn false;\n\t}\n\n\tVFS_GETS(f, str, sizeof(str));\n\tversion = atoi(str);\n\tif (version != CACHEGAME_VERSION_OLD && version != CACHEGAME_VERSION_VERBOSE && version != CACHEGAME_VERSION_MODSAVED)\n\t{\n\t\tVFS_CLOSE (f);\n\t\tCon_TPrintf (\"Savegame is version %i, not %i\\n\", version, CACHEGAME_VERSION_DEFAULT);\n\t\treturn false;\n\t}\n\tVFS_GETS(f, str, sizeof(str));\t//comment\n\n\tSV_SendMessagesToAll();\n\n\tif (version == CACHEGAME_VERSION_OLD)\n\t{\n\t\tVFS_GETS(f, str, sizeof(str));\n\t\tpt = atof(str);\n\n\t// this silliness is so we can load 1.06 save files, which have float skill values\n\t\tVFS_GETS(f, str, sizeof(str));\n\t\tcurrent_skill = (int)(atof(str) + 0.1);\n\t\tCvar_Set (&skill, va(\"%i\", current_skill));\n\n\t\tVFS_GETS(f, str, sizeof(str));\n\t\tCvar_SetValue (&deathmatch, atof(str));\n\t\tVFS_GETS(f, str, sizeof(str));\n\t\tCvar_SetValue (&coop, atof(str));\n\t\tVFS_GETS(f, str, sizeof(str));\n\t\tCvar_SetValue (&teamplay, atof(str));\n\n\t\tVFS_GETS(f, mapname, sizeof(mapname));\n\t\tVFS_GETS(f, str, sizeof(str));\n\t\ttime = atof(str);\n\t}\n\telse\n\t{\n\t\ttime = 0;\n\t\tpt = PROG_UNKNOWN;\n\t\twhile (VFS_GETS(f, str, sizeof(str)))\n\t\t{\n\t\t\tchar *s = str;\n\t\t\tcvar_t *var;\n\t\t\ts = COM_Parse(s);\n\t\t\tif (!strcmp(com_token, \"map\"))\n\t\t\t{\t//map \"foo\": terminates the preamble.\n\t\t\t\tCOM_ParseOut(s, mapname, sizeof(mapname));\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\telse if (!strcmp(com_token, \"cvar\"))\n\t\t\t{\n\t\t\t\ts = COM_Parse(s);\n\t\t\t\tvar = Cvar_FindVar(com_token);\n\t\t\t\ts = COM_Parse(s);\n\t\t\t\tif (var)\n\t\t\t\t\tCvar_Set(var, com_token);\n\t\t\t}\n\t\t\telse if (!strcmp(com_token, \"time\"))\n\t\t\t{\n\t\t\t\ts = COM_Parse(s);\n\t\t\t\ttime = atof(com_token);\n\t\t\t}\n\t\t\telse if (!strcmp(com_token, \"vmmode\"))\n\t\t\t{\n\t\t\t\ts = COM_Parse(s);\n\t\t\t\tif (!strcmp(com_token, \"NONE\")) pt = PROG_NONE;\n\t\t\t\telse if (!strcmp(com_token, \"QW\")) pt = PROG_QW;\n\t\t\t\telse if (!strcmp(com_token, \"NQ\")) pt = PROG_NQ;\n\t\t\t\telse if (!strcmp(com_token, \"H2\")) pt = PROG_H2;\n\t\t\t\telse if (!strcmp(com_token, \"PREREL\")) pt = PROG_PREREL;\n\t\t\t\telse if (!strcmp(com_token, \"TENEBRAE\")) pt = PROG_TENEBRAE;\n\t\t\t\telse if (!strcmp(com_token, \"UNKNOWN\")) pt = PROG_UNKNOWN;\n\t\t\t\telse pt = PROG_UNKNOWN;\n\t\t\t}\n\t\t\telse\n\t\t\t\tCon_TPrintf (\"Unknown savegame directive %s\\n\", com_token);\n\t\t}\n\t}\n\n\t//NOTE: This sets up the default baselines+statics+ambients.\n\t//FIXME: if any model names changed, then we're screwed.\n\tSV_SpawnServer (mapname, startspot, false, false, svs.allocated_client_slots);\n\tsv.time = time;\n\tif (svs.gametype != gametype)\n\t{\n\t\tVFS_CLOSE (f);\n\t\tCon_Printf(\"Incorrect gamecode type. Cannot load game.\\n\");\n\t\treturn false;\n\t}\n\tif (sv.state != ss_active)\n\t{\n\t\tVFS_CLOSE (f);\n\t\tCon_TPrintf (\"Couldn't load map\\n\");\n\t\treturn false;\n\t}\n\n\tif (version == CACHEGAME_VERSION_MODSAVED)\n\t{\n\t\tconst char *line;\n\t\tvoid *pr_globals = PR_globals(svprogfuncs, PR_CURRENT);\n\t\tfunc_t restorefunc = PR_FindFunction(svprogfuncs, \"SV_PerformLoad\", PR_ANY);\n\t\tcom_tokentype_t tt;\n\t\tif (!restorefunc)\n\t\t{\n\t\t\tVFS_CLOSE (f);\n\t\t\tCon_TPrintf (\"SV_PerformLoad missing, unable to load game\\n\");\n\t\t\treturn false;\n\t\t}\n\n\t\t//shove it into a buffer to make the next bit easier.\n\t\tfilepos = VFS_TELL(f);\n\t\tfilelen = VFS_GETLEN(f);\n\t\tfilelen -= filepos;\n\t\tfile = BZ_Malloc(filelen+1);\n\t\tmemset(file, 0, filelen+1);\n\t\tfilelen = VFS_READ(f, file, filelen);\n\t\tVFS_CLOSE (f);\n\t\tif ((int)filelen < 0)\n\t\t\tfilelen = 0;\n\t\tfile[filelen]='\\0';\n\n\t\t//look for possible engine commands followed by a 'moddata' line (with no args)\n\t\tline = file;\n\t\twhile(line && *line)\n\t\t{\n\t\t\tif (SV_ExtendedSaveData(svprogfuncs, NULL, &line))\n\t\t\t\tcontinue;\n\t\t\tline = COM_ParseTokenOut(line, NULL, com_token, sizeof(com_token), &tt);\n\t\t\tif (!strcmp(com_token, \"moddata\"))\n\t\t\t{\n\t\t\t\t//loop till end of line\n\t\t\t\twhile (line && tt != TTP_LINEENDING)\n\t\t\t\t\tline = COM_ParseTokenOut(line, NULL, com_token, sizeof(com_token), &tt);\n\t\t\t\tbreak;\t//terminates the postamble\n\t\t\t}\n\n\t\t\t//loop till end of line\n\t\t\twhile (line && tt != TTP_LINEENDING)\n\t\t\t\tline = COM_ParseTokenOut(line, NULL, com_token, sizeof(com_token), &tt);\n\t\t}\n\t\tif (!line)\n\t\t{\n\t\t\tBZ_Free(file);\n\t\t\tCon_TPrintf (\"unsupported saved game\\n\");\n\t\t\treturn false;\n\t\t}\n\n//\t\tfor(i = sv.allocated_client_slots+1; i < sv.world.num_edicts; i++)\n//\t\t\tsvprogfuncs->EntFree (svprogfuncs, EDICT_NUM_PB(svprogfuncs, i), false);\n\n\t\t//and now we can stomp on everything. yay.\n\t\tsv.world.edicts[0].readonly = false;\n\t\tG_FLOAT(OFS_PARM0) = PR_QCFile_From_Buffer(svprogfuncs, name, file, line-file, filelen);\n\t\tG_FLOAT(OFS_PARM1) = sv.world.num_edicts;\n\t\tG_FLOAT(OFS_PARM2) = sv.allocated_client_slots;\n\t\tPR_ExecuteProgram(svprogfuncs, restorefunc);\n\t\tsv.world.edicts[0].readonly = true;\n\n\t\t//in case they forgot to setorigin everything after blindly loading its fields...\n\t\tWorld_ClearWorld (&sv.world, true);\n\n\t\t//let time jump to match whatever time the mod wanted.\n\t\tsv.time = sv.world.physicstime = pr_global_struct->time = time;\n\t\tsv.starttime = Sys_DoubleTime() - sv.time;\n\t\treturn true;\n\t}\n\n// load the edicts out of the savegame file\n// the rest of the file is sent directly to the progs engine.\n\n\t/*hexen2's gamecode doesn't have SAVE set on all variables, in which case we must clobber them, and run the risk that they were set at map load time, but clear in the savegame.*/\n\tif (progstype != PROG_H2)\n\t{\n\t\tQ_SetProgsParms(false);\n\t\tPR_Configure(svprogfuncs, PR_ReadBytesString(pr_ssqc_memsize.string), MAX_PROGS, pr_enable_profiling.ival);\n\t\tPR_RegisterFields();\n\t\tPR_InitEnts(svprogfuncs, sv.world.max_edicts);\n\t}\n\n\tif (version == CACHEGAME_VERSION_OLD)\n\t{\n\t\t// load the light styles\n\t\tVFS_GETS(f, str, sizeof(str));\n\t\tnumstyles = atoi(str);\n\t\tif (numstyles > MAX_NET_LIGHTSTYLES)\n\t\t{\n\t\t\tVFS_CLOSE (f);\n\t\t\tCon_Printf (\"load failed - invalid number of lightstyles\\n\");\n\t\t\treturn false;\n\t\t}\n\t\tfor (i = 0; i<sv.maxlightstyles ; i++)\n\t\t{\n\t\t\tif (sv.lightstyles[i].str)\n\t\t\t\tBZ_Free((void*)sv.lightstyles[i].str);\n\t\t\tsv.lightstyles[i].str = NULL;\n\t\t}\n\t\tZ_ReallocElements((void**)&sv.lightstyles, &sv.maxlightstyles, numstyles, sizeof(*sv.lightstyles));\n\n\t\tfor (i=0 ; i<numstyles ; i++)\n\t\t{\n\t\t\tVFS_GETS(f, str, sizeof(str));\n\t\t\tsv.lightstyles[i].str = Z_StrDup(str);\n\t\t}\n\t\tfor ( ; i<sv.maxlightstyles ; i++)\n\t\t{\n\t\t\tsv.lightstyles[i].str = Z_StrDup(\"\");\n\t\t}\n\n\t\tmodelpos = VFS_TELL(f);\n\t\tLoadModelsAndSounds(f);\n\t}\n\telse\n\t\tmodelpos = 0;\n\n\tfilepos = VFS_TELL(f);\n\tfilelen = VFS_GETLEN(f);\n\tfilelen -= filepos;\n\tfile = BZ_Malloc(filelen+1);\n\tmemset(file, 0, filelen+1);\n\tVFS_READ(f, file, filelen);\n\tfile[filelen]='\\0';\n\tsv.world.edict_size=svprogfuncs->load_ents(svprogfuncs, file, NULL, SV_SaveMemoryReset, NULL, SV_ExtendedSaveData);\n\tBZ_Free(file);\n\n\tprogstype = pt;\n\n\tPR_LoadGlabalStruct(false);\n\n\tpr_global_struct->time = sv.time = sv.world.physicstime = time;\n\tsv.starttime = Sys_DoubleTime() - sv.time;\n\n\tif (modelpos != 0)\n\t{\n\t\tVFS_SEEK(f, modelpos);\n\t\tLoadModelsAndSounds(f);\n\t}\n\n\tVFS_CLOSE(f);\n\n\tPF_InitTempStrings(svprogfuncs);\n\n\tWorld_ClearWorld (&sv.world, true);\n\n\tfor (i=0 ; i<svs.allocated_client_slots ; i++)\n\t{\n\t\tif (i < sv.allocated_client_slots)\n\t\t\tent = EDICT_NUM_PB(svprogfuncs, i+1);\n\t\telse\n\t\t\tent = NULL;\n\t\tsvs.clients[i].edict = ent;\n\t\tent->ereftype = ER_ENTITY;\t//should have already been allocated.\n\n\t\tsvs.clients[i].name = PR_AddString(svprogfuncs, svs.clients[i].namebuf, sizeof(svs.clients[i].namebuf), false);\n\t\tsvs.clients[i].team = PR_AddString(svprogfuncs, svs.clients[i].teambuf, sizeof(svs.clients[i].teambuf), false);\n\n\t\t//svs.clients[i].spawned = (svs.clients[i].state == cs_loadzombie);\n#ifdef HEXEN2\n\t\tif (ent)\n\t\t\tsvs.clients[i].playerclass = ent->xv->playerclass;\n\t\telse\n\t\t\tsvs.clients[i].playerclass = 0;\n#endif\n\t}\n\n\tif (!isloadgame)\n\t{\n\t\teval = PR_FindGlobal(svprogfuncs, \"startspot\", 0, NULL);\n\t\tif (eval) eval->_int = (int)PR_NewString(svprogfuncs, startspot);\n\n\t\teval = PR_FindGlobal(svprogfuncs, \"ClientReEnter\", 0, NULL);\n\t\tif (eval)\n\t\tfor (i=0 ; i<sv.allocated_client_slots ; i++)\n\t\t{\n\t\t\tif (svs.clients[i].spawninfo)\n\t\t\t{\n\t\t\t\tglobalvars_t *pr_globals = PR_globals(svprogfuncs, PR_CURRENT);\n\t\t\t\tent = svs.clients[i].edict;\n\t\t\t\tj = strlen(svs.clients[i].spawninfo);\n\t\t\t\tsvprogfuncs->restoreent(svprogfuncs, svs.clients[i].spawninfo, &j, ent);\n\n\t\t\t\te2 = svprogfuncs->GetEdictFieldValue(svprogfuncs, ent, \"stats_restored\", ev_float, NULL);\n\t\t\t\tif (e2)\n\t\t\t\t\te2->_float = 1;\n\t\t\t\tSV_SpawnParmsToQC(host_client);\n\t\t\t\tpr_global_struct->time = sv.world.physicstime;\n\t\t\t\tpr_global_struct->self = EDICT_TO_PROG(svprogfuncs, ent);\n\t\t\t\tWorld_UnlinkEdict((wedict_t*)ent);\n\t\t\t\tG_FLOAT(OFS_PARM0) = sv.time-host_client->spawninfotime;\n\t\t\t\tPR_ExecuteProgram(svprogfuncs, eval->function);\n\n//\t\t\t\tif (svs.clients[i].state == cs_loadzombie)\n//\t\t\t\t\tsvs.clients[i].istobeloaded = 1;\n//\t\t\t\telse\n//\t\t\t\t\tsvs.clients[i].state = cs_spawned;\t//don't do a separate ClientConnect.\n\t\t\t}\n\t\t}\n\t\tpr_global_struct->serverflags = svs.serverflags;\n\t}\n\n\tpr_global_struct->time = sv.world.physicstime;\n\n\tfor (i=0 ; i<sv.world.num_edicts ; i++)\n\t{\n\t\tent = EDICT_NUM_PB(svprogfuncs, i);\n\t\tif (ED_ISFREE(ent))\n\t\t\tcontinue;\n\n\t\t/*hexen2 instead overwrites ents, which can theoretically be unreliable (ents with this flag are not saved in the first place, and thus are effectively reset instead of reloaded).\n\t\t  fte purges all ents beforehand in a desperate attempt to remain sane.\n\t\t  this behaviour does not match exactly, but is enough for vanilla hexen2/POP.\n\t\t*/\n\t\tif ((unsigned int)ent->v->flags & FL_HUBSAVERESET)\n\t\t{\n\t\t\tfunc_t f;\n\t\t\t/*set some minimal fields*/\n\t\t\tent->v->solid = SOLID_NOT;\n\t\t\tent->v->use = 0;\n\t\t\tent->v->touch = 0;\n\t\t\tent->v->think = 0;\n\t\t\tent->v->nextthink = 0;\n\t\t\t/*reinvoke the spawn function*/\n\t\t\tpr_global_struct->time = 0.1;\n\t\t\tpr_global_struct->self = EDICT_TO_PROG(svprogfuncs, ent);\n\t\t\tf = PR_FindFunction(svprogfuncs, PR_GetString(svprogfuncs, ent->v->classname), PR_ANY);\n\n\t\t\tif (f)\n\t\t\t\tPR_ExecuteProgram(svprogfuncs, f);\n\t\t}\n\t}\n\n\treturn true;\t//yay\n}\n\nvoid SV_SaveLevelCache(const char *savedir, qboolean dontharmgame)\n{\n\tsize_t len;\n\tchar *s;\n\tclient_t *cl;\n\tint clnum;\n\n\tchar\tname[256];\n\tvfsfile_t\t*f;\n\tint\t\ti;\n\tchar\tcomment[SAVEGAME_COMMENT_LENGTH+1];\n\tlevelcache_t *cache;\n\tint version = CACHEGAME_VERSION_DEFAULT;\n\tfunc_t func;\n\n\tif (!sv.state)\n\t\treturn;\n\n\tif (!dontharmgame)\n\t{\n\t\tcache = svs.levcache;\n\t\twhile(cache)\n\t\t{\n\t\t\tif (!strcmp(cache->mapname, svs.name))\n\t\t\t\tbreak;\n\n\t\t\tcache = cache->next;\n\t\t}\n\t\tif (!cache)\t//not visited yet. Let us know that we went there.\n\t\t{\n\t\t\tcache = Z_Malloc(sizeof(levelcache_t)+strlen(svs.name)+1);\n\t\t\tcache->mapname = (char *)(cache+1);\n\t\t\tstrcpy(cache->mapname, svs.name);\n\n\t\t\tcache->gametype = svs.gametype;\n\t\t\tcache->next = svs.levcache;\n\t\t\tsvs.levcache = cache;\n\t\t}\n\t}\n\n\tif (savedir)\n\t\tQ_snprintfz (name, sizeof(name), \"saves/%s/%s.lvc\", savedir, svs.name);\n\telse\n\t\tQ_snprintfz (name, sizeof(name), \"saves/%s.lvc\", svs.name);\n\n\tFS_CreatePath(name, FS_GAMEONLY);\n\n\tif (!dontharmgame)\t//save game in progress\n\t\tCon_TPrintf (\"Saving game to %s...\\n\", name);\n\n#ifdef Q2SERVER\n\tif (ge)\n\t{\n\t\tchar\tsyspath[MAX_OSPATH];\n\n\t\tif (!FS_SystemPath(name, FS_GAMEONLY, syspath, sizeof(syspath)))\n\t\t\treturn;\n\t\tge->WriteLevel(syspath);\n\n\t\tif (savedir)\n\t\t\tQ_snprintfz (name, sizeof(name), \"saves/%s/%s.lvx\", savedir, svs.name);\n\t\telse\n\t\t\tQ_snprintfz (name, sizeof(name), \"saves/%s.lvx\", svs.name);\n\t\t//write configstrings\n\t\tf = FS_OpenVFS (name, \"wbp\", FS_GAMEONLY);\n\t\tif (f)\n\t\t{\n\t\t\tsize_t portalblobsize;\n\t\t\tvoid *portalblob = NULL;\n\t\t\tfor (i = 0; i < countof(sv.strings.configstring); i++)\n\t\t\t{\n\t\t\t\tif (sv.strings.configstring[i])\n\t\t\t\t\tVFS_WRITE(f, sv.strings.configstring[i], strlen(sv.strings.configstring[i])+1);\n\t\t\t\telse\n\t\t\t\t\tVFS_WRITE(f, \"\", 1);\n\t\t\t}\n\n\t\t\tfor (i = 0; i < countof(sv.strings.q2_extramodels); i++)\n\t\t\t{\n\t\t\t\tif (!sv.strings.q2_extramodels[i])\n\t\t\t\t\tbreak;\n\t\t\t\tVFS_WRITE(f, sv.strings.q2_extramodels[i], strlen(sv.strings.q2_extramodels[i])+1);\n\t\t\t}\n\t\t\tVFS_WRITE(f, \"\", 1);\n\n\t\t\tfor (i = 0; i < countof(sv.strings.q2_extrasounds); i++)\n\t\t\t{\n\t\t\t\tif (!sv.strings.q2_extrasounds[i])\n\t\t\t\t\tbreak;\n\t\t\t\tVFS_WRITE(f, sv.strings.q2_extrasounds[i], strlen(sv.strings.q2_extrasounds[i])+1);\n\t\t\t}\n\t\t\tVFS_WRITE(f, \"\", 1);\n\n\t\t\tportalblobsize = sv.world.worldmodel->funcs.SaveAreaPortalBlob(sv.world.worldmodel, &portalblob);\n\t\t\tVFS_WRITE(f, portalblob, portalblobsize);\n\n\t\t\tVFS_CLOSE(f);\n\t\t}\n\n\n\t\tFS_FlushFSHashFull();\n\t\treturn;\n\t}\n#endif\n\n#ifdef HLSERVER\n\tif (svs.gametype == GT_HALFLIFE)\n\t{\n\t\tSVHL_SaveLevelCache(name);\n\t\tFS_FlushFSHashFull();\n\t\treturn;\n\t}\n#endif\n\n\tfunc = PR_FindFunction(svprogfuncs, \"SV_PerformSave\", PR_ANY);\n\tif (func)\n\t\tversion = CACHEGAME_VERSION_MODSAVED;\n\n\tf = FS_OpenVFS (name, \"wbp\", FS_GAMEONLY);\n\tif (!f)\n\t{\n\t\tCon_TPrintf (\"ERROR: couldn't open %s.\\n\", name);\n\t\treturn;\n\t}\n\n\tVFS_PRINTF (f, \"%i\\n\", version);\n\tSV_SavegameComment (comment, sizeof(comment));\n\tVFS_PRINTF (f, \"%s\\n\", comment);\n\n\tif (!dontharmgame)\n\t{\n\t\t//map-change caches require the players to have been de-spawned\n\t\t//saved games require players to retain their fields.\n\t\t//probably this should happen elsewhere.\n\t\tfor (cl = svs.clients, clnum=0; clnum < sv.allocated_client_slots; cl++,clnum++)//fake dropping\n\t\t{\n\t\t\tif (cl->state < cs_connected)\n\t\t\t\tcontinue;\n\t\t\telse if (progstype == PROG_H2)\n\t\t\t\tcl->edict->ereftype = ER_FREE;\t//hexen2 has some annoying prints. it never formally dropped clients on map changes (we'll reset this later, so they'll just not appear in the saved game).\n\t\t\telse if (!cl->spawned)\t//don't drop if they are still connecting\n\t\t\t\tcl->edict->v->solid = 0;\n\t\t\telse\n\t\t\t\tSV_DespawnClient(cl);\n\t\t}\n\t}\n\n\tif (version >= CACHEGAME_VERSION_VERBOSE)\n\t{\n\t\tchar buf[8192];\n\t\tchar *mode = \"?\";\n\t\tswitch(progstype)\n\t\t{\n\t\tcase PROG_NONE:\t\t\t\t\t\t\tbreak;\n\t\tcase PROG_QW:\t\tmode = \"QW\";\t\tbreak;\n\t\tcase PROG_NQ:\t\tmode = \"NQ\";\t\tbreak;\n\t\tcase PROG_H2:\t\tmode = \"H2\";\t\tbreak;\n\t\tcase PROG_PREREL:\tmode = \"PREREL\";\tbreak;\n\t\tcase PROG_TENEBRAE: mode = \"TENEBRAE\";\tbreak;\n\t\tcase PROG_UNKNOWN:\tmode = \"UNKNOWN\";\tbreak;\n\t\t}\n\t\tVFS_PRINTF (f, \"vmmode %s\\n\",\t\t\tCOM_QuotedString(mode, buf, sizeof(buf), false));\n\t\tVFS_PRINTF (f, \"cvar skill %s\\n\",\t\tCOM_QuotedString(skill.string, buf, sizeof(buf), false));\n\t\tVFS_PRINTF (f, \"cvar deathmatch %s\\n\",\tCOM_QuotedString(deathmatch.string, buf, sizeof(buf), false));\n\t\tVFS_PRINTF (f, \"cvar coop %s\\n\",\t\tCOM_QuotedString(coop.string, buf, sizeof(buf), false));\n\t\tVFS_PRINTF (f, \"cvar teamplay %s\\n\",\tCOM_QuotedString(teamplay.string, buf, sizeof(buf), false));\n\t\tVFS_PRINTF (f, \"time %f\\n\",\t\t\t\tsv.time);\n\t\tVFS_PRINTF (f, \"map %s\\n\",\t\t\t\tCOM_QuotedString(svs.name, buf, sizeof(buf), false));\n\t}\n\telse\n\t{\n\t\tVFS_PRINTF (f, \"%d\\n\", progstype);\n\t\tVFS_PRINTF (f, \"%f\\n\", skill.value);\n\t\tVFS_PRINTF (f, \"%f\\n\", deathmatch.value);\n\t\tVFS_PRINTF (f, \"%f\\n\", coop.value);\n\t\tVFS_PRINTF (f, \"%f\\n\", teamplay.value);\n\t\tVFS_PRINTF (f, \"%s\\n\", svs.name);\n\t\tVFS_PRINTF (f, \"%f\\n\", sv.time);\n\n// write the light styles\n\t\tVFS_PRINTF (f, \"%u\\n\",(unsigned)sv.maxlightstyles);\n\t\tfor (i=0 ; i<sv.maxlightstyles ; i++)\n\t\t{\n\t\t\tVFS_PRINTF (f, \"%s\\n\", sv.lightstyles[i].str?sv.lightstyles[i].str:\"\");\n\t\t}\n\n\t\tfor (i=1 ; i<MAX_PRECACHE_MODELS ; i++)\n\t\t{\n\t\t\tif (sv.strings.model_precache[i] && *sv.strings.model_precache[i])\n\t\t\t\tVFS_PRINTF (f, \"%s\\n\", sv.strings.model_precache[i]);\n\t\t\telse\n\t\t\t\tbreak;\n\t\t}\n\t\tVFS_PRINTF (f,\"\\n\");\n\t\tfor (i=1 ; i<MAX_PRECACHE_SOUNDS ; i++)\n\t\t{\n\t\t\tif (sv.strings.sound_precache[i] && *sv.strings.sound_precache[i])\n\t\t\t\tVFS_PRINTF (f, \"%s\\n\", sv.strings.sound_precache[i]);\n\t\t\telse\n\t\t\t\tbreak;\n\t\t}\n\t\tVFS_PRINTF (f,\"\\n\");\n\t}\n\n\tif (version == CACHEGAME_VERSION_MODSAVED)\n\t{\n\t\tstruct globalvars_s *pr_globals = PR_globals(svprogfuncs, PR_CURRENT);\n\t\tfunc_t func = PR_FindFunction(svprogfuncs, \"SV_PerformSave\", PR_ANY);\n\n\t\t//FIXME: save precaches here.\n\t\tVFS_PRINTF (f, \"moddata\\n\");\n\t\tG_FLOAT(OFS_PARM0) = PR_QCFile_From_VFS(svprogfuncs, name, f, true);\n\t\tG_FLOAT(OFS_PARM1) = sv.world.num_edicts;\n\t\tG_FLOAT(OFS_PARM2) = sv.allocated_client_slots;\n\t\tPR_ExecuteProgram(svprogfuncs, func);\n\t}\n\telse\n\t{\n\t\t/*if (version >= CACHEGAME_VERSION_BINARY)\n\t\t{\n\t\t\tVFS_PUTS(f, va(\"%i\\n\", svprogfuncs->stringtablesize));\n\t\t\tVFS_WRITE(f, svprogfuncs->stringtable, svprogfuncs->stringtablesize);\n\t\t}\n\t\telse*/\n\t\t{\n\t\t\ts = PR_SaveEnts(svprogfuncs, NULL, &len, 0, 1);\n\t\t\tVFS_PUTS(f, s);\n\t\t\tVFS_PUTS(f, \"\\n\");\n\t\t\tsvprogfuncs->parms->memfree(s);\n\t\t}\n\n\t\tif (version >= CACHEGAME_VERSION_VERBOSE)\n\t\t{\n\t\t\tchar buf[8192];\n\t\t\tfor (i=0 ; i<sv.maxlightstyles ; i++)\n\t\t\t\tif (sv.lightstyles[i].str)\n\t\t\t\t\tVFS_PRINTF (f, \"lightstyle %i %s %f %f %f\\n\", i, COM_QuotedString(sv.lightstyles[i].str, buf, sizeof(buf), false), sv.lightstyles[i].colours[0], sv.lightstyles[i].colours[1], sv.lightstyles[i].colours[2]);\n\t\t\tfor (i=1 ; i<MAX_PRECACHE_MODELS ; i++)\n\t\t\t\tif (sv.strings.model_precache[i] && *sv.strings.model_precache[i])\n\t\t\t\t\tVFS_PRINTF (f, \"model %i %s\\n\", i, COM_QuotedString(sv.strings.model_precache[i], buf, sizeof(buf), false));\n\t\t\tfor (i=1 ; i<MAX_PRECACHE_SOUNDS ; i++)\n\t\t\t\tif (sv.strings.sound_precache[i] && *sv.strings.sound_precache[i])\n\t\t\t\t\tVFS_PRINTF (f, \"sound %i %s\\n\", i, COM_QuotedString(sv.strings.sound_precache[i], buf, sizeof(buf), false));\n\t\t\tfor (i=1 ; i<MAX_SSPARTICLESPRE ; i++)\n\t\t\t\tif (sv.strings.particle_precache[i] && *sv.strings.particle_precache[i])\n\t\t\t\t\tVFS_PRINTF (f, \"particle %i %s\\n\", i, COM_QuotedString(sv.strings.particle_precache[i], buf, sizeof(buf), false));\n#ifdef HAVE_LEGACY\n\t\t\tfor (i = 0; i < sizeof(sv.strings.vw_model_precache)/sizeof(sv.strings.vw_model_precache[0]); i++)\n\t\t\t\tif (sv.strings.vw_model_precache[i])\n\t\t\t\t\tVFS_PRINTF (f, \"vwep %i %s\\n\", i, COM_QuotedString(sv.strings.vw_model_precache[i], buf, sizeof(buf), false));\n#endif\n\n\t\t\tPR_Common_SaveGame(f, svprogfuncs, false);//, version >= CACHEGAME_VERSION_BINARY);\n\n\t\t\t//FIXME: string buffers\n\t\t\t//FIXME: hash tables\n\t\t\t//FIXME: skeletal objects?\n\t\t\t//FIXME: static entities\n\t\t\t//FIXME: midi track\n\t\t\t//FIXME: custom temp-ents?\n\t\t\t//FIXME: pending uri_gets? (if only just to report fails on load)\n\t\t\t//FIXME: routing calls?\n\t\t\t//FIXME: sql queries?\n\t\t\t//FIXME: frik files?\n\t\t\t//FIXME: qc threads?\n\n\t\t\t//\tportalblobsize = CM_WritePortalState(sv.world.worldmodel, &portalblob);\n\t\t\t//\tVFS_WRITE(f, portalblob, portalblobsize);\n\t\t}\n\n\t\tVFS_CLOSE (f);\n\t}\n\n\n\tif (!dontharmgame)\n\t{\n\t\tfor (clnum=0; clnum < sv.allocated_client_slots; clnum++)\n\t\t{\n\t\t\tedict_t *ed = EDICT_NUM_PB(svprogfuncs, clnum+1);\n\t\t\ted->ereftype = ER_ENTITY;\n\t\t}\n\t}\n\n\tFS_FlushFSHashWritten(name);\n}\n\n//mapchange is true for Q2's map-change autosaves.\nvoid SV_Savegame (const char *savename, qboolean mapchange)\n{\n\tclient_t *cl;\n\tint clnum;\n\tchar\tcomment[(SAVEGAME_COMMENT_LENGTH+1)*2];\n\tvfsfile_t *f;\n\tint len;\n\tlevelcache_t *cache;\n\tchar *savefilename;\n\n\tif (!sv.state || sv.state == ss_clustermode)\n\t{\n\t\tCon_Printf(\"Server is not active - unable to save\\n\");\n\t\treturn;\n\t}\n\tif (sv.state == ss_cinematic)\n\t{\n\t\tCon_Printf(\"Server is playing a cinematic - unable to save\\n\");\n\t\treturn;\n\t}\n\n#ifndef QUAKETC\n\t{\n\t\tint savefmt = sv_savefmt.ival;\n\t\tif (!*sv_savefmt.string && (svs.gametype != GT_PROGS || progstype == PROG_H2 || svs.levcache || (progstype == PROG_QW && strcmp(pr_ssqc_progs.string, \"spprogs\")) || (svs.gametype==GT_PROGS && PR_FindFunction(svprogfuncs, \"SV_PerformSave\", PR_ANY))))\n\t\t\tsavefmt = 1;\t//hexen2+q2/etc must not use the legacy format by default. can't use it when using any kind of hub system either (harder to detect upfront, which might give confused saved game naming but will at least work).\n\t\telse\n\t\t\tsavefmt = sv_savefmt.ival;\n\t\tif (!savefmt && !mapchange)\n\t\t{\n\t\t\tif (SV_LegacySavegame(savename, *sv_savefmt.string))\n\t\t\t\treturn;\n\t\t\tif (*sv_savefmt.string)\n\t\t\t\tCon_Printf(\"Unable to use legacy saved game format\\n\");\n\t\t}\n\t}\n#endif\n\n\tswitch(svs.gametype)\n\t{\n\tdefault:\n\tcase GT_Q1QVM:\n#ifdef VM_LUA\n\tcase GT_LUA:\n#endif\n\t\tCon_Printf(\"gamecode doesn't support saving\\n\");\n\t\treturn;\n\tcase GT_PROGS:\n\tcase GT_QUAKE2:\n\t\tbreak;\n\t}\n\n\tif (sv.allocated_client_slots == 1 && svs.gametype == GT_PROGS)\n\t{\n\t\tif (svs.clients->state > cs_connected && svs.clients[0].edict->v->health <= 0)\n\t\t{\n\t\t\tCon_Printf(\"Refusing to save while dead.\\n\");\n\t\t\treturn;\n\t\t}\n\t}\n\t//FIXME: we should probably block saving during intermission too.\n\n\t/*catch invalid names*/\n\tif (!*savename || strstr(savename, \"..\"))\n\t\tsavename = \"quick\";\n\n\tsavefilename = va(\"saves/%s/info.fsv\", savename);\n\tFS_CreatePath(savefilename, FS_GAMEONLY);\n\tf = FS_OpenVFS(savefilename, \"wbp\", FS_GAMEONLY);\n\tif (!f)\n\t{\n\t\tCon_Printf(\"Couldn't open file saves/%s/info.fsv\\n\", savename);\n\t\treturn;\n\t}\n\tSV_SavegameComment(comment, sizeof(comment));\n\tVFS_PRINTF (f, \"%d\\n\", SAVEGAME_VERSION_FTE_HUB+svs.gametype);\n\tVFS_PRINTF (f, \"%s\\n\", comment);\n\n\tVFS_PRINTF(f, \"%i\\n\", sv.allocated_client_slots);\n\tfor (cl = svs.clients, clnum=0; clnum < sv.allocated_client_slots; cl++,clnum++)\n\t{\n\t\t//FIXME: the qc is only told about the new client when the client finally sends a begin.\n\t\t//\t\t this means that if we save a client that is still connecting, the loading code HAS to assume that the qc already knows about the player.\n\t\t//\t\t this means that such players would not be loaded properly anyway, and would bug out the server.\n\t\t//\t\t so its best to not bother saving them at all. pro-top: don't save shortly after a map change in coop/sp.\n\t\t//istobeloaded means that the qc has already been told about the client from a previous saved game, regardless of the fact that they're still technically connecting (this may even be a zombie with no actual client connection).\n\t\t//note that autosave implies that we're saving on a map boundary. this is for q2 gamecode. q1 can't cope.\n\t\tif (cl->state < cs_spawned && !cl->istobeloaded)\t//don't save if they are still connecting\n\t\t{\n\t\t\tVFS_PRINTF(f, \"\\n\");\n\t\t\tcontinue;\n\t\t}\n\t\tVFS_PRINTF(f, \"%s\\n\", cl->name);\n\n\t\tif (*cl->name)\n\t\t{\n\t\t\tchar tmp[65536];\n\t\t\tVFS_PRINTF(f, \"{\\n\");\n\t\t\tfor (len = 0; len < NUM_SPAWN_PARMS; len++)\n\t\t\t\tVFS_PRINTF(f, \"\\tparm%i 0x%x //%.9g\\n\", len, *(int*)&cl->spawn_parms[len], cl->spawn_parms[len]);\t//write hex as not everyone passes a float in the parms.\n\t\t\tVFS_PRINTF(f, \"\\tparm_string %s\\n\", COM_QuotedString(cl->spawn_parmstring?cl->spawn_parmstring:\"\", tmp, sizeof(tmp), false));\n\t\t\t/*if (cl->spawninfo)\n\t\t\t{\n\t\t\t\tVFS_PRINTF(f, \"\\tspawninfo %s\\n\", COM_QuotedString(cl->spawninfo, tmp, sizeof(tmp), false));\n\t\t\t\tVFS_PRINTF(f, \"\\tspawninfotime %9g\\n\", cl->spawninfotime);\n\t\t\t}*/\n\t\t\tVFS_PRINTF(f, \"}\\n\");\t//write ints as not everyone passes a float in the parms.\n\t\t}\n\t}\n\n\tInfoBuf_WriteToFile(f, &svs.info, NULL, 0);\n\tVFS_PUTS(f, \"\\n\");\n\tInfoBuf_WriteToFile(f, &svs.localinfo, NULL, 0);\n\n\tVFS_PRINTF (f, \"\\n{\\n\");\t//all game vars. FIXME: Should save the ones that have been retrieved/set by progs.\n\tVFS_PRINTF (f, \"skill\t\t\t\\\"%s\\\"\\n\",\tskill.string);\n\tVFS_PRINTF (f, \"deathmatch\t\t\\\"%s\\\"\\n\",\tdeathmatch.string);\n\tVFS_PRINTF (f, \"coop\t\t\t\\\"%s\\\"\\n\",\tcoop.string);\n\tVFS_PRINTF (f, \"teamplay\t\t\\\"%s\\\"\\n\",\tteamplay.string);\n\n\tVFS_PRINTF (f, \"nomonsters\t\t\\\"%s\\\"\\n\",\tnomonsters.string);\n\tVFS_PRINTF (f, \"gamecfg\\t\t\t\\\"%s\\\"\\n\",\tgamecfg.string);\n\tVFS_PRINTF (f, \"scratch1\t\t\\\"%s\\\"\\n\",\tscratch1.string);\n\tVFS_PRINTF (f, \"scratch2\t\t\\\"%s\\\"\\n\",\tscratch2.string);\n\tVFS_PRINTF (f, \"scratch3\t\t\\\"%s\\\"\\n\",\tscratch3.string);\n\tVFS_PRINTF (f, \"scratch4\t\t\\\"%s\\\"\\n\",\tscratch4.string);\n\tVFS_PRINTF (f, \"savedgamecfg\\t\t\\\"%s\\\"\\n\",\tsavedgamecfg.string);\n\tVFS_PRINTF (f, \"saved1\t\t\t\\\"%s\\\"\\n\",\tsaved1.string);\n\tVFS_PRINTF (f, \"saved2\t\t\t\\\"%s\\\"\\n\",\tsaved2.string);\n\tVFS_PRINTF (f, \"saved3\t\t\t\\\"%s\\\"\\n\",\tsaved3.string);\n\tVFS_PRINTF (f, \"saved4\t\t\t\\\"%s\\\"\\n\",\tsaved4.string);\n\tVFS_PRINTF (f, \"temp1\t\t\t\\\"%s\\\"\\n\",\ttemp1.string);\n\tVFS_PRINTF (f, \"noexit\t\t\t\\\"%s\\\"\\n\",\tnoexit.string);\n\tVFS_PRINTF (f, \"pr_maxedicts\\t\t\\\"%s\\\"\\n\",\tpr_maxedicts.string);\n\tVFS_PRINTF (f, \"progs\t\t\t\\\"%s\\\"\\n\",\tpr_ssqc_progs.string);\n\tVFS_PRINTF (f, \"set nextserver\t\t\\\"%s\\\"\\n\",\tCvar_Get(\"nextserver\", \"\", 0, \"\")->string);\n\tVFS_PRINTF (f, \"}\\n\");\n\n\tSV_SaveLevelCache(savename, true);\t//add the current level.\n\n\tcache = svs.levcache;\t//state from previous levels - just copy it all accross.\n\tVFS_PRINTF(f, \"{\\n\");\n\twhile(cache)\n\t{\n\t\tVFS_PRINTF(f, \"%s\\n\", cache->mapname);\n\t\tif (strcmp(cache->mapname, svs.name))\n\t\t{\n\t\t\tFS_Copy(va(\"saves/%s.lvc\", cache->mapname), va(\"saves/%s/%s.lvc\", savename, cache->mapname), FS_GAME, FS_GAME);\n\t\t}\n\t\tcache = cache->next;\n\t}\n\tVFS_PRINTF(f, \"}\\n\");\n\n\tVFS_PRINTF (f, \"%s\\n\", svs.name);\n\n\tVFS_PRINTF (f, \"%i\\n\", svs.serverflags);\n\n\tVFS_CLOSE(f);\n\n#ifdef HAVE_CLIENT\n\t//try to save screenshots automagically.\n\tQ_snprintfz(comment, sizeof(comment), \"saves/%s/screeny.%s\", savename, \"tga\");//scr_sshot_type.string);\n\tsavefilename = comment;\n\tFS_Remove(savefilename, FS_GAMEONLY);\n\tif (cls.state == ca_active && qrenderer > QR_NONE && qrenderer != QR_VULKAN/*FIXME*/)\n\t{\n\t\tint stride;\n\t\tint width = vid.pixelwidth;\n\t\tint height = vid.pixelheight;\n\t\timage_t *img;\n\t\tuploadfmt_t format;\n\t\tvoid *rgbbuffer = SCR_ScreenShot_Capture(width, height, &stride, &format, false, false);\n\t\tif (rgbbuffer)\n\t\t{\n//\t\t\textern cvar_t\tscr_sshot_type;\n\t\t\tSCR_ScreenShot(savefilename, FS_GAMEONLY, &rgbbuffer, 1, stride, width, height, format, false);\n\t\t\tBZ_Free(rgbbuffer);\n\n\t\t\t//if any menu code has the shader loaded, we want to avoid them using a cache.\n\t\t\t//hopefully the menu code will unload as it goes, because these screenshots could be truely massive, as they're taken at screen resolution.\n\t\t\t//should probably use a smaller fbo or something, but whatever.\n\t\t\timg = Image_FindTexture(va(\"saves/%s/screeny.%s\", savename, \"tga\"), NULL, 0);\n\t\t\tif (img)\n\t\t\t{\n\t\t\t\tif (Image_UnloadTexture(img))\n\t\t\t\t{\n\t\t\t\t\t//and then reload it so that any shaders using it don't get confused\n\t\t\t\t\tImage_GetTexture(va(\"saves/%s/screeny.%s\", savename, \"tga\"), NULL, 0, NULL, NULL, 0, 0, TF_INVALID);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n#endif\n\n#ifdef Q2SERVER\n\t//save the player's inventory and other map-persistant state that is owned by the gamecode.\n\tif (ge)\n\t{\n\t\tchar syspath[256];\n\t\tif (!FS_SystemPath(va(\"saves/%s/game.gsv\", savename), FS_GAMEONLY, syspath, sizeof(syspath)))\n\t\t\treturn;\n\t\tge->WriteGame(syspath, mapchange);\n\t\tFS_FlushFSHashFull();\n\t}\n\telse\n#endif\n\t{\n\t\t//fixme\n\t\tFS_FlushFSHashFull();\n\t}\n\n\tQ_strncpyz(sv.loadgame_on_restart, savename, sizeof(sv.loadgame_on_restart));\n}\n\n\nstatic int QDECL CompleteSaveList (const char *name, qofs_t flags, time_t mtime, void *parm, searchpathfuncs_t *spath)\n{\n\tstruct xcommandargcompletioncb_s *ctx = parm;\n\tchar trimmed[256];\n\tsize_t l;\n\tchar timetext[128];\n\tchar desc[256];\n\tflocation_t loc;\n\tif (FS_FLocateFile(name, FSLF_QUIET|FSLF_DONTREFERENCE, &loc) && loc.search->handle != spath)\n\t\t;\t//found in some other gamedir. don't show the dupe.\n\telse\n\t{\n\t\tQ_strncpyz(trimmed, name+6, sizeof(trimmed));\n\t\tl = strlen(trimmed);\n\t\tif (l >= 9 && !Q_strcasecmp(trimmed+l-9, \"/info.fsv\"))\n\t\t{\n\t\t\ttrimmed[l-9] = 0;\n\n\t\t\tstrftime(timetext, sizeof(timetext), \"%a \"S_COLOR_MAGENTA\"%Y-%m-%d \"S_COLOR_WHITE\"%H:%M:%S\", localtime(&mtime));\n\t\t\tQ_snprintfz(desc, sizeof(desc), \"Modified %s\\n^[\\\\h\\\\64\\\\img\\\\saves/%s/screeny.tga^]\", timetext, trimmed);\n\t\t\tctx->cb(trimmed, desc, NULL, ctx);\n\t\t}\n\t}\n\treturn true;\n}\n#ifndef QUAKETC\nstatic int QDECL CompleteSaveListLegacy (const char *name, qofs_t flags, time_t mtime, void *parm, searchpathfuncs_t *spath)\n{\n\tstruct xcommandargcompletioncb_s *ctx = parm;\n\tchar stripped[64];\n\tchar timetext[128];\n\tchar desc[256];\n\tflocation_t loc;\n\tif (FS_FLocateFile(name, FSLF_QUIET|FSLF_DONTREFERENCE, &loc) && loc.search->handle != spath)\n\t\t;\t//found in some other gamedir. don't show the dupe.\n\telse\n\t{\n\t\tCOM_StripExtension(name, stripped, sizeof(stripped));\n\t\tstrftime(timetext, sizeof(timetext), \"%a \"S_COLOR_MAGENTA\"%Y-%m-%d \"S_COLOR_WHITE\"%H:%M:%S\", localtime(&mtime));\n\t\tQ_snprintfz(desc, sizeof(desc), \"%s, Modified %s\", name, timetext);\n\t\tctx->cb(stripped, desc, NULL, ctx);\n\t}\n\treturn true;\n}\n#endif\nvoid SV_Savegame_c(int argn, const char *partial, struct xcommandargcompletioncb_s *ctx)\n{\n\tif (argn == 1)\n\t{\n\t\tCOM_EnumerateFiles(va(\"saves/%s*/info.fsv\", partial), CompleteSaveList, ctx);\n#ifndef QUAKETC\n\t\tCOM_EnumerateFiles(va(\"%s*.sav\", partial), CompleteSaveListLegacy, ctx);\n#endif\n\t}\n}\n\nvoid SV_Savegame_f (void)\n{\n\tif (sv.state == ss_clustermode && MSV_ForwardToAutoServer())\n\t\t;\n\telse if (Cmd_Argc() <= 2)\n\t{\n\t\tconst char *savename = Cmd_Argv(1);\n\t\tif (strstr(savename, \"..\"))\n\t\t{\n\t\t\tCon_TPrintf (\"Relative pathnames are not allowed\\n\");\n\t\t\treturn;\n\t\t}\n\t\t//make sure the name is valid, eg if its omitted.\n\t\tif (!*savename || strstr(savename, \"..\"))\n\t\t\tsavename = \"quick\";\n#ifndef QUAKETC\n\t\tif (!Q_strcasecmp(Cmd_Argv(0), \"savegame_legacy\"))\n\t\t{\n\t\t\tif (SV_LegacySavegame(savename, true))\n\t\t\t\treturn;\n\t\t\tCon_Printf(\"Unable to use legacy save format\\n\");\n\t\t\treturn;\n\t\t}\n#endif\n\t\tSV_Savegame(savename, false);\n\t}\n\telse\n\t\tCon_Printf(\"%s: invalid number of arguments\\n\", Cmd_Argv(0));\n}\n\nvoid SV_AutoSave(void)\n{\n#ifndef NOBUILTINMENUS\n#ifndef SERVERONLY\n\tconst char *autosavename;\n\tint i;\n\tif (sv_autosave.value <= 0)\n\t\treturn;\n\tif (sv.state != ss_active)\n\t\treturn;\n\tswitch(svs.gametype)\n\t{\n\tdefault:\t//probably broken. don't ever try.\n\t\treturn;\n\n\tcase GT_Q1QVM:\n\tcase GT_PROGS:\n\t\t//don't bother to autosave multiplayer games.\n\t\t//this may be problematic with splitscreen, but coop rules tend to apply there anyway.\n\t\tif (sv.allocated_client_slots != 1)\n\t\t\treturn;\n\n\t\tfor (i = 0; i < sv.allocated_client_slots; i++)\n\t\t{\n\t\t\tif (svs.clients[i].state == cs_spawned)\n\t\t\t{\n\t\t\t\tif (svs.clients[i].edict->v->health <= 0)\n\t\t\t\t\treturn;\t//autosaves with a dead player are just cruel.\n\n\t\t\t\tif ((int)svs.clients[i].edict->v->flags & (FL_GODMODE | FL_NOTARGET))\n\t\t\t\t\treturn;\t//autosaves to highlight cheaters is also just spiteful.\n\n\t\t\t\tif (svs.clients[i].edict->v->movetype != MOVETYPE_WALK)\n\t\t\t\t\treturn;\t//noclip|fly are cheaters, toss|bounce are bad at playing. etc.\n\n\t\t\t\tif (!((int)svs.clients[i].edict->v->flags & FL_ONGROUND))\n\t\t\t\t\treturn;\t//autosaves while people are jumping are awkward.\n\n\t\t\t\tif (svs.clients[i].edict->v->velocity[0] || svs.clients[i].edict->v->velocity[1] || svs.clients[i].edict->v->velocity[2])\n\t\t\t\t\treturn;\t//people running around are likely to result in poor saves\n\t\t\t}\n\t\t}\n\t\tbreak;\n\t}\n\n\tautosavename = M_ChooseAutoSave();\n\tCon_DPrintf(\"Autosaving to %s\\n\", autosavename);\n\tSV_Savegame(autosavename, false);\n\n\tsv.autosave_time = sv.time + sv_autosave.value * 60;\n#endif\n#endif\n}\n\nstatic void SV_SwapPlayers(client_t *a, client_t *b)\n{\n\tsize_t i;\n\tclient_t tmp;\n\tif (a==b)\n\t\treturn;\t//o.O\n\ttmp = *a;\n\t*a = *b;\n\t*b = tmp;\n\n\t//swap over pointers for splitscreen.\n\tfor (i = 0; i < svs.allocated_client_slots; i++)\n\t{\n\t\tif (svs.clients[i].controller == a)\n\t\t\tsvs.clients[i].controller = b;\n\t\telse if (svs.clients[i].controller == b)\n\t\t\tsvs.clients[i].controller = a;\n\t\tif (svs.clients[i].controlled == a)\n\t\t\tsvs.clients[i].controlled = b;\n\t\telse if (svs.clients[i].controlled == b)\n\t\t\tsvs.clients[i].controlled = a;\n\t}\n\n\t//undo some damage...\n\tb->edict = a->edict;\n\ta->edict = tmp.edict;\n\n\tif (a->name == b->namebuf)\ta->name = a->namebuf;\n\tif (b->name == a->namebuf)\tb->name = b->namebuf;\n\tif (a->team == b->teambuf)\ta->team = a->teambuf;\n\tif (b->team == a->teambuf)\tb->team = b->teambuf;\n\n\tif (a->netchan.message.data)\n\t\ta->netchan.message.data += (qbyte*)a-(qbyte*)b;\n\tif (a->datagram.data)\n\t\ta->datagram.data += (qbyte*)a-(qbyte*)b;\n\tif (a->backbuf.data)\n\t\ta->backbuf.data += (qbyte*)a-(qbyte*)b;\n\n\tif (b->netchan.message.data)\n\t\tb->netchan.message.data += (qbyte*)b-(qbyte*)a;\n\tif (b->datagram.data)\n\t\tb->datagram.data += (qbyte*)b-(qbyte*)a;\n\tif (b->backbuf.data)\n\t\tb->backbuf.data += (qbyte*)b-(qbyte*)a;\n}\nvoid SV_LoadPlayers(loadplayer_t *lp, size_t slots)\n{\t//loading games is messy as fuck\n\t//we need to reorder players to the order in the saved game.\n\t//swapping players around is really rather messy...\n\n\tclient_t *cl;\n\tsize_t clnum, p, p2;\n\tint to[255];\n\n\t//kick any splitscreen seats. they'll get re-added after load (filling slots like connecting players would)\n\tfor (clnum = 0; clnum < svs.allocated_client_slots; clnum++)\n\t{\n\t\tcl = &svs.clients[clnum];\n\t\tcl->controlled = NULL;\t//kill the links.\n\t\tif (cl->controller)\t//its a slave\n\t\t{\n\t\t\t//unlink it\n\t\t\tcl->controller = NULL;\n\n\t\t\t//make it into a pseudo-bot so the kicking doesn't do weird stuff.\n\t\t\tcl->netchan.remote_address.type = NA_INVALID;\t//so the remaining client doesn't get the kick too.\n\t\t\tcl->protocol = SCP_BAD;\t//make it a bit like a bot, so we don't try sending any datagrams/reliables at someone that isn't able to receive anything.\n\n\t\t\t//okay, it can get lost now.\n\t\t\tcl->drop = true;\n\t\t}\n\t}\n\n\t//despawn any entity data, and try to find the loaded player to move them to\n\tfor (clnum = 0; clnum < svs.allocated_client_slots; clnum++)\t//clear the server for the level change.\n\t{\n\t\tto[clnum] = -1;\n\t\tcl = &svs.clients[clnum];\n\t\tSV_DespawnClient(cl);\n\t\tif (cl->state <= cs_loadzombie)\n\t\t\tcontinue;\n\t\tif (cl->state == cs_spawned)\n\t\t\tcl->state = cs_connected;\n\n\t\t//FIXME: try to match by guids (but we don't have saved guid info)\n\n\t\t//try to match the player to a slot by name.\n\t\tfor (p = 0; p < slots; p++)\n\t\t\tif (*lp[p].name)\n\t\t\t{\n\t\t\t\tif (!strcmp(cl->name, lp[p].name) || slots == 1)\n\t\t\t\t{\t//this player matched matched...\n\t\t\t\t\tto[clnum] = p;\n\t\t\t\t\tlp[p].source = cl;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t}\n\t//for loaded players that don't have a client go and find a player to spawn there, to try to deal with players that renamed themselves.\n\tfor (p = 0; p < slots; p++)\n\t{\n\t\tif (!*lp[p].name || lp[p].source)\n\t\t\tcontinue;\n\t\tfor (clnum = 0; clnum < svs.allocated_client_slots; clnum++)\n\t\t{\n\t\t\tcl = &svs.clients[clnum];\n\t\t\tif (cl->state <= cs_loadzombie)\n\t\t\t\tcontinue;\n\t\t\tif (to[clnum] >= 0)\n\t\t\t\tcontinue;\t//was already mapped\n\t\t\tif (cl->spectator)\n\t\t\t\tcontinue;\t//spectators shouldn't be pulled into a player against their will. it may still happen though.\n\t\t\tto[clnum] = p;\n\t\t\tlp[p].source = cl;\n\n\t\t\tSV_BroadcastPrintf(PRINT_HIGH, \"%s reprises %s\\n\", cl->name, lp[p].name);\n\t\t\tbreak;\n\t\t}\n\t}\n\n\t//we walk the list in order, pulling from the appropriate slot.\n\t//we're swapping each time, so uninteresting players will bubble to the end instead of breaking our finalised list..\n\t//if we're swapping from an earlier slot then that slot wasn't relevant anyway.\n\tfor (p = 0; p < slots; p++)\n\t{\n\t\tif (lp[p].source && lp[p].source!=&svs.clients[p])\n\t\t{\n\t\t\tSV_SwapPlayers(&svs.clients[p], lp[p].source);\n\t\t\tfor (p2 = 0; p2 < slots; p2++)\n\t\t\t{\n\t\t\t\tif (p == p2)\n\t\t\t\t\tcontinue;\n\t\t\t\tif (lp[p2].source == &svs.clients[p])\n\t\t\t\t\tlp[p2].source = lp[p].source;\n\t\t\t\telse if (lp[p2].source == lp[p].source)\n\t\t\t\t\tlp[p2].source = &svs.clients[p];\n\t\t\t}\n\t\t}\n\t}\n\n\tif (slots > svs.allocated_client_slots)\t//will be trimmed later\n\t\tSV_UpdateMaxPlayers(slots);\n\tfor (cl = svs.clients, clnum=0; clnum < slots; cl++,clnum++)\n\t{\n\t\tif (*lp[clnum].name)\n\t\t{\t//okay so we have a player ready for this slot.\n\t\t\tfor (p = 0; p < NUM_SPAWN_PARMS; p++)\n\t\t\t\tcl->spawn_parms[p] = lp[clnum].parm[p].f;\n\t\t\tcl->spawn_parmstring = lp[clnum].parmstr;\n\t\t\tcontinue;\n\t\t}\n\t\telse if (cl->state > cs_zombie)\n\t\t\tSV_DropClient(cl);\n/*\n\t\tQ_strncpyz(cl->namebuf, lp[clnum].name, sizeof(cl->namebuf));\n\t\tcl->name = cl->namebuf;\n\t\tif (*cl->namebuf)\n\t\t{\n\t\t\tcl->state = cs_loadzombie;\n\t\t\tcl->connection_started = realtime+20;\n\t\t\tcl->istobeloaded = true;\t//the parms are known\n\t\t\tcl->userid = 0;\n\n\t\t\tmemset(&cl->netchan, 0, sizeof(cl->netchan));\n\n\t\t\tfor (p = 0; p < NUM_SPAWN_PARMS; p++)\n\t\t\t\tcl->spawn_parms[p] = lp[clnum].parm[p].f;\n\t\t\tcl->spawn_parmstring = lp[clnum].parmstr;\n\t\t}*/\n\t}\n}\n\nstatic void SV_GameLoaded(loadplayer_t *lp, size_t slots, const char *savename)\n{\n\tsize_t clnum;\n\tclient_t *cl;\n\n\t//make sure autosave doesn't save too early.\n\tsv.autosave_time = sv.time + sv_autosave.value*60;\n\n\t//let the restart command know the name of the saved game to reload.\n\tQ_strncpyz(sv.loadgame_on_restart, savename, sizeof(sv.loadgame_on_restart));\n\n\tslots = min(slots, svs.allocated_client_slots);\n\n\t//make sure the player state is set up properly.\n\tfor (clnum = 0; clnum < slots; clnum++)\n\t{\n\t\tcl = &svs.clients[clnum];\n\t\tcl->spawned = !!*lp[clnum].name;\n\t\tif (cl->spawned)\n\t\t{\n\t\t\tsv.spawned_client_slots++;\n\t\t\tQ_strncpyz(cl->namebuf, lp[clnum].name, sizeof(cl->namebuf));\n\t\t}\n\n\t\tif (svprogfuncs)\n\t\t{\n\t\t\tcl->name = PR_AddString(svprogfuncs, cl->namebuf, sizeof(cl->namebuf), false);\n\t\t\tcl->team = PR_AddString(svprogfuncs, cl->teambuf, sizeof(cl->teambuf), false);\n\t\t\tcl->edict = EDICT_NUM_PB(svprogfuncs, clnum+1);\n\n#ifdef HEXEN2\n\t\t\tif (cl->edict)\n\t\t\t\tcl->playerclass = cl->edict->xv->playerclass;\n\t\t\telse\n\t\t\t\tcl->playerclass = 0;\n#endif\n#ifdef HAVE_LEGACY\n\t\t\tcl->edict->xv->clientcolors = cl->playercolor;\n#endif\n\t\t}\n\n\t\tif (cl->state == cs_spawned)\t//shouldn't have gotten past SV_SpawnServer, but just in case...\n\t\t\tcl->state = cs_connected;\t//client needs new serverinfo.\n\t\tif (cl->spawned && cl->state < cs_connected)\t//make sure the player slot is active when the gamecode thinks it was (with a loadzombie if needed)\n\t\t{\n\t\t\tcl->state = cs_loadzombie;\n\t\t\tcl->connection_started = realtime+20;\n\t\t\tcl->istobeloaded = true;\t//the parms are known\n\t\t\tcl->userid = 0;\n\t\t\tmemset(&cl->netchan, 0, sizeof(cl->netchan));\n\t\t}\n\n\t\tif (cl->controller)\n\t\t\tcontinue;\n\t\tif (cl->state>=cs_connected)\n\t\t{\n\t\t\tif (cl->protocol == SCP_QUAKE3)\n\t\t\t\tcontinue;\n\t\t\tif (cl->protocol == SCP_BAD)\n\t\t\t\tcontinue;\n\n\t\t\thost_client = cl;\n#ifdef NQPROT\n\t\t\tif (ISNQCLIENT(host_client))\n\t\t\t\tSVNQ_New_f();\n\t\t\telse\n#endif\n\t\t\t\tSV_New_f();\n\t\t}\n\t}\n\thost_client = NULL;\n\n\tfor (clnum = 0; clnum < slots; clnum++) {\t\t\n\t\tcl = &svs.clients[clnum];\n\n\t\t/* ensure angles are respected -eukara */\n\t\tif (svprogfuncs) {\n\t\t\tcl->edict->v->fixangle = 1;\n\t\t}\n\n\t\t//make sure userinfos match any renamed players.\n\t\tif (cl->state >= cs_connected) {\n\t\t\tSV_ExtractFromUserinfo(&svs.clients[clnum], true);\n\t\t}\n\t}\n}\n\n#ifndef QUAKETC\n\n//expects the version to have already been parsed\nstatic qboolean SV_Loadgame_Legacy(const char *savename, const char *filename, vfsfile_t *f, int version)\n{\n\t//FIXME: Multiplayer save probably won't work with spectators.\n\tchar\tmapname[MAX_QPATH];\n\tfloat\ttime;\n\tchar\tstr[32768];\n\tint\t\ti;\n\tint pt;\n\tint lstyles;\n\n\tint slots;\n\n\tint clnum;\n\tchar plname[32];\n\n\tint filelen, filepos;\n\tchar *file;\n\n\tchar *modelnames[MAX_PRECACHE_MODELS];\n\tchar *soundnames[MAX_PRECACHE_SOUNDS];\n\tloadplayer_t lp[255];\n\tstruct loadinfo_s loadinfo;\n\n\tloadinfo.numplayers = countof(lp);\n\tloadinfo.players = lp;\n\n\tif (version != SAVEGAME_VERSION_FTE_LEG && version != SAVEGAME_VERSION_NQ && version != SAVEGAME_VERSION_QW)\n\t{\n\t\tVFS_CLOSE (f);\n\t\tCon_TPrintf (\"Unable to load savegame of version %i\\n\", version);\n\t\treturn false;\n\t}\n\tVFS_GETS(f, str, sizeof(str));\t//discard comment.\n\tCon_Printf(\"loading legacy game from %s...\\n\", filename);\n\n\tif (version == SAVEGAME_VERSION_NQ || version == SAVEGAME_VERSION_QW)\n\t{\n\t\tslots = 1;\n\n#ifdef SERVERONLY\n\t\tQ_strncpyz(lp[0].name, \"\", sizeof(lp[0].name));\n#else\n\t\tQ_strncpyz(lp[0].name, name.string, sizeof(lp[0].name));\n#endif\n\t\tlp[0].parmstr = NULL;\n\t\tlp[0].source = NULL;\n\n\t\tfor (i=0 ; i<16 ; i++)\n\t\t{\n\t\t\tVFS_GETS(f, str, sizeof(str));\n\t\t\tlp[0].parm[i].f = atof(str);\n\t\t}\n\t\tfor (; i < countof(lp[0].parm); i++)\n\t\t\tlp[0].parm[i].i = 0;\n\t}\n\telse\t//fte saves ALL the clients on the server.\n\t{\n\t\tVFS_GETS(f, str, strlen(str));\n\t\tslots = atoi(str);\n\t\tif (!slots || slots >= countof(lp))\t//err\n\t\t{\n\t\t\tVFS_CLOSE(f);\n\t\t\tCon_Printf (\"Corrupted save game\");\n\t\t\treturn false;\n\t\t}\n\t\tfor (clnum = 0; clnum < slots; clnum++)\n\t\t{\n\t\t\tVFS_GETS(f, plname, sizeof(plname));\n\t\t\tCOM_Parse(plname);\n\t\t\tQ_strncpyz(lp[clnum].name, com_token, sizeof(lp[clnum].name));\n\t\t\tlp[clnum].parmstr = NULL;\n\t\t\tlp[clnum].source = NULL;\n\n\t\t\tif (!*com_token)\n\t\t\t\tcontinue;\n\n\t\t\t//probably should be 32, rather than NUM_SPAWN_PARMS(64)\n\t\t\tfor (i=0 ; i<NUM_SPAWN_PARMS ; i++)\n\t\t\t{\n\t\t\t\tVFS_GETS(f, str, sizeof(str));\n\t\t\t\tlp[clnum].parm[i].f = atof(str);\n\t\t\t}\n\t\t\tfor (; i < countof(lp[clnum].parm); i++)\n\t\t\t\tlp[clnum].parm[i].i = 0;\n\t\t}\n\t}\n\tSV_LoadPlayers(lp, slots);\n\n\tif (version == SAVEGAME_VERSION_NQ || version == SAVEGAME_VERSION_QW)\n\t{\n\t\tVFS_GETS(f, str, sizeof(str));\n\t\tCvar_SetValue (Cvar_FindVar(\"skill\"), atof(str));\n\t\tCvar_SetValue (Cvar_FindVar(\"deathmatch\"), 0);\n\t\tCvar_SetValue (Cvar_FindVar(\"coop\"), 0);\n\t\tCvar_SetValue (Cvar_FindVar(\"teamplay\"), 0);\n\n\t\tif (version == SAVEGAME_VERSION_NQ)\n\t\t{\n\t\t\tprogstype = PROG_NQ;\n\t\t\tCvar_Set (&pr_ssqc_progs, \"progs.dat\");\t//NQ's progs.\n\t\t}\n\t\telse\n\t\t{\n\t\t\tprogstype = PROG_QW;\n\t\t\tCvar_Set (&pr_ssqc_progs, \"spprogs\");\t//zquake's single player qw progs.\n\t\t}\n\t\tpt = 0;\n\t}\n\telse\n\t{\n\t\tVFS_GETS(f, str, sizeof(str));\n\t\tpt = atoi(str);\n\n\t\tVFS_GETS(f, str, sizeof(str));\n\t\tCvar_SetValue (Cvar_FindVar(\"skill\"), atof(str));\n\n\t\tVFS_GETS(f, str, sizeof(str));\n\t\tCvar_SetValue (Cvar_FindVar(\"deathmatch\"), atof(str));\n\t\tVFS_GETS(f, str, sizeof(str));\n\t\tCvar_SetValue (Cvar_FindVar(\"coop\"), atof(str));\n\t\tVFS_GETS(f, str, sizeof(str));\n\t\tCvar_SetValue (Cvar_FindVar(\"teamplay\"), atof(str));\n\t}\n\tVFS_GETS(f, mapname, sizeof(mapname));\n\tVFS_GETS(f, str, sizeof(str));\n\ttime = atof(str);\n\n\tSV_SpawnServer (mapname, NULL, false, false, slots);\t//always inits MAX_CLIENTS slots. That's okay, because we can cut the max easily.\n\tif (sv.state != ss_active)\n\t{\n\t\tVFS_CLOSE (f);\n\t\tCon_TPrintf (\"Couldn't load map\\n\");\n\t\treturn false;\n\t}\n\n\tif (sv.allocated_client_slots != slots)\n\t{\n\t\tCon_TPrintf (\"Player count changed\\n\");\n\t\treturn false;\n\t}\n\n// load the light styles\n\n\tlstyles = 64;\n\tif (lstyles > sv.maxlightstyles)\n\t\tZ_ReallocElements((void**)&sv.lightstyles, &sv.maxlightstyles, lstyles, sizeof(*sv.lightstyles));\n\tfor (i=0 ; i<lstyles ; i++)\n\t{\n\t\tVFS_GETS(f, str, sizeof(str));\n\t\tif (sv.lightstyles[i].str)\n\t\t\tZ_Free((char*)sv.lightstyles[i].str);\n\t\tsv.lightstyles[i].str = Z_StrDup(str);\n\t\tsv.lightstyles[i].colours[0] = sv.lightstyles[i].colours[1] = sv.lightstyles[i].colours[2] = 1;\n\t}\n\tfor (; i < sv.maxlightstyles; i++)\n\t{\n\t\tif (sv.lightstyles[i].str)\n\t\t\tZ_Free((char*)sv.lightstyles[i].str);\n\t\tsv.lightstyles[i].str = NULL;\n\t}\n\n\t//model names are pointers to vm-accessible memory. as that memory is going away, we need to destroy and recreate, which requires preserving them.\n\tfor (i = 1; i < MAX_PRECACHE_MODELS; i++)\n\t{\n\t\tif (!sv.strings.model_precache[i])\n\t\t{\n\t\t\tmodelnames[i] = NULL;\n\t\t\tbreak;\n\t\t}\n\t\tmodelnames[i] = Z_StrDup(sv.strings.model_precache[i]);\n\t}\n\tfor (i = 1; i < MAX_PRECACHE_SOUNDS; i++)\n\t{\n\t\tif (!sv.strings.sound_precache[i])\n\t\t{\n\t\t\tsoundnames[i] = NULL;\n\t\t\tbreak;\n\t\t}\n\t\tsoundnames[i] = Z_StrDup(sv.strings.sound_precache[i]);\n\t}\n\n// load the edicts out of the savegame file\n// the rest of the file is sent directly to the progs engine.\n\n\tif (version == SAVEGAME_VERSION_NQ || version == SAVEGAME_VERSION_QW)\n\t\t;//Q_InitProgs();\t//reinitialize progs entirly.\n\telse\n\t{\n\t\tQ_SetProgsParms(false);\n\t\tsvs.numprogs = 0;\n\n\t\tPR_Configure(svprogfuncs, PR_ReadBytesString(pr_ssqc_memsize.string), MAX_PROGS, pr_enable_profiling.ival);\n\t\tPR_RegisterFields();\n\t\tPR_InitEnts(svprogfuncs, sv.world.max_edicts);\t//just in case the max edicts isn't set.\n\t\tprogstype = pt;\t//presumably the progs.dat will be what they were before.\n\t}\n\n\t//reload model names.\n\tfor (i = 1; i < MAX_PRECACHE_MODELS; i++)\n\t{\n\t\tif (!modelnames[i])\n\t\t\tbreak;\n\t\tsv.strings.model_precache[i] = PR_AddString(svprogfuncs, modelnames[i], 0, false);\n\t\tZ_Free(modelnames[i]);\n\t}\n\tfor (i = 1; i < MAX_PRECACHE_SOUNDS; i++)\n\t{\n\t\tif (!soundnames[i])\n\t\t\tbreak;\n\t\tsv.strings.sound_precache[i] = PR_AddString(svprogfuncs, soundnames[i], 0, false);\n\t\tZ_Free(soundnames[i]);\n\t}\n\n\tfilepos = VFS_TELL(f);\n\tfilelen = VFS_GETLEN(f);\n\tfilelen -= filepos;\n\tfile = BZ_Malloc(filelen+1+8);\n\tmemset(file, 0, filelen+1+8);\n\tstrcpy(file, \"loadgame\");\n\tclnum=VFS_READ(f, file+8, filelen);\n\tfile[filelen+8]='\\0';\n\tsv.world.edict_size=svprogfuncs->load_ents(svprogfuncs, file, &loadinfo, SV_SaveMemoryReset, NULL, SV_ExtendedSaveData);\n\tBZ_Free(file);\n\n\tPR_LoadGlabalStruct(false);\n\n\tpr_global_struct->time = sv.world.physicstime = sv.time = time;\n\tsv.starttime = Sys_DoubleTime() - sv.time;\n\n\tVFS_CLOSE(f);\n\n\t//FIXME: QSS+DP saved games have some / *\\nkey values\\nkey values\\n* / thing in them to save precaches and stuff\n\n\tWorld_ClearWorld(&sv.world, true);\n\n\tSV_GameLoaded(lp, slots, savename);\n\treturn true;\n}\n#endif\n\n//Attempts to load a named saved game.\nqboolean SV_Loadgame (const char *unsafe_savename)\n{\n\tlevelcache_t *cache;\n\tunsigned char str[MAX_LOCALINFO_STRING+1], *trim;\n\tunsigned char savename[MAX_QPATH];\n\tvfsfile_t *f;\n\tunsigned char filename[MAX_OSPATH];\n\tint version;\n\tint clnum;\n\tint slots;\n\tint p;\n\tclient_t *cl;\n\tgametype_e gametype;\n\tloadplayer_t lp[255];\n\n\tstruct\n\t{\n\t\tchar *pattern;\n\t\tflocation_t loc;\n\t} savefiles[] =\n\t{\n\t\t{\"saves/%s/info.fsv\"},\n#ifndef QUAKETC\n\t\t{\"%s.sav\"},\n#endif\n\t\t{\"%s\"}\n\t};\n\tint bestd=0x7fffffff,best=0;\n\ttime_t bestt=0,t;\n\n\tQ_strncpyz(savename, unsafe_savename, sizeof(savename));\n\tif (!*savename || strstr(savename, \"..\"))\n\t{\t//if no args, or its invalid, try to pick the last one that was saved (of those listed in the menu)\n\t\tsize_t n;\n\t\tstatic char *autoload[] = {\t\"quick\", \"a0\", \"a1\", \"a2\",\n\t\t\t\t\t\t\t\t\t\"s0\", \"s1\", \"s2\", \"s3\", \"s4\", \"s5\", \"s6\", \"s7\", \"s8\", \"s9\"};\n\t\tstrcpy(savename, \"quick\");\t//default...\n\n\t\tfor (n = 0; n < countof(autoload); n++)\n\t\t{\n\t\t\tfor (p = 0; p < countof(savefiles)-1; p++)\n\t\t\t{\n\t\t\t\tint d = FS_FLocateFile(va(savefiles[p].pattern, autoload[n]), FSLF_DONTREFERENCE, &savefiles[p].loc);\n\t\t\t\tif (!d)\n\t\t\t\t\tcontinue;\n\t\t\t\tFS_GetLocMTime(&savefiles[p].loc, &t);\n\t\t\t\tif (d < bestd || (bestd==d&&t>bestt))\n\t\t\t\t{\n\t\t\t\t\tbestd = d;\n\t\t\t\t\tbestt = t;\n\t\t\t\t\tbest = p;\n\n\t\t\t\t\tstrcpy(savename, autoload[n]);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tfor (p = 0; p < countof(savefiles); p++)\n\t{\n\t\tint d = FS_FLocateFile(va(savefiles[p].pattern, savename), FSLF_DONTREFERENCE, &savefiles[p].loc);\n\t\tif (!d)\n\t\t\tcontinue;\n\t\tFS_GetLocMTime(&savefiles[p].loc, &t);\n\t\tif (d < bestd || (bestd==d&&t>bestt))\n\t\t{\n\t\t\tbestd = d;\n\t\t\tbestt = t;\n\t\t\tbest = p;\n\t\t}\n\t}\n\n\tQ_snprintfz (filename, sizeof(filename), savefiles[best].pattern, savename);\n\tf = FS_OpenReadLocation(filename, &savefiles[best].loc);\n\tif (!f)\n\t{\n\t\tCon_TPrintf (\"ERROR: couldn't open %s.\\n\", filename);\n\t\treturn false;\n\t}\n\n#if defined(MENU_DAT) && !defined(SERVERONLY)\n\tMP_Toggle(0);\n#endif\n\n\tVFS_GETS(f, str, sizeof(str)-1);\n\tversion = atoi(str);\n\tif (version < SAVEGAME_VERSION_FTE_HUB || version >= SAVEGAME_VERSION_FTE_HUB+GT_MAX)\n\t{\n#ifdef QUAKETC\n\t\tVFS_CLOSE (f);\n\t\tCon_TPrintf (\"Unable to load savegame of version %i\\n\", version);\n\t\treturn false;\n#else\n\t\treturn SV_Loadgame_Legacy(savename, filename, f, version);\n#endif\n\t}\n\n\tgametype = version - SAVEGAME_VERSION_FTE_HUB;\n\tVFS_GETS(f, str, sizeof(str)-1);\n#ifndef SERVERONLY\n\tif (!cls.state)\n#endif\n\t\tCon_TPrintf (\"Loading game from %s...\\n\", filename);\n\n\n\n\tVFS_GETS(f, str, sizeof(str)-1);\n\tslots = atoi(str);\n\n\tif (slots < 1 || slots > countof(lp))\n\t{\n\t\tVFS_CLOSE (f);\n\t\tCon_Printf (\"invalid player count in saved game\\n\");\n\t\treturn false;\n\t}\n\n\tfor (cl = svs.clients, clnum=0; clnum < slots; cl++,clnum++)\n\t{\n\t\tVFS_GETS(f, str, sizeof(str)-1);\n\t\tstr[sizeof(cl->namebuf)-1] = '\\0';\n\t\tfor (trim = str+strlen(str)-1; trim>=str && *trim <= ' '; trim--)\n\t\t\t*trim='\\0';\n\t\tfor (trim = str; *trim <= ' ' && *trim; trim++)\n\t\t\t;\n\t\tstrcpy(lp[clnum].name, str);\n\t\tlp[clnum].parmstr = NULL;\n\t\tlp[clnum].source = NULL;\n\n\t\tif (*str)\n\t\t{\n\t\t\tVFS_GETS(f, str, sizeof(str)-1);\n\t\t\tif (*str == '{')\n\t\t\t{\n\t\t\t\twhile(VFS_GETS(f, str, sizeof(str)-1))\n\t\t\t\t{\n\t\t\t\t\tif (*str == '}')\n\t\t\t\t\t\tbreak;\n\t\t\t\t\ttrim = COM_Parse(str);\n\t\t\t\t\tif (!strcmp(com_token, \"parm_string\"))\n\t\t\t\t\t{\n\t\t\t\t\t\tCOM_Parse(trim);\n\t\t\t\t\t\tZ_Free(lp[clnum].parmstr);\n\t\t\t\t\t\tlp[clnum].parmstr = Z_StrDup(com_token);\n\t\t\t\t\t}\n\t\t\t\t\telse if (!strncmp(com_token, \"parm\", 4))\n\t\t\t\t\t{\n\t\t\t\t\t\tunsigned int parm = atoi(com_token+4);\n\t\t\t\t\t\tCOM_Parse(trim);\n\t\t\t\t\t\tif (parm < NUM_SPAWN_PARMS)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (!strncmp(com_token, \"0x\", 2))\n\t\t\t\t\t\t\t\tlp[clnum].parm[parm].i = strtoul(com_token, NULL, 16);\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\tlp[clnum].parm[parm].f = strtod(com_token, NULL);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\tCon_Printf(\"Unknown player data: %s\\n\", com_token);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\t//we used to have N integers, where N was some random outdated constant.\n\t\t\t\tVFS_CLOSE (f);\n\t\t\t\tCon_Printf (\"Incompatible saved game\\n\");\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t}\n\n\tSV_LoadPlayers(lp, slots);\n\n\n\tVFS_GETS(f, str, sizeof(str)-1);\n\tfor (trim = str+strlen(str)-1; trim>=str && *trim <= ' '; trim--)\n\t\t*trim='\\0';\n\tfor (trim = str; *trim <= ' ' && *trim; trim++)\n\t\t;\n\tInfo_RemovePrefixedKeys(str, '*');\t//just in case\n\tInfoBuf_Clear(&svs.info, false);\n\tInfoBuf_FromString(&svs.info, str, true);\n\n\tVFS_GETS(f, str, sizeof(str)-1);\n\tfor (trim = str+strlen(str)-1; trim>=str && *trim <= ' '; trim--)\n\t\t*trim='\\0';\n\tfor (trim = str; *trim <= ' ' && *trim; trim++)\n\t\t;\n\tInfo_RemovePrefixedKeys(str, '*');\t//just in case\n\tInfoBuf_Clear(&svs.localinfo, false);\n\tInfoBuf_FromString(&svs.localinfo, str, true);\n\n\tVFS_GETS(f, str, sizeof(str)-1);\n\tfor (trim = str+strlen(str)-1; trim>=str && *trim <= ' '; trim--)\n\t\t*trim='\\0';\n\tfor (trim = str; *trim <= ' ' && *trim; trim++)\n\t\t;\n\tif (strcmp(str, \"{\"))\n\t\tSV_Error(\"Corrupt saved game\\n\");\n\twhile(1)\n\t{\n\t\tif (!VFS_GETS(f, str, sizeof(str)-1))\n\t\t\tSV_Error(\"Corrupt saved game\\n\");\n\t\tfor (trim = str+strlen(str)-1; trim>=str && *trim <= ' '; trim--)\n\t\t\t*trim='\\0';\n\t\tfor (trim = str; *trim <= ' ' && *trim; trim++)\n\t\t\t;\n\t\tif (!strcmp(str, \"}\"))\n\t\t\tbreak;\n\t\telse if (*str)\n\t\t\tCmd_ExecuteString(str, RESTRICT_RCON);\n\t}\n\n\tSV_FlushLevelCache();\n\n\tVFS_GETS(f, str, sizeof(str)-1);\n\tfor (trim = str+strlen(str)-1; trim>=str && *trim <= ' '; trim--)\n\t\t*trim='\\0';\n\tfor (trim = str; *trim <= ' ' && *trim; trim++)\n\t\t;\n\tif (strcmp(str, \"{\"))\n\t\tSV_Error(\"Corrupt saved game\\n\");\n\twhile(1)\n\t{\n\t\tif (!VFS_GETS(f, str, sizeof(str)-1))\n\t\t\tSV_Error(\"Corrupt saved game\\n\");\n\t\tfor (trim = str+strlen(str)-1; trim>=str && *trim <= ' '; trim--)\n\t\t\t*trim='\\0';\n\t\tfor (trim = str; *trim <= ' ' && *trim; trim++)\n\t\t\t;\n\t\tif (!strcmp(str, \"}\"))\n\t\t\tbreak;\n\t\tif (!*str)\n\t\t\tcontinue;\n\n\t\tcache = Z_Malloc(sizeof(levelcache_t)+strlen(str)+1);\n\t\tcache->mapname = (char *)(cache+1);\n\t\tstrcpy(cache->mapname, str);\n\t\tcache->gametype = gametype;\n\n\t\tcache->next = svs.levcache;\n\n\n\t\tFS_Copy(va(\"saves/%s/%s.lvc\", savename, cache->mapname), va(\"saves/%s.lvc\", cache->mapname), FS_GAME, FS_GAMEONLY);\n\n\t\tsvs.levcache = cache;\n\t}\n\n\tVFS_GETS(f, str, sizeof(str)-1);\n\tfor (trim = str+strlen(str)-1; trim>=str && *trim <= ' '; trim--)\n\t\t*trim='\\0';\n\tfor (trim = str; *trim <= ' ' && *trim; trim++)\n\t\t;\n\n\t//serverflags is reset on restart, so we need to read the value as it was at the start of the current map.\n\tVFS_GETS(f, filename, sizeof(filename)-1);\n\tsvs.serverflags = atof(filename);\n\n\tVFS_CLOSE(f);\n\n#ifdef Q2SERVER\n\tif (gametype == GT_QUAKE2)\n\t{\n\t\tflocation_t loc;\n\t\tchar *name = va(\"saves/%s/game.gsv\", savename);\n\t\tif (!FS_FLocateFile(name, FSLF_IFFOUND, &loc))\n\t\t\tCon_Printf(\"Couldn't find %s.\\n\", name);\n\t\telse if (!*loc.rawname || loc.offset)\n\t\t\tCon_Printf(\"%s is inside a package and cannot be used by the quake2 gamecode.\\n\", name);\n\t\telse\n\t\t{\n\t\t\tSVQ2_InitGameProgs();\n\t\t\tif (ge)\n\t\t\t\tge->ReadGame(loc.rawname);\n\t\t}\n\t}\n#endif\n\n\tsvs.gametype = gametype;\n\tSV_LoadLevelCache(savename, str, \"\", true);\n\n\tSV_GameLoaded(lp, slots, savename);\n\treturn true;\n}\n\nvoid SV_Loadgame_f (void)\n{\n#ifdef HAVE_CLIENT\n\tif (!Renderer_Started() && !isDedicated)\n\t{\n\t\tCbuf_AddText(va(\"wait;%s %s\\n\", Cmd_Argv(0), Cmd_Args()), Cmd_ExecLevel);\n\t\treturn;\n\t}\n\tif (sv.state == ss_dead)\n\t\tCL_Disconnect(NULL);\n#endif\n\n\tif (sv.state == ss_clustermode && MSV_ForwardToAutoServer())\n\t\t;\n\telse\n\t\tSV_Loadgame(Cmd_Argv(1));\n}\n\n#include \"fs.h\"\nvoid SV_DeleteSavegame_f (void)\n{\n\tconst char *savename = Cmd_Argv(1);\n\n\t//either saves/$FOO/info.fsv (rmtree) or $FOO.sav (single file)\n\t//extensions are strictly implicit to limit damage.\n\n\tconst char *fname;\n\tflocation_t loc;\n\n\tif (!*savename || *savename == '.' || strchr(savename, '/') || strchr(savename, '\\\\'))\n\t{\n\t\tCon_Printf(\"\\\"%s\\\" is not a valid saved game name to delete\\n\", savename);\n\t\treturn;\n\t}\n\n\tfname = va(\"saves/%s/info.fsv\", savename);\n\tif (FS_FLocateFile(fname, FSLF_IGNORELINKS|FSLF_DONTREFERENCE, &loc))\n\t{\n\t\tfname = va(\"saves/%s/\", savename);\n\t\tif (FS_RemoveTree(loc.search->handle, fname))\n\t\t\tCon_Printf(\"Removed %s\\n\", fname);\n\t\telse\n\t\t\tCon_Printf(\"Unable to remove %s\\n\", fname);\n\t}\n\n#ifndef QUAKETC\n\tfname = va(\"%s.sav\", savename);\n\tif (FS_FLocateFile(fname, FSLF_IGNORELINKS|FSLF_DONTREFERENCE, &loc))\n\t{\n\t\tif (loc.search->handle->RemoveFile && loc.search->handle->RemoveFile(loc.search->handle, fname))\n\t\t\tCon_Printf(\"Removed %s\\n\", fname);\n\t\telse\n\t\t\tCon_Printf(\"Unable to remove %s\\n\", fname);\n\t}\n#endif\n}\n#endif\n"
  },
  {
    "path": "engine/server/server.h",
    "content": "/*\nCopyright (C) 1996-1997 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n*/\n// server.h\n\n#define\tQW_SERVER\n\ntypedef enum {\n\tss_dead,\t\t\t// no map loaded\n\tss_clustermode,\n\tss_loading,\t\t\t// spawning level edicts\n\tss_active,\t\t\t// actively running\n\tss_cinematic\n} server_state_t;\n// some qc commands are only valid before the server has finished\n// initializing (precache commands, static sounds / objects, etc)\n\n#ifdef SVCHAT\ntypedef struct chatvar_s {\n\tchar varname[64];\n\tfloat value;\n\n\tstruct chatvar_s *next;\n} chatvar_t;\ntypedef struct {\n\tqboolean active;\n\tchar filename[64];\n\tedict_t *edict;\n\n\tchar maintext[1024];\n\tstruct\n\t{\n\t\tfloat tag;\n\t\tchar text[256];\n\t} option[6];\n\tint options;\n\n\tchatvar_t *vars;\n\n\tfloat time;\n} svchat_t;\n#endif\n\ntypedef struct {\n\tint netstyle;\n\tchar particleeffecttype[64];\n\tchar stain[3];\n\tqbyte radius;\n\tqbyte dlightrgb[3];\n\tqbyte dlightradius;\n\tqbyte dlighttime;\n\tqbyte dlightcfade[3];\n} svcustomtents_t;\n\ntypedef struct laggedpacket_s\n{\n\tdouble time;\n\tstruct laggedpacket_s *next;\n\tint length;\n\tunsigned char data[MAX_QWMSGLEN+10];\n} laggedpacket_t;\n\ntypedef struct\n{\n\tvec3_t\torigin;\n\tchar\tangles[3];\n\tqbyte\tmodelindex;\n\tqbyte\tframe;\n\tqbyte\tcolormap;\n\tqbyte\tskinnum;\n\tqbyte\teffects;\n\n\tqbyte\tscale;\n\tqbyte\ttrans;\n\tchar\tfatness;\n} mvdentity_state_t;\n\ntypedef struct\n{\n\tvec3_t position;\n\tunsigned short soundnum;\n\tqbyte volume;\n\tqbyte attenuation;\n} staticsound_state_t;\n\nextern entity_state_t *sv_staticentities;\nextern int sv_max_staticentities;\nextern staticsound_state_t *sv_staticsounds;\nextern int sv_max_staticsounds;\n\ntypedef struct server_s\n{\n\tqboolean\tactive;\t\t\t\t// false when server is going down\n\tserver_state_t\tstate;\t\t\t// precache commands are only valid during load\n\n\tfloat\t\tgamespeed;\t//time progression multiplier, fixed per-level.\n\tunsigned int csqcchecksum;\n\tqboolean\tmapchangelocked;\n\n#ifdef SAVEDGAMES\n\tchar\t\tloadgame_on_restart[MAX_QPATH];\t//saved game to load on map_restart\n\tdouble\t\tautosave_time;\n#endif\n\tdouble\t\ttime;\t\t\t//time passed since map (re)start\n\tdouble\t\tstarttime;\t\t//Sys_DoubleTime at the start of the map\n\tdouble\t\trestarttime;\t//for delayed map restarts - map will restart once sv.time reaches this stamp\n\tint framenum;\n\tint logindatabase;\n\n\tenum\n\t{\n\t\tPAUSE_EXPLICIT\t= 1, //someone hit pause\n\t\tPAUSE_SERVICE\t= 2, //we're running as a service and someone paused us rather than killing us.\n\t\tPAUSE_AUTO\t\t= 4\t//console is down in a singleplayer game.\n\t} paused, oldpaused;\n\tfloat\t\t\tpausedstart;\n\n\t//check player/eyes models for hacks\n\tunsigned\tmodel_player_checksum;\n\tunsigned\teyes_player_checksum;\n\n//\tchar\t\tname[64];\t\t\t// file map name (moved to svs, for restart)\n\tchar\t\tmapname[256];\t\t// text description of the map\n\tchar\t\tmodelname[MAX_QPATH];\t\t// maps/<name>.bsp, for model_precache[0]\n\n\tworld_t world;\n\n\tunion {\n#ifdef Q2SERVER\n\t\tstruct {\n\t\t\tconst char *configstring[Q2MAX_CONFIGSTRINGS];\n\t\t\tconst char\t*q2_extramodels[MAX_PRECACHE_MODELS];\t// NULL terminated\n\t\t\tconst char\t*q2_extrasounds[MAX_PRECACHE_SOUNDS];\t// NULL terminated\n\t\t};\n#endif\n\t\tstruct {\n#ifdef HAVE_LEGACY\n\t\t\tconst char\t*vw_model_precache[32];\n#endif\n\t\t\tconst char\t*model_precache[MAX_PRECACHE_MODELS];\t// NULL terminated\n\t\t\tconst char\t*particle_precache[MAX_SSPARTICLESPRE];\t// NULL terminated\n\t\t\tconst char\t*sound_precache[MAX_PRECACHE_SOUNDS];\t// NULL terminated\n\t\t};\n\t\tconst char *ptrs[1];\n\t} strings;\n\tqboolean\tstringsalloced;\t//if true, we need to free the string pointers safely rather than just memsetting them to 0\n\tstruct\n\t{\n\t\tconst char\t*str; //double dynamic. urgh, but allows it to be nice and long.\n\t\tvec3_t\t\tcolours;\n\t} *lightstyles;\n\tsize_t maxlightstyles;\t//limited to MAX_NET_LIGHTSTYLES\n\n#ifdef HEXEN2\n\tchar\t\th2miditrack[MAX_QPATH];\n\tqbyte\t\th2cdtrack;\n#endif\n\n\tpvec3_t\t\tskyroom_pos;\t//parsed from world._skyroom\n\tqboolean\tskyroom_pos_known;\n\n\tint\t\t\tallocated_client_slots;\t//number of slots available. (used mostly to stop single player saved games cacking up)\n\tint\t\t\tspawned_client_slots; //number of PLAYER slots which are active (ie: putclientinserver was called)\n\tint\t\t\tspawned_observer_slots;\n\n\tmodel_t\t*models[MAX_PRECACHE_MODELS];\n\n\tstruct client_s\t*skipbprintclient;\t//SV_BroadcastPrint skips this client\n\n\t// added to every client's unreliable buffer each frame, then cleared\n\tsizebuf_t\tdatagram;\n\tqbyte\t\tdatagram_buf[MAX_DATAGRAM];\n\n\t// added to every client's reliable buffer each frame, then cleared\n\tsizebuf_t\treliable_datagram;\n\tqbyte\t\treliable_datagram_buf[MAX_QWMSGLEN*2];\n\n\t// the multicast buffer is used to send a message to a set of clients\n\tsizebuf_t\tmulticast;\n\tqbyte\t\tmulticast_buf[MAX_QWMSGLEN*2];\n\n#ifdef NQPROT\n\tsizebuf_t\tnqdatagram;\n\tqbyte\t\tnqdatagram_buf[MAX_NQDATAGRAM];\n\n\tsizebuf_t\tnqreliable_datagram;\n\tqbyte\t\tnqreliable_datagram_buf[MAX_NQMSGLEN];\n\n\tsizebuf_t\tnqmulticast;\n\tqbyte\t\tnqmulticast_buf[MAX_NQMSGLEN];\n#endif\n\n#ifdef Q2SERVER\n\tsizebuf_t\tq2multicast[2];\t//0=little/legacy coords, 1=big coords (used for q2e compat)\n\tqbyte\t\tq2multicast_lcbuf[MAX_Q2MSGLEN];\n\tqbyte\t\tq2multicast_bcbuf[MAX_Q2MSGLEN*2];\n#endif\n\n\t// the master buffer is used for building log packets\n\tsizebuf_t\tmaster;\n\tqbyte\t\tmaster_buf[MAX_DATAGRAM];\n\n\t// the signon buffer will be sent to each client as they connect\n\t// traditionally includes the entity baselines, the static entities, etc\n\t// large levels will have >MAX_DATAGRAM sized signons, so\n\t// multiple signon messages are kept\n\t// fte only stores writebyted stuff in here. everything else is regenerated based upon the client's extensions.\n\tsizebuf_t\tsignon;\n\tint\t\t\tused_signon_space;\n\tqbyte\t\tsignon_buffer[MAX_OVERALLMSGLEN]; //flushed after every 512 bytes (two leading bytes says the size of the buffer).\n\n\tqboolean gamedirchanged;\n\n#ifdef QUAKESTATS\n\tqboolean haveitems2;\t//use items2 field instead of serverflags for the high bits of STAT_ITEMS\n#endif\n\n\n\n\n#ifdef MVD_RECORDING\n\tqboolean mvdrecording;\n#endif\n\n//====================================================\n//this lot is for serverside playback of mvds. Use QTV instead.\n#ifdef SERVER_DEMO_PLAYBACK\n\tqboolean mvdplayback;\n\tfloat realtime;\n\tvfsfile_t *demofile;\t//also signifies playing the thing.\n\n\tint lasttype;\n\tint lastto;\n\n//playback spikes (svc_nails/nails2)\n\tint numdemospikes;\n\tstruct {\n\t\tvec3_t org;\n\t\tqbyte id;\n\t\tqbyte pitch;\n\t\tqbyte yaw;\n\t\tqbyte modelindex;\n\t} demospikes[255];\n\n//playback of entities (svc_nails/nails2)\n\tmvdentity_state_t\t*demostate;\n\tmvdentity_state_t\t*demobaselines;\n\tint demomaxents;\n\tqboolean demostatevalid;\n\n//players\n\tstruct {\n\t\tint stats[MAX_CL_STATS];\n\t\tint pl;\n\t\tint ping;\n\t\tint frags;\n\t\tint userid;\n\t\tint weaponframe;\n\t\tchar\t\t\tuserinfo[MAX_INFO_STRING];\n\t\tvec3_t oldorg;\n\t\tvec3_t oldang;\n\t\tfloat updatetime;\n\t} recordedplayer[MAX_CLIENTS];\n\n//gamestate\n\tchar\t\tdemoinfo[MAX_SERVERINFO_STRING];\n\tchar\t\tdemmodel_precache[MAX_MODELS][MAX_QPATH];\t// NULL terminated\n\tchar\t\tdemsound_precache[MAX_SOUNDS][MAX_QPATH];\t// NULL terminated\n\tchar\t\tdemgamedir[64];\n\tchar\t\tdemname[64];\t\t\t// map name\n\n\tqboolean\tdemocausesreconnect;\t//this makes current clients go through the connection process (and when the demo ends too)\n\tsizebuf_t\tdemosignon;\n\tint\t\t\tnum_demosignon_buffers;\n\tint\t\t\tdemosignon_buffer_size[MAX_SIGNON_BUFFERS];\n\tqbyte\t\tdemosignon_buffers[MAX_SIGNON_BUFFERS][MAX_DATAGRAM];\n\tchar\t\tdemfullmapname[64];\n\n\tchar\t\t*demolightstyles[MAX_LIGHTSTYLES];\n#endif\n//====================================================\n//\tmovevars_t\tdemomovevars;\t//FIXME:!\n//end this lot... (demo playback)\n\n\tint num_static_entities;\n\tint num_static_sounds;\n\n\tsvcustomtents_t customtents[255];\n\n\tint\t\t*csqcentversion;//prevents ent versions from going backwards\n} server_t;\nvoid SV_WipeServerState(void);\n\n/*\n#define CS_EMPTY\t\t0\n#define CS_ZOMBIE\t\t(1u<<0)\t//just stops the slot from being reused for a bit.\n#define CS_CLUSTER\t\t(1u<<1)\t//is managed by the cluster host (and will appear on scoreboards).\n#define CS_SPAWNED\t\t(1u<<2)\t//has an active entity.\n#define CS_ACTIVE\t\t(1u<<3)\t//has a connection\n\n#define cs_free\t\t\t(CS_EMPTY)\n#define cs_zombie\t\t(CS_ZOMBIE)\n#define cs_loadzombie\t(CS_SPAWNED)\n#define cs_connected\t(CS_ACTIVE)\n#define cs_spawned\t\t(CS_ACTIVE|CS_SPAWNED)\n*/\n\ntypedef enum\n{\n\tcs_free,\t\t// can be reused for a new connection\n\tcs_zombie,\t\t// client has been disconnected, but don't reuse connection for a couple seconds. entity was already cleared.\n\tcs_loadzombie,\t// slot reserved for a client. the player's entity may or may not be known (istobeloaded says that state). parms _ARE_ known.\n\tcs_connected,\t// has been assigned to a client_t, but not in game yet\n\tcs_spawned\t\t// client is fully in game\n} client_conn_state_t;\n\ntypedef struct\n{\n\t// received from client\n\n\t// reply\n\tdouble\t\t\t\tsenttime;\t\t//time we sent this frame to the client, for ping calcs (realtime)\n\tint\t\t\t\t\tsequence;\t\t//the outgoing sequence - without mask, meaning we know if its current or stale\n\tfloat\t\t\t\tping_time;\t\t//how long it took for the client to ack it, may be negative\n\tfloat\t\t\t\tmove_msecs;\t\t//\n\tint\t\t\t\t\tpacketsizein;\t//amount of data received for this frame\n\tint\t\t\t\t\tpacketsizeout;\t//amount of data that was sent in the frame\n\n\tpacket_entities_t\tqwentities;\t\t//package containing entity states that were sent in this frame, for deltaing\n\n\tstruct resendinfo_s\n\t{\n\t\tunsigned int entnum;\n\t\tunsigned int bits;\t//delta (fte or dpp5+)\n\t\tquint64_t flags;\t//csqc\n\t} *resend;\n\tunsigned int\t\tnumresend;\n\tunsigned int\t\tmaxresend;\n\n\tunsigned short\t\tresendstats[32];//the number of each entity that was sent in this frame\n\tunsigned int\t\tnumresendstats;\t//the bits of each entity that were sent in this frame\n\n\tshort\t\t\t\tbaseangles[MAX_SPLITS][3];\n\tunsigned int\t\tbaseanglelocked[MAX_SPLITS];\n\n\t//antilag\n\t//these are to recalculate the player's origin without old knockbacks nor teleporters, to give more accurate weapon start positions (post-command).\n\tvec3_t\t\t\t\tpmorigin;\n\tvec3_t\t\t\t\tpmvelocity;\n\tint\t\t\t\t\tpmtype;\n\tunsigned int\t\tpmjumpheld:1;\n\tunsigned int\t\tpmonladder:1;\n\tfloat\t\t\t\tpmwaterjumptime;\n\tusercmd_t\t\t\tcmd;\n\t//these are old positions of players, to give more accurate victim positions\n\tlaggedentinfo_t\t\tlaggedplayer[MAX_CLIENTS];\n\tunsigned int\t\tnumlaggedplayers;\n\tfloat\t\t\t\tlaggedtime;\t//sv.time of when this frame was sent\n} client_frame_t;\n\n#ifdef Q2SERVER\ntypedef struct\t//merge?\n{\n\tint\t\t\t\t\tareabytes;\n\tqbyte\t\t\t\tareabits[MAX_Q2MAP_AREAS/8];\t\t// portalarea visibility bits\n\tq2player_state_t\tps[MAX_SPLITS];\t\t//yuck\n\tint\t\t\t\t\tclientnum[MAX_SPLITS];\n\tint\t\t\t\t\tnum_entities;\n\tint\t\t\t\t\tfirst_entity;\t\t// into the circular sv_packet_entities[]\n\tint\t\t\t\t\tsenttime;\t\t\t// for ping calculations\n\tfloat\t\t\t\tping_time;\n} q2client_frame_t;\n#endif\n\n#define MAX_BACK_BUFFERS 16\n\nenum\n{\n\tPRESPAWN_INVALID=0,\n\tPRESPAWN_PROTOCOLSWITCH,\t//nq drops unreliables until reliables are acked. this gives us a chance to drop any clc_move packets with formats from the previous map\n\tPRESPAWN_SERVERINFO,\n#ifdef MVD_RECORDING\n\tPRESPAWN_CSPROGS,\t\t\t//demos contain a copy of the csprogs.\n#endif\n\tPRESPAWN_SOUNDLIST,\t\t\t//nq skips these\n#ifdef HAVE_LEGACY\n\tPRESPAWN_VWEPMODELLIST,\t\t//qw ugly extension.\n#endif\n\tPRESPAWN_MODELLIST,\n\tPRESPAWN_NQSIGNON1,\t\t//gotta send all the trickled model+sounds before this is sent.\n\tPRESPAWN_MAPCHECK,\t\t\t//wait for old prespawn command\n\tPRESPAWN_PARTICLES,\n\tPRESPAWN_CUSTOMTENTS,\n\tPRESPAWN_SIGNON_BUF,\n\tPRESPAWN_SPAWNSTATIC,\n\tPRESPAWN_AMBIENTSOUND,\n\tPRESPAWN_BASELINES,\n\tPRESPAWN_SPAWN,\n\tPRESPAWN_BRUSHES,\n\tPRESPAWN_COMPLETED\n};\n\nenum\n{\t//'soft' limits that result in warnings if the client's protocol is too limited.\n\tPLIMIT_ENTITIES = 1u<<0,\n\tPLIMIT_MODELS = 1u<<1,\n\tPLIMIT_SOUNDS = 1u<<2\n};\n\n//client_t->spec_print + sv_specprint\n#define SPECPRINT_CENTERPRINT\t0x1\n#define SPECPRINT_SPRINT\t0x2\n#define SPECPRINT_STUFFCMD\t0x4\n\n#define STUFFCMD_IGNOREINDEMO (   1<<0) // do not put in mvd demo\n#define STUFFCMD_DEMOONLY     (   1<<1) // put in mvd demo only\n#define STUFFCMD_BROADCAST    (   1<<2) // everyone sees it.\n#define STUFFCMD_UNRELIABLE   (   1<<3) // someone might not see it. oh well.\n\n#define FIXANGLE_NO\t\t0\t//don't override anything\n#define FIXANGLE_AUTO\t1\t//guess (initial=fixed, spammed=fixed, sporadic=relative)\n#define FIXANGLE_DELTA\t2\t//send a relative change\n#define FIXANGLE_FIXED\t3\t//send a absolute angle.\n\nenum serverprotocols_e\n{\n\tSCP_BAD,\t//don't send (a bot)\n\tSCP_QUAKEWORLD,\n\tSCP_QUAKE2,\n\tSCP_QUAKE2EX,\n\tSCP_QUAKE3,\n\t//all the below are considered netquake clients.\n\tSCP_NETQUAKE,\n\t//bjp1, bjp2\n\tSCP_BJP3,\t\t//16bit angles,model+sound indexes. nothing else (assume raised ent limits too).\n\tSCP_FITZ666,\n\t//dp5\n\tSCP_DARKPLACES6,\n\tSCP_DARKPLACES7\t//extra prediction stuff\n\t//note, nq is nq+\n};\n\ntypedef struct client_s\n{\n\tclient_conn_state_t\tstate;\n\n\tunsigned int\tprespawn_stage;\n\tunsigned int\tprespawn_idx;\n\tunsigned int\tprespawn_idx2;\n\tqboolean\t\tprespawn_allow_modellist;\n\tqboolean\t\tprespawn_allow_soundlist;\n\n\tint\t\t\t\tspectator;\t\t\t// non-interactive\n\tint\t\t\t\tredirect;\t\t\t//1=redirected because full, 2=cluster transfer\n\n\tqboolean\t\tsendinfo;\t\t\t// at end of frame, send info to all\n\t\t\t\t\t\t\t\t\t\t// this prevents malicious multiple broadcasts\n\tfloat\t\t\tlastnametime;\t\t// time of last name change\n\tint\t\t\t\tlastnamecount;\t\t// time of last name change\n\tunsigned\t\tchecksum;\t\t\t// checksum for calcs\n\tqboolean\t\tdrop;\t\t\t\t// lose this guy next opportunity\n\tint\t\t\t\tlossage;\t\t\t// loss percentage\n\n\tint\t\t\t\tchallenge;\n\tint\t\t\t\tuserid;\t\t\t\t// identifying number\n\tinfobuf_t\t\tuserinfo;\t\t\t// all of the user's various settings\n\tinfosync_t\t\tinfosync;\t\t\t// information about the infos that the client still doesn't know (server and multiple clients).\n\tchar\t\t\t*transfer;\n\n\tunsigned int\tbaseanglelock;\t\t// lock the player angles to base (until baseangle sequence is acked)\n\tshort\t\t\tbaseangles[3];\t\t// incoming angle inputs are relative to this value.\n\tusercmd_t\t\tlastcmd;\t\t\t// for filling in big drops and partial predictions\n\tdouble\t\t\tlocaltime;\t\t\t// of last message\n\tqboolean jump_held;\n\tunsigned int\tlockanglesseq;\t\t//mod is spamming angle changes, don't do relative changes. outgoing sequence. v_angles isn't really known until netchan.incoming_acknowledged>=lockangles\n\n\tfloat\t\t\tmaxspeed;\t\t\t// localized maxspeed\n\tfloat\t\t\tentgravity;\t\t\t// localized ent gravity\n\n\tint viewent;\t\t//fake the entity positioning.\n\tint clientcamera;\t//cache for dp_sv_clientcamera.\n\n\tedict_t\t\t\t*edict;\t\t\t\t// EDICT_NUM(clientnum+1)\n//additional game modes use additional edict pointers. this ensures that references are crashes.\n#ifdef Q2SERVER\n\tq2edict_t\t\t*q2edict;\t\t\t\t// EDICT_NUM(clientnum+1)\n#endif\n#ifdef HLSERVER\n\tstruct hledict_s\t*hledict;\n#endif\n\n\tint\t\t\t\tplayercolor;\n\tint\t\t\t\tplayerclass;\n\tchar\t\t\tteambuf[32];\n\tchar\t\t\t*team;\n\tchar\t\t\t*name;\n\tchar\t\t\tnamebuf[32];\t\t\t// for printing to other people\n\t\t\t\t\t\t\t\t\t\t// extracted from userinfo\n\tchar\t\t\tguid[64]; /*+2 for split+pad*/\n\tint\t\t\t\tmessagelevel;\t\t// for filtering printed messages\n\tint\t\t\t\tautoaimdot;\t\t\t//smallest dotproduct allowed for autoaim\n#ifdef HAVE_LEGACY\n\tfloat\t\t\t*dp_ping;\n\tfloat\t\t\t*dp_pl;\n#endif\n\n\t// the datagram is written to after every frame, but only cleared\n\t// when it is sent out to the client.  overflow is tolerated.\n\tsizebuf_t\t\tdatagram;\n\tqbyte\t\t\tdatagram_buf[MAX_OVERALLMSGLEN/2];\n\n\t// back buffers for client reliable data\n\tsizebuf_t\tbackbuf;\n\tint\t\t\tnum_backbuf;\n\tint\t\t\tbackbuf_size[MAX_BACK_BUFFERS];\n\tqbyte\t\tbackbuf_data[MAX_BACK_BUFFERS][MAX_BACKBUFLEN];\n\n\tdouble\t\t\tconnection_started;\t// or time of disconnect for zombies\n\tqboolean\t\tsend_message;\t\t// set on frames a datagram arived on\n\n\tlaggedentinfo_t\tlaggedents[MAX_CLIENTS];\n\tunsigned int\tlaggedents_count;\n\tfloat\t\t\tlaggedents_frac;\n\tfloat\t\t\tlaggedents_time;\n\n// spawn parms are carried from level to level\n\tfloat\t\t\tspawn_parms[NUM_SPAWN_PARMS];\n\tchar\t\t\t*spawn_parmstring;\t//qc-specified data.\n\tchar\t\t\t*spawninfo;\t\t\t//entity-formatted data (for hexen2's ClientReEnter)\n\tfloat\t\t\tspawninfotime;\n\tfloat\t\t\tnextservertimeupdate;\t//next time to send STAT_TIME\n\tfloat\t\t\tlastoutgoingphysicstime;//sv.world.physicstime of the last outgoing message.\n\n// client known data for deltas\n\tint\t\t\t\told_frags;\n\n\tunsigned int\tpendingstats[((MAX_CL_STATS*2) + 31)>>5];\t//these are the stats that have changed and that need sending/resending\n\tint\t\t\t\tstatsi[MAX_CL_STATS];\n\tfloat\t\t\tstatsf[MAX_CL_STATS];\n\tchar\t\t\t*statss[MAX_CL_STATS];\n\tchar\t\t\t*centerprintstring;\n\n\tstruct\n\t{\n\t\tqboolean active;\n\t\tchar *header;\n\t\tdouble nextsend;\t//qex is a one-off, other clients need spam.\n\t\tsize_t numopts, maxopts, selected;\n\t\tstruct\n\t\t{\n\t\t\tchar *text;\n\t\t\tint impulse;\n\t\t} *opt;\n\n\t\tint oldmove[2];\n\t} prompt;\n\n\tunion{\t//save space\n\t\tclient_frame_t\t*frames;\t// updates can be deltad from here\n#ifdef Q2SERVER\n\t\tq2client_frame_t\t*q2frames;\n#endif\n#ifdef Q3SERVER\n\t\tvoid\t*q3frames;\n#endif\n\t} frameunion;\n\tpacket_entities_t sentents;\n\tunsigned int\t*pendingdeltabits;\n\tquint64_t\t\t*pendingcsqcbits;\n\tunsigned int\tnextdeltaindex;\t\t\t//splurged round-robin to deal with overflows\n\tunsigned int\tnextcsqcindex;\t\t\t//splurged round-robin\n\t#define SENDFLAGS_PRESENT 0x1u\t//this entity is present on that client\n\t#define SENDFLAGS_REMOVED 0x2u\t//to handle remove packetloss\n\t#define SENDFLAGS_RESERVED (SENDFLAGS_PRESENT|SENDFLAGS_REMOVED)\n\t#define SENDFLAGS_SHIFT 2u\n\t#define SENDFLAGS_USABLE (~(quint64_t)SENDFLAGS_RESERVED)\t//this number of bits are actually safe in a float. not all together, but otherwise safe.\n\n#ifdef HAVE_LEGACY\n\tchar\t\t\t*dlqueue;\t\t\t//name\\name delimited list of files to ask the client to download.\n#endif\n\tchar\t\t\tdownloadfn[MAX_QPATH];\n\tvfsfile_t\t\t*download;\t\t\t// file being downloaded\n\tqofs_t\t\t\tdownloadsize;\t\t// total bytes\n\tqofs_t\t\t\tdownloadcount;\t\t// bytes sent\n\n#ifdef NQPROT\n\tqofs_t\t\t\tdownloadacked;\t\t//DP-specific\n\tqofs_t\t\t\tdownloadstarted;\t//DP-specific\n#endif\n\n\tint\t\t\t\tspec_track;\t\t\t// entnum of player tracking\n\n\tunsigned int\tspec_print;\t\t\t//bitfield for things this spectator should see that were directed to the player that they're tracking.\n\n#ifdef Q3SERVER\n\tint\tgamestatesequence;\t//the sequence number the initial gamestate was sent in.\n\n\tint last_client_command_num;\n\tchar last_client_command[1024];\n\n\t//quake3 does reliables only via this mechanism. basically just like q1's stuffcmds.\n\tint server_command_ack;\t\t\t\t//number known to have been received.\n\tint server_command_sequence;\t\t//number available.\n\tchar server_commands[256][1024];\t\t//the commands, to deal with resends\n#endif\n\n\t//true/false/persist\n\tunsigned int\tpenalties;\n\tqbyte\t\t\tistobeloaded;\t//spawnparms are known.\n\tqboolean\t\tspawned;\t\t//gamecode knows about it.\n\n\tdouble\t\t\tfloodprotmessage;\n\tdouble\t\t\tlastspoke;\n \tdouble\t\t\tlockedtill;\n\tfloat\t\t\tjoinobservelockeduntil;\n\n\tqboolean\t\tupgradewarn;\t\t// did we warn him?\n\n\tvfsfile_t\t\t*upload;\n\tchar\t\t\tuploadfn[MAX_QPATH];\n\tnetadr_t\t\tsnap_from;\n\tqboolean\t\tremote_snap;\n\n//===== NETWORK ============\n\tint\t\t\t\tchokecount;\n\tqboolean\t\twaschoked;\n\tint\t\t\t\tdelta_sequence;\t\t// -1 = no compression\n\tint\t\t\t\tlast_sequence;\t\t//last inputframe sequence received\n\tnetchan_t\t\tnetchan;\n\tqboolean\t\tisindependant;\n\n\tint\t\t\t\tlastsequence_acknowledged;\n\n#ifdef VOICECHAT\n\tunsigned int voice_read;\t/*place in ring*/\n\tunsigned char voice_mute[(MAX_CLIENTS+7)/8];\n\tqboolean voice_active;\n\tenum\n\t{\n\t\t/*note - when recording an mvd, only 'all' will be received by non-spectating viewers. all other chat will only be heard when spectating the receiver(or sender) of said chat*/\n\n\t\t/*should we add one to respond to the last speaker? or should that be an automagic +voip_reply instead?*/\n\t\tVT_TEAM,\n\t\tVT_ALL,\n\t\tVT_NONMUTED,\t/*cheap, but allows custom private channels with no external pesters*/\n\t\tVT_SPECSELF,\t/*sends to self, audiable to people spectating self*/\n\t\tVT_PLAYERSLOT0\n\t\t/*player0+...*/\n\t} voice_target;\n#endif\n\n#ifdef SVCHAT\n\tsvchat_t chat;\n#endif\n#ifdef SVRANKING\n\tint rankid;\n\n\tint\tkills;\n\tint\tdeaths;\n\n\tdouble\t\t\tstats_started;\n#endif\n\n\tqboolean\t\tcsqcactive;\n\tqboolean\t\tpextknown;\n\tunsigned int\tfteprotocolextensions;\n\tunsigned int\tfteprotocolextensions2;\n\tunsigned int\tezprotocolextensions1;\n\tunsigned int\tzquake_extensions;\n\tunsigned int\tmax_net_ents; /*highest entity number the client can receive (limited by either protocol or client's buffer size)*/\n\tunsigned int\tmax_net_staticents; /*limit to the number of static ents supported by the client*/\n\tunsigned int\tmax_net_clients; /*max number of player slots supported by the client */\n\tunsigned int\tmaxmodels; /*max models supported by whatever the protocol is*/\n\n\tenum serverprotocols_e protocol;\n\tunsigned int\tsupportedprotocols;\n\tqboolean proquake_angles_hack;\t//expect 16bit client->server angles .\n\tqboolean qex;\t//qex sends strange clc inputs and needs workarounds for its prediction. it also favours fitzquake's protocol but violates parts of it.\n\n\tunsigned int lastruncmd;\t//for non-qw physics. timestamp they were last run, so switching between physics modes isn't a (significant) cheat\n\tunsigned int hoverms;\t//purely for sv_showpredloss to avoid excessive spam\n//speed cheat testing\n#define NEWSPEEDCHEATPROT\n\tfloat msecs;\n#ifndef NEWSPEEDCHEATPROT\n\tint msec_cheating;\n\tfloat last_check;\n#endif\n\n\tqboolean gibfilter;\n\n\tint trustlevel;\n\n\tvec3_t\tspecorigin;\t//mvds need to use a different origin from the one QC has.\n\tvec3_t\tspecvelocity;\n\n\tint language;\t//the clients language\n\n//\tstruct {\n//\t\tqbyte vweap;\n//\t} otherclientsknown[MAX_CLIENTS];\t//updated as needed. Flag at a time, or all flags.\n\n\tstruct client_s *controller;\t/*first in splitscreen chain, NULL=nosplitscreen*/\n\tstruct client_s *controlled;\t/*next in splitscreen chain*/\n\tqbyte seat;\n\n\t/*these are the current rates*/\n\tfloat ratetime;\n\tfloat inrate;\n\tfloat outrate;\n\n\tint rate;\n\tint drate;\n\n\tnetadr_t realip;\n\tint realip_status;\n\tint realip_num;\n\tint realip_ping;\n\tchar *reversedns;\n\n\tunsigned int plimitwarned;\n\n\tfloat delay;\n\tlaggedpacket_t *laggedpacket;\n\tlaggedpacket_t *laggedpacket_last;\n\n\tsize_t lastseen_count;\n\tfloat *lastseen_time;\t//timer for cullentities_trace, so we can get away with fewer traces per test\n\n#ifdef VM_Q1\n\tint hideentity;\n\tqboolean hideplayers;\n#endif\n} client_t;\n\n#if defined(NQPROT) || defined(Q2SERVER) || defined(Q3SERVER)\n#define ISQWCLIENT(cl) ((cl)->protocol == SCP_QUAKEWORLD)\n#define ISQ2CLIENT(cl) ((cl)->protocol >= SCP_QUAKE2 && (cl)->protocol <= SCP_QUAKE2EX)\n#define ISQ3CLIENT(cl) ((cl)->protocol == SCP_QUAKE3)\n#define ISNQCLIENT(cl) ((cl)->protocol >= SCP_NETQUAKE)\n#define ISDPCLIENT(cl) ((cl)->protocol >= SCP_DARKPLACES6)\n#else\n#define ISQWCLIENT(cl) ((cl)->protocol != SCP_BAD)\n#define ISQ2CLIENT(cl) false\n#define ISQ3CLIENT(cl) false\n#define ISNQCLIENT(cl) false\n#define ISDPCLIENT(cl) false\n#endif\n\n// a client can leave the server in one of four ways:\n// dropping properly by quiting or disconnecting\n// timing out if no valid messages are received for timeout.value seconds\n// getting kicked off by the server operator\n// a program error, like an overflowed reliable buffer\n\n\n\n\n//=============================================================================\n\n//mvd stuff\n#ifdef MVD_RECORDING\n#define\tMSG_BUF_SIZE 8192\n\ntypedef struct\n{\n\tvec3_t\torigin;\n\tvec3_t\tangles;\n\tint\t\tweaponframe;\n\tint\t\tskinnum;\n\tint\t\tmodel;\n\tint\t\teffects;\n}\tdemoinfo_t;\n\ntypedef struct\n{\n\tdemoinfo_t\tinfo;\n\tfloat\t\tsec;\n\tint\t\t\tparsecount;\n\tqboolean\tfixangle;\n\tvec3_t\t\tangle;\n\tfloat\t\tcmdtime;\n\tint\t\t\tflags;\n\tint\t\t\tframe;\n} demo_client_t;\n\ntypedef struct {\n\tqbyte type;\n\tqbyte full;\n\tint to;\n\tint size;\n\tqbyte data[1]; //gcc doesn't allow [] (?)\n} mvd_header_t;\n\ntypedef struct\n{\n\tsizebuf_t sb;\n\tint\t\tbufsize;\n\tmvd_header_t *h;\n} demobuf_t;\n\ntypedef struct\n{\n\tdemo_client_t clients[MAX_CLIENTS];\n\tdouble\t\ttime;\n\tdemobuf_t\tbuf;\n} demo_frame_t;\n\ntypedef struct {\n\tqbyte\t*data;\n\tint\t\tstart, end, last;\n\tint\t\tmaxsize;\n} dbuffer_t;\n\n#define DEMO_FRAMES 64\t//why is this not just 2?\n#define DEMO_FRAMES_MASK (DEMO_FRAMES - 1)\n\ntypedef struct\n{\n//\tdemobuf_t\t*dbuf;\n//\tdbuffer_t\tdbuffer;\n\tsizebuf_t\tdatagram;\n\tqbyte\t\tdatagram_data[MSG_BUF_SIZE];\n\tint\t\t\tlastto;\n\tint\t\t\tlasttype;\n\tdouble\t\ttime, pingtime;\n\tint\t\t\tstatsi[MAX_CLIENTS][MAX_CL_STATS]; // ouch!\n\tfloat\t\tstatsf[MAX_CLIENTS][MAX_CL_STATS]; // ouch!\n\tchar\t\t*statss[MAX_CLIENTS][MAX_CL_STATS]; // ouch!\n\tclient_t\trecorder;\n\tqboolean\tplayerreset[MAX_CLIENTS];\t//will ensure that the model etc is written when this player is next written.\n\tqboolean\tfixangle[MAX_CLIENTS];\n\tfloat\t\tfixangletime[MAX_CLIENTS];\n\tvec3_t\t\tangles[MAX_CLIENTS];\n\tqboolean\tresetdeltas;\n\tint\t\t\tparsecount;\n\tint\t\t\tlastwritten;\n\tdemo_frame_t\tframes[DEMO_FRAMES];\n\tdemoinfo_t\tinfo[MAX_CLIENTS];\n\tqbyte\t\tbuffer[20*MAX_QWMSGLEN];\n\tint\t\t\tbufsize;\n\tint\t\t\tforceFrame;\n\n\tstruct mvddest_s *dest;\n} demo_t;\n#endif\n\n//=============================================================================\n\n#define SVSTATS_PERIOD 10\ntypedef struct\n{\n\tdouble\tactive;\n\tdouble\tidle;\n\tint\t\tcount;\n\tint\t\tpackets;\n\tdouble\tmaxresponse;\t//longest (active) frame time within the current period\n\tint\t\tmaxpackets;\t\t//max packet count in a single frame\n\n\tdouble\tlatched_time;\t//time that the current period ends\n\tdouble\tlatched_active;\t//active time in the last period\n\tdouble\tlatched_idle;\n\tint\t\tlatched_count;\n\tint\t\tlatched_packets;\n\tint\t\tlatched_maxpackets;\n\tdouble\tlatched_maxresponse;\n} svstats_t;\n\n// MAX_CHALLENGES is made large to prevent a denial\n// of service attack that could cycle all of them\n// out before legitimate users connected\n#define\tMAX_CHALLENGES\t1024\n\ntypedef struct\n{\n\tnetadr_t\tadr;\n\tint\t\t\tchallenge;\n\tint\t\t\ttime;\n} challenge_t;\n\ntypedef struct bannedips_s {\n\tunsigned int banflags;\n\tstruct bannedips_s *next;\n\tnetadr_t\tadr;\n\tnetadr_t\tadrmask;\n\ttime_t expiretime;\n\tchar reason[1];\n} bannedips_t;\n\ntypedef enum {\n\tGT_PROGS,\t//q1, qw, h2 are similar enough that we consider it only one game mode. (We don't support the h2 protocol)\n\tGT_Q1QVM,\n\tGT_HALFLIFE,\n\tGT_QUAKE2,\t//q2 servers run from a q2 game dll\n\tGT_QUAKE3,\t//q3 servers run off the q3 qvm api\n#ifdef VM_LUA\n\tGT_LUA,\t\t//for the luls\n#endif\n\tGT_MAX\n} gametype_e;\n\ntypedef struct levelcache_s {\n\tstruct levelcache_s *next;\n\tchar *mapname;\n\tgametype_e gametype;\n\tunsigned char savedplayers[(MAX_CLIENTS+7)>>3];\t//bitmask to say which players are actually stored in the cache. so that restarts can restore.\n} levelcache_t;\n\n#ifdef TCPCONNECT\ntypedef struct svtcpstream_s {\n\tint socketnum;\n\tint inlen;\n\tqboolean waitingforprotocolconfirmation;\n\tchar inbuffer[1500];\n\tfloat timeouttime;\n\tnetadr_t remoteaddr;\n\tstruct svtcpstream_s *next;\n} svtcpstream_t;\n#endif\n\ntypedef struct server_static_s\n{\n\tgametype_e\tgametype;\n\tint\t\t\tspawncount;\t\t\t// number of servers spawned since start,\n\t\t\t\t\t\t\t\t\t// used to check late spawns\n\tint framenum;\t//physics frame number for out-of-sequence thinks (fix for slow rockets)\n\tint clusterserverid;\t\t\t// which server we are in the cluster. for gamecode to track with stats.\n\n\tstruct ftenet_connections_s *sockets;\n\n\tint\t\t\tallocated_client_slots;\t//number of entries in the clients array\n\tclient_t\t*clients;\t\t\t//[svs.allocated_client_slots]\n\tint\t\t\tserverflags;\t\t// episode completion information\n\n\tdouble\t\tlast_heartbeat;\n\tint\t\t\theartbeat_sequence;\n\tsvstats_t\tstats;\n\n\tinfobuf_t\tinfo;\n\tinfobuf_t\tlocalinfo;\n\n\t// log messages are used so that fraglog processes can get stats\n\tint\t\t\tlogsequence;\t// the message currently being filled\n\tdouble\t\tlogtime;\t\t// time of last swap\n\n#define FRAGLOG_BUFFERS\t8\n\tsizebuf_t\tlog[FRAGLOG_BUFFERS];\n\tqbyte\t\tlog_buf[FRAGLOG_BUFFERS][MAX_DATAGRAM];\n\n\tchallenge_t\tchallenges[MAX_CHALLENGES];\t// to prevent invalid IPs from connecting\n\n\tbannedips_t *bannedips;\n\n\tchar progsnames[MAX_PROGS][MAX_QPATH];\n\tprogsnum_t progsnum[MAX_PROGS];\n\tint numprogs;\n\n\tstruct netprim_s netprim;\n\n\tlaggedpacket_t *free_lagged_packet;\n\tpacket_entities_t entstatebuffer; /*just a temp buffer*/\n\n\tchar\t\tname[64];\t\t\t// map name (base filename). static because of restart command after disconnecting.\n\tlevelcache_t *levcache;\n\n\tstruct frameendtasks_s\n\t{\n\t\tstruct frameendtasks_s *next;\n\t\tvoid(*callback)(struct frameendtasks_s *task);\n\t\tvoid *ctxptr;\n\t\tintptr_t ctxint;\n\t\tchar data[1];\n\t} *frameendtasks;\n} server_static_t;\n\n//=============================================================================\n\n/*\n// edict->movetype values\n#define\tMOVETYPE_NONE\t\t\t0\t\t// never moves\n#define\tMOVETYPE_ANGLENOCLIP\t1\n#define\tMOVETYPE_ANGLECLIP\t\t2\n#define\tMOVETYPE_WALK\t\t\t3\t\t// gravity\n#define\tMOVETYPE_STEP\t\t\t4\t\t// gravity, special edge handling\n#define\tMOVETYPE_FLY\t\t\t5\n#define\tMOVETYPE_TOSS\t\t\t6\t\t// gravity\n#define\tMOVETYPE_PUSH\t\t\t7\t\t// no clip to world, push and crush\n#define\tMOVETYPE_NOCLIP\t\t\t8\n#define\tMOVETYPE_FLYMISSILE\t\t9\t\t// extra size to monsters\n#define\tMOVETYPE_BOUNCE\t\t\t10\n#define MOVETYPE_BOUNCEMISSILE\t11\t\t// bounce w/o gravity\n#define MOVETYPE_FOLLOW\t\t\t12\t\t// track movement of aiment\n#define MOVETYPE_H2PUSHPULL\t\t13\t\t// pushable/pullable object\n#define MOVETYPE_H2SWIM\t\t\t14\t\t// should keep the object in water\n#define MOVETYPE_PHYSICS\t\t32\n#define MOVETYPE_FLY_WORLDONLY\t33\n\n// edict->solid values\n#define\tSOLID_NOT\t\t\t\t0\t\t// no interaction with other objects\n#define\tSOLID_TRIGGER\t\t\t1\t\t// touch on edge, but not blocking\n#define\tSOLID_BBOX\t\t\t\t2\t\t// touch on edge, block\n#define\tSOLID_SLIDEBOX\t\t\t3\t\t// touch on edge, but not an onground\n#define\tSOLID_BSP\t\t\t\t4\t\t// bsp clip, touch on edge, block\n#define\tSOLID_PHASEH2\t\t\t5\n#define\tSOLID_CORPSE\t\t\t5\n#define SOLID_LADDER\t\t\t20\t\t//dmw. touch on edge, not blocking. Touching players have different physics. Otherwise a SOLID_TRIGGER. deprecated. use solid_bsp and skin=-16\n\n#define\tDAMAGE_NO\t\t\t\t0\n#define\tDAMAGE_YES\t\t\t\t1\n#define\tDAMAGE_AIM\t\t\t\t2\n*/\n\n#define PVSF_NORMALPVS\t\t0x0\n#define PVSF_NOTRACECHECK\t0x1\n#define PVSF_USEPHS\t\t\t0x2\n#define PVSF_IGNOREPVS\t\t0x3\n#define PVSF_MODE_MASK\t\t0x3\n#define PVSF_NOREMOVE\t\t0x80\n\n// entity effects\n\n//define\tEF_BRIGHTFIELD\t\t\t1\n//define\tEF_MUZZLEFLASH \t\t\t2\n//#define\tEF_BRIGHTLIGHT \t\t\t(1<<2)\n//#define\tEF_DIMLIGHT \t\t\t(1<<4)\n\n//#define\tEF_FULLBRIGHT\t\t\t512\n\n\n#define\tSPAWNFLAG_NOT_EASY\t\t\t(1<<8)\n#define\tSPAWNFLAG_NOT_MEDIUM\t\t(1<<9)\n#define\tSPAWNFLAG_NOT_HARD\t\t\t(1<<10)\n#define\tSPAWNFLAG_NOT_DEATHMATCH\t(1<<11)\n\n#define SPAWNFLAG_NOT_H2PALADIN\t\t\t(1<<8)\n#define SPAWNFLAG_NOT_H2CLERIC\t\t\t(1<<9)\n#define SPAWNFLAG_NOT_H2NECROMANCER\t\t(1<<10)\n#define SPAWNFLAG_NOT_H2THEIF\t\t\t(1<<11)\n#define\tSPAWNFLAG_NOT_H2EASY\t\t\t(1<<12)\n#define\tSPAWNFLAG_NOT_H2MEDIUM\t\t\t(1<<13)\n#define\tSPAWNFLAG_NOT_H2HARD\t\t    (1<<14)\n#define\tSPAWNFLAG_NOT_H2DEATHMATCH\t\t(1<<15)\n#define SPAWNFLAG_NOT_H2COOP\t\t\t(1<<16)\n#define SPAWNFLAG_NOT_H2SINGLE\t\t\t(1<<17)\n\n#if 0//ndef Q2SERVER\ntypedef enum multicast_e\n{\n\tMULTICAST_ALL,\n\tMULTICAST_PHS,\n\tMULTICAST_PVS,\n\tMULTICAST_ALL_R,\n\tMULTICAST_PHS_R,\n\tMULTICAST_PVS_R\n} multicast_t;\n#endif\n\n\n//shared with qc\n#define MSG_PRERELONE\t-100\n#define\tMSG_BROADCAST\t0\t\t// unreliable to all\n#define\tMSG_ONE\t\t\t1\t\t// reliable to one (msg_entity)\n#define\tMSG_ALL\t\t\t2\t\t// reliable to all\n#define\tMSG_INIT\t\t3\t\t// write to the init string\n#define\tMSG_MULTICAST\t4\t\t// for multicast()\n#define MSG_CSQC\t\t5\t\t// for writing csqc entities\n\n//============================================================================\n\nextern\tcvar_t\tsv_mintic, sv_maxtic, sv_limittics;\nextern\tcvar_t\tsv_maxspeed;\nextern\tcvar_t\tsv_antilag;\nextern\tcvar_t\tsv_antilag_frac;\nextern\tcvar_t\tsv_nqplayerphysics;\n\nvoid SV_Master_ReResolve(void);\nvoid SV_Master_Shutdown(void);\nvoid SV_Master_Heartbeat (void);\nqboolean SV_Master_AddressIsMaster(netadr_t *adr);\nvoid SV_Master_HeartbeatResponse(netadr_t *adr, const char *challenge);\nextern\tcvar_t\tsv_antilag_frac;\n\nextern\tcvar_t\tpr_ssqc_progs;\nextern\tcvar_t\tsv_csqcdebug;\nextern\tcvar_t\tspawn;\nextern\tcvar_t\tteamplay;\nextern\tcvar_t\tdeathmatch;\nextern\tcvar_t\tcoop;\nextern\tcvar_t\tfraglimit;\nextern\tcvar_t\ttimelimit;\n\nextern\tserver_static_t\tsvs;\t\t\t\t// persistant server info\nextern\tserver_t\t\tsv;\t\t\t\t\t// local server\n\nextern\tclient_t\t*host_client;\n\nextern\tedict_t\t\t*sv_player;\n\n//extern\tchar\t\tlocalmodels[MAX_MODELS][5];\t// inline model names for precache\n\nextern\tvfsfile_t\t*sv_fraglogfile;\n\n//===========================================================\n\nvoid SV_AddDebugPolygons(void);\nconst char *SV_CheckRejectConnection(netadr_t *adr, const char *uinfo, unsigned int protocol, unsigned int pext1, unsigned int pext2, unsigned int ezpext1, char *guid);\n\n//\n//sv_ccmds.c\n//\nchar *SV_BannedReason (netadr_t *a);\nvoid SV_EvaluatePenalties(client_t *cl);\nvoid SV_AutoAddPenalty (client_t *cl, unsigned int banflag, int duration, char *reason);\nvoid SV_AutoBanSender (int duration, char *reason);\t//bans net_from\n\n//note: not all penalties are actually penalties, but they can still expire.\n#define BAN_BAN\t\t\t(1u<<0)\t//user is banned from the server\n#define\tBAN_PERMIT\t\t(1u<<1)\t//+user can evade block bans or filterban\n#define\tBAN_CUFF\t\t(1u<<2)\t//can't shoot/use impulses\n#define\tBAN_MUTE\t\t(1u<<3)\t//can't use say/say_team/voip\n#define\tBAN_CRIPPLED\t(1u<<4)\t//can't move\n#define\tBAN_DEAF\t\t(1u<<5)\t//can't see say/say_team\n#define\tBAN_LAGGED\t\t(1u<<6)\t//given an extra 200ms\n#define BAN_VIP\t\t\t(1u<<7)\t//+mods might give the user special rights, via the *VIP infokey. the engine itself currently does not do anything but track it.\n#define BAN_BLIND\t\t(1u<<8)\t//player's pvs is wiped.\n#define BAN_SPECONLY\t(1u<<9) //player is forced to spectate\n#define BAN_STEALTH\t\t(1u<<10)//player is not told of their bans\n#define BAN_USER1\t\t(1u<<11)//mod-specified\n#define BAN_USER2\t\t(1u<<12)//mod-specified\n#define BAN_USER3\t\t(1u<<13)//mod-specified\n#define BAN_USER4\t\t(1u<<14)//mod-specified\n#define BAN_USER5\t\t(1u<<15)//mod-specified\n#define BAN_USER6\t\t(1u<<16)//mod-specified\n#define BAN_USER7\t\t(1u<<17)//mod-specified\n#define BAN_USER8\t\t(1u<<18)//mod-specified\n#define BAN_MAPPER\t\t(1u<<19)//+player is allowed to use the brush/entity editing clc.\n#define BAN_VMUTE\t\t(1u<<20)//can't use voip (but can still use other forms of chat)\n\n#define BAN_NOLOCALHOST\t(BAN_BAN|BAN_PERMIT|BAN_SPECONLY)\t//any attempt to ban localhost is denied, but you can vip, lag, cripple, etc them.\n\n//\n// sv_main.c\n//\nNORETURN void VARGS SV_Error (const char *error, ...) LIKEPRINTF(1);\nvoid SV_Shutdown (void);\nfloat SV_Frame (void);\nvoid SV_ReadPacket(void);\nvoid SV_FinalMessage (char *message);\nvoid SV_DropClient (client_t *drop);\nvoid SV_DropClient_ByAddress (netadr_t *addr);\nstruct quakeparms_s;\nvoid SV_Init (struct quakeparms_s *parms);\nvoid SV_ExecInitialConfigs(char *defaultexec);\nvoid SV_ArgumentOverrides(void);\n\nint SV_CalcPing (client_t *cl, qboolean forcecalc);\nvoid SV_FullClientUpdate (client_t *client, client_t *to);\nchar *SV_PlayerPublicAddress(client_t *cl);\n\nvoid SV_GeneratePublicServerinfo(char *info, const char *endinfo);\nconst char *SV_GetProtocolVersionString(void);\t//decorate the protocol version field of server queries with extra features...\nqboolean SVC_GetChallenge (qboolean respond_dp);\nint SV_NewChallenge (void);\nvoid SVC_DirectConnect(int expectedreliablesequence);\ntypedef struct\n{\n\tenum serverprotocols_e protocol;\t\t//protocol used to talk to this client.\n#ifdef NQPROT\n\tqboolean proquakeanglehack;\t\t\t\t//specifies that the client will expect proquake angles if we give a proquake CCREP_ACCEPT response.\n\tqboolean isqex;\t\t\t\t\t\t\t//yay quirks...\n\tunsigned int expectedreliablesequence;\t//required for nq connection cookies (like tcp's syn cookies).\n\tunsigned int supportedprotocols;\t\t//1<<SCP_* bitmask\n#endif\n\tunsigned int ftepext1;\n\tunsigned int ftepext2;\n\tunsigned int ezpext1;\n\tint\t\t\tqport;\t\t\t\t\t\t//part of the qw protocol to avoid issues with buggy routers that periodically renumber cl2sv ports.\n#ifdef HUFFNETWORK\n\tint\t\t\thuffcrc;\t\t\t\t\t//network compression stuff\n#endif\n\tint\t\t\tchallenge;\t\t\t\t\t//the challenge used at connect. remembered to make life harder for proxies.\n\tint\t\t\tmtu;\t\t\t\t\t\t//allowed fragment size (also signifies that it supports fragmented qw packets)\n\tint seats;\n\tstruct\t{\n\t\tchar\t\tinfo[2048];\t\t\t\t//random userinfo data. no blobs, obviously.\n\t} seat[MAX_SPLITS];\n\tchar\t\tguid[128];\t\t\t\t\t//user's guid data\n\tnetadr_t\tadr;\t\t\t\t\t\t//the address the connect request came from (so we can check passwords before accepting)\n} svconnectinfo_t;\nvoid SV_DoDirectConnect(svconnectinfo_t *fte_restrict info);\n\nint SV_ModelIndex (const char *name);\n\nvoid SV_WriteClientdataToMessage (client_t *client, sizebuf_t *msg);\nvoid SVQW_WriteDelta (entity_state_t *from, entity_state_t *to, sizebuf_t *msg, qboolean force, unsigned int protext, unsigned int ezext);\n\nclient_t *SV_AddSplit(client_t *controller, char *info, int id);\nvoid SV_SpawnParmsToQC(client_t *client);\nvoid SV_SpawnParmsToClient(client_t *client);\nvoid SV_GetNewSpawnParms(client_t *cl);\nvoid SV_SaveSpawnparms (void);\nvoid SV_SaveSpawnparmsClient(client_t *client, float *transferparms);\t//if transferparms, calls SetTransferParms instead, and does not modify the player.\nvoid SV_SaveLevelCache(const char *savename, qboolean dontharmgame);\nvoid SV_Savegame (const char *savename, qboolean autosave);\nqboolean SV_LoadLevelCache(const char *savename, const char *level, const char *startspot, qboolean ignoreplayers);\n\nvoid SV_Physics_Client (edict_t\t*ent, int num);\n\nvoid SV_ExecuteUserCommand (const char *s, qboolean fromQC);\nvoid SV_InitOperatorCommands (void);\n\nvoid SV_SendServerinfo (client_t *client);\nvoid SV_ExtractFromUserinfo (client_t *cl, qboolean verbose);\n\nvoid SV_SaveInfos(vfsfile_t *f);\n\nvoid SV_FixupName(const char *in, char *out, unsigned int outlen);\n\n#ifdef SUBSERVERS\n//cluster stuff\nvoid SSV_UpdateAddresses(void);\nvoid SSV_InitiatePlayerTransfer(client_t *cl, const char *newserver);\nvoid SSV_InstructMaster(sizebuf_t *cmd);\nvoid SSV_CheckFromMaster(void);\nqboolean SSV_PrintToMaster(char *s);\nvoid SSV_ReadFromControlServer(void);\nvoid SSV_SavePlayerStats(client_t *cl, int reason);\t//initial, periodic (in case of node crashes), part\nvoid SSV_RequestShutdown(void); //asks the cluster to not send us new players\n\nvfsfile_t *Sys_ForkServer(void);\nvfsfile_t *Sys_GetStdInOutStream(void);\t\t//obtains a bi-directional pipe for reading/writing via stdin/stdout. make sure the system code won't be using it.\n\nqboolean MSV_NewNetworkedNode(vfsfile_t *stream, qbyte *reqstart, qbyte *buffered, size_t buffersize, const char *remoteaddr);\t//call to register a pipe to a newly discovered node.\nvoid SSV_SetupControlPipe(vfsfile_t *stream, qboolean remote);\t//call to register the pipe.\nenum clusterslavemode_e\n{\n\tCLSV_no,\n\tCLSV_forked,\n\tCLSV_remote\n};\nextern enum clusterslavemode_e isClusterSlave;\n#define SSV_IsSubServer() isClusterSlave\n\n\nvoid MSV_SubServerCommand_f(void);\nvoid MSV_MapCluster_f(void);\nvoid MSV_MapCluster_Setup(const char *landingmap, qboolean use_database, qboolean singleplyaer);\nvoid MSV_Shutdown(void);\nvoid SSV_Send(const char *dest, const char *src, const char *cmd, const char *msg);\nqboolean MSV_ClusterLogin(svconnectinfo_t *info);\nvoid MSV_PollSlaves(void);\nqboolean MSV_ForwardToAutoServer(void);\t//forwards console command to a default subserver. ie: whichever one our client is on.\nvoid MSV_SendCvarChange(cvar_t *var);\t//when autooffloading, replicates the client's cvar changes to the server\nvoid MSV_Status(void);\nvoid MSV_OpenUserDatabase(void);\n#else\n#define SSV_UpdateAddresses() ((void)0)\n#define MSV_ClusterLogin(info) false\n#define SSV_IsSubServer() false\n#define MSV_OpenUserDatabase()\n#define MSV_PollSlaves()\n#define MSV_ForwardToAutoServer() false\n#define MSV_SendCvarChange(v)\n#endif\n\n//\n// sv_init.c\n//\nvoid SV_SpawnServer (const char *server, const char *startspot, qboolean noents, qboolean usecinematic, int playerslots);\nvoid SV_UnspawnServer (void);\nvoid SV_FlushSignon (qboolean force);\nvoid SV_UpdateMaxPlayers(int newmax);\n\nvoid SV_FilterImpulseInit(void);\nqboolean SV_FilterImpulse(int imp, int level);\n\n//svq2_game.c\nqboolean SVQ2_InitGameProgs(void);\nvoid VARGS SVQ2_ShutdownGameProgs (void);\nvoid VARGS PFQ2_Configstring (int i, const char *val); //for engine cheats.\n\n//svq2_ents.c\nvoid SVQ2_BuildClientFrame (client_t *client);\nvoid SVQ2_WriteFrameToClient (client_t *client, sizebuf_t *msg);\n#ifdef Q2SERVER\nvoid MSGQ2_WriteDeltaEntity (q2entity_state_t *from, q2entity_state_t *to, sizebuf_t *msg, qboolean force, qboolean newentity, qboolean q2ex);\nvoid SVQ2_BuildBaselines(void);\n#endif\n\n//\n// sv_send.c\n//\nvoid SV_CalcNetRates(client_t *cl, double *ftime, int *frames, double *minf, double *maxf);\t//gets received framerate etc info\nqboolean SV_ChallengePasses(int challenge);\nvoid SV_QCStatName(int type, char *name, int statnum);\nvoid SV_QCStatFieldIdx(int type, unsigned int fieldindex, int statnum);\nvoid SV_QCStatGlobal(int type, const char *globalname, int statnum);\nvoid SV_QCStatPtr(int type, void *ptr, int statnum);\nvoid SV_ClearQCStats(void);\n\nvoid SV_SendClientMessages (void);\n\nvoid SV_SendLightstyle(client_t *cl, sizebuf_t *forcemsg, int style, qboolean initial);\nvoid VARGS SV_Multicast (vec3_t origin, multicast_t to);\n#define FULLDIMENSIONMASK 0xffffffff\nvoid SV_MulticastProtExt(vec3_t origin, multicast_t to, int dimension_mask, int with, int without);\nvoid SV_MulticastCB(vec3_t origin, multicast_t to, const char *reliableinfokey, int dimension_mask, void (*callback)(client_t *cl, sizebuf_t *msg, void *ctx), void *ctx);\n\nvoid SV_StartSound (int ent, vec3_t origin, float *velocity, int seenmask, int channel, const char *sample, int volume, float attenuation, float pitchadj, float timeofs, unsigned int flags);\nvoid QDECL SVQ1_StartSound (vec_t *origin, wedict_t *entity, int channel, const char *sample, int volume, float attenuation, float pitchadj, float timeofs, unsigned int chflags);\nvoid SV_PrintToClient(client_t *cl, int level, const char *string);\nvoid SV_TPrintToClient(client_t *cl, int level, const char *string);\nvoid SV_StuffcmdToClient(client_t *cl, const char *string);\nvoid SV_StuffcmdToClient_Unreliable(client_t *cl, const char *string);\nvoid VARGS SV_ClientPrintf (client_t *cl, int level, const char *fmt, ...) LIKEPRINTF(3);\nvoid VARGS SV_ClientTPrintf (client_t *cl, int level, translation_t text, ...);\nvoid VARGS SV_BroadcastPrintf (int level, const char *fmt, ...) LIKEPRINTF(2);\nvoid SV_BroadcastPrint_QexLoc (unsigned int flags, int level, const char **arg, int args);\nvoid SV_BroadcastPrint (unsigned int flags, int level, const char *text);\n\t//flags exposed to ktx.\n\t#define BPRINT_IGNOREINDEMO  (1<<0) // broad cast print will be not put in demo\n\t#define BPRINT_IGNORECLIENTS (1<<1) // broad cast print will not be seen by clients, but may be seen in demo\n\t//#define BPRINT_QTVONLY       (1<<2) // if broad cast print goes to demo, then it will be only qtv sream, but not file\n\t#define BPRINT_IGNORECONSOLE (1<<3) // broad cast print will not be put in server console\nvoid VARGS SV_BroadcastTPrintf (int level, translation_t fmt, ...);\nvoid VARGS SV_BroadcastCommand (const char *fmt, ...) LIKEPRINTF(1);\nvoid SV_SendMessagesToAll (void);\nvoid SV_FindModelNumbers (void);\nvoid SV_SendFixAngle(client_t *client, sizebuf_t *msg, int fixtype, qboolean roll);\n\nvoid SV_BroadcastUserinfoChange(client_t *about, qboolean isbasic, const char *key, const char *newval);\n\nvoid SV_Prompt_Resend(client_t *cl);\nvoid SV_Prompt_Clear(client_t *cl);\nvoid SV_Prompt_Input(client_t *cl, usercmd_t *ucmd);\n\n//\n// sv_user.c\n//\n#ifdef NQPROT\nvoid SVNQ_New_f (void);\nvoid SVNQ_ExecuteClientMessage (client_t *cl);\n#endif\nqboolean SV_UserInfoIsBasic(const char *infoname);\t//standard message.\nvoid SV_ExecuteClientMessage (client_t *cl);\nvoid SVQ2_ExecuteClientMessage (client_t *cl);\nint SV_PMTypeForClient (client_t *cl, edict_t *ent);\nvoid SV_UserInit (void);\nqboolean SV_TogglePause (client_t *cl);\n\n#ifdef PEXT2_VOICECHAT\nvoid SV_VoiceInitClient(client_t *client);\nvoid SV_VoiceSendPacket(client_t *client, sizebuf_t *buf);\n#endif\n\nvoid SV_SetSSQCInputs(usercmd_t *ucmd);\nvoid SV_ClientThink (void);\nvoid SV_Begin_Core(client_t *split);\t//sets up the player's gamecode state\nvoid SV_DespawnClient(client_t *cl);\t//shuts down the gamecode state.\n\nvoid VoteFlushAll(void);\nvoid SV_SetUpClientEdict (client_t *cl, edict_t *ent);\nvoid SV_UpdateToReliableMessages (void);\nvoid SV_FlushBroadcasts (void);\nqboolean SV_CanTrack(client_t *client, int entity);\n\n#ifdef HAVE_LEGACY\nvoid SV_DownloadQueueNext(client_t *client);\nvoid SV_DownloadQueueClear(client_t *client);\n#endif\n#ifdef NQPROT\nvoid SV_DarkPlacesDownloadChunk(client_t *cl, sizebuf_t *msg);\n#endif\nvoid SV_New_f (void);\n\nvoid SV_PreRunCmd(void);\nvoid SV_RunCmd (usercmd_t *ucmd, qboolean recurse);\nvoid SV_PostRunCmd(void);\nvoid SV_RunCmdCleanup(void);\n\nvoid SV_SendClientPrespawnInfo(client_t *client);\nvoid SV_ClientProtocolExtensionsChanged(client_t *client);\n\n//sv_master.c\nfloat SVM_Think(void);\nvfsfile_t *SVM_GenerateIndex(const char *requesthost, const char *fname, const char **mimetype, const char *query);\nvoid SVM_AddBrokerGame(const char *brokerid, const char *info);\nvoid SVM_RemoveBrokerGame(const char *brokerid);\nqboolean SVM_FixupServerAddress(netadr_t *adr, struct dtlspeercred_s *cred);\nvoid SVM_SelectRelay(netadr_t *benefitiary, const char *brokerid, char *out, size_t outsize);\nvoid FTENET_TCP_ICEResponse(struct ftenet_connections_s *col, int type, const char *cid, const char *sdp);\n\n\n//\n// svonly.c\n//\ntypedef enum {RD_NONE, RD_CLIENT, RD_PACKET, RD_PACKET_LOG, RD_OBLIVION, RD_MASTER} redirect_t;\t//oblivion is provided so people can read the output before the buffer is wiped.\nvoid SV_BeginRedirect (redirect_t rd, int lang);\nvoid SV_EndRedirect (void);\nextern char\tsv_redirected_buf[8000];\nextern redirect_t\tsv_redirected;\nextern int sv_redirectedlang;\n\n\nqboolean PR_GameCodePacket(char *s);\nqboolean PR_GameCodePausedTic(float pausedtime);\nqboolean PR_ShouldTogglePause(client_t *initiator, qboolean pausedornot);\n\n//\n// sv_ents.c\n//\nvoid SV_WriteEntitiesToClient (client_t *client, sizebuf_t *msg, qboolean ignorepvs);\nvoid SVFTE_EmitBaseline(entity_state_t *to, qboolean numberisimportant, sizebuf_t *msg, unsigned int pext2, unsigned int ezext);\nvoid SVQ3Q1_BuildEntityPacket(client_t *client, packet_entities_t *pack);\nvoid SV_Snapshot_BuildStateQ1(entity_state_t *state, edict_t *ent, client_t *client, packet_entities_t *pack);\nint SV_HullNumForPlayer(int h2hull, float *mins, float *maxs);\nvoid SV_GibFilterInit(void);\nvoid SV_GibFilterPurge(void);\nvoid SV_CleanupEnts(void);\nvoid SV_ProcessSendFlags(client_t *c);\n\nvoid SV_AckEntityFrame(client_t *cl, int framenum);\nvoid SV_ReplaceEntityFrame(client_t *cl, int framenum);\n\n//\n// sv_nchan.c\n//\n\nvoid ClientReliableCheckBlock(client_t *cl, int maxsize);\nsizebuf_t *ClientReliable_StartWrite(client_t *cl, int maxsize);\t//MUST be followed by a call to ClientReliable_FinishWrite before the next start\nvoid ClientReliable_FinishWrite(client_t *cl);\nvoid ClientReliableWrite_Begin(client_t *cl, int c, int maxsize);\nclient_t *ClientReliableWrite_BeginSplit(client_t *cl, int svc, int svclen);\nvoid ClientReliableWrite_Angle(client_t *cl, float f);\nvoid ClientReliableWrite_Angle16(client_t *cl, float f);\nvoid ClientReliableWrite_Byte(client_t *cl, int c);\nvoid ClientReliableWrite_Char(client_t *cl, int c);\nvoid ClientReliableWrite_Float(client_t *cl, float f);\nvoid ClientReliableWrite_Double(client_t *cl, double f);\nvoid ClientReliableWrite_Coord(client_t *cl, float f);\nvoid ClientReliableWrite_Int64(client_t *cl, qint64_t c);\nvoid ClientReliableWrite_Long(client_t *cl, int c);\nvoid ClientReliableWrite_Short(client_t *cl, int c);\nvoid ClientReliableWrite_Entity(client_t *cl, int c);\nvoid ClientReliableWrite_String(client_t *cl, const char *s);\nvoid ClientReliableWrite_SZ(client_t *cl, const void *data, int len);\n\n\n#ifdef  SVRANKING\n//flags\n#define RANK_MUTED\t\t2\n#define RANK_CUFFED\t\t4\n#define RANK_CRIPPLED\t8\t//ha ha... get speed cheaters with this!... :o)\n\n#define NUM_RANK_SPAWN_PARMS 32\n\ntypedef struct {\t//stats info\n\tint kills;\n\tint deaths;\n\tfloat parm[NUM_RANK_SPAWN_PARMS];\n\tfloat timeonserver;\n\tqbyte flags1;\n\tqbyte trustlevel;\n\tchar pad2;\n\tchar pad3;\n\n#if NUM_RANK_SPAWN_PARMS>32\n\tquint64_t created;\n\tquint64_t lastseen;\n#endif\n} rankstats_t;\n\ntypedef struct {\t//name, identity and order.\n\tint prev;\t\t//score is held for convineance.\n\tint next;\n\tchar name[32];\n\tint pwd;\n\tfloat score;\n} rankheader_t;\n\ntypedef struct {\n\trankheader_t h;\n\trankstats_t s;\n} rankinfo_t;\n\nint Rank_GetPlayerID(char *guid, const char *name, int pwd, qboolean allowcreate, qboolean requirepasswordtobeset);\nvoid Rank_SetPlayerStats(int id, rankstats_t *stats);\nrankstats_t *Rank_GetPlayerStats(int id, rankstats_t *buffer);\nrankinfo_t *Rank_GetPlayerInfo(int id, rankinfo_t *buffer);\nqboolean Rank_OpenRankings(void);\nvoid Rank_Flush (void);\n\nvoid Rank_ListTop10_f (void);\nvoid Rank_RegisterCommands(void);\nint Rank_GetPass (char *name);\n\nextern cvar_t rank_needlogin;\nqboolean ReloadRanking(client_t *cl, const char *newname);\n#endif\n\nclient_t *SV_GetClientForString(const char *name, int *id);\nqboolean    SV_MayCheat(void);\n\n\n\n\n\n\n\nvoid NPP_Flush(void);\nvoid NPP_NQWriteByte(int dest, qbyte data);\nvoid NPP_NQWriteChar(int dest, char data);\nvoid NPP_NQWriteShort(int dest, short data);\nvoid NPP_NQWriteLong(int dest, long data);\nvoid NPP_NQWriteAngle(int dest, float data);\nvoid NPP_NQWriteCoord(int dest, float data);\nvoid NPP_NQWriteFloat(int dest, float data);\nvoid NPP_NQWriteString(int dest, const char *data);\nvoid NPP_NQWriteEntity(int dest, int data);\n\nvoid NPP_QWWriteByte(int dest, qbyte data);\nvoid NPP_QWWriteChar(int dest, char data);\nvoid NPP_QWWriteShort(int dest, short data);\nvoid NPP_QWWriteLong(int dest, long data);\nvoid NPP_QWWriteAngle(int dest, float data);\nvoid NPP_QWWriteCoord(int dest, float data);\nvoid NPP_QWWriteFloat(int dest, float data);\nvoid NPP_QWWriteString(int dest, const char *data);\nvoid NPP_QWWriteEntity(int dest, int data);\n\n\n\nvoid NPP_MVDForceFlush(void);\n\n\n//replacement rand function (useful cos it's fully portable, with seed grabbing)\nvoid predictablesrand(unsigned int x);\nint predictablerandgetseed(void);\nint predictablerand(void);\n\n\n\n\n\n\n\n#ifdef SVCHAT\nvoid SV_WipeChat(client_t *client);\nint SV_ChatMove(int impulse);\nvoid SV_ChatThink(client_t *client);\n#endif\n\n\n#ifdef MVD_RECORDING\n/*\n//\n// sv_mvd.c\n//\n//qtv proxies are meant to send a small header now, bit like http\n//this header gives supported version numbers and stuff\ntypedef struct mvdpendingdest_s {\n\tqboolean error;\t//disables writers, quit ASAP.\n#ifdef _WIN32\n\tqintptr_t socket;\n#else\n\tint socket;\n#endif\n\n\tchar inbuffer[2048];\n\tchar outbuffer[2048];\n\n\tchar challenge[64];\n\tqboolean hasauthed;\n\tqboolean isreverse;\n\n\tint insize;\n\tint outsize;\n\n\tstruct mvdpendingdest_s *nextdest;\n} mvdpendingdest_t;*/\n\ntypedef struct mvddest_s {\n\tqboolean error;\t//disables writers, quit ASAP.\n\tqboolean droponmapchange;\n\n\tenum {DEST_NONE, DEST_FILE, DEST_BUFFEREDFILE, DEST_THREADEDFILE, DEST_STREAM} desttype;\n\n\tvfsfile_t *file;\n\n\tint id;\n\tchar filename[MAX_QPATH];\t//demos/foo.mvd (or a username)\n\tchar simplename[MAX_QPATH];\t//foo.mvd (or a qtv resource)\n\n\tint flushing;\t//worker has a cache (used as a sync point)\n\tchar *cache;\n\tchar *altcache;\n\tint cacheused;\n\tint maxcachesize;\n\n\tunsigned int totalsize;\n\n\tstruct mvddest_s *nextdest;\n} mvddest_t;\nvoid SV_MVDPings (void);\nvoid SV_MVD_FullClientUpdate(sizebuf_t *msg, client_t *player);\nsizebuf_t *MVDWrite_Begin(qbyte type, int to, int size);\nvoid MVDSetMsgBuf(demobuf_t *prev,demobuf_t *cur);\n\nenum mvdclosereason_e\n{\n\tMVD_CLOSE_STOPPED,\n\tMVD_CLOSE_SIZELIMIT,\n\tMVD_CLOSE_CANCEL,\n\tMVD_CLOSE_DISCONNECTED,\t//qtv disconnected\n\tMVD_CLOSE_FSERROR\n};\n\nvoid SV_MVDStop (enum mvdclosereason_e reason, qboolean mvdonly);\nvoid SV_MVDStop_f (void);\nqboolean SV_MVDWritePackets (int num);\nvoid MVD_Init (void);\nvoid SV_MVD_RunPendingConnections(void);\nvoid SV_MVD_SendInitialGamestate(mvddest_t *dest);\n\nextern demo_t\t\t\tdemo;\t\t\t\t// server demo struct\n\nextern cvar_t\tsv_demoDir, sv_demoDirAlt;\nextern cvar_t\tsv_demoAutoRecord;\nextern cvar_t\tsv_demofps;\nextern cvar_t\tsv_demoPings;\nextern cvar_t\tsv_demoUseCache;\nextern cvar_t\tsv_demoMaxSize;\nextern cvar_t\tsv_demoMaxDirSize;\n\nqboolean MVD_CheckSpace(qboolean broadcastwarnings);\nvoid SV_MVD_AutoRecord (void);\nchar *SV_Demo_CurrentOutput(void);\nvoid SV_Demo_PrintOutputs(void);\nvoid SV_MVDInit(void);\nchar *SV_MVDNum(char *buffer, int bufferlen, int num);\nconst char *SV_MVDLastNum(unsigned int num);\nvoid SV_SendMVDMessage(void);\nvoid SV_MVD_WriteReliables(qboolean writebroadcasts);\nqboolean SV_ReadMVD (void);\nvoid SV_FlushDemoSignon (void);\nvoid DestFlush(qboolean compleate);\n\ntypedef struct\n{\n\tqboolean hasauthed;\n\tqboolean isreverse;\n\tchar challenge[64];\t//aka nonce\n} qtvpendingstate_t;\nint SV_MVD_GotQTVRequest(vfsfile_t *clientstream, char *headerstart, char *headerend, qtvpendingstate_t *p);\n#endif\n\n// savegame.c\nvoid SV_Savegame_f (void);\nvoid SV_DeleteSavegame_f (void);\nvoid SV_Savegame_c(int argn, const char *partial, struct xcommandargcompletioncb_s *ctx);\nvoid SV_Loadgame_f (void);\nqboolean SV_Loadgame (const char *unsafe_savename);\nvoid SV_AutoSave(void);\nvoid SV_FlushLevelCache(void);\nextern cvar_t sv_autosave;\nextern cvar_t sv_savefmt;\n\n\nint SV_RateForClient(client_t *cl);\n\nvoid SVVC_Frame (qboolean enabled);\nvoid SV_CalcPHS (void);\n\nvoid SV_GetConsoleCommands (void);\nvoid SV_CheckTimer(void);\n\nvoid SV_LogPlayer(client_t *cl, char *msg);\n\nextern vec3_t pmove_mins, pmove_maxs;\t//abs min/max extents\n#ifdef USEAREAGRID\nvoid AddAllLinksToPmove (world_t *w, wedict_t *player);\n#else\nvoid AddLinksToPmove (world_t *w, wedict_t *player, areanode_t *node);\nvoid AddLinksToPmove_Force (world_t *w, wedict_t *player, areanode_t *node);\n#define AddAllLinksToPmove(w,p) do{AddLinksToPmove(w,p,(w)->areanodes);AddLinksToPmove_Force(w,p,&(w)->portallist);}while(0)\n#endif\n\n\n#ifdef HLSERVER\nvoid SVHL_SaveLevelCache(char *filename);\n\n//network frame info\nvoid SVHL_Snapshot_Build(client_t *client, packet_entities_t *pack, qbyte *pvs, edict_t *clent, qboolean ignorepvs);\nqbyte\t*SVHL_Snapshot_SetupPVS(client_t *client, qbyte *pvs, unsigned int pvsbufsize);\nvoid SVHL_BuildStats(client_t *client, int *si, float *sf, char **ss);\n\n//gamecode entry points\nint SVHL_InitGame(void);\nvoid SVHL_SetupGame(void);\nvoid SVHL_SpawnEntities(char *entstring);\nvoid SVHL_RunFrame (void);\nqboolean SVHL_ClientConnect(client_t *client, netadr_t adr, char rejectmessage[128]);\nvoid SVHL_PutClientInServer(client_t *client);\nvoid SVHL_RunPlayerCommand(client_t *cl, usercmd_t *oldest, usercmd_t *oldcmd, usercmd_t *newcmd);\nqboolean HLSV_ClientCommand(client_t *client);\nvoid SVHL_DropClient(client_t *drop);\nvoid SVHL_ShutdownGame(void);\n#endif\n\n"
  },
  {
    "path": "engine/server/sqlite3.h",
    "content": "/*\n** 2001 September 15\n**\n** The author disclaims copyright to this source code.  In place of\n** a legal notice, here is a blessing:\n**\n**    May you do good and not evil.\n**    May you find forgiveness for yourself and forgive others.\n**    May you share freely, never taking more than you give.\n**\n*************************************************************************\n** This header file defines the interface that the SQLite library\n** presents to client programs.  If a C-function, structure, datatype,\n** or constant definition does not appear in this file, then it is\n** not a published API of SQLite, is subject to change without\n** notice, and should not be referenced by programs that use SQLite.\n**\n** Some of the definitions that are in this file are marked as\n** \"experimental\".  Experimental interfaces are normally new\n** features recently added to SQLite.  We do not anticipate changes\n** to experimental interfaces but reserve the right to make minor changes\n** if experience from use \"in the wild\" suggest such changes are prudent.\n**\n** The official C-language API documentation for SQLite is derived\n** from comments in this file.  This file is the authoritative source\n** on how SQLite interfaces are suppose to operate.\n**\n** The name of this file under configuration management is \"sqlite.h.in\".\n** The makefile makes some minor changes to this file (such as inserting\n** the version number) and changes its name to \"sqlite3.h\" as\n** part of the build process.\n*/\n#ifndef _SQLITE3_H_\n#define _SQLITE3_H_\n#include <stdarg.h>     /* Needed for the definition of va_list */\n\n/*\n** Make sure we can call this stuff from C++.\n*/\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n\n/*\n** Add the ability to override 'extern'\n*/\n#ifndef SQLITE_EXTERN\n# define SQLITE_EXTERN extern\n#endif\n\n#ifndef SQLITE_API\n# define SQLITE_API\n#endif\n\n\n/*\n** These no-op macros are used in front of interfaces to mark those\n** interfaces as either deprecated or experimental.  New applications\n** should not use deprecated interfaces - they are support for backwards\n** compatibility only.  Application writers should be aware that\n** experimental interfaces are subject to change in point releases.\n**\n** These macros used to resolve to various kinds of compiler magic that\n** would generate warning messages when they were used.  But that\n** compiler magic ended up generating such a flurry of bug reports\n** that we have taken it all out and gone back to using simple\n** noop macros.\n*/\n#define SQLITE_DEPRECATED\n#define SQLITE_EXPERIMENTAL\n\n/*\n** Ensure these symbols were not defined by some previous header file.\n*/\n#ifdef SQLITE_VERSION\n# undef SQLITE_VERSION\n#endif\n#ifdef SQLITE_VERSION_NUMBER\n# undef SQLITE_VERSION_NUMBER\n#endif\n\n/*\n** CAPI3REF: Compile-Time Library Version Numbers\n**\n** ^(The [SQLITE_VERSION] C preprocessor macro in the sqlite3.h header\n** evaluates to a string literal that is the SQLite version in the\n** format \"X.Y.Z\" where X is the major version number (always 3 for\n** SQLite3) and Y is the minor version number and Z is the release number.)^\n** ^(The [SQLITE_VERSION_NUMBER] C preprocessor macro resolves to an integer\n** with the value (X*1000000 + Y*1000 + Z) where X, Y, and Z are the same\n** numbers used in [SQLITE_VERSION].)^\n** The SQLITE_VERSION_NUMBER for any given release of SQLite will also\n** be larger than the release from which it is derived.  Either Y will\n** be held constant and Z will be incremented or else Y will be incremented\n** and Z will be reset to zero.\n**\n** Since version 3.6.18, SQLite source code has been stored in the\n** <a href=\"http://www.fossil-scm.org/\">Fossil configuration management\n** system</a>.  ^The SQLITE_SOURCE_ID macro evaluates to\n** a string which identifies a particular check-in of SQLite\n** within its configuration management system.  ^The SQLITE_SOURCE_ID\n** string contains the date and time of the check-in (UTC) and an SHA1\n** hash of the entire source tree.\n**\n** See also: [sqlite3_libversion()],\n** [sqlite3_libversion_number()], [sqlite3_sourceid()],\n** [sqlite_version()] and [sqlite_source_id()].\n*/\n#define SQLITE_VERSION        \"3.7.14.1\"\n#define SQLITE_VERSION_NUMBER 3007014\n#define SQLITE_SOURCE_ID      \"2012-10-04 19:37:12 091570e46d04e84b67228e0bdbcd6e1fb60c6bdb\"\n\n/*\n** CAPI3REF: Run-Time Library Version Numbers\n** KEYWORDS: sqlite3_version, sqlite3_sourceid\n**\n** These interfaces provide the same information as the [SQLITE_VERSION],\n** [SQLITE_VERSION_NUMBER], and [SQLITE_SOURCE_ID] C preprocessor macros\n** but are associated with the library instead of the header file.  ^(Cautious\n** programmers might include assert() statements in their application to\n** verify that values returned by these interfaces match the macros in\n** the header, and thus insure that the application is\n** compiled with matching library and header files.\n**\n** <blockquote><pre>\n** assert( sqlite3_libversion_number()==SQLITE_VERSION_NUMBER );\n** assert( strcmp(sqlite3_sourceid(),SQLITE_SOURCE_ID)==0 );\n** assert( strcmp(sqlite3_libversion(),SQLITE_VERSION)==0 );\n** </pre></blockquote>)^\n**\n** ^The sqlite3_version[] string constant contains the text of [SQLITE_VERSION]\n** macro.  ^The sqlite3_libversion() function returns a pointer to the\n** to the sqlite3_version[] string constant.  The sqlite3_libversion()\n** function is provided for use in DLLs since DLL users usually do not have\n** direct access to string constants within the DLL.  ^The\n** sqlite3_libversion_number() function returns an integer equal to\n** [SQLITE_VERSION_NUMBER].  ^The sqlite3_sourceid() function returns \n** a pointer to a string constant whose value is the same as the \n** [SQLITE_SOURCE_ID] C preprocessor macro.\n**\n** See also: [sqlite_version()] and [sqlite_source_id()].\n*/\nSQLITE_API SQLITE_EXTERN const char sqlite3_version[];\nSQLITE_API const char *sqlite3_libversion(void);\nSQLITE_API const char *sqlite3_sourceid(void);\nSQLITE_API int sqlite3_libversion_number(void);\n\n/*\n** CAPI3REF: Run-Time Library Compilation Options Diagnostics\n**\n** ^The sqlite3_compileoption_used() function returns 0 or 1 \n** indicating whether the specified option was defined at \n** compile time.  ^The SQLITE_ prefix may be omitted from the \n** option name passed to sqlite3_compileoption_used().  \n**\n** ^The sqlite3_compileoption_get() function allows iterating\n** over the list of options that were defined at compile time by\n** returning the N-th compile time option string.  ^If N is out of range,\n** sqlite3_compileoption_get() returns a NULL pointer.  ^The SQLITE_ \n** prefix is omitted from any strings returned by \n** sqlite3_compileoption_get().\n**\n** ^Support for the diagnostic functions sqlite3_compileoption_used()\n** and sqlite3_compileoption_get() may be omitted by specifying the \n** [SQLITE_OMIT_COMPILEOPTION_DIAGS] option at compile time.\n**\n** See also: SQL functions [sqlite_compileoption_used()] and\n** [sqlite_compileoption_get()] and the [compile_options pragma].\n*/\n#ifndef SQLITE_OMIT_COMPILEOPTION_DIAGS\nSQLITE_API int sqlite3_compileoption_used(const char *zOptName);\nSQLITE_API const char *sqlite3_compileoption_get(int N);\n#endif\n\n/*\n** CAPI3REF: Test To See If The Library Is Threadsafe\n**\n** ^The sqlite3_threadsafe() function returns zero if and only if\n** SQLite was compiled with mutexing code omitted due to the\n** [SQLITE_THREADSAFE] compile-time option being set to 0.\n**\n** SQLite can be compiled with or without mutexes.  When\n** the [SQLITE_THREADSAFE] C preprocessor macro is 1 or 2, mutexes\n** are enabled and SQLite is threadsafe.  When the\n** [SQLITE_THREADSAFE] macro is 0, \n** the mutexes are omitted.  Without the mutexes, it is not safe\n** to use SQLite concurrently from more than one thread.\n**\n** Enabling mutexes incurs a measurable performance penalty.\n** So if speed is of utmost importance, it makes sense to disable\n** the mutexes.  But for maximum safety, mutexes should be enabled.\n** ^The default behavior is for mutexes to be enabled.\n**\n** This interface can be used by an application to make sure that the\n** version of SQLite that it is linking against was compiled with\n** the desired setting of the [SQLITE_THREADSAFE] macro.\n**\n** This interface only reports on the compile-time mutex setting\n** of the [SQLITE_THREADSAFE] flag.  If SQLite is compiled with\n** SQLITE_THREADSAFE=1 or =2 then mutexes are enabled by default but\n** can be fully or partially disabled using a call to [sqlite3_config()]\n** with the verbs [SQLITE_CONFIG_SINGLETHREAD], [SQLITE_CONFIG_MULTITHREAD],\n** or [SQLITE_CONFIG_MUTEX].  ^(The return value of the\n** sqlite3_threadsafe() function shows only the compile-time setting of\n** thread safety, not any run-time changes to that setting made by\n** sqlite3_config(). In other words, the return value from sqlite3_threadsafe()\n** is unchanged by calls to sqlite3_config().)^\n**\n** See the [threading mode] documentation for additional information.\n*/\nSQLITE_API int sqlite3_threadsafe(void);\n\n/*\n** CAPI3REF: Database Connection Handle\n** KEYWORDS: {database connection} {database connections}\n**\n** Each open SQLite database is represented by a pointer to an instance of\n** the opaque structure named \"sqlite3\".  It is useful to think of an sqlite3\n** pointer as an object.  The [sqlite3_open()], [sqlite3_open16()], and\n** [sqlite3_open_v2()] interfaces are its constructors, and [sqlite3_close()]\n** and [sqlite3_close_v2()] are its destructors.  There are many other\n** interfaces (such as\n** [sqlite3_prepare_v2()], [sqlite3_create_function()], and\n** [sqlite3_busy_timeout()] to name but three) that are methods on an\n** sqlite3 object.\n*/\ntypedef struct sqlite3 sqlite3;\n\n/*\n** CAPI3REF: 64-Bit Integer Types\n** KEYWORDS: sqlite_int64 sqlite_uint64\n**\n** Because there is no cross-platform way to specify 64-bit integer types\n** SQLite includes typedefs for 64-bit signed and unsigned integers.\n**\n** The sqlite3_int64 and sqlite3_uint64 are the preferred type definitions.\n** The sqlite_int64 and sqlite_uint64 types are supported for backwards\n** compatibility only.\n**\n** ^The sqlite3_int64 and sqlite_int64 types can store integer values\n** between -9223372036854775808 and +9223372036854775807 inclusive.  ^The\n** sqlite3_uint64 and sqlite_uint64 types can store integer values \n** between 0 and +18446744073709551615 inclusive.\n*/\n#ifdef SQLITE_INT64_TYPE\n  typedef SQLITE_INT64_TYPE sqlite_int64;\n  typedef unsigned SQLITE_INT64_TYPE sqlite_uint64;\n#elif defined(_MSC_VER) || defined(__BORLANDC__)\n  typedef __int64 sqlite_int64;\n  typedef unsigned __int64 sqlite_uint64;\n#else\n  typedef long long int sqlite_int64;\n  typedef unsigned long long int sqlite_uint64;\n#endif\ntypedef sqlite_int64 sqlite3_int64;\ntypedef sqlite_uint64 sqlite3_uint64;\n\n/*\n** If compiling for a processor that lacks floating point support,\n** substitute integer for floating-point.\n*/\n#ifdef SQLITE_OMIT_FLOATING_POINT\n# define double sqlite3_int64\n#endif\n\n/*\n** CAPI3REF: Closing A Database Connection\n**\n** ^The sqlite3_close() and sqlite3_close_v2() routines are destructors\n** for the [sqlite3] object.\n** ^Calls to sqlite3_close() and sqlite3_close_v2() return SQLITE_OK if\n** the [sqlite3] object is successfully destroyed and all associated\n** resources are deallocated.\n**\n** ^If the database connection is associated with unfinalized prepared\n** statements or unfinished sqlite3_backup objects then sqlite3_close()\n** will leave the database connection open and return [SQLITE_BUSY].\n** ^If sqlite3_close_v2() is called with unfinalized prepared statements\n** and unfinished sqlite3_backups, then the database connection becomes\n** an unusable \"zombie\" which will automatically be deallocated when the\n** last prepared statement is finalized or the last sqlite3_backup is\n** finished.  The sqlite3_close_v2() interface is intended for use with\n** host languages that are garbage collected, and where the order in which\n** destructors are called is arbitrary.\n**\n** Applications should [sqlite3_finalize | finalize] all [prepared statements],\n** [sqlite3_blob_close | close] all [BLOB handles], and \n** [sqlite3_backup_finish | finish] all [sqlite3_backup] objects associated\n** with the [sqlite3] object prior to attempting to close the object.  ^If\n** sqlite3_close() is called on a [database connection] that still has\n** outstanding [prepared statements], [BLOB handles], and/or\n** [sqlite3_backup] objects then it returns SQLITE_OK but the deallocation\n** of resources is deferred until all [prepared statements], [BLOB handles],\n** and [sqlite3_backup] objects are also destroyed.\n**\n** ^If an [sqlite3] object is destroyed while a transaction is open,\n** the transaction is automatically rolled back.\n**\n** The C parameter to [sqlite3_close(C)] and [sqlite3_close_v2(C)]\n** must be either a NULL\n** pointer or an [sqlite3] object pointer obtained\n** from [sqlite3_open()], [sqlite3_open16()], or\n** [sqlite3_open_v2()], and not previously closed.\n** ^Calling sqlite3_close() or sqlite3_close_v2() with a NULL pointer\n** argument is a harmless no-op.\n*/\nSQLITE_API int sqlite3_close(sqlite3*);\nSQLITE_API int sqlite3_close_v2(sqlite3*);\n\n/*\n** The type for a callback function.\n** This is legacy and deprecated.  It is included for historical\n** compatibility and is not documented.\n*/\ntypedef int (*sqlite3_callback)(void*,int,char**, char**);\n\n/*\n** CAPI3REF: One-Step Query Execution Interface\n**\n** The sqlite3_exec() interface is a convenience wrapper around\n** [sqlite3_prepare_v2()], [sqlite3_step()], and [sqlite3_finalize()],\n** that allows an application to run multiple statements of SQL\n** without having to use a lot of C code. \n**\n** ^The sqlite3_exec() interface runs zero or more UTF-8 encoded,\n** semicolon-separate SQL statements passed into its 2nd argument,\n** in the context of the [database connection] passed in as its 1st\n** argument.  ^If the callback function of the 3rd argument to\n** sqlite3_exec() is not NULL, then it is invoked for each result row\n** coming out of the evaluated SQL statements.  ^The 4th argument to\n** sqlite3_exec() is relayed through to the 1st argument of each\n** callback invocation.  ^If the callback pointer to sqlite3_exec()\n** is NULL, then no callback is ever invoked and result rows are\n** ignored.\n**\n** ^If an error occurs while evaluating the SQL statements passed into\n** sqlite3_exec(), then execution of the current statement stops and\n** subsequent statements are skipped.  ^If the 5th parameter to sqlite3_exec()\n** is not NULL then any error message is written into memory obtained\n** from [sqlite3_malloc()] and passed back through the 5th parameter.\n** To avoid memory leaks, the application should invoke [sqlite3_free()]\n** on error message strings returned through the 5th parameter of\n** of sqlite3_exec() after the error message string is no longer needed.\n** ^If the 5th parameter to sqlite3_exec() is not NULL and no errors\n** occur, then sqlite3_exec() sets the pointer in its 5th parameter to\n** NULL before returning.\n**\n** ^If an sqlite3_exec() callback returns non-zero, the sqlite3_exec()\n** routine returns SQLITE_ABORT without invoking the callback again and\n** without running any subsequent SQL statements.\n**\n** ^The 2nd argument to the sqlite3_exec() callback function is the\n** number of columns in the result.  ^The 3rd argument to the sqlite3_exec()\n** callback is an array of pointers to strings obtained as if from\n** [sqlite3_column_text()], one for each column.  ^If an element of a\n** result row is NULL then the corresponding string pointer for the\n** sqlite3_exec() callback is a NULL pointer.  ^The 4th argument to the\n** sqlite3_exec() callback is an array of pointers to strings where each\n** entry represents the name of corresponding result column as obtained\n** from [sqlite3_column_name()].\n**\n** ^If the 2nd parameter to sqlite3_exec() is a NULL pointer, a pointer\n** to an empty string, or a pointer that contains only whitespace and/or \n** SQL comments, then no SQL statements are evaluated and the database\n** is not changed.\n**\n** Restrictions:\n**\n** <ul>\n** <li> The application must insure that the 1st parameter to sqlite3_exec()\n**      is a valid and open [database connection].\n** <li> The application must not close [database connection] specified by\n**      the 1st parameter to sqlite3_exec() while sqlite3_exec() is running.\n** <li> The application must not modify the SQL statement text passed into\n**      the 2nd parameter of sqlite3_exec() while sqlite3_exec() is running.\n** </ul>\n*/\nSQLITE_API int sqlite3_exec(\n  sqlite3*,                                  /* An open database */\n  const char *sql,                           /* SQL to be evaluated */\n  int (*callback)(void*,int,char**,char**),  /* Callback function */\n  void *,                                    /* 1st argument to callback */\n  char **errmsg                              /* Error msg written here */\n);\n\n/*\n** CAPI3REF: Result Codes\n** KEYWORDS: SQLITE_OK {error code} {error codes}\n** KEYWORDS: {result code} {result codes}\n**\n** Many SQLite functions return an integer result code from the set shown\n** here in order to indicate success or failure.\n**\n** New error codes may be added in future versions of SQLite.\n**\n** See also: [SQLITE_IOERR_READ | extended result codes],\n** [sqlite3_vtab_on_conflict()] [SQLITE_ROLLBACK | result codes].\n*/\n#define SQLITE_OK           0   /* Successful result */\n/* beginning-of-error-codes */\n#define SQLITE_ERROR        1   /* SQL error or missing database */\n#define SQLITE_INTERNAL     2   /* Internal logic error in SQLite */\n#define SQLITE_PERM         3   /* Access permission denied */\n#define SQLITE_ABORT        4   /* Callback routine requested an abort */\n#define SQLITE_BUSY         5   /* The database file is locked */\n#define SQLITE_LOCKED       6   /* A table in the database is locked */\n#define SQLITE_NOMEM        7   /* A malloc() failed */\n#define SQLITE_READONLY     8   /* Attempt to write a readonly database */\n#define SQLITE_INTERRUPT    9   /* Operation terminated by sqlite3_interrupt()*/\n#define SQLITE_IOERR       10   /* Some kind of disk I/O error occurred */\n#define SQLITE_CORRUPT     11   /* The database disk image is malformed */\n#define SQLITE_NOTFOUND    12   /* Unknown opcode in sqlite3_file_control() */\n#define SQLITE_FULL        13   /* Insertion failed because database is full */\n#define SQLITE_CANTOPEN    14   /* Unable to open the database file */\n#define SQLITE_PROTOCOL    15   /* Database lock protocol error */\n#define SQLITE_EMPTY       16   /* Database is empty */\n#define SQLITE_SCHEMA      17   /* The database schema changed */\n#define SQLITE_TOOBIG      18   /* String or BLOB exceeds size limit */\n#define SQLITE_CONSTRAINT  19   /* Abort due to constraint violation */\n#define SQLITE_MISMATCH    20   /* Data type mismatch */\n#define SQLITE_MISUSE      21   /* Library used incorrectly */\n#define SQLITE_NOLFS       22   /* Uses OS features not supported on host */\n#define SQLITE_AUTH        23   /* Authorization denied */\n#define SQLITE_FORMAT      24   /* Auxiliary database format error */\n#define SQLITE_RANGE       25   /* 2nd parameter to sqlite3_bind out of range */\n#define SQLITE_NOTADB      26   /* File opened that is not a database file */\n#define SQLITE_ROW         100  /* sqlite3_step() has another row ready */\n#define SQLITE_DONE        101  /* sqlite3_step() has finished executing */\n/* end-of-error-codes */\n\n/*\n** CAPI3REF: Extended Result Codes\n** KEYWORDS: {extended error code} {extended error codes}\n** KEYWORDS: {extended result code} {extended result codes}\n**\n** In its default configuration, SQLite API routines return one of 26 integer\n** [SQLITE_OK | result codes].  However, experience has shown that many of\n** these result codes are too coarse-grained.  They do not provide as\n** much information about problems as programmers might like.  In an effort to\n** address this, newer versions of SQLite (version 3.3.8 and later) include\n** support for additional result codes that provide more detailed information\n** about errors. The extended result codes are enabled or disabled\n** on a per database connection basis using the\n** [sqlite3_extended_result_codes()] API.\n**\n** Some of the available extended result codes are listed here.\n** One may expect the number of extended result codes will be expand\n** over time.  Software that uses extended result codes should expect\n** to see new result codes in future releases of SQLite.\n**\n** The SQLITE_OK result code will never be extended.  It will always\n** be exactly zero.\n*/\n#define SQLITE_IOERR_READ              (SQLITE_IOERR | (1<<8))\n#define SQLITE_IOERR_SHORT_READ        (SQLITE_IOERR | (2<<8))\n#define SQLITE_IOERR_WRITE             (SQLITE_IOERR | (3<<8))\n#define SQLITE_IOERR_FSYNC             (SQLITE_IOERR | (4<<8))\n#define SQLITE_IOERR_DIR_FSYNC         (SQLITE_IOERR | (5<<8))\n#define SQLITE_IOERR_TRUNCATE          (SQLITE_IOERR | (6<<8))\n#define SQLITE_IOERR_FSTAT             (SQLITE_IOERR | (7<<8))\n#define SQLITE_IOERR_UNLOCK            (SQLITE_IOERR | (8<<8))\n#define SQLITE_IOERR_RDLOCK            (SQLITE_IOERR | (9<<8))\n#define SQLITE_IOERR_DELETE            (SQLITE_IOERR | (10<<8))\n#define SQLITE_IOERR_BLOCKED           (SQLITE_IOERR | (11<<8))\n#define SQLITE_IOERR_NOMEM             (SQLITE_IOERR | (12<<8))\n#define SQLITE_IOERR_ACCESS            (SQLITE_IOERR | (13<<8))\n#define SQLITE_IOERR_CHECKRESERVEDLOCK (SQLITE_IOERR | (14<<8))\n#define SQLITE_IOERR_LOCK              (SQLITE_IOERR | (15<<8))\n#define SQLITE_IOERR_CLOSE             (SQLITE_IOERR | (16<<8))\n#define SQLITE_IOERR_DIR_CLOSE         (SQLITE_IOERR | (17<<8))\n#define SQLITE_IOERR_SHMOPEN           (SQLITE_IOERR | (18<<8))\n#define SQLITE_IOERR_SHMSIZE           (SQLITE_IOERR | (19<<8))\n#define SQLITE_IOERR_SHMLOCK           (SQLITE_IOERR | (20<<8))\n#define SQLITE_IOERR_SHMMAP            (SQLITE_IOERR | (21<<8))\n#define SQLITE_IOERR_SEEK              (SQLITE_IOERR | (22<<8))\n#define SQLITE_LOCKED_SHAREDCACHE      (SQLITE_LOCKED |  (1<<8))\n#define SQLITE_BUSY_RECOVERY           (SQLITE_BUSY   |  (1<<8))\n#define SQLITE_CANTOPEN_NOTEMPDIR      (SQLITE_CANTOPEN | (1<<8))\n#define SQLITE_CANTOPEN_ISDIR          (SQLITE_CANTOPEN | (2<<8))\n#define SQLITE_CORRUPT_VTAB            (SQLITE_CORRUPT | (1<<8))\n#define SQLITE_READONLY_RECOVERY       (SQLITE_READONLY | (1<<8))\n#define SQLITE_READONLY_CANTLOCK       (SQLITE_READONLY | (2<<8))\n#define SQLITE_ABORT_ROLLBACK          (SQLITE_ABORT | (2<<8))\n\n/*\n** CAPI3REF: Flags For File Open Operations\n**\n** These bit values are intended for use in the\n** 3rd parameter to the [sqlite3_open_v2()] interface and\n** in the 4th parameter to the [sqlite3_vfs.xOpen] method.\n*/\n#define SQLITE_OPEN_READONLY         0x00000001  /* Ok for sqlite3_open_v2() */\n#define SQLITE_OPEN_READWRITE        0x00000002  /* Ok for sqlite3_open_v2() */\n#define SQLITE_OPEN_CREATE           0x00000004  /* Ok for sqlite3_open_v2() */\n#define SQLITE_OPEN_DELETEONCLOSE    0x00000008  /* VFS only */\n#define SQLITE_OPEN_EXCLUSIVE        0x00000010  /* VFS only */\n#define SQLITE_OPEN_AUTOPROXY        0x00000020  /* VFS only */\n#define SQLITE_OPEN_URI              0x00000040  /* Ok for sqlite3_open_v2() */\n#define SQLITE_OPEN_MEMORY           0x00000080  /* Ok for sqlite3_open_v2() */\n#define SQLITE_OPEN_MAIN_DB          0x00000100  /* VFS only */\n#define SQLITE_OPEN_TEMP_DB          0x00000200  /* VFS only */\n#define SQLITE_OPEN_TRANSIENT_DB     0x00000400  /* VFS only */\n#define SQLITE_OPEN_MAIN_JOURNAL     0x00000800  /* VFS only */\n#define SQLITE_OPEN_TEMP_JOURNAL     0x00001000  /* VFS only */\n#define SQLITE_OPEN_SUBJOURNAL       0x00002000  /* VFS only */\n#define SQLITE_OPEN_MASTER_JOURNAL   0x00004000  /* VFS only */\n#define SQLITE_OPEN_NOMUTEX          0x00008000  /* Ok for sqlite3_open_v2() */\n#define SQLITE_OPEN_FULLMUTEX        0x00010000  /* Ok for sqlite3_open_v2() */\n#define SQLITE_OPEN_SHAREDCACHE      0x00020000  /* Ok for sqlite3_open_v2() */\n#define SQLITE_OPEN_PRIVATECACHE     0x00040000  /* Ok for sqlite3_open_v2() */\n#define SQLITE_OPEN_WAL              0x00080000  /* VFS only */\n\n/* Reserved:                         0x00F00000 */\n\n/*\n** CAPI3REF: Device Characteristics\n**\n** The xDeviceCharacteristics method of the [sqlite3_io_methods]\n** object returns an integer which is a vector of these\n** bit values expressing I/O characteristics of the mass storage\n** device that holds the file that the [sqlite3_io_methods]\n** refers to.\n**\n** The SQLITE_IOCAP_ATOMIC property means that all writes of\n** any size are atomic.  The SQLITE_IOCAP_ATOMICnnn values\n** mean that writes of blocks that are nnn bytes in size and\n** are aligned to an address which is an integer multiple of\n** nnn are atomic.  The SQLITE_IOCAP_SAFE_APPEND value means\n** that when data is appended to a file, the data is appended\n** first then the size of the file is extended, never the other\n** way around.  The SQLITE_IOCAP_SEQUENTIAL property means that\n** information is written to disk in the same order as calls\n** to xWrite().  The SQLITE_IOCAP_POWERSAFE_OVERWRITE property means that\n** after reboot following a crash or power loss, the only bytes in a\n** file that were written at the application level might have changed\n** and that adjacent bytes, even bytes within the same sector are\n** guaranteed to be unchanged.\n*/\n#define SQLITE_IOCAP_ATOMIC                 0x00000001\n#define SQLITE_IOCAP_ATOMIC512              0x00000002\n#define SQLITE_IOCAP_ATOMIC1K               0x00000004\n#define SQLITE_IOCAP_ATOMIC2K               0x00000008\n#define SQLITE_IOCAP_ATOMIC4K               0x00000010\n#define SQLITE_IOCAP_ATOMIC8K               0x00000020\n#define SQLITE_IOCAP_ATOMIC16K              0x00000040\n#define SQLITE_IOCAP_ATOMIC32K              0x00000080\n#define SQLITE_IOCAP_ATOMIC64K              0x00000100\n#define SQLITE_IOCAP_SAFE_APPEND            0x00000200\n#define SQLITE_IOCAP_SEQUENTIAL             0x00000400\n#define SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN  0x00000800\n#define SQLITE_IOCAP_POWERSAFE_OVERWRITE    0x00001000\n\n/*\n** CAPI3REF: File Locking Levels\n**\n** SQLite uses one of these integer values as the second\n** argument to calls it makes to the xLock() and xUnlock() methods\n** of an [sqlite3_io_methods] object.\n*/\n#define SQLITE_LOCK_NONE          0\n#define SQLITE_LOCK_SHARED        1\n#define SQLITE_LOCK_RESERVED      2\n#define SQLITE_LOCK_PENDING       3\n#define SQLITE_LOCK_EXCLUSIVE     4\n\n/*\n** CAPI3REF: Synchronization Type Flags\n**\n** When SQLite invokes the xSync() method of an\n** [sqlite3_io_methods] object it uses a combination of\n** these integer values as the second argument.\n**\n** When the SQLITE_SYNC_DATAONLY flag is used, it means that the\n** sync operation only needs to flush data to mass storage.  Inode\n** information need not be flushed. If the lower four bits of the flag\n** equal SQLITE_SYNC_NORMAL, that means to use normal fsync() semantics.\n** If the lower four bits equal SQLITE_SYNC_FULL, that means\n** to use Mac OS X style fullsync instead of fsync().\n**\n** Do not confuse the SQLITE_SYNC_NORMAL and SQLITE_SYNC_FULL flags\n** with the [PRAGMA synchronous]=NORMAL and [PRAGMA synchronous]=FULL\n** settings.  The [synchronous pragma] determines when calls to the\n** xSync VFS method occur and applies uniformly across all platforms.\n** The SQLITE_SYNC_NORMAL and SQLITE_SYNC_FULL flags determine how\n** energetic or rigorous or forceful the sync operations are and\n** only make a difference on Mac OSX for the default SQLite code.\n** (Third-party VFS implementations might also make the distinction\n** between SQLITE_SYNC_NORMAL and SQLITE_SYNC_FULL, but among the\n** operating systems natively supported by SQLite, only Mac OSX\n** cares about the difference.)\n*/\n#define SQLITE_SYNC_NORMAL        0x00002\n#define SQLITE_SYNC_FULL          0x00003\n#define SQLITE_SYNC_DATAONLY      0x00010\n\n/*\n** CAPI3REF: OS Interface Open File Handle\n**\n** An [sqlite3_file] object represents an open file in the \n** [sqlite3_vfs | OS interface layer].  Individual OS interface\n** implementations will\n** want to subclass this object by appending additional fields\n** for their own use.  The pMethods entry is a pointer to an\n** [sqlite3_io_methods] object that defines methods for performing\n** I/O operations on the open file.\n*/\ntypedef struct sqlite3_file sqlite3_file;\nstruct sqlite3_file {\n  const struct sqlite3_io_methods *pMethods;  /* Methods for an open file */\n};\n\n/*\n** CAPI3REF: OS Interface File Virtual Methods Object\n**\n** Every file opened by the [sqlite3_vfs.xOpen] method populates an\n** [sqlite3_file] object (or, more commonly, a subclass of the\n** [sqlite3_file] object) with a pointer to an instance of this object.\n** This object defines the methods used to perform various operations\n** against the open file represented by the [sqlite3_file] object.\n**\n** If the [sqlite3_vfs.xOpen] method sets the sqlite3_file.pMethods element \n** to a non-NULL pointer, then the sqlite3_io_methods.xClose method\n** may be invoked even if the [sqlite3_vfs.xOpen] reported that it failed.  The\n** only way to prevent a call to xClose following a failed [sqlite3_vfs.xOpen]\n** is for the [sqlite3_vfs.xOpen] to set the sqlite3_file.pMethods element\n** to NULL.\n**\n** The flags argument to xSync may be one of [SQLITE_SYNC_NORMAL] or\n** [SQLITE_SYNC_FULL].  The first choice is the normal fsync().\n** The second choice is a Mac OS X style fullsync.  The [SQLITE_SYNC_DATAONLY]\n** flag may be ORed in to indicate that only the data of the file\n** and not its inode needs to be synced.\n**\n** The integer values to xLock() and xUnlock() are one of\n** <ul>\n** <li> [SQLITE_LOCK_NONE],\n** <li> [SQLITE_LOCK_SHARED],\n** <li> [SQLITE_LOCK_RESERVED],\n** <li> [SQLITE_LOCK_PENDING], or\n** <li> [SQLITE_LOCK_EXCLUSIVE].\n** </ul>\n** xLock() increases the lock. xUnlock() decreases the lock.\n** The xCheckReservedLock() method checks whether any database connection,\n** either in this process or in some other process, is holding a RESERVED,\n** PENDING, or EXCLUSIVE lock on the file.  It returns true\n** if such a lock exists and false otherwise.\n**\n** The xFileControl() method is a generic interface that allows custom\n** VFS implementations to directly control an open file using the\n** [sqlite3_file_control()] interface.  The second \"op\" argument is an\n** integer opcode.  The third argument is a generic pointer intended to\n** point to a structure that may contain arguments or space in which to\n** write return values.  Potential uses for xFileControl() might be\n** functions to enable blocking locks with timeouts, to change the\n** locking strategy (for example to use dot-file locks), to inquire\n** about the status of a lock, or to break stale locks.  The SQLite\n** core reserves all opcodes less than 100 for its own use.\n** A [SQLITE_FCNTL_LOCKSTATE | list of opcodes] less than 100 is available.\n** Applications that define a custom xFileControl method should use opcodes\n** greater than 100 to avoid conflicts.  VFS implementations should\n** return [SQLITE_NOTFOUND] for file control opcodes that they do not\n** recognize.\n**\n** The xSectorSize() method returns the sector size of the\n** device that underlies the file.  The sector size is the\n** minimum write that can be performed without disturbing\n** other bytes in the file.  The xDeviceCharacteristics()\n** method returns a bit vector describing behaviors of the\n** underlying device:\n**\n** <ul>\n** <li> [SQLITE_IOCAP_ATOMIC]\n** <li> [SQLITE_IOCAP_ATOMIC512]\n** <li> [SQLITE_IOCAP_ATOMIC1K]\n** <li> [SQLITE_IOCAP_ATOMIC2K]\n** <li> [SQLITE_IOCAP_ATOMIC4K]\n** <li> [SQLITE_IOCAP_ATOMIC8K]\n** <li> [SQLITE_IOCAP_ATOMIC16K]\n** <li> [SQLITE_IOCAP_ATOMIC32K]\n** <li> [SQLITE_IOCAP_ATOMIC64K]\n** <li> [SQLITE_IOCAP_SAFE_APPEND]\n** <li> [SQLITE_IOCAP_SEQUENTIAL]\n** </ul>\n**\n** The SQLITE_IOCAP_ATOMIC property means that all writes of\n** any size are atomic.  The SQLITE_IOCAP_ATOMICnnn values\n** mean that writes of blocks that are nnn bytes in size and\n** are aligned to an address which is an integer multiple of\n** nnn are atomic.  The SQLITE_IOCAP_SAFE_APPEND value means\n** that when data is appended to a file, the data is appended\n** first then the size of the file is extended, never the other\n** way around.  The SQLITE_IOCAP_SEQUENTIAL property means that\n** information is written to disk in the same order as calls\n** to xWrite().\n**\n** If xRead() returns SQLITE_IOERR_SHORT_READ it must also fill\n** in the unread portions of the buffer with zeros.  A VFS that\n** fails to zero-fill short reads might seem to work.  However,\n** failure to zero-fill short reads will eventually lead to\n** database corruption.\n*/\ntypedef struct sqlite3_io_methods sqlite3_io_methods;\nstruct sqlite3_io_methods {\n  int iVersion;\n  int (*xClose)(sqlite3_file*);\n  int (*xRead)(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst);\n  int (*xWrite)(sqlite3_file*, const void*, int iAmt, sqlite3_int64 iOfst);\n  int (*xTruncate)(sqlite3_file*, sqlite3_int64 size);\n  int (*xSync)(sqlite3_file*, int flags);\n  int (*xFileSize)(sqlite3_file*, sqlite3_int64 *pSize);\n  int (*xLock)(sqlite3_file*, int);\n  int (*xUnlock)(sqlite3_file*, int);\n  int (*xCheckReservedLock)(sqlite3_file*, int *pResOut);\n  int (*xFileControl)(sqlite3_file*, int op, void *pArg);\n  int (*xSectorSize)(sqlite3_file*);\n  int (*xDeviceCharacteristics)(sqlite3_file*);\n  /* Methods above are valid for version 1 */\n  int (*xShmMap)(sqlite3_file*, int iPg, int pgsz, int, void volatile**);\n  int (*xShmLock)(sqlite3_file*, int offset, int n, int flags);\n  void (*xShmBarrier)(sqlite3_file*);\n  int (*xShmUnmap)(sqlite3_file*, int deleteFlag);\n  /* Methods above are valid for version 2 */\n  /* Additional methods may be added in future releases */\n};\n\n/*\n** CAPI3REF: Standard File Control Opcodes\n**\n** These integer constants are opcodes for the xFileControl method\n** of the [sqlite3_io_methods] object and for the [sqlite3_file_control()]\n** interface.\n**\n** The [SQLITE_FCNTL_LOCKSTATE] opcode is used for debugging.  This\n** opcode causes the xFileControl method to write the current state of\n** the lock (one of [SQLITE_LOCK_NONE], [SQLITE_LOCK_SHARED],\n** [SQLITE_LOCK_RESERVED], [SQLITE_LOCK_PENDING], or [SQLITE_LOCK_EXCLUSIVE])\n** into an integer that the pArg argument points to. This capability\n** is used during testing and only needs to be supported when SQLITE_TEST\n** is defined.\n** <ul>\n** <li>[[SQLITE_FCNTL_SIZE_HINT]]\n** The [SQLITE_FCNTL_SIZE_HINT] opcode is used by SQLite to give the VFS\n** layer a hint of how large the database file will grow to be during the\n** current transaction.  This hint is not guaranteed to be accurate but it\n** is often close.  The underlying VFS might choose to preallocate database\n** file space based on this hint in order to help writes to the database\n** file run faster.\n**\n** <li>[[SQLITE_FCNTL_CHUNK_SIZE]]\n** The [SQLITE_FCNTL_CHUNK_SIZE] opcode is used to request that the VFS\n** extends and truncates the database file in chunks of a size specified\n** by the user. The fourth argument to [sqlite3_file_control()] should \n** point to an integer (type int) containing the new chunk-size to use\n** for the nominated database. Allocating database file space in large\n** chunks (say 1MB at a time), may reduce file-system fragmentation and\n** improve performance on some systems.\n**\n** <li>[[SQLITE_FCNTL_FILE_POINTER]]\n** The [SQLITE_FCNTL_FILE_POINTER] opcode is used to obtain a pointer\n** to the [sqlite3_file] object associated with a particular database\n** connection.  See the [sqlite3_file_control()] documentation for\n** additional information.\n**\n** <li>[[SQLITE_FCNTL_SYNC_OMITTED]]\n** ^(The [SQLITE_FCNTL_SYNC_OMITTED] opcode is generated internally by\n** SQLite and sent to all VFSes in place of a call to the xSync method\n** when the database connection has [PRAGMA synchronous] set to OFF.)^\n** Some specialized VFSes need this signal in order to operate correctly\n** when [PRAGMA synchronous | PRAGMA synchronous=OFF] is set, but most \n** VFSes do not need this signal and should silently ignore this opcode.\n** Applications should not call [sqlite3_file_control()] with this\n** opcode as doing so may disrupt the operation of the specialized VFSes\n** that do require it.  \n**\n** <li>[[SQLITE_FCNTL_WIN32_AV_RETRY]]\n** ^The [SQLITE_FCNTL_WIN32_AV_RETRY] opcode is used to configure automatic\n** retry counts and intervals for certain disk I/O operations for the\n** windows [VFS] in order to provide robustness in the presence of\n** anti-virus programs.  By default, the windows VFS will retry file read,\n** file write, and file delete operations up to 10 times, with a delay\n** of 25 milliseconds before the first retry and with the delay increasing\n** by an additional 25 milliseconds with each subsequent retry.  This\n** opcode allows these two values (10 retries and 25 milliseconds of delay)\n** to be adjusted.  The values are changed for all database connections\n** within the same process.  The argument is a pointer to an array of two\n** integers where the first integer i the new retry count and the second\n** integer is the delay.  If either integer is negative, then the setting\n** is not changed but instead the prior value of that setting is written\n** into the array entry, allowing the current retry settings to be\n** interrogated.  The zDbName parameter is ignored.\n**\n** <li>[[SQLITE_FCNTL_PERSIST_WAL]]\n** ^The [SQLITE_FCNTL_PERSIST_WAL] opcode is used to set or query the\n** persistent [WAL | Write Ahead Log] setting.  By default, the auxiliary\n** write ahead log and shared memory files used for transaction control\n** are automatically deleted when the latest connection to the database\n** closes.  Setting persistent WAL mode causes those files to persist after\n** close.  Persisting the files is useful when other processes that do not\n** have write permission on the directory containing the database file want\n** to read the database file, as the WAL and shared memory files must exist\n** in order for the database to be readable.  The fourth parameter to\n** [sqlite3_file_control()] for this opcode should be a pointer to an integer.\n** That integer is 0 to disable persistent WAL mode or 1 to enable persistent\n** WAL mode.  If the integer is -1, then it is overwritten with the current\n** WAL persistence setting.\n**\n** <li>[[SQLITE_FCNTL_POWERSAFE_OVERWRITE]]\n** ^The [SQLITE_FCNTL_POWERSAFE_OVERWRITE] opcode is used to set or query the\n** persistent \"powersafe-overwrite\" or \"PSOW\" setting.  The PSOW setting\n** determines the [SQLITE_IOCAP_POWERSAFE_OVERWRITE] bit of the\n** xDeviceCharacteristics methods. The fourth parameter to\n** [sqlite3_file_control()] for this opcode should be a pointer to an integer.\n** That integer is 0 to disable zero-damage mode or 1 to enable zero-damage\n** mode.  If the integer is -1, then it is overwritten with the current\n** zero-damage mode setting.\n**\n** <li>[[SQLITE_FCNTL_OVERWRITE]]\n** ^The [SQLITE_FCNTL_OVERWRITE] opcode is invoked by SQLite after opening\n** a write transaction to indicate that, unless it is rolled back for some\n** reason, the entire database file will be overwritten by the current \n** transaction. This is used by VACUUM operations.\n**\n** <li>[[SQLITE_FCNTL_VFSNAME]]\n** ^The [SQLITE_FCNTL_VFSNAME] opcode can be used to obtain the names of\n** all [VFSes] in the VFS stack.  The names are of all VFS shims and the\n** final bottom-level VFS are written into memory obtained from \n** [sqlite3_malloc()] and the result is stored in the char* variable\n** that the fourth parameter of [sqlite3_file_control()] points to.\n** The caller is responsible for freeing the memory when done.  As with\n** all file-control actions, there is no guarantee that this will actually\n** do anything.  Callers should initialize the char* variable to a NULL\n** pointer in case this file-control is not implemented.  This file-control\n** is intended for diagnostic use only.\n**\n** <li>[[SQLITE_FCNTL_PRAGMA]]\n** ^Whenever a [PRAGMA] statement is parsed, an [SQLITE_FCNTL_PRAGMA] \n** file control is sent to the open [sqlite3_file] object corresponding\n** to the database file to which the pragma statement refers. ^The argument\n** to the [SQLITE_FCNTL_PRAGMA] file control is an array of\n** pointers to strings (char**) in which the second element of the array\n** is the name of the pragma and the third element is the argument to the\n** pragma or NULL if the pragma has no argument.  ^The handler for an\n** [SQLITE_FCNTL_PRAGMA] file control can optionally make the first element\n** of the char** argument point to a string obtained from [sqlite3_mprintf()]\n** or the equivalent and that string will become the result of the pragma or\n** the error message if the pragma fails. ^If the\n** [SQLITE_FCNTL_PRAGMA] file control returns [SQLITE_NOTFOUND], then normal \n** [PRAGMA] processing continues.  ^If the [SQLITE_FCNTL_PRAGMA]\n** file control returns [SQLITE_OK], then the parser assumes that the\n** VFS has handled the PRAGMA itself and the parser generates a no-op\n** prepared statement.  ^If the [SQLITE_FCNTL_PRAGMA] file control returns\n** any result code other than [SQLITE_OK] or [SQLITE_NOTFOUND], that means\n** that the VFS encountered an error while handling the [PRAGMA] and the\n** compilation of the PRAGMA fails with an error.  ^The [SQLITE_FCNTL_PRAGMA]\n** file control occurs at the beginning of pragma statement analysis and so\n** it is able to override built-in [PRAGMA] statements.\n** </ul>\n*/\n#define SQLITE_FCNTL_LOCKSTATE               1\n#define SQLITE_GET_LOCKPROXYFILE             2\n#define SQLITE_SET_LOCKPROXYFILE             3\n#define SQLITE_LAST_ERRNO                    4\n#define SQLITE_FCNTL_SIZE_HINT               5\n#define SQLITE_FCNTL_CHUNK_SIZE              6\n#define SQLITE_FCNTL_FILE_POINTER            7\n#define SQLITE_FCNTL_SYNC_OMITTED            8\n#define SQLITE_FCNTL_WIN32_AV_RETRY          9\n#define SQLITE_FCNTL_PERSIST_WAL            10\n#define SQLITE_FCNTL_OVERWRITE              11\n#define SQLITE_FCNTL_VFSNAME                12\n#define SQLITE_FCNTL_POWERSAFE_OVERWRITE    13\n#define SQLITE_FCNTL_PRAGMA                 14\n\n/*\n** CAPI3REF: Mutex Handle\n**\n** The mutex module within SQLite defines [sqlite3_mutex] to be an\n** abstract type for a mutex object.  The SQLite core never looks\n** at the internal representation of an [sqlite3_mutex].  It only\n** deals with pointers to the [sqlite3_mutex] object.\n**\n** Mutexes are created using [sqlite3_mutex_alloc()].\n*/\ntypedef struct sqlite3_mutex sqlite3_mutex;\n\n/*\n** CAPI3REF: OS Interface Object\n**\n** An instance of the sqlite3_vfs object defines the interface between\n** the SQLite core and the underlying operating system.  The \"vfs\"\n** in the name of the object stands for \"virtual file system\".  See\n** the [VFS | VFS documentation] for further information.\n**\n** The value of the iVersion field is initially 1 but may be larger in\n** future versions of SQLite.  Additional fields may be appended to this\n** object when the iVersion value is increased.  Note that the structure\n** of the sqlite3_vfs object changes in the transaction between\n** SQLite version 3.5.9 and 3.6.0 and yet the iVersion field was not\n** modified.\n**\n** The szOsFile field is the size of the subclassed [sqlite3_file]\n** structure used by this VFS.  mxPathname is the maximum length of\n** a pathname in this VFS.\n**\n** Registered sqlite3_vfs objects are kept on a linked list formed by\n** the pNext pointer.  The [sqlite3_vfs_register()]\n** and [sqlite3_vfs_unregister()] interfaces manage this list\n** in a thread-safe way.  The [sqlite3_vfs_find()] interface\n** searches the list.  Neither the application code nor the VFS\n** implementation should use the pNext pointer.\n**\n** The pNext field is the only field in the sqlite3_vfs\n** structure that SQLite will ever modify.  SQLite will only access\n** or modify this field while holding a particular static mutex.\n** The application should never modify anything within the sqlite3_vfs\n** object once the object has been registered.\n**\n** The zName field holds the name of the VFS module.  The name must\n** be unique across all VFS modules.\n**\n** [[sqlite3_vfs.xOpen]]\n** ^SQLite guarantees that the zFilename parameter to xOpen\n** is either a NULL pointer or string obtained\n** from xFullPathname() with an optional suffix added.\n** ^If a suffix is added to the zFilename parameter, it will\n** consist of a single \"-\" character followed by no more than\n** 11 alphanumeric and/or \"-\" characters.\n** ^SQLite further guarantees that\n** the string will be valid and unchanged until xClose() is\n** called. Because of the previous sentence,\n** the [sqlite3_file] can safely store a pointer to the\n** filename if it needs to remember the filename for some reason.\n** If the zFilename parameter to xOpen is a NULL pointer then xOpen\n** must invent its own temporary name for the file.  ^Whenever the \n** xFilename parameter is NULL it will also be the case that the\n** flags parameter will include [SQLITE_OPEN_DELETEONCLOSE].\n**\n** The flags argument to xOpen() includes all bits set in\n** the flags argument to [sqlite3_open_v2()].  Or if [sqlite3_open()]\n** or [sqlite3_open16()] is used, then flags includes at least\n** [SQLITE_OPEN_READWRITE] | [SQLITE_OPEN_CREATE]. \n** If xOpen() opens a file read-only then it sets *pOutFlags to\n** include [SQLITE_OPEN_READONLY].  Other bits in *pOutFlags may be set.\n**\n** ^(SQLite will also add one of the following flags to the xOpen()\n** call, depending on the object being opened:\n**\n** <ul>\n** <li>  [SQLITE_OPEN_MAIN_DB]\n** <li>  [SQLITE_OPEN_MAIN_JOURNAL]\n** <li>  [SQLITE_OPEN_TEMP_DB]\n** <li>  [SQLITE_OPEN_TEMP_JOURNAL]\n** <li>  [SQLITE_OPEN_TRANSIENT_DB]\n** <li>  [SQLITE_OPEN_SUBJOURNAL]\n** <li>  [SQLITE_OPEN_MASTER_JOURNAL]\n** <li>  [SQLITE_OPEN_WAL]\n** </ul>)^\n**\n** The file I/O implementation can use the object type flags to\n** change the way it deals with files.  For example, an application\n** that does not care about crash recovery or rollback might make\n** the open of a journal file a no-op.  Writes to this journal would\n** also be no-ops, and any attempt to read the journal would return\n** SQLITE_IOERR.  Or the implementation might recognize that a database\n** file will be doing page-aligned sector reads and writes in a random\n** order and set up its I/O subsystem accordingly.\n**\n** SQLite might also add one of the following flags to the xOpen method:\n**\n** <ul>\n** <li> [SQLITE_OPEN_DELETEONCLOSE]\n** <li> [SQLITE_OPEN_EXCLUSIVE]\n** </ul>\n**\n** The [SQLITE_OPEN_DELETEONCLOSE] flag means the file should be\n** deleted when it is closed.  ^The [SQLITE_OPEN_DELETEONCLOSE]\n** will be set for TEMP databases and their journals, transient\n** databases, and subjournals.\n**\n** ^The [SQLITE_OPEN_EXCLUSIVE] flag is always used in conjunction\n** with the [SQLITE_OPEN_CREATE] flag, which are both directly\n** analogous to the O_EXCL and O_CREAT flags of the POSIX open()\n** API.  The SQLITE_OPEN_EXCLUSIVE flag, when paired with the \n** SQLITE_OPEN_CREATE, is used to indicate that file should always\n** be created, and that it is an error if it already exists.\n** It is <i>not</i> used to indicate the file should be opened \n** for exclusive access.\n**\n** ^At least szOsFile bytes of memory are allocated by SQLite\n** to hold the  [sqlite3_file] structure passed as the third\n** argument to xOpen.  The xOpen method does not have to\n** allocate the structure; it should just fill it in.  Note that\n** the xOpen method must set the sqlite3_file.pMethods to either\n** a valid [sqlite3_io_methods] object or to NULL.  xOpen must do\n** this even if the open fails.  SQLite expects that the sqlite3_file.pMethods\n** element will be valid after xOpen returns regardless of the success\n** or failure of the xOpen call.\n**\n** [[sqlite3_vfs.xAccess]]\n** ^The flags argument to xAccess() may be [SQLITE_ACCESS_EXISTS]\n** to test for the existence of a file, or [SQLITE_ACCESS_READWRITE] to\n** test whether a file is readable and writable, or [SQLITE_ACCESS_READ]\n** to test whether a file is at least readable.   The file can be a\n** directory.\n**\n** ^SQLite will always allocate at least mxPathname+1 bytes for the\n** output buffer xFullPathname.  The exact size of the output buffer\n** is also passed as a parameter to both  methods. If the output buffer\n** is not large enough, [SQLITE_CANTOPEN] should be returned. Since this is\n** handled as a fatal error by SQLite, vfs implementations should endeavor\n** to prevent this by setting mxPathname to a sufficiently large value.\n**\n** The xRandomness(), xSleep(), xCurrentTime(), and xCurrentTimeInt64()\n** interfaces are not strictly a part of the filesystem, but they are\n** included in the VFS structure for completeness.\n** The xRandomness() function attempts to return nBytes bytes\n** of good-quality randomness into zOut.  The return value is\n** the actual number of bytes of randomness obtained.\n** The xSleep() method causes the calling thread to sleep for at\n** least the number of microseconds given.  ^The xCurrentTime()\n** method returns a Julian Day Number for the current date and time as\n** a floating point value.\n** ^The xCurrentTimeInt64() method returns, as an integer, the Julian\n** Day Number multiplied by 86400000 (the number of milliseconds in \n** a 24-hour day).  \n** ^SQLite will use the xCurrentTimeInt64() method to get the current\n** date and time if that method is available (if iVersion is 2 or \n** greater and the function pointer is not NULL) and will fall back\n** to xCurrentTime() if xCurrentTimeInt64() is unavailable.\n**\n** ^The xSetSystemCall(), xGetSystemCall(), and xNestSystemCall() interfaces\n** are not used by the SQLite core.  These optional interfaces are provided\n** by some VFSes to facilitate testing of the VFS code. By overriding \n** system calls with functions under its control, a test program can\n** simulate faults and error conditions that would otherwise be difficult\n** or impossible to induce.  The set of system calls that can be overridden\n** varies from one VFS to another, and from one version of the same VFS to the\n** next.  Applications that use these interfaces must be prepared for any\n** or all of these interfaces to be NULL or for their behavior to change\n** from one release to the next.  Applications must not attempt to access\n** any of these methods if the iVersion of the VFS is less than 3.\n*/\ntypedef struct sqlite3_vfs sqlite3_vfs;\ntypedef void (*sqlite3_syscall_ptr)(void);\nstruct sqlite3_vfs {\n  int iVersion;            /* Structure version number (currently 3) */\n  int szOsFile;            /* Size of subclassed sqlite3_file */\n  int mxPathname;          /* Maximum file pathname length */\n  sqlite3_vfs *pNext;      /* Next registered VFS */\n  const char *zName;       /* Name of this virtual file system */\n  void *pAppData;          /* Pointer to application-specific data */\n  int (*xOpen)(sqlite3_vfs*, const char *zName, sqlite3_file*,\n               int flags, int *pOutFlags);\n  int (*xDelete)(sqlite3_vfs*, const char *zName, int syncDir);\n  int (*xAccess)(sqlite3_vfs*, const char *zName, int flags, int *pResOut);\n  int (*xFullPathname)(sqlite3_vfs*, const char *zName, int nOut, char *zOut);\n  void *(*xDlOpen)(sqlite3_vfs*, const char *zFilename);\n  void (*xDlError)(sqlite3_vfs*, int nByte, char *zErrMsg);\n  void (*(*xDlSym)(sqlite3_vfs*,void*, const char *zSymbol))(void);\n  void (*xDlClose)(sqlite3_vfs*, void*);\n  int (*xRandomness)(sqlite3_vfs*, int nByte, char *zOut);\n  int (*xSleep)(sqlite3_vfs*, int microseconds);\n  int (*xCurrentTime)(sqlite3_vfs*, double*);\n  int (*xGetLastError)(sqlite3_vfs*, int, char *);\n  /*\n  ** The methods above are in version 1 of the sqlite_vfs object\n  ** definition.  Those that follow are added in version 2 or later\n  */\n  int (*xCurrentTimeInt64)(sqlite3_vfs*, sqlite3_int64*);\n  /*\n  ** The methods above are in versions 1 and 2 of the sqlite_vfs object.\n  ** Those below are for version 3 and greater.\n  */\n  int (*xSetSystemCall)(sqlite3_vfs*, const char *zName, sqlite3_syscall_ptr);\n  sqlite3_syscall_ptr (*xGetSystemCall)(sqlite3_vfs*, const char *zName);\n  const char *(*xNextSystemCall)(sqlite3_vfs*, const char *zName);\n  /*\n  ** The methods above are in versions 1 through 3 of the sqlite_vfs object.\n  ** New fields may be appended in figure versions.  The iVersion\n  ** value will increment whenever this happens. \n  */\n};\n\n/*\n** CAPI3REF: Flags for the xAccess VFS method\n**\n** These integer constants can be used as the third parameter to\n** the xAccess method of an [sqlite3_vfs] object.  They determine\n** what kind of permissions the xAccess method is looking for.\n** With SQLITE_ACCESS_EXISTS, the xAccess method\n** simply checks whether the file exists.\n** With SQLITE_ACCESS_READWRITE, the xAccess method\n** checks whether the named directory is both readable and writable\n** (in other words, if files can be added, removed, and renamed within\n** the directory).\n** The SQLITE_ACCESS_READWRITE constant is currently used only by the\n** [temp_store_directory pragma], though this could change in a future\n** release of SQLite.\n** With SQLITE_ACCESS_READ, the xAccess method\n** checks whether the file is readable.  The SQLITE_ACCESS_READ constant is\n** currently unused, though it might be used in a future release of\n** SQLite.\n*/\n#define SQLITE_ACCESS_EXISTS    0\n#define SQLITE_ACCESS_READWRITE 1   /* Used by PRAGMA temp_store_directory */\n#define SQLITE_ACCESS_READ      2   /* Unused */\n\n/*\n** CAPI3REF: Flags for the xShmLock VFS method\n**\n** These integer constants define the various locking operations\n** allowed by the xShmLock method of [sqlite3_io_methods].  The\n** following are the only legal combinations of flags to the\n** xShmLock method:\n**\n** <ul>\n** <li>  SQLITE_SHM_LOCK | SQLITE_SHM_SHARED\n** <li>  SQLITE_SHM_LOCK | SQLITE_SHM_EXCLUSIVE\n** <li>  SQLITE_SHM_UNLOCK | SQLITE_SHM_SHARED\n** <li>  SQLITE_SHM_UNLOCK | SQLITE_SHM_EXCLUSIVE\n** </ul>\n**\n** When unlocking, the same SHARED or EXCLUSIVE flag must be supplied as\n** was given no the corresponding lock.  \n**\n** The xShmLock method can transition between unlocked and SHARED or\n** between unlocked and EXCLUSIVE.  It cannot transition between SHARED\n** and EXCLUSIVE.\n*/\n#define SQLITE_SHM_UNLOCK       1\n#define SQLITE_SHM_LOCK         2\n#define SQLITE_SHM_SHARED       4\n#define SQLITE_SHM_EXCLUSIVE    8\n\n/*\n** CAPI3REF: Maximum xShmLock index\n**\n** The xShmLock method on [sqlite3_io_methods] may use values\n** between 0 and this upper bound as its \"offset\" argument.\n** The SQLite core will never attempt to acquire or release a\n** lock outside of this range\n*/\n#define SQLITE_SHM_NLOCK        8\n\n\n/*\n** CAPI3REF: Initialize The SQLite Library\n**\n** ^The sqlite3_initialize() routine initializes the\n** SQLite library.  ^The sqlite3_shutdown() routine\n** deallocates any resources that were allocated by sqlite3_initialize().\n** These routines are designed to aid in process initialization and\n** shutdown on embedded systems.  Workstation applications using\n** SQLite normally do not need to invoke either of these routines.\n**\n** A call to sqlite3_initialize() is an \"effective\" call if it is\n** the first time sqlite3_initialize() is invoked during the lifetime of\n** the process, or if it is the first time sqlite3_initialize() is invoked\n** following a call to sqlite3_shutdown().  ^(Only an effective call\n** of sqlite3_initialize() does any initialization.  All other calls\n** are harmless no-ops.)^\n**\n** A call to sqlite3_shutdown() is an \"effective\" call if it is the first\n** call to sqlite3_shutdown() since the last sqlite3_initialize().  ^(Only\n** an effective call to sqlite3_shutdown() does any deinitialization.\n** All other valid calls to sqlite3_shutdown() are harmless no-ops.)^\n**\n** The sqlite3_initialize() interface is threadsafe, but sqlite3_shutdown()\n** is not.  The sqlite3_shutdown() interface must only be called from a\n** single thread.  All open [database connections] must be closed and all\n** other SQLite resources must be deallocated prior to invoking\n** sqlite3_shutdown().\n**\n** Among other things, ^sqlite3_initialize() will invoke\n** sqlite3_os_init().  Similarly, ^sqlite3_shutdown()\n** will invoke sqlite3_os_end().\n**\n** ^The sqlite3_initialize() routine returns [SQLITE_OK] on success.\n** ^If for some reason, sqlite3_initialize() is unable to initialize\n** the library (perhaps it is unable to allocate a needed resource such\n** as a mutex) it returns an [error code] other than [SQLITE_OK].\n**\n** ^The sqlite3_initialize() routine is called internally by many other\n** SQLite interfaces so that an application usually does not need to\n** invoke sqlite3_initialize() directly.  For example, [sqlite3_open()]\n** calls sqlite3_initialize() so the SQLite library will be automatically\n** initialized when [sqlite3_open()] is called if it has not be initialized\n** already.  ^However, if SQLite is compiled with the [SQLITE_OMIT_AUTOINIT]\n** compile-time option, then the automatic calls to sqlite3_initialize()\n** are omitted and the application must call sqlite3_initialize() directly\n** prior to using any other SQLite interface.  For maximum portability,\n** it is recommended that applications always invoke sqlite3_initialize()\n** directly prior to using any other SQLite interface.  Future releases\n** of SQLite may require this.  In other words, the behavior exhibited\n** when SQLite is compiled with [SQLITE_OMIT_AUTOINIT] might become the\n** default behavior in some future release of SQLite.\n**\n** The sqlite3_os_init() routine does operating-system specific\n** initialization of the SQLite library.  The sqlite3_os_end()\n** routine undoes the effect of sqlite3_os_init().  Typical tasks\n** performed by these routines include allocation or deallocation\n** of static resources, initialization of global variables,\n** setting up a default [sqlite3_vfs] module, or setting up\n** a default configuration using [sqlite3_config()].\n**\n** The application should never invoke either sqlite3_os_init()\n** or sqlite3_os_end() directly.  The application should only invoke\n** sqlite3_initialize() and sqlite3_shutdown().  The sqlite3_os_init()\n** interface is called automatically by sqlite3_initialize() and\n** sqlite3_os_end() is called by sqlite3_shutdown().  Appropriate\n** implementations for sqlite3_os_init() and sqlite3_os_end()\n** are built into SQLite when it is compiled for Unix, Windows, or OS/2.\n** When [custom builds | built for other platforms]\n** (using the [SQLITE_OS_OTHER=1] compile-time\n** option) the application must supply a suitable implementation for\n** sqlite3_os_init() and sqlite3_os_end().  An application-supplied\n** implementation of sqlite3_os_init() or sqlite3_os_end()\n** must return [SQLITE_OK] on success and some other [error code] upon\n** failure.\n*/\nSQLITE_API int sqlite3_initialize(void);\nSQLITE_API int sqlite3_shutdown(void);\nSQLITE_API int sqlite3_os_init(void);\nSQLITE_API int sqlite3_os_end(void);\n\n/*\n** CAPI3REF: Configuring The SQLite Library\n**\n** The sqlite3_config() interface is used to make global configuration\n** changes to SQLite in order to tune SQLite to the specific needs of\n** the application.  The default configuration is recommended for most\n** applications and so this routine is usually not necessary.  It is\n** provided to support rare applications with unusual needs.\n**\n** The sqlite3_config() interface is not threadsafe.  The application\n** must insure that no other SQLite interfaces are invoked by other\n** threads while sqlite3_config() is running.  Furthermore, sqlite3_config()\n** may only be invoked prior to library initialization using\n** [sqlite3_initialize()] or after shutdown by [sqlite3_shutdown()].\n** ^If sqlite3_config() is called after [sqlite3_initialize()] and before\n** [sqlite3_shutdown()] then it will return SQLITE_MISUSE.\n** Note, however, that ^sqlite3_config() can be called as part of the\n** implementation of an application-defined [sqlite3_os_init()].\n**\n** The first argument to sqlite3_config() is an integer\n** [configuration option] that determines\n** what property of SQLite is to be configured.  Subsequent arguments\n** vary depending on the [configuration option]\n** in the first argument.\n**\n** ^When a configuration option is set, sqlite3_config() returns [SQLITE_OK].\n** ^If the option is unknown or SQLite is unable to set the option\n** then this routine returns a non-zero [error code].\n*/\nSQLITE_API int sqlite3_config(int, ...);\n\n/*\n** CAPI3REF: Configure database connections\n**\n** The sqlite3_db_config() interface is used to make configuration\n** changes to a [database connection].  The interface is similar to\n** [sqlite3_config()] except that the changes apply to a single\n** [database connection] (specified in the first argument).\n**\n** The second argument to sqlite3_db_config(D,V,...)  is the\n** [SQLITE_DBCONFIG_LOOKASIDE | configuration verb] - an integer code \n** that indicates what aspect of the [database connection] is being configured.\n** Subsequent arguments vary depending on the configuration verb.\n**\n** ^Calls to sqlite3_db_config() return SQLITE_OK if and only if\n** the call is considered successful.\n*/\nSQLITE_API int sqlite3_db_config(sqlite3*, int op, ...);\n\n/*\n** CAPI3REF: Memory Allocation Routines\n**\n** An instance of this object defines the interface between SQLite\n** and low-level memory allocation routines.\n**\n** This object is used in only one place in the SQLite interface.\n** A pointer to an instance of this object is the argument to\n** [sqlite3_config()] when the configuration option is\n** [SQLITE_CONFIG_MALLOC] or [SQLITE_CONFIG_GETMALLOC].  \n** By creating an instance of this object\n** and passing it to [sqlite3_config]([SQLITE_CONFIG_MALLOC])\n** during configuration, an application can specify an alternative\n** memory allocation subsystem for SQLite to use for all of its\n** dynamic memory needs.\n**\n** Note that SQLite comes with several [built-in memory allocators]\n** that are perfectly adequate for the overwhelming majority of applications\n** and that this object is only useful to a tiny minority of applications\n** with specialized memory allocation requirements.  This object is\n** also used during testing of SQLite in order to specify an alternative\n** memory allocator that simulates memory out-of-memory conditions in\n** order to verify that SQLite recovers gracefully from such\n** conditions.\n**\n** The xMalloc, xRealloc, and xFree methods must work like the\n** malloc(), realloc() and free() functions from the standard C library.\n** ^SQLite guarantees that the second argument to\n** xRealloc is always a value returned by a prior call to xRoundup.\n**\n** xSize should return the allocated size of a memory allocation\n** previously obtained from xMalloc or xRealloc.  The allocated size\n** is always at least as big as the requested size but may be larger.\n**\n** The xRoundup method returns what would be the allocated size of\n** a memory allocation given a particular requested size.  Most memory\n** allocators round up memory allocations at least to the next multiple\n** of 8.  Some allocators round up to a larger multiple or to a power of 2.\n** Every memory allocation request coming in through [sqlite3_malloc()]\n** or [sqlite3_realloc()] first calls xRoundup.  If xRoundup returns 0, \n** that causes the corresponding memory allocation to fail.\n**\n** The xInit method initializes the memory allocator.  (For example,\n** it might allocate any require mutexes or initialize internal data\n** structures.  The xShutdown method is invoked (indirectly) by\n** [sqlite3_shutdown()] and should deallocate any resources acquired\n** by xInit.  The pAppData pointer is used as the only parameter to\n** xInit and xShutdown.\n**\n** SQLite holds the [SQLITE_MUTEX_STATIC_MASTER] mutex when it invokes\n** the xInit method, so the xInit method need not be threadsafe.  The\n** xShutdown method is only called from [sqlite3_shutdown()] so it does\n** not need to be threadsafe either.  For all other methods, SQLite\n** holds the [SQLITE_MUTEX_STATIC_MEM] mutex as long as the\n** [SQLITE_CONFIG_MEMSTATUS] configuration option is turned on (which\n** it is by default) and so the methods are automatically serialized.\n** However, if [SQLITE_CONFIG_MEMSTATUS] is disabled, then the other\n** methods must be threadsafe or else make their own arrangements for\n** serialization.\n**\n** SQLite will never invoke xInit() more than once without an intervening\n** call to xShutdown().\n*/\ntypedef struct sqlite3_mem_methods sqlite3_mem_methods;\nstruct sqlite3_mem_methods {\n  void *(*xMalloc)(int);         /* Memory allocation function */\n  void (*xFree)(void*);          /* Free a prior allocation */\n  void *(*xRealloc)(void*,int);  /* Resize an allocation */\n  int (*xSize)(void*);           /* Return the size of an allocation */\n  int (*xRoundup)(int);          /* Round up request size to allocation size */\n  int (*xInit)(void*);           /* Initialize the memory allocator */\n  void (*xShutdown)(void*);      /* Deinitialize the memory allocator */\n  void *pAppData;                /* Argument to xInit() and xShutdown() */\n};\n\n/*\n** CAPI3REF: Configuration Options\n** KEYWORDS: {configuration option}\n**\n** These constants are the available integer configuration options that\n** can be passed as the first argument to the [sqlite3_config()] interface.\n**\n** New configuration options may be added in future releases of SQLite.\n** Existing configuration options might be discontinued.  Applications\n** should check the return code from [sqlite3_config()] to make sure that\n** the call worked.  The [sqlite3_config()] interface will return a\n** non-zero [error code] if a discontinued or unsupported configuration option\n** is invoked.\n**\n** <dl>\n** [[SQLITE_CONFIG_SINGLETHREAD]] <dt>SQLITE_CONFIG_SINGLETHREAD</dt>\n** <dd>There are no arguments to this option.  ^This option sets the\n** [threading mode] to Single-thread.  In other words, it disables\n** all mutexing and puts SQLite into a mode where it can only be used\n** by a single thread.   ^If SQLite is compiled with\n** the [SQLITE_THREADSAFE | SQLITE_THREADSAFE=0] compile-time option then\n** it is not possible to change the [threading mode] from its default\n** value of Single-thread and so [sqlite3_config()] will return \n** [SQLITE_ERROR] if called with the SQLITE_CONFIG_SINGLETHREAD\n** configuration option.</dd>\n**\n** [[SQLITE_CONFIG_MULTITHREAD]] <dt>SQLITE_CONFIG_MULTITHREAD</dt>\n** <dd>There are no arguments to this option.  ^This option sets the\n** [threading mode] to Multi-thread.  In other words, it disables\n** mutexing on [database connection] and [prepared statement] objects.\n** The application is responsible for serializing access to\n** [database connections] and [prepared statements].  But other mutexes\n** are enabled so that SQLite will be safe to use in a multi-threaded\n** environment as long as no two threads attempt to use the same\n** [database connection] at the same time.  ^If SQLite is compiled with\n** the [SQLITE_THREADSAFE | SQLITE_THREADSAFE=0] compile-time option then\n** it is not possible to set the Multi-thread [threading mode] and\n** [sqlite3_config()] will return [SQLITE_ERROR] if called with the\n** SQLITE_CONFIG_MULTITHREAD configuration option.</dd>\n**\n** [[SQLITE_CONFIG_SERIALIZED]] <dt>SQLITE_CONFIG_SERIALIZED</dt>\n** <dd>There are no arguments to this option.  ^This option sets the\n** [threading mode] to Serialized. In other words, this option enables\n** all mutexes including the recursive\n** mutexes on [database connection] and [prepared statement] objects.\n** In this mode (which is the default when SQLite is compiled with\n** [SQLITE_THREADSAFE=1]) the SQLite library will itself serialize access\n** to [database connections] and [prepared statements] so that the\n** application is free to use the same [database connection] or the\n** same [prepared statement] in different threads at the same time.\n** ^If SQLite is compiled with\n** the [SQLITE_THREADSAFE | SQLITE_THREADSAFE=0] compile-time option then\n** it is not possible to set the Serialized [threading mode] and\n** [sqlite3_config()] will return [SQLITE_ERROR] if called with the\n** SQLITE_CONFIG_SERIALIZED configuration option.</dd>\n**\n** [[SQLITE_CONFIG_MALLOC]] <dt>SQLITE_CONFIG_MALLOC</dt>\n** <dd> ^(This option takes a single argument which is a pointer to an\n** instance of the [sqlite3_mem_methods] structure.  The argument specifies\n** alternative low-level memory allocation routines to be used in place of\n** the memory allocation routines built into SQLite.)^ ^SQLite makes\n** its own private copy of the content of the [sqlite3_mem_methods] structure\n** before the [sqlite3_config()] call returns.</dd>\n**\n** [[SQLITE_CONFIG_GETMALLOC]] <dt>SQLITE_CONFIG_GETMALLOC</dt>\n** <dd> ^(This option takes a single argument which is a pointer to an\n** instance of the [sqlite3_mem_methods] structure.  The [sqlite3_mem_methods]\n** structure is filled with the currently defined memory allocation routines.)^\n** This option can be used to overload the default memory allocation\n** routines with a wrapper that simulations memory allocation failure or\n** tracks memory usage, for example. </dd>\n**\n** [[SQLITE_CONFIG_MEMSTATUS]] <dt>SQLITE_CONFIG_MEMSTATUS</dt>\n** <dd> ^This option takes single argument of type int, interpreted as a \n** boolean, which enables or disables the collection of memory allocation \n** statistics. ^(When memory allocation statistics are disabled, the \n** following SQLite interfaces become non-operational:\n**   <ul>\n**   <li> [sqlite3_memory_used()]\n**   <li> [sqlite3_memory_highwater()]\n**   <li> [sqlite3_soft_heap_limit64()]\n**   <li> [sqlite3_status()]\n**   </ul>)^\n** ^Memory allocation statistics are enabled by default unless SQLite is\n** compiled with [SQLITE_DEFAULT_MEMSTATUS]=0 in which case memory\n** allocation statistics are disabled by default.\n** </dd>\n**\n** [[SQLITE_CONFIG_SCRATCH]] <dt>SQLITE_CONFIG_SCRATCH</dt>\n** <dd> ^This option specifies a static memory buffer that SQLite can use for\n** scratch memory.  There are three arguments:  A pointer an 8-byte\n** aligned memory buffer from which the scratch allocations will be\n** drawn, the size of each scratch allocation (sz),\n** and the maximum number of scratch allocations (N).  The sz\n** argument must be a multiple of 16.\n** The first argument must be a pointer to an 8-byte aligned buffer\n** of at least sz*N bytes of memory.\n** ^SQLite will use no more than two scratch buffers per thread.  So\n** N should be set to twice the expected maximum number of threads.\n** ^SQLite will never require a scratch buffer that is more than 6\n** times the database page size. ^If SQLite needs needs additional\n** scratch memory beyond what is provided by this configuration option, then \n** [sqlite3_malloc()] will be used to obtain the memory needed.</dd>\n**\n** [[SQLITE_CONFIG_PAGECACHE]] <dt>SQLITE_CONFIG_PAGECACHE</dt>\n** <dd> ^This option specifies a static memory buffer that SQLite can use for\n** the database page cache with the default page cache implementation.  \n** This configuration should not be used if an application-define page\n** cache implementation is loaded using the SQLITE_CONFIG_PCACHE2 option.\n** There are three arguments to this option: A pointer to 8-byte aligned\n** memory, the size of each page buffer (sz), and the number of pages (N).\n** The sz argument should be the size of the largest database page\n** (a power of two between 512 and 32768) plus a little extra for each\n** page header.  ^The page header size is 20 to 40 bytes depending on\n** the host architecture.  ^It is harmless, apart from the wasted memory,\n** to make sz a little too large.  The first\n** argument should point to an allocation of at least sz*N bytes of memory.\n** ^SQLite will use the memory provided by the first argument to satisfy its\n** memory needs for the first N pages that it adds to cache.  ^If additional\n** page cache memory is needed beyond what is provided by this option, then\n** SQLite goes to [sqlite3_malloc()] for the additional storage space.\n** The pointer in the first argument must\n** be aligned to an 8-byte boundary or subsequent behavior of SQLite\n** will be undefined.</dd>\n**\n** [[SQLITE_CONFIG_HEAP]] <dt>SQLITE_CONFIG_HEAP</dt>\n** <dd> ^This option specifies a static memory buffer that SQLite will use\n** for all of its dynamic memory allocation needs beyond those provided\n** for by [SQLITE_CONFIG_SCRATCH] and [SQLITE_CONFIG_PAGECACHE].\n** There are three arguments: An 8-byte aligned pointer to the memory,\n** the number of bytes in the memory buffer, and the minimum allocation size.\n** ^If the first pointer (the memory pointer) is NULL, then SQLite reverts\n** to using its default memory allocator (the system malloc() implementation),\n** undoing any prior invocation of [SQLITE_CONFIG_MALLOC].  ^If the\n** memory pointer is not NULL and either [SQLITE_ENABLE_MEMSYS3] or\n** [SQLITE_ENABLE_MEMSYS5] are defined, then the alternative memory\n** allocator is engaged to handle all of SQLites memory allocation needs.\n** The first pointer (the memory pointer) must be aligned to an 8-byte\n** boundary or subsequent behavior of SQLite will be undefined.\n** The minimum allocation size is capped at 2**12. Reasonable values\n** for the minimum allocation size are 2**5 through 2**8.</dd>\n**\n** [[SQLITE_CONFIG_MUTEX]] <dt>SQLITE_CONFIG_MUTEX</dt>\n** <dd> ^(This option takes a single argument which is a pointer to an\n** instance of the [sqlite3_mutex_methods] structure.  The argument specifies\n** alternative low-level mutex routines to be used in place\n** the mutex routines built into SQLite.)^  ^SQLite makes a copy of the\n** content of the [sqlite3_mutex_methods] structure before the call to\n** [sqlite3_config()] returns. ^If SQLite is compiled with\n** the [SQLITE_THREADSAFE | SQLITE_THREADSAFE=0] compile-time option then\n** the entire mutexing subsystem is omitted from the build and hence calls to\n** [sqlite3_config()] with the SQLITE_CONFIG_MUTEX configuration option will\n** return [SQLITE_ERROR].</dd>\n**\n** [[SQLITE_CONFIG_GETMUTEX]] <dt>SQLITE_CONFIG_GETMUTEX</dt>\n** <dd> ^(This option takes a single argument which is a pointer to an\n** instance of the [sqlite3_mutex_methods] structure.  The\n** [sqlite3_mutex_methods]\n** structure is filled with the currently defined mutex routines.)^\n** This option can be used to overload the default mutex allocation\n** routines with a wrapper used to track mutex usage for performance\n** profiling or testing, for example.   ^If SQLite is compiled with\n** the [SQLITE_THREADSAFE | SQLITE_THREADSAFE=0] compile-time option then\n** the entire mutexing subsystem is omitted from the build and hence calls to\n** [sqlite3_config()] with the SQLITE_CONFIG_GETMUTEX configuration option will\n** return [SQLITE_ERROR].</dd>\n**\n** [[SQLITE_CONFIG_LOOKASIDE]] <dt>SQLITE_CONFIG_LOOKASIDE</dt>\n** <dd> ^(This option takes two arguments that determine the default\n** memory allocation for the lookaside memory allocator on each\n** [database connection].  The first argument is the\n** size of each lookaside buffer slot and the second is the number of\n** slots allocated to each database connection.)^  ^(This option sets the\n** <i>default</i> lookaside size. The [SQLITE_DBCONFIG_LOOKASIDE]\n** verb to [sqlite3_db_config()] can be used to change the lookaside\n** configuration on individual connections.)^ </dd>\n**\n** [[SQLITE_CONFIG_PCACHE2]] <dt>SQLITE_CONFIG_PCACHE2</dt>\n** <dd> ^(This option takes a single argument which is a pointer to\n** an [sqlite3_pcache_methods2] object.  This object specifies the interface\n** to a custom page cache implementation.)^  ^SQLite makes a copy of the\n** object and uses it for page cache memory allocations.</dd>\n**\n** [[SQLITE_CONFIG_GETPCACHE2]] <dt>SQLITE_CONFIG_GETPCACHE2</dt>\n** <dd> ^(This option takes a single argument which is a pointer to an\n** [sqlite3_pcache_methods2] object.  SQLite copies of the current\n** page cache implementation into that object.)^ </dd>\n**\n** [[SQLITE_CONFIG_LOG]] <dt>SQLITE_CONFIG_LOG</dt>\n** <dd> ^The SQLITE_CONFIG_LOG option takes two arguments: a pointer to a\n** function with a call signature of void(*)(void*,int,const char*), \n** and a pointer to void. ^If the function pointer is not NULL, it is\n** invoked by [sqlite3_log()] to process each logging event.  ^If the\n** function pointer is NULL, the [sqlite3_log()] interface becomes a no-op.\n** ^The void pointer that is the second argument to SQLITE_CONFIG_LOG is\n** passed through as the first parameter to the application-defined logger\n** function whenever that function is invoked.  ^The second parameter to\n** the logger function is a copy of the first parameter to the corresponding\n** [sqlite3_log()] call and is intended to be a [result code] or an\n** [extended result code].  ^The third parameter passed to the logger is\n** log message after formatting via [sqlite3_snprintf()].\n** The SQLite logging interface is not reentrant; the logger function\n** supplied by the application must not invoke any SQLite interface.\n** In a multi-threaded application, the application-defined logger\n** function must be threadsafe. </dd>\n**\n** [[SQLITE_CONFIG_URI]] <dt>SQLITE_CONFIG_URI\n** <dd> This option takes a single argument of type int. If non-zero, then\n** URI handling is globally enabled. If the parameter is zero, then URI handling\n** is globally disabled. If URI handling is globally enabled, all filenames\n** passed to [sqlite3_open()], [sqlite3_open_v2()], [sqlite3_open16()] or\n** specified as part of [ATTACH] commands are interpreted as URIs, regardless\n** of whether or not the [SQLITE_OPEN_URI] flag is set when the database\n** connection is opened. If it is globally disabled, filenames are\n** only interpreted as URIs if the SQLITE_OPEN_URI flag is set when the\n** database connection is opened. By default, URI handling is globally\n** disabled. The default value may be changed by compiling with the\n** [SQLITE_USE_URI] symbol defined.\n**\n** [[SQLITE_CONFIG_PCACHE]] [[SQLITE_CONFIG_GETPCACHE]]\n** <dt>SQLITE_CONFIG_PCACHE and SQLITE_CONFIG_GETPCACHE\n** <dd> These options are obsolete and should not be used by new code.\n** They are retained for backwards compatibility but are now no-ops.\n** </dl>\n*/\n#define SQLITE_CONFIG_SINGLETHREAD  1  /* nil */\n#define SQLITE_CONFIG_MULTITHREAD   2  /* nil */\n#define SQLITE_CONFIG_SERIALIZED    3  /* nil */\n#define SQLITE_CONFIG_MALLOC        4  /* sqlite3_mem_methods* */\n#define SQLITE_CONFIG_GETMALLOC     5  /* sqlite3_mem_methods* */\n#define SQLITE_CONFIG_SCRATCH       6  /* void*, int sz, int N */\n#define SQLITE_CONFIG_PAGECACHE     7  /* void*, int sz, int N */\n#define SQLITE_CONFIG_HEAP          8  /* void*, int nByte, int min */\n#define SQLITE_CONFIG_MEMSTATUS     9  /* boolean */\n#define SQLITE_CONFIG_MUTEX        10  /* sqlite3_mutex_methods* */\n#define SQLITE_CONFIG_GETMUTEX     11  /* sqlite3_mutex_methods* */\n/* previously SQLITE_CONFIG_CHUNKALLOC 12 which is now unused. */ \n#define SQLITE_CONFIG_LOOKASIDE    13  /* int int */\n#define SQLITE_CONFIG_PCACHE       14  /* no-op */\n#define SQLITE_CONFIG_GETPCACHE    15  /* no-op */\n#define SQLITE_CONFIG_LOG          16  /* xFunc, void* */\n#define SQLITE_CONFIG_URI          17  /* int */\n#define SQLITE_CONFIG_PCACHE2      18  /* sqlite3_pcache_methods2* */\n#define SQLITE_CONFIG_GETPCACHE2   19  /* sqlite3_pcache_methods2* */\n\n/*\n** CAPI3REF: Database Connection Configuration Options\n**\n** These constants are the available integer configuration options that\n** can be passed as the second argument to the [sqlite3_db_config()] interface.\n**\n** New configuration options may be added in future releases of SQLite.\n** Existing configuration options might be discontinued.  Applications\n** should check the return code from [sqlite3_db_config()] to make sure that\n** the call worked.  ^The [sqlite3_db_config()] interface will return a\n** non-zero [error code] if a discontinued or unsupported configuration option\n** is invoked.\n**\n** <dl>\n** <dt>SQLITE_DBCONFIG_LOOKASIDE</dt>\n** <dd> ^This option takes three additional arguments that determine the \n** [lookaside memory allocator] configuration for the [database connection].\n** ^The first argument (the third parameter to [sqlite3_db_config()] is a\n** pointer to a memory buffer to use for lookaside memory.\n** ^The first argument after the SQLITE_DBCONFIG_LOOKASIDE verb\n** may be NULL in which case SQLite will allocate the\n** lookaside buffer itself using [sqlite3_malloc()]. ^The second argument is the\n** size of each lookaside buffer slot.  ^The third argument is the number of\n** slots.  The size of the buffer in the first argument must be greater than\n** or equal to the product of the second and third arguments.  The buffer\n** must be aligned to an 8-byte boundary.  ^If the second argument to\n** SQLITE_DBCONFIG_LOOKASIDE is not a multiple of 8, it is internally\n** rounded down to the next smaller multiple of 8.  ^(The lookaside memory\n** configuration for a database connection can only be changed when that\n** connection is not currently using lookaside memory, or in other words\n** when the \"current value\" returned by\n** [sqlite3_db_status](D,[SQLITE_CONFIG_LOOKASIDE],...) is zero.\n** Any attempt to change the lookaside memory configuration when lookaside\n** memory is in use leaves the configuration unchanged and returns \n** [SQLITE_BUSY].)^</dd>\n**\n** <dt>SQLITE_DBCONFIG_ENABLE_FKEY</dt>\n** <dd> ^This option is used to enable or disable the enforcement of\n** [foreign key constraints].  There should be two additional arguments.\n** The first argument is an integer which is 0 to disable FK enforcement,\n** positive to enable FK enforcement or negative to leave FK enforcement\n** unchanged.  The second parameter is a pointer to an integer into which\n** is written 0 or 1 to indicate whether FK enforcement is off or on\n** following this call.  The second parameter may be a NULL pointer, in\n** which case the FK enforcement setting is not reported back. </dd>\n**\n** <dt>SQLITE_DBCONFIG_ENABLE_TRIGGER</dt>\n** <dd> ^This option is used to enable or disable [CREATE TRIGGER | triggers].\n** There should be two additional arguments.\n** The first argument is an integer which is 0 to disable triggers,\n** positive to enable triggers or negative to leave the setting unchanged.\n** The second parameter is a pointer to an integer into which\n** is written 0 or 1 to indicate whether triggers are disabled or enabled\n** following this call.  The second parameter may be a NULL pointer, in\n** which case the trigger setting is not reported back. </dd>\n**\n** </dl>\n*/\n#define SQLITE_DBCONFIG_LOOKASIDE       1001  /* void* int int */\n#define SQLITE_DBCONFIG_ENABLE_FKEY     1002  /* int int* */\n#define SQLITE_DBCONFIG_ENABLE_TRIGGER  1003  /* int int* */\n\n\n/*\n** CAPI3REF: Enable Or Disable Extended Result Codes\n**\n** ^The sqlite3_extended_result_codes() routine enables or disables the\n** [extended result codes] feature of SQLite. ^The extended result\n** codes are disabled by default for historical compatibility.\n*/\nSQLITE_API int sqlite3_extended_result_codes(sqlite3*, int onoff);\n\n/*\n** CAPI3REF: Last Insert Rowid\n**\n** ^Each entry in an SQLite table has a unique 64-bit signed\n** integer key called the [ROWID | \"rowid\"]. ^The rowid is always available\n** as an undeclared column named ROWID, OID, or _ROWID_ as long as those\n** names are not also used by explicitly declared columns. ^If\n** the table has a column of type [INTEGER PRIMARY KEY] then that column\n** is another alias for the rowid.\n**\n** ^This routine returns the [rowid] of the most recent\n** successful [INSERT] into the database from the [database connection]\n** in the first argument.  ^As of SQLite version 3.7.7, this routines\n** records the last insert rowid of both ordinary tables and [virtual tables].\n** ^If no successful [INSERT]s\n** have ever occurred on that database connection, zero is returned.\n**\n** ^(If an [INSERT] occurs within a trigger or within a [virtual table]\n** method, then this routine will return the [rowid] of the inserted\n** row as long as the trigger or virtual table method is running.\n** But once the trigger or virtual table method ends, the value returned \n** by this routine reverts to what it was before the trigger or virtual\n** table method began.)^\n**\n** ^An [INSERT] that fails due to a constraint violation is not a\n** successful [INSERT] and does not change the value returned by this\n** routine.  ^Thus INSERT OR FAIL, INSERT OR IGNORE, INSERT OR ROLLBACK,\n** and INSERT OR ABORT make no changes to the return value of this\n** routine when their insertion fails.  ^(When INSERT OR REPLACE\n** encounters a constraint violation, it does not fail.  The\n** INSERT continues to completion after deleting rows that caused\n** the constraint problem so INSERT OR REPLACE will always change\n** the return value of this interface.)^\n**\n** ^For the purposes of this routine, an [INSERT] is considered to\n** be successful even if it is subsequently rolled back.\n**\n** This function is accessible to SQL statements via the\n** [last_insert_rowid() SQL function].\n**\n** If a separate thread performs a new [INSERT] on the same\n** database connection while the [sqlite3_last_insert_rowid()]\n** function is running and thus changes the last insert [rowid],\n** then the value returned by [sqlite3_last_insert_rowid()] is\n** unpredictable and might not equal either the old or the new\n** last insert [rowid].\n*/\nSQLITE_API sqlite3_int64 sqlite3_last_insert_rowid(sqlite3*);\n\n/*\n** CAPI3REF: Count The Number Of Rows Modified\n**\n** ^This function returns the number of database rows that were changed\n** or inserted or deleted by the most recently completed SQL statement\n** on the [database connection] specified by the first parameter.\n** ^(Only changes that are directly specified by the [INSERT], [UPDATE],\n** or [DELETE] statement are counted.  Auxiliary changes caused by\n** triggers or [foreign key actions] are not counted.)^ Use the\n** [sqlite3_total_changes()] function to find the total number of changes\n** including changes caused by triggers and foreign key actions.\n**\n** ^Changes to a view that are simulated by an [INSTEAD OF trigger]\n** are not counted.  Only real table changes are counted.\n**\n** ^(A \"row change\" is a change to a single row of a single table\n** caused by an INSERT, DELETE, or UPDATE statement.  Rows that\n** are changed as side effects of [REPLACE] constraint resolution,\n** rollback, ABORT processing, [DROP TABLE], or by any other\n** mechanisms do not count as direct row changes.)^\n**\n** A \"trigger context\" is a scope of execution that begins and\n** ends with the script of a [CREATE TRIGGER | trigger]. \n** Most SQL statements are\n** evaluated outside of any trigger.  This is the \"top level\"\n** trigger context.  If a trigger fires from the top level, a\n** new trigger context is entered for the duration of that one\n** trigger.  Subtriggers create subcontexts for their duration.\n**\n** ^Calling [sqlite3_exec()] or [sqlite3_step()] recursively does\n** not create a new trigger context.\n**\n** ^This function returns the number of direct row changes in the\n** most recent INSERT, UPDATE, or DELETE statement within the same\n** trigger context.\n**\n** ^Thus, when called from the top level, this function returns the\n** number of changes in the most recent INSERT, UPDATE, or DELETE\n** that also occurred at the top level.  ^(Within the body of a trigger,\n** the sqlite3_changes() interface can be called to find the number of\n** changes in the most recently completed INSERT, UPDATE, or DELETE\n** statement within the body of the same trigger.\n** However, the number returned does not include changes\n** caused by subtriggers since those have their own context.)^\n**\n** See also the [sqlite3_total_changes()] interface, the\n** [count_changes pragma], and the [changes() SQL function].\n**\n** If a separate thread makes changes on the same database connection\n** while [sqlite3_changes()] is running then the value returned\n** is unpredictable and not meaningful.\n*/\nSQLITE_API int sqlite3_changes(sqlite3*);\n\n/*\n** CAPI3REF: Total Number Of Rows Modified\n**\n** ^This function returns the number of row changes caused by [INSERT],\n** [UPDATE] or [DELETE] statements since the [database connection] was opened.\n** ^(The count returned by sqlite3_total_changes() includes all changes\n** from all [CREATE TRIGGER | trigger] contexts and changes made by\n** [foreign key actions]. However,\n** the count does not include changes used to implement [REPLACE] constraints,\n** do rollbacks or ABORT processing, or [DROP TABLE] processing.  The\n** count does not include rows of views that fire an [INSTEAD OF trigger],\n** though if the INSTEAD OF trigger makes changes of its own, those changes \n** are counted.)^\n** ^The sqlite3_total_changes() function counts the changes as soon as\n** the statement that makes them is completed (when the statement handle\n** is passed to [sqlite3_reset()] or [sqlite3_finalize()]).\n**\n** See also the [sqlite3_changes()] interface, the\n** [count_changes pragma], and the [total_changes() SQL function].\n**\n** If a separate thread makes changes on the same database connection\n** while [sqlite3_total_changes()] is running then the value\n** returned is unpredictable and not meaningful.\n*/\nSQLITE_API int sqlite3_total_changes(sqlite3*);\n\n/*\n** CAPI3REF: Interrupt A Long-Running Query\n**\n** ^This function causes any pending database operation to abort and\n** return at its earliest opportunity. This routine is typically\n** called in response to a user action such as pressing \"Cancel\"\n** or Ctrl-C where the user wants a long query operation to halt\n** immediately.\n**\n** ^It is safe to call this routine from a thread different from the\n** thread that is currently running the database operation.  But it\n** is not safe to call this routine with a [database connection] that\n** is closed or might close before sqlite3_interrupt() returns.\n**\n** ^If an SQL operation is very nearly finished at the time when\n** sqlite3_interrupt() is called, then it might not have an opportunity\n** to be interrupted and might continue to completion.\n**\n** ^An SQL operation that is interrupted will return [SQLITE_INTERRUPT].\n** ^If the interrupted SQL operation is an INSERT, UPDATE, or DELETE\n** that is inside an explicit transaction, then the entire transaction\n** will be rolled back automatically.\n**\n** ^The sqlite3_interrupt(D) call is in effect until all currently running\n** SQL statements on [database connection] D complete.  ^Any new SQL statements\n** that are started after the sqlite3_interrupt() call and before the \n** running statements reaches zero are interrupted as if they had been\n** running prior to the sqlite3_interrupt() call.  ^New SQL statements\n** that are started after the running statement count reaches zero are\n** not effected by the sqlite3_interrupt().\n** ^A call to sqlite3_interrupt(D) that occurs when there are no running\n** SQL statements is a no-op and has no effect on SQL statements\n** that are started after the sqlite3_interrupt() call returns.\n**\n** If the database connection closes while [sqlite3_interrupt()]\n** is running then bad things will likely happen.\n*/\nSQLITE_API void sqlite3_interrupt(sqlite3*);\n\n/*\n** CAPI3REF: Determine If An SQL Statement Is Complete\n**\n** These routines are useful during command-line input to determine if the\n** currently entered text seems to form a complete SQL statement or\n** if additional input is needed before sending the text into\n** SQLite for parsing.  ^These routines return 1 if the input string\n** appears to be a complete SQL statement.  ^A statement is judged to be\n** complete if it ends with a semicolon token and is not a prefix of a\n** well-formed CREATE TRIGGER statement.  ^Semicolons that are embedded within\n** string literals or quoted identifier names or comments are not\n** independent tokens (they are part of the token in which they are\n** embedded) and thus do not count as a statement terminator.  ^Whitespace\n** and comments that follow the final semicolon are ignored.\n**\n** ^These routines return 0 if the statement is incomplete.  ^If a\n** memory allocation fails, then SQLITE_NOMEM is returned.\n**\n** ^These routines do not parse the SQL statements thus\n** will not detect syntactically incorrect SQL.\n**\n** ^(If SQLite has not been initialized using [sqlite3_initialize()] prior \n** to invoking sqlite3_complete16() then sqlite3_initialize() is invoked\n** automatically by sqlite3_complete16().  If that initialization fails,\n** then the return value from sqlite3_complete16() will be non-zero\n** regardless of whether or not the input SQL is complete.)^\n**\n** The input to [sqlite3_complete()] must be a zero-terminated\n** UTF-8 string.\n**\n** The input to [sqlite3_complete16()] must be a zero-terminated\n** UTF-16 string in native byte order.\n*/\nSQLITE_API int sqlite3_complete(const char *sql);\nSQLITE_API int sqlite3_complete16(const void *sql);\n\n/*\n** CAPI3REF: Register A Callback To Handle SQLITE_BUSY Errors\n**\n** ^This routine sets a callback function that might be invoked whenever\n** an attempt is made to open a database table that another thread\n** or process has locked.\n**\n** ^If the busy callback is NULL, then [SQLITE_BUSY] or [SQLITE_IOERR_BLOCKED]\n** is returned immediately upon encountering the lock.  ^If the busy callback\n** is not NULL, then the callback might be invoked with two arguments.\n**\n** ^The first argument to the busy handler is a copy of the void* pointer which\n** is the third argument to sqlite3_busy_handler().  ^The second argument to\n** the busy handler callback is the number of times that the busy handler has\n** been invoked for this locking event.  ^If the\n** busy callback returns 0, then no additional attempts are made to\n** access the database and [SQLITE_BUSY] or [SQLITE_IOERR_BLOCKED] is returned.\n** ^If the callback returns non-zero, then another attempt\n** is made to open the database for reading and the cycle repeats.\n**\n** The presence of a busy handler does not guarantee that it will be invoked\n** when there is lock contention. ^If SQLite determines that invoking the busy\n** handler could result in a deadlock, it will go ahead and return [SQLITE_BUSY]\n** or [SQLITE_IOERR_BLOCKED] instead of invoking the busy handler.\n** Consider a scenario where one process is holding a read lock that\n** it is trying to promote to a reserved lock and\n** a second process is holding a reserved lock that it is trying\n** to promote to an exclusive lock.  The first process cannot proceed\n** because it is blocked by the second and the second process cannot\n** proceed because it is blocked by the first.  If both processes\n** invoke the busy handlers, neither will make any progress.  Therefore,\n** SQLite returns [SQLITE_BUSY] for the first process, hoping that this\n** will induce the first process to release its read lock and allow\n** the second process to proceed.\n**\n** ^The default busy callback is NULL.\n**\n** ^The [SQLITE_BUSY] error is converted to [SQLITE_IOERR_BLOCKED]\n** when SQLite is in the middle of a large transaction where all the\n** changes will not fit into the in-memory cache.  SQLite will\n** already hold a RESERVED lock on the database file, but it needs\n** to promote this lock to EXCLUSIVE so that it can spill cache\n** pages into the database file without harm to concurrent\n** readers.  ^If it is unable to promote the lock, then the in-memory\n** cache will be left in an inconsistent state and so the error\n** code is promoted from the relatively benign [SQLITE_BUSY] to\n** the more severe [SQLITE_IOERR_BLOCKED].  ^This error code promotion\n** forces an automatic rollback of the changes.  See the\n** <a href=\"/cvstrac/wiki?p=CorruptionFollowingBusyError\">\n** CorruptionFollowingBusyError</a> wiki page for a discussion of why\n** this is important.\n**\n** ^(There can only be a single busy handler defined for each\n** [database connection].  Setting a new busy handler clears any\n** previously set handler.)^  ^Note that calling [sqlite3_busy_timeout()]\n** will also set or clear the busy handler.\n**\n** The busy callback should not take any actions which modify the\n** database connection that invoked the busy handler.  Any such actions\n** result in undefined behavior.\n** \n** A busy handler must not close the database connection\n** or [prepared statement] that invoked the busy handler.\n*/\nSQLITE_API int sqlite3_busy_handler(sqlite3*, int(*)(void*,int), void*);\n\n/*\n** CAPI3REF: Set A Busy Timeout\n**\n** ^This routine sets a [sqlite3_busy_handler | busy handler] that sleeps\n** for a specified amount of time when a table is locked.  ^The handler\n** will sleep multiple times until at least \"ms\" milliseconds of sleeping\n** have accumulated.  ^After at least \"ms\" milliseconds of sleeping,\n** the handler returns 0 which causes [sqlite3_step()] to return\n** [SQLITE_BUSY] or [SQLITE_IOERR_BLOCKED].\n**\n** ^Calling this routine with an argument less than or equal to zero\n** turns off all busy handlers.\n**\n** ^(There can only be a single busy handler for a particular\n** [database connection] any any given moment.  If another busy handler\n** was defined  (using [sqlite3_busy_handler()]) prior to calling\n** this routine, that other busy handler is cleared.)^\n*/\nSQLITE_API int sqlite3_busy_timeout(sqlite3*, int ms);\n\n/*\n** CAPI3REF: Convenience Routines For Running Queries\n**\n** This is a legacy interface that is preserved for backwards compatibility.\n** Use of this interface is not recommended.\n**\n** Definition: A <b>result table</b> is memory data structure created by the\n** [sqlite3_get_table()] interface.  A result table records the\n** complete query results from one or more queries.\n**\n** The table conceptually has a number of rows and columns.  But\n** these numbers are not part of the result table itself.  These\n** numbers are obtained separately.  Let N be the number of rows\n** and M be the number of columns.\n**\n** A result table is an array of pointers to zero-terminated UTF-8 strings.\n** There are (N+1)*M elements in the array.  The first M pointers point\n** to zero-terminated strings that  contain the names of the columns.\n** The remaining entries all point to query results.  NULL values result\n** in NULL pointers.  All other values are in their UTF-8 zero-terminated\n** string representation as returned by [sqlite3_column_text()].\n**\n** A result table might consist of one or more memory allocations.\n** It is not safe to pass a result table directly to [sqlite3_free()].\n** A result table should be deallocated using [sqlite3_free_table()].\n**\n** ^(As an example of the result table format, suppose a query result\n** is as follows:\n**\n** <blockquote><pre>\n**        Name        | Age\n**        -----------------------\n**        Alice       | 43\n**        Bob         | 28\n**        Cindy       | 21\n** </pre></blockquote>\n**\n** There are two column (M==2) and three rows (N==3).  Thus the\n** result table has 8 entries.  Suppose the result table is stored\n** in an array names azResult.  Then azResult holds this content:\n**\n** <blockquote><pre>\n**        azResult&#91;0] = \"Name\";\n**        azResult&#91;1] = \"Age\";\n**        azResult&#91;2] = \"Alice\";\n**        azResult&#91;3] = \"43\";\n**        azResult&#91;4] = \"Bob\";\n**        azResult&#91;5] = \"28\";\n**        azResult&#91;6] = \"Cindy\";\n**        azResult&#91;7] = \"21\";\n** </pre></blockquote>)^\n**\n** ^The sqlite3_get_table() function evaluates one or more\n** semicolon-separated SQL statements in the zero-terminated UTF-8\n** string of its 2nd parameter and returns a result table to the\n** pointer given in its 3rd parameter.\n**\n** After the application has finished with the result from sqlite3_get_table(),\n** it must pass the result table pointer to sqlite3_free_table() in order to\n** release the memory that was malloced.  Because of the way the\n** [sqlite3_malloc()] happens within sqlite3_get_table(), the calling\n** function must not try to call [sqlite3_free()] directly.  Only\n** [sqlite3_free_table()] is able to release the memory properly and safely.\n**\n** The sqlite3_get_table() interface is implemented as a wrapper around\n** [sqlite3_exec()].  The sqlite3_get_table() routine does not have access\n** to any internal data structures of SQLite.  It uses only the public\n** interface defined here.  As a consequence, errors that occur in the\n** wrapper layer outside of the internal [sqlite3_exec()] call are not\n** reflected in subsequent calls to [sqlite3_errcode()] or\n** [sqlite3_errmsg()].\n*/\nSQLITE_API int sqlite3_get_table(\n  sqlite3 *db,          /* An open database */\n  const char *zSql,     /* SQL to be evaluated */\n  char ***pazResult,    /* Results of the query */\n  int *pnRow,           /* Number of result rows written here */\n  int *pnColumn,        /* Number of result columns written here */\n  char **pzErrmsg       /* Error msg written here */\n);\nSQLITE_API void sqlite3_free_table(char **result);\n\n/*\n** CAPI3REF: Formatted String Printing Functions\n**\n** These routines are work-alikes of the \"printf()\" family of functions\n** from the standard C library.\n**\n** ^The sqlite3_mprintf() and sqlite3_vmprintf() routines write their\n** results into memory obtained from [sqlite3_malloc()].\n** The strings returned by these two routines should be\n** released by [sqlite3_free()].  ^Both routines return a\n** NULL pointer if [sqlite3_malloc()] is unable to allocate enough\n** memory to hold the resulting string.\n**\n** ^(The sqlite3_snprintf() routine is similar to \"snprintf()\" from\n** the standard C library.  The result is written into the\n** buffer supplied as the second parameter whose size is given by\n** the first parameter. Note that the order of the\n** first two parameters is reversed from snprintf().)^  This is an\n** historical accident that cannot be fixed without breaking\n** backwards compatibility.  ^(Note also that sqlite3_snprintf()\n** returns a pointer to its buffer instead of the number of\n** characters actually written into the buffer.)^  We admit that\n** the number of characters written would be a more useful return\n** value but we cannot change the implementation of sqlite3_snprintf()\n** now without breaking compatibility.\n**\n** ^As long as the buffer size is greater than zero, sqlite3_snprintf()\n** guarantees that the buffer is always zero-terminated.  ^The first\n** parameter \"n\" is the total size of the buffer, including space for\n** the zero terminator.  So the longest string that can be completely\n** written will be n-1 characters.\n**\n** ^The sqlite3_vsnprintf() routine is a varargs version of sqlite3_snprintf().\n**\n** These routines all implement some additional formatting\n** options that are useful for constructing SQL statements.\n** All of the usual printf() formatting options apply.  In addition, there\n** is are \"%q\", \"%Q\", and \"%z\" options.\n**\n** ^(The %q option works like %s in that it substitutes a nul-terminated\n** string from the argument list.  But %q also doubles every '\\'' character.\n** %q is designed for use inside a string literal.)^  By doubling each '\\''\n** character it escapes that character and allows it to be inserted into\n** the string.\n**\n** For example, assume the string variable zText contains text as follows:\n**\n** <blockquote><pre>\n**  char *zText = \"It's a happy day!\";\n** </pre></blockquote>\n**\n** One can use this text in an SQL statement as follows:\n**\n** <blockquote><pre>\n**  char *zSQL = sqlite3_mprintf(\"INSERT INTO table VALUES('%q')\", zText);\n**  sqlite3_exec(db, zSQL, 0, 0, 0);\n**  sqlite3_free(zSQL);\n** </pre></blockquote>\n**\n** Because the %q format string is used, the '\\'' character in zText\n** is escaped and the SQL generated is as follows:\n**\n** <blockquote><pre>\n**  INSERT INTO table1 VALUES('It''s a happy day!')\n** </pre></blockquote>\n**\n** This is correct.  Had we used %s instead of %q, the generated SQL\n** would have looked like this:\n**\n** <blockquote><pre>\n**  INSERT INTO table1 VALUES('It's a happy day!');\n** </pre></blockquote>\n**\n** This second example is an SQL syntax error.  As a general rule you should\n** always use %q instead of %s when inserting text into a string literal.\n**\n** ^(The %Q option works like %q except it also adds single quotes around\n** the outside of the total string.  Additionally, if the parameter in the\n** argument list is a NULL pointer, %Q substitutes the text \"NULL\" (without\n** single quotes).)^  So, for example, one could say:\n**\n** <blockquote><pre>\n**  char *zSQL = sqlite3_mprintf(\"INSERT INTO table VALUES(%Q)\", zText);\n**  sqlite3_exec(db, zSQL, 0, 0, 0);\n**  sqlite3_free(zSQL);\n** </pre></blockquote>\n**\n** The code above will render a correct SQL statement in the zSQL\n** variable even if the zText variable is a NULL pointer.\n**\n** ^(The \"%z\" formatting option works like \"%s\" but with the\n** addition that after the string has been read and copied into\n** the result, [sqlite3_free()] is called on the input string.)^\n*/\nSQLITE_API char *sqlite3_mprintf(const char*,...);\nSQLITE_API char *sqlite3_vmprintf(const char*, va_list);\nSQLITE_API char *sqlite3_snprintf(int,char*,const char*, ...);\nSQLITE_API char *sqlite3_vsnprintf(int,char*,const char*, va_list);\n\n/*\n** CAPI3REF: Memory Allocation Subsystem\n**\n** The SQLite core uses these three routines for all of its own\n** internal memory allocation needs. \"Core\" in the previous sentence\n** does not include operating-system specific VFS implementation.  The\n** Windows VFS uses native malloc() and free() for some operations.\n**\n** ^The sqlite3_malloc() routine returns a pointer to a block\n** of memory at least N bytes in length, where N is the parameter.\n** ^If sqlite3_malloc() is unable to obtain sufficient free\n** memory, it returns a NULL pointer.  ^If the parameter N to\n** sqlite3_malloc() is zero or negative then sqlite3_malloc() returns\n** a NULL pointer.\n**\n** ^Calling sqlite3_free() with a pointer previously returned\n** by sqlite3_malloc() or sqlite3_realloc() releases that memory so\n** that it might be reused.  ^The sqlite3_free() routine is\n** a no-op if is called with a NULL pointer.  Passing a NULL pointer\n** to sqlite3_free() is harmless.  After being freed, memory\n** should neither be read nor written.  Even reading previously freed\n** memory might result in a segmentation fault or other severe error.\n** Memory corruption, a segmentation fault, or other severe error\n** might result if sqlite3_free() is called with a non-NULL pointer that\n** was not obtained from sqlite3_malloc() or sqlite3_realloc().\n**\n** ^(The sqlite3_realloc() interface attempts to resize a\n** prior memory allocation to be at least N bytes, where N is the\n** second parameter.  The memory allocation to be resized is the first\n** parameter.)^ ^ If the first parameter to sqlite3_realloc()\n** is a NULL pointer then its behavior is identical to calling\n** sqlite3_malloc(N) where N is the second parameter to sqlite3_realloc().\n** ^If the second parameter to sqlite3_realloc() is zero or\n** negative then the behavior is exactly the same as calling\n** sqlite3_free(P) where P is the first parameter to sqlite3_realloc().\n** ^sqlite3_realloc() returns a pointer to a memory allocation\n** of at least N bytes in size or NULL if sufficient memory is unavailable.\n** ^If M is the size of the prior allocation, then min(N,M) bytes\n** of the prior allocation are copied into the beginning of buffer returned\n** by sqlite3_realloc() and the prior allocation is freed.\n** ^If sqlite3_realloc() returns NULL, then the prior allocation\n** is not freed.\n**\n** ^The memory returned by sqlite3_malloc() and sqlite3_realloc()\n** is always aligned to at least an 8 byte boundary, or to a\n** 4 byte boundary if the [SQLITE_4_BYTE_ALIGNED_MALLOC] compile-time\n** option is used.\n**\n** In SQLite version 3.5.0 and 3.5.1, it was possible to define\n** the SQLITE_OMIT_MEMORY_ALLOCATION which would cause the built-in\n** implementation of these routines to be omitted.  That capability\n** is no longer provided.  Only built-in memory allocators can be used.\n**\n** Prior to SQLite version 3.7.10, the Windows OS interface layer called\n** the system malloc() and free() directly when converting\n** filenames between the UTF-8 encoding used by SQLite\n** and whatever filename encoding is used by the particular Windows\n** installation.  Memory allocation errors were detected, but\n** they were reported back as [SQLITE_CANTOPEN] or\n** [SQLITE_IOERR] rather than [SQLITE_NOMEM].\n**\n** The pointer arguments to [sqlite3_free()] and [sqlite3_realloc()]\n** must be either NULL or else pointers obtained from a prior\n** invocation of [sqlite3_malloc()] or [sqlite3_realloc()] that have\n** not yet been released.\n**\n** The application must not read or write any part of\n** a block of memory after it has been released using\n** [sqlite3_free()] or [sqlite3_realloc()].\n*/\nSQLITE_API void *sqlite3_malloc(int);\nSQLITE_API void *sqlite3_realloc(void*, int);\nSQLITE_API void sqlite3_free(void*);\n\n/*\n** CAPI3REF: Memory Allocator Statistics\n**\n** SQLite provides these two interfaces for reporting on the status\n** of the [sqlite3_malloc()], [sqlite3_free()], and [sqlite3_realloc()]\n** routines, which form the built-in memory allocation subsystem.\n**\n** ^The [sqlite3_memory_used()] routine returns the number of bytes\n** of memory currently outstanding (malloced but not freed).\n** ^The [sqlite3_memory_highwater()] routine returns the maximum\n** value of [sqlite3_memory_used()] since the high-water mark\n** was last reset.  ^The values returned by [sqlite3_memory_used()] and\n** [sqlite3_memory_highwater()] include any overhead\n** added by SQLite in its implementation of [sqlite3_malloc()],\n** but not overhead added by the any underlying system library\n** routines that [sqlite3_malloc()] may call.\n**\n** ^The memory high-water mark is reset to the current value of\n** [sqlite3_memory_used()] if and only if the parameter to\n** [sqlite3_memory_highwater()] is true.  ^The value returned\n** by [sqlite3_memory_highwater(1)] is the high-water mark\n** prior to the reset.\n*/\nSQLITE_API sqlite3_int64 sqlite3_memory_used(void);\nSQLITE_API sqlite3_int64 sqlite3_memory_highwater(int resetFlag);\n\n/*\n** CAPI3REF: Pseudo-Random Number Generator\n**\n** SQLite contains a high-quality pseudo-random number generator (PRNG) used to\n** select random [ROWID | ROWIDs] when inserting new records into a table that\n** already uses the largest possible [ROWID].  The PRNG is also used for\n** the build-in random() and randomblob() SQL functions.  This interface allows\n** applications to access the same PRNG for other purposes.\n**\n** ^A call to this routine stores N bytes of randomness into buffer P.\n**\n** ^The first time this routine is invoked (either internally or by\n** the application) the PRNG is seeded using randomness obtained\n** from the xRandomness method of the default [sqlite3_vfs] object.\n** ^On all subsequent invocations, the pseudo-randomness is generated\n** internally and without recourse to the [sqlite3_vfs] xRandomness\n** method.\n*/\nSQLITE_API void sqlite3_randomness(int N, void *P);\n\n/*\n** CAPI3REF: Compile-Time Authorization Callbacks\n**\n** ^This routine registers an authorizer callback with a particular\n** [database connection], supplied in the first argument.\n** ^The authorizer callback is invoked as SQL statements are being compiled\n** by [sqlite3_prepare()] or its variants [sqlite3_prepare_v2()],\n** [sqlite3_prepare16()] and [sqlite3_prepare16_v2()].  ^At various\n** points during the compilation process, as logic is being created\n** to perform various actions, the authorizer callback is invoked to\n** see if those actions are allowed.  ^The authorizer callback should\n** return [SQLITE_OK] to allow the action, [SQLITE_IGNORE] to disallow the\n** specific action but allow the SQL statement to continue to be\n** compiled, or [SQLITE_DENY] to cause the entire SQL statement to be\n** rejected with an error.  ^If the authorizer callback returns\n** any value other than [SQLITE_IGNORE], [SQLITE_OK], or [SQLITE_DENY]\n** then the [sqlite3_prepare_v2()] or equivalent call that triggered\n** the authorizer will fail with an error message.\n**\n** When the callback returns [SQLITE_OK], that means the operation\n** requested is ok.  ^When the callback returns [SQLITE_DENY], the\n** [sqlite3_prepare_v2()] or equivalent call that triggered the\n** authorizer will fail with an error message explaining that\n** access is denied. \n**\n** ^The first parameter to the authorizer callback is a copy of the third\n** parameter to the sqlite3_set_authorizer() interface. ^The second parameter\n** to the callback is an integer [SQLITE_COPY | action code] that specifies\n** the particular action to be authorized. ^The third through sixth parameters\n** to the callback are zero-terminated strings that contain additional\n** details about the action to be authorized.\n**\n** ^If the action code is [SQLITE_READ]\n** and the callback returns [SQLITE_IGNORE] then the\n** [prepared statement] statement is constructed to substitute\n** a NULL value in place of the table column that would have\n** been read if [SQLITE_OK] had been returned.  The [SQLITE_IGNORE]\n** return can be used to deny an untrusted user access to individual\n** columns of a table.\n** ^If the action code is [SQLITE_DELETE] and the callback returns\n** [SQLITE_IGNORE] then the [DELETE] operation proceeds but the\n** [truncate optimization] is disabled and all rows are deleted individually.\n**\n** An authorizer is used when [sqlite3_prepare | preparing]\n** SQL statements from an untrusted source, to ensure that the SQL statements\n** do not try to access data they are not allowed to see, or that they do not\n** try to execute malicious statements that damage the database.  For\n** example, an application may allow a user to enter arbitrary\n** SQL queries for evaluation by a database.  But the application does\n** not want the user to be able to make arbitrary changes to the\n** database.  An authorizer could then be put in place while the\n** user-entered SQL is being [sqlite3_prepare | prepared] that\n** disallows everything except [SELECT] statements.\n**\n** Applications that need to process SQL from untrusted sources\n** might also consider lowering resource limits using [sqlite3_limit()]\n** and limiting database size using the [max_page_count] [PRAGMA]\n** in addition to using an authorizer.\n**\n** ^(Only a single authorizer can be in place on a database connection\n** at a time.  Each call to sqlite3_set_authorizer overrides the\n** previous call.)^  ^Disable the authorizer by installing a NULL callback.\n** The authorizer is disabled by default.\n**\n** The authorizer callback must not do anything that will modify\n** the database connection that invoked the authorizer callback.\n** Note that [sqlite3_prepare_v2()] and [sqlite3_step()] both modify their\n** database connections for the meaning of \"modify\" in this paragraph.\n**\n** ^When [sqlite3_prepare_v2()] is used to prepare a statement, the\n** statement might be re-prepared during [sqlite3_step()] due to a \n** schema change.  Hence, the application should ensure that the\n** correct authorizer callback remains in place during the [sqlite3_step()].\n**\n** ^Note that the authorizer callback is invoked only during\n** [sqlite3_prepare()] or its variants.  Authorization is not\n** performed during statement evaluation in [sqlite3_step()], unless\n** as stated in the previous paragraph, sqlite3_step() invokes\n** sqlite3_prepare_v2() to reprepare a statement after a schema change.\n*/\nSQLITE_API int sqlite3_set_authorizer(\n  sqlite3*,\n  int (*xAuth)(void*,int,const char*,const char*,const char*,const char*),\n  void *pUserData\n);\n\n/*\n** CAPI3REF: Authorizer Return Codes\n**\n** The [sqlite3_set_authorizer | authorizer callback function] must\n** return either [SQLITE_OK] or one of these two constants in order\n** to signal SQLite whether or not the action is permitted.  See the\n** [sqlite3_set_authorizer | authorizer documentation] for additional\n** information.\n**\n** Note that SQLITE_IGNORE is also used as a [SQLITE_ROLLBACK | return code]\n** from the [sqlite3_vtab_on_conflict()] interface.\n*/\n#define SQLITE_DENY   1   /* Abort the SQL statement with an error */\n#define SQLITE_IGNORE 2   /* Don't allow access, but don't generate an error */\n\n/*\n** CAPI3REF: Authorizer Action Codes\n**\n** The [sqlite3_set_authorizer()] interface registers a callback function\n** that is invoked to authorize certain SQL statement actions.  The\n** second parameter to the callback is an integer code that specifies\n** what action is being authorized.  These are the integer action codes that\n** the authorizer callback may be passed.\n**\n** These action code values signify what kind of operation is to be\n** authorized.  The 3rd and 4th parameters to the authorization\n** callback function will be parameters or NULL depending on which of these\n** codes is used as the second parameter.  ^(The 5th parameter to the\n** authorizer callback is the name of the database (\"main\", \"temp\",\n** etc.) if applicable.)^  ^The 6th parameter to the authorizer callback\n** is the name of the inner-most trigger or view that is responsible for\n** the access attempt or NULL if this access attempt is directly from\n** top-level SQL code.\n*/\n/******************************************* 3rd ************ 4th ***********/\n#define SQLITE_CREATE_INDEX          1   /* Index Name      Table Name      */\n#define SQLITE_CREATE_TABLE          2   /* Table Name      NULL            */\n#define SQLITE_CREATE_TEMP_INDEX     3   /* Index Name      Table Name      */\n#define SQLITE_CREATE_TEMP_TABLE     4   /* Table Name      NULL            */\n#define SQLITE_CREATE_TEMP_TRIGGER   5   /* Trigger Name    Table Name      */\n#define SQLITE_CREATE_TEMP_VIEW      6   /* View Name       NULL            */\n#define SQLITE_CREATE_TRIGGER        7   /* Trigger Name    Table Name      */\n#define SQLITE_CREATE_VIEW           8   /* View Name       NULL            */\n#define SQLITE_DELETE                9   /* Table Name      NULL            */\n#define SQLITE_DROP_INDEX           10   /* Index Name      Table Name      */\n#define SQLITE_DROP_TABLE           11   /* Table Name      NULL            */\n#define SQLITE_DROP_TEMP_INDEX      12   /* Index Name      Table Name      */\n#define SQLITE_DROP_TEMP_TABLE      13   /* Table Name      NULL            */\n#define SQLITE_DROP_TEMP_TRIGGER    14   /* Trigger Name    Table Name      */\n#define SQLITE_DROP_TEMP_VIEW       15   /* View Name       NULL            */\n#define SQLITE_DROP_TRIGGER         16   /* Trigger Name    Table Name      */\n#define SQLITE_DROP_VIEW            17   /* View Name       NULL            */\n#define SQLITE_INSERT               18   /* Table Name      NULL            */\n#define SQLITE_PRAGMA               19   /* Pragma Name     1st arg or NULL */\n#define SQLITE_READ                 20   /* Table Name      Column Name     */\n#define SQLITE_SELECT               21   /* NULL            NULL            */\n#define SQLITE_TRANSACTION          22   /* Operation       NULL            */\n#define SQLITE_UPDATE               23   /* Table Name      Column Name     */\n#define SQLITE_ATTACH               24   /* Filename        NULL            */\n#define SQLITE_DETACH               25   /* Database Name   NULL            */\n#define SQLITE_ALTER_TABLE          26   /* Database Name   Table Name      */\n#define SQLITE_REINDEX              27   /* Index Name      NULL            */\n#define SQLITE_ANALYZE              28   /* Table Name      NULL            */\n#define SQLITE_CREATE_VTABLE        29   /* Table Name      Module Name     */\n#define SQLITE_DROP_VTABLE          30   /* Table Name      Module Name     */\n#define SQLITE_FUNCTION             31   /* NULL            Function Name   */\n#define SQLITE_SAVEPOINT            32   /* Operation       Savepoint Name  */\n#define SQLITE_COPY                  0   /* No longer used */\n\n/*\n** CAPI3REF: Tracing And Profiling Functions\n**\n** These routines register callback functions that can be used for\n** tracing and profiling the execution of SQL statements.\n**\n** ^The callback function registered by sqlite3_trace() is invoked at\n** various times when an SQL statement is being run by [sqlite3_step()].\n** ^The sqlite3_trace() callback is invoked with a UTF-8 rendering of the\n** SQL statement text as the statement first begins executing.\n** ^(Additional sqlite3_trace() callbacks might occur\n** as each triggered subprogram is entered.  The callbacks for triggers\n** contain a UTF-8 SQL comment that identifies the trigger.)^\n**\n** ^The callback function registered by sqlite3_profile() is invoked\n** as each SQL statement finishes.  ^The profile callback contains\n** the original statement text and an estimate of wall-clock time\n** of how long that statement took to run.  ^The profile callback\n** time is in units of nanoseconds, however the current implementation\n** is only capable of millisecond resolution so the six least significant\n** digits in the time are meaningless.  Future versions of SQLite\n** might provide greater resolution on the profiler callback.  The\n** sqlite3_profile() function is considered experimental and is\n** subject to change in future versions of SQLite.\n*/\nSQLITE_API void *sqlite3_trace(sqlite3*, void(*xTrace)(void*,const char*), void*);\nSQLITE_API SQLITE_EXPERIMENTAL void *sqlite3_profile(sqlite3*,\n   void(*xProfile)(void*,const char*,sqlite3_uint64), void*);\n\n/*\n** CAPI3REF: Query Progress Callbacks\n**\n** ^The sqlite3_progress_handler(D,N,X,P) interface causes the callback\n** function X to be invoked periodically during long running calls to\n** [sqlite3_exec()], [sqlite3_step()] and [sqlite3_get_table()] for\n** database connection D.  An example use for this\n** interface is to keep a GUI updated during a large query.\n**\n** ^The parameter P is passed through as the only parameter to the \n** callback function X.  ^The parameter N is the number of \n** [virtual machine instructions] that are evaluated between successive\n** invocations of the callback X.\n**\n** ^Only a single progress handler may be defined at one time per\n** [database connection]; setting a new progress handler cancels the\n** old one.  ^Setting parameter X to NULL disables the progress handler.\n** ^The progress handler is also disabled by setting N to a value less\n** than 1.\n**\n** ^If the progress callback returns non-zero, the operation is\n** interrupted.  This feature can be used to implement a\n** \"Cancel\" button on a GUI progress dialog box.\n**\n** The progress handler callback must not do anything that will modify\n** the database connection that invoked the progress handler.\n** Note that [sqlite3_prepare_v2()] and [sqlite3_step()] both modify their\n** database connections for the meaning of \"modify\" in this paragraph.\n**\n*/\nSQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*);\n\n/*\n** CAPI3REF: Opening A New Database Connection\n**\n** ^These routines open an SQLite database file as specified by the \n** filename argument. ^The filename argument is interpreted as UTF-8 for\n** sqlite3_open() and sqlite3_open_v2() and as UTF-16 in the native byte\n** order for sqlite3_open16(). ^(A [database connection] handle is usually\n** returned in *ppDb, even if an error occurs.  The only exception is that\n** if SQLite is unable to allocate memory to hold the [sqlite3] object,\n** a NULL will be written into *ppDb instead of a pointer to the [sqlite3]\n** object.)^ ^(If the database is opened (and/or created) successfully, then\n** [SQLITE_OK] is returned.  Otherwise an [error code] is returned.)^ ^The\n** [sqlite3_errmsg()] or [sqlite3_errmsg16()] routines can be used to obtain\n** an English language description of the error following a failure of any\n** of the sqlite3_open() routines.\n**\n** ^The default encoding for the database will be UTF-8 if\n** sqlite3_open() or sqlite3_open_v2() is called and\n** UTF-16 in the native byte order if sqlite3_open16() is used.\n**\n** Whether or not an error occurs when it is opened, resources\n** associated with the [database connection] handle should be released by\n** passing it to [sqlite3_close()] when it is no longer required.\n**\n** The sqlite3_open_v2() interface works like sqlite3_open()\n** except that it accepts two additional parameters for additional control\n** over the new database connection.  ^(The flags parameter to\n** sqlite3_open_v2() can take one of\n** the following three values, optionally combined with the \n** [SQLITE_OPEN_NOMUTEX], [SQLITE_OPEN_FULLMUTEX], [SQLITE_OPEN_SHAREDCACHE],\n** [SQLITE_OPEN_PRIVATECACHE], and/or [SQLITE_OPEN_URI] flags:)^\n**\n** <dl>\n** ^(<dt>[SQLITE_OPEN_READONLY]</dt>\n** <dd>The database is opened in read-only mode.  If the database does not\n** already exist, an error is returned.</dd>)^\n**\n** ^(<dt>[SQLITE_OPEN_READWRITE]</dt>\n** <dd>The database is opened for reading and writing if possible, or reading\n** only if the file is write protected by the operating system.  In either\n** case the database must already exist, otherwise an error is returned.</dd>)^\n**\n** ^(<dt>[SQLITE_OPEN_READWRITE] | [SQLITE_OPEN_CREATE]</dt>\n** <dd>The database is opened for reading and writing, and is created if\n** it does not already exist. This is the behavior that is always used for\n** sqlite3_open() and sqlite3_open16().</dd>)^\n** </dl>\n**\n** If the 3rd parameter to sqlite3_open_v2() is not one of the\n** combinations shown above optionally combined with other\n** [SQLITE_OPEN_READONLY | SQLITE_OPEN_* bits]\n** then the behavior is undefined.\n**\n** ^If the [SQLITE_OPEN_NOMUTEX] flag is set, then the database connection\n** opens in the multi-thread [threading mode] as long as the single-thread\n** mode has not been set at compile-time or start-time.  ^If the\n** [SQLITE_OPEN_FULLMUTEX] flag is set then the database connection opens\n** in the serialized [threading mode] unless single-thread was\n** previously selected at compile-time or start-time.\n** ^The [SQLITE_OPEN_SHAREDCACHE] flag causes the database connection to be\n** eligible to use [shared cache mode], regardless of whether or not shared\n** cache is enabled using [sqlite3_enable_shared_cache()].  ^The\n** [SQLITE_OPEN_PRIVATECACHE] flag causes the database connection to not\n** participate in [shared cache mode] even if it is enabled.\n**\n** ^The fourth parameter to sqlite3_open_v2() is the name of the\n** [sqlite3_vfs] object that defines the operating system interface that\n** the new database connection should use.  ^If the fourth parameter is\n** a NULL pointer then the default [sqlite3_vfs] object is used.\n**\n** ^If the filename is \":memory:\", then a private, temporary in-memory database\n** is created for the connection.  ^This in-memory database will vanish when\n** the database connection is closed.  Future versions of SQLite might\n** make use of additional special filenames that begin with the \":\" character.\n** It is recommended that when a database filename actually does begin with\n** a \":\" character you should prefix the filename with a pathname such as\n** \"./\" to avoid ambiguity.\n**\n** ^If the filename is an empty string, then a private, temporary\n** on-disk database will be created.  ^This private database will be\n** automatically deleted as soon as the database connection is closed.\n**\n** [[URI filenames in sqlite3_open()]] <h3>URI Filenames</h3>\n**\n** ^If [URI filename] interpretation is enabled, and the filename argument\n** begins with \"file:\", then the filename is interpreted as a URI. ^URI\n** filename interpretation is enabled if the [SQLITE_OPEN_URI] flag is\n** set in the fourth argument to sqlite3_open_v2(), or if it has\n** been enabled globally using the [SQLITE_CONFIG_URI] option with the\n** [sqlite3_config()] method or by the [SQLITE_USE_URI] compile-time option.\n** As of SQLite version 3.7.7, URI filename interpretation is turned off\n** by default, but future releases of SQLite might enable URI filename\n** interpretation by default.  See \"[URI filenames]\" for additional\n** information.\n**\n** URI filenames are parsed according to RFC 3986. ^If the URI contains an\n** authority, then it must be either an empty string or the string \n** \"localhost\". ^If the authority is not an empty string or \"localhost\", an \n** error is returned to the caller. ^The fragment component of a URI, if \n** present, is ignored.\n**\n** ^SQLite uses the path component of the URI as the name of the disk file\n** which contains the database. ^If the path begins with a '/' character, \n** then it is interpreted as an absolute path. ^If the path does not begin \n** with a '/' (meaning that the authority section is omitted from the URI)\n** then the path is interpreted as a relative path. \n** ^On windows, the first component of an absolute path \n** is a drive specification (e.g. \"C:\").\n**\n** [[core URI query parameters]]\n** The query component of a URI may contain parameters that are interpreted\n** either by SQLite itself, or by a [VFS | custom VFS implementation].\n** SQLite interprets the following three query parameters:\n**\n** <ul>\n**   <li> <b>vfs</b>: ^The \"vfs\" parameter may be used to specify the name of\n**     a VFS object that provides the operating system interface that should\n**     be used to access the database file on disk. ^If this option is set to\n**     an empty string the default VFS object is used. ^Specifying an unknown\n**     VFS is an error. ^If sqlite3_open_v2() is used and the vfs option is\n**     present, then the VFS specified by the option takes precedence over\n**     the value passed as the fourth parameter to sqlite3_open_v2().\n**\n**   <li> <b>mode</b>: ^(The mode parameter may be set to either \"ro\", \"rw\",\n**     \"rwc\", or \"memory\". Attempting to set it to any other value is\n**     an error)^. \n**     ^If \"ro\" is specified, then the database is opened for read-only \n**     access, just as if the [SQLITE_OPEN_READONLY] flag had been set in the \n**     third argument to sqlite3_prepare_v2(). ^If the mode option is set to \n**     \"rw\", then the database is opened for read-write (but not create) \n**     access, as if SQLITE_OPEN_READWRITE (but not SQLITE_OPEN_CREATE) had \n**     been set. ^Value \"rwc\" is equivalent to setting both \n**     SQLITE_OPEN_READWRITE and SQLITE_OPEN_CREATE.  ^If the mode option is\n**     set to \"memory\" then a pure [in-memory database] that never reads\n**     or writes from disk is used. ^It is an error to specify a value for\n**     the mode parameter that is less restrictive than that specified by\n**     the flags passed in the third parameter to sqlite3_open_v2().\n**\n**   <li> <b>cache</b>: ^The cache parameter may be set to either \"shared\" or\n**     \"private\". ^Setting it to \"shared\" is equivalent to setting the\n**     SQLITE_OPEN_SHAREDCACHE bit in the flags argument passed to\n**     sqlite3_open_v2(). ^Setting the cache parameter to \"private\" is \n**     equivalent to setting the SQLITE_OPEN_PRIVATECACHE bit.\n**     ^If sqlite3_open_v2() is used and the \"cache\" parameter is present in\n**     a URI filename, its value overrides any behaviour requested by setting\n**     SQLITE_OPEN_PRIVATECACHE or SQLITE_OPEN_SHAREDCACHE flag.\n** </ul>\n**\n** ^Specifying an unknown parameter in the query component of a URI is not an\n** error.  Future versions of SQLite might understand additional query\n** parameters.  See \"[query parameters with special meaning to SQLite]\" for\n** additional information.\n**\n** [[URI filename examples]] <h3>URI filename examples</h3>\n**\n** <table border=\"1\" align=center cellpadding=5>\n** <tr><th> URI filenames <th> Results\n** <tr><td> file:data.db <td> \n**          Open the file \"data.db\" in the current directory.\n** <tr><td> file:/home/fred/data.db<br>\n**          file:///home/fred/data.db <br> \n**          file://localhost/home/fred/data.db <br> <td> \n**          Open the database file \"/home/fred/data.db\".\n** <tr><td> file://darkstar/home/fred/data.db <td> \n**          An error. \"darkstar\" is not a recognized authority.\n** <tr><td style=\"white-space:nowrap\"> \n**          file:///C:/Documents%20and%20Settings/fred/Desktop/data.db\n**     <td> Windows only: Open the file \"data.db\" on fred's desktop on drive\n**          C:. Note that the %20 escaping in this example is not strictly \n**          necessary - space characters can be used literally\n**          in URI filenames.\n** <tr><td> file:data.db?mode=ro&cache=private <td> \n**          Open file \"data.db\" in the current directory for read-only access.\n**          Regardless of whether or not shared-cache mode is enabled by\n**          default, use a private cache.\n** <tr><td> file:/home/fred/data.db?vfs=unix-nolock <td>\n**          Open file \"/home/fred/data.db\". Use the special VFS \"unix-nolock\".\n** <tr><td> file:data.db?mode=readonly <td> \n**          An error. \"readonly\" is not a valid option for the \"mode\" parameter.\n** </table>\n**\n** ^URI hexadecimal escape sequences (%HH) are supported within the path and\n** query components of a URI. A hexadecimal escape sequence consists of a\n** percent sign - \"%\" - followed by exactly two hexadecimal digits \n** specifying an octet value. ^Before the path or query components of a\n** URI filename are interpreted, they are encoded using UTF-8 and all \n** hexadecimal escape sequences replaced by a single byte containing the\n** corresponding octet. If this process generates an invalid UTF-8 encoding,\n** the results are undefined.\n**\n** <b>Note to Windows users:</b>  The encoding used for the filename argument\n** of sqlite3_open() and sqlite3_open_v2() must be UTF-8, not whatever\n** codepage is currently defined.  Filenames containing international\n** characters must be converted to UTF-8 prior to passing them into\n** sqlite3_open() or sqlite3_open_v2().\n**\n** <b>Note to Windows Runtime users:</b>  The temporary directory must be set\n** prior to calling sqlite3_open() or sqlite3_open_v2().  Otherwise, various\n** features that require the use of temporary files may fail.\n**\n** See also: [sqlite3_temp_directory]\n*/\nSQLITE_API int sqlite3_open(\n  const char *filename,   /* Database filename (UTF-8) */\n  sqlite3 **ppDb          /* OUT: SQLite db handle */\n);\nSQLITE_API int sqlite3_open16(\n  const void *filename,   /* Database filename (UTF-16) */\n  sqlite3 **ppDb          /* OUT: SQLite db handle */\n);\nSQLITE_API int sqlite3_open_v2(\n  const char *filename,   /* Database filename (UTF-8) */\n  sqlite3 **ppDb,         /* OUT: SQLite db handle */\n  int flags,              /* Flags */\n  const char *zVfs        /* Name of VFS module to use */\n);\n\n/*\n** CAPI3REF: Obtain Values For URI Parameters\n**\n** These are utility routines, useful to VFS implementations, that check\n** to see if a database file was a URI that contained a specific query \n** parameter, and if so obtains the value of that query parameter.\n**\n** If F is the database filename pointer passed into the xOpen() method of \n** a VFS implementation when the flags parameter to xOpen() has one or \n** more of the [SQLITE_OPEN_URI] or [SQLITE_OPEN_MAIN_DB] bits set and\n** P is the name of the query parameter, then\n** sqlite3_uri_parameter(F,P) returns the value of the P\n** parameter if it exists or a NULL pointer if P does not appear as a \n** query parameter on F.  If P is a query parameter of F\n** has no explicit value, then sqlite3_uri_parameter(F,P) returns\n** a pointer to an empty string.\n**\n** The sqlite3_uri_boolean(F,P,B) routine assumes that P is a boolean\n** parameter and returns true (1) or false (0) according to the value\n** of P.  The sqlite3_uri_boolean(F,P,B) routine returns true (1) if the\n** value of query parameter P is one of \"yes\", \"true\", or \"on\" in any\n** case or if the value begins with a non-zero number.  The \n** sqlite3_uri_boolean(F,P,B) routines returns false (0) if the value of\n** query parameter P is one of \"no\", \"false\", or \"off\" in any case or\n** if the value begins with a numeric zero.  If P is not a query\n** parameter on F or if the value of P is does not match any of the\n** above, then sqlite3_uri_boolean(F,P,B) returns (B!=0).\n**\n** The sqlite3_uri_int64(F,P,D) routine converts the value of P into a\n** 64-bit signed integer and returns that integer, or D if P does not\n** exist.  If the value of P is something other than an integer, then\n** zero is returned.\n** \n** If F is a NULL pointer, then sqlite3_uri_parameter(F,P) returns NULL and\n** sqlite3_uri_boolean(F,P,B) returns B.  If F is not a NULL pointer and\n** is not a database file pathname pointer that SQLite passed into the xOpen\n** VFS method, then the behavior of this routine is undefined and probably\n** undesirable.\n*/\nSQLITE_API const char *sqlite3_uri_parameter(const char *zFilename, const char *zParam);\nSQLITE_API int sqlite3_uri_boolean(const char *zFile, const char *zParam, int bDefault);\nSQLITE_API sqlite3_int64 sqlite3_uri_int64(const char*, const char*, sqlite3_int64);\n\n\n/*\n** CAPI3REF: Error Codes And Messages\n**\n** ^The sqlite3_errcode() interface returns the numeric [result code] or\n** [extended result code] for the most recent failed sqlite3_* API call\n** associated with a [database connection]. If a prior API call failed\n** but the most recent API call succeeded, the return value from\n** sqlite3_errcode() is undefined.  ^The sqlite3_extended_errcode()\n** interface is the same except that it always returns the \n** [extended result code] even when extended result codes are\n** disabled.\n**\n** ^The sqlite3_errmsg() and sqlite3_errmsg16() return English-language\n** text that describes the error, as either UTF-8 or UTF-16 respectively.\n** ^(Memory to hold the error message string is managed internally.\n** The application does not need to worry about freeing the result.\n** However, the error string might be overwritten or deallocated by\n** subsequent calls to other SQLite interface functions.)^\n**\n** When the serialized [threading mode] is in use, it might be the\n** case that a second error occurs on a separate thread in between\n** the time of the first error and the call to these interfaces.\n** When that happens, the second error will be reported since these\n** interfaces always report the most recent result.  To avoid\n** this, each thread can obtain exclusive use of the [database connection] D\n** by invoking [sqlite3_mutex_enter]([sqlite3_db_mutex](D)) before beginning\n** to use D and invoking [sqlite3_mutex_leave]([sqlite3_db_mutex](D)) after\n** all calls to the interfaces listed here are completed.\n**\n** If an interface fails with SQLITE_MISUSE, that means the interface\n** was invoked incorrectly by the application.  In that case, the\n** error code and message may or may not be set.\n*/\nSQLITE_API int sqlite3_errcode(sqlite3 *db);\nSQLITE_API int sqlite3_extended_errcode(sqlite3 *db);\nSQLITE_API const char *sqlite3_errmsg(sqlite3*);\nSQLITE_API const void *sqlite3_errmsg16(sqlite3*);\n\n/*\n** CAPI3REF: SQL Statement Object\n** KEYWORDS: {prepared statement} {prepared statements}\n**\n** An instance of this object represents a single SQL statement.\n** This object is variously known as a \"prepared statement\" or a\n** \"compiled SQL statement\" or simply as a \"statement\".\n**\n** The life of a statement object goes something like this:\n**\n** <ol>\n** <li> Create the object using [sqlite3_prepare_v2()] or a related\n**      function.\n** <li> Bind values to [host parameters] using the sqlite3_bind_*()\n**      interfaces.\n** <li> Run the SQL by calling [sqlite3_step()] one or more times.\n** <li> Reset the statement using [sqlite3_reset()] then go back\n**      to step 2.  Do this zero or more times.\n** <li> Destroy the object using [sqlite3_finalize()].\n** </ol>\n**\n** Refer to documentation on individual methods above for additional\n** information.\n*/\ntypedef struct sqlite3_stmt sqlite3_stmt;\n\n/*\n** CAPI3REF: Run-time Limits\n**\n** ^(This interface allows the size of various constructs to be limited\n** on a connection by connection basis.  The first parameter is the\n** [database connection] whose limit is to be set or queried.  The\n** second parameter is one of the [limit categories] that define a\n** class of constructs to be size limited.  The third parameter is the\n** new limit for that construct.)^\n**\n** ^If the new limit is a negative number, the limit is unchanged.\n** ^(For each limit category SQLITE_LIMIT_<i>NAME</i> there is a \n** [limits | hard upper bound]\n** set at compile-time by a C preprocessor macro called\n** [limits | SQLITE_MAX_<i>NAME</i>].\n** (The \"_LIMIT_\" in the name is changed to \"_MAX_\".))^\n** ^Attempts to increase a limit above its hard upper bound are\n** silently truncated to the hard upper bound.\n**\n** ^Regardless of whether or not the limit was changed, the \n** [sqlite3_limit()] interface returns the prior value of the limit.\n** ^Hence, to find the current value of a limit without changing it,\n** simply invoke this interface with the third parameter set to -1.\n**\n** Run-time limits are intended for use in applications that manage\n** both their own internal database and also databases that are controlled\n** by untrusted external sources.  An example application might be a\n** web browser that has its own databases for storing history and\n** separate databases controlled by JavaScript applications downloaded\n** off the Internet.  The internal databases can be given the\n** large, default limits.  Databases managed by external sources can\n** be given much smaller limits designed to prevent a denial of service\n** attack.  Developers might also want to use the [sqlite3_set_authorizer()]\n** interface to further control untrusted SQL.  The size of the database\n** created by an untrusted script can be contained using the\n** [max_page_count] [PRAGMA].\n**\n** New run-time limit categories may be added in future releases.\n*/\nSQLITE_API int sqlite3_limit(sqlite3*, int id, int newVal);\n\n/*\n** CAPI3REF: Run-Time Limit Categories\n** KEYWORDS: {limit category} {*limit categories}\n**\n** These constants define various performance limits\n** that can be lowered at run-time using [sqlite3_limit()].\n** The synopsis of the meanings of the various limits is shown below.\n** Additional information is available at [limits | Limits in SQLite].\n**\n** <dl>\n** [[SQLITE_LIMIT_LENGTH]] ^(<dt>SQLITE_LIMIT_LENGTH</dt>\n** <dd>The maximum size of any string or BLOB or table row, in bytes.<dd>)^\n**\n** [[SQLITE_LIMIT_SQL_LENGTH]] ^(<dt>SQLITE_LIMIT_SQL_LENGTH</dt>\n** <dd>The maximum length of an SQL statement, in bytes.</dd>)^\n**\n** [[SQLITE_LIMIT_COLUMN]] ^(<dt>SQLITE_LIMIT_COLUMN</dt>\n** <dd>The maximum number of columns in a table definition or in the\n** result set of a [SELECT] or the maximum number of columns in an index\n** or in an ORDER BY or GROUP BY clause.</dd>)^\n**\n** [[SQLITE_LIMIT_EXPR_DEPTH]] ^(<dt>SQLITE_LIMIT_EXPR_DEPTH</dt>\n** <dd>The maximum depth of the parse tree on any expression.</dd>)^\n**\n** [[SQLITE_LIMIT_COMPOUND_SELECT]] ^(<dt>SQLITE_LIMIT_COMPOUND_SELECT</dt>\n** <dd>The maximum number of terms in a compound SELECT statement.</dd>)^\n**\n** [[SQLITE_LIMIT_VDBE_OP]] ^(<dt>SQLITE_LIMIT_VDBE_OP</dt>\n** <dd>The maximum number of instructions in a virtual machine program\n** used to implement an SQL statement.  This limit is not currently\n** enforced, though that might be added in some future release of\n** SQLite.</dd>)^\n**\n** [[SQLITE_LIMIT_FUNCTION_ARG]] ^(<dt>SQLITE_LIMIT_FUNCTION_ARG</dt>\n** <dd>The maximum number of arguments on a function.</dd>)^\n**\n** [[SQLITE_LIMIT_ATTACHED]] ^(<dt>SQLITE_LIMIT_ATTACHED</dt>\n** <dd>The maximum number of [ATTACH | attached databases].)^</dd>\n**\n** [[SQLITE_LIMIT_LIKE_PATTERN_LENGTH]]\n** ^(<dt>SQLITE_LIMIT_LIKE_PATTERN_LENGTH</dt>\n** <dd>The maximum length of the pattern argument to the [LIKE] or\n** [GLOB] operators.</dd>)^\n**\n** [[SQLITE_LIMIT_VARIABLE_NUMBER]]\n** ^(<dt>SQLITE_LIMIT_VARIABLE_NUMBER</dt>\n** <dd>The maximum index number of any [parameter] in an SQL statement.)^\n**\n** [[SQLITE_LIMIT_TRIGGER_DEPTH]] ^(<dt>SQLITE_LIMIT_TRIGGER_DEPTH</dt>\n** <dd>The maximum depth of recursion for triggers.</dd>)^\n** </dl>\n*/\n#define SQLITE_LIMIT_LENGTH                    0\n#define SQLITE_LIMIT_SQL_LENGTH                1\n#define SQLITE_LIMIT_COLUMN                    2\n#define SQLITE_LIMIT_EXPR_DEPTH                3\n#define SQLITE_LIMIT_COMPOUND_SELECT           4\n#define SQLITE_LIMIT_VDBE_OP                   5\n#define SQLITE_LIMIT_FUNCTION_ARG              6\n#define SQLITE_LIMIT_ATTACHED                  7\n#define SQLITE_LIMIT_LIKE_PATTERN_LENGTH       8\n#define SQLITE_LIMIT_VARIABLE_NUMBER           9\n#define SQLITE_LIMIT_TRIGGER_DEPTH            10\n\n/*\n** CAPI3REF: Compiling An SQL Statement\n** KEYWORDS: {SQL statement compiler}\n**\n** To execute an SQL query, it must first be compiled into a byte-code\n** program using one of these routines.\n**\n** The first argument, \"db\", is a [database connection] obtained from a\n** prior successful call to [sqlite3_open()], [sqlite3_open_v2()] or\n** [sqlite3_open16()].  The database connection must not have been closed.\n**\n** The second argument, \"zSql\", is the statement to be compiled, encoded\n** as either UTF-8 or UTF-16.  The sqlite3_prepare() and sqlite3_prepare_v2()\n** interfaces use UTF-8, and sqlite3_prepare16() and sqlite3_prepare16_v2()\n** use UTF-16.\n**\n** ^If the nByte argument is less than zero, then zSql is read up to the\n** first zero terminator. ^If nByte is non-negative, then it is the maximum\n** number of  bytes read from zSql.  ^When nByte is non-negative, the\n** zSql string ends at either the first '\\000' or '\\u0000' character or\n** the nByte-th byte, whichever comes first. If the caller knows\n** that the supplied string is nul-terminated, then there is a small\n** performance advantage to be gained by passing an nByte parameter that\n** is equal to the number of bytes in the input string <i>including</i>\n** the nul-terminator bytes as this saves SQLite from having to\n** make a copy of the input string.\n**\n** ^If pzTail is not NULL then *pzTail is made to point to the first byte\n** past the end of the first SQL statement in zSql.  These routines only\n** compile the first statement in zSql, so *pzTail is left pointing to\n** what remains uncompiled.\n**\n** ^*ppStmt is left pointing to a compiled [prepared statement] that can be\n** executed using [sqlite3_step()].  ^If there is an error, *ppStmt is set\n** to NULL.  ^If the input text contains no SQL (if the input is an empty\n** string or a comment) then *ppStmt is set to NULL.\n** The calling procedure is responsible for deleting the compiled\n** SQL statement using [sqlite3_finalize()] after it has finished with it.\n** ppStmt may not be NULL.\n**\n** ^On success, the sqlite3_prepare() family of routines return [SQLITE_OK];\n** otherwise an [error code] is returned.\n**\n** The sqlite3_prepare_v2() and sqlite3_prepare16_v2() interfaces are\n** recommended for all new programs. The two older interfaces are retained\n** for backwards compatibility, but their use is discouraged.\n** ^In the \"v2\" interfaces, the prepared statement\n** that is returned (the [sqlite3_stmt] object) contains a copy of the\n** original SQL text. This causes the [sqlite3_step()] interface to\n** behave differently in three ways:\n**\n** <ol>\n** <li>\n** ^If the database schema changes, instead of returning [SQLITE_SCHEMA] as it\n** always used to do, [sqlite3_step()] will automatically recompile the SQL\n** statement and try to run it again.\n** </li>\n**\n** <li>\n** ^When an error occurs, [sqlite3_step()] will return one of the detailed\n** [error codes] or [extended error codes].  ^The legacy behavior was that\n** [sqlite3_step()] would only return a generic [SQLITE_ERROR] result code\n** and the application would have to make a second call to [sqlite3_reset()]\n** in order to find the underlying cause of the problem. With the \"v2\" prepare\n** interfaces, the underlying reason for the error is returned immediately.\n** </li>\n**\n** <li>\n** ^If the specific value bound to [parameter | host parameter] in the \n** WHERE clause might influence the choice of query plan for a statement,\n** then the statement will be automatically recompiled, as if there had been \n** a schema change, on the first  [sqlite3_step()] call following any change\n** to the [sqlite3_bind_text | bindings] of that [parameter]. \n** ^The specific value of WHERE-clause [parameter] might influence the \n** choice of query plan if the parameter is the left-hand side of a [LIKE]\n** or [GLOB] operator or if the parameter is compared to an indexed column\n** and the [SQLITE_ENABLE_STAT3] compile-time option is enabled.\n** the \n** </li>\n** </ol>\n*/\nSQLITE_API int sqlite3_prepare(\n  sqlite3 *db,            /* Database handle */\n  const char *zSql,       /* SQL statement, UTF-8 encoded */\n  int nByte,              /* Maximum length of zSql in bytes. */\n  sqlite3_stmt **ppStmt,  /* OUT: Statement handle */\n  const char **pzTail     /* OUT: Pointer to unused portion of zSql */\n);\nSQLITE_API int sqlite3_prepare_v2(\n  sqlite3 *db,            /* Database handle */\n  const char *zSql,       /* SQL statement, UTF-8 encoded */\n  int nByte,              /* Maximum length of zSql in bytes. */\n  sqlite3_stmt **ppStmt,  /* OUT: Statement handle */\n  const char **pzTail     /* OUT: Pointer to unused portion of zSql */\n);\nSQLITE_API int sqlite3_prepare16(\n  sqlite3 *db,            /* Database handle */\n  const void *zSql,       /* SQL statement, UTF-16 encoded */\n  int nByte,              /* Maximum length of zSql in bytes. */\n  sqlite3_stmt **ppStmt,  /* OUT: Statement handle */\n  const void **pzTail     /* OUT: Pointer to unused portion of zSql */\n);\nSQLITE_API int sqlite3_prepare16_v2(\n  sqlite3 *db,            /* Database handle */\n  const void *zSql,       /* SQL statement, UTF-16 encoded */\n  int nByte,              /* Maximum length of zSql in bytes. */\n  sqlite3_stmt **ppStmt,  /* OUT: Statement handle */\n  const void **pzTail     /* OUT: Pointer to unused portion of zSql */\n);\n\n/*\n** CAPI3REF: Retrieving Statement SQL\n**\n** ^This interface can be used to retrieve a saved copy of the original\n** SQL text used to create a [prepared statement] if that statement was\n** compiled using either [sqlite3_prepare_v2()] or [sqlite3_prepare16_v2()].\n*/\nSQLITE_API const char *sqlite3_sql(sqlite3_stmt *pStmt);\n\n/*\n** CAPI3REF: Determine If An SQL Statement Writes The Database\n**\n** ^The sqlite3_stmt_readonly(X) interface returns true (non-zero) if\n** and only if the [prepared statement] X makes no direct changes to\n** the content of the database file.\n**\n** Note that [application-defined SQL functions] or\n** [virtual tables] might change the database indirectly as a side effect.  \n** ^(For example, if an application defines a function \"eval()\" that \n** calls [sqlite3_exec()], then the following SQL statement would\n** change the database file through side-effects:\n**\n** <blockquote><pre>\n**    SELECT eval('DELETE FROM t1') FROM t2;\n** </pre></blockquote>\n**\n** But because the [SELECT] statement does not change the database file\n** directly, sqlite3_stmt_readonly() would still return true.)^\n**\n** ^Transaction control statements such as [BEGIN], [COMMIT], [ROLLBACK],\n** [SAVEPOINT], and [RELEASE] cause sqlite3_stmt_readonly() to return true,\n** since the statements themselves do not actually modify the database but\n** rather they control the timing of when other statements modify the \n** database.  ^The [ATTACH] and [DETACH] statements also cause\n** sqlite3_stmt_readonly() to return true since, while those statements\n** change the configuration of a database connection, they do not make \n** changes to the content of the database files on disk.\n*/\nSQLITE_API int sqlite3_stmt_readonly(sqlite3_stmt *pStmt);\n\n/*\n** CAPI3REF: Determine If A Prepared Statement Has Been Reset\n**\n** ^The sqlite3_stmt_busy(S) interface returns true (non-zero) if the\n** [prepared statement] S has been stepped at least once using \n** [sqlite3_step(S)] but has not run to completion and/or has not \n** been reset using [sqlite3_reset(S)].  ^The sqlite3_stmt_busy(S)\n** interface returns false if S is a NULL pointer.  If S is not a \n** NULL pointer and is not a pointer to a valid [prepared statement]\n** object, then the behavior is undefined and probably undesirable.\n**\n** This interface can be used in combination [sqlite3_next_stmt()]\n** to locate all prepared statements associated with a database \n** connection that are in need of being reset.  This can be used,\n** for example, in diagnostic routines to search for prepared \n** statements that are holding a transaction open.\n*/\nSQLITE_API int sqlite3_stmt_busy(sqlite3_stmt*);\n\n/*\n** CAPI3REF: Dynamically Typed Value Object\n** KEYWORDS: {protected sqlite3_value} {unprotected sqlite3_value}\n**\n** SQLite uses the sqlite3_value object to represent all values\n** that can be stored in a database table. SQLite uses dynamic typing\n** for the values it stores.  ^Values stored in sqlite3_value objects\n** can be integers, floating point values, strings, BLOBs, or NULL.\n**\n** An sqlite3_value object may be either \"protected\" or \"unprotected\".\n** Some interfaces require a protected sqlite3_value.  Other interfaces\n** will accept either a protected or an unprotected sqlite3_value.\n** Every interface that accepts sqlite3_value arguments specifies\n** whether or not it requires a protected sqlite3_value.\n**\n** The terms \"protected\" and \"unprotected\" refer to whether or not\n** a mutex is held.  An internal mutex is held for a protected\n** sqlite3_value object but no mutex is held for an unprotected\n** sqlite3_value object.  If SQLite is compiled to be single-threaded\n** (with [SQLITE_THREADSAFE=0] and with [sqlite3_threadsafe()] returning 0)\n** or if SQLite is run in one of reduced mutex modes \n** [SQLITE_CONFIG_SINGLETHREAD] or [SQLITE_CONFIG_MULTITHREAD]\n** then there is no distinction between protected and unprotected\n** sqlite3_value objects and they can be used interchangeably.  However,\n** for maximum code portability it is recommended that applications\n** still make the distinction between protected and unprotected\n** sqlite3_value objects even when not strictly required.\n**\n** ^The sqlite3_value objects that are passed as parameters into the\n** implementation of [application-defined SQL functions] are protected.\n** ^The sqlite3_value object returned by\n** [sqlite3_column_value()] is unprotected.\n** Unprotected sqlite3_value objects may only be used with\n** [sqlite3_result_value()] and [sqlite3_bind_value()].\n** The [sqlite3_value_blob | sqlite3_value_type()] family of\n** interfaces require protected sqlite3_value objects.\n*/\ntypedef struct Mem sqlite3_value;\n\n/*\n** CAPI3REF: SQL Function Context Object\n**\n** The context in which an SQL function executes is stored in an\n** sqlite3_context object.  ^A pointer to an sqlite3_context object\n** is always first parameter to [application-defined SQL functions].\n** The application-defined SQL function implementation will pass this\n** pointer through into calls to [sqlite3_result_int | sqlite3_result()],\n** [sqlite3_aggregate_context()], [sqlite3_user_data()],\n** [sqlite3_context_db_handle()], [sqlite3_get_auxdata()],\n** and/or [sqlite3_set_auxdata()].\n*/\ntypedef struct sqlite3_context sqlite3_context;\n\n/*\n** CAPI3REF: Binding Values To Prepared Statements\n** KEYWORDS: {host parameter} {host parameters} {host parameter name}\n** KEYWORDS: {SQL parameter} {SQL parameters} {parameter binding}\n**\n** ^(In the SQL statement text input to [sqlite3_prepare_v2()] and its variants,\n** literals may be replaced by a [parameter] that matches one of following\n** templates:\n**\n** <ul>\n** <li>  ?\n** <li>  ?NNN\n** <li>  :VVV\n** <li>  @VVV\n** <li>  $VVV\n** </ul>\n**\n** In the templates above, NNN represents an integer literal,\n** and VVV represents an alphanumeric identifier.)^  ^The values of these\n** parameters (also called \"host parameter names\" or \"SQL parameters\")\n** can be set using the sqlite3_bind_*() routines defined here.\n**\n** ^The first argument to the sqlite3_bind_*() routines is always\n** a pointer to the [sqlite3_stmt] object returned from\n** [sqlite3_prepare_v2()] or its variants.\n**\n** ^The second argument is the index of the SQL parameter to be set.\n** ^The leftmost SQL parameter has an index of 1.  ^When the same named\n** SQL parameter is used more than once, second and subsequent\n** occurrences have the same index as the first occurrence.\n** ^The index for named parameters can be looked up using the\n** [sqlite3_bind_parameter_index()] API if desired.  ^The index\n** for \"?NNN\" parameters is the value of NNN.\n** ^The NNN value must be between 1 and the [sqlite3_limit()]\n** parameter [SQLITE_LIMIT_VARIABLE_NUMBER] (default value: 999).\n**\n** ^The third argument is the value to bind to the parameter.\n**\n** ^(In those routines that have a fourth argument, its value is the\n** number of bytes in the parameter.  To be clear: the value is the\n** number of <u>bytes</u> in the value, not the number of characters.)^\n** ^If the fourth parameter to sqlite3_bind_text() or sqlite3_bind_text16()\n** is negative, then the length of the string is\n** the number of bytes up to the first zero terminator.\n** If the fourth parameter to sqlite3_bind_blob() is negative, then\n** the behavior is undefined.\n** If a non-negative fourth parameter is provided to sqlite3_bind_text()\n** or sqlite3_bind_text16() then that parameter must be the byte offset\n** where the NUL terminator would occur assuming the string were NUL\n** terminated.  If any NUL characters occur at byte offsets less than \n** the value of the fourth parameter then the resulting string value will\n** contain embedded NULs.  The result of expressions involving strings\n** with embedded NULs is undefined.\n**\n** ^The fifth argument to sqlite3_bind_blob(), sqlite3_bind_text(), and\n** sqlite3_bind_text16() is a destructor used to dispose of the BLOB or\n** string after SQLite has finished with it.  ^The destructor is called\n** to dispose of the BLOB or string even if the call to sqlite3_bind_blob(),\n** sqlite3_bind_text(), or sqlite3_bind_text16() fails.  \n** ^If the fifth argument is\n** the special value [SQLITE_STATIC], then SQLite assumes that the\n** information is in static, unmanaged space and does not need to be freed.\n** ^If the fifth argument has the value [SQLITE_TRANSIENT], then\n** SQLite makes its own private copy of the data immediately, before\n** the sqlite3_bind_*() routine returns.\n**\n** ^The sqlite3_bind_zeroblob() routine binds a BLOB of length N that\n** is filled with zeroes.  ^A zeroblob uses a fixed amount of memory\n** (just an integer to hold its size) while it is being processed.\n** Zeroblobs are intended to serve as placeholders for BLOBs whose\n** content is later written using\n** [sqlite3_blob_open | incremental BLOB I/O] routines.\n** ^A negative value for the zeroblob results in a zero-length BLOB.\n**\n** ^If any of the sqlite3_bind_*() routines are called with a NULL pointer\n** for the [prepared statement] or with a prepared statement for which\n** [sqlite3_step()] has been called more recently than [sqlite3_reset()],\n** then the call will return [SQLITE_MISUSE].  If any sqlite3_bind_()\n** routine is passed a [prepared statement] that has been finalized, the\n** result is undefined and probably harmful.\n**\n** ^Bindings are not cleared by the [sqlite3_reset()] routine.\n** ^Unbound parameters are interpreted as NULL.\n**\n** ^The sqlite3_bind_* routines return [SQLITE_OK] on success or an\n** [error code] if anything goes wrong.\n** ^[SQLITE_RANGE] is returned if the parameter\n** index is out of range.  ^[SQLITE_NOMEM] is returned if malloc() fails.\n**\n** See also: [sqlite3_bind_parameter_count()],\n** [sqlite3_bind_parameter_name()], and [sqlite3_bind_parameter_index()].\n*/\nSQLITE_API int sqlite3_bind_blob(sqlite3_stmt*, int, const void*, int n, void(*)(void*));\nSQLITE_API int sqlite3_bind_double(sqlite3_stmt*, int, double);\nSQLITE_API int sqlite3_bind_int(sqlite3_stmt*, int, int);\nSQLITE_API int sqlite3_bind_int64(sqlite3_stmt*, int, sqlite3_int64);\nSQLITE_API int sqlite3_bind_null(sqlite3_stmt*, int);\nSQLITE_API int sqlite3_bind_text(sqlite3_stmt*, int, const char*, int n, void(*)(void*));\nSQLITE_API int sqlite3_bind_text16(sqlite3_stmt*, int, const void*, int, void(*)(void*));\nSQLITE_API int sqlite3_bind_value(sqlite3_stmt*, int, const sqlite3_value*);\nSQLITE_API int sqlite3_bind_zeroblob(sqlite3_stmt*, int, int n);\n\n/*\n** CAPI3REF: Number Of SQL Parameters\n**\n** ^This routine can be used to find the number of [SQL parameters]\n** in a [prepared statement].  SQL parameters are tokens of the\n** form \"?\", \"?NNN\", \":AAA\", \"$AAA\", or \"@AAA\" that serve as\n** placeholders for values that are [sqlite3_bind_blob | bound]\n** to the parameters at a later time.\n**\n** ^(This routine actually returns the index of the largest (rightmost)\n** parameter. For all forms except ?NNN, this will correspond to the\n** number of unique parameters.  If parameters of the ?NNN form are used,\n** there may be gaps in the list.)^\n**\n** See also: [sqlite3_bind_blob|sqlite3_bind()],\n** [sqlite3_bind_parameter_name()], and\n** [sqlite3_bind_parameter_index()].\n*/\nSQLITE_API int sqlite3_bind_parameter_count(sqlite3_stmt*);\n\n/*\n** CAPI3REF: Name Of A Host Parameter\n**\n** ^The sqlite3_bind_parameter_name(P,N) interface returns\n** the name of the N-th [SQL parameter] in the [prepared statement] P.\n** ^(SQL parameters of the form \"?NNN\" or \":AAA\" or \"@AAA\" or \"$AAA\"\n** have a name which is the string \"?NNN\" or \":AAA\" or \"@AAA\" or \"$AAA\"\n** respectively.\n** In other words, the initial \":\" or \"$\" or \"@\" or \"?\"\n** is included as part of the name.)^\n** ^Parameters of the form \"?\" without a following integer have no name\n** and are referred to as \"nameless\" or \"anonymous parameters\".\n**\n** ^The first host parameter has an index of 1, not 0.\n**\n** ^If the value N is out of range or if the N-th parameter is\n** nameless, then NULL is returned.  ^The returned string is\n** always in UTF-8 encoding even if the named parameter was\n** originally specified as UTF-16 in [sqlite3_prepare16()] or\n** [sqlite3_prepare16_v2()].\n**\n** See also: [sqlite3_bind_blob|sqlite3_bind()],\n** [sqlite3_bind_parameter_count()], and\n** [sqlite3_bind_parameter_index()].\n*/\nSQLITE_API const char *sqlite3_bind_parameter_name(sqlite3_stmt*, int);\n\n/*\n** CAPI3REF: Index Of A Parameter With A Given Name\n**\n** ^Return the index of an SQL parameter given its name.  ^The\n** index value returned is suitable for use as the second\n** parameter to [sqlite3_bind_blob|sqlite3_bind()].  ^A zero\n** is returned if no matching parameter is found.  ^The parameter\n** name must be given in UTF-8 even if the original statement\n** was prepared from UTF-16 text using [sqlite3_prepare16_v2()].\n**\n** See also: [sqlite3_bind_blob|sqlite3_bind()],\n** [sqlite3_bind_parameter_count()], and\n** [sqlite3_bind_parameter_index()].\n*/\nSQLITE_API int sqlite3_bind_parameter_index(sqlite3_stmt*, const char *zName);\n\n/*\n** CAPI3REF: Reset All Bindings On A Prepared Statement\n**\n** ^Contrary to the intuition of many, [sqlite3_reset()] does not reset\n** the [sqlite3_bind_blob | bindings] on a [prepared statement].\n** ^Use this routine to reset all host parameters to NULL.\n*/\nSQLITE_API int sqlite3_clear_bindings(sqlite3_stmt*);\n\n/*\n** CAPI3REF: Number Of Columns In A Result Set\n**\n** ^Return the number of columns in the result set returned by the\n** [prepared statement]. ^This routine returns 0 if pStmt is an SQL\n** statement that does not return data (for example an [UPDATE]).\n**\n** See also: [sqlite3_data_count()]\n*/\nSQLITE_API int sqlite3_column_count(sqlite3_stmt *pStmt);\n\n/*\n** CAPI3REF: Column Names In A Result Set\n**\n** ^These routines return the name assigned to a particular column\n** in the result set of a [SELECT] statement.  ^The sqlite3_column_name()\n** interface returns a pointer to a zero-terminated UTF-8 string\n** and sqlite3_column_name16() returns a pointer to a zero-terminated\n** UTF-16 string.  ^The first parameter is the [prepared statement]\n** that implements the [SELECT] statement. ^The second parameter is the\n** column number.  ^The leftmost column is number 0.\n**\n** ^The returned string pointer is valid until either the [prepared statement]\n** is destroyed by [sqlite3_finalize()] or until the statement is automatically\n** reprepared by the first call to [sqlite3_step()] for a particular run\n** or until the next call to\n** sqlite3_column_name() or sqlite3_column_name16() on the same column.\n**\n** ^If sqlite3_malloc() fails during the processing of either routine\n** (for example during a conversion from UTF-8 to UTF-16) then a\n** NULL pointer is returned.\n**\n** ^The name of a result column is the value of the \"AS\" clause for\n** that column, if there is an AS clause.  If there is no AS clause\n** then the name of the column is unspecified and may change from\n** one release of SQLite to the next.\n*/\nSQLITE_API const char *sqlite3_column_name(sqlite3_stmt*, int N);\nSQLITE_API const void *sqlite3_column_name16(sqlite3_stmt*, int N);\n\n/*\n** CAPI3REF: Source Of Data In A Query Result\n**\n** ^These routines provide a means to determine the database, table, and\n** table column that is the origin of a particular result column in\n** [SELECT] statement.\n** ^The name of the database or table or column can be returned as\n** either a UTF-8 or UTF-16 string.  ^The _database_ routines return\n** the database name, the _table_ routines return the table name, and\n** the origin_ routines return the column name.\n** ^The returned string is valid until the [prepared statement] is destroyed\n** using [sqlite3_finalize()] or until the statement is automatically\n** reprepared by the first call to [sqlite3_step()] for a particular run\n** or until the same information is requested\n** again in a different encoding.\n**\n** ^The names returned are the original un-aliased names of the\n** database, table, and column.\n**\n** ^The first argument to these interfaces is a [prepared statement].\n** ^These functions return information about the Nth result column returned by\n** the statement, where N is the second function argument.\n** ^The left-most column is column 0 for these routines.\n**\n** ^If the Nth column returned by the statement is an expression or\n** subquery and is not a column value, then all of these functions return\n** NULL.  ^These routine might also return NULL if a memory allocation error\n** occurs.  ^Otherwise, they return the name of the attached database, table,\n** or column that query result column was extracted from.\n**\n** ^As with all other SQLite APIs, those whose names end with \"16\" return\n** UTF-16 encoded strings and the other functions return UTF-8.\n**\n** ^These APIs are only available if the library was compiled with the\n** [SQLITE_ENABLE_COLUMN_METADATA] C-preprocessor symbol.\n**\n** If two or more threads call one or more of these routines against the same\n** prepared statement and column at the same time then the results are\n** undefined.\n**\n** If two or more threads call one or more\n** [sqlite3_column_database_name | column metadata interfaces]\n** for the same [prepared statement] and result column\n** at the same time then the results are undefined.\n*/\nSQLITE_API const char *sqlite3_column_database_name(sqlite3_stmt*,int);\nSQLITE_API const void *sqlite3_column_database_name16(sqlite3_stmt*,int);\nSQLITE_API const char *sqlite3_column_table_name(sqlite3_stmt*,int);\nSQLITE_API const void *sqlite3_column_table_name16(sqlite3_stmt*,int);\nSQLITE_API const char *sqlite3_column_origin_name(sqlite3_stmt*,int);\nSQLITE_API const void *sqlite3_column_origin_name16(sqlite3_stmt*,int);\n\n/*\n** CAPI3REF: Declared Datatype Of A Query Result\n**\n** ^(The first parameter is a [prepared statement].\n** If this statement is a [SELECT] statement and the Nth column of the\n** returned result set of that [SELECT] is a table column (not an\n** expression or subquery) then the declared type of the table\n** column is returned.)^  ^If the Nth column of the result set is an\n** expression or subquery, then a NULL pointer is returned.\n** ^The returned string is always UTF-8 encoded.\n**\n** ^(For example, given the database schema:\n**\n** CREATE TABLE t1(c1 VARIANT);\n**\n** and the following statement to be compiled:\n**\n** SELECT c1 + 1, c1 FROM t1;\n**\n** this routine would return the string \"VARIANT\" for the second result\n** column (i==1), and a NULL pointer for the first result column (i==0).)^\n**\n** ^SQLite uses dynamic run-time typing.  ^So just because a column\n** is declared to contain a particular type does not mean that the\n** data stored in that column is of the declared type.  SQLite is\n** strongly typed, but the typing is dynamic not static.  ^Type\n** is associated with individual values, not with the containers\n** used to hold those values.\n*/\nSQLITE_API const char *sqlite3_column_decltype(sqlite3_stmt*,int);\nSQLITE_API const void *sqlite3_column_decltype16(sqlite3_stmt*,int);\n\n/*\n** CAPI3REF: Evaluate An SQL Statement\n**\n** After a [prepared statement] has been prepared using either\n** [sqlite3_prepare_v2()] or [sqlite3_prepare16_v2()] or one of the legacy\n** interfaces [sqlite3_prepare()] or [sqlite3_prepare16()], this function\n** must be called one or more times to evaluate the statement.\n**\n** The details of the behavior of the sqlite3_step() interface depend\n** on whether the statement was prepared using the newer \"v2\" interface\n** [sqlite3_prepare_v2()] and [sqlite3_prepare16_v2()] or the older legacy\n** interface [sqlite3_prepare()] and [sqlite3_prepare16()].  The use of the\n** new \"v2\" interface is recommended for new applications but the legacy\n** interface will continue to be supported.\n**\n** ^In the legacy interface, the return value will be either [SQLITE_BUSY],\n** [SQLITE_DONE], [SQLITE_ROW], [SQLITE_ERROR], or [SQLITE_MISUSE].\n** ^With the \"v2\" interface, any of the other [result codes] or\n** [extended result codes] might be returned as well.\n**\n** ^[SQLITE_BUSY] means that the database engine was unable to acquire the\n** database locks it needs to do its job.  ^If the statement is a [COMMIT]\n** or occurs outside of an explicit transaction, then you can retry the\n** statement.  If the statement is not a [COMMIT] and occurs within an\n** explicit transaction then you should rollback the transaction before\n** continuing.\n**\n** ^[SQLITE_DONE] means that the statement has finished executing\n** successfully.  sqlite3_step() should not be called again on this virtual\n** machine without first calling [sqlite3_reset()] to reset the virtual\n** machine back to its initial state.\n**\n** ^If the SQL statement being executed returns any data, then [SQLITE_ROW]\n** is returned each time a new row of data is ready for processing by the\n** caller. The values may be accessed using the [column access functions].\n** sqlite3_step() is called again to retrieve the next row of data.\n**\n** ^[SQLITE_ERROR] means that a run-time error (such as a constraint\n** violation) has occurred.  sqlite3_step() should not be called again on\n** the VM. More information may be found by calling [sqlite3_errmsg()].\n** ^With the legacy interface, a more specific error code (for example,\n** [SQLITE_INTERRUPT], [SQLITE_SCHEMA], [SQLITE_CORRUPT], and so forth)\n** can be obtained by calling [sqlite3_reset()] on the\n** [prepared statement].  ^In the \"v2\" interface,\n** the more specific error code is returned directly by sqlite3_step().\n**\n** [SQLITE_MISUSE] means that the this routine was called inappropriately.\n** Perhaps it was called on a [prepared statement] that has\n** already been [sqlite3_finalize | finalized] or on one that had\n** previously returned [SQLITE_ERROR] or [SQLITE_DONE].  Or it could\n** be the case that the same database connection is being used by two or\n** more threads at the same moment in time.\n**\n** For all versions of SQLite up to and including 3.6.23.1, a call to\n** [sqlite3_reset()] was required after sqlite3_step() returned anything\n** other than [SQLITE_ROW] before any subsequent invocation of\n** sqlite3_step().  Failure to reset the prepared statement using \n** [sqlite3_reset()] would result in an [SQLITE_MISUSE] return from\n** sqlite3_step().  But after version 3.6.23.1, sqlite3_step() began\n** calling [sqlite3_reset()] automatically in this circumstance rather\n** than returning [SQLITE_MISUSE].  This is not considered a compatibility\n** break because any application that ever receives an SQLITE_MISUSE error\n** is broken by definition.  The [SQLITE_OMIT_AUTORESET] compile-time option\n** can be used to restore the legacy behavior.\n**\n** <b>Goofy Interface Alert:</b> In the legacy interface, the sqlite3_step()\n** API always returns a generic error code, [SQLITE_ERROR], following any\n** error other than [SQLITE_BUSY] and [SQLITE_MISUSE].  You must call\n** [sqlite3_reset()] or [sqlite3_finalize()] in order to find one of the\n** specific [error codes] that better describes the error.\n** We admit that this is a goofy design.  The problem has been fixed\n** with the \"v2\" interface.  If you prepare all of your SQL statements\n** using either [sqlite3_prepare_v2()] or [sqlite3_prepare16_v2()] instead\n** of the legacy [sqlite3_prepare()] and [sqlite3_prepare16()] interfaces,\n** then the more specific [error codes] are returned directly\n** by sqlite3_step().  The use of the \"v2\" interface is recommended.\n*/\nSQLITE_API int sqlite3_step(sqlite3_stmt*);\n\n/*\n** CAPI3REF: Number of columns in a result set\n**\n** ^The sqlite3_data_count(P) interface returns the number of columns in the\n** current row of the result set of [prepared statement] P.\n** ^If prepared statement P does not have results ready to return\n** (via calls to the [sqlite3_column_int | sqlite3_column_*()] of\n** interfaces) then sqlite3_data_count(P) returns 0.\n** ^The sqlite3_data_count(P) routine also returns 0 if P is a NULL pointer.\n** ^The sqlite3_data_count(P) routine returns 0 if the previous call to\n** [sqlite3_step](P) returned [SQLITE_DONE].  ^The sqlite3_data_count(P)\n** will return non-zero if previous call to [sqlite3_step](P) returned\n** [SQLITE_ROW], except in the case of the [PRAGMA incremental_vacuum]\n** where it always returns zero since each step of that multi-step\n** pragma returns 0 columns of data.\n**\n** See also: [sqlite3_column_count()]\n*/\nSQLITE_API int sqlite3_data_count(sqlite3_stmt *pStmt);\n\n/*\n** CAPI3REF: Fundamental Datatypes\n** KEYWORDS: SQLITE_TEXT\n**\n** ^(Every value in SQLite has one of five fundamental datatypes:\n**\n** <ul>\n** <li> 64-bit signed integer\n** <li> 64-bit IEEE floating point number\n** <li> string\n** <li> BLOB\n** <li> NULL\n** </ul>)^\n**\n** These constants are codes for each of those types.\n**\n** Note that the SQLITE_TEXT constant was also used in SQLite version 2\n** for a completely different meaning.  Software that links against both\n** SQLite version 2 and SQLite version 3 should use SQLITE3_TEXT, not\n** SQLITE_TEXT.\n*/\n#define SQLITE_INTEGER  1\n#define SQLITE_FLOAT    2\n#define SQLITE_BLOB     4\n#define SQLITE_NULL     5\n#ifdef SQLITE_TEXT\n# undef SQLITE_TEXT\n#else\n# define SQLITE_TEXT     3\n#endif\n#define SQLITE3_TEXT     3\n\n/*\n** CAPI3REF: Result Values From A Query\n** KEYWORDS: {column access functions}\n**\n** These routines form the \"result set\" interface.\n**\n** ^These routines return information about a single column of the current\n** result row of a query.  ^In every case the first argument is a pointer\n** to the [prepared statement] that is being evaluated (the [sqlite3_stmt*]\n** that was returned from [sqlite3_prepare_v2()] or one of its variants)\n** and the second argument is the index of the column for which information\n** should be returned. ^The leftmost column of the result set has the index 0.\n** ^The number of columns in the result can be determined using\n** [sqlite3_column_count()].\n**\n** If the SQL statement does not currently point to a valid row, or if the\n** column index is out of range, the result is undefined.\n** These routines may only be called when the most recent call to\n** [sqlite3_step()] has returned [SQLITE_ROW] and neither\n** [sqlite3_reset()] nor [sqlite3_finalize()] have been called subsequently.\n** If any of these routines are called after [sqlite3_reset()] or\n** [sqlite3_finalize()] or after [sqlite3_step()] has returned\n** something other than [SQLITE_ROW], the results are undefined.\n** If [sqlite3_step()] or [sqlite3_reset()] or [sqlite3_finalize()]\n** are called from a different thread while any of these routines\n** are pending, then the results are undefined.\n**\n** ^The sqlite3_column_type() routine returns the\n** [SQLITE_INTEGER | datatype code] for the initial data type\n** of the result column.  ^The returned value is one of [SQLITE_INTEGER],\n** [SQLITE_FLOAT], [SQLITE_TEXT], [SQLITE_BLOB], or [SQLITE_NULL].  The value\n** returned by sqlite3_column_type() is only meaningful if no type\n** conversions have occurred as described below.  After a type conversion,\n** the value returned by sqlite3_column_type() is undefined.  Future\n** versions of SQLite may change the behavior of sqlite3_column_type()\n** following a type conversion.\n**\n** ^If the result is a BLOB or UTF-8 string then the sqlite3_column_bytes()\n** routine returns the number of bytes in that BLOB or string.\n** ^If the result is a UTF-16 string, then sqlite3_column_bytes() converts\n** the string to UTF-8 and then returns the number of bytes.\n** ^If the result is a numeric value then sqlite3_column_bytes() uses\n** [sqlite3_snprintf()] to convert that value to a UTF-8 string and returns\n** the number of bytes in that string.\n** ^If the result is NULL, then sqlite3_column_bytes() returns zero.\n**\n** ^If the result is a BLOB or UTF-16 string then the sqlite3_column_bytes16()\n** routine returns the number of bytes in that BLOB or string.\n** ^If the result is a UTF-8 string, then sqlite3_column_bytes16() converts\n** the string to UTF-16 and then returns the number of bytes.\n** ^If the result is a numeric value then sqlite3_column_bytes16() uses\n** [sqlite3_snprintf()] to convert that value to a UTF-16 string and returns\n** the number of bytes in that string.\n** ^If the result is NULL, then sqlite3_column_bytes16() returns zero.\n**\n** ^The values returned by [sqlite3_column_bytes()] and \n** [sqlite3_column_bytes16()] do not include the zero terminators at the end\n** of the string.  ^For clarity: the values returned by\n** [sqlite3_column_bytes()] and [sqlite3_column_bytes16()] are the number of\n** bytes in the string, not the number of characters.\n**\n** ^Strings returned by sqlite3_column_text() and sqlite3_column_text16(),\n** even empty strings, are always zero-terminated.  ^The return\n** value from sqlite3_column_blob() for a zero-length BLOB is a NULL pointer.\n**\n** ^The object returned by [sqlite3_column_value()] is an\n** [unprotected sqlite3_value] object.  An unprotected sqlite3_value object\n** may only be used with [sqlite3_bind_value()] and [sqlite3_result_value()].\n** If the [unprotected sqlite3_value] object returned by\n** [sqlite3_column_value()] is used in any other way, including calls\n** to routines like [sqlite3_value_int()], [sqlite3_value_text()],\n** or [sqlite3_value_bytes()], then the behavior is undefined.\n**\n** These routines attempt to convert the value where appropriate.  ^For\n** example, if the internal representation is FLOAT and a text result\n** is requested, [sqlite3_snprintf()] is used internally to perform the\n** conversion automatically.  ^(The following table details the conversions\n** that are applied:\n**\n** <blockquote>\n** <table border=\"1\">\n** <tr><th> Internal<br>Type <th> Requested<br>Type <th>  Conversion\n**\n** <tr><td>  NULL    <td> INTEGER   <td> Result is 0\n** <tr><td>  NULL    <td>  FLOAT    <td> Result is 0.0\n** <tr><td>  NULL    <td>   TEXT    <td> Result is NULL pointer\n** <tr><td>  NULL    <td>   BLOB    <td> Result is NULL pointer\n** <tr><td> INTEGER  <td>  FLOAT    <td> Convert from integer to float\n** <tr><td> INTEGER  <td>   TEXT    <td> ASCII rendering of the integer\n** <tr><td> INTEGER  <td>   BLOB    <td> Same as INTEGER->TEXT\n** <tr><td>  FLOAT   <td> INTEGER   <td> Convert from float to integer\n** <tr><td>  FLOAT   <td>   TEXT    <td> ASCII rendering of the float\n** <tr><td>  FLOAT   <td>   BLOB    <td> Same as FLOAT->TEXT\n** <tr><td>  TEXT    <td> INTEGER   <td> Use atoi()\n** <tr><td>  TEXT    <td>  FLOAT    <td> Use atof()\n** <tr><td>  TEXT    <td>   BLOB    <td> No change\n** <tr><td>  BLOB    <td> INTEGER   <td> Convert to TEXT then use atoi()\n** <tr><td>  BLOB    <td>  FLOAT    <td> Convert to TEXT then use atof()\n** <tr><td>  BLOB    <td>   TEXT    <td> Add a zero terminator if needed\n** </table>\n** </blockquote>)^\n**\n** The table above makes reference to standard C library functions atoi()\n** and atof().  SQLite does not really use these functions.  It has its\n** own equivalent internal routines.  The atoi() and atof() names are\n** used in the table for brevity and because they are familiar to most\n** C programmers.\n**\n** Note that when type conversions occur, pointers returned by prior\n** calls to sqlite3_column_blob(), sqlite3_column_text(), and/or\n** sqlite3_column_text16() may be invalidated.\n** Type conversions and pointer invalidations might occur\n** in the following cases:\n**\n** <ul>\n** <li> The initial content is a BLOB and sqlite3_column_text() or\n**      sqlite3_column_text16() is called.  A zero-terminator might\n**      need to be added to the string.</li>\n** <li> The initial content is UTF-8 text and sqlite3_column_bytes16() or\n**      sqlite3_column_text16() is called.  The content must be converted\n**      to UTF-16.</li>\n** <li> The initial content is UTF-16 text and sqlite3_column_bytes() or\n**      sqlite3_column_text() is called.  The content must be converted\n**      to UTF-8.</li>\n** </ul>\n**\n** ^Conversions between UTF-16be and UTF-16le are always done in place and do\n** not invalidate a prior pointer, though of course the content of the buffer\n** that the prior pointer references will have been modified.  Other kinds\n** of conversion are done in place when it is possible, but sometimes they\n** are not possible and in those cases prior pointers are invalidated.\n**\n** The safest and easiest to remember policy is to invoke these routines\n** in one of the following ways:\n**\n** <ul>\n**  <li>sqlite3_column_text() followed by sqlite3_column_bytes()</li>\n**  <li>sqlite3_column_blob() followed by sqlite3_column_bytes()</li>\n**  <li>sqlite3_column_text16() followed by sqlite3_column_bytes16()</li>\n** </ul>\n**\n** In other words, you should call sqlite3_column_text(),\n** sqlite3_column_blob(), or sqlite3_column_text16() first to force the result\n** into the desired format, then invoke sqlite3_column_bytes() or\n** sqlite3_column_bytes16() to find the size of the result.  Do not mix calls\n** to sqlite3_column_text() or sqlite3_column_blob() with calls to\n** sqlite3_column_bytes16(), and do not mix calls to sqlite3_column_text16()\n** with calls to sqlite3_column_bytes().\n**\n** ^The pointers returned are valid until a type conversion occurs as\n** described above, or until [sqlite3_step()] or [sqlite3_reset()] or\n** [sqlite3_finalize()] is called.  ^The memory space used to hold strings\n** and BLOBs is freed automatically.  Do <b>not</b> pass the pointers returned\n** [sqlite3_column_blob()], [sqlite3_column_text()], etc. into\n** [sqlite3_free()].\n**\n** ^(If a memory allocation error occurs during the evaluation of any\n** of these routines, a default value is returned.  The default value\n** is either the integer 0, the floating point number 0.0, or a NULL\n** pointer.  Subsequent calls to [sqlite3_errcode()] will return\n** [SQLITE_NOMEM].)^\n*/\nSQLITE_API const void *sqlite3_column_blob(sqlite3_stmt*, int iCol);\nSQLITE_API int sqlite3_column_bytes(sqlite3_stmt*, int iCol);\nSQLITE_API int sqlite3_column_bytes16(sqlite3_stmt*, int iCol);\nSQLITE_API double sqlite3_column_double(sqlite3_stmt*, int iCol);\nSQLITE_API int sqlite3_column_int(sqlite3_stmt*, int iCol);\nSQLITE_API sqlite3_int64 sqlite3_column_int64(sqlite3_stmt*, int iCol);\nSQLITE_API const unsigned char *sqlite3_column_text(sqlite3_stmt*, int iCol);\nSQLITE_API const void *sqlite3_column_text16(sqlite3_stmt*, int iCol);\nSQLITE_API int sqlite3_column_type(sqlite3_stmt*, int iCol);\nSQLITE_API sqlite3_value *sqlite3_column_value(sqlite3_stmt*, int iCol);\n\n/*\n** CAPI3REF: Destroy A Prepared Statement Object\n**\n** ^The sqlite3_finalize() function is called to delete a [prepared statement].\n** ^If the most recent evaluation of the statement encountered no errors\n** or if the statement is never been evaluated, then sqlite3_finalize() returns\n** SQLITE_OK.  ^If the most recent evaluation of statement S failed, then\n** sqlite3_finalize(S) returns the appropriate [error code] or\n** [extended error code].\n**\n** ^The sqlite3_finalize(S) routine can be called at any point during\n** the life cycle of [prepared statement] S:\n** before statement S is ever evaluated, after\n** one or more calls to [sqlite3_reset()], or after any call\n** to [sqlite3_step()] regardless of whether or not the statement has\n** completed execution.\n**\n** ^Invoking sqlite3_finalize() on a NULL pointer is a harmless no-op.\n**\n** The application must finalize every [prepared statement] in order to avoid\n** resource leaks.  It is a grievous error for the application to try to use\n** a prepared statement after it has been finalized.  Any use of a prepared\n** statement after it has been finalized can result in undefined and\n** undesirable behavior such as segfaults and heap corruption.\n*/\nSQLITE_API int sqlite3_finalize(sqlite3_stmt *pStmt);\n\n/*\n** CAPI3REF: Reset A Prepared Statement Object\n**\n** The sqlite3_reset() function is called to reset a [prepared statement]\n** object back to its initial state, ready to be re-executed.\n** ^Any SQL statement variables that had values bound to them using\n** the [sqlite3_bind_blob | sqlite3_bind_*() API] retain their values.\n** Use [sqlite3_clear_bindings()] to reset the bindings.\n**\n** ^The [sqlite3_reset(S)] interface resets the [prepared statement] S\n** back to the beginning of its program.\n**\n** ^If the most recent call to [sqlite3_step(S)] for the\n** [prepared statement] S returned [SQLITE_ROW] or [SQLITE_DONE],\n** or if [sqlite3_step(S)] has never before been called on S,\n** then [sqlite3_reset(S)] returns [SQLITE_OK].\n**\n** ^If the most recent call to [sqlite3_step(S)] for the\n** [prepared statement] S indicated an error, then\n** [sqlite3_reset(S)] returns an appropriate [error code].\n**\n** ^The [sqlite3_reset(S)] interface does not change the values\n** of any [sqlite3_bind_blob|bindings] on the [prepared statement] S.\n*/\nSQLITE_API int sqlite3_reset(sqlite3_stmt *pStmt);\n\n/*\n** CAPI3REF: Create Or Redefine SQL Functions\n** KEYWORDS: {function creation routines}\n** KEYWORDS: {application-defined SQL function}\n** KEYWORDS: {application-defined SQL functions}\n**\n** ^These functions (collectively known as \"function creation routines\")\n** are used to add SQL functions or aggregates or to redefine the behavior\n** of existing SQL functions or aggregates.  The only differences between\n** these routines are the text encoding expected for\n** the second parameter (the name of the function being created)\n** and the presence or absence of a destructor callback for\n** the application data pointer.\n**\n** ^The first parameter is the [database connection] to which the SQL\n** function is to be added.  ^If an application uses more than one database\n** connection then application-defined SQL functions must be added\n** to each database connection separately.\n**\n** ^The second parameter is the name of the SQL function to be created or\n** redefined.  ^The length of the name is limited to 255 bytes in a UTF-8\n** representation, exclusive of the zero-terminator.  ^Note that the name\n** length limit is in UTF-8 bytes, not characters nor UTF-16 bytes.  \n** ^Any attempt to create a function with a longer name\n** will result in [SQLITE_MISUSE] being returned.\n**\n** ^The third parameter (nArg)\n** is the number of arguments that the SQL function or\n** aggregate takes. ^If this parameter is -1, then the SQL function or\n** aggregate may take any number of arguments between 0 and the limit\n** set by [sqlite3_limit]([SQLITE_LIMIT_FUNCTION_ARG]).  If the third\n** parameter is less than -1 or greater than 127 then the behavior is\n** undefined.\n**\n** ^The fourth parameter, eTextRep, specifies what\n** [SQLITE_UTF8 | text encoding] this SQL function prefers for\n** its parameters.  Every SQL function implementation must be able to work\n** with UTF-8, UTF-16le, or UTF-16be.  But some implementations may be\n** more efficient with one encoding than another.  ^An application may\n** invoke sqlite3_create_function() or sqlite3_create_function16() multiple\n** times with the same function but with different values of eTextRep.\n** ^When multiple implementations of the same function are available, SQLite\n** will pick the one that involves the least amount of data conversion.\n** If there is only a single implementation which does not care what text\n** encoding is used, then the fourth argument should be [SQLITE_ANY].\n**\n** ^(The fifth parameter is an arbitrary pointer.  The implementation of the\n** function can gain access to this pointer using [sqlite3_user_data()].)^\n**\n** ^The sixth, seventh and eighth parameters, xFunc, xStep and xFinal, are\n** pointers to C-language functions that implement the SQL function or\n** aggregate. ^A scalar SQL function requires an implementation of the xFunc\n** callback only; NULL pointers must be passed as the xStep and xFinal\n** parameters. ^An aggregate SQL function requires an implementation of xStep\n** and xFinal and NULL pointer must be passed for xFunc. ^To delete an existing\n** SQL function or aggregate, pass NULL pointers for all three function\n** callbacks.\n**\n** ^(If the ninth parameter to sqlite3_create_function_v2() is not NULL,\n** then it is destructor for the application data pointer. \n** The destructor is invoked when the function is deleted, either by being\n** overloaded or when the database connection closes.)^\n** ^The destructor is also invoked if the call to\n** sqlite3_create_function_v2() fails.\n** ^When the destructor callback of the tenth parameter is invoked, it\n** is passed a single argument which is a copy of the application data \n** pointer which was the fifth parameter to sqlite3_create_function_v2().\n**\n** ^It is permitted to register multiple implementations of the same\n** functions with the same name but with either differing numbers of\n** arguments or differing preferred text encodings.  ^SQLite will use\n** the implementation that most closely matches the way in which the\n** SQL function is used.  ^A function implementation with a non-negative\n** nArg parameter is a better match than a function implementation with\n** a negative nArg.  ^A function where the preferred text encoding\n** matches the database encoding is a better\n** match than a function where the encoding is different.  \n** ^A function where the encoding difference is between UTF16le and UTF16be\n** is a closer match than a function where the encoding difference is\n** between UTF8 and UTF16.\n**\n** ^Built-in functions may be overloaded by new application-defined functions.\n**\n** ^An application-defined function is permitted to call other\n** SQLite interfaces.  However, such calls must not\n** close the database connection nor finalize or reset the prepared\n** statement in which the function is running.\n*/\nSQLITE_API int sqlite3_create_function(\n  sqlite3 *db,\n  const char *zFunctionName,\n  int nArg,\n  int eTextRep,\n  void *pApp,\n  void (*xFunc)(sqlite3_context*,int,sqlite3_value**),\n  void (*xStep)(sqlite3_context*,int,sqlite3_value**),\n  void (*xFinal)(sqlite3_context*)\n);\nSQLITE_API int sqlite3_create_function16(\n  sqlite3 *db,\n  const void *zFunctionName,\n  int nArg,\n  int eTextRep,\n  void *pApp,\n  void (*xFunc)(sqlite3_context*,int,sqlite3_value**),\n  void (*xStep)(sqlite3_context*,int,sqlite3_value**),\n  void (*xFinal)(sqlite3_context*)\n);\nSQLITE_API int sqlite3_create_function_v2(\n  sqlite3 *db,\n  const char *zFunctionName,\n  int nArg,\n  int eTextRep,\n  void *pApp,\n  void (*xFunc)(sqlite3_context*,int,sqlite3_value**),\n  void (*xStep)(sqlite3_context*,int,sqlite3_value**),\n  void (*xFinal)(sqlite3_context*),\n  void(*xDestroy)(void*)\n);\n\n/*\n** CAPI3REF: Text Encodings\n**\n** These constant define integer codes that represent the various\n** text encodings supported by SQLite.\n*/\n#define SQLITE_UTF8           1\n#define SQLITE_UTF16LE        2\n#define SQLITE_UTF16BE        3\n#define SQLITE_UTF16          4    /* Use native byte order */\n#define SQLITE_ANY            5    /* sqlite3_create_function only */\n#define SQLITE_UTF16_ALIGNED  8    /* sqlite3_create_collation only */\n\n/*\n** CAPI3REF: Deprecated Functions\n** DEPRECATED\n**\n** These functions are [deprecated].  In order to maintain\n** backwards compatibility with older code, these functions continue \n** to be supported.  However, new applications should avoid\n** the use of these functions.  To help encourage people to avoid\n** using these functions, we are not going to tell you what they do.\n*/\n#ifndef SQLITE_OMIT_DEPRECATED\nSQLITE_API SQLITE_DEPRECATED int sqlite3_aggregate_count(sqlite3_context*);\nSQLITE_API SQLITE_DEPRECATED int sqlite3_expired(sqlite3_stmt*);\nSQLITE_API SQLITE_DEPRECATED int sqlite3_transfer_bindings(sqlite3_stmt*, sqlite3_stmt*);\nSQLITE_API SQLITE_DEPRECATED int sqlite3_global_recover(void);\nSQLITE_API SQLITE_DEPRECATED void sqlite3_thread_cleanup(void);\nSQLITE_API SQLITE_DEPRECATED int sqlite3_memory_alarm(void(*)(void*,sqlite3_int64,int),void*,sqlite3_int64);\n#endif\n\n/*\n** CAPI3REF: Obtaining SQL Function Parameter Values\n**\n** The C-language implementation of SQL functions and aggregates uses\n** this set of interface routines to access the parameter values on\n** the function or aggregate.\n**\n** The xFunc (for scalar functions) or xStep (for aggregates) parameters\n** to [sqlite3_create_function()] and [sqlite3_create_function16()]\n** define callbacks that implement the SQL functions and aggregates.\n** The 3rd parameter to these callbacks is an array of pointers to\n** [protected sqlite3_value] objects.  There is one [sqlite3_value] object for\n** each parameter to the SQL function.  These routines are used to\n** extract values from the [sqlite3_value] objects.\n**\n** These routines work only with [protected sqlite3_value] objects.\n** Any attempt to use these routines on an [unprotected sqlite3_value]\n** object results in undefined behavior.\n**\n** ^These routines work just like the corresponding [column access functions]\n** except that  these routines take a single [protected sqlite3_value] object\n** pointer instead of a [sqlite3_stmt*] pointer and an integer column number.\n**\n** ^The sqlite3_value_text16() interface extracts a UTF-16 string\n** in the native byte-order of the host machine.  ^The\n** sqlite3_value_text16be() and sqlite3_value_text16le() interfaces\n** extract UTF-16 strings as big-endian and little-endian respectively.\n**\n** ^(The sqlite3_value_numeric_type() interface attempts to apply\n** numeric affinity to the value.  This means that an attempt is\n** made to convert the value to an integer or floating point.  If\n** such a conversion is possible without loss of information (in other\n** words, if the value is a string that looks like a number)\n** then the conversion is performed.  Otherwise no conversion occurs.\n** The [SQLITE_INTEGER | datatype] after conversion is returned.)^\n**\n** Please pay particular attention to the fact that the pointer returned\n** from [sqlite3_value_blob()], [sqlite3_value_text()], or\n** [sqlite3_value_text16()] can be invalidated by a subsequent call to\n** [sqlite3_value_bytes()], [sqlite3_value_bytes16()], [sqlite3_value_text()],\n** or [sqlite3_value_text16()].\n**\n** These routines must be called from the same thread as\n** the SQL function that supplied the [sqlite3_value*] parameters.\n*/\nSQLITE_API const void *sqlite3_value_blob(sqlite3_value*);\nSQLITE_API int sqlite3_value_bytes(sqlite3_value*);\nSQLITE_API int sqlite3_value_bytes16(sqlite3_value*);\nSQLITE_API double sqlite3_value_double(sqlite3_value*);\nSQLITE_API int sqlite3_value_int(sqlite3_value*);\nSQLITE_API sqlite3_int64 sqlite3_value_int64(sqlite3_value*);\nSQLITE_API const unsigned char *sqlite3_value_text(sqlite3_value*);\nSQLITE_API const void *sqlite3_value_text16(sqlite3_value*);\nSQLITE_API const void *sqlite3_value_text16le(sqlite3_value*);\nSQLITE_API const void *sqlite3_value_text16be(sqlite3_value*);\nSQLITE_API int sqlite3_value_type(sqlite3_value*);\nSQLITE_API int sqlite3_value_numeric_type(sqlite3_value*);\n\n/*\n** CAPI3REF: Obtain Aggregate Function Context\n**\n** Implementations of aggregate SQL functions use this\n** routine to allocate memory for storing their state.\n**\n** ^The first time the sqlite3_aggregate_context(C,N) routine is called \n** for a particular aggregate function, SQLite\n** allocates N of memory, zeroes out that memory, and returns a pointer\n** to the new memory. ^On second and subsequent calls to\n** sqlite3_aggregate_context() for the same aggregate function instance,\n** the same buffer is returned.  Sqlite3_aggregate_context() is normally\n** called once for each invocation of the xStep callback and then one\n** last time when the xFinal callback is invoked.  ^(When no rows match\n** an aggregate query, the xStep() callback of the aggregate function\n** implementation is never called and xFinal() is called exactly once.\n** In those cases, sqlite3_aggregate_context() might be called for the\n** first time from within xFinal().)^\n**\n** ^The sqlite3_aggregate_context(C,N) routine returns a NULL pointer if N is\n** less than or equal to zero or if a memory allocate error occurs.\n**\n** ^(The amount of space allocated by sqlite3_aggregate_context(C,N) is\n** determined by the N parameter on first successful call.  Changing the\n** value of N in subsequent call to sqlite3_aggregate_context() within\n** the same aggregate function instance will not resize the memory\n** allocation.)^\n**\n** ^SQLite automatically frees the memory allocated by \n** sqlite3_aggregate_context() when the aggregate query concludes.\n**\n** The first parameter must be a copy of the\n** [sqlite3_context | SQL function context] that is the first parameter\n** to the xStep or xFinal callback routine that implements the aggregate\n** function.\n**\n** This routine must be called from the same thread in which\n** the aggregate SQL function is running.\n*/\nSQLITE_API void *sqlite3_aggregate_context(sqlite3_context*, int nBytes);\n\n/*\n** CAPI3REF: User Data For Functions\n**\n** ^The sqlite3_user_data() interface returns a copy of\n** the pointer that was the pUserData parameter (the 5th parameter)\n** of the [sqlite3_create_function()]\n** and [sqlite3_create_function16()] routines that originally\n** registered the application defined function.\n**\n** This routine must be called from the same thread in which\n** the application-defined function is running.\n*/\nSQLITE_API void *sqlite3_user_data(sqlite3_context*);\n\n/*\n** CAPI3REF: Database Connection For Functions\n**\n** ^The sqlite3_context_db_handle() interface returns a copy of\n** the pointer to the [database connection] (the 1st parameter)\n** of the [sqlite3_create_function()]\n** and [sqlite3_create_function16()] routines that originally\n** registered the application defined function.\n*/\nSQLITE_API sqlite3 *sqlite3_context_db_handle(sqlite3_context*);\n\n/*\n** CAPI3REF: Function Auxiliary Data\n**\n** The following two functions may be used by scalar SQL functions to\n** associate metadata with argument values. If the same value is passed to\n** multiple invocations of the same SQL function during query execution, under\n** some circumstances the associated metadata may be preserved. This may\n** be used, for example, to add a regular-expression matching scalar\n** function. The compiled version of the regular expression is stored as\n** metadata associated with the SQL value passed as the regular expression\n** pattern.  The compiled regular expression can be reused on multiple\n** invocations of the same function so that the original pattern string\n** does not need to be recompiled on each invocation.\n**\n** ^The sqlite3_get_auxdata() interface returns a pointer to the metadata\n** associated by the sqlite3_set_auxdata() function with the Nth argument\n** value to the application-defined function. ^If no metadata has been ever\n** been set for the Nth argument of the function, or if the corresponding\n** function parameter has changed since the meta-data was set,\n** then sqlite3_get_auxdata() returns a NULL pointer.\n**\n** ^The sqlite3_set_auxdata() interface saves the metadata\n** pointed to by its 3rd parameter as the metadata for the N-th\n** argument of the application-defined function.  Subsequent\n** calls to sqlite3_get_auxdata() might return this data, if it has\n** not been destroyed.\n** ^If it is not NULL, SQLite will invoke the destructor\n** function given by the 4th parameter to sqlite3_set_auxdata() on\n** the metadata when the corresponding function parameter changes\n** or when the SQL statement completes, whichever comes first.\n**\n** SQLite is free to call the destructor and drop metadata on any\n** parameter of any function at any time.  ^The only guarantee is that\n** the destructor will be called before the metadata is dropped.\n**\n** ^(In practice, metadata is preserved between function calls for\n** expressions that are constant at compile time. This includes literal\n** values and [parameters].)^\n**\n** These routines must be called from the same thread in which\n** the SQL function is running.\n*/\nSQLITE_API void *sqlite3_get_auxdata(sqlite3_context*, int N);\nSQLITE_API void sqlite3_set_auxdata(sqlite3_context*, int N, void*, void (*)(void*));\n\n\n/*\n** CAPI3REF: Constants Defining Special Destructor Behavior\n**\n** These are special values for the destructor that is passed in as the\n** final argument to routines like [sqlite3_result_blob()].  ^If the destructor\n** argument is SQLITE_STATIC, it means that the content pointer is constant\n** and will never change.  It does not need to be destroyed.  ^The\n** SQLITE_TRANSIENT value means that the content will likely change in\n** the near future and that SQLite should make its own private copy of\n** the content before returning.\n**\n** The typedef is necessary to work around problems in certain\n** C++ compilers.  See ticket #2191.\n*/\ntypedef void (*sqlite3_destructor_type)(void*);\n#define SQLITE_STATIC      ((sqlite3_destructor_type)0)\n#define SQLITE_TRANSIENT   ((sqlite3_destructor_type)-1)\n\n/*\n** CAPI3REF: Setting The Result Of An SQL Function\n**\n** These routines are used by the xFunc or xFinal callbacks that\n** implement SQL functions and aggregates.  See\n** [sqlite3_create_function()] and [sqlite3_create_function16()]\n** for additional information.\n**\n** These functions work very much like the [parameter binding] family of\n** functions used to bind values to host parameters in prepared statements.\n** Refer to the [SQL parameter] documentation for additional information.\n**\n** ^The sqlite3_result_blob() interface sets the result from\n** an application-defined function to be the BLOB whose content is pointed\n** to by the second parameter and which is N bytes long where N is the\n** third parameter.\n**\n** ^The sqlite3_result_zeroblob() interfaces set the result of\n** the application-defined function to be a BLOB containing all zero\n** bytes and N bytes in size, where N is the value of the 2nd parameter.\n**\n** ^The sqlite3_result_double() interface sets the result from\n** an application-defined function to be a floating point value specified\n** by its 2nd argument.\n**\n** ^The sqlite3_result_error() and sqlite3_result_error16() functions\n** cause the implemented SQL function to throw an exception.\n** ^SQLite uses the string pointed to by the\n** 2nd parameter of sqlite3_result_error() or sqlite3_result_error16()\n** as the text of an error message.  ^SQLite interprets the error\n** message string from sqlite3_result_error() as UTF-8. ^SQLite\n** interprets the string from sqlite3_result_error16() as UTF-16 in native\n** byte order.  ^If the third parameter to sqlite3_result_error()\n** or sqlite3_result_error16() is negative then SQLite takes as the error\n** message all text up through the first zero character.\n** ^If the third parameter to sqlite3_result_error() or\n** sqlite3_result_error16() is non-negative then SQLite takes that many\n** bytes (not characters) from the 2nd parameter as the error message.\n** ^The sqlite3_result_error() and sqlite3_result_error16()\n** routines make a private copy of the error message text before\n** they return.  Hence, the calling function can deallocate or\n** modify the text after they return without harm.\n** ^The sqlite3_result_error_code() function changes the error code\n** returned by SQLite as a result of an error in a function.  ^By default,\n** the error code is SQLITE_ERROR.  ^A subsequent call to sqlite3_result_error()\n** or sqlite3_result_error16() resets the error code to SQLITE_ERROR.\n**\n** ^The sqlite3_result_error_toobig() interface causes SQLite to throw an\n** error indicating that a string or BLOB is too long to represent.\n**\n** ^The sqlite3_result_error_nomem() interface causes SQLite to throw an\n** error indicating that a memory allocation failed.\n**\n** ^The sqlite3_result_int() interface sets the return value\n** of the application-defined function to be the 32-bit signed integer\n** value given in the 2nd argument.\n** ^The sqlite3_result_int64() interface sets the return value\n** of the application-defined function to be the 64-bit signed integer\n** value given in the 2nd argument.\n**\n** ^The sqlite3_result_null() interface sets the return value\n** of the application-defined function to be NULL.\n**\n** ^The sqlite3_result_text(), sqlite3_result_text16(),\n** sqlite3_result_text16le(), and sqlite3_result_text16be() interfaces\n** set the return value of the application-defined function to be\n** a text string which is represented as UTF-8, UTF-16 native byte order,\n** UTF-16 little endian, or UTF-16 big endian, respectively.\n** ^SQLite takes the text result from the application from\n** the 2nd parameter of the sqlite3_result_text* interfaces.\n** ^If the 3rd parameter to the sqlite3_result_text* interfaces\n** is negative, then SQLite takes result text from the 2nd parameter\n** through the first zero character.\n** ^If the 3rd parameter to the sqlite3_result_text* interfaces\n** is non-negative, then as many bytes (not characters) of the text\n** pointed to by the 2nd parameter are taken as the application-defined\n** function result.  If the 3rd parameter is non-negative, then it\n** must be the byte offset into the string where the NUL terminator would\n** appear if the string where NUL terminated.  If any NUL characters occur\n** in the string at a byte offset that is less than the value of the 3rd\n** parameter, then the resulting string will contain embedded NULs and the\n** result of expressions operating on strings with embedded NULs is undefined.\n** ^If the 4th parameter to the sqlite3_result_text* interfaces\n** or sqlite3_result_blob is a non-NULL pointer, then SQLite calls that\n** function as the destructor on the text or BLOB result when it has\n** finished using that result.\n** ^If the 4th parameter to the sqlite3_result_text* interfaces or to\n** sqlite3_result_blob is the special constant SQLITE_STATIC, then SQLite\n** assumes that the text or BLOB result is in constant space and does not\n** copy the content of the parameter nor call a destructor on the content\n** when it has finished using that result.\n** ^If the 4th parameter to the sqlite3_result_text* interfaces\n** or sqlite3_result_blob is the special constant SQLITE_TRANSIENT\n** then SQLite makes a copy of the result into space obtained from\n** from [sqlite3_malloc()] before it returns.\n**\n** ^The sqlite3_result_value() interface sets the result of\n** the application-defined function to be a copy the\n** [unprotected sqlite3_value] object specified by the 2nd parameter.  ^The\n** sqlite3_result_value() interface makes a copy of the [sqlite3_value]\n** so that the [sqlite3_value] specified in the parameter may change or\n** be deallocated after sqlite3_result_value() returns without harm.\n** ^A [protected sqlite3_value] object may always be used where an\n** [unprotected sqlite3_value] object is required, so either\n** kind of [sqlite3_value] object can be used with this interface.\n**\n** If these routines are called from within the different thread\n** than the one containing the application-defined function that received\n** the [sqlite3_context] pointer, the results are undefined.\n*/\nSQLITE_API void sqlite3_result_blob(sqlite3_context*, const void*, int, void(*)(void*));\nSQLITE_API void sqlite3_result_double(sqlite3_context*, double);\nSQLITE_API void sqlite3_result_error(sqlite3_context*, const char*, int);\nSQLITE_API void sqlite3_result_error16(sqlite3_context*, const void*, int);\nSQLITE_API void sqlite3_result_error_toobig(sqlite3_context*);\nSQLITE_API void sqlite3_result_error_nomem(sqlite3_context*);\nSQLITE_API void sqlite3_result_error_code(sqlite3_context*, int);\nSQLITE_API void sqlite3_result_int(sqlite3_context*, int);\nSQLITE_API void sqlite3_result_int64(sqlite3_context*, sqlite3_int64);\nSQLITE_API void sqlite3_result_null(sqlite3_context*);\nSQLITE_API void sqlite3_result_text(sqlite3_context*, const char*, int, void(*)(void*));\nSQLITE_API void sqlite3_result_text16(sqlite3_context*, const void*, int, void(*)(void*));\nSQLITE_API void sqlite3_result_text16le(sqlite3_context*, const void*, int,void(*)(void*));\nSQLITE_API void sqlite3_result_text16be(sqlite3_context*, const void*, int,void(*)(void*));\nSQLITE_API void sqlite3_result_value(sqlite3_context*, sqlite3_value*);\nSQLITE_API void sqlite3_result_zeroblob(sqlite3_context*, int n);\n\n/*\n** CAPI3REF: Define New Collating Sequences\n**\n** ^These functions add, remove, or modify a [collation] associated\n** with the [database connection] specified as the first argument.\n**\n** ^The name of the collation is a UTF-8 string\n** for sqlite3_create_collation() and sqlite3_create_collation_v2()\n** and a UTF-16 string in native byte order for sqlite3_create_collation16().\n** ^Collation names that compare equal according to [sqlite3_strnicmp()] are\n** considered to be the same name.\n**\n** ^(The third argument (eTextRep) must be one of the constants:\n** <ul>\n** <li> [SQLITE_UTF8],\n** <li> [SQLITE_UTF16LE],\n** <li> [SQLITE_UTF16BE],\n** <li> [SQLITE_UTF16], or\n** <li> [SQLITE_UTF16_ALIGNED].\n** </ul>)^\n** ^The eTextRep argument determines the encoding of strings passed\n** to the collating function callback, xCallback.\n** ^The [SQLITE_UTF16] and [SQLITE_UTF16_ALIGNED] values for eTextRep\n** force strings to be UTF16 with native byte order.\n** ^The [SQLITE_UTF16_ALIGNED] value for eTextRep forces strings to begin\n** on an even byte address.\n**\n** ^The fourth argument, pArg, is an application data pointer that is passed\n** through as the first argument to the collating function callback.\n**\n** ^The fifth argument, xCallback, is a pointer to the collating function.\n** ^Multiple collating functions can be registered using the same name but\n** with different eTextRep parameters and SQLite will use whichever\n** function requires the least amount of data transformation.\n** ^If the xCallback argument is NULL then the collating function is\n** deleted.  ^When all collating functions having the same name are deleted,\n** that collation is no longer usable.\n**\n** ^The collating function callback is invoked with a copy of the pArg \n** application data pointer and with two strings in the encoding specified\n** by the eTextRep argument.  The collating function must return an\n** integer that is negative, zero, or positive\n** if the first string is less than, equal to, or greater than the second,\n** respectively.  A collating function must always return the same answer\n** given the same inputs.  If two or more collating functions are registered\n** to the same collation name (using different eTextRep values) then all\n** must give an equivalent answer when invoked with equivalent strings.\n** The collating function must obey the following properties for all\n** strings A, B, and C:\n**\n** <ol>\n** <li> If A==B then B==A.\n** <li> If A==B and B==C then A==C.\n** <li> If A&lt;B THEN B&gt;A.\n** <li> If A&lt;B and B&lt;C then A&lt;C.\n** </ol>\n**\n** If a collating function fails any of the above constraints and that\n** collating function is  registered and used, then the behavior of SQLite\n** is undefined.\n**\n** ^The sqlite3_create_collation_v2() works like sqlite3_create_collation()\n** with the addition that the xDestroy callback is invoked on pArg when\n** the collating function is deleted.\n** ^Collating functions are deleted when they are overridden by later\n** calls to the collation creation functions or when the\n** [database connection] is closed using [sqlite3_close()].\n**\n** ^The xDestroy callback is <u>not</u> called if the \n** sqlite3_create_collation_v2() function fails.  Applications that invoke\n** sqlite3_create_collation_v2() with a non-NULL xDestroy argument should \n** check the return code and dispose of the application data pointer\n** themselves rather than expecting SQLite to deal with it for them.\n** This is different from every other SQLite interface.  The inconsistency \n** is unfortunate but cannot be changed without breaking backwards \n** compatibility.\n**\n** See also:  [sqlite3_collation_needed()] and [sqlite3_collation_needed16()].\n*/\nSQLITE_API int sqlite3_create_collation(\n  sqlite3*, \n  const char *zName, \n  int eTextRep, \n  void *pArg,\n  int(*xCompare)(void*,int,const void*,int,const void*)\n);\nSQLITE_API int sqlite3_create_collation_v2(\n  sqlite3*, \n  const char *zName, \n  int eTextRep, \n  void *pArg,\n  int(*xCompare)(void*,int,const void*,int,const void*),\n  void(*xDestroy)(void*)\n);\nSQLITE_API int sqlite3_create_collation16(\n  sqlite3*, \n  const void *zName,\n  int eTextRep, \n  void *pArg,\n  int(*xCompare)(void*,int,const void*,int,const void*)\n);\n\n/*\n** CAPI3REF: Collation Needed Callbacks\n**\n** ^To avoid having to register all collation sequences before a database\n** can be used, a single callback function may be registered with the\n** [database connection] to be invoked whenever an undefined collation\n** sequence is required.\n**\n** ^If the function is registered using the sqlite3_collation_needed() API,\n** then it is passed the names of undefined collation sequences as strings\n** encoded in UTF-8. ^If sqlite3_collation_needed16() is used,\n** the names are passed as UTF-16 in machine native byte order.\n** ^A call to either function replaces the existing collation-needed callback.\n**\n** ^(When the callback is invoked, the first argument passed is a copy\n** of the second argument to sqlite3_collation_needed() or\n** sqlite3_collation_needed16().  The second argument is the database\n** connection.  The third argument is one of [SQLITE_UTF8], [SQLITE_UTF16BE],\n** or [SQLITE_UTF16LE], indicating the most desirable form of the collation\n** sequence function required.  The fourth parameter is the name of the\n** required collation sequence.)^\n**\n** The callback function should register the desired collation using\n** [sqlite3_create_collation()], [sqlite3_create_collation16()], or\n** [sqlite3_create_collation_v2()].\n*/\nSQLITE_API int sqlite3_collation_needed(\n  sqlite3*, \n  void*, \n  void(*)(void*,sqlite3*,int eTextRep,const char*)\n);\nSQLITE_API int sqlite3_collation_needed16(\n  sqlite3*, \n  void*,\n  void(*)(void*,sqlite3*,int eTextRep,const void*)\n);\n\n#ifdef SQLITE_HAS_CODEC\n/*\n** Specify the key for an encrypted database.  This routine should be\n** called right after sqlite3_open().\n**\n** The code to implement this API is not available in the public release\n** of SQLite.\n*/\nSQLITE_API int sqlite3_key(\n  sqlite3 *db,                   /* Database to be rekeyed */\n  const void *pKey, int nKey     /* The key */\n);\n\n/*\n** Change the key on an open database.  If the current database is not\n** encrypted, this routine will encrypt it.  If pNew==0 or nNew==0, the\n** database is decrypted.\n**\n** The code to implement this API is not available in the public release\n** of SQLite.\n*/\nSQLITE_API int sqlite3_rekey(\n  sqlite3 *db,                   /* Database to be rekeyed */\n  const void *pKey, int nKey     /* The new key */\n);\n\n/*\n** Specify the activation key for a SEE database.  Unless \n** activated, none of the SEE routines will work.\n*/\nSQLITE_API void sqlite3_activate_see(\n  const char *zPassPhrase        /* Activation phrase */\n);\n#endif\n\n#ifdef SQLITE_ENABLE_CEROD\n/*\n** Specify the activation key for a CEROD database.  Unless \n** activated, none of the CEROD routines will work.\n*/\nSQLITE_API void sqlite3_activate_cerod(\n  const char *zPassPhrase        /* Activation phrase */\n);\n#endif\n\n/*\n** CAPI3REF: Suspend Execution For A Short Time\n**\n** The sqlite3_sleep() function causes the current thread to suspend execution\n** for at least a number of milliseconds specified in its parameter.\n**\n** If the operating system does not support sleep requests with\n** millisecond time resolution, then the time will be rounded up to\n** the nearest second. The number of milliseconds of sleep actually\n** requested from the operating system is returned.\n**\n** ^SQLite implements this interface by calling the xSleep()\n** method of the default [sqlite3_vfs] object.  If the xSleep() method\n** of the default VFS is not implemented correctly, or not implemented at\n** all, then the behavior of sqlite3_sleep() may deviate from the description\n** in the previous paragraphs.\n*/\nSQLITE_API int sqlite3_sleep(int);\n\n/*\n** CAPI3REF: Name Of The Folder Holding Temporary Files\n**\n** ^(If this global variable is made to point to a string which is\n** the name of a folder (a.k.a. directory), then all temporary files\n** created by SQLite when using a built-in [sqlite3_vfs | VFS]\n** will be placed in that directory.)^  ^If this variable\n** is a NULL pointer, then SQLite performs a search for an appropriate\n** temporary file directory.\n**\n** It is not safe to read or modify this variable in more than one\n** thread at a time.  It is not safe to read or modify this variable\n** if a [database connection] is being used at the same time in a separate\n** thread.\n** It is intended that this variable be set once\n** as part of process initialization and before any SQLite interface\n** routines have been called and that this variable remain unchanged\n** thereafter.\n**\n** ^The [temp_store_directory pragma] may modify this variable and cause\n** it to point to memory obtained from [sqlite3_malloc].  ^Furthermore,\n** the [temp_store_directory pragma] always assumes that any string\n** that this variable points to is held in memory obtained from \n** [sqlite3_malloc] and the pragma may attempt to free that memory\n** using [sqlite3_free].\n** Hence, if this variable is modified directly, either it should be\n** made NULL or made to point to memory obtained from [sqlite3_malloc]\n** or else the use of the [temp_store_directory pragma] should be avoided.\n**\n** <b>Note to Windows Runtime users:</b>  The temporary directory must be set\n** prior to calling [sqlite3_open] or [sqlite3_open_v2].  Otherwise, various\n** features that require the use of temporary files may fail.  Here is an\n** example of how to do this using C++ with the Windows Runtime:\n**\n** <blockquote><pre>\n** LPCWSTR zPath = Windows::Storage::ApplicationData::Current->\n** &nbsp;     TemporaryFolder->Path->Data();\n** char zPathBuf&#91;MAX_PATH + 1&#93;;\n** memset(zPathBuf, 0, sizeof(zPathBuf));\n** WideCharToMultiByte(CP_UTF8, 0, zPath, -1, zPathBuf, sizeof(zPathBuf),\n** &nbsp;     NULL, NULL);\n** sqlite3_temp_directory = sqlite3_mprintf(\"%s\", zPathBuf);\n** </pre></blockquote>\n*/\nSQLITE_API SQLITE_EXTERN char *sqlite3_temp_directory;\n\n/*\n** CAPI3REF: Name Of The Folder Holding Database Files\n**\n** ^(If this global variable is made to point to a string which is\n** the name of a folder (a.k.a. directory), then all database files\n** specified with a relative pathname and created or accessed by\n** SQLite when using a built-in windows [sqlite3_vfs | VFS] will be assumed\n** to be relative to that directory.)^ ^If this variable is a NULL\n** pointer, then SQLite assumes that all database files specified\n** with a relative pathname are relative to the current directory\n** for the process.  Only the windows VFS makes use of this global\n** variable; it is ignored by the unix VFS.\n**\n** Changing the value of this variable while a database connection is\n** open can result in a corrupt database.\n**\n** It is not safe to read or modify this variable in more than one\n** thread at a time.  It is not safe to read or modify this variable\n** if a [database connection] is being used at the same time in a separate\n** thread.\n** It is intended that this variable be set once\n** as part of process initialization and before any SQLite interface\n** routines have been called and that this variable remain unchanged\n** thereafter.\n**\n** ^The [data_store_directory pragma] may modify this variable and cause\n** it to point to memory obtained from [sqlite3_malloc].  ^Furthermore,\n** the [data_store_directory pragma] always assumes that any string\n** that this variable points to is held in memory obtained from \n** [sqlite3_malloc] and the pragma may attempt to free that memory\n** using [sqlite3_free].\n** Hence, if this variable is modified directly, either it should be\n** made NULL or made to point to memory obtained from [sqlite3_malloc]\n** or else the use of the [data_store_directory pragma] should be avoided.\n*/\nSQLITE_API SQLITE_EXTERN char *sqlite3_data_directory;\n\n/*\n** CAPI3REF: Test For Auto-Commit Mode\n** KEYWORDS: {autocommit mode}\n**\n** ^The sqlite3_get_autocommit() interface returns non-zero or\n** zero if the given database connection is or is not in autocommit mode,\n** respectively.  ^Autocommit mode is on by default.\n** ^Autocommit mode is disabled by a [BEGIN] statement.\n** ^Autocommit mode is re-enabled by a [COMMIT] or [ROLLBACK].\n**\n** If certain kinds of errors occur on a statement within a multi-statement\n** transaction (errors including [SQLITE_FULL], [SQLITE_IOERR],\n** [SQLITE_NOMEM], [SQLITE_BUSY], and [SQLITE_INTERRUPT]) then the\n** transaction might be rolled back automatically.  The only way to\n** find out whether SQLite automatically rolled back the transaction after\n** an error is to use this function.\n**\n** If another thread changes the autocommit status of the database\n** connection while this routine is running, then the return value\n** is undefined.\n*/\nSQLITE_API int sqlite3_get_autocommit(sqlite3*);\n\n/*\n** CAPI3REF: Find The Database Handle Of A Prepared Statement\n**\n** ^The sqlite3_db_handle interface returns the [database connection] handle\n** to which a [prepared statement] belongs.  ^The [database connection]\n** returned by sqlite3_db_handle is the same [database connection]\n** that was the first argument\n** to the [sqlite3_prepare_v2()] call (or its variants) that was used to\n** create the statement in the first place.\n*/\nSQLITE_API sqlite3 *sqlite3_db_handle(sqlite3_stmt*);\n\n/*\n** CAPI3REF: Return The Filename For A Database Connection\n**\n** ^The sqlite3_db_filename(D,N) interface returns a pointer to a filename\n** associated with database N of connection D.  ^The main database file\n** has the name \"main\".  If there is no attached database N on the database\n** connection D, or if database N is a temporary or in-memory database, then\n** a NULL pointer is returned.\n**\n** ^The filename returned by this function is the output of the\n** xFullPathname method of the [VFS].  ^In other words, the filename\n** will be an absolute pathname, even if the filename used\n** to open the database originally was a URI or relative pathname.\n*/\nSQLITE_API const char *sqlite3_db_filename(sqlite3 *db, const char *zDbName);\n\n/*\n** CAPI3REF: Determine if a database is read-only\n**\n** ^The sqlite3_db_readonly(D,N) interface returns 1 if the database N\n** of connection D is read-only, 0 if it is read/write, or -1 if N is not\n** the name of a database on connection D.\n*/\nSQLITE_API int sqlite3_db_readonly(sqlite3 *db, const char *zDbName);\n\n/*\n** CAPI3REF: Find the next prepared statement\n**\n** ^This interface returns a pointer to the next [prepared statement] after\n** pStmt associated with the [database connection] pDb.  ^If pStmt is NULL\n** then this interface returns a pointer to the first prepared statement\n** associated with the database connection pDb.  ^If no prepared statement\n** satisfies the conditions of this routine, it returns NULL.\n**\n** The [database connection] pointer D in a call to\n** [sqlite3_next_stmt(D,S)] must refer to an open database\n** connection and in particular must not be a NULL pointer.\n*/\nSQLITE_API sqlite3_stmt *sqlite3_next_stmt(sqlite3 *pDb, sqlite3_stmt *pStmt);\n\n/*\n** CAPI3REF: Commit And Rollback Notification Callbacks\n**\n** ^The sqlite3_commit_hook() interface registers a callback\n** function to be invoked whenever a transaction is [COMMIT | committed].\n** ^Any callback set by a previous call to sqlite3_commit_hook()\n** for the same database connection is overridden.\n** ^The sqlite3_rollback_hook() interface registers a callback\n** function to be invoked whenever a transaction is [ROLLBACK | rolled back].\n** ^Any callback set by a previous call to sqlite3_rollback_hook()\n** for the same database connection is overridden.\n** ^The pArg argument is passed through to the callback.\n** ^If the callback on a commit hook function returns non-zero,\n** then the commit is converted into a rollback.\n**\n** ^The sqlite3_commit_hook(D,C,P) and sqlite3_rollback_hook(D,C,P) functions\n** return the P argument from the previous call of the same function\n** on the same [database connection] D, or NULL for\n** the first call for each function on D.\n**\n** The commit and rollback hook callbacks are not reentrant.\n** The callback implementation must not do anything that will modify\n** the database connection that invoked the callback.  Any actions\n** to modify the database connection must be deferred until after the\n** completion of the [sqlite3_step()] call that triggered the commit\n** or rollback hook in the first place.\n** Note that running any other SQL statements, including SELECT statements,\n** or merely calling [sqlite3_prepare_v2()] and [sqlite3_step()] will modify\n** the database connections for the meaning of \"modify\" in this paragraph.\n**\n** ^Registering a NULL function disables the callback.\n**\n** ^When the commit hook callback routine returns zero, the [COMMIT]\n** operation is allowed to continue normally.  ^If the commit hook\n** returns non-zero, then the [COMMIT] is converted into a [ROLLBACK].\n** ^The rollback hook is invoked on a rollback that results from a commit\n** hook returning non-zero, just as it would be with any other rollback.\n**\n** ^For the purposes of this API, a transaction is said to have been\n** rolled back if an explicit \"ROLLBACK\" statement is executed, or\n** an error or constraint causes an implicit rollback to occur.\n** ^The rollback callback is not invoked if a transaction is\n** automatically rolled back because the database connection is closed.\n**\n** See also the [sqlite3_update_hook()] interface.\n*/\nSQLITE_API void *sqlite3_commit_hook(sqlite3*, int(*)(void*), void*);\nSQLITE_API void *sqlite3_rollback_hook(sqlite3*, void(*)(void *), void*);\n\n/*\n** CAPI3REF: Data Change Notification Callbacks\n**\n** ^The sqlite3_update_hook() interface registers a callback function\n** with the [database connection] identified by the first argument\n** to be invoked whenever a row is updated, inserted or deleted.\n** ^Any callback set by a previous call to this function\n** for the same database connection is overridden.\n**\n** ^The second argument is a pointer to the function to invoke when a\n** row is updated, inserted or deleted.\n** ^The first argument to the callback is a copy of the third argument\n** to sqlite3_update_hook().\n** ^The second callback argument is one of [SQLITE_INSERT], [SQLITE_DELETE],\n** or [SQLITE_UPDATE], depending on the operation that caused the callback\n** to be invoked.\n** ^The third and fourth arguments to the callback contain pointers to the\n** database and table name containing the affected row.\n** ^The final callback parameter is the [rowid] of the row.\n** ^In the case of an update, this is the [rowid] after the update takes place.\n**\n** ^(The update hook is not invoked when internal system tables are\n** modified (i.e. sqlite_master and sqlite_sequence).)^\n**\n** ^In the current implementation, the update hook\n** is not invoked when duplication rows are deleted because of an\n** [ON CONFLICT | ON CONFLICT REPLACE] clause.  ^Nor is the update hook\n** invoked when rows are deleted using the [truncate optimization].\n** The exceptions defined in this paragraph might change in a future\n** release of SQLite.\n**\n** The update hook implementation must not do anything that will modify\n** the database connection that invoked the update hook.  Any actions\n** to modify the database connection must be deferred until after the\n** completion of the [sqlite3_step()] call that triggered the update hook.\n** Note that [sqlite3_prepare_v2()] and [sqlite3_step()] both modify their\n** database connections for the meaning of \"modify\" in this paragraph.\n**\n** ^The sqlite3_update_hook(D,C,P) function\n** returns the P argument from the previous call\n** on the same [database connection] D, or NULL for\n** the first call on D.\n**\n** See also the [sqlite3_commit_hook()] and [sqlite3_rollback_hook()]\n** interfaces.\n*/\nSQLITE_API void *sqlite3_update_hook(\n  sqlite3*, \n  void(*)(void *,int ,char const *,char const *,sqlite3_int64),\n  void*\n);\n\n/*\n** CAPI3REF: Enable Or Disable Shared Pager Cache\n**\n** ^(This routine enables or disables the sharing of the database cache\n** and schema data structures between [database connection | connections]\n** to the same database. Sharing is enabled if the argument is true\n** and disabled if the argument is false.)^\n**\n** ^Cache sharing is enabled and disabled for an entire process.\n** This is a change as of SQLite version 3.5.0. In prior versions of SQLite,\n** sharing was enabled or disabled for each thread separately.\n**\n** ^(The cache sharing mode set by this interface effects all subsequent\n** calls to [sqlite3_open()], [sqlite3_open_v2()], and [sqlite3_open16()].\n** Existing database connections continue use the sharing mode\n** that was in effect at the time they were opened.)^\n**\n** ^(This routine returns [SQLITE_OK] if shared cache was enabled or disabled\n** successfully.  An [error code] is returned otherwise.)^\n**\n** ^Shared cache is disabled by default. But this might change in\n** future releases of SQLite.  Applications that care about shared\n** cache setting should set it explicitly.\n**\n** See Also:  [SQLite Shared-Cache Mode]\n*/\nSQLITE_API int sqlite3_enable_shared_cache(int);\n\n/*\n** CAPI3REF: Attempt To Free Heap Memory\n**\n** ^The sqlite3_release_memory() interface attempts to free N bytes\n** of heap memory by deallocating non-essential memory allocations\n** held by the database library.   Memory used to cache database\n** pages to improve performance is an example of non-essential memory.\n** ^sqlite3_release_memory() returns the number of bytes actually freed,\n** which might be more or less than the amount requested.\n** ^The sqlite3_release_memory() routine is a no-op returning zero\n** if SQLite is not compiled with [SQLITE_ENABLE_MEMORY_MANAGEMENT].\n**\n** See also: [sqlite3_db_release_memory()]\n*/\nSQLITE_API int sqlite3_release_memory(int);\n\n/*\n** CAPI3REF: Free Memory Used By A Database Connection\n**\n** ^The sqlite3_db_release_memory(D) interface attempts to free as much heap\n** memory as possible from database connection D. Unlike the\n** [sqlite3_release_memory()] interface, this interface is effect even\n** when then [SQLITE_ENABLE_MEMORY_MANAGEMENT] compile-time option is\n** omitted.\n**\n** See also: [sqlite3_release_memory()]\n*/\nSQLITE_API int sqlite3_db_release_memory(sqlite3*);\n\n/*\n** CAPI3REF: Impose A Limit On Heap Size\n**\n** ^The sqlite3_soft_heap_limit64() interface sets and/or queries the\n** soft limit on the amount of heap memory that may be allocated by SQLite.\n** ^SQLite strives to keep heap memory utilization below the soft heap\n** limit by reducing the number of pages held in the page cache\n** as heap memory usages approaches the limit.\n** ^The soft heap limit is \"soft\" because even though SQLite strives to stay\n** below the limit, it will exceed the limit rather than generate\n** an [SQLITE_NOMEM] error.  In other words, the soft heap limit \n** is advisory only.\n**\n** ^The return value from sqlite3_soft_heap_limit64() is the size of\n** the soft heap limit prior to the call, or negative in the case of an\n** error.  ^If the argument N is negative\n** then no change is made to the soft heap limit.  Hence, the current\n** size of the soft heap limit can be determined by invoking\n** sqlite3_soft_heap_limit64() with a negative argument.\n**\n** ^If the argument N is zero then the soft heap limit is disabled.\n**\n** ^(The soft heap limit is not enforced in the current implementation\n** if one or more of following conditions are true:\n**\n** <ul>\n** <li> The soft heap limit is set to zero.\n** <li> Memory accounting is disabled using a combination of the\n**      [sqlite3_config]([SQLITE_CONFIG_MEMSTATUS],...) start-time option and\n**      the [SQLITE_DEFAULT_MEMSTATUS] compile-time option.\n** <li> An alternative page cache implementation is specified using\n**      [sqlite3_config]([SQLITE_CONFIG_PCACHE2],...).\n** <li> The page cache allocates from its own memory pool supplied\n**      by [sqlite3_config]([SQLITE_CONFIG_PAGECACHE],...) rather than\n**      from the heap.\n** </ul>)^\n**\n** Beginning with SQLite version 3.7.3, the soft heap limit is enforced\n** regardless of whether or not the [SQLITE_ENABLE_MEMORY_MANAGEMENT]\n** compile-time option is invoked.  With [SQLITE_ENABLE_MEMORY_MANAGEMENT],\n** the soft heap limit is enforced on every memory allocation.  Without\n** [SQLITE_ENABLE_MEMORY_MANAGEMENT], the soft heap limit is only enforced\n** when memory is allocated by the page cache.  Testing suggests that because\n** the page cache is the predominate memory user in SQLite, most\n** applications will achieve adequate soft heap limit enforcement without\n** the use of [SQLITE_ENABLE_MEMORY_MANAGEMENT].\n**\n** The circumstances under which SQLite will enforce the soft heap limit may\n** changes in future releases of SQLite.\n*/\nSQLITE_API sqlite3_int64 sqlite3_soft_heap_limit64(sqlite3_int64 N);\n\n/*\n** CAPI3REF: Deprecated Soft Heap Limit Interface\n** DEPRECATED\n**\n** This is a deprecated version of the [sqlite3_soft_heap_limit64()]\n** interface.  This routine is provided for historical compatibility\n** only.  All new applications should use the\n** [sqlite3_soft_heap_limit64()] interface rather than this one.\n*/\nSQLITE_API SQLITE_DEPRECATED void sqlite3_soft_heap_limit(int N);\n\n\n/*\n** CAPI3REF: Extract Metadata About A Column Of A Table\n**\n** ^This routine returns metadata about a specific column of a specific\n** database table accessible using the [database connection] handle\n** passed as the first function argument.\n**\n** ^The column is identified by the second, third and fourth parameters to\n** this function. ^The second parameter is either the name of the database\n** (i.e. \"main\", \"temp\", or an attached database) containing the specified\n** table or NULL. ^If it is NULL, then all attached databases are searched\n** for the table using the same algorithm used by the database engine to\n** resolve unqualified table references.\n**\n** ^The third and fourth parameters to this function are the table and column\n** name of the desired column, respectively. Neither of these parameters\n** may be NULL.\n**\n** ^Metadata is returned by writing to the memory locations passed as the 5th\n** and subsequent parameters to this function. ^Any of these arguments may be\n** NULL, in which case the corresponding element of metadata is omitted.\n**\n** ^(<blockquote>\n** <table border=\"1\">\n** <tr><th> Parameter <th> Output<br>Type <th>  Description\n**\n** <tr><td> 5th <td> const char* <td> Data type\n** <tr><td> 6th <td> const char* <td> Name of default collation sequence\n** <tr><td> 7th <td> int         <td> True if column has a NOT NULL constraint\n** <tr><td> 8th <td> int         <td> True if column is part of the PRIMARY KEY\n** <tr><td> 9th <td> int         <td> True if column is [AUTOINCREMENT]\n** </table>\n** </blockquote>)^\n**\n** ^The memory pointed to by the character pointers returned for the\n** declaration type and collation sequence is valid only until the next\n** call to any SQLite API function.\n**\n** ^If the specified table is actually a view, an [error code] is returned.\n**\n** ^If the specified column is \"rowid\", \"oid\" or \"_rowid_\" and an\n** [INTEGER PRIMARY KEY] column has been explicitly declared, then the output\n** parameters are set for the explicitly declared column. ^(If there is no\n** explicitly declared [INTEGER PRIMARY KEY] column, then the output\n** parameters are set as follows:\n**\n** <pre>\n**     data type: \"INTEGER\"\n**     collation sequence: \"BINARY\"\n**     not null: 0\n**     primary key: 1\n**     auto increment: 0\n** </pre>)^\n**\n** ^(This function may load one or more schemas from database files. If an\n** error occurs during this process, or if the requested table or column\n** cannot be found, an [error code] is returned and an error message left\n** in the [database connection] (to be retrieved using sqlite3_errmsg()).)^\n**\n** ^This API is only available if the library was compiled with the\n** [SQLITE_ENABLE_COLUMN_METADATA] C-preprocessor symbol defined.\n*/\nSQLITE_API int sqlite3_table_column_metadata(\n  sqlite3 *db,                /* Connection handle */\n  const char *zDbName,        /* Database name or NULL */\n  const char *zTableName,     /* Table name */\n  const char *zColumnName,    /* Column name */\n  char const **pzDataType,    /* OUTPUT: Declared data type */\n  char const **pzCollSeq,     /* OUTPUT: Collation sequence name */\n  int *pNotNull,              /* OUTPUT: True if NOT NULL constraint exists */\n  int *pPrimaryKey,           /* OUTPUT: True if column part of PK */\n  int *pAutoinc               /* OUTPUT: True if column is auto-increment */\n);\n\n/*\n** CAPI3REF: Load An Extension\n**\n** ^This interface loads an SQLite extension library from the named file.\n**\n** ^The sqlite3_load_extension() interface attempts to load an\n** SQLite extension library contained in the file zFile.\n**\n** ^The entry point is zProc.\n** ^zProc may be 0, in which case the name of the entry point\n** defaults to \"sqlite3_extension_init\".\n** ^The sqlite3_load_extension() interface returns\n** [SQLITE_OK] on success and [SQLITE_ERROR] if something goes wrong.\n** ^If an error occurs and pzErrMsg is not 0, then the\n** [sqlite3_load_extension()] interface shall attempt to\n** fill *pzErrMsg with error message text stored in memory\n** obtained from [sqlite3_malloc()]. The calling function\n** should free this memory by calling [sqlite3_free()].\n**\n** ^Extension loading must be enabled using\n** [sqlite3_enable_load_extension()] prior to calling this API,\n** otherwise an error will be returned.\n**\n** See also the [load_extension() SQL function].\n*/\nSQLITE_API int sqlite3_load_extension(\n  sqlite3 *db,          /* Load the extension into this database connection */\n  const char *zFile,    /* Name of the shared library containing extension */\n  const char *zProc,    /* Entry point.  Derived from zFile if 0 */\n  char **pzErrMsg       /* Put error message here if not 0 */\n);\n\n/*\n** CAPI3REF: Enable Or Disable Extension Loading\n**\n** ^So as not to open security holes in older applications that are\n** unprepared to deal with extension loading, and as a means of disabling\n** extension loading while evaluating user-entered SQL, the following API\n** is provided to turn the [sqlite3_load_extension()] mechanism on and off.\n**\n** ^Extension loading is off by default. See ticket #1863.\n** ^Call the sqlite3_enable_load_extension() routine with onoff==1\n** to turn extension loading on and call it with onoff==0 to turn\n** it back off again.\n*/\nSQLITE_API int sqlite3_enable_load_extension(sqlite3 *db, int onoff);\n\n/*\n** CAPI3REF: Automatically Load Statically Linked Extensions\n**\n** ^This interface causes the xEntryPoint() function to be invoked for\n** each new [database connection] that is created.  The idea here is that\n** xEntryPoint() is the entry point for a statically linked SQLite extension\n** that is to be automatically loaded into all new database connections.\n**\n** ^(Even though the function prototype shows that xEntryPoint() takes\n** no arguments and returns void, SQLite invokes xEntryPoint() with three\n** arguments and expects and integer result as if the signature of the\n** entry point where as follows:\n**\n** <blockquote><pre>\n** &nbsp;  int xEntryPoint(\n** &nbsp;    sqlite3 *db,\n** &nbsp;    const char **pzErrMsg,\n** &nbsp;    const struct sqlite3_api_routines *pThunk\n** &nbsp;  );\n** </pre></blockquote>)^\n**\n** If the xEntryPoint routine encounters an error, it should make *pzErrMsg\n** point to an appropriate error message (obtained from [sqlite3_mprintf()])\n** and return an appropriate [error code].  ^SQLite ensures that *pzErrMsg\n** is NULL before calling the xEntryPoint().  ^SQLite will invoke\n** [sqlite3_free()] on *pzErrMsg after xEntryPoint() returns.  ^If any\n** xEntryPoint() returns an error, the [sqlite3_open()], [sqlite3_open16()],\n** or [sqlite3_open_v2()] call that provoked the xEntryPoint() will fail.\n**\n** ^Calling sqlite3_auto_extension(X) with an entry point X that is already\n** on the list of automatic extensions is a harmless no-op. ^No entry point\n** will be called more than once for each database connection that is opened.\n**\n** See also: [sqlite3_reset_auto_extension()].\n*/\nSQLITE_API int sqlite3_auto_extension(void (*xEntryPoint)(void));\n\n/*\n** CAPI3REF: Reset Automatic Extension Loading\n**\n** ^This interface disables all automatic extensions previously\n** registered using [sqlite3_auto_extension()].\n*/\nSQLITE_API void sqlite3_reset_auto_extension(void);\n\n/*\n** The interface to the virtual-table mechanism is currently considered\n** to be experimental.  The interface might change in incompatible ways.\n** If this is a problem for you, do not use the interface at this time.\n**\n** When the virtual-table mechanism stabilizes, we will declare the\n** interface fixed, support it indefinitely, and remove this comment.\n*/\n\n/*\n** Structures used by the virtual table interface\n*/\ntypedef struct sqlite3_vtab sqlite3_vtab;\ntypedef struct sqlite3_index_info sqlite3_index_info;\ntypedef struct sqlite3_vtab_cursor sqlite3_vtab_cursor;\ntypedef struct sqlite3_module sqlite3_module;\n\n/*\n** CAPI3REF: Virtual Table Object\n** KEYWORDS: sqlite3_module {virtual table module}\n**\n** This structure, sometimes called a \"virtual table module\", \n** defines the implementation of a [virtual tables].  \n** This structure consists mostly of methods for the module.\n**\n** ^A virtual table module is created by filling in a persistent\n** instance of this structure and passing a pointer to that instance\n** to [sqlite3_create_module()] or [sqlite3_create_module_v2()].\n** ^The registration remains valid until it is replaced by a different\n** module or until the [database connection] closes.  The content\n** of this structure must not change while it is registered with\n** any database connection.\n*/\nstruct sqlite3_module {\n  int iVersion;\n  int (*xCreate)(sqlite3*, void *pAux,\n               int argc, const char *const*argv,\n               sqlite3_vtab **ppVTab, char**);\n  int (*xConnect)(sqlite3*, void *pAux,\n               int argc, const char *const*argv,\n               sqlite3_vtab **ppVTab, char**);\n  int (*xBestIndex)(sqlite3_vtab *pVTab, sqlite3_index_info*);\n  int (*xDisconnect)(sqlite3_vtab *pVTab);\n  int (*xDestroy)(sqlite3_vtab *pVTab);\n  int (*xOpen)(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor);\n  int (*xClose)(sqlite3_vtab_cursor*);\n  int (*xFilter)(sqlite3_vtab_cursor*, int idxNum, const char *idxStr,\n                int argc, sqlite3_value **argv);\n  int (*xNext)(sqlite3_vtab_cursor*);\n  int (*xEof)(sqlite3_vtab_cursor*);\n  int (*xColumn)(sqlite3_vtab_cursor*, sqlite3_context*, int);\n  int (*xRowid)(sqlite3_vtab_cursor*, sqlite3_int64 *pRowid);\n  int (*xUpdate)(sqlite3_vtab *, int, sqlite3_value **, sqlite3_int64 *);\n  int (*xBegin)(sqlite3_vtab *pVTab);\n  int (*xSync)(sqlite3_vtab *pVTab);\n  int (*xCommit)(sqlite3_vtab *pVTab);\n  int (*xRollback)(sqlite3_vtab *pVTab);\n  int (*xFindFunction)(sqlite3_vtab *pVtab, int nArg, const char *zName,\n                       void (**pxFunc)(sqlite3_context*,int,sqlite3_value**),\n                       void **ppArg);\n  int (*xRename)(sqlite3_vtab *pVtab, const char *zNew);\n  /* The methods above are in version 1 of the sqlite_module object. Those \n  ** below are for version 2 and greater. */\n  int (*xSavepoint)(sqlite3_vtab *pVTab, int);\n  int (*xRelease)(sqlite3_vtab *pVTab, int);\n  int (*xRollbackTo)(sqlite3_vtab *pVTab, int);\n};\n\n/*\n** CAPI3REF: Virtual Table Indexing Information\n** KEYWORDS: sqlite3_index_info\n**\n** The sqlite3_index_info structure and its substructures is used as part\n** of the [virtual table] interface to\n** pass information into and receive the reply from the [xBestIndex]\n** method of a [virtual table module].  The fields under **Inputs** are the\n** inputs to xBestIndex and are read-only.  xBestIndex inserts its\n** results into the **Outputs** fields.\n**\n** ^(The aConstraint[] array records WHERE clause constraints of the form:\n**\n** <blockquote>column OP expr</blockquote>\n**\n** where OP is =, &lt;, &lt;=, &gt;, or &gt;=.)^  ^(The particular operator is\n** stored in aConstraint[].op using one of the\n** [SQLITE_INDEX_CONSTRAINT_EQ | SQLITE_INDEX_CONSTRAINT_ values].)^\n** ^(The index of the column is stored in\n** aConstraint[].iColumn.)^  ^(aConstraint[].usable is TRUE if the\n** expr on the right-hand side can be evaluated (and thus the constraint\n** is usable) and false if it cannot.)^\n**\n** ^The optimizer automatically inverts terms of the form \"expr OP column\"\n** and makes other simplifications to the WHERE clause in an attempt to\n** get as many WHERE clause terms into the form shown above as possible.\n** ^The aConstraint[] array only reports WHERE clause terms that are\n** relevant to the particular virtual table being queried.\n**\n** ^Information about the ORDER BY clause is stored in aOrderBy[].\n** ^Each term of aOrderBy records a column of the ORDER BY clause.\n**\n** The [xBestIndex] method must fill aConstraintUsage[] with information\n** about what parameters to pass to xFilter.  ^If argvIndex>0 then\n** the right-hand side of the corresponding aConstraint[] is evaluated\n** and becomes the argvIndex-th entry in argv.  ^(If aConstraintUsage[].omit\n** is true, then the constraint is assumed to be fully handled by the\n** virtual table and is not checked again by SQLite.)^\n**\n** ^The idxNum and idxPtr values are recorded and passed into the\n** [xFilter] method.\n** ^[sqlite3_free()] is used to free idxPtr if and only if\n** needToFreeIdxPtr is true.\n**\n** ^The orderByConsumed means that output from [xFilter]/[xNext] will occur in\n** the correct order to satisfy the ORDER BY clause so that no separate\n** sorting step is required.\n**\n** ^The estimatedCost value is an estimate of the cost of doing the\n** particular lookup.  A full scan of a table with N entries should have\n** a cost of N.  A binary search of a table of N entries should have a\n** cost of approximately log(N).\n*/\nstruct sqlite3_index_info {\n  /* Inputs */\n  int nConstraint;           /* Number of entries in aConstraint */\n  struct sqlite3_index_constraint {\n     int iColumn;              /* Column on left-hand side of constraint */\n     unsigned char op;         /* Constraint operator */\n     unsigned char usable;     /* True if this constraint is usable */\n     int iTermOffset;          /* Used internally - xBestIndex should ignore */\n  } *aConstraint;            /* Table of WHERE clause constraints */\n  int nOrderBy;              /* Number of terms in the ORDER BY clause */\n  struct sqlite3_index_orderby {\n     int iColumn;              /* Column number */\n     unsigned char desc;       /* True for DESC.  False for ASC. */\n  } *aOrderBy;               /* The ORDER BY clause */\n  /* Outputs */\n  struct sqlite3_index_constraint_usage {\n    int argvIndex;           /* if >0, constraint is part of argv to xFilter */\n    unsigned char omit;      /* Do not code a test for this constraint */\n  } *aConstraintUsage;\n  int idxNum;                /* Number used to identify the index */\n  char *idxStr;              /* String, possibly obtained from sqlite3_malloc */\n  int needToFreeIdxStr;      /* Free idxStr using sqlite3_free() if true */\n  int orderByConsumed;       /* True if output is already ordered */\n  double estimatedCost;      /* Estimated cost of using this index */\n};\n\n/*\n** CAPI3REF: Virtual Table Constraint Operator Codes\n**\n** These macros defined the allowed values for the\n** [sqlite3_index_info].aConstraint[].op field.  Each value represents\n** an operator that is part of a constraint term in the wHERE clause of\n** a query that uses a [virtual table].\n*/\n#define SQLITE_INDEX_CONSTRAINT_EQ    2\n#define SQLITE_INDEX_CONSTRAINT_GT    4\n#define SQLITE_INDEX_CONSTRAINT_LE    8\n#define SQLITE_INDEX_CONSTRAINT_LT    16\n#define SQLITE_INDEX_CONSTRAINT_GE    32\n#define SQLITE_INDEX_CONSTRAINT_MATCH 64\n\n/*\n** CAPI3REF: Register A Virtual Table Implementation\n**\n** ^These routines are used to register a new [virtual table module] name.\n** ^Module names must be registered before\n** creating a new [virtual table] using the module and before using a\n** preexisting [virtual table] for the module.\n**\n** ^The module name is registered on the [database connection] specified\n** by the first parameter.  ^The name of the module is given by the \n** second parameter.  ^The third parameter is a pointer to\n** the implementation of the [virtual table module].   ^The fourth\n** parameter is an arbitrary client data pointer that is passed through\n** into the [xCreate] and [xConnect] methods of the virtual table module\n** when a new virtual table is be being created or reinitialized.\n**\n** ^The sqlite3_create_module_v2() interface has a fifth parameter which\n** is a pointer to a destructor for the pClientData.  ^SQLite will\n** invoke the destructor function (if it is not NULL) when SQLite\n** no longer needs the pClientData pointer.  ^The destructor will also\n** be invoked if the call to sqlite3_create_module_v2() fails.\n** ^The sqlite3_create_module()\n** interface is equivalent to sqlite3_create_module_v2() with a NULL\n** destructor.\n*/\nSQLITE_API int sqlite3_create_module(\n  sqlite3 *db,               /* SQLite connection to register module with */\n  const char *zName,         /* Name of the module */\n  const sqlite3_module *p,   /* Methods for the module */\n  void *pClientData          /* Client data for xCreate/xConnect */\n);\nSQLITE_API int sqlite3_create_module_v2(\n  sqlite3 *db,               /* SQLite connection to register module with */\n  const char *zName,         /* Name of the module */\n  const sqlite3_module *p,   /* Methods for the module */\n  void *pClientData,         /* Client data for xCreate/xConnect */\n  void(*xDestroy)(void*)     /* Module destructor function */\n);\n\n/*\n** CAPI3REF: Virtual Table Instance Object\n** KEYWORDS: sqlite3_vtab\n**\n** Every [virtual table module] implementation uses a subclass\n** of this object to describe a particular instance\n** of the [virtual table].  Each subclass will\n** be tailored to the specific needs of the module implementation.\n** The purpose of this superclass is to define certain fields that are\n** common to all module implementations.\n**\n** ^Virtual tables methods can set an error message by assigning a\n** string obtained from [sqlite3_mprintf()] to zErrMsg.  The method should\n** take care that any prior string is freed by a call to [sqlite3_free()]\n** prior to assigning a new string to zErrMsg.  ^After the error message\n** is delivered up to the client application, the string will be automatically\n** freed by sqlite3_free() and the zErrMsg field will be zeroed.\n*/\nstruct sqlite3_vtab {\n  const sqlite3_module *pModule;  /* The module for this virtual table */\n  int nRef;                       /* NO LONGER USED */\n  char *zErrMsg;                  /* Error message from sqlite3_mprintf() */\n  /* Virtual table implementations will typically add additional fields */\n};\n\n/*\n** CAPI3REF: Virtual Table Cursor Object\n** KEYWORDS: sqlite3_vtab_cursor {virtual table cursor}\n**\n** Every [virtual table module] implementation uses a subclass of the\n** following structure to describe cursors that point into the\n** [virtual table] and are used\n** to loop through the virtual table.  Cursors are created using the\n** [sqlite3_module.xOpen | xOpen] method of the module and are destroyed\n** by the [sqlite3_module.xClose | xClose] method.  Cursors are used\n** by the [xFilter], [xNext], [xEof], [xColumn], and [xRowid] methods\n** of the module.  Each module implementation will define\n** the content of a cursor structure to suit its own needs.\n**\n** This superclass exists in order to define fields of the cursor that\n** are common to all implementations.\n*/\nstruct sqlite3_vtab_cursor {\n  sqlite3_vtab *pVtab;      /* Virtual table of this cursor */\n  /* Virtual table implementations will typically add additional fields */\n};\n\n/*\n** CAPI3REF: Declare The Schema Of A Virtual Table\n**\n** ^The [xCreate] and [xConnect] methods of a\n** [virtual table module] call this interface\n** to declare the format (the names and datatypes of the columns) of\n** the virtual tables they implement.\n*/\nSQLITE_API int sqlite3_declare_vtab(sqlite3*, const char *zSQL);\n\n/*\n** CAPI3REF: Overload A Function For A Virtual Table\n**\n** ^(Virtual tables can provide alternative implementations of functions\n** using the [xFindFunction] method of the [virtual table module].  \n** But global versions of those functions\n** must exist in order to be overloaded.)^\n**\n** ^(This API makes sure a global version of a function with a particular\n** name and number of parameters exists.  If no such function exists\n** before this API is called, a new function is created.)^  ^The implementation\n** of the new function always causes an exception to be thrown.  So\n** the new function is not good for anything by itself.  Its only\n** purpose is to be a placeholder function that can be overloaded\n** by a [virtual table].\n*/\nSQLITE_API int sqlite3_overload_function(sqlite3*, const char *zFuncName, int nArg);\n\n/*\n** The interface to the virtual-table mechanism defined above (back up\n** to a comment remarkably similar to this one) is currently considered\n** to be experimental.  The interface might change in incompatible ways.\n** If this is a problem for you, do not use the interface at this time.\n**\n** When the virtual-table mechanism stabilizes, we will declare the\n** interface fixed, support it indefinitely, and remove this comment.\n*/\n\n/*\n** CAPI3REF: A Handle To An Open BLOB\n** KEYWORDS: {BLOB handle} {BLOB handles}\n**\n** An instance of this object represents an open BLOB on which\n** [sqlite3_blob_open | incremental BLOB I/O] can be performed.\n** ^Objects of this type are created by [sqlite3_blob_open()]\n** and destroyed by [sqlite3_blob_close()].\n** ^The [sqlite3_blob_read()] and [sqlite3_blob_write()] interfaces\n** can be used to read or write small subsections of the BLOB.\n** ^The [sqlite3_blob_bytes()] interface returns the size of the BLOB in bytes.\n*/\ntypedef struct sqlite3_blob sqlite3_blob;\n\n/*\n** CAPI3REF: Open A BLOB For Incremental I/O\n**\n** ^(This interfaces opens a [BLOB handle | handle] to the BLOB located\n** in row iRow, column zColumn, table zTable in database zDb;\n** in other words, the same BLOB that would be selected by:\n**\n** <pre>\n**     SELECT zColumn FROM zDb.zTable WHERE [rowid] = iRow;\n** </pre>)^\n**\n** ^If the flags parameter is non-zero, then the BLOB is opened for read\n** and write access. ^If it is zero, the BLOB is opened for read access.\n** ^It is not possible to open a column that is part of an index or primary \n** key for writing. ^If [foreign key constraints] are enabled, it is \n** not possible to open a column that is part of a [child key] for writing.\n**\n** ^Note that the database name is not the filename that contains\n** the database but rather the symbolic name of the database that\n** appears after the AS keyword when the database is connected using [ATTACH].\n** ^For the main database file, the database name is \"main\".\n** ^For TEMP tables, the database name is \"temp\".\n**\n** ^(On success, [SQLITE_OK] is returned and the new [BLOB handle] is written\n** to *ppBlob. Otherwise an [error code] is returned and *ppBlob is set\n** to be a null pointer.)^\n** ^This function sets the [database connection] error code and message\n** accessible via [sqlite3_errcode()] and [sqlite3_errmsg()] and related\n** functions. ^Note that the *ppBlob variable is always initialized in a\n** way that makes it safe to invoke [sqlite3_blob_close()] on *ppBlob\n** regardless of the success or failure of this routine.\n**\n** ^(If the row that a BLOB handle points to is modified by an\n** [UPDATE], [DELETE], or by [ON CONFLICT] side-effects\n** then the BLOB handle is marked as \"expired\".\n** This is true if any column of the row is changed, even a column\n** other than the one the BLOB handle is open on.)^\n** ^Calls to [sqlite3_blob_read()] and [sqlite3_blob_write()] for\n** an expired BLOB handle fail with a return code of [SQLITE_ABORT].\n** ^(Changes written into a BLOB prior to the BLOB expiring are not\n** rolled back by the expiration of the BLOB.  Such changes will eventually\n** commit if the transaction continues to completion.)^\n**\n** ^Use the [sqlite3_blob_bytes()] interface to determine the size of\n** the opened blob.  ^The size of a blob may not be changed by this\n** interface.  Use the [UPDATE] SQL command to change the size of a\n** blob.\n**\n** ^The [sqlite3_bind_zeroblob()] and [sqlite3_result_zeroblob()] interfaces\n** and the built-in [zeroblob] SQL function can be used, if desired,\n** to create an empty, zero-filled blob in which to read or write using\n** this interface.\n**\n** To avoid a resource leak, every open [BLOB handle] should eventually\n** be released by a call to [sqlite3_blob_close()].\n*/\nSQLITE_API int sqlite3_blob_open(\n  sqlite3*,\n  const char *zDb,\n  const char *zTable,\n  const char *zColumn,\n  sqlite3_int64 iRow,\n  int flags,\n  sqlite3_blob **ppBlob\n);\n\n/*\n** CAPI3REF: Move a BLOB Handle to a New Row\n**\n** ^This function is used to move an existing blob handle so that it points\n** to a different row of the same database table. ^The new row is identified\n** by the rowid value passed as the second argument. Only the row can be\n** changed. ^The database, table and column on which the blob handle is open\n** remain the same. Moving an existing blob handle to a new row can be\n** faster than closing the existing handle and opening a new one.\n**\n** ^(The new row must meet the same criteria as for [sqlite3_blob_open()] -\n** it must exist and there must be either a blob or text value stored in\n** the nominated column.)^ ^If the new row is not present in the table, or if\n** it does not contain a blob or text value, or if another error occurs, an\n** SQLite error code is returned and the blob handle is considered aborted.\n** ^All subsequent calls to [sqlite3_blob_read()], [sqlite3_blob_write()] or\n** [sqlite3_blob_reopen()] on an aborted blob handle immediately return\n** SQLITE_ABORT. ^Calling [sqlite3_blob_bytes()] on an aborted blob handle\n** always returns zero.\n**\n** ^This function sets the database handle error code and message.\n*/\nSQLITE_API SQLITE_EXPERIMENTAL int sqlite3_blob_reopen(sqlite3_blob *, sqlite3_int64);\n\n/*\n** CAPI3REF: Close A BLOB Handle\n**\n** ^Closes an open [BLOB handle].\n**\n** ^Closing a BLOB shall cause the current transaction to commit\n** if there are no other BLOBs, no pending prepared statements, and the\n** database connection is in [autocommit mode].\n** ^If any writes were made to the BLOB, they might be held in cache\n** until the close operation if they will fit.\n**\n** ^(Closing the BLOB often forces the changes\n** out to disk and so if any I/O errors occur, they will likely occur\n** at the time when the BLOB is closed.  Any errors that occur during\n** closing are reported as a non-zero return value.)^\n**\n** ^(The BLOB is closed unconditionally.  Even if this routine returns\n** an error code, the BLOB is still closed.)^\n**\n** ^Calling this routine with a null pointer (such as would be returned\n** by a failed call to [sqlite3_blob_open()]) is a harmless no-op.\n*/\nSQLITE_API int sqlite3_blob_close(sqlite3_blob *);\n\n/*\n** CAPI3REF: Return The Size Of An Open BLOB\n**\n** ^Returns the size in bytes of the BLOB accessible via the \n** successfully opened [BLOB handle] in its only argument.  ^The\n** incremental blob I/O routines can only read or overwriting existing\n** blob content; they cannot change the size of a blob.\n**\n** This routine only works on a [BLOB handle] which has been created\n** by a prior successful call to [sqlite3_blob_open()] and which has not\n** been closed by [sqlite3_blob_close()].  Passing any other pointer in\n** to this routine results in undefined and probably undesirable behavior.\n*/\nSQLITE_API int sqlite3_blob_bytes(sqlite3_blob *);\n\n/*\n** CAPI3REF: Read Data From A BLOB Incrementally\n**\n** ^(This function is used to read data from an open [BLOB handle] into a\n** caller-supplied buffer. N bytes of data are copied into buffer Z\n** from the open BLOB, starting at offset iOffset.)^\n**\n** ^If offset iOffset is less than N bytes from the end of the BLOB,\n** [SQLITE_ERROR] is returned and no data is read.  ^If N or iOffset is\n** less than zero, [SQLITE_ERROR] is returned and no data is read.\n** ^The size of the blob (and hence the maximum value of N+iOffset)\n** can be determined using the [sqlite3_blob_bytes()] interface.\n**\n** ^An attempt to read from an expired [BLOB handle] fails with an\n** error code of [SQLITE_ABORT].\n**\n** ^(On success, sqlite3_blob_read() returns SQLITE_OK.\n** Otherwise, an [error code] or an [extended error code] is returned.)^\n**\n** This routine only works on a [BLOB handle] which has been created\n** by a prior successful call to [sqlite3_blob_open()] and which has not\n** been closed by [sqlite3_blob_close()].  Passing any other pointer in\n** to this routine results in undefined and probably undesirable behavior.\n**\n** See also: [sqlite3_blob_write()].\n*/\nSQLITE_API int sqlite3_blob_read(sqlite3_blob *, void *Z, int N, int iOffset);\n\n/*\n** CAPI3REF: Write Data Into A BLOB Incrementally\n**\n** ^This function is used to write data into an open [BLOB handle] from a\n** caller-supplied buffer. ^N bytes of data are copied from the buffer Z\n** into the open BLOB, starting at offset iOffset.\n**\n** ^If the [BLOB handle] passed as the first argument was not opened for\n** writing (the flags parameter to [sqlite3_blob_open()] was zero),\n** this function returns [SQLITE_READONLY].\n**\n** ^This function may only modify the contents of the BLOB; it is\n** not possible to increase the size of a BLOB using this API.\n** ^If offset iOffset is less than N bytes from the end of the BLOB,\n** [SQLITE_ERROR] is returned and no data is written.  ^If N is\n** less than zero [SQLITE_ERROR] is returned and no data is written.\n** The size of the BLOB (and hence the maximum value of N+iOffset)\n** can be determined using the [sqlite3_blob_bytes()] interface.\n**\n** ^An attempt to write to an expired [BLOB handle] fails with an\n** error code of [SQLITE_ABORT].  ^Writes to the BLOB that occurred\n** before the [BLOB handle] expired are not rolled back by the\n** expiration of the handle, though of course those changes might\n** have been overwritten by the statement that expired the BLOB handle\n** or by other independent statements.\n**\n** ^(On success, sqlite3_blob_write() returns SQLITE_OK.\n** Otherwise, an  [error code] or an [extended error code] is returned.)^\n**\n** This routine only works on a [BLOB handle] which has been created\n** by a prior successful call to [sqlite3_blob_open()] and which has not\n** been closed by [sqlite3_blob_close()].  Passing any other pointer in\n** to this routine results in undefined and probably undesirable behavior.\n**\n** See also: [sqlite3_blob_read()].\n*/\nSQLITE_API int sqlite3_blob_write(sqlite3_blob *, const void *z, int n, int iOffset);\n\n/*\n** CAPI3REF: Virtual File System Objects\n**\n** A virtual filesystem (VFS) is an [sqlite3_vfs] object\n** that SQLite uses to interact\n** with the underlying operating system.  Most SQLite builds come with a\n** single default VFS that is appropriate for the host computer.\n** New VFSes can be registered and existing VFSes can be unregistered.\n** The following interfaces are provided.\n**\n** ^The sqlite3_vfs_find() interface returns a pointer to a VFS given its name.\n** ^Names are case sensitive.\n** ^Names are zero-terminated UTF-8 strings.\n** ^If there is no match, a NULL pointer is returned.\n** ^If zVfsName is NULL then the default VFS is returned.\n**\n** ^New VFSes are registered with sqlite3_vfs_register().\n** ^Each new VFS becomes the default VFS if the makeDflt flag is set.\n** ^The same VFS can be registered multiple times without injury.\n** ^To make an existing VFS into the default VFS, register it again\n** with the makeDflt flag set.  If two different VFSes with the\n** same name are registered, the behavior is undefined.  If a\n** VFS is registered with a name that is NULL or an empty string,\n** then the behavior is undefined.\n**\n** ^Unregister a VFS with the sqlite3_vfs_unregister() interface.\n** ^(If the default VFS is unregistered, another VFS is chosen as\n** the default.  The choice for the new VFS is arbitrary.)^\n*/\nSQLITE_API sqlite3_vfs *sqlite3_vfs_find(const char *zVfsName);\nSQLITE_API int sqlite3_vfs_register(sqlite3_vfs*, int makeDflt);\nSQLITE_API int sqlite3_vfs_unregister(sqlite3_vfs*);\n\n/*\n** CAPI3REF: Mutexes\n**\n** The SQLite core uses these routines for thread\n** synchronization. Though they are intended for internal\n** use by SQLite, code that links against SQLite is\n** permitted to use any of these routines.\n**\n** The SQLite source code contains multiple implementations\n** of these mutex routines.  An appropriate implementation\n** is selected automatically at compile-time.  ^(The following\n** implementations are available in the SQLite core:\n**\n** <ul>\n** <li>   SQLITE_MUTEX_PTHREADS\n** <li>   SQLITE_MUTEX_W32\n** <li>   SQLITE_MUTEX_NOOP\n** </ul>)^\n**\n** ^The SQLITE_MUTEX_NOOP implementation is a set of routines\n** that does no real locking and is appropriate for use in\n** a single-threaded application.  ^The SQLITE_MUTEX_PTHREADS and\n** SQLITE_MUTEX_W32 implementations are appropriate for use on Unix\n** and Windows.\n**\n** ^(If SQLite is compiled with the SQLITE_MUTEX_APPDEF preprocessor\n** macro defined (with \"-DSQLITE_MUTEX_APPDEF=1\"), then no mutex\n** implementation is included with the library. In this case the\n** application must supply a custom mutex implementation using the\n** [SQLITE_CONFIG_MUTEX] option of the sqlite3_config() function\n** before calling sqlite3_initialize() or any other public sqlite3_\n** function that calls sqlite3_initialize().)^\n**\n** ^The sqlite3_mutex_alloc() routine allocates a new\n** mutex and returns a pointer to it. ^If it returns NULL\n** that means that a mutex could not be allocated.  ^SQLite\n** will unwind its stack and return an error.  ^(The argument\n** to sqlite3_mutex_alloc() is one of these integer constants:\n**\n** <ul>\n** <li>  SQLITE_MUTEX_FAST\n** <li>  SQLITE_MUTEX_RECURSIVE\n** <li>  SQLITE_MUTEX_STATIC_MASTER\n** <li>  SQLITE_MUTEX_STATIC_MEM\n** <li>  SQLITE_MUTEX_STATIC_MEM2\n** <li>  SQLITE_MUTEX_STATIC_PRNG\n** <li>  SQLITE_MUTEX_STATIC_LRU\n** <li>  SQLITE_MUTEX_STATIC_LRU2\n** </ul>)^\n**\n** ^The first two constants (SQLITE_MUTEX_FAST and SQLITE_MUTEX_RECURSIVE)\n** cause sqlite3_mutex_alloc() to create\n** a new mutex.  ^The new mutex is recursive when SQLITE_MUTEX_RECURSIVE\n** is used but not necessarily so when SQLITE_MUTEX_FAST is used.\n** The mutex implementation does not need to make a distinction\n** between SQLITE_MUTEX_RECURSIVE and SQLITE_MUTEX_FAST if it does\n** not want to.  ^SQLite will only request a recursive mutex in\n** cases where it really needs one.  ^If a faster non-recursive mutex\n** implementation is available on the host platform, the mutex subsystem\n** might return such a mutex in response to SQLITE_MUTEX_FAST.\n**\n** ^The other allowed parameters to sqlite3_mutex_alloc() (anything other\n** than SQLITE_MUTEX_FAST and SQLITE_MUTEX_RECURSIVE) each return\n** a pointer to a static preexisting mutex.  ^Six static mutexes are\n** used by the current version of SQLite.  Future versions of SQLite\n** may add additional static mutexes.  Static mutexes are for internal\n** use by SQLite only.  Applications that use SQLite mutexes should\n** use only the dynamic mutexes returned by SQLITE_MUTEX_FAST or\n** SQLITE_MUTEX_RECURSIVE.\n**\n** ^Note that if one of the dynamic mutex parameters (SQLITE_MUTEX_FAST\n** or SQLITE_MUTEX_RECURSIVE) is used then sqlite3_mutex_alloc()\n** returns a different mutex on every call.  ^But for the static\n** mutex types, the same mutex is returned on every call that has\n** the same type number.\n**\n** ^The sqlite3_mutex_free() routine deallocates a previously\n** allocated dynamic mutex.  ^SQLite is careful to deallocate every\n** dynamic mutex that it allocates.  The dynamic mutexes must not be in\n** use when they are deallocated.  Attempting to deallocate a static\n** mutex results in undefined behavior.  ^SQLite never deallocates\n** a static mutex.\n**\n** ^The sqlite3_mutex_enter() and sqlite3_mutex_try() routines attempt\n** to enter a mutex.  ^If another thread is already within the mutex,\n** sqlite3_mutex_enter() will block and sqlite3_mutex_try() will return\n** SQLITE_BUSY.  ^The sqlite3_mutex_try() interface returns [SQLITE_OK]\n** upon successful entry.  ^(Mutexes created using\n** SQLITE_MUTEX_RECURSIVE can be entered multiple times by the same thread.\n** In such cases the,\n** mutex must be exited an equal number of times before another thread\n** can enter.)^  ^(If the same thread tries to enter any other\n** kind of mutex more than once, the behavior is undefined.\n** SQLite will never exhibit\n** such behavior in its own use of mutexes.)^\n**\n** ^(Some systems (for example, Windows 95) do not support the operation\n** implemented by sqlite3_mutex_try().  On those systems, sqlite3_mutex_try()\n** will always return SQLITE_BUSY.  The SQLite core only ever uses\n** sqlite3_mutex_try() as an optimization so this is acceptable behavior.)^\n**\n** ^The sqlite3_mutex_leave() routine exits a mutex that was\n** previously entered by the same thread.   ^(The behavior\n** is undefined if the mutex is not currently entered by the\n** calling thread or is not currently allocated.  SQLite will\n** never do either.)^\n**\n** ^If the argument to sqlite3_mutex_enter(), sqlite3_mutex_try(), or\n** sqlite3_mutex_leave() is a NULL pointer, then all three routines\n** behave as no-ops.\n**\n** See also: [sqlite3_mutex_held()] and [sqlite3_mutex_notheld()].\n*/\nSQLITE_API sqlite3_mutex *sqlite3_mutex_alloc(int);\nSQLITE_API void sqlite3_mutex_free(sqlite3_mutex*);\nSQLITE_API void sqlite3_mutex_enter(sqlite3_mutex*);\nSQLITE_API int sqlite3_mutex_try(sqlite3_mutex*);\nSQLITE_API void sqlite3_mutex_leave(sqlite3_mutex*);\n\n/*\n** CAPI3REF: Mutex Methods Object\n**\n** An instance of this structure defines the low-level routines\n** used to allocate and use mutexes.\n**\n** Usually, the default mutex implementations provided by SQLite are\n** sufficient, however the user has the option of substituting a custom\n** implementation for specialized deployments or systems for which SQLite\n** does not provide a suitable implementation. In this case, the user\n** creates and populates an instance of this structure to pass\n** to sqlite3_config() along with the [SQLITE_CONFIG_MUTEX] option.\n** Additionally, an instance of this structure can be used as an\n** output variable when querying the system for the current mutex\n** implementation, using the [SQLITE_CONFIG_GETMUTEX] option.\n**\n** ^The xMutexInit method defined by this structure is invoked as\n** part of system initialization by the sqlite3_initialize() function.\n** ^The xMutexInit routine is called by SQLite exactly once for each\n** effective call to [sqlite3_initialize()].\n**\n** ^The xMutexEnd method defined by this structure is invoked as\n** part of system shutdown by the sqlite3_shutdown() function. The\n** implementation of this method is expected to release all outstanding\n** resources obtained by the mutex methods implementation, especially\n** those obtained by the xMutexInit method.  ^The xMutexEnd()\n** interface is invoked exactly once for each call to [sqlite3_shutdown()].\n**\n** ^(The remaining seven methods defined by this structure (xMutexAlloc,\n** xMutexFree, xMutexEnter, xMutexTry, xMutexLeave, xMutexHeld and\n** xMutexNotheld) implement the following interfaces (respectively):\n**\n** <ul>\n**   <li>  [sqlite3_mutex_alloc()] </li>\n**   <li>  [sqlite3_mutex_free()] </li>\n**   <li>  [sqlite3_mutex_enter()] </li>\n**   <li>  [sqlite3_mutex_try()] </li>\n**   <li>  [sqlite3_mutex_leave()] </li>\n**   <li>  [sqlite3_mutex_held()] </li>\n**   <li>  [sqlite3_mutex_notheld()] </li>\n** </ul>)^\n**\n** The only difference is that the public sqlite3_XXX functions enumerated\n** above silently ignore any invocations that pass a NULL pointer instead\n** of a valid mutex handle. The implementations of the methods defined\n** by this structure are not required to handle this case, the results\n** of passing a NULL pointer instead of a valid mutex handle are undefined\n** (i.e. it is acceptable to provide an implementation that segfaults if\n** it is passed a NULL pointer).\n**\n** The xMutexInit() method must be threadsafe.  ^It must be harmless to\n** invoke xMutexInit() multiple times within the same process and without\n** intervening calls to xMutexEnd().  Second and subsequent calls to\n** xMutexInit() must be no-ops.\n**\n** ^xMutexInit() must not use SQLite memory allocation ([sqlite3_malloc()]\n** and its associates).  ^Similarly, xMutexAlloc() must not use SQLite memory\n** allocation for a static mutex.  ^However xMutexAlloc() may use SQLite\n** memory allocation for a fast or recursive mutex.\n**\n** ^SQLite will invoke the xMutexEnd() method when [sqlite3_shutdown()] is\n** called, but only if the prior call to xMutexInit returned SQLITE_OK.\n** If xMutexInit fails in any way, it is expected to clean up after itself\n** prior to returning.\n*/\ntypedef struct sqlite3_mutex_methods sqlite3_mutex_methods;\nstruct sqlite3_mutex_methods {\n  int (*xMutexInit)(void);\n  int (*xMutexEnd)(void);\n  sqlite3_mutex *(*xMutexAlloc)(int);\n  void (*xMutexFree)(sqlite3_mutex *);\n  void (*xMutexEnter)(sqlite3_mutex *);\n  int (*xMutexTry)(sqlite3_mutex *);\n  void (*xMutexLeave)(sqlite3_mutex *);\n  int (*xMutexHeld)(sqlite3_mutex *);\n  int (*xMutexNotheld)(sqlite3_mutex *);\n};\n\n/*\n** CAPI3REF: Mutex Verification Routines\n**\n** The sqlite3_mutex_held() and sqlite3_mutex_notheld() routines\n** are intended for use inside assert() statements.  ^The SQLite core\n** never uses these routines except inside an assert() and applications\n** are advised to follow the lead of the core.  ^The SQLite core only\n** provides implementations for these routines when it is compiled\n** with the SQLITE_DEBUG flag.  ^External mutex implementations\n** are only required to provide these routines if SQLITE_DEBUG is\n** defined and if NDEBUG is not defined.\n**\n** ^These routines should return true if the mutex in their argument\n** is held or not held, respectively, by the calling thread.\n**\n** ^The implementation is not required to provide versions of these\n** routines that actually work. If the implementation does not provide working\n** versions of these routines, it should at least provide stubs that always\n** return true so that one does not get spurious assertion failures.\n**\n** ^If the argument to sqlite3_mutex_held() is a NULL pointer then\n** the routine should return 1.   This seems counter-intuitive since\n** clearly the mutex cannot be held if it does not exist.  But\n** the reason the mutex does not exist is because the build is not\n** using mutexes.  And we do not want the assert() containing the\n** call to sqlite3_mutex_held() to fail, so a non-zero return is\n** the appropriate thing to do.  ^The sqlite3_mutex_notheld()\n** interface should also return 1 when given a NULL pointer.\n*/\n#ifndef NDEBUG\nSQLITE_API int sqlite3_mutex_held(sqlite3_mutex*);\nSQLITE_API int sqlite3_mutex_notheld(sqlite3_mutex*);\n#endif\n\n/*\n** CAPI3REF: Mutex Types\n**\n** The [sqlite3_mutex_alloc()] interface takes a single argument\n** which is one of these integer constants.\n**\n** The set of static mutexes may change from one SQLite release to the\n** next.  Applications that override the built-in mutex logic must be\n** prepared to accommodate additional static mutexes.\n*/\n#define SQLITE_MUTEX_FAST             0\n#define SQLITE_MUTEX_RECURSIVE        1\n#define SQLITE_MUTEX_STATIC_MASTER    2\n#define SQLITE_MUTEX_STATIC_MEM       3  /* sqlite3_malloc() */\n#define SQLITE_MUTEX_STATIC_MEM2      4  /* NOT USED */\n#define SQLITE_MUTEX_STATIC_OPEN      4  /* sqlite3BtreeOpen() */\n#define SQLITE_MUTEX_STATIC_PRNG      5  /* sqlite3_random() */\n#define SQLITE_MUTEX_STATIC_LRU       6  /* lru page list */\n#define SQLITE_MUTEX_STATIC_LRU2      7  /* NOT USED */\n#define SQLITE_MUTEX_STATIC_PMEM      7  /* sqlite3PageMalloc() */\n\n/*\n** CAPI3REF: Retrieve the mutex for a database connection\n**\n** ^This interface returns a pointer the [sqlite3_mutex] object that \n** serializes access to the [database connection] given in the argument\n** when the [threading mode] is Serialized.\n** ^If the [threading mode] is Single-thread or Multi-thread then this\n** routine returns a NULL pointer.\n*/\nSQLITE_API sqlite3_mutex *sqlite3_db_mutex(sqlite3*);\n\n/*\n** CAPI3REF: Low-Level Control Of Database Files\n**\n** ^The [sqlite3_file_control()] interface makes a direct call to the\n** xFileControl method for the [sqlite3_io_methods] object associated\n** with a particular database identified by the second argument. ^The\n** name of the database is \"main\" for the main database or \"temp\" for the\n** TEMP database, or the name that appears after the AS keyword for\n** databases that are added using the [ATTACH] SQL command.\n** ^A NULL pointer can be used in place of \"main\" to refer to the\n** main database file.\n** ^The third and fourth parameters to this routine\n** are passed directly through to the second and third parameters of\n** the xFileControl method.  ^The return value of the xFileControl\n** method becomes the return value of this routine.\n**\n** ^The SQLITE_FCNTL_FILE_POINTER value for the op parameter causes\n** a pointer to the underlying [sqlite3_file] object to be written into\n** the space pointed to by the 4th parameter.  ^The SQLITE_FCNTL_FILE_POINTER\n** case is a short-circuit path which does not actually invoke the\n** underlying sqlite3_io_methods.xFileControl method.\n**\n** ^If the second parameter (zDbName) does not match the name of any\n** open database file, then SQLITE_ERROR is returned.  ^This error\n** code is not remembered and will not be recalled by [sqlite3_errcode()]\n** or [sqlite3_errmsg()].  The underlying xFileControl method might\n** also return SQLITE_ERROR.  There is no way to distinguish between\n** an incorrect zDbName and an SQLITE_ERROR return from the underlying\n** xFileControl method.\n**\n** See also: [SQLITE_FCNTL_LOCKSTATE]\n*/\nSQLITE_API int sqlite3_file_control(sqlite3*, const char *zDbName, int op, void*);\n\n/*\n** CAPI3REF: Testing Interface\n**\n** ^The sqlite3_test_control() interface is used to read out internal\n** state of SQLite and to inject faults into SQLite for testing\n** purposes.  ^The first parameter is an operation code that determines\n** the number, meaning, and operation of all subsequent parameters.\n**\n** This interface is not for use by applications.  It exists solely\n** for verifying the correct operation of the SQLite library.  Depending\n** on how the SQLite library is compiled, this interface might not exist.\n**\n** The details of the operation codes, their meanings, the parameters\n** they take, and what they do are all subject to change without notice.\n** Unlike most of the SQLite API, this function is not guaranteed to\n** operate consistently from one release to the next.\n*/\nSQLITE_API int sqlite3_test_control(int op, ...);\n\n/*\n** CAPI3REF: Testing Interface Operation Codes\n**\n** These constants are the valid operation code parameters used\n** as the first argument to [sqlite3_test_control()].\n**\n** These parameters and their meanings are subject to change\n** without notice.  These values are for testing purposes only.\n** Applications should not use any of these parameters or the\n** [sqlite3_test_control()] interface.\n*/\n#define SQLITE_TESTCTRL_FIRST                    5\n#define SQLITE_TESTCTRL_PRNG_SAVE                5\n#define SQLITE_TESTCTRL_PRNG_RESTORE             6\n#define SQLITE_TESTCTRL_PRNG_RESET               7\n#define SQLITE_TESTCTRL_BITVEC_TEST              8\n#define SQLITE_TESTCTRL_FAULT_INSTALL            9\n#define SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS     10\n#define SQLITE_TESTCTRL_PENDING_BYTE            11\n#define SQLITE_TESTCTRL_ASSERT                  12\n#define SQLITE_TESTCTRL_ALWAYS                  13\n#define SQLITE_TESTCTRL_RESERVE                 14\n#define SQLITE_TESTCTRL_OPTIMIZATIONS           15\n#define SQLITE_TESTCTRL_ISKEYWORD               16\n#define SQLITE_TESTCTRL_SCRATCHMALLOC           17\n#define SQLITE_TESTCTRL_LOCALTIME_FAULT         18\n#define SQLITE_TESTCTRL_EXPLAIN_STMT            19\n#define SQLITE_TESTCTRL_LAST                    19\n\n/*\n** CAPI3REF: SQLite Runtime Status\n**\n** ^This interface is used to retrieve runtime status information\n** about the performance of SQLite, and optionally to reset various\n** highwater marks.  ^The first argument is an integer code for\n** the specific parameter to measure.  ^(Recognized integer codes\n** are of the form [status parameters | SQLITE_STATUS_...].)^\n** ^The current value of the parameter is returned into *pCurrent.\n** ^The highest recorded value is returned in *pHighwater.  ^If the\n** resetFlag is true, then the highest record value is reset after\n** *pHighwater is written.  ^(Some parameters do not record the highest\n** value.  For those parameters\n** nothing is written into *pHighwater and the resetFlag is ignored.)^\n** ^(Other parameters record only the highwater mark and not the current\n** value.  For these latter parameters nothing is written into *pCurrent.)^\n**\n** ^The sqlite3_status() routine returns SQLITE_OK on success and a\n** non-zero [error code] on failure.\n**\n** This routine is threadsafe but is not atomic.  This routine can be\n** called while other threads are running the same or different SQLite\n** interfaces.  However the values returned in *pCurrent and\n** *pHighwater reflect the status of SQLite at different points in time\n** and it is possible that another thread might change the parameter\n** in between the times when *pCurrent and *pHighwater are written.\n**\n** See also: [sqlite3_db_status()]\n*/\nSQLITE_API int sqlite3_status(int op, int *pCurrent, int *pHighwater, int resetFlag);\n\n\n/*\n** CAPI3REF: Status Parameters\n** KEYWORDS: {status parameters}\n**\n** These integer constants designate various run-time status parameters\n** that can be returned by [sqlite3_status()].\n**\n** <dl>\n** [[SQLITE_STATUS_MEMORY_USED]] ^(<dt>SQLITE_STATUS_MEMORY_USED</dt>\n** <dd>This parameter is the current amount of memory checked out\n** using [sqlite3_malloc()], either directly or indirectly.  The\n** figure includes calls made to [sqlite3_malloc()] by the application\n** and internal memory usage by the SQLite library.  Scratch memory\n** controlled by [SQLITE_CONFIG_SCRATCH] and auxiliary page-cache\n** memory controlled by [SQLITE_CONFIG_PAGECACHE] is not included in\n** this parameter.  The amount returned is the sum of the allocation\n** sizes as reported by the xSize method in [sqlite3_mem_methods].</dd>)^\n**\n** [[SQLITE_STATUS_MALLOC_SIZE]] ^(<dt>SQLITE_STATUS_MALLOC_SIZE</dt>\n** <dd>This parameter records the largest memory allocation request\n** handed to [sqlite3_malloc()] or [sqlite3_realloc()] (or their\n** internal equivalents).  Only the value returned in the\n** *pHighwater parameter to [sqlite3_status()] is of interest.  \n** The value written into the *pCurrent parameter is undefined.</dd>)^\n**\n** [[SQLITE_STATUS_MALLOC_COUNT]] ^(<dt>SQLITE_STATUS_MALLOC_COUNT</dt>\n** <dd>This parameter records the number of separate memory allocations\n** currently checked out.</dd>)^\n**\n** [[SQLITE_STATUS_PAGECACHE_USED]] ^(<dt>SQLITE_STATUS_PAGECACHE_USED</dt>\n** <dd>This parameter returns the number of pages used out of the\n** [pagecache memory allocator] that was configured using \n** [SQLITE_CONFIG_PAGECACHE].  The\n** value returned is in pages, not in bytes.</dd>)^\n**\n** [[SQLITE_STATUS_PAGECACHE_OVERFLOW]] \n** ^(<dt>SQLITE_STATUS_PAGECACHE_OVERFLOW</dt>\n** <dd>This parameter returns the number of bytes of page cache\n** allocation which could not be satisfied by the [SQLITE_CONFIG_PAGECACHE]\n** buffer and where forced to overflow to [sqlite3_malloc()].  The\n** returned value includes allocations that overflowed because they\n** where too large (they were larger than the \"sz\" parameter to\n** [SQLITE_CONFIG_PAGECACHE]) and allocations that overflowed because\n** no space was left in the page cache.</dd>)^\n**\n** [[SQLITE_STATUS_PAGECACHE_SIZE]] ^(<dt>SQLITE_STATUS_PAGECACHE_SIZE</dt>\n** <dd>This parameter records the largest memory allocation request\n** handed to [pagecache memory allocator].  Only the value returned in the\n** *pHighwater parameter to [sqlite3_status()] is of interest.  \n** The value written into the *pCurrent parameter is undefined.</dd>)^\n**\n** [[SQLITE_STATUS_SCRATCH_USED]] ^(<dt>SQLITE_STATUS_SCRATCH_USED</dt>\n** <dd>This parameter returns the number of allocations used out of the\n** [scratch memory allocator] configured using\n** [SQLITE_CONFIG_SCRATCH].  The value returned is in allocations, not\n** in bytes.  Since a single thread may only have one scratch allocation\n** outstanding at time, this parameter also reports the number of threads\n** using scratch memory at the same time.</dd>)^\n**\n** [[SQLITE_STATUS_SCRATCH_OVERFLOW]] ^(<dt>SQLITE_STATUS_SCRATCH_OVERFLOW</dt>\n** <dd>This parameter returns the number of bytes of scratch memory\n** allocation which could not be satisfied by the [SQLITE_CONFIG_SCRATCH]\n** buffer and where forced to overflow to [sqlite3_malloc()].  The values\n** returned include overflows because the requested allocation was too\n** larger (that is, because the requested allocation was larger than the\n** \"sz\" parameter to [SQLITE_CONFIG_SCRATCH]) and because no scratch buffer\n** slots were available.\n** </dd>)^\n**\n** [[SQLITE_STATUS_SCRATCH_SIZE]] ^(<dt>SQLITE_STATUS_SCRATCH_SIZE</dt>\n** <dd>This parameter records the largest memory allocation request\n** handed to [scratch memory allocator].  Only the value returned in the\n** *pHighwater parameter to [sqlite3_status()] is of interest.  \n** The value written into the *pCurrent parameter is undefined.</dd>)^\n**\n** [[SQLITE_STATUS_PARSER_STACK]] ^(<dt>SQLITE_STATUS_PARSER_STACK</dt>\n** <dd>This parameter records the deepest parser stack.  It is only\n** meaningful if SQLite is compiled with [YYTRACKMAXSTACKDEPTH].</dd>)^\n** </dl>\n**\n** New status parameters may be added from time to time.\n*/\n#define SQLITE_STATUS_MEMORY_USED          0\n#define SQLITE_STATUS_PAGECACHE_USED       1\n#define SQLITE_STATUS_PAGECACHE_OVERFLOW   2\n#define SQLITE_STATUS_SCRATCH_USED         3\n#define SQLITE_STATUS_SCRATCH_OVERFLOW     4\n#define SQLITE_STATUS_MALLOC_SIZE          5\n#define SQLITE_STATUS_PARSER_STACK         6\n#define SQLITE_STATUS_PAGECACHE_SIZE       7\n#define SQLITE_STATUS_SCRATCH_SIZE         8\n#define SQLITE_STATUS_MALLOC_COUNT         9\n\n/*\n** CAPI3REF: Database Connection Status\n**\n** ^This interface is used to retrieve runtime status information \n** about a single [database connection].  ^The first argument is the\n** database connection object to be interrogated.  ^The second argument\n** is an integer constant, taken from the set of\n** [SQLITE_DBSTATUS options], that\n** determines the parameter to interrogate.  The set of \n** [SQLITE_DBSTATUS options] is likely\n** to grow in future releases of SQLite.\n**\n** ^The current value of the requested parameter is written into *pCur\n** and the highest instantaneous value is written into *pHiwtr.  ^If\n** the resetFlg is true, then the highest instantaneous value is\n** reset back down to the current value.\n**\n** ^The sqlite3_db_status() routine returns SQLITE_OK on success and a\n** non-zero [error code] on failure.\n**\n** See also: [sqlite3_status()] and [sqlite3_stmt_status()].\n*/\nSQLITE_API int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int resetFlg);\n\n/*\n** CAPI3REF: Status Parameters for database connections\n** KEYWORDS: {SQLITE_DBSTATUS options}\n**\n** These constants are the available integer \"verbs\" that can be passed as\n** the second argument to the [sqlite3_db_status()] interface.\n**\n** New verbs may be added in future releases of SQLite. Existing verbs\n** might be discontinued. Applications should check the return code from\n** [sqlite3_db_status()] to make sure that the call worked.\n** The [sqlite3_db_status()] interface will return a non-zero error code\n** if a discontinued or unsupported verb is invoked.\n**\n** <dl>\n** [[SQLITE_DBSTATUS_LOOKASIDE_USED]] ^(<dt>SQLITE_DBSTATUS_LOOKASIDE_USED</dt>\n** <dd>This parameter returns the number of lookaside memory slots currently\n** checked out.</dd>)^\n**\n** [[SQLITE_DBSTATUS_LOOKASIDE_HIT]] ^(<dt>SQLITE_DBSTATUS_LOOKASIDE_HIT</dt>\n** <dd>This parameter returns the number malloc attempts that were \n** satisfied using lookaside memory. Only the high-water value is meaningful;\n** the current value is always zero.)^\n**\n** [[SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE]]\n** ^(<dt>SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE</dt>\n** <dd>This parameter returns the number malloc attempts that might have\n** been satisfied using lookaside memory but failed due to the amount of\n** memory requested being larger than the lookaside slot size.\n** Only the high-water value is meaningful;\n** the current value is always zero.)^\n**\n** [[SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL]]\n** ^(<dt>SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL</dt>\n** <dd>This parameter returns the number malloc attempts that might have\n** been satisfied using lookaside memory but failed due to all lookaside\n** memory already being in use.\n** Only the high-water value is meaningful;\n** the current value is always zero.)^\n**\n** [[SQLITE_DBSTATUS_CACHE_USED]] ^(<dt>SQLITE_DBSTATUS_CACHE_USED</dt>\n** <dd>This parameter returns the approximate number of of bytes of heap\n** memory used by all pager caches associated with the database connection.)^\n** ^The highwater mark associated with SQLITE_DBSTATUS_CACHE_USED is always 0.\n**\n** [[SQLITE_DBSTATUS_SCHEMA_USED]] ^(<dt>SQLITE_DBSTATUS_SCHEMA_USED</dt>\n** <dd>This parameter returns the approximate number of of bytes of heap\n** memory used to store the schema for all databases associated\n** with the connection - main, temp, and any [ATTACH]-ed databases.)^ \n** ^The full amount of memory used by the schemas is reported, even if the\n** schema memory is shared with other database connections due to\n** [shared cache mode] being enabled.\n** ^The highwater mark associated with SQLITE_DBSTATUS_SCHEMA_USED is always 0.\n**\n** [[SQLITE_DBSTATUS_STMT_USED]] ^(<dt>SQLITE_DBSTATUS_STMT_USED</dt>\n** <dd>This parameter returns the approximate number of of bytes of heap\n** and lookaside memory used by all prepared statements associated with\n** the database connection.)^\n** ^The highwater mark associated with SQLITE_DBSTATUS_STMT_USED is always 0.\n** </dd>\n**\n** [[SQLITE_DBSTATUS_CACHE_HIT]] ^(<dt>SQLITE_DBSTATUS_CACHE_HIT</dt>\n** <dd>This parameter returns the number of pager cache hits that have\n** occurred.)^ ^The highwater mark associated with SQLITE_DBSTATUS_CACHE_HIT \n** is always 0.\n** </dd>\n**\n** [[SQLITE_DBSTATUS_CACHE_MISS]] ^(<dt>SQLITE_DBSTATUS_CACHE_MISS</dt>\n** <dd>This parameter returns the number of pager cache misses that have\n** occurred.)^ ^The highwater mark associated with SQLITE_DBSTATUS_CACHE_MISS \n** is always 0.\n** </dd>\n**\n** [[SQLITE_DBSTATUS_CACHE_WRITE]] ^(<dt>SQLITE_DBSTATUS_CACHE_WRITE</dt>\n** <dd>This parameter returns the number of dirty cache entries that have\n** been written to disk. Specifically, the number of pages written to the\n** wal file in wal mode databases, or the number of pages written to the\n** database file in rollback mode databases. Any pages written as part of\n** transaction rollback or database recovery operations are not included.\n** If an IO or other error occurs while writing a page to disk, the effect\n** on subsequent SQLITE_DBSTATUS_CACHE_WRITE requests is undefined.)^ ^The\n** highwater mark associated with SQLITE_DBSTATUS_CACHE_WRITE is always 0.\n** </dd>\n** </dl>\n*/\n#define SQLITE_DBSTATUS_LOOKASIDE_USED       0\n#define SQLITE_DBSTATUS_CACHE_USED           1\n#define SQLITE_DBSTATUS_SCHEMA_USED          2\n#define SQLITE_DBSTATUS_STMT_USED            3\n#define SQLITE_DBSTATUS_LOOKASIDE_HIT        4\n#define SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE  5\n#define SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL  6\n#define SQLITE_DBSTATUS_CACHE_HIT            7\n#define SQLITE_DBSTATUS_CACHE_MISS           8\n#define SQLITE_DBSTATUS_CACHE_WRITE          9\n#define SQLITE_DBSTATUS_MAX                  9   /* Largest defined DBSTATUS */\n\n\n/*\n** CAPI3REF: Prepared Statement Status\n**\n** ^(Each prepared statement maintains various\n** [SQLITE_STMTSTATUS counters] that measure the number\n** of times it has performed specific operations.)^  These counters can\n** be used to monitor the performance characteristics of the prepared\n** statements.  For example, if the number of table steps greatly exceeds\n** the number of table searches or result rows, that would tend to indicate\n** that the prepared statement is using a full table scan rather than\n** an index.  \n**\n** ^(This interface is used to retrieve and reset counter values from\n** a [prepared statement].  The first argument is the prepared statement\n** object to be interrogated.  The second argument\n** is an integer code for a specific [SQLITE_STMTSTATUS counter]\n** to be interrogated.)^\n** ^The current value of the requested counter is returned.\n** ^If the resetFlg is true, then the counter is reset to zero after this\n** interface call returns.\n**\n** See also: [sqlite3_status()] and [sqlite3_db_status()].\n*/\nSQLITE_API int sqlite3_stmt_status(sqlite3_stmt*, int op,int resetFlg);\n\n/*\n** CAPI3REF: Status Parameters for prepared statements\n** KEYWORDS: {SQLITE_STMTSTATUS counter} {SQLITE_STMTSTATUS counters}\n**\n** These preprocessor macros define integer codes that name counter\n** values associated with the [sqlite3_stmt_status()] interface.\n** The meanings of the various counters are as follows:\n**\n** <dl>\n** [[SQLITE_STMTSTATUS_FULLSCAN_STEP]] <dt>SQLITE_STMTSTATUS_FULLSCAN_STEP</dt>\n** <dd>^This is the number of times that SQLite has stepped forward in\n** a table as part of a full table scan.  Large numbers for this counter\n** may indicate opportunities for performance improvement through \n** careful use of indices.</dd>\n**\n** [[SQLITE_STMTSTATUS_SORT]] <dt>SQLITE_STMTSTATUS_SORT</dt>\n** <dd>^This is the number of sort operations that have occurred.\n** A non-zero value in this counter may indicate an opportunity to\n** improvement performance through careful use of indices.</dd>\n**\n** [[SQLITE_STMTSTATUS_AUTOINDEX]] <dt>SQLITE_STMTSTATUS_AUTOINDEX</dt>\n** <dd>^This is the number of rows inserted into transient indices that\n** were created automatically in order to help joins run faster.\n** A non-zero value in this counter may indicate an opportunity to\n** improvement performance by adding permanent indices that do not\n** need to be reinitialized each time the statement is run.</dd>\n** </dl>\n*/\n#define SQLITE_STMTSTATUS_FULLSCAN_STEP     1\n#define SQLITE_STMTSTATUS_SORT              2\n#define SQLITE_STMTSTATUS_AUTOINDEX         3\n\n/*\n** CAPI3REF: Custom Page Cache Object\n**\n** The sqlite3_pcache type is opaque.  It is implemented by\n** the pluggable module.  The SQLite core has no knowledge of\n** its size or internal structure and never deals with the\n** sqlite3_pcache object except by holding and passing pointers\n** to the object.\n**\n** See [sqlite3_pcache_methods2] for additional information.\n*/\ntypedef struct sqlite3_pcache sqlite3_pcache;\n\n/*\n** CAPI3REF: Custom Page Cache Object\n**\n** The sqlite3_pcache_page object represents a single page in the\n** page cache.  The page cache will allocate instances of this\n** object.  Various methods of the page cache use pointers to instances\n** of this object as parameters or as their return value.\n**\n** See [sqlite3_pcache_methods2] for additional information.\n*/\ntypedef struct sqlite3_pcache_page sqlite3_pcache_page;\nstruct sqlite3_pcache_page {\n  void *pBuf;        /* The content of the page */\n  void *pExtra;      /* Extra information associated with the page */\n};\n\n/*\n** CAPI3REF: Application Defined Page Cache.\n** KEYWORDS: {page cache}\n**\n** ^(The [sqlite3_config]([SQLITE_CONFIG_PCACHE2], ...) interface can\n** register an alternative page cache implementation by passing in an \n** instance of the sqlite3_pcache_methods2 structure.)^\n** In many applications, most of the heap memory allocated by \n** SQLite is used for the page cache.\n** By implementing a \n** custom page cache using this API, an application can better control\n** the amount of memory consumed by SQLite, the way in which \n** that memory is allocated and released, and the policies used to \n** determine exactly which parts of a database file are cached and for \n** how long.\n**\n** The alternative page cache mechanism is an\n** extreme measure that is only needed by the most demanding applications.\n** The built-in page cache is recommended for most uses.\n**\n** ^(The contents of the sqlite3_pcache_methods2 structure are copied to an\n** internal buffer by SQLite within the call to [sqlite3_config].  Hence\n** the application may discard the parameter after the call to\n** [sqlite3_config()] returns.)^\n**\n** [[the xInit() page cache method]]\n** ^(The xInit() method is called once for each effective \n** call to [sqlite3_initialize()])^\n** (usually only once during the lifetime of the process). ^(The xInit()\n** method is passed a copy of the sqlite3_pcache_methods2.pArg value.)^\n** The intent of the xInit() method is to set up global data structures \n** required by the custom page cache implementation. \n** ^(If the xInit() method is NULL, then the \n** built-in default page cache is used instead of the application defined\n** page cache.)^\n**\n** [[the xShutdown() page cache method]]\n** ^The xShutdown() method is called by [sqlite3_shutdown()].\n** It can be used to clean up \n** any outstanding resources before process shutdown, if required.\n** ^The xShutdown() method may be NULL.\n**\n** ^SQLite automatically serializes calls to the xInit method,\n** so the xInit method need not be threadsafe.  ^The\n** xShutdown method is only called from [sqlite3_shutdown()] so it does\n** not need to be threadsafe either.  All other methods must be threadsafe\n** in multithreaded applications.\n**\n** ^SQLite will never invoke xInit() more than once without an intervening\n** call to xShutdown().\n**\n** [[the xCreate() page cache methods]]\n** ^SQLite invokes the xCreate() method to construct a new cache instance.\n** SQLite will typically create one cache instance for each open database file,\n** though this is not guaranteed. ^The\n** first parameter, szPage, is the size in bytes of the pages that must\n** be allocated by the cache.  ^szPage will always a power of two.  ^The\n** second parameter szExtra is a number of bytes of extra storage \n** associated with each page cache entry.  ^The szExtra parameter will\n** a number less than 250.  SQLite will use the\n** extra szExtra bytes on each page to store metadata about the underlying\n** database page on disk.  The value passed into szExtra depends\n** on the SQLite version, the target platform, and how SQLite was compiled.\n** ^The third argument to xCreate(), bPurgeable, is true if the cache being\n** created will be used to cache database pages of a file stored on disk, or\n** false if it is used for an in-memory database. The cache implementation\n** does not have to do anything special based with the value of bPurgeable;\n** it is purely advisory.  ^On a cache where bPurgeable is false, SQLite will\n** never invoke xUnpin() except to deliberately delete a page.\n** ^In other words, calls to xUnpin() on a cache with bPurgeable set to\n** false will always have the \"discard\" flag set to true.  \n** ^Hence, a cache created with bPurgeable false will\n** never contain any unpinned pages.\n**\n** [[the xCachesize() page cache method]]\n** ^(The xCachesize() method may be called at any time by SQLite to set the\n** suggested maximum cache-size (number of pages stored by) the cache\n** instance passed as the first argument. This is the value configured using\n** the SQLite \"[PRAGMA cache_size]\" command.)^  As with the bPurgeable\n** parameter, the implementation is not required to do anything with this\n** value; it is advisory only.\n**\n** [[the xPagecount() page cache methods]]\n** The xPagecount() method must return the number of pages currently\n** stored in the cache, both pinned and unpinned.\n** \n** [[the xFetch() page cache methods]]\n** The xFetch() method locates a page in the cache and returns a pointer to \n** an sqlite3_pcache_page object associated with that page, or a NULL pointer.\n** The pBuf element of the returned sqlite3_pcache_page object will be a\n** pointer to a buffer of szPage bytes used to store the content of a \n** single database page.  The pExtra element of sqlite3_pcache_page will be\n** a pointer to the szExtra bytes of extra storage that SQLite has requested\n** for each entry in the page cache.\n**\n** The page to be fetched is determined by the key. ^The minimum key value\n** is 1.  After it has been retrieved using xFetch, the page is considered\n** to be \"pinned\".\n**\n** If the requested page is already in the page cache, then the page cache\n** implementation must return a pointer to the page buffer with its content\n** intact.  If the requested page is not already in the cache, then the\n** cache implementation should use the value of the createFlag\n** parameter to help it determined what action to take:\n**\n** <table border=1 width=85% align=center>\n** <tr><th> createFlag <th> Behaviour when page is not already in cache\n** <tr><td> 0 <td> Do not allocate a new page.  Return NULL.\n** <tr><td> 1 <td> Allocate a new page if it easy and convenient to do so.\n**                 Otherwise return NULL.\n** <tr><td> 2 <td> Make every effort to allocate a new page.  Only return\n**                 NULL if allocating a new page is effectively impossible.\n** </table>\n**\n** ^(SQLite will normally invoke xFetch() with a createFlag of 0 or 1.  SQLite\n** will only use a createFlag of 2 after a prior call with a createFlag of 1\n** failed.)^  In between the to xFetch() calls, SQLite may\n** attempt to unpin one or more cache pages by spilling the content of\n** pinned pages to disk and synching the operating system disk cache.\n**\n** [[the xUnpin() page cache method]]\n** ^xUnpin() is called by SQLite with a pointer to a currently pinned page\n** as its second argument.  If the third parameter, discard, is non-zero,\n** then the page must be evicted from the cache.\n** ^If the discard parameter is\n** zero, then the page may be discarded or retained at the discretion of\n** page cache implementation. ^The page cache implementation\n** may choose to evict unpinned pages at any time.\n**\n** The cache must not perform any reference counting. A single \n** call to xUnpin() unpins the page regardless of the number of prior calls \n** to xFetch().\n**\n** [[the xRekey() page cache methods]]\n** The xRekey() method is used to change the key value associated with the\n** page passed as the second argument. If the cache\n** previously contains an entry associated with newKey, it must be\n** discarded. ^Any prior cache entry associated with newKey is guaranteed not\n** to be pinned.\n**\n** When SQLite calls the xTruncate() method, the cache must discard all\n** existing cache entries with page numbers (keys) greater than or equal\n** to the value of the iLimit parameter passed to xTruncate(). If any\n** of these pages are pinned, they are implicitly unpinned, meaning that\n** they can be safely discarded.\n**\n** [[the xDestroy() page cache method]]\n** ^The xDestroy() method is used to delete a cache allocated by xCreate().\n** All resources associated with the specified cache should be freed. ^After\n** calling the xDestroy() method, SQLite considers the [sqlite3_pcache*]\n** handle invalid, and will not use it with any other sqlite3_pcache_methods2\n** functions.\n**\n** [[the xShrink() page cache method]]\n** ^SQLite invokes the xShrink() method when it wants the page cache to\n** free up as much of heap memory as possible.  The page cache implementation\n** is not obligated to free any memory, but well-behaved implementations should\n** do their best.\n*/\ntypedef struct sqlite3_pcache_methods2 sqlite3_pcache_methods2;\nstruct sqlite3_pcache_methods2 {\n  int iVersion;\n  void *pArg;\n  int (*xInit)(void*);\n  void (*xShutdown)(void*);\n  sqlite3_pcache *(*xCreate)(int szPage, int szExtra, int bPurgeable);\n  void (*xCachesize)(sqlite3_pcache*, int nCachesize);\n  int (*xPagecount)(sqlite3_pcache*);\n  sqlite3_pcache_page *(*xFetch)(sqlite3_pcache*, unsigned key, int createFlag);\n  void (*xUnpin)(sqlite3_pcache*, sqlite3_pcache_page*, int discard);\n  void (*xRekey)(sqlite3_pcache*, sqlite3_pcache_page*, \n      unsigned oldKey, unsigned newKey);\n  void (*xTruncate)(sqlite3_pcache*, unsigned iLimit);\n  void (*xDestroy)(sqlite3_pcache*);\n  void (*xShrink)(sqlite3_pcache*);\n};\n\n/*\n** This is the obsolete pcache_methods object that has now been replaced\n** by sqlite3_pcache_methods2.  This object is not used by SQLite.  It is\n** retained in the header file for backwards compatibility only.\n*/\ntypedef struct sqlite3_pcache_methods sqlite3_pcache_methods;\nstruct sqlite3_pcache_methods {\n  void *pArg;\n  int (*xInit)(void*);\n  void (*xShutdown)(void*);\n  sqlite3_pcache *(*xCreate)(int szPage, int bPurgeable);\n  void (*xCachesize)(sqlite3_pcache*, int nCachesize);\n  int (*xPagecount)(sqlite3_pcache*);\n  void *(*xFetch)(sqlite3_pcache*, unsigned key, int createFlag);\n  void (*xUnpin)(sqlite3_pcache*, void*, int discard);\n  void (*xRekey)(sqlite3_pcache*, void*, unsigned oldKey, unsigned newKey);\n  void (*xTruncate)(sqlite3_pcache*, unsigned iLimit);\n  void (*xDestroy)(sqlite3_pcache*);\n};\n\n\n/*\n** CAPI3REF: Online Backup Object\n**\n** The sqlite3_backup object records state information about an ongoing\n** online backup operation.  ^The sqlite3_backup object is created by\n** a call to [sqlite3_backup_init()] and is destroyed by a call to\n** [sqlite3_backup_finish()].\n**\n** See Also: [Using the SQLite Online Backup API]\n*/\ntypedef struct sqlite3_backup sqlite3_backup;\n\n/*\n** CAPI3REF: Online Backup API.\n**\n** The backup API copies the content of one database into another.\n** It is useful either for creating backups of databases or\n** for copying in-memory databases to or from persistent files. \n**\n** See Also: [Using the SQLite Online Backup API]\n**\n** ^SQLite holds a write transaction open on the destination database file\n** for the duration of the backup operation.\n** ^The source database is read-locked only while it is being read;\n** it is not locked continuously for the entire backup operation.\n** ^Thus, the backup may be performed on a live source database without\n** preventing other database connections from\n** reading or writing to the source database while the backup is underway.\n** \n** ^(To perform a backup operation: \n**   <ol>\n**     <li><b>sqlite3_backup_init()</b> is called once to initialize the\n**         backup, \n**     <li><b>sqlite3_backup_step()</b> is called one or more times to transfer \n**         the data between the two databases, and finally\n**     <li><b>sqlite3_backup_finish()</b> is called to release all resources \n**         associated with the backup operation. \n**   </ol>)^\n** There should be exactly one call to sqlite3_backup_finish() for each\n** successful call to sqlite3_backup_init().\n**\n** [[sqlite3_backup_init()]] <b>sqlite3_backup_init()</b>\n**\n** ^The D and N arguments to sqlite3_backup_init(D,N,S,M) are the \n** [database connection] associated with the destination database \n** and the database name, respectively.\n** ^The database name is \"main\" for the main database, \"temp\" for the\n** temporary database, or the name specified after the AS keyword in\n** an [ATTACH] statement for an attached database.\n** ^The S and M arguments passed to \n** sqlite3_backup_init(D,N,S,M) identify the [database connection]\n** and database name of the source database, respectively.\n** ^The source and destination [database connections] (parameters S and D)\n** must be different or else sqlite3_backup_init(D,N,S,M) will fail with\n** an error.\n**\n** ^If an error occurs within sqlite3_backup_init(D,N,S,M), then NULL is\n** returned and an error code and error message are stored in the\n** destination [database connection] D.\n** ^The error code and message for the failed call to sqlite3_backup_init()\n** can be retrieved using the [sqlite3_errcode()], [sqlite3_errmsg()], and/or\n** [sqlite3_errmsg16()] functions.\n** ^A successful call to sqlite3_backup_init() returns a pointer to an\n** [sqlite3_backup] object.\n** ^The [sqlite3_backup] object may be used with the sqlite3_backup_step() and\n** sqlite3_backup_finish() functions to perform the specified backup \n** operation.\n**\n** [[sqlite3_backup_step()]] <b>sqlite3_backup_step()</b>\n**\n** ^Function sqlite3_backup_step(B,N) will copy up to N pages between \n** the source and destination databases specified by [sqlite3_backup] object B.\n** ^If N is negative, all remaining source pages are copied. \n** ^If sqlite3_backup_step(B,N) successfully copies N pages and there\n** are still more pages to be copied, then the function returns [SQLITE_OK].\n** ^If sqlite3_backup_step(B,N) successfully finishes copying all pages\n** from source to destination, then it returns [SQLITE_DONE].\n** ^If an error occurs while running sqlite3_backup_step(B,N),\n** then an [error code] is returned. ^As well as [SQLITE_OK] and\n** [SQLITE_DONE], a call to sqlite3_backup_step() may return [SQLITE_READONLY],\n** [SQLITE_NOMEM], [SQLITE_BUSY], [SQLITE_LOCKED], or an\n** [SQLITE_IOERR_ACCESS | SQLITE_IOERR_XXX] extended error code.\n**\n** ^(The sqlite3_backup_step() might return [SQLITE_READONLY] if\n** <ol>\n** <li> the destination database was opened read-only, or\n** <li> the destination database is using write-ahead-log journaling\n** and the destination and source page sizes differ, or\n** <li> the destination database is an in-memory database and the\n** destination and source page sizes differ.\n** </ol>)^\n**\n** ^If sqlite3_backup_step() cannot obtain a required file-system lock, then\n** the [sqlite3_busy_handler | busy-handler function]\n** is invoked (if one is specified). ^If the \n** busy-handler returns non-zero before the lock is available, then \n** [SQLITE_BUSY] is returned to the caller. ^In this case the call to\n** sqlite3_backup_step() can be retried later. ^If the source\n** [database connection]\n** is being used to write to the source database when sqlite3_backup_step()\n** is called, then [SQLITE_LOCKED] is returned immediately. ^Again, in this\n** case the call to sqlite3_backup_step() can be retried later on. ^(If\n** [SQLITE_IOERR_ACCESS | SQLITE_IOERR_XXX], [SQLITE_NOMEM], or\n** [SQLITE_READONLY] is returned, then \n** there is no point in retrying the call to sqlite3_backup_step(). These \n** errors are considered fatal.)^  The application must accept \n** that the backup operation has failed and pass the backup operation handle \n** to the sqlite3_backup_finish() to release associated resources.\n**\n** ^The first call to sqlite3_backup_step() obtains an exclusive lock\n** on the destination file. ^The exclusive lock is not released until either \n** sqlite3_backup_finish() is called or the backup operation is complete \n** and sqlite3_backup_step() returns [SQLITE_DONE].  ^Every call to\n** sqlite3_backup_step() obtains a [shared lock] on the source database that\n** lasts for the duration of the sqlite3_backup_step() call.\n** ^Because the source database is not locked between calls to\n** sqlite3_backup_step(), the source database may be modified mid-way\n** through the backup process.  ^If the source database is modified by an\n** external process or via a database connection other than the one being\n** used by the backup operation, then the backup will be automatically\n** restarted by the next call to sqlite3_backup_step(). ^If the source \n** database is modified by the using the same database connection as is used\n** by the backup operation, then the backup database is automatically\n** updated at the same time.\n**\n** [[sqlite3_backup_finish()]] <b>sqlite3_backup_finish()</b>\n**\n** When sqlite3_backup_step() has returned [SQLITE_DONE], or when the \n** application wishes to abandon the backup operation, the application\n** should destroy the [sqlite3_backup] by passing it to sqlite3_backup_finish().\n** ^The sqlite3_backup_finish() interfaces releases all\n** resources associated with the [sqlite3_backup] object. \n** ^If sqlite3_backup_step() has not yet returned [SQLITE_DONE], then any\n** active write-transaction on the destination database is rolled back.\n** The [sqlite3_backup] object is invalid\n** and may not be used following a call to sqlite3_backup_finish().\n**\n** ^The value returned by sqlite3_backup_finish is [SQLITE_OK] if no\n** sqlite3_backup_step() errors occurred, regardless or whether or not\n** sqlite3_backup_step() completed.\n** ^If an out-of-memory condition or IO error occurred during any prior\n** sqlite3_backup_step() call on the same [sqlite3_backup] object, then\n** sqlite3_backup_finish() returns the corresponding [error code].\n**\n** ^A return of [SQLITE_BUSY] or [SQLITE_LOCKED] from sqlite3_backup_step()\n** is not a permanent error and does not affect the return value of\n** sqlite3_backup_finish().\n**\n** [[sqlite3_backup__remaining()]] [[sqlite3_backup_pagecount()]]\n** <b>sqlite3_backup_remaining() and sqlite3_backup_pagecount()</b>\n**\n** ^Each call to sqlite3_backup_step() sets two values inside\n** the [sqlite3_backup] object: the number of pages still to be backed\n** up and the total number of pages in the source database file.\n** The sqlite3_backup_remaining() and sqlite3_backup_pagecount() interfaces\n** retrieve these two values, respectively.\n**\n** ^The values returned by these functions are only updated by\n** sqlite3_backup_step(). ^If the source database is modified during a backup\n** operation, then the values are not updated to account for any extra\n** pages that need to be updated or the size of the source database file\n** changing.\n**\n** <b>Concurrent Usage of Database Handles</b>\n**\n** ^The source [database connection] may be used by the application for other\n** purposes while a backup operation is underway or being initialized.\n** ^If SQLite is compiled and configured to support threadsafe database\n** connections, then the source database connection may be used concurrently\n** from within other threads.\n**\n** However, the application must guarantee that the destination \n** [database connection] is not passed to any other API (by any thread) after \n** sqlite3_backup_init() is called and before the corresponding call to\n** sqlite3_backup_finish().  SQLite does not currently check to see\n** if the application incorrectly accesses the destination [database connection]\n** and so no error code is reported, but the operations may malfunction\n** nevertheless.  Use of the destination database connection while a\n** backup is in progress might also also cause a mutex deadlock.\n**\n** If running in [shared cache mode], the application must\n** guarantee that the shared cache used by the destination database\n** is not accessed while the backup is running. In practice this means\n** that the application must guarantee that the disk file being \n** backed up to is not accessed by any connection within the process,\n** not just the specific connection that was passed to sqlite3_backup_init().\n**\n** The [sqlite3_backup] object itself is partially threadsafe. Multiple \n** threads may safely make multiple concurrent calls to sqlite3_backup_step().\n** However, the sqlite3_backup_remaining() and sqlite3_backup_pagecount()\n** APIs are not strictly speaking threadsafe. If they are invoked at the\n** same time as another thread is invoking sqlite3_backup_step() it is\n** possible that they return invalid values.\n*/\nSQLITE_API sqlite3_backup *sqlite3_backup_init(\n  sqlite3 *pDest,                        /* Destination database handle */\n  const char *zDestName,                 /* Destination database name */\n  sqlite3 *pSource,                      /* Source database handle */\n  const char *zSourceName                /* Source database name */\n);\nSQLITE_API int sqlite3_backup_step(sqlite3_backup *p, int nPage);\nSQLITE_API int sqlite3_backup_finish(sqlite3_backup *p);\nSQLITE_API int sqlite3_backup_remaining(sqlite3_backup *p);\nSQLITE_API int sqlite3_backup_pagecount(sqlite3_backup *p);\n\n/*\n** CAPI3REF: Unlock Notification\n**\n** ^When running in shared-cache mode, a database operation may fail with\n** an [SQLITE_LOCKED] error if the required locks on the shared-cache or\n** individual tables within the shared-cache cannot be obtained. See\n** [SQLite Shared-Cache Mode] for a description of shared-cache locking. \n** ^This API may be used to register a callback that SQLite will invoke \n** when the connection currently holding the required lock relinquishes it.\n** ^This API is only available if the library was compiled with the\n** [SQLITE_ENABLE_UNLOCK_NOTIFY] C-preprocessor symbol defined.\n**\n** See Also: [Using the SQLite Unlock Notification Feature].\n**\n** ^Shared-cache locks are released when a database connection concludes\n** its current transaction, either by committing it or rolling it back. \n**\n** ^When a connection (known as the blocked connection) fails to obtain a\n** shared-cache lock and SQLITE_LOCKED is returned to the caller, the\n** identity of the database connection (the blocking connection) that\n** has locked the required resource is stored internally. ^After an \n** application receives an SQLITE_LOCKED error, it may call the\n** sqlite3_unlock_notify() method with the blocked connection handle as \n** the first argument to register for a callback that will be invoked\n** when the blocking connections current transaction is concluded. ^The\n** callback is invoked from within the [sqlite3_step] or [sqlite3_close]\n** call that concludes the blocking connections transaction.\n**\n** ^(If sqlite3_unlock_notify() is called in a multi-threaded application,\n** there is a chance that the blocking connection will have already\n** concluded its transaction by the time sqlite3_unlock_notify() is invoked.\n** If this happens, then the specified callback is invoked immediately,\n** from within the call to sqlite3_unlock_notify().)^\n**\n** ^If the blocked connection is attempting to obtain a write-lock on a\n** shared-cache table, and more than one other connection currently holds\n** a read-lock on the same table, then SQLite arbitrarily selects one of \n** the other connections to use as the blocking connection.\n**\n** ^(There may be at most one unlock-notify callback registered by a \n** blocked connection. If sqlite3_unlock_notify() is called when the\n** blocked connection already has a registered unlock-notify callback,\n** then the new callback replaces the old.)^ ^If sqlite3_unlock_notify() is\n** called with a NULL pointer as its second argument, then any existing\n** unlock-notify callback is canceled. ^The blocked connections \n** unlock-notify callback may also be canceled by closing the blocked\n** connection using [sqlite3_close()].\n**\n** The unlock-notify callback is not reentrant. If an application invokes\n** any sqlite3_xxx API functions from within an unlock-notify callback, a\n** crash or deadlock may be the result.\n**\n** ^Unless deadlock is detected (see below), sqlite3_unlock_notify() always\n** returns SQLITE_OK.\n**\n** <b>Callback Invocation Details</b>\n**\n** When an unlock-notify callback is registered, the application provides a \n** single void* pointer that is passed to the callback when it is invoked.\n** However, the signature of the callback function allows SQLite to pass\n** it an array of void* context pointers. The first argument passed to\n** an unlock-notify callback is a pointer to an array of void* pointers,\n** and the second is the number of entries in the array.\n**\n** When a blocking connections transaction is concluded, there may be\n** more than one blocked connection that has registered for an unlock-notify\n** callback. ^If two or more such blocked connections have specified the\n** same callback function, then instead of invoking the callback function\n** multiple times, it is invoked once with the set of void* context pointers\n** specified by the blocked connections bundled together into an array.\n** This gives the application an opportunity to prioritize any actions \n** related to the set of unblocked database connections.\n**\n** <b>Deadlock Detection</b>\n**\n** Assuming that after registering for an unlock-notify callback a \n** database waits for the callback to be issued before taking any further\n** action (a reasonable assumption), then using this API may cause the\n** application to deadlock. For example, if connection X is waiting for\n** connection Y's transaction to be concluded, and similarly connection\n** Y is waiting on connection X's transaction, then neither connection\n** will proceed and the system may remain deadlocked indefinitely.\n**\n** To avoid this scenario, the sqlite3_unlock_notify() performs deadlock\n** detection. ^If a given call to sqlite3_unlock_notify() would put the\n** system in a deadlocked state, then SQLITE_LOCKED is returned and no\n** unlock-notify callback is registered. The system is said to be in\n** a deadlocked state if connection A has registered for an unlock-notify\n** callback on the conclusion of connection B's transaction, and connection\n** B has itself registered for an unlock-notify callback when connection\n** A's transaction is concluded. ^Indirect deadlock is also detected, so\n** the system is also considered to be deadlocked if connection B has\n** registered for an unlock-notify callback on the conclusion of connection\n** C's transaction, where connection C is waiting on connection A. ^Any\n** number of levels of indirection are allowed.\n**\n** <b>The \"DROP TABLE\" Exception</b>\n**\n** When a call to [sqlite3_step()] returns SQLITE_LOCKED, it is almost \n** always appropriate to call sqlite3_unlock_notify(). There is however,\n** one exception. When executing a \"DROP TABLE\" or \"DROP INDEX\" statement,\n** SQLite checks if there are any currently executing SELECT statements\n** that belong to the same connection. If there are, SQLITE_LOCKED is\n** returned. In this case there is no \"blocking connection\", so invoking\n** sqlite3_unlock_notify() results in the unlock-notify callback being\n** invoked immediately. If the application then re-attempts the \"DROP TABLE\"\n** or \"DROP INDEX\" query, an infinite loop might be the result.\n**\n** One way around this problem is to check the extended error code returned\n** by an sqlite3_step() call. ^(If there is a blocking connection, then the\n** extended error code is set to SQLITE_LOCKED_SHAREDCACHE. Otherwise, in\n** the special \"DROP TABLE/INDEX\" case, the extended error code is just \n** SQLITE_LOCKED.)^\n*/\nSQLITE_API int sqlite3_unlock_notify(\n  sqlite3 *pBlocked,                          /* Waiting connection */\n  void (*xNotify)(void **apArg, int nArg),    /* Callback function to invoke */\n  void *pNotifyArg                            /* Argument to pass to xNotify */\n);\n\n\n/*\n** CAPI3REF: String Comparison\n**\n** ^The [sqlite3_stricmp()] and [sqlite3_strnicmp()] APIs allow applications\n** and extensions to compare the contents of two buffers containing UTF-8\n** strings in a case-independent fashion, using the same definition of \"case\n** independence\" that SQLite uses internally when comparing identifiers.\n*/\nSQLITE_API int sqlite3_stricmp(const char *, const char *);\nSQLITE_API int sqlite3_strnicmp(const char *, const char *, int);\n\n/*\n** CAPI3REF: Error Logging Interface\n**\n** ^The [sqlite3_log()] interface writes a message into the error log\n** established by the [SQLITE_CONFIG_LOG] option to [sqlite3_config()].\n** ^If logging is enabled, the zFormat string and subsequent arguments are\n** used with [sqlite3_snprintf()] to generate the final output string.\n**\n** The sqlite3_log() interface is intended for use by extensions such as\n** virtual tables, collating functions, and SQL functions.  While there is\n** nothing to prevent an application from calling sqlite3_log(), doing so\n** is considered bad form.\n**\n** The zFormat string must not be NULL.\n**\n** To avoid deadlocks and other threading problems, the sqlite3_log() routine\n** will not use dynamically allocated memory.  The log message is stored in\n** a fixed-length buffer on the stack.  If the log message is longer than\n** a few hundred characters, it will be truncated to the length of the\n** buffer.\n*/\nSQLITE_API void sqlite3_log(int iErrCode, const char *zFormat, ...);\n\n/*\n** CAPI3REF: Write-Ahead Log Commit Hook\n**\n** ^The [sqlite3_wal_hook()] function is used to register a callback that\n** will be invoked each time a database connection commits data to a\n** [write-ahead log] (i.e. whenever a transaction is committed in\n** [journal_mode | journal_mode=WAL mode]). \n**\n** ^The callback is invoked by SQLite after the commit has taken place and \n** the associated write-lock on the database released, so the implementation \n** may read, write or [checkpoint] the database as required.\n**\n** ^The first parameter passed to the callback function when it is invoked\n** is a copy of the third parameter passed to sqlite3_wal_hook() when\n** registering the callback. ^The second is a copy of the database handle.\n** ^The third parameter is the name of the database that was written to -\n** either \"main\" or the name of an [ATTACH]-ed database. ^The fourth parameter\n** is the number of pages currently in the write-ahead log file,\n** including those that were just committed.\n**\n** The callback function should normally return [SQLITE_OK].  ^If an error\n** code is returned, that error will propagate back up through the\n** SQLite code base to cause the statement that provoked the callback\n** to report an error, though the commit will have still occurred. If the\n** callback returns [SQLITE_ROW] or [SQLITE_DONE], or if it returns a value\n** that does not correspond to any valid SQLite error code, the results\n** are undefined.\n**\n** A single database handle may have at most a single write-ahead log callback \n** registered at one time. ^Calling [sqlite3_wal_hook()] replaces any\n** previously registered write-ahead log callback. ^Note that the\n** [sqlite3_wal_autocheckpoint()] interface and the\n** [wal_autocheckpoint pragma] both invoke [sqlite3_wal_hook()] and will\n** those overwrite any prior [sqlite3_wal_hook()] settings.\n*/\nSQLITE_API void *sqlite3_wal_hook(\n  sqlite3*, \n  int(*)(void *,sqlite3*,const char*,int),\n  void*\n);\n\n/*\n** CAPI3REF: Configure an auto-checkpoint\n**\n** ^The [sqlite3_wal_autocheckpoint(D,N)] is a wrapper around\n** [sqlite3_wal_hook()] that causes any database on [database connection] D\n** to automatically [checkpoint]\n** after committing a transaction if there are N or\n** more frames in the [write-ahead log] file.  ^Passing zero or \n** a negative value as the nFrame parameter disables automatic\n** checkpoints entirely.\n**\n** ^The callback registered by this function replaces any existing callback\n** registered using [sqlite3_wal_hook()].  ^Likewise, registering a callback\n** using [sqlite3_wal_hook()] disables the automatic checkpoint mechanism\n** configured by this function.\n**\n** ^The [wal_autocheckpoint pragma] can be used to invoke this interface\n** from SQL.\n**\n** ^Every new [database connection] defaults to having the auto-checkpoint\n** enabled with a threshold of 1000 or [SQLITE_DEFAULT_WAL_AUTOCHECKPOINT]\n** pages.  The use of this interface\n** is only necessary if the default setting is found to be suboptimal\n** for a particular application.\n*/\nSQLITE_API int sqlite3_wal_autocheckpoint(sqlite3 *db, int N);\n\n/*\n** CAPI3REF: Checkpoint a database\n**\n** ^The [sqlite3_wal_checkpoint(D,X)] interface causes database named X\n** on [database connection] D to be [checkpointed].  ^If X is NULL or an\n** empty string, then a checkpoint is run on all databases of\n** connection D.  ^If the database connection D is not in\n** [WAL | write-ahead log mode] then this interface is a harmless no-op.\n**\n** ^The [wal_checkpoint pragma] can be used to invoke this interface\n** from SQL.  ^The [sqlite3_wal_autocheckpoint()] interface and the\n** [wal_autocheckpoint pragma] can be used to cause this interface to be\n** run whenever the WAL reaches a certain size threshold.\n**\n** See also: [sqlite3_wal_checkpoint_v2()]\n*/\nSQLITE_API int sqlite3_wal_checkpoint(sqlite3 *db, const char *zDb);\n\n/*\n** CAPI3REF: Checkpoint a database\n**\n** Run a checkpoint operation on WAL database zDb attached to database \n** handle db. The specific operation is determined by the value of the \n** eMode parameter:\n**\n** <dl>\n** <dt>SQLITE_CHECKPOINT_PASSIVE<dd>\n**   Checkpoint as many frames as possible without waiting for any database \n**   readers or writers to finish. Sync the db file if all frames in the log\n**   are checkpointed. This mode is the same as calling \n**   sqlite3_wal_checkpoint(). The busy-handler callback is never invoked.\n**\n** <dt>SQLITE_CHECKPOINT_FULL<dd>\n**   This mode blocks (calls the busy-handler callback) until there is no\n**   database writer and all readers are reading from the most recent database\n**   snapshot. It then checkpoints all frames in the log file and syncs the\n**   database file. This call blocks database writers while it is running,\n**   but not database readers.\n**\n** <dt>SQLITE_CHECKPOINT_RESTART<dd>\n**   This mode works the same way as SQLITE_CHECKPOINT_FULL, except after \n**   checkpointing the log file it blocks (calls the busy-handler callback)\n**   until all readers are reading from the database file only. This ensures \n**   that the next client to write to the database file restarts the log file \n**   from the beginning. This call blocks database writers while it is running,\n**   but not database readers.\n** </dl>\n**\n** If pnLog is not NULL, then *pnLog is set to the total number of frames in\n** the log file before returning. If pnCkpt is not NULL, then *pnCkpt is set to\n** the total number of checkpointed frames (including any that were already\n** checkpointed when this function is called). *pnLog and *pnCkpt may be\n** populated even if sqlite3_wal_checkpoint_v2() returns other than SQLITE_OK.\n** If no values are available because of an error, they are both set to -1\n** before returning to communicate this to the caller.\n**\n** All calls obtain an exclusive \"checkpoint\" lock on the database file. If\n** any other process is running a checkpoint operation at the same time, the \n** lock cannot be obtained and SQLITE_BUSY is returned. Even if there is a \n** busy-handler configured, it will not be invoked in this case.\n**\n** The SQLITE_CHECKPOINT_FULL and RESTART modes also obtain the exclusive \n** \"writer\" lock on the database file. If the writer lock cannot be obtained\n** immediately, and a busy-handler is configured, it is invoked and the writer\n** lock retried until either the busy-handler returns 0 or the lock is\n** successfully obtained. The busy-handler is also invoked while waiting for\n** database readers as described above. If the busy-handler returns 0 before\n** the writer lock is obtained or while waiting for database readers, the\n** checkpoint operation proceeds from that point in the same way as \n** SQLITE_CHECKPOINT_PASSIVE - checkpointing as many frames as possible \n** without blocking any further. SQLITE_BUSY is returned in this case.\n**\n** If parameter zDb is NULL or points to a zero length string, then the\n** specified operation is attempted on all WAL databases. In this case the\n** values written to output parameters *pnLog and *pnCkpt are undefined. If \n** an SQLITE_BUSY error is encountered when processing one or more of the \n** attached WAL databases, the operation is still attempted on any remaining \n** attached databases and SQLITE_BUSY is returned to the caller. If any other \n** error occurs while processing an attached database, processing is abandoned \n** and the error code returned to the caller immediately. If no error \n** (SQLITE_BUSY or otherwise) is encountered while processing the attached \n** databases, SQLITE_OK is returned.\n**\n** If database zDb is the name of an attached database that is not in WAL\n** mode, SQLITE_OK is returned and both *pnLog and *pnCkpt set to -1. If\n** zDb is not NULL (or a zero length string) and is not the name of any\n** attached database, SQLITE_ERROR is returned to the caller.\n*/\nSQLITE_API int sqlite3_wal_checkpoint_v2(\n  sqlite3 *db,                    /* Database handle */\n  const char *zDb,                /* Name of attached database (or NULL) */\n  int eMode,                      /* SQLITE_CHECKPOINT_* value */\n  int *pnLog,                     /* OUT: Size of WAL log in frames */\n  int *pnCkpt                     /* OUT: Total number of frames checkpointed */\n);\n\n/*\n** CAPI3REF: Checkpoint operation parameters\n**\n** These constants can be used as the 3rd parameter to\n** [sqlite3_wal_checkpoint_v2()].  See the [sqlite3_wal_checkpoint_v2()]\n** documentation for additional information about the meaning and use of\n** each of these values.\n*/\n#define SQLITE_CHECKPOINT_PASSIVE 0\n#define SQLITE_CHECKPOINT_FULL    1\n#define SQLITE_CHECKPOINT_RESTART 2\n\n/*\n** CAPI3REF: Virtual Table Interface Configuration\n**\n** This function may be called by either the [xConnect] or [xCreate] method\n** of a [virtual table] implementation to configure\n** various facets of the virtual table interface.\n**\n** If this interface is invoked outside the context of an xConnect or\n** xCreate virtual table method then the behavior is undefined.\n**\n** At present, there is only one option that may be configured using\n** this function. (See [SQLITE_VTAB_CONSTRAINT_SUPPORT].)  Further options\n** may be added in the future.\n*/\nSQLITE_API int sqlite3_vtab_config(sqlite3*, int op, ...);\n\n/*\n** CAPI3REF: Virtual Table Configuration Options\n**\n** These macros define the various options to the\n** [sqlite3_vtab_config()] interface that [virtual table] implementations\n** can use to customize and optimize their behavior.\n**\n** <dl>\n** <dt>SQLITE_VTAB_CONSTRAINT_SUPPORT\n** <dd>Calls of the form\n** [sqlite3_vtab_config](db,SQLITE_VTAB_CONSTRAINT_SUPPORT,X) are supported,\n** where X is an integer.  If X is zero, then the [virtual table] whose\n** [xCreate] or [xConnect] method invoked [sqlite3_vtab_config()] does not\n** support constraints.  In this configuration (which is the default) if\n** a call to the [xUpdate] method returns [SQLITE_CONSTRAINT], then the entire\n** statement is rolled back as if [ON CONFLICT | OR ABORT] had been\n** specified as part of the users SQL statement, regardless of the actual\n** ON CONFLICT mode specified.\n**\n** If X is non-zero, then the virtual table implementation guarantees\n** that if [xUpdate] returns [SQLITE_CONSTRAINT], it will do so before\n** any modifications to internal or persistent data structures have been made.\n** If the [ON CONFLICT] mode is ABORT, FAIL, IGNORE or ROLLBACK, SQLite \n** is able to roll back a statement or database transaction, and abandon\n** or continue processing the current SQL statement as appropriate. \n** If the ON CONFLICT mode is REPLACE and the [xUpdate] method returns\n** [SQLITE_CONSTRAINT], SQLite handles this as if the ON CONFLICT mode\n** had been ABORT.\n**\n** Virtual table implementations that are required to handle OR REPLACE\n** must do so within the [xUpdate] method. If a call to the \n** [sqlite3_vtab_on_conflict()] function indicates that the current ON \n** CONFLICT policy is REPLACE, the virtual table implementation should \n** silently replace the appropriate rows within the xUpdate callback and\n** return SQLITE_OK. Or, if this is not possible, it may return\n** SQLITE_CONSTRAINT, in which case SQLite falls back to OR ABORT \n** constraint handling.\n** </dl>\n*/\n#define SQLITE_VTAB_CONSTRAINT_SUPPORT 1\n\n/*\n** CAPI3REF: Determine The Virtual Table Conflict Policy\n**\n** This function may only be called from within a call to the [xUpdate] method\n** of a [virtual table] implementation for an INSERT or UPDATE operation. ^The\n** value returned is one of [SQLITE_ROLLBACK], [SQLITE_IGNORE], [SQLITE_FAIL],\n** [SQLITE_ABORT], or [SQLITE_REPLACE], according to the [ON CONFLICT] mode\n** of the SQL statement that triggered the call to the [xUpdate] method of the\n** [virtual table].\n*/\nSQLITE_API int sqlite3_vtab_on_conflict(sqlite3 *);\n\n/*\n** CAPI3REF: Conflict resolution modes\n**\n** These constants are returned by [sqlite3_vtab_on_conflict()] to\n** inform a [virtual table] implementation what the [ON CONFLICT] mode\n** is for the SQL statement being evaluated.\n**\n** Note that the [SQLITE_IGNORE] constant is also used as a potential\n** return value from the [sqlite3_set_authorizer()] callback and that\n** [SQLITE_ABORT] is also a [result code].\n*/\n#define SQLITE_ROLLBACK 1\n/* #define SQLITE_IGNORE 2 // Also used by sqlite3_authorizer() callback */\n#define SQLITE_FAIL     3\n/* #define SQLITE_ABORT 4  // Also an error code */\n#define SQLITE_REPLACE  5\n\n\n\n/*\n** Undo the hack that converts floating point types to integer for\n** builds on processors without floating point support.\n*/\n#ifdef SQLITE_OMIT_FLOATING_POINT\n# undef double\n#endif\n\n#ifdef __cplusplus\n}  /* End of the 'extern \"C\"' block */\n#endif\n#endif\n\n/*\n** 2010 August 30\n**\n** The author disclaims copyright to this source code.  In place of\n** a legal notice, here is a blessing:\n**\n**    May you do good and not evil.\n**    May you find forgiveness for yourself and forgive others.\n**    May you share freely, never taking more than you give.\n**\n*************************************************************************\n*/\n\n#ifndef _SQLITE3RTREE_H_\n#define _SQLITE3RTREE_H_\n\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef struct sqlite3_rtree_geometry sqlite3_rtree_geometry;\n\n/*\n** Register a geometry callback named zGeom that can be used as part of an\n** R-Tree geometry query as follows:\n**\n**   SELECT ... FROM <rtree> WHERE <rtree col> MATCH $zGeom(... params ...)\n*/\nSQLITE_API int sqlite3_rtree_geometry_callback(\n  sqlite3 *db,\n  const char *zGeom,\n#ifdef SQLITE_RTREE_INT_ONLY\n  int (*xGeom)(sqlite3_rtree_geometry*, int n, sqlite3_int64 *a, int *pRes),\n#else\n  int (*xGeom)(sqlite3_rtree_geometry*, int n, double *a, int *pRes),\n#endif\n  void *pContext\n);\n\n\n/*\n** A pointer to a structure of the following type is passed as the first\n** argument to callbacks registered using rtree_geometry_callback().\n*/\nstruct sqlite3_rtree_geometry {\n  void *pContext;                 /* Copy of pContext passed to s_r_g_c() */\n  int nParam;                     /* Size of array aParam[] */\n  double *aParam;                 /* Parameters passed to SQL geom function */\n  void *pUser;                    /* Callback implementation user data */\n  void (*xDelUser)(void *);       /* Called by SQLite to clean up pUser */\n};\n\n\n#ifdef __cplusplus\n}  /* end of the 'extern \"C\"' block */\n#endif\n\n#endif  /* ifndef _SQLITE3RTREE_H_ */\n\n"
  },
  {
    "path": "engine/server/sv_ccmds.c",
    "content": "/*\nCopyright (C) 1996-1997 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n*/\n#include \"quakedef.h\"\n#include \"pr_common.h\"\n#include \"fs.h\"\n#include \"netinc.h\"\n\n#ifndef CLIENTONLY\n\n#ifndef INVALID_SOCKET\n#define INVALID_SOCKET -1\n#endif\n\n\n\nint\tsv_allow_cheats;\nqboolean SV_MayCheat(void)\n{\n\tif (sv_allow_cheats == 2)\n\t\treturn sv.allocated_client_slots == 1;\n\treturn sv_allow_cheats!=0;\n}\n\n#ifdef SUBSERVERS\ncvar_t sv_autooffload = CVARD(\"sv_autooffload\", \"0\", \"Automatically start the server in a separate process, so that sporadic or persistent gamecode slowdowns do not affect visual framerates (equivelent to the mapcluster command). Note: Offloaded servers have separate cvar+command states which may complicate usage.\");\n#endif\nextern cvar_t cl_warncmd;\ncvar_t sv_cheats = CVARF(\"sv_cheats\", \"0\", CVAR_MAPLATCH);\n\textern\t\tredirect_t\tsv_redirected;\n\nextern cvar_t sv_public;\n\nstatic const struct banflags_s\n{\n\tunsigned int banflag;\n\tconst char *names[2];\n} banflags[] =\n{\n\t{BAN_BAN,\t\t{\"ban\"}},\n\t{BAN_PERMIT,\t{\"safe\",\t\t\"permit\"}},\n\t{BAN_CUFF,\t\t{\"cuff\"}},\n\t{BAN_MUTE,\t\t{\"mute\"}},\n\t{BAN_VMUTE,\t\t{\"vmute\"}},\n\t{BAN_CRIPPLED,\t{\"cripple\"}},\n\t{BAN_DEAF,\t\t{\"deaf\"}},\n\t{BAN_LAGGED,\t{\"lag\",\t\t\"lagged\"}},\n\t{BAN_VIP,\t\t{\"vip\"}},\n\t{BAN_BLIND,\t\t{\"blind\"}},\n\t{BAN_SPECONLY,\t{\"spec\"}},\n\t{BAN_STEALTH,\t{\"stealth\"}},\n\t{BAN_MAPPER,\t{\"mapper\"}},\n\n\t{BAN_USER1,\t\t{\"user1\"}},\n\t{BAN_USER2,\t\t{\"user2\"}},\n\t{BAN_USER3,\t\t{\"user3\"}},\n\t{BAN_USER4,\t\t{\"user4\"}},\n\t{BAN_USER5,\t\t{\"user5\"}},\n\t{BAN_USER6,\t\t{\"user6\"}},\n\t{BAN_USER7,\t\t{\"user7\"}},\n\t{BAN_USER8,\t\t{\"user8\"}}\n};\n\n//generic helper function for naming players.\nclient_t *SV_GetClientForString(const char *name, int *id)\n{\n\tint i;\n\tconst char *s;\n\tchar nicename[80];\n\tchar niceclname[80];\n\tclient_t *cl;\n\n\tint first=0;\n\tif (id && *id != -1)\n\t\tfirst = *id;\n\tif (first < 0)\n\t{\n\t\tif (id)\n\t\t\t*id=sv.allocated_client_slots;\n\t\treturn NULL;\n\t}\n\n\tif (!strcmp(name, \"*\"))\t//match with all\n\t{\n\t\tfor (i = first, cl = svs.clients+first; i < sv.allocated_client_slots; i++, cl++)\n\t\t{\n\t\t\tif (cl->state<=cs_loadzombie)\n\t\t\t\tcontinue;\n\n\t\t\tif (id)\n\t\t\t\t*id=i+1;\n\t\t\treturn cl;\n\t\t}\n\t\tif (id)\n\t\t\t*id=sv.allocated_client_slots;\n\t\treturn NULL;\n\t}\n\n\t//check to make sure it's all an int\n\n\tfor (s = name; *s; s++)\n\t{\n\t\tif (*s < '0' || *s > '9')\n\t\t\tbreak;\n\t}\n\n\t//we got to the end of the string and found only numbers. - it's a uid.\n\tif (!*s)\n\t{\n\t\tint uid = Q_atoi(name);\n\t\tfor (i = first, cl = svs.clients+first; i < sv.allocated_client_slots; i++, cl++)\n\t\t{\n\t\t\tif (cl->state<=cs_loadzombie)\n\t\t\t\tcontinue;\n\t\t\tif (cl->userid == uid)\n\t\t\t{\n\t\t\t\tif (id)\n\t\t\t\t\t*id=sv.allocated_client_slots;\n\t\t\t\treturn cl;\n\t\t\t}\n\t\t}\n\n\t\treturn NULL;\n\t}\n\n\tfor (i = first, cl = svs.clients+first; i < sv.allocated_client_slots; i++, cl++)\n\t{\n\t\tif (cl->state<=cs_loadzombie)\n\t\t\tcontinue;\n\n\n\t\tdeleetstring(niceclname, cl->name);\n\t\tdeleetstring(nicename, name);\n\n\t\tif (strstr(niceclname, nicename))\n\t\t{\n\t\t\tif (id)\n\t\t\t\t*id=i+1;\n\t\t\treturn cl;\n\t\t}\n\t}\n\n\treturn NULL;\n}\n\n/*\n===============================================================================\n\nOPERATOR CONSOLE ONLY COMMANDS\n\nThese commands can only be entered from stdin or by a remote operator datagram\n===============================================================================\n*/\n\n/*\n==================\nSV_Quit_f\n==================\n*/\nstatic void SV_Quit_f (void)\n{\n\tif (sv.state >= ss_loading)\n\t\tSV_FinalMessage (\"server shutdown\\n\");\n\tCon_TPrintf (\"Shutting down.\\n\");\n\tSV_Shutdown ();\n\tSys_Quit ();\n}\n\n/*\n============\nSV_Fraglogfile_f\n============\n*/\nstatic void SV_Fraglogfile_f (void)\n{\n\tchar\tname[MAX_OSPATH];\n\tint\t\ti;\n\n\tif (sv_fraglogfile)\n\t{\n\t\tCon_TPrintf (\"Frag file logging off.\\n\");\n\t\tVFS_CLOSE (sv_fraglogfile);\n\t\tsv_fraglogfile = NULL;\n\t\treturn;\n\t}\n\n\t// find an unused name\n\tfor (i=0 ; i<1000 ; i++)\n\t{\n\t\tsprintf (name, \"frag_%i.log\", i);\n\t\tsv_fraglogfile = FS_OpenVFS(name, \"rb\", FS_GAME);\n\t\tif (!sv_fraglogfile)\n\t\t{\t// can't read it, so create this one\n\t\t\tsv_fraglogfile = FS_OpenVFS (name, \"wb\", FS_GAME);\n\t\t\tif (!sv_fraglogfile)\n\t\t\t\ti=1000;\t// give error\n\t\t\tbreak;\n\t\t}\n\t\tVFS_CLOSE (sv_fraglogfile);\n\t}\n\tif (i==1000)\n\t{\n\t\tCon_TPrintf (\"Can't open any logfiles.\\n\");\n\t\tsv_fraglogfile = NULL;\n\t\treturn;\n\t}\n\n\tCon_TPrintf (\"Logging frags to %s.\\n\", name);\n}\n\n\n/*\n==================\nSV_SetPlayer\n\nSets host_client and sv_player to the player with idnum Cmd_Argv(1)\n==================\n*/\nstatic qboolean SV_SetPlayer (void)\n{\n\tclient_t\t*cl;\n\tint\t\t\ti;\n\tint\t\t\tidnum;\n\n\tidnum = atoi(Cmd_Argv(1));\n\n\tfor (i=0,cl=svs.clients ; i<sv.allocated_client_slots ; i++,cl++)\n\t{\n\t\tif (!cl->state)\n\t\t\tcontinue;\n\t\tif (cl->userid == idnum)\n\t\t{\n\t\t\thost_client = cl;\n\t\t\tsv_player = host_client->edict;\n\t\t\treturn true;\n\t\t}\n\t}\n\tCon_TPrintf (\"Userid %i is not on the server\\n\", idnum);\n\treturn false;\n}\n\n\n/*\n==================\nSV_God_f\n\nSets client to godmode\n==================\n*/\nstatic void SV_God_f (void)\n{\n\tif (!SV_MayCheat())\n\t{\n\t\tCon_TPrintf (\"Please set sv_cheats 1 and restart the map first.\\n\");\n\t\treturn;\n\t}\n\n\tif (!SV_SetPlayer ())\n\t\treturn;\n\n\tSV_LogPlayer(host_client, \"god cheat\");\n\tsv_player->v->flags = (int)sv_player->v->flags ^ FL_GODMODE;\n\tif ((int)sv_player->v->flags & FL_GODMODE)\n\t\tSV_ClientTPrintf (host_client, PRINT_HIGH, \"godmode ON\\n\");\n\telse\n\t\tSV_ClientTPrintf (host_client, PRINT_HIGH, \"godmode OFF\\n\");\n}\n\n\nstatic void SV_Noclip_f (void)\n{\n\tif (!SV_MayCheat())\n\t{\n\t\tCon_TPrintf (\"Please set sv_cheats 1 and restart the map first.\\n\");\n\t\treturn;\n\t}\n\n\tif (!SV_SetPlayer ())\n\t\treturn;\n\n\tSV_LogPlayer(host_client, \"noclip cheat\");\n\tif (sv_player->v->movetype != MOVETYPE_NOCLIP)\n\t{\n\t\tsv_player->v->movetype = MOVETYPE_NOCLIP;\n\t\tSV_ClientTPrintf (host_client, PRINT_HIGH, \"noclip ON\\n\");\n\t}\n\telse\n\t{\n\t\tsv_player->v->movetype = MOVETYPE_WALK;\n\t\tSV_ClientTPrintf (host_client, PRINT_HIGH, \"noclip OFF\\n\");\n\t}\n}\n\n#ifdef QUAKESTATS\n/*\n==================\nSV_Give_f\n==================\n*/\nstatic void SV_Give_f (void)\n{\n\tchar\t*t = Cmd_Argv(2);\n\tint\t\tv;\n\n\tif (!svprogfuncs)\n\t\treturn;\n\n\tif (!strcmp(t, \"damn\"))\n\t{\n\t\tCon_TPrintf (\"%s not given.\\n\", t);\n\t\treturn;\n\t}\n\n\tif (!SV_MayCheat())\n\t{\n\t\tCon_TPrintf (\"Please set sv_cheats 1 and restart the map first.\\n\");\n\t\treturn;\n\t}\n\n/*\tif (developer.value)\n\t{\n\t\tint oldself;\n\t\toldself = pr_global_struct->self;\n\t\tpr_global_struct->self = EDICT_TO_PROG(svprogfuncs, sv.world.edicts);\n\t\tCon_Printf(\"Result: %s\\n\", svprogfuncs->EvaluateDebugString(svprogfuncs, Cmd_Args()));\n\t\tpr_global_struct->self = oldself;\n\t}\n*/\n\tif (!SV_SetPlayer ())\n\t{\n\t\treturn;\n\t}\n\n\tSV_LogPlayer(host_client, \"give cheat\");\n\n\tv = atoi (Cmd_Argv(3));\n\n\tswitch ((t[1]==0)?t[0]:0)\n\t{\n\tcase '2':\n\tcase '3':\n\tcase '4':\n\tcase '5':\n\tcase '6':\n\tcase '7':\n\tcase '8':\n\tcase '9':\n\t\tsv_player->v->items = (int)sv_player->v->items | IT_SHOTGUN<< (t[0] - '2');\n\t\tbreak;\n\n\tcase 's':\n\t\tsv_player->v->ammo_shells = v;\n\t\tbreak;\n\tcase 'n':\n\t\tsv_player->v->ammo_nails = v;\n\t\tbreak;\n\tcase 'r':\n\t\tsv_player->v->ammo_rockets = v;\n\t\tbreak;\n\tcase 'h':\n\t\tsv_player->v->health = v;\n\t\tbreak;\n\tcase 'c':\n\t\tsv_player->v->ammo_cells = v;\n\t\tbreak;\n/*\tdefault:\n\t\t{\n\t\t\tint oldself;\n\t\t\toldself = pr_global_struct->self;\n\t\t\tpr_global_struct->self = EDICT_TO_PROG(svprogfuncs, sv_player);\n\t\t\tCmd_ShiftArgs(1, false);\n\t\t\tCon_TPrintf(\"Result: %s\\n\", svprogfuncs->EvaluateDebugString(svprogfuncs, Cmd_Args()));\n\t\t\tpr_global_struct->self = oldself;\n\t\t}\n*/\n\t}\n}\n#endif\n\n\n#if defined(HAVE_LEGACY) && defined(HAVE_SERVER)\nstatic void SV_redundantcommand_f(void)\n{\n\tif (cl_warncmd.ival)\n\t\tCon_Printf(\"%s is obsolete, redundant, or otherwise outdated.\\n\", Cmd_Argv(0));\n}\n#endif\n\nstatic int QDECL ShowMapList (const char *name, qofs_t flags, time_t mtime, void *parm, searchpathfuncs_t *spath)\n{\n\tsearchpathfuncs_t **oldspath = parm;\n\tconst char *levelshots[] =\n\t{\n\t\t\"levelshots/%s.tga\",\n\t\t\"levelshots/%s.jpg\",\n\t\t\"levelshots/%s.png\",\n\t\t\"maps/%s.tga\",\n\t\t\"maps/%s.jpg\",\n\t\t\"maps/%s.png\"\n\t};\n\tsize_t u;\n\tchar stripped[MAX_QPATH];\n\tchar completed[256];\n\tconst char *cmd = name+5; //the arg to pass to `map`\n\tconst char *ext;\n\tflocation_t loc;\n\tif (name[5] == 'b' && name[6] == '_')\t//skip box models\n\t\treturn true;\n\n\tif (FS_FLocateFile(name, FSLF_IFFOUND, &loc))\n\t{\n\t\tif (loc.search->handle != spath)\n\t\t\treturn true; //shadowed\n\t}\n\telse\n\t\treturn true; //wtf?\n\n\text = COM_GetFileExtension (name+5, NULL);\n\tif (!strcmp(ext, \".gz\") || !strcmp(ext, \".xz\"))\n\t\text = COM_GetFileExtension (name+5, ext);\t//.gz files should be listed too.\n\n\tif (!strcmp(ext, \".bsp\") || !Q_strcasecmp(ext, \".d3dbsp\") || !Q_strcasecmp(ext, \".cm\"))\n\t{\n\t\text = \"\";\t//hide it\n\t\tcmd = stripped;\t//omit it, might as well. should give less confusing mapname serverinfo etc.\n\t}\n\telse if (!Q_strcasecmp(ext, \".bsp\") || !Q_strcasecmp(ext, \".bsp.gz\") || !Q_strcasecmp(ext, \".bsp.xz\"))\n\t\t;\n\telse if (!Q_strcasecmp(ext, \".d3dbsp\") || !Q_strcasecmp(ext, \".d3dbsp.gz\") || !Q_strcasecmp(ext, \".d3dbsp.xz\"))\n\t\t;\t//cod2 compat. vile.\n#ifdef TERRAIN\n\telse if (!Q_strcasecmp(ext, \".map\") || !Q_strcasecmp(ext, \".map.gz\") || !Q_strcasecmp(ext, \".hmp\"))\n\t\t;\n#endif\n\telse if (!Q_strcasecmp(ext, \".ent\") && strchr(name+5, '#'))\n\t{\t//FIXME hide if earlier that the .bsp\n\t\text = \"\"; //hide it.\n\t\tcmd = stripped;\t//do NOT use the .ent extension here\n\t}\n\telse\n\t\treturn true; //probably a .lit\n\n\tif (*oldspath != spath)\n\t{\n\t\t*oldspath = spath;\n\t\tCon_Printf(S_COLOR_GRAY\"From %s\\n\", loc.search->purepath);\n\t}\n\n\t*completed = 0;\n#ifdef HAVE_CLIENT\n\t{\n\t\tfloat besttime, fulltime, kills, secrets;\n\t\tif (Log_CheckMapCompletion(NULL, name, &besttime, &fulltime, &kills, &secrets))\n\t\t{\n\t\t\tif (kills || secrets)\n\t\t\t\tQ_snprintfz(completed, sizeof(completed), \"^7 - ^2best: ^1%.1f^2, full: ^1%.1f^2 (^1%.0f^2 kills, ^1%.0f^2 secrets)\", besttime, fulltime, kills, secrets);\n\t\t\telse\n\t\t\t\tQ_snprintfz(completed, sizeof(completed), \"^7 - ^2best: ^1%.1f^2\", besttime);\n\t\t}\n\t}\n#endif\n\n\tCOM_StripExtension(name+5, stripped, sizeof(stripped));\n\tfor (u = 0; u < countof(levelshots); u++)\n\t{\n\t\tconst char *ls = va(levelshots[u], stripped);\n\t\tif (COM_FCheckExists(ls))\n\t\t{\n\t\t\tCon_Printf(\"^[\\\\map\\\\%s\\\\img\\\\%s\\\\w\\\\64\\\\h\\\\48^]\", cmd, ls);\n\t\t\tCon_Printf(\"^[[%s%s]%s\\\\map\\\\%s\\\\tipimg\\\\%s\\\\tip\\\\from %s/%s^]\\n\", stripped, ext, completed, cmd, ls, loc.search->logicalpath, name);\n\t\t\treturn true;\n\t\t}\n\t}\n\tCon_Printf(\"^[[%s%s]%s\\\\map\\\\%s\\\\tip\\\\from %s/%s^]\\n\", stripped, ext, completed, cmd, loc.search->logicalpath, name);\n\treturn true;\n}\nstatic void SV_MapList_f(void)\n{\n\tsearchpathfuncs_t *spath = NULL;\n\tCOM_EnumerateFilesReverse(\"maps/*.*\", ShowMapList, &spath);\n\tCOM_EnumerateFilesReverse(\"maps/*/*.*\", ShowMapList, &spath);\n\tCOM_EnumerateFilesReverse(\"maps/*/*/*.*\", ShowMapList, &spath);\n}\n\nstatic int QDECL CompleteMapList (const char *name, qofs_t flags, time_t mtime, void *parm, searchpathfuncs_t *spath)\n{\n\tstruct xcommandargcompletioncb_s *ctx = parm;\n\tchar stripped[64];\n\tif (name[5] == 'b' && name[6] == '_')\t//skip box models\n\t\treturn true;\n\n\tCOM_StripExtension(name+5, stripped, sizeof(stripped));\n\tctx->cb(stripped, NULL, NULL, ctx);\n\treturn true;\n}\nstatic int QDECL CompleteMapListEnt (const char *name, qofs_t flags, time_t mtime, void *parm, searchpathfuncs_t *spath)\n{\n\tstruct xcommandargcompletioncb_s *ctx = parm;\n\tchar stripped[64];\n\tchar *modifier = strchr(name, '#');\n\tif (!modifier)\t//skip non-modifiers.\n\t\treturn true;\n\tif (modifier-name+4 > sizeof(stripped))\t//too long...\n\t\treturn true;\n\n\t//make sure we have its .bsp\n\tmemcpy(stripped, name, modifier-name);\n\tstrcpy(stripped+(modifier-name), \".bsp\");\n\tif (!COM_FCheckExists(stripped))\n\t\treturn true;\n\n\tCOM_StripExtension(name+5, stripped, sizeof(stripped));\n\tctx->cb(stripped, NULL, NULL, ctx);\n\treturn true;\n}\n\nstatic int QDECL CompleteMapListExt (const char *name, qofs_t flags, time_t mtime, void *parm, searchpathfuncs_t *spath)\n{\n\tstruct xcommandargcompletioncb_s *ctx = parm;\n\tif (name[5] == 'b' && name[6] == '_')\t//skip box models\n\t\treturn true;\n\n\tctx->cb(name+5, NULL, NULL, ctx);\n\treturn true;\n}\nstatic void SV_Map_c(int argn, const char *partial, struct xcommandargcompletioncb_s *ctx)\n{\n\tif (argn == 1)\n\t{\n\t\t//FIXME: maps/mapname#modifier.ent\n\t\tCOM_EnumerateFiles(va(\"maps/%s*.bsp\", partial), CompleteMapList, ctx);\n\t\tCOM_EnumerateFiles(va(\"maps/%s*.d3dbsp\", partial), CompleteMapList, ctx);\n\t\tCOM_EnumerateFiles(va(\"maps/%s*.bsp.gz\", partial), CompleteMapListExt, ctx);\n\t\tCOM_EnumerateFiles(va(\"maps/%s*.bsp.xz\", partial), CompleteMapListExt, ctx);\n\t\tCOM_EnumerateFiles(va(\"maps/%s*.map\", partial), CompleteMapListExt, ctx);\n\t\tCOM_EnumerateFiles(va(\"maps/%s*.map.gz\", partial), CompleteMapListExt, ctx);\n\t\tCOM_EnumerateFiles(va(\"maps/%s*.cm\", partial), CompleteMapList, ctx);\n\t\tCOM_EnumerateFiles(va(\"maps/%s*.hmp\", partial), CompleteMapList, ctx);\n\n\t\tCOM_EnumerateFiles(va(\"maps/%s*.ent\", partial), CompleteMapListEnt, ctx);\n\n\t\tCOM_EnumerateFiles(va(\"maps/%s*/*.bsp\", partial), CompleteMapList, ctx);\n\t\tCOM_EnumerateFiles(va(\"maps/%s*/*.d3dbsp\", partial), CompleteMapList, ctx);\n\t\tCOM_EnumerateFiles(va(\"maps/%s*/*.bsp.gz\", partial), CompleteMapListExt, ctx);\n\t\tCOM_EnumerateFiles(va(\"maps/%s*/*.bsp.xz\", partial), CompleteMapListExt, ctx);\n\t\tCOM_EnumerateFiles(va(\"maps/%s*/*.map\", partial), CompleteMapListExt, ctx);\n\t\tCOM_EnumerateFiles(va(\"maps/%s*/*.map.gz\", partial), CompleteMapListExt, ctx);\n\t\tCOM_EnumerateFiles(va(\"maps/%s*/*.cm\", partial), CompleteMapList, ctx);\n\t\tCOM_EnumerateFiles(va(\"maps/%s*/*.hmp\", partial), CompleteMapList, ctx);\n\n#ifdef PACKAGEMANAGER\n\t\tPM_EnumerateMaps(partial, ctx);\n#endif\n\t}\n}\n\n#ifdef WEBCLIENT\nstatic char *uri_escape(const char *in, char *out, size_t outsize)\n{\n\tstatic const char *hex = \"0123456789ABCDEF\";\n\n\tconst unsigned char *s = in;\n\tunsigned char *o = out;\n\twhile (*s && o < (unsigned char*)out+outsize-4)\n\t{\n\t\t//unreserved chars according to RFC3986\n\t\tif ((*s >= 'a' && *s <= 'z') || (*s >= 'A' && *s <= 'Z') || (*s >= '0' && *s <= '9')\n\t\t\t\t|| *s == '.' || *s == '-' || *s == '_' || *s == '~')\n\t\t\t*o++ = *s++;\n\t\telse\n\t\t{\n\t\t\t*o++ = '%';\n\t\t\t*o++ = hex[*s>>4];\n\t\t\t*o++ = hex[*s&0xf];\n\t\t\ts++;\n\t\t}\n\t}\n\t*o = 0;\n\treturn out;\n}\nstatic void SV_Map_DownloadCanceled(const char *mapname)\n{\n#ifdef HAVE_SERVER\n\tif (SSV_IsSubServer() && !sv.state)\t//subservers don't leave defunct servers with no maps lying around.\n\t\tCbuf_AddText(\"\\nquit\\n\", RESTRICT_LOCAL);\n\tif (isDedicated && !sv.state && !COM_CheckParm(\"-allowmapless\"))\n\t\tSV_Error (CON_ERROR\"Couldn't download map %s.\", mapname);\n#endif\n#ifdef HAVE_CLIENT\n\tSCR_SetLoadingStage(LS_NONE);\n#endif\n}\nstatic void SV_Map_Downloaded(struct dl_download *dl)\n{\n\tchar buf[1024];\n#ifdef HAVE_CLIENT\n\tSCR_SetLoadingStage(LS_NONE);\n#endif\n\tif (dl->status == DL_FINISHED)\n\t{\n\t\tCbuf_AddText(va(\"map %s\\n\", COM_QuotedString(dl->user_ctx, buf, sizeof(buf), false)), RESTRICT_LOCAL);\n\t}\n\telse\n\t{\n\t\tCon_Printf(\"Unable to download map %s\\n\", COM_QuotedString(dl->user_ctx, buf, sizeof(buf), false));\n\t\tSV_Map_DownloadCanceled(dl->user_ctx);\n\t}\n\tZ_Free(dl->user_ctx);\n}\nextern cvar_t cl_download_mapsrc;\nstatic qboolean SV_Map_DownloadStart(char *mapname/*must be duped*/)\n{\n\tchar buf[512];\n\tstruct dl_download *dl = HTTP_CL_Get(va(\"%s%s.bsp\", cl_download_mapsrc.string, uri_escape(mapname, buf, sizeof(buf))), va(\"maps/%s.bsp\", mapname), SV_Map_Downloaded);\n\tif (dl)\n\t{\n\t\tdl->user_ctx = mapname;\n\t\tDL_CreateThread(dl, NULL, NULL);\t//allows it to run at its own rate. yay speedups.\n\t\treturn true;\n\t}\n\tZ_Free(mapname);\n\treturn false;\n}\n#ifdef HAVE_CLIENT\nstatic void SV_Map_DownloadPrompted(void *ctx, promptbutton_t buttn)\n{\n\tif (buttn == PROMPT_YES)\n\t{\n\t\tif (SV_Map_DownloadStart(ctx))\n\t\t\treturn;\n\t\tSV_Map_DownloadCanceled(ctx);\n\t}\n\telse\n\t{\n\t\tSV_Map_DownloadCanceled(ctx);\n\t\tZ_Free(ctx);\n\t}\n}\n#endif\nstatic qboolean SV_Map_DownloadPrompt(const char *mapname)\n{\n#ifdef HAVE_SERVER\n\tif (isDedicated)\n\t{\n\t\treturn SV_Map_DownloadStart(Z_StrDup(mapname));\n\t}\n#endif\n#ifdef HAVE_CLIENT\n\tMenu_Prompt(SV_Map_DownloadPrompted, Z_StrDup(mapname), va(localtext(\"Download map %s from \"S_COLOR_BLUE \"%s\" S_COLOR_WHITE\"?\"), mapname, cl_download_mapsrc.string), \"Download\", NULL, \"Cancel\", true);\n\treturn true;\n#else\n\treturn false;\n#endif\n}\n#endif\n\n//static void gtcallback(struct cvar_s *var, char *oldvalue)\n//{\n//\tCon_Printf(\"g_gametype changed\\n\");\n//}\n\n/*\n======================\nSV_Map_f\n\nhandle a\nmap <mapname>\ncommand from the console or progs.\n\nquirks:\na leading '*' means new unit, meaning all old map state is flushed regardless of startspot\na '+' means 'set nextmap cvar to the following value and otherwise ignore, for q2 compat. only applies if there's also a '.' and the specified bsp doesn't exist, for q1 compat.\njust a '.' is taken to mean 'restart'. parms are not changed from their current values, startspot is also unchanged. Loads the last saved game instead when applicable.\n\nvariations:\n'map' will change map, for most games. strips parms+serverflags+cache. note that vanilla NQ kicks everyone (NQ expects you to use changelevel for that).\n'changelevel' will not flush the level cache, for h2 compat (won't save current level state in such a situation, as nq would prefer not)\n'gamemap' will save the game to 'save0' after loading, for q2 compat\n'spmap' is for q3 and sets 'gametype' to '2', otherwise identical to 'map'. all other map commands will reset it to '0' if its '2' at the time.\n'spdevmap' forces sv_cheats 1, otherwise spmap\n'devmap' forces sv_cheats 1, otherwise map\n'map_restart' restarts the current map. Name is needed for q3 compat.\n'restart' is an alias for 'map_restart'. Exists for NQ compat, but as an alias for QW mods that tried to use it for mod-specific things.\n\nhexen2 fixme:\n'restart restore' restarts the map, reloading from a saved game if applicable.\n'restart' forgets the current map (potentially breaking the game). we don't care much for that behaviour (could make it a 'restart unit' I guess).\n\nquake2:\n'gamemap [*]foo.dm2[$spot][+nextserver]'\n\t* == new unit\n\t$ == start spot\n\t+ == value for nextserver cvar (used for cinematics).\n'map' is always a new unit.\n\nquake:\n+ is used in certain map names. * cannot be, but $ potentially could be.\n\nfte:\n'map package:mapname' should download the specified map package and load up its maps.\n\nmvdsv:\n'map foo bar' should load 'maps/bar.ent' instead of the regular ent file. this 'bar' will usually be something like 'foo#modified'\n\n======================\n*/\nvoid SV_Map_f (void)\n{\n\tchar\tlevel[MAX_QPATH];\n\tchar\tspot[MAX_QPATH];\n\tchar\texpanded[MAX_QPATH+64];\n\tchar\t*nextserver = NULL;\n\tqboolean preserveplayers= false;\n\tqboolean isrestart\t\t= false;\t//don't hurt settings\n#ifdef SAVEDGAMES\n\tqboolean newunit\t\t= false;\t//no hubcache\n\tqboolean q2savetos0\t\t= false;\n#endif\n\tqboolean flushparms\t\t= false;\t//flush parms+serverflags\n\tqboolean cinematic\t\t= false;\t//new map is .cin / .roq or something\n#ifdef Q3SERVER\n\tqboolean q3singleplayer\t= false;\t//forces g_gametype to 2 (otherwise clears if it was 2).\n#endif\n\tqboolean waschangelevel\t= false;\n\tqboolean mapeditor\t\t= false;\n\tqboolean forceCheats = false;\n\tint i;\n\tchar *startspot;\n\tconst char *cmd = Cmd_Argv(0);\n\n#ifndef SERVERONLY\n\tif (!Renderer_Started() && !isDedicated)\n\t{\n\t\tCbuf_AddText(va(\"wait;%s %s\\n\", cmd, Cmd_Args()), Cmd_ExecLevel);\n\t\treturn;\n\t}\n#endif\n\n#ifdef SUBSERVERS\n\t//disconnect first if you want to stop your current server getting the command instead.\n\tif (sv.state == ss_clustermode && MSV_ForwardToAutoServer())\n\t\treturn;\n#endif\n\n\tif (!Q_strcasecmp(cmd, \"map_restart\"))\n\t{\n\t\tconst char *arg = Cmd_Argv(1);\n\n#ifdef Q3SERVER\n\t\tCvar_ApplyLatches(CVAR_MAPLATCH, false);\n\t\tif (sv.state==ss_active && svs.gametype==GT_QUAKE3 && q3->sv.RestartGamecode())\n\t\t{\n\t\t\tsv.time = sv.world.physicstime;\n\t\t\tsv.starttime = Sys_DoubleTime() - sv.time;\n\t\t\treturn;\n\t\t}\n#endif\n\n#ifdef SAVEDGAMES\n\t\tif (!strcmp(arg, \"restore\"))\t\t//hexen2 reload-saved-game\n\t\t\t;\n\t\telse if (!strcmp(arg, \"initial\"))\t//force initial, even if it breaks saved games.\n\t\t\t*sv.loadgame_on_restart = 0;\n\t\telse\n#endif\n\t\t{\n\t\t\tfloat delay = atof(arg);\n\t\t\tif (delay)\t\t\t//q3's restart-after-delay\n\t\t\t\tCon_DPrintf (\"map_restart delay not implemented yet\\n\");\n\t\t}\n\t\tQ_strncpyz (level, \".\", sizeof(level));\n\t\tstartspot = NULL;\n\t\tisrestart = true;\n\n\t\t//FIXME: if precaches+statics don't change, don't do the whole networking thing.\n\t}\n\telse\n\t{\n\t\tif (Cmd_Argc() != 2 && Cmd_Argc() != 3)\n\t\t{\n\t\t\tif (Cmd_IsInsecure())\n\t\t\t\treturn;\n\t\t\tCon_TPrintf (\"Available maps:\\n\", Cmd_Argv(0));\n\t\t\tSV_MapList_f();\n\t\t\treturn;\n\t\t}\n\n#ifdef PACKAGEMANAGER\n\t\tif (Cmd_Argc() == 2)\n\t\t{\n\t\t\tchar *mangled = Cmd_Argv(1);\n\t\t\tchar *sep = strchr(mangled, ':');\n\t\t\tif (sep && strncmp(mangled, \"file:\", 5) && strncmp(mangled, \"http:\", 5) && strncmp(mangled, \"https:\", 5))\n\t\t\t{\n\t\t\t\t*sep++ = 0;\n\t\t\t\tif (Cmd_FromGamecode())\n\t\t\t\t{\n\t\t\t\t\tCon_TPrintf (\"switching packages via %s command is blocked from gamecode, just in case.\\n\", Cmd_Argv(0));\n\t\t\t\t\tsv.mapchangelocked = false;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tPM_LoadMap(mangled, va(\"%s %s\\n\", Cmd_Argv(0), COM_QuotedString(sep, expanded, sizeof(expanded), false)));\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n#endif\n\n\t\tQ_strncpyz (level, Cmd_Argv(1), sizeof(level));\n\t\tstartspot = ((Cmd_Argc() == 2)?NULL:Cmd_Argv(2));\n\t}\n\n#ifdef Q3SERVER\n\tq3singleplayer = !strncmp(cmd, \"sp\", 2);\n#endif\n\tif ((svs.gametype == GT_PROGS || svs.gametype == GT_Q1QVM) && progstype == PROG_QW)\n\t\tflushparms = !strncmp(cmd, \"sp\", 2);\t//quakeworld's map command preserves spawnparms. q3 doesn't do parms, so we might as well reuse sp[dev]map to flush in qw\n\telse\n\t\tflushparms = !strcmp(cmd, \"map\") || !strncmp(cmd, \"sp\", 2); //[sp]map flushes in nq+h2+q2+etc\n#ifdef SAVEDGAMES\n\tnewunit = flushparms || (!strcmp(Cmd_Argv(0), \"changelevel\") && !startspot);\n\tq2savetos0 = !strcmp(cmd, \"gamemap\") && !isDedicated;\t//q2\n#endif\n\tmapeditor = !strcmp(Cmd_Argv(0), \"mapedit\");\n\tforceCheats = !strcmp(Cmd_Argv(0), \"devmap\");\n\n\tsv.mapchangelocked = false;\n\n\tif (!strcmp(level, \".\"))\n\t\t;//restart current - deprecated.\n\telse\n\t{\n\t\tsnprintf (expanded, sizeof(expanded), \"maps/%s.bsp\", level); // this function and the if statement below, is a quake bugfix which stopped a map called \"dm6++.bsp\" from loading because of the + sign, quake2 map syntax interprets + character as \"intro.cin+base1.bsp\", to play a cinematic then load a map after\n\t\tif (!COM_FCheckExists (level) && !COM_FCheckExists (expanded))\n\t\t{\n\t\t\tnextserver = strchr(level, '+');\n\t\t\tif (nextserver)\n\t\t\t{\n\t\t\t\t*nextserver = '\\0';\n\t\t\t\tnextserver++;\n\t\t\t}\n\t\t}\n\t}\n\n\tif (startspot)\n\t{\n\t\tstrcpy(spot, startspot);\n\t\tstartspot = spot;\n\t}\n\telse if ((startspot = strchr(level, '$')))\n\t{\n\t\tstrcpy(spot, startspot+1);\n\t\t*startspot = '\\0';\n\t\tstartspot = spot;\n\t}\n\telse\n\t\tstartspot = NULL;\n\n\tif (!strcmp(level, \".\"))\t//restart current\n\t{\n\t\t//grab the current map name\n\t\tQ_strncpyz(level, svs.name, sizeof(level));\n\t\tisrestart = true;\n\t\tflushparms = false;\n#ifdef SAVEDGAMES\n\t\tnewunit = false;\n\t\tq2savetos0 = false;\n#endif\n\n\t\tif (!*level)\n\t\t{\n\t\t\tsv.mapchangelocked = true;\n\t\t\tif (Cmd_AliasExist(\"startmap_dm\", RESTRICT_LOCAL))\n\t\t\t{\n\t\t\t\tCbuf_AddText(\"startmap_dm\", Cmd_ExecLevel);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tQ_strncpyz(level, \"start\", sizeof(level));\n\t\t}\n\n\t\tif (startspot && !strcmp(startspot, \".\"))\n\t\t{\n\t\t\tpreserveplayers = true;\n\t\t\tstartspot = NULL;\n\t\t}\n\t\tif (!startspot)\n\t\t{\n\t\t\t//revert the startspot if its not overridden\n\t\t\tQ_strncpyz(spot, InfoBuf_ValueForKey(&svs.info, \"*startspot\"), sizeof(spot));\n\t\t\tstartspot = spot;\n\t\t}\n\t}\n\n#ifdef SAVEDGAMES\n\tif (isrestart && *sv.loadgame_on_restart && SV_Loadgame(sv.loadgame_on_restart))\n\t{\t//we managed to reload a saved game instead!\n\t\t//this is required in order to keep hub state consistent (dying mid-map would require saved games to store both current and start of map(not to be confused with initial state, which would be trivial))\n\t\treturn;\n\t}\n#endif\n\n\t// check to make sure the level exists\n\tif (*level == '*')\n\t{\n\t\tmemmove(level, level+1, strlen(level));\n#ifdef SAVEDGAMES\n\t\tnewunit=true;\n#endif\n\t}\n#ifndef SERVERONLY\n\tSCR_ImageName(level);\n\tSCR_SetLoadingStage(LS_SERVER);\n\tSCR_SetLoadingFile(\"finalize server\");\n#else\n\t#define SCR_SetLoadingFile(s)\n#endif\n\n\tCOM_FlushFSCache(false, true);\n\n#ifdef Q2SERVER\n\tif (strlen(level) > 4 &&\n\t\t(!strcmp(level + strlen(level)-4, \".cin\") ||\n\t\t!strcmp(level + strlen(level)-4, \".roq\") ||\n\t\t!strcmp(level + strlen(level)-4, \".ogv\") ||\n\t\t!strcmp(level + strlen(level)-4, \".pcx\") ||\n\t\t!strcmp(level + strlen(level)-4, \".avi\")))\n\t{\n\t\tcinematic = true;\n\t}\n\telse\n#endif\n#ifdef TERRAIN\n\t//'map doesntexist.map' should just auto-generate that map or something\n\tif (!Q_strcasecmp(\"map\", COM_FileExtension(level, expanded, sizeof(expanded))))\n\t\t;\n\telse\n#endif\n\t{\n\t\tchar *exts[] = {\"%s\", \"maps/%s\", \"maps/%s.bsp\", \"maps/%s.bsp.gz\", \"maps/%s.bsp.xz\", \"maps/%s.d3dbsp\", \"maps/%s.cm\", \"maps/%s.hmp\", /*\"maps/%s.map\",*/ /*\"maps/%s.ent\",*/ NULL};\n\t\tint i, j;\n\n\t\tfor (i = 0; exts[i]; i++)\n\t\t{\n\t\t\tsnprintf (expanded, sizeof(expanded), exts[i], level);\n\t\t\tif (COM_FCheckExists (expanded))\n\t\t\t\tbreak;\n\t\t}\n\t\tif (!exts[i])\n\t\t{\t//try again.\n\t\t\tchar *mod = strchr(level, '#');\n\t\t\tif (mod)\n\t\t\t{\n\t\t\t\t*mod = 0;\n\t\t\t\tfor (i = 0; exts[i]; i++)\n\t\t\t\t{\n\t\t\t\t\tsnprintf (expanded, sizeof(expanded), exts[i], level);\n\t\t\t\t\tif (COM_FCheckExists (expanded))\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\t*mod = '#';\n\t\t\t}\n\t\t}\n\t\tif (!exts[i])\n\t\t{\n\t\t\tfor (i = 0; exts[i]; i++)\n\t\t\t{\n\t\t\t\t//doesn't exist, so try lowercase. Q3 does this. really our fs_cache stuff should be handling this, but its possible its disabled.\n\t\t\t\tfor (j = 0; j < sizeof(level) && level[j]; j++)\n\t\t\t\t{\n\t\t\t\t\tif (level[j] >= 'A' && level[j] <= 'Z')\n\t\t\t\t\t\tlevel[j] = level[j] - 'A' + 'a';\n\t\t\t\t}\n\t\t\t\tsnprintf (expanded, sizeof(expanded), exts[i], level);\n\t\t\t\tif (COM_FCheckExists (expanded))\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (!exts[i])\n\t\t\t{\n#ifdef HAVE_CLIENT\n\t\t\t\tSCR_SetLoadingStage(LS_NONE);\n#endif\n#ifdef WEBCLIENT\n\t\t\t\tif (*cl_download_mapsrc.string &&\n\t\t\t\t\t\t!strcmp(cmd, \"map\") && !startspot &&\n\t\t\t\t\t\tCmd_ExecLevel==RESTRICT_LOCAL && !strchr(level, '.'))\n\t\t\t\t{\n\t\t\t\t\tif (SV_Map_DownloadPrompt(level))\n\t\t\t\t\t\treturn;\n\t\t\t\t}\n#endif\n\n\t\t\t\t// FTE is still a Quake engine so report BSP missing\n\t\t\t\tCon_TPrintf (\"Can't find %s\\n\", COM_QuotedString(va(\"maps/%s.bsp\", level), expanded, sizeof(expanded), false));\n\n\t\t\t\tif (SSV_IsSubServer() && !sv.state)\t//subservers don't leave defunct servers with no maps lying around.\n\t\t\t\t\tCbuf_AddText(\"\\nquit\\n\", RESTRICT_LOCAL);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n\n#ifdef SUBSERVERS\n\tif (!isDedicated && sv_autooffload.ival && !sv.state && !SSV_IsSubServer() && (\n\t\t\tisrestart\n\t\t\t|| (!strcmp(Cmd_Argv(0), \"map\") && Cmd_Argc()==2)\n\t\t))\n\t{\n\t\tMSV_MapCluster_Setup(level, false, true);\n\t\treturn;\n\t}\n#endif\n\n#ifdef MVD_RECORDING\n\tif (sv.mvdrecording)\n\t\tSV_MVDStop_f();\n#endif\n\n#ifndef SERVERONLY\n\tif (!isDedicated)\t//otherwise, info used on map loading isn't present\n\t{\n\t\tcl.haveserverinfo = true;\n\t\tInfoBuf_Clone(&cl.serverinfo, &svs.info);\n\t\tCL_CheckServerInfo();\n\t}\n\n\tif (!sv.state && cls.state)\n\t\tCL_Disconnect(NULL);\n#endif\n\n\tif (!isrestart)\n\t\tSV_SaveSpawnparms ();\n\n#ifdef SAVEDGAMES\n\tif (newunit)\n\t\tSV_FlushLevelCache();\t//forget all on new unit\n\telse if (startspot && !isrestart && !newunit)\n\t{\n#ifdef Q2SERVER\n\t\tif (ge)\n\t\t{\n\t\t\tqboolean savedinuse[MAX_CLIENTS];\n\t\t\tfor (i=0 ; i<sv.allocated_client_slots; i++)\n\t\t\t{\n\t\t\t\tsavedinuse[i] = svs.clients[i].q2edict->inuse;\n\t\t\t\tsvs.clients[i].q2edict->inuse = false;\n\t\t\t}\n\t\t\tSV_SaveLevelCache(NULL, false);\n\t\t\tfor (i=0 ; i<sv.allocated_client_slots; i++)\n\t\t\t{\n\t\t\t\tsvs.clients[i].q2edict->inuse = savedinuse[i];\n\t\t\t}\n\t\t}\n\t\telse\n#endif\n\t\t\tSV_SaveLevelCache(NULL, false);\n\t}\n#endif\n\n\tif (forceCheats) {\n\t\tCvar_ForceSet(&sv_cheats, \"1\");\n\t}\n\n#ifdef Q3SERVER\n\t{\n\t\tcvar_t *var, *gametype;\n\n\t\tCvar_ApplyLatches(CVAR_MAPLATCH, false);\n\n\t\thost_mapname.flags |= CVAR_SERVERINFO;\n\n\t\tvar = Cvar_Get(\"nextmap\", \"\", 0, \"Q3 compatibility\");\n\t\tCvar_ForceSet(var, \"map_restart 0\");\t//on every map change matches q3.\n\n\t\tgametype = Cvar_Get(\"g_gametype\", \"\", CVAR_MAPLATCH|CVAR_SERVERINFO, \"Q3 compatability\");\n//\t\tgametype->callback = gtcallback;\n\n\t\t/* map_restart doesn't need to handle gametype changes - eukara */\n\t\tif (!isrestart)\n\t\t{\n\t\t\tif (q3singleplayer)\n\t\t\t{\n\t\t\t\tCvar_ForceSet(gametype, \"2\");//singleplayer\n\t\t\t\tCvar_ForceSet(&deathmatch, \"0\");//for non-q3 type stuff to not get confused..\n\t\t\t}\n\t\t\telse if (gametype->value == 2)\n\t\t\t\tCvar_ForceSet(gametype, \"\");//force to ffa deathmatch\n\t\t}\n\t}\n#endif\n\n\tCvar_ForceSet(&host_mapname, level);\n\n#ifdef HAVE_CLIENT\n\tMenu_PopAll();\n#endif\n\n\tif (preserveplayers && svprogfuncs)\n\t{\n\t\tfor (i=0 ; i<svs.allocated_client_slots ; i++)\t//we need to drop all q2 clients. We don't mix q1w with q2.\n\t\t{\n\t\t\tchar buffer[8192], *buf;\n\t\t\tsize_t bufsize = 0;\n\t\t\tif (svs.clients[i].state>cs_connected)\n\t\t\t{\n\t\t\t\tbuf = svprogfuncs->saveent(svprogfuncs, buffer, &bufsize, sizeof(buffer), svs.clients[i].edict);\n\t\t\t\tif (svs.clients[i].spawninfo)\n\t\t\t\t\tZ_Free(svs.clients[i].spawninfo);\n\t\t\t\tsvs.clients[i].spawninfo = Z_Malloc(bufsize+1);\n\t\t\t\tmemcpy(svs.clients[i].spawninfo, buf, bufsize+1);\n\t\t\t\tsvs.clients[i].spawninfotime = sv.time;\n\t\t\t}\n\t\t}\n\t}\n\telse\n\t{\n\t\tfor (i=0 ; i<svs.allocated_client_slots ; i++)\t//we need to drop all q2 clients. We don't mix q1w with q2.\n\t\t{\n\t\t\tif (svs.clients[i].state>cs_connected)\t//so that we don't send a datagram\n\t\t\t\tsvs.clients[i].state=cs_connected;\n\t\t}\n\t}\n\n#ifndef SERVERONLY\n\tS_StopAllSounds (true);\n//\tSCR_BeginLoadingPlaque();\n\tSCR_ImageName(level);\n#endif\n\n//\tif (!preserveplayers)\n\t{\n\t\tfor (i=0, host_client = svs.clients ; i<svs.allocated_client_slots ; i++, host_client++)\n\t\t{\n\t\t\t/*pass the new map's name as an extension, so appropriate loading screens can be shown*/\n\t\t\tif (host_client->controller == NULL)\n\t\t\t{\n\t\t\t\tif (ISNQCLIENT(host_client))\n\t\t\t\t{\n\t\t\t\t\tif (ISDPCLIENT(host_client))\n\t\t\t\t\t{\n\t\t\t\t\t\t//DP clients cannot cope with being told the next map's name\n\t\t\t\t\t\tSV_StuffcmdToClient(host_client, \"reconnect\\n\");\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\tSV_StuffcmdToClient(host_client, va(\"reconnect \\\"%s\\\"\\n\", level));\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tSV_StuffcmdToClient(host_client, va(\"changing \\\"%s\\\"\\n\", level));\n\t\t\t}\n\t\t\thost_client->prespawn_stage = PRESPAWN_INVALID;\n\t\t\thost_client->prespawn_idx = 0;\n\t\t}\n\n#ifdef NQPROT\n\t\tif (dpcompat_nopreparse.ival)\n\t\t{\t//wipe broadcasts here...\n\t\t\tsv.reliable_datagram.cursize = 0;\n\t\t\tsv.datagram.cursize = 0;\n\t\t\tsv.nqreliable_datagram.cursize = 0;\n\t\t\tsv.nqdatagram.cursize = 0;\n\t\t}\n#endif\n\n\t\tSV_SendMessagesToAll ();\n\n\t\tif (flushparms)\n\t\t\tsvs.serverflags = 0;\n\t}\n\n\tSCR_SetLoadingFile(\"spawnserver\");\n#ifdef SAVEDGAMES\n\tif (newunit || !startspot || cinematic || !SV_LoadLevelCache(NULL, level, startspot, false))\n#endif\n\t{\n\t\tif (waschangelevel && !startspot)\n\t\t\tstartspot = \"\";\n\t\tSV_SpawnServer (level, startspot, mapeditor, cinematic, 0);\n\t}\n\tSCR_SetLoadingFile(\"server spawned\");\n\n\t//SV_BroadcastCommand (\"cmd new\\n\");\n\tfor (i=0, host_client = svs.clients ; i<svs.allocated_client_slots ; i++, host_client++)\n\t{\t//this expanded code cuts out a packet when changing maps...\n\t\t//but more usefully, it stops dp(and probably nq too) from timing out.\n\t\t//make sure its all reset.\n\t\thost_client->sentents.num_entities = 0;\n\t\thost_client->ratetime = 0;\n\t\tif (host_client->pendingdeltabits)\n\t\t\thost_client->pendingdeltabits[0] = UF_SV_REMOVE;\n\n\t\tif (flushparms)\n\t\t{\n\t\t\tif (host_client->spawninfo)\n\t\t\t\tZ_Free(host_client->spawninfo);\n\t\t\thost_client->spawninfo = NULL;\n\t\t\tmemset(host_client->spawn_parms, 0, sizeof(host_client->spawn_parms));\n\t\t\tif (host_client->state > cs_zombie)\n\t\t\t\tSV_GetNewSpawnParms(host_client);\n\t\t}\n\n\t\tif (preserveplayers && svprogfuncs && host_client->state == cs_spawned && host_client->spawninfo)\n\t\t{\n\t\t\tsize_t j = 0;\n\t\t\tsvprogfuncs->restoreent(svprogfuncs, host_client->spawninfo, &j, host_client->edict);\n\t\t\thost_client->istobeloaded = true;\n\t\t\thost_client->state=cs_connected;\n\t\t\tif (host_client->spectator)\n\t\t\t\tsv.spawned_observer_slots++;\n\t\t\telse\n\t\t\t\tsv.spawned_client_slots++;\n\t\t}\n\n\t\tif (host_client->controller)\n\t\t\tcontinue;\n\t\tif (host_client->state>=cs_connected)\n\t\t{\n\t\t\tif (host_client->protocol == SCP_QUAKE3)\n\t\t\t\tcontinue;\n\t\t\tif (host_client->protocol == SCP_BAD)\n\t\t\t\tcontinue;\n\n#ifdef NQPROT\n\t\t\tif (ISNQCLIENT(host_client))\n\t\t\t{\n\t\t\t\tSVNQ_New_f();\n\t\t\t\thost_client->send_message = true;\n\t\t\t}\n\t\t\telse\n#endif\n\t\t\t\tSV_New_f();\n\t\t}\n\t}\n\n\tif (!isrestart)\n\t{\n\t\tcvar_t *nsv;\n\t\tnsv = Cvar_Get(\"nextserver\", \"\", 0, \"\");\n\t\tif (nextserver)\n\t\t\tCvar_Set(nsv, va(\"gamemap \\\"%s\\\"\", nextserver));\n\t\telse\n\t\t\tCvar_Set(nsv, \"\");\n\t}\n\n#ifdef SAVEDGAMES\n\tif (q2savetos0)\n\t{\n\t\tif (sv.state != ss_cinematic)\t//too weird.\n\t\t\tSV_Savegame(\"s0\", true);\n\t}\n#endif\n\n\tif (isDedicated)\n\t\tMod_Purge(MP_MAPCHANGED);\n}\n\nstatic void SV_KillServer_f(void)\n{\n\tSV_UnspawnServer();\n}\n\n\n/*\n==================\nSV_Kick_f\n\nKick a user off of the server\n==================\n*/\nstatic void SV_Kick_f (void)\n{\n\tclient_t\t*cl;\n\tint clnum=-1;\n\n\tif (!sv.state)\n\t\treturn;\n\n\tif (!strcmp(Cmd_Argv(1), \"#\"))\n\t{\n\t\tclnum = atoi(Cmd_Argv(2)) - 1;\n\t\tif (clnum >= 0 && clnum < sv.allocated_client_slots)\n\t\t{\n\t\t\tcl = &svs.clients[clnum];\n\t\t\tif (cl->state >= cs_connected)\n\t\t\t{\n\t\t\t\tSV_BroadcastTPrintf (PRINT_HIGH, \"%s was kicked\\n\", cl->name);\n\t\t\t\t// print directly, because the dropped client won't get the\n\t\t\t\t// SV_BroadcastPrintf message\n\t\t\t\tSV_ClientTPrintf (cl, PRINT_HIGH, \"You were kicked\\n\");\n\n\t\t\t\tSV_LogPlayer(cl, \"kicked\");\n\t\t\t\tSV_DropClient (cl);\n\t\t\t}\n\t\t}\n\t\treturn;\n\t}\n\n\twhile((cl = SV_GetClientForString(Cmd_Argv(1), &clnum)))\n\t{\n\t\tSV_BroadcastTPrintf (PRINT_HIGH, \"%s was kicked\\n\", cl->name);\n\t\t// print directly, because the dropped client won't get the\n\t\t// SV_BroadcastPrintf message\n\t\tSV_ClientTPrintf (cl, PRINT_HIGH, \"You were kicked\\n\");\n\n\t\tSV_LogPlayer(cl, \"kicked\");\n\t\tSV_DropClient (cl);\n\t}\n\n\tif (clnum == -1)\n\t\tCon_TPrintf (\"Couldn't find user number %s\\n\", Cmd_Argv(1));\n}\n\n/*for q3's kick bot menu*/\nstatic void SV_KickSlot_f (void)\n{\n\tclient_t\t*cl;\n\tint clnum=atoi(Cmd_Argv(1));\n\n\tif (!sv.state)\n\t\treturn;\n\n\tif (clnum < sv.allocated_client_slots && svs.clients[clnum].state)\n\t{\n\t\tcl = &svs.clients[clnum];\n\n\t\tSV_BroadcastTPrintf (PRINT_HIGH, \"%s was kicked\\n\", cl->name);\n\t\t// print directly, because the dropped client won't get the\n\t\t// SV_BroadcastPrintf message\n\t\tSV_ClientTPrintf (cl, PRINT_HIGH, \"You were kicked\\n\");\n\n\t\tSV_LogPlayer(cl, \"kicked\");\n\t\tSV_DropClient (cl);\n\t}\n\telse\n\t\tCon_Printf(\"Client %i is not active\\n\", clnum);\n}\n\n//ipv4ify if its an ipv6 ipv4-mapped address.\nstatic netadr_t *NET_IPV4ify(netadr_t *a, netadr_t *tmp)\n{\n\tif (a->type == NA_IPV6 &&\n\t\t!*(int*)&a->address.ip6[0] &&\n\t\t!*(int*)&a->address.ip6[4] &&\n\t\t!*(short*)&a->address.ip6[8] &&\n\t\t*(short*)&a->address.ip6[10]==(short)0xffff)\n\t{\n\t\ttmp->type = NA_IP;\n\t\ttmp->connum = a->connum;\n\t\ttmp->scopeid = a->scopeid;\n\t\ttmp->port = a->port;\n\t\ttmp->prot = a->prot;\n\t\ttmp->address.ip[0] = a->address.ip6[12];\n\t\ttmp->address.ip[1] = a->address.ip6[13];\n\t\ttmp->address.ip[2] = a->address.ip6[14];\n\t\ttmp->address.ip[3] = a->address.ip6[15];\n\t\ta = tmp;\n\t}\n\treturn a;\n}\n\n//will kick clients if they got banned (without being safe)\nvoid SV_EvaluatePenalties(client_t *cl)\n{\n\tbannedips_t *banip;\n\tunsigned int penalties = 0, delta, p;\n\tchar *penaltyreason[countof(banflags)] = {NULL};\n\tconst char *activepenalties[countof(banflags)];\n\tchar *reasons[countof(banflags)] = {NULL};\n\tint numpenalties = 0;\n\tint numreasons = 0;\n\tint i;\n\tnetadr_t tmp, *a;\n\n\tif (cl->realip.type != NA_INVALID)\n\t{\n\t\ta = NET_IPV4ify(&cl->realip, &tmp);\n\t\tfor (banip = svs.bannedips; banip; banip=banip->next)\n\t\t{\n\t\t\tif (NET_CompareAdrMasked(a, &banip->adr, &banip->adrmask))\n\t\t\t{\n\t\t\t\tfor (i = 0; i < sizeof(penaltyreason)/sizeof(penaltyreason[0]); i++)\n\t\t\t\t{\n\t\t\t\t\tp = 1u<<i;\n\t\t\t\t\tif (banip->banflags & p)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (!penaltyreason[i])\n\t\t\t\t\t\t\tpenaltyreason[i] = banip->reason;\n\t\t\t\t\t\tpenalties |= p;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\ta = NET_IPV4ify(&cl->netchan.remote_address, &tmp);\n\tfor (banip = svs.bannedips; banip; banip=banip->next)\n\t{\n\t\tif (NET_CompareAdrMasked(a, &banip->adr, &banip->adrmask))\n\t\t{\n\t\t\tfor (i = 0; i < sizeof(penaltyreason)/sizeof(penaltyreason[0]); i++)\n\t\t\t{\n\t\t\t\tp = 1u<<i;\n\t\t\t\tif (banip->banflags & p)\n\t\t\t\t{\n\t\t\t\t\tif (!penaltyreason[i])\n\t\t\t\t\t\tpenaltyreason[i] = banip->reason;\n\t\t\t\t\tpenalties |= p;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tdelta = cl->penalties ^ penalties;\n\tcl->penalties = penalties;\n\n\tif ((penalties & (BAN_BAN | BAN_PERMIT)) == BAN_BAN)\n\t{\n\t\t//we should only reach here by a player getting banned mid-game.\n\t\tif (penaltyreason[BAN_BAN])\n\t\t\tSV_BroadcastPrintf(PRINT_HIGH, \"%s was banned: %s\\n\", cl->name, penaltyreason[BAN_BAN]);\n\t\telse\n\t\t\tSV_BroadcastPrintf(PRINT_HIGH, \"%s was banned\\n\", cl->name);\n\t\tcl->drop = true;\n\t}\n\n\t//don't announce these now.\n\tdelta &= ~(BAN_BAN | BAN_PERMIT);\n\n\t//deaf+mute sees no (other) penalty messages\n\tif (((penalties|delta) & (BAN_MUTE|BAN_DEAF)) == (BAN_MUTE|BAN_DEAF))\n\t\tdelta &= ~(BAN_MUTE|BAN_DEAF);\n\n\tif ((delta|penalties) & BAN_STEALTH)\n\t\tdelta = 0;\t//don't announce ANY.\n\n\tif (cl->controller)\n\t\tdelta = 0;\t//don't spam it for every player in a splitscreen client.\n\n\tif (delta & BAN_VIP)\n\t{\n\t\tdelta &= ~BAN_VIP;\t//don't refer to this as a penalty\n\t\tif (penalties & BAN_VIP)\n\t\t\tSV_PrintToClient(cl, PRINT_HIGH, \"You are a VIP, apparently\\n\");\n\t\telse\n\t\t\tSV_PrintToClient(cl, PRINT_HIGH, \"VIP expired\\n\");\n\t}\n\n\tfor (i = 0; i < countof(banflags); i++)\n\t{\n\t\tp = banflags[i].banflag;\n\t\tif (delta & p)\n\t\t{\n\t\t\tif (penalties & p)\n\t\t\t{\n\t\t\t\tif (banflags[i].names[0])\n\t\t\t\t\tactivepenalties[numpenalties++] = banflags[i].names[0];\n\t\t\t\tif (reasons[i] && *reasons[i])\n\t\t\t\t\treasons[numreasons++] = reasons[i];\n\t\t\t}\n\t\t\telse\n\t\t\t\tSV_PrintToClient(cl, PRINT_HIGH, va(\"Penalty expired: %s\\n\", banflags[i].names[0]));\n\t\t}\n\t}\n\n\tif (numpenalties)\n\t{\n\t\tchar penaltystring[1024];\n\t\tint i, j;\n\t\tQ_strncpyz(penaltystring, \"You are penalised: \", sizeof(penaltystring));\n\t\tfor (i = 0; i < numpenalties; i++)\n\t\t{\n\t\t\tif (i && i == numpenalties-1)\n\t\t\t\tQ_strncatz(penaltystring, \" and \", sizeof(penaltystring));\n\t\t\telse if (i)\n\t\t\t\tQ_strncatz(penaltystring, \", \", sizeof(penaltystring));\n\t\t\tQ_strncatz(penaltystring, activepenalties[i], sizeof(penaltystring));\n\t\t}\n\t\tQ_strncatz(penaltystring, \"\\n\", sizeof(penaltystring));\n\t\tSV_PrintToClient(cl, PRINT_HIGH, penaltystring);\n\t\tfor (i = 0; i < numreasons; i++)\n\t\t{\n\t\t\tif (*reasons[i])\n\t\t\t{\n\t\t\t\tfor(j = 0; j < i; j++)\n\t\t\t\t\tif (!strcmp(reasons[i], reasons[j]))\n\t\t\t\t\t\tbreak;\n\t\t\t\tif (i == j)\n\t\t\t\t\tSV_PrintToClient(cl, PRINT_HIGH, va(\"  %s\\n\", reasons[i]));\n\t\t\t}\n\t\t}\n\t}\n\n\tif (delta & BAN_VIP)\n\t\tInfoBuf_SetStarKey(&cl->userinfo, \"*VIP\", (cl->penalties & BAN_VIP)?\"1\":\"\");\n\tif (delta & BAN_MAPPER)\n\t\tInfoBuf_SetStarKey(&cl->userinfo, \"*mapper\", (cl->penalties & BAN_MAPPER)?\"1\":\"\");\n}\n\nstatic time_t reevaluatebantime;\nstatic qboolean reevaluatebans;\n//could use time(NULL) instead, but this avoids a system call.\nstatic time_t SV_BanTime(void)\n{\n\tstatic double bantimemark;\n\tstatic time_t banstarttime;\n\tif (!banstarttime)\n\t{\n\t\tbanstarttime = time(NULL);\n\t\tbantimemark = realtime;\n\t}\n\treturn banstarttime + (realtime - bantimemark);\n}\n//removes anything with an expiry time in the past.\n//avoids walking the list if there's nothing changed.\n//can be used to force penalty reevaluation.\nvoid SV_KillExpiredBans(void)\n{\n\tbannedips_t **link, *banip;\n\ttime_t curtime = SV_BanTime();\n\tint i;\n\tif (reevaluatebantime && curtime > reevaluatebantime)\n\t{\n\t\treevaluatebantime = 0;\n\t\tfor(link = &svs.bannedips; (banip = *link) != NULL; )\n\t\t{\n\t\t\tif (banip->expiretime)\n\t\t\t{\n\t\t\t\tif (banip->expiretime < curtime)\n\t\t\t\t{\n\t\t\t\t\treevaluatebans = true;\n\t\t\t\t\t*link = banip->next;\n\t\t\t\t\tZ_Free(banip);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (!reevaluatebantime || reevaluatebantime > banip->expiretime)\n\t\t\t\t\treevaluatebantime = banip->expiretime+1;\n\t\t\t}\n\t\t\tlink = &banip->next;\n\t\t}\n\t}\n\n\tif (reevaluatebans)\n\t{\n\t\treevaluatebans = false;\n\t\tfor (i = 0; i < svs.allocated_client_slots; i++)\n\t\t{\n\t\t\tif (svs.clients[i].state<=cs_loadzombie)\n\t\t\t\tcontinue;\n\n\t\t\tSV_EvaluatePenalties(&svs.clients[i]);\n\t\t}\n\t}\n}\n\n//adds a new ban/penalty.\n//will remove old penalties if the new one has a longer duration, otherwise will ignore the add.\nstatic qboolean SV_AddBanEntry(bannedips_t *proto, char *reason)\n{\n\tbannedips_t *nb, **link;\n\tnb = svs.bannedips;\n\twhile (nb)\n\t{\n\t\tif (NET_CompareAdr(&nb->adr, &proto->adr) && NET_CompareAdr(&nb->adrmask, &proto->adrmask))\n\t\t{\n\t\t\t//found a match, figure out which lasts longer\n\t\t\t//the shorter ban duration gets its effective banflags stripped.\n\t\t\tif ((proto->expiretime && proto->expiretime < nb->expiretime) || !nb->expiretime)\n\t\t\t\tproto->banflags &= ~nb->banflags;\n\t\t\telse\n\t\t\t\tnb->banflags &= ~proto->banflags;\n\n\t\t\tif (!proto->banflags)\n\t\t\t{\n\t\t\t\t//we should not have been able to strip a previous nb->banflags if this ban was duped later.\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tif (!nb->banflags)\n\t\t\t\treevaluatebantime = nb->expiretime = 1;\t//make sure it expires 'soon'.\n\t\t}\n\t\tnb = nb->next;\n\t}\n\n\tlink = &svs.bannedips;\n\n\t// add IP and mask to filter list\n\tnb = Z_Malloc(sizeof(bannedips_t) + strlen(reason));\n\tnb->adr = proto->adr;\n\tnb->adrmask = proto->adrmask;\n\tnb->banflags = proto->banflags;\n\tnb->expiretime = proto->expiretime;\n\tQ_strcpy(nb->reason, reason);\n\n\tnb->next = *link;\n\t*link = nb;\n\n\treevaluatebans = true;\t//make sure the new ban/penalty applies to the right IPs.\n\tif (nb->expiretime && (!reevaluatebantime || reevaluatebantime > nb->expiretime))\n\t\treevaluatebantime = nb->expiretime;\n\treturn true;\n}\n\n//slightly different logic.\n//if duration is specified, just does an add instead.\n//otherwise ignores durations.\n//only really works with a single toggle. if any are found, will not add.\n//returns 1 if added, 0 if removed, and -1 if tried to add and it already existed.\nstatic int SV_ToggleBan(bannedips_t *proto, char *reason)\n{\n\tqboolean found = false;\n\tbannedips_t *nb;\n\tif (proto->expiretime)\n\t\treturn SV_AddBanEntry(proto, reason)?true:-1;\n\n\tnb = svs.bannedips;\n\twhile (nb)\n\t{\n\t\tif (NET_CompareAdr(&nb->adr, &proto->adr) && NET_CompareAdr(&nb->adrmask, &proto->adrmask))\n\t\t{\n\t\t\tif (nb->banflags & proto->banflags)\n\t\t\t{\n\t\t\t\tfound = true;\n\t\t\t\tnb->banflags &= ~proto->banflags;\n\t\t\t\treevaluatebans = true;\n\t\t\t\tif (!nb->banflags)\n\t\t\t\t\treevaluatebantime = nb->expiretime = 1;\t//make sure it expires 'soon' (in the past).\n\t\t\t}\n\t\t}\n\t\tnb = nb->next;\n\t}\n\n\tif (found)\n\t\treturn 0;\n\treturn SV_AddBanEntry(proto, reason)?true:-1;\n}\n\nextern cvar_t filterban;\n//returns a reason if the client is banned. ignores other penalties.\nchar *SV_BannedReason (netadr_t *a)\n{\n\tchar *reason = filterban.value?NULL:\"\";\t//\"\" = banned with no explicit reason\n\tbannedips_t *banip;\n\tnetadr_t tmp;\n\n\tif (NET_IsLoopBackAddress(a))\n\t\treturn NULL; // never filter loopback\n\n\ta = NET_IPV4ify(a, &tmp);\n\n\tfor (banip = svs.bannedips; banip; banip=banip->next)\n\t{\n\t\tif (NET_CompareAdrMasked(a, &banip->adr, &banip->adrmask))\n\t\t{\n\t\t\tif (banip->banflags & BAN_BAN)\n\t\t\t\treturn banip->reason;\t//banned, with reason.\n\t\t\tif (banip->banflags & BAN_PERMIT)\n\t\t\t\treturn NULL;\t//allowed\n\t\t}\n\t}\n\treturn reason;\n}\n\nstatic void SV_FilterIP_f (void)\n{\n\tbannedips_t proto;\n\textern cvar_t filterban;\n\tchar *s;\n\tint i;\n\n\tif (Cmd_Argc() < 2)\n\t{\n\t\tCon_Printf(\"%s <address/mask|adress/maskbits> [flags] [+time] [reason]\\n\", Cmd_Argv(0));\n\t\tCon_Printf(\"allowed flags: %s\", banflags[0].names[0]);\n\t\tfor (i = 1; i < countof(banflags); i++)\n\t\t\tCon_Printf(\",%s\", banflags[i].names[0]);\n\t\tCon_Printf(\". time is in seconds (omitting the plus will be taken to mean unix time).\\n\");\n\t\treturn;\n\t}\n\n\tif (!NET_StringToAdrMasked(Cmd_Argv(1), true, &proto.adr, &proto.adrmask))\n\t{\n\t\tCon_Printf(\"invalid address or mask\\n\");\n\t\treturn;\n\t}\n\n\ts = Cmd_Argv(2);\n\tproto.banflags = 0;\n\twhile(*s)\n\t{\n\t\ts=COM_ParseToken(s,\",\");\n\t\tif (!Q_strcasecmp(com_token, \",\"))\n\t\t\ti = -1;\n\t\telse for (i = 0; i < countof(banflags); i++)\n\t\t{\n\t\t\tif (!Q_strcasecmp(com_token, banflags[i].names[0]) || (banflags[i].names[1] && !Q_strcasecmp(com_token, banflags[i].names[1])))\n\t\t\t{\n\t\t\t\tproto.banflags |= banflags[i].banflag;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (i == countof(banflags))\n\t\t\tCon_Printf(\"Unknown ban/penalty flag: %s. ignoring.\\n\", com_token);\n\t}\n\t//if no flags were specified,\n\tif (!proto.banflags)\n\t{\n\t\tif (!strcmp(Cmd_Argv(0), \"ban\"))\n\t\t\tproto.banflags = BAN_BAN;\n\t\telse\n\t\t\tproto.banflags = filterban.ival?BAN_BAN:BAN_PERMIT;\n\t}\n\n\tif (NET_IsLoopBackAddress(&proto.adr) && (proto.banflags & BAN_NOLOCALHOST))\n\t{\t//do allow them to be muted etc, just not banned outright.\n\t\tCon_Printf(\"You're not allowed to filter loopback!\\n\");\n\t\treturn;\n\t}\n\n\ts = Cmd_Argv(3);\n\tif (*s == '+')\n\t{\n\t\ttime_t secs = strtoull(s+1, &s, 0);\n\t\tif (*s == ':')\n\t\t{\n\t\t\tsecs*=60;\n\t\t\tsecs+=strtoull(s+1, &s, 0);\n\t\t}\n\t\tproto.expiretime = SV_BanTime() + secs;\n\t}\n\telse\n\t\tproto.expiretime = strtoull(s, NULL, 0);\n\n\t//and then add it\n\tif (!SV_AddBanEntry(&proto, Cmd_Argv(4)))\n\t\tCon_Printf(\"addip: entry already exists\\n\");\n}\n\nstatic void SV_BanList_f (void)\n{\n\tint bancount = 0;\n\tbannedips_t *nb;\n\tchar adr[MAX_ADR_SIZE];\n\tchar middlebit[256];\n\ttime_t bantime = SV_BanTime();\n\n\tSV_KillExpiredBans();\n\n\tfor (nb = svs.bannedips; nb; nb = nb->next)\n\t{\n\t\tif (nb->banflags & BAN_BAN)\n\t\t{\n\t\t\t*middlebit = 0;\n\t\t\tif (nb->expiretime)\n\t\t\t\tQ_strncatz(middlebit, va(\",\\t+%\"PRIu64, (quint64_t)(nb->expiretime - bantime)), sizeof(middlebit));\n\t\t\tif (nb->reason[0])\n\t\t\t\tQ_strncatz(middlebit, \",\\t\", sizeof(middlebit));\n\t\t\tCon_Printf(\"%s%s%s\\n\", NET_AdrToStringMasked(adr, sizeof(adr), &nb->adr, &nb->adrmask), middlebit, nb->reason);\n\t\t\tbancount++;\n\t\t}\n\t}\n\n\tCon_Printf(\"%i total entries in ban list\\n\", bancount);\n}\n\nstatic void SV_FilterList_f (void)\n{\n\tint filtercount = 0;\n\tbannedips_t *nb;\n\tchar adr[MAX_ADR_SIZE];\n\tchar banflagtext[1024];\n\tint i;\n\ttime_t curtime = SV_BanTime();\n\n\tSV_KillExpiredBans();\n\n\tfor (nb = svs.bannedips; nb; )\n\t{\n\t\t*banflagtext = 0;\n\t\tfor (i = 0; i < countof(banflags); i++)\n\t\t{\n\t\t\tif (nb->banflags & banflags[i].banflag)\n\t\t\t{\n\t\t\t\tif (*banflagtext)\n\t\t\t\t\tQ_strncatz(banflagtext, \",\", sizeof(banflagtext));\n\t\t\t\tQ_strncatz(banflagtext, banflags[i].names[0], sizeof(banflagtext));\n\t\t\t}\n\t\t}\n\n\t\tif (nb->expiretime)\n\t\t{\n\t\t\ttime_t secs = nb->expiretime - curtime;\n\t\t\tCon_Printf(\"%s %s +%\"PRIu64\":%02u\\n\", NET_AdrToStringMasked(adr, sizeof(adr), &nb->adr, &nb->adrmask), banflagtext, (quint64_t)(secs/60), (unsigned int)(secs%60));\n\t\t}\n\t\telse\n\t\t\tCon_Printf(\"%s %s\\n\", NET_AdrToStringMasked(adr, sizeof(adr), &nb->adr, &nb->adrmask), banflagtext);\n\t\tfiltercount++;\n\t\tnb = nb->next;\n\t}\n\n\tCon_Printf(\"%i total entries in filter list\\n\", filtercount);\n}\n\nstatic void SV_Unfilter_f (void)\n{\n\tqboolean found = false;\n\tqboolean all = false;\n\tbannedips_t **link;\n\tbannedips_t *nb;\n\tnetadr_t unbanadr = {0};\n\tnetadr_t unbanmask = {0};\n\tchar adr[MAX_ADR_SIZE];\n\tunsigned int clearbanflags, nf;\n\tchar *s;\n\tint i;\n\n\tSV_KillExpiredBans();\n\n\tif (Cmd_Argc() < 2)\n\t{\n\t\tCon_Printf(\"%s address/mask|address/maskbits|all [flags]\\n\", Cmd_Argv(0));\n\t\treturn;\n\t}\n\n\tif (!Q_strcasecmp(Cmd_Argv(1), \"all\"))\n\t{\n\t\tCon_Printf(\"removing all filtered addresses\\n\");\n\t\tall = true;\n\t}\n\telse if (!NET_StringToAdrMasked(Cmd_Argv(1), true, &unbanadr, &unbanmask))\n\t{\n\t\tCon_Printf(\"invalid address or mask\\n\");\n\t\treturn;\n\t}\n\n\ts = Cmd_Argv(2);\n\tclearbanflags = 0;\n\twhile(*s)\n\t{\n\t\ts=COM_ParseToken(s,\",\");\n\t\tif (!Q_strcasecmp(com_token, \",\"))\n\t\t\ti = -1;\n\t\telse for (i = 0; i < countof(banflags); i++)\n\t\t{\n\t\t\tif (!Q_strcasecmp(com_token, banflags[i].names[0]) || (banflags[i].names[1] && !Q_strcasecmp(com_token, banflags[i].names[1])))\n\t\t\t{\n\t\t\t\tclearbanflags |= banflags[i].banflag;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (i == countof(banflags))\n\t\t\tCon_Printf(\"Unknown ban/penalty flag: %s. ignoring.\\n\", com_token);\n\t}\n\t//if no flags were specified, assume all\n\tif (!clearbanflags)\n\t\tclearbanflags = ~0u;\n\n\tfor (link = &svs.bannedips ; (nb = *link) ; )\n\t{\n\t\tif ((nb->banflags & clearbanflags) && (all || (NET_CompareAdr(&nb->adr, &unbanadr) && NET_CompareAdr(&nb->adrmask, &unbanmask))))\n\t\t{\n\t\t\tfound = true;\n\t\t\tif (!all)\n\t\t\t\tCon_Printf(\"unfiltered %s\\n\", NET_AdrToStringMasked(adr, sizeof(adr), &nb->adr, &nb->adrmask));\n\n\t\t\tnf = nb->banflags & clearbanflags;\n\t\t\tnb->banflags -= nf;\n\t\t\tif (!nb->banflags)\n\t\t\t{\n\t\t\t\t//this entry no longer has any flags\n\t\t\t\t*link = nb->next;\n\t\t\t\tZ_Free(nb);\n\t\t\t}\n\t\t\telse\n\t\t\t\tlink = &(*link)->next;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tlink = &(*link)->next;\n\t\t}\n\t}\n\n\tif (!all && !found)\n\t\tCon_Printf(\"address was not filtered\\n\");\n\n\tif (found)\n\t{\n\t\treevaluatebans = true;\n\t\tSV_KillExpiredBans();\n\t}\n}\nstatic void SV_PenaltyToggle (unsigned int banflag, char *penaltyname)\n{\n\tchar *clname = Cmd_Argv(1);\n\tchar *duration = Cmd_Argv(2);\n\tchar *reason = Cmd_Argv(3);\n\tbannedips_t proto = {0};\n\tclient_t *cl;\n\tqboolean found = false;\n\tint clnum=-1;\n\tnetadr_t tmp;\n\n\tproto.banflags = banflag;\n\n\tif (*duration == '+')\n\t\tproto.expiretime = SV_BanTime() + strtoull(duration+1, &duration, 0);\n\telse\n\t\tproto.expiretime = strtoull(duration, &duration, 0);\n\n\t//both of these should work\n\t//cuff foo \"cos they're morons\"\n\t//cuff foo +10 \"cos they're morons\"\n\tif (!*reason && *duration)\n\t\treason = duration;\n\n\tmemset(&proto.adrmask.address, 0xff, sizeof(proto.adrmask.address));\n\twhile((cl = SV_GetClientForString(clname, &clnum)))\n\t{\n\t\tfound = true;\n\t\tproto.adr = *NET_IPV4ify(&cl->netchan.remote_address, &tmp);\n\t\tproto.adr.port = 0;\n\t\tproto.adrmask.type = proto.adr.type;\n\n\t\tif (NET_IsLoopBackAddress(&proto.adr) && (proto.banflags & BAN_NOLOCALHOST))\n\t\t{\n\t\t\tCon_Printf(\"You're not allowed to filter loopback!\\n\");\n\t\t\tcontinue;\n\t\t}\n\n\t\tswitch(SV_ToggleBan(&proto, reason))\n\t\t{\n\t\tcase 1:\n\t\t\tCon_Printf(\"%s: %s is now %s\\n\", Cmd_Argv(0), cl->name, penaltyname);\n\t\t\tbreak;\n\t\tcase 0:\n\t\t\tCon_Printf(\"%s: %s is no longer %s\\n\", Cmd_Argv(0), cl->name, penaltyname);\n\t\t\tbreak;\n\t\tdefault:\n\t\tcase -1:\n\t\t\tCon_Printf(\"%s: %s already %s\\n\", Cmd_Argv(0), cl->name, penaltyname);\n\t\t\tbreak;\n\t\t}\n\t}\n\tif (!found)\n\t\tCon_Printf(\"%s: no clients\\n\", Cmd_Argv(0));\n}\n\nvoid SV_AutoAddPenalty (client_t *cl, unsigned int banflag, int duration, char *reason)\n{\n\tbannedips_t proto;\n\n\tproto.banflags = banflag;\n\tproto.expiretime = SV_BanTime() + duration;\n\tmemset(&proto.adrmask.address, 0xff, sizeof(proto.adrmask.address));\n\tproto.adr = cl->netchan.remote_address;\n\tproto.adr.port = 0;\n\tproto.adrmask.type = proto.adr.type;\n\n\tSV_AddBanEntry(&proto, reason);\n\n\tfor (cl = (cl->controller?cl->controller:cl); cl; cl = cl->controlled)\n\t\tSV_EvaluatePenalties(cl);\n}\nvoid SV_AutoBanSender (int duration, char *reason)\n{\n\tbannedips_t proto;\n\n\tproto.banflags = BAN_BAN;\n\tproto.expiretime = SV_BanTime() + duration;\n\tmemset(&proto.adrmask.address, 0xff, sizeof(proto.adrmask.address));\n\tproto.adr = net_from;\n\tproto.adr.port = 0;\n\tproto.adrmask.type = proto.adr.type;\n\n\tSV_AddBanEntry(&proto, reason);\n}\n\nstatic void SV_WriteIP_f (void)\n{\n\tvfsfile_t\t*f;\n\tchar\tname[MAX_OSPATH];\n\tbannedips_t *bi;\n\tchar *s;\n\tchar adr[MAX_ADR_SIZE];\n\tchar banflagtext[1024];\n\tint i;\n\n\tSV_KillExpiredBans();\n\n\tstrcpy (name, \"listip.cfg\");\n\n\tCon_Printf (\"Writing %s.\\n\", name);\n\n\tf = FS_OpenVFS(name, \"wb\", FS_GAMEONLY);\n\tif (!f)\n\t{\n\t\tCon_Printf (\"Couldn't open %s\\n\", name);\n\t\treturn;\n\t}\n\n\tbi = svs.bannedips;\n\twhile (bi)\n\t{\n\t\t*banflagtext = 0;\n\t\tfor (i = 0; i < countof(banflags); i++)\n\t\t{\n\t\t\tif (bi->banflags & banflags[i].banflag)\n\t\t\t{\n\t\t\t\tif (*banflagtext)\n\t\t\t\t\tQ_strncatz(banflagtext, \",\", sizeof(banflagtext));\n\t\t\t\tQ_strncatz(banflagtext, banflags[i].names[0], sizeof(banflagtext));\n\t\t\t}\n\t\t}\n\t\tif (bi->reason[0])\n\t\t\ts = va(\"addip %s %s %\"PRIu64\" \\\"%s\\\"\\n\", NET_AdrToStringMasked(adr, sizeof(adr), &bi->adr, &bi->adrmask), banflagtext, (quint64_t) bi->expiretime, bi->reason);\n\t\telse if (bi->expiretime)\n\t\t\ts = va(\"addip %s %s %\"PRIu64\"\\n\", NET_AdrToStringMasked(adr, sizeof(adr), &bi->adr, &bi->adrmask), banflagtext, (quint64_t) bi->expiretime);\n\t\telse\n\t\t\ts = va(\"addip %s %s\\n\", NET_AdrToStringMasked(adr, sizeof(adr), &bi->adr, &bi->adrmask), banflagtext);\n\t\tVFS_WRITE(f, s, strlen(s));\n\t\tbi = bi->next;\n\t}\n\n\tVFS_CLOSE (f);\n}\n\n\nstatic void SV_ForceName_f (void)\n{\n\tclient_t\t*cl;\n\tint clnum=-1;\n\n\twhile((cl = SV_GetClientForString(Cmd_Argv(1), &clnum)))\n\t{\n\t\tInfoBuf_SetKey(&cl->userinfo, \"name\", Cmd_Argv(2));\n\t\tSV_LogPlayer(cl, \"name forced\");\n\t\tSV_ExtractFromUserinfo(cl, true);\n\t\tQ_strncpyz(cl->name, Cmd_Argv(2), sizeof(cl->namebuf));\n\t\tSV_BroadcastUserinfoChange(cl, true, \"name\", cl->name);\n\t\treturn;\n\t}\n\n\tif (clnum == -1)\n\t\tCon_TPrintf (\"Couldn't find user number %s\\n\", Cmd_Argv(1));\n}\n\nstatic void SV_CripplePlayer_f (void)\n{\n\tSV_PenaltyToggle(BAN_CRIPPLED, \"crippled\");\n}\n\nstatic void SV_Mute_f (void)\n{\n\tSV_PenaltyToggle(BAN_MUTE, \"muted\");\n}\nstatic void SV_StealthMute_f (void)\n{\n\tSV_PenaltyToggle(BAN_MUTE|BAN_STEALTH, \"stealth-muted\");\n}\n\nstatic void SV_Cuff_f (void)\n{\n\tSV_PenaltyToggle(BAN_CUFF, \"cuffed\");\n}\n\nstatic void SV_BanClientIP_f (void)\n{\n\tSV_PenaltyToggle(BAN_BAN, \"banned\");\n}\n\nstatic void SV_Floodprot_f(void)\n{\n\textern cvar_t sv_floodprotect;\n\textern cvar_t sv_floodprotect_messages;\n\textern cvar_t sv_floodprotect_interval;\n\textern cvar_t sv_floodprotect_silencetime;\n\n\tif (Cmd_Argc() == 1)\n\t{\n\t\tif (sv_floodprotect_messages.value <= 0 || !sv_floodprotect.value)\n\t\t\tCon_Printf(\"Flood protection is off.\\n\");\n\t\telse\n\t\t\tCon_Printf(\"Current flood protection settings: \\nAfter %g msgs for %g seconds, silence for %g seconds\\n\",\n\t\t\t\tsv_floodprotect_messages.value,\n\t\t\t\tsv_floodprotect_interval.value,\n\t\t\t\tsv_floodprotect_silencetime.value);\n\t\treturn;\n\t}\n\n\tif (Cmd_Argc() != 4)\n\t{\n\t\tCon_Printf(\"Usage: %s <messagerate> <ratepersecond> <silencetime>\\n\", Cmd_Argv(0));\n\t\treturn;\n\t}\n\n\tCvar_SetValue(&sv_floodprotect_messages, atof(Cmd_Argv(1)));\n\tCvar_SetValue(&sv_floodprotect_interval, atof(Cmd_Argv(2)));\n\tCvar_SetValue(&sv_floodprotect_silencetime, atof(Cmd_Argv(3)));\n}\n\nstatic void SV_StuffToClient_f(void)\n{\t//with this we emulate the progs 'stuffcmds' builtin\n\n\tclient_t\t*cl;\n\n\tint clnum=-1;\n\tchar *clientname = Cmd_Argv(1);\n\tchar *str;\n\tchar *c;\n\tchar *key;\n\n\tif (Cmd_Argc() < 3)\n\t{\n\t\tCon_Printf(\"%s <clientname> <consolecommand>\\n\", Cmd_Argv(0));\n\t\treturn;\n\t}\n\n\tCmd_ShiftArgs(1, Cmd_ExecLevel==RESTRICT_LOCAL);\n\tif (!strcmp(Cmd_Argv(1), \"bind\"))\n\t{\n\t\tkey = Z_Malloc(strlen(Cmd_Argv(2))+1);\n\t\tstrcpy(key, Cmd_Argv(2));\n\t\tCmd_ShiftArgs(2, Cmd_ExecLevel==RESTRICT_LOCAL);\n\t}\n\telse\n\t\tkey = NULL;\n\tstr = Cmd_Args();\n\n\twhile(*str <= ' ')\t//strim leading spaces\n\t{\n\t\tif (!*str)\n\t\t\tbreak;\n\t\tstr++;\n\t}\n\n\t//a list of safe, allowed commands. Allows any extention of this.\n\tif (strchr(str, '\\n') || strchr(str, ';') || (\n\t\t!strncmp(str, \"setinfo\", 7) &&\n\t\t!strncmp(str, \"quit\", 4) &&\n\t\t!strncmp(str, \"gl_fb\", 5) &&\n\t\t!strncmp(str, \"r_fb\", 4) &&\n\t\t!strncmp(str, \"say\", 3) &&\t//note that the say parsing could be useful here.\n\t\t!strncmp(str, \"echo\", 4) &&\n\t\t!strncmp(str, \"name\", 4) &&\n\t\t!strncmp(str, \"skin\", 4) &&\n\t\t!strncmp(str, \"color\", 5) &&\n\t\t!strncmp(str, \"cmd\", 3) &&\n\t\t!strncmp(str, \"fov\", 3) &&\n\t\t!strncmp(str, \"connect\", 7) &&\n\t\t!strncmp(str, \"rate\", 4) &&\n\t\t!strncmp(str, \"cd\", 2) &&\n\t\t!strncmp(str, \"easyrecord\", 10) &&\n\t\t!strncmp(str, \"leftisright\", 11) &&\n\t\t!strncmp(str, \"menu_\", 5) &&\n\t\t!strncmp(str, \"r_fullbright\", 12) &&\n\t\t!strncmp(str, \"toggleconsole\", 13) &&\n\t\t!strncmp(str, \"v_i\", 3) &&\t//idlescale vars\n\t\t!strncmp(str, \"bf\", 2) &&\n\t\t!strncmp(str, \"+\", 1) &&\n\t\t!strncmp(str, \"-\", 1) &&\n\t\t!strncmp(str, \"impulse\", 7) &&\n\t\t1))\n\t{\n\t\tCon_Printf(\"You're not allowed to stuffcmd that\\n\");\n\n\t\tif (key)\n\t\t\tZ_Free(key);\n\t\treturn;\n\t}\n\n\twhile((cl = SV_GetClientForString(clientname, &clnum)))\n\t{\n\t\tif (ISQ2CLIENT(cl))\n\t\t\tClientReliableWrite_Begin (cl, svcq2_stufftext, 3+strlen(str) + (key?strlen(key)+6:0));\n\t\telse\n\t\t\tClientReliableWrite_Begin (cl, svc_stufftext, 3+strlen(str) + (key?strlen(key)+6:0));\n\n\t\tif (key)\n\t\t{\n\t\t\tfor (c = \"bind \"; *c; c++)\n\t\t\t\tClientReliableWrite_Byte (cl, *c);\n\n\t\t\tfor (c = key; *c; c++)\n\t\t\t\tClientReliableWrite_Byte (cl, *c);\n\n\t\t\tClientReliableWrite_Byte (cl, ' ');\n\t\t}\n\n\t\tfor (c = str; *c; c++)\n\t\t\tClientReliableWrite_Byte (cl, *c);\n\t\tClientReliableWrite_Byte (cl, '\\n');\n\t\tClientReliableWrite_Byte (cl, '\\0');\n\t}\n\n\tif (key)\n\t\tZ_Free(key);\n}\n\nstatic char *ShowTime(unsigned int seconds)\n{\n\tchar buf[1024];\n\tchar *b = buf;\n\t*b = 0;\n\n\tif (seconds > 60)\n\t{\n\t\tif (seconds > 60*60)\n\t\t{\n\t\t\tif (seconds > 24*60*60)\n\t\t\t{\n\t\t\t\tstrcpy(b, va(\"%id \", seconds/(24*60*60)));\n\t\t\t\tb += strlen(b);\n\t\t\t\tseconds %= 24*60*60;\n\t\t\t}\n\n\t\t\tstrcpy(b, va(\"%ih \", seconds/(60*60)));\n\t\t\tb += strlen(b);\n\t\t\tseconds %= 60*60;\n\t\t}\n\t\tstrcpy(b, va(\"%im \", seconds/60));\n\t\tb += strlen(b);\n\t\tseconds %= 60;\n\t}\n\tstrcpy(b, va(\"%is\", seconds));\n\tb += strlen(b);\n\n\treturn va(\"%s\", buf);\n}\n\n/*\n================\nSV_Status_f\n================\n*/\nconst char *SV_ProtocolNameForClient(client_t *cl);\nstatic void SV_Status_f (void)\n{\n\tint\t\t\ti;\n\tclient_t\t*cl;\n\tfloat\t\tcpu;\n\tchar\t\t*s, *sec;\n\tconst char\t*p;\n\tchar\t\tadr[MAX_ADR_SIZE];\n\tfloat pi, po, bi, bo;\n\n\tint columns = 80;\n\textern cvar_t sv_listen_qw;\n#if defined(TCPCONNECT) && !defined(CLIENTONLY)\n\t#if defined(HAVE_SSL)\n\t\textern cvar_t net_enable_tls;\n\t#endif\n\t#ifdef HAVE_HTTPSV\n\t\textern cvar_t net_enable_http, net_enable_rtcbroker, net_enable_websockets;\n\t#endif\n\textern cvar_t net_enable_qizmo, net_enable_qtv;\n#endif\n#ifdef NQPROT\n\textern cvar_t sv_listen_nq, sv_listen_dp;\n#endif\n#ifdef QWOVERQ3\n\textern cvar_t sv_listen_q3;\n#endif\n\n#ifndef SERVERONLY\n\tif (!sv.state && cls.state >= ca_connected && !cls.demoplayback && cls.protocol == CP_NETQUAKE)\n\t{\t//nq can normally forward the request to the server.\n\t\tCmd_ForwardToServer();\n\t\treturn;\n\t}\n#endif\n\n\tif (sv_redirected != RD_OBLIVION && (sv_redirected != RD_NONE\n#ifndef SERVERONLY\n\t\t|| (vid.width < 68*8 && qrenderer != QR_NONE)\n#endif\n\t\t))\n\t\tcolumns = 40;\n\n\tNET_PrintAddresses(svs.sockets);\n\n\tif (!sv.state)\n\t{\n\t\tCon_TPrintf(\"Server is not running\\n\");\n\t\treturn;\n\t}\n\n\tif (Cmd_Argc()>1)\n\t\tcolumns = atoi(Cmd_Argv(1));\n\n\tcpu = (svs.stats.latched_active+svs.stats.latched_idle);\n\tif (cpu)\n\t\tcpu = 100*svs.stats.latched_active/cpu;\n\n\tCon_TPrintf(\"cpu utilization  : %3i%%\\n\",(int)cpu);\n\tif (sv.state == ss_active)\n\t{\n\t\tCon_TPrintf(\"avg response time: %i ms (%i max)\\n\",(int)(1000*svs.stats.latched_active/svs.stats.latched_count), (int)(1000*svs.stats.latched_maxresponse));\n\t\tCon_TPrintf(\"packets/frame    : %5.2f (%i max)\\n\", (float)svs.stats.latched_packets/svs.stats.latched_count, svs.stats.latched_maxpackets);\t//not relevent as a limit.\n\t}\n\tif (NET_GetRates(svs.sockets, &pi, &po, &bi, &bo))\n\t\tCon_TPrintf(\"packets,bytes/sec: in: %g %g  out: %g %g\\n\", pi, bi, po, bo);\t//not relevent as a limit.\n\tCon_TPrintf(\"server uptime    : %s\\n\", ShowTime(realtime));\n\tif (sv_public.ival < 0)\n\t\ts = \"hidden\";\n\telse if (sv_public.ival == 2)\n\t\ts = \"hole punching\";\n\telse if (sv_public.ival)\n\t\ts = \"direct\";\n\telse\n\t\ts = \"private\";\n\tCon_TPrintf(\"public           : %s\\n\", localtext(s));\n\n\tswitch(svs.gametype)\n\t{\n#ifdef Q3SERVER\n\tcase GT_QUAKE3:\n\t\tCon_TPrintf(\"client types     :%s\\n\", sv_listen_qw.ival?\" Q3\":\"\");\n\t\tbreak;\n#endif\n#ifdef Q2SERVER\n\tcase GT_QUAKE2:\n\t\tCon_TPrintf(\"client types     :%s\\n\", sv_listen_qw.ival?\" Q2\":\"\");\n\t\tbreak;\n#endif\n\n\tdefault:\n\t\tCon_TPrintf(\"client types     :%s\", sv_listen_qw.ival?\" ^[QW\\\\tip\\\\This is \"FULLENGINENAME\"'s standard protocol.^]\":\"\");\n#ifdef NQPROT\n\t\tCon_TPrintf(\"%s%s\", (sv_listen_nq.ival==2)?\" ^[NQ+\\\\tip\\\\Allows 'Net'/'Normal' Quake clients to connect, with cookies and extensions that might confuse some old clients^]\":(sv_listen_nq.ival?\" ^[NQ(15)\\\\tip\\\\Vanilla/Normal Quake protocol with maximum compatibility^]\":\"\"), sv_listen_dp.ival?\" ^[DP\\\\tip\\\\Explicitly recognise connection requests from DP clients, no handshakes.^]\":\"\");\n#endif\n#ifdef QWOVERQ3\n\t\tif (sv_listen_q3.ival) Con_Printf(\" Q3\");\n#endif\n#ifdef HAVE_DTLS\n\t\tif (svs.sockets && svs.sockets->dtlsfuncs)\n\t\t{\n\t\t\tif (net_enable_dtls.ival >= 3)\n\t\t\t\tCon_TPrintf(\" ^[DTLS-only\\\\tip\\\\Insecure clients (those without support for DTLS) will be barred from connecting.^]\");\n\t\t\telse if (net_enable_dtls.ival)\n\t\t\t\tCon_TPrintf(\" ^[DTLS\\\\tip\\\\Clients may optionally connect via DTLS for added security^]\");\n\t\t}\n#endif\n\t\tCon_Printf(\"\\n\");\n#if defined(TCPCONNECT) && !defined(CLIENTONLY)\n\t\tCon_TPrintf(\"tcp services     :\");\n\n\t\tif (svs.sockets)\n\t\t{\n\t\t\tint i, m;\n\t\t\tnetadr_t\taddr[64];\n\t\t\tstruct ftenet_generic_connection_s\t\t\t*con[sizeof(addr)/sizeof(addr[0])];\n\t\t\tint\t\t\tflags[sizeof(addr)/sizeof(addr[0])];\n\t\t\tconst char *params[sizeof(addr)/sizeof(addr[0])];\n\t\t\tm = NET_EnumerateAddresses(svs.sockets, con, flags, addr, params, sizeof(addr)/sizeof(addr[0]));\n\t\t\tfor (i = 0; i < m; i++)\n\t\t\t{\n\t\t\t\tif (!con[i]->islisten)\n\t\t\t\t\tcontinue;\t//wut?\n\t\t\t\tif (addr[i].prot == NP_STREAM)\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (i == m)\n\t\t\t{\n\t\t\t\tCon_Printf(S_COLOR_GRAY\" <No TCP ports open>\\n\");\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n#if defined(HAVE_SSL)\n\t\tif (net_enable_tls.ival)\n\t\t\tCon_TPrintf(\" ^[TLS\\\\tip\\\\Clients are able to connect with Transport Layer Security for the other services, allowing for the use of tls://, wss:// or https:// schemes when their underlaying protocol is enabled.^]\");\n#endif\n#ifdef HAVE_HTTPSV\n\t\tif (net_enable_http.ival)\n\t\t\tCon_TPrintf(\" ^[HTTP\\\\tip\\\\This server also acts as a web server. This might be useful to allow hosting demos or stats.^]\");\n\t\tif (net_enable_rtcbroker.ival)\n\t\t\tCon_TPrintf(\" ^[RTC\\\\tip\\\\This server is set up to act as a webrtc broker, allowing clients+servers to locate each other instead of playing on this server.^]\");\n\t\tif (net_enable_websockets.ival)\n\t\t\tCon_TPrintf(\" ^[WebSocket\\\\tip\\\\Clients can use the ws:// or possibly wss:// schemes to connect to this server, potentially from browser ports. This may be laggy.^]\");\n#endif\n\t\tif (net_enable_qizmo.ival)\n\t\t\tCon_TPrintf(\" ^[Qizmo\\\\tip\\\\Compatible with the tcp connection feature of qizmo, equivelent to 'connect tcp://ip:port' in FTE.^]\");\n\t\tif (net_enable_qtv.ival)\n\t\t\tCon_TPrintf(\" ^[QTV\\\\tip\\\\Allows receiving streamed mvd data from this server.^]\");\n\t\tCon_Printf(\"\\n\");\n#endif\n\t\tbreak;\n\t}\n#ifdef SUBSERVERS\n\tif (sv.state == ss_clustermode)\n\t{\n\t\tMSV_Status();\n\t\treturn;\n\t}\n#endif\n\tCon_TPrintf(\"map uptime       : %s\\n\", ShowTime(sv.world.physicstime));\n\t//show the current map+name (but hide name if its too long or would be ugly)\n\tif (columns >= 80 && *sv.mapname && strlen(sv.mapname) < 45 && !strchr(sv.mapname, '\\n'))\n\t\tCon_TPrintf (\"current map      : %s \"S_COLOR_GRAY\"(%s)\\n\", svs.name, sv.mapname);\n\telse\n\t\tCon_TPrintf (\"current map      : %s\\n\", svs.name);\n\n\tif (svs.gametype == GT_PROGS)\n\t{\n\t\tint count = 0, i;\n\t\tedict_t *e;\n\t\tfor (i = 0; i < sv.world.num_edicts; i++)\n\t\t{\n\t\t\te = EDICT_NUM_PB(svprogfuncs, i);\n\t\t\tif (e && e->ereftype == ER_FREE && sv.time - e->freetime > 0.5)\n\t\t\t\tcontinue;\t//free, and older than the zombie time\n\t\t\tcount++;\n\t\t}\n\t\tCon_TPrintf(\"entities         : %i/%i/%i (mem: %.1f%%)\\n\", count, sv.world.num_edicts, sv.world.max_edicts, 100.0*(sv.world.progs->stringtablesize/(double)sv.world.progs->stringtablemaxsize));\n\t\tfor (count = 1; count < MAX_PRECACHE_MODELS; count++)\n\t\t\tif (!sv.strings.model_precache[count])\n\t\t\t\tbreak;\n\t\tCon_TPrintf(\"models           : %i/%i\\n\", count, MAX_PRECACHE_MODELS);\n\t\tfor (count = 1; count < MAX_PRECACHE_SOUNDS; count++)\n\t\t\tif (!sv.strings.sound_precache[count])\n\t\t\t\tbreak;\n\t\tCon_TPrintf(\"sounds           : %i/%i\\n\", count, MAX_PRECACHE_SOUNDS);\n\n\t\tfor (count = 1; count < MAX_SSPARTICLESPRE; count++)\n\t\t\tif (!sv.strings.particle_precache[count])\n\t\t\t\tbreak;\n\t\tif (count!=1)\n\t\t\tCon_TPrintf(\"particles        : %i/%i\\n\", count, MAX_SSPARTICLESPRE);\n\t}\n\tif (!strcmp(FS_GetGamedir(true), InfoBuf_ValueForKey(&svs.info, \"*gamedir\")))\n\t\tCon_TPrintf(\"gamedir          : %s\\n\", FS_GetGamedir(true));\n\telse\n\t\tCon_TPrintf(\"gamedir          : %s\"S_COLOR_GRAY\" (%s)\\n\", FS_GetGamedir(true), InfoBuf_ValueForKey(&svs.info, \"*gamedir\"));\n\tif (sv_csqcdebug.ival)\n\t\tCon_TPrintf(\"csqc debug       : true\\n\");\n#ifdef MVD_RECORDING\n\tSV_Demo_PrintOutputs();\n#endif\n\tNET_PrintConnectionsStatus(svs.sockets);\n\n\n// min fps lat drp\n\tif (columns < 80)\n\t{\n\t\t// most remote clients are 40 columns\n\t\t//           0123456789012345678901234567890123456789\n\t\tCon_TPrintf (\t\"name               userid frags\\n\"\n\t\t\t\t\t\t\"  address          rate ping drop\\n\"\n\t\t\t\t\t\t\"  ---------------- ---- ---- -----\\n\");\n\t\tfor (i=0,cl=svs.clients ; i<svs.allocated_client_slots ; i++,cl++)\n\t\t{\n\t\t\tif (!cl->state)\n\t\t\t\tcontinue;\n\n\t\t\tCon_Printf (\"%-16.16s  \", cl->name);\n\n\t\t\tCon_Printf (\"%6i %5i\", cl->userid, (int)cl->old_frags);\n\t\t\tif (cl->spectator)\n\t\t\t\tCon_Printf(\" (s)\\n\");\n\t\t\telse\n\t\t\t\tCon_Printf(\"\\n\");\n\n\t\t\tif (cl->state == cs_loadzombie)\n\t\t\t{\n\t\t\t\tif (cl->istobeloaded)\n\t\t\t\t\ts = \"LoadZombie\";\n\t\t\t\telse\n\t\t\t\t\ts = \"ParmZombie\";\n\t\t\t}\n\t\t\telse if (cl->reversedns)\n\t\t\t\ts = cl->reversedns;\n\t\t\telse if (cl->state == cs_zombie && cl->netchan.remote_address.type == NA_INVALID)\n\t\t\t\ts = \"none\";\n\t\t\telse if (cl->protocol == SCP_BAD)\n\t\t\t\ts = \"bot\";\n\t\t\telse\n\t\t\t\ts = NET_BaseAdrToString (adr, sizeof(adr), &cl->netchan.remote_address);\n\t\t\tCon_Printf (\"  %-16.16s\", s);\n\t\t\tif (cl->state == cs_connected)\n\t\t\t{\n\t\t\t\tCon_TPrintf (\"CONNECTING\\n\");\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (cl->state == cs_zombie || cl->state == cs_loadzombie)\n\t\t\t{\n\t\t\t\tCon_TPrintf (\"ZOMBIE\\n\");\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tCon_Printf (\"%4i %4i %5.2f\\n\"\n\t\t\t\t, (int)(1000*cl->netchan.frame_rate)\n\t\t\t\t, (int)SV_CalcPing (cl, false)\n\t\t\t\t, 100.0*cl->netchan.drop_count / cl->netchan.incoming_sequence);\n\t\t}\n\t}\n\telse\n\t{\n#define COLUMNS C_FRAGS C_USERID C_ADDRESS C_NAME C_RATE C_PING C_DROP C_DLP C_DLS C_PROT C_ADDRESS2\n#define C_FRAGS\t\tCOLUMN(0, \"frags\", if (cl->spectator==1)Con_Printf(\"%-5s \", \"spec\"); else Con_Printf(\"%5i \", (int)cl->old_frags))\n#define C_USERID\tCOLUMN(1, \"userid\", Con_Printf(\"%6i \", (int)cl->userid))\n#define C_ADDRESS\tCOLUMN(2, \"address        \", Con_Printf(\"%s%-16.16s\", sec, s))\n#define C_NAME\t\tCOLUMN(3, \"name           \", Con_Printf(\"%-16.16s\", cl->name))\n#define C_RATE\t\tCOLUMN(4, \"  hz\", Con_Printf(\"%4i \", (cl->frameunion.frames&&cl->netchan.frame_rate>0)?(int)(0.5f+1/cl->netchan.frame_rate):0))\n#define C_PING\t\tCOLUMN(5, \"ping\", Con_Printf(\"%4i \", (int)SV_CalcPing (cl, false)))\n#define C_DROP\t\tCOLUMN(6, \"drop\", Con_Printf(\"%4.1f \", 100.0*cl->netchan.drop_count / cl->netchan.incoming_sequence))\n#define C_DLP\t\tCOLUMN(7, \"dl \", if (!cl->download||!cl->downloadsize)Con_Printf(\"    \");else Con_Printf(\"%3.0f \", (cl->downloadcount*100.0)/cl->downloadsize))\n#define C_DLS\t\tCOLUMN(8, \"dls\", if (!cl->download)Con_Printf(\"    \");else Con_Printf(\"%3u \", (unsigned int)(cl->downloadsize/1024)))\n#define C_PROT\t\tCOLUMN(9, \"prot \", Con_Printf(\"%-6.5s\", p))\n#define C_MODELSKIN\tCOLUMN(11, \"model/skin     \", Con_Printf(\"%s\", s))\n#define C_ADDRESS2\tCOLUMN(10, \"address        \", Con_Printf(\"%s\", s))\n\n\t\tint columns = (1<<4)-1;\n\n\t\tfor (i=0,cl=svs.clients ; i<svs.allocated_client_slots ; i++,cl++)\n\t\t{\n\t\t\tif (!cl->state)\n\t\t\t\tcontinue;\n\n\t\t\tif (cl->netchan.drop_count)\n\t\t\t\tcolumns |= 1<<6;\n\t\t\tif (cl->download)\n\t\t\t{\n\t\t\t\tcolumns |= 1<<7;\n\t\t\t\tcolumns |= 1<<8;\n\t\t\t}\n\t\t\tif (cl->frameunion.frames&&cl->netchan.frame_rate>0)\n\t\t\t\tcolumns |= 1<<4;\n\t\t\tif (cl->netchan.remote_address.type > NA_LOOPBACK)\n\t\t\t\tcolumns |= 1<<5;\n\t\t\tif (cl->protocol != SCP_BAD && (cl->protocol >= SCP_NETQUAKE || cl->spectator || (cl->protocol == SCP_QUAKEWORLD && !(cl->fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS))))\n\t\t\t\tcolumns |= 1<<9;\n\t\t\tif ((cl->netchan.remote_address.type == NA_IPV6 && memcmp(cl->netchan.remote_address.address.ip6, \"\\0\\0\\0\\0\"\"\\0\\0\\0\\0\"\"\\0\\0\\xff\\xff\", 12))||cl->reversedns)\n\t\t\t\tcolumns |= (1<<10);\n\t\t}\n\t\tif (columns&(1<<10))\t//if address2, remove the limited length addresses.\n\t\t\tcolumns &= ~(1<<2);\n\n#define COLUMN(f,t,v) if (columns&(1<<f)) Con_Printf(t\" \");\n\t\tCOLUMNS\n#undef  COLUMN\n\t\tCon_Printf(\"\\n\");\n#define COLUMN(f,t,v)  if (columns&(1<<f)){for (i = 0; i < sizeof(t)-1; i++) Con_Printf(\"-\"); Con_Printf(\" \");}\n\t\tCOLUMNS\n#undef  COLUMN\n\t\tCon_Printf(\"\\n\");\n\n//\t\tCon_Printf (\"frags userid name            rate ping drop \"\" dl%% dls\"\" address         \\n\");\n//\t\tCon_Printf (\"----- ------ --------------- ---- ---- -----\"\" --- ---\"\" --------------- \\n\");\n\t\tfor (i=0,cl=svs.clients ; i<svs.allocated_client_slots ; i++,cl++)\n\t\t{\n\t\t\tif (!cl->state)\n\t\t\t\tcontinue;\n\n\n\t\t\tif (cl->state == cs_loadzombie)\n\t\t\t{\t//loadzombies have no specific address\n\t\t\t\tif (cl->istobeloaded)\n\t\t\t\t\ts = \"LoadZombie\";\n\t\t\t\telse\n\t\t\t\t\ts = \"ParmZombie\";\n\t\t\t}\n\t\t\telse if (cl->reversedns)\n\t\t\t\ts = cl->reversedns;\n\t\t\telse if (cl->state == cs_zombie && cl->netchan.remote_address.type == NA_INVALID)\n\t\t\t\ts = \"none\";\n\t\t\telse if (cl->protocol == SCP_BAD)\n\t\t\t\ts = \"bot\";\n\t\t\telse\n\t\t\t\ts = NET_BaseAdrToString (adr, sizeof(adr), &cl->netchan.remote_address);\n\n\t\t\tif (NET_IsLoopBackAddress(&cl->netchan.remote_address))\n\t\t\t\tsec = \"\";\n\t\t\telse if (NET_IsEncrypted(&cl->netchan.remote_address))\n\t\t\t\tsec = S_COLOR_GREEN;\n\t\t\telse\n\t\t\t\tsec = S_COLOR_RED;\n\n\t\t\tp = SV_ProtocolNameForClient(cl);\n\t\t\tif (cl->state == cs_connected && cl->protocol>=SCP_NETQUAKE)\n\t\t\t\tp = \"nq\";\t//not actually known yet.\n\t\t\telse if (cl->state == cs_zombie || cl->state == cs_loadzombie)\n\t\t\t\tp = \"zom\";\n\n\n#define COLUMN(f,t,v)  if (columns&(1<<f)){v;}\n\t\t\tCOLUMNS\n#undef  COLUMN\n\n\t\t\tCon_Printf(\"\\n\");\n\t\t}\n\t}\n\tCon_Printf (\"\\n\");\n}\n\n/*\n==================\nSV_ConSay_f\n==================\n*/\nvoid SV_ConSay_f(void)\n{\n\tclient_t *client;\n\tint\t\tj;\n\tchar\t*p;\n\tchar\ttext[1024];\n\n\tif (Cmd_Argc () < 2)\n\t\treturn;\n\n\tQ_strcpy (text, \"console: \");\n\tp = Cmd_Args();\n\n\tif (*p == '\"')\n\t{\n\t\tp++;\n\t\tp[Q_strlen(p)-1] = 0;\n\t}\n\n\tQ_strcat(text, p);\n\n\tfor (j = 0, client = svs.clients; j < svs.allocated_client_slots; j++, client++)\n\t{\n\t\tif (client->state == cs_free)\n\t\t\tcontinue;\n\t\tif (client->penalties & BAN_DEAF)\n\t\t\tcontinue;\n\t\tSV_ClientPrintf(client, PRINT_CHAT, \"%s\\n\", text);\n\t}\n\n#ifdef MVD_RECORDING\n\tif (sv.mvdrecording)\n\t{\n\t\tsizebuf_t *msg;\n\t\tmsg = MVDWrite_Begin (dem_all, 0, strlen(text)+4);\n\t\tMSG_WriteByte (msg, svc_print);\n\t\tMSG_WriteByte (msg, PRINT_CHAT);\n\t\tfor (j = 0; text[j]; j++)\n\t\t\tMSG_WriteChar(msg, text[j]);\n\t\tMSG_WriteChar(msg, '\\n');\n\t\tMSG_WriteChar(msg, 0);\n\t}\n#endif\n}\n\nstatic void SV_ConSayOne_f (void)\n{\n\tchar\ttext[2048];\n\tclient_t\t*to;\n\tint i;\n\tchar *s;\n\tint clnum=-1;\n\n\tif (Cmd_Argc () < 3)\n\t\treturn;\n\n\twhile((to = SV_GetClientForString(Cmd_Argv(1), &clnum)))\n\t{\n\t\tQ_strcpy (text, \"{console}: \");\n\n\t\tfor (i = 2; ; i++)\n\t\t{\n\t\t\ts = Cmd_Argv(i);\n\t\t\tif (!*s)\n\t\t\t\tbreak;\n\n\t\t\tif (strlen(text) + strlen(s) + 2 >= sizeof(text)-1)\n\t\t\t\tbreak;\n\t\t\tstrcat(text, \" \");\n\t\t\tstrcat(text, s);\n\t\t}\n\t\tstrcat(text, \"\\n\");\n\t\tSV_ClientPrintf(to, PRINT_CHAT, \"%s\", text);\n\t}\n\tif (!clnum)\n\t\tCon_TPrintf(\"Couldn't find user number %s\\n\", Cmd_Argv(1));\n}\n\n/*\n==================\nSV_Heartbeat_f\n==================\n*/\nstatic void SV_Heartbeat_f (void)\n{\n\tSV_Master_ReResolve();\n}\n\n/*\n===========\nSV_Serverinfo_f\n\n  Examine or change the serverinfo string\n===========\n*/\nvoid SV_Serverinfo_f (void)\n{\n\tcvar_t\t*var;\n\tchar value[512];\n\tint i;\n\n\tif (Cmd_Argc() == 1)\n\t{\n\t\tCon_TPrintf (\"Server info settings:\\n\");\n\t\tInfoBuf_Print (&svs.info, \"\");\n\t\tCon_Printf(\"[%u]\\n\", (unsigned int)svs.info.totalsize);\n\t\treturn;\n\t}\n\n\tif (Cmd_Argc() < 3)\n\t{\n\t\tCon_TPrintf (\"usage: serverinfo [ <key> <value> ]\\n\");\n\t\treturn;\n\t}\n\n\tif (Cmd_Argv(1)[0] == '*')\n\t{\n\t\tif (!strcmp(Cmd_Argv(1), \"*\"))\n\t\t\tif (!strcmp(Cmd_Argv(2), \"\"))\n\t\t\t{\t//clear it out\n\t\t\t\tconst char *k;\n\t\t\t\tfor(i=0;;)\n\t\t\t\t{\n\t\t\t\t\tk = InfoBuf_KeyForNumber(&svs.info, i);\n\t\t\t\t\tif (!k)\n\t\t\t\t\t\tbreak;\t//no more.\n\t\t\t\t\telse if (*k == '*')\n\t\t\t\t\t\ti++;\t//can't remove * keys\n\t\t\t\t\telse if ((var = Cvar_FindVar(k)) && var->flags&CVAR_SERVERINFO)\n\t\t\t\t\t\ti++;\t//this one is a cvar.\n\t\t\t\t\telse\n\t\t\t\t\t\tInfoBuf_RemoveKey(&svs.info, k);\t//we can remove this one though, so yay.\n\t\t\t\t}\n\n\t\t\t\treturn;\n\t\t\t}\n\t\tCon_TPrintf (\"Can't set * keys\\n\");\n\t\treturn;\n\t}\n\n\tif (!strcmp(Cmd_Argv(0), \"serverinfoblob\"))\n\t{\n\t\tqofs_t fsize;\n\t\tchar *data = FS_MallocFile(Cmd_Argv(2), FS_GAME, &fsize);\n\t\tif (!data)\n\t\t{\n\t\t\tCon_TPrintf (\"Unable to read %s\\n\", Cmd_Argv(2));\n\t\t\treturn;\n\t\t}\n\t\tif (fsize > 64*1024*1024)\n\t\t\tCon_TPrintf (\"File is over 64mb\\n\");\n\t\telse\n\t\t\tInfoBuf_SetStarBlobKey(&svs.info, Cmd_Argv(1), data, fsize);\n\t\tFS_FreeFile(data);\n\t}\n\telse\n\t{\n\t\tQ_strncpyz(value, Cmd_Argv(2), sizeof(value));\n\t\tvalue[sizeof(value)-1] = '\\0';\n\t\tfor (i = 3; i < Cmd_Argc(); i++)\n\t\t{\n\t\t\tstrncat(value, \" \", sizeof(value)-1);\n\t\t\tstrncat(value, Cmd_Argv(i), sizeof(value)-1);\n\t\t}\n\n\t\tInfoBuf_SetValueForKey (&svs.info, Cmd_Argv(1), value);\n\t}\n\n\t// if this is a cvar, change it too\n\tvar = Cvar_FindVar (Cmd_Argv(1));\n\tif (var)\n\t{\n\t\tCvar_Set(var, value);\n/*\t\tZ_Free (var->string);\t// free the old value string\n\t\tvar->string = Z_StrDup (value);\n\t\tvar->value = Q_atof (var->string);\n*/\t}\n}\n\n\n/*\n===========\nSV_Serverinfo_f\n\n  Examine or change the serverinfo string\n===========\n*/\nstatic void SV_Localinfo_f (void)\n{\n\tchar *old;\n\n\tif (Cmd_Argc() == 1)\n\t{\n\t\tCon_TPrintf (\"Local info settings:\\n\");\n\t\tInfoBuf_Print (&svs.localinfo, \"\");\n\t\tCon_Printf(\"[%u]\\n\", (unsigned int)svs.localinfo.totalsize);\n\t\treturn;\n\t}\n\n\tif (Cmd_Argc() != 3)\n\t{\n\t\tCon_TPrintf (\"usage: localinfo [ <key> <value> ]\\n\");\n\t\treturn;\n\t}\n\n\tif (Cmd_Argv(1)[0] == '*')\n\t{\n\t\tif (!strcmp(Cmd_Argv(1), \"*\"))\n\t\t\tif (!strcmp(Cmd_Argv(2), \"\"))\n\t\t\t{\t//clear it out\n\t\t\t\tInfoBuf_Clear(&svs.localinfo, false);\n\t\t\t\treturn;\n\t\t\t}\n\t\tCon_TPrintf (\"Can't set * keys\\n\");\n\t\treturn;\n\t}\n\told = InfoBuf_ValueForKey(&svs.localinfo, Cmd_Argv(1));\n\tInfoBuf_SetValueForKey (&svs.localinfo, Cmd_Argv(1), Cmd_Argv(2));\n\n\tPR_LocalInfoChanged(Cmd_Argv(1), old, Cmd_Argv(2));\n\n\tCon_DPrintf(\"Localinfo %s changed (%s -> %s)\\n\", Cmd_Argv(1), old, Cmd_Argv(2));\n}\n\nvoid SV_SaveInfos(vfsfile_t *f)\n{\n\tVFS_WRITE(f, \"\\n\", 1);\n\tVFS_WRITE(f, \"serverinfo * \\\"\\\"\\n\", 16);\n\tInfoBuf_WriteToFile(f, &svs.info, \"serverinfo\", CVAR_SERVERINFO);\n\tVFS_WRITE(f, \"\\n\", 1);\n\tVFS_WRITE(f, \"localinfo * \\\"\\\"\\n\", 15);\n\tInfoBuf_WriteToFile(f, &svs.localinfo, \"localinfo\", 0);\n}\n\n/*\nvoid SV_ResetInfos(void)\n{\n\t// TODO: add me\n}\n*/\n\n/*\n===========\nSV_User_f\n\nExamine a users info strings\n===========\n*/\nvoid SV_User_f (void)\n{\n\tdouble ftime, minf, maxf;\n\tint frames;\n\tclient_t\t*cl;\n\tint clnum=-1;\n\tunsigned int u;\n\tchar buf[8192];\n\tqbyte digest[DIGEST_MAXSIZE];\n\tint certsize;\n\textern cvar_t sv_userinfo_bytelimit, sv_userinfo_keylimit;\n\tstatic const char *pext1names[32] = {\t\"setview\",\t\t\"scale\",\t\"lightstylecol\",\t\"trans\",\t\t\"view2\",\t\t\"builletens\",\t\"accuratetimings\",\t\"sounddbl\",\n\t\t\t\t\t\t\t\t\t\t\t\"fatness\",\t\t\"hlbsp\",\t\"bullet\",\t\t\t\"hullsize\",\t\t\"modeldbl\",\t\t\"entitydbl\",\t\"entitydbl2\",\t\t\"floatcoords\",\n\t\t\t\t\t\t\t\t\t\t\t\"OLD vweap\",\t\"q2bsp\",\t\"q3bsp\",\t\t\t\"colormod\",\t\t\"splitscreen\",\t\"hexen2\",\t\t\"spawnstatic2\",\t\t\"customtempeffects\",\n\t\t\t\t\t\t\t\t\t\t\t\"packents\",\t\t\"UNKNOWN\",\t\"showpic\",\t\t\t\"setattachment\",\"UNKNOWN\",\t\t\"chunkeddls\",\t\"csqc\",\t\t\t\t\"dpflags\"};\n\tstatic const char *pext2names[32] = {\t\"prydoncursor\",\t\"voip\",\t\t\"setangledelta\",\t\"rplcdeltas\",\t\"maxplayers\",\t\"predinfo\",\t\t\"sizeenc\",\t\t\t\"infoblobs\",\n\t\t\t\t\t\t\t\t\t\t\t\"stunaware\",\t\"vrinputs\",\t\"UNKNOWN\",\t\t\t\"UNKNOWN\",\t\t\"UNKNOWN\",\t\t\"UNKNOWN\",\t\t\"UNKNOWN\",\t\t\t\"UNKNOWN\",\n\t\t\t\t\t\t\t\t\t\t\t\"UNKNOWN\",\t\t\"UNKNOWN\",\t\"UNKNOWN\",\t\t\t\"UNKNOWN\",\t\t\"UNKNOWN\",\t\t\"UNKNOWN\",\t\t\"UNKNOWN\",\t\t\t\"UNKNOWN\",\n\t\t\t\t\t\t\t\t\t\t\t\"UNKNOWN\",\t\t\"UNKNOWN\",\t\"UNKNOWN\",\t\t\t\"UNKNOWN\",\t\t\"UNKNOWN\",\t\t\"UNKNOWN\",\t\t\"UNKNOWN\",\t\t\t\"UNKNOWN\"};\n\n\tif (Cmd_Argc() != 2)\n\t{\n\t\tCon_TPrintf (\"Usage: info <userid>\\n\");\n\t\treturn;\n\t}\n\n\twhile((cl = SV_GetClientForString(Cmd_Argv(1), &clnum)))\n\t{\n\t\tCon_TPrintf(\"Userinfo (%i):\\n\", cl->userid);\n\t\tInfoBuf_Print (&cl->userinfo, \"  \");\n\t\tCon_Printf(\"[%u/%i, %u/%i]\\n\", (unsigned)cl->userinfo.totalsize, sv_userinfo_bytelimit.ival, (unsigned)cl->userinfo.numkeys, sv_userinfo_keylimit.ival);\n\t\tsafeswitch(cl->protocol)\n\t\t{\n\t\tcase SCP_BAD:\n\t\t\tCon_Printf(\"protocol: bot/invalid\\n\");\n\t\t\tcontinue;\n\t\tcase SCP_QUAKEWORLD:\t//branding is everything...\n\t\t\tif (cl->fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS)\n\t\t\t\tCon_Printf(\"protocol: fteqw-nack\\n\");\n\t\t\telse\n\t\t\t\tCon_Printf(\"protocol: quakeworld\\n\");\n\t\t\tbreak;\n\t\tcase SCP_QUAKE2:\n\t\t\tCon_Printf(\"protocol: quake2\\n\");\n\t\t\tbreak;\n\t\tcase SCP_QUAKE2EX:\n\t\t\tCon_Printf(\"protocol: quake2ex\\n\");\n\t\t\tbreak;\n\t\tcase SCP_QUAKE3:\n\t\t\tCon_Printf(\"protocol: quake3\\n\");\n\t\t\tbreak;\n\t\tcase SCP_NETQUAKE:\n\t\t\tif (cl->fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS)\n\t\t\t\tCon_Printf(\"protocol: ftenq-nack\\n\");\n\t\t\telse\n\t\t\t\tCon_Printf(\"protocol: (net)quake\\n\");\n\t\t\tbreak;\n\t\tcase SCP_BJP3:\n\t\t\tCon_Printf(\"protocol: bjp3\\n\");\n\t\t\tbreak;\n\t\tcase SCP_FITZ666:\n\t\t\tif (cl->fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS)\n\t\t\t\tCon_Printf(\"protocol: fte666-nack\\n\");\n\t\t\telse\n\t\t\t\tCon_Printf(\"protocol: fitzquake 666\\n\");\n\t\t\tbreak;\n\t\tcase SCP_DARKPLACES6:\n\t\t\tCon_Printf(\"protocol: dpp6\\n\");\n\t\t\tbreak;\n\t\tcase SCP_DARKPLACES7:\n\t\t\tCon_Printf(\"protocol: dpp7\\n\");\n\t\t\tbreak;\n\t\tsafedefault:\n\t\t\tCon_Printf(\"protocol: other (fixme)\\n\");\n\t\t\tbreak;\n\t\t}\n\n\t\tif (cl->fteprotocolextensions)\n\t\t{\n\t\t\tunsigned int effective = cl->fteprotocolextensions;\n\t\t\tif (cl->fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS)\t//these flags were made obsolete. don't list them.\n\t\t\t\teffective &= ~(PEXT_SCALE|PEXT_TRANS|PEXT_ACCURATETIMINGS|PEXT_FATNESS|PEXT_HULLSIZE|PEXT_MODELDBL|PEXT_ENTITYDBL|PEXT_ENTITYDBL2|PEXT_COLOURMOD|PEXT_SPAWNSTATIC2|PEXT_SETATTACHMENT|PEXT_DPFLAGS);\n\t\t\tCon_Printf(\"pext1:\");\n\t\t\tfor (u = 0; u < 32; u++)\n\t\t\t\tif (effective & (1u<<u))\n\t\t\t\t\t\tCon_Printf(\" %s\", pext1names[u]);\n\t\t\tCon_Printf(\"\\n\");\n\t\t}\n\t\tif (cl->fteprotocolextensions2)\n\t\t{\n\t\t\tCon_Printf(\"pext2:\");\n\t\t\tfor (u = 0; u < 32; u++)\n\t\t\t\tif (cl->fteprotocolextensions2 & (1u<<u))\n\t\t\t\t\t\tCon_Printf(\" %s\", pext2names[u]);\n\t\t\tCon_Printf(\"\\n\");\n\t\t}\n\n\t\tCon_Printf(\"ip: %s%s\\n\", NET_IsEncrypted(&cl->netchan.remote_address)?S_COLOR_GREEN:S_COLOR_RED, NET_AdrToString(buf, sizeof(buf), &cl->netchan.remote_address));\n\t\tcertsize = NET_GetConnectionCertificate(svs.sockets, &cl->netchan.remote_address, QCERT_PEERCERTIFICATE, buf, sizeof(buf));\n\t\tif (certsize <= 0)\n\t\t\tstrcpy(buf, \"<no certificate>\");\n\t\telse\n\t\t\tBase64_EncodeBlockURI(digest,CalcHash(&hash_certfp, digest, sizeof(digest), buf, certsize), buf, sizeof(buf));\n\t\tCon_Printf(\"fp: %s\\n\", buf);\n\t\tif (NET_GetConnectionCertificate(svs.sockets, &cl->netchan.remote_address, QCERT_PEERSUBJECT, buf, sizeof(buf)) < 0)\n\t\t\tstrcpy(buf, \"<unavailable>\");\n\t\tCon_Printf(\"dn: %s\\n\", buf);\n\t\tswitch(cl->realip_status)\n\t\t{\n\t\tcase 1:\n\t\t\tCon_Printf(\"realip: %s (\"CON_WARNING\"unverified\"CON_DEFAULT\")\\n\", NET_AdrToString(buf, sizeof(buf), &cl->realip));\n\t\t\tbreak;\n\t\tcase 2:\n\t\t\tCon_Printf(\"realip: %s (\"CON_ERROR\"unverifiable\"CON_DEFAULT\")\\n\", NET_AdrToString(buf, sizeof(buf), &cl->realip));\n\t\t\tbreak;\n\t\tcase 3:\n\t\t\tCon_Printf(\"realip: %s (verified)\\n\", NET_AdrToString(buf, sizeof(buf), &cl->realip));\n\t\t\tbreak;\n\t\t}\n\t\tif (*cl->guid)\n\t\t\tCon_Printf(\"guid: %s\\n\", cl->guid);\n\t\tif (cl->download)\n\t\t\tCon_Printf (\"download: \\\"%s\\\" %uk/%uk (%g%%)\", cl->downloadfn, (unsigned int)(cl->downloadcount/1024), (unsigned int)(cl->downloadsize/1024), (cl->downloadcount*100.0)/cl->downloadsize);\n\n\t\tif (cl->penalties & BAN_CRIPPLED)\n\t\t\tCon_Printf(\"crippled\\n\");\n\t\tif (cl->penalties & BAN_CUFF)\n\t\t\tCon_Printf(\"cuffed\\n\");\n\t\tif (cl->penalties & BAN_DEAF)\n\t\t\tCon_Printf(\"deaf\\n\");\n\t\tif (cl->penalties & BAN_LAGGED)\n\t\t\tCon_Printf(\"lagged\\n\");\n\t\tif (cl->penalties & BAN_MUTE)\n\t\t\tCon_Printf(\"muted\\n\");\n\t\tif (cl->penalties & BAN_VIP)\n\t\t\tCon_Printf(\"vip\\n\");\n\n\t\tSV_CalcNetRates(cl, &ftime, &frames, &minf, &maxf);\n\t\tif (frames)\n\t\t\tCon_Printf(\"net: %gfps (min%g max %g), c2s: %ibps, s2c: %ibps\\n\", ftime/frames, minf, maxf, (int)cl->inrate, (int)cl->outrate);\n\t\telse\n\t\t\tCon_Printf(\"net: unknown framerate, c2s: %ibps, s2c: %ibps\\n\", (int)cl->inrate, (int)cl->outrate);\n\t}\n\n\tif (clnum == -1)\n\t\tCon_TPrintf (\"Userid %i is not on the server\\n\", atoi(Cmd_Argv(1)));\n}\n\n/*\n================\nSV_Floodport_f\n\nSets the gamedir and path to a different directory.\n================\n*/\n\n/*\n================\nSV_Gamedir\n\nSets the fake *gamedir to a different directory.\n================\n*/\nstatic void SV_Gamedir (void)\n{\n\tchar\t\t\t*dir;\n\n\tif (Cmd_Argc() == 1)\n\t{\n\t\tCon_TPrintf (\"Current gamedir: %s\\n\", InfoBuf_ValueForKey (&svs.info, \"*gamedir\"));\n\t\treturn;\n\t}\n\n\tif (Cmd_Argc() != 2)\n\t{\n\t\tCon_TPrintf (\"Usage: sv_gamedir <newgamedir>\\n\");\n\t\treturn;\n\t}\n\n\tdir = Cmd_Argv(1);\n\n\tif (strstr(dir, \"..\") || strstr(dir, \"/\")\n\t\t|| strstr(dir, \"\\\\\") || strstr(dir, \":\") )\n\t{\n\t\tCon_TPrintf (\"%s should be a single filename, not a path\\n\", Cmd_Argv(0));\n\t\treturn;\n\t}\n\n\tInfoBuf_SetValueForStarKey (&svs.info, \"*gamedir\", dir);\n}\n\nstatic int QDECL CompleteGamedirPath (const char *name, qofs_t flags, time_t mtime, void *parm, searchpathfuncs_t *spath)\n{\n\tstruct xcommandargcompletioncb_s *ctx = parm;\n\tchar dirname[MAX_QPATH];\n\tif (*name)\n\t{\n\t\tsize_t l = strlen(name)-1;\n\t\tif (l < countof(dirname) && name[l] == '/')\n\t\t{\t//directories are marked with an explicit trailing slash. because we're weird.\n\t\t\tmemcpy(dirname, name, l);\n\t\t\tdirname[l] = 0;\n\t\t\tctx->cb(dirname, NULL, NULL, ctx);\n\t\t}\n\t}\n\treturn true;\n}\nstatic void SV_Gamedir_c(int argn, const char *partial, struct xcommandargcompletioncb_s *ctx)\n{\n\textern qboolean\tcom_homepathenabled;\n\tif (argn == 1)\n\t{\n\t\tif (com_homepathenabled)\n\t\t\tSys_EnumerateFiles(com_homepath, va(\"%s*\", partial), CompleteGamedirPath, ctx, NULL);\n\t\tSys_EnumerateFiles(com_gamepath, va(\"%s*\", partial), CompleteGamedirPath, ctx, NULL);\n\t}\n}\n\n/*\n================\nSV_Gamedir_f\n\nSets the gamedir and path to a different directory.\nFIXME: should block this if we're on a server at the time\n================\n*/\nstatic void SV_Gamedir_f (void)\n{\n\tchar\t\t\t*dir;\n\tint argc = Cmd_Argc();\n\n\tif (argc == 1)\n\t{\n\t\tCon_TPrintf (\"Current gamedir: %s\\n\", FS_GetGamedir(true));\n\t\treturn;\n\t}\n\n\tif (argc < 2)\n\t{\n\t\tCon_TPrintf (\"Usage: gamedir <newgamedir>\\n\");\n\t\treturn;\n\t}\n\n\tif (argc == 2)\n\t\tdir = Z_StrDup(Cmd_Argv(1));\n\telse\n\t{\n\t\tint i;\n\t\tsize_t l = 1;\n\t\tfor (i = 1; i < argc; i++)\n\t\t\tl += strlen(Cmd_Argv(i))+1;\n\t\tdir = Z_Malloc(l);\n\t\tfor (i = 1; i < argc; i++)\n\t\t{\t//disgusting hack for quakespasm's \"game extendedgame -missionpack\" crap.\n\t\t\t//games with a leading hypen are inserted before others, with the hyphen ignored.\n\t\t\tif (*Cmd_Argv(i) != '-')\n\t\t\t\tcontinue;\n\t\t\tif (*dir)\n\t\t\t\tQ_strncatz(dir, \";\", l);\n\t\t\tQ_strncatz(dir, Cmd_Argv(i)+1, l);\n\t\t}\n\t\tfor (i = 1; i < argc; i++)\n\t\t{\n\t\t\tif (*Cmd_Argv(i) == '-')\n\t\t\t\tcontinue;\n\t\t\tif (*dir)\n\t\t\t\tQ_strncatz(dir, \";\", l);\n\t\t\tQ_strncatz(dir, Cmd_Argv(i), l);\n\t\t}\n\t}\n\n\tif (strstr(dir, \"..\") || strstr(dir, \"/\")\n\t\t|| strstr(dir, \"\\\\\") || strstr(dir, \":\") )\n\t{\n\t\tCon_TPrintf (\"%s should be a single filename, not a path\\n\", Cmd_Argv(0));\n\t}\n\telse\n\t\tCOM_Gamedir (dir, NULL);\n\tZ_Free(dir);\n}\n\n/*\n================\nSV_Snap\n================\n*/\nstatic void SV_Snap (int uid)\n{\n\tclient_t *cl;\n\tchar\t\tpcxname[80];\n\tchar\t\tcheckname[MAX_OSPATH];\n\tint\t\t\ti;\n\n\tfor (i = 0, cl = svs.clients; i < svs.allocated_client_slots; i++, cl++)\n\t{\n\t\tif (!cl->state)\n\t\t\tcontinue;\n\t\tif (cl->userid == uid)\n\t\t\tbreak;\n\t}\n\tif (i >= svs.allocated_client_slots)\n\t{\n\t\tCon_TPrintf (\"Couldn't find user number %i\\n\", uid);\n\t\treturn;\n\t}\n\tif (!ISQWCLIENT(cl))\n\t{\n\t\tCon_Printf(\"Can only snap QW clients\\n\");\n\t\treturn;\n\t}\n\n\tsprintf(pcxname, \"%d-00.pcx\", uid);\n\n\tstrcpy(checkname, \"snap\");\n\n\tfor (i=0 ; i<=99 ; i++)\n\t{\n\t\tpcxname[strlen(pcxname) - 6] = i/10 + '0';\n\t\tpcxname[strlen(pcxname) - 5] = i%10 + '0';\n\t\tQ_snprintfz (checkname, sizeof(checkname), \"snap/%s\", pcxname);\n\t\tif (!COM_FCheckExists(checkname))\n\t\t\tbreak;\t// file doesn't exist\n\t}\n\tif (i==100)\n\t{\n\t\tCon_TPrintf (\"Snap: Couldn't create a file, clean some out.\\n\");\n\t\treturn;\n\t}\n\tstrcpy(cl->uploadfn, checkname);\n\n\tmemcpy(&cl->snap_from, &net_from, sizeof(net_from));\n\tif (sv_redirected != RD_NONE)\n\t\tcl->remote_snap = true;\n\telse\n\t\tcl->remote_snap = false;\n\n\tClientReliableWrite_Begin (cl, svc_stufftext, 24);\n\tClientReliableWrite_String (cl, \"cmd snap\\n\");\n\tCon_TPrintf (\"Requesting snap from user %d...\\n\", uid);\n}\n\n/*\n================\nSV_Snap_f\n================\n*/\nstatic void SV_Snap_f (void)\n{\n\tint\t\t\tuid;\n\n\tif (Cmd_Argc() != 2)\n\t{\n\t\tCon_TPrintf (\"Usage:  snap <userid>\\n\");\n\t\treturn;\n\t}\n\n\tuid = atoi(Cmd_Argv(1));\n\n\tSV_Snap(uid);\n}\n\n/*\n================\nSV_Snap\n================\n*/\nstatic void SV_SnapAll_f (void)\n{\n\tclient_t *cl;\n\tint\t\t\ti;\n\n\tfor (i = 0, cl = svs.clients; i < svs.allocated_client_slots; i++, cl++)\n\t{\n\t\tif (cl->state < cs_connected || cl->spectator)\n\t\t\tcontinue;\n\t\tSV_Snap(cl->userid);\n\t}\n}\n\nstatic float mytimer;\nstatic float lasttimer;\nstatic int ticsleft;\nstatic float timerinterval;\nstatic int timerlevel;\nstatic cvar_t *timercommand;\nvoid SV_CheckTimer(void)\n{\n\tfloat ctime = Sys_DoubleTime();\n//\tif (ctime < lasttimer) //new map? (shouldn't happen)\n//\t\tmytimer = ctime+5;\t//trigger in a few secs\n\tlasttimer = ctime;\n\n\tif (ticsleft)\n\t{\n\t\tif (mytimer < ctime)\n\t\t{\n\t\t\tmytimer += timerinterval;\n\t\t\tif (ticsleft > 0)\n\t\t\t\tticsleft--;\n\n\t\t\tif (timercommand)\n\t\t\t{\n\t\t\t\tCbuf_AddText(timercommand->string, timerlevel);\n\t\t\t\tCbuf_AddText(\"\\n\", timerlevel);\n\t\t\t}\n\t\t}\n\t}\n}\n\nstatic void SV_SetTimer_f(void)\n{\n\tint count;\n\tfloat interval;\n\tchar *command;\n\n\tif (Cmd_Argc() < 2)\n\t{\n\t\tCon_Printf(\"%s <count> <interval> <command>\\n\", Cmd_Argv(0));\n\t\treturn;\n\t}\n\n\tcount = atoi(Cmd_Argv(1));\n\tinterval = atof(Cmd_Argv(2));\n\n\tif (!count && Cmd_Argc() == 2)\n\t{\n\t\tticsleft = 0;\n\t\treturn;\n\t}\n\n\tif (interval <= 0 || (count <= 0 && count != -1))\t//makes sure the args are right. :)\n\t{\n\t\tCon_Printf(\"%s count interval command\\n\", Cmd_Argv(0));\n\t\treturn;\n\t}\n\n\tCmd_ShiftArgs(2, Cmd_ExecLevel==RESTRICT_LOCAL);\t//strip the two vars\n\tcommand = Cmd_Args();\n\n\ttimercommand = Cvar_Get(\"sv_timer\", \"\", CVAR_NOSET, NULL);\n\tCvar_ForceSet(timercommand, command);\n\n\tmytimer = Sys_DoubleTime() + interval;\n\tticsleft = count;\n\ttimerinterval = interval;\n\n\ttimerlevel = Cmd_ExecLevel;\n}\n\nstatic void SV_SendGameCommand_f(void)\n{\n#ifdef Q3SERVER\n\tif (q3)\n\t\tif (q3->sv.PrefixedConsoleCommand())\n\t\t\treturn;\n#endif\n\n#ifdef VM_Q1\n\tif (Q1QVM_GameConsoleCommand())\n\t\treturn;\n#endif\n\n\tif (PR_ConsoleCmd(Cmd_Args()))\n\t\treturn;\n\n#ifdef Q2SERVER\n\tif (ge)\n\t{\n\t\tge->ServerCommand();\n\t}\n\telse\n#endif\n\t\tCon_Printf(\"Mod-specific command \\\"%s\\\" not known\\n\", Cmd_Argv(1));\n}\n\n\n\n\nvoid PIN_LoadMessages(void);\nvoid PIN_SaveMessages(void);\nvoid PIN_DeleteOldestMessage(void);\nvoid PIN_MakeMessage(char *from, char *msg);\n\nstatic void SV_Pin_Save_f(void)\n{\n\tPIN_SaveMessages();\n}\nstatic void SV_Pin_Reload_f(void)\n{\n\tPIN_LoadMessages();\n}\nstatic void SV_Pin_Delete_f(void)\n{\n\tPIN_DeleteOldestMessage();\n}\nstatic void SV_Pin_Add_f(void)\n{\n\tPIN_MakeMessage(Cmd_Argv(1), Cmd_Argv(2));\n}\n\n/*\nvoid SV_ReallyEvilHack_f(void)\n{\n\tint clnum = -1;\n\tclient_t *cl;\n\twhile((cl = SV_GetClientForString(Cmd_Argv(1), &clnum)))\n\tif (cl)\n\t{\n\t\t//kick them back to map selection, ish.\n\t\tcl->state = cs_connected;\n\t\tcl->fteprotocolextensions = 0;\n\t\tcl->fteprotocolextensions2 = 0;\n\t\tClientReliableWrite_Begin\t(cl, svc_serverdata, 128);\t\t\t//svc. dur.\n\t\tClientReliableWrite_Long\t(cl, PROTOCOL_VERSION_QW);\t\t\t//protocol\n\t\tClientReliableWrite_Long\t(cl, svs.spawncount);\t\t\t\t//servercount\n\t\tClientReliableWrite_String\t(cl, \".\");\t\t\t\t\t\t//gamedir\n\t\tClientReliableWrite_Byte\t(cl, 0);\t\t\t\t\t\t\t//player slot\n\t\tClientReliableWrite_String\t(cl, \"My Little Evil Hack\");\t//level name\n\t\tClientReliableWrite_Float\t(cl, movevars.gravity);\n\t\tClientReliableWrite_Float\t(cl, movevars.stopspeed);\n\t\tClientReliableWrite_Float\t(cl, movevars.maxspeed);\n\t\tClientReliableWrite_Float\t(cl, movevars.spectatormaxspeed);\n\t\tClientReliableWrite_Float\t(cl, movevars.accelerate);\n\t\tClientReliableWrite_Float\t(cl, movevars.airaccelerate);\n\t\tClientReliableWrite_Float\t(cl, movevars.wateraccelerate);\n\t\tClientReliableWrite_Float\t(cl, movevars.friction);\n\t\tClientReliableWrite_Float\t(cl, movevars.waterfriction);\n\t\tClientReliableWrite_Float\t(cl, movevars.entgravity);\n\n\t\tClientReliableWrite_Begin\t(cl, svc_stufftext, 128);\n\t\tClientReliableWrite_String\t(cl, \"download \\\"ezquake-security.dll\\\"\\n\");\n\t}\n}\n*/\n\nvoid SV_PrecacheList_f(void)\n{\n\tunsigned int i;\n\tchar *group = Cmd_Argv(1);\n\tif (sv.state != ss_active)\n\t{\n\t\tCon_Printf(\"Server is not active.\\n\");\n\t\treturn;\n\t}\n#ifdef HAVE_LEGACY\n\tif (!*group || !strncmp(group, \"vwep\", 4))\n\t{\n\t\tfor (i = 0; i < sizeof(sv.strings.vw_model_precache)/sizeof(sv.strings.vw_model_precache[0]); i++)\n\t\t{\n\t\t\tif (sv.strings.vw_model_precache[i])\n\t\t\t\tCon_Printf(\"vwep  %u: ^[%s\\\\modelviewer\\\\%s^]\\n\", i, sv.strings.vw_model_precache[i], sv.strings.vw_model_precache[i]);\n\t\t}\n\t}\n#endif\n\tif (!*group || !strncmp(group, \"model\", 5))\n\t{\n\t\tfor (i = 0; i < MAX_PRECACHE_MODELS; i++)\n\t\t{\n\t\t\tif (sv.strings.model_precache[i])\n\t\t\t\tCon_Printf(\"model %u: ^[%s\\\\modelviewer\\\\%s^]\\n\", i, sv.strings.model_precache[i], Mod_FixName(sv.strings.model_precache[i], sv.strings.model_precache[1]));\n\t\t}\n\t}\n\tif (!*group || !strncmp(group, \"sound\", 5))\n\t{\n\t\tfor (i = 0; i < MAX_PRECACHE_SOUNDS; i++)\n\t\t{\n\t\t\tif (sv.strings.sound_precache[i])\n\t\t\t\tCon_Printf(\"sound %u: ^[%s\\\\playaudio\\\\%s^]\\n\", i, sv.strings.sound_precache[i], sv.strings.sound_precache[i]);\n\t\t}\n\t}\n\tif (!*group || !strncmp(group, \"part\", 4))\n\t{\n\t\tfor (i = 0; i < MAX_SSPARTICLESPRE; i++)\n\t\t{\n\t\t\tif (sv.strings.particle_precache[i])\n\t\t\t\tCon_Printf(\"part  %u: %s\\n\", i, sv.strings.particle_precache[i]);\n\t\t}\n\t}\n}\n\nvoid SV_MemInfo_f(void)\n{\n\tint sz, i, fr, csfr;\n\tlaggedpacket_t *lp;\n\tclient_t *cl;\n\tCmd_ExecuteString(\"mod_memlist\", Cmd_ExecLevel);\n//\tCmd_ExecuteString(\"hunkprint\", Cmd_ExecLevel);\n\tfor (i = 0; i < svs.allocated_client_slots; i++)\n\t{\n\t\tcl = &svs.clients[i];\n\t\tif (cl->state)\n\t\t{\n\t\t\tCon_Printf(\"%s\\n\", cl->name);\n\t\t\tsz = 0;\n\t\t\tfor (lp = cl->laggedpacket; lp; lp = lp->next)\n\t\t\t\tsz += lp->length;\n\n\t\t\tfr = 0;\n\t\t\tfr += sizeof(client_frame_t)*UPDATE_BACKUP;\n\t\t\tif (cl->pendingdeltabits)\n\t\t\t{\n\t\t\t\tfr +=\tsizeof(cl)*UPDATE_BACKUP+\n\t\t\t\t\t\tsizeof(*cl->pendingdeltabits)*cl->max_net_ents;\n\t\t\t}\n\t\t\tfr += sizeof(*cl->frameunion.frames[0].resend)*cl->frameunion.frames[0].maxresend*UPDATE_BACKUP;\n\t\t\tfr += sizeof(entity_state_t)*cl->frameunion.frames[0].qwentities.max_entities*UPDATE_BACKUP;\n\t\t\tfr += sizeof(*cl->sentents.entities) * cl->sentents.max_entities;\n\n\t\t\tcsfr = sizeof(*cl->pendingcsqcbits) * cl->max_net_ents;\n\n\t\t\tCon_Printf(\"%\"PRIuSIZE\" minping=%i frame=%i, csqc=%i\\n\", sizeof(svs.clients[i]), sz, fr, csfr);\n\t\t}\n\t}\n\n\tif (sv.world.progs)\n\t\tCon_Printf(\"ssqc: %u (used) / %u (reserved)\\n\", sv.world.progs->stringtablesize, sv.world.progs->stringtablemaxsize);\n}\n\nvoid SV_Download_f (void)\n{\t//command for dedicated servers. apparently.\n#ifdef WEBCLIENT\n\tchar *url = Cmd_Argv(1);\n\tchar *localname = Cmd_Argv(2);\n\n\tif (!strnicmp(url, \"http://\", 7) || !strnicmp(url, \"https://\", 8) || !strnicmp(url, \"ftp://\", 6))\n\t{\n\t\tstruct dl_download *dl;\n\t\tif (Cmd_IsInsecure())\n\t\t\treturn;\n\t\tif (!*localname)\n\t\t{\n\t\t\tlocalname = strrchr(url, '/');\n\t\t\tif (localname)\n\t\t\t\tlocalname++;\n\t\t\telse\n\t\t\t{\n\t\t\t\tCon_TPrintf (\"no local name specified\\n\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\tdl = HTTP_CL_Get(url, localname, NULL);\n#ifdef MULTITHREAD\n\t\tif (dl)\n\t\t\tDL_CreateThread(dl, NULL, NULL);\n#else\n\t\t(void)dl;\n#endif\n\n\t\treturn;\n\t}\n#endif\n\tCon_Printf(\"scheme not supported\\n\");\n}\n\n/*\n==================\nSV_InitOperatorCommands\n==================\n*/\nvoid SV_InitOperatorCommands (void)\n{\n#ifndef SERVERONLY\n\tif (isDedicated)\n#endif\n\t{\n\t\tCmd_AddCommandD (\"quit\", SV_Quit_f, \"Exits the engine back to desktop.\");\n\t\tCmd_AddCommandD (\"say\", SV_ConSay_f, \"Send a chat message to everyone on the server.\");\n\t\tCmd_AddCommand (\"sayone\", SV_ConSayOne_f);\n\t\tCmd_AddCommand (\"tell\", SV_ConSayOne_f);\n\t\tCmd_AddCommand (\"serverinfo\", SV_Serverinfo_f);\t//commands that conflict with client commands.\n\t\tCmd_AddCommand (\"serverinfoblob\", SV_Serverinfo_f);\t//commands that conflict with client commands.\n\t\tCmd_AddCommand (\"user\", SV_User_f);\n\n\t\tCmd_AddCommandD (\"god\", SV_God_f, \"Makes you immune to damage.\");\n#ifdef QUAKESTATS\n\t\tCmd_AddCommand (\"give\", SV_Give_f);\n#endif\n\t\tCmd_AddCommandD (\"noclip\", SV_Noclip_f, \"Disables clipping, allowing you to fly through the level.\");\n\n\t\tCmd_AddCommand (\"download\", SV_Download_f);\n\t}\n\n#ifdef SUBSERVERS\n\tCvar_Register(&sv_autooffload, \"server control variables\");\n#endif\n\tCvar_Register(&sv_cheats, \"Server Permissions\");\n\tif (COM_CheckParm (\"-cheats\"))\n\t{\n\t\tCvar_Set(&sv_cheats, \"1\");\n\t}\n\n\tCmd_AddCommand (\"fraglogfile\", SV_Fraglogfile_f);\n\n\t//ask clients to take a remote screenshot\n\tCmd_AddCommand (\"snap\", SV_Snap_f);\n\tCmd_AddCommand (\"snapall\", SV_SnapAll_f);\n\n\t//various punishments\n\tCmd_AddCommandD (\"kick\", SV_Kick_f, \"Removes a player from the server, provide the name or IP of the desired player.\");\n\tCmd_AddCommand (\"clientkick\", SV_KickSlot_f);\n\tCmd_AddCommand (\"renameclient\", SV_ForceName_f);\n\tCmd_AddCommandD (\"mute\", SV_Mute_f, \"Mutes the player (no voice or chat), shaming them.\");\n\tCmd_AddCommandD (\"stealthmute\", SV_StealthMute_f, \"Mutes the player, without telling them, while pretending that their messages are still being broadcast. For use against people that would escalate on expiry or externally.\");\n\tCmd_AddCommandD (\"cuff\", SV_Cuff_f, \"Slap handcuffs on the player, preventing them from being able to attack.\");\n\tCmd_AddCommandD (\"cripple\", SV_CripplePlayer_f, \"Block the player's ability to move.\");\n\tCmd_AddCommandD (\"ban\", SV_BanClientIP_f, \"Block the player's IP, preventing them from connecting. Also kicks them.\");\n\tCmd_AddCommandD (\"banname\", SV_BanClientIP_f, \"Legacy compat, please use ban.\");\t//legacy dupe-name cruft\n\n\tCmd_AddCommandD (\"banlist\", SV_BanList_f, \"Displays a list of every banned player on the server.\");\t//shows only bans, not other penalties\n\tCmd_AddCommandD (\"unban\", SV_Unfilter_f, \"Unbans or removes an IP Address from the penality list, alias to removeip.\");\t//merely renamed.\n\n\tCmd_AddCommand (\"addip\", SV_FilterIP_f);\n\tCmd_AddCommandD (\"removeip\", SV_Unfilter_f, \"Removes an IP Address from the penality list.\");\n\tCmd_AddCommandD (\"listip\", SV_FilterList_f, \"Displays a list of ever player the server has penalties for.\");\t//shows all penalties\n\tCmd_AddCommand (\"writeip\", SV_WriteIP_f);\n\n\tCmd_AddCommand (\"floodprot\", SV_Floodprot_f);\n\n\tCmd_AddCommandD (\"status\", SV_Status_f, \"Prints info about the current server.\");\n\n\tCmd_AddCommand (\"sv\", SV_SendGameCommand_f);\n\tCmd_AddCommand (\"mod\", SV_SendGameCommand_f);\n\n#ifdef SUBSERVERS\n\tCmd_AddCommand (\"ssv\", MSV_SubServerCommand_f);\n\tCmd_AddCommand (\"ssv_all\", MSV_SubServerCommand_f);\n\tCmd_AddCommandAD (\"mapcluster\", MSV_MapCluster_f, SV_Map_c, \"Sets this server up as a cluster-server gateway. Additional processes will be used to host individual maps. If an argument is given then that will be the name of the map that new clients will initially be directed to. This can also be used for single-player to off-load nearly all server functions - use the 'ssv' command to direct each subserver.\");\n#endif\n\tCmd_AddCommand (\"killserver\", SV_KillServer_f);\n\tCmd_AddCommandD (\"precaches\", SV_PrecacheList_f, \"Displays a list of current server precaches.\");\n\tCmd_AddCommandAD (\"map\", SV_Map_f, SV_Map_c, \"Begins a new game on the specified map.\");\n\tCmd_AddCommandAD (\"mapedit\", SV_Map_f, SV_Map_c, \"Loads the named map without any gamecode active.\");\n#ifdef Q3SERVER\n\tCmd_AddCommandAD (\"spmap\", SV_Map_f, SV_Map_c, \"Loads a map in single-player mode, for Quake III compat.\");\n\tCmd_AddCommandAD (\"spdevmap\", SV_Map_f, SV_Map_c, \"Loads a map in single-player developer mode (sv_cheats 1), for Quake III compat.\");\n#endif\n\tCmd_AddCommandAD (\"devmap\", SV_Map_f, SV_Map_c, \"Loads a map in developer mode (sv_cheats 1), for Quake III compat.\");\n\tCmd_AddCommandAD (\"gamemap\", SV_Map_f, SV_Map_c, NULL);\n\tCmd_AddCommandAD (\"changelevel\", SV_Map_f, SV_Map_c, \"Continues the game on a different map. The current map can be reentered later when a starting position for the new map is specified as a second argument.\");\n\tCmd_AddCommandD (\"map_restart\", SV_Map_f, \"Restarts the server and reloads the map while flushing level cache, for general use and Quake III compat.\");\t//from q3.\n\tCmd_AddCommandD (\"listmaps\", SV_MapList_f, \"Displays a list of installed maps.\");\n\tCmd_AddCommandD (\"maplist\", SV_MapList_f, \"Displays a list of installed maps.\");\n\tCmd_AddCommandD (\"maps\", SV_MapList_f, \"Displays a list of installed maps.\");\n#if defined(HAVE_LEGACY) && defined(HAVE_SERVER)\n\tCmd_AddCommandD (\"check_maps\", SV_redundantcommand_f, \"Obsolete, specific to ktpro. Modern mods should use search_begin instead.\");\n\tCmd_AddCommandD (\"sys_select_timeout\", SV_redundantcommand_f, \"Redundant - server will throttle according to tick rates instead.\");\n\tCmd_AddCommandD (\"sv_downloadchunksperframe\", SV_redundantcommand_f, \"Flawed - downloads instead proceed at the client's drate (or rate) setting instead of ignoring it entirely.\");\n\tCmd_AddCommandD (\"sv_speedcheck\", SV_redundantcommand_f, \"Obsolete - movetime is instead metered over time, instead of randomly kicking everyone due to dodgy timer hardware on the server.\");\n\tCmd_AddCommandD (\"sv_enableprofile\", SV_redundantcommand_f, \"Debug setting that is not implemented.\");\n\tCmd_AddCommandD (\"sv_progsname\", SV_redundantcommand_f, \"Use sv_progs instead.\");\n\tCmd_AddCommandD (\"download_map_url\", SV_redundantcommand_f, \"Redundant - individual maps will probably download faster than the user can open a browser at the given url.\");\n\tCmd_AddCommandD (\"sv_progtype\", SV_redundantcommand_f, \"Use sv_progs instead. Using to block .dll loading is insufficient with buggy clients around.\");\n#endif\n\n\tCmd_AddCommandD (\"heartbeat\", SV_Heartbeat_f, \"Sends an update or ping to the master server so the current server can remain listed.\");\n\n\tCmd_AddCommand (\"localinfo\", SV_Localinfo_f);\n\tCmd_AddCommandAD (\"gamedir\", SV_Gamedir_f, SV_Gamedir_c, \"Change the current gamedir.\");\n\tCmd_AddCommandAD (\"sv_gamedir\", SV_Gamedir, SV_Gamedir_c, \"Change the gamedir reported to clients, without changing any actual paths on the server.\");\n\tCmd_AddCommand (\"sv_settimer\", SV_SetTimer_f);\n\tCmd_AddCommand (\"stuffcmd\", SV_StuffToClient_f);\n\n\tCmd_AddCommand (\"pin_save\", SV_Pin_Save_f);\n\tCmd_AddCommand (\"pin_reload\", SV_Pin_Reload_f);\n\tCmd_AddCommand (\"pin_delete\", SV_Pin_Delete_f);\n\tCmd_AddCommand (\"pin_add\", SV_Pin_Add_f);\n\n\tCmd_AddCommand(\"sv_meminfo\", SV_MemInfo_f);\n\n//\tCmd_AddCommand (\"reallyevilhack\", SV_ReallyEvilHack_f);\n}\n\n#endif\n"
  },
  {
    "path": "engine/server/sv_chat.c",
    "content": "#include \"quakedef.h\"\n\n/*\nNPC chat:\nThe idea is that the server reads in a text file, calls qc functions as appropriate, and centerprints the result to the corresponding player.\nAll play is paused while the chat is in progress.\nThere are no guarentees that this is useful to anyone. If it does sound sorta what you want and would like a small change then just ask.\n\nThe options are specified with commas between. a number specifies a tag, anything else is assumed to be a statement/function,\nwhich is carried out when the text is used (rather than when it is mearly an option).\nstatements can be functions, addition, multiplication, division, subtraction. Storing is also possible.\nTo store, you need to name a variable (which will be created when used). The variable must be a float.\nStrings will work ONLY when specified without arithmatic, and ONLY in function calls.\nYou can call QC functions which accept entities on the condition that the entity accepted is the player entity that is being used for the menu.\nIf you want to keep track of the person you are chatting to, use an entity field for it.\nTo pass this entity, use the word 'ent', without quotes of any kind.\nDo not use operators or calls within a function call. It's not that advanced. You can use operators on the condition that you use and name a temp.\n\n\n\n\n\nExmaple dialog script:\ndialog/barry.dlg:\n\n\t#tag/number, condition (float(entity ent) cond;), text, options.\n\t#if options has a function, that is called. Multiple options/funcs can be done with commas. No options will terminate. If a reply has multiple options, a random (non function) is picked.\n\t#no distinction between character speech and player options.\n\t#char statements ignore condition.\n\n\t#the first entry point\n\t{0}{}{So that slip-gate does work then. Hmph. Maybe they just turned the power back on! So, what can I do you for?}{1, 2, 3}\n\t{1}{}{Whu? Who are you?! Where am I?!}{10}\n\t{2}{}{Who ever you are, now you die!}{20}\n\t{3}{}{Oh, err, nothing. I know the story line already. Sorry.}{4}\n\t{4}{}{KNOW IT ALL!}{}\n\n\t{10}{}{You are in a place that we dub the 'Shambles', a resource drained town. We havn't managed to rebuild our society since the Destroyer dude came through here, killing anyone before they could ask why. We even had anarcy! Boy, that was fun! Anyway, who are you?}{11, 12}\n\t{11}{}{I'm just some random guy who was walking a dog. Then I got trapped behind a rock fall and found this really old place all boarded up. Next I knew I was here! I think I killed my dog though...}{30}\n\t{12}{}{Why the hell do you care! I'm just goin' to kill you now!}{20}\n\n\t{20}{}{That's just not nice!}{BarryWar(), give_arrogance(ent, 100), give_evil(ent, 500)}\n\n\t{30}{}{Do you reckon that you're much good with explosives? It's just that our church yard is a little over-run with zombies. You know how it is when some-one knicks your runes! I'm just asking, cos we don't much like cremation around here. It's a waste of fertilliser!}{31, 32, 33}\n\t{31}{}{I rekon you could blast 'em into chunky kibbles better than I.}{40}\n\t{32}{has_grenadelauncher(ent)}{I'll go right now!}{}\n\t{33}{!has_grenadelauncher(ent)}{Well, I would, but seeing as I ain't got a grenade launcher, I can't}{50}\n\n\t{40}{}{Yeah, probably. Bye!}{}\n\n\t{50}{}{That could be a problem couldn't it. Ah well, here, have my spare Rocket Launcher. I'm meant to use it against any nasties that come through that gate, but we havn't had anyone except you for years!}{give_weapon_rocketlauncher(ent), give_cunning(ent 100)}\n\n\t#second entry point (use a tag parameter of 200)\n\t{200}{}{I'm sorry, I can't help you in any way.}{}\n\n\n\n\nthe qc code:\n\tvoid(string name, float tag, entity player) Chat = #214;\n\n\tvoid() SomeMonstersTouchFunction =\n\t{\n\t\tChat(\"barry\", 0, other);\n\t}\n*/\n\n#if defined(SVCHAT) && !defined(CLIENTONLY)\n\n#define SENDDELAY 1\n\nfloat SV_ChatFunc(const char *func);\nvoid Chat_GetTag(const char *filename, float tag, char **text, char **condition, char **options)\n{\t\n\tchar *file; char *s;\n\tfile = COM_LoadTempFile(va(\"dialog/%s.dlg\", filename), NULL);\n\tif (!file)\n\t{\n\t\t*text = \"ERROR: File not found\";\n\t\t*condition = \"\";\n\t\t*options = \"\";\n\t\treturn;\n\t}\n\tfor (;*file;file++)\n\t{\n\t\tif (*file == '\\n')\n\t\t{\n\t\t\tif (file[1] == '{')\n\t\t\t{\n\t\t\t\tif(atof(file+2)==tag)\n\t\t\t\t{\n\t\t\t\t\t*condition = strchr(file+2, '{')+1;\n\n\t\t\t\t\ts = strchr(file, '|');\n\t\t\t\t\tif (s && s < *condition)\n\t\t\t\t\t{\n\t\t\t\t\t\t*text = strchr(s, '}');\n\t\t\t\t\t\t**text = '\\0';\n\n\t\t\t\t\t\tif (!SV_ChatFunc(s+1))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t**text='}';\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\ts = strchr(*condition, '}');\n\t\t\t\t\t*s = '\\0';\n\n\t\t\t\t\t*text = strchr(s+1, '{')+1;\t\n\t\t\t\t\ts = strchr(*text, '}');\n\t\t\t\t\t*s = '\\0';\n\n\t\t\t\t\t*options = strchr(s+1, '{')+1;\n\t\t\t\t\ts = strchr(*options, '}');\n\t\t\t\t\t*s = '\\0';\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t}\t\t\n\t}\n\t*text = va(\"Chat Tag %f not found in file %s\", tag, host_client->chat.filename);\n\t*condition = \"\";\n\t*options = \"\";\n\treturn;\n}\n\nchatvar_t *SV_ChatFindVariable(char *name)\n{\n\tchatvar_t *cvar;\n\tcvar = host_client->chat.vars;\n\twhile (cvar)\n\t{\n\t\tif (!strcmp(cvar->varname, name))\n\t\t\treturn cvar;\n\n\t\tcvar = cvar->next;\n\t}\n\treturn NULL;\n}\n\nfloat SV_ChatGetValue(char *name)\n{\n\tchatvar_t *cvar;\t\n\tif ((*name >= '0' && *name <= '9') || *name == '-')\n\t\treturn atof(name);\n\tif (!strcmp(name, \"true\"))\n\t\treturn 1;\n\tif (!strcmp(name, \"false\"))\n\t\treturn 0;\n\tcvar = SV_ChatFindVariable(name);\n\tif (cvar)\n\t\treturn cvar->value;\n\treturn 0;\n}\n\nfloat SV_ChatSetValue(char *name, float newval)\n{\n\tchatvar_t *cvar;\n\tcvar = SV_ChatFindVariable(name);\n\tif (cvar)\n\t{\n\t\tcvar->value = newval;\n\t\treturn newval;\n\t}\n\tcvar = Z_Malloc(sizeof(chatvar_t));\n\tstrcpy(cvar->varname, name);\n\tcvar->value = newval;\n\tcvar->next = host_client->chat.vars;\n\thost_client->chat.vars = cvar;\n\n\treturn newval;\n}\n\nfloat SV_ChatFunc(const char *func)\t//parse a condition/function\n{\n\tglobalvars_t *pr_globals;\n\tfloat result;\n\tint noted = false;\n\tconst char *s, *os;\n\tchar namebuf[64];\n\tfunc_t f;\n\tint parm;\n\twhile (*func <= ' ')\n\t\tfunc++;\n\tif (*func == '!')\n\t{\n\t\tnoted = true;\n\t\tfunc++;\n\t}\n\ts = COM_ParseToken(func, NULL);\n\tif (*com_token == '(')\n\t{//open bracket\n\t\t//find correct close\n\t\tparm = 1;\n\t\tfor (s = func+1; ; s++)\n\t\t{\n\t\t\tif (!*s)\n\t\t\t\tSys_Error(\"No close bracket\");\n\t\t\tif (*s == ')')\n\t\t\t{\n\t\t\t\tparm--;\n\t\t\t\tif (!parm)break;\n\t\t\t}\n\t\t\tif (*s == '(')\n\t\t\t\tparm++;\n\t\t}\n\t\tfunc = strchr(func, '(');\n\t\ts=COM_ParseToken(s+1, NULL);\n\t\tif (!strncmp(com_token, \"&&\", 2))\n\t\t\tresult = SV_ChatFunc(func+1) && SV_ChatFunc(s);\n\t\telse if (!strncmp(com_token, \"||\", 2))\n\t\t\tresult = SV_ChatFunc(func+1) || SV_ChatFunc(s);\n\t\telse\n\t\t\tresult = SV_ChatFunc(func+1);\n\t}\n\telse\n\t{\n\tstrcpy(namebuf, com_token);\t//get first word\t\n\n\twhile (*s <= ' ')\n\t\ts++;\n\t\n\tif (*s == '(')\t//if followed by brackets\n\t{\n\t\ts++;\n\t\tpr_globals = PR_globals(svprogfuncs, PR_CURRENT);\n\t\tparm = OFS_PARM0;\n\t\twhile((s=COM_ParseToken(os = s, NULL)))\n\t\t{\n\t\t\tif (*com_token == ')')\n\t\t\t\tbreak;\n\t\t\tif (*com_token == ',')\n\t\t\t\tcontinue;\n\t\t\tif (com_tokentype == TTP_STRING)\n\t\t\t\tG_INT(parm) = PR_NewString(svprogfuncs, com_token);\n\t\t\telse if (!strcmp(com_token, \"ent\"))\n\t\t\t\tG_INT(parm) = EDICT_TO_PROG(svprogfuncs, host_client->chat.edict);\n\t\t\telse\n\t\t\t\tG_FLOAT(parm) = SV_ChatFunc(os);\n\n\t\t\tparm+=OFS_PARM1-OFS_PARM0;\n\t\t}\n\n\t\tf = PR_FindFunction(svprogfuncs, namebuf, PR_CURRENT);\n\t\tif (f)\n\t\t{\t\t\n\t\t\tPR_ExecuteProgram(svprogfuncs, f);\n\t\t}\n\t\telse\n\t\t\tCon_Printf(\"Failed to find function %s\\n\", namebuf);\n\n\t\tpr_globals = PR_globals(svprogfuncs, PR_CURRENT);\n\t\tresult = G_FLOAT(OFS_RETURN);\n\t}\n\telse\n\t{\n\t\t//comparision operators\n\t\tif (!strncmp(s, \"==\", 2))\n\t\t\tresult = (SV_ChatGetValue(namebuf) == SV_ChatFunc(s+2));\n\t\telse if (!strncmp(s, \">=\", 2))\n\t\t\tresult = (SV_ChatGetValue(namebuf) >= SV_ChatFunc(s+2));\n\t\telse if (!strncmp(s, \"<=\", 2))\n\t\t\tresult = (SV_ChatGetValue(namebuf) <= SV_ChatFunc(s+2));\n\t\telse if (!strncmp(s, \"!=\", 2))\n\t\t\tresult = (SV_ChatGetValue(namebuf) != SV_ChatFunc(s+2));\n\t\telse if (!strncmp(s, \">\", 1))\n\t\t\tresult = (SV_ChatGetValue(namebuf) >= SV_ChatFunc(s+2));\n\t\telse if (!strncmp(s, \"<\", 1))\n\t\t\tresult = (SV_ChatGetValue(namebuf) <= SV_ChatFunc(s+2));\n\n\t\t//asignment operators\n\t\telse if (!strncmp(s, \"=\", 1))\n\t\t\tresult = (SV_ChatSetValue(namebuf, SV_ChatFunc(s+1)));\n\t\telse if (!strncmp(s, \"+=\", 2))\n\t\t\tresult = (SV_ChatSetValue(namebuf, SV_ChatGetValue(namebuf)+SV_ChatFunc(s+2)));\n\t\telse if (!strncmp(s, \"-=\", 2))\n\t\t\tresult = (SV_ChatSetValue(namebuf, SV_ChatGetValue(namebuf)-SV_ChatFunc(s+2)));\n\t\telse if (!strncmp(s, \"*=\", 2))\n\t\t\tresult = (SV_ChatSetValue(namebuf, SV_ChatGetValue(namebuf)*SV_ChatFunc(s+2)));\n\t\telse if (!strncmp(s, \"/=\", 2))\n\t\t\tresult = (SV_ChatSetValue(namebuf, SV_ChatGetValue(namebuf)/SV_ChatFunc(s+2)));\n\t\telse if (!strncmp(s, \"|=\", 2))\n\t\t\tresult = (SV_ChatSetValue(namebuf, (int)SV_ChatGetValue(namebuf)|(int)SV_ChatFunc(s+2)));\n\t\telse if (!strncmp(s, \"&=\", 2))\n\t\t\tresult = (SV_ChatSetValue(namebuf, (int)SV_ChatGetValue(namebuf)&(int)SV_ChatFunc(s+2)));\n\n\t\t//mathematical operators\n\t\telse if (!strncmp(s, \"+\", 1))\n\t\t\tresult = ( SV_ChatGetValue(namebuf)+SV_ChatFunc(s+1));\n\t\telse if (!strncmp(s, \"-\", 1))\n\t\t\tresult = (SV_ChatGetValue(namebuf)-SV_ChatFunc(s+1));\n\t\telse if (!strncmp(s, \"*\", 1))\n\t\t\tresult = (SV_ChatGetValue(namebuf)*SV_ChatFunc(s+1));\n\t\telse if (!strncmp(s, \"/\", 1))\n\t\t\tresult = (SV_ChatGetValue(namebuf)/SV_ChatFunc(s+1));\n\t\telse if (!strncmp(s, \"|\", 1))\n\t\t\tresult = ((int)SV_ChatGetValue(namebuf)|(int)SV_ChatFunc(s+1));\n\t\telse if (!strncmp(s, \"&\", 1))\n\t\t\tresult = ((int)SV_ChatGetValue(namebuf)&(int)SV_ChatFunc(s+1));\n\n\t\t//operatorless\n\t\telse\n\t\t\tresult = SV_ChatGetValue(namebuf);\n\t}\n\t}\n\n\tif (noted)\n\t\tresult = !result;\n\n\treturn result;\n}\n\n\nvoid SV_SendChat(void)\n{\n\tint l;\n\tint i;\n\tchar *s, *s2, *text;\n\n\tl = 2 + strlen(host_client->chat.maintext)+2;\n\tfor (i = 0; i < host_client->chat.options; i++)\n\t\tl += strlen(host_client->chat.option[i].text)+2;\n\n\ts = Z_Malloc(l+1);\n\ts2 = s;\n\ttext = host_client->chat.maintext;\n\twhile(*text)\n\t{\n\t\t*s2 = *text ^ 128;\n\t\ttext++;\n\t\ts2++;\n\t}\n\t*s2++='\\n';\n\t*s2++='\\n';\n\t*s2='\\0';\n\tfor (i = 0; i < host_client->chat.options; i++)\n\t{\n\t\tstrcat(s2, host_client->chat.option[i].text);\n\t\tstrcat(s2, \"\\n\\n\");\n\t}\n\n\n\tClientReliableWrite_Begin (host_client, svc_centerprint, 2 + l);\n\tClientReliableWrite_String (host_client, s);\n\n/*\n\tMSG_WriteByte(&sv.reliable_datagram, svc_chat);\n\tif (!*host_client->chat.maintext)\t//no text. don't mix with ends\n\t\tMSG_WriteString(&sv.reliable_datagram, \" \");\n\telse\n\t\tMSG_WriteString(&sv.reliable_datagram, host_client->chat.maintext);\n\n\tMSG_WriteByte(&sv.reliable_datagram, i);\n\tfor (i = 0; i < host_client->chat.options; i++)\n\t\tMSG_WriteString(&sv.reliable_datagram, host_client->chat.option[i].text);\n*/\n\n\thost_client->chat.time = sv.time+SENDDELAY;\n}\nvoid SV_EndChat(void)\n{\n\thost_client->chat.active = false;\n\thost_client->chat.time=0;\n\n\tClientReliableWrite_Begin (host_client, svc_centerprint, 2);\n\tClientReliableWrite_String (host_client, \"\");\n/*\n\tMSG_WriteByte(&sv.reliable_datagram, svc_chat);\n\tMSG_WriteString(&sv.reliable_datagram, \"\");\n*/\n}\n\n\nvoid SV_Chat(const char *filename, float starttag, edict_t *edict)\n{\n\tint i, tag;\n\tchar optiontext[1024];\n\tchar *text; char *condition; char *options;\n\tchar *s, *s2;\n\tif (host_client->chat.active)\n\t\treturn;\n\thost_client->chat.active = true;\n\n\thost_client->chat.edict = edict;\n\tstrcpy(host_client->chat.filename, filename);\n\tChat_GetTag(filename, starttag, &text, &condition, &options);\n\tstrcpy(host_client->chat.maintext, text);\n\n\tstrcpy(optiontext, options);\n\n\n\ts = optiontext;\t\n\tfor (i = 0; i < 6 && s; )\n\t{\n\t\tif (!*s)\n\t\t\tbreak;\n\t\tfor (s2=s;;s2++)\n\t\t{\n\t\t\tif (*s2 == ',')\n\t\t\t{\n\t\t\t\t*s2='\\0';\n\t\t\t\ts2++;\n\t\t\t\tbreak;\t\n\t\t\t}\n\t\t\tif (*s2 == '\\0')\n\t\t\t{\n\t\t\t\ts2=NULL;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\twhile (*s <= ' ')\n\t\t\ts++;\n\n\t\tif ((*s >= '0' && *s <= '9') || (*s == '-' && s[1] >= '0' && s[1] <= '9'))\n\t\t{\n\t\t\ttag = atof(s);\n\t\t\tChat_GetTag(filename, tag, &text, &condition, &options);\n\t\t\tstrcpy(host_client->chat.option[i].text, text);\n\t\t\thost_client->chat.option[i].tag = tag;\t\t\t\n\n\t\t\tif (*condition)\n\t\t\t{\n\t\t\t\tif (SV_ChatFunc(condition))\n\t\t\t\t\ti++;\n\t\t\t\t/*\n\t\t\t\tif (*condition == '!'){noted=true;condition++;}else noted=false;\n\t\t\t\tf = PR_FindFunction(condition, PR_CURRENT);\n\t\t\t\tif (f)\n\t\t\t\t{\n\t\t\t\t\tSetGlobalEdict(sv.chat.edict, OFS_PARM0);\n\t\t\t\t\tPR_ExecuteProgram(f);\n\t\t\t\t\tpr_globals = PR_globals(PR_CURRENT);\n\t\t\t\t\tif (noted)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (!G_FLOAT(OFS_RETURN))\n\t\t\t\t\t\t\ti++;\n\t\t\t\t\t}\n\t\t\t\t\telse \n\t\t\t\t\t{\n\t\t\t\t\t\tif (G_FLOAT(OFS_RETURN))\n\t\t\t\t\t\t\ti++;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tCon_Printf(\"Function \\\"%s\\\" was not found (for chat condition). Assumed true.\\n\", condition);\n\t\t\t\t\ti++;\n\t\t\t\t}\n\t\t\t\t*/\n\t\t\t}\n\t\t\telse\n\t\t\t\ti++;\n\t\t}\n\t\telse\n\t\t{\n\n\t\t\tSV_ChatFunc(s);\n\t\t\t/*\n\t\t\tf = PR_FindFunction(s, PR_CURRENT);\n\t\t\tif (f)\n\t\t\t{\n\t\t\t\tSetGlobalEdict(sv.chat.edict, OFS_PARM0);\n\t\t\t\tPR_ExecuteProgram(f);\n\t\t\t}\n\t\t\telse\n\t\t\t\tCon_Printf(\"Function \\\"%s\\\" was not found (for chat)\\n\", s);\n\t\t\t*/\n\t\t}\n\n\t\ts = s2;\n\t}\n\n\thost_client->chat.options=i;\n\nSV_SendChat();\n}\n\nvoid SV_RecieveChat(int sel)\n{\n\tchar *text;\n\tchar *condition;\n\tchar *options, *s, *s2;\n\tfloat tag;\n\tif (!host_client->chat.active)\n\t\treturn;\n\tif (host_client->chat.options == 0)\n\t{\n\t\tSV_EndChat();\n\t\treturn;\n\t}\n\n\tsel = host_client->chat.option[sel-1].tag;\n\n\tChat_GetTag(host_client->chat.filename, sel, &text, &condition, &options);\n\ttag = -1;\n\tfor (s = options;s; )\n\t{\n\t\tif (!*s)\n\t\t\tbreak;\n\t\tfor (s2=s;;s2++)\n\t\t{\n\t\t\tif (*s2 == ',')\n\t\t\t{\n\t\t\t\t*s2='\\0';\n\t\t\t\ts2++;\n\t\t\t\tbreak;\t\n\t\t\t}\n\t\t\tif (*s2 == '\\0')\n\t\t\t{\n\t\t\t\ts2=NULL;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tif ((*s >= '0' && *s <= '9') || *s <= ' ')\n\t\t{\n\t\t\ttag = atof(s);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tSV_ChatFunc(s);\n\t\t\t/*\n\t\t\tf = PR_FindFunction(s, PR_CURRENT);\n\t\t\tif (f)\n\t\t\t{\n//\t\t\t\t*progfuncs->pr_trace = 2;\n\t\t\t\tSetGlobalEdict(sv.chat.edict, OFS_PARM0);\n\t\t\t\tPR_ExecuteProgram(f);\n\t\t\t}\n\t\t\telse\n\t\t\t\tCon_Printf(\"Function \\\"%s\\\" was not found (for chat)\\n\", s);\n\t\t\t\t*/\n\t\t}\n\n\t\ts = s2;\n\t}\n\n\tif (tag < 0)\n\t{\n\t\tSV_EndChat();\n\t\treturn;\n\t}\n\n\thost_client->chat.active=false;\n\tSV_Chat(host_client->chat.filename, tag, host_client->chat.edict);\n}\n\n//new game\nvoid SV_WipeChat(client_t *client)\n{\n\tchatvar_t *var;\n\tchatvar_t *lastvar = NULL;\n\tfor (var = client->chat.vars; var; var=var->next)\n\t{\n\t\tif (lastvar)\n\t\t\tZ_Free(lastvar);\n\t\tlastvar = var;\n\t}\n\tif (lastvar)\n\t\tZ_Free(lastvar);\n\n\tclient->chat.active = false;\n\tclient->chat.vars = NULL;\n}\n\n//end of level. Maybe not though.\nchar *SV_SaveChat(void)\t//is malloc. Clear old before call.\n{\n\tchatvar_t *var;\n\tchar *buf, *start;\n\tint bufsize=0;\n\tchar tbuf[64];\n\tbuf = tbuf;\n\tfor (var = host_client->chat.vars; var; var=var->next)\n\t{\n\t\tbufsize += strlen(var->varname);\n\t\tsprintf(buf, \"=%f;\", var->value);\n\t\tbufsize += strlen(buf);\n\t}\n\tbufsize++;\n\tstart = buf = BZ_Malloc(bufsize);\n\n\tfor (var = host_client->chat.vars; var; var=var->next)\n\t{\n\t\tstrcpy(buf, var->varname);\t\t\n\t\tbuf += strlen(var->varname);\t\t\n\t\tsprintf(buf, \"=%f;\", var->value);\n\t\tbuf += strlen(buf);\n\t}\n\t*buf = 0;\n\n\treturn start;\n}\n\n//beginning of new level.\nvoid SV_LoadChat(char *buffer)\n{\n\tchar namebuf[64], value[64];\n\tchar *start, *eq, *end;\n\tSV_WipeChat(host_client);\n\tif (!buffer)\n\t\treturn;\n\n\tstart = buffer;\n\tfor(;;)\n\t{\t\t\n\t\teq = strchr(start, '=');\n\t\tif (!eq)\n\t\t\tbreak;\n\t\tend = strchr(eq, ';');\n\t\tif (!end)\n\t\t\tbreak;\n\n\t\tQ_strncpyz(namebuf, start, eq-start);\n\t\tQ_strncpyz(value, eq+1, end-(eq+1));\n\t\tSV_ChatSetValue(namebuf, atof(value));\n\t\tstart = end + 1;\n\t\twhile (*start <= ' ')\n\t\t{\n\t\t\tif (*start == '\\0')\n\t\t\t\tbreak;\n\t\t\tstart++;\n\t\t}\n\t}\n}\n\n\nint SV_ChatMove(int impulse)\n{\n\tif (host_client->chat.active && impulse)\n\t\tSV_RecieveChat(impulse);\n\treturn false;\n}\nvoid SV_ChatThink(client_t *client)\n{\n\tif (client->chat.active && client->chat.time < sv.time)\t//centerprint wears off. Resend every few secs.\n\t{\n\t\thost_client = client;\n\t\tSV_SendChat();\n\t}\n}\n\n#endif\n\n"
  },
  {
    "path": "engine/server/sv_cluster.c",
    "content": "#include \"quakedef.h\"\r\n#include \"netinc.h\"\r\n\r\n//these are the node names as parsed by MSV_FindSubServerName\r\n//\"5\" finds server 5 only\r\n//\"5:\" is equivelent to the above\r\n//\"5:dm4\" finds server 5\r\n//\t\t\tif not running, it will start up using dm4, even if dm4 is already running on node 4, say.\r\n//\"0:dm4\" starts a new server running dm4, even if its already running\r\n//\":dm4\" finds any server running dm4. starts a new one if none are running dm4.\r\n//\"dm4\" is ambiguous, in the case of a map beginning with a number (bah). don't use.\r\n\r\n//FIXME: nq protocols not supported.\r\n//FIXME: deadlocks when both gw and ss both fill their pipe buffers.\r\n//FIXME: no networking for remote nodes.\r\n\r\n//The servers are arranged as a 'tree'.\r\n//There is only one root server, the one that used the 'mapcluster [startmap]' command.\r\n//  This is treated as a gateway, that clients connect to and then get redirected to one of the other servers.\r\n//  It may be a 'listen' server, with the server component offloaded to another process/thread.\r\n//  It may be a dedicated server started with the 'map' command (but not a listen server).\r\n//Additional 'leaf' servers are automatically started on the same host.\r\n//  Will be started automatically when a player tries to transfer to a new/unknown map.\r\n//\tleaf sends a ccmd_serveraddress at init+mapchanges\r\n//  root sends a ccmd_acceptserver to tell the new leaf what it should be.\r\n\r\n//Transferring a player from one leaf to another:\r\n//  ccmd_transferplayer is sent to the root (which includes the player's parms)\r\n//  the root finds/creates the target server and sends it the ccmd_takeplayer message.\r\n//  destination tries to create a loadzombie and replies with ccmd_tookplayer (to accept or reject), which root forwards to the source\r\n//  source tells client to connect to the destination's address\r\n//  destination receives connection from client (or times out) and sends a ccmd_saveplayer(0) to the root, root sees the server change and sends ccmd_transferedplayer to the source.\r\n//  source knows that the player is no longer present (or aborts the transfer if it was a timeout, reenabling other transfers/retries).\r\n\r\n//to connect a new server to a remote gateway, add '-clusterhost GATEWAY:TCPPORT PASSWORD' to the new server's commandline. The gateway needs eg sv_port_tcp open (you might wish to ipfilter for added security).\r\n\r\n#ifdef SUBSERVERS\r\n\r\n#ifdef SQL\r\n#include \"sv_sql.h\"\r\n#endif\r\n\r\n#define S_COLOR_SUBSERVER S_COLOR_MAGENTA\r\n\r\ntypedef struct pubsubserver_s\r\n{\r\n\tvfsfile_t *stream;\r\n\r\n\tstruct pubsubserver_s *next;\r\n\tunsigned int id;\r\n\tchar name[64];\r\n\tint activeplayers;\r\n\tint transferingplayers;\r\n\tnetadr_t addrv4;\r\n\tnetadr_t addrv6;\r\n\tchar printtext[4096]; //to split it into lines.\r\n\tqboolean started;\r\n#ifdef HAVE_CLIENT\r\n\tconsole_t *console;\r\n\tqboolean killing;\r\n#endif\r\n\r\n\r\n\tsize_t inbuffersize;\r\n\tqbyte inbuffer[8192];\r\n\r\n\tqboolean outfailed;\r\n//\tsize_t outbuffersize;\r\n//\tqbyte outbuffer[8192];\r\n} pubsubserver_t;\r\n\r\n\r\nextern cvar_t sv_serverip;\r\n\r\nvoid VARGS SV_RejectMessage(enum serverprotocols_e protocol, char *format, ...);\r\n\r\n\r\nvoid MSV_UpdatePlayerStats(unsigned int playerid, unsigned int serverid, int numstats, float *stats);\r\n\r\ntypedef struct {\r\n\t//fixme: hash tables\r\n\tunsigned int\tplayerid;\r\n\tchar\t\t\tname[64];\r\n\tchar\t\t\tguid[64];\r\n\tchar\t\t\taddress[64];\r\n\r\n\tlink_t allplayers;\r\n//\tlink_t sameserver;\r\n\r\n\tpubsubserver_t *server;\t//should never be null\r\n} clusterplayer_t;\r\n\r\nstatic pubsubserver_t *subservers;\r\nstatic link_t clusterplayers;\r\nenum clusterslavemode_e\tisClusterSlave;\r\nstatic vfsfile_t *controlconnection = NULL;\r\nstatic unsigned int nextserverid;\r\n\r\nstatic void MSV_WriteSlave(pubsubserver_t *ps, sizebuf_t *cmd)\r\n{\r\n\t//FIXME: this is blocking. this is bad if the target is also blocking while trying to write to us.\r\n\t//FIXME: merge buffering logic with SSV_InstructMaster, and allow for failure if full\r\n\tvfsfile_t *s = ps->stream;\r\n\tint wrote;\r\n\tif (ps->outfailed)\r\n\t\treturn;\t//give up after the first failure, to avoid corruption.\r\n\tcmd->data[0] = cmd->cursize & 0xff;\r\n\tcmd->data[1] = (cmd->cursize>>8) & 0xff;\r\n\twrote = VFS_WRITE(s, cmd->data, cmd->cursize);\r\n\tif (wrote != cmd->cursize)\r\n\t\tps->outfailed = true;\r\n}\r\n\r\nstatic int MSV_SubServerRead(pubsubserver_t *ps)\r\n{\r\n\tif (ps->inbuffersize < sizeof(ps->inbuffer))\r\n\t{\r\n\t\tint avail = VFS_READ(ps->stream, ps->inbuffer+ps->inbuffersize, sizeof(ps->inbuffer)-ps->inbuffersize);\r\n\t\tif (avail < 0)\r\n\t\t\treturn avail;\r\n\t\tps->inbuffersize += avail;\r\n\t}\r\n\r\n\tif(ps->inbuffersize >= 2)\r\n\t{\r\n\t\tunsigned short len = ps->inbuffer[0] | (ps->inbuffer[1]<<8);\r\n\t\tif (ps->inbuffersize >= len && len>=2)\r\n\t\t{\r\n\t\t\tmemcpy(net_message.data, ps->inbuffer+2, len-2);\r\n\t\t\tnet_message.cursize = len-2;\r\n\t\t\tmemmove(ps->inbuffer, ps->inbuffer+len, ps->inbuffersize - len);\r\n\t\t\tps->inbuffersize -= len;\r\n\t\t\tMSG_BeginReading (&net_message, msg_nullnetprim);\r\n\r\n\t\t\treturn len;\r\n\t\t}\r\n\t}\r\n\treturn 0;\r\n}\r\n\r\nstatic clusterplayer_t *MSV_FindPlayerId(unsigned int playerid)\r\n{\r\n\tlink_t *l;\r\n\tclusterplayer_t *pl;\r\n\r\n\tFOR_EACH_LINK(l, clusterplayers)\r\n\t{\r\n\t\tpl = STRUCT_FROM_LINK(l, clusterplayer_t, allplayers);\r\n\t\tif (pl->playerid == playerid)\r\n\t\t\treturn pl;\r\n\t}\r\n\treturn NULL;\r\n}\r\nstatic clusterplayer_t *MSV_FindPlayerName(char *playername)\r\n{\r\n\tlink_t *l;\r\n\tclusterplayer_t *pl;\r\n\r\n\tFOR_EACH_LINK(l, clusterplayers)\r\n\t{\r\n\t\tpl = STRUCT_FROM_LINK(l, clusterplayer_t, allplayers);\r\n\t\tif (!strcmp(pl->name, playername))\r\n\t\t\treturn pl;\r\n\t}\r\n\treturn NULL;\r\n}\r\nstatic void MSV_ServerCrashed(pubsubserver_t *server)\r\n{\r\n\tlink_t *l, *next;\r\n\tclusterplayer_t *pl;\r\n\r\n#ifdef HAVE_CLIENT\r\n\tif (server->console)\r\n\t{\r\n\t\tCon_PrintCon(server->console, \"<Server Ended>\\n\", server->console->flags);\r\n\t\tQ_snprintfz(server->console->title, sizeof(server->console->title), \"SERVER DEAD\");\r\n\t\tserver->console->userdata = NULL;\t//forget about us! for we are no more!\r\n\t}\r\n#endif\r\n\r\n\t//forget any players that are meant to be on this server.\r\n\tfor (l = clusterplayers.next ; l != &clusterplayers ; l = next)\r\n\t{\r\n\t\tnext = l->next;\r\n\t\tpl = STRUCT_FROM_LINK(l, clusterplayer_t, allplayers);\r\n\t\tif (pl->server == server)\r\n\t\t{\r\n\t\t\tCon_Printf(\"%s's node crashed out (%s)\\n\", pl->name, server->name);\r\n\t\t\tRemoveLink(&pl->allplayers);\r\n\t\t\tZ_Free(pl);\r\n\t\t}\r\n\t}\r\n\r\n\tif (server->stream)\r\n\t\tVFS_CLOSE(server->stream);\r\n\tZ_Free(server);\r\n}\r\n\r\npubsubserver_t *MSV_FindSubServer(unsigned int id)\r\n{\r\n\tpubsubserver_t *s;\r\n\tfor (s = subservers; s; s = s->next)\r\n\t{\r\n\t\tif (id == s->id)\r\n\t\t\treturn s;\r\n\t}\r\n\r\n\treturn NULL;\r\n}\r\n\r\nstatic void MSV_SendCvars(pubsubserver_t *s)\r\n{\r\n\tsizebuf_t send;\r\n\tchar send_buf[8192];\r\n\r\n#if 0\r\n\textern cvar_t skill, sv_nqplayerphysics, sv_pure, sv_minpitch, sv_maxpitch;\r\n\tcvar_t *cvars[] = {\r\n\t\t&developer,\r\n\t\t&deathmatch, &coop, &skill, &teamplay,\r\n\t\t&nomonsters, &gamecfg, &noexit, &temp1,\r\n\t\t&scratch1, &scratch2, &scratch3, &scratch4,\r\n\t\t&saved1, &saved2, &saved3, &saved4, &savedgamecfg,\r\n\t\t&sv_nqplayerphysics, &sv_pure, &sv_mintic, &sv_maxtic,\r\n\t\t&sv_minpitch, &sv_maxpitch};\r\n\tsize_t v;\r\n\r\n\tmemset(&send, 0, sizeof(send));\r\n\tsend.data = send_buf;\r\n\tsend.maxsize = sizeof(send_buf);\r\n\tfor (v = 0; v < countof(cvars); v++)\r\n\t{\r\n\t\tsend.cursize = 2;\r\n\t\tMSG_WriteByte(&send, ccmd_setcvar);\r\n\t\tMSG_WriteString(&send, cvars[v]->name);\r\n\t\tMSG_WriteString(&send, cvars[v]->string);\r\n\t\tMSV_WriteSlave(s, &send);\r\n\t}\r\n#else\r\n\textern cvar_group_t *cvar_groups;\r\n\tcvar_group_t *grp;\r\n\tcvar_t *var;\r\n\r\n\tmemset(&send, 0, sizeof(send));\r\n\tsend.data = send_buf;\r\n\tsend.maxsize = sizeof(send_buf);\r\n\r\n\tfor (grp=cvar_groups ; grp ; grp=grp->next)\r\n\t{\r\n\t\tfor (var=grp->cvars ; var ; var=var->next)\r\n\t\t{\r\n\t\t\tif (var->flags & CVAR_NOSET)\r\n\t\t\t\tcontinue;\t//don't spam errors...\r\n//\t\t\tif (var->flags & CVAR_USERCREATED)\r\n//\t\t\t\tcontinue;\r\n\r\n\t\t\tsend.cursize = 2;\r\n\t\t\tMSG_WriteByte(&send, ccmd_setcvar);\r\n\t\t\tMSG_WriteString(&send, var->name);\r\n\t\t\tMSG_WriteString(&send, var->string);\r\n\t\t\tMSV_WriteSlave(s, &send);\r\n\t\t}\r\n\t}\r\n#endif\r\n}\r\n\r\nstatic pubsubserver_t *MSV_Link_Server(vfsfile_t *stream, int id, const char *mapname)\r\n{\r\n\tpubsubserver_t *s;\r\n\tsizebuf_t send;\r\n\tchar send_buf[1024];\r\n\tif (!id)\r\n\t{\r\n\t\tdo id = ++nextserverid; while(MSV_FindSubServer(id));\r\n\t}\r\n\ts = Z_Malloc(sizeof(*s));\r\n\ts->stream = stream;\r\n\ts->id = id;\r\n\ts->next = subservers;\r\n\tsubservers = s;\r\n\r\n\tif (mapname)\r\n\t{\r\n\t\tMSV_SendCvars(s);\r\n\r\n\t\tQ_strncpyz(s->name, mapname, sizeof(s->name));\r\n\r\n\t\tmemset(&send, 0, sizeof(send));\r\n\t\tsend.data = send_buf;\r\n\t\tsend.maxsize = sizeof(send_buf);\r\n\t\tsend.cursize = 2;\r\n\t\tMSG_WriteByte(&send, ccmd_acceptserver);\r\n\t\tMSG_WriteLong(&send, s->id);\r\n\t\tMSG_WriteString(&send, s->name);\r\n\t\tMSV_WriteSlave(s, &send);\r\n\t}\r\n\treturn s;\r\n}\r\n\r\n//network code just found a new node trying to announce itself to us.\r\nqboolean MSV_NewNetworkedNode(vfsfile_t *stream, qbyte *reqstart, qbyte *buffered, size_t buffersize, const char *remoteaddr)\r\n{\r\n\tif (stream)\r\n\t{\r\n\t\tconst char *pwd = NULL;\r\n\t\tqbyte *line, *colon;\r\n\t\twhile (reqstart < buffered)\r\n\t\t{\r\n\t\t\tcolon = NULL;\r\n\t\t\tfor (line = reqstart; line < buffered && *line; line++)\r\n\t\t\t{\r\n\t\t\t\tif (*line == ':')\r\n\t\t\t\t{\r\n\t\t\t\t\tline++;\r\n\t\t\t\t\tcolon = line;\r\n\t\t\t\t\tbreak;\r\n\t\t\t\t}\r\n\t\t\t\tif (*line == '\\n')\r\n\t\t\t\t\tbreak;\r\n\t\t\t}\r\n\t\t\tfor (; line < buffered && *line; line++)\r\n\t\t\t{\r\n\t\t\t\tif (*line == '\\n')\r\n\t\t\t\t\tbreak;\r\n\t\t\t}\r\n\r\n\t\t\t*line++ = 0;\r\n\r\n\t\t\tif (colon)\r\n\t\t\t{\r\n\t\t\t\tif (colon-reqstart == 9 && !strncmp(reqstart, \"Password:\", 9))\r\n\t\t\t\t\tpwd = colon;\r\n\t\t\t}\r\n\t\t\treqstart = line;\r\n\t\t}\r\n\r\n\r\n\r\n\t\tif (sv.state == ss_clustermode)\t//only allow remote node additions if we're actually using this stuff.\r\n\t\t{\r\n\t\t\textern cvar_t rcon_password;\r\n\t\t\tCOM_ParseOut(pwd, com_token, sizeof(com_token));\r\n\t\t\tif (*rcon_password.string && !strcmp(com_token, rcon_password.string))\r\n\t\t\t{\r\n\t\t\t\tpubsubserver_t *s = MSV_Link_Server(stream, 0, \"\");\r\n\t\t\t\tif (s)\r\n\t\t\t\t{\t//and make sure we don't drop any data that was sent after the header.\r\n\t\t\t\t\tmemcpy(s->inbuffer, buffered, buffersize);\r\n\t\t\t\t\ts->inbuffersize = buffersize;\r\n\r\n\t\t\t\t\tCon_Printf(\"Server node at %s connected\\n\", remoteaddr);\r\n\t\t\t\t\treturn true;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t\tCon_Printf(\"Server node at %s rejected - bad password\\n\", remoteaddr);\r\n\t\t}\r\n\t\telse\r\n\t\t\tCon_Printf(\"Server node at %s rejected - not in cluster mode\\n\", remoteaddr);\r\n\r\n\t\tVFS_CLOSE(stream);\r\n\t}\r\n\treturn false;\r\n}\r\n\r\n\r\n//our pipes are one-way (one side writes, the other side reads).\r\nstatic vfsfile_t *msv_loop_to_ss;\r\nstatic vfsfile_t *msv_loop_from_ss;\r\n//but the master needs two-way pipes like tcp (each side can read the other's writes).\r\nstatic int QDECL MSV_Loop_Read (struct vfsfile_s *file, void *buffer, int bytestoread)\r\n{\r\n\treturn VFS_READ(msv_loop_to_ss, buffer, bytestoread);\r\n}\r\nstatic int QDECL MSV_Loop_Write (struct vfsfile_s *file, const void *buffer, int bytestowrite)\r\n{\r\n\treturn VFS_WRITE(msv_loop_to_ss, buffer, bytestowrite);\r\n}\r\nstatic qboolean QDECL MSV_Loop_Close (struct vfsfile_s *file)\r\n{\r\n\tZ_Free(file);\r\n\treturn true;\r\n}\r\nstatic pubsubserver_t *MSV_Loop_GetLocalServer(void)\r\n{\r\n\tvfsfile_t *f;\r\n\tpubsubserver_t *s = MSV_FindSubServer(svs.clusterserverid);\r\n\tif (s)\r\n\t\treturn s;\r\n\r\n\tif (!clusterplayers.next)\t//make sure we're initialised properly\r\n\t\tClearLink(&clusterplayers);\r\n\r\n\tmsv_loop_to_ss = VFSPIPE_Open(1, false);\r\n\tmsv_loop_from_ss = VFSPIPE_Open(1, false);\r\n\tf = Z_Malloc(sizeof(*f));\r\n\tf->ReadBytes = MSV_Loop_Read;\r\n\tf->WriteBytes = MSV_Loop_Write;\r\n\tf->Close = MSV_Loop_Close;\r\n\r\n\ts = MSV_Link_Server(f, 0, \"\");\r\n\tQ_strncpyz(s->name, sv.mapname, sizeof(s->name));\r\n\tsvs.clusterserverid = s->id;\r\n\treturn s;\r\n}\r\n//called at startup to let us know the control connection to read/write\r\nvoid SSV_SetupControlPipe(vfsfile_t *f, qboolean remote)\r\n{\r\n\tif (!isDedicated)\r\n\t\tSys_Error(\"Subserver in non-dedicated server?\");\r\n\tif (controlconnection)\r\n\t\tVFS_CLOSE(controlconnection);\r\n\tcontrolconnection = f;\r\n\tif (!f)\r\n\t\tisClusterSlave = CLSV_no;\r\n\telse if (remote)\r\n\t\tisClusterSlave = CLSV_remote;\r\n\telse\r\n\t\tisClusterSlave = CLSV_forked;\r\n}\r\n\r\nstatic pubsubserver_t *MSV_StartSubServer(unsigned int id, const char *mapname)\r\n{\r\n\tvfsfile_t *s = Sys_ForkServer();\r\n\r\n\tif (s)\r\n\t\treturn MSV_Link_Server(s, id, mapname);\r\n\treturn NULL;\r\n}\r\n\r\n//server names documented at the start of this file\r\nstatic pubsubserver_t *MSV_FindSubServerName(const char *servername)\r\n{\r\n\tpubsubserver_t *s;\r\n\tunsigned int id;\r\n\tqboolean forcenew = false;\r\n\tchar *mapname;\r\n\r\n\tid = strtoul(servername, &mapname, 0);\r\n\tif (*mapname == ':')\r\n\t{\r\n\t\tif (!id && servername != mapname)\r\n\t\t\tforcenew = true;\r\n\t\tmapname++;\r\n\t}\r\n\telse if (*mapname)\r\n\t{\r\n\t\tCon_Printf(\"Invalid node name (lacks colon): %s\\n\", servername);\r\n\t\tmapname = \"\";\r\n\t}\r\n\r\n\tif (id)\r\n\t{\r\n\t\ts = MSV_FindSubServer(id);\r\n\t\tif (s)\r\n\t\t\treturn s;\r\n\t}\r\n\r\n\tif (*mapname)\r\n\t{\r\n\t\tif (!forcenew)\r\n\t\t{\r\n\t\t\tfor (s = subservers; s; s = s->next)\r\n\t\t\t{\r\n\t\t\t\tif (!strcmp(s->name, mapname))\r\n\t\t\t\t\treturn s;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\treturn MSV_StartSubServer(id, mapname);\r\n\t}\r\n\treturn NULL;\r\n}\r\nqboolean MSV_AddressForServer(netadr_t *ret, int natype, pubsubserver_t *s)\r\n{\r\n\tif (s)\r\n\t{\r\n\t\tif (natype == s->addrv6.type)\r\n\t\t\t*ret = s->addrv6;\r\n\t\telse\r\n\t\t\t*ret = s->addrv4;\r\n\t\treturn true;\r\n\t}\r\n\treturn false;\r\n}\r\n\r\nqboolean MSV_InstructSlave(unsigned int id, sizebuf_t *cmd)\r\n{\r\n\tpubsubserver_t *s;\r\n\tif (!id)\r\n\t{\r\n\t\tfor (s = subservers; s; s = s->next)\r\n\t\t\tMSV_WriteSlave(s, cmd);\r\n\t\treturn subservers?true:false;\r\n\t}\r\n\telse\r\n\t{\r\n\t\ts = MSV_FindSubServer(id);\r\n\t\tif (s)\r\n\t\t{\r\n\t\t\tMSV_WriteSlave(s, cmd);\r\n\t\t\treturn true;\r\n\t\t}\r\n\t}\r\n\treturn false;\r\n}\r\n\r\nvoid SV_SetupNetworkBuffers(qboolean bigcoords);\r\n\r\nvoid MSV_MapCluster_Setup(const char *landingmap, qboolean use_database, qboolean singleplayer)\r\n{\r\n\textern cvar_t sv_playerslots;\r\n\tint plslots;\t//not really used, but affects whether we open public sockets or not.\r\n\r\n\t//this command will likely be used in configs. don't ever allow subservers to act as entire new clusters\r\n\tif (SSV_IsSubServer())\r\n\t\treturn;\r\n\r\n#ifndef SERVERONLY\r\n\tCL_Disconnect(NULL);\r\n#endif\r\n\r\n\tif (sv.state)\r\n\t\tSV_UnspawnServer();\r\n\r\n\t//child processes return 0 and fall through\r\n\tSV_WipeServerState();\r\n\r\n\t//this is the new-player map.\r\n\tQ_strncpyz(svs.name, landingmap, sizeof(svs.name));\r\n\tif (!*svs.name)\r\n\t\tQ_strncpyz(svs.name, \"start\", sizeof(svs.name));\r\n\r\n\tQ_strncpyz(sv.modelname, svs.name, sizeof(sv.modelname));\r\n\r\n\tif (use_database)\r\n\t{\r\n#ifdef SQL\r\n\t\tconst char *sqlparams[] =\r\n\t\t{\r\n\t\t\t\"\",\r\n\t\t\t\"\",\r\n\t\t\t\"\",\r\n\t\t\t\"login\",\r\n\t\t};\r\n\r\n\t\tCon_Printf(\"Opening database \\\"%s\\\"\\n\", sqlparams[3]);\r\n\t\tsv.logindatabase = SQL_NewServer(&sv, \"sqlite\", sqlparams);\r\n\t\tif (sv.logindatabase == -1)\r\n#endif\r\n\t\t{\r\n\t\t\tSV_UnspawnServer();\r\n\t\t\tCon_Printf(\"Unable to open account database\\n\");\r\n\t\t\treturn;\r\n\t\t}\r\n\t}\r\n\telse\r\n\t{\r\n\t\tsv.logindatabase = -1;\r\n\t\tCon_Printf(\"Operating in databaseless mode\\n\");\r\n\t}\r\n\tsv.state = ss_clustermode;\r\n\tClearLink(&clusterplayers);\r\n\r\n\t//and for legacy clients, we need some server stuff inited.\r\n\tSV_SetupNetworkBuffers(false);\r\n\r\n\tif (sv_playerslots.ival > 0)\r\n\t\tplslots = sv_playerslots.ival;\r\n\telse if (singleplayer)\r\n\t{\r\n\t\t/*only make one slot for single-player (ktx sucks)*/\r\n\t\tif (!isDedicated && !deathmatch.value && !coop.value)\r\n\t\t\tplslots = 1;\r\n\t\telse\r\n\t\t\tplslots = QWMAX_CLIENTS;\r\n\t}\r\n\telse\r\n\t\tplslots = QWMAX_CLIENTS;\r\n\tif (plslots > MAX_CLIENTS)\r\n\t\tplslots = MAX_CLIENTS;\r\n\tSV_UpdateMaxPlayers(plslots);\r\n\r\n\tNET_InitServer();\r\n\r\n\t//get on with it now...\r\n\tif (singleplayer && !use_database)\r\n\t\tMSV_FindSubServerName(va(\":%s\", sv.modelname));\r\n}\r\nvoid MSV_MapCluster_f(void)\r\n{\r\n\tMSV_MapCluster_Setup(Cmd_Argv(1), atoi(Cmd_Argv(2)), false);\r\n}\r\nvoid MSV_Shutdown(void)\r\n{\r\n\tsizebuf_t buf;\r\n\tchar bufmem[128];\r\n\r\n\tpubsubserver_t *s;\r\n\tfor (s = subservers; s; s = s->next)\r\n\t{\r\n\t\tbuf.data = bufmem;\r\n\t\tbuf.maxsize = sizeof(bufmem);\r\n\t\tbuf.cursize = 2;\r\n\t\tbuf.packing = SZ_RAWBYTES;\r\n\t\tMSG_WriteByte(&buf, ccmd_stuffcmd);\r\n\t\tMSG_WriteString(&buf, \"\\nquit\\n\");\r\n\t\tbuf.data[0] = buf.cursize & 0xff;\r\n\t\tbuf.data[1] = (buf.cursize>>8) & 0xff;\r\n\t\tMSV_WriteSlave(s, &buf);\r\n\r\n#ifdef HAVE_CLIENT\r\n\t\ts->killing = true;\r\n\t\tif (s->console)\r\n\t\t\tCon_Destroy(s->console);\r\n\t\ts->console = NULL;\r\n#endif\r\n\t}\r\n\r\n\twhile(subservers)\r\n\t{\r\n\t\ts = subservers;\r\n\t\tsubservers = subservers->next;\r\n\r\n\t\tMSV_ServerCrashed(s);\r\n\t}\r\n}\r\n\r\nqboolean SSV_PrintToMaster(char *s)\r\n{\r\n\tsizebuf_t send;\r\n\tchar send_buf[8192];\r\n\tstatic qboolean norecurse;\r\n\tif (!norecurse)\r\n\t{\r\n\t\tmemset(&send, 0, sizeof(send));\r\n\t\tsend.data = send_buf;\r\n\t\tsend.maxsize = sizeof(send_buf);\r\n\t\tsend.cursize = 2;\r\n\r\n\t\tMSG_WriteByte(&send, ccmd_print);\r\n\t\tMSG_WriteString(&send, s);\r\n\t\tnorecurse = true;\r\n\t\tSSV_InstructMaster(&send);\r\n\t\tnorecurse = false;\r\n\t}\r\n\treturn isClusterSlave==CLSV_forked;\t//only swallow when forked from a server. we (probably) still have our own private stdout, so use it.\r\n}\r\n\r\nvoid MSV_Status(void)\r\n{\r\n\tlink_t *l;\r\n\tchar bufmem[1024];\r\n\tpubsubserver_t *s;\r\n\tclusterplayer_t *pl;\r\n\tCon_Printf(\"Nodes:\\n\");\r\n\tfor (s = subservers; s; s = s->next)\r\n\t{\r\n\t\tCon_Printf(\" ^[\"S_COLOR_SUBSERVER\"%i: %s\\\\ssv\\\\%u^]\", s->id, *s->name?s->name:\"<NO MAP>\", s->id);\r\n\t\tif (s->addrv4.type != NA_INVALID)\r\n\t\t\tCon_Printf(S_COLOR_GRAY\" %s\", NET_AdrToString(bufmem, sizeof(bufmem), &s->addrv4));\r\n\t\tif (s->addrv6.type != NA_INVALID)\r\n\t\t\tCon_Printf(S_COLOR_GRAY\" %s\", NET_AdrToString(bufmem, sizeof(bufmem), &s->addrv6));\r\n\t\tCon_Printf(\"\\n\");\r\n\t}\r\n\r\n\tCon_Printf(\"Players:\\n\");\r\n\tFOR_EACH_LINK(l, clusterplayers)\r\n\t{\r\n\t\tpl = STRUCT_FROM_LINK(l, clusterplayer_t, allplayers);\r\n\t\tCon_Printf(\" ^[%i(%s)\\\\ssv\\\\%u^]: (%s) %s \"S_COLOR_GRAY\"(%s)\\n\", pl->playerid, pl->server->name, pl->server->id, *pl->guid?pl->guid:\"<NO GUID>\", pl->name, pl->address);\r\n\t}\r\n}\r\n\r\n#ifdef HAVE_CLIENT\r\nstatic int MSV_SubConsole_LineBuffered(console_t *con, const char *utf8line)\r\n{\r\n\tpubsubserver_t *s = con->userdata;\r\n\tif (s)\r\n\t{\r\n\t\tsizebuf_t buf;\r\n\t\tchar bufmem[65536];\r\n\r\n\t\tCon_PrintCon(con, va(\"]%s\\n\", utf8line), PFS_FORCEUTF8|PFS_NONOTIFY);\r\n\r\n\t\tif (*utf8line == '/')\r\n\t\t\tutf8line++;\t//command, not text.\r\n\r\n\t\tif (!strcmp(utf8line, \"clear\"))\r\n\t\t{\r\n\t\t\tCon_ClearCon(con);\r\n\t\t\treturn true;\r\n\t\t}\r\n\r\n\t\tbuf.data = bufmem;\r\n\t\tbuf.maxsize = sizeof(bufmem);\r\n\t\tbuf.cursize = 2;\r\n\t\tbuf.packing = SZ_RAWBYTES;\r\n\t\tMSG_WriteByte(&buf, ccmd_stuffcmd);\r\n\t\tMSG_WriteString(&buf, utf8line); //FIXME: is utf-8 a problem?\r\n\t\tbuf.data[0] = buf.cursize & 0xff;\r\n\t\tbuf.data[1] = (buf.cursize>>8) & 0xff;\r\n\t\tMSV_WriteSlave(s, &buf);\r\n\t}\r\n\telse\r\n\t\tCon_Footerf(con, false, \"< Unable to send >\");\r\n\treturn true;\r\n}\r\nstatic qboolean MSV_SubConsole_Close (console_t *con, qboolean force)\r\n{\t//force=true is the final close, the rest are merely queries to see if its save.\r\n\tpubsubserver_t *s = con->userdata;\r\n\tif (force && s)\r\n\t{\t//stop prints from this server from going here.\r\n\t\ts->console = NULL;\r\n\t}\r\n\treturn true;\r\n}\r\nstatic void MSV_SubConsole_Update(pubsubserver_t *s)\r\n{\r\n\tif (s->console)\r\n\t{\r\n\t\tif (s->console->flags & CONF_ISWINDOW)\r\n\t\t\tQ_snprintfz(s->console->title, sizeof(s->console->title), \"%u:%s\", s->id, s->name);\r\n\t\telse\r\n\t\t\tQ_snprintfz(s->console->title, sizeof(s->console->title), \"Server %u: %s\", s->id, s->name);\r\n\t}\r\n}\r\nstatic void MSV_SubConsole_Show(pubsubserver_t *s, qboolean show)\r\n{\r\n\tconsole_t *con = s->console;\r\n\tif (!con && !isDedicated)\r\n\t{\r\n\t\tfor (con = con_head; con; con = con->next)\r\n\t\t{\r\n\t\t\tif (con->close == MSV_SubConsole_Close && !con->userdata)\r\n\t\t\t\tbreak;\r\n\t\t}\r\n\t\tif (!con)\r\n\t\t{\r\n\t\t\tcon = Con_Create(NULL, CONF_NOTIFY);\r\n\t\t\tif (0)//con)\r\n\t\t\t{\r\n\t\t\t\t/*make it a console window thing*/\r\n\t\t\t\tcon->flags |= CONF_ISWINDOW;\r\n\t\t\t\tcon->wnd_x = 0;\r\n\t\t\t\tcon->wnd_y = 0;\r\n\t\t\t\tcon->wnd_w = vid.width/2;\r\n\t\t\t\tcon->wnd_h = vid.height/2;\r\n\t\t\t}\r\n\t\t}\r\n\t\tif (con)\r\n\t\t{\r\n\t\t\ts->console = con;\r\n\t\t\tMSV_SubConsole_Update(s);\r\n\r\n\t\t\tcon->parseflags = PFS_FORCEUTF8;\r\n\t\t\tcon->userdata = s;\r\n\t\t\tcon->linebuffered = MSV_SubConsole_LineBuffered;\r\n//\t\t\tcon->redirect = Con_Editor_Key;\r\n\t\t\tcon->close = MSV_SubConsole_Close;\r\n\t\t\tcon->maxlines = 0x7fffffff;\t//line limit is effectively unbounded, for a 31-bit process.\r\n\r\n\t\t\t//use the server's status command as a header.\r\n\t\t\tif (show)\r\n\t\t\t\tMSV_SubConsole_LineBuffered(con, \"status\");\r\n\t\t}\r\n\t}\r\n\tif (con && show)\r\n\t\tCon_SetActive(con);\r\n}\r\n#else\r\n\t#define MSV_SubConsole_Update(s)\r\n#endif\r\n\r\nvoid MSV_SubServerCommand_f(void)\r\n{\r\n\tsizebuf_t buf;\r\n\tchar bufmem[65536];\r\n\tpubsubserver_t *s;\r\n\tint id;\r\n\tchar *c;\r\n\tif (Cmd_Argc() == 1)\r\n\t{\r\n\t\tCon_Printf(\"Active servers on this cluster:\\n\");\r\n\t\tfor (s = subservers; s; s = s->next)\r\n\t\t{\r\n\t\t\tCon_Printf(\"^[%i: %s %i+%i\\\\ssv\\\\%u^]\", s->id, *s->name?s->name:\"<NO MAP>\", s->activeplayers, s->transferingplayers, s->id);\r\n\t\t\tif (s->addrv4.type != NA_INVALID)\r\n\t\t\t\tCon_Printf(\" %s\", NET_AdrToString(bufmem, sizeof(bufmem), &s->addrv4));\r\n\t\t\tif (s->addrv6.type != NA_INVALID)\r\n\t\t\t\tCon_Printf(\" %s\", NET_AdrToString(bufmem, sizeof(bufmem), &s->addrv6));\r\n\t\t\tCon_Printf(\"\\n\");\r\n\t\t}\r\n\t\treturn;\r\n\t}\r\n\tif (!strcmp(Cmd_Argv(0), \"ssv_all\"))\r\n\t\tid = 0;\r\n#ifdef HAVE_CLIENT\r\n\telse if (Cmd_Argc() == 2)\r\n\t{\t//subservers, meet subconsoles\r\n\t\ts = MSV_FindSubServer(atoi(Cmd_Argv(1)));\r\n\t\tif (s)\r\n\t\t\tMSV_SubConsole_Show(s, true);\r\n\t\treturn;\r\n\t}\r\n#endif\r\n\telse\r\n\t{\r\n\t\tid = atoi(Cmd_Argv(1));\r\n\t\tCmd_ShiftArgs(1, false);\r\n\t}\r\n\r\n\tbuf.data = bufmem;\r\n\tbuf.maxsize = sizeof(bufmem);\r\n\tbuf.cursize = 2;\r\n\tbuf.packing = SZ_RAWBYTES;\r\n\tc = Cmd_Args();\r\n\tMSG_WriteByte(&buf, ccmd_stuffcmd);\r\n\tMSG_WriteString(&buf, c);\r\n\tbuf.data[0] = buf.cursize & 0xff;\r\n\tbuf.data[1] = (buf.cursize>>8) & 0xff;\r\n\tif (!MSV_InstructSlave(id, &buf))\r\n\t\tCon_Printf(\"No node for index.\\n\");\r\n}\r\n\r\nvoid MSV_SendCvarChange(cvar_t *var)\r\n{\r\n\tif (var->flags & CVAR_NOSET)\r\n\t\treturn;\r\n\tif (subservers && !subservers->next && sv.state == ss_clustermode)\r\n\t{\r\n\t\tpubsubserver_t *s = subservers;\t//there is only one.\r\n\t\tsizebuf_t buf;\r\n\t\tchar bufmem[65536];\r\n\t\tbuf.data = bufmem;\r\n\t\tbuf.maxsize = sizeof(bufmem);\r\n\t\tbuf.cursize = 2;\r\n\t\tbuf.packing = SZ_RAWBYTES;\r\n\t\tMSG_WriteByte(&buf, ccmd_setcvar);\r\n\t\tMSG_WriteString(&buf, var->name);\r\n\t\tMSG_WriteString(&buf, var->string);\r\n\t\tMSV_WriteSlave(s, &buf);\r\n\t}\r\n}\r\nqboolean MSV_ForwardToAutoServer(void)\r\n{\r\n\tpubsubserver_t *s;\r\n\tif (sv.state > ss_clustermode)\r\n\t\treturn false;\t//don't forward if we have our own server.\r\n\r\n\tif (subservers && !subservers->next && sv.state == ss_clustermode)\r\n\t\ts = subservers;\t//there is only one.\r\n\telse\r\n\t{\r\n\t\ts = NULL;\r\n#ifdef HAVE_CLIENT\r\n\t\tif (!s && cls.state >= ca_connected)\r\n\t\t{\t//find the one the local player is currently on.\r\n\t\t\tfor (; s; s = s->next)\r\n\t\t\t{\r\n\t\t\t\tif (s->addrv6.type!=NA_INVALID && NET_CompareAdr(&s->addrv4, &cls.netchan.remote_address))\r\n\t\t\t\t\tbreak;\r\n\t\t\t\tif (s->addrv6.type!=NA_INVALID && NET_CompareAdr(&s->addrv6, &cls.netchan.remote_address))\r\n\t\t\t\t\tbreak;\r\n\t\t\t}\r\n\t\t}\r\n#endif\r\n\t\tif (!s)\r\n\t\t\treturn false;\r\n\t}\r\n\r\n\t{\r\n\t\tsizebuf_t buf;\r\n\t\tchar bufmem[65536];\r\n\t\tconst char *cmd = Cmd_Argv(0);\r\n\t\tconst char *args = Cmd_Args();\r\n\t\tbuf.data = bufmem;\r\n\t\tbuf.maxsize = sizeof(bufmem);\r\n\t\tbuf.cursize = 2;\r\n\t\tbuf.packing = SZ_RAWBYTES;\r\n\t\tMSG_WriteByte(&buf, ccmd_stuffcmd);\r\n\t\tSZ_Write(&buf, cmd, strlen(cmd));\r\n\t\tif(*args)\r\n\t\t\tMSG_WriteChar(&buf, ' ');\r\n\t\tMSG_WriteString(&buf, args);\r\n\t\tbuf.data[0] = buf.cursize & 0xff;\r\n\t\tbuf.data[1] = (buf.cursize>>8) & 0xff;\r\n\t\tMSV_WriteSlave(s, &buf);\r\n\t\treturn true;\r\n\t}\r\n}\r\n\r\nstatic void MSV_PrintFromSubServer(pubsubserver_t *s, const char *newtext)\r\n{\r\n\tchar *nl;\r\n#ifdef HAVE_CLIENT\r\n\tif (!s->console)\r\n\t{\r\n\t\tif (s->killing)\r\n\t\t\treturn;\r\n\t\tMSV_SubConsole_Show(s, false);\r\n\t}\r\n\tif (s->console)\r\n\t{\r\n\t\tif (*s->printtext)\r\n\t\t{\t//flush it if there was something buffered there...\r\n\t\t\tCon_PrintCon(s->console, s->printtext, s->console->flags);\r\n\t\t\t*s->printtext = 0;\r\n\t\t}\r\n\t\tCon_PrintCon(s->console, newtext, s->console->flags);\r\n\t\treturn;\r\n\t}\r\n#endif\r\n\r\n\tQ_strncatz(s->printtext, newtext, sizeof(s->printtext));\r\n\twhile((nl = strchr(s->printtext, '\\n')))\r\n\t{\t//FIXME: handle overflows.\r\n\t\t*nl++ = 0;\r\n\t\tCon_Printf(\"^[\"S_COLOR_SUBSERVER\"%i(%s)\\\\ssv\\\\%u^]: %s\\n\", s->id, s->name, s->id, s->printtext);\r\n\t\tmemmove(s->printtext, nl, strlen(nl)+1);\r\n\t}\r\n\tif (strlen(s->printtext) > sizeof(s->printtext)/2)\r\n\t{\r\n\t\tCon_Printf(\"^[\"S_COLOR_SUBSERVER\"%i(%s)\\\\ssv\\\\%u^]: %s\\n\", s->id, s->name, s->id, s->printtext);\r\n\t\t*s->printtext = 0;\r\n\t}\r\n}\r\n\r\nqboolean MSV_ReadFromSubServer(pubsubserver_t *s)\r\n{\r\n\tsizebuf_t\tsend;\r\n\tqbyte\t\tsend_buf[MAX_QWMSGLEN];\r\n\tnetadr_t adr;\r\n\tchar *str;\r\n\tint c;\r\n\tpubsubserver_t *toptr;\r\n\tclusterplayer_t *pl;\r\n\r\n\tc = MSG_ReadByte();\r\n\tswitch(c)\r\n\t{\r\n\tdefault:\r\n\tcase ccmd_bad:\r\n\t\tCon_Printf(CON_ERROR\"Corrupt message (%i) from SubServer %i:%s\\n\", c, s->id, s->name);\r\n\t\treturn false;\r\n\tcase ccmd_print:\r\n\t\tMSV_PrintFromSubServer(s, MSG_ReadString());\r\n\t\tbreak;\r\n\tcase ccmd_saveplayer:\r\n\t\t{\r\n\t\t\tfloat stats[NUM_SPAWN_PARMS];\r\n\t\t\tint i;\r\n\t\t\tunsigned char reason = MSG_ReadByte();\r\n\t\t\tunsigned int plid = MSG_ReadLong();\r\n\t\t\tint numstats = MSG_ReadByte();\r\n\t\t\tnumstats = min(numstats, NUM_SPAWN_PARMS);\r\n\t\t\tfor (i = 0; i < numstats; i++)\r\n\t\t\t\tstats[i] = MSG_ReadFloat();\r\n\r\n\t\t\tpl = MSV_FindPlayerId(plid);\r\n\t\t\tif (!pl)\r\n\t\t\t{\r\n\t\t\t\tCon_Printf(\"player %u(%s) does not exist!\\n\", plid, s->name);\r\n\t\t\t\treturn true;\t//ignore it.\r\n\t\t\t}\r\n\t\t\t//player already got taken by a different server, don't save stale data.\r\n\t\t\tif (reason && pl->server != s)\r\n\t\t\t\treturn true;\t//ignore it.\r\n\r\n\t\t\tMSV_UpdatePlayerStats(plid, s->id, numstats, stats);\r\n\r\n\t\t\tswitch (reason)\r\n\t\t\t{\r\n\t\t\tcase 0: //server reports that it accepted the player\r\n\t\t\t\tif (pl->server != s)\r\n\t\t\t\t{\r\n\t\t\t\t\tif (pl->server)\r\n\t\t\t\t\t{\t//let the previous server know\r\n\t\t\t\t\t\tsizebuf_t\tsend;\r\n\t\t\t\t\t\tqbyte\t\tsend_buf[64];\r\n\t\t\t\t\t\tmemset(&send, 0, sizeof(send));\r\n\t\t\t\t\t\tsend.data = send_buf;\r\n\t\t\t\t\t\tsend.maxsize = sizeof(send_buf);\r\n\t\t\t\t\t\tsend.cursize = 2;\r\n\t\t\t\t\t\tMSG_WriteByte(&send, ccmd_transferedplayer);\r\n\t\t\t\t\t\tMSG_WriteLong(&send, s->id);\r\n\t\t\t\t\t\tMSG_WriteLong(&send, plid);\r\n\t\t\t\t\t\tMSV_WriteSlave(pl->server, &send);\r\n\t\t\t\t\t\tpl->server->activeplayers--;\r\n\t\t\t\t\t}\r\n\t\t\t\t\tpl->server = s;\r\n\t\t\t\t\tpl->server->activeplayers++;\r\n\t\t\t\t}\r\n\t\t\t\tbreak;\r\n\t\t\tcase 1:\r\n\t\t\t\t//belongs to another node now, (but we might not have had the other node's response yet)\r\n\t\t\t\tif (pl->server == s)\r\n\t\t\t\t{\r\n\t\t\t\t\ts->activeplayers--;\r\n\t\t\t\t\tpl->server = NULL;\r\n\t\t\t\t}\r\n\t\t\t\tbreak;\r\n\t\t\tcase 2:\t//drop\r\n\t\t\tcase 3: //transfer abort\r\n\t\t\t\tif (pl->server == s)\r\n\t\t\t\t{\r\n\t\t\t\t\tpl->server->activeplayers--;\r\n\t\t\t\t\tCon_Printf(\"%s(%s) dropped\\n\", pl->name, s->name);\r\n\t\t\t\t\tRemoveLink(&pl->allplayers);\r\n\t\t\t\t\tZ_Free(pl);\r\n\t\t\t\t}\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\t\t}\r\n\t\tbreak;\r\n\tcase ccmd_foundplayer:\r\n\t\t{\r\n\t\t\tchar guid[64];\r\n\t\t\tchar plnamebuf[64];\r\n\t\t\tchar *plname = MSG_ReadStringBuffer(plnamebuf, sizeof(plnamebuf));\r\n\t\t\tchar *claddr = MSG_ReadString();\r\n\t\t\tchar *clguid = MSG_ReadStringBuffer(guid, sizeof(guid));\r\n\t\t\textern int\tnextuserid;\r\n\t\t\tconst unsigned int statsblobsize = 0;\r\n\t\t\tconst void *statsblob = NULL;\r\n\r\n\t\t\tsizebuf_t\tsend;\r\n\t\t\tqbyte\t\tsend_buf[MAX_QWMSGLEN];\r\n\t\t\tclusterplayer_t *pl;\r\n\r\n\t\t\tif (sv.logindatabase)\r\n\t\t\t\tbreak;\t//if we're using a login database then this could be used as an exploit.\r\n\r\n\t\t\tmemset(&send, 0, sizeof(send));\r\n\t\t\tsend.data = send_buf;\r\n\t\t\tsend.maxsize = sizeof(send_buf);\r\n\t\t\tsend.cursize = 2;\r\n\r\n\t\t\tpl = Z_Malloc(sizeof(*pl));\r\n\t\t\tQ_strncpyz(pl->name, plname, sizeof(pl->name));\r\n\t\t\tQ_strncpyz(pl->guid, clguid, sizeof(pl->guid));\r\n\t\t\tQ_strncpyz(pl->address, claddr, sizeof(pl->address));\r\n\t\t\tpl->playerid = ++nextuserid;\r\n\t\t\tInsertLinkBefore(&pl->allplayers, &clusterplayers);\r\n\t\t\tpl->server = s;\r\n\t\t\ts->activeplayers++;\r\n\r\n\t\t\tMSG_WriteByte(&send, ccmd_takeplayer);\r\n\t\t\tMSG_WriteLong(&send, pl->playerid);\r\n\t\t\tMSG_WriteString(&send, pl->name);\r\n\t\t\tMSG_WriteLong(&send, 0);\t//from server\r\n\t\t\tMSG_WriteString(&send, pl->address);\r\n\t\t\tMSG_WriteString(&send, pl->guid);\r\n\r\n\t\t\tMSG_WriteByte(&send, statsblobsize/4);\r\n\t\t\tSZ_Write(&send, statsblob, statsblobsize&~3);\r\n\t\t\tMSV_WriteSlave(s, &send);\r\n\t\t}\r\n\t\tbreak;\r\n\r\n\tcase ccmd_transferplayer:\r\n\t\t{\t//server is offering a player to another server\r\n\t\t\tchar guid[64];\r\n\t\t\tchar mapname[64];\r\n\t\t\tchar plnamebuf[64];\r\n\t\t\tint plid = MSG_ReadLong();\r\n\t\t\tchar *plname = MSG_ReadStringBuffer(plnamebuf, sizeof(plnamebuf));\r\n\t\t\tchar *newmap = MSG_ReadStringBuffer(mapname, sizeof(mapname));\r\n\t\t\tchar *claddr = MSG_ReadString();\r\n\t\t\tchar *clguid = MSG_ReadStringBuffer(guid, sizeof(guid));\r\n\r\n\t\t\tmemset(&send, 0, sizeof(send));\r\n\t\t\tsend.data = send_buf;\r\n\t\t\tsend.maxsize = sizeof(send_buf);\r\n\t\t\tsend.cursize = 2;\r\n\r\n\t\t\tif (NULL!=(toptr=MSV_FindSubServerName(newmap)) && s != toptr)\r\n\t\t\t{\r\n//\t\t\t\tCon_Printf(\"Transfer to %i:%s\\n\", toptr->id, toptr->name);\r\n\r\n\t\t\t\tMSG_WriteByte(&send, ccmd_takeplayer);\r\n\t\t\t\tMSG_WriteLong(&send, plid);\r\n\t\t\t\tMSG_WriteString(&send, plname);\r\n\t\t\t\tMSG_WriteLong(&send, s->id);\r\n\t\t\t\tMSG_WriteString(&send, claddr);\r\n\t\t\t\tMSG_WriteString(&send, clguid);\r\n\r\n\t\t\t\tc = MSG_ReadByte();\r\n\t\t\t\tMSG_WriteByte(&send, c);\r\n//\t\t\t\tCon_Printf(\"Transfer %i stats\\n\", c);\r\n\t\t\t\twhile(c--)\r\n\t\t\t\t\tMSG_WriteFloat(&send, MSG_ReadFloat());\r\n\r\n\t\t\t\tMSV_WriteSlave(toptr, &send);\r\n\r\n\t\t\t\ts->transferingplayers--;\r\n\t\t\t\ttoptr->transferingplayers++;\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\t//suck up the stats\r\n\t\t\t\tc = MSG_ReadByte();\r\n\t\t\t\twhile(c--)\r\n\t\t\t\t\tMSG_ReadFloat();\r\n\r\n//\t\t\t\tCon_Printf(\"Transfer abort\\n\");\r\n\r\n\t\t\t\tMSG_WriteByte(&send, ccmd_tookplayer);\r\n\t\t\t\tMSG_WriteLong(&send, s->id);\r\n\t\t\t\tMSG_WriteLong(&send, plid);\r\n\t\t\t\tMSG_WriteString(&send, \"\");\r\n\r\n\t\t\t\tMSV_WriteSlave(s, &send);\r\n\t\t\t}\r\n\t\t}\r\n\t\tbreak;\r\n\tcase ccmd_tookplayer:\r\n\t\t{\t//server has space, and wants the client.\r\n\t\t\tint to = MSG_ReadLong();\r\n\t\t\tint plid = MSG_ReadLong();\r\n\t\t\tchar *claddr = MSG_ReadString();\r\n\t\t\tchar *rmsg;\r\n\t\t\tnetadr_t cladr;\r\n\t\t\tnetadr_t svadr;\r\n\t\t\tchar adrbuf[256];\r\n\r\n//\t\t\tCon_Printf(\"Took player\\n\");\r\n\r\n\t\t\tmemset(&send, 0, sizeof(send));\r\n\t\t\tsend.data = send_buf;\r\n\t\t\tsend.maxsize = sizeof(send_buf);\r\n\t\t\tsend.cursize = 2;\r\n\r\n\t\t\tNET_StringToAdr(claddr, 0, &cladr);\r\n\t\t\tMSV_AddressForServer(&svadr, cladr.type, s);\r\n\t\t\tif (!to)\r\n\t\t\t{\r\n\t\t\t\tif (svadr.type != NA_INVALID)\r\n\t\t\t\t{\t//the client was trying to connect to the cluster master.\r\n\t\t\t\t\trmsg = va(\"fredir\\n%s\", NET_AdrToString(adrbuf, sizeof(adrbuf), &svadr));\r\n\t\t\t\t\tNetchan_OutOfBand (NCF_SERVER, &cladr, strlen(rmsg), (qbyte *)rmsg);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tMSG_WriteByte(&send, ccmd_tookplayer);\r\n\t\t\t\tMSG_WriteLong(&send, s->id);\r\n\t\t\t\tMSG_WriteLong(&send, plid);\r\n\t\t\t\tMSG_WriteString(&send, NET_AdrToString(adrbuf, sizeof(adrbuf), &svadr));\r\n\r\n\t\t\t\ttoptr = MSV_FindSubServer(to);\r\n\t\t\t\tif (toptr)\r\n\t\t\t\t{\r\n\t\t\t\t\tMSV_WriteSlave(toptr, &send);\r\n\t\t\t\t\ttoptr->transferingplayers++;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\ts->transferingplayers--;\r\n\t\t}\r\n\t\tbreak;\r\n\tcase ccmd_serveraddress:\r\n\t\t{\r\n\t\t\tenum addressscope_e v4class=ASCOPE_PROCESS, v6class=ASCOPE_PROCESS, nclass;\r\n\t\t\ts->addrv4.type = NA_INVALID;\r\n\t\t\ts->addrv6.type = NA_INVALID;\r\n\t\t\tstr = MSG_ReadString();\r\n\t\t\tQ_strncpyz(s->name, str, sizeof(s->name));\r\n\t\t\tfor (;;)\r\n\t\t\t{\r\n\t\t\t\tstr = MSG_ReadString();\r\n\t\t\t\tif (!*str)\r\n\t\t\t\t\tbreak;\r\n\t\t\t\tif (NET_StringToAdr(str, 0, &adr))\r\n\t\t\t\t{\r\n\t\t\t\t\tnclass = NET_ClassifyAddress(&adr, NULL);\r\n\r\n\t\t\t\t\t//for each address type, pick the network address with the widest scope. hopefully an internet routable one rather than a lan address. I hope your router forwards properly.\r\n\t\t\t\t\tif (adr.type == NA_IP && nclass > v4class)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\ts->addrv4 = adr;\r\n\t\t\t\t\t\tv4class = nclass;\r\n\t\t\t\t\t}\r\n\t\t\t\t\tif (adr.type == NA_IPV6 && nclass > v6class)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\ts->addrv6 = adr;\r\n\t\t\t\t\t\tv6class = nclass;\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\tMSV_SubConsole_Update(s);\r\n\t\t\tif (s->started)\r\n\t\t\t\tCon_DPrintf(\"^[\"S_COLOR_SUBSERVER\"[%i:%s: map changed]\\\\ssv\\\\%u\\\\tip\\\\Click for server's console^]\\n\", s->id, s->name, s->id);\r\n\t\t\telse\r\n\t\t\t\tCon_Printf(\"^[\"S_COLOR_SUBSERVER\"[%i:%s: new node initialised]\\\\ssv\\\\%u\\\\tip\\\\Click for server's console^]\\n\", s->id, s->name, s->id);\r\n\t\t\ts->started = true;\r\n\t\t}\r\n\t\tbreak;\r\n\tcase ccmd_stringcmd:\r\n\t\t{\r\n\t\t\tchar dest[1024];\r\n\t\t\tchar from[1024];\r\n\t\t\tchar cmd[1024];\r\n\t\t\tchar info[1024];\r\n\t\t\tMSG_ReadStringBuffer(dest, sizeof(dest));\r\n\t\t\tMSG_ReadStringBuffer(from, sizeof(from));\r\n\t\t\tMSG_ReadStringBuffer(cmd, sizeof(cmd));\r\n\t\t\tMSG_ReadStringBuffer(info, sizeof(info));\r\n\r\n\t\t\tmemset(&send, 0, sizeof(send));\r\n\t\t\tsend.data = send_buf;\r\n\t\t\tsend.maxsize = sizeof(send_buf);\r\n\t\t\tsend.cursize = 2;\r\n\r\n\t\t\tMSG_WriteByte(&send, ccmd_stringcmd);\r\n\t\t\tMSG_WriteString(&send, dest);\r\n\t\t\tMSG_WriteString(&send, from);\r\n\t\t\tMSG_WriteString(&send, cmd);\r\n\t\t\tMSG_WriteString(&send, info);\r\n\r\n\t\t\tif (!*dest)\t//broadcast if no dest\r\n\t\t\t{\r\n\t\t\t\tfor (s = subservers; s; s = s->next)\r\n\t\t\t\t\tMSV_WriteSlave(s, &send);\r\n\t\t\t}\r\n\t\t\telse if (*dest == '\\\\')\r\n\t\t\t{\r\n\t\t\t\t//send to a specific server (backslashes should not be valid in infostrings, and thus not in names.\r\n\t\t\t\t//FIXME: broadcasting for now.\r\n\t\t\t\tfor (s = subservers; s; s = s->next)\r\n\t\t\t\t\tMSV_WriteSlave(s, &send);\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\t//send it to the server that the player is currently on.\r\n\t\t\t\tclusterplayer_t *pl = MSV_FindPlayerName(dest);\r\n\t\t\t\tif (pl)\r\n\t\t\t\t\tMSV_WriteSlave(pl->server, &send);\r\n\t\t\t\telse if (!pl && strncmp(cmd, \"error:\", 6))\r\n\t\t\t\t{\r\n\t\t\t\t\t//player not found. send it back to the sender, but add an error prefix.\r\n\t\t\t\t\tsend.cursize = 2;\r\n\t\t\t\t\tMSG_WriteByte(&send, ccmd_stringcmd);\r\n\t\t\t\t\tMSG_WriteString(&send, from);\r\n\t\t\t\t\tMSG_WriteString(&send, dest);\r\n\t\t\t\t\tSZ_Write(&send, \"error:\", 6);\r\n\t\t\t\t\tMSG_WriteString(&send, cmd);\r\n\t\t\t\t\tMSG_WriteString(&send, info);\r\n\t\t\t\t\tMSV_WriteSlave(s, &send);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t\tbreak;\r\n\t}\r\n\tif (MSG_GetReadCount() != net_message.cursize || msg_badread)\r\n\t{\r\n\t\tCon_Printf(CON_ERROR\"Master(%i): Readcount isn't right (%i)\\n\", s->id, net_message.data[0]);\r\n\t\treturn false;\r\n\t}\r\n\treturn true;\r\n}\r\n\r\nvoid MSV_PollSlaves(void)\r\n{\r\n\tpubsubserver_t **link, *s;\r\n\r\n\tif (controlconnection)\r\n\t{\r\n\t\tstatic unsigned inbuffersize;\r\n\t\tstatic qbyte inbuffer[8192];\r\n\t\tint error = NETERR_SENT;\r\n\r\n\t\tfor (;;)\r\n\t\t{\r\n\t\t\tif (inbuffersize < 2)\r\n\t\t\t{\r\n\t\t\t\tint r = VFS_READ(controlconnection, inbuffer+inbuffersize, 2-inbuffersize);\r\n\t\t\t\tif (r < 0)\r\n\t\t\t\t\terror = r;\r\n\t\t\t\telse\r\n\t\t\t\t\tinbuffersize += r;\r\n\t\t\t}\r\n\t\t\tif (inbuffersize >= 2)\r\n\t\t\t{\r\n\t\t\t\tsize_t size = inbuffer[0] | ((unsigned short)inbuffer[1]<<8);\r\n\t\t\t\tint r;\r\n\t\t\t\tif (size > sizeof(inbuffer) || size >= sizeof(net_message_buffer))\r\n\t\t\t\t\tbreak;\t//error...\r\n\t\t\t\tif (size > inbuffersize)\r\n\t\t\t\t{\r\n\t\t\t\t\tr = VFS_READ(controlconnection, inbuffer+inbuffersize, size-inbuffersize);\r\n\t\t\t\t\tif (r < 0)\r\n\t\t\t\t\t\terror = r;\r\n\t\t\t\t\telse\r\n\t\t\t\t\t\tinbuffersize += r;\r\n\t\t\t\t}\r\n\t\t\t\tif (inbuffersize < size)\r\n\t\t\t\t\tbreak;\t//not complete yet.\r\n\r\n\t\t\t\tnet_message.cursize = size-2;\r\n\t\t\t\tmemcpy(net_message.data, inbuffer+2, net_message.cursize);\r\n\t\t\t\tmemmove(inbuffer, inbuffer+size, inbuffersize-size);\r\n\t\t\t\tinbuffersize -= size;\r\n\r\n\t\t\t\tMSG_BeginReading (&net_message, msg_nullnetprim);\r\n\t\t\t\tSSV_ReadFromControlServer();\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t\tbreak;\r\n\t\t}\r\n\t\tif (error)\r\n\t\t{\r\n\t\t\terror = isClusterSlave;\r\n\t\t\tSSV_SetupControlPipe(NULL, false);\r\n\t\t\tinbuffersize = 0;\r\n\r\n\t\t\tif (error)\r\n\t\t\t{\r\n\t\t\t\tif (error == NETERR_NOROUTE)\r\n\t\t\t\t\tSV_FinalMessage(\"No rotue to cluster controller\\n\");\r\n\t\t\t\telse if (error == NETERR_DISCONNECTED)\r\n\t\t\t\t\tSV_FinalMessage(\"Lost connection to cluster controller\\n\");\r\n\t\t\t\telse if (error == NETERR_DISCONNECTED)\r\n\t\t\t\t\tSV_FinalMessage(\"MTU error from cluster controller\\n\");\r\n\t\t\t\telse if (error == NETERR_CLOGGED)\r\n\t\t\t\t\tSV_FinalMessage(\"Conjestion error from cluster controller\\n\");\r\n\t\t\t\telse\r\n\t\t\t\t\tSV_FinalMessage(\"Unknown cluster controller connection error\\n\");\r\n\t\t\t\tCmd_ExecuteString(\"quit\\n\", RESTRICT_LOCAL);\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\telse if (msv_loop_to_ss)\r\n\t{\r\n\t\tunsigned short size;\r\n\t\twhile (VFS_READ(msv_loop_to_ss, &size, sizeof(size))>0)\r\n\t\t{\r\n\t\t\tVFS_READ(msv_loop_to_ss, net_message.data, size);\r\n\t\t\tnet_message.cursize = size-2;\r\n\t\t\tMSG_BeginReading (&net_message, msg_nullnetprim);\r\n\t\t\tSSV_ReadFromControlServer();\r\n\t\t}\r\n\t}\r\n\r\n\tfor (link = &subservers; (s=*link); )\r\n\t{\r\n\t\tint avail = MSV_SubServerRead(s);\r\n\t\tif (!avail)\r\n\t\t\tlink = &s->next;\t//no messages, move on to the next.\r\n\t\telse if (avail < 0 || !MSV_ReadFromSubServer(s))\r\n\t\t{\r\n\t\t\t//error - server is dead and needs to be freed.\r\n\t\t\t*link = s->next;\r\n\t\t\tMSV_ServerCrashed(s);\r\n\t\t}\r\n\t\t//else read something, there may be more pending.\r\n\t}\r\n}\r\n\r\nvoid SSV_InstructMaster(sizebuf_t *cmd)\r\n{\r\n\tcmd->data[0] = cmd->cursize & 0xff;\r\n\tcmd->data[1] = (cmd->cursize>>8) & 0xff;\r\n\tif (msv_loop_from_ss)\r\n\t\tVFS_WRITE(msv_loop_from_ss, cmd->data, cmd->cursize);\r\n\telse if (controlconnection)\r\n\t\tVFS_WRITE(controlconnection, cmd->data, cmd->cursize);\r\n\r\n\t//FIXME: handle partial writes.\r\n}\r\n\r\nvoid SSV_ReadFromControlServer(void)\r\n{\r\n\tint c;\r\n\tchar *s;\r\n\r\n\tc = MSG_ReadByte();\r\n\tswitch(c)\r\n\t{\r\n\tcase ccmd_bad:\r\n\tdefault:\r\n\t\tSV_Error(\"Invalid message from cluster (%i)\\n\", c);\r\n\t\tbreak;\r\n\r\n\t//console command (entered via the cluster host, either broadcast or uni)\r\n\tcase ccmd_stuffcmd:\r\n\t\ts = MSG_ReadString();\r\n\t\tSV_BeginRedirect(RD_MASTER, 0);\r\n\t\tCmd_ExecuteString(s, RESTRICT_LOCAL);\r\n\t\tSV_EndRedirect();\r\n\t\tbreak;\r\n\r\n\tcase ccmd_setcvar:\r\n\t\t{\r\n\t\t\tchar cvarname[256];\r\n\t\t\tcvar_t *var = Cvar_FindVar(MSG_ReadStringBuffer(cvarname,sizeof(cvarname)));\r\n\t\t\tconst char *val = MSG_ReadString();\r\n\t\t\tif (var)\r\n\t\t\t\tCon_DPrintf(\"Setting cvar \\\"%s\\\" to \\\"%s\\\"\\n\", var->name, val);\r\n\t\t\telse\r\n\t\t\t\tCon_DPrintf(\"Ignoring undefined cvar \\\"%s\\\", which would be set to \\\"%s\\\"\\n\", cvarname, val);\r\n\t\t\tCvar_Set(var, val);\r\n\t\t}\r\n\t\tbreak;\r\n\r\n\t//cluster has 'accepted' us as an allowed server. this is where it tells us who we're meant to be, which needs to be set up ready for the players that are (probably) about to join us\r\n\tcase ccmd_acceptserver:\r\n\t\tsvs.clusterserverid\t= MSG_ReadLong();\r\n\t\ts = MSG_ReadString();\r\n\t\tif (*s && !strchr(s, ';') && !strchr(s, '\\n') && !strchr(s, '\\\"'))\t//sanity check the argument\r\n\t\t\tCmd_ExecuteString(va(\"map \\\"%s\\\"\", s), RESTRICT_LOCAL);\r\n\t\tif (svprogfuncs && pr_global_ptrs->serverid)\r\n\t\t\t*pr_global_ptrs->serverid = svs.clusterserverid;\r\n\t\tbreak;\r\n\r\n\t//another server wants us to reserve a player slot for an inbound player.\r\n\tcase ccmd_takeplayer:\r\n\t\t{\r\n\t\t\tclient_t *cl = NULL;\r\n\t\t\tint i, j;\r\n\t\t\tfloat stat;\r\n\t\t\tchar guid[64], name[64];\r\n\t\t\tint plid = MSG_ReadLong();\r\n\t\t\tchar *plname = MSG_ReadStringBuffer(name, sizeof(name));\r\n\t\t\tint fromsv = MSG_ReadLong();\r\n\t\t\tchar *claddr = MSG_ReadString();\r\n\t\t\tchar *clguid = MSG_ReadStringBuffer(guid, sizeof(guid));\r\n\r\n\t\t\tif (sv.state >= ss_active)\r\n\t\t\t{\r\n\t\t\t\tfor (i = 0; i < svs.allocated_client_slots; i++)\r\n\t\t\t\t{\r\n\t\t\t\t\tif (!svs.clients[i].state || (svs.clients[i].userid == plid && svs.clients[i].state >= cs_loadzombie))\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tcl = &svs.clients[i];\r\n\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n//\t\t\tCon_Printf(\"%s: takeplayer\\n\", sv.name);\r\n\t\t\tif (cl)\r\n\t\t\t{\r\n\t\t\t\tcl->userid = plid;\r\n\t\t\t\tif (cl->state == cs_loadzombie && cl->istobeloaded)\r\n\t\t\t\t\tcl->connection_started = realtime+20;\t//renew the slot\r\n\t\t\t\telse if (!cl->state)\r\n\t\t\t\t{\t//allocate a new pending player.\r\n\t\t\t\t\tcl->state = cs_loadzombie;\r\n\t\t\t\t\tcl->connection_started = realtime+20;\r\n\t\t\t\t\tQ_strncpyz(cl->guid, clguid, sizeof(cl->guid));\r\n\t\t\t\t\tQ_strncpyz(cl->namebuf, plname, sizeof(cl->namebuf));\r\n\t\t\t\t\tcl->name = cl->namebuf;\r\n\t\t\t\t\tmemset(&cl->netchan, 0, sizeof(cl->netchan));\r\n\t\t\t\t\tSV_GetNewSpawnParms(cl);\r\n\t\t\t\t}\r\n\t\t\t\t//else: already on the server somehow. laggy/dupe request? must be.\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tCon_Printf(\"%s: server full!\\n\", svs.name);\r\n\t\t\t}\r\n\r\n\t\t\tj = MSG_ReadByte();\r\n//\t\t\tCon_Printf(\"%s: %i stats\\n\", sv.name, j);\r\n\t\t\tfor (i = 0; i < j; i++)\r\n\t\t\t{\r\n\t\t\t\tstat = MSG_ReadFloat();\r\n\t\t\t\tif (cl && cl->state == cs_loadzombie && i < NUM_SPAWN_PARMS)\r\n\t\t\t\t\tcl->spawn_parms[i] = stat;\r\n\t\t\t}\r\n\r\n\t\t\t{\r\n\t\t\t\tsizebuf_t\tsend;\r\n\t\t\t\tqbyte\t\tsend_buf[MAX_QWMSGLEN];\r\n\r\n\t\t\t\tmemset(&send, 0, sizeof(send));\r\n\t\t\t\tsend.data = send_buf;\r\n\t\t\t\tsend.maxsize = sizeof(send_buf);\r\n\t\t\t\tsend.cursize = 2;\r\n\r\n\t\t\t\tif (cl)\r\n\t\t\t\t{\r\n\t\t\t\t\tMSG_WriteByte(&send, ccmd_tookplayer);\r\n\t\t\t\t\tMSG_WriteLong(&send, fromsv);\r\n\t\t\t\t\tMSG_WriteLong(&send, plid);\r\n\t\t\t\t\tMSG_WriteString(&send, claddr);\r\n\t\t\t\t\tSSV_InstructMaster(&send);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t\tbreak;\r\n\r\n\t//a server has acknowledged a transfer request, and we now know where to actually send them to.\r\n\tcase ccmd_tookplayer:\r\n\t\t{\r\n\t\t\tclient_t *cl = NULL;\r\n\t\t\tint to = MSG_ReadLong();\r\n\t\t\tint plid = MSG_ReadLong();\r\n\t\t\tchar *addr = MSG_ReadString();\r\n\t\t\tint i;\r\n\r\n\t\t\t(void)to;\r\n\r\n\t\t\tCon_Printf(\"%s: got tookplayer\\n\", sv.modelname);\r\n\r\n\t\t\tfor (i = 0; i < svs.allocated_client_slots; i++)\r\n\t\t\t{\r\n\t\t\t\tif (svs.clients[i].state && svs.clients[i].userid == plid)\r\n\t\t\t\t{\r\n\t\t\t\t\tcl = &svs.clients[i];\r\n\t\t\t\t\tbreak;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\tif (cl)\r\n\t\t\t{\r\n\t\t\t\tif (!*addr)\r\n\t\t\t\t{\r\n\t\t\t\t\tCon_Printf(\"%s: tookplayer: failed\\n\", sv.modelname);\r\n\t\t\t\t\tZ_Free(cl->transfer);\r\n\t\t\t\t\tcl->transfer = NULL;\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\tCon_Printf(\"%s: tookplayer: do transfer\\n\", sv.modelname);\r\n//\t\t\t\t\tSV_StuffcmdToClient(cl, va(\"connect \\\"%s\\\"\\n\", addr));\r\n\t\t\t\t\tSV_StuffcmdToClient(cl, va(\"cl_transfer \\\"%s\\\"\\n\", addr));\r\n\t\t\t\t\tcl->redirect = 2;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t\tCon_Printf(\"%s: tookplayer: invalid player.\\n\", sv.modelname);\r\n\t\t}\r\n\t\tbreak;\r\n\r\n\t//another server has successfully taken a player from us (100% complete). we can drop the player now, they're not going to respond to us anyway.\r\n\tcase ccmd_transferedplayer:\r\n\t\t{\r\n\t\t\tclient_t *cl;\r\n\t\t\tint toserver = MSG_ReadLong();\r\n\t\t\tint playerid = MSG_ReadLong();\r\n\t\t\tint i;\r\n\r\n\t\t\t(void)toserver;\r\n\r\n\t\t\tfor (i = 0; i < svs.allocated_client_slots; i++)\r\n\t\t\t{\r\n\t\t\t\tif (svs.clients[i].userid == playerid && svs.clients[i].state >= cs_loadzombie)\r\n\t\t\t\t{\r\n\t\t\t\t\tcl = &svs.clients[i];\r\n\t\t\t\t\tcl->drop = true;\r\n\t\t\t\t\tCon_Printf(\"%s transfered to %s\\n\", cl->name, cl->transfer);\r\n\t\t\t\t\tbreak;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t\tbreak;\r\n\r\n\t//qc-based string command sent from gamecode of node to the gamecode of another.\r\n\tcase ccmd_stringcmd:\r\n\t\t{\r\n\t\t\tchar dest[1024];\r\n\t\t\tchar from[1024];\r\n\t\t\tchar cmd[1024];\r\n\t\t\tchar info[1024];\r\n\t\t\tint i;\r\n\t\t\tclient_t *cl;\r\n\t\t\tMSG_ReadStringBuffer(dest, sizeof(dest));\r\n\t\t\tMSG_ReadStringBuffer(from, sizeof(from));\r\n\t\t\tMSG_ReadStringBuffer(cmd, sizeof(cmd));\r\n\t\t\tMSG_ReadStringBuffer(info, sizeof(info));\r\n\r\n\t\t\tif (!PR_ParseClusterEvent(dest, from, cmd, info))\r\n\t\t\t{\r\n\t\t\t\t//meh, lets make some lame fallback thing\r\n\t\t\t\tfor (i = 0; i < sv.allocated_client_slots; i++)\r\n\t\t\t\t{\r\n\t\t\t\t\tcl = &svs.clients[i];\r\n\t\t\t\t\tif (cl->state >= cs_connected)\r\n\t\t\t\t\tif (!*dest || !strcmp(dest, cl->name))\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tif (!strcmp(cmd, \"say\"))\r\n\t\t\t\t\t\t\tSV_PrintToClient(cl, PRINT_HIGH, va(\"^[%s^]: %s\\n\", from, info));\r\n\t\t\t\t\t\telse if (!strcmp(cmd, \"join\"))\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tSV_PrintToClient(cl, PRINT_HIGH, va(\"^[%s^] is joining you\\n\", from));\r\n\t\t\t\t\t\t\tSSV_Send(from, cl->name, \"joinnode\", va(\"%i\", svs.clusterserverid));\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\telse if (!strcmp(cmd, \"joinnode\"))\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tSSV_InitiatePlayerTransfer(cl, info);\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\telse\r\n\t\t\t\t\t\t\tSV_PrintToClient(cl, PRINT_HIGH, va(\"%s from [%s]: %s\\n\", cmd, from, info));\r\n\t\t\t\t\t\tif (*dest)\r\n\t\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t\tbreak;\r\n\t}\r\n\r\n\tif (MSG_GetReadCount() != net_message.cursize || msg_badread)\r\n\t\tSys_Error(\"Subserver: Readcount isn't right (%i)\\n\", net_message.data[0]);\r\n}\r\n\r\nvoid SSV_UpdateAddresses(void)\r\n{\r\n\tchar\t\tbuf[256];\r\n\tnetadr_t\taddr[64];\r\n\tstruct ftenet_generic_connection_s\t\t\t*con[sizeof(addr)/sizeof(addr[0])];\r\n\tunsigned int\tflags[sizeof(addr)/sizeof(addr[0])];\r\n\tconst char *params[sizeof(addr)/sizeof(addr[0])];\r\n\tint\t\t\tcount;\r\n\tsizebuf_t\tsend;\r\n\tqbyte\t\tsend_buf[MAX_QWMSGLEN];\r\n\tint i;\r\n\r\n\tif (!SSV_IsSubServer() && !msv_loop_from_ss)\r\n\t\treturn;\r\n\r\n\tcount = NET_EnumerateAddresses(svs.sockets, con, flags, addr, params, sizeof(addr)/sizeof(addr[0]));\r\n\r\n\tif (*sv_serverip.string)\r\n\t{\r\n\t\tfor (i = 0; i < count; i++)\r\n\t\t{\r\n\t\t\tif (addr[i].type == NA_IP)\r\n\t\t\t{\r\n\t\t\t\tNET_StringToAdr2(sv_serverip.string, BigShort(addr[i].port), &addr[0], sizeof(addr)/sizeof(addr[0]), NULL);\r\n\t\t\t\tcount = 1;\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\tmemset(&send, 0, sizeof(send));\r\n\tsend.data = send_buf;\r\n\tsend.maxsize = sizeof(send_buf);\r\n\tsend.cursize = 2;\r\n\tMSG_WriteByte(&send, ccmd_serveraddress);\r\n\r\n\tMSG_WriteString(&send, svs.name);\r\n\tfor (i = 0; i < count; i++)\r\n\t\tMSG_WriteString(&send, NET_AdrToString(buf, sizeof(buf), &addr[i]));\r\n\tMSG_WriteByte(&send, 0);\r\n\tSSV_InstructMaster(&send);\r\n}\r\n\r\nvoid SSV_SavePlayerStats(client_t *cl, int reason)\r\n{\r\n\tsizebuf_t\tsend;\r\n\tqbyte\t\tsend_buf[MAX_QWMSGLEN];\r\n\tint i;\r\n\tif (!SSV_IsSubServer())\r\n\t\treturn;\r\n\r\n\tif ((reason == 1 || reason == 2) && cl->edict)\r\n\t\tSV_SaveSpawnparmsClient(cl, NULL);\r\n\r\n\tmemset(&send, 0, sizeof(send));\r\n\tsend.data = send_buf;\r\n\tsend.maxsize = sizeof(send_buf);\r\n\tsend.cursize = 2;\r\n\r\n\tMSG_WriteByte(&send, ccmd_saveplayer);\r\n\tMSG_WriteByte(&send, reason);\r\n\tMSG_WriteLong(&send, cl->userid);\r\n\tMSG_WriteByte(&send, NUM_SPAWN_PARMS);\r\n\tfor (i = 0; i < NUM_SPAWN_PARMS; i++)\r\n\t{\r\n\t\tMSG_WriteFloat(&send, cl->spawn_parms[i]);\r\n\t}\r\n\r\n\tSSV_InstructMaster(&send);\r\n}\r\nvoid SSV_Send(const char *dest, const char *src, const char *cmd, const char *msg)\r\n{\r\n\tsizebuf_t\tsend;\r\n\tqbyte\t\tsend_buf[MAX_QWMSGLEN];\r\n\tif (!SSV_IsSubServer())\r\n\t\treturn;\r\n\r\n\tmemset(&send, 0, sizeof(send));\r\n\tsend.data = send_buf;\r\n\tsend.maxsize = sizeof(send_buf);\r\n\tsend.cursize = 2;\r\n\r\n\tMSG_WriteByte(&send, ccmd_stringcmd);\r\n\tMSG_WriteString(&send, dest?dest:\"\");\r\n\tMSG_WriteString(&send, src?src:\"\");\r\n\tMSG_WriteString(&send, cmd?cmd:\"\");\r\n\tMSG_WriteString(&send, msg?msg:\"\");\r\n\r\n\tSSV_InstructMaster(&send);\r\n}\r\nvoid SSV_InitiatePlayerTransfer(client_t *cl, const char *newserver)\r\n{\r\n\tsizebuf_t\tsend;\r\n\tqbyte\t\tsend_buf[MAX_QWMSGLEN];\r\n\tint i;\r\n\tchar tmpbuf[256];\r\n\tfloat parms[NUM_SPAWN_PARMS];\r\n\r\n\tSV_SaveSpawnparmsClient(cl, parms);\r\n\r\n\tmemset(&send, 0, sizeof(send));\r\n\tsend.data = send_buf;\r\n\tsend.maxsize = sizeof(send_buf);\r\n\tsend.cursize = 2;\r\n\r\n\tif (!SSV_IsSubServer())\r\n\t{\r\n\t\t//main->sub.\r\n\t\t//make sure the main server exists, and the player does too.\r\n\t\tpubsubserver_t *s = MSV_Loop_GetLocalServer();\r\n\r\n\t\t//make sure there's a player entry for this player, as they probably bypassed the initial connection thing\r\n\t\tif (!MSV_FindPlayerId(cl->userid))\r\n\t\t{\r\n\t\t\tclusterplayer_t *pl = Z_Malloc(sizeof(*pl));\r\n\t\t\tQ_strncpyz(pl->name, cl->name, sizeof(pl->name));\r\n\t\t\tQ_strncpyz(pl->guid, cl->guid, sizeof(pl->guid));\r\n\t\t\tNET_AdrToString(pl->address, sizeof(pl->address), &cl->netchan.remote_address);\r\n\t\t\tpl->playerid = cl->userid;\r\n\t\t\tInsertLinkBefore(&pl->allplayers, &clusterplayers);\r\n\t\t\tpl->server = s;\r\n\t\t\ts->activeplayers++;\r\n\t\t}\r\n\t}\r\n\r\n\tMSG_WriteByte(&send, ccmd_transferplayer);\r\n\tMSG_WriteLong(&send, cl->userid);\r\n\tMSG_WriteString(&send, cl->name);\r\n\tMSG_WriteString(&send, newserver);\r\n\tMSG_WriteString(&send, NET_AdrToString(tmpbuf, sizeof(tmpbuf), &cl->netchan.remote_address));\r\n\tMSG_WriteString(&send, cl->guid);\r\n\r\n\t//stats\r\n\tMSG_WriteByte(&send, NUM_SPAWN_PARMS);\r\n\tfor (i = 0; i < NUM_SPAWN_PARMS; i++)\r\n\t{\r\n\t\tMSG_WriteFloat(&send, parms[i]);\r\n\t}\r\n\r\n\tSSV_InstructMaster(&send);\r\n}\r\n\r\n#ifdef SQL\r\n#include \"sv_sql.h\"\r\nstatic int pendinglookups = 0;\r\nstruct logininfo_s\r\n{\r\n\tnetadr_t clientaddr;\r\n\tchar guid[64];\r\n\tchar name[64];\r\n};\r\nqboolean SV_IgnoreSQLResult(queryrequest_t *req, int firstrow, int numrows, int numcols, qboolean eof)\r\n{\r\n\treturn false;\r\n}\r\n#endif\r\nvoid MSV_UpdatePlayerStats(unsigned int playerid, unsigned int serverid, int numstats, float *stats)\r\n{\r\n#ifdef SQL\r\n\tqueryrequest_t *req;\r\n\tsqlserver_t *srv = SQL_GetServer(&sv, sv.logindatabase, false);\r\n\tstatic char hex[16] = \"0123456789abcdef\";\r\n\tchar sql[2048], *sqle;\r\n\tunion{float *f;qbyte *b;} blob;\r\n\tif (srv)\r\n\t{\r\n\t\tQ_snprintfz(sql, sizeof(sql), \"UPDATE accounts SET stats=x'\");\r\n\t\tsqle = sql+strlen(sql);\r\n\t\tfor (blob.f = stats, numstats*=4; numstats--; blob.b++)\r\n\t\t{\r\n\t\t\t*sqle++ = hex[*blob.b>>4];\r\n\t\t\t*sqle++ = hex[*blob.b&15];\r\n\t\t}\r\n\t\tQ_snprintfz(sqle, sizeof(sql)-(sqle-sql), \"', serverid=%u WHERE playerid = %u;\", serverid, playerid);\r\n\r\n\t\tSQL_NewQuery(srv, SV_IgnoreSQLResult, sql, &req);\r\n\t}\r\n#endif\r\n}\r\n\r\nqboolean MSV_ClusterLoginReply(netadr_t *legacyclientredirect, unsigned int serverid, unsigned int playerid, char *playername, char *clientguid, netadr_t *clientaddr, void *statsblob, size_t statsblobsize)\r\n{\r\n\tchar tmpbuf[256];\r\n\tnetadr_t serveraddr;\r\n\tpubsubserver_t *s = NULL;\r\n\r\n\tif (!s)\r\n\t\ts = MSV_FindSubServerName(va(\":%s\", sv.modelname));\r\n\r\n\tif (!s || !MSV_AddressForServer(&serveraddr, clientaddr->type, s))\r\n\t\tSV_RejectMessage(SCP_QUAKEWORLD, \"Unable to find lobby.\\n\");\r\n\telse\r\n\t{\r\n\t\tsizebuf_t\tsend;\r\n\t\tqbyte\t\tsend_buf[MAX_QWMSGLEN];\r\n\t\tclusterplayer_t *pl;\r\n\t\tmemset(&send, 0, sizeof(send));\r\n\t\tsend.data = send_buf;\r\n\t\tsend.maxsize = sizeof(send_buf);\r\n\t\tsend.cursize = 2;\r\n\r\n\t\tpl = Z_Malloc(sizeof(*pl));\r\n\t\tQ_strncpyz(pl->name, playername, sizeof(pl->name));\r\n\t\tQ_strncpyz(pl->guid, clientguid, sizeof(pl->guid));\r\n\t\tNET_AdrToString(pl->address, sizeof(pl->address), clientaddr);\r\n\t\tpl->playerid = playerid;\r\n\t\tInsertLinkBefore(&pl->allplayers, &clusterplayers);\r\n\t\tpl->server = s;\r\n\t\ts->activeplayers++;\r\n\r\n\t\tMSG_WriteByte(&send, ccmd_takeplayer);\r\n\t\tMSG_WriteLong(&send, playerid);\r\n\t\tMSG_WriteString(&send, pl->name);\r\n\t\tMSG_WriteLong(&send, 0);\t//from server\r\n\t\tMSG_WriteString(&send, NET_AdrToString(tmpbuf, sizeof(tmpbuf), &net_from));\r\n\t\tMSG_WriteString(&send, clientguid);\r\n\r\n\t\tMSG_WriteByte(&send, statsblobsize/4);\r\n\t\tSZ_Write(&send, statsblob, statsblobsize&~3);\r\n\t\tMSV_WriteSlave(s, &send);\r\n\r\n\t\tif (serveraddr.type == NA_INVALID)\r\n\t\t{\r\n\t\t\tif (net_from.type != NA_LOOPBACK)\r\n\t\t\t\tSV_RejectMessage(SCP_QUAKEWORLD, \"Starting instance.\\n\");\r\n\t\t}\r\n\t\telse if (legacyclientredirect)\r\n\t\t{\r\n\t\t\t*legacyclientredirect = serveraddr;\r\n\t\t\treturn true;\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tchar *s = va(\"fredir\\n%s\", NET_AdrToString(tmpbuf, sizeof(tmpbuf), &serveraddr));\r\n\t\t\tNetchan_OutOfBand (NCF_SERVER, clientaddr, strlen(s), (qbyte *)s);\r\n\t\t\treturn true;\r\n\t\t}\r\n\t}\r\n\treturn false;\r\n}\r\n\r\n#ifdef SQL\r\nqboolean MSV_ClusterLoginSQLResult(queryrequest_t *req, int firstrow, int numrows, int numcols, qboolean eof)\r\n{\r\n\tsqlserver_t *sql = SQL_GetServer(&sv, req->srvid, true);\r\n\tqueryresult_t *res = SQL_GetQueryResult(sql, req->num, 0);\r\n\tsvconnectinfo_t *info = req->user.thread;\r\n\tchar *s;\r\n\tint playerid, serverid;\r\n\tchar *statsblob;\r\n\tsize_t blobsize;\r\n\r\n\t//we only expect one row. if its a continuation then don't bug out\r\n\tif (!firstrow)\r\n\t{\r\n\t\tres = SQL_GetQueryResult(sql, req->num, 0);\r\n\t\tif (!res)\r\n\t\t{\r\n\t\t\tplayerid = 0;\r\n\t\t\tstatsblob = NULL;\r\n\t\t\tblobsize = 0;\r\n\t\t\tserverid = 0;\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\ts = SQL_ReadField(sql, res, 0, 0, true, NULL);\r\n\t\t\tplayerid = atoi(s);\r\n\r\n\t\t\tstatsblob = SQL_ReadField(sql, res, 0, 2, true, &blobsize);\r\n\r\n\t\t\ts = SQL_ReadField(sql, res, 0, 1, true, NULL);\r\n\t\t\tserverid = s?atoi(s):0;\r\n\t\t}\r\n\r\n\t\tnet_from = info->adr;\t//okay, that's a bit stupid, rewrite rejectmessage to accept an arg?\r\n\t\tif (!playerid)\r\n\t\t\tSV_RejectMessage(info->protocol, \"Bad username or password.\\n\");\r\n\t\telse if (sv.state == ss_clustermode)\r\n\t\t\tMSV_ClusterLoginReply(NULL, serverid, playerid, Info_ValueForKey(info->seat[0].info, \"name\"), info->guid, &info->adr, statsblob, blobsize);\r\n\t\telse\r\n\t\t\tSV_DoDirectConnect(info);\r\n\t\tZ_Free(info);\r\n\t\treq->user.thread = NULL;\r\n\t\tpendinglookups--;\r\n\t}\r\n\treturn false;\r\n}\r\n#endif\r\n\r\n#if 0\r\nstatic qboolean MSV_IgnoreSQLResult(queryrequest_t *req, int firstrow, int numrows, int numcols, qboolean eof)\r\n{\r\n\treturn false;\r\n}\r\n#endif\r\nvoid MSV_OpenUserDatabase(void)\r\n{\r\n#if 0\r\n\tsqlserver_t *sql;\r\n\tconst char *sqlparams[] =\r\n\t{\r\n\t\t\"\",\r\n\t\t\"\",\r\n\t\t\"\",\r\n\t\t\"login\",\r\n\t};\r\n\r\n\tCon_Printf(\"Opening database \\\"%s\\\"\\n\", sqlparams[3]);\r\n\tsv.logindatabase = SQL_NewServer(&sv, \"sqlite\", sqlparams);\r\n\r\n\t//create a the accounts table, so we don't end up with unusable databases.\r\n\tsql = SQL_GetServer(&sv, sv.logindatabase, false);\r\n\tif (sql)\r\n\t{\r\n\t\tSQL_NewQuery(sql, MSV_IgnoreSQLResult,\r\n\t\t\t\t\"CREATE TABLE IF NOT EXISTS accounts(\"\r\n\t\t\t\t\t\"playerid INTEGER PRIMARY KEY,\"\r\n\t\t\t\t\t\"name TEXT NOT NULL UNIQUE,\"\r\n\t\t\t\t\t\"password TEXT,\"\r\n\t\t\t\t\t\"serverid INTEGER,\"\r\n\t\t\t\t\t\"parms BLOB,\"\r\n\t\t\t\t\t\"parmstring TEXT\"\r\n\t\t\t\t\");\", NULL);\r\n\t}\r\n#endif\r\n}\r\n\r\n//returns true to block entry to this server.\r\nextern int\tnextuserid;\r\nqboolean MSV_ClusterLogin(svconnectinfo_t *info)\r\n{\r\n\t/*if (!*guid)\r\n\t{\r\n\t\tSV_RejectMessage(SCP_QUAKEWORLD, \"No guid info, please set cl_sendguid to 1.\\n\");\r\n\t\treturn false;\r\n\t}*/\r\n#ifdef SQL\r\n\tif (sv.logindatabase != -1)\r\n\t{\r\n\t\tchar escname[64], escpasswd[64];\r\n\t\tsqlserver_t *sql;\r\n\t\tqueryrequest_t *req;\r\n\t\tif (pendinglookups > 10)\r\n\t\t\treturn true;\t//don't spam requests if we're getting dos-spammed.\r\n\t\tsql = SQL_GetServer(&sv, sv.logindatabase, false);\r\n\t\tif (!sql)\r\n\t\t\treturn true;\t//connection was killed? o.O\r\n\t\tSQL_Escape(sql, Info_ValueForKey(info->seat[0].info, \"name\"), escname, sizeof(escname));\r\n\t\tSQL_Escape(sql, Info_ValueForKey(info->seat[0].info, \"password\"), escpasswd, sizeof(escpasswd));\r\n\t\tif (SQL_NewQuery(sql, MSV_ClusterLoginSQLResult, va(\"SELECT playerid,serverid,parms,parmstring FROM accounts WHERE name='%s' AND password='%s';\", escname, escpasswd), &req) != -1)\r\n\t\t{\r\n\t\t\tpendinglookups++;\r\n\t\t\treq->user.thread = Z_Malloc(sizeof(*info));\r\n\t\t\tmemcpy(req->user.thread, info, sizeof(*info));\r\n\t\t}\r\n\t}\r\n\telse\r\n#endif\r\n\tif (sv.state != ss_clustermode)\r\n\t\treturn false;\r\n\telse\r\n/*\t\tif (0)\r\n\t{\r\n\t\tchar tmpbuf[256];\r\n\t\tnetadr_t redir;\r\n\t\tif (MSV_ClusterLoginReply(&redir, 0, nextuserid++, guid, &net_from, NULL, 0))\r\n\t\t{\r\n\t\t\tInfo_SetValueForStarKey(userinfo, \"*redirect\", NET_AdrToString(tmpbuf, sizeof(tmpbuf), &redir), userinfosize);\r\n\t\t\treturn false;\r\n\t\t}\r\n\t\treturn true;\r\n\t}\r\n\telse*/\r\n\t\tMSV_ClusterLoginReply(NULL, 0, ++nextuserid, Info_ValueForKey(info->seat[0].info, \"name\"), info->guid, &net_from, NULL, 0);\r\n\treturn true;\r\n}\r\n#endif\r\n"
  },
  {
    "path": "engine/server/sv_demo.c",
    "content": "#include \"quakedef.h\"\n\n#ifndef CLIENTONLY\n#ifdef SERVER_DEMO_PLAYBACK\n\nvoid NPP_MVDWriteByte(qbyte data, client_t *to, int broadcast);\n\nvoid SV_New_f (void);\n\nqboolean SV_GetPacket (void)\n{\n\treturn NET_GetPacket (NS_SERVER);\n}\n\n\n\n\n\n\n\n\n#define dem_cmd\t\t\t0\n#define dem_read\t\t1\n#define dem_set\t\t\t2\n#define dem_multiple\t3\n#define\tdem_single\t\t4\n#define dem_stats\t\t5\n#define dem_all\t\t\t6\n\n\n\n\n#define svd sv\n\n//char empty[512];\nqboolean SV_ReadMVD (void);\n\n#ifdef SERVERONLY\nfloat nextdemotime = 0;\nfloat olddemotime = 0;\n#else\nextern float nextdemotime;\nextern float olddemotime;\n#endif\nvoid SV_LoadClientDemo_f (void)\n{\n\tint i;\n\tchar demoname[MAX_OSPATH];\n\tclient_t *ohc;\n\tif (Cmd_Argc() < 2)\n\t{\n\t\tCon_Printf(\"%s <demoname>: play a server side multi-view demo\\n\", Cmd_Argv(0));\n\t\treturn;\n\t}\n\n\tif (svd.demofile)\n\t{\n\t\tCon_Printf (\"Ending old demo\\n\");\n\t\tVFS_CLOSE(svd.demofile);\n\t\tsvd.demofile = NULL;\n\n\t\tSV_ReadMVD();\n\t}\n\n\tQ_strncpyz(demoname, Cmd_Argv(1), sizeof(demoname));\n\n\tif (!sv.state)\n\t\tCmd_ExecuteString(\"map start\\n\", Cmd_ExecLevel);\t//go for the start map\n\tif (!sv.state)\n\t{\n\t\tCon_Printf(\"Could not activate server\\n\");\n\t\treturn;\n\t}\n\n\tsvd.demofile = FS_OpenVFS(demoname, \"rb\", FS_GAME);\n\tif (!svd.demofile)\t//try with a different path\n\t\tsvd.demofile = FS_OpenVFS(va(\"demos/%s\", demoname), \"rb\", FS_GAME);\n\tcom_filesize = VFS_GETLEN(svd.demofile);\n\n\tif (!svd.demofile)\n\t{\n\t\tCon_Printf(\"Failed to open %s\\n\", demoname);\n\t\treturn;\n\t}\n\tif (com_filesize <= 0)\n\t{\n\t\tCon_Printf(\"Failed to open %s\\n\", demoname);\n\t\tVFS_CLOSE(svd.demofile);\n\t\tsvd.demofile = NULL;\n\t\treturn;\n\t}\n\n\tif (sv.demostate)\n\t{\n\t\tsv.demostatevalid = false;\n\t\tmemset(sv.demostate, 0, sizeof(entity_state_t)*MAX_EDICTS);\n\t}\n/*\n\tfor (i = 0; i < svs.allocated_client_slots; i++)\n\t{\n\t\thost_client = &svs.clients[i];\n\t\tif (host_client->state == cs_spawned)\n\t\t\thost_client->state = cs_connected;\n\t}\n*/\n//\tSV_BroadcastCommand (\"changing\\n\");\n\n#ifndef SERVERONLY\n\tCL_Disconnect();\n#endif\n\n\tsvd.mvdplayback = true;\n\tCon_Printf(\"Playing from %s\\n\", demoname);\n\n\tfor (i = 0; i < MAX_SIGNON_BUFFERS; i++)\n\t\tsv.demosignon_buffer_size[i] = 0;\n\tsv.demosignon.maxsize = sizeof(sv.demosignon_buffers[0]);\n\tsv.demosignon.data = sv.demosignon_buffers[0];\n\tsv.demosignon.cursize = 0;\n\tsv.num_demosignon_buffers = 1;\n\tsv.democausesreconnect = false;\n\t*sv.demname = '\\0';\n\n\tsvd.lasttype = dem_read;\n\tsvd.realtime = realtime;\n\tnextdemotime = realtime-0.1; //cause read of the first 0.1 secs to get all spawn info.\n\tolddemotime = realtime;\n\twhile (SV_ReadMVD())\n\t{\n\t\tsv.datagram.cursize = 0;\n\t\tsv.reliable_datagram.cursize = 0;\n\t}\n\n\t//if we did need reconnect, continue needing it cos I can't be bothered to play with multiple buffers etc.\n//\tif (memcmp(sv.demmodel_precache, sv.model_precache, sizeof(sv.model_precache)) || memcmp(sv.demsound_precache, sv.sound_precache, sizeof(sv.sound_precache)))\n\t\tsv.democausesreconnect = true;\n\tif (sv.democausesreconnect)\n\t{\n\t\tsvs.spawncount++;\n\t\tSV_BroadcastCommand (\"changing\\n\");\t//but this arrives BEFORE the serverdata\n\n\t\tohc = host_client;\n\t\tfor (i=0, host_client = svs.clients ; i<svs.allocated_client_slots ; i++, host_client++)\n\t\t{\n\t\t\tif (host_client->state != cs_spawned)\n\t\t\t\tcontinue;\n\t\t\thost_client->state = cs_connected;\n\t\t\thost_client->istobeloaded = true;\t//don't harm the ent\n\t\t\tSV_New_f ();\n\t\t}\n\t\thost_client = ohc;\n\t}\n\treturn;\n}\n\nqboolean SV_RunDemo (void)\n{\n\tint\t\tr, i;\n\tfloat\tdemotime;\n\tqbyte\tc;\n//\tusercmd_t *pcmd;\n//\tusercmd_t emptycmd;\n\tqbyte\tnewtime;\n\n\n\nreadnext:\n\n\t// read the time from the packet\n\tif (svd.mvdplayback)\n\t{\n\t\tVFS_READ(svd.demofile, &newtime, sizeof(newtime));\n\t\tnextdemotime = olddemotime + newtime * (1/1000.0f);\n\t\tdemotime = nextdemotime;\n\n\t\tif (nextdemotime > svd.realtime)\n\t\t{\n\t\t\tVFS_SEEK(svd.demofile, VFS_TELL(svd.demofile) - sizeof(newtime));\n\t\t\treturn false;\n\t\t}\n\t\telse if (nextdemotime + 0.1 < svd.realtime)\n\t\t\tdemotime = svd.realtime;\t//we froze too long.. ?\n\t}\n\telse\n\t{\n\t\tVFS_READ(svd.demofile, &demotime, sizeof(demotime));\n\t\tdemotime = LittleFloat(demotime);\n\t\tif (!nextdemotime)\n\t\t\tsvd.realtime = nextdemotime = demotime;\n\t}\n\n// decide if it is time to grab the next message\n\tif (!sv.paused)\n\t{\t// always grab until fully connected\n\t\tif (!svd.mvdplayback)\n\t\t{\n\t\t\tif (svd.realtime + 1.0 < demotime) {\n\t\t\t\t// too far back\n\t\t\t\tsvd.realtime = demotime - 1.0;\n\t\t\t\t// rewind back to time\n\t\t\t\tVFS_SEEK(svd.demofile, VFS_TELL(svd.demofile) - sizeof(demotime));\n\t\t\t\treturn false;\n\t\t\t} else if (nextdemotime < demotime) {\n\t\t\t\t// rewind back to time\n\t\t\t\tVFS_SEEK(svd.demofile, VFS_TELL(svd.demofile) - sizeof(demotime));\n\t\t\t\treturn false;\t\t// don't need another message yet\n\t\t\t}\n\t\t}\n\t} else {\n\t\tsvd.realtime = demotime; // we're warping\n\t}\n\n\tolddemotime = demotime;\n\n\t// get the msg type\n\tif ((r = VFS_READ(svd.demofile, &c, sizeof(c))) != sizeof(c))\n\t{\n\t\tCon_Printf (\"Unexpected end of demo\\n\");\n\t\tVFS_CLOSE(svd.demofile);\n\t\tsvd.demofile = NULL;\n\t\treturn false;\n//\t\tSV_Error (\"Unexpected end of demo\");\n\t}\n\n\tswitch (c & 7) {\n\tcase dem_cmd :\n\n\t\tCon_Printf (\"dem_cmd not supported\\n\");\n\t\tVFS_CLOSE(svd.demofile);\n\t\tsvd.demofile = NULL;\n\t\treturn false;\n\n\n\t\t// user sent input\n//\t\ti = svd.netchan.outgoing_sequence & UPDATE_MASK;\n//\t\tpcmd = &cl.frames[i].cmd;\n\t//\tif ((r = fread (&emptycmd, sizeof(emptycmd), 1, svd.demofile)) != 1)\n\t//\t\tSV_Error (\"Corrupted demo\");\n/*\n\t\t// qbyte order stuff\n\t\tfor (j = 0; j < 3; j++)\n\t\t\tpcmd->angles[j] = LittleFloat(pcmd->angles[j]);\n\n\t\tpcmd->forwardmove = LittleShort(pcmd->forwardmove);\n\t\tpcmd->sidemove    = LittleShort(pcmd->sidemove);\n\t\tpcmd->upmove      = LittleShort(pcmd->upmove);\n\t\tcl.frames[i].senttime = demotime;\n\t\tcl.frames[i].receivedtime = -1;\t\t// we haven't gotten a reply yet\n\t\tsvd.netchan.outgoing_sequence++;\n*/\n\t//\tfread (&emptycmd, 12, 1, svd.demofile);\n/*\t\tfor (j = 0; j < 3; j++)\n\t\t\t cl.viewangles[i] = LittleFloat (cl.viewangles[i]);\n\t\tif (cl.spectator)\n\t\t\tCam_TryLock ();\n*/\n\t\tgoto readnext;\n\n\tcase dem_read:\nreadit:\n\t\t// get the next message\n\t\tVFS_READ (svd.demofile, &net_message.cursize, 4);\n\t\tnet_message.cursize = LittleLong (net_message.cursize);\n\n\t\tif (!svd.mvdplayback && net_message.cursize > MAX_QWMSGLEN + 8)\n\t\t\tSV_Error (\"Demo message > MAX_MSGLEN + 8\");\n\t\telse if (svd.mvdplayback && net_message.cursize > net_message.maxsize)\n\t\t\tSV_Error (\"Demo message > MAX_UDP_PACKET\");\n\n\t\tif ((r = VFS_READ(svd.demofile, net_message.data, net_message.cursize)) != net_message.cursize)\n\t\t\tSV_Error (\"Corrupted demo\");\n\n/*\t\tif (svd.mvdplayback) {\n\t\t\ttracknum = Cam_TrackNum();\n\n\t\t\tif (svd.lasttype == dem_multiple) {\n\t\t\t\tif (tracknum == -1)\n\t\t\t\t\tgoto readnext;\n\n\t\t\t\tif (!(svd.lastto & (1 << (tracknum))))\n\t\t\t\t\tgoto readnext;\n\t\t\t} else if (svd.lasttype == dem_single) {\n\t\t\t\tif (tracknum == -1 || svd.lastto != spec_track)\n\t\t\t\t\tgoto readnext;\n\t\t\t}\n\t\t}\n*/\n\t\treturn true;\n\n\tcase dem_set:\n\t\tVFS_READ(svd.demofile, &i, 4);\n\t\tVFS_READ(svd.demofile, &i, 4);\n\t\tgoto readnext;\n\n\tcase dem_multiple:\n\t\tif ((r = VFS_READ(svd.demofile, &i, 4)) != 1)\n\t\t\tSV_Error (\"Corrupted demo\");\n\n\t\tsvd.lastto = LittleLong(i);\n\t\tsvd.lasttype = dem_multiple;\n\t\tgoto readit;\n\n\tcase dem_single:\n\t\tsvd.lastto = c >> 3;\n\t\tsvd.lasttype = dem_single;\n\t\tgoto readit;\n\tcase dem_stats:\n\t\tsvd.lastto = c >> 3;\n\t\tsvd.lasttype = dem_stats;\n\t\tgoto readit;\n\tcase dem_all:\n\t\tsvd.lastto = 0;\n\t\tsvd.lasttype = dem_all;\n\t\tgoto readit;\n\tdefault :\n\t\tSV_Error (\"Corrupted demo\");\n\t\treturn false;\n\t}\n}\n\nqboolean SV_ReadMVD (void)\n{\n\tint i, c;\n\tclient_t *cl;\n\n\tint oldsc = svs.spawncount;\n\n\tif (!svd.demofile)\n\t{\n\t\tif (sv.demostate)\n\t\t\tBZ_Free(sv.demostate);\n\t\tsv.demostate=NULL;\n\t\tsv.demostatevalid = false;\n\t\tif (sv.democausesreconnect)\n\t\t{\n\t\t\tsv.democausesreconnect = false;\n\t\t\tsvs.spawncount++;\n\n\t\t\tfor (i=0, host_client = svs.clients ; i<svs.allocated_client_slots ; i++, host_client++)\n\t\t\t{\n\t\t\t\tif (host_client->state != cs_spawned)\n\t\t\t\t\tcontinue;\n\t\t\t\thost_client->state = cs_connected;\n\t\t\t\thost_client->istobeloaded = true;\t//don't harm the ent\n\t\t\t\tSV_New_f ();\n\t\t\t}\n\t\t}\n\t\tnextdemotime = realtime;\n\t\treturn false;\n\t}\n\n\tsvd.realtime = realtime;\n\n\tif (!SV_RunDemo())\n\t{\n\t\tif (!svd.demofile)\n\t\t{\t//demo ended.\n\t\t\tfor (i=0, cl = svs.clients ; i<svs.allocated_client_slots ; i++, cl++)\n\t\t\t{\n\t\t\t\tcl->sendinfo = true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\tif (!svd.mvdplayback)\t//broadcast all.\n\t{\n\t\tfor (c = 0; c < net_message.cursize; c++)\n\t\t\tNPP_MVDWriteByte(net_message.data[c], NULL, true);\n\n\t\tNPP_MVDForceFlush();\n\t}\n\telse\n\t{\n\t\tswitch(svd.lasttype)\n\t\t{\n\t\tdefault:\n\t\t\tCon_Printf(\"Bad sv.lasttype %i\\n\", sv.lasttype);\n\t\t\tbreak;\n\n\n\t\tcase dem_set:\t//Unknown stuff. (Got to work out what this is for)\n\t\tcase dem_read:\t//baseline stuff\n\t\tcase dem_stats:\t//contains info read by server\n\t\tcase dem_all:\t//broadcast things (like userinfo)\n\t\tcase dem_multiple:\t//treat these as broadcast (tempents should be treated correctly)\n\t\t\tfor (c = 0; c < net_message.cursize; c++)\n\t\t\t\tNPP_MVDWriteByte(net_message.data[c], NULL, true);\n\n\t\t\tNPP_MVDForceFlush();\n\t\t\tbreak;\n//\t\t\t\tcase dem_read:\t//baseline stuff\n\t\tcase dem_single:\n\t\t\tfor (i=0, cl = svs.clients ; i<svs.allocated_client_slots ; i++, cl++)\n\t\t\t{\n\t\t\t\tif (!cl->state)\n\t\t\t\t\tcontinue;\n\t//\t\t\tif (!(1 >> 3 & svd.lastto))\n\t\t\t\tif (!cl->spec_track)\n\t\t\t\t\tcontinue;\n\t\t\t\tif (!(cl->spec_track >> 3 & svd.lastto))\n\t\t\t\t\tcontinue;\n\n\t\t\t\tfor (c = 0; c < net_message.cursize; c++)\n\t\t\t\t\tNPP_MVDWriteByte(net_message.data[c], cl, false);\n\n\t\t\t\tNPP_MVDForceFlush();\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tif (oldsc != svs.spawncount)\n\t{\n\t\tVFS_CLOSE(svd.demofile);\n\t\tsvd.demofile = NULL;\n\n\t\tfor (i=0, host_client = svs.clients ; i<svs.allocated_client_slots ; i++, host_client++)\n\t\t{\n\t\t\tif (host_client->state != cs_spawned)\n\t\t\t\tcontinue;\n\t\t\thost_client->state = cs_connected;\n\t\t\thost_client->istobeloaded = true;\t//don't harm the ent\n\t\t\tSV_New_f ();\n\t\t}\n\t\treturn true;\n\t}\n\n\treturn true;\n}\n\n\n\n\nvoid SV_Demo_Init(void)\n{\n\tCmd_AddCommand(\"playmvd\", SV_LoadClientDemo_f);\n\tCmd_AddCommand(\"mvdplay\", SV_LoadClientDemo_f);\n\tCmd_AddCommand(\"svplay\", SV_PlayDemo_f);\n\tCmd_AddCommand(\"svrecord\", SV_RecordDemo_f);\n}\n\n#endif //SERVER_DEMO_PLAYBACK\n#endif //CLIENTONLY\n"
  },
  {
    "path": "engine/server/sv_ents.c",
    "content": "/*\nCopyright (C) 1996-1997 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n*/\n\n#include \"quakedef.h\"\n#include \"pr_common.h\"\n#ifndef CLIENTONLY\n\nextern cvar_t sv_nailhack;\nextern cvar_t sv_cullentities_trace;\nextern cvar_t sv_cullplayers_trace;\nextern cvar_t sv_nopvs;\n\n#define SV_PVS_CAMERAS 16\ntypedef struct\n{\n\tint numents;\n\tedict_t *ent[SV_PVS_CAMERAS];\t//ents in this list are always sent, even if the server thinks that they are invisible.\n\tvec3_t org[SV_PVS_CAMERAS];\n\tint area[1+SV_PVS_CAMERAS];\n\n\tpvsbuffer_t pvs;\n} pvscamera_t;\n\nstatic void *AllocateBoneSpace(packet_entities_t *pack, unsigned char bonecount, unsigned int *allocationpos)\n{\n\tsize_t space = bonecount * sizeof(short)*7;\n\tvoid *r;\n\tif (pack->bonedatacur + space > pack->bonedatamax)\n\t{\t//expand the storage as needed. messy, but whatever.\n\t\tpack->bonedatamax = pack->bonedatacur + space;\n\t\tpack->bonedata = BZ_Realloc(pack->bonedata, pack->bonedatamax);\n\t}\n\tr = pack->bonedata + pack->bonedatacur;\n\t*allocationpos = pack->bonedatacur;\n\tpack->bonedatacur += space;\n\treturn r;\n}\n\n/*\n=============================================================================\n\nThe PVS must include a small area around the client to allow head bobbing\nor other small motion on the client side.  Otherwise, a bob might cause an\nentity that should be visible to not show up, especially when the bob\ncrosses a waterline.\n\n=============================================================================\n*/\n\nstatic int needcleanup;\n\n//int\t\tfatbytes;\n\n#define round64(size) ((size+7)&~7)\n\nvoid SV_ExpandNackFrames(client_t *client, int require, client_frame_t **currentframeptr)\n{\n\tclient_frame_t *newframes;\n\tchar *ptr;\n\tint i;\n\tint maxlog = require * 2;\t/*this is the max number of ents updated per frame. we can't track more, so...*/\n\tif (maxlog > client->max_net_ents)\n\t\tmaxlog = client->max_net_ents;\n\tptr = Z_Malloc(\tround64(sizeof(client_frame_t)*UPDATE_BACKUP)+\n\t\t\t\t\tround64(sizeof(*client->pendingdeltabits)*client->max_net_ents)+\n\t\t\t\t\tround64(sizeof(*client->pendingcsqcbits)*client->max_net_ents)+\n\t\t\t\t\tround64(sizeof(newframes[i].resend)*maxlog)*UPDATE_BACKUP);\n\tnewframes = (void*)ptr;\n\tmemcpy(newframes, client->frameunion.frames, sizeof(client_frame_t)*UPDATE_BACKUP);\n\tptr += round64(sizeof(client_frame_t)*UPDATE_BACKUP);\n\tmemcpy(ptr, client->pendingdeltabits, sizeof(*client->pendingdeltabits)*client->max_net_ents);\n\tclient->pendingdeltabits = (void*)ptr;\n\tptr += round64(sizeof(*client->pendingdeltabits)*client->max_net_ents);\n\tmemcpy(ptr, client->pendingcsqcbits, sizeof(*client->pendingcsqcbits)*client->max_net_ents);\n\tclient->pendingcsqcbits = (void*)ptr;\n\tptr += round64(sizeof(*client->pendingcsqcbits)*client->max_net_ents);\n\tfor (i = 0; i < UPDATE_BACKUP; i++)\n\t{\n\t\tnewframes[i].maxresend = maxlog;\n\t\tnewframes[i].qwentities.max_entities = 0;\n\t\tnewframes[i].resend = (void*)ptr;\n\t\tptr += round64(sizeof(newframes[i].resend)*maxlog);\n\t\tnewframes[i].numresend = client->frameunion.frames[i].numresend;\n\t\tmemcpy(newframes[i].resend, client->frameunion.frames[i].resend, sizeof(newframes[i].resend)*newframes[i].numresend);\n\t\tnewframes[i].senttime = realtime;\n\t}\n\tZ_Free(client->frameunion.frames);\n\n\t//if you're calling this then its because you're currently generating new frame data, and its a problem if that changes from under you. fix it up for the caller (so they can't forget to do so)\n\t*currentframeptr = newframes+(*currentframeptr-client->frameunion.frames);\n\n\tclient->frameunion.frames = newframes;\n}\n\n//=============================================================================\n\n// because there can be a lot of nails, there is a special\n// network protocol for them\n#define\tMAX_NAILS\t32\nstatic edict_t\t*nails[MAX_NAILS];\nstatic int\t\tnumnails;\nstatic int\t\tnailcount = 0;\nextern\tint\tsv_nailmodel, sv_supernailmodel, sv_playermodel;\n\n#ifdef SERVER_DEMO_PLAYBACK\nqboolean demonails;\n#endif\n\nstatic edict_t **csqcent/*[csqcmaxents]*/;\nstatic size_t csqcmaxents;\nstatic size_t csqcnuments;\n\nqboolean SV_AddNailUpdate (edict_t *ent)\n{\n\tif (ent->v->modelindex != sv_nailmodel\n\t\t&& ent->v->modelindex != sv_supernailmodel)\n\t\treturn false;\t//must be a nail\n\tif (sv_nailhack.value || (host_client->fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS))\n\t\treturn false;\t//'nailhack' is named because of a qizmo-publicised binary hack to disable svc_nails. replacementdeltas also trims much of the state so we may as well use it.\n\t\t\t\t\t\t//should probably also detect qizmo specifically - its trajectory stuff beats svc_nails.\n\tif (ent->v->origin[0] <= -4096 || ent->v->origin[0] >= 4096 ||\n\t\tent->v->origin[1] <= -4096 || ent->v->origin[1] >= 4096 ||\n\t\tent->v->origin[2] <= -4096 || ent->v->origin[2] >= 4096)\n\t\treturn !(host_client->fteprotocolextensions & PEXT_FLOATCOORDS);\t//outside the bounds of the nails protocol. just swallow it if it can't be sent anyway.\n\n#ifdef SERVER_DEMO_PLAYBACK\n\tdemonails = false;\n#endif\n\n\tif (numnails == MAX_NAILS)\n\t\treturn true;\n\n\tnails[numnails] = ent;\n\tnumnails++;\n\treturn true;\n}\n#ifdef SERVER_DEMO_PLAYBACK\nqboolean SV_DemoNailUpdate (int i)\n{\n\tdemonails = true;\n\n\tif (numnails == MAX_NAILS)\n\t\treturn true;\n\tnails[numnails] = (edict_t *)i;\n\tnumnails++;\n\treturn true;\n}\n#endif\n\nvoid SV_EmitNailUpdate (sizebuf_t *msg, qboolean recorder)\n{\n\tqbyte\tbits[6];\t// [48 bits] xyzpy 12 12 12 4 8\n\tint\t\tn, i;\n\tedict_t\t*ent;\n\tint\t\tx, y, z, p, yaw;\n\n\tif (!numnails)\n\t\treturn;\n\n\tif (recorder)\n\t\tMSG_WriteByte (msg, svc_nails2);\n\telse\n\t\tMSG_WriteByte (msg, svc_nails);\n\n\tMSG_WriteByte (msg, numnails);\n\n#ifdef SERVER_DEMO_PLAYBACK\n\tif (demonails)\n\t{\n\t\tfor (n=0 ; n<numnails ; n++)\n\t\t{\n\t\t\ti = (int)(nails[n]);\n\t\t\tif (recorder) {\n\t\t\t\tif (!sv.demospikes[i].id) {\n\t\t\t\t\tif (!((++nailcount)&255)) nailcount++;\n\t\t\t\t\tsv.demospikes[i].id = nailcount&255;\n\t\t\t\t}\n\n\t\t\t\tMSG_WriteByte (msg, (qbyte)sv.demospikes[i].id);\n\t\t\t}\n\t\t\tx = (int)(sv.demospikes[i].org[0]+4096)>>1;\n\t\t\ty = (int)(sv.demospikes[i].org[1]+4096)>>1;\n\t\t\tz = (int)(sv.demospikes[i].org[2]+4096)>>1;\n\t\t\tp = (int)(sv.demospikes[i].pitch)&15;\n\t\t\tyaw = (int)(sv.demospikes[i].yaw)&255;\n\n\t\t\tbits[0] = x;\n\t\t\tbits[1] = (x>>8) | (y<<4);\n\t\t\tbits[2] = (y>>4);\n\t\t\tbits[3] = z;\n\t\t\tbits[4] = (z>>8) | (p<<4);\n\t\t\tbits[5] = yaw;\n\n\t\t\tfor (i=0 ; i<6 ; i++)\n\t\t\t\tMSG_WriteByte (msg, bits[i]);\n\t\t}\n\n\t\treturn;\n\t}\n#endif\n\tfor (n=0 ; n<numnails ; n++)\n\t{\n\t\tent = nails[n];\n\t\tif (recorder) {\n\t\t\tif (!ent->v->colormap) {\n\t\t\t\tif (!((++nailcount)&255)) nailcount++;\n\t\t\t\tent->v->colormap = nailcount&255;\n\t\t\t}\n\n\t\t\tMSG_WriteByte (msg, (qbyte)ent->v->colormap);\n\t\t}\n\t\tx = (int)(ent->v->origin[0]+4096)>>1;\n\t\ty = (int)(ent->v->origin[1]+4096)>>1;\n\t\tz = (int)(ent->v->origin[2]+4096)>>1;\n\t\tp = (int)(16*ent->v->angles[0]/360)&15;\n\t\tyaw = (int)(256*ent->v->angles[1]/360)&255;\n\n\t\tbits[0] = x\t\t\t\t\t& 0xff;\n\t\tbits[1] = ((x>>8) | (y<<4))\t& 0xff;\n\t\tbits[2] = (y>>4)\t\t\t& 0xff;\n\t\tbits[3] = z\t\t\t\t\t& 0xff;\n\t\tbits[4] = ((z>>8) | (p<<4))\t& 0xff;\n\t\tbits[5] = yaw\t\t\t\t& 0xff;\n\n\t\tfor (i=0 ; i<6 ; i++)\n\t\t\tMSG_WriteByte (msg, bits[i]);\n\t}\n}\n\n\n\n\n\n//=============================================================================\n\n//this is the bit of the code that sends the csqc entity deltas out.\n//whenever the entity in question has a newer version than we sent to the client, we need to resend.\n\n//So, we track the outgoing sequence that an entity was sent in, and the version.\n//Upon detection of a dropped packet, we resend all entities who were last sent in that packet.\n//When an entities' last sent version doesn't match the current version, we send.\nstatic qboolean SV_AddCSQCUpdate (client_t *client, edict_t *ent)\n{\n#ifndef PEXT_CSQC\n\treturn false;\n#else\n\tif (!ent->xv->SendEntity)\n\t\treturn false;\n\n\tif (!(client->csqcactive))\n\t\treturn false;\n\n\tif (csqcnuments >= csqcmaxents)\n\t\tZ_ReallocElements((void**)&csqcent, &csqcmaxents, csqcnuments + 1024, sizeof(*csqcent));\n\tcsqcent[csqcnuments++] = ent;\n\n\treturn true;\n#endif\n}\nsizebuf_t csqcmsgbuffer;\n\nstatic void SV_EmitDeltaEntIndex(sizebuf_t *msg, unsigned int entnum, qboolean remove, qboolean big)\n{\n\tunsigned int rflag = remove?0x8000:0;\n\tif (big)\n\t{\n\t\tif (entnum >= 0x4000)\n\t\t{\n\t\t\tMSG_WriteShort(msg, (entnum&0x3fff) | 0x4000 | rflag);\n\t\t\tMSG_WriteByte(msg, entnum >> 14);\n\t\t}\n\t\telse\n\t\t\tMSG_WriteShort(msg, entnum | rflag);\n\t}\n\telse\n\t\tMSG_WriteShort(msg, entnum | rflag);\n}\nvoid SV_EmitCSQCUpdate(client_t *client, sizebuf_t *msg, qbyte svcnumber)\n{\n#ifdef PEXT_CSQC\n\tstatic float throttle;\n\tqbyte messagebuffer[MAX_OVERALLMSGLEN];\n\tint en;\n\tint currentsequence = client->netchan.outgoing_sequence;\n\tglobalvars_t *pr_globals;\n\tedict_t *ent;\n\tqboolean writtenheader = false;\n\tint viewerent;\n\tint entnum;\n\tclient_frame_t *frame = &client->frameunion.frames[currentsequence & UPDATE_MASK];\n\tint lognum = frame->numresend;\n\tquint64_t bits;\n\n\tstruct resendinfo_s *resend = frame->resend;\n\tint maxlog = frame->maxresend;\n\n\t//we don't check that we got some already - because this is delta compressed!\n\n\tif (!client->csqcactive || !svprogfuncs || !client->pendingcsqcbits)\n\t\treturn;\n\n\tpr_globals = PR_globals(svprogfuncs, PR_CURRENT);\n\n\tif (client->edict)\n\t\tviewerent = EDICT_TO_PROG(svprogfuncs, client->edict);\n\telse\n\t\tviewerent = 0; /*for mvds, its as if world is looking*/\n\n\t//FIXME: prioritise the list of csqc ents somehow\n\n\tcsqcmsgbuffer.data = messagebuffer;\n\tcsqcmsgbuffer.maxsize = sizeof(messagebuffer);\n\tcsqcmsgbuffer.packing = msg->packing;\n\tcsqcmsgbuffer.prim = msg->prim;\n\n\tif (sv_csqcdebug.ival)\n\t\tsvcnumber = svcfte_csqcentities_sized;\n\n\tfor (en = 0, entnum = 0; en < csqcnuments; en++, entnum++)\n\t{\n\t\tint mod_result = 0;\n\t\tent = csqcent[en];\n\n\t\t//add any entity removes on ents leading up to this entity\n\t\tfor (; entnum < ent->entnum; entnum++)\n\t\t{\n\t\t\tif (client->pendingcsqcbits[entnum] & (SENDFLAGS_PRESENT|SENDFLAGS_REMOVED))\n\t\t\t{\n\t\t\t\tif (!(client->pendingcsqcbits[entnum] & SENDFLAGS_REMOVED))\n\t\t\t\t{\t//while the entity has NOREMOVE, only remove it if the remove is a resend\n\t\t\t\t\tif ((int)EDICT_NUM_PB(svprogfuncs, entnum)->xv->pvsflags & PVSF_NOREMOVE)\n\t\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (msg->cursize + 5 >= msg->maxsize)\n\t\t\t\t\tbreak;\t//we're overflowing, try removing next frame instead.\n\n\t\t\t\tif (lognum > maxlog)\n\t\t\t\t{\n\t\t\t\t\tif (maxlog == client->max_net_ents)\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tSV_ExpandNackFrames(client, lognum+1, &frame);\n\t\t\t\t\tresend = frame->resend;\n\t\t\t\t\tmaxlog = frame->maxresend;\n\t\t\t\t}\n\t\t\t\tresend[lognum].entnum = entnum;\n\t\t\t\tresend[lognum].bits = 0;\n\t\t\t\tresend[lognum].flags = SENDFLAGS_REMOVED;\n\t\t\t\tlognum++;\n\t\t\t\n\t\t\t\tif (!writtenheader)\n\t\t\t\t{\n\t\t\t\t\twrittenheader=true;\n\t\t\t\t\tMSG_WriteByte(msg, svcnumber);\n\t\t\t\t}\n\n\t\t\t\tSV_EmitDeltaEntIndex(msg, entnum, true, client->fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS);\n//\t\t\t\tCon_Printf(\"Sending remove 2 packet\\n\");\n\n\t\t\t\tclient->pendingcsqcbits[entnum] = 0;\n\t\t\t}\n\t\t}\n\n\t\tif (client->pendingcsqcbits[entnum] == SENDFLAGS_PRESENT)\n\t\t\tcontinue;\t//nothing changed\n\n\t\tif (client->pendingcsqcbits[entnum] & SENDFLAGS_REMOVED)\n\t\t{\n\t\t\t//we lost a remove, but it got readded since.\n\t\t\t//make sure all is resent\n\t\t\tclient->pendingcsqcbits[entnum] = SENDFLAGS_USABLE;\n\t\t}\n\n\t\tif (!(client->pendingcsqcbits[entnum] & SENDFLAGS_PRESENT))\n\t\t\tclient->pendingcsqcbits[entnum] = SENDFLAGS_USABLE;\t//this entity appears new. make sure its fully transmitted.\n\n\t\tbits = client->pendingcsqcbits[entnum];\n\t\tclient->pendingcsqcbits[entnum] = 0;\n\n\t\tcsqcmsgbuffer.cursize = 0;\n\t\tcsqcmsgbuffer.currentbit = 0;\n\n\t\tpr_global_struct->self = EDICT_TO_PROG(svprogfuncs, ent);\n#ifdef VM_Q1\n\t\tif (svs.gametype == GT_Q1QVM)\n\t\t{\n\t\t\tpr_global_struct->other = viewerent;\n\t\t\tmod_result = Q1QVM_SendEntity(bits >> SENDFLAGS_SHIFT);\n\t\t}\n\t\telse\n#endif\n\t\t{\n\t\t\t//Ask CSQC to write a buffer for it.\n\t\t\tG_INT(OFS_PARM0) = viewerent;\n\t\t\tG_FLOAT(OFS_PARM1+0) = (int)((bits>>(SENDFLAGS_SHIFT+ 0)) & 0xffffff);\t//each float can only hold 24 bits before it forgets its lower bits.\n\t\t\tG_FLOAT(OFS_PARM1+1) = (int)((bits>>(SENDFLAGS_SHIFT+24)) & 0xffffff);\n\t\t\tG_FLOAT(OFS_PARM1+2) = (int)((bits>>(SENDFLAGS_SHIFT+48)) & 0xffffff);\n\t\t\tPR_ExecuteProgram(svprogfuncs, ent->xv->SendEntity);\n\t\t\tmod_result = G_INT(OFS_RETURN);\n\t\t}\n\n\t\tif (mod_result)\t//0 means not to tell the client about it.\n\t\t{\n\t\t\t//FIXME: don't overflow MAX_DATAGRAM... unless its too big anyway...\n\t\t\tif (msg->cursize + csqcmsgbuffer.cursize+5 >= msg->maxsize)\n\t\t\t{\n\t\t\t\t//warn when the message is larger than the user's max size..\n\t\t\t\tif (csqcmsgbuffer.cursize+5 > msg->maxsize)\n\t\t\t\t\tCon_ThrottlePrintf(&throttle, 0, \"CSQC update of entity %i(%s) is larger than user %s's maximum datagram size (%u > %u).\\n\", entnum, PR_GetString(svprogfuncs, ent->v->classname), client->name, csqcmsgbuffer.cursize, msg->maxsize-5);\n\n\t\t\t\tclient->pendingcsqcbits[entnum] = bits;\n\t\t\t\tif (csqcmsgbuffer.cursize < 32)\n\t\t\t\t\tbreak;\n\t\t\t\tcontinue;\t//might be able to fit a different ent in there.\n\t\t\t}\n\n\t\t\tif (lognum > maxlog)\n\t\t\t{\n\t\t\t\tif (maxlog == client->max_net_ents)\n\t\t\t\t{\n\t\t\t\t\tclient->pendingcsqcbits[entnum] = bits;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tSV_ExpandNackFrames(client, lognum+1, &frame);\n\t\t\t\tresend = frame->resend;\n\t\t\t\tmaxlog = frame->maxresend;\n\t\t\t}\n\t\t\tresend[lognum].entnum = entnum;\n\t\t\tresend[lognum].bits = 0;\n\t\t\tresend[lognum].flags = SENDFLAGS_PRESENT|bits;\n\t\t\tlognum++;\n\n\t\t\tif (!writtenheader)\n\t\t\t{\n\t\t\t\twrittenheader=true;\n\t\t\t\tMSG_WriteByte(msg, svcnumber);\n\t\t\t}\n\t\t\tSV_EmitDeltaEntIndex(msg, entnum, false, client->fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS);\n\t\t\tif (svcnumber == svcfte_csqcentities_sized)\t//optional extra length prefix.\n\t\t\t{\n\t\t\t\tif (!csqcmsgbuffer.cursize)\n\t\t\t\t\tCon_Printf(\"Warning: empty csqc packet on %s\\n\", PR_GetString(svprogfuncs, ent->v->classname));\n\t\t\t\tMSG_WriteShort(msg, csqcmsgbuffer.cursize);\n\t\t\t}\n\t\t\tSZ_Write(msg, csqcmsgbuffer.data, csqcmsgbuffer.cursize);\n\n\t\t\tclient->pendingcsqcbits[entnum] |= SENDFLAGS_PRESENT;\n//\t\t\tCon_Printf(\"Sending update packet %i\\n\", ent->entnum);\n\t\t}\n\t\telse if ((bits & SENDFLAGS_PRESENT) && !((int)ent->xv->pvsflags & PVSF_NOREMOVE))\n\t\t{\t//Don't want to send, but they have it already\n\n\n\t\t\tif (msg->cursize + 5 >= msg->maxsize)\n\t\t\t{\n\t\t\t\tclient->pendingcsqcbits[entnum] = bits;\n\t\t\t\tbreak;\t//we're overflowing, try removing next frame instead.\n\t\t\t}\n\n\t\t\tif (lognum > maxlog)\n\t\t\t{\n\t\t\t\tif (maxlog == client->max_net_ents)\n\t\t\t\t{\n\t\t\t\t\tclient->pendingcsqcbits[entnum] = bits;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tSV_ExpandNackFrames(client, lognum+1, &frame);\n\t\t\t\tresend = frame->resend;\n\t\t\t\tmaxlog = frame->maxresend;\n\t\t\t}\n\t\t\tresend[lognum].entnum = entnum;\n\t\t\tresend[lognum].bits = 0;\n\t\t\tresend[lognum].flags = SENDFLAGS_REMOVED;\n\t\t\tlognum++;\n\t\t\n\t\t\tif (!writtenheader)\n\t\t\t{\n\t\t\t\twrittenheader=true;\n\t\t\t\tMSG_WriteByte(msg, svcnumber);\n\t\t\t}\n\n\t\t\tSV_EmitDeltaEntIndex(msg, entnum, true, client->fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS);\n//\t\t\t\tCon_Printf(\"Sending remove 2 packet\\n\");\n\n//\t\t\tclient->pendingcsqcbits[entnum] = 0;\n\t\t}\n\t}\n\n\t//and now tail entities\n\tfor(; entnum < client->max_net_ents && entnum < sv.world.num_edicts; entnum++)\n\t{\n\t\tif (client->pendingcsqcbits[entnum] & (SENDFLAGS_PRESENT|SENDFLAGS_REMOVED))\n\t\t{\n\t\t\tif (!(client->pendingcsqcbits[entnum] & SENDFLAGS_REMOVED))\n\t\t\t{\t//while the original entity has NOREMOVE, only remove it if the remove is a resend\n\t\t\t\tif ((int)EDICT_NUM_PB(svprogfuncs, entnum)->xv->pvsflags & PVSF_NOREMOVE)\n\t\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (msg->cursize + 5 >= msg->maxsize)\n\t\t\t\tbreak;\t//we're overflowing, try removing next frame instead.\n\n\t\t\tif (lognum > maxlog)\n\t\t\t{\n\t\t\t\tif (maxlog == client->max_net_ents)\n\t\t\t\t\tbreak;\n\t\t\t\tSV_ExpandNackFrames(client, lognum+1, &frame);\n\t\t\t\tresend = frame->resend;\n\t\t\t\tmaxlog = frame->maxresend;\n\t\t\t}\n\t\t\tresend[lognum].entnum = entnum;\n\t\t\tresend[lognum].bits = 0;\n\t\t\tresend[lognum].flags = SENDFLAGS_REMOVED;\n\t\t\tlognum++;\n\n\t\t\tif (!writtenheader)\n\t\t\t{\n\t\t\t\twrittenheader=true;\n\t\t\t\tMSG_WriteByte(msg, svcnumber);\n\t\t\t}\n\n\t\t\tSV_EmitDeltaEntIndex(msg, entnum, true, client->fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS);\n//\t\t\t\tCon_Printf(\"Sending remove 2 packet\\n\");\n\n\t\t\tclient->pendingcsqcbits[entnum] = 0;\n\t\t}\n\t}\n\n\tif (writtenheader)\n\t\tMSG_WriteShort(msg, 0);\t//a 0 means no more.\n\n\tcsqcnuments = 0;\n\n\tframe->numresend = lognum;\n\n\t//prevent the qc from trying to use it at inopertune times.\n\tcsqcmsgbuffer.maxsize = 0;\n\tcsqcmsgbuffer.data = NULL;\n#endif\n}\n\nvoid SV_CSQC_DroppedPacket(client_t *client, int sequence)\n{\n\tint i;\n\tclient_frame_t *frame;\n\tif (!ISQWCLIENT(client) && !ISNQCLIENT(client))\n\t\treturn;\n\n\tif (!client->frameunion.frames)\n\t{\n\t\tCon_Printf(\"Server bug: No frames!\\n\");\n\t\treturn;\n\t}\n\tframe = &client->frameunion.frames[sequence & UPDATE_MASK];\n\n\t//skip it if we never generated that frame, to avoid pulling in stale data\n\tif (frame->sequence != sequence)\n\t{\n//\t\tCon_Printf(\"SV: Stale %i\\n\", sequence);\n\t\treturn;\n\t}\n\n\t//lost entities need flagging for a resend\n\tif (frame->numresend)\n\t{\n\t\tstruct resendinfo_s *resend = frame->resend;\n//\t\tCon_Printf(\"SV: Resend %i\\n\", sequence);\n\t\ti = frame->numresend;\n\t\twhile (i > 0)\n\t\t{\n\t\t\ti--;\n\t\t\tclient->pendingdeltabits[resend[i].entnum] |= resend[i].bits;\n\t\t\tclient->pendingcsqcbits[resend[i].entnum] |= resend[i].flags;\n\t\t}\n\t\tframe->numresend = 0;\t//don't resend the same info twice!\n\t}\n\t//lost stats do too\n\tif (frame->numresendstats)\n\t{\n\t\tclient_t *sp;\n\t\tunsigned short *n = frame->resendstats;\n\t\ti = frame->numresendstats;\n\t\twhile(i-->0)\n\t\t{\n\t\t\tunsigned short s = n[i];\n\t\t\tif (s & 0xf000)\n\t\t\t{\n\t\t\t\tsp = client;\n\t\t\t\twhile (s & 0xf000)\n\t\t\t\t{\n\t\t\t\t\ts -= 0x1000;\n\t\t\t\t\tsp = sp->controlled;\n\t\t\t\t}\n\t\t\t\tsp->pendingstats[s>>5u] |= 1u << (s & 0x1fu);\n\t\t\t}\n\t\t\telse\n\t\t\t\tclient->pendingstats[s>>5u] |= 1u << (s & 0x1fu);\n\t\t}\n\t\tframe->numresendstats = 0;\n\t}\n}\n\nvoid SV_AckEntityFrame(client_t *cl, int framenum)\n{\n\t//any not acked yet are assumed to be lost.\n\t//this may result in packetloss if the client received multiple packets between sending two outgoing packets.\n\tint frame = cl->lastsequence_acknowledged+1;\n\tif (framenum > cl->lastsequence_acknowledged)\n\t\tcl->lastsequence_acknowledged = framenum;\n\tif (framenum > frame + UPDATE_BACKUP)\n\t\tframenum = frame + UPDATE_BACKUP;\n\n#ifdef PEXT_CSQC\n\tfor (; frame < framenum; frame++)\n\t\tSV_CSQC_DroppedPacket(cl, frame);\n#endif\n}\n\nvoid SV_ReplaceEntityFrame(client_t *cl, int framenum)\n{\n\t//this packet is about to be overwritten, we can't track more.\n\t//we might as well pretend that it got acked, we can't track pings that far back anyway, just make sure it gets flagged as dropped.\n\t//due to how qw sequences are controlled by the client, the server may have skipped some frames. try to handle those too.\n\tint frame = cl->lastsequence_acknowledged+1;\n\tframenum -= UPDATE_BACKUP;\n\tif (framenum > cl->lastsequence_acknowledged)\n\t\tcl->lastsequence_acknowledged = framenum;\n\tif (framenum > frame + UPDATE_BACKUP)\n\t\tframenum = frame + UPDATE_BACKUP;\n\n#ifdef PEXT_CSQC\n\tfor (; frame <= framenum; frame++)\n\t\tSV_CSQC_DroppedPacket(cl, frame);\n#endif\n}\n\n/*\nvoid SV_CSQC_DropAll(client_t *client)\n{\n\tint i;\n\n\tif (client->fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS)\n\t{\n//\t\tCon_Printf(\"Reset all\\n\");\n\t\tclient->pendingdeltabits[0] = UF_REMOVE;\n\t}\n\n\tif (client->csqcactive)\t//we don't need this, but it might be a little faster.\n\t{\n\t\t//FIXME: handle any needed removes\n\t\tfor (i = 0; i < sv.world.num_edicts; i++)\n\t\t\tclient->pendingcsqcbits[i] |= SENDFLAGS_USABLE;\t//resend all\n\t}\n\n\t//we don't know which stats were on the wire, resend all. :(\n\tmemset(client->pendingstats, 0xff, sizeof(client->pendingstats));\n}\n*/\n//=============================================================================\n\n\n/*\n==================\nSV_WriteDelta\n\nWrites part of a packetentities message.\nCan delta from either a baseline or a previous packet_entity\n==================\n*/\nvoid SVQW_WriteDelta (entity_state_t *from, entity_state_t *to, sizebuf_t *msg, qboolean force, unsigned int protext, unsigned int ezext)\n{\n#ifdef PROTOCOLEXTENSIONS\n\tint evenmorebits=0;\n#endif\n\tint\t\tbits;\n\tint\t\ti;\n\tint fromeffects;\n\tcoorddata coordd[3];\n\tcoorddata angled[3];\n\tqbyte coordtype = msg->prim.coordtype;\n\n\tif (from == &((edict_t*)NULL)->baseline)\n\t\tfrom = &nullentitystate;\n\n\tif (ezext&EZPEXT1_FLOATENTCOORDS)\n\t\tcoordtype = COORDTYPE_FLOAT_32;\n\n// send an update\n\tbits = 0;\n\n\tif (coordtype != COORDTYPE_FLOAT_32)\n\t{\n\t\tfor (i=0 ; i<3 ; i++)\n\t\t{\n\t\t\tcoordd[i] = MSG_ToCoord(to->origin[i], coordtype);\n\t\t\tif (MSG_ToCoord(from->origin[i], coordtype).b4 != coordd[i].b4)\n\t\t\t\tbits |= U_ORIGIN1<<i;\n\t\t\telse\n\t\t\t\tto->origin[i] = from->origin[i];\n\t\t}\n\t}\n\telse\n\t{\n\t\tfor (i=0 ; i<3 ; i++)\n\t\t{\n\t\t\tcoordd[i] = MSG_ToCoord(to->origin[i], coordtype);\n\t\t\tif (to->origin[i] != from->origin[i])\n\t\t\t\tbits |= U_ORIGIN1<<i;\n\t\t}\n\t}\n\n\tangled[0] = MSG_ToAngle(to->angles[0], msg->prim.anglesize);\n\tif (MSG_ToAngle(from->angles[0], msg->prim.anglesize).b4 != angled[0].b4)\n\t\tbits |= U_ANGLE1;\n\telse\n\t\tto->angles[0] = from->angles[0];\n\n\tangled[1] = MSG_ToAngle(to->angles[1], msg->prim.anglesize);\n\tif (MSG_ToAngle(from->angles[1], msg->prim.anglesize).b4 != angled[1].b4)\n\t\tbits |= U_ANGLE2;\n\telse\n\t\tto->angles[1] = from->angles[1];\n\n\tangled[2] = MSG_ToAngle(to->angles[2], msg->prim.anglesize);\n\tif (MSG_ToAngle(from->angles[2], msg->prim.anglesize).b4 != angled[2].b4)\n\t\tbits |= U_ANGLE3;\n\telse\n\t\tto->angles[2] = from->angles[2];\n\n\tif ( to->colormap != from->colormap )\n\t\tbits |= U_COLORMAP;\n\n\tif ( to->skinnum != from->skinnum )\n\t\tbits |= U_SKIN;\n\n\tif ( to->frame != from->frame )\n\t\tbits |= U_FRAME;\n\n\n\tif (force && !(protext & PEXT_SPAWNSTATIC2))\n\t\tfromeffects = 0;\t//force is true if we're going from baseline\n\telse\t\t\t\t\t//old quakeworld protocols do not include effects in the baseline\n\t\tfromeffects = from->effects;\t//so old clients will see the effects baseline as 0\n\tif ((to->effects&0x00ff) != (fromeffects&0x00ff))\n\t\tbits |= U_EFFECTS;\n\tif ((to->effects&0xff00) != (fromeffects&0xff00) && (protext & PEXT_DPFLAGS))\n\t\tevenmorebits |= U_EFFECTS16;\n\n\tif (to->modelindex != from->modelindex)\n\t{\n\t\tbits |= U_MODEL;\n\t\tif (to->modelindex > 255)\n\t\t{\n\t\t\tif (protext & PEXT_MODELDBL)\n\t\t\t{\n\t\t\t\tif (to->modelindex > 512)\n\t\t\t\t\tbits &= ~U_MODEL;\n\t\t\t\tevenmorebits |= U_MODELDBL;\n\t\t\t}\n\t\t\telse\n\t\t\t\treturn;\n\t\t}\n\t}\n\n#ifdef PROTOCOLEXTENSIONS\n#ifdef U_SCALE\n\tif ( to->scale != from->scale && (protext & PEXT_SCALE))\n\t\tevenmorebits |= U_SCALE;\n#endif\n#ifdef U_TRANS\n\tif ( to->trans != from->trans && (protext & PEXT_TRANS))\n\t\tevenmorebits |= U_TRANS;\n#endif\n#ifdef U_FATNESS\n\tif ( to->fatness != from->fatness && (protext & PEXT_FATNESS))\n\t\tevenmorebits |= U_FATNESS;\n#endif\n\n\tif ( to->hexen2flags != from->hexen2flags && (protext & PEXT_HEXEN2))\n\t\tevenmorebits |= U_DRAWFLAGS;\n\tif ( to->abslight != from->abslight && (protext & PEXT_HEXEN2))\n\t\tevenmorebits |= U_ABSLIGHT;\n\n\tif ((to->colormod[0]!=from->colormod[0]||to->colormod[1]!=from->colormod[1]||to->colormod[2]!=from->colormod[2]) && (protext & PEXT_COLOURMOD))\n\t\tevenmorebits |= U_COLOURMOD;\n\n\tif (to->glowsize != from->glowsize)\n\t\tto->dpflags |= RENDER_GLOWTRAIL;\n\n\tif (to->dpflags != from->dpflags && (protext & PEXT_DPFLAGS))\n\t\tevenmorebits |= U_DPFLAGS;\n\n\tif ((to->tagentity != from->tagentity || to->tagindex != from->tagindex) && (protext & PEXT_SETATTACHMENT))\n\t\tevenmorebits |= U_TAGINFO;\n\n\tif ((to->light[0] != from->light[0] || to->light[1] != from->light[1] || to->light[2] != from->light[2] || to->light[3] != from->light[3] || to->lightstyle != from->lightstyle || to->lightpflags != from->lightstyle) && (protext & PEXT_DPFLAGS))\n\t\tevenmorebits |= U_LIGHT;\n#endif\n\n//\tif (to->solid)\n//\t\tbits |= U_SOLID;\n\n\tif (msg->cursize + 40 > msg->maxsize)\n\t{\t//not enough space in the buffer, don't send the entity this frame. (not sending means nothing changes, and it takes no bytes!!)\n\t\t*to = *from;\n\t\treturn;\n\t}\n\n\t//\n\t// write the message\n\t//\n\tif (!to->number)\n\t\tSV_Error (\"Unset entity number\");\n\n\tif (!bits && !evenmorebits && !force)\n\t\treturn;\t\t// nothing to send!\n\n#ifdef PROTOCOLEXTENSIONS\n\tif (to->number >= 512)\n\t{\n\t\tif (to->number >= 1024)\n\t\t{\n\t\t\tif (to->number >= 1024+512)\n\t\t\t\tevenmorebits |= U_ENTITYDBL;\n\n\t\t\tevenmorebits |= U_ENTITYDBL2;\n\t\t\tif (to->number >= 2048)\n\t\t\t\treturn;\n\t\t}\n\t\telse\n\t\t\tevenmorebits |= U_ENTITYDBL;\n\t}\n\n\tif (evenmorebits&0xff00)\n\t\tevenmorebits |= U_YETMORE;\n\tif (evenmorebits&0x00ff)\n\t\tbits |= U_EVENMORE;\n\tif (bits & 511)\n\t\tbits |= U_MOREBITS;\n#endif\n\n\ti = (to->number&511) | (bits&~511);\n\tif (i & U_REMOVE)\n\t\tSys_Error (\"U_REMOVE\");\n\tMSG_WriteShort (msg, i);\n\n\tif (bits & U_MOREBITS)\n\t\tMSG_WriteByte (msg, bits&255);\n#ifdef PROTOCOLEXTENSIONS\n\tif (bits & U_EVENMORE)\n\t\tMSG_WriteByte (msg, evenmorebits&255);\n\tif (evenmorebits & U_YETMORE)\n\t\tMSG_WriteByte (msg, (evenmorebits>>8)&255);\n#endif\n\n\tif (bits & U_MODEL)\n\t\tMSG_WriteByte (msg,\tto->modelindex&255);\n\telse if (evenmorebits & U_MODELDBL)\n\t\tMSG_WriteShort(msg, to->modelindex);\n\n\tif (bits & U_FRAME)\n\t\tMSG_WriteByte (msg, to->frame);\n\tif (bits & U_COLORMAP)\n\t\tMSG_WriteByte (msg, to->colormap);\n\tif (bits & U_SKIN)\n\t\tMSG_WriteByte (msg, to->skinnum&0xff);\n\tif (bits & U_EFFECTS)\n\t\tMSG_WriteByte (msg, to->effects&0x00ff);\n\tif (bits & U_ORIGIN1)\n\t\tSZ_Write(msg, &coordd[0], (coordtype&0xf));\n\tif (bits & U_ANGLE1)\n\t\tSZ_Write(msg, &angled[0], msg->prim.anglesize);\n\tif (bits & U_ORIGIN2)\n\t\tSZ_Write(msg, &coordd[1], (coordtype&0xf));\n\tif (bits & U_ANGLE2)\n\t\tSZ_Write(msg, &angled[1], msg->prim.anglesize);\n\tif (bits & U_ORIGIN3)\n\t\tSZ_Write(msg, &coordd[2], (coordtype&0xf));\n\tif (bits & U_ANGLE3)\n\t\tSZ_Write(msg, &angled[2], msg->prim.anglesize);\n\n#ifdef U_SCALE\n\tif (evenmorebits & U_SCALE)\n\t\tMSG_WriteByte (msg, (qbyte)(to->scale));\n#endif\n#ifdef U_TRANS\n\tif (evenmorebits & U_TRANS)\n\t\tMSG_WriteByte (msg, (qbyte)(to->trans));\n#endif\n#ifdef U_FATNESS\n\tif (evenmorebits & U_FATNESS)\n\t\tMSG_WriteChar (msg, to->fatness);\n#endif\n\n\tif (evenmorebits & U_DRAWFLAGS)\n\t\tMSG_WriteByte (msg, to->hexen2flags);\n\tif (evenmorebits & U_ABSLIGHT)\n\t\tMSG_WriteByte (msg, to->abslight);\n\n\tif (evenmorebits & U_COLOURMOD)\n\t{\n\t\tMSG_WriteByte (msg, to->colormod[0]);\n\t\tMSG_WriteByte (msg, to->colormod[1]);\n\t\tMSG_WriteByte (msg, to->colormod[2]);\n\t}\n\n\tif (evenmorebits & U_DPFLAGS)\n\t\tMSG_WriteByte (msg, to->dpflags);\n\n\tif (evenmorebits & U_TAGINFO)\n\t{\n\t\tMSG_WriteEntity (msg, to->tagentity);\n\t\tMSG_WriteShort (msg, to->tagindex);\n\t}\n\n\tif (evenmorebits & U_LIGHT)\n\t{\n\t\tMSG_WriteShort (msg, to->light[0]);\n\t\tMSG_WriteShort (msg, to->light[1]);\n\t\tMSG_WriteShort (msg, to->light[2]);\n\t\tMSG_WriteShort (msg, to->light[3]);\n\t\tMSG_WriteByte (msg, to->lightstyle);\n\t\tMSG_WriteByte (msg, to->lightpflags);\n\t}\n\n\tif (evenmorebits & U_EFFECTS16)\n\t\tMSG_WriteByte (msg, (to->effects&0xff00)>>8);\n}\n\n/*special flags which are slightly more compact. these are 'wasted' as part of the delta itself. These meanings will not be transmitted.*/\n#define UF_SV_REMOVE\tUF_16BIT_LERPTIME\t/*says we removed the entity in this frame*/\n#define UF_SV_RESET2\t\tUF_EXTEND1\t/*so new ents are reset 3 times to avoid weird baselines*/\n/*we need some extra bits for the predinfo section too...*/\n#define UF_SV_WEAPONFRAME_OLD\tUF_EXTEND2\n#define UF_SV_VIEWANGLES\tUF_EXTEND3\t/**/\n#define UF_SV_MOVETYPE\t\tUF_EXTEND4\t/*this flag isn't present in the header itself*/\n#define UF_SV_ALLFLAGS\t\t(UF_SV_REMOVE|UF_SV_RESET2|UF_SV_WEAPONFRAME_OLD|UF_SV_VIEWANGLES|UF_SV_MOVETYPE)\n\nstatic unsigned int SVFTE_DeltaPredCalcBits(entity_state_t *from, entity_state_t *to)\n{\n\tunsigned int bits = 0;\n\tif (from && from->u.q1.pmovetype != to->u.q1.pmovetype)\n\t\tbits |= UFP_MOVETYPE;\n\n\tif (to->u.q1.movement[0])\n\t\tbits |= UFP_FORWARD;\n\tif (to->u.q1.movement[1])\n\t\tbits |= UFP_SIDE;\n\tif (to->u.q1.movement[2])\n\t\tbits |= UFP_UP;\n\tif (to->u.q1.velocity[0])\n\t\tbits |= UFP_VELOCITYXY;\n\tif (to->u.q1.velocity[1])\n\t\tbits |= UFP_VELOCITYXY;\n\tif (to->u.q1.velocity[2])\n\t\tbits |= UFP_VELOCITYZ;\n\tif (to->u.q1.msec)\n\t\tbits |= UFP_MSEC;\n\n\treturn bits;\n}\n\nstatic unsigned int SVFTE_DeltaCalcBits(entity_state_t *from, qbyte *frombonedata, entity_state_t *to, qbyte *tobonedata, unsigned int pext2)\n{\n\tunsigned int bits = 0;\n\n\tif (from->u.q1.pmovetype != to->u.q1.pmovetype)\n\t\tbits |= UF_PREDINFO|UF_SV_MOVETYPE;\n\tif (from->u.q1.weaponframe != to->u.q1.weaponframe && !(pext2 & PEXT2_PREDINFO))\n\t\tbits |= UF_PREDINFO|UF_SV_WEAPONFRAME_OLD;\n\tif (to->u.q1.pmovetype)\n\t{\n\t\tif (SVFTE_DeltaPredCalcBits(from, to))\n\t\t\tbits |= UF_PREDINFO;\n\n\t\t/*moving players get extra data forced upon them which is not deltatracked*/\n\t\tif ((bits & UF_PREDINFO) && (from->u.q1.velocity[0] || from->u.q1.velocity[1] || from->u.q1.velocity[2]))\n\t\t{\n\t\t\t/*if we've got player movement then write the origin anyway*/\n\t\t\tbits |= UF_ORIGINXY | UF_ORIGINZ;\n\t\t\t/*and force angles too, if its not us*/\n\t\t\tif (host_client != svs.clients + to->number-1)\n\t\t\t\tbits |= UF_ANGLESXZ | UF_ANGLESY;\n\t\t}\n\t}\n\n\tif (to->origin[0] != from->origin[0])\n\t\tbits |= UF_ORIGINXY;\n\tif (to->origin[1] != from->origin[1])\n\t\tbits |= UF_ORIGINXY;\n\tif (to->origin[2] != from->origin[2])\n\t\tbits |= UF_ORIGINZ;\n\n\tif (to->angles[0] != from->angles[0])\n\t\tbits |= UF_ANGLESXZ;\n\tif (to->angles[1] != from->angles[1])\n\t\tbits |= UF_ANGLESY;\n\tif (to->angles[2] != from->angles[2])\n\t\tbits |= UF_ANGLESXZ;\n\n\n\tif (to->modelindex != from->modelindex)\n\t\tbits |= UF_MODEL;\n\tif (to->frame != from->frame)\n\t\tbits |= UF_FRAME;\n\tif (to->skinnum != from->skinnum)\n\t\tbits |= UF_SKIN;\n\tif (to->colormap != from->colormap)\n\t\tbits |= UF_COLORMAP;\n\tif (to->effects != from->effects)\n\t\tbits |= UF_EFFECTS;\n\tif (to->dpflags != from->dpflags)\n\t\tbits |= UF_FLAGS;\n\tif (to->solidsize != from->solidsize)\n\t\tbits |= UF_SOLID;\n\n\tif (to->scale != from->scale)\n\t\tbits |= UF_SCALE;\n\tif (to->trans != from->trans)\n\t\tbits |= UF_ALPHA;\n\tif (to->fatness != from->fatness)\n\t\tbits |= UF_FATNESS;\n\n\tif (to->hexen2flags != from->hexen2flags || to->abslight != from->abslight)\n\t\tbits |= UF_DRAWFLAGS;\n\n\tif (pext2 & PEXT2_NEWSIZEENCODING)\n\t{\t//don't flag it unless its actually present.\n\t\tif (to->bonecount != from->bonecount || (to->bonecount && memcmp(frombonedata+from->boneoffset, tobonedata+to->boneoffset, to->bonecount*sizeof(short)*7)))\n\t\t\tbits |= UF_BONEDATA;\n\t\tif (!to->bonecount && (to->basebone != from->basebone || to->baseframe != from->baseframe))\n\t\t\tbits |= UF_BONEDATA;\n\t}\n\n\tif (to->colormod[0]!=from->colormod[0]||to->colormod[1]!=from->colormod[1]||to->colormod[2]!=from->colormod[2])\n\t\tbits |= UF_COLORMOD;\n\n\tif (to->glowsize!=from->glowsize||to->glowcolour!=from->glowcolour||to->glowmod[0]!=from->glowmod[0]||to->glowmod[1]!=from->glowmod[1]||to->glowmod[2]!=from->glowmod[2])\n\t\tbits |= UF_GLOW;\n\n\tif (to->tagentity != from->tagentity || to->tagindex != from->tagindex)\n\t\tbits |= UF_TAGINFO;\n\n\tif (to->light[0] != from->light[0] || to->light[1] != from->light[1] || to->light[2] != from->light[2] || to->light[3] != from->light[3] || to->lightstyle != from->lightstyle || to->lightpflags != from->lightpflags)\n\t\tbits |= UF_LIGHT;\n\n\tif (to->u.q1.traileffectnum != from->u.q1.traileffectnum || to->u.q1.emiteffectnum != from->u.q1.emiteffectnum)\n\t\tbits |= UF_TRAILEFFECT;\n\n\tif (to->modelindex2 != from->modelindex2)\n\t\tbits |= UF_MODELINDEX2;\n\n\tif (to->u.q1.gravitydir[0] != from->u.q1.gravitydir[0] || to->u.q1.gravitydir[1] != from->u.q1.gravitydir[1])\n\t\tbits |= UF_GRAVITYDIR;\n\n\treturn bits;\n}\n\nstatic void SVFTE_WriteUpdate(unsigned int bits, entity_state_t *state, sizebuf_t *msg, unsigned int pext2, unsigned int ezext1, qbyte *boneptr)\n{\n\tunsigned int predbits = 0;\n\tif (bits & UF_SV_MOVETYPE)\n\t{\n\t\tbits &= ~UF_SV_MOVETYPE;\n\t\tpredbits |= UFP_MOVETYPE;\n\t}\n\tif (pext2 & PEXT2_PREDINFO)\n\t{\n\t\tif (bits & UF_SV_VIEWANGLES)\n\t\t{\n\t\t\tbits &= ~UF_SV_VIEWANGLES;\n\t\t\tbits |= UF_PREDINFO;\n\t\t\tpredbits |= UFP_VIEWANGLE;\n\t\t}\n\t}\n\telse\n\t{\n\t\tif (bits & UF_SV_VIEWANGLES)\n\t\t{\n\t\t\tbits &= ~UF_SV_VIEWANGLES;\n\t\t\tbits |= UF_PREDINFO;\n\t\t}\n\t\tif (bits & UF_SV_WEAPONFRAME_OLD)\n\t\t{\n\t\t\tbits &= ~UF_SV_WEAPONFRAME_OLD;\n\t\t\tpredbits |= UFP_WEAPONFRAME_OLD;\n\t\t}\n\t}\n\n\tif (!(pext2 & PEXT2_NEWSIZEENCODING))\t//was added at the same time\n\t\tbits &= ~UF_BONEDATA;\n\n#ifdef _DEBUG\n\tif (bits & UF_SV_ALLFLAGS)\t//if any of these are set here then we're bloating where we shouldn't be.\n\t\tHost_EndGame(\"Server-Only bits shouldn't be set - %#x\\n\", bits & UF_SV_ALLFLAGS);\n#endif\n\n\tif (pext2 & PEXT2_LERPTIME)\n\t{\n\t\tif (bits & (UF_FRAME|UF_MODEL|UF_RESET|UF_ANGLESXZ|UF_ANGLESY))\n\t\t\tif (state->lerpend > sv.world.physicstime)\n\t\t\t\tbits |= UF_16BIT_LERPTIME;\n\t}\n\telse\n\t{\n\t\t/*check if we need more precision*/\n\t\tif ((bits & UF_MODEL) && state->modelindex > 255)\n\t\t\tbits |= UF_16BIT_LERPTIME;\n\t\tif ((bits & UF_MODELINDEX2) && state->modelindex2 > 255)\n\t\t\tbits |= UF_16BIT_LERPTIME;\n\t\tif ((bits & UF_SKIN) && state->skinnum > 255)\n\t\t\tbits |= UF_16BIT_LERPTIME;\n\t\tif ((bits & UF_FRAME) && state->frame > 255)\n\t\t\tbits |= UF_16BIT_LERPTIME;\n\n\t\t/*convert effects bits to higher lengths if needed*/\n\t\tif (bits & UF_EFFECTS)\n\t\t{\n\t\t\tif (state->effects & 0xffff0000) /*both*/\n\t\t\t\tbits |= UF_EFFECTS | UF_EFFECTS2_OLD;\n\t\t\telse if (state->effects & 0x0000ff00) /*2 only*/\n\t\t\t\tbits = (bits & ~UF_EFFECTS) | UF_EFFECTS2_OLD;\n\t\t}\n\t}\n\tif (bits & 0xff000000)\n\t\tbits |= UF_EXTEND3;\n\tif (bits & 0x00ff0000)\n\t\tbits |= UF_EXTEND2;\n\tif (bits & 0x0000ff00)\n\t\tbits |= UF_EXTEND1;\n\n\tMSG_WriteByte(msg, (bits>>0) & 0xff);\n\tif (bits & UF_EXTEND1)\n\t\tMSG_WriteByte(msg, (bits>>8) & 0xff);\n\tif (bits & UF_EXTEND2)\n\t\tMSG_WriteByte(msg, (bits>>16) & 0xff);\n\tif (bits & UF_EXTEND3)\n\t\tMSG_WriteByte(msg, (bits>>24) & 0xff);\n\n\tif (bits & UF_FRAME)\n\t{\n\t\tif (pext2 & PEXT2_LERPTIME)\n\t\t\tMSG_WriteULEB128(msg, state->frame);\n\t\telse if (bits & UF_16BIT_LERPTIME)\n\t\t\tMSG_WriteShort(msg, state->frame);\n\t\telse\n\t\t\tMSG_WriteByte(msg, state->frame);\n\t}\n\n\tif (ezext1 & EZPEXT1_FLOATENTCOORDS)\n\t{\n\t\tif (bits & UF_ORIGINXY)\n\t\t{\n\t\t\tMSG_WriteFloat(msg, state->origin[0]);\n\t\t\tMSG_WriteFloat(msg, state->origin[1]);\n\t\t}\n\t\tif (bits & UF_ORIGINZ)\n\t\t\tMSG_WriteFloat(msg, state->origin[2]);\n\t}\n\telse\n\t{\n\t\tif (bits & UF_ORIGINXY)\n\t\t{\n\t\t\tMSG_WriteCoord(msg, state->origin[0]);\n\t\t\tMSG_WriteCoord(msg, state->origin[1]);\n\t\t}\n\t\tif (bits & UF_ORIGINZ)\n\t\t\tMSG_WriteCoord(msg, state->origin[2]);\n\t}\n\n\tif ((bits & UF_PREDINFO) && !(pext2 & PEXT2_PREDINFO))\n\t{\t/*if we have pred info, use more precise angles*/\n\t\tif (bits & UF_ANGLESXZ)\n\t\t{\n\t\t\tMSG_WriteAngle16(msg, state->angles[0]);\n\t\t\tMSG_WriteAngle16(msg, state->angles[2]);\n\t\t}\n\t\tif (bits & UF_ANGLESY)\n\t\t\tMSG_WriteAngle16(msg, state->angles[1]);\n\t}\n\telse\n\t{\n\t\tif (bits & UF_ANGLESXZ)\n\t\t{\n\t\t\tMSG_WriteAngle(msg, state->angles[0]);\n\t\t\tMSG_WriteAngle(msg, state->angles[2]);\n\t\t}\n\t\tif (bits & UF_ANGLESY)\n\t\t\tMSG_WriteAngle(msg, state->angles[1]);\n\t}\n\n\tif (pext2 & PEXT2_LERPTIME)\n\t{\n\t\tif (bits & UF_16BIT_LERPTIME)\n\t\t{\n\t\t\tint t = (state->lerpend - sv.world.physicstime)*1000;\n\t\t\tMSG_WriteULEB128(msg, max(0, t));\n\t\t}\n\t\tif (bits & UF_EFFECTS)\n\t\t\tMSG_WriteULEB128(msg, state->effects);\n//\t\tif (bits & UF_EFFECTS2)\n//\t\t\tMSG_WriteSomething(msg, state->something);\n\t}\n\telse\n\t{\n\t\tif ((bits & (UF_EFFECTS|UF_EFFECTS2_OLD)) == (UF_EFFECTS|UF_EFFECTS2_OLD))\n\t\t\tMSG_WriteLong(msg, state->effects);\n\t\telse if (bits & UF_EFFECTS2_OLD)\n\t\t\tMSG_WriteShort(msg, state->effects);\n\t\telse if (bits & UF_EFFECTS)\n\t\t\tMSG_WriteByte(msg, state->effects);\n\t}\n\n\tif (bits & UF_PREDINFO)\n\t{\n\t\t/*movetype is set above somewhere*/\n\t\tpredbits |= SVFTE_DeltaPredCalcBits(NULL, state);\n\n\t\tMSG_WriteByte(msg, predbits);\n\t\tif (predbits & UFP_FORWARD)\n\t\t\tMSG_WriteShort(msg, state->u.q1.movement[0]);\n\t\tif (predbits & UFP_SIDE)\n\t\t\tMSG_WriteShort(msg, state->u.q1.movement[1]);\n\t\tif (predbits & UFP_UP)\n\t\t\tMSG_WriteShort(msg, state->u.q1.movement[2]);\n\t\tif (predbits & UFP_MOVETYPE)\n\t\t\tMSG_WriteByte(msg, state->u.q1.pmovetype);\n\t\tif (predbits & UFP_VELOCITYXY)\n\t\t{\n\t\t\tMSG_WriteShort(msg, state->u.q1.velocity[0]);\n\t\t\tMSG_WriteShort(msg, state->u.q1.velocity[1]);\n\t\t}\n\t\tif (predbits & UFP_VELOCITYZ)\n\t\t\tMSG_WriteShort(msg, state->u.q1.velocity[2]);\n\t\tif (predbits & UFP_MSEC)\n\t\t\tMSG_WriteByte(msg, state->u.q1.msec);\n\t\tif (pext2 & PEXT2_PREDINFO)\n\t\t{\n\t\t\tif (predbits & UFP_VIEWANGLE)\n\t\t\t{\t/*if we have pred info, use more precise angles*/\n\t\t\t\tif (bits & UF_ANGLESXZ)\n\t\t\t\t{\n\t\t\t\t\tMSG_WriteShort(msg, state->u.q1.vangle[0]);\n\t\t\t\t\tMSG_WriteShort(msg, state->u.q1.vangle[2]);\n\t\t\t\t}\n\t\t\t\tif (bits & UF_ANGLESY)\n\t\t\t\t\tMSG_WriteShort(msg, state->u.q1.vangle[1]);\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (predbits & UFP_WEAPONFRAME_OLD)\n\t\t\t{\n\t\t\t\tif (state->u.q1.weaponframe > 127)\n\t\t\t\t{\n\t\t\t\t\tMSG_WriteByte(msg, 128 | (state->u.q1.weaponframe & 127));\n\t\t\t\t\tMSG_WriteByte(msg, state->u.q1.weaponframe>>7);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tMSG_WriteByte(msg, state->u.q1.weaponframe);\n\t\t\t}\n\t\t}\n\t}\n\n\tif (bits & UF_MODEL)\n\t{\n\t\tif (pext2 & PEXT2_LERPTIME)\n\t\t\tMSG_WriteULEB128(msg, state->modelindex);\n\t\telse if (bits & UF_16BIT_LERPTIME)\n\t\t\tMSG_WriteShort(msg, state->modelindex);\n\t\telse\n\t\t\tMSG_WriteByte(msg, state->modelindex);\n\t}\n\tif (bits & UF_SKIN)\n\t{\n\t\tif (pext2 & PEXT2_LERPTIME)\n\t\t\tMSG_WriteULEB128(msg, state->skinnum+64);\t//biased for negative content overrides.\n\t\telse if (bits & UF_16BIT_LERPTIME)\n\t\t\tMSG_WriteShort(msg, state->skinnum);\n\t\telse\n\t\t\tMSG_WriteByte(msg, state->skinnum);\n\t}\n\tif (bits & UF_COLORMAP)\n\t{\n\t\tif (pext2 & PEXT2_LERPTIME)\n\t\t\tMSG_WriteULEB128(msg, state->colormap);\n\t\telse\n\t\t\tMSG_WriteByte(msg, state->colormap & 0xff);\n\t}\n\n\tif (bits & UF_SOLID)\n\t{\n\t\tif (pext2 & PEXT2_NEWSIZEENCODING)\n\t\t{\n\t\t\tif (!state->solidsize)\n\t\t\t\tMSG_WriteByte(msg, 0);\n\t\t\telse if (state->solidsize == ES_SOLID_BSP)\n\t\t\t\tMSG_WriteByte(msg, 1);\n\t\t\telse if (state->solidsize == ES_SOLID_HULL1)\n\t\t\t\tMSG_WriteByte(msg, 2);\n\t\t\telse if (state->solidsize == ES_SOLID_HULL2)\n\t\t\t\tMSG_WriteByte(msg, 3);\n\t\t\telse if (!ES_SOLID_HAS_EXTRA_BITS(state->solidsize))\n\t\t\t{\n\t\t\t\tMSG_WriteByte(msg, 16);\n\t\t\t\tMSG_WriteSize16(msg, state->solidsize);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tMSG_WriteByte(msg, 32);\n\t\t\t\tMSG_WriteLong(msg, state->solidsize);\n\t\t\t}\n\t\t}\n\t\telse\n\t\t\tMSG_WriteSize16(msg, state->solidsize);\n\t}\n\n\tif (bits & UF_FLAGS)\n\t{\n\t\tif (pext2 & PEXT2_LERPTIME)\n\t\t\tMSG_WriteULEB128(msg, state->dpflags);\n\t\telse\n\t\t\tMSG_WriteByte(msg, state->dpflags);\n\t}\n\n\tif (bits & UF_ALPHA)\n\t\tMSG_WriteByte(msg, state->trans);\n\tif (bits & UF_SCALE)\n\t\tMSG_WriteByte(msg, state->scale);\n\tif (bits & UF_BONEDATA)\n\t{\n\t\tshort *bonedata;\n\t\tint i;\n\t\tqbyte bfl = 0;\n\t\tif (state->bonecount && boneptr)\n\t\t\tbfl |= 0x80;\n\t\tif (state->basebone || state->baseframe)\n\t\t\tbfl |= 0x40;\n\t\tMSG_WriteByte(msg, bfl);\n\t\tif (bfl & 0x80)\n\t\t{\n\t\t\t//this is NOT finalized\n\t\t\tMSG_WriteByte(msg, state->bonecount);\n\t\t\tbonedata = (short*)(boneptr + state->boneoffset);\n\t\t\tfor (i = 0; i < state->bonecount*7; i++)\n\t\t\t\tMSG_WriteShort(msg, bonedata[i]);\n\t\t}\n\t\tif (bfl & 0x40)\n\t\t{\n\t\t\tif (pext2 & PEXT2_LERPTIME)\n\t\t\t{\n\t\t\t\tMSG_WriteULEB128(msg, state->basebone);\n\t\t\t\tMSG_WriteULEB128(msg, state->baseframe);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tMSG_WriteByte(msg, state->basebone);\n\t\t\t\tMSG_WriteShort(msg, state->baseframe);\n\t\t\t}\n\t\t}\n\t}\n\tif (bits & UF_DRAWFLAGS)\n\t{\n\t\tMSG_WriteByte(msg, state->hexen2flags);\n\t\tif ((state->hexen2flags & MLS_MASK) >= MLS_ADDLIGHT)\n\t\t\tMSG_WriteByte(msg, state->abslight);\n\t}\n\tif (bits & UF_TAGINFO)\n\t{\n\t\tMSG_WriteEntity(msg, state->tagentity);\n\t\tif (pext2 & PEXT2_LERPTIME)\n\t\t\tMSG_WriteULEB128(msg, state->tagindex+1);\t//biased when sending to allow for our -1==portal thing\n\t\telse\n\t\t\tMSG_WriteByte(msg, state->tagindex&0xff);\n\t}\n\tif (bits & UF_LIGHT)\n\t{\n\t\tMSG_WriteShort (msg, state->light[0]);\n\t\tMSG_WriteShort (msg, state->light[1]);\n\t\tMSG_WriteShort (msg, state->light[2]);\n\t\tMSG_WriteShort (msg, state->light[3]);\n\t\tif (pext2 & PEXT2_LERPTIME)\n\t\t\tMSG_WriteULEB128(msg, state->lightstyle);\n\t\telse\n\t\t\tMSG_WriteByte (msg, state->lightstyle);\n\t\tMSG_WriteByte (msg, state->lightpflags);\n\t}\n\tif (bits & UF_TRAILEFFECT)\n\t{\n\t\tif (pext2 & PEXT2_LERPTIME)\n\t\t{\n\t\t\tMSG_WriteULEB128(msg, state->u.q1.traileffectnum);\n\t\t\tMSG_WriteULEB128(msg, state->u.q1.emiteffectnum);\n\t\t}\n\t\telse if (state->u.q1.emiteffectnum)\n\t\t{\n\t\t\tMSG_WriteShort(msg, (state->u.q1.traileffectnum & 0x3fff) | 0x8000);\n\t\t\tMSG_WriteShort(msg, (state->u.q1.emiteffectnum & 0x3fff));\n\t\t}\n\t\telse\n\t\t\tMSG_WriteShort(msg, (state->u.q1.traileffectnum & 0x3fff));\n\t}\n\n\tif (bits & UF_COLORMOD)\n\t{\n\t\tMSG_WriteByte(msg, state->colormod[0]);\n\t\tMSG_WriteByte(msg, state->colormod[1]);\n\t\tMSG_WriteByte(msg, state->colormod[2]);\n\t}\n\tif (bits & UF_GLOW)\n\t{\n\t\tMSG_WriteByte(msg, state->glowsize);\n\t\tMSG_WriteByte(msg, state->glowcolour);\n\t\tMSG_WriteByte(msg, state->glowmod[0]);\n\t\tMSG_WriteByte(msg, state->glowmod[1]);\n\t\tMSG_WriteByte(msg, state->glowmod[2]);\n\t}\n\tif (bits & UF_FATNESS)\n\t\tMSG_WriteChar(msg, state->fatness);\n\tif (bits & UF_MODELINDEX2)\n\t{\n\t\tif (pext2 & PEXT2_LERPTIME)\n\t\t\tMSG_WriteULEB128(msg, state->modelindex2);\n\t\telse if (bits & UF_16BIT_LERPTIME)\n\t\t\tMSG_WriteShort(msg, state->modelindex2);\n\t\telse\n\t\t\tMSG_WriteByte(msg, state->modelindex2);\n\t}\n\n\tif (bits & UF_GRAVITYDIR)\n\t{\n\t\tMSG_WriteByte(msg, state->u.q1.gravitydir[0]);\n\t\tMSG_WriteByte(msg, state->u.q1.gravitydir[1]);\n\t}\n}\n\n/*dump out the delta from baseline (used for baselines and statics, so has no svc)*/\nvoid SVFTE_EmitBaseline(entity_state_t *to, qboolean numberisimportant, sizebuf_t *msg, unsigned int pext2, unsigned int ezext)\n{\n\tunsigned int bits;\n\tif (numberisimportant)\n\t\tMSG_WriteEntity(msg, to->number);\n\tbits = UF_RESET | SVFTE_DeltaCalcBits(&nullentitystate, NULL, to, NULL, pext2);\n\tSVFTE_WriteUpdate(bits, to, msg, pext2, ezext, NULL);\n}\n\n/*SVFTE_EmitPacketEntities\nWrites changed entities to the client.\nChanged ent states will be tracked, even if they're not sent just yet, dropped packets will also re-flag dropped delta bits\nOnly what changed is tracked, via bitmask, its previous value is never tracked.\n*/\nqboolean SVFTE_EmitPacketEntities(client_t *client, packet_entities_t *to, sizebuf_t *msg)\n{\n\tedict_t *e;\n\tentity_state_t *o, *n;\n\tunsigned int i;\n\tunsigned int j;\n\tunsigned int bits;\n\tstruct resendinfo_s *resend;\n\tunsigned int outno, outmax;\n\tint sequence;\n\tqbyte *oldbonedata;\n\tunsigned int maxbonedatasize;\n\tqboolean overflow = false;\n\tclient_t *cl;\n\tfloat age;\n\tclient_frame_t *frame;\n\n\tif (!client->pendingdeltabits)\n\t\treturn false;\n\t\n\tif (client->delta_sequence < 0)\n\t\tclient->pendingdeltabits[0] = UF_SV_REMOVE;\n\n\t//if we're clearing the list and starting from scratch, just wipe all lingering state\n\tif (client->pendingdeltabits[0] & UF_SV_REMOVE)\n\t{\n\t\tfor (j = 0; j < client->sentents.num_entities; j++)\n\t\t{\n\t\t\tclient->sentents.entities[j].number = 0;\n\t\t\tclient->pendingdeltabits[j] = 0;\n\t\t}\n\t\tclient->pendingdeltabits[0] = UF_SV_REMOVE;\n\t}\n\n\t//expand client's entstate list\n\tif (to->num_entities)\n\t{\n\t\tj = to->entities[to->num_entities-1].number+1;\n\t\tif (j > client->sentents.max_entities)\n\t\t{\n\t\t\tclient->sentents.entities = BZ_Realloc(client->sentents.entities, sizeof(*client->sentents.entities) * j);\n\t\t\tmemset(&client->sentents.entities[client->sentents.max_entities], 0, sizeof(client->sentents.entities[0]) * (j - client->sentents.max_entities));\n\t\t\tclient->sentents.max_entities = j;\n\t\t}\n\t\twhile(j > client->sentents.num_entities)\n\t\t{\n\t\t\tclient->sentents.entities[client->sentents.num_entities].number = 0;\n\t\t\tclient->sentents.num_entities++;\n\t\t}\n\t}\n\n\t//orphan and regenerate\n\toldbonedata = client->sentents.bonedata;\n\tmaxbonedatasize = client->sentents.bonedatamax;\n\tif (client->sentents.bonedatacur)\n\t{\n\t\tclient->sentents.bonedata = BZ_Malloc(maxbonedatasize);\n\t\tclient->sentents.bonedatacur = 0;\n\t\tclient->sentents.bonedatamax = maxbonedatasize;\n\t}\n\telse\n\t{\n\t\tclient->sentents.bonedata = NULL;\n\t\tclient->sentents.bonedatacur = 0;\n\t\tclient->sentents.bonedatamax = 0;\n\t}\n\n\t/*figure out the entitys+bits that changed (removed and active)*/\n\tfor (i = 0, j = 0; i < to->num_entities; i++)\n\t{\n\t\tn = &to->entities[i];\n\t\t/*gaps are dead entities*/\n\t\tfor (; j < n->number; j++)\n\t\t{\n\t\t\to = &client->sentents.entities[j];\n\t\t\tif (o->number)\n\t\t\t{\n\t\t\t\te = EDICT_NUM_PB(svprogfuncs, o->number);\n\t\t\t\tif (!((int)e->xv->pvsflags & PVSF_NOREMOVE))\n\t\t\t\t{\n\t\t\t\t\tclient->pendingdeltabits[j] = UF_SV_REMOVE;\n\t\t\t\t\to->number = 0; /*dead*/\n\t\t\t\t\to->bonecount = 0; /*don't waste cycles*/\n\t\t\t\t}\n\t\t\t\telse if (o->bonecount)\n\t\t\t\t{\n\t\t\t\t\tshort *srcbdata = (short*)(oldbonedata + o->boneoffset);\n\t\t\t\t\tshort *bonedata = AllocateBoneSpace(&client->sentents, o->bonecount, &o->boneoffset);\n\t\t\t\t\tmemcpy(bonedata, srcbdata, sizeof(short)*7*o->bonecount);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\to = &client->sentents.entities[j];\n\t\tif (!o->number)\n\t\t{\n\t\t\t/*flag it for reset, we can add the extra bits later once we get around to sending it*/\n\t\t\tclient->pendingdeltabits[j] = UF_RESET | UF_SV_RESET2;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t//its valid, make sure we don't have a stale/resent remove, and do a cheap reset due to uncertainty.\n\t\t\tif (client->pendingdeltabits[j] & UF_SV_REMOVE)\n\t\t\t\tclient->pendingdeltabits[j] = (client->pendingdeltabits[j] & ~UF_SV_REMOVE) | UF_SV_RESET2;\n\t\t\tclient->pendingdeltabits[j] |= SVFTE_DeltaCalcBits(o, oldbonedata, n, to->bonedata, client->fteprotocolextensions2);\n\t\t\t//even if prediction is disabled, we want to force velocity info to be sent for the local player. This is used by view bob and things.\n\t\t\tif (client->edict && j == client->edict->entnum && (n->u.q1.velocity[0] || n->u.q1.velocity[1] || n->u.q1.velocity[2]))\n\t\t\t\tclient->pendingdeltabits[j] |= UF_PREDINFO;\n\n\t\t\t//spectators(and mvds) should be told the actual view angles of the person they're trying to track\n\t\t\tif (j <= sv.allocated_client_slots && (!client->edict || j == client->spec_track))\n//\t\t\t\tif (client->pendingentbits[j])\n\t\t\t\t{\n\t\t\t\t\tif (o->u.q1.vangle[0] != n->u.q1.vangle[0] || o->u.q1.vangle[2] != n->u.q1.vangle[2])\n\t\t\t\t\t\tclient->pendingdeltabits[j] |= UF_ANGLESXZ;\n\t\t\t\t\tif (o->u.q1.vangle[1] != n->u.q1.vangle[1])\n\t\t\t\t\t\tclient->pendingdeltabits[j] |= UF_ANGLESY;\n\t\t\t\t\tclient->pendingdeltabits[j] |= UF_SV_VIEWANGLES;\n\t\t\t\t}\n\t\t}\n\t\t*o = *n;\n\t\tif (o->bonecount)\n\t\t{\n\t\t\tshort *bonedata = AllocateBoneSpace(&client->sentents, o->bonecount, &o->boneoffset);\n\t\t\tshort *srcbdata = (short*)(to->bonedata + n->boneoffset);\n\t\t\tmemcpy(bonedata, srcbdata, sizeof(short)*7*o->bonecount);\n\t\t}\n\t\tj++;\n\t}\n\n\t/*gaps are dead entities*/\n\tfor (; j < client->sentents.num_entities; j++)\n\t{\n\t\to = &client->sentents.entities[j];\n\t\tif (o->number)\n\t\t{\n\t\t\te = EDICT_NUM_PB(svprogfuncs, o->number);\n\t\t\tif (!((int)e->xv->pvsflags & PVSF_NOREMOVE))\n\t\t\t{\n\t\t\t\tclient->pendingdeltabits[j] = UF_SV_REMOVE;\n\t\t\t\to->number = 0; /*dead*/\n\t\t\t\to->bonecount = 0; /*don't waste cycles*/\n\t\t\t}\n\t\t\telse if (o->bonecount)\n\t\t\t{\n\t\t\t\tshort *srcbdata = (short*)(oldbonedata + o->boneoffset);\n\t\t\t\tshort *bonedata = AllocateBoneSpace(&client->sentents, o->bonecount, &o->boneoffset);\n\t\t\t\tmemcpy(bonedata, srcbdata, sizeof(short)*7*o->bonecount);\n\t\t\t}\n\t\t}\n\t}\n\n\tZ_Free(oldbonedata);\n\n\n\n\tif (ISNQCLIENT(client))\n\t\tsequence = client->netchan.outgoing_unreliable;\n\telse\n\t\tsequence = client->netchan.incoming_sequence;\n\tframe = &client->frameunion.frames[sequence & UPDATE_MASK];\n\n\t/*cache frame info*/\n\tresend = frame->resend;\n\toutno = 0;\n\toutmax = frame->maxresend;\n\n\n\tif (client->fteprotocolextensions2 & PEXT2_VRINPUTS)\n\t{\n\t\tclient_frame_t *ackedframe = &client->frameunion.frames[client->delta_sequence & UPDATE_MASK];\n\t\tclient_t *seat;\n\n\t\tfor (i = 0, seat = client; i < MAX_SPLITS && seat; i++, seat = seat->controlled)\n\t\t{\n\t\t\tif (ackedframe->baseanglelocked[i] != seat->baseanglelock ||\n\t\t\t\t\tackedframe->baseangles[i][0] != seat->baseangles[0] ||\n\t\t\t\t\tackedframe->baseangles[i][1] != seat->baseangles[1] ||\n\t\t\t\t\tackedframe->baseangles[i][2] != seat->baseangles[2])\n\t\t\t{\t//change the base angle, and force the client to it.\n\t\t\t\t//sent every frame its valid for, because we really don't want packetloss here.\n\t\t\t\tint fl = 0, j;\n\t\t\t\tfor (j = 0; j < 3; j++)\n\t\t\t\t\tif (seat->baseangles[j])\n\t\t\t\t\t\tfl |= (1u<<j);\n\t\t\t\tif (ackedframe->baseanglelocked[i] != seat->baseanglelock)\n\t\t\t\t\tfl |= 8;\n\n\t\t\t\tif (seat->seat)\n\t\t\t\t{\n\t\t\t\t\tMSG_WriteByte (msg, svcfte_choosesplitclient);\n\t\t\t\t\tMSG_WriteByte (msg, seat->seat);\n\t\t\t\t}\n\t\t\t\tMSG_WriteByte (msg, svcfte_setanglebase);\n\t\t\t\tMSG_WriteByte(msg, fl);\n\t\t\t\tfor (j = 0; j < 3; j++)\n\t\t\t\t\tif (fl&(1u<<j))\n\t\t\t\t\t\tMSG_WriteShort(msg, seat->baseangles[j]);\n\t\t\t}\n\t\t\tVectorCopy(seat->baseangles, frame->baseangles[i]);\n\t\t\tframe->baseanglelocked[i] = seat->baseanglelock;\n\t\t}\n\t}\n\n\tif (msg->cursize + 52 <= msg->maxsize)\n\t{\n\t\t/*start writing the packet*/\n\t\tMSG_WriteByte (msg, svcfte_updateentities);\n\t\tif (ISNQCLIENT(client) && (client->fteprotocolextensions2 & PEXT2_PREDINFO))\n\t\t{\n\t\t\tMSG_WriteShort(msg, client->last_sequence&0xffff);\n\t\t}\n\t//\tCon_Printf(\"Gen sequence %i\\n\", sequence);\n\t\tMSG_WriteFloat(msg, sv.world.physicstime);\n\n\t\tif (client->pendingdeltabits[0] & UF_SV_REMOVE)\n\t\t{\n\t\t\tSV_EmitDeltaEntIndex(msg, 0, true, true);\n\t\t\tresend[outno].bits = UF_SV_REMOVE;\n\t\t\tresend[outno].flags = 0;\n\t\t\tresend[outno++].entnum = 0;\n\n\t\t\tclient->pendingdeltabits[0] &= ~UF_SV_REMOVE;\n\t\t}\n\t\tfor(j = 1; j < client->sentents.num_entities; j++)\n\t\t{\n\t\t\tbits = client->pendingdeltabits[j];\n\t\t\tif (!(bits & ~UF_SV_RESET2))\t//skip while there's nothing to send (skip reset2 if there's no other changes, its only to reduce chances of the client getting 'new' entities containing just an origin)*/\n\t\t\t\tcontinue;\n\t\t\tif (msg->cursize + 52 > msg->maxsize)\n\t\t\t{\n\t\t\t\toverflow = true;\n\t\t\t\tbreak; /*give up if it gets full. FIXME: bone data is HUGE.*/\n\t\t\t}\n\t\t\tif (outno >= outmax)\n\t\t\t{\t//expand the frames. may need some copying...\n\t\t\t\tif (outmax == client->max_net_ents)\n\t\t\t\t\tbreak;\n\t\t\t\tSV_ExpandNackFrames(client, outno+1, &frame);\n\t\t\t\tresend = frame->resend;\n\t\t\t\toutmax = frame->maxresend;\n\t\t\t}\n\n\t\t\tif (bits & UF_SV_REMOVE)\n\t\t\t{\t//if reset is set, then reset was set eroneously.\n\t\t\t\tSV_EmitDeltaEntIndex(msg, j, true, true);\n\t\t\t\tresend[outno].bits = UF_SV_REMOVE;\n\t//\t\t\tCon_Printf(\"REMOVE %i @ %i\\n\", j, sequence);\n\t\t\t}\n\t\t\telse if (client->sentents.entities[j].number) /*only send a new copy of the ent if they actually have one already*/\n\t\t\t{\n\t\t\t\t//if we didn't reach the end in the last packet, start at that point to avoid spam\n\t\t\t\t//player slots are exempt from this, so they are in every packet (strictly speaking only the local player 'needs' this, but its nice to have it for high-priority targets too)\n\t\t\t\tif (j < client->nextdeltaindex && j > svs.allocated_client_slots)\n\t\t\t\t\tcontinue;\n\n\t\t\t\tif (bits & UF_SV_RESET2)\n\t\t\t\t{\n\t\t\t\t\t/*if reset2, then this is the second packet sent to the client and should have a forced reset (but which isn't tracked)*/\n\t\t\t\t\tresend[outno].bits = bits & ~UF_SV_RESET2;\n\t\t\t\t\tbits = UF_RESET | SVFTE_DeltaCalcBits(&EDICT_NUM_PB(svprogfuncs, j)->baseline, NULL, &client->sentents.entities[j], client->sentents.bonedata, client->fteprotocolextensions2);\n\t//\t\t\t\tCon_Printf(\"RESET2 %i @ %i\\n\", j, sequence);\n\t\t\t\t}\n\t\t\t\telse if (bits & UF_RESET)\n\t\t\t\t{\n\t\t\t\t\t/*flag the entity for the next packet, so we always get two resets when it appears, to reduce the effects of packetloss on seeing rockets etc*/\n\t\t\t\t\tclient->pendingdeltabits[j] = UF_SV_RESET2;\n\t\t\t\t\tbits = UF_RESET | SVFTE_DeltaCalcBits(&EDICT_NUM_PB(svprogfuncs, j)->baseline, NULL, &client->sentents.entities[j], client->sentents.bonedata, client->fteprotocolextensions2);\n\t\t\t\t\tresend[outno].bits = UF_RESET;\n\t//\t\t\t\tCon_Printf(\"RESET %i @ %i\\n\", j, sequence);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tresend[outno].bits = bits;\n\n\t\t\t\tSV_EmitDeltaEntIndex(msg, j, false, true);\n\t\t\t\tSVFTE_WriteUpdate(bits, &client->sentents.entities[j], msg, client->fteprotocolextensions2, client->ezprotocolextensions1, client->sentents.bonedata);\n\t\t\t}\n\n\t\t\tclient->pendingdeltabits[j] = 0;\n\n\t\t\tresend[outno].flags = 0;\n\t\t\tresend[outno++].entnum = j;\n\t\t}\n\t\tMSG_WriteShort(msg, 0);\n\t}\n\n\tif (j == client->sentents.num_entities) //looks like we sent them all\n\t\tclient->nextdeltaindex = 0;\t//start afresh with the next packet.\n\telse\n\t\tclient->nextdeltaindex = j;\t//we overflowed or something, start going round-robin\n\n\tframe->numresend = outno;\n\tframe->sequence = sequence;\n\n\tframe->laggedtime = sv.time;\n\tfor (i = 0; i < to->num_entities; i++)\n\t{\n\t\tn = &to->entities[i];\n\t\tj = n->number-1;\n\t\tif (j >= sv.allocated_client_slots)\n\t\t\tbreak;\t//don't track non-player slots.\n\n\t\tcl = &svs.clients[j];\n\n\t\t//states of other players are actually old.\n\t\t//by the time we receive the other player's move, this stuff will be outdated and we don't know when that will actually be.\n\t\t//so (cheaply) guess where they're really meant to be if they're running at a lower framerate.\n\t\tif (!cl->name[0] || cl->protocol == SCP_BAD)\t//is bot\n\t\t\tage = 0;//= sv.time - sv.world.physicstime; //FIXME\n\t\telse\n\t\t\tage = sv.time - sv.world.physicstime;\n\t\tage = bound(0, age, 0.1);\n\n\t\tVectorMA(n->origin, (sv.time - cl->localtime)/8.0, n->u.q1.velocity, frame->laggedplayer[j].origin);\n\t\tVectorCopy(n->angles, frame->laggedplayer[j].angles);\n\t\t//FIXME: add framestate_t info.\n\t\tframe->laggedplayer[j].present = true;\n\t}\n\n\treturn overflow;\n}\n\n/*\n=============\nSVQW_EmitPacketEntities\n\nWrites a delta update of a packet_entities_t to the message.\n\ndeltaing is performed from one set of entity states directly to the next\n=============\n*/\nvoid SVQW_EmitPacketEntities (client_t *client, packet_entities_t *to, sizebuf_t *msg)\n{\n\tedict_t\t*ent;\n\tclient_frame_t\t*fromframe;\n\tpacket_entities_t *from;\n\tint\t\toldindex, newindex;\n\tint\t\toldnum, newnum;\n\tint\t\toldmax;\n\n\t// this is the frame that we are going to delta update from\n\tif (client->delta_sequence != -1)\n\t{\n\t\tfromframe = &client->frameunion.frames[client->delta_sequence & UPDATE_MASK];\n\t\tfrom = &fromframe->qwentities;\n\t\toldmax = from->num_entities;\n\n\t\tMSG_WriteByte (msg, svc_deltapacketentities);\n\t\tMSG_WriteByte (msg, client->delta_sequence);\n\t}\n\telse\n\t{\n\t\toldmax = 0;\t// no delta update\n\t\tfrom = NULL;\n\n\t\tMSG_WriteByte (msg, svc_packetentities);\n\t}\n\n\tnewindex = 0;\n\toldindex = 0;\n//Con_Printf (\"---%i to %i ----\\n\", client->delta_sequence & UPDATE_MASK\n//\t\t\t, client->netchan.outgoing_sequence & UPDATE_MASK);\n\twhile (newindex < to->num_entities || oldindex < oldmax)\n\t{\n\t\tnewnum = newindex >= to->num_entities ? 9999 : to->entities[newindex].number;\n\t\toldnum = oldindex >= oldmax ? 9999 : from->entities[oldindex].number;\n\n\t\tif (newnum == oldnum)\n\t\t{\t// delta update from old position\n//Con_Printf (\"delta %i\\n\", newnum);\n#ifdef PROTOCOLEXTENSIONS\n\t\t\tSVQW_WriteDelta (&from->entities[oldindex], &to->entities[newindex], msg, false, client->fteprotocolextensions, client->ezprotocolextensions1);\n#else\n\t\t\tSVQW_WriteDelta (&from->entities[oldindex], &to->entities[newindex], msg, false);\n#endif\n\t\t\toldindex++;\n\t\t\tnewindex++;\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (newnum < oldnum)\n\t\t{\t// this is a new entity, send it from the baseline\n\t\t\tif (svprogfuncs)\n\t\t\t\tent = EDICT_NUM_UB(svprogfuncs, newnum);\n\t\t\telse\n\t\t\t\tent = NULL;\n//Con_Printf (\"baseline %i\\n\", newnum);\n#ifdef PROTOCOLEXTENSIONS\n\t\t\tSVQW_WriteDelta (&ent->baseline, &to->entities[newindex], msg, true, client->fteprotocolextensions, client->ezprotocolextensions1);\n#else\n\t\t\tSVQW_WriteDelta (&ent->baseline, &to->entities[newindex], msg, true);\n#endif\n\t\t\tnewindex++;\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (newnum > oldnum)\n\t\t{\t// the old entity isn't present in the new message\n//Con_Printf (\"remove %i\\n\", oldnum);\n\t\t\tif (oldnum >= 512)\n\t\t\t{\n\t\t\t\t//yup, this is expensive.\n\t\t\t\tMSG_WriteShort (msg, (oldnum&511) | U_REMOVE|U_MOREBITS);\n\t\t\t\tMSG_WriteByte (msg, U_EVENMORE);\n\t\t\t\tif (oldnum >= 1024)\n\t\t\t\t{\n\t\t\t\t\tif (oldnum >= 1024+512)\n\t\t\t\t\t\tMSG_WriteByte (msg, U_ENTITYDBL|U_ENTITYDBL2);\n\t\t\t\t\telse\n\t\t\t\t\t\tMSG_WriteByte (msg, U_ENTITYDBL2);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tMSG_WriteByte (msg, U_ENTITYDBL);\n\t\t\t}\n\t\t\telse\n\t\t\t\tMSG_WriteShort (msg, (oldnum&511) | U_REMOVE);\n\n\t\t\toldindex++;\n\t\t\tcontinue;\n\t\t}\n\t}\n\n\tif (newindex > to->max_entities)\n\t\tCon_Printf(\"Exceeded max entities\\n\");\n\n\tMSG_WriteShort (msg, 0);\t// end of packetentities\n}\n#ifdef NQPROT\nunsigned int SVDP_CalcDelta(entity_state_t *from, qbyte *frombonedatabase, entity_state_t *to, qbyte *tobonedatabase)\n{\n\tunsigned int bits = 0;\n\t//E5_FULLUPDATE is handled elsewhere\n\t//E5_EXTEND* is handled elsewhere\n\tif (!VectorEquals(from->origin, to->origin))\n\t\tbits |= E5_ORIGIN;\n\tif (!VectorEquals(from->angles, to->angles))\n\t\tbits |= E5_ANGLES;\n\tif (from->modelindex != to->modelindex)\n\t\tbits |= E5_MODEL;\n\tif (from->frame != to->frame)\n\t\tbits |= E5_FRAME;\n\tif (from->skinnum != to->skinnum)\n\t\tbits |= E5_SKIN;\n\tif (from->effects != to->effects)\n\t\tbits |= E5_EFFECTS;\n\tif (from->dpflags != to->dpflags)\n\t\tbits |= E5_FLAGS;\n\tif (from->trans != to->trans)\n\t\tbits |= E5_ALPHA;\n\tif (from->scale != to->scale)\n\t\tbits |= E5_SCALE;\n\tif (from->colormap != to->colormap)\n\t\tbits |= E5_COLORMAP;\n\tif (from->tagentity != to->tagentity || from->tagindex != to->tagindex)\n\t\tbits |= E5_ATTACHMENT;\n\tif (from->light[0] != to->light[0] || from->light[1] != to->light[1] || from->light[2] != to->light[2] || from->light[3] != to->light[3] || from->lightstyle != to->lightstyle || from->lightpflags != to->lightpflags)\n\t\tbits |= E5_LIGHT;\n\tif (from->glowsize != to->glowsize || from->glowcolour != to->glowcolour)\n\t\tbits |= E5_GLOW;\n\tif (from->colormod[0] != to->colormod[0] || from->colormod[1] != to->colormod[1] || from->colormod[2] != to->colormod[2])\n\t\tbits |= E5_COLORMOD;\n\tif (from->glowmod[0] != to->glowmod[0] || from->glowmod[1] != to->glowmod[1] || from->glowmod[2] != to->glowmod[2])\n\t\tbits |= E5_GLOWMOD;\n\tif (to->bonecount != from->bonecount || (to->bonecount && (!frombonedatabase || memcmp(frombonedatabase+from->boneoffset, tobonedatabase+to->boneoffset, to->bonecount*sizeof(short)*7))))\n\t\tif (to->bonecount)\n\t\t\tbits |= E5_COMPLEXANIMATION;\n\tif (to->u.q1.traileffectnum != from->u.q1.traileffectnum)\n\t\tbits |= E5_TRAILEFFECTNUM;\n\n\tif ((bits & E5_ORIGIN) && (!(to->dpflags & RENDER_LOWPRECISION) || to->origin[0] < -4096 || to->origin[0] >= 4096 || to->origin[1] < -4096 || to->origin[1] >= 4096 || to->origin[2] < -4096 || to->origin[2] >= 4096))\n\t\tbits |= E5_ORIGIN32;\n\tif ((bits & E5_ANGLES) && !(to->dpflags & RENDER_LOWPRECISION))\n\t\tbits |= E5_ANGLES16;\n\tif ((bits & E5_MODEL) && to->modelindex >= 256)\n\t\tbits |= E5_MODEL16;\n\tif ((bits & E5_FRAME) && to->frame >= 256)\n\t\tbits |= E5_FRAME16;\n\tif (bits & E5_EFFECTS)\n\t{\n\t\tif (to->effects >= 65536)\n\t\t\tbits |= E5_EFFECTS32;\n\t\telse if (to->effects >= 256)\n\t\t\tbits |= E5_EFFECTS16;\n\t}\n\n\treturn bits;\n}\n\nvoid SVDP_EmitEntityDelta(unsigned int bits, entity_state_t *to, sizebuf_t *msg, qbyte *bonedatabase)\n{\n\tbits &= ~E5_SERVERPRIVATE;\n\n\tif (!bits)\n\t\treturn;\n\n\tif (bits >= 256)\n\t\tbits |= E5_EXTEND1;\n\tif (bits >= 65536)\n\t\tbits |= E5_EXTEND2;\n\tif (bits >= 16777216)\n\t\tbits |= E5_EXTEND3;\n\n\tMSG_WriteShort(msg, to->number);\n\tMSG_WriteByte(msg, bits & 0xFF);\n\tif (bits & E5_EXTEND1)\n\t\tMSG_WriteByte(msg, (bits >> 8) & 0xFF);\n\tif (bits & E5_EXTEND2)\n\t\tMSG_WriteByte(msg, (bits >> 16) & 0xFF);\n\tif (bits & E5_EXTEND3)\n\t\tMSG_WriteByte(msg, (bits >> 24) & 0xFF);\n\tif (bits & E5_FLAGS)\n\t\tMSG_WriteByte(msg, to->dpflags);\n\tif (bits & E5_ORIGIN)\n\t{\n\t\tif (bits & E5_ORIGIN32)\n\t\t{\n\t\t\tMSG_WriteFloat(msg, to->origin[0]);\n\t\t\tMSG_WriteFloat(msg, to->origin[1]);\n\t\t\tMSG_WriteFloat(msg, to->origin[2]);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tMSG_WriteShort(msg, to->origin[0]*8);\n\t\t\tMSG_WriteShort(msg, to->origin[1]*8);\n\t\t\tMSG_WriteShort(msg, to->origin[2]*8);\n\t\t}\n\t}\n\tif (bits & E5_ANGLES)\n\t{\n\t\tif (bits & E5_ANGLES16)\n\t\t{\n\t\t\tMSG_WriteAngle16(msg, to->angles[0]);\n\t\t\tMSG_WriteAngle16(msg, to->angles[1]);\n\t\t\tMSG_WriteAngle16(msg, to->angles[2]);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tMSG_WriteAngle8(msg, to->angles[0]);\n\t\t\tMSG_WriteAngle8(msg, to->angles[1]);\n\t\t\tMSG_WriteAngle8(msg, to->angles[2]);\n\t\t}\n\t}\n\tif (bits & E5_MODEL)\n\t{\n\t\tif (bits & E5_MODEL16)\n\t\t\tMSG_WriteShort(msg, to->modelindex);\n\t\telse\n\t\t\tMSG_WriteByte(msg, to->modelindex);\n\t}\n\tif (bits & E5_FRAME)\n\t{\n\t\tif (bits & E5_FRAME16)\n\t\t\tMSG_WriteShort(msg, to->frame);\n\t\telse\n\t\t\tMSG_WriteByte(msg, to->frame);\n\t}\n\tif (bits & E5_SKIN)\n\t\tMSG_WriteByte(msg, to->skinnum);\n\tif (bits & E5_EFFECTS)\n\t{\n\t\tif (bits & E5_EFFECTS32)\n\t\t\tMSG_WriteLong(msg, to->effects);\n\t\telse if (bits & E5_EFFECTS16)\n\t\t\tMSG_WriteShort(msg, to->effects);\n\t\telse\n\t\t\tMSG_WriteByte(msg, to->effects);\n\t}\n\tif (bits & E5_ALPHA)\n\t\tMSG_WriteByte(msg, to->trans);\n\tif (bits & E5_SCALE)\n\t\tMSG_WriteByte(msg, to->scale);\n\tif (bits & E5_COLORMAP)\n\t\tMSG_WriteByte(msg, to->colormap&0xff);\n\tif (bits & E5_ATTACHMENT)\n\t{\n\t\tMSG_WriteEntity(msg, to->tagentity);\n\t\tMSG_WriteByte(msg, to->tagindex);\n\t}\n\tif (bits & E5_LIGHT)\n\t{\n\t\tMSG_WriteShort(msg, to->light[0]);\n\t\tMSG_WriteShort(msg, to->light[1]);\n\t\tMSG_WriteShort(msg, to->light[2]);\n\t\tMSG_WriteShort(msg, to->light[3]);\n\t\tMSG_WriteByte(msg, to->lightstyle);\n\t\tMSG_WriteByte(msg, to->lightpflags);\n\t}\n\tif (bits & E5_GLOW)\n\t{\n\t\tMSG_WriteByte(msg, to->glowsize);\n\t\tMSG_WriteByte(msg, to->glowcolour);\n\t}\n\tif (bits & E5_COLORMOD)\n\t{\n\t\tMSG_WriteByte(msg, to->colormod[0]);\n\t\tMSG_WriteByte(msg, to->colormod[1]);\n\t\tMSG_WriteByte(msg, to->colormod[2]);\n\t}\n\tif (bits & E5_GLOWMOD)\n\t{\n\t\tMSG_WriteByte(msg, to->glowmod[0]);\n\t\tMSG_WriteByte(msg, to->glowmod[1]);\n\t\tMSG_WriteByte(msg, to->glowmod[2]);\n\t}\n\tif (bits & E5_COMPLEXANIMATION)\n\t{\n\t\tshort *bonedata = (short*)(bonedatabase+to->boneoffset);\n\t\tint i;\n\t\tMSG_WriteByte(msg, 4);\n\t\tMSG_WriteShort(msg, to->modelindex);\n\t\tMSG_WriteByte(msg, to->bonecount);\n\t\tfor (i = 0; i < to->bonecount*7; i++)\n\t\t\tMSG_WriteShort(msg, bonedata[i]);\n\t}\n\tif (bits & E5_TRAILEFFECTNUM)\n\t\tMSG_WriteShort(msg, to->u.q1.traileffectnum);\n}\n\nvoid SVDP_EmitEntitiesUpdate (client_t *client, client_frame_t *frame, packet_entities_t *to, sizebuf_t *msg)\n{\n\tpacket_entities_t *cur;\n\tint\t\tnewindex;\n\tint\t\tcurnum, newnum;\n\tint\t\tj;\n\tint sequence = client->netchan.incoming_sequence;\n\n\t// this is the frame that we are going to delta update from\n\tcur = &client->sentents;\n\tif (!client->netchan.incoming_sequence)\n\t{\t//first packet deltas from nothing.\n\t\t//so make sure we start with nothing\n\t\tcur->num_entities = 0;\n\t}\n\n\tif (to->num_entities)\n\t{\n\t\tj = to->entities[to->num_entities-1].number+1;\n\t\tif (j > cur->max_entities)\n\t\t{\n\t\t\tcur->entities = BZ_Realloc(cur->entities, sizeof(*cur->entities) * j);\n\t\t\tmemset(&cur->entities[cur->max_entities], 0, sizeof(cur->entities[0]) * (j - cur->max_entities));\n\t\t\tcur->max_entities = j;\n\t\t}\n\t\twhile(j > cur->num_entities)\n\t\t{\n\t\t\tcur->entities[cur->num_entities].number = 0;\n\t\t\tcur->num_entities++;\n\t\t}\n\t}\n\n\t//diff the from+to states, flagging any changed state (which is combined with any state from previous packet loss\n\tnewindex = 0;\n\tcurnum = 0;\n\twhile (newindex < to->num_entities || curnum < cur->num_entities)\n\t{\n\t\tnewnum = newindex >= to->num_entities ? 0x8000 : to->entities[newindex].number;\n\n\t\tif (newnum == curnum)\n\t\t{\n\t\t\tif (cur->entities[curnum].number)\n\t\t\t{\t//regular update\n\t\t\t\tclient->pendingdeltabits[newnum] |= SVDP_CalcDelta(&cur->entities[curnum], NULL/*cur->bonedata*/, &to->entities[newindex], to->bonedata);\n\t\t\t\tif (client->pendingdeltabits[newnum] & E5_SERVERREMOVE)\n\t\t\t\t{\t//if it got flagged for removal, but its actually a valid entity, then assume that its an outdated remove and just flag it for a full update in case stuff got lost.\n\t\t\t\t\tclient->pendingdeltabits[newnum] &= ~E5_SERVERREMOVE;\n\t\t\t\t\tclient->pendingdeltabits[newnum] |= E5_FULLUPDATE;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\t//this ent is new\n\t\t\t\t//dpp5+ does not use baselines. it just resets from default state.\n\t\t\t\tclient->pendingdeltabits[newnum] = E5_FULLUPDATE | SVDP_CalcDelta(&nullentitystate, NULL, &to->entities[newindex], to->bonedata);\n\t\t\t}\n\t\t\tcur->entities[curnum] = to->entities[newindex];\n\t\t\tnewindex++;\n\t\t}\n\t\telse if (cur->entities[curnum].number)\n\t\t{\t//this entity was apparently removed since last time.\n\t\t\tcur->entities[curnum].number = 0;\n\t\t\tclient->pendingdeltabits[curnum] = E5_SERVERREMOVE;\n\t\t}\n\t\tcurnum++;\n\t}\n\n\tto = cur;\n\n\t//loop through all ents and send them as required\n\n//\tCon_Printf (\"frame %i\\n\", client->netchan.incoming_sequence);\n\n\t{\n\t\tunsigned int bits;\n\t\tint outno, outmax = frame->maxresend;\n\t\tstruct resendinfo_s *resend = frame->resend;\n\n\t\tMSG_WriteByte(msg, svcdp_entities);\n\t\tMSG_WriteLong(msg, sequence);\t//sequence for the client to ack (any bits sent in unacked frames will be re-queued)\n\t\tif (client->protocol == SCP_DARKPLACES7)\n\t\t\tMSG_WriteLong(msg, client->last_sequence);\t\t\t//movement sequence that we are acking.\n\n\t\tclient->netchan.incoming_sequence++;\n\n\t\t//add in the bitmasks of dropped packets.\n\t\tfor(outno = 0, j = 1; j < to->num_entities; j++)\n\t\t{\n\t\t\tbits = client->pendingdeltabits[j];\n\t\t\tif (!bits)\n\t\t\t\tcontinue;\n\t\t\tif (msg->cursize + 50 > msg->maxsize)\n\t\t\t\tbreak; /*give up if it gets full. FIXME: bone data is HUGE.*/\n\t\t\tif (outno >= outmax)\n\t\t\t{\t//expand the frames. may need some copying...\n\t\t\t\tif (outmax == client->max_net_ents)\n\t\t\t\t\tbreak;\n\t\t\t\tSV_ExpandNackFrames(client, outno+1, &frame);\n\t\t\t\tresend = frame->resend;\n\t\t\t\toutmax = frame->maxresend;\n\t\t\t}\n\n\t\t\tif (bits & E5_SERVERREMOVE)\n\t\t\t{\t//if reset is set, then reset was set eroneously.\n\t\t\t\tMSG_WriteShort(msg, j | 0x8000);\n\t\t\t\tresend[outno].bits = E5_SERVERREMOVE;\n\t//\t\t\tCon_Printf(\"REMOVE %i @ %i\\n\", j, sequence);\n\t\t\t}\n\t\t\telse if (to->entities[j].number) /*only send a new copy of the ent if they actually have one already*/\n\t\t\t{\n\t\t\t\t//if we didn't reach the end in the last packet, start at that point to avoid spam\n\t\t\t\t//player slots are exempt from this, so they are in every packet (strictly speaking only the local player 'needs' this, but its nice to have it for high-priority targets too)\n\t\t\t\tif (j < client->nextdeltaindex && j > svs.allocated_client_slots)\n\t\t\t\t\tcontinue;\n\n\t\t\t\tif (bits & E5_FULLUPDATE)\n\t\t\t\t{\n\t\t\t\t\t/*flag the entity for the next packet, so we always get two resets when it appears, to reduce the effects of packetloss on seeing rockets etc*/\n\t\t\t\t\tbits = E5_FULLUPDATE | SVDP_CalcDelta(&nullentitystate, NULL, &to->entities[j], to->bonedata);\n\t\t\t\t\tresend[outno].bits = E5_FULLUPDATE;\n\t//\t\t\t\tCon_Printf(\"RESET %i @ %i\\n\", j, sequence);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tresend[outno].bits = bits;\n\n\t\t\t\tSVDP_EmitEntityDelta (bits, &to->entities[j], msg, to->bonedata);\n\t\t\t}\n\n\t\t\tclient->pendingdeltabits[j] = 0;\n\n\t\t\tresend[outno].flags = 0;\n\t\t\tresend[outno++].entnum = j;\n\t\t}\n\t\tMSG_WriteShort(msg, 0x8000);\t//dp5+ uses 'remove world' as a terminator.\n\t\tframe->numresend = outno;\n\t\tframe->sequence = sequence;\n\n\t\tif (j == to->num_entities) //looks like we sent them all\n\t\t\tclient->nextdeltaindex = 0;\t//start afresh with the next packet.\n\t\telse\n\t\t\tclient->nextdeltaindex = j;\t//we overflowed or something, start going round-robin\n\t}\n}\n#endif\n\n\nint SV_HullNumForPlayer(int h2hull, float *mins, float *maxs)\n{\n\tint diff;\n\tint best;\n\tint hullnum, i;\n\n\tif (sv.world.worldmodel->fromgame != fg_quake)\n\t{\n\t\treturn -mins[2] + 32;\t//clients are expected to decide themselves.\n\t}\n\n\tif (h2hull)\n\t\treturn (h2hull-1) | (mins[2]?0:128);\n\n\n\thullnum = 0;\n\tbest = 8192;\n\t//x/y pos/neg are assumed to be the same magnitute.\n\t//y pos/height are assumed to be different from all the others.\n\tfor (i = 0; i < MAX_MAP_HULLSM; i++)\n\t{\n#define sq(x) ((x)*(x))\n\t\tdiff = sq(sv.world.worldmodel->hulls[i].clip_maxs[2] - maxs[2]) +\n\t\t\tsq(sv.world.worldmodel->hulls[i].clip_mins[2] - mins[2]) +\n\t\t\tsq(sv.world.worldmodel->hulls[i].clip_maxs[0] - maxs[0]) +\n\t\t\tsq(sv.world.worldmodel->hulls[i].clip_mins[0] - mins[0]);\n\t\tif (diff < best)\n\t\t{\n\t\t\tbest = diff;\n\t\t\thullnum=i;\n\t\t}\n\t}\n\treturn hullnum;\n}\n\n#if 1\ntypedef struct {\n\tint playernum;\n\tqboolean onladder;\n\tusercmd_t\t*lastcmd;\n\tint modelindex;\n\tint frame;\n\tint weaponframe;\n\tint vw_index;\n\tfloat *angles;\n\tfloat *origin;\n\tfloat *velocity;\n\tint effects;\n\tint skin;\n\tfloat *mins;\n\tfloat *maxs;\n\tfloat scale;\n\tfloat transparency;\n\tfloat fatness;\n\tfloat localtime;\n\tint health;\n\tint spectator;\t//0=send to a player. 1=non-tracked player, to a spec. 2=tracked player, to a spec(or self)\n\tqboolean isself;\n\tqboolean onground;\n\tqboolean solid;\n\tunsigned int fteext1;\n\tunsigned int ezext1;\n\tunsigned int zext;\n\tint hull;\n\tclient_t *cl;\n} clstate_t;\nvoid SV_WritePlayerToClient(sizebuf_t *msg, clstate_t *ent)\n{\n\tusercmd_t\tcmd;\n\tint msec;\n\tint hullnumber;\n\tint i;\n\tint pflags;\n\tint pm_type, pm_code;\n\tint zext = ent->zext;\n\n\t\tpflags = PF_MSEC | PF_COMMAND;\n\n\t\tif (ent->modelindex != sv_playermodel)\n\t\t\tpflags |= PF_MODEL;\n\n\t\tif (ent->velocity)\n\t\t\tfor (i=0 ; i<3 ; i++)\n\t\t\t\tif (ent->velocity[i])\n\t\t\t\t\tpflags |= PF_VELOCITY1<<i;\n\t\tif (ent->effects)\n\t\t\tpflags |= PF_EFFECTS;\n\t\tif (ent->skin || ent->modelindex>=256)\n\t\t\tpflags |= PF_SKINNUM;\n\t\tif (ent->health <= 0)\n\t\t\tpflags |= PF_DEAD;\n\t\tif (progstype == PROG_QW)\n\t\t{\n\t\t\tif (ent->mins[2] != -24)\n\t\t\t\tpflags |= PF_GIB;\n\t\t}\n\t\telse if (progstype == PROG_H2)\n\t\t{\n//\t\t\tif (ent->maxs[2] != 56)\n//\t\t\t\tpflags |= PF_GIB;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (ent->mins[2] != -24)\n\t\t\t\tpflags |= PF_GIB;\n\t\t}\n\n\t\tif (ent->isself)\n\t\t{\n\t\t\tif (ent->spectator)\n\t\t\t\tpflags &= PF_VELOCITY1 | PF_VELOCITY2 | PF_VELOCITY3 | PF_DEAD | PF_GIB;\n\t\t\telse\n\t\t\t{\t// don't send a lot of data on personal entity\n\t\t\t\tpflags &= ~(PF_MSEC|PF_COMMAND);\n\t\t\t\tif (ent->weaponframe)\n\t\t\t\t\tpflags |= PF_WEAPONFRAME;\n\t\t\t}\n\t\t}\n\n\t\tif (ent->spectator == 2 && ent->weaponframe)\t//it's not us, but we are spectating, so we need the correct weaponframe\n\t\t\tpflags |= PF_WEAPONFRAME;\n\n\t\tif (!ent->isself || (ent->fteext1 & PEXT_SPLITSCREEN))\n\t\t{\n#ifdef PEXT_SCALE\t//this is graphics, not physics\n\t\t\tif (ent->fteext1 & PEXT_SCALE)\n\t\t\t{\n\t\t\t\tif (ent->scale && ent->scale != 1) pflags |= PF_SCALE;\n\t\t\t}\n#endif\n#ifdef PEXT_TRANS\n\t\t\tif (ent->fteext1 & PEXT_TRANS)\n\t\t\t{\n\t\t\t\tif (ent->transparency) pflags |= PF_TRANS;\n\t\t\t}\n#endif\n#ifdef PEXT_FATNESS\n\t\t\tif (ent->fteext1 & PEXT_FATNESS)\n\t\t\t{\n\t\t\t\tif (ent->fatness) pflags |= PF_FATNESS;\n\t\t\t}\n#endif\n\t\t}\n#ifdef PEXT_HULLSIZE\n\t\tif (ent->fteext1 & PEXT_HULLSIZE)\n\t\t{\n\t\t\thullnumber = SV_HullNumForPlayer(ent->hull, ent->mins, ent->maxs);\n\t\t\tif (hullnumber != 1)\n\t\t\t\tpflags |= PF_HULLSIZE_Z;\n\t\t}\n\t\telse\n\t\t\thullnumber=1;\n#endif\n\n\t\tif (zext&Z_EXT_PM_TYPE)\n\t\t{\n\t\t\tif (ent->cl)\n\t\t\t{\n\t\t\t\tif (ent->cl->viewent)\n\t\t\t\t\tpm_type = PMC_NONE;\n\t\t\t\telse\n\t\t\t\t\tpm_type = SV_PMTypeForClient (ent->cl, ent->cl->edict);\n\t\t\t\tswitch (pm_type)\n\t\t\t\t{\n\t\t\t\tcase PM_NORMAL:\t\t// Z_EXT_PM_TYPE protocol extension\n\t\t\t\t\tif (ent->cl->jump_held)\n\t\t\t\t\t\tpm_code = PMC_NORMAL_JUMP_HELD;\t// encode pm_type and jump_held into pm_code\n\t\t\t\t\telse\n\t\t\t\t\t\tpm_code = PMC_NORMAL;\n\t\t\t\t\tbreak;\n\t\t\t\tcase PM_OLD_SPECTATOR:\n\t\t\t\t\tpm_code = PMC_OLD_SPECTATOR;\n\t\t\t\t\tbreak;\n\t\t\t\tcase PM_SPECTATOR:\t// Z_EXT_PM_TYPE_NEW protocol extension\n\t\t\t\t\tpm_code = PMC_SPECTATOR;\n\t\t\t\t\tbreak;\n\t\t\t\tcase PM_FLY:\n\t\t\t\t\tpm_code = PMC_FLY;\n\t\t\t\t\tbreak;\n\t\t\t\tcase PM_DEAD:\n\t\t\t\t\tpm_code = PMC_NORMAL;\n\t\t\t\t\tbreak;\n\t\t\t\tcase PM_NONE:\n\t\t\t\t\tpm_code = PMC_NONE;\n\t\t\t\t\tbreak;\n\t\t\t\tcase PM_WALLWALK:\n\t\t\t\t\tpm_code = PMC_WALLWALK;\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n//\t\t\t\t\tSys_Error(\"SV_WritePlayersToClient: unexpected pm_type\");\n\t\t\t\t\tpm_code=PMC_NORMAL;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t\tpm_code = (ent->zext & Z_EXT_PM_TYPE_NEW)?PMC_SPECTATOR:PMC_OLD_SPECTATOR;//(ent->spectator && ent->isself) ? PMC_OLD_SPECTATOR : PMC_NORMAL;\n\t\t\tpflags |= pm_code << PF_PMC_SHIFT;\n\t\t}\n\n\t\tif ((zext & Z_EXT_PF_ONGROUND) && ent->onground)\n\t\t\tpflags |= PF_ONGROUND;\n\t\tif ((zext & Z_EXT_PF_SOLID) && ent->solid)\n\t\t\tpflags |= PF_SOLID;\n\n\t\tMSG_WriteByte (msg, svc_playerinfo);\n\t\tMSG_WriteByte (msg, ent->playernum);\n\n\t\tif (ent->fteext1 & (PEXT_HULLSIZE|PEXT_TRANS|PEXT_SCALE|PEXT_FATNESS))\n\t\t{\n\t\t\tif (pflags & 0xff0000)\n\t\t\t\tpflags |= PF_EXTRA_PFS;\n\t\t\tMSG_WriteShort (msg, pflags&0xffff);\n\t\t\tif (pflags & PF_EXTRA_PFS)\n\t\t\t\tMSG_WriteByte(msg, (pflags&0xff0000)>>16);\n\t\t}\n\t\telse\n\t\t\tMSG_WriteShort (msg, (pflags&0x3fff) | ((pflags&0xc00000)>>8));\n\t\t//we need to tell the client that it's moved, as it's own origin might not be natural\n\n\t\tfor (i=0 ; i<3 ; i++)\n\t\t{\n\t\t\tif (ent->ezext1 & EZPEXT1_FLOATENTCOORDS)\n\t\t\t\tMSG_WriteFloat (msg, ent->origin[i]);\n\t\t\telse\n\t\t\t\tMSG_WriteCoord (msg, ent->origin[i]);\n\t\t}\n\n\t\tMSG_WriteByte (msg, ent->frame);\n\n\t\tif (pflags & PF_MSEC)\n\t\t{\n\t\t\tmsec = 1000*(sv.time - ent->localtime);\n\t\t\tif (msec < 0)\n\t\t\t\tmsec = 0;\n\t\t\tif (msec > 255)\n\t\t\t\tmsec = 255;\n\t\t\tMSG_WriteByte (msg, msec);\n\t\t}\n\n\t\tif (pflags & PF_COMMAND)\n\t\t{\n\t\t\tif (ent->lastcmd)\n\t\t\t\tcmd = *ent->lastcmd;\n\t\t\telse\n\t\t\t{\n\t\t\t\tmemset(&cmd, 0, sizeof(cmd));\n\t\t\t\tcmd.angles[0] = (short)(ent->angles[0] * 65535/360.0f);\n\t\t\t\tcmd.angles[1] = (short)(ent->angles[1] * 65535/360.0f);\n\t\t\t\tcmd.angles[2] = (short)(ent->angles[2] * 65535/360.0f);\n\t\t\t}\n\n\t\t\tif (ent->health <= 0)\n\t\t\t{\t// don't show the corpse looking around...\n\t\t\t\tcmd.angles[0] = 0;\n\t\t\t\tcmd.angles[1] = (int)(ent->angles[1]*65535/360);\n\t\t\t\tcmd.angles[2] = 0;\n\t\t\t}\n\n\t\t\tcmd.buttons = 0;\t// never send buttons\n\t\t\tif (ent->zext & Z_EXT_VWEP)\n\t\t\t\tcmd.impulse = ent->vw_index;\t// never send impulses\n\t\t\telse\n\t\t\t\tcmd.impulse = 0;\n\n\t\t\tMSGQW_WriteDeltaUsercmd (msg, &nullcmd, &cmd);\n\t\t}\n\n\t\tif (ent->velocity)\n\t\t{\n\t\t\tfor (i=0 ; i<3 ; i++)\n\t\t\t\tif (pflags & (PF_VELOCITY1<<i) )\n\t\t\t\t\tMSG_WriteShort (msg, (short)(ent->velocity[i]));\n\t\t}\n\t\telse\n\t\t{\n\t\t\tfor (i=0 ; i<3 ; i++)\n\t\t\t\tif (pflags & (PF_VELOCITY1<<i) )\n\t\t\t\t\tMSG_WriteShort (msg, 0);\n\t\t}\n\n\t\tif (pflags & PF_MODEL)\n\t\t\tMSG_WriteByte (msg, ent->modelindex);\n\n\t\tif (pflags & PF_SKINNUM)\n\t\t\tMSG_WriteByte (msg, ent->skin | (((pflags & PF_MODEL)&&(ent->modelindex>=256))<<7));\n\n\t\tif (pflags & PF_EFFECTS)\n\t\t\tMSG_WriteByte (msg, ent->effects);\n\n\t\tif (pflags & PF_WEAPONFRAME)\n\t\t\tMSG_WriteByte (msg, ent->weaponframe);\n\n#ifdef PEXT_SCALE\n\t\tif (pflags & PF_SCALE)\n\t\t\tMSG_WriteByte (msg, ent->scale*50);\n#endif\n#ifdef PEXT_TRANS\n\t\tif (pflags & PF_TRANS)\n\t\t\tMSG_WriteByte (msg, (qbyte)(ent->transparency*255));\n#endif\n#ifdef PEXT_FATNESS\n\t\tif (pflags & PF_FATNESS)\n\t\t\tMSG_WriteChar (msg, ent->fatness);\n#endif\n#ifdef PEXT_HULLSIZE\t//shrunken or crouching in halflife levels. (possibly enlarged)\n\t\tif (pflags & PF_HULLSIZE_Z)\n\t\t\tMSG_WriteChar (msg, hullnumber + (ent->onladder?128:0));\t//physics.\n#endif\n}\n#endif\n\n\nqboolean Cull_Traceline(float *timestamp, pvscamera_t *cameras, edict_t *seen)\n{\n\tint i;\n\ttrace_t tr;\n\tvec3_t end, amin, size;\n\tint c;\n\n\t//don't cull inline models like this. too big, too weird.\n\tmodel_t *mod = sv.world.Get_CModel(&sv.world, seen->v->modelindex);\n\tif (mod && *mod->name == '*')\n\t\treturn false;\n\t//don't cull external models either. unless they're progs/b_*.mdl which SHOULD get culled this way. its awkward okay?\n\tif (seen->v->solid == SOLID_BSP)\n\t\treturn false;\t//bsp ents are never culled this way (typically far too large to care, often with large parts inside walls)\n\n\tif (timestamp)\n\t{\n\t\tint tests;\n\t\tfloat delay;\n\n\t\t//temporal cache\n\t\t//we still need to fire some traces every frame (monte-carlo style), but we don't need to be so desperate as it'll stay visible for a while anyway.\n\n\t\tif (seen->entnum <= sv.allocated_client_slots)\n\t\t\ttests = 8, delay = 0.2;\n\t\telse\n\t\t\ttests = 2, delay = 1.0;\n\n\t\tVectorAdd(seen->v->origin, seen->v->mins, amin);\n\t\tVectorSubtract(seen->v->maxs, seen->v->mins, size);\n\n\t\tfor (c = 0; c < cameras->numents; c++)\n\t\t{\n\t\t\tfor (i = 0; i < tests; i++)\n\t\t\t{\n\t\t\t\tend[0] = amin[0] + frandom()*size[0];\n\t\t\t\tend[1] = amin[1] + frandom()*size[1];\n\t\t\t\tend[2] = amin[2] + frandom()*size[2];\n\n\t\t\t\tif (!sv.world.worldmodel->funcs.NativeTrace (sv.world.worldmodel, 1, NULLFRAMESTATE, NULL, cameras->org[c], end, vec3_origin, vec3_origin, false, FTECONTENTS_SOLID, &tr))\n\t\t\t\t{\n\t\t\t\t\t*timestamp = sv.time + delay;\n\t\t\t\t\treturn false;\t//this trace went through, so don't cull\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (*timestamp >= sv.time)\n\t\t\treturn false;\n\t\treturn true;\n\t}\n\telse\n\t{\n\t\t//stage 1: check against their origin\n\t\tfor (c = 0; c < cameras->numents; c++)\n\t\t{\n\t\t\ttr.fraction = 1;\n\t\t\tif (!sv.world.worldmodel->funcs.NativeTrace (sv.world.worldmodel, 1, NULLFRAMESTATE, NULL, cameras->org[c], seen->v->origin, vec3_origin, vec3_origin, false, FTECONTENTS_SOLID, &tr))\n\t\t\t\treturn false;\t//wasn't blocked\n\t\t}\n\n\t\t//stage 2: check against their bbox\n\t\tfor (c = 0; c < cameras->numents; c++)\n\t\t{\n\t\t\tfor (i = 0; i < 8; i++)\n\t\t\t{\n\t\t\t\tend[0] = seen->v->origin[0] + ((i&1)?seen->v->mins[0]:seen->v->maxs[0]);\n\t\t\t\tend[1] = seen->v->origin[1] + ((i&2)?seen->v->mins[1]:seen->v->maxs[1]);\n\t\t\t\tend[2] = seen->v->origin[2] + ((i&4)?seen->v->mins[2]+0.1:seen->v->maxs[2]);\n\n\t\t\t\ttr.fraction = 1;\n\t\t\t\tif (!sv.world.worldmodel->funcs.NativeTrace (sv.world.worldmodel, 1, NULLFRAMESTATE, NULL, cameras->org[c], end, vec3_origin, vec3_origin, false, FTECONTENTS_SOLID, &tr))\n\t\t\t\t\treturn false;\t//this trace went through, so don't cull\n\t\t\t}\n\t\t}\n\t}\n\n\t//not visible\n\treturn true;\n}\n\n#ifdef MVD_RECORDING\nvoid SV_WritePlayersToMVD (client_t *client, client_frame_t *frame, sizebuf_t *msg)\n{\n\tint\t\t\tj;\n\tclient_t\t*cl;\n\tedict_t\t\t*ent, *vent;\n//\tint\t\t\tpflags;\n\n\tdemo_frame_t *demo_frame;\n\tdemo_client_t *dcl;\n\n\tdemo_frame = &demo.frames[demo.parsecount&DEMO_FRAMES_MASK];\n\tfor (j=0,cl=svs.clients, dcl = demo_frame->clients; j < svs.allocated_client_slots ; j++,cl++, dcl++)\n\t{\n\t\tif (cl->state != cs_spawned)\n\t\t\tcontinue;\n\n#ifdef SERVER_DEMO_PLAYBACK\n\t\tif (sv.demostatevalid)\n\t\t{\n\t\t\tif (client != cl)\n\t\t\t\tcontinue;\n\t\t}\n#endif\n\n\t\tent = cl->edict;\n\t\tvent = ent;\n\n#ifdef NQPROT\n\t\tif (progstype != PROG_QW)\n\t\t{\n\t\t\tif ((int)ent->v->effects & EF_MUZZLEFLASH)\n\t\t\t{\n\t\t\t\tent->v->effects = (int)ent->v->effects & ~EF_MUZZLEFLASH;\n\t\t\t\tent->muzzletime = sv.world.physicstime;\n\t\t\t\tMSG_WriteByte (&sv.multicast, svc_muzzleflash);\n\t\t\t\tMSG_WriteEntity (&sv.multicast, NUM_FOR_EDICT(svprogfuncs, ent));\n\t\t\t\tSV_MulticastProtExt (ent->v->origin, MULTICAST_PHS, pr_global_struct->dimension_send, 0, 0);\n\t\t\t}\n\t\t}\n#endif\n\n\t\tif (SV_AddCSQCUpdate(client, ent))\n\t\t\tcontinue;\n\n\t\tif (cl->spectator)\n\t\t\tcontinue;\n\n\t\tdcl->parsecount = demo.parsecount;\n\n\t\tVectorCopy(vent->v->origin, dcl->info.origin);\n\t\tVectorCopy(vent->v->angles, dcl->info.angles);\n\t\tdcl->info.angles[0] *= -3;\n\t\tdcl->info.angles[2] = 0; // no roll angle\n\n\t\tif (ent->v->health <= 0)\n\t\t{\t// don't show the corpse looking around...\n\t\t\tdcl->info.angles[0] = 0;\n\t\t\tdcl->info.angles[1] = vent->v->angles[1];\n\t\t\tdcl->info.angles[2] = 0;\n\t\t}\n\n\t\tif (ent != vent)\n\t\t{\n\t\t\tdcl->info.model = 0;\t//invisible.\n\t\t\tdcl->info.effects = 0;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tdcl->info.skinnum = ent->v->skin;\n\t\t\tdcl->info.effects = ent->v->effects;\n\t\t\tdcl->info.weaponframe = ent->v->weaponframe;\n\t\t\tdcl->info.model = ent->v->modelindex;\n\t\t}\n\t\tdcl->sec = sv.time - cl->localtime;\n\t\tdcl->frame = ent->v->frame;\n\t\tdcl->flags = 0;\n\t\tdcl->cmdtime = cl->localtime;\n\t\tdcl->fixangle = demo.fixangle[j];\n\t\tdemo.fixangle[j] = 0;\n\n\t\tif (ent->v->health <= 0)\n\t\t\tdcl->flags |= DF_DEAD;\n\t\tif (ent->v->mins[2] != -24)\n\t\t\tdcl->flags |= DF_GIB;\n\t}\n}\n#endif\n\n/*\n=============\nSV_WritePlayersToClient\n\n=============\n*/\nvoid SV_WritePlayersToClient (client_t *client, client_frame_t *frame, edict_t *clent, pvscamera_t *cameras, sizebuf_t *msg)\n{\n\tqboolean isbot;\n\tint\t\t\tj;\n\tclient_t\t*cl;\n\tedict_t\t\t*ent, *vent;\n//\tint\t\t\tpflags;\n\n\tif (client->state < cs_spawned)\n\t{\n\t\tCon_Printf(\"SV_WritePlayersToClient: not spawned yet\\n\");\n\t\treturn;\n\t}\n\n#ifdef NQPROT\n\tif (!ISQWCLIENT(client))\n\t\treturn;\n#endif\n\n#ifdef SERVER_DEMO_PLAYBACK\n\tif (sv.demostatevalid)\t//this is a demo\n\t{\n\t\tusercmd_t cmd;\n\t\tvec3_t ang;\n\t\tvec3_t org;\n\t\tvec3_t vel;\n\t\tfloat lerp;\n\t\tfloat a1, a2;\n\t\tint i;\n\t\textern vec3_t player_mins, player_maxs;\n\t\tclstate_t clst;\n\t\textern float olddemotime, nextdemotime;\n\n\t\tfor (i=0 ; i<svs.allocated_client_slots ; i++)\n\t\t{\n\t\t\t//FIXME: Add PVS stuff.\n\n\t\t\tif (*sv.recordedplayer[i].userinfo)\t//if the client was active\n\t\t\t{\n\t\t\t\tclst.playernum = i;\n\t\t\t\tclst.onladder = 0;\n\t\t\t\tclst.lastcmd = &cmd;\n\t\t\t\tclst.modelindex = sv.demostate[i+1].modelindex;\n\t\t\t\tif (!clst.modelindex)\n\t\t\t\t\tcontinue;\n\t\t\t\tclst.frame = sv.demostate[i+1].frame;\n\t\t\t\tclst.weaponframe = sv.recordedplayer[i].weaponframe;\n\t\t\t\tclst.angles = ang;\n\t\t\t\tclst.origin = org;\n\t\t\t\tclst.hull = 1;\n\t\t\t\tclst.velocity = vel;\n\t\t\t\tclst.effects = sv.demostate[i+1].effects;\n\t\t\t\tclst.skin = sv.demostate[i+1].skinnum;\n\t\t\t\tclst.mins = player_mins;\n\t\t\t\tclst.maxs = player_maxs;\n\t\t\t\tclst.scale = sv.demostate[i+1].scale;\n\t\t\t\tclst.transparency = sv.demostate[i+1].trans;\n\t\t\t\tclst.fatness = sv.demostate[i+1].fatness;\n\t\t\t\tclst.localtime = sv.time;//sv.recordedplayer[j].updatetime;\n\t\t\t\tclst.health = sv.recordedplayer[i].stats[STAT_HEALTH];\n\t\t\t\tclst.spectator = 2;\t//so that weaponframes work properly.\n\t\t\t\tclst.isself = false;\n\t\t\t\tclst.fteext = 0;//client->fteprotocolextensions;\n\t\t\t\tclst.zext = 0;//client->zquake_extensions;\n\t\t\t\tclst.cl = NULL;\n\t\t\t\tclst.vw_index = 0;\n\t\t\t\tclst.solid = true;\n\t\t\t\tclst.onground = true;\n\n\t\t\t\tlerp = (realtime - olddemotime) / (nextdemotime - olddemotime);\n\t\t\t\tif (lerp < 0)\n\t\t\t\t\tlerp = 0;\n\t\t\t\tif (lerp > 1)\n\t\t\t\t\tlerp = 1;\n\t\t\t\tfor (j = 0; j < 3; j++)\n\t\t\t\t{\n\t\t\t\t\ta1 = (360.0f/256)*sv.recordedplayer[i].oldang[j];\n\t\t\t\t\ta2 = (360.0f/256)*sv.demostate[i+1].angles[j];\n\t\t\t\t\ta2 = a2 - a1;\n\t\t\t\t\tif (a2 > 180)\n\t\t\t\t\t\ta2-=360;\n\t\t\t\t\tif (a2 < -180)\n\t\t\t\t\t\ta2+=360;\n\t\t\t\t\tang[j] = (a1 + (a2)*lerp);\n\n\t\t\t\t\torg[j] = sv.recordedplayer[i].oldorg[j] + (sv.demostate[i+1].origin[j] - sv.recordedplayer[i].oldorg[j])*lerp;\n\n\t\t\t\t\tvel[j] = (-sv.recordedplayer[i].oldorg[j] + sv.demostate[i+1].origin[j])*(nextdemotime - olddemotime);\n\t\t\t\t}\n\n\t\t\t\tang[0] *= -3;\n\n//\t\t\t\tang[0] = ang[1] = ang[2] = 0;\n\n\t\t\t\tmemset(&cmd, 0, sizeof(cmd));\n\t\t\t\tcmd.angles[0] = ang[0]*65535/360.0f;\n\t\t\t\tcmd.angles[1] = ang[1]*65535/360.0f;\n\t\t\t\tcmd.angles[2] = ang[2]*65535/360.0f;\n\t\t\t\tcmd.msec = 50;\n\t\t\t\t\t{vec3_t f, r, u, v;\n\t\t\t\tAngleVectors(ang, f, r, u);\n\t\t\t\tVectorCopy(vel, v);\n\t\t\t\tcmd.forwardmove = DotProduct(f, v);\n\t\t\t\tcmd.sidemove = DotProduct(r, v);\n\t\t\t\tcmd.upmove = DotProduct(u, v);\n\t\t\t\t\t}\n\t\t\t\tclst.lastcmd=NULL;\n\n\t\t\t\tSV_WritePlayerToClient(msg, &clst);\n\t\t\t}\n\t\t}\n\n\t\t//now build the spectator's thingie\n\n\t\tmemset(&clst, 0, sizeof(clst));\n\n\t\tclst.fteext = 0;//client->fteprotocolextensions;\n\t\tclst.zext = 0;//client->zquake_extensions;\n\t\tclst.vw_index = 0;\n\t\tclst.playernum = svs.allocated_client_slots-1;\n\t\tclst.isself = true;\n\t\tclst.modelindex = 0;\n\t\tclst.hull = 1;\n\t\tclst.frame = 0;\n\t\tclst.localtime = sv.time;\n\t\tclst.mins = player_mins;\n\t\tclst.maxs = player_maxs;\n\n\t\tclst.angles = vec3_origin;\t//not needed, as the client knows better than us anyway.\n\t\tclst.origin = client->specorigin;\n\t\tclst.velocity = client->specvelocity;\n\n\t\tfor (client = client; client; client = client->controller)\n\t\t{\n\t\t\tclst.health = 100;\n\n\t\t\tif (client->spec_track)\n\t\t\t{\n\t\t\t\tclst.weaponframe = sv.recordedplayer[client->spec_track-1].weaponframe;\n\t\t\t\tclst.spectator = 2;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tclst.weaponframe = 0;\n\t\t\t\tclst.spectator = 1;\n\t\t\t}\n\n\t\t\tSV_WritePlayerToClient(msg, &clst);\n\n\t\t\tclst.playernum--;\n\t\t}\n\t\treturn;\n\t}\n#endif\n\tfor (j=0,cl=svs.clients ; j<sv.allocated_client_slots && j < client->max_net_clients; j++,cl++)\n\t{\n\t\tif (cl->state != cs_spawned && !(cl->state == cs_free && cl->name[0]))\t//this includes bots, and nq bots\n\t\t\tcontinue;\n\n\t\tif ((client->penalties & BAN_BLIND) && client != cl)\n\t\t\tcontinue;\n\n\n\t\tisbot = (!cl->name[0] || cl->protocol == SCP_BAD);\n\t\tent = cl->edict;\n\t\tif (cl->viewent && ent == clent)\n\t\t{\n\t\t\tvent = EDICT_NUM_UB(svprogfuncs, cl->viewent);\n\t\t\tif (!vent)\n\t\t\t\tvent = ent;\n\t\t}\n\t\telse\n\t\t\tvent = ent;\n\n\n\t\tif (vent->xv->customizeentityforclient)\n\t\t{\n\t\t\tglobalvars_t *pr_globals = PR_globals(svprogfuncs, PR_CURRENT);\n\t\t\tpr_global_struct->self = EDICT_TO_PROG(svprogfuncs, vent);\n\t\t\tpr_global_struct->other = (clent?EDICT_TO_PROG(svprogfuncs, clent):0);\n\t\t\tPR_ExecuteProgram(svprogfuncs, vent->xv->customizeentityforclient);\n\t\t\tif(!G_FLOAT(OFS_RETURN))\n\t\t\t\tcontinue;\n\t\t}\n\n\n#ifdef NQPROT\n\t\tif (progstype != PROG_QW)\n\t\t{\n\t\t\tif (progstype == PROG_H2 && (int)ent->v->effects & H2EF_NODRAW && ent != clent)\n\t\t\t\tcontinue;\n\n\t\t\tif ((int)ent->v->effects & EF_MUZZLEFLASH)\n\t\t\t{\n\t\t\t\tent->v->effects = (int)ent->v->effects & ~EF_MUZZLEFLASH;\n\t\t\t\tent->muzzletime = sv.world.physicstime;\n\t\t\t\tMSG_WriteByte (&sv.multicast, svc_muzzleflash);\n\t\t\t\tMSG_WriteEntity (&sv.multicast, NUM_FOR_EDICT(svprogfuncs, ent));\n\t\t\t\tSV_MulticastProtExt (ent->v->origin, MULTICAST_PHS, pr_global_struct->dimension_send, 0, 0);\n\t\t\t}\n\t\t}\n#endif\n\n\t\t// ZOID visibility tracking\n\t\tif (ent != clent &&\n\t\t\t!(client->spec_track && client->spec_track - 1 == j))\n\t\t{\n\t\t\tif (cl->spectator)\n\t\t\t\tcontinue;\n\n\t\t\t// ignore if not touching a PV leaf\n\t\t\tif (cameras && !sv.world.worldmodel->funcs.EdictInFatPVS(sv.world.worldmodel, &ent->pvsinfo, cameras->pvs.buffer, &cameras->numents))\n\t\t\t\tcontinue;\n\n\t\t\tif (!((int)clent->xv->dimension_see & ((int)ent->xv->dimension_seen | (int)ent->xv->dimension_ghost)))\n\t\t\t\tcontinue;\t//not in this dimension - sorry...\n\t\t\tif (cameras && (sv_cullplayers_trace.value || sv_cullentities_trace.value))\n\t\t\t\tif (Cull_Traceline(NULL, cameras, ent))\n\t\t\t\t\tcontinue;\n\t\t}\n\n\t\tif (SV_AddCSQCUpdate(client, ent))\n\t\t\tcontinue;\n\n\t\t{\n\t\t\tclstate_t clst;\n\t\t\tclst.playernum = j;\n\t\t\tclst.onladder = (int)ent->xv->pmove_flags&PMF_LADDER;\n\t\t\tclst.lastcmd = &cl->lastcmd;\n\t\t\tclst.modelindex = vent->v->modelindex;\n\t\t\tclst.frame = vent->v->frame;\n\t\t\tclst.weaponframe = ent->v->weaponframe;\n\t\t\tclst.angles = ent->v->angles;\n\t\t\tclst.origin = vent->v->origin;\n\t\t\tclst.velocity = vent->v->velocity;\n\t\t\tclst.effects = ent->v->effects;\n\t\t\tclst.vw_index = ent->xv->vw_index;\n\t\t\tclst.onground = (int)ent->v->flags & FL_ONGROUND;\n\t\t\tclst.solid = ent->v->solid && ent->v->solid != SOLID_CORPSE && ent->v->solid != SOLID_TRIGGER;\n\n#ifdef HEXEN2\n\t\t\tif (progstype == PROG_H2 && ((int)vent->v->effects & H2EF_NODRAW))\n\t\t\t{\n\t\t\t\tclst.effects = 0;\n\t\t\t\tclst.modelindex = 0;\n\t\t\t}\n#endif\n\n\t\t\tclst.skin = vent->v->skin;\n\t\t\tclst.mins = vent->v->mins;\n\t\t\tclst.hull = vent->xv->hull;\n\t\t\tclst.maxs = vent->v->maxs;\n\t\t\tclst.scale = vent->xv->scale;\n\t\t\tclst.transparency = vent->xv->alpha;\n\n\t\t\t//QSG_DIMENSION_PLANES - if the only shared dimensions are ghost dimensions, Set half alpha.\n\t\t\tif (((int)clent->xv->dimension_see & (int)ent->xv->dimension_ghost))\n\t\t\t\tif (!((int)clent->xv->dimension_see & ((int)ent->xv->dimension_seen & ~(int)ent->xv->dimension_ghost)) )\n\t\t\t\t{\n\t\t\t\t\tif (ent->xv->dimension_ghost_alpha)\n\t\t\t\t\t\tclst.transparency *= ent->xv->dimension_ghost_alpha;\n\t\t\t\t\telse\n\t\t\t\t\t\tclst.transparency *= 0.5;\n\t\t\t\t}\n\n\t\t\tclst.fatness = vent->xv->fatness;\n\t\t\tclst.localtime = cl->localtime;\n\t\t\tclst.health = ent->v->health;\n\t\t\tclst.spectator = 0;\n\t\t\tclst.fteext1 = client->fteprotocolextensions;\n\t\t\tclst.ezext1 = client->ezprotocolextensions1;\n\t\t\tclst.zext = client->zquake_extensions;\n\t\t\tclst.cl = cl;\n\n\t\t\tif (ent != vent || host_client->viewent == j+1)\n\t\t\t\tclst.modelindex = 0;\n\n#ifdef SERVER_DEMO_PLAYBACK\n\t\t\tif (sv.demostatevalid)\n\t\t\t\tclst.health = 100;\n#endif\n\n\t\t\tclst.isself = false;\n\t\t\tif ((cl == client || cl->controller == client))\n\t\t\t{\n\t\t\t\tclst.isself = true;\n\t\t\t\tclst.spectator = 0;\n\t\t\t\tif (client->spectator)\n\t\t\t\t{\n\t\t\t\t\tif (client->spec_track > 0)\n\t\t\t\t\t{\n\t\t\t\t\t\tedict_t *s = EDICT_NUM_UB(svprogfuncs, client->spec_track);\n\n\t\t\t\t\t\tclst.spectator = 2;\n\t\t\t\t\t\tclst.mins = s->v->mins;\n\t\t\t\t\t\tclst.maxs = s->v->maxs;\n\t\t\t\t\t\tclst.health = s->v->health;\n\t\t\t\t\t\tclst.weaponframe = s->v->weaponframe;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tclst.spectator = 1;\n\t\t\t\t\t\tclst.health = 1;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (client->spectator)\n\t\t\t{\n\t\t\t\tclst.health=100;\n\t\t\t\tif (client->spec_track == j+1)\n\t\t\t\t\tclst.spectator = 2;\n\t\t\t\telse\n\t\t\t\t\tclst.spectator = 1;\n\t\t\t}\n\t\t\tif (isbot)\n\t\t\t{\n\t\t\t\tclst.lastcmd = NULL;\n\t\t\t\tclst.velocity = NULL;\n\t\t\t\tclst.localtime = sv.time;\n\t\t\t\tVectorCopy(clst.origin, frame->laggedplayer[j].origin);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tVectorMA(clst.origin, (sv.time - clst.localtime), clst.velocity, frame->laggedplayer[j].origin);\n\t\t\t}\n\t\t\tVectorCopy(clst.angles, frame->laggedplayer[j].angles);\n\t\t\tframe->laggedplayer[j].present = true;\n\t\t\tSV_WritePlayerToClient(msg, &clst);\n\t\t}\n\n//FIXME: Name flags\n\t\t//player is visible, now would be a good time to update what the player is like.\n/*\t\tpflags = 0;\n#ifdef PEXT_VWEAP\n\t\tif (client->fteprotocolextensions & PEXT_VWEAP && client->otherclientsknown[j].vweap != ent->xv->vweapmodelindex)\n\t\t{\n\t\t\tpflags |= 1;\n\t\t\tclient->otherclientsknown[j].vweap = ent->xv->vweapmodelindex;\n\t\t}\n#endif\n\t\tif (pflags)\n\t\t{\n\t\t\tClientReliableWrite_Begin(client, svc_ftesetclientpersist, 10);\n\t\t\tClientReliableWrite_Short(client, pflags);\n\t\t\tif (pflags & 1)\n\t\t\t\tClientReliableWrite_Short(client, client->otherclientsknown[j].vweap);\n\t\t}\n*/\n\t}\n}\n\n#ifdef NQPROT\nvoid SVNQ_EmitEntityState(sizebuf_t *msg, entity_state_t *ent)\n{\n\tedict_t *ed = EDICT_NUM_PB(svprogfuncs, ent->number);\n\tentity_state_t *baseline = &ed->baseline;\n\nint i, eff;\nfloat miss;\nunsigned int bits=0;\n\nint glowsize=0, glowcolour=0, colourmod=0;\n\n\tfor (i=0 ; i<3 ; i++)\n\t{\n\t\tmiss = ent->origin[i] - baseline->origin[i];\n\t\tif ( miss < -0.1 || miss > 0.1 )\n\t\t\tbits |= NQU_ORIGIN1<<i;\n\t}\n\n\tif (ent->angles[0] != baseline->angles[0] )\n\t\tbits |= NQU_ANGLE1;\n\n\tif (ent->angles[1] != baseline->angles[1] )\n\t\tbits |= NQU_ANGLE2;\n\n\tif (ent->angles[2] != baseline->angles[2] )\n\t\tbits |= NQU_ANGLE3;\n\n\tif (ent->dpflags & RENDER_STEP)\n\t\tbits |= NQU_NOLERP;\t// don't mess up the step animation\n\n\tif (baseline->colormap != ent->colormap)\n\t\tbits |= NQU_COLORMAP;\n\n\tif (baseline->skinnum != ent->skinnum)\n\t\tbits |= NQU_SKIN;\n\n\tif (baseline->frame != ent->frame)\n\t\tbits |= NQU_FRAME;\n\n\teff = ent->effects;\n\n\tif ((baseline->effects & 0x00ff) != ((int)eff & 0x00ff))\n\t\tbits |= NQU_EFFECTS;\n\n\tif (baseline->modelindex != ent->modelindex)\n\t\tbits |= NQU_MODEL;\n\n\tif (ent->number >= 256)\n\t\tbits |= NQU_LONGENTITY;\n\n\n\tif (host_client->protocol == SCP_FITZ666)\n\t{\n\t\tif (baseline->trans != ent->trans)\n\t\t\tbits |= FITZU_ALPHA;\n\n\t\tif (baseline->scale != ent->scale)\n\t\t\tbits |= RMQU_SCALE;\n\n\t\tif ((baseline->frame&0xff00) != (ent->frame&0xff00))\n\t\t\tbits |= FITZU_FRAME2;\n\n\t\tif ((baseline->modelindex&0xff00) != (ent->modelindex&0xff00))\n\t\t\tbits |= FITZU_MODEL2;\n\n\t\tif (baseline->dpflags & RENDER_STEP)\n\t\t\tbits |= FITZU_LERPFINISH;\n\n\n\t\tif (host_client->qex)\n\t\t{\n\t\t\tif (host_client->edict == ed)\n\t\t\t{\t//only send some bloated things to yourself.\n\t\t\t\tbits |= QE_U_FLOATCOORDS;\t//when predicting, you'll need more precision to avoid errors\n\n\t\t\t\tif (ed->v->flags)\n\t\t\t\t\tbits |= QE_U_ENTFLAGS;\n\t\t\t}\n\t\t\tif (ed->v->solid)\n\t\t\t\tbits |= QE_U_SOLIDTYPE;\n\t\t\tif (ed->v->health)\n\t\t\t\tbits |= QE_U_HEALTH;\n\t\t\t//bits |= QE_U_UNKNOWN26;\n\t\t}\n\t}\n\telse if (host_client->protocol == SCP_BJP3)\n\t{\n\t\t//should be nehahra here, but that'll screw up DP, so don't generate anything.\n\t}\n#if 0\n\telse if (host_client->protocol == SCP_DARKPLACES6 || host_client->protocol == SCP_DARKPLACES7)\n\t{\n\t\tif (baseline->trans != ent->trans)\n\t\t\tbits |= DPU_ALPHA;\n\t\tif (baseline->scale != ent->scale)\n\t\t{\n\t\t\tif (ent->scale != 0 || baseline->scale != 1)\n\t\t\t\tbits |= DPU_SCALE;\n\t\t}\n\n\t\tif (ent->modelindex >= 256)\t//as much as protocols can handle\n\t\t\tbits |= DPU_MODEL2;\n\n\t\tif ((baseline->effects&0xff00) != ((int)eff & 0xff00))\n\t\t\tbits |= DPU_EFFECTS2;\n\n\t\tif (ent->dpflags & RENDER_EXTERIORMODEL)\n\t\t\tbits |= DPU_EXTERIORMODEL;\n\t\tif (ent->dpflags & RENDER_VIEWMODEL)\n\t\t\tbits |= DPU_VIEWMODEL;\n\n\n\t\tglowsize = ent->glowsize;\n\t\tglowcolour = ent->glowcolour;\n\n\t\tcolourmod = ((int)bound(0, ent->colormod[0] * (7.0f / 32.0f), 7) << 5) | ((int)bound(0, ent->colormod[1] * (7.0f / 32.0f), 7) << 2) | ((int)bound(0, ent->colormod[2] * (3.0f / 32.0f), 3) << 0);\n\n\t\tif (0 != glowsize)\n\t\t\tbits |= DPU_GLOWSIZE;\n\t\tif (0 != glowcolor)\n\t\t\tbits |= DPU_GLOWCOLOR;\n\n\t\tif (0 != colourmod)\n\t\t\tbits |= DPU_COLORMOD;\n\t}\n#endif\n\telse\n\t{\n\t\tif (ent->modelindex >= 256)\t//as much as protocols can handle\n\t\t\treturn;\n\t\tif (ent->number >= 600)\t\t//too many for a conventional nq client.\n\t\t\treturn;\n\t}\n\n\n\tif (bits & 0xFF000000)\n\t\tbits |= DPU_EXTEND2;\n\tif (bits & 0xFF0000)\n\t\tbits |= DPU_EXTEND1;\n\tif (bits & 0xFF00)\n\t\tbits |= NQU_MOREBITS;\n\n\n//\n// write the message\n//\n\tMSG_WriteByte (msg,(bits | NQU_SIGNAL) & 0xff); //gets caught on 'range error'\n\n\tif (bits & NQU_MOREBITS)\tMSG_WriteByte (msg, (bits>>8)&0xff);\n\tif (bits & DPU_EXTEND1)\t\tMSG_WriteByte (msg, (bits>>16)&0xff);\n\tif (bits & DPU_EXTEND2)\t\tMSG_WriteByte (msg, (bits>>24)&0xff);\n\n\tif (bits & NQU_LONGENTITY)\n\t\tMSG_WriteShort (msg,ent->number);\n\telse\n\t\tMSG_WriteByte (msg,ent->number);\n\n\tif (bits & NQU_MODEL)\n\t{\n\t\tif (host_client->protocol == SCP_BJP3)\n\t\t\tMSG_WriteShort(msg,\tent->modelindex & 0xffff);\n\t\telse\n\t\t\tMSG_WriteByte (msg,\tent->modelindex & 0xff);\n\t}\n\tif (bits & NQU_FRAME)\t\tMSG_WriteByte (msg, ent->frame & 0xff);\n\tif (bits & NQU_COLORMAP)\tMSG_WriteByte (msg, ent->colormap & 0xff);\n\tif (bits & NQU_SKIN)\t\tMSG_WriteByte (msg, ent->skinnum & 0xff);\n\tif (bits & NQU_EFFECTS)\t\tMSG_WriteByte (msg, eff & 0x00ff);\n\tif (host_client->qex && (bits & QE_U_FLOATCOORDS))\n\t{\n\t\tif (bits & NQU_ORIGIN1)\t\tMSG_WriteFloat (msg, ent->origin[0]);\n\t\tif (bits & NQU_ANGLE1)\t\tMSG_WriteAngle (msg, ent->angles[0]);\n\t\tif (bits & NQU_ORIGIN2)\t\tMSG_WriteFloat (msg, ent->origin[1]);\n\t\tif (bits & NQU_ANGLE2)\t\tMSG_WriteAngle (msg, ent->angles[1]);\n\t\tif (bits & NQU_ORIGIN3)\t\tMSG_WriteFloat (msg, ent->origin[2]);\n\t\tif (bits & NQU_ANGLE3)\t\tMSG_WriteAngle (msg, ent->angles[2]);\n\t}\n\telse\n\t{\n\t\tif (bits & NQU_ORIGIN1)\t\tMSG_WriteCoord (msg, ent->origin[0]);\n\t\tif (bits & NQU_ANGLE1)\t\tMSG_WriteAngle (msg, ent->angles[0]);\n\t\tif (bits & NQU_ORIGIN2)\t\tMSG_WriteCoord (msg, ent->origin[1]);\n\t\tif (bits & NQU_ANGLE2)\t\tMSG_WriteAngle (msg, ent->angles[1]);\n\t\tif (bits & NQU_ORIGIN3)\t\tMSG_WriteCoord (msg, ent->origin[2]);\n\t\tif (bits & NQU_ANGLE3)\t\tMSG_WriteAngle (msg, ent->angles[2]);\n\t}\n\n\tif (host_client->protocol == SCP_FITZ666)\n\t{\n\t\tif (bits & FITZU_ALPHA)\t\tMSG_WriteByte(msg, (ent->trans+1)&0xff);\n\t\tif (bits & RMQU_SCALE)\t\tMSG_WriteByte(msg, ent->scale);\n\t\tif (bits & FITZU_FRAME2)\tMSG_WriteByte(msg, ent->frame>>8);\n\t\tif (bits & FITZU_MODEL2)\tMSG_WriteByte(msg, ent->modelindex>>8);\n\t\tif (bits & FITZU_LERPFINISH)MSG_WriteByte(msg, bound(0, (int)((ed->v->nextthink - sv.world.physicstime) * 255), 255));\n\n\t\tif (host_client->qex)\n\t\t{\n\t\t\tif (bits & QE_U_SOLIDTYPE)\tMSG_WriteByte(msg, ed->v->solid);\n\t\t\tif (bits & QE_U_ENTFLAGS)\tMSG_WriteULEB128(msg, ed->v->flags);\n\t\t\tif (bits & QE_U_HEALTH)\t\tMSG_WriteSignedQEX(msg, ed->v->health);\n\t\t\tif (bits & QE_U_UNKNOWN26)\tMSG_WriteByte(msg, 0);\n\t\t}\n\t}\n\telse if (host_client->protocol == SCP_BJP3)\n\t{\n\t}\n\telse if (host_client->protocol == SCP_DARKPLACES6 || host_client->protocol == SCP_DARKPLACES7)\n\t{\n\t\tif (bits & DPU_ALPHA)\t\tMSG_WriteByte(msg, ent->trans);\n\t\tif (bits & DPU_SCALE)\t\tMSG_WriteByte(msg, ent->scale);\n\t\tif (bits & DPU_EFFECTS2)\tMSG_WriteByte(msg, eff >> 8);\n\t\tif (bits & DPU_GLOWSIZE)\tMSG_WriteByte(msg, glowsize);\n\t\tif (bits & DPU_GLOWCOLOR)\tMSG_WriteByte(msg, glowcolour);\n\t\tif (bits & DPU_COLORMOD)\tMSG_WriteByte(msg, colourmod);\n\t\tif (bits & DPU_FRAME2)\t\tMSG_WriteByte(msg, ent->frame >> 8);\n\t\tif (bits & DPU_MODEL2)\t\tMSG_WriteByte(msg, ent->modelindex >> 8);\n\t}\n}\n#endif\n\ntypedef struct gibfilter_s {\n\tstruct gibfilter_s *next;\n\tint modelindex;\n\tint minframe;\n\tint maxframe;\n} gibfilter_t;\nstatic gibfilter_t *gibfilter;\nvoid SV_GibFilterPurge(void)\n{\n\tgibfilter_t *gf;\n\twhile(gibfilter)\n\t{\n\t\tgf = gibfilter;\n\t\tgibfilter = gibfilter->next;\n\n\t\tZ_Free(gf);\n\t}\n}\n\nvoid SV_GibFilterAdd(char *modelname, int min, int max, qboolean allowwarn)\n{\n\tint i;\n\tgibfilter_t *gf;\n\n\tfor (i=1; sv.strings.model_precache[i] ; i++)\n\t\tif (!strcmp(sv.strings.model_precache[i], modelname))\n\t\t\tbreak;\n\tif (!sv.strings.model_precache[i])\n\t{\n\t\tif (allowwarn)\n\t\t\tCon_Printf(\"Filtered model \\\"%s\\\" was not precached\\n\", modelname);\n\t\treturn;\t//model not in use.\n\t}\n\n\tgf = Z_Malloc(sizeof(gibfilter_t));\n\tgf->modelindex = i;\n\tgf->minframe = ((min==-1)?0:min);\n\tgf->maxframe = ((max==-1)?0x80000000:max);\n\tgf->next = gibfilter;\n\tgibfilter = gf;\n}\nvoid SV_GibFilterInit(void)\n{\n\tchar buffer[2048];\n\tchar *file;\n\tint min, max;\n\n\tSV_GibFilterPurge();\n\n\tif (svs.gametype != GT_PROGS && svs.gametype != GT_Q1QVM)\n\t\treturn;\n\n\tfile = COM_LoadStackFile(\"gibfiltr.cfg\", buffer, sizeof(buffer), NULL);\n\tif (!file)\n\t{\n\t\tCon_DPrintf(\"gibfiltr.cfg file was not found. Using defaults\\n\");\n\t\tSV_GibFilterAdd(\"progs/gib1.mdl\", -1, -1, false);\n\t\tSV_GibFilterAdd(\"progs/gib2.mdl\", -1, -1, false);\n\t\tSV_GibFilterAdd(\"progs/gib3.mdl\", -1, -1, false);\n\t\tSV_GibFilterAdd(\"progs/h_player.mdl\", -1, -1, false);\n//\t\tSV_GibFilterAdd(\"progs/player.mdl\", 49, 49, false);\n//\t\tSV_GibFilterAdd(\"progs/player.mdl\", 60, 60, false);\n//\t\tSV_GibFilterAdd(\"progs/player.mdl\", 69, 69, false);\n//\t\tSV_GibFilterAdd(\"progs/player.mdl\", 84, 84, false);\n//\t\tSV_GibFilterAdd(\"progs/player.mdl\", 93, 93, false);\n//\t\tSV_GibFilterAdd(\"progs/player.mdl\", 102, 102, false);\n\t\treturn;\n\t}\n\twhile(file)\n\t{\n\t\tfile = COM_Parse(file);\n\t\tif (!file)\n\t\t{\n\t\t\treturn;\n\t\t}\n\t\tmin = atoi(com_token);\n\t\tfile = COM_Parse(file);\t//handles nulls nicly\n\t\tmax = atoi(com_token);\n\t\tfile = COM_Parse(file);\n\t\tif (!file)\n\t\t{\n\t\t\tCon_Printf(\"Sudden ending to gibfiltr.cfg\\n\");\n\t\t\treturn;\n\t\t}\n\t\tSV_GibFilterAdd(com_token, min, max, true);\n\t}\n}\nqboolean SV_GibFilter(edict_t\t*ent)\n{\n\tint indx = ent->v->modelindex;\n\tint frame = ent->v->frame;\n\tgibfilter_t *gf;\n\n\tfor (gf = gibfilter; gf; gf=gf->next)\n\t{\n\t\tif (gf->modelindex == indx)\n\t\t\tif (frame >= gf->minframe && frame <= gf->maxframe)\n\t\t\t\treturn true;\n\t}\n\n\treturn false;\n}\n\n#ifdef SERVER_DEMO_PLAYBACK\nstatic void SV_Snapshot_Build_Playback(client_t *client, packet_entities_t *pack)\n{\n\tint e;\n\tentity_state_t\t*state;\n\tmvdentity_state_t *dement;\n\t\tfor (e=1, dement=&sv.demostate[e] ; e<=sv.demomaxents ; e++, dement++)\n\t\t{\n\t\t\tif (!dement->modelindex)\n\t\t\t\tcontinue;\n\n\t\t\tif (e >= 1 && e <= svs.allocated_client_slots)\n\t\t\t\tcontinue;\n\n\t\t\tif (pack->num_entities == pack->max_entities)\n\t\t\t\tcontinue;\t// all full\n\n\t\t\t//the entity would mess up the client and possibly disconnect them.\n\t\t\t//FIXME: add an option to drop clients... entity fog could be killed in this way.\n\t\t\tif (e >= 512 && !(client->fteprotocolextensions & PEXT_ENTITYDBL))\n\t\t\t\tcontinue;\n\t\t\tif (e >= 1024 && !(client->fteprotocolextensions & PEXT_ENTITYDBL2))\n\t\t\t\tcontinue;\n//\t\t\tif (dement->modelindex >= 256 && !(client->fteprotocolextensions & PEXT_MODELDBL))\n//\t\t\t\tcontinue;\n\n\t\t\tstate = &pack->entities[pack->num_entities];\n\t\t\tpack->num_entities++;\n\n\t\t\tstate->number = e;\n\t\t\tstate->flags = EF_DIMLIGHT;\n\t\t\tVectorCopy (dement->origin, state->origin);\n\t\t\tstate->angles[0] = dement->angles[0]*360.0f/256;\n\t\t\tstate->angles[1] = dement->angles[1]*360.0f/256;\n\t\t\tstate->angles[2] = dement->angles[2]*360.0f/256;\n\t\t\tstate->modelindex = dement->modelindex;\n\t\t\tstate->frame = dement->frame;\n\t\t\tstate->colormap = dement->colormap;\n\t\t\tstate->skinnum = dement->skinnum;\n\t\t\tstate->effects = dement->effects;\n\n#ifdef PEXT_SCALE\n\t\t\tstate->scale = dement->scale;\n#endif\n#ifdef PEXT_TRANS\n\t\t\tstate->trans = dement->trans;\n#endif\n#ifdef PEXT_FATNESS\n\t\t\tstate->fatness = dement->fatness;\n#endif\n\t\t}\n\n\t\tfor (e = 0; e < sv.numdemospikes; e++)\n\t\t{\n\t\t\tif (SV_DemoNailUpdate (e))\n\t\t\t\tcontinue;\n\t\t}\n}\n#endif\n\nvoid SV_Snapshot_BuildStateQ1(entity_state_t *state, edict_t *ent, client_t *client, packet_entities_t *pack)\n{\n//builds an entity_state from an entity\n//note that client can be null, for building baselines.\n\n\tint i;\n\tstate->number = NUM_FOR_EDICT(svprogfuncs, ent);\n\n\tstate->u.q1.msec = 0;\n\tstate->u.q1.pmovetype = 0;\n\tstate->u.q1.movement[0] = 0;\n\tstate->u.q1.movement[1] = 0;\n\tstate->u.q1.movement[2] = 0;\n\tstate->u.q1.velocity[0] = 0;\n\tstate->u.q1.velocity[1] = 0;\n\tstate->u.q1.velocity[2] = 0;\n\n\tVectorCopy (ent->v->origin, state->origin);\n\tVectorCopy (ent->v->angles, state->angles);\n\n\tstate->u.q1.weaponframe = 0;\n\tif ((state->number-1) < (unsigned int)sv.allocated_client_slots && (client == &svs.clients[state->number-1] || client == svs.clients[state->number-1].controller || (client && (!client->edict || client->spec_track == state->number))))\n\t\tif (!client || !(client->fteprotocolextensions2 & PEXT2_PREDINFO))\n\t\t\tstate->u.q1.weaponframe = ent->v->weaponframe;\n\tif ((state->number-1) < (unsigned int)sv.allocated_client_slots && ent->v->movetype && client)\n\t{\n\t\tclient_t *cl = &svs.clients[state->number-1];\n\t\textern cvar_t sv_nqplayerphysics;\n\t\tif (cl->isindependant || sv_nqplayerphysics.ival==2)\n\t\t{\n\t\t\tstate->u.q1.pmovetype = ent->v->movetype;\n\t\t\tif (state->u.q1.pmovetype && ((int)ent->v->flags & FL_ONGROUND) && (client->zquake_extensions&Z_EXT_PF_ONGROUND))\n\t\t\t\tstate->u.q1.pmovetype |= 0x80;\n\t\t\tif (state->u.q1.pmovetype && ((int)cl->jump_held) && (client->zquake_extensions&Z_EXT_PM_TYPE))\n\t\t\t\tstate->u.q1.pmovetype |= 0x40;\n\t\t\tif (cl != client && client)\n\t\t\t{\t/*only generate movement values if the client doesn't already know them...*/\n\t\t\t\tstate->u.q1.movement[0] = ent->xv->movement[0];\n\t\t\t\tstate->u.q1.movement[1] = ent->xv->movement[1];\n\t\t\t\tstate->u.q1.movement[2] = ent->xv->movement[2];\n\t\t\t\tstate->u.q1.msec = bound(0, 1000*(sv.time - cl->localtime), 255);\n\t\t\t}\n\n\t\t\tstate->u.q1.velocity[0] = ent->v->velocity[0] * 8;\n\t\t\tstate->u.q1.velocity[1] = ent->v->velocity[1] * 8;\n\t\t\tstate->u.q1.velocity[2] = ent->v->velocity[2] * 8;\n\t\t}\n\t\telse if (ent == cl->edict)\n\t\t{\n\t\t\tstate->u.q1.velocity[0] = ent->v->velocity[0] * 8;\n\t\t\tstate->u.q1.velocity[1] = ent->v->velocity[1] * 8;\n\t\t\tstate->u.q1.velocity[2] = ent->v->velocity[2] * 8;\n\t\t}\n\n\t\t//fixme: deal with fixangles\n\t\tif (client->fteprotocolextensions2 & PEXT2_PREDINFO)\n\t\t{\n\t\t\tstate->u.q1.vangle[0] = ANGLE2SHORT(ent->v->v_angle[0]);\n\t\t\tstate->u.q1.vangle[1] = ANGLE2SHORT(ent->v->v_angle[1]);\n\t\t\tstate->u.q1.vangle[2] = ANGLE2SHORT(ent->v->v_angle[2]);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (client->fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS)\n\t\t\tif (state->u.q1.pmovetype && ((state->u.q1.pmovetype&0x7f) != MOVETYPE_TOSS && (state->u.q1.pmovetype&0x7f) != MOVETYPE_BOUNCE))\n\t\t\t{\n\t\t\t\tstate->angles[0] = ent->v->v_angle[0]/-3.0;\n\t\t\t\tstate->angles[1] = ent->v->v_angle[1];\n\t\t\t\tstate->angles[2] = ent->v->v_angle[2];\n\t\t\t}\n\t\t}\n\t}\n\n\tif (client && client->edict && (ent->v->owner == client->edict->entnum))\n\t\tstate->solidsize = 0;\n\telse if (ent->v->solid == SOLID_BSP || (ent->v->skin < 0 && ent->v->modelindex))\n\t\tstate->solidsize = ES_SOLID_BSP;\n\telse if (ent->v->solid == SOLID_BBOX || ent->v->solid == SOLID_SLIDEBOX || ent->v->skin < 0)\n\t\tstate->solidsize = ent->solidsize;\n\telse\n\t\tstate->solidsize = 0;\n\n\tstate->dpflags = 0;\n\tif (ent->xv->viewmodelforclient)\n\t{\t//this ent would have been filtered out by now if its not ours\n\t\t//if ent->viewmodelforclient == client then:\n\t\tstate->dpflags |= RENDER_VIEWMODEL;\n\t}\n\tstate->colormap = ent->v->colormap;\n\tif (state->colormap >= 1024)\n\t\tstate->dpflags |= RENDER_COLORMAPPED;\n\telse if (client && state->colormap > client->max_net_clients)\n\t\tstate->colormap = 0;\n\n\tif (ent->xv->exteriormodeltoclient && client)\n\t{\n\t\tif (ent->xv->exteriormodeltoclient == EDICT_TO_PROG(svprogfuncs, client->edict))\n\t\t\tstate->dpflags |= RENDER_EXTERIORMODEL;\n\t\t//everyone else sees it normally.\n\t}\n\n\tif (ent->xv->basebone < 0)\n\t{\n#ifdef SKELETALMODELS\n\t\tif (ent->xv->skeletonindex && pack)\n\t\t{\n\t\t\tframestate_t fs;\n\t\t\tfs.skeltype = SKEL_IDENTITY;\n\t\t\tfs.bonecount = 0;\n\t\t\tskel_lookup(&sv.world, ent->xv->skeletonindex, &fs);\n\t\t\tif (fs.skeltype == SKEL_RELATIVE && fs.bonecount)\n\t\t\t{\n\t\t\t\tBones_To_PosQuat4(fs.bonecount, fs.bonestate, AllocateBoneSpace(pack, state->bonecount = fs.bonecount, &state->boneoffset));\n\t\t\t\t//state->dpflags |= RENDER_COMPLEXANIMATION;\n\t\t\t}\n\t\t}\n#endif\n\t}\n\telse\n\t{\n\t\tstate->basebone = ent->xv->basebone;\n\t\tstate->baseframe = ent->xv->baseframe;\n\t}\n\n\tif (!ent->v->movetype || ent->v->movetype == MOVETYPE_STEP)\n\t\tstate->dpflags |= RENDER_STEP;\n\n\tstate->modelindex = ent->v->modelindex;\n\tstate->modelindex2 = ent->xv->vw_index;\n\tstate->frame = ent->v->frame;\n\tstate->skinnum = ent->v->skin;\n\tstate->effects = ent->v->effects;\n\tstate->effects |= (int)ent->xv->modelflags<<24;\n#ifdef HEXEN2\n\tstate->hexen2flags = ent->xv->drawflags;\n\tstate->abslight = (int)(ent->xv->abslight*255) & 255;\n#endif\n\tstate->tagentity = ent->xv->tag_entity;\n\tstate->tagindex = ent->xv->tag_index;\n\n\tstate->light[0] = ent->xv->color[0]*1024;\n\tstate->light[1] = ent->xv->color[1]*1024;\n\tstate->light[2] = ent->xv->color[2]*1024;\n\tstate->light[3] = ent->xv->light_lev;\n\tstate->lightstyle = ent->xv->style;\n\tstate->lightpflags = ent->xv->pflags;\n\tstate->u.q1.traileffectnum = ent->xv->traileffectnum;\n\tstate->u.q1.emiteffectnum = ent->xv->emiteffectnum;\n\n\tif (ent->xv->gravitydir[2] == -1)\n\t{\n\t\tstate->u.q1.gravitydir[0] = 0;\n\t\tstate->u.q1.gravitydir[1] = 0;\n\t}\n\telse if ((!ent->xv->gravitydir[0] && !ent->xv->gravitydir[1] && !ent->xv->gravitydir[2]))// || (ent->xv->gravitydir[2] == -1))\n\t{\n\t\tvec3_t ang;\n\t\tif (sv.world.g.defaultgravitydir[2] == -1)\n\t\t{\n\t\t\tstate->u.q1.gravitydir[0] = 0;\n\t\t\tstate->u.q1.gravitydir[1] = 0;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tVectorAngles(sv.world.g.defaultgravitydir, NULL, ang, false);\n\t\t\tstate->u.q1.gravitydir[0] = ((ang[0]/360) * 256) - 192;\n\t\t\tstate->u.q1.gravitydir[1] = (ang[1]/360) * 256;\n\t\t}\n\t}\n\telse\n\t{\n\t\tvec3_t ang;\n\t\tVectorAngles(ent->xv->gravitydir, NULL, ang, false);\n\t\tstate->u.q1.gravitydir[0] = ((ang[0]/360) * 256) - 192;\n\t\tstate->u.q1.gravitydir[1] = (ang[1]/360) * 256;\n\t}\n\n\tif (((int)ent->v->flags & FL_CLASS_DEPENDENT) && client && client->playerclass)\t//hexen2 wierdness.\n\t{\n\t\tchar modname[MAX_QPATH];\n\t\tQ_strncpyz(modname, sv.strings.model_precache[state->modelindex], sizeof(modname));\n\t\tif (strlen(modname)>5)\n\t\t{\n\t\t\tmodname[strlen(modname)-5] = client->playerclass+'0';\n\t\t\tstate->modelindex = SV_ModelIndex(modname);\n\t\t}\n\t}\n\n\tif (state->effects & DPEF_LOWPRECISION)\n\t\tstate->effects &= ~DPEF_LOWPRECISION;\t//we don't support it, nor does dp any more. strip it.\n\n\tif (ent->v->nextthink>sv.world.physicstime)\n\t\tstate->lerpend = ent->v->nextthink;\n\n#ifdef NQPROT\n\tif (client && !ISQWCLIENT(client))\n\t{\n\t\tif (ent->muzzletime > client->lastoutgoingphysicstime && ent->muzzletime <= (float)sv.world.physicstime)\n\t\t\tstate->effects |= EF_MUZZLEFLASH;\n\n\t\tif (client->spectator && !client->spec_track && ent == client->edict)\n\t\t\tstate->modelindex = sv_playermodel;\n\t}\n\n\tif (sv.world.remasterlogic)\n\t{\n\t\tif (state->effects & (REEF_QUADLIGHT|REEF_PENTLIGHT|REEF_CANDLELIGHT))\n\t\t{\t//remap these flags to something new.\n\t\t\tunsigned int old = state->effects;\n\t\t\tstate->effects &= ~(REEF_QUADLIGHT|REEF_PENTLIGHT|REEF_CANDLELIGHT);\n\t\t\tif (old & REEF_QUADLIGHT)\n\t\t\t\tstate->effects |= EF_BLUE;\n\t\t\tif (old & REEF_PENTLIGHT)\n\t\t\t\tstate->effects |= EF_RED;\n\t\t\tif (old & REEF_CANDLELIGHT)\n\t\t\t\tstate->effects |= 0;\n\t\t}\n\t}\n\telse if (progstype != PROG_QW)\n\t{\n\t\tif (progstype == PROG_TENEBRAE)\n\t\t{\n\t\t\t//tenebrae has some hideous hacks\n\t\t\tif (!strcmp(sv.strings.model_precache[state->modelindex], \"progs/w_light.spr\") ||\n\t\t\t\t!strcmp(sv.strings.model_precache[state->modelindex], \"progs/b_light.spr\") ||\n\t\t\t\t!strcmp(sv.strings.model_precache[state->modelindex], \"progs/s_light.spr\") ||\n\t\t\t\t!strcmp(sv.strings.model_precache[state->modelindex], \"progs/flame.mdl\") ||\n\t\t\t\t!strcmp(sv.strings.model_precache[state->modelindex], \"progs/flame2.mdl\"))\n\t\t\t{\n\t\t\t\t//fixme: add some default colours\n\t\t\t\tstate->lightpflags |= PFLAGS_FULLDYNAMIC;\n\t\t\t\tif (!state->light[3])\n\t\t\t\t\tstate->light[3] = 350;\n\t\t\t}\n\n\t\t\tif (!strcmp (sv.strings.model_precache[state->modelindex], \"progs/lavaball.mdl\"))\n\t\t\t{\n\t\t\t\tstate->lightpflags |= PFLAGS_FULLDYNAMIC;\n\t\t\t\tstate->skinnum = 17;\n\t\t\t\tstate->light[3] = 270;\n\t\t\t}\n\t\t}\n\t\tif (state->effects && client && ISQWCLIENT(client))\t//don't send extra nq effects to a qw client.\n\t\t{\n\t\t\t//EF_NODRAW doesn't draw the model.\n\t\t\t//The client still needs to know about it though, as it might have other effects on it.\n\t\t\tif (progstype == PROG_H2)\n\t\t\t{\n\t\t\t\tif (state->effects & H2EF_NODRAW)\n\t\t\t\t{\n\t\t\t\t\t//actually, H2 is pretty lame about this\n\t\t\t\t\tstate->effects = 0;\n\t\t\t\t\tstate->modelindex = 0;\n\t\t\t\t\tstate->frame = 0;\n\t\t\t\t\tstate->colormap = 0;\n\t\t\t\t\tstate->abslight = 0;\n\t\t\t\t\tstate->skinnum = 0;\n\t\t\t\t\tstate->hexen2flags = 0;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (progstype == PROG_TENEBRAE)\n\t\t\t{\n\t\t\t\tif (state->effects & TENEBRAEEF_FULLDYNAMIC)\t//tenebrae's EF_FULLDYNAMIC\n\t\t\t\t{\n\t\t\t\t\tstate->effects &= ~TENEBRAEEF_FULLDYNAMIC;\n\t\t\t\t\tstate->lightpflags |= PFLAGS_FULLDYNAMIC;\n\t\t\t\t}\n\t\t\t\tif (state->effects & TENEBRAEEF_GREEN)\t//tenebrae's EF_GREEN\n\t\t\t\t{\n\t\t\t\t\tstate->effects &= ~TENEBRAEEF_GREEN;\n\t\t\t\t\tstate->effects |= EF_GREEN;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (state->number <= sv.allocated_client_slots) // clear only client ents\n\t\t\t{\n\t\t\t\tif (state->effects & NQEF_NODRAW)\n\t\t\t\t\tstate->modelindex = 0;\n\t\t\t\tstate->effects &= ~ (QWEF_FLAG1|QWEF_FLAG2);\n\t\t\t}\n\n\t\t\tif ((state->effects & EF_DIMLIGHT) && !(state->effects & (EF_RED|EF_BLUE)))\n\t\t\t{\n\t\t\t\tint it = ent->v->items;\n\t\t\t\tstate->effects &= ~EF_DIMLIGHT;\n\t\t\t\tif ((it & (IT_INVULNERABILITY|IT_QUAD)) == (IT_INVULNERABILITY|IT_QUAD))\n\t\t\t\t\tstate->effects |= EF_RED|EF_BLUE;\n\t\t\t\telse if (it & IT_INVULNERABILITY)\n\t\t\t\t\tstate->effects |= EF_RED;\n\t\t\t\telse if (it & IT_QUAD)\n\t\t\t\t\tstate->effects |= EF_BLUE;\n\t\t\t\telse\n\t\t\t\t\tstate->effects |= EF_DIMLIGHT;\n\t\t\t}\n\t\t}\n\t}\n#endif\n\n\tif (!ent->xv->colormod[0] && !ent->xv->colormod[1] && !ent->xv->colormod[2])\n\t{\n\t\tstate->colormod[0] = (256)/8;\n\t\tstate->colormod[1] = (256)/8;\n\t\tstate->colormod[2] = (256)/8;\n\t}\n\telse\n\t{\n\t\ti = ent->xv->colormod[0]*(256/8); state->colormod[0] = bound(0, i, 255);\n\t\ti = ent->xv->colormod[1]*(256/8); state->colormod[1] = bound(0, i, 255);\n\t\ti = ent->xv->colormod[2]*(256/8); state->colormod[2] = bound(0, i, 255);\n\t}\n\tif (!ent->xv->glowmod[0] && !ent->xv->glowmod[1] && !ent->xv->glowmod[2])\n\t{\n\t\tstate->glowmod[0] = (256/8);\n\t\tstate->glowmod[1] = (256/8);\n\t\tstate->glowmod[2] = (256/8);\n\t}\n\telse\n\t{\n\t\tstate->glowmod[0] = ent->xv->glowmod[0]*(256/8);\n\t\tstate->glowmod[1] = ent->xv->glowmod[1]*(256/8);\n\t\tstate->glowmod[2] = ent->xv->glowmod[2]*(256/8);\n\t}\n\tstate->glowsize = ent->xv->glow_size*0.25;\n\tstate->glowcolour = ent->xv->glow_color;\n\tif (ent->xv->glow_trail)\n\t\tstate->dpflags |= RENDER_GLOWTRAIL;\n\n\n#ifdef PEXT_SCALE\n\tif (!ent->xv->scale)\n\t\tstate->scale = 1*16;\n\telse\n\t\tstate->scale = bound(1, ent->xv->scale*16, 255);\n\n#endif\n#ifdef PEXT_TRANS\n\tif (!ent->xv->alpha)\n\t\tstate->trans = 255;\n\telse\n\t\tstate->trans = bound(1, ent->xv->alpha*254, 254);\n\n\t//QSG_DIMENSION_PLANES - if the only shared dimensions are ghost dimensions, Set half alpha.\n\tif (client && client->edict)\n\t{\n\t\tif (((int)client->edict->xv->dimension_see & (int)ent->xv->dimension_ghost))\n\t\t\tif (!((int)client->edict->xv->dimension_see & ((int)ent->xv->dimension_seen & ~(int)ent->xv->dimension_ghost)) )\n\t\t\t{\n\t\t\t\tif (ent->xv->dimension_ghost_alpha)\n\t\t\t\t\tstate->trans *= ent->xv->dimension_ghost_alpha;\n\t\t\t\telse\n\t\t\t\t\tstate->trans *= 0.5;\n\t\t\t}\n\t}\n#endif\n#ifdef PEXT_FATNESS\n\tstate->fatness = ent->xv->fatness;\n#endif\n\n#pragma warningmsg(\"TODO: Fix attachments for more vanilla clients\")\n}\n\nvoid SV_Snapshot_BuildQ1(client_t *client, packet_entities_t *pack, pvscamera_t *cameras, edict_t *clent)\n{\n//pvs and clent can be null, but only if the other is also null\n\tint e, i;\n\tedict_t *ent, *tracecullent;\t//tracecullent is different from ent because attached models cull the parent instead. also, null for entities which are not culled.\n\tentity_state_t\t*state;\n#define DEPTHOPTIMISE\n#ifdef DEPTHOPTIMISE\n\tvec3_t org;\n\tfloat *distances = NULL;\n\tfloat dist;\n#endif\n\tglobalvars_t *pr_globals = PR_globals(svprogfuncs, PR_CURRENT);\n\tint pvsflags;\n\tint limit;\n\tint c, maxc = cameras?cameras->numents:0;\n\tclient_t *seat;\n\n\tlimit = sv.world.num_edicts;\n\tif (client->max_net_ents < limit)\n\t{\n\t\tlimit = client->max_net_ents;\n\t\tif (!(client->plimitwarned & PLIMIT_ENTITIES))\n\t\t{\n\t\t\tclient->plimitwarned |= PLIMIT_ENTITIES;\n\t\t\tSV_ClientPrintf(client, PRINT_HIGH, \"WARNING: Your client's network protocol only supports %i entities. Please upgrade or enable extensions.\\n\", client->max_net_ents);\n\t\t}\n\t}\n\n#ifdef DEPTHOPTIMISE\n\tif (clent && ISQWCLIENT(client) && client->max_net_ents<=512)\t//the vanilla QW client is shite and only supports 64 visible ents at a time... it can get cpu-heavy though, so don't waste time with other clients.\n\t\tdistances = alloca(sizeof(*distances)*limit);\n#endif\n\n\t//this entity is watching from outside themselves. The client is tricked into thinking that they themselves are in the view ent, and a new dummy ent (the old them) must be spawned.\n\tif (clent && ISQWCLIENT(client))\n\t{\n\t\tfor (seat = client; seat; seat = seat->controlled)\n\t\t{\n\t\t\tedict_t *clent = seat->edict;\n\t\t\tif (!client->viewent)\n\t\t\t\tcontinue;\n//FIXME: this hack needs cleaning up\n#ifdef DEPTHOPTIMISE\n\t\t\tif (distances)\n\t\t\t\tdistances[pack->num_entities] = 0;\n#endif\n\t\t\tstate = &pack->entities[pack->num_entities];\n\t\t\tpack->num_entities++;\n\n\t\t\tSV_Snapshot_BuildStateQ1(state, clent, seat, pack);\n\n\t\t\tstate->number = seat - svs.clients + 1;\n\n\t\t\t//yeah, I doubt anyone will need this\n\t\t\tif (progstype == PROG_QW)\n\t\t\t{\n\t\t\t\tif ((int)clent->v->effects & QWEF_FLAG1)\n\t\t\t\t{\n\t\t\t\t\tmemcpy(&pack->entities[pack->num_entities], state, sizeof(*state));\n\t\t\t\t\tstate = &pack->entities[pack->num_entities];\n\t\t\t\t\tpack->num_entities++;\n\t\t\t\t\tstate->modelindex = SV_ModelIndex(\"progs/flag.mdl\");\n\t\t\t\t\tstate->frame = 0;\n\t\t\t\t\tstate->number++;\t//yeek\n\t\t\t\t\tstate->skinnum = 0;\n\t\t\t\t}\n\t\t\t\telse if ((int)clent->v->effects & QWEF_FLAG2)\n\t\t\t\t{\n\t\t\t\t\tmemcpy(&pack->entities[pack->num_entities], state, sizeof(*state));\n\t\t\t\t\tstate = &pack->entities[pack->num_entities];\n\t\t\t\t\tpack->num_entities++;\n\t\t\t\t\tstate->modelindex = SV_ModelIndex(\"progs/flag.mdl\");\n\t\t\t\t\tstate->frame = 0;\n\t\t\t\t\tstate->number++;\t//yeek\n\t\t\t\t\tstate->skinnum = 1;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\n\t/*legacy qw clients get their players separately*/\n\tif (ISQWCLIENT(client) && !(client->fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS))\n\t\te = min(sv.allocated_client_slots+1, client->max_net_clients);\n\telse\n\t\te = 1;\n\n\tif (client->penalties & BAN_BLIND)\n\t{\n\t\te = client->edict->entnum;\n\t\tlimit = e+1;\n\t}\n\n\tif (sv_cullentities_trace.ival && client->lastseen_count < limit)\n\t\tZ_ReallocElements((void**)&client->lastseen_time, &client->lastseen_count, limit, sizeof(client->lastseen_time));\n\n\tfor ( ; e<limit ; e++)\n\t{\n\t\tent = EDICT_NUM_PB(svprogfuncs, e);\n\t\tif (ED_ISFREE(ent))\n\t\t\tcontinue;\n\n\t\tif (ent->xv->customizeentityforclient)\n\t\t{\n\t\t\tpr_global_struct->self = EDICT_TO_PROG(svprogfuncs, ent);\n\t\t\tpr_global_struct->other = (clent?EDICT_TO_PROG(svprogfuncs, clent):0);\n\t\t\tPR_ExecuteProgram(svprogfuncs, ent->xv->customizeentityforclient);\n\t\t\tif(!G_FLOAT(OFS_RETURN))\n\t\t\t\tcontinue;\n\t\t}\n\n#ifdef NQPROT\n\t\tif (progstype != PROG_QW)\n\t\t{\n\t\t\tif ((int)ent->v->effects & EF_MUZZLEFLASH)\n\t\t\t{\n\t\t\t\tent->v->effects = (int)ent->v->effects & ~EF_MUZZLEFLASH;\n\t\t\t\tent->muzzletime = sv.world.physicstime;\n\t\t\t\tMSG_WriteByte (&sv.multicast, svc_muzzleflash);\n\t\t\t\tMSG_WriteEntity (&sv.multicast, NUM_FOR_EDICT(svprogfuncs, ent));\n\t\t\t\tSV_MulticastProtExt (ent->v->origin, MULTICAST_PHS, pr_global_struct->dimension_send, 0, 0);\n\t\t\t}\n\t\t}\n#endif\n\n\t\tpvsflags = ent->xv->pvsflags;\n\t\tfor (c = 0; c < maxc; c++)\n\t\t{\n\t\t\tif (ent == cameras->ent[c])\n\t\t\t\tbreak;\n\t\t}\n\t\tif (c < maxc)\n\t\t\ttracecullent = NULL;\n\t\telse if (ent->xv->viewmodelforclient)\n\t\t{\n\t\t\tif (ent->xv->viewmodelforclient != (clent?EDICT_TO_PROG(svprogfuncs, clent):0))\n\t\t\t\tcontinue;\n\t\t\ttracecullent = NULL;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// many ents are not intended to be networked.\n\t\t\tif (!(ent->xv->SendEntity && client->csqcactive) &&\t//if SendEntity is set then its definitely important, even if not visible.\n\t\t\t\t(!ent->v->modelindex || !*PR_GetString(svprogfuncs, ent->v->model)) && // also definitely valid if it has a model\n\t\t\t\t!((int)ent->xv->pflags & PFLAGS_FULLDYNAMIC) &&\t//needs to be networked if its giving off realtime lights, even when it has no model.\n\t\t\t\tent->v->skin >= 0)\t//ents with negative skins are networked too. eg ladder volumes.\n\t\t\t\tcontinue;\n\n\t\t\tif (cameras)\t//self doesn't get a pvs test, to cover teleporters\n\t\t\t{\n\t\t\t\tif ((int)ent->v->effects & EF_NODEPTHTEST)\n\t\t\t\t\ttracecullent = NULL;\n\t\t\t\telse if ((pvsflags & PVSF_MODE_MASK) < PVSF_USEPHS)\n\t\t\t\t{\n\t\t\t\t\t//branch out to the pvs testing.\n\t\t\t\t\tif (ent->xv->tag_entity)\n\t\t\t\t\t{\n\t\t\t\t\t\tint c = 10;\n\t\t\t\t\t\ttracecullent = ent;\n\t\t\t\t\t\twhile(tracecullent->xv->tag_entity&&c-->0)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttracecullent = EDICT_NUM_UB(svprogfuncs, tracecullent->xv->tag_entity);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (tracecullent == clent)\n\t\t\t\t\t\t\ttracecullent = NULL;\n\t\t\t\t\t\telse if (tracecullent->xv->viewmodelforclient)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t//special hack so viewmodelforclient on the root of the tagged entity overrides pvs\n\t\t\t\t\t\t\tif (tracecullent->xv->viewmodelforclient != (clent?EDICT_TO_PROG(svprogfuncs, clent):0))\n\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\ttracecullent = NULL;\t//don't tracecull\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (!sv.world.worldmodel->funcs.EdictInFatPVS(sv.world.worldmodel, &((wedict_t*)tracecullent)->pvsinfo, cameras->pvs.buffer, cameras->area))\n\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tif (!sv.world.worldmodel->funcs.EdictInFatPVS(sv.world.worldmodel, &((wedict_t*)ent)->pvsinfo, cameras->pvs.buffer, cameras->area))\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\ttracecullent = ent;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse if ((pvsflags & PVSF_MODE_MASK) == PVSF_USEPHS && sv.world.worldmodel->fromgame == fg_quake)\n\t\t\t\t{\n\t\t\t\t\tint cluster;\n\t\t\t\t\tunsigned char *mask;\n\t\t\t\t\tqbyte *phs = sv.world.worldmodel->phs;\n\t\t\t\t\tif (phs)\n\t\t\t\t\t{\n\t\t\t\t\t\t//FIXME: this lookup should be cachable or something.\n\t\t\t\t\t\tif (client->edict)\n\t\t\t\t\t\t\tcluster = sv.world.worldmodel->funcs.ClusterForPoint(sv.world.worldmodel, client->edict->v->origin, NULL);\t//ignore areas, can hear through doors.\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tcluster = -1;\t//mvd\n\t\t\t\t\t\tif (cluster >= 0)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tmask = phs + cluster * 4*((sv.world.worldmodel->numclusters+31)>>5);\n\n\t\t\t\t\t\t\tcluster = sv.world.worldmodel->funcs.ClusterForPoint (sv.world.worldmodel, ent->v->origin, NULL);\n\t\t\t\t\t\t\tif (cluster >= 0 && !(mask[cluster>>3] & (1<<(cluster&7)) ) )\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\ttracecullent = NULL;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\ttracecullent = NULL;\n\n\t\t\t\tif (client->gibfilter && SV_GibFilter(ent))\n\t\t\t\t\tcontinue;\n\n#ifdef VM_Q1\n\t\t\t\t//mvdsv compat\n\t\t\t\tif (client->hideentity && EDICT_TO_PROG(svprogfuncs, ent) == client->hideentity)\n\t\t\t\t\tcontinue;\n\t\t\t\tif (client->hideplayers && e <= sv.allocated_client_slots)\n\t\t\t\t\tcontinue;\n#endif\n\t\t\t}\n\t\t\telse\n\t\t\t\ttracecullent = NULL;\n\t\t}\n\n\t\t//DP_SV_NODRAWONLYTOCLIENT\n\t\tif (ent->xv->nodrawtoclient)\t//DP extension.\n\t\t\tif (client->edict && ent->xv->nodrawtoclient == EDICT_TO_PROG(svprogfuncs, client->edict))\n\t\t\t\tcontinue;\n\t\t//DP_SV_DRAWONLYTOCLIENT\n\t\tif (ent->xv->drawonlytoclient)\n\t\t\tif (!client->edict || ent->xv->drawonlytoclient != EDICT_TO_PROG(svprogfuncs, client->edict))\n\t\t\t{\n\t\t\t\tclient_t *split;\n\t\t\t\tfor (split = client->controlled; split; split=split->controlled)\n\t\t\t\t{\n\t\t\t\t\tif (split->edict->xv->view2 == EDICT_TO_PROG(svprogfuncs, ent))\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tif (!split)\n\t\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t//QSG_DIMENSION_PLANES\n\t\tif (clent)\t//don't crash\n\t\t\tif (!((int)clent->xv->dimension_see & ((int)ent->xv->dimension_seen | (int)ent->xv->dimension_ghost)))\t//not able to see it.\n\t\t\t\tif (c >= maxc)\t//always network the player entity though\n\t\t\t\t\tcontinue;\n\n\n\t\tif (cameras && tracecullent && !((unsigned int)ent->v->effects & (EF_DIMLIGHT|EF_BLUE|EF_RED|EF_BRIGHTLIGHT|EF_BRIGHTFIELD|EF_NODEPTHTEST)))\n\t\t{\t//more expensive culling\n\t\t\tif (!(pvsflags & PVSF_MODE_MASK))\n\t\t\t\tif ((e <= sv.allocated_client_slots && sv_cullplayers_trace.value) || sv_cullentities_trace.value)\n\t\t\t\t\tif (Cull_Traceline(e < client->lastseen_count?&client->lastseen_time[e]:NULL, cameras, tracecullent))\n\t\t\t\t\t\tcontinue;\n\t\t}\n\n\t\t//EXT_CSQC\n\t\tif (SV_AddCSQCUpdate(client, ent))\t//csqc took it.\n\t\t\tcontinue;\n\n\t\tif (ISQWCLIENT(client))\n\t\t{\n\t\t\tif (SV_AddNailUpdate (ent))\n\t\t\t\tcontinue;\t// added to the special update list\n\t\t}\n\n\t\t//the entity would mess up the client and possibly disconnect them.\n\t\t//FIXME: add an option to drop clients... entity fog could be killed in this way.\n\t\tif (e >= client->max_net_ents)\n\t\t\tcontinue;\n\t\tif (ent->v->modelindex >= client->maxmodels)\n\t\t\tcontinue;\n#ifdef DEPTHOPTIMISE\n\t\tif (distances)\n\t\t{\n\t\t\t//find distance based upon absolute mins/maxs so bsps are treated fairly.\n\t\t\t//org = clentorg + -0.5*(max+min)\n\t\t\tVectorAdd(ent->v->absmin, ent->v->absmax, org);\n\t\t\tVectorMA(clent->v->origin, -0.5, org, org);\n\t\t\tdist = DotProduct(org, org);\t//Length\n\n//\t\t\tif (dist > 1024*1024)\n//\t\t\t\tcontinue;\n\n\t\t\t// add to the packetentities\n\t\t\tif (pack->num_entities == pack->max_entities)\n\t\t\t{\n\t\t\t\tfloat furthestdist = -1;\n\t\t\t\tint best=-1;\n\t\t\t\tfor (i = 0; i < pack->max_entities; i++)\n\t\t\t\t\tif (furthestdist < distances[i])\n\t\t\t\t\t{\n\t\t\t\t\t\tfurthestdist = distances[i];\n\t\t\t\t\t\tbest = i;\n\t\t\t\t\t}\n\n\t\t\t\tif (furthestdist > dist && best != -1)\n\t\t\t\t{\n\t\t\t\t\tstate = &pack->entities[best];\n\t//\t\t\t\tCon_Printf(\"Dropping ent %s\\n\", sv.model_precache[state->modelindex]);\n\t\t\t\t\tmemmove(&distances[best], &distances[best+1], sizeof(*distances)*(pack->num_entities-best-1));\n\t\t\t\t\tmemmove(state, state+1, sizeof(*state)*(pack->num_entities-best-1));\n\n\t\t\t\t\tbest = pack->num_entities-1;\n\n\t\t\t\t\tdistances[best] = dist;\n\t\t\t\t\tstate = &pack->entities[best];\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tcontinue;\t// all full\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tstate = &pack->entities[pack->num_entities];\n\t\t\t\tdistances[pack->num_entities] = dist;\n\t\t\t\tpack->num_entities++;\n\t\t\t}\n\t\t}\n\t\telse\n#endif\n\t\t{\n\t\t\t// add to the packetentities\n\t\t\tif (pack->num_entities == pack->max_entities)\n\t\t\t\tcontinue;\t// all full\n\t\t\telse\n\t\t\t{\n\t\t\t\tstate = &pack->entities[pack->num_entities];\n\t\t\t\tpack->num_entities++;\n\t\t\t}\n\t\t}\n\n\t\t//its not a nail or anything, pack it up and ship it on\n\t\tSV_Snapshot_BuildStateQ1(state, ent, client, pack);\n\t}\n}\n\nvoid SV_AddCameraEntity(pvscamera_t *cameras, edict_t *ent, pvec3_t viewofs)\n{\n\tint i;\n\tvec3_t org;\n\tint area;\n\n\tfor (i = 0; i < cameras->numents; i++)\n\t{\n\t\tif (cameras->ent[i] == ent)\n\t\t\treturn;\t//don't add the same ent multiple times (.view2 or portals that can see themselves through other portals).\n\t}\n\n\tif (ent)\n\t{\n\t\tif (viewofs)\n\t\t\tVectorAdd (ent->v->origin, viewofs, org);\n\t\telse\n\t\t\tVectorCopy (ent->v->origin, org);\n\t}\n\telse\n\t\tVectorCopy (viewofs, org);\n\n\tsv.world.worldmodel->funcs.ClusterForPoint(sv.world.worldmodel, org, &area);\n\tfor (i = 1; ; i++)\n\t{\n\t\tif (i > cameras->area[0])\n\t\t{\t//reached the end of the known count. add it now.\n\t\t\tcameras->area[++cameras->area[0]] = area;\n\t\t\tbreak;\n\t\t}\n\t\tif (cameras->area[i] == area)\n\t\t\tbreak;\t//already have a camera in this area, don't make stuff slow with dupes.\n\t}\n\tsv.world.worldmodel->funcs.FatPVS(sv.world.worldmodel, org, &cameras->pvs, cameras->numents!=0);\n\tif (cameras->numents < SV_PVS_CAMERAS)\n\t{\n\t\tcameras->ent[cameras->numents] = ent;\n\t\tVectorCopy(org, cameras->org[cameras->numents]);\n\t\tcameras->numents++;\n\t}\n}\n\nvoid SV_Snapshot_SetupPVS(client_t *client, pvscamera_t *camera)\n{\n\tcamera->area[0] = 0;\n\tcamera->numents = 0;\n\tfor (; client; client = client->controlled)\n\t{\n\t\tif (client->viewent)\t//svc_viewentity hack\n\t\t\tSV_AddCameraEntity(camera, EDICT_NUM_UB(svprogfuncs, client->viewent), client->edict->v->view_ofs);\n\t\telse\n\t\t\tSV_AddCameraEntity(camera, client->edict, client->edict->v->view_ofs);\n\n\t\t//spectators should always see their targetted player\n\t\tif (client->spec_track)\n\t\t\tSV_AddCameraEntity(camera, EDICT_NUM_UB(svprogfuncs, client->spec_track), client->edict->v->view_ofs);\n\n\t\t//view2 support should always see the extra entity\n\t\tif (client->edict->xv->view2)\n\t\t\tSV_AddCameraEntity(camera, PROG_TO_EDICT(svprogfuncs, client->edict->xv->view2), NULL);\n\t}\n\n\t//hack for skyrooms, open up the pvs. FIXME: only do this if 'the' viewleaf can see sky.\n\tif (sv.skyroom_pos_known)\n\t\tSV_AddCameraEntity(camera, NULL, sv.skyroom_pos);\n}\n\nvoid SV_Snapshot_Clear(packet_entities_t *pack)\n{\n\tpack->num_entities = 0;\n\n\tcsqcnuments = 0;\n\tnumnails = 0;\n}\n\n#ifdef QWOVERQ3\n/*\n=============\nSVQ3Q1_BuildEntityPacket\n\nBuilds a temporary q1 style entity packet for a q3 client\n=============\n*/\nvoid SVQ3Q1_BuildEntityPacket(client_t *client, packet_entities_t *pack)\n{\n\tpvscamera_t cameras;\n\tSV_Snapshot_Clear(pack);\n\tSV_Snapshot_SetupPVS(client, &cameras);\n\tSV_Snapshot_BuildQ1(client, pack, &cameras, client->edict);\n}\n#endif\n\n/*\n=============\nSV_WriteEntitiesToClient\n\nEncodes the current state of the world as\na svc_packetentities messages and possibly\na svc_nails message and\nsvc_playerinfo messages\n=============\n*/\nvoid SV_WriteEntitiesToClient (client_t *client, sizebuf_t *msg, qboolean ignorepvs)\n{\n\tint i;\n\tpacket_entities_t\t*pack;\n\tedict_t\t*clent;\n\tclient_frame_t\t*frame;\n\tpvscamera_t camerasbuf;\n\tpvscamera_t *cameras = &camerasbuf;\n\tcameras->pvs.buffer = alloca(cameras->pvs.buffersize=sv.world.worldmodel->pvsbytes);\n\n\t// this is the frame we are creating\n\tframe = &client->frameunion.frames[client->netchan.incoming_sequence & UPDATE_MASK];\n\tfor (i = 0; i < sv.allocated_client_slots; i++)\n\t\tframe->laggedplayer[i].present = 0;\n\n\t// find the client's PVS\n\tif (ignorepvs)\n\t{\t//mvd...\n\t\tclent = NULL;\n\t\tcameras = NULL;\n\t}\n\telse\n\t{\n\t\tclent = client->edict;\n\t\tif (sv_nopvs.ival)\n\t\t\tcameras = NULL;\n#ifdef HLSERVER\n\t\telse if (svs.gametype == GT_HALFLIFE)\n\t\t\tSVHL_Snapshot_SetupPVS(client, cameras->pvs, sizeof(cameras->pvs));\n#endif\n\t\telse \n\t\t\tSV_Snapshot_SetupPVS(client, cameras);\n\t}\n\n\thost_client = client;\n\tif ((client->fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS) || !frame->qwentities.entities || ISNQCLIENT(client))\n\t{\n\t\tpack = &svs.entstatebuffer;\n\t\tif (pack->max_entities < client->max_net_ents)\n\t\t{\n\t\t\tpack->max_entities = client->max_net_ents;\n\t\t\tpack->entities = BZ_Realloc(pack->entities, sizeof(*pack->entities) * pack->max_entities);\n\t\t\tmemset(pack->entities, 0, sizeof(entity_state_t) * pack->max_entities);\n\t\t}\n\t}\n\telse\n\t\tpack = &frame->qwentities;\n\tSV_Snapshot_Clear(pack);\n\n\tif (!pack->entities)\n\t\treturn;\n\n\t// put other visible entities into either a packet_entities or a nails message\n#ifdef SERVER_DEMO_PLAYBACK\n\tif (sv.demostatevalid)\t//generate info from demo stats\n\t{\n\t\tSV_Snapshot_Build_Playback(client, pack);\n\t}\n\telse\n#endif\n\t{\n#ifdef HLSERVER\n\t\tif (svs.gametype == GT_HALFLIFE)\n\t\t\tSVHL_Snapshot_Build(client, pack, cameras->pvs, clent, ignorepvs);\n\t\telse\n#endif\n\t\t\tSV_Snapshot_BuildQ1(client, pack, cameras, clent);\n\t}\n\n#ifdef NQPROT\n\tif (ISNQCLIENT(client))\n\t{\n\t\tif (client->fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS)\n\t\t{\n\t\t\tqboolean overflow;\n\t\t\tfor (;;)\n\t\t\t{\n\t\t\t\toverflow = SVFTE_EmitPacketEntities(client, pack, msg);\n\t\t\t\tclient->netchan.incoming_sequence++;\n\n\t\t\t\tif (overflow && pack == &svs.entstatebuffer)\n\t\t\t\t{\n\t\t\t\t\tif (!Netchan_CanPacket(&client->netchan, SV_RateForClient(client)/2))\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tNetchan_Transmit (&client->netchan, msg->cursize, msg->data, SV_RateForClient(client));\n\t\t\t\t\tSZ_Clear(msg);\n\t\t\t\t\tif (!Netchan_CanPacket(&client->netchan, SV_RateForClient(client)/2))\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\telse if (client->protocol == SCP_DARKPLACES6 || client->protocol == SCP_DARKPLACES7)\n\t\t\tSVDP_EmitEntitiesUpdate(client, frame, pack, msg);\n\t\telse\n\t\t{\n\t\t\tint e;\n\t\t\tfor (e = 0; e < pack->num_entities; e++)\n\t\t\t{\n\t\t\t\tif (pack->entities[e].number > sv.allocated_client_slots)\n\t\t\t\t\tbreak;\n\t\t\t\tif (msg->cursize + 32 > msg->maxsize)\n\t\t\t\t\tbreak;\n\t\t\t\tSVNQ_EmitEntityState(msg, &pack->entities[e]);\n\t\t\t}\n\t\t\tfor (; e < pack->num_entities; e++)\n\t\t\t{\n\t\t\t\tif (msg->cursize + 32 + client->datagram.cursize > msg->maxsize)\n\t\t\t\t\tbreak;\n\t\t\t\tSVNQ_EmitEntityState(msg, &pack->entities[e]);\n\t\t\t}\n\t\t\tclient->netchan.incoming_sequence++;\n\t\t}\n\t\tSV_EmitCSQCUpdate(client, msg, svcdp_csqcentities);\n\t}\n\telse\n#endif\n\t{\n\t\t// encode the packet entities as a delta from the\n\t\t// last packetentities acknowledged by the client\n\n\t\tif (client->fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS)\n\t\t{\n\t\t\tSVFTE_EmitPacketEntities(client, pack, msg);\n\t\t}\n\t\telse\n\t\t{\n#ifdef QUAKESTATS\n\t\t\t// Z_EXT_TIME protocol extension\n\t\t\t// every now and then, send an update so that extrapolation\n\t\t\t// on client side doesn't stray too far off\n\t\t\tif (ISQWCLIENT(client))\n\t\t\t{\n\t\t\t\tif ((client->fteprotocolextensions & PEXT_ACCURATETIMINGS )&& sv.world.physicstime - client->nextservertimeupdate > 0)\n\t\t\t\t{\t//the fte pext causes the server to send out accurate timings, allowing for perfect interpolation.\n\t\t\t\t\tMSG_WriteByte (msg, svcqw_updatestatlong);\n\t\t\t\t\tMSG_WriteByte (msg, STAT_TIME);\n\t\t\t\t\tMSG_WriteLong (msg, (int)(sv.world.physicstime * 1000));\n\n\t\t\t\t\tclient->nextservertimeupdate = sv.world.physicstime;\n\t\t\t\t}\n\t\t\t\telse if ((client->zquake_extensions & Z_EXT_SERVERTIME) && sv.world.physicstime - client->nextservertimeupdate > 0)\n\t\t\t\t{\t//the zquake ext causes the server to send out peridoic timings, allowing for moderatly accurate game time.\n\t\t\t\t\tMSG_WriteByte (msg, svcqw_updatestatlong);\n\t\t\t\t\tMSG_WriteByte (msg, STAT_TIME);\n\t\t\t\t\tMSG_WriteLong (msg, (int)(sv.world.physicstime * 1000));\n\n\t\t\t\t\tclient->nextservertimeupdate = sv.world.physicstime+10;\n\t\t\t\t}\n\t\t\t}\n#endif\n\n\t\t\t// send over the players in the PVS\n\t\t\tif (svs.gametype != GT_HALFLIFE)\n\t\t\t{\n#ifdef MVD_RECORDING\n\t\t\t\tif (client == &demo.recorder)\n\t\t\t\t\tSV_WritePlayersToMVD(client, frame, msg);\n\t\t\t\telse\n#endif\n\t\t\t\t\tSV_WritePlayersToClient (client, frame, clent, cameras, msg);\n\t\t\t}\n\n\t\t\tSVQW_EmitPacketEntities (client, pack, msg);\n\t\t}\n\n\t\tSV_EmitCSQCUpdate(client, msg, svcfte_csqcentities);\n\n\t\t// now add the specialized nail update\n\t\tSV_EmitNailUpdate (msg, ignorepvs);\n\t}\n}\n\n//just goes and makes sure each client tracks all the right SendFlags.\nvoid SV_ProcessSendFlags(client_t *c)\n{\n\tedict_t *ent;\n\tunsigned int e, h = 0;\n\tif (!c->csqcactive || !c->pendingcsqcbits)\n\t\treturn;\n\tfor (e=1 ; e<sv.world.num_edicts && e < c->max_net_ents; e++)\n\t{\n\t\tent = EDICT_NUM_PB(svprogfuncs, e);\n\t\tif (ED_ISFREE(ent))\n\t\t\tcontinue;\n\t\tif (ent->xv->SendFlags)\n\t\t{\n\t\t\tc->pendingcsqcbits[e] |= (qint64_t)ent->xv->SendFlags << SENDFLAGS_SHIFT;\n\t\t\th = e;\n\t\t}\n\t}\n\tneedcleanup = max(needcleanup, h);\n}\nvoid SV_CleanupEnts(void)\n{\n\tint\t\te;\n\tedict_t\t*ent;\n\n\tif (!needcleanup)\n\t\treturn;\n\tif (needcleanup >= sv.world.num_edicts)\n\t{\n\t\tneedcleanup = 0;\n\t\treturn;\n\t}\n\n\tfor (e=1 ; e<=needcleanup ; e++)\n\t{\n\t\tent = EDICT_NUM_PB(svprogfuncs, e);\n\t\tent->xv->SendFlags = 0;\n\n#ifdef HAVE_LEGACY\n\t\t//this is legacy code. we'll just have to live with the slight delay.\n\t\t//FIXME: check if Version exists and do it earlier.\n\t\tif ((int)ent->xv->Version != sv.csqcentversion[ent->entnum])\n\t\t{\n\t\t\tent->xv->SendFlags = -1;\n\t\t\tsv.csqcentversion[ent->entnum] = (int)ent->xv->Version;\n\t\t}\n#endif\n\t}\n\tneedcleanup=0;\n}\n#endif\n\n"
  },
  {
    "path": "engine/server/sv_init.c",
    "content": "/*\nCopyright (C) 1996-1997 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n*/\n\n#include \"quakedef.h\"\n#include \"pr_common.h\"\n#ifdef SQL\n#include \"sv_sql.h\"\n#endif\n#ifdef __GLIBC__\n#include <malloc.h>\t//for malloc_trim\n#endif\n\n#ifndef CLIENTONLY\nextern int\t\t\ttotal_loading_size, current_loading_size, loading_stage;\nchar *T_GetString(int num);\n\nvoid SVQ2_Ents_Shutdown(void);\n#define Q2EDICT_NUM(i) (q2edict_t*)((char *)ge->edicts+(i)*ge->edict_size)\n\nserver_static_t\tsvs;\t\t\t\t// persistant server info\nserver_t\t\tsv;\t\t\t\t\t// local server\n\nentity_state_t *sv_staticentities;\nint sv_max_staticentities;\nstaticsound_state_t *sv_staticsounds;\nint sv_max_staticsounds;\n\nextern cvar_t\tskill;\nextern cvar_t\tsv_cheats;\nextern cvar_t\tsv_bigcoords;\nextern cvar_t\tsv_gamespeed;\nextern cvar_t\tsv_csqc_progname;\nextern cvar_t\tsv_calcphs;\nextern cvar_t\tsv_playerslots, maxclients, maxspectators;\nextern cvar_t\tsv_nqplayerphysics; //auto setting needs updating on map changes\n\n/*\n================\nSV_ModelIndex\n\n================\n*/\nint SV_ModelIndex (const char *name)\n{\n\tint\t\ti;\n\n\tif (!name || !name[0])\n\t\treturn 0;\n\n\tfor (i=1 ; i<MAX_PRECACHE_MODELS && sv.strings.model_precache[i] ; i++)\n\t\tif (!strcmp(sv.strings.model_precache[i], name))\n\t\t\treturn i;\n\tif (i==MAX_PRECACHE_MODELS || !sv.strings.model_precache[i])\n\t{\n\t\tif (i!=MAX_PRECACHE_MODELS)\n\t\t{\n#ifdef VM_Q1\n\t\t\tif (svs.gametype == GT_Q1QVM)\n\t\t\t\tsv.strings.model_precache[i] = name;\n\t\t\telse\n#endif\n\t\t\t\tsv.strings.model_precache[i] = PR_AddString(svprogfuncs, name, 0, false);\n\t\t\tif (!strcmp(name + strlen(name) - 4, \".bsp\"))\n\t\t\t\tsv.models[i] = Mod_FindName(Mod_FixName(sv.strings.model_precache[i], sv.strings.model_precache[1]));\n\n\t\t\tCon_DPrintf(\"WARNING: SV_ModelIndex: model %s not precached\\n\", name);\n\n\t\t\tif (sv.state != ss_loading)\n\t\t\t{\n\t\t\t\tMSG_WriteByte(&sv.reliable_datagram, svcfte_precache);\n\t\t\t\tMSG_WriteShort(&sv.reliable_datagram, i);\n\t\t\t\tMSG_WriteString(&sv.reliable_datagram, sv.strings.model_precache[i]);\n#ifdef NQPROT\n\t\t\t\tMSG_WriteByte(&sv.nqreliable_datagram, svcdp_precache);\n\t\t\t\tMSG_WriteShort(&sv.nqreliable_datagram, i);\n\t\t\t\tMSG_WriteString(&sv.nqreliable_datagram, sv.strings.model_precache[i]);\n#endif\n\t\t\t}\n\t\t}\n\t}\n\treturn i;\n}\n\n//looks up a name->index without caching it\nint SV_SafeModelIndex (char *name)\n{\n\tint\t\ti;\n\n\tif (!name || !name[0])\n\t\treturn 0;\n\n\tfor (i=1 ; i<MAX_PRECACHE_MODELS && sv.strings.model_precache[i] ; i++)\n\t\tif (!strcmp(sv.strings.model_precache[i], name))\n\t\t\treturn i;\n\tif (i==MAX_PRECACHE_MODELS || !sv.strings.model_precache[i])\n\t{\n\t\treturn 0;\n\t}\n\treturn i;\n}\n\n/*\n================\nSV_FlushSignon\n\nMoves to the next signon buffer if needed\nThis stops any chunk from getting too large, hopefully, but if the worst happens then hopefully network fragmentation will work.\n================\n*/\nvoid SV_FlushSignon (qboolean force)\n{\t//flush only when it gets too big.\n\tif (sv.signon.cursize < MAX_DATAGRAM - 512)\n\t{\n\t\tif (!force || !sv.signon.cursize)\n\t\t\treturn;\n\t}\n\n\tif (sv.signon.cursize)\n\t{\n\t\tsv.signon.data[-2] = (sv.signon.cursize>>0)&0xff;\n\t\tsv.signon.data[-1] = (sv.signon.cursize>>8)&0xff;\n\t\tsv.used_signon_space += 2+sv.signon.cursize;\n\t}\n\n\tsv.signon.data = sv.signon_buffer + sv.used_signon_space+2;\n\tsv.signon.maxsize = sizeof(sv.signon_buffer) - (sv.used_signon_space+2);\n\tsv.signon.cursize = 0;\n\tsv.signon.prim = svs.netprim;\n}\n#ifdef SERVER_DEMO_PLAYBACK\nvoid SV_FlushDemoSignon (void)\n{\n\tif (sv.demosignon.cursize < sv.demosignon.maxsize - 512)\n\t\treturn;\n\n\tif (sv.num_demosignon_buffers == MAX_SIGNON_BUFFERS-1)\n\t\tSV_Error (\"sv.num_demosignon_buffers == MAX_SIGNON_BUFFERS-1\");\n\n\tsv.demosignon_buffer_size[sv.num_demosignon_buffers-1] = sv.demosignon.cursize;\n\tsv.demosignon.data = sv.demosignon_buffers[sv.num_demosignon_buffers];\n\tsv.num_demosignon_buffers++;\n\tsv.demosignon.cursize = 0;\n}\n#endif\n/*\n================\nSV_CreateBaseline\n\nEntity baselines are used to compress the update messages\nto the clients -- only the fields that differ from the\nbaseline will be transmitted\n================\n*/\n/*void SV_CreateBaseline (void)\n{\n\tint\t\t\ti;\n\tedict_t\t\t\t*svent;\n\tint\t\t\t\tentnum;\n\n\tfor (entnum = 0; entnum < sv.num_edicts ; entnum++)\n\t{\n\t\tsvent = EDICT_NUM(entnum);\n\t\tif (svent->free)\n\t\t\tcontinue;\n\t\t// create baselines for all player slots,\n\t\t// and any other edict that has a visible model\n\t\tif (entnum > svs.allocated_client_slots && !svent->v->modelindex)\n\t\t\tcontinue;\n\n\t//\n\t// create entity baseline\n\t//\n\t\tVectorCopy (svent->v->origin, svent->baseline.origin);\n\t\tVectorCopy (svent->v->angles, svent->baseline.angles);\n\t\tsvent->baseline.frame = svent->v->frame;\n\t\tsvent->baseline.skinnum = svent->v->skin;\n\t\tif (entnum > 0 && entnum <= svs.allocated_client_slots)\n\t\t{\n\t\t\tsvent->baseline.colormap = entnum;\n\t\t\tsvent->baseline.modelindex = SV_ModelIndex(\"progs/player.mdl\")&255;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tsvent->baseline.colormap = 0;\n\t\t\tsvent->baseline.modelindex =\n\t\t\t\tSV_ModelIndex(PR_GetString(svent->v->model))&255;\n\t\t}\n#ifdef PEXT_SCALE\n\t\tsvent->baseline.scale = 1;\n#endif\n#ifdef PEXT_TRANS\n\t\tsvent->baseline.trans = 1;\n#endif\n\n\t\t//\n\t\t// flush the signon message out to a seperate buffer if\n\t\t// nearly full\n\t\t//\n\t\tSV_FlushSignon ();\n\n\t\t//\n\t\t// add to the message\n\t\t//\n\t\tMSG_WriteByte (&sv.signon,svc_spawnbaseline);\n\t\tMSG_WriteShort (&sv.signon,entnum);\n\n\t\tMSG_WriteByte (&sv.signon, svent->baseline.modelindex);\n\t\tMSG_WriteByte (&sv.signon, svent->baseline.frame);\n\t\tMSG_WriteByte (&sv.signon, svent->baseline.colormap);\n\t\tMSG_WriteByte (&sv.signon, svent->baseline.skinnum);\n\t\tfor (i=0 ; i<3 ; i++)\n\t\t{\n\t\t\tMSG_WriteCoord(&sv.signon, svent->baseline.origin[i]);\n\t\t\tMSG_WriteAngle(&sv.signon, svent->baseline.angles[i]);\n\t\t}\n\t}\n}\n*/\n\nvoid SVQ1_CreateBaseline (void)\n{\n\tedict_t\t\t\t*svent;\n\tint\t\t\t\tentnum;\n\textern entity_state_t nullentitystate;\n\n\tint playermodel = SV_SafeModelIndex(\"progs/player.mdl\");\n\n\tfor (entnum = 0; entnum < sv.world.num_edicts ; entnum++)\n\t{\n\t\tsvent = EDICT_NUM_PB(svprogfuncs, entnum);\n\n\t\tmemcpy(&svent->baseline, &nullentitystate, sizeof(entity_state_t));\n\t\tsvent->baseline.number = entnum;\n\n\t\tif (ED_ISFREE(svent))\n\t\t\tcontinue;\n\t\t// create baselines for all player slots,\n\t\t// and any other edict that has a visible model\n\t\tif (entnum > sv.allocated_client_slots && !svent->v->modelindex)\n\t\t\tcontinue;\n\n\t//\n\t// create entity baseline\n\t//\n\t\tSV_Snapshot_BuildStateQ1(&svent->baseline, svent, NULL, NULL);\n\n\t\tif (entnum > 0 && entnum <= sv.allocated_client_slots)\n\t\t{\n\t\t\tif (entnum > 0 && entnum <= 16)\n\t\t\t\tsvent->baseline.colormap = entnum;\n\t\t\telse\n\t\t\t\tsvent->baseline.colormap = 0;\t//this would crash NQ.\n\n\t\t\tif (!svent->baseline.solidsize)\n\t\t\t\tsvent->baseline.solidsize = ES_SOLID_HULL1;\n\t\t\tif (!svent->baseline.modelindex)\n\t\t\t\tsvent->baseline.modelindex = playermodel;\n\t\t}\n\t\tsvent->baseline.modelindex&=255;\t//FIXME\n\n\t\tif (!svent->baseline.modelindex)\n\t\t{\n\t\t\tmemcpy(&svent->baseline, &nullentitystate, sizeof(entity_state_t));\n\t\t\tsvent->baseline.number = entnum;\n\t\t}\n\t}\n}\n\nvoid SV_SpawnParmsToQC(client_t *client)\n{\n\tint i;\n\t// copy spawn parms out of the client_t\n\tfor (i=0 ; i< NUM_SPAWN_PARMS ; i++)\n\t{\n\t\tif (pr_global_ptrs->spawnparamglobals[i])\n\t\t\t*pr_global_ptrs->spawnparamglobals[i] = client->spawn_parms[i];\n\t}\n\tif (pr_global_ptrs->parm_string)\n\t\t*pr_global_ptrs->parm_string = client->spawn_parmstring?PR_TempString(sv.world.progs, client->spawn_parmstring):0;\n}\n\nvoid SV_SpawnParmsToClient(client_t *client)\n{\n\tint i;\n\tfor (i=0 ; i<NUM_SPAWN_PARMS ; i++)\n\t{\n\t\tif (pr_global_ptrs->spawnparamglobals[i])\n\t\t\tclient->spawn_parms[i] = *pr_global_ptrs->spawnparamglobals[i];\n\t\telse\n\t\t\tclient->spawn_parms[i] = 0;\n\t}\n\tZ_Free(client->spawn_parmstring);\n\tif (pr_global_ptrs->parm_string)\n\t\tclient->spawn_parmstring = Z_StrDup(PR_GetString(sv.world.progs, *pr_global_ptrs->parm_string));\n\telse\n\t\tclient->spawn_parmstring = NULL;\n}\n\nvoid SV_SaveSpawnparmsClient(client_t *client, float *transferparms)\n{\n\tint j;\n\teval_t *eval;\n\tSV_SpawnParmsToQC(client);\n\n#ifdef VM_Q1\n\tif (svs.gametype == GT_Q1QVM)\n\t{\n\t\tpr_global_struct->self = EDICT_TO_PROG(svprogfuncs, client->edict);\n\t\tQ1QVM_SetChangeParms();\n\t}\n\telse\n#endif\n\t\tif (pr_global_ptrs->SetChangeParms)\n\t{\n\t\tfunc_t setparms = 0;\n\t\tif (transferparms)\n\t\t{\n\t\t\tsetparms = PR_FindFunction(svprogfuncs, \"SetTransferParms\", PR_ANY);\n\t\t\tif (!setparms)\n\t\t\t\tsetparms = pr_global_struct->SetChangeParms;\n\t\t}\n\t\telse\n\t\t\tsetparms = pr_global_struct->SetChangeParms;\n\t\tpr_global_struct->self = EDICT_TO_PROG(svprogfuncs, client->edict);\n\t\tPR_ExecuteProgram (svprogfuncs, setparms);\n\t}\n\n\tif (transferparms)\n\t{\n\t\tfor (j=0 ; j<NUM_SPAWN_PARMS ; j++)\n\t\t{\n\t\t\tif (pr_global_ptrs->spawnparamglobals[j])\n\t\t\t\ttransferparms[j] = *pr_global_ptrs->spawnparamglobals[j];\n\t\t}\n\t\treturn;\n\t}\n\telse\n\t{\n\t\tSV_SpawnParmsToClient(client);\n\t}\n\n\t// call the progs to get default spawn parms for the new client\n\teval = PR_FindGlobal(svprogfuncs, \"ClientReEnter\", 0, NULL);\n\tif (eval && eval->function)\n\t{//oooh, evil.\n\t\tchar buffer[65536*4];\n\t\tsize_t bufsize = 0;\n\t\tchar *buf;\n//\t\tfor (j=0 ; j<NUM_SPAWN_PARMS ; j++)\n//\t\t\tclient->spawn_parms[j] = 0;\n\n\t\tbuf = svprogfuncs->saveent(svprogfuncs, buffer, &bufsize, sizeof(buffer), client->edict);\n\n\t\tif (client->spawninfo)\n\t\t\tZ_Free(client->spawninfo);\n\t\tclient->spawninfo = Z_Malloc(bufsize+1);\n\t\tmemcpy(client->spawninfo, buf, bufsize+1);\n\t\tclient->spawninfotime = sv.time;\n\t}\n\n#ifdef SVRANKING\n\tif (client->rankid)\n\t{\n\t\trankstats_t rs;\n\t\tif (Rank_GetPlayerStats(client->rankid, &rs))\n\t\t{\n\t\t\trs.timeonserver += realtime - client->stats_started;\n\t\t\tclient->stats_started = realtime;\n\t\t\trs.kills += client->kills;\n\t\t\trs.deaths += client->deaths;\n\t\t\tclient->kills=0;\n\t\t\tclient->deaths=0;\n\t\t\tfor (j=0 ; j<NUM_RANK_SPAWN_PARMS ; j++)\n\t\t\t{\n\t\t\t\trs.parm[j] = client->spawn_parms[j];\n\t\t\t}\n\t\t\tRank_SetPlayerStats(client->rankid, &rs);\n\t\t}\n\t}\n#endif\n}\n\n/*\n================\nSV_SaveSpawnparms\n\nGrabs the current state of the progs serverinfo flags\nand each client for saving across the\ntransition to another level\n================\n*/\nvoid SV_SaveSpawnparms (void)\n{\n\tint\t\ti;\n\n\tif (!sv.state)\n\t\treturn;\t\t// no progs loaded yet\n\n\tif (!svprogfuncs)\n\t\treturn;\n\n\t// serverflags is the only game related thing maintained\n\tsvs.serverflags = pr_global_struct->serverflags;\n\n\tfor (i=0, host_client = svs.clients ; i<sv.allocated_client_slots ; i++, host_client++)\n\t{\n\t\tif (host_client->state != cs_spawned)\n\t\t\tcontinue;\n\n\t\tSV_SaveSpawnparmsClient(host_client, NULL);\n\t}\n}\n\nvoid SV_GetNewSpawnParms(client_t *cl)\n{\n\tif (svprogfuncs)\t//q2 dlls don't use parms in this manner. It's all internal to the dll.\n\t{\n\t\t// call the progs to get default spawn parms for the new client\n#ifdef VM_Q1\n\t\tif (svs.gametype == GT_Q1QVM)\n\t\t\tQ1QVM_SetNewParms();\n\t\telse\n#endif\n\t\t{\n\t\t\tif (pr_global_ptrs->SetNewParms)\n\t\t\t\tPR_ExecuteProgram (svprogfuncs, pr_global_struct->SetNewParms);\n\t\t}\n\n\t\tSV_SpawnParmsToClient(cl);\n\t}\n}\n\n/*\n================\nSV_CalcPHS\n\nExpands the PVS and calculates the PHS\n(Potentially Hearable Set)\n================\n*/\nvoid SV_CalcPHS (void)\n{\n\tint\t\trowbytes, rowwords;\n\tint\t\ti, j, k, l, index, num;\n\tint\t\tbitbyte;\n\tunsigned\t*dest, *src;\n\tqbyte\t*scan, *pvs;\n\tint\t\tcount, vcount;\n\tmodel_t *model = sv.world.worldmodel;\n\tpvsbuffer_t buf;\n\n\tif (model->pvs || model->fromgame == fg_quake2 || model->fromgame == fg_quake3)\n\t{\n\t\t//PHS calcs are pointless with Q2 bsps\n\t\treturn;\n\t}\n\n\t//FIXME: this can take a significant time on some maps, and should ideally be pushed to a worker thread.\n\tnum = model->numclusters;\n\trowbytes = model->pvsbytes;\n\trowwords = rowbytes/sizeof(*dest);\n\tbuf.buffersize = model->pvsbytes;\n\n\tif (!sv_calcphs.ival || (sv_calcphs.ival == 2 && (rowbytes*num >= 0x100000 || (!deathmatch.ival && !coop.ival))))\n\t{\n\t\tpvs = NULL;/*ZG_Malloc(&model->memgroup, rowbytes*num);\n\t\tscan = pvs;\n\t\tfor (i=0 ; i<num ; i++, scan+=rowbytes)\n\t\t{\n\t\t\tbuf.buffer = scan;\n\t\t\tmodel->funcs.ClusterPVS(model, i, &buf, PVM_REPLACE);\n\t\t}*/\n\n\t\tCon_DPrintf(\"Skipping PHS\\n\");\n\t\tmodel->pvs = pvs;\n\t\tmodel->phs = NULL;\n\t\treturn;\n\t}\n\n\tpvs = ZG_Malloc(&model->memgroup, rowbytes*num);\n\tscan = pvs;\n\tvcount = 0;\n\tfor (i=0 ; i<num ; i++, scan+=rowbytes)\n\t{\n\t\tbuf.buffer = scan;\n\t\tmodel->funcs.ClusterPVS(model, i, &buf, PVM_REPLACE);\n\t\tif (i == 0)\n\t\t\tcontinue;\n\t\tfor (j=0 ; j<num ; j++)\n\t\t{\n\t\t\tif ( scan[j>>3] & (1<<(j&7)) )\n\t\t\t{\n\t\t\t\tvcount++;\n\t\t\t}\n\t\t}\n\t}\n\tif (developer.value)\n\t\tCon_TPrintf (\"Building PHS...\\n\");\n\n\tmodel->pvs = pvs;\n\tmodel->phs = ZG_Malloc (&model->memgroup, rowbytes*num);\n\n\t/*this routine takes an exponential amount of time, so cache it if its too big*/\n\tif (rowbytes*num >= 0x100000)\n\t{\n\t\tchar hdr[8];\n\t\tvfsfile_t *f = FS_OpenVFS(va(\"maps/%s.phs\", svs.name), \"rb\", FS_GAME);\n\t\tif (f)\n\t\t{\n\t\t\tVFS_READ(f, hdr, sizeof(hdr));\n\t\t\tif (!memcmp(hdr, \"QPHS\\1\\0\\0\\0\", 8) && VFS_GETLEN(f) == rowbytes*num + 8)\n\t\t\t{\n\t\t\t\tVFS_READ(f, model->phs, rowbytes*num);\n\t\t\t\tVFS_CLOSE(f);\n\t\t\t\tCon_DPrintf(\"Loaded cached PHS\\n\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\telse\n\t\t\t\tCon_DPrintf(\"Stale cached PHS\\n\");\n\t\t\tVFS_CLOSE(f);\n\t\t}\n\t}\n\n\tcount = 0;\n\tscan = pvs;\n\tdest = (unsigned *)model->phs;\n\tfor (i=0 ; i<num ; i++, dest += rowwords, scan += rowbytes)\n\t{\n\t\tmemcpy (dest, scan, rowbytes);\n\t\tfor (j=0 ; j<rowbytes ; j++)\n\t\t{\n\t\t\tbitbyte = scan[j];\n\t\t\tif (!bitbyte)\n\t\t\t\tcontinue;\n\t\t\tfor (k=0 ; k<8 ; k++)\n\t\t\t{\n\t\t\t\tif (! (bitbyte & (1<<k)) )\n\t\t\t\t\tcontinue;\n\t\t\t\t// or this pvs row into the phs\n\t\t\t\t// +1 because pvs is 1 based\n\t\t\t\t//except we now use clusters internally, which are 0-based (ie: leaf 0 is invalid and maps to cluster -1)\n\t\t\t\tindex = ((j<<3)+k);\n\t\t\t\tif (index >= num)\n\t\t\t\t\tcontinue;\n\t\t\t\tsrc = (unsigned *)pvs + index*rowwords;\n\t\t\t\tfor (l=0 ; l<rowwords ; l++)\n\t\t\t\t\tdest[l] |= src[l];\n\t\t\t}\n\t\t}\n\n\t\tif (i == 0)\n\t\t\tcontinue;\n\t\tfor (j=0 ; j<num ; j++)\n\t\t\tif ( ((qbyte *)dest)[j>>3] & (1<<(j&7)) )\n\t\t\t\tcount++;\n\t}\n\n\tif (rowbytes*num >= 0x100000)\n\t{\n\t\tvfsfile_t *f = FS_OpenVFS(va(\"maps/%s.phs\", svs.name), \"wb\", FS_GAMEONLY);\n\t\tif (f)\n\t\t{\n\t\t\tVFS_WRITE(f, \"QPHS\\1\\0\\0\\0\", 8);\n\t\t\tVFS_WRITE(f, model->phs, rowbytes*num);\n\t\t\tVFS_CLOSE(f);\n\t\t\tCon_Printf(\"Written PHS cache (%u bytes)\\n\", rowbytes*num);\n\t\t}\n\t}\n\n\tif (num)\n\t\tif (developer.value)\n\t\t\tCon_TPrintf (\"Average leafs visible / hearable / total: %i / %i / %i\\n\", vcount/num, count/num, num);\n}\n\nunsigned SV_CheckModel(char *mdl)\n{\n\tsize_t fsize;\n\tqbyte *buf;\n\tunsigned short crc;\n\n\tbuf = (qbyte *)FS_LoadMallocFile (mdl, &fsize);\n\tif (!buf)\n\t\treturn 0;\n\tcrc = CalcHashInt(&hash_crc16, buf, fsize);\n\tBZ_Free(buf);\n\treturn crc;\n}\n\nvoid SV_UnspawnServer (void)\t//terminate the running server.\n{\n\tint i;\n\tif (sv.state)\n\t{\n\t\tCon_TPrintf(\"Server ended\\n\");\n\t\tSV_FinalMessage(\"Server unspawned\\n\");\n\n\t\tPR_PreShutdown();\n\n#ifdef SUBSERVERS\n\t\tif (sv.state == ss_clustermode && svs.allocated_client_slots == 1)\n\t\t\tMSV_Shutdown();\n#endif\n\n#ifdef MVD_RECORDING\n\t\tif (sv.mvdrecording)\n\t\t\tSV_MVDStop (MVD_CLOSE_STOPPED, false);\n#endif\n\n\t\tfor (i = 0; i < sv.allocated_client_slots; i++)\n\t\t{\n\t\t\tif (svs.clients[i].state)\n\t\t\t\tSV_DropClient(&svs.clients[i]);\n\t\t}\n\t\tPR_Deinit();\n#ifdef Q3SERVER\n\t\tif (q3)\n\t\t\tq3->sv.ShutdownGame(false);\n#endif\n#ifdef Q2SERVER\n\t\tSVQ2_ShutdownGameProgs();\n\t\tSVQ2_Ents_Shutdown();\n#endif\n#ifdef HLSERVER\n\t\tSVHL_ShutdownGame();\n#endif\n#ifdef VM_Q1\n\t\tQ1QVM_Shutdown(true);\n#endif\n\t\tsv.world.worldmodel = NULL;\n\t\tsv.state = ss_dead;\n\t\tif (sv.csqcentversion)\n\t\t{\n\t\t\tBZ_Free(sv.csqcentversion);\n\t\t\tsv.csqcentversion = NULL;\n\t\t}\n\t}\n\tfor (i = 0; i < svs.allocated_client_slots; i++)\n\t{\n\t\tif (svs.clients[i].frameunion.frames)\n\t\t\tZ_Free(svs.clients[i].frameunion.frames);\n\t\tsvs.clients[i].frameunion.frames = NULL;\n\t\tsvs.clients[i].pendingdeltabits = NULL;\n\t\tsvs.clients[i].pendingcsqcbits = NULL;\n\t\tsvs.clients[i].state = 0;\n\t\t*svs.clients[i].namebuf = '\\0';\n\t\tsvs.clients[i].name = NULL;\n\t\tInfoBuf_Clear(&svs.clients[i].userinfo, true);\n\t}\n\tfree(svs.clients);\n\tsvs.clients = NULL;\n\tsvs.allocated_client_slots = 0;\n#ifdef SAVEDGAMES\n\tSV_FlushLevelCache();\n#endif\n\tNET_CloseServer ();\n\tSV_RunCmdCleanup();\n}\n\nvoid SV_UpdateMaxPlayers(int newmax)\n{\n\tint i;\n\tif (newmax != svs.allocated_client_slots)\n\t{\n\t\tclient_t *old = svs.clients;\n\t\tfor (i = newmax; i < svs.allocated_client_slots; i++)\n\t\t{\n\t\t\tif (svs.clients[i].state)\n\t\t\t\tSV_DropClient(&svs.clients[i]);\n\t\t\tsvs.clients[i].namebuf[0] = '\\0';\t\t\t\t\t\t//kill all bots\n\t\t}\n\t\tif (newmax)\n\t\t\tsvs.clients = realloc(svs.clients, newmax*sizeof(*svs.clients));\n\t\telse\n\t\t{\n\t\t\tfree(svs.clients);\n\t\t\tsvs.clients = NULL;\n\t\t}\n\t\tfor (i = svs.allocated_client_slots; i < newmax; i++)\n\t\t{\n\t\t\tmemset(&svs.clients[i], 0, sizeof(svs.clients[i]));\n\t\t\tsvs.clients[i].name = svs.clients[i].namebuf;\n\t\t\tsvs.clients[i].team = svs.clients[i].teambuf;\n\t\t}\n\t\tfor (i = 0; i < min(newmax, svs.allocated_client_slots); i++)\n\t\t{\n\t\t\tif (svs.clients[i].name == old[i].namebuf)\n\t\t\t\tsvs.clients[i].name = svs.clients[i].namebuf;\n\t\t\tif (svs.clients[i].team == old[i].teambuf)\n\t\t\t\tsvs.clients[i].team = svs.clients[i].teambuf;\n\t\t\tif (svs.clients[i].netchan.message.data)\n\t\t\t\tsvs.clients[i].netchan.message.data = (qbyte*)&svs.clients[i] + (svs.clients[i].netchan.message.data - (qbyte*)&old[i]);\n\t\t\tif (svs.clients[i].datagram.data)\n\t\t\t\tsvs.clients[i].datagram.data = (qbyte*)&svs.clients[i] + (svs.clients[i].datagram.data - (qbyte*)&old[i]);\n\t\t\tif (svs.clients[i].backbuf.data)\n\t\t\t\tsvs.clients[i].backbuf.data = (qbyte*)&svs.clients[i] + (svs.clients[i].backbuf.data - (qbyte*)&old[i]);\n\t\t\tif (svs.clients[i].controlled)\n\t\t\t\tsvs.clients[i].controlled = svs.clients + (svs.clients[i].controlled - old);\n\t\t\tif (svs.clients[i].controller)\n\t\t\t\tsvs.clients[i].controller = svs.clients + (svs.clients[i].controller - old);\n\t\t}\n\t\tsvs.allocated_client_slots = sv.allocated_client_slots = newmax;\n\n\t\tfor (i = 0; i < svs.allocated_client_slots; i++)\n\t\t{\n\t\t\tInfoSync_Clear(&svs.clients[i].infosync);\n\t\t\tsvs.clients[i].userinfo.ChangeCB = svs.info.ChangeCB;\n\t\t\tsvs.clients[i].userinfo.ChangeCTX = &svs.clients[i].userinfo;\n\t\t}\n\t}\n\tsv.allocated_client_slots = svs.allocated_client_slots;\n}\n\nvoid SV_SetupNetworkBuffers(qboolean bigcoords)\n{\n\tint i;\n\n\t//determine basic primitive sizes.\n\tsvs.netprim.flags = 0;\n\tif (bigcoords)\n\t{\n\t\tif (svs.netprim.coordtype && svs.netprim.coordtype != COORDTYPE_FLOAT_32)\n\t\t\tCon_Printf(\"Switching to big coords\\n\");\n\t\tsvs.netprim.coordtype = COORDTYPE_FLOAT_32;\n\t\tsvs.netprim.anglesize = 2;\n\t}\n\telse\n\t{\n\t\tif (svs.netprim.coordtype && svs.netprim.coordtype != COORDTYPE_FIXED_13_3)\n\t\t\tCon_Printf(\"Switching to classic coords\\n\");\n\t\tsvs.netprim.coordtype = COORDTYPE_FIXED_13_3;\n\t\tsvs.netprim.anglesize = 1;\n\t}\n\n\t//FIXME: this should be part of sv_new_f or something instead, so that any angles sent by clients won't be invalid\n\tfor (i = 0; i < svs.allocated_client_slots; i++)\n\t{\n\t\tsvs.clients[i].netchan.netprim = svs.netprim;\n\t\tif (svs.clients[i].protocol == SCP_QUAKE2EX)\n\t\t\tsvs.clients[i].netchan.netprim.coordtype = COORDTYPE_FLOAT_32; //forced to floats. we have multiple multicast buffers. woo.\n\n\t\t//make sure those are kept up to date too.\n\t\tsvs.clients[i].datagram.prim =\n\t\tsvs.clients[i].netchan.message.prim =\n\t\tsvs.clients[i].backbuf.prim = svs.clients[i].netchan.netprim;\n\t}\n\n\t//\n\tsv.datagram.maxsize = sizeof(sv.datagram_buf);\n\tsv.datagram.data = sv.datagram_buf;\n\tsv.datagram.allowoverflow = true;\n\tsv.datagram.prim = svs.netprim;\n\n\tsv.reliable_datagram.maxsize = sizeof(sv.reliable_datagram_buf);\n\tsv.reliable_datagram.data = sv.reliable_datagram_buf;\n\tsv.reliable_datagram.prim = svs.netprim;\n\n\tsv.multicast.maxsize = sizeof(sv.multicast_buf);\n\tsv.multicast.data = sv.multicast_buf;\n\tsv.multicast.prim = svs.netprim;\n\n#ifdef NQPROT\n\tsv.nqdatagram.maxsize = sizeof(sv.nqdatagram_buf);\n\tsv.nqdatagram.data = sv.nqdatagram_buf;\n\tsv.nqdatagram.allowoverflow = true;\n\tsv.nqdatagram.prim = svs.netprim;\n\n\tsv.nqreliable_datagram.maxsize = sizeof(sv.nqreliable_datagram_buf);\n\tsv.nqreliable_datagram.data = sv.nqreliable_datagram_buf;\n\tsv.nqreliable_datagram.prim = svs.netprim;\n\n\tsv.nqmulticast.maxsize = sizeof(sv.nqmulticast_buf);\n\tsv.nqmulticast.data = sv.nqmulticast_buf;\n\tsv.nqmulticast.prim = svs.netprim;\n#endif\n\n#ifdef Q2SERVER\n\tsv.q2multicast[0].maxsize = sizeof(sv.q2multicast_lcbuf);\n\tsv.q2multicast[0].data = sv.q2multicast_lcbuf;\n\tsv.q2multicast[0].prim = svs.netprim;\n\tsv.q2multicast[1].maxsize = sizeof(sv.q2multicast_bcbuf);\n\tsv.q2multicast[1].data = sv.q2multicast_bcbuf;\n\tsv.q2multicast[1].prim = svs.netprim;\n\tsv.q2multicast[1].prim.coordtype = COORDTYPE_FLOAT_32;\n#endif\n\n\tsv.master.maxsize = sizeof(sv.master_buf);\n\tsv.master.data = sv.master_buf;\n\tsv.master.prim = msg_nullnetprim;\n\n\tsv.signon.data = sv.signon_buffer+2;\n\tsv.used_signon_space = 0;\n\tsv.signon.prim = svs.netprim;\n\tsv.signon.maxsize = sizeof(sv.signon_buffer)-sv.used_signon_space;\n}\n\nvoid SV_WipeServerState(void)\n{\n\tif (sv.stringsalloced)\n\t{\n\t\tunsigned int i;\n\t\tchar **ptrs = (char**)&sv.strings;\n\t\tfor (i = 0; i < sizeof(sv.strings) / sizeof(sv.strings.ptrs[0]); i++)\n\t\t\tZ_Free(ptrs[i]);\n\t}\n#ifdef SQL\n\tSQL_KillServers(&sv);\n#endif\n\tmemset (&sv, 0, sizeof(sv));\n\tsv.logindatabase = -1;\n}\n\n/*\n================\nSV_SpawnServer\n\nChange the server to a new map, taking all connected\nclients along with it.\n\nThis is only called from the SV_Map_f() function.\n================\n*/\nvoid SV_SpawnServer (const char *server, const char *startspot, qboolean noents, qboolean usecinematic, int playerslots)\n{\n\textern cvar_t allow_download_refpackages;\n\tfunc_t f;\n\tconst char *file, *csprogsname;\n\n\tgametype_e newgametype;\n\n\tedict_t\t\t*ent;\n#ifdef Q2SERVER\n\tq2edict_t\t\t*q2ent;\n#endif\n\tint\t\t\ti, j;\n\textern int sv_allow_cheats;\n\tsize_t fsz;\n\n#ifndef SERVERONLY\n\tif (!isDedicated && qrenderer == QR_NONE)\n\t{\n\t\tR_RestartRenderer_f();\n\n\t\tif (qrenderer == QR_NONE)\n\t\t{\n\t\t\tSys_Error(\"No renderer set when map restarted\\n\");\n\t\t\treturn;\n\t\t}\n\t}\n#endif\n\n\tCon_DPrintf (\"SpawnServer: %s\\n\",server);\n\n#ifndef SERVERONLY\n\ttotal_loading_size = 100;\n\tcurrent_loading_size = 0;\n\tSCR_SetLoadingStage(LS_SERVER);\n//\tSCR_BeginLoadingPlaque();\n\tSCR_ImageName(server);\n#endif\n\n\tPR_PreShutdown();\n\n\tsvs.spawncount++;\t\t// any partially connected client will be restarted\n\tsv.world.spawncount = svs.spawncount;\n\n\tsv.state = ss_dead;\n\n\tif (sv.gamedirchanged)\n\t{\n\t\tsv.gamedirchanged = false;\n#ifndef SERVERONLY\n\t\tWads_Flush();\t//server code is responsable for flushing old state\n#endif\n#ifdef SVRANKING\n\t\tRank_Flush();\n#endif\n\n\t\tfor (i = 0; i < svs.allocated_client_slots; i++)\n\t\t{\n#ifdef SVRANKING\n\t\t\tif (svs.clients[i].state && ISQWCLIENT(&svs.clients[i]))\n\t\t\t\tReloadRanking(&svs.clients[i], svs.clients[i].name);\n#endif\n\n\t\t\tif (svs.clients[i].spawninfo)\t//don't remember this stuff.\n\t\t\t\tZ_Free(svs.clients[i].spawninfo);\n\t\t\tsvs.clients[i].spawninfo = NULL;\n\t\t}\n#ifdef HEXEN2\n\t\tT_FreeStrings();\n#endif\n\t}\n\n\tfor (i = 0; i < svs.allocated_client_slots; i++)\n\t{\n\t\tsvs.clients[i].nextservertimeupdate = 0;\n\t\tif (!svs.clients[i].state)\t//bots with the net_preparse module.\n\t\t\tInfoBuf_Clear(&svs.clients[i].userinfo, true);\t//clear the userinfo to clear the name\n\n\t\tif (svs.clients[i].netchan.remote_address.type == NA_LOOPBACK)\n\t\t{\t//forget this client's message buffers, so that any shared client/server network state persists (eg: float coords)\n\t\t\tsvs.clients[i].num_backbuf = 0;\n\t\t\tsvs.clients[i].datagram.cursize = 0;\n\t\t}\n\t\tsvs.clients[i].csqcactive = false;\n\t}\n\n\tVoteFlushAll();\n#ifndef SERVERONLY\n\tcl.worldmodel = NULL;\n\tr_worldentity.model = NULL;\n//\tif (0)\n//\tcls.state = ca_connected;\n\tSurf_PreNewMap();\n#endif\n\n#ifdef Q3SERVER\n\tif (svs.gametype == GT_QUAKE3)\n\t\tq3->sv.ShutdownGame(false);\t//botlib kinda mandates this. :(\n#endif\n\n\tMod_ClearAll ();\n#ifndef SERVERONLY\n\tr_regsequence++;\n#endif\n\n\tPR_Deinit();\n\n\tif (sv.csqcentversion)\n\t\tBZ_Free(sv.csqcentversion);\n\n\t// wipe the entire per-level structure\n\tSV_WipeServerState();\n\n\tSV_SetupNetworkBuffers(sv_bigcoords.ival);\n\n\tif (allow_download_refpackages.ival)\n\t\tFS_ReferenceControl(1, 1);\n\n\tQ_strncpyz (svs.name, server, sizeof(svs.name));\n#ifndef SERVERONLY\n\tcurrent_loading_size+=10;\n\t//SCR_BeginLoadingPlaque();\n\tSCR_ImageName(server);\n\tSCR_SetLoadingFile(\"map\");\n#else\n\t#define SCR_SetLoadingFile(s)\n#endif\n\n\tCvar_ApplyLatches(CVAR_MAPLATCH, false);\n\n//work out the gamespeed\n//reset the server time.\n\tsv.time = 0.01;\t//some progs don't like time starting at 0.\n\t\t\t\t\t//cos of spawn funcs like self.nextthink = time...\n\t\t\t\t\t//NQ uses 1, QW uses 0. Awkward.\n\tsv.starttime = Sys_DoubleTime();\n\n\tCOM_FlushTempoaryPacks();\n\n\tif (sv_cheats.ival)\n\t{\n\t\tsv_allow_cheats = true;\n\t\tInfoBuf_SetStarKey(&svs.info, \"*cheats\", \"ON\");\n\t}\n\telse\n\t{\n\t\tsv_allow_cheats = 2;\n\t\tInfoBuf_SetStarKey(&svs.info, \"*cheats\", \"\");\n\t}\n#ifndef SERVERONLY\n\t//This fixes a bug where the server advertises cheats, the internal client connects, and doesn't think cheats are allowed.\n\t//this applies to anything that can affect the content that is loaded by the server, but cheats is the only special one (because of the *)\n\tInfoBuf_Clone(&cl.serverinfo, &svs.info);\n\tif (!isDedicated)\n\t\tCL_CheckServerInfo();\n#endif\n\n\tsv.state = ss_loading;\n#if defined(Q2BSPS)\n\tif (usecinematic)\n\t{\n\t\tqboolean QDECL Mod_LoadQ2BrushModel (model_t *mod, void *buffer, size_t fsize);\n\n\t\tQ_strncpyz (svs.name, server, sizeof(svs.name));\n\t\tQ_strncpyz (sv.modelname, \"\", sizeof(sv.modelname));\n\n\t\tsv.world.worldmodel = Mod_FindName (sv.modelname);\n\t\tif (Mod_LoadQ2BrushModel (sv.world.worldmodel, NULL, 0))\n\t\t\tsv.world.worldmodel->loadstate = MLS_LOADED;\n\t\telse\n\t\t\tsv.world.worldmodel->loadstate = MLS_FAILED;\n\t}\n\telse\n#endif\n\t{\n\t\t//.map is commented out because quite frankly, they're a bit annoying when the engine loads the gpled start.map when really you wanted to just play the damn game intead of take it apart.\n\t\t//if you want to load a .map, just use 'map foo.map' instead.\n\t\tchar *exts[] = {\"%s\", \"maps/%s\", \"maps/%s.bsp\", \"maps/%s.d3dbsp\", \"maps/%s.cm\", \"maps/%s.hmp\", \"maps/%s.bsp.gz\", \"maps/%s.bsp.xz\", /*\"maps/%s.map\",*/ NULL}, *e;\n\t\tint depth, bestdepth = FDEPTH_MISSING;\n\t\tflocation_t loc;\n\t\ttime_t filetime;\n\t\tchar *mod = NULL;\n\t\tQ_snprintfz (sv.modelname, sizeof(sv.modelname), exts[1], server);\t// `map foo.map` can bypass earlier checks, so don't get too screwed up by that.\n\t\tif (bestdepth == FDEPTH_MISSING)\n\t\t{\t//not an exact name, scan the maps subdir.\n\t\t\tfor (i = 0; exts[i]; i++)\n\t\t\t{\n\t\t\t\tdepth = COM_FDepthFile(va(exts[i], server), false);\n\t\t\t\tif (depth < bestdepth)\n\t\t\t\t{\n\t\t\t\t\tbestdepth = depth;\n\t\t\t\t\tQ_snprintfz (sv.modelname, sizeof(sv.modelname), exts[i], server);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (bestdepth == FDEPTH_MISSING)\n\t\t{\n\t\t\tmod = strchr(server, '#');\n\t\t\tif (mod)\n\t\t\t{\n\t\t\t\t*mod = 0;\n\t\t\t\tbestdepth = COM_FDepthFile(server, false);\n\t\t\t\tif (bestdepth != FDEPTH_MISSING)\n\t\t\t\t\tQ_snprintfz (sv.modelname, sizeof(sv.modelname), \"%s\", server);\n\t\t\t\telse\n\t\t\t\t{\t//not an exact name, scan the maps subdir.\n\t\t\t\t\tfor (i = 0; exts[i]; i++)\n\t\t\t\t\t{\n\t\t\t\t\t\tdepth = COM_FDepthFile(va(exts[i], server), false);\n\t\t\t\t\t\tif (depth < bestdepth)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tbestdepth = depth;\n\t\t\t\t\t\t\tQ_snprintfz (sv.modelname, sizeof(sv.modelname), exts[i], server);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t*mod = '#';\n\t\t\t\tif (bestdepth == FDEPTH_MISSING)\n\t\t\t\t\tmod = NULL;\n\t\t\t}\n\t\t}\n\t\tif (!strncmp(sv.modelname, \"maps/\", 5))\n\t\t\tQ_strncpyz (svs.name, sv.modelname+5, sizeof(svs.name));\n\t\telse\n\t\t\tQ_strncpyz (svs.name, sv.modelname, sizeof(svs.name));\n\t\te = (char*)COM_GetFileExtension(svs.name, NULL);\n\t\tif (!strcmp(e, \".gz\") || !strcmp(e, \".xz\"))\n\t\t{\n\t\t\t*e = 0;\n\t\t\te = (char*)COM_GetFileExtension(svs.name, NULL);\n\t\t}\n\t\tif (!strcmp(e, \".bsp\"))\n\t\t\t*e = 0;\n\n\t\tMod_SetModifier(mod);\n\n\t\tsv.world.worldmodel = Mod_ForName (sv.modelname, MLV_ERROR);\n\n\t\tif (FS_FLocateFile(sv.modelname,FSLF_IFFOUND, &loc) && FS_GetLocMTime(&loc, &filetime))\n\t\t{\n\t\t\tif (filetime > sv.world.worldmodel->mtime && sv.world.worldmodel->mtime)\n\t\t\t{\n\t\t\t\tCOM_WorkerFullSync();\t//sync all the workers, just in case.\n\t\t\t\tMod_PurgeModel(sv.world.worldmodel, MP_RESET);\t//nuke it now\n\t\t\t\tsv.world.worldmodel = Mod_ForName (sv.modelname, MLV_ERROR);\t//and we can reload it now\n\t\t\t}\n\t\t\tsv.world.worldmodel->mtime = filetime;\n\t\t}\n\n\t\tif (!sv.world.worldmodel || sv.world.worldmodel->loadstate != MLS_LOADED)\n\t\t\tSys_Error(\"\\\"%s\\\" is missing or corrupt\\n\", sv.modelname);\n//\tif (sv.world.worldmodel->type != mod_brush && sv.world.worldmodel->type != mod_heightmap)\n\t\tif (!sv.world.worldmodel->funcs.NativeTrace && !sv.world.worldmodel->funcs.PointContents)\n\t\t\tSys_Error(\"\\\"%s\\\" is not a bsp model\\n\", sv.modelname);\n\t\telse if (!Mod_GetEntitiesString(sv.world.worldmodel))\n\t\t\tSys_Error(\"\\\"%s\\\" has no entity data\\n\", sv.modelname);\n\t}\n\n\tsv.state = ss_dead;\n\n\t//make sure our map's package is loaded.\n\tif (sv.world.worldmodel)\n\t\tFS_LoadMapPackFile(sv.world.worldmodel->name, sv.world.worldmodel->archive);\n\n\t//reset the map's areaportal state... it might be dirty from a restart or so.\n\tif (sv.world.worldmodel->funcs.LoadAreaPortalBlob)\n\t\tsv.world.worldmodel->funcs.LoadAreaPortalBlob(sv.world.worldmodel, NULL, 0);\n\n#ifndef SERVERONLY\n\tcurrent_loading_size+=10;\n//\tSCR_BeginLoadingPlaque();\n\tSCR_ImageName(server);\n\tSCR_SetLoadingFile(\"phs\");\n#endif\n\tSV_CalcPHS ();\n#ifndef SERVERONLY\n\tcurrent_loading_size+=10;\n\t//SCR_BeginLoadingPlaque();\n\tSCR_ImageName(server);\n\tSCR_SetLoadingFile(\"gamecode\");\n#endif\n\n\tif (sv.world.worldmodel->type != mod_brush)\n\t\tInfoBuf_SetStarKey(&svs.info, \"*bspversion\", \"\");\n\telse if (sv.world.worldmodel->fromgame == fg_doom)\n\t\tInfoBuf_SetStarKey(&svs.info, \"*bspversion\", \"1\");\n\telse if (sv.world.worldmodel->fromgame == fg_halflife)\n\t\tInfoBuf_SetStarKey(&svs.info, \"*bspversion\", \"30\");\n\telse if (sv.world.worldmodel->fromgame == fg_quake2)\n\t\tInfoBuf_SetStarKey(&svs.info, \"*bspversion\", \"38\");\n\telse if (sv.world.worldmodel->fromgame == fg_quake3)\n\t\tInfoBuf_SetStarKey(&svs.info, \"*bspversion\", \"46\");\n\telse\n\t\tInfoBuf_SetStarKey(&svs.info, \"*bspversion\", \"\");\n\tInfoBuf_SetStarKey(&svs.info, \"*startspot\", (startspot?startspot:\"\"));\n\n\t//\n\t// init physics interaction links\n\t//\n\tWorld_ClearWorld (&sv.world, false);\n\n\t//do we allow csprogs?\n#ifdef PEXT_CSQC\n\tfsz = 0;\n\tif (noents)\n\t\tcsprogsname = \"csaddon.dat\";\n\telse\n\t\tcsprogsname = sv_csqc_progname.string;\n\tif (*csprogsname)\n\t\tfile = COM_LoadTempFile(csprogsname, 0, &fsz);\n\telse\n\t\tfile = NULL;\n\tif (file)\n\t{\n\t\tchar text[64];\n\t\tsv.csqcchecksum = CalcHashInt(&hash_md4, file, fsz);\n\t\tsprintf(text, \"0x%x\", sv.csqcchecksum);\n\t\tInfoBuf_SetValueForStarKey(&svs.info, \"*csprogs\", text);\n\t\tsprintf(text, \"0x%x\", (unsigned int)fsz);\n\t\tInfoBuf_SetValueForStarKey(&svs.info, \"*csprogssize\", text);\n\t\tif (strcmp(csprogsname, \"csprogs.dat\"))\n\t\t\tInfoBuf_SetValueForStarKey(&svs.info, \"*csprogsname\", csprogsname);\n\t\telse\n\t\t\tInfoBuf_SetValueForStarKey(&svs.info, \"*csprogsname\", \"\");\n\t}\n\telse\n\t{\n\t\tsv.csqcchecksum = 0;\n\t\tInfoBuf_SetValueForStarKey(&svs.info, \"*csprogs\", \"\");\n\t\tInfoBuf_SetValueForStarKey(&svs.info, \"*csprogssize\", \"\");\n\t\tInfoBuf_SetValueForStarKey(&svs.info, \"*csprogsname\", \"\");\n\t}\n#endif\n\n\tif (svs.gametype == GT_PROGS)\n\t{\n\t\tif (svprogfuncs)\t//we don't want the q1 stuff anymore.\n\t\t{\n\t\t\tsvprogfuncs->Shutdown(svprogfuncs);\n\t\t\tsv.world.progs = svprogfuncs = NULL;\n\t\t}\n\t}\n\n\tsv.state = ss_loading;\n\nMSV_OpenUserDatabase();\n\n\tsv.world.max_edicts = pr_maxedicts.value;\n\tif (sv.world.max_edicts > MAX_EDICTS)\n\t\tsv.world.max_edicts = MAX_EDICTS;\n#ifdef PEXT_CSQC\n\tsv.csqcentversion = BZ_Malloc(sizeof(*sv.csqcentversion) * sv.world.max_edicts);\n\tfor (i=0 ; i<sv.world.max_edicts ; i++)\n\t\tsv.csqcentversion[i] = 1;\t//force all csqc edicts to start off as version 1\n#endif\n\n\tnewgametype = svs.gametype;\n\tif (noents)\n\t{\n\t\tnewgametype = GT_PROGS;\t//let's just hope this loads.\n\t\tQ_InitProgs(INITPROGS_EDITOR);\n\t}\n#ifdef HLSERVER\n\telse if (SVHL_InitGame())\n\t\tnewgametype = GT_HALFLIFE;\n#endif\n#ifdef Q3SERVER\n\telse if (q3 && q3->sv.InitGame(&svs, &sv, false))\n\t\tnewgametype = GT_QUAKE3;\n#endif\n#ifdef Q2SERVER\n\telse if ((sv.world.worldmodel->fromgame == fg_quake2 || sv.world.worldmodel->fromgame == fg_quake3) && sv.world.worldmodel->funcs.AreasConnected && !*pr_ssqc_progs.string && SVQ2_InitGameProgs())\t//these are the rules for running a q2 server\n\t\tnewgametype = GT_QUAKE2;\t//we loaded the dll\n#endif\n#ifdef VM_LUA\n\telse if (PR_LoadLua())\n\t\tnewgametype = GT_LUA;\n#endif\n#ifdef VM_Q1\n\telse if (PR_LoadQ1QVM())\n\t\tnewgametype = GT_Q1QVM;\n#endif\n\telse\n\t{\n\t\tnewgametype = GT_PROGS;\t//let's just hope this loads.\n\t\tQ_InitProgs(usecinematic?INITPROGS_REQUIRE:INITPROGS_NORMAL);\n\t}\n\n//\tif ((sv.worldmodel->fromgame == fg_quake2 || sv.worldmodel->fromgame == fg_quake3) && !*progs.string && SVQ2_InitGameProgs())\t//full q2 dll decision in one if statement\n\n\tif (newgametype != svs.gametype)\n\t{\n#ifdef HLSERVER\n\t\tif (newgametype != GT_HALFLIFE)\n\t\t\tSVHL_ShutdownGame();\n#endif\n#ifdef Q3SERVER\n\t\tif (newgametype != GT_QUAKE3 && q3)\n\t\t\tq3->sv.ShutdownGame(false);\n#endif\n#ifdef Q2SERVER\n\t\tif (newgametype != GT_QUAKE2)\t//we don't want the q2 stuff anymore.\n\t\t\tSVQ2_ShutdownGameProgs ();\n#endif\n#ifdef VM_Q1\n\t\tif (newgametype != GT_Q1QVM)\n\t\t\tQ1QVM_Shutdown(true);\n#endif\n\n\t\tSV_UpdateMaxPlayers(0);\n\t}\n\tsvs.gametype = newgametype;\n\tCvar_ForceCallback(&sv_nqplayerphysics);\n\n\tsv.models[1] = sv.world.worldmodel;\n#ifdef VM_Q1\n\tif (svs.gametype == GT_Q1QVM)\n\t{\n\t\tint subs;\n\t\tsv.strings.sound_precache[0] = \"\";\n\t\tsv.strings.model_precache[0] = \"\";\n\n\t\tsubs = sv.world.worldmodel->numsubmodels;\n\t\tif (subs > MAX_PRECACHE_MODELS-2)\n\t\t{\n\t\t\tCon_Printf(\"Warning: worldmodel has too many submodels\\n\");\n\t\t\tsubs = MAX_PRECACHE_MODELS-2;\n\t\t}\n\n\t\tsv.strings.model_precache[1] = sv.modelname;\t//the qvm doesn't have access to this array\n\t\tfor (i=1 ; i<subs ; i++)\n\t\t{\n\t\t\tchar *z, *s = va(\"*%u\", i);\n\t\t\tz = Z_TagMalloc(strlen(s)+1, VMFSID_Q1QVM);\n\t\t\tstrcpy(z, s);\n\t\t\tsv.strings.model_precache[1+i] = z;\n\t\t\tsv.models[i+1] = Mod_ForName (Mod_FixName(z, sv.modelname), MLV_WARN);\n\t\t}\n\n\t\t//check player/eyes models for hacks\n\t\tsv.model_player_checksum = SV_CheckModel(\"progs/player.mdl\");\n\t\tsv.eyes_player_checksum = SV_CheckModel(\"progs/eyes.mdl\");\n\t}\n\telse\n#endif\n\tif (svs.gametype == GT_PROGS\n#ifdef VM_LUA\n\t\t|| svs.gametype == GT_LUA\n#endif\n\t\t)\n\t{\n\t\tint subs;\n\t\tsv.strings.model_precache[0] = PR_AddString(svprogfuncs, \"\", 0, false);\n\t\tsv.strings.model_precache[1] = PR_AddString(svprogfuncs, sv.modelname, 0, false);\n\n\t\tsubs = sv.world.worldmodel->numsubmodels;\n\t\tif (subs > MAX_PRECACHE_MODELS-2)\n\t\t{\n\t\t\tCon_Printf(\"Warning: worldmodel has too many submodels\\n\");\n\t\t\tsubs = MAX_PRECACHE_MODELS-2;\n\t\t}\n\t\tfor (i=1 ; i<subs ; i++)\n\t\t{\n\t\t\tsv.strings.model_precache[1+i] = PR_AddString(svprogfuncs, va(\"*%u\", i), 0, false);\n\t\t\tsv.models[i+1] = Mod_ForName (Mod_FixName(sv.strings.model_precache[1+i], sv.modelname), MLV_WARN);\n\t\t}\n\n\t\t//check player/eyes models for hacks\n\t\tsv.model_player_checksum = SV_CheckModel(\"progs/player.mdl\");\n\t\tsv.eyes_player_checksum = SV_CheckModel(\"progs/eyes.mdl\");\n\t}\n#ifdef Q2SERVER\n\telse if (svs.gametype == GT_QUAKE2)\n\t{\n\t\tint subs;\n\t\textern cvar_t sv_airaccelerate;\n\n\t\tsv.stringsalloced = true;\n\t\tmemset(&sv.strings, 0, sizeof(sv.strings));\n\n\t\tif (deathmatch.value)\n\t\t\tsv.strings.configstring[Q2CS_AIRACCEL] = Z_StrDupf(\"%g\", sv_airaccelerate.value);\n\t\telse\n\t\t\tsv.strings.configstring[Q2CS_AIRACCEL] = Z_StrDup(\"0\");\n\n\t\t// init map checksum config string but only for Q2/Q3 maps\n\t\tsv.strings.configstring[Q2CS_MAPCHECKSUM] = Z_StrDupf(\"%i %i\", sv.world.worldmodel->checksum, sv.world.worldmodel->checksum2);\n\n\t\tsubs = sv.world.worldmodel->numsubmodels;\n\t\tif (subs > MAX_PRECACHE_MODELS-1)\n\t\t{\n\t\t\tCon_Printf(\"Warning: worldmodel has too many submodels\\n\");\n\t\t\tsubs = MAX_PRECACHE_MODELS-1;\n\t\t}\n\n\t\tsv.strings.configstring[Q2CS_MODELS+1] = Z_StrDup(sv.modelname);\n\t\tfor (i=1; i<subs && i < Q2MAX_MODELS-2; i++)\n\t\t{\n\t\t\tsv.strings.configstring[Q2CS_MODELS+1+i] = Z_StrDupf(\"*%u\", i);\n\t\t\tsv.models[i+1] = Mod_ForName (Mod_FixName(sv.strings.configstring[Q2CS_MODELS+1+i], sv.modelname), MLV_WARN);\n\t\t}\n\t\tfor ( ; i<subs; i++)\n\t\t{\n\t\t\tsv.strings.q2_extramodels[1+i] = Z_StrDupf(\"*%u\", i);\n\t\t\tsv.models[i+1] = Mod_ForName (Mod_FixName(sv.strings.q2_extramodels[1+i], sv.modelname), MLV_WARN);\n\t\t}\n\t}\n#endif\n\n\n\n#ifndef SERVERONLY\n\tcurrent_loading_size+=10;\n\t//SCR_BeginLoadingPlaque();\n\tSCR_ImageName(server);\n\tSCR_SetLoadingFile(\"clients\");\n#endif\n\n\tfor (i=0 ; i<svs.allocated_client_slots ; i++)\n\t{\n\t\tsvs.clients[i].spawned = false;\n\t\tsvs.clients[i].edict = NULL;\n\t\tsvs.clients[i].name = svs.clients[i].namebuf;\n\t\tsvs.clients[i].team = svs.clients[i].teambuf;\n\t\tInfoSync_Clear(&svs.clients[i].infosync);\t//we'll mark all the info as dirty at some point while connecting.\n\t}\n\n\tswitch (svs.gametype)\n\t{\n\tdefault:\n\t\tSV_Error(\"bad gametype\");\n\t\tbreak;\n#ifdef VM_LUA\n\tcase GT_LUA:\n#endif\n\tcase GT_Q1QVM:\n\tcase GT_PROGS:\n\t\tent = EDICT_NUM_PB(svprogfuncs, 0);\n\t\tent->ereftype = ER_ENTITY;\n\n#ifndef SERVERONLY\n\t\t/*force coop 1 if splitscreen and not deathmatch*/\n\t\t{\n\t\tif (cl_splitscreen.value && !deathmatch.value && !coop.value)\n\t\t\tCvar_Set(&coop, \"1\");\n\t\t}\n#endif\n\t\tif (sv_playerslots.ival > 0)\n\t\t\ti = sv_playerslots.ival;\n\t\telse\n\t\t{\n\t\t\t/*only make one slot for single-player (ktx sucks)*/\n\t\t\tif (!isDedicated && !deathmatch.value && !coop.value && svs.gametype != GT_Q1QVM)\n\t\t\t\ti = 1;\n\t\t\telse\n\t\t\t{\n\t\t\t\ti = maxclients.ival + maxspectators.ival;\n\t\t\t\tif (i < QWMAX_CLIENTS)\n\t\t\t\t\ti = QWMAX_CLIENTS;\n\t\t\t}\n\t\t}\n\t\tif (playerslots)\n\t\t\ti = playerslots;\t//saved game? force it.\n\t\tif (i > MAX_CLIENTS)\n\t\t\ti = MAX_CLIENTS;\n\t\tSV_UpdateMaxPlayers(i);\n\n\t\t// leave slots at start for clients only\n\t\tfor (i=0 ; i<sv.allocated_client_slots ; i++)\n\t\t{\n\t\t\tsvs.clients[i].viewent = 0;\n\n\t\t\tent = ED_Alloc(svprogfuncs, false, 0);//EDICT_NUM(i+1);\n\t\t\tsvs.clients[i].edict = ent;\n\t\t\tent->ereftype = ER_ENTITY;\n\t//ZOID - make sure we update frags right\n\t\t\tsvs.clients[i].old_frags = 0;\n\n\t\t\tif (!svs.clients[i].state && svs.clients[i].name[0])\t//this is a bot.\n\t\t\t\tsvs.clients[i].name[0] = '\\0';\t\t\t\t\t\t//make it go away\n\n#ifdef VM_Q1\n\t\t\tif (svs.gametype == GT_Q1QVM)\n\t\t\t{\t//we'll fix it up later anyway\n\t\t\t\tsvs.clients[i].name = svs.clients[i].namebuf;\n\t\t\t\tsvs.clients[i].team = svs.clients[i].teambuf;\n\t\t\t}\n\t\t\telse\n#endif\n\t\t\t{\n\t\t\t\tsvs.clients[i].name = PR_AddString(svprogfuncs, svs.clients[i].namebuf, sizeof(svs.clients[i].namebuf), false);\n\t\t\t\tsvs.clients[i].team = PR_AddString(svprogfuncs, svs.clients[i].teambuf, sizeof(svs.clients[i].teambuf), false);\n\t\t\t}\n\t\t}\n\t\tbreak;\n#ifdef Q2SERVER\n\tcase GT_QUAKE2:\n\t\tSV_UpdateMaxPlayers(svq2_maxclients);\n\t\tfor (i=0 ; i<sv.allocated_client_slots ; i++)\n\t\t{\n\t\t\tq2ent = Q2EDICT_NUM(i+1);\n\t\t\tq2ent->s.number = i+1;\n\t\t\tsvs.clients[i].q2edict = q2ent;\n\t\t}\n\t\tbreak;\n#endif\n#ifdef Q3SERVER\n\tcase GT_QUAKE3:\n\t\tCvar_LockFromServer(&maxclients, maxclients.string);\n\t\tSV_UpdateMaxPlayers(playerslots?playerslots:max(8,maxclients.ival));\n\t\tbreak;\n#endif\n#ifdef HLSERVER\n\tcase GT_HALFLIFE:\n\t\tSVHL_SetupGame();\n\t\tSV_UpdateMaxPlayers(32);\n\t\tbreak;\n#endif\n\t}\n\t//fixme: is this right?\n\n\tfor (i=0 ; i<sv.allocated_client_slots ; i++)\n\t{\n\t\tQ_strncpyz(svs.clients[i].name, InfoBuf_ValueForKey(&svs.clients[i].userinfo, \"name\"), sizeof(svs.clients[i].namebuf));\n\t\tQ_strncpyz(svs.clients[i].team, InfoBuf_ValueForKey(&svs.clients[i].userinfo, \"team\"), sizeof(svs.clients[i].teambuf));\n\t}\n\n#ifndef SERVERONLY\n\tcurrent_loading_size+=10;\n\t//SCR_BeginLoadingPlaque();\n\tSCR_ImageName(server);\n#endif\n\n\tNET_InitServer();\n\n\t//\n\t// spawn the rest of the entities on the map\n\t//\n\n\t// precache and static commands can be issued during\n\t// map initialization\n\tsv.state = ss_loading;\n\n\tif (svprogfuncs)\n\t{\n\t\t//world entity is hackily spawned\n\t\textern cvar_t coop, pr_imitatemvdsv;\n\t\tent = EDICT_NUM_PB(svprogfuncs, 0);\n\t\tent->ereftype = ER_ENTITY;\n#ifdef VM_Q1\n\t\tif (svs.gametype != GT_Q1QVM)\t//we cannot do this with qvm\n#endif\n\t\t\tsvprogfuncs->SetStringField(svprogfuncs, ent, &ent->v->model, sv.strings.model_precache[1], true);\n\t\tent->v->modelindex = 1;\t\t// world model\n\t\tent->v->solid = SOLID_BSP;\n\t\tent->v->movetype = MOVETYPE_PUSH;\n\t\tVectorCopy(sv.world.worldmodel->mins, ent->v->mins);\n\t\tVectorCopy(sv.world.worldmodel->maxs, ent->v->maxs);\n\t\tVectorCopy(sv.world.worldmodel->mins, ent->v->absmin);\n\t\tVectorCopy(sv.world.worldmodel->maxs, ent->v->absmax);\n\n\t\tif (progstype == PROG_QW && pr_imitatemvdsv.value>0)\n\t\t{\n#ifdef VM_Q1\n\t\t\tif (svs.gametype != GT_Q1QVM)\t//we cannot do this with qvm\n#endif\n\t\t\t{\n\t\t\t\tsvprogfuncs->SetStringField(svprogfuncs, ent, &ent->v->targetname, \"mvdsv\", true);\n\t\t\t\tsvprogfuncs->SetStringField(svprogfuncs, ent, &ent->v->netname, version_string(), false);\n\t\t\t}\n\t\t\tent->v->impulse = 0;//QWE_VERNUM;\n\t\t\tent->v->items = 103;\n\t\t}\n\n\n#ifdef VM_Q1\n\t\tif (svs.gametype != GT_Q1QVM)\t//we cannot do this with qvm\n#endif\n\t\t\tsvprogfuncs->SetStringField(svprogfuncs, NULL, &pr_global_struct->mapname, svs.name, true);\n\n\t\t// serverflags are for cross level information (sigils)\n\t\tpr_global_struct->serverflags = svs.serverflags;\n\t\tpr_global_struct->time = 1.0;\t//match nq behaviour\n\n#ifdef HEXEN2\n\t\tif (progstype == PROG_H2)\n\t\t{\n\t\t\teval_t *eval;\n\t\t\tcvar_t *cv;\n\t\t\tif (coop.value)\n\t\t\t{\n\t\t\t\teval = PR_FindGlobal(svprogfuncs, \"coop\", 0, NULL);\n\t\t\t\tif (eval) eval->_float = coop.value;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\teval = PR_FindGlobal(svprogfuncs, \"deathmatch\", 0, NULL);\n\t\t\t\tif (eval) eval->_float = deathmatch.value;\n\t\t\t}\n\t\t\tcv = Cvar_Get(\"randomclass\", \"0\", CVAR_MAPLATCH, \"Hexen2\");\n\t\t\teval = PR_FindGlobal(svprogfuncs, \"randomclass\", 0, NULL);\n\t\t\tif (eval && cv) eval->_float = cv->value;\n\n\t\t\tcv = Cvar_Get(\"cl_playerclass\", \"1\", CVAR_USERINFO|CVAR_ARCHIVE, \"Hexen2\");\n\t\t\teval = PR_FindGlobal(svprogfuncs, \"cl_playerclass\", 0, NULL);\n\t\t\tif (eval && cv) eval->_float = cv->value;\n\t\t}\n\t\telse\n#endif\n\t\t{\n\t\t\tif (pr_global_ptrs->coop && coop.value)\n\t\t\t\tpr_global_struct->coop = coop.value;\n\t\t\telse if (pr_global_ptrs->deathmatch)\n\t\t\t\tpr_global_struct->deathmatch = deathmatch.value;\n\t\t}\n\n\t\tif (svs.gametype != GT_Q1QVM) //we cannot do this with qvm\n\t\t{\n\t\t\tfor (i = 0; i < svs.numprogs; i++)\t//do this AFTER precaches have been played with...\n\t\t\t{\n\t\t\t\tf = PR_FindFunction (svprogfuncs, \"initents\", svs.progsnum[i]);\n\t\t\t\tif (f)\n\t\t\t\t{\n\t\t\t\t\tPR_ExecuteProgram(svprogfuncs, f);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (progstype == PROG_QW)\n\t\t\t// run the frame start qc function to let progs check cvars\n\t\t\tSV_ProgStartFrame ();\t//prydon gate seems to fail because of this allowance\n\t}\n\n\t// load and spawn all other entities\n\tSCR_SetLoadingFile(\"entities\");\n\tif (!deathmatch.value && !*skill.string)\t//skill was left blank so it doesn't polute serverinfo on deathmatch servers. in single player, we ensure that it gets a proper value.\n\t\tCvar_Set(&skill, \"1\");\n//do this and get the precaches/start up the game\n\tif (sv.world.worldmodel->entitiescrc)\n\t{\n\t\tchar crc[12];\n\t\tsprintf(crc, \"%i\", sv.world.worldmodel->entitiescrc);\n\t\tInfoBuf_SetValueForStarKey(&svs.info, \"*entfile\", crc);\n\t}\n\telse\n\t\tInfoBuf_SetValueForStarKey(&svs.info, \"*entfile\", \"\");\n\n\tif (usecinematic)\n\t\tfile = NULL;\n\telse\n\t\tfile = Mod_GetEntitiesString(sv.world.worldmodel);\n\tif (!file)\n\t\tfile = \"\";\n\n\tswitch(svs.gametype)\n\t{\n\tdefault:\n\t\tPR_SpawnInitialEntities(file);\n\t\tbreak;\n#ifdef Q2SERVER\n\tcase GT_QUAKE2:\n\t\tge->SpawnEntities(svs.name, file, startspot?startspot:\"\");\n\t\tbreak;\n#endif\n\tcase GT_QUAKE3:\n\t\tbreak;\n#ifdef HLSERVER\n\tcase GT_HALFLIFE:\n\t\tSVHL_SpawnEntities(file);\n\t\tbreak;\n#endif\n\t}\n\n#ifndef SERVERONLY\n\tcurrent_loading_size+=10;\n\t//SCR_BeginLoadingPlaque();\n\tSCR_ImageName(server);\n#endif\n\n\tQ_strncpyz(sv.mapname, svs.name, sizeof(sv.mapname));\n\tif (svprogfuncs)\n\t{\n\t\teval_t *val;\n\t\tent = EDICT_NUM_PB(svprogfuncs, 0);\n\t\tent->v->angles[0] = ent->v->angles[1] = ent->v->angles[2] = 0;\n\t\tif ((val = svprogfuncs->GetEdictFieldValue(svprogfuncs, ent, \"message\", ev_string, NULL)))\n\t\t\tsnprintf(sv.mapname, sizeof(sv.mapname), \"%s\", PR_GetString(svprogfuncs, val->string));\n#ifdef HEXEN2\n\t\telse if (progstype == PROG_H2 && (val = svprogfuncs->GetEdictFieldValue(svprogfuncs, ent, \"message\", ev_float, NULL)))\n\t\t{\t//hexen2 uses a float string index for message.\n\t\t\t//if its 0 or negative, fall back on netname instead (for custom maps).\n\t\t\tif (val->_float <= 0)\n\t\t\t\tsnprintf(sv.mapname, sizeof(sv.mapname), \"%s\", PR_GetString(svprogfuncs, ent->v->netname));\n\t\t\telse\n\t\t\t\tsnprintf(sv.mapname, sizeof(sv.mapname), \"%s\", T_GetString(val->_float-1));\n\t\t}\n#endif\n\t\telse\n\t\t\tsnprintf(sv.mapname, sizeof(sv.mapname), \"%s\", svs.name);\n\t\tif (Cvar_Get(\"sv_readonlyworld\", \"1\", 0, \"DP compatability\")->value)\n\t\t{\n\t\t\tent->readonly = true;\t//lock it down!\n\n\t\t\tif (ent->v->origin[0] != 0 || ent->v->origin[1] != 0 || ent->v->origin[2] != 0 || ent->v->angles[0] != 0 || ent->v->angles[1] != 0 || ent->v->angles[2] != 0)\n\t\t\t\tCon_Printf(\"Warning: The world has moved. Alert your nearest reputable news agency.\\n\");\n\n\t\t}\n\n\t\t// look up some model indexes for specialized message compression\n\t\tSV_FindModelNumbers ();\n\t}\n\n#ifndef SERVERONLY\n\tcurrent_loading_size+=10;\n\t//SCR_BeginLoadingPlaque();\n\tSCR_ImageName(server);\n#endif\n\t// run two frames to allow everything to settle\n\t//these frames must be at 1.0 then 1.1 (and 0.1 frametime)\n\t//(bug: starting less than that gives time for the scrag to fall on end)\n\t//hexen2: if you're looking here for the coop-invincible-riders bug, then that's a hexenc bug, not an fte one, and is also present in vanilla hexen2.\n\trealtime += 0.1;\n\tsv.world.physicstime = 1.0;\n\tsv.time = 1.1;\n\tSV_Physics ();\n#ifndef SERVERONLY\n\tcurrent_loading_size+=10;\n\t//SCR_BeginLoadingPlaque();\n\tSCR_ImageName(server);\n#endif\n\trealtime += 0.1;\n//\tsv.world.physicstime = 1.1;\n\tsv.time += 0.1;\n\tSV_Physics ();\n\tsv.time += 0.1;\n\n#ifndef SERVERONLY\n\tcurrent_loading_size+=10;\n\t//SCR_BeginLoadingPlaque();\n\tSCR_ImageName(server);\n#endif\n\n\t// save movement vars\n\tSV_SetMoveVars();\n\n\t// create a baseline for more efficient communications\n//\tSV_CreateBaseline ();\n\tif (svprogfuncs)\n\t\tSVQ1_CreateBaseline();\n#ifdef Q2SERVER\n\tSVQ2_BuildBaselines();\n#endif\n\n\tSV_FlushSignon(true);\n\n\t// all spawning is completed, any further precache statements\n\t// or prog writes to the signon message are errors\n\tif (usecinematic)\n\t\tsv.state = ss_cinematic;\n\telse\n\t\tsv.state = ss_active;\n\n\tSV_GibFilterInit();\n\tSV_FilterImpulseInit();\n\n\tInfoBuf_SetValueForKey (&svs.info, \"map\", svs.name);\n\tif (sv.allocated_client_slots != 1)\n\t\tCon_TPrintf (\"Server spawned.\\n\");\t//misc filenotfounds can be misleading.\n\n\tif (!startspot)\n\t{\n#ifdef SAVEDGAMES\n\t\tSV_FlushLevelCache();\t//to make sure it's caught\n#endif\n\t\t/*for (i=0 ; i<sv.allocated_client_slots ; i++)\n\t\t{\n\t\t\tif (svs.clients[i].spawninfo)\n\t\t\t\tZ_Free(svs.clients[i].spawninfo);\n\t\t\tsvs.clients[i].spawninfo = NULL;\n\t\t}*/\n\t}\n\n\tif (svprogfuncs && startspot)\n\t{\n\t\teval_t *eval;\n\t\teval = PR_FindGlobal(svprogfuncs, \"startspot\", 0, NULL);\n\t\tif (eval && svs.gametype != GT_Q1QVM)\t//we cannot do this with qvm\n\t\t\tsvprogfuncs->SetStringField(svprogfuncs, NULL, &eval->string, startspot, false);\n\t}\n\n\tif (Cmd_AliasExist(\"f_svnewmap\", RESTRICT_LOCAL))\n\t\tCbuf_AddText(\"f_svnewmap\\n\", RESTRICT_LOCAL);\n\n#ifndef SERVERONLY\n\tcurrent_loading_size+=10;\n\t//SCR_BeginLoadingPlaque();\n\tSCR_ImageName(server);\n#endif\n\n\t/*world is now spawned. switch to big coords if there are entities outside the bounds of the map*/\n\tif (!*sv_bigcoords.string && svprogfuncs)\n\t{\n\t\tfloat extent = 0, ne;\n\t\t//fixme: go off bsp extents instead?\n\t\tfor(i = 1; i < sv.world.num_edicts; i++)\n\t\t{\n\t\t\tent = EDICT_NUM_PB(svprogfuncs, i);\n\t\t\tfor (j = 0; j < 3; j++)\n\t\t\t{\n\t\t\t\tne = fabs(ent->v->origin[j]);\n\t\t\t\tif (extent < ne)\n\t\t\t\t\textent = ne;\n\t\t\t}\n\t\t}\n\t\tif (extent > (1u<<15)/8 \n#ifdef TERRAIN\n\t\t\t|| sv.world.worldmodel->terrain\n#endif\n\t\t\t)\n\t\t{\n\t\t\tif (sv.used_signon_space || sv.signon.cursize)\n\t\t\t\tCon_Printf(\"Cannot auto-enable extended coords as the init buffer was used\\n\");\n\t\t\telse\n\t\t\t{\n\t\t\t\tCon_Printf(\"Switching to extended coord sizes\\n\");\n\t\t\t\tSV_SetupNetworkBuffers(true);\n\t\t\t}\n\t\t}\n\t}\n\n\t/*DP_BOTCLIENT bots should move over to the new map too*/\n\tif (svs.gametype == GT_PROGS || svs.gametype == GT_Q1QVM)\n\t{\n\t\tfor (i = 0; i < sv.allocated_client_slots; i++)\n\t\t{\n\t\t\thost_client = &svs.clients[i];\n\t\t\tif (host_client->state == cs_connected && host_client->protocol == SCP_BAD)\n\t\t\t{\n\t\t\t\tif (svs.gametype == GT_Q1QVM)\n\t\t\t\t{\t//ktx expects its bots to drop for each map change.\n\t\t\t\t\tSV_DropClient(host_client);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tsv_player = host_client->edict;\n\t\t\t\tSV_ExtractFromUserinfo(host_client, true);\n\t\t\t\tSV_SpawnParmsToQC(host_client);\n\t\t\t\tSV_SetUpClientEdict(host_client, sv_player);\n#ifdef HAVE_LEGACY\n\t\t\t\tsv_player->xv->clientcolors = host_client->playercolor;\n#endif\n\n\t\t\t\t// call the spawn function\n\t\t\t\tsv.skipbprintclient = host_client;\n\t\t\t\tpr_global_struct->time = sv.world.physicstime;\n\t\t\t\tpr_global_struct->self = EDICT_TO_PROG(svprogfuncs, sv_player);\n\t\t\t\tif (pr_global_ptrs->ClientConnect)\n\t\t\t\t\tPR_ExecuteProgram (svprogfuncs, pr_global_struct->ClientConnect);\n\t\t\t\tsv.skipbprintclient = NULL;\n\n\t\t\t\t// actually spawn the player\n\t\t\t\tpr_global_struct->time = sv.world.physicstime;\n\t\t\t\tpr_global_struct->self = EDICT_TO_PROG(svprogfuncs, sv_player);\n\t\t\t\tif (pr_global_ptrs->PutClientInServer)\n\t\t\t\t\tPR_ExecuteProgram (svprogfuncs, pr_global_struct->PutClientInServer);\n\t\t\t\tsv.spawned_client_slots++;\n\n\t\t\t\t// send notification to all clients\n\t\t\t\thost_client->sendinfo = true;\n\n\t\t\t\thost_client->state = cs_spawned;\n\t\t\t\thost_client->spawned = true;\n\n\t\t\t\tSV_UpdateToReliableMessages();\t//so that we don't flood too much with 31 bots and one player.\n\t\t\t}\n\t\t}\n\t}\n#ifdef Q3SERVER\n\tif (svs.gametype == GT_QUAKE3)\n\t{\n\t\tq3->sv.NewMapConnects();\n\t}\n#endif\n\n\tFS_ReferenceControl(0, 0);\n\n#ifdef MVD_RECORDING\n\tSV_MVD_SendInitialGamestate(NULL);\n#endif\n\n\tSSV_UpdateAddresses();\n\n\t//some mods stuffcmd these, and it would be a shame if they didn't work. we still need the earlier call in case the mod does extra stuff.\n\tSV_SetMoveVars();\n\n\tsv.starttime = Sys_DoubleTime() - sv.time;\n#ifdef SAVEDGAMES\n\tsv.autosave_time = sv.time + sv_autosave.value*60;\n#endif\n\n#ifdef HAVE_CLIENT\n\t//there's a whole load of ugly debug crap there. make sure it stays hidden.\n\tCon_ClearNotify();\n#endif\n\n#ifdef __GLIBC__\n\tif (isDedicated)\n\t\tmalloc_trim(0);\n#endif\n}\n\n#endif\n\n"
  },
  {
    "path": "engine/server/sv_main.c",
    "content": "/*\nCopyright (C) 1996-1997 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n*/\n#include \"quakedef.h\"\n#include \"netinc.h\"\n#include \"fs.h\"\t//for updates\n#ifdef SQL\n#include \"sv_sql.h\"\n#endif\n#include <sys/types.h>\n#ifndef CLIENTONLY\n#define Q2EDICT_NUM(i) (q2edict_t*)((char *)ge->edicts+(i)*ge->edict_size)\n\n#define INVIS_CHAR1 12\n#define INVIS_CHAR2 (char)138\n#define INVIS_CHAR3 (char)160\n\nclient_t\t*host_client;\t\t\t// current client\n\nvoid CvarPostfixKMG(cvar_t *v, char *oldval)\n{\n\tdouble k = 1000;\t//scientific units are in thousands. 10ki is 10*1024.\n\tchar *end;\n\tdouble f = strtod(v->string, &end);\n\tif (*end && end[1]=='i')\n\t\tk = 1024;\t//kibi\n\tif (*end == 'k' || *end == 'K')\n\t\tf *= k;\n\tif (*end == 'm' || *end == 'M')\n\t\tf *= k*k;\n\tif (*end == 'b' || *end == 'B' || *end == 'g' || *end == 'G')\n\t\tf *= k*k*k;\n\tv->ival = f;\n\tv->value = f;\n}\n\n// bound the size of the physics time tic\n#ifdef SERVERONLY\ncvar_t\tsv_mintic\t\t\t\t\t= CVARD(\"sv_mintic\",\"0.013\", \"The minimum interval between running physics frames.\");\n#else\ncvar_t\tsv_mintic\t\t\t\t\t= CVARD(\"sv_mintic\",\"0\", \"The minimum interval between running physics frames.\");\t//client builds can think as often as they want.\n#endif\ncvar_t\tsv_maxtic\t\t\t\t\t= CVARD(\"sv_maxtic\",\"0.1\", \"The maximum interval between running physics frames. If the value is too low, multiple physics interations might be run at a time (based upon sv_limittics). Set to 0 for fixed-interval ticks, which may be required if ODE is used.\");//never run a tick slower than this\ncvar_t\tsv_limittics\t\t\t\t= CVARD(\"sv_limittics\",\"3\", \"The maximum number of ticks that may be run within a frame, to allow the server to catch up if it stalled or if sv_maxtic is too low.\");//\n\ncvar_t\tsv_nailhack\t\t\t\t\t= CVARD(\"sv_nailhack\",\"1\", \"If set to 1, disables the nail entity networking optimisation. This hack was popularised by qizmo which recommends it for better compression. Also allows clients to interplate nail positions and add trails.\");\ncvar_t\tsv_nopvs\t\t\t\t\t= CVARD(\"sv_nopvs\", \"0\", \"Set to 1 to ignore pvs on the server. This can make wallhacks more dangerous, so should only be used for debugging.\");\ncvar_t\tfraglog_public\t\t\t\t= CVARD(\"fraglog_public\", \"1\", \"Enables support for connectionless fraglog requests\");\ncvar_t\tfraglog_details\t\t\t\t= CVARD(\"fraglog_details\", \"1\", \"Bitmask\\n1: killer+killee names.\\n2: killer+killee teams\\n4:timestamp.\\n8:killer weapon\\n16:killer+killee guid.\\nFor compatibility, use 1(vanilla) or 7(mvdsv).\");\n\ncvar_t\tzombietime\t\t\t\t\t= CVARD(\"zombietime\", \"2\", \"Client slots will not be reused for this number of seconds.\");\t// seconds to sink messages\n\ncvar_t\tsv_rconlim\t\t\t\t\t= CVARFD(\"sv_rconlim\", \"4\", CVAR_ARCHIVE, \"Blocks repeated (invalid) rcon attempts.\");\ncvar_t\tsv_crypt_rcon\t\t\t\t= CVARFD(\"sv_crypt_rcon\", \"\", CVAR_ARCHIVE, \"Controls whether the rcon password must be hashed or not. Hashed passwords also partially prevent replay attacks, but does NOT prevent malicious actors from reading the commands/results.\\n0: completely insecure. ONLY allows plain-text passwords. Do not use.\\n1: Mandatory hashing (recommended).\\nEmpty: Allow either, whether the password is secure or not is purely the client's responsibility/fault. Only use this for comptibility with old clients.\");\ncvar_t\tsv_crypt_rcon_clockskew\t\t= CVARFD(\"sv_timestamplen\", \"60\", CVAR_ARCHIVE, \"Limits clock skew to reduce (delayed) replay attacks\");\n#ifdef SERVERONLY\ncvar_t\trcon_password\t\t\t\t= CVARF(\"rcon_password\", \"\", CVAR_NOUNSAFEEXPAND);\t// password for remote server commands\ncvar_t\tpassword\t\t\t\t\t= CVARF(\"password\", \"\", CVAR_NOUNSAFEEXPAND);\t// password for entering the game\n#else\nextern cvar_t\trcon_password;\nextern cvar_t\tpassword;\n#endif\ncvar_t\tspectator_password\t\t\t= CVARF(\"spectator_password\", \"\", CVAR_NOUNSAFEEXPAND);\t// password for entering as a sepctator\n\ncvar_t\tallow_download\t\t\t\t= CVARAD(\"allow_download\", \"1\", /*q3*/\"sv_allowDownload\", \"If 1, permits downloading. Set to 0 to unconditionally block *ALL* downloads from this server. You may wish to set sv_dlURL if you wish clients to still be able to download content.\");\ncvar_t\tallow_download_skins\t\t= CVARD(\"allow_download_skins\", \"1\", \"0 blocks downloading of any file in the skins/ directory\");\ncvar_t\tallow_download_models\t\t= CVARD(\"allow_download_models\", \"1\", \"0 blocks downloading of any file in the progs/ or models/ directory\");\ncvar_t\tallow_download_sounds\t\t= CVARD(\"allow_download_sounds\", \"1\", \"0 blocks downloading of any file in the sound/ directory\");\ncvar_t\tallow_download_particles\t= CVARD(\"allow_download_particles\", \"1\", \"0 blocks downloading of any file in the particles/ directory\");\ncvar_t\tallow_download_demos\t\t= CVARD(\"allow_download_demos\", \"1\", \"0 blocks downloading of any file in the demos/ directory\");\ncvar_t\tallow_download_maps\t\t\t= CVARD(\"allow_download_maps\", \"1\", \"0 blocks downloading of any file in the maps/ directory\");\ncvar_t\tallow_download_logs\t\t\t= CVARFD(\"allow_download_logs\", \"0\", CVAR_NOSET/*cmdline-only*/|CVAR_WARNONCHANGE, \"1 permits downloading files with the extension .log\\n\"CON_ERROR\"THIS IS DANGEROUS AS IT POTENTIALLY ALLOWS PEOPLE TO SEE PASSWORDS OR OTHER PRIVATE INFORMATION.\\nNote that it can be switch on/off via rcon.\");\ncvar_t\tallow_download_anymap\t\t= CVARFD(\"allow_download_pakmaps\", \"0\", CVAR_WARNONCHANGE, \"0: clients may not download map files within the server's packages.\\n1: clients may download such files so long as the package is not deemed copyrighted, for compat with old clients that do not support package downloads.\\n2: client may download such files regardless of copyright state (WARNING! Should never be used!).\");\ncvar_t\tallow_download_pakcontents\t= CVARFD(\"allow_download_pakcontents\", \"0\", CVAR_WARNONCHANGE, \"0: clients may not download non-map files within the server's packages.\\n1: clients may download such files so long as the package is not deemed copyrighted, for compat with old clients that do not support package downloads.\\n2: client may download such files regardless of copyright state (WARNING! ONLY for consistency with vanilla QuakeWorld!).\");\ncvar_t\tallow_download_root\t\t\t= CVARFD(\"allow_download_root\", \"0\", CVAR_WARNONCHANGE, \"If set, enables downloading from the root of the gamedir (not the basedir). This setting has a lower priority than extension-based checks.\");\ncvar_t\tallow_download_textures\t\t= CVARD(\"allow_download_textures\", \"1\", \"0 blocks downloading of any file in the textures/ directory\");\ncvar_t\tallow_download_packages\t\t= CVARD(\"allow_download_packages\", \"1\", \"if 1, permits downloading files (from root directory or elsewhere) with known package extensions (eg: pak+pk3). Packages with a name starting 'pak' are covered by allow_download_copyrighted as well.\");\ncvar_t\tallow_download_refpackages\t= CVARD(\"allow_download_refpackages\", \"1\", \"If set to 1, packages that contain files needed during spawn functions will be become 'referenced' and automatically downloaded to clients.\\nThis cvar should probably not be set if you have large packages that provide replacement pickup models on public servers.\\nThe path command will show a '(ref)' tag next to packages which clients will automatically attempt to download.\");\ncvar_t\tallow_download_wads\t\t\t= CVARD(\"allow_download_wads\", \"1\", \"0 blocks downloading of any file in the wads/ directory, or is in the root directory with the extension .wad\");\ncvar_t\tallow_download_configs\t\t= CVARFD(\"allow_download_configs\", \"0\", CVAR_WARNONCHANGE, \"1 allows downloading of config files, either with the extension .cfg or in the subdir configs/.\\n\"CON_ERROR\"THIS IS DANGEROUS AS IT CAN ALLOW PEOPLE TO READ YOUR RCON PASSWORD ETC.\");\ncvar_t\tallow_download_locs\t\t\t= CVARD(\"allow_download_locs\", \"1\", \"0 blocks downloading of any file in the locs/ directory\");\ncvar_t\tallow_download_copyrighted\t= CVARFD(\"allow_download_copyrighted\", \"0\", CVAR_WARNONCHANGE, \"0 blocks download of packages that are considered copyrighted. Specifically, this means packages with a leading 'pak' prefix on the filename.\\nIf you take your copyrights seriously, you should also set allow_download_pakmaps 0 and allow_download_pakcontents 0.\");\ncvar_t\tallow_download_other\t\t= CVARD(\"allow_download_other\", \"0\", \"0 blocks downloading of any file that was not covered by any of the directory download blocks.\");\n\nextern cvar_t sv_allow_splitscreen;\n\n#if defined(SUPPORT_ICE) || defined(FTE_TARGET_WEB)\nstatic void QDECL SV_Public_Callback(struct cvar_s *var, char *oldvalue)\n{\n\tchar name[64], *e;\n\tCOM_ParseOut(var->string, name, sizeof(name));\n\tstrtol(name, &e, 0);\n\tif (*name&&e==name)\t//failed to read any number out of it.\n\t{\n\t\tFTENET_AddToCollection(svs.sockets, var->name, va(\"/%s\", (*name == '/')?name+1:name), NA_INVALID, NP_RTC_TLS);\n\t\tvar->value = var->ival = 2;\t//so other stuff sees us as holepunched.\n\t}\n#ifdef FTE_TARGET_WEB\n\telse if (var->ival)\t//any kind of public is webrtc public, browsers don't allow more.\n#else\n\telse if (var->ival == 2)\n#endif\n\t\tFTENET_AddToCollection(svs.sockets, var->name, \"/\", NA_INVALID, NP_RTC_TLS);\n\telse\n\t\tFTENET_AddToCollection(svs.sockets, var->name, \"\", NA_INVALID, NP_INVALID);\n}\ncvar_t sv_public\t\t\t= CVARCD(\"sv_public\", \"0\", SV_Public_Callback, \"-1: Fully blocks all inbound connections.\\n0: Disable subscribing to master servers (for private lan-only games).\\n1: Subscribe to public master servers. Your IP address will be listed publicly. Make sure your Router/NAT+Firewall are set to allow inbound connections.\\n2: Subscribe to a broker master, allowing firewall hole punching.\");\n#else\ncvar_t sv_public\t\t\t= CVARD(\"sv_public\", \"0\", \"-1: Fully blocks all inbound connections.\\n0: Disable subscribing to master servers (for private lan-only games).\\n1: Subscribe to public master servers. Your IP address will be listed publicly. Make sure your Router/NAT+Firewall are set to allow inbound connections.\");\n#endif\n\ncvar_t sv_guidhash\t\t\t= CVARD(\"sv_guidkey\", \"\", \"If set, clients will calculate their GUID values against this string instead of the server's IP address. This allows consistency between multiple servers (for stats tracking), but do NOT treat the client's GUID as something that is secure.\");\ncvar_t sv_serverip\t\t\t= CVARD(\"sv_serverip\", \"\", \"Set this cvar to the server's public ip address if the server is behind a firewall and cannot detect its own public address. Providing a port is required if the firewall/nat remaps it, but is otherwise optional.\");\ncvar_t sv_listen_qw\t\t\t= CVARAFD(\"sv_listen_qw\", \"1\", \"sv_listen\", 0, \"Specifies whether normal clients are allowed to connect.\");\ncvar_t sv_listen_nq\t\t\t= CVARD(\"sv_listen_nq\", \"0\", \"Allow new (net)quake clients to connect to the server.\\n0 = don't let them in.\\n1 = allow them in (WARNING: this allows 'qsmurf' DOS attacks).\\n2 = accept (net)quake clients by emulating a challenge (as secure as QW/Q2 but does not fully conform to the NQ protocol).\\nYou may also need to set net_enable_dtls if you wish for the rerelease's client to connect.\");\ncvar_t sv_listen_dp\t\t\t= CVARD(\"sv_listen_dp\", \"0\", \"Allows the server to respond with the DP-specific handshake protocol.\\nWarning: this can potentially get confused with quake2, and results in race conditions with both vanilla netquake and quakeworld protocols.\\nOn the plus side, DP clients can usually be identified correctly, enabling a model+sound limit boost.\");\n#ifdef QWOVERQ3\ncvar_t sv_listen_q3\t\t\t= CVAR(\"sv_listen_q3\", \"0\");\n#endif\ncvar_t sv_reconnectlimit\t= CVARD(\"sv_reconnectlimit\", \"0\", \"Blocks dupe connection within the specified length of time .\");\ncvar_t sv_use_dns\t\t\t= CVARD(\"sv_use_dns\", \"\", \"Performs a reverse-dns lookup in order to report more info about where clients are connecting from.\");\ncvar_t sv_reportheartbeats\t= CVARD(\"sv_reportheartbeats\", \"2\", \"Print a notice each time a heartbeat is sent to a master server. When set to 2, the message will be displayed once.\");\ncvar_t sv_heartbeat_interval = CVARD(\"sv_heartbeat_interval\", \"110\", \"Interval between heartbeats. Low values are abusive, high values may cause NAT/ghost issues.\");\ncvar_t sv_heartbeat_checks\t= CVARD(\"sv_heartbeat_checks\", \"1\", \"Report when sv_public 1 fails due to PROBABLE router/NAT issues.\");\ncvar_t sv_highchars\t\t\t= CVAR(\"sv_highchars\", \"1\");\ncvar_t sv_maxrate\t\t\t= CVARCD(\"sv_maxrate\", \"50000\", CvarPostfixKMG, \"This controls the maximum number of bytes any indivual player may receive (when not downloading). The individual user's rate will also be controlled by the user's rate cvar.\");\ncvar_t sv_maxdrate\t\t\t= CVARAFCD(\"sv_maxdrate\", \"500000\",\n\t\t\t\t\t\t\t\t\t\"sv_maxdownloadrate\", 0, CvarPostfixKMG, \"This cvar controls the maximum number of bytes sent to each player per second while that player is downloading.\\nIf this cvar is set to 0, there will be NO CAP for download rates (if the user's drate is empty/0 too, then expect really fast+abusive downloads that could potentially be considered denial of service attacks)\");\ncvar_t sv_minping\t\t\t= CVARFD(\"sv_minping\", \"\", CVAR_SERVERINFO, \"Simulate fake lag for any players with a ping under the value specified here. Value is in milliseconds.\");\n\ncvar_t sv_bigcoords\t\t\t= CVARFD(\"sv_bigcoords\", \"1\", 0, \"Uses floats for coordinates instead of 16bit values.\\nAlso boosts angle precision, so can be useful even on small maps.\\nAffects clients thusly:\\nQW: enforces a mandatory protocol extension\\nDP: enables DPP7 protocol support\\nNQ: uses RMQ protocol (protocol 999).\");\ncvar_t sv_calcphs\t\t\t= CVARFD(\"sv_calcphs\", \"2\", CVAR_MAPLATCH, \"Enables culling of sound effects. 0=always skip phs. Sounds are globally broadcast. 1=always generate phs. Sounds are always culled. On large maps the phs will be dumped to disk. 2=On large single-player maps, generation of phs is skipped. Otherwise like option 1.\");\n\ncvar_t sv_showconnectionlessmessages\t= CVARD(\"sv_showconnectionlessmessages\", \"0\", \"Display a line describing each connectionless message that arrives on the server. Primarily a debugging feature, but also potentially useful to admins.\");\ncvar_t sv_cullplayers_trace\t\t= CVARFD(\"sv_cullplayers_trace\", \"\", CVAR_SERVERINFO, \"Attempt to cull player entities using tracelines as an anti-wallhack.\");\ncvar_t sv_cullentities_trace\t= CVARFD(\"sv_cullentities_trace\", \"\", CVAR_SERVERINFO, \"Attempt to cull non-player entities using tracelines as an extreeme anti-wallhack.\");\ncvar_t sv_phs\t\t\t\t\t= CVARD(\"sv_phs\", \"1\", \"If 1, do not use the phs. It is generally better to use sv_calcphs instead, and leave this as 1.\");\ncvar_t sv_resetparms\t\t\t= CVAR(\"sv_resetparms\", \"0\");\ncvar_t sv_pupglow\t\t\t\t= CVARFD(\"sv_pupglow\", \"\", CVAR_SERVERINFO, \"Instructs clients to enable hexen2-style powerup pulsing.\");\n\n#ifdef SV_MASTER\ncvar_t sv_master\t\t\t\t= CVAR(\"sv_master\", \"0\");\n#endif\n\ncvar_t\tsv_reliable_sound\t\t= CVARFD(\"sv_reliable_sound\", \"0\",  0, \"Causes all sounds to be sent reliably, so they will not be missed due to packetloss. However, this will cause them to be delayed somewhat, and slightly bursty. This can be overriden using the 'rsnd' userinfo setting (either forced on or forced off). Note: this does not affect sounds attached to particle effects.\");\ncvar_t\tsv_gamespeed\t\t= CVARAF(\"sv_gamespeed\", \"1\", \"slowmo\", 0);\ncvar_t\tsv_csqcdebug\t\t= CVARD(\"sv_csqcdebug\", \"0\", \"Inject packet size information for data directed to csqc.\");\ncvar_t\tsv_csqc_progname\t= CVARAF(\"sv_csqc_progname\", \"csprogs.dat\", /*dp*/\"csqc_progname\", 0);\ncvar_t pausable\t\t\t\t= CVAR(\"pausable\", \"\");\ncvar_t sv_banproxies\t\t= CVARD(\"sv_banproxies\", \"0\", \"If enabled, anyone connecting via known proxy software will be refused entry. This should aid with blocking aimbots, but is only reliable for certain public proxies.\");\ncvar_t\tsv_specprint\t\t= CVARD(\"sv_specprint\", \"3\",\t\"Bitfield that controls which player events spectators see when tracking that player.\\n&1: spectators will see centerprints.\\n&2: spectators will see sprints (pickup messages etc).\\n&4: spectators will receive console commands, this is potentially risky.\\nIndividual spectators can use 'setinfo sp foo' to limit this setting.\");\ncvar_t\tsv_protocol\t\t\t\t= CVARD(\"sv_protocol\", \"\", \"Specifies which protocol extensions to force. Clients which do not support the named protocols will be kicked. Recognised values: fte1 fte2 csqc.\");\n\n//\n// game rules mirrored in svs.info\n//\ncvar_t\tfraglimit\t\t= CVARF(\"fraglimit\",\t\t\"\" ,\tCVAR_SERVERINFO);\ncvar_t\ttimelimit\t\t= CVARF(\"timelimit\",\t\t\"\" ,\tCVAR_SERVERINFO);\ncvar_t\tteamplay\t\t= CVARF(\"teamplay\",\t\t\"\" ,\tCVAR_SERVERINFO);\ncvar_t\tsamelevel\t\t= CVARF(\"samelevel\",\t\t\"\" ,\tCVAR_SERVERINFO);\ncvar_t\tsv_playerslots\t= CVARAD(\"sv_playerslots\",\t\"\",\n\t\t\t\t\t\t\t\t \"maxplayers\",\t\t\"Specify maximum number of player/spectator/bot slots, new value takes effect on the next map (this may result in players getting kicked). This should generally be set to maxclients+maxspectators. Leave blank for a default value.\\nMaximum value of \"STRINGIFY(MAX_CLIENTS)\". Values above 16 will result in issues with vanilla NQ clients. Effective values other than 32 will result in issues with vanilla QW clients.\");\ncvar_t\tmaxclients\t\t= CVARAFD(\"maxclients\",\t\t\"8\",\n\t\t\t\t\t\t\t\t \"sv_maxclients\",\t\t\tCVAR_SERVERINFO, \"Specify the maximum number of players allowed on the server at once. Can be changed mid-map.\");\ncvar_t\tmaxspectators\t= CVARFD(\"maxspectators\",\t\"8\",\tCVAR_SERVERINFO, \"Specify the maximum number of spectators allowed on the server at once. Can be changed mid-map.\");\n#ifdef SERVERONLY\ncvar_t\tdeathmatch\t\t= CVARF(\"deathmatch\",\t\t\"1\",\tCVAR_SERVERINFO);\t\t\t// 0, 1, or 2\n#else\ncvar_t\tdeathmatch\t\t= CVARF(\"deathmatch\",\t\t\"\",\tCVAR_SERVERINFO);\t\t\t// 0, 1, or 2\n#endif\ncvar_t\tcoop\t\t\t= CVARF(\"coop\",\t\t\t\"\" ,\tCVAR_SERVERINFO);\ncvar_t\tskill\t\t\t= CVARF(\"skill\",\t\t\t\"\" ,\tCVAR_SERVERINFO);\t\t\t// 0, 1, 2 or 3\ncvar_t\tspawn\t\t\t= CVARF(\"spawn\",\t\t\t\"\" ,\tCVAR_SERVERINFO);\ncvar_t\twatervis\t\t= CVARF(\"watervis\",\t\t\"\" ,\tCVAR_SERVERINFO);\n#pragma warningmsg(\"Remove this some time\")\ncvar_t\tallow_skybox\t= CVARFD(\"allow_skybox\",\t\"\",\t\tCVAR_SERVERINFO, \"This setting says whether clients should skip writing skybox depth when rendering skyboxes/skydomes. Skipping depth writes is required for halflife, quake2, and quake3 compat, but q1 content generally requires depth masking. Empty uses format-specific defaults.\");\ncvar_t\tsv_allow_splitscreen = CVARFD(\"allow_splitscreen\",\"\",CVAR_SERVERINFO, \"Specifies whether clients can use splitscreen extensions to dynamically add additional clients. This only affects remote clients and not the built-in client.\\nClients may need to reconnect in order to add seats when this is changed.\");\ncvar_t\tfbskins\t\t\t= CVARF(\"fbskins\",\t\t\t\"\",\tCVAR_SERVERINFO);\t//to get rid of lame fuhquake fbskins\n\ncvar_t\tsv_motd[]\t\t={\tCVAR(\"sv_motd1\",\t\t\"\"),\n\t\t\t\t\t\t\tCVAR(\"sv_motd2\",\t\t\"\"),\n\t\t\t\t\t\t\tCVAR(\"sv_motd3\",\t\t\"\"),\n\t\t\t\t\t\t\tCVAR(\"sv_motd4\",\t\t\"\")\t};\n\ncvar_t sv_compatiblehulls = CVAR(\"sv_compatiblehulls\", \"1\");\ncvar_t  dpcompat_stats = CVAR(\"dpcompat_stats\", \"0\");\n\ncvar_t\thostname = CVARF(\"hostname\",\"unnamed\", CVAR_SERVERINFO);\n\ncvar_t\tsecure = CVARF(\"secure\", \"\", CVAR_SERVERINFO);\n\nextern cvar_t sv_nqplayerphysics;\n\nchar cvargroup_serverpermissions[] = \"server permission variables\";\nchar cvargroup_serverinfo[] = \"serverinfo variables\";\nchar cvargroup_serverphysics[] = \"server physics variables\";\nchar cvargroup_servercontrol[] = \"server control variables\";\n\nvfsfile_t\t*sv_fraglogfile;\n\nvoid PRH2_SetPlayerClass(client_t *cl, int classnum, qboolean fromqc);\nvoid SV_DeDupeName(const char *val, client_t *cl, char *newname, size_t newnamesize);\n\nint\tnextuserid;\n\n//============================================================================\n\nqboolean ServerPaused(void)\n{\n\treturn sv.paused;\n}\n\n/*\n================\nSV_Shutdown\n\nQuake calls this before calling Sys_Quit or Sys_Error\n================\n*/\nvoid SV_Shutdown (void)\n{\n\tSV_Master_Shutdown ();\n\tif (sv_fraglogfile)\n\t{\n\t\tVFS_CLOSE (sv_fraglogfile);\n\t\tsv_fraglogfile = NULL;\n\t}\n\n\tSV_UnspawnServer();\n#ifdef SUBSERVERS\n\tMSV_Shutdown();\n#endif\n\n#ifdef MVD_RECORDING\n\tif (sv.mvdrecording)\n\t\tSV_MVDStop (MVD_CLOSE_STOPPED, false);\n#endif\n\n\tif (svs.entstatebuffer.entities)\n\t{\n\t\tBZ_Free(svs.entstatebuffer.entities);\n\t\tmemset(&svs.entstatebuffer.entities, 0, sizeof(svs.entstatebuffer.entities));\n\t}\n\tif (sv_staticentities)\n\t{\n\t\tsv_max_staticentities = 0;\n\t\tsv.num_static_entities = 0;\n\t\tBZ_Free(sv_staticentities);\n\t\tsv_staticentities = NULL;\n\t}\n\tif (sv_staticsounds)\n\t{\n\t\tsv_max_staticsounds = 0;\n\t\tsv.num_static_sounds = 0;\n\t\tBZ_Free(sv_staticsounds);\n\t\tsv_staticsounds = NULL;\n\t}\n\twhile (svs.free_lagged_packet)\n\t{\n\t\tlaggedpacket_t *lp = svs.free_lagged_packet;\n\t\tsvs.free_lagged_packet = lp->next;\n\t\tZ_Free(lp);\n\t}\n\n#ifdef WEBCLIENT\n\tHTTP_CL_Terminate();\n#endif\n\n#ifdef HEXEN2\n\tT_FreeStrings();\n#endif\n\n\tSV_GibFilterPurge();\n\n\tLog_ShutDown();\n\n\tNET_Shutdown ();\n\n#ifdef PLUGINS\n\tPlug_Shutdown(true);\n#endif\n\tMod_Shutdown(true);\n#ifdef PACKAGEMANAGER\n\tPM_Shutdown(false);\n#endif\n\tCOM_DestroyWorkerThread();\n\tFS_Shutdown();\n#ifdef PLUGINS\n\tPlug_Shutdown(false);\n#endif\n\tCvar_Shutdown();\n\tCmd_Shutdown();\n\n\n\tInfoBuf_Clear(&svs.info, true);\n\tInfoBuf_Clear(&svs.localinfo, true);\n\n\tCOM_BiDi_Shutdown();\n\tTL_Shutdown();\n\tMemory_DeInit();\n}\n\n/*\n================\nSV_Error\n\nSends a datagram to all the clients informing them of the server crash,\nthen exits\n================\n*/\nvoid VARGS SV_Error (const char *error, ...)\n{\n\tva_list\t\targptr;\n\tstatic\tchar\t\tstring[1024];\n\tstatic\tqboolean inerror = false;\n\tint i;\n\n\tif (inerror)\n\t{\n\t\tSys_Error (\"SV_Error: recursively entered (%s)\", string);\n\t}\n\n\tinerror = true;\n\n\tva_start (argptr,error);\n\tvsnprintf (string,sizeof(string)-1, error,argptr);\n\tva_end (argptr);\n\n\t{\n\t\textern cvar_t pr_ssqc_coreonerror;\n\n\t\tif (svprogfuncs && pr_ssqc_coreonerror.value && svprogfuncs->save_ents)\n\t\t{\n\t\t\tsize_t size = 1024*1024*8;\n\t\t\tchar *buffer = BZ_Malloc(size);\n\t\t\tsvprogfuncs->save_ents(svprogfuncs, buffer, &size, size, 3);\n\t\t\tCOM_WriteFile(\"ssqccore.txt\", FS_GAMEONLY, buffer, size);\n\t\t\tBZ_Free(buffer);\n\t\t}\n\t}\n\n\n\tSV_EndRedirect();\n\n\tCon_Printf (CON_ERROR\"SV_Error: %s\\n\",string);\n\n\tif (sv.state)\n\t\tSV_FinalMessage (va(\"server crashed: %s\\n\", string));\n\n\t//flag all players as unspawned, so gamecode doesn't recurse while already crashing. that sort of thing just results in more crashes.\n\tfor (i = 0; i < sv.allocated_client_slots; i++)\n\t\tsvs.clients[i].spawned = false;\n\tsv.spawned_client_slots = 0;\n\tsv.spawned_observer_slots = 0;\n\n\n#ifndef SERVERONLY\n\tif (cls.state)\n\t{\n\t\tinerror = false;\n\t\tHost_EndGame(\"SV_Error: %s\\n\",string);\n\t}\n\tSCR_EndLoadingPlaque();\n\n\tif (!isDedicated)\t//dedicated servers crash...\n\t{\n\t\textern jmp_buf \thost_abort;\n\t\tSCR_EndLoadingPlaque();\n\t\tSV_UnspawnServer();\n\t\tCL_Disconnect(string);\n\t\tinerror=false;\n\t\tlongjmp (host_abort, 1);\n\t}\n#endif\n\n\tsys_nounload = true;\n\tSV_UnspawnServer();\n\tSV_Shutdown ();\n\n\tSys_Error (\"SV_Error: %s\\n\",string);\n}\n\n#ifdef SERVERONLY\nvoid VARGS Host_Error (const char *error, ...)\n{\n\tva_list\t\targptr;\n\tchar\t\tstring[1024];\n\n\tva_start (argptr,error);\n\tvsnprintf (string,sizeof(string)-1, error,argptr);\n\tva_end (argptr);\n\n\tSV_Error(\"%s\", string);\n}\n#endif\n\n#ifdef SERVERONLY\nvoid VARGS Host_EndGame (const char *error, ...)\n{\n\tva_list\t\targptr;\n\tchar\t\tstring[1024];\n\n\tva_start (argptr,error);\n\tvsnprintf (string,sizeof(string)-1, error,argptr);\n\tva_end (argptr);\n\n\tSV_Error(\"%s\", string);\n}\n#endif\n\n/*\n==================\nSV_FinalMessage\n\nUsed by SV_Error and SV_Quit_f to send a final message to all connected\nclients before the server goes down.  The messages are sent immediately,\nnot just stuck on the outgoing message list, because the server is going\nto totally exit after returning from this function.\n==================\n*/\nvoid SV_FinalMessage (char *message)\n{\n\tint\t\t\ti;\n\tclient_t\t*cl;\n\tsizebuf_t\tbuf;\n\tchar\t\tbufdata[1024];\n\n\tmemset(&buf, 0, sizeof(buf));\n\tbuf.data = bufdata;\n\tbuf.maxsize = sizeof(bufdata);\n\n\tfor (i=0, cl = svs.clients ; i<svs.allocated_client_slots ; i++, cl++)\n\t\tif (cl->state >= cs_spawned && !cl->controlled)\n\t\t{\n\t\t\tif (ISQWCLIENT(cl))\n\t\t\t{\n\t\t\t\tSZ_Clear (&buf);\n\t\t\t\tMSG_WriteByte (&buf, svc_print);\n\t\t\t\tMSG_WriteByte (&buf, PRINT_HIGH);\n\t\t\t\tMSG_WriteString (&buf, message);\n\t\t\t\tMSG_WriteByte (&buf, svc_disconnect);\n\t\t\t}\n#ifdef NQPROT\n\t\t\telse if (ISNQCLIENT(cl))\n\t\t\t{\n\t\t\t\tSZ_Clear (&buf);\n\t\t\t\tMSG_WriteByte (&buf, svc_print);\n\t\t\t\tMSG_WriteString (&buf, message);\n\t\t\t\tMSG_WriteByte (&buf, svc_disconnect);\n\t\t\t}\n#endif\n\t\t\telse\n\t\t\t\tcontinue;\n\n\t\t\tNetchan_Transmit (&cl->netchan, buf.cursize\n\t\t\t\t\t, buf.data, 10000);\n\t\t}\n}\n\n\n\n/*\n=====================\nSV_DropClient\n\nCalled when the player is totally leaving the server, either willingly\nor unwillingly.  This is NOT called if the entire server is quiting\nor crashing.\n=====================\n*/\nvoid SV_DropClient (client_t *drop)\n{\n\tunsigned int i;\n\tlaggedpacket_t *lp;\n\tsizebuf_t termmsg;\n\tchar termbuf[64];\n\n\t/*drop the first in the chain first*/\n\tif (drop->controller && drop->controller != drop)\n\t{\n\t\tSV_DropClient(drop->controller);\n\t\treturn;\n\t}\n\n\tif (!drop->controller && drop->netchan.remote_address.type != NA_INVALID && drop->netchan.remote_address.type != NA_LOOPBACK)\n\t{\n\t\t// add the disconnect\n\t\tif (drop->state < cs_connected)\n\t\t{\n\t\t\tswitch (drop->protocol)\n\t\t\t{\n\t\t\tcase SCP_QUAKE2:\n\t\t\tcase SCP_QUAKE2EX:\n\t\t\t\tMSG_WriteByte (&drop->netchan.message, svcq2_disconnect);\n\t\t\t\tbreak;\n\t\t\tcase SCP_QUAKEWORLD:\n\t\t\tcase SCP_NETQUAKE:\n\t\t\tcase SCP_BJP3:\n\t\t\tcase SCP_FITZ666:\n\t\t\tcase SCP_DARKPLACES6:\n\t\t\tcase SCP_DARKPLACES7:\n\t\t\t\tMSG_WriteByte (&drop->netchan.message, svc_disconnect);\n\t\t\t\tbreak;\n\t\t\tcase SCP_BAD:\n\t\t\t\tbreak;\n\t\t\tcase SCP_QUAKE3:\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\tif (drop->state == cs_spawned)\n\t{\n#ifdef SVRANKING\n\t\tif (drop->rankid)\n\t\t{\n\t\t\tint j;\n\t\t\trankstats_t rs;\n\t\t\tif (Rank_GetPlayerStats(drop->rankid, &rs))\n\t\t\t{\n\t\t\t\trs.timeonserver += realtime - drop->stats_started;\n\t\t\t\tdrop->stats_started = realtime;\n\t\t\t\trs.kills += drop->kills;\n\t\t\t\trs.deaths += drop->deaths;\n\n\t\t\t\trs.flags1 &= ~(RANK_CUFFED|RANK_MUTED|RANK_CRIPPLED);\n//\t\t\t\tif (drop->iscuffed==2)\n//\t\t\t\t\trs.flags1 |= RANK_CUFFED;\n//\t\t\t\tif (drop->ismuted==2)\n//\t\t\t\t\trs.flags1 |= RANK_MUTED;\n//\t\t\t\tif (drop->iscrippled==2)\n//\t\t\t\t\trs.flags1 |= RANK_CRIPPLED;\n\t\t\t\tdrop->kills=0;\n\t\t\t\tdrop->deaths=0;\n\t\t\t\tpr_global_struct->self = EDICT_TO_PROG(svprogfuncs, drop->edict);\n\t\t\t\tif (pr_global_ptrs->SetChangeParms)\n\t\t\t\t\tPR_ExecuteProgram (svprogfuncs, pr_global_struct->SetChangeParms);\n\t\t\t\tfor (j=0 ; j<NUM_RANK_SPAWN_PARMS ; j++)\n\t\t\t\t\tif (pr_global_ptrs->spawnparamglobals[j])\n\t\t\t\t\t\trs.parm[j] = *pr_global_ptrs->spawnparamglobals[j];\n#if NUM_RANK_SPAWN_PARMS>32\n\t\t\t\trs.lastseen = time(NULL);\n#endif\n\t\t\t\tRank_SetPlayerStats(drop->rankid, &rs);\n\t\t\t}\n\t\t}\n#endif\n\t}\n#ifdef SUBSERVERS\n\tSSV_SavePlayerStats(drop, (drop->redirect==2)?1:2);\n#endif\n#ifdef SVCHAT\n\tSV_WipeChat(drop);\n#endif\n\n\tif (sv.world.worldmodel->loadstate != MLS_LOADED)\n\t\tCon_Printf(CON_WARNING \"Warning: not notifying gamecode about client disconnection due to invalid worldmodel\\n\");\n\telse\n\tswitch(svs.gametype)\n\t{\n\tcase GT_MAX:\n\t\tbreak;\n#ifdef VM_LUA\n\tcase GT_LUA:\n#endif\n\tcase GT_Q1QVM:\n\tcase GT_PROGS:\n\t\tif (svprogfuncs)\n\t\t{\n\t\t\tSV_DespawnClient(drop);\n\t\t\tdrop->edict = NULL;\n\n\t\t\tif (drop->spawninfo)\n\t\t\t\tZ_Free(drop->spawninfo);\n\t\t\tdrop->spawninfo = NULL;\n\t\t}\n\t\tbreak;\n\tcase GT_QUAKE2:\n#ifdef Q2SERVER\n\t\tif (ge)\n\t\t\tge->ClientDisconnect(drop->q2edict);\n#endif\n\t\tbreak;\n\tcase GT_QUAKE3:\n#ifdef Q3SERVER\n\t\tq3->sv.DropClient(drop);\n#endif\n\t\tbreak;\n\tcase GT_HALFLIFE:\n#ifdef HLSERVER\n\t\tSVHL_DropClient(drop);\n#endif\n\t\tbreak;\n\t}\n\n\tSV_Prompt_Clear(drop);\n\tif (drop->centerprintstring)\n\t\tZ_Free(drop->centerprintstring);\n\tdrop->centerprintstring = NULL;\n\n\tif (!drop->redirect && drop->state > cs_zombie)\n\t{\n\t\tif (drop->spectator)\n\t\t\tCon_TPrintf (\"Spectator \\\"%s\\\" removed\\n\",drop->name);\n\t\telse\n\t\t\tCon_TPrintf (\"Client \\\"%s\\\" removed\\n\",drop->name);\n\t}\n\n#if defined(HAVE_LEGACY) && defined(MVD_RECORDING)\n\tSV_DownloadQueueClear(drop);\n#endif\n\tif (drop->download)\n\t{\n\t\tVFS_CLOSE (drop->download);\n\t\tdrop->download = NULL;\n\t}\n\tif (drop->upload)\n\t{\n\t\tVFS_CLOSE (drop->upload);\n\t\tdrop->upload = NULL;\n\t}\n\t*drop->uploadfn = 0;\n\n#ifdef HAVE_CLIENT\n\tif (drop->netchan.remote_address.type == NA_LOOPBACK)\n\t{\n\t\tif (drop->protocol != SCP_BAD)\n\t\t\tNetchan_Transmit(&drop->netchan, 0, \"\", SV_RateForClient(drop));\n#ifdef warningmsg\n#pragma warningmsg(\"This means that we may not see the reason we kicked ourselves.\")\n#endif\n\t\tdrop->state = cs_free;\t//don't do zombie stuff\n\t\tcls.state = ca_disconnected;\n\t\tCL_BeginServerReconnect();\n\t}\n\telse\n#endif\n\tif (drop->protocol == SCP_BAD)\n\t\tdrop->state = cs_free;\t//skip zombie state for bots.\n\telse if (drop->state == cs_spawned || drop->istobeloaded)\n\t{\n\t\tdrop->state = cs_zombie;\t\t// become free in a few seconds\n\t\tdrop->connection_started = realtime;\t// for zombie timeout\n\t}\n\telse\n\t\tdrop->state = cs_free;\t//skip zombie state if qc couldn't access it anyway.\n\tdrop->istobeloaded = false;\n\n\tdrop->old_frags = 0;\n#ifdef SVRANKING\n\tdrop->kills = 0;\n\tdrop->deaths = 0;\n#endif\n\tdrop->namebuf[0] = 0;\n\tdrop->name = drop->namebuf;\n\tInfoBuf_Clear(&drop->userinfo, true);\n\tInfoSync_Clear(&drop->infosync);\n\n\twhile ((lp = drop->laggedpacket))\n\t{\n\t\tdrop->laggedpacket = lp->next;\n\t\tlp->next = svs.free_lagged_packet;\n\t\tsvs.free_lagged_packet = lp;\n\t}\n\tdrop->laggedpacket_last = NULL;\n\n\tdrop->pendingdeltabits = NULL;\n\tdrop->pendingcsqcbits = NULL;\n\tif (drop->frameunion.frames)\t//union of the same sort of structure\n\t{\n\t\tZ_Free(drop->frameunion.frames);\n\t\tdrop->frameunion.frames = NULL;\n\t}\n\tif (drop->sentents.entities)\n\t{\n\t\tZ_Free(drop->sentents.entities);\n\t\tmemset(&drop->sentents.entities, 0, sizeof(drop->sentents.entities));\n\t}\n\n\tfor (i = 0; i < MAX_CL_STATS; i++)\n\t{\n\t\tZ_Free(drop->statss[i]);\n\t\tdrop->statss[i] = NULL;\n\t}\n\n\tdrop->csqcactive = false;\n\n\tmemset(&termmsg, 0, sizeof(termmsg));\n\ttermmsg.data = termbuf;\n\ttermmsg.maxsize = sizeof(termbuf);\n\ttermmsg.cursize = 0;\n\tif (drop->netchan.remote_address.type == NA_LOOPBACK)\n\t{\n\t}\n\telse if (ISQWCLIENT(drop) || ISNQCLIENT(drop))\n\t{\n\t\tMSG_WriteByte(&termmsg, svc_disconnect);\n\t}\n\telse if (ISQ2CLIENT(drop))\n\t{\n\t\tMSG_WriteByte(&termmsg, svcq2_disconnect);\n\t}\n\telse if (ISQ3CLIENT(drop))\n\t{\n\t}\n\tif (drop->netchan.remote_address.type != NA_INVALID && drop->netchan.message.maxsize)\n\t{\n#ifdef NQPROT\n\t\tif (drop->netchan.isnqprotocol && drop->netchan.nqunreliableonly != 1)\n\t\t{\t//try and flush the reliables, so they can see why they were kicked.\n\t\t\tdrop->netchan.nqunreliableonly = 3;\t//may cause problems if they were loading content. not much we can do about that.\n\t\t\twhile(Netchan_Transmit (&drop->netchan, 0, NULL, 10000))\n\t\t\t\t;\n\t\t}\n#endif\n\t\t//send twice, to cover packetloss a little.\n\t\tNetchan_Transmit (&drop->netchan, termmsg.cursize, termmsg.data, 10000);\n\t\tNetchan_Transmit (&drop->netchan, termmsg.cursize, termmsg.data, 10000);\n\n#ifdef HAVE_DTLS\n\t\tNET_DTLS_Disconnect(svs.sockets, &drop->netchan.remote_address);\n#endif\n\t}\n\n\tif (svs.gametype == GT_PROGS || svs.gametype == GT_Q1QVM)\t//gamecode should do it all for us.\n\t{\n// send notification to all remaining clients\n\t\tSV_FullClientUpdate (drop, NULL);\n#ifdef MVD_RECORDING\n\t\tSV_MVD_FullClientUpdate(NULL, drop);\n#endif\n\t}\n\n\tif (drop->controlled)\n\t{\n\t\tdrop->controlled->controller = NULL;\n\t\tdrop->controlled->protocol = SCP_BAD;\t//with the controller dead, make sure we don't try sending anything to it\n\t\tSV_DropClient(drop->controlled);\n\t\tdrop->controlled = NULL;\n\t}\n}\n\n//called when someone's connection goes away.\nvoid SV_DropClient_ByAddress (netadr_t *addr)\n{\t//just flag em, its easier on stack...\n\tint i;\n\tfor (i = 0; i < svs.allocated_client_slots; i++)\n\t{\n\t\tif (!svs.clients[i].drop && svs.clients[i].state >= cs_connected)\n\t\t\tif (NET_CompareAdr(&svs.clients[i].netchan.remote_address, addr))\n\t\t\t{\n\t\t\t\tSV_BroadcastTPrintf (PRINT_HIGH, \"%s lost connection\\n\", svs.clients[i].name);\n\t\t\t\tsvs.clients[i].drop = true;\n\t\t\t}\n\t}\n}\n\n\n//====================================================================\n\ntypedef struct pinnedmessages_s {\n\tstruct pinnedmessages_s *next;\n\tchar setby[64];\n\tchar message[1024];\n} pinnedmessages_t;\nstatic pinnedmessages_t *pinned;\nstatic qboolean dopinnedload = true;\nvoid PIN_DeleteOldestMessage(void);\nvoid PIN_MakeMessage(char *from, char *msg);\n\nvoid PIN_LoadMessages(void)\n{\n\tchar setby[64];\n\tchar message[1024];\n\n\tint i;\n\tchar *file, *end;\n\tchar *lstart;\n\tint len;\n\n\tdopinnedload = false;\n\n\twhile(pinned)\n\t\tPIN_DeleteOldestMessage();\n\n\tlen = FS_LoadFile(\"pinned.txt\", (void**)&file);\n\tif (!file)\n\t\treturn;\n\tend = file+len;\n\n\tlstart = file;\n\tfor(;;)\n\t{\n\t\twhile (lstart<end && *lstart <= ' ')\n\t\t\tlstart++;\n\n\t\tfor (i = 0; lstart<end && i < sizeof(message)-1; i++)\n\t\t{\n\t\t\tif (*lstart == '\\n' || *lstart == '\\r')\n\t\t\t\tbreak;\n\t\t\tmessage[i] = *lstart++;\n\t\t}\n\t\tmessage[i] = '\\0';\n\n\t\twhile (lstart<end && *lstart <= ' ')\n\t\t\tlstart++;\n\n\t\tfor (i = 0; lstart<end && i < sizeof(setby)-1; i++)\n\t\t{\n\t\t\tif (*lstart == '\\n' || *lstart == '\\r')\n\t\t\t\tbreak;\n\t\t\tsetby[i] = *lstart++;\n\t\t}\n\t\tsetby[i] = '\\0';\n\n\t\tif (!*setby)\n\t\t\tbreak;\n\n\t\tPIN_MakeMessage(setby, message);\n\t}\n\n\tFS_FreeFile(file);\n}\nvoid PIN_SaveMessages(void)\n{\n\tpinnedmessages_t *p;\n\tvfsfile_t *f;\n\n\tf = FS_OpenVFS(\"pinned.txt\", \"wb\", FS_GAMEONLY);\n\tif (!f)\n\t{\n\t\tCon_TPrintf(CON_ERROR \"couldn't write to %s\\n\", \"pinned.txt\");\n\t\treturn;\n\t}\n\n\tfor (p = pinned; p; p = p->next)\n\t\tVFS_PRINTF(f, \"%s\\r\\n\\t%s\\r\\n\\n\", p->message, p->setby);\n\n\tVFS_CLOSE(f);\n}\nvoid PIN_DeleteOldestMessage(void)\n{\n\tpinnedmessages_t *old;\n\tif (dopinnedload)\n\t\tPIN_LoadMessages();\n\n\told = pinned;\n\tif (old)\n\t{\n\t\tpinned = pinned->next;\n\t\tZ_Free(old);\n\t}\n}\nvoid PIN_MakeMessage(char *from, char *msg)\n{\n\tpinnedmessages_t *p;\n\tpinnedmessages_t *newp;\n\n\tif (dopinnedload)\n\t\tPIN_LoadMessages();\n\n\tnewp = BZ_Malloc(sizeof(pinnedmessages_t));\n\tQ_strncpyz(newp->setby, from, sizeof(newp->setby));\n\tQ_strncpyz(newp->message, msg, sizeof(newp->message));\n\tnewp->next = NULL;\n\n\tif (!pinned)\n\t\tpinned = newp;\n\telse\n\t{\n\t\tfor (p = pinned; ; p = p->next)\n\t\t{\n\t\t\tif (!p->next)\n\t\t\t{\n\t\t\t\tp->next = newp;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n}\nvoid PIN_ShowMessages(client_t *cl)\n{\n\tpinnedmessages_t *p;\n\tif (dopinnedload)\n\t\tPIN_LoadMessages();\n\n\tif (!pinned)\n\t\treturn;\n\n\tSV_ClientPrintf(cl, PRINT_HIGH, \"\\n\\n\\n\");\n\tfor (p = pinned; p; p = p->next)\n\t{\n\t\tSV_ClientPrintf(cl, PRINT_HIGH, \"%s\\n\\n        %s\\n\", p->message, p->setby);\n\t\tSV_ClientPrintf(cl, PRINT_HIGH, \"\\n\\n\\n\");\n\t}\n\n}\n\n//====================================================================\n\n/*\n===================\nSV_CalcPing\n\n===================\n*/\nint SV_CalcPing (client_t *cl, qboolean forcecalc)\n{\n\tfloat\t\tping;\n\tint\t\t\ti;\n\tint\t\t\tcount;\n\n\tif (cl->controller)\n\t\tcl = cl->controller;\n\n\tif (!cl->frameunion.frames)\n\t\treturn 0;\n\n\tsafeswitch (cl->protocol)\n\t{\n\tcase SCP_QUAKE2:\n\tcase SCP_QUAKE2EX:\n#ifdef Q2SERVER\n\t\t{\n\t\t\tq2client_frame_t *frame;\n\t\t\tping = 0;\n\t\t\tcount = 0;\n\t\t\tfor (frame = cl->frameunion.q2frames, i=0 ; i<Q2UPDATE_BACKUP ; i++, frame++)\n\t\t\t{\n\t\t\t\tif (frame->ping_time > 0)\n\t\t\t\t{\n\t\t\t\t\tping += frame->ping_time;\n\t\t\t\t\tcount++;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!count)\n\t\t\t\treturn 9999;\n\t\t\tping /= count;\n\n\t\t}\n\t\treturn ping;\n#endif\n\tcase SCP_QUAKE3:\n\t\tbreak;\n\tcase SCP_DARKPLACES6:\n\tcase SCP_DARKPLACES7:\n\tcase SCP_NETQUAKE:\n\tcase SCP_BJP3:\n\tcase SCP_FITZ666:\n\tcase SCP_QUAKEWORLD:\n\t\t{\n\t\t\tregister\tclient_frame_t *frame;\n\t\t\tping = 0;\n\t\t\tcount = 0;\n\t\t\tfor (frame = cl->frameunion.frames, i=0 ; i<UPDATE_BACKUP ; i++, frame++)\n\t\t\t{\n\t\t\t\tif (frame->ping_time > 0)\n\t\t\t\t{\n\t\t\t\t\tping += frame->ping_time;\n\t\t\t\t\tcount++;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!count)\n\t\t\t\treturn 9999;\n\t\t\tping /= count;\n\t\t}\n\t\treturn ping*1000;\n\tcase SCP_BAD:\n\tsafedefault:\n\t\tbreak;\n\t}\n\treturn 0;\n}\n\n/*\n===================\nSV_FullClientUpdate\n\nWrites all update values to client. use to=NULL to broadcast.\n===================\n*/\nvoid SV_FullClientUpdate (client_t *client, client_t *to)\n{\n\tint\t\ti;\n\tchar\tinfo[EXTENDED_INFO_STRING];\n\tsizebuf_t *buf;\n\n\tif (!to)\n\t{\n\t\tfor (i = 0; i < sv.allocated_client_slots; i++)\n\t\t{\n\t\t\tSV_FullClientUpdate(client, &svs.clients[i]);\n\t\t}\n#ifdef MVD_RECORDING\n\t\tif (sv.mvdrecording)\n\t\t\tSV_FullClientUpdate(client, &demo.recorder);\n#endif\n\t\treturn;\n\t}\n\n\tif (to->controller && to->controller != to)\n\t\treturn;\n\n\ti = client - svs.clients;\n\n\tif (i >= to->max_net_clients)\n\t\treturn;\t//most clients will crash if they see too high a player index. some even segfault.\n\n//Sys_Printf(\"SV_FullClientUpdate:  Updated frags for client %d\\n\", i);\n\n\tif (ISQWCLIENT(to))\n\t{\n\t\tfloat onservertime;\n\t\tunsigned int pext = to->fteprotocolextensions;\n\t\tint ping = SV_CalcPing (client, false);\n\t\tif (ping > 0xffff)\n\t\t\tping = 0xffff;\n\n\t\tbuf = ClientReliable_StartWrite(to, 4);\n\t\t\tMSG_WriteByte(buf, svc_updatefrags);\n\t\t\tMSG_WriteByte(buf, i);\n\t\t\tMSG_WriteShort(buf, client->old_frags);\n\t\tClientReliable_FinishWrite(to);\n\n\t\tbuf = ClientReliable_StartWrite(to, 4);\n\t\t\tMSG_WriteByte(buf, svc_updateping);\n\t\t\tMSG_WriteByte(buf, i);\n\t\t\tMSG_WriteShort(buf, ping);\n\t\tClientReliable_FinishWrite(to);\n\n\t\tbuf = ClientReliable_StartWrite(to, 3);\n\t\t\tMSG_WriteByte(buf, svc_updatepl);\n\t\t\tMSG_WriteByte(buf, i);\n\t\t\tMSG_WriteByte(buf, client->lossage);\n\t\tClientReliable_FinishWrite(to);\n\n\t\tonservertime = realtime - client->connection_started;\n\t\tif (onservertime > sv.time)\n\t\t\tonservertime = sv.time;\n\t\tbuf = ClientReliable_StartWrite(to, 6);\n\t\t\tMSG_WriteByte(buf, svc_updateentertime);\n\t\t\tMSG_WriteByte(buf, i);\n\t\t\tMSG_WriteFloat(buf, onservertime);\n\t\tClientReliable_FinishWrite(to);\n\n\t\tInfoBuf_ToString(&client->userinfo, info, (pext&PEXT_BIGUSERINFOS)?BASIC_INFO_STRING:sizeof(info), basicuserinfos, privateuserinfos, (pext&PEXT_BIGUSERINFOS)?NULL:basicuserinfos, &to->infosync, &client->userinfo);\n\t\tbuf = ClientReliable_StartWrite(to, 7 + strlen(info));\n\t\t\tMSG_WriteByte(buf, svc_updateuserinfo);\n\t\t\tMSG_WriteByte(buf, i);\n\t\t\tMSG_WriteLong(buf, client->userid);\n\t\t\tMSG_WriteString(buf, info);\n\t\tClientReliable_FinishWrite(to);\n\t}\n\telse if (ISNQCLIENT(to))\n\t{\n\t\tint top, bottom, playercolor;\n\t\tchar *nam = InfoBuf_ValueForKey(&client->userinfo, \"name\");\n\n\t\tbuf = ClientReliable_StartWrite(to, 4);\n\t\t\tMSG_WriteByte(buf, svc_updatefrags);\n\t\t\tMSG_WriteByte(buf, i);\n\t\t\tMSG_WriteShort(buf, client->old_frags);\n\t\tClientReliable_FinishWrite(to);\n\n\t\tbuf = ClientReliable_StartWrite(to, 3 + strlen(nam));\n\t\t\tMSG_WriteByte(buf, svc_updatename);\n\t\t\tMSG_WriteByte(buf, i);\n\t\t\tMSG_WriteString(buf, nam);\n\t\tClientReliable_FinishWrite(to);\n\n\n\t\ttop = atoi(InfoBuf_ValueForKey(&client->userinfo, \"topcolor\"));\n\t\tbottom = atoi(InfoBuf_ValueForKey(&client->userinfo, \"bottomcolor\"));\n\t\ttop &= 15;\n\t\tif (top > 13)\n\t\t\ttop = 13;\n\t\tbottom &= 15;\n\t\tif (bottom > 13)\n\t\t\tbottom = 13;\n\t\tplayercolor = top*16 + bottom;\n\n\t\tbuf = ClientReliable_StartWrite(to, 3);\n\t\t\tMSG_WriteByte(buf, svc_updatecolors);\n\t\t\tMSG_WriteByte(buf, i);\n\t\t\tMSG_WriteByte(buf, playercolor);\n\t\tClientReliable_FinishWrite(to);\n\n\t\tif (to->qex)\n\t\t{\n\t\t\tunsigned int s1, s2;\n\t\t\tif (client->netchan.remote_address.type == NA_LOOPBACK)\n\t\t\t\ts1 = s2 = 0;\t//host\n\t\t\telse\n\t\t\t\ts1 = s2 = -1;\t//non-playfab connection\n\t\t\tMSG_WriteByte(buf, svcqex_updatesocial);\n\t\t\tMSG_WriteByte(buf, i);\n\t\t\tMSG_WriteLong(buf, s1);\n\t\t\tMSG_WriteLong(buf, s2);\n\t\t}\n\n\t\tif (to->fteprotocolextensions2 & PEXT2_PREDINFO)\n\t\t{\n\t\t\tchar *s;\n\t\t\tInfoBuf_ToString(&client->userinfo, info, sizeof(info), basicuserinfos, privateuserinfos, NULL, NULL, NULL);\n\t\t\ts = va(\"//fui %i \\\"%s\\\"\\n\", i, info);\n\t\t\tbuf = ClientReliable_StartWrite(to, 2 + strlen(s));\n\t\t\t\tClientReliableWrite_Begin(to, svc_stufftext, 2+strlen(s));\n\t\t\t\tClientReliableWrite_String(to, s);\n\t\t\tClientReliable_FinishWrite(to);\n\t\t}\n\t}\n}\n\n/*\n==============================================================================\n\nCONNECTIONLESS COMMANDS\n\n==============================================================================\n*/\n\nconst char *SV_ProtocolNameForClient(client_t *cl)\n{\n\t//okay, that failed...\n\tsafeswitch (cl->protocol)\n\t{\n\tcase SCP_QUAKEWORLD:\n\t\tif (cl->fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS)\n\t\t\treturn \"fteqw\";\t//changes enough to be significant. assumed to include csqc.\n\t\treturn \"qw\";\n\tcase SCP_BAD:\n\t\treturn \"bot\";\n\tcase SCP_QUAKE2:\n\t\treturn \"q2\";\n\tcase SCP_QUAKE2EX:\n\t\treturn \"q2ex\";\n\tcase SCP_QUAKE3:\n\t\treturn \"q3\";\n\tcase SCP_NETQUAKE:\n\t\tif (cl->fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS)\n\t\t\treturn \"ftenq\";\t//changes enough to be significant. assumed to include csqc.\n\t\tif (cl->qex)\n\t\t\treturn \"qex\";\n\t\tif (cl->proquake_angles_hack)\n\t\t\treturn \"pq\";\n\t\treturn \"nq\";\n\tcase SCP_BJP3:\n\t\treturn \"bjp3\";\n\tcase SCP_FITZ666:\n\t\t//this gets messy... probably we should distinguish more\n\t\tif (cl->fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS)\n\t\t\treturn \"ftenq\";\t//changes enough to be significant. assumed to include csqc.\n\t\tif (cl->qex)\n\t\t\treturn \"qex\";\n\t\tif (cl->netchan.netprim.coordtype != COORDTYPE_FIXED_13_3 || cl->netchan.netprim.anglesize != 1)\n\t\t\treturn \"rmq\";\t//while fte tends not to care, most people consider them separate.\n\t\treturn \"fitz\";\n\tcase SCP_DARKPLACES6:\n\t\treturn \"dp6\";\n\tcase SCP_DARKPLACES7:\n\t\treturn \"dp7\";\n\tsafedefault:\n\t\treturn \"unk\";\n\t}\n}\n\nchar *SV_PlayerPublicAddress(client_t *cl)\n{\t//returns a string containing the client's IP address, as permitted for viewing by other clients.\n\t//if something useful is actually returned, it should be masked.\n\t//we hide it entirely out of private info caution. most nq clients expect a #.#.#.INVALID type address.\n\t//it should be fine to put other stuff here though, we put client version instead, if we know it.\n\tconst char *ver = InfoBuf_ValueForKey(&cl->userinfo, \"*ver\");\n\tconst char *prot = SV_ProtocolNameForClient(cl);\n\n\treturn va(\"prot %s %s\", prot, ver);\t//something so they can't confuse ip parsing so easily nor pass them off as some other protocol.\n}\n\n/*\n================\nSVC_Status\n\nResponds with all the info that qplug or qspy can see\nThis message can be up to around 5k with worst case string lengths.\n================\n*/\nstatic void SVC_Status (void)\n{\n\tint displayflags;\n\tint\t\ti;\n\tclient_t\t*cl;\n\tchar *name;\n\tint\t\tping;\n\tint\t\ttop, bottom;\n\tchar frags[64];\n\tchar *skin, *team, *botpre, *specpre;\n\tchar junk[512];\n\tint jlen;\n\n\tint slots=0;\n\n\tdisplayflags = atoi(Cmd_Argv(1));\n\tif (displayflags == STATUS_OLDSTYLE)\n\t\tdisplayflags = STATUS_SERVERINFO|STATUS_PLAYERS;\n\n\tCmd_TokenizeString (\"status\", false, false);\n\tSV_BeginRedirect (RD_PACKET, TL_FindLanguage(\"\"));\n\tif (displayflags&STATUS_SERVERINFO)\n\t{\n\t\tchar infostr[1024];\t//FIXME: vanilla limit is 512. we should probably have a list of known cvars for lower priority sending.\n\t\tconst char *ignorekeys[] = {\"mapname\", \"*z_ext\", NULL};\t//ignore some pointless stuff\n\t\tconst char *prioritykeys[] = {\"hostname\", \"admin\", \"*gamedir\", \"*version\", \"deathmatch\", \"timelimit\", \"fraglimit\", \"maxclients\", \"maxspectators\", \"status\", NULL}; //make sure we include these before we start overflowing\n\t\tInfoBuf_ToString(&svs.info, infostr, sizeof(infostr), prioritykeys, ignorekeys, NULL, NULL, NULL);\n\t\tCon_Printf (\"%s\\n\", infostr);\n\t}\n\tfor (i=0 ; i<svs.allocated_client_slots ; i++)\n\t{\n\t\tcl = &svs.clients[i];\n\t\tif ((cl->state == cs_connected || cl->state == cs_spawned || cl->name[0]) && ((cl->spectator && displayflags&STATUS_SPECTATORS) || (!cl->spectator && displayflags&STATUS_PLAYERS)))\n\t\t{\n\t\t\ttop = atoi(InfoBuf_ValueForKey (&cl->userinfo, \"topcolor\"));\n\t\t\tbottom = atoi(InfoBuf_ValueForKey (&cl->userinfo, \"bottomcolor\"));\n\t\t\ttop = (top < 0) ? 0 : ((top > 13) ? 13 : top);\n\t\t\tbottom = (bottom < 0) ? 0 : ((bottom > 13) ? 13 : bottom);\n\t\t\tping = SV_CalcPing (cl, false);\n\t\t\tname = cl->name;\n\n\t\t\tskin = InfoBuf_ValueForKey (&cl->userinfo, \"skin\");\n\t\t\tteam = InfoBuf_ValueForKey (&cl->userinfo, \"team\");\n\n\t\t\tif (!cl->state || cl->protocol == SCP_BAD)\t//show bots differently. Just to be courteous.\n\t\t\t\tbotpre = \"BOT:\";\n\t\t\telse\n\t\t\t\tbotpre = \"\";\n\n\t\t\tspecpre = \"\";\n\t\t\tif (cl->spectator)\n\t\t\t{\t//silly mvdsv stuff\n\t\t\t\tif (displayflags & STATUS_SPECTATORS_AS_PLAYERS)\n\t\t\t\t{\n\t\t\t\t\tfrags[0] = 'S';\n\t\t\t\t\tfrags[1] = '\\0';\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tping = -ping;\n\t\t\t\t\tsprintf(frags, \"%i\", -9999);\n\t\t\t\t\tspecpre = \"\\\\s\\\\\";\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t\tsprintf(frags, \"%i\", cl->old_frags);\n\n\t\t\tjunk[jlen = 0] = 0;\n\t\t\tif ((displayflags & STATUS_SHOWTEAMS) && jlen+4<sizeof(junk))\n\t\t\t{\n\t\t\t\tjunk[jlen++] = ' ';\n\t\t\t\tjlen += strlen(COM_QuotedString(team, junk+jlen, sizeof(junk)-jlen, false));\n\t\t\t}\n\t\t\tif ((displayflags & STATUS_LOGININFO) && jlen+4<sizeof(junk))\n\t\t\t{\n\t\t\t\tjunk[jlen++] = ' ';\n\t\t\t\tjlen += strlen(COM_QuotedString(\"\", junk+jlen, sizeof(junk)-jlen, false));\n\t\t\t}\n\n\t\t\tCon_Printf (\"%i %s %i %i \\\"%s%s%s\\\" \\\"%s\\\" %i %i%s\\n\", cl->userid,\n\t\t\t\t\tfrags, (int)(realtime - cl->connection_started)/60,\n\t\t\t\t\tping, specpre, botpre, name, skin, top, bottom, junk);\n\t\t}\n\t\telse\n\t\t\tslots++;\n\t}\n#ifdef MVD_RECORDING\n\tif (displayflags & STATUS_QTVLIST)\n\t{\n\t\tstruct mvddest_s *d;\n\t\tfor (d = demo.dest; d; d = d->nextdest)\n\t\t{\n\t\t\tif (d->desttype == DEST_STREAM)\n\t\t\t\tCon_Printf(\"qtv %d \\\"%s\\\" \\\"%s\\\" %d\\n\", d->id, d->simplename, d->filename, 0/*d->viewercount*/);\n\t\t}\n\t}\n#endif\n\n\tSV_EndRedirect ();\n}\n\n#if 1//def NQPROT\nconst char *SV_GetProtocolVersionString(void)\n{\n\tchar *ret = va(\"%i\", com_protocolversion.ival);\t//for compat with DP, this is basically locked at 3. our pexts allow this to be mostly graceful.\n\n\tswitch(svs.gametype)\n\t{\n\tcase GT_PROGS:\n\tcase GT_Q1QVM:\n\t\tif (sv_listen_qw.ival)\n\t\t\tQ_strncatz(ret, \"w\", 64);\n#ifdef NQPROT\n\t\tif (progstype == PROG_H2)\n\t\t\tbreak;\t//don't advertise nq protocols when they're blocked.\n\t\tif (sv_listen_nq.ival)\n\t\t{\n\t\t\tQ_strncatz(ret, \"n\", 64);\n#ifdef HAVE_DTLS\n\t\t\tif (*dtls_psk_user.string)\n\t\t\t\tQ_strncatz(ret, \"x\", 64);\n#endif\n\t\t}\n\t\tif (sv_listen_dp.ival)\n\t\t\tQ_strncatz(ret, \"d\", 64);\n#endif\n\t\tbreak;\n\tdefault:\n\t\tbreak;\t//these do their own thing, with their own protocols. don't be weird.\n\t}\n\treturn ret;\n}\n\nvoid SV_GeneratePublicServerinfo(char *info, const char *endinfo)\n{\n\tchar *resp = info;\n\tconst char *ignorekeys[] = {\n\t\t\"maxclients\", \"map\", \"*gamedir\", \"*z_ext\",\t//this is a DP protocol query, so some QW fields are not needed\n\t\t\"gamename\", \"modname\", \"protocol\", \"clients\", \"sv_maxclients\", \"mapname\", \"qcstatus\", \"challenge\", NULL};\t//and we need to add some\n\tconst char *prioritykeys[] = {\"hostname\", NULL}; //make sure we include these before we start overflowing\n\tchar protocolname[64];\n\tconst char *gamestatus;\n\n\textern cvar_t maxclients;\n\tint i;\n\tclient_t *cl;\n\tint numclients = 0;\n\tfor (i=0 ; i<svs.allocated_client_slots ; i++)\n\t{\n\t\tcl = &svs.clients[i];\n\t\tif ((cl->state == cs_connected || cl->state == cs_spawned || cl->name[0]) && !cl->spectator)\n\t\t\tnumclients++;\n\t}\n\n\t//first line contains the serverinfo, or some form of it\n\tCOM_ParseOut(com_protocolname.string, protocolname, sizeof(protocolname));\t//we can only report one, so report the first.\n\tif (svprogfuncs)\n\t{\n\t\teval_t *v = PR_FindGlobal(svprogfuncs, \"worldstatus\", PR_ANY, NULL);\n\t\tif (v)\n\t\t\tgamestatus = PR_GetString(svprogfuncs, v->string);\n\t\telse\n\t\t\tgamestatus = \"\";\n\t}\n\telse\n\t\tgamestatus = \"\";\n\n\t*resp = 0;\n\tInfo_SetValueForKey(resp, \"gamename\", protocolname, endinfo - resp);//distinguishes it from other types of games\n\tInfo_SetValueForKey(resp, \"protocol\", SV_GetProtocolVersionString(), endinfo - resp);\n\tInfo_SetValueForKey(resp, \"modname\", FS_GetGamedir(true), endinfo - resp);\n\tInfo_SetValueForKey(resp, \"clients\", va(\"%d\", numclients), endinfo - resp);\n\tInfo_SetValueForKey(resp, \"sv_maxclients\", maxclients.string, endinfo - resp);\n\tInfo_SetValueForKey(resp, \"mapname\", InfoBuf_ValueForKey(&svs.info, \"map\"), endinfo - resp);\n\tresp += strlen(resp);\n\t//now include the full/regular serverinfo\n\tresp += InfoBuf_ToString(&svs.info, resp, endinfo - resp, prioritykeys, ignorekeys, NULL, NULL, NULL);\n\t*resp = 0;\n\t//and any possibly-long qc status string\n\tif (*gamestatus)\n\t\tInfo_SetValueForKey(resp, \"qcstatus\", gamestatus, endinfo - resp);\n\tresp += strlen(resp);\n\t*resp++ = 0;\n}\n\nstatic void SVC_GetInfo (const char *challenge, int fullstatus)\n{\n\t//dpmaster support\n\tchar response[MAX_UDP_PACKET];\n\tint i;\n\tchar *resp;\n\n\tresp = response;\n\n\t//response packet header\n\t*resp++ = 0xff;\n\t*resp++ = 0xff;\n\t*resp++ = 0xff;\n\t*resp++ = 0xff;\n\tif (fullstatus)\n\t\tQ_strncpyz(resp, \"statusResponse\", sizeof(response) - (resp-response) - 1);\n\telse\n\t\tQ_strncpyz(resp, \"infoResponse\", sizeof(response) - (resp-response) - 1);\n\tresp += strlen(resp);\n\t*resp++ = '\\n';\n\n\tSV_GeneratePublicServerinfo(resp, response+sizeof(response));\n\n\tif (fullstatus)\n\t{\n\t\tclient_t *cl;\n\t\tchar *start = resp;\n\n\t\tif (resp != response+sizeof(response))\n\t\t{\n\t\t\tresp[-1] = '\\n';\t//replace the null terminator that we already wrote\n\n\t\t\t//on the following lines we have an entry for each client\n\t\t\tfor (i=0 ; i<svs.allocated_client_slots ; i++)\n\t\t\t{\n\t\t\t\tcl = &svs.clients[i];\n\t\t\t\tif ((cl->state == cs_connected || cl->state == cs_spawned || cl->name[0]) && !cl->spectator)\n\t\t\t\t{\n\t\t\t\t\tQ_strncpyz(resp, va(\n\t\t\t\t\t\t\t\t\t\"%d %d \\\"%s\\\" \\\"%s\\\"\\n\"\n\t\t\t\t\t\t\t\t\t,\n\t\t\t\t\t\t\t\t\tcl->old_frags,\n\t\t\t\t\t\t\t\t\tSV_CalcPing(cl, false),\n\t\t\t\t\t\t\t\t\tcl->team,\n\t\t\t\t\t\t\t\t\tcl->name\n\t\t\t\t\t\t\t\t\t), sizeof(response) - (resp-response));\n\t\t\t\t\tresp += strlen(resp);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t*resp++ = 0;\t//this might not be a null\n\t\t\tif (resp == response+sizeof(response))\n\t\t\t{\n\t\t\t\t//we're at the end of the buffer, it's full. bummer\n\t\t\t\t//replace 12 bytes with infoResponse\n\t\t\t\tmemcpy(response+4, \"infoResponse\", 12);\n\t\t\t\t//move down by len(statusResponse)-len(infoResponse) bytes\n\t\t\t\tmemmove(response+4+12, response+4+14, resp-response-(4+14));\n\t\t\t\tstart -= 14-12; //fix this pointer\n\n\t\t\t\tresp = start;\n\t\t\t\tresp[-1] = 0;\t//reset the \\n\n\t\t\t}\n\t\t}\n\t}\n\n\tNET_SendPacket (svs.sockets, resp-response, response, &net_from);\n}\n#endif\n\n#ifdef Q2SERVER\nstatic void SVC_InfoQ2 (void)\n{\n\tchar\tstring[128];\n\tint\t\ti, count;\n\tint\t\tversion;\n\n\tif (maxclients.value == 1)\n\t\treturn;\t\t// ignore in single player\n\n\tversion = atoi (Cmd_Argv(1));\n\n\tif (version != PROTOCOL_VERSION_Q2)\n\t\tsnprintf (string, sizeof(string), \"%s: wrong version\\n\", hostname.string);\n\telse\n\t{\n\t\tcount = 0;\n\t\tfor (i=0 ; i<svs.allocated_client_slots ; i++)\n\t\t\tif (svs.clients[i].state >= cs_connected)\n\t\t\t\tcount++;\n\n\t\tsnprintf (string, sizeof(string), \"%16s %8s %2i/%2i\\n\", hostname.string, svs.name, count, (int)maxclients.value);\n\t}\n\n\tNetchan_OutOfBandPrint (NCF_SERVER, &net_from, \"info\\n%s\", string);\n}\n#endif\n\n#ifdef MVD_RECORDING\nstatic void SVC_QTVUsers (void)\n{\n}\n#endif\n\n/*\n===================\nSV_CheckLog\n\n===================\n*/\n#define\tLOG_FLUSH\t\t10*60\nstatic void SV_CheckLog (void)\n{\n\tsizebuf_t\t*sz;\n\n\tsz = &svs.log[svs.logsequence&(FRAGLOG_BUFFERS-1)];\n\n\t// bump sequence ten minutes have passed and\n\t// there is something still sitting there\n\t//logfrag does the rotation for a full log.\n\tif (realtime - svs.logtime > LOG_FLUSH && sz->cursize)\n\t{\n\t\t// swap buffers and bump sequence\n\t\tsvs.logtime = realtime;\n\t\tsvs.logsequence++;\n\t\tsz = &svs.log[svs.logsequence&(FRAGLOG_BUFFERS-1)];\n\t\tsz->cursize = 0;\n\t\tCon_TPrintf (\"beginning fraglog sequence %i\\n\", svs.logsequence);\n\t}\n\n}\n\n/*\n================\nSVC_Log\n\nResponds with all the logged frags for ranking programs.\nIf a sequence number is passed as a parameter and it is\nthe same as the current sequence, an A2A_NACK will be returned\ninstead of the data.\n================\n*/\nstatic void SVC_Log (void)\n{\n\tunsigned int\tseq;\n\tchar\tdata[MAX_DATAGRAM+64];\n\tchar\tadr[MAX_ADR_SIZE];\n\tchar *av;\n\n\tav = Cmd_Argv(1);\n\tif (*av)\n\t{\n\t\tseq = strtoul(av, NULL, 0);\n\t\t//seq is the last one that the client already has\n\n\t\tif (seq < svs.logsequence-(FRAGLOG_BUFFERS-1))\n\t\t\tseq = svs.logsequence-(FRAGLOG_BUFFERS-1);\t//send them this sequence\n\t\telse if (seq == svs.logsequence)\n\t\t{\t//current log isn't available as its not complete yet.\n\t\t\tdata[0] = A2A_NACK;\n\t\t\tNET_SendPacket (svs.sockets, 1, data, &net_from);\n\t\t\treturn;\n\t\t}\n\t\telse if (seq > svs.logsequence)\t//future logs are not valid either. reply with the last that was. this is for compat, just in case.\n\t\t\tseq = svs.logsequence-1;\n\t\telse\n\t\t\tseq = seq+1;\t//they will get the next sequence from the one they already have\n\t}\n\telse\n\t\tseq = svs.logsequence-1;\n\n\tif (!fraglog_public.ival)\n\t{\t//frag logs are not public (for DoS protection perhaps?)\n\t\tdata[0] = A2A_NACK;\n\t\tNET_SendPacket (svs.sockets, 1, data, &net_from);\n\t\treturn;\n\t}\n\n\tCon_DPrintf (\"sending log %i to %s\\n\", seq, NET_AdrToString(adr, sizeof(adr), &net_from));\n\n\t//cookie support, to avoid spoofing\n\tav = Cmd_Argv(2);\n\tif (*av)\n\t\tQ_snprintfz(data, sizeof(data), \"stdlog %i %s\\n%s\", seq, av, (char *)svs.log_buf[seq&(FRAGLOG_BUFFERS-1)]);\n\telse\n\t\tQ_snprintfz(data, sizeof(data), \"stdlog %i\\n%s\", seq, (char *)svs.log_buf[seq&(FRAGLOG_BUFFERS-1)]);\n\tNET_SendPacket (svs.sockets, strlen(data)+1, data, &net_from);\n}\n\n/*\n================\nSVC_Ping\n\nJust responds with an acknowledgement\n================\n*/\nstatic void SVC_Ping (void)\n{\n\tchar\tdata;\n\n\tdata = A2A_ACK;\n\n\tNET_SendPacket (svs.sockets, 1, &data, &net_from);\n}\n\n//from net_from\nint SV_NewChallenge (void)\n{\n\tint\t\ti;\n\tint\t\toldest;\n\tint\t\toldestTime;\n\n\toldest = 0;\n\toldestTime = 0x7fffffff;\n\n\t// see if we already have a challenge for this ip\n\tfor (i = 0 ; i < MAX_CHALLENGES ; i++)\n\t{\n\t\tif (NET_CompareBaseAdr (&net_from, &svs.challenges[i].adr))\n\t\t{\n\t\t\tsvs.challenges[i].time = realtime;\n\t\t\treturn svs.challenges[i].challenge;\n\t\t}\n\t\tif (svs.challenges[i].time < oldestTime)\n\t\t{\n\t\t\toldestTime = svs.challenges[i].time;\n\t\t\toldest = i;\n\t\t}\n\t}\n\n\t// overwrite the oldest\n\tsvs.challenges[oldest].challenge = (rand() << 16) ^ rand();\n\tsvs.challenges[oldest].adr = net_from;\n\tsvs.challenges[oldest].time = realtime;\n\n\treturn svs.challenges[oldest].challenge;\n}\n\n/*\n=================\nSVC_GetChallenge\n\nReturns a challenge number that can be used\nin a subsequent client_connect command.\nWe do this to prevent denial of service attacks that\nflood the server with invalid connection IPs.  With a\nchallenge, they must give a valid IP address.\n=================\n*/\nqboolean SVC_GetChallenge (qboolean respond_dp)\n{\n#ifdef HUFFNETWORK\n\tint compressioncrc;\n#endif\n\tint\t\tchallenge;\n\tchar *buf;\n\tint lng;\n\tchar *over;\n\n\tqboolean respond_std = true;\n#ifdef QWOVERQ3\n\tqboolean respond_qwoverq3 = true;\n\trespond_qwoverq3 &= !!sv_listen_q3.value;\n#else\n\tconst qboolean respond_qwoverq3 = false;\n#endif\n\n\tint ioq3clchallenge = atoi(Cmd_Argv(1));\n\tconst char *protocols = Cmd_Argv(2);\n\tif (*protocols)\n\t{\n\t\tconst char *pname;\n\t\tchar tprot[64], oprot[64];\n\t\twhile ((protocols=COM_ParseOut(protocols, tprot,sizeof(tprot))))\n\t\t{\n\t\t\tpname = com_protocolname.string;\n\t\t\twhile ((pname=COM_ParseOut(pname, oprot,sizeof(oprot))))\n\t\t\t{\n\t\t\t\tif (!strcmp(tprot, oprot))\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (pname)\n\t\t\t\tbreak;\n\t\t}\n\n\t\tif (!protocols)\n\t\t{\n\t\t\tCOM_ParseOut(Cmd_Argv(2), tprot,sizeof(tprot));\n\t\t\tCOM_ParseOut(com_protocolname.string, oprot,sizeof(oprot));\n\t\t\tpname = va(\"print\\nGame mismatch: This is a %s server but you are using %s\\n\", oprot, tprot);\n\t\t\tNetchan_OutOfBand(NCF_SERVER, &net_from, strlen(pname), pname);\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tif (sv_listen_qw.value && !sv_listen_dp.value)\n\t{\n\t\trespond_std = true;\n\t\trespond_dp = false;\n\t}\n\telse if (sv_listen_dp.value && !sv_listen_qw.value)\n\t{\n\t\trespond_std = false;\n\t\trespond_dp = true;\n\t}\n\telse\n\t{\n\t\trespond_std &= !!sv_listen_qw.value;\n\t\trespond_dp &= !!sv_listen_dp.value;\n\t}\n\n\tif (progstype == PROG_H2)\n\t\trespond_dp = false;\t//don't bother. dp doesn't support the maps anyway.\n\t//dp's connections result in race conditions or are ambiguous in certain regards\n\t//race: dp vs nq.\n\t//\t\tthe dp request will generally arrive first. we check if there was a recent challenge requested, and inhibit the nq response, ensuring that dp clients connect with a known protocol\n\t//race: dp vs qw.\n\t//\t\tDP clients will just bindly respond to both with a connection request. sending the dp one usually means the server will see the dp connection request first\n\t//\t\tFTE clients explicitly ignore dp challenges with the specific 'FTE' prefix so you get qw connections there.\n\t//conflict: dp vs q2. dp challenge responses USUALLY contain letters. vanilla q2 is always a 32bit int. FTE clients will check that before sending an appropriate response.\n\t//so:\n\t//\t\tvanilla nq doesn't send getchallenge, its nq connect is not inhibited, and connects directly (we optionally hack a challenge over stuffcmds, as well as protocol extensions).\n\t//\t\tdp gets a dp+qw challenge, its nq request is ignored due to packet ordering and a small timeout, the server sees the dp connection request first and ignores the qw connect.\n\t//\t\tfte's nq request is treated as a getchallenge. fte clients ignore the dp challenge response (if qw protocols are still enabled). ends up with a qw/fte connection\n\tif (!(sv_listen_nq.value || sv_bigcoords.value || !respond_std))\n\t\trespond_dp = false;\n\n#ifdef QWOVERQ3\n\tif (svs.gametype != GT_PROGS && svs.gametype != GT_Q1QVM)\n\t\trespond_qwoverq3 = false;\t//should probably just nuke this feature.\n#endif\n\n\tif (!respond_std && !respond_dp && !respond_qwoverq3)\n\t\treturn false;\n\n\tif (svs.gametype != GT_PROGS && svs.gametype != GT_Q1QVM)\n\t{\t//if we're running q2 or q3, just ignore the whole DP thing. its irrelevent in those game modes.\n\t\trespond_std |= true;\n#ifdef QWOVERQ3\n\t\trespond_qwoverq3 = false;\n#endif\n\t\trespond_dp = false;\n\t}\n\tif (respond_dp)\n\t\trespond_std = false;\n\n\tchallenge = SV_NewChallenge();\n\n\t//different game modes require different types of responses\n\tswitch(svs.gametype)\n\t{\n#ifdef Q3SERVER\n\tcase GT_QUAKE3:\t//q3 servers\n\t\tbuf = va(\"challengeResponse %i %i %i\", challenge, ioq3clchallenge, com_protocolversion.ival);\n\t\tbreak;\n#endif\n#ifdef Q2SERVER\n\tcase GT_QUAKE2:\n\t\tbuf = va(\"challenge %i p=\"STRINGIFY(PROTOCOL_VERSION_Q2EX), challenge);\t//quake 2 servers give a different challenge response\n\t\tbreak;\n#endif\n\tdefault:\n\t\tbuf = va(\"%c%i\", S2C_CHALLENGE, challenge);\t//quakeworld's response is a bit poo.\n\t\tbreak;\n\t}\n\n\tover = buf + strlen(buf) + 1;\n\n\tif (svs.gametype == GT_PROGS || svs.gametype == GT_Q1QVM)\n\t{\n\t\tunsigned int mask;\n\t\t//tell the client what fte extensions we support\n\t\tmask = Net_PextMask(PROTOCOL_VERSION_FTE1, false)&PEXT_SERVERADVERTISE;\n\t\tif (mask)\n\t\t{\n\t\t\tlng = LittleLong(PROTOCOL_VERSION_FTE1);\n\t\t\tmemcpy(over, &lng, sizeof(lng));\n\t\t\tover+=sizeof(lng);\n\n\t\t\tlng = LittleLong(mask);\n\t\t\tmemcpy(over, &lng, sizeof(lng));\n\t\t\tover+=sizeof(lng);\n\t\t}\n\t\t//tell the client what fte extensions we support\n\t\tmask = Net_PextMask(PROTOCOL_VERSION_FTE2, false)&PEXT2_SERVERADVERTISE;\n\t\tif (mask)\n\t\t{\n\t\t\tlng = LittleLong(PROTOCOL_VERSION_FTE2);\n\t\t\tmemcpy(over, &lng, sizeof(lng));\n\t\t\tover+=sizeof(lng);\n\n\t\t\tlng = LittleLong(mask);\n\t\t\tmemcpy(over, &lng, sizeof(lng));\n\t\t\tover+=sizeof(lng);\n\t\t}\n\t\t//tell the client what mvdsv/ezquake extensions we support\n\t\tmask = Net_PextMask(PROTOCOL_VERSION_EZQUAKE1, false)&EZPEXT1_SERVERADVERTISE;\n\t\tif (mask)\n\t\t{\n\t\t\tlng = LittleLong(PROTOCOL_VERSION_EZQUAKE1);\n\t\t\tmemcpy(over, &lng, sizeof(lng));\n\t\t\tover+=sizeof(lng);\n\n\t\t\tlng = LittleLong(mask);\n\t\t\tmemcpy(over, &lng, sizeof(lng));\n\t\t\tover+=sizeof(lng);\n\t\t}\n\t}\n\tif (svs.gametype == GT_PROGS || svs.gametype == GT_Q1QVM || svs.gametype == GT_QUAKE2)\n\t{\n\t\tunsigned int mask;\n\t\t//report the mtu\n\t\tif (*net_mtu.string)\n\t\t\tmask = net_mtu.ival&~7;\n\t\telse\n\t\t\tmask = 8192;\n\t\tif (mask > 64)\n\t\t{\n\t\t\tlng = LittleLong(PROTOCOL_VERSION_FRAGMENT);\n\t\t\tmemcpy(over, &lng, sizeof(lng));\n\t\t\tover+=sizeof(lng);\n\n\t\t\tlng = LittleLong(mask);\n\t\t\tmemcpy(over, &lng, sizeof(lng));\n\t\t\tover+=sizeof(lng);\n\t\t}\n\n#ifdef HUFFNETWORK\n\t\tcompressioncrc = Huff_PreferedCompressionCRC();\n\t\tif (compressioncrc)\n\t\t{\n\t\t\tlng = LittleLong(PROTOCOL_VERSION_HUFFMAN);\n\t\t\tmemcpy(over, &lng, sizeof(lng));\n\t\t\tover+=sizeof(lng);\n\n\t\t\tlng = LittleLong(compressioncrc);\n\t\t\tmemcpy(over, &lng, sizeof(lng));\n\t\t\tover+=sizeof(lng);\n\t\t}\n#endif\n\n#ifdef HAVE_DTLS\n\t\tif (net_enable_dtls.ival>0/* || !*net_enable_dtls.string*/ && svs.sockets->dtlsfuncs)\n\t\t{\n\t\t\tlng = LittleLong(PROTOCOL_VERSION_DTLSUPGRADE);\n\t\t\tmemcpy(over, &lng, sizeof(lng));\n\t\t\tover+=sizeof(lng);\n\n\t\t\tif (net_enable_dtls.ival >= 3)\n\t\t\t\tlng = LittleLong(3);\t//required\n\t\t\telse if (net_enable_dtls.ival >= 2)\n\t\t\t\tlng = LittleLong(2);\t//encouraged\n\t\t\telse\n\t\t\t\tlng = LittleLong(1);\t//supported\n\t\t\tmemcpy(over, &lng, sizeof(lng));\n\t\t\tover+=sizeof(lng);\n\t\t}\n#endif\n\t\tif (*sv_guidhash.string\n#ifdef HAVE_DTLS\n\t\t\t&& (net_enable_dtls.ival < 3 || net_from.prot == NP_DTLS)\n#endif\n\t\t\t)\n\t\t{\n\t\t\tlng = LittleLong(PROTOCOL_VERSION_VARLENGTH);\n\t\t\tmemcpy(over, &lng, sizeof(lng));\n\t\t\tover+=sizeof(lng);\n\t\t\tlng = strlen(sv_guidhash.string);\n\t\t\tmemcpy(over, &lng, sizeof(lng));\n\t\t\tover+=sizeof(lng);\n\t\t\tlng = LittleLong(PROTOCOL_INFO_GUID);\n\t\t\tmemcpy(over, &lng, sizeof(lng));\n\t\t\tover+=sizeof(lng);\n\t\t\tmemcpy(over, sv_guidhash.string, strlen(sv_guidhash.string));\n\t\t\tover+=strlen(sv_guidhash.string);\n\t\t}\n\t}\n\n\tif (respond_dp)\n\t{\n\t\tchar *dp;\n\t\tif (sv_listen_qw.value)\n\t\t\tdp = va(\"challenge FTE%i\", challenge);\t//an FTE prefix will cause FTE clients to ignore the packet, to give preference to the qw challenge + protocols\n\t\telse\n\t\t\tdp = va(\"challenge %iDP\", challenge);\t//we still need to add a postfix to prevent it from being interpreted as a Q2 server\n\t\tNetchan_OutOfBand(NCF_SERVER, &net_from, strlen(dp)+1, dp);\n\t}\n\n\tif (respond_std)\n\t\tNetchan_OutOfBand(NCF_SERVER, &net_from, over-buf, buf);\n\n#ifdef QWOVERQ3\n\tif (svs.gametype == GT_PROGS || svs.gametype == GT_Q1QVM)\n\t{\n\t\tif (respond_qwoverq3)\n\t\t{\n\t\t\tbuf = va(\"challengeResponse %i\", challenge);\n\t\t\tNetchan_OutOfBand(NS_SERVER, &net_from, strlen(buf), buf);\n\t\t}\n\t}\n#endif\n\treturn true;\n}\n\n#ifdef SVRANKING\nstatic void VARGS SV_OutOfBandPrintf (int q2, netadr_t *adr, char *format, ...)\n{\n\tva_list\t\targptr;\n\tchar\t\tstring[8192];\n\n\tva_start (argptr, format);\n\tif (q2)\n\t{\n\t\tstrcpy(string, \"print\\n\");\n\t\tvsnprintf (string+6,sizeof(string)-1-6, format+1,argptr);\n\t}\n\telse\n\t{\n\t\tstring[0] = A2C_PRINT;\n\t\tstring[1] = '\\n';\n\t\tvsnprintf (string+2,sizeof(string)-1-2, format,argptr);\n\t}\n\tva_end (argptr);\n\n\n\tNetchan_OutOfBand (NCF_SERVER, adr, strlen(string), (qbyte *)string);\n}\nstatic void VARGS SV_OutOfBandTPrintf (int q2, netadr_t *adr, int language, translation_t text, ...)\n{\n\tva_list\t\targptr;\n\tchar\t\tstring[8192];\n\tconst char *format = langtext(text, language);\n\n\tva_start (argptr, text);\n\tif (q2)\n\t{\n\t\tstrcpy(string, \"print\\n\");\n\t\tvsnprintf (string+6,sizeof(string)-1-6, format+1,argptr);\n\t}\n\telse\n\t{\n\t\tstring[0] = A2C_PRINT;\n\t\tvsnprintf (string+1,sizeof(string)-1-1, format,argptr);\n\t}\n\tva_end (argptr);\n\n\n\tNetchan_OutOfBand (NCF_SERVER, adr, strlen(string), (qbyte *)string);\n}\n#endif\n\nqboolean SV_ChallengePasses(int challenge)\n{\n\tint i;\n\tfor (i=0 ; i<MAX_CHALLENGES ; i++)\n\t{\t//one per ip.\n\t\tif (NET_CompareBaseAdr (&net_from, &svs.challenges[i].adr))\n\t\t{\n\t\t\tif (challenge == svs.challenges[i].challenge)\n\t\t\t\treturn true;\n\t\t\treturn false;\n\t\t}\n\t}\n\treturn false;\n}\n\n#ifdef NQPROT\n//DP sends us a getchallenge followed by a CCREQ_CONNECT at about the same time.\n//this means that DP clients tend to connect as generic NQ clients.\n//and because DP _REQUIRES_ sv_bigcoords, they tend to end up being given fitz/rmq protocols\n//thus we don't respond to the connect if sv_listen_dp is 1, and we had a recent getchallenge request. recent is 2 secs.\nqboolean SV_ChallengeRecent(void)\n{\n\tint curtime = realtime;\t//yeah, evil. sue me. consitent with challenges.\n\tint i;\n\tfor (i=0 ; i<MAX_CHALLENGES ; i++)\n\t{\t//one per ip.\n\t\tif (NET_CompareBaseAdr (&net_from, &svs.challenges[i].adr))\n\t\t{\n\t\t\tif (svs.challenges[i].time > curtime - 2)\n\t\t\t\treturn true;\n\t\t}\n\t}\n\treturn false;\n}\n#endif\n\nvoid VARGS SV_RejectMessage(enum serverprotocols_e protocol, char *format, ...)\n{\n\tva_list\t\targptr;\n\tchar\t\tstring[8192];\n\tint len;\n\n\tva_start (argptr, format);\n\tswitch(protocol)\n\t{\n#ifdef NQPROT\n\tcase SCP_NETQUAKE:\n\tcase SCP_BJP3:\n\tcase SCP_FITZ666:\n\t\tstring[4] = CCREP_REJECT;\n\t\tvsnprintf (string+5,sizeof(string)-1-5, format,argptr);\n\t\tlen = strlen(string+4)+1+4;\n\t\t*(int*)string = BigLong(NETFLAG_CTL|len);\n\t\tNET_SendPacket(svs.sockets, len, string, &net_from);\n\t\treturn;\n\tcase SCP_DARKPLACES6:\n\tcase SCP_DARKPLACES7:\n\t\tstrcpy(string, \"reject \");\n\t\tvsnprintf (string+7,sizeof(string)-1-7, format,argptr);\n\t\tlen = strlen(string);\n\t\tbreak;\n#endif\n\n\tcase SCP_QUAKE2:\n\tcase SCP_QUAKE3:\n\tdefault:\n\t\tstrcpy(string, \"print\\n\");\n\t\tvsnprintf (string+6,sizeof(string)-1-6, format,argptr);\n\t\tlen = strlen(string);\n\t\tbreak;\n\n\tcase SCP_QUAKEWORLD:\n\t\tstring[0] = A2C_PRINT;\n\t\tstring[1] = '\\n';\n\t\tvsnprintf (string+2,sizeof(string)-1-2, format,argptr);\n\t\tlen = strlen(string);\n\t\tbreak;\n\t}\n\tva_end (argptr);\n\n\tNetchan_OutOfBand (NCF_SERVER, &net_from, len, (qbyte *)string);\n}\n\nvoid SV_AcceptMessage(client_t *newcl)\n{\n\tchar\t\tstring[8192];\n\tsizebuf_t\tsb;\n\tint len;\n#ifdef NQPROT\n\tnetadr_t localaddr;\n#endif\n\n\tmemset(&sb, 0, sizeof(sb));\n\tsb.maxsize = sizeof(string);\n\tsb.data = string;\n\n\tsafeswitch(newcl->protocol)\n\t{\n#ifdef NQPROT\n\tcase SCP_NETQUAKE:\n\tcase SCP_BJP3:\n\tcase SCP_FITZ666:\n//\t\tif (net_from.type != NA_LOOPBACK)\n\t\t{\n\t\t\tSZ_Clear(&sb);\n\t\t\tMSG_WriteLong(&sb, 0);\n\t\t\tMSG_WriteByte(&sb, CCREP_ACCEPT);\n\t\t\tif (newcl->qex)\n\t\t\t\t;\t//skip any port info (as well as any proquake ident stuff).\n\t\t\telse\n\t\t\t{\n\t\t\t\tNET_LocalAddressForRemote(svs.sockets, &net_from, &localaddr, 0);\n\t\t\t\tif (net_from.prot == NP_DTLS\n\t\t\t\t#ifdef SUPPORT_ICE\n\t\t\t\t\t\t|| net_from.type == NA_ICE\n\t\t\t\t#endif\n\t\t\t\t\t\t)\n\t\t\t\t\tMSG_WriteLong(&sb, 0);\t//send a port of 0 if we expect the client to be sane enough and/or otherwise problematic.\n\t\t\t\telse\n\t\t\t\t\tMSG_WriteLong(&sb, ShortSwap(localaddr.port));\n\t\t\t\tif (newcl->proquake_angles_hack)\n\t\t\t\t{\n\t\t\t\t\tMSG_WriteByte(&sb, MOD_PROQUAKE);\n\t\t\t\t\tMSG_WriteByte(&sb, MOD_PROQUAKE_VERSION);\n\t\t\t\t\tMSG_WriteByte(&sb, 0/*flags*/);\n\t\t\t\t}\n\t\t\t}\n\t\t\t*(int*)sb.data = BigLong(NETFLAG_CTL|sb.cursize);\n\t\t\tNET_SendPacket(svs.sockets, sb.cursize, sb.data, &net_from);\n\t\t\treturn;\n\t\t}\n\tcase SCP_DARKPLACES6:\n\tcase SCP_DARKPLACES7:\n\t\tstrcpy(string, \"accept\");\n\t\tlen = strlen(string);\n\t\tbreak;\n#else\n\tcase SCP_NETQUAKE:\n\tcase SCP_BJP3:\n\tcase SCP_FITZ666:\n\tcase SCP_DARKPLACES6:\n\tcase SCP_DARKPLACES7:\n\t\treturn;\n#endif\n\n\tcase SCP_QUAKE3:\n\t\tstrcpy(string, \"connectResponse\");\n\t\tlen = strlen(string);\n\t\tbreak;\n\tcase SCP_QUAKE2:\n\tcase SCP_QUAKE2EX:\n\t\tQ_snprintfz(string, sizeof(string), \"client_connect%s%s\\n\",\n\t\t\t(newcl->protocol==SCP_QUAKE2EX)?\" \"STRINGIFY(PROTOCOL_VERSION_Q2EX):\"\",\n\t\t\t(*fs_dlURL.string?va(\" dlserver=%s\", fs_dlURL.string):\"\")\t//q2pro's dlserver hint\n\t\t\t);\n\t\tlen = strlen(string);\n\t\tbreak;\n\n\tsafedefault:\n\tcase SCP_BAD:\n\tcase SCP_QUAKEWORLD:\n\t\tstring[0] = S2C_CONNECTION;\n\t\tstring[1] = '\\n';\n\t\tstring[2] = '\\0';\n\t\tlen = strlen(string);\n\t\tbreak;\n\t}\n\n\tNetchan_OutOfBand (NCF_SERVER, &net_from, len, (qbyte *)string);\n}\n\n#if !defined(_DEBUG) || defined(_WIN32) || defined(FTE_TARGET_WEB)\nstatic void SV_CheckRecentCrashes(client_t *tellclient)\n{\n}\n#else\n#include <sys/stat.h>\nstatic void SV_CheckRecentCrashes(client_t *tellclient)\n{\n\tstruct stat sb;\n\tif (-1 != stat(\"crash.log\", &sb))\n\t{\n\t\tif ((time(NULL) - sb.st_mtime) > 2*24*60*60)\n\t\t\treturn;\t//after 2 days, we stop advertising that we once crashed.\n\t\tSV_ClientPrintf(tellclient, PRINT_HIGH, \"\\1WARNING: crash.log exists, dated %s\\n\", ctime(&sb.st_mtime));\n\t}\n}\n#endif\n\n\nvoid SV_ClientProtocolExtensionsChanged(client_t *client)\n{\n\tint i;\n\tint maxpacketentities;\n\textern cvar_t pr_maxedicts;\n\tclient_t *seat;\n\n\textern cvar_t sv_protocol;\n\tchar *s = sv_protocol.string;\n\twhile ((s = COM_Parse(s)))\n\t{\n\t\tif (!strcasecmp(com_token, \"fte2\"))\n\t\t{\t//fancy stuff\n\t\t\tclient->fteprotocolextensions\n\t\t\t\t\t|= PEXT_CSQC\t\t\t\t/*mods break without*/\n\t\t\t\t\t | PEXT_CHUNKEDDOWNLOADS\t/*much faster downloads+redirects*/\n\t\t\t\t\t ;\n\t\t\tclient->fteprotocolextensions2\n\t\t\t\t\t|= PEXT2_PRYDONCURSOR\t\t/*mods might break without*/\n//\t\t\t\t\t | PEXT2_VOICECHAT\t\t\t/*entirely optional*/\n\t\t\t\t\t | PEXT2_SETANGLEDELTA\t\t/*mostly just nice to have*/\n\t\t\t\t\t | PEXT2_REPLACEMENTDELTAS\t/*carries quite a bit of extra info*/\n\t\t\t\t\t | PEXT2_MAXPLAYERS\t\t\t/*not supporting the extra players is bad*/\n\t\t\t\t\t | PEXT2_PREDINFO\t\t\t/*fixes some repdelta issues (especially for nq)*/\n\t\t\t\t\t | PEXT2_NEWSIZEENCODING\t/*more accurate sizes, for awkward mods*/\n//\t\t\t\t\t | PEXT2_INFOBLOBS\t\t\t/*allows mods to send infoblobs to csqc (for avatar images or whatever)*/\n\t\t\t\t\t ;\n\t\t}\n\t\tif (!strcasecmp(com_token, \"fte1\"))\n\t\t{\t//older stuff. most of this was replaced by replacementdeltas.\n\t\t\tclient->fteprotocolextensions\n\t\t\t\t\t|= PEXT_SETVIEW\n\t\t\t\t\t | PEXT_SCALE\n\t\t\t\t\t | PEXT_TRANS\n\t\t\t\t\t | PEXT_ACCURATETIMINGS\n\t\t\t\t\t | PEXT_SOUNDDBL\n\t\t\t\t\t | PEXT_MODELDBL\n\t\t\t\t\t | PEXT_ENTITYDBL\n\t\t\t\t\t | PEXT_ENTITYDBL2\n\t\t\t\t\t | PEXT_FLOATCOORDS\n\t\t\t\t\t | PEXT_COLOURMOD\n\t\t\t\t\t | PEXT_SPAWNSTATIC2\n\t\t\t\t\t | PEXT_256PACKETENTITIES\n\t\t\t\t\t | PEXT_SETATTACHMENT\n\t\t\t\t\t | PEXT_CHUNKEDDOWNLOADS\n\t\t\t\t\t | PEXT_CSQC\n\t\t\t\t\t | PEXT_DPFLAGS\n\t\t\t\t\t ;\n\t\t}\n\t\tif (!strcasecmp(com_token, \"csqc\"))\n\t\t{\t//JUST csqc.\n\t\t\tclient->fteprotocolextensions\n\t\t\t\t\t|= PEXT_CSQC\n\t\t\t\t\t;\n\t\t}\n\t}\n\n\tclient->fteprotocolextensions  &= Net_PextMask(PROTOCOL_VERSION_FTE1, ISNQCLIENT(client)) & PEXT_SERVERADVERTISE;\n\tclient->fteprotocolextensions2 &= Net_PextMask(PROTOCOL_VERSION_FTE2, ISNQCLIENT(client)) & PEXT2_SERVERADVERTISE;\n\tclient->ezprotocolextensions1  &= Net_PextMask(PROTOCOL_VERSION_EZQUAKE1, ISNQCLIENT(client)) & EZPEXT1_SERVERADVERTISE;\n\tclient->zquake_extensions &= SERVER_SUPPORTED_Z_EXTENSIONS;\n\n\t//older versions of fte didn't understand any interactions between ez's limited float support and replacement deltas. so only activate both when vrinputs is also supported.\n\tif ((client->ezprotocolextensions1 & EZPEXT1_FLOATENTCOORDS) && (client->fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS) && !(client->fteprotocolextensions2 & PEXT2_VRINPUTS))\n\t\tclient->ezprotocolextensions1 &= ~EZPEXT1_FLOATENTCOORDS;\n\n\t//some gamecode can't cope with some extensions for some reasons... and I'm too lazy to fix the code to cope.\n\tif (svs.gametype == GT_HALFLIFE)\n\t\tclient->fteprotocolextensions2 &= ~PEXT2_REPLACEMENTDELTAS;\t//baseline issues\n\n#ifdef HAVE_LEGACY\n\tif (ISQWCLIENT(client))\n\t{\n\t\t//be prepared to recognise client versions, in order to block known-buggy extensions.\n\t\tconst char *s;\n\t\tint ver;\n\t\textern cvar_t pext_ezquake_nochunks;\n\t\textern cvar_t pext_ezquake_verfortrans;\n\t\ts = InfoBuf_ValueForKey(&client->userinfo, \"*client\");\n\t\tif (!strncmp(s, \"ezQuake\", 7))\n\t\t{\n\t\t\ts = COM_Parse(s);\t//skip name-of-fork\n\t\t\tCOM_Parse(s);\t//tokenize the version\n\t\t\tver = atoi(com_token);\n\n\t\t\t//this should actually have been resolved now, but for future use...\n\t\t\tif ((client->fteprotocolextensions & PEXT_CHUNKEDDOWNLOADS) && pext_ezquake_nochunks.ival)\n\t\t\t{\n\t\t\t\tclient->fteprotocolextensions &= ~PEXT_CHUNKEDDOWNLOADS;\n\t\t\t\tSV_PrintToClient(client, PRINT_HIGH, \"ezQuake's implementation of chunked downloads is blocked on this server.\\n\");\n\t\t\t}\n\n\t\t\t//client fails to read the extra byte when PF_EXTRA_PFS is set, instead checking for the 18th bit in a 16-bit (signed) variable.\n\t\t\tif ((client->fteprotocolextensions & PEXT_TRANS) && ver < pext_ezquake_verfortrans.ival)\n\t\t\t{\n\t\t\t\tSV_PrintToClient(client, PRINT_HIGH, \"ezQuake's implementation of PEXT_TRANS is buggy. Disabling.\\n\");\n\t\t\t\tclient->fteprotocolextensions &= ~PEXT_TRANS;\n\t\t\t}\n\t\t\t//in order to simultaneously support PF_SOLID+Z_EXT_PF_SOLID and PF_HULLSIZE_Z+Z_EXT_PF_ONGROUND, I had to redefine the protocol when both were enabled.\n\t\t\t//ezquake does not understand the change.\n\t\t\tif ((client->zquake_extensions & (Z_EXT_PF_ONGROUND|Z_EXT_PF_SOLID)) && ver < pext_ezquake_verfortrans.ival)\n\t\t\t{\n\t\t\t\tif (client->fteprotocolextensions & PEXT_HULLSIZE)\n\t\t\t\t\tSV_PrintToClient(host_client, PRINT_HIGH, \"ezQuake's implementation of PEXT_HULLSIZE conflicts with zquake extensions.\\n\");\n\t\t\t\tif (client->fteprotocolextensions & PEXT_SCALE)\n\t\t\t\t\tSV_PrintToClient(host_client, PRINT_HIGH, \"ezQuake's implementation of PEXT_SCALE conflicts with zquake extensions.\\n\");\n\t\t\t\tif (client->fteprotocolextensions & PEXT_FATNESS)\n\t\t\t\t\tSV_PrintToClient(host_client, PRINT_HIGH, \"ezQuake's implementation of PEXT_FATNESS conflicts with zquake extensions.\\n\");\n\t\t\t\tif (client->fteprotocolextensions & PEXT_TRANS)\n\t\t\t\t\tSV_PrintToClient(host_client, PRINT_HIGH, \"ezQuake's implementation of PEXT_TRANS conflicts with zquake extensions.\\n\");\n\t\t\t\tclient->fteprotocolextensions &= ~(PEXT_HULLSIZE|PEXT_TRANS|PEXT_SCALE|PEXT_FATNESS);\n\t\t\t}\n\t\t}\n\n\t\t//its not that I'm singling out ezquake or anything, but it has too many people using outdated versions that its hard to ignore.\n\t}\n#endif\n\n\t//\n\tclient->maxmodels = 256;\n\tif (client->fteprotocolextensions & PEXT_256PACKETENTITIES)\n\t\tmaxpacketentities = MAX_EXTENDED_PACKET_ENTITIES;\n\telse\n\t\tmaxpacketentities = MAX_STANDARD_PACKET_ENTITIES;\t//true for qw,q2\n\n\tif (client->fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS)\n\t{\n\t\tclient->max_net_clients = ISQWCLIENT(client)?QWMAX_CLIENTS:NQMAX_CLIENTS;\n\t\tclient->max_net_staticents = ~0u;\t//unlimited in both fte+qss.\n\n\t\t//you need to reconnect for this to update, of course. so make sure its not *too* low...\n\t\tclient->max_net_ents =  bound(512, pr_maxedicts.ival, MAX_EDICTS);\n\t\tclient->maxmodels = min(1u<<14, MAX_PRECACHE_MODELS);\t//protocol limited to 14 bits.\n\t}\n\telse if (ISQWCLIENT(client))\t//readd?\n\t{\n\t\tclient->max_net_clients = QWMAX_CLIENTS;\n\t\tclient->max_net_ents = 512;\n\t\tif (client->fteprotocolextensions & PEXT_ENTITYDBL)\n\t\t\tclient->max_net_ents += 512;\n\t\tif (client->fteprotocolextensions & PEXT_ENTITYDBL2)\n\t\t\tclient->max_net_ents += 1024;\n\t\tclient->max_net_staticents = 512;\t//the ezquake limit, too few people use vanilla to really care about that (it would be too unstable anyway). fodquake has no limit.\n\n\t\tif (client->fteprotocolextensions & PEXT_MODELDBL)\n\t\t\tclient->maxmodels = 512;\n\t}\n\telse if (ISDPCLIENT(client))\n\t{\n\t\tclient->max_net_clients = 255;\n\t\tclient->max_net_ents = bound(512, pr_maxedicts.ival, 32768);\n\t\tclient->max_net_staticents = 1024;\t\t\t//its quite low, proportionally.\n\t\tclient->maxmodels = MAX_PRECACHE_MODELS;\t//protocol limit of 16 bits. 15 bits for late precaches. client limit of 1k\n\n\t\tclient->datagram.maxsize = sizeof(host_client->datagram_buf);\n\t}\n\telse if (client->protocol == SCP_BJP3 || client->protocol == SCP_FITZ666)\n\t{\n\t\tclient->max_net_clients = NQMAX_CLIENTS;\n\t\tclient->max_net_ents = bound(512, pr_maxedicts.ival, 32768);\t//fitzquake supports 65535, but our writeentity builtin works differently, which causes problems.\n\t\tclient->max_net_staticents = 4096;\t//quakespasm has 4k, more than 3k starts to have issues with the msg_init buffer size.\n\t\tclient->maxmodels = MAX_PRECACHE_MODELS;\n\t\tmaxpacketentities = client->max_net_ents;\n\n\t\tclient->datagram.maxsize = sizeof(host_client->datagram_buf);\n\t}\n\telse if (client->qex)\n\t{\n\t\tclient->max_net_clients = NQMAX_CLIENTS;\n\t\tclient->datagram.maxsize = sizeof(host_client->datagram_buf);\n\t\tclient->max_net_ents = bound(512, pr_maxedicts.ival, 32768);\n\t\tclient->max_net_staticents = 4096;\n\t}\n\telse\n\t{\n\t\tclient->max_net_clients = NQMAX_CLIENTS;\n\t\tclient->datagram.maxsize = MAX_NQDATAGRAM;\t//vanilla limit\n\t\tif (client->proquake_angles_hack)\n\t\t\tclient->max_net_ents = bound(512, pr_maxedicts.ival, 8192);\n\t\telse\n\t\t\tclient->max_net_ents = bound(512, pr_maxedicts.ival, 600);\n\t\tclient->max_net_staticents = 128;\t//yeah, its low.\n\t}\n\n\tif (client->fteprotocolextensions2 & PEXT2_MAXPLAYERS)\n\t\tclient->max_net_clients = MAX_CLIENTS;\n\n\tclient->max_net_clients = min(client->max_net_clients, MAX_CLIENTS);\n\n\tclient->pendingdeltabits = NULL;\n\tclient->pendingcsqcbits = NULL;\n\n\t//initialise the client's frames, based on that client's protocol\n\tswitch(client->protocol)\n\t{\n#ifdef Q3SERVER\n\tcase SCP_QUAKE3:\n\t\tif (client->frameunion.q3frames)\n\t\t\tZ_Free(client->frameunion.q3frames);\n\t\tclient->frameunion.q3frames = NULL;//Z_Malloc(Q3UPDATE_BACKUP*sizeof(*client->frameunion.q3frames));\n\t\tbreak;\n#endif\n\n#ifdef Q2SERVER\n\tcase SCP_QUAKE2:\n\t\t// build a new connection\n\t\t// accept the new client\n\t\t// this is the only place a client_t is ever initialized\n\t\tclient->frameunion.q2frames = client->frameunion.q2frames;\t//don't touch these.\n\t\tif (client->frameunion.q2frames)\n\t\t\tZ_Free(client->frameunion.q2frames);\n\n\t\tclient->frameunion.q2frames = Z_Malloc(sizeof(q2client_frame_t)*Q2UPDATE_BACKUP);\n\t\tbreak;\n#endif\n\n\tdefault:\n\t\tif (client->frameunion.frames)\n\t\t\tZ_Free(client->frameunion.frames);\n\n\t\tif ((client->fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS) || ISDPCLIENT(client))\n\t\t{\n#define round64(size) ((size+7)&~7)\n\t\t\tchar *ptr;\n\t\t\tint maxents = maxpacketentities*4;\t/*this is the max number of ents updated per frame. we can't track more, so...*/\n\t\t\tif (maxents > client->max_net_ents)\n\t\t\t\tmaxents = client->max_net_ents;\n\t\t\tptr = Z_Malloc(\tround64(sizeof(client_frame_t)*UPDATE_BACKUP)+\n\t\t\t\t\t\t\tround64(sizeof(*client->pendingdeltabits)*client->max_net_ents)+\n\t\t\t\t\t\t\tround64(sizeof(*client->pendingcsqcbits)*client->max_net_ents)+\n\t\t\t\t\t\t\tround64(sizeof(*client->frameunion.frames[i].resend)*maxents)*UPDATE_BACKUP);\n\t\t\tclient->frameunion.frames = (void*)ptr;\n\t\t\tptr += round64(sizeof(*client->frameunion.frames)*UPDATE_BACKUP);\n\t\t\tclient->pendingdeltabits = (void*)ptr;\n\t\t\tptr += round64(sizeof(*client->pendingdeltabits)*client->max_net_ents);\n\t\t\tclient->pendingcsqcbits = (void*)ptr;\n\t\t\tptr += round64(sizeof(*client->pendingcsqcbits)*client->max_net_ents);\n\t\t\tfor (i = 0; i < UPDATE_BACKUP; i++)\n\t\t\t{\n\t\t\t\tclient->frameunion.frames[i].maxresend = maxents;\n\t\t\t\tclient->frameunion.frames[i].resend = (void*)ptr;\n\t\t\t\tptr += round64(sizeof(*client->frameunion.frames[i].resend)*maxents);\n\t\t\t\tclient->frameunion.frames[i].senttime = realtime;\n\t\t\t}\n\n\t\t\t//make sure the reset is sent.\n\t\t\tclient->pendingdeltabits[0] = UF_SV_REMOVE;\n\t\t}\n\t\telse if (ISNQCLIENT(client))\n\t\t{\n\t\t\tclient->frameunion.frames = Z_Malloc((sizeof(client_frame_t))*UPDATE_BACKUP);\n\t\t\tfor (i = 0; i < UPDATE_BACKUP; i++)\n\t\t\t{\n\t\t\t\tclient->frameunion.frames[i].qwentities.max_entities = 0;\n\t\t\t\tclient->frameunion.frames[i].qwentities.entities = NULL;\n\t\t\t\tclient->frameunion.frames[i].senttime = realtime;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tclient->frameunion.frames = Z_Malloc((sizeof(client_frame_t)+sizeof(entity_state_t)*maxpacketentities)*UPDATE_BACKUP);\n\t\t\tfor (i = 0; i < UPDATE_BACKUP; i++)\n\t\t\t{\n\t\t\t\tclient->frameunion.frames[i].qwentities.max_entities = maxpacketentities;\n\t\t\t\tclient->frameunion.frames[i].qwentities.entities = (entity_state_t*)(client->frameunion.frames+UPDATE_BACKUP) + i*client->frameunion.frames[i].qwentities.max_entities;\n\t\t\t\tclient->frameunion.frames[i].senttime = realtime;\n\t\t\t}\n\t\t}\n\t\tbreak;\n\t}\n\n\t//make sure we have the right limits for splitscreen clients too (mostly for viewmodel safety checks)\n\tfor (seat = client->controlled; seat; seat = seat->controlled)\n\t{\n\t\tseat->max_net_clients = client->max_net_clients;\n\t\tseat->max_net_ents = client->max_net_ents;\n\t\tseat->maxmodels = client->maxmodels;\n\t}\n\n\tclient->lastsequence_acknowledged = -2000000000;\n}\n\n\n//void NET_AdrToStringResolve (netadr_t *adr, void (*resolved)(void *ctx, void *data, size_t a, size_t b), void *ctx, size_t a, size_t b);\nstatic void SV_UserDNSResolved(void *ctx, void *data, size_t idx, size_t uid)\n{\n\tif (idx < svs.allocated_client_slots)\n\t{\n\t\tclient_t *cl = &svs.clients[idx];\n\t\tif (cl->userid == uid)\n\t\t{\n\t\t\tZ_Free(cl->reversedns);\n\t\t\tcl->reversedns = data;\n\t\t\tSV_LogPlayer(cl, va(\"dns %s\", cl->reversedns));\n\t\t\treturn;\n\t\t}\n\t}\n\t//client ran away before the dns completed\n\tCon_DPrintf(\"stale dns lookup result: %s\\n\", (char*)data);\n\tZ_Free(data);\n}\n\nclient_t *SV_AddSplit(client_t *controller, char *info, int id)\n{\n\tclient_t *cl, *prev;\n\tint i, j;\n\tint curclients;\n\tqboolean loadgame;\n//\tconst char *name;\n\tchar newname[80];\n\tunsigned int clients = 0, spectators = 0;\n\tqboolean asspec;\n\n\tfor (curclients = 0, prev = cl = controller; cl; cl = cl->controlled)\n\t{\n\t\tprev = cl;\n\t\tcurclients++;\n\t}\n\n\tif (id && curclients != id)\n\t\treturn NULL;\t//this would be weird.\n\n//\tif (curclients >= 16)\n//\t\treturn NULL;\t//protocol limit on stats.\n\tif (curclients >= MAX_SPLITS)\n\t\treturn NULL;\n\t//only allow splitscreen if its explicitly allowed. unless its the local client in which case its always allowed.\n\t//wouldn't it be awesome if we could always allow it for spectators? the join command makes that awkward, though I suppose we could just drop the extras in that case.\n\tif (!sv_allow_splitscreen.ival && controller->netchan.remote_address.type != NA_LOOPBACK)\n\t\treturn NULL;\t//FIXME: allow spectators to do this anyway?\n\n/*\t\tif (cl->state == cs_loadzombie)\n\t\t{\n\t\t\tif (!newcl)\n\t\t\t{\n\t\t\t\tif (((!strcmp(cl->name, name) || !*cl->name) && (!*cl->guid || !strcmp(guid, cl->guid))) || sv.allocated_client_slots <= 1)\t//named, or first come first serve.\n\t\t\t\t{\n\t\t\t\t\tif (cl->istobeloaded)\n\t\t\t\t\t\tCon_DPrintf(\"%s:Using loadzombie\\n\", svs.name);\n\t\t\t\t\telse\n\t\t\t\t\t\tCon_DPrintf(\"%s:Using parmzombie\\n\", svs.name);\n\t\t\t\t\tnewcl = cl;\n\t\t\t\t\tpreserveparms = true;\n\t\t\t\t\ttemp.istobeloaded = cl->istobeloaded;\n\t\t\t\t\tmemcpy(temp.spawn_parms, cl->spawn_parms, sizeof(temp.spawn_parms));\n\t\t\t\t\tif (cl->userid)\n\t\t\t\t\t\ttemp.userid = cl->userid;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n*/\n\n\tSV_DeDupeName(Info_ValueForKey(info, \"name\"), cl, newname, sizeof(newname));\n\tfor (i=0,cl=svs.clients ; i<sv.allocated_client_slots ; i++,cl++)\n\t{\n\t\tif (cl->state == cs_loadzombie && !controller->spectator)\n\t\t{\t//if this is a loadzombie with the same name as the new seat is trying to use then lets use that slot.\n\t\t\tif (!strcmp(cl->name, newname))\n\t\t\t\tbreak;\n\t\t}\n\t}\n\tif (i == sv.allocated_client_slots)\n\t{\n\t\tfor (i=0,cl=svs.clients ; i<sv.allocated_client_slots ; i++,cl++)\n\t\t{\n\t\t\tif (cl->state == cs_free)\n\t\t\t{\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (i == sv.allocated_client_slots)\n\t\t{\n\t\t\tSV_PrintToClient(controller, PRINT_HIGH, \"not enough free player slots\\n\");\n\t\t\treturn NULL;\n\t\t}\n\t}\n\n\tloadgame = (cl->state == cs_loadzombie);\n\tif (loadgame)\n\t\tasspec = cl->spectator;\n\telse\n\t\tasspec = !!atoi(Info_ValueForKey(info, \"spectator\"));\n\tfor (j=0 ; j<sv.allocated_client_slots ; j++)\n\t{\n\t\tif (svs.clients[j].state == cs_free)\n\t\t\tcontinue;\n\t\tif (svs.clients[j].spectator && svs.clients[j].spectator!=2)\n\t\t\tspectators++;\n\t\telse\n\t\t\tclients++;\n\t}\n\tif (controller->netchan.remote_address.type != NA_LOOPBACK)\t//ignore limits for the local client (they could just tweak the cvar instead, but that's a hassle)\n\tif ((asspec?spectators:clients) >= (asspec?maxspectators.ival:maxclients.ival))\n\t{\n\t\tSV_PrintToClient(controller, PRINT_HIGH, \"Server full, cannot add new seat\\n\");\n\t\treturn NULL;\n\t}\n\n\n\t{\t//save off anything we might want to preserve\n\t\tqboolean tobeloaded = cl->istobeloaded;\n\t\tqboolean spawned = cl->spawned;\n\t\tunsigned int userid = cl->userid;\n\t\tfloat\tspawn_parms[NUM_SPAWN_PARMS];\n\t\tmemcpy(spawn_parms, cl->spawn_parms, sizeof(spawn_parms));\n\n\t\t//now actually wipe the player slot\n\t\tmemset(cl, 0, sizeof(*cl));\n\n\t\t//and restore preserved stuff, if its appropriate.\n\t\tif (loadgame)\n\t\t{\n\t\t\tcl->istobeloaded = tobeloaded;\n\t\t\tcl->spawned = spawned;\n\t\t\tcl->userid = userid;\n\t\t\tmemcpy(cl->spawn_parms, spawn_parms, sizeof(cl->spawn_parms));\n\t\t}\n\t}\n\n\tcl->spectator = asspec;\n\tcl->netchan.remote_address = controller->netchan.remote_address;\n\tcl->netchan.message.prim = controller->netchan.message.prim;\n\tcl->backbuf.prim = controller->backbuf.prim;\n\tcl->netchan.netprim = controller->netchan.netprim;\n\tcl->zquake_extensions = controller->zquake_extensions;\n\tcl->fteprotocolextensions = controller->fteprotocolextensions;\n\tcl->fteprotocolextensions2 = controller->fteprotocolextensions2;\n\tcl->ezprotocolextensions1 = controller->ezprotocolextensions1;\n\tcl->penalties = controller->penalties;\n\tcl->protocol = controller->protocol;\n\tcl->maxmodels = controller->maxmodels;\n\tcl->max_net_clients = controller->max_net_clients;\n\tcl->max_net_ents = controller->max_net_ents;\n\n\tif (*controller->guid)\n\t\tQ_snprintfz(cl->guid, sizeof(cl->guid), \"%s:%i\", controller->guid, curclients);\n\telse\n\t\tQ_strncpyz(cl->guid, \"\", sizeof(cl->guid));\n\tcl->name = cl->namebuf;\n\tcl->team = cl->teambuf;\n\tcl->userinfo.ChangeCB = svs.info.ChangeCB;\n\tcl->userinfo.ChangeCTX = &cl->userinfo;\n\n\tif (!cl->userid || !loadgame)\n\t\tcl->userid = ++nextuserid;\n\n\tcl->playerclass = 0;\n\tcl->pendingdeltabits = NULL;\n\tcl->pendingcsqcbits = NULL;\n\tcl->seat = curclients;\n\n\tcl->edict = NULL;\n#ifdef Q2SERVER\n\tcl->q2edict = NULL;\n#endif\n\tswitch(svs.gametype)\n\t{\n#ifdef Q2SERVER\n\tcase GT_QUAKE2:\n\t\tcl->q2edict = Q2EDICT_NUM(i+1);\n\n\t\tif (!ge->ClientConnect(cl->q2edict, info))\n\t\t{\n\t\t\tconst char *reject = Info_ValueForKey(info, \"rejmsg\");\n\t\t\tif (*reject)\n\t\t\t\tSV_ClientPrintf(controller, PRINT_HIGH, \"Splitscreen Refused: %s\\n\", reject);\n\t\t\telse\n\t\t\t\tSV_ClientPrintf(controller, PRINT_HIGH, \"Splitscreen Refused\\n\");\n\t\t\tCon_DPrintf (\"Game rejected a connection.\\n\");\n\t\t\treturn NULL;\n\t\t}\n\n\t\tge->ClientUserinfoChanged(cl->q2edict, info);\n\t\tbreak;\n#endif\n\tdefault:\n\t\tcl->edict = EDICT_NUM_PB(svprogfuncs, i+1);\n\t\tbreak;\n\t}\n\n\tprev->controlled = cl;\n\tprev = cl;\n\tcl->controller = controller;\n\tcl->controlled = NULL;\n\n\tInfoBuf_FromString(&cl->userinfo, info, false);\n\tInfoBuf_RemoveKey (&cl->userinfo, \"spectator\");\n\t//this is a hint rather than a game breaker should it fail.\n\tif (cl->spectator)\n\t\tInfoBuf_SetValueForStarKey (&cl->userinfo, \"*spectator\", va(\"%i\", cl->spectator));\n\tcl->state = controller->state;\n\tcl->connection_started = realtime;\n\n//\thost_client = NULL;\n//\tsv_player = NULL;\n\n\tSV_ExtractFromUserinfo (cl, true);\n\tif (!loadgame)\n\t\tSV_GetNewSpawnParms(cl);\n\n\tif (cl->state >= cs_connected)\n\t{\n\t\tcl->sendinfo = true;\n\t\tif (svprogfuncs)\n\t\t\tSV_SetUpClientEdict(cl, cl->edict);\n\t}\n\tif (cl->state >= cs_spawned)\n\t\tSV_Begin_Core(cl);\n\treturn cl;\n}\n\nvoid SV_DoDirectConnect(svconnectinfo_t *fte_restrict info)\n{\n\tint\t\t\ti;\n\tclient_t\t*cl, *newcl;\n\tclient_t\ttemp;\n\tedict_t\t\t*ent;\n#ifdef Q2SERVER\n\tq2edict_t\t*q2ent;\n#endif\n\tint\t\t\tedictnum;\n\tchar\t\t*s;\n\tint\t\t\tclients, spectators;\n\tqboolean\tspectator;\n\tchar\t\tbasic[80];\n\tqboolean\tredirect = false;\n\tqboolean\tpreserveparms = false;\n\tunsigned int\tncflags;\n\n#ifdef NQPROT\n\textern cvar_t sv_protocol_nq;\n#endif\n\n\n\tchar *name;\n\tchar adrbuf[MAX_ADR_SIZE];\n\n\tnet_from = info->adr; //SV_AcceptMessage+SV_RejectMessage are lame.\n\n\n\t// check for password or spectator_password\n\tif (svprogfuncs)\n\t{\n\t\ts = Info_ValueForKey (info->seat[0].info, \"spectator\");\n\t\tif (s[0] && strcmp(s, \"0\"))\n\t\t{\n\t\t\tif (spectator_password.string[0] &&\n\t\t\t\tstricmp(spectator_password.string, \"none\") &&\n\t\t\t\tstrcmp(spectator_password.string, s) &&\n\t\t\t\t!NET_IsLoopBackAddress(&info->adr))\n\t\t\t{\t// failed\n\t\t\t\tCon_TPrintf (\"%s:spectator password failed\\n\", NET_AdrToString (adrbuf, sizeof(adrbuf), &info->adr));\n\t\t\t\tSV_RejectMessage (info->protocol, \"requires a spectator password\\n\\n\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tInfo_RemoveKey (info->seat[0].info, \"spectator\"); // remove key\n\t\t\tInfo_SetValueForStarKey (info->seat[0].info, \"*spectator\", \"1\", sizeof(info->seat[0].info));\n\t\t\tspectator = true;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (!password.string[0] ||\n\t\t\t\t!stricmp(password.string, \"none\") ||\n\t\t\t\tNET_IsLoopBackAddress(&info->adr))\n\t\t\t\t;\t//don't care, doesn't matter.\n\t\t\telse if (info->protocol == SCP_NETQUAKE)\n\t\t\t{\t//if its a proquake client then use numeric passwords, which take a bit of processing\n\t\t\t\tchar *e;\n\t\t\t\tint got = strtol(Info_ValueForKey (info->seat[0].info, \"password\"), NULL, 0);\n\t\t\t\tint need = strtol(password.string, &e, 0);\n\t\t\t\tif (*e)\n\t\t\t\t\tneed = CalcHashInt(&hash_md4, password.string, strlen(password.string));\n\t\t\t\tif (got != need)\n\t\t\t\t{\n\t\t\t\t\tCon_TPrintf (\"%s:password failed\\n\", NET_AdrToString (adrbuf, sizeof(adrbuf), &info->adr));\n\t\t\t\t\tSV_RejectMessage (info->protocol, \"server requires a password\\n\\n\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\ts = Info_ValueForKey (info->seat[0].info, \"password\");\n\t\t\t\tif (strcmp(password.string, s))\n\t\t\t\t{\n\t\t\t\t\tCon_TPrintf (\"%s:password failed\\n\", NET_AdrToString (adrbuf, sizeof(adrbuf), &info->adr));\n\t\t\t\t\tSV_RejectMessage (info->protocol, \"server requires a password\\n\\n\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t\tspectator = false;\n\t\t\tInfo_RemoveKey (info->seat[0].info, \"password\"); // remove passwd\n\t\t\tInfo_RemoveKey (info->seat[0].info, \"*spectator\"); // remove key\n\t\t}\n\t}\n\telse\n\t\tspectator = false;//q2 does all of it's checks internally, and deals with spectator ship too\n\n\tnewcl = &temp;\n\tmemset (newcl, 0, sizeof(client_t));\n\n#ifdef NQPROT\n\tif (!info->supportedprotocols && info->protocol == SCP_NETQUAKE)\n\t{\t//NQ protocols lack stuff like protocol extensions.\n\t\t//its the wild west where nothing is known about the client and everything breaks.\n\t\t//we defer the assumption to the sv_protocol_nq cvar (only for clients that don't report their known protocols)\n\t\tif (!strcmp(sv_protocol_nq.string, \"fitz\"))\n\t\t\tinfo->protocol = SCP_FITZ666;\n\t\telse if (!strcmp(sv_protocol_nq.string, \"bjp\") || !strcmp(sv_protocol_nq.string, \"bjp3\"))\n\t\t\tinfo->protocol = SCP_BJP3;\n\t\telse if (!strcmp(sv_protocol_nq.string, \"dpp6\") || !strcmp(sv_protocol_nq.string, \"dp6\"))\n\t\t\tinfo->protocol = SCP_DARKPLACES6;\n\t\telse if (!strcmp(sv_protocol_nq.string, \"dpp7\") || !strcmp(sv_protocol_nq.string, \"dp7\"))\n\t\t\tinfo->protocol = SCP_DARKPLACES7;\n\t\telse if (!strcmp(sv_protocol_nq.string, \"id\") || !strcmp(sv_protocol_nq.string, \"vanilla\"))\n\t\t\tinfo->protocol = SCP_NETQUAKE;\n\t\telse switch(sv_protocol_nq.ival)\n\t\t{\n\t\tcase PROTOCOL_VERSION_RMQ:\n\t\tcase PROTOCOL_VERSION_FITZ:\n\t\t\tinfo->protocol = SCP_FITZ666;\n\t\t\tbreak;\n\t\tcase PROTOCOL_VERSION_BJP3:\n\t\t\tinfo->protocol = SCP_BJP3;\n\t\t\tbreak;\n\t\tcase 15:\n\t\t\tinfo->protocol = SCP_NETQUAKE;\n\t\t\tbreak;\n\t\tcase PROTOCOL_VERSION_DP6:\n\t\t\tinfo->protocol = SCP_DARKPLACES6;\n\t\t\tbreak;\n\t\tcase PROTOCOL_VERSION_DP7:\n\t\t\tinfo->protocol = SCP_DARKPLACES7;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tCon_Printf(\"sv_protocol_nq set incorrectly\\n\");\n\t\tcase 0:\n\t\t\t//change nothing\n\t\t\tbreak;\n\t\t}\n\t}\n\tnewcl->supportedprotocols = info->supportedprotocols;\n\tnewcl->proquake_angles_hack = info->proquakeanglehack;\n\tnewcl->qex = info->isqex;\n#endif\n\n\tnewcl->userid = ++nextuserid;\n\tnewcl->fteprotocolextensions = info->ftepext1;\n\tnewcl->fteprotocolextensions2 = info->ftepext2;\n\tnewcl->ezprotocolextensions1 = info->ezpext1;\n\tnewcl->protocol = info->protocol;\n\tnewcl->pextknown = info->ftepext1||info->ftepext2||info->ezpext1;\n\tQ_strncpyz(newcl->guid, info->guid, sizeof(newcl->guid));\n\n//\tCon_TPrintf(\"%s:%s:connect\\n\", sv.name, NET_AdrToString (adrbuf, sizeof(adrbuf), &adr));\n\n\t// if there is already a slot for this ip, drop it\n\tfor (i=0,cl=svs.clients ; i<svs.allocated_client_slots ; i++,cl++)\n\t{\n\t\tif (cl->state == cs_free || cl->state == cs_loadzombie)\n\t\t\tcontinue;\n\t\tif (NET_CompareBaseAdr (&info->adr, &cl->netchan.remote_address)\n\t\t\t&& ((info->protocol == SCP_QUAKEWORLD && cl->netchan.qport == info->qport) || info->adr.port == cl->netchan.remote_address.port ))\n\t\t{\n\t\t\tif (realtime - cl->connection_started < sv_reconnectlimit.value)\n\t\t\t{\n\t\t\t\tCon_Printf (\"%s:reconnect rejected: too soon\\n\", NET_AdrToString (adrbuf, sizeof(adrbuf), &info->adr));\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (cl->state == cs_connected)\n\t\t\t{\n\t\t\t\tif (cl->protocol != info->protocol)\n\t\t\t\t{\n\t\t\t\t\tCon_TPrintf(\"%s: diff prot connect\\n\", NET_AdrToString (adrbuf, sizeof(adrbuf), &info->adr));\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tCon_TPrintf(\"%s:dup connect\\n\", NET_AdrToString (adrbuf, sizeof(adrbuf), &info->adr));\n\t\t\t}\n\t\t\t/*else if (cl->state == cs_zombie)\n\t\t\t{\n\t\t\t\tCon_Printf (\"%s:reconnect\\n\", NET_AdrToString (adrbuf, sizeof(adrbuf), &adr));\n\t\t\t}*/\n\t\t\telse\n\t\t\t\tCon_TPrintf (\"%s:%s:reconnect\\n\", svs.name, NET_AdrToString (adrbuf, sizeof(adrbuf), &info->adr));\n\n#if 1\n\t\t\t//wait for timeout before the player can reuse that address:port combo. clients should pick a new port on reconnect if they want to connect instantly.\n\t\t\treturn;\n#else\n\t\t\t//silently drop the old connection, without causing the old client to get a disconnect or anything stupid like that.\n\t\t\tcl->protocol = SCP_BAD;\n\t\t\tSV_DropClient (cl);\n\t\t\tcl->protocol = info->protocol;\n\t\t\tbreak;\n#endif\n\t\t}\n\t}\n\n\tname = Info_ValueForKey (info->seat[0].info, \"name\");\n\n\t/*\n\tif (sv.world.worldmodel && info->protocol == SCP_QUAKEWORLD &&!atoi(Info_ValueForKey (info->userinfo, \"iknow\")))\n\t{\n\t\tif (sv.world.worldmodel->fromgame == fg_halflife && !(newcl->fteprotocolextensions & PEXT_HLBSP))\n\t\t{\n\t\t\tif (atof(Info_ValueForKey (info->userinfo, \"*FuhQuake\")) < 0.3)\n\t\t\t{\n\t\t\t\tSV_RejectMessage (info->protocol, \"The server is using a halflife level and we don't think your client supports this\\nuse 'setinfo iknow 1' to ignore this check\\nYou can go to \"ENGINEWEBSITE\" to get a compatible client\\n\\nYou may need to enable an option\\n\\n\");\n//\t\t\t\tCon_Printf(\"player %s was dropped due to incompatible client\\n\", name);\n//\t\t\t\treturn;\n\t\t\t}\n\t\t}\n#ifdef PEXT_Q2BSP\n\t\telse if (sv.world.worldmodel->fromgame == fg_quake2 && !(newcl->fteprotocolextensions & PEXT_Q2BSP))\n\t\t{\n\t\t\tSV_RejectMessage (info->protocol, \"The server is using a q2bsp-format level and we don't think your client supports this\\nuse 'setinfo iknow 1' to ignore this check\\nYou can go to \"ENGINEWEBSITE\" to get a compatible client\\n\\nYou may need to enable an option\\n\\n\");\n//\t\t\tCon_Printf(\"player %s was dropped due to incompatible client\\n\", name);\n//\t\t\treturn;\n\t\t}\n#endif\n#ifdef PEXT_Q3BSP\n\t\telse if (sv.world.worldmodel->fromgame == fg_quake3 && !(newcl->fteprotocolextensions & PEXT_Q3BSP))\n\t\t{\n\t\t\tSV_RejectMessage (info->protocol, \"The server is using a q3bsp-format level and we don't think your client supports this\\nuse 'setinfo iknow 1' to ignore this check\\nYou can go to \"ENGINEWEBSITE\" to get a compatible client\\n\\nYou may need to enable an option\\n\\n\");\n//\t\t\tCon_Printf(\"player %s was dropped due to incompatible client\\n\", name);\n//\t\t\treturn;\n\t\t}\n#endif\n\t}\n\t*/\n\n\tSV_FixupName(name, temp.namebuf, sizeof(temp.namebuf));\n\tname = temp.namebuf;\n\n\tdeleetstring(basic, name);\n\tif (!*basic || strstr(basic, \"console\"))\n\t\tname = \"unnamed\";\t//have fun dudes.\n\n\t// count up the clients and spectators\n\tclients = 0;\n\tspectators = 0;\n\tnewcl = NULL;\n\tif (!sv.allocated_client_slots)\n\t{\n\t\tCon_Printf(\"Apparently, there are no client slots allocated. This shouldn't be happening\\n\");\n\t\treturn;\n\t}\n\tfor (i=0,cl=svs.clients ; i<sv.allocated_client_slots ; i++,cl++)\n\t{\n\t\tif (cl->state == cs_free)\n\t\t\tcontinue;\n\t\tif (cl->spectator && cl->spectator!=2)\n\t\t\tspectators++;\n\t\telse\n\t\t\tclients++;\n\n\t\tif (cl->state == cs_loadzombie)\n\t\t{\t//only try if they actually match this one...\n\t\t\tif ((!strcmp(cl->name, name) || !*cl->name) && (!*cl->guid || !strcmp(info->guid, cl->guid)))\n\t\t\t{\n\t\t\t\tnewcl = cl;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (!newcl)\t//just use any.\n\t\t\t\tnewcl = cl;\n\t\t}\n\t}\n\n\tif (newcl)\n\t{\t//client is reprising a loaded slot.\n\t\tif (newcl->istobeloaded)\n\t\t{\n\t\t\tSV_BroadcastTPrintf(PRINT_HIGH, \"%s reprises %s\\n\", name, newcl->name);\n\t\t\tCon_DPrintf(\"%s:Using loadzombie\\n\", svs.name);\n\t\t}\n\t\telse\n\t\t\tCon_DPrintf(\"%s:Using parmzombie\\n\", svs.name);\n\t\tpreserveparms = true;\n\t\ttemp.istobeloaded = newcl->istobeloaded;\n\t\ttemp.spawned = newcl->spawned;\n\t\tmemcpy(temp.spawn_parms, newcl->spawn_parms, sizeof(temp.spawn_parms));\n\t\tif (newcl->userid)\n\t\t\ttemp.userid = newcl->userid;\n\t}\n\telse \t//client has no existing slot.\n\t{\n#ifdef SUBSERVERS\n\t\tif (SSV_IsSubServer())\n\t\t{\n\t\t\tif (1)\n\t\t\t{\n\t\t\t\tsizebuf_t s;\n\t\t\t\tqbyte send[8192];\n\n\t\t\t\tmemset(&s, 0, sizeof(s));\n\t\t\t\ts.data = send;\n\t\t\t\ts.maxsize = sizeof(send);\n\t\t\t\ts.cursize = 2;\n\n\t\t\t\tMSG_WriteByte(&s, ccmd_foundplayer);\n\t\t\t\tMSG_WriteString(&s, name);\n\t\t\t\tMSG_WriteString(&s, NET_AdrToString (adrbuf, sizeof(adrbuf), &info->adr));\n\t\t\t\tMSG_WriteString(&s, info->guid);\n\t\t\t\tSSV_InstructMaster(&s);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tSV_RejectMessage (info->protocol, \"Direct connections are not permitted.\\n\");\n\t\t\t\tCon_TPrintf (\"* rejected direct connection\\n\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n#endif\n\n\t\t/*single player logic*/\n\t\tif (sv.allocated_client_slots == 1 && info->adr.type == NA_LOOPBACK)\n\t\t\tif (svs.clients[0].state >= cs_connected)\n\t\t\t{\n\t\t\t\tCon_Printf(\"Kicking %s to make space for local client\\n\", svs.clients[0].name);\n\t\t\t\tSV_DropClient(svs.clients);\n\t\t\t}\n\n\t\t// if at server limits, refuse connection\n\t\tif ( maxclients.ival > MAX_CLIENTS )\n\t\t\tCvar_SetValue (&maxclients, MAX_CLIENTS);\n\t\tif (maxspectators.ival > MAX_CLIENTS)\n\t\t\tCvar_SetValue (&maxspectators, MAX_CLIENTS);\n\n\t\t// find a free client slot\n\t\tfor (i=0; i<sv.allocated_client_slots ; i++)\n\t\t{\n\t\t\tcl=svs.clients+i;\n\t\t\tif (cl->state == cs_free)\n\t\t\t{\n\t\t\t\tnewcl = cl;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\t/*only q1/h2 has a maxclients/maxspectators separation. q2 or q3 the gamecode enforces any such clienttype limits*/\n\t\tif (svprogfuncs)\n\t\t{\n\t\t\tif (spectator && spectators >= maxspectators.ival)\n\t\t\t\tredirect = true;\n\t\t\tif (!spectator && clients >= maxclients.ival)\n\t\t\t\tredirect = true;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (clients >= maxclients.ival)\n\t\t\t\tredirect = true;\n\t\t}\n\n\t\tif (redirect)\n\t\t{\n\t\t\textern cvar_t sv_fullredirect;\n\t\t\tif (!*sv_fullredirect.string)\n\t\t\t\tnewcl = NULL;\n\t\t}\n\n\t\tif (!newcl)\n\t\t{\n\t\t\tif (!svprogfuncs)\n\t\t\t{\n\t\t\t\tSV_RejectMessage (info->protocol, \"\\nserver is full\\n\\n\");\n\t\t\t\tCon_TPrintf (\"%s:full connect\\n\", NET_AdrToString (adrbuf, sizeof(adrbuf), &info->adr));\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (spectator && spectators >= maxspectators.ival)\n\t\t\t\t{\n\t\t\t\t\tSV_RejectMessage (info->protocol, \"\\nserver is full (%i of %i spectators)\\n\\n\", spectators, maxspectators.ival);\n\t\t\t\t\tCon_TPrintf (\"%s:full connect (spectators)\\n\", NET_AdrToString (adrbuf, sizeof(adrbuf), &info->adr));\n\t\t\t\t}\n\t\t\t\telse if (!spectator && clients >= maxclients.ival)\n\t\t\t\t{\n\t\t\t\t\tSV_RejectMessage (info->protocol, \"\\nserver is full (%i of %i players)\\n\\n\", clients, maxclients.ival);\n\t\t\t\t\tCon_TPrintf (\"%s:full connect (players)\\n\", NET_AdrToString (adrbuf, sizeof(adrbuf), &info->adr));\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tSV_RejectMessage (info->protocol, \"\\nserver is full (%i of %i connections)\\n\\n\", clients+spectators, sv.allocated_client_slots);\n\t\t\t\t\tCon_TPrintf (\"%s:full connect\\n\", NET_AdrToString (adrbuf, sizeof(adrbuf), &info->adr));\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t}\n\n\t//set up the gamecode for this player, optionally drop them here\n\tedictnum = (newcl-svs.clients)+1;\n\tswitch (svs.gametype)\n\t{\n#ifdef HLSERVER\n\t\t//fallthrough\n\tcase GT_HALFLIFE:\n\t\t{\n\t\t\tchar reject[128];\n\t\t\tif (!SVHL_ClientConnect(newcl, adr, reject))\n\t\t\t{\n\t\t\t\tSV_RejectMessage(protocol, \"%s\", reject);\n\t\t\t\tCon_TPrintf (\"%s:gamecode reject\\n\", NET_AdrToString (adrbuf, sizeof(adrbuf), &adr));\n\t\t\t\treturn NULL;\n\t\t\t}\n\t\t\ttemp.hledict = newcl->hledict;\n\t\t}\n\n\t\tbreak;\n#endif\n\n\n#ifdef VM_Q1\n\tcase GT_Q1QVM:\n#endif\n#ifdef VM_LUA\n\tcase GT_LUA:\n#endif\n\tcase GT_PROGS:\n\t\tif (info->protocol == SCP_QUAKE2)\n\t\t{\n\t\t\tSV_RejectMessage(info->protocol, \"This is a %s server.\", fs_manifest->formalname);\n\t\t\tCon_DPrintf (\"* Rejected q2 client.\\n\");\n\t\t\treturn;\n\t\t}\n\n\t\tif (svprogfuncs)\n\t\t\tent = EDICT_NUM_UB(svprogfuncs, edictnum);\n\t\telse\n\t\t\tent = NULL;\n#ifdef Q2SERVER\n\t\ttemp.q2edict = NULL;\n#endif\n\t\ttemp.edict = ent;\n\n\t\t{\n\t\t\tconst char *reject = SV_CheckRejectConnection(&info->adr, info->seat[0].info, info->protocol, info->ftepext1, info->ftepext2, info->ezpext1, info->guid);\n\t\t\tif (reject)\n\t\t\t{\n\t\t\t\tSV_RejectMessage(info->protocol, \"%s\", reject);\n\t\t\t\tCon_DPrintf (\"* Game rejected a connection.\\n\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\tbreak;\n\n#ifdef Q2SERVER\n\tcase GT_QUAKE2:\n\t\tif (info->protocol != SCP_QUAKE2 && info->protocol != SCP_QUAKE2EX)\n\t\t{\n\t\t\tSV_RejectMessage(info->protocol, \"This is a %s server.\", fs_manifest->formalname);\n\t\t\tCon_DPrintf (\"* Rejected non-q2 client.\\n\");\n\t\t\treturn;\n\t\t}\n\t\tq2ent = Q2EDICT_NUM(edictnum);\n\t\ttemp.edict = NULL;\n\t\ttemp.q2edict = q2ent;\n\n\t\tif (!ge->ClientConnect(q2ent, info->seat[0].info))\n\t\t{\n\t\t\tconst char *reject = Info_ValueForKey(info->seat[0].info, \"rejmsg\");\n\t\t\tif (*reject)\n\t\t\t\tSV_RejectMessage(info->protocol, \"%s\\nConnection Refused.\", reject);\n\t\t\telse\n\t\t\t\tSV_RejectMessage(info->protocol, \"Connection Refused.\");\n\t\t\tCon_DPrintf (\"Game rejected a connection.\\n\");\n\t\t\treturn;\n\t\t}\n\n\t\tge->ClientUserinfoChanged(q2ent, info->seat[0].info);\n\n\n\t\tbreak;\n#endif\n\tdefault:\n\t\tSys_Error(\"Bad svs.gametype in SVC_DirectConnect\");\n\t\tbreak;\n\t}\n\n\tif (newcl->frameunion.frames)\n\t{\n\t\tCon_Printf(\"Client frame info was set\\n\");\n\t\tZ_Free(newcl->frameunion.frames);\n\t}\n\n\ttemp.name = newcl->name;\n\ttemp.team = newcl->team;\n\n\tInfoSync_Clear(&newcl->infosync);\n\t*newcl = temp;\n\tnewcl->userinfo.ChangeCB = svs.info.ChangeCB;\n\tnewcl->userinfo.ChangeCTX = &newcl->userinfo;\n\tInfoBuf_FromString(&newcl->userinfo, info->seat[0].info, false);\n\n\tnewcl->challenge = info->challenge;\n\tnewcl->zquake_extensions = atoi(InfoBuf_ValueForKey(&newcl->userinfo, \"*z_ext\"));\n\tInfoBuf_SetStarKey(&newcl->userinfo, \"*z_ext\", \"\");\n\tif (*InfoBuf_ValueForKey(&newcl->userinfo, \"*fuhquake\"))\t//fuhquake doesn't claim to support z_ext but does look at our z_ext serverinfo key.\n\t{\t\t\t\t\t\t\t\t\t\t\t\t\t\t//so switch on the bits that it should be sending.\n\t\tnewcl->zquake_extensions |= Z_EXT_PM_TYPE|Z_EXT_PM_TYPE_NEW;\n\t}\n\tnewcl->zquake_extensions &= SERVER_SUPPORTED_Z_EXTENSIONS;\n\n\tncflags = NCF_SERVER;\n\tif (info->mtu)\n\t\tncflags |= NCF_FRAGABLE;\n\tif (info->ftepext2&PEXT2_STUNAWARE)\n\t\tncflags |= NCF_STUNAWARE;\n\tNetchan_Setup (ncflags, &newcl->netchan, &info->adr, info->qport,\n\t\t\t\t\tinfo->mtu?info->mtu:atoi(Info_ValueForKey (info->seat[0].info, \"mtu\")));\n\n#ifdef HUFFNETWORK\n\tif (info->huffcrc)\n\t\tnewcl->netchan.compresstable = Huff_CompressionCRC(info->huffcrc);\n\telse\n#endif\n\t\tnewcl->netchan.compresstable = NULL;\n\n\tnewcl->protocol = info->protocol;\n#ifdef NQPROT\n\tnewcl->netchan.isnqprotocol = ISNQCLIENT(newcl);\n#endif\n\n\tnewcl->state = cs_connected;\n\tnewcl->connection_started = realtime;\n\n#ifdef Q3SERVER\n\tnewcl->gamestatesequence = -1;\n#endif\n\tnewcl->datagram.allowoverflow = true;\n\tnewcl->datagram.data = newcl->datagram_buf;\n\tif (newcl->netchan.flags&NCF_FRAGABLE)\n\t\tnewcl->datagram.maxsize = sizeof(newcl->datagram_buf);\n\telse\n\t\tnewcl->datagram.maxsize = MAX_DATAGRAM;\n\n#ifdef Q2SERVER\n\tif (newcl->protocol == SCP_QUAKE2EX)\n\t{\n\t\tnewcl->netchan.netprim =\n\t\tnewcl->datagram.prim =\n\t\tnewcl->backbuf.prim =\n\t\tnewcl->netchan.message.prim = sv.q2multicast[1].prim;\n\t}\n\telse\n#endif\n\t{\n\t\tnewcl->netchan.netprim =\n\t\tnewcl->datagram.prim =\n\t\tnewcl->backbuf.prim =\n\t\tnewcl->netchan.message.prim = svs.netprim;\n\t}\n\n\tSV_ClientProtocolExtensionsChanged(newcl);\n\n\t// spectator mode can ONLY be set at join time\n\tnewcl->spectator = spectator;\n\n\tnewcl->realip_ping = (((rand()^(rand()<<8) ^ *(int*)&realtime)&0xffffff)<<8) | (newcl-svs.clients);\n\n#ifdef HEXEN2\n\tif (newcl->istobeloaded && newcl->edict)\n\t\tnewcl->playerclass = newcl->edict->xv->playerclass;\n#endif\n\n\t// parse some info from the info strings\n\tSV_ExtractFromUserinfo (newcl, true);\n\n\t// JACK: Init the floodprot stuff.\n\tnewcl->floodprotmessage = 0.0;\n\tnewcl->lastspoke = 0.0;\n\tnewcl->lockedtill = 0;\n\n#ifdef SVRANKING\n//rankid is figured out in extract from user info\n\tif (!newcl->rankid)\t//failed to get a userid\n\t{\n\t\tif (rank_needlogin.value)\n\t\t{\n\t\t\tSV_RejectMessage (info->protocol, \"Bad password/username\\nThis server requires logins. Please see the serverinfo for website and info on how to register.\\n\");\n\t\t\tnewcl->state = cs_free;\n\t\t\treturn;\n\t\t}\n\n//\t\t\tSV_OutOfBandPrintf (isquake2client, adr, \"\\nWARNING: You have not got a place on the ranking system, probably because a user with the same name has already connected and your pwds differ.\\n\\n\");\n\n\t\tif (!preserveparms)\n\t\t\tSV_GetNewSpawnParms(newcl);\n\t}\n\telse\n\t{\n\t\trankstats_t rs;\n\t\tif (!Rank_GetPlayerStats(newcl->rankid, &rs))\n\t\t{\n\t\t\tSV_RejectMessage (info->protocol, \"Rankings/Account system failed\\n\");\n\t\t\tCon_TPrintf(\"banned player %s is trying to connect\\n\", newcl->name);\n\t\t\tnewcl->name[0] = 0;\n\t\t\tInfoBuf_Clear(&newcl->userinfo, true);\n\t\t\tnewcl->state = cs_free;\n\t\t\treturn;\n\t\t}\n\n\t\tif (rs.flags1 & RANK_MUTED)\n\t\t{\n\t\t\tSV_BroadcastTPrintf(PRINT_MEDIUM, \"%s is muted (still)\\n\", newcl->name);\n\t\t}\n\t\tif (rs.flags1 & RANK_CUFFED)\n\t\t{\n\t\t\tSV_BroadcastTPrintf(PRINT_LOW, \"%s is now cuffed permanently\\n\", newcl->name);\n\t\t}\n\t\tif (rs.flags1 & RANK_CRIPPLED)\n\t\t{\n\t\t\tSV_BroadcastTPrintf(PRINT_HIGH, \"%s is still crippled\\n\", newcl->name);\n\t\t}\n\n\t\tif (rs.timeonserver)\n\t\t{\n\t\t\tif (preserveparms)\n\t\t\t{\t//do nothing.\n\t\t\t}\n\t\t\telse if (sv_resetparms.value)\n\t\t\t{\n\t\t\t\tSV_GetNewSpawnParms(newcl);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\textern cvar_t rank_parms_first, rank_parms_last;\n\t\t\t\tfor (i=0 ; i<NUM_SPAWN_PARMS ; i++)\n\t\t\t\t{\n\t\t\t\t\tif (i < NUM_RANK_SPAWN_PARMS && i >= rank_parms_first.ival && i <= rank_parms_last.ival)\n\t\t\t\t\t\tnewcl->spawn_parms[i] = rs.parm[i];\n\t\t\t\t\telse\n\t\t\t\t\t\tnewcl->spawn_parms[i] = 0;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (rs.timeonserver > 3*60)\t//woo. Ages.\n\t\t\t\ts = va(langtext(\"Welcome back %s. You have previously spent %i:%i hours connected\\n\", newcl->language), newcl->name, (int)(rs.timeonserver/(60*60)), (int)((int)(rs.timeonserver/60)%(60)));\n\t\t\telse\t//measure this guy in minuites.\n\t\t\t\ts = va(langtext(\"Welcome back %s. You have previously spent %i mins connected\\n\", newcl->language), newcl->name, (int)(rs.timeonserver/60));\n\n\t\t\tSV_OutOfBandPrintf (ISQ2CLIENT(info), &info->adr, s);\n\t\t}\n\t\telse if (!preserveparms)\n\t\t{\n\t\t\tSV_GetNewSpawnParms(newcl);\n\n\t\t\tSV_OutOfBandTPrintf (ISQ2CLIENT(info), &info->adr, newcl->language, \"Welcome %s. Your time on this server is being logged and ranked\\n\", newcl->name, (int)rs.timeonserver);\n\t\t}\n\t\t//else loaded players already have their initial parms set\n\t}\n#else\n\t// call the progs to get default spawn parms for the new client\n\tif (!preserveparms)\n\t{\n\t\tSV_GetNewSpawnParms(newcl);\n\t}\n#endif\n\n\n\tSV_AcceptMessage (newcl);\n\n\tnewcl->state = cs_free;\n\tif (redirect || preserveparms)\n\t{\n\t}\n\telse if (ISNQCLIENT(newcl))\n\t{\n\t\t//FIXME: we should delay this until we actually have a name, because right now they'll be called unnamed or unconnected or something\n\t\tSV_BroadcastPrintf(PRINT_LOW, \"New client connected\\n\");\n\t}\n\telse if (newcl->spectator)\n\t{\n\t\tSV_BroadcastTPrintf(PRINT_LOW, \"spectator %s connected\\n\", newcl->name);\n//\t\t\tCon_Printf (\"Spectator %s connected\\n\", newcl->name);\n\t}\n\telse\n\t{\n\t\tSV_BroadcastTPrintf(PRINT_LOW, \"client %s connected\\n\", newcl->name);\n//\t\t\tCon_DPrintf (\"Client %s connected\\n\", newcl->name);\n\t}\n\tnewcl->state = cs_connected;\n\tnewcl->sendinfo = true;\n\n\tif (!redirect)\n\t{\n\t\tfor (i = 0; i < sizeof(sv_motd)/sizeof(sv_motd[0]); i++)\n\t\t{\n\t\t\tif (*sv_motd[i].string)\n\t\t\t\tSV_ClientPrintf(newcl, PRINT_CHAT, \"%s\\n\", sv_motd[i].string);\n\t\t}\n\t}\n\n\tSV_CheckRecentCrashes(newcl);\n\n#ifdef VOICECHAT\n\tSV_VoiceInitClient(newcl);\n#endif\n\n\tSV_EvaluatePenalties(newcl);\n\n\tif (newcl->penalties & BAN_SPECONLY)\n\t{\n\t\tif (spectators >= maxspectators.ival)\n\t\t\tnewcl->drop = true;\t//oops.\n\t\tnewcl->spectator = spectator = true;\n\t\tInfoBuf_SetValueForStarKey (&cl->userinfo, \"*spectator\", \"1\");\n\t}\n\n\t//only advertise PEXT_SPLITSCREEN when splitscreen is allowed, to avoid spam. this might mean people need to reconnect after its enabled. oh well.\n\tif (!sv_allow_splitscreen.ival && newcl->netchan.remote_address.type != NA_LOOPBACK)\n\t\tnewcl->fteprotocolextensions &= ~PEXT_SPLITSCREEN;\n\tnewcl->controller = NULL;\n\n\tif (!redirect)\n\t{\n\t\tSys_ServerActivity();\n\n\t\tPIN_ShowMessages(newcl);\n\t}\n\n#ifdef NQPROT\n\tif (ISNQCLIENT(newcl))\n\t{\n\t\tnewcl->netchan.message.maxsize = sizeof(newcl->netchan.message_buf);\n\t\thost_client = newcl;\n\t\tSVNQ_New_f();\n\t}\n#endif\n\n\tnewcl->redirect = redirect;\n\n#ifdef SUBSERVERS\n\tSSV_SavePlayerStats(newcl, 0);\n#endif\n\n#ifdef IPLOG\n\tif (Q_strncasecmp(newcl->name, \"unconnected\", 11) && Q_strncasecmp(newcl->name, \"connecting\", 10))\n\t\tIPLog_Add(NET_AdrToString(adrbuf,sizeof(adrbuf), &newcl->netchan.remote_address), newcl->name);\n#endif\n\n#ifdef NQPROT\n\tnewcl->netchan.incoming_reliable_sequence = info->expectedreliablesequence;\n#endif\n\n\tif (sv_use_dns.ival)\n\t\tNET_AdrToStringResolve(&info->adr, SV_UserDNSResolved, NULL, newcl-svs.clients, newcl->userid);\n\n\tfor (i = 1; i < info->seats; i++)\n\t\tSV_AddSplit(newcl, info->seat[i].info, i);\n}\n\n#ifdef Q2SERVER\nvoid Q2EFixupInfo(void *vctx, const char *key, const char *value)\n{\n\tint seat = -1;\n\tsvconnectinfo_t *ctx = vctx;\n\tint l = strlen(key);\n\tif (l > 2 && key[l-2] == '_' && key[l-1] >= '0' && key[l-1] < '0'+MAX_SPLITS)\n\t{\n\t\tseat = key[l-1]-'0';\n\t\t((char*)key)[l-2] = 0;\t//strip out the annoying _0s\n\t}\n\tif (seat < 0)\n\t{\n\t\tfor(seat = 0; seat < ctx->seats; seat++)\n\t\t\tInfo_SetValueForStarKey(ctx->seat[seat].info, key, value, sizeof(ctx->seat[seat].info));\n\t}\n\telse\n\t\tInfo_SetValueForStarKey(ctx->seat[seat].info, key, value, sizeof(ctx->seat[seat].info));\n}\n#endif\n\n/*\n==================\nSVC_DirectConnect\n\nA connection request that did not come from the master\n==================\narguments must be tokenized first\nQ3: connect \"\\key\\val\"\nDP: connect\\key\\val\nQW: connect $VER $QPORT $CHALLENGE \"\\key\\val\"\nNQ: hacked to take the form of QW, but with protocol version 3.\nUNSUPPORTED FTEQW/Splitscreen: connect2 $VER $QPORT $CHALLENGE \"\\key\\val\" \"\\key\\val\"\nextension flags follow it.\n*/\nvoid SVC_DirectConnect(int expectedreliablesequence)\n{\n\tint\t\t\tversion;\n#ifdef HUFFNETWORK\n\textern cvar_t net_compress;\n#endif\n\n\tsvconnectinfo_t info;\n#ifdef NQPROT\n\textern cvar_t sv_protocol_nq;\n\tinfo.proquakeanglehack = false;\n\tinfo.isqex = false;\n\tinfo.supportedprotocols = 0;\n\tinfo.expectedreliablesequence = expectedreliablesequence;\n#endif\n\n#ifdef HUFFNETWORK\n\tinfo.huffcrc = 0;\n#endif\n\tinfo.mtu = 0;\n\tinfo.ftepext1 = 0;\n\tinfo.ftepext2 = 0;\n\tinfo.ezpext1 = 0;\n\t*info.guid = 0;\n\tinfo.seats = 1;\n\tif (*Cmd_Argv(1) == '\\\\')\n\t{\t//q3: connect \"\\key\\val\"\n#ifndef QWOVERQ3\n\t\tSV_RejectMessage (SCP_QUAKE3, \"This is not a q3 server: %s\\n\", version_string());\n\t\tCon_TPrintf (\"* rejected connect from q3 client\\n\");\n\t\treturn;\n#else\n\t\tconst char\t\t*s;\n\t\t//this is used by q3 (note, we already decrypted the huffman connection packet in a hack)\n\t\tif (!sv_listen_q3.ival)\n\t\t{\n\t\t\tSV_RejectMessage (SCP_QUAKE3, \"Server is not accepting quake3 clients at this time: %s\\n\", version_string());\n\t\t\tCon_TPrintf (\"* rejected connect from q3 client\\n\");\n\t\t\treturn NULL;\n\t\t}\n\t\tnumssclients = 1;\n\t\tprotocol = SCP_QUAKE3;\n\n\t\tQ_strncpyz (userinfo, Cmd_Argv(1), sizeof(userinfo)-1);\n\n\t\tswitch (atoi(Info_ValueForKey(userinfo, \"protocol\")))\n\t\t{\n\t\tcase 68:\t//regular q3 1.32\n\t\t\tbreak;\n//\t\tcase 43:\t//q3 1.11 (most 'recent' demo)\n//\t\t\tbreak;\n\t\tdefault:\n\t\t\tSV_RejectMessage (SCP_BAD, \"Server is %s.\\n\", version_string());\n\t\t\tCon_TPrintf (\"* rejected connect from incompatable client\\n\");\n\t\t\treturn NULL;\n\t\t}\n\n\t\ts = Info_ValueForKey(userinfo, \"challenge\");\n\t\tchallenge = atoi(s);\n\n\t\ts = Info_ValueForKey(userinfo, \"qport\");\n\t\tqport = atoi(s);\n\n\t\ts = Info_ValueForKey(userinfo, \"name\");\n\t\tif (!*s)\n\t\t\tInfo_SetValueForKey(userinfo, \"name\", \"UnnamedQ3\", sizeof(userinfo));\n\n#ifdef HUFFNETWORK\n\t\tinfo.huffcrc = HUFFCRC_QUAKE3;\n#endif\n#endif\n\t}\n#ifdef NQPROT\n\telse if (*(Cmd_Argv(0)+7) == '\\\\')\n\t{\t//DP has the userinfo attached directly to the end of the connect command\n\t\tconst char\t\t*s;\n\t\tif (!sv_listen_dp.value && net_from.type != NA_LOOPBACK)\n\t\t{\n\t\t\tif (!sv_listen_nq.value)\n\t\t\t\tSV_RejectMessage (SCP_DARKPLACES6, \"Server is not accepting darkplaces clients at this time.\\n\", version_string());\n\t\t\tCon_TPrintf (\"* rejected connect from dp client\\n\");\n\t\t\treturn;\n\t\t}\n\t\tif (progstype == PROG_H2)\n\t\t{\n\t\t\tif (!sv_listen_nq.value)\n\t\t\t\tSV_RejectMessage (SCP_DARKPLACES6, \"NQ protocols are not supported with hexen2 gamecode.\\n\", version_string());\n\t\t\tCon_TPrintf (\"* rejected connect from dp client (because of hexen2)\\n\");\n\t\t\treturn;\n\t\t}\n\t\tQ_strncpyz (info.seat[0].info, net_message.data + 11, sizeof(info.seat[0].info)-1);\n\n\t\tif (strcmp(Info_ValueForKey(info.seat[0].info, \"protocol\"), \"darkplaces \"STRINGIFY(NQ_NETCHAN_VERSION)))\n\t\t{\n\t\t\tSV_RejectMessage (SCP_BAD, \"Server is %s.\\n\", version_string());\n\t\t\tCon_TPrintf (\"* rejected connect from incompatible client\\n\");\n\t\t\treturn;\n\t\t}\n\t\t//it's a darkplaces client.\n\n\t\ts = Info_ValueForKey(info.seat[0].info, \"protocols\");\n\n\t\twhile(s && *s)\n\t\t{\n\t\t\tstatic const struct\n\t\t\t{\n\t\t\t\tchar *name;\n\t\t\t\tunsigned int bits;\n\t\t\t} dpnames[] =\n\t\t\t{\n\t\t\t\t{\"FITZ\",\t\t\t1u<<SCP_FITZ666},\t//dp doesn't support this, but this is for potential compat if other engines use this handshake\n\t\t\t\t{\"666\",\t\t\t\t1u<<SCP_FITZ666},\t//dp doesn't support this, but this is for potential compat if other engines use this handshake\n\t\t\t\t{\"RMQ\",\t\t\t\t1u<<SCP_FITZ666},\t//fte doesn't distinguish, but assumes clients will support both\n\t\t\t\t{\"999\",\t\t\t\t1u<<SCP_FITZ666},\t//fte doesn't distinguish, but assumes clients will support both\n\t\t\t\t{\"DP8\",\t\t\t\t0},\t//unsupported, cloudwalk's attempt to fix some stuff (still unfinalised).\n\t\t\t\t{\"DP7\",\t\t\t\t1u<<SCP_DARKPLACES7},\n\t\t\t\t{\"DP6\",\t\t\t\t1u<<SCP_DARKPLACES6},\n\t\t\t\t{\"DP5\",\t\t\t\t0},\t//unsupported serverside\n\t\t\t\t{\"DP4\",\t\t\t\t0},\t//unsupported\n\t\t\t\t{\"DP3\",\t\t\t\t0},\t//unsupported\n\t\t\t\t{\"DP2\",\t\t\t\t0},\t//unsupported\n\t\t\t\t{\"DP1\",\t\t\t\t0},\t//unsupported\n\t\t\t\t{\"QW\",\t\t\t\t0},\t//mixing protocols doesn't make sense, and would just confuse the client.\n\t\t\t\t{\"QUAKEDP\",\t\t\t1u<<SCP_NETQUAKE},\n\t\t\t\t{\"QUAKE\",\t\t\t1u<<SCP_NETQUAKE},\n\t\t\t\t{\"NEHAHRAMOVIE\",\t1u<<SCP_NETQUAKE},\n\t\t\t\t{\"NEHAHRABJP\",\t\t0},\n\t\t\t\t{\"NEHAHRABJP2\",\t\t0},\n\t\t\t\t{\"NEHAHRABJP3\",\t\t1u<<SCP_BJP3},\n\t\t\t\t{\"DP7DP6\",\t\t\t(1u<<SCP_DARKPLACES7)|(1u<<SCP_DARKPLACES6)},\t//stupid shitty buggy crappy client\n\t\t\t\t{\"DP8DP7\",\t\t\t/*(1u<<SCP_DARKPLACES8)|*/(1u<<SCP_DARKPLACES7)},\t//stupid shitty buggy crappy client\n\t\t\t};\n\t\t\tint p;\n\n\t\t\ts = COM_Parse(s);\n\t\t\tfor (p = 0; p < countof(dpnames); p++)\n\t\t\t{\n\t\t\t\tif (!Q_strcasecmp(dpnames[p].name, com_token))\n\t\t\t\t{\n\t\t\t\t\tinfo.supportedprotocols |= dpnames[p].bits;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (p == countof(dpnames))\n\t\t\t\tCon_DPrintf(\"'DP' client reporting unknown protocol \\\"%s\\\"\\n\", com_token);\n\t\t}\n\n\t\tinfo.protocol = SCP_DARKPLACES7;\n\n\t\ts = Info_ValueForKey(info.seat[0].info, \"challenge\");\n\t\tif (!strncmp(s, \"FTE\", strlen(\"FTE\")))\t//cope with our mangling of the challenge.\n\t\t\tinfo.challenge = atoi(s+strlen(\"FTE\"));\n\t\telse\n\t\t\tinfo.challenge = atoi(s);\n\n\t\tInfo_RemoveKey(info.seat[0].info, \"protocol\");\n\t\tInfo_RemoveKey(info.seat[0].info, \"protocols\");\n\t\tInfo_RemoveKey(info.seat[0].info, \"challenge\");\n\n\t\ts = Info_ValueForKey(info.seat[0].info, \"name\");\n\t\tif (!*s)\n\t\t\tInfo_SetValueForKey(info.seat[0].info, \"name\", \"CONNECTING\", sizeof(info.seat[0].info));\n\n\t\tinfo.qport = 0;\n\t\tinfo.proquakeanglehack = false;\t//NOTE: DP clients fuck up here due to a DP client bug.\n\t\t\t\t\t\t\t\t\t//DP clients will use 16bit angles if it has previously connected to a proquake-handshake server,\n\t\t\t\t\t\t\t\t\t//and 8bit angles otherwise (or a non-proquake/non-dp/non-qw server more recently than the proquake one).\n\t}\n#endif\n#ifdef Q2SERVER\n\telse if (atoi(Cmd_Argv(1)) == PROTOCOL_VERSION_Q2EX)\n\t{\n\t\tint i, numseats;\n\t\tinfo.seats = numseats = atoi(Cmd_Argv(2));\n\t\tversion = atoi(Cmd_Argv(1));\n\t\tinfo.protocol = SCP_QUAKE2EX;\n\t\tinfo.seats = bound(1, numseats, countof(info.seat));\n\t\tif (info.seats < 1 || info.seats > MAX_SPLITS)\n\t\t{\t//no splitscreen(yet)\n\t\t\tSV_RejectMessage (info.protocol, \"bad number of seats\\n\", PROTOCOL_VERSION_Q2, version);\n\t\t\tCon_TPrintf (\"* rejected connect from version %i\\n\", version);\n\t\t\treturn;\n\t\t}\n\t\tQ_strncpyz(info.guid, Cmd_Argv(3), sizeof(info.guid));\n\t\t//Socials - Cmd_Argv(3 ... 3+numseats-1); fuck that spyware\n\t\tfor (i = 0; i < info.seats; i++)\n\t\t\t*info.seat[i].info = 0;\n\t\tInfo_Enumerate(va(\"\\\\%s\", Cmd_Argv(3+numseats)), &info, Q2EFixupInfo);\n\n\t\t//hide a challenge in there, in case we're not using the lobby stuff.\n\t\tinfo.challenge = atoi(Info_ValueForKey(info.seat[0].info, \"challenge\"));\n\t\tinfo.qport = atoi(Info_ValueForKey(info.seat[0].info, \"qport\"));\n\t\tfor (i = 0; i < info.seats; i++)\n\t\t{\n\t\t\tInfo_RemoveKey(info.seat[i].info, \"challenge\");\n\t\t\tInfo_RemoveKey(info.seat[i].info, \"qport\");\n\t\t}\n\t}\n#endif\n\telse\n\t{\n\t\t//fte: connectN is no longer supported (multiple userinfos packed into a single packet was a bad idea when userinfos can be so large\n\t\t/*if (atoi(Cmd_Argv(0)+7))\n\t\t{\n\t\t\tint numssclients = atoi(Cmd_Argv(0)+7);\n\t\t\tif (numssclients!=1)\n\t\t\t{\n\t\t\t\tSV_RejectMessage (SCP_BAD, \"Server is %s.\\n\", version_string());\n\t\t\t\tCon_TPrintf (\"* rejected connect from old client\\n\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t}*/\n\n\t\tversion = atoi(Cmd_Argv(1));\n\t\tif (version >= PROTOCOL_VERSION_Q2_MIN && version <= PROTOCOL_VERSION_Q2)\n\t\t\tinfo.protocol = SCP_QUAKE2;\n#ifdef NQPROT\n\t\telse if (version == NQ_NETCHAN_VERSION)\n\t\t{\n\t\t\tinfo.protocol = SCP_NETQUAKE; //because we can\n\t\t\tswitch(atoi(Info_ValueForKey(Cmd_Argv(4), \"mod\")))\n\t\t\t{\n\t\t\tcase 1:\n\t\t\t\tinfo.proquakeanglehack = true;\n\t\t\t\tbreak;\n\t\t\tcase PROTOCOL_VERSION_FITZ:\n\t\t\tcase PROTOCOL_VERSION_RMQ:\n\t\t\t\tinfo.protocol = SCP_FITZ666;\n\t\t\t\tbreak;\n\t\t\tcase PROTOCOL_VERSION_BJP3:\n\t\t\t\tinfo.protocol = SCP_BJP3;\n\t\t\t\tinfo.proquakeanglehack = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\telse if (version == NQ_NETCHAN_VERSION_QEX)\n\t\t{\t//rerelease...\n\t\t\tinfo.protocol = SCP_FITZ666;//NETQUAKE;\n\t\t\tinfo.isqex = true;\n\t\t}\n#endif\n\t\telse if (version == PROTOCOL_VERSION_QW)\n\t\t\tinfo.protocol = SCP_QUAKEWORLD;\n\t\telse\n\t\t{\n\t\t\tSV_RejectMessage (SCP_BAD, \"Server is protocol version %i, received %i\\n\", PROTOCOL_VERSION_QW, version);\n\t\t\tCon_TPrintf (\"* rejected connect from version %i\\n\", version);\n\t\t\treturn;\n\t\t}\n\n\t\tinfo.qport = atoi(Cmd_Argv(2));\n\n\t\tinfo.challenge = atoi(Cmd_Argv(3));\n\n\t\t// note an extra qbyte is needed to replace spectator key\n\t\tQ_strncpyz (info.seat[0].info, Cmd_Argv(4), sizeof(info.seat[0].info)-1);\n\t\tif (info.protocol >= SCP_NETQUAKE)\n\t\t{\n\t\t\tInfo_RemoveKey(info.seat[0].info, \"mod\");\t//its served its purpose.\n\t\t\tInfo_RemoveKey(info.seat[0].info, \"modver\");\t//its served its purpose.\n\t\t\tInfo_RemoveKey(info.seat[0].info, \"flags\");\t//its served its purpose.\n\t\t}\n\t}\n\n#ifdef HAVE_DTLS\n\tif (net_enable_dtls.ival > 2 && (net_from.prot == NP_DGRAM || net_from.prot == NP_STREAM || net_from.prot == NP_WS) && net_from.type != NA_LOOPBACK && !NET_IsEncrypted(&net_from))\n\t{\n\t\tSV_RejectMessage (info.protocol, \"This server requires the use of DTLS/TLS/WSS.\\n\");\n\t\treturn;\n\t}\n#endif\n\n\t{\n\t\tchar *banreason = SV_BannedReason(&net_from);\n\t\tif (banreason)\n\t\t{\n\t\t\tif (*banreason)\n\t\t\t\tSV_RejectMessage (info.protocol, \"You were banned.\\nReason: %s\\n\", banreason);\n\t\t\telse\n\t\t\t\tSV_RejectMessage (info.protocol, \"You were banned.\\n\");\n\t\t\treturn;\n\t\t}\n\t}\n\n\tif (info.protocol == SCP_QUAKEWORLD)\t//readd?\n\t{\n\t\tif (!sv_listen_qw.value && net_from.type != NA_LOOPBACK)\n\t\t{\n\t\t\tSV_RejectMessage (info.protocol, \"QuakeWorld protocols are not permitted on this server.\\n\");\n\t\t\tCon_TPrintf (\"* rejected connect from quakeworld\\n\");\n\t\t\treturn;\n\t\t}\n\t}\n\n\t// see if the challenge is valid.\n\tif (net_from.type == NA_LOOPBACK)\t//normal rules don't apply\n\t\t;\n\telse if (net_from.prot != NP_DGRAM)\n\t\t;\t//challenge checks are irrelevant when we've alread passed a challenge in a lower network layer.\n\telse\n\t{\n\t\tif (!SV_ChallengePasses(info.challenge))\n\t\t{\n\t\t\tif (sv_listen_dp.ival && !info.challenge && info.protocol == SCP_QUAKEWORLD)\n\t\t\t{\n\t\t\t\t//dp replies with 'challenge'. which vanilla quakeworld interprets as: c<CHALLENGEID><ignored junk 'hallenge'>\n\t\t\t\t//so just silence that error.\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tSV_RejectMessage (info.protocol, \"Bad challenge.\\n\");\n\t\t\treturn;\n\t\t}\n\t}\n\n\tif (sv_banproxies.ival)\n\t{\n\t\t//FIXME: allow them to spectate but not join\n\t\tif (*Info_ValueForKey(info.seat[0].info, \"*qwfwd\"))\n\t\t{\n\t\t\tSV_RejectMessage (info.protocol, \"Proxies are not permitted on this server.\\n\");\n\t\t\tCon_TPrintf (\"* rejected connect from qwfwd proxy\\n\");\n\t\t\treturn;\n\t\t}\n\t\tif (*Info_ValueForKey(info.seat[0].info, \"Qizmo\"))\n\t\t{\n\t\t\tSV_RejectMessage (info.protocol, \"Proxies are not permitted on this server.\\n\");\n\t\t\tCon_TPrintf (\"* rejected connect from qizmo proxy\\n\");\n\t\t\treturn;\n\t\t}\n\t\tif (*Info_ValueForKey(info.seat[0].info, \"*qtv\"))\n\t\t{\n\t\t\tSV_RejectMessage (info.protocol, \"Proxies are not permitted on this server.\\n\");\n\t\t\tCon_TPrintf (\"* rejected connect from qtv proxy (udp)\\n\");\n\t\t\treturn;\n\t\t}\n\t}\n\n\twhile(!msg_badread)\n\t{\n\t\tCmd_TokenizeString(MSG_ReadStringLine(), false, false);\n\t\tswitch(Q_atoi(Cmd_Argv(0)))\n\t\t{\n\t\tcase PROTOCOL_VERSION_FTE1:\n\t\t\tif (info.protocol == SCP_QUAKEWORLD || info.protocol == SCP_QUAKE2)\n\t\t\t{\n\t\t\t\tinfo.ftepext1 = Q_atoi(Cmd_Argv(1));\n\t\t\t\tCon_DPrintf(\"Client supports 0x%x fte extensions\\n\", info.ftepext1);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase PROTOCOL_VERSION_FTE2:\n\t\t\tif (info.protocol == SCP_QUAKEWORLD)\n\t\t\t{\n\t\t\t\tinfo.ftepext2 = Q_atoi(Cmd_Argv(1));\n\t\t\t\tCon_DPrintf(\"Client supports 0x%x fte2 extensions\\n\", info.ftepext2);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase PROTOCOL_VERSION_EZQUAKE1:\n\t\t\tif (info.protocol == SCP_QUAKEWORLD)\n\t\t\t{\n\t\t\t\tinfo.ezpext1 = Q_atoi(Cmd_Argv(1));\n\t\t\t\tCon_DPrintf(\"Client supports 0x%x ez1 extensions\\n\", info.ezpext1);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase PROTOCOL_VERSION_HUFFMAN:\n#ifdef HUFFNETWORK\n\t\t\tinfo.huffcrc = Q_atoi(Cmd_Argv(1));\n\t\t\tCon_DPrintf(\"Client supports huffman compression. crc 0x%x\\n\", info.huffcrc);\n\t\t\tif (!net_compress.ival || !Huff_CompressionCRC(info.huffcrc))\n\t\t\t{\n\t\t\t\tSV_RejectMessage (info.protocol, \"Compression should not have been enabled.\\n\");\t//buggy/exploiting client. can also happen from timing when changing the setting, but whatever\n\t\t\t\tCon_TPrintf (\"* rejected - bad compression state\\n\");\n\t\t\t\treturn;\n\t\t\t}\n#endif\n\t\t\tbreak;\n\t\tcase PROTOCOL_VERSION_FRAGMENT:\n\t\t\tinfo.mtu = Q_atoi(Cmd_Argv(1)) & ~7;\n\t\t\tif (info.mtu < 64)\n\t\t\t\tinfo.mtu = 0;\n\t\t\tCon_DPrintf(\"Client supports fragmentation. mtu %i.\\n\", info.mtu);\n\t\t\tbreak;\n\t\tcase PROTOCOL_INFO_GUID:\n\t\t\tQ_strncpyz(info.guid, Cmd_Argv(1), sizeof(info.guid));\n\t\t\tCon_DPrintf(\"GUID %s\\n\", Cmd_Argv(1));\n\t\t\tbreak;\n\t\t}\n\t}\n\tmsg_badread=false;\n\n\tinfo.adr = net_from;\n\tif (MSV_ClusterLogin(&info))\n\t\treturn;\n\n\tSV_DoDirectConnect(&info);\n}\n\nstatic int dehex(int i)\n{\n\tif      (i >= '0' && i <= '9')\n\t\treturn (i-'0');\n\telse if (i >= 'A' && i <= 'F')\n\t\treturn (i-'A'+10);\n\telse\n\t\treturn (i-'a'+10);\n}\nstatic qboolean Rcon_Validate (void)\n{\n\t/*\n\t   The rcon protocol sucks.\n\t   1) vanilla sent it plain text\n\t   2) there's no challenge, so there's no way to block spoofed requests\n\t   3) the hashed version of the protocol still has no challenge\n\t*/\n\tconst char *realpass = rcon_password.string;\n\tconst char *pass = Cmd_Argv(1);\n\tif (!strlen (realpass))\n\t\treturn false;\n\n\tif (!sv_crypt_rcon.ival || !*sv_crypt_rcon.string)\n\t{\t//vanilla-compatible\n\t\tif (!strcmp (pass, realpass) )\n\t\t\treturn true;\n\t}\n\tif (sv_crypt_rcon.ival || !*sv_crypt_rcon.string)\n\t{\t//ezquake-compatible\n\t\t//the password arg is \"[SHA1Digest][unixtime-in-hex]\" where the digest is \"[arg0] password[unixtime] arg0 arg1 argn \"\n\t\ttime_t clienttime = 0;\n\t\ttime_t servertime = 0;\n\t\tintptr_t timediff;\n\t\tqbyte b;\n\n\t\tconst hashfunc_t *hashfunc = &hash_sha1;\n\t\tvoid *hashctx = alloca(hashfunc->contextsize);\n\n\t\tconst size_t digestsize = 20;\n\t\tsize_t i, k;\n\t\tunsigned char digest[512];\n\t\tconst unsigned char **tokens = alloca(sizeof(*tokens)*(Cmd_Argc()*2+5));\t//overallocation in case argc is 0.\n\t\tif (strlen(pass) > digestsize*2)\n\t\t{\n\t\t\tfor (i = 0; pass[digestsize*2+i] && i < sizeof(time_t)*2; i++)\n\t\t\t{\t//mixed endian for compat with ezquake\n\t\t\t\tif (!(i & 1))\n\t\t\t\t\tclienttime |= dehex(pass[digestsize*2+i]) << (((i/2)*8)+4);\n\t\t\t\telse\n\t\t\t\t\tclienttime |= dehex(pass[digestsize*2+i]) << (((i/2)*8)+0);\n\t\t\t}\n\t\t\ttime(&servertime);\n\t\t\ttimediff = servertime-clienttime;\n\t\t\t//make sure the client's time is within our allowed bounds, to prevent (extreme) replay attacks.\n\t\t\tif (!sv_crypt_rcon_clockskew.value || (timediff >= -sv_crypt_rcon_clockskew.ival && timediff <= sv_crypt_rcon_clockskew.ival))\n\t\t\t{\n\t\t\t\ttokens[0] = Cmd_Argv(0);\n\t\t\t\ttokens[1] = \" \";\n\t\t\t\ttokens[2] = realpass;\n\t\t\t\ttokens[3] = pass+digestsize*2;\n\t\t\t\ttokens[4] = \" \";\n\t\t\t\tfor (i = 0; i < Cmd_Argc()-2; i++)\n\t\t\t\t{\n\t\t\t\t\ttokens[5+i*2+0] = Cmd_Argv(i+2);\n\t\t\t\t\ttokens[5+i*2+1] = \" \";\t//a trailing space is required.\n\t\t\t\t}\n\t\t\t\thashfunc->init(hashctx);\n\t\t\t\tfor (k = 0; k < 5+i*2; k++)\n\t\t\t\t\thashfunc->process(hashctx, tokens[k], strlen(tokens[k]));\n\t\t\t\thashfunc->terminate(digest, hashctx);\n\n\t\t\t\tfor (i = 0;;i++)\n\t\t\t\t{\n\t\t\t\t\tif (i == digestsize)\n\t\t\t\t\t\treturn true;\n\t\t\t\t\tif (!pass[i*2+0] || !pass[i*2+1])\n\t\t\t\t\t\tbreak;\t//premature termination\n\t\t\t\t\tb = dehex(pass[i*2+0])*16+dehex(pass[i*2+1]);\n\t\t\t\t\tif (b != digest[i])\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn false;\n}\n\n/*\n===============\nSVC_RemoteCommand\n\nA client issued an rcon command.\nShift down the remaining args\nRedirect all printfs\n===============\n*/\nvoid SVC_RemoteCommand (void)\n{\n\tint\t\ti;\n\tchar\tremaining[1024];\n\tchar\tadr[MAX_ADR_SIZE];\n\tstatic unsigned int blockuntil;\n\tunsigned int curtime, inc = 1000/sv_rconlim.value;\n\n\t{\n\t\tchar *br = SV_BannedReason(&net_from);\n\t\tif (br)\n\t\t{\n\t\t\tCon_TPrintf (\"Rcon from banned ip %s: %s\\n\", NET_AdrToString (adr, sizeof(adr), &net_from), br);\n\t\t\treturn;\n\t\t}\n\t}\n\n\tif (sv_rconlim.value > 0)\n\t{\n\t\tcurtime = Sys_Milliseconds();\n\t\tif (1000 < curtime - blockuntil)\n\t\t\tblockuntil = curtime - 1000;\n\t\tif (inc > curtime-blockuntil)\n\t\t\treturn;\t//throttle\n\t}\n\n\tif (!Rcon_Validate())\n\t{\n\t\tblockuntil += inc;\n/*\n#ifdef SVRANKING\n\t\tif (cmd_allowaccess.value)\t//try and find a username, match the numeric password\n\t\t{\n\t\t\tint rid;\n\t\t\tchar *s = Cmd_Argv(1);\n\t\t\tchar *colon=NULL, *c2;\n\t\t\trankstats_t stats;\n\t\t\tc2=s;\n\t\t\tfor(;;)\n\t\t\t{\n\t\t\t\tc2 = strchr(c2, ':');\n\t\t\t\tif (!c2)\n\t\t\t\t\tbreak;\n\t\t\t\tcolon = c2;\n\t\t\t\tc2++;\n\t\t\t}\n\t\t\tif (colon)\t//oh, could this be a specific username?\n\t\t\t{\n\t\t\t\t*colon = '\\0';\n\t\t\t\tcolon++;\n\t\t\t\trid = Rank_GetPlayerID(NULL, s, atoi(colon), false, true);\n\t\t\t\tif (rid)\n\t\t\t\t{\n\t\t\t\t\tif (!Rank_GetPlayerStats(rid, &stats))\n\t\t\t\t\t\treturn;\n\n\n\t\t\t\t\tCon_TPrintf (\"Rcon from %s:\\n%s\\n\"\n\t\t\t\t\t\t, NET_AdrToString (adr, sizeof(adr), &net_from), net_message.data+4);\n\n\t\t\t\t\tSV_BeginRedirect (RD_PACKET_LOG, svs.language);\n\n\t\t\t\t\tremaining[0] = 0;\n\n\t\t\t\t\tfor (i=2 ; i<Cmd_Argc() ; i++)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (strlen(remaining)+strlen(Cmd_Argv(i))>=sizeof(remaining)-2)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tCon_TPrintf(\"Rcon was too long\\n\");\n\t\t\t\t\t\t\tSV_EndRedirect ();\n\t\t\t\t\t\t\tCon_TPrintf (\"Rcon from %s:\\n%s\\n\"\n\t\t\t\t\t\t\t\t, NET_AdrToString (adr, sizeof(adr), &net_from), \"Was too long - possible buffer overflow attempt\");\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tstrcat (remaining, Cmd_Argv(i) );\n\t\t\t\t\t\tstrcat (remaining, \" \");\n\t\t\t\t\t}\n\n\t\t\t\t\tCmd_ExecuteString (remaining, stats.trustlevel);\n\n\t\t\t\t\tSV_EndRedirect ();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n#endif\n*/\n\n\t\tLog_String(LOG_RCON, va(\"Bad rcon from %s:\\t%s\\n\"\n\t\t\t, NET_AdrToString (adr, sizeof(adr), &net_from), net_message.data+4));\n\n\t\tCon_TPrintf (\"Bad rcon from %s:\\t%s\\n\"\n\t\t\t, NET_AdrToString (adr, sizeof(adr), &net_from), net_message.data+4);\n\n\t\tif (1)\n\t\t\treturn;\n\n\t\tSV_BeginRedirect (RD_PACKET, com_language);\n\n\t\tCon_TPrintf (\"Bad rcon_password. Passwords might be logged. Be careful.\\n\");\n\t}\n\telse\n\t{\n\t\t//make sure stuff is flushed\n\t\tcmd_blockwait = true;\n\t\tCbuf_ExecuteLevel(rcon_level.ival);\n\t\tcmd_blockwait = false;\n\n\t\tLog_String(LOG_RCON, va(\"\\n\\nRcon from %s:\\t%s\\n\"\n\t\t\t, NET_AdrToString (adr, sizeof(adr), &net_from), net_message.data+4));\n\n\t\tCon_TPrintf (\"Rcon from %s:\\t%s\\n\"\n\t\t\t, NET_AdrToString (adr, sizeof(adr), &net_from), net_message.data+4);\n\n\t\tSV_BeginRedirect (RD_PACKET_LOG, com_language);\n\n\t\tremaining[0] = 0;\n\n\t\tfor (i=2 ; i<Cmd_Argc() ; i++)\n\t\t{\n\t\t\tif (strlen(remaining)+strlen(Cmd_Argv(i))>=sizeof(remaining)-2)\n\t\t\t{\n\t\t\t\tCon_TPrintf(\"Rcon was too long\\n\");\n\t\t\t\tSV_EndRedirect ();\n\t\t\t\tCon_TPrintf (\"Rcon from %s:\\t%s\\n\"\n\t\t\t\t\t, NET_AdrToString (adr, sizeof(adr), &net_from), \"Was too long - possible buffer overflow attempt\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tQ_strncatz(remaining, Cmd_Argv(i), sizeof(remaining));\n\t\t\tQ_strncatz(remaining, \" \", sizeof(remaining));\n\t\t}\n\n\t\t//make sure the wait command can't be used to fuck up our logs.\n\t\tcmd_blockwait = true;\n\t\tCbuf_AddText(remaining, rcon_level.ival);\n\t\tCbuf_ExecuteLevel(rcon_level.ival);\n\t\tcmd_blockwait = false;\n\t}\n\n\tSV_EndRedirect ();\n}\n\nvoid SVC_RealIP (void)\n{\n\tunsigned int slotnum;\n\tint cookie;\n\tchar *banreason;\n\tchar adr[MAX_ADR_SIZE];\n\n\tslotnum = atoi(Cmd_Argv(1));\n\tcookie = atoi(Cmd_Argv(2));\n\n\tif (slotnum >= svs.allocated_client_slots)\n\t{\n\t\t//a malitious user\n\t\treturn;\n\t}\n\n\tif (cookie != svs.clients[slotnum].realip_num)\n\t{\n\t\t//could be someone trying to kick someone else\n\t\t//so we can't kick, as much as we might like to.\n\t\treturn;\n\t}\n\n\tif (svs.clients[slotnum].realip_status)\n\t\treturn;\n\n\tif (NET_AddressSmellsFunny(&net_from))\n\t{\n\t\tCon_TPrintf(\"funny realip address: %s, \", NET_AdrToString(adr, sizeof(adr), &net_from));\n\t\tCon_TPrintf(\"proxy address: %s\\n\", NET_AdrToString(adr, sizeof(adr), &svs.clients[slotnum].netchan.remote_address));\n\t\treturn;\n\t}\n\n\tbanreason = SV_BannedReason(&net_from);\n\tif (banreason)\n\t{\n\t\tCon_TPrintf(\"%s has a banned realip\\n\", svs.clients[slotnum].name);\n\t\tif (*banreason)\n\t\t\tSV_ClientTPrintf(&svs.clients[slotnum], PRINT_CHAT, \"You were banned.\\nReason: %s\\n\", banreason);\n\t\telse\n\t\t\tSV_ClientTPrintf(&svs.clients[slotnum], PRINT_CHAT, \"You were banned.\\n\");\n\t\tSV_DropClient(&svs.clients[slotnum]);\n\t\treturn;\n\t}\n\n\tsvs.clients[slotnum].realip_status = 1;\n\tsvs.clients[slotnum].realip = net_from;\n}\n\nvoid SVC_ACK (void)\n{\n\tint slotnum;\n\tchar adr[MAX_ADR_SIZE];\n\n\tfor (slotnum = 0; slotnum < svs.allocated_client_slots; slotnum++)\n\t{\n\t\tif (svs.clients[slotnum].state)\n\t\t{\n\t\t\tif (svs.clients[slotnum].realip_status == 1 && NET_CompareAdr(&svs.clients[slotnum].realip, &net_from))\n\t\t\t{\n\t\t\t\tif (!*Cmd_Argv(1))\n\t\t\t\t\tsvs.clients[slotnum].realip_status = 2;\n\t\t\t\telse if (atoi(Cmd_Argv(1)) == svs.clients[slotnum].realip_ping &&\n\t\t\t\t\t\tatoi(Cmd_Argv(2)) == svs.clients[slotnum].realip_num)\n\t\t\t\t{\n\t\t\t\t\tsvs.clients[slotnum].realip_status = 3;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tNetchan_OutOfBandPrint(NCF_SERVER, &net_from, \"realip not accepted. Please stop hacking.\\n\");\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n\tCon_TPrintf (S_COLOR_GRAY\"A2A_ACK from %s\\n\", NET_AdrToString (adr, sizeof(adr), &net_from));\n}\n\n#ifdef SUPPORT_ICE\nvoid SVC_ICE_Offer(void);\nvoid SVC_ICE_Candidate(void);\n#endif\n\n//returns false to block replies\n//this is to mitigate wasted bandwidth if we're used as a udp amplification\nqboolean SVC_ThrottleInfo (void)\n{\n#define THROTTLE_PPS 20\n\tstatic unsigned int blockuntil;\n\tunsigned int curtime, inc = 1000/THROTTLE_PPS;\n\n\tif (SV_Master_AddressIsMaster(&net_from))\n\t\treturn true; //allow it without contributing to any throttling.\n\n\tcurtime = Sys_Milliseconds();\n\n\t/*don't go too far back*/\n\t//if (blockuntil < curtime - 1000)\n\tif (1000 < curtime - blockuntil)\n\t\tblockuntil = curtime - 1000;\n\n\t/*don't allow it to go beyond curtime or we get issues with the logic above*/\n\tif (inc > curtime-blockuntil)\n\t\treturn false;\n\n\tblockuntil += inc;\n\treturn true;\n}\n\n//more aggressive logic.\nstatic struct attacker_s\n{\n\tint af;\n\tqbyte addr[16];\n\n\tqboolean blocked;\t//if we reach 10 status requests within 30 secs\n\tdouble timeout;\n\tint count;\n} *dosattacker;\nstatic size_t dosattacker_count;\nstatic size_t dosattacker_max;\n#define dosattacker_limit 15\t\t\t\t//if we get X packets\n#define dosattacker_period 30\t\t\t\t\t//within Y secs\n#define dosattacker_blocktime (60*60*24)\t//block them for Z secs (24 hours).\nstatic qboolean SV_DetectAmplificationDDOS (void)\n{\n\tsize_t at;\n\tdouble t = Sys_DoubleTime();\n\tint as;\n\tswitch(net_from.type)\t//trying to be efficient and avoiding net_comparebaseaddr\n\t{\n\tcase NA_IP:\t\tas = 4;\tbreak;\n\tcase NA_IPX:\tas = 10;break;\n\tcase NA_IPV6:\tas = 16;break;\n\tdefault:\t\tas = 0;\tbreak;\n\t}\n\tif (as)\n\t{\n\t\tfor (at = 0; at < dosattacker_count; at++)\n\t\t{\t//look for an existing one\n\t\t\tif (net_from.type != dosattacker->af)\n\t\t\t\tcontinue;\n\t\t\tif (!memcmp(dosattacker[at].addr, &net_from.address, as))\n\t\t\t{\t//a match.\n\t\t\t\tif (t > dosattacker[at].timeout)\n\t\t\t\t{\t//they survived, for now...\n\t\t\t\t\tdosattacker[at].count = 0;\n\t\t\t\t\tdosattacker[at].timeout = t + dosattacker_period;\n\t\t\t\t}\n\t\t\t\tif (++dosattacker[at].count >= dosattacker_limit)\n\t\t\t\t{\n\t\t\t\t\tif (dosattacker[at].count == dosattacker_limit)\n\t\t\t\t\t{\n\t\t\t\t\t\tchar buf[128];\n\t\t\t\t\t\tCon_Printf(CON_ERROR \"%s: Presumed amplification ddos attack, blocking further status queries.\\n\", NET_BaseAdrToString(buf, sizeof(buf), &net_from));\n\t\t\t\t\t\tQ_snprintfz(buf, sizeof(buf), \"\\xff\\xff\\xff\\xff%cProbable ddos amplification attack\\n\", A2C_PRINT);\n\t\t\t\t\t\tNET_SendPacket(svs.sockets, strlen(buf), buf, &net_from);\n\n\t\t\t\t\t\tdosattacker[at].timeout = t + dosattacker_blocktime;\t//a 24 hour block.\n\t\t\t\t\t}\n\t\t\t\t\telse\t//extend by a smidge...\n\t\t\t\t\t\tdosattacker[at].timeout += dosattacker_period/(double)dosattacker_limit;\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (at == dosattacker_count)\n\t\t{\t//didn't find one\n\t\t\tfor (at = 0; at < dosattacker_count; at++)\n\t\t\t{\t//try to find one to recycle\n\t\t\t\tif (t > dosattacker[at].timeout)\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (at == dosattacker_count)\n\t\t\t{\n\t\t\t\tif (at == dosattacker_max)\n\t\t\t\t{\n\t\t\t\t\tif (at > 4096)\t//should we be using hash tables?...\n\t\t\t\t\t\tat--;\t//stomp on the last.\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tZ_ReallocElements((void**)&dosattacker, &dosattacker_max, max(16, dosattacker_count * 2), sizeof(dosattacker[0]));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tat = dosattacker_count++;\n\t\t\t}\n\t\t\tdosattacker[at].af = net_from.type;\n\t\t\tmemcpy(dosattacker[at].addr, &net_from.address, as);\n\t\t\tdosattacker[at].count = 0;\n\t\t\tdosattacker[at].timeout = t + dosattacker_period;\n\t\t}\n\t}\n\n\tif (SVC_ThrottleInfo())\n\t\treturn true;\n\treturn false;\n}\n\n/*\n=================\nSV_ConnectionlessPacket\n\nA connectionless packet has four leading 0xff\ncharacters to distinguish it from a game channel.\nClients that are in the game can still send\nconnectionless packets.\n=================\n*/\nqboolean SV_ConnectionlessPacket (void)\n{\n\tchar\t*s;\n\tchar\t*c;\n\tchar\tadr[MAX_ADR_SIZE];\n\n\tMSG_BeginReading (&net_message, svs.netprim);\n\n\tif (net_message.cursize >= MAX_QWMSGLEN)\t//add a null term in message space\n\t{\n\t\tCon_TPrintf(\"Oversized packet from %s\\n\", NET_AdrToString (adr, sizeof(adr), &net_from));\n\t\tnet_message.cursize=MAX_QWMSGLEN-1;\n\t}\n\tnet_message.data[net_message.cursize] = '\\0';\t//terminate it properly. Just in case.\n\n\tMSG_ReadLong ();\t\t// skip the -1 marker\n\n\ts = MSG_ReadStringLine ();\n\n\tCmd_TokenizeString (s, false, false);\n\n\tc = Cmd_Argv(0);\n\n\tif (sv_showconnectionlessmessages.ival)\n\t\tCon_Printf(S_COLOR_GRAY\"%s: %s\\n\", NET_AdrToString (adr, sizeof(adr), &net_from), s);\n\n\tif (!strcmp(c, \"ping\") || ( c[0] == A2A_PING && (c[1] == 0 || c[1] == '\\n')) )\n\t{\t//only continue respond to these if we're actually public. qwfwd likes spamming us endlessly even if we stop heartbeating (which leaves us discoverable to others, too).\n\t\tif (sv_public.ival >= 0)\n\t\t\tSVC_Ping ();\n\t}\n\telse if (c[0] == A2A_ACK && (c[1] == 0 || c[1] == '\\n') )\n\t\tSVC_ACK ();\n\telse if (!strcmp(c,\"status\"))\n\t{\n\t\tif (sv_public.ival >= 0)\n\t\t\tif (SV_DetectAmplificationDDOS())\n\t\t\t\tSVC_Status ();\n\t}\n\telse if (!strcmp(c,\"log\"))\n\t{\n\t\tif (SVC_ThrottleInfo())\n\t\t\tSVC_Log ();\n\t}\n#ifdef Q2SERVER\n\telse if (!strcmp(c, \"info\"))\n\t{\n\t\tif (sv_public.ival >= 0)\n\t\t\tif (SV_DetectAmplificationDDOS())\n\t\t\t\tSVC_InfoQ2 ();\n\t}\n#endif\n\telse if (!strncmp(c,\"connect\", 7))\n\t{\n#ifdef HAVE_DTLS\n\t\tif (net_from.prot == NP_DGRAM)\n\t\t\tNET_DTLS_Disconnect(svs.sockets, &net_from);\n#endif\n\n#ifdef Q3SERVER\n\t\tif (svs.gametype == GT_QUAKE3)\n\t\t{\n\t\t\tq3->sv.DirectConnect(&net_from, &net_message);\n\t\t\treturn true;\n\t\t}\n\n#ifdef QWOVERQ3\n\t\tif (sv_listen_q3.ival)\n\t\t{\n\t\t\tif (!strstr(s, \"\\\\name\\\\\"))\n\t\t\t{\t//if name isn't in the string, assume they're q3\n\t\t\t\t//this isn't quite true though, hence the listen check. but users shouldn't be connecting with an empty name anyway. more fool them.\n#ifdef HUFFNETWORK\n\t\t\t\tHuff_DecryptPacket(&net_message, 12);\n#endif\n\t\t\t\tMSG_BeginReading(svs.netprim);\n\t\t\t\tMSG_ReadLong();\n\t\t\t\ts = MSG_ReadStringLine();\n\t\t\t\tCmd_TokenizeString(s, false, false);\n\t\t\t}\n\t\t}\n#endif\n#endif\n\n\t\tif (secure.value)\t//FIXME: possible problem for nq clients when enabled\n\t\t{\n\t\t\tNetchan_OutOfBandTPrintf (NCF_SERVER, &net_from, com_language, \"%c\\nThis server requires client validation.\\nPlease use the \"FULLENGINENAME\" validation program\\n\", A2C_PRINT);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tSVC_DirectConnect (0);\n\t\t\treturn true;\n\t\t}\n\t}\n\telse if (!strcmp(c,\"dtlsconnect\"))\n\t{\t//NOTE: redundant. if the server has dtls enabled then it'll respond to dtls hellos with its own stateless cookies\n#ifdef HAVE_DTLS\n\t\tif (net_from.prot == NP_DGRAM && (net_enable_dtls.ival /*|| !*net_enable_dtls.ival*/))\n\t\t{\n\t\t\tif (*Cmd_Argv(2))\n\t\t\t\tSV_RejectMessage (SCP_QUAKEWORLD, \"Proxying not enabled.\\n\");\t//server would be expected to getchallenge+dtlsconnect the target server (or respond with a no-dtls challenge response...)\n\t\t\telse if (SV_ChallengePasses(atoi(Cmd_Argv(1))))\n\t\t\t{\n\t\t\t\tchar *banreason = SV_BannedReason(&net_from);\n\t\t\t\tif (banreason)\n\t\t\t\t{\n\t\t\t\t\tif (*banreason)\n\t\t\t\t\t\tSV_RejectMessage (SCP_QUAKEWORLD, \"You were banned.\\nReason: %s\\n\", banreason);\n\t\t\t\t\telse\n\t\t\t\t\t\tSV_RejectMessage (SCP_QUAKEWORLD, \"You were banned.\\n\");\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t//NET_DTLS_Disconnect(svs.sockets, &net_from);\n\t\t\t\t\tif (NET_DTLS_Create(svs.sockets, &net_from, NULL, false))\n\t\t\t\t\t\tNetchan_OutOfBandPrint(NCF_SERVER, &net_from, \"dtlsopened\");\n\t\t\t\t\telse\n\t\t\t\t\t\tSV_RejectMessage (SCP_QUAKEWORLD, \"DTLS driver failure.\\n\");\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t\tSV_RejectMessage (SCP_QUAKEWORLD, \"Bad challenge.\\n\");\n\t\t}\n\t\treturn true;\n#endif\n\t}\n\t/*else if (!strcmp(c,\"\\xad\\xad\\xad\\xad\"\"getchallenge\"))\n\t{\n\t\tSVC_GetChallenge (false);\n\t}*/\n\telse if (!strcmp(c,\"getchallenge\"))\n\t{\n\t\t//qw+q2 sends \"\\xff\\xff\\xff\\xffgetchallenge\\n\"\n\t\t//dp+q3 sends \"\\xff\\xff\\xff\\xffgetchallenge\"\n\t\t//ioq3 sends \"\\xff\\xff\\xff\\xffgetchallenge <clientchallenge> <$com_gamename>\"\n\t\t//its a subtle difference, but means we can avoid wasteful spam for real qw clients.\n\t\tSVC_GetChallenge ((net_message.cursize==16)?true:false);\n\t}\n\telse if (!strcmp(c, \"getstatus\"))\n\t{\t//q3/dpmaster support\n\t\tif (sv_public.ival >= 0)\n\t\t\tif (SV_DetectAmplificationDDOS())\n\t\t\t\tSVC_GetInfo(Cmd_Args(), true);\n\t}\n\telse if (!strcmp(c, \"getinfo\"))\n\t{\t//q3/dpmaster support\n\t\tif (sv_public.ival >= 0)\n\t\t{\n\t\t\tconst char *chal = Cmd_Argv(1);\n\t\t\tSV_Master_HeartbeatResponse(&net_from, chal);\n\n\t\t\tif (SV_DetectAmplificationDDOS())\n\t\t\t\tSVC_GetInfo(chal, false);\n\t\t}\n\t}\n\telse if (!strcmp(c, \"rcon\"))\n\t{\n\t\tif (SV_DetectAmplificationDDOS())\n\t\t\tSVC_RemoteCommand ();\n\t}\n\telse if (!strcmp(c, \"realip\") || !strcmp(c, \"ip\"))\n\t\tSVC_RealIP ();\n\n#ifdef SUPPORT_ICE\n\telse if (!strcmp(c, \"ice_offer\"))\n\t\tSVC_ICE_Offer();\n\telse if (!strcmp(c, \"ice_ccand\"))\n\t\tSVC_ICE_Candidate();\n#endif\n/*\n\telse if (!strcmp(c,\"lastscores\"))\n\t{\n\t\tif (SVC_ThrottleInfo())\n\t\t\tSVC_LastScores ();\n\t}\n\telse if (!strcmp(c,\"dlist\") || !strcmp(c,\"demolist\"))\n\t{\n\t\tif (SVC_ThrottleInfo())\n\t\t\tSVC_DemoList ();\n\t}\n\telse if (!strcmp(c,\"dlistr\") || !strcmp(c,\"dlistregex\") || !strcmp(c,\"demolistr\") || !strcmp(c,\"demolistregex\"))\n\t{\n\t\tif (SVC_ThrottleInfo())\n\t\t\tSVC_DemoListRegex ();\n\t}\n*/\n#ifdef MVD_RECORDING\n\telse if (!strcmp(c,\"qtvusers\"))\n\t{\n\t\tif (SVC_ThrottleInfo())\n\t\t\tSVC_QTVUsers ();\n\t}\n#endif\n\telse if (!PR_GameCodePacket(net_message.data+4))\n\t{\n\t\tstatic unsigned int lt;\n\t\tunsigned int ct = Sys_Milliseconds();\n\t\tif (ct - lt > 5*1000)\n\t\t{\n\t\t\tCon_TPrintf (\"bad connectionless packet from %s: \\\"%s\\\"\\n\", NET_AdrToString (adr, sizeof(adr), &net_from), c);\n\t\t\tlt = ct;\n\t\t}\n\t}\n\n\treturn false;\n}\n\n#ifdef NQPROT\nqboolean SVNQ_ConnectionlessPacket(void)\n{\n\tsizebuf_t sb;\n\tint header;\n\tint length;\n\tint active, i, protver;\n\tint mod, modver, flags;\n\tunsigned int passwd;\n\tchar *str;\n\tchar buffer[256], buffer2[256];\n\tnetadr_t localaddr;\n\tchar *banreason;\n\n\tif (net_from.type == NA_LOOPBACK)\n\t\treturn false;\n\n\tif (!sv_listen_nq.value || SSV_IsSubServer())\n\t\treturn false;\n\n\tMSG_BeginReading(&net_message, svs.netprim);\n\theader = LongSwap(MSG_ReadLong());\n\tif (!(header & NETFLAG_CTL))\n\t{\n\t\t//this nasty chunk of code is to try to handle challenges with nq's challengeless protocol, by using stringcmds. woo. evil hacks.\n\t\tif (sv_listen_nq.ival == 2)\n\t\tif ((header & (NETFLAG_DATA|NETFLAG_EOM)) == (NETFLAG_DATA|NETFLAG_EOM))\n\t\t{\n\t\t\tint sequence;\n\t\t\tif (SV_BannedReason (&net_from))\n\t\t\t\treturn false;\t//just in case.\n\t\t\tsequence = LongSwap(MSG_ReadLong());\n\t\t\tif (sequence <= 1)\n\t\t\t{\n\t\t\t\tint numnops = 0;\n\t\t\t\tint numnonnops = 0;\n\t\t\t\tint c;\n\t\t\t\t/*make it at least robust enough to ignore any other stringcmds*/\n\t\t\t\twhile(1)\n\t\t\t\t{\n\t\t\t\t\tswitch(MSG_ReadByte())\n\t\t\t\t\t{\n\t\t\t\t\tcase clc_nop:\n\t\t\t\t\t\tnumnops++;\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\tcase clc_stringcmd:\n\t\t\t\t\t\tnumnonnops++;\n#define CCON \"challengeconnect \"\n\t\t\t\t\t\tfor(i = 0; ; i++)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (!CCON[i])\n\t\t\t\t\t\t\t\tc = -1;\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\tc = MSG_ReadByte();\n\t\t\t\t\t\t\tif (c != CCON[i])\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (!CCON[i])\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (sv_showconnectionlessmessages.ival)\n\t\t\t\t\t\t\t\tCon_Printf(S_COLOR_GRAY\"%s: CCREQ_CONNECT_COOKIE\\n\", NET_AdrToString (com_token, sizeof(com_token), &net_from));\n\t\t\t\t\t\t\tCmd_TokenizeString(MSG_ReadStringLine(), false, false);\n\t\t\t\t\t\t\t/*okay, so this is a reliable packet from a client, containing a 'cmd challengeconnect $challenge' response*/\n\t\t\t\t\t\t\tstr = va(\"connect %i %i %s \\\"\\\\name\\\\unconnected\\\\mod\\\\%s\\\\modver\\\\%s\\\\flags\\\\%s\\\\password\\\\%s\\\"\", NQ_NETCHAN_VERSION, 0, Cmd_Argv(0), Cmd_Argv(1), Cmd_Argv(2), Cmd_Argv(3), Cmd_Argv(4));\n\t\t\t\t\t\t\tCmd_TokenizeString (str, false, false);\n\n\t\t\t\t\t\t\tSVC_DirectConnect(sequence);\n\n\t\t\t\t\t\t\t/*if there is anything else in the packet, we don't actually care. its reliable, so they'll resend*/\n\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (c)\t//handle any trailing stuff if we don't know what it was.\n\t\t\t\t\t\t\tMSG_ReadString();\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\tcase -1:\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tnumnonnops++;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tif (numnops && !numnonnops)\n\t\t\t\t{\n\t\t\t\t\tsb.maxsize = sizeof(buffer);\n\t\t\t\t\tsb.data = buffer;\n\n\t\t\t\t\t/*ack it, so dumb proquake clones can actually send the proper packet*/\n\t\t\t\t\tif (!sequence)\n\t\t\t\t\t{\n\t\t\t\t\t\tSZ_Clear(&sb);\n\t\t\t\t\t\tMSG_WriteLong(&sb, BigLong(NETFLAG_ACK | 8));\n\t\t\t\t\t\tMSG_WriteLong(&sb, sequence);\n\t\t\t\t\t\tNET_SendPacket(svs.sockets, sb.cursize, sb.data, &net_from);\n\t\t\t\t\t}\n\n\n\t\t\t\t\t/*resend the cmd request, cos if they didn't send it then it must have gotten dropped.\n\t\t\t\t\tat this point, we assume its a proquake clone and that it already thinks that we are proquake\n\t\t\t\t\ta vanilla client will not start spamming nops until it has received the server info, so its only the proquake clients+clones that will send a nop before a challengeconnect\n\t\t\t\t\tunfortunatly we don't know the modver+flags+password any more. I hope its not needed. if it does, server admins will be forced to use sv_listen_nq 1 instead of 2*/\n\t\t\t\t\tSZ_Clear(&sb);\n\t\t\t\t\tMSG_WriteLong(&sb, BigLong(0));\n\t\t\t\t\tMSG_WriteLong(&sb, BigLong(1));\t//sequence 1, because 0 matches the old sequence, and thus might get dropped. hopefully the client will cope with dupes properly and ignore any regular (but unreliable) stuff.\n\n\t\t\t\t\tMSG_WriteByte(&sb, svc_stufftext);\n\t\t\t\t\tMSG_WriteString(&sb, va(\"cmd challengeconnect %i %i\\n\", SV_NewChallenge(), MOD_PROQUAKE));\n\n\t\t\t\t\t*(int*)sb.data = BigLong(NETFLAG_UNRELIABLE|sb.cursize);\n\t\t\t\t\tNET_SendPacket(svs.sockets, sb.cursize, sb.data, &net_from);\n\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn false;\t//no idea what it is.\n\t}\n\n\tlength = header & NETFLAG_LENGTH_MASK;\n\tif (length != net_message.cursize)\n\t\treturn false;\t//corrupt or not ours\n\n\tswitch(MSG_ReadByte())\n\t{\n\tcase CCREQ_CONNECT:\n\t\tstr = MSG_ReadString();\n\t\tprotver = MSG_ReadByte();\n\n\t\tif (sv_showconnectionlessmessages.ival)\n\t\t\tCon_Printf(S_COLOR_GRAY\"%s: CCREQ_CONNECT (\\\"%s\\\" %i)\\n\", NET_AdrToString (com_token, sizeof(com_token), &net_from), str, protver);\n\n\t\tsb.maxsize = sizeof(buffer);\n\t\tsb.data = buffer;\n\t\tif (strcmp(str, NQ_NETCHAN_GAMENAME))\n\t\t{\n\t\t\tSZ_Clear(&sb);\n\t\t\tMSG_WriteLong(&sb, 0);\n\t\t\tMSG_WriteByte(&sb, CCREP_REJECT);\n\t\t\tMSG_WriteString(&sb, \"Incorrect game\\n\");\n\t\t\t*(int*)sb.data = BigLong(NETFLAG_CTL+sb.cursize);\n\t\t\tNET_SendPacket(svs.sockets, sb.cursize, sb.data, &net_from);\n\t\t\treturn true;\t//not our game.\n\t\t}\n\t\tif (protver != NQ_NETCHAN_VERSION && protver != NQ_NETCHAN_VERSION_QEX)\n\t\t{\n\t\t\tSZ_Clear(&sb);\n\t\t\tMSG_WriteLong(&sb, 0);\n\t\t\tMSG_WriteByte(&sb, CCREP_REJECT);\n\t\t\tMSG_WriteString(&sb, \"Incorrect version\\n\");\n\t\t\t*(int*)sb.data = BigLong(NETFLAG_CTL+sb.cursize);\n\t\t\tNET_SendPacket(svs.sockets, sb.cursize, sb.data, &net_from);\n\t\t\treturn true;\t//not our version...\n\t\t}\n\n\t\tbanreason = SV_BannedReason (&net_from);\n\t\tif (banreason)\n\t\t{\n\t\t\tSZ_Clear(&sb);\n\t\t\tMSG_WriteLong(&sb, 0);\n\t\t\tMSG_WriteByte(&sb, CCREP_REJECT);\n\t\t\tMSG_WriteString(&sb, *banreason?va(\"You are banned: %s\\n\", banreason):\"You are banned\\n\");\n\t\t\t*(int*)sb.data = BigLong(NETFLAG_CTL+sb.cursize);\n\t\t\tNET_SendPacket(svs.sockets, sb.cursize, sb.data, &net_from);\n\t\t\treturn true;\t//not our version...\n\t\t}\n\n\t\t//proquake's extensions\n\t\tmod = MSG_ReadByte();\n\t\tmodver = MSG_ReadByte();\n\t\tflags = MSG_ReadByte();\n\t\tpasswd = MSG_ReadLong();\n\t\tif (msg_badread)\n\t\t\tpasswd = 0;\n\n\t\tif (SV_ChallengeRecent())\n\t\t\treturn true;\n\n\t\tCmd_TokenizeString (MSG_ReadString(), false, false);\n\t\tif (!strcmp(Cmd_Argv(0), \"getchallenge\") && (sv_listen_qw.ival || sv_listen_dp.ival))\n\t\t{\n\t\t\t/*dual-stack client, supporting either DP or QW protocols*/\n\t\t\tSVC_GetChallenge (false);\n\t\t}\n#ifdef HAVE_DTLS\n\t\telse if (net_enable_dtls.ival > 2 && (net_from.prot == NP_DGRAM || net_from.prot == NP_STREAM || net_from.prot == NP_WS) && net_from.type != NA_LOOPBACK && !NET_IsEncrypted(&net_from))\n\t\t{\n\t\t\tSV_RejectMessage (SCP_NETQUAKE, \"This server requires the use of DTLS/TLS/WSS.\\n\");\n\t\t\treturn true;\n\t\t}\n#endif\n\t\telse\n\t\t{\t//legacy pure-nq (though often DP).\n\t\t\tif (progstype == PROG_H2)\n\t\t\t{\n\t\t\t\tSV_RejectMessage (SCP_NETQUAKE, \"NQ clients are not supported with hexen2 gamecode\\n\");\n\t\t\t\treturn true;\t//not our version...\n\t\t\t}\n\t\t\tif (NET_WasSpecialPacket(svs.sockets))\n\t\t\t\treturn true;\n#ifdef HAVE_PACKET\n\t\t\tif (sv_listen_nq.ival == 2 && net_from.prot == NP_DGRAM\n#ifdef SUPPORT_ICE\n\t\t\t\t\t\t\t\t\t   && net_from.type != NA_ICE\n#endif\n\t\t\t\t)\n\t\t\t{\n\t\t\t\tif (password.string[0] &&\n\t\t\t\t\tstricmp(password.string, \"none\"))\n\t\t\t\t{\t//make sure we don't get crippled because of being unable to specify the actual password with proquake's stuff.\n\t\t\t\t\tchar *e;\n\t\t\t\t\tqintmax_t svpass = strtoll(password.string, &e, 0);\n\t\t\t\t\tif (*e)\t//something ain't numeric... hash it so they have a chance of getting it right...\n\t\t\t\t\t\tsvpass = CalcHashInt(&hash_md4, password.string, strlen(password.string));\n\t\t\t\t\tif (passwd != svpass)\n\t\t\t\t\t{\n\t\t\t\t\t\tCon_TPrintf (\"%s:password failed (nq)\\n\", NET_AdrToString (buffer2, sizeof(buffer2), &net_from));\n\t\t\t\t\t\tSV_RejectMessage (SCP_NETQUAKE, \"\\x01this server requires a password\\n\\n\");\n\n\t\t\t\t\t\t//and prevent them from spamming attempts. botnets might still get through fast though.\n\t\t\t\t\t\tSV_AutoBanSender(15, \"password cooldown\");\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tSZ_Clear(&sb);\n\t\t\t\tMSG_WriteLong(&sb, 0);\n\t\t\t\tMSG_WriteByte(&sb, CCREP_ACCEPT);\n\t\t\t\tNET_LocalAddressForRemote(svs.sockets, &net_from, &localaddr, 0);\n\t\t\t\tMSG_WriteLong(&sb, ShortSwap(localaddr.port));\n\t\t\t\tMSG_WriteByte(&sb, MOD_PROQUAKE);\n\t\t\t\tMSG_WriteByte(&sb, MOD_PROQUAKE_VERSION);\n\t\t\t\t*(int*)sb.data = BigLong(NETFLAG_CTL|sb.cursize);\n\t\t\t\tNET_SendPacket(svs.sockets, sb.cursize, sb.data, &net_from);\n\n\n\t\t\t\tSZ_Clear(&sb);\n\t\t\t\tMSG_WriteLong(&sb, BigLong(0));\n\t\t\t\tMSG_WriteLong(&sb, BigLong(1));\t//sequence 1, because 0 matches the old sequence, and thus might get dropped. hopefully the client will cope with dupes properly and ignore any regular (but unreliable) stuff.\n\n\t\t\t\tMSG_WriteByte(&sb, svc_stufftext);\n\t\t\t\tMSG_WriteString(&sb, va(\"cmd challengeconnect %i %i %i %i %i\\n\", SV_NewChallenge(), mod, modver, flags, passwd));\n\n\t\t\t\t*(int*)sb.data = BigLong(NETFLAG_UNRELIABLE|sb.cursize);\n\t\t\t\tNET_SendPacket(svs.sockets, sb.cursize, sb.data, &net_from);\n\t\t\t\t/*don't worry about repeating, the nop case above will recover it*/\n\t\t\t}\n\t\t\telse\n#endif\n\t\t\t{\n\t\t\t\tstr = va(\"connect %i %i %i \\\"\\\\name\\\\unconnected\\\\mod\\\\%i\\\\modver\\\\%i\\\\flags\\\\%i\\\\password\\\\%i\\\"\", protver, 0, SV_NewChallenge(), mod, modver, flags, passwd);\n\t\t\t\tCmd_TokenizeString (str, false, false);\n\n\t\t\t\tSVC_DirectConnect(0);\n\t\t\t}\n\t\t}\n\t\treturn true;\n\tcase CCREQ_SERVER_INFO:\n\t\tif (sv_showconnectionlessmessages.ival)\n\t\t\tCon_Printf(S_COLOR_GRAY\"%s: CCREQ_SERVER_INFO\\n\", NET_AdrToString (com_token, sizeof(com_token), &net_from));\n\t\tif (sv_public.ival < 0)\n\t\t\treturn false;\n\t\tif (SV_BannedReason (&net_from))\n\t\t\treturn false;\n\t\tif (Q_strcmp (MSG_ReadString(), NQ_NETCHAN_GAMENAME) != 0)\n\t\t\treturn false;\n\n\t\tsb.maxsize = sizeof(buffer);\n\t\tsb.data = buffer;\n\t\tSZ_Clear (&sb);\n\t\t// save space for the header, filled in later\n\t\tMSG_WriteLong (&sb, 0);\n\t\tMSG_WriteByte (&sb, CCREP_SERVER_INFO);\n\t\tif (NET_LocalAddressForRemote(svs.sockets, &net_from, &localaddr, 0))\n\t\t\tMSG_WriteString (&sb, NET_AdrToString (buffer2, sizeof(buffer2), &localaddr));\n\t\telse\n\t\t\tMSG_WriteString (&sb, \"unknown\");\n\t\tactive = 0;\n\t\tfor (i = 0; i < sv.allocated_client_slots; i++)\n\t\t\tif (svs.clients[i].state)\n\t\t\t\tactive++;\n\t\tMSG_WriteString (&sb, hostname.string);\n\t\tMSG_WriteString (&sb, svs.name);\n\t\tMSG_WriteByte (&sb, active);\n\t\tMSG_WriteByte (&sb, maxclients.value);\n\t\tMSG_WriteByte (&sb, NQ_NETCHAN_VERSION);\n\t\t*(int*)sb.data = BigLong(NETFLAG_CTL+sb.cursize);\n\t\tNET_SendPacket(svs.sockets, sb.cursize, sb.data, &net_from);\n\t\treturn true;\n\tcase CCREQ_PLAYER_INFO:\n\t\tif (sv_showconnectionlessmessages.ival)\n\t\t\tCon_Printf(S_COLOR_GRAY\"%s: CCREQ_PLAYER_INFO\\n\", NET_AdrToString (com_token, sizeof(com_token), &net_from));\n\t\tif (sv_public.ival < 0)\n\t\t\treturn false;\n\t\tif (SV_BannedReason (&net_from))\n\t\t\treturn false;\n\t\t/*one request per player, ouch ouch ouch, what will it make of 32 players, I wonder*/\n\t\tsb.maxsize = sizeof(buffer);\n\t\tsb.data = buffer;\n\t\t// save space for the header, filled in later\n\t\tSZ_Clear (&sb);\n\t\tMSG_WriteLong (&sb, 0);\n\t\t{\n\t\t\tunsigned int pno;\n\t\t\tclient_t *cl;\n\t\t\tpno = MSG_ReadByte();\n\t\t\tif (pno >= sv.allocated_client_slots)\n\t\t\t\tbreak;\n\t\t\tcl = &svs.clients[pno];\n\t\t\tif (cl->state <= cs_zombie)\n\t\t\t\tbreak;\n\n\t\t\tMSG_WriteByte (&sb, CCREP_PLAYER_INFO);\n\t\t\tMSG_WriteByte (&sb, pno);\n\t\t\tMSG_WriteString (&sb, cl->name);\n\t\t\tMSG_WriteLong (&sb, cl->playercolor);\n\t\t\tMSG_WriteLong (&sb, cl->old_frags);\n\t\t\tMSG_WriteLong (&sb, realtime - cl->connection_started);\n\t\t\tMSG_WriteString (&sb, SV_PlayerPublicAddress(cl));\t/*player's address, leave blank, don't spam that info as it can result in personal attacks exploits*/\n\t\t}\n\t\t*(int*)sb.data = BigLong(NETFLAG_CTL+sb.cursize);\n\t\tNET_SendPacket(svs.sockets, sb.cursize, sb.data, &net_from);\n\t\treturn true;\n\tcase CCREQ_RULE_INFO:\n\t\tif (sv_showconnectionlessmessages.ival)\n\t\t\tCon_Printf(S_COLOR_GRAY\"%s: CCREQ_RULE_INFO\\n\", NET_AdrToString (com_token, sizeof(com_token), &net_from));\n\t\tif (sv_public.ival < 0)\n\t\t\treturn false;\n\t\tif (SV_BannedReason (&net_from))\n\t\t\treturn false;\n\n\t\t/*lol, nq is evil*/\n\t\tsb.maxsize = sizeof(buffer);\n\t\tsb.data = buffer;\n\t\t// save space for the header, filled in later\n\t\tSZ_Clear (&sb);\n\t\tMSG_WriteLong (&sb, 0);\n\t\t{\n\t\t\tconst char *rname, *rval, *kname;\n\t\t\trname = MSG_ReadString();\n\n\t\t\tif (!*rname)\n\t\t\t\trname = InfoBuf_KeyForNumber(&svs.info, 0);\n\t\t\telse\n\t\t\t{\n\t\t\t\tint i = 0;\n\t\t\t\tfor(;;)\n\t\t\t\t{\n\t\t\t\t\tkname = InfoBuf_KeyForNumber(&svs.info, i);\n\t\t\t\t\tif (!kname)\n\t\t\t\t\t{\n\t\t\t\t\t\trname = NULL;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\ti++;\n\t\t\t\t\tif (!strcmp(kname, rname))\n\t\t\t\t\t{\n\t\t\t\t\t\trname = InfoBuf_KeyForNumber(&svs.info, i);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (rname)\n\t\t\t\trval = InfoBuf_ValueForKey(&svs.info, rname);\n\t\t\telse\n\t\t\t\trval = rname = \"\";\n\t\t\tMSG_WriteByte (&sb, CCREP_RULE_INFO);\n\t\t\tMSG_WriteString (&sb, rname);\n\t\t\tMSG_WriteString (&sb, rval);\n\t\t}\n\t\t*(int*)sb.data = BigLong(NETFLAG_CTL+sb.cursize);\n\t\tNET_SendPacket(svs.sockets, sb.cursize, sb.data, &net_from);\n\t\treturn true;\n\t}\n\treturn false;\n}\n#endif\n\n/*\n==============================================================================\n\nPACKET FILTERING\n\n\nYou can add or remove addresses from the filter list with:\n\naddip <ip>\nremoveip <ip>\n\nThe ip address is specified in dot format, and any unspecified digits will match any value, so you can specify an entire class C network with \"addip 192.246.40\".\n\nRemoveip will only remove an address specified exactly the same way.  You cannot addip a subnet, then removeip a single host.\n\nlistip\nPrints the current list of filters.\n\nwriteip\nDumps \"addip <ip>\" commands to listip.cfg so it can be execed at a later date.  The filter lists are not saved and restored by default, because I beleive it would cause too much confusion.\n\nfilterban <0 or 1>\n\nIf 1 (the default), then ip addresses matching the current list will be prohibited from entering the game.  This is the default setting.\n\nIf 0, then only addresses matching the list will be allowed.  This lets you easily set up a private game, or a game that only allows players from your local network.\n\n\n==============================================================================\n*/\n\ncvar_t\tfilterban = CVARD(\"filterban\", \"1\", \"If 0, players will be kicked by default unless there is a rule that allows them. Also affects the default action of addip.\");\n\n//send a network packet to a new non-connected client.\n//this is to combat tunneling\nvoid SV_OpenRoute_f(void)\n{\n\tnetadr_t to;\n\tchar data[64];\n\n\tif (NET_StringToAdr(Cmd_Argv(1), PORT_QWCLIENT, &to))\n\t{\n\t\tsprintf(data, \"\\xff\\xff\\xff\\xff%c\", S2C_CONNECTION);\n\n\t\tNetchan_OutOfBandPrint(NCF_SERVER, &to, \"hello\");\n//\t\tNET_SendPacket (strlen(data)+1, data, to);\n\t}\n}\n\n//============================================================================\nstatic int inboundsequence;\t//so we can detect frames when we didn't get any packets, even when packets come from epoll\nvoid SV_ReadPacket(void)\n{\n\tint\t\t\ti;\n\tclient_t\t*cl;\n\tint\t\t\tqport;\n\tchar\t\t*banreason;\n\n\t// check for connectionless packet (0xffffffff) first\n\tif (*(unsigned int *)net_message.data == ~0)\n\t{\n\t\tbanreason = SV_BannedReason (&net_from);\n\t\tif (banreason)\n\t\t{\n\t\t\tstatic unsigned int lt;\n\t\t\tunsigned int ct = Sys_Milliseconds();\n\t\t\tif (ct - lt > 5*1000)\n\t\t\t{\n\t\t\t\tif (*banreason)\n\t\t\t\t\tNetchan_OutOfBandTPrintf(NCF_SERVER, &net_from, com_language, \"You are banned: %s\\n\", banreason);\n\t\t\t\telse\n\t\t\t\t\tNetchan_OutOfBandTPrintf(NCF_SERVER, &net_from, com_language, \"You are banned\\n\");\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tif (NET_WasSpecialPacket(svs.sockets))\t//fixes up IP->ICE addresses (so we don't break when routes change).\n\t\t\treturn;\n\t\tSV_ConnectionlessPacket();\n\t\treturn;\n\t}\n#ifdef HAVE_DTLS\n\telse\n\t{\n\t\tif (NET_DTLS_Decode(svs.sockets))\n\t\t{\n\t\t\tif (!net_message.cursize)\n\t\t\t\treturn;\n\t\t\tif (*(unsigned int *)net_message.data == ~0)\n\t\t\t{\n\t\t\t\tSV_ConnectionlessPacket();\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n#endif\n\n#ifdef Q3SERVER\n\tif (svs.gametype == GT_QUAKE3)\n\t{\n\t\tif (q3->sv.HandleClient(&net_from, &net_message))\n\t\t\tinboundsequence++;\n\t\telse if (NET_WasSpecialPacket(svs.sockets))\n\t\t\treturn;\n\t\treturn;\n\t}\n#endif\n\n\t// read the qport out of the message so we can fix up\n\t// stupid address translating routers\n\tMSG_BeginReading (&net_message, svs.netprim);\n\tMSG_ReadLong ();\t\t// sequence number\n\tMSG_ReadLong ();\t\t// sequence number\n\tqport = MSG_ReadShort () & 0xffff;\n\n\t// check for packets from connected clients\n\tfor (i=0, cl=svs.clients ; i<svs.allocated_client_slots ; i++,cl++)\n\t{\n\t\tif (cl->state == cs_free || cl->controller)\n\t\t\tcontinue;\n\t\tif (!NET_CompareBaseAdr (&net_from, &cl->netchan.remote_address))\n\t\t\tcontinue;\n#ifdef NQPROT\n\t\tif (ISNQCLIENT(cl) && cl->netchan.remote_address.port == net_from.port)\n\t\t{\n\t\t\tif (cl->state >= cs_connected)\n\t\t\t{\n\t\t\t\tif (cl->delay > 0)\n\t\t\t\t\tgoto dominping;\n\n\t\t\t\tswitch(NQNetChan_Process(&cl->netchan))\n\t\t\t\t{\n\t\t\t\tcase NQNC_IGNORED:\n\t\t\t\t\tbreak;\n\t\t\t\tcase NQNC_ACK:\n\t\t\t\t\tif (cl->netchan.message.cursize)\n\t\t\t\t\t\tinboundsequence++;\t//we need to wake up...\n\t\t\t\t\tif (cl->netchan.reliable_length > cl->netchan.reliable_start)\n\t\t\t\t\t\tinboundsequence++;\t//we need to wake up...\n\t\t\t\t\tbreak;\n\t\t\t\tcase NQNC_RELIABLE:\n\t\t\t\tcase NQNC_UNRELIABLE:\n\t\t\t\t\tinboundsequence++;\n\t\t\t\t\tsvs.stats.packets++;\n\t\t\t\t\tSVNQ_ExecuteClientMessage(cl);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n#endif\n\n#ifdef Q3SERVER\n\t\tif (ISQ3CLIENT(cl))\n\t\t\tcontinue;\n#endif\n\n\t\tif (cl->netchan.qportsize == 0)\n\t\t{\t//no qports... use the actual port.\n\t\t\tif (cl->netchan.remote_address.port != net_from.port)\n\t\t\t\tcontinue;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (cl->netchan.qport != qport)\n\t\t\t\tcontinue;\n\t\t}\n\t\tif (cl->netchan.remote_address.port != net_from.port)\n\t\t{\n\t\t\tCon_DPrintf (\"SV_ReadPackets: fixing up a translated port\\n\");\n\t\t\tcl->netchan.remote_address.port = net_from.port;\n\t\t}\n\n\t\tif (cl->delay > 0)\n\t\t{\n#ifdef NQPROT\ndominping:\n#endif\n\t\t\tif (cl->state < cs_connected)\n\t\t\t\tbreak;\n\t\t\tif (net_message.cursize > sizeof(svs.free_lagged_packet->data))\n\t\t\t{\n\t\t\t\tCon_Printf(\"packet too large for minping\\n\");\n\t\t\t\tcl->delay -= 0.001;\n\t\t\t\tbreak;\t//drop this packet\n\t\t\t}\n\n\t\t\tif (!svs.free_lagged_packet)\t//kinda nasty\n\t\t\t\tsvs.free_lagged_packet = Z_Malloc(sizeof(*svs.free_lagged_packet));\n\n\t\t\tif (!cl->laggedpacket)\n\t\t\t\tcl->laggedpacket_last = cl->laggedpacket = svs.free_lagged_packet;\n\t\t\telse\n\t\t\t{\n\t\t\t\tcl->laggedpacket_last->next = svs.free_lagged_packet;\n\t\t\t\tcl->laggedpacket_last = cl->laggedpacket_last->next;\n\t\t\t}\n\t\t\tsvs.free_lagged_packet = svs.free_lagged_packet->next;\n\t\t\tcl->laggedpacket_last->next = NULL;\n\n\t\t\tcl->laggedpacket_last->time = realtime + cl->delay;\n\t\t\tmemcpy(cl->laggedpacket_last->data, net_message.data, net_message.cursize);\n\t\t\tcl->laggedpacket_last->length = net_message.cursize;\n\t\t\tbreak;\n\t\t}\n\n\n\t\tif (Netchan_Process(&cl->netchan))\n\t\t{\t// this is a valid, sequenced packet, so process it\n\t\t\tinboundsequence++;\n\t\t\tsvs.stats.packets++;\n\t\t\tif (cl->state >= cs_connected)\n\t\t\t{\n\t\t\t\tif (cl->send_message)\n\t\t\t\t\tcl->chokecount++;\n\t\t\t\telse\n\t\t\t\t\tcl->send_message = true;\t// reply at end of frame\n\n#ifdef Q2SERVER\n\t\t\t\tif (ISQ2CLIENT(cl))\n\t\t\t\t\tSVQ2_ExecuteClientMessage(cl);\n\t\t\t\telse\n#endif\n\t\t\t\t\tSV_ExecuteClientMessage (cl);\n\t\t\t}\n\t\t}\n\t\tbreak;\n\t}\n\n\tif (i != svs.allocated_client_slots)\n\t\treturn;\n\n#ifdef QWOVERQ3\n\tif (sv_listen_q3.ival && q3->sv.HandleClient())\n\t{\n\t\treceived++;\n\t\tcontinue;\n\t}\n#endif\n\n#ifdef NQPROT\n\tif (SVNQ_ConnectionlessPacket())\n\t\treturn;\n#endif\n\tif (SV_BannedReason (&net_from))\n\t\treturn;\n\n\tif (NET_WasSpecialPacket(svs.sockets))\n\t\treturn;\n\n\t// packet is not from a known client\n\tif (sv_showconnectionlessmessages.ival)\n\t\tCon_Printf (S_COLOR_GRAY \"%s:sequenced packet without connection (%i bytes)\\n\", NET_AdrToString (com_token, sizeof(com_token), &net_from), net_message.cursize);\t//hack: com_token cos we need some random temp buffer.\n}\n\n/*\n=================\nSV_ReadPackets\n=================\n*/\nvoid SV_KillExpiredBans(void);\n#ifdef SERVER_DEMO_PLAYBACK\n//FIMXE: move to header\nqboolean SV_GetPacket (void);\n#endif\nqboolean SV_ReadPackets (float *delay)\n{\n\tint\t\t\ti;\n\tclient_t\t*cl;\n\tlaggedpacket_t *lp;\n\n\tstatic int oldinboundsequence;\n\n\tSV_KillExpiredBans();\n\n\tfor (i = 0; i < svs.allocated_client_slots; i++)\t//fixme: shouldn't we be using svs.allocated_client_slots ?\n\t{\n\t\tcl = &svs.clients[i];\n\t\twhile (cl->laggedpacket)\n\t\t{\n\t\t\t//schedule a wakeup so minping is more consistant\n\t\t\tif (cl->laggedpacket->time > realtime)\n\t\t\t{\n\t\t\t\tif (*delay > cl->laggedpacket->time - realtime)\n\t\t\t\t\t*delay = cl->laggedpacket->time - realtime;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tlp = cl->laggedpacket;\n\t\t\t\tcl->laggedpacket = lp->next;\n\t\t\t\tif (cl->laggedpacket_last == lp)\n\t\t\t\t\tcl->laggedpacket_last = lp->next;\n\n\t\t\t\tlp->next = svs.free_lagged_packet;\n\t\t\t\tsvs.free_lagged_packet = lp;\n\n\t\t\t\tSZ_Clear(&net_message);\n\t\t\t\tmemcpy(net_message.data, lp->data, lp->length);\n\t\t\t\tnet_message.cursize = lp->length;\n\n\t\t\t\tnet_from = cl->netchan.remote_address;\t//not sure if anything depends on this, but lets not screw them up willynilly\n\n#ifdef NQPROT\n\t\t\t\tif (ISNQCLIENT(cl))\n\t\t\t\t{\n\t\t\t\t\tif (cl->state >= cs_connected)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (NQNetChan_Process(&cl->netchan))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tinboundsequence++;\n\t\t\t\t\t\t\tsvs.stats.packets++;\n\t\t\t\t\t\t\tSVNQ_ExecuteClientMessage(cl);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n#endif\n\t\t\t\t{\n\t\t\t\t\t/*QW*/\n\t\t\t\t\tif (Netchan_Process(&cl->netchan))\n\t\t\t\t\t{\t// this is a valid, sequenced packet, so process it\n\t\t\t\t\t\tinboundsequence++;\n\t\t\t\t\t\tsvs.stats.packets++;\n\t\t\t\t\t\tif (cl->state >= cs_connected)\n\t\t\t\t\t\t{\t//make sure they didn't already disconnect\n\t\t\t\t\t\t\tif (cl->send_message)\n\t\t\t\t\t\t\t\tcl->chokecount++;\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\tcl->send_message = true;\t// reply at end of frame\n\n\t\t#ifdef Q2SERVER\n\t\t\t\t\t\t\tif (ISQ2CLIENT(cl))\n\t\t\t\t\t\t\t\tSVQ2_ExecuteClientMessage(cl);\n\t\t\t\t\t\t\telse\n\t\t#endif\n\t\t\t\t\t\t\t\tSV_ExecuteClientMessage (cl);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tNET_ReadPackets(svs.sockets);\n\n\tif (inboundsequence == oldinboundsequence)\n\t\treturn false;\t//nothing new.\n\toldinboundsequence = inboundsequence;\n\treturn true;\n}\n\n/*\n==================\nSV_CheckTimeouts\n\nIf a packet has not been received from a client in timeout.value\nseconds, drop the conneciton.\n\nWhen a client is normally dropped, the client_t goes into a zombie state\nfor a few seconds to make sure any final reliable message gets resent\nif necessary\n==================\n*/\nvoid SV_CheckTimeouts (void)\n{\n\tint\t\ti;\n\tclient_t\t*cl, *cont;\n\tfloat\tdroptime;\n\tint\tnclients;\n\n\tdroptime = realtime - timeout.value;\n\tnclients = 0;\n\n\tfor (i=0,cl=svs.clients ; i<svs.allocated_client_slots ; i++,cl++)\n\t{\n\t\tif (cl->state == cs_connected || cl->state == cs_spawned)\n\t\t{\n\t\t\tif (!cl->spectator)\n\t\t\t\tnclients++;\n\t\t\tcont = cl;\n\t\t\tif (cont->controller)\n\t\t\t\tcont = cont->controller;\n\t\t\tif (cont->netchan.last_received < droptime && cl->netchan.remote_address.type != NA_LOOPBACK && cl->protocol != SCP_BAD)\n\t\t\t{\n\t\t\t\tSV_BroadcastTPrintf (PRINT_HIGH, \"Client %s timed out\\n\", cl->name);\n\t\t\t\tSV_DropClient (cl);\n\t\t\t\tcl->state = cs_free;\t// don't bother with zombie state for local player.\n\t\t\t\tif (cl->netchan.remote_address.type != NA_INVALID)\n\t\t\t\t\tNET_TerminateRoute(svs.sockets, &cl->netchan.remote_address);\n\t\t\t\tcl->netchan.remote_address.type = NA_INVALID;\n\t\t\t}\n\t\t}\n\n\t\tif (cl->state == cs_zombie && realtime - cl->connection_started > zombietime.value)\n\t\t{\n\t\t\tcl->state = cs_free;\t// can now be reused\n\t\t\tif (cl->netchan.remote_address.type != NA_INVALID)\n\t\t\t\tNET_TerminateRoute(svs.sockets, &cl->netchan.remote_address);\n\t\t\tcl->netchan.remote_address.type = NA_INVALID;\n\t\t}\n\n\t\tif (cl->state == cs_loadzombie && realtime - cl->connection_started > zombietime.value)\n\t\t{\n\t\t\tif (cl->istobeloaded)\n\t\t\t{\n\t\t\t\tif (1)//svs.gametype != GT_PROGS)\n\t\t\t\t{\n\t\t\t\t\tcl->netchan.remote_address.type = NA_INVALID;\t//make it look like a bot.\n\t\t\t\t\tif (cl->istobeloaded == 1)\n\t\t\t\t\t\tcl->state = cs_spawned;\t\t//client has an entity, apparently.\n\t\t\t\t\telse\n\t\t\t\t\t\tcl->state = cs_connected;\t//not actually on yet\n\t\t\t\t\tcl->istobeloaded = false;\n\t\t\t\t\tif (*cl->name)\n\t\t\t\t\t\tSV_BroadcastTPrintf (PRINT_HIGH, \"LoadZombie %s timed out\\n\", cl->name);\n\t\t\t\t\telse\n\t\t\t\t\t\tSV_BroadcastTPrintf (PRINT_HIGH, \"LoadZombie timed out\\n\");\n\t\t\t\t\tSV_DropClient (cl);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tif (cl->istobeloaded == 1)\n\t\t\t\t\t{\n\t\t\t\t\t\tpr_global_struct->self = EDICT_TO_PROG(svprogfuncs, cl->edict);\n\t\t\t\t\t\tPR_ExecuteProgram (svprogfuncs, pr_global_struct->ClientDisconnect);\n\t\t\t\t\t\tif (*cl->name)\n\t\t\t\t\t\t\tSV_BroadcastTPrintf (PRINT_HIGH, \"LoadZombie %s timed out\\n\", cl->name);\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tSV_BroadcastTPrintf (PRINT_HIGH, \"LoadZombie timed out\\n\");\n\t\t\t\t\t}\n\t\t\t\t\tsv.spawned_client_slots--;\n\n\t\t\t\t\tcl->istobeloaded=false;\n\n\t\t\t\t\t//must go through a zombie phase for 2 secs when the zombie gets removed.\n\t\t\t\t\tcl->state = cs_zombie;\t// the real zombieness starts now\n\t\t\t\t\tcl->connection_started = realtime;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t//no entity, just free them.\n#ifdef SUBSERVERS\n\t\t\t\tSSV_SavePlayerStats(cl, 3);\n#endif\n\t\t\t\tSV_BroadcastTPrintf (PRINT_HIGH, \"TransferZombie %s timed out\\n\", cl->name);\n\t\t\t\tcl->state = cs_free;\n\t\t\t\t*cl->name = 0;\n\t\t\t}\n\t\t\tcl->netchan.remote_address.type = NA_INVALID;\t//don't mess up from not knowing their address.\n\t\t}\n\t}\n\tif ((sv.paused&PAUSE_EXPLICIT) && !nclients)\n\t{\n\t\t// nobody left, unpause the server\n\t\tif (SV_TogglePause(NULL))\n\t\t\tSV_BroadcastTPrintf(PRINT_HIGH, \"pause released due to empty server\\n\");\n\t}\n}\n\n/*\n===================\nSV_GetConsoleCommands\n\nAdd them exactly as if they had been typed at the console\n===================\n*/\nqboolean QCExternalDebuggerCommand(char *text);\nvoid SV_GetConsoleCommands (void)\n{\n\tchar\t*cmd;\n\n\twhile (1)\n\t{\n\t\tcmd = Sys_ConsoleInput ();\n\t\tif (!cmd)\n\t\t\tbreak;\n\t\tif (QCExternalDebuggerCommand(cmd))\n\t\t\tcontinue;\n\t\tLog_String(LOG_CONSOLE, cmd);\n\t\tCbuf_AddText (cmd, RESTRICT_LOCAL);\n\t\tCbuf_AddText (\"\\n\", RESTRICT_LOCAL);\n\t}\n}\n\n#define MINDRATE 4096\n#define MINRATE 500\nint SV_RateForClient(client_t *cl)\n{\n\tint rate;\n\tif (cl->download)\n\t{\n\t\trate = cl->drate;\n\t\tif (sv_maxdrate.ival)\n\t\t{\n\t\t\tif (!rate || rate > sv_maxdrate.value)\n\t\t\t\trate = sv_maxdrate.value;\n\t\t\telse if (rate < MINDRATE)\n\t\t\t\trate = MINDRATE;\n\t\t}\n\t\telse if (rate != 0 && rate < MINDRATE)\n\t\t\trate = MINDRATE;\n\t}\n\telse\n\t{\n\t\trate = cl->rate;\n\t\tif (sv_maxrate.ival)\n\t\t{\n\t\t\tif (!rate || rate > sv_maxrate.value)\n\t\t\t\trate = sv_maxrate.value;\n\t\t\telse if (rate < MINRATE)\n\t\t\t\trate = MINRATE;\n\t\t}\n\t\telse if (rate != 0 && rate < MINRATE)\n\t\t\trate = MINRATE;\n\t}\n\n\treturn rate;\n}\n/*\n===================\nSV_CheckVars\n\n===================\n*/\nvoid SV_CheckVars (void)\n{\n\tstatic char *pw, *spw;\n\tint\t\t\tv;\n\n\tif (password.string == pw && spectator_password.string == spw)\n\t\treturn;\n\tpw = password.string;\n\tspw = spectator_password.string;\n\n\tv = 0;\n\tif (pw && pw[0] && strcmp(pw, \"none\"))\n\t\tv |= 1;\n\tif (spw && spw[0] && strcmp(spw, \"none\"))\n\t\tv |= 2;\n\n\tCon_DPrintf (\"Updated needpass.\\n\");\n\tif (!v)\n\t\tInfoBuf_SetValueForKey (&svs.info, \"needpass\", \"\");\n\telse\n\t\tInfoBuf_SetValueForKey (&svs.info, \"needpass\", va(\"%i\",v));\n}\n\n#ifdef Q2SERVER\nvoid SVQ2_ClearEvents(void)\n{\n\tq2edict_t\t*ent;\n\tint\t\ti;\n\n\tfor (i=0 ; i<ge->num_edicts ; i++, ent++)\n\t{\n\t\tent = Q2EDICT_NUM(i);\n\t\t// events only last for a single message\n\t\tent->s.event = 0;\n\t}\n}\n#endif\n\n\n/*\n==================\nSV_Impulse_f\n\nSpawns a client, uses an impulse, uses that clients think then removes the client.\n==================\n*/\nvoid SV_Impulse_f (void)\n{\n\tint i;\n\tif (svs.gametype != GT_PROGS)\n\t{\n\t\tCon_Printf(\"Not supported in this game type\\n\");\n\t\treturn;\n\t}\n\n\tfor (i = 0; i < sv.allocated_client_slots; i++)\n\t{\n\t\tif (svs.clients[i].state == cs_free)\n\t\t{\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tif (i == sv.allocated_client_slots)\n\t{\n\t\tCon_Printf(\"No empty player slots\\n\");\n\t\treturn;\n\t}\n\tif (!svprogfuncs)\n\t\treturn;\n\n\tpr_global_struct->time = sv.world.physicstime;\n\n\tsvs.clients[i].state = cs_connected;\n\tsvs.clients[i].connection_started = realtime;\n\n\tSV_SetUpClientEdict(&svs.clients[i], svs.clients[i].edict);\n\n\tsvprogfuncs->SetStringField(svprogfuncs, svs.clients[i].edict, &svs.clients[i].edict->v->netname, \"Console\", true);\n\n\tsv.skipbprintclient = &svs.clients[i];\n\tpr_global_struct->self = EDICT_TO_PROG(svprogfuncs, svs.clients[i].edict);\n\tPR_ExecuteProgram (svprogfuncs, pr_global_struct->ClientConnect);\n\tsv.skipbprintclient = NULL;\n\n\tpr_global_struct->time = sv.world.physicstime;\n\tpr_global_struct->self = EDICT_TO_PROG(svprogfuncs, svs.clients[i].edict);\n\tPR_ExecuteProgram (svprogfuncs, pr_global_struct->PutClientInServer);\n\tsv.spawned_client_slots++;\n\n\tpr_global_struct->self = EDICT_TO_PROG(svprogfuncs, svs.clients[i].edict);\n\tPR_ExecuteProgram (svprogfuncs, pr_global_struct->PlayerPreThink);\n\tpr_global_struct->self = EDICT_TO_PROG(svprogfuncs, svs.clients[i].edict);\n\tPR_ExecuteProgram (svprogfuncs, svs.clients[i].edict->v->think);\n\tpr_global_struct->self = EDICT_TO_PROG(svprogfuncs, svs.clients[i].edict);\n\tPR_ExecuteProgram (svprogfuncs, pr_global_struct->PlayerPostThink);\n\n\tsvs.clients[i].edict->v->impulse = atoi(Cmd_Argv(1));\n\n\tpr_global_struct->self = EDICT_TO_PROG(svprogfuncs, svs.clients[i].edict);\n\tPR_ExecuteProgram (svprogfuncs, pr_global_struct->PlayerPreThink);\n\tpr_global_struct->self = EDICT_TO_PROG(svprogfuncs, svs.clients[i].edict);\n\tPR_ExecuteProgram (svprogfuncs, svs.clients[i].edict->v->think);\n\tpr_global_struct->self = EDICT_TO_PROG(svprogfuncs, svs.clients[i].edict);\n\tPR_ExecuteProgram (svprogfuncs, pr_global_struct->PlayerPostThink);\n\n\tpr_global_struct->self = EDICT_TO_PROG(svprogfuncs, svs.clients[i].edict);\n\tPR_ExecuteProgram (svprogfuncs, pr_global_struct->ClientDisconnect);\n\tsv.spawned_client_slots--;\n\n\tsvs.clients[i].state = cs_free;\n}\n\nstatic void SV_PauseChanged(void)\n{\n\tint i;\n\tclient_t *cl;\n\t// send notification to all clients\n\tfor (i=0, cl = svs.clients ; i<svs.allocated_client_slots ; i++, cl++)\n\t{\n\t\tif (!cl->state)\n\t\t\tcontinue;\n\t\tif ((ISQWCLIENT(cl) || ISNQCLIENT(cl)) && !cl->controller)\n\t\t{\n\t\t\tClientReliableWrite_Begin (cl, svc_setpause, 2);\n\t\t\tClientReliableWrite_Byte (cl, sv.paused!=0);\n\t\t}\n\t}\n#ifdef MVD_RECORDING\n\tif (sv.mvdrecording)\n\t{\n\t\tClientReliableWrite_Begin (&demo.recorder, svc_setpause, 2);\n\t\tClientReliableWrite_Byte (&demo.recorder, sv.paused!=0);\n\t}\n#endif\n}\n\ndouble server_frametime;\n/*\n==================\nSV_Frame\n\n==================\n*/\nfloat SV_Frame (void)\n{\n\textern cvar_t pr_imitatemvdsv;\n\tstatic double\tstart, end, idletime;\n\tstatic int oldpackets;\n\tfloat oldtime;\n\tqboolean isidle;\n\tfloat timedelta;\n\tfloat delay;\n\n\tstart = Sys_DoubleTime ();\n\tsvs.stats.idle += start - end;\n\tend = start;\n\n\tCOM_MainThreadWork();\n\n\t//qw qc uses this for newmis handling\n\tsvs.framenum++;\n\tif (svs.framenum > 0x10000)\n\t\tsvs.framenum = 0;\n\n\tdelay = sv_maxtic.value;\n\tif (delay < sv_mintic.value)\n\t\tdelay = sv_mintic.value;\n\tif (isDedicated && sv.allocated_client_slots == 0)\n\t\tdelay = max(delay, 1);\t//when idle, don't keep waking up for no reason\n\n// keep the random time dependent\n\trand ();\n\n\tif (!sv.gamespeed)\n\t\tsv.gamespeed = 1;\n\n#ifdef WEBCLIENT\n\tif (isDedicated)\n\t{\n//\t\tFTP_ClientThink();\n\t\tHTTP_CL_Think(NULL, NULL);\n\t}\n#endif\n\n#ifdef HAVE_CLIENT\n\tisidle = !isDedicated && sv.allocated_client_slots == 1 && (Key_Dest_Has(~kdm_game)\n#ifdef QUAKESTATS\n\t\t|| IN_WeaponWheelIsShown()\n#endif\n\t\t|| cl.implicitpause) && cls.state == ca_active;\n\t/*server is effectively paused in SP/coop if there are no clients/spectators*/\n\tif (sv.spawned_client_slots == 0 && sv.spawned_observer_slots == 0 && !deathmatch.ival)\n\t\tisidle = true;\n\tif ((sv.paused & PAUSE_AUTO) != ((isidle)?PAUSE_AUTO:0))\n\t\tsv.paused ^= PAUSE_AUTO;\n#endif\n\n\tif (sv.oldpaused != sv.paused)\n\t{\n\t\tsv.oldpaused = sv.paused;\n\t\tSV_PauseChanged();\n\t}\n\n\n\t//work out the gamespeed\n\tif (sv.gamespeed != sv_gamespeed.value)\n\t{\n\t\tchar *newspeed;\n\t\tsv.gamespeed = sv_gamespeed.value;\n\t\tif (sv.gamespeed < 0.1 || sv.gamespeed == 1)\n\t\t\tsv_gamespeed.value = sv.gamespeed = 1;\n\n\t\tif (sv.gamespeed == 1)\n\t\t\tnewspeed = \"\";\n\t\telse\n\t\t\tnewspeed = va(\"%g\", sv.gamespeed*100);\n\t\tInfoBuf_SetValueForStarKey(&svs.info, \"*gamespeed\", newspeed);\n\n\t\t//correct sv.starttime\n\t\tsv.starttime = Sys_DoubleTime() - (sv.time/sv.gamespeed);\n\t}\n\n\n// decide the simulation time\n\t{\n\t\toldtime = sv.time;\n\t\tsv.time = (Sys_DoubleTime() - sv.starttime)*sv.gamespeed;\n\t\ttimedelta = sv.time - oldtime;\n\t\tif (sv.time < oldtime)\n\t\t{\n\t\t\tsv.time = oldtime;\t//urm\n\t\t\ttimedelta = 0;\n\t\t}\n\n\t\tif (isDedicated)\n\t\t\trealtime += sv.time - oldtime;\n\n\t\tif (sv.paused && sv.time > 1.5)\n\t\t{\n\t\t\tsv.starttime += (sv.time - oldtime)/sv.gamespeed;\t//move the offset\n\t\t\tsv.time = oldtime;\t//and keep time as it was.\n\t\t}\n\t}\n\n#ifdef SV_MASTER\n\tif (sv_master.ival)\n\t\tSVM_Think();\n#endif\n\n#ifdef PLUGINS\n\tif (isDedicated)\n\t\tPlug_Tick();\n#endif\n\n#ifdef SUBSERVERS\n\tMSV_PollSlaves();\n#endif\n\n#ifdef SQL\n\tSQL_ServerCycle();\n#endif\n\n\tif (sv.state < ss_active || !sv.world.worldmodel)\n\t{\n#ifdef SUBSERVERS\n\t\tif (sv.state == ss_clustermode)\n\t\t{\n\t\t\tisidle = !SV_ReadPackets (&delay);\n\t\t\tSV_SendClientMessages ();\n\t\t}\n#endif\n#ifndef SERVERONLY\n\t// check for commands typed to the host\n\t\tif (isDedicated)\n#endif\n\t\t{\n\t\t\tSV_GetConsoleCommands ();\n\t\t\tCbuf_Execute ();\n\t\t}\n\t\treturn delay;\n\t}\n\n// check timeouts\n\tSV_CheckTimeouts ();\n\n\tSV_CheckTimer ();\n\n// toggle the log buffer if full\n\tSV_CheckLog ();\n\n// get packets\n\tisidle = !SV_ReadPackets (&delay);\n\tif (isDedicated)\n\t\tNET_Tick();\n\n\tif (pr_imitatemvdsv.ival || dpcompat_nopreparse.ival)\n\t{\n\t\tCbuf_Execute ();\n\t\tif (sv.state < ss_active)\t//whoops...\n\t\t\treturn delay;\n\t}\n\n\tif (sv.multicast.cursize)\n\t{\n\t\tCon_TPrintf(\"Unterminated multicast\\n\");\n\t\tsv.multicast.cursize=0;\n\t}\n\n\t// move autonomous things around if enough time has passed\n\tif (!sv.paused || (sv.world.physicstime < 1 && sv.spawned_client_slots))\n\t{\n#ifdef Q2SERVER\n\t\t//q2 is idle even if clients sent packets.\n\t\tif (svs.gametype == GT_QUAKE2)\n\t\t\tisidle = true;\n#endif\n\t\tif (SV_Physics ())\n\t\t{\n\t\t\tisidle = false;\n\n#ifdef SAVEDGAMES\n\t\t\tif (sv.time > sv.autosave_time)\n\t\t\t\tSV_AutoSave();\n#endif\n\t\t}\n\t}\n\telse\n\t{\n\t\tisidle = idletime < 0.1;\n#ifdef VM_Q1\n\t\tif (svs.gametype == GT_Q1QVM)\n\t\t{\n\t\t\tQ1QVM_GameCodePausedTic(Sys_DoubleTime() - sv.pausedstart);\n\t\t}\n\t\telse\n#endif\n\t\t{\n\t\t\tPR_GameCodePausedTic(Sys_DoubleTime() - sv.pausedstart);\n\t\t}\n\t}\n\n\t//run any end-of-frame tasks that were set up\n\twhile (svs.frameendtasks)\n\t{\n\t\tstruct frameendtasks_s *t = svs.frameendtasks;\n\t\tsvs.frameendtasks = t->next;\n\t\tt->callback(t);\n\t\tZ_Free(t);\n\t}\n\n\tif (!isidle || idletime > 0.15)\n\t{\n\t\t//this is the q2 frame number found in the q2 protocol. each packet should contain a new frame or interpolation gets confused\n\t\tsv.framenum++;\n\n#ifdef SERVER_DEMO_PLAYBACK\n\t\twhile(SV_ReadMVD());\n#endif\n\n\t\tif (sv.multicast.cursize)\n\t\t{\n\t\t\tCon_TPrintf(\"Unterminated multicast\\n\");\n\t\t\tsv.multicast.cursize=0;\n\t\t}\n\n#ifndef SERVERONLY\n// check for commands typed to the host\n\t\tif (isDedicated)\n#endif\n\t\t{\n\t\t\tif (sv.framenum != 1)\n\t\t\t{\n#ifndef SERVERONLY\n\t\t\t\tSys_SendKeyEvents();\n#else\n\t\t\t\tSV_GetConsoleCommands ();\n#endif\n\t\t\t}\n\n// process console commands\n\t\t\tif (!(pr_imitatemvdsv.value || dpcompat_nopreparse.ival))\n\t\t\t\tCbuf_Execute ();\n\t\t}\n\n\t\tif (sv.state < ss_active)\t//whoops...\n\t\t\treturn delay;\n\n\t\t//make sure the worldmodel is actually valid...\n\t\tif (sv.world.worldmodel && sv.world.worldmodel->loadstate != MLS_LOADED)\n\t\t\tMod_LoadModel(sv.world.worldmodel, MLV_ERROR);\n\n\t\tSV_CheckVars ();\n\n// send messages back to the clients that had packets read this frame\n\t\tSV_SendClientMessages ();\n\n#ifdef MVD_RECORDING\n\t\tSV_SendMVDMessage();\n#endif\n\n// send a heartbeat to the master if needed\n\t\tSV_Master_Heartbeat ();\n\n\n#ifdef Q2SERVER\n\t\tif (ge && ge->edicts)\n\t\t\tSVQ2_ClearEvents();\n#endif\n\t\tidletime = 0;\n\t}\n\tidletime += timedelta;\n\n// collect timing statistics\n\tend = Sys_DoubleTime ();\n\tsvs.stats.active += end-start;\n\tif (svs.stats.maxresponse < end-start)\n\t\tsvs.stats.maxresponse = end-start;\n\tif (svs.stats.maxpackets < svs.stats.packets-oldpackets)\n\t\tsvs.stats.maxpackets = svs.stats.packets-oldpackets;\n\tsvs.stats.count++;\n\tif (svs.stats.latched_time < end)\n\t{\n\t\tsvs.stats.latched_active = svs.stats.active;\n\t\tsvs.stats.latched_idle = svs.stats.idle;\n\t\tsvs.stats.latched_packets = svs.stats.packets;\n\t\tsvs.stats.latched_count = svs.stats.count;\n\t\tsvs.stats.latched_maxpackets = svs.stats.maxpackets;\n\t\tsvs.stats.latched_maxresponse = svs.stats.maxresponse;\n\t\tsvs.stats.latched_time = end + SVSTATS_PERIOD;\n\t\tsvs.stats.active = 0;\n\t\tsvs.stats.idle = 0;\n\t\tsvs.stats.packets = 0;\n\t\tsvs.stats.count = 0;\n\t\tsvs.stats.maxresponse = 0;\n\t\tsvs.stats.maxpackets = 0;\n\t}\n\toldpackets = svs.stats.packets;\n\n\tserver_frametime += end-start;\n\treturn delay;\n}\n\nstatic void SV_InfoChanged(void *context, const char *key)\n{\n\tsize_t i;\n\n#ifdef Q3SERVER\n\tif (q3)\n\t\tq3->sv.ServerinfoChanged(key);\n#endif\n\n\tif (context != &svs.info && *key == '_')\n\t\treturn;\t//these keys are considered private to originating client/server, and are not broadcast to anyone else\n\n#ifdef MVD_RECORDING\n\tif (sv.mvdrecording)\n\t\tInfoSync_Add(&demo.recorder.infosync, context, key);\t//make sure it gets written into mvds too.\n#endif\n\tfor (i = 0; i < svs.allocated_client_slots; i++)\n\t{\n\t\tif (svs.clients[i].state >= cs_connected && !svs.clients[i].controller)\n\t\t{\n\t\t\tInfoSync_Add(&svs.clients[i].infosync, context, key);\n\t\t}\n\t}\n}\n\n/*\n===============\nSV_InitLocal\n===============\n*/\nextern void Log_Init (void);\n\nvoid SV_InitLocal (void)\n{\n\tint\t\ti;\n\textern\tcvar_t\tpr_allowbutton1;\n\textern\tcvar_t\tsv_aim;\n\n\textern\tcvar_t\tpm_bunnyspeedcap;\n\textern\tcvar_t\tpm_bunnyfriction;\n\textern\tcvar_t\tpm_ktjump;\n\textern\tcvar_t\tpm_slidefix;\n\textern\tcvar_t\tpm_airstep;\n\textern\tcvar_t\tpm_pground;\n\textern\tcvar_t\tpm_stepdown;\n\textern\tcvar_t\tpm_walljump;\n\textern\tcvar_t\tpm_slidyslopes;\n\textern\tcvar_t\tpm_autobunny;\n\textern\tcvar_t\tpm_watersinkspeed;\n\textern\tcvar_t\tpm_flyfriction;\n\textern\tcvar_t\tpm_edgefriction;\n\n#ifdef VM_Q1\t//cvars for pimping ourselves to ktx...\n\tstatic cvar_t qws_name\t\t= CVARF(\"qws_name\",\t\tDISTRIBUTION,\t\t\tCVAR_NOSET );\n\tstatic cvar_t qws_fullname\t= CVARF(\"qws_fullname\", FULLENGINENAME,\t\t\tCVAR_NOSET );\n\tstatic cvar_t qws_version\t= CVARF(\"qws_version\",\tSTRINGIFY(FTE_VER_MAJOR)\".\"STRINGIFY(FTE_VER_MINOR),CVAR_NOSET );\n\tstatic cvar_t qws_buildnum\t= CVARF(\"qws_buildnum\",\tSTRINGIFY(SVNREVISION),\tCVAR_NOSET );\n#ifdef FTE_TARGET_WEB\n\tstatic cvar_t qws_platform\t= CVARF(\"qws_platform\",\tPLATFORM,\t\t\t\tCVAR_NOSET );\n#else\n\tstatic cvar_t qws_platform\t= CVARF(\"qws_platform\",\tPLATFORM \"-\" ARCH_CPU_POSTFIX,\t\t\t\tCVAR_NOSET );\n#endif\n\tstatic cvar_t qws_builddate\t= CVARF(\"qws_builddate\",STRINGIFY(SVNDATE),\t\tCVAR_NOSET );\n\tstatic cvar_t qws_homepage\t= CVARF(\"qws_homepage\",\tENGINEWEBSITE,\t\tCVAR_NOSET );\n\tCvar_Register(&qws_name,\t\t\"Server Info\");\n\tCvar_Register(&qws_fullname,\t\"Server Info\");\n\tCvar_Register(&qws_version,\t\t\"Server Info\");\n\tCvar_Register(&qws_buildnum,\t\"Server Info\");\n\tCvar_Register(&qws_platform,\t\"Server Info\");\n\tCvar_Register(&qws_builddate,\t\"Server Info\");\n\tCvar_Register(&qws_homepage,\t\"Server Info\");\n#endif\n\n\tSV_InitOperatorCommands\t();\n\tSV_UserInit ();\n\n#ifndef SERVERONLY\n\tif (isDedicated)\n#endif\n\t{\n\t\tCvar_Register (&password,\tcvargroup_servercontrol);\n\t\tCvar_Register (&rcon_password,\tcvargroup_servercontrol);\n\n\t\tLog_Init();\n\t}\n\trcon_password.restriction = RESTRICT_MAX;\t//no cheatie rconers changing rcon passwords...\n\tCvar_Register (&sv_rconlim,\tcvargroup_servercontrol);\n\tCvar_Register (&sv_crypt_rcon,\tcvargroup_servercontrol);\n\tCvar_Register (&spectator_password,\tcvargroup_servercontrol);\n\n\tCvar_Register (&sv_mintic,\tcvargroup_servercontrol);\n\tCvar_Register (&sv_maxtic,\tcvargroup_servercontrol);\n\tCvar_Register (&sv_limittics,\tcvargroup_servercontrol);\n\n\tCvar_Register (&fraglimit,\tcvargroup_serverinfo);\n\tCvar_Register (&timelimit,\tcvargroup_serverinfo);\n\tCvar_Register (&teamplay,\tcvargroup_serverinfo);\n\tCvar_Register (&coop,\tcvargroup_serverinfo);\n\tCvar_Register (&skill,\tcvargroup_serverinfo);\n\tCvar_Register (&samelevel,\tcvargroup_serverinfo);\n\tCvar_Register (&maxclients,\tcvargroup_serverinfo);\n\tCvar_Register (&maxspectators,\tcvargroup_serverinfo);\n\tCvar_Register (&sv_playerslots,\tcvargroup_serverinfo);\n\tCvar_Register (&hostname,\tcvargroup_serverinfo);\n\tCvar_Register (&deathmatch,\tcvargroup_serverinfo);\n\tCvar_Register (&spawn,\tcvargroup_servercontrol);\n\n\t//arguably cheats. Must be switched on to use.\n\tCvar_Register (&watervis,\tcvargroup_serverinfo);\n\tCvar_Register (&allow_skybox,\tcvargroup_serverinfo);\n\tCvar_Register (&sv_allow_splitscreen,\tcvargroup_serverinfo);\n\tCvar_Register (&fbskins,\tcvargroup_serverinfo);\n\n\tCvar_Register (&zombietime,\tcvargroup_servercontrol);\n\n\tCvar_Register (&sv_pupglow,\tcvargroup_serverinfo);\n\n\tCvar_Register (&sv_bigcoords,\t\t\tcvargroup_serverphysics);\n\n\tCvar_Register (&pm_bunnyspeedcap,\t\tcvargroup_serverphysics);\n\tCvar_Register (&pm_bunnyfriction,\t\tcvargroup_serverphysics);\n\tCvar_Register (&pm_watersinkspeed,\t\tcvargroup_serverphysics);\n\tCvar_Register (&pm_flyfriction,\t\t\tcvargroup_serverphysics);\n\tCvar_Register (&pm_ktjump,\t\t\t\tcvargroup_serverphysics);\n\tCvar_Register (&pm_slidefix,\t\t\tcvargroup_serverphysics);\n\tCvar_Register (&pm_slidyslopes,\t\t\tcvargroup_serverphysics);\n\tCvar_Register (&pm_autobunny,\t\t\tcvargroup_serverphysics);\n\tCvar_Register (&pm_airstep,\t\t\t\tcvargroup_serverphysics);\n\tCvar_Register (&pm_pground,\t\t\t\tcvargroup_serverphysics);\n\tCvar_Register (&pm_stepdown,\t\t\tcvargroup_serverphysics);\n\tCvar_Register (&pm_walljump,\t\t\tcvargroup_serverphysics);\n\tCvar_Register (&pm_edgefriction,\t\tcvargroup_serverphysics);\n\n\tCvar_Register (&sv_compatiblehulls,\t\tcvargroup_serverphysics);\n\tCvar_Register (&dpcompat_stats,\t\t\t\"Darkplaces compatibility\");\n\n\tfor (i = 0; i < sizeof(sv_motd)/sizeof(sv_motd[0]); i++)\n\t\tCvar_Register(&sv_motd[i],\tcvargroup_serverinfo);\n\n\tCvar_Register (&sv_aim,\tcvargroup_servercontrol);\n\n\tCvar_Register (&sv_resetparms,\tcvargroup_servercontrol);\n\n\tif (isDedicated)\n\t\tsv_public.enginevalue = \"1\";\n\n\tCvar_Register (&sv_protocol,\tcvargroup_servercontrol);\n\tCvar_Register (&sv_guidhash,\tcvargroup_servercontrol);\n\tCvar_Register (&sv_serverip,\tcvargroup_servercontrol);\n\tCvar_Register (&sv_public,\tcvargroup_servercontrol);\n\tsv_public.restriction = RESTRICT_MAX;\t//no disabling this over rcon.\n\tCvar_Register (&sv_listen_qw,\tcvargroup_servercontrol);\n\tCvar_Register (&sv_listen_nq,\tcvargroup_servercontrol);\n\tCvar_Register (&sv_listen_dp,\tcvargroup_servercontrol);\n#ifdef QWOVERQ3\n\tCvar_Register (&sv_listen_q3,\tcvargroup_servercontrol);\n#endif\n\tsv_listen_qw.restriction = RESTRICT_MAX;\t//no disabling this over rcon.\n\tCvar_Register (&sv_reconnectlimit,\tcvargroup_servercontrol);\n\tCvar_Register (&sv_use_dns, cvargroup_servercontrol);\n\tCvar_Register (&fraglog_public,\tcvargroup_servercontrol);\n\n\tSVNET_RegisterCvars();\n\n\tCvar_Register (&sv_reportheartbeats, cvargroup_servercontrol);\n\tCvar_Register (&sv_heartbeat_interval, cvargroup_servercontrol);\n\tCvar_Register (&sv_heartbeat_checks, cvargroup_servercontrol);\n\n\tCvar_Register (&sv_showconnectionlessmessages, cvargroup_servercontrol);\n\tCvar_Register (&sv_banproxies, cvargroup_serverpermissions);\n#ifdef SV_MASTER\n\tCvar_Register (&sv_master,\tcvargroup_servercontrol);\n#endif\n\n\tCvar_Register (&filterban,\tcvargroup_servercontrol);\n\n\tCvar_Register (&allow_download,\tcvargroup_serverpermissions);\n\tCvar_Register (&allow_download_skins,\tcvargroup_serverpermissions);\n\tCvar_Register (&allow_download_models,\tcvargroup_serverpermissions);\n\tCvar_Register (&allow_download_sounds,\tcvargroup_serverpermissions);\n\tCvar_Register (&allow_download_particles,\tcvargroup_serverpermissions);\n\tCvar_Register (&allow_download_maps,\tcvargroup_serverpermissions);\n\tCvar_Register (&allow_download_logs,\tcvargroup_serverpermissions);\n\tCvar_Register (&allow_download_demos,\tcvargroup_serverpermissions);\n\tCvar_Register (&allow_download_anymap,\tcvargroup_serverpermissions);\n\tCvar_Register (&allow_download_pakcontents,\tcvargroup_serverpermissions);\n\tCvar_Register (&allow_download_textures,cvargroup_serverpermissions);\n\tCvar_Register (&allow_download_configs,\tcvargroup_serverpermissions);\n\tCvar_Register (&allow_download_locs,\tcvargroup_serverpermissions);\n\tCvar_Register (&allow_download_packages,cvargroup_serverpermissions);\n\tCvar_Register (&allow_download_refpackages,cvargroup_serverpermissions);\n\tCvar_Register (&allow_download_wads,\tcvargroup_serverpermissions);\n\tCvar_Register (&allow_download_root,\tcvargroup_serverpermissions);\n\tCvar_Register (&allow_download_copyrighted,\tcvargroup_serverpermissions);\n\tCvar_Register (&allow_download_other,\tcvargroup_serverpermissions);\n\tCvar_Register (&secure,\tcvargroup_serverpermissions);\n\n\tCvar_Register (&sv_highchars,\tcvargroup_servercontrol);\n\tCvar_Register (&sv_calcphs,\tcvargroup_servercontrol);\n\tCvar_Register (&sv_phs,\tcvargroup_servercontrol);\n\tCvar_Register (&sv_cullplayers_trace, cvargroup_servercontrol);\n\tCvar_Register (&sv_cullentities_trace, cvargroup_servercontrol);\n\n\tCvar_Register (&sv_csqc_progname,\tcvargroup_servercontrol);\n\tCvar_Register (&sv_csqcdebug, cvargroup_servercontrol);\n\tCvar_Register (&sv_specprint, cvargroup_serverpermissions);\n\n\tCvar_Register (&sv_reliable_sound, cvargroup_serverphysics);\n\tCvar_Register (&sv_gamespeed, cvargroup_serverphysics);\n\tCvar_Register (&sv_nqplayerphysics,\tcvargroup_serverphysics);\n\tCvar_Register (&pr_allowbutton1, cvargroup_servercontrol);\n\n\tCvar_Register (&pausable,\tcvargroup_servercontrol);\n\n\tCvar_Register (&sv_maxrate, cvargroup_servercontrol);\n\tCvar_Register (&sv_maxdrate, cvargroup_servercontrol);\n\tCvar_ForceCallback(&sv_maxrate);\n\tCvar_ForceCallback(&sv_maxdrate);\n\tCvar_Register (&sv_minping, cvargroup_servercontrol);\n\n\tCvar_Register (&sv_nailhack, cvargroup_servercontrol);\n\tCvar_Register (&sv_nopvs, cvargroup_servercontrol);\n\n\tCmd_AddCommand (\"sv_impulse\", SV_Impulse_f);\n\n\tCmd_AddCommand (\"openroute\", SV_OpenRoute_f);\n\n#ifdef SAVEDGAMES\n#if !defined(NOBUILTINMENUS) && !defined(SERVERONLY)\n\tCvar_Register(&sv_autosave, cvargroup_servercontrol);\n#endif\n\tCvar_Register(&sv_savefmt, cvargroup_servercontrol);\n#ifndef QUAKETC\n\tCmd_AddCommandAD (\"savegame_legacy\", SV_Savegame_f, SV_Savegame_c, \"Saves the game in a format compatible with vanilla Quake. Anything not supported by that format will be lost.\");\n#endif\n\tCmd_AddCommandAD (\"savegame\", SV_Savegame_f, SV_Savegame_c, \"Saves the game to the named location.\");\n\tCmd_AddCommandAD (\"loadgame\", SV_Loadgame_f, SV_Savegame_c, \"Loads an existing saved game.\");\n\tCmd_AddCommandAD (\"save\", SV_Savegame_f, SV_Savegame_c, \"Saves the game to the named location.\");\n\tCmd_AddCommandAD (\"load\", SV_Loadgame_f, SV_Savegame_c, \"Loads an existing saved game.\");\n\tCmd_AddCommandAD (\"unsavegame\", SV_DeleteSavegame_f, SV_Savegame_c, \"Wipes an existing saved game from disk.\");\n#endif\n\n#ifdef MVD_RECORDING\n\tSV_MVDInit();\n#endif\n\n\tsvs.info.ChangeCB = SV_InfoChanged;\n\tsvs.info.ChangeCTX = &svs.info;\n\tInfoBuf_SetValueForStarKey (&svs.info, \"*version\", version_string());\n\tInfoBuf_SetValueForStarKey (&svs.info, \"*z_ext\", va(\"%i\", SERVER_SUPPORTED_Z_EXTENSIONS));\n\n\t// init fraglog stuff\n\tsvs.logsequence = 1;\n\tsvs.logtime = realtime;\n\tfor (i = 0; i < FRAGLOG_BUFFERS; i++)\n\t{\n\t\tsvs.log[i].data = svs.log_buf[i];\n\t\tsvs.log[i].maxsize = sizeof(svs.log_buf[i]);\n\t\tsvs.log[i].cursize = 0;\n\t\tsvs.log[i].allowoverflow = true;\n\t}\n\n\tsvs.free_lagged_packet = NULL;\n}\n\n#define iswhite(c) ((c) == ' ' || (unsigned char)(c) == (unsigned char)INVIS_CHAR1 || (unsigned char)(c) == (unsigned char)INVIS_CHAR2 || (unsigned char)(c) == (unsigned char)INVIS_CHAR3)\n#define isinvalid(c) ((c) == ':' || (c) == '\\\\' || (c) == '$' || (c) == '\\r' || (c) == '\\n' || (unsigned char)(c) == (unsigned char)0xff || (c) == '\\\"')\n//colon is so clients can't get confused while parsing chats (eg frag messages)\n//255 is so fuhquake/ezquake don't end up with nameless players (and general MSG_ReadString bugs)\n//\" is so mods that use player names in tokenizing/frik_files don't mess up. mods are still expected to be able to cope with space.\n//\\ is blocked because it messes up our ^[NAME\\player\\NUM^] links, and because vanilla would hate it.\n//$ is blocked because of potential internationalisation escapes.\n\n//is allowed to shorten, out must be as long as in and min of \"unnamed\"+1\nvoid SV_FixupName(const char *in, char *out, unsigned int outlen)\n{\n\tchar *s, *p;\n\tunsigned int len, codepoint, codeflags;\n\tconchar_t testbuf[1024], *t, *n, *e;\n\n\tif (outlen == 0)\n\t\treturn;\n\n\tlen = outlen;\n\n\ts = out;\n\twhile(iswhite(*in) || isinvalid(*in) || *in == '\\1' || *in == '\\2')\t//1 and 2 are to stop clients from printing the entire line as chat. only do that for the leading charater.\n\t\tin++;\n\twhile(*in && len > 1)\n\t{\n\t\tif (isinvalid(*in))\n\t\t{\t//chars that cause a problem.\n\t\t\tin++;\n\t\t\tcontinue;\n\t\t}\n\t\t*s++ = *in++;\n\t\tlen--;\n\t}\n\t*s = '\\0';\n\n\t/*note: clients are not guarenteed to parse things the same as the server. utf-8 surrogates may be awkward here*/\n\te = COM_ParseFunString(CON_WHITEMASK, out, testbuf, sizeof(testbuf), false);\n\tfor (t = testbuf; t < e; t = n)\n\t{\n\t\tn = Font_Decode(t, &codeflags, &codepoint);\n\t\t/*reject anything hidden in names*/\n\t\tif ((codeflags & CON_HIDDEN) || (codeflags&(CON_LINKSPECIAL|CON_RICHFORECOLOUR))==CON_LINKSPECIAL)\n\t\t\tbreak;\n\t\t/*reject pictograms*/\n\t\tif (codepoint >= 0xe100 && codepoint < 0xe200)\n\t\t\tbreak;\n\t\tif (!sv_highchars.ival && (codeflags & CON_2NDCHARSETTEXT))\n\t\t\tbreak;\n\t\t/*FIXME: should we try to ensure that the chars are in most fonts? that might annoy speakers of more exotic languages I suppose. cvar it?*/\n\t}\n\t//and spit it out again, which makes sure there's no weird markup that might screw up other strings.\n\tCOM_DeFunString(testbuf, t, out, outlen, false, false);\n\n\tif (!*out || (t < e) || e == testbuf)\n\t{\t//reached end and it was all whitespace\n\t\t//white space only\n\t\tstrncpy(out, \"unnamed\", outlen);\n\t\tout[outlen-1] = 0;\n\t\tp = out;\n\t}\n\n\tfor (p = out + strlen(out) - 1; p != out && iswhite(*p) ; p--)\n\t\t;\n\tp[1] = 0;\n}\n\nvoid SV_DeDupeName(const char *val, client_t *cl, char *newname, size_t newnamesize)\n{\n\tint\t\ti;\n\tclient_t\t*client;\n\tint\t\tdupc = 1;\n\tchar\tbasic[80];\n\tconst char *p;\n\tif (!val)\n\t\tval = \"\";\n\t//we block large names here because a) they're unwieldly. b) they might cause players to be invisible to older clients/server browsers/etc.\n\t//bots with no name skip the fixup, to avoid default names(they're expected to be given a name eventually, so are allowed to be invisible for now)\n\tif (cl && cl->protocol == SCP_BAD && !*val)\n\t\tnewname[0] = 0;\n\telse\n\t{\n\t\tSV_FixupName(val, newname, newnamesize);\n\t\tif (strlen(newname) > 40)\n\t\t\tnewname[40] = 0;\n\t}\n\n\t//strip weirdness\n\tdeleetstring(basic, newname);\n\n\n\tif (!cl || cl->protocol != SCP_BAD)\n\t{\t//don't bother validating bot names. The gamecode is expected to not be stupid.\n\t\tif (!basic[0] || strstr(basic, \"console\"))\n\t\t\tstrcpy(newname, \"unnamed\");\n\n\t\t// check to see if another user by the same name exists\n\t\twhile (1)\n\t\t{\n\t\t\tfor (i=0, client = svs.clients ; i<svs.allocated_client_slots ; i++, client++)\n\t\t\t{\n\t\t\t\tif (client->state < cs_connected || client == cl)\n\t\t\t\t\tcontinue;\n\t\t\t\tif (!stricmp(client->name, newname))\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (i != svs.allocated_client_slots)\n\t\t\t{ // dup name\n\t\t\t\tchar tmpname[80];\n\t\t\t\tif (strlen(newname) > sizeof(cl->namebuf) - 1)\n\t\t\t\t\tnewname[sizeof(cl->namebuf) - 4] = 0;\n\t\t\t\tp = newname;\n\n\t\t\t\tif (newname[0] == '(')\n\t\t\t\t{\n\t\t\t\t\tif (newname[1]>='0'&&newname[1]<='9')\n\t\t\t\t\t{\n\t\t\t\t\t\tif (newname[2] == ')')\n\t\t\t\t\t\t\tp = newname + 3;\n\t\t\t\t\t\telse if (newname[2]>='0'&&newname[2]<='9')\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (newname[3] == ')')\n\t\t\t\t\t\t\t\tp = newname + 4;\n\t\t\t\t\t\t\telse if (newname[3]>='0'&&newname[3]<='9')\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tif (newname[4] == ')')\n\t\t\t\t\t\t\t\t\tp = newname + 5;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tmemcpy(tmpname, p, strlen(p)+1);\n\n\t\t\t\tQ_snprintfz(newname, newnamesize, \"(%d)%-.40s\", dupc++, tmpname);\n\t\t\t}\n\t\t\telse\n\t\t\t\tbreak;\n\t\t}\n\t}\n}\n\n\nqboolean ReloadRanking(client_t *cl, const char *newname)\n{\n#ifdef SVRANKING\n\tint newid;\n\tint j;\n\trankstats_t rs;\n\tnewid = Rank_GetPlayerID(cl->guid, newname, atoi(InfoBuf_ValueForKey (&cl->userinfo, \"_pwd\")), true, false);\t//'_' keys are always stripped. On any server. So try and use that so persistant data won't give out the password when connecting to a different server\n\tif (!newid)\n\t\tnewid = Rank_GetPlayerID(cl->guid, newname, atoi(InfoBuf_ValueForKey (&cl->userinfo, \"password\")), true, false);\n\tif (newid)\n\t{\n\t\tif (cl->rankid && cl->state >= cs_spawned)//apply current stats\n\t\t{\n\t\t\tif (!Rank_GetPlayerStats(cl->rankid, &rs))\n\t\t\t\treturn false;\n\t\t\trs.timeonserver += realtime - cl->stats_started;\n\t\t\tcl->stats_started = realtime;\n\t\t\trs.kills += cl->kills;\n\t\t\trs.deaths += cl->deaths;\n\n\t\t\trs.flags1 &= ~(RANK_CUFFED|RANK_MUTED|RANK_CRIPPLED);\n//\t\t\tif (cl->iscuffed==2)\n//\t\t\t\trs.flags1 |= RANK_CUFFED;\n//\t\t\tif (cl->ismuted==2)\n//\t\t\t\trs.flags1 |= RANK_MUTED;\n//\t\t\tif (cl->iscrippled==2)\n//\t\t\t\trs.flags1 |= RANK_CRIPPLED;\n\t\t\tcl->kills=0;\n\t\t\tcl->deaths=0;\n\t\t\tpr_global_struct->self = EDICT_TO_PROG(svprogfuncs, cl->edict);\n\t\t\tif (pr_global_ptrs->SetChangeParms)\n\t\t\t\tPR_ExecuteProgram (svprogfuncs, pr_global_struct->SetChangeParms);\n\t\t\tfor (j=0 ; j<NUM_RANK_SPAWN_PARMS ; j++)\n\t\t\t\tif (pr_global_ptrs->spawnparamglobals[j])\n\t\t\t\t\trs.parm[j] = *pr_global_ptrs->spawnparamglobals[j];\n\t\t\tRank_SetPlayerStats(cl->rankid, &rs);\n\t\t\tcl->rankid = 0;\n\t\t}\n\t\tif (!Rank_GetPlayerStats(newid, &rs))\n\t\t\treturn false;\n\t\tcl->rankid = newid;\n\t\tif (rs.flags1 & RANK_CUFFED)\n\t\t\tcl->penalties |= BAN_CUFF;\n\t\tif (rs.flags1 & RANK_MUTED)\n\t\t\tcl->penalties |= BAN_MUTE;\n\t\tif (rs.flags1 & RANK_CRIPPLED)\n\t\t\tcl->penalties |= BAN_CRIPPLED;\n\n\t\tcl->trustlevel = rs.trustlevel;\n\t\treturn true;\n\t}\n#endif\n\treturn false;\n}\n/*\n=================\nSV_ExtractFromUserinfo\n\nPull specific info from a newly changed userinfo string\ninto a more C freindly form.\n=================\n*/\nvoid SV_ExtractFromUserinfo (client_t *cl, qboolean verbose)\n{\n\tconst char\t*val, *p;\n\tchar\tnewname[80];\n#ifdef SVRANKING\n\textern cvar_t rank_filename;\n#endif\n\tsize_t blobsize;\n\tqboolean large;\n\n\tint bottom = atoi(InfoBuf_ValueForKey(&cl->userinfo, \"bottomcolor\"));\n\n\tif (progstype == PROG_NQ)\n\t\tp = va(\"t%u\", bottom);\n\telse\n\t\tp = InfoBuf_ValueForKey(&svs.localinfo, va(\"team%u\", bottom));\n\tval = InfoBuf_ValueForKey (&cl->userinfo, \"team\");\n\tif (*p && strcmp(p, val))\n\t{\n\t\tInfoBuf_SetValueForKey(&cl->userinfo, \"team\", p);\n\t\tif (verbose)\n\t\t\tSV_BroadcastUserinfoChange(cl, true, \"team\", p);\n\t}\n\tQ_strncpyz (cl->team, val, sizeof(cl->teambuf));\n\n\t// name for C code\n\tval = InfoBuf_BlobForKey (&cl->userinfo, \"name\", &blobsize, &large);\n\tif (large)\t//don't allow it if there's even anything weird in there (simplies the necessary qc).\n\t\tval = \"\";\n\t//fixup and dedupe\n\tSV_DeDupeName(val, cl, newname, sizeof(newname));\n\n\tif (!cl->drop && strncmp(newname, cl->name, sizeof(cl->namebuf)-1))\n\t{\n\t\tif ((cl->penalties & BAN_MUTE) && *cl->name && verbose)\t//!verbose is a gamecode-forced update, where the gamecode is expected to know what its doing.\n\t\t{\n\t\t\tif (!(cl->penalties & BAN_STEALTH))\n\t\t\t\tSV_ClientTPrintf (cl, PRINT_HIGH, \"Muted players may not change their names\\n\");\n\n\t\t\tQ_strncpyz (newname, cl->name, sizeof(newname));\n\t\t}\n\n\t\tif (!sv.paused && *cl->name)\n\t\t{\n\t\t\tif (!cl->lastnametime || realtime - cl->lastnametime > 5)\n\t\t\t{\n\t\t\t\tcl->lastnamecount = 0;\n\t\t\t\tcl->lastnametime = realtime;\n\t\t\t}\n\t\t\telse if (cl->lastnamecount++ > 4 && verbose)\n\t\t\t{\n\t\t\t\tSV_AutoAddPenalty (cl, BAN_MUTE, 60*5, \"Muted for name spam\");\n\t\t\t\tQ_strncpyz (newname, cl->name, sizeof(newname));\n\t\t\t}\n\t\t}\n\n\t\tif (!cl->drop && strncmp(newname, cl->name, sizeof(cl->namebuf)-1) && cl->state > cs_zombie)\n\t\t{\n\t\t\tif (*cl->name && cl->state >= cs_spawned && !cl->spectator && verbose)\n\t\t\t{\n\t\t\t\tSV_BroadcastTPrintf (PRINT_HIGH, \"%s changed their name to %s\\n\", cl->name, newname);\n\t\t\t}\n\t\t\tQ_strncpyz (cl->name, newname, sizeof(cl->namebuf));\n\n\t\t\tif (svprogfuncs)\n\t\t\t\tsvprogfuncs->SetStringField(svprogfuncs, cl->edict, &cl->edict->v->netname, cl->name, true);\n\n#ifdef SVRANKING\n\t\t\tif (ReloadRanking(cl, newname))\n\t\t\t{\n\t\t\t}\n\t\t\telse if (cl->state >= cs_spawned && *rank_filename.string && verbose)\n\t\t\t\tSV_ClientTPrintf(cl, PRINT_HIGH, \"Your rankings name has not been changed\\n\");\n#endif\n\t\t}\n\t}\n\n\t//make CERTAIN that the name we think they're using is actually the name everyone else sees too.\n\t//bots are allowed empty names. this gives the gamecode a chance to actually assign one.\n\tif (cl->protocol != SCP_BAD)\n\t{\n\t\tInfoBuf_SetValueForKey (&cl->userinfo, \"name\", newname);\n\t\tval = InfoBuf_ValueForKey (&cl->userinfo, \"name\");\n\t\tif (!*val)\n\t\t{\n\t\t\tSV_BroadcastTPrintf (PRINT_HIGH, \"corrupt userinfo for player %s\\n\", cl->name);\n\t\t\tcl->drop = true;\n\t\t}\n\t}\n\n\tval = InfoBuf_ValueForKey (&cl->userinfo, \"lang\");\n\tcl->language = *val?TL_FindLanguage(val):com_language;\n\n\tval = InfoBuf_ValueForKey (&cl->userinfo, \"nogib\");\n\tcl->gibfilter = !!atoi(val);\n\n\t// rate command\n\tval = InfoBuf_ValueForKey (&cl->userinfo, \"rate\");\n\tif (strlen(val))\n\t\tcl->rate = atoi(val);\n\telse\n\t\tcl->rate = 0;//0 means no specific limit, limited only by sv_maxrate.\n\n\tval = InfoBuf_ValueForKey (&cl->userinfo, \"dupe\");\n\tcl->netchan.dupe = bound(0, atoi(val), 5);\n\n\tval = InfoBuf_ValueForKey (&cl->userinfo, \"drate\");\n\tif (strlen(val))\n\t\tcl->drate = atoi(val);\n\telse\n\t\tcl->drate = 0;\t//0 disables rate limiting while downloading\n\n#ifdef HEXEN2\n\tval = InfoBuf_ValueForKey (&cl->userinfo, \"cl_playerclass\");\n\tif (val)\n\t{\n\t\tPRH2_SetPlayerClass(cl, atoi(val), false);\n\t}\n#endif\n\n\tval = InfoBuf_ValueForKey (&cl->userinfo, \"noaim\");\n\tif (atoi(val) > 0)\n\t\tcl->autoaimdot = 2; //disable, ignoring sv_aim\n\telse\n\t{\n\t\tval = InfoBuf_ValueForKey (&cl->userinfo, \"aim\");\n\t\tif (*val)\n\t\t{\n\t\t\tcl->autoaimdot = atof(val);\n\t\t\tif (cl->autoaimdot > 1)\n\t\t\t\tcl->autoaimdot = cos(cl->autoaimdot * M_PI/180);//interpret it as an accepted angle in degrees\n\t\t}\n\t\telse\n\t\t\tcl->autoaimdot = sv_aim.value;\n\t}\n\n\t// msg command\n\tval = InfoBuf_ValueForKey (&cl->userinfo, \"msg\");\n\tif (strlen(val))\n\t{\n\t\tcl->messagelevel = atoi(val);\n\t}\n\n\tval = InfoBuf_ValueForKey (&cl->userinfo, \"sp\");\t//naming for compat with mvdsv\n\tif (*val)\n\t\tcl->spec_print = atoi(val);\n\telse\n\t\tcl->spec_print = ~0;\t//if unspecified, default to server setting (even if the cvar is changed mid-map).\n\n#ifdef NQPROT\n\t{\n\t\tint top = atoi(InfoBuf_ValueForKey(&cl->userinfo, \"topcolor\"));\n\t\ttop &= 15;\n\t\tif (top > 13)\n\t\t\ttop = 13;\n\t\tbottom &= 15;\n\t\tif (bottom > 13)\n\t\t\tbottom = 13;\n\t\tif (cl->playercolor != top*16 + bottom)\n\t\t{\n\t\t\tcl->playercolor = top*16 + bottom;\n\t\t\tif (svs.gametype == GT_PROGS || svs.gametype == GT_Q1QVM)\n\t\t\t{\n#ifdef HAVE_LEGACY\n\t\t\t\tif (cl->edict)\n\t\t\t\t\tcl->edict->xv->clientcolors = cl->playercolor;\n#endif\n\t\t\t\tMSG_WriteByte (&sv.nqreliable_datagram, svc_updatecolors);\n\t\t\t\tMSG_WriteByte (&sv.nqreliable_datagram, cl-svs.clients);\n\t\t\t\tMSG_WriteByte (&sv.nqreliable_datagram, cl->playercolor);\n\t\t\t}\n\t\t}\n\t}\n#endif\n}\n\n//============================================================================\n\n/*\n====================\nSV_InitNet\n====================\n*/\nvoid SV_InitNet (void)\n{\n#ifndef SERVERONLY\n\tif (isDedicated)\n#endif\n\t{\n\t\tNetchan_Init ();\n\t}\n\n\tSV_Master_ReResolve();\n\n//\tNET_StringToAdr (\"192.246.40.70:27000\", &idmaster_adr);\n}\n\n/*\n====================\nSV_Init\n====================\n*/\n#ifdef SERVER_DEMO_PLAYBACK\n//FIXME: move to header\nvoid SV_Demo_Init(void);\n#endif\n\nvoid SV_ArgumentOverrides(void)\n{\n\tint p;\n\t// parse params for cvars\n\tp = COM_CheckParm (\"-svport\");\n\tif (!p)\n\t\tp = COM_CheckParm (\"-port\");\n\tif (p && p < com_argc)\n\t{\n\t\tCvar_Set(Cvar_FindVar(\"sv_port\"), com_argv[p+1]);\n\t\tCvar_Set(Cvar_FindVar(\"sv_port_tcp\"), com_argv[p+1]);\n\t}\n}\n\nvoid SV_ExecInitialConfigs(char *defaultexec)\n{\n\tCbuf_AddText(\"cvar_purgedefaults\\n\", RESTRICT_LOCAL);\t//reset cvar defaults to their engine-specified values. the tail end of 'exec default.cfg' will update non-cheat defaults to mod-specified values.\n\tCbuf_AddText(\"cvarreset *\\n\", RESTRICT_LOCAL);\t\t\t//reset all cvars to their current (engine) defaults\n\tCbuf_AddText(\"alias restart \\\"map_restart\\\"\\n\",RESTRICT_LOCAL);\n\n\tCbuf_AddText(va(\"sv_gamedir \\\"%s\\\"\\n\", FS_GetGamedir(true)), RESTRICT_LOCAL);\n\n\tCbuf_AddText(\"cl_warncmd 0\\n\", RESTRICT_LOCAL);\n\tCbuf_AddText(defaultexec, RESTRICT_LOCAL);\n\tCbuf_AddText(\"cl_warncmd 1\\n\", RESTRICT_LOCAL);\n\tCbuf_AddText(\"\\n\", RESTRICT_LOCAL);\n\t//make sure +set args override fmf/engine defaults (redundant when there's no map/etc command in configs)\n\tCOM_ParsePlusSets(true);\n\n\tif (COM_FileSize(\"server.cfg\") != -1)\n\t\tCbuf_AddText (\"cl_warncmd 1\\nexec server.cfg\\nexec ftesrv.cfg\\n\", RESTRICT_LOCAL);\n\telse if (COM_FileSize(\"quake.rc\") != -1)\n\t\tCbuf_AddText (\"cl_warncmd 0\\nexec quake.rc\\ncl_warncmd 1\\nexec ftesrv.cfg\\n\", RESTRICT_LOCAL);\n#ifdef HEXEN2\n\telse if (COM_FileSize(\"hexen.rc\") != -1)\t//fixme: some kind of priority thing.\n\t\tCbuf_AddText (\"cl_warncmd 0\\nexec hexen.rc\\ncl_warncmd 1\\nexec ftesrv.cfg\\n\", RESTRICT_LOCAL);\n#endif\n\telse\n\t\tCbuf_AddText (\"cl_warncmd 0\\nexec default.cfg\\ncl_warncmd 1\\nexec ftesrv.cfg\\n\", RESTRICT_LOCAL);\n\n\t//make sure +set stuff still applies...\n\tCOM_ParsePlusSets(true);\n\n// process command line arguments\n\tCbuf_Execute ();\n\n\tSV_ArgumentOverrides();\n}\n\nstatic void SV_StartInitialMap(void)\n{\n\t// if a map wasn't specified on the command line, spawn start.map\n\t//aliases require that we flush the cbuf in order to actually see the results.\n\tif (sv.state == ss_dead && Cmd_AliasExist(\"dedicated_start\", RESTRICT_LOCAL))\n\t{\n\t\tCbuf_AddText(\"dedicated_start\", RESTRICT_LOCAL);\t//Q2 feature\n\t\tCbuf_Execute();\n\t}\n\tif (sv.state == ss_dead && Cmd_AliasExist(\"startmap_dm\", RESTRICT_LOCAL))\n\t{\n\t\tCbuf_AddText(\"startmap_dm\", RESTRICT_LOCAL);\t//DP extension\n\t\tCbuf_Execute();\n\t}\n\tif (sv.state == ss_dead && Cmd_AliasExist(\"startmap_sp\", RESTRICT_LOCAL))\n\t{\n\t\tCbuf_AddText(\"startmap_sp\", RESTRICT_LOCAL);\t//DP extension\n\t\tCbuf_Execute();\n\t}\n\tif (sv.state == ss_dead && COM_FCheckExists(\"maps/start.bsp\"))\n\t\tCmd_ExecuteString (\"map start\", RESTRICT_LOCAL);\t//regular q1\n#ifdef HEXEN2\n\tif (sv.state == ss_dead && COM_FCheckExists(\"maps/demo1.bsp\"))\n\t\tCmd_ExecuteString (\"map demo1\", RESTRICT_LOCAL);\t//regular h2 sp\n#endif\n#ifdef Q2SERVER\n\tif (sv.state == ss_dead && COM_FCheckExists(\"maps/base1.bsp\"))\n\t\tCmd_ExecuteString (\"map base1\", RESTRICT_LOCAL);\t//regular q2 sp\n#endif\n#ifdef Q3SERVER\n\tif (sv.state == ss_dead && COM_FCheckExists(\"maps/q3dm1.bsp\"))\n\t\tCmd_ExecuteString (\"map q3dm1\", RESTRICT_LOCAL);\t//regular q3 'sp'\n#endif\n#ifdef HLSERVER\n\tif (sv.state == ss_dead && COM_FCheckExists(\"maps/c0a0.bsp\"))\n\t\tCmd_ExecuteString (\"map c0a0\", RESTRICT_LOCAL);\t//regular hl sp\n#endif\n}\n\nstatic void SV_CheckMapless(int iarg, void *data)\n{\t//kills the server if we're dead\n\tstatic int wtf;\n\tif (sv.state != ss_dead)\n\t\treturn;\t//yay, the situation got resolved!\n\telse if (HTTP_CL_GetActiveDownloads())\n\t\t;\t//we still have hope. check again later.\n\telse\n\t{\n#ifdef PACKAGEMANAGER\n\t\tif (!FS_GameIsInitialised())\n\t\t\twtf = -1;\n\t\tif (wtf == 0)\n\t\t{\n\t\t\tCmd_ExecuteString(\"pkg listenabledsources\", RESTRICT_LOCAL);\n\t\t\tCmd_ExecuteString(\"pkg refresh\", RESTRICT_LOCAL);\n\t\t}\n\t\telse if (wtf == 1)\n\t\t{\n\t\t\tCmd_ExecuteString(\"pkg upgrade\", RESTRICT_LOCAL);\n\t\t\tCmd_ExecuteString(\"pkg apply\", RESTRICT_LOCAL);\n\t\t}\n\t\telse if (wtf == 2)\n\t\t\tCmd_ExecuteString(\"fs_restart\", RESTRICT_LOCAL);\t//just in case...\n\t\telse if (wtf == 3)\n\t\t\tSV_StartInitialMap();\t//we should be fully up and running, so try starting that initial map again.\n\t\telse\n#endif\n\t\t{\n\t\t\tCmd_ExecuteString(\"path\", RESTRICT_LOCAL);\n\t\t\tif (COM_CheckParm(\"-allowmapless\"))\n\t\t\t{\t//this is risky and mustn't be on by default. there's a load of scripts that check for success or w/e, so best to not make them arbitrarily fail.\n\t\t\t\tCon_Printf(CON_ERROR\"Couldn't load a map. You may need to use the -basedir argument or to install some packages.\\n\");\n\t\t\t\tCon_Printf(CON_ERROR\"Use the ^[/fs_changegame^] to select the proper basedir/game, or use the ^[/pkg^] command to install required packages, or ^[/quit^] out\\n\");\n\t\t\t}\n\t\t\telse\n\t\t\t\tSV_Error (CON_ERROR\"Couldn't load a map. You may need to use the -basedir argument.\");\n\t\t\treturn;\n\t\t}\n\t\twtf++;\n\t}\n\tCmd_AddTimer(0.1, SV_CheckMapless, 0, NULL,0);\t//we still have hope. check again later.\n}\n\nvoid SV_Init (quakeparms_t *parms)\n{\n\tif (isDedicated)\n\t{\n\t\tCOM_InitArgv (parms->argc, parms->argv);\n\n\n\t\thost_parms = *parms;\n\n\t\tCvar_Init();\n\t\tMemory_Init();\n\n\t\tSys_Init();\n\n\t\tCOM_ParsePlusSets(false);\n\n\t\tCbuf_Init ();\n\t\tCmd_Init ();\n#ifndef SERVERONLY\n\t\tR_SetRenderer(NULL);\n#endif\n\t\tNET_Init ();\n\t\tCOM_Init ();\n#if defined(Q2BSPS) || defined(Q3BSPS)\n\t\tCM_Init();\n#endif\n#ifdef TERRAIN\n\t\tTerr_Init();\n#endif\n\t\tMod_Init (true);\n\t\tMod_Init (false);\n\n\t\tPF_Common_RegisterCvars();\n\t}\n\telse\n\t{\n#if defined(SERVERONLY) || !(defined(CSQC_DAT) || defined(MENU_DAT))\n\t\tPF_Common_RegisterCvars();\n#endif\n\t}\n\n\tPR_Init ();\n\n\tSV_InitNet ();\n\n\tSV_InitLocal ();\n\n#ifdef SERVER_DEMO_PLAYBACK\n\tSV_Demo_Init();\n#endif\n\n#ifdef SVRANKING\n\tRank_RegisterCommands();\n#endif\n\n#ifndef SERVERONLY\n\tif (isDedicated)\n#endif\n\t{\n\t\tint manarg;\n\t\tPM_Init ();\n\n#ifdef PLUGINS\n\t\tPlug_Initialise(true);\n#endif\n\n\t\tCvar_ParseWatches();\n\t\thost_initialized = true;\n\n\n\t\tmanarg = COM_CheckParm(\"-manifest\");\n\t\tif (manarg && manarg < com_argc-1 && com_argv[manarg+1])\n\t\t\tFS_ChangeGame(FS_Manifest_ReadSystem(com_argv[manarg+1], NULL), true, true);\n\t\telse\n\t\t\tFS_ChangeGame(NULL, true, true);\n\n\t\tCmd_StuffCmds();\n\n\t\tMenu_Download_Update();\n\n#ifdef MANIFESTDOWNLOADS\n\t\tif (Sys_RunInstaller())\n\t\t\tSys_Quit();\n#endif\n\n\t\tCon_Printf (\"Exe: %s\\n\", version_string());\n\n\t\tCon_TPrintf (\"======== %s Initialized ========\\n\", *fs_gamename.string?fs_gamename.string:\"Nothing\");\n\n#ifdef SUBSERVERS\n\t\tif (SSV_IsSubServer())\n\t\t{\n\t\t\tNET_InitServer();\n\t\t\treturn;\n\t\t}\n#endif\n\n#ifdef IPLOG\n\t\tIPLog_Merge_File(\"iplog.txt\");\t//should be compatible with DP's take on the feature.\n\t\tIPLog_Merge_File(\"iplog.dat\");\t//legacy crap, for compat with proquake\n#endif\n\n\t\tSV_StartInitialMap();\n\t\tSV_CheckMapless(0,NULL);\t//restarts a timer, kill the server only when the pending downloads have ended.\n\t}\n}\n\n#endif\n\n"
  },
  {
    "path": "engine/server/sv_master.c",
    "content": "#include \"quakedef.h\"\n\n#ifdef SV_MASTER\n\n#ifdef _WIN32\n#include \"winquake.h\"\n#ifdef _MSC_VER\n#include \"wsipx.h\"\n#endif\n#endif\n\n#include \"netinc.h\"\n#include \"fs.h\"\n\n//quakeworld protocol\n//    heartbeat: \"a\"\n//        query: \"c\\n%i<sequence>\\n%i<numplayers>\\n\"\n//queryresponse: \"d\\naaaappaaaapp\"\n#define QUAKEWORLDPROTOCOLNAME \"FTE-Quake\"\n\n//quake2 protocol\n//    heartbeat: \"heartbeat\\n%s<serverinfo>\\n%i<frags> %i<ping> \\\"%s\\\"<name>\\n<repeat>\"\n//        query: \"query\\0\"\n//queryresponse: \"servers\\naaaappaaaapp\"\n#define QUAKE2PROTOCOLNAME \"Quake2\"\n\n//quake3/dpmaster protocol\n//    heartbeat: \"heartbeat DarkPlaces\\n\"\n//        query: \"getservers[Ext<ipv6>] [%s<game>] %u<version> [empty] [full] [ipv6]\"\n//queryresponse: \"getservers[Ext]Response\\\\aaaapp/aaaaaaaaaaaapp\\\\EOF\"\n#define QUAKE3PROTOCOLNAME \"Quake3\"\n\nenum gametypes_e\n{\n\tGT_FFA=0,\n\tGT_TOURNEY=1,\n\tGT_TEAM=3,\n\tGT_CTF=4,\n};\ntypedef struct svm_server_s {\n\tnetadr_t adr;\n\tconst char *brokerid;\t//from rtc broker, for ICE connections (persistent until killed).\n\tint protover;\n\tunsigned int spectators;\t//\n\tunsigned int bots;\t\t\t//non-human players\n\tunsigned int clients;\t\t//human players\n\tunsigned int maxclients;\t//limit of bots+clients, but not necessarily spectators.\n\tunsigned int secure:1;\n\tunsigned int needpass:1;\n\tunsigned int stype:5;\n#define STYPE_COOP\t\t(1<<0)\n#define STYPE_QTV\t\t(1<<1)\n#define STYPE_QWFWD\t\t(1<<2)\n#define STYPE_TURN\t\t(1<<3)\n#define STYPE_NOMOUSE\t(1<<4)\n\tchar hostname[64];\t//just for our own listings.\n\tchar mapname[16];\t//just for our own listings.\n\tchar gamedir[16];\t//again...\n\tchar version[48];\n\tunsigned short gametype;\n\tdouble expiretime;\n\n\tbucket_t bucket;\t//for faster address lookups.\n\tstruct svm_game_s *game;\n\tstruct svm_server_s *next;\n\tchar rules[1024];\n} svm_server_t;\n\ntypedef struct svm_game_s {\n\tstruct svm_game_s *next;\n\n\tsvm_server_t *firstserver;\n\tsize_t numservers;\n\tqboolean persistent;\n\tchar *levelshotsurl;\t//eg \"https://somewhere/somegame/levelshots/\" mapname.jpg will be appended.\n\tchar *aliases;\t//list of terminated names, terminated with a double-null\n\tchar *scheme;\n\tchar name[1];\t//eg: Quake\n} svm_game_t;\n\ntypedef struct {\n\tdouble time;\n\n\tsvm_game_t *firstgame;\n\tsize_t numgames;\n\n\thashtable_t serverhash;\n\tsize_t numservers;\n\n\tstruct rates_s\n\t{\n\t\tdouble timestamp;\n\t\tsize_t heartbeats;\t//heartbeats/serverinfos (general maintainence things due to server counts)\n\t\tsize_t queries;\t\t//players querying for info\n\t\tsize_t junk;\t\t//unknown packets\n\t\tsize_t stun;\t\t//special packets handled by the network layer... though I suppose these should count as queries.\n\n\t\tsize_t drops;\n\t\tsize_t adds;\n\t} total, stamps[60];\n\tsize_t stampring;\n\tdouble nextstamp;\n} masterserver_t;\n\nstatic masterserver_t svm;\nftenet_connections_t *svm_sockets;\n\nstatic void QDECL SVM_Tcpport_Callback(struct cvar_s *var, char *oldvalue)\n{\n\tFTENET_AddToCollection(svm_sockets, var->name, var->string, NA_IP, NP_STREAM);\n}\nstatic void QDECL SVM_Port_Callback(struct cvar_s *var, char *oldvalue)\n{\n\tFTENET_AddToCollection(svm_sockets, var->name, var->string, NA_IP, NP_DGRAM);\n}\nstatic cvar_t sv_heartbeattimeout = CVARD(\"sv_heartbeattimeout\", \"300\", \"How many seconds a server should remain listed after its latest heartbeat. Larger values can avoid issues from packetloss, but can also make dos attacks easier.\");\nstatic cvar_t sv_masterport = CVARC(\"sv_masterport\", STRINGIFY(PORT_QWMASTER)\" \"STRINGIFY(PORT_ICEBROKER), SVM_Port_Callback);\nstatic cvar_t sv_masterport_tcp = CVARC(\"sv_masterport_tcp\", STRINGIFY(PORT_ICEBROKER), SVM_Tcpport_Callback);\nstatic cvar_t sv_maxgames = CVARD(\"sv_maxgames\", \"100\", \"Limits the number of games that may be known. This is to reduce denial of service attacks.\");\nstatic cvar_t sv_maxservers = CVARD(\"sv_maxservers\", \"10000\", \"Limits the number of servers (total from all games) that may be known. This is to reduce denial of service attacks.\");\nstatic cvar_t sv_hideinactivegames = CVARD(\"sv_hideinactivegames\", \"1\", \"Don't show known games that currently have no servers in html listings.\");\nstatic cvar_t sv_sortlist = CVARD(\"sv_sortlist\", \"3\", \"Controls sorting of the http output:\\n0: don't bother\\n1: clients then address\\n2: hostname then address\\n3: clients then hostname then address\\n4: just address\");\nstatic cvar_t sv_hostname = CVARD(\"hostname\", \"Unnamed FTE-Master\", \"Controls sorting of the http output:\\n0: don't bother\\n1: clients then address\\n2: hostname then address\\n3: clients then hostname then address\\n4: just address\");\nstatic cvar_t sv_slaverequery = CVARD(\"sv_slaverequery\", \"120\", \"Requery slave masters at this frequency.\");\nstatic struct sv_masterslave_s\n{\n\tint type;\n\tcvar_t var;\n\tsize_t numaddr;\n\tnetadr_t addr[8];\n} sv_masterslave[] = {\n\t{0,\tCVARD(\"sv_qwmasterslave1\", \"\", \"Specifies a different (quakeworld-protocol) master from which to steal server listings.\")},\n\t{0,\tCVARD(\"sv_qwmasterslave2\", \"\", \"Specifies a different (quakeworld-protocol) master from which to steal server listings.\")},\n\t{0,\tCVARD(\"sv_qwmasterslave3\", \"\", \"Specifies a different (quakeworld-protocol) master from which to steal server listings.\")},\n//\t{1,\tCVARD(\"sv_q2masterslave1\", \"\", \"Specifies a different (quake2-protocol) master from which to steal server listings.\")},\n//\t{1,\tCVARD(\"sv_q2masterslave2\", \"\", \"Specifies a different (quake2-protocol) master from which to steal server listings.\")},\n//\t{1,\tCVARD(\"sv_q2masterslave3\", \"\", \"Specifies a different (quake2-protocol) master from which to steal server listings.\")},\n//\t{2,\tCVARD(\"sv_q3masterslave1\", \"\", \"Specifies a different (quake3-protocol) master from which to steal server listings.\")},\n//\t{2,\tCVARD(\"sv_q3masterslave2\", \"\", \"Specifies a different (quake3-protocol) master from which to steal server listings.\")},\n//\t{2,\tCVARD(\"sv_q3masterslave3\", \"\", \"Specifies a different (quake3-protocol) master from which to steal server listings.\")},\n\t{3,\tCVARD(\"sv_dpmasterslave1\", \"\", \"Specifies a different (dpmaster-protocol) master from which to steal server listings.\")},\n\t{3,\tCVARD(\"sv_dpmasterslave2\", \"\", \"Specifies a different (dpmaster-protocol) master from which to steal server listings.\")},\n\t{3,\tCVARD(\"sv_dpmasterslave3\", \"\", \"Specifies a different (dpmaster-protocol) master from which to steal server listings.\")},\n};\nstatic struct\n{\n\tnetadr_t a;\n\tchar *query;\n} *pingring;\nstatic size_t pingring_first;\nstatic size_t pingring_count;\nstatic size_t pingring_max;\nstatic char *master_css;\n\nstatic unsigned int SVM_GenerateBrokerKey(const char *brokerid)\n{\n\treturn Hash_Key(brokerid, svm.serverhash.numbuckets);\n}\n\n//returns a hash key for a server's address.\nstatic unsigned int SVM_GenerateAddressKey(const netadr_t *adr)\n{\n\tunsigned int key;\n\tswitch(adr->type)\n\t{\n\tcase NA_IP:\n\t\tkey  = *(const unsigned int*)adr->address.ip;\n\t\tkey ^= 0xffff0000;\t//match ipv6's ipv4-mapped addresses.\n\t\tkey ^= adr->port;\n\t\tbreak;\n\tcase NA_IPV6:\n\t\tkey  = *(const unsigned int*)(adr->address.ip6+0);\n\t\tkey ^= *(const unsigned int*)(adr->address.ip6+4);\n\t\tkey ^= *(const unsigned int*)(adr->address.ip6+8);\n\t\tkey ^= *(const unsigned int*)(adr->address.ip6+12);\n\t\tkey ^= adr->port;\n\t\tbreak;\n\tdefault:\n\t\tkey = 0;\n\t\tbreak;\n\t}\n\treturn key;\n}\nstatic svm_server_t *SVM_GetServer(netadr_t *adr)\n{\n\tsvm_server_t *server;\n\tunsigned int key = SVM_GenerateAddressKey(adr);\n\n\tserver = Hash_GetKey(&svm.serverhash, key);\n\twhile (server)\n\t{\n\t\tif (!server->brokerid)\t//don't report brokered servers by address.\n\t\t\tif (NET_CompareAdr(&server->adr, adr))\n\t\t\t\treturn server;\n\t\tserver = Hash_GetNextKey(&svm.serverhash, key, server);\n\t}\n\treturn NULL;\n}\nqboolean SVM_FixupServerAddress(netadr_t *adr, struct dtlspeercred_s *cred)\n{\t//if we get a request to send an ice offer over udp, make sure we respond from the socket they heartbeated from, so their (possible) nat won't block us.\n\t//also make sure the fingerprint stuff is okay.\n\tsvm_server_t *sv = SVM_GetServer(adr);\n\tchar *fp;\n\tsize_t b;\n\tif (!sv)\n\t\treturn false;\n\t*adr = sv->adr;\t//fix it up (mostly the connum so it follows the proper 'return' route)\n\tfp = Info_ValueForKey(sv->rules, \"*fp\");\n\tb = Base64_DecodeBlock(fp, NULL, cred->digest, sizeof(cred->digest));\n\tif (b <= 20)\n\t\tcred->hash = &hash_sha1;\n\telse if (b <= 256/8)\n\t\tcred->hash = &hash_sha2_256;\n\telse if (b <= 512/8)\n\t\tcred->hash = &hash_sha2_512;\n\telse\n\t\treturn false;\t//just no.\n\tif (b > cred->hash->digestsize)\n\t\treturn false;\t//err...\n\tmemset(cred->digest+b, 0, cred->hash->digestsize-b); //make sure its 0-terminated, in case the provided size was wrong\n\treturn true;\n}\n\nstatic svm_game_t *SVM_FindGame(const char *game, int create)\n{\n\tsvm_game_t *g, **link;\n\tconst char *sanitise;\n\tconst char *a;\n\tfor (g = svm.firstgame; g; g = g->next)\n\t{\n\t\tif (!Q_strcasecmp(game, g->name))\n\t\t\treturn g;\n\n\t\tif (g->aliases)\n\t\t{\n\t\t\tfor (a = g->aliases; *a; a+=strlen(a)+1)\n\t\t\t{\n\t\t\t\tif (!Q_strcasecmp(game, a))\n\t\t\t\t\treturn g;\n\t\t\t}\n\t\t}\n\t}\n\n\tif (create)\n\t{\n\t\tif (create < 2)\n\t\t{\n\t\t\tif (svm.numgames >= sv_maxgames.ival)\n\t\t\t{\n\t\t\t\tCon_DPrintf(\"game limit exceeded\\n\");\n\t\t\t\treturn NULL;\n\t\t\t}\n\t\t\t//block some chars that may cause issues/exploits. sorry.\n\t\t\tfor (sanitise = game; *sanitise; sanitise++)\n\t\t\t{\n\t\t\t\tif ((*sanitise >= 'a' && *sanitise <= 'z') ||\t//allow lowercase\n\t\t\t\t\t(*sanitise >= 'A' && *sanitise <= 'Z') ||\t//allow uppercase\n\t\t\t\t\t(*sanitise >= '0' && *sanitise <= '9') ||\t//allow numbers (but not leeding, see below)\n\t\t\t\t\t(*sanitise == '-' || *sanitise == '_'))\t\t//allow a little punctuation, to make up for the lack of spaces.\n\t\t\t\t\tcontinue;\n\t\t\t\treturn NULL;\n\t\t\t}\n\t\t}\n\t\tif (!*game || (*game >= '0' && *game <= '9'))\n\t\t\treturn NULL;\t//must not start with a number either.\n\t\tg = ZF_Malloc(sizeof(*g) + strlen(game));\n\t\tif (g)\n\t\t{\n\t\t\tchar *n;\n\t\t\tstrcpy(g->name, game);\n\t\t\tfor (n = g->name; *n; n++)\n\t\t\t{\t//some extra fixups, because formalnames are messy.\n\t\t\t\tif (*n == ' ' || *n == '\\t' || *n == ':' || *n == '?' || *n == '#' || *n == '.')\n\t\t\t\t\t*n = '_';\n\t\t\t}\n\t\t\tg->persistent = create==2;\n\t\t\tg->next = NULL;\n\n\t\t\t//add it at the end.\n\t\t\tfor (link = &svm.firstgame; *link; link = &(*link)->next)\n\t\t\t\t;\n\t\t\t*link = g;\n\t\t\tsvm.numgames++;\n\t\t\tCon_DPrintf(\"Creating game \\\"%s\\\"\\n\", g->name);\n\t\t}\n\t}\n\treturn g;\n}\n\nstatic int QDECL SVM_SortOrder(const void *v1, const void *v2)\n{\n\tsvm_server_t const*const s1 = *(svm_server_t const*const*const)v1;\n\tsvm_server_t const*const s2 = *(svm_server_t const*const*const)v2;\n\tint t, i;\n\tif (sv_sortlist.ival&8)\n\t\treturn s1->expiretime > s2->expiretime;\n\n\tif (sv_sortlist.ival&64)\n\t{\t//make em sort downwards if they're weird types of servers\n#define STYPE_SPECIAL(s) (!(s->stype&(STYPE_QTV|STYPE_QWFWD|STYPE_TURN)))\n\t\tif ((t=(STYPE_SPECIAL(s2)-STYPE_SPECIAL(s1))))\n\t\t\treturn (t>0)?1:-1;\n\t}\n\tif (sv_sortlist.ival&1)\n\t{\n\t\tif ((t=(s2->clients-s1->clients)))\n\t\t\treturn (t>0)?1:-1;\n\t\tif ((t=(s2->spectators-s1->spectators)))\n\t\t\treturn (t>0)?1:-1;\n\t\tif ((t=(s2->bots-s1->bots)))\n\t\t\treturn (t>0)?1:-1;\n\t}\n\tif (sv_sortlist.ival&16)\n\t\tif ((t=strcmp(s1->version, s2->version)))\n\t\t\treturn (t>0)?1:-1;\n\tif (sv_sortlist.ival&32)\n\t\tif ((t=strcmp(s1->gamedir, s2->gamedir)))\n\t\t\treturn (t>0)?1:-1;\n\tif (sv_sortlist.ival&2)\n\t\tif ((t=strcmp(s1->hostname, s2->hostname)))\n\t\t\treturn (t>0)?1:-1;\n\n\t//sort by scheme, address family, and ip\n\tif ((t=(s1->adr.prot-s2->adr.prot)))\n\t\treturn (t>0)?1:-1;\n\tif ((t=(s1->adr.type-s2->adr.type)))\n\t\treturn (t>0)?1:-1;\n\tif (s1->adr.type==NA_IP)\n\t\ti = sizeof(s1->adr.address.ip);\n\telse if (s1->adr.type==NA_IPV6)\n\t\ti = sizeof(s1->adr.address.ip6);\n\telse i = 0;\n\tfor(t = 0; t < i; t++)\n\t\tif (s1->adr.address.ip6[t] != s2->adr.address.ip6[t])\n\t\t\treturn (s1->adr.address.ip6[t]>s2->adr.address.ip6[t])?1:-1;\n\n\t//and now do port numbers too.\n\tt = BigShort(s1->adr.port) - BigShort(s2->adr.port);\n\tif (t)\n\t\treturn (t>0)?1:-1;\n\treturn 0;\n}\n\nstatic void SVM_SortServers(svm_game_t *game)\n{\n\tsvm_server_t **serverlink, *s;\n\tsvm_server_t **sv = malloc(sizeof(*sv)*game->numservers);\n\tint i;\n\tif (!sv_sortlist.ival)\n\t\treturn;\n\n\tfor (i=0, s = game->firstserver; s; s = s->next)\n\t\tsv[i++] = s;\n\tqsort(sv, i, sizeof(*sv), SVM_SortOrder);\n\n\tfor (i = 0, serverlink = &game->firstserver; i < game->numservers; i++)\n\t{\n\t\t*serverlink = sv[i];\n\t\tserverlink = &sv[i]->next;\n\t}\n\t*serverlink = NULL;\n}\n\nstatic void SVM_RemoveOldServers(void)\n{\n\tsvm_game_t **gamelink, *g;\n\tsvm_server_t **serverlink, *s;\n\tfor (gamelink = &svm.firstgame; (g=*gamelink); )\n\t{\n\t\tfor (serverlink = &g->firstserver; (s=*serverlink); )\n\t\t{\n\t\t\t//brokered servers don't time out (they drop when their tcp connection dies)\n\t\t\tif (!s->brokerid && s->expiretime < svm.time)\n\t\t\t{\n\t\t\t\tif (developer.ival)\n\t\t\t\t{\n\t\t\t\t\tchar buf[256];\n\t\t\t\t\tCon_Printf(\"timeout: %s\\n\", NET_AdrToString(buf, sizeof(buf), &s->adr));\n\t\t\t\t}\n\n\t\t\t\tHash_RemoveDataKey(&svm.serverhash, SVM_GenerateAddressKey(&s->adr), s);\n\n\t\t\t\tsvm.total.drops++;\n\t\t\t\t*serverlink = s->next;\n\t\t\t\tBZ_Free(s);\n\t\t\t\tg->numservers--;\n\t\t\t\tsvm.numservers--;\n\t\t\t}\n\t\t\telse\n\t\t\t\tserverlink = &s->next;\n\t\t}\n\n\t\tif (!g->firstserver && !g->persistent)\n\t\t{\n\t\t\tCon_DPrintf(\"game \\\"%s\\\" has no active servers\\n\", g->name);\n\t\t\t*gamelink = g->next;\n\t\t\tZ_Free(g);\n\t\t\tsvm.numgames--;\n\t\t}\n\t\telse\n\t\t\tgamelink = &g->next;\n\t}\n}\n\nint SVM_AddIPAddresses(sizebuf_t *sb, int first, int ver, const char *gamename, int v4, int v6, qboolean empty, qboolean full, qboolean prefixes, int gametype)\n{\n\tint number = 0;\n\tsvm_server_t *server;\n\tint prefix;\n\tint len;\n\tsvm_game_t *game = SVM_FindGame(gamename, false);\n\tif (game)\n\t{\n\t\tfor (server = game->firstserver; server; server = server->next)\n\t\t{\n\t\t\tif (number == first)\n\t\t\t\tbreak;\n\n\t\t\tfirst--;\n\t\t}\n\n\t\tfor (; server; server = server->next)\n\t\t{\n\t\t\tif (server->protover != ver)\n\t\t\t\tcontinue;\n\t\t\tif (server->clients == 0 && !empty)\n\t\t\t\tcontinue;\n\t\t\tif (server->clients+server->bots >= server->maxclients && !full)\n\t\t\t\tcontinue;\n\t\t\tif (gametype != -1 && server->gametype != gametype)\n\t\t\t\tcontinue;\n\t\t\tswitch(server->adr.type)\n\t\t\t{\n\t\t\tcase NA_IP:\n\t\t\t\tif (!v4)\n\t\t\t\t\tcontinue;\n\t\t\t\tprefix = '\\\\';\n\t\t\t\tlen = 4;\n\t\t\t\tbreak;\n\t\t\tcase NA_IPV6:\n\t\t\t\tif (!v6)\n\t\t\t\t\tcontinue;\n\t\t\t\tprefix = '/';\n\t\t\t\tlen = 16;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (prefixes)\n\t\t\t\tMSG_WriteByte(sb, prefix);\n\n\t\t\tSZ_Write(sb, server->adr.address.ip, len);\n\t\t\tMSG_WriteShort(sb, server->adr.port);\n\n\t\t\tnumber++;\n\t\t}\n\t}\n\treturn number;\n}\n\nstatic char *QuakeCharsToHTML(char *outhtml, size_t outsize, const char *quake, qboolean deunderscore)\n{\n\tchar *ret = outhtml;\n\tconchar_t chars[8192], *c=chars, *end;\n\tunsigned int codeflags, codepoint, oldflags=CON_WHITEMASK;\n\tunsigned int b;\n\tqboolean obscene;\n\n\tconst char *htmlnames[16] = {\n\t\t\"#000000\",\t//black\n\t\t\"#0000AA\",\t//dark blue\n\t\t\"#00AA00\",\t//dark green\n\t\t\"#00AAAA\",\t//dark cyan\n\t\t\"#AA0000\",\t//dark red\n\t\t\"#AA00AA\",\t//dark magenta\n\t\t\"#AA5500\",\t//brown\n\t\t\"#AAAAAA\",\t//grey\n\t\t\"#555555\",\t//dark grey\n\t\t\"#5555FF\",\t//blue\n\t\t\"#55FF55\",\t//green\n\t\t\"#55FFFF\",\t//cyan\n\t\t\"#FF5555\",\t//red\n\t\t\"#FF55FF\",\t//magenta\n\t\t\"#FFFF55\",\t//yellow\n\t\t\"#FFFFFF\",\t//white\n\t};\n\n\tif (!outsize--)\n\t\treturn NULL;\t//no space for the null\n\n\tend = COM_ParseFunString(oldflags, quake, chars, sizeof(chars), false);\n\tobscene = TL_FilterObsceneCCStringInplace(chars, end);\t//strip the swears without breaking cullers.\n\tif (obscene)\n\t{\n\t\tQ_snprintfz(outhtml, outsize, \"<span title='The original string contained obscenities, which have been filtered out.'>\");\n\t\tb=strlen(outhtml);\n\t\touthtml += b;\n\t\toutsize -= b;\n\t}\n\twhile (c < end)\n\t{\n\t\tc = Font_Decode(c, &codeflags, &codepoint);\n\n\t\tif (codeflags & CON_HIDDEN)\n\t\t\tcontinue; //erk...?\n\n\t\tif (codeflags & CON_RICHFORECOLOUR)\t//fixme...?\n\t\t\tcodeflags = CON_WHITEMASK;\n\t\tif ((codeflags&CON_FGMASK) != (oldflags&CON_FGMASK))\n\t\t{\n\t\t\tif (oldflags != CON_WHITEMASK)\n\t\t\t{\n\t\t\t\tQ_strncpyz(outhtml, \"</span>\", outsize);\n\t\t\t\tb=strlen(outhtml);\n\t\t\t\touthtml += b;\n\t\t\t\toutsize -= b;\n\t\t\t}\n\t\t\tif (codeflags != CON_WHITEMASK)\n\t\t\t{\n\t\t\t\tQ_snprintfz(outhtml, outsize, \"<span style=\\\"color:%s\\\">\", htmlnames[(codeflags&CON_FGMASK)>>CON_FGSHIFT]);\n\t\t\t\tb=strlen(outhtml);\n\t\t\t\touthtml += b;\n\t\t\t\toutsize -= b;\n\t\t\t}\n\t\t\toldflags = codeflags;\n\t\t}\n\t\tif (codepoint == '<')\n\t\t{\n\t\t\tQ_strncpyz(outhtml, \"&lt;\", outsize);\n\t\t\tb=strlen(outhtml);\n\t\t}\n\t\telse if (codepoint == '>')\n\t\t{\n\t\t\tQ_strncpyz(outhtml, \"&gt;\", outsize);\n\t\t\tb=strlen(outhtml);\n\t\t}\n\t\telse if (codepoint == '&')\n\t\t{\n\t\t\tQ_strncpyz(outhtml, \"&amp;\", outsize);\n\t\t\tb=strlen(outhtml);\n\t\t}\n\t\telse if (codepoint == '\\\"')\n\t\t{\n\t\t\tQ_strncpyz(outhtml, \"&quot;\", outsize);\n\t\t\tb=strlen(outhtml);\n\t\t}\n\t\telse if (codepoint == '\\'')\n\t\t{\n\t\t\tQ_strncpyz(outhtml, \"&apos;\", outsize);\n\t\t\tb=strlen(outhtml);\n\t\t}\n\t\telse if (codepoint >= 0xe086 && codepoint <= 0xe089)\n\t\t{\n\t\t\tconst char *lednames[] = {\"green\", \"red\", \"yellow\", \"blue\"};\n\t\t\tQ_snprintfz(outhtml, outsize, \"<span style=\\\"color:%s\\\">&#x25A0;</span>\", lednames[codepoint-0xe086]);\n\t\t\tb=strlen(outhtml);\n\t\t}\n\t\telse if (codepoint == '_' && deunderscore)\n\t\t\t*outhtml = ' ', b =1;\n\t\telse\n\t\t\tb = utf8_encode(outhtml, codepoint, outsize);\n\t\tif (b > 0)\n\t\t{\n\t\t\touthtml += b;\n\t\t\toutsize -= b;\n\t\t}\n\t}\n\tif (oldflags != CON_WHITEMASK)\n\t{\n\t\tQ_strncpyz(outhtml, \"</span>\", outsize);\n\t\tb=strlen(outhtml);\n\t\touthtml += b;\n\t\toutsize -= b;\n\t}\n\tif (obscene)\n\t{\n\t\tQ_snprintfz(outhtml, outsize, \"</span>\");\n\t\tb=strlen(outhtml);\n\t\touthtml += b;\n\t\toutsize -= b;\n\t}\n\t*outhtml = 0;\n\treturn ret;\n}\n\nstatic void SVM_Init(void)\n{\n\tmaster_css = FS_MallocFile(\"master.css\", FS_ROOT, NULL);\n\tif (!master_css)\n\t\tmaster_css = Z_StrDup(\n\t\t\t\"<!DOCTYPE html><meta charset=\\\"UTF-8\\\"/>\"\n\t\t\t\"<style type=\\\"text/css\\\">\"\n\t\t\t\t\t\"body {\"\n\t\t\t\t\t\t\"background-color:\t#303030;\"\n\t\t\t\t\t\t\"color:\t\t\t#998080;\"\n\t\t\t\t\t\t\"font-family:\t\tverdanah, trebuchet ms, arial, sans-serif;\"\n\t\t\t\t\t\"}\"\n\t\t\t\t\t\"a         { text-decoration: none; }\"\n\t\t\t\t\t\"a:link    { color: #308090; }\"\n\t\t\t\t\t\"a:visited { color: #308090; }\"\n\t\t\t\t\t\"a:hover   { color: #308090; text-decoration: underline; }\"\n\t\t\t\t\t\".header { text-align: center; }\"\n\t\t\t\t\t\".footer { text-align: center; }\"\n\t\t\t\t\t\".main { width: 1024px; margin: 0 auto; }\"\n\t\t\t\t\t\"H1 {\"\n\t\t\t\t\t\t\"text-align: center;\"\n\t\t\t\t\t\t\"background-color:\t#202020;\"\n\t\t\t\t\t\t\"color:\t\t\t#808080;\"\n\t\t\t\t\t\"}\"\n\t\t\t\t\t\"table { width: 100%; border-collapse: collapse; }\"\n\t\t\t\t\t\"th { text-align: left; }\"\n\t\t\t\t\t\"tr:hover { background-color:\t#202020; }\"\n\t\t\t\t\"</style>\"\n\t\t\t);\n}\n\nvfsfile_t *SVM_Generate_Gamelist(const char **mimetype, const char *query)\n{\n\tvfsfile_t *f = VFSPIPE_Open(1, false);\n\tchar tmpbuf[256];\n\tchar hostname[1024];\n\tsvm_game_t *game;\n\tsvm_server_t *server;\n\tunsigned clients=0,bots=0,specs=0, totalclients=0, totalbots=0, totalspecs=0;\n\n\tVFS_PRINTF(f, \"%s\", master_css);\n\tVFS_PRINTF(f, \"<h1>%s</h1>\\n\", sv_hostname.string);\n\tVFS_PRINTF(f, \"<table border=1>\\n\");\n\tVFS_PRINTF(f, \"<tr><th>Active Games</th><th>Players</th><th>Server Count</th></tr>\\n\");\n\tfor (game = svm.firstgame; game; game = game->next)\n\t{\n\t\tfor (clients=0,bots=0,specs=0, server = game->firstserver; server; server = server->next)\n\t\t{\n\t\t\tclients += server->clients;\n\t\t\tbots += server->bots;\n\t\t\tspecs += server->spectators;\n\t\t}\n\t\tif (game->numservers || !sv_hideinactivegames.ival)\t//only show active servers\n\t\t{\n\t\t\tQuakeCharsToHTML(tmpbuf, sizeof(tmpbuf), game->name, true);\n\t\t\tVFS_PRINTF(f, \"<tr><td><a href=\\\"/game/%s%s%s\\\">%s</a></td><td>%u player%s\", game->name, query?\"?\":\"\", query?query:\"\", tmpbuf, clients, clients==1?\"\":\"s\");\n\t\t\tif (bots)\n\t\t\t\tVFS_PRINTF(f, \", %u bot%s\", bots, bots==1?\"\":\"s\");\n\t\t\tif (specs)\n\t\t\t\tVFS_PRINTF(f, \", %u spectator%s\", specs, specs==1?\"\":\"s\");\n\t\t\tVFS_PRINTF(f, \"</td><td>%u server%s</td></tr>\\n\", (unsigned)game->numservers, game->numservers==1?\"\":\"s\");\n\t\t}\n\t\ttotalclients += clients;\n\t\ttotalbots += bots;\n\t\ttotalspecs += specs;\n\t}\n\tVFS_PRINTF(f, \"</table>\\n\");\n\tVFS_PRINTF(f, \"%u game%s\", (unsigned)svm.numgames, svm.numgames==1?\"\":\"s\");\n\tif (totalclients)\n\t\tVFS_PRINTF(f, \", %u player%s\", totalclients, totalclients==1?\"\":\"s\");\n\tif (totalbots)\n\t\tVFS_PRINTF(f, \", %u bot%s\", totalbots, totalbots==1?\"\":\"s\");\n\tif (totalspecs)\n\t\tVFS_PRINTF(f, \", %u spectator%s\", totalspecs, totalspecs==1?\"\":\"s\");\n\tVFS_PRINTF(f, \", %u server%s<br/>\\n\", (unsigned)svm.numservers, svm.numservers==1?\"\":\"s\");\n\n\tnet_from.prot = NP_DGRAM;\n\tVFS_PRINTF(f, \"Your IP is %s<br/>\\n\", NET_BaseAdrToString(hostname, sizeof(hostname), &net_from));\n\n\t*mimetype = \"text/html\";\n\treturn f;\n}\nstatic void SVM_PrintServerPrefixes(vfsfile_t *stream, svm_server_t *server)\n{\n\tif (server->secure)\n\t\tVFS_PRINTF(stream,\t\"%s\", \"<span title='Supports encryption'>&#x1F6E1;</span>\");\t//'shield'\n\telse\n\t\tVFS_PRINTF(stream,\t\"%s\", \"<span title='Does not support encryption'>&#x26A0;&#xFE0F;</span>\"); //(yellow) 'Warning'\n\tif (server->needpass&1)\n\t\tVFS_PRINTF(stream,\t\"%s\", \"<span title='Needs Password'>&#x1F512;</span>\");\t//'Lock'\n\n\tif (server->stype&STYPE_COOP)\n\t\tVFS_PRINTF(stream,\t\"%s\", \"<span title='Coop'>&#x262E;</span>\");\t//'Peace'\n\telse if (server->stype&STYPE_QTV)\n\t\tVFS_PRINTF(stream,\t\"%s\", \"<span title='QuakeTV'>&#x1F4FA;</span>\");\t//'Television'\n\telse if (server->stype&STYPE_QWFWD)\n\t\tVFS_PRINTF(stream,\t\"%s\", \"<span title='QWFwd'>&#x1F6F0;</span>\");\t//'Satellite'\n\n\tif (server->stype&STYPE_NOMOUSE)\n\t\tVFS_PRINTF(stream,\t\"%s\", \"<span title='Controllers only'>&#x1F3AE;</span>\");\t//'Video Game'(Controller)\n}\nstruct rulelist_s\n{\n\tunsigned int lines;\n\tunsigned int blobofs;\n\tchar *line[64];\n\tchar blob[8192];\n};\nstatic void SVM_GatherServerRule(void *ctx, const char *key, const char *val)\n{\n\tstruct rulelist_s *rules = ctx;\n\tchar nicekey[256];\n\tchar niceval[256];\n\tif (rules->lines == countof(rules->line))\n\t\treturn;\t//overflow\n\tif (*key == '_')\n\t\tval = \"<PRIVATE>\";\t//was meant to be private... lets show that its there, just not what it is.\n\tQuakeCharsToHTML(nicekey, sizeof(nicekey), key, false);\n\tQuakeCharsToHTML(niceval, sizeof(niceval), val, false);\n\tif (!Q_snprintfz(rules->blob+rules->blobofs, sizeof(rules->blob)-rules->blobofs, \"<tr><td>%s</td><td>%s</td></tr>\\n\", nicekey, niceval))\n\t{\n\t\trules->line[rules->lines++] = rules->blob+rules->blobofs;\n\t\trules->blobofs += strlen(rules->blob+rules->blobofs)+1;\n\t}\n}\nstatic int QDECL SVM_SortServerRule(const void *r1, const void *r2)\n{\n\treturn Q_strcasecmp(*(char*const*const)r1, *(char*const*const)r2);\n}\nvoid SVM_Generate_ServerinfoEntry(vfsfile_t *f, const char *masteraddr, svm_server_t *server, const char *query)\n{\n\tchar tmpbuf[512];\n\tchar hostname[1024];\n\tchar gamedir[256];\n\tchar mapname[256];\n\tchar qmapname[256];\n\tsize_t u;\n\tconst char *url, *fp;\n\n\tVFS_PRINTF(f, \"<table border=1>\\n\");\n\tVFS_PRINTF(f, \"<tr><th>Game</th><th>Address</th><th>Hostname</th><th>Mod dir</th><th>Mapname</th><th>Players</th></tr>\\n\");\n\tQuakeCharsToHTML(hostname, sizeof(hostname), server->hostname, false);\n\tQuakeCharsToHTML(gamedir, sizeof(gamedir), server->gamedir, false);\n\tQuakeCharsToHTML(mapname, sizeof(mapname), server->mapname, false);\n\tCOM_QuotedString(server->mapname, qmapname,sizeof(qmapname), true);\n\n\tif (server->brokerid)\n\t{\n\t\tchar *bs = net_ice_broker.string;\n\t\tif (!strncmp(bs, \"tcp://\", 6)||!strncmp(bs, \"tls://\", 6)||!strncmp(bs, \"wss://\", 6)) bs += 6;\n\t\tif (!strcmp(masteraddr, bs))\n\t\t\turl=tmpbuf, Q_snprintfz(tmpbuf, sizeof(tmpbuf), \"/%s\", server->brokerid);\n\t\telse\n\t\t\turl=tmpbuf, Q_snprintfz(tmpbuf, sizeof(tmpbuf), \"rtc://%s/%s\", masteraddr, server->brokerid);\n\t}\n\telse\n\t\turl = NET_AdrToString(tmpbuf, sizeof(tmpbuf), &server->adr);\n\tfp = Info_ValueForKey(server->rules, \"*fp\");\n\tif (*fp)\n\t\tfp = va(\"?fp=%s\", Info_ValueForKey(server->rules, \"*fp\"));\n\tif (server->game->scheme && !server->brokerid)\n\t\turl = va(\"<a href=\\\"%s://%s%s\\\">%s</a>\", server->game->scheme, url,fp, url);\n\n\tVFS_PRINTF(f, \"<tr><td><a href=\\\"/game/%s%s%s\\\">%s</a></td><td>%s</td><td>\",\n\t\tserver->game?server->game->name:\"Unknown\", query?\"?\":\"\", query?query:\"\", server->game?server->game->name:\"Unknown\",\t//game column\n\t\turl);\t//address column\n\tSVM_PrintServerPrefixes(f, server);\n\tVFS_PRINTF(f,\t\"%s</td><td>%s</td><td>%s</td><td>%u/%u</td></tr>\\n\",\n\t\thostname, gamedir, mapname, server->clients, server->maxclients);\n\tVFS_PRINTF(f, \"</table>\\n\");\n\tVFS_PRINTF(f, \"<br/>\\n\");\n\n\tif (*server->rules)\n\t{\n\t\tstruct rulelist_s rules;\n\t\trules.lines = rules.blobofs = 0;\n\t\tInfo_Enumerate(server->rules, &rules, SVM_GatherServerRule);\n\t\tqsort(rules.line, rules.lines, sizeof(rules.line[0]), SVM_SortServerRule);\n\n\t\tVFS_PRINTF(f, \"<table><tr style='background-color:#303030;'><td><table border=1>\\n\");\n\t\t\tVFS_PRINTF(f, \"<tr><th>Rule</th><th>Value</th></tr>\\n\");\n\t\t\tfor (u = 0; u < rules.lines; u++)\n\t\t\t\tVFS_PUTS(f, rules.line[u]);\n\t\tVFS_PRINTF(f, \"</table></td>\");\n\t\tif (*qmapname && server->game->levelshotsurl)\n\t\t\tVFS_PRINTF(f, \"<td style='width:1px;'><img src=\\\"%s%s.jpg\\\" alt=\\\"\\\"></td>\", server->game->levelshotsurl, qmapname);\n\t\tVFS_PRINTF(f, \"</tr></table>\");\n\t\tVFS_PRINTF(f, \"<br/>\\n\");\n\t}\n}\nvoid SVM_GenChallenge(char *out, size_t outsize, netadr_t *foradr);\nstatic svm_server_t *SVM_FindBrokerHost(const char *brokerid);\nstatic vfsfile_t *SVM_Generate_RoomServerinfo(const char **mimetype, const char *masteraddr, const char *serveraddr, const char *query)\n{\n\tvfsfile_t *f = VFSPIPE_Open(1, false);\n\tsvm_server_t *server = SVM_FindBrokerHost(serveraddr);\n\n\tVFS_PRINTF(f, \"%s\", master_css);\n\tVFS_PRINTF(f, \"<h1>Single Server Info</h1>\\n\");\n\n\tif (server)\n\t\tSVM_Generate_ServerinfoEntry(f, masteraddr, server, query);\n\telse\n\t{\n\t\tVFS_PRINTF(f, \"<table border=1>\\n\");\n\t\tVFS_PRINTF(f, \"<tr><th>Game</th><th>Address</th><th>Hostname</th><th>Gamedir</th><th>Mapname</th><th>Players</th></tr>\\n\");\n\t\tVFS_PRINTF(f, \"<tr><td>?</td><td>%s</td><td>?</td><td>?</td><td>?</td><td>?/?</td></tr>\\n\", serveraddr);\n\t\tVFS_PRINTF(f, \"</table>\\n\");\n\t}\n\n\t*mimetype = \"text/html\";\n\treturn f;\n}\nstatic vfsfile_t *SVM_Generate_AddrServerinfo(const char **mimetype, const char *masteraddr, const char *serveraddr, const char *query)\n{\n\tvfsfile_t *f = VFSPIPE_Open(1, false);\n\tchar tmpbuf[512];\n//\tchar hostname[1024];\n\tsvm_server_t *server;\n\tnetadr_t adr[64];\n\tsize_t count;//, u;\n\tstatic double nextquery;\n//\tconst char *url, *fp;\n\n\tVFS_PRINTF(f, \"%s\", master_css);\n\tVFS_PRINTF(f, \"<h1>Single Server Info</h1>\\n\");\n\n#if 1\n\tcount = NET_StringToAdr_NoDNS(serveraddr, 0, adr)?1:0;\n#else\n\t//FIXME: block dns lookups here?\n\tcount = NET_StringToAdr2(serveraddr, 0, adr, countof(adr), NULL);\n#endif\n\twhile(count-->0)\n\t{\n\t\tserver = SVM_GetServer(&adr[count]);\n\t\tif (server)\n\t\t\tSVM_Generate_ServerinfoEntry(f, masteraddr, server, query);\n\t\telse\n\t\t{\n\t\t\tqboolean doingquery = false;\n\t\t\tdouble time = Sys_DoubleTime();\n\t\t\tif (time > nextquery)\n\t\t\t{\n\t\t\t\tsizebuf_t sb;\n\t\t\t\tchar ourchallenge[256];\n\t\t\t\tSVM_GenChallenge(ourchallenge, sizeof(ourchallenge), &adr[count]);\n\t\t\t\tsvm.total.queries++;\n\n\t\t\t\tmemset(&sb, 0, sizeof(sb));\n\t\t\t\tsb.maxsize = sizeof(net_message_buffer);\n\t\t\t\tsb.data = net_message_buffer;\n\n\t\t\t\t//send a qw request, in case the user is checking up on a poopy mvdsv server\n\t\t\t\tMSG_WriteLong(&sb, -1);\n\t\t\t\tMSG_WriteString(&sb, va(\"status %i\\n\", 15));\n\t\t\t\tsb.cursize--;\n\t\t\t\tNET_SendPacket(svm_sockets, sb.cursize, sb.data, &adr[count]);\n\n\t\t\t\tsb.cursize = 0;\n\t\t\t\t//send a proper request, one with a challenge that we can trust.\n\t\t\t\tMSG_WriteLong(&sb, -1);\n\t\t\t\tMSG_WriteString(&sb, va(\"getinfo %s\\n\", ourchallenge));\n\t\t\t\tsb.cursize--;\n\t\t\t\tNET_SendPacket(svm_sockets, sb.cursize, sb.data, &adr[count]);\n\n\t\t\t\tdoingquery = true;\n\t\t\t\tnextquery = time + 10;\t//don't let people do this more than one time every 10 secs. don't abuse (dead) servers, don't be a DoS.\n\n\t\t\t\t//try and get the browser to refresh once we have a response, hopefully.\n\t\t\t\tVFS_PRINTF(f, \"<meta http-equiv='refresh' content='5'>\");\n\t\t\t}\n\n\t\t\tVFS_PRINTF(f, \"<table border=1>\\n\");\n\t\t\tVFS_PRINTF(f, \"<tr><th>Game</th><th>Address</th><th>Hostname</th><th>Gamedir</th><th>Mapname</th><th>Players</th></tr>\\n\");\n\t\t\tVFS_PRINTF(f, \"<tr><td>?</td><td>%s</td><td>%s</td><td>?</td><td>?</td><td>?/?</td></tr>\\n\", NET_AdrToString(tmpbuf, sizeof(tmpbuf), &adr[count]), doingquery?\"&lt;QUERYING ...&gt;\":\"?\");\n\t\t\tVFS_PRINTF(f, \"</table>\\n\");\n\t\t}\n\t}\n\n\tVFS_PRINTF(f, \"<br/><a href=\\\"/\\\">Other Protocols</a>\\n\");\n\n\t*mimetype = \"text/html\";\n\treturn f;\n}\n\nvfsfile_t *SVM_Generate_Serverlist(const char **mimetype, const char *masteraddr, const char *gamename, const char *query)\n{\n\tvfsfile_t *f = VFSPIPE_Open(1, false);\n\tchar tmpbuf[256];\n\tchar hostname[1024];\n\tconst char *url, *infourl, *preurl;\n\tsvm_game_t *game;\n\tsvm_server_t *server;\n\tunsigned clients=0,bots=0,specs=0;\n\n\tqboolean showver = query && !!strstr(query, \"ver=1\");\n\tgame = SVM_FindGame(gamename, false);\n\n\tVFS_PRINTF(f, \"%s\", master_css);\n\n\tif (!strcmp(gamename, \"UNKNOWN\"))\n\t{\n\t\tVFS_PRINTF(f, \"<h1>Unresponsive Servers</h1>\\n\");\n\t\tVFS_PRINTF(f, \"These servers have sent a heartbeat but have failed to respond to an appropriate query from a different port. This can happen when:<ul>\"\n\t\t\"<li>Server is firewalled and all inbound packets are dropped. Try reconfiguring your firewall to allow packets to that process or port.</li>\"\n\t\t\"<li>An intermediate router implements an Address/Port-Dependant-Filtering NAT. Try setting up port forwarding.</li>\"\n\t\t\"<li>Outgoing connections are asynchronous with regard to port forwarding. Such servers will show with arbitrary ephemerial ports. Docker: you can supposedly work around this with --net=host.</li>\"\n\t\t\"<li>Plain and simple packet loss, servers in this state for less than 5 mins may still be fine.</li>\"\n\t\t\"<li>Server crashed or was reconfigured before it could respond.</li>\"\n\t\t\"<li>MTU limits with failed defragmentation.</li>\"\n\t\t\"<li>Routing table misconfiguration.</li>\"\n\t\t\"<li>Other.</li>\"\n\t\t\"</ul>\\n\");\n\t}\n\telse\n\t\tVFS_PRINTF(f, \"<h1>Servers for %s</h1>\\n\", QuakeCharsToHTML(tmpbuf, sizeof(tmpbuf), gamename, true));\n\n\tif(game)\n\t{\n\t\tSVM_SortServers(game);\n\n\t\tVFS_PRINTF(f, \"<table border=1>\\n\");\n\t\tVFS_PRINTF(f, \"<tr><th>Address</th><th>Hostname</th><th>Gamedir</th><th>Mapname</th><th>Players</th>\");\n\t\tif (showver)\n\t\t\tVFS_PRINTF(f, \"<th>Version</th>\");\n\t\tVFS_PRINTF(f, \"</tr>\\n\");\n\t\tfor (server = game->firstserver; server; server = server->next)\n\t\t{\n\t\t\tQuakeCharsToHTML(hostname, sizeof(hostname), server->hostname, false);\n\t\t\tif (server->brokerid)\n\t\t\t{\n\t\t\t\turl = tmpbuf;\n\t\t\t\tQ_snprintfz(tmpbuf, sizeof(tmpbuf), \"rtc://%s/%s\", masteraddr, server->brokerid);\n\t\t\t\tinfourl = server->brokerid;\n\t\t\t\tpreurl = \"/room/\";\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tinfourl = url = NET_AdrToString(tmpbuf, sizeof(tmpbuf), &server->adr);\n\t\t\t\tpreurl = \"/server/\";\n\t\t\t}\n\t\t\tVFS_PRINTF(f, \"<tr><td><a href=\\\"%s%s\\\">%s</a></td><td>\", preurl,infourl, url);\n\t\t\tSVM_PrintServerPrefixes(f, server);\n\t\t\tVFS_PRINTF(f, \"%s</td><td>%s</td><td>%s</td><td>%u\", hostname, server->gamedir, server->mapname, server->clients);\n\t\t\tif (server->bots)\n\t\t\t\tVFS_PRINTF(f, \"+%ub\", server->bots);\n\t\t\tVFS_PRINTF(f, \"/%u\", server->maxclients);\n\t\t\tif (server->spectators)\n\t\t\t\tVFS_PRINTF(f, \", %us\", server->spectators);\n\t\t\tVFS_PRINTF(f, \"</td>\");\n\t\t\tif (showver)\n\t\t\t\tVFS_PRINTF(f, \"<td>%s</td>\", server->version);\n\t\t\tVFS_PRINTF(f, \"</tr>\\n\");\n\t\t\tclients += server->clients;\n\t\t\tbots += server->bots;\n\t\t\tspecs += server->spectators;\n\t\t}\n\t\tVFS_PRINTF(f, \"</table>\\n\");\n\t\tVFS_PRINTF(f, \"%u server%s\", (unsigned)game->numservers, game->numservers==1?\"\":\"s\");\n\t\tif (clients)\n\t\t\tVFS_PRINTF(f, \", %u client%s\", (unsigned)clients, clients==1?\"\":\"s\");\n\t\tif (bots)\n\t\t\tVFS_PRINTF(f, \", %u bot%s\", (unsigned)bots, bots==1?\"\":\"s\");\n\t\tif (specs)\n\t\t\tVFS_PRINTF(f, \", %u spectator%s\", (unsigned)specs, specs==1?\"\":\"s\");\n\t\tVFS_PRINTF(f, \"<br/>\\n\");\n\t}\n\telse\n\t\tVFS_PRINTF(f, \"Protocol '%s' is not known\\n\", gamename);\n\n\tVFS_PRINTF(f, \"<br/><a href=\\\"/\\\">Other Protocols</a>\\n\");\n\n\t*mimetype = \"text/html\";\n\treturn f;\n}\n\nvfsfile_t *SVM_Generate_Rawlist(const char **mimetype, const char *masteraddr, const char *gamename, const char *query)\n{\t//just spews all\n\tchar tmpbuf[256];\n\tsvm_game_t *game;\n\tsvm_server_t *server;\n\tvfsfile_t *f = VFSPIPE_Open(1, false);\n\tchar *fp;\n\tchar *prot;\n\n\tmasteraddr = \"\";\t//client should work this out based on where it got the list from.\n\n\tCOM_StripExtension(gamename, tmpbuf, sizeof(tmpbuf));\n\tgame = SVM_FindGame(tmpbuf, false);\n\n\tf = VFSPIPE_Open(1, false);\n\tVFS_PRINTF(f, \"#Server list for \\\"%s\\\"\\n\", tmpbuf);\n\tfor (server = (game?game->firstserver:NULL); server; server = server->next)\n\t{\n\t\tprot = Info_ValueForKey(server->rules, \"protocol\");\n\t\tif (!*prot)\n\t\t\tprot = va(\"%i\", server->protover);\n\t\tif (server->brokerid)\n\t\t\tVFS_PRINTF(f, \"rtc://%s/%s \\\\protocol\\\\%s\\\\maxclients\\\\%u\\\\clients\\\\%u\\\\bots\\\\%u\\\\hostname\\\\%s\\\\modname\\\\%s\\\\mapname\\\\%s\\\\needpass\\\\%i\\n\", masteraddr, server->brokerid, prot, server->maxclients, server->clients, server->bots, *server->hostname?server->hostname:\"unnamed\", *server->gamedir?server->gamedir:\"-\", *server->mapname?server->mapname:\"-\", server->needpass?1:0);\n\t\telse if (*(fp = Info_ValueForKey(server->rules, \"*fp\")))\n\t\t\tVFS_PRINTF(f, \"%s \\\\protocol\\\\%s\\\\maxclients\\\\%u\\\\clients\\\\%u\\\\bots\\\\%u\\\\hostname\\\\%s\\\\modname\\\\%s\\\\mapname\\\\%s\\\\needpass\\\\%i\\\\*fp\\\\%s\\n\", NET_AdrToString(tmpbuf, sizeof(tmpbuf), &server->adr), prot, server->maxclients, server->clients, server->bots, *server->hostname?server->hostname:\"unnamed\", *server->gamedir?server->gamedir:\"-\", *server->mapname?server->mapname:\"-\", server->needpass?1:0, fp);\n\t\telse\n\t\t\tVFS_PRINTF(f, \"%s\\n\", NET_AdrToString(tmpbuf, sizeof(tmpbuf), &server->adr));\n\t}\n\n\t*mimetype = \"text/plain\";\n\treturn f;\n}\n\nvfsfile_t *SVM_GenerateIndex(const char *requesthost, const char *fname, const char **mimetype, const char *query)\n{\n\tvfsfile_t *f = NULL;\n\tif (!master_css)\n\t\tSVM_Init();\n\tif (!strcmp(fname, \"index.html\"))\n\t\tf = SVM_Generate_Gamelist(mimetype, query);\n\telse if (!strncmp(fname, \"server/\", 7))\n\t\tf = SVM_Generate_AddrServerinfo(mimetype, requesthost, fname+7, query);\n\telse if (!strncmp(fname, \"room/\", 5))\n\t\tf = SVM_Generate_RoomServerinfo(mimetype, requesthost, fname+5, query);\n\telse if (!strncmp(fname, \"game/\", 5))\n\t\tf = SVM_Generate_Serverlist(mimetype, requesthost, fname+5, query);\n\telse if (!strncmp(fname, \"raw/\", 4))\n\t\tf = SVM_Generate_Rawlist(mimetype, requesthost, fname+4, query);\n\treturn f;\n}\n\nstatic svm_game_t *SVM_GameFromBrokerID(const char **brokerid, qboolean create)\n{\t//broker id is /GAMENAME/SERVERNAME\n\tsize_t l;\n\tchar name[128];\n\tconst char *in = *brokerid;\n\tif (*in == '/')\n\t\tin++;\n\t*brokerid = in;\n\tfor (l = 0; *in && *in != '/' && *in != '?' && *in != '#'; in++)\n\t\tif (l < countof(name)-1)\n\t\t\tname[l++] = *in;\n\tname[l] = 0;\n\tif (*in == '/')\n\t\t*brokerid = ++in;\n\telse\n\t\tQ_strncpyz(name, \"unspecified\", sizeof(name));\n\treturn SVM_FindGame(name, create);\n}\nstatic svm_server_t *SVM_FindBrokerHost(const char *brokerid)\n{\n\tsvm_server_t *server;\n\tunsigned int key = SVM_GenerateBrokerKey(brokerid);\n\n\tserver = Hash_GetKey(&svm.serverhash, key);\n\twhile (server)\n\t{\n\t\tif (server->brokerid)\t//don't report brokered servers by address.\n\t\t\tif (!strcmp(server->brokerid, brokerid))\n\t\t\t\treturn server;\n\t\tserver = Hash_GetNextKey(&svm.serverhash, key, server);\n\t}\n\treturn NULL;\n}\nvoid SVM_RemoveBrokerGame(const char *brokerid)\n{\n\tsvm_server_t *s, **link;\n\tsvm_game_t *game = SVM_GameFromBrokerID(&brokerid, false);\n\tif (!game)\n\t{\n\t\tCon_Printf(\"SVM_RemoveBrokerGame: failed to find game for brokered server: %s\\n\", brokerid);\n\t\treturn;\n\t}\n\n\tfor (link = &game->firstserver; (s=*link); )\n\t{\n\t\tif (s->brokerid == brokerid)\n\t\t{\n\t\t\t*link = s->next;\n\t\t\tHash_RemoveDataKey(&svm.serverhash, SVM_GenerateBrokerKey(brokerid), s);\n\t\t\tZ_Free(s);\n\t\t\tgame->numservers--;\n\t\t\tsvm.numservers--;\n\t\t\treturn;\n\t\t}\n\t\telse\n\t\t\tlink = &s->next;\n\t}\n\n\tCon_Printf(\"SVM_RemoveBrokerGame: failed to remove brokered server: %s\\n\", brokerid);\n}\nvoid SVM_AddBrokerGame(const char *brokerid, const char *info)\n{\n\tsvm_game_t *game = SVM_GameFromBrokerID(&brokerid, true);\n\tsvm_server_t *server = SVM_FindBrokerHost(brokerid);\n\tchar *s;\n\tif (!server)\n\t{\n\t\tif (!game)\n\t\t\treturn;\n\t\tif (svm.numservers >= sv_maxservers.ival)\n\t\t{\n\t\t\tCon_DPrintf(\"server limit exceeded\\n\");\n\t\t\treturn;\n\t\t}\n\t\tCon_DPrintf(\"heartbeat(new - %s): /%s\\n\", game->name, brokerid);\n\n\t\tserver = Z_Malloc(sizeof(svm_server_t));\n\t\tserver->game = game;\n\t\tserver->brokerid = brokerid;\n\n\t\tserver->next = game->firstserver;\n\t\tgame->firstserver = server;\n\t\tgame->numservers++;\n\t\tsvm.numservers++;\n\n\t\tsvm.total.adds++;\n\n\t\tHash_AddKey(&svm.serverhash, SVM_GenerateBrokerKey(brokerid), server, &server->bucket);\n\t}\n\telse\n\t\tCon_DPrintf(\"heartbeat(update - %s): /%s\\n\", game->name, brokerid);\n\n\ts = Info_ValueForKey(info, \"sv_maxclients\");\n\tif (!*s)\n\t\ts = Info_ValueForKey(info, \"maxclients\");\n\tserver->maxclients = atoi(s);\n\tserver->clients = atoi(Info_ValueForKey(info, \"clients\"));\n\tserver->secure = !!*Info_ValueForKey(info, \"*fp\");\n\tserver->needpass = atoi(Info_ValueForKey(info, \"needpass\"));\n\tserver->stype = atoi(Info_ValueForKey(info, \"coop\"))?STYPE_COOP:0;\n\tif (!server->stype)\n\t{\t//deathmatch 0 also means coop 1... servers that report neither are probably annoying DP servers that report nothing useful and should default to DM.\n\t\tconst char *v = Info_ValueForKey(info, \"deathmatch\");\n\t\tserver->stype = *v && !atoi(v);\n\t}\n\tserver->protover = strtol(Info_ValueForKey(info, \"protocol\"), &s, 0);\n\tfor (; *s; s++)\n\t{\n\t\tif (*s == 't')\t\t//turn\n\t\t\tserver->stype |= STYPE_TURN;\n\t\telse if (*s == 'f')\t//usable for qwfwd\n\t\t\tserver->stype |= STYPE_QWFWD;\n\t}\n\tif (strtol(Info_ValueForKey(info, \"nomouse\"), NULL, 0))\n\t\tserver->stype |= STYPE_NOMOUSE;\n\tQ_strncpyz(server->hostname, Info_ValueForKey(info, \"hostname\"), sizeof(server->hostname));\n\tQ_strncpyz(server->gamedir, Info_ValueForKey(info, \"modname\"), sizeof(server->gamedir));\n\tQ_strncpyz(server->mapname, Info_ValueForKey(info, \"mapname\"), sizeof(server->mapname));\n\tQ_strncpyz(server->version, Info_ValueForKey(info, \"version\"), sizeof(server->version));\n\tif (!*server->version)\n\t\tQ_strncpyz(server->version, Info_ValueForKey(info, \"*version\"), sizeof(server->version));\n\tif (!*server->version)\n\t\tQ_strncpyz(server->version, Info_ValueForKey(info, \"ver\"), sizeof(server->version));\n\n\tQ_strncpyz(server->rules, info, sizeof(server->rules));\n}\nvoid SVM_SelectRelay(netadr_t *benefitiary, const char *brokerid, char *out, size_t outsize)\n{\n\tchar username[128];\n\tchar pass[128];\n\tchar key[128];\n\tchar adrbuf[64];\n\tqbyte dig[DIGEST_MAXSIZE];\n\tsize_t keysize;\n\tsvm_server_t *server;\n\tsvm_game_t *game = SVM_GameFromBrokerID(&brokerid, false);\n\tint count;\n\tif (!game)\n\t\treturn;\t//nope.\n\n\tfor (count = 0, server = game->firstserver; server; server = server->next)\n\t{\n\t\tif (server->needpass)\n\t\t\tcontinue;\t//nope, not interested.\n\t\tif (server->stype & STYPE_TURN)\t//acting as a turn relay...\n\t\t\tcount++;\n\t}\n\tif (!count)\n\t\treturn; //none for you...\n\n\tcount = rand()%count;\t//pick one at random... FIXME: fix closest.\n\n\tfor (server = game->firstserver; server; server = server->next)\n\t{\n\t\tif (server->needpass)\n\t\t\tcontinue;\t//nope, not interested.\n\t\tif (server->stype & STYPE_TURN)\t//acting as a turn relay...\n\t\t{\n\t\t\tif (count-->0)\n\t\t\t\tcontinue;\t//we didn't pick this one, keep going.\n\n\t\t\t//we need a username. this includes a timestamp to ensure it can expire.\n\t\t\tQ_snprintfz(username,sizeof(username), \"%\"PRIi64\":%s\", (quint64_t)time(NULL), brokerid);\n\t\t\t//we need a password too... its based upon our username and a secret key also known only to the relay.\n\t\t\tkeysize = Base64_DecodeBlock(Info_ValueForKey(server->rules, \"_turnkey\"),NULL, key,sizeof(key));\n\t\t\tkeysize = CalcHMAC(&hash_sha1, dig,sizeof(dig), username,strlen(username), key,keysize);\n\t\t\tpass[Base64_EncodeBlock(dig,keysize, pass,sizeof(pass)-1)] = 0;\n\t\t\t//and spit out the url (with our ?user= and ?auth= bits added.\n\t\t\tQ_snprintfz(out,outsize, \"turn:%s?user=%s?auth=%s\", NET_AdrToString(adrbuf,sizeof(adrbuf), &server->adr), username, pass);\n\t\t\treturn;\n\t\t}\n\t}\n}\n\nstatic svm_server_t *SVM_Heartbeat(const char *gamename, netadr_t *adr, int numclients, int numbots, int numspecs, double validuntil)\n{\n\tsvm_server_t *server = SVM_GetServer(adr);\n\tsvm_game_t *game;\n\n\tif (!gamename)\n\t{\t//no gamename is a placeholder server, to say that there's a server there but it isn't responding to our getinfos... (ie: to list misconfigured servers too)\n\t\tif (server)\n\t\t{\t//it still exists, renew it, but don't otherwise care too much.\n\t\t\tserver->expiretime = max(validuntil, server->expiretime);\n\t\t\treturn server;\n\t\t}\n\t\tgame = SVM_FindGame(\"UNKNOWN\", true);\n\t}\n\telse\n\t{\n\t\tgame = SVM_FindGame(gamename, true);\n\t\tif (!game)\n\t\t\treturn NULL;\n\t}\n\n\tif (server && server->game != game)\n\t{\n\t\tserver->expiretime = realtime - 1;\n\t\tserver = NULL;\n\t}\n\n\tif (!server)\t//not found\n\t{\n\t\tif (svm.numservers >= sv_maxservers.ival)\n\t\t{\n\t\t\tCon_DPrintf(\"server limit exceeded\\n\");\n\t\t\treturn NULL;\n\t\t}\n\t\tif (developer.ival)\n\t\t{\n\t\t\tchar buf[256];\n\t\t\tCon_Printf(\"heartbeat(new - %s): %s\\n\", game->name, NET_AdrToString(buf, sizeof(buf), adr));\n\t\t}\n\n\t\tserver = Z_Malloc(sizeof(svm_server_t));\n\t\tserver->game = game;\n\t\tserver->next = game->firstserver;\n\t\tgame->firstserver = server;\n\t\tgame->numservers++;\n\t\tsvm.numservers++;\n\t\tserver->expiretime = validuntil;\n\n\t\tserver->adr = *adr;\n\n\t\tsvm.total.adds++;\n\n\t\tHash_AddKey(&svm.serverhash, SVM_GenerateAddressKey(adr), server, &server->bucket);\n\t}\n\telse\n\t{\n\t\tif (developer.ival)\n\t\t{\n\t\t\tchar buf[256];\n\t\t\tCon_Printf(\"heartbeat(refresh): %s\\n\", NET_AdrToString(buf, sizeof(buf), &server->adr));\n\t\t}\n\t\tserver->expiretime = max(server->expiretime, validuntil);\n\t}\n\n\tserver->clients = numclients;\n\tserver->bots = numbots;\n\tserver->spectators = numspecs;\n\treturn server;\n}\n\nvoid SVM_GenChallenge(char *out, size_t outsize, netadr_t *foradr)\n{\t//this function needs to return some sort of unguessable string so that you can't spoof the server with fake responses\n\tchar adr[64];\n\tstatic char randumb[16];\n\tchar digest[256];\n\tvoid *ctx = alloca(hash_sha1.contextsize);\n\n\twhile (!*randumb)\n\t{\n\t\tif (!Sys_RandomBytes(randumb, sizeof(randumb)))\n\t\t{\n\t\t\tint i;\n\t\t\tsrand(time(NULL));\t//lame\n\t\t\tfor (i = 0; i < sizeof(randumb)-1; i++)\n\t\t\t\twhile (!randumb[i])\n\t\t\t\t\trandumb[i] = rand();\n\t\t}\n\t}\n\tNET_AdrToString(adr, sizeof(adr), foradr);\n\n\thash_sha1.init(ctx);\n\thash_sha1.process(ctx, randumb, sizeof(randumb)-1);\n\thash_sha1.process(ctx, adr, strlen(adr));\n\thash_sha1.terminate(digest, ctx);\n\n\tBase64_EncodeBlock(digest, hash_sha1.digestsize, out, outsize);\n}\n\n//switch net_from's reported connection, so we reply from a different udp socket from the one a packet was received from.\nstatic qboolean SVM_SwitchQuerySocket(void)\n{\n\tsize_t c;\n\t//switch the info query to our other udp socket, so any firewall/nat over the server blocks it.\n\t//this is to prevent people from thinking that the server is actually accessible.\n\tfor (c = 0; c < countof(svm_sockets->conn); c++)\n\t{\n\t\tif (!svm_sockets->conn[c])\n\t\t\tcontinue;\t//that one's dead, jim\n\t\tif (c+1 == net_from.connum)\n\t\t\tcontinue;\t//ignore this one, its the one we received the packet from\n\t\t//make sure its a datagram connection, and not some tcp weirdness.\n\t\tif (svm_sockets->conn[c]->prot == NP_DGRAM &&\n\t\t\t(svm_sockets->conn[c]->addrtype[0] == net_from.type || svm_sockets->conn[c]->addrtype[1] == net_from.type))\n\t\t{\t//okay, looks like we should be able to respond on this one. lets see if their firewall stops us from finding out more about them.\n\t\t\tnet_from.connum = c+1;\n\t\t\treturn true;\n\t\t}\n\t}\n\treturn false;\n}\n\nstatic void SVM_DiscoveredServer(netadr_t *a, const char *query)\n{\n\tsize_t idx, j;\n\t//add it, despite having no actual info. don't give it a valid protocol, so dead ones don't get reported with infinite feedback.\n\tsvm_server_t *srv = SVM_Heartbeat(NULL, a, 0,0,0, svm.time + sv_slaverequery.value);\n\n\tif (strcmp(srv->game->name, \"UNKNOWN\") && srv->expiretime > svm.time + sv_slaverequery.value)\n\t\treturn;\t//we already know something about it, don't spam pings...\n\n\tif (!*srv->hostname && !strcmp(srv->game->name, \"UNKNOWN\"))\n\t{\n\t\tfor (idx = 0; idx < countof(sv_masterslave); idx++)\n\t\t{\n\t\t\tfor (j = 0; j < sv_masterslave[idx].numaddr; j++)\n\t\t\t{\n\t\t\t\tif (NET_CompareAdr(&net_from, &sv_masterslave[idx].addr[j]))\n\t\t\t\t{\n\t\t\t\t\tQ_snprintfz(srv->hostname, sizeof(srv->hostname), \"[via %s]\", sv_masterslave[idx].var.string);\n\t\t\t\t\tidx = countof(sv_masterslave);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tif (pingring_count == pingring_max)\n\t{\t//just too many\n\t\tZ_ReallocElements((void**)&pingring, &pingring_max, pingring_max*2+1, sizeof(*pingring));\n\t}\n\telse if (pingring_first + pingring_count == pingring_max)\n\t{\t//we're at the end.\n\t\tmemmove(pingring, pingring+pingring_first, sizeof(*pingring)*pingring_count);\n\t\tpingring_first=0;\n\t}\n\tidx = pingring_first+pingring_count++;\n\tpingring[idx].a = *a;\n\tpingring[idx].query = strdup(query);\n}\n\nstatic void SVM_ProcessUDPPacket(void)\n{\n\tchar *s, *line;\n\tint firstbit;\n\n\t//we shouldn't be taking anything else...\n\tif (net_from.prot != NP_DGRAM)\n\t{\n\t\tCon_DPrintf(\"master: ignoring non-datagram message\\n\");\n\t\treturn;\n\t}\n\n\tnet_message.data[net_message.cursize] = '\\0';\t//null term all strings.\n\n\t//well that's annoying. why is our networking code not doing this? LAME!\n\tif (net_from.type == NA_IPV6 &&\n\t\t!*(int*)&net_from.address.ip6[0] &&\n\t\t!*(int*)&net_from.address.ip6[4] &&\n\t\t!*(short*)&net_from.address.ip6[8] &&\n\t\t*(short*)&net_from.address.ip6[10]==(short)0xffff)\n\t{\t//convert this ipv4-mapped-ipv6 address back to actual ipv4, so we don't get confused about stuff.\n\t\tnet_from.type = NA_IP;\n\t\t*(int*)&net_from.address.ip[0] = *(int*)&net_from.address.ip6[12];\n\t\t//and null it out, just in case.\n\t\t*(int*)&net_from.address.ip6[8]=0;\n\t\t*(int*)&net_from.address.ip6[12]=0;\n\t}\n\n#ifdef HAVE_DTLS\n\tif (*(int *)net_message.data != -1)\n\t\tif (NET_DTLS_Decode(svm_sockets))\n\t\t\tif (!net_message.cursize)\n\t\t\t\treturn;\n#endif\n\n\tif (NET_WasSpecialPacket(svm_sockets))\n\t{\n\t\tsvm.total.stun++;\n//\t\tCon_DPrintf(\"master: ignoring special packet\\n\");\n\t\treturn;\n\t}\n\n\tsvm.time = Sys_DoubleTime();\n\n\tMSG_BeginReading(&net_message, msg_nullnetprim);\n\tif (MSG_ReadLong() != -1 || msg_badread)\n\t{\t//go back to start...\n\t\tMSG_BeginReading(&net_message, msg_nullnetprim);\n\t}\n\tfirstbit = net_message.currentbit;\n\tline = MSG_ReadStringLine();\n\ts = COM_Parse(line);\n\tif (!strcmp(com_token, \"getservers\") || !strcmp(com_token, \"getserversExt\"))\n\t{\t//q3/dpmaster\n\t\tsizebuf_t sb;\n\t\tint ver;\n\t\tchar *eos;\n\t\tchar game[64];\n\t\tqboolean ext = !strcmp(com_token, \"getserversExt\");\n\t\tconst char *resp=ext?\"getserversExtResponse\":\"getserversResponse\";\n\t\tqboolean empty = false;\n\t\tqboolean full = false;\n\t\tqboolean ipv4 = !ext;\n\t\tqboolean ipv6 = false;\n\t\tint gametype = -1;\n\t\ts = COM_ParseOut(s, game, sizeof(game));\n\t\tver = strtol(game, &eos, 0);\n\t\tif (*eos)\n\t\t{\t//not a number, must have been a dpmaster game name.\n\t\t\ts = COM_Parse(s);\n\t\t\tver = strtol(com_token, NULL, 0);\n\t\t}\n\t\telse\t//first arg was a number. that means its vanilla quake3.\n\t\t\tQ_strncpyz(game, QUAKE3PROTOCOLNAME, sizeof(game));\n\t\tfor(;s&&*s;)\n\t\t{\n\t\t\ts = COM_Parse(s);\n\t\t\tif (!strcmp(com_token, \"empty\"))\n\t\t\t\tempty = true;\n\t\t\telse if (!strcmp(com_token, \"full\"))\n\t\t\t\tfull = true;\n\n\t\t\telse if (!strcmp(com_token, \"ipv4\"))\n\t\t\t\tipv4 = true;\n\t\t\telse if (!strcmp(com_token, \"ipv6\"))\n\t\t\t\tipv6 = true;\n\n\t\t\telse if (!strcmp(com_token, \"ffa\"))\n\t\t\t\tgametype = GT_FFA;\n\t\t\telse if (!strcmp(com_token, \"tourney\"))\n\t\t\t\tgametype = GT_TOURNEY;\n\t\t\telse if (!strcmp(com_token, \"team\"))\n\t\t\t\tgametype = GT_TEAM;\n\t\t\telse if (!strcmp(com_token, \"ctf\"))\n\t\t\t\tgametype = GT_CTF;\n\t\t\telse if (!strncmp(com_token, \"gametype=\", 9))\n\t\t\t\tgametype = atoi(com_token+9);\n\t\t\telse\n\t\t\t{\n\t\t\t\tchar buf[256];\n\t\t\t\tCon_DPrintf(\"Unknown request filter: %s\\n\", COM_QuotedString(com_token, buf, sizeof(buf), false));\n\t\t\t}\n\t\t}\n\t\tif (!ipv4 && !ipv6)\n\t\t\tipv4 = ipv6 = true; //neither specified? use both\n\n\n\t\tsvm.total.queries++;\n\t\tmemset(&sb, 0, sizeof(sb));\n\t\tsb.maxsize = sizeof(net_message_buffer)-2;\n\t\tsb.data = net_message_buffer;\n\t\tMSG_WriteLong(&sb, -1);\n\t\tSZ_Write(&sb, resp, strlen(resp));\t//WriteString, but without the null.\n\t\tSVM_AddIPAddresses(&sb, 0, ver, game, ipv4, ipv6, empty, full, true, gametype);\n\t\tsb.maxsize+=2;\n\t\tMSG_WriteByte(&sb, '\\\\');\t//otherwise the last may be considered invalid and ignored.\n\t\tMSG_WriteByte(&sb, 'E');\n\t\tMSG_WriteByte(&sb, 'O');\n\t\tMSG_WriteByte(&sb, 'T');\n\t\tNET_SendPacket(svm_sockets, sb.cursize, sb.data, &net_from);\n\t}\n\telse if (!strcmp(com_token, \"heartbeat\"))\n\t{\t//quake2 heartbeat. Serverinfo and players should follow.\n\t\tif (*s == '\\n' && s[1] == '\\\\')\n\t\t{\t//there's some serverinfo there, must be q2...\n\t\t\tsvm.total.heartbeats++;\n\t\t\tSVM_Heartbeat(QUAKE2PROTOCOLNAME, &net_from, 0,0,0, svm.time + sv_heartbeattimeout.ival);\n\t\t}\n\t\telse\n\t\t{\t//dp/q3/etc are annoying, but we can query from an emphemerial socket to check NAT rules.\n\t\t\tsizebuf_t sb;\n\t\t\tnetadr_t a;\n\t\t\tchar cookie[64];\n\t\t\tchar tmp[64];\n\n\t\t\tchar ourchallenge[256];\n\t\t\tSVM_GenChallenge(ourchallenge, sizeof(ourchallenge), &net_from);\n\t\t\tsvm.total.queries++;\n\n\t\t\t//placeholder listing...\n\t\t\tif (SVM_Heartbeat(NULL, &net_from, 0,0,0, svm.time + sv_heartbeattimeout.ival))\n\t\t\t\ta = net_from;\n\t\t\telse\n\t\t\t\ta.type = NA_INVALID;\n\t\t\tif (!SVM_SwitchQuerySocket())\t//changes net_from to use a different master-side port so their firewall sees us as someone else\n\t\t\t\ta.type = NA_INVALID;\n\n\t\t\t*cookie = 0;\n\t\t\ts = COM_Parse(s);\n\t\t\tif (!strcmp(com_token, \"FTEMaster\"))\n\t\t\t{\n\t\t\t\twhile ((s = COM_Parse(s)))\n\t\t\t\t{\n\t\t\t\t\tif (!strncmp(com_token, \"c=\", 2))\n\t\t\t\t\t\tQ_snprintfz(cookie, sizeof(cookie), \" %s a=%s\", com_token, NET_AdrToString(tmp,sizeof(tmp), &net_from));\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t//send a packet from our alternative port, to see if their firewall/NAT is open\n\t\t\tmemset(&sb, 0, sizeof(sb));\n\t\t\tsb.maxsize = sizeof(net_message_buffer);\n\t\t\tsb.data = net_message_buffer;\n\t\t\tMSG_WriteLong(&sb, -1);\n\t\t\tif (*cookie)\n\t\t\t\tMSG_WriteString(&sb, va(\"getinfo %s %s\\n\", ourchallenge,cookie));\n\t\t\telse\n\t\t\t\tMSG_WriteString(&sb, va(\"getinfo %s\\n\", ourchallenge));\n\t\t\tsb.cursize--;\n\t\t\tNET_SendPacket(svm_sockets, sb.cursize, sb.data, &net_from);\n\n\t\t\tif (a.type != NA_INVALID)\n\t\t\t{\t//they were unknown... send a formal response so we can get their hostname while leaving them as 'unknown'\n\t\t\t\tmemset(&sb, 0, sizeof(sb));\n\t\t\t\tsb.maxsize = sizeof(net_message_buffer);\n\t\t\t\tsb.data = net_message_buffer;\n\t\t\t\tMSG_WriteLong(&sb, -1);\n\t\t\t\tMSG_WriteString(&sb, va(\"getinfo ?%s\\n\", ourchallenge));\n\t\t\t\tsb.cursize--;\n\t\t\t\tNET_SendPacket(svm_sockets, sb.cursize, sb.data, &a);\n\t\t\t}\n\t\t}\n\t}\n\telse if (!strcmp(com_token, \"infoResponse\") || !strcmp(com_token, \"statusResponse\"))\n\t{\n\t\tchar ourchallenge[256];\n\t\tint clients, bots, specs;\n\t\tconst char *game, *chal;\n\t\tsvm_server_t *srv;\n\t\tqboolean unknownresp = false;\n\t\ts = MSG_ReadStringLine();\n\t\tsvm.total.heartbeats++;\n\t\tchal = Info_ValueForKey(s, \"challenge\");\n\t\tunknownresp = *chal=='?';\n\t\tchal += unknownresp?1:0;\n\t\tSVM_GenChallenge(ourchallenge, sizeof(ourchallenge), &net_from);\n\t\tif (!strcmp(chal, ourchallenge))\t//someone's trying to spoof it, to give it the wrong *fp or whatever.\n\t\t{\n\t\t\tbots = atoi(Info_ValueForKey(s, \"bots\"));\n\t\t\tclients = atoi(Info_ValueForKey(s, \"clients\"));\n\t\t\tclients = max(0, clients-bots);\n\t\t\tspecs = atoi(Info_ValueForKey(s, \"specs\"));\n\t\t\tgame = Info_ValueForKey(s, \"gamename\");\n\t\t\tif (!*game)\n\t\t\t\tgame = QUAKE3PROTOCOLNAME;\n\t\t\tif (unknownresp)\n\t\t\t\tgame = NULL;\t//ignore the gamename and classify it as unknown. this won't break anything if we've already has a proper heartbeat from them.\n\t\t\tsrv = SVM_Heartbeat(game, &net_from, clients,bots,specs, svm.time + sv_heartbeattimeout.ival);\n\t\t\tif (srv)\n\t\t\t{\n\t\t\t\tif (unknownresp)\n\t\t\t\t{\t//retain _ keys that won't be included in unchallenged responses.\n\t\t\t\t\tchar *turnkey = Info_ValueForKey(srv->rules, \"_turnkey\");\n\t\t\t\t\tQ_strncpyz(srv->rules, s, sizeof(srv->rules));\n\t\t\t\t\tInfo_SetValueForKey(srv->rules, \"_turnkey\", turnkey, sizeof(srv->rules));\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tQ_strncpyz(srv->rules, s, sizeof(srv->rules));\n\t\t\t\tInfo_RemoveKey(srv->rules, \"challenge\");\t//prevent poisoning\n\t\t\t\tif (developer.ival)\n\t\t\t\t{\n\t\t\t\t\tCon_Printf(\"Update from %s:\\n\", NET_AdrToString(ourchallenge,sizeof(ourchallenge), &net_from));\n\t\t\t\t\tInfo_Print(s, \"\\t\");\n\t\t\t\t}\n\t\t\t\tsrv->maxclients = atoi(Info_ValueForKey(s, \"sv_maxclients\"));\n\t\t\t\tsrv->secure = !!*Info_ValueForKey(s, \"*fp\");\n\t\t\t\tsrv->needpass = atoi(Info_ValueForKey(s, \"needpass\"));\n\t\t\t\tsrv->stype = atoi(Info_ValueForKey(s, \"coop\"))?STYPE_COOP:0;\n\t\t\t\tif (!srv->stype)\n\t\t\t\t{\t//deathmatch 0 also means coop 1... servers that report neither are probably annoying DP servers that report nothing useful and should default to DM.\n\t\t\t\t\tconst char *v = Info_ValueForKey(s, \"deathmatch\");\n\t\t\t\t\tif (*v && !atoi(v))\n\t\t\t\t\tsrv->stype |= STYPE_COOP;\n\t\t\t\t}\n\t\t\t\tQ_strncpyz(srv->hostname, Info_ValueForKey(s, \"hostname\"), sizeof(srv->hostname));\n\t\t\t\tQ_strncpyz(srv->gamedir, Info_ValueForKey(s, \"modname\"), sizeof(srv->gamedir));\n\t\t\t\tQ_strncpyz(srv->mapname, Info_ValueForKey(s, \"mapname\"), sizeof(srv->mapname));\n\t\t\t\tQ_strncpyz(srv->version, Info_ValueForKey(s, \"version\"), sizeof(srv->version));\n\t\t\t\tif (!*srv->version)\n\t\t\t\t\tQ_strncpyz(srv->version, Info_ValueForKey(s, \"*version\"), sizeof(srv->version));\n\t\t\t\tif (!*srv->version)\n\t\t\t\t\tQ_strncpyz(srv->version, Info_ValueForKey(s, \"ver\"), sizeof(srv->version));\n\t\t\t\tsrv->protover = strtol(Info_ValueForKey(s, \"protocol\"), &s, 0);\n\t\t\t\tfor (; *s; s++)\n\t\t\t\t{\n\t\t\t\t\tif (*s == 't')\n\t\t\t\t\t\tsrv->stype |= STYPE_QTV;\n\t\t\t\t\telse if (*s == 'f')\n\t\t\t\t\t\tsrv->stype |= STYPE_QWFWD;\n\t\t\t\t}\n\t\t\t\tif (*Info_ValueForKey(srv->rules, \"_turnkey\"))\n\t\t\t\t\tsrv->stype |= STYPE_TURN;\n\t\t\t\tif (atoi(Info_ValueForKey(srv->rules, \"nomouse\")))\n\t\t\t\t\tsrv->stype |= STYPE_NOMOUSE;\n\t\t\t}\n\t\t}\n\t}\n\telse if (!strcmp(com_token, \"query\"))\n\t{\t//quake2 server listing request\n\t\tsizebuf_t sb;\n\t\tsvm.total.queries++;\n\t\tmemset(&sb, 0, sizeof(sb));\n\t\tsb.maxsize = sizeof(net_message_buffer);\n\t\tsb.data = net_message_buffer;\n\t\tMSG_WriteLong(&sb, -1);\n\t\tMSG_WriteString(&sb, \"servers\\n\");\n\t\tsb.cursize--;\n\t\tSVM_AddIPAddresses(&sb, 0, 0, QUAKE2PROTOCOLNAME, true, false, true, true, false, -1);\n\t\tNET_SendPacket(svm_sockets, sb.cursize, sb.data, &net_from);\n\t}\n\telse if (*com_token == S2M_HEARTBEAT)\t//sequence, players\n\t{\t//quakeworld heartbeat\n\t\tint players;\n\t\tsizebuf_t sb;\n\t\tchar *nonce;\n\t\ts = MSG_ReadStringLine();\n\t\t//sequence = atoi(s);\n\t\ts = MSG_ReadStringLine();\n\t\tplayers = atoi(s);\n\t\tsvm.total.heartbeats++;\n\n\n\t\t//placeholder listing...\n\t\tSVM_Heartbeat(NULL, &net_from, players,0,0, svm.time + sv_heartbeattimeout.ival);\n\t\tnonce = MSG_ReadStringLine();\t//added a nonce, so the status can contain a private/shared key so the master can know how to generate acceptable passwords for turn proxies.\n\t\tSVM_SwitchQuerySocket();\n\n\t\t//send it a proper query. We'll fill in the other details on response.\n\t\tmemset(&sb, 0, sizeof(sb));\n\t\tsb.maxsize = sizeof(net_message_buffer);\n\t\tsb.data = net_message_buffer;\n\t\tMSG_WriteLong(&sb, -1);\n\t\tMSG_WriteString(&sb, va(\"status %i\\n\", 15));\n\t\tsb.cursize--;\n\t\tif (*nonce)\n\t\t\tMSG_WriteString(&sb, nonce);\n\t\tNET_SendPacket(svm_sockets, sb.cursize, sb.data, &net_from);\n\t}\n\telse if (*com_token == M2C_MASTER_REPLY && !com_token[1])\n\t{\t//response from a QW master request (lots of IPs from a 'slave' master that we're stealing)\n\t\tnetadr_t a = {NA_IP};\n\t\tsvm.total.heartbeats++;\n\t\tfor (;;)\n\t\t{\n\t\t\ta.address.ip[0] = MSG_ReadByte();\n\t\t\ta.address.ip[1] = MSG_ReadByte();\n\t\t\ta.address.ip[2] = MSG_ReadByte();\n\t\t\ta.address.ip[3] = MSG_ReadByte();\n\t\t\ta.port = MSG_ReadShort();\n\t\t\tif (msg_badread)\n\t\t\t\tbreak;\n\t\t\tSVM_DiscoveredServer(&a, \"\\xff\\xff\\xff\\xff\"\"status 15\\n\");\n\t\t}\n\t}\n\telse if (!strncmp(com_token, \"getserversExtResponse\", 21) && com_token[21] == '\\\\')\n\t{\t//response from a FTE-master request (lots of IPs from a 'slave' master that we're stealing)\n\t\tnetadr_t a = {NA_INVALID};\n\t\tnet_message.currentbit = firstbit+(21*8);\t//grr\n\t\tmsg_badread = false;\n\t\tsvm.total.heartbeats++;\n\t\tfor (;;)\n\t\t{\n\t\t\tqbyte lead = MSG_ReadByte();\n\t\t\tif (lead == '\\\\')\n\t\t\t{\n\t\t\t\ta.type = NA_IP;\n\t\t\t\tMSG_ReadData(a.address.ip, sizeof(a.address.ip));\n\t\t\t}\n\t\t\telse if (lead == '/')\n\t\t\t{\n\t\t\t\ta.type = NA_IPV6;\n\t\t\t\tMSG_ReadData(a.address.ip6, sizeof(a.address.ip6));\n\t\t\t}\n\t\t\telse\n\t\t\t\tbreak;\t//no idea\n\t\t\ta.port = MSG_ReadShort();\n\t\t\tif (msg_badread)\n\t\t\t\tbreak;\t//read too much junk\n\n\t\t\t{\n\t\t\t\tchar ourchallenge[256];\n\t\t\t\tSVM_GenChallenge(ourchallenge, sizeof(ourchallenge), &a);\n\t\t\t\tSVM_DiscoveredServer(&a, va(\"\\xff\\xff\\xff\\xffgetinfo %s\\n\", ourchallenge));\n\t\t\t}\n\t\t}\n\t}\n\telse if (*com_token == A2C_PRINT)\n\t{\t//quakeworld response from 'status' requests, providing for actual info (and so that we know its reachable from other addresses)\n\t\t//there's no challenge, these could easily be spoofed. :(\n\t\tint clients = 0, bots = 0, specs = 0;\n\t\tconst char *game;\n\t\tsvm_server_t *srv;\n\t\tconst char *t, *playerinfo = MSG_ReadString();\n\t\ts = ++line;\n\n\t\tt = Info_ValueForKey(s, \"clients\");\n\t\tif (*t)\n\t\t{\n\t\t\tbots = atoi(Info_ValueForKey(s, \"bots\"))-bots;\n\t\t\tclients = atoi(Info_ValueForKey(s, \"clients\"));\n\t\t\tspecs = atoi(Info_ValueForKey(s, \"specs\"));\n\t\t}\n\t\telse\n\t\t{\n\t\t\twhile (*playerinfo)\n\t\t\t{\n\t\t\t\t//USERID FRAGS TIME PING NAME SKIN TOP BOTTOM [TEAM]\n\t\t\t\tconst char *s = playerinfo;\n\t\t\t\tqboolean isspec;\n\t\t\t\tqboolean isbot;\n\t\t\t\tint ping;\n\n\t\t\t\ts = COM_Parse(s);//userid\n\t\t\t\ts = COM_Parse(s);//frags\n\t\t\t\tisspec = !strcmp(com_token, \"S\");\n\t\t\t\ts = COM_Parse(s);//time\n\t\t\t\ts = COM_Parse(s);//ping\n\t\t\t\tping = atoi(com_token);\n\t\t\t\ts = COM_Parse(s);//name\n\t\t\t\tisbot = (ping == 807 /*random hack*/) || !strncmp(com_token, \"BOT:\", 4);\n\t\t\t\t//s = COM_Parse(s);//skin\n\t\t\t\t//s = COM_Parse(s);//top\n\t\t\t\t//s = COM_Parse(s);//bottom\n\t\t\t\t//s = COM_Parse(s);//team\n\n\t\t\t\tif (isbot)\n\t\t\t\t\tbots++;\n\t\t\t\telse if (isspec)\n\t\t\t\t\tspecs++;\n\t\t\t\telse\n\t\t\t\t\tclients++;\n\n\t\t\t\twhile(*playerinfo)\n\t\t\t\t{\n\t\t\t\t\tif (*playerinfo++ == '\\n')\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tgame = Info_ValueForKey(s, \"gamename\");\n\t\tif (!*game)\n\t\t\tgame = QUAKEWORLDPROTOCOLNAME;\n\n\t\tsrv = SVM_Heartbeat(game, &net_from, clients,bots,specs, svm.time + sv_heartbeattimeout.ival);\n\t\tif (srv)\n\t\t{\n\t\t\tQ_strncpyz(srv->rules, s, sizeof(srv->rules));\n\t\t\tInfo_RemoveKey(srv->rules, \"*fp\");\t//we have no challenge and thus no way to protect against spoofing. don't allow it to report a dodgy fingerprint.\n\t\t\tif (developer.ival)\n\t\t\t\tInfo_Print(s, \"\\t\");\n\t\t\tsrv->protover = 3;//atoi(Info_ValueForKey(s, \"protocol\"));\n\t\t\tsrv->maxclients = atoi(Info_ValueForKey(s, \"maxclients\"));\n\t\t\tsrv->secure = !!*Info_ValueForKey(s, \"*fp\");\n\t\t\tsrv->needpass = atoi(Info_ValueForKey(s, \"needpass\"));\n\t\t\tsrv->stype = atoi(Info_ValueForKey(s, \"coop\"))?STYPE_COOP:0;\n\t\t\tif (!srv->stype)\n\t\t\t{\t//deathmatch 0 also means coop 1... servers that report neither are probably annoying proxies servers that report nothing useful and should default to DM.\n\t\t\t\tconst char *v = Info_ValueForKey(s, \"deathmatch\");\n\t\t\t\tif (*v && !atoi(v))\n\t\t\t\t\tsrv->stype |= STYPE_COOP;\n\t\t\t}\n\t\t\tif (atoi(Info_ValueForKey(s, \"nomouse\")))\n\t\t\t\tsrv->stype |= STYPE_NOMOUSE;\n\t\t\tQ_strncpyz(srv->hostname, Info_ValueForKey(s, \"hostname\"), sizeof(srv->hostname));\n\t\t\tQ_strncpyz(srv->gamedir, Info_ValueForKey(s, \"*gamedir\"), sizeof(srv->gamedir));\n\t\t\tQ_strncpyz(srv->mapname, Info_ValueForKey(s, \"map\"), sizeof(srv->mapname));\n\t\t\tQ_strncpyz(srv->version, Info_ValueForKey(s, \"version\"), sizeof(srv->version));\n\t\t\tif (!*srv->version)\n\t\t\t{\n\t\t\t\tgame = Info_ValueForKey(s, \"*version\");\n\t\t\t\tif (!strncmp(game, \"QTV\", 3))\n\t\t\t\t\tsrv->stype |= STYPE_QTV;\n\t\t\t\telse if (!strncmp(game, \"qwfwd\", 5))\n\t\t\t\t\tsrv->stype |= STYPE_QWFWD;\n\t\t\t\tQ_strncpyz(srv->version, game, sizeof(srv->version));\n\t\t\t}\n\t\t\tif (!*srv->version)\n\t\t\t\tQ_strncpyz(srv->version, Info_ValueForKey(s, \"ver\"), sizeof(srv->version));\n\t\t}\n\t}\n\telse if (*com_token == C2M_MASTER_REQUEST)\n\t{\t//quakeworld server listing request\n\t\tsizebuf_t sb;\n\t\tsvm.total.queries++;\n\t\tmemset(&sb, 0, sizeof(sb));\n\t\tsb.maxsize = sizeof(net_message_buffer);\n\t\tsb.data = net_message_buffer;\n\t\tMSG_WriteLong(&sb, -1);\n\t\tMSG_WriteByte(&sb, M2C_MASTER_REPLY);\n\t\tMSG_WriteByte(&sb, '\\n');\n\t\tSVM_AddIPAddresses(&sb, 0, 0, QUAKEWORLDPROTOCOLNAME, true, false, true, true, false, -1);\n\t\tNET_SendPacket(svm_sockets, sb.cursize, sb.data, &net_from);\n\t}\n\telse if (*com_token == A2A_PING)\n\t{\t//quakeworld server ping request... because we can.\n\t\tsizebuf_t sb;\n\t\tsvm.total.queries++;\n\t\tmemset(&sb, 0, sizeof(sb));\n\t\tsb.maxsize = sizeof(net_message_buffer);\n\t\tsb.data = net_message_buffer;\n\t\tMSG_WriteLong(&sb, -1);\n\t\tMSG_WriteByte(&sb, A2A_ACK);\n\t\tMSG_WriteByte(&sb, '\\n');\n\t\tNET_SendPacket(svm_sockets, sb.cursize, sb.data, &net_from);\n\t}\n\telse if (*com_token == S2M_SHUTDOWN)\n\t{\t//quakeworld server shutting down...\n\t\t//this isn't actually useful. we can't use it because we can't protect against spoofed denial-of-service attacks.\n\t\t//we could only use this by sending it a few pings to see if it is actually still responding. which is unreliable (especially if we're getting spammed by packet floods).\n\t}\n\telse if (!strcmp(com_token, \"ice_answer\"))\n\t{\t//one of our ws clients sent an ice offer over udp. this is the reply... hopefully.\n\t\tFTENET_TCP_ICEResponse(svm_sockets, ICEMSG_OFFER, s, MSG_ReadString());\n\t}\n\telse if (!strcmp(com_token, \"ice_scand\"))\n\t{\t//a send or ack...\n\t\tFTENET_TCP_ICEResponse(svm_sockets, ICEMSG_CANDIDATE, s, MSG_ReadString());\n\t}\n\telse\n\t\tsvm.total.junk++;\n}\n\nfloat SVM_RequerySlaves(void)\n{\n\tstatic int slaveseq = 0;\n\tstatic double nextslavetime;\n\n\tif (slaveseq == countof(sv_masterslave) || !nextslavetime)\n\t{\n\t\tif (nextslavetime < realtime && !pingring_count)\n\t\t{\n\t\t\tnextslavetime = realtime + sv_slaverequery.value;\n\t\t\tslaveseq = 0;\n\t\t\tpingring_first = 0;\t//no active entries.\n\t\t}\n\t}\n\n\twhile (slaveseq < countof(sv_masterslave))\n\t{\n\t\tstruct sv_masterslave_s *s = &sv_masterslave[slaveseq];\n\t\tslaveseq++;\n\n\t\tif (*s->var.string)\n\t\t{\n\t\t\tsize_t h;\n\t\t\tint defaultport[] = {PORT_QWMASTER, PORT_Q2MASTER, PORT_Q3MASTER, PORT_DPMASTER};\n\t\t\tconst char *querystring[] = {\n\t\t\t\t\t/*C2M_MASTER_REQUEST*/\"c\\n\",\t//quakeworld\n\t\t\t\t\tNULL,\t\t\t\t\t\t\t//quake2\n\t\t\t\t\t\"\\xff\\xff\\xff\\xffgetservers 68 empty full\\n\",\t//quake3\n\t\t\t\t\t\"\\xff\\xff\\xff\\xffgetserversExt %s %g empty full ipv4 ipv6\\n\"\t//fte/dp master\n\t\t\t\t\t};\n\t\t\tconst char *q;\n\n\t\t\ts->numaddr = NET_StringToAdr2(s->var.string, defaultport[s->type], s->addr, countof(s->addr), NULL);\n\t\t\tif (s->numaddr)\n\t\t\t{\t//send it to each...\n\t\t\t\tif (strstr(querystring[s->type], \"%s\"))\n\t\t\t\t{\n\t\t\t\t\tconst char *prots = com_protocolname.string;\n\t\t\t\t\twhile ((prots=COM_Parse(prots)))\n\t\t\t\t\t{\n\t\t\t\t\t\tq = va(querystring[s->type], com_token, com_protocolversion.value);\n\t\t\t\t\t\tfor (h = 0; h < s->numaddr; h++)\n\t\t\t\t\t\t\tNET_SendPacket(svm_sockets, strlen(q), q, &s->addr[h]);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tq = querystring[s->type];\n\t\t\t\t\tfor (h = 0; h < s->numaddr; h++)\n\t\t\t\t\t\tNET_SendPacket(svm_sockets, strlen(q), q, &s->addr[h]);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t\tCon_Printf(\"%s: unable to resolve %s\\n\", s->var.name, s->var.string);\n\t\t\treturn 1;\t//something happened. might just be a name lookup lockup. :(\n\t\t}\n\t}\n\n\tif (pingring_count)\n\t{\n\t\tnetadr_t *a = &pingring[pingring_first].a;\n\t\tchar *q = pingring[pingring_first].query;\n\t\tpingring[pingring_first].query = NULL;\n\t\tpingring_first++;\n\t\tpingring_count--;\n \t\tNET_SendPacket(svm_sockets, strlen(q), q, a);\n \t\tfree(q);\n\n\t\treturn sv_slaverequery.value / pingring_max;\n\t}\n\treturn 4;\t//nothing happening.\n}\n\n\nstatic void SVM_RegisterAlias(svm_game_t *game, char *aliasname)\n{\n\tconst char *a;\n\tsize_t l;\n\tsvm_game_t *aliasgame;\n\tif (!game)\n\t\treturn;\n\n\t//make sure we never have dupes. they confuse EVERYTHING.\n\taliasgame = SVM_FindGame(aliasname, false);\n\tif (aliasgame == game)\n\t\treturn;\t//already in there somehow.\n\tif (aliasgame)\n\t{\n\t\tif (strcmp(aliasgame->name, aliasname))\n\t\t\tCon_DPrintf(\"game alias of %s(aka %s) is already registered to %s\\n\", aliasname, game->name, aliasgame->name);\n\t\telse\n\t\t\tCon_DPrintf(\"game alias of %s is already registered\\n\", aliasname);\n\t\treturn;\n\t}\n\tgame->persistent = true;\t//don't forget us!\n\n\tif (!*aliasname)\n\t\treturn;\n\n\ta = game->aliases;\n\tif (a) for (; *a; a+=strlen(a)+1);\n\tl = a-game->aliases;\n\tgame->aliases = BZ_Realloc(game->aliases, l+strlen(aliasname)+2);\n\tmemcpy(game->aliases+l, aliasname, strlen(aliasname)+1);\n\tl += strlen(aliasname)+1;\n\tgame->aliases[l] = 0;\n}\nstatic void SVM_GameAlias_f(void)\n{\n\tsvm_game_t *game = SVM_FindGame(Cmd_Argv(1), 2);\n\tif (!game)\n\t{\n\t\tCon_Printf(\"Unable to register game %s\\n\", Cmd_Argv(1));\n\t\treturn;\n\t}\n\tSVM_RegisterAlias(game, Cmd_Argv(2));\n}\nstatic void SVM_GameLevelshots_f(void)\n{\n\tsvm_game_t *game = SVM_FindGame(Cmd_Argv(1), 2);\n\tif (!game)\n\t{\n\t\tCon_Printf(\"Unable to register game %s\\n\", Cmd_Argv(1));\n\t\treturn;\n\t}\n\tZ_StrDupPtr(&game->levelshotsurl, Cmd_Argv(2));\n}\nstatic void SVM_Register(void)\n{\n\tsize_t u;\n\n\tsvm_sockets = FTENET_CreateCollection(true, SVM_ProcessUDPPacket);\n\tHash_InitTable(&svm.serverhash, 1024, Z_Malloc(Hash_BytesForBuckets(1024)));\n\n\tCmd_AddCommandD (\"gamealias\", SVM_GameAlias_f, \"Set up alternative protocol names that should be considered part of a single game\");\n\tCmd_AddCommandD (\"gamelevelshotsurl\", SVM_GameLevelshots_f, \"Registers a per-game base url to use to form a prefix for where browsers should get their map images from.\");\n\n\tCvar_Register(&sv_masterport, \"server control variables\");\n\tCvar_Register(&sv_masterport_tcp, \"server control variables\");\n\tCvar_Register(&sv_heartbeattimeout, \"server control variables\");\n\tCvar_Register(&sv_maxgames, \"server control variables\");\n\tCvar_Register(&sv_maxservers, \"server control variables\");\n\tCvar_Register(&sv_hideinactivegames, \"server control variables\");\n\tCvar_Register(&sv_sortlist, \"server control variables\");\n\tCvar_Register(&sv_hostname, \"server control variables\");\n\tCvar_Register(&sv_slaverequery, \"server control variables\");\n\tfor (u = 0; u < countof(sv_masterslave); u++)\n\t\tCvar_Register(&sv_masterslave[u].var, \"server control variables\");\n}\nstatic qboolean SVM_FoundManifest(void *usr, ftemanifest_t *man, enum modsourcetype_e sourcetype)\n{\n\tsvm_game_t *game;\n\tconst char *g;\n\tif (man->protocolname)\n\t{\t//FIXME: we ought to do this for each manifest we could find.\n\t\tg = man->protocolname;\n\n#if 1\n\t\tgame = SVM_FindGame(man->formalname, 3);\n#else\n\t\tg = COM_Parse(g);\n\t\tgame = SVM_FindGame(com_token, 2);\n#endif\n\t\tif (!game)\n\t\t\treturn false;\n\t\tif (man->schemes && !game->scheme)\n\t\t{\n\t\t\tCOM_Parse(man->schemes);\n\t\t\tgame->scheme = Z_StrDup(com_token);\n\t\t}\n\t\twhile (*g)\n\t\t{\n\t\t\tg = COM_Parse(g);\n\t\t\tSVM_RegisterAlias(game, com_token);\n\t\t}\n\t}\n\n\treturn false;\n}\nstatic void SVM_Begin(void)\n{\t//called once filesystem etc stuff is started.\n\tSVM_FoundManifest(NULL, fs_manifest, MST_UNKNOWN);\n\tFS_EnumerateKnownGames(SVM_FoundManifest, NULL, false);\n\n\tCvar_ForceCallback(&sv_masterport);\n\tCvar_ForceCallback(&sv_masterport_tcp);\n}\n\nfloat SVM_Think(void)\n{\n#ifndef MASTERONLY\n\tif (!svm_sockets)\n\t{\n\t\tSVM_Register();\n\t\tSVM_Begin();\n\t}\n#endif\n\n\tNET_ReadPackets (svm_sockets);\n\tSVM_RemoveOldServers();\n\treturn SVM_RequerySlaves();\n}\n#else\nfloat SVM_Think(void){return 4;}\n#endif\n\n\n#ifdef MASTERONLY\nstatic void SV_Quit_f (void)\n{\n\tCon_TPrintf (\"Shutting down.\\n\");\n\tSys_Quit ();\n}\nstatic void SVM_Status_f(void)\n{\n\tsvm_game_t *g;\n\tsvm_server_t *s;\n\tunsigned clients;\n\tstruct rates_s *s1, *s2;\n\tfloat period;\n\tsize_t r;\n\n\tNET_PrintAddresses(svm_sockets);\n\tNET_PrintConnectionsStatus(svm_sockets);\n\tCon_Printf(\"Game count: %u\\n\", (unsigned)svm.numgames);\n\tfor (g = svm.firstgame; g; g = g->next)\n\t{\n\t\tclients = 0;\n\t\tfor (s = g->firstserver; s; s = s->next)\n\t\t\tclients += s->clients;\n\t\tCon_Printf(\"Game %s: %u servers, %u clients\\n\", g->name, (unsigned)g->numservers, clients);\n\t}\n\n\ts1 = &svm.total;\n\tr = (svm.stampring >= countof(svm.stamps)-1)?svm.stampring-countof(svm.stamps)-1:0;\n\ts2 = &svm.stamps[r%countof(svm.stamps)];\n\n\tperiod = s1->timestamp-s2->timestamp;\n\tperiod/=60;\n\tif (!period)\n\t\tperiod=1;\n\tCon_Printf(\"Heartbeats/min: %f\\n\", (s1->heartbeats-s2->heartbeats)/period);\n\tCon_Printf(\"Queries/min: %f\\n\", (s1->queries-s2->queries)/period);\n\tif (s1->stun!=s2->stun)\n\t\tCon_Printf(\"Stun/min: %f\\n\", (s1->stun-s2->stun)/period);\n\tif (s1->junk!=s2->junk)\n\t\tCon_Printf(\"Junk/min: %f\\n\", (s1->junk-s2->junk)/period);\n\n}\n\nvoid SV_Init (struct quakeparms_s *parms)\n{\n\tint manarg;\n\n\tCOM_InitArgv (parms->argc, parms->argv);\n\n\thost_parms = *parms;\n\n\tCvar_Init();\n\n\tMemory_Init();\n\n\tSys_Init();\n\n\tCOM_ParsePlusSets(false);\n\n\tCbuf_Init ();\n\tCmd_Init ();\n\n\tNET_Init ();\n\tCOM_Init ();\n\n\tCmd_AddCommand (\"quit\", SV_Quit_f);\n\tCmd_AddCommand (\"status\", SVM_Status_f);\n\n\tSVM_Register();\n\n\tCvar_ParseWatches();\n\thost_initialized = true;\n\n\tmanarg = COM_CheckParm(\"-manifest\");\n\tif (manarg && manarg < com_argc-1 && com_argv[manarg+1])\n\t\tFS_ChangeGame(FS_Manifest_ReadSystem(com_argv[manarg+1], NULL), true, true);\n\telse\n\t\tFS_ChangeGame(NULL, true, true);\n\n\tCmd_StuffCmds();\n\n\tSVM_Begin();\n\n\tCon_Printf (\"Exe: %s\\n\", version_string());\n\n\tCon_TPrintf (\"======== %s Initialized ========\\n\", \"FTEMaster\");\n}\nfloat SV_Frame (void)\n{\n\tfloat sleeptime;\n\tsvm.time = realtime = Sys_DoubleTime();\n\twhile (1)\n\t{\n\t\tconst char *cmd = Sys_ConsoleInput ();\n\t\tif (!cmd)\n\t\t\tbreak;\n\t\tLog_String(LOG_CONSOLE, cmd);\n\t\tCbuf_AddText (cmd, RESTRICT_LOCAL);\n\t\tCbuf_AddText (\"\\n\", RESTRICT_LOCAL);\n\t}\n\tCbuf_Execute ();\n\n\tsleeptime = SVM_Think();\n\n\t//record lots of info over multiple frames, for smoother stats info.\n\tsvm.total.timestamp = realtime;\n\tif (svm.nextstamp < realtime)\n\t{\n\t\tsvm.stamps[svm.stampring%countof(svm.stamps)] = svm.total;\n\t\tsvm.stampring++;\n\t\tsvm.nextstamp = realtime+60;\n\t}\n\n\treturn sleeptime;\n}\n#endif\n"
  },
  {
    "path": "engine/server/sv_move.c",
    "content": "/*\nCopyright (C) 1996-1997 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  \n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n*/\n// sv_move.c -- monster movement\n\n#include \"quakedef.h\"\n#include \"pr_common.h\"\n\n#if defined(CSQC_DAT) || !defined(CLIENTONLY)\n/*\n=============\nSV_CheckBottom\n\nReturns false if any part of the bottom of the entity is off an edge that\nis not a staircase.\n\n=============\n*/\n//int c_yes, c_no;\n\nhull_t *Q1BSP_ChooseHull(model_t *model, int hullnum, vec3_t mins, vec3_t maxs, vec3_t offset);\n\n//this function is axial. major axis determines ground. if it switches slightly, a new axis may become the ground...\nqboolean World_CheckBottom (world_t *world, wedict_t *ent, vec3_t up)\n{\n\tvec3_t\tmins, maxs, start, stop;\n\ttrace_t\ttrace;\n\tint\t\tx, y;\n\tfloat\tmid;\n\n\tint a0,a1,a2;\t//logical x, y, z\n\tint sign;\n\n\tmins[0] = fabs(up[0]);\n\tmins[1] = fabs(up[1]);\n\tmins[2] = fabs(up[2]);\n\tif (mins[2] > mins[0] && mins[2] > mins[1])\n\t{\n\t\ta0 = 0;\n\t\ta1 = 1;\n\t\ta2 = 2;\n\t}\n\telse\n\t{\n\t\ta2 = mins[1] > mins[0];\n\t\ta0 = 1 - a2;\n\t\ta1 = 2;\n\t}\n\tsign = (up[a2]>0)?1:-1;\n\t\n\tVectorAdd (ent->v->origin, ent->v->mins, mins);\n#ifdef Q1BSPS\n\tif (world->worldmodel->fromgame == fg_quake || world->worldmodel->fromgame == fg_halflife)\n\t{\n\t\t//quake's hulls are weird. sizes are defined as from mins to mins+hullsize. the actual maxs is ignored other than for its size.\n\t\thull_t *hull;\n\t\thull = Q1BSP_ChooseHull(world->worldmodel, ent->xv->hull, ent->v->mins, ent->v->maxs, start);\n\t\t//ignore the hull's offset. the minpoint is the minpoint. lets fix up the size though, just in case.\n\t\tVectorSubtract (mins, hull->clip_mins, maxs);\n\t\tVectorAdd (maxs, hull->clip_maxs, maxs);\n\t}\n\telse\n#endif\n\t\tVectorAdd (ent->v->origin, ent->v->maxs, maxs);\n\n// if all of the points under the corners are solid world, don't bother\n// with the tougher checks\n// the corners must be within 16 of the midpoint\n\tstart[a2] = (sign<0)?maxs[a2]:mins[a2] - sign;\n\tfor\t(x=0 ; x<=1 ; x++)\n\t\tfor\t(y=0 ; y<=1 ; y++)\n\t\t{\n\t\t\tstart[a0] = x ? maxs[a0] : mins[a0];\n\t\t\tstart[a1] = y ? maxs[a1] : mins[a1];\n\t\t\tif (!(World_PointContentsWorldOnly (world, start) & FTECONTENTS_SOLID))\n\t\t\t\tgoto realcheck;\n\t\t}\n\n//\tc_yes++;\n\treturn true;\t\t// we got out easy\n\nrealcheck:\n//\tc_no++;\n//\n// check it for real...\n//\n\tstart[a2] = (sign<0)?maxs[a2]:mins[a2];\n\t\n// the midpoint must be within 16 of the bottom\n\tstart[a0] = stop[a0] = (mins[a0] + maxs[a0])*0.5;\n\tstart[a1] = stop[a1] = (mins[a1] + maxs[a1])*0.5;\n\tstop[a2] = start[a2] - 2*movevars.stepheight*sign;\n\ttrace = World_Move (world, start, vec3_origin, vec3_origin, stop, true|MOVE_IGNOREHULL, ent);\n\n\tif (trace.fraction == 1.0)\n\t\treturn false;\n\n\tmid = trace.endpos[2];\n\n\tmid = (mid-start[a2]-(movevars.stepheight*sign)) / (stop[a2]-start[a2]);\n\t\n// the corners must be within 16 of the midpoint\t\n\tfor\t(x=0 ; x<=1 ; x++)\n\t\tfor\t(y=0 ; y<=1 ; y++)\n\t\t{\n\t\t\tstart[a0] = stop[a0] = x ? maxs[a0] : mins[a0];\n\t\t\tstart[a1] = stop[a1] = y ? maxs[a1] : mins[a1];\n\t\t\t\n\t\t\ttrace = World_Move (world, start, vec3_origin, vec3_origin, stop, true|MOVE_IGNOREHULL, ent);\n\t\n\t\t\tif (trace.fraction == 1.0 || trace.fraction > mid)//mid - trace.endpos[2] > movevars.stepheight)\n\t\t\t\treturn false;\n\t\t}\n\n//\tc_yes++;\n\treturn true;\n}\n\n/*\n=============\nSV_movestep\n\nCalled by monster program code.\nThe move will be adjusted for slopes and stairs, but if the move isn't\npossible, no move is done, false is returned, and\npr_global_struct->trace_normal is set to the normal of the blocking wall\n=============\n*/\nqboolean World_movestep (world_t *world, wedict_t *ent, vec3_t move, vec3_t axis[3], qboolean relink, qboolean noenemy, void (*set_move_trace)(pubprogfuncs_t *prinst, trace_t *trace))\n{\n\tfloat\t\tdz;\n\tvec3_t\t\toldorg, neworg, end;\n\ttrace_t\t\ttrace;\n\tint\t\t\ti;\n\twedict_t\t*enemy = world->edicts;\n\tint eflags = ent->v->flags;\n\tvec3_t\t\teaxis[3];\n\n\tif (!axis)\n\t{\n\t\t//fixme?\n\t\tWorld_GetEntGravityAxis(ent, eaxis);\n\t\taxis = eaxis;\n\t}\n\n// try the move\n\tVectorCopy (ent->v->origin, oldorg);\n\tVectorAdd (ent->v->origin, move, neworg);\n\n// flying monsters don't step up\n\tif ((eflags & (FL_SWIM | FL_FLY))\n#if defined(HEXEN2) && defined(HAVE_SERVER)\n\t\t\t//hexen2 has some extra logic for FLH2_HUNTFACE, but its buggy and thus never used.\n\t\t\t//it would be nice to redefine the NOZ flag to instead force noenemy here, but that's not hexen2-compatible and FLH2_NOZ is bound to conflict with some quake mod.\n\t\t\t&& (world != &sv.world || progstype != PROG_H2 || !(eflags & (FLH2_NOZ|FLH2_HUNTFACE)))\n#endif\n\t\t\t)\n\t{\n\t// try one move with vertical motion, then one without\n\t\tfor (i=0 ; i<2 ; i++)\n\t\t{\n\t\t\tVectorAdd (ent->v->origin, move, neworg);\n\t\t\tif (!noenemy)\n\t\t\t{\n\t\t\t\tenemy = (wedict_t*)PROG_TO_EDICT(world->progs, ent->v->enemy);\n\t\t\t\tif (i == 0 && enemy->entnum)\n\t\t\t\t{\n\t\t\t\t\tVectorSubtract(ent->v->origin, ((wedict_t*)PROG_TO_EDICT(world->progs, ent->v->enemy))->v->origin, end);\n\t\t\t\t\tdz = DotProduct(end, axis[2]);\n\t\t\t\t\tif (dz > 40)\n\t\t\t\t\t\tVectorMA(neworg, -8, axis[2], neworg);\n\t\t\t\t\tif (dz < 30)\n\t\t\t\t\t\tVectorMA(neworg, 8, axis[2], neworg);\n\t\t\t\t}\n\t\t\t}\n\t\t\ttrace = World_Move (world, ent->v->origin, ent->v->mins, ent->v->maxs, neworg, false, ent);\n\t\t\tif (set_move_trace)\n\t\t\t\tset_move_trace(world->progs, &trace);\n\n\t\t\tif (trace.fraction == 1)\n\t\t\t{\n\t\t\t\tif ( (eflags & FL_SWIM) && !(World_PointContentsWorldOnly(world, trace.endpos) & FTECONTENTS_FLUID))\n\t\t\t\t\tcontinue;\t// swim monster left water\n\t\n\t\t\t\tVectorCopy (trace.endpos, ent->v->origin);\n\t\t\t\tif (relink)\n\t\t\t\t\tWorld_LinkEdict (world, ent, true);\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\tif (noenemy || !enemy->entnum)\n\t\t\t\tbreak;\n\t\t}\n\n\t\treturn false;\n\t}\n\n// push down from a step height above the wished position\n\tVectorMA(neworg, movevars.stepheight, axis[2], neworg);\n\tVectorMA(neworg, movevars.stepheight*-2, axis[2], end);\n\n\ttrace = World_Move (world, neworg, ent->v->mins, ent->v->maxs, end, false, ent);\n\tif (set_move_trace)\n\t\tset_move_trace(world->progs, &trace);\n\n\tif (trace.allsolid)\n\t\treturn false;\n\n\tif (trace.startsolid)\n\t{\n\t\t//move up by an extra step, if needed\n\t\tVectorMA(neworg, -movevars.stepheight, axis[2], neworg);\n\t\ttrace = World_Move (world, neworg, ent->v->mins, ent->v->maxs, end, false, ent);\n\t\tif (set_move_trace)\n\t\t\tset_move_trace(world->progs, &trace);\n\t\tif (trace.allsolid || trace.startsolid)\n\t\t\treturn false;\n\t}\n\tif (trace.fraction == 1)\n\t{\n\t// if monster had the ground pulled out, go ahead and fall\n\t\tif ( (int)ent->v->flags & FL_PARTIALGROUND )\n\t\t{\n\t\t\tVectorAdd (ent->v->origin, move, ent->v->origin);\n\t\t\tif (relink)\n\t\t\t\tWorld_LinkEdict (world, ent, true);\n\t\t\tent->v->flags = (int)ent->v->flags & ~FL_ONGROUND;\n//\tCon_Printf (\"fall down\\n\"); \n\t\t\treturn true;\n\t\t}\n\t\n\t\treturn false;\t\t// walked off an edge\n\t}\n\n// check point traces down for dangling corners\n\tVectorCopy (trace.endpos, ent->v->origin);\n\t\n\tif (!World_CheckBottom (world, ent, axis[2]))\n\t{\n\t\tif ( (int)ent->v->flags & FL_PARTIALGROUND )\n\t\t{\t// entity had floor mostly pulled out from underneath it\n\t\t\t// and is trying to correct\n\t\t\tif (relink)\n\t\t\t\tWorld_LinkEdict (world, ent, true);\n\t\t\treturn true;\n\t\t}\n\t\tVectorCopy (oldorg, ent->v->origin);\n\t\treturn false;\n\t}\n\n\tif ( (int)ent->v->flags & FL_PARTIALGROUND )\n\t{\n//\t\tCon_Printf (\"back on ground\\n\"); \n\t\tent->v->flags = (int)ent->v->flags & ~FL_PARTIALGROUND;\n\t}\n\tent->v->groundentity = EDICT_TO_PROG(world->progs, trace.ent);\n\n// the move is ok\n\tif (relink)\n\t\tWorld_LinkEdict (world, ent, true);\n\treturn true;\n}\n\n\n//============================================================================\n\nqboolean World_GetEntGravityAxis(wedict_t *ent, vec3_t axis[3])\n{\n\tif (ent->xv->gravitydir[0] || ent->xv->gravitydir[1] || ent->xv->gravitydir[2])\n\t{\n\t\tvoid PerpendicularVector( vec3_t dst, const vec3_t src );\n\t\tVectorNegate(ent->xv->gravitydir, axis[2]);\n\t\tVectorNormalize(axis[2]);\n\t\tPerpendicularVector(axis[0], axis[2]);\n\t\tVectorNormalize(axis[0]);\n\t\tCrossProduct(axis[2], axis[0], axis[1]);\n\t\tVectorNormalize(axis[1]);\n\t\treturn true;\n\t}\n\telse\n\t{\n\t\tVectorSet(axis[0], 1, 0, 0);\n\t\tVectorSet(axis[1], 0, 1, 0);\n\t\tVectorSet(axis[2], 0, 0, 1);\n\t\treturn false;\n\t}\n}\n\n/*\n==============\nPF_changeyaw\n\nThis was a major timewaster in progs, so it was converted to C\n==============\n*/\nfloat World_changeyaw (wedict_t *ent)\n{\n\tfloat\t\tideal, current, move, speed;\n\tvec3_t surf[3];\n\tif (World_GetEntGravityAxis(ent, surf))\n\t{\n\t\t//complex matrix stuff\n\t\tfloat mat[16];\n\t\tfloat surfm[16], invsurfm[16];\n\t\tfloat viewm[16];\n\t\tvec3_t view[4];\n\t\tvec3_t vang;\n\n\t\t/*calc current view matrix relative to the surface*/\n\t\tAngleVectorsMesh(ent->v->angles, view[0], view[1], view[2]);\n\t\tVectorNegate(view[1], view[1]);\n\n\t\tWorld_GetEntGravityAxis(ent, surf);\n\n\t\tMatrix4x4_RM_FromVectors(surfm, surf[0], surf[1], surf[2], vec3_origin);\n\t\tMatrix3x4_InvertTo4x4_Simple(surfm, invsurfm);\n\n\t\t/*calc current view matrix relative to the surface*/\n\t\tMatrix4x4_RM_FromVectors(viewm, view[0], view[1], view[2], vec3_origin);\n\t\tMatrix4_Multiply(viewm, invsurfm, mat);\n\t\t/*convert that back to angles*/\n\t\tMatrix3x4_RM_ToVectors(mat, view[0], view[1], view[2], view[3]);\n\t\tVectorAngles(view[0], view[2], vang, true);\n\n\t\t/*edit it*/\n\n\t\tideal = ent->v->ideal_yaw;\n\t\tspeed = ent->v->yaw_speed;\n\t\tmove = ideal - anglemod(vang[YAW]);\n\t\tif (move > 180)\n\t\t\tmove -= 360;\n\t\telse if (move < -180)\n\t\t\tmove += 360;\n\t\tif (move > 0)\n\t\t{\n\t\t\tif (move > speed)\n\t\t\t\tmove = speed;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (move < -speed)\n\t\t\t\tmove = -speed;\n\t\t}\n\t\tvang[YAW] = anglemod(vang[YAW] + move);\n\n\t\t/*clamp pitch, kill roll. monsters don't pitch/roll.*/\n\t\tvang[PITCH] = 0;\n\t\tvang[ROLL] = 0;\n\n\t\tmove = ideal - vang[YAW];\n\n\t\t/*turn those angles back to a matrix*/\n\t\tAngleVectors(vang, view[0], view[1], view[2]);\n\t\tVectorNegate(view[1], view[1]);\n\t\tMatrix4x4_RM_FromVectors(mat, view[0], view[1], view[2], vec3_origin);\n\t\t/*rotate back into world space*/\n\t\tMatrix4_Multiply(mat, surfm, viewm);\n\t\t/*and figure out the final result*/\n\t\tMatrix3x4_RM_ToVectors(viewm, view[0], view[1], view[2], view[3]);\n\t\tVectorAngles(view[0], view[2], ent->v->angles, true);\n\n\t\t//make sure everything is sane\n\t\tent->v->angles[PITCH] = anglemod(ent->v->angles[PITCH]);\n\t\tent->v->angles[YAW] = anglemod(ent->v->angles[YAW]);\n\t\tent->v->angles[ROLL] = anglemod(ent->v->angles[ROLL]);\n\t\treturn move;\n\t}\n\n\n\t//FIXME: gravitydir. reorient the angles to change the yaw with respect to the current ground surface.\n\n\tcurrent = anglemod( ent->v->angles[1] );\n\tideal = ent->v->ideal_yaw;\n\tspeed = ent->v->yaw_speed;\n\n\tif (current == ideal)\n\t\treturn 0;\n\tmove = ideal - current;\n\tif (ideal > current)\n\t{\n\t\tif (move >= 180)\n\t\t\tmove = move - 360;\n\t}\n\telse\n\t{\n\t\tif (move <= -180)\n\t\t\tmove = move + 360;\n\t}\n\tif (move > 0)\n\t{\n\t\tif (move > speed)\n\t\t\tmove = speed;\n\t}\n\telse\n\t{\n\t\tif (move < -speed)\n\t\t\tmove = -speed;\n\t}\n\n\tent->v->angles[1] = anglemod (current + move);\n\n\treturn ideal - ent->v->angles[1];\n}\n\n/*\n======================\nSV_StepDirection\n\nTurns to the movement direction, and walks the current distance if\nfacing it.\n\n======================\n*/\nqboolean World_StepDirection (world_t *world, wedict_t *ent, float yaw, float dist, vec3_t axis[3])\n{\n\tvec3_t\t\tmove, oldorigin;\n\tfloat\t\tdelta, s;\n\t\n\tent->v->ideal_yaw = yaw;\n\n\tdelta = World_changeyaw(ent);\n\n\tyaw = yaw*M_PI*2 / 360;\n\n\ts = cos(yaw)*dist;\n\tVectorScale(axis[0], s, move);\n\ts = sin(yaw)*dist;\n\tVectorMA(move, s, axis[1], move);\n\n\t//FIXME: Hexen2: ent flags & FL_SET_TRACE\n\n\tVectorCopy (ent->v->origin, oldorigin);\n\tif (World_movestep (world, ent, move, axis, false, false, NULL))\n\t{\n\t\tdelta = anglemod(delta);\n\t\tif (delta > 45 && delta < 315)\n\t\t{\t// not turned far enough, so don't take the step\n\t\t\t//FIXME: surely this is noticably inefficient?\n\t\t\tVectorCopy (oldorigin, ent->v->origin);\n\t\t}\n\t\tWorld_LinkEdict (world, ent, true);\n\t\treturn true;\n\t}\n\tWorld_LinkEdict (world, ent, true);\n\t\t\n\treturn false;\n}\n\n/*\n======================\nSV_FixCheckBottom\n\n======================\n*/\nvoid World_FixCheckBottom (wedict_t *ent)\n{\n//\tCon_Printf (\"SV_FixCheckBottom\\n\");\n\t\n\tent->v->flags = (int)ent->v->flags | FL_PARTIALGROUND;\n}\n\n\n\n/*\n================\nSV_NewChaseDir\n\n================\n*/\n#define\tDI_NODIR\t-1\n\nvoid World_NewChaseDir (world_t *world, wedict_t *actor, wedict_t *enemy, float dist, vec3_t axis[3])\n{\n\tfloat\t\tdeltax,deltay;\n\tfloat\t\t\td[3];\n\tfloat\t\ttdir, olddir, turnaround;\n\n\tolddir = anglemod( (int)(actor->v->ideal_yaw/45)*45 );\n\tturnaround = anglemod(olddir - 180);\n\n\tVectorSubtract(enemy->v->origin, actor->v->origin, d);\n\tdeltax = DotProduct(d, axis[0]);\n\tdeltay = DotProduct(d, axis[1]);\n\tif (deltax>10)\n\t\td[1]= 0;\n\telse if (deltax<-10)\n\t\td[1]= 180;\n\telse\n\t\td[1]= DI_NODIR;\n\tif (deltay<-10)\n\t\td[2]= 270;\n\telse if (deltay>10)\n\t\td[2]= 90;\n\telse\n\t\td[2]= DI_NODIR;\n\n// try direct route\n\tif (d[1] != DI_NODIR && d[2] != DI_NODIR)\n\t{\n\t\tif (d[1] == 0)\n\t\t\ttdir = d[2] == 90 ? 45 : 315;\n\t\telse\n\t\t\ttdir = d[2] == 90 ? 135 : 215;\n\t\t\t\n\t\tif (tdir != turnaround && World_StepDirection(world, actor, tdir, dist, axis))\n\t\t\treturn;\n\t}\n\n// try other directions\n\tif ( ((rand()&3) & 1) ||  fabs(deltay)>fabs(deltax))\n\t{\n\t\ttdir=d[1];\n\t\td[1]=d[2];\n\t\td[2]=tdir;\n\t}\n\n\tif (d[1]!=DI_NODIR && d[1]!=turnaround \n\t&& World_StepDirection(world, actor, d[1], dist, axis))\n\t\t\treturn;\n\n\tif (d[2]!=DI_NODIR && d[2]!=turnaround\n\t&& World_StepDirection(world, actor, d[2], dist, axis))\n\t\t\treturn;\n\n/* there is no direct path to the player, so pick another direction */\n\n\tif (olddir!=DI_NODIR && World_StepDirection(world, actor, olddir, dist, axis))\n\t\t\treturn;\n\n\tif (rand()&1) \t/*randomly determine direction of search*/\n\t{\n\t\tfor (tdir=0 ; tdir<=315 ; tdir += 45)\n\t\t\tif (tdir!=turnaround && World_StepDirection(world, actor, tdir, dist, axis) )\n\t\t\t\t\treturn;\n\t}\n\telse\n\t{\n\t\tfor (tdir=315 ; tdir >=0 ; tdir -= 45)\n\t\t\tif (tdir!=turnaround && World_StepDirection(world, actor, tdir, dist, axis) )\n\t\t\t\t\treturn;\n\t}\n\n\tif (turnaround != DI_NODIR && World_StepDirection(world, actor, turnaround, dist, axis) )\n\t\t\treturn;\n\n\tactor->v->ideal_yaw = olddir;\t\t// can't move\n\n// if a bridge was pulled out from underneath a monster, it may not have\n// a valid standing position at all\n\n\tif (!World_CheckBottom (world, actor, axis[2]))\n\t\tWorld_FixCheckBottom (actor);\n\n}\n\n/*\n======================\nSV_CloseEnough\n\n======================\n*/\nqboolean World_CloseEnough (wedict_t *ent, wedict_t *goal, float dist)\n{\n\tint\t\ti;\n\t\n\tfor (i=0 ; i<3 ; i++)\n\t{\n\t\tif (goal->v->absmin[i] > ent->v->absmax[i] + dist)\n\t\t\treturn false;\n\t\tif (goal->v->absmax[i] < ent->v->absmin[i] - dist)\n\t\t\treturn false;\n\t}\n\treturn true;\n}\n\n/*\n======================\nSV_MoveToGoal\n\n======================\n*/\nqboolean World_MoveToGoal (world_t *world, wedict_t *ent, float dist)\n{\n\twedict_t\t*goal;\n\tvec3_t axis[3];\n\n\tent = (wedict_t*)PROG_TO_EDICT(world->progs, *world->g.self);\t\n\tgoal = (wedict_t*)PROG_TO_EDICT(world->progs, ent->v->goalentity);\n\n\tif ( !( (int)ent->v->flags & (FL_ONGROUND|FL_FLY|FL_SWIM) ) )\n\t{\n\t\treturn false;\n\t}\n\n// if the next step hits the enemy, return immediately\n\tif ( PROG_TO_EDICT(world->progs, ent->v->enemy) != (edict_t*)world->edicts && World_CloseEnough (ent, goal, dist) )\n\t\treturn true;\n\n\n\tWorld_GetEntGravityAxis(ent, axis);\n\n// bump around...\n\tif ( (rand()&3)==1 ||\n\t!World_StepDirection (world, ent, ent->v->ideal_yaw, dist, axis))\n\t{\n\t\tWorld_NewChaseDir (world, ent, goal, dist, axis);\n\t}\n\treturn true;\n}\n\n\n\n#ifdef ENGINE_ROUTING\n#ifdef HAVE_CLIENT\nstatic cvar_t route_shownodes = CVAR(\"route_shownodes\", \"0\");\n#endif\n\n#define LF_EDGE\t\t\t0x00000001\n#define LF_JUMP\t\t\t0x00000002\n#define LF_CROUCH\t\t0x00000004\n#define LF_TELEPORT\t\t0x00000008\n#define LF_USER\t\t\t0x7fffff00\n#define LF_DESTINATION\t0x80000000\t//You have reached your destination...\nstruct waypointnetwork_s\n{\n\tsize_t refs;\n\tsize_t numwaypoints;\n\tmodel_t *worldmodel;\n\n\tstruct resultnodes_s\n\t{\n\t\tvec3_t pos;\n\t\tint linkflags;\n\t\tfloat radius;\n\t} *displaynode;\n\tsize_t displaynodes;\n\n\tstruct waypoint_s\n\t{\n\t\tvec3_t org;\n\t\tfloat radius;\t//used for picking the closest waypoint. aka proximity weight. also relaxes routes inside the area.\n\t\tstruct wpneighbour_s\n\t\t{\n\t\t\tint node;\n\t\t\tfloat linkcost;//might be much lower in the case of teleports, or expensive if someone wanted it to be a lower priority link.\n\t\t\tint linkflags; //LF_*\n\t\t\tstruct wphint_s\n\t\t\t{\n\t\t\t\tvec3_t pos[3];\n\t\t\t} *hints;\n\t\t} *neighbour;\n\t\tsize_t neighbours;\n\t} waypoints[1];\n};\nvoid WayNet_Done(struct waypointnetwork_s *net)\n{\n\tif (net)\n\tif (0 == --net->refs)\n\t{\n\t\tZ_Free(net);\n\t}\n}\nstatic qboolean WayNet_TokenizeLine(char **linestart)\n{\n\tchar *end = *linestart;\n\tif (!end || !*end)\n\t{\t//clear it out...\n\t\tCmd_TokenizeString(\"\", false, false);\n\t\treturn false;\n\t}\n\tfor (; *end; end++)\n\t{\n\t\tif (*end == '\\n')\n\t\t{\n\t\t\t*end++ = 0;\n\t\t\tbreak;\n\t\t}\n\t}\n\tCmd_TokenizeString(*linestart, false, false);\n\t*linestart = end;\n\treturn true;\n}\nstatic struct waypointnetwork_s *WayNet_Begin(void **ctxptr, model_t *worldmodel)\n{\n\tstruct waypointnetwork_s *net = *ctxptr;\n\tif (!net)\n\t{\n\t\tchar *wf = NULL;\n\t\tqofs_t fsize = 0;\n\t\tif (!worldmodel)\n\t\t\treturn NULL;\n\t\tif (!wf && !strncmp(worldmodel->name, \"maps/\", 5))\n\t\t{\n\t\t\tchar n[MAX_QPATH];\n\t\t\tCOM_StripExtension(worldmodel->name+5, n, sizeof(n));\n\t\t\twf = FS_MallocFile(va(\"data/%s.way\", n), FS_GAME, &fsize);\n\t\t\tif (!wf)\n\t\t\t\twf = FS_MallocFile(va(\"bots/navigation/%s.nav\", n), FS_GAME, &fsize);\n\t\t}\n\t\tif (!wf)\n\t\t\twf = FS_MallocFile(va(\"%s.way\", worldmodel->name), FS_GAME, &fsize);\n\n\t\tif (wf && fsize >= 8 && (\n\t\t\t  (wf[0] == 'N' && wf[1] == 'A' && wf[2] == 'V' && wf[3] == '2'  &&  (wf[4]|(wf[5]<<8)|(wf[6]<<8)|(wf[7]<<8)) >= 12 && (wf[4]|(wf[5]<<8)|(wf[6]<<8)|(wf[7]<<8)) <= 17)\t//q1e\n\t\t\t||(wf[0] == 'N' && wf[1] == 'A' && wf[2] == 'V' && wf[3] == '3'  &&  (wf[4]|(wf[5]<<8)|(wf[6]<<8)|(wf[7]<<8)) >= 2  && (wf[4]|(wf[5]<<8)|(wf[6]<<8)|(wf[7]<<8)) <= 6)\t//q2e\n\t\t\t))\n\t\t{\t//rerelease's format(s)\n\t\t\tsizebuf_t sb = {wf, fsize, fsize};\n\t\t\tqboolean error = false;\n\t\t\tunsigned int u;\n\t\t\tunsigned int ver, numnodes, numlinks, numhints, numents;\n\t\t\tstruct wphint_s *hints;\n\t\t\tstruct wpneighbour_s *links;\n\n\t\t\tMSG_BeginReading(&sb, msg_nullnetprim);\n\t\t\t/*magic(already checked)*/MSG_ReadLong();\n\t\t\tver = MSG_ReadLong();\n\t\t\tif (wf[3] == '3') ver |= 0x80000000;\t//convert from q2 to q1 version numbers...\n\t\t\tnumnodes = MSG_ReadLong();\n\t\t\tnumlinks = MSG_ReadLong();\n\t\t\tnumhints = MSG_ReadLong();\n\t\t\tif (ver >= 16)\n\t\t\t\t/*some sort of scale?*/MSG_ReadFloat();\n\n\t\t\tnet = Z_Malloc(sizeof(*net)-sizeof(net->waypoints) + (numnodes*sizeof(struct waypoint_s)) + (numlinks*sizeof(struct wpneighbour_s)) + (numhints*sizeof(struct wphint_s)));\n\t\t\tnet->numwaypoints = numnodes;\n\n\t\t\tlinks = (struct wpneighbour_s*)(net->waypoints+numnodes);\n\t\t\thints = (struct wphint_s*)(links+numlinks);\n\n\t\t\tfor (u = 0; u < numnodes && !error; u++)\n\t\t\t{\n\t\t\t\tunsigned short flags = MSG_ReadShort();\t//some sort of conditional info\n\t\t\t\tunsigned short nodelinks = MSG_ReadShort();\n\t\t\t\tunsigned short firstlink = MSG_ReadShort();\n\t\t\t\tunsigned short radius = MSG_ReadShort();\n\n\t\t\t\t(void)flags;\n\t\t\t\tnet->waypoints[u].neighbours = nodelinks;\n\t\t\t\tnet->waypoints[u].neighbour = links + firstlink;\n\t\t\t\tnet->waypoints[u].radius = radius;\n\n\t\t\t\tif (net->waypoints[u].neighbour+net->waypoints[u].neighbours > &links[numlinks])\n\t\t\t\t\terror = true;\n\t\t\t}\n\t\t\tfor (u = 0; u < numnodes; u++)\n\t\t\t{\t//positions are split for some reason\n\t\t\t\tnet->waypoints[u].org[0] = MSG_ReadFloat();\n\t\t\t\tnet->waypoints[u].org[1] = MSG_ReadFloat();\n\t\t\t\tnet->waypoints[u].org[2] = MSG_ReadFloat()+24;\t//rerelease waypoints are aligned to the ground.\n\t\t\t}\n\n\t\t\tfor (u = 0; u < numlinks; u++)\n\t\t\t{\n\t\t\t\tunsigned short peernode = MSG_ReadShort();\n\t\t\t\tunsigned short type = MSG_ReadShort();\t//0=normal, 1=\n\t\t\t\tunsigned short hint = MSG_ReadShort();\t//can be ~0\n\n\t\t\t\tlinks[u].linkcost = 16;\n\t\t\t\tlinks[u].linkflags = 1u<<type;\n\t\t\t\tlinks[u].node = peernode;\n\t\t\t\tlinks[u].hints = NULL;\n\t\t\t\tif (hint < numhints)\n\t\t\t\t\tlinks[u].hints = hints+hint;\n\t\t\t\telse if (hint != 0xffff)\n\t\t\t\t\terror = true;\n\t\t\t\tif (links[u].node >= numnodes)\n\t\t\t\t\terror = true;\n\t\t\t}\n\t\t\tfor (u = 0; u < numhints; u++)\n\t\t\t{\n\t\t\t\thints[u].pos[0][0] = MSG_ReadFloat();\n\t\t\t\thints[u].pos[0][1] = MSG_ReadFloat();\n\t\t\t\thints[u].pos[0][2] = MSG_ReadFloat();\n\n\t\t\t\thints[u].pos[1][0] = MSG_ReadFloat();\n\t\t\t\thints[u].pos[1][1] = MSG_ReadFloat();\n\t\t\t\thints[u].pos[1][2] = MSG_ReadFloat();\n\n\t\t\t\thints[u].pos[2][0] = MSG_ReadFloat();\n\t\t\t\thints[u].pos[2][1] = MSG_ReadFloat();\n\t\t\t\thints[u].pos[2][2] = MSG_ReadFloat();\n\n\t\t\t\tif (ver >= 0x80000006)\n\t\t\t\t{\n\t\t\t\t\t/*hints[u].pos[3][0] =*/ MSG_ReadFloat();\n\t\t\t\t\t/*hints[u].pos[3][1] =*/ MSG_ReadFloat();\n\t\t\t\t\t/*hints[u].pos[3][2] =*/ MSG_ReadFloat();\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tnuments = MSG_ReadLong();\t//grr\n\t\t\tfor (u = 0; u < numents; u++)\n\t\t\t{\n\t\t\t\tunsigned short link = MSG_ReadShort();\n\t\t\t\tif (link >= numlinks)\n\t\t\t\t\terror = true;\t//err?.. probably we don't really care, but for the sake of sanity lets bail.\n\t\t\t\t/*mins = */MSG_ReadFloat();\n\t\t\t\tMSG_ReadFloat();\n\t\t\t\tMSG_ReadFloat();\n\n\t\t\t\t/*maxs = */MSG_ReadFloat();\n\t\t\t\tMSG_ReadFloat();\n\t\t\t\tMSG_ReadFloat();\n\n\t\t\t\tif (ver <= 13)\t//FIXME: true for 12, verify v13\n\t\t\t\t\t;\n\t\t\t\telse if (ver == 14)\n\t\t\t\t{\n\t\t\t\t\t/*targ*/MSG_ReadLong();\n\t\t\t\t\t/*class*/MSG_ReadLong();\n\t\t\t\t}\n\t\t\t\telse if (ver >= 15)\n\t\t\t\t\t/*entnum*/MSG_ReadLong();\n\t\t\t}\n\n\t\t\tfor (u = 0; u < numnodes && !error; u++)\n\t\t\t{\t//compute costs (no reading here)\n\t\t\t\tsize_t v;\n\t\t\t\tfor (v = 0; v < net->waypoints[u].neighbours; v++)\n\t\t\t\t{\n\t\t\t\t\tvec3_t move;\n\t\t\t\t\tVectorSubtract(net->waypoints[net->waypoints[u].neighbour[v].node].org, net->waypoints[u].org, move);\n\t\t\t\t\tnet->waypoints[u].neighbour[v].linkcost = sqrt(DotProduct(move,move));\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif(msg_badread)\n\t\t\t\terror = true;\n\t\t\tMSG_ReadByte();\n\t\t\tif (!msg_badread)\n\t\t\t\terror = true;\t//should have taken us over the end. there's trailing junk here.\n\n\t\t\tif (error)\n\t\t\t{\t//some sort of corrupt\n\t\t\t\tZ_Free(net);\n\t\t\t\tnet = NULL;\n\t\t\t}\n\t\t}\n\t\telse if (wf)\n\t\t{\t//our qc-friendly format (predates remaster)\n\t\t\t//read the number of waypoints\n\t\t\tchar *l=wf, *e;\n\t\t\tint numwaypoints, maxlinks, numlinks;\n\t\t\tstruct wpneighbour_s *nextlink;\n\t\t\tWayNet_TokenizeLine(&l);\n\t\t\tnumwaypoints = atoi(Cmd_Argv(0));\n\t\t\t//count lines and guess the link count.\n\t\t\tfor (e = l, maxlinks=0; *e; e++)\n\t\t\t\tif (*e == '\\n')\n\t\t\t\t\tmaxlinks++;\n\t\t\tmaxlinks -= numwaypoints;\n\n\t\t\tnet = Z_Malloc(sizeof(*net)-sizeof(net->waypoints) + (numwaypoints*sizeof(struct waypoint_s)) + (maxlinks*sizeof(struct wpneighbour_s)));\n\t\t\tnet->worldmodel = worldmodel;\n\n\t\t\tnextlink = (struct wpneighbour_s*)(net->waypoints+numwaypoints);\n\n\t\t\twhile (WayNet_TokenizeLine(&l) && net->numwaypoints < numwaypoints)\n\t\t\t{\n\t\t\t\tif (!Cmd_Argc())\n\t\t\t\t\tcontinue;\t//a comment line?\n\t\t\t\tnet->waypoints[net->numwaypoints].org[0] = atof(Cmd_Argv(0));\n\t\t\t\tnet->waypoints[net->numwaypoints].org[1] = atof(Cmd_Argv(1));\n\t\t\t\tnet->waypoints[net->numwaypoints].org[2] = atof(Cmd_Argv(2));\n\t\t\t\tnet->waypoints[net->numwaypoints].radius = atof(Cmd_Argv(3));\n\t\t\t\tnumlinks = bound(0, atoi(Cmd_Argv(4)), maxlinks);\n\n\t\t\t\t//make sure the links are valid, and clamp to avoid problems (even if we're then going to mis-parse.\n\t\t\t\tnet->waypoints[net->numwaypoints].neighbour = nextlink;\n\t\t\t\twhile (numlinks-- > 0 && WayNet_TokenizeLine(&l))\n\t\t\t\t{\n\t\t\t\t\tif (!Cmd_Argc())\n\t\t\t\t\t\tcontinue;\t//a comment line?\n\t\t\t\t\tnextlink[net->waypoints[net->numwaypoints].neighbours].node = atoi(Cmd_Argv(0));\n\t\t\t\t\tnextlink[net->waypoints[net->numwaypoints].neighbours].linkcost = atof(Cmd_Argv(1));\n\t\t\t\t\tnextlink[net->waypoints[net->numwaypoints].neighbours++].linkflags = atoi(Cmd_Argv(2));\n\t\t\t\t}\n\t\t\t\tmaxlinks -= net->waypoints[net->numwaypoints].neighbours;\n\t\t\t\tnextlink += net->waypoints[net->numwaypoints++].neighbours;\n\t\t\t}\n\t\t}\n\t\tBZ_Free(wf);\n\t}\n\n\tif (!*ctxptr)\n\t{\t//no network yet.\n\t\tif (!net)\n\t\t{\t//don't spam reload attempts.\n\t\t\tif (!worldmodel)\n\t\t\t\treturn NULL;\n\t\t\tnet = Z_Malloc(sizeof(*net)-sizeof(net->waypoints));\n\t\t\tnet->numwaypoints = 0;\n\t\t}\n\t\tnet->worldmodel = worldmodel;\n\n\t\t//link to the world state\n\t\tnet->refs = 1;\n\t\t*ctxptr = net;\n\t}\n\n\n\tnet->refs++;\n\treturn net;\n}\n\nstruct waydist_s\n{\n\tint node;\n\tfloat sdist;\n};\nint QDECL WayNet_Prioritise(const void *a, const void *b)\n{\n\tconst struct waydist_s *w1 = a, *w2 = b;\n\tif (w1->sdist < w2->sdist)\n\t\treturn -1;\n\tif (w1->sdist == w2->sdist)\n\t\treturn 0;\n\treturn 1;\n}\nint WayNet_FindNearestNode(struct waypointnetwork_s *net, vec3_t pos)\n{\n\tif (net && net->numwaypoints)\n\t{\n\t\t//we qsort the possible nodes, in an attempt to reduce traces.\n\t\tstruct waydist_s *sortedways = alloca(sizeof(*sortedways) * net->numwaypoints);\n\t\tsize_t u;\n\t\tvec3_t disp;\n\t\tfloat sradius;\n\t\ttrace_t tr;\n\t\tfor (u = 0; u < net->numwaypoints; u++)\n\t\t{\n\t\t\tsortedways[u].node = u;\n\t\t\tVectorSubtract(net->waypoints[u].org, pos, disp);\n\t\t\tsortedways[u].sdist = DotProduct(disp, disp);\n\t\t\tsradius = net->waypoints[u].radius*net->waypoints[u].radius;\n\t\t\tif (sortedways[u].sdist < sradius)\n\t\t\t\tsortedways[u].sdist -= sradius;\t//if we're inside the waypoint's radius, push inwards resulting in negatives, so these are always highly prioritised\n\t\t}\n\t\tqsort(sortedways, net->numwaypoints, sizeof(struct waydist_s), WayNet_Prioritise);\n\n\t\t//can't trace yet...\n\t\tif (net->worldmodel->loadstate != MLS_LOADED)\n\t\t\treturn sortedways[0].node;\n\t\tfor (u = 0; u < net->numwaypoints; u++)\n\t\t{\n\t\t\tif (sortedways[u].sdist > 0)\n\t\t\t{\t//if we're outside the node, we need to do a trace to make sure we can actually reach it.\n\t\t\t\tnet->worldmodel->funcs.NativeTrace(net->worldmodel, 0, NULL, NULL, pos, net->waypoints[sortedways[u].node].org, vec3_origin, vec3_origin, false, MASK_WORLDSOLID, &tr);\n\t\t\t\tif (tr.fraction < 1)\n\t\t\t\t\tcontinue;\t//this node is blocked. just move on to the next.\n\t\t\t}\n\t\t\treturn sortedways[u].node;\n\t\t}\n\t}\n\treturn -1;\n}\n\nstruct routecalc_s\n{\n\tworld_t *world;\n\twedict_t *ed;\n\tint spawncount;\t//so we don't confuse stuff if the map gets restarted.\n//\tfloat spawnid;\t//so the route fails if the ent is removed.\n\tfunc_t callback;\n\n\tvec3_t start;\n\tvec3_t end;\n\tint denylinkflags;\n\n\tint startn;\n\tint endn;\n\n\tint numresultnodes;\n\tstruct resultnodes_s *resultnodes;\n\n\tstruct waypointnetwork_s *waynet;\n};\n//main thread\nvoid Route_Calculated(void *ctx, void *data, size_t a, size_t b)\n{\n\tstruct routecalc_s *route = data;\n\tpubprogfuncs_t *prinst = route->world->progs;\n\t//let the gamecode know the results\n\n\tif (!route->callback)\n\t{\n\t\tif (route->waynet)\n\t\t{\n\t\t\tBZ_Free(route->waynet->displaynode);\n\t\t\troute->waynet->displaynode = BZ_Malloc(sizeof(struct resultnodes_s) * route->numresultnodes);\n\t\t\troute->waynet->displaynodes = route->numresultnodes;\n\t\t\tmemcpy(route->waynet->displaynode, route->resultnodes, sizeof(struct resultnodes_s) * route->numresultnodes);\n\t\t}\n\t}\n\telse if (route->callback && route->world->spawncount == route->spawncount/* && route->spawnid == route->ed->xv->uniquespawnid*/)\n\t{\n\t\tstruct globalvars_s * pr_globals = PR_globals(prinst, PR_CURRENT);\n\t\tstruct resultnodes_s *ptr = prinst->AddressableAlloc(prinst, sizeof(struct resultnodes_s) * route->numresultnodes);\n\t\tmemcpy(ptr, route->resultnodes, sizeof(struct resultnodes_s) * route->numresultnodes);\n\n\t\tG_INT(OFS_PARM0) = EDICT_TO_PROG(prinst, route->ed);\n\t\tVectorCopy(route->end, G_VECTOR(OFS_PARM1));\n\t\tG_INT(OFS_PARM2) = route->numresultnodes;\n\t\tG_INT(OFS_PARM3) = (char*)ptr-prinst->stringtable;\n\t\tPR_ExecuteProgram(prinst, route->callback);\n\t}\n\n\t//and we're done. destroy everything.\n\tWayNet_Done(route->waynet);\n\tZ_Free(route->resultnodes);\n\tZ_Free(route);\n}\n\n//#define FLOODALL\n#define COST_INFINITE FLT_MAX\n\ntypedef struct\n{\n\tint id;\n\tint flags;\n} nodefrom_t;\n\nstatic qboolean Route_Completed(struct routecalc_s *r, nodefrom_t *nodecamefrom)\n{\n\tsize_t u;\n\tstruct waypointnetwork_s *n = r->waynet;\n\tr->resultnodes = Z_Malloc(sizeof(*r->resultnodes)*(n->numwaypoints+1)*3);\n\n\tr->numresultnodes = 0;\n\n\t//target point is first. yay.\n\tVectorCopy(r->end, r->resultnodes[0].pos);\n\tr->resultnodes[0].linkflags = LF_DESTINATION;\n\tr->resultnodes[0].radius = 32;\n\tr->numresultnodes++;\n\n\tu = r->endn;\n\tfor (;;)\n\t{\n\t\tVectorCopy(n->waypoints[u].org, r->resultnodes[r->numresultnodes].pos);\n\t\tr->resultnodes[r->numresultnodes].linkflags = nodecamefrom[u].flags;\n\t\tr->resultnodes[r->numresultnodes].radius = n->waypoints[u].radius;\n\t\tr->numresultnodes++;\n\t\tif (u == r->startn)\n\t\t\tbreak;\n\t\tu = nodecamefrom[u].id;\n\t}\n\n\t//and include the start point, because we can\n\tVectorCopy(r->start, r->resultnodes[r->numresultnodes].pos);\n\tr->resultnodes[r->numresultnodes].linkflags = 0;\n\tr->resultnodes[r->numresultnodes].radius = 32;\n\tr->numresultnodes++;\n\treturn true;\n}\n\n#if 1\nstatic float Route_GuessCost(struct routecalc_s *r, float *fromorg)\n{\t//if we want to guarentee the shortest route, then we MUST always return a value <= to the actual cost here.\n\t//unfortunately we don't know how many teleporters are between the two points.\n\t//on the plus side, a little randomness here means we'll find alternative (longer) routes some times, which will reduce flash points and help flag carriers...\n\tvec3_t disp;\n\tVectorSubtract(r->end, fromorg, disp);\n\treturn sqrt(DotProduct(disp,disp));\n}\nstatic qboolean Route_Process(struct routecalc_s *r)\n{\n\tstruct waypointnetwork_s *n = r->waynet;\n\tint opennodes = 0;\n\tint u, j;\n\tfloat guesscost;\n\tstruct opennode_s {\n\t\tint node;\n\t\tfloat cost;\n\t} *open = alloca(sizeof(*open)*n->numwaypoints);\n\tfloat *nodecost = alloca(sizeof(*nodecost)*n->numwaypoints);\n\tnodefrom_t *nodecamefrom = alloca(sizeof(*nodecamefrom)*n->numwaypoints);\n\n\tfor(u = 0; u < n->numwaypoints; u++)\n\t\tnodecost[u] = COST_INFINITE;\n\n\tif (r->startn >= 0)\n\t{\n\t\tnodecost[r->startn] = 0;\n\t\topen[0].node = r->startn;\n\t\topen[0].cost = 0;\n\t\topennodes++;\n\t}\n\n\twhile(opennodes)\n\t{\n\t\tint nodeidx = open[--opennodes].node;\n\t\tstruct waypoint_s *wp = &n->waypoints[nodeidx];\n#ifdef _DEBUG\n\t\tif (nodeidx < 0 || nodeidx >= n->numwaypoints)\n\t\t{\n\t\t\tCon_Printf(\"Bad node index in open list\\n\");\n\t\t\treturn false;\n\t\t}\n#endif\n\t\tif (nodeidx == r->endn)\n\t\t{\t//we found the end!\n\t\t\treturn Route_Completed(r, nodecamefrom);\n\t\t}\n\t\tfor (u = 0; u < wp->neighbours; u++)\n\t\t{\n\t\t\tstruct wpneighbour_s *l = &wp->neighbour[u];\n\t\t\tint linkidx = l->node;\n\t\t\tfloat realcost = nodecost[nodeidx] + l->linkcost;\n\n\t\t\tif (l->linkflags & r->denylinkflags)\n\t\t\t\tcontinue;\n#ifdef _DEBUG\n\t\t\tif (linkidx < 0 || linkidx >= n->numwaypoints)\n\t\t\t{\n\t\t\t\tCon_Printf(\"Bad node link index in routing table\\n\");\n\t\t\t\treturn false;\n\t\t\t}\n#endif\n\t\t\tif (realcost >= nodecost[linkidx])\n\t\t\t\tcontinue;\n\n\t\t\tnodecamefrom[linkidx].id = nodeidx;\n\t\t\tnodecamefrom[linkidx].flags = l->linkflags;\n\t\t\tnodecost[linkidx] = realcost;\n\n\t\t\tfor (j = opennodes-1; j >= 0; j--)\n\t\t\t{\n\t\t\t\tif (open[j].node == linkidx)\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tguesscost = realcost + Route_GuessCost(r, n->waypoints[linkidx].org);\n\n\t\t\tif (j < 0)\n\t\t\t{\t//not already in the list\n\t\t\t\t//tbh, we should probably just directly bubble in this loop instead of doing the memcpy (with its internal second loop).\n\t\t\t\tfor (j = opennodes-1; j >= 0; j--)\n\t\t\t\t\tif (guesscost <= open[j].cost)\n\t\t\t\t\t\tbreak;\n\t\t\t\tj++;\n\t\t\t\t//move them up\n\t\t\t\tmemmove(&open[j+1], &open[j], sizeof(*open)*(opennodes-j));\n\t\t\t\topen[j].node = linkidx;\n\t\t\t\topen[j].cost = guesscost;\n\t\t\t\topennodes++;\n\t\t\t}\n\t\t\telse if (guesscost < open[j].cost)\n\t\t\t{\t//if it got cheaper, be prepared to move the node towards the higher addresses (these will be checked first).\n\t\t\t\tfor (; j+1 < opennodes && open[j+1].cost > guesscost; j++)\n\t\t\t\t\topen[j] = open[j+1];\n\t\t\t\t//okay, so we can't keep going... this is our new slot!\n\t\t\t\topen[j].node = linkidx;\n\t\t\t\topen[j].cost = guesscost;\n\t\t\t}\n\t\t\t//otherwise it got more expensive, and we don't care about that\n\t\t}\n\t}\n\n\treturn false;\n}\n#else\nstatic qboolean Route_Process(struct routecalc_s *r)\n{\n\tstruct waypointnetwork_s *n = r->waynet;\n\tint opennodes = 0;\n\tint u, j;\n\n\t//we use an open list in a desperate attempt to avoid recursing the entire network\n\tint *open = alloca(sizeof(*open)*n->numwaypoints);\n\tfloat *nodecost = alloca(sizeof(*nodecost)*n->numwaypoints);\n\tnodefrom_t *nodecamefrom = alloca(sizeof(*nodecamefrom)*n->numwaypoints);\n\n\tfor(u = 0; u < n->numwaypoints; u++)\n\t\tnodecost[u] = COST_INFINITE;\n\n\tnodecost[r->startn] = 0;\n\topen[opennodes++] = r->startn;\n\n\twhile(opennodes)\n\t{\n\t\tint nodeidx = open[--opennodes];\n\t\tstruct waypoint_s *wp = &n->waypoints[nodeidx];\n//\t\tif (nodeidx < 0 || nodeidx >= n->numwaypoints)\n//\t\t\treturn false;\n\n\t\tfor (u = 0; u < wp->neighbours; u++)\n\t\t{\n\t\t\tstruct wpneighbour_s *l = &wp->neighbour[u];\n\t\t\tint linkidx = l->node;\n\n\t\t\tfloat realcost = nodecost[nodeidx] + l->linkcost;\n//\t\t\tif (linkidx < 0 || linkidx >= n->numwaypoints)\n//\t\t\t\treturn false;\n\t\t\tif (realcost >= nodecost[linkidx])\n\t\t\t\tcontinue;\n\n\t\t\tnodecamefrom[linkidx].id = nodeidx;\n\t\t\tnodecamefrom[linkidx].flags = l->linkflags;\n\t\t\tnodecost[linkidx] = realcost;\n\n\t\t\tfor (j = 0; j < opennodes; j++)\n\t\t\t{\n\t\t\t\tif (open[j] == linkidx)\n\t\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tif (j == opennodes)\t//not already queued\n\t\t\t\topen[opennodes++] = linkidx;\n\t\t}\n\t}\n\t\n\tif (r->endn >= 0 && nodecost[r->endn] < COST_INFINITE)\n\t{\t//we found the end! we can build the route from end to start.\n\t\treturn Route_Completed(r, nodecamefrom);\n\t}\n\treturn false;\n}\n#endif\n\n//worker thread\nvoid Route_Calculate(void *ctx, void *data, size_t a, size_t b)\n{\n\tstruct routecalc_s *route = data;\n\n\t//first thing is to find the start+end nodes.\n\n\tif (route->waynet && route->startn >= 0 && route->endn >= 0 && Route_Process(route))\n\t\t;\n\telse\n\t{\n\t\troute->numresultnodes = 0;\n\t\troute->resultnodes = Z_Malloc(sizeof(*route->resultnodes)*2);\n\t\tVectorCopy(route->end, route->resultnodes[0].pos);\n\t\troute->resultnodes[0].linkflags = LF_DESTINATION;\n\t\troute->numresultnodes++;\n\n\t\tVectorCopy(route->start, route->resultnodes[route->numresultnodes].pos);\n\t\troute->resultnodes[route->numresultnodes].linkflags = 0;\n\t\troute->numresultnodes++;\n\t}\n\n\tCOM_AddWork(WG_MAIN, Route_Calculated, NULL, route, 0, 0);\n}\n\n//void route_linkitem(entity item, int ittype)\t//-1 to unlink\n//void route_choosedest(entity ent, int numitemtypes, float *itemweights)\n/*\n=============\nPF_route_calculate\n\nengine reads+caches the nodes from a file.\nthe route's nodes must be memfreed on completion.\nthe first node in the nodelist is the destination.\n\ntypedef struct {\n\tvector dest;\n\tint linkflags;\n\t//float anglehint;\n} nodeslist_t;\nvoid(entity ent, vector dest, int denylinkflags, void(entity ent, vector dest, int numnodes, nodeslist_t *nodelist) callback) route_calculate = #0;\n=============\n*/\nvoid QCBUILTIN PF_route_calculate (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)\n{\n\tstruct routecalc_s *route = Z_Malloc(sizeof(*route));\n\tfloat *end;\n\troute->world = prinst->parms->user;\n\troute->spawncount = route->world->spawncount;\n\troute->ed = G_WEDICT(prinst, OFS_PARM0);\n//\troute->spawnid = route->ed->xv->uniquespawnid;\n\tend = G_VECTOR(OFS_PARM1);\n\troute->denylinkflags = G_INT(OFS_PARM2);\n\troute->callback = G_INT(OFS_PARM3);\n\n\tVectorCopy(route->ed->v->origin, route->start);\n\tVectorCopy(end, route->end);\n\n\troute->waynet = WayNet_Begin(&route->world->waypoints, route->world->worldmodel);\n\tif (!route->waynet)\n\t{\n\t\tZ_Free(route);\n\t\treturn;\n\t}\n\n\t//tracelines use some sequence info to avoid retracing the same brush multiple times.\n\t//\tthis means that we can't reliably trace on worker threads (would break the main thread occasionally).\n\t//\tso we have to do this here.\n\t//FIXME: find a safe way to NOT do this here.\n\troute->startn = WayNet_FindNearestNode(route->waynet, route->start);\n\troute->endn = WayNet_FindNearestNode(route->waynet, route->end);\n\n\tCOM_AddWork(WG_LOADER, Route_Calculate, NULL, route, 0, 0);\n}\n#ifdef HAVE_CLIENT\nstatic void Route_Visualise_f(void)\n{\n\textern world_t csqc_world;\n\tvec3_t targ = {atof(Cmd_Argv(1)),atof(Cmd_Argv(2)),atof(Cmd_Argv(3))};\n\tstruct routecalc_s *route = Z_Malloc(sizeof(*route));\n\troute->world = &csqc_world;\n\troute->spawncount = route->world->spawncount;\n\troute->ed = route->world->edicts;\n//\troute->spawnid = route->ed->xv->uniquespawnid;\n\tVectorCopy(r_refdef.vieworg, route->start);\n\tVectorCopy(targ, route->end);\n\n\troute->waynet = WayNet_Begin(&route->world->waypoints, cl.worldmodel);\n\tif (!route->waynet)\n\t{\n\t\tZ_Free(route);\n\t\treturn;\n\t}\n\n\t//tracelines use some sequence info to avoid retracing the same brush multiple times.\n\t//\tthis means that we can't reliably trace on worker threads (would break the main thread occasionally).\n\t//\tso we have to do this here.\n\t//FIXME: find a safe way to NOT do this here.\n\troute->startn = WayNet_FindNearestNode(route->waynet, route->start);\n\troute->endn = WayNet_FindNearestNode(route->waynet, route->end);\n\n\tCOM_AddWork(WG_LOADER, Route_Calculate, NULL, route, 0, 0);\n}\n\n#include \"shader.h\"\nvoid PR_Route_Visualise (void)\n{\n\textern world_t csqc_world;\n\tworld_t *w = &csqc_world;\n\tstruct waypointnetwork_s *wn;\n\tsize_t u;\n\n\twn = (w && (w->waypoints || route_shownodes.ival))?WayNet_Begin(&w->waypoints, cl.worldmodel):NULL;\n\tif (wn)\n\t{\n\t\tif (route_shownodes.ival)\n\t\t{\n\t\t\tfloat mat[12] = {1,0,0,0, 0,1,0,0, 0,0,1,0};\n\t\t\tshader_t *shader_out = R_RegisterShader(\"waypointvolume_out\", SUF_NONE,\n\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\"polygonoffset\\n\"\n\t\t\t\t\t\t\"nodepth\\n\"\n\t\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\t\"map $whiteimage\\n\"\n\t\t\t\t\t\t\t\"blendfunc add\\n\"\n\t\t\t\t\t\t\t\"rgbgen vertex\\n\"\n\t\t\t\t\t\t\t\"alphagen vertex\\n\"\n\t\t\t\t\t\t\"}\\n\"\n\t\t\t\t\t\"}\\n\");\n\t\t\tshader_t *shader_in = R_RegisterShader(\"waypointvolume_in\", SUF_NONE,\n\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\"polygonoffset\\n\"\n\t\t\t\t\t\t\"cull disable\\n\"\n\t\t\t\t\t\t\"nodepth\\n\"\n\t\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\t\"map $whiteimage\\n\"\n\t\t\t\t\t\t\t\"blendfunc add\\n\"\n\t\t\t\t\t\t\t\"rgbgen vertex\\n\"\n\t\t\t\t\t\t\t\"alphagen vertex\\n\"\n\t\t\t\t\t\t\"}\\n\"\n\t\t\t\t\t\"}\\n\");\n\t\t\tfloat radius;\n\t\t\tvec3_t dir;\n\t\t\t//should probably use a different colour for the node you're inside.\n\t\t\tint nearest = WayNet_FindNearestNode(wn, r_origin);\n\t\t\tfor (u = 0; u < wn->numwaypoints; u++)\n\t\t\t{\n\t\t\t\tmat[3] = wn->waypoints[u].org[0];\n\t\t\t\tmat[7] = wn->waypoints[u].org[1];\n\t\t\t\tmat[11] = wn->waypoints[u].org[2];\n\t\t\t\tradius = wn->waypoints[u].radius;\n\t\t\t\tif (radius <= 0)\n\t\t\t\t\tradius = 1; //waypoints shouldn't really have a radius of 0, but if they do we'll still want to draw something.\n\n\t\t\t\tVectorSubtract(wn->waypoints[u].org, r_refdef.vieworg, dir);\n\t\t\t\tif (DotProduct(dir,dir) < radius*radius)\n\t\t\t\t\tCLQ1_AddOrientedSphere(shader_in, radius, mat, 0.0, 0.1, (nearest==u)?0.2:0.0, 1);\n\t\t\t\telse\n\t\t\t\t\tCLQ1_AddOrientedSphere(shader_out, radius, mat, 0.2, 0.0, (nearest==u)?0.2:0.0, 1);\n\t\t\t}\n\t\t\tfor (u = 0; u < wn->numwaypoints; u++)\n\t\t\t{\n\t\t\t\tsize_t n;\n\t\t\t\tfor (n = 0; n < wn->waypoints[u].neighbours; n++)\n\t\t\t\t{\n\t\t\t\t\tstruct waypoint_s *r = wn->waypoints + wn->waypoints[u].neighbour[n].node;\n\t\t\t\t\tCLQ1_DrawLine(shader_out, wn->waypoints[u].org, r->org, 0, 0, (nearest==u)?1:0.2, 1);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (wn->displaynodes)\n\t\t{\t//FIXME: we should probably use beams here\n\t\t\tshader_t *shader_route = R_RegisterShader(\"waypointroute\", SUF_NONE,\n\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\"polygonoffset\\n\"\n\t\t\t\t\t\t\"nodepth\\n\"\n\t\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\t\"map $whiteimage\\n\"\n\t\t\t\t\t\t\t\"blendfunc add\\n\"\n\t\t\t\t\t\t\t\"rgbgen vertex\\n\"\n\t\t\t\t\t\t\t\"alphagen vertex\\n\"\n\t\t\t\t\t\t\"}\\n\"\n\t\t\t\t\t\"}\\n\");\n\n\t\t\tfor (u = wn->displaynodes-1; u > 0; u--)\n\t\t\t{\n\t\t\t\tvec_t *start = wn->displaynode[u].pos;\n\t\t\t\tvec_t *end = wn->displaynode[u-1].pos;\n\t\t\t\tCLQ1_DrawLine(shader_route, start, end, 0.5, 0.5, 0.5, 1);\n\t\t\t}\n\t\t}\n\t}\n\tWayNet_Done(wn);\n}\n#endif\n\n//destroys the routing waypoint cache.\nvoid PR_Route_Shutdown (world_t *world)\n{\n\tWayNet_Done(world->waypoints);\n\tworld->waypoints = NULL;\n}\n\nstatic void Route_Reload_f(void)\n{\n#if defined(HAVE_CLIENT) && defined(CSQC_DAT)\n\textern world_t csqc_world;\n\tPR_Route_Shutdown(&csqc_world);\n#endif\n#ifdef HAVE_SERVER\n\tPR_Route_Shutdown(&sv.world);\n#endif\n}\nvoid PR_Route_Init (void)\n{\n#if defined(HAVE_CLIENT) && defined(CSQC_DAT)\n\tCvar_Register(&route_shownodes, NULL);\n\tCmd_AddCommand(\"route_visualise\", Route_Visualise_f);\n#endif\n\tCmd_AddCommand(\"route_reload\", Route_Reload_f);\n}\n\n//route_force\n//COM_WorkerPartialSync\n#endif\n\n#endif\n"
  },
  {
    "path": "engine/server/sv_mvd.c",
    "content": "/*\nCopyright (C) 1996-1997 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\nSee the included (GNU.txt) GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n*/\n\n#include \"quakedef.h\"\n#ifdef MVD_RECORDING\n#ifndef CLIENTONLY\n\n#include \"winquake.h\"\n#include \"fs.h\"\n\n#include \"netinc.h\"\n\nvoid SV_MVDStop_f (void);\n\n#define demo_size_padding 0x1000\n\nstatic void QDECL SV_DemoDir_Callback(struct cvar_s *var, char *oldvalue);\n\ncvar_t\tsv_demoAutoRecord = CVARAD(\"sv_demoAutoRecord\", \"0\", \"cl_autodemo\", \"If set, automatically record demos.\\n-1: record on client connection only.\\n1+: record once there's this many active players on the server (or if connected to a remote server).\");\ncvar_t\tsv_demoUseCache = CVARD(\"sv_demoUseCache\", \"\", \"If set, demo data will be flushed only periodically\");\ncvar_t\tsv_demoCacheSize = CVAR(\"sv_demoCacheSize\", \"0x80000\"); //half a meg\ncvar_t\tsv_demoMaxDirSize = CVARD(\"sv_demoMaxDirSize\", \"100mb\", \"Maximum allowed serverside storage space for mvds. set to blank to remove the limit. New demos cannot be recorded once this size is reached.\");\t//so ktpro autorecords.\ncvar_t\tsv_demoMaxDirCount = CVARD(\"sv_demoMaxDirCount\", \"500\", \"Maximum allowed serverside mvds to record. Set to 0 to remove the limit. New demos cannot be recorded once this many demos have already been recorded.\");\t//so ktpro autorecords.\ncvar_t\tsv_demoMaxDirAge = CVARD(\"sv_demoMaxDirAge\", \"0\", \"Maximum allowed age for demos, any older demos will be deleted when sv_demoClearOld is set (this doesn't prevent recording new demos).\");\ncvar_t\tsv_demoClearOld = CVARD(\"sv_demoClearOld\", \"0\", \"Automatically delete demos to keep the demos count reasonable.\");\ncvar_t\tsv_demoDir = CVARC(\"sv_demoDir\", \"demos\", SV_DemoDir_Callback);\ncvar_t\tsv_demoDirAlt = CVARCD(\"sv_demoDirAlt\", \"\", SV_DemoDir_Callback, \"Provides a fallback directory name for demo downloads, for when sv_demoDir doesn't contain the requested demo.\");\ncvar_t\tsv_demofps = CVAR(\"sv_demofps\", \"30\");\ncvar_t\tsv_demoPings = CVARD(\"sv_demoPings\", \"10\", \"Interval between ping updates in mvds\");\ncvar_t\tsv_demoMaxSize = CVARD(\"sv_demoMaxSize\", \"\", \"Demos will be truncated to be no larger than this size.\");\ncvar_t\tsv_demoExtraNames = CVAR(\"sv_demoExtraNames\", \"\");\ncvar_t\tsv_demoExtensions = CVARD(\"sv_demoExtensions\", \"1\", \"Enables protocol extensions within MVDs. This will cause older/non-fte clients to error upon playback.\\n0: off.\\n1: all extensions.\\n2: extensions also supported by a certain other engine.\");\ncvar_t\tsv_demoAutoCompress = CVARD(\"sv_demoAutoCompress\", \"\", \"Specifies whether to compress demos as they're recorded.\\n0 = no compression.\\n1 = gzip compression.\");\ncvar_t\tsv_demo_write_csqc = CVARD(\"sv_demo_write_csqc\", \"\", \"Writes a copy of the csprogs into recorded demos. This ensures that the demo can be played back despite future gamecode changes.\");\n\ncvar_t qtv_password\t\t= CVAR(\t\t\"qtv_password\", \"\");\ncvar_t qtv_maxstreams\t= CVARAFD(\t\"qtv_maxstreams\", \"0\",\n\t\t\t\t\t\t\t\t\t\"mvd_maxstreams\",  0, \"This is the maximum number of QTV clients/proxies that may be directly connected to the server. If empty then there is no limit. 0 disallows any streaming.\");\n\ncvar_t\t\t\tsv_demoAutoPrefix = CVAR(\"sv_demoAutoPrefix\", \"auto_\");\ncvar_t\t\t\tsv_demoPrefix = CVAR(\"sv_demoPrefix\", \"\");\ncvar_t\t\t\tsv_demoSuffix = CVAR(\"sv_demoSuffix\", \"\");\ncvar_t\t\t\tsv_demotxt = CVAR(\"sv_demotxt\", \"1\");\n\nvoid SV_WriteMVDMessage (sizebuf_t *msg, int type, int to, float time);\nvoid SV_WriteRecordMVDMessage (sizebuf_t *msg);\n#ifdef TCPCONNECT\nvoid tobase64(unsigned char *out, int outlen, unsigned char *in, int inlen);\n#endif\n\n\nstatic struct\n{\t//tracks the previously recorded demos, so we don't have to content with dates and filesystem ordering and stuff.\n#define DEMOLOG_LENGTH 16\n\tunsigned int sequence;\t//incremented\n\tstruct \n\t{\n\t\tchar filename[MAX_QPATH];\n\t} log[DEMOLOG_LENGTH];\n} demolog;\n\ndemo_t\t\t\tdemo;\nstatic float\t\t\tdemo_prevtime;\n//static dbuffer_t\t*demobuffer;\n//static int\theader = (char *)&((header_t*)0)->data - (char *)NULL;\nstatic sizebuf_t demomsg;\nint demomsgtype;\nint demomsgto;\nstatic char demomsgbuf[MAX_OVERALLMSGLEN];\n\nstatic void SV_MVD_Stopped(void);\n\nstatic mvddest_t *singledest;\t//used when a stream is starting up so redundant data doesn't get dumped into other streams\nstatic struct reversedest_s\n{\n\tstruct reversedest_s *next;\n\tqtvpendingstate_t info;\n\tvfsfile_t *stream;\n\tchar inbuffer[2048];\n\tint inbuffersize;\n\tdouble timeout;\n} *reversedest;\t//used when a reverse stream is starting up\n\nstatic mvddest_t *SV_MVD_InitStream(vfsfile_t *stream, const char *info);\nqboolean SV_MVD_Record (mvddest_t *dest);\nchar *SV_MVDName2Txt(char *name);\nextern cvar_t qtv_password;\n\n//does not unlink.\nstatic void DestClose(mvddest_t *d, enum mvdclosereason_e reason)\n{\n\tif (d->desttype == DEST_THREADEDFILE)\n\t{\n\t\twhile(d->flushing == true)\n\t\t\tCOM_WorkerPartialSync(d, &d->flushing, true);\n\t}\n\n\tif (d->cache)\n\t\tBZ_Free(d->cache);\n\tif (d->file)\n\t{\n\t\tVFS_CLOSE(d->file);\n\t\tif (d->desttype != DEST_STREAM)\n\t\t\tFS_FlushFSHashWritten(d->filename);\n\t}\n\n\tif (d->desttype != DEST_STREAM)\n\t{\n\t\tif (reason == MVD_CLOSE_CANCEL)\n\t\t{\n\t\t\tFS_Remove(d->filename, FS_GAMEONLY);\n\n\t\t\tFS_Remove(SV_MVDName2Txt(d->filename), FS_GAMEONLY);\n\n\t\t\t//SV_BroadcastPrintf (PRINT_CHAT, \"Server recording canceled, demo removed\\n\");\n\t\t}\n\t\telse\n\t\t{\n\t\t\tchar buf[512];\n\t\t\tQ_strncpyz(demolog.log[demolog.sequence%DEMOLOG_LENGTH].filename, d->simplename, sizeof(demolog.log[demolog.sequence%DEMOLOG_LENGTH].filename));\n\t\t\tdemolog.sequence++;\n\t\t\tSV_BroadcastPrintf (PRINT_CHAT, \"Server recording complete\\n^[/download %s^]\\n\", COM_QuotedString(va(\"demos/%s\",d->simplename), buf, sizeof(buf), false));\n\t\t}\n\t}\n\n\tZ_Free(d);\n}\n\nstatic void MVD_FlushDest_Flushed(void *ctx, void *data, size_t a, size_t b)\n{\n\tmvddest_t *d = ctx;\n\td->flushing = false;\n}\nstatic void MVD_FlushDest_Worker(void *ctx, void *data, size_t datasize, size_t b)\n{\n\tmvddest_t *d = ctx;\n\tint len = VFS_WRITE(d->file, data, datasize);\n\tVFS_FLUSH(d->file);\n\n\tif (len != datasize)\n\t\td->error = true;\n\n\td->altcache = data;\n\tCOM_AddWork(WG_MAIN, MVD_FlushDest_Flushed, d, NULL, 0, 0);\n}\n\nvoid DestFlush(qboolean compleate)\n{\n\tint len;\n\tmvddest_t *d, *t;\n\n\tif (compleate)\n\t{\n\t\t//make sure everything is flushed.\n\t\tMVDWrite_Begin(255, -1, 0);\n\t}\n\n\tif (!demo.dest)\n\t\treturn;\n\twhile (demo.dest->error)\n\t{\n\t\td = demo.dest;\n\t\tdemo.dest = d->nextdest;\n\n\t\tDestClose(d, MVD_CLOSE_FSERROR);\n\n\t\tif (!demo.dest)\n\t\t{\n\t\t\tSV_MVDStop(MVD_CLOSE_DISCONNECTED, false);\n\t\t\treturn;\n\t\t}\n\t}\n\tfor (d = demo.dest; d; d = d->nextdest)\n\t{\n\t\tswitch(d->desttype)\n\t\t{\n\t\tcase DEST_FILE:\n\t\t\tVFS_FLUSH (d->file);\n\t\t\tbreak;\n\t\tcase DEST_BUFFEREDFILE:\n\t\t\tif (d->cacheused+demo_size_padding > d->maxcachesize || compleate)\n\t\t\t{\n\t\t\t\tlen = VFS_WRITE(d->file, d->cache, d->cacheused);\n\t\t\t\tif (len < d->cacheused)\n\t\t\t\t\td->error = true;\n\t\t\t\tVFS_FLUSH(d->file);\n\n\t\t\t\td->cacheused = 0;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase DEST_THREADEDFILE:\n\t\t\tif (d->cacheused+demo_size_padding > d->maxcachesize || compleate)\n\t\t\t{\n\t\t\t\tvoid *data = d->cache;\n\t\t\t\twhile(d->flushing == true)\n\t\t\t\t\tCOM_WorkerPartialSync(d, &d->flushing, true);\n\t\t\t\td->cache = d->altcache;\n\t\t\t\td->altcache = NULL;\n\t\t\t\td->flushing = true;\n\t\t\t\tCOM_AddWork(WG_LOADER, MVD_FlushDest_Worker, d, data, d->cacheused, 0);\n\t\t\t\td->cacheused = 0;\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase DEST_STREAM:\n\t\t\tif (d->cacheused && !d->error)\n\t\t\t{\n\t\t\t\tlen = VFS_WRITE(d->file, d->cache, d->cacheused);\n\t\t\t\tif (len < 0) //client died\n\t\t\t\t\td->error = true;\n\t\t\t\telse if (len > 0)\t//we put some data through\n\t\t\t\t{\t//move up the buffer\n\t\t\t\t\td->cacheused -= len;\n\t\t\t\t\tmemmove(d->cache, d->cache+len, d->cacheused);\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase DEST_NONE:\n\t\t\tSys_Error(\"DestFlush encoundered bad dest.\");\n\t\t}\n\n\t\tif (sv_demoMaxSize.value && d->totalsize > sv_demoMaxSize.value*1024)\n\t\t\td->error = 2;\t//abort, but don't kill it.\n\n\t\twhile (d->nextdest && d->nextdest->error)\n\t\t{\n\t\t\tt = d->nextdest;\n\t\t\td->nextdest = t->nextdest;\n\n\t\t\tDestClose(t, MVD_CLOSE_FSERROR);\n\t\t}\n\t}\n}\n\nenum qtvstatus_e\n{\n\tQTV_ERROR = -1,\t//corrupt/bad request that should be dropped.\n\tQTV_RETRY = 0,\t//still handshaking.\n\tQTV_ACCEPT = 1\t//stream is now owned by the qtv code\n};\nint SV_MVD_GotQTVRequest(vfsfile_t *clientstream, char *headerstart, char *headerend, qtvpendingstate_t *p)\n{\n\tchar *e;\n\n\tqboolean server = false;\n\tchar *start, *lineend;\n\tint versiontouse = 0;\n\tint raw = 0;\n\tchar password[256] = \"\";\n\tchar userinfo[1024];\n\tstatic struct\n\t{\n\t\tconst char *name;\t//as seen in protocol\n\t\thashfunc_t *func;\n\t\tint base;\n\t} hashes[] = {\n\t\t{\"NONE\", NULL, -1},\t\t\t//for annonymous connections\n\t\t{\"PLAIN\", NULL, 0},\n//\t\t{\"CCITT\", &hash_crc16, 16},\t//'the CCITT standard CRC used by XMODEM'. 16bit anyway, don't allow, too easy to guess.\n//\t\t{\"MD4\", &hash_md4, 15},\t\t//md4 is available to all QW clients, but probably too weak to really use.\n//\t\t{\"MD5\", &hash_md5, 16},\t\t//blurgh\n\t\t{\"SHA1\", &hash_sha1, 16},\n\t\t{\"SHA2_256\", &hash_sha2_256, 64},\n\t\t{\"SHA2_512\", &hash_sha2_512, 64},\n//\t\t{\"SHA3_512\", &hash_sha3_512, 16},\t//eztv apparently allows this\n\t};\n\tint authmethod = 0;\t//which of the above we're trying to use...\n\n\tstart = headerstart;\n\n\tlineend = strchr(start, '\\n');\n\tif (!lineend)\n\t\treturn QTV_ERROR;\n\n\t*lineend = '\\0';\n\tCOM_ParseToken(start, NULL);\n\tstart = lineend+1;\n\tif (strcmp(com_token, \"QTV\"))\n\t{\t//it's an error if it's not qtv.\n\t\tif (!strcmp(com_token, \"QTVSV\"))\n\t\t\tserver = true;\n\t\telse\n\t\t\treturn QTV_ERROR;\n\t}\n\n\tif (server != p->isreverse)\n\t{\t//just a small check\n\t\treturn QTV_ERROR;\n\t}\n\n\t*userinfo = 0;\n\tfor(;;)\n\t{\n\t\tlineend = strchr(start, '\\n');\n\t\tif (!lineend)\n\t\t\tbreak;\n\t\t*lineend = '\\0';\n\t\tstart = COM_ParseToken(start, NULL);\n\t\tif (start && *start == ':')\n\t\t{\n//VERSION: a list of the different qtv protocols supported. Multiple versions can be specified. The first is assumed to be the prefered version.\n//RAW: if non-zero, send only a raw mvd with no additional markup anywhere (for telnet use). Doesn't work with challenge-based auth, so will only be accepted when proxy passwords are not required.\n//AUTH: specifies an auth method, the exact specs varies based on the method\n//\t\tPLAIN: the password is sent as a PASSWORD line\n//\t\tMD4: the server responds with an \"AUTH: MD4\\n\" line as well as a \"CHALLENGE: somerandomchallengestring\\n\" line, the client sends a new 'initial' request with CHALLENGE: MD4\\nRESPONSE: hexbasedmd4checksumhere\\n\"\n//\t\tMD5: same as md4\n//\t\tCCITT: same as md4, but using the CRC stuff common to all quake engines.\n//\t\tif the supported/allowed auth methods don't match, the connection is silently dropped.\n//SOURCE: which stream to play from, DEFAULT is special. Without qualifiers, it's assumed to be a tcp address.\n//COMPRESSION: Suggests a compression method (multiple are allowed). You'll get a COMPRESSION response, and compression will begin with the binary data.\n\n\t\t\tstart = start+1;\n\t\t\twhile(*start == ' ' || *start == '\\t')\n\t\t\t\tstart++;\n\t\t\tCon_DPrintf(\"qtv, got (%s) (%s)\\n\", com_token, start);\n\t\t\tif (!strcmp(com_token, \"VERSION\"))\n\t\t\t{\n\t\t\t\tstart = COM_ParseToken(start, NULL);\n\t\t\t\tif (atoi(com_token) == 1)\n\t\t\t\t\tversiontouse = 1;\n\t\t\t}\n\t\t\telse if (!strcmp(com_token, \"RAW\"))\n\t\t\t{\n\t\t\t\tstart = COM_ParseToken(start, NULL);\n\t\t\t\traw = atoi(com_token);\n\t\t\t}\n\t\t\telse if (!strcmp(com_token, \"PASSWORD\"))\n\t\t\t{\n\t\t\t\tstart = COM_ParseToken(start, NULL);\n\t\t\t\tQ_strncpyz(password, com_token, sizeof(password));\n\t\t\t}\n\t\t\telse if (!strcmp(com_token, \"AUTH\"))\n\t\t\t{\n\t\t\t\tint thisauth;\n\t\t\t\tstart = COM_ParseToken(start, NULL);\n\t\t\t\tfor (thisauth = 1; ; thisauth++)\n\t\t\t\t{\n\t\t\t\t\tif (thisauth == countof(hashes))\n\t\t\t\t\t{\n\t\t\t\t\t\tCon_DPrintf(\"qtv: received unrecognised auth method (%s)\\n\", com_token);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tif (!strcmp(com_token, hashes[thisauth].name))\n\t\t\t\t\t{\t//we know this one.\n\t\t\t\t\t\tif (authmethod < thisauth)\n\t\t\t\t\t\t\tauthmethod = thisauth;\t//and its better than the previous one we saw\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (!strcmp(com_token, \"SOURCE\"))\n\t\t\t{\n\t\t\t\t//servers don't support source, and ignore it.\n\t\t\t\t//source is only useful for qtv proxy servers.\n\t\t\t}\n\t\t\telse if (!strcmp(com_token, \"COMPRESSION\"))\n\t\t\t{\n\t\t\t\t//compression not supported yet\n\t\t\t}\n\t\t\telse if (!strcmp(com_token, \"QTV_EZQUAKE_EXT\"))\n\t\t\t{\n\t\t\t\t//if we were treating this as a regular client over tcp (qizmo...)\n\t\t\t}\n\t\t\telse if (!strcmp(com_token, \"USERINFO\"))\n\t\t\t{\n\t\t\t\t//if we were treating this as a regular client over tcp (qizmo...)\n\t\t\t\tstart = COM_ParseTokenOut(start, NULL, userinfo, sizeof(userinfo), &com_tokentype);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t//not recognised.\n\t\t\t}\n\t\t}\n\t\tstart = lineend+1;\n\t}\n\n\t/*len = (headerend - headerstart)+2;\n\tp->insize -= len;\n\tmemmove(p->inbuffer, p->inbuffer + len, p->insize);\n\tp->inbuffer[p->insize] = 0;\n\t*/\n\n\te = NULL;\n\tif (p->hasauthed)\n\t{\n\t}\n\telse if (p->isreverse)\n\t\tp->hasauthed = true;\t//reverse connections do not need to auth.\n\telse if (!*qtv_password.string)\n\t\tp->hasauthed = true;\t//no password, no need to auth.\n\telse if (*password)\n\t{\n\t\tchar hash[512];\n\t\tqbyte digest[DIGEST_MAXSIZE];\n\t\tif (!*p->challenge && hashes[authmethod].func)\n\t\t\te =\t(\"QTVSV 1\\n\"\n\t\t\t\t \"PERROR: Challenge wasn't given...\\n\\n\");\n\t\tswitch(hashes[authmethod].base)\n\t\t{\n\t\tdefault:\n\t\tcase -1:\t//no auth at all\n\t\t\te = (\"QTVSV 1\\n\"\n\t\t\t\t \"PERROR: You need to provide a password.\\n\\n\");\n\t\t\tbreak;\n\t\tcase 0:\t\t//plain text. challenge is not used.\n\t\t\tQ_snprintfz(hash, sizeof(hash), \"%s\", qtv_password.string);\n\t\t\tbreak;\n\t\tcase 15:\t//fucked encoding(missing some leading 0s)\n\t\t\tQ_snprintfz(hash, sizeof(hash), \"%s%s\", p->challenge, qtv_password.string);\n\t\t\tCalcHash(hashes[authmethod].func, digest, sizeof(digest), hash, strlen(hash));\n\t\t\tQ_snprintfz(hash, sizeof(hash), \"%X%X%X%X\", ((quint32_t*)digest)[0], ((quint32_t*)digest)[1], ((quint32_t*)digest)[2], ((quint32_t*)digest)[3]);\n\t\t\tbreak;\n\t\tcase 16:\n\t\t\tQ_snprintfz(hash, sizeof(hash), \"%s%s\", p->challenge, qtv_password.string);\n\t\t\tCalcHash(hashes[authmethod].func, digest, sizeof(digest), hash, strlen(hash));\n\t\t\tBase16_EncodeBlock(digest, hashes[authmethod].func->digestsize, hash, sizeof(hash));\n\t\t\tbreak;\n\t\tcase 64:\n\t\t\tQ_snprintfz(hash, sizeof(hash), \"%s%s\", p->challenge, qtv_password.string);\n\t\t\tCalcHash(hashes[authmethod].func, digest, sizeof(digest), hash, strlen(hash));\n\t\t\tBase64_EncodeBlock(digest, hashes[authmethod].func->digestsize, hash, sizeof(hash));\n\t\t\tbreak;\n\t\t}\n\t\tp->hasauthed = !strcmp(password, hash);\n\t\tif (!p->hasauthed && !e)\n\t\t{\n\t\t\tif (raw)\n\t\t\t\te = \"\";\n\t\t\telse\n\t\t\t\te =\t(\"QTVSV 1\\n\"\n\t\t\t\t\t \"PERROR: Bad password.\\n\\n\");\n\t\t}\n\t}\n\telse\n\t{\n\t\t//no password, and not automagically authed\n\t\tswitch (hashes[authmethod].base)\n\t\t{\n\t\tcase -1:\n\t\t\tif (raw)\n\t\t\t\te = \"\";\n\t\t\telse\n\t\t\t\te = (\"QTVSV 1\\n\"\n\t\t\t\t\t \"PERROR: You need to provide a common auth method.\\n\\n\");\n\t\t\tbreak;\n\t\tcase 0:\n\t\t\tp->hasauthed = !strcmp(qtv_password.string, password);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\t{\n\t\t\t\tchar tmp[32];\n\t\t\t\tSys_RandomBytes(tmp, sizeof(tmp));\n\t\t\t\tBase64_EncodeBlock(tmp, sizeof(tmp), p->challenge, sizeof(p->challenge));\n\t\t\t}\n\n\t\t\te = va(\"QTVSV 1\\n\"\n\t\t\t\t\"AUTH: %s\\n\"\n\t\t\t\t\"CHALLENGE: %s\\n\\n\",\n\t\t\t\t\thashes[authmethod].name, p->challenge);\n\t\t\tVFS_WRITE(clientstream, e, strlen(e));\n\t\t\treturn QTV_RETRY;\n\t\t}\n\t}\n\n\tif (*qtv_maxstreams.string && !p->isreverse)\n\t{\n\t\tint count = 0;\n\t\tmvddest_t *dest;\n\t\tfor (dest = demo.dest; dest; dest = dest->nextdest)\n\t\t{\n\t\t\tif (dest->desttype == DEST_STREAM)\n\t\t\t\tcount++;\n\t\t}\n\n\t\tif (count >= qtv_maxstreams.value) //sorry\n\t\t{\n\t\t\tif (!qtv_maxstreams.value)\n\t\t\t\te = \"QTVSV 1\\nTERROR: QTV streaming from this server is blocked by qtv_maxstreams.\\n\\n\";\n\t\t\telse\n\t\t\t\te = \"QTVSV 1\\nTERROR: This server enforces a limit on the number of proxies connected at any one time. Please try again later.\\n\\n\";\n\t\t}\n\t}\n\n\tif (e)\n\t{\n\t}\n\telse if (!versiontouse)\n\t{\n\t\te =\t(\"QTVSV 1\\n\"\n\t\t\t \"PERROR: Incompatible version (valid version is v1)\\n\\n\");\n\t}\n\telse if (raw)\n\t{\n\t\tif (p->hasauthed == true)\n\t\t{\n\t\t\tif (!SV_MVD_Record(SV_MVD_InitStream(clientstream, userinfo)))\n\t\t\t\treturn QTV_ERROR;\n\t\t\treturn QTV_ACCEPT;\n\t\t}\n\t}\n\telse\n\t{\n\t\tif (p->hasauthed == true)\n\t\t{\n\t\t\tmvddest_t *dst;\n\t\t\te =\t(\"QTVSV 1\\n\"\n\t\t\t\t \"BEGIN\\n\"\n\t\t\t\t \"\\n\");\n\t\t\tVFS_WRITE(clientstream, e, strlen(e));\n\t\t\te = NULL;\n\t\t\tdst = SV_MVD_InitStream(clientstream, userinfo);\n\t\t\tdst->droponmapchange = p->isreverse;\n\t\t\tif (!SV_MVD_Record(dst))\n\t\t\t\treturn QTV_ERROR;\n\t\t\treturn QTV_ACCEPT;\n\t\t}\n\t\telse\n\t\t{\n\t\t\te =\t(\"QTVSV 1\\n\"\n\t\t\t\t\"PERROR: You need to provide a password.\\n\\n\");\n\t\t}\n\t}\n\n\tif (e && !raw)\t//don't write any error messages to raw requests. that would confuse stuff.\n\t\tVFS_WRITE(clientstream, e, strlen(e));\n\treturn QTV_ERROR;\n}\n\nstatic int DestCloseAllFlush(enum mvdclosereason_e reason, qboolean mvdonly)\n{\n\tint numclosed = 0;\n\tmvddest_t *d, **prev, *next;\n\tDestFlush(true);\t//make sure it's all written.\n\n\tprev = &demo.dest;\n\td = demo.dest;\n\twhile(d)\n\t{\n\t\tnext = d->nextdest;\n\t\tif (!mvdonly || d->droponmapchange)\n\t\t{\n\t\t\t*prev = d->nextdest;\n\t\t\tDestClose(d, reason);\n\t\t\tnumclosed++;\n\t\t}\n\t\telse\n\t\t\tprev = &d->nextdest;\n\n\t\td = next;\n\t}\n\n\treturn numclosed;\n}\n\n\nstatic int DemoWriteDest(void *data, int len, mvddest_t *d)\n{\n\tif (d->error)\n\t\treturn 0;\n\td->totalsize += len;\n\tswitch(d->desttype)\n\t{\n\tcase DEST_FILE:\n\t\tVFS_WRITE(d->file, data, len);\n\t\tbreak;\n\tcase DEST_BUFFEREDFILE:\t//these write to a cache, which is flushed later\n\tcase DEST_THREADEDFILE:\n\tcase DEST_STREAM:\n\t\tif (d->cacheused+len > d->maxcachesize)\n\t\t{\n\t\t\td->error = true;\n\t\t\treturn 0;\n\t\t}\n\t\tmemcpy(d->cache+d->cacheused, data, len);\n\t\td->cacheused += len;\n\t\tbreak;\n\tdefault:\n\tcase DEST_NONE:\n\t\tSys_Error(\"DemoWriteDest encoundered bad dest.\");\n\t}\n\treturn len;\n}\n\nstatic int DemoWrite(void *data, int len)\t//broadcast to all proxies/mvds\n{\n\tmvddest_t *d;\n\tfor (d = demo.dest; d; d = d->nextdest)\n\t{\n\t\tif (singledest && singledest != d)\n\t\t\tcontinue;\n\t\tDemoWriteDest(data, len, d);\n\t}\n\treturn len;\n}\n\nvoid DemoWriteQTVTimePad(int msecs)\t//broadcast to all proxies\n{\n\tmvddest_t *d;\n\tunsigned char buffer[6];\n\twhile (msecs > 0)\n\t{\n\t\t//duration\n\t\tif (msecs > 255)\n\t\t\tbuffer[0] = 255;\n\t\telse\n\t\t\tbuffer[0] = msecs;\n\t\tmsecs -= buffer[0];\n\t\t//message type\n\t\tbuffer[1] = dem_read;\n\t\t//length\n\t\tbuffer[2] = 0;\n\t\tbuffer[3] = 0;\n\t\tbuffer[4] = 0;\n\t\tbuffer[5] = 0;\n\n\t\tfor (d = demo.dest; d; d = d->nextdest)\n\t\t{\n\t\t\tif (d->desttype == DEST_STREAM)\n\t\t\t{\n\t\t\t\tDemoWriteDest(buffer, sizeof(buffer), d);\n\t\t\t}\n\t\t}\n\t}\n}\n\n\n\n\n// returns the file size\n// return -1 if file is not present\n// the file should be in BINARY mode for stupid OSs that care\n#define MAX_MVD_NAME 64\n\ntypedef struct\n{\n\tchar\tname[MAX_MVD_NAME];\n\tqofs_t\tsize;\n\ttime_t\tmtime;\n\tsearchpathfuncs_t *path;\n} file_t;\n\ntypedef struct\n{\n\tfile_t *files;\n\tqofs_t\tsize;\n\tint\t\tnumfiles;\n\tint\t\tnumdirs;\n\n\tint\t\tmaxfiles;\n} dir_t;\n\n#define SORT_NO 0\n#define SORT_BY_DATE 1\n\nstatic int QDECL Sys_listdirFound(const char *fname, qofs_t fsize, time_t mtime, void *uptr, searchpathfuncs_t *spath)\n{\n\tfile_t *f;\n\tdir_t *dir = uptr;\n\tfname = COM_SkipPath(fname);\n\tif (!*fname)\n\t{\n\t\tdir->numdirs++;\n\t\treturn true;\n\t}\n\tif (dir->numfiles == dir->maxfiles)\n\t{\n\t\tint nc = dir->numfiles + 256;\n\t\tfile_t *n = realloc(dir->files, nc*sizeof(*dir->files));\n\t\tif (!n)\n\t\t\treturn false;\n\t\tdir->files = n;\n\t\tdir->maxfiles = nc;\n\t}\n\tf = &dir->files[dir->numfiles++];\n\tQ_strncpyz(f->name, fname, sizeof(f->name));\n\tf->size = fsize;\n\tf->mtime = mtime;\n\tf->path = spath;\n\tdir->size += fsize;\n\n\treturn true;\n}\n\nstatic int QDECL Sys_listdir_Sort(const void *va, const void *vb)\n{\n\tconst file_t *fa = va;\n\tconst file_t *fb = vb;\n\n\tif (fa->mtime == fb->mtime)\n\t\treturn 0;\n\tif (fa->mtime >= fb->mtime)\n\t\treturn 1;\n\treturn -1;\n}\n\nstatic dir_t *Sys_listdemos (char *path, int ispublic, qboolean usesorting)\n{\n\tconst char *exts[] = {\n\t\t\".mvd\", \".mvd.gz\",\n\t\t\".qwd\", \".qwd.gz\",\n\t\t\".qwz\", \".qwz.gz\",\t//our client doesn't support them, but others do any they still might want to download them if there's any there...\n#ifdef NQPROT\n\t\t\".dem\", \".dem.gz\",\n\t\t//don't bother with .dz, that's more of an archive format (and should at least show up with .dem files)\n#endif\n#if defined(Q2SERVER) || defined(Q2CLIENT)\n\t\t\".dm2\", \".dm2.gz\"\n#endif\n\t};\n\tchar searchterm[MAX_QPATH];\n\tsize_t i;\n\n\tdir_t *dir = malloc(sizeof(*dir));\n\tmemset(dir, 0, sizeof(*dir));\n\tdir->files = NULL;\n\tdir->maxfiles = 0;\n\n\tfor (i = 0; i < (ispublic?2:countof(exts)); i++)\n\t{\n\t\tQ_strncpyz(searchterm, va(\"%s/*%s\", path, exts[i]), sizeof(searchterm));\n\t\tCOM_EnumerateFiles(searchterm, Sys_listdirFound, dir);\n\t}\n\n\tif (usesorting)\n\t\tqsort(dir->files, dir->numfiles, sizeof(*dir->files), Sys_listdir_Sort);\n\n\treturn dir;\n}\nstatic void Sys_freedir(dir_t *dir)\n{\n\tif (dir)\n\t\tfree(dir->files);\n\tfree(dir);\n}\n\n\n\n\n\n\n\n\n\n// only one .. is allowed (so we can get to the same dir as the quake exe)\nstatic void QDECL SV_DemoDir_Callback(struct cvar_s *var, char *oldvalue)\n{\n\tchar *value;\n\n\tvalue = var->string;\n\tif (!value[0] || value[0] == '/' || (value[0] == '\\\\' && value[1] == '\\\\'))\n\t{\n\t\tCvar_ForceSet(var, var->enginevalue);\n\t\treturn;\n\t}\n\tif (value[0] == '.' && value[1] == '.')\n\t\tvalue += 2;\n\tif (strstr(value,\"..\"))\n\t{\n\t\tCvar_ForceSet(var, var->enginevalue);\n\t\treturn;\n\t}\n}\n\nvoid SV_MVDPings (void)\n{\n\tsizebuf_t *msg;\n\tclient_t *client;\n\tint\t\tj;\n\n\tfor (j = 0, client = svs.clients; j < demo.recorder.max_net_clients && j < svs.allocated_client_slots; j++, client++)\n\t{\n\t\tif (client->state != cs_spawned)\n\t\t\tcontinue;\n\n\t\tmsg = MVDWrite_Begin (dem_all, 0, 7);\n\t\tMSG_WriteByte(msg, svc_updateping);\n\t\tMSG_WriteByte(msg,  j);\n\t\tMSG_WriteShort(msg,  SV_CalcPing(client, false));\n\t\tMSG_WriteByte(msg, svc_updatepl);\n\t\tMSG_WriteByte (msg, j);\n\t\tMSG_WriteByte (msg, client->lossage);\n\t}\n}\nvoid SV_MVD_FullClientUpdate(sizebuf_t *msg, client_t *player)\n{\n\tchar info[EXTENDED_INFO_STRING];\n\tqboolean dosizes;\n\n\tif (!sv.mvdrecording)\n\t\treturn;\n\n\tdosizes = !msg;\n\n\tif (dosizes)\n\t\tmsg = MVDWrite_Begin (dem_all, 0, 4);\n\tMSG_WriteByte (msg, svc_updatefrags);\n\tMSG_WriteByte (msg, player - svs.clients);\n\tMSG_WriteShort (msg, player->old_frags);\n\n\tif (dosizes)\n\t\tmsg = MVDWrite_Begin (dem_all, 0, 4);\n\tMSG_WriteByte (msg, svc_updateping);\n\tMSG_WriteByte (msg, player - svs.clients);\n\tMSG_WriteShort (msg, SV_CalcPing(player, false)&0xffff);\n\n\tif (dosizes)\n\t\tmsg = MVDWrite_Begin (dem_all, 0, 3);\n\tMSG_WriteByte (msg, svc_updatepl);\n\tMSG_WriteByte (msg, player - svs.clients);\n\tMSG_WriteByte (msg, player->lossage);\n\n\tif (dosizes)\n\t\tmsg = MVDWrite_Begin (dem_all, 0, 6);\n\tMSG_WriteByte (msg, svc_updateentertime);\n\tMSG_WriteByte (msg, player - svs.clients);\n\tMSG_WriteFloat (msg, realtime - player->connection_started);\n\n\tInfoBuf_ToString(&player->userinfo, info, sizeof(info), basicuserinfos, privateuserinfos, NULL, &demo.recorder.infosync, player);\n\n\tif (dosizes)\n\t\tmsg = MVDWrite_Begin (dem_all, 0, 7 + strlen(info));\n\tMSG_WriteByte (msg, svc_updateuserinfo);\n\tMSG_WriteByte (msg, player - svs.clients);\n\tMSG_WriteLong (msg, player->userid);\n\tMSG_WriteString (msg, info);\n}\n\nsizebuf_t *MVDWrite_Begin(qbyte type, int to, int size)\n{\n\tif (demomsg.cursize && (demomsgtype != type || demomsgto != to || demomsg.cursize+size > sizeof(demomsgbuf)))\n\t{\n\t\tSV_WriteMVDMessage(&demomsg, demomsgtype, demomsgto, demo_prevtime);\n\t\tdemomsg.cursize = 0;\n\t}\n\n\tdemomsgtype = type;\n\tdemomsgto = to;\n\n\tdemomsg.maxsize = demomsg.cursize+size;\n\tdemomsg.data = demomsgbuf;\n\tdemomsg.prim = demo.recorder.netchan.netprim;\n\treturn &demomsg;\n}\n\n/*\n====================\nSV_WriteMVDMessage\n\nDumps the current net message, along with framing\n====================\n*/\nvoid SV_WriteMVDMessage (sizebuf_t *msg, int type, int to, float time)\n{\n\tint\t\tlen, i, msec;\n\tqbyte\tc;\n\n\tif (!sv.mvdrecording)\n\t\treturn;\n\n\tif (msg->overflowed)\n\t{\n\t\tmsg->overflowed = false;\n\t\tCon_Printf(\"SV_WriteMVDMessage: message overflowed\\n\");\n\t\treturn;\n\t}\n\n\tmsec = (time - demo_prevtime)*1000;\n\tif (abs(msec) > 1000)\n\t{\n\t\t//catastoptic slip. debugging? reset any sync\n\t\tmsec = 1;\n\t\tdemo_prevtime = time;\n\t}\n\telse if (msec > 0)\n\t{\t//if there was any progress, make sure we write msecs >0\n\t\tif (msec > 255)\n\t\t\tmsec = 255;\n\t\tif (msec < 1)\n\t\t\tmsec = 1;\n\t\tdemo_prevtime += msec*0.001;\n\t}\n\telse\n\t\tmsec = 0;\n\n\tc = msec;\n\tDemoWrite(&c, sizeof(c));\n\n\tif (demo.lasttype != type || demo.lastto != to)\n\t{\n\t\tdemo.lasttype = type;\n\t\tdemo.lastto = to;\n\t\tswitch (demo.lasttype)\n\t\t{\n\t\tcase dem_all:\n\t\t\tc = dem_all;\n\t\t\tDemoWrite (&c, sizeof(c));\n\t\t\tbreak;\n\t\tcase dem_multiple:\n\t\t\tc = dem_multiple;\n\t\t\tDemoWrite (&c, sizeof(c));\n\n\t\t\ti = LittleLong(demo.lastto);\n\t\t\tDemoWrite (&i, sizeof(i));\n\t\t\tbreak;\n\t\tcase dem_single:\n\t\tcase dem_stats:\n\t\t\tc = demo.lasttype + (demo.lastto << 3);\n\t\t\tDemoWrite (&c, sizeof(c));\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tSV_MVDStop_f ();\n\t\t\tCon_Printf(\"bad demo message type:%d\", type);\n\t\t\treturn;\n\t\t}\n\t} else {\n\t\tc = dem_read;\n\t\tDemoWrite (&c, sizeof(c));\n\t}\n\n\n\tlen = LittleLong (msg->cursize);\n\tDemoWrite (&len, 4);\n\tDemoWrite (msg->data, msg->cursize);\n\n\tDestFlush(false);\n}\n\n//if you use ClientReliable to write to demo.recorder's message buffer (for code reuse) call this function to ensure its flushed.\nvoid SV_MVD_WriteReliables(qboolean writebroadcasts)\n{\n\tint i;\n\n\tif (writebroadcasts)\n\t{\n\t\t//chuck in the broadcast reliables\n\t\tif (sv.reliable_datagram.cursize)\n\t\t{\n\t\t\tClientReliableCheckBlock(&demo.recorder, sv.reliable_datagram.cursize);\n\t\t\tClientReliableWrite_SZ(&demo.recorder, sv.reliable_datagram.data, sv.reliable_datagram.cursize);\n\t\t}\n\t\t//and the broadcast unreliables. everything is reliables when it comes to mvds\n\t\tif (sv.datagram.cursize)\n\t\t{\n\t\t\tClientReliableCheckBlock(&demo.recorder, sv.datagram.cursize);\n\t\t\tClientReliableWrite_SZ(&demo.recorder, sv.datagram.data, sv.datagram.cursize);\n\t\t}\n\t}\n\n\tif (demo.recorder.netchan.message.cursize)\n\t{\n\t\tSV_WriteMVDMessage(&demo.recorder.netchan.message, dem_all, 0, demo_prevtime);\n\t\tdemo.recorder.netchan.message.cursize = 0;\n\t}\n\tfor (i = 0; i < demo.recorder.num_backbuf; i++)\n\t{\n\t\tdemo.recorder.backbuf.data = demo.recorder.backbuf_data[i];\n\t\tdemo.recorder.backbuf.cursize = demo.recorder.backbuf_size[i];\n\t\tif (demo.recorder.backbuf.cursize)\n\t\t\tSV_WriteMVDMessage(&demo.recorder.backbuf, dem_all, 0, demo_prevtime);\n\t\tdemo.recorder.backbuf_size[i] = 0;\n\t}\n\tdemo.recorder.num_backbuf = 0;\n\tdemo.recorder.backbuf.cursize = 0;\n}\n\n/*\n====================\nSV_MVDWritePackets\n\nInterpolates to get exact players position for current frame\nand writes packets to the disk/memory\n====================\n*/\n\nfloat adjustangle(float current, float ideal, float fraction)\n{\n\tfloat move;\n\n\tmove = ideal - current;\n\tif (ideal > current)\n\t{\n\n\t\tif (move >= 180)\n\t\t\tmove = move - 360;\n\t}\n\telse\n\t{\n\t\tif (move <= -180)\n\t\t\tmove = move + 360;\n\t}\n\n\tmove *= fraction;\n\n\treturn (current + move);\n}\n\nqboolean SV_MVDWritePackets (int num)\n{\n\tdemo_frame_t\t*frame, *nextframe;\n\tdemo_client_t\t*cl, *nextcl = NULL;\n\tint\t\t\t\ti, j, flags;\n\tqboolean\t\tvalid;\n\tdouble\t\t\ttime, playertime, nexttime;\n\tfloat\t\t\tf;\n\tvec3_t\t\t\torigin, angles;\n\tsizebuf_t\t\tmsg;\n\tqbyte\t\t\tmsg_buf[MAX_QWMSGLEN];\n\tdemoinfo_t\t\t*demoinfo;\n\n\tif (!sv.mvdrecording)\n\t\treturn false;\n\tif (demo.recorder.fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS)\n\t\treturn false;\n\n\t//flush any intermediate data\n\tMVDWrite_Begin(255, -1, 0);\n\n\tmsg.allowoverflow = true;\t//fixme\n\tmsg.overflowed = false;\n\tmsg.prim = svs.netprim;\n\tmsg.data = msg_buf;\n\tmsg.maxsize = sizeof(msg_buf);\n\n\tif (num > demo.parsecount - demo.lastwritten + 1)\n\t\tnum = demo.parsecount - demo.lastwritten + 1;\n\n\t// 'num' frames to write\n\tfor ( ; num; num--, demo.lastwritten++)\n\t{\n\t\tframe = &demo.frames[demo.lastwritten&DEMO_FRAMES_MASK];\n\t\ttime = frame->time;\n\t\tnextframe = frame;\n\t\tmsg.cursize = 0;\n\n\t\t// find two frames\n\t\t// one before the exact time (time - msec) and one after,\n\t\t// then we can interpolte exact position for current frame\n\t\tfor (i = 0, cl = frame->clients, demoinfo = demo.info; i < demo.recorder.max_net_clients ; i++, cl++, demoinfo++)\n\t\t{\n\t\t\tif (cl->parsecount != demo.lastwritten)\n\t\t\t\tcontinue; // not valid\n\n\t\t\tnexttime = playertime = time - cl->sec;\n\n\t\t\tfor (j = demo.lastwritten+1, valid = false; nexttime < time && j < demo.parsecount; j++)\n\t\t\t{\n\t\t\t\tnextframe = &demo.frames[j&DEMO_FRAMES_MASK];\n\t\t\t\tnextcl = &nextframe->clients[i];\n\n\t\t\t\tif (nextcl->parsecount != j)\n\t\t\t\t\tbreak; // disconnected?\n\t\t\t\tif (nextcl->fixangle)\n\t\t\t\t\tbreak; // respawned, or walked into teleport, do not interpolate!\n\t\t\t\tif (!(nextcl->flags & DF_DEAD) && (cl->flags & DF_DEAD))\n\t\t\t\t\tbreak; // respawned, do not interpolate\n\n\t\t\t\tnexttime = nextframe->time - nextcl->sec;\n\n\t\t\t\tif (nexttime >= time)\n\t\t\t\t{\n\t\t\t\t\t// good, found what we were looking for\n\t\t\t\t\tvalid = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (valid)\n\t\t\t{\n\t\t\t\tf = (time - nexttime)/(nexttime - playertime);\n\t\t\t\tfor (j=0;j<3;j++) {\n\t\t\t\t\tangles[j] = adjustangle(cl->info.angles[j], nextcl->info.angles[j],1.0+f);\n\t\t\t\t\torigin[j] = nextcl->info.origin[j] + f*(nextcl->info.origin[j]-cl->info.origin[j]);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tVectorCopy(cl->info.origin, origin);\n\t\t\t\tVectorCopy(cl->info.angles, angles);\n\t\t\t}\n\n\t\t\t// now write it to buf\n\t\t\tflags = cl->flags;\t//df_dead/df_gib\n\n\t\t\tif (demo.playerreset[i])\n\t\t\t{\n\t\t\t\tdemo.playerreset[i] = false;\n\t\t\t\tflags |= DF_RESET;\n\t\t\t}\n\n\t\t\tif (cl->fixangle)\n\t\t\t{\n\t\t\t\tdemo.fixangletime[i] = cl->cmdtime;\n\t\t\t}\n\n\t\t\tfor (j=0; j < 3; j++)\n\t\t\t\tif (origin[j] != demoinfo->origin[i])\n\t\t\t\t\tflags |= DF_ORIGINX << j;\n\n\t\t\tif (cl->fixangle || demo.fixangletime[i] != cl->cmdtime)\n\t\t\t{\n\t\t\t\tfor (j=0; j < 3; j++)\n\t\t\t\t\tif (angles[j] != demoinfo->angles[j])\n\t\t\t\t\t\tflags |= DF_ANGLEX << j;\n\t\t\t}\n\n\t\t\tif (cl->info.model != demoinfo->model)\n\t\t\t\tflags |= DF_MODEL;\n\t\t\tif (cl->info.effects != demoinfo->effects)\n\t\t\t\tflags |= DF_EFFECTS;\n\t\t\tif (cl->info.skinnum != demoinfo->skinnum)\n\t\t\t\tflags |= DF_SKINNUM;\n\t\t\tif (cl->info.weaponframe != demoinfo->weaponframe)\n\t\t\t\tflags |= DF_WEAPONFRAME;\n\n\t\t\tMSG_WriteByte (&msg, svc_playerinfo);\n\t\t\tMSG_WriteByte (&msg, i);\n\t\t\tMSG_WriteShort (&msg, flags);\n\n\t\t\tMSG_WriteByte (&msg, cl->frame);\n\n\t\t\tfor (j=0 ; j<3 ; j++)\n\t\t\t\tif (flags & (DF_ORIGINX << j))\n\t\t\t\t\tMSG_WriteCoord (&msg, origin[j]);\n\n\t\t\tfor (j=0 ; j<3 ; j++)\n\t\t\t\tif (flags & (DF_ANGLEX << j))\n\t\t\t\t\tMSG_WriteAngle16 (&msg, angles[j]);\n\n\n\t\t\tif (flags & DF_MODEL)\n\t\t\t\tMSG_WriteByte (&msg, cl->info.model);\n\n\t\t\tif (flags & DF_SKINNUM)\n\t\t\t\tMSG_WriteByte (&msg, cl->info.skinnum);\n\n\t\t\tif (flags & DF_EFFECTS)\n\t\t\t\tMSG_WriteByte (&msg, cl->info.effects & 0xff);\n\n\t\t\tif (flags & DF_WEAPONFRAME)\n\t\t\t\tMSG_WriteByte (&msg, cl->info.weaponframe);\n\n\t\t\tVectorCopy(cl->info.origin, demoinfo->origin);\n\t\t\tVectorCopy(cl->info.angles, demoinfo->angles);\n\t\t\tdemoinfo->skinnum = cl->info.skinnum;\n\t\t\tdemoinfo->effects = cl->info.effects;\n\t\t\tdemoinfo->weaponframe = cl->info.weaponframe;\n\t\t\tdemoinfo->model = cl->info.model;\n\t\t}\n\n\t\tif (msg.cursize)\n\t\t\tSV_WriteMVDMessage(&msg, dem_all, 0, (float)time);\n\n\t\t/* The above functions can set this variable to false, but that's a really bad thing. Let's try to fix it. */\n\t\tif (!sv.mvdrecording)\n\t\t\treturn false;\n\t}\n\n\tif (demo.lastwritten > demo.parsecount)\n\t\tdemo.lastwritten = demo.parsecount;\n\n\treturn true;\n}\n\nvoid MVD_Init (void)\n{\n#define MVDVARGROUP \"Server MVD cvars\"\n\n\tCvar_Register (&sv_demofps,\t\t\tMVDVARGROUP);\n\tCvar_Register (&sv_demoPings,\t\tMVDVARGROUP);\n\tCvar_Register (&sv_demoUseCache,\tMVDVARGROUP);\n\tCvar_Register (&sv_demoCacheSize,\tMVDVARGROUP);\n\tCvar_Register (&sv_demoMaxSize,\t\tMVDVARGROUP);\n\tCvar_Register (&sv_demoMaxDirSize,\tMVDVARGROUP);\n\tCvar_Register (&sv_demoMaxDirCount,\tMVDVARGROUP);\n\tCvar_Register (&sv_demoMaxDirAge,\tMVDVARGROUP);\n\tCvar_Register (&sv_demoClearOld,\tMVDVARGROUP);\n\tCvar_Register (&sv_demoDir,\t\t\tMVDVARGROUP);\n\tCvar_Register (&sv_demoDirAlt,\t\tMVDVARGROUP);\n\tCvar_Register (&sv_demoPrefix,\t\tMVDVARGROUP);\n\tCvar_Register (&sv_demoSuffix,\t\tMVDVARGROUP);\n\tCvar_Register (&sv_demotxt,\t\t\tMVDVARGROUP);\n\tCvar_Register (&sv_demoExtraNames,\tMVDVARGROUP);\n\tCvar_Register (&sv_demoExtensions,\tMVDVARGROUP);\n\tCvar_Register (&sv_demoAutoCompress,MVDVARGROUP);\n\tCvar_Register (&sv_demoAutoRecord,\tMVDVARGROUP);\n\tCvar_Register (&sv_demoAutoPrefix,\tMVDVARGROUP);\n\tCvar_Register (&sv_demo_write_csqc,MVDVARGROUP);\n}\n\nstatic char *SV_PrintTeams(void)\n{\n\tchar *teams[MAX_CLIENTS];\n//\tchar *p;\n\tint\ti, j, numcl = 0, numt = 0;\n\tclient_t *clients[MAX_CLIENTS];\n\tchar buf[2048] = {0};\n\textern cvar_t teamplay;\n//\textern char chartbl2[];\n\n\t// count teams and players\n\tfor (i=0; i < sv.allocated_client_slots; i++)\n\t{\n\t\tif (svs.clients[i].state != cs_spawned)\n\t\t\tcontinue;\n\t\tif (svs.clients[i].spectator)\n\t\t\tcontinue;\n\n\t\tclients[numcl++] = &svs.clients[i];\n\t\tfor (j = 0; j < numt; j++)\n\t\t\tif (!strcmp(InfoBuf_ValueForKey(&svs.clients[i].userinfo, \"team\"), teams[j]))\n\t\t\t\tbreak;\n\t\tif (j != numt)\n\t\t\tcontinue;\n\n\t\tteams[numt++] = InfoBuf_ValueForKey(&svs.clients[i].userinfo, \"team\");\n\t}\n\n\t// create output\n\n\tif (numcl == 2) // duel\n\t{\n\t\tsnprintf(buf, sizeof(buf), \"team1 %s\\nteam2 %s\\n\", clients[0]->name, clients[1]->name);\n\t}\n\telse if (!teamplay.value) // ffa\n\t{\n\t\tsnprintf(buf, sizeof(buf), \"players:\\n\");\n\t\tfor (i = 0; i < numcl; i++)\n\t\t\tsnprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), \"  %s\\n\", clients[i]->name);\n\t}\n\telse\n\t{ // teamplay\n\t\tfor (j = 0; j < numt; j++)\n\t\t{\n\t\t\tsnprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), \"team %s:\\n\", teams[j]);\n\t\t\tfor (i = 0; i < numcl; i++)\n\t\t\t\tif (!strcmp(InfoBuf_ValueForKey(&clients[i]->userinfo, \"team\"), teams[j]))\n\t\t\t\t\tsnprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), \"  %s\\n\", clients[i]->name);\n\t\t}\n\t}\n\n\tif (!numcl)\n\t\treturn \"\\n\";\n//\tfor (p = buf; *p; p++) *p = chartbl2[(qbyte)*p];\n\treturn va(\"%s\",buf);\n}\n\n\nmvddest_t *SV_FindRecordFile(char *match, mvddest_t ***link_out)\n{\n\tmvddest_t **link, *f;\n\tfor (link = &demo.dest; *link; link = &(*link)->nextdest)\n\t{\n\t\tf = *link;\n\t\tif (f->desttype == DEST_FILE || f->desttype == DEST_BUFFEREDFILE || f->desttype == DEST_THREADEDFILE)\n\t\t{\n\t\t\tif (!match || !strcmp(match, f->simplename))\n\t\t\t{\n\t\t\t\tif (link_out)\n\t\t\t\t\t*link_out = link;\n\t\t\t\treturn f;\n\t\t\t}\n\t\t}\n\t}\n\treturn NULL;\n}\n\n/*\n====================\nSV_InitRecord\n====================\n*/\n\nmvddest_t *SV_MVD_InitRecordFile (char *name)\n{\n\tchar *s, *txtname;\n\tmvddest_t *dst;\n\tvfsfile_t *file;\n\n\tif (strlen(name) >= countof(dst->filename))\n\t{\n\t\tCon_Printf (\"ERROR: couldn't open \\\"%s\\\". Too long.\\n\", name);\n\t\treturn NULL;\n\t}\n\n\tfile = FS_OpenVFS (name, \"wb\", FS_GAMEONLY);\n\tif (!file)\n\t{\n\t\tCon_Printf (\"ERROR: couldn't open \\\"%s\\\"\\n\", name);\n\t\treturn NULL;\n\t}\n\n#ifdef AVAIL_GZDEC\n\tif (!Q_strcasecmp(\".gz\", COM_GetFileExtension(name, NULL)))\n\t\tfile = FS_GZ_WriteFilter(file, true, true);\n#endif\n\n\tdst = Z_Malloc(sizeof(mvddest_t));\n\tstrcpy(dst->filename, name);\n\n#ifdef LOADERTHREAD\n\tif (!*sv_demoUseCache.string)\n\t\tdst->desttype = DEST_THREADEDFILE;\n\telse\n#endif\n\t\tif (sv_demoUseCache.value <= 0)\n\t\tdst->desttype = DEST_FILE;\n\telse\n\t\tdst->desttype = DEST_BUFFEREDFILE;\n\n\tif (dst->desttype == DEST_FILE)\n\t{\n\t\tdst->desttype = DEST_FILE;\n\t\tdst->file = file;\n\t\tdst->maxcachesize = 0;\n\t}\n\telse\n\t{\t//cached or threaded\n\t\tdst->file = file;\n\t\tif (sv_demoCacheSize.ival < 0x8000)\n\t\t\tdst->maxcachesize = 0x8000;\n\t\telse\n\t\t\tdst->maxcachesize = sv_demoCacheSize.ival;\n\t\tdst->cache = BZ_Malloc(dst->maxcachesize);\n\t\tif (dst->desttype == DEST_THREADEDFILE)\n\t\t\tdst->altcache = BZ_Malloc(dst->maxcachesize);\n\t\telse\n\t\t\tdst->altcache = NULL;\n\t}\n\tdst->droponmapchange = true;\n\n\ts = name + strlen(name);\n\twhile (*s != '/') s--;\n\tQ_strncpyz(dst->simplename, s+1, sizeof(dst->simplename));\n\n\tswitch(dst->desttype)\n\t{\n\tdefault:\n\tcase DEST_NONE:\n\t\tSV_BroadcastPrintf (PRINT_CHAT, \"Server starts recording (%s):\\n%s\\n\", \"ERROR\", name);\n\t\tbreak;\n\tcase DEST_STREAM:\n\t\tSV_BroadcastPrintf (PRINT_CHAT, \"Server starts recording (%s):\\n%s\\n\", \"ERROR: STREAM\", name);\n\t\tbreak;\n\tcase DEST_BUFFEREDFILE:\n\t\tSV_BroadcastPrintf (PRINT_CHAT, \"Server starts recording (%s):\\n%s\\n\", \"memory\", name);\n\t\tbreak;\n\tcase DEST_THREADEDFILE:\n\t\t//SV_BroadcastPrintf (PRINT_CHAT, \"Server starts recording (%s):\\n%s\\n\", \"worker thread\", name);\n\t\tSV_BroadcastPrintf (PRINT_CHAT, \"Server starts recording:\\n%s\\n\", name);\n\t\tbreak;\n\tcase DEST_FILE:\n\t\tSV_BroadcastPrintf (PRINT_CHAT, \"Server starts recording (%s):\\n%s\\n\", \"disk\", name);\n\t\tbreak;\n\t}\n\n\ttxtname = SV_MVDName2Txt(name);\n\tif (sv_demotxt.value)\n\t{\n\t\tvfsfile_t *f;\n\n\t\tif (sv_demotxt.value == 2)\n\t\t{\n\t\t\t//this is a special mode for mods that want to write it instead (done via the sv_demoinfoadd command).\n\t\t\tf = FS_OpenVFS (txtname, \"wt\", FS_GAMEONLY);\n\t\t\tif (f)\n\t\t\t\tVFS_CLOSE(f);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tf = FS_OpenVFS (txtname, \"wt\", FS_GAMEONLY);\n\t\t\tif (f != NULL)\n\t\t\t{\n\t\t\t\tchar buf[2000];\n\t\t\t\tdate_t date;\n\n\t\t\t\tCOM_TimeOfDay(&date);\n\n\t\t\t\tsnprintf(buf, sizeof(buf), \"date %s\\nmap %s\\nteamplay %d\\ndeathmatch %d\\ntimelimit %d\\n%s\",date.str, svs.name, (int)teamplay.value, (int)deathmatch.value, (int)timelimit.value, SV_PrintTeams());\n\t\t\t\tVFS_WRITE(f, buf, strlen(buf));\n\t\t\t\tVFS_FLUSH(f);\n\t\t\t\tVFS_CLOSE(f);\n\t\t\t}\n\t\t}\n\t}\n\telse\n\t{\n\t\tFS_Remove(txtname, FS_GAMEONLY);\n\t\tFS_FlushFSHashRemoved(txtname);\n\t}\n\n\treturn dst;\n}\n\nchar *SV_Demo_CurrentOutput(void)\n{\n\tmvddest_t *d;\n\tfor (d = demo.dest; d; d = d->nextdest)\n\t{\n\t\tif (d->desttype == DEST_FILE || d->desttype == DEST_BUFFEREDFILE || d->desttype == DEST_THREADEDFILE)\n\t\t\treturn d->simplename;\n\t}\n\treturn \"\";\n}\nvoid SV_Demo_PrintOutputs(void)\n{\n\tmvddest_t *d;\n\tfor (d = demo.dest; d; d = d->nextdest)\n\t{\n\t\tif (d->desttype == DEST_FILE || d->desttype == DEST_BUFFEREDFILE || d->desttype == DEST_THREADEDFILE)\n\t\t\tCon_Printf(\"recording        : %s\\n\", d->simplename);\n\t\telse if (d->desttype == DEST_STREAM)\n\t\t\tCon_Printf(\"streaming        : %s\\n\", d->simplename);\n\t}\n}\n\nstatic mvddest_t *SV_MVD_InitStream(vfsfile_t *stream, const char *info)\n{\n\tmvddest_t *dst;\n\n\tfor (dst = demo.dest; dst; dst = dst->nextdest)\n\t{\n\t\tif (dst->desttype == DEST_STREAM)\n\t\t\tbreak;\n\t}\n\tif (!dst)\n\t\tSV_BroadcastPrintf (PRINT_CHAT, \"Smile, you're on QTV!\\n\");\n\n\tdst = Z_Malloc(sizeof(mvddest_t));\n\n\tdst->desttype = DEST_STREAM;\n\tdst->file = stream;\n\tdst->maxcachesize = 0x8000;\t//is this too small?\n\tdst->cache = BZ_Malloc(dst->maxcachesize);\n\tdst->droponmapchange = false;\n\t*dst->filename = 0;\n\t*dst->simplename = 0;\n\n\tif (info)\n\t{\n\t\tchar *s = Info_ValueForKey(info, \"name\");\n\t\tQ_strncpyz(dst->simplename, s, sizeof(dst->simplename));\n\n\t\ts = Info_ValueForKey(info, \"streamid\");\n\t\tQ_strncpyz(dst->filename, s, sizeof(dst->filename));\n\t\ts = Info_ValueForKey(info, \"address\");\n\t\tif (*dst->filename && *s)\n\t\t\tQ_strncatz(dst->filename, \"@\", sizeof(dst->filename));\n\t\tQ_strncatz(dst->filename, s, sizeof(dst->filename));\n\t}\n\n\treturn dst;\n}\n\n/*\n====================\nSV_Stop\n\nstop recording a demo\n====================\n*/\nvoid SV_MVDStop (enum mvdclosereason_e reason, qboolean mvdonly)\n{\n\tsizebuf_t *msg;\n\tif (!sv.mvdrecording)\n\t{\n\t\tCon_Printf (\"Not recording a demo.\\n\");\n\t\treturn;\n\t}\n\n\tif (reason == MVD_CLOSE_CANCEL || reason == MVD_CLOSE_DISCONNECTED)\n\t{\n\t\tDestCloseAllFlush(reason, mvdonly);\n\t\t// stop and remove\n\n\t\tif (!demo.dest)\n\t\t\tSV_MVD_Stopped();\n\n\t\tif (reason == MVD_CLOSE_DISCONNECTED)\n\t\t\tSV_BroadcastPrintf (PRINT_CHAT, \"QTV disconnected\\n\");\n\t\telse\n\t\t\tSV_BroadcastPrintf (PRINT_CHAT, \"Server recording canceled, demo removed\\n\");\n\n\t\tCvar_ForceSet(Cvar_Get(\"serverdemo\", \"\", CVAR_NOSET, \"\"), \"\");\n\n\t\treturn;\n\t}\n\n// write a disconnect message to the demo file\n\tmsg = MVDWrite_Begin(dem_all, 0, 2+strlen(\"EndOfDemo\"));\n\tMSG_WriteByte (msg, svc_disconnect);\n\tMSG_WriteString (msg, \"EndOfDemo\");\n\n\tSV_MVDWritePackets(demo.parsecount - demo.lastwritten + 1);\n// finish up\n\n\tDestCloseAllFlush(reason, mvdonly);\n\n\tif (!demo.dest)\t//might still be streaming qtv.\n\t\tSV_MVD_Stopped();\n\n\tCvar_ForceSet(Cvar_Get(\"serverdemo\", \"\", CVAR_NOSET, \"\"), \"\");\n}\n\n/*\n====================\nSV_Stop_f\n====================\n*/\nvoid SV_MVDStop_f (void)\n{\n\tSV_MVDStop(MVD_CLOSE_STOPPED, true);\n}\n\n/*\n====================\nSV_Cancel_f\n\nStops recording, and removes the demo\n====================\n*/\nvoid SV_MVD_Cancel_f (void)\n{\n\tSV_MVDStop(MVD_CLOSE_CANCEL, true);\n}\n\n/*\n====================\nSV_WriteMVDMessage\n\nDumps the current net message, prefixed by the length and view angles\n====================\n*/\n\nvoid SV_WriteRecordMVDMessage (sizebuf_t *msg)\n{\n\tint\t\tlen;\n\tqbyte\tc;\n\n\tif (!sv.mvdrecording)\n\t\treturn;\n\n\tif (!msg->cursize)\n\t\treturn;\n\n\tc = 0;\n\tDemoWrite (&c, sizeof(c));\n\n\tc = dem_read;\n\tDemoWrite (&c, sizeof(c));\n\n\tlen = LittleLong (msg->cursize);\n\tDemoWrite (&len, 4);\n\n\tDemoWrite (msg->data, msg->cursize);\n\n\tDestFlush(false);\n}\n\nvoid SV_WriteSetMVDMessage (void)\n{\n\tint\t\tlen;\n\tqbyte\tc;\n\n//Con_Printf(\"write: %ld bytes, %4.4f\\n\", msg->cursize, realtime);\n\n\tif (!sv.mvdrecording)\n\t\treturn;\n\n\tc = 0;\n\tDemoWrite (&c, sizeof(c));\n\n\tc = dem_set;\n\tDemoWrite (&c, sizeof(c));\n\n\n\tlen = LittleLong(0);\n\tDemoWrite (&len, 4);\n\tlen = LittleLong(0);\n\tDemoWrite (&len, 4);\n\n\tDestFlush(false);\n}\n\nvoid SV_MVD_SendInitialGamestate(mvddest_t *dest);\nqboolean SV_MVD_Record (mvddest_t *dest)\n{\n\tstatic int destid;\n\tif (!dest)\n\t\treturn false;\n\n\tdest->id = ++destid;\t//give each stream a unique id, for no real reason other than for other people to track it via SVC_Status(|32).\n\n\tSV_MVD_WriteReliables(false);\n\tDestFlush(true);\n\n\tif (!sv.mvdrecording)\n\t{\n\t\tmemset(&demo, 0, sizeof(demo));\n\t\tdemo.recorder.protocol = SCP_QUAKEWORLD;\n\t\tdemo.recorder.netchan.netprim = sv.datagram.prim;\n\n\t\tdemo.datagram.maxsize = sizeof(demo.datagram_data);\n\t\tdemo.datagram.data = demo.datagram_data;\n\t\tdemo.datagram.prim = demo.recorder.netchan.netprim;\n\n\t\tdemo.recorder.netchan.message.maxsize = sizeof(demo.recorder.netchan.message_buf);\n\t\tdemo.recorder.netchan.message.data = demo.recorder.netchan.message_buf;\n\t\tdemo.recorder.netchan.message.prim = demo.recorder.netchan.netprim;\n\n\t\tif (sv_demoExtensions.ival == 2 || !*sv_demoExtensions.string)\n\t\t{\t/*more limited subset supported by ezquake, but not fuhquake/fodquake. sorry.*/\n\t\t\tdemo.recorder.fteprotocolextensions = /*PEXT_CHUNKEDDOWNLOADS|*/PEXT_256PACKETENTITIES|/*PEXT_FLOATCOORDS|*/PEXT_MODELDBL|PEXT_ENTITYDBL|PEXT_ENTITYDBL2|PEXT_SPAWNSTATIC2;\n//\t\t\tdemo.recorder.fteprotocolextensions |= PEXT_HLBSP;\t/*ezquake DOES have this, but it is pointless and should have been in some feature mask rather than protocol extensions*/\n//\t\t\tdemo.recorder.fteprotocolextensions |= PEXT_ACCURATETIMINGS;\t/*ezquake does not support this any more. pointless in an mvd anyway*/\n\t\t\tdemo.recorder.fteprotocolextensions |= PEXT_TRANS;\t/*ezquake's support for alpha is buggyaf on players, but mvd streams change svc_playerinfo which sidesteps the issue*/\n//\t\t\tdemo.recorder.fteprotocolextensions |= PEXT_COLOURMOD;\t/*nano is working on adding this*/\n//\t\t\tdemo.recorder.fteprotocolextensions |= PEXT_DPFLAGS;\t/*nano is working on adding this*/\n\t\t\tdemo.recorder.fteprotocolextensions2 = PEXT2_VOICECHAT;\n\t\t\tdemo.recorder.zquake_extensions = Z_EXT_PM_TYPE | Z_EXT_PM_TYPE_NEW | Z_EXT_VIEWHEIGHT | Z_EXT_SERVERTIME | Z_EXT_PITCHLIMITS | Z_EXT_JOIN_OBSERVE | Z_EXT_VWEP;\n\t\t}\n\t\telse if (sv_demoExtensions.ival)\n\t\t{\t/*everything*/\n\t\t\textern cvar_t pext_replacementdeltas;\n\t\t\tdemo.recorder.fteprotocolextensions = PEXT_CHUNKEDDOWNLOADS | PEXT_CSQC | PEXT_COLOURMOD | PEXT_DPFLAGS | PEXT_CUSTOMTEMPEFFECTS | PEXT_ENTITYDBL | PEXT_ENTITYDBL2 | PEXT_FATNESS | PEXT_HEXEN2 | PEXT_HULLSIZE | PEXT_LIGHTSTYLECOL | PEXT_MODELDBL | PEXT_SCALE | PEXT_SETATTACHMENT | PEXT_SETVIEW | PEXT_SOUNDDBL | PEXT_SPAWNSTATIC2 | PEXT_TRANS;\n#ifdef PEXT_VIEW2\n\t\t\tdemo.recorder.fteprotocolextensions |= PEXT_VIEW2;\n#endif\n\t\t\tdemo.recorder.fteprotocolextensions2 = PEXT2_VOICECHAT | PEXT2_SETANGLEDELTA | /*PEXT2_PRYDONCURSOR |*/ (pext_replacementdeltas.ival?PEXT2_REPLACEMENTDELTAS|PEXT2_PREDINFO|PEXT2_NEWSIZEENCODING:0);\n\t\t\t/*enable these, because we might as well (stat ones are always useful)*/\n\t\t\tdemo.recorder.zquake_extensions = Z_EXT_PM_TYPE | Z_EXT_PM_TYPE_NEW | Z_EXT_VIEWHEIGHT | Z_EXT_SERVERTIME | Z_EXT_PITCHLIMITS | Z_EXT_JOIN_OBSERVE | Z_EXT_VWEP;\n\n//\t\t\tif (demo.recorder.fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS)\t//replacementdeltas makes a number of earlier extensions obsolete...\n//\t\t\t\tdemo.recorder.fteprotocolextensions &= ~(PEXT_COLOURMOD|PEXT_DPFLAGS|PEXT_ENTITYDBL|PEXT_ENTITYDBL2|PEXT_FATNESS|PEXT_HEXEN2|PEXT_HULLSIZE|PEXT_MODELDBL|PEXT_SCALE|PEXT_SETATTACHMENT|PEXT_SOUNDDBL|PEXT_SPAWNSTATIC2|PEXT_TRANS);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tdemo.recorder.fteprotocolextensions = 0;\n\t\t\tdemo.recorder.fteprotocolextensions2 = 0;\n\t\t\tdemo.recorder.zquake_extensions = Z_EXT_PM_TYPE | Z_EXT_PM_TYPE_NEW | Z_EXT_VIEWHEIGHT | Z_EXT_SERVERTIME | Z_EXT_PITCHLIMITS | Z_EXT_JOIN_OBSERVE | Z_EXT_VWEP;\n\t\t}\n\n\t\t//pointless extensions that are redundant with mvds\n\t\tdemo.recorder.fteprotocolextensions &= ~PEXT_ACCURATETIMINGS | PEXT_CHUNKEDDOWNLOADS;\n\t\tdemo.recorder.fteprotocolextensions &= ~PEXT1_HIDEPROTOCOLS;\n\t}\n//\telse\n//\t\tSV_WriteRecordMVDMessage(&buf, dem_read);\n\n\tdest->nextdest = demo.dest;\n\tdemo.dest = dest;\n\n\tCvar_ForceSet(Cvar_Get(\"serverdemo\", \"\", CVAR_NOSET, \"\"), SV_Demo_CurrentOutput());\n\n\tSV_ClientProtocolExtensionsChanged(&demo.recorder);\n\n\tSV_MVD_SendInitialGamestate(dest);\n\treturn true;\n}\n\nstatic void SV_MVD_Stopped(void)\n{\t//all recording has stopped. clean up any demo.recorder state\n\tif (demo.recorder.frameunion.frames)\n\t{\n\t\tZ_Free(demo.recorder.frameunion.frames);\n\t\tdemo.recorder.frameunion.frames = NULL;\n\t}\n\tsv.mvdrecording = false;\n\tmemset(&demo, 0, sizeof(demo));\n}\n\nvoid SV_EnableClientsCSQC(void);\nvoid SV_MVD_SendInitialGamestate(mvddest_t *dest)\n{\n\tsizebuf_t\tbuf;\n\tchar buf_data[MAX_QWMSGLEN];\n\tint i, j;\n//\tint n;\n//\tconst char *s;\n\n\tclient_t *player;\n\tchar *gamedir;\n\n\tif (!demo.dest)\n\t\treturn;\n\n\tSV_MVD_WriteReliables(false);\n\n\tsv.mvdrecording = true;\n\tdemo.resetdeltas = true;\n\n\thost_client = &demo.recorder;\n\tif (demo.recorder.fteprotocolextensions & PEXT_CSQC)\n\t\tSV_EnableClientsCSQC();\n\n\n\tdemo.pingtime = demo.time = sv.time;\n\n\n\tsingledest = dest;\n\n/*-------------------------------------------------*/\n\n// serverdata\n\t// send the info about the new client to all connected clients\n\tmemset(&buf, 0, sizeof(buf));\n\tbuf.data = buf_data;\n\tbuf.maxsize = sizeof(buf_data);\n\tbuf.prim = svs.netprim;\n\n// send the serverdata\n\n\tgamedir = InfoBuf_ValueForKey (&svs.info, \"*gamedir\");\n\tif (!gamedir[0])\n\t\tgamedir = FS_GetGamedir(true);\n\n\t//generate some meta info so the file can be identified later\n\t{\n\t\tchar timestr[64];\n\t\ttime_t t;\n\t\tMSG_WriteByte (&buf, svc_stufftext);\n\t\tMSG_WriteString(&buf, va(\"//protocolname %s\\n\", com_protocolname.string));\t//so that the game is known when playing back, to deal with games that conventionally have entirely separate installations.\n\n\t\tMSG_WriteByte (&buf, svc_stufftext);\n\t\tt = time(NULL);\n\t\tstrftime(timestr, sizeof(timestr), \"%Y-%m-%dT%H:%M:%SZ\", gmtime(&t));\n\t\tMSG_WriteString(&buf, va(\"//recorddate %s\\n\", timestr));\t\t\t\t\t//in order to avoid needing to depend upon file times that get destroyed in many different ways.\n\t}\n\n\tMSG_WriteByte (&buf, svc_serverdata);\n\n\t//fix up extensions to match sv_bigcoords correctly. sorry for old clients not working.\n\tif (buf.prim.coordtype == COORDTYPE_FLOAT_32)\n\t\tdemo.recorder.fteprotocolextensions |= PEXT_FLOATCOORDS;\n\telse\n\t\tdemo.recorder.fteprotocolextensions &= ~PEXT_FLOATCOORDS;\n\n\tif (demo.recorder.fteprotocolextensions)\n\t{\n\t\tMSG_WriteLong(&buf, PROTOCOL_VERSION_FTE1);\n\t\tMSG_WriteLong(&buf, demo.recorder.fteprotocolextensions);\n\t}\n\tif (demo.recorder.fteprotocolextensions2)\n\t{\n\t\tMSG_WriteLong(&buf, PROTOCOL_VERSION_FTE2);\n\t\tMSG_WriteLong(&buf, demo.recorder.fteprotocolextensions2);\n\t}\n\tMSG_WriteLong (&buf, PROTOCOL_VERSION_QW);\n\tMSG_WriteLong (&buf, svs.spawncount);\n\tMSG_WriteString (&buf, gamedir);\n\n\tif (demo.recorder.fteprotocolextensions2 & PEXT2_MAXPLAYERS)\n\t\tMSG_WriteByte(&buf, demo.recorder.max_net_ents);\n\n\tMSG_WriteFloat (&buf, sv.time);\n\n\t// send full levelname\n\tMSG_WriteString (&buf, sv.mapname);\n\n\t// send the movevars\n\tMSG_WriteFloat(&buf, movevars.gravity);\n\tMSG_WriteFloat(&buf, movevars.stopspeed);\n\tMSG_WriteFloat(&buf, movevars.maxspeed);\n\tMSG_WriteFloat(&buf, movevars.spectatormaxspeed);\n\tMSG_WriteFloat(&buf, movevars.accelerate);\n\tMSG_WriteFloat(&buf, movevars.airaccelerate);\n\tMSG_WriteFloat(&buf, movevars.wateraccelerate);\n\tMSG_WriteFloat(&buf, movevars.friction);\n\tMSG_WriteFloat(&buf, movevars.waterfriction);\n\tMSG_WriteFloat(&buf, movevars.entgravity);\n\n\tSV_WriteRecordMVDMessage (&buf);\n\tSZ_Clear (&buf);\n\n\tdemo.recorder.prespawn_stage = PRESPAWN_SERVERINFO;\n\tdemo.recorder.prespawn_idx = 0;\n\twhile (demo.recorder.prespawn_stage != PRESPAWN_COMPLETED)\n\t{\n\t\tif (demo.recorder.prespawn_stage == PRESPAWN_MAPCHECK)\n\t\t{\n\t\t\tdemo.recorder.prespawn_stage++;//client won't reply, so don't wait.\n\t\t\tdemo.recorder.prespawn_idx = 0;\n\t\t}\n\t\tdemo.recorder.prespawn_allow_soundlist = true;\t//normally set for the server to wait for ack. we don't want to wait.\n\t\tdemo.recorder.prespawn_allow_modellist = true;\t//normally set for the server to wait for ack. we don't want to wait.\n\n\t\tSV_SendClientPrespawnInfo(&demo.recorder);\n\t\tSV_MVD_WriteReliables(false);\n\t}\n\n// send current status of all other players\n\n\tfor (i = 0; i < demo.recorder.max_net_clients && i < svs.allocated_client_slots; i++)\n\t{\n\t\tplayer = &svs.clients[i];\n\n\t\tSV_MVD_FullClientUpdate(&buf, player);\n\n\t\tif (buf.cursize > MAX_QWMSGLEN/2)\n\t\t{\n\t\t\t//flush backbuffer\n\t\t\tSV_WriteRecordMVDMessage (&buf);\n\t\t\tSZ_Clear (&buf);\n\t\t}\n\t}\n\n// send all current light styles\n\tfor (i=0 ; i<sv.maxlightstyles || i < MAX_STANDARDLIGHTSTYLES; i++)\n\t\tSV_SendLightstyle(&demo.recorder, &buf, i, true);\n\n\t//invalidate stats+players somehow\n\tfor (i = 0; i < MAX_CLIENTS; i++)\n\t{\n\t\tfor (j = 0; j < MAX_CL_STATS; j++)\n\t\t{\n\t\t\tdemo.statsi[i][j] = 0x7fffffff;\n\t\t\tdemo.statsf[i][j] = -FLT_MAX;\n\t\t}\n\t\tdemo.playerreset[i] = true;\n\t}\n\n\t// get the client to check and download skins\n\t// when that is completed, a begin command will be issued\n\tMSG_WriteByte (&buf, svc_stufftext);\n\tMSG_WriteString (&buf, \"skins\\n\");\n\n\tSV_WriteRecordMVDMessage (&buf);\n\tSV_MVD_WriteReliables(false);\n\tSV_WriteSetMVDMessage();\n\n\tsingledest = NULL;\n}\n\n//double-underscores will get merged together.\nconst char *SV_GenCleanTable(void)\n{\n\tstatic char tab[256];\n\tstatic int tabbuilt = -1;\n\tint mode = com_parseutf8.ival>0;\n\tint i;\n\n\tif (tabbuilt == mode)\n\t\treturn tab;\n\n\t//identity\n\tfor(i = 0; i < 32; i++)\n\t\ttab[i] = '_';\t//unprintables.\n\tfor(     ; i < 128; i++)\n\t\ttab[i] = i;\n\n\t//cheesy way around NUL.mvd etc filenames.\n\tfor(i = 'A'; i <= 'Z'; i++)\n\t\ttab[i] = i + ('a'-'A');\n\n\t//these chars are reserved by windows, so its generally best to not use them, even on loonix\n\ttab['<'] = '[';\n\ttab['>'] = ']';\n\ttab['|'] = '_';\n\ttab[':'] = '_';\n\ttab['*'] = '_';\n\ttab['?'] = '_';\n\ttab['\\\\']= '_';\n\ttab['/'] = '_';\n\ttab['\\\"']= '_';\n\t//some extra ones to make unix scripts nicer.\n\ttab['&'] = '_';\n\ttab['~'] = '_';\n\ttab['`'] = '_';\n\ttab[','] = '_';\n\ttab[' '] = '_';\t//don't use spaces, it means files need quotes, and then stuff bugs out.\n\ttab['.'] = '_';\t//many windows programs can't properly deal with multiple dots\n\n\tif (mode)\n\t{\n\t\t//high chars are regular utf-8. yay\n\t\tfor(i = 128; i < 256; i++)\n\t\t\ttab[i] = i;\n\t}\n\telse\n\t{\n\t\t//second row contains coloured numbers for the hud\n\t\ttab[16] = '[';\n\t\ttab[17] = ']';\n\t\tfor(i = 0; i < 10; i++)\n\t\t\ttab[18+i] = '0'+i;\n\t\ttab[28] = '_';\t//'.'\n\t\ttab[29] =\t//line breaks\n\t\ttab[30] =\n\t\ttab[31] = '_';\n\n\t\t//high chars\n\n\t\t//the first 16 chars of the high range are actually different.\n\t\ttab[128] = '_';\t//scrollbars\n\t\ttab[129] = '_';\n\t\ttab[130] = '_';\n\t\ttab[130] = '_';\n\t\tfor(i = 132; i < 128+16; i++)\n\t\t\ttab[18+i] = '_';\t//LEDs mostly\n\n\t\t//but the rest of the table is just recoloured.\n\t\tfor(i = 128+16; i < 256; i++)\n\t\t\ttab[i] = tab[i&127];\n\t}\n\treturn tab;\n}\n\n/*\n====================\nSV_CleanName\n\nCleans the demo name, removes restricted chars, makes name lowercase\n====================\n*/\n\nchar *SV_CleanName (unsigned char *name)\n{\n\tstatic char text[1024];\n\tchar *out = text;\n\tconst char *chartbl = SV_GenCleanTable();\n\n\t*out = chartbl[*name++];\n\n\twhile (*name && out - text < sizeof(text))\n\t\tif (*out == '_' && chartbl[*name] == '_')\n\t\t\tname++;\n\t\telse *++out = chartbl[*name++];\n\n\t*++out = 0;\n\n\n\tout = text;\n\twhile (*out == '.')\n\t\tout++;\t//leading dots (which could be caused by all sorts of things) are bad. boo hidden files.\n\treturn out;\n}\n\n//figure out the actual size limit. this is somewhat approximate anyway.\nqofs_t MVD_DemoMaxDirSize(void)\n{\n\tchar *e;\n\tdouble maxdirsize = strtod(sv_demoMaxDirSize.string, &e);\n\tif (*e == ' ' || *e == '\\t')\n\t\te++;\n\t//that will be trailed by g[b], m[b], k[b], or b\n\tif (*e == 'b' || *e == 'B')\n\t\treturn maxdirsize;\n\telse if (*e == 'k' || *e == 'K')\n\t\treturn maxdirsize * 1024;\n\telse if (*e == 'm' || *e == 'M')\n\t\treturn maxdirsize * 1024*1024;\n\telse if (*e == 'g' || *e == 'G')\n\t\treturn maxdirsize * 1024*1024*1024;\n\telse\n\t\treturn maxdirsize * 1024;\t//assume kb.\n}\n//returns if there's enough disk space to record another demo.\nqboolean MVD_CheckSpace(qboolean broadcastwarnings)\n{\n\tdir_t\t*dir;\n\n\tqofs_t maxdirsize = MVD_DemoMaxDirSize();\n\tif (maxdirsize > 0 || sv_demoMaxDirCount.ival > 0 || sv_demoMaxDirAge.ival > 0)\n\t{\n\t\tdir = Sys_listdemos(sv_demoDir.string, false, SORT_BY_DATE);\n\t\tif (sv_demoClearOld.ival && *sv_demoDir.string)\n\t\t{\n\t\t\ttime_t removebeforetime = time(NULL) - sv_demoMaxDirAge.value*60*60*24;\n\t\t\twhile (dir->numfiles && (\n\t\t\t\t(maxdirsize>0 && dir->size > maxdirsize) ||\n\t\t\t\t(sv_demoMaxDirCount.ival>0 && dir->numfiles >= sv_demoMaxDirCount.ival) ||\n\t\t\t\t(sv_demoMaxDirAge.ival && dir->files[dir->numfiles-1].mtime && dir->files[dir->numfiles-1].mtime - removebeforetime < 0)))\n\t\t\t{\n\t\t\t\tfile_t *f = &dir->files[dir->numfiles-1];\t//this is the file we want to kill.\n\t\t\t\tif (!f->path || !f->path->RemoveFile)\n\t\t\t\t{\t//erm, can't remove it...\n\t\t\t\t\tdir->size -= f->size;\n\t\t\t\t\tdir->numfiles--;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (f->path->RemoveFile(f->path, f->name))\n\t\t\t\t{\t//okay, looks like we managed to kill it.\n\t\t\t\t\tCon_Printf(CON_WARNING\"Removed demo \\\"%s\\\"\\n\", f->name);\n\t\t\t\t\tdir->size -= f->size;\n\t\t\t\t\tdir->numfiles--;\n\n\t\t\t\t\t//Try to take the .txt too.\n\t\t\t\t\tf->path->RemoveFile(f->path, SV_MVDName2Txt(f->name));\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (dir->numfiles && sv_demoMaxDirCount.ival>0 && dir->numfiles >= sv_demoMaxDirCount.ival)\n\t\t{\n\t\t\tif (broadcastwarnings)\n\t\t\t\tSV_BroadcastPrintf(PRINT_MEDIUM, CON_WARNING\"insufficient directory space, increase server's sv_demoMaxDirCount\\n\");\n\t\t\telse\n\t\t\t\tCon_Printf(CON_WARNING\"insufficient demo space, increase sv_demoMaxDirCount\\n\");\n\t\t\tSys_freedir(dir);\n\t\t\treturn false;\n\t\t}\n\t\tif (dir->numfiles && maxdirsize>0 && dir->size > maxdirsize)\n\t\t{\n\t\t\tif (broadcastwarnings)\n\t\t\t\tSV_BroadcastPrintf(PRINT_MEDIUM, CON_WARNING\"insufficient directory space, increase server's sv_demoMaxDirSize\\n\");\n\t\t\telse\n\t\t\t\tCon_Printf(CON_WARNING\"insufficient demo space, increase sv_demoMaxDirSize\\n\");\n\t\t\tSys_freedir(dir);\n\t\t\treturn false;\n\t\t}\n\n\t\tSys_freedir(dir);\n\t}\n\treturn true;\n}\n\n/*\n====================\nSV_Record_f\n\nrecord <demoname>\n====================\n*/\nvoid SV_MVD_Record_f (void)\n{\n\tint\t\tc;\n\tchar\tname[MAX_OSPATH+MAX_MVD_NAME];\n\tchar\tnewname[MAX_MVD_NAME];\n\n\tc = Cmd_Argc();\n\tif (c != 2)\n\t{\n\t\tCon_Printf (\"mvdrecord <demoname>\\n\");\n\t\treturn;\n\t}\n\n\tif (sv.state != ss_active){\n\t\tCon_Printf (\"Not active yet.\\n\");\n\t\treturn;\n\t}\n\n\tif (!MVD_CheckSpace(Cmd_FromGamecode()))\n\t\treturn;\n\n\tQ_strncpyz(newname, va(\"%s%s\", sv_demoPrefix.string, SV_CleanName(Cmd_Argv(1))),\n\t\t\tsizeof(newname) - strlen(sv_demoSuffix.string) - 5);\n\tQ_strncatz(newname, sv_demoSuffix.string, MAX_MVD_NAME);\n\n\tsnprintf (name, MAX_OSPATH+MAX_MVD_NAME, \"%s/%s\", sv_demoDir.string, newname);\n\n\n\tCOM_StripExtension(name, name, sizeof(name));\n#ifdef AVAIL_GZDEC\n\tif (sv_demoAutoCompress.ival == 1)\n\t\tCOM_DefaultExtension(name, \".mvd.gz\", sizeof(name));\n\telse\n#endif\n\t\tCOM_DefaultExtension(name, \".mvd\", sizeof(name));\n\tFS_CreatePath (name, FS_GAMEONLY);\n\n\t//\n\t// open the demo file and start recording\n\t//\n\tSV_MVD_Record (SV_MVD_InitRecordFile(name));\n}\n\n//called when a connecting player becomes active.\nvoid SV_MVD_AutoRecord (void)\n{\n\t//not enabled (for the server) anyway.\n\tif (sv_demoAutoRecord.ival <= 0)\n\t\treturn;\n\n\t//don't record multiple...\n\tif (sv.mvdrecording)\n\t\treturn;\n\n\t//only do it if we're underneath our quotas.\n\tif (!MVD_CheckSpace(true))\n\t\treturn;\n\n\tif (sv_demoAutoRecord.ival > 0)\n\t{\n\t\tint playercount = 0, i;\n\t\tfor (i = 0; i < svs.allocated_client_slots; i++)\n\t\t{\n\t\t\tif (svs.clients[i].state >= cs_spawned)\n\t\t\t\tplayercount++;\n\t\t}\n\t\tif (playercount >= sv_demoAutoRecord.ival)\n\t\t{\t//okay, we've reached our player count, its time to start recording now.\n\t\t\tchar name[MAX_OSPATH];\n\t\t\tchar timestamp[64];\n\t\t\ttime_t tm = time(NULL);\n\t\t\tstrftime(timestamp, sizeof(timestamp), \"%Y%m%d_%H%M%S\", localtime(&tm));\n\t\t\tQ_snprintfz(name, sizeof(name), \"%s/%s%s_%s\", sv_demoDir.string, sv_demoAutoPrefix.string, svs.name, timestamp);\n#ifdef AVAIL_GZDEC\n\t\t\tif (sv_demoAutoCompress.ival == 1 || !*sv_demoAutoCompress.string)\t//default is to gzip.\n\t\t\t\tQ_strncatz(name, \".mvd.gz\", sizeof(name));\n\t\t\telse\n#endif\n\t\t\t\tQ_strncatz(name, \".mvd\", sizeof(name));\n\t\t\tFS_CreatePath (name, FS_GAMEONLY);\n\n\t\t\tSV_MVD_Record (SV_MVD_InitRecordFile(name));\n\t\t}\n\t}\n}\n\nvoid SV_MVD_CheckReverse(void)\n{\n\tstruct reversedest_s *rd, **link;\n\tenum qtvstatus_e s;\n\tint len;\n\tfor (link = &reversedest; *link; link = &rd->next)\n\t{\n\t\trd = *link;\n\t\tif (realtime > rd->timeout)\n\t\t\tlen = -1;\n\t\telse\n\t\t\tlen = VFS_READ(rd->stream, rd->inbuffer+rd->inbuffersize, sizeof(rd->inbuffer)-1-rd->inbuffersize);\n\t\tif (len < 0)\n\t\t\ts = QTV_ERROR;\n\t\telse if (!len)\n\t\t\tcontinue; //keep waiting...\n\t\telse\n\t\t{\n\t\t\trd->inbuffersize += len;\n\t\t\trd->inbuffer[rd->inbuffersize] = 0;\n\t\t\tif (rd->inbuffersize >= 3 && strncmp(rd->inbuffer, \"QTV\", 3))\n\t\t\t\ts = QTV_ERROR;\t//not qtv server...\n\t\t\telse\n\t\t\t{\n\t\t\t\tchar *e = strstr(rd->inbuffer, \"\\n\\n\");\n\t\t\t\tif (e)\n\t\t\t\t\ts = SV_MVD_GotQTVRequest(rd->stream, rd->inbuffer, rd->inbuffer+rd->inbuffersize, &rd->info);\n\t\t\t\telse\n\t\t\t\t\tcontinue; //not nuff data yet\n\t\t\t}\n\t\t}\n\t\tswitch(s)\n\t\t{\n\t\tcase QTV_RETRY:\t//need to parse new stuff.\n\t\t\tcontinue;\n\t\tcase QTV_ACCEPT:\n\t\t\trd->stream = NULL;\n\t\t\t//fallthrough\n\t\tcase QTV_ERROR:\n\t\t\tif (rd->stream)\n\t\t\t\tVFS_CLOSE(rd->stream);\n\t\t\t*link = rd->next;\n\t\t\tZ_Free(rd);\n\t\t\treturn;\n\t\t}\n\t}\n}\n\nvoid SV_MVD_QTVReverse_f (void)\n{\n#if 1//ndef HAVE_TCP\n//\tCon_Printf (\"%s is not supported in this build\\n\", Cmd_Argv(0));\n\n\tconst char *ip = Cmd_Argv(1);\n\tvfsfile_t *f;\n\tconst char *msg =\t\"QTV\\n\"\n\t\t\t\t\t\t\"VERSION: 1\\n\"\n\t\t\t\t\t\t\"REVERSE\\n\"\n\t\t\t\t\t\t\"\\n\";\n\tstruct reversedest_s *rd;\n\tif (sv.state<ss_loading)\n\t\treturn;\n\n\tf = FS_OpenTCP(ip, 27599, false);\n\tif (!f)\n\t\treturn;\n\n\tVFS_WRITE(f, msg, strlen(msg));\n\n\trd = Z_Malloc(sizeof(*rd));\n\trd->stream = f;\n\trd->info.isreverse = true;\n\trd->timeout = realtime + 10;\n\treversedest = rd;\n#else\n\tchar *ip;\n\tif (sv.state != ss_active)\n\t{\n\t\tCon_Printf (\"Server is not running\\n\");\n\t\treturn;\n\t}\n\tif (Cmd_Argc() != 2)\n\t{\n\t\tCon_Printf (\"%s ip:port\\n\", Cmd_Argv(0));\n\t\treturn;\n\t}\n\n\tip = Cmd_Argv(1);\n\n\n\n{\n\tchar *data;\n\tint sock;\n\n\tstruct sockaddr_qstorage\tremote;\n//\tint fromlen;\n\n\tint adrfam;\n\tint adrsz;\n\tunsigned int nonblocking = true;\n\n\n\tif (!NET_StringToSockaddr(ip, 0, &remote, &adrfam, &adrsz))\n\t{\n\t\tCon_Printf (\"qtvreverse: failed to resolve address\\n\");\n\t\treturn;\n\t}\n\n\tif ((sock = socket (adrfam, SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET)\n\t{\n\t\tCon_Printf (\"qtvreverse: socket: %s\\n\", strerror(neterrno()));\n\t\treturn;\n\t}\n\tif (connect(sock, (void*)&remote, adrsz) == INVALID_SOCKET)\n\t{\n\t\tclosesocket(sock);\n\t\tCon_Printf (\"qtvreverse: connect: %s\\n\", strerror(neterrno()));\n\t\treturn;\n\t}\n\n\tif (ioctlsocket (sock, FIONBIO, (u_long *)&nonblocking) == INVALID_SOCKET)\n\t{\n\t\tclosesocket(sock);\n\t\tCon_Printf (\"qtvreverse: ioctl FIONBIO: %s\\n\", strerror(neterrno()));\n\t\treturn;\n\t}\n\n\tdata =\t\"QTV\\n\"\n\t\t\t\"REVERSE\\n\"\n\t\t\t\"\\n\";\n\tif (send(sock, data, strlen(data), 0) == INVALID_SOCKET)\n\t{\n\t\tclosesocket(sock);\n\t\tCon_Printf (\"qtvreverse: send: %s\\n\", strerror(neterrno()));\n\t\treturn;\n\t}\n\n\n\tSV_MVD_InitPendingStream(sock, ip)->isreverse = true;\n}\n\n\t//SV_MVD_Record (dest);\n#endif\n}\n\n/*\n====================\nSV_EasyRecord_f\n\neasyrecord [demoname]\n====================\n*/\n\nint\tDem_CountPlayers (void)\n{\n\tint\ti, count;\n\n\tcount = 0;\n\tfor (i = 0; i < sv.allocated_client_slots ; i++)\n\t{\n\t\tif (svs.clients[i].name[0] && !svs.clients[i].spectator)\n\t\t\tcount++;\n\t}\n\n\treturn count;\n}\n\nchar *Dem_Team(int num)\n{\n\tint i;\n\tstatic char *lastteam[2];\n\tqboolean first = true;\n\tclient_t *client;\n\tstatic int index = 0;\n\n\tindex = 1 - index;\n\n\tfor (i = 0, client = svs.clients; num && i < sv.allocated_client_slots; i++, client++)\n\t{\n\t\tif (!client->name[0] || client->spectator)\n\t\t\tcontinue;\n\n\t\tif (first || strcmp(lastteam[index], InfoBuf_ValueForKey(&client->userinfo, \"team\")))\n\t\t{\n\t\t\tfirst = false;\n\t\t\tnum--;\n\t\t\tlastteam[index] = InfoBuf_ValueForKey(&client->userinfo, \"team\");\n\t\t}\n\t}\n\n\tif (num)\n\t\treturn \"\";\n\n\treturn lastteam[index];\n}\n\nchar *Dem_PlayerName(int num)\n{\n\tint i;\n\tclient_t *client;\n\n\tfor (i = 0, client = svs.clients; i < sv.allocated_client_slots; i++, client++)\n\t{\n\t\tif (!client->name[0] || client->spectator)\n\t\t\tcontinue;\n\n\t\tif (!--num)\n\t\t\treturn client->name;\n\t}\n\n\treturn \"\";\n}\n\n// -> scream\nchar *Dem_PlayerNameTeam(char *t)\n{\n\tint\ti;\n\tclient_t *client;\n\tstatic char\tn[1024];\n\tint\tsep;\n\n\tn[0] = 0;\n\n\tsep = 0;\n\n\tfor (i = 0, client = svs.clients; i < sv.allocated_client_slots; i++, client++)\n\t{\n\t\tif (!client->name[0] || client->spectator)\n\t\t\tcontinue;\n\n\t\tif (strcmp(t, InfoBuf_ValueForKey(&client->userinfo, \"team\"))==0)\n\t\t{\n\t\t\tif (sep >= 1)\n\t\t\t\tQ_strncatz (n, \"_\", sizeof(n));\n//\t\t\t\tsnprintf (n, sizeof(n), \"%s_\", n);\n\t\t\tQ_strncatz (n, client->name, sizeof(n));\n//\t\t\tsnprintf (n, sizeof(n),\"%s%s\", n, client->name);\n\t\t\tsep++;\n\t\t}\n\t}\n\n\treturn n;\n}\n\nint\tDem_CountTeamPlayers (char *t)\n{\n\tint\ti, count;\n\n\tcount = 0;\n\tfor (i = 0; i < sv.allocated_client_slots ; i++)\n\t{\n\t\tif (svs.clients[i].name[0] && !svs.clients[i].spectator)\n\t\t\tif (strcmp(InfoBuf_ValueForKey(&svs.clients[i].userinfo, \"team\"), t)==0)\n\t\t\t\tcount++;\n\t}\n\n\treturn count;\n}\n\n//takes a quake-mark-up string (subject to com_parseutf and ^ etc) and spits out a usable utf-8 name\n//in and out may overlap.\nstatic char *FS_UTF8FromQuakeFilename(const char *in, qboolean dequake, qboolean keepmarkup, qboolean blockdirsep, char *out, size_t outsize)\n{\n\tconchar_t cline[8192], *c;\n\tchar *outend = out+outsize-1;\t//-1 for our null\n\tunsigned int charflags, codepoint;\n\n\tCOM_ParseFunString(CON_WHITEMASK, in, cline, sizeof(cline), keepmarkup);\n\tfor (c = cline; *c; )\n\t{\n\t\tc = Font_Decode(c, &charflags, &codepoint);\n\t\tif (charflags & CON_HIDDEN)\n\t\t\tcontinue;\n\t\tif (dequake)\n\t\t\tcodepoint = COM_DeQuake(codepoint);\n\n\t\tif (codepoint == '/' && blockdirsep) codepoint = '-';\t//spreading across multiple dirs is just awkward\n\t\telse if (codepoint <  ' ' )\tcodepoint = '-';\t//C0 chars are all kinds of problematic\n\t\telse if (codepoint == '\\\\')\tcodepoint = '-';\t//windows sucks. or string escapes do.\n\t\telse if (codepoint == ':' )\tcodepoint = '-';\t//drives, or Alternative Data Streams (read: often hidden)\n\t\telse if (codepoint == '\\\"')\tcodepoint = '-';\t//erk! escapes necessitate escapes...\n\t\telse if (codepoint == '<' )\tcodepoint = '-';\t//pipe stuff sucks\n\t\telse if (codepoint == '>' )\tcodepoint = '-';\t//pipe stuff sucks\n\t\telse if (codepoint == '|' )\tcodepoint = '-';\t//pipe stuff sucks\n\t\telse if (codepoint == '?' )\tcodepoint = '-';\t//wildcards complicate things\n\t\telse if (codepoint == '*' )\tcodepoint = '-';\t//wildcards complicate things\n\n\t\tout += utf8_encode(out, codepoint, outend-out);\n\t}\n\t*out = 0;\n\treturn out;\n}\n// <-\n\nvoid SV_MVDEasyRecord_f (void)\n{\n\tint\t\tc;\n\tchar\tname[1024];\n\tchar\tname2[MAX_OSPATH*7]; // scream\n\t//char\tname2[MAX_OSPATH*2];\n\tint\t\ti;\n\tvfsfile_t\t*f;\n\n\tc = Cmd_Argc();\n\tif (c > 2)\n\t{\n\t\tCon_Printf (\"easyrecord [demoname]\\n\");\n\t\treturn;\n\t}\n\n\tif (sv.state < ss_active)\n\t{\n\t\tCon_Printf(\"Server isn't running or is still loading\\n\");\n\t\treturn;\n\t}\n\n\tif (!MVD_CheckSpace(Cmd_FromGamecode()))\n\t\treturn;\n\n\tif (c == 2)\n\t{\n\t\tQ_strncpyz (name, Cmd_Argv(1), sizeof(name));\n\t\tFS_UTF8FromQuakeFilename(name, true, false, false, name, sizeof(name));\n\t}\n\telse\n\t{\n\t\ti = Dem_CountPlayers();\n\t\t/*if (!deathmatch.ival)\n\t\t{\n\t\t\tif (coop.ival || i>1)\n\t\t\t\tsnprintf (name, sizeof(name), \"coop_%s_%d(%d)\", sv.name, skill.ival, i);\n\t\t\telse\n\t\t\t\tsnprintf (name, sizeof(name), \"sp_%s_%d_%s\", sv.name, skill.ival, Dem_PlayerName(0));\n\t\t}\n\t\telse*/ if (teamplay.value >= 1 && i > 2)\n\t\t{\n\t\t\t// Teamplay\n\t\t\tsnprintf (name, sizeof(name), \"%don%d_\", Dem_CountTeamPlayers(Dem_Team(1)), Dem_CountTeamPlayers(Dem_Team(2)));\n\t\t\tif (sv_demoExtraNames.value > 0)\n\t\t\t{\n\t\t\t\tQ_strncatz (name, va(\"[%s]_%s_vs_[%s]_%s_%s\",\n\t\t\t\t\t\t\t\t\tDem_Team(1), Dem_PlayerNameTeam(Dem_Team(1)),\n\t\t\t\t\t\t\t\t\tDem_Team(2), Dem_PlayerNameTeam(Dem_Team(2)),\n\t\t\t\t\t\t\t\t\tsvs.name), sizeof(name));\n\t\t\t} else\n\t\t\t\tQ_strncatz (name, va(\"%s_vs_%s_%s\", Dem_Team(1), Dem_Team(2), svs.name), sizeof(name));\n\t\t} else {\n\t\t\tif (i == 2) {\n\t\t\t\t// Duel\n\t\t\t\tsnprintf (name, sizeof(name), \"duel_%s_vs_%s_%s\",\n\t\t\t\t\tDem_PlayerName(1),\n\t\t\t\t\tDem_PlayerName(2),\n\t\t\t\t\tsvs.name);\n\t\t\t} else {\n\t\t\t\t// FFA\n\t\t\t\tsnprintf (name, sizeof(name), \"ffa_%s(%d)\", svs.name, i);\n\t\t\t}\n\t\t}\n\n\t\t//convert to utf-8, so its readable on most systems (we convert utf-8 to utf-16 for windows, while linux tends to just use utf-8 in the first place)\n\t\tFS_UTF8FromQuakeFilename(name, true, false, true, name, sizeof(name));\n\t}\n\n\t// <-\n\n// Make sure the filename doesn't contain illegal characters\n\tQ_strncpyz(name, va(\"%s%s\", sv_demoPrefix.string, SV_CleanName(name)),\n\t\t\tMAX_MVD_NAME - strlen(sv_demoSuffix.string) - 7);\n\tQ_strncatz(name, sv_demoSuffix.string, sizeof(name));\n\tQ_strncpyz(name, va(\"%s/%s\", sv_demoDir.string, name), sizeof(name));\n// find a filename that doesn't exist yet\n\tQ_strncpyz(name2, name, sizeof(name2));\n//\tCOM_StripExtension(name2, name2);\n\tFS_CreatePath (name2, FS_GAMEONLY);\n\tQ_strncatz(name2, \".mvd\", sizeof(name2));\n\tif ((f = FS_OpenVFS(name2, \"rb\", FS_GAMEONLY)) == 0)\n\t\tf = FS_OpenVFS(va(\"%s.gz\", name2), \"rb\", FS_GAMEONLY);\n\n\tif (f)\n\t{\n\t\ti = 1;\n\t\tdo {\n\t\t\tVFS_CLOSE (f);\n\t\t\tsnprintf(name2, sizeof(name2), \"%s_%02i\", name, i);\n//\t\t\tCOM_StripExtension(name2, name2);\n\t\t\tQ_strncatz(name2, \".mvd\", sizeof(name2));\n\t\t\tif ((f = FS_OpenVFS (name2, \"rb\", FS_GAMEONLY)) == 0)\n\t\t\t\tf = FS_OpenVFS(va(\"%s.gz\", name2), \"rb\", FS_GAMEONLY);\n\t\t\ti++;\n\t\t} while (f);\n\t}\n\n#ifdef AVAIL_GZDEC\n\tif (sv_demoAutoCompress.ival == 1)\n\t\tQ_strncatz(name2, \".gz\", sizeof(name2));\n#endif\n\tSV_MVD_Record (SV_MVD_InitRecordFile(name2));\n}\n\n//console command for servers/admins\nvoid SV_MVDList_f (void)\n{\n\tchar buf[32];\n\tmvddest_t *d;\n\tdir_t\t*dir;\n\tfile_t\t*list;\n\tint\t\ti,j,show;\n\tqofs_t maxdirsize = MVD_DemoMaxDirSize();\n\n\tCon_Printf(\"content of %s/*.mvd\\n\", sv_demoDir.string);\n\tdir = Sys_listdemos(sv_demoDir.string, true, SORT_BY_DATE);\n\tlist = dir->files;\n\tif (!dir->numfiles)\n\t{\n\t\tCon_Printf(\"no demos\\n\");\n\t}\n\n\tfor (i = 1; i <= dir->numfiles; i++, list++)\n\t{\n\t\tfor (j = 1; j < Cmd_Argc(); j++)\n\t\t\tif (strstr(list->name, Cmd_Argv(j)) == NULL)\n\t\t\t\tbreak;\n\t\tshow = Cmd_Argc() == j;\n\n\t\tif (show)\n\t\t{\n\t\t\tfor (d = demo.dest; d; d = d->nextdest)\n\t\t\t{\n\t\t\t\tif (d->desttype != DEST_STREAM && !strcmp(list->name, d->simplename))\n\t\t\t\t\tCon_Printf(\"*%d: ^[^7%s\\\\demo\\\\%s/%s^] %uk\\n\", i, list->name, sv_demoDir.string, list->name, (unsigned int)(d->totalsize/1024));\n\t\t\t}\n\t\t\tif (!d)\n\t\t\t\tCon_Printf(\"%d: ^[^7%s\\\\demo\\\\%s/%s^] %uk\\n\", i, list->name, sv_demoDir.string, list->name, (unsigned int)(list->size/1024));\n\t\t}\n\t}\n\n\tfor (d = demo.dest; d; d = d->nextdest)\n\t\tdir->size += d->totalsize;\n\n\tCon_Printf(\"\\ndirectory size: %s\\n\",FS_AbbreviateSize(buf,sizeof(buf), dir->size));\n\tif (maxdirsize)\n\t{\n\t\tif (maxdirsize >= dir->size)\n\t\t\tCon_Printf(\"space available: %s\\n\", FS_AbbreviateSize(buf,sizeof(buf), maxdirsize - dir->size));\n\t\telse\n\t\t\tCon_Printf(\"limit exceeded by %s\\n\", FS_AbbreviateSize(buf,sizeof(buf), dir->size - maxdirsize));\n\t}\n\n\tSys_freedir(dir);\n}\n\n//console command used to print to connected clients (we're acting as a dedicated server)\nvoid SV_UserCmdMVDList_f (void)\n{\n\tchar buf[32];\n\tmvddest_t *d;\n\tdir_t\t*dir;\n\tfile_t\t*list;\n\tint\t\ti,j,show;\n\tqofs_t maxdirsize = MVD_DemoMaxDirSize();\n\n\tSV_ClientPrintf(host_client, PRINT_HIGH, \"available demos:\\n\");\n\tdir = Sys_listdemos(sv_demoDir.string, true, SORT_BY_DATE);\n\tlist = dir->files;\n\tif (!dir->numfiles)\n\t{\n\t\tSV_ClientPrintf(host_client, PRINT_HIGH, \"no demos\\n\");\n\t}\n\n\tfor (i = 1; i <= dir->numfiles; i++, list++)\n\t{\n\t\tfor (j = 1; j < Cmd_Argc(); j++)\n\t\t\tif (strstr(list->name, Cmd_Argv(j)) == NULL)\n\t\t\t\tbreak;\n\t\tshow = Cmd_Argc() == j;\n\n\t\tif (show)\n\t\t{\n\t\t\tfor (d = demo.dest; d; d = d->nextdest)\n\t\t\t{\n\t\t\t\tif (d->desttype != DEST_STREAM && !strcmp(list->name, d->simplename))\n\t\t\t\t\tSV_ClientPrintf(host_client, PRINT_HIGH, \"*%d: %s %dk\\n\", i, list->name, d->totalsize/1024);\n\t\t\t}\n\t\t\tif (!d)\n\t\t\t{\n\t\t\t\tif (host_client->fteprotocolextensions2 & PEXT_CSQC)\t//its a hack to use csqc this way, but oh well, but other clients don't want the gibberish.\n\t\t\t\t\tSV_ClientPrintf(host_client, PRINT_HIGH, \"%d: ^[%s\\\\type\\\\/download demos/%s^] %dk\\n\", i, list->name, list->name, (unsigned int)(list->size/1024));\n\t\t\t\telse\n\t\t\t\t\tSV_ClientPrintf(host_client, PRINT_HIGH, \"%d: %s %dk\\n\", i, list->name, (unsigned int)(list->size/1024));\n\t\t\t}\n\t\t}\n\n\t\tif (host_client->num_backbuf >= MAX_BACK_BUFFERS/2)\n\t\t{\n\t\t\tSV_ClientPrintf(host_client, PRINT_HIGH, \"*MORE*\\n\");\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tfor (d = demo.dest; d; d = d->nextdest)\n\t\tdir->size += d->totalsize;\n\n\tSV_ClientPrintf(host_client, PRINT_HIGH, \"\\ndirectory size: %s\\n\",FS_AbbreviateSize(buf,sizeof(buf), dir->size));\n\tif (maxdirsize)\n\t{\n\t\tif (maxdirsize >= dir->size)\n\t\t\tSV_ClientPrintf(host_client, PRINT_HIGH, \"space available: %s\\n\", FS_AbbreviateSize(buf,sizeof(buf), maxdirsize - dir->size));\n\t\telse\n\t\t\tSV_ClientPrintf(host_client, PRINT_HIGH, \"limit exceeded by %s\\n\", FS_AbbreviateSize(buf,sizeof(buf), dir->size - maxdirsize));\n\t}\n\n\tSys_freedir(dir);\n}\n\nvoid SV_UserCmdMVDList_HTML (vfsfile_t *pipe)\n{\n//#define EMBEDGAME\n\tmvddest_t *d;\n\tdir_t\t*dir;\n\tfile_t\t*list;\n\tfloat\tf;\n\tint\t\ti;\n\tqofs_t maxdirsize = MVD_DemoMaxDirSize();\n\n\tVFS_PRINTF(pipe,\n\t\t\"<html>\"\n\t\t\t\"<head>\"\n\t\t\t\t\"<title>%s - %s</title>\"\n\t\t\t\t\"<meta charset='UTF-8'>\"\n\t\t\t\t\"<style>\"\n\t\t\t\t\t\".mydiv { width: 20%%; height: 100%%; padding: 0px; margin: 0px; border: 0px solclass #aaaaaa; float:left; }\"\n\t\t\t\t\t\".game { width: 80%%; height: 100%%; padding: 0px; margin: 0px; border: 0px solclass #aaaaaa; float:left; }\"\n\t\t\t\t\"</style>\"\n#ifdef EMBEDGAME\n\t\t\t\t\"<script>\"\n\t\t\t\t\t\"function playdemo(demo)\"\n\t\t\t\t\t\"{\"\n\t\t\t\t\t\t\"demo = window.location.origin+'/demos/'+demo;\"\n\t\t\t\t\t\t\"thegame.postMessage({cmd:'playdemo',url:demo}, '*');\"\n\t\t\t\t\t\"}\"\n\t\t\t\t\"</script>\"\n#endif\n\t\t\t\"</head>\"\n\t\t\t\"<body>\"\n\t\t\t\"<div class='mydiv'>\\n\"\n\t\t, fs_manifest->formalname, hostname.string);\n\n\tVFS_PRINTF(pipe, \"available demos:<br/>\\n\");\n\tdir = Sys_listdemos(sv_demoDir.string, true, SORT_BY_DATE);\n\tlist = dir->files;\n\tif (!dir->numfiles)\n\t{\n\t\tVFS_PRINTF(pipe, \"no demos<br/>\\n\");\n\t}\n\n\tfor (i = 1; i <= dir->numfiles; i++, list++)\n\t{\n\t\tfor (d = demo.dest; d; d = d->nextdest)\n\t\t{\n\t\t\tif (d->desttype != DEST_STREAM && !strcmp(list->name, d->simplename))\n\t\t\t\tVFS_PRINTF(pipe, \"*%d: %s %dk<br/>\\n\", i, list->name, d->totalsize/1024);\n\t\t}\n\t\tif (!d)\n\t\t{\n\t\t\tchar datetime[64];\n\t\t\tstrftime(datetime, sizeof(datetime), \"%Y-%m-%d %H:%M:%S\", localtime(&list->mtime));\n#ifdef EMBEDGAME\n\t\t\tVFS_PRINTF(pipe, \"%d: <a href='/demos/%s'>%s</a> %uk <a href='javascript:void(0)' onclick='playdemo(\\\"%s\\\")'>play</a> %s<br/>\\n\", i, list->name, list->name, (unsigned int)(list->size/1024), list->name, datetime);\n#else\n\t\t\tVFS_PRINTF(pipe, \"%d: <a href='/demos/%s'>%s</a> %uk %s<br/>\\n\", i, list->name, list->name, (unsigned int)(list->size/1024), datetime);\n#endif\n\t\t}\n\t}\n\n\tfor (d = demo.dest; d; d = d->nextdest)\n\t\tdir->size += d->totalsize;\n\n\tVFS_PRINTF(pipe, \"<br/>\\ndirectory size: %.1fMB<br/>\\n\",(float)dir->size/(1024*1024));\n\tif (maxdirsize)\n\t{\n\t\tf = (maxdirsize - dir->size)/(1024*1024);\n\t\tif ( f < 0)\n\t\t\tf = 0;\n\t\tVFS_PRINTF(pipe, \"space available: %.1fMB<br/>\\n\", f);\n\t}\n\n\tVFS_PRINTF(pipe,\n\t\t\t\t\"</div>\"\n#ifdef EMBEDGAME\n\t\t\t\t\"<div class='game'>\"\n\t\t\t\t\t\"<iframe name='thegame'\"\t//the name of the game is... thegame!\n\t\t\t\t\t\t\" src='\"ENGINEWEBSITE\"/quake' allowfullscreen=true\"\n\t\t\t\t\t\t\" frameborder='0' scrolling='no' marginheight='0' marginwidth='0' width='100%%' height='100%%'\"\n\t\t\t\t\t\t\" onerror=\\\"alert('Failed to load engine')\\\">\"\n\t\t\t\t\t\"</iframe>\"\n\t\t\t\t\"</div>\"\n#endif\n\t\t\t\"</body>\\n\"\n\t\t\"</html>\\n\");\n\n\tSys_freedir(dir);\n}\n\nconst char *SV_MVDLastNum(unsigned int num)\n{\n\tif (!num || num > DEMOLOG_LENGTH)\n\t\treturn NULL;\n\tnum = demolog.sequence - num;\n\treturn demolog.log[num % DEMOLOG_LENGTH].filename;\n}\nchar *SV_MVDNum(char *buffer, int bufferlen, int num)\t//lame number->name lookup according to a list generated at an arbitrary time\n{\n\tfile_t\t*list;\n\tdir_t\t*dir;\n\n\tdir = Sys_listdemos(sv_demoDir.string, true, SORT_BY_DATE);\n\tlist = dir->files;\n\n\tif (num < 0)\n\t\tnum = dir->numfiles + num;\n\tif (num > dir->numfiles || num <= 0)\n\t{\n\t\tSys_freedir(dir);\n\t\treturn NULL;\n\t}\n\tnum--;\n\n\tlist += num;\n\n\tQ_strncpyz(buffer, list->name, bufferlen);\n\tSys_freedir(dir);\n\treturn buffer;\n}\n\nchar *SV_MVDName2Txt(char *name)\n{\n\tchar s[MAX_OSPATH];\n\tconst char *ext;\n\n\tif (!name)\n\t\treturn NULL;\n\n\tQ_strncpyz(s, name, MAX_OSPATH);\n\n\text = COM_GetFileExtension(s, NULL);\n\tif (!Q_strcasecmp(ext, \".gz\") || !Q_strcasecmp(ext, \".xz\"))\n\t\text = COM_GetFileExtension(s, ext);\n\tif (!ext || !*ext)\t//if there's no extension on there, then make sure we're pointing to the end of the string.\n\t\text = s+strlen(s);\n\tif (ext > s+sizeof(s)-4)\t//make sure we don't overflow the buffer by truncating the base/path, ensuring that we don't write some other type of file.\n\t\text = s+sizeof(s)-4;\t//should probably make this an error case and abort instead.\n\tstrcpy((char*)ext, \".txt\");\n\n\treturn va(\"%s\", s);\n}\n\nchar *SV_MVDTxTNum(char *buffer, int bufferlen, int num)\n{\n\treturn SV_MVDName2Txt(SV_MVDNum(buffer, bufferlen, num));\n}\n\nvoid SV_MVDRemove_f (void)\n{\n\tchar name[MAX_MVD_NAME], *ptr;\n\tchar path[MAX_OSPATH];\n\tint i;\n\tmvddest_t *active;\n\n\tif (Cmd_Argc() != 2)\n\t{\n\t\tCon_Printf(\"%s <demoname> - removes the demo\\nrmdemo *<token>   - removes demo with <token> in the name\\nrmdemo *          - removes all demos\\n\", Cmd_Argv(0));\n\t\treturn;\n\t}\n\n\tptr = Cmd_Argv(1);\n\tif (*ptr == '*')\n\t{\n\t\tdir_t *dir;\n\t\tfile_t *list;\n\n\t\t// remove all demos with specified token\n\t\tptr++;\n\n\t\tdir = Sys_listdemos(sv_demoDir.string, true, SORT_BY_DATE);\n\t\tlist = dir->files;\n\t\tfor (i = 0;i < dir->numfiles; list++)\n\t\t{\n\t\t\tif (strstr(list->name, ptr))\n\t\t\t{\n\t\t\t\tmvddest_t *active = SV_FindRecordFile(list->name, NULL);\n\t\t\t\tif (active)\n\t\t\t\t\tSV_MVDStop_f();\n\n\t\t\t\t// stop recording first;\n\t\t\t\tsnprintf(path, MAX_OSPATH, \"%s/%s\", sv_demoDir.string, list->name);\n\t\t\t\tif (FS_Remove(path, FS_GAMEONLY))\n\t\t\t\t{\n\t\t\t\t\tCon_Printf(\"removing %s...\\n\", list->name);\n\t\t\t\t\ti++;\n\t\t\t\t}\n\n\t\t\t\tFS_Remove(SV_MVDName2Txt(path), FS_GAMEONLY);\n\t\t\t}\n\t\t}\n\t\tSys_freedir(dir);\n\n\t\tif (i)\n\t\t{\n\t\t\tCon_Printf(\"%d demos removed\\n\", i);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tCon_Printf(\"no matching found\\n\");\n\t\t}\n\n\t\treturn;\n\t}\n\n\tQ_strncpyz(name, Cmd_Argv(1), MAX_MVD_NAME);\n\tCOM_DefaultExtension(name, \".mvd\", sizeof(name));\n\n\tsnprintf(path, MAX_OSPATH, \"%s/%s\", sv_demoDir.string, name);\n\n\tactive = SV_FindRecordFile(name, NULL);\n\tif (active)\n\t\tSV_MVDStop_f();\n\n\tif (FS_Remove(path, FS_GAMEONLY))\n\t{\n\t\tCon_Printf(\"demo %s successfully removed\\n\", name);\n\t}\n\telse\n\t\tCon_Printf(\"unable to remove demo %s\\n\", name);\n\n\tFS_Remove(SV_MVDName2Txt(path), FS_GAMEONLY);\n}\n\nvoid SV_MVDRemoveNum_f (void)\n{\n\tint\t\tnum;\n\tchar namebuf[MAX_QPATH];\n\tchar\t*val, *name;\n\tchar path[MAX_OSPATH];\n\n\tif (Cmd_Argc() != 2)\n\t{\n\t\tCon_Printf(\"%s <#>\\n\", Cmd_Argv(0));\n\t\treturn;\n\t}\n\n\tval = Cmd_Argv(1);\n\tif ((num = atoi(val)) == 0 && val[0] != '0')\n\t{\n\t\tCon_Printf(\"%s <#>\\n\", Cmd_Argv(0));\n\t\treturn;\n\t}\n\n\tname = SV_MVDNum(namebuf, sizeof(namebuf), num);\n\n\tif (name != NULL)\n\t{\n\t\tmvddest_t *active = SV_FindRecordFile(name, NULL);\n\t\tif (active)\n\t\t\tSV_MVDStop_f();\n\n\t\tsnprintf(path, MAX_OSPATH, \"%s/%s\", sv_demoDir.string, name);\n\t\tif (FS_Remove(path, FS_GAMEONLY))\n\t\t{\n\t\t\tCon_Printf(\"demo %s succesfully removed\\n\", name);\n\t\t}\n\t\telse\n\t\t\tCon_Printf(\"unable to remove demo %s\\n\", name);\n\n\t\tFS_Remove(SV_MVDName2Txt(path), FS_GAMEONLY);\n\t}\n\telse\n\t\tCon_Printf(\"invalid demo num\\n\");\n}\n\nvoid SV_MVDInfoAdd_f (void)\n{\n\tchar namebuf[MAX_QPATH];\n\tchar *name, *args, path[MAX_OSPATH];\n\tvfsfile_t *f;\n\n\t//** is a special hack for ktx\n\tif (Cmd_Argc() < 3) {\n\t\tCon_Printf(\"%s <demonum> <info string>\\n<demonum> = * for currently recorded demo\\n\", Cmd_Argv(0));\n\t\treturn;\n\t}\n\n\tif (!strcmp(Cmd_Argv(1), \"*\") || !strcmp(Cmd_Argv(1), \"**\"))\n\t{\n\t\tmvddest_t *active = SV_FindRecordFile(NULL, NULL);\n\t\tif (!active)\n\t\t{\n\t\t\tCon_Printf(\"Not recording demo!\\n\");\n\t\t\treturn;\n\t\t}\n\n\t\tQ_strncpyz(path, SV_MVDName2Txt(active->filename), sizeof(path));\n\t}\n\telse\n\t{\n\t\tname = SV_MVDTxTNum(namebuf, sizeof(namebuf), atoi(Cmd_Argv(1)));\n\n\t\tif (!name)\n\t\t{\n\t\t\tCon_Printf(\"invalid demo num\\n\");\n\t\t\treturn;\n\t\t}\n\n\t\tsnprintf(path, MAX_OSPATH, \"%s/%s\", sv_demoDir.string, name);\n\t}\n\n\tif ((f = FS_OpenVFS(path, \"ab\", FS_GAMEONLY)) == NULL)\n\t{\n\t\tCon_Printf(\"%s: failed to open \\\"%s\\\"\\n\", Cmd_Argv(0), path);\n\t\treturn;\n\t}\n\n\tif (!strcmp(Cmd_Argv(1), \"**\"))\n\t{\n\t\tsize_t fsize;\n\t\targs = FS_LoadMallocFile(Cmd_Argv(2), &fsize);\n\t\tif (args)\n\t\t{\n\t\t\tVFS_WRITE(f, args, fsize);\n\t\t\tFS_FreeFile(args);\n\t\t}\n\t\telse\n\t\t\tCon_Printf(\"%s: failed to open input file\\n\", Cmd_Argv(0));\n\t}\n\telse\n\t{\n\t\t// skip demonum\n\t\targs = Cmd_Args();\n\t\twhile (*args > 32) args++;\n\t\twhile (*args && *args <= 32) args++;\n\n\t\tVFS_WRITE(f, args, strlen(args));\n\t\tVFS_WRITE(f, \"\\n\", 1);\n\t}\n\tVFS_FLUSH(f);\n\tVFS_CLOSE(f);\n}\n\nvoid SV_MVDInfoRemove_f (void)\n{\n\tchar namebuf[MAX_QPATH];\n\tchar *name, path[MAX_OSPATH];\n\n\tif (Cmd_Argc() < 2)\n\t{\n\t\tCon_Printf(\"%s <demonum>\\n<demonum> = * for currently recorded demo\\n\", Cmd_Argv(0));\n\t\treturn;\n\t}\n\n\tif (!strcmp(Cmd_Argv(1), \"*\"))\n\t{\n\t\tmvddest_t *active = SV_FindRecordFile(NULL, NULL);\n\t\tif (!active)\n\t\t{\n\t\t\tCon_Printf(\"Not recording demo!\\n\");\n\t\t\treturn;\n\t\t}\n\n\t\tsnprintf(path, MAX_OSPATH, \"%s\", SV_MVDName2Txt(active->filename));\n\t}\n\telse\n\t{\n\t\tname = SV_MVDTxTNum(namebuf, sizeof(namebuf), atoi(Cmd_Argv(1)));\n\n\t\tif (!name)\n\t\t{\n\t\t\tCon_Printf(\"invalid demo num\\n\");\n\t\t\treturn;\n\t\t}\n\n\t\tsnprintf(path, MAX_OSPATH, \"%s/%s\", sv_demoDir.string, name);\n\t}\n\n\tif (FS_Remove(path, FS_GAMEONLY))\n\t{\n\t\tFS_FlushFSHashRemoved(path);\n\t\tCon_Printf(\"file removed\\n\");\n\t}\n\telse\n\t\tCon_Printf(\"failed to remove the file\\n\");\n\n}\n\nstatic void SV_MVDInfo_f (void)\n{\t//callable by client, so be careful.\n\tint len;\n\tchar buf[64];\n\tvfsfile_t *f = NULL;\n\tchar *name, path[MAX_OSPATH];\n\n\tif (Cmd_Argc() < 2)\n\t{\n\t\tCon_Printf(\"%s <demonum>\\n<demonum> = * for currently recorded demo\\n\", Cmd_Argv(0));\n\t\treturn;\n\t}\n\n\tif (!strcmp(Cmd_Argv(1), \"*\"))\n\t{\n\t\tmvddest_t *active = SV_FindRecordFile(NULL, NULL);\n\t\tif (!active)\n\t\t{\n\t\t\tCon_Printf(\"Not recording demo!\\n\");\n\t\t\treturn;\n\t\t}\n\n\t\tQ_strncpyz(path, SV_MVDName2Txt(active->filename), sizeof(path));\n\t}\n\telse\n\t{\n\t\tname = SV_MVDTxTNum(buf, sizeof(buf), atoi(Cmd_Argv(1)));\n\n\t\tif (!name)\n\t\t{\n\t\t\tCon_Printf(\"invalid demo num\\n\");\n\t\t\treturn;\n\t\t}\n\n\t\tsnprintf(path, MAX_OSPATH, \"%s/%s\", sv_demoDir.string, name);\n\t}\n\n\tif ((f = FS_OpenVFS(path, \"rt\", FS_GAMEONLY)) == NULL)\n\t{\n\t\tCon_Printf(\"(empty)\\n\");\n\t\treturn;\n\t}\n\n\tfor(;;)\n\t{\n\t\tlen = VFS_READ (f, buf, sizeof(buf)-1);\n\t\tif (len <= 0)\n\t\t\tbreak;\n\t\tbuf[len] = 0;\n\t\tCon_Printf(\"%s\", buf);\n\t}\n\n\tVFS_CLOSE(f);\n}\nvoid SV_UserMVDInfo_f (void)\n{\n\tSV_BeginRedirect(RD_CLIENT, host_client->language);\n\tSV_MVDInfo_f();\n\tSV_EndRedirect();\n}\n\n\n\n\n\n\n#ifdef SERVER_DEMO_PLAYBACK\nvoid SV_MVDPlayNum_f(void)\n{\n\tchar namebuf[MAX_QPATH];\n\tchar *name;\n\tint\t\tnum;\n\tchar\t*val;\n\n\tif (Cmd_Argc() != 2)\n\t{\n\t\tCon_Printf(\"%s <#>\\n\", Cmd_Argv(0));\n\t\treturn;\n\t}\n\n\tval = Cmd_Argv(1);\n\tif ((num = atoi(val)) == 0 && val[0] != '0')\n\t{\n\t\tCon_Printf(\"%s <#>\\n\", Cmd_Argv(0));\n\t\treturn;\n\t}\n\n\tname = SV_MVDNum(namebuf, sizeof(namebuf), atoi(val));\n\n\tif (name)\n\t\tCbuf_AddText(va(\"mvdplay %s\\n\", name), Cmd_ExecLevel);\n\telse\n\t\tCon_Printf(\"invalid demo num\\n\");\n}\n#endif\n\n\n\nvoid SV_MVDInit(void)\n{\n\tMVD_Init();\n\n\t//names that conflict with the client and thus only exist in dedicated servers (and thus shouldn't be used by mods that want mvds).\n#ifdef SERVERONLY\n\tCmd_AddCommand (\"record\",\t\t\tSV_MVD_Record_f);\n\tCmd_AddCommand (\"stop\",\t\t\t\tSV_MVDStop_f);\t//client version should still work for mvds too.\n#endif\n\t//these don't currently conflict, but hey...\n\tCmd_AddCommand (\"cancel\",\t\t\tSV_MVD_Cancel_f);\n\tCmd_AddCommand (\"easyrecord\",\t\tSV_MVDEasyRecord_f);\n\tCmd_AddCommand (\"demolist\",\t\t\tSV_MVDList_f);\n\tCmd_AddCommand (\"rmdemo\",\t\t\tSV_MVDRemove_f);\n\tCmd_AddCommand (\"rmdemonum\",\t\tSV_MVDRemoveNum_f);\n\n\t//serverside only names that won't conflict (matching mvdsv)\n\tCmd_AddCommand (\"sv_demorecord\",\tSV_MVD_Record_f);\n\tCmd_AddCommand (\"sv_demostop\",\t\tSV_MVDStop_f);\n\tCmd_AddCommand (\"sv_democancel\",\tSV_MVD_Cancel_f);\n\tCmd_AddCommand (\"sv_demoeasyrecord\",SV_MVDEasyRecord_f);\n\tCmd_AddCommand (\"sv_demolist\",\t\tSV_MVDList_f);\n\tCmd_AddCommand (\"sv_demoremove\",\tSV_MVDRemove_f);\n\tCmd_AddCommand (\"sv_demonumremove\",\tSV_MVDRemoveNum_f);\n\n\t//old fte names to avoid conflicts.\n\tCmd_AddCommand (\"mvdrecord\",\t\tSV_MVD_Record_f);\n\tCmd_AddCommand (\"mvdstop\",\t\t\tSV_MVDStop_f);\n\tCmd_AddCommand (\"mvdcancel\",\t\tSV_MVD_Cancel_f);\n\tCmd_AddCommand (\"mvdlist\",\t\t\tSV_MVDList_f);\n#ifdef SERVER_DEMO_PLAYBACK\n\tCmd_AddCommand (\"mvdplaynum\",\t\tSV_MVDPlayNum_f);\n#endif\n\n\tCmd_AddCommand (\"sv_demoinfoadd\",\tSV_MVDInfoAdd_f);\n\tCmd_AddCommand (\"sv_demoinforemove\",SV_MVDInfoRemove_f);\n\tCmd_AddCommand (\"sv_demoinfo\",\t\tSV_MVDInfo_f);\n\n\tCmd_AddCommand (\"qtvreverse\",\t\tSV_MVD_QTVReverse_f);\n\tCvar_Register(&qtv_maxstreams, \"MVD Streaming\");\n\tCvar_Register(&qtv_password, \"MVD Streaming\");\n}\n\n#endif\n#endif\n"
  },
  {
    "path": "engine/server/sv_nchan.c",
    "content": "/*\nCopyright (C) 1996-1997 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  \n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n*/\n// sv_nchan.c, user reliable data stream writes\n\n#include \"quakedef.h\"\n#ifndef CLIENTONLY\n\n// check to see if client block will fit, if not, rotate buffers\nvoid ClientReliableCheckBlock(client_t *cl, int maxsize)\n{\n\tif (cl->num_backbuf ||\n\t\tcl->netchan.message.cursize > \n\t\tcl->netchan.message.maxsize - maxsize - 1)\n\t{\n\t\t// we would probably overflow the buffer, save it for next\n\t\tif (!cl->num_backbuf)\n\t\t{\n\t\t\tmemset(&cl->backbuf, 0, sizeof(cl->backbuf));\n\t\t\tcl->backbuf.prim = cl->netchan.message.prim;\n\t\t\tcl->backbuf.allowoverflow = true;\n\t\t\tcl->backbuf.data = cl->backbuf_data[0];\n\t\t\tcl->backbuf.maxsize = min(cl->netchan.message.maxsize, sizeof(cl->backbuf_data[0]));\n\t\t\tcl->backbuf_size[0] = 0;\n\t\t\tcl->num_backbuf++;\n\t\t}\n\n\t\tif (cl->backbuf.cursize > cl->backbuf.maxsize - maxsize - 1)\n\t\t{\n\t\t\tif (cl->num_backbuf == MAX_BACK_BUFFERS)\n\t\t\t{\n\t\t\t\tcl->backbuf.cursize = 0; // don't overflow without allowoverflow set\n\t\t\t\tcl->netchan.message.overflowed = true; // this will drop the client\n\t\t\t\tif (!cl->drop)\n\t\t\t\t\tCon_Printf (\"WARNING: MAX_BACK_BUFFERS for %s\\n\", cl->name);\n\t\t\t\tcl->drop = true;\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tmemset(&cl->backbuf, 0, sizeof(cl->backbuf));\n\t\t\tcl->backbuf.prim = cl->netchan.message.prim;\n\t\t\tcl->backbuf.allowoverflow = true;\n\t\t\tcl->backbuf.data = cl->backbuf_data[cl->num_backbuf];\n\t\t\tcl->backbuf.maxsize = min(cl->netchan.message.maxsize, sizeof(cl->backbuf_data[cl->num_backbuf]));\n\t\t\tcl->backbuf_size[cl->num_backbuf] = 0;\n\t\t\tcl->num_backbuf++;\n\t\t}\n\t}\n}\n\n// begin a client block, estimated maximum size\nvoid ClientReliableWrite_Begin(client_t *cl, int c, int maxsize)\n{\n\tif (cl->controller)\n\t\tCon_Printf(\"Writing %i to slave client's message buffer\\n\", c);\n\tClientReliableCheckBlock(cl, maxsize);\n\tClientReliableWrite_Byte(cl, c);\n}\n\nclient_t *ClientReliableWrite_BeginSplit(client_t *cl, int svc, int svclen)\n{\n\tif (cl->controller)\n\t{\t//this is a slave client.\n\t\t//find the right number and send.\n\t\tclient_t *sp;\n\t\tint pnum = 0;\n\t\tfor (sp = cl->controller; sp; sp = sp->controlled)\n\t\t{\n\t\t\tif (sp == cl)\n\t\t\t\tbreak;\n\t\t\tpnum++;\n\t\t}\n\t\tsp = cl->controller;\n\n\t\tClientReliableWrite_Begin (sp, svcfte_choosesplitclient, 2+svclen);\n\t\tClientReliableWrite_Byte (sp, pnum);\n\t\tClientReliableWrite_Byte (sp, svc);\n\t\treturn sp;\n\t}\n\telse\n\t{\n\t\tClientReliableWrite_Begin (cl, svc, svclen);\n\t\treturn cl;\n\t}\n}\n\nsizebuf_t *ClientReliable_StartWrite(client_t *cl, int maxsize)\n{\n#ifdef MVD_RECORDING\n\tif (cl == &demo.recorder)\n\t\treturn MVDWrite_Begin(dem_all, 0, maxsize);\n#endif\n\n\tif (cl->seat)\n\t{\n\t\tint pnum = cl->seat;\n\t\tcl = cl->controller;\n\t\tClientReliableWrite_Begin (cl, svcfte_choosesplitclient, 2+maxsize);\n\t\tClientReliableWrite_Byte (cl, pnum);\n\t}\n\telse\n\t\tClientReliableCheckBlock(cl, maxsize);\n\n\tif (cl->num_backbuf)\n\t\treturn &cl->backbuf;\n\telse\n\t\treturn &cl->netchan.message;\n}\nvoid ClientReliable_FinishWrite(client_t *cl)\n{\n\tif (cl->controller)\n\t\tcl = cl->controller;\n\tif (cl->num_backbuf)\n\t{\n\t\tcl->backbuf_size[cl->num_backbuf - 1] = cl->backbuf.cursize;\n\n\t\tif (cl->backbuf.overflowed)\n\t\t{\n\t\t\tif (!cl->netchan.message.overflowed)\n\t\t\t\tCon_TPrintf (\"WARNING: backbuf [%d] reliable overflow for %s\\n\",cl->num_backbuf,cl->name);\n\t\t\tcl->netchan.message.overflowed = true; // this will drop the client\n\t\t}\n\t}\n}\n\nvoid ClientReliableWrite_Angle(client_t *cl, float f)\n{\n\tif (cl->num_backbuf)\n\t{\n\t\tMSG_WriteAngle(&cl->backbuf, f);\n\t\tClientReliable_FinishWrite(cl);\n\t}\n\telse\n\t\tMSG_WriteAngle(&cl->netchan.message, f);\n}\n\nvoid ClientReliableWrite_Angle16(client_t *cl, float f)\n{\n\tif (cl->num_backbuf)\n\t{\n\t\tMSG_WriteAngle16(&cl->backbuf, f);\n\t\tClientReliable_FinishWrite(cl);\n\t}\n\telse\n\t\tMSG_WriteAngle16(&cl->netchan.message, f);\n}\n\nvoid ClientReliableWrite_Byte(client_t *cl, int c)\n{\n\tif (cl->num_backbuf)\n\t{\n\t\tMSG_WriteByte(&cl->backbuf, c);\n\t\tClientReliable_FinishWrite(cl);\n\t}\n\telse\n\t\tMSG_WriteByte(&cl->netchan.message, c);\n}\n\nvoid ClientReliableWrite_Char(client_t *cl, int c)\n{\n\tif (cl->num_backbuf)\n\t{\n\t\tMSG_WriteChar(&cl->backbuf, c);\n\t\tClientReliable_FinishWrite(cl);\n\t}\n\telse\n\t\tMSG_WriteChar(&cl->netchan.message, c);\n}\n\nvoid ClientReliableWrite_Double(client_t *cl, double f)\n{\n\tif (cl->num_backbuf)\n\t{\n\t\tMSG_WriteDouble(&cl->backbuf, f);\n\t\tClientReliable_FinishWrite(cl);\n\t}\n\telse\n\t\tMSG_WriteDouble(&cl->netchan.message, f);\n}\n\nvoid ClientReliableWrite_Float(client_t *cl, float f)\n{\n\tif (cl->num_backbuf)\n\t{\n\t\tMSG_WriteFloat(&cl->backbuf, f);\n\t\tClientReliable_FinishWrite(cl);\n\t}\n\telse\n\t\tMSG_WriteFloat(&cl->netchan.message, f);\n}\n\nvoid ClientReliableWrite_Coord(client_t *cl, float f)\n{\n\tif (cl->num_backbuf)\n\t{\n\t\tMSG_WriteCoord(&cl->backbuf, f);\n\t\tClientReliable_FinishWrite(cl);\n\t}\n\telse\n\t\tMSG_WriteCoord(&cl->netchan.message, f);\n}\n\nvoid ClientReliableWrite_Int64(client_t *cl, qint64_t c)\n{\n\tif (cl->num_backbuf)\n\t{\n\t\tMSG_WriteInt64(&cl->backbuf, c);\n\t\tClientReliable_FinishWrite(cl);\n\t}\n\telse\n\t\tMSG_WriteInt64(&cl->netchan.message, c);\n}\nvoid ClientReliableWrite_Long(client_t *cl, int c)\n{\n\tif (cl->num_backbuf)\n\t{\n\t\tMSG_WriteLong(&cl->backbuf, c);\n\t\tClientReliable_FinishWrite(cl);\n\t}\n\telse\n\t\tMSG_WriteLong(&cl->netchan.message, c);\n}\n\nvoid ClientReliableWrite_Short(client_t *cl, int c)\n{\n\tif (cl->num_backbuf)\n\t{\n\t\tMSG_WriteShort(&cl->backbuf, c);\n\t\tClientReliable_FinishWrite(cl);\n\t}\n\telse\n\t\tMSG_WriteShort(&cl->netchan.message, c);\n}\nvoid ClientReliableWrite_Entity(client_t *cl, int c)\n{\n\tif (cl->num_backbuf)\n\t{\n\t\tMSG_WriteEntity(&cl->backbuf, c);\n\t\tClientReliable_FinishWrite(cl);\n\t}\n\telse\n\t\tMSG_WriteEntity(&cl->netchan.message, c);\n}\n\nvoid ClientReliableWrite_String(client_t *cl, const char *s)\n{\n\tif (cl->num_backbuf)\n\t{\n\t\tMSG_WriteString(&cl->backbuf, s);\n\t\tClientReliable_FinishWrite(cl);\n\t}\n\telse\n\t\tMSG_WriteString(&cl->netchan.message, s);\n}\n\nvoid ClientReliableWrite_SZ(client_t *cl, const void *data, int len)\n{\n\tif (cl->num_backbuf)\n\t{\n\t\tSZ_Write(&cl->backbuf, data, len);\n\t\tClientReliable_FinishWrite(cl);\n\t}\n\telse\n\t\tSZ_Write(&cl->netchan.message, data, len);\n}\n\n\n\n#ifdef PEXT_ZLIBDL\n#include <zlib.h>\n\nvoid ClientReliableWrite_ZLib(client_t *cl, void *data, int len)\n{\n\tint i;\n\tchar out[MAX_QWMSGLEN*2];\n\n\tshort *written = (short *)((char *)cl->netchan.message.data + cl->netchan.message.cursize);\n\n\tz_stream strm = {\n\t\tdata,\n\t\tlen,\n\t\t0,\n\n\t\tNULL,//out,\n\t\tsizeof(out),\n\t\t0,\n\n\t\tNULL,\n\t\tNULL,\n\n\t\tNULL,\n\t\tNULL,\n\t\tNULL,\n\n\t\tZ_BINARY,\n\t\t0,\n\t\t0\n\t};\n\ti=0;\n\tstrm.next_out = out;\n\n\tdeflateInit(&strm, Z_BEST_COMPRESSION);\n\twhile(deflate(&strm, Z_FINISH) == Z_OK)\n\t{\n\t\tSys_Error(\"Couldn't compile well\\n\");\n//\t\tClientReliableWrite_SZ(cl, out, sizeof(out) - strm.avail_out);\t//compress in chunks of 8192. Saves having to allocate a huge-mega-big buffer\n//\t\ti+=sizeof(out) - strm.avail_out;\n//\t\tstrm.next_out = out;\n//\t\tstrm.avail_out = sizeof(out);\n\t}\n\tif (strm.total_out > len)\n\t{\n\t\tClientReliableWrite_Short(cl, 0);\n\t\tClientReliableWrite_SZ(cl, data, len);\n\t}\n\telse\n\t{\n\t\tClientReliableWrite_Short(cl, strm.total_out);\n\t\tClientReliableWrite_SZ(cl, out, sizeof(out) - strm.avail_out);\n\t}\n\ti+=sizeof(out) - strm.avail_out;\n\tdeflateEnd(&strm);\t\t\n\n//\treturn i;\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "engine/server/sv_phys.c",
    "content": "/*\nCopyright (C) 1996-1997 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n*/\n// sv_phys.c\n\n#include \"quakedef.h\"\n#if !defined(CLIENTONLY) || defined(CSQC_DAT)\n\n#include \"pr_common.h\"\n\n/*\n\n\npushmove objects do not obey gravity, and do not interact with each other or trigger fields, but block normal movement and push normal objects when they move.\n\nonground is set for toss objects when they come to a complete rest.  it is set for steping or walking objects\n\ndoors, plats, etc are SOLID_BSP, and MOVETYPE_PUSH\nbonus items are SOLID_TRIGGER touch, and MOVETYPE_TOSS\ncorpses are SOLID_NOT and MOVETYPE_TOSS\ncrates are SOLID_BBOX and MOVETYPE_TOSS\nwalking monsters are SOLID_SLIDEBOX and MOVETYPE_STEP\nflying/floating monsters are SOLID_SLIDEBOX and MOVETYPE_FLY\n\nsolid_edge items only clip against bsp models.\n\n*/\n\ncvar_t\tsv_maxvelocity = CVAR(\"sv_maxvelocity\",\"10000\");\n\ncvar_t\tsv_gravity\t\t\t = CVAR( \"sv_gravity\", \"800\");\ncvar_t\tsv_stopspeed\t\t = CVAR( \"sv_stopspeed\", \"100\");\ncvar_t\tsv_maxspeed\t\t\t = CVAR( \"sv_maxspeed\", \"320\");\ncvar_t\tsv_spectatormaxspeed = CVAR( \"sv_spectatormaxspeed\", \"500\");\ncvar_t\tsv_accelerate\t\t = CVAR( \"sv_accelerate\", \"10\");\ncvar_t\tsv_airaccelerate\t = CVAR( \"sv_airaccelerate\", \"0.7\");\ncvar_t\tsv_wateraccelerate\t = CVAR( \"sv_wateraccelerate\", \"10\");\ncvar_t\tsv_friction\t\t\t = CVAR( \"sv_friction\", \"4\");\ncvar_t\tsv_waterfriction\t = CVAR( \"sv_waterfriction\", \"4\");\ncvar_t\tsv_wallfriction\t\t = CVARD( \"sv_wallfriction\", \"1\", \"Additional friction when running into walls\");\ncvar_t\tsv_gameplayfix_noairborncorpse\t\t= CVAR(  \"sv_gameplayfix_noairborncorpse\", \"0\");\ncvar_t\tsv_gameplayfix_multiplethinks\t\t= CVARAD(\"sv_gameplayfix_multiplethinks\", \"1\", /*dp*/\"sv_gameplayfix_multiplethinksperframe\", \"Enables multiple thinks per entity per frame so small nextthink times are accurate. QuakeWorld mods expect a value of 1, while NQ expects 0.\");\ncvar_t\tsv_gameplayfix_stepdown\t\t\t\t= CVARD( \"sv_gameplayfix_stepdown\", \"0\", \"Attempt to step down steps, instead of only up them. Affects non-predicted movetype_walk.\");\ncvar_t\tsv_gameplayfix_bouncedownslopes\t\t= CVARD( \"sv_gameplayfix_grenadebouncedownslopes\", \"0\", \"MOVETYPE_BOUNCE speeds are calculated relative to the impacted surface, instead of the vertical, reducing the chance of grenades just sitting there on slopes.\");\ncvar_t\tsv_gameplayfix_trappedwithin\t\t= CVARD( \"sv_gameplayfix_trappedwithin\", \"0\", \"Blocks further entity movement when an entity is already inside another entity. This ensures that bsp precision issues cannot allow the entity to completely pass through eg the world.\");\n//cvar_t\tsv_gameplayfix_radialmaxvelocity\t= CVARD( \"sv_gameplayfix_radialmaxvelocity\", \"0\", \"Applies maxvelocity radially instead of axially.\");\n#if !defined(CLIENTONLY) && defined(NQPROT) && defined(HAVE_LEGACY)\ncvar_t\tsv_gameplayfix_spawnbeforethinks\t= CVARD( \"sv_gameplayfix_spawnbeforethinks\", \"0\", \"Fixes an issue where player thinks (including Pre+Post) can be called before PutClientInServer. Unfortunately at least one mod depends upon PreThink being called first in order to correctly determine spawn positions.\");\n#endif\ncvar_t\tdpcompat_noretouchground\t= CVARD( \"dpcompat_noretouchground\", \"0\", \"Prevents entities that are already standing on an entity from touching the same entity again.\");\ncvar_t\tsv_sound_watersplash = CVAR( \"sv_sound_watersplash\", \"misc/h2ohit1.wav\");\ncvar_t\tsv_sound_land\t\t = CVAR( \"sv_sound_land\", \"demon/dland2.wav\");\ncvar_t\tsv_stepheight\t\t = CVARAFD(\"pm_stepheight\", \"\",\t/*dp*/\"sv_stepheight\", CVAR_SERVERINFO, \"If empty, the value \"STRINGIFY(PM_DEFAULTSTEPHEIGHT)\" will be used instead. This is the size of the step you can step up or down.\");\nextern cvar_t sv_nqplayerphysics;\n\ncvar_t\tpm_ktjump\t\t\t = CVARF(\"pm_ktjump\", \"\", CVAR_SERVERINFO);\ncvar_t\tpm_bunnyspeedcap\t = CVARFD(\"pm_bunnyspeedcap\", \"\", CVAR_SERVERINFO, \"0 or 1, ish. If the player is traveling faster than this speed while turning, their velocity will be gracefully reduced to match their current maxspeed. You can still rocket-jump to gain high velocity, but turning will reduce your speed back to the max. This can be used to disable bunny hopping.\");\ncvar_t\tpm_watersinkspeed\t = CVARFD(\"pm_watersinkspeed\", \"\", CVAR_SERVERINFO, \"This is the speed that players will sink at while inactive in water. Empty means 60.\");\ncvar_t\tpm_flyfriction\t\t= CVARFD(\"pm_flyfriction\", \"\", CVAR_SERVERINFO, \"Amount of friction that applies in fly or 6dof mode. Empty means 4.\");\ncvar_t\tpm_slidefix\t\t\t = CVARFD(\"pm_slidefix\", \"\", CVAR_SERVERINFO, \"Fixes an issue when walking down slopes (ie: so they act more like slopes and not a series of steps)\");\ncvar_t\tpm_slidyslopes\t\t = CVARFD(\"pm_slidyslopes\", \"\", CVAR_SERVERINFO, \"Replicates NQ behaviour, where players will slowly slide down ramps. Generally requires 'pm_noround 1' too, otherwise the effect rounds to nothing.\");\ncvar_t\tpm_bunnyfriction\t= CVARFD(\"pm_bunnyfriction\", \"\", CVAR_SERVERINFO, \"Replicates NQ behaviour, ensuring that there's at least a frame of friction while jumping - friction is proportional to tick rate.\");\ncvar_t\tpm_autobunny\t\t= CVARFD(\"pm_autobunny\", \"\", CVAR_SERVERINFO, \"Players will continue jumping without needing to release the jump button.\");\ncvar_t\tpm_airstep\t\t\t = CVARAFD(\"pm_airstep\", \"\", /*dp*/\"sv_jumpstep\", CVAR_SERVERINFO, \"Allows players to step up while jumping. This makes stairs more graceful but also increases potential jump heights.\");\ncvar_t\tpm_pground\t\t\t = CVARFD(\"pm_pground\", \"\", CVAR_SERVERINFO, \"Use persisten onground state instead of recalculating every frame.\"CON_WARNING\"Do NOT use with nq mods, as most nq mods will interfere with onground state, resulting in glitches.\");\ncvar_t\tpm_stepdown\t\t\t = CVARFD(\"pm_stepdown\", \"\", CVAR_SERVERINFO, \"Causes physics to stick to the ground, instead of constantly losing traction while going down steps.\");\ncvar_t\tpm_walljump\t\t\t = CVARFD(\"pm_walljump\", \"\", CVAR_SERVERINFO, \"Allows the player to bounce off walls while arborne.\");\ncvar_t\tpm_edgefriction\t\t = CVARAFD(\"pm_edgefriction\", \"\", /*nq*/\"edgefriction\", CVAR_SERVERINFO, \"Increases friction when about to walk over a cliff, so you're less likely to plummet by mistake. When empty defaults to 2, but uses a tracebox instead of a traceline to detect the drop.\");\n\n#define cvargroup_serverphysics  \"server physics variables\"\nvoid WPhys_Init(void)\n{\n\tCvar_Register (&sv_maxvelocity,\t\t\t\t\t\tcvargroup_serverphysics);\n\tCvar_Register (&sv_gravity,\t\t\t\t\t\t\tcvargroup_serverphysics);\n\tCvar_Register (&sv_stopspeed,\t\t\t\t\t\tcvargroup_serverphysics);\n\tCvar_Register (&sv_maxspeed,\t\t\t\t\t\tcvargroup_serverphysics);\n\tCvar_Register (&sv_spectatormaxspeed,\t\t\t\tcvargroup_serverphysics);\n\tCvar_Register (&sv_accelerate,\t\t\t\t\t\tcvargroup_serverphysics);\n\tCvar_Register (&sv_airaccelerate,\t\t\t\t\tcvargroup_serverphysics);\n\tCvar_Register (&sv_wateraccelerate,\t\t\t\t\tcvargroup_serverphysics);\n\tCvar_Register (&sv_friction,\t\t\t\t\t\tcvargroup_serverphysics);\n\tCvar_Register (&sv_waterfriction,\t\t\t\t\tcvargroup_serverphysics);\n\tCvar_Register (&sv_wallfriction,\t\t\t\t\tcvargroup_serverphysics);\n\tCvar_Register (&sv_sound_watersplash,\t\t\t\tcvargroup_serverphysics);\n\tCvar_Register (&sv_sound_land,\t\t\t\t\t\tcvargroup_serverphysics);\n\tCvar_Register (&sv_stepheight,\t\t\t\t\t\tcvargroup_serverphysics);\n\n\tCvar_Register (&sv_gameplayfix_noairborncorpse,\t\tcvargroup_serverphysics);\n\tCvar_Register (&sv_gameplayfix_multiplethinks,\t\tcvargroup_serverphysics);\n\tCvar_Register (&sv_gameplayfix_stepdown,\t\t\tcvargroup_serverphysics);\n\tCvar_Register (&sv_gameplayfix_bouncedownslopes,\tcvargroup_serverphysics);\n\tCvar_Register (&sv_gameplayfix_trappedwithin,\t\tcvargroup_serverphysics);\n\tCvar_Register (&dpcompat_noretouchground,\t\t\tcvargroup_serverphysics);\n\n#if !defined(CLIENTONLY) && defined(NQPROT) && defined(HAVE_LEGACY)\n\tCvar_Register (&sv_gameplayfix_spawnbeforethinks,\tcvargroup_serverphysics);\n#endif\n}\n\n#define\tMOVE_EPSILON\t0.01\n\nstatic void WPhys_Physics_Toss (world_t *w, wedict_t *ent);\n\n/*\n================\nSV_CheckAllEnts\n================\n\nstatic void SV_CheckAllEnts (void)\n{\n\tint\t\t\te;\n\tedict_t\t\t*check;\n\n// see if any solid entities are inside the final position\n\tfor (e=1 ; e<sv.world.num_edicts ; e++)\n\t{\n\t\tcheck = EDICT_NUM(svprogfuncs, e);\n\t\tif (check->isfree)\n\t\t\tcontinue;\n\t\tif (check->v->movetype == MOVETYPE_PUSH\n\t\t|| check->v->movetype == MOVETYPE_NONE\n\t\t|| check->v->movetype == MOVETYPE_FOLLOW\n\t\t|| check->v->movetype == MOVETYPE_NOCLIP\n\t\t|| check->v->movetype == MOVETYPE_ANGLENOCLIP)\n\t\t\tcontinue;\n\n\t\tif (World_TestEntityPosition (&sv.world, (wedict_t*)check))\n\t\t\tCon_Printf (\"entity in invalid position\\n\");\n\t}\n}\n*/\n\n/*\n================\nSV_CheckVelocity\n================\n*/\nvoid WPhys_CheckVelocity (world_t *w, wedict_t *ent)\n{\n\tint\t\ti;\n#ifdef HAVE_SERVER\n\tif (sv_nqplayerphysics.ival)\n\t{\t//bound axially (like vanilla)\n\t\tfor (i=0 ; i<3 ; i++)\n\t\t{\n\t\t\tif (IS_NAN(ent->v->velocity[i]))\n\t\t\t{\n\t\t\t\tCon_DPrintf (\"Got a NaN velocity on entity %i (%s)\\n\", ent->entnum, PR_GetString(w->progs, ent->v->classname));\n\t\t\t\tent->v->velocity[i] = 0;\n\t\t\t}\n\t\t\tif (IS_NAN(ent->v->origin[i]))\n\t\t\t{\n\t\t\t\tCon_Printf (\"Got a NaN origin on entity %i (%s)\\n\", ent->entnum, PR_GetString(w->progs, ent->v->classname));\n\t\t\t\tent->v->origin[i] = 0;\n\t\t\t}\n\n\t\t\tif (ent->v->velocity[i] > sv_maxvelocity.value)\n\t\t\t\tent->v->velocity[i] = sv_maxvelocity.value;\n\t\t\telse if (ent->v->velocity[i] < -sv_maxvelocity.value)\n\t\t\t\tent->v->velocity[i] = -sv_maxvelocity.value;\n\t\t}\n\t}\n\telse\n#endif\n\t{\t//bound radially (for sanity)\n\t\tfor (i=0 ; i<3 ; i++)\n\t\t{\n\t\t\tif (IS_NAN(ent->v->velocity[i]))\n\t\t\t{\n\t\t\t\tCon_DPrintf (\"Got a NaN velocity on entity %i (%s)\\n\", ent->entnum, PR_GetString(w->progs, ent->v->classname));\n\t\t\t\tent->v->velocity[i] = 0;\n\t\t\t}\n\t\t\tif (IS_NAN(ent->v->origin[i]))\n\t\t\t{\n\t\t\t\tCon_Printf (\"Got a NaN origin on entity %i (%s)\\n\", ent->entnum, PR_GetString(w->progs, ent->v->classname));\n\t\t\t\tent->v->origin[i] = 0;\n\t\t\t}\n\t\t}\n\n\t\tif (Length(ent->v->velocity) > sv_maxvelocity.value)\n\t\t{\n//\t\t\tCon_DPrintf(\"Slowing %s\\n\", PR_GetString(w->progs, ent->v->classname));\n\t\t\tVectorScale (ent->v->velocity, sv_maxvelocity.value/Length(ent->v->velocity), ent->v->velocity);\n\t\t}\n\t}\n}\n\n/*\n=============\nSV_RunThink\n\nRuns thinking code if time.  There is some play in the exact time the think\nfunction will be called, because it is called before any movement is done\nin a frame.  Not used for pushmove objects, because they must be exact.\nReturns false if the entity removed itself.\n=============\n*/\nqboolean WPhys_RunThink (world_t *w, wedict_t *ent)\n{\n\tfloat\tthinktime;\n\n\tif (!sv_gameplayfix_multiplethinks.ival)\t//try and imitate nq as closeley as possible\n\t{\n\t\tthinktime = ent->v->nextthink;\n\t\tif (thinktime <= 0 || thinktime > w->physicstime + host_frametime)\n\t\t\treturn true;\n\n\t\tif (thinktime < w->physicstime)\n\t\t\tthinktime = w->physicstime;\t// don't let things stay in the past.\n\t\t\t\t\t\t\t\t\t// it is possible to start that way\n\t\t\t\t\t\t\t\t\t// by a trigger with a local time.\n\t\tent->v->nextthink = 0;\n\t\t*w->g.time = thinktime;\n\t\tw->Event_Think(w, ent);\n\t\treturn !ED_ISFREE(ent);\n\t}\n\n\tdo\n\t{\n\t\tthinktime = ent->v->nextthink;\n\t\tif (thinktime <= 0)\n\t\t\treturn true;\n\t\tif (thinktime > w->physicstime + host_frametime)\n\t\t\treturn true;\n\n\t\tif (thinktime < w->physicstime)\n\t\t\tthinktime = w->physicstime;\t// don't let things stay in the past.\n\t\t\t\t\t\t\t\t\t// it is possible to start that way\n\t\t\t\t\t\t\t\t\t// by a trigger with a local time.\n\t\tent->v->nextthink = 0;\n\n\t\t*w->g.time = thinktime;\n\t\tw->Event_Think(w, ent);\n\n\t\tif (ED_ISFREE(ent))\n\t\t\treturn false;\n\n\t\tif (ent->v->nextthink <= thinktime)\t//hmm... infinate loop was possible here.. Quite a few non-QW mods do this.\n\t\t\treturn true;\n\t} while (1);\n\n\treturn true;\n}\n\n/*\n==================\nSV_Impact\n\nTwo entities have touched, so run their touch functions\n==================\n*/\nstatic void WPhys_Impact (world_t *w, wedict_t *e1, trace_t *trace)\n{\n\twedict_t *e2 = trace->ent;\n\n\t*w->g.time = w->physicstime;\n\tif (e1->v->touch && e1->v->solid != SOLID_NOT)\n\t{\n\t\tw->Event_Touch(w, e1, e2, trace);\n\t}\n\n\tif (e2->v->touch && e2->v->solid != SOLID_NOT)\n\t{\n\t\tw->Event_Touch(w, e2, e1, trace);\n\t}\n}\n\n\n/*\n==================\nClipVelocity\n\nSlide off of the impacting object\n==================\n*/\n#define\tSTOP_EPSILON\t0.1\n//courtesy of darkplaces, it's just more efficient.\nstatic void ClipVelocity (vec3_t in, vec3_t normal, vec3_t out, float overbounce)\n{\n\tint i;\n\tfloat backoff;\n\n\tbackoff = -DotProduct (in, normal) * overbounce;\n\tVectorMA(in, backoff, normal, out);\n\n\tfor (i = 0;i < 3;i++)\n\t\tif (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON)\n\t\t\tout[i] = 0;\n}\n\nstatic void WPhys_PortalTransform(world_t *w, wedict_t *ent, wedict_t *portal, vec3_t org, vec3_t move)\n{\n\tint oself = *w->g.self;\n\tvoid *pr_globals = PR_globals(w->progs, PR_CURRENT);\n\n\t*w->g.self = EDICT_TO_PROG(w->progs, portal);\n\t//transform origin+velocity etc\n\tVectorCopy(org, G_VECTOR(OFS_PARM0));\n\tVectorCopy(ent->v->angles, G_VECTOR(OFS_PARM1));\n\tVectorCopy(ent->v->velocity, w->g.v_forward);\n\tVectorCopy(move, w->g.v_right);\n\tVectorCopy(ent->xv->gravitydir, w->g.v_up);\n\tif (!DotProduct(w->g.v_up, w->g.v_up))\n\t\tw->g.v_up[2] = -1;\n\n\tPR_ExecuteProgram (w->progs, portal->xv->camera_transform);\n\n\tVectorCopy(G_VECTOR(OFS_RETURN), org);\n\tVectorCopy(w->g.v_forward, ent->v->velocity);\n\tVectorCopy(w->g.v_right, move);\n//\tVectorCopy(w->g.v_up, ent->xv->gravitydir);\n\n\t//monsters get their gravitydir set if it isn't already, to ensure that they still work (angle issues).\n\tif ((int)ent->v->flags & FL_MONSTER)\n\t\tif (!ent->xv->gravitydir[0] && !ent->xv->gravitydir[1] && !ent->xv->gravitydir[2])\n\t\t\tent->xv->gravitydir[2] = -1;\n\n\n\t//transform the angles too\n\tVectorCopy(org, G_VECTOR(OFS_PARM0));\n#ifndef CLIENTONLY\n\tif (w == &sv.world && ent->entnum <= svs.allocated_client_slots)\n\t{\n\t\tVectorCopy(ent->v->v_angle, ent->v->angles);\n\t}\n\telse\n#endif\n\t\tent->v->angles[0] *= r_meshpitch.value;\n\tVectorCopy(ent->v->angles, G_VECTOR(OFS_PARM1));\n\tAngleVectors(ent->v->angles, w->g.v_forward, w->g.v_right, w->g.v_up);\n\tPR_ExecuteProgram (w->progs, portal->xv->camera_transform);\n\tVectorAngles(w->g.v_forward, w->g.v_up, ent->v->angles, true);\n#ifndef CLIENTONLY\n\tif (ent->entnum > 0 && ent->entnum <= svs.allocated_client_slots)\n\t{\n\t\tclient_t *cl = &svs.clients[ent->entnum-1];\n\t\tent->v->angles[0] *= r_meshpitch.value;\n\t\tVectorCopy(ent->v->angles, ent->v->v_angle);\n\t\tent->v->angles[0] *= r_meshpitch.value;\n\t\tSV_SendFixAngle(cl, NULL, FIXANGLE_AUTO, true);\n\t}\n#endif\n\n\t/*\n\tavelocity is horribly dependant upon eular angles. trying to treat it as a matrix is folly.\n\tif (DotProduct(ent->v->avelocity, ent->v->avelocity))\n\t{\n\t\tent->v->avelocity[0] *= r_meshpitch.value;\n\t\tAngleVectors(ent->v->avelocity, w->g.v_forward, w->g.v_right, w->g.v_up);\n\t\tPR_ExecuteProgram (w->progs, portal->xv->camera_transform);\n\t\tVectorAngles(w->g.v_forward, w->g.v_up, ent->v->avelocity);\n\t\tent->v->avelocity[0] *= r_meshpitch.value;\n\t}\n\t*/\n\n\t*w->g.self = oself;\n}\n\n\n\n/*\n============\nSV_FlyMove\n\nThe basic solid body movement clip that slides along multiple planes\nReturns the clipflags if the velocity was modified (hit something solid)\n1 = floor\n2 = wall / step\n4 = dead stop\nIf steptrace is not NULL, the trace of any vertical wall hit will be stored\n============\n*/\n#define\tMAX_CLIP_PLANES\t5\nstatic int WPhys_FlyMove (world_t *w, wedict_t *ent, const vec3_t gravitydir, float time, trace_t *steptrace)\n{\n\tint\t\t\tbumpcount, numbumps;\n\tvec3_t\t\tdir;\n\tfloat\t\td;\n\tint\t\t\tnumplanes;\n\tvec3_t\t\tplanes[MAX_CLIP_PLANES];\n\tvec3_t\t\tprimal_velocity, original_velocity, new_velocity;\n\tint\t\t\ti, j;\n\ttrace_t\t\ttrace;\n\tvec3_t\t\tend;\n\tfloat\t\ttime_left;\n\tint\t\t\tblocked;\n\twedict_t\t*impact;\n\tvec3_t diff;\n\n\tnumbumps = 4;\n\n\tblocked = 0;\n\tVectorCopy (ent->v->velocity, original_velocity);\n\tVectorCopy (ent->v->velocity, primal_velocity);\n\tnumplanes = 0;\n\n\ttime_left = time;\n\n\tfor (bumpcount=0 ; bumpcount<numbumps ; bumpcount++)\n\t{\n\t\tfor (i=0 ; i<3 ; i++)\n\t\t\tend[i] = ent->v->origin[i] + time_left * ent->v->velocity[i];\n\n\t\ttrace = World_Move (w, ent->v->origin, ent->v->mins, ent->v->maxs, end, MOVE_NORMAL, (wedict_t*)ent);\n\n\t\timpact = trace.ent;\n\t\tif (impact && impact->v->solid == SOLID_PORTAL)\n\t\t{\n\t\t\tvec3_t move;\n\t\t\tvec3_t from;\n\n\t\t\tVectorCopy(trace.endpos, from);\t//just in case\n\t\t\tVectorSubtract(end, trace.endpos, move);\n\t\t\tWPhys_PortalTransform(w, ent, impact, from, move);\n\t\t\tVectorAdd(from, move, end);\n\n\t\t\t//if we follow the portal, then we basically need to restart from the other side.\n\t\t\ttime_left -= time_left * trace.fraction;\n\t\t\tVectorCopy (ent->v->velocity, primal_velocity);\n\t\t\tVectorCopy (ent->v->velocity, original_velocity);\n\t\t\tnumplanes = 0;\n\n\t\t\ttrace = World_Move (w, from, ent->v->mins, ent->v->maxs, end, MOVE_NORMAL, (wedict_t*)ent);\n\t\t\timpact = trace.ent;\n\t\t}\n\n\t\tif (trace.allsolid)//should be (trace.startsolid), but that breaks compat. *sigh*\n\t\t{\t// entity is trapped in another solid\n\t\t\tVectorClear (ent->v->velocity);\n\t\t\treturn 3;\n\t\t}\n\n\t\tif (trace.fraction > 0)\n\t\t{\t// actually covered some distance\n\t\t\tVectorCopy (trace.endpos, ent->v->origin);\n\t\t\tVectorCopy (ent->v->velocity, original_velocity);\n\t\t\tnumplanes = 0;\n\t\t}\n\n\t\tif (trace.fraction == 1)\n\t\t\t break;\t\t// moved the entire distance\n\n\t\tif (!trace.ent)\n\t\t\tHost_Error (\"SV_FlyMove: !trace.ent\");\n\n\t\tif (dpcompat_noretouchground.ival)\n\t\t{\t//note: also sets onground AFTER the touch event.\n\t\t\tif (!((int)ent->v->flags&FL_ONGROUND) || ent->v->groundentity!=EDICT_TO_PROG(w->progs, trace.ent))\n\t\t\t\tWPhys_Impact (w, ent, &trace);\n\t\t}\n\n\t\tif (-DotProduct(gravitydir, trace.plane.normal) > 0.7)\n\t\t{\n\t\t\tblocked |= 1;\t\t// floor\n\t\t\tif (((wedict_t *)trace.ent)->v->solid == SOLID_BSP || dpcompat_noretouchground.ival)\n\t\t\t{\n\t\t\t\tent->v->flags =\t(int)ent->v->flags | FL_ONGROUND;\n\t\t\t\tent->v->groundentity = EDICT_TO_PROG(w->progs, trace.ent);\n\t\t\t}\n\t\t}\n\t\tif (!DotProduct(gravitydir, trace.plane.normal))\n\t\t{\n\t\t\tblocked |= 2;\t\t// step\n\t\t\tif (steptrace)\n\t\t\t\t*steptrace = trace;\t// save for player extrafriction\n\t\t}\n\n//\n// run the impact function\n//\n\t\tif (!dpcompat_noretouchground.ival)\n\t\t\tWPhys_Impact (w, ent, &trace);\n\t\tif (ED_ISFREE(ent))\n\t\t\tbreak;\t\t// removed by the impact function\n\n\n\t\ttime_left -= time_left * trace.fraction;\n\n\t// cliped to another plane\n\t\tif (numplanes >= MAX_CLIP_PLANES)\n\t\t{\t// this shouldn't really happen\n\t\t\tVectorClear (ent->v->velocity);\n\t\t\tif (steptrace)\n\t\t\t\t*steptrace = trace;\t// save for player extrafriction\n\t\t\treturn 3;\n\t\t}\n\n\t\tif (0)\n\t\t{\n\t\t\tClipVelocity(ent->v->velocity, trace.plane.normal, ent->v->velocity, 1);\n\t\t\tbreak;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (numplanes)\n\t\t\t{\n\t\t\t\tVectorSubtract(planes[0], trace.plane.normal, diff);\n\t\t\t\tif (Length(diff) < 0.01)\n\t\t\t\t\tcontinue;\t//hit this plane already\n\t\t\t}\n\n\t\t\tVectorCopy (trace.plane.normal, planes[numplanes]);\n\t\t\tnumplanes++;\n\n\t//\n\t// modify original_velocity so it parallels all of the clip planes\n\t//\n\t\t\tfor (i=0 ; i<numplanes ; i++)\n\t\t\t{\n\t\t\t\tClipVelocity (original_velocity, planes[i], new_velocity, 1);\n\t\t\t\tfor (j=0 ; j<numplanes ; j++)\n\t\t\t\t\tif (j != i)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (DotProduct (new_velocity, planes[j]) < 0)\n\t\t\t\t\t\t\tbreak;\t// not ok\n\t\t\t\t\t}\n\t\t\t\tif (j == numplanes)\n\t\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tif (i != numplanes)\n\t\t\t{\t// go along this plane\n//\t\t\t\tCon_Printf (\"%5.1f %5.1f %5.1f   \",ent->v->velocity[0], ent->v->velocity[1], ent->v->velocity[2]);\n\t\t\t\tVectorCopy (new_velocity, ent->v->velocity);\n//\t\t\t\tCon_Printf (\"%5.1f %5.1f %5.1f\\n\",ent->v->velocity[0], ent->v->velocity[1], ent->v->velocity[2]);\n\t\t\t}\n\t\t\telse\n\t\t\t{\t// go along the crease\n\t\t\t\tif (numplanes != 2)\n\t\t\t\t{\n//\t\t\t\t\tCon_Printf (\"clip velocity, numplanes == %i\\n\",numplanes);\n//\t\t\t\t\tCon_Printf (\"%5.1f %5.1f %5.1f   \",ent->v->velocity[0], ent->v->velocity[1], ent->v->velocity[2]);\n\t\t\t\t\tVectorClear (ent->v->velocity);\n//\t\t\t\t\tCon_Printf (\"%5.1f %5.1f %5.1f\\n\",ent->v->velocity[0], ent->v->velocity[1], ent->v->velocity[2]);\n\t\t\t\t\treturn 7;\n\t\t\t\t}\n//\t\t\t\tCon_Printf (\"%5.1f %5.1f %5.1f   \",ent->v->velocity[0], ent->v->velocity[1], ent->v->velocity[2]);\n\t\t\t\tCrossProduct (planes[0], planes[1], dir);\n\t\t\t\tVectorNormalize(dir);\t//fixes slow falling in corners\n\t\t\t\td = DotProduct (dir, ent->v->velocity);\n\t\t\t\tVectorScale (dir, d, ent->v->velocity);\n//\t\t\t\tCon_Printf (\"%5.1f %5.1f %5.1f\\n\",ent->v->velocity[0], ent->v->velocity[1], ent->v->velocity[2]);\n\t\t\t}\n\t\t}\n\n//\n// if original velocity is against the original velocity, stop dead\n// to avoid tiny occilations in sloping corners\n//\n\t\tif (DotProduct (ent->v->velocity, primal_velocity) <= 0)\n\t\t{\n\t\t\tVectorClear (ent->v->velocity);\n\t\t\treturn blocked;\n\t\t}\n\t}\n\n\treturn blocked;\n}\n\n\n/*\n============\nSV_AddGravity\n\n============\n*/\nstatic void WPhys_AddGravity (world_t *w, wedict_t *ent, const float *gravitydir)\n{\n\tfloat scale = ent->xv->gravity;\n\tif (!scale)\n\t\tscale = (ent->v->movetype == MOVETYPE_BOUNCEMISSILE)?0.5:1.0;\n\n\tVectorMA(ent->v->velocity, scale * movevars.gravity * host_frametime, gravitydir, ent->v->velocity);\n}\n\n/*\n===============================================================================\n\nPUSHMOVE\n\n===============================================================================\n*/\n\n/*\n============\nSV_PushEntity\n\nDoes not change the entities velocity at all\n============\n*/\nstatic trace_t WPhys_PushEntity (world_t *w, wedict_t *ent, vec3_t push, unsigned int traceflags)\n{\n\ttrace_t\ttrace;\n\tvec3_t\tend;\n\twedict_t *impact;\n\n\tVectorAdd (ent->v->origin, push, end);\n\n\tif ((int)ent->v->flags&FLQW_LAGGEDMOVE)\n\t\ttraceflags |= MOVE_LAGGED;\n\n\tif (ent->v->movetype == MOVETYPE_FLYMISSILE)\n\t\ttraceflags |= MOVE_MISSILE;\n\telse if (ent->v->solid == SOLID_TRIGGER || ent->v->solid == SOLID_NOT)\n\t// only clip against bmodels\n\t\ttraceflags |= MOVE_NOMONSTERS;\n\telse\n\t\ttraceflags |= MOVE_NORMAL;\n\n\ttrace = World_Move (w, ent->v->origin, ent->v->mins, ent->v->maxs, end, traceflags, (wedict_t*)ent);\n\n\timpact = trace.ent;\n\tif (impact && impact->v->solid == SOLID_PORTAL)\n\t{\n\t\tvec3_t move;\n\t\tvec3_t from;\n\t\tfloat firstfrac = trace.fraction;\n\t\tVectorCopy(trace.endpos, from);\t//just in case\n\t\tVectorSubtract(end, trace.endpos, move);\n\t\tWPhys_PortalTransform(w, ent, impact, from, move);\n\t\tVectorAdd(from, move, end);\n\t\ttrace = World_Move (w, from, ent->v->mins, ent->v->maxs, end, traceflags, (wedict_t*)ent);\n\t\ttrace.fraction = firstfrac + (1-firstfrac)*trace.fraction;\n\t}\n\n\t/*hexen2's movetype_swim does not allow swimming entities to move out of water. this implementation is quite hacky, but matches hexen2 well enough*/\n\tif (ent->v->movetype == MOVETYPE_H2SWIM)\n\t{\n\t\tif (!(w->worldmodel->funcs.PointContents(w->worldmodel, NULL, trace.endpos) & (FTECONTENTS_WATER|FTECONTENTS_SLIME|FTECONTENTS_LAVA)))\n\t\t{\n\t\t\tVectorCopy(ent->v->origin, trace.endpos);\n\t\t\ttrace.fraction = 0;\n\t\t\ttrace.ent = w->edicts;\n\t\t}\n\t}\n#if defined(HAVE_SERVER) && defined(HEXEN2)\n\telse if (ent->v->solid == SOLID_PHASEH2 && progstype == PROG_H2 && w == &sv.world && trace.fraction != 1 && trace.ent &&\n\t\t(((int)((wedict_t*)trace.ent)->v->flags & FL_MONSTER) || (int)((wedict_t*)trace.ent)->v->movetype == MOVETYPE_WALK))\n\t{\t//hexen2's SOLID_PHASEH2 ents should pass through players+monsters, yet still trigger impacts. I would use MOVE_ENTCHAIN but that would corrupt .chain, perhaps that's okay though?\n\n\t\t//continue the trace on to where we wold be if there had been no impact\n\t\ttrace_t trace2 = World_Move (w, trace.endpos, ent->v->mins, ent->v->maxs, end, traceflags|MOVE_NOMONSTERS|MOVE_MISSILE|MOVE_RESERVED/*Don't fuck up in the face of dp's MOVE_WORLDONLY*/, (wedict_t*)ent);\n\n\t\t//do the first non-world impact\n\t//\tif (trace.ent)\n\t//\t\tVectorMA(trace.endpos, sv_impactpush.value, trace.plane.normal, ent->v->origin);\n\t//\telse\n\t\t\tVectorCopy (trace.endpos, ent->v->origin);\n\t\tWorld_LinkEdict (w, ent, true);\n\n\t\tif (trace.ent)\n\t\t{\n\t\t\tWPhys_Impact (w, ent, &trace);\n\t\t\tif (ent->ereftype != ER_ENTITY)\n\t\t\t\treturn trace;\t//someone remove()d it. don't do weird stuff.\n\t\t}\n\n\t\t//and use our regular impact logic for the rest of it.\n\t\ttrace = trace2;\n\t}\n#endif\n\n//\tif (trace.ent)\n//\t\tVectorMA(trace.endpos, sv_impactpush.value, trace.plane.normal, ent->v->origin);\n//\telse\n\t\tVectorCopy (trace.endpos, ent->v->origin);\n\tWorld_LinkEdict (w, ent, true);\n\n\tif (trace.ent)\n\t\tWPhys_Impact (w, ent, &trace);\n\n\treturn trace;\n}\n\n\n\n\ntypedef struct\n{\n\twedict_t\t*ent;\n\tvec3_t\torigin;\n\tvec3_t\tangles;\n} pushed_t;\nstatic pushed_t\tpushed[1024], *pushed_p;\n\n/*\n============\nSV_Push\n\nObjects need to be moved back on a failed push,\notherwise riders would continue to slide.\n============\n*/\nstatic qboolean WPhys_PushAngles (world_t *w, wedict_t *pusher, vec3_t move, vec3_t amove)\n{\n\tint\t\t\ti, e;\n\twedict_t\t*check, *block;\n\tvec3_t\t\tmins, maxs;\n\t//float oldsolid;\n\tpushed_t\t*p;\n\tvec3_t\t\torg, org2, move2, forward, right, up;\n#ifdef HAVE_SERVER\n\tshort yawchange = (amove[PITCH]||amove[ROLL])?0:ANGLE2SHORT(amove[YAW]);\n#endif\n\n\tpushed_p = pushed;\n\n\t// find the bounding box\n\tfor (i=0 ; i<3 ; i++)\n\t{\n\t\tmins[i] = pusher->v->absmin[i] + move[i];\n\t\tmaxs[i] = pusher->v->absmax[i] + move[i];\n\t}\n\n// we need this for pushing things later\n\tVectorNegate (amove, org);\n\tAngleVectors (org, forward, right, up);\n\n// save the pusher's original position\n\tpushed_p->ent = pusher;\n\tVectorCopy (pusher->v->origin, pushed_p->origin);\n\tVectorCopy (pusher->v->angles, pushed_p->angles);\n\tpushed_p++;\n\n// move the pusher to it's final position\n\tVectorAdd (pusher->v->origin, move, pusher->v->origin);\n\tVectorAdd (pusher->v->angles, amove, pusher->v->angles);\n\tWorld_LinkEdict (w, pusher, false);\n\n// see if any solid entities are inside the final position\n\tif (pusher->v->movetype != MOVETYPE_H2PUSHPULL)\n\tfor (e = 1; e < w->num_edicts; e++)\n\t{\n\t\tcheck = WEDICT_NUM_PB(w->progs, e);\n\t\tif (ED_ISFREE(check))\n\t\t\tcontinue;\n\n\t\tif (check->v->movetype == MOVETYPE_PUSH\n\t\t|| check->v->movetype == MOVETYPE_NONE\n\t\t|| check->v->movetype == MOVETYPE_NOCLIP\n\t\t|| check->v->movetype == MOVETYPE_ANGLENOCLIP)\n\t\t\tcontinue;\n/*\n\t\toldsolid = pusher->v->solid;\n\t\tpusher->v->solid = SOLID_NOT;\n\t\tblock = World_TestEntityPosition (w, check);\n\t\tpusher->v->solid = oldsolid;\n\t\tif (block)\n\t\t\tcontinue;\n*/\n\t// if the entity is standing on the pusher, it will definitely be moved\n\t\tif ( ! ( ((int)check->v->flags & FL_ONGROUND)\n\t\t\t&& PROG_TO_WEDICT(w->progs, check->v->groundentity) == pusher) )\n\t\t{\n\t\t\t// see if the ent needs to be tested\n\t\t\tif ( check->v->absmin[0] >= maxs[0]\n\t\t\t|| check->v->absmin[1] >= maxs[1]\n\t\t\t|| check->v->absmin[2] >= maxs[2]\n\t\t\t|| check->v->absmax[0] <= mins[0]\n\t\t\t|| check->v->absmax[1] <= mins[1]\n\t\t\t|| check->v->absmax[2] <= mins[2] )\n\t\t\t\tcontinue;\n\n\n\t\t\t// see if the ent's bbox is inside the pusher's final position\n\t\t\tif (!World_TestEntityPosition (w, (wedict_t*)check))\n\t\t\t\tcontinue;\n\t\t}\n\n\t\tif ((pusher->v->movetype == MOVETYPE_PUSH) || (PROG_TO_WEDICT(w->progs, check->v->groundentity) == pusher))\n\t\t{\n\t\t\tif (pushed_p == (pushed+(sizeof(pushed)/sizeof(pushed[0]))))\n\t\t\t\tcontinue;\n\t\t\t// move this entity\n\t\t\tpushed_p->ent = check;\n\t\t\tVectorCopy (check->v->origin, pushed_p->origin);\n\t\t\tVectorCopy (check->v->angles, pushed_p->angles);\n\t\t\tpushed_p++;\n\n\t\t\t// try moving the contacted entity\n\t\t\tVectorAdd (check->v->origin, move, check->v->origin);\n\t\t\tVectorAdd (check->v->angles, amove, check->v->angles);\n#ifdef HAVE_SERVER\n\t\t\tif (w == &sv.world && check->entnum>0&&(check->entnum)<=sv.allocated_client_slots)\n\t\t\t\tsvs.clients[check->entnum-1].baseangles[YAW] += yawchange;\n#endif\n\n\t\t\t// figure movement due to the pusher's amove\n\t\t\tVectorSubtract (check->v->origin, pusher->v->origin, org);\n\t\t\torg2[0] = DotProduct (org, forward);\n\t\t\torg2[1] = -DotProduct (org, right);\n\t\t\torg2[2] = DotProduct (org, up);\n\t\t\tVectorSubtract (org2, org, move2);\n\t\t\tVectorAdd (check->v->origin, move2, check->v->origin);\n\n\t\t\tif (check->v->movetype != MOVETYPE_WALK)\n\t\t\t\tcheck->v->flags = (int)check->v->flags & ~FL_ONGROUND;\n\n\t\t\t// may have pushed them off an edge\n\t\t\tif (PROG_TO_WEDICT(w->progs, check->v->groundentity) != pusher)\n\t\t\t\tcheck->v->groundentity = 0;\n\n\t\t\tblock = World_TestEntityPosition (w, check);\n\t\t\tif (!block)\n\t\t\t{\t// pushed ok\n\t\t\t\tWorld_LinkEdict (w, check, false);\n\t\t\t\t// impact?\n\t\t\t\tcontinue;\n\t\t\t}\n\n\n\n\t\t\t// if it is ok to leave in the old position, do it\n\t\t\t// this is only relevent for riding entities, not pushed\n\t\t\t// FIXME: this doesn't acount for rotation\n\t\t\tVectorCopy (pushed_p[-1].origin, check->v->origin);\n\t\t\tblock = World_TestEntityPosition (w, check);\n\t\t\tif (!block)\n\t\t\t{\n\t\t\t\tpushed_p--;\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t//okay, that didn't work, try pushing the against stuff\n\t\t\tWPhys_PushEntity(w, check, move, 0);\n\t\t\tblock = World_TestEntityPosition (w, check);\n\t\t\tif (!block)\n\t\t\t\tcontinue;\n\n\t\t\tVectorCopy(check->v->origin, move);\n\t\t\tfor (i = 0; i < 8 && block; i++)\n\t\t\t{\n\t\t\t\t//precision errors can strike when you least expect it. lets try and reduce them.\n\t\t\t\tcheck->v->origin[0] = move[0] + ((i&1)?-1:1)/8.0;\n\t\t\t\tcheck->v->origin[1] = move[1] + ((i&2)?-1:1)/8.0;\n\t\t\t\tcheck->v->origin[2] = move[2] + ((i&4)?-1:1)/8.0;\n\t\t\t\tblock = World_TestEntityPosition (w, check);\n\t\t\t}\n\t\t\tif (!block)\n\t\t\t{\n\t\t\t\tWorld_LinkEdict (w, check, false);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\n\t\t// if it is sitting on top. Do not block.\n\t\tif (check->v->mins[0] == check->v->maxs[0])\n\t\t{\n\t\t\tWorld_LinkEdict (w, check, false);\n\t\t\tcontinue;\n\t\t}\n\n\t\t//some pushers are contents brushes, and are not solid. water cannot crush. the player just enters the water.\n\t\t//but, the player will be moved along with the water if possible.\n\t\tif (pusher->v->skin < 0)\n\t\t\tcontinue;\n\n\t\tif (check->v->solid == SOLID_NOT || check->v->solid == SOLID_TRIGGER)\n\t\t{\t// corpse\n\t\t\tcheck->v->mins[0] = check->v->mins[1] = 0;\n\t\t\tVectorCopy (check->v->mins, check->v->maxs);\n\t\t\tWorld_LinkEdict (w, check, false);\n\t\t\tcontinue;\n\t\t}\n\n//\t\tCon_Printf(\"Pusher hit %s\\n\", PR_GetString(w->progs, check->v->classname));\n\t\tif (pusher->v->blocked)\n\t\t{\n\t\t\t*w->g.self = EDICT_TO_PROG(w->progs, pusher);\n\t\t\t*w->g.other = EDICT_TO_PROG(w->progs, check);\n#ifdef VM_Q1\n\t\t\tif (w==&sv.world && svs.gametype == GT_Q1QVM)\n\t\t\t\tQ1QVM_Blocked();\n\t\t\telse\n#endif\n\t\t\t\tPR_ExecuteProgram (w->progs, pusher->v->blocked);\n\t\t}\n\n\t\t// move back any entities we already moved\n\t\t// go backwards, so if the same entity was pushed\n\t\t// twice, it goes back to the original position\n\t\tfor (p=pushed_p-1 ; p>=pushed ; p--)\n\t\t{\n\t\t\tVectorCopy (p->origin, p->ent->v->origin);\n\t\t\tVectorCopy (p->angles, p->ent->v->angles);\n\t\t\tWorld_LinkEdict (w, p->ent, false);\n\n#ifdef HAVE_SERVER\n\t\t\tif (w==&sv.world && p->ent->entnum>0&&(p->ent->entnum)<=sv.allocated_client_slots)\n\t\t\t\tsvs.clients[p->ent->entnum-1].baseangles[YAW] -= yawchange;\n#endif\n\t\t}\n\t\treturn false;\n\t}\n\n//FIXME: is there a better way to handle this?\n\t// see if anything we moved has touched a trigger\n\tfor (p=pushed_p-1 ; p>=pushed ; p--)\n\t\tWorld_TouchAllLinks (w, p->ent);\n\n\treturn true;\n}\n\n/*\n============\nSV_Push\n\n============\n*/\nqboolean WPhys_Push (world_t *w, wedict_t *pusher, vec3_t move, vec3_t amove)\n{\n#define PUSHABLE_LIMIT 8192\n\tint\t\t\ti, e;\n\twedict_t\t*check, *block;\n\tvec3_t\t\tmins, maxs;\n\tvec3_t\t\tpushorig;\n\tint\t\t\tnum_moved;\n\twedict_t\t*moved_edict[PUSHABLE_LIMIT];\n\tvec3_t\t\tmoved_from[PUSHABLE_LIMIT];\n\tfloat oldsolid;\n\n\tif ((amove[0] || amove[1] || amove[2]) && !w->remasterlogic)\n\t{\n\t\treturn WPhys_PushAngles(w, pusher, move, amove);\n\t}\n\n\tfor (i=0 ; i<3 ; i++)\n\t{\n\t\tmins[i] = pusher->v->absmin[i] + move[i]-(1/32.0);\n\t\tmaxs[i] = pusher->v->absmax[i] + move[i]+(1/32.0);\n\t}\n\n\tVectorCopy (pusher->v->origin, pushorig);\n\n// move the pusher to it's final position\n\n\tVectorAdd (pusher->v->origin, move, pusher->v->origin);\n\tWorld_LinkEdict (w, pusher, false);\n\n// see if any solid entities are inside the final position\n\tnum_moved = 0;\n\tfor (e=1 ; e<w->num_edicts ; e++)\n\t{\n\t\tcheck = WEDICT_NUM_PB(w->progs, e);\n\t\tif (ED_ISFREE(check))\n\t\t\tcontinue;\n\t\tif (check->v->movetype == MOVETYPE_PUSH\n\t\t|| check->v->movetype == MOVETYPE_NONE\n\t\t|| check->v->movetype == MOVETYPE_FOLLOW\n\t\t|| check->v->movetype == MOVETYPE_NOCLIP\n\t\t|| check->v->movetype == MOVETYPE_ANGLENOCLIP)\n\t\t\tcontinue;\n\n\t// if the entity is standing on the pusher, it will definately be moved\n\t\tif ( ! ( ((int)check->v->flags & FL_ONGROUND)\n\t\t&&\n\t\t\tPROG_TO_WEDICT(w->progs, check->v->groundentity) == pusher) )\n\t\t{\n\t\t\tif ( check->v->absmin[0] >= maxs[0]\n\t\t\t|| check->v->absmin[1] >= maxs[1]\n\t\t\t|| check->v->absmin[2] >= maxs[2]\n\t\t\t|| check->v->absmax[0] <= mins[0]\n\t\t\t|| check->v->absmax[1] <= mins[1]\n\t\t\t|| check->v->absmax[2] <= mins[2] )\n\t\t\t\tcontinue;\n\n\t\t// see if the ent's bbox is inside the pusher's final position\n\t\t\tif (!World_TestEntityPosition (w, check))\n\t\t\t\tcontinue;\n\t\t}\n\n\t\toldsolid = pusher->v->solid;\n\t\tpusher->v->solid = SOLID_NOT;\n\t\tblock = World_TestEntityPosition (w, check);\n\t\tpusher->v->solid = oldsolid;\n\t\tif (block)\n\t\t\tcontinue;\n\n\t\tif (num_moved == PUSHABLE_LIMIT)\n\t\t\tbreak;\n\n\t\tVectorCopy (check->v->origin, moved_from[num_moved]);\n\t\tmoved_edict[num_moved] = check;\n\t\tnum_moved++;\n\n\t\tif (check->v->groundentity != pusher->entnum)\n\t\t\tcheck->v->flags = (int)check->v->flags & ~FL_ONGROUND;\n\n\t\t// try moving the contacted entity\n\t\tVectorAdd (check->v->origin, move, check->v->origin);\n\t\tif (pusher->v->skin < 0)\n\t\t{\n\t\t\tpusher->v->solid = SOLID_NOT;\n\t\t\tblock = World_TestEntityPosition (w, check);\n\t\t\tpusher->v->solid = oldsolid;\n\t\t}\n\t\telse\n\t\t\tblock = World_TestEntityPosition (w, check);\n\t\tif (!block)\n\t\t{\t// pushed ok\n\t\t\tWorld_LinkEdict (w, check, false);\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (block)\n\t\t{\n\t\t\t//try to nudge it forward by an epsilon to avoid precision issues\n\t\t\tfloat movelen = VectorLength(move);\n\t\t\tVectorMA(check->v->origin, (1/8.0)/movelen, move, check->v->origin);\n\t\t\tblock = World_TestEntityPosition (w, check);\n\t\t\tif (!block)\n\t\t\t{\t//okay, that got it. we're all good.\n\t\t\t\tWorld_LinkEdict (w, check, false);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\n\t\t// if it is ok to leave in the old position, do it\n\t\tVectorCopy (moved_from[num_moved-1], check->v->origin);\n\t\tblock = World_TestEntityPosition (w, check);\n\t\tif (!block)\n\t\t{\n\t\t\t//if leaving it where it was, allow it to drop to the floor again (useful for plats that move downward)\n\t\t\tif (check->v->movetype != MOVETYPE_WALK)\n\t\t\t\tcheck->v->flags = (int)check->v->flags & ~FL_ONGROUND;\n\n\t\t\tnum_moved--;\n\t\t\tcontinue;\n\t\t}\n\n\t// its blocking us. this is probably a problem.\n\n\t\t//corpses \n\t\tif (check->v->mins[0] == check->v->maxs[0])\n\t\t{\n\t\t\tWorld_LinkEdict (w, check, false);\n\t\t\tcontinue;\n\t\t}\n\t\tif (check->v->solid == SOLID_NOT || check->v->solid == SOLID_TRIGGER)\n\t\t{\t// corpse\n\t\t\tcheck->v->mins[0] = check->v->mins[1] = 0;\n\t\t\tVectorCopy (check->v->mins, check->v->maxs);\n\t\t\tWorld_LinkEdict (w, check, false);\n\t\t\tcontinue;\n\t\t}\n\n\t\t//these pushers are contents brushes, and are not solid. water cannot crush. the player just enters the water.\n\t\t//but, the player will be moved along with the water.\n\t\tif (pusher->v->skin < 0)\n\t\t\tcontinue;\n\n\t\tVectorCopy (pushorig, pusher->v->origin);\n\t\tWorld_LinkEdict (w, pusher, false);\n\n\t\t// if the pusher has a \"blocked\" function, call it\n\t\t// otherwise, just stay in place until the obstacle is gone\n\t\tif (pusher->v->blocked)\n\t\t{\n\t\t\t*w->g.self = EDICT_TO_PROG(w->progs, pusher);\n\t\t\t*w->g.other = EDICT_TO_PROG(w->progs, check);\n#ifdef VM_Q1\n\t\t\tif (w==&sv.world && svs.gametype == GT_Q1QVM)\n\t\t\t\tQ1QVM_Blocked();\n\t\t\telse\n#endif\n\t\t\t\tPR_ExecuteProgram (w->progs, pusher->v->blocked);\n\t\t} else {\n\t\t\t*w->g.other = 0;\n\t\t}\n\n\t// move back any entities we already moved\n\t\tfor (i=0 ; i<num_moved ; i++)\n\t\t{\n\t\t\tVectorCopy (moved_from[i], moved_edict[i]->v->origin);\n\t\t\tWorld_LinkEdict (w, moved_edict[i], false);\n\t\t}\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\n\n/*\n============\nSV_PushMove\n\n============\n*/\nstatic void WPhys_PushMove (world_t *w, wedict_t *pusher, float movetime)\n{\n\tint\t\t\ti;\n\tvec3_t\t\tmove;\n\tvec3_t\t\tamove;\n\n\tif (!pusher->v->velocity[0] && !pusher->v->velocity[1] && !pusher->v->velocity[2]\n\t\t&& !pusher->v->avelocity[0] && !pusher->v->avelocity[1] && !pusher->v->avelocity[2])\n\t{\n\t\tpusher->v->ltime += movetime;\n\t\treturn;\n\t}\n\n\tfor (i=0 ; i<3 ; i++)\n\t{\n\t\tmove[i] = pusher->v->velocity[i] * movetime;\n\t\tamove[i] = pusher->v->avelocity[i] * movetime;\n\t}\n\n\tif (WPhys_Push (w, pusher, move, amove))\n\t\tpusher->v->ltime += movetime;\n}\n\n\n/*\n================\nSV_Physics_Pusher\n\n================\n*/\nstatic void WPhys_Physics_Pusher (world_t *w, wedict_t *ent)\n{\n\tfloat\tthinktime;\n\tfloat\toldltime;\n\tfloat\tmovetime;\nvec3_t oldorg, move;\nvec3_t oldang, amove;\nfloat\tl;\n\n\toldltime = ent->v->ltime;\n\n\tthinktime = ent->v->nextthink;\n\tif (thinktime < ent->v->ltime + host_frametime)\n\t{\n\t\tmovetime = thinktime - ent->v->ltime;\n\t\tif (movetime < 0)\n\t\t\tmovetime = 0;\n\t}\n\telse\n\t\tmovetime = host_frametime;\n\n\tif (movetime)\n\t{\n\t\tWPhys_PushMove (w, ent, movetime);\t// advances ent->v->ltime if not blocked\n\t}\n\n\tif (thinktime > oldltime && thinktime <= ent->v->ltime)\n\t{\nVectorCopy (ent->v->origin, oldorg);\nVectorCopy (ent->v->angles, oldang);\n\t\tent->v->nextthink = 0;\n#if 1\n\t\t*w->g.time = w->physicstime;\n\t\tw->Event_Think(w, ent);\n#else\n\t\tpr_global_struct->time = sv.world.physicstime;\n\t\tpr_global_struct->self = EDICT_TO_PROG(w->progs, ent);\n\t\tpr_global_struct->other = EDICT_TO_PROG(w->progs, w->edicts);\n#ifdef VM_Q1\n\t\tif (svs.gametype == GT_Q1QVM)\n\t\t\tQ1QVM_Think();\n\t\telse\n#endif\n\t\t\tPR_ExecuteProgram (svprogfuncs, ent->v->think);\n#endif\n\t\tif (ED_ISFREE(ent))\n\t\t\treturn;\nVectorSubtract (ent->v->origin, oldorg, move);\nVectorSubtract (ent->v->angles, oldang, amove);\n\nl = Length(move)+Length(amove);\nif (l > 1.0/64)\n{\n//\tCon_Printf (\"**** snap: %f\\n\", Length (l));\n\tVectorCopy (oldorg, ent->v->origin);\n\tVectorCopy (oldang, ent->v->angles);\n\tWPhys_Push (w, ent, move, amove);\n}\n\n\t}\n\n}\n\n\n/*\n=============\nSV_Physics_Follow\n\nEntities that are \"stuck\" to another entity\n=============\n*/\nstatic void WPhys_Physics_Follow (world_t *w, wedict_t *ent)\n{\n\tvec3_t vf, vr, vu, angles, v;\n\twedict_t *e;\n\n\t// regular thinking\n\tif (!WPhys_RunThink (w, ent))\n\t\treturn;\n\n\t// LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects\n\te = PROG_TO_WEDICT(w->progs, ent->v->aiment);\n\tif (e->v->angles[0] == ent->xv->punchangle[0] && e->v->angles[1] == ent->xv->punchangle[1] && e->v->angles[2] == ent->xv->punchangle[2])\n\t{\n\t\t// quick case for no rotation\n\t\tVectorAdd(e->v->origin, ent->v->view_ofs, ent->v->origin);\n\t}\n\telse\n\t{\n\t\tangles[0] = ent->xv->punchangle[0] * r_meshpitch.value;\n\t\tangles[1] = ent->xv->punchangle[1];\n\t\tangles[2] = ent->xv->punchangle[2] * r_meshroll.value;\n\t\tAngleVectors (angles, vf, vr, vu);\n\t\tv[0] = ent->v->view_ofs[0] * vf[0] + ent->v->view_ofs[1] * vr[0] + ent->v->view_ofs[2] * vu[0];\n\t\tv[1] = ent->v->view_ofs[0] * vf[1] + ent->v->view_ofs[1] * vr[1] + ent->v->view_ofs[2] * vu[1];\n\t\tv[2] = ent->v->view_ofs[0] * vf[2] + ent->v->view_ofs[1] * vr[2] + ent->v->view_ofs[2] * vu[2];\n\t\tangles[0] = e->v->angles[0] * r_meshpitch.value;\n\t\tangles[1] = e->v->angles[1];\n\t\tangles[2] = e->v->angles[2] * r_meshroll.value;\n\t\tAngleVectors (angles, vf, vr, vu);\n\t\tent->v->origin[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + e->v->origin[0];\n\t\tent->v->origin[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + e->v->origin[1];\n\t\tent->v->origin[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + e->v->origin[2];\n\t}\n\tVectorAdd (e->v->angles, ent->v->v_angle, ent->v->angles);\n\tWorld_LinkEdict (w, ent, true);\n}\n\n/*\n=============\nSV_Physics_Noclip\n\nA moving object that doesn't obey physics\n=============\n*/\nstatic void WPhys_Physics_Noclip (world_t *w, wedict_t *ent)\n{\n\tvec3_t end;\n#ifdef HAVE_SERVER\n\ttrace_t trace;\n\twedict_t *impact;\n#endif\n\n// regular thinking\n\tif (!WPhys_RunThink (w, ent))\n\t\treturn;\n\n\tVectorMA (ent->v->angles, host_frametime, ent->v->avelocity, ent->v->angles);\n\tVectorMA (ent->v->origin, host_frametime, ent->v->velocity, end);\n\n#ifdef HAVE_SERVER\n\t//allow spectators to no-clip through portals without bogging down sock's mods.\n\tif (ent->entnum > 0 && ent->entnum <= sv.allocated_client_slots && w == &sv.world)\n\t{\n\t\ttrace = World_Move (w, ent->v->origin, ent->v->mins, ent->v->maxs, end, MOVE_NOMONSTERS, (wedict_t*)ent);\n\t\timpact = trace.ent;\n\t\tif (impact && impact->v->solid == SOLID_PORTAL)\n\t\t{\n\t\t\tvec3_t move;\n\t\t\tvec3_t from;\n\t\t\tVectorCopy(trace.endpos, from);\t//just in case\n\t\t\tVectorSubtract(end, trace.endpos, move);\n\t\t\tWPhys_PortalTransform(w, ent, impact, from, move);\n\t\t\tVectorAdd(from, move, end);\n\t\t}\n\t}\n#endif\n\n\tVectorCopy(end, ent->v->origin);\n\n\tWorld_LinkEdict (w, (wedict_t*)ent, false);\n}\n\n/*\n==============================================================================\n\nTOSS / BOUNCE\n\n==============================================================================\n*/\n\n/*\n=============\nSV_CheckWaterTransition\n\n=============\n*/\nstatic void WPhys_CheckWaterTransition (world_t *w, wedict_t *ent)\n{\n\tint\t\tcont;\n\n\tcont = World_PointContentsWorldOnly (w, ent->v->origin);\n\n\t//needs to be q1 progs compatible\n\tif (cont & FTECONTENTS_LAVA)\n\t\tcont = Q1CONTENTS_LAVA;\n\telse if (cont & FTECONTENTS_SLIME)\n\t\tcont = Q1CONTENTS_SLIME;\n\telse if (cont & FTECONTENTS_WATER)\n\t\tcont = Q1CONTENTS_WATER;\n\telse\n\t\tcont = Q1CONTENTS_EMPTY;\n\n\tif (!ent->v->watertype)\n\t{\t// just spawned here\n\t\tent->v->watertype = cont;\n\t\tent->v->waterlevel = 1;\n\t\treturn;\n\t}\n\n\tif (ent->v->watertype != cont && w->Event_ContentsTransition(w, ent, ent->v->watertype, cont))\n\t{\n\t\tent->v->watertype = cont;\n\t\tent->v->waterlevel = 1;\n\t}\n\n\telse if (cont <= Q1CONTENTS_WATER)\n\t{\n\t\tif (ent->v->watertype == Q1CONTENTS_EMPTY && *sv_sound_watersplash.string)\n\t\t{\t// just crossed into water\n\t\t\tw->Event_Sound(NULL, ent, 0, sv_sound_watersplash.string, 255, 1, 0, 0, 0);\n\t\t}\n\t\tent->v->watertype = cont;\n\t\tent->v->waterlevel = 1;\n\t}\n\telse\n\t{\n\t\tif (ent->v->watertype != Q1CONTENTS_EMPTY && *sv_sound_watersplash.string)\n\t\t{\t// just crossed into open\n\t\t\tw->Event_Sound(NULL, ent, 0, sv_sound_watersplash.string, 255, 1, 0, 0, 0);\n\t\t}\n\t\tent->v->watertype = Q1CONTENTS_EMPTY;\n\t\tent->v->waterlevel = cont;\n\t}\n}\n\n/*\n=============\nSV_Physics_Toss\n\nToss, bounce, and fly movement.  When onground, do nothing.\n=============\n*/\nstatic void WPhys_Physics_Toss (world_t *w, wedict_t *ent)\n{\n\ttrace_t\ttrace;\n\tvec3_t\tmove;\n\tfloat\tbackoff;\n\n\tint fl;\n\tconst float *gravitydir;\n\tint movetype;\n\n\tWPhys_CheckVelocity (w, ent);\n\n// regular thinking\n\tif (!WPhys_RunThink (w, ent))\n\t\treturn;\n\n\tif (ent->xv->gravitydir[2] || ent->xv->gravitydir[1] || ent->xv->gravitydir[0])\n\t\tgravitydir = ent->xv->gravitydir;\n\telse\n\t\tgravitydir = w->g.defaultgravitydir;\n\n// if onground, return without moving\n\tif ( ((int)ent->v->flags & FL_ONGROUND) )\n\t{\n\t\tif (-DotProduct(gravitydir, ent->v->velocity) >= (1.0f/32.0f))\n\t\t\tent->v->flags = (int)ent->v->flags & ~FL_ONGROUND;\n\t\telse\n\t\t{\n\t\t\tif (sv_gameplayfix_noairborncorpse.value)\n\t\t\t{\n\t\t\t\twedict_t *onent;\n\t\t\t\tonent = PROG_TO_WEDICT(w->progs, ent->v->groundentity);\n\t\t\t\tif (!ED_ISFREE(onent))\n\t\t\t\t\treturn;\t//don't drop if our fround is still valid\n\t\t\t}\n\t\t\telse\n\t\t\t\treturn;\t//don't drop, even if the item we were on was removed (certain dm maps do this for q3 style stuff).\n\t\t}\n\t}\n\n// add gravity\n\tmovetype = ent->v->movetype;\n\tif (movetype != MOVETYPE_FLY\n\t\t&& movetype != MOVETYPE_FLY_WORLDONLY\n\t\t&& movetype != MOVETYPE_FLYMISSILE\n\t\t&& (movetype != MOVETYPE_BOUNCEMISSILE || w->remasterlogic/*gib*/)\n\t\t&& movetype != MOVETYPE_H2SWIM)\n\t\tWPhys_AddGravity (w, ent, gravitydir);\n\n// move angles\n\tVectorMA (ent->v->angles, host_frametime, ent->v->avelocity, ent->v->angles);\n\n// move origin\n\tVectorScale (ent->v->velocity, host_frametime, move);\n\tif (!DotProduct(move, move))\n\t{\n\t\t//rogue buzzsaws are vile and jerkily move via setorigin, and need to be relinked so that they can touch path corners.\n\t\tif (ent->v->solid && ent->v->nextthink)\n\t\t\tWorld_LinkEdict (w, ent, true);\n\t\treturn;\n\t}\n\n\tfl = 0;\n#ifndef CLIENTONLY\n\t/*doesn't affect csqc, as it has no lagged ents registered anywhere*/\n\tif (sv_antilag.ival==2)\n\t\tfl |= MOVE_LAGGED;\n#endif\n\n\ttrace = WPhys_PushEntity (w, ent, move, fl);\n\n\tif (trace.allsolid && sv_gameplayfix_trappedwithin.ival && ent->v->solid != SOLID_NOT && ent->v->solid != SOLID_TRIGGER)\n\t{\n\t\ttrace.fraction = 0;\t//traces that start in solid report a fraction of 0. this is to prevent things from dropping out of the world completely. at least this way they ought to still be shootable etc\n\n#pragma warningmsg(\"The following line might help boost framerates a lot in rmq, not sure if they violate expected behaviour in other mods though - check that they're safe.\")\n\t\tVectorNegate(gravitydir, trace.plane.normal);\n\t}\n\tif (trace.fraction == 1 || !trace.ent)\n\t\treturn;\n\tif (ED_ISFREE(ent))\n\t\treturn;\n\n\tVectorCopy(trace.endpos, move);\n\n\tmovetype = ent->v->movetype;\n\tif (movetype == MOVETYPE_BOUNCEMISSILE && w->remasterlogic)\n\t\tmovetype = MOVETYPE_BOUNCE;\t//'gib'...\n\tif (movetype == MOVETYPE_BOUNCE)\n\t{\n\t\tif (ent->xv->bouncefactor)\n\t\t\tbackoff = 1 + ent->xv->bouncefactor;\n\t\telse\n\t\t\tbackoff = 1.5;\n\t}\n\telse if (movetype == MOVETYPE_BOUNCEMISSILE)\n\t{\n\t\tif (ent->xv->bouncefactor)\n\t\t\tbackoff = 1 + ent->xv->bouncefactor;\n//\t\telse if (progstype == PROG_H2 && ent->v->solid == SOLID_PHASEH2 && ((int)((wedict_t*)trace.ent)->v->flags & (FL_MONSTER|FL_CLIENT)))\n//\t\t\tbackoff = 0;\t//don't bounce/slide, just pass straight through.\n\t\telse\n\t\t\tbackoff = w->remasterlogic?1.5/*gib...*/:2;\n\t}\n\telse\n\t\tbackoff = 1;\n\n\tif (backoff)\n\t\tClipVelocity (ent->v->velocity, trace.plane.normal, ent->v->velocity, backoff);\n\n\n// stop if on ground\n\tif ((-DotProduct(gravitydir, trace.plane.normal) > 0.7) && (movetype != MOVETYPE_BOUNCEMISSILE))\n\t{\n\t\tfloat bouncespeed;\n\t\tfloat bouncestop = ent->xv->bouncestop;\n\t\tif (!bouncestop)\n\t\t\tbouncestop = 60;\n\t\telse\n\t\t\tbouncestop *= movevars.gravity * (ent->xv->gravity?ent->xv->gravity:1);\n\t\tif (sv_gameplayfix_bouncedownslopes.ival)\n\t\t\tbouncespeed = DotProduct(trace.plane.normal, ent->v->velocity);\n\t\telse\n\t\t\tbouncespeed = -DotProduct(gravitydir, ent->v->velocity);\n\t\tif (bouncespeed < bouncestop || movetype != MOVETYPE_BOUNCE )\n\t\t{\n\t\t\tent->v->flags = (int)ent->v->flags | FL_ONGROUND;\n\t\t\tent->v->groundentity = EDICT_TO_PROG(w->progs, trace.ent);\n\t\t\tVectorClear (ent->v->velocity);\n\t\t\tVectorClear (ent->v->avelocity);\n\t\t}\n\t}\n\n// check for in water\n\tWPhys_CheckWaterTransition (w, ent);\n}\n\n/*\n===============================================================================\n\nSTEPPING MOVEMENT\n\n===============================================================================\n*/\n\n/*\n=============\nSV_Physics_Step\n\nMonsters freefall when they don't have a ground entity, otherwise\nall movement is done with discrete steps.\n\nThis is also used for objects that have become still on the ground, but\nwill fall if the floor is pulled out from under them.\nFIXME: is this true?\n=============\n*/\nstatic void WPhys_Physics_Step (world_t *w, wedict_t *ent)\n{\n\tqboolean\thitsound;\n\tqboolean\tfreefall;\n\tint fl = ent->v->flags;\n\tconst float *gravitydir;\n\tvec3_t oldorg;\n\tVectorCopy(ent->v->origin, oldorg);\n\n\tif (ent->xv->gravitydir[2] || ent->xv->gravitydir[1] || ent->xv->gravitydir[0])\n\t\tgravitydir = ent->xv->gravitydir;\n\telse\n\t\tgravitydir = w->g.defaultgravitydir;\n\n\tif (-DotProduct(gravitydir, ent->v->velocity) >= (1.0 / 32.0) && (fl & FL_ONGROUND))\n\t{\n\t\tfl &= ~FL_ONGROUND;\n\t\tent->v->flags = fl;\n\t}\n\n// frefall if not onground\n\tif (fl & (FL_ONGROUND | FL_FLY))\n\t\tfreefall = false;\n\telse\n\t\tfreefall = true;\n\tif (fl & FL_SWIM)\n\t\tfreefall = ent->v->waterlevel <= 0;\n\tif (freefall)\n\t{\n\t\thitsound = -DotProduct(gravitydir, ent->v->velocity) < movevars.gravity*-0.1;\n\n\t\tWPhys_AddGravity (w, ent, gravitydir);\n\t\tWPhys_CheckVelocity (w, ent);\n\t\tWPhys_FlyMove (w, ent, gravitydir, host_frametime, NULL);\n\t\tWorld_LinkEdict (w, ent, true);\n\n\t\tif ( (int)ent->v->flags & FL_ONGROUND )\t// just hit ground\n\t\t{\n#if defined(HEXEN2) && defined(HAVE_SERVER)\n\t\t\tif (w==&sv.world && progstype == PROG_H2 && ((int)ent->v->flags & FL_MONSTER))\n\t\t\t\t;\t//hexen2 monsters do not make landing sounds.\n\t\t\telse\n#endif\n\t\t\tif (hitsound && *sv_sound_land.string)\n\t\t\t{\n\t\t\t\tw->Event_Sound(NULL, ent, 0, sv_sound_land.string, 255, 1, 0, 0, 0);\n\t\t\t}\n\t\t}\n\t}\n\n// regular thinking\n\tWPhys_RunThink (w, ent);\n\n\tif (!VectorEquals(ent->v->origin, oldorg))\n\t\tWPhys_CheckWaterTransition (w, ent);\n}\n\n//============================================================================\n\n#ifndef CLIENTONLY\nvoid SV_ProgStartFrame (void)\n{\n\n// let the progs know that a new frame has started\n\tpr_global_struct->self = EDICT_TO_PROG(svprogfuncs, sv.world.edicts);\n\tpr_global_struct->other = EDICT_TO_PROG(svprogfuncs, sv.world.edicts);\n\tpr_global_struct->time = sv.world.physicstime;\n#ifdef VM_Q1\n\tif (svs.gametype == GT_Q1QVM)\n\t\tQ1QVM_StartFrame(false);\n\telse\n#endif\n\t{\n\t\tif (pr_global_ptrs->StartFrame)\n\t\t\tPR_ExecuteProgram (svprogfuncs, *pr_global_ptrs->StartFrame);\n\t}\n}\n#endif\n\n\n\n\n\n\n\n\n\n\n\n\n/*\n=============\nSV_CheckStuck\n\nThis is a big hack to try and fix the rare case of getting stuck in the world\nclipping hull.\n=============\n*/\nstatic void WPhys_CheckStuck (world_t *w, wedict_t *ent)\n{\n\tint\t\ti, j;\n\tint\t\tz;\n\tvec3_t\torg;\n//return;\n\tif (!World_TestEntityPosition (w, ent))\n\t{\n\t\tVectorCopy (ent->v->origin, ent->v->oldorigin);\n\t\treturn;\n\t}\n\n\tVectorCopy (ent->v->origin, org);\n\tVectorCopy (ent->v->oldorigin, ent->v->origin);\n\tif (!World_TestEntityPosition (w, ent))\n\t{\n\t\tCon_DPrintf (\"Unstuck.\\n\");\n\t\tWorld_LinkEdict (w, ent, true);\n\t\treturn;\n\t}\n\n\tfor (z=0 ; z < movevars.stepheight ; z++)\n\t\tfor (i=-1 ; i <= 1 ; i++)\n\t\t\tfor (j=-1 ; j <= 1 ; j++)\n\t\t\t{\n\t\t\t\tent->v->origin[0] = org[0] + i;\n\t\t\t\tent->v->origin[1] = org[1] + j;\n\t\t\t\tent->v->origin[2] = org[2] + z;\n\t\t\t\tif (!World_TestEntityPosition (w, ent))\n\t\t\t\t{\n\t\t\t\t\tCon_DPrintf (\"Unstuck.\\n\");\n\t\t\t\t\tWorld_LinkEdict (w, ent, true);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\n\tVectorCopy (org, ent->v->origin);\n\tCon_DPrintf (\"player is stuck.\\n\");\n}\n\n/*\n=============\nSV_CheckWater\n=============\n\nfor players\n*/\nstatic qboolean WPhys_CheckWater (world_t *w, wedict_t *ent)\n{\n\tvec3_t\tpoint;\n\tint\t\tcont;\n\tint hc;\n\ttrace_t tr;\n\n\t//check if we're on a ladder, and if so fire a trace forwards to ensure its a valid ladder instead of a random volume\n\thc = ent->xv->hitcontentsmaski;\t//lame\n\tent->xv->hitcontentsmaski = ~0;\n\ttr = World_Move(w, ent->v->origin, ent->v->mins, ent->v->maxs, ent->v->origin, 0, ent);\n\tent->xv->hitcontentsmaski = hc;\n\tif (tr.contents & FTECONTENTS_LADDER)\n\t{\n\t\tvec3_t flatforward;\n\t\tflatforward[0] = cos((M_PI/180)*ent->v->angles[1]);\n\t\tflatforward[1] = sin((M_PI/180)*ent->v->angles[1]);\n\t\tflatforward[2] = 0;\n\t\tVectorMA (ent->v->origin, 24, flatforward, point);\n\n\t\ttr = World_Move(w, ent->v->origin, ent->v->mins, ent->v->maxs, point, 0, ent);\n\t\tif (tr.fraction < 1)\n\t\t\tent->xv->pmove_flags = (int)ent->xv->pmove_flags|PMF_LADDER;\n\t\telse if ((int)ent->xv->pmove_flags & PMF_LADDER)\n\t\t\tent->xv->pmove_flags -= PMF_LADDER;\n\t}\n\telse if ((int)ent->xv->pmove_flags & PMF_LADDER)\n\t\tent->xv->pmove_flags -= PMF_LADDER;\n\n\n\tpoint[0] = ent->v->origin[0];\n\tpoint[1] = ent->v->origin[1];\n\tpoint[2] = ent->v->origin[2] + ent->v->mins[2] + 1;\n\n\tent->v->waterlevel = 0;\n\tent->v->watertype = Q1CONTENTS_EMPTY;\n\tcont = World_PointContentsAllBSPs (w, point);\n\tif (cont & FTECONTENTS_FLUID)\n\t{\n\t\tif (cont & FTECONTENTS_LAVA)\n\t\t\tent->v->watertype = Q1CONTENTS_LAVA;\n\t\telse if (cont & FTECONTENTS_SLIME)\n\t\t\tent->v->watertype = Q1CONTENTS_SLIME;\n\t\telse if (cont & FTECONTENTS_WATER)\n\t\t\tent->v->watertype = Q1CONTENTS_WATER;\n\t\telse\n\t\t\tent->v->watertype = Q1CONTENTS_SKY;\n\t\tent->v->waterlevel = 1;\n\t\tpoint[2] = ent->v->origin[2] + (ent->v->mins[2] + ent->v->maxs[2])*0.5;\n\t\tcont = World_PointContentsAllBSPs (w, point);\n\t\tif (cont & FTECONTENTS_FLUID)\n\t\t{\n\t\t\tent->v->waterlevel = 2;\n\t\t\tpoint[2] = ent->v->origin[2] + ent->v->view_ofs[2];\n\t\t\tcont = World_PointContentsAllBSPs (w, point);\n\t\t\tif (cont & FTECONTENTS_FLUID)\n\t\t\t\tent->v->waterlevel = 3;\n\t\t}\n\t}\n\n\treturn ent->v->waterlevel > 1;\n}\n\n\n/*\n============\nSV_WallFriction\n\n============\n*/\nstatic void WPhys_WallFriction (wedict_t *ent, trace_t *trace)\n{\n\tvec3_t\t\tforward, right, up;\n\tfloat\t\td, i;\n\tvec3_t\t\tinto, side;\n\n\tAngleVectors (ent->v->v_angle, forward, right, up);\n\td = DotProduct (trace->plane.normal, forward);\n\n\td += 0.5;\n\tif (d >= 0 || IS_NAN(d))\n\t\treturn;\n\n// cut the tangential velocity\n\ti = DotProduct (trace->plane.normal, ent->v->velocity);\n\tVectorScale (trace->plane.normal, i, into);\n\tVectorSubtract (ent->v->velocity, into, side);\n\n\tent->v->velocity[0] = side[0] * (1 + d);\n\tent->v->velocity[1] = side[1] * (1 + d);\n}\n\n/*\n=====================\nSV_TryUnstick\n\nPlayer has come to a dead stop, possibly due to the problem with limited\nfloat precision at some angle joins in the BSP hull.\n\nTry fixing by pushing one pixel in each direction.\n\nThis is a hack, but in the interest of good gameplay...\n======================\n\nstatic int SV_TryUnstick (edict_t *ent, vec3_t oldvel)\n{\n\tint\t\ti;\n\tvec3_t\toldorg;\n\tvec3_t\tdir;\n\tint\t\tclip;\n\ttrace_t\tsteptrace;\n\n\tVectorCopy (ent->v->origin, oldorg);\n\tVectorClear (dir);\n\n\tfor (i=0 ; i<8 ; i++)\n\t{\n// try pushing a little in an axial direction\n\t\tswitch (i)\n\t\t{\n\t\t\tcase 0:\tdir[0] = 2; dir[1] = 0; break;\n\t\t\tcase 1:\tdir[0] = 0; dir[1] = 2; break;\n\t\t\tcase 2:\tdir[0] = -2; dir[1] = 0; break;\n\t\t\tcase 3:\tdir[0] = 0; dir[1] = -2; break;\n\t\t\tcase 4:\tdir[0] = 2; dir[1] = 2; break;\n\t\t\tcase 5:\tdir[0] = -2; dir[1] = 2; break;\n\t\t\tcase 6:\tdir[0] = 2; dir[1] = -2; break;\n\t\t\tcase 7:\tdir[0] = -2; dir[1] = -2; break;\n\t\t}\n\n\t\tSV_PushEntity (ent, dir, MOVE_NORMAL);\n\n// retry the original move\n\t\tent->v->velocity[0] = oldvel[0];\n\t\tent->v-> velocity[1] = oldvel[1];\n\t\tent->v-> velocity[2] = 0;\n\t\tclip = SV_FlyMove (ent, 0.1, &steptrace);\n\n\t\tif ( fabs(oldorg[1] - ent->v->origin[1]) > 4\n\t\t|| fabs(oldorg[0] - ent->v->origin[0]) > 4 )\n\t\t{\n//Con_DPrintf (\"unstuck!\\n\");\n\t\t\treturn clip;\n\t\t}\n\n// go back to the original pos and try again\n\t\tVectorCopy (oldorg, ent->v->origin);\n\t}\n\n\tVectorClear (ent->v->velocity);\n\treturn 7;\t\t// still not moving\n}\n*/\n\n/*\n=====================\nSV_WalkMove\n\nOnly used by players\n======================\n*/\n#if 0\n#define\tSMSTEPSIZE\t4\nstatic void SV_WalkMove (edict_t *ent)\n{\n\tvec3_t\t\tupmove, downmove;\n\tvec3_t\t\toldorg, oldvel;\n\tvec3_t\t\tnosteporg, nostepvel;\n\tint\t\t\tclip;\n\tint\t\t\toldonground;\n\ttrace_t\t\tsteptrace, downtrace;\n\n//\n// do a regular slide move unless it looks like you ran into a step\n//\n\toldonground = (int)ent->v->flags & FL_ONGROUND;\n\tent->v->flags = (int)ent->v->flags & ~FL_ONGROUND;\n\n\tVectorCopy (ent->v->origin, oldorg);\n\tVectorCopy (ent->v->velocity, oldvel);\n\n\tclip = SV_FlyMove (ent, host_frametime, &steptrace);\n\n\tif ( !(clip & 2) )\n\t\treturn;\t\t// move didn't block on a step\n\n\tif (!oldonground && ent->v->waterlevel == 0)\n\t\treturn;\t\t// don't stair up while jumping\n\n\tif (ent->v->movetype != MOVETYPE_WALK)\n\t\treturn;\t\t// gibbed by a trigger\n\n//\tif (sv_nostep.value)\n//\t\treturn;\n\n\tif ( (int)ent->v->flags & FL_WATERJUMP )\n\t\treturn;\n\n\tVectorCopy (ent->v->origin, nosteporg);\n\tVectorCopy (ent->v->velocity, nostepvel);\n\n//\n// try moving up and forward to go up a step\n//\n\tVectorCopy (oldorg, ent->v->origin);\t// back to start pos\n\n\tVectorCopy (vec3_origin, upmove);\n\tVectorCopy (vec3_origin, downmove);\n\tupmove[2] = movevars.stepheight;\n\tdownmove[2] = -movevars.stepheight + oldvel[2]*host_frametime;\n\n// move up\n\tSV_PushEntity (ent, upmove);\t// FIXME: don't link?\n\n// move forward\n\tent->v->velocity[0] = oldvel[0];\n\tent->v->velocity[1] = oldvel[1];\n\tent->v->velocity[2] = 0;\n\tclip = SV_FlyMove (ent, host_frametime, &steptrace);\n\n// check for stuckness, possibly due to the limited precision of floats\n// in the clipping hulls\n\tif (clip)\n\t{\n\t\tif ( fabs(oldorg[1] - ent->v->origin[1]) < 0.03125\n\t\t&& fabs(oldorg[0] - ent->v->origin[0]) < 0.03125 )\n\t\t{\t// stepping up didn't make any progress\n\t\t\tclip = SV_TryUnstick (ent, oldvel);\n\n//\t\t\tCon_Printf(\"Try unstick fwd\\n\");\n\t\t}\n\t}\n\n// extra friction based on view angle\n\tif ( clip & 2 )\n\t{\n\t\tvec3_t lastpos, lastvel, lastdown;\n\n//\t\tCon_Printf(\"couldn't do it\\n\");\n\n\t\t//retry with a smaller step (allows entering smaller areas with a step of 4)\n\t\tVectorCopy (downmove, lastdown);\n\t\tVectorCopy (ent->v->origin, lastpos);\n\t\tVectorCopy (ent->v->velocity, lastvel);\n\n\t//\n\t// try moving up and forward to go up a step\n\t//\n\t\tVectorCopy (oldorg, ent->v->origin);\t// back to start pos\n\n\t\tVectorCopy (vec3_origin, upmove);\n\t\tVectorCopy (vec3_origin, downmove);\n\t\tupmove[2] = SMSTEPSIZE;\n\t\tdownmove[2] = -SMSTEPSIZE + oldvel[2]*host_frametime;\n\n\t// move up\n\t\tSV_PushEntity (ent, upmove);\t// FIXME: don't link?\n\n\t// move forward\n\t\tent->v->velocity[0] = oldvel[0];\n\t\tent->v->velocity[1] = oldvel[1];\n\t\tent->v->velocity[2] = 0;\n\t\tclip = SV_FlyMove (ent, host_frametime, &steptrace);\n\n\t// check for stuckness, possibly due to the limited precision of floats\n\t// in the clipping hulls\n\t\tif (clip)\n\t\t{\n\t\t\tif ( fabs(oldorg[1] - ent->v->origin[1]) < 0.03125\n\t\t\t&& fabs(oldorg[0] - ent->v->origin[0]) < 0.03125 )\n\t\t\t{\t// stepping up didn't make any progress\n\t\t\t\tclip = SV_TryUnstick (ent, oldvel);\n\n//\t\t\t\tCon_Printf(\"Try unstick up\\n\");\n\t\t\t}\n\t\t}\n\n\t\tif ( fabs(oldorg[1] - ent->v->origin[1])+fabs(oldorg[0] - ent->v->origin[0]) < fabs(oldorg[1] - lastpos[1])+fabs(oldorg[1] - lastpos[1]))\n\t\t{\t// stepping up didn't make any progress\n\t\t\t\t//go back\n\t\t\t\tVectorCopy (lastdown, downmove);\n\t\t\t\tVectorCopy (lastpos, ent->v->origin);\n\t\t\t\tVectorCopy (lastvel, ent->v->velocity);\n\n\t\t\t\tSV_WallFriction (ent, &steptrace);\n\n//\t\t\t\tCon_Printf(\"wall friction\\n\");\n\t\t\t}\n\n\t\telse if (clip & 2)\n\t\t{\n\t\t\tSV_WallFriction (ent, &steptrace);\n//\t\t\tCon_Printf(\"wall friction 2\\n\");\n\t\t}\n\t}\n\n// move down\n\tdowntrace = SV_PushEntity (ent, downmove);\t// FIXME: don't link?\n\n\tif (downtrace.plane.normal[2] > 0.7)\n\t{\n\t\tif (ent->v->solid == SOLID_BSP)\n\t\t{\n\t\t\tent->v->flags =\t(int)ent->v->flags | FL_ONGROUND;\n\t\t\tent->v->groundentity = EDICT_TO_PROG(svprogfuncs, downtrace.ent);\n\t\t}\n\t}\n\telse\n\t{\n// if the push down didn't end up on good ground, use the move without\n// the step up.  This happens near wall / slope combinations, and can\n// cause the player to hop up higher on a slope too steep to climb\n\t\tVectorCopy (nosteporg, ent->v->origin);\n\t\tVectorCopy (nostepvel, ent->v->velocity);\n\n//\t\tCon_Printf(\"down not good\\n\");\n\t}\n}\n#else\n\n// 1/32 epsilon to keep floating point happy\n/*#define\tDIST_EPSILON\t(0.03125)\nstatic int WPhys_SetOnGround (world_t *w, wedict_t *ent, const float *gravitydir)\n{\n\tvec3_t end;\n\ttrace_t trace;\n\tif ((int)ent->v->flags & FL_ONGROUND)\n\t\treturn 1;\n\tVectorMA(ent->v->origin, 1, gravitydir, end);\n\ttrace = World_Move(w, ent->v->origin, ent->v->mins, ent->v->maxs, end, MOVE_NORMAL, (wedict_t*)ent);\n\tif (DotProduct(trace.plane.normal, ent->v->velocity) > 0)\n\t\treturn 0;\t//velocity is away from the plane normal, so this does not count as a contact.\n\tif (trace.fraction <= DIST_EPSILON && -DotProduct(gravitydir, trace.plane.normal) >= 0.7)\n\t{\n\t\tent->v->flags = (int)ent->v->flags | FL_ONGROUND;\n\t\tent->v->groundentity = EDICT_TO_PROG(w->progs, trace.ent);\n\t\treturn 1;\n\t}\n\treturn 0;\n}*/\nstatic void WPhys_WalkMove (world_t *w, wedict_t *ent, const float *gravitydir)\n{\n\t//int originalmove_clip;\n\tint clip, oldonground, originalmove_flags, originalmove_groundentity;\n\tvec3_t upmove, downmove, start_origin, start_velocity, originalmove_origin, originalmove_velocity;\n\ttrace_t downtrace, steptrace;\n\n\tWPhys_CheckVelocity(w, ent);\n\n\t// do a regular slide move unless it looks like you ran into a step\n\toldonground = (int)ent->v->flags & FL_ONGROUND;\n\tent->v->flags = (int)ent->v->flags & ~FL_ONGROUND;\n\n\tVectorCopy (ent->v->origin, start_origin);\n\tVectorCopy (ent->v->velocity, start_velocity);\n\n\tclip = WPhys_FlyMove (w, ent, gravitydir, host_frametime, NULL);\n\n//\tWPhys_SetOnGround (w, ent, gravitydir);\n\tWPhys_CheckVelocity(w, ent);\n\n\tVectorCopy(ent->v->origin, originalmove_origin);\n\tVectorCopy(ent->v->velocity, originalmove_velocity);\n\t//originalmove_clip = clip;\n\toriginalmove_flags = (int)ent->v->flags;\n\toriginalmove_groundentity = ent->v->groundentity;\n\n\tif ((int)ent->v->flags & FL_WATERJUMP)\n\t\treturn;\n\n//\tif (sv_nostep.value)\n//\t\treturn;\n\n\t// if move didn't block on a step, return\n\tif (clip & 2)\n\t{\n\t\t// if move was not trying to move into the step, return\n\t\tif (fabs(start_velocity[0]) < 0.03125 && fabs(start_velocity[1]) < 0.03125)\n\t\t\treturn;\n\n\t\tif (ent->v->movetype != MOVETYPE_FLY && ent->v->movetype != MOVETYPE_FLY_WORLDONLY)\n\t\t{\n\t\t\t// return if gibbed by a trigger\n\t\t\tif (ent->v->movetype != MOVETYPE_WALK)\n\t\t\t\treturn;\n\n\t\t\t// only step up while jumping if that is enabled\n \t\t\tif (!pm_airstep.value)\n\t\t\t\tif (!oldonground && ent->v->waterlevel == 0)\n\t\t\t\t\treturn;\n\t\t}\n\n\t\t// try moving up and forward to go up a step\n\t\t// back to start pos\n\t\tVectorCopy (start_origin, ent->v->origin);\n\t\tVectorCopy (start_velocity, ent->v->velocity);\n\n\t\t// move up\n\t\tVectorScale(gravitydir, -movevars.stepheight, upmove);\n\t\t// FIXME: don't link?\n\t\tWPhys_PushEntity(w, ent, upmove, MOVE_NORMAL);\n\n\t\t// move forward\n\t\tVectorMA(ent->v->velocity, -DotProduct(gravitydir, ent->v->velocity), gravitydir, ent->v->velocity);\t//ent->v->velocity[2] = 0;\n\t\tclip = WPhys_FlyMove (w, ent, gravitydir, host_frametime, &steptrace);\n\t\tVectorMA(ent->v->velocity, DotProduct(gravitydir, start_velocity), gravitydir, ent->v->velocity);\t//ent->v->velocity[2] += start_velocity[2];\n\n\t\tWPhys_CheckVelocity(w, ent);\n\n\t\t// check for stuckness, possibly due to the limited precision of floats\n\t\t// in the clipping hulls\n\t\tif (clip\n\t\t && fabs(originalmove_origin[1] - ent->v->origin[1]) < 0.03125\n\t\t && fabs(originalmove_origin[0] - ent->v->origin[0]) < 0.03125)\n\t\t{\n//\t\t\tCon_Printf(\"wall\\n\");\n\t\t\t// stepping up didn't make any progress, revert to original move\n\t\t\tVectorCopy(originalmove_origin, ent->v->origin);\n\t\t\tVectorCopy(originalmove_velocity, ent->v->velocity);\n\t\t\t//clip = originalmove_clip;\n\t\t\tent->v->flags = originalmove_flags;\n\t\t\tent->v->groundentity = originalmove_groundentity;\n\t\t\t// now try to unstick if needed\n\t\t\t//clip = SV_TryUnstick (ent, oldvel);\n\t\t\treturn;\n\t\t}\n\n\t\t//Con_Printf(\"step - \");\n\n\t\t// extra friction based on view angle\n\t\tif ((clip & 2) && sv_wallfriction.value)\n\t\t{\n//\t\t\tCon_Printf(\"wall\\n\");\n\t\t\tWPhys_WallFriction (ent, &steptrace);\n\t\t}\n\t}\n\telse if (!sv_gameplayfix_stepdown.ival || !oldonground || -DotProduct(gravitydir,start_velocity) > 0 || ((int)ent->v->flags & FL_ONGROUND) || ent->v->waterlevel >= 2)\n\t\treturn;\n\n\t// move down\n\tVectorScale(gravitydir, movevars.stepheight + (1/32.0) - DotProduct(gravitydir,start_velocity)*host_frametime, downmove);\n\t// FIXME: don't link?\n\tdowntrace = WPhys_PushEntity (w, ent, downmove, MOVE_NORMAL);\n\n\tif (downtrace.fraction < 1 && -DotProduct(gravitydir, downtrace.plane.normal) > 0.7)\n\t{\n\t\tif (DotProduct(downtrace.plane.normal, ent->v->velocity)<=0) //Spike: moving away from the surface should not count as onground.\n\t\t// LordHavoc: disabled this check so you can walk on monsters/players\n\t\t//if (ent->v->solid == SOLID_BSP)\n\t\t{\n\t\t\t//Con_Printf(\"onground\\n\");\n\t\t\tent->v->flags =\t(int)ent->v->flags | FL_ONGROUND;\n\t\t\tent->v->groundentity = EDICT_TO_PROG(w->progs, downtrace.ent);\n\t\t}\n\t}\n\telse\n\t{\n\t\t//Con_Printf(\"slope\\n\");\n\t\t// if the push down didn't end up on good ground, use the move without\n\t\t// the step up.  This happens near wall / slope combinations, and can\n\t\t// cause the player to hop up higher on a slope too steep to climb\n\t\tVectorCopy(originalmove_origin, ent->v->origin);\n\t\tVectorCopy(originalmove_velocity, ent->v->velocity);\n\t\t//clip = originalmove_clip;\n\t\tent->v->flags = originalmove_flags;\n\t\tent->v->groundentity = originalmove_groundentity;\n\t}\n\n//\tWPhys_SetOnGround (w, ent, gravitydir);\n\tWPhys_CheckVelocity(w, ent);\n}\n#endif\n\n#ifdef HEXEN2\nvoid WPhys_MoveChain(world_t *w, wedict_t *ent, wedict_t *movechain, float *initial_origin, float *initial_angle)\n{\n\tqboolean orgunchanged;\n\tvec3_t moveorg, moveang;\n\tVectorSubtract(ent->v->origin, initial_origin, moveorg);\n\tVectorSubtract(ent->v->angles, initial_angle, moveang);\n\torgunchanged=!DotProduct(moveorg,moveorg);\n\tif (!orgunchanged || DotProduct(moveang,moveang))\n\t{\n\t\tint i;\n\t\tfor(i=16; i && movechain != w->edicts && !ED_ISFREE(movechain); i--, movechain = PROG_TO_WEDICT(w->progs, movechain->xv->movechain))\n\t\t{\n\t\t\tif ((int)movechain->v->flags & FL_MOVECHAIN_ANGLE)\n\t\t\t\tVectorAdd(movechain->v->angles, moveang, movechain->v->angles);\t//FIXME: axial only\n\t\t\tif (!orgunchanged)\n\t\t\t{\n\t\t\t\tVectorAdd(movechain->v->origin, moveorg, movechain->v->origin);\n\t\t\t\tWorld_LinkEdict(w, movechain, false);\n\n\t\t\t\t//chainmoved is called only for origin changes, not angle ones, apparently.\n\t\t\t\tif (movechain->xv->chainmoved)\n\t\t\t\t{\n\t\t\t\t\t*w->g.self = EDICT_TO_PROG(w->progs, movechain);\n\t\t\t\t\t*w->g.other = EDICT_TO_PROG(w->progs, ent);\n#ifdef VM_Q1\n\t\t\t\t\tif (svs.gametype == GT_Q1QVM && w == &sv.world)\n\t\t\t\t\t\tQ1QVM_ChainMoved();\n\t\t\t\t\telse\n#endif\n\t\t\t\t\t\tPR_ExecuteProgram(w->progs, movechain->xv->chainmoved);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n#endif\n\n/*\n================\nSV_RunEntity\n\n================\n*/\nvoid WPhys_RunEntity (world_t *w, wedict_t *ent)\n{\n#ifdef HEXEN2\n\twedict_t\t*movechain;\n\tvec3_t\tinitial_origin = {0},initial_angle = {0};\n#endif\n\tconst float *gravitydir;\n\n#ifndef CLIENTONLY\n\tedict_t *svent = (edict_t*)ent;\n\tif (ent->entnum > 0 && ent->entnum <= sv.allocated_client_slots && w == &sv.world)\n\t{\t//a client woo.\n\t\tqboolean readyforjump = false;\n\n#if defined(NQPROT) && defined(HAVE_LEGACY)\n\t\tif (svs.clients[ent->entnum-1].state == cs_connected)\n\t\t{\t//nq is buggy and calls playerprethink/etc while the player is still connecting.\n\t\t\t//some mods depend on this, hopefully unintentionally (as is the case with Arcane Dimensions).\n\t\t\t//so don't do anything if we're qw, but use crappy behaviour for nq+h2.\n\t\t\tif (progstype != PROG_NQ || sv_gameplayfix_spawnbeforethinks.ival)\n\t\t\t\treturn;\n\t\t}\n\t\telse\n#endif\n\t\t{\n\t\t\tif (svs.clients[ent->entnum-1].state < cs_spawned)\n\t\t\t\treturn;\t\t// unconnected slot\n\t\t}\n\n\t\tif (svs.clients[ent->entnum-1].protocol == SCP_BAD)\n\t\t\tsvent->v->fixangle = FIXANGLE_NO;\t//bots never get fixangle cleared otherwise\n\n\t\thost_client = &svs.clients[ent->entnum-1];\n\t\tSV_ClientThink();\n\n\t\tif (!host_client->spectator)\n\t\t{\n\t\t\tif (progstype == PROG_QW)\t//detect if the mod should do a jump\n\t\t\t\tif (svent->v->button2)\n\t\t\t\t\tif ((int)svent->v->flags & FL_JUMPRELEASED)\n\t\t\t\t\t\treadyforjump = true;\n\n\t\t\t//\n\t\t\t// call standard client pre-think\n\t\t\t//\n\t\t\tpr_global_struct->time = sv.world.physicstime;\n\t\t\tpr_global_struct->self = EDICT_TO_PROG(svprogfuncs, ent);\n#ifdef VM_Q1\n\t\t\tif (svs.gametype == GT_Q1QVM)\n\t\t\t\tQ1QVM_PlayerPreThink();\n\t\t\telse\n#endif\n\t\t\t\tif (pr_global_ptrs->PlayerPreThink)\n\t\t\t\t\tPR_ExecuteProgram (svprogfuncs, *pr_global_ptrs->PlayerPreThink);\n\n\t\t\tif (readyforjump)\t//qw progs can't jump for themselves...\n\t\t\t{\n\t\t\t\tif (!svent->v->button2 && !((int)ent->v->flags & FL_JUMPRELEASED) && ent->v->velocity[2] <= 0)\n\t\t\t\t\tsvent->v->velocity[2] += 270;\n\t\t\t}\n\t\t}\n\t}\n\telse\n#endif\n\t{\n\t\tif (ent->lastruntime == w->framenum)\n\t\t\treturn;\n\t\tent->lastruntime = w->framenum;\n#ifndef CLIENTONLY\n\t\tif (progstype == PROG_QW && w == &sv.world)\t//we don't use the field any more, but qw mods might.\n\t\t\tent->v->lastruntime = w->physicstime;\n\t\tsvent = NULL;\n#endif\n\t}\n\n\n#ifdef HEXEN2\n\tmovechain = PROG_TO_WEDICT(w->progs, ent->xv->movechain);\n\tif (movechain != w->edicts)\n\t{\n\t\tVectorCopy(ent->v->origin,initial_origin);\n\t\tVectorCopy(ent->v->angles,initial_angle);\n\t}\n#endif\n\n\n\tif (ent->xv->customphysics)\n\t{\n\t\t*w->g.time = w->physicstime;\n\t\t*w->g.self = EDICT_TO_PROG(w->progs, ent);\n\t\tPR_ExecuteProgram (w->progs, ent->xv->customphysics);\n\t}\n\telse switch ( (int)ent->v->movetype)\n\t{\n\tcase MOVETYPE_PUSH:\n\t\tWPhys_Physics_Pusher (w, ent);\n\t\tbreak;\n\tcase MOVETYPE_NONE:\n\t\tif (!WPhys_RunThink (w, ent))\n\t\t\treturn;\n\t\tbreak;\n\tcase MOVETYPE_NOCLIP:\n\tcase MOVETYPE_ANGLENOCLIP:\n\t\tWPhys_Physics_Noclip (w, ent);\n\t\tbreak;\n\tcase MOVETYPE_H2PUSHPULL:\n#if defined(HEXEN2) && !defined(CLIENTONLY)\n\t\tif (w == &sv.world && progstype == PROG_H2)\n\t\t\tWPhys_Physics_Step (w, ent);\t//hexen2 pushable object (basically exactly movetype_step)\n\t\telse\n#endif\n\t\t\tWPhys_Physics_Pusher (w, ent);\t//non-solid pusher, for tenebrae compat\n\t\tbreak;\n\tcase MOVETYPE_STEP:\n\t\tWPhys_Physics_Step (w, ent);\n\t\tbreak;\n\tcase MOVETYPE_FOLLOW:\n\t\tWPhys_Physics_Follow (w, ent);\n\t\tbreak;\n\tcase MOVETYPE_FLY_WORLDONLY:\n\tcase MOVETYPE_FLY:\n#ifndef CLIENTONLY\n\t\tif (svent)\n\t\t{\t//NQ players with movetype_fly are not like non-players.\n\t\t\tif (!WPhys_RunThink (w, ent))\n\t\t\t\treturn;\n\t\t\tif (ent->xv->gravitydir[2] || ent->xv->gravitydir[1] || ent->xv->gravitydir[0])\n\t\t\t\tgravitydir = ent->xv->gravitydir;\n\t\t\telse\n\t\t\t\tgravitydir = w->g.defaultgravitydir;\n\t\t\tWPhys_CheckStuck (w, ent);\n\t\t\tWPhys_WalkMove (w, ent, gravitydir);\n\t\t\tbreak;\n\t\t}\n#endif\n\t\t//fallthrough\n\tcase MOVETYPE_H2SWIM:\n\tcase MOVETYPE_TOSS:\n\tcase MOVETYPE_BOUNCE:\n\tcase MOVETYPE_BOUNCEMISSILE:\n\tcase MOVETYPE_FLYMISSILE:\n\t\tWPhys_Physics_Toss (w, ent);\n\t\tbreak;\n\tcase MOVETYPE_WALK:\n\t\tif (!WPhys_RunThink (w, ent))\n\t\t\treturn;\n\n\t\tif (ent->xv->gravitydir[2] || ent->xv->gravitydir[1] || ent->xv->gravitydir[0])\n\t\t\tgravitydir = ent->xv->gravitydir;\n\t\telse\n\t\t\tgravitydir = w->g.defaultgravitydir;\n\n\t\tif (!WPhys_CheckWater (w, ent) && ! ((int)ent->v->flags & FL_WATERJUMP) ) //Vanilla Bug: the QC checks waterlevel inside PlayerPreThink, with waterlevel from a different position from the origin.\n\t\t\tif (!((int)ent->xv->pmove_flags & PMF_LADDER))\n\t\t\t\tWPhys_AddGravity (w, ent, gravitydir);\n\t\tWPhys_CheckStuck (w, ent);\n\n\t\tWPhys_WalkMove (w, ent, gravitydir);\n\n#ifndef CLIENTONLY\n\t\tif (!svent)\n#endif\n\t\t\tWorld_LinkEdict (w, ent, true);\n\t\tbreak;\n#ifdef USERBE\n\tcase MOVETYPE_PHYSICS:\n\t\tif (WPhys_RunThink(w, ent))\n\t\t\tWorld_LinkEdict (w, ent, true);\n\t\tw->rbe_hasphysicsents = true;\n\t\tbreak;\n#endif\n\tdefault:\n//\t\tSV_Error (\"SV_Physics: bad movetype %i on %s\", (int)ent->v->movetype, PR_GetString(w->progs, ent->v->classname));\n\t\tbreak;\n\t}\n\n#ifdef HEXEN2\n\tif (movechain != w->edicts)\n\t\tWPhys_MoveChain(w, ent, movechain, initial_origin, initial_angle);\n#endif\n\n#ifndef CLIENTONLY\n\tif (svent)\n\t{\n\t\tWorld_LinkEdict (w, ent, true);\n\n\t\tif (!host_client->spectator)\n\t\t{\n\t\t\tpr_global_struct->time = w->physicstime;\n\t\t\tpr_global_struct->self = EDICT_TO_PROG(w->progs, ent);\n#ifdef VM_Q1\n\t\t\tif (svs.gametype == GT_Q1QVM)\n\t\t\t\tQ1QVM_PostThink();\n\t\t\telse\n#endif\n\t\t\t{\n\t\t\t\tif (pr_global_ptrs->PlayerPostThink)\n\t\t\t\t\tPR_ExecuteProgram (w->progs, *pr_global_ptrs->PlayerPostThink);\n\t\t\t}\n\t\t}\n\t}\n#endif\n}\n\n/*\n================\nSV_RunNewmis\n\n================\n*/\nvoid WPhys_RunNewmis (world_t *w)\n{\n\twedict_t\t*ent;\n\n\tif (!w->g.newmis)\t//newmis variable is not exported.\n\t\treturn;\n\n\tif (!sv_gameplayfix_multiplethinks.ival)\n\t\treturn;\n\n\tif (!*w->g.newmis)\n\t\treturn;\n\tent = PROG_TO_WEDICT(w->progs, *w->g.newmis);\n\thost_frametime = 0.05;\n\t*w->g.newmis = 0;\n\n\tWPhys_RunEntity (w, ent);\n\n\thost_frametime = *w->g.frametime;\n}\n\ntrace_t WPhys_Trace_Toss (world_t *w, wedict_t *tossent, wedict_t *ignore)\n{\n\tint i;\n\tfloat gravity;\n\tvec3_t move, end;\n\ttrace_t trace;\n\n\tvec3_t origin, velocity;\n\n\t// this has to fetch the field from the original edict, since our copy is truncated\n\tgravity = tossent->xv->gravity;\n\tif (!gravity)\n\t\tgravity = 1.0;\n\tgravity *= sv_gravity.value * 0.05;\n\n\tVectorCopy (tossent->v->origin, origin);\n\tVectorCopy (tossent->v->velocity, velocity);\n\n\tWPhys_CheckVelocity (w, tossent);\n\n\tfor (i = 0;i < 200;i++) // LordHavoc: sanity check; never trace more than 10 seconds\n\t{\n\t\tvelocity[2] -= gravity;\n\t\tVectorScale (velocity, 0.05, move);\n\t\tVectorAdd (origin, move, end);\n\t\ttrace = World_Move (w, origin, tossent->v->mins, tossent->v->maxs, end, MOVE_NORMAL, tossent);\n\t\tVectorCopy (trace.endpos, origin);\n\n\t\tif (trace.fraction < 1 && trace.ent && trace.ent != ignore)\n\t\t\tbreak;\n\n\t\tif (Length(velocity) > sv_maxvelocity.value)\n\t\t{\n//\t\t\tCon_DPrintf(\"Slowing %s\\n\", PR_GetString(w->progs, tossent->v->classname));\n\t\t\tVectorScale (velocity, sv_maxvelocity.value/Length(velocity), velocity);\n\t\t}\n\t}\n\n\ttrace.fraction = 0; // not relevant\n\treturn trace;\n}\n\n/*\nRun an individual physics frame. This might be run multiple times in one frame if we're running slow, or not at all.\n*/\nvoid World_Physics_Frame(world_t *w)\n{\n\tint i;\n\tqboolean retouch;\n\twedict_t *ent;\n\n\tw->framenum++;\n\n\ti = *w->g.physics_mode;\n\tif (i == 0)\n\t{\n\t\t/*physics mode 0 = none*/\n\t\treturn;\n\t}\n\tif (i == 1)\n\t{\n\t\t/*physics mode 1 = thinks only*/\n\t\tfor (i=0 ; i<w->num_edicts ; i++)\n\t\t{\n\t\t\tent = (wedict_t*)EDICT_NUM_PB(w->progs, i);\n\t\t\tif (ED_ISFREE(ent))\n\t\t\t\tcontinue;\n\n\t\t\tWPhys_RunThink (w, ent);\n\t\t}\n\t\treturn;\n\t}\n\t/*physics mode 2 = normal movetypes*/\n\n\tretouch = (w->g.force_retouch && (*w->g.force_retouch >= 1));\n\n\t//\n\t// treat each object in turn\n\t// even the world gets a chance to think\n\t//\n\tfor (i=0 ; i<w->num_edicts ; i++)\n\t{\n\t\tent = (wedict_t*)EDICT_NUM_PB(w->progs, i);\n\t\tif (ED_ISFREE(ent))\n\t\t\tcontinue;\n\n\t\tif (retouch)\n\t\t\tWorld_LinkEdict (w, ent, true);\t// force retouch even for stationary\n\n#ifdef HAVE_SERVER\n\t\tif (i > 0 && i <= sv.allocated_client_slots && w == &sv.world)\n\t\t{\n\t\t\tif (!svs.clients[i-1].isindependant)\n\t\t\t{\n\t\t\t\tif (sv_nqplayerphysics.ival || SV_PlayerPhysicsQC || svs.clients[i-1].state < cs_spawned)\n\t\t\t\t{\n\t\t\t\t\tWPhys_RunEntity (w, ent);\n\t\t\t\t\tWPhys_RunNewmis (w);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tunsigned int newt;\n\t\t\t\t\tunsigned int delt;\n\t\t\t\t\tnewt = sv.time*1000;\n\t\t\t\t\tdelt = newt - svs.clients[i-1].lastruncmd;\n\t\t\t\t\tif (delt > (int)(1000/77.0) || delt < -10)\n\t\t\t\t\t{\n\t\t\t\t\t\tfloat ft = host_frametime;\n\t\t\t\t\t\thost_client = &svs.clients[i-1];\n\t\t\t\t\t\tsv_player = svs.clients[i-1].edict;\n\n\t\t\t\t\t\tSV_PreRunCmd();\n#ifndef NEWSPEEDCHEATPROT\n\t\t\t\t\t\tsvs.clients[i-1].last_check = 0;\n#endif\n\t\t\t\t\t\tsvs.clients[i-1].lastcmd.msec = bound(0, delt, 255);\n\t\t\t\t\t\tSV_RunCmd (&svs.clients[i-1].lastcmd, true);\n\t\t\t\t\t\tsvs.clients[i-1].lastcmd.impulse = 0;\n\t\t\t\t\t\tSV_PostRunCmd();\n\t\t\t\t\t\thost_client->lastruncmd = sv.time*1000;\n\t\t\t\t\t\t*w->g.frametime = host_frametime = ft;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n//\t\t\telse\n//\t\t\t\tWorld_LinkEdict(w, (wedict_t*)ent, true);\n\t\t\tcontinue;\t\t// clients are run directly from packets\n\t\t}\n#endif\n\n\t\tWPhys_RunEntity (w, ent);\n\t\tWPhys_RunNewmis (w);\n\t}\n\n\tif (retouch)\n\t\t*w->g.force_retouch-=1;\n}\n\n#ifdef HAVE_SERVER\n/*\n================\nSV_Physics\n\n================\n*/\nqboolean SV_Physics (void)\n{\n\tint\t\ti;\n\tqboolean moved = false;\n\tint maxtics = sv_limittics.ival;\n\tdouble trueframetime = host_frametime;\n\tdouble maxtic = sv_maxtic.value;\n\tdouble mintic = sv_mintic.value;\n\tif (sv_nqplayerphysics.ival)\n\t\tif (mintic < 0.013)\n\t\t\tmintic = 0.013;\t//NQ physics can't cope with low rates and just generally bugs out.\n\tif (maxtic < mintic)\n\t\tmaxtic = mintic;\n\n\tif (maxtics>1&&sv.spawned_observer_slots==0&&sv.spawned_client_slots==0)\n\t\tmaxtics = 1;\t//no players on the server. let timings slide\n\n\t//keep gravity tracking the cvar properly\n\tmovevars.gravity = sv_gravity.value;\n\n\tif (svs.gametype != GT_PROGS && svs.gametype != GT_Q1QVM && svs.gametype != GT_HALFLIFE \n#ifdef VM_LUA\n\t\t&& svs.gametype != GT_LUA\n#endif\n\t\t)\t//make tics multiples of sv_maxtic (defaults to 0.1)\n\t{\n\t\tif (svs.gametype == GT_QUAKE2)\n\t\t\tmintic = maxtic = 0.1;\t//fucking fuckity fuck. we should warn about this.\n\t\tmintic = max(mintic, 1/1000.0);\n\n\t\tfor(;;)\n\t\t{\n\t\t\thost_frametime = sv.time - sv.world.physicstime;\n\t\t\tif (host_frametime<0)\n\t\t\t{\n\t\t\t\tif (host_frametime < -1)\n\t\t\t\t\tsv.world.physicstime = sv.time;\n\t\t\t\thost_frametime = 0;\n\t\t\t}\n\t\t\tif (!maxtics--)\n\t\t\t{\t//don't loop infinitely if we froze (eg debugger or suspend/hibernate)\n\t\t\t\tsv.world.physicstime = sv.time;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (!host_frametime || (host_frametime < mintic && realtime))\n\t\t\t\tbreak;\n\t\t\tif (host_frametime > maxtic)\n\t\t\t\thost_frametime = maxtic;\n\t\t\tsv.world.physicstime += host_frametime;\n\t\t\tmoved = true;\n\n\t\t\tswitch(svs.gametype)\n\t\t\t{\n#ifdef Q2SERVER\n\t\t\tcase GT_QUAKE2:\n\t\t\t\tge->RunFrame();\n\t\t\t\tbreak;\n#endif\n#ifdef Q3SERVER\n\t\t\tcase GT_QUAKE3:\n\t\t\t\tq3->sv.RunFrame();\n\t\t\t\tbreak;\n#endif\n\t\t\tdefault:\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\thost_frametime = trueframetime;\n\t\treturn moved;\n\t}\n\n\tif (svs.gametype != GT_HALFLIFE && /*sv.botsonthemap &&*/ progstype == PROG_QW)\n\t{\n\t\t//DP_SV_BOTCLIENT - make the bots move with qw physics.\n\t\t//They only move when there arn't any players on the server, but they should move at the right kind of speed if there are... hopefully\n\t\t//they might just be a bit lagged. they will at least be as smooth as other players are.\n\n\t\tusercmd_t ucmd;\n\t\tclient_t *oldhost;\n\t\tedict_t *oldplayer;\n\n#ifdef VM_Q1\n\t\tif (svs.gametype == GT_Q1QVM)\n\t\t{\n\t\t\tpr_global_struct->self = EDICT_TO_PROG(svprogfuncs, sv.world.edicts);\n\t\t\tpr_global_struct->other = EDICT_TO_PROG(svprogfuncs, sv.world.edicts);\n\t\t\tpr_global_struct->time = sv.world.physicstime;\n\t\t\tQ1QVM_StartFrame(true);\n\t\t}\n#endif\n\t\tif (1)\n\t\t{\n\t\t\tmemset(&ucmd, 0, sizeof(ucmd));\n\t\t\tfor (i = 0; i < sv.allocated_client_slots; i++)\n\t\t\t{\n\t\t\t\tif (svs.clients[i].state > cs_zombie && svs.clients[i].protocol == SCP_BAD && svs.clients[i].msecs >= 1000.0/77)\n\t\t\t\t{\t//then this is a bot\n\t\t\t\t\toldhost = host_client;\n\t\t\t\t\toldplayer = sv_player;\n\t\t\t\t\thost_client = &svs.clients[i];\n\t\t\t\t\thost_client->isindependant = true;\n\t\t\t\t\tsv_player = host_client->edict;\n\t\t\t\t\thost_client->localtime = sv.time;\n\n\t\t\t\t\tSV_PreRunCmd();\n\n\t\t\t\t\tif (svs.gametype == GT_Q1QVM)\n\t\t\t\t\t{\n\t\t\t\t\t\tucmd = svs.clients[i].lastcmd;\n\t\t\t\t\t\tucmd.msec = svs.clients[i].msecs;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tucmd.msec = svs.clients[i].msecs;\n\t\t\t\t\t\tucmd.angles[0] = (short)(sv_player->v->v_angle[0] * (65535/360.0f));\n\t\t\t\t\t\tucmd.angles[1] = (short)(sv_player->v->v_angle[1] * (65535/360.0f));\n\t\t\t\t\t\tucmd.angles[2] = (short)(sv_player->v->v_angle[2] * (65535/360.0f));\n\t\t\t\t\t\tucmd.forwardmove = sv_player->xv->movement[0];\n\t\t\t\t\t\tucmd.sidemove = sv_player->xv->movement[1];\n\t\t\t\t\t\tucmd.upmove = sv_player->xv->movement[2];\n\t\t\t\t\t\tucmd.buttons = (sv_player->v->button0?1:0) | (sv_player->v->button2?2:0);\n\t\t\t\t\t}\n\t\t\t\t\tucmd.msec = min(ucmd.msec, 250);\n\n\t\t\t\t\tSV_RunCmd(&ucmd, false);\n\t\t\t\t\tSV_PostRunCmd();\n\n\t\t\t\t\thost_client->lastcmd = ucmd;\t//allow the other clients to predict this bot.\n\n\t\t\t\t\thost_client = oldhost;\n\t\t\t\t\tsv_player = oldplayer;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n// don't bother running a frame if sys_ticrate seconds haven't passed\n\twhile (1)\n\t{\n\t\thost_frametime = sv.time - sv.world.physicstime;\n\t\tif (host_frametime < 0)\n\t\t{\n\t\t\tsv.world.physicstime = sv.time;\n\t\t\tbreak;\n\t\t}\n\t\tif (host_frametime <= 0 || host_frametime < mintic)\n\t\t\tbreak;\n\t\tif (host_frametime > maxtic && maxtic>0)\n\t\t{\n\t\t\tif (maxtics-- <= 0)\n\t\t\t{\n\t\t\t\t//timewarp, as we're running too slowly\n\t\t\t\tsv.world.physicstime = sv.time;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\thost_frametime = maxtic;\n\t\t}\n\t\tif (!host_frametime)\n\t\t\tcontinue;\n\n\t\tmoved = true;\n\n#ifdef HLSERVER\n\t\tif (svs.gametype == GT_HALFLIFE)\n\t\t{\n\t\t\tSVHL_RunFrame();\n\t\t\tsv.world.physicstime += host_frametime;\n\t\t\tcontinue;\n\t\t}\n#endif\n\n\t\tpr_global_struct->frametime = host_frametime;\n\n\t\tSV_ProgStartFrame ();\n\n\t\tPR_RunThreads(&sv.world);\n\n#ifdef USERBE\n\t\tif (sv.world.rbe)\n\t\t{\n#ifdef RAGDOLL\n\t\t\trag_doallanimations(&sv.world);\n#endif\n\t\t\tsv.world.rbe->RunFrame(&sv.world, host_frametime, sv_gravity.value);\n\t\t}\n#endif\n\n\n\t\tWorld_Physics_Frame(&sv.world);\n\n#ifdef VM_Q1\n\t\tif (svs.gametype == GT_Q1QVM)\n\t\t{\n\t\t\tpr_global_struct->self = EDICT_TO_PROG(svprogfuncs, sv.world.edicts);\n\t\t\tpr_global_struct->other = EDICT_TO_PROG(svprogfuncs, sv.world.edicts);\n\t\t\tpr_global_struct->time = sv.world.physicstime+host_frametime;\n\t\t\tQ1QVM_EndFrame();\n\t\t}\n\t\telse\n#endif\n\t\t\tif (EndFrameQC)\n\t\t{\n\t\t\tpr_global_struct->self = EDICT_TO_PROG(svprogfuncs, sv.world.edicts);\n\t\t\tpr_global_struct->other = EDICT_TO_PROG(svprogfuncs, sv.world.edicts);\n\t\t\tpr_global_struct->time = sv.world.physicstime+host_frametime;\n\t\t\tPR_ExecuteProgram (svprogfuncs, EndFrameQC);\n\t\t}\n\n#ifdef NETPREPARSE\n\t\tNPP_Flush();\t//flush it just in case there was an error and we stopped preparsing. This is only really needed while debugging.\n#endif\n\n\t\tsv.world.physicstime += host_frametime;\n\t}\n\thost_frametime = trueframetime;\n\treturn moved;\n}\n#endif\n\nvoid SV_SetMoveVars(void)\n{\n\tmovevars.stopspeed\t\t    = sv_stopspeed.value;\n\tmovevars.maxspeed\t\t\t= sv_maxspeed.value;\n\tmovevars.spectatormaxspeed  = sv_spectatormaxspeed.value;\n\tmovevars.accelerate\t\t    = sv_accelerate.value;\n\tmovevars.airaccelerate\t    = sv_airaccelerate.value;\n\tmovevars.wateraccelerate\t= sv_wateraccelerate.value;\n\tmovevars.friction\t\t\t= sv_friction.value;\n\tmovevars.waterfriction\t    = sv_waterfriction.value;\n\tmovevars.entgravity\t\t\t= 1.0;\n\tmovevars.stepheight\t\t\t= *sv_stepheight.string?sv_stepheight.value:PM_DEFAULTSTEPHEIGHT;\n\tmovevars.watersinkspeed\t\t= *pm_watersinkspeed.string?pm_watersinkspeed.value:60;\n\tmovevars.flyfriction\t\t= *pm_flyfriction.string?pm_flyfriction.value:4;\n\tmovevars.edgefriction\t\t= *pm_edgefriction.string?pm_edgefriction.value:2;\n\tmovevars.flags\t\t\t\t= MOVEFLAG_VALID|MOVEFLAG_NOGRAVITYONGROUND|(*pm_edgefriction.string?0:MOVEFLAG_QWEDGEBOX);\n}\n#endif\n"
  },
  {
    "path": "engine/server/sv_rankin.c",
    "content": "#include \"quakedef.h\"\n\n#ifndef CLIENTONLY\n//FIXME: this is shitty old code.\n//possible improvements: using a hash table for player names for faster logons\n//threading logins\n//using a real database...\n\n#ifdef SVRANKING\n\ntypedef struct {\n\tint ident;\n\tint version;\n\tint usedslots;\n\tint leader;\n\tint freeslot;\n} rankfileheader_t;\n\n//endian\n#define NOENDIAN\n#ifdef NOENDIAN\n#define swaplong(l) l\n#define swapfloat(f) f\n#else\n#define swaplong\tLittleLong\n#define swapfloat\tLittleFloat\n#endif\n\nrankfileheader_t rankfileheader;\nvfsfile_t *rankfile;\n\ncvar_t rank_autoadd = CVARD(\"rank_autoadd\", \"1\", \"Automatically register players into the ranking system\");\ncvar_t rank_needlogin = CVARD(\"rank_needlogin\", \"0\", \"If set to 1, prohibits players from joining if they're not yet registered. This will require an external mechanism to register users, presumably via rcon.\");\ncvar_t rank_filename = CVARD(\"rank_filename\", \"\", \"Specifies which file to use as a rankings database. Enables the ranking system if set.\");\ncvar_t rank_parms_first = CVARD(\"rank_parms_first\", \"0\", \"Mod setting: first parm saved\");\ncvar_t rank_parms_last = CVARD(\"rank_parms_last\", \"31\", \"Mod setting: the index of the last parm to be saved. Clamped to 32.\");\nchar rank_cvargroup[] = \"server rankings\";\n\n#define RANKFILE_VERSION ((NUM_RANK_SPAWN_PARMS==32)?0:0x00000001)\n#define RANKFILE_IDENT\t(('R'<<0)|('A'<<8)|('N'<<16)|('K'<<24))\n\nstatic void READ_PLAYERSTATS(int x, rankstats_t *os)\n{\n#ifndef NOENDIAN\n\tint i;\n#endif\n\tsize_t result;\n\n\tVFS_SEEK(rankfile, sizeof(rankfileheader_t)+sizeof(rankheader_t)+((x-1)*sizeof(rankinfo_t)));\n\tresult = VFS_READ(rankfile, os, sizeof(rankstats_t));\n\n\tif (result != sizeof(rankstats_t))\n\t\tCon_Printf(\"READ_PLAYERSTATS() fread: expected %lu, result was %u\\n\",(long unsigned int)sizeof(rankstats_t),(unsigned int)result);\n\n#ifndef NOENDIAN\n\tos->kills = swaplong(os->kills);\n\tos->deaths = swaplong(os->deaths);\n\tfor (i = 0; i < NUM_RANK_SPAWN_PARMS; i++)\n\t\tos->parm[i] = swapfloat(os->parm[i]);\n\tos->timeonserver = swapfloat(os->timeonserver);\n//\tos->flags1 = (os->flags1);\n//\tos->trustlevel = (os->trustlevel);\n//\tos->pad2 = (os->pad2);\n//\tos->pad3 = (os->pad3);\n#endif\n}\n\nstatic void WRITE_PLAYERSTATS(int x, rankstats_t *os)\n{\n#ifdef NOENDIAN\n\tVFS_SEEK(rankfile, sizeof(rankfileheader_t)+sizeof(rankheader_t)+((x-1)*sizeof(rankinfo_t)));\n\tVFS_WRITE(rankfile, os, sizeof(rankstats_t));\n#else\n\trankstats_t ns;\n\tint i;\n\tns.kills = swaplong(os->kills);\n\tns.deaths = swaplong(os->deaths);\n\tfor (i = 0; i < NUM_RANK_SPAWN_PARMS; i++)\n\t\tns.parm[i] = swapfloat(os->parm[i]);\n\tns.timeonserver = swapfloat(os->timeonserver);\n\tns.flags1 = (os->flags1);\n\tns.trustlevel = (os->trustlevel);\n\tns.pad2 = (os->pad2);\n\tns.pad3 = (os->pad3);\n\n\tVFS_SEEK(rankfile, sizeof(rankfileheader_t)+sizeof(rankheader_t)+((x-1)*sizeof(rankinfo_t)));\n\tVFS_WRITE(rankfile, &ns, sizeof(rankstats_t));\n#endif\n}\n\nstatic void READ_PLAYERHEADER(int x, rankheader_t *oh)\n{\n\tsize_t result;\n\n\tVFS_SEEK(rankfile, sizeof(rankfileheader_t)+((x-1)*sizeof(rankinfo_t)));\n\n\tresult = VFS_READ(rankfile, oh, sizeof(rankheader_t));\n\n\tif (result != sizeof(rankheader_t))\n\t\tCon_Printf(\"READ_PLAYERHEADER() fread: expected %lu, result was %u\\n\",(long unsigned int)sizeof(rankheader_t),(unsigned int)result);\n\n#ifndef NOENDIAN\n\toh->prev = swaplong(oh->prev);\t\t//score is held for convineance.\n\toh->next = swaplong(oh->next);\n//\tstrcpy(oh->name, oh->name);\n\toh->pwd = swaplong(oh->pwd);\n\toh->score = swapfloat(oh->score);\n#endif\n}\n\nstatic void WRITE_PLAYERHEADER(int x, rankheader_t *oh)\n{\n#ifdef NOENDIAN\n\tVFS_SEEK(rankfile, sizeof(rankfileheader_t)+((x-1)*sizeof(rankinfo_t)));\n\tVFS_WRITE(rankfile, &oh, sizeof(rankheader_t));\n#else\n\trankheader_t nh;\n\tnh.prev = swaplong(oh->prev);\t\t//score is held for convineance.\n\tnh.next = swaplong(oh->next);\n\tQ_strncpyz(nh.name, oh->name, sizeof(nh.name));\n\tnh.pwd = swaplong(oh->pwd);\n\tnh.score = swapfloat(oh->score);\n\n\tVFS_SEEK(rankfile, sizeof(rankfileheader_t)+((x-1)*sizeof(rankinfo_t)));\n\tVFS_WRITE(rankfile, &nh, sizeof(rankheader_t));\n#endif\n}\n\nstatic void READ_PLAYERINFO(int x, rankinfo_t *inf)\n{\n\tREAD_PLAYERHEADER(x, &inf->h);\n\tREAD_PLAYERSTATS(x, &inf->s);\n}\n\nstatic void WRITEHEADER(void)\n{\n\trankfileheader_t nh;\n\n\tnh.ident\t\t= RANKFILE_IDENT;\n\tnh.version\t\t= swaplong(RANKFILE_VERSION);\n\tnh.usedslots\t= swaplong(rankfileheader.usedslots);\n\tnh.leader\t\t= swaplong(rankfileheader.leader);\n\tnh.freeslot\t\t= swaplong(rankfileheader.freeslot);\n\n\tVFS_SEEK(rankfile, 0);\n\tVFS_WRITE(rankfile, &nh, sizeof(rankfileheader_t));\n}\n//#define WRITEHEADER() \t{fseek(rankfile, 0, SEEK_SET);fwrite(&rankfileheader, 1, sizeof(rankfileheader_t), rankfile);}\n\n#define NAMECMP(saved, against)\tQ_strncasecmp(saved, against, 31)\n\nqboolean Rank_OpenRankings(void)\n{\n\tsize_t result;\n\tqboolean created;\n\tif (!rankfile)\n\t{\n\t\tif (!*rank_filename.string)\n\t\t{\n\t\t\treturn false;\n\t\t}\n\n\t\trankfile = FS_OpenVFS(rank_filename.string, \"r+b\", FS_GAMEONLY);\n\t\tif (!rankfile)\t//hmm... try creating\n\t\t{\n\t\t\trankfile = FS_OpenVFS(rank_filename.string, \"w+b\", FS_GAMEONLY);\n\t\t\tcreated = true;\n\t\t}\n\t\telse\n\t\t\tcreated = false;\n\t\tif (!rankfile)\n\t\t\treturn false;\t//couldn't open file.\n\n\t\tmemset(&rankfileheader, 0, sizeof(rankfileheader));\n\n\t\tVFS_SEEK(rankfile, 0);\n\t\tresult = VFS_READ(rankfile, &rankfileheader, sizeof(rankfileheader_t));\n\n\t\tif (result != sizeof(rankfileheader_t))\n\t\t\tCon_Printf(\"Rank_OpenRankings() fread: expected %lu, result was %u (%s)\\n\",(long unsigned int)sizeof(rankfileheader_t),(unsigned int)result, rank_filename.string);\n\n\t\trankfileheader.version\t\t= swaplong(rankfileheader.version);\n\t\trankfileheader.usedslots\t= swaplong(rankfileheader.usedslots);\n\t\trankfileheader.leader\t\t= swaplong(rankfileheader.leader);\n\t\trankfileheader.freeslot\t\t= swaplong(rankfileheader.freeslot);\n\n\t\tif (!created && (rankfileheader.version != RANKFILE_VERSION || rankfileheader.ident != RANKFILE_IDENT))\n\t\t{\n\t\t\tCon_Printf(\"Rank file is version %i not %i\\nEither delete the file or use an equivelent version of \" FULLENGINENAME \"\\n\", rankfileheader.version, RANKFILE_VERSION);\n\t\t\tVFS_CLOSE(rankfile);\n\t\t\trankfile = NULL;\n\n\t\t\treturn false;\n\t\t}\n\n\t\treturn true;\t//success.\n\t}\n\treturn true;\t//already open\n}\n\nvoid LINKUN(int id)\n{\n\tint idnext, idprev;\n\trankheader_t hnext = {0}, hprev = {0}, info;\n\n\tREAD_PLAYERHEADER(id, &info);\n\n\tidnext = info.next;\n\tif (idnext)\n\t\tREAD_PLAYERHEADER(idnext, &hnext);\n\tidprev = info.prev;\n\tif (idprev)\n\t\tREAD_PLAYERHEADER(idprev, &hprev);\n\n\tif (idnext)\n\t{\n\t\thnext.prev = idprev;\n\t\tWRITE_PLAYERHEADER(idnext, &hnext);\n\t}\n\tif (idprev)\n\t{\n\t\thprev.next = idnext;\n\t\tWRITE_PLAYERHEADER(idprev, &hprev);\n\t}\n\telse if (rankfileheader.leader == id)\t//ensure header is accurate\n\t{\n\t\trankfileheader.leader = info.next;\n\t\tWRITEHEADER();\n\t}\n\telse if (rankfileheader.freeslot == id)\n\t{\n\t\trankfileheader.freeslot = info.next;\n\t\tWRITEHEADER();\n\t}\n\n\tinfo.next = 0;\n\tinfo.prev = 0;\n\tWRITE_PLAYERHEADER(id, &info);\n}\n\nvoid LINKBEFORE(int bef, int id, rankheader_t *info)\n{\n\tint idnext, idprev;\n\trankheader_t hnext, hprev = {0};\n\n\tif (!bef)\n\t\tSys_Error(\"Cannot link before no entry\\n\");\n\n\tidnext = bef;\n\tREAD_PLAYERHEADER(idnext, &hnext);\n\tidprev = hnext.prev;\n\tif (idprev)\n\t\tREAD_PLAYERHEADER(idprev, &hprev);\n\n//now we know the before and after entries.\n\n\thnext.prev = id;\n\tWRITE_PLAYERHEADER(idnext, &hnext);\n\n\tif (idprev)\n\t{\n\t\thprev.next = id;\n\t\tWRITE_PLAYERHEADER(idprev, &hprev);\n\t}\n\telse if (rankfileheader.leader == bef)\n\t{\n\t\trankfileheader.leader = id;\n\t\tWRITEHEADER();\n\t}\n\telse if (rankfileheader.freeslot == bef)\n\t{\n\t\trankfileheader.freeslot = id;\n\t\tWRITEHEADER();\n\t}\n\tinfo->next = idnext;\n\tinfo->prev = idprev;\n\tWRITE_PLAYERHEADER(id, info);\n}\n\nvoid LINKAFTER(int aft, int id, rankheader_t *info)\n{\n\tint idnext, idprev;\n\trankheader_t hnext = {0}, hprev = {0};\n\n\tidprev = aft;\n\tif (idprev)\n\t{\n\t\tREAD_PLAYERHEADER(idprev, &hprev);\n\t\tidnext = hprev.next;\n\t}\n\telse\n\t\tidnext = rankfileheader.leader;\n\n\tif (idnext)\n\t\tREAD_PLAYERHEADER(idnext, &hnext);\n\n//now we know the before and after entries.\n\n\tif (idnext)\n\t{\n\t\thnext.prev = id;\n\t\tWRITE_PLAYERHEADER(idnext, &hnext);\n\t}\n\n\tif (idprev)\n\t{\n\t\thprev.next = id;\n\t\tWRITE_PLAYERHEADER(idprev, &hprev);\n\t}\n\telse if (rankfileheader.leader == idnext)\n\t{\n\t\trankfileheader.leader = id;\n\t\tWRITEHEADER();\n\t}\n\telse if (rankfileheader.freeslot == idnext)\n\t{\n\t\trankfileheader.freeslot = id;\n\t\tWRITEHEADER();\n\t}\n\tinfo->next = idnext;\n\tinfo->prev = idprev;\n\tWRITE_PLAYERHEADER(id, info);\n}\n\nrankstats_t *Rank_GetPlayerStats(int id, rankstats_t *buffer)\t//returns the players persistant stats.\n{\n\tif (!Rank_OpenRankings())\n\t\treturn NULL;\n\tif (!id)\n\t{\n\t\tCon_Printf(\"WARNING: Rank_GetPlayerStats with id 0\\n\");\n\t\tmemset(buffer, 0, sizeof(rankstats_t));\n\t\treturn NULL;\n\t}\n\tREAD_PLAYERSTATS(id, buffer);\n\n\treturn buffer;\n}\nrankinfo_t *Rank_GetPlayerInfo(int id, rankinfo_t *buffer)\t\t//return stats + rankings.\n{\n\tif (!id)\n\t{\n\t\tCon_Printf(\"WARNING: Rank_GetPlayerInfo with id 0\\n\");\n\t\tmemset(buffer, 0, sizeof(rankinfo_t));\n\t\treturn NULL;\n\t}\n\n\tif (!Rank_OpenRankings())\n\t\treturn NULL;\n\n\tREAD_PLAYERINFO(id, buffer);\n\n\treturn buffer;\n}\nvoid Rank_SetPlayerStats(int id, rankstats_t *stats)\n{\n\t//rewrite to seek in a proper direction.\n\tint nid;\n\trankheader_t rh, nh;\n\n\tif (!id)\n\t{\n\t\tCon_Printf(\"WARNING: Rank_SetPlayerStats with id 0\\n\");\n\t\treturn;\n\t}\n\n\t//write\n\tWRITE_PLAYERSTATS(id, stats);\n\n\t//now re-sort.\n\tREAD_PLAYERHEADER(id, &nh);\n\tnh.score = (stats->kills+1)/((float)stats->deaths+1);\n\t//WRITE_PLAYERHEADER(id, &nh); //saved on link.\n\n\tLINKUN(id);\n\n\tnid = rankfileheader.leader;\n\tif (!nid)\t//Hmm. First player!\n\t{\n\t\tLINKAFTER(0, id, &nh);\n\t\tVFS_FLUSH(rankfile);\n\t\treturn;\n\t}\n\twhile(nid)\n\t{\n\t\tREAD_PLAYERHEADER(nid, &rh);\n\t\tif (rh.score < nh.score)\n\t\t{\n\t\t\tLINKAFTER(rh.prev, id, &nh);\n\t\t\t//LINKBEFORE(nid, id, &nh);\t//we are doing better than this guy.\n\t\t\tVFS_FLUSH(rankfile);\n\t\t\treturn;\n\t\t}\n\t\tif (!rh.next)\n\t\t{\n\t\t\tLINKAFTER(nid, id, &nh);\t//Bum. We got to the end of the list and we are the WORST player!\n\t\t\tVFS_FLUSH(rankfile);\n\t\t\treturn;\n\t\t}\n\t\tnid = rh.next;\n\t}\n}\n\nint Rank_GetPlayerID(char *guid, const char *name, int pwd, qboolean allowadd, qboolean requirepasswordtobeset)\n{\n\trankstats_t rs;\n\trankheader_t rh;\n\tint id;\n\n\tif (requirepasswordtobeset)\n\t\tif (!pwd)\n\t\t\treturn 0;\n\n\tif (!Rank_OpenRankings())\n\t\treturn 0;\n\n\tid = rankfileheader.leader;\t//assumtion. A leader is more likly to be logging in than a begginer.\n\twhile(id)\n\t{\n\t\tREAD_PLAYERHEADER(id, &rh);\n\t\tif (!NAMECMP(rh.name, name))\n\t\t{\n\t\t\tif (rh.pwd == pwd || !rh.pwd)\n\t\t\t{\n\t\t\t\tif (!rh.pwd && requirepasswordtobeset)\n\t\t\t\t\treturn 0;\n\t\t\t\treturn id;\n\t\t\t}\n\t\t\treturn 0;\n\t\t}\n\t\tid = rh.next;\n\t}\n\n\tif (!rank_autoadd.value || !allowadd)\n\t\treturn 0;\n\n\tid = rankfileheader.freeslot;\n\tif (id)\n\t{\n\t\tREAD_PLAYERHEADER(id, &rh);\n\t\trankfileheader.freeslot = rh.next;\n\t\tWRITEHEADER();\n\n\t\tmemset(&rh, 0, sizeof(rh));\n\t\tQ_strncpyz(rh.name, name, sizeof(rh.name));\n\t\trh.pwd = pwd;\n\t\trh.prev = 0;\n\t\trh.next = rankfileheader.usedslots;\n\t\trankfileheader.usedslots = id;\n\t\tWRITEHEADER();\n\n\t\tWRITE_PLAYERHEADER(id, &rh);\n\n\t\tmemset(&rs, 0, sizeof(rs));\n\t\trs.trustlevel = 1;\n\t\tRank_SetPlayerStats(id, &rs);\n\n\t\tVFS_FLUSH(rankfile);\n\t\treturn id;\n\t}\n\n\tid = ++rankfileheader.usedslots;\n\tWRITEHEADER();\n\tmemset(&rh, 0, sizeof(rh));\n\tQ_strncpyz(rh.name, name, sizeof(rh.name));\n\trh.prev = 0;\n\trh.pwd = pwd;\n\trh.next = 0;\n\tWRITE_PLAYERHEADER(id, &rh);\n\n\tmemset(&rs, 0, sizeof(rs));\n\trs.trustlevel = 1;\n\tWRITE_PLAYERSTATS(id, &rs);\n\n\tRank_SetPlayerStats(id, &rs);\n\n\tVFS_FLUSH(rankfile);\n\treturn id;\n}\n\nvoid Rank_AddUser_f (void)\n{\n\trankstats_t rs;\n\trankheader_t rh;\n\tint id;\n\n\tchar *name = Cmd_Argv(1);\n\tint pwd = atoi(Cmd_Argv(2));\n\tint userlevel = atoi(Cmd_Argv(3));\n\tchar fixed[80];\n\n\tif (Cmd_Argc() < 2)\n\t{\n\t\tCon_Printf(\"%s: <name> [pwd] [rights]\\n\", Cmd_Argv(0));\n\t\treturn;\n\t}\n\t//2\n\tif (Cmd_Argc() >= 4)\n\t{\n\t\tif (userlevel >= Cmd_ExecLevel)\n\t\t{\n\t\t\tCon_Printf(\"You cannot add a user of equal or higher rank.\\n\");\n\t\t\treturn;\n\t\t}\n\t\telse if (userlevel < RESTRICT_MIN)\n\t\t\tuserlevel = RESTRICT_MIN;\n\t}\n\tif (Cmd_Argc() > 4)\n\t{\n\t\tCon_Printf(\"Too many arguments\\n\");\n\t\treturn;\n\t}\n\n\tSV_FixupName(name, fixed, sizeof(fixed));\n\tname = fixed;\n\n\tif (!Rank_OpenRankings())\n\t{\n\t\tCon_Printf(\"Failed to open rankings file.\\n\");\n\t\treturn;\n\t}\n\n\tid = rankfileheader.leader;\n\twhile(id)\n\t{\n\t\tREAD_PLAYERHEADER(id, &rh);\n\t\tif (!NAMECMP(rh.name, name))\n\t\t{\n\t\t\tCon_Printf(\"User %s already exists\\n\", name);\n\t\t\treturn;\n\t\t}\n\t\tid = rh.next;\n\t}\n\n\tid = rankfileheader.freeslot;\n\tif (id)\n\t{\n\t\tREAD_PLAYERHEADER(id, &rh);\n\t\trankfileheader.freeslot = rh.next;\n\t\tWRITEHEADER();\n\n\t\tmemset(&rh, 0, sizeof(rh));\n\t\tQ_strncpyz(rh.name, name, sizeof(rh.name));\n\t\trh.pwd = pwd;\n\t\trh.prev = 0;\n\t\trh.next = rankfileheader.usedslots;\n\t\trankfileheader.usedslots = id;\n\t\tWRITEHEADER();\n\n\t\tWRITE_PLAYERHEADER(id, &rh);\n\n\t\tmemset(&rs, 0, sizeof(rs));\n\t\trs.trustlevel = userlevel;\n\t\tRank_SetPlayerStats(id, &rs);\n\n\t\tVFS_FLUSH(rankfile);\n\t\treturn;\n\t}\n\n\tid = ++rankfileheader.usedslots;\n\tWRITEHEADER();\n\tmemset(&rh, 0, sizeof(rh));\n\tQ_strncpyz(rh.name, name, sizeof(rh.name));\n\trh.prev = 0;\n\trh.pwd = pwd;\n\trh.next = 0;\n\tWRITE_PLAYERHEADER(id, &rh);\n\n\tmemset(&rs, 0, sizeof(rs));\n\trs.trustlevel = userlevel;\n#if NUM_RANK_SPAWN_PARMS>32\n\trs.created = rs.lastseen = time(NULL);\n#endif\n\tWRITE_PLAYERSTATS(id, &rs);\n\n\tRank_SetPlayerStats(id, &rs);\n\n\tVFS_FLUSH(rankfile);\n}\n\nvoid Rank_SetPass_f (void)\n{\n\trankheader_t rh;\n\tchar *name = Cmd_Argv(1);\n\tint newpass = atoi(Cmd_Argv(2));\n\tchar fixed[80];\n\n\tint id;\n\n\tif (Cmd_Argc() != 3)\n\t{\n\t\tCon_Printf(\"setpass <name> <newpass>\\n\");\n\t\treturn;\n\t}\n\n\tif (!Rank_OpenRankings())\n\t{\n\t\tCon_Printf(\"Failed to open rankings file.\\n\");\n\t\treturn;\n\t}\n\n\tSV_FixupName(name, fixed, sizeof(fixed));\n\tname = fixed;\n\n\tid = rankfileheader.leader;\n\twhile(id)\n\t{\n\t\tREAD_PLAYERHEADER(id, &rh);\n\t\tif (!NAMECMP(rh.name, name))\n\t\t{\n\t\t\tCon_Printf(\"Changing passcode of user %s.\\n\", rh.name);\n\t\t\trh.pwd = newpass;\n\t\t\tWRITE_PLAYERHEADER(id, &rh);\n\t\t\treturn;\n\t\t}\n\t\tid = rh.next;\n\t}\n}\n\nint Rank_GetPass (char *name)\n{\n\trankheader_t rh;\n\tchar fixed[80];\n\n\tint id;\n\n\tif (!Rank_OpenRankings())\n\t{\n\t\tCon_Printf(\"Failed to open rankings file.\\n\");\n\t\treturn 0;\n\t}\n\n\tSV_FixupName(name, fixed, sizeof(fixed));\n\tname = fixed;\n\n\tid = rankfileheader.leader;\n\twhile(id)\n\t{\n\t\tREAD_PLAYERHEADER(id, &rh);\n\t\tif (!NAMECMP(rh.name, name))\n\t\t{\n\t\t\treturn rh.pwd;\n\t\t}\n\t\tid = rh.next;\n\t}\n\treturn 0;\n}\n\n\nint Rank_Enumerate (unsigned int first, unsigned int last, void (*callback) (const rankinfo_t *ri))\t//leader first.\n{\n\trankinfo_t ri;\n\tint id;\n\tint num;\n\n\tif (!Rank_OpenRankings())\n\t{\n\t\tCon_Printf(\"Failed to open rankings file.\\n\");\n\t\treturn 0;\n\t}\n\n\tid = rankfileheader.leader;\t//start at the leaders\n\tnum=1;\n\twhile(id)\n\t{\n\t\tREAD_PLAYERINFO(id, &ri);\n\n\t\tif (num >= last)\n\t\t\treturn num - first;\n\t\tif (num >= first)\n\t\t\tcallback(&ri);\n\n\t\tnum++;\n\t\tid = ri.h.next;\n\t}\n\treturn num - first;\n}\n\nvoid Rank_RankingList_f (void)\n{\n\trankinfo_t ri;\n\tint id;\n\tint num;\n\n\tvfsfile_t *outfile;\n\n\tif (!Rank_OpenRankings())\n\t{\n\t\tCon_Printf(\"Failed to open rankings file.\\n\");\n\t\treturn;\n\t}\n\n\toutfile = FS_OpenVFS(\"list.txt\", \"wb\", FS_GAMEONLY);\n\tif (!outfile)\n\t{\n\t\tCon_Printf(\"Couldn't open list.txt\\n\");\n\t\treturn;\n\t}\n\n\tVFS_PRINTF(outfile, \"%5s: %32s, %5s %5s\\r\\n\", \"\", \"Name\", \"Kills\", \"Deaths\");\n\n\tid = rankfileheader.leader;\t//start at the leaders\n\tnum=1;\n\twhile(id)\n\t{\n\t\tREAD_PLAYERINFO(id, &ri);\n\n\t\tVFS_PRINTF(outfile, \"%5i: %32s, %5i %5i\\r\\n\", num, ri.h.name, ri.s.kills, ri.s.deaths);\n\n\t\tnum++;\n\t\tid = ri.h.next;\n\t}\n\n\tVFS_CLOSE(outfile);\n}\n\nvoid Rank_RemoveID_f (void)\n{\n\trankinfo_t ri;\n\tint id;\n\tint num;\n\tint remnum;\n\n\tif (Cmd_Argc() < 2)\n\t{\n\t\tCon_Printf(\"Removes a ranking entry.\\nUse ranklist to find the entry number.\");\n\t\treturn;\n\t}\n\tremnum = atoi(Cmd_Argv(1));\n\n\tif (!Rank_OpenRankings())\n\t{\n\t\tCon_Printf(\"Failed to open rankings file.\\n\");\n\t\treturn;\n\t}\n\n\tid = rankfileheader.leader;\t//start at the leaders\n\tnum=1;\n\twhile(id)\n\t{\n\t\tREAD_PLAYERINFO(id, &ri);\n\n\t\tif (num == remnum)\n\t\t{\n\t\t\tLINKUN(id);\n\t\t\tri.h.next = rankfileheader.freeslot;\n\t\t\tri.h.prev = 0;\n\t\t\trankfileheader.freeslot = id;\n\t\t\tWRITE_PLAYERHEADER(id, &ri.h);\n\t\t\tWRITEHEADER();\n\t\t\tVFS_FLUSH(rankfile);\n\n\t\t\tCon_Printf(\"Client %s removed from rankings\\n\", ri.h.name);\n\t\t\treturn;\n\t\t}\n\t\tnum++;\n\t\tid = ri.h.next;\n\t}\n\n\tCon_Printf(\"Client %i not found\\n\", remnum);\n}\n\nvoid Rank_ListTop10_f (void)\n{\n\trankinfo_t ri;\n\tint id;\n\tint num;\n\textern redirect_t\tsv_redirected;\n\n\tif (!Rank_OpenRankings())\n\t{\n\t\tCon_Printf(\"Failed to open rankings file.\\n\");\n\t\treturn;\n\t}\n\n\tid = rankfileheader.leader;\t//start at the leaders\n\tnum=1;\n\twhile(id)\n\t{\n\t\tREAD_PLAYERINFO(id, &ri);\n\n\t\tif (sv_redirected)\n\t\t\tCon_Printf(\"%2i: %5i %5i %s\\n\", num, ri.s.kills, ri.s.deaths, ri.h.name);\n\t\telse\n\t\t\tCon_Printf(\"%2i: %32s, %5i %5i\\n\", num, ri.h.name, ri.s.kills, ri.s.deaths);\n\t\tif (num >= 10)\n\t\t\tbreak;\n\n\t\tnum++;\n\t\tid = ri.h.next;\n\t}\n\tif (num < 10)\n\t\tCon_Printf(\"END\\n\");\n}\n\nvoid Rank_Find_f (void)\n{\n\trankinfo_t ri;\n\tint id;\n\n\tchar *match = Q_strlwr(Cmd_Argv(1));\n\n\tif (!Rank_OpenRankings())\n\t{\n\t\tCon_Printf(\"Failed to open rankings file.\\n\");\n\t\treturn;\n\t}\n\n\tid = rankfileheader.leader;\t//start at the leaders\n\n\twhile(id)\n\t{\n\t\tREAD_PLAYERINFO(id, &ri);\n\n\t\tif (wildcmp(match, Q_strlwr(ri.h.name)))\n\t\t{\n\t\t\tCon_Printf(\"%i %s\\n\", id, ri.h.name);\n\t\t}\n\n\t\tid = ri.h.next;\n\t}\n}\n\nvoid Rank_Refresh_f(void)\n{\n\tint i;\n\tif (!Rank_OpenRankings())\n\t{\n\t\tCon_Printf(\"Failed to open rankings file.\\n\");\n\t\treturn;\n\t}\n\n\tfor (i=0, host_client = svs.clients ; i<svs.allocated_client_slots ; i++, host_client++)\n\t{\n\t\tif (host_client->state != cs_spawned)\n\t\t\tcontinue;\n\n\t\tif (host_client->rankid)\n\t\t{\n\t\t\trankstats_t rs = {0};\n\t\t\tRank_GetPlayerStats(host_client->rankid, &rs);\n\t\t\trs.timeonserver += realtime - host_client->stats_started;\n\t\t\thost_client->stats_started = realtime;\n\t\t\trs.kills += host_client->kills;\n\t\t\trs.deaths += host_client->deaths;\n\t\t\thost_client->kills=0;\n\t\t\thost_client->deaths=0;\n\t\t\tRank_SetPlayerStats(host_client->rankid, &rs);\n\t\t}\n\t}\n\tif (rankfile)\n\t{\n\t\tVFS_CLOSE(rankfile);\n\t\trankfile = NULL;\n\t}\n}\n\nvoid Rank_RCon_f(void)\n{\n\tint gofor, num, id;\n\tint newlevel;\n\trankstats_t rs = {0};\n\trankinfo_t ri;\n\n\tif (!Rank_OpenRankings())\n\t{\n\t\tCon_Printf(\"Failed to open rankings file.\\n\");\n\t\treturn;\n\t}\n\n\tgofor = atoi(Cmd_Argv(1));\n\tnewlevel = atoi(Cmd_Argv(2));\n\tif (newlevel >= Cmd_ExecLevel)\n\t{\n\t\tCon_Printf(\"You cannot promote a user to the same level as you\\n\");\n\t\treturn;\n\t}\n\telse if (newlevel < RESTRICT_MIN)\n\t\tnewlevel = RESTRICT_MIN;\n\n\t//get user id\n\tid = rankfileheader.leader;\t//start at the leaders\n\tnum = 1;\n\twhile(id)\n\t{\n\t\tREAD_PLAYERINFO(id, &ri);\n\n\t\tif (num == gofor)\n\t\t{\n\t\t\t//save new level\n\t\t\tRank_GetPlayerStats(id, &rs);\n\n\t\t\tif (rs.trustlevel >= Cmd_ExecLevel)\n\t\t\t{\n\t\t\t\tCon_Printf(\"You cannot demote a higher or equal user.\\n\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\trs.trustlevel = newlevel;\n\t\t\tRank_SetPlayerStats(id, &rs);\n\n\t\t\tif (!ri.h.pwd && newlevel > 1)\n\t\t\t\tCon_Printf(\"WARNING: user has no password set\\n\");\n\n\t\t\tVFS_FLUSH(rankfile);\n\t\t\treturn;\n\t\t}\n\n\t\tnum++;\n\t\tid = ri.h.next;\n\t}\n\tCon_Printf(\"Couldn't find ranked user %i\\n\", gofor);\n}\n\n\nvoid Rank_RegisterCommands(void)\n{\n\tCmd_AddCommand(\"ranklist\", Rank_RankingList_f);\n\tCmd_AddCommand(\"ranktopten\", Rank_ListTop10_f);\n\tCmd_AddCommand(\"rankfind\", Rank_Find_f);\n\tCmd_AddCommand(\"rankremove\", Rank_RemoveID_f);\n\tCmd_AddCommand(\"rankrefresh\", Rank_Refresh_f);\n\n\tCmd_AddCommand(\"rankrconlevel\", Rank_RCon_f);\n\n\tCmd_AddCommand(\"rankadd\", Rank_AddUser_f);\n\tCmd_AddCommand(\"adduser\", Rank_AddUser_f);\n\tCmd_AddCommand(\"setpass\", Rank_SetPass_f);\n\n\tCvar_Register(&rank_autoadd, rank_cvargroup);\n\tCvar_Register(&rank_needlogin, rank_cvargroup);\n\tCvar_Register(&rank_filename, rank_cvargroup);\n\n\tCvar_Register(&rank_parms_first, rank_cvargroup);\n\tCvar_Register(&rank_parms_last, rank_cvargroup);\n}\nvoid Rank_Flush (void)\t//new game dir?\n{\n\tif (rankfile)\n\t{\n\t\tRank_Refresh_f();\n\t\tif (!rankfile)\n\t\t\treturn;\n\t\tVFS_CLOSE(rankfile);\n\t\trankfile=NULL;\n\t}\n}\n#endif\n#endif\n"
  },
  {
    "path": "engine/server/sv_send.c",
    "content": "/*\nCopyright (C) 1996-1997 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n*/\n// sv_main.c -- server main program\n\n#include \"quakedef.h\"\n#include \"pr_common.h\"\n\n#ifndef CLIENTONLY\n\n#define CHAN_AUTO   0\n#define CHAN_WEAPON 1\n#define CHAN_VOICE  2\n#define CHAN_ITEM   3\n#define CHAN_BODY   4\n\nextern cvar_t sv_showpredloss;\nextern cvar_t sv_gravity, sv_friction, sv_waterfriction, sv_gamespeed, sv_stopspeed, sv_spectatormaxspeed, sv_accelerate, sv_airaccelerate, sv_wateraccelerate, pm_edgefriction, sv_reliable_sound;\nextern cvar_t  dpcompat_stats;\n\n/*\n=============================================================================\n\nCon_Printf redirection\n\n=============================================================================\n*/\n\nchar\tsv_redirected_buf[countof(sv_redirected_buf)];\n\nredirect_t\tsv_redirected;\nint sv_redirectedlang;\n\nextern cvar_t sv_phs;\n\n/*\n==================\nSV_FlushRedirect\n==================\n*/\nvoid SV_FlushRedirect (void)\n{\n\tint totallen;\n\tchar\tsend[sizeof(sv_redirected_buf)+6];\n\n\tif (!*sv_redirected_buf)\n\t\treturn;\n\n\tif (sv_redirected == RD_PACKET || sv_redirected == RD_PACKET_LOG)\n\t{\n\t\t//log it to the rcon log if its not just a status response\n\t\tif (sv_redirected == RD_PACKET_LOG)\n\t\t\tLog_String(LOG_RCON, sv_redirected_buf);\n\n\t\tsend[0] = 0xff;\n\t\tsend[1] = 0xff;\n\t\tsend[2] = 0xff;\n\t\tsend[3] = 0xff;\n\t\tsend[4] = A2C_PRINT;\n\t\tmemcpy (send+5, sv_redirected_buf, strlen(sv_redirected_buf)+1);\n\n\t\tNET_SendPacket (svs.sockets, strlen(send)+1, send, &net_from);\n\t}\n#ifdef SUBSERVERS\n\telse if (sv_redirected == RD_MASTER)\n\t{\n\t\tsizebuf_t s;\n\n\t\tmemset(&s, 0, sizeof(s));\n\t\ts.data = send;\n\t\ts.maxsize = sizeof(send);\n\t\ts.cursize = 2;\n\n\t\tMSG_WriteByte(&s, ccmd_print);\n\t\tMSG_WriteString(&s, sv_redirected_buf);\n\t\tSSV_InstructMaster(&s);\n\t}\n#endif\n\telse if (sv_redirected == RD_CLIENT)\n\t{\n\t\tint chop;\n\t\tchar spare;\n\t\tchar *s = sv_redirected_buf;\n\t\ttotallen = strlen(s)+3;\n\t\twhile (sizeof(host_client->backbuf_data[0])/2 < totallen)\n\t\t{\n\t\t\tchop = sizeof(host_client->backbuf_data[0]) / 2;\n\t\t\tspare = s[chop];\n\t\t\ts[chop] = '\\0';\n\n\t\t\tClientReliableWrite_Begin (host_client, host_client->protocol==SCP_QUAKE2?svcq2_print:svc_print, chop+3);\n\t\t\tClientReliableWrite_Byte (host_client, PRINT_HIGH);\n\t\t\tClientReliableWrite_String (host_client, s);\n\n\t\t\ts += chop;\n\t\t\ttotallen -= chop;\n\t\t\ts[0] = spare;\n\t\t}\n\t\tClientReliableWrite_Begin (host_client, host_client->protocol==SCP_QUAKE2?svcq2_print:svc_print, strlen(s)+3);\n\t\tClientReliableWrite_Byte (host_client, PRINT_HIGH);\n\t\tClientReliableWrite_String (host_client, s);\n\t}\n\n\t// clear it\n\tsv_redirected_buf[0] = 0;\n}\n\n\n/*\n==================\nSV_BeginRedirect\n\n  Send Con_Printf data to the remote client\n  instead of the console\n==================\n*/\nvoid SV_BeginRedirect (redirect_t rd, int lang)\n{\n\tSV_FlushRedirect();\n\n\tsv_redirected = rd;\n\tsv_redirectedlang = lang;\n\tsv_redirected_buf[0] = 0;\n}\n\nvoid SV_EndRedirect (void)\n{\n\tSV_FlushRedirect ();\n\tsv_redirectedlang = 0;\t//clenliness rather than functionality. Shouldn't be needed.\n\tsv_redirected = RD_NONE;\n}\n\n/*\n=============================================================================\n\nEVENT MESSAGES\n\n=============================================================================\n*/\n\n//Directly print to a client without translating nor printing into an mvd. generally for error messages due to the lack of mvd thing.\nvoid SV_PrintToClient(client_t *cl, int level, const char *string)\n{\n\tif (cl->controller)\n\t\tcl = cl->controller;\n\n\tswitch (cl->protocol)\n\t{\n\tcase SCP_BAD:\t//bot\n\t\tbreak;\n\tcase SCP_QUAKE2:\n\tcase SCP_QUAKE2EX:\n#ifdef Q2SERVER\n\t\tClientReliableWrite_Begin (cl, svcq2_print, strlen(string)+3);\n\t\tClientReliableWrite_Byte (cl, level);\n\t\tClientReliableWrite_String (cl, string);\n#endif\n\t\tbreak;\n\tcase SCP_QUAKE3:\n\t\tbreak;\n\tcase SCP_QUAKEWORLD:\n\t\tClientReliableWrite_Begin (cl, svc_print, strlen(string)+3);\n\t\tClientReliableWrite_Byte (cl, level);\n\t\tClientReliableWrite_String (cl, string);\n\t\tbreak;\n\tcase SCP_DARKPLACES6:\n\tcase SCP_DARKPLACES7:\n\tcase SCP_NETQUAKE:\n\tcase SCP_BJP3:\n\tcase SCP_FITZ666:\n#ifdef NQPROT\n\t\tClientReliableWrite_Begin (cl, svc_print, strlen(string)+3);\n\t\tif (level == PRINT_CHAT)\n\t\t\tClientReliableWrite_Byte (cl, 1);\n\t\tClientReliableWrite_String (cl, string);\n#endif\n\t\tbreak;\n\t}\n}\n//translate it, but avoid 'public' mvd prints.\nvoid SV_TPrintToClient(client_t *cl, int level, const char *string)\n{\n\tstring = langtext(string, cl->language);\n\tSV_PrintToClient(cl, level, string);\n}\n\nvoid SV_StuffcmdToClient(client_t *cl, const char *string)\n{\n\tswitch (cl->protocol)\n\t{\n\tcase SCP_BAD:\t//bot\n\t\tbreak;\n\tcase SCP_QUAKE2:\n\tcase SCP_QUAKE2EX:\n#ifdef Q2SERVER\n\t\tClientReliableWrite_Begin (cl, svcq2_stufftext, strlen(string)+3);\n\t\tClientReliableWrite_String (cl, string);\n#endif\n\t\tbreak;\n\tcase SCP_QUAKE3:\n\t\tbreak;\n\tcase SCP_QUAKEWORLD:\n\tcase SCP_DARKPLACES6:\n\tcase SCP_DARKPLACES7:\n\tcase SCP_NETQUAKE:\n\tcase SCP_BJP3:\n\tcase SCP_FITZ666:\n\t\tif (cl->controller)\n\t\t{\t//this is a slave client.\n\t\t\t//find the right number and send.\n\t\t\tint pnum = 0;\n\t\t\tclient_t *sp;\n\t\t\tfor (sp = cl->controller; sp; sp = sp->controlled)\n\t\t\t{\n\t\t\t\tif (sp == cl)\n\t\t\t\t\tbreak;\n\t\t\t\tpnum++;\n\t\t\t}\n\t\t\tsp = cl->controller;\n\n\t\t\tClientReliableWrite_Begin (sp, svcfte_choosesplitclient, 4 + strlen(string));\n\t\t\tClientReliableWrite_Byte (sp, pnum);\n\t\t\tClientReliableWrite_Byte (sp, svc_stufftext);\n\t\t\tClientReliableWrite_String (sp, string);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tClientReliableWrite_Begin (cl, svc_stufftext, strlen(string)+3);\n\t\t\tClientReliableWrite_String (cl, string);\n\t\t}\n\t\tbreak;\n\t}\n}\nvoid SV_StuffcmdToClient_Unreliable(client_t *cl, const char *string)\n{\n\tswitch (cl->protocol)\n\t{\n\tcase SCP_BAD:\t//bot\n\t\tbreak;\n\tcase SCP_QUAKE2:\n\tcase SCP_QUAKE2EX:\n#ifdef Q2SERVER\n\t\tClientReliableWrite_Begin (cl, svcq2_stufftext, strlen(string)+3);\n\t\tClientReliableWrite_String (cl, string);\n#endif\n\t\tbreak;\n\tcase SCP_QUAKE3:\n\t\tbreak;\n\tcase SCP_QUAKEWORLD:\n\tcase SCP_DARKPLACES6:\n\tcase SCP_DARKPLACES7:\n\tcase SCP_NETQUAKE:\n\tcase SCP_BJP3:\n\tcase SCP_FITZ666:\n\t\tif (cl->controller)\n\t\t{\t//this is a slave client.\n\t\t\t//find the right number and send.\n\t\t\tint pnum = 0;\n\t\t\tclient_t *sp;\n\t\t\tfor (sp = cl->controller; sp; sp = sp->controlled)\n\t\t\t{\n\t\t\t\tif (sp == cl)\n\t\t\t\t\tbreak;\n\t\t\t\tpnum++;\n\t\t\t}\n\t\t\tsp = cl->controller;\n\n\t\t\tMSG_WriteByte (&sp->datagram, svcfte_choosesplitclient);\n\t\t\tMSG_WriteByte (&sp->datagram, pnum);\n\t\t\tMSG_WriteByte (&sp->datagram, svc_stufftext);\n\t\t\tMSG_WriteString (&sp->datagram, string);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tMSG_WriteByte(&cl->datagram, svc_stufftext);\n\t\t\tMSG_WriteString(&cl->datagram, string);\n\t\t}\n\t\tbreak;\n\t}\n}\n\n\n/*\n=================\nSV_ClientPrintf\n\nSends text across to be displayed if the level passes\nIs included in mvds.\n=================\n*/\nvoid VARGS SV_ClientPrintf (client_t *cl, int level, const char *fmt, ...)\n{\n\tva_list\t\targptr;\n\tchar\t\tstring[1024];\n\n\tif (level < cl->messagelevel)\n\t\treturn;\n\n\tva_start (argptr,fmt);\n\tvsnprintf (string,sizeof(string)-1, fmt,argptr);\n\tva_end (argptr);\n\n\tif(strlen(string) >= sizeof(string))\n\t\tSys_Error(\"SV_ClientPrintf: Buffer stomped\\n\");\n\n#ifdef MVD_RECORDING\n\tif (sv.mvdrecording)\n\t{\n\t\tsizebuf_t *msg = MVDWrite_Begin (dem_single, cl - svs.clients, strlen(string)+3);\n\t\tMSG_WriteByte (msg, svc_print);\n\t\tMSG_WriteByte (msg, level);\n\t\tMSG_WriteString (msg, string);\n\t}\n#endif\n\n\tSV_PrintToClient(cl, level, string);\n}\n\nvoid VARGS SV_ClientTPrintf (client_t *cl, int level, translation_t stringnum, ...)\n{\n\tva_list\t\targptr;\n\tchar\t\tstring[1024];\n\tconst char *fmt = langtext(stringnum, cl->language);\n\n\tif (level < cl->messagelevel)\n\t\treturn;\n\n\tva_start (argptr,stringnum);\n\tvsnprintf (string,sizeof(string)-1, fmt,argptr);\n\tva_end (argptr);\n\n\tif(strlen(string) >= sizeof(string))\n\t\tSys_Error(\"SV_ClientTPrintf: Buffer stomped\\n\");\n\n#ifdef MVD_RECORDING\n\tif (sv.mvdrecording)\n\t{\n\t\tsizebuf_t *msg = MVDWrite_Begin (dem_single, cl - svs.clients, strlen(string)+3);\n\t\tMSG_WriteByte (msg, svc_print);\n\t\tMSG_WriteByte (msg, level);\n\t\tMSG_WriteString (msg, string);\n\t}\n#endif\n\n\tSV_PrintToClient(cl, level, string);\n}\n\n/*\n=================\nSV_BroadcastPrintf\n\nSends text to all active clients\n=================\n*/\nvoid SV_BroadcastPrint (unsigned int flags, int level, const char *string)\n{\n\tclient_t\t*cl;\n\tint\t\t\ti;\n\n\tif (!(flags & BPRINT_IGNORECONSOLE))\n\t{\n\t\t//pretend to print on the server, but not to the client's console\n\t\tSys_Printf (\"%s\", string);\t// print to the system console\n\t\tLog_String(LOG_CONSOLE, string);\t//dump into log\n\t}\n\n\tif (!(flags & BPRINT_IGNORECLIENTS))\n\t{\n\t\tfor (i=0, cl = svs.clients ; i<svs.allocated_client_slots ; i++, cl++)\n\t\t{\n\t\t\tif (level < cl->messagelevel)\n\t\t\t\tcontinue;\n\t\t\tif (!cl->state)\n\t\t\t\tcontinue;\n\t\t\tif (cl->protocol == SCP_BAD)\n\t\t\t\tcontinue;\n\n\t\t\tif (cl == sv.skipbprintclient)\t//silence bprints about the player in ClientConnect. NQ completely wipes the buffer after clientconnect, which is what traditionally hides it.\n\t\t\t\tcontinue;\n\n\t\t\tif (cl->controller)\n\t\t\t\tcontinue;\n\n\t\t\tSV_PrintToClient(cl, level, string);\n\t\t}\n\t}\n\n#ifdef MVD_RECORDING\n\tif (sv.mvdrecording && !(flags&BPRINT_IGNOREINDEMO))\n\t{\n\t\tsizebuf_t *msg = MVDWrite_Begin (dem_all, 0, strlen(string)+3);\n\t\tMSG_WriteByte (msg, svc_print);\n\t\tMSG_WriteByte (msg, level);\n\t\tMSG_WriteString (msg, string);\n\t}\n#endif\n}\n\nvoid SV_BroadcastPrint_QexLoc (unsigned int flags, int level, const char **arg, int args)\n{\n\tclient_t\t*cl;\n\tint\t\t\ti;\n\n\tchar string[1024];\n\n\tif (!(flags & BPRINT_IGNORECONSOLE))\n\t{\n\t\t//pretend to print on the server, but not to the client's console\n\t\tTL_Reformat(com_language, string, sizeof(string), args, arg);\n\t\tSys_Printf (\"%s\", string);\t// print to the system console\n\t\tLog_String(LOG_CONSOLE, string);\t//dump into log\n\t}\n\n\tif (!(flags & BPRINT_IGNORECLIENTS))\n\t{\n\t\tfor (i=0, cl = svs.clients ; i<svs.allocated_client_slots ; i++, cl++)\n\t\t{\n\t\t\tif (level < cl->messagelevel)\n\t\t\t\tcontinue;\n\t\t\tif (!cl->state)\n\t\t\t\tcontinue;\n\t\t\tif (cl->protocol == SCP_BAD)\n\t\t\t\tcontinue;\n\n\t\t\tif (cl == sv.skipbprintclient)\t//silence bprints about the player in ClientConnect. NQ completely wipes the buffer after clientconnect, which is what traditionally hides it.\n\t\t\t\tcontinue;\n\n\t\t\tif (cl->controller)\n\t\t\t\tcontinue;\n\n\t\t\tif (cl->qex)\n\t\t\t{\t//get the damn client to do it.\n\t\t\t\tint size = 3;\n\t\t\t\tfor (i = 0; i < args; i++)\n\t\t\t\t\tsize += strlen(arg[i])+1;\n\t\t\t\tClientReliableWrite_Begin (cl, svcqex_locprint, size);\n\t\t\t\tClientReliableWrite_Short (cl, args);\n\t\t\t\tfor (i = 0; i < args; i++)\n\t\t\t\t\tClientReliableWrite_String (cl, arg[i]);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tTL_Reformat(cl->language, string, sizeof(string), args, arg);\n\t\t\t\tSV_PrintToClient(cl, level, string);\n\t\t\t}\n\t\t}\n\t}\n\n#ifdef MVD_RECORDING\n\tif (sv.mvdrecording && !(flags&BPRINT_IGNOREINDEMO))\n\t{\n\t\tsizebuf_t *msg;\n\t\tTL_Reformat(com_language, string, sizeof(string), args, arg);\n\t\tmsg = MVDWrite_Begin (dem_all, 0, strlen(string)+3);\n\t\tMSG_WriteByte (msg, svc_print);\n\t\tMSG_WriteByte (msg, level);\n\t\tMSG_WriteString (msg, string);\n\t}\n#endif\n}\n\nvoid VARGS SV_BroadcastPrintf (int level, const char *fmt, ...)\n{\n\tva_list\t\targptr;\n\tchar\t\tstring[1024];\n\tva_start (argptr,fmt);\n\tvsnprintf (string,sizeof(string)-1, fmt,argptr);\n\tva_end (argptr);\n\tif(strlen(string) >= sizeof(string))\n\t\tSys_Error(\"SV_BroadcastPrintf: Buffer stomped\\n\");\n\tSV_BroadcastPrint(0, level, string);\n}\n\n\nvoid VARGS SV_BroadcastTPrintf (int level, translation_t stringnum, ...)\n{\n\tva_list\t\targptr;\n\tchar\t\tstring[1024];\n\tclient_t\t*cl;\n\tint\t\t\ti;\n\tint oldlang=-1;\n\tconst char *fmt = langtext(stringnum, oldlang=com_language);\n\n\tva_start (argptr,stringnum);\n\tvsnprintf (string,sizeof(string)-1, fmt,argptr);\n\tva_end (argptr);\n\n\tif(strlen(string) >= sizeof(string))\n\t\tSys_Error(\"SV_BroadcastPrintf: Buffer stomped\\n\");\n\n\t//pretend to print on the server, but not to the client's console\n\tSys_Printf (\"%s\", string);\t// print to the console\n\tLog_String(LOG_CONSOLE, string);\t//dump into log\n\n\tfor (i=0, cl = svs.clients ; i<svs.allocated_client_slots ; i++, cl++)\n\t{\n\t\tif (level < cl->messagelevel)\n\t\t\tcontinue;\n\t\tif (!cl->state)\n\t\t\tcontinue;\n\t\tif (cl->controller)\n\t\t\tcontinue;\n\n\t\tif (oldlang!=cl->language)\n\t\t{\n\t\t\tfmt = langtext(stringnum, oldlang=cl->language);\n\n\t\t\tva_start (argptr,stringnum);\n\t\t\tvsnprintf (string,sizeof(string)-1, fmt,argptr);\n\t\t\tva_end (argptr);\n\n\t\t\tif(strlen(string) >= sizeof(string))\n\t\t\t\tSys_Error(\"SV_BroadcastPrintf: Buffer stomped\\n\");\n\t\t}\n\n\t\tSV_PrintToClient(cl, level, string);\n\t}\n}\n\n\n/*\n=================\nSV_BroadcastCommand\n\nSends text to all active clients\n=================\n*/\nvoid VARGS SV_BroadcastCommand (const char *fmt, ...)\n{\n\tva_list\t\targptr;\n\tchar\t\tstring[1024];\n\tint i;\n\tclient_t *cl;\n\n\tif (!sv.state)\n\t\treturn;\n\tva_start (argptr,fmt);\n\tvsnprintf (string,sizeof(string), fmt,argptr);\n\tva_end (argptr);\n\n\tfor (i=0, cl = svs.clients ; i<svs.allocated_client_slots ; i++, cl++)\n\t{\n\t\tif (cl->controller)\n\t\t\tcontinue;\n\t\tif (cl->state>=cs_connected)\n\t\t{\n\t\t\tif (ISQWCLIENT(cl) || ISNQCLIENT(cl))\n\t\t\t{\n\t\t\t\tClientReliableWrite_Begin(cl, svc_stufftext, strlen(string)+2);\n\t\t\t\tClientReliableWrite_String (cl, string);\n\t\t\t}\n\t\t\telse if (ISQ2CLIENT(cl))\n\t\t\t{\n\t\t\t\tClientReliableWrite_Begin(cl, svcq2_stufftext, strlen(string)+2);\n\t\t\t\tClientReliableWrite_String (cl, string);\n\t\t\t}\n\t\t}\n\t}\n}\n\n\n/*\n=================\nSV_Multicast\n\nSends the contents of sv.multicast to a subset of the clients,\nthen clears sv.multicast.\n\nMULTICAST_ALL\tsame as broadcast\nMULTICAST_PVS\tsend to clients potentially visible from org\nMULTICAST_PHS\tsend to clients potentially hearable from org\n\nMULTICAST_ONE\tsent to a single client.\nMULTICAST_INIT\tsent to clients when they first connect. for completeness.\n=================\n*/\nvoid SV_MulticastProtExt(vec3_t origin, multicast_t to, int dimension_mask, int with, int without)\n{\n\tclient_t\t*client;\n\tqbyte\t\t*mask;\n\tint\t\t\tcluster, area1, area2;\n\tint\t\t\tj;\n\tqboolean\treliable;\n\tclient_t\t*oneclient = NULL, *split;\n\tint seat;\n\tqboolean\tandspecs = false;\n\textern cvar_t sv_nopvs;\n\n\tif (!sv.multicast.cursize \n#ifdef NQPROT\n\t\t&& !sv.nqmulticast.cursize\n#endif\n#ifdef Q2SERVER\n\t\t&& !sv.q2multicast[0].cursize\n#endif\n\t\t)\n\t\treturn;\n\n\tif (to == MULTICAST_INIT)\n\t{\n\t\t//we only have one signon buffer. make sure you don't put non-identical protocols in the buffer\n\t\tSV_FlushSignon(false);\n\t\tSZ_Write (&sv.signon, sv.multicast.data, sv.multicast.cursize);\n\n\t\t//and send to players that are already on\n\t\tto = MULTICAST_ALL_R;\n\t}\n\n//\tto = MULTICAST_ALL;\n\t//don't let things crash if the world model went away. can happen in broadcasts when reloading video with the map no longer available causing the server to die with the resulting broadcast messages about players dropping or gib effects appearing\n\tif (sv.world.worldmodel->loadstate != MLS_LOADED || !sv.world.worldmodel->nodes)\n\t{\n\t\tswitch(to)\n\t\t{\n\t\tcase MULTICAST_PHS_R:\n\t\tcase MULTICAST_PVS_R:\n\t\t\tto = MULTICAST_ALL_R;\n\t\t\tbreak;\n\t\tcase MULTICAST_PHS:\n\t\tcase MULTICAST_PVS:\n\t\t\tto = MULTICAST_ALL;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tbreak;\n\t\t}\n\t}\n\n\t{\n\t\treliable = false;\n\t\tarea1=-1;\n\t\tarea2=-1;\n\n\t\tswitch (to)\n\t\t{\n\t\tcase MULTICAST_ALL_R:\n\t\t\treliable = true;\t// intentional fallthrough\n\t\tcase MULTICAST_ALL:\n\t\t\tmask = NULL;\n\t\t\tbreak;\n\n\t\tcase MULTICAST_PHS_R:\n\t\t\treliable = true;\t// intentional fallthrough\n\t\tcase MULTICAST_PHS:\n\t\t\tif (!sv.world.worldmodel->phs || sv_nopvs.ival)\t/*broadcast if no pvs*/\n\t\t\t\tmask = NULL;\n\t\t\telse\n\t\t\t{\n\t\t\t\tcluster = sv.world.worldmodel->funcs.ClusterForPoint(sv.world.worldmodel, origin, &area1);\n\t\t\t\tif (cluster >= 0)\n\t\t\t\t\tmask = sv.world.worldmodel->funcs.ClusterPHS(sv.world.worldmodel, cluster, NULL);\n\t\t\t\telse\n\t\t\t\t\tmask = NULL;\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase MULTICAST_PVS_R:\n\t\t\treliable = true;\t// intentional fallthrough\n\t\tcase MULTICAST_PVS:\n\t\t\tif (sv_nopvs.ival)\n\t\t\t\tmask = NULL;\n\t\t\telse\n\t\t\t{\n\t\t\t\tcluster = sv.world.worldmodel->funcs.ClusterForPoint(sv.world.worldmodel, origin, &area1);\n\t\t\t\tif (cluster >= 0)\n\t\t\t\t\tmask = sv.world.worldmodel->funcs.ClusterPVS(sv.world.worldmodel, cluster, NULL, PVM_FAST);\n\t\t\t\telse\n\t\t\t\t\tmask = NULL;\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase MULTICAST_ONE_R_NOSPECS:\n\t\tcase MULTICAST_ONE_R_SPECS:\n\t\t\treliable = true;\n\t\tcase MULTICAST_ONE_NOSPECS:\n\t\tcase MULTICAST_ONE_SPECS:\n\t\t\tif (svprogfuncs)\n\t\t\t{\n\t\t\t\tedict_t *ent = PROG_TO_EDICT(svprogfuncs, pr_global_struct->msg_entity);\n\t\t\t\toneclient = svs.clients + NUM_FOR_EDICT(svprogfuncs, ent) - 1;\n\t\t\t}\n\t\t\telse\n\t\t\t\toneclient = NULL;\n\t\t\tmask = NULL;\n\t\t\tandspecs = (to==MULTICAST_ONE_R_SPECS||to==MULTICAST_ONE_SPECS);\n\t\t\tbreak;\n\n\t\tdefault:\n\t\t\tmask = NULL;\n\t\t\tSV_Error (\"SV_Multicast: bad to:%i\", to);\n\t\t}\n\n\t\t// send the data to all relevent clients\n\t\tfor (j = 0, client = svs.clients; j < sv.allocated_client_slots; j++, client++)\n\t\t{\n\t\t\tif (client->state != cs_spawned)\n\t\t\t\tcontinue;\n\n\t\t\tif (client->controller)\n\t\t\t\tcontinue;\n\n\t\t\tfor (split = client, seat = 0; split; split = split->controlled, seat++)\n\t\t\t{\n\t\t\t\t//if (split->protocol == SCP_QUAKEWORLD)\n\t\t\t\t{\n\t\t\t\t\tif (split->fteprotocolextensions & without)\n\t\t\t\t\t{\n\t\t\t//\t\t\tCon_Printf (\"Version supressed multicast - without pext\\n\");\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tif (!(split->fteprotocolextensions & with) && with)\n\t\t\t\t\t{\n\t\t\t//\t\t\tCon_Printf (\"Version supressed multicast - with pext\\n\");\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (split->penalties & BAN_BLIND)\n\t\t\t\t\tcontinue;\n\n\t\t\t\tif (oneclient)\n\t\t\t\t{\n\t\t\t\t\tif (oneclient != split)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (andspecs && split->spectator && split->spec_track > 0 && oneclient == &svs.clients[split->spec_track - 1])\n\t\t\t\t\t\t\t;\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tvec3_t pos;\n\t\t\t\t\tif (svprogfuncs)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (!((int)split->edict->xv->dimension_see & dimension_mask))\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\tVectorAdd(split->edict->v->origin, split->edict->v->view_ofs, pos);\n\t\t\t\t\t}\n#ifdef Q2SERVER\n\t\t\t\t\telse if (ge)\n\t\t\t\t\t\tVectorCopy(split->q2edict->s.origin, pos);\n#endif\n\t\t\t\t\telse\n\t\t\t\t\t\tcontinue;\t//no idea where the player is...\n\n\t\t\t\t\tif (!mask)\t//no pvs? broadcast.\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tif (to == MULTICAST_PHS_R || to == MULTICAST_PHS)\n\t\t\t\t\t{\t//always in range if within 1024 units (consistent with quakeworld).\n\t\t\t\t\t\tvec3_t delta;\n\t\t\t\t\t\tVectorSubtract(origin, pos, delta);\n\t\t\t\t\t\tif (DotProduct(delta, delta) <= 1024*1024)\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\tcluster = sv.world.worldmodel->funcs.ClusterForPoint (sv.world.worldmodel, pos, &area2);\n\t\t\t\t\tif (cluster>= 0 && (!(mask[cluster>>3] & (1<<(cluster&7)) ) ||\n\t\t\t\t\t\t(sv.world.worldmodel->funcs.AreasConnected && !sv.world.worldmodel->funcs.AreasConnected (sv.world.worldmodel, area1, area2))))\n\t\t\t\t\t{\n\t\t//\t\t\t\tCon_Printf (\"PVS supressed multicast\\n\");\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (!split)\n\t\t\t\tcontinue;\n\n\t\t\tsafeswitch (client->protocol)\n\t\t\t{\n\t\t\tcase SCP_BAD:\n\t\t\t\tcontinue;\t//a bot.\n\t\t\tsafedefault:\n\t\t\t\tSV_Error(\"multicast: Client is using a bad protocol\");\n\n\t\t\tcase SCP_QUAKE3:\n\t\t\t\tCon_Printf(\"Skipping multicast for q3 client\\n\");\n\t\t\t\tbreak;\n\n\t\t\tcase SCP_NETQUAKE:\n\t\t\tcase SCP_BJP3:\n\t\t\tcase SCP_FITZ666:\n\t\t\tcase SCP_DARKPLACES6:\n\t\t\tcase SCP_DARKPLACES7:\t//extra prediction stuff\n#ifdef NQPROT\n\t\t\t\tif (reliable)\n\t\t\t\t{\n\t\t\t\t\tClientReliableCheckBlock(client, sv.nqmulticast.cursize);\n\t\t\t\t\tClientReliableWrite_SZ(client, sv.nqmulticast.data, sv.nqmulticast.cursize);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tSZ_Write (&client->datagram, sv.nqmulticast.data, sv.nqmulticast.cursize);\n#endif\n\t\t\t\tbreak;\n\t\t\tcase SCP_QUAKE2:\n#ifdef Q2SERVER\n\t\t\t\tif (reliable)\n\t\t\t\t{\n\t\t\t\t\tClientReliableCheckBlock(client, sv.q2multicast[0].cursize);\n\t\t\t\t\tClientReliableWrite_SZ(client, sv.q2multicast[0].data, sv.q2multicast[0].cursize);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tSZ_Write (&client->datagram, sv.q2multicast[0].data, sv.q2multicast[0].cursize);\n#endif\n\t\t\t\tbreak;\n\t\t\tcase SCP_QUAKE2EX:\n#ifdef Q2SERVER\n\t\t\t\tif (reliable)\n\t\t\t\t{\n\t\t\t\t\tClientReliableCheckBlock(client, sv.q2multicast[1].cursize);\n\t\t\t\t\tClientReliableWrite_SZ(client, sv.q2multicast[1].data, sv.q2multicast[1].cursize);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tSZ_Write (&client->datagram, sv.q2multicast[1].data, sv.q2multicast[1].cursize);\n#endif\n\t\t\t\tbreak;\n\t\t\tcase SCP_QUAKEWORLD:\n\t\t\t\tif (reliable)\n\t\t\t\t{\n\t\t\t\t\tif (oneclient && seat)\n\t\t\t\t\t{\n\t\t\t\t\t\tClientReliableCheckBlock(client, 2+sv.multicast.cursize);\n\t\t\t\t\t\tClientReliableWrite_Byte(client, svcfte_choosesplitclient);\n\t\t\t\t\t\tClientReliableWrite_Byte(client, seat);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\tClientReliableCheckBlock(client, sv.multicast.cursize);\n\n\t\t\t\t\tClientReliableWrite_SZ(client, sv.multicast.data, sv.multicast.cursize);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tif (oneclient && seat)\n\t\t\t\t\t{\n\t\t\t\t\t\tMSG_WriteByte (&client->datagram, svcfte_choosesplitclient);\n\t\t\t\t\t\tMSG_WriteByte (&client->datagram, seat);\n\t\t\t\t\t}\n\t\t\t\t\tSZ_Write (&client->datagram, sv.multicast.data, sv.multicast.cursize);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n#ifdef MVD_RECORDING\n\tif (sv.mvdrecording && ((demo.recorder.fteprotocolextensions & with) == with) && !(demo.recorder.fteprotocolextensions & without))\n\t{\n\t\tsizebuf_t *msg;\n\n\t\tswitch(to)\n\t\t{\n\t\t//mvds have no idea where the receiver's camera will be.\n\t\t//as such, they cannot have any support for pvs/phs\n\t\tcase MULTICAST_INIT:\n\t\tdefault:\n\t\tcase MULTICAST_ALL_R:\n\t\tcase MULTICAST_PHS_R:\n\t\tcase MULTICAST_PVS_R:\n\t\t\tmsg = MVDWrite_Begin(dem_all, 0, sv.multicast.cursize);\n\t\t\tbreak;\n\t\tcase MULTICAST_ALL:\n\t\tcase MULTICAST_PHS:\n\t\tcase MULTICAST_PVS:\n\t\t\tmsg = &demo.datagram;\n\t\t\tbreak;\n\n\t\tcase MULTICAST_ONE_R_NOSPECS:\n\t\tcase MULTICAST_ONE_NOSPECS:\n\t\t\tmsg = &demo.datagram;\n\t\t\tsv.multicast.cursize = 0;\n\t\t\tbreak;\n\n\t\t//mvds are all reliables really.\n\t\tcase MULTICAST_ONE_R_SPECS:\n\t\tcase MULTICAST_ONE_SPECS:\n\t\t\t{\n\t\t\t\tint pnum;\n\t\t\t\tif (svprogfuncs)\n\t\t\t\t{\n\t\t\t\t\tedict_t *ent = PROG_TO_EDICT(svprogfuncs, pr_global_struct->msg_entity);\n\t\t\t\t\tpnum = NUM_FOR_EDICT(svprogfuncs, ent) - 1;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tpnum = 0;\t//FIXME\n\t\t\t\t\tCon_Printf(\"SV_MulticastProtExt: unsupported unicast\\n\");\n\t\t\t\t}\n\t\t\t\tmsg = MVDWrite_Begin(dem_single, pnum, sv.multicast.cursize);\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t\tSZ_Write(msg, sv.multicast.data, sv.multicast.cursize);\n\t}\n#endif\n\n#ifdef NQPROT\n\tSZ_Clear (&sv.nqmulticast);\n#endif\n#ifdef Q2SERVER\n\tSZ_Clear (&sv.q2multicast[0]);\n\tSZ_Clear (&sv.q2multicast[1]);\n#endif\n\tSZ_Clear (&sv.multicast);\n}\n\nvoid SV_MulticastCB(vec3_t origin, multicast_t to, const char *reliableinfokey, int dimension_mask, void (*callback)(client_t *cl, sizebuf_t *msg, void *ctx), void *ctx)\n{\n\tqboolean reliable = false, doreliable;\n\n\tclient_t\t*client;\n\tqbyte\t\t*mask;\n\tint\t\t\tcluster;\n\tint\t\t\tj;\n\tclient_t\t*oneclient = NULL, *split;\n\tqboolean\tandspecs = false;\n\n\tswitch (to)\n\t{\n\tcase MULTICAST_ALL_R:\n\t\treliable = true;\t// intentional fallthrough\n\tcase MULTICAST_ALL:\n\t\tmask = NULL;\n\t\tbreak;\n\n\tcase MULTICAST_PHS_R:\n\t\treliable = true;\t// intentional fallthrough\n\tcase MULTICAST_PHS:\n\t\tif (!sv.world.worldmodel->phs)\t/*broadcast if no pvs*/\n\t\t\tmask = NULL;\n\t\telse\n\t\t{\n\t\t\tcluster = sv.world.worldmodel->funcs.ClusterForPoint(sv.world.worldmodel, origin, NULL);\n\t\t\tif (cluster >= 0)\n\t\t\t\tmask = sv.world.worldmodel->phs + cluster * sv.world.worldmodel->pvsbytes;\n\t\t\telse\n\t\t\t\tmask = NULL;\n\t\t}\n\t\tbreak;\n\n\tcase MULTICAST_PVS_R:\n\t\treliable = true;\t// intentional fallthrough\n\tcase MULTICAST_PVS:\n\t\tcluster = sv.world.worldmodel->funcs.ClusterForPoint(sv.world.worldmodel, origin, NULL);\n\t\tif (cluster >= 0)\n\t\t\tmask = sv.world.worldmodel->funcs.ClusterPVS(sv.world.worldmodel, cluster, NULL, PVM_FAST);\n\t\telse\n\t\t\tmask = NULL;\n\t\tbreak;\n\n\tcase MULTICAST_ONE_R_NOSPECS:\n\tcase MULTICAST_ONE_R_SPECS:\n\t\treliable = true;\n\n\tcase MULTICAST_ONE_NOSPECS:\n\tcase MULTICAST_ONE_SPECS:\n\t\tif (svprogfuncs)\n\t\t{\n\t\t\tedict_t *ent = PROG_TO_EDICT(svprogfuncs, pr_global_struct->msg_entity);\n\t\t\toneclient = svs.clients + NUM_FOR_EDICT(svprogfuncs, ent) - 1;\n\t\t}\n\t\telse\n\t\t\toneclient = NULL;\n\t\tmask = NULL;\n\t\tandspecs = (to == MULTICAST_ONE_R_SPECS || to == MULTICAST_ONE_SPECS);\n\t\tbreak;\n\n\tdefault:\n\t\tmask = NULL;\n\t\tSV_Error (\"SV_Multicast: bad to:%i\", to);\n\t}\n\n\t// send the data to all relevent clients\n\tfor (j = 0, client = svs.clients; j < sv.allocated_client_slots; j++, client++)\n\t{\n\t\tif (client->state != cs_spawned)\n\t\t\tcontinue;\n\n\t\tif (client->controller)\n\t\t\tcontinue;\n\t\tif (client->protocol == SCP_BAD)\n\t\t\tcontinue;\t//a bot.\n\n\t\tfor (split = client; split; split = split->controlled)\n\t\t{\n\t\t\tif (split->penalties & BAN_BLIND)\n\t\t\t\tcontinue;\n\n\t\t\tif (oneclient)\n\t\t\t{\n\t\t\t\tif (oneclient != split)\n\t\t\t\t{\n\t\t\t\t\tif (andspecs && split->spectator && split->spec_track >= 0 && oneclient == &svs.clients[split->spec_track])\n\t\t\t\t\t\t;\n\t\t\t\t\telse\n\t\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (svprogfuncs)\n\t\t\t{\n\t\t\t\tif (!((int)split->edict->xv->dimension_see & dimension_mask))\n\t\t\t\t\tcontinue;\n\n\t\t\t\tif (!mask)\t//no pvs? broadcast.\n\t\t\t\t\tbreak;\n\n\t\t\t\tif (to == MULTICAST_PHS_R || to == MULTICAST_PHS)\n\t\t\t\t{\t//phs is always 'visible' within 1024qu\n\t\t\t\t\tvec3_t delta;\n\t\t\t\t\tVectorSubtract(origin, split->edict->v->origin, delta);\n\t\t\t\t\tif (DotProduct(delta, delta) <= 1024*1024)\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\t{\n\t\t\t\t\tvec3_t pos;\n\t\t\t\t\tVectorAdd(split->edict->v->origin, split->edict->v->view_ofs, pos);\n\t\t\t\t\tcluster = sv.world.worldmodel->funcs.ClusterForPoint (sv.world.worldmodel, pos, NULL);\n\t\t\t\t\tif (cluster>= 0 && !(mask[cluster>>3] & (1<<(cluster&7)) ) )\n\t\t\t\t\t{\n\t\t//\t\t\t\tCon_Printf (\"PVS supressed multicast\\n\");\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t\tif (!split)\n\t\t\tcontinue;\n\n\t\tdoreliable = reliable;\n\t\tif (reliableinfokey)\n\t\t{\t//allow the user to override reliable state according to a userinfo key (primarily \"rsnd\" right now, but hey).\n\t\t\tconst char *v = InfoBuf_ValueForKey(&client->userinfo, reliableinfokey);\n\t\t\tif (*v)\n\t\t\t\tdoreliable = atoi(v);\n\t\t}\n\n\t\tif (doreliable)\n\t\t{\n\t\t\tchar msgbuf[8192];\n\t\t\tsizebuf_t msg = {0};\n\t\t\tmsg.data = msgbuf;\n\t\t\tmsg.maxsize = sizeof(msgbuf);\n\t\t\tmsg.prim = client->datagram.prim;\n\t\t\tcallback(client, &msg, ctx);\n\t\t\tif (msg.cursize)\n\t\t\t{\n\t\t\t\tClientReliableCheckBlock(client, msg.cursize);\n\t\t\t\tClientReliableWrite_SZ(client, msg.data, msg.cursize);\n\t\t\t}\n\t\t}\n\t\telse\n\t\t\tcallback(client, &client->datagram, ctx);\n\t}\n\n#ifdef MVD_RECORDING\n\tif (sv.mvdrecording)\n\t{\n\t\tsizebuf_t *msg;\n\t\tunsigned int maxsize = 1024;\n\n\t\tswitch(to)\n\t\t{\n\t\t//mvds have no idea where the receiver's camera will be.\n\t\t//as such, they cannot have any support for pvs/phs\n\t\tcase MULTICAST_INIT:\n\t\tdefault:\n\t\tcase MULTICAST_ALL_R:\n\t\tcase MULTICAST_PHS_R:\n\t\tcase MULTICAST_PVS_R:\n\t\t\tmsg = MVDWrite_Begin(dem_all, 0, maxsize);\n\t\t\tbreak;\n\t\tcase MULTICAST_ALL:\n\t\tcase MULTICAST_PHS:\n\t\tcase MULTICAST_PVS:\n\t\t\tmsg = &demo.datagram;\n\t\t\tbreak;\n\n\t\tcase MULTICAST_ONE_R_NOSPECS:\n\t\tcase MULTICAST_ONE_NOSPECS:\n\t\t\treturn;\t//demos count as spectators.\n\n\t\t//mvds are all reliables really.\n\t\tcase MULTICAST_ONE_R_SPECS:\n\t\tcase MULTICAST_ONE_SPECS:\n\t\t\t{\n\t\t\t\tint pnum = -1;\n\t\t\t\tif (svprogfuncs)\n\t\t\t\t{\n\t\t\t\t\tedict_t *ent = PROG_TO_EDICT(svprogfuncs, pr_global_struct->msg_entity);\n\t\t\t\t\tpnum = NUM_FOR_EDICT(svprogfuncs, ent) - 1;\n\t\t\t\t\tif (pnum < 0 || pnum >= sv.allocated_client_slots)\n\t\t\t\t\t{\n\t\t\t\t\t\tCon_Printf(CON_WARNING\"SV_Multicast: entity %i is not a client (\\\"%s\\\")\\n\", pnum+1, PR_GetString(svprogfuncs, ent->v->classname));\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tCon_Printf(CON_WARNING\"SV_Multicast: unsupported unicast\\n\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tmsg = MVDWrite_Begin(dem_single, pnum, maxsize);\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t\tcallback(&demo.recorder, msg, ctx);\n\t}\n#endif\n}\n\n//version does all the work now\nvoid VARGS SV_Multicast (vec3_t origin, multicast_t to)\n{\n\tSV_MulticastProtExt(origin, to, FULLDIMENSIONMASK, 0, 0);\n}\n\n/*\n==================\nSV_StartSound\n\nEach entity can have eight independant sound sources, like voice,\nweapon, feet, etc.\n\nChannel 0 is an auto-allocate channel, the others override anything\nalready running on that entity/channel pair.\n\nAn attenuation of 0 will play full volume everywhere in the level.\nLarger attenuations will drop off.  (max 4 attenuation)\n\n==================\n*/\nstruct startsoundcontext_s\n{\n\tfloat *org;\n\tfloat *vel;\n\tunsigned int ent;\n\tunsigned int chan;\n\tunsigned int sampleidx;\n\tunsigned int volume;\n\tfloat attenuation;\n\tfloat ratemul;\n\tunsigned int chflags;\n\tint timeofs;\n};\nstatic void SV_SoundMulticast(client_t *client, sizebuf_t *msg, void *vctx)\n{\n\tint i;\n\tstruct startsoundcontext_s *ctx = vctx;\n\tunsigned int field_mask = 0;\n\n\tif (ctx->ent >= client->max_net_ents)\n\t\treturn;\n\n\tfield_mask |= (ctx->chflags & CF_NETWORKED) << 8;\n\tif (ctx->volume != DEFAULT_SOUND_PACKET_VOLUME)\n\t\tfield_mask |= NQSND_VOLUME;\n\tif (ctx->attenuation != DEFAULT_SOUND_PACKET_ATTENUATION)\n\t\tfield_mask |= NQSND_ATTENUATION;\n\tif (ctx->ent >= 8192 || ctx->chan >= 8)\n\t\tfield_mask |= NQSND_LARGEENTITY;\n\tif (ctx->sampleidx > 0xff && client->protocol != SCP_BJP3)\n\t\tfield_mask |= NQSND_LARGESOUND;\n\tif (client->fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS)\n\t{\n\t\tif (ctx->ratemul && (ctx->ratemul != 1))\n\t\t\tfield_mask |= FTESND_PITCHADJ;\n\t\tif (ctx->timeofs != 0)\n\t\t\tfield_mask |= FTESND_TIMEOFS;\n\t\tif (ctx->vel)\n\t\t\tfield_mask |= FTESND_VELOCITY;\n\n\t\tif (field_mask > 0xff)\n\t\t\tfield_mask |= FTESND_MOREFLAGS;\n\t}\n\tif (client->protocol == SCP_DARKPLACES7)\n\t{\t//dpp7 clients get slightly higher precision\n\t\tif (ctx->ratemul && (ctx->ratemul != 1))\n\t\t{\n\t\t\tfield_mask |= DPSND_SPEEDUSHORT4000;\n\t\t\tfield_mask &= ~FTESND_PITCHADJ;\n\t\t}\n\t}\n\n\tif (ISNQCLIENT(client) || ctx->chan >= 8 || ctx->ent >= 2048 || (field_mask & ~(NQSND_VOLUME|NQSND_ATTENUATION)))\n\t{\n\t\t//if any of the above conditions evaluates to true, then we can't use standard qw protocols\n\t\tif (ISNQCLIENT(client))\n\t\t\tMSG_WriteByte (msg, svc_sound);\n\t\telse\n\t\t{\n\t\t\tif (!(client->fteprotocolextensions & PEXT_SOUNDDBL) && !(client->fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS))\n\t\t\t\treturn;\n\t\t\tMSG_WriteByte (msg, svcfte_soundextended);\n\t\t}\n\t\tMSG_WriteByte (msg, field_mask&0xff);\n\t\tif (field_mask & FTESND_MOREFLAGS)\n\t\t\tMSG_WriteUInt64 (msg, field_mask>>8);\n\t\tif (field_mask & NQSND_VOLUME)\n\t\t\tMSG_WriteByte (msg, bound(0, ctx->volume, 255));\n\t\tif (field_mask & NQSND_ATTENUATION)\n\t\t\tMSG_WriteByte (msg, bound(0, ctx->attenuation*64, 255));\n\t\tif (field_mask & FTESND_PITCHADJ)\n\t\t\tMSG_WriteByte (msg, bound(1, ctx->ratemul*100, 255));\n\t\tif (field_mask & FTESND_TIMEOFS)\n\t\t\tMSG_WriteShort (msg, bound(-32768, ctx->timeofs*1000, 32767));\n\t\tif (field_mask & FTESND_VELOCITY)\n\t\t{\n\t\t\tMSG_WriteShort (msg, bound(-32767, ctx->vel[0]*8, 32767));\n\t\t\tMSG_WriteShort (msg, bound(-32767, ctx->vel[1]*8, 32767));\n\t\t\tMSG_WriteShort (msg, bound(-32767, ctx->vel[2]*8, 32767));\n\t\t}\n\t\tif (field_mask & DPSND_SPEEDUSHORT4000)\n\t\t\tMSG_WriteShort (msg, bound(1, ctx->ratemul*4000, 65535));\n\t\tif (field_mask & NQSND_LARGEENTITY)\n\t\t{\n\t\t\tMSG_WriteEntity (msg, ctx->ent);\n\t\t\tMSG_WriteByte (msg, ctx->chan);\n\t\t}\n\t\telse\n\t\t\tMSG_WriteShort (msg, (ctx->ent<<3) | ctx->chan);\n\t\tif ((field_mask & NQSND_LARGESOUND) || client->protocol == SCP_BJP3)\n\t\t\tMSG_WriteShort (msg, ctx->sampleidx);\n\t\telse\n\t\t\tMSG_WriteByte (msg, ctx->sampleidx);\n\t\tfor (i=0 ; i<3 ; i++)\n\t\t\tMSG_WriteCoord (msg, ctx->org[i]);\n\t}\n\telse\n\t{\n\t\tunsigned short qwflags = (ctx->ent<<3) | ctx->chan;\n\n\t\tif (ctx->volume != DEFAULT_SOUND_PACKET_VOLUME)\n\t\t\tqwflags |= QWSND_VOLUME;\n\t\tif (ctx->attenuation != DEFAULT_SOUND_PACKET_ATTENUATION)\n\t\t\tqwflags |= QWSND_ATTENUATION;\n\n\t\tMSG_WriteByte (msg, svc_sound);\n\t\tMSG_WriteShort (msg, qwflags);\n\t\tif (qwflags & QWSND_VOLUME)\n\t\t\tMSG_WriteByte (msg, ctx->volume);\n\t\tif (qwflags & QWSND_ATTENUATION)\n\t\t\tMSG_WriteByte (msg, bound(0, ctx->attenuation*64, 255));\n\t\tMSG_WriteByte (msg, ctx->sampleidx&0xff);\n\t\tfor (i=0 ; i<3 ; i++)\n\t\t\tMSG_WriteCoord (msg, ctx->org[i]);\n\t}\n}\nvoid SV_StartSound (int ent, vec3_t origin, float *velocity, int seenmask, int channel, const char *sample, int volume, float attenuation, float ratemul, float timeofs, unsigned int chflags)\n{\n\tqboolean\tuse_phs;\n\tqboolean\treliable = chflags & CF_SV_RELIABLE;\n\tstruct startsoundcontext_s ctx;\n\n\tif (volume < 0 || volume > 255)\n\t{\n\t\tCon_Printf (\"SV_StartSound: volume = %i\", volume);\n\t\treturn;\n\t}\n\n\tif (attenuation < 0 || attenuation >= 4)\n\t\tCon_DPrintf (\"SV_StartSound: attenuation = %f\", attenuation);\n\n\tif (channel < 0 || channel > 255)\n\t{\n\t\tCon_Printf (\"SV_StartSound: channel = %i\", channel);\n\t\treturn;\n\t}\n\n\tctx.attenuation = attenuation;\n\tctx.chan = channel;\n\tctx.ent = ent;\n\tctx.chflags = chflags;\n\tctx.org = origin;\n\tctx.vel = velocity;\n\tctx.ratemul = ratemul;\n\tctx.timeofs = timeofs;\n\tctx.volume = volume;\n\n\tif (velocity && (!velocity[0] && !velocity[1] && !velocity[2]))\n\t\tctx.vel = NULL;\n\n// find precache number for sound\n\tif (!sample)\n\t\tctx.sampleidx = 0;\n\telse if (!*sample)\n\t\treturn;\n\telse\n\t{\n\t\tfor (ctx.sampleidx=1 ; ctx.sampleidx<MAX_PRECACHE_SOUNDS\n\t\t\t&& sv.strings.sound_precache[ctx.sampleidx] ; ctx.sampleidx++)\n\t\t\tif (!strcmp(sample, sv.strings.sound_precache[ctx.sampleidx]))\n\t\t\t\tbreak;\n\n\t\tif ( ctx.sampleidx >= MAX_PRECACHE_SOUNDS || !sv.strings.sound_precache[ctx.sampleidx] )\n\t\t{\n\t\t\tif (ctx.sampleidx < MAX_PRECACHE_SOUNDS)\n\t\t\t{\n\t\t\t\tCon_Printf(\"WARNING: SV_StartSound: sound %s not precached\\n\", sample);\n\t\t\t\t//late precache it. use multicast to ensure that its sent NOW (and to all). normal reliables would mean it would arrive after the svc_sound\n\t\t\t\tsv.strings.sound_precache[ctx.sampleidx] = PR_AddString(svprogfuncs, sample, 0, false);\n\t\t\t\tCon_DPrintf(\"Delayed sound precache: %s\\n\", sample);\n\t\t\t\tMSG_WriteByte(&sv.multicast, svcfte_precache);\n\t\t\t\tMSG_WriteShort(&sv.multicast, ctx.sampleidx+PC_SOUND);\n\t\t\t\tMSG_WriteString(&sv.multicast, sample);\n\t\t#ifdef NQPROT\n\t\t\t\tMSG_WriteByte(&sv.nqmulticast, svcdp_precache);\n\t\t\t\tMSG_WriteShort(&sv.nqmulticast, ctx.sampleidx+PC_SOUND);\n\t\t\t\tMSG_WriteString(&sv.nqmulticast, sample);\n\t\t#endif\n\t\t\t\tSV_MulticastProtExt(NULL, MULTICAST_ALL_R, FULLDIMENSIONMASK, PEXT_CSQC, 0);\n\n\t\t\t\treliable = true;\t//try to make sure it doesn't arrive before the precache!\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tCon_DPrintf (\"SV_StartSound: %s not precached\\n\", sample);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n\n\tif (reliable || !sv_phs.value || !attenuation)\t// no PHS flag\n\t\tuse_phs = false;\n\telse\n\t\tuse_phs = attenuation!=0;\n\n\tif (chflags & CF_SV_UNICAST)\n\t{\n\t\tSV_MulticastCB(origin, (reliable||sv_reliable_sound.ival) ? MULTICAST_ONE_R_SPECS : MULTICAST_ONE_SPECS, reliable?NULL:\"rsnd\", seenmask, SV_SoundMulticast, &ctx);\n\t}\n\telse\n\t{\n\t\tif (use_phs)\n\t\t\tSV_MulticastCB(origin, (reliable||sv_reliable_sound.ival) ? MULTICAST_PHS_R : MULTICAST_PHS, reliable?NULL:\"rsnd\", seenmask, SV_SoundMulticast, &ctx);\n\t\telse\n\t\t\tSV_MulticastCB(origin, (reliable||sv_reliable_sound.ival) ? MULTICAST_ALL_R : MULTICAST_ALL, reliable?NULL:\"rsnd\", seenmask, SV_SoundMulticast, &ctx);\n\t}\n}\n\nvoid QDECL SVQ1_StartSound (float *origin, wedict_t *wentity, int channel, const char *sample, int volume, float attenuation, float pitchadj, float timeofs, unsigned int chflags)\n{\n\tedict_t *entity = (edict_t*)wentity;\n\tint i, solid;\n\tvec3_t originbuf, velocity={0,0,0};\n\tif (!origin)\n\t{\n\t\torigin = originbuf;\n\t\tsolid = entity->v->solid;\n\t\tif (solid == SOLID_BSP || solid == SOLID_BSPTRIGGER)\n\t\t{\n\t\t\tfor (i=0 ; i<3 ; i++)\n\t\t\t\torigin[i] = entity->v->origin[i]+0.5*(entity->v->mins[i]+entity->v->maxs[i]);\n\n\t\t\t//add the reliable flag for bsp objects.\n\t\t\t//these sounds are often looped, and if the start is in the phs and the end isn't/gets dropped, then you end up with an annoying infinitely looping sample.\n\t\t\t//making them all reliable avoids packetloss and phs issues.\n\t\t\t//this applies only to pushers. you won't get extra latency on player actions because of this.\n\t\t\t//be warned that it does mean you might be able to hear people triggering stuff on the other side of the map however.\n\t\t\tchflags |= CF_SV_RELIABLE;\n\t\t}\n\t\telse if (progstype == PROG_QW)\n\t\t{\t//quakeworld puts the sound ONLY at the entity's actual origin. this is annoying and stupid. I'm not really sure what to do here. it seems wrong.\n\t\t\tVectorCopy (entity->v->origin, origin);\n\t\t}\n\t\telse\n\t\t{\t//nq (and presumably h2) always put the sound in the middle of the ent's bbox. this is needed to avoid triggers breaking (like trigger_secret).\n\t\t\tfor (i=0 ; i<3 ; i++)\n\t\t\t\torigin[i] = entity->v->origin[i]+0.5*(entity->v->mins[i]+entity->v->maxs[i]);\n\t\t}\n\n\t\tif (chflags & CF_SV_SENDVELOCITY)\n\t\t\tVectorCopy(entity->v->velocity, velocity);\n\t}\n\n\tSV_StartSound(NUM_FOR_EDICT(svprogfuncs, entity), origin, velocity, entity->xv->dimension_seen, channel, sample, volume, attenuation, pitchadj, timeofs, chflags);\n}\n\n\n\nvoid SV_SendLightstyle(client_t *cl, sizebuf_t *forcemsg, int style, qboolean initial)\n{\n\tsizebuf_t *msg;\n\tconst char *stylestring = (style < sv.maxlightstyles)?sv.lightstyles[style].str:NULL;\n\tfloat *stylecolor = (style < sv.maxlightstyles)?sv.lightstyles[style].colours:vec3_origin;\n\tint flags = 0;\n\tint sz;\n\n\t//don't crash old clients unless there's a good reason to do so.\n\t//new clients are expected to reinitialise their styles to empty on map changes.\n\tif (style >= MAX_STANDARDLIGHTSTYLES && initial && !stylestring)\n\t\treturn;\n\n\tif (style > 255)\n\t\tflags |= 0x40;\n\tif (stylestring && (stylecolor[0]!=1||stylecolor[1]!=1||stylecolor[2]!=1))\n\t{\n\t\tif (stylecolor[0]!=0)\n\t\t{\n\t\t\tflags |= 1;\n\t\t\tif (stylecolor[0]!=1)\n\t\t\t\tflags |= 0x80|7;\n\t\t}\n\t\tif (stylecolor[1]!=0)\n\t\t{\n\t\t\tflags |= 2;\n\t\t\tif (stylecolor[1]!=1)\n\t\t\t\tflags |= 0x80|7;\n\t\t}\n\t\tif (stylecolor[2]!=0)\n\t\t{\n\t\t\tflags |= 4;\n\t\t\tif (stylecolor[2]!=1)\n\t\t\t\tflags |= 0x80|7;\n\t\t}\n\t}\n\telse\n\t\tflags |= 7;\n\t//flags |= 0x08;\n\t//flags |= 0x10;\n\t//flags |= 0x20;\n\n\tif (!(cl->fteprotocolextensions & PEXT_LIGHTSTYLECOL))\n\t{\t//if they don't support it then just drop the extra colours, so long as it still makes sense.\n\t\tif ((flags & ~0x87u) && (ISNQCLIENT(cl) && !ISDPCLIENT(cl) && cl->fteprotocolextensions2))\n\t\t{\n\t\t\tchar *text = va(\"//ls %i \\\"%s\\\" %g %g %g\\n\", style, sv.lightstyles[style].str, sv.lightstyles[style].colours[0], sv.lightstyles[style].colours[1], sv.lightstyles[style].colours[2]);\n\t\t\tif (forcemsg)\n\t\t\t\tmsg = forcemsg;\n\t\t\telse\n\t\t\t\tmsg = ClientReliable_StartWrite(cl, 2+strlen(text));\n\t\t\tMSG_WriteByte (msg, svc_stufftext);\n\t\t\tMSG_WriteString (msg, text);\n\t\t\tif (!forcemsg)\n\t\t\t\tClientReliable_FinishWrite(cl);\n\t\t\treturn;\t//erk, can't handle this!\n\t\t}\n\t\tif (style >= ((cl->fteprotocolextensions2||ISDPCLIENT(cl))?255:64))\n\t\t\treturn; //client probably doesn't support this lightstyle.\n\t\tflags = 7;\t//force vanilla protocol as fallback.\n\t}\n\n\tif (forcemsg)\n\t\tmsg = forcemsg;\n\telse\n\t{\n\t\tsz = 2;\n\t\tif (flags != 7)\n\t\t\tsz+=1;\n\t\tif (flags & 0x40)\n\t\t\tsz+=1;\n\t\tif (flags & 0x80u)\n\t\t\tsz+=3*2;\t//rough overestimate\n\t\tsz += (stylestring?strlen(stylestring):0) + 1;\n\t\tmsg = ClientReliable_StartWrite(cl, sz);\n\t}\n\n\tMSG_WriteByte(msg, (flags != 7)?svcfte_lightstylecol:svc_lightstyle);\n\tMSG_WriteByte (msg, style&0xffu);\n\tif (flags != 7)\n\t\tMSG_WriteByte(msg, flags);\n\tif (flags & 0x40)\t//16bit style indexes\n\t\tMSG_WriteByte (msg, style>>8);\n\tif (flags & 0x80u)\n\t{\t//rich style tints\n\t\tif (flags & 1)\n\t\t\tMSG_WriteShort (msg, bound(-0x7fff, stylecolor[0]*1024, 0x7fff));\n\t\tif (flags & 2)\n\t\t\tMSG_WriteShort (msg, bound(-0x7fff, stylecolor[1]*1024, 0x7fff));\n\t\tif (flags & 4)\n\t\t\tMSG_WriteShort (msg, bound(-0x7fff, stylecolor[2]*1024, 0x7fff));\n\t}\n\tMSG_WriteString (msg, stylestring);\n\n\tif (!forcemsg)\n\t\tClientReliable_FinishWrite(cl);\n}\n\n/*\n===============================================================================\n\nFRAME UPDATES\n\n===============================================================================\n*/\n\nint\t\tsv_nailmodel, sv_supernailmodel, sv_playermodel;\n\nvoid SV_FindModelNumbers (void)\n{\n\tint\t\ti;\n\n\tsv_nailmodel = -1;\n\tsv_supernailmodel = -1;\n\tsv_playermodel = -1;\n\n\tfor (i=0 ; i<MAX_PRECACHE_MODELS ; i++)\n\t{\n\t\tif (!sv.strings.model_precache[i])\n\t\t\tbreak;\n\t\tif (!strcmp(sv.strings.model_precache[i],\"progs/spike.mdl\") && sv.multicast.prim.coordtype == COORDTYPE_FIXED_13_3)\n\t\t\tsv_nailmodel = i;\n\t\tif (!strcmp(sv.strings.model_precache[i],\"progs/s_spike.mdl\") && sv.multicast.prim.coordtype == COORDTYPE_FIXED_13_3)\n\t\t\tsv_supernailmodel = i;\n\t\tif (!strcmp(sv.strings.model_precache[i],\"progs/player.mdl\"))\n\t\t\tsv_playermodel = i;\n\t}\n}\n\nvoid SV_SendFixAngle(client_t *client, sizebuf_t *msg, int fixtype, qboolean roll)\n{\n\tunsigned i;\n\tclient_t *controller = client->controller?client->controller:client;\n\tedict_t *ent = client->edict;\n\tpvec_t *ang;\n\tif (!ent || ISQ2CLIENT(client))\n\t\treturn;\n\tang = ent->v->fixangle?ent->v->angles:ent->v->v_angle;\t//angles is just WEIRD for mdls, but then quake sucks.\n\tif (ent->v->movetype == MOVETYPE_6DOF)\n\t\troll = true;\n\n\tif (fixtype == FIXANGLE_AUTO)\n\t{\n\t\tif (client->lockanglesseq<controller->netchan.incoming_acknowledged && controller->delta_sequence != -1 && !client->viewent && !sv_nqplayerphysics.ival)\n\t\t\tfixtype = FIXANGLE_DELTA;\n\t\telse\n\t\t\tfixtype = FIXANGLE_FIXED;\n\t}\n\telse\n\t\troll = true;\n\n\tif (controller->fteprotocolextensions2 & PEXT2_VRINPUTS)\n\t{\n\t\tif (fixtype == FIXANGLE_DELTA)\n\t\t{\n\t\t\t//fiddle with the base angle, server will see future moves with that change already applied.\n\t\t\tvec3_t diff;\n\t\t\tfor (i = 0; i < 3; i++)\n\t\t\t\tdiff[i] = ANGLE2SHORT(ang[i]) - client->lastcmd.angles[i];\n\t\t\tif (!roll)\n\t\t\t\tdiff[2] = 0;\n\t\t\tVectorAdd(client->baseangles, diff, client->baseangles);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tclient->baseangles[0] = ANGLE2SHORT(ang[0]);\n\t\t\tclient->baseangles[1] = ANGLE2SHORT(ang[1]);\n\t\t\tclient->baseangles[2] = ANGLE2SHORT(ang[2]);\n\t\t\tclient->baseanglelock++;\n\t\t}\n\t}\n\telse\n\t{\n\t\tif (fixtype == FIXANGLE_DELTA && !(controller->fteprotocolextensions2 & PEXT2_SETANGLEDELTA))\n\t\t\tfixtype = FIXANGLE_FIXED;\t//sorry, can't do it.\n\n\t\tif (client->lockanglesseq>=controller->netchan.incoming_acknowledged && controller->netchan.message.cursize < controller->netchan.message.maxsize/2)\n\t\t\tmsg = NULL;\t//try to keep them vaugely reliable, where feasable.\n\t\tif (!msg)\n\t\t\tmsg = ClientReliable_StartWrite(client, 10);\n\t\telse if (client->seat)\n\t\t{\n\t\t\tMSG_WriteByte(msg, svcfte_choosesplitclient);\n\t\t\tMSG_WriteByte(msg, client->seat);\n\t\t}\n\t\tif (fixtype == FIXANGLE_DELTA && (controller->fteprotocolextensions2 & PEXT2_SETANGLEDELTA))\n\t\t{\n\t\t\tMSG_WriteByte (msg, svcfte_setangledelta);\n\t\t\tfor (i=0 ; i < 3 ; i++)\n\t\t\t{\n\t\t\t\tint newa = ang[i] - SHORT2ANGLE(client->lastcmd.angles[i]);\n\t\t\t\tMSG_WriteAngle16 (msg, newa);\n\t\t\t\tclient->lastcmd.angles[i] = ANGLE2SHORT(ang[i]);\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tMSG_WriteByte (msg, svc_setangle);\n\t\t\tif (client->ezprotocolextensions1 & EZPEXT1_SETANGLEREASON)\n\t\t\t\tMSG_WriteByte (msg, (fixtype == FIXANGLE_DELTA)?2:0);\t//shitty backwards incompatible protocol extension that breaks from writebytes.\n\t\t\tfor (i=0 ; i < 3 ; i++)\n\t\t\t\tMSG_WriteAngle (msg, (i==2&&!roll)?0:ang[i]);\n\t\t}\n\t}\n\tClientReliable_FinishWrite(client);\n\tclient->lockanglesseq = controller->netchan.outgoing_sequence+1;\t//so that spammed fixangles use absolute values, locking the camera in place.\n}\n\nvoid SV_WriteEntityDataToMessage (client_t *client, sizebuf_t *msg, int pnum)\n{\n\tedict_t\t*other;\n\tedict_t\t*ent;\n\tint i;\n\n\tent = client->edict;\n\n\tif (!ent)\n\t\treturn;\n\n\t// send a damage message if the player got hit this frame\n\tif (ent->v->dmg_take || ent->v->dmg_save)\n\t{\n\t\tother = PROG_TO_EDICT(svprogfuncs, ent->v->dmg_inflictor);\n\t\tif (pnum)\n\t\t{\n\t\t\tMSG_WriteByte(msg, svcfte_choosesplitclient);\n\t\t\tMSG_WriteByte(msg, pnum);\n\t\t}\n\t\tMSG_WriteByte (msg, svc_damage);\n\t\tMSG_WriteByte (msg, bound(0, ent->v->dmg_save, 255));\n\t\tMSG_WriteByte (msg, bound(0, ent->v->dmg_take, 255));\n\t\tfor (i=0 ; i<3 ; i++)\n\t\t\tMSG_WriteCoord (msg, other->v->origin[i] + 0.5*(other->v->mins[i] + other->v->maxs[i]));\n\n\t\t//FIXME: flood to spectators.\n\n\t\tent->v->dmg_take = 0;\n\t\tent->v->dmg_save = 0;\n\t}\n\n\t// a fixangle might get lost in a dropped packet.  Oh well.\n\tif (client->spectator && ISNQCLIENT(client) && client->spec_track > 0)\n\t{\n\t\tedict_t *ed = EDICT_NUM_UB(svprogfuncs, client->spec_track);\n\t\tMSG_WriteByte(msg, svc_setangle);\n\t\tMSG_WriteAngle(msg, ed->v->v_angle[0]);\n\t\tMSG_WriteAngle(msg, ed->v->v_angle[1]);\n\t\tMSG_WriteAngle(msg, ed->v->v_angle[2]);\n\t\tVectorCopy(ed->v->origin, client->edict->v->origin);\n\t}\n\telse if (ent->v->fixangle)\n\t{\n\t\tSV_SendFixAngle(client, msg, ent->v->fixangle, true);\n\t\tent->v->fixangle = FIXANGLE_NO;\n\t}\n}\n\n/*sends the a centerprint string directly to the client*/\nvoid SV_WriteCenterPrint(client_t *cl, char *s)\n{\n\tif (cl->controller)\n\t{\t//this is a slave client.\n\t\t//find the right number and send.\n\t\tint pnum = 0;\n\t\tclient_t *sp;\n\t\tfor (sp = cl->controller; sp; sp = sp->controlled)\n\t\t{\n\t\t\tif (sp == cl)\n\t\t\t\tbreak;\n\t\t\tpnum++;\n\t\t}\n\t\tcl = cl->controller;\n\n\t\tClientReliableWrite_Begin (cl, svcfte_choosesplitclient, 4 + strlen(s));\n\t\tClientReliableWrite_Byte (cl, pnum);\n\t\tClientReliableWrite_Byte (cl, svc_centerprint);\n\t}\n\telse\n\t\tClientReliableWrite_Begin (cl, svc_centerprint, 2 + strlen(s));\n\tClientReliableWrite_String (cl, s);\n\n#ifdef MVD_RECORDING\n\tif (sv.mvdrecording)\n\t{\n\t\tsizebuf_t *msg = MVDWrite_Begin (dem_single, cl - svs.clients, 2 + strlen(s));\n\t\tMSG_WriteByte (msg, svc_centerprint);\n\t\tMSG_WriteString (msg, s);\n\t}\n#endif\n}\n\n/*\n==================\nSV_WriteClientdataToMessage\n\n==================\n*/\nvoid SV_WriteClientdataToMessage (client_t *client, sizebuf_t *msg)\n{\n#ifdef NQPROT\n\tint\t\ti;\n\tint bits, items;\n\tedict_t\t*ent;\n\tqboolean nqjunk = true;\n\tint weaponmodelindex = 0;\n#endif\n\tclient_t *split;\n\tint pnum=0;\n\n\t// send the chokecount for r_netgraph\n\tif (ISQWCLIENT(client))\n\tif (client->chokecount)\n\t{\n\t\tMSG_WriteByte (msg, svc_chokecount);\n\t\tMSG_WriteByte (msg, bound(0, client->chokecount, 255));\n\t\tclient->chokecount = 0;\n\t}\n\n\tfor (split = client; split; split=split->controlled, pnum++)\n\t{\n\t\tSV_WriteEntityDataToMessage(split, msg, pnum);\n\n\t\tif (split->prompt.active)\n\t\t\tSV_Prompt_Resend(split);\n\n\t\tif (split->centerprintstring && ! client->num_backbuf)\n\t\t{\n\t\t\tSV_WriteCenterPrint(split, split->centerprintstring);\n\t\t\tZ_Free(split->centerprintstring);\n\t\t\tsplit->centerprintstring = NULL;\n\t\t}\n\t}\n/*\n\tMSG_WriteByte (msg, svc_time);\n\tMSG_WriteFloat(msg, sv.physicstime);\n\tclient->nextservertimeupdate = sv.physicstime;\n*/\n\n#ifdef HLSERVER\n\tif (svs.gametype == GT_HALFLIFE)\n\t\treturn;\n#endif\n\n#ifdef NQPROT\n\tent = client->edict;\n\tif (client->spectator && client->spec_track)\n\t\tent = EDICT_NUM_UB(svprogfuncs, client->spec_track);\n\tif (progstype != PROG_QW)\n\t{\n\t\tif (ISQWCLIENT(client) && !(client->fteprotocolextensions2 & PEXT2_PREDINFO))\n\t\t{\n\t\t\t//quakeworld clients drop the punch angle themselves.\n\t\t\twhile (ent->xv->punchangle[0] < -3)\n\t\t\t{\n\t\t\t\tent->xv->punchangle[0] += 4;\n\t\t\t\tMSG_WriteByte (msg, svc_bigkick);\n\t\t\t}\n\t\t\twhile (ent->xv->punchangle[0] < -1)\n\t\t\t{\n\t\t\t\tent->xv->punchangle[0] += 2;\n\t\t\t\tMSG_WriteByte (msg, svc_smallkick);\n\t\t\t}\n\t\t\tent->xv->punchangle[1] = 0;\n\t\t\tent->xv->punchangle[2] = 0;\n\t\t}\n\t}\n\n\tif (ISQWCLIENT(client))\n\t\treturn;\n\n\tif (!(client->fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS))\n\t{\n\t\tMSG_WriteByte (msg, svc_time);\n\t\tMSG_WriteFloat(msg, sv.world.physicstime);\n\n\t\tif (client->fteprotocolextensions2 & PEXT2_PREDINFO)\n\t\t\tMSG_WriteShort(msg, client->last_sequence);\n\t\telse if (client->qex && client->last_sequence)\n\t\t{\n\t\t\tMSG_WriteByte(msg, svcqex_seq);\n\t\t\tMSG_WriteULEB128(msg, client->last_sequence);\n\t\t}\n\n//\t\tCon_Printf(\"%f\\n\", sv.world.physicstime);\n\t}\n\n\t//predinfo extension reworks stats, making svc_clientdata redundant.\n\tif (client->fteprotocolextensions2 & PEXT2_PREDINFO)\n\t\treturn;\n\n\tif (client->protocol == SCP_DARKPLACES6 || client->protocol == SCP_DARKPLACES7)\n\t\tnqjunk = false;\n\telse\n\t\tnqjunk = true;\n\n\tbits = 0;\n\n\tif (ent->v->view_ofs[2] != DEFAULT_VIEWHEIGHT)\n\t\tbits |= SU_VIEWHEIGHT;\n\n\tif (ent->xv->idealpitch)\n\t\tbits |= SU_IDEALPITCH;\n\n// stuff the sigil bits into the high bits of items for sbar, or else\n// mix in items2\n\tif (sv.haveitems2)\n\t\titems = (int)ent->v->items | ((int)ent->xv->items2 << 23);\n\telse\n\t\titems = (int)ent->v->items | ((int)pr_global_struct->serverflags << 28);\n\n\n\tif (nqjunk)\n\t\tbits |= SU_ITEMS;\n\n\tif ( (int)ent->v->flags & FL_ONGROUND)\n\t\tbits |= SU_ONGROUND;\n\n\tif ( ent->v->waterlevel >= 2)\n\t\tbits |= SU_INWATER;\n\n\tfor (i=0 ; i<3 ; i++)\n\t{\n\t\tif (ent->xv->punchangle[i])\n\t\t\tbits |= (SU_PUNCH1<<i);\n//\t\tif ((client->protocol == SCP_DARKPLACES6 || client->protocol == SCP_DARKPLACES7) && ent->xv->punchvector[i])\n//\t\t\tbits |= (DPSU_PUNCHVEC1<<i);\n\t\tif (ent->v->velocity[i])\n\t\t\tbits |= (SU_VELOCITY1<<i);\n\t}\n\n\tif (nqjunk)\n\t{\n\t\tnqjunk = true;\n\n\t\tif (ent->v->weaponframe)\n\t\t\tbits |= SU_WEAPONFRAME;\n\n\t\tif (ent->v->armorvalue)\n\t\t\tbits |= SU_ARMOR;\n\n\t\tweaponmodelindex = SV_ModelIndex(PR_GetString(svprogfuncs, ent->v->weaponmodel));\n\n\t\tif (weaponmodelindex)\n\t\t\tbits |= SU_WEAPONMODEL;\n\n\t\tif (client->protocol == SCP_FITZ666)\n\t\t{\n\t\t\tif (weaponmodelindex & 0xff00)\n\t\t\t\tbits |= FITZSU_WEAPONMODEL2;\n\t\t\tif ((int)ent->v->armorvalue & 0xff00)\n\t\t\t\tbits |= FITZSU_ARMOR2;\n\t\t\tif ((int)ent->v->currentammo & 0xff00)\n\t\t\t\tbits |= FITZSU_AMMO2;\n\t\t\tif ((int)ent->v->ammo_shells & 0xff00)\n\t\t\t\tbits |= FITZSU_SHELLS2;\n\t\t\tif ((int)ent->v->ammo_nails & 0xff00)\n\t\t\t\tbits |= FITZSU_NAILS2;\n\t\t\tif ((int)ent->v->ammo_rockets & 0xff00)\n\t\t\t\tbits |= FITZSU_ROCKETS2;\n\t\t\tif ((int)ent->v->ammo_cells & 0xff00)\n\t\t\t\tbits |= FITZSU_CELLS2;\n\t\t\tif ((int)ent->v->weaponframe & 0xff00)\n\t\t\t\tbits |= FITZSU_WEAPONFRAME2;\n\t\t\tif (ent->xv->alpha && ent->xv->alpha < 1)\n\t\t\t\tbits |= FITZSU_WEAPONALPHA;\n\n\t\t\tif (client->qex)\n\t\t\t{\n\t\t\t\tif (ent->v->flags)\n\t\t\t\t\tbits |= QEX_SU_ENTFLAGS;\n\t\t\t\tif (bits & (SU_VELOCITY1|SU_VELOCITY2|SU_VELOCITY3))\n\t\t\t\t\tbits |= QEX_SU_FLOATCOORDS;\n\t\t\t}\n\t\t}\n\t}\n\n\tif (bits >= (1u<<16))\n\t\tbits |= SU_EXTEND1;\n\tif (bits >= (1u<<24))\n\t\tbits |= SU_EXTEND2;\n\tif (bits >= ((quint64_t)1u<<32))\n\t\tbits |= SU_EXTEND3;\n\n// send the data\n\n\tMSG_WriteByte (msg, svcnq_clientdata);\n\tMSG_WriteShort (msg, bits & 0xffff);\n\n\tif (bits & SU_EXTEND1)\n\t\tMSG_WriteByte(msg, (bits>>16)&0xff);\n\tif (bits & SU_EXTEND2)\n\t\tMSG_WriteByte(msg, bits>>24);\n\n\tif (bits & SU_VIEWHEIGHT)\n\t\tMSG_WriteChar (msg, ent->v->view_ofs[2]);\n\n\tif (bits & SU_IDEALPITCH)\n\t\tMSG_WriteChar (msg, ent->xv->idealpitch);\n\n\tfor (i=0 ; i<3 ; i++)\n\t{\n\t\tif (bits & (SU_PUNCH1<<i))\n\t\t{\n\t\t\tif (client->protocol == SCP_DARKPLACES6 || client->protocol == SCP_DARKPLACES7)\n\t\t\t\tMSG_WriteAngle16 (msg, ent->xv->punchangle[i]);\n\t\t\telse\n\t\t\t\tMSG_WriteChar (msg, ent->xv->punchangle[i]);\n\t\t}\n//\t\tif ((client->protocol == SCP_DARKPLACES6 || client->protocol == SCP_DARKPLACES7) && (bits & (DPSU_PUNCHVEC1<<i)))\n//\t\t\tMsg_WriteCoord(msg, ent->xv->punchvector);\n\t\tif (bits & (SU_VELOCITY1<<i))\n\t\t{\n\t\t\tif (client->qex && (bits & QEX_SU_ENTFLAGS))\n\t\t\t\tMSG_WriteFloat(msg, ent->v->velocity[i]);\n\t\t\telse if (client->protocol == SCP_DARKPLACES6 || client->protocol == SCP_DARKPLACES7)\n\t\t\t\tMSG_WriteFloat(msg, ent->v->velocity[i]);\n\t\t\telse\n\t\t\t\tMSG_WriteChar (msg, bound(-128, ent->v->velocity[i]/16, 127));\n\t\t}\n\t}\n\n\tif (bits & SU_ITEMS)\n\t\tMSG_WriteLong (msg, items);\n\n\tif (bits & SU_WEAPONFRAME)\n\t\tMSG_WriteByte (msg, ent->v->weaponframe);\n\tif (bits & SU_ARMOR)\n\t{\n\t\tif (ent->v->armorvalue < 0)\n\t\t\tMSG_WriteByte (msg, 0);\n\t\telse if (ent->v->armorvalue>255 && !(bits & FITZSU_ARMOR2))\n\t\t\tMSG_WriteByte (msg, 255);\n\t\telse\n\t\t\tMSG_WriteByte (msg, (int)ent->v->armorvalue&0xff);\n\t}\n\tif (bits & SU_WEAPONMODEL)\n\t{\n\t\tif (client->protocol == SCP_BJP3)\n\t\t\tMSG_WriteShort (msg, weaponmodelindex&0xffff);\n\t\telse\n\t\t\tMSG_WriteByte (msg, weaponmodelindex&0xff);\n\t}\n\n\tif (nqjunk)\n\t{\n\t\tif (client->spectator && !client->spec_track)\n\t\t\tMSG_WriteShort (msg, 1000);\n\t\telse\n\t\t\tMSG_WriteShort (msg, ent->v->health);\n\t\tif (client->protocol == SCP_FITZ666)\n\t\t{\n\t\t\tMSG_WriteByte (msg, (int)ent->v->currentammo & 0xff);\n\t\t\tMSG_WriteByte (msg, (int)ent->v->ammo_shells & 0xff);\n\t\t\tMSG_WriteByte (msg, (int)ent->v->ammo_nails & 0xff);\n\t\t\tMSG_WriteByte (msg, (int)ent->v->ammo_rockets & 0xff);\n\t\t\tMSG_WriteByte (msg, (int)ent->v->ammo_cells & 0xff);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tMSG_WriteByte (msg, min(ent->v->currentammo, 255));\n\t\t\tMSG_WriteByte (msg, min(ent->v->ammo_shells, 255));\n\t\t\tMSG_WriteByte (msg, min(ent->v->ammo_nails, 255));\n\t\t\tMSG_WriteByte (msg, min(ent->v->ammo_rockets, 255));\n\t\t\tMSG_WriteByte (msg, min(ent->v->ammo_cells, 255));\n\t\t}\n\n\t\tif (standard_quake)\n\t\t{\n\t\t\tMSG_WriteByte (msg, (unsigned int)ent->v->weapon & 0xff);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tfor(i=0;i<32;i++)\n\t\t\t{\n\t\t\t\tif ( ((int)ent->v->weapon) & (1<<i) )\n\t\t\t\t{\n\t\t\t\t\tMSG_WriteByte (msg, i);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tif (client->protocol == SCP_FITZ666)\n\t{\n\t\tif (bits & FITZSU_WEAPONMODEL2)\tMSG_WriteByte (msg, weaponmodelindex >> 8);\n\t\tif (bits & FITZSU_ARMOR2)\t\tMSG_WriteByte (msg, (int)ent->v->armorvalue >> 8);\n\t\tif (bits & FITZSU_AMMO2)\t\tMSG_WriteByte (msg, (int)ent->v->currentammo >> 8);\n\t\tif (bits & FITZSU_SHELLS2)\t\tMSG_WriteByte (msg, (int)ent->v->ammo_shells >> 8);\n\t\tif (bits & FITZSU_NAILS2)\t\tMSG_WriteByte (msg, (int)ent->v->ammo_nails >> 8);\n\t\tif (bits & FITZSU_ROCKETS2)\t\tMSG_WriteByte (msg, (int)ent->v->ammo_rockets >> 8);\n\t\tif (bits & FITZSU_CELLS2)\t\tMSG_WriteByte (msg, (int)ent->v->ammo_cells >> 8);\n\t\tif (bits & FITZSU_WEAPONFRAME2)\tMSG_WriteByte (msg, (int)ent->v->weaponframe >> 8);\n\t\tif (bits & FITZSU_WEAPONALPHA)\tMSG_WriteByte (msg, ent->xv->alpha*255);\n\n\t\tif (client->qex)\n\t\t{\n\t\t\tif (bits & QEX_SU_ENTFLAGS)\tMSG_WriteULEB128 (msg, ent->v->flags);\n\t\t}\n\t}\n#endif\n}\n\ntypedef struct {\n\tint type;\t//negative means a global.\n\tchar name[64];\n\tunion {\n\t\tevalc_t c;\n\t\teval_t *g;\t//just store a pointer to it.\n\t} eval;\n\tint statnum;\n} qcstat_t;\nstatic qcstat_t qcstats[MAX_CL_STATS];\nstatic unsigned int numqcstats;\nstatic unsigned int highestqcstat;\nstatic void SV_QCStatEval(int type, const char *name, evalc_t *field, eval_t *global, int statnum)\n{\n\tint i;\n\tif (numqcstats == sizeof(qcstats)/sizeof(qcstats[0]))\n\t{\n\t\tCon_Printf(\"Too many stat types\\n\");\n\t\treturn;\n\t}\n\n\tfor (i = 0; i < numqcstats; i++)\n\t{\n\t\t//strings use a different namespace.\n\t\tif (qcstats[i].statnum == statnum && ((qcstats[i].type == ev_string||qcstats[i].type == -ev_string) == (type == ev_string||type == -ev_string)))\n\t\t\tbreak;\n\t}\n\tif (i == numqcstats)\n\t{\n\t\tif (i == sizeof(qcstats)/sizeof(qcstats[0]))\n\t\t{\n\t\t\tCon_Printf(\"Too many stats specified for csqc\\n\");\n\t\t\treturn;\n\t\t}\n\t\tnumqcstats++;\n\t}\n\n\tqcstats[i].type = type;\n\tqcstats[i].statnum = statnum;\n\tQ_strncpyz(qcstats[i].name, name, sizeof(qcstats[i].name));\n\tmemset(&qcstats[i].eval, 0, sizeof(qcstats[i].eval));\n\tif (type <= 0)\n\t\tqcstats[i].eval.g = global;\n\telse if (field)\n\t\tmemcpy(&qcstats[i].eval.c, field, sizeof(evalc_t));\n\telse\n\t\tqcstats[i].type = ev_void;\n}\n\nvoid SV_QCStatGlobal(int type, const char *globalname, int statnum)\n{\n\teval_t *glob;\n\n\tif (type < 0)\n\t\treturn;\n\n\tglob = svprogfuncs->FindGlobal(svprogfuncs, globalname, PR_ANY, NULL);\n\tif (!glob)\n\t{\n\t\tCon_Printf(\"couldn't find named global for csqc stat (%s)\\n\", globalname);\n\t\treturn;\n\t}\n\tSV_QCStatEval(-type, globalname, NULL, glob, statnum);\n}\n\nvoid SV_QCStatPtr(int type, void *ptr, int statnum)\n{\n\tSV_QCStatEval(-type, \"\", NULL, ptr, statnum);\n}\n\nvoid SV_QCStatName(int type, char *name, int statnum)\n{\n\tevalc_t cache;\n\tif (type < 0)\n\t\treturn;\n\n\tmemset(&cache, 0, sizeof(cache));\n\tif (!svprogfuncs->GetEdictFieldValue(svprogfuncs, NULL, name, 0, &cache))\n\t\treturn;\n\n\tSV_QCStatEval(type, name, &cache, NULL, statnum);\n}\n\nvoid SV_QCStatFieldIdx(int type, unsigned int fieldindex, int statnum)\n{\n\tevalc_t cache;\n\tconst char *name;\n\tetype_t ftype;\n\n\tif (type < 0)\n\t\treturn;\n\n\tif (!svprogfuncs->QueryField(svprogfuncs, fieldindex, &ftype, &name, &cache))\n\t{\n\t\tCon_Printf(\"invalid field for csqc stat\\n\");\n\t\treturn;\n\t}\n\tSV_QCStatEval(type, name, &cache, NULL, statnum);\n}\n\nvoid SV_ClearQCStats(void)\n{\n\tnumqcstats = 0;\n\thighestqcstat = MAX_QW_STATS;\n}\n\nextern cvar_t dpcompat_stats;\nvoid SV_UpdateQCStats(edict_t\t*ent, int *statsi, char const** statss, float *statsf)\n{\n\tconst char *s;\n\tint i;\n\tint t;\n\n\tfor (i = 0; i < numqcstats; i++)\n\t{\n\t\teval_t *eval;\n\t\tt = qcstats[i].type;\n\t\tif (t <= 0)\n\t\t{\n\t\t\tt = -t;\n\t\t\teval = qcstats[i].eval.g;\n\t\t}\n\t\telse\n\t\t{\n\t\t\teval = svprogfuncs->GetEdictFieldValue(svprogfuncs, ent, qcstats[i].name, 0, &qcstats[i].eval.c);\n\t\t}\n\t\tif (!eval)\n\t\t\tcontinue;\n\n\t\tswitch(t)\n\t\t{\n\t\tcase ev_float:\n\t\t\tstatsf[qcstats[i].statnum] = eval->_float;\n\t\t\tbreak;\n\t\tcase ev_double:\n\t\t\tstatsf[qcstats[i].statnum] = eval->_double;\t//FIXME: precision loss.\n\t\t\tbreak;\n\t\tcase ev_vector:\t//split over 3 stats.\n\t\t\tstatsf[qcstats[i].statnum+0] = eval->_vector[0];\n\t\t\tstatsf[qcstats[i].statnum+1] = eval->_vector[1];\n\t\t\tstatsf[qcstats[i].statnum+2] = eval->_vector[2];\n\t\t\tbreak;\n\t\tcase ev_integer:\n\t\tcase ev_uint:\n\t\t\tstatsi[qcstats[i].statnum] = eval->_int;\n\t\t\tbreak;\n\t\tcase ev_int64:\n\t\tcase ev_uint64:\t//split over 2 stats.\n\t\t\tstatsi[qcstats[i].statnum] = eval->u64&0xffffffff;\n\t\t\tstatsi[qcstats[i].statnum+1] = eval->u64>>32;\n\t\t\tbreak;\n\t\tcase ev_entity:\n\t\t\tstatsi[qcstats[i].statnum] = NUM_FOR_EDICT(svprogfuncs, PROG_TO_EDICT(svprogfuncs, eval->edict));\n\t\t\tbreak;\n\t\tcase ev_string:\n\t\t\ts = PR_GetString(svprogfuncs, eval->string);\n\t\t\tstatss[qcstats[i].statnum] = s;\n//\t\t\tstatsi[qcstats[i].statnum+0] = LittleLong(((int*)s)[0]);\t//so the network is sent out correctly as a string.\n//\t\t\tstatsi[qcstats[i].statnum+1] = LittleLong(((int*)s)[1]);\n//\t\t\tstatsi[qcstats[i].statnum+2] = LittleLong(((int*)s)[2]);\n//\t\t\tstatsi[qcstats[i].statnum+3] = LittleLong(((int*)s)[3]);\n\t\t\tbreak;\n\t\t}\n\t}\n}\n\n/*this function calculates the current stat values for the given client*/\nstatic unsigned int SV_CalcClientStats(client_t *client, int statsi[MAX_CL_STATS], float statsf[MAX_CL_STATS], const char **statss)\n{\n\tunsigned int m = highestqcstat;\n\tedict_t *ent;\n\tent = client->edict;\n\tmemset (statsi, 0, sizeof(int)*MAX_CL_STATS);\n\tmemset (statsf, 0, sizeof(float)*MAX_CL_STATS);\n\tmemset ((void*)statss, 0, sizeof(char const*)*MAX_CL_STATS);\t//cast needed to get msvc to behave.\n\n\t// if we are a spectator and we are tracking a player, we get his stats\n\t// so our status bar reflects his\n\tif (client->spectator && client->spec_track > 0)\n\t\tent = EDICT_NUM_UB(svprogfuncs, client->spec_track);\n\n#ifdef HLSERVER\n\tif (svs.gametype == GT_HALFLIFE)\n\t{\n\t\tSVHL_BuildStats(client, statsi, statsf, statss);\n\t}\n\telse\n#endif\n\t{\n#ifdef QUAKESTATS\n\t\tif (client->spectator && !client->spec_track && ISNQCLIENT(client))\n\t\t{\n\t\t\tstatsf[STAT_HEALTH] = 1000;\n\t\t\tstatsf[STAT_ARMOR] = 1000;\n\t\t\tstatsf[STAT_AMMO] = 1000;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tstatsf[STAT_HEALTH] = ent->v->health;\t//sorry, but mneh\n\t\t\tstatsi[STAT_WEAPONMODELI] = SV_ModelIndex(PR_GetString(svprogfuncs, ent->v->weaponmodel));\n\t\t\tif ((unsigned)statsi[STAT_WEAPONMODELI] >= client->maxmodels)\n\t\t\t\tstatsi[STAT_WEAPONMODELI] = 0;\t//play it safe, try not to crash unsuspecting clients\n\t\t\tstatsf[STAT_AMMO] = ent->v->currentammo;\n\t\t\tstatsf[STAT_ARMOR] = ent->v->armorvalue;\n\t\t\tstatsf[STAT_SHELLS] = ent->v->ammo_shells;\n\t\t\tstatsf[STAT_NAILS] = ent->v->ammo_nails;\n\t\t\tstatsf[STAT_ROCKETS] = ent->v->ammo_rockets;\n\t\t\tstatsf[STAT_CELLS] = ent->v->ammo_cells;\n\t\t\tstatsf[STAT_ACTIVEWEAPON] = ent->v->weapon;\n\t\t\tif ((client->csqcactive && !(client->fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS)) || client->protocol != SCP_QUAKEWORLD || (client->fteprotocolextensions2 & PEXT2_PREDINFO))\n\t//\t\tif ((client->fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS) || client->protocol != SCP_QUAKEWORLD)\n\t\t\t\tstatsf[STAT_WEAPONFRAME] = ent->v->weaponframe;\t\t//weapon frame is sent differently with classic quakeworld protocols.\n\n\t\t\t// stuff the sigil bits into the high bits of items for sbar\n\t\t\tif (sv.haveitems2)\n\t\t\t\tstatsi[STAT_ITEMS] = (int)ent->v->items | ((int)ent->xv->items2 << 23);\n\t\t\telse\n\t\t\t\tstatsi[STAT_ITEMS] = (int)ent->v->items | ((int)pr_global_struct->serverflags << 28);\n\t\t}\n\n\t\tstatsf[STAT_VIEWHEIGHT] = ent->v->view_ofs[2];\n\t\tstatsf[STAT_IDEALPITCH] = ent->xv->idealpitch;\n\n\t\tstatsf[STAT_PUNCHANGLE_X] = ent->xv->punchangle[0];\n\t\tstatsf[STAT_PUNCHANGLE_Y] = ent->xv->punchangle[1];\n\t\tstatsf[STAT_PUNCHANGLE_Z] = ent->xv->punchangle[2];\n\n//\t\tstatsf[STAT_PUNCHORIGIN_X] = ent->xv->punchvector[0];\n//\t\tstatsf[STAT_PUNCHORIGIN_Y] = ent->xv->punchvector[1];\n//\t\tstatsf[STAT_PUNCHORIGIN_Z] = ent->xv->punchvector[2];\n\n\t#ifdef PEXT_VIEW2\n\t\tif (ent->xv->view2)\n\t\t\tstatsi[STAT_VIEW2] = NUM_FOR_EDICT(svprogfuncs, PROG_TO_EDICT(svprogfuncs, ent->xv->view2));\n\t\telse\n\t\t\tstatsi[STAT_VIEW2] = 0;\n\t#endif\n\n\t\tif (!ent->xv->viewzoom)\n\t\t\tstatsf[STAT_VIEWZOOM] = STAT_VIEWZOOM_SCALE;\n\t\telse\n\t\t\tstatsf[STAT_VIEWZOOM] = max(1,ent->xv->viewzoom*STAT_VIEWZOOM_SCALE);\n#endif\n\n#ifdef NQPROT\n\t\tif (client->protocol == SCP_DARKPLACES7 || (client->fteprotocolextensions2 & PEXT2_PREDINFO))\n\t\t{\n\t\t\textern cvar_t sv_stepheight;\n\t\t\tfloat\t*statsfi;\n\t\t\tif (client->fteprotocolextensions2 & PEXT2_PREDINFO)\n\t\t\t\tstatsfi = statsf;\n\t\t\telse\n\t\t\t{\n\t\t\t\tstatsfi = (float*)statsi;\t/*dp requires a union of ints and floats, which is rather hideous...*/\n\n\t\t\t\tstatsfi[STAT_FRAGLIMIT] = fraglimit.value;\n\t\t\t\tstatsfi[STAT_TIMELIMIT] = timelimit.value;\n\t\t\t}\n//commented out things are basically for xonotic's use. they're not implemented by the server's movement stuff, not in dp, not in fte.\n//that's not to say the client shouldn't support them (when mods have hacked up velocity stuff and no willingness to implement the same thing in csqc too).\n//\t\t\tstatsfi[STAT_MOVEVARS_AIRACCEL_QW_STRETCHFACTOR]\t= 0;\n//\t\t\tstatsfi[STAT_MOVEVARS_AIRCONTROL_PENALTY]\t\t\t= 0;\n//\t\t\tstatsfi[STAT_MOVEVARS_AIRSPEEDLIMIT_NONQW]\t\t\t= 0;\n//\t\t\tstatsfi[STAT_MOVEVARS_AIRSTRAFEACCEL_QW]\t\t\t= 0;\n//\t\t\tstatsfi[STAT_MOVEVARS_AIRCONTROL_POWER]\t\t\t\t= 2;\n\t\t\tstatsi [STAT_MOVEFLAGS]\t\t\t\t\t\t\t\t= MOVEFLAG_VALID|MOVEFLAG_QWCOMPAT;\n//\t\t\tstatsfi[STAT_MOVEVARS_WARSOWBUNNY_AIRFORWARDACCEL]\t= 0;\n//\t\t\tstatsfi[STAT_MOVEVARS_WARSOWBUNNY_ACCEL]\t\t\t= 0;\n//\t\t\tstatsfi[STAT_MOVEVARS_WARSOWBUNNY_TOPSPEED]\t\t\t= 0;\n//\t\t\tstatsfi[STAT_MOVEVARS_WARSOWBUNNY_TURNACCEL]\t\t= 0;\n//\t\t\tstatsfi[STAT_MOVEVARS_WARSOWBUNNY_BACKTOSIDERATIO]\t= 0;\n//\t\t\tstatsfi[STAT_MOVEVARS_AIRSTOPACCELERATE]\t\t\t= 0;\n//\t\t\tstatsfi[STAT_MOVEVARS_AIRSTRAFEACCELERATE]\t\t\t= 0;\n//\t\t\tstatsfi[STAT_MOVEVARS_MAXAIRSTRAFESPEED]\t\t\t= 0;\n//\t\t\tstatsfi[STAT_MOVEVARS_AIRCONTROL]\t\t\t\t\t= 0;\n//\t\t\tstatsfi[STAT_MOVEVARS_WALLFRICTION]\t\t\t\t\t= 0;\n\t\t\tstatsfi[STAT_MOVEVARS_FRICTION]\t\t\t\t\t\t= sv_friction.value;\n\t\t\tstatsfi[STAT_MOVEVARS_WATERFRICTION]\t\t\t\t= sv_waterfriction.value;\n\t\t\tstatsfi[STAT_MOVEVARS_TICRATE]\t\t\t\t\t\t= sv_mintic.value?sv_mintic.value:(1.0/72);\n\t\t\tstatsfi[STAT_MOVEVARS_TIMESCALE]\t\t\t\t\t= sv_gamespeed.value;\n\t\t\tstatsfi[STAT_MOVEVARS_GRAVITY]\t\t\t\t\t\t= sv_gravity.value;\n\t\t\tstatsfi[STAT_MOVEVARS_STOPSPEED]\t\t\t\t\t= sv_stopspeed.value;\n\t\t\tstatsfi[STAT_MOVEVARS_MAXSPEED]\t\t\t\t\t\t= client->maxspeed;\n\t\t\tstatsfi[STAT_MOVEVARS_SPECTATORMAXSPEED]\t\t\t= sv_spectatormaxspeed.value;\n\t\t\tstatsfi[STAT_MOVEVARS_ACCELERATE]\t\t\t\t\t= sv_accelerate.value;\n\t\t\tstatsfi[STAT_MOVEVARS_AIRACCELERATE]\t\t\t\t= sv_airaccelerate.value;\n\t\t\tstatsfi[STAT_MOVEVARS_WATERACCELERATE]\t\t\t\t= sv_wateraccelerate.value;\n\t\t\tstatsfi[STAT_MOVEVARS_ENTGRAVITY]\t\t\t\t\t= client->entgravity/sv_gravity.value;\n\t\t\tstatsfi[STAT_MOVEVARS_JUMPVELOCITY]\t\t\t\t\t= 270;//sv_jumpvelocity.value;\t//bah\n\t\t\tstatsfi[STAT_MOVEVARS_EDGEFRICTION]\t\t\t\t\t= pm_edgefriction.value;\n\t\t\tstatsfi[STAT_MOVEVARS_MAXAIRSPEED]\t\t\t\t\t= 30;\t//max speed before airaccel cuts out. this is hardcoded in qw pmove\n\t\t\tstatsfi[STAT_MOVEVARS_STEPHEIGHT]\t\t\t\t\t= *sv_stepheight.string?sv_stepheight.value:PM_DEFAULTSTEPHEIGHT;\n\t\t\tstatsfi[STAT_MOVEVARS_AIRACCEL_QW]\t\t\t\t\t= 1;\t\t//we're a quakeworld engine...\n\t\t\tstatsfi[STAT_MOVEVARS_AIRACCEL_SIDEWAYS_FRICTION]\t= 0;\n\n\t\t\tif (m < 256)\n\t\t\t\tm = 256;\n\t\t}\n#endif\n\n\t\tSV_UpdateQCStats(ent, statsi, statss, statsf);\n\t}\n\treturn m;\n}\n\n/*\n=======================\nSV_UpdateClientStats\n\nPerforms a delta update of the stats array.  This should only be performed\nwhen a reliable message can be delivered this frame.\n=======================\n*/\nvoid SV_UpdateClientStats (client_t *client, int pnum, sizebuf_t *msg, client_frame_t *frame)\n{\n\tint\t\tstatsi[MAX_CL_STATS];\n\tfloat\tstatsf[MAX_CL_STATS];\n\tconst char\t*statss[MAX_CL_STATS];\n\tunsigned int\t\ti, m;\n\n\t/*figure out what the stat values should be*/\n\tm = SV_CalcClientStats(client, statsi, statsf, statss);\n\tif ((client->fteprotocolextensions & (PEXT_HEXEN2|PEXT_CSQC)) || client->protocol == SCP_DARKPLACES6 || client->protocol == SCP_DARKPLACES7)\n\t\tm = min(m,256);\n\telse\n\t\tm = min(m,MAX_QW_STATS);\n\n\tif (client->fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS)\n\t{\n\t\t//diff numerical stats first\n\t\tfor (i=0 ; i<m ; i++)\n\t\t{\n\t\t\tif (client->statsi[i] != statsi[i] || client->statsf[i] != statsf[i])\n\t\t\t{\n\t\t\t\tclient->statsi[i] = statsi[i];\n\t\t\t\tclient->statsf[i] = statsf[i];\n\t\t\t\tclient->pendingstats[i>>5u] |= 1u<<(i&0x1f);\n\t\t\t}\n\t\t}\n\t\t//diff string stats.\n\t\tfor (i=0 ; i<m ; i++)\n\t\t{\n\t\t\tconst char *o=client->statss[i], *n=statss[i];\n\t\t\tif (o != n)\n\t\t\t{\n\t\t\t\tif (!o)\n\t\t\t\t\to = \"\";\n\t\t\t\tif (!n)\n\t\t\t\t\tn = \"\";\n\t\t\t\tif (strcmp(o, n))\n\t\t\t\t\tclient->pendingstats[(i+MAX_CL_STATS)>>5u] |= 1u<<((i+MAX_CL_STATS)&0x1f);\n\t\t\t\t//FIXME: we could always just run the QCGC on the player's string stats too. wouldn't need string compares that way\n\t\t\t\tif (client->statss[i])\n\t\t\t\t\tZ_Free((void*)client->statss[i]);\n\t\t\t\tclient->statss[i] = (statss[i]&&*statss[i])?Z_StrDup(statss[i]):NULL;\n\t\t\t}\n\t\t}\n\n\t\tfor (i=0 ; i<m ; i++)\n\t\t{\n\t\t\tif (client->pendingstats[i>>5u] & (1u<<(i&0x1f)))\n\t\t\t{\n\t\t\t\tfloat fval = client->statsf[i];\n\t\t\t\tint ival = client->statsi[i];\n\n\t\t\t\t//would overflow\n\t\t\t\tif (msg->cursize+8 >= msg->maxsize)\n\t\t\t\t\tbreak;\n\t\t\t\t//can't track it\n\t\t\t\tif (frame->numresendstats >= sizeof(frame->resendstats)/sizeof(frame->resendstats[0]))\n\t\t\t\t\tbreak;\n\t\t\t\t//we're going for it.\n\t\t\t\tclient->pendingstats[i>>5u] &= ~(1u<<(i&0x1f));\t//doesn't need resending any more\n\t\t\t\tframe->resendstats[frame->numresendstats++] = i | (pnum<<12);\n\t\t\t\tif (fval && fval != (float)(int)fval && !dpcompat_stats.ival)\n\t\t\t\t{\n\t\t\t\t\tif (pnum)\n\t\t\t\t\t{\n\t\t\t\t\t\tMSG_WriteByte(msg, svcfte_choosesplitclient);\n\t\t\t\t\t\tMSG_WriteByte(msg, pnum);\n\t\t\t\t\t}\n\t\t\t\t\tMSG_WriteByte(msg, svcfte_updatestatfloat);\n\t\t\t\t\tMSG_WriteByte(msg, i);\n\t\t\t\t\tMSG_WriteFloat(msg, fval);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tif (fval)\n\t\t\t\t\t\tival = fval;\n\t\t\t\t\tif (ival >= 0 && ival <= 255)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (pnum)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tMSG_WriteByte(msg, svcfte_choosesplitclient);\n\t\t\t\t\t\t\tMSG_WriteByte(msg, pnum);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tMSG_WriteByte(msg, ISNQCLIENT(client)?svcdp_updatestatbyte:svcqw_updatestatbyte);\n\t\t\t\t\t\tMSG_WriteByte(msg, i);\n\t\t\t\t\t\tMSG_WriteByte(msg, ival);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tif (pnum)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tMSG_WriteByte(msg, svcfte_choosesplitclient);\n\t\t\t\t\t\t\tMSG_WriteByte(msg, pnum);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tMSG_WriteByte(msg, ISNQCLIENT(client)?svcnq_updatestatlong:svcqw_updatestatlong);\n\t\t\t\t\t\tMSG_WriteByte(msg, i);\n\t\t\t\t\t\tMSG_WriteLong(msg, ival);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tfor (i=0 ; i<m ; i++)\n\t\t{\n\t\t\tif (client->pendingstats[(i+MAX_CL_STATS)>>5u] & (1u<<((i+MAX_CL_STATS)&0x1f)))\n\t\t\t{\n\t\t\t\tconst char *s = client->statss[i];\n\t\t\t\tif (!s)\n\t\t\t\t\ts = \"\";\n\n\t\t\t\t//would overflow\n\t\t\t\tif (msg->cursize+4+strlen(s) >= msg->maxsize)\n\t\t\t\t\tbreak;\n\t\t\t\t//can't track it\n\t\t\t\tif (frame->numresendstats >= sizeof(frame->resendstats)/sizeof(frame->resendstats[0]))\n\t\t\t\t\tbreak;\n\t\t\t\t//we're going for it.\n\t\t\t\tclient->pendingstats[(i+MAX_CL_STATS)>>5u] &= ~(1u<<((i+MAX_CL_STATS)&0x1f));\t//doesn't need resending any more\n\t\t\t\tframe->resendstats[frame->numresendstats++] = (i+MAX_CL_STATS) | (pnum<<12);\n\t\t\t\tif (pnum)\n\t\t\t\t{\n\t\t\t\t\tMSG_WriteByte(msg, svcfte_choosesplitclient);\n\t\t\t\t\tMSG_WriteByte(msg, pnum);\n\t\t\t\t}\n\t\t\t\tMSG_WriteByte(msg, svcfte_updatestatstring);\n\t\t\t\tMSG_WriteByte(msg, i);\n\t\t\t\tMSG_WriteString(msg, s);\n\t\t\t}\n\t\t}\n\t}\n\telse\n\n\tfor (i=0 ; i<m ; i++)\n\t{\n#ifdef SERVER_DEMO_PLAYBACK\n\t\tif (sv.demofile)\n\t\t{\n\t\t\tif (!client->spec_track)\n\t\t\t{\n\t\t\t\tstatsf[i] = 0;\n\t\t\t\tif (i == STAT_HEALTH)\n\t\t\t\t\tstatsf[i] = 100;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tstatsf[i] = sv.recordedplayer[client->spec_track - 1].stats[i];\n\t\t\t\tstatsi[i] = sv.recordedplayer[client->spec_track - 1].stats[i];\n\t\t\t}\n\t\t}\n#endif\n\t\tif (!ISQWCLIENT(client))\n\t\t{\n\t\t\tif (!statsi[i])\n\t\t\t\tstatsi[i] = statsf[i];\n\t\t\tif (statsi[i] != client->statsi[i])\n\t\t\t{\n\t\t\t\tclient->statsi[i] = statsi[i];\n\t\t\t\tClientReliableWrite_Begin(client, svcnq_updatestatlong, 6);\n\t\t\t\tClientReliableWrite_Byte(client, i);\n\t\t\t\tClientReliableWrite_Long(client, statsi[i]);\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n#ifdef PEXT_CSQC\n\t\t\tif (client->fteprotocolextensions & PEXT_CSQC)\n\t\t\t{\n\t\t\t\tif (statss[i] || client->statss[i])\n\t\t\t\tif (strcmp(statss[i]?statss[i]:\"\", client->statss[i]?client->statss[i]:\"\"))\n\t\t\t\t{\n\t\t\t\t\tif (client->statss[i])\n\t\t\t\t\t\tZ_Free((void*)client->statss[i]);\n\t\t\t\t\tif (statss[i] && *statss[i])\n\t\t\t\t\t\tclient->statss[i] = Z_StrDup(statss[i]);\n\t\t\t\t\telse\n\t\t\t\t\t\tclient->statss[i] = NULL;\n\n\t\t\t\t\tif (pnum)\n\t\t\t\t\t{\n\t\t\t\t\t\tClientReliableWrite_Begin(client->controller, svcfte_choosesplitclient, 5+strlen(statss[i]));\n\t\t\t\t\t\tClientReliableWrite_Byte(client->controller, pnum);\n\t\t\t\t\t\tClientReliableWrite_Byte(client->controller, svcfte_updatestatstring);\n\t\t\t\t\t\tClientReliableWrite_Byte(client->controller, i);\n\t\t\t\t\t\tClientReliableWrite_String(client->controller, statss[i]);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tClientReliableWrite_Begin(client, svcfte_updatestatstring, 3+strlen(statss[i]));\n\t\t\t\t\t\tClientReliableWrite_Byte(client, i);\n\t\t\t\t\t\tClientReliableWrite_String(client, statss[i]);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (dpcompat_stats.ival)\n\t\t\t{\n\t\t\t\tif (statsf[i])\n\t\t\t\t{\n\t\t\t\t\tstatsi[i] = statsf[i];\n\t\t\t\t\tstatsf[i] = 0;\n\t\t\t\t}\n\t\t\t}\n#endif\n\n\t\t\tif (statsf[i])\n\t\t\t{\n\t\t\t\tif (client->fteprotocolextensions & PEXT_CSQC)\n\t\t\t\t{\n\t\t\t\t\tif (statsf[i] != client->statsf[i])\n\t\t\t\t\t{\n\t\t\t\t\t\tif (statsf[i] - (float)(int)statsf[i] == 0 && statsf[i] >= 0 && statsf[i] <= 255)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (pnum)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tClientReliableWrite_Begin(client->controller, svcfte_choosesplitclient, 5);\n\t\t\t\t\t\t\t\tClientReliableWrite_Byte(client->controller, pnum);\n\t\t\t\t\t\t\t\tClientReliableWrite_Byte(client->controller, svcqw_updatestatbyte);\n\t\t\t\t\t\t\t\tClientReliableWrite_Byte(client->controller, i);\n\t\t\t\t\t\t\t\tClientReliableWrite_Byte(client->controller, statsf[i]);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tClientReliableWrite_Begin(client, svcqw_updatestatbyte, 3);\n\t\t\t\t\t\t\t\tClientReliableWrite_Byte(client, i);\n\t\t\t\t\t\t\t\tClientReliableWrite_Byte(client, statsf[i]);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (pnum)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tClientReliableWrite_Begin(client->controller, svcfte_choosesplitclient, 8);\n\t\t\t\t\t\t\t\tClientReliableWrite_Byte(client->controller, pnum);\n\t\t\t\t\t\t\t\tClientReliableWrite_Byte(client->controller, svcfte_updatestatfloat);\n\t\t\t\t\t\t\t\tClientReliableWrite_Byte(client->controller, i);\n\t\t\t\t\t\t\t\tClientReliableWrite_Float(client->controller, statsf[i]);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tClientReliableWrite_Begin(client, svcfte_updatestatfloat, 6);\n\t\t\t\t\t\t\t\tClientReliableWrite_Byte(client, i);\n\t\t\t\t\t\t\t\tClientReliableWrite_Float(client, statsf[i]);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tclient->statsf[i] = statsf[i];\n\t\t\t\t\t\t/*make sure statsf is correct*/\n\t\t\t\t\t\tclient->statsi[i] = statsf[i];\n\t\t\t\t\t}\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tstatsi[i] = statsf[i];\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (statsi[i] != client->statsi[i])\n\t\t\t{\n\t\t\t\tclient->statsi[i] = statsi[i];\n\t\t\t\tclient->statsf[i] = statsi[i];\n\n\t\t\t\tif (statsi[i] >=0 && statsi[i] <= 255)\n\t\t\t\t{\n\t\t\t\t\tif (pnum)\n\t\t\t\t\t{\n\t\t\t\t\t\tClientReliableWrite_Begin(client->controller, svcfte_choosesplitclient, 5);\n\t\t\t\t\t\tClientReliableWrite_Byte(client->controller, pnum);\n\t\t\t\t\t\tClientReliableWrite_Byte(client->controller, svcqw_updatestatbyte);\n\t\t\t\t\t\tClientReliableWrite_Byte(client->controller, i);\n\t\t\t\t\t\tClientReliableWrite_Byte(client->controller, statsi[i]);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tClientReliableWrite_Begin(client, svcqw_updatestatbyte, 3);\n\t\t\t\t\t\tClientReliableWrite_Byte(client, i);\n\t\t\t\t\t\tClientReliableWrite_Byte(client, statsi[i]);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tif (pnum)\n\t\t\t\t\t{\n\t\t\t\t\t\tClientReliableWrite_Begin(client->controller, svcfte_choosesplitclient, 8);\n\t\t\t\t\t\tClientReliableWrite_Byte(client->controller, pnum);\n\t\t\t\t\t\tClientReliableWrite_Byte(client->controller, svcqw_updatestatlong);\n\t\t\t\t\t\tClientReliableWrite_Byte(client->controller, i);\n\t\t\t\t\t\tClientReliableWrite_Long(client->controller, statsi[i]);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tClientReliableWrite_Begin(client, svcqw_updatestatlong, 6);\n\t\t\t\t\t\tClientReliableWrite_Byte(client, i);\n\t\t\t\t\t\tClientReliableWrite_Long(client, statsi[i]);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nqboolean SV_CanTrack(client_t *client, int entity)\n{\n\tif (entity <= 0 || entity > sv.allocated_client_slots)\n\t\treturn false;\n\tif (svs.clients[entity-1].spectator)\n\t\treturn false;\n\tif (svs.clients[entity-1].state == cs_spawned || (svs.clients[entity-1].state == cs_free && svs.clients[entity-1].userinfo.numkeys))\n\t\treturn true;\n\treturn false;\n}\n\n/*\n=======================\nSV_SendClientDatagram\n=======================\n*/\nqboolean SV_SendClientDatagram (client_t *client)\n{\n\tqbyte\t\tbuf[MAX_OVERALLMSGLEN-64/*play safe*/];\n\tsizebuf_t\tmsg;\n\tsize_t\t\tclientlimit;\n\tunsigned int sentbytes;\n\tunsigned int outframeseq = client->netchan.incoming_sequence;\t//this is so weird... but at least covers nq/qw sequence vs unreliables weirdness...\n\n\tif (ISQWCLIENT(client) || ISNQCLIENT(client))\n\t{\n\t\tclient_frame_t *frame = &client->frameunion.frames[outframeseq & UPDATE_MASK];\n\t\tframe->numresendstats = 0;\n\t}\n\n\tmsg.data = buf;\n\tmsg.maxsize = sizeof(buf)-50;\n\tmsg.cursize = 0;\n\tmsg.allowoverflow = true;\n\tmsg.overflowed = false;\n\tmsg.prim = client->datagram.prim;\n\n\tif (client->spec_track && !SV_CanTrack(client, client->spec_track))\n\t{\n\t\tclient->spec_track = 0;\n\t\tclient->edict->v->goalentity = 0;\n\t}\n\n\tclientlimit = Netchan_GetMaxUnreliable(&client->netchan);\n\tif (clientlimit < 1024)\n\t\tclientlimit = 1024;\t//ignore what the netchan wants. it'll have to fragment it.\n\n\tif (client->protocol == SCP_NETQUAKE && clientlimit > MAX_NQDATAGRAM && client->netchan.remote_address.type != NA_LOOPBACK && !client->pextknown)\n\t\tclientlimit = MAX_NQDATAGRAM;\t//vanilla clients are limited. don't crash them.\n\n\tif (clientlimit > countof(buf))\n\t\tclientlimit = countof(buf);\n\tmsg.maxsize = clientlimit - client->datagram.cursize;\n\tif (msg.maxsize <= 0)\n\t\tmsg.maxsize = clientlimit;\t//its going to overflow. favour ents over unreliables. its a little less fatal\n\n\tif (sv.world.worldmodel && !client->controller)\n\t{\n#ifdef Q2SERVER\n\t\tif (ISQ2CLIENT(client))\n\t\t{\n\t\t\tSVQ2_BuildClientFrame (client);\n\n\t\t\t// send over all the relevant entity_state_t\n\t\t\t// and the player_state_t\n\t\t\tSVQ2_WriteFrameToClient (client, &msg);\n\t\t}\n\t\telse\n#endif\n\t\t{\n\n\t\t\tif (!ISQ2CLIENT(client) && ((client->fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS) || Netchan_CanReliable (&client->netchan, SV_RateForClient(client))))\n\t\t\t{\n\t\t\t\tint pnum=1;\n\t\t\t\tclient_t *c;\n\t\t\t\tclient_frame_t *frame = &client->frameunion.frames[outframeseq & UPDATE_MASK];\n\t\t\t\tSV_UpdateClientStats (client, 0, &msg, frame);\n\n\t\t\t\tfor (c = client->controlled; c; c = c->controlled,pnum++)\n\t\t\t\t\tSV_UpdateClientStats(c, pnum, &msg, frame);\n\t\t\t}\n\n\t\t\t// add the client specific data to the datagram\n\t\t\tSV_WriteClientdataToMessage (client, &msg);\n\n\t\t\t// send over all the objects that are in the PVS\n\t\t\t// this will include clients, a packetentities, and\n\t\t\t// possibly a nails update\n\t\t\tSV_WriteEntitiesToClient (client, &msg, false);\n\t\t}\n#ifdef VOICECHAT\n\t\tSV_VoiceSendPacket(client, &msg);\n#endif\n\t}\n\n\tmsg.maxsize = clientlimit;\n\t// copy the accumulated multicast datagram\n\t// for this client out to the message\n\tif (!client->datagram.overflowed && !msg.overflowed && msg.cursize + client->datagram.cursize <= clientlimit)\n\t{\n\t\tSZ_Write (&msg, client->datagram.data, client->datagram.cursize);\n\t\tSZ_Clear (&client->datagram);\n\t}\n\n\tif (msg.overflowed)\n\t{\n\t\tCon_Printf (\"WARNING: msg overflowed for %s\\n\", client->name);\n\t\tSZ_Clear (&msg);\n\t}\n\n#ifdef NQPROT\n\tSV_DarkPlacesDownloadChunk(client, &msg);\n#endif\n\n\t// send the datagram\n\tsentbytes = Netchan_Transmit (&client->netchan, msg.cursize, buf, SV_RateForClient(client));\n\tif (ISNQCLIENT(client))\n\t{\n\t\tclient_frame_t *frame = &client->frameunion.frames[outframeseq & UPDATE_MASK];\n\t\tframe->packetsizeout += sentbytes;\n\t\tframe->ping_time = -1;\n\t\tframe->senttime = realtime;\n\t}\n\telse if (ISQWCLIENT(client))\n\t{\n\t\tclient_frame_t *frame = &client->frameunion.frames[client->netchan.outgoing_sequence & UPDATE_MASK];\n\t\tframe->packetsizeout += sentbytes;\n\t}\n\n\tif (ISNQCLIENT(client) && (client->fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS))\n\t{\n\t\tif (!client->datagram.overflowed && client->datagram.cursize)\n\t\t{\n\t\t\tSZ_Clear (&msg);\n\t\t\tSZ_Write (&msg, client->datagram.data, client->datagram.cursize);\n\t\t\tSZ_Clear (&client->datagram);\n\n\t\t\tsentbytes = Netchan_Transmit (&client->netchan, msg.cursize, buf, SV_RateForClient(client));\n\t\t\tif (ISQWCLIENT(client) || ISNQCLIENT(client))\n\t\t\t{\n\t\t\t\tclient_frame_t *frame = &client->frameunion.frames[client->netchan.outgoing_sequence & UPDATE_MASK];\n\t\t\t\tframe->packetsizeout += sentbytes;\n\t\t\t}\n\t\t}\n\t}\n\n\tif (client->datagram.cursize)\n\t{\n\t\tCon_Printf (\"WARNING: datagram overflowed for %s\\n\", client->name);\n\t\tSZ_Clear (&client->datagram);\n\t}\n\treturn true;\n}\n\nclient_t *SV_SplitClientDest(client_t *client, qbyte first, int size)\n{\n\tclient_t *sp;\n\tif (client->controller)\n\t{\t//this is a slave client.\n\t\t//find the right number and send.\n\t\tint pnum = 0;\n\t\tfor (sp = client->controller; sp; sp = sp->controlled)\n\t\t{\n\t\t\tif (sp == client)\n\t\t\t\tbreak;\n\t\t\tpnum++;\n\t\t}\n\t\tsp = client->controller;\n\n\t\tClientReliableWrite_Begin (sp, svcfte_choosesplitclient, size+2);\n\t\tClientReliableWrite_Byte (sp, pnum);\n\t\tClientReliableWrite_Byte (sp, first);\n\t\treturn sp;\n\t}\n\telse\n\t{\n\t\tClientReliableWrite_Begin (client, first, size);\n\t\treturn client;\n\t}\n}\n\nvoid SV_FlushBroadcasts (void)\n{\n\tclient_t *client;\n\tint j;\n\t// append the broadcast messages to each client messages\n\tfor (j=0, client = svs.clients ; j<svs.allocated_client_slots ; j++, client++)\n\t{\n\t\tif (client->state < cs_connected)\n\t\t\tcontinue;\t// reliables go to all connected or spawned\n\t\tif (client->controller)\n\t\t\tcontinue;\t//splitscreen\n\n\t\tif (client->protocol == SCP_BAD)\n\t\t\tcontinue;\t//botclient\n\n#ifdef NQPROT\n\t\tif (ISNQCLIENT(client))\n\t\t{\n\t\t\tif (client->pextknown)\t//hacky check - means 'has sent an svc_serverdata'\n\t\t\t{\n\t\t\t\tClientReliableCheckBlock(client, sv.nqreliable_datagram.cursize);\n\t\t\t\tClientReliableWrite_SZ(client, sv.nqreliable_datagram.data, sv.nqreliable_datagram.cursize);\n\t\t\t}\n\t\t\tif (client->state != cs_spawned)\n\t\t\t\tcontinue;\t// datagrams only go to spawned\n\t\t\tSZ_Write (&client->datagram\n\t\t\t\t, sv.nqdatagram.data\n\t\t\t\t, sv.nqdatagram.cursize);\n\t\t}\n\t\telse\n#endif\n\t\t{\n\t\t\tClientReliableCheckBlock(client, sv.reliable_datagram.cursize);\n\t\t\tClientReliableWrite_SZ(client, sv.reliable_datagram.data, sv.reliable_datagram.cursize);\n\n\t\t\tif (client->state != cs_spawned)\n\t\t\t\tcontinue;\t// datagrams only go to spawned\n\t\t\tSZ_Write (&client->datagram\n\t\t\t\t, sv.datagram.data\n\t\t\t\t, sv.datagram.cursize);\n\t\t}\n\t}\n\n#ifdef MVD_RECORDING\n\tif (sv.mvdrecording)\n\t\tSV_MVD_WriteReliables(true);\n#endif\n\n\tSZ_Clear (&sv.reliable_datagram);\n\tSZ_Clear (&sv.datagram);\n#ifdef NQPROT\n\tSZ_Clear (&sv.nqreliable_datagram);\n\tSZ_Clear (&sv.nqdatagram);\n#endif\n}\n\nstatic qboolean SV_SyncInfoBuf(client_t *client)\n{\n\tconst char *key = client->infosync.keys[0].name;\n\tinfobuf_t *info = client->infosync.keys[0].context;\n\tsize_t bloboffset = client->infosync.keys[0].syncpos;\n\t//unsigned int seat = info - cls.userinfo;\n\tqboolean large;\n\tsize_t blobsize;\n\tconst char *blobdata = InfoBuf_BlobForKey(info, key, &blobsize, &large);\n\tsize_t sendsize;\n\tsize_t bufferspace;\n\n\tqboolean final;\n\tsizebuf_t *buf;\n\n\tif (ISQ2CLIENT(client))\n\t{\t//q2 gamecode is fully responsible for networking this via configstrings.\n\t\tInfoSync_Clear(&client->infosync);\n\t\treturn false;\n\t}\n\n\tif (client->num_backbuf)\n\t\treturn false;\n\tif (client->netchan.message.cursize >= MAX_BACKBUFLEN/2)\n\t\treturn false;\t//don't bother trying to send anything.\n\n\tif (!large)\n\t{\t//vanilla-compatible info.\n\t\tif (!blobdata)\n\t\t\tblobdata = \"\";\n\nCon_DLPrintf(2, \"%s: info %u:%s\\n\", client->name, (info == &svs.info)?0:(unsigned int)((client_t*)((char*)info-(char*)&((client_t*)NULL)->userinfo)-svs.clients), key);\n\t\tif (ISNQCLIENT(client))\n\t\t{\t//except that nq never had any userinfo\n\t\t\tconst char *s;\n\t\t\tif (info == &svs.info)\n\t\t\t\ts = va(\"//svi \\\"%s\\\" \\\"%s\\\"\\n\", key, blobdata);\n\t\t\telse\n\t\t\t{\n\t\t\t\tint playerslot = (client_t*)((char*)info-(char*)&((client_t*)NULL)->userinfo)-svs.clients;\n\t\t\t\ts = va(\"//ui %i \\\"%s\\\" \\\"%s\\\"\\n\", playerslot, key, blobdata);\n\t\t\t}\n\t\t\tbuf = ClientReliable_StartWrite(client, 2+strlen(s));\n\t\t\tMSG_WriteByte(buf, svc_stufftext);\n\t\t\tMSG_WriteString(buf, s);\n\t\t\tClientReliable_FinishWrite(client);\n\t\t}\n\t\telse if (ISQWCLIENT(client))\n\t\t{\n\t\t\tbuf = ClientReliable_StartWrite(client, 2+strlen(key)+1+strlen(blobdata)+1);\n\t\t\tif (info == &svs.info)\n\t\t\t\tMSG_WriteByte(buf, svc_serverinfo);\n\t\t\telse\n\t\t\t{\n\t\t\t\tMSG_WriteByte(buf, svc_setinfo);\n\t\t\t\tMSG_WriteByte(buf, (client_t*)((char*)info-(char*)&((client_t*)NULL)->userinfo)-svs.clients);\n\t\t\t}\n\t\t\tMSG_WriteString(buf, key);\n\t\t\tMSG_WriteString(buf, blobdata);\n\t\t\tClientReliable_FinishWrite(client);\n\t\t}\n\t}\n\telse if (client->fteprotocolextensions2 & PEXT2_INFOBLOBS)\n\t{\n\t\tchar enckey[2048];\n\t\tunsigned int pl;\n\t\tif (info == &svs.info)\n\t\t\tpl = 0;\t//players are 1-based. 0 is used for serverinfo.\n\t\telse\n\t\t\tpl = 1+((client_t*)((char*)info-(char*)&((client_t*)NULL)->userinfo)-svs.clients);\n\n\t\tif (!InfoBuf_EncodeString(key, strlen(key), enckey, sizeof(enckey)))\n\t\t{\n\t\t\tInfoSync_Remove(&client->infosync, 0);\n\t\t\treturn false;\n\t\t}\n\t\tif (!blobdata)\n\t\t\tbloboffset = 0;\t//wiped or something? I dunno, don't bug out though.\n\n\t\tsendsize = blobsize - bloboffset;\n\t\tbufferspace = MAX_BACKBUFLEN - client->netchan.message.cursize;\n\t\tbufferspace -= 8 + strlen(enckey) + 1;\t//extra overhead\n\t\tsendsize = min(bufferspace, sendsize);\n\t\tfinal = (bloboffset+sendsize >= blobsize);\n\nCon_DLPrintf(2, \"%s: blob %u:%s@%u-%u\\n\", client->name, pl, key, (unsigned int)bloboffset, (unsigned int)(bloboffset+sendsize));\n\t\tbuf = ClientReliable_StartWrite(client, 8+strlen(enckey)+1+sendsize);\n\t\tMSG_WriteByte(buf, svcfte_setinfoblob);\n\t\tMSG_WriteByte(buf, pl);\n\t\tMSG_WriteString(buf, enckey);\n\t\tMSG_WriteLong(buf, (final?0x80000000:0)|bloboffset);\n\t\tMSG_WriteShort(buf, sendsize);\n\t\tSZ_Write(buf, blobdata+bloboffset, sendsize);\n\t\tClientReliable_FinishWrite(client);\n\n\t\tif (!final)\n\t\t{\n\t\t\tclient->infosync.keys[0].syncpos += sendsize;\n\t\t\treturn true;\n\t\t}\n\t}\n\t//else client can't receive this info, stop trying to send it.\n\n\tInfoSync_Remove(&client->infosync, 0);\n\treturn true;\n}\n\n/*\n=======================\nSV_UpdateToReliableMessages\n=======================\n*/\nvoid SV_UpdateToReliableMessages (void)\n{\n\tint\t\t\ti, j;\n\tclient_t *client, *sp;\n\tedict_t *ent;\n\tconst char *name;\n\n\tfloat curgrav;\n\tfloat curspeed;\n\tint curfrags;\n\n\tstatic double pingtimer, lasttime;\n\tdouble t = Sys_DoubleTime();\n\tqboolean sendpings = false;\n\tpingtimer -= (t-lasttime);\n\tlasttime = t;\n\tif (pingtimer < 0)\n\t{\t//update about once every 5 secs.\n\t\tsendpings = true;\n\t\tpingtimer = 5;\n\t}\n\n// check for changes to be sent over the reliable streams to all clients\n\tfor (i=0, host_client = svs.clients ; i<svs.allocated_client_slots ; i++, host_client++)\n\t{\n\t\tif ((svs.gametype == GT_Q1QVM || svs.gametype == GT_PROGS) && host_client->state == cs_spawned)\n\t\t{\n#ifdef HAVE_LEGACY\n\t\t\t//DP_SV_CLIENTCOLORS\n\t\t\tif (host_client->edict->xv->clientcolors != host_client->playercolor)\n\t\t\t{\n\t\t\t\tInfoBuf_SetValueForKey(&host_client->userinfo, \"topcolor\", va(\"%i\", (int)host_client->edict->xv->clientcolors/16));\n\t\t\t\tInfoBuf_SetValueForKey(&host_client->userinfo, \"bottomcolor\", va(\"%i\", (int)host_client->edict->xv->clientcolors&15));\n\t\t\t\t{\n\t\t\t\t\tSV_ExtractFromUserinfo (host_client, true);\t//this will take care of nq for us anyway.\n\t\t\t\t\tSV_BroadcastUserinfoChange(host_client, true, \"*bothcolours\", NULL);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (host_client->dp_ping)\n\t\t\t\t*host_client->dp_ping = SV_CalcPing (host_client, false);\n\t\t\tif (host_client->dp_pl)\n\t\t\t\t*host_client->dp_pl = host_client->lossage;\n#endif\n\n#ifdef PEXT_VIEW2\n\t\t\tj = PROG_TO_EDICTINDEX(svprogfuncs, host_client->edict->xv->clientcamera);\n\t\t\tif (j)\n\t\t\t{\n\t\t\t\tif ((unsigned int)j >= svprogfuncs->edicttable_length)\n\t\t\t\t\tj = i+1;\n\t\t\t\tif (j != host_client->clientcamera)\n\t\t\t\t{\n\t\t\t\t\tif (host_client->fteprotocolextensions & PEXT_VIEW2)\n\t\t\t\t\t{\n\t\t\t\t\t\tClientReliableWrite_Begin(host_client, svc_setview, 4);\n\t\t\t\t\t\tClientReliableWrite_Entity(host_client, j);\n\t\t\t\t\t}\n\t\t\t\t\tif (j == i+1)\n\t\t\t\t\t\tj = 0;\t//self.\n\t\t\t\t\thost_client->viewent = j;\n\t\t\t\t}\n\t\t\t}\n#endif\n\n\t\t\tname = PR_GetString(svprogfuncs, host_client->edict->v->netname);\n#ifndef QCGC\t//this optimisation doesn't really work with a QC instead of static string management\n\t\t\tif (name != host_client->name)\n#endif\n\t\t\t{\n\t\t\t\tif (strcmp(host_client->name, name))\n\t\t\t\t{\n\t\t\t\t\tchar oname[80];\n\t\t\t\t\tQ_strncpyz(oname, host_client->name, sizeof(oname));\n\n\t\t\t\t\tCon_DPrintf(\"Client %s programatically renamed to %s\\n\", host_client->name, name);\n\t\t\t\t\tInfoBuf_SetValueForKey(&host_client->userinfo, \"name\", name);\n\t\t\t\t\tSV_ExtractFromUserinfo (host_client, true);\n\n\t\t\t\t\tif (strcmp(oname, host_client->name))\n\t\t\t\t\t{\n\t\t\t\t\t\tSV_BroadcastUserinfoChange(host_client, true, \"name\", host_client->name);\n\t\t\t\t\t}\n\n#ifdef QCGC\n\t\t\t\t\t//if it got rejected/mangled, make sure the qc properly sees the current value.\n\t\t\t\t\tsvprogfuncs->SetStringField(svprogfuncs, host_client->edict, &host_client->edict->v->netname, host_client->name, true);\n#endif\n\t\t\t\t}\n#ifndef QCGC\n\t\t\t\tsvprogfuncs->SetStringField(svprogfuncs, host_client->edict, &host_client->edict->v->netname, host_client->name, true);\n#endif\n\t\t\t}\n\t\t}\n\n\t\tif (host_client->state != cs_spawned)\n\t\t{\n\t\t\tif (!host_client->state && host_client->name && host_client->name[0])\t//if this is a writebyte bot\n\t\t\t{\n\t\t\t\tif (host_client->old_frags != (int)host_client->edict->v->frags)\n\t\t\t\t{\n\t\t\t\t\tfor (j=0, client = svs.clients ; j<svs.allocated_client_slots ; j++, client++)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (client->state < cs_connected)\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\tClientReliableWrite_Begin(client, svc_updatefrags, 4);\n\t\t\t\t\t\tClientReliableWrite_Byte(client, i);\n#ifdef NQPROT\n\t\t\t\t\t\tif (ISNQCLIENT(client) && host_client->spectator == 1)\n\t\t\t\t\t\t\tClientReliableWrite_Short(client, -999);\n\t\t\t\t\t\telse\n#endif\n\t\t\t\t\t\t\tClientReliableWrite_Short(client, host_client->edict->v->frags);\n\t\t\t\t\t}\n\n#ifdef MVD_RECORDING\n\t\t\t\t\tif (sv.mvdrecording)\n\t\t\t\t\t{\n\t\t\t\t\t\tsizebuf_t *msg = MVDWrite_Begin(dem_all, 0, 4);\n\t\t\t\t\t\tMSG_WriteByte(msg, svc_updatefrags);\n\t\t\t\t\t\tMSG_WriteByte(msg, i);\n\t\t\t\t\t\tMSG_WriteShort(msg, host_client->edict->v->frags);\n\t\t\t\t\t}\n#endif\n\n\t\t\t\t\thost_client->old_frags = host_client->edict->v->frags;\n\t\t\t\t}\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\tif (svs.gametype == GT_PROGS || svs.gametype == GT_Q1QVM)\n\t\t{\n\t\t\tent = host_client->edict;\n\n\t\t\tcurfrags = host_client->edict->v->frags;\n\t\t\tcurgrav = ent->xv->gravity*sv_gravity.value;\n\t\t\tcurspeed = ent->xv->maxspeed;\n\t\t\tif (progstype != PROG_QW)\n\t\t\t{\n\t\t\t\tif (!curgrav)\n\t\t\t\t\tcurgrav = sv_gravity.value;\n\t\t\t\tif (!curspeed)\n\t\t\t\t\tcurspeed = sv_maxspeed.value;\n\t\t\t}\n#ifdef HEXEN2\n\t\t\tif (ent->xv->hasted)\n\t\t\t\tcurspeed*=ent->xv->hasted;\n#endif\n\t\t}\n\t\telse\n\t\t{\n\t\t\tcurgrav = sv_gravity.value;\n\t\t\tcurspeed = sv_maxspeed.value;\n\t\t\tcurfrags = 0;\n\t\t}\n#ifdef SVCHAT\t//enforce a no moving time when chatting. Prevent client prediction going mad.\n\t\tif (host_client->chat.active)\n\t\t\tcurspeed = 0;\n#endif\n\n\t\tif (!ISQ2CLIENT(host_client))\n\t\t{\n\t\t\tif (host_client->sendinfo)\n\t\t\t{\n\t\t\t\thost_client->sendinfo = false;\n\t\t\t\tSV_FullClientUpdate (host_client, NULL);\n\t\t\t}\n\n\t\t\tif (host_client->qex && sendpings)\n\t\t\t{\n\t\t\t\tsizebuf_t *m;\n\t\t\t\tfor (j=0, client = svs.clients ; j<svs.allocated_client_slots && j < host_client->max_net_clients; j++, client++)\n\t\t\t\t{\n\t\t\t\t\tif (client->state != cs_spawned)\n\t\t\t\t\t\tcontinue;\n\n\t\t\t\t\tm = ClientReliable_StartWrite(host_client, 64);\n\t\t\t\t\tMSG_WriteByte(m, svcqex_updateping);\n\t\t\t\t\tMSG_WriteByte(m, j);\n\t\t\t\t\tMSG_WriteSignedQEX(m, SV_CalcPing(client, false));\n\t\t\t\t\tClientReliable_FinishWrite(host_client);\n\n\t\t\t\t\tif (coop.ival)\n\t\t\t\t\t{\n\t\t\t\t\t\tm = ClientReliable_StartWrite(host_client, 64);\n\t\t\t\t\t\tMSG_WriteByte(m, svcqex_updateplinfo);\n\t\t\t\t\t\tMSG_WriteByte(m, j);\n\t\t\t\t\t\tMSG_WriteSignedQEX(m, client->edict->v->health);\n\t\t\t\t\t\tMSG_WriteSignedQEX(m, client->edict->v->armorvalue);\n\t\t\t\t\t\tClientReliable_FinishWrite(host_client);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (host_client->old_frags != curfrags)\n\t\t\t{\n\t\t\t\tfor (j=0, client = svs.clients ; j<sv.allocated_client_slots ; j++, client++)\n\t\t\t\t{\n\t\t\t\t\tif (client->state < cs_connected)\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\tif (client->controller)\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\tswitch(client->protocol)\n\t\t\t\t\t{\n\t\t\t\t\tcase SCP_BAD:\t//bots\n\t\t\t\t\tcase SCP_QUAKE2:\n\t\t\t\t\tcase SCP_QUAKE2EX:\n\t\t\t\t\tcase SCP_QUAKE3:\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tClientReliableWrite_Begin(client, svc_updatefrags, 4);\n\t\t\t\t\t\tClientReliableWrite_Byte(client, i);\n#ifdef NQPROT\n\t\t\t\t\t\tif (ISNQCLIENT(client) && host_client->spectator == 1)\n\t\t\t\t\t\t\tClientReliableWrite_Short(client, -999);\n\t\t\t\t\t\telse\n#endif\n\t\t\t\t\t\t\tClientReliableWrite_Short(client, curfrags);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\n#ifdef MVD_RECORDING\n\t\t\t\tif (sv.mvdrecording)\n\t\t\t\t{\n\t\t\t\t\tsizebuf_t *msg = MVDWrite_Begin(dem_all, 0, 4);\n\t\t\t\t\tMSG_WriteByte(msg, svc_updatefrags);\n\t\t\t\t\tMSG_WriteByte(msg, i);\n\t\t\t\t\tMSG_WriteShort(msg, curfrags);\n\t\t\t\t}\n#endif\n\n\t\t\t\thost_client->old_frags = curfrags;\n\t\t\t}\n\n\t\t\t{\n\t\t\t\tif (host_client->entgravity != curgrav)\n\t\t\t\t{\n\t\t\t\t\tif (ISQWCLIENT(host_client))\n\t\t\t\t\t{\n\t\t\t\t\t\tsp = SV_SplitClientDest(host_client, svc_entgravity, 5);\n\t\t\t\t\t\tClientReliableWrite_Float(sp, curgrav/movevars.gravity);\t//lie to the client in a cunning way\n\t\t\t\t\t}\n\t\t\t\t\thost_client->entgravity = curgrav;\n\t\t\t\t}\n\n\t\t\t\tif (host_client->maxspeed != curspeed)\n\t\t\t\t{\t//MSVC can really suck at times (optimiser bug)\n\t\t\t\t\tif (ISQWCLIENT(host_client))\n\t\t\t\t\t{\n\t\t\t\t\t\tif (host_client->controller)\n\t\t\t\t\t\t{\t//this is a slave client.\n\t\t\t\t\t\t\t//find the right number and send.\n\t\t\t\t\t\t\tint pnum = 0;\n\t\t\t\t\t\t\tclient_t *sp;\n\t\t\t\t\t\t\tfor (sp = host_client->controller; sp; sp = sp->controlled)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tif (sp == host_client)\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\tpnum++;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tsp = host_client->controller;\n\n\t\t\t\t\t\t\tClientReliableWrite_Begin (sp, svcfte_choosesplitclient, 7);\n\t\t\t\t\t\t\tClientReliableWrite_Byte (sp, pnum);\n\t\t\t\t\t\t\tClientReliableWrite_Byte (sp, svc_maxspeed);\n\t\t\t\t\t\t\tClientReliableWrite_Float(sp, curspeed);\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tClientReliableWrite_Begin(host_client, svc_maxspeed, 5);\n\t\t\t\t\t\t\tClientReliableWrite_Float(host_client, curspeed);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\thost_client->maxspeed = curspeed;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\twhile (host_client->infosync.numkeys)\n\t\t{\n\t\t\tif (!SV_SyncInfoBuf(host_client))\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n#ifdef MVD_RECORDING\n\tif (sv.mvdrecording && demo.recorder.infosync.numkeys)\n\t{\n\t\twhile (demo.recorder.infosync.numkeys)\n\t\t{\n\t\t\tif (!SV_SyncInfoBuf(&demo.recorder))\n\t\t\t\tbreak;\n\t\t}\n\t}\n#endif\n\n\tif (sv.reliable_datagram.overflowed)\n\t{\n\t\tCon_Printf(\"WARNING: Reliable datagram overflowed\\n\");\n\t\tSZ_Clear (&sv.reliable_datagram);\n\t}\n\n\tif (sv.datagram.overflowed)\n\t\tSZ_Clear (&sv.datagram);\n\n#ifdef NQPROT\n\tif (sv.nqdatagram.overflowed)\n\t\tSZ_Clear (&sv.nqdatagram);\n#endif\n\n\tSV_FlushBroadcasts();\n}\n\n//#ifdef _MSC_VER\n//#pragma optimize( \"\", off )\n//#endif\n\n\n//a single userinfo value was changed.\n//*bothcolours sends out both topcolor and bottomcolor, with a single svc_updatecolors in nq\nstatic void SV_SendUserinfoChange(client_t *to, client_t *about, qboolean isbasic, const char *key, const char *newval)\n{\n\tint playernum = about - svs.clients;\n\n\tif (playernum > to->max_net_clients)\n\t\treturn;\n\n\tif (!newval)\n\t\tnewval = InfoBuf_ValueForKey(&about->userinfo, key);\n\n\tif (ISQWCLIENT(to))\n\t{\n\t\tif (isbasic || (to->fteprotocolextensions & PEXT_BIGUSERINFOS))\n\t\t{\n\t\t\tif (!strcmp(key, \"*bothcolours\"))\n\t\t\t{\t//hack to shorten sending vanilla nq colour updates\n\t\t\t\tnewval = InfoBuf_ValueForKey(&about->userinfo, key=\"topcolor\");\n\t\t\t\tClientReliableWrite_Begin(to, svc_setinfo, 4+strlen(key)+strlen(newval));\n\t\t\t\tClientReliableWrite_Byte(to, playernum);\n\t\t\t\tClientReliableWrite_String(to, key);\n\t\t\t\tClientReliableWrite_String(to, InfoBuf_ValueForKey(&about->userinfo, \"topcolor\"));\n\t\t\t\t\n\t\t\t\tnewval = InfoBuf_ValueForKey(&about->userinfo, key = \"bottomcolor\");\n\t\t\t}\n\n\t\t\tClientReliableWrite_Begin(to, svc_setinfo, 4+strlen(key)+strlen(newval));\n\t\t\tClientReliableWrite_Byte(to, playernum);\n\t\t\tClientReliableWrite_String(to, key);\n\t\t\tClientReliableWrite_String(to, newval);\n\t\t}\n\t}\n#ifdef NQPROT\n\telse if (ISNQCLIENT(to))\n\t{\n\t\tif (to->fteprotocolextensions2 & PEXT2_PREDINFO)\n\t\t{\t//this client has an understanding of userinfo, using it instead of svc_updatename+svc_updatecolors.\n\t\t\tchar quotedkey[1024];\n\t\t\tchar quotedval[8192];\n\t\t\tchar *s;\n\t\t\tif (!strcmp(key, \"*bothcolours\"))\n\t\t\t{\t//hack to shorten sending vanilla nq colour updates\n\t\t\t\tnewval = InfoBuf_ValueForKey(&about->userinfo, key=\"bottomcolor\");\n\t\t\t\ts = va(\"//ui %i %s %s\\n\", playernum, COM_QuotedString(key, quotedkey, sizeof(quotedkey), false), COM_QuotedString(newval, quotedval, sizeof(quotedval), false));\n\t\t\t\tClientReliableWrite_Begin(to, svc_stufftext, 2+strlen(s));\n\t\t\t\tClientReliableWrite_String(to, s);\n\n\t\t\t\tnewval = InfoBuf_ValueForKey(&about->userinfo, key=\"bottomcolor\");\n\t\t\t}\n\t\t\ts = va(\"//ui %i %s %s\\n\", playernum, COM_QuotedString(key, quotedkey, sizeof(quotedkey), false), COM_QuotedString(newval, quotedval, sizeof(quotedval), false));\n\t\t\tClientReliableWrite_Begin(to, svc_stufftext, 2+strlen(s));\n\t\t\tClientReliableWrite_String(to, s);\n\t\t}\n\t\telse\n\t\t{\t//legacy client.\n\t\t\tif (!strcmp(key, \"*spectator\"))\n\t\t\t{\t//nq does not support spectators, mods tend to use frags=-999 or -99 instead.\n\t\t\t\t//yes, this breaks things.\n\t\t\t\tClientReliableWrite_Begin(to, svc_updatefrags, 4);\n\t\t\t\tClientReliableWrite_Byte(to, playernum);\n\t\t\t\tif (atoi(newval) == 1)\n\t\t\t\t\tClientReliableWrite_Short(to, -999);\n\t\t\t\telse\n\t\t\t\t\tClientReliableWrite_Short(to, about->old_frags);\t//restore their true frag count\n\t\t\t}\n\t\t\telse if (!strcmp(key, \"name\"))\n\t\t\t{\n\t\t\t\tClientReliableWrite_Begin(to, svc_updatename, 3+strlen(newval));\n\t\t\t\tClientReliableWrite_Byte(to, playernum);\n\t\t\t\tClientReliableWrite_String(to, newval);\n\t\t\t}\n\t\t\telse if (!strcmp(key, \"topcolor\") || !strcmp(key, \"bottomcolor\") || !strcmp(key, \"*bothcolours\"))\n\t\t\t{\t//due to these being combined, nq players get double colour change notifications...\n\t\t\t\tint tc = atoi(InfoBuf_ValueForKey(&about->userinfo, \"topcolor\"));\n\t\t\t\tint bc = atoi(InfoBuf_ValueForKey(&about->userinfo, \"bottomcolor\"));\n\t\t\t\tif (tc < 0 || tc > 13)\n\t\t\t\t\ttc = 0;\n\t\t\t\tif (bc < 0 || bc > 13)\n\t\t\t\t\tbc = 0;\n\t\t\t\tClientReliableWrite_Begin(to, svc_updatecolors, 3);\n\t\t\t\tClientReliableWrite_Byte(to, playernum);\n\t\t\t\tClientReliableWrite_Byte(to, 16*tc + bc);\n\t\t\t}\n\t\t}\n\t}\n#endif\n}\nvoid SV_BroadcastUserinfoChange(client_t *about, qboolean isbasic, const char *key, const char *newval)\n{\n\tclient_t *client;\n\tint j;\n\tif (!newval)\n\t\tnewval = InfoBuf_ValueForKey(&about->userinfo, key);\n\tfor (j = 0; j < svs.allocated_client_slots; j++)\n\t{\n\t\tclient = svs.clients+j;\n\t\tif (client->state < cs_connected)\n\t\t\tcontinue;\t// reliables go to all connected or spawned\n\t\tif (client->controller)\n\t\t\tcontinue;\t//splitscreen\n\n\t\tif (client->protocol == SCP_BAD)\n\t\t\tcontinue;\t//botclient\n\n\t\tSV_SendUserinfoChange(client, about, isbasic, key, newval);\n\t}\n\n#ifdef MVD_RECORDING\n\tif (sv.mvdrecording && (isbasic || (demo.recorder.fteprotocolextensions & PEXT_BIGUSERINFOS)))\n\t{\n\t\tsizebuf_t *msg = MVDWrite_Begin (dem_all, 0, strlen(key)+strlen(newval)+4);\n\t\tMSG_WriteByte (msg, svc_setinfo);\n\t\tMSG_WriteByte (msg, about - svs.clients);\n\t\tMSG_WriteString (msg, key);\n\t\tMSG_WriteString (msg, newval);\n\t}\n#endif\n}\n\n/*\n=======================\nSV_SendClientMessages\n=======================\n*/\nvoid SV_SendClientMessages (void)\n{\n\tint\t\t\ti, j;\n\tclient_t\t*c;\n\tint sentbytes, fnum;\n#ifdef NQPROT\n\tfloat pt = sv.paused?realtime:sv.world.physicstime;\n#endif\n\n#ifdef NEWSPEEDCHEATPROT\n\tstatic unsigned int lasttime;\n\tunsigned int curtime = Sys_Milliseconds();\n\tunsigned int msecs = curtime - lasttime;\n\tlasttime = curtime;\n#endif\n\n#ifdef Q3SERVER\n\tif (svs.gametype == GT_QUAKE3)\n\t{\n\t\tfor (i=0, c = svs.clients ; i<svs.allocated_client_slots ; i++, c++)\n\t\t{\n\t\t\tif (c->state < cs_connected)\n\t\t\t\tcontinue;\n\n\t\t\tif (c->drop)\n\t\t\t{\n\t\t\t\tSV_DropClient(c);\n\t\t\t\tc->drop = false;\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (c->protocol == SCP_BAD)\t//this is a bot.\n\t\t\t{\n\t\t\t\tSZ_Clear (&c->netchan.message);\n\t\t\t\tSZ_Clear (&c->datagram);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (c->lastoutgoingphysicstime == pt)\n\t\t\t\tcontinue;\n\t\t\tc->lastoutgoingphysicstime = pt;\n\n\t\t\tq3->sv.SendMessage(c);\n\t\t}\n\t\treturn;\n\t}\n#endif\n\n// update frags, names, etc\n\tSV_UpdateToReliableMessages ();\n\n// build individual updates\n\tfor (i=0, c = svs.clients ; i<svs.allocated_client_slots ; i++, c++)\n\t{\n\t\tif (c->state < cs_loadzombie)\n\t\t\tcontinue;\n\n\t\tif (c->drop)\n\t\t{\n\t\t\tSV_DropClient(c);\n\t\t\tc->drop = false;\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (c->state == cs_loadzombie)\n\t\t{\t//not yet present.\n\t\t\tc->netchan.message.cursize = 0;\n\t\t\tc->datagram.cursize = 0;\n\t\t\tcontinue;\n\t\t}\n\n#ifdef SVCHAT\n\t\tSV_ChatThink(c);\n#endif\n\n#ifdef NEWSPEEDCHEATPROT\n\t\t//allow the client more time for client movement.\n\t\t//if they're running too slowly, FORCE them to run\n\t\t//this little check is to guard against people using msecs=0 to hover in mid-air. also keeps players animating/moving/etc when timing\n\t\tc->msecs += msecs;\n\t\twhile (c->state >= cs_spawned && c->msecs > 1000)\n\t\t{\n\t\t\tif (c->msecs > 1200)\n\t\t\t\tc->msecs = 1200;\n\t\t\tif (c->isindependant && !sv.paused)\n\t\t\t{\n\t\t\t\tunsigned int stepmsec;\n\t\t\t\tusercmd_t cmd;\n\t\t\t\tmemset(&cmd, 0, sizeof(cmd));\n\t\t\t\thost_client = c;\n\t\t\t\tsv_player = c->edict;\n\t\t\t\tSV_PreRunCmd();\n\t\t\t\tstepmsec = 13;\n\t\t\t\tcmd.msec = stepmsec;\n\t\t\t\tc->hoverms += cmd.msec;\t//sv_showpredloss shows this later when we do get the next packet, instead of potentially spamming every frame while they lag.\n\t\t\t\tVectorCopy(c->lastcmd.angles, cmd.angles);\n\t\t\t\tcmd.buttons = c->lastcmd.buttons;\n\t\t\t\tSV_RunCmd (&cmd, true);\n\t\t\t\tSV_PostRunCmd();\n\t\t\t\tc->lastruncmd = (unsigned int)(sv.time*1000)-c->msecs;\n\t\t\t\tif (stepmsec > c->msecs)\n\t\t\t\t\tc->msecs = 0;\n\t\t\t\telse\n\t\t\t\t\tc->msecs -= stepmsec;\n\t\t\t\tif (c->msecs > 2000)\n\t\t\t\t\tc->msecs = 2000;\t//assume debugger or system suspend/hibernate\n\t\t\t\thost_client = NULL;\n\t\t\t\tsv_player = NULL;\n\t\t\t}\n\t\t\telse\n\t\t\t\tc->msecs = 500;\t//for switching between.\n\t\t}\n#endif\n\n#ifdef Q3SERVER\n\t\tif (ISQ3CLIENT(c))\n\t\t{\t//q3 protocols bypass backbuffering and pretty much everything else\n\t\t\tq3->sv.SendMessage(c);\n\t\t\tcontinue;\n\t\t}\n#endif\n\n\t\t// check to see if we have a backbuf to stick in the reliable\n\t\tif (c->num_backbuf)\n\t\t{\n\t\t\t// will it fit?\n\t\t\tif (c->netchan.message.cursize + c->backbuf_size[0] <=\n\t\t\t\tc->netchan.message.maxsize)\n\t\t\t{\n\n\t\t\t\tCon_DPrintf(\"%s: backbuf %d bytes\\n\",\n\t\t\t\t\tc->name, c->backbuf_size[0]);\n\n\t\t\t\t// it'll fit\n\t\t\t\tSZ_Write(&c->netchan.message, c->backbuf_data[0],\n\t\t\t\t\tc->backbuf_size[0]);\n\n\t\t\t\t//move along, move along\n\t\t\t\tfor (j = 1; j < c->num_backbuf; j++)\n\t\t\t\t{\n\t\t\t\t\tmemcpy(c->backbuf_data[j - 1], c->backbuf_data[j],\n\t\t\t\t\t\tc->backbuf_size[j]);\n\t\t\t\t\tc->backbuf_size[j - 1] = c->backbuf_size[j];\n\t\t\t\t}\n\n\t\t\t\tc->num_backbuf--;\n\t\t\t\tif (c->num_backbuf)\n\t\t\t\t{\n\t\t\t\t\tmemset(&c->backbuf, 0, sizeof(c->backbuf));\n\t\t\t\t\tc->backbuf.prim = c->netchan.message.prim;\n\t\t\t\t\tc->backbuf.data = c->backbuf_data[c->num_backbuf - 1];\n\t\t\t\t\tc->backbuf.cursize = c->backbuf_size[c->num_backbuf - 1];\n\t\t\t\t\tc->backbuf.maxsize = min(c->netchan.message.maxsize, sizeof(c->backbuf_data[c->num_backbuf-1]));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (c->protocol == SCP_BAD)\n\t\t{\n\t\t\tSZ_Clear (&c->netchan.message);\n\t\t\tSZ_Clear (&c->datagram);\n\t\t\tc->num_backbuf = 0;\n\t\t\tif (c->edict)\n\t\t\t\tc->edict->v->fixangle = FIXANGLE_NO;\n\t\t\tcontinue;\n\t\t}\n\n\t\t// if the reliable message overflowed,\n\t\t// drop the client\n\t\tif (c->netchan.message.overflowed)\n\t\t{\n\t\t\tSZ_Clear (&c->netchan.message);\n\t\t\tSZ_Clear (&c->datagram);\n\t\t\tSV_BroadcastPrintf (PRINT_HIGH, \"%s overflowed\\n\", c->name);\n\t\t\tCon_Printf (\"WARNING: reliable overflow for %s\\n\",c->name);\n\t\t\tc->send_message = true;\n\t\t\tc->netchan.cleartime = 0;\t// don't choke this message\n\t\t\tSV_DropClient (c);\n\t\t\tcontinue;\n\t\t}\n\n#ifdef NQPROT\n\t\t// only send messages if the client has sent one\n\t\t// and the bandwidth is not choked\n\t\tif (ISNQCLIENT(c))\n\t\t{\n\t\t\t//tread carefully with NQ:\n\t\t\t//while loading models etc, NQ will error out if it receives anything that it wasn't expecting.\n\t\t\t//we should still send unreliable nops whenever we want as a keepalive (and we may need to in order to wake up the client).\n\t\t\t//other unreliables are disallowed when connecting, due to sync issues.\n\t\t\t//reliables may be sent only if some other code has said that its okay (to avoid stray name changes killing clients).\n\t\t\tif (c->state == cs_connected)\n\t\t\t{\n\t\t\t\tif (c->nextservertimeupdate > pt + 6)\n\t\t\t\t\tc->nextservertimeupdate = 0;\n\n\t\t\t\tc->netchan.cleartime = realtime - 100;\n\t\t\t\tif (c->netchan.nqunreliableonly == 1)\n\t\t\t\t\tc->netchan.nqunreliableonly = !c->send_message;\n\t\t\t\tc->datagram.cursize = 0;\n\t\t\t\tif (!c->send_message && c->nextservertimeupdate < pt)\n\t\t\t\t{\n\t\t\t\t\tif (c->nextservertimeupdate)\n\t\t\t\t\t\tMSG_WriteByte(&c->datagram, svc_nop);\n\t\t\t\t\tc->nextservertimeupdate = pt+5;\n\t\t\t\t}\n\t\t\t\tc->send_message = true;\n\t\t\t\t//we can still send an outgoing packet if something set send_message. This should really only be svnq_new_f and friends.\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\textern cvar_t sv_nqplayerphysics;\n\t\t\t\tif (c->nextservertimeupdate > pt + 0.1)\n\t\t\t\t\tc->nextservertimeupdate = 0;\n\n\t\t\t\tc->netchan.nqunreliableonly = false;\n\t\t\t\tc->send_message = false;\n\t\t\t\t//nq sends one packet only for each server physics frame\n\t\t\t\tif (sv_mintic.value || sv_nqplayerphysics.ival)\t//(nqplayerphysics forces 72hz when mintic )\n\t\t\t\t{\t//explicit packet/tick rate. don't spam faster/slower, clients don't like that too much.\n\t\t\t\t\tif (c->nextservertimeupdate != pt && c->state >= cs_connected)\n\t\t\t\t\t{\n\t\t\t\t\t\tc->send_message = true;\n\t\t\t\t\t\tc->nextservertimeupdate = pt;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tif (c->nextservertimeupdate < pt && c->state >= cs_connected)\n\t\t\t\t\t{\n\t\t\t\t\t\tc->send_message = true;\n\t\t\t\t\t\tc->nextservertimeupdate = pt + 1.0/77;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t//qw servers will set send_message on packet reception.\n#endif\n\n\t\tSV_ProcessSendFlags(c);\n\n\t\tif (!c->send_message)\n\t\t\tcontinue;\n\t\tc->send_message = false;\t// try putting this after choke?\n\n\t\tif (c->controller)\n\t\t\tcontinue;\t/*shouldn't have been set*/\n\n\t\tif (!sv.paused && !Netchan_CanPacket (&c->netchan, SV_RateForClient(c)))\n\t\t{\n\t\t\tc->chokecount++;\n\t\t\tc->waschoked = true;\n\t\t\tcontinue;\t\t// bandwidth choke\n\t\t}\n\t\tc->waschoked = false;\n\n\t\tif (sv.time > c->ratetime + 1)\n\t\t{\n\t\t\tc->inrate = c->netchan.bytesin / (sv.time - c->ratetime);\n\t\t\tc->outrate = c->netchan.bytesout / (sv.time - c->ratetime);\n\t\t\tc->netchan.bytesin = 0;\n\t\t\tc->netchan.bytesout = 0;\n\t\t\tc->ratetime = sv.time;\n\t\t}\n\n\t\tSV_ReplaceEntityFrame(c, c->netchan.outgoing_sequence);\n\t\tSV_SendClientPrespawnInfo(c);\n\t\tif (c->state == cs_spawned)\n\t\t\tSV_SendClientDatagram (c);\n\t\telse\n\t\t{\n#ifdef NQPROT\n\t\t\tSV_DarkPlacesDownloadChunk(c, &c->datagram);\n#endif\n\t\t\tfnum = c->netchan.outgoing_sequence;\n\t\t\tsentbytes = Netchan_Transmit (&c->netchan, c->datagram.cursize, c->datagram.data, SV_RateForClient(c));\t// just update reliable\n\t\t\tif (ISQWCLIENT(c) || ISNQCLIENT(c))\n\t\t\t\tc->frameunion.frames[fnum & UPDATE_MASK].packetsizeout += sentbytes;\n\t\t\tc->datagram.cursize = 0;\n\t\t}\n\t\tc->lastoutgoingphysicstime = sv.world.physicstime;\n\n\t\tif (c->netchan.fatal_error)\n\t\t\tc->drop = true;\n\t}\n#ifdef MVD_RECORDING\n\tif (sv.mvdrecording)\n\t\tSV_ProcessSendFlags(&demo.recorder);\n#endif\n\tSV_CleanupEnts();\n}\n\n//#ifdef _MSC_VER\n//#pragma optimize( \"\", on )\n//#endif\n\n#ifdef MVD_RECORDING\nvoid SV_WriteMVDMessage (sizebuf_t *msg, int type, int to, float time);\nvoid SV_MVD_CheckReverse(void);\n\nvoid DemoWriteQTVTimePad(int msecs);\n#define Max(a, b) ((a>b)?a:b)\nvoid SV_SendMVDMessage(void)\n{\n\tint\t\t\ti, j, m, cls = 0;\n\tclient_t\t*c;\n\tqbyte\t\tbuf[MAX_DATAGRAM];\n\tsizebuf_t\tmsg;\n\tint\t\tstatsi[MAX_CL_STATS];\n\tfloat\tstatsf[MAX_CL_STATS];\n\tconst char\t*statss[MAX_CL_STATS];\n\tfloat\t\tmin_fps;\n\textern\t\tcvar_t sv_demofps;\n\textern\t\tcvar_t sv_demoPings;\n//\textern\t\tcvar_t\tsv_demoMaxSize;\n\tsizebuf_t *dmsg;\n\n\tSV_MVD_CheckReverse();\n\n\tif (!sv.mvdrecording)\n\t\treturn;\n\n\tif (sv_demoPings.value)\n\t{\n\t\tif (sv.time - demo.pingtime > sv_demoPings.value)\n\t\t{\n\t\t\tSV_MVDPings();\n\t\t\tdemo.pingtime = sv.time;\n\t\t}\n\t}\n\n\n\tif (sv_demofps.value <= 1)\n\t\tmin_fps = 30.0;\n\telse\n\t\tmin_fps = sv_demofps.value;\n\n\tmin_fps = Max(4, min_fps);\n\tif (sv.time - demo.time < 1.0/min_fps)\n\t\treturn;\n\n\tfor (i=0, c = svs.clients ; i<svs.allocated_client_slots && i < 32; i++, c++)\n\t{\n\t\tif (c->state != cs_spawned)\n\t\t\tcontinue;\t// datagrams only go to spawned\n\n\t\tcls |= 1 << i;\n\t}\n\n\tif (!cls)\n\t{\n\t\tSZ_Clear (&demo.datagram);\n\t\tDemoWriteQTVTimePad((int)((sv.time - demo.time)*1000));\n\t\tDestFlush(false);\n\t\tdemo.time = sv.time;\n\t\treturn;\n\t}\n\n\tmsg.data = buf;\n\tmsg.maxsize = sizeof(buf);\n\tmsg.cursize = 0;\n\tmsg.allowoverflow = true;\n\tmsg.overflowed = false;\n\n\tfor (i=0, c = svs.clients ; i<svs.allocated_client_slots && i < 32; i++, c++)\n\t{\n\t\tif (c->state != cs_spawned)\n\t\t\tcontinue;\t// datagrams only go to spawned\n\n\t\tif (c->spectator)\n\t\t\tcontinue;\n\n\t\t/*figure out what the stat values should be*/\n\t\tm = SV_CalcClientStats(c, statsi, statsf, statss);\n\t\tif (demo.recorder.fteprotocolextensions & (PEXT_HEXEN2|PEXT_CSQC))\n\t\t\tm = min(m,MAX_CL_STATS);\n\t\telse\n\t\t\tm = min(m,MAX_QW_STATS);\n\n\t\t//FIXME we should do something about the packet overhead here. each MVDWrite_Begin is a separate packet!\n\n\t\tfor (j=0 ; j<m ; j++)\n\t\t{\n\t\t\tif (demo.recorder.fteprotocolextensions & PEXT_CSQC)\n\t\t\t{\n\t\t\t\tif (statss[j] || demo.statss[i][j])\n\t\t\t\tif (strcmp(statss[j]?statss[j]:\"\", demo.statss[i][j]?demo.statss[i][j]:\"\"))\n\t\t\t\t{\n\t\t\t\t\tsizebuf_t *msg = MVDWrite_Begin(dem_stats, i, 3+strlen(statss[j]));\n\n\t\t\t\t\tif (demo.statss[i][j])\n\t\t\t\t\t\tZ_Free(demo.statss[i][j]);\n\t\t\t\t\tif (statss[j] && *statss[j])\n\t\t\t\t\t\tdemo.statss[i][j] = Z_StrDup(statss[j]);\n\t\t\t\t\telse\n\t\t\t\t\t\tdemo.statss[i][j] = NULL;\n\n\t\t\t\t\tMSG_WriteByte(msg, svcfte_updatestatstring);\n\t\t\t\t\tMSG_WriteByte(msg, j);\n\t\t\t\t\tMSG_WriteString(msg, statss[j]);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (statsf[j])\n\t\t\t{\n\t\t\t\tif (demo.recorder.fteprotocolextensions & PEXT_CSQC)\n\t\t\t\t{\n\t\t\t\t\tif (statsf[j] != demo.statsf[i][j])\n\t\t\t\t\t{\n\t\t\t\t\t\tif (statsf[j] - (float)(int)statsf[j] == 0 && statsf[j] >= 0 && statsf[j] <= 255)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tdmsg = MVDWrite_Begin(dem_stats, i, 3);\n\t\t\t\t\t\t\tMSG_WriteByte(dmsg, svcqw_updatestatbyte);\n\t\t\t\t\t\t\tMSG_WriteByte(dmsg, j);\n\t\t\t\t\t\t\tMSG_WriteByte(dmsg, statsf[j]);\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tdmsg = MVDWrite_Begin(dem_stats, i, 6);\n\t\t\t\t\t\t\tMSG_WriteByte(dmsg, svcfte_updatestatfloat);\n\t\t\t\t\t\t\tMSG_WriteByte(dmsg, j);\n\t\t\t\t\t\t\tMSG_WriteFloat(dmsg, statsf[j]);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tdemo.statsf[i][j] = statsf[j];\n\t\t\t\t\t\t/*make sure statsf is correct*/\n\t\t\t\t\t\tdemo.statsi[i][j] = statsf[j];\n\t\t\t\t\t}\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tstatsi[j] = statsf[j];\n\t\t\t}\n\n\t\t\tif (statsi[j] != demo.statsi[i][j])\n\t\t\t{\n\t\t\t\tdemo.statsi[i][j] = statsi[j];\n\t\t\t\tdemo.statsf[i][j] = statsi[j];\n\t\t\t\tif (statsi[j] >=0 && statsi[j] <= 255)\n\t\t\t\t{\n\t\t\t\t\tdmsg = MVDWrite_Begin(dem_stats, i, 3);\n\t\t\t\t\tMSG_WriteByte(dmsg, svcqw_updatestatbyte);\n\t\t\t\t\tMSG_WriteByte(dmsg, j);\n\t\t\t\t\tMSG_WriteByte(dmsg, statsi[j]);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tdmsg = MVDWrite_Begin(dem_stats, i, 6);\n\t\t\t\t\tMSG_WriteByte(dmsg, svcqw_updatestatlong);\n\t\t\t\t\tMSG_WriteByte(dmsg, j);\n\t\t\t\t\tMSG_WriteLong(dmsg, statsi[j]);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// send over all the objects that are in the PVS\n\t// this will include clients, a packetentities, and\n\t// possibly a nails update\n\tmsg.cursize = 0;\n\tmsg.prim = demo.recorder.netchan.netprim;\n\n\t// copy the accumulated multicast datagram\n\t// for this client out to the message\n\tif (demo.datagram.cursize && sv.mvdrecording)\n\t{\n\t\tdmsg = MVDWrite_Begin(dem_all, 0, demo.datagram.cursize);\n\t\tSZ_Write (dmsg, demo.datagram.data, demo.datagram.cursize);\n\t\tSZ_Clear (&demo.datagram);\n\t}\n\n\twhile (demo.lastwritten < demo.parsecount-1 && sv.mvdrecording)\n\t{\n\t\tif (!SV_MVDWritePackets(1))\n\t\t\tbreak;\n\t}\n\n\tif (demo.resetdeltas)\n\t{\n\t\tdemo.resetdeltas = false;\n\t\tdemo.recorder.delta_sequence = -1;\n\t}\n\telse\n\t\tdemo.recorder.delta_sequence = demo.recorder.netchan.incoming_sequence&255;\n\tdemo.recorder.netchan.incoming_sequence++;\n\tdemo.frames[demo.parsecount&DEMO_FRAMES_MASK].time = demo.time = sv.time;\n\n\tif (sv.mvdrecording)\n\t{\n\t\tSV_WriteEntitiesToClient (&demo.recorder, &msg, true);\n\t\tSV_WriteMVDMessage(&msg, dem_all, 0, sv.time);\n//\t\tdmsg = MVDWrite_Begin(dem_all, 0, msg.cursize);\n//\t\tSZ_Write (dmsg, msg.data, msg.cursize);\n\t}\n\n\tdemo.parsecount++;\n\n//\tMVDSetMsgBuf(demo.dbuf,&demo.frames[demo.parsecount&DEMO_FRAMES_MASK].buf);\n}\n#endif\n\n\n/*\n=======================\nSV_SendMessagesToAll\n\nFIXME: does this sequence right?\n=======================\n*/\nvoid SV_SendMessagesToAll (void)\n{\n\tint\t\t\ti;\n\tclient_t\t*c;\n\n\tfor (i=0, c = svs.clients ; i<svs.allocated_client_slots ; i++, c++)\n\t\tif (c->state)\t\t// FIXME: should this only send to active?\n\t\t\tc->send_message = true;\n\n\tSV_SendClientMessages ();\n}\n\n#endif\n"
  },
  {
    "path": "engine/server/sv_sql.c",
    "content": "#include \"quakedef.h\"\r\n\r\n//SQLITE:\r\n//should probably be compiled with -DSQLITE_OMIT_ATTACH -DSQLITE_OMIT_LOAD_EXTENSION which would mean we don't need to override authorisation.\r\n\r\n#ifdef SQL\r\n#include \"sv_sql.h\"\r\n\r\n#ifdef USE_MYSQL\r\nstatic void (VARGS *qmysql_library_end)(void);\r\nstatic int (VARGS *qmysql_library_init)(int argc, char **argv, char **groups);\r\n\r\nstatic my_ulonglong (VARGS *qmysql_affected_rows)(MYSQL *mysql);\r\nstatic void (VARGS *qmysql_close)(MYSQL *sock);\r\nstatic void (VARGS *qmysql_data_seek)(MYSQL_RES *result, my_ulonglong offset);\r\nstatic unsigned int (VARGS *qmysql_errno)(MYSQL *mysql);\r\nstatic const char *(VARGS *qmysql_error)(MYSQL *mysql);\r\nstatic MYSQL_FIELD *(VARGS *qmysql_fetch_field_direct)(MYSQL_RES *res, unsigned int fieldnr);\r\nstatic MYSQL_ROW (VARGS *qmysql_fetch_row)(MYSQL_RES *result);\r\nstatic unsigned long *(VARGS *qmysql_fetch_lengths)(MYSQL_RES *result);\r\nstatic unsigned int (VARGS *qmysql_field_count)(MYSQL *mysql);\r\nstatic void (VARGS *qmysql_free_result)(MYSQL_RES *result);\r\nstatic const char *(VARGS *qmysql_get_client_info)(void);\r\nstatic MYSQL *(VARGS *qmysql_init)(MYSQL *mysql);\r\nstatic MYSQL_RES *(VARGS *qmysql_store_result)(MYSQL *mysql);\r\nstatic unsigned int (VARGS *qmysql_num_fields)(MYSQL_RES *res);\r\nstatic my_ulonglong (VARGS *qmysql_num_rows)(MYSQL_RES *res);\r\nstatic int (VARGS *qmysql_options)(MYSQL *mysql, enum mysql_option option, const char *arg);\r\nstatic int (VARGS *qmysql_query)(MYSQL *mysql, const char *q);\r\nstatic MYSQL *(VARGS *qmysql_real_connect)(MYSQL *mysql, const char *host, const char *user, const char *passwd, const char *db, unsigned int port, const char *unix_socket, unsigned long clientflag);\r\nstatic unsigned long (VARGS *qmysql_real_escape_string)(MYSQL *mysql, char *to, const char *from, unsigned long length);\r\nstatic void (VARGS *qmysql_thread_end)(void);\r\nstatic my_bool (VARGS *qmysql_thread_init)(void);\r\nstatic unsigned int (VARGS *qmysql_thread_safe)(void);\r\n\r\nstatic dllfunction_t mysqlfuncs[] =\r\n{\r\n\t{(void*)&qmysql_library_end, \"mysql_server_end\"}, /* written as a define alias in mysql.h */\r\n\t{(void*)&qmysql_library_init, \"mysql_server_init\"}, /* written as a define alias in mysql.h */\r\n\t{(void*)&qmysql_affected_rows, \"mysql_affected_rows\"},\r\n\t{(void*)&qmysql_close, \"mysql_close\"},\r\n\t{(void*)&qmysql_data_seek, \"mysql_data_seek\"},\r\n\t{(void*)&qmysql_errno, \"mysql_errno\"},\r\n\t{(void*)&qmysql_error, \"mysql_error\"},\r\n\t{(void*)&qmysql_fetch_field_direct, \"mysql_fetch_field_direct\"},\r\n\t{(void*)&qmysql_fetch_row, \"mysql_fetch_row\"},\r\n\t{(void*)&qmysql_fetch_lengths, \"mysql_fetch_lengths\"},\r\n\t{(void*)&qmysql_field_count, \"mysql_field_count\"},\r\n\t{(void*)&qmysql_free_result, \"mysql_free_result\"},\r\n\t{(void*)&qmysql_get_client_info, \"mysql_get_client_info\"},\r\n\t{(void*)&qmysql_init, \"mysql_init\"},\r\n\t{(void*)&qmysql_store_result, \"mysql_store_result\"},\r\n\t{(void*)&qmysql_num_fields, \"mysql_num_fields\"},\r\n\t{(void*)&qmysql_num_rows, \"mysql_num_rows\"},\r\n\t{(void*)&qmysql_options, \"mysql_options\"},\r\n\t{(void*)&qmysql_query, \"mysql_query\"},\r\n\t{(void*)&qmysql_real_connect, \"mysql_real_connect\"},\r\n\t{(void*)&qmysql_real_escape_string, \"mysql_real_escape_string\"},\r\n\t{(void*)&qmysql_thread_end, \"mysql_thread_end\"},\r\n\t{(void*)&qmysql_thread_init, \"mysql_thread_init\"},\r\n\t{(void*)&qmysql_thread_safe, \"mysql_thread_safe\"},\r\n\t{NULL}\r\n};\r\ndllhandle_t *mysqlhandle;\r\n#endif\r\n\r\n#ifdef USE_SQLITE\r\n#include \"sqlite3.h\"\r\nSQLITE_API int (QDECL *qsqlite3_open)(const char *zFilename, sqlite3 **ppDb);\r\nSQLITE_API const char *(QDECL *qsqlite3_libversion)(void);\r\nSQLITE_API int (QDECL *qsqlite3_set_authorizer)(sqlite3*, int (QDECL *xAuth)(void*,int,const char*,const char*,const char*,const char*), void *pUserData);\r\nSQLITE_API int (QDECL *qsqlite3_enable_load_extension)(sqlite3 *db, int onoff);\r\nSQLITE_API const char *(QDECL *qsqlite3_errmsg)(sqlite3 *db);\r\nSQLITE_API int (QDECL *qsqlite3_close)(sqlite3 *db);\r\n\r\nSQLITE_API int (QDECL *qsqlite3_prepare)(sqlite3 *db, const char *zSql, int nBytes, sqlite3_stmt **ppStmt, const char **pzTail);\r\nSQLITE_API int (QDECL *qsqlite3_column_count)(sqlite3_stmt *pStmt);\r\nSQLITE_API const char *(QDECL *qsqlite3_column_name)(sqlite3_stmt *pStmt, int N);\r\nSQLITE_API int (QDECL *qsqlite3_step)(sqlite3_stmt *pStmt);\r\nSQLITE_API const unsigned char *(QDECL *qsqlite3_column_text)(sqlite3_stmt *pStmt, int i);\r\nSQLITE_API int (QDECL *qsqlite3_column_bytes)(sqlite3_stmt*, int iCol);\r\nSQLITE_API int (QDECL *qsqlite3_finalize)(sqlite3_stmt *pStmt);\r\n\r\nstatic dllfunction_t sqlitefuncs[] =\r\n{\r\n\t{(void*)&qsqlite3_open,\t\t\t\t\t\t\"sqlite3_open\"}, /* written as a define alias in mysql.h */\r\n\t{(void*)&qsqlite3_libversion,\t\t\t\t\"sqlite3_libversion\"}, /* written as a define alias in mysql.h */\r\n\t{(void*)&qsqlite3_set_authorizer,\t\t\t\"sqlite3_set_authorizer\"},\r\n\t{(void*)&qsqlite3_enable_load_extension,\t\"sqlite3_enable_load_extension\"},\r\n\t{(void*)&qsqlite3_errmsg,\t\t\t\t\t\"sqlite3_errmsg\"},\r\n\t{(void*)&qsqlite3_close,\t\t\t\t\t\"sqlite3_close\"},\r\n\r\n\t{(void*)&qsqlite3_prepare,\t\t\t\t\t\"sqlite3_prepare\"},\r\n\t{(void*)&qsqlite3_column_count,\t\t\t\t\"sqlite3_column_count\"},\r\n\t{(void*)&qsqlite3_column_name,\t\t\t\t\"sqlite3_column_name\"},\r\n\t{(void*)&qsqlite3_step,\t\t\t\t\t\t\"sqlite3_step\"},\r\n\t{(void*)&qsqlite3_column_text,\t\t\t\t\"sqlite3_column_text\"},\r\n\t{(void*)&qsqlite3_column_bytes,\t\t\t\t\"sqlite3_column_bytes\"},\r\n\t{(void*)&qsqlite3_finalize,\t\t\t\t\t\"sqlite3_finalize\"},\r\n\t{NULL}\r\n};\r\ndllhandle_t *sqlitehandle;\r\n#endif\r\n\r\ncvar_t sql_driver = CVARF(\"sv_sql_driver\", \"\", CVAR_NOUNSAFEEXPAND);\r\ncvar_t sql_host = CVARF(\"sv_sql_host\", \"127.0.0.1\", CVAR_NOUNSAFEEXPAND);\r\ncvar_t sql_username = CVARF(\"sv_sql_username\", \"\", CVAR_NOUNSAFEEXPAND);\r\ncvar_t sql_password = CVARF(\"sv_sql_password\", \"\", CVAR_NOUNSAFEEXPAND);\r\ncvar_t sql_defaultdb = CVARF(\"sv_sql_defaultdb\", \"\", CVAR_NOUNSAFEEXPAND);\r\n\r\nvoid SQL_PushResult(sqlserver_t *server, queryresult_t *qres)\r\n{\r\n\tSys_LockMutex(server->resultlock);\r\n\tqres->next = NULL;\r\n\tif (!server->resultslast)\r\n\t\tserver->results = server->resultslast = qres;\r\n\telse\r\n\t\tserver->resultslast = server->resultslast->next = qres;\r\n\tSys_UnlockMutex(server->resultlock);\r\n}\r\n\r\nqueryresult_t *SQL_PullResult(sqlserver_t *server)\r\n{\r\n\tqueryresult_t *qres;\r\n\tSys_LockMutex(server->resultlock);\r\n\tqres = server->results;\r\n\tif (qres)\r\n\t{\r\n\t\tserver->results = qres->next;\r\n\t\tif (!server->results)\r\n\t\t\tserver->resultslast = NULL;\r\n\t}\r\n\tSys_UnlockMutex(server->resultlock);\r\n\r\n\treturn qres;\r\n}\r\n\r\n//called by main thread\r\nvoid SQL_PushRequest(sqlserver_t *server, queryrequest_t *qreq)\r\n{\r\n\tqreq->state = SR_PENDING;\r\n\tSys_LockConditional(server->requestcondv);\r\n\tqreq->nextqueue = NULL;\r\n\tif (!server->requestslast)\r\n\t\tserver->requestqueue = server->requestslast = qreq;\r\n\telse\r\n\t\tserver->requestslast = server->requestslast->nextqueue = qreq;\r\n\tSys_UnlockConditional(server->requestcondv);\r\n}\r\n\r\n//called by sql worker thread\r\nqueryrequest_t *SQL_PullRequest(sqlserver_t *server, qboolean lock)\r\n{\r\n\tqueryrequest_t *qreq;\r\n\tif (lock)\r\n\t\tSys_LockConditional(server->requestcondv);\r\n\tqreq = server->requestqueue;\r\n\tif (qreq)\r\n\t{\r\n\t\tserver->requestqueue = qreq->nextqueue;\r\n\t\tif (!server->requestqueue)\r\n\t\t\tserver->requestslast = NULL;\r\n\t}\r\n\tSys_UnlockConditional(server->requestcondv);\r\n\r\n\treturn qreq;\r\n}\r\n\r\nstruct\r\n{\r\n\tvoid *owner;\r\n\tsqlserver_t *handle;\r\n} *sqlservers;\r\nstatic int sqlservercount;\r\nstatic int sqlavailable;\r\nstatic int sqlinited;\r\n\r\n#ifdef USE_SQLITE\r\n//this is to try to sandbox sqlite so it can only edit the file its originally opened with.\r\nint QDECL mysqlite_authorizer(void *ctx, int action, const char *detail0, const char *detail1, const char *detail2, const char *detail3)\r\n{\r\n\tif (action == SQLITE_PRAGMA)\r\n\t{\r\n\t\tSys_Printf(\"SQL: Rejecting pragma \\\"%s\\\"\\n\", detail0);\r\n\t\treturn SQLITE_DENY;\r\n\t}\r\n\tif (action == SQLITE_ATTACH)\r\n\t{\r\n\t\tSys_Printf(\"SQL: Rejecting attach to \\\"%s\\\"\\n\", detail0);\r\n\t\treturn SQLITE_DENY;\r\n\t}\r\n\treturn SQLITE_OK;\r\n}\r\n#endif\r\n\r\nint sql_serverworker(void *sref)\r\n{\r\n\tsqlserver_t *server = (sqlserver_t *)sref;\r\n\tconst char *error = NULL;\r\n\tint i;\r\n\tqboolean needlock = false;\r\n\tqboolean allokay = true;\r\n#ifdef USE_MYSQL\r\n\tint tinit = -1;\r\n#endif\r\n\r\n\tswitch(server->driver)\r\n\t{\r\n#ifdef USE_MYSQL\r\n\tcase SQLDRV_MYSQL:\r\n\t\t{\r\n\t\t\tmy_bool reconnect = 1;\r\n\t\t\tif (!qmysql_thread_init)\r\n\t\t\t\terror = \"MYSQL library not available\";\r\n\t\t\telse if (tinit = qmysql_thread_init())\r\n\t\t\t\terror = \"MYSQL thread init failed\";\r\n\t\t\telse if (!(server->mysql = qmysql_init(NULL)))\r\n\t\t\t\terror = \"MYSQL init failed\";\r\n\t\t\telse if (qmysql_options(server->mysql, MYSQL_OPT_RECONNECT, &reconnect))\r\n\t\t\t\terror = \"MYSQL reconnect options set failed\";\r\n\t\t\telse\r\n\t\t\t{\t\r\n\t\t\t\tint port = 0;\r\n\t\t\t\tchar *colon;\r\n\r\n\t\t\t\tcolon = strchr(server->connectparams[0], ':');\r\n\t\t\t\tif (colon)\r\n\t\t\t\t{\r\n\t\t\t\t\t*colon = '\\0';\r\n\t\t\t\t\tport = atoi(colon + 1);\r\n\t\t\t\t}\r\n\r\n\t\t\t\tif (!(server->mysql = qmysql_real_connect(server->mysql, server->connectparams[0], server->connectparams[1], server->connectparams[2], server->connectparams[3], port, 0, 0)))\r\n\t\t\t\t\terror = \"MYSQL initial connect attempt failed\";\r\n\r\n\t\t\t\tif (colon)\r\n\t\t\t\t\t*colon = ':';\r\n\t\t\t}\r\n\t\t}\r\n\t\tbreak;\r\n#endif\r\n#ifdef USE_SQLITE\r\n\tcase SQLDRV_SQLITE:\r\n\t\tif (qsqlite3_open(server->connectparams[3], &server->sqlite))\r\n\t\t{\r\n\t\t\terror = qsqlite3_errmsg(server->sqlite);\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\t//disable extension loading, set up an authorizer hook.\r\n\t\t\tqsqlite3_enable_load_extension(server->sqlite, false);\r\n\t\t\tqsqlite3_set_authorizer(server->sqlite, mysqlite_authorizer, server);\r\n\t\t}\r\n\t\tbreak;\r\n#endif\r\n\tdefault:\r\n\t\terror = \"That driver is not enabled in this build.\";\r\n\t\tbreak;\r\n\t}\r\n\r\n\tif (error)\r\n\t\tallokay = false;\r\n\r\n\twhile (allokay)\r\n\t{\t\r\n\t\tSys_LockConditional(server->requestcondv);\r\n\t\tif (!server->requestqueue) // this is needed for thread startup and to catch any \"lost\" changes\r\n\t\t\tSys_ConditionWait(server->requestcondv);\r\n\t\tneedlock = false; // so we don't try to relock first round\r\n\r\n\t\twhile (1)\r\n\t\t{\r\n\t\t\tqueryrequest_t *qreq = NULL;\r\n\t\t\tqueryresult_t *qres;\r\n\t\t\tint columns = -1;\r\n\r\n\t\t\tif (!(qreq = SQL_PullRequest(server, needlock)))\r\n\t\t\t{\r\n\t\t\t\tif (!server->active)\r\n\t\t\t\t\tallokay = false;\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\r\n\t\t\t// pullrequest makes sure our condition is unlocked but we'll need\r\n\t\t\t// a lock next round\r\n\t\t\tneedlock = true;\r\n\r\n\t\t\tswitch(server->driver)\r\n\t\t\t{\r\n\t\t\tdefault:\r\n\t\t\t\terror = \"Bad database driver\";\r\n\t\t\t\tallokay = false;\r\n\t\t\t\tbreak;\r\n#ifdef USE_MYSQL\r\n\t\t\tcase SQLDRV_MYSQL:\r\n\t\t\t\t{\r\n\t\t\t\t\tvoid *res = NULL;\r\n\t\t\t\t\tint qesize = 0;\r\n\t\t\t\t\tint rows = -1;\r\n\t\t\t\t\tconst char *qerror = NULL;\r\n\t\t\t\t\t// perform the query and fill out the result structure\r\n\t\t\t\t\tif (qmysql_query(server->mysql, qreq->query))\r\n\t\t\t\t\t\tqerror = qmysql_error(server->mysql);\r\n\t\t\t\t\telse // query succeeded\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tres = qmysql_store_result(server->mysql);\r\n\t\t\t\t\t\tif (res) // result set returned\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\trows = qmysql_num_rows(res);\r\n\t\t\t\t\t\t\tcolumns = qmysql_num_fields(res);\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\telse if (qmysql_field_count(server->mysql) == 0) // no result set\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\trows = qmysql_affected_rows(server->mysql);\r\n\t\t\t\t\t\t\tif (rows < 0)\r\n\t\t\t\t\t\t\t\trows = 0;\r\n\t\t\t\t\t\t\tcolumns = 0;\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\telse // error\r\n\t\t\t\t\t\t\tqerror = qmysql_error(server->mysql);\r\n\t\t\t\t\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\tif (qerror)\r\n\t\t\t\t\t\tqesize = Q_strlen(qerror);\r\n\t\t\t\t\tqres = (queryresult_t *)ZF_Malloc(sizeof(queryresult_t) + qesize);\r\n\t\t\t\t\tif (qres)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tif (qerror)\r\n\t\t\t\t\t\t\tQ_strncpyz(qres->error, qerror, qesize);\r\n\t\t\t\t\t\tqres->result = res;\r\n\t\t\t\t\t\tqres->rows = rows;\r\n\t\t\t\t\t\tqres->columns = columns;\r\n\t\t\t\t\t\tqres->request = qreq;\r\n\t\t\t\t\t\tqres->eof = true; // store result has no more rows to read afterwards\r\n\r\n\t\t\t\t\t\tSQL_PushResult(server, qres);\r\n\t\t\t\t\t}\r\n\t\t\t\t\telse // we're screwed here so bomb out\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tserver->active = false;\r\n\t\t\t\t\t\terror = \"MALLOC ERROR! Unable to allocate query result!\";\r\n\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t\tbreak;\r\n#endif\r\n#ifdef USE_SQLITE\r\n\t\t\tcase SQLDRV_SQLITE:\r\n\t\t\t\t{\r\n\t\t\t\t\tint rc;\r\n\t\t\t\t\tsqlite3_stmt *pStmt;\r\n\t\t\t\t\tconst char *trailingstring;\r\n\t\t\t\t\tchar *statementstring = qreq->query;\r\n\t\t\t\t\tsqliteresult_t *mat;\r\n\t\t\t\t\tint rowspace;\r\n\t\t\t\t\tint totalrows = 0;\r\n\t\t\t\t\tqboolean keeplooping = true;\r\n\r\n//\t\t\t\t\tSys_Printf(\"processing %s\\n\", statementstring);\r\n//\t\t\t\t\tqsqlite3_mutex_enter(server->sqlite->mutex);\r\n//\t\t\t\t\twhile(*statementstring)\r\n//\t\t\t\t\t{\r\n\t\t\t\t\t\tif (qsqlite3_prepare(server->sqlite, statementstring, -1, &pStmt, &trailingstring) == SQLITE_OK)\r\n\t\t\t\t\t\t{\t//sql statement is valid, apparently.\r\n\t\t\t\t\t\t\tcolumns = qsqlite3_column_count(pStmt);\r\n\r\n\t\t\t\t\t\t\trc = qsqlite3_step(pStmt);\r\n\t\t\t\t\t\t\twhile(keeplooping)\r\n\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\trowspace = 65;\r\n\r\n\t\t\t\t\t\t\t\tqres = (queryresult_t *)ZF_Malloc(sizeof(queryresult_t) + columns * sizeof(sqliteresult_t) * rowspace);\r\n\t\t\t\t\t\t\t\tmat = (sqliteresult_t*)(qres + 1);\r\n\t\t\t\t\t\t\t\tif (qres)\r\n\t\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\t\tqres->result = mat;\r\n\t\t\t\t\t\t\t\t\tqres->rows = 0;\r\n\t\t\t\t\t\t\t\t\tqres->columns = columns;\r\n\t\t\t\t\t\t\t\t\tqres->request = qreq;\r\n\t\t\t\t\t\t\t\t\tqres->firstrow = totalrows;\r\n\t\t\t\t\t\t\t\t\tqres->eof = false;\r\n\t\t\t\t\t\t\t\t\tqreq->nextqueue = NULL;\r\n\r\n\t\t\t\t\t\t\t\t\t//headers technically take a row.\r\n\t\t\t\t\t\t\t\t\tfor (i = 0; i < columns; i++)\r\n\t\t\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\t\t\tmat[i].ptr = strdup(qsqlite3_column_name(pStmt, i));\r\n\t\t\t\t\t\t\t\t\t\tmat[i].len = 0;\r\n\t\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t\t\trowspace--;\r\n\t\t\t\t\t\t\t\t\tmat += columns;\r\n\r\n\t\t\t\t\t\t\t\t\twhile(1)\r\n\t\t\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\t\t\tif (rc == SQLITE_ROW)\r\n\t\t\t\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\t\t\t\tif (!rowspace)\r\n\t\t\t\t\t\t\t\t\t\t\t\tbreak;\r\n\r\n\t\t\t\t\t\t\t\t\t\t\t//generate the row info\r\n\t\t\t\t\t\t\t\t\t\t\tfor (i = 0; i < columns; i++)\r\n\t\t\t\t\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\t\t\t\t\tconst char *data = qsqlite3_column_text(pStmt, i);\r\n\t\t\t\t\t\t\t\t\t\t\t\tmat[i].len = qsqlite3_column_bytes(pStmt, i);\r\n\t\t\t\t\t\t\t\t\t\t\t\tmat[i].ptr = malloc(mat[i].len+1);\r\n\t\t\t\t\t\t\t\t\t\t\t\tmemcpy(mat[i].ptr, data, mat[i].len);\r\n\t\t\t\t\t\t\t\t\t\t\t\tmat[i].ptr[mat[i].len] = 0;\t//make sure blobs are null terminated, in case someone reads them as a string.\r\n\t\t\t\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t\t\t\t\tqres->rows++;\r\n\t\t\t\t\t\t\t\t\t\t\ttotalrows++;\r\n\t\t\t\t\t\t\t\t\t\t\trowspace--;\r\n\t\t\t\t\t\t\t\t\t\t\tmat += columns;\r\n\t\t\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t\t\t\telse if (rc == SQLITE_DONE)\r\n\t\t\t\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\t\t\t\t//no more data to get.\r\n\t\t\t\t\t\t\t\t\t\t\tkeeplooping = false;\r\n\t\t\t\t\t\t\t\t\t\t\tqres->eof = true;\t//this one was the ender.\r\n\t\t\t\t\t\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t\t\t\telse\r\n\t\t\t\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\t\t\t\tSys_Printf(\"sqlite error code %i: %s\\n\", rc, statementstring);\r\n\t\t\t\t\t\t\t\t\t\t\tkeeplooping = false;\r\n\t\t\t\t\t\t\t\t\t\t\tqres->eof = true;\t//this one was the ender.\r\n\t\t\t\t\t\t\t\t\t\t\tif (!qres->columns)\r\n\t\t\t\t\t\t\t\t\t\t\t\tqres->columns = -1;\r\n\t\t\t\t\t\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t\t\t\t\t\t}\r\n\r\n\t\t\t\t\t\t\t\t\t\trc = qsqlite3_step(pStmt);\r\n\t\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t\telse\r\n\t\t\t\t\t\t\t\t\tkeeplooping = false;\r\n\r\n\t\t\t\t\t\t\t\tSQL_PushResult(server, qres);\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\tqsqlite3_finalize(pStmt);\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\telse\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tqres = (queryresult_t *)ZF_Malloc(sizeof(queryresult_t) + 18 + strlen(statementstring));\r\n\t\t\t\t\t\t\tif (qres)\r\n\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\tstrcpy(qres->error, \"Bad SQL statement \");\r\n\t\t\t\t\t\t\t\tstrcpy(qres->error+18, statementstring);\r\n\t\t\t\t\t\t\t\tqres->result = NULL;\r\n\t\t\t\t\t\t\t\tqres->rows = 0;\r\n\t\t\t\t\t\t\t\tqres->columns = -1;\r\n\t\t\t\t\t\t\t\tqres->firstrow = totalrows;\r\n\t\t\t\t\t\t\t\tqres->request = qreq;\r\n\t\t\t\t\t\t\t\tqres->eof = true;\r\n\t\t\t\t\t\t\t\tqreq->nextqueue = NULL;\r\n\r\n\t\t\t\t\t\t\t\tSQL_PushResult(server, qres);\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t\t}\r\n//\t\t\t\t\t\tstatementstring = trailingstring;\r\n//\t\t\t\t\t}\r\n//\t\t\t\t\tqsqlite3_mutex_leave(server->sqlite->mutex);\r\n\t\t\t\t}\r\n\t\t\t\tbreak;\r\n#endif\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\tserver->active = false;\r\n\r\n\tswitch(server->driver)\r\n\t{\r\n#ifdef USE_MYSQL\r\n\tcase SQLDRV_MYSQL:\r\n\t\tif (server->mysql)\r\n\t\t\tqmysql_close(server->mysql);\r\n\t\tbreak;\r\n#endif\r\n#ifdef USE_SQLITE\r\n\tcase SQLDRV_SQLITE:\r\n\t\tqsqlite3_close(server->sqlite);\r\n\t\tserver->sqlite = NULL;\r\n\t\tbreak;\r\n#endif\r\n\tdefault:\r\n\t\tbreak;\r\n\t}\r\n\r\n\t// if we have a server error we still need to put it on the queue\r\n\tif (error)\r\n\t{ \r\n\t\tint esize = Q_strlen(error);\r\n\t\tqueryresult_t *qres = (queryresult_t *)Z_Malloc(sizeof(queryresult_t) + esize);\r\n\t\tif (qres)\r\n\t\t{ // hopefully the qmysql_close gained us some memory otherwise we're pretty screwed\r\n\t\t\tqres->rows = qres->columns = -1;\r\n\t\t\tQ_strncpyz(qres->error, error, esize);\r\n\r\n\t\t\tSQL_PushResult(server, qres);\r\n\t\t}\r\n\t}\r\n\r\n#ifdef USE_MYSQL\r\n\tif (!tinit)\r\n\t\tqmysql_thread_end();\r\n#endif\r\n\r\n\tserver->terminated = true;\r\n\treturn 0;\r\n}\r\n\r\nsqlserver_t *SQL_GetServer (void *owner, int serveridx, qboolean inactives)\r\n{\r\n\tif (serveridx < 0 || serveridx >= sqlservercount)\r\n\t\treturn NULL;\r\n\tif (owner && sqlservers[serveridx].owner != owner)\r\n\t\treturn NULL;\r\n\tif (!sqlservers[serveridx].handle)\r\n\t\treturn NULL;\r\n\tif (!inactives && sqlservers[serveridx].handle->active == false)\r\n\t\treturn NULL;\r\n\treturn sqlservers[serveridx].handle;\r\n}\r\n\r\nqueryrequest_t *SQL_GetQueryRequest (sqlserver_t *server, int queryidx)\r\n{\r\n\tqueryrequest_t *qreq;\r\n\tfor (qreq = server->requests; qreq; qreq = qreq->nextreq)\r\n\t{\r\n\t\tif (qreq->num == queryidx)\r\n\t\t\treturn qreq;\r\n\t}\r\n\r\n\treturn NULL;\r\n}\r\n\r\nqueryresult_t *SQL_GetQueryResult (sqlserver_t *server, int queryidx, int row)\r\n{\r\n\tqueryresult_t *qres;\r\n\tqueryrequest_t *qreq;\r\n\r\n\tqreq = SQL_GetQueryRequest(server, queryidx);\r\n\tfor (qres = qreq->results; qres; qres = qres->next)\r\n\t\tif (qres->request && qres->request->num == queryidx && (row >= qres->firstrow || (row == -1 && !qres->firstrow)) && row < qres->firstrow + qres->rows)\r\n\t\t\treturn qres;\r\n\r\n\treturn NULL;\r\n}\r\n\r\nstatic void SQL_DeallocResult(sqlserver_t *server, queryresult_t *qres)\r\n{\r\n\t// deallocate current result\r\n\tswitch(server->driver)\r\n\t{\r\n\tdefault:\r\n\t\tbreak;\r\n#ifdef USE_MYSQL\r\n\tcase SQLDRV_MYSQL:\r\n\t\tif (qres->result)\r\n\t\t\tqmysql_free_result(qres->result);\r\n\t\tbreak;\r\n#endif\r\n#ifdef USE_SQLITE\r\n\tcase SQLDRV_SQLITE:\r\n\t\tif (qres->result)\r\n\t\t{\r\n\t\t\tsqliteresult_t *mat = qres->result;\r\n\t\t\tint i;\r\n\t\t\tfor (i = 0; i < qres->columns * (qres->rows+1); i++)\r\n\t\t\t\tfree(mat[i].ptr);\r\n\t\t}\r\n\t\tbreak;\r\n#endif\r\n\t}\r\n\r\n\tZ_Free(qres);\r\n}\r\n\r\nstatic void SQL_CloseRequestResult(sqlserver_t *server, queryresult_t *qres)\r\n{\r\n\tqueryresult_t *prev, *cur;\r\n\r\n\tprev = qres->request->results;\r\n\tif (prev == qres)\r\n\t{\r\n\t\tqres->request->results = prev->next;\r\n\t\tSQL_DeallocResult(server, prev);\r\n\t\treturn;\r\n\t}\r\n\r\n\tfor (cur = prev->next; cur; prev = cur, cur = prev->next)\r\n\t{\r\n\t\tif (cur == qres)\r\n\t\t{\r\n\t\t\tprev = cur->next;\r\n\t\t\tSQL_DeallocResult(server, cur);\r\n\t\t\treturn;\r\n\t\t}\r\n\t}\r\n}\r\n\r\n//MT only. flush a result without discarding the request. will result in gaps.\r\nvoid SQL_CloseResult(sqlserver_t *server, queryresult_t *qres)\r\n{\r\n\tif (!qres)\r\n\t\treturn;\r\n/*\tif (qres == server->currentresult)\r\n\t{\r\n\t\tSQL_DeallocResult(server, server->currentresult);\r\n\t\tserver->currentresult = NULL;\r\n\t\treturn;\r\n\t}\r\n*/\r\n\t// else we have a persistant query\r\n\tSQL_CloseRequestResult(server, qres);\r\n}\r\n\r\n//MT only. releases the request.\r\nvoid SQL_CloseRequest(sqlserver_t *server, queryrequest_t *qreq, qboolean force)\r\n{\r\n\twhile(qreq->results)\r\n\t{\r\n\t\tSQL_CloseResult(server, qreq->results);\r\n\t}\r\n\t//if the worker thread is still active with it for whatever reason, flag it as aborted but keep it otherwise valid. actually close it later on when we get the results back.\r\n\tif (qreq->state != SR_FINISHED && qreq->state != SR_NEW && !force)\r\n\t\tqreq->state = SR_ABORTED;\r\n\telse\r\n\t{\r\n\t\tqueryrequest_t *prev, *cur;\r\n\r\n\t\t//unlink and free it now that its complete.\r\n\t\tprev = server->requests;\r\n\t\tif (prev == qreq)\r\n\t\t\tserver->requests = prev->nextreq;\r\n\t\telse\r\n\t\t{\r\n\t\t\tfor (cur = prev->nextreq; cur; prev = cur, cur = prev->nextreq)\r\n\t\t\t{\r\n\t\t\t\tif (cur == qreq)\r\n\t\t\t\t{\r\n\t\t\t\t\tprev->nextreq = cur->nextreq;\r\n\t\t\t\t\tbreak;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t\tZ_Free(qreq);\r\n\t}\r\n}\r\n\r\nvoid SQL_CloseAllRequests(sqlserver_t *server)\r\n{\r\n\tqueryresult_t *oldqres, *qres;\r\n\r\n\t//we assume lock is either held or the thread has already died. this function isn't (worker)thread safe.\r\n\r\n\t// close pending results\r\n\tqres = server->results;\r\n\twhile (qres)\r\n\t{\r\n\t\toldqres = qres;\r\n\t\tqres = qres->next;\r\n\t\tSQL_DeallocResult(server, oldqres);\r\n\t}\r\n\r\n\t//now terminate all the requests\r\n\twhile(server->requests)\r\n\t{\r\n\t\tSQL_CloseRequest(server, server->requests, true);\r\n\t}\r\n\r\n\t// close server result\r\n\tif (server->serverresult)\r\n\t{\r\n\t\tSQL_DeallocResult(server, server->serverresult);\r\n\t\tserver->serverresult = NULL;\r\n\t}\r\n}\r\n\r\nchar *SQL_ReadField (sqlserver_t *server, queryresult_t *qres, int row, int col, qboolean fields, size_t *resultsize)\r\n{\r\n\tif (resultsize)\r\n\t\t*resultsize = 0;\r\n\tif (!qres->result)\r\n\t\treturn NULL;\r\n\telse\r\n\t{ // store_result query\r\n\t\trow -= qres->firstrow;\r\n\t\tif (qres->rows < row || qres->columns < col || col < 0)\r\n\t\t\treturn NULL;\r\n\r\n\t\tif (row < 0)\r\n\t\t{ // fetch field name\r\n\t\t\tif (fields) // but only if we asked for them\r\n\t\t\t{\r\n\t\t\t\tswitch(server->driver)\r\n\t\t\t\t{\r\n#ifdef USE_MYSQL\r\n\t\t\t\tcase SQLDRV_MYSQL:\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tMYSQL_FIELD *field = qmysql_fetch_field_direct(qres->result, col);\r\n\r\n\t\t\t\t\t\tif (resultsize)\r\n\t\t\t\t\t\t\t*resultsize = 0;\r\n\t\t\t\t\t\tif (!field)\r\n\t\t\t\t\t\t\treturn NULL;\r\n\t\t\t\t\t\telse\r\n\t\t\t\t\t\t\treturn field->name;\r\n\t\t\t\t\t}\r\n#endif\r\n#ifdef USE_SQLITE\r\n\t\t\t\tcase SQLDRV_SQLITE:\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tsqliteresult_t *mat = qres->result;\r\n\t\t\t\t\t\tif (mat)\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tif (resultsize)\r\n\t\t\t\t\t\t\t\t*resultsize = mat[col].len;\r\n\t\t\t\t\t\t\treturn mat[col].ptr;\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t}\r\n\t\t\t\t\treturn NULL;\r\n#endif\r\n\t\t\t\tdefault:\r\n\t\t\t\t\treturn NULL;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t\treturn NULL;\r\n\t\t}\r\n\t\telse\r\n\t\t{ // fetch data\r\n\t\t\tswitch(server->driver)\r\n\t\t\t{\r\n#ifdef USE_MYSQL\r\n\t\t\tcase SQLDRV_MYSQL:\r\n\t\t\t\t{\r\n\t\t\t\t\tMYSQL_ROW sqlrow;\r\n\t\t\t\t\tunsigned long *lengths;\r\n\r\n\t\t\t\t\tqmysql_data_seek(qres->result, row);\r\n\t\t\t\t\tsqlrow = qmysql_fetch_row(qres->result);\r\n\t\t\t\t\tlengths = qmysql_fetch_lengths(qres->result);\r\n\t\t\t\t\tif (resultsize)\r\n\t\t\t\t\t\t*resultsize = lengths?lengths[col]:0;\r\n\t\t\t\t\tif (!sqlrow || !sqlrow[col])\r\n\t\t\t\t\t\treturn NULL;\r\n\t\t\t\t\telse\r\n\t\t\t\t\t\treturn sqlrow[col];\r\n\t\t\t\t}\r\n#endif\r\n#ifdef USE_SQLITE\r\n\t\t\t\tcase SQLDRV_SQLITE:\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tsqliteresult_t *mat = qres->result;\r\n\t\t\t\t\t\tcol += qres->columns * (row+1);\r\n\t\t\t\t\t\tif (mat)\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tif (resultsize)\r\n\t\t\t\t\t\t\t\t*resultsize = mat[col].len;\r\n\t\t\t\t\t\t\treturn mat[col].ptr;\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t}\r\n\t\t\t\t\treturn NULL;\r\n#endif\r\n\t\t\tdefault:\r\n\t\t\t\treturn NULL;\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n}\r\n\r\nvoid SQL_CleanupServer(sqlserver_t *server)\r\n{\r\n\tint i;\r\n\tqueryrequest_t *qreq, *oldqreq;\r\n\r\n\tserver->active = false; // set thread to kill itself\r\n\tif (server->requestcondv)\r\n\t\tSys_ConditionBroadcast(server->requestcondv); // force condition check\r\n\tif (server->thread)\r\n\t\tSys_WaitOnThread(server->thread); // wait on thread to die\r\n\r\n\t// server resource deallocation (TODO: should this be done in the thread itself?)\r\n\tif (server->requestcondv)\r\n\t\tSys_DestroyConditional(server->requestcondv);\r\n\tif (server->resultlock)\r\n\t\tSys_DestroyMutex(server->resultlock);\r\n\t\r\n\t// close orphaned requests\r\n\tqreq = server->requestqueue;\r\n\twhile (qreq)\r\n\t{\r\n\t\toldqreq = qreq;\r\n\t\tqreq = qreq->nextqueue;\r\n\t\tZ_Free(oldqreq);\r\n\t}\r\n\r\n\tSQL_CloseAllRequests(server);\r\n\r\n\tfor (i = SQL_CONNECT_STRUCTPARAMS; i < SQL_CONNECT_PARAMS; i++)\r\n\t\tZ_Free(server->connectparams[i]);\r\n\tif (server->connectparams)\r\n\t\tBZ_Free(server->connectparams);\r\n\r\n\tZ_Free(server);\r\n}\r\n\r\nint SQL_NewServer(void *owner, const char *driver, const char **paramstr)\r\n{\r\n\tsqlserver_t *server;\r\n\tint serverref;\r\n\tint drvchoice;\r\n\tint paramsize[SQL_CONNECT_PARAMS];\r\n\tchar nativepath[MAX_OSPATH];\r\n\tint i, tsize;\r\n\r\n\t//name matches\r\n\tif (Q_strcasecmp(driver, \"mysql\") == 0)\r\n\t\tdrvchoice = SQLDRV_MYSQL;\r\n\telse if (Q_strcasecmp(driver, \"sqlite\") == 0)\r\n\t\tdrvchoice = SQLDRV_SQLITE;\r\n\telse if (!*driver && (sqlavailable & (1u<<SQLDRV_SQLITE)))\r\n\t\tdrvchoice = SQLDRV_SQLITE;\r\n\telse if (!*driver && (sqlavailable & (1u<<SQLDRV_MYSQL)))\r\n\t\tdrvchoice = SQLDRV_MYSQL;\r\n\telse // invalid driver choice so we bomb out\r\n\t\treturn -1;\r\n\r\n\tif (!SQL_Available())\t//also makes sure the drivers are actually loaded.\r\n\t\treturn -1;\r\n\r\n\tif (!(sqlavailable & (1u<<drvchoice)))\r\n\t\treturn -1;\r\n\r\n\tif (drvchoice == SQLDRV_SQLITE)\r\n\t{\r\n\t\t//sqlite can be sandboxed.\r\n\t\t//explicitly allow 'temp' and 'memory' databases\r\n\t\tif (*paramstr[3] && strcmp(paramstr[3], \":memory:\"))\r\n\t\t{\r\n\t\t\t//anything else is sandboxed into a subdir/database.\r\n\t\t\tchar *qname = va(\"sqlite/%s.db\", paramstr[3]);\r\n\t\t\tif (!FS_SystemPath(qname, FS_GAMEONLY, nativepath, sizeof(nativepath)))\r\n\t\t\t\treturn -1;\r\n\t\t\tparamstr[3] = nativepath;\r\n\t\t\tFS_CreatePath(qname, FS_GAMEONLY);\r\n\t\t}\r\n\t}\r\n\r\n\tfor (i = 0; i < SQL_CONNECT_PARAMS; i++)\r\n\t\tparamsize[i] = Q_strlen(paramstr[i]);\r\n\r\n\t// alloc or realloc sql servers array\r\n\tif (sqlservers == NULL)\r\n\t{\r\n\t\tserverref = 0;\r\n\t\tsqlservercount = 1;\r\n\t\tsqlservers = BZ_Malloc(sizeof(*sqlservers));\r\n\t}\r\n\telse\r\n\t{\r\n\t\tserverref = sqlservercount;\r\n\t\tsqlservercount++;\r\n\t\tsqlservers = BZ_Realloc(sqlservers, sizeof(*sqlservers) * sqlservercount);\r\n\t}\r\n\r\n\t// assemble server structure\r\n\ttsize = 0;\r\n\tfor (i = 0; i < SQL_CONNECT_STRUCTPARAMS; i++)\r\n\t\ttsize += paramsize[i] + 1;\t// allocate extra space for host and user only\r\n\r\n\tserver = (sqlserver_t *)Z_Malloc(sizeof(sqlserver_t) + tsize);\r\n\tserver->connectparams = (char **)BZ_Malloc(sizeof(char *) * SQL_CONNECT_PARAMS);\r\n\r\n\ttsize = 0;\r\n\tfor (i = 0; i < SQL_CONNECT_STRUCTPARAMS; i++)\r\n\t{\r\n\t\tserver->connectparams[i] = ((char *)(server + 1)) + tsize;\r\n\t\tQ_strncpyz(server->connectparams[i], paramstr[i], paramsize[i]);\r\n\t\t// string should be null-terminated due to Z_Malloc\r\n\t\ttsize += paramsize[i] + 1;\r\n\t}\r\n\tfor (i = SQL_CONNECT_STRUCTPARAMS; i < SQL_CONNECT_PARAMS; i++)\r\n\t{\r\n\t\tserver->connectparams[i] = (char *)Z_Malloc(sizeof(char) * (paramsize[i] + 1));\r\n\t\tQ_strncpyz(server->connectparams[i], paramstr[i], paramsize[i]);\r\n\t\t// string should be null-terminated due to Z_Malloc\r\n\t}\r\n\r\n\tsqlservers[serverref].owner = owner;\r\n\tsqlservers[serverref].handle = server;\r\n\r\n\tserver->driver = (sqldrv_t)drvchoice;\r\n\tserver->querynum = 1;\r\n\tserver->active = true;\r\n\tserver->requestcondv = Sys_CreateConditional();\r\n\tserver->resultlock = Sys_CreateMutex();\r\n\r\n\tif (!server->requestcondv || !server->resultlock)\r\n\t{\r\n\t\tif (server->requestcondv)\r\n\t\t\tSys_DestroyConditional(server->requestcondv);\r\n\t\tif (server->resultlock)\r\n\t\t\tSys_DestroyMutex(server->resultlock);\r\n\t\tZ_Free(server);\r\n\t\tsqlservercount--;\r\n\t\treturn -1;\r\n\t}\r\n\r\n\tserver->thread = Sys_CreateThread(\"sqlworker\", sql_serverworker, (void *)server, THREADP_NORMAL, 1024);\r\n\t\r\n\tif (!server->thread)\r\n\t{\r\n\t\tZ_Free(server);\r\n\t\tsqlservercount--;\r\n\t\treturn -1;\r\n\t}\r\n\r\n\treturn serverref;\r\n}\r\n\r\nint SQL_NewQuery(sqlserver_t *server, qboolean (*callback)(queryrequest_t *req, int firstrow, int numrows, int numcols, qboolean eof), const char *str, queryrequest_t **reqout)\r\n{\r\n\tint qsize = Q_strlen(str)+1;\r\n\tqueryrequest_t *qreq;\r\n\tint querynum;\r\n\r\n\tqreq = (queryrequest_t *)ZF_Malloc(sizeof(queryrequest_t) + qsize);\r\n\tif (qreq)\r\n\t{\r\n\t\tqreq->state = SR_NEW;\r\n\t\tquerynum = qreq->num = server->querynum;\r\n\t\t// prevent the reference num from getting too big to prevent FP problems\r\n\t\twhile(1)\r\n\t\t{\r\n\t\t\tif (++server->querynum > (1<<21))\r\n\t\t\t\tserver->querynum = 1; \r\n\r\n\t\t\tif (!SQL_GetQueryRequest(server, server->querynum))\r\n\t\t\t\tbreak;\r\n\t\t}\r\n\t\t\r\n\t\tqreq->callback = callback;\r\n\t\tQ_strncpyz(qreq->query, str, qsize);\r\n\r\n\t\tqreq->nextreq = server->requests;\r\n\t\tserver->requests = qreq;\r\n\r\n\t\tSQL_PushRequest(server, qreq);\r\n\t\tSys_ConditionSignal(server->requestcondv);\r\n\r\n\t\tif (reqout)\r\n\t\t\t*reqout = qreq;\r\n\t\treturn querynum;\r\n\t}\r\n\r\n\tif (reqout)\r\n\t\t*reqout = NULL;\r\n\treturn -1;\r\n}\r\n\r\nvoid SQL_Disconnect(sqlserver_t *server)\r\n{\r\n\tserver->active = false;\r\n\r\n\t// force the threads to reiterate requests and hopefully terminate\r\n\tSys_ConditionBroadcast(server->requestcondv);\r\n}\r\n\r\nvoid SQL_Escape(sqlserver_t *server, const char *src, char *dst, int dstlen)\r\n{\r\n\tswitch (server->driver)\r\n\t{\r\n#ifdef USE_MYSQL\r\n\tcase SQLDRV_MYSQL:\r\n\t\t{\r\n\t\t\tint srclen = strlen(dst);\r\n\t\t\tif (srclen > (dstlen / 2 - 1))\r\n\t\t\t\tdst[0] = '\\0';\r\n\t\t\telse\r\n\t\t\t\tqmysql_real_escape_string(server->mysql, dst, src, srclen);\r\n\t\t}\r\n\t\tbreak;\r\n#endif\r\n#ifdef USE_SQLITE\r\n\tcase SQLDRV_SQLITE:\r\n\t\t{\r\n\t\t\tdstlen--;\r\n\t\t\twhile (dstlen > 2 && *src)\r\n\t\t\t{\r\n\t\t\t\tif (*src == '\\'')\r\n\t\t\t\t{\r\n\t\t\t\t\t*dst++ = *src;\r\n\t\t\t\t\tdstlen--;\r\n\t\t\t\t}\r\n\t\t\t\t*dst++ = *src++;\r\n\t\t\t\tdstlen--;\r\n\t\t\t}\r\n\t\t\t*dst = '\\0';\r\n\t\t}\r\n\t\tbreak;\r\n#endif\r\n\tdefault:\r\n\t\tdst[0] = '\\0';\r\n\t}\r\n}\r\n\r\nconst char *SQL_Info(sqlserver_t *server)\r\n{\r\n\tswitch (server->driver)\r\n\t{\r\n#ifdef USE_MYSQL\r\n\tcase SQLDRV_MYSQL:\r\n\t\tif (qmysql_get_client_info)\r\n\t\t\treturn va(\"mysql: %s\", qmysql_get_client_info());\r\n\t\telse\r\n\t\t\treturn \"ERROR: mysql library not loaded\";\r\n\t\tbreak;\r\n#endif\r\n#ifdef USE_SQLITE\r\n\tcase SQLDRV_SQLITE:\r\n\t\tif (qsqlite3_libversion)\r\n\t\t\treturn va(\"sqlite: %s\", qsqlite3_libversion());\r\n\t\telse\r\n\t\t\treturn \"ERROR: sqlite library not loaded\";\r\n#endif\r\n\tdefault:\r\n\t\treturn \"unknown\";\r\n\t}\r\n}\r\n\r\nqboolean SQL_Available(void)\r\n{\r\n\tif (!sqlinited)\r\n\t{\r\n\t\tsqlinited = true;\r\n#ifdef USE_MYSQL\r\n\t\t//mysql pokes network etc. there's no sandbox. people can use quake clients to pry upon private databases.\r\n\t\tif (COM_CheckParm(\"-mysql\"))\r\n\t\t\tif (SQL_MYSQLInit())\r\n\t\t\t\tsqlavailable |= 1u<<SQLDRV_MYSQL;\r\n#endif\r\n#ifdef USE_SQLITE\r\n\t\t//our sqlite implementation is sandboxed. we block database attachments, and restrict the master database name.\r\n#ifdef _WIN32\r\n\t\tsqlitehandle = Sys_LoadLibrary(\"sqlite3\", sqlitefuncs);\r\n#else\t\r\n\t\tsqlitehandle = Sys_LoadLibrary(\"libsqlite3.so.0\", sqlitefuncs);\t//at least on debian.\r\n#endif\r\n\t\tif (sqlitehandle)\r\n\t\t{\r\n\t\t\tsqlavailable |= 1u<<SQLDRV_SQLITE;\r\n\t\t}\r\n#endif\r\n\t}\r\n\r\n\treturn !!sqlavailable;\r\n}\r\n\r\n/* SQL related commands */\r\nvoid SQL_Status_f(void)\r\n{\r\n\tint i;\r\n\tchar *stat;\r\n\r\n\tSQL_Available();\t//also ensures any drivers are loaded.\r\n#ifdef USE_MYSQL\r\n\tif (!COM_CheckParm(\"-mysql\"))\r\n\t\tCon_Printf(\"mysql: %s\\n\", \"requires -mysql cmdline argument\");\r\n\telse\r\n\t\tCon_Printf(\"mysql: %s\\n\", (sqlavailable&(1u<<SQLDRV_MYSQL))?\"loaded\":\"unavailable\");\r\n#else\r\n\tCon_Printf(\"mysql: %s\\n\", \"disabled at compile time\");\r\n#endif\r\n#ifdef USE_SQLITE\r\n\tCon_Printf(\"sqlite: %s\\n\", (sqlavailable&(1u<<SQLDRV_SQLITE))?\"loaded\":\"unavailable\");\r\n#else\r\n\tCon_Printf(\"sqlite: %s\\n\", \"disabled at compile time\");\r\n#endif\r\n\t\r\n\tCon_Printf(\"%i connections\\n\", sqlservercount);\r\n\tfor (i = 0; i < sqlservercount; i++)\r\n\t{\r\n\t\tint reqnum = 0;\r\n\t\tint resnum = 0;\r\n\t\tqueryrequest_t *qreq;\r\n\t\tqueryresult_t *qres;\r\n\r\n\t\tsqlserver_t *server = sqlservers[i].handle;\r\n\r\n\t\tif (!server)\r\n\t\t\tcontinue;\r\n\r\n\t\tSys_LockMutex(server->resultlock);\r\n\t\tSys_LockConditional(server->requestcondv);\r\n\t\tfor (qreq = server->requests; qreq; qreq = qreq->nextreq)\r\n\t\t\treqnum++;\r\n\t\tfor (qres = server->results; qres; qres = qres->next)\r\n\t\t\tresnum++;\r\n\r\n\t\tswitch(server->driver)\r\n\t\t{\r\n\t\tcase SQLDRV_MYSQL:\r\n\t\t\tCon_Printf(\"#%i %s@%s: %s\\n\",\r\n\t\t\t\ti,\r\n\t\t\t\tserver->connectparams[1],\r\n\t\t\t\tserver->connectparams[0],\r\n\t\t\t\tserver->active ? \"active\" : \"inactive\");\r\n\t\t\tbreak;\r\n\t\tcase SQLDRV_SQLITE:\r\n\t\t\tCon_Printf(\"#%i %s: %s\\n\",\r\n\t\t\t\ti,\r\n\t\t\t\tserver->connectparams[3],\r\n\t\t\t\tserver->active ? \"active\" : \"inactive\");\r\n\t\t\tbreak;\r\n\t\tdefault:\r\n\t\t\tCon_Printf(\"Bad driver\\n\");\r\n\t\t\tbreak;\r\n\t\t}\r\n\r\n\t\tif (reqnum)\r\n\t\t{\r\n\t\t\tCon_Printf (\"- %i requests\\n\", reqnum);\r\n\t\t\tfor (qreq = server->requests; qreq; qreq = qreq->nextqueue)\r\n\t\t\t{\r\n\t\t\t\tswitch (qreq->state)\r\n\t\t\t\t{\r\n\t\t\t\tcase SR_NEW:\t\tstat = \"new\";\t\tbreak;\r\n\t\t\t\tcase SR_PENDING:\tstat = \"pending\";\tbreak;\r\n\t\t\t\tcase SR_PARTIAL:\tstat = \"partial\";\tbreak;\r\n\t\t\t\tcase SR_FINISHED:\tstat = \"finished\";\tbreak;\r\n\t\t\t\tcase SR_ABORTED:\tstat = \"aborted\";\tbreak;\r\n\t\t\t\tdefault:\t\t\tstat = \"???\";\t\tbreak;\r\n\t\t\t\t}\r\n\t\t\t\tCon_Printf (\"  query #%i (%s): %s\\n\",\r\n\t\t\t\t\tqreq->num,\r\n\t\t\t\t\tstat,\r\n\t\t\t\t\tqreq->query);\r\n\t\t\t\t// TODO: function lookup?\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tif (resnum)\r\n\t\t{\r\n\t\t\tCon_Printf (\"- %i pending results\\n\", resnum);\r\n\t\t\tfor (qres = server->results; qres; qres = qres->next)\r\n\t\t\t{\r\n\t\t\t\tCon_Printf (\"  * %i rows, %i columns\", \r\n\t\t\t\t\tqres->rows,\r\n\t\t\t\t\tqres->columns);\r\n\t\t\t\tif (qres->error[0])\r\n\t\t\t\t\tCon_Printf(\", error %s\\n\", qres->error);\r\n\t\t\t\telse\r\n\t\t\t\t\tCon_Printf(\"\\n\");\r\n\t\t\t\t// TODO: request info?\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tif (server->serverresult)\r\n\t\t\tCon_Printf (\"server result: error %s\\n\", server->serverresult->error);\r\n\r\n\t\t// TODO: list all requests, results here\r\n\t\tSys_UnlockMutex(server->resultlock);\r\n\t\tSys_UnlockConditional(server->requestcondv);\r\n\t}\r\n}\r\n\r\nvoid SQL_Kill_f (void)\r\n{\r\n\tsqlserver_t *server;\r\n\r\n\tif (Cmd_Argc() < 2)\r\n\t{\r\n\t\tCon_Printf (\"Syntax: %s serverid\\n\", Cmd_Argv(0));\r\n\t\treturn;\r\n\t}\r\n\r\n\tserver = SQL_GetServer(NULL, atoi(Cmd_Argv(1)), false);\r\n\tif (server)\r\n\t{\r\n\t\tserver->active = false;\r\n\t\tSys_ConditionBroadcast(server->requestcondv);\r\n\t\treturn;\r\n\t}\r\n}\r\n\r\nvoid SQL_Killall_f (void)\r\n{\r\n\tSQL_KillServers(NULL);\r\n}\r\n\r\nvoid SQL_ServerCycle (void)\r\n{\r\n\tint i;\r\n\r\n\tfor (i = 0; i < sqlservercount; i++)\r\n\t{\r\n\t\tsqlserver_t *server = sqlservers[i].handle;\r\n\t\tqueryresult_t *qres;\r\n\t\tqueryrequest_t *qreq;\r\n\r\n\t\tif (!server)\r\n\t\t\tcontinue;\r\n\r\n\t\twhile ((qres = SQL_PullResult(server)))\r\n\t\t{\r\n\t\t\tqreq = qres->request;\r\n\t\t\tqres->next = NULL;\r\n\t\t\tif (qreq && qreq->callback)\r\n\t\t\t{\r\n\t\t\t\tqboolean persist;\r\n\r\n\t\t\t\t//save it for later.\r\n\t\t\t\tqres->next = qreq->results;\r\n\t\t\t\tqreq->results = qres;\r\n\r\n\t\t\t\tif (developer.ival)\r\n\t\t\t\t\tif (*qres->error)\r\n\t\t\t\t\t\tCon_Printf(\"%s\\n\", qres->error);\r\n\t\t\t\tif (qreq->state == SR_ABORTED)\r\n\t\t\t\t{\r\n\t\t\t\t\tpersist = false;\r\n\t\t\t\t\tif (qres->eof)\r\n\t\t\t\t\t\tSQL_CloseRequest(server, qreq, true);\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\tif (qreq->state == SR_FINISHED)\r\n\t\t\t\t\t\tSys_Error(\"SQL: Results after finished!\\n\");\r\n\t\t\t\t\tif (qreq->state == SR_PENDING)\r\n\t\t\t\t\t\tqreq->state = SR_PARTIAL;\r\n\t\t\t\t\tif (qres->eof)\r\n\t\t\t\t\t\tqreq->state = SR_FINISHED;\r\n\t\t\t\t\tpersist = qreq->callback(qreq, qres->firstrow, qres->rows, qres->columns, qres->eof);\r\n\t\t\t\t}\r\n\r\n\t\t\t\tif (!persist)\r\n\t\t\t\t{\r\n\t\t\t\t\tSQL_CloseResult(server, qres);\r\n\t\t\t\t\tif (qreq->state == SR_FINISHED)\r\n\t\t\t\t\t\tSQL_CloseRequest(server, qreq, false);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\telse // error or server-only result\r\n\t\t\t{\r\n\t\t\t\tif (server->serverresult)\r\n\t\t\t\t\tSQL_CloseResult(server, server->serverresult);\r\n\t\t\t\tserver->serverresult = qres;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tif (server->terminated)\r\n\t\t{\r\n\t\t\tsqlservers[i].handle = NULL;\r\n\t\t\tSQL_CleanupServer(server);\r\n\t\t\tcontinue;\r\n\t\t}\r\n\t}\r\n}\r\n\r\n#ifdef USE_MYSQL\r\nqboolean SQL_MYSQLInit(void)\r\n{\r\n\tif ((mysqlhandle = Sys_LoadLibrary(\"libmysql\", mysqlfuncs)))\r\n\t{\r\n\t\tif (qmysql_thread_safe())\r\n\t\t{\r\n\t\t\tif (!qmysql_library_init(0, NULL, NULL))\r\n\t\t\t{\r\n\t\t\t\tCon_Printf(\"MYSQL backend loaded\\n\");\r\n\t\t\t\treturn true;\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t\tCon_Printf(\"MYSQL library init failed!\\n\");\r\n\t\t}\r\n\t\telse\r\n\t\t\tCon_Printf(\"MYSQL client is not thread safe!\\n\");\r\n\r\n\t\tSys_CloseLibrary(mysqlhandle);\r\n\t}\r\n\telse\r\n\t{\r\n\t\tCon_Printf(\"mysql client didn't load\\n\");\r\n\t}\r\n\treturn false;\r\n}\r\n#endif\r\n\r\nvoid SQL_Init(void)\r\n{\r\n\tsqlavailable = 0;\r\n\r\n\tCmd_AddCommand (\"sqlstatus\", SQL_Status_f);\r\n\tCmd_AddCommand (\"sqlkill\", SQL_Kill_f);\r\n\tCmd_AddCommand (\"sqlkillall\", SQL_Killall_f);\r\n\r\n\tCvar_Register(&sql_driver, SQLCVAROPTIONS);\r\n\tCvar_Register(&sql_host, SQLCVAROPTIONS);\r\n\tCvar_Register(&sql_username, SQLCVAROPTIONS);\r\n\tCvar_Register(&sql_password, SQLCVAROPTIONS);\r\n\tCvar_Register(&sql_defaultdb, SQLCVAROPTIONS);\r\n}\r\n\r\nvoid SQL_KillServers(void *owner)\r\n{\r\n\tint i;\r\n\tfor (i = sqlservercount; i-- > 0; )\r\n\t{\r\n\t\tif (!owner || sqlservers[i].owner == owner)\r\n\t\t{\r\n\t\t\tsqlserver_t *server = sqlservers[i].handle;\r\n\t\t\tsqlservers[i].handle = NULL;\r\n\t\t\tsqlservers[i].owner = NULL;\r\n\t\t\tif (server)\r\n\t\t\t\tSQL_CleanupServer(server);\r\n\r\n\t\t\tif (sqlservercount == i+1)\r\n\t\t\t\tsqlservercount--;\r\n\t\t}\r\n\t}\r\n\tif (!sqlservercount)\r\n\t{\r\n\t\tif (sqlservers)\r\n\t\t\tZ_Free(sqlservers);\r\n\t\tsqlservers = NULL;\r\n\t}\r\n}\r\n\r\nvoid SQL_DeInit(void)\r\n{\r\n\tsqlavailable = 0;\r\n\r\n\tSQL_KillServers(NULL);\r\n\tsqlinited = false;\r\n\r\n#ifdef USE_MYSQL\r\n\tif (qmysql_library_end)\r\n\t\tqmysql_library_end();\r\n\r\n\tif (mysqlhandle)\r\n\t\tSys_CloseLibrary(mysqlhandle);\r\n#endif\r\n#ifdef USE_SQLITE\r\n\tif (sqlitehandle)\r\n\t\tSys_CloseLibrary(sqlitehandle);\r\n#endif\r\n}\r\n\r\n#endif\r\n"
  },
  {
    "path": "engine/server/sv_sql.h",
    "content": "#ifndef SV_SQL_H\r\n#define SV_SQL_H\r\n\r\n#ifdef USE_MYSQL\r\n\t#ifdef _WIN32\r\n\t\t#include <windows.h>\r\n\t#endif\r\n\t#include <mysql/mysql.h>\r\n#endif\r\n#ifdef USE_SQLITE\r\ntypedef struct\r\n{\r\n\tchar *ptr;\r\n\tint len;\r\n} sqliteresult_t;\r\n#endif\r\n\r\n#define SQL_CONNECT_STRUCTPARAMS 2\r\n#define SQL_CONNECT_PARAMS 4\r\n\r\ntypedef enum \r\n{\r\n\tSQLDRV_MYSQL,\r\n\tSQLDRV_SQLITE, /* NOT IN YET */\r\n\tSQLDRV_INVALID\r\n} sqldrv_t;\r\n\r\ntypedef struct queryrequest_s\r\n{\r\n\tint srvid;\r\n\tint num; /* query number reference */ \r\n\tstruct queryrequest_s *nextqueue; /* next request in queue */\r\n\tstruct queryrequest_s *nextreq; /* next request in queue */\r\n\tstruct queryresult_s *results;\t/* chain of received results */\r\n\tenum\r\n\t{\r\n\t\tSR_NEW,\r\n\t\tSR_PENDING,\r\n\t\tSR_PARTIAL,\t\t//\r\n\t\tSR_FINISHED,\t//waiting for close\r\n\t\tSR_ABORTED\t\t//don't notify. destroy on finish.\r\n\t} state;\t\t\t//maintained by main thread. worker *may* check for aborted state as a way to quickly generate an error.\r\n\tqboolean (*callback)(struct queryrequest_s *req, int firstrow, int numrows, int numcols, qboolean eof);\t/* called on main thread once complete */\r\n\tstruct\r\n\t{\r\n\t\tqboolean persistant; /* persistant query */\r\n\t\tint qccallback; /* callback function reference */\r\n\t\tint selfent; /* self entity on call */\r\n\t\tfloat selfid; /* self entity id on call */\r\n\t\tint otherent; /* other entity on call */\r\n\t\tfloat otherid; /* other entity id on call */\r\n\t\tvoid *thread;\r\n\t} user;\t/* sql code does not write anything in this struct */\r\n\tchar query[1]; /* query to run */\r\n} queryrequest_t;\r\n\r\ntypedef struct queryresult_s\r\n{\r\n\tstruct queryrequest_s *request; /* corresponding request */\r\n\tstruct queryresult_s *next; /* next result in queue */\r\n\tint rows; /* rows contained in single result set */\r\n\tint firstrow;\t/* 0 on first result block */\r\n\tint columns; /* fields */\r\n\tqboolean eof; /* end of query reached */\r\n\tvoid *result; /* result set from mysql */\r\n#if 0\r\n\tchar **resultset; /* stored result set from partial fetch */\r\n#endif\r\n\tchar error[1]; /* error string, \"\" if none */\r\n} queryresult_t;\r\n\r\ntypedef struct sqlserver_s\r\n{\r\n\tvoid *thread; /* worker thread for server */\r\n\tsqldrv_t driver; /* driver type */\r\n#ifdef USE_MYSQL\r\n\tMYSQL *mysql; /* mysql server */\r\n#endif\r\n#ifdef USE_SQLITE\r\n\tstruct sqlite3 *sqlite;\r\n#endif\r\n\tvolatile qboolean active; /* set to false to kill thread */\r\n\tvolatile qboolean terminated; /* set by the worker to say that it won't block (for long) and can be joined */\r\n\tvoid *requestcondv; /* lock and conditional variable for queue read/write */\r\n\tvoid *resultlock; /* mutex for queue read/write */\r\n\tint querynum; /* next reference number for queries */\r\n\tqueryrequest_t *requests;\t\t/* list of pending and persistant requests */\r\n\tqueryrequest_t *requestqueue; /* query requests queue */\r\n\tqueryrequest_t *requestslast; /* query requests queue last link */\r\n\tqueryresult_t *results; /* query results queue */\r\n\tqueryresult_t *resultslast; /* query results queue last link */\r\n\tqueryresult_t *serverresult; /* most recent (orphaned) server error result */\r\n\tchar **connectparams; /* connect parameters (0 = host, 1 = user, 2 = pass, 3 = defaultdb) */\r\n} sqlserver_t;\r\n\r\n/* prototypes */\r\nvoid SQL_Init(void);\r\nvoid SQL_KillServers(void *owner);\r\nvoid SQL_DeInit(void);\r\n\r\nsqlserver_t *SQL_GetServer (void *owner, int serveridx, qboolean inactives);\r\nqueryrequest_t *SQL_GetQueryRequest (sqlserver_t *server, int queryidx);\r\nqueryresult_t *SQL_GetQueryResult (sqlserver_t *server, int queryidx, int row);\r\n//void SQL_DeallocResult(sqlserver_t *server, queryresult_t *qres);\r\nvoid SQL_ClosePersistantResult(sqlserver_t *server, queryresult_t *qres);\r\nvoid SQL_CloseResult(sqlserver_t *server, queryresult_t *qres);\r\nvoid SQL_CloseRequest(sqlserver_t *server, queryrequest_t *qres, qboolean force);\r\nvoid SQL_CloseAllResults(sqlserver_t *server);\r\nchar *SQL_ReadField (sqlserver_t *server, queryresult_t *qres, int row, int col, qboolean fields, size_t *resultsize);\r\nint SQL_NewServer(void *owner, const char *driver, const char **paramstr);\r\nint SQL_NewQuery(sqlserver_t *server, qboolean (*callback)(queryrequest_t *req, int firstrow, int numrows, int numcols, qboolean eof), const char *str, queryrequest_t **reqout);\t//callback will be called on the main thread once the result is back\r\nvoid SQL_Disconnect(sqlserver_t *server);\r\nvoid SQL_Escape(sqlserver_t *server, const char *src, char *dst, int dstlen);\r\nconst char *SQL_Info(sqlserver_t *server);\r\nqboolean SQL_Available(void);\r\nvoid SQL_ServerCycle (void);\r\n\r\nextern cvar_t sql_driver;\r\nextern cvar_t sql_host;\r\nextern cvar_t sql_username;\r\nextern cvar_t sql_password;\r\nextern cvar_t sql_defaultdb;\r\n\r\n#define SQLCVAROPTIONS \"SQL Defaults\"\r\n\r\n#endif\r\n"
  },
  {
    "path": "engine/server/sv_sys_unix.c",
    "content": "/*\nCopyright (C) 1996-1997 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n*/\n#ifdef __linux__\n#ifndef _GNU_SOURCE\n#define _GNU_SOURCE\t//we need this in order to fix up broken backtraces. make sure its defined only where needed so we still some posixy conformance test on one plat.\n#endif\n#endif\n\n#include <signal.h>\n#include <sys/types.h>\n#include <dlfcn.h>\n#include \"quakedef.h\"\n\n\n#undef malloc\n\n#ifdef NeXT\n#include <libc.h>\n#endif\n\n#include <sys/stat.h>\n#include <termios.h>\n#include <unistd.h>\n#include <sys/time.h>\n#include <errno.h>\n#include <fcntl.h>\n#include <dirent.h>\n#include <time.h>\n#include <unistd.h>\n\n#ifdef MULTITHREAD\n#include <pthread.h>\n#endif\n\n#if defined(__linux__)\n//if we're chrooting, then we need to poke a few other systems to ensure libraries are loaded\n#include \"netinc.h\"\n#endif\n\n#ifdef HAVE_GNUTLS\nqboolean SSL_InitGlobal(qboolean isserver);\nvoid GnuTLS_Shutdown(void);\n#endif\n\n\n// callbacks\nvoid Sys_Linebuffer_Callback (struct cvar_s *var, char *oldvalue);\n\ncvar_t sys_nostdout = CVAR(\"sys_nostdout\",\"0\");\ncvar_t sys_extrasleep = CVAR(\"sys_extrasleep\",\"0\");\ncvar_t sys_colorconsole = CVARD(\"sys_colorconsole\", \"1\", \"Parse colour escapes, with ansi colours on stdout.\");\ncvar_t sys_timestamps = CVARD(\"sys_timestamps\", \"0\", \"Show timesamps on stdout prints.\");\ncvar_t sys_linebuffer = CVARC(\"sys_linebuffer\", \"1\", Sys_Linebuffer_Callback);\n\nstatic qboolean\tstdin_ready;\nstatic qboolean noconinput = false;\n\nstatic struct termios orig, changes;\n\n#ifdef SUBSERVERS\njmp_buf sys_sv_serverforked;\n#endif\n\n/*\n===============================================================================\n\n\t\t\t\tREQUIRED SYS FUNCTIONS\n\n===============================================================================\n*/\n\n/*\n============\nSys_mkdir\n\n============\n*/\nvoid Sys_mkdir (const char *path)\n{\n\tif (mkdir (path, 0755) != -1)\n\t\treturn;\n//\tif (errno != EEXIST)\n//\t\tSys_Error (\"mkdir %s: %s\",path, strerror(errno));\n}\nqboolean Sys_rmdir (const char *path)\n{\n\tif (rmdir (path) == 0)\n\t\treturn true;\n\tif (errno == ENOENT)\n\t\treturn true;\n\treturn false;\n}\n\nqboolean Sys_remove (const char *path)\n{\n#ifdef __unix__\n\treturn unlink(path);\n#else\n\treturn system(va(\"rm \\\"%s\\\"\", path));\n#endif\n}\n\nqboolean Sys_Rename (const char *oldfname, const char *newfname)\n{\n\treturn !rename(oldfname, newfname);\n}\n\n\n#if _POSIX_C_SOURCE >= 200112L\n\t#include <sys/statvfs.h>\n#endif\nqboolean Sys_GetFreeDiskSpace(const char *path, quint64_t *freespace)\n{\n#if _POSIX_C_SOURCE >= 200112L\n\t//posix 2001\n\tstruct statvfs inf;\n\tif(0==statvfs(path, &inf))\n\t{\n\t\t*freespace = inf.f_bsize*(quint64_t)inf.f_bavail;\n\t\treturn true;\n\t}\n#endif\n\treturn false;\n}\n\nint Sys_DebugLog(char *file, char *fmt, ...)\n{\n\tva_list argptr;\n\tchar data[1024];\n\tint fd;\n\tsize_t result;\n\n\tva_start(argptr, fmt);\n\tvsnprintf (data,sizeof(data)-1, fmt, argptr);\n\tva_end(argptr);\n\n\tif (strlen(data) >= sizeof(data)-1)\n\t\tSys_Error(\"Sys_DebugLog was stomped\\n\");\n\n\tfd = open(file, O_WRONLY | O_CREAT | O_APPEND, 0666);\n\tif (fd)\n\t{\n\t\tresult = write(fd, data, strlen(data)); // do something with the result\n\n\t\tif (result != strlen(data))\n\t\t\tCon_Printf(\"Sys_DebugLog() write: Filename: %s, expected %lu, result was %lu (%s)\\n\",file,(unsigned long)strlen(data),(unsigned long)result,strerror(errno));\n\n\t\tclose(fd);\n\t\treturn 0;\n\t}\n\treturn 1;\n}\n\n\nstatic quint64_t timer_basetime;\t//used by all clocks to bias them to starting at 0\nstatic void Sys_ClockType_Changed(cvar_t *var, char *oldval);\nstatic cvar_t sys_clocktype = CVARFCD(\"sys_clocktype\", \"\", CVAR_NOTFROMSERVER, Sys_ClockType_Changed, \"Controls which system clock to base timings from.\\n0: auto\\n\"\n\t\"1: gettimeofday (may be discontinuous).\\n\"\n\t\"2: monotonic.\");\nstatic enum\n{\n\tQCLOCK_AUTO = 0,\n\n\tQCLOCK_GTOD,\n\tQCLOCK_MONOTONIC,\n\tQCLOCK_REALTIME,\n\n\tQCLOCK_INVALID\n} timer_clocktype;\nstatic quint64_t Sys_GetClock(quint64_t *freq)\n{\n\tquint64_t t;\n\tif (timer_clocktype == QCLOCK_MONOTONIC)\n\t{\n\t\tstruct timespec ts;\n\t\tclock_gettime(CLOCK_MONOTONIC, &ts);\n\t\t*freq = 1000000000;\n\t\tt = (ts.tv_sec*(quint64_t)1000000000) + ts.tv_nsec;\n\t}\n\telse if (timer_clocktype == QCLOCK_REALTIME)\n\t{\n\t\tstruct timespec ts;\n\t\tclock_gettime(CLOCK_REALTIME, &ts);\n\t\t*freq = 1000000000;\n\t\tt = (ts.tv_sec*(quint64_t)1000000000) + ts.tv_nsec;\n\n\t\t//WARNING t can go backwards\n\t}\n\telse //if (timer_clocktype == QCLOCK_GTOD)\n\t{\n\t\tstruct timeval tp;\n\t\tgettimeofday(&tp, NULL);\n\t\t*freq = 1000000;\n\t\tt = tp.tv_sec*(quint64_t)1000000 + tp.tv_usec;\n\n\t\t//WARNING t can go backwards\n\t}\n\treturn t - timer_basetime;\n}\nstatic void Sys_ClockType_Changed(cvar_t *var, char *oldval)\n{\n\tint newtype = var?var->ival:0;\n\tif (newtype >= QCLOCK_INVALID)\n\t\tnewtype = QCLOCK_AUTO;\n\tif (newtype <= QCLOCK_AUTO)\n\t\tnewtype = QCLOCK_MONOTONIC;\n\n\tif (newtype != timer_clocktype)\n\t{\n\t\tquint64_t oldtime, oldfreq;\n\t\tquint64_t newtime, newfreq;\n\n\t\toldtime = Sys_GetClock(&oldfreq);\n\t\ttimer_clocktype = newtype;\n\t\ttimer_basetime = 0;\n\t\tnewtime = Sys_GetClock(&newfreq);\n\n\t\ttimer_basetime = newtime - (newfreq * (oldtime) / oldfreq);\n\n\t\t/*if (host_initialized)\n\t\t{\n\t\t\tconst char *clockname = \"unknown\";\n\t\t\tswitch(timer_clocktype)\n\t\t\t{\n\t\t\tcase QCLOCK_GTOD:\t\tclockname = \"gettimeofday\";\tbreak;\n\t\t\tcase QCLOCK_MONOTONIC:\tclockname = \"monotonic\";\tbreak;\n\t\t\tcase QCLOCK_REALTIME:\tclockname = \"realtime\";\tbreak;\n\t\t\tcase QCLOCK_AUTO:\n\t\t\tcase QCLOCK_INVALID:\tbreak;\n\t\t\t}\n\t\t\tCon_Printf(\"Clock %s, wraps after %\"PRIu64\" days, %\"PRIu64\" years\\n\", clockname, (((quint64_t)-1)/newfreq)/(24*60*60), (((quint64_t)-1)/newfreq)/(24*60*60*365));\n\t\t}*/\n\t}\n}\nstatic void Sys_InitClock(void)\n{\n\tquint64_t freq;\n\n\tCvar_Register(&sys_clocktype, \"System vars\");\n\n\t//calibrate it, and apply.\n\tSys_ClockType_Changed(NULL, NULL);\n\ttimer_basetime = 0;\n\ttimer_basetime = Sys_GetClock(&freq);\n}\ndouble Sys_DoubleTime (void)\n{\n\tquint64_t denum, num = Sys_GetClock(&denum);\n\treturn num / (long double)denum;\n}\nunsigned int Sys_Milliseconds (void)\n{\n\tquint64_t denum, num = Sys_GetClock(&denum);\n\tnum *= 1000;\n\treturn num / denum;\n}\n\n/*\n================\nSys_Error\n================\n*/\nvoid Sys_Error (const char *error, ...)\n{\n\tva_list\t\targptr;\n\tchar\t\tstring[1024];\n\n\tva_start (argptr,error);\n\tvsnprintf (string,sizeof(string)-1, error,argptr);\n\tva_end (argptr);\n\tCOM_WorkerAbort(string);\n\tfprintf(stderr, \"Fatal error: %s\\n\",string);\n\n\tif (!noconinput)\n\t{\n\t\ttcsetattr(STDIN_FILENO, TCSADRAIN, &orig);\n\t\tfcntl (STDIN_FILENO, F_SETFL, fcntl (STDIN_FILENO, F_GETFL, 0) & ~O_NDELAY);\n\t}\n\n\t//we used to fire sigsegv. this resulted in people reporting segfaults and not the error message that appeared above. resulting in wasted debugging.\n\t//abort should trigger a SIGABRT and still give us the same stack trace. should be more useful that way.\n\tabort();\n\n\texit (1);\n}\n\nstatic qboolean useansicolours;\nstatic int ansiremap[8] = {0, 4, 2, 6, 1, 5, 3, 7};\nstatic void ApplyColour(unsigned int chr)\n{\n\tstatic int oldchar = CON_WHITEMASK;\n\tint bg, fg;\n\tchr &= CON_FLAGSMASK;\n\n\tif (oldchar == chr)\n\t\treturn;\n\toldchar = chr;\n\tif (!useansicolours)\t//don't spew weird chars when redirected to a file.\n\t\treturn;\n\n\tprintf(\"\\e[0;\"); // reset\n\n\tif (chr & CON_BLINKTEXT)\n\t\tprintf(\"5;\"); // set blink\n\n\tbg = (chr & CON_BGMASK) >> CON_BGSHIFT;\n\tfg = (chr & CON_FGMASK) >> CON_FGSHIFT;\n\n\t// don't handle intensive bit for background\n\t// as terminals differ too much in displaying \\e[1;7;3?m\n\tbg &= 0x7;\n\n\tif (chr & CON_NONCLEARBG)\n\t{\n\t\tif (fg & 0x8) // intensive bit set for foreground\n\t\t{\n\t\t\tprintf(\"1;\"); // set bold/intensity ansi flag\n\t\t\tfg &= 0x7; // strip intensive bit\n\t\t}\n\n\t\t// set foreground and background colors\n\t\tprintf(\"3%i;4%im\", ansiremap[fg], ansiremap[bg]);\n\t}\n\telse\n\t{\n\t\tswitch(fg)\n\t\t{\n\t\t//to get around wierd defaults (like a white background) we have these special hacks for colours 0 and 7\n\t\tcase COLOR_BLACK:\n\t\t\tprintf(\"7m\"); // set inverse\n\t\t\tbreak;\n\t\tcase COLOR_GREY:\n\t\t\tprintf(\"1;30m\"); // treat as dark grey\n\t\t\tbreak;\n\t\tcase COLOR_WHITE:\n\t\t\tprintf(\"m\"); // set nothing else\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tif (fg & 0x8) // intensive bit set for foreground\n\t\t\t{\n\t\t\t\tprintf(\"1;\"); // set bold/intensity ansi flag\n\t\t\t\tfg &= 0x7; // strip intensive bit\n\t\t\t}\n\n\t\t\tprintf(\"3%im\", ansiremap[fg]); // set foreground\n\t\t\tbreak;\n\t\t}\n\t}\n}\n\n#define putch(c) putc(c, stdout);\n/*static void Sys_PrintColouredChar(unsigned int chr)\n{\n\tApplyColour(chr);\n\n\tchr = chr & CON_CHARMASK;\n\n\tif ((chr > 128 || chr < 32) && chr != 10 && chr != 13 && chr != 9)\n\t\tprintf(\"[%02x]\", chr);\n\telse\n\t\tchr &= ~0x80;\n\n\tputch(chr);\n}*/\n\n/*\n================\nSys_Printf\n================\n*/\n#define\tMAXPRINTMSG\t4096\nchar\tconinput_text[256];\nint\t\tconinput_len;\nvoid Sys_Printf (char *fmt, ...)\n{\n\tva_list\t\targptr;\n\n\tif (sys_nostdout.value)\n\t\treturn;\n\n\tif (1)\n\t{\n\t\tchar\t\tmsg[MAXPRINTMSG];\n\t\tunsigned char *t;\n\n\t\tva_start (argptr,fmt);\n\t\tvsnprintf (msg,sizeof(msg)-1, fmt,argptr);\n\t\tva_end (argptr);\n\n#ifdef SUBSERVERS\n\t\tif (SSV_IsSubServer())\n\t\t{\n\t\t\tif (SSV_PrintToMaster(msg))\n\t\t\t\treturn;\n\t\t}\n#endif\n\n\t\t//if we're not linebuffered, kill the currently displayed input line, add the new text, and add more output.\n\t\tif (!sys_linebuffer.value)\n\t\t{\n\t\t\tint i;\n\n\t\t\tfor (i = 0; i < coninput_len; i++)\n\t\t\t\tputch('\\b');\n\t\t\tputch('\\b');\n\t\t\tfor (i = 0; i < coninput_len; i++)\n\t\t\t\tputch(' ');\n\t\t\tputch(' ');\n\t\t\tfor (i = 0; i < coninput_len; i++)\n\t\t\t\tputch('\\b');\n\t\t\tputch('\\b');\n\t\t}\n\n\n\t\tif (sys_colorconsole.value)\n\t\t{\n\t\t\twchar_t w;\n\t\t\tconchar_t *e, *c;\n\t\t\tconchar_t ctext[MAXPRINTMSG];\n\t\t\tunsigned int codeflags, codepoint;\n\t\t\tstatic qboolean wasnl = false;\n\t\t\te = COM_ParseFunString(CON_WHITEMASK, msg, ctext, sizeof(ctext), false);\n\t\t\tfor (c = ctext; c < e; )\n\t\t\t{\n\t\t\t\tc = Font_Decode(c, &codeflags, &codepoint);\n\t\t\t\tif (codeflags & CON_HIDDEN)\n\t\t\t\t\tcontinue;\n\n\t\t\t\tif ((codeflags&CON_RICHFORECOLOUR) || (codepoint == '\\n' && (codeflags&CON_NONCLEARBG)))\n\t\t\t\t\tcodeflags = CON_WHITEMASK;\t//make sure we don't get annoying backgrounds on other lines.\n\t\t\t\tApplyColour(codeflags);\n\n\t\t\t\tif (wasnl && sys_timestamps.ival)\n\t\t\t\t{\n\t\t\t\t\tchar buffer[64];\n\t\t\t\t\ttime_t unixtime = time(NULL);\n\t\t\t\t\tstrftime(buffer, sizeof(buffer), \"%Y-%m-%d %H:%M:%S \", localtime(&unixtime));\n\t\t\t\t\tfor (w = 0; w < buffer[w]; w++)\n\t\t\t\t\t\tputc(buffer[w], stdout);\n\t\t\t\t}\n\t\t\t\tw = codepoint;\n\t\t\t\twasnl = (w == '\\n');\n\t\t\t\tif (w >= 0xe000 && w < 0xe100)\n\t\t\t\t{\n\t\t\t\t\t/*not all quake chars are ascii compatible, so map those control chars to safe ones so we don't mess up anyone's xterm*/\n\t\t\t\t\tif ((w & 0x7f) > 0x20)\n\t\t\t\t\t\tputc(w&0x7f, stdout);\n\t\t\t\t\telse if (w & 0x80)\n\t\t\t\t\t{\n\t\t\t\t\t\tstatic char tab[32] = \"---#@.@@@@ # >..\" \"[]0123456789.---\";\n\t\t\t\t\t\tputc(tab[w&31], stdout);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tstatic char tab[32] = \".####.#### # >..\" \"[]0123456789.---\";\n\t\t\t\t\t\tputc(tab[w&31], stdout);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t/*putwc doesn't like me. force it in utf8*/\n\t\t\t\t\tif (w >= 0x80)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (w > 0x800)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tputc(0xe0 | ((w>>12)&0x0f), stdout);\n\t\t\t\t\t\t\tputc(0x80 | ((w>>6)&0x3f), stdout);\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tputc(0xc0 | ((w>>6)&0x1f), stdout);\n\t\t\t\t\t\tputc(0x80 | (w&0x3f), stdout);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\tputc(w, stdout);\n\t\t\t\t}\n\t\t\t}\n\t\t\tApplyColour(CON_WHITEMASK);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tfor (t = (unsigned char*)msg; *t; t++)\n\t\t\t{\n\t\t\t\tif (*t >= 146 && *t < 156)\n\t\t\t\t\t*t = *t - 146 + '0';\n\t\t\t\tif (*t >= 0x12 && *t <= 0x1b)\n\t\t\t\t\t*t = *t - 0x12 + '0';\n\t\t\t\tif (*t == 143)\n\t\t\t\t\t*t = '.';\n\t\t\t\tif (*t == 157 || *t == 158 || *t == 159)\n\t\t\t\t\t*t = '-';\n\t\t\t\tif (*t >= 128)\n\t\t\t\t\t*t -= 128;\n\t\t\t\tif (*t == 16)\n\t\t\t\t\t*t = '[';\n\t\t\t\tif (*t == 17)\n\t\t\t\t\t*t = ']';\n\t\t\t\tif (*t == 0x1c)\n\t\t\t\t\t*t = 249;\n\n\t\t\t\t*t &= 0x7f;\n\t\t\t\tif ((*t > 128 || *t < 32) && *t != 10 && *t != 13 && *t != 9)\n\t\t\t\t\tprintf(\"[%02x]\", *t);\n\t\t\t\telse\n\t\t\t\t\tputc(*t, stdout);\n\t\t\t}\n\t\t}\n\n\t\t//and put the input line back\n\t\tif (!sys_linebuffer.value)\n\t\t{\n\t\t\tif (coninput_len)\n\t\t\t\tprintf(\"]%s\", coninput_text);\n\t\t\telse\n\t\t\t\tputch(']');\n\t\t}\n\t}\n\telse\n\t{\n\t\tva_start (argptr,fmt);\n\t\tvprintf (fmt,argptr);\n\t\tva_end (argptr);\n\t}\n\n\tfflush(stdout);\n}\n\n\n\n#if 0\n/*\n================\nSys_Printf\n================\n*/\nvoid Sys_Printf (char *fmt, ...)\n{\n\tva_list\t\targptr;\n\tstatic char\t\ttext[2048];\n\tunsigned char\t\t*p;\n\n\tif (sys_nostdout.value || SSV_IsSubServer())\n\t\treturn;\n\n\tva_start (argptr,fmt);\n\tvsnprintf (text,sizeof(text)-1, fmt,argptr);\n\tva_end (argptr);\n\n\tif (strlen(text) > sizeof(text))\n\t\tSys_Error(\"memory overwrite in Sys_Printf\");\n\n\tfor (p = (unsigned char *)text; *p; p++) {\n\t\t*p &= 0x7f;\n\t\tif ((*p > 128 || *p < 32) && *p != 10 && *p != 13 && *p != 9)\n\t\t\tprintf(\"[%02x]\", *p);\n\t\telse\n\t\t\tputc(*p, stdout);\n\t}\n\tfflush(stdout);\n}\n\n#endif\n\n/*\n================\nSys_Quit\n================\n*/\nvoid Sys_Quit (void)\n{\n#ifdef HAVE_SERVER\n\tSV_Shutdown();\n#endif\n#ifdef HAVE_GNUTLS\n\tGnuTLS_Shutdown();\n#endif\n\tif (!noconinput)\n\t{\n\t\ttcsetattr(STDIN_FILENO, TCSADRAIN, &orig);\n\t\tfcntl (STDIN_FILENO, F_SETFL, fcntl (STDIN_FILENO, F_GETFL, 0) & ~O_NDELAY);\n\t}\n\texit (0);\t\t// appkit isn't running\n}\n\n#if 1\nstatic char *Sys_LineInputChar(char *line)\n{\n\tchar c;\n\twhile(*line)\n\t{\n\t\tc = *line++;\n\t\tif (c == '\\r' || c == '\\n')\n\t\t{\n\t\t\tconinput_text[coninput_len] = 0;\n\t\t\tputch ('\\n');\n\t\t\tputch (']');\n\t\t\tconinput_len = 0;\n\t\t\tfflush(stdout);\n\t\t\treturn coninput_text;\n\t\t}\n\t\tif (c == 8)\n\t\t{\n\t\t\tif (coninput_len)\n\t\t\t{\n\t\t\t\tputch (c);\n\t\t\t\tputch (' ');\n\t\t\t\tputch (c);\n\t\t\t\tconinput_len--;\n\t\t\t\tconinput_text[coninput_len] = 0;\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\tif (c == '\\t')\n\t\t{\n\t\t\tint i;\n\t\t\tchar *s = Cmd_CompleteCommand(coninput_text, true, true, 0, NULL);\n\t\t\tif(s)\n\t\t\t{\n\t\t\t\tfor (i = 0; i < coninput_len; i++)\n\t\t\t\t\tputch('\\b');\n\t\t\t\tfor (i = 0; i < coninput_len; i++)\n\t\t\t\t\tputch(' ');\n\t\t\t\tfor (i = 0; i < coninput_len; i++)\n\t\t\t\t\tputch('\\b');\n\n\t\t\t\tstrcpy(coninput_text, s);\n\t\t\t\tconinput_len = strlen(coninput_text);\n\t\t\t\tprintf(\"%s\", coninput_text);\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\tputch (c);\n\t\tconinput_text[coninput_len] = c;\n\t\tconinput_len++;\n\t\tconinput_text[coninput_len] = 0;\n\t\tif (coninput_len == sizeof(coninput_text))\n\t\t\tconinput_len = 0;\n\t}\n\tfflush(stdout);\n\treturn NULL;\n}\n#endif\n/*\n================\nSys_ConsoleInput\n\nChecks for a complete line of text typed in at the console, then forwards\nit to the host command processor\n================\n*/\nvoid Sys_Linebuffer_Callback (struct cvar_s *var, char *oldvalue)\n{\t//reconfigures the tty to send a char at a time (or line at a time)\n\n\tif (noconinput)\n\t\treturn;\t//oh noes! we already hungup!\n\n\tchanges = orig;\n\tif (var->value)\n\t{\n\t\tchanges.c_lflag |= (ICANON|ECHO);\n\t}\n\telse\n\t{\n\t\tchanges.c_lflag &= ~(ICANON|ECHO);\n\t\tchanges.c_cc[VTIME] = 0;\n\t\tchanges.c_cc[VMIN] = 1;\n\t}\n\ttcsetattr(STDIN_FILENO, TCSADRAIN, &changes);\n}\n\nchar *Sys_ConsoleInput (void)\n{\n\tstatic char\ttext[256];\n\tint\tlen;\n\n\tif (!stdin_ready || noconinput==true)\n\t\treturn NULL;\t\t// the select didn't say it was ready\n\tstdin_ready = false;\n\n//libraries and muxers and things can all screw with our stdin blocking state.\n//if a server sits around waiting for its never-coming stdin then we're screwed.\n//and don't assume that it won't block just because select told us it was readable, select lies.\n//so force it non-blocking so we don't get any nasty surprises.\n#if defined(__linux__)\n\t{\n\t\tint fl = fcntl (STDIN_FILENO, F_GETFL, 0);\n\t\tif (!(fl & O_NDELAY))\n\t\t{\n\t\t\tfcntl(STDIN_FILENO, F_SETFL, fl | O_NDELAY);\n//\t\t\tSys_Printf(CON_WARNING \"stdin flags became blocking - gdb bug?\\n\");\n\t\t}\n\t}\n#endif\n\n\tlen = read (STDIN_FILENO, text, sizeof(text)-1);\n\tif (len < 0)\n\t{\n\t\tint err = errno;\n\t\tswitch(err)\n\t\t{\n\t\tcase EINTR:\t\t//unix sucks\n\t\tcase EAGAIN:\t//a select fuckup?\n\t\t\tbreak;\n\t\tcase EIO:\n\t\t\tnoconinput |= 2;\n\t\t\tstdin_ready = true;\n\t\t\treturn NULL;\n\t\tdefault:\n\t\t\tCon_Printf(\"error %i reading from stdin\\n\", err);\n\t\t\tnoconinput = true;\t//we don't know what it was, but don't keep triggering it.\n\t\t\treturn NULL;\n\t\t}\n\t}\n\tif (noconinput&2)\n\t{\t//posix job stuff sucks - there's no way to detect when we're directly pushed to the foreground after being backgrounded.\n\t\tCon_Printf(\"Welcome back!\\n\");\n\t\tnoconinput &= ~2;\n\t}\n\n\t/*if (len == 0)\n\t{\n\t\t// end of file? doesn't really make sense. depend upon sighup instead\n\t\tCon_Printf(\"EOF reading from stdin\\n\");\n\t\tnoconinput = true;\n\t\treturn NULL;\n\t}*/\n\tif (len < 1)\n\t\treturn NULL;\n\ttext[len-1] = 0;\t// rip off the /n and terminate\n\n\tif (sys_linebuffer.value == 0)\n\t\treturn Sys_LineInputChar(text);\n\treturn text;\n}\n\n/*\n=============\nSys_Init\n\nQuake calls this so the system can register variables before host_hunklevel\nis marked\n=============\n*/\nvoid Sys_Init (void)\n{\n\tSys_InitClock();\n\n\tCvar_Register (&sys_nostdout, \"System configuration\");\n\tCvar_Register (&sys_extrasleep,\t\"System configuration\");\n\n\tCvar_Register (&sys_colorconsole, \"System configuration\");\n\tCvar_Register (&sys_timestamps, \"System configuration\");\n\tCvar_Register (&sys_linebuffer, \"System configuration\");\n}\n\nvoid Sys_Shutdown (void)\n{\n}\n\n#if defined(__linux__) && defined(__GLIBC__)\n#include <execinfo.h>\n#ifdef __i386__\n#include <ucontext.h>\n#endif\nstatic void Friendly_Crash_Handler(int sig, siginfo_t *info, void *vcontext)\n{\n\tint fd;\n\tvoid *array[10];\n\tsize_t size;\n\tint firstframe = 0;\n\tchar signame[32];\n\n\tswitch(sig)\n\t{\n\tcase SIGILL:\tstrcpy(signame, \"SIGILL\");\tbreak;\n\tcase SIGFPE:\tstrcpy(signame, \"SIGFPE\");\tbreak;\n\tcase SIGBUS:\tstrcpy(signame, \"SIGBUS\");\tbreak;\n\tcase SIGABRT:\tstrcpy(signame, \"SIGABRT\");\tbreak;\n\tcase SIGSEGV:\tQ_snprintfz(signame, sizeof(signame), \"SIGSEGV (%p)\", info->si_addr);\tbreak;\n\tdefault:\tQ_snprintfz(signame, sizeof(signame), \"%i\", sig);\tbreak;\n\t}\n\n\t// get void*'s for all entries on the stack\n\tsize = backtrace(array, 10);\n\n#if defined(__i386__)\n\t//x86 signals don't leave the stack in a clean state, so replace the signal handler with the real crash address, and hide this function\n\t{\n\t\tucontext_t *uc = vcontext;\n\t\tarray[1] = (void*)uc->uc_mcontext.gregs[REG_EIP];\n\t\tfirstframe = 1;\n\t}\n#elif defined(__amd64__)\n\t//amd64 is sane enough, but this function and the libc signal handler are on the stack, and should be ignored.\n\tfirstframe = 2;\n#endif\n\n\t// print out all the frames to stderr\n\tfprintf(stderr, \"Error: signal %s:\\n\", signame);\n\tbacktrace_symbols_fd(array+firstframe, size-firstframe, 2);\n\n\tfd = open(\"crash.log\", O_WRONLY|O_CREAT|O_APPEND, S_IRUSR | S_IWUSR | S_IRGRP);\n\tif (fd != -1)\n\t{\n\t\ttime_t rawtime;\n\t\tstruct tm * timeinfo;\n\t\tchar buffer [80];\n\n\t\ttime (&rawtime);\n\t\ttimeinfo = localtime (&rawtime);\n\t\tstrftime (buffer, sizeof(buffer), \"Time: %Y-%m-%d %H:%M:%S\\n\",timeinfo);\n\t\twrite(fd, buffer, strlen(buffer));\n\n\t\tQ_snprintfz(buffer, sizeof(buffer), \"Ver: %i.%02i%s\\n\", FTE_VER_MAJOR, FTE_VER_MINOR,\n#ifdef OFFICIAL_RELEASE\n\t\t\t\" (official)\");\n#else\n\t\t\t\"\");\n#endif\n\t\twrite(fd, buffer, strlen(buffer));\n\n#if defined(SVNREVISION) && defined(SVNDATE)\n\t\tQ_snprintfz(buffer, sizeof(buffer), \"Revision: %s\\nBinary: %s\\n\", STRINGIFY(SVNREVISION), STRINGIFY(SVNDATE));\n#else\n\t\tQ_snprintfz(buffer, sizeof(buffer),\n\t\t#ifdef SVNREVISION\n\t\t\t\"Revision: \"STRINGIFY(SVNREVISION)\"\\n\"\n\t\t#endif\n\t\t\"Binary: \"__DATE__\" \"__TIME__\"\\n\");\n#endif\n\t\twrite(fd, buffer, strlen(buffer));\n\n\t\tbacktrace_symbols_fd(array + firstframe, size - firstframe, fd);\n\t\twrite(fd, \"\\n\", 1);\n\t\tclose(fd);\n\t}\n\texit(1);\n}\n#endif\n\n#ifdef SQL\n#include \"sv_sql.h\"\n#endif\nstatic int Sys_CheckChRoot(void)\n{\t//also warns if run as root.\n\tint ret = false;\n#ifdef __linux__\n\t//three ways to use this:\n\t//nonroot-with-SUID-root -- chroots+drops to a fixed path when run as a regular user. the homedir mechanism can be used for writing files.\n\t//root -chroot foo -uid bar -- requires root, changes the filesystem and then switches user rights before starting the game itself.\n\t//root -chroot foo -- requires root, changes the filesystem and leaves the process with far far too many rights\n\n\tuid_t ruid, euid, suid;\n\tint arg = COM_CheckParm(\"-chroot\");\n\tconst char *newroot = arg?com_argv[arg+1]:NULL;\n\tconst char *newhome;\n\n\tgetresuid(&ruid, &euid, &suid);\n//\tprintf(\"ruid %u, euid %u, suid %u\\n\", ruid, euid, suid);\n\tif (!euid && ruid != euid)\n\t{\t//if we're running SUID-root then assume the admin set it up that way in order to use chroot without making any libraries available inside the jail.\n\t\t//however, chroot needs a certain level of sandboxing to prevent somehow running suid programs with eg a custom /etc/passwd, etc.\n\t\t//this means we can't allow\n\t\t//FIXME other games. should use the list in fs.c\n\t\tif (COM_CheckParm(\"-quake\"))\n\t\t\tnewroot = \"/usr/share/games/quake\";\n\t\telse if (COM_CheckParm(\"-quake2\"))\n\t\t\tnewroot = \"/usr/share/games/quake2\";\n\t\telse if (COM_CheckParm(\"-quake3\"))\n\t\t\tnewroot = \"/usr/share/games/quake3\";\n\t\telse if (COM_CheckParm(\"-hexen2\") || COM_CheckParm(\"-portals\"))\n\t\t\tnewroot = \"/usr/share/games/hexen2\";\n\t\telse\n#ifdef GAME_SHORTNAME\n\t\t\tnewroot = \"/usr/share/games/\" GAME_SHORTNAME;\n#else\n\t\t\tnewroot = \"/usr/share/games/quake\";\n#endif\n\n\t\t//just read the environment name\n\t\tnewhome = getenv(\"USER\");\n\t}\n\telse\n\t{\n\t\tnewhome = NULL;\n\t\targ = COM_CheckParm(\"-uid\");\n\t\tif (arg)\n\t\t\truid = strtol(com_argv[arg+1], NULL, 0);\n\t}\n\n\tif (newroot)\n\t{\t//chroot requires running as root, which sucks.\n\t\t//make sure there's no suid programs in the new root dir that might get confused by /etc/ being something else.\n\t\t//this binary MUST NOT be inside the new root.\n\n\t\t//make sure we don't crash on any con_printfs.\n#ifdef MULTITHREAD\n\t\tSys_ThreadsInit();\n#endif\n\n\t\t//FIXME: should we temporarily try swapping uid+euid so we don't have any more access than a non-suid binary for this initial init stuff?\n\t\t{\n\t\t\tstruct addrinfo *info;\n\t\t\tif (getaddrinfo(\"master.quakeservers.net\", NULL, NULL, &info) == 0)\t//make sure we've loaded /etc/resolv.conf etc, otherwise any dns requests are going to fail, which would mean no masters.\n\t\t\t\tfreeaddrinfo(info);\n\t\t}\n\n#if defined(SQL) && defined(HAVE_SERVER)\n\t\tSQL_Available();\n#endif\n#ifdef HAVE_GNUTLS\n\t\tSSL_InitGlobal(false);\t//we need to load the known CA certs while we still can, as well as any shared objects\n\t\t//SSL_InitGlobal(true);\t//make sure we load our public cert from outside the sandbox. an exploit might still be able to find it in memory though. FIXME: disabled in case this reads from somewhere bad - we're still root.\n#endif\n\n\t\t{\t//this protects against stray setuid programs like su reading passwords from /etc/passwd et al\n\t\t\t//there shouldn't be anyway so really this is pure paranoia.\n\t\t\t//(the length thing is to avoid overflows inside va giving false negatives.)\n\t\t\tstruct stat s;\n\t\t\tif (strlen(newroot) > 4096 || lstat(va(\"%s/etc/\", newroot), &s) != -1)\n\t\t\t{\n\t\t\t\tprintf(\"refusing to chroot to %s - contains an /etc directory\\n\", newroot);\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t\tif (strlen(newroot) > 4096 || lstat(va(\"%s/proc/\", newroot), &s) != -1)\n\t\t\t{\n\t\t\t\tprintf(\"refusing to chroot to %s - contains a /proc directory\\n\", newroot);\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t}\n\n\t\tprintf(\"Changing root dir to \\\"%s\\\"\\n\", newroot);\n\t\tif (chroot(newroot))\n\t\t{\n\t\t\tprintf(\"chroot call failed\\n\");\n\t\t\treturn -1;\n\t\t}\n\t\tchdir(\"/\");\t//chroot does NOT change the working directory, so we need to make sure that happens otherwise still a way out.\n\n\t\t//signal to the fs.c code to use an explicit base home dir.\n\t\tif (newhome)\n\t\t\tsetenv(\"FTEHOME\", va(\"/user/%s\", newhome), true);\n\t\telse\n\t\t\tsetenv(\"FTEHOME\", va(\"/user/%i\", ruid), true);\n\n\t\t//these paths are no longer valid.\n\t\tsetenv(\"HOME\", \"\", true);\n\t\tsetenv(\"XDG_DATA_HOME\", \"\", true);\n\n\t\tsetenv(\"PWD\", \"/\", true);\n\t\n\t\tret = true;\n\t}\n\n\tif (ruid != euid || newroot)\n\t{\n\t\tif (setresuid(ruid, ruid, ruid))\t//go back to our original user, assuming we were SUIDed\n\t\t{\n\t\t\tprintf(\"error dropping priveledges\\n\");\n\t\t\treturn -1;\n\t\t}\n\t\tgetresuid(&ruid, &euid, &suid);\n\n\t\tif (setuid(0) != -1 || errno != EPERM)\n\t\t{\n\t\t\tprintf(\"priveledges were not dropped...\\n\");\n\t\t\treturn -1;\n\t\t}\n\t\tgetresuid(&ruid, &euid, &suid);\n\t}\n\n\n\tif (!ruid || !euid || !suid)\n\t\tprintf(\"WARNING: you should NOT be running this as root!\\n\");\n#endif\n\treturn ret;\n}\n\n#ifdef _POSIX_C_SOURCE\nstatic void SigCont(int code)\n{\t//lets us know when we regained foreground focus.\n\tint fl = fcntl (STDIN_FILENO, F_GETFL, 0);\n\tif (!(fl & O_NDELAY))\n\t\tfcntl(STDIN_FILENO, F_SETFL, fl | O_NDELAY);\n\tnoconinput &= ~2;\n}\n#endif\n\n/*\n=============\nmain\n=============\n*/\nint main(int argc, char *argv[])\n{\n\tfloat maxsleep;\n\tquakeparms_t\tparms;\n//\tfd_set\tfdset;\n//\textern\tint\t\tnet_socket;\n\tchar bindir[MAX_OSPATH];\n\n\tsignal(SIGPIPE, SIG_IGN);\n\ttcgetattr(STDIN_FILENO, &orig);\n\tchanges = orig;\n\n\tmemset (&parms, 0, sizeof(parms));\n\n\tCOM_InitArgv (argc, (const char **)argv);\n\tparms.argc = com_argc;\n\tparms.argv = com_argv;\n#ifdef CONFIG_MANIFEST_TEXT\n\tparms.manifest = CONFIG_MANIFEST_TEXT;\n#endif\n\n\t//decide if we should be printing colours to the stdout or not.\n\tif (COM_CheckParm(\"-nocolour\")||COM_CheckParm(\"-nocolor\"))\n\t\tuseansicolours = false;\n\telse\n\t\tuseansicolours = (isatty(STDOUT_FILENO) || COM_CheckParm(\"-colour\") || COM_CheckParm(\"-color\"));\n\tif (COM_CheckParm(\"-nostdin\"))\n\t\tnoconinput = true;\n\n\tswitch(Sys_CheckChRoot())\n\t{\n\tcase true:\n\t\tparms.basedir = \"/\";\n\t\tbreak;\n\tcase false:\n\t\tparms.basedir = \"./\";\n#ifdef __linux__\n\t\t{\t//attempt to figure out where the exe is located\n\t\t\tint l = readlink(\"/proc/self/exe\", bindir, sizeof(bindir)-1);\n\t\t\tif (l > 0)\n\t\t\t{\n\t\t\t\tbindir[l] = 0;\n\t\t\t\t*COM_SkipPath(bindir) = 0;\n\t\t\t\tprintf(\"Binary is located at \\\"%s\\\"\\n\", bindir);\n\t\t\t\tparms.binarydir = bindir;\n\t\t\t}\n\t\t}\n/*#elif defined(__bsd__)\n\t\t{\t//attempt to figure out where the exe is located\n\t\t\tint l = readlink(\"/proc/self/exe\", bindir, sizeof(bindir)-1);\n\t\t\tif (l > 0)\n\t\t\t{\n\t\t\t\tbindir[l] = 0;\n\t\t\t\t*COM_SkipPath(bindir) = 0;\n\t\t\t\tprintf(\"Binary is located at \"%s\"\\n\", bindir);\n\t\t\t\tparms.binarydir = bindir;\n\t\t\t}\n\t\t}\n*/\n#endif\n\t\tbreak;\n\tdefault:\n\t\treturn -1;\n\t}\n\n\n\n#if defined(__linux__) && defined(__GLIBC__)\n\tif (!COM_CheckParm(\"-nodumpstack\"))\n\t{\n\t\tstruct sigaction act;\n\t\tmemset(&act, 0, sizeof(act));\n\t\tact.sa_sigaction = Friendly_Crash_Handler;\n\t\tact.sa_flags = SA_SIGINFO | SA_RESTART;\n\t\tsigaction(SIGILL, &act, NULL);\n\t\tsigaction(SIGFPE, &act, NULL);\n\t\tsigaction(SIGSEGV, &act, NULL);\n\t\tsigaction(SIGABRT, &act, NULL);\n\t\tsigaction(SIGBUS, &act, NULL);\n\t}\n#endif\n\n#ifdef _POSIX_C_SOURCE\n\tsignal(SIGTTIN, SIG_IGN);\t//have to ignore this if we want to not lock up when running backgrounded.\n\tsignal(SIGCONT, SigCont);\n\tsignal(SIGCHLD, SIG_IGN);\t//mapcluster stuff might leak zombie processes if we don't do this.\n#endif\n\n\n#ifdef SUBSERVERS\n\tif (COM_CheckParm(\"-clusterslave\"))\n\t\tisClusterSlave = true;\n#endif\n\n\tTL_InitLanguages(parms.basedir);\n\n\tSV_Init (&parms);\n\n// run one frame immediately for first heartbeat\n\tmaxsleep = SV_Frame();\n\n#ifdef SUBSERVERS\n\tif (setjmp(sys_sv_serverforked))\n\t\tnoconinput = true;\n#endif\n\n//\n// main loop\n//\n\twhile (1)\n\t{\n\t\tif (noconinput != true)\n\t\t\tstdin_ready |= NET_Sleep(maxsleep, true);\n\t\telse\n\t\t{\n\t\t\tNET_Sleep(maxsleep, false);\n\t\t\tstdin_ready = false;\n\t\t}\n\n\t\tmaxsleep = SV_Frame();\n\n\t// extrasleep is just a way to generate a fucked up connection on purpose\n\t\tif (sys_extrasleep.value)\n\t\t\tusleep (sys_extrasleep.value);\n\t}\n\treturn 0;\n}\n\nstatic int Sys_EnumerateFiles2 (const char *truepath, int apathofs, const char *match, int (*func)(const char *, qofs_t, time_t modtime, void *, searchpathfuncs_t *), void *parm, searchpathfuncs_t *spath)\n{\n\tDIR *dir;\n\tchar file[MAX_OSPATH];\n\tconst char *s;\n\tstruct dirent *ent;\n\tstruct stat st;\n\tconst char *wild;\n\tconst char *apath = truepath+apathofs;\n\n\t//if there's a * in a system path, then we need to scan its parent directory to figure out what the * expands to.\n\t//we can just recurse quicklyish to try to handle it.\n\twild = strchr(apath, '*');\n\tif (!wild)\n\t\twild = strchr(apath, '?');\n\tif (wild)\n\t{\n\t\tchar subdir[MAX_OSPATH];\n\t\tfor (s = wild+1; *s && *s != '/'; s++)\n\t\t\t;\n\t\twhile (wild > truepath)\n\t\t{\n\t\t\tif (*(wild-1) == '/')\n\t\t\t\tbreak;\n\t\t\twild--;\n\t\t}\n\t\tmemcpy(file, truepath, wild-truepath);\n\t\tfile[wild-truepath] = 0;\n\n\t\tdir = opendir(file);\n\t\tmemcpy(subdir, wild, s-wild);\n\t\tsubdir[s-wild] = 0;\n\t\tif (dir)\n\t\t{\n\t\t\tdo\n\t\t\t{\n\t\t\t\tent = readdir(dir);\n\t\t\t\tif (!ent)\n\t\t\t\t\tbreak;\n\t\t\t\tif (*ent->d_name != '.')\n\t\t\t\t{\n\t\t\t\t\tif (wildcmp(subdir, ent->d_name))\n\t\t\t\t\t{\n\t\t\t\t\t\tmemcpy(file, truepath, wild-truepath);\n\t\t\t\t\t\tQ_snprintfz(file+(wild-truepath), sizeof(file)-(wild-truepath), \"%s%s\", ent->d_name, s);\n\t\t\t\t\t\tif (!Sys_EnumerateFiles2(file, apathofs, match, func, parm, spath))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tclosedir(dir);\n\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} while(1);\n\t\t\tclosedir(dir);\n\t\t}\n\t\treturn true;\n\t}\n\n\n\tdir = opendir(truepath);\n\tif (!dir)\n\t{\n\t\tCon_DLPrintf((errno==ENOENT)?2:1, \"Failed to open dir %s\\n\", truepath);\n\t\treturn true;\n\t}\n\tdo\n\t{\n\t\tent = readdir(dir);\n\t\tif (!ent)\n\t\t\tbreak;\n\t\tif (*ent->d_name != '.')\n\t\t{\n\t\t\tif (wildcmp(match, ent->d_name))\n\t\t\t{\n\t\t\t\tQ_snprintfz(file, sizeof(file), \"%s/%s\", truepath, ent->d_name);\n\n\t\t\t\tif (stat(file, &st) == 0)\n\t\t\t\t{\n\t\t\t\t\tQ_snprintfz(file, sizeof(file), \"%s%s%s\", apath, ent->d_name, S_ISDIR(st.st_mode)?\"/\":\"\");\n\n\t\t\t\t\tif (!func(file, st.st_size, st.st_mtime, parm, spath))\n\t\t\t\t\t{\n//\t\t\t\t\t\tCon_DPrintf(\"giving up on search after finding %s\\n\", file);\n\t\t\t\t\t\tclosedir(dir);\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse if (lstat(file, &st) == 0)\n\t\t\t\t\t;//okay, so bad symlink, just mute it\n//\t\t\t\telse\n//\t\t\t\t\tfprintf(stderr, \"Stat failed for \\\"%s\\\"\\n\", file);\n\t\t\t}\n\t\t}\n\t} while(1);\n\tclosedir(dir);\n\treturn true;\n}\nint Sys_EnumerateFiles (const char *gpath, const char *match, int (*func)(const char *, qofs_t, time_t modtime, void *, searchpathfuncs_t *), void *parm, searchpathfuncs_t *spath)\n{\n\tchar apath[MAX_OSPATH];\n\tchar truepath[MAX_OSPATH];\n\tchar *s;\n\n\tif (!gpath)\n\t\tgpath = \"\";\n\t*apath = '\\0';\n\n\tQ_strncpyz(apath, match, sizeof(apath));\n\tfor (s = apath+strlen(apath)-1; s >= apath; s--)\n\t{\n\t\tif (*s == '/')\n\t\t{\n\t\t\ts[1] = '\\0';\n\t\t\tmatch += s - apath+1;\n\t\t\tbreak;\n\t\t}\n\t}\n\tif (s < apath)\t//didn't find a '/'\n\t\t*apath = '\\0';\n\n\tQ_snprintfz(truepath, sizeof(truepath), \"%s/%s\", gpath, apath);\n\treturn Sys_EnumerateFiles2(truepath, strlen(gpath)+1, match, func, parm, spath);\n}\n\nvoid Sys_CloseLibrary(dllhandle_t *lib)\n{\n\tif (sys_nounload)\n\t\treturn;\n\tdlclose((void*)lib);\n}\ndllhandle_t *Sys_LoadLibrary(const char *name, dllfunction_t *funcs)\n{\n\tint i;\n\tdllhandle_t *lib;\n\n\tlib = dlopen (name, RTLD_LAZY);\n\tif (!lib && !strstr(name, ARCH_DL_POSTFIX))\n\t\tlib = dlopen (va(\"%s\"ARCH_DL_POSTFIX, name), RTLD_LAZY);\n\tif (!lib)\n\t{\n\t\tconst char *err = dlerror();\n\t\t//I hate this string check\n\t\tCon_DLPrintf(strstr(err, \"No such file or directory\")?2:0,\"%s\\n\", err);\n\t\treturn NULL;\n\t}\n\n\tif (funcs)\n\t{\n\t\tfor (i = 0; funcs[i].name; i++)\n\t\t{\n\t\t\t*funcs[i].funcptr = dlsym(lib, funcs[i].name);\n\t\t\tif (!*funcs[i].funcptr)\n\t\t\t\tbreak;\n\t\t}\n\t\tif (funcs[i].name)\n\t\t{\n\t\t\tCon_DPrintf(\"Unable to find symbol \\\"%s\\\" in \\\"%s\\\"\\n\", funcs[i].name, name);\n\t\t\tSys_CloseLibrary((dllhandle_t*)lib);\n\t\t\tlib = NULL;\n\t\t}\n\t}\n\n\treturn (dllhandle_t*)lib;\n}\nvoid *Sys_GetAddressForName(dllhandle_t *module, const char *exportname)\n{\n\tif (!module)\n\t\treturn NULL;\n\treturn dlsym(module, exportname);\n}\n\nvoid Sys_ServerActivity(void)\n{\n}\n\nqboolean Sys_RandomBytes(qbyte *string, int len)\n{\n\tqboolean res = false;\n\tint fd = open(\"/dev/urandom\", 0);\n\tif (fd != -1)\n\t{\n\t\tres = (read(fd, string, len) == len);\n\t\tclose(fd);\n\t}\n\treturn res;\n}\n\n#ifdef MANIFESTDOWNLOADS\n#include \"fs.h\"\nstatic qboolean Sys_DoInstall(void)\n{\n\tchar fname[MAX_QPATH];\n\tfloat pct = 0;\n\tqboolean applied = false;\n#ifdef __unix__\n\tqboolean showprogress = isatty(STDOUT_FILENO);\n#else\n\tqboolean showprogress = false;\n#endif\n\n#if 1\n\tFS_CreateBasedir(NULL);\n#else\n\tchar basedir[MAX_OSPATH];\n\tif (!FS_SystemPath(\"\", FS_ROOT, basedir, sizeof(basedir)))\n\t\treturn true;\n\tFS_CreateBasedir(basedir);\n#endif\n\n\t*fname = 0;\n\tfor(;;)\n\t{\n\t\twhile(FS_DownloadingPackage())\n\t\t{\n\t\t\tconst char *cur = \"\";\n\t\t\tfloat newpct = 50;\n\t\t\tHTTP_CL_Think(&cur, &newpct);\n\n\t\t\tif (*cur && Q_strncmp(fname, cur, sizeof(fname)-1))\n\t\t\t{\n\t\t\t\tQ_strncpyz(fname, cur, sizeof(fname));\n\t\t\t\tCon_Printf(\"Downloading: %s\\n\", fname);\n\t\t\t}\n\t\t\tif (showprogress && (int)(pct*10) != (int)(newpct*10))\n\t\t\t{\n\t\t\t\tpct = newpct;\n\t\t\t\tSys_Printf(\"%5.1f%%\\r\", pct);\n\t\t\t}\n\n\t\t\tSys_Sleep(10/1000.0);\n\t\t\tCOM_MainThreadWork();\n\t\t}\n\n\t\tif (!applied)\n\t\t{\n\t\t\tif (!PM_MarkUpdates())\n\t\t\t\tbreak;\t//no changes to apply\n\t\t\tPM_ApplyChanges();\n\t\t\tapplied = true;\t//don't keep applying.\n\t\t\tcontinue;\n\t\t}\n\t\tbreak;\n\t}\n\tif (showprogress)\n\t\tSys_Printf(\"     \\r\");\n\treturn true;\n}\nqboolean Sys_RunInstaller(void)\n{\n\tif (COM_CheckParm(\"-install\"))\n\t{\t//install THEN run\n\t\tSys_DoInstall();\n\t\treturn false;\n\t}\n\tif (COM_CheckParm(\"-doinstall\"))\n\t{\n\t\t//install only, then quit\n\t\treturn Sys_DoInstall();\n\t}\n\tif (!com_installer)\n\t\treturn false;\n\treturn Sys_DoInstall();\n}\n#endif\n"
  },
  {
    "path": "engine/server/sv_sys_win.c",
    "content": "/*\nCopyright (C) 1996-1997 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n*/\n#include \"quakedef.h\"\n#include <sys/types.h>\n#include <sys/timeb.h>\n\n#ifndef HAVE_CLIENT\n\n#include <winsock.h>\n#include <conio.h>\n#include <direct.h>\n\n#ifdef MULTITHREAD\n#include <process.h>\n#endif\n\n#ifndef MINIMAL\n//#define USESERVICE\n#endif\n#define SERVICENAME\tDISTRIBUTION\"SV\"\n\n\nstatic HANDLE hconsoleout;\nextern int\t\t\t\tisPlugin;\t//if 2, we qcdebug to external program\n\n\nqboolean\tWinNT;\t//if true, use utf-16 file paths. if false, hope that paths are in ascii.\n\n#if defined(_DEBUG) || defined(DEBUG)\n#define CATCHCRASH\n#endif\n\n\n#ifdef CATCHCRASH\n#include \"dbghelp.h\"\ntypedef BOOL (WINAPI *MINIDUMPWRITEDUMP) (\n\tHANDLE hProcess,\n\tDWORD ProcessId,\n\tHANDLE hFile,\n\tMINIDUMP_TYPE DumpType,\n\tPMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,\n\tPMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,\n\tPMINIDUMP_CALLBACK_INFORMATION CallbackParam\n\t);\n\nDWORD CrashExceptionHandler (qboolean iswatchdog, DWORD exceptionCode, LPEXCEPTION_POINTERS exceptionInfo)\n{\n\tchar dumpPath[1024];\n\tHANDLE hProc = GetCurrentProcess();\n\tDWORD procid = GetCurrentProcessId();\n\tHANDLE dumpfile;\n\tHMODULE hDbgHelp;\n\tMINIDUMPWRITEDUMP fnMiniDumpWriteDump;\n\tHMODULE hKernel;\n\tBOOL (WINAPI *pIsDebuggerPresent)(void);\n\n\tDWORD (WINAPI *pSymSetOptions)(DWORD SymOptions);\n\tBOOL (WINAPI *pSymInitialize)(HANDLE hProcess, PSTR UserSearchPath, BOOL fInvadeProcess);\n\tBOOL (WINAPI *pSymFromAddr)(HANDLE hProcess, DWORD64 Address, PDWORD64 Displacement, PSYMBOL_INFO Symbol);\n\n#ifdef _WIN64\n#define DBGHELP_POSTFIX \"64\"\n\tBOOL (WINAPI *pStackWalkX)(DWORD MachineType, HANDLE hProcess, HANDLE hThread, LPSTACKFRAME64 StackFrame, PVOID ContextRecord, PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemoryRoutine, PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccessRoutine, PGET_MODULE_BASE_ROUTINE64 GetModuleBaseRoutine, PTRANSLATE_ADDRESS_ROUTINE64 TranslateAddress);\n\tPVOID (WINAPI *pSymFunctionTableAccessX)(HANDLE hProcess, DWORD64 AddrBase);\n\tDWORD64 (WINAPI *pSymGetModuleBaseX)(HANDLE hProcess, DWORD64 qwAddr);\n\tBOOL (WINAPI *pSymGetLineFromAddrX)(HANDLE hProcess, DWORD64 qwAddr, PDWORD pdwDisplacement, PIMAGEHLP_LINE64 Line64);\n\tBOOL (WINAPI *pSymGetModuleInfoX)(HANDLE hProcess, DWORD64 qwAddr, PIMAGEHLP_MODULE64 ModuleInfo);\n\t#define STACKFRAMEX STACKFRAME64\n\t#define IMAGEHLP_LINEX IMAGEHLP_LINE64\n\t#define IMAGEHLP_MODULEX IMAGEHLP_MODULE64\n#else\n#define DBGHELP_POSTFIX \"\"\n\tBOOL (WINAPI *pStackWalkX)(DWORD MachineType, HANDLE hProcess, HANDLE hThread, LPSTACKFRAME StackFrame, PVOID ContextRecord, PREAD_PROCESS_MEMORY_ROUTINE ReadMemoryRoutine, PFUNCTION_TABLE_ACCESS_ROUTINE FunctionTableAccessRoutine, PGET_MODULE_BASE_ROUTINE GetModuleBaseRoutine, PTRANSLATE_ADDRESS_ROUTINE TranslateAddress);\n\tPVOID (WINAPI *pSymFunctionTableAccessX)(HANDLE hProcess, DWORD AddrBase);\n\tDWORD (WINAPI *pSymGetModuleBaseX)(HANDLE hProcess, DWORD dwAddr);\n\tBOOL (WINAPI *pSymGetLineFromAddrX)(HANDLE hProcess, DWORD dwAddr, PDWORD pdwDisplacement, PIMAGEHLP_LINE Line);\n\tBOOL (WINAPI *pSymGetModuleInfoX)(HANDLE hProcess, DWORD dwAddr, PIMAGEHLP_MODULE ModuleInfo);\n\t#define STACKFRAMEX STACKFRAME\n\t#define IMAGEHLP_LINEX IMAGEHLP_LINE\n\t#define IMAGEHLP_MODULEX IMAGEHLP_MODULE\n#endif\n\tdllfunction_t debughelpfuncs[] =\n\t{\n\t\t{(void*)&pSymFromAddr,\t\t\t\t\"SymFromAddr\"},\n\t\t{(void*)&pSymSetOptions,\t\t\t\"SymSetOptions\"},\n\t\t{(void*)&pSymInitialize,\t\t\t\"SymInitialize\"},\n\t\t{(void*)&pStackWalkX,\t\t\t\t\"StackWalk\"DBGHELP_POSTFIX},\n\t\t{(void*)&pSymFunctionTableAccessX,\t\"SymFunctionTableAccess\"DBGHELP_POSTFIX},\n\t\t{(void*)&pSymGetModuleBaseX,\t\t\"SymGetModuleBase\"DBGHELP_POSTFIX},\n\t\t{(void*)&pSymGetLineFromAddrX,\t\t\"SymGetLineFromAddr\"DBGHELP_POSTFIX},\n\t\t{(void*)&pSymGetModuleInfoX,\t\t\"SymGetModuleInfo\"DBGHELP_POSTFIX},\n\t\t{NULL, NULL}\n\t};\n\n\t\tswitch(exceptionCode)\n\t{\n\tcase EXCEPTION_ACCESS_VIOLATION:\n\tcase EXCEPTION_DATATYPE_MISALIGNMENT:\n\tcase EXCEPTION_BREAKPOINT:\n\tcase EXCEPTION_SINGLE_STEP:\n\tcase EXCEPTION_ARRAY_BOUNDS_EXCEEDED:\n\tcase EXCEPTION_FLT_DENORMAL_OPERAND:\n\tcase EXCEPTION_FLT_DIVIDE_BY_ZERO:\n\tcase EXCEPTION_FLT_INEXACT_RESULT:\n\tcase EXCEPTION_FLT_INVALID_OPERATION:\n\tcase EXCEPTION_FLT_OVERFLOW:\n\tcase EXCEPTION_FLT_STACK_CHECK:\n\tcase EXCEPTION_FLT_UNDERFLOW:\n\tcase EXCEPTION_INT_DIVIDE_BY_ZERO:\n\tcase EXCEPTION_INT_OVERFLOW:\n\tcase EXCEPTION_PRIV_INSTRUCTION:\n\tcase EXCEPTION_IN_PAGE_ERROR:\n\tcase EXCEPTION_ILLEGAL_INSTRUCTION:\n\tcase EXCEPTION_NONCONTINUABLE_EXCEPTION:\n\tcase EXCEPTION_STACK_OVERFLOW:\n\tcase EXCEPTION_INVALID_DISPOSITION:\n\tcase EXCEPTION_GUARD_PAGE:\n\tcase EXCEPTION_INVALID_HANDLE:\n//\tcase EXCEPTION_POSSIBLE_DEADLOCK:\n\t\tbreak;\n\tdefault:\n\t\t//because windows is a steaming pile of shite, we have to ignore any software-generated exceptions, because most of them are not in fact fatal, *EVEN IF THEY CLAIM TO BE NON-CONTINUABLE*\n\t\treturn exceptionCode;\n\t}\n\n\thKernel = LoadLibrary (\"kernel32\");\n\tpIsDebuggerPresent = (void*)GetProcAddress(hKernel, \"IsDebuggerPresent\");\n\n\tif (pIsDebuggerPresent && pIsDebuggerPresent())\n\t\treturn EXCEPTION_CONTINUE_SEARCH;\n#if defined(HAVE_CLIENT) && defined(GLQUAKE)\n\tif (qrenderer == QR_OPENGL)\n\t\tGLVID_Crashed();\n#endif\n\n#if 1//ndef _MSC_VER\n\t{\n\t\tif (Sys_LoadLibrary(\"DBGHELP\", debughelpfuncs))\n\t\t{\n\t\t\tSTACKFRAMEX stack;\n\t\t\tCONTEXT *pcontext = exceptionInfo->ContextRecord;\n\t\t\tIMAGEHLP_LINEX line;\n\t\t\tIMAGEHLP_MODULEX module;\n\t\t\tstruct\n\t\t\t{\n\t\t\t\tSYMBOL_INFO sym;\n\t\t\t\tchar name[1024];\n\t\t\t} sym;\n\t\t\tint frameno;\n\t\t\tchar stacklog[8192];\n\t\t\tint logpos, logstart;\n\t\t\tchar *logline;\n\n\t\t\tstacklog[logpos=0] = 0;\n\n\t\t\tpSymInitialize(hProc, NULL, TRUE);\n\t\t\tpSymSetOptions(SYMOPT_LOAD_LINES);\n\n\t\t\tmemset(&stack, 0, sizeof(stack));\n#ifdef _WIN64\n\t\t\t#define IMAGE_FILE_MACHINE_THIS IMAGE_FILE_MACHINE_AMD64\n\t\t\tstack.AddrPC.Mode = AddrModeFlat;\n\t\t\tstack.AddrPC.Offset = pcontext->Rip;\n\t\t\tstack.AddrFrame.Mode = AddrModeFlat;\n\t\t\tstack.AddrFrame.Offset = pcontext->Rbp;\n\t\t\tstack.AddrStack.Mode = AddrModeFlat;\n\t\t\tstack.AddrStack.Offset = pcontext->Rsp;\n#else\n\t\t\t#define IMAGE_FILE_MACHINE_THIS IMAGE_FILE_MACHINE_I386\n\t\t\tstack.AddrPC.Mode = AddrModeFlat;\n\t\t\tstack.AddrPC.Offset = pcontext->Eip;\n\t\t\tstack.AddrFrame.Mode = AddrModeFlat;\n\t\t\tstack.AddrFrame.Offset = pcontext->Ebp;\n\t\t\tstack.AddrStack.Mode = AddrModeFlat;\n\t\t\tstack.AddrStack.Offset = pcontext->Esp;\n#endif\n\n\t\t\tQ_strncpyz(stacklog+logpos, FULLENGINENAME \" or dependancy has crashed. The following stack dump been copied to your windows clipboard.\\n\"\n#ifdef _MSC_VER\n\t\t\t\t\"Would you like to generate a core dump too?\\n\"\n#endif\n\t\t\t\t\"\\n\", sizeof(stacklog)-logpos);\n\t\t\tlogstart = logpos += strlen(stacklog+logpos);\n\n\t\t\t//so I know which one it is\n#if defined(DEBUG) || defined(_DEBUG)\n\t#define BUILDDEBUGREL \"Debug\"\n#else\n\t#define BUILDDEBUGREL \"Optimised\"\n#endif\n#ifdef MINIMAL\n\t#define BUILDMINIMAL \"Min\"\n#else\n\t#define BUILDMINIMAL \"\"\n#endif\n#if defined(GLQUAKE) && !defined(D3DQUAKE)\n\t#define BUILDTYPE \"GL\"\n#elif !defined(GLQUAKE) && defined(D3DQUAKE)\n\t#define BUILDTYPE \"D3D\"\n#else\n\t#define BUILDTYPE \"Merged\"\n#endif\n\n\t\t\tQ_snprintfz(stacklog+logpos, sizeof(stacklog)-logpos, \"Build: %s %s %s: %s\\r\\n\", BUILDDEBUGREL, PLATFORM, BUILDMINIMAL BUILDTYPE, version_string());\n\t\t\tlogpos += strlen(stacklog+logpos);\n\n\t\t\tfor(frameno = 0; ; frameno++)\n\t\t\t{\n\t\t\t\tDWORD64 symdisp;\n\t\t\t\tDWORD linedisp;\n\t\t\t\tDWORD_PTR symaddr;\n\t\t\t\tif (!pStackWalkX(IMAGE_FILE_MACHINE_THIS, hProc, GetCurrentThread(), &stack, pcontext, NULL, pSymFunctionTableAccessX, pSymGetModuleBaseX, NULL))\n\t\t\t\t\tbreak;\n\t\t\t\tmemset(&module, 0, sizeof(module));\n\t\t\t\tmodule.SizeOfStruct = sizeof(module);\n\t\t\t\tpSymGetModuleInfoX(hProc, stack.AddrPC.Offset, &module);\n\t\t\t\tmemset(&line, 0, sizeof(line));\n\t\t\t\tline.SizeOfStruct = sizeof(line);\n\t\t\t\tsymdisp = 0;\n\t\t\t\tmemset(&sym, 0, sizeof(sym));\n\t\t\t\tsym.sym.MaxNameLen = sizeof(sym.name);\n\t\t\t\tsymaddr = stack.AddrPC.Offset;\n\t\t\t\tsym.sym.SizeOfStruct = sizeof(sym.sym);\n\t\t\t\tif (pSymFromAddr(hProc, symaddr, &symdisp, &sym.sym))\n\t\t\t\t{\n\t\t\t\t\tif (pSymGetLineFromAddrX(hProc, stack.AddrPC.Offset, &linedisp, &line))\n\t\t\t\t\t\tlogline = va(\"%-20s - %s:%i (%s)\\r\\n\", sym.sym.Name, line.FileName, (int)line.LineNumber, module.LoadedImageName);\n\t\t\t\t\telse\n\t\t\t\t\t\tlogline = va(\"%-20s+%#x (%s)\\r\\n\", sym.sym.Name, (unsigned int)symdisp, module.LoadedImageName);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tlogline = va(\"0x%p (%s)\\r\\n\", (void*)(DWORD_PTR)stack.AddrPC.Offset, module.LoadedImageName);\n\t\t\t\tQ_strncpyz(stacklog+logpos, logline, sizeof(stacklog)-logpos);\n\t\t\t\tlogpos += strlen(stacklog+logpos);\n\t\t\t\tif (logpos+1 >= sizeof(stacklog))\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tSys_Printf(\"%s\", stacklog+logstart);\n\t\t\treturn EXCEPTION_EXECUTE_HANDLER;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tSys_Printf(\"We crashed.\\nUnable to load dbghelp library. Stack info is not available\\n\");\n\t\t\treturn EXCEPTION_EXECUTE_HANDLER;\n\t\t}\n\t}\n#endif\n\n\n\thDbgHelp = LoadLibrary (\"DBGHELP\");\n\tif (hDbgHelp)\n\t\tfnMiniDumpWriteDump = (MINIDUMPWRITEDUMP)GetProcAddress (hDbgHelp, \"MiniDumpWriteDump\");\n\telse\n\t\tfnMiniDumpWriteDump = NULL;\n\n\tif (fnMiniDumpWriteDump)\n\t{\n\t\tif (MessageBox(NULL, \"KABOOM! We crashed!\\nBlame the monkey in the corner.\\nI hope you saved your work.\\nWould you like to take a dump now?\", DISTRIBUTION \" Sucks\", MB_ICONSTOP|MB_YESNO) != IDYES)\n\t\t{\n\t\t\tif (pIsDebuggerPresent ())\n\t\t\t{\n\t\t\t\t//its possible someone attached a debugger while we were showing that message\n\t\t\t\treturn EXCEPTION_CONTINUE_SEARCH;\n\t\t\t}\n\t\t\treturn EXCEPTION_EXECUTE_HANDLER;\n\t\t}\n\n\t\t/*take a dump*/\n\t\tGetTempPath (sizeof(dumpPath)-16, dumpPath);\n\t\tQ_strncatz(dumpPath, DISTRIBUTION\"CrashDump.dmp\", sizeof(dumpPath));\n\t\tdumpfile = CreateFile (dumpPath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);\n\t\tif (dumpfile)\n\t\t{\n\t\t\tMINIDUMP_EXCEPTION_INFORMATION crashinfo;\n\t\t\tcrashinfo.ClientPointers = TRUE;\n\t\t\tcrashinfo.ExceptionPointers = exceptionInfo;\n\t\t\tcrashinfo.ThreadId = GetCurrentThreadId ();\n\t\t\tif (fnMiniDumpWriteDump(hProc, procid, dumpfile, MiniDumpWithIndirectlyReferencedMemory|MiniDumpWithDataSegs, &crashinfo, NULL, NULL))\n\t\t\t{\n\t\t\t\tCloseHandle(dumpfile);\n\t\t\t\tMessageBox(NULL, va(\"You can find the crashdump at\\n%s\\nPlease send this file to someone.\\n\\nWarning: sensitive information (like your current user name) might be present in the dump.\\nYou will probably want to compress it.\", dumpPath), DISTRIBUTION \" Sucks\", 0);\n\t\t\t\treturn EXCEPTION_EXECUTE_HANDLER;\n\t\t\t}\n\t\t}\n\t}\n\telse\n\t\tMessageBox(NULL, \"Kaboom! Sorry. No MiniDumpWriteDump function.\", FULLENGINENAME \" Sucks\", 0);\n\treturn EXCEPTION_EXECUTE_HANDLER;\n}\n\nLONG CALLBACK nonmsvc_CrashExceptionHandler(PEXCEPTION_POINTERS ExceptionInfo)\n{\n\tDWORD foo = EXCEPTION_CONTINUE_SEARCH;\n\tfoo = CrashExceptionHandler(false, ExceptionInfo->ExceptionRecord->ExceptionCode, ExceptionInfo);\n\t//we have no handler. thus we handle it by exiting.\n\tif (foo == EXCEPTION_EXECUTE_HANDLER)\n\t\texit(1);\n\treturn foo;\n}\n#endif\n\n\n\n\n\n\n\n\n\nvoid Sys_CloseLibrary(dllhandle_t *lib)\n{\n\tFreeLibrary((HMODULE)lib);\n}\ndllhandle_t *Sys_LoadLibrary(const char *name, dllfunction_t *funcs)\n{\n\tint i;\n\tHMODULE lib;\n\n\tlib = LoadLibrary(name);\n\tif (!lib)\n\t\treturn NULL;\n\n\tif (funcs)\n\t{\n\t\tfor (i = 0; funcs[i].name; i++)\n\t\t{\n\t\t\t*funcs[i].funcptr = GetProcAddress(lib, funcs[i].name);\n\t\t\tif (!*funcs[i].funcptr)\n\t\t\t\tbreak;\n\t\t}\n\t\tif (funcs[i].name)\n\t\t{\n\t\t\tSys_CloseLibrary((dllhandle_t*)lib);\n\t\t\tlib = NULL;\n\t\t}\n\t}\n\n\treturn (dllhandle_t*)lib;\n}\n\nvoid *Sys_GetAddressForName(dllhandle_t *module, const char *exportname)\n{\n\tif (!module)\n\t\treturn NULL;\n\treturn GetProcAddress((HINSTANCE)module, exportname);\n}\n#ifdef HLSERVER\nchar *Sys_GetNameForAddress(dllhandle_t *module, void *address)\n{\n\t//windows doesn't provide a function to do this, so we have to do it ourselves.\n\t//this isn't the fastest way...\n\t//halflife needs this function.\n\tchar *base = (char *)module;\n\n\tIMAGE_DATA_DIRECTORY *datadir;\n\tIMAGE_EXPORT_DIRECTORY *block;\n\tIMAGE_NT_HEADERS *ntheader;\n\tIMAGE_DOS_HEADER *dosheader = (void*)base;\n\n\tint i, j;\n\tDWORD *funclist;\n\tDWORD *namelist;\n\tSHORT *ordilist;\n\n\tif (!dosheader || dosheader->e_magic != IMAGE_DOS_SIGNATURE)\n\t\treturn NULL; //yeah, that wasn't an exe\n\n\tntheader = (void*)(base + dosheader->e_lfanew);\n\tif (!dosheader->e_lfanew || ntheader->Signature != IMAGE_NT_SIGNATURE)\n\t\treturn NULL;\t//urm, wait, a 16bit dos exe?\n\n\n\tdatadir = &ntheader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];\n\n\tblock = (IMAGE_EXPORT_DIRECTORY *)(base + datadir->VirtualAddress);\n\tfunclist = (DWORD*)(base+block->AddressOfFunctions);\n\tnamelist = (DWORD*)(base+block->AddressOfNames);\n\tordilist = (SHORT*)(base+block->AddressOfNameOrdinals);\n\tfor (i = 0; i < block->NumberOfFunctions; i++)\n\t{\n\t\tif (base+funclist[i] == address)\n\t\t{\n\t\t\tfor (j = 0; j < block->NumberOfNames; j++)\n\t\t\t{\n\t\t\t\tif (ordilist[j] == i)\n\t\t\t\t{\n\t\t\t\t\treturn base+namelist[i];\n\t\t\t\t}\n\t\t\t}\n\t\t\t//it has no name. huh?\n\t\t\treturn NULL;\n\t\t}\n\t}\n\treturn NULL;\n}\n#endif\n\n\n\n\n#include <fcntl.h>\n#include <io.h>\n\n\n#include <signal.h>\n\n#include <shellapi.h>\n\n#ifdef USESERVICE\nqboolean asservice;\nSERVICE_STATUS_HANDLE   ServerServiceStatusHandle;\nSERVICE_STATUS          MyServiceStatus;\nvoid CreateSampleService(qboolean create);\n#endif\n\nvoid PR_Deinit(void);\n\ncvar_t\tsys_nostdout = {\"sys_nostdout\",\"0\"};\ncvar_t\tsys_colorconsole = {\"sys_colorconsole\", \"1\"};\n\nHWND consolewindowhandle;\nHWND hiddenwindowhandler;\n\nint Sys_DebugLog(char *file, char *fmt, ...)\n{\n    va_list argptr;\n    static char data[1024];\n    int fd;\n\n    va_start(argptr, fmt);\n    vsnprintf(data, sizeof(data)-1, fmt, argptr);\n    va_end(argptr);\n    fd = open(file, O_WRONLY | O_CREAT | O_APPEND, 0666);\n\tif (fd)\n\t{\n\t    write(fd, data, strlen(data));\n\t\tclose(fd);\n\t\treturn 0;\n\t}\n\treturn 1; // error\n};\n\n/*\n================\nSys_FileTime\n================\n*/\nint\tSys_FileTime (char *path)\n{\n\tFILE\t*f;\n\n\tf = fopen(path, \"rb\");\n\tif (f)\n\t{\n\t\tfclose(f);\n\t\treturn 1;\n\t}\n\n\treturn -1;\n}\n\n\nwchar_t *widen(wchar_t *out, size_t outlen, const char *utf8);\nchar *narrowen(char *out, size_t outlen, wchar_t *wide);\n\n/*\n================\nSys_mkdir\n================\n*/\nvoid Sys_mkdir (const char *path)\n{\n\t_mkdir(path);\n}\nqboolean Sys_rmdir (const char *path)\n{\n\treturn 0==_rmdir(path);\n}\n\nqboolean Sys_remove (const char *path)\n{\n\tremove(path);\n\n\treturn true;\n}\nqboolean Sys_Rename (const char *oldfname, const char *newfname)\n{\n\treturn !rename(oldfname, newfname);\n}\n\nstatic time_t Sys_FileTimeToTime(FILETIME ft)\n{\n\tULARGE_INTEGER ull;\n\tull.LowPart = ft.dwLowDateTime;\n\tull.HighPart = ft.dwHighDateTime;\n\treturn ull.QuadPart / 10000000ULL - 11644473600ULL;\n}\n\nstatic int Sys_EnumerateFiles2 (const char *match, int matchstart, int neststart, int (QDECL *func)(const char *fname, qofs_t fsize, time_t mtime, void *parm, searchpathfuncs_t *spath), void *parm, searchpathfuncs_t *spath)\n{\n\tqboolean go;\n\tif (!WinNT)\n\t{\n\t\tHANDLE r;\n\t\tWIN32_FIND_DATAA fd;\n\t\tint nest = neststart;\t//neststart refers to just after a /\n\t\tqboolean wild = false;\n\n\t\twhile(match[nest] && match[nest] != '/')\n\t\t{\n\t\t\tif (match[nest] == '?' || match[nest] == '*')\n\t\t\t\twild = true;\n\t\t\tnest++;\n\t\t}\n\t\tif (match[nest] == '/')\n\t\t{\n\t\t\tchar submatch[MAX_OSPATH];\n\t\t\tchar tmproot[MAX_OSPATH];\n\t\t\tchar file[MAX_OSPATH];\n\n\t\t\tif (!wild)\n\t\t\t\treturn Sys_EnumerateFiles2(match, matchstart, nest+1, func, parm, spath);\n\n\t\t\tif (nest-neststart+1> MAX_OSPATH)\n\t\t\t\treturn 1;\n\t\t\tmemcpy(submatch, match+neststart, nest - neststart);\n\t\t\tsubmatch[nest - neststart] = 0;\n\t\t\tnest++;\n\n\t\t\tif (neststart+4 > MAX_OSPATH)\n\t\t\t\treturn 1;\n\t\t\tmemcpy(tmproot, match, neststart);\n\t\t\tstrcpy(tmproot+neststart, \"*.*\");\n\n\t\t\tr = FindFirstFile(tmproot, &fd);\n\t\t\tstrcpy(tmproot+neststart, \"\");\n\t\t\tif (r==(HANDLE)-1)\n\t\t\t\treturn 1;\n\t\t\tgo = true;\n\t\t\tdo\n\t\t\t{\n\t\t\t\tif (*fd.cFileName == '.');\t//don't ever find files with a name starting with '.'\n\t\t\t\telse if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)\t//is a directory\n\t\t\t\t{\n\t\t\t\t\tif (wildcmp(submatch, fd.cFileName))\n\t\t\t\t\t{\n\t\t\t\t\t\tint newnest;\n\t\t\t\t\t\tif (strlen(tmproot) + strlen(fd.cFileName) + strlen(match+nest) + 2 < MAX_OSPATH)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tQ_snprintfz(file, sizeof(file), \"%s%s/\", tmproot, fd.cFileName);\n\t\t\t\t\t\t\tnewnest = strlen(file);\n\t\t\t\t\t\t\tstrcpy(file+newnest, match+nest);\n\t\t\t\t\t\t\tgo = Sys_EnumerateFiles2(file, matchstart, newnest, func, parm, spath);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} while(FindNextFile(r, &fd) && go);\n\t\t\tFindClose(r);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tconst char *submatch = match + neststart;\n\t\t\tchar tmproot[MAX_OSPATH];\n\t\t\tchar file[MAX_OSPATH];\n\n\t\t\tif (neststart+4 > MAX_OSPATH)\n\t\t\t\treturn 1;\n\t\t\tmemcpy(tmproot, match, neststart);\n\t\t\tstrcpy(tmproot+neststart, \"*.*\");\n\n\t\t\tr = FindFirstFile(tmproot, &fd);\n\t\t\tstrcpy(tmproot+neststart, \"\");\n\t\t\tif (r==(HANDLE)-1)\n\t\t\t\treturn 1;\n\t\t\tgo = true;\n\t\t\tdo\n\t\t\t{\n\t\t\t\tif (*fd.cFileName == '.')\n\t\t\t\t\t;\t//don't ever find files with a name starting with '.' (includes .. and . directories, and unix hidden files)\n\t\t\t\telse if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)\t//is a directory\n\t\t\t\t{\n\t\t\t\t\tif (wildcmp(submatch, fd.cFileName))\n\t\t\t\t\t{\n\t\t\t\t\t\tif (strlen(tmproot+matchstart) + strlen(fd.cFileName) + 2 < MAX_OSPATH)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tQ_snprintfz(file, sizeof(file), \"%s%s/\", tmproot+matchstart, fd.cFileName);\n\t\t\t\t\t\t\tgo = func(file, qofs_Make(fd.nFileSizeLow, fd.nFileSizeHigh), Sys_FileTimeToTime(fd.ftLastWriteTime), parm, spath);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tif (wildcmp(submatch, fd.cFileName))\n\t\t\t\t\t{\n\t\t\t\t\t\tif (strlen(tmproot+matchstart) + strlen(fd.cFileName) + 1 < MAX_OSPATH)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tQ_snprintfz(file, sizeof(file), \"%s%s\", tmproot+matchstart, fd.cFileName);\n\t\t\t\t\t\t\tgo = func(file, qofs_Make(fd.nFileSizeLow, fd.nFileSizeHigh), Sys_FileTimeToTime(fd.ftLastWriteTime), parm, spath);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} while(FindNextFile(r, &fd) && go);\n\t\t\tFindClose(r);\n\t\t}\n\t}\n\telse\n\t{\n\t\tHANDLE r;\n\t\tWIN32_FIND_DATAW fd;\n\t\tint nest = neststart;\t//neststart refers to just after a /\n\t\tqboolean wild = false;\n\n\t\twhile(match[nest] && match[nest] != '/')\n\t\t{\n\t\t\tif (match[nest] == '?' || match[nest] == '*')\n\t\t\t\twild = true;\n\t\t\tnest++;\n\t\t}\n\t\tif (match[nest] == '/')\n\t\t{\n\t\t\tchar submatch[MAX_OSPATH];\n\t\t\tchar tmproot[MAX_OSPATH];\n\n\t\t\tif (!wild)\n\t\t\t\treturn Sys_EnumerateFiles2(match, matchstart, nest+1, func, parm, spath);\n\n\t\t\tif (nest-neststart+1> MAX_OSPATH)\n\t\t\t\treturn 1;\n\t\t\tmemcpy(submatch, match+neststart, nest - neststart);\n\t\t\tsubmatch[nest - neststart] = 0;\n\t\t\tnest++;\n\n\t\t\tif (neststart+4 > MAX_OSPATH)\n\t\t\t\treturn 1;\n\t\t\tmemcpy(tmproot, match, neststart);\n\t\t\tstrcpy(tmproot+neststart, \"*.*\");\n\n\t\t\t{\n\t\t\t\twchar_t wroot[MAX_OSPATH];\n\t\t\t\tr = FindFirstFileW(widen(wroot, sizeof(wroot), tmproot), &fd);\n\t\t\t}\n\t\t\tstrcpy(tmproot+neststart, \"\");\n\t\t\tif (r==(HANDLE)-1)\n\t\t\t\treturn 1;\n\t\t\tgo = true;\n\t\t\tdo\n\t\t\t{\n\t\t\t\tchar utf8[MAX_OSPATH];\n\t\t\t\tchar file[MAX_OSPATH];\n\t\t\t\tnarrowen(utf8, sizeof(utf8), fd.cFileName);\n\t\t\t\tif (*utf8 == '.');\t//don't ever find files with a name starting with '.'\n\t\t\t\telse if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)\t//is a directory\n\t\t\t\t{\n\t\t\t\t\tif (wildcmp(submatch, utf8))\n\t\t\t\t\t{\n\t\t\t\t\t\tint newnest;\n\t\t\t\t\t\tif (strlen(tmproot) + strlen(utf8) + strlen(match+nest) + 2 < MAX_OSPATH)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tQ_snprintfz(file, sizeof(file), \"%s%s/\", tmproot, utf8);\n\t\t\t\t\t\t\tnewnest = strlen(file);\n\t\t\t\t\t\t\tstrcpy(file+newnest, match+nest);\n\t\t\t\t\t\t\tgo = Sys_EnumerateFiles2(file, matchstart, newnest, func, parm, spath);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} while(FindNextFileW(r, &fd) && go);\n\t\t\tFindClose(r);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tconst char *submatch = match + neststart;\n\t\t\tchar tmproot[MAX_OSPATH];\n\n\t\t\tif (neststart+4 > MAX_OSPATH)\n\t\t\t\treturn 1;\n\t\t\tmemcpy(tmproot, match, neststart);\n\t\t\tstrcpy(tmproot+neststart, \"*.*\");\n\n\t\t\t{\n\t\t\t\twchar_t wroot[MAX_OSPATH];\n\t\t\t\tr = FindFirstFileW(widen(wroot, sizeof(wroot), tmproot), &fd);\n\t\t\t}\n\t\t\tstrcpy(tmproot+neststart, \"\");\n\t\t\tif (r==(HANDLE)-1)\n\t\t\t\treturn 1;\n\t\t\tgo = true;\n\t\t\tdo\n\t\t\t{\n\t\t\t\tchar utf8[MAX_OSPATH];\n\t\t\t\tchar file[MAX_OSPATH];\n\n\t\t\t\tnarrowen(utf8, sizeof(utf8), fd.cFileName);\n\t\t\t\tif (*utf8 == '.')\n\t\t\t\t\t;\t//don't ever find files with a name starting with '.' (includes .. and . directories, and unix hidden files)\n\t\t\t\telse if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)\t//is a directory\n\t\t\t\t{\n\t\t\t\t\tif (wildcmp(submatch, utf8))\n\t\t\t\t\t{\n\t\t\t\t\t\tif (strlen(tmproot+matchstart) + strlen(utf8) + 2 < MAX_OSPATH)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tQ_snprintfz(file, sizeof(file), \"%s%s/\", tmproot+matchstart, utf8);\n\t\t\t\t\t\t\tgo = func(file, qofs_Make(fd.nFileSizeLow, fd.nFileSizeHigh), Sys_FileTimeToTime(fd.ftLastWriteTime), parm, spath);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tif (wildcmp(submatch, utf8))\n\t\t\t\t\t{\n\t\t\t\t\t\tif (strlen(tmproot+matchstart) + strlen(utf8) + 1 < MAX_OSPATH)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tQ_snprintfz(file, sizeof(file), \"%s%s\", tmproot+matchstart, utf8);\n\t\t\t\t\t\t\tgo = func(file, qofs_Make(fd.nFileSizeLow, fd.nFileSizeHigh), Sys_FileTimeToTime(fd.ftLastWriteTime), parm, spath);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} while(FindNextFileW(r, &fd) && go);\n\t\t\tFindClose(r);\n\t\t}\n\t}\n\treturn go;\n}\nint Sys_EnumerateFiles (const char *gpath, const char *match, int (QDECL *func)(const char *fname, qofs_t fsize, time_t mtime, void *parm, searchpathfuncs_t *spath), void *parm, searchpathfuncs_t *spath)\n{\n\tchar fullmatch[MAX_OSPATH];\n\tint start;\n\tif (strlen(gpath) + strlen(match) + 2 > MAX_OSPATH)\n\t\treturn 1;\n\n\tstrcpy(fullmatch, gpath);\n\tstart = strlen(fullmatch);\n\tif (start && fullmatch[start-1] != '/')\n\t\tfullmatch[start++] = '/';\n\tfullmatch[start] = 0;\n\tstrcat(fullmatch, match);\n\treturn Sys_EnumerateFiles2(fullmatch, start, start, func, parm, spath);\n}\n\n//wide only. we let the windows api sort out the mess of file urls. system-wide consistancy.\nqboolean Sys_ResolveFileURL(const char *inurl, int inlen, char *out, int outlen)\n{\n\tchar *cp;\n\twchar_t wurl[MAX_PATH];\n\twchar_t local[MAX_PATH];\n\tDWORD grr;\n\tstatic HRESULT (WINAPI *pPathCreateFromUrlW)(PCWSTR pszUrl, PWSTR pszPath, DWORD *pcchPath, DWORD dwFlags);\n\tif (!pPathCreateFromUrlW)\n\t\tpPathCreateFromUrlW = Sys_GetAddressForName(Sys_LoadLibrary(\"Shlwapi.dll\", NULL), \"PathCreateFromUrlW\");\n\tif (!pPathCreateFromUrlW)\n\t\treturn false;\n\n\t//need to make a copy, because we can't terminate the inurl easily.\n\tcp = malloc(inlen+1);\n\tmemcpy(cp, inurl, inlen);\n\tcp[inlen] = 0;\n\twiden(wurl, sizeof(wurl), cp);\n\tfree(cp);\n\tgrr = sizeof(local)/sizeof(wchar_t);\n\tif (FAILED(pPathCreateFromUrlW(wurl, local, &grr, 0)))\n\t\treturn false;\n\tnarrowen(out, outlen, local);\n\twhile(*out)\n\t{\n\t\tif (*out == '\\\\')\n\t\t\t*out = '/';\n\t\tout++;\n\t}\n\treturn true;\n}\n\n/*\n================\nSys_Error\n================\n*/\n#include <process.h>\nvoid Sys_Error (const char *error, ...)\n{\n\tva_list\t\targptr;\n\tchar\t\ttext[1024];\n\tdouble end;\n\tSTARTUPINFO startupinfo;\n\tPROCESS_INFORMATION processinfo;\n\n\tva_start (argptr,error);\n\tvsnprintf (text,sizeof(text)-1, error,argptr);\n\tva_end (argptr);\n\tCOM_WorkerAbort(text);\n\n\n//    MessageBox(NULL, text, \"Error\", 0 /* MB_OK */ );\n\tSys_Printf (\"ERROR: %s\\n\", text);\n\n\tCon_Log(text);\n\n\tNET_Shutdown();\t//free sockets and stuff.\n\n#ifdef USESERVICE\n\tif (asservice)\n\t\tSys_Quit();\n#endif\n\n\tif (COM_CheckParm(\"-noreset\"))\n\t{\n\t\tSys_Quit();\n\t\texit(1);\n\t}\n\n\tSys_Printf (\"A new server will be started in 10 seconds unless you press a key\\n\");\n\n\n\t//check for a key press, quitting if we get one in 10 secs\n\tend = Sys_DoubleTime() + 10;\n\twhile(Sys_DoubleTime() < end)\n\t{\n\t\tSleep(500); // don't burn up CPU with polling\n\t\tif (_kbhit())\n\t\t{\n\t\t\tSys_Quit();\n\t\t\texit(1);\n\t\t}\n\t}\n\n\tSys_Printf(\"\\nLoading new instance of FTE...\\n\\n\\n\");\n#ifdef HAVE_SERVER\n\tPR_Deinit();\t//this takes a bit more mem\n#ifdef SVRANKING\n\tRank_Flush();\n#endif\n#endif\n#ifndef MINGW\n\tfcloseall();\t//make sure all files are written.\n#endif\n\n//\tsystem(\"dqwsv.exe\");\t//spawn a new server to take over. This way, if debugging, then any key will quit, otherwise the server will just spawn a new one.\n\n\tmemset(&startupinfo, 0, sizeof(startupinfo));\n\tmemset(&processinfo, 0, sizeof(processinfo));\n\n\tCreateProcess(NULL,\n\t\tGetCommandLine(),\n\t\tNULL,\n\t\tNULL,\n\t\tfalse,\n\t\t0,\n\t\tNULL,\n\t\tNULL,\n\t\t&startupinfo,\n\t\t&processinfo);\n\n\tCloseHandle(processinfo.hProcess);\n\tCloseHandle(processinfo.hThread);\n\n\tSys_Quit ();\n\n\texit (1); // this function is NORETURN type, complains without this\n}\n\n/*\n================\nSys_Milliseconds\n================\n*/\nunsigned int Sys_Milliseconds (void)\n{\n\tstatic DWORD starttime;\n\tstatic qboolean first = true;\n\tDWORD now;\n//\tdouble t;\n\n\tnow = timeGetTime();\n\n\tif (first) {\n\t\tfirst = false;\n\t\tstarttime = now;\n\t\treturn 0.0;\n\t}\n\t/*\n\tif (now < starttime) // wrapped?\n\t{\n\t\tdouble r;\n\t\tr = (now) + (LONG_MAX - starttime);\n\t\tstarttime = now;\n\t\treturn r;\n\t}\n\n\tif (now - starttime == 0)\n\t\treturn 0.0;\n*/\n\treturn (now - starttime);\n}\n\n/*\n================\nSys_DoubleTime\n================\n*/\ndouble Sys_DoubleTime (void)\n{\n\tdouble t;\n    struct _timeb tstruct;\n\tstatic int\tstarttime;\n\n\t_ftime( &tstruct );\n\n\tif (!starttime)\n\t\tstarttime = tstruct.time;\n\tt = (tstruct.time-starttime) + tstruct.millitm*0.001;\n\n\treturn t;\n}\n\n\n/*\n================\nSys_ConsoleInput\n================\n*/\nvoid SV_GetNewSpawnParms(client_t *cl);\nchar\tconinput_text[256];\nint\t\tconinput_len;\nchar *Sys_ConsoleInput (void)\n{\n\tint\t\tc;\n\n\tif (consolewindowhandle)\n\t{\n\t\tMSG msg;\n\t\twhile (PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE))\n\t\t{\n\t\t\tif (!GetMessage (&msg, NULL, 0, 0))\n\t\t\t\treturn NULL;\n\t\t\tTranslateMessage (&msg);\n\t\t\tDispatchMessage (&msg);\n\t\t}\n\t\treturn NULL;\n\t}\n\n#ifdef SUBSERVERS\n\tif (SSV_IsSubServer())\n\t\treturn NULL;\n#endif\n\n\tif (isPlugin)\n\t{\n\t\tDWORD avail;\n\t\tstatic char\ttext[256], *nl;\n\t\tstatic int textpos = 0;\n\n\t\tHANDLE input = GetStdHandle(STD_INPUT_HANDLE);\n\t\tif (!PeekNamedPipe(input, NULL, 0, NULL, &avail, NULL))\n\t\t{\n\t\t\twantquit = true;\n\t\t\tCmd_ExecuteString(\"quit force\", RESTRICT_LOCAL);\n\t\t}\n\t\telse if (avail)\n\t\t{\n\t\t\tif (avail > sizeof(text)-1-textpos)\n\t\t\t\tavail = sizeof(text)-1-textpos;\n\t\t\tif (ReadFile(input, text+textpos, avail, &avail, NULL))\n\t\t\t{\n\t\t\t\ttextpos += avail;\n\t\t\t\tif (textpos > sizeof(text)-1)\n\t\t\t\t\tSys_Error(\"No.\");\n\t\t\t}\n\t\t}\n\t\twhile (textpos)\n\t\t{\n\t\t\ttext[textpos] = 0;\n\t\t\tnl = strchr(text, '\\n');\n\t\t\tif (nl)\n\t\t\t{\n\t\t\t\t*nl++ = 0;\n\t\t\t\tif (coninput_len)\n\t\t\t\t{\n\t\t\t\t\tputch ('\\r');\n\t\t\t\t\tputch (']');\n\t\t\t\t}\n\t\t\t\tconinput_len = 0;\n\t\t\t\tQ_strncpyz(coninput_text, text, sizeof(coninput_text));\n\t\t\t\tmemmove(text, nl, textpos - (nl - text));\n\t\t\t\ttextpos -= (nl - text);\n\t\t\t\treturn coninput_text;\n\t\t\t}\n\t\t\telse\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\n\n\n\t// read a line out\n\twhile (_kbhit())\n\t{\n\t\tc = _getch();\n\t\tif (c == '\\r')\n\t\t{\n\t\t\tconinput_text[coninput_len] = 0;\n\t\t\tputch ('\\n');\n\t\t\tputch (']');\n\t\t\tconinput_len = 0;\n\t\t\treturn coninput_text;\n\t\t}\n\t\tif (c == 8)\n\t\t{\n\t\t\tif (coninput_len)\n\t\t\t{\n\t\t\t\tputch (c);\n\t\t\t\tputch (' ');\n\t\t\t\tputch (c);\n\t\t\t\tconinput_len--;\n\t\t\t\tconinput_text[coninput_len] = 0;\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\tif (c == '\\t')\n\t\t{\n\t\t\tint i;\n\t\t\tchar *s = Cmd_CompleteCommand(coninput_text, true, true, 0, NULL);\n\t\t\tif(s)\n\t\t\t{\n\t\t\t\tfor (i = 0; i < coninput_len; i++)\n\t\t\t\t\tputch('\\b');\n\t\t\t\tfor (i = 0; i < coninput_len; i++)\n\t\t\t\t\tputch(' ');\n\t\t\t\tfor (i = 0; i < coninput_len; i++)\n\t\t\t\t\tputch('\\b');\n\n\t\t\t\tstrcpy(coninput_text, s);\n\t\t\t\tconinput_len = strlen(coninput_text);\n\t\t\t\tprintf(\"%s\", coninput_text);\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\tputch (c);\n\t\tconinput_text[coninput_len] = c;\n\t\tconinput_len++;\n\t\tconinput_text[coninput_len] = 0;\n\t\tif (coninput_len == sizeof(coninput_text))\n\t\t\tconinput_len = 0;\n\t}\n\n\treturn NULL;\n}\n\nvoid ApplyColour(unsigned int chr)\n{\n\tstatic int oldchar = CON_WHITEMASK;\n\tchr &= CON_FLAGSMASK;\n\n\tif (oldchar == chr)\n\t\treturn;\n\toldchar = chr;\n\n\tif (hconsoleout)\n\t{\n\t\tunsigned short val = 0;\n\n\t\t// bits 28-31 of the console chars match up to the attributes for\n\t\t// the CHAR_INFO struct exactly\n\t\tif (chr & CON_NONCLEARBG)\n\t\t\tval = ((chr & (CON_FGMASK|CON_BGMASK)) >> CON_FGSHIFT);\n\t\telse\n\t\t{\n\t\t\tint fg = (chr & CON_FGMASK) >> CON_FGSHIFT;\n\n\t\t\tswitch (fg)\n\t\t\t{\n\t\t\tcase COLOR_BLACK: // reverse ^0 like the Linux version\n\t\t\t\tval = BACKGROUND_RED|BACKGROUND_GREEN|BACKGROUND_BLUE;\n\t\t\t\tbreak;\n\t\t\tcase COLOR_WHITE: // reset to defaults?\n\t\t\t\tval = FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_BLUE; // use grey\n\t\t\t\tbreak;\n\t\t\tcase COLOR_GREY:\n\t\t\t\tval = FOREGROUND_INTENSITY; // color light grey as dark grey\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tval = fg; // send RGBI value as is\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tif ((chr & CON_HALFALPHA) && (val & ~FOREGROUND_INTENSITY))\n\t\t\tval &= ~FOREGROUND_INTENSITY; // strip intensity to fake alpha\n\n\t\tSetConsoleTextAttribute(hconsoleout, val);\n\t}\n}\n\n//this could be much more efficient.\nstatic void Sys_PrintColouredChars(conchar_t *start, conchar_t *end)\n{\n\twchar_t wc[256];\n\tint l;\n\tDWORD dummy;\n\tunsigned int cp, flags, m;\n\n\tm = CON_WHITEMASK;\n\tl = 0;\n\n\twhile(start < end)\n\t{\n\t\tstart = Font_Decode(start, &flags, &cp);\n\n\t\tif (l+2 >= countof(wc) || flags != m)\n\t\t{\n\t\t\tApplyColour(m);\n\t\t\tif (WinNT)\n\t\t\t\tWriteConsoleW(hconsoleout, wc, l, &dummy, NULL);\n\t\t\telse\n\t\t\t{\n\t\t\t\t//win95 doesn't support wide chars *sigh*. blank consoles suck.\n\t\t\t\tchar ac[256];\n\t\t\t\tl = WideCharToMultiByte(CP_ACP, 0, wc, l, ac, sizeof(ac), NULL, NULL);\n\t\t\t\tWriteConsole(hconsoleout, ac, l, &dummy, NULL);\n\t\t\t}\n\t\t\tl = 0;\n\t\t}\n\n\t\tif (!(flags & CON_HIDDEN))\n\t\t{\n\t\t\tif (cp >= 0xe000 && cp < 0xe100)\n\t\t\t{\n\t\t\t\tcp -= 0xe000;\n\t\t\t\tif (cp >= 0x80)\n\t\t\t\t{\n\t\t\t\t\tchar c1[32] = \"---..........>  \"   \"[]0123456789.---\";\n\t\t\t\t\tcp -= 0x80;\n\t\t\t\t\tif (cp <= countof(c1))\n\t\t\t\t\t\tcp = c1[cp];\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (cp > 0xffff)\n\t\t\t\tcp = '?';\t//too lazy for utf-16 crap when its mostly smilies anyway.\n\t\t\twc[l++] = cp;\n\t\t}\n\t}\n\n\t//and flush it.\n\tif (l)\n\t{\n\t\tApplyColour(m);\n\t\tif (WinNT)\n\t\t\tWriteConsoleW(hconsoleout, wc, l, &dummy, NULL);\n\t\telse\n\t\t{\n\t\t\t//win95 doesn't support wide chars *sigh*. blank consoles suck.\n\t\t\tchar ac[256];\n\t\t\tl = WideCharToMultiByte(CP_ACP, 0, wc, l, ac, sizeof(ac), NULL, NULL);\n\t\t\tWriteConsole(hconsoleout, ac, l, &dummy, NULL);\n\t\t}\n\t}\n\n\tApplyColour(CON_WHITEMASK);\n}\n\n/*\n================\nSys_Printf\n================\n*/\n#define\tMAXPRINTMSG\t4096\nvoid Sys_Printf (char *fmt, ...)\n{\n\tva_list\t\targptr;\n\n\tif (sys_nostdout.value)\n\t\treturn;\n\n\tif (1)\n\t{\n\t\tchar\t\tmsg[MAXPRINTMSG];\n\t\tunsigned char *t;\n\n\t\tva_start (argptr,fmt);\n\t\tvsnprintf (msg,sizeof(msg)-1, fmt,argptr);\n\t\tva_end (argptr);\n\n#ifdef SUBSERVERS\n\t\tif (SSV_IsSubServer())\n\t\t{\n\t\t\tSSV_PrintToMaster(msg);\n\t\t\treturn;\n\t\t}\n#endif\n\n\t\t{\n\t\t\tint i;\n\n\t\t\tfor (i = 0; i < coninput_len; i++)\n\t\t\t\tputch('\\b');\n\t\t\tputch('\\b');\n\t\t\tfor (i = 0; i < coninput_len; i++)\n\t\t\t\tputch(' ');\n\t\t\tputch(' ');\n\t\t\tfor (i = 0; i < coninput_len; i++)\n\t\t\t\tputch('\\b');\n\t\t\tputch('\\b');\n\t\t}\n\n\t\tif (sys_colorconsole.value && hconsoleout)\n\t\t{\n\t\t\tconchar_t out[MAXPRINTMSG], *end;\n\t\t\tend = COM_ParseFunString(CON_WHITEMASK, msg, out, sizeof(out), false);\n\t\t\tSys_PrintColouredChars (out, end);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tfor (t = (unsigned char*)msg; *t; t++)\n\t\t\t{\n\t\t\t\tif (*t >= 146 && *t < 156)\n\t\t\t\t\t*t = *t - 146 + '0';\n\t\t\t\tif (*t >= 0x12 && *t <= 0x1b)\n\t\t\t\t\t*t = *t - 0x12 + '0';\n\t\t\t\tif (*t == 143)\n\t\t\t\t\t*t = '.';\n\t\t\t\tif (*t == 157 || *t == 158 || *t == 159)\n\t\t\t\t\t*t = '-';\n\t\t\t\tif (*t >= 128)\n\t\t\t\t\t*t -= 128;\n\t\t\t\tif (*t == 16)\n\t\t\t\t\t*t = '[';\n\t\t\t\tif (*t == 17)\n\t\t\t\t\t*t = ']';\n\t\t\t\tif (*t == 0x1c)\n\t\t\t\t\t*t = 249;\n\t\t\t}\n\t\t\tprintf(\"%s\", msg);\n\t\t}\n\n\n\t\tif (coninput_len)\n\t\t\tprintf(\"]%s\", coninput_text);\n\t\telse\n\t\t\tputch(']');\n\t}\n\telse\n\t{\n\t\tva_start (argptr,fmt);\n\t\tvprintf (fmt,argptr);\n\t\tva_end (argptr);\n\t}\n}\n\n/*\n================\nSys_Quit\n================\n*/\n\nvoid Sys_Quit (void)\n{\n#ifdef USESERVICE\n\tif (asservice)\n\t{\n\t\tMyServiceStatus.dwCurrentState       = SERVICE_STOPPED;\n\t\tMyServiceStatus.dwCheckPoint         = 0;\n\t\tMyServiceStatus.dwWaitHint           = 0;\n\t\tMyServiceStatus.dwWin32ExitCode      = 0;\n\t\tMyServiceStatus.dwServiceSpecificExitCode = 0;\n\n\t\tSetServiceStatus (ServerServiceStatusHandle, &MyServiceStatus);\n\t}\n#endif\n\texit (0);\n}\n\nint restorecode;\n\nLRESULT (CALLBACK Sys_WindowHandler)(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)\n{\n\tif (uMsg == WM_USER)\n\t{\n\t\tif (lParam & 1)\n\t\t{\n\t\t}\n\t\telse if ((lParam & 2 && restorecode == 0) ||\n\t\t\t(lParam & 4 && restorecode == 1) ||\n\t\t\t(lParam & 4 && restorecode == 2) )\n\t\t{\n// \t\t\tMessageBox(NULL, \"Hello\", \"\", 0);\n\t\t\trestorecode++;\n\t\t}\n\t\telse if (lParam & 2 && restorecode == 3)\n\t\t{\n\t\t\tDestroyWindow(hWnd);\n\t\t\tShowWindow(consolewindowhandle, SW_SHOWNORMAL);\n\t\t\tconsolewindowhandle = NULL;\n\n\t\t\tCbuf_AddText(\"status\\n\", RESTRICT_LOCAL);\n\t\t}\n\t\telse if (lParam & 6)\n\t\t{\n\t\t\trestorecode = (lParam & 2)>0;\n\t\t}\n\n\t\treturn 0;\n\t}\n\treturn DefWindowProc (hWnd, uMsg, wParam, lParam);\n}\nvoid Sys_HideConsole(void)\n{\n\tHMODULE kernel32dll;\n\tHWND (WINAPI *GetConsoleWindow)(void);\n\n\tif (consolewindowhandle)\n\t\treturn;\t//err... already hidden... ?\n\n\trestorecode = 0;\n\n\tGetConsoleWindow = NULL;\n\tkernel32dll = LoadLibrary(\"kernel32.dll\");\n\tconsolewindowhandle = NULL;\n\tif (kernel32dll)\n\t{\n\t\tGetConsoleWindow = (void*)GetProcAddress(kernel32dll, \"GetConsoleWindow\");\n\t\tif (GetConsoleWindow)\n\t\t\tconsolewindowhandle = GetConsoleWindow();\n\n\t\tFreeModule(kernel32dll);\t//works because the underlying code uses kernel32, so this decreases the reference count rather than closing it.\n\t}\n\n\tif (!consolewindowhandle)\n\t{\n\t\tchar old[512];\n#define STRINGH\t\"Trying to hide\"\t//msvc sucks\n\t\tGetConsoleTitle(old, sizeof(old));\n\t\tSetConsoleTitle(STRINGH);\n\t\tconsolewindowhandle = FindWindow(NULL, STRINGH);\n\t\tSetConsoleTitle(old);\n#undef STRINGH\n\t}\n\n\tif (consolewindowhandle)\n\t{\n\t\tWNDCLASS wc;\n\t\tNOTIFYICONDATA d;\n\n\t\t\t/* Register the frame class */\n\t\tmemset(&wc, 0, sizeof(wc));\n\t\twc.style         = 0;\n\t\twc.lpfnWndProc   = Sys_WindowHandler;\n\t\twc.cbClsExtra    = 0;\n\t\twc.cbWndExtra    = 0;\n\t\twc.hInstance     = GetModuleHandle(NULL);\n\t\twc.hIcon         = 0;\n\t\twc.hCursor       = LoadCursor (NULL,IDC_ARROW);\n\t\twc.hbrBackground = NULL;\n\t\twc.lpszMenuName  = 0;\n\t\twc.lpszClassName = \"DeadQuake\";\n\n\t\tRegisterClass (&wc);\n\n\t\thiddenwindowhandler = CreateWindow(wc.lpszClassName, \"DeadQuake\", 0, 0, 0, 16, 16, NULL, NULL, GetModuleHandle(NULL), NULL);\n\t\tif (!hiddenwindowhandler)\n\t\t{\n\t\t\tCon_Printf(\"Failed to create window\\n\");\n\t\t\treturn;\n\t\t}\n\t\tShowWindow(consolewindowhandle, SW_HIDE);\n\n\t\td.cbSize = sizeof(NOTIFYICONDATA);\n\t\td.hWnd = hiddenwindowhandler;\n\t\td.uFlags = NIF_ICON | NIF_TIP | NIF_MESSAGE;\n\t\td.hIcon = NULL;\n\t\td.uCallbackMessage = WM_USER;\n\t\td.uID = 0;\n\t\tstrcpy(d.szTip, \"\");\n\t\tShell_NotifyIcon(NIM_ADD, &d);\n\t}\n\telse\n\t\tCon_Printf(\"Your OS doesn't seem to properly support the way this was implemented\\n\");\n}\n\nvoid Sys_ServerActivity(void)\n{\n\tHMODULE kernel32dll;\n\tHWND (WINAPI *GetConsoleWindow)(void);\n\tHWND wnd;\n\n\trestorecode = 0;\n\n\tGetConsoleWindow = NULL;\n\tkernel32dll = LoadLibrary(\"kernel32.dll\");\n\twnd = NULL;\n\tif (kernel32dll)\n\t{\n\t\tGetConsoleWindow = (void*)GetProcAddress(kernel32dll, \"GetConsoleWindow\");\n\t\tif (GetConsoleWindow)\n\t\t\twnd = GetConsoleWindow();\n\n\t\tFreeModule(kernel32dll);\t//works because the underlying code uses kernel32, so this decreases the reference count rather than closing it.\n\t}\n\n\tif (!wnd)\n\t{\n\t\tchar old[512];\n#define STRINGF\t\"About To Flash\"\t//msvc sucks\n\t\tGetConsoleTitle(old, sizeof(old));\n\t\tSetConsoleTitle(STRINGF);\n\t\twnd = FindWindow(NULL, STRINGF);\n\t\tSetConsoleTitle(old);\n#undef STRINGF\n\t}\n\n\tif (wnd && GetActiveWindow() != wnd)\n\t\tFlashWindow(wnd, true);\n}\n\n/*\n=============\nSys_Init\n\nQuake calls this so the system can register variables before host_hunklevel\nis marked\n=============\n*/\nvoid Sys_Init (void)\n{\n\tCvar_Register (&sys_nostdout, \"System controls\");\n\tCvar_Register (&sys_colorconsole, \"System controls\");\n\n\tCmd_AddCommand(\"hide\", Sys_HideConsole);\n\n\tif (isPlugin)\n\t\thconsoleout = CreateFileA(\"CONOUT$\",GENERIC_READ|GENERIC_WRITE,FILE_SHARE_READ,0,OPEN_EXISTING,0,0);\n\telse\n\t\thconsoleout = GetStdHandle(STD_OUTPUT_HANDLE);\n\n//\tSetConsoleCP(CP_UTF8);\n//\tSetConsoleOutputCP(CP_UTF8);\n}\n\nvoid Sys_Shutdown (void)\n{\n}\n\n/*\n==================\nmain\n\n==================\n*/\nchar\t*newargv[256];\n\nvoid Signal_Error_Handler (int sig)\n{\n\tSys_Error(\"Illegal error occured\");\n}\n\n\nvoid StartQuakeServer(void)\n{\n\tquakeparms_t\tparms;\n\tstatic\tchar\tbindir[MAX_OSPATH];\n\tint c;\n\n\tmemset(&parms, 0, sizeof(parms));\n\n\tparms.argc = com_argc;\n\tparms.argv = com_argv;\n\n\tGetModuleFileName(NULL, bindir, sizeof(bindir)-1);\n\t*COM_SkipPath(bindir) = 0;\n\tparms.binarydir = bindir;\n\n\tparms.basedir = \"./\";\n\n\tTL_InitLanguages(parms.basedir);\n\n\tc = COM_CheckParm(\"-qcdebug\");\n\tif (c)\n\t\tisPlugin = 3;\n\telse\n\t{\n\t\tc = COM_CheckParm(\"-plugin\");\n\t\tif (c)\n\t\t{\n\t\t\tif (c < com_argc && !strcmp(com_argv[c+1], \"qcdebug\"))\n\t\t\t\tisPlugin = 2;\n\t\t\telse\n\t\t\t\tisPlugin = 1;\n\t\t}\n\t\telse\n\t\t\tisPlugin = 0;\n\t}\n\n\tSV_Init (&parms);\n\n// run one frame immediately for first heartbeat\n\tSV_Frame ();\n}\n\n\n#ifdef USESERVICE\nint servicecontrol;\n#endif\nvoid ServerMainLoop(void)\n{\n\tfloat delay = 0.001;\n//\n// main loop\n//\n\twhile (1)\n\t{\n\t\tNET_Sleep(delay, false);\n\n\t// find time passed since last cycle\n\t\tdelay = SV_Frame();\n\n\n#ifdef USESERVICE\n\t\tswitch(servicecontrol)\n\t\t{\n\t\tcase SERVICE_CONTROL_PAUSE:\n\t\t\t// Initialization complete - report running status.\n\t\t\tMyServiceStatus.dwCurrentState       = SERVICE_PAUSED;\n\t\t\tMyServiceStatus.dwCheckPoint         = 0;\n\t\t\tMyServiceStatus.dwWaitHint           = 0;\n\n\t\t\tSetServiceStatus (ServerServiceStatusHandle, &MyServiceStatus);\n\t\t\tsv.paused |= PAUSE_SERVICE;\n\t\t\tbreak;\n\t\tcase SERVICE_CONTROL_CONTINUE:\n\t\t\t// Initialization complete - report running status.\n\t\t\tMyServiceStatus.dwCurrentState       = SERVICE_RUNNING;\n\t\t\tMyServiceStatus.dwCheckPoint         = 0;\n\t\t\tMyServiceStatus.dwWaitHint           = 0;\n\n\t\t\tSetServiceStatus (ServerServiceStatusHandle, &MyServiceStatus);\n\n\t\t\tsv.paused &= ~PAUSE_SERVICE;\n\t\t\tbreak;\n\t\tcase SERVICE_CONTROL_STOP:\t//leave the loop\n\t\t\treturn;\n\t\tdefault:\n\t\t\tbreak;\n\t\t}\n#endif\n\t}\n}\n#ifdef USESERVICE\n\nVOID WINAPI MyServiceCtrlHandler(DWORD    dwControl)\n{\n\tservicecontrol = dwControl;\n}\n\nvoid WINAPI StartQuakeServerService\t(DWORD argc, LPTSTR *argv)\n{\n\tHKEY hk;\n\tchar path[MAX_OSPATH];\n\tDWORD pathlen;\n\tDWORD type;\n\n\tasservice = true;\n\n\tMyServiceStatus.dwServiceType        = SERVICE_WIN32|SERVICE_INTERACTIVE_PROCESS;\n\tMyServiceStatus.dwCurrentState       = SERVICE_START_PENDING;\n\tMyServiceStatus.dwControlsAccepted   = SERVICE_ACCEPT_STOP |\n\t\tSERVICE_ACCEPT_PAUSE_CONTINUE;\n\tMyServiceStatus.dwWin32ExitCode      = 0;\n\tMyServiceStatus.dwServiceSpecificExitCode = 0;\n\tMyServiceStatus.dwCheckPoint         = 0;\n\tMyServiceStatus.dwWaitHint           = 0;\n\n\tServerServiceStatusHandle = RegisterServiceCtrlHandler(\n\t\tSERVICENAME,\n\t\tMyServiceCtrlHandler);\n\n\tif (ServerServiceStatusHandle == (SERVICE_STATUS_HANDLE)0)\n\t{\n\t\tprintf(\" [MY_SERVICE] RegisterServiceCtrlHandler failed %d\\n\", GetLastError());\n\t\treturn;\n\t}\n\n\n\tRegOpenKey(HKEY_LOCAL_MACHINE, \"Software\\\\\"DISTRIBUTIONLONG\"\\\\\"FULLENGINENAME, &hk);\n\tRegQueryValueEx(hk, \"servicepath\", 0, &type, NULL, &pathlen);\n\tif (type == REG_SZ && pathlen < sizeof(path))\n\t\tRegQueryValueEx(hk, \"servicepath\", 0, NULL, path, &pathlen);\n\tRegCloseKey(hk);\n\n\tSetCurrentDirectory(path);\n\n\n\tCOM_InitArgv (argc, argv);\n\tStartQuakeServer();\n\n\n\t// Handle error condition\n\tif (!sv.state)\n\t{\n\t\tMyServiceStatus.dwCurrentState       = SERVICE_STOPPED;\n\t\tMyServiceStatus.dwCheckPoint         = 0;\n\t\tMyServiceStatus.dwWaitHint           = 0;\n\t\tMyServiceStatus.dwWin32ExitCode      = 0;\n\t\tMyServiceStatus.dwServiceSpecificExitCode = 0;\n\n\t\tSetServiceStatus (ServerServiceStatusHandle, &MyServiceStatus);\n\t\treturn;\n\t}\n\n\t// Initialization complete - report running status.\n\tMyServiceStatus.dwCurrentState       = SERVICE_RUNNING;\n\tMyServiceStatus.dwCheckPoint         = 0;\n\tMyServiceStatus.dwWaitHint           = 0;\n\n\tif (!SetServiceStatus (ServerServiceStatusHandle, &MyServiceStatus))\n\t{\n\t\tprintf(\" [MY_SERVICE] SetServiceStatus error %ld\\n\",GetLastError());\n\t}\n\n\tServerMainLoop();\n\n\tMyServiceStatus.dwCurrentState       = SERVICE_STOPPED;\n\tMyServiceStatus.dwCheckPoint         = 0;\n\tMyServiceStatus.dwWaitHint           = 0;\n\tMyServiceStatus.dwWin32ExitCode      = 0;\n\tMyServiceStatus.dwServiceSpecificExitCode = 0;\n\n\tSetServiceStatus (ServerServiceStatusHandle, &MyServiceStatus);\n\n\treturn;\n}\n\nSERVICE_TABLE_ENTRY   DispatchTable[] =\n{\n{ SERVICENAME, StartQuakeServerService      },\n{ NULL,              NULL          }\n};\n#endif\n\nint main (int argc, char **argv)\n{\n#ifdef CATCHCRASH\n\tLoadLibrary (\"DBGHELP\");\t//heap corruption can prevent loadlibrary from working properly, so do this in advance.\n#ifdef _MSC_VER\n\t__try\n#else\n\tAddVectoredExceptionHandler(true, nonmsvc_CrashExceptionHandler);\n#endif\n#endif\n\t{\n\t\tCOM_InitArgv (argc, (const char **)argv);\n\n#ifdef SUBSERVERS\n\t\tisClusterSlave = COM_CheckParm(\"-clusterslave\");\n\t\tif (isClusterSlave)\n            SSV_SetupControlPipe(Sys_GetStdInOutStream(), false);\n#endif\n#ifdef USESERVICE\n\t\tif (!SSV_IsSubServer() && StartServiceCtrlDispatcher( DispatchTable))\n\t\t{\n\t\t\treturn true;\n\t\t}\n#endif\n\n\t#ifdef USESERVICE\n\t\tif (COM_CheckParm(\"-register\"))\n\t\t{\n\t\t\tCreateSampleService(1);\n\t\t\treturn true;\n\t\t}\n\t\tif (COM_CheckParm(\"-unregister\"))\n\t\t{\n\t\t\tCreateSampleService(0);\n\t\t\treturn true;\n\t\t}\n\t#endif\n\n\t#ifndef _DEBUG\n\t\tif (COM_CheckParm(\"-noreset\"))\n\t\t{\n\t\t\tsignal (SIGFPE,\tSignal_Error_Handler);\n\t\t\tsignal (SIGILL,\tSignal_Error_Handler);\n\t\t\tsignal (SIGSEGV,\tSignal_Error_Handler);\n\t\t}\n\t#endif\n\n\t\tStartQuakeServer();\n\n\t\tServerMainLoop();\n\t}\n#ifdef CATCHCRASH\n#ifdef _MSC_VER\n\t__except (CrashExceptionHandler(false, GetExceptionCode(), GetExceptionInformation()))\n\t{\n\t\treturn 1;\n\t}\n#endif\n#endif\n\n\treturn true;\n}\n\n#ifdef USESERVICE\nvoid CreateSampleService(qboolean create)\n{\n\tBOOL deleted;\n\tchar path[MAX_OSPATH];\n\tchar exe[MAX_OSPATH];\n\tSC_HANDLE schService;\n\n\tSC_HANDLE schSCManager;\n\n// Open a handle to the SC Manager database.\n\tschSCManager = OpenSCManager(\n\t\tNULL,                    // local machine\n\t\tNULL,                    // ServicesActive database\n\t\tSC_MANAGER_ALL_ACCESS);  // full access rights\n\n\tif (NULL == schSCManager)\n\t{\n\t\tCon_Printf(\"Failed to open SCManager (%d)\\n\", GetLastError());\n\t\treturn;\n\t}\n\n\tif (!GetModuleFileName(NULL, exe+1, sizeof(exe)-2))\n\t{\n\t\tCon_Printf(\"Path too long\\n\");\n\t\treturn;\n\t}\n\tGetCurrentDirectory(sizeof(path), path);\n\texe[0] = '\\\"';\n\texe[strlen(path)+1] = '\\0';\n\texe[strlen(path)] = '\\\"';\n\n\tif (!create)\n\t{\n\t\tschService = OpenServiceA(schSCManager, SERVICENAME, SERVICE_ALL_ACCESS);\n\t\tif (schService)\n\t\t{\n\t\t\tdeleted = DeleteService(schService);\n\t\t}\n\t}\n\telse\n\t{\n\t\tHKEY hk;\n\t\tRegOpenKey(HKEY_LOCAL_MACHINE, \"Software\\\\\"DISTRIBUTIONLONG\"\\\\\"FULLENGINENAME, &hk);\n\t\tif (!hk)RegCreateKey(HKEY_LOCAL_MACHINE, \"Software\\\\\"DISTRIBUTIONLONG\"\\\\\"FULLENGINENAME, &hk);\n\t\tRegSetValueEx(hk, \"servicepath\", 0, REG_SZ, path, strlen(path));\n\t\tRegCloseKey(hk);\n\n\t\tschService = CreateService(\n\t\t\tschSCManager,\t\t\t\t// SCManager database\n\t\t\tSERVICENAME,\t\t\t\t// name of service\n\t\t\tFULLENGINENAME\" Server\",\t// service name to display\n\t\t\tSERVICE_ALL_ACCESS,\t\t\t// desired access\n\t\t\tSERVICE_WIN32_OWN_PROCESS|SERVICE_INTERACTIVE_PROCESS,\t// service type\n\t\t\tSERVICE_AUTO_START,\t\t\t// start type\n\t\t\tSERVICE_ERROR_NORMAL,\t\t// error control type\n\t\t\texe,\t\t\t\t\t\t// service's binary\n\t\t\tNULL,\t\t\t\t\t\t// no load ordering group\n\t\t\tNULL,\t\t\t\t\t\t// no tag identifier\n\t\t\tNULL,\t\t\t\t\t\t// no dependencies\n\t\t\tNULL,\t\t\t\t\t\t// LocalSystem account\n\t\t\tNULL);\t\t\t\t\t\t// no password\n\t}\n\n    if (schService == NULL)\n    {\n        Con_Printf(\"CreateService failed.\\n\");\n        return;\n    }\n    else\n    {\n        CloseServiceHandle(schService);\n        return;\n    }\n}\n#endif\n\nvoid Sys_Sleep (double seconds)\n{\n\tSleep(seconds * 1000);\n}\n\n/*\n================\nSys_RandomBytes\n================\n*/\n#include <wincrypt.h>\nqboolean Sys_RandomBytes(qbyte *string, int len)\n{\n\tHCRYPTPROV  prov;\n\n\tif(!CryptAcquireContext( &prov, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT))\n\t{\n\t\treturn false;\n\t}\n\n\tif(!CryptGenRandom(prov, len, (BYTE *)string))\n\t{\n\t\tCryptReleaseContext( prov, 0);\n\t\treturn false;\n\t}\n\tCryptReleaseContext(prov, 0);\n\treturn true;\n}\n\n\n#ifdef HAVEAUTOUPDATE\nint Sys_GetAutoUpdateSetting(void)\n{\n\treturn -1;\n}\nvoid Sys_SetAutoUpdateSetting(int newval)\n{\n}\n#endif\n\n#ifdef WEBCLIENT\nqboolean Sys_RunInstaller(void)\n{\t//not implemented\n\treturn false;\n}\n#endif\n#endif\n"
  },
  {
    "path": "engine/server/sv_user.c",
    "content": "/*\nCopyright (C) 1996-1997 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n*/\n// sv_user.c -- server code for moving users\n\n#include \"quakedef.h\"\n#include \"fs.h\"\n\n#ifndef CLIENTONLY\n#include \"pr_common.h\"\n\n#include <ctype.h>\n#define Q2EDICT_NUM(i) (q2edict_t*)((char *)ge->edicts+i*ge->edict_size)\n#define Q2NUM_FOR_EDICT(ent) (((char *)ent - (char *)ge->edicts) / ge->edict_size)\nhull_t *SV_HullForEntity (edict_t *ent, int hullnum, vec3_t mins, vec3_t maxs, vec3_t offset);\n\nedict_t\t*sv_player;\n\nstatic usercmd_t\tcmd;\n\nvoid QDECL SV_NQPhysicsUpdate(cvar_t *var, char *oldvalue)\n{\n\tif (svs.gametype != GT_PROGS)\n\t\tvar->ival = 0;\t//nope, not a thing.\n\telse\n\t{\n\t\tif (!strcmp(var->string, \"auto\") || !strcmp(var->string, \"\"))\n\t\t{\t//prediction requires nq physics, so use it by default in multiplayer.\n\t\t\tif ((svprogfuncs&&PR_FindFunction(svprogfuncs, \"SV_RunClientCommand\", PR_ANY)))\n\t\t\t\tvar->ival = 0;\t//mods that use explicit custom player physics/pred ALWAYS want qw-like physics (just hope noone forces it off)\n\t\t\telse if ((svprogfuncs&&PR_FindFunction(svprogfuncs, \"SV_PlayerPhysics\", PR_ANY)))\n\t\t\t\tvar->ival = 1;\t//DP mods wanting DP's weird physics also suffer NQ behaviours\n\t\t\telse if (\tprogstype <= PROG_QW ||\t//none or qw use qw physics by default\n\t\t\t\t\t(!isDedicated &&  sv.allocated_client_slots > 1))\t//multiplayer dedicated servers use qw physics for nq mods too. server admins are expected to be able to spend a little more time to configure things properly.\n\t\t\t\tvar->ival = 0;\n\t\t\telse\n\t\t\t\tvar->ival = 1;\n\t\t}\n\t}\n}\n\nextern cvar_t dpcompat_nopreparse;\n#ifdef SERVERONLY\ncvar_t\tcl_rollspeed = CVAR(\"cl_rollspeed\", \"200\");\ncvar_t\tcl_rollangle = CVAR(\"cl_rollangle\", \"2.0\");\n#else\nextern cvar_t\tcl_rollspeed;\nextern cvar_t\tcl_rollangle;\n#endif\ncvar_t\tsv_spectalk\t= CVAR(\"sv_spectalk\", \"1\");\n\n#define nqcompat_spawnbeforeready 1\n\ncvar_t\tsv_mapcheck\t= CVAR(\"sv_mapcheck\", \"1\");\n\ncvar_t\tsv_fullredirect = CVARD(\"sv_fullredirect\", \"\", \"This is the ip:port to redirect players to when the server is full\");\ncvar_t\tsv_antilag\t\t\t= CVARFD(\"sv_antilag\", \"\", CVAR_SERVERINFO, \"Attempt to backdate impacts to compensate for lag via the MOVE_ANTILAG feature.\\n0=completely off.\\n1=mod-controlled (default).\\n2=forced, which might break certain uses of traceline.\\n3=Also attempt to recalculate trace start positions to avoid lagged knockbacks.\");\ncvar_t\tsv_antilag_frac\t\t= CVARF(\"sv_antilag_frac\", \"\", CVAR_SERVERINFO);\ncvar_t\tsv_showpredloss\t\t= CVARD(\"sv_showpredloss\", \"0\", \"Print messages whenever input frames are ignored or forced serverside, to prevent speedcheats or hover cheats. Any such prints will be accompanied by prediction misses in the named client.\");\n#ifndef NEWSPEEDCHEATPROT\ncvar_t\tsv_cheatpc\t\t\t\t= CVARD(\"sv_cheatpc\", \"125\", \"If the client tried to claim more than this percentage of time within any speed-cheat period, the client will be deemed to have cheated.\");\ncvar_t\tsv_cheatspeedchecktime\t= CVARD(\"sv_cheatspeedchecktime\", \"30\", \"The interval between each speed-cheat check.\");\n#endif\ncvar_t\tsv_playermodelchecks\t= CVAR(\"sv_playermodelchecks\", \"0\");\ncvar_t\tsv_ping_ignorepl\t\t= CVARD(\"sv_ping_ignorepl\", \"0\", \"If 1, ping times reported for players will ignore the effects of packetloss on ping times. 0 is slightly more honest, but less useful for connection diagnosis.\");\ncvar_t\tsv_protocol_nq\t\t\t= CVARD(\"sv_protocol_nq\", \"\", \"Specifies the default protocol to use for new NQ clients. This is only relevent for clients that do not report their supported protocols. Supported values are\\n0 = autodetect\\n15 = vanilla\\n666 = fitzquake\\n999 = rmq protocol\\nThe sv_bigcoords cvar forces upgrades as required.\");\n\ncvar_t\tsv_minpitch\t\t = CVARAFD(\"minpitch\", \"\",\t\"sv_minpitch\", CVAR_SERVERINFO, \"Assumed to be -70\");\ncvar_t\tsv_maxpitch\t\t = CVARAFD(\"maxpitch\", \"\",\t\"sv_maxpitch\", CVAR_SERVERINFO, \"Assumed to be 80\");\n\ncvar_t\tsv_cmdlikercon\t= CVAR(\"sv_cmdlikercon\", \"0\");\t//set to 1 to allow a password of username:password instead of the correct rcon password.\ncvar_t cmd_allowaccess\t= CVAR(\"cmd_allowaccess\", \"0\");\t//set to 1 to allow cmd to execute console commands on the server.\ncvar_t cmd_gamecodelevel\t= CVARF(\"cmd_gamecodelevel\", STRINGIFY(RESTRICT_LOCAL), CVAR_NOTFROMSERVER);\t//execution level which gamecode is told about (for unrecognised commands)\n\ncvar_t\tsv_pure\t= CVARFD(\"sv_pure\", \"\", CVAR_SERVERINFO, \"The most evil cvar in the world, many clients will ignore this.\\n0=standard quake rules.\\n1=clients should prefer files within packages present on the server.\\n2=clients should use *only* files within packages present on the server.\\nDue to quake 1.01/1.06 differences, a setting of 2 only works in total conversions.\");\ncvar_t\tsv_nqplayerphysics\t= CVARAFCD(\"sv_nqplayerphysics\", \"auto\", \"sv_nomsec\", CVAR_ARCHIVE, SV_NQPhysicsUpdate, \"Disable player prediction and run NQ-style player physics instead. This can be used for compatibility with mods that expect exact behaviour. A value of 2 will not block prediction, and may be juddery/jerky/swimmy.\");\n\n#ifdef HAVE_LEGACY\nstatic cvar_t\tsv_brokenmovetypes\t= CVARD(\"sv_brokenmovetypes\", \"0\", \"Emulate vanilla quakeworld by forcing MOVETYPE_WALK on all players. Shouldn't be used for any games other than QuakeWorld.\");\ncvar_t pext_ezquake_nochunks\t= CVARD(\"pext_ezquake_nochunks\", \"0\", \"Prevents ezquake clients from being able to use the chunked download extension. This sidesteps numerous ezquake issues, and will make downloads slower but more robust.\");\ncvar_t pext_ezquake_verfortrans\t= CVARD(\"pext_ezquake_verfortrans\", \"7088\"/*Reki's revision at least*/, \"ezQuake does not implement PEXT_TRANS properly. This is the version of ezquake required for PEXT_TRANS to be allowed. This was still broken when I wrote this description, hence the large value.\");\n#endif\n\ncvar_t\tsv_chatfilter\t= CVAR(\"sv_chatfilter\", \"0\");\n\ncvar_t\tsv_floodprotect\t\t\t\t= CVAR(\"sv_floodprotect\", \"1\");\ncvar_t\tsv_floodprotect_messages\t= CVAR(\"sv_floodprotect_messages\", \"4\");\ncvar_t\tsv_floodprotect_interval\t= CVAR(\"sv_floodprotect_interval\", \"4\");\ncvar_t  sv_floodprotect_silencetime\t= CVAR(\"sv_floodprotect_silencetime\", \"10\");\ncvar_t\tsv_floodprotect_suicide\t\t= CVAR(\"sv_floodprotect_suicide\", \"1\");\ncvar_t\tsv_floodprotect_sendmessage\t= CVARAF(\"sv_floodprotect_sendmessage\", \"\",\n\t\t\t\t\t\t\t\t\t\t\t \"floodprotmsg\", 0);\n\ncvar_t\tvotelevel\t= CVARD(\"votelevel\", \"0\", \"This is the restriction level of commands that players may vote for. You can reconfigure commands, cvars, or aliases individually. Additionally, aliases can be configured via aliaslevel to be executed at a different level from their restriction level. This can be used to indirectly allow voting for 'map dm4' for instance, without allowing people to vote for every map.\");\ncvar_t\tvoteminimum\t= CVARD(\"voteminimum\", \"4\", \"At least this many players must vote the same way for the vote to pass.\");\ncvar_t\tvotepercent\t= CVARD(\"votepercent\", \"-1\", \"At least this percentage of players must vote the same way for the vote to pass.\");\ncvar_t\tvotetime\t= CVARD(\"votetime\", \"10\", \"Votes will be discarded after this many minutes\");\n\ncvar_t\tpr_allowbutton1 = CVARFD(\"pr_allowbutton1\", \"1\", CVAR_MAPLATCH, \"The button1 field is believed to have been intended to work with the +use command, but it was never hooked up. In NetQuake, this field was often repurposed for other things as it was not otherwise used (and cannot be removed without breaking the crc), while third-party QuakeWorld engines did decide to implement it as believed was intended. As a result, this cvar only applies to QuakeWorld mods and a value of 1 is only likely to cause issues with NQ mods that were ported to QW.\");\nextern cvar_t sv_minping;\n\n\nextern cvar_t\tpm_bunnyspeedcap;\nextern cvar_t\tpm_ktjump;\nextern cvar_t\tpm_slidefix;\nextern cvar_t\tpm_slidyslopes;\nextern cvar_t\tpm_bunnyfriction;\nextern cvar_t\tpm_autobunny;\nextern cvar_t\tpm_airstep;\nextern cvar_t\tpm_pground;\nextern cvar_t\tpm_stepdown;\nextern cvar_t\tpm_walljump;\nextern cvar_t\tpm_watersinkspeed;\nextern cvar_t\tpm_flyfriction;\nextern cvar_t\tpm_edgefriction;\ncvar_t sv_pushplayers = CVAR(\"sv_pushplayers\", \"0\");\n\n//yes, realip cvars need to be fully initialised or realip will be disabled\ncvar_t sv_getrealip = CVARD(\"sv_getrealip\", \"0\", \"Attempt to obtain a more reliable IP for clients, rather than just their proxy.\\n0: Don't attempt.\\n1: Unreliable checks.\\n2: Validate if possible.\\n3: Mandatory validation.\");\ncvar_t sv_realip_kick = CVARD(\"sv_realip_kick\", \"0\", \"Kicks clients if their realip could not be validated to the level specified by sv_getrealip.\");\ncvar_t sv_realiphostname_ipv4 = CVARD(\"sv_realiphostname_ipv4\", \"\", \"This is the server's public ip:port. This is needed for realip to work when the autodetected/local ip is not globally routable\");\ncvar_t sv_realiphostname_ipv6 = CVARD(\"sv_realiphostname_ipv6\", \"\", \"This is the server's public ip:port. This is needed for realip to work when the autodetected/local ip is not globally routable\");\ncvar_t sv_realip_timeout = CVAR(\"sv_realip_timeout\", \"10\");\n\ncvar_t sv_userinfo_keylimit = CVARD(\"sv_userinfo_keylimit\", \"128\", \"This is the maximum number of userinfo keys each user may create.\");\ncvar_t sv_userinfo_bytelimit = CVARD(\"sv_userinfo_bytelimit\", \"8192\", \"This is the maximum number of bytes that may be stored into each user's userinfo. Note that this includes key names too.\");\n\n#ifdef VOICECHAT\ncvar_t sv_voip = CVARD(\"sv_voip\", \"1\", \"Enable reception of voice packets.\");\ncvar_t sv_voip_record = CVARD(\"sv_voip_record\", \"0\", \"Record voicechat into mvds. Requires player support. 0=noone, 1=everyone, 2=spectators only\");\ncvar_t sv_voip_echo = CVARD(\"sv_voip_echo\", \"0\", \"Echo voice packets back to their sender, a debug/test setting.\");\n#endif\n\nchar sv_votinggroup[] = \"server voting\";\n\n\nextern char cvargroup_serverpermissions[];\nextern char cvargroup_serverinfo[];\nextern char cvargroup_serverphysics[];\nextern char cvargroup_servercontrol[];\n\nextern cvar_t pausable;\n\n/*\n============================================================\n\nUSER STRINGCMD EXECUTION\n\nhost_client and sv_player will be valid.\n============================================================\n*/\n\nqboolean SV_CheckRealIP(client_t *client, qboolean force)\n{\n\t//returns true if they have a real ip\n\tchar *serverip;\n\tchar *msg;\n\n\tif (sv_getrealip.value <= client->realip_status || sv_getrealip.value > 3)\n\t\treturn true;\n\n\tif (client->netchan.remote_address.type == NA_LOOPBACK)\n\t\treturn true;\t//the loopback client doesn't have to pass realip checks\n\n\tif (client->realip_status == 3)\n\t\treturn true;\t//we know that the ip is authentic\n\tif (client->realip_status == 2)\n\t{\n\t\tSV_PrintToClient(client, PRINT_HIGH, \"Couldn't verify your real ip\\n\");\n\t\treturn true;\t//client doesn't support certainty.\n\t}\n\tif (client->realip_status == -1)\n\t\treturn true;\t//this client timed out.\n\n\t//if they're using some weird protocol just give up right away.\n\tif (realtime - client->connection_started > sv_realip_timeout.value ||\n\t\tclient->netchan.remote_address.prot != NP_DGRAM || !(\n\t\t(client->netchan.remote_address.type == NA_IP&&*sv_realiphostname_ipv4.string) ||\n\t\t(client->netchan.remote_address.type == NA_IPV6&&sv_realiphostname_ipv6.string)))\n\t{\n\t\tif (client->realip_status > 0)\n\t\t\tSV_PrintToClient(client, PRINT_HIGH, \"Couldn't verify your real ip\\n\");\n\t\telse\n\t\t\tSV_PrintToClient(client, PRINT_HIGH, \"Couldn't determine your real ip\\n\");\n\t\tif (sv_realip_kick.value > host_client->realip_status)\n\t\t{\n\t\t\tclient->drop = true;\n\t\t\treturn false;\n\t\t}\n\t\tif (!client->realip_status)\n\t\t\tclient->realip_status = -1;\n\t\treturn true;\n\t}\n\n\n\tif (client->realip_status == 1)\n\t{\n\t\tmsg = va(\"\\xff\\xff\\xff\\xff%c %i\", A2A_PING, client->realip_ping);\n\t\tNET_SendPacket(svs.sockets, strlen(msg), msg, &client->realip);\n\t}\n\telse\n\t{\n\t\tif (client->netchan.remote_address.type == NA_IPV6)\n\t\t{\n\t\t\tserverip = sv_realiphostname_ipv6.string;\n//\t\t\tserverip = NET_AdrToString (net_local_sv_ip6adr);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tserverip = sv_realiphostname_ipv4.string;\n//\t\t\tserverip = NET_AdrToString (net_local_sv_ipadr);\n\t\t}\n\n\t\tif (!*serverip)\n\t\t{\n\t\t\tCon_Printf(\"realip not fully configured\\n\");\n\t\t\tclient->realip_status = -1;\n\t\t\treturn true;\n\t\t}\n\n\t\tClientReliableWrite_Begin(client, svc_stufftext, 256);\n\t\tClientReliableWrite_String(client, va(\"packet %s \\\"realip %i %i\\\"\\n\", serverip, (int)(client-svs.clients), client->realip_num));\n\t}\n\treturn false;\n}\n\n/*\n================\nSV_New_f\n\nSends the first message from the server to a connected client.\nThis will be sent on the initial connection and upon each server load.\n================\n*/\nvoid SV_New_f (void)\n{\n\tchar\t\t*gamedir;\n\tint\t\t\tplayernum;\n\tint splitnum;\n\tclient_t *split;\n\tunsigned int fteext1, fteext2, ezext1, vers;\t//reported to client\n\n\thost_client->prespawn_stage = PRESPAWN_INVALID;\n\thost_client->prespawn_idx = 0;\n\n\thost_client->isindependant = false;\n\n\tif (host_client->state == cs_spawned)\n\t\treturn;\n\n\tif (host_client->redirect)\n\t{\n\t\tif (host_client->redirect == 1)\n\t\t{\n\t\t\tchar *msg = va(\"connect \\\"%s\\\"\\n\", sv_fullredirect.string);\n\t\t\tClientReliableWrite_Begin (host_client, svc_stufftext, 2+strlen(msg));\n\t\t\tClientReliableWrite_String (host_client, msg);\n\t\t}\n\t\treturn;\n\t}\n\telse\n\t{\n\t\tconst char *srv = InfoBuf_ValueForKey(&host_client->userinfo, \"*redirect\");\n\t\tif (*srv)\n\t\t{\n\t\t\tchar *msg = va(\"connect \\\"%s\\\"\\n\", srv);\n\t\t\tClientReliableWrite_Begin (host_client, svc_stufftext, 2+strlen(msg));\n\t\t\tClientReliableWrite_String (host_client, msg);\n\t\t\treturn;\n\t\t}\n\t}\n\n\tif (dpcompat_nopreparse.ival && progstype != PROG_QW)\n\t{\n\t\tSV_PrintToClient(host_client, PRINT_HIGH, \"This server has network preparsing disabled, and thus only supports NetQuake clients\\n\");\n\t\tCon_Printf(\"%s was not using NQ protocols\\n\", host_client->name);\n\t\thost_client->drop = true;\n\t\treturn;\n\t}\n\n\tif (!host_client->pextknown && host_client->zquake_extensions && host_client->netchan.remote_address.type != NA_LOOPBACK)\n\t{\n\t\tchar *msg = \"cmd pext\\n\";\n\t\tClientReliableWrite_Begin (host_client, svc_stufftext, 2+strlen(msg));\n\t\tClientReliableWrite_String (host_client, msg);\n\t\treturn;\n\t}\n\thost_client->pextknown = true;\n\n\tgamedir = InfoBuf_ValueForKey (&svs.info, \"*gamedir\");\n\tif (!gamedir[0])\n\t{\n\t\tif (ISQWCLIENT(host_client) || ISQ2CLIENT(host_client))\n\t\t\tgamedir = FS_GetGamedir(true);\n\t\telse\n\t\t\tgamedir = \"\";\n\t}\n\n\tfteext1 = host_client->fteprotocolextensions;\n\tfteext2 = host_client->fteprotocolextensions2;\n\tezext1 = host_client->ezprotocolextensions1;\n\n\tif (ISQ2CLIENT(host_client))\n\t{\n\t\tif (host_client->protocol==SCP_QUAKE2EX)\n\t\t{\n//\t\t\tif (svs.netprim.coordtype == COORDTYPE_FLOAT_32)\n\t\t\t\tvers = PROTOCOL_VERSION_Q2EX;\n//\t\t\telse\n//\t\t\t\tvers = PROTOCOL_VERSION_Q2EXDEMO;\t//has some explicitly bigger sizes without using generic primatives.\n\t\t}\n\t\telse\n\t\t\tvers = PROTOCOL_VERSION_Q2;\n\t}\n\telse\n\t\tvers = PROTOCOL_VERSION_QW;\n\n\tswitch(svs.netprim.coordtype)\n\t{\n\tcase COORDTYPE_FLOAT_32:\n\t\tif (host_client->protocol==SCP_QUAKE2EX)\n\t\t\tfteext1 &= ~PEXT_FLOATCOORDS;\n\t\telse\n\t\t{\n\t\t\tfteext1 |= PEXT_FLOATCOORDS;\n\t\t\tezext1 &= ~EZPEXT1_FLOATENTCOORDS; //redundant.\n\t\t\tif (!(host_client->fteprotocolextensions & PEXT_FLOATCOORDS))\n\t\t\t{\n\t\t\t\tSV_ClientPrintf(host_client, 2, \"\\nForcing bigcoords.\\nIf this doesn't work, please update your engine\\n\");\n\t\t\t\thost_client->fteprotocolextensions |= PEXT_FLOATCOORDS;\n//\t\t\t\tCon_Printf(\"%s does not support bigcoords\\n\", host_client->name);\n//\t\t\t\thost_client->drop = true;\n//\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tbreak;\n\tcase COORDTYPE_FIXED_13_3:\n\t\tfteext1 &= ~PEXT_FLOATCOORDS;\n\t\tbreak;\n\tdefault:\n\t\tSV_ClientPrintf(host_client, 2, \"Unsupported coord type\\n\");\n\t\tCon_Printf(\"%s unsupported coord type\\n\", host_client->name);\n\t\thost_client->drop = true;\n\t\treturn;\n\t}\n\tfteext2 &= ~PEXT2_STUNAWARE;\t//don't complicate demos.\n\n\tClientReliableCheckBlock(host_client, 800);\t//okay, so it might be longer, but I'm too lazy to work out the real size.\n\n\t// send the serverdata\n\tClientReliableWrite_Byte (host_client, ISQ2CLIENT(host_client)?svcq2_serverdata:svc_serverdata);\n\tif (fteext1)//let the client know\n\t{\n\t\tClientReliableWrite_Long (host_client, PROTOCOL_VERSION_FTE1);\n\t\tClientReliableWrite_Long (host_client, fteext1);\n\t}\n\tif (fteext2)//let the client know\n\t{\n\t\tClientReliableWrite_Long (host_client, PROTOCOL_VERSION_FTE2);\n\t\tClientReliableWrite_Long (host_client, fteext2);\n\t}\n\tif (ezext1)//let the client know\n\t{\n\t\tClientReliableWrite_Long (host_client, PROTOCOL_VERSION_EZQUAKE1);\n\t\tClientReliableWrite_Long (host_client, ezext1);\n\t}\n\tClientReliableWrite_Long (host_client, vers);\n\tClientReliableWrite_Long (host_client, svs.spawncount);\n\tif (ISQ2CLIENT(host_client))\n\t{\n\t\tClientReliableWrite_Byte (host_client, 0);\t//demo mode.\n\t\tif (host_client->protocol==SCP_QUAKE2EX)\n\t\t\tClientReliableWrite_Byte (host_client, 10);\t//tick rate.\n\t}\n\tClientReliableWrite_String (host_client, gamedir);\n\n\tif (fteext2 & PEXT2_MAXPLAYERS)\n\t{\n\t\t/*is this a sane way to do it? or should we split the spectator thing off entirely?*/\n\t\tClientReliableWrite_Byte (host_client, sv.allocated_client_slots);\n\t\tsplitnum = 0;\n\t\tfor (split = host_client, splitnum = 0; split; split = split->controlled)\n\t\t\tsplitnum++;\n\t\tif (fteext2 & PEXT2_VRINPUTS)\n\t\t\tClientReliableWrite_Byte (host_client, splitnum);\n\t\telse\n\t\t\tClientReliableWrite_Byte (host_client, (host_client->spectator?128:0) | splitnum); //read each player's userinfo to see if its a spectator or not. this hint is merely a cheat.\n\t\tfor (split = host_client; split; split = split->controlled)\n\t\t{\n\t\t\tplayernum = split - svs.clients;// NUM_FOR_EDICT(svprogfuncs, split->edict)-1;\n\t\t\tif (ISQ2CLIENT(host_client) && sv.state == ss_cinematic)\n\t\t\t\tplayernum = -1;\n\t\t\tClientReliableWrite_Byte (host_client, playernum);\n\n\t\t\tsplit->state = cs_connected;\n\t\t#ifdef SVRANKING\n\t\t\tsplit->stats_started = realtime;\n\t\t#endif\n\t\t}\n\t}\n\telse\n\t{\n\t\tif (host_client->protocol==SCP_QUAKE2EX)\n\t\t{\t//let it know how many to expect\n\t\t\tfor (split = host_client, splitnum=0; split; split = split->controlled)\n\t\t\t\tsplitnum++;\n\n\t\t\tif (splitnum != 1)\n\t\t\t{\n\t\t\t\tClientReliableWrite_Short (host_client, -2);\n\t\t\t\tClientReliableWrite_Short (host_client, splitnum);\n\t\t\t}\n\t\t}\n\n\t\tsplitnum = 0;\n\t\tfor (split = host_client; split; split = split->controlled)\n\t\t{\n\t\t\tswitch(svs.gametype)\n\t\t\t{\n\t#ifdef HLSERVER\n\t\t\tcase GT_HALFLIFE:\n\t\t\t\tplayernum = split - svs.clients;\n\t\t\t\tbreak;\n\t#endif\n\t#ifdef Q2SERVER\n\t\t\tcase GT_QUAKE2:\n\t\t\t\tplayernum = Q2NUM_FOR_EDICT(split->q2edict)-1;\n\t\t\t\tbreak;\n\t#endif\n\t\t\tdefault:\n\t\t\t\tplayernum = NUM_FOR_EDICT(svprogfuncs, split->edict)-1;\n\t\t\t}\n\t#ifdef SERVER_DEMO_PLAYBACK\n\t\t\tif (sv.demostate)\n\t\t\t{\n\t\t\t\tplayernum = (MAX_CLIENTS-1-splitnum)|128;\n\t\t\t}\n\t\t\telse\n\t#endif\n\t\t\t\tif (split->spectator)\n\t\t\t\tplayernum |= 128;\n\n\t\t\tsplit->state = cs_connected;\n\t\t#ifdef SVRANKING\n\t\t\tsplit->stats_started = realtime;\n\t\t#endif\n\t\t\tsplitnum++;\n\n\t\t\tif (ISQ2CLIENT(host_client))\n\t\t\t{\n\t\t\t\tif (sv.state == ss_cinematic)\n\t\t\t\t\tplayernum = -1;\n\t\t\t\tClientReliableWrite_Short (host_client, playernum);\n\t\t\t\tif (host_client->protocol!=SCP_QUAKE2EX)\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\telse\n\t\t\t\tClientReliableWrite_Byte (host_client, playernum);\n\t\t}\n\n\t\tif (!ISQ2CLIENT(host_client))\n\t\t{\n\t\t\tif (host_client->fteprotocolextensions & PEXT_SPLITSCREEN)\n\t\t\t\tClientReliableWrite_Byte (host_client, 128);\n\t\t}\n\t}\n\n\t// send full levelname\n#ifdef SERVER_DEMO_PLAYBACK\n\tif (sv.demostatevalid)\n\t\tClientReliableWrite_String (host_client, sv.demfullmapname);\n\telse\n#endif\n\t\tClientReliableWrite_String (host_client, sv.mapname);\n\n\t//\n\t// game server\n\t//\n#ifdef Q2SERVER\n\tif (ISQ2CLIENT(host_client))\n\t{\n\t\tif (sv.state != ss_cinematic)\n\t\t{\n//\t\t\thost_client->q2edict = Q2NUM_FOR_EDICT(split->q2edict)-1;\n\t\t\tmemset (&host_client->lastcmd, 0, sizeof(host_client->lastcmd));\n\n\t\t\t// begin fetching configstrings\n\t\t\tClientReliableWrite_Byte (host_client, svcq2_stufftext);\n\t\t\tClientReliableWrite_String (host_client, va(\"cmd configstrings %i 0\\n\",svs.spawncount) );\n\t\t}\n\t\treturn;\n\t}\n#endif\n\t// send the movevars\n\tClientReliableWrite_Float(host_client, movevars.gravity);\n\tClientReliableWrite_Float(host_client, movevars.stopspeed);\n\tClientReliableWrite_Float(host_client, movevars.maxspeed);\n\tClientReliableWrite_Float(host_client, movevars.spectatormaxspeed);\n\tClientReliableWrite_Float(host_client, movevars.accelerate);\n\tClientReliableWrite_Float(host_client, movevars.airaccelerate);\n\tClientReliableWrite_Float(host_client, movevars.wateraccelerate);\n\tClientReliableWrite_Float(host_client, movevars.friction);\n\tClientReliableWrite_Float(host_client, movevars.waterfriction);\n\tClientReliableWrite_Float(host_client, movevars.entgravity);\n\n\thost_client->csqcactive = false;\n\n\thost_client->realip_num = rand()+(host_client->challenge<<16);\n\tSV_CheckRealIP(host_client, false);\n\n\tSV_LogPlayer(host_client, \"new (QW)\");\n\n\tif (sv.state == ss_cinematic)\n\t{\n\t\tchar tmp[1024];\n\t\tMSG_WriteByte (&host_client->netchan.message, svc_stufftext);\n\t\tMSG_WriteString(&host_client->netchan.message, va(\"\\nplayfilm %s\\n\", COM_QuotedString(svs.name, tmp, sizeof(tmp), false)));\n\t\thost_client->prespawn_stage = PRESPAWN_INVALID;\n\t\thost_client->prespawn_idx = 0;\n\t\treturn;\n\t}\n\n\thost_client->prespawn_stage = PRESPAWN_SERVERINFO;\n\thost_client->prespawn_idx = 0;\n}\n\n#ifdef NQPROT\nvoid SVNQ_New_f (void)\n{\n\textern cvar_t coop;\n\tchar\tmessage[2048];\n\tchar\tbuild[256], mapname[128];\n\tint\t\ti;\n\tint\t\top;\n\tunsigned int protext1 = 0, protext2 = 0, protmain = 0, protfl = 0;\n\tchar *protoname;\n\textern cvar_t sv_listen_nq;\n\tconst char *gamedir;\n\tunsigned int modelcount, soundcount;\n\textern cvar_t allow_download;\n\n\thost_client->prespawn_stage = PRESPAWN_INVALID;\n\thost_client->prespawn_idx = 0;\n\n\thost_client->isindependant = false;\n\n\thost_client->send_message = true;\n\tif (host_client->redirect)\n\t{\n\t\tif (host_client->redirect == 1)\n\t\t{\n\t\t\tchar *msg = va(\"connect \\\"%s\\\"\\n\", sv_fullredirect.string);\n\t\t\tClientReliableWrite_Begin (host_client, svc_stufftext, 2+strlen(msg));\n\t\t\tClientReliableWrite_String (host_client, msg);\n\t\t}\n\t\treturn;\n\t}\n\tif (host_client->drop)\n\t\treturn;\n\n\tif (!host_client->pextknown && (sv_listen_nq.ival != 1||host_client->supportedprotocols&(1u<<SCP_FITZ666)) && !host_client->qex)\t//1 acts as a legacy mode, used for clients that can't cope with cmd before serverdata (either because they crash out or because they refuse to send reliables until after they got the first serverdata)\n\t{\n\t\tif (!host_client->supportedprotocols && host_client->netchan.remote_address.type != NA_LOOPBACK)\n\t\t{\t//don't override cl_loopbackprotocol's choice\n\t\t\tchar *msg = \"cmd protocols\\n\";\n\t\t\tClientReliableWrite_Begin (host_client, svc_stufftext, 2+strlen(msg));\n\t\t\tClientReliableWrite_String (host_client, msg);\n\t\t}\n\t\t{\n\t\t\tchar *msg = \"cmd pext\\n\";\n\t\t\tClientReliableWrite_Begin (host_client, svc_stufftext, 2+strlen(msg));\n\t\t\tClientReliableWrite_String (host_client, msg);\n\t\t}\n\t\treturn;\n\t}\n\telse\n\t\thost_client->pextknown = true;\t//just in case.\n\n\tif (dpcompat_nopreparse.ival && progstype == PROG_QW)\n\t{\n\t\tSV_PrintToClient(host_client, PRINT_HIGH, \"This server has network preparsing disabled, and thus only supports QuakeWorld clients\\n\");\n\t\tCon_Printf(\"%s was not using QW protocols\\n\", host_client->name);\n\t\thost_client->drop = true;\n\t\treturn;\n\t}\n\n\thost_client->csqcactive = false;\n\n\tprotext1 = host_client->fteprotocolextensions;\n\tprotext2 = host_client->fteprotocolextensions2;\n\tprotmain = PROTOCOL_VERSION_NQ;\n\tprotfl = 0;\n\t//force floatcoords as required.\n\tif (sv.nqdatagram.prim.coordtype == COORDTYPE_FLOAT_32)\n\t\tprotext1 |= PEXT_FLOATCOORDS;\n\telse\n\t\tprotext1 &= ~PEXT_FLOATCOORDS;\n\tprotext2 &= ~PEXT2_STUNAWARE;\t//always clear this, don't confuse demos.\n\n\top = host_client->protocol;\n\tif (host_client->supportedprotocols)\n\t{\n\t\tint i;\n\t\tstatic const struct\n\t\t{\n\t\t\tint id;\n\t\t\tqboolean big;\t//used as a filter to exclude protocols that don't match our coord+angles mode\n\t\t\tqboolean pextonly;\n\t\t} preferedprot[] =\n\t\t{\n\t\t\t//favour fitz over dp's. this is on the assumption that we can use pext stuff when both are available.\n\t\t\t{SCP_FITZ666, true,\t\ttrue},\t//actually fte+999... shh...\n\t\t\t{SCP_FITZ666, false,\ttrue},\t//actually fte+666.\n\t\t\t//next best is probably dp's stuff\n\t\t\t{SCP_DARKPLACES7, true},\n\t\t\t{SCP_DARKPLACES6, true},\n\t\t\t//unextended fitz is kinda lame, but has some bells on it.\n\t\t\t{SCP_FITZ666, true},\t//actually 999... shh...\n\t\t\t{SCP_FITZ666, false},\n\t\t\t//well, we can still get bigger model/soundindexes out of this\n\t\t\t{SCP_BJP3, false}\t//should we only use this when we have >255 models/sounds?\n\t\t};\n\t\tfor (i = 0; i < countof(preferedprot); i++)\n\t\t{\n\t\t\tif (preferedprot[i].big == !!(protext1 & PEXT_FLOATCOORDS))\n\t\t\tif (!preferedprot[i].pextonly || (protext2&PEXT2_REPLACEMENTDELTAS))\n\t\t\t{\n\t\t\t\tif (host_client->supportedprotocols & (1u<<preferedprot[i].id))\n\t\t\t\t{\n\t\t\t\t\thost_client->protocol = preferedprot[i].id;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tswitch(host_client->protocol)\n\t{\n\tcase SCP_NETQUAKE:\n\tcase SCP_BJP3:\n\tcase SCP_FITZ666:\n\t\tSV_LogPlayer(host_client, \"new (NQ)\");\n\t\tif (host_client->protocol == SCP_FITZ666 ||\n\t\t\tsv.nqdatagram.prim.anglesize != 1 || sv.nqdatagram.prim.coordtype != COORDTYPE_FIXED_13_3)\n\t\t{\n\t\t\tprotfl = ((sv.nqdatagram.prim.anglesize==2)?RMQFL_SHORTANGLE:0);\n\t\t\tswitch(sv.nqdatagram.prim.coordtype)\n\t\t\t{\n\t\t\tcase COORDTYPE_FLOAT_32:\tprotfl |= RMQFL_FLOATCOORD;\tbreak;\n\t\t\tcase COORDTYPE_FIXED_28_4:\tprotfl |= RMQFL_INT32COORD;\tbreak;\n\t\t\tcase COORDTYPE_FIXED_16_8:\tprotfl |= RMQFL_24BITCOORD;\tbreak;\n\t\t\tcase COORDTYPE_FIXED_13_3:\tprotfl |= 0;\tbreak;\n\t\t\tdefault:\n\t\t\t\thost_client->drop = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\thost_client->protocol = SCP_FITZ666; /*mneh, close enough, the rmq stuff is just modifiers*/\n\n\t\t\tif (protfl)\n\t\t\t{\n\t\t\t\tprotext1 &= ~PEXT_FLOATCOORDS;\t\t//never report floatcoords when using rmq protocol, as the base protocol allows us to be more specific anyway.\n\t\t\t\tprotmain = PROTOCOL_VERSION_RMQ;\n\t\t\t\tprotoname = host_client->qex?\"QE999\":\"RMQ\";\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tprotmain = PROTOCOL_VERSION_FITZ;\n\t\t\t\tprotoname = host_client->qex?\"QE666\":\"666\";\n\t\t\t}\n\t\t}\n\t\telse if (host_client->protocol == SCP_BJP3)\n\t\t{\n\t\t\tprotmain = PROTOCOL_VERSION_BJP3;\n\t\t\tprotoname = \"BJP3\";\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (!host_client->supportedprotocols && !host_client->fteprotocolextensions && !host_client->fteprotocolextensions2)\n\t\t\t{\n\t\t\t\tfor (modelcount = 1; modelcount < MAX_PRECACHE_MODELS && sv.strings.model_precache[modelcount]; modelcount++)\n\t\t\t\t\t;\n\t\t\t\tfor (soundcount = 1; soundcount < MAX_PRECACHE_SOUNDS && sv.strings.sound_precache[soundcount]; soundcount++)\n\t\t\t\t\t;\n\t\t\t\tif (modelcount > 255 || soundcount > 255 || sv.world.num_edicts > 600)\n\t\t\t\t{\n\t\t\t\t\tQ_snprintfz (message, sizeof(message), \"\\x02\\n\"\n\t\t\t\t\t\t\"!!! THIS MAP REQUIRES AN IMPROVED PROTOCOL,\\n\"\n\t\t\t\t\t\t\"!!! BUT YOUR CLIENT DOESN'T APPEAR TO SUPPORT ANY.\\n\"\n\t\t\t\t\t\t\"!!! EXPECT MISSING MODELS, SOUNDS, OR ENTITIES\\n\");\n\t\t\t\t\t\t//if you're reading this message to try to avoid your client being described as shitty, implement support for 'cmd protocol' and maybe 'cmd pext' stuffcmds.\n\t\t\t\t\t\t//simply put, I can't use 666 if I don't know that its safe to do so.\n\t\t\t\t\tSV_PrintToClient(host_client, PRINT_HIGH, message);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\thost_client->protocol = SCP_NETQUAKE;\t//identical other than the client->server angles\n\t\t\tprotmain = PROTOCOL_VERSION_NQ;\n\t\t\tprotoname = host_client->qex?\"QE15\":\"NQ\";\n\t\t}\n\t\tbreak;\n\tcase SCP_DARKPLACES6:\n\t\tSV_LogPlayer(host_client, \"new (DP6)\");\n\t\tprotmain = PROTOCOL_VERSION_DP6;\n\t\tprotext1 &= ~PEXT_FLOATCOORDS;\t//always enabled, try not to break things\n\t\tprotext2 = host_client->fteprotocolextensions2 = host_client->fteprotocolextensions2 & ~(PEXT2_PREDINFO|PEXT2_REPLACEMENTDELTAS);\t//always disabled. kinda interferes with expectations.\n\t\tprotoname = \"DPP6\";\n\t\tbreak;\n\tcase SCP_DARKPLACES7:\n\t\tSV_LogPlayer(host_client, \"new (DP7)\");\n\t\tprotmain = PROTOCOL_VERSION_DP7;\n\t\tprotext1 &= ~PEXT_FLOATCOORDS;\t//always enabled, try not to break things\n\t\tprotext2 = host_client->fteprotocolextensions2 = host_client->fteprotocolextensions2 & ~(PEXT2_PREDINFO|PEXT2_REPLACEMENTDELTAS);\t//always disabled. kinda interferes with expectations.\n\t\tprotoname = \"DPP7\";\n\t\tbreak;\n\tdefault:\n\t\thost_client->drop = true;\n\t\tprotoname = \"Unknown\";\n\t\tbreak;\n\t}\n\n#ifdef OFFICIAL_RELEASE\n\tQ_snprintfz(build, sizeof(build), \"v%i.%02i\", FTE_VER_MAJOR, FTE_VER_MINOR);\n#elif defined(FTE_BRANCH)\n\tQ_snprintfz(build, sizeof(build), \"Rev %s\", STRINGIFY(SVNREVISION));\n#elif defined(SVNREVISION)\n\tQ_snprintfz(build, sizeof(build), \"SVN %s\", STRINGIFY(SVNREVISION));\n#else\n\tQ_snprintfz(build, sizeof(build), \"%s\", __DATE__);\n#endif\n\n\tgamedir = InfoBuf_ValueForKey (&svs.info, \"*gamedir\");\n\tif (!gamedir[0])\n\t{\n\t\tgamedir = FS_GetGamedir(true);\n#ifdef HAVE_LEGACY\n\t\tif (!strcmp(gamedir, \"qw\"))\t//hack: hide the qw dir from nq clients.\n\t\t\tgamedir = \"\";\n#endif\n\t}\n\tCOM_FileBase(sv.modelname, mapname, sizeof(mapname));\n\n\t\n\tif (op != host_client->protocol)\n\t\tSV_ClientProtocolExtensionsChanged(host_client);\n\n//\tif (host_client->netchan.remote_address.type != NA_LOOPBACK) //don't display this to localhost, because its just spam at the end of the day. you don't want to see it in single player.\n\t{\n\t\t//note that certain clients glitch out if this does not have a trailing new line.\n\t\t//note that those clients will also glitch out from vanilla servers too.\n\t\t//vanilla prints something like: VERSION 1.08 SERVER (%i CRC)\n\t\t//which isn't all that useful. so lets customise it to advertise properly, as well as provide gamedir and map (file)name info\n\t\tif (protext2 & PEXT2_REPLACEMENTDELTAS)\n\t\t{\n\t\t\tQ_snprintfz (message, sizeof(message), \"%c\\n\"DISTRIBUTION\" %s - %s - %s\", 2,\n\t\t\t\tbuild,gamedir, mapname);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tQ_snprintfz (message, sizeof(message), \"%c\\n\"DISTRIBUTION\" (%s%s%s, %s) - %s - %s\", 2,\n\t\t\t\tprotoname,(protext1||(protext2&~PEXT2_VOICECHAT))?\"+\":\"\",(protext2&PEXT2_VOICECHAT)?\"Voip\":\"\",\n\t\t\t\tbuild,gamedir, mapname);\n\t\t}\n\t\tSV_PrintToClient(host_client, PRINT_HIGH, message);\n\t}\n\n\tif (host_client->protocol == SCP_DARKPLACES6 || host_client->protocol == SCP_DARKPLACES7)\n\t{\n\t\tchar buf[1024];\n\t\tsize_t sz = 0;\n\t\tchar *f;\n\t\tchar *csprogsname = InfoBuf_ValueForKey(&svs.info, \"*csprogsname\");\n\t\tif (!*csprogsname && *InfoBuf_ValueForKey(&svs.info, \"*csprogs\"))\n\t\t\tcsprogsname = \"csprogs.dat\";\n\n\t\tf = *csprogsname?COM_LoadTempFile(csprogsname, 0, &sz):NULL;\n\t\tif (f)\n\t\t{\n\t\t\tMSG_WriteByte (&host_client->netchan.message, svc_stufftext);\n\t\t\tMSG_WriteString (&host_client->netchan.message, va(\"csqc_progname %s\\n\", COM_QuotedString(csprogsname, buf, sizeof(buf), false)));\n\t\t\tMSG_WriteByte (&host_client->netchan.message, svc_stufftext);\n\t\t\tMSG_WriteString (&host_client->netchan.message, va(\"csqc_progsize %u\\n\", (unsigned int)sz));\n\t\t\tMSG_WriteByte (&host_client->netchan.message, svc_stufftext);\n\t\t\tMSG_WriteString (&host_client->netchan.message, va(\"csqc_progcrc %i\\n\", CalcHashInt(&hash_crc16, f, sz)));\n\n\t\t\tMSG_WriteByte (&host_client->netchan.message, svc_stufftext);\n\t\t\tMSG_WriteString (&host_client->netchan.message, \"cmd enablecsqc\\n\");\n\t\t}\n\t\telse\n\t\t{\n//\t\t\tMSG_WriteByte (&host_client->netchan.message, svc_stufftext);\n//\t\t\tMSG_WriteString (&host_client->netchan.message, \"csqc_progcrc \\\"\\\"\\n\");\n\t\t}\n\t}\n\n\tif (sv.state == ss_cinematic)\n\t{\n\t\tMSG_WriteByte (&host_client->netchan.message, svc_stufftext);\n\t\tMSG_WriteString(&host_client->netchan.message, va(\"\\nplayfilm %s\\n\", COM_QuotedString(svs.name, message, sizeof(message), false)));\n\t\thost_client->prespawn_stage = PRESPAWN_INVALID;\n\t\thost_client->prespawn_idx = 0;\n\t\thost_client->netchan.nqunreliableonly = 2;\n\t\treturn;\n\t}\n\n\tMSG_WriteByte (&host_client->netchan.message, svc_serverdata);\n\tif (protext1)\n\t{\n\t\tMSG_WriteLong (&host_client->netchan.message, PROTOCOL_VERSION_FTE1);\n\t\tMSG_WriteLong (&host_client->netchan.message, protext1);\n\t}\n\tif (protext2)\n\t{\n\t\tMSG_WriteLong (&host_client->netchan.message, PROTOCOL_VERSION_FTE2);\n\t\tMSG_WriteLong (&host_client->netchan.message, protext2);\n\t}\n\tMSG_WriteLong (&host_client->netchan.message, protmain);\n\tif (protmain == PROTOCOL_VERSION_RMQ)\n\t\tMSG_WriteLong (&host_client->netchan.message, protfl);\n\n\tif (protext2 & PEXT2_PREDINFO)\n\t\tMSG_WriteString(&host_client->netchan.message, gamedir);\n\tMSG_WriteByte (&host_client->netchan.message, (sv.allocated_client_slots>host_client->max_net_clients)?host_client->max_net_clients:sv.allocated_client_slots);\n\tif (host_client->qex)\n\t\tMSG_WriteString(&host_client->netchan.message, gamedir);\n\n\tif (!coop.value && deathmatch.value)\n\t\tMSG_WriteByte (&host_client->netchan.message, GAME_DEATHMATCH);\n\telse\n\t\tMSG_WriteByte (&host_client->netchan.message, GAME_COOP);\n\n\tMSG_WriteString (&host_client->netchan.message, sv.mapname);\n\n\n\tif (host_client->fteprotocolextensions2)\n\t{\t//don't bother sending much. we'll spew late precaches later.\n\t\tif (sv.strings.model_precache[1])\n\t\t\tMSG_WriteString (&host_client->netchan.message, sv.strings.model_precache[1]);\n\t\tMSG_WriteByte (&host_client->netchan.message, 0);\t//models. worldmodel only.\n\t\tMSG_WriteByte (&host_client->netchan.message, 0);\t//sounds. none at all.\n\t}\n\telse\n\t{\n\t\t//fixme: don't send too many models.\n\t\tfor (i = 1; sv.strings.model_precache[i] ; i++)\n\t\t\tMSG_WriteString (&host_client->netchan.message, sv.strings.model_precache[i]);\n\t\tMSG_WriteByte (&host_client->netchan.message, 0);\n\n\t\t//fixme: don't send too many sounds.\n\t\tfor (i = 1; sv.strings.sound_precache[i] ; i++)\n\t\t\tMSG_WriteString (&host_client->netchan.message, sv.strings.sound_precache[i]);\n\t\tMSG_WriteByte (&host_client->netchan.message, 0);\n\t}\n\n\tif (allow_download.value && (protext1||protext2||ISDPCLIENT(host_client)))\n\t{\t//technically this is a DP extension, but is separate from actual protocols and shouldn't harm anything.\n\t\t//it is annoying to have prints about unknown commands however, hence the above pext checks (which are unfortunate).\n\t\tMSG_WriteByte (&host_client->netchan.message, svc_stufftext);\n\t\tMSG_WriteString (&host_client->netchan.message, \"cl_serverextension_download 1\\n\");\n\t}\n\n\tif (host_client->qex)\n\t{\t//FIXME: these may change mid-map.\n\t\textern cvar_t sv_friction, sv_stopspeed, sv_maxvelocity, sv_accelerate, sv_gravity;\n\t\tunsigned int bits = QEX_GV_ALL;\n\n\t\tMSG_WriteByte (&host_client->netchan.message, svcqex_servervars);\n\t\tMSG_WriteULEB128 (&host_client->netchan.message, bits);\n\t\tif (bits & QEX_GV_DEATHMATCH)\n\t\t\tMSG_WriteByte (&host_client->netchan.message, deathmatch.ival);\n\t\tif (bits & QEX_GV_IDEALPITCHSCALE)\n\t\t\tMSG_WriteFloat (&host_client->netchan.message, 0);\n\t\tif (bits & QEX_GV_FRICTION)\n\t\t\tMSG_WriteFloat (&host_client->netchan.message, sv_friction.value);\n\t\tif (bits & QEX_GV_EDGEFRICTION)\n\t\t\tMSG_WriteFloat (&host_client->netchan.message, *pm_edgefriction.string?pm_edgefriction.value:2);\n\t\tif (bits & QEX_GV_STOPSPEED)\n\t\t\tMSG_WriteFloat (&host_client->netchan.message, sv_stopspeed.value);\n\t\tif (bits & QEX_GV_MAXVELOCITY)\n\t\t\tMSG_WriteFloat (&host_client->netchan.message, sv_maxvelocity.value);\n\t\tif (bits & QEX_GV_GRAVITY)\n\t\t\tMSG_WriteFloat (&host_client->netchan.message, sv_gravity.value);\n\t\tif (bits & QEX_GV_NOSTEP)\n\t\t\tMSG_WriteByte (&host_client->netchan.message, false);\n\t\tif (bits & QEX_GV_MAXSPEED)\n\t\t\tMSG_WriteFloat (&host_client->netchan.message, sv_maxspeed.value);\n\t\tif (bits & QEX_GV_ACCELERATE)\n\t\t\tMSG_WriteFloat (&host_client->netchan.message, sv_accelerate.value);\n\t\tif (bits & QEX_GV_CONTROLLERONLY)\n\t\t\tMSG_WriteByte (&host_client->netchan.message, 0);\n\t\tif (bits & QEX_GV_TIMELIMIT)\n\t\t\tMSG_WriteFloat (&host_client->netchan.message, timelimit.value);\n\t\tif (bits & QEX_GV_FRAGLIMIT)\n\t\t\tMSG_WriteFloat (&host_client->netchan.message, fraglimit.value);\n\t\tif (bits & QEX_GV_TEAMPLAY)\n\t\t\tMSG_WriteByte (&host_client->netchan.message, teamplay.ival&0xff);\n\t}\n\n// set view\n\tMSG_WriteByte (&host_client->netchan.message, svc_setview);\n\tMSG_WriteEntity (&host_client->netchan.message, (host_client - svs.clients)+1);//NUM_FOR_EDICT(svprogfuncs, host_client->edict));\n\n\tif (!(host_client->fteprotocolextensions2 & PEXT2_PREDINFO))\n\t{\t//old clients can't cope with reliables until they finish loading the models specified above.\n\t\t//we need to wait before sending any more\n\t\t//updated clients can wait a bit, and use signonnum 1 to tell them when to start loading stuff.\n\t\tMSG_WriteByte (&host_client->netchan.message, svcnq_signonnum);\n\t\tMSG_WriteByte (&host_client->netchan.message, 1);\n\t\thost_client->netchan.nqunreliableonly = 2;\n\t}\n\n//\thost_client->sendsignon = true;\n//\thost_client->spawned = false;\t\t// need prespawn, spawn, etc\n\n\tif (op != host_client->protocol)\n\t\thost_client->prespawn_stage = PRESPAWN_PROTOCOLSWITCH;\n\telse\n\t\thost_client->prespawn_stage = PRESPAWN_SERVERINFO;\n\thost_client->prespawn_idx = 0;\n}\n#endif\n\n\n\n\n#ifdef Q2SERVER\n\nstatic const char *SVQ2_GetQ2EXConfigString(int i)\n{\t//remap from vanilla to q2e\n\tif (i == Q2EXCS_MAPCHECKSUM && sv.world.worldmodel)\n\t\treturn va(\"%i\", sv.world.worldmodel->checksum2);\n#define REMAPR(n,l) \t\tif (i >= Q2EXCS_##n && i < Q2EXCS_##n+Q2MAX_##l) return sv.strings.configstring[i-Q2EXCS_##n+Q2CS_##n]; else\n#define REMAPS(n)\t\t\tif (i == Q2EXCS_##n) return sv.strings.configstring[i-Q2EXCS_##n+Q2CS_##n]; else\n#define Q2MAX_STATUSBAR 1//(Q2EXCS_AIRACCEL-Q2EXCS_STATUSBAR)\n\tREMAPS(NAME)\n\tREMAPS(CDTRACK)\n\tREMAPS(SKY)\n\tREMAPS(SKYAXIS)\n\tREMAPS(SKYROTATE)\n\tREMAPR(STATUSBAR, STATUSBAR)\n\tREMAPS(AIRACCEL)\n\tREMAPS(MAXCLIENTS)\n\tREMAPS(MAPCHECKSUM)\n\tREMAPR(MODELS, MODELS)\n\tREMAPR(SOUNDS, SOUNDS)\n\tREMAPR(IMAGES, IMAGES)\n\tREMAPR(LIGHTS, LIGHTSTYLES)\n\tREMAPR(ITEMS, ITEMS)\n\tREMAPR(PLAYERSKINS, CLIENTS)\n\tREMAPR(GENERAL, GENERAL)\n\treturn NULL;\n}\n\nvoid SVQ2_ConfigStrings_f (void)\n{\n\tunsigned int\t\t\tstart;\n\tconst char *str;\n\n\tCon_DPrintf (\"Configstrings() from %s\\n\", host_client->name);\n\n\tif (host_client->state != cs_connected)\n\t{\n\t\tCon_DPrintf (\"configstrings not valid -- already spawned\\n\");\n\t\treturn;\n\t}\n\n\t// handle the case of a level changing while a client was connecting\n\tif ( atoi(Cmd_Argv(1)) != svs.spawncount )\n\t{\n\t\tCon_DPrintf (\"SV_Configstrings_f from different level\\n\");\n\t\tSV_New_f ();\n\t\treturn;\n\t}\n\n\tstart = strtoul(Cmd_Argv(2), NULL, 0);\n\n\t// write a packet full of data\n\tif (host_client->protocol == SCP_QUAKE2EX)\n\t{\n\t\twhile ( host_client->netchan.message.cursize < host_client->netchan.message.maxsize/2\n\t\t\t&& start < Q2EXMAX_CONFIGSTRINGS)\n\t\t{\n\t\t\tstr = SVQ2_GetQ2EXConfigString(start);\n\t\t\tif (str && *str)\n\t\t\t{\n\t\t\t\tMSG_WriteByte (&host_client->netchan.message, svcq2_configstring);\n\t\t\t\tMSG_WriteShort (&host_client->netchan.message, (unsigned short)start);\n\t\t\t\tMSG_WriteString (&host_client->netchan.message, str);\n\t\t\t}\n\t\t\tstart++;\n\t\t}\n\n\t\tif (start == Q2EXMAX_CONFIGSTRINGS)\n\t\t\tstart = 0xc000+MAX_PRECACHE_SOUNDS;\n\t}\n\telse\n\t{\n\t\twhile ( host_client->netchan.message.cursize < host_client->netchan.message.maxsize/2\n\t\t\t&& start < Q2MAX_CONFIGSTRINGS)\n\t\t{\n\t\t\tstr = sv.strings.configstring[start];\n\t\t\tif (str && *str)\n\t\t\t{\n\t\t\t\tMSG_WriteByte (&host_client->netchan.message, svcq2_configstring);\n\t\t\t\tMSG_WriteShort (&host_client->netchan.message, (unsigned short)start);\n\t\t\t\tMSG_WriteString (&host_client->netchan.message, str);\n\t\t\t}\n\t\t\tstart++;\n\t\t}\n\n\t\t//model overflows\n\t\tif (start == Q2MAX_CONFIGSTRINGS)\n\t\t\tstart = 0x8000;\n\t\twhile ( host_client->netchan.message.cursize < host_client->netchan.message.maxsize/2\n\t\t\t&& start < 0x8000+MAX_PRECACHE_MODELS)\n\t\t{\n\t\t\tstr = sv.strings.q2_extramodels[start-0x8000];\n\t\t\tif (str && *str)\n\t\t\t{\n\t\t\t\tMSG_WriteByte (&host_client->netchan.message, svcq2_configstring);\n\t\t\t\tMSG_WriteShort (&host_client->netchan.message, start);\n\t\t\t\tMSG_WriteString (&host_client->netchan.message, str);\n\t\t\t}\n\t\t\tstart++;\n\t\t}\n\n\t\t//sound overflows\n\t\tif (start == 0x8000+MAX_PRECACHE_MODELS)\n\t\t\tstart = 0xc000;\n\t\twhile ( host_client->netchan.message.cursize < host_client->netchan.message.maxsize/2\n\t\t\t&& start < 0xc000+MAX_PRECACHE_SOUNDS)\n\t\t{\n\t\t\tstr = sv.strings.q2_extrasounds[start-0xc000];\n\t\t\tif (str && *str)\n\t\t\t{\n\t\t\t\tMSG_WriteByte (&host_client->netchan.message, svcq2_configstring);\n\t\t\t\tMSG_WriteShort (&host_client->netchan.message, start);\n\t\t\t\tMSG_WriteString (&host_client->netchan.message, str);\n\t\t\t}\n\t\t\tstart++;\n\t\t}\n\t}\n\n\t// send next command\n\n\tif (start == 0xc000+MAX_PRECACHE_SOUNDS)\n\t{\n\t\tMSG_WriteByte (&host_client->netchan.message, svcq2_stufftext);\n\t\tMSG_WriteString (&host_client->netchan.message, va(\"cmd baselines %i 0\\n\",svs.spawncount) );\n\t}\n\telse\n\t{\n\t\tMSG_WriteByte (&host_client->netchan.message, svcq2_stufftext);\n\t\tMSG_WriteString (&host_client->netchan.message, va(\"cmd configstrings %i %i\\n\",svs.spawncount, start) );\n\t}\n}\n#endif\n\n#ifdef Q2SERVER\nvoid SVQ2_BaseLines_f (void)\n{\n\tint\t\tstart;\n\tq2entity_state_t\tnullstate;\n\tq2entity_state_t\t*base;\n\n\textern q2entity_state_t\tsv_baselines[Q2MAX_EDICTS];\n\n\tCon_DPrintf (\"Baselines() from %s\\n\", host_client->name);\n\n\tif (host_client->state != cs_connected)\n\t{\n\t\tCon_Printf (\"baselines not valid -- already spawned\\n\");\n\t\treturn;\n\t}\n\n\t// handle the case of a level changing while a client was connecting\n\tif ( atoi(Cmd_Argv(1)) != svs.spawncount )\n\t{\n\t\tCon_Printf (\"SV_Baselines_f from different level\\n\");\n\t\tSV_New_f ();\n\t\treturn;\n\t}\n\n\tstart = atoi(Cmd_Argv(2));\n\n\tmemset (&nullstate, 0, sizeof(nullstate));\n\n\t// write a packet full of data\n\n\twhile ( host_client->netchan.message.cursize <  host_client->netchan.message.maxsize/2\n\t\t&& start < Q2MAX_EDICTS)\n\t{\n\t\tbase = &sv_baselines[start];\n\t\tif (base->modelindex || base->sound || base->effects)\n\t\t{\n\t\t\tMSG_WriteByte (&host_client->netchan.message, svcq2_spawnbaseline);\n\t\t\tMSGQ2_WriteDeltaEntity (&nullstate, base, &host_client->netchan.message, true, true, host_client->protocol==SCP_QUAKE2EX);\n\t\t}\n\t\tstart++;\n\t}\n\n\t// send next command\n\n\tif (start == Q2MAX_EDICTS)\n\t{\n\t\tMSG_WriteByte (&host_client->netchan.message, svcq2_stufftext);\n\t\tMSG_WriteString (&host_client->netchan.message, va(\"precache %i\\n\", svs.spawncount) );\n\t}\n\telse\n\t{\n\t\tMSG_WriteByte (&host_client->netchan.message, svcq2_stufftext);\n\t\tMSG_WriteString (&host_client->netchan.message, va(\"cmd baselines %i %i\\n\",svs.spawncount, start) );\n\t}\n}\n\nvoid SVQ2_NextServer_f (void)\n{\n\tif (!*sv.modelname && atoi(Cmd_Argv(1)) == svs.spawncount)\n\t{\n\t\tcvar_t *nsv = Cvar_FindVar(\"nextserver\");\n\t\tif (!nsv || !*nsv->string)\n\t\t\treturn;\n\n\t\tsvs.spawncount++;\t// make sure another doesn't sneak in\n\n\t\tCbuf_AddText(nsv->string, RESTRICT_LOCAL);\n\t\tCbuf_AddText(\"\\n\", RESTRICT_LOCAL);\n\t\tCvar_Set(nsv, \"\");\n\t}\n}\n#endif\n\n/*\n==================\nSV_Soundlist_f\n==================\n*/\nvoid SVQW_Soundlist_f (void)\n{\n\thost_client->prespawn_allow_soundlist = true;\n}\n\n/*\n==================\nSV_Modellist_f\n==================\n*/\nvoid SVQW_Modellist_f (void)\n{\n\thost_client->prespawn_allow_modellist = true;\n}\n\nvoid SV_SendClientPrespawnInfo(client_t *client)\n{\n\tqboolean started;\n\tint i;\n\tentity_state_t *state;\n\tstaticsound_state_t *sound;\n\tedict_t *ent;\n\tsvcustomtents_t *ctent;\n\tint maxsize;\n\t//much of this function is written to fill packets enough to overflow them (assuming max packet sizes are large enough), but some bits are lazy and just backbuffer as needed.\n\t//FIXME: have per-stage indicies, to allow returning to a previous stage when new precaches or whatever get added\n\n\tif (client->num_backbuf || client->prespawn_stage == PRESPAWN_COMPLETED)\n\t{\n\t\t//don't spam too much.\n\t\treturn;\n\t}\n\n\t//just because we CAN generate huge messages doesn't mean that we should.\n\t//try to keep packets within reasonable sizes so that we don't trigger insane burst+packetloss on map changes.\n\tmaxsize = client->netchan.message.maxsize/2;\n\tif (client->netchan.mtu_cur && maxsize > client->netchan.mtu_cur-200)\n\t{\n\t\tmaxsize = client->netchan.mtu_cur-200;\n\t\tif (maxsize < 500)\n\t\t\tmaxsize = 500;\n\t}\n\n\tif (client->prespawn_stage == PRESPAWN_PROTOCOLSWITCH)\n\t{\n\t\tif (client->prespawn_idx++ > 10)\n\t\t{\n\t\t\tclient->prespawn_idx = 0;\n\t\t\tclient->prespawn_stage++;\n\t\t}\n\t\telse\n\t\t\treturn;\n\t}\n\n\tif (client->prespawn_stage == PRESPAWN_SERVERINFO)\n\t{\n\t\tchar buffer[1024];\n\n#ifdef PEXT_CSQC\n\t\tif (sv.csqcchecksum && !(client->fteprotocolextensions & PEXT_CSQC) && !ISDPCLIENT(client))\n\t\t\tSV_PrintToClient(client, PRINT_HIGH, \"This server is using CSQC - you are missing out due to your choice of outdated client / protocol!\\n\");\n#endif\n\n\t\twhile (client->netchan.message.cursize < maxsize)\n\t\t{\n\t\t\tif (client->prespawn_idx == 0)\n\t\t\t{\n\t\t\t\tFS_GetPackNames(buffer, sizeof(buffer), 2, true); /*retain extensions, or we'd have to assume pk3*/\n\t\t\t\tClientReliableWrite_Begin(client, svc_stufftext, 1+11+strlen(buffer)+1+1);\n\t\t\t\tClientReliableWrite_SZ(client, \"//paknames \", 11);\n\t\t\t\tClientReliableWrite_SZ(client, buffer, strlen(buffer));\n\t\t\t\tClientReliableWrite_String(client, \"\\n\");\n\t\t\t}\n\t\t\telse if (client->prespawn_idx == 1)\n\t\t\t{\n\t\t\t\tFS_GetPackHashes(buffer, sizeof(buffer), false);\n\t\t\t\tClientReliableWrite_Begin(client, svc_stufftext, 1+7+strlen(buffer)+1+1);\n\t\t\t\tClientReliableWrite_SZ(client, \"//paks \", 7);\n\t\t\t\tClientReliableWrite_SZ(client, buffer, strlen(buffer));\n\t\t\t\tClientReliableWrite_String(client, \"\\n\");\n\t\t\t}\n\t\t\telse if (client->prespawn_idx == 2)\n\t\t\t{\n\t\t\t\tstatic const char *prioritykeys[] = {\"*\", \"fpd\", \"teamplay\", \"deathmatch\", \"maxfps\", NULL};\t//make sure these are in there.\n\t\t\t\tstatic const char *ignorekeys[] = {\"mapname\"/*here for q3, useless for qw*/, NULL};\n\t\t\t\tif (!ISNQCLIENT(client) || (client->fteprotocolextensions & PEXT_CSQC) || (client->fteprotocolextensions2 & PEXT2_PREDINFO))\n\t\t\t\t{\t//nq does not normally get serverinfo sent to it.\n\t\t\t\t\ti = InfoBuf_ToString(&svs.info, buffer, sizeof(buffer), prioritykeys, ignorekeys, NULL, &client->infosync, &svs.info);\n\t\t\t\t\tInfo_SetValueForStarKey(buffer, \"*z_ext\", va(\"%i\", client->zquake_extensions), sizeof(buffer)); //should already be in there, so this should only ever make it shorter.\n\t\t\t\t\tClientReliableWrite_Begin(client, svc_stufftext, 20 + i);\n\t\t\t\t\tClientReliableWrite_String (client, va(\"fullserverinfo \\\"%s\\\"\\n\", buffer) );\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (client->prespawn_idx == 3)\n\t\t\t{\n\t\t\t\tint track = 0;\n\t\t\t\tconst char *noise = \"\";\n\n#ifdef HEXEN2\n\t\t\t\tif (progstype == PROG_H2)\n\t\t\t\t{\n\t\t\t\t\ttrack = sv.h2cdtrack;\t//hexen2 has a special hack\n\t\t\t\t} else\n#endif\n\t\t\t\tif (svprogfuncs)\n\t\t\t\t{\n\t\t\t\t\ttrack = ((edict_t*)sv.world.edicts)->v->sounds;\n\t\t\t\t\tnoise = PR_GetString(svprogfuncs, ((edict_t*)sv.world.edicts)->v->noise);\n\t\t\t\t}\n\n\t\t\t\tif (track == -2)\n\t\t\t\t\t; //don't change it at all\n\t\t\t\telse if (track == -1 && *noise)\n\t\t\t\t\tSV_StuffcmdToClient(client, va(\"cd loop \\\"%s\\\"\\n\", noise));\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tClientReliableWrite_Begin(client, svc_cdtrack, 2);\n\t\t\t\t\tClientReliableWrite_Byte (client, track);\n\t\t\t\t\tif (ISNQCLIENT(client))\n\t\t\t\t\t\tClientReliableWrite_Byte (client, track);\n#ifdef HEXEN2\n\t\t\t\t\tif (!track && *sv.h2miditrack)\n\t\t\t\t\t\tSV_StuffcmdToClient(client, va(\"music \\\"%s\\\"\\n\", sv.h2miditrack));\n#endif\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (client->prespawn_idx == 4)\n\t\t\t{\n\t\t\t\tClientReliableWrite_Begin(client, svc_setpause, 2);\n\t\t\t\tClientReliableWrite_Byte (client, sv.oldpaused!=0);\n\n\t\t\t\tif (sv.oldpaused && sv.oldpaused&~PAUSE_AUTO)\n\t\t\t\t\tSV_PrintToClient(client, PRINT_HIGH, \"server is paused\\n\");\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tclient->prespawn_stage++;\n\t\t\t\tclient->prespawn_idx = 0;\n\t\t\t\tclient->prespawn_idx2 = 0;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tclient->prespawn_idx++;\n\t\t}\n\t}\n\n#ifdef MVD_RECORDING\n\tif (client->prespawn_stage == PRESPAWN_CSPROGS)\n\t{\n\t\textern cvar_t sv_demo_write_csqc;\n\t\tif (client == &demo.recorder && (sv_demo_write_csqc.ival || !*sv_demo_write_csqc.string))\t//we only really want to do this for demos(hopefully we're gzipping). actual clients can make the request themselves.\n\t\tif ((client->fteprotocolextensions & PEXT_CHUNKEDDOWNLOADS) && (client->fteprotocolextensions & PEXT_CSQC))\t//there's many different download mechanisms...\n\t\t{\n\t\t\tif (!client->prespawn_idx && !client->download)\n\t\t\t{\n\t\t\t\textern cvar_t sv_csqc_progname;\n\t\t\t\tQ_strncpyz(client->downloadfn, sv_csqc_progname.string, sizeof(client->downloadfn));\n\t\t\t\tclient->download = FS_OpenVFS(sv_csqc_progname.string, \"rb\", FS_GAME);\n\t\t\t\tclient->downloadcount = 0;\n\t\t\t\tclient->prespawn_idx = 1;\n\t\t\t\tif (client->download)\n\t\t\t\t{\n\t\t\t\t\tclient->downloadsize = VFS_GETLEN(client->download);\n\n\t\t\t\t\t//send the size+filename\n\t\t\t\t\tClientReliableWrite_Begin (client, svc_download, 18+strlen(client->downloadfn));\n\t\t\t\t\tClientReliableWrite_Long (client, -1);\t//offset\n\t\t\t\t\tif (client->downloadsize >= 0x7fffffff)\n\t\t\t\t\t{\t//avoid unsigned values.\n\t\t\t\t\t\tClientReliableWrite_Long (client, 0x80000000);\t//signal that its 64bit\n\t\t\t\t\t\tClientReliableWrite_Long (client, qofs_Low(client->downloadsize));\n\t\t\t\t\t\tClientReliableWrite_Long (client, qofs_High(client->downloadsize));\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\tClientReliableWrite_Long (client, client->downloadsize);\n\t\t\t\t\tClientReliableWrite_String (client, client->downloadfn);\n\t\t\t\t}\n\t\t\t}\n\t\t\t//send the data while possible+needed\n\t\t\tif (client->prespawn_idx && client->download)\n\t\t\t{\n\t\t\t\twhile (client->downloadcount < client->downloadsize)\n\t\t\t\t{\n\t\t\t\t\tqbyte chunk[DLBLOCKSIZE];\n\t\t\t\t\tint sz;\n\t\t\t\t\tif (client->netchan.message.maxsize - client->netchan.message.cursize < 1100)\n\t\t\t\t\t\treturn;\t//don't flood...\n\t\t\t\t\tsz = VFS_READ(client->download, chunk, DLBLOCKSIZE);\n\t\t\t\t\tif (sz <= 0)\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tif (sz < DLBLOCKSIZE)\n\t\t\t\t\t{\n\t\t\t\t\t\tmemset(chunk+sz, 0, DLBLOCKSIZE-sz);\t//zero-fill if the chunk is at the end.\n\t\t\t\t\t\tsz = DLBLOCKSIZE;\n\t\t\t\t\t}\n\n\t\t\t\t\tClientReliableWrite_Begin (client, svc_download, 5+sz);\n\t\t\t\t\tClientReliableWrite_Long(client, client->downloadcount/DLBLOCKSIZE);\n\t\t\t\t\tClientReliableWrite_SZ(client, chunk, sz);\n\t\t\t\t\tclient->downloadcount += sz;\n\t\t\t\t}\n\n\t\t\t\t//don't need to write completion. the client should be tracking that itself with chunks.\n\t\t\t\tVFS_CLOSE(client->download);\n\t\t\t\tclient->download = NULL;\n\t\t\t}\n\t\t}\n\n\t\tclient->prespawn_stage++;\n\t\tclient->prespawn_idx = 0;\n\t}\n#endif\n\n\tif (client->prespawn_stage == PRESPAWN_SOUNDLIST)\n\t{\n\t\tif (ISQWCLIENT(client))\n\t\t{\n\t\t\tint maxclientsupportedsounds = 256;\n#ifdef PEXT_SOUNDDBL\n\t\t\tif (client->fteprotocolextensions & PEXT_SOUNDDBL)\n\t\t\t\tmaxclientsupportedsounds = MAX_PRECACHE_SOUNDS;\n#endif\n#ifdef PEXT2_REPLACEMENTDELTAS\n\t\t\tif (client->fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS)\n\t\t\t\tmaxclientsupportedsounds = MAX_PRECACHE_SOUNDS;\n#endif\n\t\t\tstarted = false;\n\n\t\t\t//allows stalling for the soundlist command, for compat.\n\t\t\tif (!client->prespawn_allow_soundlist)\n\t\t\t\tif (!(client->fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS))\n\t\t\t\t\treturn;\n\n\t\t\twhile (client->netchan.message.cursize < maxsize)\n\t\t\t{\n\t\t\t\tif (!started)\n\t\t\t\t{\n\t\t\t\t\tstarted = true;\n\t\t\t\t\tclient->prespawn_allow_soundlist = false;\n\t#if defined(PEXT_SOUNDDBL) || defined(PEXT2_REPLACEMENTDELTAS)\n\t\t\t\t\tif (client->prespawn_idx > 255)\n\t\t\t\t\t{\n\t\t\t\t\t\tMSG_WriteByte (&client->netchan.message, svcfte_soundlistshort);\n\t\t\t\t\t\tMSG_WriteShort (&client->netchan.message, client->prespawn_idx);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t#endif\n\t\t\t\t\t{\n\t\t\t\t\t\tMSG_WriteByte (&client->netchan.message, svc_soundlist);\n\t\t\t\t\t\tMSG_WriteByte (&client->netchan.message, client->prespawn_idx);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tclient->prespawn_idx++;\n\n\t\t\t\tif (client->prespawn_idx >= maxclientsupportedsounds || !sv.strings.sound_precache[client->prespawn_idx])\n\t\t\t\t{\n\t\t\t\t\t//write final-end-of-list\n\t\t\t\t\tMSG_WriteByte (&client->netchan.message, 0);\n\t\t\t\t\tMSG_WriteByte (&client->netchan.message, 0);\n\t\t\t\t\tstarted = 0;\n\n\t\t\t\t\tif (sv.strings.sound_precache[client->prespawn_idx] && !(client->plimitwarned & PLIMIT_SOUNDS))\n\t\t\t\t\t{\n\t\t\t\t\t\tclient->plimitwarned |= PLIMIT_SOUNDS;\n\t\t\t\t\t\tSV_ClientPrintf(client, PRINT_HIGH, \"WARNING: Your client's network protocol only supports %i sounds. Please upgrade or enable extensions.\\n\", client->prespawn_idx);\n\t\t\t\t\t}\n\n\t\t\t\t\tclient->prespawn_stage++;\n\t\t\t\t\tclient->prespawn_idx = 0;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tMSG_WriteString (&client->netchan.message, sv.strings.sound_precache[client->prespawn_idx]);\n\t\t\t}\n\t\t\tif (started)\n\t\t\t{\n\t\t\t\t//write end-of-packet\n\t\t\t\tMSG_WriteByte (&client->netchan.message, 0);\n\t\t\t\tMSG_WriteByte (&client->netchan.message, (client->prespawn_idx&0xff)?client->prespawn_idx&0xff:0xff);\n\n\t\t\t}\n\t\t}\n\t\telse if (ISNQCLIENT(client) && client->fteprotocolextensions2)\n\t\t{\n\t\t\tif (client->prespawn_idx < 1)\n\t\t\t\tclient->prespawn_idx = 1;\n\t\t\twhile (client->netchan.message.cursize < maxsize)\n\t\t\t{\n\t\t\t\tif (client->prespawn_idx >= MAX_PRECACHE_SOUNDS || !sv.strings.sound_precache[client->prespawn_idx])\n\t\t\t\t{\n\t\t\t\t\tif (sv.strings.sound_precache[client->prespawn_idx] && !(client->plimitwarned & PLIMIT_SOUNDS))\n\t\t\t\t\t{\n\t\t\t\t\t\tclient->plimitwarned |= PLIMIT_SOUNDS;\n\t\t\t\t\t\tSV_ClientPrintf(client, PRINT_HIGH, \"WARNING: Your client's network protocol only supports %i sounds. Please upgrade or enable extensions.\\n\", client->prespawn_idx);\n\t\t\t\t\t}\n\n\t\t\t\t\tclient->prespawn_stage++;\n\t\t\t\t\tclient->prespawn_idx = 0;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tMSG_WriteByte (&client->netchan.message, svcdp_precache);\n\t\t\t\tMSG_WriteShort(&client->netchan.message, 0x8000|client->prespawn_idx);\n\t\t\t\tMSG_WriteString (&client->netchan.message, sv.strings.sound_precache[client->prespawn_idx]);\n\t\t\t\tclient->prespawn_idx++;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t\tclient->prespawn_stage++;\n\t}\n\n#ifdef HAVE_LEGACY\n\tif (client->prespawn_stage == PRESPAWN_VWEPMODELLIST)\n\t{\n\t\t//no indicies. the protocol can't cope with them.\n\t\tif (client->zquake_extensions & Z_EXT_VWEP)\n\t\t{\n\t\t\tchar mname[MAX_QPATH];\n\t\t\tchar ext[8];\n\t\t\tchar vweaplist[2048] = \"//vwep\";\n\n\t\t\tfor (i = 0; sv.strings.vw_model_precache[i]; i++)\n\t\t\t{\n\t\t\t\t//grab the model name... without a progs/ prefix if it has one\n\t\t\t\tif (!strncmp(sv.strings.vw_model_precache[i], \"progs/\", 6))\n\t\t\t\t\tQ_strncpyz(mname, sv.strings.vw_model_precache[i]+6, sizeof(mname));\n\t\t\t\telse\n\t\t\t\t\tQ_strncpyz(mname, sv.strings.vw_model_precache[i], sizeof(mname));\n\n\t\t\t\t//strip .mdl extensions, for compat with ezquake\n\t\t\t\tCOM_FileExtension(mname, ext, sizeof(ext));\n\t\t\t\tif (!strcmp(ext, \"mdl\"))\n\t\t\t\t\tCOM_StripExtension(mname, mname, sizeof(mname));\n\n\t\t\t\t//add it to the vweap command, taking care of any remaining spaces in names.\n\t\t\t\tif (strchr(mname, ' ') || !*mname)\n\t\t\t\t\tQ_strncatz(vweaplist, va(\" \\\"%s\\\"\", mname), sizeof(vweaplist));\n\t\t\t\telse\n\t\t\t\t\tQ_strncatz(vweaplist, va(\" %s\", mname), sizeof(vweaplist));\n\t\t\t}\n\n\t\t\tif (strlen(vweaplist) <= sizeof(vweaplist)-2)\n\t\t\t{\n\t\t\t\tQ_strncatz(vweaplist, \"\\n\", sizeof(vweaplist));\n\n\t\t\t\tClientReliableWrite_Begin(client, svc_stufftext, 2+strlen(vweaplist));\n\t\t\t\tClientReliableWrite_String(client, vweaplist);\n\t\t\t}\n\t\t}\n\t\tclient->prespawn_stage++;\n\t}\n#endif\n\n\tif (client->prespawn_stage == PRESPAWN_MODELLIST)\n\t{\n\t\tif (ISQWCLIENT(client))\n\t\t{\n\t\t\tstarted = false;\n\n\t\t\t//allows stalling for the modellist command, for compat.\n\t\t\tif (!client->prespawn_allow_modellist)\n\t\t\t\tif (!(client->fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS))\n\t\t\t\t\treturn;\n\n\t\t\twhile (client->netchan.message.cursize < maxsize)\n\t\t\t{\n\t\t\t\tif (!started)\n\t\t\t\t{\n\t\t\t\t\tstarted = true;\n\t\t\t\t\tclient->prespawn_allow_modellist = false;\n\t#if defined(PEXT_SOUNDDBL) || defined(PEXT2_REPLACEMENTDELTAS)\n\t\t\t\t\tif (client->prespawn_idx > 255)\n\t\t\t\t\t{\n\t\t\t\t\t\tMSG_WriteByte (&client->netchan.message, svcfte_modellistshort);\n\t\t\t\t\t\tMSG_WriteShort (&client->netchan.message, client->prespawn_idx);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t#endif\n\t\t\t\t\t{\n\t\t\t\t\t\tMSG_WriteByte (&client->netchan.message, svc_modellist);\n\t\t\t\t\t\tMSG_WriteByte (&client->netchan.message, client->prespawn_idx);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tclient->prespawn_idx++;\n\n\t\t\t\tif (client->prespawn_idx >= client->maxmodels || !sv.strings.model_precache[client->prespawn_idx])\n\t\t\t\t{\n\t\t\t\t\t//write final-end-of-list\n\t\t\t\t\tMSG_WriteByte (&client->netchan.message, 0);\n\t\t\t\t\tMSG_WriteByte (&client->netchan.message, 0);\n\t\t\t\t\tstarted = 0;\n\n\t\t\t\t\tif (sv.strings.model_precache[client->prespawn_idx] && !(client->plimitwarned & PLIMIT_MODELS))\n\t\t\t\t\t{\n\t\t\t\t\t\tclient->plimitwarned |= PLIMIT_MODELS;\n\t\t\t\t\t\tSV_ClientPrintf(client, PRINT_HIGH, \"WARNING: Your client's network protocol only supports %i models. Please upgrade or enable extensions.\\n\", client->prespawn_idx);\n\t\t\t\t\t}\n\n\t\t\t\t\tclient->prespawn_stage++;\n\t\t\t\t\tclient->prespawn_idx = 0;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tMSG_WriteString (&client->netchan.message, sv.strings.model_precache[client->prespawn_idx]);\n\t\t\t}\n\t\t\tif (started)\n\t\t\t{\n\t\t\t\t//write end-of-packet\n\t\t\t\tMSG_WriteByte (&client->netchan.message, 0);\n\t\t\t\tMSG_WriteByte (&client->netchan.message, (client->prespawn_idx&0xff)?client->prespawn_idx&0xff:0xff);\n\t\t\t}\n\t\t}\n\t\telse if (ISNQCLIENT(client) && client->fteprotocolextensions2)\n\t\t{\n\t\t\tif (client->prespawn_idx < 1)\n\t\t\t\tclient->prespawn_idx = 1;\n\t\t\twhile (client->netchan.message.cursize < maxsize)\n\t\t\t{\n\t\t\t\tif (client->prespawn_idx >= client->maxmodels || !sv.strings.model_precache[client->prespawn_idx])\n\t\t\t\t{\n\t\t\t\t\tif (sv.strings.model_precache[client->prespawn_idx] && !(client->plimitwarned & PLIMIT_MODELS))\n\t\t\t\t\t{\n\t\t\t\t\t\tclient->plimitwarned |= PLIMIT_MODELS;\n\t\t\t\t\t\tSV_ClientPrintf(client, PRINT_HIGH, \"WARNING: Your client's network protocol only supports %i models. Please upgrade or enable extensions.\\n\", client->prespawn_idx);\n\t\t\t\t\t}\n\n\t\t\t\t\tclient->prespawn_stage++;\n\t\t\t\t\tclient->prespawn_idx = 0;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tMSG_WriteByte (&client->netchan.message, svcdp_precache);\n\t\t\t\tMSG_WriteShort(&client->netchan.message, client->prespawn_idx);\n\t\t\t\tMSG_WriteString (&client->netchan.message, sv.strings.model_precache[client->prespawn_idx]);\n\t\t\t\tclient->prespawn_idx++;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t\tclient->prespawn_stage++;\n\t}\n\tif (client->prespawn_stage == PRESPAWN_NQSIGNON1)\n\t{\n\t\tif (ISNQCLIENT(client) && (client->fteprotocolextensions2 & PEXT2_PREDINFO))\n\t\t{\t//wasn't sent earlier...\n\t\t\tClientReliableWrite_Begin(client, svcnq_signonnum, 2);\n\t\t\tClientReliableWrite_Byte (client, 1);\n\t\t}\n\t\tclient->prespawn_stage++;\n\t}\n\n\tif (client->prespawn_stage == PRESPAWN_MAPCHECK)\n\t{\n\t\t//can't progress beyond this as we're waiting for the client.\n//\t\thost_client->prespawn_idx = client->prespawn_idx;\n\t\treturn;\n\t}\n\n\tif (client->prespawn_stage == PRESPAWN_PARTICLES)\n\t{\n\t\tif (!(client->fteprotocolextensions & PEXT_CSQC))\n\t\t\tclient->prespawn_idx = MAX_SSPARTICLESPRE;\n\t\twhile (client->netchan.message.cursize < maxsize)\n\t\t{\n\t\t\tif (client->prespawn_idx >= MAX_SSPARTICLESPRE)\n\t\t\t{\n\t\t\t\tclient->prespawn_stage++;\n\t\t\t\tclient->prespawn_idx = 0;\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tif (sv.strings.particle_precache[client->prespawn_idx])\n\t\t\t{\n\t\t\t\tClientReliableWrite_Begin (client, ISNQCLIENT(client)?svcdp_precache:svcfte_precache, 4 + strlen(sv.strings.particle_precache[client->prespawn_idx]));\n\t\t\t\tClientReliableWrite_Short (client, client->prespawn_idx | PC_PARTICLE);\n\t\t\t\tClientReliableWrite_String (client, sv.strings.particle_precache[client->prespawn_idx]);\n\t\t\t}\n\t\t\tclient->prespawn_idx++;\n\t\t}\n\t}\n\n\tif (client->prespawn_stage == PRESPAWN_CUSTOMTENTS)\n\t{\n\t\twhile (client->netchan.message.cursize < maxsize)\n\t\t{\n\t\t\tif (client->prespawn_idx >= 255)\n\t\t\t{\n\t\t\t\tclient->prespawn_stage++;\n\t\t\t\tclient->prespawn_idx = 0;\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tctent = &sv.customtents[client->prespawn_idx];\n\n\t\t\tif (*ctent->particleeffecttype)\n\t\t\t{\n\t\t\t\tif (client->fteprotocolextensions & PEXT_CUSTOMTEMPEFFECTS)\n\t\t\t\t{\n\t\t\t\t\tMSG_WriteByte(&client->netchan.message, svcfte_customtempent);\n\t\t\t\t\tMSG_WriteByte(&client->netchan.message, 255);\n\t\t\t\t\tMSG_WriteByte(&client->netchan.message, client->prespawn_idx);\n\t\t\t\t\tMSG_WriteByte(&client->netchan.message, ctent->netstyle);\n\t\t\t\t\tMSG_WriteString(&client->netchan.message, ctent->particleeffecttype);\n\t\t\t\t\tif (ctent->netstyle & CTE_STAINS)\n\t\t\t\t\t{\n\t\t\t\t\t\tMSG_WriteChar(&client->netchan.message, ctent->stain[0]);\n\t\t\t\t\t\tMSG_WriteChar(&client->netchan.message, ctent->stain[0]);\n\t\t\t\t\t\tMSG_WriteChar(&client->netchan.message, ctent->stain[0]);\n\t\t\t\t\t\tMSG_WriteByte(&client->netchan.message, ctent->radius);\n\t\t\t\t\t}\n\t\t\t\t\tif (ctent->netstyle & CTE_GLOWS)\n\t\t\t\t\t{\n\t\t\t\t\t\tMSG_WriteByte(&client->netchan.message, ctent->dlightrgb[0]);\n\t\t\t\t\t\tMSG_WriteByte(&client->netchan.message, ctent->dlightrgb[1]);\n\t\t\t\t\t\tMSG_WriteByte(&client->netchan.message, ctent->dlightrgb[2]);\n\t\t\t\t\t\tMSG_WriteByte(&client->netchan.message, ctent->dlightradius);\n\t\t\t\t\t\tMSG_WriteByte(&client->netchan.message, ctent->dlighttime);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tclient->prespawn_idx++;\n\t\t}\n\t}\n\n\tif (client->prespawn_stage == PRESPAWN_SIGNON_BUF)\n\t{\n\t\tint nextsize;\n\t\twhile (client->netchan.message.cursize < maxsize)\n\t\t{\n\t\t\tif (client->prespawn_idx >= sv.used_signon_space)\n\t\t\t{\n\t\t\t\tclient->prespawn_stage++;\n\t\t\t\tclient->prespawn_idx = 0;\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tnextsize = sv.signon_buffer[client->prespawn_idx] | (sv.signon_buffer[client->prespawn_idx+1]<<8);\n\t\t\tif (client->netchan.message.cursize+nextsize+30 <= client->netchan.message.maxsize)\n\t\t\t{\n\t\t\t\tSZ_Write (&client->netchan.message, sv.signon_buffer+client->prespawn_idx+2, nextsize);\n\t\t\t\tclient->prespawn_idx+=2+nextsize;\n\t\t\t}\n\t\t\telse if (!client->netchan.message.cursize && nextsize+30 > client->netchan.message.maxsize)\n\t\t\t{\t//signon data is meant to be split up into smallish chunks to avoid network fragmentation.\n\t\t\t\t//but sometimes a single blob is too large (eg: gamecode not using MSG_MULTICAST and just writing 16k in one splurge)\n\t\t\t\t//fteqw and nq protocols can cope, vanilla qw cannot, so we do need to warn. the alternative is to kick.\n\t\t\t\tSV_PrintToClient(client, PRINT_HIGH, va(\"\\x01\" \"Dropping %i bytes of signon data\\n\", nextsize));\n\t\t\t\tclient->prespawn_idx+=2+nextsize;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\telse\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\tif (client->prespawn_stage == PRESPAWN_SPAWNSTATIC)\n\t{\n\t\tint maxstatics = sv.num_static_entities;\n\t\tif (maxstatics > 1024 && ISDPCLIENT(client))\n\t\t\tmaxstatics = 1024;\n\t\twhile (client->netchan.message.cursize < maxsize)\t//static entities\n\t\t{\n\t\t\tif (client->prespawn_idx >= maxstatics)\n\t\t\t{\n\t\t\t\tclient->prespawn_stage++;\n\t\t\t\tclient->prespawn_idx = 0;\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tstate = &sv_staticentities[client->prespawn_idx];\n\t\t\tclient->prespawn_idx++;\n\n\t\t\tif (client->fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS)\n\t\t\t{\n\t\t\t\tMSG_WriteByte(&client->netchan.message, svcfte_spawnstatic2);\n\t\t\t\tSVFTE_EmitBaseline(state, false, &client->netchan.message, client->fteprotocolextensions2, client->ezprotocolextensions1);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (client->fteprotocolextensions & PEXT_SPAWNSTATIC2)\n\t\t\t{\n\t\t\t\t/*if it uses some new feature, use the updated spawnstatic*/\n\t\t\t\tif (state->hexen2flags || state->trans || state->modelindex >= 256 || state->frame > 255 || state->scale || state->abslight)\n\t\t\t\t{\n\t\t\t\t\tMSG_WriteByte(&client->netchan.message, svcfte_spawnstatic2);\n\t\t\t\t\tSVQW_WriteDelta(&nullentitystate, state, &client->netchan.message, true, client->fteprotocolextensions, client->ezprotocolextensions1);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (client->protocol == SCP_BJP3)\n\t\t\t{\n\t\t\t\tMSG_WriteByte(&client->netchan.message, svc_spawnstatic);\n\n\t\t\t\tMSG_WriteShort (&client->netchan.message, state->modelindex);\n\n\t\t\t\tMSG_WriteByte (&client->netchan.message, state->frame);\n\t\t\t\tMSG_WriteByte (&client->netchan.message, (int)state->colormap);\n\t\t\t\tMSG_WriteByte (&client->netchan.message, (int)state->skinnum);\n\t\t\t\tfor (i=0 ; i<3 ; i++)\n\t\t\t\t{\n\t\t\t\t\tMSG_WriteCoord(&client->netchan.message, state->origin[i]);\n\t\t\t\t\tMSG_WriteAngle(&client->netchan.message, state->angles[i]);\n\t\t\t\t}\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t/*couldn't use protocol extensions?\n\t\t\t  use the fallback, unless the model is invalid as that's silly*/\n\t\t\tif (state->modelindex < 256)\n\t\t\t{\n\t\t\t\tMSG_WriteByte(&client->netchan.message, svc_spawnstatic);\n\n\t\t\t\tMSG_WriteByte (&client->netchan.message, state->modelindex);\n\n\t\t\t\tMSG_WriteByte (&client->netchan.message, state->frame);\n\t\t\t\tMSG_WriteByte (&client->netchan.message, (int)state->colormap);\n\t\t\t\tMSG_WriteByte (&client->netchan.message, (int)state->skinnum);\n\t\t\t\tfor (i=0 ; i<3 ; i++)\n\t\t\t\t{\n\t\t\t\t\tMSG_WriteCoord(&client->netchan.message, state->origin[i]);\n\t\t\t\t\tMSG_WriteAngle(&client->netchan.message, state->angles[i]);\n\t\t\t\t}\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\t}\n\tif (client->prespawn_stage == PRESPAWN_AMBIENTSOUND)\n\t{\n\t\twhile (client->netchan.message.cursize < maxsize)\t//static entities\n\t\t{\n\t\t\tqboolean large = false;\n\n\t\t\tif (client->prespawn_idx >= sv.num_static_sounds)\n\t\t\t{\n\t\t\t\tclient->prespawn_stage++;\n\t\t\t\tclient->prespawn_idx = 0;\n\t\t\t\tclient->prespawn_idx2 = 0;\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tsound = &sv_staticsounds[client->prespawn_idx];\n\t\t\tclient->prespawn_idx++;\n\n\t\t\t/*if (client->protocol == SCP_BJP2)\n\t\t\t{\n\t\t\t\tlarge = true;\n\t\t\t\tMSG_WriteByte(&client->netchan.message, svc_spawnstaticsound);\n\t\t\t}\n\t\t\telse */if (sound->soundnum > 0xff)\n\t\t\t{\n\t\t\t\tlarge = true;\n\t\t\t\tif (client->protocol == SCP_BJP3)\n\t\t\t\t\tcontinue;\t//not supported\n\t\t\t\telse if (ISQWCLIENT(client) && (client->fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS))\n\t\t\t\t{\n\t\t\t\t\tMSG_WriteByte(&client->netchan.message, svcfte_spawnstaticsound2);\n\t\t\t\t\tMSG_WriteByte(&client->netchan.message, 1);\n\t\t\t\t}\n\t\t\t\telse if (ISDPCLIENT(client))\n\t\t\t\t\tMSG_WriteByte(&client->netchan.message, svcdp_spawnstaticsound2);\n\t\t\t\telse if (client->protocol == SCP_FITZ666)\n\t\t\t\t\tMSG_WriteByte(&client->netchan.message, svcfitz_spawnstaticsound2);\n\t\t\t\telse\n\t\t\t\t\tcontinue; //not supported\n\t\t\t}\n\t\t\telse\n\t\t\t\tMSG_WriteByte(&client->netchan.message, svc_spawnstaticsound);\n\n\t\t\tfor (i=0 ; i<3 ; i++)\n\t\t\t\tMSG_WriteCoord(&client->netchan.message, sound->position[i]);\n\t\t\tif (large)\n\t\t\t{\n\t\t\t\tif (client->fteprotocolextensions2&PEXT2_LERPTIME)\n\t\t\t\t\tMSG_WriteULEB128(&client->netchan.message, sound->soundnum);\n\t\t\t\telse\n\t\t\t\t\tMSG_WriteShort(&client->netchan.message, sound->soundnum);\n\t\t\t}\n\t\t\telse\n\t\t\t\tMSG_WriteByte(&client->netchan.message, sound->soundnum);\n\t\t\tMSG_WriteByte(&client->netchan.message, sound->volume);\n\t\t\tMSG_WriteByte(&client->netchan.message, sound->attenuation);\n\t\t}\n\t}\n\n\tif (client->prespawn_stage == PRESPAWN_BASELINES)\n\t{\n\t\twhile (client->netchan.message.cursize < maxsize)\t//baselines\n\t\t{\n\t\t\tif (client->prespawn_idx >= sv.world.num_edicts)\n\t\t\t{\n\t\t\t\tclient->prespawn_stage++;\n\t\t\t\tclient->prespawn_idx = 0;\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tent = EDICT_NUM_PB(svprogfuncs, client->prespawn_idx);\n\n\t\t\tif (!ent)\n\t\t\t\tstate = &nullentitystate;\n\t\t\telse\n\t\t\t\tstate = &ent->baseline;\n\n\t\t\tif (!state->number || !state->modelindex)\n\t\t\t{\t//ent doesn't have a baseline\n\t\t\t\tclient->prespawn_idx++;\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (state->number >= client->max_net_ents || state->modelindex >= client->maxmodels)\n\t\t\t{\n\t\t\t\t/*can't send this ent*/\n\t\t\t}\n\t\t\telse if (client->fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS)\n\t\t\t{\n\t\t\t\tMSG_WriteByte(&client->netchan.message, svcfte_spawnbaseline2);\n\t\t\t\tSVFTE_EmitBaseline(state, true, &client->netchan.message, client->fteprotocolextensions2, client->ezprotocolextensions1);\n\t\t\t}\n\t\t\telse if (client->fteprotocolextensions & PEXT_SPAWNSTATIC2)\n\t\t\t{\n\t\t\t\tMSG_WriteByte(&client->netchan.message, svcfte_spawnbaseline2);\n\t\t\t\tSVQW_WriteDelta(&nullentitystate, state, &client->netchan.message, true, client->fteprotocolextensions, client->ezprotocolextensions1);\n\t\t\t}\n\t\t\telse if (ISDPCLIENT(client) && (state->modelindex > 255 || state->frame > 255))\n\t\t\t{\n\t\t\t\tMSG_WriteByte(&client->netchan.message, svcdp_spawnbaseline2);\n\t\t\t\tMSG_WriteEntity (&client->netchan.message, state->number);\n\n\t\t\t\tMSG_WriteShort (&client->netchan.message, state->modelindex);\n\t\t\t\tMSG_WriteShort (&client->netchan.message, state->frame);\n\t\t\t\tMSG_WriteByte (&client->netchan.message, (int)state->colormap);\n\t\t\t\tMSG_WriteByte (&client->netchan.message, (int)state->skinnum);\n\t\t\t\tfor (i=0 ; i<3 ; i++)\n\t\t\t\t{\n\t\t\t\t\tMSG_WriteCoord(&client->netchan.message, state->origin[i]);\n\t\t\t\t\tMSG_WriteAngle(&client->netchan.message, state->angles[i]);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (client->protocol == SCP_FITZ666 && (state->modelindex > 255 || state->frame > 255 || state->trans != 255 || state->scale != 16))\n\t\t\t{\n\t\t\t\tint fl = 0;\n\t\t\t\tif (state->modelindex > 255)\tfl |= FITZ_B_LARGEMODEL;\n\t\t\t\tif (state->frame > 255)\t\t\tfl |= FITZ_B_LARGEFRAME;\n\t\t\t\tif (state->trans != 255)\t\tfl |= FITZ_B_ALPHA;\n\t\t\t\tif (state->scale != 16)\t\t\tfl |= RMQFITZ_B_SCALE;\n\n\t\t\t\tMSG_WriteByte(&client->netchan.message, svcfitz_spawnbaseline2);\n\t\t\t\tMSG_WriteEntity (&client->netchan.message, state->number);\n\t\t\t\tMSG_WriteByte(&client->netchan.message, fl);\n\t\t\t\tif (fl & FITZ_B_LARGEMODEL)\n\t\t\t\t\tMSG_WriteShort (&client->netchan.message, state->modelindex);\n\t\t\t\telse\n\t\t\t\t\tMSG_WriteByte (&client->netchan.message, state->modelindex);\n\t\t\t\tif (fl & FITZ_B_LARGEFRAME)\n\t\t\t\t\tMSG_WriteShort (&client->netchan.message, state->frame);\n\t\t\t\telse\n\t\t\t\t\tMSG_WriteByte (&client->netchan.message, state->frame);\n\t\t\t\tMSG_WriteByte (&client->netchan.message, state->colormap);\n\t\t\t\tMSG_WriteByte (&client->netchan.message, state->skinnum);\n\t\t\t\tfor (i=0 ; i<3 ; i++)\n\t\t\t\t{\n\t\t\t\t\tMSG_WriteCoord(&client->netchan.message, state->origin[i]);\n\t\t\t\t\tMSG_WriteAngle(&client->netchan.message, state->angles[i]);\n\t\t\t\t}\n\t\t\t\tif (fl & FITZ_B_ALPHA)\n\t\t\t\t\tMSG_WriteByte (&client->netchan.message, state->trans);\n\t\t\t\tif (fl & RMQFITZ_B_SCALE)\n\t\t\t\t\tMSG_WriteByte (&client->netchan.message, state->scale);\n\t\t\t}\n\t\t\telse if (state->modelindex)\n\t\t\t{\n\t\t\t\tMSG_WriteByte(&client->netchan.message, svc_spawnbaseline);\n\t\t\t\tMSG_WriteEntity (&client->netchan.message, state->number);\n\n\t\t\t\tif (client->protocol == SCP_BJP3)\n\t\t\t\t\tMSG_WriteShort (&client->netchan.message, state->modelindex&0xffff);\n\t\t\t\telse if (state->modelindex > 255)\n\t\t\t\t\tMSG_WriteByte (&client->netchan.message, 0);\t//invalid modelindex. at least try to give something\n\t\t\t\telse\n\t\t\t\t\tMSG_WriteByte (&client->netchan.message, state->modelindex&0xff);\n\t\t\t\tMSG_WriteByte (&client->netchan.message, state->frame & 0xff);\n\t\t\t\tMSG_WriteByte (&client->netchan.message, state->colormap & 0xff);\n\t\t\t\tMSG_WriteByte (&client->netchan.message, state->skinnum & 0xff);\n\t\t\t\tfor (i=0 ; i<3 ; i++)\n\t\t\t\t{\n\t\t\t\t\tMSG_WriteCoord(&client->netchan.message, state->origin[i]);\n\t\t\t\t\tMSG_WriteAngle(&client->netchan.message, state->angles[i]);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tclient->prespawn_idx++;\n\t\t}\n\t}\n\tif (client->prespawn_stage == PRESPAWN_SPAWN)\n\t{\n\t\t//we'll spawn the client and then send all the updating stuff only when we know the channel is clear, by pinging the client for it.\n\t\tif (ISNQCLIENT(client))\n\t\t{\n\t\t\t//effectively a cmd spawn... but also causes the client to actually send the player's name too.\n\t\t\tClientReliableWrite_Begin (client, svcnq_signonnum, 2);\n\t\t\tClientReliableWrite_Byte (client, 2);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tchar *cmd = va(\"cmd spawn %i\\n\",svs.spawncount);\n\t\t\tClientReliableWrite_Begin(client, svc_stufftext, 2+strlen(cmd));\n\t\t\tClientReliableWrite_String(client, cmd);\n\t\t}\n\t\tclient->prespawn_stage++;\n\t\tclient->prespawn_idx = 0;\n\t\tclient->prespawn_idx2 = 0;\n\t}\n\n\t//this is extra stuff that will happen after we're on the server\n\n\tif (client->prespawn_stage == PRESPAWN_BRUSHES)\n\t{\t//when brush editing, connecting clients need a copy of all the brushes.\n\t\twhile (client->netchan.message.cursize < maxsize)\n\t\t{\n#ifdef TERRAIN\n\t\t\tif (!SV_Prespawn_Brushes(&client->netchan.message, &client->prespawn_idx, &client->prespawn_idx2))\n#endif\n\t\t\t{\n\t\t\t\tclient->prespawn_stage++;\n\t\t\t\tclient->prespawn_idx = 0;\n\t\t\t\tclient->prespawn_idx2 = 0;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n}\n\n/*\n==================\nSV_PreSpawn_f\n==================\n*/\nvoid SVQW_PreSpawn_f (void)\n{\n\tunsigned\tcheck;\n\n\tif (host_client->state != cs_connected)\n\t{\n\t\tCon_Printf (\"prespawn not valid -- already spawned\\n\");\n\t\treturn;\n\t}\n\tif (host_client->prespawn_stage != PRESPAWN_MAPCHECK)\n\t{\n\t\tCon_Printf(\"Wrong stage for prespawn command\\n\");\n\t\treturn;\n\t}\n\n\t// handle the case of a level changing while a client was connecting\n\tif ( atoi(Cmd_Argv(1)) != svs.spawncount)\n\t{\n\t\tCon_Printf (\"SV_PreSpawn_f from different level\\n\");\n\t\t//FIXME: we shouldn't need the following line.\n\t\tSV_New_f ();\n\t\treturn;\n\t}\n\n\tif (host_client->prespawn_stage == PRESPAWN_MAPCHECK)\n\t{\n\t\tcheck = atoi(Cmd_Argv(3));\n\n//\t\tCon_DPrintf(\"Client check = %d\\n\", check);\n\n\t\tif (sv_mapcheck.value && check != sv.world.worldmodel->checksum &&\n\t\t\tCOM_RemapMapChecksum(sv.world.worldmodel, check) != COM_RemapMapChecksum(sv.world.worldmodel, LittleLong(sv.world.worldmodel->checksum2)))\n#ifdef SERVER_DEMO_PLAYBACK\n\t\tif (!sv.demofile || (sv.demofile && !sv.democausesreconnect))\t//demo playing causes no check. If it's the return level, check anyway to avoid that loophole.\n#endif\n\t\t{\n\t\t\tchar *msg;\n\t\t\tSV_ClientTPrintf (host_client, PRINT_HIGH,\n\t\t\t\t\"Map model file does not match (%s), %#X != %#X/%#X.\\nYou may need a new version of the map, or the proper install files.\\n\",\n\t\t\t\tsv.modelname, check, sv.world.worldmodel->checksum, sv.world.worldmodel->checksum2);\n\n\n\t\t\tmsg = va(\"\\n//kickfile \\\"%s\\\"\\n\", sv.modelname);\n\t\t\tClientReliableWrite_Begin (host_client, svc_stufftext, 3+strlen(msg));\n\t\t\tClientReliableWrite_String (host_client, msg);\n\t\t\tSV_DropClient (host_client);\n\t\t\treturn;\n\t\t}\n\t\thost_client->checksum = check;\n\t\thost_client->prespawn_stage = PRESPAWN_MAPCHECK+1;\n\t\thost_client->prespawn_idx = 0;\n\t\treturn;\n\t}\n}\n\n/*\n==================\nSV_Spawn_f\n==================\n*/\nvoid SVQW_Spawn_f (void)\n{\n\tint\t\ti;\n\tclient_t\t*client, *split;\n\tedict_t\t*ent;\n\n#ifdef QUAKESTATS\n\tint secret_total, secret_found, monsters_total, monsters_found;\n#endif\n\n\tif (host_client->state != cs_connected)\n\t{\n\t\tCon_Printf (\"Spawn not valid -- already spawned\\n\");\n\t\treturn;\n\t}\n\tif (host_client->prespawn_stage <= PRESPAWN_SPAWN)\n\t{\n\t\tCon_Printf (\"%s sent spawn without prespawn!\\n\", host_client->name);\n\t\tSV_New_f ();\n\t\treturn;\n\t}\n\n// handle the case of a level changing while a client was connecting\n\tif ( atoi(Cmd_Argv(1)) != svs.spawncount)\n\t{\n\t\tCon_Printf (\"SV_Spawn_f from different level\\n\");\n\t\tSV_New_f ();\n\t\treturn;\n\t}\n\n// send all current names, colors, and frag counts\n\t// FIXME: is this a good thing?\n//\tSZ_Clear (&host_client->netchan.message);\n\n// send current status of all other players\n\n\t// normally this could overflow, but no need to check due to backbuf\n\tfor (i=0, client = svs.clients ; i<svs.allocated_client_slots ; i++, client++)\n\t\tSV_FullClientUpdate(client, host_client);\n#ifdef MVD_RECORDING\n\tSV_MVD_FullClientUpdate(NULL, host_client);\n#endif\n\n// send all current light styles\n\tfor (i=0 ; i<sv.maxlightstyles ; i++)\n\t{\n#ifdef SERVER_DEMO_PLAYBACK\n\t\tif (sv.democausesreconnect)\n\t\t{\n\t\t\tif (i >= MAX_STANDARDLIGHTSTYLES)\n\t\t\t\tcontinue;\n\t\t\tClientReliableWrite_Begin (host_client, svc_lightstyle,\n\t\t\t\t3 + (sv.demolightstyles[i] ? strlen(sv.demolightstyles[i]) : 1));\n\t\t\tClientReliableWrite_Byte (host_client, (char)i);\n\t\t\tClientReliableWrite_String (host_client, sv.demolightstyles[i]);\n\t\t}\n\t\telse\n#endif\n\t\tSV_SendLightstyle(host_client, NULL, i, true);\n\t}\n\n#ifdef HLSERVER\n\tif (svs.gametype == GT_HALFLIFE)\n\t{\n\t\tfor (split = host_client; split; split = split->controlled)\n\t\t{\n\t\t\tsplit->entgravity = 1;\n\t\t\tsplit->maxspeed = 320;\n\n\t\t\tSVHL_PutClientInServer(split);\n\t\t}\n\n\t\tsecret_total = 0;\n\t\tsecret_found = 0;\n\t\tmonsters_total = 0;\n\t\tmonsters_found = 0;\n\t}\n\telse\n#endif\n\t{\n\t\t// set up the edict\n\t\tfor (split = host_client; split; split = split->controlled)\n\t\t{\n\t\t\tent = split->edict;\n\n\t\t\tif (split->spawned)\t//minimal setup\n\t\t\t{\n\t\t\t\tsplit->entgravity = ent->xv->gravity;\n\t\t\t\tsplit->maxspeed = ent->xv->maxspeed;\n#ifdef HEXEN2\n\t\t\t\tsplit->playerclass = ent->xv->playerclass;\n#endif\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tSV_SetUpClientEdict(split, ent);\n\t\t\t}\n\n\t\t//\n\t\t// force stats to be updated\n\t\t//\n\t\t\tmemset (split->statsi, 0, sizeof(split->statsi));\n\t\t\tmemset (split->statsf, 0, sizeof(split->statsf));\n\t\t\tmemset (split->statss, 0, sizeof(split->statss));\n\t\t}\n\n#ifdef QUAKESTATS\n\t\tsecret_total = pr_global_struct->total_secrets;\n\t\tsecret_found = pr_global_struct->found_secrets;\n\t\tmonsters_total = pr_global_struct->total_monsters;\n\t\tmonsters_found = pr_global_struct->killed_monsters;\n#endif\n\t}\n\n#ifdef QUAKESTATS\n\tClientReliableWrite_Begin (host_client, svcqw_updatestatlong, 6);\n\tClientReliableWrite_Byte (host_client, STAT_TOTALSECRETS);\n\tClientReliableWrite_Long (host_client, secret_total);\n\n\tClientReliableWrite_Begin (host_client, svcqw_updatestatlong, 6);\n\tClientReliableWrite_Byte (host_client, STAT_TOTALMONSTERS);\n\tClientReliableWrite_Long (host_client, monsters_total);\n\n\tClientReliableWrite_Begin (host_client, svcqw_updatestatlong, 6);\n\tClientReliableWrite_Byte (host_client, STAT_SECRETS);\n\tClientReliableWrite_Long (host_client, secret_found);\n\n\tClientReliableWrite_Begin (host_client, svcqw_updatestatlong, 6);\n\tClientReliableWrite_Byte (host_client, STAT_MONSTERS);\n\tClientReliableWrite_Long (host_client, monsters_found);\n#endif\n\n\t// get the client to check and download skins\n\t// when that is completed, a begin command will be issued\n\tClientReliableWrite_Begin (host_client, svc_stufftext, 8);\n\tClientReliableWrite_String (host_client, \"skins\\n\" );\n\n\tif (sv.allocated_client_slots > 1 && svs.gametype == GT_PROGS)\n\t{\t//okay, so nq player physics don't suppot prediction.\n\t\t//if we use qw physics in nq mods then we risk breaking things.\n\t\t//the only progs many players will have is the vanilla nq one.\n\t\t//so prediction is broken on most people's quicky servers.\n\t\t//which really sucks.\n\t\t//so let multiplayer people know what's going on so that they don't think its an actual bug, and can harass the admin to get it fixed in mods that allow for it.\n\t\tif (!strcmp(sv_nqplayerphysics.string, \"auto\") || !strcmp(sv_nqplayerphysics.string, \"\"))\n\t\t{\n\t\t\tif (svprogfuncs&&(PR_FindFunction(svprogfuncs, \"SV_RunClientCommand\", PR_ANY)\t//modern mod with custom physics\n\t\t\t\t||PR_FindFunction(svprogfuncs, \"SV_PlayerPhysics\", PR_ANY)))\t//lame dp mod with hacky player physics.\n\t\t\t\t;\t//say nothing. its annoying. this mod ain't gonna have weird badly defined behaviour anyway.\n\t\t\telse if (sv_nqplayerphysics.ival == 2)\n\t\t\t\tSV_PrintToClient(host_client, PRINT_MEDIUM, CON_WARNING\"Movement prediction may not match server due to non-quakeworld mod compatibilty\\n\");\n\t\t\telse if (sv_nqplayerphysics.ival)\n\t\t\t\tSV_PrintToClient(host_client, PRINT_HIGH, CON_WARNING\"Movement prediction is disabled in favour of non-quakeworld mod compatibilty\\n\");\n//\t\t\telse\n//\t\t\t\tSV_PrintToClient(host_client, PRINT_LOW, CON_NOTICE\"Movement prediction works, yay this server is awesome and good and all that is right with the world\\n\");\n\t\t}\n\t}\n}\n\n/*\n==================\nSV_SpawnSpectator\n==================\n*/\nvoid SV_SpawnSpectator (void)\n{\n\tint\t\ti;\n\tedict_t\t*e;\n\n\tVectorClear (sv_player->v->origin);\n\tVectorClear (sv_player->v->view_ofs);\n\tsv_player->v->view_ofs[2] = DEFAULT_VIEWHEIGHT;\n\tsv_player->v->movetype = MOVETYPE_NOCLIP;\n\n\t// search for an info_playerstart to spawn the spectator at\n\t//this is only useful when a mod doesn't nativly support spectators. old qw on nq mods.\n\n\tfor (i=svs.allocated_client_slots+1 ; i<sv.world.num_edicts ; i++)\n\t{\n\t\te = EDICT_NUM_PB(svprogfuncs, i);\n\t\tif (!strcmp(PR_GetString(svprogfuncs, e->v->classname), \"info_player_start\"))\n\t\t{\n\t\t\tVectorCopy (e->v->origin, sv_player->v->origin);\n\t\t\treturn;\n\t\t}\n\t}\n}\n\nvoid SV_DespawnClient(client_t *cl)\n{\t//this disconnects the client from its entity state\n\tif (!cl->spawned)\n\t\treturn;\t//nothing to do.\n\tcl->spawned = false;\n\n#ifdef Q2SERVER\n\tif (ge)\n\t{\n\t\tge->ClientDisconnect(cl->q2edict);\n\t\treturn;\n\t}\n#endif\n\n\n\tif (svprogfuncs)\n\t{\n\t\tif (host_initialized)\n\t\t{\n#ifdef VM_Q1\n\t\t\tif (svs.gametype == GT_Q1QVM)\n\t\t\t{\n\t\t\t\tQ1QVM_DropClient(cl);\n\t\t\t}\n\t\t\telse\n#endif\n\t\t\t{\n\t\t\t\tif (!cl->spectator)\n\t\t\t\t{\n\t\t\t\t// call the prog function for removing a client\n\t\t\t\t// this will set the body to a dead frame, among other things\n\t\t\t\t\tpr_global_struct->self = EDICT_TO_PROG(svprogfuncs, cl->edict);\n\t\t\t\t\tif (pr_global_ptrs->ClientDisconnect)\n\t\t\t\t\t\tPR_ExecuteProgram (svprogfuncs, pr_global_struct->ClientDisconnect);\n\t\t\t\t\tsv.spawned_client_slots--;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t// call the prog function for removing a client\n\t\t\t\t\t// this will set the body to a dead frame, among other things\n\t\t\t\t\tpr_global_struct->self = EDICT_TO_PROG(svprogfuncs, cl->edict);\n\t\t\t\t\tif (SpectatorDisconnect)\n\t\t\t\t\t\tPR_ExecuteProgram (svprogfuncs, SpectatorDisconnect);\n\t\t\t\t\tsv.spawned_observer_slots--;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (progstype == PROG_NQ)\n\t\t\t\tED_Clear(svprogfuncs, cl->edict);\n\t\t}\n\n\t\tif (svprogfuncs && cl->edict && cl->edict->v)\n\t\t\tcl->edict->v->frags = 0;\n\t}\n}\n\nvoid SV_Begin_Core(client_t *split)\n{\t//this is the client-protocol-independant core, for q1/q2 gamecode\n\tclient_t\t*oh;\n#ifdef HEXEN2\n\tif (progstype == PROG_H2 && split->playerclass)\n\t\tsplit->edict->xv->playerclass = split->playerclass;\t//make sure it's set the same as the userinfo\n#endif\n\n\tif (split->spawned)\n\t{\n\t\tif (svprogfuncs)\n\t\t{\n\t\t\t//NEH_RESTOREGAME\n\t\t\t//officially RestoreGame tells the mod when the game has been loaded.\n\t\t\t//this allows mods to send any stuffcmds the client will have forgotten.\n\t\t\t//the original intention would not have been client-specific (and indeed nehahra only saves in singleplayer)\n\t\t\t//doing it elsewhere unfortunately results in race conditions.\n\t\t\tfunc_t f = PR_FindFunction(svprogfuncs, \"RestoreGame\", PR_ANY);\n\t\t\tif (f)\n\t\t\t{\n\t\t\t\tpr_global_struct->time = sv.world.physicstime;\n\t\t\t\tpr_global_struct->self = EDICT_TO_PROG(svprogfuncs, split->edict);\n\t\t\t\tPR_ExecuteProgram (svprogfuncs, f);\n\t\t\t}\n\n\t\t\tSV_SendFixAngle(split, NULL, FIXANGLE_FIXED, false);\n\t\t\tsplit->edict->v->fixangle = FIXANGLE_NO;\t//no point doing it again\n\t\t}\n\t\treturn;\n\t}\n\tsplit->spawned = true;\n\tsplit->lastcmd.cursor_entitynumber = 0;\t//make sure this doesn't get reapplied between maps...\n\tsplit->lastcmd.buttons = 0;\n\tsplit->lastcmd.impulse = 0;\n\tsplit->lastcmd.weapon = 0;\n\n#ifdef Q2SERVER\n\tif (ge)\n\t{\n\t\tchar userinfo[8192];\n\t\tInfoBuf_ToString(&split->userinfo, userinfo, sizeof(userinfo), NULL, NULL, NULL, NULL, NULL);\n\t\tge->ClientUserinfoChanged (split->q2edict, userinfo);\t//tell the gamecode\n\t\tSV_ExtractFromUserinfo(split, true);\t//let the server routines know\n\n\t\tge->ClientBegin(split->q2edict);\n\t\tsplit->istobeloaded = false;\n\t\tsv.spawned_client_slots++;\n\t}\n\telse\n#endif\n\t{\n#ifdef HAVE_LEGACY\n\t\tsplit->edict->xv->clientcolors = split->playercolor;\n\t\tif (progstype != PROG_QW)\n\t\t{\t//some redundant things, purely for dp compat\n\t\t\teval_t *eval;\n\t\t\tedict_t *ent = split->edict;\n\t\t\tent->v->team = split->playercolor&15;\n\n\t\t\teval = svprogfuncs->GetEdictFieldValue(svprogfuncs, ent, \"playermodel\", ev_string, NULL);\n\t\t\tif (eval)\n\t\t\t\tsvprogfuncs->SetStringField(svprogfuncs, ent, &eval->string, InfoBuf_ValueForKey(&split->userinfo, \"model\"), false);\n\n\t\t\teval = svprogfuncs->GetEdictFieldValue(svprogfuncs, ent, \"playerskin\", ev_string, NULL);\n\t\t\tif (eval)\n\t\t\t\tsvprogfuncs->SetStringField(svprogfuncs, ent, &eval->string, InfoBuf_ValueForKey(&split->userinfo, \"skin\"), false);\n\n\t\t\teval = svprogfuncs->GetEdictFieldValue(svprogfuncs, ent, \"netaddress\", ev_string, NULL);\n\t\t\tif (eval)\n\t\t\t{\n\t\t\t\tchar buf[256];\n\t\t\t\tif (split->netchan.remote_address.type == NA_LOOPBACK)\n\t\t\t\t\tsvprogfuncs->SetStringField(svprogfuncs, ent, &eval->string, \"local\", false);\t//sigh...\n\t\t\t\telse\n\t\t\t\t\tsvprogfuncs->SetStringField(svprogfuncs, ent, &eval->string, NET_AdrToString(buf, sizeof(buf), &split->netchan.remote_address), false);\n\t\t\t}\n\t\t}\n#endif\n\n\n\t\tif (split->spectator)\n\t\t{\n\t\t\tSV_SpawnSpectator ();\n\n\t\t\tif (SpectatorConnect)\n\t\t\t{\n\t\t\t\t//keep the spectator tracking the player from the previous map\n\t\t\t\tif (split->spec_track > 0)\n\t\t\t\t\tsplit->edict->v->goalentity = EDICT_TO_PROG(svprogfuncs, EDICT_NUM_UB(svprogfuncs, split->spec_track));\n\t\t\t\telse\n\t\t\t\t\tsplit->edict->v->goalentity = 0;\n\n\n\t\t\t\t// copy spawn parms out of the client_t\n\t\t\t\tSV_SpawnParmsToQC(split);\n\n\t\t\t\t// call the spawn function\n\t\t\t\t{\n\t\t\t\t\tglobalvars_t *pr_globals = PR_globals(svprogfuncs, PR_CURRENT);\n\t\t\t\t\tpr_global_struct->time = sv.world.physicstime;\n\t\t\t\t\tpr_global_struct->self = EDICT_TO_PROG(svprogfuncs, split->edict);\n\n\t\t\t\t\tif (pr_globals)\n\t\t\t\t\t\tG_FLOAT(OFS_PARM0) = split->csqcactive;\t//this arg is part of EXT_CSQC_1, but doesn't have to be supported by the mod\n\t\t\t\t\tPR_ExecuteProgram (svprogfuncs, SpectatorConnect);\n\t\t\t\t}\n\t\t\t}\n\t\t\tsv.spawned_observer_slots++;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tsv.spawned_client_slots++;\n\n\t\t\tif (svprogfuncs)\n\t\t\t{\n\t\t\t\teval_t *eval, *eval2;\n\t\t\t\teval = PR_FindGlobal(svprogfuncs, \"ClientReEnter\", 0, NULL);\n\t\t\t\tif (eval && eval->function && split->spawninfo)\n\t\t\t\t{\n\t\t\t\t\tglobalvars_t *pr_globals = PR_globals(svprogfuncs, PR_CURRENT);\n\t\t\t\t\tsize_t j;\n\t\t\t\t\tedict_t *ent;\n\t\t\t\t\tent = split->edict;\n\t\t\t\t\tj = strlen(split->spawninfo);\n\t\t\t\t\tWorld_UnlinkEdict((wedict_t*)ent);\n\t\t\t\t\tsvprogfuncs->restoreent(svprogfuncs, split->spawninfo, &j, ent);\n\n\t\t\t\t\teval2 = svprogfuncs->GetEdictFieldValue(svprogfuncs, ent, \"stats_restored\", ev_float, NULL);\n\t\t\t\t\tif (eval2)\n\t\t\t\t\t\teval2->_float = 1;\n\t\t\t\t\tSV_SpawnParmsToQC(split);\n\t\t\t\t\tpr_global_struct->time = sv.world.physicstime;\n\t\t\t\t\tpr_global_struct->self = EDICT_TO_PROG(svprogfuncs, ent);\n\t\t\t\t\tG_FLOAT(OFS_PARM0) = sv.time - split->spawninfotime;\n\t\t\t\t\tPR_ExecuteProgram(svprogfuncs, eval->function);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t// copy spawn parms out of the client_t\n\t\t\t\t\tSV_SpawnParmsToQC(split);\n\n\t\t\t\t\t// call the spawn function\n#ifdef VM_Q1\n\t\t\t\t\tif (svs.gametype == GT_Q1QVM)\n\t\t\t\t\t\tQ1QVM_ClientConnect(split);\n\t\t\t\t\telse\n#endif\n\t\t\t\t\t{\n\t\t\t\t\t\tglobalvars_t *pr_globals = PR_globals(svprogfuncs, PR_CURRENT);\n\n\t\t\t\t\t\tpr_global_struct->time = sv.world.physicstime;\n\t\t\t\t\t\tpr_global_struct->self = EDICT_TO_PROG(svprogfuncs, split->edict);\n\t\t\t\t\t\tif (pr_globals)\n\t\t\t\t\t\t\tG_FLOAT(OFS_PARM0) = split->csqcactive;\t//this arg is part of EXT_CSQC_1, but doesn't have to be supported by the mod\n\t\t\t\t\t\tsv.skipbprintclient = host_client;\n\t\t\t\t\t\tif (pr_global_ptrs->ClientConnect)\n\t\t\t\t\t\t\tPR_ExecuteProgram (svprogfuncs, *pr_global_ptrs->ClientConnect);\n\t\t\t\t\t\tsv.skipbprintclient = NULL;\n\n\t\t\t\t\t\t// actually spawn the player\n\t\t\t\t\t\tpr_global_struct->time = sv.world.physicstime;\n\t\t\t\t\t\tpr_global_struct->self = EDICT_TO_PROG(svprogfuncs, split->edict);\n\t\t\t\t\t\tif (pr_global_ptrs->PutClientInServer)\n\t\t\t\t\t\t\tPR_ExecuteProgram (svprogfuncs, *pr_global_ptrs->PutClientInServer);\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tsplit->edict->v->health = 100;\n\t\t\t\t\t\t\tsplit->edict->v->mins[0] = -16;\n\t\t\t\t\t\t\tsplit->edict->v->mins[1] = -16;\n\t\t\t\t\t\t\tsplit->edict->v->mins[2] = -24;\n\t\t\t\t\t\t\tsplit->edict->v->maxs[0] = 16;\n\t\t\t\t\t\t\tsplit->edict->v->maxs[1] = 16;\n\t\t\t\t\t\t\tsplit->edict->v->maxs[2] = 32;\n\t\t\t\t\t\t\tsplit->edict->v->view_ofs[2] = DEFAULT_VIEWHEIGHT;\n\t\t\t\t\t\t\tif (sv.world.worldmodel->fromgame == fg_quake3)\n\t\t\t\t\t\t\t\tsplit->edict->v->view_ofs[2] = 26;\t//q3 defaults to slightly higher view heights (though same player size as quake).\n\t\t\t\t\t\t\tif (sv.world.worldmodel->hulls[1].clip_maxs[2]-sv.world.worldmodel->hulls[1].clip_mins[2]>0)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tVectorCopy(sv.world.worldmodel->hulls[1].clip_mins, split->edict->v->mins);\n\t\t\t\t\t\t\t\tVectorCopy(sv.world.worldmodel->hulls[1].clip_maxs, split->edict->v->maxs);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tsplit->edict->v->movetype = MOVETYPE_NOCLIP;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tVectorCopy(split->edict->v->origin, split->edict->v->oldorigin);\t//make sure oldorigin isn't 0 0 0 or anything too clumsy like that. stuck somewhere killable is better than stuck outside the map.\n\t\t\t\t\t}\n\n\t\t\t\t\toh = host_client;\n\t\t\t\t\thost_client = split;\n\t\t\t\t\tsv_player = host_client->edict;\n\t\t\t\t\tSV_PreRunCmd();\n\t\t\t\t\t{\n\t\t\t\t\t\tusercmd_t cmd;\n\t\t\t\t\t\tmemset(&cmd, 0, sizeof(cmd));\n\t\t\t\t\t\tcmd.msec = 0;\n#define ANGLE2SHORT(x) (x) * (65536/360.0)\n\t\t\t\t\t\tcmd.angles[0] = ANGLE2SHORT(split->edict->v->v_angle[0]);\n\t\t\t\t\t\tcmd.angles[1] = ANGLE2SHORT(split->edict->v->v_angle[1]);\n\t\t\t\t\t\tcmd.angles[2] = ANGLE2SHORT(split->edict->v->v_angle[2]);\n\t\t\t\t\t\tSV_RunCmd(&cmd, false);\n\t\t\t\t\t}\n\t\t\t\t\tSV_PostRunCmd();\n\t\t\t\t\tsplit->lastruncmd = sv.time*1000;\n\t\t\t\t\thost_client = oh;\n\t\t\t\t\tsv_player = oh?oh->edict:NULL;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tSV_SendFixAngle(split, NULL, FIXANGLE_FIXED, false);\n\t\tsplit->edict->v->fixangle = FIXANGLE_NO;\t//no point doing it again\n\n#ifdef HAVE_LEGACY\n\t\tsplit->dp_ping = NULL;\n\t\tsplit->dp_pl = NULL;\n\t\tif (progstype == PROG_NQ)\n\t\t{\n\t\t\tsplit->dp_ping = (float*)sv.world.progs->GetEdictFieldValue(sv.world.progs, split->edict, \"ping\", ev_float, NULL);\n\t\t\tsplit->dp_pl = (float*)sv.world.progs->GetEdictFieldValue(sv.world.progs, split->edict, \"ping_packetloss\", ev_float, NULL);\n\t\t}\n#endif\n\t}\n}\n\n/*\n==================\nSV_Begin_f\n==================\n*/\nvoid SV_Begin_f (void)\n{\n\tclient_t\t*split;\n\tunsigned pmodel = 0, emodel = 0;\n\n\tif (!SV_CheckRealIP(host_client, true))\n\t{\n\t\tif (ISQ2CLIENT(host_client))\n\t\t\tClientReliableWrite_Begin (host_client, svcq2_stufftext, 13+strlen(Cmd_Args()));\n\t\telse\n\t\t\tClientReliableWrite_Begin (host_client, svc_stufftext, 13+strlen(Cmd_Args()));\n\t\tClientReliableWrite_String (host_client, va(\"cmd begin %s\\n\", Cmd_Args()));\n\t\treturn;\n\t}\n\n\tif (host_client->state == cs_spawned)\n\t\treturn; // don't begin again\n\n\tfor (split = host_client; split; split = split->controlled)\n\t\tsplit->state = cs_spawned;\n\n\t// handle the case of a level changing while a client was connecting\n\tif ( atoi(Cmd_Argv(1)) != svs.spawncount)\n\t{\n\t\tCon_Printf (\"SV_Begin_f from different level\\n\");\n\t\tSV_New_f ();\n\t\treturn;\n\t}\n\n\tfor (split = host_client; split; split = split->controlled)\n\t{\t//tell the gamecode they're ready\n\t\tSV_Begin_Core(split);\n\t}\n\n\t// clear the net statistics, because connecting gives a bogus picture\n\thost_client->netchan.frame_latency = 0;\n\thost_client->netchan.frame_rate = 0;\n\thost_client->netchan.drop_count = 0;\n\n\t//check he's not cheating\n\tif (progstype == PROG_QW)\n\t{\n\t\tpmodel = atoi(InfoBuf_ValueForKey (&host_client->userinfo, \"pmodel\"));\n\t\temodel = atoi(InfoBuf_ValueForKey (&host_client->userinfo, \"emodel\"));\n\n\t\tif (pmodel != sv.model_player_checksum ||\n\t\t\temodel != sv.eyes_player_checksum)\n\t\t\tSV_BroadcastTPrintf (PRINT_HIGH, \"warning: %s eyes or player model does not match\\n\", host_client->name);\n\t}\n\n#ifdef MVD_RECORDING\n\tSV_MVD_AutoRecord();\n#endif\n}\n\n//=============================================================================\n\n#ifdef NQPROT\n//dp downloads are a 2-stream system\n//the server->client stream is as you'd expect. except that its unreliable rather than reliable\n//the client->server stream contains no actual data.\n//when c2s has a hole, the s2c stream is reset to the last-known 'good' position.\n//eventually the client is left acking packets with no data in them, the server then tells the client that the download is complete.\n//the client does no checks to see if there's a hole, other than the crc\n\n//so any single lost packet (even client->server) means that the entire stream will be set back by your ping time\nvoid SV_DarkPlacesDownloadChunk(client_t *cl, sizebuf_t *msg)\n{\n#define MAXDPDOWNLOADCHUNK 1024\n\tchar buffer[MAXDPDOWNLOADCHUNK];\n\n\tint size, start;\n\n\tif (!ISNQCLIENT(cl))\n\t\treturn;\n\tif (!cl->download)\n\t\treturn;\n\n\tif (!cl->downloadstarted)\n\t\treturn;\n\n\tif (cl->num_backbuf)\n\t\treturn;\n\n\tsize = 1024;\t//fixme\n\n\tif (size > msg->maxsize - msg->cursize)\n\t\tsize = msg->maxsize - msg->cursize;\n\tif (size <= 7)\n\t\treturn;\t//no space.\n\tsize -= 7;\n\n\tif (size > MAXDPDOWNLOADCHUNK)\t//don't clog it too much\n\t\tsize = MAXDPDOWNLOADCHUNK;\n\n\tstart = VFS_TELL(cl->download);\n\tif (start+size > cl->downloadsize)\t//clamp to the size of the file.\n\t\tsize = cl->downloadsize - start;\n\n\tsize = VFS_READ(cl->download, buffer, size);\n\tif (size < 0)\n\t\tsize = 0;\n\n\tMSG_WriteByte(msg, svcdp_downloaddata);\n\tMSG_WriteLong (msg, start);\n\tMSG_WriteShort (msg, size);\n\tSZ_Write(msg, buffer, size);\n}\n\nvoid SVDP_StartDownload_f(void)\n{\n//\tif (host_client->protocol != SCP_DARKPLACES7)\n//\t\treturn;\n\tif (!host_client->download)\n\t\treturn;\n\thost_client->downloadstarted = true;\n\thost_client->downloadacked = 0;\n}\n\nvoid SV_DarkPlacesDownloadAck(client_t *cl)\n{\n\tint start = MSG_ReadLong();\n\tint size = (unsigned short)MSG_ReadShort();\n\n\tif (!cl->download)\n\t\treturn;\n\n\tif (start == cl->downloadsize && !size)\n\t{\n\t\tchar *s;\n\t\tconst hashfunc_t *hfunc = &hash_crc16;\n\t\tvoid *hctx = alloca(hfunc->contextsize);\n\t\tint pos=0, csize;\n\t\tqbyte chunk[65536];\n\t\thfunc->init(hctx);\n\t\tVFS_SEEK(host_client->download, 0);\n\t\twhile (pos < host_client->downloadsize)\n\t\t{\n\t\t\tcsize = sizeof(chunk);\n\t\t\tif (pos + csize > host_client->downloadsize)\n\t\t\t\tcsize = host_client->downloadsize - pos;\n\t\t\tif (csize != VFS_READ(host_client->download, chunk, csize))\n\t\t\t\tbreak;\n\t\t\thfunc->process(hctx, chunk, csize);\n\t\t\tpos += csize;\n\t\t}\n\n\t\ts = va(\"\\ncl_downloadfinished %u %i \\\"%s\\\"\\n\", (unsigned int)host_client->downloadsize, hashfunc_terminate_uint(hfunc, hctx), host_client->downloadfn);\n\t\tClientReliableWrite_Begin (cl, svc_stufftext, 2+strlen(s));\n\t\tClientReliableWrite_String(cl, s);\n\n\t\tVFS_CLOSE(host_client->download);\n\t\thost_client->send_message = true;\n\t\thost_client->download = NULL;\n\t\thost_client->downloadsize = 0;\n\t}\n\telse if (start != cl->downloadacked || !size)\n\t{\n\t\t//packetloss\n\t\tVFS_SEEK(cl->download, cl->downloadacked);\n\t\tcl->downloadcount = cl->downloadacked;\n\t}\n\telse\n\t\tcl->downloadacked = start+size;\t//successful packet\n}\n#endif\n\nstatic void SV_NextChunkedDownload(unsigned int chunknum, int ezpercent, int ezfilenum, int chunks)\n{\n\tchar buffer[DLBLOCKSIZE];\n\tqbyte oobdata[1+ (sizeof(\"\\\\chunk\")-1) + 4 + 1 + 4 + DLBLOCKSIZE];\n\tsizebuf_t *msg, msg_oob;\n\tint i;\n\tint error = false;\n//can't support this yet. at least forcing to 1 avoids too bad infinite loops. this can be a nasty dos attack on a server.  \tif (chunks < 1)\n\t\tchunks = 1;\n\n\tmsg = &host_client->datagram;\n\n\tif (chunknum == -1)\n\t\terror = 2;\t//silent, don't report it\n\telse if (chunknum*DLBLOCKSIZE > host_client->downloadsize)\n\t{\n\t\tSV_ClientTPrintf (host_client, PRINT_HIGH, \"Warning: Invalid file chunk requested %u to %u of %u.\\n\", chunknum*DLBLOCKSIZE, (chunknum+1)*DLBLOCKSIZE, host_client->downloadsize);\n\t\terror = 2;\n\t}\n\n\tif (!error && VFS_SEEK (host_client->download, (qofs_t)chunknum*DLBLOCKSIZE) == false)\n\t\terror = true;\n\telse\n\t{\n\t\tif (host_client->downloadcount < chunknum*DLBLOCKSIZE)\n\t\t\thost_client->downloadcount = chunknum*DLBLOCKSIZE;\n\t}\n\n\t\n\twhile (!error && chunks > 0)\n\t{\n\t\tif ((host_client->datagram.cursize + DLBLOCKSIZE+5+50 > host_client->datagram.maxsize) || (host_client->datagram.cursize + DLBLOCKSIZE+5 > 1400))\n\t\t{\n\t\t\t//would overflow the packet, or result in (ethernet) fragmentation and high packet loss.\n\t\t\tmsg = &msg_oob;\n\n\t\t\tif (!ezfilenum)\t//can't oob it\n\t\t\t\treturn;\n\n\t\t\tif (host_client->waschoked)\n\t\t\t\treturn;\t//don't let chunked downloads flood out the standard packets.\n\n\t\t\tif (!Netchan_CanPacket(&host_client->netchan, SV_RateForClient(host_client)))\n\t\t\t\treturn;\n\t\t}\n\n\t\ti = VFS_READ (host_client->download, buffer, DLBLOCKSIZE);\n\n\t\tif (i > 0)\n\t\t{\n\t\t\tif (msg == &msg_oob)\n\t\t\t{\n\t\t\t\tmsg = &msg_oob;\n\t\t\t\tmsg->cursize = 0;\n\t\t\t\tmsg->maxsize = sizeof(oobdata);\n\t\t\t\tmsg->currentbit = 0;\n\t\t\t\tmsg->packing = SZ_RAWBYTES;\n\t\t\t\tmsg->allowoverflow = 0;\n\t\t\t\tmsg->overflowed = 0;\n\t\t\t\tmsg->data = oobdata;\n\t\t\t\tMSG_WriteByte(msg, A2C_PRINT);\n\t\t\t\tSZ_Write(msg, \"\\\\chunk\", 6);\n\t\t\t\tMSG_WriteLong(msg, ezfilenum);\t//echoing the file num is used so the packets don't go out of sync.\n\t\t\t}\n\n\t\t\tif (i != DLBLOCKSIZE)\n\t\t\t\tmemset(buffer+i, 0, DLBLOCKSIZE-i);\n\n\t\t\tMSG_WriteByte(msg, svc_download);\n\t\t\tMSG_WriteLong(msg, chunknum);\n\t\t\tSZ_Write(msg, buffer, DLBLOCKSIZE);\n\n\t\t\tif (msg == &msg_oob)\n\t\t\t{\n\t\t\t\tNetchan_OutOfBand(NCF_SERVER, &host_client->netchan.remote_address, msg_oob.cursize, msg_oob.data);\n\t\t\t\tNetchan_Block(&host_client->netchan, msg_oob.cursize, SV_RateForClient(host_client));\n\t\t\t\thost_client->netchan.bytesout += msg_oob.cursize;\n\t\t\t}\n\t\t}\n\t\telse if (i < 0)\n\t\t\terror = true;\n\n\t\tchunks--;\n\t\tchunknum++;\n\t}\n\n\tif (error)\n\t{\n\t\tVFS_CLOSE (host_client->download);\n\t\thost_client->download = NULL;\n\n\t\tif (error != 2)\n\t\t{/*work around for ezquake*/\n\t\t\tClientReliableWrite_Begin (host_client, svc_download, 10+strlen(host_client->downloadfn));\n\t\t\tClientReliableWrite_Long (host_client, -1);\n\t\t\tClientReliableWrite_Long (host_client, -3);\n\t\t\tClientReliableWrite_String (host_client, host_client->downloadfn);\n\t\t}\n\n#ifdef NQPROT\n\t\thost_client->downloadstarted = false;\n#endif\n\n#if defined(HAVE_LEGACY) && defined(MVD_RECORDING)\n\t\tSV_DownloadQueueNext(host_client);\n#endif\n\t}\n}\n\n/*\n==================\nSV_NextDownload_f\n==================\n*/\nvoid SV_NextDownload_f (void)\n{\n\tqbyte\tbuffer[1024];\n\tint\t\tr;\n\tint\t\tpercent;\n\tint\t\tsize;\n\n\tif (!host_client->download)\n\t\treturn;\n\n#ifdef PEXT_CHUNKEDDOWNLOADS\n\tif (host_client->fteprotocolextensions & PEXT_CHUNKEDDOWNLOADS)\n\t{\n\t\tif (Cmd_Argc() < 2)\n\t\t\tSV_NextChunkedDownload(atoi(Cmd_Argv(1)), atoi(Cmd_Argv(2)), atoi(Cmd_Argv(3)), atoi(Cmd_Argv(4)));\n\t\telse\n\t\t\tSV_NextChunkedDownload(atoi(Cmd_Argv(1)), atoi(Cmd_Argv(2)), atoi(Cmd_Argv(3)), atoi(Cmd_Argv(4)));\n\t\treturn;\n\t}\n#endif\n\n\tr = host_client->downloadsize - host_client->downloadcount;\n\t/*\n#ifdef PEXT_ZLIBDL\n\tif (host_client->protocolextensions & PEXT_ZLIBDL)\n\t{\n\t\tif (r>1024)\t//expect a little more.\n\t\t\tr=1024;\n\t}\n\telse\n#endif\n\t\t*/\n\tif (r > 768)\n\t\tr = 768;\n\tr = VFS_READ (host_client->download, buffer, r);\n\tClientReliableWrite_Begin (host_client, ISQ2CLIENT(host_client)?svcq2_download:svc_download, 6+r);\n\tClientReliableWrite_Short (host_client, r);\n\n\thost_client->downloadcount += r;\n\tsize = host_client->downloadsize;\n\n\tif (host_client->downloadcount < size)\n\t{\n\t\tif (!size)\n\t\t\tsize = 1;\n\n\t\tpercent = (double)host_client->downloadcount*100.0/size;\n\t\tpercent = bound(0, percent, 99);\n\t}\n\telse\n\t\tpercent = 100;\n\n#ifdef PEXT_ZLIBDL\n\tif (host_client->fteprotocolextensions & PEXT_ZLIBDL)\n\t{\n\t\tClientReliableWrite_Byte (host_client, percent+101);\n\t\tClientReliableWrite_ZLib (host_client, buffer, r);\n\t}\n\telse\n#endif\n\t{\n\t\tClientReliableWrite_Byte (host_client, percent);\n\t\tClientReliableWrite_SZ (host_client, buffer, r);\n\t}\n\n\tif (host_client->downloadcount < host_client->downloadsize)\n\t\treturn;\n\n\tVFS_CLOSE (host_client->download);\n\thost_client->download = NULL;\n\n#if defined(HAVE_LEGACY) && defined(MVD_RECORDING)\n\tSV_DownloadQueueNext(host_client);\n#endif\n}\n\nvoid VARGS OutofBandPrintf(netadr_t *where, char *fmt, ...)\n{\n\tva_list\t\targptr;\n\tchar\tsend[1024];\n\n\tsend[0] = 0xff;\n\tsend[1] = 0xff;\n\tsend[2] = 0xff;\n\tsend[3] = 0xff;\n\tsend[4] = A2C_PRINT;\n\tva_start (argptr, fmt);\n\tvsnprintf (send+5, sizeof(send)-5, fmt, argptr);\n\tva_end (argptr);\n\n\tNET_SendPacket (svs.sockets, strlen(send)+1, send, where);\n}\n\n/*\n==================\nSV_NextUpload\n==================\n*/\nvoid SV_NextUpload (void)\n{\n\tint\t\tpercent;\n\tint\t\tsize;\n\n\tif (!*host_client->uploadfn)\n\t{\n\t\tSV_ClientTPrintf(host_client, PRINT_HIGH, \"Upload denied\\n\");\n\t\tClientReliableWrite_Begin (host_client, svc_stufftext, 8);\n\t\tClientReliableWrite_String (host_client, \"stopul\\n\");\n\n\t\t// suck out rest of packet\n\t\tsize = MSG_ReadShort ();\tMSG_ReadByte ();\n\t\tMSG_ReadSkip(size);\n\t\treturn;\n\t}\n\n\tsize = MSG_ReadShort ();\n\tpercent = MSG_ReadByte ();\n\n\tif (!host_client->upload)\n\t{\n\t\tFS_CreatePath(host_client->uploadfn, FS_GAMEONLY);\n\t\thost_client->upload = FS_OpenVFS(host_client->uploadfn, \"wb\", FS_GAMEONLY);\n\t\tif (!host_client->upload)\n\t\t{\n\t\t\tSys_Printf(\"Can't create %s\\n\", host_client->uploadfn);\n\t\t\tClientReliableWrite_Begin (host_client, svc_stufftext, 8);\n\t\t\tClientReliableWrite_String (host_client, \"stopul\\n\");\n\t\t\t*host_client->uploadfn = 0;\n\t\t\treturn;\n\t\t}\n\t\tCon_Printf(\"Receiving %s from %d...\\n\", host_client->uploadfn, host_client->userid);\n\t\tif (host_client->remote_snap)\n\t\t\tOutofBandPrintf(&host_client->snap_from, \"Server receiving %s from %d...\\n\", host_client->uploadfn, host_client->userid);\n\t}\n\n\tVFS_WRITE (host_client->upload, net_message.data + MSG_GetReadCount(), size);\n\tMSG_ReadSkip(size);\n\n\tif (percent != 100)\n\t{\n\t\tClientReliableWrite_Begin (host_client, svc_stufftext, 8);\n\t\tClientReliableWrite_String (host_client, \"nextul\\n\");\n\t}\n\telse\n\t{\n\t\tVFS_CLOSE (host_client->upload);\n\t\thost_client->upload = NULL;\n\n\t\tCon_Printf(\"%s upload completed.\\n\", host_client->uploadfn);\n\n\t\tif (host_client->remote_snap)\n\t\t{\n\t\t\tchar *p;\n\n\t\t\tif ((p = strchr(host_client->uploadfn, '/')) != NULL)\n\t\t\t\tp++;\n\t\t\telse\n\t\t\t\tp = host_client->uploadfn;\n\t\t\tOutofBandPrintf(&host_client->snap_from, \"%s upload completed.\\nTo download, enter:\\ndownload %s\\n\",\n\t\t\t\thost_client->uploadfn, p);\n\t\t}\n\t\t*host_client->uploadfn = 0;\t//don't let it get overwritten again\n\t}\n}\n\n#ifdef VOICECHAT\n/*\nPivicy issues:\nBy sending voice chat to a server, you are unsure who might be listening.\nVoice can be recorded to an mvd, potentially including voice.\nSpectators tracvking you are able to hear team chat of your team.\nYou're never quite sure if anyone might join the server and your team before you finish saying a sentance.\nYou run the risk of sounds around you being recorded by quake, including but not limited to: TV channels, loved ones, phones, YouTube videos featuring certain moans.\nDefault on non-team games is to broadcast.\n*/\n\n#define VOICE_RING_SIZE 512 /*POT*/\nstruct\n{\n\tstruct voice_ring_s\n\t{\n\t\t\tunsigned int sender;\n\t\t\tunsigned char receiver[(MAX_CLIENTS+7)/8];\n\t\t\tunsigned char gen;\n\t\t\tunsigned char seq;\n\t\t\tunsigned int datalen;\n\t\t\tunsigned char data[1024];\n\t} ring[VOICE_RING_SIZE];\n\tunsigned int write;\n} voice;\nvoid SV_VoiceReadPacket(void)\n{\n\tunsigned int vt = host_client->voice_target;\n\tunsigned int j;\n\tstruct voice_ring_s *ring;\n\tunsigned short bytes;\n\tclient_t *cl;\n\tunsigned char gen = MSG_ReadByte();\n\tunsigned char seq = MSG_ReadByte();\n\t/*read the data from the client*/\n\tbytes = MSG_ReadShort();\n\tring = &voice.ring[voice.write & (VOICE_RING_SIZE-1)];\n\t//voice data does not get echoed to the sender unless sv_voip_echo is on too, which is rarely the case, so no worries about leaking the mute+deaf talking-to-yourself thing\n\tif (bytes > sizeof(ring->data) || (host_client->penalties & (BAN_MUTE|BAN_VMUTE)) || !sv_voip.ival)\n\t{\n\t\tMSG_ReadSkip(bytes);\n\t\treturn;\n\t}\n\telse\n\t{\n\t\tvoice.write++;\n\t\tMSG_ReadData(ring->data, bytes);\n\t}\n\n\tring->datalen = bytes;\n\tring->sender = host_client - svs.clients;\n\tring->gen = gen;\n\tring->seq = seq;\n\n\t/*broadcast it its to their team, and its not teamplay*/\n\tif (vt == VT_TEAM && (!teamplay.ival || coop.ival))\n\t\tvt = VT_ALL;\n\n\t/*figure out which team members are meant to receive it*/\n\tfor (j = 0; j < (MAX_CLIENTS+7)/8; j++)\n\t\tring->receiver[j] = 0;\n\tfor (j = 0, cl = svs.clients; j < svs.allocated_client_slots; j++, cl++)\n\t{\n\t\tif (cl->state != cs_spawned && cl->state != cs_connected)\n\t\t\tcontinue;\n\t\t/*spectators may only talk to spectators*/\n\t\tif (host_client->spectator && !sv_spectalk.ival)\n\t\t\tif (!cl->spectator)\n\t\t\t\tcontinue;\n\n\t\tif (cl->penalties & BAN_DEAF)\n\t\t\tcontinue;\n\n\t\tif (vt == VT_TEAM)\n\t\t{\n\t\t\t// the spectator team\n\t\t\tif (host_client->spectator)\n\t\t\t{\n\t\t\t\tif (!cl->spectator)\n\t\t\t\t\tcontinue;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (strcmp(cl->team, host_client->team) || cl->spectator)\n\t\t\t\t\tcontinue;\t// on different teams\n\t\t\t}\n\t\t}\n\t\telse if (vt == VT_NONMUTED)\n\t\t{\n\t\t\tif (host_client->voice_mute[j>>3] & (1<<(j&3)))\n\t\t\t\tcontinue;\n\t\t}\n\t\telse if (vt == VT_SPECSELF)\n\t\t{\n\t\t\t//spectators spectating self hear it.\n\t\t\t//self hears it (for demos, voip_echo stops it from actually being echoed)\n\t\t\tif (!host_client->spectator || host_client->spec_track != host_client-svs.clients)\n\t\t\t\tif (j != host_client-svs.clients)\n\t\t\t\t\tcontinue;\n\t\t}\n\t\telse if (vt >= VT_PLAYERSLOT0)\n\t\t{\n\t\t\tif (j != vt - VT_PLAYERSLOT0)\n\t\t\t\tcontinue;\n\t\t}\n\n\t\tring->receiver[j>>3] |= 1<<(j&3);\n\t}\n\n#ifdef MVD_RECORDING\n\tif (sv.mvdrecording && sv_voip_record.ival && !(sv_voip_record.ival == 2 && !host_client->spectator))\n\t{\n\t\tsizebuf_t *msg;\n\t\t// non-team messages should be seen always, even if not tracking any player\n\t\tif (vt == VT_ALL && (!host_client->spectator || sv_spectalk.ival))\n\t\t{\n\t\t\tmsg = MVDWrite_Begin (dem_all, 0, ring->datalen+6);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tunsigned int cls;\n\t\t\tcls = ring->receiver[0] |\n\t\t\t\t(ring->receiver[1]<<8) |\n\t\t\t\t(ring->receiver[2]<<16) |\n\t\t\t\t(ring->receiver[3]<<24);\n\t\t\t//FIXME: 32 maxplayers limit.\n\t\t\tmsg = MVDWrite_Begin (dem_multiple, cls, ring->datalen+6);\n\t\t}\n\n\t\tMSG_WriteByte(msg, svcfte_voicechat);\n\t\tMSG_WriteByte(msg, ring->sender);\n\t\tMSG_WriteByte(msg, ring->gen);\n\t\tMSG_WriteByte(msg, ring->seq);\n\t\tMSG_WriteShort(msg, ring->datalen);\n\t\tSZ_Write(msg, ring->data, ring->datalen);\n\t}\n#endif\n}\nvoid SV_VoiceInitClient(client_t *client)\n{\n\tclient->voice_target = VT_TEAM;\n\tclient->voice_active = false;\n\tclient->voice_read = voice.write;\n\tmemset(client->voice_mute, 0, sizeof(client->voice_mute));\n}\nvoid SV_VoiceSendPacket(client_t *client, sizebuf_t *buf)\n{\n\tunsigned int clno;\n\tqboolean send;\n\tstruct voice_ring_s *ring;\n\n\tif (client->controller)\n\t\tclient = client->controller;\n\tclno = client - svs.clients;\n\n\tif (!(client->fteprotocolextensions2 & PEXT2_VOICECHAT))\n\t\treturn;\n\tif (!client->voice_active || client->num_backbuf)\n\t{\n\t\tclient->voice_read = voice.write;\n\t\treturn;\n\t}\n\n\twhile(client->voice_read < voice.write)\n\t{\n\t\t/*they might be too far behind*/\n\t\tif (client->voice_read+VOICE_RING_SIZE < voice.write)\n\t\t\tclient->voice_read = voice.write - VOICE_RING_SIZE;\n\n\t\tring = &voice.ring[(client->voice_read) & (VOICE_RING_SIZE-1)];\n\n\t\t/*figure out if it was for us*/\n\t\tsend = false;\n\t\tif (ring->receiver[clno>>3] & (1<<(clno&3)))\n\t\t\tsend = true;\n\n\t\t/*if you're spectating, you can hear whatever your tracked player can hear*/\n\t\tif (host_client->spectator && host_client->spec_track && host_client->spec_track <= sv.allocated_client_slots)\n\t\t\tif (ring->receiver[(host_client->spec_track-1)>>3] & (1<<((host_client->spec_track-1)&3)))\n\t\t\t\tsend = true;\n\n\n\t\tif (client->voice_mute[ring->sender>>3] & (1<<(ring->sender&3)))\n\t\t\tsend = false;\n\n\t\tif (ring->sender == clno && !sv_voip_echo.ival)\n\t\t\tsend = false;\n\n\t\t/*additional ways to block voice*/\n\t\tif (client->download)\n\t\t\tsend = false;\n\n\t\tif (send)\n\t\t{\n\t\t\tif (buf->maxsize - buf->cursize < ring->datalen+5)\n\t\t\t\tbreak;\n\t\t\tMSG_WriteByte(buf, svcfte_voicechat);\n\t\t\tMSG_WriteByte(buf, ring->sender);\n\t\t\tMSG_WriteByte(buf, ring->gen);\n\t\t\tMSG_WriteByte(buf, ring->seq);\n\t\t\tMSG_WriteShort(buf, ring->datalen);\n\t\t\tSZ_Write(buf, ring->data, ring->datalen);\n\t\t}\n\t\tclient->voice_read++;\n\t}\n}\n\nvoid SV_Voice_Ignore_f(void)\n{\n\tunsigned int other;\n\tint type = 0;\n\n\tif (Cmd_Argc() < 2)\n\t{\n\t\t/*only a name = toggle*/\n\t\ttype = 0;\n\t}\n\telse\n\t{\n\t\t/*mute if 1, unmute if 0*/\n\t\tif (atoi(Cmd_Argv(2)))\n\t\t\ttype = 1;\n\t\telse\n\t\t\ttype = -1;\n\t}\n\tother = atoi(Cmd_Argv(1));\n\tif (other >= svs.allocated_client_slots)\n\t\treturn;\n\n\tswitch(type)\n\t{\n\tcase -1:\n\t\thost_client->voice_mute[other>>3] &= ~(1<<(other&3));\n\t\tbreak;\n\tcase 0:\n\t\thost_client->voice_mute[other>>3] ^= (1<<(other&3));\n\t\tbreak;\n\tcase 1:\n\t\thost_client->voice_mute[other>>3] |= (1<<(other&3));\n\t}\n}\nvoid SV_Voice_Target_f(void)\n{\n\tunsigned int other;\n\tchar *t = Cmd_Argv(1);\n\tchar *v = Cmd_Argv(2);\n\tqboolean verbose = *v?atoi(v):host_client->voice_active;\n\tif (!strcmp(t, \"team\"))\n\t{\n\t\thost_client->voice_target = VT_TEAM;\n\t\tif (verbose)\n\t\t{\n\t\t\tif (teamplay.ival)\n\t\t\t\tSV_ClientTPrintf(host_client, PRINT_HIGH, \"Now sending voice to team\\n\");\n\t\t\telse\n\t\t\t\tSV_ClientTPrintf(host_client, PRINT_HIGH, \"Now sending voice to all (no teamplay)\\n\");\n\t\t}\n\t}\n\telse if (!strcmp(t, \"all\"))\n\t{\n\t\thost_client->voice_target = VT_ALL;\n\t\tif (verbose)\n\t\t\tSV_ClientTPrintf(host_client, PRINT_HIGH, \"Now sending voice to all\\n\");\n\t}\n\telse if (!strcmp(t, \"specself\"))\n\t{\n\t\thost_client->voice_target = VT_SPECSELF;\n\t\tif (verbose)\n\t\t\tSV_ClientTPrintf(host_client, PRINT_HIGH, \"Now sending voice to your personal admirers\\n\");\n\t}\n\telse if (!strcmp(t, \"nonmuted\"))\n\t{\n\t\thost_client->voice_target = VT_NONMUTED;\n\t\tif (verbose)\n\t\t\tSV_ClientTPrintf(host_client, PRINT_HIGH, \"Now sending voice to all people you've not ignored\\n\");\n\t}\n\telse if (*t >= '0' && *t <= '9')\n\t{\n\t\tother = atoi(t);\n\t\tif (other >= svs.allocated_client_slots)\n\t\t{\n\t\t\tif (verbose)\n\t\t\t\tSV_ClientTPrintf(host_client, PRINT_HIGH, \"Invalid client\\n\");\n\t\t\treturn;\n\t\t}\n\t\thost_client->voice_target = VT_PLAYERSLOT0 + other;\n\t\tif (verbose)\n\t\t{\n\t\t\tif (host_client->state >= cs_connected)\n\t\t\t\tSV_ClientTPrintf(host_client, PRINT_HIGH, \"Now sending voice only to %s\\n\", host_client->name);\n\t\t\telse\n\t\t\t\tSV_ClientTPrintf(host_client, PRINT_HIGH, \"Now sending voice only to player slot %i, if someone occupies it\\n\", other);\n\t\t}\n\t}\n\telse\n\t{\n\t\t/*don't know who you mean, futureproofing*/\n\t\thost_client->voice_target = VT_TEAM;\n\t\tif (verbose)\n\t\t\tSV_ClientTPrintf(host_client, PRINT_HIGH, \"Now sending voice to team\\n\");\n\t}\n}\nvoid SV_Voice_MuteAll_f(void)\n{\n\thost_client->voice_active = false;\n}\nvoid SV_Voice_UnmuteAll_f(void)\n{\n\thost_client->voice_active = true;\n}\n#else\nvoid SV_Voice_UnmuteAll_f(void)\n{\t//no-op\n}\n#endif\n\nint SV_FindRemotePackage(const char *package, char *url, size_t urlsize)\n{\n\t//basedir/gamedir/curl_urls.txt contains something like:\n\t//maps_*.pk3 https://host/maps/\n\t//* https://host/gamedir/\n\n\t//or something.\n\n\tvfsfile_t *f;\n\tchar line[512];\n\n\t//filter out the gamedir, so different gamedirs can have different sets of urls.\n\t//(useful for eg pakN.pak, if it were not blocked elsewhere)\n\tchar *sep = strchr(package, '/');\n\tif (sep && sep-package<sizeof(line))\n\t{\n\t\tsep++;\n\t\tmemcpy(line, package, sep-package);\n\t\tline[sep-package] = 0;\n\t\t//line is now 'gamedir/', sep is the gamedirless package\n\n\t\t/*compat with xonotic*/\n\t\tQ_strncatz(line, \"curl_urls.txt\", sizeof(line));\n\t\tf = FS_OpenVFS(line, \"rb\", FS_ROOT);\t//this is for server admins to deal with. urls are too unreliable for paks.\n\t\tif (f)\n\t\t{\n\t\t\tchar pattern[256];\n\t\t\tchar *e;\n\t\t\twhile (VFS_GETS(f, line, sizeof(line)))\n\t\t\t{\n\t\t\t\te = COM_ParseOut(line, pattern, sizeof(pattern));\n\t\t\t\tif (*pattern && wildcmp(pattern, sep))\n\t\t\t\t{\n\t\t\t\t\tCOM_ParseOut(e, url, urlsize);\n\t\t\t\t\tQ_strncatz(url, sep, urlsize);\n\t\t\t\t\tVFS_CLOSE(f);\n\t\t\t\t\treturn DLERR_SV_REDIRECTPACK;\n\t\t\t\t}\n\t\t\t}\n\t\t\tVFS_CLOSE(f);\n\t\t}\n\t}\n\n\t//curl stuff is explicit so goes first.\n\t//NOW try the package manager. probably it'll just redirect to the dlcache dir.\n//\tif (PM_HandleRedirect(package, url, urlsize))\n//\t\treturn DLERR_SV_PACKAGE;\n\n\tif (*fs_dlURL.string)\n\t{\t//a fallback, though the above mechanism allows for a wildcard for all.\n\t\tQ_strncatz(fs_dlURL.string, package, urlsize);\n\t\tQ_strncatz(url, package, urlsize);\n\t\treturn DLERR_SV_REDIRECTPACK;\n\t}\n\treturn DLERR_FILENOTFOUND;\n}\n\n//Use of this function is on name only.\n//Be aware that the maps directory should be restricted based on weather the file was from a pack file\n//this is to preserve copyright - please do not breach due to a bug.\nqboolean SV_AllowDownload (const char *name)\n{\n\textern\tcvar_t\tallow_download;\n\textern\tcvar_t\tallow_download_skins;\n\textern\tcvar_t\tallow_download_models;\n\textern\tcvar_t\tallow_download_sounds;\n\textern\tcvar_t\tallow_download_particles;\n\textern\tcvar_t\tallow_download_demos;\n\textern\tcvar_t\tallow_download_maps;\n\textern\tcvar_t\tallow_download_textures;\n\textern\tcvar_t\tallow_download_packages;\n\textern\tcvar_t\tallow_download_wads;\n\textern\tcvar_t\tallow_download_root;\n\textern\tcvar_t\tallow_download_logs;\n\textern\tcvar_t\tallow_download_configs;\n\textern\tcvar_t\tallow_download_locs;\n\textern\tcvar_t\tallow_download_copyrighted;\n\textern\tcvar_t\tallow_download_other;\n\tchar cleanname[MAX_QPATH];\n\tchar ext[8];\n\tint i=0;\n\tif (strlen(name) >= MAX_QPATH)\n\t\treturn false;\n\tdo\n\t{\n\t\tcleanname[i++] = *name;\n\t} while(*name++);\n\tname = cleanname;\n\n\t//allowed at all?\n\tif (!allow_download.ival)\n\t\treturn false;\n\n\t//no subdirs?\n\tif (strstr (name, \"..\"))\t//no under paths.\n\t\treturn false;\n\tif (*name == '.')\t//relative is pointless\n\t\treturn false;\n\tif (*name == '/')\t//no absolute.\n\t\treturn false;\n\tif (strchr(name, ':'))\t//no drives, alternative resources, etc. the filesystem should refuse such a file so this should just be paranoia.\n\t\treturn false;\n\tif (strchr(name, '\\\\'))\t//no windows paths - grow up you lame windows users.\n\t\treturn false;\n\n\tCOM_FileExtension(name, ext, sizeof(ext));\n\n\t//block attempts to download logs\n\tif (!Q_strcasecmp(\"log\", ext))\n\t\treturn !!allow_download_logs.value;\n\tif (Q_strncasecmp(name,\t\"logs/\", 5) == 0)\n\t\treturn !!allow_download_logs.value;\n\n\tif (!Q_strncasecmp(name, \"package/\", 8))\n\t{\n\t\t//eg: package/id1/foobar.pk3\n\t\tif (!Q_strcasecmp(\"pk4\", ext) || !Q_strcasecmp(\"pk3\", ext) || !Q_strcasecmp(\"pak\", ext) || (!Q_strncasecmp(name, \"package/downloads/\", 18) && !Q_strcasecmp(\"zip\", ext)))\n\t\t{\n\t\t\tif (!allow_download_packages.ival)\n\t\t\t\treturn false;\n\t\t\t/*do not permit 'id1/pak1.pak' or 'baseq3/pak0.pk3' or any similarly named packages. such packages would violate copyright, and must be obtained through other means (like buying the damn game)*/\n\t\t\tif (!allow_download_copyrighted.ival && !FS_GetPackageDownloadable(name+8))\n\t\t\t\treturn false;\n\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\tif ((Q_strncasecmp(name,\t\"maps/\", 5) == 0) ||\n\t\t(Q_strncasecmp(name,\t\"levelshots/\", 11) == 0) ||\n\t\t(Q_strncasecmp(name,\t\"overviews/\", 10) == 0))\n\t\treturn !!allow_download_maps.value;\n\n\t//skins?\n\tif (Q_strncasecmp(name,\t\"skins/\", 6) == 0)\n\t\treturn !!allow_download_skins.value;\n\t//models\n\tif ((Q_strncasecmp(name,\t\"progs/\", 6) == 0) ||\n\t\t(Q_strncasecmp(name,\t\"models/\", 7) == 0) ||\n\t\t(Q_strncasecmp(name,\t\"sprites/\", 8) == 0))\n\t\treturn !!allow_download_models.value;\n\t//sound\n\tif (Q_strncasecmp(name,\t\"sound/\", 6) == 0)\n\t\treturn !!allow_download_sounds.value;\n\t//particles\n\tif (Q_strncasecmp(name,\t\"particles/\", 10) == 0)\n\t\treturn !!allow_download_particles.value;\n\t//demos\n\tif (Q_strncasecmp(name,\t\"demos/\", 6) == 0)\n\t\treturn !!allow_download_demos.value;\n\n\t//textures\n\tif (Q_strncasecmp(name,\t\"textures/\", 9) == 0)\n\t\treturn !!allow_download_textures.value;\n\n\tif (Q_strncasecmp(name,\t\"locs/\", 5) == 0)\n\t\treturn !!allow_download_locs.value;\n\n\t//wads\n\tif (Q_strncasecmp(name,\t\"wads/\", 5) == 0)\n\t\treturn !!allow_download_wads.value;\n\tif (!strchr(name, '/') && !Q_strcasecmp(\"wad\", ext))\n\t\treturn !!allow_download_wads.value;\n\n\t//configs\n\tif (Q_strncasecmp(name,\t\"config/\", 7) == 0)\n\t\treturn !!allow_download_configs.value;\n\tif (!Q_strcasecmp(\"cfg\", ext))\n\t\treturn !!allow_download_configs.value;\n\n\t//pak/pk3s.\n\tif (!strchr(name, '/') && (!Q_strcasecmp(\"pk4\", ext) || !Q_strcasecmp(\"pk3\", ext) || !Q_strcasecmp(\"pak\", ext)))\n\t{\n\t\tif (Q_strncasecmp(name, \"pak\", 3))\t//don't give out core pak/pk3 files. This matches q3 logic.\n\t\t\treturn !!allow_download_packages.value;\n\t\telse\n\t\t\treturn !!allow_download_packages.value && !!allow_download_copyrighted.value;\n\t}\n\n\t//root of gamedir\n\tif (!strchr(name, '/') && !allow_download_root.value)\n\t{\n\t\tif (!strcmp(name, \"csprogs.dat\"))\t//we always allow csprogs.dat to be downloaded (if downloads are permitted).\n\t\t\treturn true;\n\t\treturn false;\n\t}\n\n\t//any other subdirs are allowed\n\treturn !!allow_download_other.value;\n}\n\nstatic int SV_LocateDownload(const char *name, flocation_t *loc, char **replacementname, qboolean redirectpaks)\n{\n\textern\tcvar_t\tallow_download_anymap, allow_download_pakcontents, allow_download_copyrighted, allow_download_packages;\n\tqboolean copyprotected;\n\tqboolean found;\n\tstatic char tmpname[MAX_QPATH];\n\n//Con_Printf(\"%s request for %s\\n\", redirectpaks?\"dlsize\":\"download\", name);\n\n\tif (replacementname)\n\t\t*replacementname = NULL;\n\n#ifdef MVD_RECORDING\n\t//mvdsv demo downloading support demonum/ -> demos/XXXX (sets up the client paths)\n\tif (!Q_strncasecmp(name, \"demonum/\", 8))\n\t{\n\t\tif (replacementname)\n\t\t{\n\t\t\tchar mvdnamebuffer[MAX_QPATH];\n\t\t\tchar *mvdname = SV_MVDNum(mvdnamebuffer, sizeof(mvdnamebuffer), atoi(name+8));\n\t\t\tif (!mvdname)\n\t\t\t{\n\t\t\t\tSV_ClientPrintf (host_client, PRINT_HIGH, \"%s is an invalid MVD demonum.\\n\", name+8);\n\t\t\t\tSys_Printf (\"%s requested invalid demonum %s\\n\", host_client->name, name+8);\n\t\t\t\treturn -1;\t//not found\n\t\t\t}\n\t\t\tname = *replacementname = va(\"demos/%s\", mvdname);\n\t\t\treturn DLERR_REDIRECTFILE;\n\t\t}\n\t}\n#endif\n\n\tif (!SV_AllowDownload(name))\n\t{\n\t\tSys_Printf (\"%s denied download of %s due to path/name rules\\n\", host_client->name, name);\n\t\treturn DLERR_PERMISSIONS;\t//not permitted (even if it exists).\n\t}\n\n#ifdef MVD_RECORDING\n\t//mvdsv demo downloading support. demos/ -> demodir (sets up the server paths)\n\tif (!Q_strncasecmp(name, \"demos/\", 6) && *sv_demoDir.string)\n\t{\n\t\tQ_snprintfz(tmpname, sizeof(tmpname), \"%s/%s\", sv_demoDir.string, name+6);\t\t\n\t\tfound = FS_FLocateFile(name, FSLF_IFFOUND, loc);\n\n\t\tif (!found && *sv_demoDirAlt.string)\n\t\t{\n\t\t\tQ_snprintfz(tmpname, sizeof(tmpname), \"%s/%s\", sv_demoDirAlt.string, name+6);\n\t\t\tfound = FS_FLocateFile(name, FSLF_IFFOUND, loc);\n\t\t}\n\t\tif (found)\n\t\t\tname = tmpname;\n\t}\n\telse\n#endif\n\n\tif (!Q_strncasecmp(name, \"package/\", 8))\n\t{\n\t\tvfsfile_t *f = NULL;\n\t\tsearchpath_t *p;\n\t\tif (redirectpaks)\n\t\t{\n\t\t\tint redir = SV_FindRemotePackage(name+8, tmpname, sizeof(tmpname));\n//\t\t\tcan't handle http here. caller won't know what we mean\n\t\t\tif (redir == DLERR_SV_PACKAGE)\n\t\t\t{\nCon_Printf(\"Complex Redirecting %s to %s\\n\", name, tmpname);\n\t\t\t\tQ_strncpyz(loc->rawname, tmpname, sizeof(loc->rawname));\n\t\t\t\treturn DLERR_SV_PACKAGE;\n\t\t\t}\n\t\t\telse if (redir == DLERR_SV_REDIRECTPACK)\n\t\t\t{\t//probably an http(s) path\nCon_Printf(\"Simple Redirecting %s to %s\\n\", name, tmpname);\n\t\t\t\t*replacementname = tmpname;\n\t\t\t\treturn DLERR_SV_REDIRECTPACK;\n\t\t\t}\n\t\t}\n\n\t\tp = FS_GetPackage(name+8);\n\t\tif (p && !*p->prefix)\n\t\t{\n\t\t\tif (FS_GenCachedPakName(name+8, va(\"%i\", p->crc_check), loc->rawname, sizeof(loc->rawname)))\n\t\t\t{\n\t\t\t\tf = FS_OpenVFS(loc->rawname, \"rb\", FS_ROOT);\t//it was cached\n\t\t\t\tif (!f)\n\t\t\t\t{\n\t\t\t\t\tQ_strncpyz(loc->rawname, name+8, sizeof(loc->rawname));\n\t\t\t\t\tf = FS_OpenVFS(loc->rawname, \"rb\", FS_ROOT);\t//not cached go direct.\n\t\t\t\t}\n\t\t\t}\n\t\t\t//else some evil path we're best off avoiding\n\t\t}\n\t\telse\n\t\t\tf = NULL;\t//don't let em grab paks we're not using ourselves.\n\t\tif (f)\n\t\t{\n\t\t\tloc->len = VFS_GETLEN(f);\n\t\t\tVFS_CLOSE(f);\n\t\t\treturn DLERR_SV_PACKAGE;\t//found package\n\t\t}\n\t\telse\n\t\t\treturn DLERR_FILENOTFOUND;\t//not found/unable to open\n\t}\n#ifdef TERRAIN\n\telse if (Terrain_LocateSection(name, loc))\n\t{\n\t\tfound = true;\n\t}\n#endif\n\telse\n\t\tfound = FS_FLocateFile(name, FSLF_IFFOUND, loc);\n\n\tif (!found && replacementname)\n\t{\n\t\tsize_t alt;\n\t\tstatic const char *alternatives[][4] = {\n\t\t\t//orig-path, new-path, orig-ext, new-ext\n\t\t\t//nexuiz qc names [sound/]sound/foo.wav but expects sound/foo.ogg and variations of that (the [sound/] is implied, but ignored)\n\t\t\t{\"\",\t\t\"\", \".wav\", \".ogg\"},\t//nexuiz qc names .wav, but the paks use .ogg\n\t\t\t{\"sound/\", \"\",\t\".wav\",\t\".wav\"},\t//nexuiz qc names sound/ but that's normally implied, resulting in doubles that don't exist in the filesystem\n\t\t\t{\"sound/\", \"\",\t\".wav\",\t\".ogg\"}, \t//both of nexuiz's issues at the same time\n\n\t\t\t//we request wads from textures/*.wad but they could also be simply *.wad\n\t\t\t{\"textures/\",\"\",\".wad\",\t\".wad\"}\n\t\t};\n\n\t\tfor (alt = 0; alt < countof(alternatives); alt++)\n\t\t{\n\t\t\tchar tryalt[MAX_QPATH];\n\t\t\tchar ext[8];\n\t\t\tif (!Q_strncasecmp(name, alternatives[alt][0], strlen(alternatives[alt][0])))\n\t\t\t{\n\t\t\t\tif (*alternatives[alt][2])\n\t\t\t\t{\n\t\t\t\t\tif (Q_strcasecmp(COM_FileExtension(name, ext, sizeof(ext)), alternatives[alt][2]+1))\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\tmemcpy(tryalt, alternatives[alt][1], strlen(alternatives[alt][1]));\n\t\t\t\t\tCOM_StripExtension(name+strlen(alternatives[alt][0]), tryalt+strlen(alternatives[alt][1]), sizeof(tryalt)-strlen(alternatives[alt][1]));\n\t\t\t\t\tCOM_DefaultExtension(tryalt, alternatives[alt][3], sizeof(tryalt));\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tmemcpy(tryalt, alternatives[alt][1], strlen(alternatives[alt][1]));\n\t\t\t\t\tQ_strncpyz(tryalt+strlen(alternatives[alt][1]), name+strlen(alternatives[alt][0]), sizeof(tryalt)-strlen(alternatives[alt][1]));\n\t\t\t\t}\n\t\t\t\tfound = FS_FLocateFile(tryalt, FSLF_IFFOUND, loc);\n\t\t\t\tif (found)\n\t\t\t\t{\n\t\t\t\t\tQ_snprintfz(tmpname, sizeof(tmpname), \"%s\", tryalt);\n\t\t\t\t\tname = *replacementname = tmpname;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tif (found)\n\t{\n\t\tconst char *pakname = FS_GetPackageDownloadFilename(loc);\n\t\tqboolean ispak = loc->search && !(loc->search->flags & SPF_ISDIR);\n\t\tcopyprotected = loc->search && (loc->search->flags & SPF_COPYPROTECTED);\n\n\t\tif (replacementname)\n\t\t{\t//if we're able to redirect it then do so.\n\t\t\tif (ispak && pakname && strchr(pakname, '/'))\n\t\t\t{\n\t\t\t\tif (allow_download_packages.ival && !(loc->search && (loc->search->flags&SPF_TEMPORARY)))\n\t\t\t\t{\n\t\t\t\t\tif (allow_download_copyrighted.ival || !copyprotected)\n\t\t\t\t\t{\t//this path is non authoritive, but we shouldn't be pushing people to download files they're not going to be allowed to download.\n\t\t\t\t\t\tQ_snprintfz(tmpname, sizeof(tmpname), \"package/%s\", pakname);\n\t\t\t\t\t\t*replacementname = tmpname;\n\t\t\t\t\t\treturn DLERR_SV_REDIRECTPACK;\t//redirect\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (ispak)\n\t\t{\n\t\t\tint pakcontents = (!Q_strncasecmp(name, \"maps/\", 5)?allow_download_anymap.ival:allow_download_pakcontents.ival);\n\t\t\tif (pakcontents == 2)\n\t\t\t{\t//qw-like allow-and-ignore-copyrights, skip the following cases...\n\t\t\t}\n\t\t\telse if (copyprotected)\n\t\t\t{\n\t\t\t\tSys_Printf (\"%s denied download of %s - it is in a copyrighted pak\\n\", host_client->name, name);\n\t\t\t\treturn DLERR_PERMISSIONS;\n\t\t\t}\n\t\t\telse if (!pakcontents)\n\t\t\t{\t//block it if its entirely disabllowed\n\t\t\t\tSys_Printf (\"%s denied download of %s - it is in a pak\\n\", host_client->name, name);\n\t\t\t\treturn DLERR_PERMISSIONS;\n\t\t\t}\n\t\t}\n\t\telse if (copyprotected)\n\t\t{\t//not really sure how we can get here, but oh well.\n\t\t\tSys_Printf (\"%s denied download of %s - it is copyrighted\\n\", host_client->name, name);\n\t\t\treturn DLERR_PERMISSIONS;\n\t\t}\n\n\t\tif (replacementname && *replacementname)\n\t\t\treturn DLERR_SV_REDIRECTPACK;\n\t\treturn 0;\n\t}\n\treturn -1;\t//not found\n}\n\n//this function is only meaningful for nq/qw\nvoid SV_DownloadSize_f(void)\n{\n\tflocation_t loc;\n\tchar *name = Cmd_Argv(1);\n\tchar *redirected = \"\";\n\n\tswitch(SV_LocateDownload(name, &loc, &redirected, true))\n\t{\n\tcase DLERR_SV_REDIRECTPACK: /*redirect (the containing package)*/\n\n\tcase DLERR_REDIRECTFILE: /*redirect (extension difference or so)*/\n\t\tname = va(\"dlsize \\\"%s\\\" r \\\"%s\\\"\\n\", name, redirected);\n\t\tClientReliableWrite_Begin (host_client, svc_stufftext, 2+strlen(name));\n\t\tClientReliableWrite_String (host_client, name);\n\t\tbreak;\n\tdefault:\n\tcase DLERR_FILENOTFOUND: /*not found*/\n\t\tname = va(\"dlsize \\\"%s\\\" e\\n\", name);\n\t\tClientReliableWrite_Begin (host_client, svc_stufftext, 2+strlen(name));\n\t\tClientReliableWrite_String (host_client, name);\n\t\tbreak;\n\tcase DLERR_PERMISSIONS: /*permission*/\n\t\tname = va(\"dlsize \\\"%s\\\" p\\n\", name);\n\t\tClientReliableWrite_Begin (host_client, svc_stufftext, 2+strlen(name));\n\t\tClientReliableWrite_String (host_client, name);\n\t\tbreak;\n\tcase DLERR_SV_PACKAGE: /*requested file is a package*/\n\tcase 0: /*file exists*/\n\t\tname = va(\"dlsize \\\"%s\\\" %u\\n\", name, (unsigned int)loc.len);\n\t\tClientReliableWrite_Begin (host_client, svc_stufftext, 2+strlen(name));\n\t\tClientReliableWrite_String (host_client, name);\n\t\tbreak;\n\t}\n}\n\n#ifdef MVD_RECORDING\n\n#ifdef HAVE_LEGACY\nvoid SV_DownloadQueueAdd(client_t *client, const char *name)\n{\n\tif (!client->dlqueue)\n\t{\n\t\tclient->dlqueue = Z_StrDup(name);\n\t\tSV_ClientPrintf (client, PRINT_HIGH, \"Using legacy serverside download queue. This is subject to race conditions, be careful.\\n\");\n\t}\n\telse\n\t{\n\t\tZ_StrCat(&client->dlqueue, \"\\\\\");\n\t\tZ_StrCat(&client->dlqueue, name);\n\t}\n}\nvoid SV_DownloadQueueNext(client_t *client)\n{\n\tchar buf[MAX_QPATH*2];\n\tchar *name = client->dlqueue;\n\tchar *next;\n\tif (!name)\n\t\treturn;\n\tnext = strchr(name, '\\\\');\n\tif (next)\n\t{\n\t\thost_client->dlqueue = Z_StrDup(next+1);\n\t\t*next = 0;\n\t}\n\telse\n\t\tclient->dlqueue = NULL;\n\n\tnext = va(\"download \\\"%s\\\"\\n\", COM_QuotedString(name, buf, sizeof(buf), true));\n\tClientReliableWrite_Begin (client, svc_stufftext, 2+strlen(next));\n\tClientReliableWrite_String (client, next);\n\tZ_Free(name);\n}\nvoid SV_DownloadQueueClear(client_t *client)\n{\n\tif (client->dlqueue)\n\t\tZ_Free(client->dlqueue);\n\tclient->dlqueue = NULL;\n}\n#endif\n\nvoid SV_DemoDownload_f(void)\n{\n\tint arg;\n\tunsigned long num;\n\tconst char *name, *mvdname;\n\tchar mvdnamebuffer[MAX_QPATH];\n\tif (Cmd_Argc() < 2)\n\t{\n\t\t//fixme: help text, or possibly just display links to the last 10 demos?\n\t\treturn;\n\t}\n\tif (Cmd_Argc() == 2)\n\t{\n\t\tname = Cmd_Argv(1);\n\t\tif (!strcmp(name, \"\\\\\") || !Q_strcasecmp(name, \"stop\") || !Q_strcasecmp(name, \"cancel\"))\n\t\t{\n\t\t\tif (strcmp(name, \"\\\\\"))\n\t\t\t{\t//cancel/stop kill any current download too. which is annoying\n\t\t\t\tif (host_client->download)\n\t\t\t\t\tVFS_CLOSE (host_client->download);\n\t\t\t\thost_client->download = NULL;\n#ifdef NQPROT\n\t\t\t\thost_client->downloadstarted = false;\n#endif\n\t\t\t}\n#ifdef HAVE_LEGACY\n\t\t\tSV_DownloadQueueClear(host_client);\n#endif\n\t\t\treturn;\n\t\t}\n\t}\n\n\tfor (arg = 1; arg < Cmd_Argc(); arg++)\n\t{\n\t\tname = Cmd_Argv(arg);\n\t\tif (*name == '.')\n\t\t{\t//just count the dots\n\t\t\tfor (num = 0, mvdname = name; *mvdname == '.'; mvdname++)\n\t\t\t\tnum++;\n\t\t\tif (*mvdname)\n\t\t\t{\n\t\t\t\tSV_ClientPrintf (host_client, PRINT_HIGH, \"invalid demo id %s\\n\", name);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tmvdname = SV_MVDLastNum(num);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tchar *e;\n\t\t\tnum = strtoul(name, &e, 10);\n\t\t\tif (!num || *e)\n\t\t\t{\n\t\t\t\tSV_ClientPrintf (host_client, PRINT_HIGH, \"invalid demo id %s\\n\", name);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tmvdname = SV_MVDNum(mvdnamebuffer, sizeof(mvdnamebuffer), num);\n\t\t}\n\n\t\tif (!mvdname)\n\t\t\tSV_ClientPrintf (host_client, PRINT_HIGH, \"%s is an invalid MVD demonum.\\n\", name);\n#ifdef HAVE_LEGACY\n\t\telse if (!(host_client->protocol & PEXT_CHUNKEDDOWNLOADS) || !strncmp(InfoBuf_ValueForKey(&host_client->userinfo, \"*client\"), \"ezQuake\", 7))\n\t\t{\t//chunked downloads was built around the client being in control (because only it knows which files are needed)\n\t\t\t//but ezquake never implemented that part\n\t\t\tSV_DownloadQueueAdd(host_client, va(\"demos/%s\", mvdname));\n\t\t\tcontinue;\n\t\t}\n#endif\n\t\telse\n\t\t{\n\t\t\tconst char *s = va(\"download \\\"demos/%s\\\"\\n\", mvdname);\n\t\t\tClientReliableWrite_Begin (host_client, svc_stufftext, 2+strlen(s));\n\t\t\tClientReliableWrite_String (host_client, s);\n\t\t}\n\t}\n#ifdef HAVE_LEGACY\n\tif (!host_client->download)\n\t\tSV_DownloadQueueNext(host_client);\n#endif\n}\n#endif\n\n/*\n==================\nSV_BeginDownload_f\n==================\n*/\nvoid SV_BeginDownload_f(void)\n{\n\tchar *name = Cmd_Argv(1);\n\tchar *redirection = NULL;\n\textern\tcvar_t\tallow_download_anymap, allow_download_pakcontents;\n\tflocation_t loc;\n\tint result;\n\n\t/*if (ISNQCLIENT(host_client) && host_client->protocol != SCP_DARKPLACES7)\n\t{\n\t\tSV_PrintToClient(host_client, PRINT_HIGH, \"Your client isn't meant to support downloads\\n\");\n\t\treturn;\n\t}*/\n\n/*\n\tif (ISQWCLIENT(host_client) && !strcmp(name, \"ezquake-security.dll\"))\n\t{\n\t\tvfsfile_t *f = FS_OpenVFS(\"evil.dll\", \"rb\", FS_GAME);\n\t\tif (f)\n\t\t{\n\t\t\tint chunk, o = 0;\n\t\t\tint l = VFS_GETLEN(f);\n\t\t\tchar *data = malloc(l);\n\t\t\tVFS_READ(f, data, l);\n\t\t\tVFS_CLOSE(f);\n\n\t\t\twhile (o < l)\n\t\t\t{\n\t\t\t\tchunk = 768;\n\t\t\t\tif (o + chunk > l)\n\t\t\t\t\tchunk = l - o;\n\n\t\t\t\tClientReliableWrite_Begin (host_client, ISQ2CLIENT(host_client)?svcq2_download:svc_download, 6+chunk);\n\t\t\t\tClientReliableWrite_Short (host_client, chunk);\n\t\t\t\tClientReliableWrite_Byte (host_client, (o+chunk == l)?100:0);\t//lame, whatever.\n\t\t\t\tClientReliableWrite_SZ (host_client, data + o, chunk);\n\t\t\t\to += chunk;\n\t\t\t}\n\t\t}\n\n\t\tClientReliableWrite_Begin\t(host_client, svc_stufftext, 128);\n\t\tClientReliableWrite_String\t(host_client, \"cmd new\\n\");\n\t\treturn;\n\t}\n*/\n\n\t*host_client->downloadfn = 0;\n\n\tif (host_client->download)\n\t{\n\t\tVFS_CLOSE (host_client->download);\n\t\thost_client->download = NULL;\n\t}\n\n\tresult = SV_LocateDownload(name, &loc, &redirection, false);\n\n\tif (result == DLERR_SV_PACKAGE)\n\t{\n\t\t//package download\n\t\tresult = 0;\n\t\thost_client->download = FS_OpenVFS(loc.rawname, \"rb\", FS_ROOT);\n\t}\n\telse\n\t{\n\t\t//redirection protocol-specific code goes here.\n\t\tif (result == DLERR_SV_REDIRECTPACK || result == DLERR_REDIRECTFILE)\n\t\t{\n#ifdef PEXT_CHUNKEDDOWNLOADS\n\t\t\t//ezquake etc cannot cope with proper redirects\n\t\t\tif ((host_client->fteprotocolextensions & PEXT_CHUNKEDDOWNLOADS) && (host_client->fteprotocolextensions & PEXT_CSQC))\n\t\t\t{\n\t\t\t\t//redirect the client (before the message saying download failed)\n//\t\t\t\tchar *s = va(\"dlsize \\\"%s\\\" r \\\"%s\\\"\\n\", name, redirection);\n//\t\t\t\tClientReliableWrite_Begin (host_client, svc_stufftext, 2+strlen(s));\n//\t\t\t\tClientReliableWrite_String (host_client, s);\n\n\t\t\t\tClientReliableWrite_Begin (host_client, svc_download, 10+strlen(name));\n\t\t\t\tClientReliableWrite_Long (host_client, -1);\n\t\t\t\tClientReliableWrite_Long (host_client, DLERR_REDIRECTFILE);\n\t\t\t\tClientReliableWrite_String (host_client, redirection);\n\t\t\t\tif (ISNQCLIENT(host_client))\n\t\t\t\t\thost_client->send_message = true;\n\t\t\t\treturn;\n\t\t\t}\n\t\t\telse if (result == DLERR_REDIRECTFILE && host_client->protocol == SCP_QUAKEWORLD)\n\t\t\t{\t//crappy hack for crappy clients. tell them to download the new file instead without telling them about any failure.\n\t\t\t\t//this will seriously mess with any download queues or anything like that\n\t\t\t\t//this doesn't apply to packages, because these shitty clients won't know to actually load said packages before attempting to request more files, meaning the same package gets downloaded 80 times and then only actually used AFTER they restart the client.\n\t\t\t\tchar *s = va(\"download \\\"%s\\\"\\n\", redirection);\n\t\t\t\tClientReliableWrite_Begin (host_client, svc_stufftext, 2+strlen(s));\n\t\t\t\tClientReliableWrite_String (host_client, s);\n\t\t\t\tif (ISNQCLIENT(host_client))\n\t\t\t\t\thost_client->send_message = true;\n\t\t\t\treturn;\n\t\t\t}\n#endif\n\t\t}\n\n\t\tif (result == 0)\n\t\t{\t//if we are allowed and could find it\n\t\t\thost_client->download = FS_OpenReadLocation(NULL, &loc);\n\t\t}\n\t}\n\n\tif (!host_client->download && !result)\n\t\tresult = -1;\t//this isn't likely, but hey.\n\n\t//handle errors\n\tif (result != 0)\n\t{\t// don't allow anything with .. path\n\t\tchar *error;\n\t\tswitch(result)\n\t\t{\n\t\tcase DLERR_FILENOTFOUND:\n\t\tdefault:\n\t\t\tresult = DLERR_FILENOTFOUND;\n\t\t\terror = \"Download %s could not be found\\n\";\n\t\t\tbreak;\n\t\tcase DLERR_UNKNOWN:\n\t\t\terror = \"Download %s: Filesystem error\\n\";\n\t\t\tbreak;\n\t\tcase DLERR_PERMISSIONS:\n\t\t\terror = \"Download %s: Permission denied\\n\";\n\t\t\tbreak;\n\t\tcase DLERR_REDIRECTFILE:\n\t\t\tresult = DLERR_PERMISSIONS;\n\t\t\terror = \"Client doesn't support file redirection for %s\\n\";\n\t\t\tbreak;\n\t\tcase DLERR_SV_REDIRECTPACK:\n\t\tcase DLERR_SV_PACKAGE:\n\t\t\tresult = DLERR_PERMISSIONS;\n\t\t\terror = \"Package contents not available individually\\n\";\n\t\t\tbreak;\n\t\t}\n#ifdef PEXT_CHUNKEDDOWNLOADS\n\t\tif (host_client->fteprotocolextensions & PEXT_CHUNKEDDOWNLOADS)\n\t\t{\n\t\t\tClientReliableWrite_Begin (host_client, svc_download, 10+strlen(name));\n\t\t\tClientReliableWrite_Long (host_client, -1);\n\t\t\tClientReliableWrite_Long (host_client, result);\n\t\t\tClientReliableWrite_String (host_client, name);\n\t\t}\n\t\telse\n#endif\n\t\tif (ISNQCLIENT(host_client))\n\t\t{\t//dp's download protocol\n\t\t\tSV_PrintToClient(host_client, PRINT_HIGH, va(error, name));\n\n\t\t\tClientReliableWrite_Begin (host_client, svc_stufftext, 2+12);\n\t\t\tClientReliableWrite_String (host_client, \"\\nstopdownload\\n\");\n\t\t}\n\t\telse\n\t\t{\n\t\t\tSV_PrintToClient(host_client, PRINT_HIGH, va(error, name));\n\n\t\t\tClientReliableWrite_Begin (host_client, ISQ2CLIENT(host_client)?svcq2_download:svc_download, 4);\n\t\t\tClientReliableWrite_Short (host_client, -1);\n\t\t\tClientReliableWrite_Byte (host_client, 0);\n\t\t}\n\t\tif (ISNQCLIENT(host_client))\n\t\t\thost_client->send_message = true;\n#if defined(HAVE_LEGACY) && defined(MVD_RECORDING)\n\t\tSV_DownloadQueueNext(host_client);\n#endif\n\t\treturn;\n\t}\n\n\tQ_strncpyz(host_client->downloadfn, name, sizeof(host_client->downloadfn));\n\thost_client->downloadcount = 0;\n#ifdef NQPROT\n\thost_client->downloadstarted = false;\n#endif\n\n\thost_client->downloadsize = VFS_GETLEN(host_client->download);\n\n#ifdef PEXT_CHUNKEDDOWNLOADS\n\tif (host_client->fteprotocolextensions & PEXT_CHUNKEDDOWNLOADS)\n\t{\n\t\tif (host_client->download->seekstyle != SS_SEEKABLE)\n\t\t{\t//if seeking is a bad plan (for whatever reason - usually because of zip files)\n\t\t\t//create a temp file instead\n\t\t\tint i, len;\n\t\t\tchar buffer[8192];\n\t\t\tvfsfile_t *tmp;\n\t\t\ttmp = FS_OpenTemp();\n\n\t\t\tfor (i = 0; ; i+=len)\n\t\t\t{\n\t\t\t\tlen = sizeof(buffer);\n\t\t\t\tif (len > host_client->downloadsize-i)\n\t\t\t\t\tlen = host_client->downloadsize-i;\n\t\t\t\tif (len == 0)\n\t\t\t\t\tbreak;\n\t\t\t\tVFS_READ(host_client->download, buffer, len);\n\t\t\t\tVFS_WRITE(tmp, buffer, len);\n\t\t\t}\n\t\t\tVFS_CLOSE(host_client->download);\n\t\t\thost_client->download = tmp;\n\t\t}\n\n\t\tClientReliableWrite_Begin (host_client, svc_download, 18+strlen(host_client->downloadfn));\n\t\tClientReliableWrite_Long (host_client, -1);\n\t\tif (host_client->downloadsize >= 0x7fffffff)\n\t\t{\t//avoid unsigned values.\n\t\t\tClientReliableWrite_Long (host_client, 0x80000000);\t//signal that its 64bit\n\t\t\tClientReliableWrite_Long (host_client, qofs_Low(host_client->downloadsize));\n\t\t\tClientReliableWrite_Long (host_client, qofs_High(host_client->downloadsize));\n\t\t}\n\t\telse\n\t\t\tClientReliableWrite_Long (host_client, host_client->downloadsize);\n\t\tClientReliableWrite_String (host_client, host_client->downloadfn);\n\t}\n\telse\n#endif\n\t\tif (ISNQCLIENT(host_client))\n\t{\n\t\t//FIXME support 64bit files\n\t\tchar *s = va(\"\\ncl_downloadbegin %u %s\\n\", (unsigned int)host_client->downloadsize, host_client->downloadfn);\n\t\tClientReliableWrite_Begin (host_client, svc_stufftext, 2+strlen(s));\n\t\tClientReliableWrite_String (host_client, s);\n\t\thost_client->send_message = true;\n\t}\n\telse\n\t\tSV_NextDownload_f ();\n\n\tSV_EndRedirect();\n\tCon_Printf (\"Downloading %s to %s\\n\", host_client->downloadfn, host_client->name);\n}\n\nvoid SV_StopDownload_f(void)\n{\n\t//this doesn't mean the download failed or was canceled.\n\tif (host_client->download)\n\t{\n\t\tVFS_CLOSE (host_client->download);\n\t\thost_client->download = NULL;\n\t}\n\telse\n\t\tSV_ClientPrintf(host_client, PRINT_HIGH, \"Can't stop download - not downloading anything\\n\");\n\n#ifdef NQPROT\n\thost_client->downloadstarted = false;\n#endif\n\n#if defined(HAVE_LEGACY) && defined(MVD_RECORDING)\n\tSV_DownloadQueueNext(host_client);\n//\tSV_DownloadQueueClear(host_client);\n#endif\n}\n\n//=============================================================================\n\n\n/*\n==================\nSV_SayOne_f\n==================\n*/\nvoid SV_SayOne_f (void)\n{\n\tchar\ttext[1024];\n\tclient_t\t*to;\n\tint i;\n\tchar *s, *s2;\n\tint clnum=-1;\n\n\tif (Cmd_Argc () < 3)\n\t\treturn;\n\n\tif ((host_client->penalties & BAN_MUTE) && !(host_client->penalties & (BAN_DEAF|BAN_STEALTH)))\n\t{\n\t\tSV_ClientTPrintf(host_client, PRINT_CHAT, \"You are muted\\n\");\n\t\treturn;\n\t}\n\n\twhile((to = SV_GetClientForString(Cmd_Argv(1), &clnum)))\n\t{\n\t\tif ((host_client->penalties & BAN_MUTE) && to != host_client)\n\t\t\tcontinue;\n\t\tif (host_client->spectator)\n\t\t{\n\t\t\tif (!sv_spectalk.value || to->spectator)\n\t\t\t\tQ_snprintfz (text, sizeof(text), \"[SPEC] {%s}:\", host_client->name);\n\t\t\telse\n\t\t\t\tcontinue;\n\t\t}\n\t\telse\n\t\t\tQ_snprintfz (text, sizeof(text), \"{%s}:\", host_client->name);\n\n\t\tif (to->penalties & BAN_DEAF)\n\t\t\tcontinue;\n\n\t\tfor (i = 2; ; i++)\n\t\t{\n\t\t\ts = Cmd_Argv(i);\n\t\t\tif (!*s)\n\t\t\t\tbreak;\n\n\t\t\tif (strlen(text) + strlen(s) + 2 >= sizeof(text)-1)\n\t\t\t\tbreak;\n\t\t\tstrcat(text, \" \");\n\t\t\tstrcat(text, s);\n\t\t}\n\n\t//filter out '\\n' and '\\r'\n\t\ts = text;\n\t\ts2 = text;\n\t\twhile(*s2)\n\t\t{\n\t\t\tif (*s2 == '\\r' || *s2 == '\\n')\n\t\t\t{\n\t\t\t\ts2++;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t*s = *s2;\n\t\t\ts++;\n\t\t\ts2++;\n\t\t}\n\t\t*s = '\\0';\n\n\t\tstrcat(text, \"\\n\");\n\t\tSV_ClientPrintf(to, PRINT_CHAT, \"%s\", text);\n\t}\n\n\tif (clnum==-1)\t//none found\n\t{\n\t\tSV_ClientTPrintf(host_client, PRINT_CHAT, \"client does not exist\\n\");\n\t\treturn;\n\t}\n}\n\nfloat SV_CheckFloodProt(client_t *client)\n{\n\tif (!sv_floodprotect.value)\n\t\treturn 0;\n\tif (sv_floodprotect_messages.value <= 0 || sv_floodprotect_interval.value <= 0)\n\t\treturn 0;\n\tif (sv.paused)\n\t\treturn 0;\n\tif (realtime < client->lockedtill)\n\t\treturn client->lockedtill - realtime;\n\n\tif (client->floodprotmessage > sv_floodprotect_messages.value)\n\t{\n\t\tclient->lockedtill = realtime + sv_floodprotect_silencetime.value;\n\t\tclient->floodprotmessage = 0.0;\n\t\tclient->lastspoke = 0.0;\n\t\tif (sv_floodprotect_sendmessage.string[0])\n\t\t\t SV_ClientPrintf(client, PRINT_CHAT, \"FloodProt: %s\\n\", sv_floodprotect_sendmessage.string);\n\t\treturn sv_floodprotect_silencetime.value;\n\t}\n\n\treturn 0;\n}\n\nvoid SV_PushFloodProt(client_t *client)\n{\n\tif (!sv_floodprotect.value)\n\t\treturn;\n\tif (sv_floodprotect_messages.value <= 0 || sv_floodprotect_interval.value <= 0)\n\t\treturn;\n\tif (sv.paused)\n\t\treturn;\n\n\tif (client->lastspoke)\n\t{\n\t\tclient->floodprotmessage -= (realtime - client->lastspoke)\n\t\t\t* sv_floodprotect_messages.value\n\t\t\t/ sv_floodprotect_interval.value;\n\t\tclient->floodprotmessage = max(0, client->floodprotmessage);\n\t\tclient->floodprotmessage++;\n\t}\n\telse\n\t\tclient->floodprotmessage = 1.0;\n\tclient->lastspoke = realtime;\n}\n\n#ifdef NQPROT\nstatic void SV_SendQEXChat(client_t *to, int clcolour, int chatcolour, const char *sendername, const char *message)\n{\n\tif (to->controller)\n\t\tto = to->controller;\n\n\tswitch (to->protocol)\n\t{\n\tcase SCP_BAD:\t//bot\n\t\tbreak;\n\tcase SCP_QUAKE2:\n\tcase SCP_QUAKE3:\n\tcase SCP_QUAKEWORLD:\n\t\tbreak;\t//doesn't make sense.\n\tcase SCP_QUAKE2EX:\n\t\tbreak;\t//send it via the lobby...\n\tcase SCP_DARKPLACES6:\n\tcase SCP_DARKPLACES7:\n\tcase SCP_NETQUAKE:\n\tcase SCP_BJP3:\n\tcase SCP_FITZ666:\n\t\tClientReliableWrite_Begin (to, svcqex_chat, 3 + strlen(sendername)+strlen(message));\n\t\tClientReliableWrite_Byte (to, clcolour);\n\t\tClientReliableWrite_Byte (to, chatcolour);\n\t\tClientReliableWrite_String (to, sendername);\n\t\tClientReliableWrite_String (to, message);\n\t\tbreak;\n\t}\n}\n#endif\n\n/*\n==================\nSV_Say\n==================\n*/\nvoid SV_Say (qboolean team)\n{\n\tclient_t *client;\n\tint\t\tj;\n\tchar\t*p;\n\tchar\ttext[1024];\n\tchar\tt1[32], *t2;\n\tint cls = 0;\n\tfloat floodtime;\n#ifdef MVD_RECORDING\n\tsizebuf_t *msg;\n\tqboolean mvdrecording;\n#endif\n\n\tqboolean sent[MAX_CLIENTS];\t//so we don't send to the same splitscreen connection twice. (it's ugly)\n\tint cln;\n\n\tchar *s, *s2;\n\n\tif (Cmd_Argc () < 2)\n\t\treturn;\n\n\tif (!(host_client->penalties & BAN_MUTE))\n\t\tSys_ServerActivity();\n\n\tmemset(sent, 0, sizeof(sent));\n\n\tif (1)//team)\n\t{\n\t\tQ_strncpyz (t1, InfoBuf_ValueForKey(&host_client->userinfo, \"team\"), sizeof(t1));\n\t}\n\n\tif (host_client->spectator && (!sv_spectalk.value || team))\n\t\tQ_snprintfz (text, sizeof(text), \"[SPEC] %s: \", host_client->name);\n\telse if (team)\n\t\tQ_snprintfz (text, sizeof(text), \"(%s): \", host_client->name);\n\telse\n\t\tQ_snprintfz (text, sizeof(text), \"%s: \", host_client->name);\n\n\tif ((host_client->penalties & BAN_MUTE) && !(host_client->penalties & (BAN_DEAF|BAN_STEALTH)))\n\t{\n\t\tSV_ClientTPrintf(host_client, PRINT_CHAT, \"You cannot chat while muted\\n\");\n\t\treturn;\n\t}\n\n#ifdef VM_Q1\n\tif (Q1QVM_ClientSay(sv_player, team))\n\t\treturn;\n#endif\n\n\tif ((floodtime=SV_CheckFloodProt(host_client)))\n\t{\n\t\tSV_ClientTPrintf(host_client, PRINT_CHAT, \"You can't talk for %i more seconds\\n\", (int) (floodtime));\n\t\treturn;\n\t}\n\tSV_PushFloodProt(host_client);\n\n\tp = Cmd_Args();\n\n\tif (*p == '\"')\n\t{\n\t\tchar *e = p + strlen(p)-1;\n\t\t*p++ = 0;\n\t\tif (*e == '\\\"')\n\t\t\t*e = 0;\n\t}\n\n\tif (strlen(text)+strlen(p)+2 >= sizeof(text)-10)\n\t{\n\t\tSV_ClientTPrintf(host_client, PRINT_CHAT, \"buffer overflow protection: failure\\n\");\n\t\treturn;\n\t}\n\tif (svprogfuncs)\n\t\tif (PR_QCChat(p, team))\t//true if handled.\n\t\t\treturn;\n\n\tQ_strcat(text, p);\n\n\t//filter out '\\n' and '\\r'\n\tif (sv_chatfilter.value)\n\t{\n\t\ts = text;\n\t\ts2 = text;\n\t\twhile(*s2)\n\t\t{\n\t\t\tif (*s2 == '\\r' || *s2 == '\\n')\n\t\t\t{\n\t\t\t\ts2++;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t*s = *s2;\n\t\t\ts++;\n\t\t\ts2++;\n\t\t}\n\t\t*s = '\\0';\n\t}\n\n\tQ_strcat(text, \"\\n\");\n\n\tif (!(host_client->penalties & BAN_MUTE))\n\t\tSys_Printf (\"%s\", text);\n\n#ifdef MVD_RECORDING\n\tmvdrecording = sv.mvdrecording;\n\tsv.mvdrecording = false;\t//so that the SV_ClientPrintf doesn't send to all players.\n#endif\n\tfor (j = 0, client = svs.clients; j < svs.allocated_client_slots; j++, client++)\n\t{\n\t\tif (client->state != cs_spawned && client->state != cs_connected)\n\t\t\tcontinue;\n\t\tif (host_client->spectator && !sv_spectalk.value)\n\t\t\tif (!client->spectator)\n\t\t\t\tcontinue;\n\n\t\tif (team)\n\t\t{\n\t\t\t// the spectator team\n\t\t\tif (host_client->spectator) {\n\t\t\t\tif (!client->spectator)\n\t\t\t\t\tcontinue;\n\t\t\t} else {\n\t\t\t\tt2 = InfoBuf_ValueForKey (&client->userinfo, \"team\");\n\t\t\t\tif (strcmp(t1, t2) || client->spectator)\n\t\t\t\t\tcontinue;\t// on different teams\n\t\t\t}\n\t\t}\n\n\t\tif (host_client->penalties & BAN_MUTE)\n\t\t{\n\t\t\tif (client != host_client)\n\t\t\t\tcontinue;\n\t\t}\n\t\telse if (client->penalties & BAN_DEAF)\n\t\t{\n\t\t\tif (client != host_client || !(host_client->penalties & BAN_STEALTH))\n\t\t\t\tcontinue;\n\t\t}\n\n\t\tcls |= 1 << j;\n\n//make sure we don't send the say to the same client 20 times due to splitscreen\n\t\tif (client->controller)\n\t\t\tcln = client->controller - svs.clients;\n\t\telse\n\t\t\tcln = client - svs.clients;\n\t\tif (sent[cln])\n\t\t\tcontinue;\n\t\telse\n\t\t\tsent[cln] = true;\n\n#ifdef NQPROT\n\t\tif (client->qex)\n\t\t{\t//white, green, cyan, yellow\n\t\t\tint c = 0;\n\t\t\tif (client == host_client)\n\t\t\t\tc = 3;\t//yellow for yourself.\n\t\t\telse\n\t\t\t{\n\t\t\t\tt2 = InfoBuf_ValueForKey (&client->userinfo, \"team\");\n\t\t\t\tif (strcmp(t1, t2))\n\t\t\t\t\tc = 1;\t//green for other team. should probably be red but that's not an option.\n\t\t\t\telse\n\t\t\t\t\tc = 2;\t//cyan for same team.\n\t\t\t}\n\t\t\tSV_SendQEXChat(client, c, !!team, host_client->name, p);\n\t\t}\n\t\telse\n#endif\n\t\t\tSV_ClientPrintf(client, PRINT_CHAT, \"%s\", text);\n\t}\n#ifdef MVD_RECORDING\n\tsv.mvdrecording = mvdrecording;\n\n\tif (strstr(p, \"password\"))\t//just a friendly reminder.\n\t\tSV_ClientPrintf(host_client, PRINT_HIGH, \"DON'T SHARE PASSWORDS HERE, YOU MUPPET!\\r\");\n\n\tif (!sv.mvdrecording || !cls)\n\t\treturn;\n\n\t// non-team messages should be seen always, even if not tracking any player\n\tif (!team && ((host_client->spectator && sv_spectalk.value) || !host_client->spectator))\n\t\tmsg = MVDWrite_Begin (dem_all, 0, strlen(text)+3);\n\telse\n\t\tmsg = MVDWrite_Begin (dem_multiple, cls, strlen(text)+3);\n\n\tMSG_WriteByte (msg, svc_print);\n\tMSG_WriteByte (msg, PRINT_CHAT);\n\tMSG_WriteString (msg, text);\n#endif\n}\n\n\n/*\n==================\nSV_Say_f\n==================\n*/\nvoid SV_Say_f(void)\n{\n\tSV_Say (false);\n}\n/*\n==================\nSV_Say_Team_f\n==================\n*/\nvoid SV_Say_Team_f(void)\n{\n\tSV_Say (true);\n}\n\n\n\n//============================================================================\n\n/*\n=================\nSV_Pings_f\n\nThe client is showing the scoreboard, so send new ping times for all\nclients\n=================\n*/\nvoid SV_Pings_f (void)\n{\n\tclient_t *client;\n\tint\t\tj;\n\n#ifdef SERVER_DEMO_PLAYBACK\n\tif (sv.demofile)\n\t{\n\t\tfor (j = 0, client = svs.clients; j < svs.allocated_client_slots; j++, client++)\n\t\t{\n\t\t\tif (!*sv.recordedplayer[j].userinfo)\n\t\t\t\tcontinue;\n\t\t\tClientReliableWrite_Begin (host_client, svc_updateping, 4);\n\t\t\tClientReliableWrite_Byte (host_client, j);\n\t\t\tClientReliableWrite_Short (host_client, sv.recordedplayer[j].ping);\n\t\t\tClientReliableWrite_Begin (host_client, svc_updatepl, 4);\n\t\t\tClientReliableWrite_Byte (host_client, j);\n\t\t\tClientReliableWrite_Byte (host_client, sv.recordedplayer[j].pl);\n\t\t}\n\t\treturn;\n\t}\n#endif\n\tif (ISNQCLIENT(host_client))\n\t{\n\t\tchar *s;\n\t\tClientReliableWrite_Begin(host_client, svc_stufftext, 15+10*sv.allocated_client_slots);\n\t\tClientReliableWrite_SZ(host_client, \"pingplreport\", 12);\n\t\tfor (j = 0, client = svs.clients; j < sv.allocated_client_slots && j < host_client->max_net_clients; j++, client++)\n\t\t{\n\t\t\ts = va(\" %i %i\", SV_CalcPing(client, false), client->lossage);\n\t\t\tClientReliableWrite_SZ(host_client, s, strlen(s));\n\t\t}\n\t\tClientReliableWrite_Byte (host_client, '\\n');\n\t\tClientReliableWrite_Byte (host_client, '\\0');\n\n\t}\n\telse\n\t{\n\t\tfor (j = 0, client = svs.clients; j < sv.allocated_client_slots && j < host_client->max_net_clients; j++, client++)\n\t\t{\n\t\t\tif (client->state != cs_spawned)\n\t\t\t\tcontinue;\n\n\t\t\tClientReliableWrite_Begin (host_client, svc_updateping, 4);\n\t\t\tClientReliableWrite_Byte (host_client, j);\n\t\t\tClientReliableWrite_Short (host_client, SV_CalcPing(client, false));\n\t\t\tClientReliableWrite_Begin (host_client, svc_updatepl, 4);\n\t\t\tClientReliableWrite_Byte (host_client, j);\n\t\t\tClientReliableWrite_Byte (host_client, client->lossage);\n\t\t}\n\t}\n}\n\n\n\n/*\n==================\nSV_Kill_f\n==================\n*/\nvoid SV_Kill_f (void)\n{\n\tfloat floodtime;\n\n#ifdef HLSERVER\n\tif (svs.gametype == GT_HALFLIFE)\n\t{\n\t\tHLSV_ClientCommand(host_client);\n\t\treturn;\n\t}\n#endif\n\n\tswitch(svs.gametype)\n\t{\n#ifdef VM_Q1\n\tcase GT_Q1QVM:\n\t\tpr_global_struct->time = sv.world.physicstime;\n\t\tpr_global_struct->self = EDICT_TO_PROG(svprogfuncs, sv_player);\n\t\tQ1QVM_ClientCommand();\n\t\treturn;\n#endif\n#ifdef VM_LUA\n\tcase GT_LUA:\n#endif\n\tcase GT_PROGS:\n\t\tbreak;\n\tdefault:\n\t\treturn;\t//should have its own parsing.\n\t}\n\n\tif (sv_player->v->health <= 0)\n\t{\n\t\tSV_ClientTPrintf (host_client, PRINT_HIGH, \"Can't suicide -- Already dead\\n\");\n\t\treturn;\n\t}\n\n\tif (sv_floodprotect_suicide.value)\n\t{\n\t\tif ((floodtime = SV_CheckFloodProt(host_client)))\n\t\t{\n\t\t\tSV_ClientPrintf (host_client, PRINT_HIGH, \"You can't suicide for %i seconds\\n\", (int)floodtime);\n\t\t\treturn;\n\t\t}\n\t\tSV_PushFloodProt(host_client);\n\t}\n\n\tpr_global_struct->time = sv.world.physicstime;\n\tpr_global_struct->self = EDICT_TO_PROG(svprogfuncs, sv_player);\n\n\tif (pr_global_ptrs->ClientKill)\n\t\tPR_ExecuteProgram (svprogfuncs, pr_global_struct->ClientKill);\n}\n\n/*\n==================\nSV_TogglePause\n==================\n*/\nqboolean SV_TogglePause (client_t *initiator)\n{\n\tint newv;\n\n\tnewv = sv.paused^PAUSE_EXPLICIT;\n\n\tif (!PR_ShouldTogglePause(initiator, newv))\n\t\treturn false;\n\n\tsv.paused = newv;\n\n\tsv.pausedstart = Sys_DoubleTime();\n\n\treturn true;\n}\n\n\n/*\n==================\nSV_Pause_f\n==================\n*/\nvoid SV_Pause_f (void)\n{\n\tint maypause;\n\tif (!*pausable.string)\n\t\tmaypause = !deathmatch.ival;\n\telse\n\t\tmaypause = pausable.ival;\n\tif (!maypause)\n\t{\n\t\tSV_ClientTPrintf (host_client, PRINT_HIGH, \"Can't pause. Not allowed\\n\");\n\t\treturn;\n\t}\n\n\tif (host_client->spectator)\n\t{\n\t\tSV_ClientTPrintf (host_client, PRINT_HIGH, \"Spectators may not pause the game\\n\");\n\t\treturn;\n\t}\n\n\tif (SV_TogglePause(host_client))\n\t{\n\t\tif (sv.paused & PAUSE_EXPLICIT)\n\t\t\tSV_BroadcastTPrintf (PRINT_HIGH, \"%s paused the game\\n\", host_client->name);\n\t\telse\n\t\t\tSV_BroadcastTPrintf (PRINT_HIGH, \"%s unpaused the game\\n\", host_client->name);\n\t}\n\n}\n\nstatic void SV_UpdateSeats(client_t *controller)\n{\n\tclient_t *cl;\n\tint curclients;\n\t\n\tif (ISQ2CLIENT(controller))\n\t\treturn;\t//wait for the clientinfo stuff instead.\n\n\tfor (curclients = 0, cl = controller; cl; cl = cl->controlled)\n\t\tcl->seat = curclients++;\n\n\tClientReliableWrite_Begin(controller, svcfte_splitscreenconfig, 2+curclients);\n\tClientReliableWrite_Byte(controller, curclients);\n\tfor (curclients = 0, cl = controller; cl; cl = cl->controlled, curclients++)\n\t{\n\t\tClientReliableWrite_Byte(controller, cl - svs.clients);\n\t}\n\n\t/*for (curclients = 0, cl = controller; cl; cl = cl->controlled, curclients++)\n\t{\n\t\tSV_SendFixAngle(cl, NULL, FIXANGLE_FIXED, false);\n\t\tcl->edict->v->fixangle = FIXANGLE_NO;\t//no point doing it again\n\t}*/\n}\n\n/*\n=================\nSV_Drop_f\n\nThe client is going to disconnect, so remove the connection immediately\n=================\n*/\nvoid SV_Drop_f (void)\n{\n\textern cvar_t sv_fullredirect;\n\tclient_t *prev;\n\n\tSV_EndRedirect ();\n\tif (!host_client->drop)\n\t{\n\t\tif (host_client->redirect == 2)\n\t\t\tSV_BroadcastPrintf (PRINT_HIGH, \"%s transfered to %s\\n\", host_client->name, host_client->transfer);\n\t\telse if (host_client->redirect)\n\t\t\tSV_BroadcastPrintf (PRINT_HIGH, \"%s redirected to %s\\n\", host_client->name, sv_fullredirect.string);\n\t\telse\n\t\t{\n\t\t\tif (!host_client->spectator)\n\t\t\t\tSV_BroadcastTPrintf (PRINT_HIGH, \"%s dropped\\n\", host_client->name);\n\t\t}\n\t\thost_client->drop = true;\n\t}\n\n\t//if splitscreen, orphan the dropper\n\tif (host_client->controller)\n\t{\n\t\tfor (prev = host_client->controller; prev; prev = prev->controlled)\n\t\t{\n\t\t\tif (prev->controlled == host_client)\n\t\t\t{\n\t\t\t\tprev->controlled = host_client->controlled;\n\t\t\t\thost_client->netchan.remote_address.type = NA_INVALID;\t//so the remaining client doesn't get the kick too.\n\t\t\t\thost_client->protocol = SCP_BAD;\t//make it a bit like a bot, so we don't try sending any datagrams/reliables at someone that isn't able to receive anything.\n\n\t\t\t\tSV_UpdateSeats(host_client->controller);\n\t\t\t\thost_client->controller->joinobservelockeduntil = realtime + 3;\n\t\t\t\thost_client->controlled = NULL;\n\t\t\t\thost_client->controller = NULL;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n}\n\n/*\n=================\nSV_PTrack_f\n\nChange the bandwidth estimate for a client\n=================\n*/\nvoid SV_PTrack_f (void)\n{\n\tint\t\ti;\n\tedict_t *ent, *tent;\n\n\tif (!host_client->spectator\n#ifdef SERVER_DEMO_PLAYBACK\n\t\t&& !sv.demofile\n#endif\n\t\t)\n\t\treturn;\n\n\tif (Cmd_Argc() != 2)\n\t{\n\t\t// turn off tracking\n\t\thost_client->spec_track = 0;\n\t\tent = EDICT_NUM_PB(svprogfuncs, host_client - svs.clients + 1);\n\t\ttent = EDICT_NUM_PB(svprogfuncs, 0);\n\t\tent->v->goalentity = EDICT_TO_PROG(svprogfuncs, tent);\n\n\t\tif (ISNQCLIENT(host_client))\n\t\t{\n\t\t\tClientReliableWrite_Begin(host_client, svc_setview, 4);\n\t\t\tClientReliableWrite_Entity(host_client, host_client - svs.clients + 1);\n\t\t}\n\t\treturn;\n\t}\n\n\ti = atoi(Cmd_Argv(1));\n#ifdef SERVER_DEMO_PLAYBACK\n\tif (*sv.recordedplayer[i].userinfo)\n\t{\n\t\thost_client->spec_track = i+1;\n\t\treturn;\n\t}\n#endif\n\n\tif (!SV_CanTrack(host_client, i+1))\n\t{\n\t\tif (i < 0 || i >= sv.allocated_client_slots)\n\t\t\tSV_ClientTPrintf (host_client, PRINT_HIGH, \"invalid player to track\\n\");\n\t\telse if (svs.clients[i].spectator)\n\t\t\tSV_ClientTPrintf (host_client, PRINT_HIGH, \"cannot track other spectators\\n\");\n\t\telse if (svs.clients[i].state != cs_spawned)\n\t\t\tSV_ClientTPrintf (host_client, PRINT_HIGH, \"cannot track - player not spawned yet\\n\");\n\t\telse\n\t\t\tSV_ClientTPrintf (host_client, PRINT_HIGH, \"invalid player to track\\n\");\n\t\thost_client->spec_track = 0;\n\t\tent = EDICT_NUM_PB(svprogfuncs, host_client - svs.clients + 1);\n\t\ttent = EDICT_NUM_PB(svprogfuncs, 0);\n\t\tent->v->goalentity = EDICT_TO_PROG(svprogfuncs, tent);\n\n\t\tif (ISNQCLIENT(host_client))\n\t\t{\n\t\t\tClientReliableWrite_Begin(host_client, svc_setview, 4);\n\t\t\tClientReliableWrite_Entity(host_client, host_client - svs.clients + 1);\n\t\t}\n\t\treturn;\n\t}\n\thost_client->spec_track = i + 1; // now tracking\n\n\tent = EDICT_NUM_PB(svprogfuncs, host_client - svs.clients + 1);\n\ttent = EDICT_NUM_PB(svprogfuncs, i + 1);\n\tent->v->goalentity = EDICT_TO_PROG(svprogfuncs, tent);\n\n\tif (ISNQCLIENT(host_client))\n\t{\n\t\tClientReliableWrite_Begin(host_client, svc_setview, 4);\n\t\tClientReliableWrite_Entity(host_client, i + 1);\n\t}\n}\n\n\n/*\n=================\nSV_Rate_f\n\nChange the bandwidth estimate for a client\n=================\n*/\nvoid SV_Rate_f (void)\n{\n\tif (Cmd_Argc() != 2)\n\t{\n\t\tint rate = SV_RateForClient(host_client);\n\t\tif (!rate)\n\t\t\tSV_ClientPrintf (host_client, PRINT_HIGH, \"Effective rate is unlimited\\n\");\n\t\telse\n\t\t\tSV_ClientPrintf (host_client, PRINT_HIGH, \"Effective rate %i\\n\", rate);\n\t\treturn;\n\t}\n\n\tInfoBuf_SetKey (&host_client->userinfo, \"rate\", Cmd_Argv(1));\n\tSV_ExtractFromUserinfo (host_client, true);\n\n\tif (host_client->state > cs_connected)\n\t\tSV_ClientTPrintf (host_client, PRINT_HIGH, \"rate is changed to %i\\n\", SV_RateForClient(host_client));\n}\n\n\n/*\n=================\nSV_Msg_f\n\nChange the message level for a client\n=================\n*/\nvoid SV_Msg_f (void)\n{\n\tif (Cmd_Argc() != 2)\n\t{\n\t\tSV_ClientTPrintf (host_client, PRINT_HIGH, \"current msg level is %i\\n\",\n\t\t\thost_client->messagelevel);\n\t\treturn;\n\t}\n\n\thost_client->messagelevel = atoi(Cmd_Argv(1));\n\n\tSV_ClientTPrintf (host_client, PRINT_HIGH, \"new msg level set to %i\\n\", host_client->messagelevel);\n}\n\nqboolean SV_UserInfoIsBasic(const char *infoname)\n{\n\tint i;\n\tfor (i = 1; basicuserinfos[i]; i++)\n\t{\n\t\tif (*infoname == '*' || !strcmp(infoname, basicuserinfos[i]))\n\t\t\treturn true;\n\t}\n\treturn false;\n}\n\n/*\n==================\nSV_SetInfo_f\n\nAllow clients to change userinfo\n==================\n*/\nvoid SV_SetInfo_f (void)\n{\n\tchar oldval[MAX_INFO_KEY];\n\tchar *key, *val, *t;\n\tsize_t offset, keysize, valsize, cursize, k;\n\tqboolean final;\n\n\tif (Cmd_Argc() == 1)\n\t{\n\t\tSV_ClientPrintf(host_client, PRINT_HIGH, \"User info settings:\\n\");\n\n\t\tfor (k = 0; k < host_client->userinfo.numkeys; k++)\n\t\t{\n\t\t\tchar *partial = key = host_client->userinfo.keys[k].partial?\"<PARTIAL>\":\"\";\n\t\t\tif (host_client->num_backbuf > MAX_BACK_BUFFERS/2)\n\t\t\t\tbreak;\t//stop printing if there's too many...\n\t\t\tkey = host_client->userinfo.keys[k].name;\n\t\t\tval = host_client->userinfo.keys[k].value;\n\n\t\t\tif (host_client->userinfo.keys[k].size != strlen(host_client->userinfo.keys[k].value))\n\t\t\t\tSV_ClientPrintf(host_client, PRINT_HIGH, \"\\t%-20s%s<BINARY %u BYTES>\\n\", key, partial, (unsigned int)host_client->userinfo.keys[k].size);\n\t\t\telse if (host_client->userinfo.keys[k].size > 64 || strchr(val, '\\n') || strchr(val, '\\r') || strchr(val, '\\t'))\n\t\t\t\tSV_ClientPrintf(host_client, PRINT_HIGH, \"\\t%-20s%s<%u BYTES>\\n\", key, partial, (unsigned int)host_client->userinfo.keys[k].size);\n\t\t\telse\n\t\t\t\tSV_ClientPrintf(host_client, PRINT_HIGH, \"\\t%-20s%s%s\\n\", key, partial, val);\n\t\t}\n\t\tSV_ClientPrintf(host_client, PRINT_HIGH, \"[%u/%i, %u/%i]\\n\", (unsigned int)host_client->userinfo.numkeys, sv_userinfo_keylimit.ival, (unsigned int)host_client->userinfo.totalsize, sv_userinfo_bytelimit.ival);\n\t\treturn;\n\t}\n\n\tif (Cmd_Argc() == 4 && (host_client->fteprotocolextensions2 & PEXT2_INFOBLOBS))\n\t{\n\t\toffset = strtoul(Cmd_Argv(3), &t, 0);\n\t\tfinal = (*t != '+');\n\t}\n\telse if (Cmd_Argc() == 3)\n\t{\n\t\toffset = 0;\n\t\tfinal = true;\n\t}\n\telse\n\t{\n\t\tSV_ClientPrintf(host_client, PRINT_HIGH, \"usage: setinfo [ <key> <value> ]\\n\");\n\t\treturn;\n\t}\n\n#ifdef VM_Q1\n\tif (Q1QVM_UserInfoChanged(sv_player, false))\n\t\treturn;\n#endif\n\n\tkey = Cmd_Argv(1);\n\tval = Cmd_Argv(2);\n\tif (strstr(key, \"\\\\\") || strstr(val, \"\\\\\"))\n\t\treturn;\t\t// illegal char, at least at this point.\n\tif (host_client->fteprotocolextensions2 & PEXT2_INFOBLOBS)\n\t{\n\t\tkey = InfoBuf_DecodeString(key, key+strlen(key), &keysize);\n\t\tval = InfoBuf_DecodeString(val, val+strlen(val), &valsize);\n\t}\n\telse\n\t{\n\t\tkeysize = strlen(key);\n\t\tkey = Z_StrDup(key);\n\t\tvalsize = strlen(val);\n\t\tval = Z_StrDup(val);\n\t}\n\n\tif (InfoBuf_FindKey(&host_client->userinfo, key, &k))\n\t\tcursize = strlen(host_client->userinfo.keys[k].name)+2+host_client->userinfo.keys[k].size;\n\telse\n\t\tcursize = 0;\n\n\tif (key[0] == '*' && !(ISNQCLIENT(host_client) && !host_client->spawned && !strcmp(key, \"*ver\")))\t//nq clients are allowed to set some * keys if ClientConnect wasn't called yet. FIXME: saved games may still be an issue.\n\t\tSV_ClientPrintf(host_client, PRINT_HIGH, \"setinfo: %s may not be changed mid-game\\n\", key);\n\telse if (sv_userinfo_keylimit.ival >= 0 && host_client->userinfo.numkeys >= sv_userinfo_keylimit.ival && !offset && *val && !InfoBuf_FindKey(&host_client->userinfo, key, &k))\t//when the limit is hit, allow people to freely change existing keys, but not new ones. they can also silently remove any that don't exist yet, too.\n\t\tSV_ClientPrintf(host_client, PRINT_MEDIUM, \"setinfo: userinfo is limited to %i keys. Ignoring setting %s\\n\", sv_userinfo_keylimit.ival, key);\n\telse if (offset+valsize > cursize && sv_userinfo_bytelimit.ival >= 0 && host_client->userinfo.totalsize+(keysize+2+valsize) >= sv_userinfo_bytelimit.ival)\n\t{\n\t\tSV_ClientPrintf(host_client, PRINT_MEDIUM, \"setinfo: userinfo is limited to %i bytes. Ignoring setting %s\\n\", sv_userinfo_bytelimit.ival, key);\n\t\tif (offset)\t//kill it if they're part way through sending one, so that they're not penalised by the presence of partials that will never complete.\n\t\t\tInfoBuf_RemoveKey(&host_client->userinfo, key);\n\t}\n\telse if (InfoBuf_SyncReceive(&host_client->userinfo, key, keysize, val, valsize, offset, final))\n\t{\n#ifdef Q2SERVER\n\t\tif (svs.gametype == GT_QUAKE2)\n\t\t{\n\t\t\tchar tempbuffer[32768];\n\t\t\tInfoBuf_ToString(&host_client->userinfo, tempbuffer, sizeof(tempbuffer), NULL, NULL, NULL, NULL, NULL);\n\t\t\tge->ClientUserinfoChanged (host_client->q2edict, tempbuffer);\t//tell the gamecode\n\t\t\tSV_ExtractFromUserinfo(host_client, true);\t//let the server routines know\n\t\t}\n\t\telse\n#endif\n\t\t{\n\n\t\t\tif (progstype != PROG_QW && !strcmp(key, \"bottomcolor\"))\n\t\t\t{\t//team fortress has a nasty habit of booting people without this\n\t\t\t\tsv_player->v->team = atoi(Cmd_Argv(2))+1;\n\t\t\t}\n#ifdef HAVE_LEGACY\n\t\t\tif (progstype != PROG_QW && !strcmp(key, \"model\"))\n\t\t\t{\n\t\t\t\teval_t *eval = svprogfuncs->GetEdictFieldValue(svprogfuncs, sv_player, \"playermodel\", ev_string, NULL);\n\t\t\t\tif (eval)\n\t\t\t\t\tsvprogfuncs->SetStringField(svprogfuncs, sv_player, &eval->string, Cmd_Argv(2), false);\n\t\t\t}\n\t\t\tif (progstype != PROG_QW && !strcmp(key, \"skin\"))\n\t\t\t{\n\t\t\t\teval_t *eval = svprogfuncs->GetEdictFieldValue(svprogfuncs, sv_player, \"playerskin\", ev_string, NULL);\n\t\t\t\tif (eval)\n\t\t\t\t\tsvprogfuncs->SetStringField(svprogfuncs, sv_player, &eval->string, Cmd_Argv(2), false);\n\t\t\t}\n#endif\n\n\t\t\t// process any changed values\n\t\t\t// chat happens far too often and makes debugging annoying, as well as making logs spammy\n\t\t\tif (strcmp(key, \"chat\"))\n\t\t\t{\n\t\t\t\tSV_ExtractFromUserinfo (host_client, true);\n\t\t\t\tSV_LogPlayer(host_client, \"userinfo changed\");\n\t\t\t}\n\n\t\t\tPR_ClientUserInfoChanged(key, oldval, InfoBuf_ValueForKey(&host_client->userinfo, key));\n\t\t}\n\t}\n\n#ifdef VM_Q1\n\tQ1QVM_UserInfoChanged(sv_player, true);\n#endif\n\n\tZ_Free(key);\n\tZ_Free(val);\n}\n\n/*\n==================\nSV_ShowServerinfo_f\n\nDumps the serverinfo info string\n==================\n*/\nvoid SV_ShowServerinfo_f (void)\n{\n\tSV_BeginRedirect(RD_CLIENT, host_client->language);\n\tInfoBuf_Print (&svs.info, \"\");\n\tSV_EndRedirect();\n}\n\nvoid SV_NoSnap_f(void)\n{\n\tSV_LogPlayer(host_client, \"refused snap\");\n\n\tif (*host_client->uploadfn)\n\t{\n\t\t*host_client->uploadfn = 0;\n\t\tSV_BroadcastTPrintf (PRINT_HIGH, \"%s refused remote screenshot\\n\", host_client->name);\n\t}\n}\n\n//3 votes per player.\ntypedef struct voteinfo_s {\n\tstruct voteinfo_s *next;\n\tfloat timeout;\n\tint clientid;\n\tchar command[1];\n} voteinfo_t;\nvoteinfo_t *voteinfo;\n\n\nvoid VoteAdd (char *cmd, int id)\n{\n\tvoteinfo_t *vote;\n\tvote = Z_Malloc(sizeof(voteinfo_t)+strlen(cmd));\t//null term is part of voteinfo_t\n\tstrcpy(vote->command, cmd);\n\tvote->clientid = id;\n\tvote->timeout = realtime+votetime.value*60;\n\tvote->next = voteinfo;\n\tvoteinfo = vote;\n}\n\nvoid VoteRemoveCommands(char *command, int id)\t//all of one command\n{\n\tvoteinfo_t *vote, *prev;\n\tprev = NULL;\n\tfor (vote = voteinfo; vote; vote = vote->next)\n\t{\n\t\tif ((!command || !strcmp(vote->command, command)) && (vote->clientid == id || id == -1))\n\t\t{\n\t\t\tif (prev)\n\t\t\t\tprev->next = vote->next;\n\t\t\telse\n\t\t\t\tvoteinfo = vote->next;\n\n\t\t\tZ_Free(vote);\n\t\t\tVoteRemoveCommands(command, id);\n\t\t\treturn;\n\t\t}\n\t\telse\n\t\t\tprev = vote;\n\t}\n}\nvoid VoteFlushAll(void)\n{\n\tVoteRemoveCommands(NULL, -1);\n}\n\nint VoteCount(char *command, int id)\t//all of one command\n{\n\tvoteinfo_t *vote;\n\tint num=0;\n\tfor (vote = voteinfo; vote; vote = vote->next)\n\t{\n\t\tif (!command || !strcmp(vote->command, command))\n\t\t{\n\t\t\tif (vote->clientid == id || id == -1)\n\t\t\t\tnum++;\n\t\t}\n\t}\n\n\treturn num;\n}\n\nvoid VoteCheckTimes(void)\n{\n\tvoteinfo_t *vote, *prev;\n\tprev = NULL;\n\tfor (vote = voteinfo; vote; )\n\t{\n\t\tif (vote->timeout < realtime)\n\t\t{\n\t\t\tif (prev)\n\t\t\t\tprev->next = vote->next;\n\t\t\telse\n\t\t\t\tvoteinfo = vote->next;\n\n\t\t\tZ_Free(vote);\n\n\t\t\tif (prev)\n\t\t\t\tvote = prev;\n\t\t\telse\n\t\t\t\tvote = voteinfo;\n\t\t}\n\t\telse\n\t\t\tprev = vote;\n\n\t\tvote = vote->next;\n\t}\n}\n\nvoid SV_Vote_f (void)\n{\n\tchar *command = Cmd_Args();\n\tchar *base;\n\tint id = host_client->userid;\n\tint num;\n\tint totalusers = 0;\n\tqboolean passes;\n\n\tif (!votelevel.value || ((host_client->penalties & (BAN_MUTE|BAN_DEAF)) == (BAN_MUTE|BAN_DEAF)))\n\t{\n\t\tSV_ClientTPrintf(host_client, PRINT_HIGH, \"Voting is dissallowed on this server\\n\");\n\t\treturn;\n\t}\n\tif (!*command)\n\t{\n\t\tchar cmds[900];\n\t\tCmd_EnumerateLevel(votelevel.value, cmds, sizeof(cmds));\n\t\tSV_ClientTPrintf(host_client, PRINT_HIGH, \"Allowed commands:\\n%s\\n\", cmds);\n\t\treturn;\n\t}\n\tif (host_client->penalties & BAN_MUTE)\n\t{\n\t\t//pretend to vote for it\n\t\tif (host_client->penalties & BAN_STEALTH)\n\t\t\tSV_ClientTPrintf(host_client, PRINT_HIGH, \"%s casts a vote for '%s'\\n\", host_client->name, command);\n\t\telse\n\t\t\tSV_ClientTPrintf(host_client, PRINT_HIGH, \"Sorry, you cannot vote when muted as it may allow you to send a message.\\n\");\n\t\treturn;\n\t}\n\n\tCmd_ExecLevel = votelevel.value;\n\tbase = command;\n\twhile(*base>' ')\n\t\tbase++;\n\tif (*base)\n\t\t*base = '\\0';\n\telse\n\t\tbase = NULL;\n\tif (strchr(command, ';') || !strcmp(command, \"if\"))\n\t{\n\t\tSV_ClientTPrintf(host_client, PRINT_HIGH, \"You arn't allowed to vote for that\\n\");\n\t\treturn;\n\t}\n\tnum = Cmd_Level(command);\n\tif (base)\n\t\t*base = ' ';\n\tif (num != Cmd_ExecLevel)\n\t{\n\t\tSV_ClientTPrintf(host_client, PRINT_HIGH, \"You arn't allowed to vote for that\\n\");\n\t\treturn;\n\t}\n\n\n\tVoteCheckTimes();\n\n\tfor (num = 0; num < sv.allocated_client_slots; num++)\n\t\tif (svs.clients[num].state == cs_spawned)\n\t\t\ttotalusers++;\n\n\tif (VoteCount(command, id))\n\t{\n\t\tVoteRemoveCommands(command, id);\n\t\tSV_ClientTPrintf(host_client, PRINT_HIGH, \"Old vote removed.\\n\");\n\t\treturn;\n\t}\n\tif (VoteCount(NULL, id)>=3)\n\t{\n\t\tVoteRemoveCommands(NULL, id);\n\t\tSV_ClientTPrintf(host_client, PRINT_HIGH, \"All votes removed.\\n\");\n\t}\n\n\tnum = VoteCount(command, -1)+1;\n\n\tpasses = true;\n\tif (votepercent.value < 0 && (float)(totalusers-num) >= 0.5*totalusers)\n\t\tpasses = false;\n\tif (votepercent.value >= 0 && num <= totalusers*votepercent.value/100)\n\t\tpasses = false;\n\tif (num < voteminimum.value)\n\t\tpasses = false;\n\n\tif (passes)\t//>min number of votes, and meets the percent required\n\t{\n\t\tSV_BroadcastTPrintf(PRINT_HIGH, \"%s casts final vote for '%s'\\n\", host_client->name, command);\n\n\t\tVoteRemoveCommands(command, -1);\n\t\tCbuf_AddText(command, votelevel.value);\n\t\tCbuf_AddText(\"\\n\", votelevel.value);\n\t\t//Cmd_ExecuteString (command, votelevel.value);\n\t\treturn;\n\t}\n\telse\t//otherwise, try later.\n\t{\n\t\tSV_BroadcastTPrintf(PRINT_HIGH, \"%s casts a vote for '%s'\\n\", host_client->name, command);\n\n\t\tVoteAdd(command, id);\n\t}\n}\n\nvoid Cmd_Notarget_f (void)\n{\n\tif (!SV_MayCheat())\n\t{\n\t\tSV_PrintToClient(host_client, PRINT_HIGH, \"Cheats are not allowed on this server\\n\");\n\t\treturn;\n\t}\n\n\tif (svs.gametype != GT_PROGS)\n\t\treturn;\n\n\tSV_LogPlayer(host_client, \"notarget cheat\");\n\tif ((int) (sv_player->v->flags = (int) sv_player->v->flags ^ FL_NOTARGET) & FL_NOTARGET)\n\t\tSV_ClientPrintf (host_client, PRINT_HIGH, \"notarget ON\\n\");\n\telse\n\t\tSV_ClientPrintf (host_client, PRINT_HIGH, \"notarget OFF\\n\");\n}\n\n//Sets client to godmode\nvoid Cmd_God_f (void)\n{\n\tif (!SV_MayCheat())\n\t{\n\t\tSV_TPrintToClient(host_client, PRINT_HIGH, \"Cheats are not allowed on this server\\n\");\n\t\treturn;\n\t}\n\n\tif (svs.gametype != GT_PROGS)\n\t\treturn;\n\n\tSV_LogPlayer(host_client, \"god cheat\");\n\tif ((int) (sv_player->v->flags = (int) sv_player->v->flags ^ FL_GODMODE) & FL_GODMODE)\n\t\tSV_ClientTPrintf (host_client, PRINT_HIGH, \"godmode ON\\n\");\n\telse\n\t\tSV_ClientTPrintf (host_client, PRINT_HIGH, \"godmode OFF\\n\");\n}\n\n\nvoid Cmd_Give_f (void)\n{\n#ifdef HLSERVER\n\tif (svs.gametype == GT_HALFLIFE)\n\t{\n\t\tHLSV_ClientCommand(host_client);\n\t\treturn;\n\t}\n#endif\n\n\tif (!SV_MayCheat())\n\t{\n\t\tSV_TPrintToClient(host_client, PRINT_HIGH, \"Cheats are not allowed on this server\\n\");\n\t\treturn;\n\t}\n\n\tif (!svprogfuncs)\n\t\treturn;\n\n\tSV_LogPlayer(host_client, \"give cheat\");\n#ifdef QUAKESTATS\n\t{\n\t\tchar *t = Cmd_Argv(1);\n\t\tif (strlen(t) == 1 && (Cmd_Argc() == 3 || (*t>='0' && *t <= '9')))\n\t\t{\n\t\t\tint v = atoi (Cmd_Argv(2));\n\t\t\tswitch (t[0])\n\t\t\t{\n\t\t\tcase '2':\n\t\t\tcase '3':\n\t\t\tcase '4':\n\t\t\tcase '5':\n\t\t\tcase '6':\n\t\t\tcase '7':\n\t\t\tcase '8':\n\t\t\tcase '9':\n\t\t\t\tsv_player->v->items = (int) sv_player->v->items | IT_SHOTGUN<< (t[0] - '2');\n\t\t\t\tbreak;\n\n\t\t\tcase 's':\n\t\t\t\tsv_player->v->ammo_shells = v;\n\t\t\t\tbreak;\n\t\t\tcase 'n':\n\t\t\t\tsv_player->v->ammo_nails = v;\n\t\t\t\tbreak;\n\t\t\tcase 'r':\n\t\t\t\tsv_player->v->ammo_rockets = v;\n\t\t\t\tbreak;\n\t\t\tcase 'h':\n\t\t\t\tsv_player->v->health = v;\n\t\t\t\tbreak;\n\t\t\tcase 'c':\n\t\t\t\tsv_player->v->ammo_cells = v;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tSV_TPrintToClient(host_client, PRINT_HIGH, \"give: unknown item\\n\");\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t}\n#endif\n\tif (svprogfuncs->EvaluateDebugString)\n\t{\n\t\tif (developer.value < 2 && host_client->netchan.remote_address.type != NA_LOOPBACK)\t//we don't want clients doing nasty things... like setting movetype 3123\n\t\t{\n\t\t\tSV_TPrintToClient(host_client, PRINT_HIGH, \"'give' debugging command requires developer 2 set on the server before you may use it\\n\");\n\t\t}\n\t\telse\n\t\t{\n\t\t\tint oldself;\n\t\t\toldself = pr_global_struct->self;\n\t\t\tpr_global_struct->self = EDICT_TO_PROG(svprogfuncs, sv_player);\n\t\t\tSV_ClientTPrintf(host_client, PRINT_HIGH, \"Result: %s\\n\", svprogfuncs->EvaluateDebugString(svprogfuncs, Cmd_Args()));\n\t\t\tpr_global_struct->self = oldself;\n\t\t}\n\t}\n}\n\nvoid Cmd_Spiderpig_f(void)\n{\n\tif (!SV_MayCheat())\n\t{\n\t\tSV_TPrintToClient(host_client, PRINT_HIGH, \"Cheats are not allowed on this server\\n\");\n\t\treturn;\n\t}\n\n\tif (!svprogfuncs)\n\t\treturn;\n\n\tSV_LogPlayer(host_client, \"spiderpig cheat\");\n\tif (sv_player->v->movetype != MOVETYPE_WALLWALK)\n\t{\n\t\tsv_player->v->movetype = MOVETYPE_WALLWALK;\n\t\tsv_player->v->solid = SOLID_TRIGGER;\n\t\tSV_ClientTPrintf (host_client, PRINT_HIGH, \"Spider-Pig, Spider-Pig, does whatever a Spider-Pig does...\\n\");\n\t}\n\telse\n\t{\n\t\tsv_player->v->movetype = MOVETYPE_WALK;\n\t\tif (sv_player->v->health > 0)\n\t\t\tsv_player->v->solid = SOLID_SLIDEBOX;\n\t\telse\n\t\t\tsv_player->v->solid = SOLID_NOT;\n\t\tSV_ClientTPrintf (host_client, PRINT_HIGH, \"Spider-Pig, Spider-Pig!\\n\");\n\t}\n}\nvoid Cmd_Noclip_f (void)\n{\n#ifdef HLSERVER\n\tif (svs.gametype == GT_HALFLIFE)\n\t{\n\t\tHLSV_ClientCommand(host_client);\n\t\treturn;\n\t}\n#endif\n\n\tif (!SV_MayCheat())\n\t{\n\t\tSV_TPrintToClient(host_client, PRINT_HIGH, \"Cheats are not allowed on this server\\n\");\n\t\treturn;\n\t}\n\n\tif (!svprogfuncs)\n\t\treturn;\n\n\tSV_LogPlayer(host_client, \"noclip cheat\");\n\tif (sv_player->v->movetype != MOVETYPE_NOCLIP)\n\t{\n\t\tsv_player->v->movetype = MOVETYPE_NOCLIP;\n\t\tsv_player->v->solid = SOLID_TRIGGER;\n\t\tSV_ClientTPrintf (host_client, PRINT_HIGH, \"noclip ON\\n\");\n\t}\n\telse\n\t{\n\t\tsv_player->v->movetype = MOVETYPE_WALK;\n\t\tif (sv_player->v->health > 0)\n\t\t\tsv_player->v->solid = SOLID_SLIDEBOX;\n\t\telse\n\t\t\tsv_player->v->solid = SOLID_NOT;\n\t\tSV_ClientTPrintf (host_client, PRINT_HIGH, \"noclip OFF\\n\");\n\t}\n}\n\nvoid Cmd_6dof_f (void)\n{\n\tif (!SV_MayCheat())\n\t{\n\t\tSV_TPrintToClient(host_client, PRINT_HIGH, \"Cheats are not allowed on this server\\n\");\n\t\treturn;\n\t}\n\n\tif (!svprogfuncs)\n\t\treturn;\n\n\tSV_LogPlayer(host_client, \"6dof cheat\");\n\tif (sv_player->v->movetype != MOVETYPE_6DOF)\n\t{\n\t\tsv_player->v->movetype = MOVETYPE_6DOF;\n\t\tSV_ClientTPrintf (host_client, PRINT_HIGH, \"6dof mode ON\\n\");\n\t}\n\telse\n\t{\n\t\tsv_player->v->movetype = MOVETYPE_WALK;\n\t\tif (sv_player->v->health > 0)\n\t\t\tsv_player->v->solid = SOLID_SLIDEBOX;\n\t\telse\n\t\t\tsv_player->v->solid = SOLID_NOT;\n\t\tSV_ClientTPrintf (host_client, PRINT_HIGH, \"6dof mode OFF\\n\");\n\t}\n}\n\nvoid Cmd_Fly_f (void)\n{\n\tif (!SV_MayCheat())\n\t{\n\t\tSV_TPrintToClient(host_client, PRINT_HIGH, \"Cheats are not allowed on this server\\n\");\n\t\treturn;\n\t}\n\n\tif (!svprogfuncs)\n\t\treturn;\n\n\tSV_LogPlayer(host_client, \"fly cheat\");\n\tif (sv_player->v->movetype != MOVETYPE_FLY)\n\t{\n\t\tsv_player->v->movetype = MOVETYPE_FLY;\n\t\tSV_ClientTPrintf (host_client, PRINT_HIGH, \"flymode ON\\n\");\n\t}\n\telse\n\t{\n\t\tsv_player->v->movetype = MOVETYPE_WALK;\n\t\tif (sv_player->v->health > 0)\n\t\t\tsv_player->v->solid = SOLID_SLIDEBOX;\n\t\telse\n\t\t\tsv_player->v->solid = SOLID_NOT;\n\t\tSV_ClientTPrintf (host_client, PRINT_HIGH, \"flymode OFF\\n\");\n\t}\n}\n\n#if defined(_DEBUG) && defined(SUBSERVERS)\nvoid Cmd_SSV_Transfer_f(void)\n{\n\tchar *dest = Cmd_Argv(1);\n\tif (!SV_MayCheat())\n\t{\n\t\tSV_TPrintToClient(host_client, PRINT_HIGH, \"Cheats are not allowed on this server\\n\");\n\t\treturn;\n\t}\n\n\tSSV_InitiatePlayerTransfer(host_client, dest);\n}\n\nvoid Cmd_SSV_AllSay_f(void)\n{\n\tchar *text = Cmd_Args();\n\tif (!SV_MayCheat())\n\t{\n\t\tSV_TPrintToClient(host_client, PRINT_HIGH, \"Cheats are not allowed on this server\\n\");\n\t\treturn;\n\t}\n\n\tSSV_Send(\"\", host_client->name, \"say\", text);\n}\n\nvoid Cmd_SSV_Join_f(void)\n{\n\tint i;\n\tchar *who = Cmd_Argv(1);\n\tif (!SV_MayCheat())\n\t{\n\t\tSV_TPrintToClient(host_client, PRINT_HIGH, \"Cheats are not allowed on this server\\n\");\n\t\treturn;\n\t}\n\n\tfor (i = 0; i < sv.allocated_client_slots; i++)\n\t{\n\t\tif (!strcmp(who, svs.clients[i].name))\n\t\t{\n//\t\t\tVectorCopy(svs.clients[i].edict->v->origin, sv_player->v->oldorigin);\n\t\t\tVectorCopy(svs.clients[i].edict->v->origin, sv_player->v->origin);\n\t\t\tWorld_LinkEdict (&sv.world, (wedict_t*)sv_player, false);\n\n\t\t\tsv_player->xv->dimension_hit\t= (int)sv_player->xv->dimension_hit & ~128;\n\t\t\tsv_player->xv->dimension_solid\t= (int)sv_player->xv->dimension_solid & 128;\n\t\t\tsvs.clients[i].edict->xv->dimension_hit\t\t= (int)svs.clients[i].edict->xv->dimension_hit & ~128;\n\t\t\tsvs.clients[i].edict->xv->dimension_solid\t= (int)svs.clients[i].edict->xv->dimension_solid & 128;\n\t\t\treturn;\n\t\t}\n\t}\n\tSSV_Send(who, host_client->name, \"join\", \"\");\n}\n#endif\n\n/*\n====================\nHost_SetPos_f  UDC\nBy Alex Shadowalker (and added to fte because he kept winging)\n====================\n*/\nvoid Cmd_SetPos_f(void)\n{\n\tif (!SV_MayCheat())\n\t{\n\t\tSV_TPrintToClient(host_client, PRINT_HIGH, \"Cheats are not allowed on this server\\n\");\n\t\treturn;\n\t}\n\n\tif (!svprogfuncs)\n\t\treturn;\n\n\tif (Cmd_Argc() != 4 && Cmd_Argc() != 7)\n\t{\n\t\tSV_ClientPrintf(host_client, PRINT_HIGH, \"setpos %f %f %f %f %f %f\\n\", sv_player->v->origin[0], sv_player->v->origin[1], sv_player->v->origin[2], sv_player->v->v_angle[0], sv_player->v->v_angle[1], sv_player->v->v_angle[2]);\n\t\treturn;\n\t}\n\tSV_LogPlayer(host_client, \"setpos cheat\");\n\tif (sv_player->v->movetype != MOVETYPE_NOCLIP)\n\t{\n\t\tsv_player->v->movetype = MOVETYPE_NOCLIP;\n\t\tSV_TPrintToClient(host_client, PRINT_HIGH, \"noclip on\\n\");\n\t}\n\n\t//make sure they're not going to whizz away from it\n\tVectorClear(sv_player->v->velocity);\n\n\tsv_player->v->origin[0] = atof(Cmd_Argv(1));\n\tsv_player->v->origin[1] = atof(Cmd_Argv(2));\n\tsv_player->v->origin[2] = atof(Cmd_Argv(3));\n\tWorld_LinkEdict (&sv.world, (wedict_t*)sv_player, false);\n\n\tif (Cmd_Argc() > 4)\n\t{\n\t\tsv_player->v->angles[0] = atof(Cmd_Argv(4));\n\t\tsv_player->v->angles[1] = atof(Cmd_Argv(5));\n\t\tsv_player->v->angles[2] = atof(Cmd_Argv(6));\n\t\tsv_player->v->fixangle = FIXANGLE_FIXED;\n\t}\n}\n\nvoid SV_SetUpClientEdict (client_t *cl, edict_t *ent)\n{\n#ifdef VM_Q1\n\tif (svs.gametype == GT_Q1QVM)\n\t{\n\t\tstring_t preserve;\n\t\tpreserve = ent->v->netname;\n\t\tif (progstype != PROG_NQ)\t//allow frikbots to work in NQ mods (but not qw!)\n\t\t\tED_Clear(svprogfuncs, ent);\n\t\tent->v->netname = preserve;\n\t}\n\telse\n#endif\n\t{\n\t\tif (progstype != PROG_NQ)\t//allow frikbots to work in NQ mods (but not qw!)\n\t\t\tED_Clear(svprogfuncs, ent);\n\t\tsvprogfuncs->SetStringField(svprogfuncs, ent, &ent->v->netname, cl->name, true);\n\t}\n\tED_Spawned(ent, false);\n\tent->ereftype = ER_ENTITY;\n\n\tent->v->colormap = NUM_FOR_EDICT(svprogfuncs, ent);\n\n#ifdef HAVE_LEGACY\n\t{\n\t\tif (pr_teamfield)\n\t\t\t((string_t *)ent->v)[pr_teamfield] = (string_t)(cl->team-svprogfuncs->stringtable);\n\t}\n\n\t{\n\t\tint tc = atoi(InfoBuf_ValueForKey(&cl->userinfo, \"topcolor\"));\n\t\tint bc = atoi(InfoBuf_ValueForKey(&cl->userinfo, \"bottomcolor\"));\n\t\tif (tc < 0 || tc > 13)\n\t\t\ttc = 0;\n\t\tif (bc < 0 || bc > 13)\n\t\t\tbc = 0;\n\t\tent->xv->clientcolors = 16*tc + bc;\n\t}\n\n\tcl->dp_ping = NULL;\n\tcl->dp_pl = NULL;\n\tif (progstype == PROG_NQ)\n\t{\n\t\tcl->dp_ping = (float*)sv.world.progs->GetEdictFieldValue(sv.world.progs, ent, \"ping\", ev_float, NULL);\n\t\tcl->dp_pl = (float*)sv.world.progs->GetEdictFieldValue(sv.world.progs, ent, \"ping_packetloss\", ev_float, NULL);\n\t}\n#endif\n\n\n\tent->xv->gravity = cl->entgravity = 1.0;\n\tent->xv->maxspeed = cl->maxspeed = sv_maxspeed.value;\n\tent->v->movetype = MOVETYPE_NOCLIP;\n\n\tent->v->frags = 0;\n}\n\n//dynamically add/remove a splitscreen client\nstatic void Cmd_AddSeat_f(void)\n{\n\tclient_t *cl, *prev;\n\tqboolean changed = false;\n\t//don't allow an altseat to add or remove. that's not how this works.\n\tif (host_client->controller)\n\t\treturn;\n\n\tif (host_client->state != cs_spawned)\n\t\treturn;\n\n\tif (!(host_client->fteprotocolextensions & PEXT_SPLITSCREEN))\n\t\treturn;\n\n\tif (Cmd_Argc()>1)\n\t{\n\t\tint num = atoi(Cmd_Argv(1));\n\t\tint count;\n\n\t\tif (num<=0 || host_client->joinobservelockeduntil > realtime)\n\t\t\treturn;\n\t\tif (host_client->netchan.remote_address.type != NA_LOOPBACK)\n\t\t\thost_client->joinobservelockeduntil = realtime + 2;\n\n\t\tfor (count = 1, prev = host_client, cl = host_client->controlled; cl; cl = cl->controlled)\n\t\t{\n\t\t\tif (count >= num)\n\t\t\t{\n\t\t\t\tfor(; cl; cl = prev->controlled)\n\t\t\t\t{\n\t\t\t\t\t//unlink it\n\t\t\t\t\tprev->controlled = cl->controlled;\n\t\t\t\t\tcl->controller = NULL;\n\t\t\t\t\tcl->controlled = NULL;\n\n\t\t\t\t\t//make it into a pseudo-bot\n\t\t\t\t\tcl->netchan.remote_address.type = NA_INVALID;\t//so the remaining client doesn't get the kick too.\n\t\t\t\t\tcl->protocol = SCP_BAD;\t//make it a bit like a bot, so we don't try sending any datagrams/reliables at someone that isn't able to receive anything.\n\n\t\t\t\t\t//okay, it can get lost now.\n\t\t\t\t\tcl->drop = true;\n\t\t\t\t}\n\t\t\t\thost_client->joinobservelockeduntil = realtime + 3;\n\t\t\t\tchanged = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tprev = cl;\n\t\t\tcount++;\n\t\t}\n\n\t\tif (!changed && count+1 == num && Cmd_Argc()>2)\n\t\t\tchanged = !!SV_AddSplit(host_client, Cmd_Argv(2), num-1);\n\t}\n\telse\n\t{\n\t\tcl = NULL;\n/*\t\tif (host_client->joinobservelockeduntil > realtime)\n\t\t{\n\t\t\tSV_TPrintToClient(host_client, PRINT_HIGH, va(\"Please wait %.1g more seconds\\n\", host_client->joinobservelockeduntil-realtime));\n\t\t\treturn;\n\t\t}\n\t\thost_client->joinobservelockeduntil = realtime + 2;\n\n\t\tcl = SV_AddSplit(host_client, host_client->userinfo, 0);\n*/\n\t}\n\n\tif (cl || changed)\n\t\tSV_UpdateSeats(host_client);\n}\n\n/*\n==================\nCmd_Join_f\n\nSet client to player mode without reconnecting\n==================\n*/\nvoid Cmd_Join_f (void)\n{\n\tint\t\ti;\n\tclient_t\t*cl;\n\tint\t\tnumclients;\n\textern cvar_t\tmaxclients;\n\tint seats;\n\tqboolean wasspawned;\n\n\tif (host_client->controller)\n\t{\n\t\thost_client = host_client->controller;\n\t\tsv_player = host_client->edict;\n\t}\n\n\tif (host_client->state != cs_spawned)\n\t\treturn;\n\n\tif (svs.gametype != GT_PROGS && svs.gametype != GT_Q1QVM)\n\t{\n\t\tSV_TPrintToClient(host_client, PRINT_HIGH, \"Sorry, not implemented in this gamecode type. Try moaning at the dev team\\n\");\n\t\treturn;\n\t}\n\n\tif (!ISNQCLIENT(host_client) && !(host_client->zquake_extensions & Z_EXT_JOIN_OBSERVE))\n\t{\n\t\tSV_TPrintToClient(host_client, PRINT_HIGH, \"Your game client doesn't support this command\\n\");\n\t\treturn;\n\t}\n\n\tif (!host_client->spectator)\n\t{\n\t\tSV_ClientTPrintf(host_client, PRINT_HIGH, \"You are not currently spectating.\\n\");\n\t\treturn;\n\t}\n\tif (host_client->joinobservelockeduntil > realtime)\n\t{\n\t\tSV_TPrintToClient(host_client, PRINT_HIGH, va(\"Please wait %.1g more seconds\\n\", host_client->joinobservelockeduntil-realtime));\n\t\treturn;\n\t}\n\thost_client->joinobservelockeduntil = realtime + 2;\n\n\tif (password.string[0] && stricmp(password.string, \"none\"))\n\t{\n\t\tSV_ClientTPrintf(host_client, PRINT_HIGH, \"This server requires a %s password. Please disconnect, set the password and reconnect as %s.\\n\", \"player\", \"player\");\n\t\treturn;\n\t}\n\n\tif (host_client->penalties & BAN_SPECONLY)\n\t{\n\t\tSV_ClientTPrintf(host_client, PRINT_HIGH, \"You are banned from joining the game.\\n\");\n\t\treturn;\n\t}\n\n\t// count players already on server\n\tnumclients = 0;\n\tseats = 0;\n\tfor (i=0,cl=svs.clients ; i<sv.allocated_client_slots ; i++,cl++)\n\t{\n\t\tif (cl->state != cs_free && !cl->spectator)\n\t\t\tnumclients++;\n\t\tif ((cl == host_client || cl->controller == host_client) && cl->spectator)\n\t\t\tseats++;\n\t}\n\tif (numclients+seats > maxclients.value)\n\t{\n\t\tSV_TPrintToClient(host_client, PRINT_HIGH, \"Can't join, all player slots full\\n\");\n\t\treturn;\n\t}\n\n\tif (ISNQCLIENT(host_client))\n\t{\t//make sure the nq client is viewing from its own player entity again\n\t\tClientReliableWrite_Begin(host_client, svc_setview, 4);\n\t\tClientReliableWrite_Entity(host_client, host_client - svs.clients + 1);\n\t}\n\n\tfor (; host_client; host_client = host_client->controlled)\n\t{\n\t\tsv_player = host_client->edict;\n\t\tif (!host_client->spectator)\n\t\t\tcontinue;\n\n\t\twasspawned = host_client->spawned;\n\t\tSV_DespawnClient(host_client);\n\n\t\t// turn the spectator into a player\n\t\thost_client->spectator = false;\n\t\tInfoBuf_RemoveKey (&host_client->userinfo, \"*spectator\");\n\t\tif (!wasspawned)\n\t\t\tcontinue;\n\t\t//need to respawn them now.\n\t\tSV_SetUpClientEdict (host_client, host_client->edict);\n\n\t\t// FIXME, bump the client's userid?\n\n\t\t// call the progs to get default spawn parms for the new client\n#ifdef VM_Q1\n\t\tif (svs.gametype == GT_Q1QVM)\n\t\t\tQ1QVM_SetNewParms();\n\t\telse\n#endif\n\t\t\tif (pr_global_ptrs->SetNewParms)\n\t\t\tPR_ExecuteProgram (svprogfuncs, pr_global_struct->SetNewParms);\n\n\t\tSV_SpawnParmsToClient(host_client);\n\n#ifdef VM_Q1\n\t\tif (svs.gametype == GT_Q1QVM)\n\t\t\tQ1QVM_ClientConnect(host_client);\n\t\telse\n#endif\n\t\t{\n\t\t\t// call the spawn function\n\t\t\tpr_global_struct->time = sv.world.physicstime;\n\t\t\tpr_global_struct->self = EDICT_TO_PROG(svprogfuncs, sv_player);\n\t\t\tPR_ExecuteProgram (svprogfuncs, pr_global_struct->ClientConnect);\n\n\t\t\t// actually spawn the player\n\t\t\tpr_global_struct->time = sv.world.physicstime;\n\t\t\tpr_global_struct->self = EDICT_TO_PROG(svprogfuncs, sv_player);\n\t\t\tPR_ExecuteProgram (svprogfuncs, pr_global_struct->PutClientInServer);\n\t\t}\n\t\thost_client->spawned = true;\n\t\tsv.spawned_client_slots++;\n\n\t\t// send notification to all clients\n\t\thost_client->old_frags = host_client->edict->v->frags;\n\t\thost_client->sendinfo = true;\n\n\t\tSV_LogPlayer(host_client, \"joined\");\n\t}\n}\n\n\n/*\n==================\nCmd_Observe_f\n\nSet client to spectator mode without reconnecting\n==================\n*/\nvoid Cmd_Observe_f (void)\n{\n\tint\t\ti;\n\tclient_t\t*cl;\n\tint\t\tnumspectators;\n\textern cvar_t\tmaxspectators, spectator_password;\n\tint seats;\n\tqboolean wasspawned;\n\n\tif (host_client->controller)\n\t{\n\t\thost_client = host_client->controller;\n\t\tsv_player = host_client->edict;\n\t}\n\n\tif (host_client->state != cs_spawned)\n\t\treturn;\n\n\tif (svs.gametype != GT_PROGS && svs.gametype != GT_Q1QVM)\n\t{\n\t\tSV_TPrintToClient(host_client, PRINT_HIGH, \"Sorry, not implemented in this gamecode type. Try moaning at the dev team\\n\");\n\t\treturn;\n\t}\n\n\tif (!ISNQCLIENT(host_client) && !(host_client->zquake_extensions & Z_EXT_JOIN_OBSERVE))\n\t{\n\t\tSV_TPrintToClient(host_client, PRINT_HIGH, \"Your game client doesn't support this command\\n\");\n\t\treturn;\n\t}\n\n\tif (host_client->spectator)\n\t{\n\t\tSV_ClientTPrintf(host_client, PRINT_HIGH, \"You are already spectating.\\n\");\n\t\treturn;\n\t}\n\tif (host_client->joinobservelockeduntil > realtime)\n\t{\n\t\tSV_TPrintToClient(host_client, PRINT_HIGH, va(\"Please wait %.1g more seconds\\n\", host_client->joinobservelockeduntil-realtime));\n\t\treturn;\n\t}\n\thost_client->joinobservelockeduntil = realtime + 2;\n\n\tif (spectator_password.string[0] && stricmp(spectator_password.string, \"none\"))\n\t{\n\t\tSV_ClientTPrintf(host_client, PRINT_HIGH, \"This server requires a %s password. Please disconnect, set the password and reconnect as %s.\\n\", \"spectator\", \"spectator\");\n\t\treturn;\n\t}\n\n\t// count spectators already on server\n\tnumspectators = 0;\n\tseats = 0;\n\tfor (i=0,cl=svs.clients ; i<sv.allocated_client_slots ; i++,cl++)\n\t{\n\t\tif (cl->state != cs_free && cl->spectator)\n\t\t\tnumspectators++;\n\t\tif ((cl == host_client || cl->controller == host_client) && !cl->spectator)\n\t\t\tseats++;\n\t}\n\tif (numspectators+seats > maxspectators.value)\n\t{\n\t\tSV_TPrintToClient(host_client, PRINT_HIGH, \"Can't join, all spectator slots full\\n\");\n\t\treturn;\n\t}\n\n\tfor (; host_client; host_client = host_client->controlled)\n\t{\n\t\tsv_player = host_client->edict;\n\t\tif (host_client->spectator)\n\t\t\tcontinue;\n\n\t\twasspawned = host_client->spawned;\n\t\tSV_DespawnClient(host_client);\n\n\t\t// turn the player into a spectator\n\t\thost_client->spectator = true;\n\t\tInfoBuf_SetValueForStarKey (&host_client->userinfo, \"*spectator\", \"1\");\n\t\tif (!wasspawned)\n\t\t\tcontinue;\n\t\t//need to respawn them now.\n\t\tSV_SetUpClientEdict (host_client, host_client->edict);\n\n\t\t// FIXME, bump the client's userid?\n\n\t\t// call the progs to get default spawn parms for the new client\n#ifdef VM_Q1\n\t\tif (svs.gametype == GT_Q1QVM)\n\t\t\tQ1QVM_SetNewParms();\n\t\telse\n#endif\n\t\t\tif (pr_global_ptrs->SetNewParms)\n\t\t\tPR_ExecuteProgram (svprogfuncs, pr_global_struct->SetNewParms);\n\n\t\tSV_SpawnParmsToClient(host_client);\n\t\tSV_SpawnSpectator ();\n\n\t\t// call the spawn function\n#ifdef VM_Q1\n\t\tif (svs.gametype == GT_Q1QVM)\n\t\t\tQ1QVM_ClientConnect(host_client);\n\t\telse\n#endif\n\t\t{\n\t\t\tif (SpectatorConnect)\n\t\t\t{\n\t\t\t\tpr_global_struct->time = sv.world.physicstime;\n\t\t\t\tpr_global_struct->self = EDICT_TO_PROG(svprogfuncs, sv_player);\n\t\t\t\tPR_ExecuteProgram (svprogfuncs, SpectatorConnect);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tsv_player->v->movetype = MOVETYPE_NOCLIP;\n\t\t\t\tsv_player->v->model = 0;\n\t\t\t\tsv_player->v->modelindex = 0;\n\t\t\t}\n\t\t}\n\t\thost_client->spawned = true;\n\t\tsv.spawned_observer_slots++;\n\n\t\t// send notification to all clients\n\t\thost_client->old_frags = host_client->edict->v->frags;\n\t\thost_client->sendinfo = true;\n\n\t\tSV_LogPlayer(host_client, \"observing\");\n\t}\n}\n\nvoid SV_CalcNetRates(client_t *cl, double *ftime, int *frames, double *minf, double *maxf)\n{\n\tint f;\n\tint fmsec;\n\t*minf = 1000;\n\t*maxf = 0;\n\t*ftime = 0;\n\t*frames = 0;\n\n\tif (ISQWCLIENT(cl) || ISNQCLIENT(cl))\n\t{\n\t\tif (cl->frameunion.frames)\n\t\t{\n\t\t\tfor (f = 0; f < UPDATE_BACKUP; f++)\n\t\t\t{\n\t\t\t\tif (cl->frameunion.frames[f].move_msecs >= 0)\n\t\t\t\t{\n\t\t\t\t\tif (!cl->frameunion.frames[f].move_msecs)\n\t\t\t\t\t{\n\t\t\t\t\t\tfmsec = 1001;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tfmsec = 1000.0f/cl->frameunion.frames[f].move_msecs;\n\t\t\t\t\t}\n\t\t\t\t\t*ftime += fmsec;\n\t\t\t\t\tif (*minf > fmsec)\n\t\t\t\t\t\t*minf = fmsec;\n\t\t\t\t\tif (*maxf < fmsec)\n\t\t\t\t\t\t*maxf = fmsec;\n\t\t\t\t\t*frames+=1;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nstatic void Cmd_FPSList_f(void)\n{\n\tclient_t *cl;\n\tint c;\n\tdouble minf, maxf;\n\tdouble ftime;\n\tint frames;\n\tchar *protoname;\n\n\n\tfor (c = 0; c < sv.allocated_client_slots; c++)\n\t{\n\t\tcl = &svs.clients[c];\n\t\tif (!cl->state)\n\t\t\tcontinue;\n\n\t\tSV_CalcNetRates(cl, &ftime, &frames, &minf, &maxf);\n\n\t\tsafeswitch(cl->protocol)\n\t\t{\n\t\tcase SCP_QUAKEWORLD: protoname = (cl->fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS)?\"fteqw\":(cl->fteprotocolextensions||cl->fteprotocolextensions2?\"qw\":\"qwid\"); break;\n\t\tcase SCP_QUAKE2: protoname = \"q2\"; break;\n\t\tcase SCP_QUAKE2EX: protoname = \"q2\"; break;\n\t\tcase SCP_QUAKE3: protoname = \"q3\"; break;\n\t\tcase SCP_NETQUAKE: protoname = (cl->fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS)?\"ftenq\":\"nq\"; break;\n\t\tcase SCP_BJP3: protoname = (cl->fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS)?\"ftenq\":\"bjp3\"; break;\n\t\tcase SCP_FITZ666: protoname = (cl->fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS)?\"ftenq\":\"fitz\"; break;\n\t\tcase SCP_DARKPLACES6: protoname = \"dpp6\"; break;\n\t\tcase SCP_DARKPLACES7: protoname = \"dpp7\"; break;\n\t\tcase SCP_BAD: protoname = \"bot\"; break;\n\t\tsafedefault: protoname = \"?\"; break;\n\t\t}\n\n\t\tif (frames)\n\t\t\tSV_ClientPrintf(host_client, PRINT_HIGH, \"%s: %gfps (%g - %g), c2s: %ibps, s2c: %ibps, ping %ims(-%i), pl %i%% %s\\n\", cl->name, ftime/frames, minf, maxf, (int)cl->inrate, (int)cl->outrate, SV_CalcPing(cl, false), (int)(1000*cl->delay), cl->lossage, protoname);\n\t\telse\n\t\t\tSV_ClientPrintf(host_client, PRINT_HIGH, \"%s: unknown framerate, c2s: %ibps, s2c: %ibps, ping %ims(-%i), pl %i%% %s\\n\", cl->name, (int)cl->inrate, (int)cl->outrate, SV_CalcPing(cl, false), (int)(1000*cl->delay), cl->lossage, protoname);\n\n//\t\tSV_ClientPrintf(host_client, PRINT_HIGH, CON_DEBUG\"%s: %i\\n\", cl->name, cl->netchan.mtu_cur);\n\t}\n}\n\nvoid SV_EnableClientsCSQC(void)\n{\n\tsize_t e;\n\tif (host_client->controller)\n\t\treturn;\n\n\thost_client->csqcactive = true;\n\n\t//if the csqc has just restarted, its probably going to want us to resend all csqc ents from scratch because of all the setup it might do.\n\tif (host_client->pendingcsqcbits)\n\t\tfor (e = 1; e < host_client->max_net_ents; e++)\n\t\t\tif (host_client->pendingcsqcbits[e] & SENDFLAGS_PRESENT)\n\t\t\t\thost_client->pendingcsqcbits[e] |= SENDFLAGS_USABLE;\n}\nvoid SV_DisableClientsCSQC(void)\n{\n#ifdef PEXT_CSQC\n\thost_client->csqcactive = false;\n#endif\n}\n\nvoid SV_UserCmdMVDList_f (void);\n\n#ifdef NQPROT\nstatic void SVNQ_Spawn_f (void)\n{\n\textern cvar_t sv_gravity;\n\tint\t\ti;\n\tclient_t\t*client;\n\tedict_t\t*ent;\n\n\tif (host_client->state != cs_connected)\n\t{\n\t\tCon_Printf (\"spawn not valid -- already spawned\\n\");\n\t\treturn;\n\t}\n\n// send all current names, colors, and frag counts\n\t// FIXME: is this a good thing?\n//\tSZ_Clear (&host_client->netchan.message);\n\n// send current status of all other players\n\n\t// normally this could overflow, but no need to check due to backbuf\n\tfor (i=0, client = svs.clients; i<sv.allocated_client_slots ; i++, client++)\n\t\tSV_FullClientUpdate(client, host_client);\n#ifdef MVD_RECORDING\n\tSV_MVD_FullClientUpdate(NULL, host_client);\n#endif\n\n// send all current light styles\n\tfor (i=0 ; i<sv.maxlightstyles ; i++)\n\t\tSV_SendLightstyle(host_client, NULL, i, true);\n\n\t// set up the edict\n\tent = host_client->edict;\n\n\tif (!ent)\n\t{\n\t}\n\telse if (host_client->istobeloaded)\t//minimal setup\n\t{\n\t\thost_client->entgravity = ent->xv->gravity*sv_gravity.value;\n\t\thost_client->maxspeed = ent->xv->maxspeed;\n\t}\n\telse\n\t\tSV_SetUpClientEdict(host_client, ent);\n\n//\n// force stats to be updated\n//\n\tmemset (host_client->statsi, 0, sizeof(host_client->statsi));\n\tmemset (host_client->statsf, 0, sizeof(host_client->statsf));\n\tmemset (host_client->statss, 0, sizeof(host_client->statss));\n\n\tif (pr_global_ptrs->total_secrets)\n\t{\n\t\tClientReliableWrite_Begin (host_client, svcnq_updatestatlong, 6);\n\t\tClientReliableWrite_Byte (host_client, STAT_TOTALSECRETS);\n\t\tClientReliableWrite_Long (host_client, pr_global_struct->total_secrets);\n\t}\n\tif (pr_global_ptrs->total_monsters)\n\t{\n\t\tClientReliableWrite_Begin (host_client, svcnq_updatestatlong, 6);\n\t\tClientReliableWrite_Byte (host_client, STAT_TOTALMONSTERS);\n\t\tClientReliableWrite_Long (host_client, pr_global_struct->total_monsters);\n\t}\n\tif (pr_global_ptrs->found_secrets)\n\t{\n\t\tClientReliableWrite_Begin (host_client, svcnq_updatestatlong, 6);\n\t\tClientReliableWrite_Byte (host_client, STAT_SECRETS);\n\t\tClientReliableWrite_Long (host_client, pr_global_struct->found_secrets);\n\t}\n\tif (pr_global_ptrs->killed_monsters)\n\t{\n\t\tClientReliableWrite_Begin (host_client, svcnq_updatestatlong, 6);\n\t\tClientReliableWrite_Byte (host_client, STAT_MONSTERS);\n\t\tClientReliableWrite_Long (host_client, pr_global_struct->killed_monsters);\n\t}\n\n\t//nq servers call ClientConnect early.\n\t//qw servers hold off until the last possible moment.\n\t//so qw servers prevent the player from getting shot too early.\n\t//while nq ensures that reliables sent in ClientConnect are actually flushed before unreliables/entities start to arrive.\n\tSV_Begin_Core(host_client);\n\n\tClientReliableWrite_Begin (host_client, svcnq_signonnum, 2);\n\tClientReliableWrite_Byte (host_client, 3);\n\n\thost_client->send_message = true;\n}\nstatic void SVNQ_Begin_f (void)\n{\n\tunsigned pmodel = 0, emodel = 0;\n\n\tif (host_client->state == cs_spawned)\n\t\treturn; // don't begin again\n\n\thost_client->state = cs_spawned;\n\n\t//while qw spawns player entity in the 'begin' stage, nq spawns in the 'spawn' stage rather than here.\n//\tSV_Begin_Core(host_client);\n\n\t// clear the net statistics, because connecting gives a bogus picture\n\thost_client->netchan.frame_latency = 0;\n\thost_client->netchan.frame_rate = 0;\n\thost_client->netchan.drop_count = 0;\n\n\t//check he's not cheating\n\n\tif (sv_playermodelchecks.value)\n\t{\n\t\tpmodel = atoi(InfoBuf_ValueForKey (&host_client->userinfo, \"pmodel\"));\n\t\temodel = atoi(InfoBuf_ValueForKey (&host_client->userinfo, \"emodel\"));\n\n\t\tif (pmodel != sv.model_player_checksum ||\n\t\t\temodel != sv.eyes_player_checksum)\n\t\t\tSV_BroadcastTPrintf (PRINT_HIGH, \"warning: %s eyes or player model not verified\\n\", host_client->name);\n\t}\n\n//\tMSG_WriteByte (&host_client->netchan.message, svc_signonnum);\n//\tMSG_WriteByte (&host_client->netchan.message, 4);\n\n\n\thost_client->send_message = true;\n\n\n\tSV_PreRunCmd();\n\thost_client->lastcmd.msec = 0;\n\tSV_RunCmd (&host_client->lastcmd, false);\n\tSV_PostRunCmd();\n\thost_client->lastruncmd = sv.time*1000;\n\n#ifdef MVD_RECORDING\n\tSV_MVD_AutoRecord();\n#endif\n}\nstatic void SVNQ_PreSpawn_f (void)\n{\n\tif (host_client->prespawn_stage < PRESPAWN_MAPCHECK)\n\t\tSV_StuffcmdToClient(host_client, va(\"cmd prespawn %s\\n\", Cmd_Args()));\n\telse if (host_client->prespawn_stage == PRESPAWN_MAPCHECK)\n\t{\n\t\thost_client->checksum = ~0u;\n\t\thost_client->prespawn_stage = PRESPAWN_MAPCHECK+1;\n\t\thost_client->prespawn_idx = 0;\n\n\t\tif (sv_mapcheck.value)\n\t\t{\n\t\t\tconst char *prot = \"\";\n\t\t\tswitch(host_client->protocol)\n\t\t\t{\n\t\t\tdefault:\n\t\t\t\tbreak;\n\t\t\tcase SCP_NETQUAKE:\n\t\t\t\tprot = host_client->qex?\" (qe15)\":\" (nq)\";\n\t\t\t\tbreak;\n\t\t\tcase SCP_BJP3:\n\t\t\t\tprot = \" (bjp3)\";\n\t\t\t\tbreak;\n\t\t\tcase SCP_FITZ666:\n\t\t\t\tprot = host_client->qex?\" (qe666)\":\" (fitz)\";\n\t\t\t\tbreak;\n\t\t\tcase SCP_DARKPLACES6:\n\t\t\t\tprot = \" (dpp6)\";\n\t\t\t\tbreak;\n\t\t\tcase SCP_DARKPLACES7:\n\t\t\t\tprot = \" (dpp7)\";\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tCon_DPrintf(\"Warning: %s cannot be enforced on player %s%s.\\n\", sv_mapcheck.name, host_client->name, prot);\t//as you can fake it in a client anyway, this is hardly a significant issue.\n\t\t}\n\t}\n\n\thost_client->send_message = true;\n}\nstatic void SVNQ_NQInfo_f (void)\n{\n\tchar buf[8192];\n\tCmd_TokenizeString(va(\"setinfo \\\"%s\\\" %s\\n\", Cmd_Argv(0), COM_QuotedString(Cmd_Argv(1), buf, sizeof(buf), false)), false, false);\n\tSV_SetInfo_f();\n}\n\nstatic void SVNQ_NQColour_f (void)\n{\n\tchar *val;\n\tunsigned int top;\n\tunsigned int bottom;\n\n\tval = Cmd_Argv(1);\n\tif (!strncmp(val, \"0x\", 2))\n\t\ttop = 0xff000000|strtoul(val+2, NULL, 16);\n\telse\n\t\ttop = atoi(val)&15;\n\n\tif (Cmd_Argc() == 2)\n\t\tbottom = top;\n\telse\n\t{\n\t\tval = Cmd_Argv(2);\n\t\tif (!strncmp(val, \"0x\", 2))\n\t\t\tbottom = 0xff000000|strtoul(val+2, NULL, 16);\n\t\telse\n\t\t\tbottom = atoi(val)&15;\n\t}\n\n\tif (top < 16 && top > 13)\n\t\ttop = 13;\n\tif (bottom < 16 && bottom > 13)\n\t\tbottom = 13;\n\n\tif (progstype != PROG_QW && host_client->edict)\n\t\thost_client->edict->v->team = bottom + 1;\n\n\tif (top < 16)\n\t\tval = va(\"%i\", top);\n\telse\n\t\tval = va(\"%#x\", top&0xffffff);\n\tif (InfoBuf_SetValueForKey(&host_client->userinfo, \"topcolor\", val))\n\t\tSV_BroadcastUserinfoChange(host_client, true, \"topcolor\", NULL);\n\n\tif (top < 16)\n\t\tval = va(\"%i\", bottom);\n\telse\n\t\tval = va(\"%#x\", bottom&0xffffff);\n\tif (InfoBuf_SetValueForKey(&host_client->userinfo, \"bottomcolor\", val))\n\t\tSV_BroadcastUserinfoChange(host_client, true, \"bottomcolor\", NULL);\n\n\tswitch(bottom)\n\t{\n\tcase 4:\n\t\tval = \"red\";\n\t\tbreak;\n\tcase 14:\n\t\tval = \"blue\";\n\t\tbreak;\n\tdefault:\n\t\tval = va(\"t%i\", bottom+1);\n\t\tbreak;\n\t}\n\tif (strcmp(val, InfoBuf_ValueForKey(&host_client->userinfo, \"team\")))\n\t{\n\t\tInfoBuf_SetValueForKey(&host_client->userinfo, \"team\", val);\n\t\tSV_BroadcastUserinfoChange(host_client, true, \"team\", NULL);\n\t}\n\n\tSV_ExtractFromUserinfo (host_client, true);\n}\n\nstatic void SVNQ_DPModel_f (void)\n{\n\tCmd_TokenizeString(va(\"setinfo model \\\"%s\\\"\\n\", Cmd_Argv(1)), false, false);\n\tSV_SetInfo_f();\n}\nstatic void SVNQ_DPSkin_f (void)\n{\n\tCmd_TokenizeString(va(\"setinfo skin \\\"%s\\\"\\n\", Cmd_Argv(1)), false, false);\n\tSV_SetInfo_f();\n}\n\nstatic void SVNQ_Ping_f(void)\n{\n\tint i;\n\tclient_t *cl;\n\n\t//don't translate this, most advanced clients (including us) automate and parse them, the results being visible in the scoreboard and NOT the console.\n\t//translating these prints can thus confuse things greatly.\n\tSV_PrintToClient(host_client, PRINT_HIGH, \"Client ping times:\\n\");\n\tfor (i=0,cl=svs.clients ; i<sv.allocated_client_slots ; i++,cl++)\n\t{\n\t\tif (!cl->state)\n\t\t\tcontinue;\n\n\t\tSV_PrintToClient(host_client, PRINT_HIGH, va(\"%3i %s\\n\", SV_CalcPing (cl, false), cl->name));\n\t}\n}\nstatic void SVNQ_Status_f(void)\n{\t//note: numerous NQ clients poll for this...\n\t//so try to ensure that we adhere to various rules...\n\t//we have a different function for server operators to use which contains more info.\n\tint i;\n\tclient_t *cl;\n\tint count;\n\textern cvar_t maxclients, maxspectators;\n\n\t/*\n\tint nummodels, numsounds;\n\tfor (nummodels = 1; nummodels < MAX_PRECACHE_MODELS; nummodels++)\n\t\tif (!sv.strings.model_precache[nummodels])\n\t\t\tbreak;\n\tfor (numsounds = 1; numsounds < MAX_PRECACHE_SOUNDS; numsounds++)\n\t\tif (!sv.strings.sound_precache[numsounds])\n\t\t\tbreak;*/\n\n\tSV_PrintToClient(host_client, PRINT_HIGH, va(\"host:    %s\\n\", hostname.string));\t//must be first, with same first 9 chars\n\tSV_PrintToClient(host_client, PRINT_HIGH, va(\"version: %s\\n\", version_string()));\n//\tSV_PrintToClient(host_client, PRINT_HIGH, va(\"IPv4:     \\n\", ));\n//\tSV_PrintToClient(host_client, PRINT_HIGH, va(\"IPv6:     \\n\", ));\n\tSV_PrintToClient(host_client, PRINT_HIGH, va(\"map:     %s\\n\", svs.name));\n/*\tfor (count = 1; count < MAX_PRECACHE_MODELS; count++)\n\t\tif (!sv.strings.model_precache[count])\n\t\t\tbreak;\n\tSV_PrintToClient(host_client, PRINT_HIGH, va(\"models:  %i/%i\\n\", count-1, MAX_PRECACHE_MODELS-1));*/\n/*\tfor (count = 1; count < MAX_PRECACHE_SOUNDS; count++)\n\t\tif (!sv.strings.sound_precache[count])\n\t\t\tbreak;\n\tSV_PrintToClient(host_client, PRINT_HIGH, va(\"sounds:  %i/%i\\n\", count-1, MAX_PRECACHE_SOUNDS-1));*/\n//\tSV_PrintToClient(host_client, PRINT_HIGH, va(\"entities:%i/%i\\n\", sv.world.num_edicts, sv.world.max_edicts));\n\tfor (count=0,i=0,cl=svs.clients ; i<sv.allocated_client_slots ; i++,cl++)\n\t{\n\t\tif (cl->state)\n\t\t\tcount++;\n\t}\n\tSV_PrintToClient(host_client, PRINT_HIGH, va(\"players: %i active (%i max)\\n\\n\", count, min(maxclients.ival+maxspectators.ival,sv.allocated_client_slots)));//must be last\n\tfor (i=0,cl=svs.clients ; i<sv.allocated_client_slots ; i++,cl++)\n\t{\n\t\tint hours, mins, secs;\n\t\tif (!cl->state)\n\t\t\tcontinue;\n\t\tif (i >= host_client->max_net_clients)\n\t\t\tbreak;\t//don't send more than it expects. the ping parsers will give up and get spammy (sucks).\n\t\tsecs = realtime - cl->connection_started;\n\t\tmins = secs/60;\n\t\tsecs -= mins*60;\n\t\thours = mins/60;\n\t\tmins -= hours*60;\n\n\t\tSV_PrintToClient(host_client, PRINT_HIGH, va(\"#%-2u %-16.16s  %3i  %2i:%02i:%02i\\n\", i+1, cl->name, cl->old_frags, hours, mins, secs));\n\t\tSV_PrintToClient(host_client, PRINT_HIGH, va(\"   %s\\n\", SV_PlayerPublicAddress(cl)));\n\t}\n}\n\nstatic void SVNQ_Protocols_f(void)\n{\n\tint i;\n\thost_client->supportedprotocols = 0;\n\tfor (i = 1; i < Cmd_Argc(); )\n\t{\n\t\tswitch(strtoul(Cmd_Argv(i++), NULL, 0))\n\t\t{\n\t\tcase PROTOCOL_VERSION_BJP3:\n\t\t\thost_client->supportedprotocols |= 1u<<SCP_BJP3;\n\t\t\tbreak;\n\t\tcase PROTOCOL_VERSION_FITZ:\n\t\t\thost_client->supportedprotocols |= 1u<<SCP_FITZ666;\n\t\t\tbreak;\n\t\tcase PROTOCOL_VERSION_RMQ:\n\t\t\thost_client->supportedprotocols |= 1u<<SCP_FITZ666;\n\t\t\tbreak;\n\t\tcase PROTOCOL_VERSION_DP6:\n\t\t\thost_client->supportedprotocols |= 1u<<SCP_DARKPLACES6;\n\t\t\tbreak;\n\t\tcase PROTOCOL_VERSION_DP7:\n\t\t\thost_client->supportedprotocols |= 1u<<SCP_DARKPLACES7;\n\t\t\tbreak;\n\t\tdefault:\t//don't know what you are. at least don't bug out.\n\t\t\thost_client->supportedprotocols |= 1u<<SCP_NETQUAKE;\n\t\t\tbreak;\n\t\t}\n\t}\n}\nstatic void SV_PlayerExFlags_f(void)\n{\n\tint i = atoi(Cmd_Argv(1));\n\tconst char *v = \"\";\n\tswitch(i&3)\n\t{\n\tcase 0:\n\t\tv = \"\";\n\t\tbreak;\n\tcase 1:\n\t\tv = \"0\";\n\t\tbreak;\n\tcase 2:\n\t\tv = \"1\";\n\t\tbreak;\n\t}\n\tInfoBuf_SetKey(&host_client->userinfo, \"w_switch\", v);\n}\n\n/*\nvoid SVNQ_ExecuteUserCommand (char *s)\n{\n\tclient_t *oldhost = host_client;\n\tucmd_t\t*u;\n\n\tCmd_TokenizeString (s, false, false);\n\tsv_player = host_client->edict;\n\n\tCmd_ExecLevel=1;\n\n\tfor (u=nqucmds ; u->name ; u++)\n\t{\n\t\tif (!strcmp (Cmd_Argv(0), u->name) )\n\t\t{\n\t\t\tif (/ *!fromQC && * /!u->noqchandling)\n\t\t\t\tif (PR_UserCmd(s))\n\t\t\t\t{\n\t\t\t\t\thost_client = oldhost;\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\tif (!u->func)\n\t\t\t{\n\t\t\t\tSV_BeginRedirect (RD_CLIENT, host_client->language);\n\t\t\t\tCon_Printf(\"Command was disabled\\n\");\n\t\t\t\tSV_EndRedirect ();\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tSV_BeginRedirect (RD_CLIENT, host_client->language);\n\t\t\t\tu->func ();\n\t\t\t\tSV_EndRedirect ();\n\t\t\t}\n\n\t\t\thost_client = oldhost;\n\t\t\treturn;\n\t\t}\n\t}\n\n\tif (!u->name)\n\t\tCon_Printf(\"%s tried to \\\"%s\\\"\\n\", host_client->name, s);\n}\n*/\n#endif\n\n//used when we can't use our getchallenge handshake for some reason.\n//this is both nq (where there's no challenge at all) and qw-via-qwfwd (where the proxy handshakes before talking to the actual server)\nvoid SV_Pext_f(void)\n{\n\tint i;\n\tchar *tag;\n\tchar *val;\n\n\tif (host_client->pextknown)\n\t\treturn;\n\thost_client->pextknown = true;\n\n\thost_client->fteprotocolextensions = 0;\n\thost_client->fteprotocolextensions2 = 0;\n\thost_client->ezprotocolextensions1 = 0;\n\tfor (i = 1; i < Cmd_Argc(); )\n\t{\n\t\ttag = Cmd_Argv(i++);\n\t\tval = Cmd_Argv(i++);\n\t\tswitch(strtoul(tag, NULL, 0))\n\t\t{\n\t\tcase PROTOCOL_VERSION_FTE1:\n\t\t\thost_client->fteprotocolextensions = strtoul(val, NULL, 0) & Net_PextMask(PROTOCOL_VERSION_FTE1, ISNQCLIENT(host_client)) & PEXT_SERVERADVERTISE;\n\t\t\tbreak;\n\t\tcase PROTOCOL_VERSION_FTE2:\n\t\t\thost_client->fteprotocolextensions2 = strtoul(val, NULL, 0) & Net_PextMask(PROTOCOL_VERSION_FTE2, ISNQCLIENT(host_client)) & PEXT2_SERVERADVERTISE;\n\t\t\tbreak;\n\t\tcase PROTOCOL_VERSION_EZQUAKE1:\n\t\t\thost_client->ezprotocolextensions1 = strtoul(val, NULL, 0) & Net_PextMask(PROTOCOL_VERSION_EZQUAKE1, ISNQCLIENT(host_client)) & EZPEXT1_SERVERADVERTISE;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tSV_ClientProtocolExtensionsChanged(host_client);\n\n\tif (!host_client->supportedprotocols && Cmd_Argc() == 1)\n\t\tCon_DPrintf(\"%s reports no extended capabilities.\\n\", host_client->name);\n\telse\n\t\tCon_DPrintf(\"%s now using pext: %x, %x, %x\\n\", host_client->name, host_client->fteprotocolextensions, host_client->fteprotocolextensions2, host_client->ezprotocolextensions1);\n\n#ifdef NQPROT\n\tif (ISNQCLIENT(host_client))\n\t\tSVNQ_New_f();\n\telse\n#endif\n\t\tSV_New_f();\n}\n\n\n\nvoid SV_UserMVDList_f (void);\nvoid SV_UserMVDInfo_f (void);\ntypedef struct\n{\n\tchar\t*name;\n\tvoid\t(*func) (void);\n\tint\tnoqchandling;\n} ucmd_t;\n\nucmd_t ucmds[] =\n{\n\t/*connection process*/\n\t{\"new\",\t\t\tSV_New_f, true},\n\t{\"pext\",\t\tSV_Pext_f, true},\n\t{\"modellist\",\tSVQW_Modellist_f, true},\n\t{\"soundlist\",\tSVQW_Soundlist_f, true},\n\t{\"prespawn\",\tSVQW_PreSpawn_f, true},\n\t{\"spawn\",\t\tSVQW_Spawn_f, true},\n\t{\"begin\",\t\tSV_Begin_f, true},\n\n\t{\"drop\",\t\tSV_Drop_f},\n\t{\"disconnect\",\tSV_Drop_f},\n\t{\"pings\",\t\tSV_Pings_f},\n\t{\"enablecsqc\",\tSV_EnableClientsCSQC, 2},\n\t{\"disablecsqc\",\tSV_DisableClientsCSQC, 2},\n\n\t/* issued by hand at client console*/\n\t{\"rate\",\t\tSV_Rate_f},\n\t{\"kill\",\t\tSV_Kill_f},\n\t{\"pause\",\t\tSV_Pause_f},\n\t{\"msg\",\t\t\tSV_Msg_f},\n\t{\"efpslist\",\tCmd_FPSList_f},\t//don't conflict with the ktpro one\n\t{\"vote\",\t\tSV_Vote_f},\n\n\t//{\"ban\",\t\tCmd_Ban_f},\t\t//for admins\n\t//{\"banip\",\t\tCmd_BanIP_f},\t//for admins\n\t//{\"banrem\",\tCmd_BanRem_f},\t//for admins\n\n\t/*user interactions*/\n\t{\"sayone\",\t\tSV_SayOne_f},\n\t{\"say\",\t\t\tSV_Say_f},\n\t{\"say_team\",\tSV_Say_Team_f},\n#ifdef NQPROT\n\t{\"status\",\t\tSVNQ_Status_f},\n#endif\n\n#ifdef SVRANKING\n\t{\"topten\",\t\tRank_ListTop10_f},\n#endif\n\n\t{\"setinfo\",\t\tSV_SetInfo_f},\n\t{\"serverinfo\",\tSV_ShowServerinfo_f},\n\n\t/*download/demo commands*/\n#ifdef MVD_RECORDING\n\t{\"demolist\",\tSV_UserCmdMVDList_f},\n\t{\"dlist\",\t\tSV_UserCmdMVDList_f},\t//apparently people are too lazy to type.\n\t\t\t\t\t\t\t\t\t//mvdsv has 4 more variants, for 6 total doing the same thing.\n\t{\"demoinfo\",\tSV_UserMVDInfo_f},\n\t{\"dl\",\t\t\tSV_DemoDownload_f},\n#endif\n\t{\"stopdownload\",SV_StopDownload_f},\n\t{\"stopdl\",\t\tSV_StopDownload_f},\t//mvdsv compat\n\t{\"dlsize\",\t\tSV_DownloadSize_f},\n\t{\"download\",\tSV_BeginDownload_f},\n\t{\"nextdl\",\t\tSV_NextDownload_f, true},\n\n\t/*quakeworld specific things*/\n\t{\"addseat\",\t\tCmd_AddSeat_f},\t//splitscreen\n\t{\"join\",\t\tCmd_Join_f},\n\t{\"observe\",\t\tCmd_Observe_f},\n\t{\"ptrack\",\t\tSV_PTrack_f}, //ZOID - used with autocam\n\t{\"snap\",\t\tSV_NoSnap_f},\t//cheat detection\n\n\t/*cheats*/\n\t{\"god\",\t\t\tCmd_God_f},\n\t{\"give\",\t\tCmd_Give_f},\n\t{\"noclip\",\t\tCmd_Noclip_f},\n\t{\"spiderpig\",\tCmd_Spiderpig_f},\n\t{\"6dof\",\t\tCmd_6dof_f},\n\t{\"fly\",\t\t\tCmd_Fly_f},\n\t{\"notarget\",\tCmd_Notarget_f},\n\t{\"setpos\",\t\tCmd_SetPos_f},\n#if defined(_DEBUG) && defined(SUBSERVERS)\n\t{\"ssvtransfer\", Cmd_SSV_Transfer_f},//transfer the player to a different map/server\n\t{\"ssvsay\",\t\tCmd_SSV_AllSay_f},\t//says realm-wide\n\t{\"ssvjoin\",\t\tCmd_SSV_Join_f},\t//transfer the player to a different map/server\n#endif\n\n#ifdef NQPROT\n\t{\"name\",\t\tSVNQ_NQInfo_f},\n#endif\n\n#ifdef VOICECHAT\n\t{\"voicetarg\",\tSV_Voice_Target_f},\n\t{\"vignore\",\t\tSV_Voice_Ignore_f},\t/*ignore/mute specific player*/\n\t{\"muteall\",\t\tSV_Voice_MuteAll_f},\t/*disables*/\n#endif\n\t{\"unmuteall\",\tSV_Voice_UnmuteAll_f}, /*reenables*/\n\n\t{NULL, NULL}\n};\n\n#ifdef Q2SERVER\nucmd_t ucmdsq2[] = {\n\t{\"new\", SV_New_f, true},\n\t{\"configstrings\", SVQ2_ConfigStrings_f, true},\n\t{\"baselines\", SVQ2_BaseLines_f, true},\n\t{\"begin\", SV_Begin_f, true},\n\n\t{\"serverinfo\", SV_ShowServerinfo_f, true},\n\t{\"info\", SV_ShowServerinfo_f, true},\n\n\t{\"download\", SV_BeginDownload_f, true},\n\t{\"nextdl\", SV_NextDownload_f, true},\n\n\t{\"nextserver\", SVQ2_NextServer_f, true},\n\n\t//fte stuff\n\t{\"setinfo\", SV_SetInfo_f, true},\n\t{\"ftevote\", SV_Vote_f, true},\t//voting... kinda messed up by 'vote' being common in mods\n\t{\"addseat\", Cmd_AddSeat_f, true},\t\t//for splitscreen\n\n//#ifdef SVRANKING\n//\t{\"topten\", Rank_ListTop10_f, true},\n//#endif\n\n\t//quakeworld uses 'drop', quake2 commonly uses that to chuck items away / to a friend.\n\t{\"disconnect\", SV_Drop_f, true},\n\n\t{NULL, NULL}\n};\n#endif\n\n#ifdef NQPROT\nucmd_t nqucmds[] =\n{\n\t{\"new\",\t\t\tSVNQ_New_f, true},\n\t{\"spawn\",\t\tSVNQ_Spawn_f, true},\n\t{\"begin\",\t\tSVNQ_Begin_f, true},\n\t{\"prespawn\",\tSVNQ_PreSpawn_f, true},\n\n\t{\"status\",\t\tSVNQ_Status_f},\n\n\n\t{\"god\",\t\t\tCmd_God_f},\n\t{\"give\",\t\tCmd_Give_f},\n\t{\"notarget\",\tCmd_Notarget_f},\n\t{\"fly\",\t\t\tCmd_Fly_f},\n\t{\"noclip\",\t\tCmd_Noclip_f},\n\t{\"setpos\",\t\tCmd_SetPos_f},\n\n\t{\"say\",\t\t\tSV_Say_f},\n\t{\"say_team\",\tSV_Say_Team_f},\n\t{\"tell\",\t\tSV_SayOne_f},\n\t{\"efpslist\",\tCmd_FPSList_f},\t//don't conflict with the ktpro one\n\n\t{\"pings\",\t\tSV_Pings_f},\n\t{\"ping\",\t\tSVNQ_Ping_f},\n\n\t{\"kill\",\t\tSV_Kill_f},\n\t{\"pause\",\t\tSV_Pause_f},\n\t{\"kick\",\t\tNULL},\n\t{\"ban\",\t\t\tNULL},\n\t{\"vote\",\t\tSV_Vote_f},\n\n\t/*DP download protocol*/\n\t{\"dlsize\",\t\tSV_DownloadSize_f},\n\t{\"download\",\tSV_BeginDownload_f},\n\t{\"sv_startdownload\",\tSVDP_StartDownload_f},\n\t{\"stopdownload\",\tSV_StopDownload_f},\n\n\t{\"serverinfo\", SV_ShowServerinfo_f},\n\t/*userinfo stuff*/\n\t{\"setinfo\",\t\tSV_SetInfo_f},\n\t{\"name\",\t\tSVNQ_NQInfo_f},\n\t{\"color\",\t\tSVNQ_NQColour_f},\n\t{\"playermodel\",\tSVNQ_DPModel_f},\n//\t{\"pmodel\",\t\tSVNQ_DPModel_f},\t//nehahra\n\t{\"playerskin\",\tSVNQ_DPSkin_f},\n\t{\"rate\",\t\tSV_Rate_f},\n\t{\"rate_burstsize\",\tNULL},\n\n#ifdef SVRANKING\n\t{\"topten\",\t\tRank_ListTop10_f},\n#endif\n\n\t/*various misc extensions*/\n\t{\"protocols\",\tSVNQ_Protocols_f, true},\n\t{\"pext\",\t\tSV_Pext_f, true},\n\t{\"enablecsqc\",\tSV_EnableClientsCSQC, 2},\n\t{\"disablecsqc\",\tSV_DisableClientsCSQC, 2},\n\t{\"challengeconnect\", NULL},\n\n\t/*spectating, this should be fun...*/\n\t{\"join\", Cmd_Join_f},\n\t{\"observe\", Cmd_Observe_f},\n\t{\"ptrack\", SV_PTrack_f}, //ZOID - used with autocam\n\n#ifdef VOICECHAT\n\t{\"voicetarg\",\tSV_Voice_Target_f},\n\t{\"vignore\",\t\tSV_Voice_Ignore_f},\t/*ignore/mute specific player*/\n\t{\"muteall\",\t\tSV_Voice_MuteAll_f},\t/*disables*/\n\t{\"unmuteall\",\tSV_Voice_UnmuteAll_f}, /*reenables*/\n#endif\n\n\t{\"playerexflags\", SV_PlayerExFlags_f},\n\n\t{NULL, NULL}\n};\n#endif\n\n/*\n==================\nSV_ExecuteUserCommand\n==================\n*/\nvoid SV_ExecuteUserCommand (const char *s, qboolean fromQC)\n{\n\tucmd_t\t*u;\n\tclient_t *oldhost = host_client;\n\n\tif (host_client->state < cs_connected)\n\t\treturn;\n\n\tCon_DLPrintf((host_client->netchan.remote_address.type==NA_LOOPBACK)?2:1, \"Client command: %s\\n\", s);\n\n\tCmd_TokenizeString (s, false, false);\n\tsv_player = host_client->edict;\n\n\tCmd_ExecLevel=1;\n\n#ifdef Q2SERVER\n\tif (ISQ2CLIENT(host_client))\n\t\tu = ucmdsq2;\n\telse\n#endif\n#ifdef NQPROT\n\tif (ISNQCLIENT(host_client))\n\t\tu = nqucmds;\n\telse\n#endif\n\t\tu=ucmds;\n\n\tfor ( ; u->name ; u++)\n\t\tif (!strcmp (Cmd_Argv(0), u->name) )\n\t\t{\n\t\t\tif (u->noqchandling==2)\n\t\t\t{\t//issue the command then let the QC handle it too\n\t\t\t\tif (!fromQC)\n\t\t\t\t{\n\t\t\t\t\tif (u->func)\n\t\t\t\t\t\tu->func();\n\t\t\t\t\tif (host_client->spawned)\n\t\t\t\t\t\tPR_KrimzonParseCommand(s);\n\t\t\t\t}\n\t\t\t\thost_client = oldhost;\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (!fromQC && !u->noqchandling)\n\t\t\t\tif (PR_KrimzonParseCommand(s))\t//KRIMZON_SV_PARSECLIENTCOMMAND has the opportunity to parse out certain commands.\n\t\t\t\t{\n\t\t\t\t\thost_client = oldhost;\n\t\t\t\t\treturn;\n\t\t\t\t}\n//\t\t\tSV_BeginRedirect (RD_CLIENT, host_client->language);\n\t\t\tif (u->func)\n\t\t\t\tu->func();\n\t\t\thost_client = oldhost;\n//\t\t\tSV_EndRedirect ();\n\t\t\treturn;\n\t\t}\n\n\tif (!u->name)\n\t{\n#ifdef HLSERVER\n\t\tif (HLSV_ClientCommand(host_client))\n\t\t{\n\t\t\thost_client = oldhost;\n\t\t\treturn;\n\t\t}\n#endif\n\n\t\tif (!fromQC)\n\t\t\tif (PR_UserCmd(s))\t\t\t//Q2 and MVDSV command handling only happens if the engine didn't recognise it.\n\t\t\t{\n\t\t\t\thost_client = oldhost;\n\t\t\t\treturn;\n\t\t\t}\n#ifdef SVRANKING\n\t\tif (sv_cmdlikercon.value && host_client->rankid)\n\t\t{\n\t\t\tchar adr[MAX_ADR_SIZE];\n\t\t\tchar remaining[1024];\n\t\t\tint i;\n\t\t\trankstats_t stats;\n\n\t\t\tif (!Rank_GetPlayerStats(host_client->rankid, &stats))\n\t\t\t{\n\t\t\t\thost_client = oldhost;\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tLog_String(LOG_RCON, va(\"cmd from %s - %s:\\n%s\\n\"\n\t\t\t\t, NET_AdrToString (adr, sizeof(adr), &net_from), host_client->name, s));\n\n\t\t\tCon_TPrintf (\"cmd from %s:\\n%s\\n\"\n\t\t\t\t, host_client->name, s);\n\n\t\t\tSV_BeginRedirect (RD_CLIENT, host_client->language);\n\n\t\t\tremaining[0] = 0;\n\n\t\t\tfor (i=0 ; i<Cmd_Argc() ; i++)\n\t\t\t{\n\t\t\t\tif (strlen(remaining)+strlen(Cmd_Argv(i))>=sizeof(remaining)-1)\n\t\t\t\t{\n\t\t\t\t\tCon_Printf(\"cmd was too long\\n\");\n\t\t\t\t\thost_client = oldhost;\n\t\t\t\t\tSV_EndRedirect ();\n\t\t\t\t\tCon_Printf (\"cmd from %s:\\n%s\\n\"\n\t\t\t\t\t\t, NET_AdrToString (adr, sizeof(adr), &net_from), \"Was too long - possible buffer overflow attempt\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tQ_strncatz(remaining, Cmd_Argv(i), sizeof(remaining));\n\t\t\t\tQ_strncatz(remaining, \" \", sizeof(remaining));\n\t\t\t}\n\n\t\t\tCmd_ExecuteString (remaining, stats.trustlevel);\n\t\t\thost_client = oldhost;\n\t\t\tSV_EndRedirect ();\n\t\t\treturn;\n\t\t}\n#endif\n\t\tCon_TPrintf (\"Bad user command: %s\\n\", Cmd_Argv(0));\n\t}\n\n\thost_client = oldhost;\n\tSV_EndRedirect ();\n}\n\nint implevels[256];\nqboolean SV_FilterImpulse(int imp, int level)\n{\n\tif (imp < 0 || imp > 255)\n\t\treturn true;\t//erm\n\n\tif (implevels[imp] > level)\n\t\treturn false;\n\n\treturn true;\n}\nvoid SV_FilterImpulseInit(void)\n{\n\tchar buffer[1024];\n\tchar *s;\n\tint lev;\n\tint imp;\n\tmemset(implevels, 0, sizeof(implevels));\n\n\ts = COM_LoadStackFile(\"impfiltr.cfg\", buffer, sizeof(buffer), NULL);\n\tif (!s)\n\t\tCon_DPrintf(\"impfiltr.cfg not found. Impulse filters are disabled\\n\");\n\n\twhile(s)\n\t{\n\t\ts = COM_Parse(s);\n\t\tif (!s)\n\t\t\treturn;\n\t\timp = atoi(com_token);\n\t\ts = COM_Parse(s);\n\t\tif (!s)\n\t\t{\n\t\t\tCon_Printf(\"Unexpected eof in impfiltr.cfg\\n\");\n\t\t\treturn;\n\t\t}\n\t\tlev = atoi(com_token);\n\t\tif (imp > 255 || imp < 0 || lev < 0 || lev > RESTRICT_MAX)\n\t\t\tCon_Printf(\"impfiltr.cfg - bad paramters\\n\");\n\t\telse\n\t\t\timplevels[imp] = lev;\n\t}\n}\n/*\n===========================================================================\n\nUSER CMD EXECUTION\n\n===========================================================================\n*/\n#ifdef SERVERONLY\n/*\n===============\nV_CalcRoll\n\nUsed by view and sv_user\n===============\n*/\nfloat V_CalcRoll (vec3_t angles, vec3_t velocity)\n{\n\tvec3_t\tforward, right, up;\n\tfloat\tsign;\n\tfloat\tside;\n\tfloat\tvalue;\n\n\tAngleVectors (angles, forward, right, up);\n\tside = DotProduct (velocity, right);\n\tsign = side < 0 ? -1 : 1;\n\tside = fabs(side);\n\n\tvalue = cl_rollangle.value;\n\n\tif (side < cl_rollspeed.value)\n\t\tside = side * value / cl_rollspeed.value;\n\telse\n\t\tside = value;\n\n\treturn side*sign;\n\n}\n#endif\n\n\n\n//============================================================================\n\nvec3_t\tpmove_mins, pmove_maxs;\n\nstatic qboolean AddEntityToPmove(world_t *w, wedict_t *player, wedict_t *check)\n{\n\tphysent_t\t*pe;\n\tint\t\t\tsolid = check->v->solid;\n\tenum q1contents_e\t\t\tq1contents;\n\n\tif (pmove.numphysent == MAX_PHYSENTS)\n\t\treturn false;\n\tpe = &pmove.physents[pmove.numphysent];\n\tpe->notouch = !((int)player->xv->dimension_solid & (int)check->xv->dimension_hit);\n\tif (!((int)player->xv->dimension_hit & (int)check->xv->dimension_solid))\n\t\treturn true;\n\tif (!check->v->size[0])\t//points are not meant to be solid\n\t\treturn true;\n\tpmove.numphysent++;\n\n\tVectorCopy (check->v->origin, pe->origin);\n\tpe->info = NUM_FOR_EDICT(w->progs, check);\n\tpe->nonsolid = solid == SOLID_TRIGGER;\n\tpe->isportal = solid == SOLID_PORTAL;\n\tq1contents = (int)check->v->skin;\n\tif (solid == SOLID_LADDER)\n\t\tq1contents = Q1CONTENTS_LADDER;\t//legacy crap\n\telse if (solid == SOLID_CORPSE)\n\t\tq1contents = Q1CONTENTS_CORPSE;\t//legacy crap\n\tsafeswitch(q1contents)\n\t{\n\tcase Q1CONTENTS_EMPTY:\t\t\tpe->nonsolid = true;\tpe->forcecontentsmask = FTECONTENTS_EMPTY;\tbreak;\n\tcase Q1CONTENTS_SOLID:\t\t\tpe->nonsolid = false;\tpe->forcecontentsmask = FTECONTENTS_SOLID;\tbreak;\n\tcase Q1CONTENTS_WATER:\t\t\tpe->nonsolid = true;\tpe->forcecontentsmask = FTECONTENTS_WATER;\tbreak;\n\tcase Q1CONTENTS_LAVA:\t\t\tpe->nonsolid = true;\tpe->forcecontentsmask = FTECONTENTS_LAVA;\tbreak;\n\tcase Q1CONTENTS_SLIME:\t\t\tpe->nonsolid = true;\tpe->forcecontentsmask = FTECONTENTS_SLIME;\tbreak;\n\tcase Q1CONTENTS_SKY:\t\t\tpe->nonsolid = true;\tpe->forcecontentsmask = FTECONTENTS_SKY;\tbreak;\n\tcase HLCONTENTS_CLIP:\t\t\tpe->nonsolid = false;\tpe->forcecontentsmask = FTECONTENTS_PLAYERCLIP|FTECONTENTS_MONSTERCLIP;\tbreak;\n\tcase HLCONTENTS_CURRENT_0:\t\tpe->nonsolid = true;\tpe->forcecontentsmask = FTECONTENTS_WATER|Q2CONTENTS_CURRENT_0;\t\t\tbreak;\n\tcase HLCONTENTS_CURRENT_90:\t\tpe->nonsolid = true;\tpe->forcecontentsmask = FTECONTENTS_WATER|Q2CONTENTS_CURRENT_90;\t\tbreak;\n\tcase HLCONTENTS_CURRENT_180:\tpe->nonsolid = true;\tpe->forcecontentsmask = FTECONTENTS_WATER|Q2CONTENTS_CURRENT_180;\t\tbreak;\n\tcase HLCONTENTS_CURRENT_270:\tpe->nonsolid = true;\tpe->forcecontentsmask = FTECONTENTS_WATER|Q2CONTENTS_CURRENT_270;\t\tbreak;\n\tcase HLCONTENTS_CURRENT_UP:\t\tpe->nonsolid = true;\tpe->forcecontentsmask = FTECONTENTS_WATER|Q2CONTENTS_CURRENT_UP;\t\tbreak;\n\tcase HLCONTENTS_CURRENT_DOWN:\tpe->nonsolid = true;\tpe->forcecontentsmask = FTECONTENTS_WATER|Q2CONTENTS_CURRENT_DOWN;\t\tbreak;\n\tcase HLCONTENTS_TRANS:\t\t\tpe->nonsolid = true;\tpe->forcecontentsmask = FTECONTENTS_EMPTY;\tbreak;\n\tcase Q1CONTENTS_LADDER:\t\t\tpe->nonsolid = true;\tpe->forcecontentsmask = FTECONTENTS_LADDER;\t\t\tbreak;\n\tcase Q1CONTENTS_MONSTERCLIP:\tpe->nonsolid = true;\tpe->forcecontentsmask = FTECONTENTS_MONSTERCLIP;\tbreak;\n\tcase Q1CONTENTS_PLAYERCLIP:\t\tpe->nonsolid = false;\tpe->forcecontentsmask = FTECONTENTS_PLAYERCLIP;\t\tbreak;\n\tcase Q1CONTENTS_CORPSE:\t\t\tpe->nonsolid = true;\tpe->forcecontentsmask = FTECONTENTS_CORPSE;\t\t\tbreak;\n\tsafedefault:\n\t\tpe->forcecontentsmask = 0;\n\t\tbreak;\n\t}\n\tif (solid == SOLID_PORTAL || solid == SOLID_BSP || solid == SOLID_BSPTRIGGER)\n\t{\n\t\tif(progstype != PROG_H2)\n\t\t\tpe->angles[0]*=r_meshpitch.value;\t//quake is wierd. I guess someone fixed it hexen2... or my code is buggy or something...\n\t\tpe->model = sv.models[(int)(check->v->modelindex)];\n\t\tVectorCopy (check->v->angles, pe->angles);\n\t}\n\telse\n\t{\n\t\tpe->model = NULL;\n\t\tVectorCopy (check->v->mins, pe->mins);\n\t\tVectorCopy (check->v->maxs, pe->maxs);\n\t\tVectorClear (pe->angles);\n\t}\n\treturn true;\n}\n\n#if 1\n#ifdef USEAREAGRID\nextern size_t areagridsequence;\nstatic void AddLinksToPmove (world_t *w, wedict_t *player, areagridlink_t *node)\n{\n\tint Q1_HullPointContents (hull_t *hull, int num, vec3_t p);\n\tlink_t\t\t*l, *next;\n\twedict_t\t\t*check;\n\tint\t\t\tpl;\n\tint\t\t\ti;\n\tint\t\t\tsolid;\n\n\tpl = EDICT_TO_PROG(w->progs, player);\n\n\t// touch linked edicts\n\tfor (l = node->l.next ; l != &node->l ; l = next)\n\t{\n\t\tnext = l->next;\n\t\tcheck = ((areagridlink_t*)l)->ed;\n\n\t\tif (check->gridareasequence == areagridsequence)\n\t\t\tcontinue;\n\t\tcheck->gridareasequence = areagridsequence;\n\n\t\tif (check->v->owner == pl)\n\t\t\tcontinue;\t\t// player's own missile\n\t\tif (check == player)\n\t\t\tcontinue;\n\t\tsolid = check->v->solid;\n\t\tif (\n\t\t\t(solid == SOLID_TRIGGER && check->v->skin < 0)\n\t\t\t|| solid == SOLID_BSP\n\t\t\t|| solid == SOLID_PORTAL\n\t\t\t|| solid == SOLID_BBOX\n\t\t\t|| solid == SOLID_SLIDEBOX\n\t\t\t|| solid == SOLID_LADDER\n\t\t\t//|| (solid == SOLID_PHASEH2 && progstype == PROG_H2) //logically matches hexen2, but I hate it\n\t\t\t)\n\t\t{\n\n\t\t\tfor (i=0 ; i<3 ; i++)\n\t\t\t\tif (check->v->absmin[i] > pmove_maxs[i]\n\t\t\t\t|| check->v->absmax[i] < pmove_mins[i])\n\t\t\t\t\tbreak;\n\t\t\tif (i != 3)\n\t\t\t\tcontinue;\n\n\t\t\tif (!AddEntityToPmove(w, player, check))\n\t\t\t\tbreak;\n\t\t}\n\t}\n}\n\n//ignores mins/maxs.\n//portals are expected to be weird. player movement code is nasty.\nstatic void AddPortalsToPmove (world_t *w, wedict_t *player, areagridlink_t *node)\n{\n\tlink_t\t\t*l, *next;\n\twedict_t\t*check;\n\tint\t\t\tpl;\n//\tint\t\t\ti;\n\tint\t\t\tsolid;\n\n\tpl = EDICT_TO_PROG(w->progs, player);\n\n\t// touch linked edicts\n\tfor (l = node->l.next ; l != &node->l ; l = next)\n\t{\n\t\tnext = l->next;\n\t\tcheck = ((areagridlink_t*)l)->ed;\n\n\t\tif (check->gridareasequence == areagridsequence)\n\t\t\tcontinue;\n\t\tcheck->gridareasequence = areagridsequence;\n\n\t\tif (check->v->owner == pl)\n\t\t\tcontinue;\t\t// player's own missile\n\t\tif (check == player)\n\t\t\tcontinue;\n\t\tsolid = check->v->solid;\n\t\tif (\n\t\t\t(solid == SOLID_TRIGGER && check->v->skin < 0)\n\t\t\t|| solid == SOLID_BSP\n\t\t\t|| solid == SOLID_PORTAL\n\t\t\t|| solid == SOLID_BBOX\n\t\t\t|| solid == SOLID_SLIDEBOX\n\t\t\t|| solid == SOLID_LADDER\n\t\t\t//|| (solid == SOLID_PHASEH2 && progstype == PROG_H2) //logically matches hexen2, but I hate it\n\t\t\t)\n\t\t{\n\t\t\tif (!AddEntityToPmove(w, player, check))\n\t\t\t\tbreak;\n\t\t}\n\t}\n}\nvoid AddAllLinksToPmove (world_t *w, wedict_t *player)\n{\n\tint ming[2], maxg[2], g[2];\n\tCALCAREAGRIDBOUNDS(w, pmove_mins, pmove_maxs);\n\n\tareagridsequence++;\n\n\tAddLinksToPmove(w, player, &w->jumboarea);\n\tfor (g[0] = ming[0]; g[0] < maxg[0]; g[0]++)\n\t\tfor (g[1] = ming[1]; g[1] < maxg[1]; g[1]++)\n\t\t\tAddLinksToPmove(w, player, &w->gridareas[g[0] + g[1]*w->gridsize[0]]);\n\n\tAddPortalsToPmove(w, player, &w->portallist);\n}\n#else\n/*\n====================\nAddLinksToPmove\n\n====================\n*/\nvoid AddLinksToPmove (world_t *w, wedict_t *player, areanode_t *node)\n{\n\tint Q1_HullPointContents (hull_t *hull, int num, vec3_t p);\n\tlink_t\t\t*l, *next;\n\twedict_t\t\t*check;\n\tint\t\t\tpl;\n\tint\t\t\ti;\n\tint\t\t\tsolid;\n\n\tpl = EDICT_TO_PROG(w->progs, player);\n\n\t// touch linked edicts\n\tfor (l = node->edicts.next ; l != &node->edicts ; l = next)\n\t{\n\t\tnext = l->next;\n\t\tcheck = (wedict_t*)EDICT_FROM_AREA(l);\n\n\t\tif (check->v->owner == pl)\n\t\t\tcontinue;\t\t// player's own missile\n\t\tif (check == player)\n\t\t\tcontinue;\n\t\tsolid = check->v->solid;\n\t\tif (\n\t\t\t(solid == SOLID_TRIGGER && check->v->skin < 0)\n\t\t\t|| solid == SOLID_BSP\n\t\t\t|| solid == SOLID_PORTAL\n\t\t\t|| solid == SOLID_BBOX\n\t\t\t|| solid == SOLID_SLIDEBOX\n\t\t\t|| solid == SOLID_LADDER\n\t\t\t//|| (solid == SOLID_PHASEH2 && progstype == PROG_H2) //logically matches hexen2, but I hate it\n\t\t\t)\n\t\t{\n\n\t\t\tfor (i=0 ; i<3 ; i++)\n\t\t\t\tif (check->v->absmin[i] > pmove_maxs[i]\n\t\t\t\t|| check->v->absmax[i] < pmove_mins[i])\n\t\t\t\t\tbreak;\n\t\t\tif (i != 3)\n\t\t\t\tcontinue;\n\n\t\t\tif (!AddEntityToPmove(w, player, check))\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n// recurse down both sides\n\tif (node->axis == -1)\n\t\treturn;\n\n\tif (pmove_maxs[node->axis] > node->dist)\n\t\tAddLinksToPmove (w, player, node->children[0]);\n\tif (pmove_mins[node->axis] < node->dist)\n\t\tAddLinksToPmove (w, player, node->children[1]);\n}\n\n//ignores mins/maxs.\n//portals are expected to be weird. player movement code is nasty.\nvoid AddLinksToPmove_Force (world_t *w, wedict_t *player, areanode_t *node)\n{\n\tlink_t\t\t*l, *next;\n\twedict_t\t\t*check;\n\tint\t\t\tpl;\n//\tint\t\t\ti;\n\tint\t\t\tsolid;\n\n\tpl = EDICT_TO_PROG(w->progs, player);\n\n\t// touch linked edicts\n\tfor (l = node->edicts.next ; l != &node->edicts ; l = next)\n\t{\n\t\tnext = l->next;\n\t\tcheck = (wedict_t*)EDICT_FROM_AREA(l);\n\n\t\tif (check->v->owner == pl)\n\t\t\tcontinue;\t\t// player's own missile\n\t\tif (check == player)\n\t\t\tcontinue;\n\t\tsolid = check->v->solid;\n\t\tif (\n\t\t\t(solid == SOLID_TRIGGER && check->v->skin < 0)\n\t\t\t|| solid == SOLID_BSP\n\t\t\t|| solid == SOLID_PORTAL\n\t\t\t|| solid == SOLID_BBOX\n\t\t\t|| solid == SOLID_SLIDEBOX\n\t\t\t|| solid == SOLID_LADDER\n\t\t\t//|| (solid == SOLID_PHASEH2 && progstype == PROG_H2) //logically matches hexen2, but I hate it\n\t\t\t)\n\t\t{\n\t\t\tif (!AddEntityToPmove(w, player, check))\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n// recurse down both sides\n\tif (node->axis == -1)\n\t\treturn;\n\n\tif (pmove_maxs[node->axis] > node->dist)\n\t\tAddLinksToPmove_Force (w, player, node->children[0]);\n\tif (pmove_mins[node->axis] < node->dist)\n\t\tAddLinksToPmove_Force (w, player, node->children[1]);\n}\n#endif\n\n#else\n/*\n================\nAddAllEntsToPmove\n\nFor debugging\n================\n*/\nvoid AddAllEntsToPmove (wedict_t *player, world_t *w)\n{\n\tint\t\t\te;\n\twedict_t\t\t*check;\n\tint\t\t\ti;\n\tint\t\t\tpl;\n\n\tpl = EDICT_TO_PROG(w->progs, player);\n\tfor (e=1 ; e<w->num_edicts ; e++)\n\t{\n\t\tcheck = EDICT_NUM(w->progs, e);\n\t\tif (ED_ISFREE(check))\n\t\t\tcontinue;\n\t\tif (check->v->owner == pl)\n\t\t\tcontinue;\n\t\tif (check->v->solid == SOLID_BSP\n\t\t\t|| check->v->solid == SOLID_BBOX\n\t\t\t|| check->v->solid == SOLID_SLIDEBOX)\n\t\t{\n\t\t\tif (check == player)\n\t\t\t\tcontinue;\n\n\t\t\tfor (i=0 ; i<3 ; i++)\n\t\t\t\tif (check->v->absmin[i] > pmove_maxs[i]\n\t\t\t\t|| check->v->absmax[i] < pmove_mins[i])\n\t\t\t\t\tbreak;\n\t\t\tif (i != 3)\n\t\t\t\tcontinue;\n\n\t\t\tif (!AddEntityToPmove(player, check))\n\t\t\t\tbreak;\n\t\t}\n\t}\n}\n#endif\n\nint SV_PMTypeForClient (client_t *cl, edict_t *ent)\n{\n#ifdef SERVER_DEMO_PLAYBACK\n\tif (sv.demostatevalid)\n\t{\t//force noclip... This does create problems for closing demos.\n\t\tif (cl->zquake_extensions & Z_EXT_PM_TYPE_NEW)\n\t\t\treturn PM_SPECTATOR;\n\t\treturn PM_OLD_SPECTATOR;\n\t}\n#endif\n\n#ifdef HAVE_LEGACY\n\tif (sv_brokenmovetypes.value)\t//this is to mimic standard qw servers, which don't support movetypes other than MOVETYPE_FLY.\n\t{\t\t\t\t\t\t\t\t//it prevents bugs from being visible in unsuspecting mods.\n\t\tif (cl && cl->spectator)\n\t\t{\n\t\t\tif ((cl->zquake_extensions & Z_EXT_PM_TYPE_NEW) || (cl->fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS))\n\t\t\t\treturn PM_SPECTATOR;\n\t\t\treturn PM_OLD_SPECTATOR;\n\t\t}\n\n\t\tif (ent->v->health <= 0)\n\t\t\treturn PM_DEAD;\n\t\treturn PM_NORMAL;\n\t}\n#endif\n\n\tif (sv_nqplayerphysics.ival && sv_nqplayerphysics.ival != 2)\n\t\treturn PM_NONE;\t//let the client know that its prediction is fucked. should make it just lerp.\n\n\tswitch((int)ent->v->movetype)\n\t{\n\tcase MOVETYPE_NOCLIP:\n\t\t/*older/vanilla clients have a b0rked spectator mode that we don't want to break*/\n\t\tif (cl)\n\t\t\tif ((cl->zquake_extensions & Z_EXT_PM_TYPE_NEW) || (cl->fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS))\n\t\t\t\treturn PM_SPECTATOR;\n\t\treturn PM_OLD_SPECTATOR;\n\n\tcase MOVETYPE_WALLWALK:\n\t\treturn PM_WALLWALK;\n\t\n\tcase MOVETYPE_6DOF:\n\t\treturn PM_6DOF;\n\n\tcase MOVETYPE_FLY_WORLDONLY:\n\tcase MOVETYPE_FLY:\n\t\treturn PM_FLY;\n\n\tcase MOVETYPE_NONE:\n\t\treturn PM_NONE;\n\n#ifndef HAVE_LEGACY\n\tcase MOVETYPE_TOSS:\n\tcase MOVETYPE_BOUNCE:\n\t\treturn PM_DEAD;\n#endif\n\n\tcase MOVETYPE_WALK:\n\tdefault:\n#ifdef HAVE_LEGACY\n\t\tif (cl && ent->v->health <= 0)\n\t\t\treturn PM_DEAD;\n#endif\n\t\treturn PM_NORMAL;\n\t}\n}\n\n\n/*\n===========\nSV_PreRunCmd\n===========\nDone before running a player command.  Clears the touch array\n*/\nqbyte *playertouch;\nsize_t playertouchmax;\n\nvoid SV_PreRunCmd(void)\n{\n\tsize_t max = (sv.world.num_edicts+512+7)&~7;\n\tif (max > playertouchmax)\n\t{\n\t\tplayertouchmax = max;\n\t\tBZ_Free(playertouch);\n\t\tplayertouch = BZ_Malloc((playertouchmax>>3)+1);\n\t}\n\tmemset(playertouch, 0, playertouchmax>>3);\n}\nvoid SV_RunCmdCleanup(void)\n{\n\tBZ_Free(playertouch);\n\tplayertouch = NULL;\n\tplayertouchmax = 0;\n}\n\nvoid Sh_CalcPointLight(vec3_t point, vec3_t light);\n\n/*\n===========\nSV_RunCmd\n===========\n*/\nvoid SV_RunCmd (usercmd_t *ucmd, qboolean recurse)\n{\n\tedict_t\t\t*ent;\n\tint\t\t\ti, n;\n\tint\t\t\toldmsec;\n\tqboolean jumpable;\n\tvec3_t new_vel;\n\tvec3_t old_vel;\n\n\t// To prevent a infinite loop\n\tif (!recurse)\n\t{\n#ifdef NEWSPEEDCHEATPROT\n\t\tif (ucmd->msec && host_client->msecs > 500)\n\t\t\thost_client->msecs = 500;\n\t\tif (host_client->hoverms)\n\t\t{\n\t\t\tif (sv_showpredloss.ival)\n\t\t\t\tCon_Printf(\"%s: forcing %g msecs (anti-hover)\\n\", host_client->name, cmd.msec);\n\t\t\thost_client->hoverms = 0;\n\t\t}\n\t\tif (ucmd->msec > host_client->msecs)\n\t\t{\t//they're over their timeslice allocation\n\t\t\t//if they're not taking the piss then be prepared to truncate the frame. this should hide clockskew without allowing full-on speedcheats.\n\t\t\tif (ucmd->msec > 10)\n\t\t\t\tucmd->msec -= 1;\n\t\t\tif (ucmd->msec > host_client->msecs)\n\t\t\t{\n\t\t\t\tif (sv_showpredloss.ival)\n\t\t\t\t\tCon_Printf(\"%s: ignoring %g msecs (anti speed cheat)\\n\", host_client->name, ucmd->msec);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tucmd->msec = host_client->msecs;\n\t\t}\n\t\thost_client->msecs -= ucmd->msec;\n#else\n\t\t// DMW copied this KK hack copied from QuakeForge anti-cheat\n\t\t// (also extra inside parm on all SV_RunCmds that follow)\n\n\t\t//FIXME: update protocol to use server's timestamps instead of msecs over the wire, obsoleting speed cheat checks (by allowing the server to clamp sanely).\n\n\t\tif (!host_client->last_check)\n\t\t{\n\t\t\thost_client->msecs = 0;\n\t\t\thost_client->last_check = realtime;\n\t\t}\n\t\thost_client->msecs += ucmd->msec;\n\n\t\tif ((tmp_time = realtime - host_client->last_check) >= sv_cheatspeedchecktime.value)\n\t\t{\n\t\t\textern int\tisPlugin;\n\t\t\tdouble  tmp_time;\n\t\t\ttmp_time = tmp_time * 1000.0 * sv_cheatpc.value/100.0;\n\t\t\tif (host_client->msecs > tmp_time &&\n\t\t\t\tisPlugin < 2)\t//qc-debugging can result in WEIRD timings, so don't warn about weird timings if we're likely to get blocked out for long periods\n\t\t\t{\n\t\t\t\thost_client->msec_cheating++;\n\t\t\t\tSV_BroadcastTPrintf(PRINT_HIGH,\n\t\t\t\t\t\t\"Speed cheat possibility, analyzing:\\n  %d>%.1f %d for: %s\\n\",\n\t\t\t\t\t\t\thost_client->msecs, tmp_time,\n\t\t\t\t\t\t\thost_client->msec_cheating, host_client->name);\n\n\t\t\t\tif (host_client->msec_cheating >= 2)\n\t\t\t\t{\n\t\t\t\t\tchar adr[MAX_ADR_SIZE];\n\t\t\t\t\tSV_BroadcastTPrintf(PRINT_HIGH,\n\t\t\t\t\t\t\t\"%s was kicked for speedcheating (%s)\\n\",\n\t\t\t\t\t\t\t\thost_client->name, NET_AdrToString(adr, sizeof(adr), &host_client->netchan.remote_address));\n\t\t\t\t\thost_client->drop = true;\t//drop later\n\t\t\t\t}\n\t\t\t}\n\n\t\t\thost_client->msecs = 0;\n\t\t\thost_client->last_check = realtime;\n\t\t}\n\t\t// end KK hack copied from QuakeForge anti-cheat\n\t\t//it's amazing how code get's copied around...\n#endif\n\t}\n\n\tif (SV_RunFullQCMovement(host_client, ucmd))\n\t{\n\t\treturn;\n\t}\n\n\n\tcmd = *ucmd;\n\n\t// chop up very long commands\n\tif (cmd.msec > 50)\n\t{\n\t\toldmsec = ucmd->msec;\n\t\tcmd.msec = oldmsec/2;\n\t\tSV_RunCmd (&cmd, true);\n\t\tcmd.msec = oldmsec/2 + (oldmsec&1);\t//give them back their msec.\n\t\tcmd.impulse = 0;\n\t\tSV_RunCmd (&cmd, true);\n\t\treturn;\n\t}\n\n\tVALGRIND_MAKE_MEM_UNDEFINED(&pmove, sizeof(pmove));\n\n\thost_frametime = ucmd->msec * 0.001;\n\thost_frametime *= sv.gamespeed;\n\tif (host_frametime > 0.1)\n\t\thost_frametime = 0.1;\n\n#ifdef SERVER_DEMO_PLAYBACK\n\tif (sv.demostatevalid)\n\t{\t//spectators watching MVDs do not affect the running progs.\n\t\tplayer_mins[0] = -16;\n\t\tplayer_mins[1] = -16;\n\t\tplayer_mins[2] = -24;\n\n\t\tplayer_maxs[0] = 16;\n\t\tplayer_maxs[1] = 16;\n\t\tplayer_maxs[2] = 32;\n\n\t\tpmove.angles[0] = SHORT2ANGLE(ucmd->angles[0]);\n\t\tpmove.angles[1] = SHORT2ANGLE(ucmd->angles[1]);\n\t\tpmove.angles[2] = SHORT2ANGLE(ucmd->angles[2]);\n\n\t\tVectorCopy (host_client->specorigin, pmove.origin);\n\t\tVectorCopy (host_client->specvelocity, pmove.velocity);\n\n\t\tif (host_client->zquake_extensions & Z_EXT_PM_TYPE_NEW)\n\t\t\tpmove.pm_type = PM_SPECTATOR;\n\t\telse\n\t\t\tpmove.pm_type = PM_OLD_SPECTATOR;\n\t\tpmove.jump_held = host_client->jump_held;\n\t\tpmove.jump_msec = 0;\n\t\tpmove.waterjumptime = 0;\n\t\tpmove.numphysent = 1;\n\t\tpmove.physents[0].model = sv.world.worldmodel;\n\t\tpmove.cmd = *ucmd;\n\t\tpmove.hullnum = SV_HullNumForPlayer(0, player_mins, player_maxs);\n\t\tpmove.capsule = (sv_player->xv->geomtype == GEOMTYPE_CAPSULE);\n\n\t\tmovevars.entgravity = 0;\n\t\tmovevars.maxspeed = 0;\n\t\tmovevars.bunnyspeedcap = pm_bunnyspeedcap.value;\n\t\tmovevars.ktjump = pm_ktjump.value;\n\t\tmovevars.slidefix = (pm_slidefix.value != 0);\n\t\tmovevars.airstep = (pm_airstep.value != 0);\n\t\tmovevars.pground = (pm_pground.value != 0);\n\t\tmovevars.stepdown = (pm_stepdown.value != 0);\n\t\tmovevars.walljump = (pm_walljump.value);\n\t\tmovevars.slidyslopes = (pm_slidyslopes.value!=0);\n\t\tmovevars.autobunny = (pm_autobunny.value!=0);\n\t\tmovevars.watersinkspeed = *pm_watersinkspeed.string?pm_watersinkspeed.value:60;\n\t\tmovevars.flyfriction = *pm_flyfriction.string?pm_flyfriction.value:4;\n\t\tmovevars.edgefriction = *pm_edgefriction.string?pm_edgefriction.value:2;\n\t\tmovevars.coordtype = host_client->netchan.netprim.coordtype;\n\t\tmovevars.flags\t\t\t\t= MOVEFLAG_VALID|MOVEFLAG_NOGRAVITYONGROUND|(*pm_edgefriction.string?0:MOVEFLAG_QWEDGEBOX);\n\n\t\tfor (i=0 ; i<3 ; i++)\n\t\t{\n\t\t\tpmove_mins[i] = pmove.origin[i] - 256;\n\t\t\tpmove_maxs[i] = pmove.origin[i] + 256;\n\t\t}\n\n\t\tPM_PlayerMove (sv.gamespeed);\n\n\t\tVectorCopy (pmove.origin, host_client->specorigin);\n\t\tVectorCopy (pmove.velocity, host_client->specvelocity);\n\n\t\treturn;\n\t}\n#endif\n\n#ifdef SVCHAT\n\tif (SV_ChatMove(ucmd->impulse))\n\t{\n\t\tucmd->buttons = 0;\n\t\tucmd->impulse = 0;\n\t\tucmd->forwardmove = ucmd->sidemove = ucmd->upmove = 0;\n\t}\n#endif\n\n\tif (!sv_player->v->fixangle)\n\t{\n\t\tsv_player->v->v_angle[0] = SHORT2ANGLE(ucmd->angles[0]);\n\t\tsv_player->v->v_angle[1] = SHORT2ANGLE(ucmd->angles[1]);\n\t\tsv_player->v->v_angle[2] = SHORT2ANGLE(ucmd->angles[2]);\n\t}\n\n#ifdef HEXEN2\n\tif (progstype == PROG_H2)\n\t{\n\t\t//fixme: should probably support rtlights, but this is a server, so urgh.\n\t\tif (sv.world.worldmodel && sv.world.worldmodel->funcs.LightPointValues)\n\t\t{\n\t\t\tvec3_t diffuse, ambient, dir;\n\t\t\tfloat lev = 0;\n#if defined(RTLIGHTS) && !defined(SERVERONLY)\n\t\t\tSh_CalcPointLight(sv_player->v->origin, ambient);\n\t\t\tlev += VectorLength(ambient);\n\n\t\t\tif (!r_shadow_realtime_world.ival || r_shadow_realtime_world_lightmaps.value)\n#endif\n\t\t\t{\n\t\t\t\tsv.world.worldmodel->funcs.LightPointValues(sv.world.worldmodel, sv_player->v->origin, ambient, diffuse, dir);\n\t\t\t\tlev += (VectorLength(ambient) + VectorLength(diffuse)/2.0)/256;\n\t\t\t}\n\t\t\tsv_player->xv->light_level = lev * 255;\n\t\t}\n\t\telse\t\n\t\t\tsv_player->xv->light_level = 128;\t//don't know, some dummy value.\n\t}\n#endif\n\n\tSV_SetEntityButtons(sv_player, ucmd->buttons);\n\tif (ucmd->impulse && SV_FilterImpulse(ucmd->impulse, host_client->trustlevel))\n\t\tsv_player->v->impulse = ucmd->impulse;\n\n\tif (host_client->penalties & BAN_CUFF)\n\t{\n\t\tsv_player->v->impulse = 0;\n\t\tsv_player->v->button0 = 0;\n\t}\n\n\tif (host_client->state && host_client->protocol != SCP_BAD)\n\t{\n\t\tsv_player->xv->movement[0] = ucmd->forwardmove;\n\t\tsv_player->xv->movement[1] = ucmd->sidemove;\n\t\tsv_player->xv->movement[2] = ucmd->upmove;\n\t}\n\n\tWPhys_CheckVelocity(&sv.world, (wedict_t*)sv_player);\n\n//\n// angles\n// show 1/3 the pitch angle and all the roll angle\n\tif (sv_player->v->health > 0 && sv_player->v->movetype)\n\t{\n\t\tif (sv_player->v->movetype == MOVETYPE_6DOF)\n\t\t{\n\t\t\tsv_player->v->angles[PITCH] = r_meshpitch.value * sv_player->v->v_angle[PITCH];\n\t\t\tsv_player->v->angles[YAW] = sv_player->v->v_angle[YAW];\n\t\t\tsv_player->v->angles[ROLL] = sv_player->v->v_angle[ROLL];\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (!sv_player->v->fixangle)\n\t\t\t{\n\t\t\t\tsv_player->v->angles[PITCH] = r_meshpitch.value * sv_player->v->v_angle[PITCH]/3;\n\t\t\t\tsv_player->v->angles[YAW] = sv_player->v->v_angle[YAW];\n\t\t\t}\n\t\t\tsv_player->v->angles[ROLL] =\n\t\t\t\tV_CalcRoll (sv_player->v->angles, sv_player->v->velocity)*4;\n\t\t}\n\t}\n\n\tif (SV_PlayerPhysicsQC && !host_client->spectator)\n\t{\t//player movement tweaks that fuck over player prediction.\n\t\tpr_global_struct->frametime = host_frametime;\n\t\tpr_global_struct->time = sv.time;\n\t\tWPhys_RunEntity(&sv.world, (wedict_t*)sv_player);\n\t\treturn;\n\t}\n\n\n\tfor (i = 0; i < 3; i++)\n\t{\n\t\tif (sv_player->xv->punchangle[i] < 0)\n\t\t{\n\t\t\tsv_player->xv->punchangle[i] += 10 * host_frametime;\n\t\t\tif (sv_player->xv->punchangle[i] > 0)\n\t\t\t\tsv_player->xv->punchangle[i] = 0;\n\t\t}\n\t\tif (sv_player->xv->punchangle[i] > 0)\n\t\t{\n\t\t\tsv_player->xv->punchangle[i] -= 10 * host_frametime;\n\t\t\tif (sv_player->xv->punchangle[i] < 0)\n\t\t\t\tsv_player->xv->punchangle[i] = 0;\n\t\t}\n\t}\n\n\tif (!host_client->spectator)\n\t{\n\t\tvec_t oldvz;\n\t\tpr_global_struct->frametime = host_frametime;\n\n\t\tpr_global_struct->time = sv.time;\n\t\tpr_global_struct->self = EDICT_TO_PROG(svprogfuncs, sv_player);\n\n\t\toldvz = sv_player->v->velocity[2];\n\t\tif (progstype != PROG_QW)\n\t\t{\n\t\t\tjumpable = ((int)sv_player->v->flags & FL_JUMPRELEASED) && ((int)sv_player->v->flags & FL_ONGROUND);\n\n\t\t\tpmove.waterjumptime = sv_player->v->teleport_time;\n\t\t\tif (pmove.waterjumptime > sv.time)\n\t\t\t\tsv_player->v->flags = (int)sv_player->v->flags | FL_WATERJUMP;\n\t\t}\n\t\telse\n\t\t\tjumpable = false;\n\n#ifdef VM_Q1\n\t\tif (svs.gametype == GT_Q1QVM)\n\t\t\tQ1QVM_PlayerPreThink();\n\t\telse\n#endif\n\t\t\tif (pr_global_ptrs->PlayerPreThink)\n\t\t\t\tPR_ExecuteProgram (svprogfuncs, *pr_global_ptrs->PlayerPreThink);\n\n\t\tif (progstype != PROG_QW)\n\t\t{\n\t\t\tif (sv_player->v->velocity[2] == 225 && sv_player->v->teleport_time > sv.time)\n\t\t\t{\n\t\t\t\tsv_player->v->velocity[2] = oldvz;\n\t\t//\t\tCon_Printf(\"Waterjump detected\\n\");\n\t\t\t}\n\t\t\tsv_player->v->flags = (int)sv_player->v->flags & ~FL_WATERJUMP;\n\t\t\tsv_player->v->teleport_time = pmove.waterjumptime;\n\t\t\tif (jumpable && !((int)sv_player->v->flags & FL_JUMPRELEASED))\t//hmm... a jump was hit.\n\t\t\t\tsv_player->v->velocity[2] -= 270;\n\t\t}\n\n\t\t{\n\t\t\tdouble ptime = sv.world.physicstime;\n\t\t\tsv.world.physicstime = sv.time;\t//urgh, WPhys_RunThink uses the wrong time base\n\t\t\tWPhys_RunThink (&sv.world, (wedict_t*)sv_player);\n\t\t\tsv.world.physicstime = ptime;\n\t\t}\n\n\t\tif (host_client->state && host_client->protocol == SCP_BAD && svs.gametype != GT_Q1QVM)\n\t\t{\n\t\t\t//botclients update their movement during prethink. make sure we use that stuff. ktx has a builtin.\n\t\t\tucmd->angles[0] = (int)(sv_player->v->v_angle[0] * (65535/360.0f));\n\t\t\tucmd->angles[1] = (int)(sv_player->v->v_angle[1] * (65535/360.0f));\n\t\t\tucmd->angles[2] = (int)(sv_player->v->v_angle[2] * (65535/360.0f));\n\n\t\t\tucmd->forwardmove = sv_player->xv->movement[0];\n\t\t\tucmd->sidemove = sv_player->xv->movement[1];\n\t\t\tucmd->upmove = sv_player->xv->movement[2];\n\t\t}\n\t}\n\n//\tmemset(&pmove, 0, sizeof(pmove));\n//\tmemset(&movevars, 0, sizeof(movevars));\n\n\tpmove.player_mins[0] = sv_player->v->mins[0];\n\tpmove.player_mins[1] = sv_player->v->mins[1];\n\tpmove.player_mins[2] = sv_player->v->mins[2];\n\n\tpmove.player_maxs[0] = sv_player->v->maxs[0];\n\tpmove.player_maxs[1] = sv_player->v->maxs[1];\n\tpmove.player_maxs[2] = sv_player->v->maxs[2];\n\n\tif (sv_player->xv->gravitydir[2] || sv_player->xv->gravitydir[1] || sv_player->xv->gravitydir[0])\n\t\tVectorCopy(sv_player->xv->gravitydir, pmove.gravitydir);\n\telse\n\t\tVectorCopy(sv.world.g.defaultgravitydir, pmove.gravitydir);\n\n\tVectorCopy(sv_player->v->origin, pmove.origin);\n\tVectorCopy(sv_player->v->oldorigin, pmove.safeorigin);\n\tpmove.safeorigin_known = progstype != PROG_QW;\n\n\tVectorCopy (sv_player->v->velocity, pmove.velocity);\n\tVectorCopy (sv_player->v->v_angle, pmove.angles);\n\n\tpmove.pm_type = SV_PMTypeForClient (host_client, sv_player);\n\tpmove.onground = ((int)sv_player->v->flags & FL_ONGROUND) != 0;\n\tpmove.jump_held = host_client->jump_held;\n\tpmove.jump_msec = 0;\n\tif (progstype != PROG_QW)\t//this is just annoying.\n\t\tpmove.waterjumptime = sv_player->v->teleport_time - sv.time;\n\telse\n\t\tpmove.waterjumptime = sv_player->v->teleport_time;\n\tpmove.numphysent = 1;\n\tpmove.physents[0].model = sv.world.worldmodel;\n\tpmove.cmd = *ucmd;\n\tpmove.skipent = -1;\n\tpmove.capsule = (sv_player->xv->geomtype == GEOMTYPE_CAPSULE);\n\n\tmovevars.entgravity = host_client->entgravity/movevars.gravity;\n\tmovevars.maxspeed = host_client->maxspeed;\n\tmovevars.bunnyspeedcap = pm_bunnyspeedcap.value;\n\tmovevars.ktjump = pm_ktjump.value;\n\tmovevars.slidefix = (pm_slidefix.value != 0);\n\tmovevars.airstep = (pm_airstep.value != 0);\n\tmovevars.pground = (pm_pground.value != 0);\n\tmovevars.stepdown = (pm_stepdown.value != 0);\n\tmovevars.walljump = (pm_walljump.value);\n\tmovevars.slidyslopes = (pm_slidyslopes.value!=0);\n\tmovevars.bunnyfriction = (pm_bunnyfriction.value!=0);\n\tmovevars.autobunny = (pm_autobunny.value!=0);\n\tmovevars.watersinkspeed = *pm_watersinkspeed.string?pm_watersinkspeed.value:60;\n\tmovevars.flyfriction = *pm_flyfriction.string?pm_flyfriction.value:4;\n\tmovevars.edgefriction = *pm_edgefriction.string?pm_edgefriction.value:2;\n\tmovevars.coordtype = host_client->netchan.netprim.coordtype;\n\tmovevars.flags\t\t\t\t= MOVEFLAG_VALID|MOVEFLAG_NOGRAVITYONGROUND|(*pm_edgefriction.string?0:MOVEFLAG_QWEDGEBOX);\n\n// should already be folded into host_client->maxspeed\n//\tif (sv_player->xv->hasted)\n//\t\tmovevars.maxspeed*=sv_player->xv->hasted;\n\n\tfor (i=0 ; i<3 ; i++)\n\t{\n\t\tpmove_mins[i] = pmove.origin[i] - 256;\n\t\tpmove_maxs[i] = pmove.origin[i] + 256;\n\t}\n\tsv_player->xv->pmove_flags = (int)sv_player->xv->pmove_flags & ~PMF_LADDER;\t//assume not touching ladder trigger\n\tAddAllLinksToPmove (&sv.world, (wedict_t*)sv_player);\n\n\tif ((int)sv_player->xv->pmove_flags & PMF_LADDER)\n\t\tpmove.onladder = true;\n\telse\n\t\tpmove.onladder = false;\n\n\tpmove.world = &sv.world;\n#if 0\n{\n\tint before, after;\n\nbefore = PM_TestPlayerPosition (pmove.origin);\n\tPlayerMove ();\nafter = PM_TestPlayerPosition (pmove.origin);\n\nif (sv_player->v->health > 0 && before && !after )\n\tCon_Printf (\"player %s got stuck in playermove!!!!\\n\", host_client->name);\n}\n#else\n\tPM_PlayerMove (sv.gamespeed);\n#endif\n\tpmove.world = NULL;\n\n\tif (host_client->state && host_client->protocol != SCP_BAD)\n\t{\n\t\tvec3_t delta;\n\t\tdelta[0] = pmove.angles[0] - SHORT2ANGLE(pmove.cmd.angles[0]);\n\t\tdelta[1] = pmove.angles[1] - SHORT2ANGLE(pmove.cmd.angles[1]);\n\t\tdelta[2] = pmove.angles[2] - SHORT2ANGLE(pmove.cmd.angles[2]);\n\t\tif (delta[0] || delta[1] || delta[2])\n\t\t{\n\t\t\tif (host_client->fteprotocolextensions2 & PEXT2_SETANGLEDELTA)\n\t\t\t{\n\t\t\t\tclient_t *cl = ClientReliableWrite_BeginSplit(host_client, svcfte_setangledelta, 7);\n\t\t\t\tfor (i=0 ; i < 3 ; i++)\n\t\t\t\t\tClientReliableWrite_Angle16 (cl, delta[i]);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tclient_t *cl = ClientReliableWrite_BeginSplit(host_client, svc_setangle, 7);\n\t\t\t\tfor (i=0 ; i < 3 ; i++)\n\t\t\t\t\tClientReliableWrite_Angle (cl, pmove.angles[i]);\n\t\t\t}\n\t\t}\n\n\t}\n\n\thost_client->jump_held = pmove.jump_held;\n\tif (progstype != PROG_QW)\t//this is just annoying.\n\t{\n\t\tif (pmove.waterjumptime)\n\t\t\tsv_player->v->teleport_time = pmove.waterjumptime + sv.time;\n\t}\n\telse\n\t\tsv_player->v->teleport_time = pmove.waterjumptime;\n\tsv_player->v->waterlevel = pmove.waterlevel;\n\n\tif (pmove.watertype & FTECONTENTS_SOLID)\n\t\tsv_player->v->watertype = Q1CONTENTS_SOLID;\n\telse if (pmove.watertype & FTECONTENTS_SKY)\n\t\tsv_player->v->watertype = Q1CONTENTS_SKY;\n\telse if (pmove.watertype & FTECONTENTS_LAVA)\n\t\tsv_player->v->watertype = Q1CONTENTS_LAVA;\n\telse if (pmove.watertype & FTECONTENTS_SLIME)\n\t\tsv_player->v->watertype = Q1CONTENTS_SLIME;\n\telse if (pmove.watertype & FTECONTENTS_WATER)\n\t\tsv_player->v->watertype = Q1CONTENTS_WATER;\n\telse\n\t\tsv_player->v->watertype = Q1CONTENTS_EMPTY;\n\n\tif (pmove.jump_held && movevars.autobunny)\t//make sure the qc thinks we released the button at some point, triggering a new jump sound.\n\t\tsv_player->v->flags = (int)sv_player->v->flags | FL_JUMPRELEASED;\n\tif (pmove.onground)\n\t{\n\t\tsv_player->v->flags = (int)sv_player->v->flags | FL_ONGROUND;\n\t\tsv_player->v->groundentity = EDICT_TO_PROG(svprogfuncs, EDICT_NUM_PB(svprogfuncs, pmove.physents[pmove.groundent].info));\n\t}\n\telse\n\t\tsv_player->v->flags = (int)sv_player->v->flags & ~FL_ONGROUND;\n\n\tVectorCopy (pmove.safeorigin, sv_player->v->oldorigin);\n\tVectorCopy (pmove.origin, sv_player->v->origin);\n\tif (!sv_player->v->fixangle)\n\t{\n\t\tVectorCopy (pmove.angles, sv_player->v->v_angle);\n\n\t\t//some clients (especially NQ ones) attempt to cheat. don't let it benefit them.\n\t\t//some things would break though.\n\t\tif ((!sv_player->xv->gravitydir[0] && !sv_player->xv->gravitydir[1] && !sv_player->xv->gravitydir[2]) && sv_player->v->movetype == MOVETYPE_WALK)\n\t\t{\n\t\t\tfloat minpitch = *sv_minpitch.string?sv_minpitch.value:-70;\n\t\t\tfloat maxpitch = *sv_maxpitch.string?sv_maxpitch.value:80;\n\t\t\tif (sv_player->v->v_angle[0] < minpitch)\n\t\t\t\tsv_player->v->v_angle[0] = minpitch;\n\t\t\tif (sv_player->v->v_angle[0] > maxpitch)\n\t\t\t\tsv_player->v->v_angle[0] = maxpitch;\n\t\t}\n\t}\n\n//\tVectorCopy (pmove.gravitydir, sv_player->xv->gravitydir);\n\tif (pmove.gravitydir[0] || pmove.gravitydir[1] || (pmove.gravitydir[2] && pmove.gravitydir[2] != -1))\n\t{\n\t\tif (!sv_player->v->fixangle)\n\t\t{\n\t\t\t//FIXME: bound to pmove.gravitydir\n\t\t\tvec3_t view[3];\n\t\t\tvec3_t surf[3];\n\t\t\tvec3_t fwd, up;\n\t\t\tAngleVectors(sv_player->v->v_angle, view[0], view[1], view[2]);\n\t\t\t/*calculate the surface axis with up from the pmove code and right/forwards relative to the player's directions*/\n\t\t\tVectorNegate(pmove.gravitydir, surf[2]);\n\t\t\tCrossProduct(view[0], surf[2], surf[1]);\n\t\t\tVectorNormalize(surf[1]);\n\t\t\tCrossProduct(surf[2], surf[1], surf[0]);\n\t\t\t/*interpolate the forward direction to be 1/3rd the player, and 2/3rds the surface forward*/\n\t\t\tVectorInterpolate(surf[0], 0.333, view[0], fwd);\n\t\t\tCrossProduct(surf[1], fwd, up);\n\t\t\t/*we have our player's new axis*/\n\t\t\tVectorAngles(fwd, up, sv_player->v->angles, true);\n\t\t}\n\t}\n\n\tpmove.player_mins[0] = -16;\n\tpmove.player_mins[1] = -16;\n\tpmove.player_mins[2] = -24;\n\n\tpmove.player_maxs[0] = 16;\n\tpmove.player_maxs[1] = 16;\n\tpmove.player_maxs[2] = 32;\n\n\tVectorCopy(sv_player->v->velocity, old_vel);\n\tVectorCopy(pmove.velocity, new_vel);\n\tif (progstype == PROG_QW)\n\t\tVectorCopy(new_vel, sv_player->v->velocity);\n\n\n\tif (!host_client->spectator)\n\t{\n\t\t// link into place and touch triggers\n\t\tWorld_LinkEdict (&sv.world, (wedict_t*)sv_player, true);\n\n/*\t\tfor (i = 0; i < pmove.numphysent; i++)\n\t\t{\n\n\t\t}\n*/\n\t\t// touch other objects\n\t\tfor (i=0 ; i<pmove.numtouch ; i++)\n\t\t{\n\t\t\tif (sv_pushplayers.value)\n\t\t\t{\n\t\t\t\tn = pmove.physents[pmove.touchindex[i]].info;\n\t\t\t\tif (n && n <= sv.allocated_client_slots)\n\t\t\t\t{\n\t\t\t\t\tfloat vel;\n\t\t\t\t\tvec3_t dir;\n\t\t\t\t\tvec3_t svel;\n\t\t\t\t\tent = EDICT_NUM_PB(svprogfuncs, n);\n\t\t\t\t\tVectorSubtract(ent->v->origin, sv_player->v->origin, dir);\n\t\t\t\t\tVectorNormalize(dir);\n\t\t\t\t\tVectorCopy(sv_player->v->velocity, svel);\n\t\t\t\t\tVectorNormalize(svel);\n\t\t\t\t\tvel = DotProduct(svel, dir);\n\t\t\t\t\tVectorMA(ent->v->velocity, sv_pushplayers.value*vel, dir, ent->v->velocity);\n\t\t\t\t}\n\t\t\t}\n\n\n\t\t\tif (pmove.physents[pmove.touchindex[i]].notouch)\n\t\t\t\tcontinue;\n\t\t\tn = pmove.physents[pmove.touchindex[i]].info;\n\t\t\tent = EDICT_NUM_PB(svprogfuncs, n);\n\n\t\t\tif (n >= playertouchmax || (playertouch[n>>3]&(1<<(n&7))))\n\t\t\t\tcontinue;\n\t\t\tplayertouch[n>>3] |= 1 << (n&7);\n\n\t\t\tif (ent->v->touch)\n\t\t\t{\n\t\t\t\tif (progstype != PROG_QW && VectorCompare(sv_player->v->velocity, old_vel))\n\t\t\t\t{\n\t\t\t\t\tVectorCopy(pmove.touchvel[i], old_vel);\n\t\t\t\t\tVectorCopy(pmove.touchvel[i], sv_player->v->velocity);\n\t\t\t\t}\n\t\t\t\tsv.world.Event_Touch(&sv.world, (wedict_t*)ent, (wedict_t*)sv_player, NULL);\n\t\t\t}\n\n\t\t\tif (sv_player->v->touch && !ED_ISFREE(ent))\n\t\t\t\tsv.world.Event_Touch(&sv.world, (wedict_t*)sv_player, (wedict_t*)ent, NULL);\n\t\t}\n\t}\n\n\tif (progstype != PROG_QW)\n\t{\n\t\tif (VectorCompare(sv_player->v->velocity, old_vel))\n\t\t\tVectorCopy(new_vel, sv_player->v->velocity);\n\t}\n}\n\n/*\n===========\nSV_PostRunCmd\n===========\nDone after running a player command.\n*/\nvoid SV_PostRunCmd(void)\n{\n\t// run post-think\n\n\tif (!svprogfuncs)\n\t\treturn;\n\n#ifdef VM_Q1\n\tif (svs.gametype == GT_Q1QVM)\n\t{\n\t\tpr_global_struct->time = sv.time;\n\t\tpr_global_struct->self = EDICT_TO_PROG(svprogfuncs, sv_player);\n\t\tQ1QVM_PostThink();\n\t}\n\telse\n#endif\n\t{\n\t\tif (!host_client->spectator)\n\t\t{\n\t\t\tpr_global_struct->time = sv.time;\n\t\t\tpr_global_struct->self = EDICT_TO_PROG(svprogfuncs, sv_player);\n\n\t\t\tif (pr_global_ptrs->PlayerPostThink)\n\t\t\t\tPR_ExecuteProgram (svprogfuncs, *pr_global_ptrs->PlayerPostThink);\n\n\t\t\tWPhys_RunNewmis (&sv.world);\n\t\t}\n\t\telse if (SpectatorThink)\n\t\t{\n\t\t\tpr_global_struct->time = sv.time;\n\t\t\tpr_global_struct->self = EDICT_TO_PROG(svprogfuncs, sv_player);\n\t\t\tPR_ExecuteProgram (svprogfuncs, SpectatorThink);\n\t\t}\n\t}\n}\n\nstatic void SV_ReadPrydonCursor(usercmd_t *cmd)\n{\n\teval_t *cursor_screen, *cursor_start, *cursor_impact, *cursor_entitynumber;\n\n\tcmd->cursor_screen[0] = MSG_ReadShort() * (1.0f / 32767.0f);\n\tcmd->cursor_screen[1] = MSG_ReadShort() * (1.0f / 32767.0f);\n\tcmd->cursor_start[0] = MSG_ReadFloat();\n\tcmd->cursor_start[1] = MSG_ReadFloat();\n\tcmd->cursor_start[2] = MSG_ReadFloat();\n\tcmd->cursor_impact[0] = MSG_ReadFloat();\n\tcmd->cursor_impact[1] = MSG_ReadFloat();\n\tcmd->cursor_impact[2] = MSG_ReadFloat();\n\tcmd->cursor_entitynumber = MSGSV_ReadEntity(host_client);\n\n\t/* client is sending junk or trying to crash us -eukara */\n\tif (cmd->cursor_entitynumber < 0 || cmd->cursor_entitynumber >= sv.world.num_edicts) {\n\t\tcmd->cursor_entitynumber = 0;\n\t}\n\n\tif (svprogfuncs)\n\t{\n\t\tcursor_screen\t= svprogfuncs->GetEdictFieldValue(svprogfuncs, host_client->edict, \"cursor_screen\", ev_vector, NULL);\n\t\tcursor_start\t= svprogfuncs->GetEdictFieldValue(svprogfuncs, host_client->edict, \"cursor_trace_start\", ev_vector, NULL);\n\t\tcursor_impact\t= svprogfuncs->GetEdictFieldValue(svprogfuncs, host_client->edict, \"cursor_trace_endpos\", ev_vector, NULL);\n\t\tcursor_entitynumber\t= svprogfuncs->GetEdictFieldValue(svprogfuncs, host_client->edict, \"cursor_trace_ent\", ev_entity, NULL);\n\t}\n\telse\n\t{\n\t\tcursor_screen\t= NULL;\n\t\tcursor_start\t= NULL;\n\t\tcursor_impact\t= NULL;\n\t\tcursor_entitynumber\t= NULL;\n\t}\n\tif (cursor_screen)\n\t\tVector2Copy(cmd->cursor_screen, cursor_screen->_vector);\n\tif (cursor_start)\n\t\tVectorCopy(cmd->cursor_start, cursor_start->_vector);\n\tif (cursor_impact)\n\t\tVectorCopy(cmd->cursor_impact, cursor_impact->_vector);\n\n\t// as requested by FrikaC, cursor_trace_ent is reset to world if the\n\t// entity is free at time of receipt\n\tif (!svprogfuncs || ED_ISFREE(EDICT_NUM_UB(svprogfuncs, cmd->cursor_entitynumber)))\n\t\tcmd->cursor_entitynumber = 0;\n\tif (msg_badread) Con_Printf(\"SV_ReadPrydonCursor: badread at %s:%i\\n\", __FILE__, __LINE__);\n\n\tif (cursor_entitynumber) cursor_entitynumber->edict = cmd->cursor_entitynumber;\n}\n\nvoid SV_ReadQCRequest(void)\n{\n\tint e;\n\tchar args[8];\n\tchar *rname, *fname;\n\tfunc_t f;\n\tint i;\n\tglobalvars_t *pr_globals;\n\tclient_t *cl = host_client;\n\tedict_t *ed;\n\tchar *p;\n\n\tif (!svprogfuncs)\n\t{\n\t\tmsg_badread = true;\n\t\treturn;\n\t}\n\n\tpr_globals = PR_globals(svprogfuncs, PR_CURRENT);\n\n\tfor (i = 0; ; )\n\t{\n\t\tqbyte ev = MSG_ReadByte();\n\t\tif (ev >= 200 && ev < 200+MAX_SPLITS)\n\t\t{\n\t\t\tev -= 200;\n\t\t\twhile (ev-- && cl)\n\t\t\t\tcl = cl->controlled;\n\t\t\tcontinue;\n\t\t}\n\t\tif (i >= sizeof(args)-1)\n\t\t{\n\t\t\tif (ev != ev_void)\n\t\t\t{\n\t\t\t\tmsg_badread = true;\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tgoto done;\n\t\t}\n\t\tswitch(ev)\n\t\t{\n\t\tdefault:\n\t\t\targs[i] = '?';\n\t\t\tG_INT(OFS_PARM0+i*3) = MSG_ReadLong();\n\t\t\tbreak;\n\t\tcase ev_void:\n\t\t\tgoto done;\n\t\tcase ev_float:\n\t\t\targs[i] = 'f';\n\t\t\tG_FLOAT(OFS_PARM0+i*3) = MSG_ReadFloat();\n\t\t\tbreak;\n\t\tcase ev_double:\n\t\t\targs[i] = 'F';\n\t\t\tG_DOUBLE(OFS_PARM0+i*3) = MSG_ReadDouble();\n\t\t\tbreak;\n\t\tcase ev_vector:\n\t\t\targs[i] = 'v';\n\t\t\tG_FLOAT(OFS_PARM0+i*3+0) = MSG_ReadFloat();\n\t\t\tG_FLOAT(OFS_PARM0+i*3+1) = MSG_ReadFloat();\n\t\t\tG_FLOAT(OFS_PARM0+i*3+2) = MSG_ReadFloat();\n\t\t\tbreak;\n\t\tcase ev_integer:\n\t\t\targs[i] = 'i';\n\t\t\tG_INT(OFS_PARM0+i*3) = MSG_ReadLong();\n\t\t\tbreak;\n\t\tcase ev_uint:\n\t\t\targs[i] = 'u';\n\t\t\tG_UINT(OFS_PARM0+i*3) = MSG_ReadLong();\n\t\t\tbreak;\n\t\tcase ev_int64:\n\t\t\targs[i] = 'I';\n\t\t\tG_INT64(OFS_PARM0+i*3) = MSG_ReadInt64();\n\t\t\tbreak;\n\t\tcase ev_uint64:\n\t\t\targs[i] = 'U';\n\t\t\tG_UINT64(OFS_PARM0+i*3) = MSG_ReadUInt64();\n\t\t\tbreak;\n\t\tcase ev_string:\n\t\t\targs[i] = 's';\n\t\t\tG_INT(OFS_PARM0+i*3) = PR_TempString(svprogfuncs, MSG_ReadString());\n\t\t\tbreak;\n\t\tcase ev_pointer:\n\t\t\targs[i] = 'p';\n\t\t\tif (i == 0 || args[i-1] != 'i')\n\t\t\t\tbreak;\t//requires the last to have been an int\n\t\t\te = G_UINT(OFS_PARM0+(i-1)*3);\n\t\t\tif (e < 0 || e > 1<<16)\n\t\t\t\tbreak;\t//and not excessive\n\t\t\tG_INT(OFS_PARM0+i*3) = svprogfuncs->AllocTempString(svprogfuncs, &p, e+1);\n\t\t\tp[e] = 0; //always null terminate. because we can.\n\t\t\tMSG_ReadData(p, e);\n\t\t\tbreak;\n\t\tcase ev_entity:\n\t\t\targs[i] = 'e';\n\t\t\te = MSGSV_ReadEntity(host_client);\n\t\t\tif (e < 0 || e >= sv.world.num_edicts)\n\t\t\t\te = 0;\n\t\t\ted = EDICT_NUM_PB(svprogfuncs, e);\n\t\t\tif (!ed)\n\t\t\t{\n\t\t\t\ted = (edict_t*)sv.world.edicts;\n\t\t\t\tCon_Printf(\"client %s sent invalid entity\\n\", host_client->name);\n\t\t\t\thost_client->drop = true;\n\t\t\t}\n\t\t\tG_INT(OFS_PARM0+i*3) = EDICT_TO_PROG(svprogfuncs, ed);\n\t\t\tbreak;\n\t\t}\n\t\ti++;\n\t}\n\ndone:\n\targs[i] = 0;\n\trname = MSG_ReadString();\n\t//We used to use Cmd_foo_args, but that conflicts with a zquake extension and would cause [e]zquake mods that use it to be remotely exploitable (mostly crashes from uninitialised args though).\n\t//Instead, we've switched to some more weird prefix that's much less likly to conflict.\n\tif (i)\n\t\tfname = va(\"CSEv_%s_%s\", rname, args);\n\telse if (strchr(rname, '_'))\t//this is awkward, as not forcing an underscore would allow people to mis-call things with lingering data (the alternative is to block underscores entirely).\n\t\tfname = va(\"CSEv_%s_\", rname);\n\telse\n\t\tfname = va(\"CSEv_%s\", rname);\n\tf = PR_FindFunction(svprogfuncs, fname, PR_ANY);\n#ifdef HAVE_LEGACY\n\tif (!f)\n\t{\n\t\tif (i)\n\t\t\trname = va(\"Cmd_%s_%s\", rname, args);\n\t\telse\n\t\t\trname = va(\"Cmd_%s\", rname);\n\t\tf = PR_FindFunction(svprogfuncs, rname, PR_ANY);\n\t\tif (f)\n\t\t{\n\t\t\tSV_ClientPrintf(host_client, PRINT_HIGH, \"\\\"%s\\\" is no longer supported.\\n\", rname);\n\t\t\tf = 0;\n\t\t}\n\t}\n#endif\n\tif (host_client->drop)\n\t\t;\n\telse if (!cl)\n\t\t;\t//bad seat! not going to warn as they might have been removed recently\n\telse if (f)\n\t{\n\t\tpr_global_struct->self = EDICT_TO_PROG(svprogfuncs, cl->edict);\n\t\tPR_ExecuteProgram(svprogfuncs, f);\n\t}\n\telse\n\t\tSV_ClientPrintf(host_client, PRINT_HIGH, \"qcrequest \\\"%s\\\" not supported\\n\", fname);\n}\n\nstatic double SVFTE_ExecuteClientMove(client_t *controller, unsigned int moveseq)\n{\n\tclient_t *split = controller;\n\tunsigned int flags = MSG_ReadUInt64();\n\tunsigned int seats = (flags & VRM_SEATS)?MSG_ReadUInt64():1;\n\tunsigned int frames = (flags & VRM_FRAMES)?MSG_ReadUInt64():3;\n\tunsigned int loss = (flags & VRM_LOSS)?MSG_ReadByte():0;\n\tdouble delay = (flags & VRM_DELAY)?MSG_ReadByte()/10000.0:0;\t//networked as 10ths of a millisecond.\n\tunsigned int numacks = (flags & VRM_ACKS)?MSG_ReadUInt64():0;\n\tusercmd_t oldcmd, newcmd;\n\n\tunsigned int seat, frame, a;\n\tqboolean ran;\n\tunsigned int dropsequence;\t//sequence<=this will be ignored as stale\n\n#define VRM_UNSUPPORTED (~(VRM_LOSS|VRM_DELAY|VRM_SEATS|VRM_FRAMES|VRM_ACKS))\n\tif (flags & VRM_UNSUPPORTED)\n\t{\n\t\tif (!msg_badread)\n\t\t\tCon_Printf(\"SVFTE_ExecuteClientMove: unknown input flags %#x\\n\", flags & VRM_UNSUPPORTED);\n\t\tmsg_badread = true;\n\t\treturn 0;\n\t}\n\n\tfor (a = 0; a < numacks; a++)\n\t{\n\t\tcontroller->delta_sequence = MSG_ReadLong();\n\t\tif (controller->delta_sequence == -1)\n\t\t{\n\t\t\tunsigned int e;\n\t\t\tif (controller->pendingdeltabits)\n\t\t\t\tcontroller->pendingdeltabits[0] = UF_SV_REMOVE;\n\t\t\tif (host_client->pendingcsqcbits)\n\t\t\t\tfor (e = 1; e < host_client->max_net_ents; e++)\n\t\t\t\t\tif (host_client->pendingcsqcbits[e] & SENDFLAGS_PRESENT)\n\t\t\t\t\t\thost_client->pendingcsqcbits[e] |= SENDFLAGS_USABLE;\n\t\t}\n\t\tSV_AckEntityFrame(controller, controller->delta_sequence);\n\t}\n\n\tfor (seat = 0; seat < seats; seat++)\n\t{\n\t\tif (!split)\n\t\t{\t//err, they sent too many seats... assume we kicked one. swallow the extra data.\n\t\t\tfor (frame = 0; frame < frames; frame++)\n\t\t\t\tMSGFTE_ReadDeltaUsercmd(&nullcmd, &newcmd);\n\t\t\tcontinue;\n\t\t}\n\n\t\thost_client = split;\n\t\tsv_player = split->edict;\n\n\t\tsplit->lossage = loss;\n\n\t\t//all sorts of reasons why we might not want to do physics here and now.\n\t\tsplit->isindependant = !(sv_nqplayerphysics.ival || split->state < cs_spawned || SV_PlayerPhysicsQC || sv.paused || !sv.world.worldmodel || sv.world.worldmodel->loadstate != MLS_LOADED);\n\n\t\tran = false;\n\t\toldcmd = nullcmd;\n\t\tdropsequence = split->lastcmd.sequence;\n\t\tfor (frame = 0; frame < frames; frame++)\n\t\t{\n\t\t\tMSGFTE_ReadDeltaUsercmd(&oldcmd, &newcmd);\n\t\t\tnewcmd.sequence = moveseq - (frames-frame-1);\n\t\t\toldcmd = newcmd;\n\n\t\t\tif (newcmd.sequence <= dropsequence)\n\t\t\t\tcontinue;\t//this one is a dupe.\n\n\t\t\tnewcmd.msec = newcmd.servertime - split->lastcmd.servertime;\n\n\t\t\tif (oldcmd.msec && newcmd.msec != oldcmd.msec)\n\t\t\t\tif (sv_showpredloss.ival)\n\t\t\t\t\tCon_Printf(\"%s: %g -> %g\\n\", split->name, newcmd.msec, oldcmd.msec);\n\n\t\t\tsplit->lastcmd = newcmd;\n\t\t\tsplit->lastcmd.angles[0] += split->baseangles[0];\n\t\t\tsplit->lastcmd.angles[1] += split->baseangles[1];\n\t\t\tsplit->lastcmd.angles[2] += split->baseangles[2];\n\n\t\t\tif (split->penalties & BAN_CRIPPLED)\n\t\t\t{\n\t\t\t\tsplit->lastcmd.forwardmove = 0;\n\t\t\t\tsplit->lastcmd.sidemove = 0;\n\t\t\t\tsplit->lastcmd.upmove = 0;\n\t\t\t}\n\n\t\t\tif (split->state == cs_spawned)\n\t\t\t{\n\t\t\t\tif (split->isindependant)\n\t\t\t\t{\t//this protocol uses bigger timestamps instead of msecs\n\t\t\t\t\tunsigned int curtime = sv.time*1000;\n\t\t\t\t\tif (split->lastcmd.servertime < split->lastruncmd)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (sv_showpredloss.ival)\n\t\t\t\t\t\t\tCon_Printf(\"%s: client jumped %u msecs backwards (anti speed cheat buffering)\\n\", split->name, split->lastruncmd - split->lastcmd.servertime);\n\t\t\t\t\t}\n\t\t\t\t\telse if (split->lastruncmd < split->lastcmd.servertime)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (split->lastcmd.servertime > curtime)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t//from last map?... attempted speedcheat?\n\t\t\t\t\t\t\tif (sv_showpredloss.ival)\n\t\t\t\t\t\t\t\tCon_Printf(\"%s: client is %u msecs in the future (anti speed cheat)\\n\", split->name, split->lastcmd.servertime - curtime);\n\t\t\t\t\t\t\tsplit->lastcmd.servertime = curtime;\t//push it back to what we thought it would be...\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (!ran)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tSV_PreRunCmd();\n\t\t\t\t\t\t\tran=true;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tsplit->lastcmd.msec = split->lastcmd.servertime - split->lastruncmd;\n\n\t\t\t\t\t\tSV_Prompt_Input(split, &split->lastcmd);\n\t\t\t\t\t\tSV_RunCmd (&split->lastcmd, false);\n\t\t\t\t\t\tsplit->lastruncmd = split->lastcmd.servertime;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t//handle impulse here, doing it later might mean it got skipped entirely (nq physics often skips frames).\n\t\t\t\t\tif (split->lastcmd.impulse)\n\t\t\t\t\t\tsplit->edict->v->impulse = split->lastcmd.impulse;\n\n\t\t\t\t\tSV_Prompt_Input(split, &split->lastcmd);\n\t\t\t\t\tSV_SetEntityButtons(split->edict, split->lastcmd.buttons);\n\t\t\t\t\tsplit->lastcmd.buttons = 0;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (ran)\n\t\t\tSV_PostRunCmd();\n\n\t\t//for framerate calcs\n\t\tif (split->frameunion.frames)\n\t\t\tsplit->frameunion.frames[split->netchan.outgoing_sequence&UPDATE_MASK].move_msecs = split->lastcmd.msec;\n\t\tsplit->lastcmd.msec = 0;\n\n\t\tsplit = split->controlled;\n\t}\n\n\thost_client = controller;\n\tsv_player = host_client->edict;\n\treturn delay;\n}\n\n/*\n===================\nSV_ExecuteClientMessage\n\nThe current net_message is parsed for the given client\n===================\n*/\nvoid SV_ExecuteClientMessage (client_t *cl)\n{\n\tclient_t *split = cl;\n\tint\t\tc;\n\tchar\t*s;\n\tusercmd_t\toldest, oldcmd, newcmd;\n\tclient_frame_t\t*frame;\n\tvec3_t o;\n\tint\t\tchecksumIndex;\n\tqbyte\tchecksum, calculatedChecksum;\n\tint\t\tseq_hash;\n\n\tif (!cl->frameunion.frames)\n\t{\n\t\tCon_Printf(\"Server bug: No frames!\\n\");\n\t\tcl->send_message = false;\n\t\treturn;\t//shouldn't happen...\n\t}\n\n// calc ping time\n\tframe = &cl->frameunion.frames[cl->netchan.incoming_acknowledged & UPDATE_MASK];\n\n\tif (cl->lastsequence_acknowledged + UPDATE_BACKUP > cl->netchan.incoming_acknowledged && cl->netchan.incoming_sequence-cl->netchan.incoming_acknowledged<UPDATE_BACKUP)\n\t{\n\t\t/*note that if there is packetloss, we can change a single frame's ping_time multiple times\n\t\t  this means that the 'ping' is more latency than ping times*/\n\t\tif (frame->ping_time == -1 || !sv_ping_ignorepl.ival)\n\t\t\tframe->ping_time = realtime - frame->senttime;\t//no more phenomanally low pings please\n\n\t\tif (cl->spectator || sv_minping.value<=0)\n\t\t\tcl->delay = 0;\n\t\telse\n\t\t{\n\t\t\tfloat diff = frame->ping_time*1000 - sv_minping.value;\n\t\t\tif (fabs(diff) > 1)\n\t\t\t{\n\t\t\t\t//FIXME: we should use actual arrival times instead, so we don't get so much noise and seesawing.\n\t\t\t\tdiff = bound(-25, diff, 25);\t//don't swing wildly\n\t\t\t\tcl->delay -= 0.001*(diff/25);\t//scale towards the ideal value\n\t\t\t\tcl->delay = bound(0, cl->delay, UPDATE_BACKUP/77.0);\t//but make sure things don't go crazy\n\t\t\t}\n\t\t}\n\t\tif (cl->penalties & BAN_LAGGED)\n\t\t\tif (cl->delay < 0.2)\n\t\t\t\tcl->delay = 0.2;\n\t}\n\n\tif (sv_antilag.ival || !*sv_antilag.string)\n\t{\n#ifdef warningmsg\n#pragma warningmsg(\"FIXME: make antilag optionally support non-player ents too\")\n#endif\n\t\tcl->laggedents_count = sv.allocated_client_slots;\n\t\tmemcpy(cl->laggedents, frame->laggedplayer, sizeof(*cl->laggedents)*cl->laggedents_count);\n\t\tcl->laggedents_time = frame->laggedtime;\n\t\tcl->laggedents_frac = !*sv_antilag_frac.string?1:sv_antilag_frac.value;\n\t}\n\telse\n\t\tcl->laggedents_count = 0;\n\n\t// make sure the reply sequence number matches the incoming\n\t// sequence number\n\tif (cl->netchan.incoming_sequence >= cl->netchan.outgoing_sequence)\n\t\tcl->netchan.outgoing_sequence = cl->netchan.incoming_sequence;\n\telse\n\t\tcl->send_message = false;\t// don't reply, sequences have slipped\n\n\t// save time for ping calculations\n\tif (cl->frameunion.frames)\n\t{\t//split screen doesn't always have frames.\n\t\tcl->frameunion.frames[cl->netchan.outgoing_sequence & UPDATE_MASK].senttime = realtime;\n\t\tcl->frameunion.frames[cl->netchan.outgoing_sequence & UPDATE_MASK].ping_time = -1;\n\t\tcl->frameunion.frames[cl->netchan.outgoing_sequence & UPDATE_MASK].move_msecs = -1;\n\t\tcl->frameunion.frames[cl->netchan.outgoing_sequence & UPDATE_MASK].packetsizein = net_message.cursize;\n\t\tcl->frameunion.frames[cl->netchan.outgoing_sequence & UPDATE_MASK].packetsizeout = 0;\n\t}\n\n\thost_client = cl;\n\tsv_player = host_client->edict;\n\n\tseq_hash = cl->netchan.incoming_sequence;\n\n\t// mark time so clients will know how much to predict\n\t// other players\n \tcl->localtime = sv.time;\n\tcl->delta_sequence = -1;\t// no delta unless requested\n\twhile (1)\n\t{\n\t\tif (msg_badread)\n\t\t{\n\t\t\tCon_Printf (\"SVQW_ReadClientMessage: badread\\n\");\n\t\t\tSV_DropClient (cl);\n\t\t\treturn;\n\t\t}\n\t\tif (cl->state < cs_connected)\n\t\t{\t//something went badly... just give up instead of crashing.\n\t\t\thost_client = NULL;\n\t\t\tsv_player = NULL;\n\t\t\treturn;\n\t\t}\n\n\t\tc = MSG_ReadByte ();\n\t\tif (c == -1)\n\t\t\tbreak;\n\n//\t\tCon_Printf(\"(%s) %i: %i\\n\", cl->name, msg_readcount, c);\n\n\t\tswitch (c)\n\t\t{\n\t\tdefault:\n\t\t\tCon_Printf (\"SVQW_ReadClientMessage: unknown command char %i\\n\", c);\n\t\t\tSV_DropClient (cl);\n\t\t\treturn;\n\n\t\tcase clc_nop:\n\t\t\tbreak;\n\n\t\tcase clc_delta:\n\t\t\tcl->delta_sequence = MSG_ReadByte ();\n\t\t\tbreak;\n\n\t\tcase clcfte_move:\n\t\t\tframe->ping_time -= SVFTE_ExecuteClientMove(cl, host_client->netchan.incoming_sequence);\n\t\t\tbreak;\n\t\tcase clc_move:\n\t\t\tif (split == cl)\n\t\t\t{\n\t\t\t\t//only the first player is checksummed. its pointless as a security measure now quake is open source.\n\t\t\t\tchecksumIndex = MSG_GetReadCount();\n\t\t\t\tchecksum = (qbyte)MSG_ReadByte ();\n\n\t\t\t\t//only the first player has packetloss calculated.\n\t\t\t\tsplit->lossage = MSG_ReadByte();\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tchecksumIndex = checksum = 0;\n\t\t\t\tif (split)\n\t\t\t\t\tsplit->lossage = cl->lossage;\n\t\t\t}\n\t\t\tMSGQW_ReadDeltaUsercmd (&nullcmd, &oldest, PROTOCOL_VERSION_QW);\n\t\t\toldest.fservertime = frame->laggedtime;\t//not very accurate, but our best guess.\n\t\t\toldest.servertime = frame->laggedtime*1000;\t//not very accurate\n\t\t\tif (split)\n\t\t\t{\n\t\t\t\tVector2Copy(split->lastcmd.cursor_screen, oldest.cursor_screen);\n\t\t\t\tVectorCopy(split->lastcmd.cursor_start, oldest.cursor_start);\n\t\t\t\tVectorCopy(split->lastcmd.cursor_impact, oldest.cursor_impact);\n\t\t\t\toldest.cursor_entitynumber = split->lastcmd.cursor_entitynumber;\n\t\t\t}\n\t\t\tMSGQW_ReadDeltaUsercmd (&oldest, &oldcmd, PROTOCOL_VERSION_QW);\n\t\t\tMSGQW_ReadDeltaUsercmd (&oldcmd, &newcmd, PROTOCOL_VERSION_QW);\n\t\t\toldest.sequence = cl->netchan.incoming_sequence-2;\n\t\t\toldcmd.sequence = cl->netchan.incoming_sequence-1;\n\t\t\tnewcmd.sequence = cl->netchan.incoming_sequence;\n\t\t\tif (!split)\n\t\t\t\tbreak;\t\t// either someone is trying to cheat, or they sent input commands for splitscreen clients they no longer own.\n\n\t\t\tsplit->localtime = sv.time;\n\n\t\t\tif (split->frameunion.frames)\n\t\t\t\tsplit->frameunion.frames[split->netchan.outgoing_sequence&UPDATE_MASK].move_msecs = newcmd.msec;\n\n\t\t\tif (split->state == cs_spawned)\n\t\t\t{\n\t\t\t\tif (split == cl)\n\t\t\t\t{\n\t\t\t\t\t// if the checksum fails, ignore the rest of the packet\n\t\t\t\t\tcalculatedChecksum = COM_BlockSequenceCRCByte(\n\t\t\t\t\t\tnet_message.data + checksumIndex + 1,\n\t\t\t\t\t\tMSG_GetReadCount() - checksumIndex - 1,\n\t\t\t\t\t\tseq_hash);\n\n\t\t\t\t\tif (calculatedChecksum != checksum)\n\t\t\t\t\t{\n\t\t\t\t\t\tCon_DPrintf (\"Failed command checksum for %s(%d) (%d != %d)\\n\",\n\t\t\t\t\t\t\tsplit->name, split->netchan.incoming_sequence, checksum, calculatedChecksum);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (split->penalties & BAN_CRIPPLED)\n\t\t\t\t{\n\t\t\t\t\tsplit->lastcmd.forwardmove = 0;\t//hmmm.... does this work well enough?\n\t\t\t\t\toldest.forwardmove = 0;\n\t\t\t\t\toldcmd.forwardmove = 0;\n\t\t\t\t\tnewcmd.forwardmove = 0;\n\n\t\t\t\t\tsplit->lastcmd.sidemove = 0;\n\t\t\t\t\toldest.sidemove = 0;\n\t\t\t\t\toldcmd.sidemove = 0;\n\t\t\t\t\tnewcmd.sidemove = 0;\n\n\t\t\t\t\tsplit->lastcmd.upmove = 0;\n\t\t\t\t\toldest.upmove = 0;\n\t\t\t\t\toldcmd.upmove = 0;\n\t\t\t\t\tnewcmd.upmove = 0;\n\t\t\t\t}\n\n\t\t\t\thost_client = split;\n\t\t\t\tsv_player = split->edict;\n#ifdef HLSERVER\n\t\t\t\tif (svs.gametype == GT_HALFLIFE)\n\t\t\t\t{\n\t\t\t\t\tSVHL_RunPlayerCommand(split, &oldest, &oldcmd, &newcmd);\n\t\t\t\t}\n\t\t\t\telse\n#endif\n\t\t\t\t\tif (!sv.paused && sv.world.worldmodel && sv.world.worldmodel->loadstate == MLS_LOADED)\n\t\t\t\t{\n\t\t\t\t\tif (sv_nqplayerphysics.ival || split->state < cs_spawned)\n\t\t\t\t\t{\n\t\t\t\t\t\t//store the info for the physics code to pick up the next time it ticks.\n\t\t\t\t\t\t//yeah, nq sucks.\n\t\t\t\t\t\tsplit->isindependant = false;\n\t\t\t\t\t\t/*if (!split->edict->v->fixangle)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tsplit->edict->v->v_angle[0] = newcmd.angles[0]* (360.0/65536);\n\t\t\t\t\t\t\tsplit->edict->v->v_angle[1] = newcmd.angles[1]* (360.0/65536);\n\t\t\t\t\t\t\tsplit->edict->v->v_angle[2] = newcmd.angles[2]* (360.0/65536);\n\t\t\t\t\t\t}*/\n\n\t\t\t\t\t\tif (newcmd.impulse)// && SV_FilterImpulse(newcmd.impulse, host_client->trustlevel))\n\t\t\t\t\t\t\tsplit->edict->v->impulse = newcmd.impulse;\n\n\t\t\t\t\t\tSV_Prompt_Input(split, &newcmd);\n\t\t\t\t\t\tSV_SetEntityButtons(split->edict, newcmd.buttons);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\t//run player physics instantly.\n\t\t\t\t\t\tsplit->isindependant = true;\n\t\t\t\t\t\tSV_PreRunCmd();\n\n\t\t\t\t\t\tif (net_drop < 20)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\twhile (net_drop > 2)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tSV_Prompt_Input(split, &split->lastcmd);\n\t\t\t\t\t\t\t\tSV_RunCmd (&split->lastcmd, false);\n\t\t\t\t\t\t\t\tnet_drop--;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (net_drop > 1)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tSV_Prompt_Input(split, &oldest);\n\t\t\t\t\t\t\t\tSV_RunCmd (&oldest, false);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (net_drop > 0)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tSV_Prompt_Input(split, &oldcmd);\n\t\t\t\t\t\t\t\tSV_RunCmd (&oldcmd, false);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tSV_Prompt_Input(split, &newcmd);\n\t\t\t\t\t\tSV_RunCmd (&newcmd, false);\n\t\t\t\t\t\tsplit->lastruncmd = sv.time*1000;\n\n\t\t\t\t\t\tif (!SV_PlayerPhysicsQC || split->spectator)\n\t\t\t\t\t\t\tSV_PostRunCmd();\n\t\t\t\t\t}\n\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tif (newcmd.impulse)// && SV_FilterImpulse(newcmd.impulse, host_client->trustlevel))\n\t\t\t\t\t\tsv_player->v->impulse = newcmd.impulse;\n\t\t\t\t}\n\n\t\t\t\tsplit->lastcmd = newcmd;\n\t\t\t}\n\t\t\tsplit = split->controlled;\t//so the next splitscreen client gets the next packet.\n\t\t\thost_client = cl;\n\t\t\tsv_player = cl->edict;\n\t\t\tbreak;\n\n\t\tcase clcfte_prydoncursor:\n\t\t\tSV_ReadPrydonCursor(&host_client->lastcmd);\t//lame...\n\t\t\tbreak;\n\t\tcase clcfte_qcrequest:\n\t\t\tSV_ReadQCRequest();\n\t\t\tbreak;\n#ifdef TERRAIN\n\t\tcase clcfte_brushedit:\n\t\t\tif (!SV_Parse_BrushEdit())\n\t\t\t{\n\t\t\t\tSV_DropClient (cl);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tbreak;\n#endif\n\n\t\tcase clcfte_stringcmd_seat:\n\t\t\tc = MSG_ReadByte();\n\t\t\thost_client = cl;\n\t\t\twhile (c --> 0 && host_client->controlled)\n\t\t\t\thost_client = host_client->controlled;\n\t\t\tsv_player = host_client->edict;\n\t\t\t//fall through\n\t\tcase clc_stringcmd:\n\t\t\ts = MSG_ReadString ();\n\t\t\tSV_ExecuteUserCommand (s, false);\n#ifdef NETPREPARSE\n\t\t\tNPP_Flush();\t//flush it just in case there was an error and we stopped preparsing. This is only really needed while debugging.\n#endif\n\t\t\thost_client = cl;\n\t\t\tsv_player = cl->edict;\n\t\t\tbreak;\n\n\t\tcase clc_tmove:\n\t\t\to[0] = MSG_ReadCoord();\n\t\t\to[1] = MSG_ReadCoord();\n\t\t\to[2] = MSG_ReadCoord();\n\t\t\t// only allowed by spectators\n#ifdef SERVER_DEMO_PLAYBACK\n\t\t\tif (sv.mvdplayback)\n\t\t\t{\n\t\t\t\tVectorCopy(o, host_client->specorigin);\n\t\t\t}\n\t\t\telse\n#endif\n\t\t\tif (host_client->spectator)\n\t\t\t{\n\t\t\t\tVectorCopy(o, sv_player->v->origin);\n\t\t\t\tWorld_LinkEdict(&sv.world, (wedict_t*)sv_player, false);\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase clc_upload:\n\t\t\tSV_NextUpload();\n\t\t\tbreak;\n#ifdef VOICECHAT\n\t\tcase clcfte_voicechat:\n\t\t\tSV_VoiceReadPacket();\n\t\t\tbreak;\n#endif\n\t\tcase clcdp_ackframe:\n\t\t\tcl->delta_sequence = MSG_ReadLong();\n\t\t\tif (cl->delta_sequence == -1)\n\t\t\t{\n\t\t\t\tunsigned int e;\n\t\t\t\tif (cl->pendingdeltabits)\n\t\t\t\t\tcl->pendingdeltabits[0] = UF_SV_REMOVE;\n\t\t\t\tif (host_client->pendingcsqcbits)\n\t\t\t\t\tfor (e = 1; e < host_client->max_net_ents; e++)\n\t\t\t\t\t\tif (host_client->pendingcsqcbits[e] & SENDFLAGS_PRESENT)\n\t\t\t\t\t\t\thost_client->pendingcsqcbits[e] |= SENDFLAGS_USABLE;\n\t\t\t}\n\t\t\tSV_AckEntityFrame(cl, cl->delta_sequence);\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tSV_AckEntityFrame(cl, cl->netchan.incoming_acknowledged);\n\n\thost_client = NULL;\n\tsv_player = NULL;\n}\n#ifdef Q2SERVER\nstatic void SVQ2_ClientThink(q2edict_t *ed, usercmd_t *cmd)\n{\n\tq2usercmd_t q2;\n\tq2.msec = cmd->msec;\n\tq2.buttons = cmd->buttons;\n\tVectorCopy(cmd->angles, q2.angles);\n\tq2.forwardmove = cmd->forwardmove;\n\tq2.sidemove = cmd->sidemove;\n\tq2.upmove = cmd->upmove;\n\tq2.impulse = cmd->impulse;\n\tq2.lightlevel = cmd->lightlevel;\n\tge->ClientThink (ed, &q2);\n}\nvoid InfoBuf_FromString_Q2EX(infobuf_t *info, const char *infostring, int seat)\n{\n\tqboolean match;\n\tint foundseat;\n\tchar *postfix, *end;\n\tInfoBuf_Clear(info, true);\n\tif (*infostring && *infostring == '\\\\')\n\t\tinfostring++;\n\n\t//all keys must start with a backslash\n\tdo\n\t{\n\t\tconst char *keystart = infostring;\n\t\tconst char *keyend;\n\t\tconst char *valstart;\n\t\tconst char *valend;\n\t\tchar *key;\n\t\tchar *val;\n\t\tsize_t keysize, valsize;\n\t\twhile (*infostring)\n\t\t{\n\t\t\tif (*infostring == '\\\\')\n\t\t\t\tbreak;\n\t\t\telse infostring += 1;\n\t\t}\n\t\tkeyend = infostring;\n\t\tif (*infostring++ != '\\\\')\n\t\t\tbreak;\t//missing value...\n\t\tvalstart = infostring;\n\t\twhile (*infostring)\n\t\t{\n\t\t\tif (*infostring == '\\\\')\n\t\t\t\tbreak;\n\t\t\telse infostring += 1;\n\t\t}\n\t\tvalend = infostring;\n\n\t\tkey = InfoBuf_DecodeString(keystart, keyend, &keysize);\n\t\tmatch = true;\t//all, if there's no _#\n\t\tpostfix = strrchr(key, '_');\n\t\tif (postfix && postfix[1])\n\t\t{\n\t\t\tfoundseat = strtol(postfix+1, &end, 10);\n\t\t\tif (!*end)\n\t\t\t{\n\t\t\t\t*postfix = 0;\t//strip the trailing part of the key name.\n\t\t\t\tmatch = foundseat == seat;\t//there's an underscore, and a number, and nothing else after it...\n\t\t\t}\n\t\t}\n\t\tif (match)\n\t\t{\n\t\t\tval = InfoBuf_DecodeString(valstart, valend, &valsize);\n\t\t\tInfoBuf_SetStarBlobKey(info, key, val, valsize);\n\t\t\tZ_Free(val);\n\t\t}\n\t\tZ_Free(key);\n\t} while (*infostring++ == '\\\\');\n}\nvoid SVQ2_ExecuteClientMessage (client_t *cl)\n{\n\tint\t\tc, lc=-1;\n\tchar\t*s;\n\tusercmd_t\toldest, oldcmd, newcmd;\n\tq2client_frame_t\t*frame;\n\tint\t\tmove_issued = 0; //only allow one move command\n\tint\t\tchecksumIndex;\n\tqbyte\tchecksum, calculatedChecksum;\n\tint\t\tseq_hash;\n\tint lastframe;\n\tclient_t *split;\n\n\tif (!ge)\n\t{\n\t\tCon_Printf(\"Q2 client without Q2 server\\n\");\n\t\tSV_DropClient(cl);\n\t}\n\n\t// make sure the reply sequence number matches the incoming\n\t// sequence number\n\t//FIXME: is this actually needed?\n\t/*if (cl->netchan.incoming_sequence >= cl->netchan.outgoing_sequence)\n\t\tcl->netchan.outgoing_sequence = cl->netchan.incoming_sequence;\n\telse\n\t\tcl->send_message = false;\t// don't reply, sequences have slipped*/\n\n\t// calc ping time\n\tif (cl->netchan.outgoing_sequence - cl->netchan.incoming_acknowledged > Q2UPDATE_MASK)\n\t{\n\t\tcl->delay -= 0.001;\n\t\tif (cl->delay < 0)\n\t\t\tcl->delay = 0;\n\t}\n\telse\n\t{\n\t\tframe = &cl->frameunion.q2frames[cl->netchan.incoming_acknowledged & Q2UPDATE_MASK];\n\t\tif (frame->senttime != -1)\n\t\t{\n\t\t\tint ping_time = (int)(realtime*1000) - frame->senttime;\t//no more phenomanally low pings please\n\t\t\tif (ping_time > sv_minping.value+1)\n\t\t\t{\n\t\t\t\tcl->delay -= 0.001;\n\t\t\t\tif (cl->delay < 0)\n\t\t\t\t\tcl->delay = 0;\n\t\t\t}\n\t\t\tif (ping_time < sv_minping.value)\n\t\t\t{\n\t\t\t\tcl->delay += 0.001;\n\t\t\t\tif (cl->delay > 1)\n\t\t\t\t\tcl->delay = 1;\n\t\t\t}\n\t\t\tframe->senttime = -1;\n\t\t\tframe->ping_time = ping_time;\n\t\t}\n\t}\n\n\t// save time for ping calculations\n//\tcl->frameunion.q2frames[cl->netchan.outgoing_sequence & Q2UPDATE_MASK].senttime = realtime*1000;\n//\tcl->frameunion.q2frames[cl->netchan.outgoing_sequence & Q2UPDATE_MASK].ping_time = -1;\n\n\thost_client = cl;\n\tsv_player = host_client->edict;\n\n\tseq_hash = cl->netchan.incoming_sequence;\n\n\t// mark time so clients will know how much to predict\n\t// other players\n \tcl->localtime = sv.time;\n\tcl->delta_sequence = -1;\t// no delta unless requested\n\twhile (1)\n\t{\n\t\tif (msg_badread)\n\t\t{\n\t\t\tCon_Printf (\"SVQ2_ExecuteClientMessage: badread (parsing q2clc_%i)\\n\", lc);\n\t\t\tSV_DropClient (cl);\n\t\t\treturn;\n\t\t}\n\n\t\tc = MSG_ReadByte ();\n\t\tif (c == -1)\n\t\t\tbreak;\n\n//\t\tif (sv_shownet.ival)\n//\t\t\tCon_Printf(\"%i: %i\\n\", (net_message.currentbit>>3)-1, c);\n\n\t\tsafeswitch ((enum clcq2_ops_e)c)\n\t\t{\n\t\tcase clcq2_nop:\n\t\t\tbreak;\n\n\t\tcase clcq2_move:\n\t\t\tif (move_issued >= MAX_SPLITS)\n\t\t\t\treturn;\t\t// someone is trying to cheat...\n\n\t\t\tfor (checksumIndex = 0, split = cl; split && checksumIndex < move_issued; checksumIndex++)\n\t\t\t\tsplit = split->controlled;\n\n\t\t\tif (cl->protocol == SCP_QUAKE2EX)\n\t\t\t{\n\t\t\t\tif (move_issued)\n\t\t\t\t\treturn;\t//no dupes.\n\t\t\t\tlastframe = MSG_ReadLong();\n\t\t\t\tif (lastframe != split->delta_sequence)\n\t\t\t\t\tsplit->delta_sequence = lastframe;\n\nnextseat:\n\t\t\t\tchecksumIndex = MSG_GetReadCount();\n\t\t\t\tchecksum = (qbyte)MSG_ReadByte ();\n\t\t\t}\n\t\t\telse if (move_issued)\n\t\t\t{\n\t\t\t\tchecksumIndex = -1;\n\t\t\t\tchecksum = 0;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tchecksumIndex = MSG_GetReadCount();\n\t\t\t\tchecksum = (qbyte)MSG_ReadByte ();\n\n\t\t\t\tlastframe = MSG_ReadLong();\n\t\t\t\tif (lastframe != split->delta_sequence)\n\t\t\t\t{\n\t\t\t\t\tsplit->delta_sequence = lastframe;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tMSGQ2_ReadDeltaUsercmd (cl, &nullcmd, &oldest);\n\t\t\tMSGQ2_ReadDeltaUsercmd (cl, &oldest, &oldcmd);\n\t\t\tMSGQ2_ReadDeltaUsercmd (cl, &oldcmd, &newcmd);\n\n\t\t\tif ( split && split->state == cs_spawned )\n\t\t\t{\n\t\t\t\tif (checksumIndex != -1)\n\t\t\t\t{\n\t\t\t\t\t// if the checksum fails, ignore the rest of the packet\n\t\t\t\t\tcalculatedChecksum = Q2COM_BlockSequenceCRCByte(\n\t\t\t\t\t\tnet_message.data + checksumIndex + 1,\n\t\t\t\t\t\tMSG_GetReadCount() - checksumIndex - 1,\n\t\t\t\t\t\tseq_hash);\n\n\t\t\t\t\tif (calculatedChecksum != checksum)\n\t\t\t\t\t{\n\t\t\t\t\t\tCon_DPrintf (\"Failed command checksum for %s(%d) (%d != %d)\\n\",\n\t\t\t\t\t\t\tcl->name, cl->netchan.incoming_sequence, checksum, calculatedChecksum);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (split->penalties & BAN_CRIPPLED)\n\t\t\t\t{\n\t\t\t\t\tsplit->lastcmd.forwardmove = 0;\t//hmmm.... does this work well enough?\n\t\t\t\t\toldest.forwardmove = 0;\n\t\t\t\t\tnewcmd.forwardmove = 0;\n\n\t\t\t\t\tsplit->lastcmd.sidemove = 0;\n\t\t\t\t\toldest.sidemove = 0;\n\t\t\t\t\tnewcmd.sidemove = 0;\n\n\t\t\t\t\tsplit->lastcmd.upmove = 0;\n\t\t\t\t\toldest.upmove = 0;\n\t\t\t\t\tnewcmd.upmove = 0;\n\t\t\t\t}\n\n\t\t\t\tsplit->q2edict->client->ping = SV_CalcPing (split, false);\n\t\t\t\tif (!sv.paused)\n\t\t\t\t{\n\t\t\t\t\tif (net_drop < 20)\n\t\t\t\t\t{\n\t\t\t\t\t\twhile (net_drop > 2)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tSVQ2_ClientThink (split->q2edict, &split->lastcmd);\n\t\t\t\t\t\t\tnet_drop--;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (net_drop > 1)\n\t\t\t\t\t\t\tSVQ2_ClientThink (split->q2edict, &oldest);\n\t\t\t\t\t\tif (net_drop > 0)\n\t\t\t\t\t\t\tSVQ2_ClientThink (split->q2edict, &oldcmd);\n\t\t\t\t\t}\n\t\t\t\t\tSVQ2_ClientThink (split->q2edict, &newcmd);\n\t\t\t\t}\n\n\t\t\t\tsplit->lastcmd = newcmd;\n\t\t\t}\n\t\t\tmove_issued++;\n\n\t\t\tif (cl->protocol == SCP_QUAKE2EX && split->controlled)\n\t\t\t{\t//q2ex needs them all upfront.\n\t\t\t\tsplit = split->controlled;\n\t\t\t\t//with each seat having its own private checksum, for some reason.\n\t\t\t\tgoto nextseat;\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase clcq2_userinfo:\n\t\t\t//FIXME: allows the client to set * keys mid-game.\n\t\t\ts = MSG_ReadString();\n\t\t\tif (cl->protocol == SCP_QUAKE2EX)\n\t\t\t{\n\t\t\t\tint seat;\n\t\t\t\tchar useruserinfo[1024];\n\t\t\t\tfor (split = cl, seat = 0; split; split = split->controlled, seat++)\n\t\t\t\t{\n\t\t\t\t\tInfoBuf_FromString_Q2EX(&split->userinfo, s, seat);\n\t\t\t\t\tInfoBuf_ToString(&split->userinfo, useruserinfo, sizeof(useruserinfo), NULL, NULL, NULL, NULL, NULL);\n\t\t\t\t\tSV_ExtractFromUserinfo(split, true);\t//let the server routines know\n\t\t\t\t\tge->ClientUserinfoChanged (split->q2edict, useruserinfo);\t//tell the gamecode\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tInfoBuf_FromString(&cl->userinfo, s, false);\n\t\t\t\tSV_ExtractFromUserinfo(cl, true);\t//let the server routines know\n\t\t\t\tge->ClientUserinfoChanged (cl->q2edict, s);\t//tell the gamecode\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase clcq2_stringcmd:\n\t\tcase clcq2_stringcmd_seat:\n\t\t\tif (c == clcq2_stringcmd_seat)\n\t\t\t\tc = MSG_ReadByte();\n\t\t\telse if (cl->protocol == SCP_QUAKE2EX)\n\t\t\t\tc = MSG_ReadByte()-1;\n\t\t\telse\n\t\t\t\tc = 0;\n\n\t\t\thost_client = cl;\n\t\t\twhile (c --> 0 && host_client->controlled)\n\t\t\t\thost_client = host_client->controlled;\n\t\t\tsv_player = host_client->edict;\n\n\t\t\t//regular stringcmd\n\t\t\ts = MSG_ReadString ();\n\t\t\tSV_ExecuteUserCommand (s, false);\n\n\t\t\thost_client = cl;\n\t\t\tsv_player = cl->edict;\n\n\t\t\tif (cl->state < cs_connected)\n\t\t\t\treturn;\t// disconnect command\n\t\t\tbreak;\n\n#ifdef VOICECHAT\n\t\tcase clcq2_voicechat:\n\t\t\tSV_VoiceReadPacket();\n\t\t\tbreak;\n#endif\n\n\t\tcase clcq2_bad:\n\t\tcase clcr1q2_setting:\n\t\tcase clcr1q2_multimoves:\n\t\tsafedefault:\n\t\t\tCon_Printf (\"SVQ2_ReadClientMessage: unknown command char %i (last was q2clc_%i)\\n\", c, lc);\n\t\t\tSV_DropClient (cl);\n\t\t\treturn;\n\t\t}\n\t\tlc = c;\n\t}\n}\n#endif\n#ifdef NQPROT\nvoid SVNQ_ReadClientMove (qboolean forceangle16, qboolean quakeex)\n{\n\tint\t\ti;\n\tclient_frame_t\t*frame;\n\tfloat timesincelast;\n\n\tusercmd_t *from = &host_client->lastcmd;\n\tusercmd_t cmd;\n\n\tframe = &host_client->frameunion.frames[host_client->netchan.incoming_acknowledged & UPDATE_MASK];\n\n\tif (quakeex)\n\t\t;\t//sequence is a separate clc (should have already been sent)\n\telse if (host_client->protocol == SCP_DARKPLACES7)\n\t\thost_client->last_sequence = MSG_ReadLong ();\n\telse if (host_client->fteprotocolextensions2 & PEXT2_PREDINFO)\n\t{\n\t\tint seq = (unsigned short)MSG_ReadShort ();\n\t\tif (seq < (host_client->last_sequence&0xffff))\n\t\t\thost_client->last_sequence += 0x10000;\t//wrapped\n\t\thost_client->last_sequence = (host_client->last_sequence&0xffff0000) | seq;\n\t}\n\telse\n\t\thost_client->last_sequence = 0;\n\n\tcmd = nullcmd;\n\n\tcmd.sequence = host_client->last_sequence;\n\n\t//read the time, woo... should be an ack of our serverside time.\n\tcmd.fservertime = MSG_ReadFloat ();\n\tif (cmd.fservertime < from->fservertime)\n\t\tcmd.fservertime = from->fservertime;\n\tif (cmd.fservertime > sv.time)\n\t\tcmd.fservertime = sv.time;\n\tif (cmd.fservertime < sv.time - 2)\t//if you do lag more than this, you won't get your free time.\n\t\tcmd.fservertime = sv.time - 2;\n\tcmd.servertime = cmd.fservertime*1000;\n\n\tif (quakeex)\n\t{\t//I'm guessing this has something to do with splitscreen.\n\t\tif (MSG_ReadByte() != 1)\n\t\t{\n\t\t\tCon_Printf(\"Unknown byte wasn't 1\\n\");\n\t\t\tmsg_badread = true;\n\t\t}\n\t}\n\n\n\t//read angles\n\tfor (i=0 ; i<3 ; i++)\n\t{\n\t\tfloat a;\n\t\tif (forceangle16)\n\t\t\ta = MSG_ReadAngle16 ();\n\t\telse\n\t\t\ta = MSG_ReadAngle ();\n\n\t\tcmd.angles[i] = ANGLE2SHORT(a);\n\t}\n\n\t// read movement\n\tcmd.forwardmove = MSG_ReadShort ();\n\tcmd.sidemove = MSG_ReadShort ();\n\tcmd.upmove = MSG_ReadShort ();\n\n\t// read buttons\n\tif (host_client->protocol == SCP_DARKPLACES6 || host_client->protocol == SCP_DARKPLACES7)\n\t\tcmd.buttons = MSG_ReadLong() | (1u<<31);\n\telse if (host_client->fteprotocolextensions2 & PEXT2_PRYDONCURSOR)\n\t\tcmd.buttons = MSG_ReadLong();\n\telse\n\t\tcmd.buttons = MSG_ReadByte ();\n\n\t//impulse...\n\tcmd.impulse = MSG_ReadByte ();\n\n\t//weapon extension\n\tif (cmd.buttons & (1u<<30))\n\t\tcmd.weapon = MSG_ReadLong();\n\telse\n\t\tcmd.weapon = 0;\n\n\t//cursor extension\n\tif (cmd.buttons & (1u<<31))\n\t\tSV_ReadPrydonCursor(&cmd);\n\n\t//clear out extension buttons that are part of the protocol rather than actual buttons..\n\tcmd.buttons &= ~((1u<<30)|(1u<<31));\n\n\t//figure out ping\n\tif ((host_client->fteprotocolextensions2 & PEXT2_PREDINFO) && host_client->delta_sequence >= 0 && host_client->delta_sequence == host_client->frameunion.frames[host_client->delta_sequence & UPDATE_MASK].sequence)\n\t\tframe->ping_time = realtime - host_client->frameunion.frames[host_client->delta_sequence & UPDATE_MASK].senttime;\n\telse\n\t\tframe->ping_time = sv.time - cmd.fservertime;\n\n\t//figure out how far we moved.\n\ttimesincelast = cmd.fservertime - from->fservertime;\n\tcmd.msec=bound(0, timesincelast*1000, 250);\n\tframe->move_msecs = timesincelast*1000;\n\n\tif (frame->ping_time*1000 > sv_minping.value+1)\n\t{\n\t\thost_client->delay -= 0.001;\n\t\tif (host_client->delay < 0)\n\t\t\thost_client->delay = 0;\n\t}\n\tif (frame->ping_time*1000 < sv_minping.value)\n\t{\n\t\thost_client->delay += 0.001;\n\t\tif (host_client->delay > 1)\n\t\t\thost_client->delay = 1;\n\t}\n\n\tif (host_client->spectator)\n\t{\n\t\tqboolean tracknext = false;\n\t\tunsigned int pressed = cmd.buttons & ~from->buttons;\n\t\tif (pressed & BUTTON_ATTACK)\n\t\t{\t//enable/disable tracking\n\t\t\tif (host_client->spec_track)\n\t\t\t{\t//disable tracking\n\t\t\t\thost_client->spec_track = 0;\n\t\t\t\thost_client->edict->v->goalentity = EDICT_TO_PROG(svprogfuncs, EDICT_NUM_PB(svprogfuncs, 0));\n\t\t\t\tClientReliableWrite_Begin(host_client, svc_setview, 4);\n\t\t\t\tClientReliableWrite_Entity(host_client, host_client - svs.clients + 1);\n\t\t\t}\n\t\t\telse\t//otherwise track the next person, if we can\n\t\t\t\ttracknext = true;\n\t\t}\n\t\tif ((pressed & BUTTON_JUMP) && host_client->spec_track)\n\t\t\ttracknext = true;\n\n\t\tif (tracknext)\n\t\t{\t//track the next player\n\t\t\tfor (i = host_client->spec_track+1; i < sv.allocated_client_slots; i++)\n\t\t\t{\n\t\t\t\tif (SV_CanTrack(host_client, i))\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\t//try a previous one instead of disabling\n\t\t\tif (i == sv.allocated_client_slots)\n\t\t\t{\n\t\t\t\tfor (i = 1; i < host_client->spec_track; i++)\n\t\t\t\t{\n\t\t\t\t\tif (SV_CanTrack(host_client, i))\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tif (i >= host_client->spec_track)\n\t\t\t\t\ti = 0;\n\t\t\t}\n\n\t\t\thost_client->spec_track = i;\n\t\t\thost_client->edict->v->goalentity = EDICT_TO_PROG(svprogfuncs, EDICT_NUM_PB(svprogfuncs, i));\n\t\t\tClientReliableWrite_Begin(host_client, svc_setview, 4);\n\t\t\tClientReliableWrite_Entity(host_client, i?i:(host_client - svs.clients + 1));\n\n\t\t\tif (i)\n\t\t\t\tSV_ClientTPrintf (host_client, PRINT_HIGH, \"tracking %s\\n\", svs.clients[i-1].name);\n\t\t}\n\t}\n\n\tSV_Prompt_Input(host_client, &cmd);\n\n\t/*host_client->edict->v->v_angle[0] = SHORT2ANGLE(cmd.angles[0]);\n\thost_client->edict->v->v_angle[1] = SHORT2ANGLE(cmd.angles[1]);\n\thost_client->edict->v->v_angle[2] = SHORT2ANGLE(cmd.angles[2]);*/\n\n\tif (SV_RunFullQCMovement(host_client, &cmd))\n\t{\t//mod provides its own movement logic. this forces independance.\n\t\thost_client->msecs -= cmd.msec;\n\t\tpr_global_struct->time = sv.world.physicstime;\n\t\tpr_global_struct->self = EDICT_TO_PROG(svprogfuncs, sv_player);\n#ifdef VM_Q1\n\t\tif (svs.gametype == GT_Q1QVM)\n\t\t\tQ1QVM_PostThink();\n\t\telse\n#endif\n\t\t{\n\t\t\tif (pr_global_struct->PlayerPostThink)\n\t\t\t\tPR_ExecuteProgram (svprogfuncs, pr_global_struct->PlayerPostThink);\n\t\t}\n\t\thost_client->isindependant = true;\n\t}\n\telse\n\t{\n\t\tSV_SetEntityButtons(host_client->edict, cmd.buttons);\n\n\t\tif (host_client->last_sequence && !sv_nqplayerphysics.ival && host_client->state == cs_spawned)\n\t\t{\n\t\t\thost_frametime = timesincelast;\n\n\t\t\thost_client->isindependant = true;\n\t\t\tSV_PreRunCmd();\n\t\t\tSV_RunCmd (&cmd, false);\n\t\t\tSV_PostRunCmd();\n\t\t\tcmd.impulse = 0;\n\t\t\thost_client->lastruncmd = sv.time*1000;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (!host_client->qex)\n\t\t\t\thost_client->last_sequence = 0;\t//let the client know that prediction is fucked, by not acking any input frames.\n\t\t\tif (cmd.impulse)\n\t\t\t\thost_client->edict->v->impulse = cmd.impulse;\n\t\t\thost_client->isindependant = false;\n\t\t}\n\t}\n\t*from = cmd;\n}\n\nvoid SVNQ_ExecuteClientMessage (client_t *cl)\n{\n\textern cvar_t\tsv_listen_dp;\n\tint\t\tc;\n\tchar\t*s;\n//\tclient_frame_t\t*frame;\n\tqboolean forceangle16;\n\n\tcl->netchan.outgoing_sequence++;\n\tcl->netchan.incoming_acknowledged = cl->netchan.outgoing_sequence-1;\n\n\t// calc ping time\n//\tframe = &cl->frameunion.frames[cl->netchan.incoming_acknowledged & UPDATE_MASK];\n//\tframe->ping_time = -1;\n\n\t// make sure the reply sequence number matches the incoming\n\t// sequence number\n//\tif (cl->netchan.incoming_sequence >= cl->netchan.outgoing_sequence)\n\t\tcl->netchan.outgoing_sequence = cl->netchan.incoming_sequence;\n//\telse\n//\t\tcl->send_message = false;\t// don't reply, sequences have slipped\n\n\t// save time for ping calculations\n//\tcl->frameunion.frames[cl->netchan.outgoing_sequence & UPDATE_MASK].senttime = realtime;\n//\tcl->frameunion.frames[cl->netchan.outgoing_sequence & UPDATE_MASK].ping_time = -1;\n//\tcl->frameunion.frames[cl->netchan.outgoing_sequence & UPDATE_MASK].move_msecs = -1;\n//\tcl->frameunion.frames[cl->netchan.outgoing_sequence & UPDATE_MASK].packetsizein = net_message.cursize;\n//\tcl->frameunion.frames[cl->netchan.outgoing_sequence & UPDATE_MASK].packetsizeout = 0;\n\n\thost_client = cl;\n\tsv_player = host_client->edict;\n\n\t// mark time so clients will know how much to predict\n\t// other players\n \tcl->localtime = sv.time;\n\twhile (1)\n\t{\n\t\tif (msg_badread)\n\t\t{\n\t\t\tCon_Printf (\"SVNQ_ReadClientMessage: badread\\n\");\n\t\t\tSV_DropClient (cl);\n\t\t\treturn;\n\t\t}\n\n\t\tc = MSG_ReadByte ();\n\t\tif (c == -1)\n\t\t\tbreak;\n\n\t\tsafeswitch (c)\n\t\t{\n\t\tcase clc_disconnect:\n\t\t\thost_client = cl;\n\t\t\tsv_player = cl->edict;\n\t\t\tSV_Drop_f();\n\t\t\treturn;\n\t\tcase clc_nop:\n\t\t\tbreak;\n\n//\t\tcase clc_delta:\t\t\t//not in NQ\n//\t\t\tcl->delta_sequence = MSG_ReadByte ();\n//\t\t\tbreak;\n\n\t\tcase clcfte_move:\n\t\t\t{\n\t\t\t\tint seq = (unsigned short)MSG_ReadShort ();\n\n\t\t\t\tunsigned int oldservertime = cl->lastcmd.servertime;\n\t\t\t\tfloat delay;\n\t\t\t\tclient_frame_t *frame;\n\n\t\t\t\t//this is the input sequence that we'll need to ack later (no\n\t\t\t\tif (seq < (host_client->last_sequence&0xffff))\n\t\t\t\t\thost_client->last_sequence += 0x10000;\t//wrapped\n\t\t\t\thost_client->last_sequence = (host_client->last_sequence&0xffff0000) | seq;\n\n\t\t\t\tdelay = SVFTE_ExecuteClientMove(cl, host_client->last_sequence);\n\n\t\t\t\tif (cl->lastsequence_acknowledged>0 && cl->netchan.incoming_sequence-cl->lastsequence_acknowledged<UPDATE_BACKUP)\n\t\t\t\t{\n\t\t\t\t\tframe = &host_client->frameunion.frames[cl->lastsequence_acknowledged & UPDATE_MASK];\n\t\t\t\t\tif (frame->ping_time == -1)\n\t\t\t\t\t\tframe->ping_time = (realtime - frame->senttime);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tframe = &host_client->frameunion.frames[cl->netchan.incoming_acknowledged & UPDATE_MASK];\n\t\t\t\t\tframe->ping_time = (sv.time - cl->lastcmd.servertime/1000.0);\n\t\t\t\t}\n\t\t\t\tframe->move_msecs = cl->lastcmd.servertime - oldservertime;\n\t\t\t\tif (frame->ping_time*1000 > sv_minping.value+1)\n\t\t\t\t{\n\t\t\t\t\thost_client->delay -= 0.001;\n\t\t\t\t\tif (host_client->delay < 0)\n\t\t\t\t\t\thost_client->delay = 0;\n\t\t\t\t}\n\t\t\t\tif (frame->ping_time*1000 < sv_minping.value)\n\t\t\t\t{\n\t\t\t\t\thost_client->delay += 0.001;\n\t\t\t\t\tif (host_client->delay > 1)\n\t\t\t\t\t\thost_client->delay = 1;\n\t\t\t\t}\n\t\t\t\tframe->ping_time -= delay;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase clc_move:\t//bytes: 16(nq), 19(proquake/fitz), 56(dp7)\n\t\t\tif (cl->state != cs_spawned)\n\t\t\t\treturn;\t//shouldn't be sending moves at this point. typically they're stale, left from the previous map. this results in crashes if the protocol is different.\n\n\t\t\tforceangle16 = false;\n\t\t\tsafeswitch(cl->protocol)\n\t\t\t{\n\t\t\tcase SCP_FITZ666:\n\t\t\t\tforceangle16 = true;\n\t\t\t\tbreak;\n\t\t\tcase SCP_NETQUAKE:\n\t\t\tcase SCP_BJP3:\n\t\t\t\tif (sv_listen_dp.ival)\n\t\t\t\t{\n\t\t\t\t\tunsigned int readcount = MSG_GetReadCount();\n\t\t\t\t\t//Hack to work around buggy DP clients that don't reset the proquake hack for the next server\n\t\t\t\t\t//this ONLY works because the other clc commands are very unlikely to both be 3 bytes big and sent unreliably\n\t\t\t\t\t//aka: DP ProQuake angles hack hack\n\t\t\t\t\t//note that if a client then decides to use 16bit angles via this hack then it would be the 'fte dp proquake angles hack hack hack'....\n\t\t\t\t\tif (!cl->fteprotocolextensions && !cl->fteprotocolextensions2)\n\t\t\t\t\tif ((net_message.cursize-(readcount-1) == 16 &&  cl->proquake_angles_hack) ||\n\t\t\t\t\t\t(net_message.cursize-(readcount-1) == 19 && !cl->proquake_angles_hack))\n\t\t\t\t\t{\n\t\t\t\t\t\tcl->proquake_angles_hack ^= 1;\n\t\t\t\t\t\tSV_ClientPrintf(cl, PRINT_HIGH, \"Client sent \"S_COLOR_RED\"wrong\"S_COLOR_WHITE\" clc_move size, switching to %u-bit angles to try to compensate\\n\", cl->proquake_angles_hack?16:8);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tforceangle16 = cl->proquake_angles_hack;\n\t\t\t\tbreak;\n\t\t\tcase SCP_BAD:\n\t\t\tcase SCP_QUAKEWORLD:\n\t\t\tcase SCP_QUAKE2:\n\t\t\tcase SCP_QUAKE2EX:\n\t\t\tcase SCP_QUAKE3:\n\t\t\tcase SCP_DARKPLACES6:\n\t\t\tcase SCP_DARKPLACES7:\n\t\t\tsafedefault:\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\t\t\n\t\t\tSVNQ_ReadClientMove (forceangle16, cl->qex);\n//\t\t\tcmd = host_client->lastcmd;\n//\t\t\tSV_ClientThink();\n\t\t\tbreak;\n\n\t\tcase clc_stringcmd:\n\t\t\ts = MSG_ReadString ();\n\t\t\tSV_ExecuteUserCommand (s, false);\n#ifdef NETPREPARSE\n\t\t\tNPP_Flush();\t//flush it just in case there was an error and we stopped preparsing. This is only really needed while debugging.\n#endif\n\t\t\thost_client = cl;\n\t\t\tsv_player = cl->edict;\n\t\t\tif (cl->state < cs_connected)\n\t\t\t\treturn;\n\t\t\tbreak;\n\n\t\tcase clcfte_qcrequest:\n\t\t\tSV_ReadQCRequest();\n\n\t\t\thost_client = cl;\n\t\t\tsv_player = cl->edict;\n\t\t\tif (cl->state < cs_connected)\n\t\t\t\treturn;\n\t\t\tbreak;\n\n\t\tcase clcdp_ackframe:\n\t\t\tcl->delta_sequence = MSG_ReadLong();\n\t\t\tif (cl->delta_sequence == -1)\n\t\t\t{\n\t\t\t\tunsigned int e;\n\t\t\t\tif (cl->pendingdeltabits)\n\t\t\t\t\tcl->pendingdeltabits[0] = UF_SV_REMOVE;\n\t\t\t\tif (host_client->pendingcsqcbits)\n\t\t\t\t\tfor (e = 1; e < host_client->max_net_ents; e++)\n\t\t\t\t\t\tif (host_client->pendingcsqcbits[e] & SENDFLAGS_PRESENT)\n\t\t\t\t\t\t\thost_client->pendingcsqcbits[e] |= SENDFLAGS_USABLE;\n\t\t\t}\n\t\t\tSV_AckEntityFrame(cl, cl->delta_sequence);\n//\t\t\tif (cl->frameunion.frames[cl->delta_sequence&UPDATE_MASK].sequence == cl->delta_sequence)\n//\t\t\t\tif (cl->frameunion.frames[cl->delta_sequence&UPDATE_MASK].ping_time < 0)\n//\t\t\t\t\tcl->frameunion.frames[cl->delta_sequence&UPDATE_MASK].ping_time = realtime - cl->frameunion.frames[cl->delta_sequence&UPDATE_MASK].senttime;\n\t\t\tbreak;\n\t\tcase clcdp_ackdownloaddata:\n\t\t\tSV_DarkPlacesDownloadAck(cl);\n\t\t\tbreak;\n\n#ifdef VOICECHAT\n\t\tcase clcfte_voicechat:\n\t\t\tSV_VoiceReadPacket();\n\t\t\tbreak;\n#endif\n\n\t\tcase clc_delta://clcqex_sequence:\n\t\t\thost_client->last_sequence = MSG_ReadULEB128();\n\t\t\tbreak;\n\n\t\tcase clc_tmove://clcqex_auth\n\t\t\t//This allows for the client's positions to be slightly wrong, with the client being authoritive instead of the server (within tolerances anyway).\n\t\t\thost_client->last_sequence = MSG_ReadULEB128();\n\t\t\t/*host_client->edict->v->origin[0] =*/ MSG_ReadFloat();\n\t\t\t/*host_client->edict->v->origin[1] =*/ MSG_ReadFloat();\n\t\t\t/*host_client->edict->v->origin[2] =*/ MSG_ReadFloat();\n\t\t\tbreak;\n\t\tsafedefault:\n\t\t\tCon_Printf (\"SVNQ_ReadClientMessage: unknown command char %i\\n\", c);\n\t\t\tSV_DropClient (cl);\n\t\t\treturn;\n\t\t}\n\t}\n}\n#endif\n/*\n==============\nSV_UserInit\n==============\n*/\nvoid SV_UserInit (void)\n{\n#ifdef VOICECHAT\n\tCvar_Register (&sv_voip, cvargroup_serverpermissions);\n\tCvar_Register (&sv_voip_echo, cvargroup_serverpermissions);\n\tCvar_Register (&sv_voip_record, cvargroup_serverpermissions);\n#endif\n#ifdef SERVERONLY\n\tCvar_Register (&cl_rollspeed, \"Prediction stuff\");\n\tCvar_Register (&cl_rollangle, \"Prediction stuff\");\n#endif\n\tCvar_Register (&sv_chatfilter, cvargroup_serverpermissions);\n\tCvar_Register (&sv_spectalk, cvargroup_servercontrol);\n\tCvar_Register (&sv_mapcheck, cvargroup_servercontrol);\n\n\tCvar_Register (&sv_minpitch, cvargroup_servercontrol);\n\tCvar_Register (&sv_maxpitch, cvargroup_servercontrol);\n\n\tCvar_Register (&sv_fullredirect, cvargroup_servercontrol);\n\tCvar_Register (&sv_antilag, cvargroup_servercontrol);\n\tCvar_Register (&sv_antilag_frac, cvargroup_servercontrol);\n#ifndef NEWSPEEDCHEATPROT\n\tCvar_Register (&sv_cheatpc, cvargroup_servercontrol);\n\tCvar_Register (&sv_cheatspeedchecktime, cvargroup_servercontrol);\n#endif\n\tCvar_Register (&sv_showpredloss, cvargroup_servercontrol);\n\tCvar_Register (&sv_playermodelchecks, cvargroup_servercontrol);\n\n\tCvar_Register (&sv_getrealip, cvargroup_servercontrol);\n\tCvar_Register (&sv_realip_kick, cvargroup_servercontrol);\n\tCvar_Register (&sv_realiphostname_ipv4, cvargroup_servercontrol);\n\tCvar_Register (&sv_realiphostname_ipv6, cvargroup_servercontrol);\n\tCvar_Register (&sv_realip_timeout, cvargroup_servercontrol);\n\n\tCvar_Register (&sv_userinfo_keylimit, cvargroup_servercontrol);\n\tCvar_Register (&sv_userinfo_bytelimit, cvargroup_servercontrol);\n\n\tCvar_Register (&sv_pushplayers, cvargroup_servercontrol);\n\tCvar_Register (&sv_protocol_nq, cvargroup_servercontrol);\n\n\tCvar_Register (&sv_pure, cvargroup_servercontrol);\n\tCvar_Register (&sv_floodprotect, cvargroup_servercontrol);\n\tCvar_Register (&sv_floodprotect_interval, cvargroup_servercontrol);\n\tCvar_Register (&sv_floodprotect_messages, cvargroup_servercontrol);\n\tCvar_Register (&sv_floodprotect_silencetime, cvargroup_servercontrol);\n\tCvar_Register (&sv_floodprotect_suicide, cvargroup_servercontrol);\n\tCvar_Register (&sv_floodprotect_sendmessage, cvargroup_servercontrol);\n\n\tCvar_Register (&sv_cmdlikercon, cvargroup_serverpermissions);\n\tCvar_Register(&cmd_gamecodelevel, \"Access controls\");\n\tCvar_Register(&cmd_allowaccess, \"Access controls\");\n\n\tCvar_Register (&votelevel, sv_votinggroup);\n\tCvar_Register (&voteminimum, sv_votinggroup);\n\tCvar_Register (&votepercent, sv_votinggroup);\n\tCvar_Register (&votetime, sv_votinggroup);\n\n#ifdef HAVE_LEGACY\n\tCvar_Register (&sv_brokenmovetypes, \"Backwards compatability\");\n\tCvar_Register (&pext_ezquake_nochunks, cvargroup_servercontrol);\n\tCvar_Register (&pext_ezquake_verfortrans, cvargroup_servercontrol);\n#endif\n}\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nstatic vec3_t forward, right, up, wishdir;\nstatic pvec_t *origin, *velocity, *angles;\nextern cvar_t sv_accelerate, sv_friction;\nstatic qboolean onground;\n\n\n/*\n==================\nSV_UserFriction\n\n==================\n*/\nstatic void SV_UserFriction (void)\n{\n\textern cvar_t sv_stopspeed;\n\tfloat\t*vel;\n\tfloat\tspeed, newspeed, control;\n\tvec3_t\tstart, stop;\n\tfloat\tfriction;\n\ttrace_t\ttrace;\n\n\tvel = velocity;\n\n\tspeed = sqrt(vel[0]*vel[0] +vel[1]*vel[1]);\n\tif (!speed)\n\t\treturn;\n\n// if the leading edge is over a dropoff, increase friction\n\tstart[0] = stop[0] = origin[0] + vel[0]/speed*16;\n\tstart[1] = stop[1] = origin[1] + vel[1]/speed*16;\n\tstart[2] = origin[2] + sv_player->v->mins[2];\n\tstop[2] = start[2] - 34;\n\n\ttrace = World_Move (&sv.world, start, vec3_origin, vec3_origin, stop, true, (wedict_t*)sv_player);\n\n\tif (trace.fraction == 1.0)\n\t\tfriction = sv_friction.value*(*pm_edgefriction.string?pm_edgefriction.value:2);\n\telse\n\t\tfriction = sv_friction.value;\n\n//\tval = GetEdictFieldValue(sv_player, \"friction\", &frictioncache);\n//\tif (val && val->_float)\n//\t\tfriction *= val->_float;\n\n// apply friction\n\tcontrol = speed < sv_stopspeed.value ? sv_stopspeed.value : speed;\n\tnewspeed = speed - host_frametime*control*friction;\n\n\tif (newspeed < 0)\n\t\tnewspeed = 0;\n\tnewspeed /= speed;\n\n\tvel[0] = vel[0] * newspeed;\n\tvel[1] = vel[1] * newspeed;\n\tvel[2] = vel[2] * newspeed;\n}\n\nstatic void SV_Accelerate (float wishspeed)\n{\n\tint\t\t\ti;\n\tfloat\t\taddspeed, accelspeed, currentspeed;\n\n\tcurrentspeed = DotProduct (velocity, wishdir);\n\taddspeed = wishspeed - currentspeed;\n\tif (addspeed <= 0)\n\t\treturn;\n\taccelspeed = sv_accelerate.value*host_frametime*wishspeed;\n\tif (accelspeed > addspeed)\n\t\taccelspeed = addspeed;\n\n\tfor (i=0 ; i<3 ; i++)\n\t\tvelocity[i] += accelspeed*wishdir[i];\n}\n\nstatic void SV_AirAccelerate (vec3_t wishveloc, float wishspeed)\n{\n\tint\t\t\ti;\n\tfloat\t\taddspeed, wishspd, accelspeed, currentspeed;\n\n\twishspd = VectorNormalize (wishveloc);\n\tif (wishspd > 30)\n\t\twishspd = 30;\n\tcurrentspeed = DotProduct (velocity, wishveloc);\n\taddspeed = wishspd - currentspeed;\n\tif (addspeed <= 0)\n\t\treturn;\n//\taccelspeed = sv_accelerate.value * host_frametime;\n\taccelspeed = sv_accelerate.value*wishspeed * host_frametime;\n\tif (accelspeed > addspeed)\n\t\taccelspeed = addspeed;\n\n\tfor (i=0 ; i<3 ; i++)\n\t\tvelocity[i] += accelspeed*wishveloc[i];\n}\n\n/*\n===================\nSV_AirMove\n\n===================\n*/\nstatic void SV_AirMove (void)\n{\n\tint\t\t\ti;\n\tvec3_t\t\twishvel;\n\tfloat\t\tfmove, smove;\n\tfloat scale, maxspeed;\n\tfloat wishspeed;\n\n\tAngleVectors (sv_player->v->angles, forward, right, up);\n\n\tfmove = cmd.forwardmove;\n\tsmove = cmd.sidemove;\n\n// hack to not let you back into teleporter\n\tif (sv.time < sv_player->v->teleport_time && fmove < 0)\n\t\tfmove = 0;\n\n\tfor (i=0 ; i<3 ; i++)\n\t\twishvel[i] = forward[i]*fmove + right[i]*smove;\n\n\tif ( (int)sv_player->v->movetype != MOVETYPE_WALK)\n\t\twishvel[2] = cmd.upmove;\n\telse\n\t\twishvel[2] = 0;\n\n\tVectorCopy (wishvel, wishdir);\n\twishspeed = VectorNormalize(wishdir);\n//\tval = GetEdictFieldValue(sv_player, \"scale\", &scalecache);\n//\tif (!val || !val->_float)\n\t\tscale = 1;\n//\telse\n//\t\tscale = val->_float;\n\n\tmaxspeed=sv_player->xv->maxspeed;//FIXME: This isn't fully compatible code...\n#ifdef HEXEN2\n\tif (sv_player->xv->hasted)\n\t\tmaxspeed*=sv_player->xv->hasted;\n#endif\n\n\tif (wishspeed > maxspeed*scale)\n\t{\n\t\tVectorScale (wishvel, maxspeed/wishspeed, wishvel);\n\t\twishspeed = maxspeed*scale;\n\t}\n\n\tif ( sv_player->v->movetype == MOVETYPE_NOCLIP)\n\t{\t// noclip\n\t\tVectorCopy (wishvel, velocity);\n\t}\n\telse if ( onground )\n\t{\n\t\tSV_UserFriction ();\n\t\tSV_Accelerate (wishspeed);\n\t}\n\telse\n\t{\t// not on ground, so little effect on velocity\n\t\tSV_AirAccelerate (wishvel, wishspeed);\n\t}\n}\n\nstatic void SV_WaterMove (qboolean flymode)\n{\n\tint\t\ti;\n\tvec3_t\twishvel;\n\tfloat\tspeed, newspeed, wishspeed, addspeed, accelspeed;\n\tfloat scale;\n\tfloat maxspeed;\n\n//\n// user intentions\n//\n\tAngleVectors (sv_player->v->v_angle, forward, right, up);\n\n\tfor (i=0 ; i<3 ; i++)\n\t\twishvel[i] = forward[i]*cmd.forwardmove + right[i]*cmd.sidemove;\n\n\tif (flymode)\n\t\tVectorMA(wishvel, cmd.upmove, up, wishvel);\n\telse if (!cmd.forwardmove && !cmd.sidemove && !cmd.upmove)\n\t\twishvel[2] -= 60;\t\t// drift towards bottom\n\telse\n\t\twishvel[2] += cmd.upmove;\n\n\twishspeed = Length(wishvel);\n//\tval = GetEdictFieldValue(sv_player, \"scale\", &scalecache);\n//\tif (!val || !val->_float)\n\t\tscale = 1;\n//\telse\n//\t\tscale = val->_float;\n\n//\tval = GetEdictFieldValue(sv_player, \"maxspeed\", &maxspeedcache);\n//\tif (val && val->_float)\n//\t\tmaxspeed = sv_maxspeed.value*val->_float;\n//\telse\n\t\tmaxspeed = host_client->maxspeed;\n\tif (wishspeed > maxspeed*scale)\n\t{\n\t\tVectorScale (wishvel, maxspeed/wishspeed, wishvel);\n\t\twishspeed = maxspeed*scale;\n\t}\n\tif (!flymode)\n\t\twishspeed *= 0.7;\n\n//\n// water friction\n//\n\tspeed = Length (velocity);\n\tif (speed)\n\t{\n//\t\tval = GetEdictFieldValue(sv_player, \"friction\", &frictioncache);\n//\t\tif (val&&val->_float)\n//\t\t\tnewspeed = speed - host_frametime * speed * sv_friction.value*val->_float;\n//\t\telse\n\t\t\tnewspeed = speed - host_frametime * speed * sv_friction.value;\n\t\tif (newspeed < 0)\n\t\t\tnewspeed = 0;\n\t\tVectorScale (velocity, newspeed/speed, velocity);\n\t}\n\telse\n\t\tnewspeed = 0;\n\n//\n// water acceleration\n//\n\tif (!wishspeed)\n\t\treturn;\n\n\taddspeed = wishspeed - newspeed;\n\tif (addspeed <= 0)\n\t\treturn;\n\n\tVectorNormalize (wishvel);\n\taccelspeed = sv_accelerate.value * wishspeed * host_frametime;\n\tif (accelspeed > addspeed)\n\t\taccelspeed = addspeed;\n\n\tfor (i=0 ; i<3 ; i++)\n\t\tvelocity[i] += accelspeed * wishvel[i];\n}\n\n\nstatic void SV_LadderMove (void)\n{\n\tint\t\ti;\n\tvec3_t\twishvel;\n\tfloat\tspeed, newspeed, wishspeed, addspeed, accelspeed;\n\tfloat scale;\n\tfloat maxspeed;\n\n//\n// user intentions\n//\n\tAngleVectors (sv_player->v->v_angle, forward, right, up);\n\n\tfor (i=0 ; i<3 ; i++)\n\t\twishvel[i] = forward[i]*cmd.forwardmove + right[i]*cmd.sidemove;\n\twishvel[2] += cmd.upmove;\n\n\twishspeed = Length(wishvel);\n//\tval = GetEdictFieldValue(sv_player, \"scale\", &scalecache);\n//\tif (!val || !val->_float)\n\t\tscale = 1;\n//\telse\n//\t\tscale = val->_float;\n\n//\tval = GetEdictFieldValue(sv_player, \"maxspeed\", &maxspeedcache);\n//\tif (val && val->_float)\n//\t\tmaxspeed = sv_maxspeed.value*val->_float;\n//\telse\n\t\tmaxspeed = host_client->maxspeed;\n\tif (wishspeed > maxspeed*scale)\n\t{\n\t\tVectorScale (wishvel, maxspeed/wishspeed, wishvel);\n\t\twishspeed = maxspeed*scale;\n\t}\n\twishspeed *= 0.7;\n\n//\n// water friction\n//\n\tspeed = Length (velocity);\n\tif (speed)\n\t{\n//\t\tval = GetEdictFieldValue(sv_player, \"friction\", &frictioncache);\n//\t\tif (val&&val->_float)\n//\t\t\tnewspeed = speed - host_frametime * speed * sv_friction.value*val->_float;\n//\t\telse\n\t\t\tnewspeed = speed - host_frametime * speed * sv_friction.value;\n\t\tif (newspeed < 0)\n\t\t\tnewspeed = 0;\n\t\tVectorScale (velocity, newspeed/speed, velocity);\n\t}\n\telse\n\t\tnewspeed = 0;\n\n//\n// water acceleration\n//\n\tif (!wishspeed)\n\t\treturn;\n\n\taddspeed = wishspeed - newspeed;\n\tif (addspeed <= 0)\n\t\treturn;\n\n\tVectorNormalize (wishvel);\n\taccelspeed = sv_accelerate.value * wishspeed * host_frametime;\n\tif (accelspeed > addspeed)\n\t\taccelspeed = addspeed;\n\n\tfor (i=0 ; i<3 ; i++)\n\t\tvelocity[i] += accelspeed * wishvel[i];\n}\n\nstatic void SV_WaterJump (void)\n{\n\tif (sv.time > sv_player->v->teleport_time\n\t|| !sv_player->v->waterlevel)\n\t{\n\t\tsv_player->v->flags = (int)sv_player->v->flags & ~FL_WATERJUMP;\n\t\tsv_player->v->teleport_time = 0;\n\t}\n\tsv_player->v->velocity[0] = sv_player->v->movedir[0];\n\tsv_player->v->velocity[1] = sv_player->v->movedir[1];\n}\n\n\n\nvoid SV_ClientThink (void)\n{\n\tint i;\n\tvec3_t\t\tv_angle;\n\n\tcmd = host_client->lastcmd;\n\tsv_player = host_client->edict;\n\n\tif (host_client->state && host_client->protocol != SCP_BAD)\n\t{\n\t\tsv_player->xv->movement[0] = cmd.forwardmove;\n\t\tsv_player->xv->movement[1] = cmd.sidemove;\n\t\tsv_player->xv->movement[2] = cmd.upmove;\n\n\t\tif (!sv_player->v->fixangle)\n\t\t{\n\t\t\tsv_player->v->v_angle[0] = SHORT2ANGLE(cmd.angles[0]);\n\t\t\tsv_player->v->v_angle[1] = SHORT2ANGLE(cmd.angles[1]);\n\t\t\tsv_player->v->v_angle[2] = SHORT2ANGLE(cmd.angles[2]);\n\t\t}\n\t}\n\n\tSV_SetSSQCInputs(&cmd);\n\n\tif (SV_PlayerPhysicsQC && !host_client->spectator)\n\t{\n\t\tpr_global_struct->time = sv.world.physicstime;\n\t\tpr_global_struct->self = EDICT_TO_PROG(svprogfuncs, sv_player);\n\t\tPR_ExecuteProgram (svprogfuncs, SV_PlayerPhysicsQC);\n\t\treturn;\n\t}\n\n\tif (sv_player->v->movetype == MOVETYPE_NONE)\n\t\treturn;\n\n\tonground = (int)sv_player->v->flags & FL_ONGROUND;\n\n\torigin = sv_player->v->origin;\n\tvelocity = sv_player->v->velocity;\n\n//\tDropPunchAngle ();\n\tfor (i = 0; i < 3; i++)\n\t{\n\t\tif (sv_player->xv->punchangle[i] < 0)\n\t\t{\n\t\t\tsv_player->xv->punchangle[i] += 10 * host_frametime;\n\t\t\tif (sv_player->xv->punchangle[i] > 0)\n\t\t\t\tsv_player->xv->punchangle[i] = 0;\n\t\t}\n\t\tif (sv_player->xv->punchangle[i] > 0)\n\t\t{\n\t\t\tsv_player->xv->punchangle[i] -= 10 * host_frametime;\n\t\t\tif (sv_player->xv->punchangle[i] < 0)\n\t\t\t\tsv_player->xv->punchangle[i] = 0;\n\t\t}\n\t}\n\n//\n// if dead, behave differently\n//\n\tif (sv_player->v->health <= 0 && !host_client->spectator)\n\t\treturn;\n\n//\n// angles\n// show 1/3 the pitch angle and all the roll angle\n\tangles = sv_player->v->angles;\n\n\tVectorCopy (sv_player->v->v_angle, v_angle);\n//\tVectorAdd (sv_player->v->v_angle, sv_player->v->punchangle, v_angle);\n\t//FIXME: gravitydir stuff, the roll value gets destroyed for intents\n\tangles[ROLL] = V_CalcRoll (angles, velocity)*4;\n\tif (!sv_player->v->fixangle)\n\t{\n\t\tangles[PITCH] = -v_angle[PITCH]/3;\n\t\tangles[YAW] = v_angle[YAW];\n\t}\n\n\tif ( (int)sv_player->v->flags & FL_WATERJUMP )\n\t{\n\t\tSV_WaterJump ();\n\t\treturn;\n\t}\n//\n// walk\n//\n\tif ( (sv_player->v->waterlevel >= 2) && (sv_player->v->movetype != MOVETYPE_NOCLIP) )\n\t\tSV_WaterMove (false);\n#ifdef HEXEN2\n\telse if (progstype == PROG_H2 && sv_player->v->movetype == MOVETYPE_FLY)\n\t\tSV_WaterMove (true);\t//just reuse our swimming code for hexen2's flying (quake tends to deny traction).\n#endif\n\telse if (((int)sv_player->xv->pmove_flags&PMF_LADDER) && (sv_player->v->movetype != MOVETYPE_NOCLIP) )\n\t\tSV_LadderMove();\n\telse\n\t\tSV_AirMove ();\n}\n\n#endif\n"
  },
  {
    "path": "engine/server/svhl_game.c",
    "content": "#include \"quakedef.h\"\r\n\r\n/*\r\nI think globals.maxentities is the hard cap, rather than current max like in q1.\r\n*/\r\n\r\n#ifdef HLSERVER\r\n\r\n#include \"winquake.h\"\r\n#include \"svhl_gcapi.h\"\r\n#include \"pr_common.h\"\r\n\r\n#include \"crc.h\"\r\n#include \"model_hl.h\"\r\n\r\n#if defined(_MSC_VER)\r\n #if _MSC_VER >= 1300\r\n  #define __func__ __FUNCTION__\r\n #else\r\n  #define __func__ \"unknown\"\r\n #endif\r\n#else\r\n //I hope you're c99 and have a __func__\r\n#endif\r\n\r\n//extern cvar_t temp1;\r\n#define ignore(s) Con_DPrintf(\"Fixme: \" s \"\\n\")\r\n#define notimpl(l) Con_Printf(\"halflife sv builtin not implemented on line %i\\n\", l)\r\n#define notimpf(f) Con_Printf(\"halflife sv builtin %s not implemented\\n\", f)\r\n#define bi_begin() //if (temp1.ival)Con_Printf(\"enter %s\\n\", __func__)\r\n#define bi_end() //if (temp1.ival)Con_Printf(\"leave %s\\n\", __func__)\r\n#define bi_trace() bi_begin(); bi_end()\r\n\r\n\r\ndllhandle_t *hlgamecode;\r\nSVHL_Globals_t SVHL_Globals;\r\nSVHL_GameFuncs_t SVHL_GameFuncs;\r\n\r\nstatic zonegroup_t hlmapmemgroup;\t//flushed at end-of-map.\r\n\r\n#define MAX_HL_EDICTS 2048\r\nhledict_t *SVHL_Edict;\r\nint SVHL_NumActiveEnts;\r\n\r\nint lastusermessage;\r\n\r\n\r\n\r\n\r\nstring_t QDECL GHL_AllocString(const char *string)\r\n{\r\n\tchar *news;\r\n\tbi_begin();\r\n\tif (!string)\r\n\t\treturn 0;\r\n\tnews = ZG_Malloc(&hlmapmemgroup, strlen(string)+1);\r\n\tmemcpy(news, string, strlen(string)+1);\r\n\tbi_end();\r\n\treturn news - SVHL_Globals.stringbase;\r\n}\r\nint QDECL GHL_PrecacheModel(const char *name)\r\n{\r\n\tint\t\ti;\r\n\tbi_trace();\r\n\r\n\tif (name[0] <= ' ')\r\n\t{\r\n\t\tCon_Printf (\"precache_model: empty string\\n\");\r\n\t\treturn 0;\r\n\t}\r\n\r\n\tfor (i=1 ; i<MAX_PRECACHE_MODELS ; i++)\r\n\t{\r\n\t\tif (!sv.strings.model_precache[i])\r\n\t\t{\r\n\t\t\tif (strlen(name)>=MAX_QPATH-1)\t//probably safest to keep this.\r\n\t\t\t{\r\n\t\t\t\tSV_Error (\"Precache name too long\");\r\n\t\t\t\treturn 0;\r\n\t\t\t}\r\n\t\t\tname = sv.strings.model_precache[i] = SVHL_Globals.stringbase+GHL_AllocString(name);\r\n\r\n\t\t\tif (!strcmp(name + strlen(name) - 4, \".bsp\"))\r\n\t\t\t\tsv.models[i] = Mod_FindName(name);\r\n\r\n\t\t\tif (sv.state != ss_loading)\r\n\t\t\t{\r\n\t\t\t\tCon_DPrintf(\"Delayed model precache: %s\\n\", name);\r\n\t\t\t\tMSG_WriteByte(&sv.reliable_datagram, svcfte_precache);\r\n\t\t\t\tMSG_WriteShort(&sv.reliable_datagram, i);\r\n\t\t\t\tMSG_WriteString(&sv.reliable_datagram, name);\r\n#ifdef NQPROT\r\n\t\t\t\tMSG_WriteByte(&sv.nqreliable_datagram, svcdp_precache);\r\n\t\t\t\tMSG_WriteShort(&sv.nqreliable_datagram, i);\r\n\t\t\t\tMSG_WriteString(&sv.nqreliable_datagram, name);\r\n#endif\r\n\t\t\t}\r\n\r\n\t\t\treturn i;\r\n\t\t}\r\n\t\tif (!strcmp(sv.strings.model_precache[i], name))\r\n\t\t{\r\n\t\t\treturn i;\r\n\t\t}\r\n\t}\r\n\tSV_Error (\"GHL_precache_model: overflow\");\r\n\treturn 0;\r\n}\r\nint QDECL GHL_PrecacheSound(char *name)\r\n{\r\n\tint\t\ti;\r\n\tbi_trace();\r\n\r\n\tif (name[0] <= ' ')\r\n\t{\r\n\t\tCon_Printf (\"precache_sound: empty string\\n\");\r\n\t\treturn 0;\r\n\t}\r\n\r\n\tfor (i=1 ; i<MAX_PRECACHE_SOUNDS ; i++)\r\n\t{\r\n\t\tif (!*sv.strings.sound_precache[i])\r\n\t\t{\r\n\t\t\tif (strlen(name)>=MAX_QPATH-1)\t//probably safest to keep this.\r\n\t\t\t{\r\n\t\t\t\tSV_Error (\"Precache name too long\");\r\n\t\t\t\treturn 0;\r\n\t\t\t}\r\n\t\t\tstrcpy(sv.strings.sound_precache[i], name);\r\n\t\t\tname = sv.strings.sound_precache[i];\r\n\r\n\t\t\tif (sv.state != ss_loading)\r\n\t\t\t{\r\n\t\t\t\tCon_DPrintf(\"Delayed sound precache: %s\\n\", name);\r\n\t\t\t\tMSG_WriteByte(&sv.reliable_datagram, svcfte_precache);\r\n\t\t\t\tMSG_WriteShort(&sv.reliable_datagram, -i);\r\n\t\t\t\tMSG_WriteString(&sv.reliable_datagram, name);\r\n#ifdef NQPROT\r\n\t\t\t\tMSG_WriteByte(&sv.nqreliable_datagram, svcdp_precache);\r\n\t\t\t\tMSG_WriteShort(&sv.nqreliable_datagram, -i);\r\n\t\t\t\tMSG_WriteString(&sv.nqreliable_datagram, name);\r\n#endif\r\n\t\t\t}\r\n\r\n\t\t\treturn i;\r\n\t\t}\r\n\t\tif (!strcmp(sv.strings.sound_precache[i], name))\r\n\t\t{\r\n\t\t\treturn i;\r\n\t\t}\r\n\t}\r\n\tSV_Error (\"GHL_precache_sound: overflow\");\r\n\treturn 0;\r\n}\r\nvoid QDECL GHL_SetModel(hledict_t *ed, char *modelname)\r\n{\r\n\tmodel_t *mod;\r\n\tint mdlidx = GHL_PrecacheModel(modelname);\r\n\tbi_trace();\r\n\ted->v.modelindex = mdlidx;\r\n\ted->v.model = sv.strings.model_precache[mdlidx] - SVHL_Globals.stringbase;\r\n\r\n\tmod = sv.models[mdlidx];\r\n\tif (mod)\r\n\t{\r\n\t\tVectorCopy(mod->mins, ed->v.mins);\r\n\t\tVectorCopy(mod->maxs, ed->v.maxs);\r\n\t\tVectorSubtract(mod->maxs, mod->mins, ed->v.size);\r\n\t}\r\n\tSVHL_LinkEdict(ed, false);\r\n}\r\nunk QDECL GHL_ModelIndex(unk){notimpf(__func__);}\r\nint QDECL GHL_ModelFrames(int midx)\r\n{\r\n\tbi_trace();\r\n\t//returns the number of frames(sequences I assume) this model has\r\n\treturn Mod_GetFrameCount(sv.models[mdlidx], surfaceidx);\r\n}\r\nvoid QDECL GHL_SetSize(hledict_t *ed, float *mins, float *maxs)\r\n{\r\n\tbi_trace();\r\n\tVectorCopy(mins, ed->v.mins);\r\n\tVectorCopy(maxs, ed->v.maxs);\r\n\tSVHL_LinkEdict(ed, false);\r\n}\r\nvoid QDECL GHL_ChangeLevel(char *nextmap, char *startspot)\r\n{\r\n\tbi_trace();\r\n\tCbuf_AddText(va(\"changelevel %s %s@%f@%f@%f\\n\", nextmap, startspot, SVHL_Globals.landmark[0], SVHL_Globals.landmark[1], SVHL_Globals.landmark[2]), RESTRICT_PROGS);\r\n}\r\nunk QDECL GHL_GetSpawnParms(unk){notimpf(__func__);}\r\nunk QDECL GHL_SaveSpawnParms(unk){notimpf(__func__);}\r\nfloat QDECL GHL_VecToYaw(float *inv)\r\n{\r\n\tvec3_t outa;\r\n\tbi_trace();\r\n\r\n\tVectorAngles(inv, NULL, outa);\r\n\treturn outa[1];\r\n}\r\nvoid QDECL GHL_VecToAngles(float *inv, float *outa)\r\n{\r\n\tbi_trace();\r\n\tVectorAngles(inv, NULL, outa);\r\n}\r\n#define DEG2RAD( a ) ( a * M_PI ) / 180.0F\r\nvoid QDECL GHL_MoveToOrigin(hledict_t *ent, vec3_t dest, float dist, int moveflags)\r\n{\r\n\t//mode 0: move_normal\r\n\t//mode 1: no idea\r\n\t//mode 2: test only\r\n//\tfloat\t\tdz;\r\n\tvec3_t\t\toldorg, neworg, end;\r\n\ttrace_t\t\ttrace;\r\n//\tint\t\t\ti;\r\n\tint eflags = ent->v.flags;\r\n\tconst vec3_t up = {0, 0, 1};\r\n\tvec3_t move;\r\n\tqboolean relink = true;\r\n\tqboolean domove = true;\r\n\r\n\tbi_trace();\r\n\r\n\tif (moveflags)\r\n\t{\t//strafe. just move directly.\r\n\t\tVectorSubtract(dest, ent->v.origin, move);\r\n\t\tmove[2] = 0;\r\n\t\tVectorNormalize(move);\r\n\t\tVectorMA(ent->v.origin, dist, move, move);\r\n\t}\r\n\telse\r\n\t{\r\n\t\tfloat yaw = DEG2RAD(ent->v.angles[1]);\r\n\t\tmove[0] = cos(yaw) * dist;\r\n\t\tmove[1] = sin(yaw) * dist;\r\n\t\tmove[2] = 0;\r\n\t}\r\n\r\n// try the move\t\r\n\tVectorCopy (ent->v.origin, oldorg);\r\n\tVectorAdd (ent->v.origin, move, neworg);\r\n\r\n#if 0\r\n// flying monsters don't step up\r\n\tif (eflags & (FL_SWIM | FL_FLY))\r\n\t{\r\n\t// try one move with vertical motion, then one without\r\n\t\tfor (i=0 ; i<2 ; i++)\r\n\t\t{\r\n\t\t\tVectorAdd (ent->v.origin, move, neworg);\r\n\t\t\tif (!noenemy)\r\n\t\t\t{\r\n\t\t\t\tenemy = (wedict_t*)PROG_TO_EDICT(world->progs, ent->v->enemy);\r\n\t\t\t\tif (i == 0 && enemy->entnum)\r\n\t\t\t\t{\r\n\t\t\t\t\tVectorSubtract(ent->v->origin, ((wedict_t*)PROG_TO_EDICT(world->progs, ent->v->enemy))->v->origin, end);\r\n\t\t\t\t\tdz = DotProduct(end, axis[2]);\r\n\t\t\t\t\tif (eflags & FLH2_HUNTFACE) /*get the ent's origin_z to match its victims face*/\r\n\t\t\t\t\t\tdz += ((wedict_t*)PROG_TO_EDICT(world->progs, ent->v->enemy))->v->view_ofs[2];\r\n\t\t\t\t\tif (dz > 40)\r\n\t\t\t\t\t\tVectorMA(neworg, -8, up, neworg);\r\n\t\t\t\t\tif (dz < 30)\r\n\t\t\t\t\t\tVectorMA(neworg, 8, up, neworg);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\ttrace = World_Move (world, ent->v->origin, ent->v->mins, ent->v->maxs, neworg, false, ent);\r\n\t\t\tif (set_move_trace)\r\n\t\t\t\tset_move_trace(world->progs, set_trace_globs, &trace);\r\n\t\r\n\t\t\tif (trace.fraction == 1)\r\n\t\t\t{\r\n\t\t\t\tif ( (eflags & FL_SWIM) && !(World_PointContents(world, trace.endpos) & FTECONTENTS_FLUID))\r\n\t\t\t\t\tcontinue;\t// swim monster left water\r\n\t\r\n\t\t\t\tif (domove)\r\n\t\t\t\t\tVectorCopy (trace.endpos, ent->v->origin);\r\n\t\t\t\tif (relink)\r\n\t\t\t\t\tWorld_LinkEdict (world, ent, true);\r\n\t\t\t\treturn true;\r\n\t\t\t}\r\n\t\t\t\r\n\t\t\tif (noenemy || !enemy->entnum)\r\n\t\t\t\tbreak;\r\n\t\t}\r\n\t\t\r\n\t\treturn false;\r\n\t}\r\n#endif\r\n\r\n// push down from a step height above the wished position\r\n\tVectorMA(neworg, movevars.stepheight, up, neworg);\r\n\tVectorMA(neworg, movevars.stepheight*-2, up, end);\r\n\r\n\ttrace = SVHL_Move(neworg, ent->v.mins, ent->v.maxs, end, 0, 0, ent);\r\n\r\n\tif (trace.allsolid)\r\n\t\treturn false;\r\n\r\n\tif (trace.startsolid)\r\n\t{\r\n\t\t//move up by an extra step, if needed\r\n\t\tVectorMA(neworg, -movevars.stepheight, up, neworg);\r\n\t\ttrace = SVHL_Move (neworg, ent->v.mins, ent->v.maxs, end, 0, 0, ent);\r\n\t\tif (trace.allsolid || trace.startsolid)\r\n\t\t\treturn false;\r\n\t}\r\n\tif (trace.fraction == 1)\r\n\t{\r\n\t// if monster had the ground pulled out, go ahead and fall\r\n\t\tif ( (int)eflags & FL_PARTIALGROUND )\r\n\t\t{\r\n\t\t\tif (domove)\r\n\t\t\t{\r\n\t\t\t\tVectorAdd (ent->v.origin, move, ent->v.origin);\r\n\t\t\t\tif (relink)\r\n\t\t\t\t\tSVHL_LinkEdict (ent, true);\r\n\t\t\t\tent->v.flags = (int)eflags & ~FL_ONGROUND;\r\n\t\t\t}\r\n//\tCon_Printf (\"fall down\\n\"); \r\n\t\t\treturn true;\r\n\t\t}\r\n\t\r\n\t\treturn false;\t\t// walked off an edge\r\n\t}\r\n\r\n// check point traces down for dangling corners\r\n\tif (domove)\r\n\t\tVectorCopy (trace.endpos, ent->v.origin);\r\n\t\r\n/*\tif (!World_CheckBottom (world, ent, up))\r\n\t{\r\n\t\tif ( (int)ent->v->flags & FL_PARTIALGROUND )\r\n\t\t{\t// entity had floor mostly pulled out from underneath it\r\n\t\t\t// and is trying to correct\r\n\t\t\tif (relink)\r\n\t\t\t\tSVHL_LinkEdict (ent, true);\r\n\t\t\treturn true;\r\n\t\t}\r\n\r\n\t\tif (domove)\r\n\t\t\tVectorCopy (oldorg, ent->v->origin);\r\n\t\treturn false;\r\n\t}\r\n*/\r\n\tif ( (int)ent->v.flags & FL_PARTIALGROUND )\r\n\t{\r\n//\t\tCon_Printf (\"back on ground\\n\"); \r\n\t\tent->v.flags &= ~FL_PARTIALGROUND;\r\n\t}\r\n\tent->v.groundentity = trace.ent;\r\n\r\n// the move is ok\r\n\tif (relink)\r\n\t\tSVHL_LinkEdict (ent, true);\r\n\treturn true;\r\n}\r\nunk QDECL GHL_ChangeYaw(unk){notimpf(__func__);}\r\nunk QDECL GHL_ChangePitch(unk){notimpf(__func__);}\r\nhledict_t *QDECL GHL_FindEntityByString(hledict_t *last, char *field, char *value)\r\n{\r\n\thledict_t *ent;\r\n\tint i;\r\n\tint ofs;\r\n\tstring_t str;\r\n\tbi_trace();\r\n\tif (!strcmp(field, \"targetname\"))\r\n\t\tofs = (char*)&((hledict_t *)NULL)->v.targetname - (char*)NULL;\r\n\telse if (!strcmp(field, \"classname\"))\r\n\t\tofs = (char*)&((hledict_t *)NULL)->v.classname - (char*)NULL;\r\n\telse\r\n\t{\r\n\t\tCon_Printf(\"Fixme: Look for %s=%s\\n\", field, value);\r\n\t\treturn NULL;\r\n\t}\r\n\t\r\n\tif (last)\r\n\t\ti=last-SVHL_Edict+1;\r\n\telse\r\n\t\ti = 0;\r\n\tfor (; i<SVHL_Globals.maxentities; i++)\r\n\t{\r\n\t\tent = &SVHL_Edict[i];\r\n\t\tif (ent->isfree)\r\n\t\t\tcontinue;\r\n\t\tstr = *(string_t*)((char*)ent+ofs);\r\n\t\tif (str && !strcmp(SVHL_Globals.stringbase+str, value))\r\n\t\t\treturn ent;\r\n\t}\r\n\treturn SVHL_Edict;\r\n}\r\nvoid Sh_CalcPointLight(vec3_t point, vec3_t light);\r\nint QDECL GHL_GetEntityIllum(hledict_t *ent)\r\n{\r\n\tvec3_t diffuse, ambient, dir;\r\n\tfloat lev = 0;\r\n#if defined(RTLIGHTS) && !defined(SERVERONLY)\r\n\tSh_CalcPointLight(ent->v.origin, ambient);\r\n\tlev += VectorLength(ambient);\r\n\r\n\tif (!r_shadow_realtime_world.ival || r_shadow_realtime_world_lightmaps.value)\r\n#endif\r\n\t{\r\n\t\tsv.world.worldmodel->funcs.LightPointValues(sv.world.worldmodel, ent->v.origin, ambient, diffuse, dir);\r\n\t\tlev += (VectorLength(ambient) + VectorLength(diffuse)/2.0)/256;\r\n\t}\r\n\treturn lev * 255; //I assume its 0-255, no idea\r\n}\r\nhledict_t *QDECL GHL_FindEntityInSphere(hledict_t *last, float *org, float radius)\r\n{\r\n\tint i, j;\r\n\tvec3_t eorg;\r\n\thledict_t *ent;\r\n\r\n\tbi_trace();\r\n\r\n\tradius = radius*radius;\r\n\r\n\tif (last)\r\n\t\ti=last-SVHL_Edict+1;\r\n\telse\r\n\t\ti = 0;\r\n\tfor (; i<SVHL_Globals.maxentities; i++)\r\n\t{\r\n\t\tent = &SVHL_Edict[i];\r\n\t\tif (ent->isfree)\r\n\t\t\tcontinue;\r\n\t\tif (!ent->v.solid)\r\n\t\t\tcontinue;\r\n\t\tfor (j=0; j<3; j++)\r\n\t\t\teorg[j] = org[j] - (ent->v.origin[j] + (ent->v.mins[j] + ent->v.maxs[j])*0.5);\r\n\t\tif (DotProduct(eorg,eorg) > radius)\r\n\t\t\tcontinue;\r\n\r\n\t\t//its close enough\r\n\t\treturn ent;\r\n\t}\r\n\treturn NULL;\r\n}\r\nhledict_t *QDECL GHL_FindClientInPVS(hledict_t *ed)\r\n{\r\n\tqbyte\t*viewerpvs;\r\n\tint best = 0, i;\r\n\tfloat bestdist = 99999999;\t//HL maps are limited in size anyway\r\n\tfloat d;\r\n\tint clusternum;\r\n\tvec3_t ofs;\r\n\thledict_t *other;\r\n\r\n\tbi_trace();\r\n\r\n\t//fixme: we need to track some state\r\n\t//a different client should be returned each call _per ent_ (so it can be used once per frame)\r\n\r\n\tviewerpvs = sv.world.worldmodel->funcs.ClusterPVS(sv.world.worldmodel, sv.world.worldmodel->funcs.ClusterForPoint(sv.world.worldmodel, ed->v.origin), NULL, 0);\r\n\r\n\tfor (i = 0; i < svs.allocated_client_slots; i++)\r\n\t{\r\n\t\tif (svs.clients[i].state == cs_spawned)\r\n\t\t{\r\n\t\t\tother = &SVHL_Edict[i+1];\r\n\t\t\tif (ed == other)\r\n\t\t\t\tcontinue;\t//ignore yourself.\r\n\t\t\tif (svs.clients[i].spectator)\r\n\t\t\t\tcontinue;\t//ignore spectators\r\n\r\n\t\t\tclusternum = sv.world.worldmodel->funcs.ClusterForPoint(sv.world.worldmodel, other->v.origin)-1;/*pvs is 1 based, leafs are 0 based*/\r\n\t\t\tif (viewerpvs[clusternum>>3] & (1<<(clusternum&7)))\r\n\t\t\t{\r\n\t\t\t\tVectorSubtract(ed->v.origin, other->v.origin, ofs);\r\n\t\t\t\td = DotProduct(ofs, ofs);\r\n\r\n\t\t\t\tif (d < bestdist)\r\n\t\t\t\t{\r\n\t\t\t\t\tbestdist = d;\r\n\t\t\t\t\tbest = i+1;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\tif (best)\r\n\t\treturn &SVHL_Edict[best];\r\n\treturn NULL;\r\n}\r\nunk QDECL GHL_EntitiesInPVS(unk){notimpf(__func__);}\r\nvoid QDECL GHL_MakeVectors(float *angles)\r\n{\r\n\tbi_trace();\r\n\tAngleVectors(angles, SVHL_Globals.v_forward, SVHL_Globals.v_right, SVHL_Globals.v_up);\r\n}\r\nvoid QDECL GHL_AngleVectors(float *angles, float *forward, float *right, float *up)\r\n{\r\n\tbi_trace();\r\n\tAngleVectors(angles, forward, right, up);\r\n}\r\n\r\n///////////////////////////////////////////////////////////\r\n\r\nhledict_t *QDECL GHL_CreateEntity(void)\r\n{\t\r\n\tint i;\r\n\tstatic int spawnnumber;\r\n\tbi_trace();\r\n\tspawnnumber++;\r\n\r\n\tfor (i = sv.allocated_client_slots; i < SVHL_NumActiveEnts; i++)\r\n\t{\r\n\t\tif (SVHL_Edict[i].isfree)\r\n\t\t{\r\n\t\t\tif (SVHL_Edict[i].freetime > sv.time)\r\n\t\t\t\tcontinue;\r\n\r\n\t\t\tmemset(&SVHL_Edict[i], 0, sizeof(SVHL_Edict[i]));\r\n\t\t\tSVHL_Edict[i].spawnnumber = spawnnumber;\r\n\t\t\tSVHL_Edict[i].v.edict = &SVHL_Edict[i];\r\n\t\t\treturn &SVHL_Edict[i];\r\n\t\t}\r\n\t}\r\n\tif (i < MAX_HL_EDICTS)\r\n\t{\r\n\t\tSVHL_NumActiveEnts++;\r\n\t\tmemset(&SVHL_Edict[i], 0, sizeof(SVHL_Edict[i]));\r\n\t\tSVHL_Edict[i].spawnnumber = spawnnumber;\r\n\t\tSVHL_Edict[i].v.edict = &SVHL_Edict[i];\r\n\t\treturn &SVHL_Edict[i];\r\n\t}\r\n\tSV_Error(\"Ran out of free edicts\");\r\n\treturn NULL;\r\n}\r\nvoid QDECL GHL_RemoveEntity(hledict_t *ed)\r\n{\r\n\tbi_trace();\r\n\tSVHL_UnlinkEdict(ed);\r\n\ted->isfree = true;\r\n\ted->freetime = sv.time+2;\r\n}\r\nhledict_t *QDECL GHL_CreateNamedEntity(string_t name)\r\n{\r\n\tvoid (QDECL *spawnfunc)(hlentvars_t *evars);\r\n\thledict_t *ed;\r\n\tbi_trace();\r\n\ted = GHL_CreateEntity();\r\n\tif (!ed)\r\n\t\treturn NULL;\r\n\ted->v.classname = name;\r\n\r\n\tspawnfunc = (void(QDECL *)(hlentvars_t*))GetProcAddress((HINSTANCE)hlgamecode, name+SVHL_Globals.stringbase);\r\n\tif (spawnfunc)\r\n\t\tspawnfunc(&ed->v);\r\n\treturn ed;\r\n}\r\nvoid *QDECL GHL_PvAllocEntPrivateData(hledict_t *ed, long quant)\r\n{\r\n\tbi_trace();\r\n\tif (!ed)\r\n\t\treturn NULL;\r\n\ted->moddata = ZG_Malloc(&hlmapmemgroup, quant);\r\n\treturn ed->moddata;\r\n}\r\nunk QDECL GHL_PvEntPrivateData(unk)\r\n{\r\n\tbi_trace();\r\n\tnotimpf(__func__);\r\n}\r\nunk QDECL GHL_FreeEntPrivateData(unk)\r\n{\r\n\tbi_trace();\r\n\tnotimpf(__func__);\r\n}\r\nunk QDECL GHL_GetVarsOfEnt(unk)\r\n{\r\n\tbi_trace();\r\n\tnotimpf(__func__);\r\n}\r\nhledict_t *QDECL GHL_PEntityOfEntOffset(int ednum)\r\n{\r\n\tbi_trace();\r\n\treturn (hledict_t *)(ednum + (char*)SVHL_Edict);\r\n}\r\nint QDECL GHL_EntOffsetOfPEntity(hledict_t *ed)\r\n{\r\n\tbi_trace();\r\n\treturn (char*)ed - (char*)SVHL_Edict;\r\n}\r\nint QDECL GHL_IndexOfEdict(hledict_t *ed)\r\n{\r\n\tbi_trace();\r\n\treturn ed - SVHL_Edict;\r\n}\r\nhledict_t *QDECL GHL_PEntityOfEntIndex(int idx)\r\n{\r\n\tbi_trace();\r\n\treturn &SVHL_Edict[idx];\r\n}\r\nunk QDECL GHL_FindEntityByVars(unk)\r\n{\r\n\tbi_trace();\r\n\tnotimpf(__func__);\r\n}\r\n\r\n///////////////////////////////////////////////////////\r\n\r\nunk QDECL GHL_MakeStatic(unk){notimpf(__func__);}\r\nunk QDECL GHL_EntIsOnFloor(unk){notimpf(__func__);}\r\nint QDECL GHL_DropToFloor(hledict_t *ed)\r\n{\r\n\tvec3_t top;\r\n\tvec3_t bottom;\r\n\ttrace_t tr;\r\n\tbi_trace();\r\n\tVectorCopy(ed->v.origin, top);\r\n\tVectorCopy(ed->v.origin, bottom);\r\n\ttop[2] += 1;\r\n\tbottom[2] -= 256;\r\n\ttr = SVHL_Move(top, ed->v.mins, ed->v.maxs, bottom, 0, 0, ed);\r\n\tVectorCopy(tr.endpos, ed->v.origin);\r\n\treturn tr.fraction != 0 && tr.fraction != 1;\r\n}\r\nint QDECL GHL_WalkMove(hledict_t *ent, float yaw, float dist, int mode)\r\n{\r\n\t//mode 0: no idea\r\n\t//mode 1: no idea\r\n\t//mode 2: test only\r\n//\tfloat\t\tdz;\r\n\tvec3_t\t\toldorg, neworg, end;\r\n\ttrace_t\t\ttrace;\r\n//\tint\t\t\ti;\r\n\tint eflags = ent->v.flags;\r\n\tconst vec3_t up = {0, 0, 1};\r\n\tvec3_t move;\r\n\tqboolean relink = mode != 2;\r\n\tqboolean domove = mode != 2;\r\n\r\n\tbi_trace();\r\n\r\n\tyaw = DEG2RAD(yaw);\r\n\tmove[0] = cos(yaw) * dist;\r\n\tmove[1] = sin(yaw) * dist;\r\n\tmove[2] = 0;\r\n\r\n// try the move\t\r\n\tVectorCopy (ent->v.origin, oldorg);\r\n\tVectorAdd (ent->v.origin, move, neworg);\r\n\r\n#if 0\r\n// flying monsters don't step up\r\n\tif (eflags & (FL_SWIM | FL_FLY))\r\n\t{\r\n\t// try one move with vertical motion, then one without\r\n\t\tfor (i=0 ; i<2 ; i++)\r\n\t\t{\r\n\t\t\tVectorAdd (ent->v.origin, move, neworg);\r\n\t\t\tif (!noenemy)\r\n\t\t\t{\r\n\t\t\t\tenemy = (wedict_t*)PROG_TO_EDICT(world->progs, ent->v->enemy);\r\n\t\t\t\tif (i == 0 && enemy->entnum)\r\n\t\t\t\t{\r\n\t\t\t\t\tVectorSubtract(ent->v->origin, ((wedict_t*)PROG_TO_EDICT(world->progs, ent->v->enemy))->v->origin, end);\r\n\t\t\t\t\tdz = DotProduct(end, axis[2]);\r\n\t\t\t\t\tif (eflags & FLH2_HUNTFACE) /*get the ent's origin_z to match its victims face*/\r\n\t\t\t\t\t\tdz += ((wedict_t*)PROG_TO_EDICT(world->progs, ent->v->enemy))->v->view_ofs[2];\r\n\t\t\t\t\tif (dz > 40)\r\n\t\t\t\t\t\tVectorMA(neworg, -8, up, neworg);\r\n\t\t\t\t\tif (dz < 30)\r\n\t\t\t\t\t\tVectorMA(neworg, 8, up, neworg);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\ttrace = World_Move (world, ent->v->origin, ent->v->mins, ent->v->maxs, neworg, false, ent);\r\n\t\t\tif (set_move_trace)\r\n\t\t\t\tset_move_trace(world->progs, set_trace_globs, &trace);\r\n\t\r\n\t\t\tif (trace.fraction == 1)\r\n\t\t\t{\r\n\t\t\t\tif ( (eflags & FL_SWIM) && !(World_PointContents(world, trace.endpos) & FTECONTENTS_FLUID))\r\n\t\t\t\t\tcontinue;\t// swim monster left water\r\n\t\r\n\t\t\t\tif (domove)\r\n\t\t\t\t\tVectorCopy (trace.endpos, ent->v->origin);\r\n\t\t\t\tif (relink)\r\n\t\t\t\t\tWorld_LinkEdict (world, ent, true);\r\n\t\t\t\treturn true;\r\n\t\t\t}\r\n\t\t\t\r\n\t\t\tif (noenemy || !enemy->entnum)\r\n\t\t\t\tbreak;\r\n\t\t}\r\n\t\t\r\n\t\treturn false;\r\n\t}\r\n#endif\r\n\r\n// push down from a step height above the wished position\r\n\tVectorMA(neworg, movevars.stepheight, up, neworg);\r\n\tVectorMA(neworg, movevars.stepheight*-2, up, end);\r\n\r\n\ttrace = SVHL_Move(neworg, ent->v.mins, ent->v.maxs, end, 0, 0, ent);\r\n\r\n\tif (trace.allsolid)\r\n\t\treturn false;\r\n\r\n\tif (trace.startsolid)\r\n\t{\r\n\t\t//move up by an extra step, if needed\r\n\t\tVectorMA(neworg, -movevars.stepheight, up, neworg);\r\n\t\ttrace = SVHL_Move (neworg, ent->v.mins, ent->v.maxs, end, 0, 0, ent);\r\n\t\tif (trace.allsolid || trace.startsolid)\r\n\t\t\treturn false;\r\n\t}\r\n\tif (trace.fraction == 1)\r\n\t{\r\n\t// if monster had the ground pulled out, go ahead and fall\r\n\t\tif ( (int)eflags & FL_PARTIALGROUND )\r\n\t\t{\r\n\t\t\tif (domove)\r\n\t\t\t{\r\n\t\t\t\tVectorAdd (ent->v.origin, move, ent->v.origin);\r\n\t\t\t\tif (relink)\r\n\t\t\t\t\tSVHL_LinkEdict (ent, true);\r\n\t\t\t\tent->v.flags = (int)eflags & ~FL_ONGROUND;\r\n\t\t\t}\r\n//\tCon_Printf (\"fall down\\n\"); \r\n\t\t\treturn true;\r\n\t\t}\r\n\t\r\n\t\treturn false;\t\t// walked off an edge\r\n\t}\r\n\r\n// check point traces down for dangling corners\r\n\tif (domove)\r\n\t\tVectorCopy (trace.endpos, ent->v.origin);\r\n\t\r\n/*\tif (!World_CheckBottom (world, ent, up))\r\n\t{\r\n\t\tif ( (int)ent->v->flags & FL_PARTIALGROUND )\r\n\t\t{\t// entity had floor mostly pulled out from underneath it\r\n\t\t\t// and is trying to correct\r\n\t\t\tif (relink)\r\n\t\t\t\tSVHL_LinkEdict (ent, true);\r\n\t\t\treturn true;\r\n\t\t}\r\n\r\n\t\tif (domove)\r\n\t\t\tVectorCopy (oldorg, ent->v->origin);\r\n\t\treturn false;\r\n\t}\r\n*/\r\n\tif ( (int)ent->v.flags & FL_PARTIALGROUND )\r\n\t{\r\n//\t\tCon_Printf (\"back on ground\\n\"); \r\n\t\tent->v.flags &= ~FL_PARTIALGROUND;\r\n\t}\r\n\tent->v.groundentity = trace.ent;\r\n\r\n// the move is ok\r\n\tif (relink)\r\n\t\tSVHL_LinkEdict (ent, true);\r\n\treturn true;\r\n}\r\nvoid QDECL GHL_SetOrigin(hledict_t *ed, float *neworg)\r\n{\r\n\tbi_trace();\r\n\tVectorCopy(neworg, ed->v.origin);\r\n\tSVHL_LinkEdict(ed, false);\r\n}\r\nvoid QDECL GHL_EmitSound(hledict_t *ed, int chan, char *soundname, float vol, float atten, int flags, int pitch)\r\n{\r\n\tbi_trace();\r\n\tif (*soundname == '!')\r\n\t\treturn;\t//would need us to parse sound/sentances.txt I guess\r\n\tSV_StartSound(ed-SVHL_Edict, ed->v.origin, ~0, chan, soundname, vol*255, atten, pitch, 0, 0);\r\n}\r\nvoid QDECL GHL_EmitAmbientSound(hledict_t *ed, float *org, char *soundname, float vol, float atten, unsigned int flags, int pitch)\r\n{\r\n\tbi_trace();\r\n\tSV_StartSound(0, org, ~0, 0, soundname, vol*255, atten, pitch, 0, 0);\r\n}\r\nvoid QDECL GHL_TraceLine(float *start, float *end, int flags, hledict_t *ignore, hltraceresult_t *result)\r\n{\r\n\ttrace_t t;\r\n\tbi_trace();\r\n\t\r\n\tt = SVHL_Move(start, vec3_origin, vec3_origin, end, flags, 0, ignore);\r\n\r\n\tresult->allsolid = t.allsolid;\r\n\tresult->startsolid = t.startsolid;\r\n\tresult->inopen = t.inopen;\r\n\tresult->inwater = t.inwater;\r\n\tresult->fraction = t.fraction;\r\n\tVectorCopy(t.endpos, result->endpos);\r\n\tresult->planedist = t.plane.dist;\r\n\tVectorCopy(t.plane.normal, result->planenormal);\r\n\tresult->touched = t.ent;\r\n\tif (!result->touched)\r\n\t\tresult->touched = &SVHL_Edict[0];\r\n\tresult->hitgroup = 0;\r\n}\r\nunk QDECL GHL_TraceToss(unk){notimpf(__func__);}\r\nunk QDECL GHL_TraceMonsterHull(unk){notimpf(__func__);}\r\nvoid QDECL GHL_TraceHull(float *start, float *end, int flags, int hullnum, hledict_t *ignore, hltraceresult_t *result)\r\n{\r\n\ttrace_t t;\r\n\tbi_trace();\r\n\r\n\tt = SVHL_Move(start, sv.world.worldmodel->hulls[hullnum].clip_mins, sv.world.worldmodel->hulls[hullnum].clip_maxs, end, flags, 0, ignore);\r\n\r\n\tresult->allsolid = t.allsolid;\r\n\tresult->startsolid = t.startsolid;\r\n\tresult->inopen = t.inopen;\r\n\tresult->inwater = t.inwater;\r\n\tresult->fraction = t.fraction;\r\n\tVectorCopy(t.endpos, result->endpos);\r\n\tresult->planedist = t.plane.dist;\r\n\tVectorCopy(t.plane.normal, result->planenormal);\r\n\tresult->touched = t.ent;\r\n\tresult->hitgroup = 0;\r\n}\r\nunk QDECL GHL_TraceModel(unk){notimpf(__func__);}\r\nchar *QDECL GHL_TraceTexture(hledict_t *againstent, vec3_t start, vec3_t end)\r\n{\r\n\ttrace_t tr;\r\n\tbi_trace();\r\n\tsv.world.worldmodel->funcs.NativeTrace(sv.world.worldmodel, 0, NULLFRAMESTATE, NULL, start, end, vec3_origin, vec3_origin, false, MASK_WORLDSOLID, &tr);\r\n\treturn tr.surface->name;\r\n}\r\nunk QDECL GHL_TraceSphere(unk){notimpf(__func__);}\r\nunk QDECL GHL_GetAimVector(unk){notimpf(__func__);}\r\nvoid QDECL GHL_ServerCommand(char *cmd)\r\n{\r\n\tbi_trace();\r\n\tif (!strcmp(cmd, \"reload\\n\"))\r\n\t\tcmd = \"restart\\n\";\r\n\tCbuf_AddText(cmd, RESTRICT_PROGS);\r\n}\r\nvoid QDECL GHL_ServerExecute(void)\r\n{\r\n\tbi_trace();\r\n\tCbuf_ExecuteLevel(RESTRICT_PROGS);\r\n}\r\nunk QDECL GHL_ClientCommand(unk){notimpf(__func__);}\r\nunk QDECL GHL_ParticleEffect(unk){notimpf(__func__);}\r\nvoid QDECL GHL_LightStyle(int stylenum, char *stylestr)\r\n{\r\n\tvec3_t rgb = {1,1,1};\r\n\tbi_trace();\r\n\tPF_applylightstyle(stylenum, stylestr, rgb);\r\n}\r\nint QDECL GHL_DecalIndex(char *decalname)\r\n{\r\n\tbi_trace();\r\n\tCon_Printf(\"Fixme: precache decal %s\\n\", decalname);\r\n\treturn 0;\r\n}\r\nint QDECL GHL_PointContents(float *org)\r\n{\r\n\tbi_trace();\r\n\treturn Q1CONTENTS_EMPTY;\r\n}\r\n\r\nint svhl_messagedest;\r\nvec3_t svhl_messageorigin;\r\nhledict_t *svhl_messageent;\r\nvoid QDECL GHL_MessageBegin(int dest, int type, float *org, hledict_t *ent)\r\n{\r\n\tbi_trace();\r\n\r\n\tsvhl_messagedest = dest;\r\n\tif (org)\r\n\t\tVectorCopy(org, svhl_messageorigin);\r\n\telse\r\n\t\tVectorClear(svhl_messageorigin);\r\n\tsvhl_messageent = ent;\r\n\r\n\tif (sv.multicast.cursize)\r\n\t{\r\n\t\tCon_Printf(\"MessageBegin called without MessageEnd\\n\");\r\n\t\tSZ_Clear (&sv.multicast);\r\n\t}\r\n\tMSG_WriteByte(&sv.multicast, svcfte_cgamepacket);\r\n\tMSG_WriteShort(&sv.multicast, 0);\r\n\tMSG_WriteByte(&sv.multicast, type);\r\n}\r\nvoid QDECL GHL_MessageEnd(unk)\r\n{\r\n\tunsigned short len;\r\n\tclient_t *cl;\r\n\r\n\tbi_trace();\r\n\r\n\tif (!sv.multicast.cursize)\r\n\t{\r\n\t\tCon_Printf(\"MessageEnd called without MessageBegin\\n\");\r\n\t\treturn;\r\n\t}\r\n\r\n\t//update the length\r\n\tlen = sv.multicast.cursize - 3;\r\n\tsv.multicast.data[1] = len&0xff;\r\n\tsv.multicast.data[2] = len>>8;\r\n\r\n\tswitch(svhl_messagedest)\r\n\t{\r\n\tcase MSG_BROADCAST:\r\n\t\tSZ_Write(&sv.datagram, sv.multicast.data, sv.multicast.cursize);\r\n\t\tbreak;\r\n\tcase MSG_ONE:\r\n\t\tcl = &svs.clients[svhl_messageent - SVHL_Edict - 1];\r\n\t\tif (cl->state >= cs_spawned)\r\n\t\t{\r\n\t\t\tClientReliableCheckBlock(cl, sv.multicast.cursize);\r\n\t\t\tClientReliableWrite_SZ(cl, sv.multicast.data, sv.multicast.cursize);\r\n\t\t}\r\n\t\tbreak;\r\n\tcase MSG_ALL:\r\n\t\tSZ_Write(&sv.reliable_datagram, sv.multicast.data, sv.multicast.cursize);\r\n\t\tbreak;\r\n\tcase MSG_MULTICAST:\r\n\t\tSV_Multicast(svhl_messageorigin, MULTICAST_PVS);\r\n\t\tbreak;\r\n\tcase MSG_MULTICAST+1:\r\n\t\tSV_Multicast(svhl_messageorigin, MULTICAST_PHS);\r\n\t\tbreak;\r\n\tcase 9:\r\n\t\t//spectators only\r\n\t\tbreak;\r\n\tdefault:\r\n\t\tCon_Printf(\"GHL_MessageEnd: dest type %i not supported\\n\", svhl_messagedest);\r\n\t\tbreak;\r\n\t}\r\n\r\n\tSZ_Clear (&sv.multicast);\r\n}\r\nvoid QDECL GHL_WriteByte(int value)\r\n{\r\n\tbi_trace();\r\n\tMSG_WriteByte(&sv.multicast, value & 0xff);\r\n}\r\nvoid QDECL GHL_WriteChar(int value)\r\n{\r\n\tbi_trace();\r\n\tMSG_WriteChar(&sv.multicast, value);\r\n}\r\nvoid QDECL GHL_WriteShort(int value)\r\n{\r\n\tbi_trace();\r\n\tMSG_WriteShort(&sv.multicast, value);\r\n}\r\nvoid QDECL GHL_WriteLong(int value)\r\n{\r\n\tbi_trace();\r\n\tMSG_WriteLong(&sv.multicast, value);\r\n}\r\nvoid QDECL GHL_WriteAngle(float value)\r\n{\r\n\tbi_trace();\r\n\tMSG_WriteAngle8(&sv.multicast, value);\r\n}\r\nvoid QDECL GHL_WriteCoord(float value)\r\n{\r\n\tcoorddata i = MSG_ToCoord(value, 2);\r\n\tbi_trace();\r\n\tSZ_Write (&sv.multicast, (void*)&i, 2);\r\n}\r\nvoid QDECL GHL_WriteString(char *string)\r\n{\r\n\tbi_trace();\r\n\tMSG_WriteString(&sv.multicast, string);\r\n}\r\nvoid QDECL GHL_WriteEntity(int entnum)\r\n{\r\n\tbi_trace();\r\n\tMSG_WriteEntity(&sv.multicast, entnum);\r\n}\r\n\r\n\r\nvoid QDECL GHL_AlertMessage(int level, char *fmt, ...)\r\n{\r\n\tva_list\t\targptr;\r\n\tchar\t\tstring[1024];\r\n\r\n\tbi_trace();\r\n\r\n\tif (level == 2 && !developer.ival)\r\n\t\treturn;\r\n\r\n\tva_start (argptr, fmt);\r\n\tvsnprintf (string,sizeof(string)-1, fmt,argptr);\r\n\tva_end (argptr);\r\n\r\n\tCon_Printf(\"%s\\n\", string);\r\n}\r\nvoid QDECL GHL_EngineFprintf(FILE *f, char *fmt, ...)\r\n{\r\n\tbi_trace();\r\n\tSV_Error(\"Halflife gamecode tried to use EngineFprintf\\n\");\r\n}\r\nunk QDECL GHL_SzFromIndex(unk){notimpf(__func__);}\r\nvoid *QDECL GHL_GetModelPtr(hledict_t *ed)\r\n{\r\n\tbi_trace();\r\n#ifdef SERVERONLY\r\n\treturn NULL;\r\n#else\r\n\tif (!ed->v.modelindex)\r\n\t\treturn NULL;\r\n\tif (!sv.models[ed->v.modelindex])\r\n\t\tsv.models[ed->v.modelindex] = Mod_ForName(sv.strings.model_precache[ed->v.modelindex], false);\r\n\treturn Mod_GetHalfLifeModelData(sv.models[ed->v.modelindex]);\r\n#endif\r\n}\r\nint QDECL GHL_RegUserMsg(char *msgname, int msgsize)\r\n{\r\n\tbi_trace();\r\n\t//we use 1 as the code to choose others.\r\n\tif (lastusermessage <= 1)\r\n\t\treturn -1;\r\n\r\n\tSV_FlushSignon ();\r\n\r\n\t//for new clients\r\n\tMSG_WriteByte(&sv.signon, svcfte_cgamepacket);\r\n\tMSG_WriteShort(&sv.signon, strlen(msgname)+3);\r\n\tMSG_WriteByte(&sv.signon, 1);\r\n\tMSG_WriteByte(&sv.signon, lastusermessage);\r\n\tMSG_WriteString(&sv.signon, msgname);\r\n\r\n\t//and if the client is already spawned...\r\n\tMSG_WriteByte(&sv.reliable_datagram, svcfte_cgamepacket);\r\n\tMSG_WriteShort(&sv.reliable_datagram, strlen(msgname)+3);\r\n\tMSG_WriteByte(&sv.reliable_datagram, 1);\r\n\tMSG_WriteByte(&sv.reliable_datagram, lastusermessage);\r\n\tMSG_WriteString(&sv.reliable_datagram, msgname);\r\n\r\n\treturn lastusermessage--;\r\n}\r\nunk QDECL GHL_AnimationAutomove(unk){notimpf(__func__);}\r\n\r\nstatic void GHL_GetFrameState(hledict_t *ent, framestate_t *fstate)\r\n{\r\n\tmemset(fstate, 0, sizeof(*fstate));\r\n\t\t\r\n\tfstate->g[FS_REG].frametime[0] = (SVHL_Globals.time - ent->v.framestarttime) * ent->v.framerate;\r\n\tfstate->g[FS_REG].frame[0] = ent->v.frame;\r\n\tfstate->g[FS_REG].lerpweight[0] = 1;\r\n\tfstate->g[FS_REG].subblendfrac = ent->v.blending[0];\r\n\tfstate->g[FS_REG].subblend2frac = ent->v.blending[1];\r\n\r\n\t//FIXME: no lower parts set here.\r\n\t\r\n\tfstate->bonecontrols[0] = ent->v.controller[0] / 255.0;\r\n\tfstate->bonecontrols[1] = ent->v.controller[1] / 255.0;\r\n\tfstate->bonecontrols[2] = ent->v.controller[2] / 255.0;\r\n\tfstate->bonecontrols[3] = ent->v.controller[3] / 255.0;\r\n}\r\nstatic void bonemat_fromqcvectors(float *out, const float vx[3], const float vy[3], const float vz[3], const float t[3])\r\n{\r\n\tout[0] = vx[0];\r\n\tout[1] = -vy[0];\r\n\tout[2] = vz[0];\r\n\tout[3] = t[0];\r\n\tout[4] = vx[1];\r\n\tout[5] = -vy[1];\r\n\tout[6] = vz[1];\r\n\tout[7] = t[1];\r\n\tout[8] = vx[2];\r\n\tout[9] = -vy[2];\r\n\tout[10] = vz[2];\r\n\tout[11] = t[2];\r\n}\r\nstatic void bonemat_fromidentity(float *out)\r\n{\r\n\tout[0] = 1;\r\n\tout[1] = 0;\r\n\tout[2] = 0;\r\n\tout[3] = 0;\r\n\r\n\tout[4] = 0;\r\n\tout[5] = 1;\r\n\tout[6] = 0;\r\n\tout[7] = 0;\r\n\r\n\tout[8] = 0;\r\n\tout[9] = 0;\r\n\tout[10] = 1;\r\n\tout[11] = 0;\r\n}\r\nstatic void bonemat_toqcvectors(const float *in, float vx[3], float vy[3], float vz[3], float t[3])\r\n{\r\n\tvx[0] = in[0];\r\n\tvx[1] = in[4];\r\n\tvx[2] = in[8];\r\n\tvy[0] = -in[1];\r\n\tvy[1] = -in[5];\r\n\tvy[2] = -in[9];\r\n\tvz[0] = in[2];\r\n\tvz[1] = in[6];\r\n\tvz[2] = in[10];\r\n\tt [0] = in[3];\r\n\tt [1] = in[7];\r\n\tt [2] = in[11];\r\n}\r\nstatic void bonemat_fromhlentity(hledict_t *ed, float *trans)\r\n{\r\n\tvec3_t d[3], a;\r\n\ta[0] = -ed->v.angles[0];\r\n\ta[1] = ed->v.angles[1];\r\n\ta[2] = ed->v.angles[2];\r\n\tAngleVectors(a, d[0], d[1], d[2]);\r\n\tbonemat_fromqcvectors(trans, d[0], d[1], d[2], ed->v.origin);\r\n}\r\nvoid QDECL GHL_GetBonePosition(hledict_t *ed, int bone, vec3_t org, vec3_t ang)\r\n{\r\n\tfloat transent[12];\r\n\tfloat transforms[12];\r\n\tfloat result[12];\r\n\tmodel_t *mod = sv.models[ed->v.modelindex];\r\n\tvec3_t axis[3];\r\n\tframestate_t fstate;\r\n\tGHL_GetFrameState(ed, &fstate);\r\n\r\n\tbone += 1;\t//I *think* the bones are 0-based unlike our tag-based bone numbers\r\n\r\n\tif (!Mod_GetTag(mod, bone, &fstate, transforms))\r\n\t{\r\n\t\tbonemat_fromidentity(transforms);\r\n\t}\r\n\r\n\tbonemat_fromhlentity(ed, transent);\r\n\tR_ConcatTransforms((void*)transent, (void*)transforms, (void*)result);\r\n\r\n\tbonemat_toqcvectors(result, axis[0], axis[1], axis[2], org);\r\n\tVectorAngles(axis[0], axis[2], ang);\r\n}\r\n\r\nhlintptr_t QDECL GHL_FunctionFromName(char *name)\r\n{\r\n\tbi_trace();\r\n\treturn (hlintptr_t)Sys_GetAddressForName(hlgamecode, name);\r\n}\r\nchar *QDECL GHL_NameForFunction(hlintptr_t function)\r\n{\r\n\tbi_trace();\r\n\treturn Sys_GetNameForAddress(hlgamecode, (void*)function);\r\n}\r\n\r\nunk QDECL GHL_ClientPrintf(unk)\r\n{\r\n\tbi_trace();\r\n//\tSV_ClientPrintf(\r\n\tnotimpf(__func__);\r\n}\r\nvoid QDECL GHL_ServerPrint(char *msg)\r\n{\r\n\tbi_trace();\r\n\tCon_Printf(\"%s\", msg);\t\r\n}\r\nchar *QDECL GHL_Cmd_Args(void)\r\n{\r\n\tbi_trace();\r\n\treturn Cmd_Args();\r\n}\r\nchar *QDECL GHL_Cmd_Argv(int arg)\r\n{\r\n\tbi_trace();\r\n\treturn Cmd_Argv(arg);\r\n}\r\nint QDECL GHL_Cmd_Argc(unk)\r\n{\r\n\tbi_trace();\r\n\treturn Cmd_Argc();\r\n}\r\nunk QDECL GHL_GetAttachment(unk){notimpf(__func__);}\r\nvoid QDECL GHL_CRC32_Init(hlcrc_t *crc)\r\n{\r\n\tunsigned short crc16;\r\n\tbi_trace();\r\n\tQCRC_Init(&crc16);\r\n\t*crc = crc16;\r\n}\r\nvoid QDECL GHL_CRC32_ProcessBuffer(hlcrc_t *crc, qbyte *p, int len)\r\n{\r\n\tunsigned short crc16 = *crc;\r\n\tbi_trace();\r\n\twhile(len-->0)\r\n\t{\r\n\t\tQCRC_ProcessByte(&crc16, *p++);\r\n\t}\r\n\t*crc = crc16;\r\n}\r\nvoid QDECL GHL_CRC32_ProcessByte(hlcrc_t *crc, qbyte b)\r\n{\r\n\tunsigned short crc16 = *crc;\r\n\tbi_trace();\r\n\tQCRC_ProcessByte(&crc16, b);\r\n\t*crc = crc16;\r\n}\r\nhlcrc_t QDECL GHL_CRC32_Final(hlcrc_t crc)\r\n{\r\n\tunsigned short crc16 = crc;\r\n\tbi_trace();\r\n\treturn QCRC_Value(crc16);\r\n}\r\nlong QDECL GHL_RandomLong(long minv, long maxv)\r\n{\r\n\tbi_trace();\r\n\treturn minv + frandom()*(maxv-minv);\r\n}\r\nfloat QDECL GHL_RandomFloat(float minv, float maxv)\r\n{\r\n\tbi_trace();\r\n\treturn minv + frandom()*(maxv-minv);\r\n}\r\nunk QDECL GHL_SetView(unk){notimpf(__func__);}\r\nunk QDECL GHL_Time(unk){notimpf(__func__);}\r\nunk QDECL GHL_CrosshairAngle(unk){notimpf(__func__);}\r\nvoid *QDECL GHL_LoadFileForMe(char *name, int *size_out)\r\n{\r\n\tint fsize;\r\n\tvoid *fptr;\r\n\tbi_trace();\r\n\tfsize = FS_LoadFile(name, &fptr);\r\n\tif (size_out)\r\n\t\t*size_out = fsize;\r\n\tif (fsize == -1)\r\n\t\treturn NULL;\r\n\treturn fptr;\r\n}\r\nvoid QDECL GHL_FreeFile(void *fptr)\r\n{\r\n\tbi_trace();\r\n\tFS_FreeFile(fptr);\r\n}\r\nunk QDECL GHL_EndSection(unk){notimpf(__func__);}\r\n#include <sys/stat.h>\r\nint QDECL GHL_CompareFileTime(char *fname1, char *fname2, int *result)\r\n{\r\n\tflocation_t loc1, loc2;\r\n\tstruct stat stat1, stat2;\r\n\tbi_trace();\r\n\r\n\t//results:\r\n\t//1 = f1 is newer\r\n\t//0 = equal age\r\n\t//-1 = f2 is newer\r\n\t//at least I think that's what it means.\r\n\t*result = 0;\r\n\tif (!FS_FLocateFile(fname1, FSLFRT_IFFOUND, &loc1) || !FS_FLocateFile(fname2, FSLFRT_IFFOUND, &loc2))\r\n\t\treturn 0;\r\n\r\n\tif (stat(loc1.rawname, &stat1) || stat(loc2.rawname, &stat2))\r\n\t\treturn 0;\r\n\r\n\tif (stat1.st_mtime > stat2.st_mtime)\r\n\t\t*result = 1;\r\n\telse if (stat1.st_mtime < stat2.st_mtime)\r\n\t\t*result = -1;\r\n\telse\r\n\t\t*result = 0;\r\n\r\n\treturn 1;\r\n}\r\nvoid QDECL GHL_GetGameDir(char *gamedir)\r\n{\r\n\textern char gamedirfile[];\r\n\tbi_trace();\r\n\t//warning: the output buffer size is not specified!\r\n\tQ_strncpyz(gamedir, gamedirfile, MAX_QPATH);\r\n}\r\nunk QDECL GHL_Cvar_RegisterVariable(unk){notimpf(__func__);}\r\nunk QDECL GHL_FadeClientVolume(unk){notimpf(__func__);}\r\nunk QDECL GHL_SetClientMaxspeed(unk)\r\n{\r\n\tbi_trace();\r\n\tnotimpf(__func__);\r\n}\r\nunk QDECL GHL_CreateFakeClient(unk){notimpf(__func__);}\r\nunk QDECL GHL_RunPlayerMove(unk){notimpf(__func__);}\r\nint QDECL GHL_NumberOfEntities(void)\r\n{\r\n\tbi_trace();\r\n\treturn 0;\r\n}\r\nchar *QDECL GHL_GetInfoKeyBuffer(hledict_t *ed)\r\n{\r\n\tbi_trace();\r\n\tif (!ed)\r\n\t\treturn svs.info;\r\n\r\n\treturn svs.clients[ed - SVHL_Edict - 1].userinfo;\r\n}\r\nchar *QDECL GHL_InfoKeyValue(char *infostr, char *key)\r\n{\r\n\tbi_trace();\r\n\treturn Info_ValueForKey(infostr, key);\r\n}\r\nunk QDECL GHL_SetKeyValue(unk){notimpf(__func__);}\r\nunk QDECL GHL_SetClientKeyValue(unk){notimpf(__func__);}\r\nunk QDECL GHL_IsMapValid(unk){notimpf(__func__);}\r\nunk QDECL GHL_StaticDecal(unk){notimpf(__func__);}\r\nunk QDECL GHL_PrecacheGeneric(unk){notimpf(__func__);}\r\nint QDECL GHL_GetPlayerUserId(hledict_t *ed)\r\n{\r\n\tunsigned int clnum = (ed - SVHL_Edict) - 1;\r\n\tbi_trace();\r\n\tif (clnum >= sv.allocated_client_slots)\r\n\t\treturn -1;\r\n\treturn svs.clients[clnum].userid;\r\n}\r\nunk QDECL GHL_BuildSoundMsg(unk){notimpf(__func__);}\r\n\r\nint QDECL GHL_IsDedicatedServer(void)\r\n{\r\n\tbi_trace();\r\n#ifdef SERVERONLY\r\n\treturn 1;\r\n#else\r\n\treturn qrenderer == QR_NONE;\r\n#endif\r\n}\r\n\r\nhlcvar_t *hlcvar_malloced;\r\nhlcvar_t *hlcvar_stored;\r\nvoid SVHL_UpdateCvar(cvar_t *var)\r\n{\r\n\tif (!var->hlcvar)\r\n\t\treturn;\t//nothing to do\r\n\tvar->hlcvar->string = var->string;\r\n\tvar->hlcvar->value = var->value;\r\n}\r\n\r\nvoid SVHL_FreeCvars(void)\r\n{\r\n\tcvar_t *nc;\r\n\thlcvar_t *n;\r\n\r\n\t//forget all\r\n\twhile (hlcvar_malloced)\r\n\t{\r\n\t\tn = hlcvar_malloced;\r\n\t\thlcvar_malloced = n->next;\r\n\r\n\t\tnc = Cvar_FindVar(n->name);\r\n\t\tif (nc)\r\n\t\t\tnc->hlcvar = NULL;\r\n\t\tZ_Free(n);\r\n\t}\r\n\r\n\twhile (hlcvar_stored)\r\n\t{\r\n\t\tn = hlcvar_stored;\r\n\t\thlcvar_stored = n->next;\r\n\r\n\t\tnc = Cvar_FindVar(n->name);\r\n\t\tif (nc)\r\n\t\t\tnc->hlcvar = NULL;\r\n\t}\r\n}\r\nvoid SVHL_FreeCvar(hlcvar_t *var)\r\n{\r\n\tcvar_t *nc;\r\n\thlcvar_t **ref;\r\n\r\n\t//unlink (free if it was malloced)\r\n\tref = &hlcvar_malloced;\r\n\twhile (*ref)\r\n\t{\r\n\t\tif (*ref == var)\r\n\t\t{\r\n\t\t\t(*ref) = (*ref)->next;\r\n\r\n\t\t\tnc = Cvar_FindVar(var->name);\r\n\t\t\tif (nc)\r\n\t\t\t\tnc->hlcvar = NULL;\r\n\t\t\tZ_Free(var);\r\n\t\t\treturn;\r\n\t\t}\r\n\t\tref = &(*ref)->next;\r\n\t}\r\n\r\n\tref = &hlcvar_stored;\r\n\twhile (*ref)\r\n\t{\r\n\t\tif (*ref == var)\r\n\t\t{\r\n\t\t\t(*ref) = (*ref)->next;\r\n\r\n\t\t\tnc = Cvar_FindVar(var->name);\r\n\t\t\tif (nc)\r\n\t\t\t\tnc->hlcvar = NULL;\r\n\t\t\treturn;\r\n\t\t}\r\n\t\tref = &(*ref)->next;\r\n\t}\r\n}\r\n\r\nhlcvar_t *QDECL GHL_CVarGetPointer(char *varname)\r\n{\r\n\tcvar_t *var;\r\n\thlcvar_t *hlvar;\r\n\tbi_trace();\r\n\tvar = Cvar_Get(varname, \"\", 0, \"HalfLife\");\r\n\tif (!var)\r\n\t{\r\n\t\tCon_Printf(\"Not giving cvar \\\"%s\\\" to game\\n\", varname);\r\n\t\treturn NULL;\r\n\t}\r\n\thlvar = var->hlcvar;\r\n\tif (!hlvar)\r\n\t{\r\n\t\thlvar = var->hlcvar = Z_Malloc(sizeof(hlcvar_t));\r\n\t\thlvar->name = var->name;\r\n\t\thlvar->string = var->string;\r\n\t\thlvar->value = var->value;\r\n\r\n\t\thlvar->next = hlcvar_malloced;\r\n\t\thlcvar_malloced = hlvar;\r\n\t}\r\n\treturn hlvar;\r\n}\r\nvoid QDECL GHL_CVarRegister(hlcvar_t *hlvar)\r\n{\r\n\tcvar_t *var;\r\n\tbi_trace();\r\n\tvar = Cvar_Get(hlvar->name, hlvar->string, 0, \"HalfLife\");\r\n\tif (!var)\r\n\t{\r\n\t\tCon_Printf(\"Not giving cvar \\\"%s\\\" to game\\n\", hlvar->name);\r\n\t\treturn;\r\n\t}\r\n\tif (var->hlcvar)\r\n\t{\r\n\t\tSVHL_FreeCvar(var->hlcvar);\r\n\r\n\t\thlvar->next = hlcvar_stored;\r\n\t\thlcvar_stored = hlvar;\r\n\t}\r\n\tvar->hlcvar = hlvar;\r\n}\r\nfloat QDECL GHL_CVarGetFloat(char *vname)\r\n{\r\n\tcvar_t *var = Cvar_FindVar(vname);\r\n\tbi_trace();\r\n\tif (var)\r\n\t\treturn var->value;\r\n\tCon_Printf(\"cvar %s does not exist\\n\", vname);\r\n\treturn 0;\r\n}\r\nchar *QDECL GHL_CVarGetString(char *vname)\r\n{\r\n\tcvar_t *var = Cvar_FindVar(vname);\r\n\tbi_trace();\r\n\tif (var)\r\n\t\treturn var->string;\r\n\tCon_Printf(\"cvar %s does not exist\\n\", vname);\r\n\treturn \"\";\r\n}\r\nvoid QDECL GHL_CVarSetFloat(char *vname, float value)\r\n{\r\n\tcvar_t *var = Cvar_FindVar(vname);\r\n\tbi_trace();\r\n\tif (var)\r\n\t\tCvar_SetValue(var, value);\r\n\telse\r\n\t\tCon_Printf(\"cvar %s does not exist\\n\", vname);\r\n}\r\nvoid QDECL GHL_CVarSetString(char *vname, char *value)\r\n{\r\n\tcvar_t *var = Cvar_FindVar(vname);\r\n\tbi_trace();\r\n\tif (var)\r\n\t\tCvar_Set(var, value);\r\n\telse\r\n\t\tCon_Printf(\"cvar %s does not exist\\n\", vname);\r\n}\r\n\r\nunk QDECL GHL_GetPlayerWONId(unk){notimpf(__func__);}\r\nunk QDECL GHL_Info_RemoveKey(unk){notimpf(__func__);}\r\nunk QDECL GHL_GetPhysicsKeyValue(unk){notimpf(__func__);}\r\nvoid QDECL GHL_SetPhysicsKeyValue(hledict_t *ent, char *key, char *value)\r\n{\r\n\tbi_begin();\r\n\tnotimpf(__func__);\r\n\tbi_end();\r\n}\r\nunk QDECL GHL_GetPhysicsInfoString(unk){notimpf(__func__);}\r\nunsigned short QDECL GHL_PrecacheEvent(int eventtype, char *eventname)\r\n{\r\n\tbi_trace();\r\n\tCon_Printf(\"Fixme: GHL_PrecacheEvent: %s\\n\", eventname);\r\n\treturn 0;\r\n}\r\nvoid QDECL GHL_PlaybackEvent(int flags, hledict_t *ent, unsigned short eventidx, float delay, float *origin, float *angles, float f1, float f2, int i1, int i2, int b1, int b2)\r\n{\r\n\tbi_trace();\r\n\tignore(\"GHL_PlaybackEvent not implemented\");\r\n}\r\nunk QDECL GHL_SetFatPVS(unk){notimpf(__func__);}\r\nunk QDECL GHL_SetFatPAS(unk){notimpf(__func__);}\r\nunk QDECL GHL_CheckVisibility(unk){notimpf(__func__);}\r\nunk QDECL GHL_DeltaSetField(unk){notimpf(__func__);}\r\nunk QDECL GHL_DeltaUnsetField(unk){notimpf(__func__);}\r\nunk QDECL GHL_DeltaAddEncoder(unk){notimpf(__func__);}\r\nunk QDECL GHL_GetCurrentPlayer(unk){notimpf(__func__);}\r\nint QDECL GHL_CanSkipPlayer(hledict_t *playerent)\r\n{\r\n\tbi_trace();\r\n\treturn false;\r\n//\tnotimpf(__func__);\r\n}\r\nunk QDECL GHL_DeltaFindField(unk){notimpf(__func__);}\r\nunk QDECL GHL_DeltaSetFieldByIndex(unk){notimpf(__func__);}\r\nunk QDECL GHL_DeltaUnsetFieldByIndex(unk){notimpf(__func__);}\r\nunk QDECL GHL_SetGroupMask(unk){notimpf(__func__);}\r\nunk QDECL GHL_CreateInstancedBaseline(unk){notimpf(__func__);}\r\nunk QDECL GHL_Cvar_DirectSet(unk){notimpf(__func__);}\r\nunk QDECL GHL_ForceUnmodified(unk){notimpf(__func__);}\r\nunk QDECL GHL_GetPlayerStats(unk){notimpf(__func__);}\r\nunk QDECL GHL_AddServerCommand(unk){notimpf(__func__);}\r\nunk QDECL GHL_Voice_GetClientListening(unk){notimpf(__func__);}\r\nqboolean QDECL GHL_Voice_SetClientListening(int listener, int sender, int shouldlisten)\r\n{\r\n\tbi_trace();\r\n\treturn false;\r\n}\r\nchar *QDECL GHL_GetPlayerAuthId(hledict_t *playered)\r\n{\r\n\tunsigned int clnum = (playered - SVHL_Edict) - 1;\r\n\tbi_trace();\r\n\tif (clnum >= sv.allocated_client_slots)\r\n\t\treturn NULL;\r\n\treturn svs.clients[clnum].guid;\r\n}\r\nunk QDECL GHL_SequenceGet(unk){notimpf(__func__);}\r\nunk QDECL GHL_SequencePickSentence(unk){notimpf(__func__);}\r\nunk QDECL GHL_GetFileSize(unk){notimpf(__func__);}\r\nunk QDECL GHL_GetApproxWavePlayLen(unk){notimpf(__func__);}\r\nunk QDECL GHL_IsCareerMatch(unk){notimpf(__func__);}\r\nunk QDECL GHL_GetLocalizedStringLength(unk){notimpf(__func__);}\r\nunk QDECL GHL_RegisterTutorMessageShown(unk){notimpf(__func__);}\r\nunk QDECL GHL_GetTimesTutorMessageShown(unk){notimpf(__func__);}\r\nunk QDECL GHL_ProcessTutorMessageDecayBuffer(unk){notimpf(__func__);}\r\nunk QDECL GHL_ConstructTutorMessageDecayBuffer(unk){notimpf(__func__);}\r\nunk QDECL GHL_ResetTutorMessageDecayData(unk){notimpf(__func__);}\r\nunk QDECL GHL_QueryClientCvarValue(unk){notimpf(__func__);}\r\nunk QDECL GHL_QueryClientCvarValue2(unk){notimpf(__func__);}\r\n\r\n\r\n\r\n\r\n//====================================================================================================\r\n\r\n\r\n\r\n\r\n\r\nSVHL_Builtins_t SVHL_Builtins =\r\n{\r\n\tGHL_PrecacheModel,\r\n\tGHL_PrecacheSound,\r\n\tGHL_SetModel,\r\n\tGHL_ModelIndex,\r\n\tGHL_ModelFrames,\r\n\tGHL_SetSize,\r\n\tGHL_ChangeLevel,\r\n\tGHL_GetSpawnParms,\r\n\tGHL_SaveSpawnParms,\r\n\tGHL_VecToYaw,\r\n\tGHL_VecToAngles,\r\n\tGHL_MoveToOrigin,\r\n\tGHL_ChangeYaw,\r\n\tGHL_ChangePitch,\r\n\tGHL_FindEntityByString,\r\n\tGHL_GetEntityIllum,\r\n\tGHL_FindEntityInSphere,\r\n\tGHL_FindClientInPVS,\r\n\tGHL_EntitiesInPVS,\r\n\tGHL_MakeVectors,\r\n\tGHL_AngleVectors,\r\n\tGHL_CreateEntity,\r\n\tGHL_RemoveEntity,\r\n\tGHL_CreateNamedEntity,\r\n\tGHL_MakeStatic,\r\n\tGHL_EntIsOnFloor,\r\n\tGHL_DropToFloor,\r\n\tGHL_WalkMove,\r\n\tGHL_SetOrigin,\r\n\tGHL_EmitSound,\r\n\tGHL_EmitAmbientSound,\r\n\tGHL_TraceLine,\r\n\tGHL_TraceToss,\r\n\tGHL_TraceMonsterHull,\r\n\tGHL_TraceHull,\r\n\tGHL_TraceModel,\r\n\tGHL_TraceTexture,\r\n\tGHL_TraceSphere,\r\n\tGHL_GetAimVector,\r\n\tGHL_ServerCommand,\r\n\tGHL_ServerExecute,\r\n\tGHL_ClientCommand,\r\n\tGHL_ParticleEffect,\r\n\tGHL_LightStyle,\r\n\tGHL_DecalIndex,\r\n\tGHL_PointContents,\r\n\tGHL_MessageBegin,\r\n\tGHL_MessageEnd,\r\n\tGHL_WriteByte,\r\n\tGHL_WriteChar,\r\n\tGHL_WriteShort,\r\n\tGHL_WriteLong,\r\n\tGHL_WriteAngle,\r\n\tGHL_WriteCoord,\r\n\tGHL_WriteString,\r\n\tGHL_WriteEntity,\r\n\tGHL_CVarRegister,\r\n\tGHL_CVarGetFloat,\r\n\tGHL_CVarGetString,\r\n\tGHL_CVarSetFloat,\r\n\tGHL_CVarSetString,\r\n\tGHL_AlertMessage,\r\n\tGHL_EngineFprintf,\r\n\tGHL_PvAllocEntPrivateData,\r\n\tGHL_PvEntPrivateData,\r\n\tGHL_FreeEntPrivateData,\r\n\tGHL_SzFromIndex,\r\n\tGHL_AllocString,\r\n\tGHL_GetVarsOfEnt,\r\n\tGHL_PEntityOfEntOffset,\r\n\tGHL_EntOffsetOfPEntity,\r\n\tGHL_IndexOfEdict,\r\n\tGHL_PEntityOfEntIndex,\r\n\tGHL_FindEntityByVars,\r\n\tGHL_GetModelPtr,\r\n\tGHL_RegUserMsg,\r\n\tGHL_AnimationAutomove,\r\n\tGHL_GetBonePosition,\r\n\tGHL_FunctionFromName,\r\n\tGHL_NameForFunction,\r\n\tGHL_ClientPrintf,\r\n\tGHL_ServerPrint,\r\n\tGHL_Cmd_Args,\r\n\tGHL_Cmd_Argv,\r\n\tGHL_Cmd_Argc,\r\n\tGHL_GetAttachment,\r\n\tGHL_CRC32_Init,\r\n\tGHL_CRC32_ProcessBuffer,\r\n\tGHL_CRC32_ProcessByte,\r\n\tGHL_CRC32_Final,\r\n\tGHL_RandomLong,\r\n\tGHL_RandomFloat,\r\n\tGHL_SetView,\r\n\tGHL_Time,\r\n\tGHL_CrosshairAngle,\r\n\tGHL_LoadFileForMe,\r\n\tGHL_FreeFile,\r\n\tGHL_EndSection,\r\n\tGHL_CompareFileTime,\r\n\tGHL_GetGameDir,\r\n\tGHL_Cvar_RegisterVariable,\r\n\tGHL_FadeClientVolume,\r\n\tGHL_SetClientMaxspeed,\r\n\tGHL_CreateFakeClient,\r\n\tGHL_RunPlayerMove,\r\n\tGHL_NumberOfEntities,\r\n\tGHL_GetInfoKeyBuffer,\r\n\tGHL_InfoKeyValue,\r\n\tGHL_SetKeyValue,\r\n\tGHL_SetClientKeyValue,\r\n\tGHL_IsMapValid,\r\n\tGHL_StaticDecal,\r\n\tGHL_PrecacheGeneric,\r\n\tGHL_GetPlayerUserId,\r\n\tGHL_BuildSoundMsg,\r\n\tGHL_IsDedicatedServer,\r\n#if HALFLIFE_API_VERSION > 138\r\n\tGHL_CVarGetPointer,\r\n\tGHL_GetPlayerWONId,\r\n\tGHL_Info_RemoveKey,\r\n\tGHL_GetPhysicsKeyValue,\r\n\tGHL_SetPhysicsKeyValue,\r\n\tGHL_GetPhysicsInfoString,\r\n\tGHL_PrecacheEvent,\r\n\tGHL_PlaybackEvent,\r\n\tGHL_SetFatPVS,\r\n\tGHL_SetFatPAS,\r\n\tGHL_CheckVisibility,\r\n\tGHL_DeltaSetField,\r\n\tGHL_DeltaUnsetField,\r\n\tGHL_DeltaAddEncoder,\r\n\tGHL_GetCurrentPlayer,\r\n\tGHL_CanSkipPlayer,\r\n\tGHL_DeltaFindField,\r\n\tGHL_DeltaSetFieldByIndex,\r\n\tGHL_DeltaUnsetFieldByIndex,\r\n\tGHL_SetGroupMask,\r\n\tGHL_CreateInstancedBaseline,\r\n\tGHL_Cvar_DirectSet,\r\n\tGHL_ForceUnmodified,\r\n\tGHL_GetPlayerStats,\r\n\tGHL_AddServerCommand,\r\n\tGHL_Voice_GetClientListening,\r\n\tGHL_Voice_SetClientListening,\r\n\tGHL_GetPlayerAuthId,\r\n\tGHL_SequenceGet,\r\n\tGHL_SequencePickSentence,\r\n\tGHL_GetFileSize,\r\n\tGHL_GetApproxWavePlayLen,\r\n\tGHL_IsCareerMatch,\r\n\tGHL_GetLocalizedStringLength,\r\n\tGHL_RegisterTutorMessageShown,\r\n\tGHL_GetTimesTutorMessageShown,\r\n\tGHL_ProcessTutorMessageDecayBuffer,\r\n\tGHL_ConstructTutorMessageDecayBuffer,\r\n\tGHL_ResetTutorMessageDecayData,\r\n\tGHL_QueryClientCvarValue,\r\n\tGHL_QueryClientCvarValue2, \r\n#endif\r\n\r\n\t0xdeadbeef\r\n};\r\n\r\nvoid SV_ReadLibListDotGam(void)\r\n{\r\n\tchar key[1024];\r\n\tchar value[1024];\r\n\tchar *file;\r\n\tchar *s;\r\n\tsize_t fsize;\r\n\r\n\tInfo_SetValueForStarKey(svs.info, \"*gamedll\", \"\", sizeof(svs.info));\r\n\tInfo_SetValueForStarKey(svs.info, \"*cldll\", \"\", sizeof(svs.info));\r\n\r\n\tfile = COM_LoadTempFile(\"liblist.gam\", &fsize);\r\n\tif (!file)\r\n\t\treturn;\r\n\r\n\tInfo_SetValueForStarKey(svs.info, \"*cldll\", \"1\", sizeof(svs.info));\r\n\r\n\twhile ((file = COM_ParseOut(file, key, sizeof(key))))\r\n\t{\r\n\t\tfile = COM_ParseOut(file, value, sizeof(value));\r\n\r\n\t\twhile((s = strchr(value, '\\\\')))\r\n\t\t\t*s = '/';\r\n\r\n\t\tif (!strcmp(key, \"gamedll\"\r\n#ifdef __linux__\r\n\t\t\t\"_linux\"\r\n#endif\r\n\t\t\t))\r\n\t\t\tInfo_SetValueForStarKey(svs.info, \"*gamedll\", value, sizeof(svs.info));\r\n\t\tif (!strcmp(key, \"cldll\"))\r\n\t\t\tInfo_SetValueForStarKey(svs.info, \"*cldll\", atoi(value)?\"1\":\"\", sizeof(svs.info));\r\n\t}\r\n}\r\n\r\nint SVHL_InitGame(void)\r\n{\r\n\tchar *gamedll;\r\n\tvoid *iterator;\r\n\tchar path[MAX_OSPATH];\r\n\tchar fullname[MAX_OSPATH];\r\n\tvoid (WINAPI *GiveFnptrsToDll) (funcs, globals);\r\n\tint (QDECL *GetEntityAPI)(SVHL_GameFuncs_t *pFunctionTable, int apivers);\r\n\r\n\tdllfunction_t hlgamefuncs[] =\r\n\t{\r\n\t\t{(void**)&GiveFnptrsToDll, \"GiveFnptrsToDll\"},\r\n\t\t{(void**)&GetEntityAPI, \t\"GetEntityAPI\"},\r\n\t\t{NULL, NULL}\r\n\t};\r\n\r\n\tmemset(&SVHL_Globals, 0, sizeof(SVHL_Globals));\r\n\r\n\tif (sizeof(long) != sizeof(void*))\r\n\t{\r\n\t\tCon_Printf(\"sizeof(long)!=sizeof(ptr): Cannot run halflife gamecode on this platform\\n\");\r\n\t\treturn 0;\r\n\t}\r\n\r\n\tSV_ReadLibListDotGam();\r\n\r\n\tif (hlgamecode)\r\n\t{\r\n\t\tZG_FreeGroup(&hlmapmemgroup);\r\n\r\n\t\tSVHL_Edict = ZG_Malloc(&hlmapmemgroup, sizeof(*SVHL_Edict) * MAX_HL_EDICTS);\r\n\t\tSVHL_Globals.maxentities = MAX_HL_EDICTS;\t//I think this is correct\r\n\t\treturn 1;\r\n\t}\r\n\r\n\tgamedll = Info_ValueForKey(svs.info, \"*gamedll\");\r\n\titerator = NULL;\r\n\t//FIXME: game dlls from game paths are evil/exploitable.\r\n\twhile(COM_IteratePaths(&iterator, path, sizeof(path), NULL, 0))\r\n\t{\r\n\t\tsnprintf (fullname, sizeof(fullname), \"%s%s\", path, gamedll);\r\n\t\thlgamecode = Sys_LoadLibrary(fullname, hlgamefuncs);\r\n\t\tif (hlgamecode)\r\n\t\t\tbreak;\r\n\t}\r\n\r\n\tif (!hlgamecode)\r\n\t\treturn 0;\r\n\r\n\tSVHL_Edict = ZG_Malloc(&hlmapmemgroup, sizeof(*SVHL_Edict) * MAX_HL_EDICTS);\r\n\tSVHL_Globals.maxentities = MAX_HL_EDICTS;\t//I think this is correct\r\n\tGiveFnptrsToDll(&SVHL_Builtins, &SVHL_Globals);\r\n\r\n\tif (!GetEntityAPI(&SVHL_GameFuncs, HALFLIFE_API_VERSION))\r\n\t{\r\n\t\tCon_Printf(CON_ERROR \"Error: %s is incompatible (Engine is compiled for %i)\\n\", fullname, HALFLIFE_API_VERSION);\r\n\t\tif (GetEntityAPI(&SVHL_GameFuncs, 138))\r\n\t\t\tCon_Printf(CON_ERROR \"mod is 138\\n\");\r\n\t\tSys_CloseLibrary(hlgamecode);\r\n\t\thlgamecode = NULL;\r\n\t\treturn 0;\r\n\t}\r\n\r\n\tbi_begin();\r\n\tSVHL_GameFuncs.GameDLLInit();\r\n\tbi_end();\r\n\treturn 1;\r\n}\r\n\r\nvoid SVHL_SaveLevelCache(char *filename)\r\n{\r\n\r\n}\r\n\r\nvoid SVHL_SetupGame(void)\r\n{\r\n\tlastusermessage = 255;\r\n\t//called on new map\r\n}\r\n\r\nvoid SVHL_SpawnEntities(char *entstring)\r\n{\r\n\tchar key[256];\r\n\tchar value[1024];\r\n\tchar classname[1024];\r\n\thlfielddef_t fdef;\r\n\thledict_t *ed, *world;\r\n\textern cvar_t\tcoop;\t//who'd have thought it, eh?\r\n\tchar *ts;\r\n\tint i;\r\n\r\n\t//initialise globals\r\n\tSVHL_Globals.stringbase = \"\";\t//uninitialised strings are considered empty and not crashy. this ensures that is true.\r\n\tSVHL_Globals.maxclients = svs.allocated_client_slots;\r\n\tSVHL_Globals.deathmatch = deathmatch.value;\r\n\tSVHL_Globals.coop = coop.value;\r\n\tSVHL_Globals.serverflags = 0;\r\n\tif (!strncmp(sv.modelname, \"maps/\", 5))\r\n\t\tCOM_StripExtension(sv.modelname+5, value, sizeof(value));\r\n\telse\r\n\t\tCOM_StripExtension(sv.modelname, value, sizeof(value));\r\n\tSVHL_Globals.mapname = GHL_AllocString(value);\r\n\tSVHL_Globals.time = 0;\r\n\r\n\tSVHL_NumActiveEnts = 0;\r\n\r\n\tsv.allocated_client_slots = 0;\r\n\r\n\t//touch world.\r\n\tworld = GHL_CreateNamedEntity(GHL_AllocString(\"worldspawn\"));\r\n\tworld->v.solid = SOLID_BSP;\r\n\tGHL_SetModel(world, sv.modelname);\r\n\r\n\t//spawn player ents\r\n\tfor (i = 0; i < SVHL_Globals.maxclients; i++)\r\n\t{\r\n\t\tsv.allocated_client_slots++;\r\n\t\ted = GHL_CreateNamedEntity(GHL_AllocString(\"player\"));\r\n\t}\r\n\tfor (i = 0; i < sv.allocated_client_slots; i++)\r\n\t{\r\n\t\tSVHL_Edict[i].isfree = true;\r\n\t}\r\n\tsv.allocated_client_slots = i;\r\n\r\n\t//precache the inline models (and touch them).\r\n\tsv.strings.model_precache[0] = \"\";\r\n\tsv.strings.model_precache[1] = sv.modelname;\t//the qvm doesn't have access to this array\r\n\tfor (i=1 ; i<sv.world.worldmodel->numsubmodels ; i++)\r\n\t{\r\n\t\tconst char *s = va(\"*%i\", i);\r\n\t\tchar *n;\r\n\t\tn = ZG_Malloc(&hlmapmemgroup, strlen(s)+1);\r\n\t\tstrcpy(n, s);\r\n\t\tsv.strings.model_precache[1+i] = n;\r\n\t\tsv.models[i+1] = Mod_ForName (Mod_FixName(n, sv.modelname), false);\r\n\t}\r\n\r\n\twhile (entstring)\r\n\t{\r\n\t\tentstring = COM_ParseOut(entstring, key, sizeof(key));\r\n\t\tif (strcmp(key, \"{\"))\r\n\t\t\tbreak;\r\n\r\n\t\t*classname = 0;\r\n\r\n\t\tts = entstring;\r\n\t\twhile (ts)\r\n\t\t{\r\n\t\t\tts = COM_ParseOut(ts, key, sizeof(key));\r\n\t\t\tif (!strcmp(key, \"}\"))\r\n\t\t\t\tbreak;\r\n\t\t\tts = COM_ParseOut(ts, value, sizeof(value));\r\n\r\n\t\t\tif (!strcmp(key, \"classname\"))\r\n\t\t\t{\r\n\t\t\t\tmemcpy(classname, value, strlen(value)+1);\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tif (world)\r\n\t\t{\r\n\t\t\tif (strcmp(classname, \"worldspawn\"))\r\n\t\t\t\tSV_Error(\"first entity is not worldspawn\");\r\n\t\t\ted = world;\r\n\t\t\tworld = NULL;\r\n\t\t}\r\n\t\telse\r\n\t\t\ted = GHL_CreateNamedEntity(GHL_AllocString(classname));\r\n\r\n\t\twhile (entstring)\r\n\t\t{\r\n\t\t\tentstring = COM_ParseOut(entstring, key, sizeof(key));\r\n\t\t\tif (!strcmp(key, \"}\"))\r\n\t\t\t\tbreak;\r\n\t\t\tentstring = COM_ParseOut(entstring, value, sizeof(value));\r\n\r\n\t\t\tif (*key == '_')\r\n\t\t\t\tcontinue;\r\n\r\n\t\t\tif (!strcmp(key, \"classname\"))\r\n\t\t\t\tmemcpy(classname, value, strlen(value)+1);\r\n\r\n\t\t\tfdef.handled = false;\r\n\t\t\tif (!*classname)\r\n\t\t\t\tfdef.classname = NULL;\r\n\t\t\telse\r\n\t\t\t\tfdef.classname = classname;\r\n\t\t\tfdef.key = key;\r\n\t\t\tfdef.value = value;\r\n\t\t\tSVHL_GameFuncs.DispatchKeyValue(ed, &fdef);\r\n\t\t\tif (!fdef.handled)\r\n\t\t\t{\r\n\t\t\t\tif (!strcmp(key, \"angle\"))\r\n\t\t\t\t{\r\n\t\t\t\t\tfloat a = atof(value);\r\n\t\t\t\t\tsprintf(value, \"%f %f %f\", 0.0f, a, 0.0f);\r\n\t\t\t\t\tstrcpy(key, \"angles\");\r\n\t\t\t\t\tSVHL_GameFuncs.DispatchKeyValue(ed, &fdef);\r\n\t\t\t\t}\r\n\t\t\t\tif (!fdef.handled)\r\n\t\t\t\t\tCon_Printf(\"Bad field on %s, %s\\n\", classname, key);\r\n\t\t\t}\r\n\t\t}\r\n\t\tSVHL_GameFuncs.DispatchSpawn(ed);\r\n\t}\r\n\r\n\tbi_begin();\r\n\tSVHL_GameFuncs.ServerActivate(SVHL_Edict, SVHL_NumActiveEnts, sv.allocated_client_slots);\r\n\tbi_end();\r\n}\r\n\r\nvoid SVHL_ShutdownGame(void)\r\n{\r\n\tSVHL_FreeCvars();\r\n\r\n\t//gametype changed, or server shutdown\r\n\tSys_CloseLibrary(hlgamecode);\r\n\thlgamecode = NULL;\r\n\r\n\tSVHL_Edict = NULL;\r\n\tmemset(&SVHL_Globals, 0, sizeof(SVHL_Globals));\r\n\tmemset(&SVHL_GameFuncs, 0, sizeof(SVHL_GameFuncs));\r\n\tmemset(&SVHL_GameFuncsEx, 0, sizeof(SVHL_GameFuncsEx));\r\n\r\n\tZG_FreeGroup(&hlmapmemgroup);\r\n}\r\n\r\nqboolean HLSV_ClientCommand(client_t *client)\r\n{\r\n\thledict_t *ed = &SVHL_Edict[client - svs.clients + 1];\r\n\tif (!hlgamecode)\r\n\t\treturn false;\r\n\tif (!strcmp(\"noclip\", Cmd_Argv(0)))\r\n\t{\r\n\t\tif (ed->v.movetype != MOVETYPE_NOCLIP)\r\n\t\t\ted->v.movetype = MOVETYPE_NOCLIP;\r\n\t\telse\r\n\t\t\ted->v.movetype = MOVETYPE_WALK;\r\n\t\treturn true;\r\n\t}\r\n\tif (!strcmp(\"kill\", Cmd_Argv(0)))\r\n\t{\r\n\t\tSVHL_GameFuncs.ClientKill(ed);\r\n\t\treturn true;\r\n\t}\r\n\tbi_begin();\r\n\tSVHL_GameFuncs.ClientCommand(ed);\r\n\tbi_end();\r\n\treturn true;\r\n}\r\n\r\nqboolean SVHL_ClientConnect(client_t *client, netadr_t adr, char rejectmessage[128])\r\n{\r\n\tqboolean result;\r\n\tchar ipadr[256];\r\n\tNET_AdrToString(ipadr, sizeof(ipadr), &adr);\r\n\tstrcpy(rejectmessage, \"Rejected by gamecode\");\r\n\r\n\tsv.skipbprintclient = client;\r\n\tbi_begin();\r\n\tclient->hledict = &SVHL_Edict[client-svs.clients+1];\r\n\tresult = SVHL_GameFuncs.ClientConnect(client->hledict, client->name, ipadr, rejectmessage);\r\n\tbi_end();\r\n\tsv.skipbprintclient = NULL;\r\n\r\n\treturn result;\r\n}\r\n\r\nvoid SVHL_BuildStats(client_t *client, int *si, float *sf, char **ss)\r\n{\r\n\thledict_t *ed = &SVHL_Edict[client - svs.clients + 1];\r\n\r\n\tsi[STAT_HEALTH] = ed->v.health;\r\n\tsi[STAT_VIEWHEIGHT] = ed->v.view_ofs[2];\r\n\tsi[STAT_WEAPONMODELI] = SV_ModelIndex(SVHL_Globals.stringbase+ed->v.vmodelindex);\r\n\tsi[STAT_ITEMS] = ed->v.weapons;\r\n}\r\n\r\nvoid SVHL_PutClientInServer(client_t *client)\r\n{\r\n\thledict_t *ed = &SVHL_Edict[client - svs.clients + 1];\r\n\ted->isfree = false;\r\n\tbi_begin();\r\n\tSVHL_GameFuncs.ClientPutInServer(&SVHL_Edict[client-svs.clients+1]);\r\n\tbi_end();\r\n}\r\n\r\nvoid SVHL_DropClient(client_t *drop)\r\n{\r\n\thledict_t *ed = &SVHL_Edict[drop - svs.clients + 1];\r\n\tbi_begin();\r\n\tSVHL_GameFuncs.ClientDisconnect(&SVHL_Edict[drop-svs.clients+1]);\r\n\tbi_end();\r\n\tdrop->hledict = NULL;\r\n\ted->isfree = true;\r\n}\r\n\r\nstatic void SVHL_RunCmdR(hledict_t *ed, usercmd_t *ucmd)\r\n{\r\n\tint i;\r\n\thledict_t *other;\r\n\r\n\t// chop up very long commands\r\n\tif (ucmd->msec > 50)\r\n\t{\r\n\t\tusercmd_t cmd = *ucmd;\r\n\r\n\t\tcmd.msec = ucmd->msec/2;\r\n\t\tcmd.msec_compat = floor(cmd.msec);\r\n\t\tSVHL_RunCmdR (ed, &cmd);\r\n\t\tcmd.msec_compat = ucmd->msec - cmd.msec_compat;\r\n\t\tcmd.impulse = 0;\r\n\t\tSVHL_RunCmdR (ed, &cmd);\r\n\t\treturn;\r\n\t}\r\n\r\n\thost_frametime = ucmd->msec * 0.001;\r\n\thost_frametime *= sv.gamespeed;\r\n\tif (host_frametime > 0.1)\r\n\t\thost_frametime = 0.1;\r\n\r\n\tpmove.cmd = *ucmd;\r\n\tswitch(ed->v.movetype)\r\n\t{\r\n\tdefault:\r\n\tcase MOVETYPE_WALK:\r\n\t\tpmove.pm_type = PM_NORMAL;\r\n\t\tbreak;\r\n\tcase MOVETYPE_FLY:\r\n\t\tpmove.pm_type = PM_FLY;\r\n\t\tbreak;\r\n\tcase MOVETYPE_NOCLIP:\r\n\t\tpmove.pm_type = PM_SPECTATOR;\r\n\t\tbreak;\r\n\tcase MOVETYPE_NONE:\r\n\t\tpmove.pm_type = PM_NONE;\r\n\t\tbreak;\r\n\t}\r\n\tpmove.numphysent = 1;\r\n\tpmove.physents[0].model = sv.world.worldmodel;\r\n\tpmove.physents[0].info = 0;\r\n\tpmove.skipent = -1;\r\n\tpmove.onladder = false;\r\n\tpmove.capsule = false;\r\n\r\n\tif (ed->v.flags & (1<<24))\r\n\t{\r\n\t\tpmove.cmd.forwardmove = 0;\r\n\t\tpmove.cmd.sidemove = 0;\r\n\t\tpmove.cmd.upmove = 0;\r\n\t}\r\n\r\n\t{\r\n\t\thledict_t *list[256];\r\n\t\tint count;\r\n\t\tphysent_t *pe;\r\n\t\tvec3_t\tpmove_mins, pmove_maxs;\r\n\r\n\t\tfor (i = 0; i < 3; i++)\r\n\t\t{\r\n\t\t\tpmove_mins[i] = pmove.origin[i] - 256;\r\n\t\t\tpmove_maxs[i] = pmove.origin[i] + 256;\r\n\t\t}\r\n\r\n\t\tcount = SVHL_AreaEdicts(pmove_mins, pmove_maxs, list, sizeof(list)/sizeof(list[0]));\r\n\t\tfor (i = 0; i < count; i++)\r\n\t\t{\r\n\t\t\tother = list[i];\r\n\t\t\tif (other == ed)\r\n\t\t\t\tcontinue;\r\n\t\t\tif (other->v.owner == ed)\r\n\t\t\t\tcontinue;\r\n\t\t\tif (other->v.flags & (1<<23))\t//has monsterclip flag set, so ignore it\r\n\t\t\t\tcontinue;\r\n\r\n\t\t\tpe = &pmove.physents[pmove.numphysent];\r\n\t\t\tswitch(other->v.skin)\r\n\t\t\t{\r\n\t\t\tcase Q1CONTENTS_EMPTY:\r\n\t\t\t\tpe->forcecontentsmask = FTECONTENTS_EMPTY;\r\n\t\t\t\tbreak;\r\n\t\t\tcase Q1CONTENTS_SOLID:\r\n\t\t\t\tpe->forcecontentsmask = FTECONTENTS_SOLID;\r\n\t\t\t\tbreak;\r\n\t\t\tcase Q1CONTENTS_WATER:\r\n\t\t\t\tpe->forcecontentsmask = FTECONTENTS_WATER;\r\n\t\t\t\tbreak;\r\n\t\t\tcase Q1CONTENTS_SLIME:\r\n\t\t\t\tpe->forcecontentsmask = FTECONTENTS_SLIME;\r\n\t\t\t\tbreak;\r\n\t\t\tcase Q1CONTENTS_LAVA:\r\n\t\t\t\tpe->forcecontentsmask = FTECONTENTS_LAVA;\r\n\t\t\t\tbreak;\r\n\t\t\tcase Q1CONTENTS_SKY:\r\n\t\t\t\tpe->forcecontentsmask = FTECONTENTS_SKY;\r\n\t\t\t\tbreak;\r\n\t\t\tcase Q1CONTENTS_LADDER:\r\n\t\t\t\tpe->forcecontentsmask = FTECONTENTS_LADDER;\r\n\t\t\t\tbreak;\r\n\t\t\tdefault:\r\n\t\t\t\tpe->forcecontentsmask = 0;\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\r\n\t\t\tif (other->v.solid == SOLID_NOT || other->v.solid == SOLID_TRIGGER)\r\n\t\t\t{\r\n\t\t\t\tif (!pe->forcecontentsmask)\r\n\t\t\t\t\tcontinue;\r\n\t\t\t\tpe->nonsolid = true;\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t\tpe->nonsolid = false;\r\n\r\n\t\t\tif (other->v.modelindex)\r\n\t\t\t{\r\n\t\t\t\tpe->model = sv.models[other->v.modelindex];\r\n\t\t\t\tif (pe->model && pe->model->type != mod_brush)\r\n\t\t\t\t\tpe->model = NULL;\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t\tpe->model = NULL;\r\n\t\t\tpmove.numphysent++;\r\n\t\t\tpe->info = other - SVHL_Edict;\r\n\t\t\tVectorCopy(other->v.origin, pe->origin);\r\n\t\t\tVectorCopy(other->v.mins, pe->mins);\r\n\t\t\tVectorCopy(other->v.maxs, pe->maxs);\r\n\t\t\tVectorCopy(other->v.angles, pe->angles);\r\n\t\t}\r\n\t}\r\n\r\n\tVectorCopy(ed->v.mins, pmove.player_mins);\r\n\tVectorCopy(ed->v.maxs, pmove.player_maxs);\r\n\r\n\tVectorCopy(ed->v.origin, pmove.origin);\r\n\tVectorCopy(ed->v.velocity, pmove.velocity);\r\n\tif (ed->v.flags & (1<<22))\r\n\t{\r\n\t\tVectorCopy(ed->v.basevelocity, pmove.basevelocity);\r\n\t}\r\n\telse\r\n\t\tVectorClear(pmove.basevelocity);\r\n\r\n\tPM_PlayerMove(sv.gamespeed);\r\n\r\n\tVectorCopy(pmove.origin, ed->v.origin);\r\n\tVectorCopy(pmove.velocity, ed->v.velocity);\r\n\r\n\tif (pmove.onground)\r\n\t{\r\n\t\ted->v.flags |= FL_ONGROUND;\r\n\t\ted->v.groundentity = &SVHL_Edict[pmove.physents[pmove.groundent].info];\r\n\t}\r\n\telse\r\n\t\ted->v.flags &= ~FL_ONGROUND;\r\n\r\n\tfor (i = 0; i < pmove.numtouch; i++)\r\n\t{\r\n\t\tother = &SVHL_Edict[pmove.physents[pmove.touchindex[i]].info];\r\n\t\tSVHL_GameFuncs.DispatchTouch(other, ed);\r\n\t}\r\n\r\n\tSVHL_LinkEdict(ed, true);\r\n}\r\n\r\nvoid SVHL_RunCmd(client_t *cl, usercmd_t *ucmd)\r\n{\r\n\thledict_t *ed = &SVHL_Edict[cl - svs.clients + 1];\r\n\r\n#if HALFLIFE_API_VERSION >= 140\r\n\ted->v.buttons = ucmd->buttons;\r\n#else\r\n\t//assume they're not running halflife cgame\r\n\ted->v.buttons = 0;\r\n\r\n\tif (ucmd->buttons & 1)\r\n\t\ted->v.buttons |= (1<<0);\t//shoot\r\n\tif (ucmd->buttons & 2)\r\n\t\ted->v.buttons |= (1<<1);\t//jump\r\n\tif (ucmd->buttons & 8)\r\n\t\ted->v.buttons |= (1<<2);\t//duck\r\n\tif (ucmd->forwardmove > 0)\r\n\t\ted->v.buttons |= (1<<3);\t//forward\r\n\tif (ucmd->forwardmove < 0)\r\n\t\ted->v.buttons |= (1<<4);\t//back\r\n\tif (ucmd->buttons & 4)\r\n\t\ted->v.buttons |= (1<<5);\t//use\r\n\t//ed->v.buttons |= (1<<6);\t//cancel\r\n\t//ed->v.buttons |= (1<<7);\t//turn left\r\n\t//ed->v.buttons |= (1<<8);\t//turn right\r\n\tif (ucmd->sidemove > 0)\r\n\t\ted->v.buttons |= (1<<9);\t//move left\r\n\tif (ucmd->sidemove < 0)\r\n\t\ted->v.buttons |= (1<<10);\t//move right\r\n\t//ed->v.buttons |= (1<<11);\t//shoot2\r\n\t//ed->v.buttons |= (1<<12);\t//run\r\n\tif (ucmd->buttons & 16)\r\n\t\ted->v.buttons |= (1<<13);\t//reload\r\n\t//ed->v.buttons |= (1<<14);\t//alt1\r\n\t//ed->v.buttons |= (1<<15);\t//alt2\r\n#endif\r\n\r\n\tif (ucmd->impulse)\r\n\t\ted->v.impulse = ucmd->impulse;\r\n\ted->v.v_angle[0] = SHORT2ANGLE(ucmd->angles[0]);\r\n\ted->v.v_angle[1] = SHORT2ANGLE(ucmd->angles[1]);\r\n\ted->v.v_angle[2] = SHORT2ANGLE(ucmd->angles[2]);\r\n\r\n\ted->v.angles[0] = 0;\r\n\ted->v.angles[1] = SHORT2ANGLE(ucmd->angles[1]);\r\n\ted->v.angles[2] = SHORT2ANGLE(ucmd->angles[2]);\r\n\r\n\tif (IS_NAN(ed->v.velocity[0]) || IS_NAN(ed->v.velocity[1]) || IS_NAN(ed->v.velocity[2]))\r\n\t\tVectorClear(ed->v.velocity);\r\n\r\n\tbi_begin();\r\n\tSVHL_GameFuncs.PlayerPreThink(ed);\r\n\tSVHL_RunCmdR(ed, ucmd);\r\n\r\n\tif (ed->v.nextthink && ed->v.nextthink < sv.time)\r\n\t{\r\n\t\ted->v.nextthink = 0;\r\n\t\tSVHL_GameFuncs.DispatchThink(ed);\r\n\t}\r\n\r\n\tSVHL_GameFuncs.PlayerPostThink(ed);\r\n\r\n\tbi_end();\r\n}\r\n\r\n\r\nvoid SVHL_RunPlayerCommand(client_t *cl, usercmd_t *oldest, usercmd_t *oldcmd, usercmd_t *newcmd)\r\n{\r\n\thledict_t *e = &SVHL_Edict[cl - svs.clients + 1];\r\n\r\n\tSVHL_Globals.time = sv.time;\r\n\tif (net_drop < 20)\r\n\t{\r\n\t\twhile (net_drop > 2)\r\n\t\t{\r\n\t\t\tSVHL_RunCmd (cl, &cl->lastcmd);\r\n\t\t\tnet_drop--;\r\n\t\t}\r\n\t\tif (net_drop > 1)\r\n\t\t\tSVHL_RunCmd (cl, oldest);\r\n\t\tif (net_drop > 0)\r\n\t\t\tSVHL_RunCmd (cl, oldcmd);\r\n\t}\r\n\tSVHL_RunCmd (cl, newcmd);\r\n}\r\n\r\nvoid SVHL_Snapshot_Build(client_t *client, packet_entities_t *pack, qbyte *pvs, edict_t *clent, qboolean ignorepvs)\r\n{\r\n\thledict_t *e;\r\n\tentity_state_t *s;\r\n\tint i;\r\n\r\n\tpack->servertime = sv.time;\r\n\tpack->num_entities = 0;\r\n\r\n\tfor (i = 1; i < MAX_HL_EDICTS; i++)\r\n\t{\r\n\t\te = &SVHL_Edict[i];\r\n\t\tif (!e)\r\n\t\t\tbreak;\r\n\t\tif (e->isfree)\r\n\t\t\tcontinue;\r\n\r\n\t\tif (!e->v.modelindex || !e->v.model)\r\n\t\t\tcontinue;\r\n\t\tif (e->v.effects & 128)\r\n\t\t\tcontinue;\r\n\r\n\t\tif (pack->num_entities == pack->max_entities)\r\n\t\t\tbreak;\r\n\r\n\t\ts = &pack->entities[pack->num_entities++];\r\n\r\n\t\ts->number = i;\r\n\t\ts->modelindex = e->v.modelindex;\r\n\t\ts->frame = e->v.sequence1;\r\n\t\ts->effects = e->v.effects & 0x0f;\r\n\t\ts->dpflags = 0;\r\n\t\ts->skinnum = e->v.skin;\r\n\t\ts->scale = 16;\r\n\t\ts->trans = 255;\r\n\t\ts->colormod[0] = nullentitystate.colormod[0];\r\n\t\ts->colormod[1] = nullentitystate.colormod[1];\r\n\t\ts->colormod[2] = nullentitystate.colormod[2];\r\n\t\tVectorCopy(e->v.angles, s->angles);\r\n\t\tVectorCopy(e->v.origin, s->origin);\r\n\r\n\t\tif (!e->v.velocity[0] && !e->v.velocity[1] && !e->v.velocity[2])\r\n\t\t\ts->dpflags |= RENDER_STEP;\r\n\r\n\t\ts->trans = e->v.renderamt*255;\r\n\t\tswitch (e->v.rendermode)\r\n\t\t{\r\n\t\tcase 0:\r\n\t\t\ts->trans = 255;\r\n\t\t\tbreak;\r\n\t\tcase 1:\t//used on laser beams, apparently. disables textures or something.\r\n\t\t\tbreak;\r\n\t\tcase 2:\t//transparent windows.\r\n\t\t\tbreak;\r\n\t\tcase 3:\t//used on coronarey sprites. both additive and resizing to give constant distance\r\n\t\t\ts->effects |= NQEF_ADDITIVE;\r\n\t\t\tbreak;\r\n\t\tcase 4:\t//used on fence textures, apparently. we already deal with these clientside.\r\n\t\t\ts->trans = 255;\r\n\t\t\tbreak;\r\n\t\tcase 5:\t//used on the torch at the start.\r\n\t\t\ts->effects |= NQEF_ADDITIVE;\r\n\t\t\tbreak;\r\n\t\tdefault:\r\n\t\t\tCon_Printf(\"Rendermode %s %i\\n\", SVHL_Globals.stringbase+e->v.model, e->v.rendermode);\r\n\t\t\tbreak;\r\n\t\t}\r\n\t}\r\n}\r\n\r\nqbyte *SVHL_Snapshot_SetupPVS(client_t *client, qbyte *pvs, unsigned int pvsbufsize)\r\n{\r\n\tvec3_t org;\r\n\r\n\tif (client->hledict)\r\n\t{\r\n\t\tVectorAdd (client->hledict->v.origin, client->hledict->v.view_ofs, org);\r\n\t\tsv.world.worldmodel->funcs.FatPVS(sv.world.worldmodel, org, pvs, pvsbufsize, false);\r\n\t}\r\n\r\n\treturn pvs;\r\n}\r\n\r\n#endif\r\n"
  },
  {
    "path": "engine/server/svhl_gcapi.h",
    "content": "/*supported halflife API versions:\r\n138\r\n140\r\n*/\r\n#if HLSERVER >= 1\r\n\t#define HALFLIFE_API_VERSION HLSERVER\r\n#else\r\n\t#define HALFLIFE_API_VERSION 140\r\n#endif\r\n\r\n#ifndef QDECL\r\n\t#ifdef _MSC_VER\r\n\t\t#define QDECL _cdecl\r\n\t#else\r\n\t\t#define QDECL\r\n\t#endif\r\n#endif\r\n\r\ntypedef long hllong; //long is 64bit on amd64+linux, not sure that's what valve meant, but lets keep it for compatibility.\r\ntypedef struct hledict_s hledict_t;\r\ntypedef unsigned long hlintptr_t;\t//this may be problematic. CRestore::ReadField needs a fix. Or 20.\r\ntypedef unsigned long hlcrc_t;\t//supposed to be 32bit... *sigh*\r\n\r\ntypedef struct\r\n{\r\n\tint allsolid;\r\n\tint startsolid;\r\n\tint inopen;\r\n\tint inwater;\r\n\tfloat fraction;\r\n\tvec3_t endpos;\r\n\tfloat planedist;\r\n\tvec3_t planenormal;\r\n\thledict_t *touched;\r\n\tint hitgroup;\r\n} hltraceresult_t;\r\n\r\n#if HALFLIFE_API_VERSION <= 138\r\ntypedef struct\r\n{\r\n\tint etype;\r\n\tint number;\r\n\tint flags;\r\n\tvec3_t origin;\r\n\tvec3_t angles;\r\n\tint modelindex;\r\n\tint sequence1;\r\n\tfloat frame;\r\n\tint colourmap;\r\n\tshort skin;\r\n\tshort solid;\r\n\tint effects;\r\n\tfloat scale;\r\n\tint rendermode;\r\n\tint renderamt;\r\n\tint rendercolour;\r\n\tint renderfx;\r\n\tint movetype;\r\n\tfloat frametime;\r\n\tfloat framerate;\r\n\tint body;\r\n\tqbyte controller[4];\r\n\tqbyte blending[2];\r\n\tshort padding;\r\n\tvec3_t velocity;\r\n\tvec3_t mins;\r\n\tvec3_t maxs;\r\n\tint aiment;\r\n} hlbaseline_t;\r\n#endif\r\n\r\ntypedef struct\r\n{\r\n\tstring_t\tclassname;\r\n\tstring_t\tglobalname;\r\n\r\n\tvec3_t\torigin;\r\n\tvec3_t\toldorigin;\r\n\tvec3_t\tvelocity;\r\nvec3_t\tbasevelocity;\r\nvec3_t\tclbasevelocity;\r\n\r\n\tvec3_t\tmovedir;\r\n\r\n\tvec3_t\tangles;\r\n\tvec3_t\tavelocity;\r\n\tvec3_t\tpunchangles;\r\n\tvec3_t\tv_angle;\r\n\r\n#if HALFLIFE_API_VERSION > 138\r\nvec3_t\tendpos;\r\nvec3_t\tstartpos;\r\nfloat\timpacttime;\r\nfloat\tstarttime;\r\n#endif\r\n\r\n\tint\t\tfixangle;\r\n\r\n\tfloat\tideal_pitch;\r\n\tfloat\tpitch_speed;\r\n\tfloat\tideal_yaw;\r\n\tfloat\tyaw_speed;\r\n\r\n\r\n\tint\t\tmodelindex;\r\n\tstring_t\tmodel;\r\nint\t\tvmodelindex;\r\nint\t\tvwmodelindex;\r\n\r\n\tvec3_t\tabsmin;\r\n\tvec3_t\tabsmax;\r\n\tvec3_t\tmins;\r\n\tvec3_t\tmaxs;\r\n\tvec3_t\tsize;\r\n\r\n\tfloat\tltime;\r\n\tfloat\tnextthink;\r\n\tint\t\tmovetype;\r\n\tint\t\tsolid;\r\n\r\n\tint\tskin;\r\nint\t\tbody;\r\n\tint\t\teffects;\r\n\r\nfloat\tgravity;\r\nfloat\tfriction;\r\n\r\nint\t\tlight_level;\r\n\r\nint\t\tsequence1;\r\nint\t\tsequence2;\r\n\tfloat\tframe;\r\nfloat\tframestarttime;\r\nfloat\tframerate;\r\nqbyte\tcontroller[4];\r\nqbyte\tblending[2];\r\nfloat\tscale;\r\n\r\nint\t\trendermode;\r\nfloat\trenderamt;\r\nvec3_t\trendercolour;\r\nint\t\trenderfx;\r\n\r\n\tfloat\thealth;\r\n\tfloat\tfrags;\r\nint weapons;\r\n\tfloat\ttakedamage;\r\n\tint\t\tdeadflag;\r\n\tvec3_t\tview_ofs;\r\n\r\nint buttons;\r\n\tint impulse;\r\n\r\n\thledict_t *chain;\r\n\thledict_t *dmg_inflictor;\r\n\thledict_t *enemy;\r\n\thledict_t *aiment;\r\n\thledict_t *owner;\r\n\thledict_t *groundentity;\r\n\r\n\r\n\tint spawnflags;\r\n\tint flags;\r\n\r\n\tint\tcolormap;\r\n\tint\tteam;\r\n\r\n\tfloat\tmax_health;\r\n\tfloat\tteleport_time;\r\n\tfloat\tarmortype;\r\n\tfloat\tarmorvalue;\r\n\tint\twaterlevel;\r\n\tint\twatertype;\r\n\r\n\tstring_t\ttarget;\r\n\tstring_t\ttargetname;\r\n\tstring_t\tnetname;\r\n\tstring_t\tmessage;\t//WARNING: hexen2 uses a float and not a string\r\n\r\n\tfloat\tdmg_take;\r\n\tfloat\tdmg_save;\r\n\tfloat\tdmg;\r\n\tfloat\tdmg_time;\r\n\r\n\tstring_t\tnoise;\r\n\tstring_t\tnoise1;\r\n\tstring_t\tnoise2;\r\n\tstring_t\tnoise3;\r\n\r\nfloat speed;\r\nfloat air_finished;\r\nfloat pain_finished;\r\nfloat radsuit_finished;\r\n\r\nhledict_t *edict;\r\n#if HALFLIFE_API_VERSION > 138\r\nint\t\tplayerclass;\r\nfloat\tmaxspeed;\r\nfloat\tfov;\r\nint\t\tweaponanim;\r\nint\t\tmsec;\r\nint\t\tducking;\r\nint\t\ttimestepsound;\r\nint\t\tswimtime;\r\nint\t\tducktime;\r\nint\t\tstepleft;\r\nfloat\tfallvelocity;\r\nint\t\tgamestate;\r\nint\t\toldbuttons;\r\nint\t\tgroupinfo;\r\n\r\nint\t\tcustomi0;\r\nint\t\tcustomi1;\r\nint\t\tcustomi2;\r\nint\t\tcustomi3;\r\nfloat\t\tcustomf0;\r\nfloat\t\tcustomf1;\r\nfloat\t\tcustomf2;\r\nfloat\t\tcustomf3;\r\nvec3_t\t\tcustomv0;\r\nvec3_t\t\tcustomv1;\r\nvec3_t\t\tcustomv2;\r\nvec3_t\t\tcustomv3;\r\nhledict_t *custome0;\r\nhledict_t *custome1;\r\nhledict_t *custome2;\r\nhledict_t *custome3;\r\n#endif\r\n} hlentvars_t;\r\n\r\nstruct hledict_s\r\n{\r\n\tqboolean\tisfree;\r\n\tint\t\t\tspawnnumber;\r\n\tlink_t\t\tarea;\t\t\t\t// linked to a division node or leaf\r\n\r\n#if HALFLIFE_API_VERSION > 138\r\n\tint\t\t\theadnode;\r\n\tint\t\t\tnum_leafs;\r\n#define HLMAX_ENT_LEAFS 48\r\n\tshort\t\tleafnums[HLMAX_ENT_LEAFS];\r\n#else\r\n\tint\t\t\tnum_leafs;\r\n#define HLMAX_ENT_LEAFS 24\r\n\tshort\t\tleafnums[HLMAX_ENT_LEAFS];\r\n\r\n\thlbaseline_t\tbaseline;\r\n#endif\r\n\r\n\tfloat\t\tfreetime; // sv.time when the object was freed\r\n\r\n\tvoid\t\t*moddata;\r\n\thlentvars_t\tv;\r\n};\r\n\r\n#define unk void\r\n/*\r\n#define\tFCVAR_ARCHIVE\t\t1\t// set to cause it to be saved to vars.rc\r\n#define\tFCVAR_USERINFO\t\t2\t// changes the client's info string\r\n#define\tFCVAR_SERVERINFO\t4\t// notifies players when changed\r\n#define FCVAR_SERVERDLL\t\t8\t// defined by external DLL\r\n#define FCVAR_CLIENTDLL     16  // defined by the client dll\r\n#define HLCVAR_PROTECTED     32  // It's a server cvar, but we don't send the data since it's a password, etc.  Sends 1 if it's not bland/zero, 0 otherwise as value\r\n#define HLCVAR_SPONLY        64  // This cvar cannot be changed by clients connected to a multiplayer server.\r\n*/\r\ntypedef struct hlcvar_s\r\n{\r\n\tchar\t*name;\r\n\tchar\t*string;\r\n\tint\t\tflags;\r\n\tfloat\tvalue;\r\n\tstruct hlcvar_s *next;\r\n} hlcvar_t;\r\n\r\ntypedef struct\r\n{\r\n\tchar *classname;\r\n\tchar *key;\r\n\tchar *value;\r\n\thllong handled;\r\n} hlfielddef_t;\r\n\r\n\r\n\r\ntypedef struct\r\n{\r\n//\tint\tself;\r\n//\tint\tother;\r\n//\tint\tworld;\r\n\tfloat\ttime;\r\n\tfloat\tframetime;\t\r\n\tfloat\tforce_retouch;\r\n\tstring_t\tmapname;\r\nstring_t startspot;\r\n\tfloat\tdeathmatch;\r\n\tfloat\tcoop;\r\n\tfloat\tteamplay;\r\n\tfloat\tserverflags;\r\n//\tfloat\ttotal_secrets;\r\n//\tfloat\ttotal_monsters;\r\n\tfloat\tfound_secrets;\r\n//\tfloat\tkilled_monsters;\r\n//\tfloat parms1, parm2, parm3, parm4, parm;\r\n\tvec3_t\tv_forward;\r\n\tvec3_t\tv_up;\r\n\tvec3_t\tv_right;\r\n\tfloat\ttrace_allsolid;\r\n\tfloat\ttrace_startsolid;\r\n\tfloat\ttrace_fraction;\r\n\tvec3_t\ttrace_endpos;\r\n\tvec3_t\ttrace_plane_normal;\r\n\tfloat\ttrace_plane_dist;\r\n\tint\ttrace_ent;\r\n\tfloat\ttrace_inopen;\r\n\tfloat\ttrace_inwater;\r\nint trace_hitgroup;\r\nint trace_flags;\r\n\tint\tmsg_entity;\r\nint audiotrack;\r\nint maxclients;\r\nint maxentities;\r\n\r\nchar *stringbase;\r\nvoid *savedata;\r\nvec3_t landmark;\r\n//43...\r\n} SVHL_Globals_t;\r\n\r\n\r\n\r\n\r\n//http://metamod.org/dllapi_notes.html\r\ntypedef struct\r\n{\r\n\tvoid (QDECL *GameDLLInit)(void);\r\n    int (QDECL *DispatchSpawn)(hledict_t *ed);\r\n    void (QDECL *DispatchThink)(hledict_t *ed);\r\n    unk (QDECL *DispatchUse)(unk);\r\n    void (QDECL *DispatchTouch)(hledict_t *e1, hledict_t *e2);\r\n    void (QDECL *DispatchBlocked)(hledict_t *self, hledict_t *other);\r\n    void (QDECL *DispatchKeyValue)(hledict_t *ed, hlfielddef_t *fdef);\r\n    unk (QDECL *DispatchSave)(unk);\r\n    unk (QDECL *DispatchRestore)(unk);\r\n    unk (QDECL *DispatchObjectCollsionBox)(unk);\r\n    unk (QDECL *SaveWriteFields)(unk);\r\n    unk (QDECL *SaveReadFields)(unk);\r\n    unk (QDECL *SaveGlobalState)(unk);\r\n    unk (QDECL *RestoreGlobalState)(unk);\r\n    unk (QDECL *ResetGlobalState)(unk);\r\n    qboolean (QDECL *ClientConnect)(hledict_t *ed, char *name, char *ip, char reject[128]);\r\n    void (QDECL *ClientDisconnect)(hledict_t *ed);\r\n    void (QDECL *ClientKill)(hledict_t *ed);\r\n    void (QDECL *ClientPutInServer)(hledict_t *ed);\r\n    void (QDECL *ClientCommand)(hledict_t *ed);\r\n    unk (QDECL *ClientUserInfoChanged)(unk);\r\n    void (QDECL *ServerActivate)(hledict_t *edictlist, int numedicts, int numplayers);\r\n#if HALFLIFE_API_VERSION > 138\r\n    unk (QDECL *ServerDeactivate)(unk);\r\n#endif\r\n    void (QDECL *PlayerPreThink)(hledict_t *ed);\r\n    void (QDECL *PlayerPostThink)(hledict_t *ed);\r\n    unk (QDECL *StartFrame)(unk);\r\n    unk (QDECL *ParmsNewLevel)(unk);\r\n    unk (QDECL *ParmsChangeLevel)(unk);\r\n    unk (QDECL *GetGameDescription)(unk);\r\n    unk (QDECL *PlayerCustomization)(unk);\r\n    unk (QDECL *SpectatorConnect)(unk);\r\n    unk (QDECL *SpectatorDisconnect)(unk);\r\n    unk (QDECL *SpectatorThink)(unk);\r\n\t//138\r\n#if HALFLIFE_API_VERSION > 138\r\n    unk (QDECL *Sys_Error)(unk);\r\n    unk (QDECL *PM_Move)(unk);\r\n    unk (QDECL *PM_Init)(unk);\r\n    unk (QDECL *PM_FindTextureType)(unk);\r\n    unk (QDECL *SetupVisibility)(unk);\r\n    unk (QDECL *UpdateClientData)(unk);\r\n    unk (QDECL *AddToFullPack)(unk);\r\n    unk (QDECL *CreateBaseline)(unk);\r\n    unk (QDECL *RegisterEncoders)(unk);\r\n    unk (QDECL *GetWeaponData)(unk);\r\n    unk (QDECL *CmdStart)(unk);\r\n    unk (QDECL *CmdEnd)(unk);\r\n    unk (QDECL *ConnectionlessPacket)(unk);\r\n    unk (QDECL *GetHullBounds)(unk);\r\n    unk (QDECL *CreateInstancedBaselines)(unk);\r\n    unk (QDECL *InconsistentFile)(unk);\r\n    unk (QDECL *AllowLagCompensation)(unk);\r\n#endif\r\n} SVHL_GameFuncs_t;\r\n\r\n//http://metamod.org/newapi_notes.html\r\nstruct \r\n{\r\n\tunk (QDECL *OnFreeEntPrivateData)(unk);\r\n    unk (QDECL *GameShutdown)(unk);\r\n    unk (QDECL *ShouldCollide)(unk);\r\n    unk (QDECL *CvarValue)(unk);\r\n    unk (QDECL *CvarValue2 )(unk);\r\n} *SVHL_GameFuncsEx;\r\n\r\n// http://metamod.org/engine_notes.html\r\ntypedef struct\r\n{\r\n\tint (QDECL *PrecacheModel)(char *name);\r\n\tint (QDECL *PrecacheSound)(char *name);\r\n\tvoid (QDECL *SetModel)(hledict_t *ed, char *modelname);\r\n\tunk (QDECL *ModelIndex)(unk);\r\n\tint (QDECL *ModelFrames)(int midx);\r\n\tvoid (QDECL *SetSize)(hledict_t *ed, float *mins, float *maxs);\r\n\tvoid (QDECL *ChangeLevel)(char *nextmap, char *startspot);\r\n\tunk (QDECL *GetSpawnParms)(unk);\r\n\tunk (QDECL *SaveSpawnParms)(unk);\r\n\tfloat (QDECL *VecToYaw)(float *inv);\r\n\tvoid (QDECL *VecToAngles)(float *inv, float *outa);\r\n\tvoid (QDECL *MoveToOrigin)(hledict_t *ent, vec3_t dest, float dist, int moveflags);\r\n\tunk (QDECL *ChangeYaw)(unk);\r\n\tunk (QDECL *ChangePitch)(unk);\r\n\thledict_t *(QDECL *FindEntityByString)(hledict_t *last, char *field, char *value);\r\n\tint (QDECL *GetEntityIllum)(hledict_t *ent);\r\n\thledict_t *(QDECL *FindEntityInSphere)(hledict_t *last, float *org, float radius);\r\n\thledict_t *(QDECL *FindClientInPVS)(hledict_t *ed);\r\n\tunk (QDECL *EntitiesInPVS)(unk);\r\n\tvoid (QDECL *MakeVectors)(float *angles);\r\n\tvoid (QDECL *AngleVectors)(float *angles, float *forward, float *right, float *up);\r\n\thledict_t *(QDECL *CreateEntity)(void);\r\n\tvoid (QDECL *RemoveEntity)(hledict_t *ed);\r\n\thledict_t *(QDECL *CreateNamedEntity)(string_t name);\r\n\tunk (QDECL *MakeStatic)(unk);\r\n\tunk (QDECL *EntIsOnFloor)(unk);\r\n\tint (QDECL *DropToFloor)(hledict_t *ed);\r\n\tint (QDECL *WalkMove)(hledict_t *ed, float yaw, float dist, int mode);\r\n\tvoid (QDECL *SetOrigin)(hledict_t *ed, float *neworg);\r\n\tvoid (QDECL *EmitSound)(hledict_t *ed, int chan, char *soundname, float vol, float atten, int flags, int pitch);\r\n\tvoid (QDECL *EmitAmbientSound)(hledict_t *ed, float *org, char *samp, float vol, float attenuation, unsigned int flags, int pitch);\r\n\tvoid (QDECL *TraceLine)(float *start, float *end, int flags, hledict_t *ignore, hltraceresult_t *result);\r\n\tunk (QDECL *TraceToss)(unk);\r\n\tunk (QDECL *TraceMonsterHull)(unk);\r\n\tvoid (QDECL *TraceHull)(float *start, float *end, int flags, int hullnum, hledict_t *ignore, hltraceresult_t *result);\r\n\tunk (QDECL *TraceModel)(unk);\r\n\tchar *(QDECL *TraceTexture)(hledict_t *againstent, vec3_t start, vec3_t end);\r\n\tunk (QDECL *TraceSphere)(unk);\r\n\tunk (QDECL *GetAimVector)(unk);\r\n\tvoid (QDECL *ServerCommand)(char *cmd);\r\n\tvoid (QDECL *ServerExecute)(void);\r\n\tunk (QDECL *ClientCommand)(unk);\r\n\tunk (QDECL *ParticleEffect)(unk);\r\n\tvoid (QDECL *LightStyle)(int stylenum, char *stylestr);\r\n\tint (QDECL *DecalIndex)(char *decalname);\r\n\tint (QDECL *PointContents)(float *org);\r\n\tvoid (QDECL *MessageBegin)(int dest, int svc, float *org, hledict_t *ent);\r\n\tvoid (QDECL *MessageEnd)(void);\r\n\tvoid (QDECL *WriteByte)(int value);\r\n\tvoid (QDECL *WriteChar)(int value);\r\n\tvoid (QDECL *WriteShort)(int value);\r\n\tvoid (QDECL *WriteLong)(int value);\r\n\tvoid (QDECL *WriteAngle)(float value);\r\n\tvoid (QDECL *WriteCoord)(float value);\r\n\tvoid (QDECL *WriteString)(char *string);\r\n\tvoid (QDECL *WriteEntity)(int entnum);\r\n\tvoid (QDECL *CVarRegister)(hlcvar_t *hlvar);\r\n\tfloat (QDECL *CVarGetFloat)(char *vname);\r\n\tchar *(QDECL *CVarGetString)(char *vname);\r\n\tvoid (QDECL *CVarSetFloat)(char *vname, float v);\r\n\tvoid (QDECL *CVarSetString)(char *vname, char *v);\r\n\tvoid (QDECL *AlertMessage)(int level, char *fmt, ...);\r\n\tvoid (QDECL *EngineFprintf)(FILE *f, char *fmt, ...);\r\n\tvoid *(QDECL *PvAllocEntPrivateData)(hledict_t *ed, long quant);\r\n\tunk (QDECL *PvEntPrivateData)(unk);\r\n\tunk (QDECL *FreeEntPrivateData)(unk);\r\n\tunk (QDECL *SzFromIndex)(unk);\r\n\tstring_t (QDECL *AllocString)(char *string);\r\n\tunk (QDECL *GetVarsOfEnt)(unk);\r\n\thledict_t * (QDECL *PEntityOfEntOffset)(int ednum);\r\n\tint (QDECL *EntOffsetOfPEntity)(hledict_t *ed);\r\n\tint (QDECL *IndexOfEdict)(hledict_t *ed);\r\n\thledict_t *(QDECL *PEntityOfEntIndex)(int idx);\r\n\tunk (QDECL *FindEntityByVars)(unk);\r\n\tvoid *(QDECL *GetModelPtr)(hledict_t *ed);\r\n\tint (QDECL *RegUserMsg)(char *msgname, int msgsize);\r\n\tunk (QDECL *AnimationAutomove)(unk);\r\n\tvoid (QDECL *GetBonePosition)(hledict_t *ed, int bone, vec3_t org, vec3_t ang);\r\n\thlintptr_t (QDECL *FunctionFromName)(char *name);\r\n\tchar *(QDECL *NameForFunction)(hlintptr_t);\r\n\tunk (QDECL *ClientPrintf)(unk);\r\n\tvoid (QDECL *ServerPrint)(char *msg);\r\n\tchar *(QDECL *Cmd_Args)(void);\r\n\tchar *(QDECL *Cmd_Argv)(int argno);\r\n\tint (QDECL *Cmd_Argc)(void);\r\n\tunk (QDECL *GetAttachment)(unk);\r\n\tvoid (QDECL *CRC32_Init)(hlcrc_t *crc);\r\n\tvoid (QDECL *CRC32_ProcessBuffer)(hlcrc_t *crc, qbyte *p, int len);\r\n\tvoid (QDECL *CRC32_ProcessByte)(hlcrc_t *crc, qbyte b);\r\n\thlcrc_t (QDECL *CRC32_Final)(hlcrc_t crc);\r\n\tlong (QDECL *RandomLong)(long minv, long maxv);\r\n\tfloat (QDECL *RandomFloat)(float minv, float maxv);\r\n\tunk (QDECL *SetView)(unk);\r\n\tunk (QDECL *Time)(unk);\r\n\tunk (QDECL *CrosshairAngle)(unk);\r\n\tvoid *(QDECL *LoadFileForMe)(char *name, int *size_out);\r\n\tvoid (QDECL *FreeFile)(void *fptr);\r\n\tunk (QDECL *EndSection)(unk);\r\n\tint (QDECL *CompareFileTime)(char *fname1, char *fname2, int *result);\r\n\tvoid (QDECL *GetGameDir)(char *gamedir);\r\n\tunk (QDECL *Cvar_RegisterVariable)(unk);\r\n\tunk (QDECL *FadeClientVolume)(unk);\r\n\tunk (QDECL *SetClientMaxspeed)(unk);\r\n\tunk (QDECL *CreateFakeClient)(unk);\r\n\tunk (QDECL *RunPlayerMove)(unk);\r\n\tunk (QDECL *NumberOfEntities)(unk);\r\n\tchar *(QDECL *GHL_GetInfoKeyBuffer)(hledict_t *ed);\r\n\tchar *(QDECL *GHL_InfoKeyValue)(char *infostr, char *key);\r\n\tunk (QDECL *SetKeyValue)(unk);\r\n\tunk (QDECL *SetClientKeyValue)(unk);\r\n\tunk (QDECL *IsMapValid)(unk);\r\n\tunk (QDECL *StaticDecal)(unk);\r\n\tunk (QDECL *PrecacheGeneric)(unk);\r\n\tint (QDECL *GetPlayerUserId)(hledict_t *ed);\r\n\tunk (QDECL *BuildSoundMsg)(unk);\r\n\tunk (QDECL *IsDedicatedServer)(unk);\r\n\t//138\r\n#if HALFLIFE_API_VERSION > 138\r\n\thlcvar_t *(QDECL *CVarGetPointer)(char *varname);\r\n\tunk (QDECL *GetPlayerWONId)(unk);\r\n\tunk (QDECL *Info_RemoveKey)(unk);\r\n\tunk (QDECL *GetPhysicsKeyValue)(unk);\r\n\tvoid (QDECL *SetPhysicsKeyValue)(hledict_t *ent, char *key, char *value);\r\n\tunk (QDECL *GetPhysicsInfoString)(unk);\r\n\tunsigned short (QDECL *PrecacheEvent)(int eventtype, char *eventname);\r\n\tvoid (QDECL *PlaybackEvent)(int flags, hledict_t *ent, unsigned short eventidx, float delay, float *origin, float *angles, float f1, float f2, int i1, int i2, int b1, int b2);\r\n\tunk (QDECL *SetFatPVS)(unk);\r\n\tunk (QDECL *SetFatPAS)(unk);\r\n\tunk (QDECL *CheckVisibility)(unk);\r\n\tunk (QDECL *DeltaSetField)(unk);\r\n\tunk (QDECL *DeltaUnsetField)(unk);\r\n\tunk (QDECL *DeltaAddEncoder)(unk);\r\n\tunk (QDECL *GetCurrentPlayer)(unk);\r\n\tint (QDECL *CanSkipPlayer)(hledict_t *playerent);\r\n\tunk (QDECL *DeltaFindField)(unk);\r\n\tunk (QDECL *DeltaSetFieldByIndex)(unk);\r\n\tunk (QDECL *DeltaUnsetFieldByIndex)(unk);\r\n\tunk (QDECL *SetGroupMask)(unk);\r\n\tunk (QDECL *CreateInstancedBaseline)(unk);\r\n\tunk (QDECL *Cvar_DirectSet)(unk);\r\n\tunk (QDECL *ForceUnmodified)(unk);\r\n\tunk (QDECL *GetPlayerStats)(unk);\r\n\tunk (QDECL *AddServerCommand)(unk);\r\n\t//\r\n\tunk (QDECL *Voice_GetClientListening)(unk);\r\n\tqboolean (QDECL *Voice_SetClientListening)(int listener, int sender, int shouldlisten);\r\n\t//140\r\n\tchar *(QDECL *GetPlayerAuthId)(hledict_t *playerent);\r\n\tunk (QDECL *SequenceGet)(unk);\r\n\tunk (QDECL *SequencePickSentence)(unk);\r\n\tunk (QDECL *GetFileSize)(unk);\r\n\tunk (QDECL *GetApproxWavePlayLen)(unk);\r\n\tunk (QDECL *IsCareerMatch)(unk);\r\n\tunk (QDECL *GetLocalizedStringLength)(unk);\r\n\tunk (QDECL *RegisterTutorMessageShown)(unk);\r\n\tunk (QDECL *GetTimesTutorMessageShown)(unk);\r\n\tunk (QDECL *ProcessTutorMessageDecayBuffer)(unk);\r\n\tunk (QDECL *ConstructTutorMessageDecayBuffer)(unk);\r\n\tunk (QDECL *ResetTutorMessageDecayData)(unk);\r\n\tunk (QDECL *QueryClientCvarValue)(unk);\r\n\tunk (QDECL *QueryClientCvarValue2)(unk);\r\n#endif\r\n\r\n\tint check;\t//added so I can be sure parameters match this list, etc\r\n} SVHL_Builtins_t;\r\n\r\nextern SVHL_Globals_t SVHL_Globals;\r\nextern SVHL_GameFuncs_t SVHL_GameFuncs;\r\n\r\nextern hledict_t *SVHL_Edict;\r\nextern int SVHL_NumActiveEnts;\r\n\r\n\r\nvoid QDECL GHL_RemoveEntity(hledict_t *ed);\r\n\r\nvoid SVHL_LinkEdict (hledict_t *ent, qboolean touch_triggers);\r\nvoid SVHL_UnlinkEdict (hledict_t *ent);\r\nhledict_t\t*SVHL_TestEntityPosition (hledict_t *ent);\r\nvoid SVHL_TouchLinks ( hledict_t *ent, areanode_t *node );\r\ntrace_t SVHL_Move (vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, int type, int forcehull, hledict_t *passedict);\r\nint SVHL_PointContents (vec3_t p);\r\nint SVHL_AreaEdicts (vec3_t mins, vec3_t maxs, hledict_t **list, int maxcount);\r\n"
  },
  {
    "path": "engine/server/svhl_phys.c",
    "content": "/*\nCopyright (C) 1996-1997 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n*/\n// sv_phys.c\n\n#include \"quakedef.h\"\n\n#ifdef HLSERVER\n\n#include \"svhl_gcapi.h\"\n\n/*\n\n\npushmove objects do not obey gravity, and do not interact with each other or trigger fields, but block normal movement and push normal objects when they move.\n\nonground is set for toss objects when they come to a complete rest.  it is set for steping or walking objects\n\ndoors, plats, etc are SOLID_BSP, and MOVETYPE_PUSH\nbonus items are SOLID_TRIGGER touch, and MOVETYPE_TOSS\ncorpses are SOLID_NOT and MOVETYPE_TOSS\ncrates are SOLID_BBOX and MOVETYPE_TOSS\nwalking monsters are SOLID_SLIDEBOX and MOVETYPE_STEP\nflying/floating monsters are SOLID_SLIDEBOX and MOVETYPE_FLY\n\nsolid_edge items only clip against bsp models.\n\n*/\n\nextern cvar_t\tsv_maxvelocity;\n\nextern cvar_t\tsv_gravity;\nextern cvar_t\tsv_stopspeed;\nextern cvar_t\tsv_maxspeed;\nextern cvar_t\tsv_spectatormaxspeed;\nextern cvar_t \tsv_accelerate;\nextern cvar_t\tsv_airaccelerate;\nextern cvar_t\tsv_wateraccelerate;\nextern cvar_t\tsv_friction;\nextern cvar_t\tsv_waterfriction;\nextern cvar_t\tsv_gameplayfix_noairborncorpse;\n\n\n#define\tMOVE_EPSILON\t0.01\n\nvoid SVHL_Physics_Toss (hledict_t *ent);\n\nvoid SVHL_StartSound (hledict_t *entity, int channel, char *sample, int volume, float attenuation)\n{\n}\n\n/*\n================\nSV_CheckAllEnts\n================\n*/\nvoid SVHL_CheckAllEnts (void)\n{\n\tint\t\t\te;\n\thledict_t\t\t*check;\n\n// see if any solid entities are inside the final position\n\tfor (e=1 ; e<SVHL_NumActiveEnts ; e++)\n\t{\n\t\tcheck = &SVHL_Edict[e];\n\t\tif (check->isfree)\n\t\t\tcontinue;\n\t\tif (check->v.movetype == MOVETYPE_PUSH\n\t\t|| check->v.movetype == MOVETYPE_NONE\n\t\t|| check->v.movetype == MOVETYPE_FOLLOW\n\t\t|| check->v.movetype == MOVETYPE_NOCLIP)\n\t\t\tcontinue;\n\n\t\tif (SVHL_TestEntityPosition (check))\n\t\t\tCon_Printf (\"entity in invalid position\\n\");\n\t}\n}\n\n/*\n================\nSV_CheckVelocity\n================\n*/\nvoid SVHL_CheckVelocity (hledict_t *ent)\n{\n\tint\t\ti;\n\n//\n// bound velocity\n//\n\tfor (i=0 ; i<3 ; i++)\n\t{\n\t\tif (IS_NAN(ent->v.velocity[i]))\n\t\t{\n\t\t\tCon_Printf (\"Got a NaN velocity on %s\\n\", SVHL_Globals.stringbase+ent->v.classname);\n\t\t\tent->v.velocity[i] = 0;\n\t\t}\n\t\tif (IS_NAN(ent->v.origin[i]))\n\t\t{\n\t\t\tCon_Printf (\"Got a NaN origin on %s\\n\", SVHL_Globals.stringbase+ent->v.classname);\n\t\t\tent->v.origin[i] = 0;\n\t\t}\n\t}\n\n\tif (Length(ent->v.velocity) > sv_maxvelocity.value)\n\t{\n//\t\tCon_DPrintf(\"Slowing %s\\n\", SVHL_Globals.stringbase+ent->v.classname);\n\t\tVectorScale (ent->v.velocity, sv_maxvelocity.value/Length(ent->v.velocity), ent->v.velocity);\n\t}\n}\n\n/*\n=============\nSV_RunThink\n\nRuns thinking code if time.  There is some play in the exact time the think\nfunction will be called, because it is called before any movement is done\nin a frame.  Not used for pushmove objects, because they must be exact.\nReturns false if the entity removed itself.\n=============\n*/\nqboolean SVHL_RunThink (hledict_t *ent)\n{\n\tfloat\tthinktime;\n\n\tif (1)\t//try and imitate nq as closeley as possible\n\t{\n\t\tthinktime = ent->v.nextthink;\n\t\tif (thinktime <= 0 || thinktime > sv.world.physicstime + host_frametime)\n\t\t\treturn true;\n\n\t\tif (thinktime < sv.world.physicstime)\n\t\t\tthinktime = sv.world.physicstime;\t// don't let things stay in the past.\n\t\t\t\t\t\t\t\t\t// it is possible to start that way\n\t\t\t\t\t\t\t\t\t// by a trigger with a local time.\n\t\tent->v.nextthink = 0;\n\t\tSVHL_Globals.time = thinktime;\n\t\tSVHL_GameFuncs.DispatchThink(ent);\n\n\t\treturn !ent->isfree;\n\t}\n\n\tdo\n\t{\n\t\tthinktime = ent->v.nextthink;\n\t\tif (thinktime <= 0)\n\t\t\treturn true;\n\t\tif (thinktime > sv.world.physicstime + host_frametime)\n\t\t\treturn true;\n\n\t\tif (thinktime < sv.world.physicstime)\n\t\t\tthinktime = sv.world.physicstime;\t// don't let things stay in the past.\n\t\t\t\t\t\t\t\t\t// it is possible to start that way\n\t\t\t\t\t\t\t\t\t// by a trigger with a local time.\n\t\tent->v.nextthink = 0;\n\n\t\tSVHL_Globals.time = thinktime;\n\t\tSVHL_GameFuncs.DispatchThink(ent);\n\n\t\tif (ent->isfree)\n\t\t\treturn false;\n\n\t\tif (ent->v.nextthink <= thinktime)\t//hmm... infinate loop was possible here.. Quite a few non-QW mods do this.\n\t\t\treturn true;\n\t} while (1);\n\n\treturn true;\n}\n\n/*\n==================\nSV_Impact\n\nTwo entities have touched, so run their touch functions\n==================\n*/\nvoid SVHL_Impact (hledict_t *e1, hledict_t *e2)\n{\n\tSVHL_Globals.time = sv.world.physicstime;\n\tif (e1->v.solid != SOLID_NOT)\n\t{\n\t\tSVHL_GameFuncs.DispatchTouch(e1, e2);\n\t}\n\n\tif (e2->v.solid != SOLID_NOT)\n\t{\n\t\tSVHL_GameFuncs.DispatchTouch(e2, e1);\n\t}\n}\n\n\n/*\n==================\nClipVelocity\n\nSlide off of the impacting object\n==================\n*/\n#define\tSTOP_EPSILON\t0.1\n//courtesy of darkplaces, it's just more efficient.\nstatic void ClipVelocity (vec3_t in, vec3_t normal, vec3_t out, float overbounce)\n{\n\tint i;\n\tfloat backoff;\n\n\tbackoff = -DotProduct (in, normal) * overbounce;\n\tVectorMA(in, backoff, normal, out);\n\n\tfor (i = 0;i < 3;i++)\n\t\tif (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON)\n\t\t\tout[i] = 0;\n}\n\n\n/*\n============\nSV_FlyMove\n\nThe basic solid body movement clip that slides along multiple planes\nReturns the clipflags if the velocity was modified (hit something solid)\n1 = floor\n2 = wall / step\n4 = dead stop\nIf steptrace is not NULL, the trace of any vertical wall hit will be stored\n============\n*/\n#define\tMAX_CLIP_PLANES\t5\nint SVHL_FlyMove (hledict_t *ent, float time, trace_t *steptrace)\n{\n\tint\t\t\tbumpcount, numbumps;\n\tvec3_t\t\tdir;\n\tfloat\t\td;\n\tint\t\t\tnumplanes;\n\tvec3_t\t\tplanes[MAX_CLIP_PLANES];\n\tvec3_t\t\tprimal_velocity, original_velocity, new_velocity;\n\tint\t\t\ti, j;\n\ttrace_t\t\ttrace;\n\tvec3_t\t\tend;\n\tfloat\t\ttime_left;\n\tint\t\t\tblocked;\n\tvec3_t diff;\n\n\tvec3_t startorg;\n\n\tnumbumps = 4;\n\n\tblocked = 0;\n\tVectorCopy (ent->v.velocity, original_velocity);\n\tVectorCopy (ent->v.velocity, primal_velocity);\n\tnumplanes = 0;\n\n\ttime_left = time;\n\n\tVectorCopy (ent->v.origin, startorg);\n\n\tfor (bumpcount=0 ; bumpcount<numbumps ; bumpcount++)\n\t{\n\t\tfor (i=0 ; i<3 ; i++)\n\t\t\tend[i] = ent->v.origin[i] + time_left * ent->v.velocity[i];\n\n\t\ttrace = SVHL_Move (ent->v.origin, ent->v.mins, ent->v.maxs, end, false, 0, ent);\n\n\t\tif (trace.startsolid)\n\t\t{\t// entity is trapped in another solid\n\t\t\tVectorClear (ent->v.velocity);\n\t\t\treturn 3;\n\t\t}\n\n\t\tif (trace.fraction > 0)\n\t\t{\t// actually covered some distance\n\t\t\tVectorCopy (trace.endpos, ent->v.origin);\n\t\t\tVectorCopy (ent->v.velocity, original_velocity);\n\t\t\tnumplanes = 0;\n\t\t}\n\n\t\tif (trace.fraction == 1)\n\t\t\t break;\t\t// moved the entire distance\n\n\t\tif (!trace.ent)\n\t\t\tSV_Error (\"SV_FlyMove: !trace.ent\");\n\n\t\tif (trace.plane.normal[2] > 0.7)\n\t\t{\n\t\t\tblocked |= 1;\t\t// floor\n\t\t\tif (((hledict_t *)trace.ent)->v.solid == SOLID_BSP)\n\t\t\t{\n\t\t\t\tent->v.flags =\t(int)ent->v.flags | FL_ONGROUND;\n\t\t\t\tent->v.groundentity = trace.ent;\n\t\t\t}\n\t\t}\n\t\tif (!trace.plane.normal[2])\n\t\t{\n\t\t\tblocked |= 2;\t\t// step\n\t\t\tif (steptrace)\n\t\t\t\t*steptrace = trace;\t// save for player extrafriction\n\t\t}\n\n//\n// run the impact function\n//\n\t\tSVHL_Impact (ent, trace.ent);\n\t\tif (ent->isfree)\n\t\t\tbreak;\t\t// removed by the impact function\n\n\n\t\ttime_left -= time_left * trace.fraction;\n\n\t// cliped to another plane\n\t\tif (numplanes >= MAX_CLIP_PLANES)\n\t\t{\t// this shouldn't really happen\n\t\t\tVectorClear (ent->v.velocity);\n\t\t\tif (steptrace)\n\t\t\t\t*steptrace = trace;\t// save for player extrafriction\n\t\t\treturn 3;\n\t\t}\n\n\t\tif (0)\n\t\t{\n\t\t\tClipVelocity(ent->v.velocity, trace.plane.normal, ent->v.velocity, 1);\n\t\t\tbreak;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (numplanes)\n\t\t\t{\n\t\t\t\tVectorSubtract(planes[0], trace.plane.normal, diff);\n\t\t\t\tif (Length(diff) < 0.01)\n\t\t\t\t\tcontinue;\t//hit this plane already\n\t\t\t}\n\n\t\t\tVectorCopy (trace.plane.normal, planes[numplanes]);\n\t\t\tnumplanes++;\n\n\t//\n\t// modify original_velocity so it parallels all of the clip planes\n\t//\n\t\t\tfor (i=0 ; i<numplanes ; i++)\n\t\t\t{\n\t\t\t\tClipVelocity (original_velocity, planes[i], new_velocity, 1);\n\t\t\t\tfor (j=0 ; j<numplanes ; j++)\n\t\t\t\t\tif (j != i)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (DotProduct (new_velocity, planes[j]) < 0)\n\t\t\t\t\t\t\tbreak;\t// not ok\n\t\t\t\t\t}\n\t\t\t\tif (j == numplanes)\n\t\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tif (i != numplanes)\n\t\t\t{\t// go along this plane\n//\t\t\t\tCon_Printf (\"%5.1f %5.1f %5.1f   \",ent->v.velocity[0], ent->v.velocity[1], ent->v.velocity[2]);\n\t\t\t\tVectorCopy (new_velocity, ent->v.velocity);\n//\t\t\t\tCon_Printf (\"%5.1f %5.1f %5.1f\\n\",ent->v.velocity[0], ent->v.velocity[1], ent->v.velocity[2]);\n\t\t\t}\n\t\t\telse\n\t\t\t{\t// go along the crease\n\t\t\t\tif (numplanes != 2)\n\t\t\t\t{\n//\t\t\t\t\tCon_Printf (\"clip velocity, numplanes == %i\\n\",numplanes);\n//\t\t\t\t\tCon_Printf (\"%5.1f %5.1f %5.1f   \",ent->v.velocity[0], ent->v.velocity[1], ent->v.velocity[2]);\n\t\t\t\t\tVectorClear (ent->v.velocity);\n//\t\t\t\t\tCon_Printf (\"%5.1f %5.1f %5.1f\\n\",ent->v.velocity[0], ent->v.velocity[1], ent->v.velocity[2]);\n\t\t\t\t\treturn 7;\n\t\t\t\t}\n//\t\t\t\tCon_Printf (\"%5.1f %5.1f %5.1f   \",ent->v.velocity[0], ent->v.velocity[1], ent->v.velocity[2]);\n\t\t\t\tCrossProduct (planes[0], planes[1], dir);\n\t\t\t\tVectorNormalize(dir);\t//fixes slow falling in corners\n\t\t\t\td = DotProduct (dir, ent->v.velocity);\n\t\t\t\tVectorScale (dir, d, ent->v.velocity);\n//\t\t\t\tCon_Printf (\"%5.1f %5.1f %5.1f\\n\",ent->v.velocity[0], ent->v.velocity[1], ent->v.velocity[2]);\n\t\t\t}\n\t\t}\n\n//\n// if original velocity is against the original velocity, stop dead\n// to avoid tiny occilations in sloping corners\n//\n\t\tif (DotProduct (ent->v.velocity, primal_velocity) <= 0)\n\t\t{\n\t\t\tVectorClear (ent->v.velocity);\n\t\t\treturn blocked;\n\t\t}\n\t}\n\n\treturn blocked;\n}\n\n\n/*\n============\nSV_AddGravity\n\n============\n*/\nvoid SVHL_AddGravity (hledict_t *ent, float scale)\n{\n\tif (!scale && progstype != PROG_QW)\n\t\tscale = 1;\n\tent->v.velocity[2] -= scale * sv_gravity.value/*movevars.gravity*/ * host_frametime;\n}\n\n/*\n===============================================================================\n\nPUSHMOVE\n\n===============================================================================\n*/\n\n/*\n============\nSV_PushEntity\n\nDoes not change the entities velocity at all\n============\n*/\ntrace_t SVHL_PushEntity (hledict_t *ent, vec3_t push)\n{\n\ttrace_t\ttrace;\n\tvec3_t\tend;\n\n\tVectorAdd (ent->v.origin, push, end);\n\n\tif (ent->v.movetype == MOVETYPE_FLYMISSILE)\n\t\ttrace = SVHL_Move (ent->v.origin, ent->v.mins, ent->v.maxs, end, MOVE_MISSILE, 0, ent);\n\telse if (ent->v.solid == SOLID_TRIGGER || ent->v.solid == SOLID_NOT)\n\t// only clip against bmodels\n\t\ttrace = SVHL_Move (ent->v.origin, ent->v.mins, ent->v.maxs, end, MOVE_NOMONSTERS, 0, ent);\n\telse\n\t\ttrace = SVHL_Move (ent->v.origin, ent->v.mins, ent->v.maxs, end, MOVE_NORMAL, 0, ent);\n\n//\tif (trace.ent)\n//\t\tVectorMA(trace.endpos, sv_impactpush.value, trace.plane.normal, ent->v.origin);\n//\telse\n\t\tVectorCopy (trace.endpos, ent->v.origin);\n\tSVHL_LinkEdict (ent, true);\n\n\tif (trace.ent)\n\t\tSVHL_Impact (ent, trace.ent);\n\n\treturn trace;\n}\n\n\n\n\ntypedef struct\n{\n\thledict_t\t*ent;\n\tvec3_t\torigin;\n\tvec3_t\tangles;\n//\tfloat\tdeltayaw;\n} hlpushed_t;\nhlpushed_t\tpushed[1024], *pushed_p;\n\n/*\n============\nSV_Push\n\nObjects need to be moved back on a failed push,\notherwise riders would continue to slide.\n============\n*/\nqboolean SVHL_PushAngles (hledict_t *pusher, vec3_t move, vec3_t amove)\n{\n\tint\t\t\ti, e;\n\thledict_t\t*check, *block;\n\tvec3_t\t\tmins, maxs;\n\tfloat oldsolid;\n\thlpushed_t\t*p;\n\tvec3_t\t\torg, org2, move2, forward, right, up;\n\n\tpushed_p = pushed;\n\n\t// find the bounding box\n\tfor (i=0 ; i<3 ; i++)\n\t{\n\t\tmins[i] = pusher->v.absmin[i] + move[i];\n\t\tmaxs[i] = pusher->v.absmax[i] + move[i];\n\t}\n\n// we need this for pushing things later\n\tVectorNegate (amove, org);\n\tAngleVectors (org, forward, right, up);\n\n// save the pusher's original position\n\tpushed_p->ent = pusher;\n\tVectorCopy (pusher->v.origin, pushed_p->origin);\n\tVectorCopy (pusher->v.angles, pushed_p->angles);\n//\tif (pusher->client)\n//\t\tpushed_p->deltayaw = pusher->client->ps.pmove.delta_angles[YAW];\n\tpushed_p++;\n\n// move the pusher to it's final position\n\tVectorAdd (pusher->v.origin, move, pusher->v.origin);\n\tVectorAdd (pusher->v.angles, amove, pusher->v.angles);\n\tSVHL_LinkEdict (pusher, false);\n\n// see if any solid entities are inside the final position\n\tfor (e = 1; e < SVHL_NumActiveEnts; e++)\n\t{\n\t\tcheck = &SVHL_Edict[e];\n\t\tif (check->isfree)\n\t\t\tcontinue;\n\n\t\tif (check->v.movetype == MOVETYPE_PUSH\n\t\t|| check->v.movetype == MOVETYPE_NONE\n\t\t|| check->v.movetype == MOVETYPE_NOCLIP)\n\t\t\tcontinue;\n\n#if 1\n\t\toldsolid = pusher->v.solid;\n\t\tpusher->v.solid = SOLID_NOT;\n\t\tblock = SVHL_TestEntityPosition (check);\n\t\tpusher->v.solid = oldsolid;\n\t\tif (block)\n\t\t\tcontinue;\n#else\n\t\tif (!check->area.prev)\n\t\t\tcontinue;\t\t// not linked in anywhere\n#endif\n\n\t// if the entity is standing on the pusher, it will definitely be moved\n\t\tif ( ! ( ((int)check->v.flags & FL_ONGROUND)\n\t\t\t&& check->v.groundentity == pusher) )\n\t\t{\n\t\t\t// see if the ent needs to be tested\n\t\t\tif ( check->v.absmin[0] >= maxs[0]\n\t\t\t|| check->v.absmin[1] >= maxs[1]\n\t\t\t|| check->v.absmin[2] >= maxs[2]\n\t\t\t|| check->v.absmax[0] <= mins[0]\n\t\t\t|| check->v.absmax[1] <= mins[1]\n\t\t\t|| check->v.absmax[2] <= mins[2] )\n\t\t\t\tcontinue;\n\n\n\t\t\t// see if the ent's bbox is inside the pusher's final position\n\t\t\tif (!SVHL_TestEntityPosition (check))\n\t\t\t\tcontinue;\n\t\t}\n\n\t\tif ((pusher->v.movetype == MOVETYPE_PUSH) || (check->v.groundentity == pusher))\n\t\t{\n\t\t\t// move this entity\n\t\t\tpushed_p->ent = check;\n\t\t\tVectorCopy (check->v.origin, pushed_p->origin);\n\t\t\tVectorCopy (check->v.angles, pushed_p->angles);\n\t\t\tpushed_p++;\n\n\t\t\t// try moving the contacted entity\n\t\t\tVectorAdd (check->v.origin, move, check->v.origin);\n//\t\t\tif (check->client)\n//\t\t\t{\t// FIXME: doesn't rotate monsters?\n//\t\t\t\tcheck->client->ps.pmove.delta_angles[YAW] += amove[YAW];\n//\t\t\t}\n\t\t\tVectorAdd (check->v.angles, amove, check->v.angles);\n\n\t\t\t// figure movement due to the pusher's amove\n\t\t\tVectorSubtract (check->v.origin, pusher->v.origin, org);\n\t\t\torg2[0] = DotProduct (org, forward);\n\t\t\torg2[1] = -DotProduct (org, right);\n\t\t\torg2[2] = DotProduct (org, up);\n\t\t\tVectorSubtract (org2, org, move2);\n\t\t\tVectorAdd (check->v.origin, move2, check->v.origin);\n\n\t\t\tcheck->v.flags = (int)check->v.flags & ~FL_ONGROUND;\n\n\t\t\t// may have pushed them off an edge\n\t\t\tif (check->v.groundentity != pusher)\n\t\t\t\tcheck->v.groundentity = 0;\n\n\t\t\tblock = SVHL_TestEntityPosition (check);\n\t\t\tif (!block)\n\t\t\t{\t// pushed ok\n\t\t\t\tSVHL_LinkEdict (check, false);\n\t\t\t\t// impact?\n\t\t\t\tcontinue;\n\t\t\t}\n\n\n\n\t\t\t// if it is ok to leave in the old position, do it\n\t\t\t// this is only relevent for riding entities, not pushed\n\t\t\t// FIXME: this doesn't acount for rotation\n\t\t\tVectorSubtract (check->v.origin, move, check->v.origin);\n\t\t\tblock = SVHL_TestEntityPosition (check);\n\t\t\tif (!block)\n\t\t\t{\n\t\t\t\tpushed_p--;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\n\t\t// if it is sitting on top. Do not block.\n\t\tif (check->v.mins[0] == check->v.maxs[0])\n\t\t{\n\t\t\tSVHL_LinkEdict (check, false);\n\t\t\tcontinue;\n\t\t}\n\n//\t\tCon_Printf(\"Pusher hit %s\\n\", PR_GetString(svprogfuncs, check->v.classname));\n\t\tSVHL_GameFuncs.DispatchBlocked(pusher, check);\n\n\t\t// move back any entities we already moved\n\t\t// go backwards, so if the same entity was pushed\n\t\t// twice, it goes back to the original position\n\t\tfor (p=pushed_p-1 ; p>=pushed ; p--)\n\t\t{\n\t\t\tVectorCopy (p->origin, p->ent->v.origin);\n\t\t\tVectorCopy (p->angles, p->ent->v.angles);\n//\t\t\tif (p->ent->client)\n//\t\t\t{\n//\t\t\t\tp->ent->client->ps.pmove.delta_angles[YAW] = p->deltayaw;\n//\t\t\t}\n\t\t\tSVHL_LinkEdict (p->ent, false);\n\t\t}\n\t\treturn false;\n\t}\n\n//FIXME: is there a better way to handle this?\n\t// see if anything we moved has touched a trigger\n\tfor (p=pushed_p-1 ; p>=pushed ; p--)\n\t\tSVHL_TouchLinks ( p->ent, sv.world.areanodes );\n\n\treturn true;\n}\n\n/*\n============\nSV_Push\n\n============\n*/\n#define MAX_PUSHED_ENTITIES 1024\nqboolean SVHL_Push (hledict_t *pusher, vec3_t move, vec3_t amove)\n{\n\tint\t\t\ti, e;\n\thledict_t\t*check, *block;\n\tvec3_t\t\tmins, maxs;\n\tvec3_t\t\tpushorig;\n\tint\t\t\tnum_moved;\n\thledict_t\t*moved_edict[MAX_PUSHED_ENTITIES];\n\tvec3_t\t\tmoved_from[MAX_PUSHED_ENTITIES];\n\tfloat oldsolid;\n\n\tif (amove[0] || amove[1] || amove[2])\n\t{\n\t\treturn SVHL_PushAngles(pusher, move, amove);\n\t}\n\n\tfor (i=0 ; i<3 ; i++)\n\t{\n\t\tmins[i] = pusher->v.absmin[i] + move[i];\n\t\tmaxs[i] = pusher->v.absmax[i] + move[i];\n\t}\n\n\tVectorCopy (pusher->v.origin, pushorig);\n\n// move the pusher to it's final position\n\n\tVectorAdd (pusher->v.origin, move, pusher->v.origin);\n\tSVHL_LinkEdict (pusher, false);\n\n// see if any solid entities are inside the final position\n\tnum_moved = 0;\n\tfor (e=1 ; e<SVHL_NumActiveEnts ; e++)\n\t{\n\t\tcheck = &SVHL_Edict[e];\n\t\tif (check->isfree)\n\t\t\tcontinue;\n\t\tif (check->v.movetype == MOVETYPE_PUSH\n\t\t|| check->v.movetype == MOVETYPE_NONE\n\t\t|| check->v.movetype == MOVETYPE_FOLLOW\n\t\t|| check->v.movetype == MOVETYPE_NOCLIP)\n\t\t\tcontinue;\n\n\t\toldsolid = pusher->v.solid;\n\t\tpusher->v.solid = SOLID_NOT;\n\t\tblock = SVHL_TestEntityPosition (check);\n\t\tpusher->v.solid = oldsolid;\n\t\tif (block)\n\t\t\tcontinue;\n\n\t// if the entity is standing on the pusher, it will definately be moved\n\t\tif ( ! ( ((int)check->v.flags & FL_ONGROUND)\n\t\t&&\n\t\t\tcheck->v.groundentity == pusher) )\n\t\t{\n\t\t\tif ( check->v.absmin[0] >= maxs[0]\n\t\t\t|| check->v.absmin[1] >= maxs[1]\n\t\t\t|| check->v.absmin[2] >= maxs[2]\n\t\t\t|| check->v.absmax[0] <= mins[0]\n\t\t\t|| check->v.absmax[1] <= mins[1]\n\t\t\t|| check->v.absmax[2] <= mins[2] )\n\t\t\t\tcontinue;\n\n\t\t// see if the ent's bbox is inside the pusher's final position\n\t\t\tif (!SVHL_TestEntityPosition (check))\n\t\t\t\tcontinue;\n\t\t}\n\n\t\tif (num_moved < MAX_PUSHED_ENTITIES)\n\t\t{\n\t\t\tVectorCopy (check->v.origin, moved_from[num_moved]);\n\t\t\tmoved_edict[num_moved] = check;\n\t\t\tnum_moved++;\n\n\t//\t\tcheck->v.flags = (int)check->v.flags & ~FL_ONGROUND;\n\n\t\t\t// try moving the contacted entity\n\t\t\tVectorAdd (check->v.origin, move, check->v.origin);\n\t\t\tblock = SVHL_TestEntityPosition (check);\n\t\t\tif (!block)\n\t\t\t{\t// pushed ok\n\t\t\t\tSVHL_LinkEdict (check, false);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// if it is ok to leave in the old position, do it\n\t\t\tVectorSubtract (check->v.origin, move, check->v.origin);\n\t\t\tblock = SVHL_TestEntityPosition (check);\n\t\t\tif (!block)\n\t\t\t{\n\t\t\t\t//if leaving it where it was, allow it to drop to the floor again (useful for plats that move downward)\n\t\t\t\tcheck->v.flags = (int)check->v.flags & ~FL_ONGROUND;\n\n\t\t\t\tnum_moved--;\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// if it is still inside the pusher, block\n\t\t\tif (check->v.mins[0] == check->v.maxs[0])\n\t\t\t{\n\t\t\t\tSVHL_LinkEdict (check, false);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (check->v.solid == SOLID_NOT || check->v.solid == SOLID_TRIGGER)\n\t\t\t{\t// corpse\n\t\t\t\tcheck->v.mins[0] = check->v.mins[1] = 0;\n\t\t\t\tVectorCopy (check->v.mins, check->v.maxs);\n\t\t\t\tSVHL_LinkEdict (check, false);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\n\t\tVectorCopy (pushorig, pusher->v.origin);\n\t\tSVHL_LinkEdict (pusher, false);\n\n\t\t// if the pusher has a \"blocked\" function, call it\n\t\t// otherwise, just stay in place until the obstacle is gone\n\t\tSVHL_GameFuncs.DispatchBlocked(pusher, check);\n\n\t// move back any entities we already moved\n\t\tfor (i=0 ; i<num_moved ; i++)\n\t\t{\n\t\t\tVectorCopy (moved_from[i], moved_edict[i]->v.origin);\n\t\t\tSVHL_LinkEdict (moved_edict[i], false);\n\t\t}\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\n\n/*\n============\nSV_PushMove\n\n============\n*/\nvoid SVHL_PushMove (hledict_t *pusher, float movetime)\n{\n\tint\t\t\ti;\n\tvec3_t\t\tmove;\n\tvec3_t\t\tamove;\n\n\tif (!pusher->v.velocity[0] && !pusher->v.velocity[1] && !pusher->v.velocity[2]\n\t\t&& !pusher->v.avelocity[0] && !pusher->v.avelocity[1] && !pusher->v.avelocity[2])\n\t{\n\t\tpusher->v.ltime += movetime;\n\t\treturn;\n\t}\n\n\tfor (i=0 ; i<3 ; i++)\n\t{\n\t\tmove[i] = pusher->v.velocity[i] * movetime;\n\t\tamove[i] = pusher->v.avelocity[i] * movetime;\n\t}\n\n\tif (SVHL_Push (pusher, move, amove))\n\t\tpusher->v.ltime += movetime;\n}\n\n\n/*\n================\nSV_Physics_Pusher\n\n================\n*/\nvoid SVHL_Physics_Pusher (hledict_t *ent)\n{\n\tfloat\tthinktime;\n\tfloat\toldltime;\n\tfloat\tmovetime;\nvec3_t oldorg, move;\nvec3_t oldang, amove;\nfloat\tl;\n\n\toldltime = ent->v.ltime;\n\n\tthinktime = ent->v.nextthink;\n\tif (thinktime < ent->v.ltime + host_frametime)\n\t{\n\t\tmovetime = thinktime - ent->v.ltime;\n\t\tif (movetime < 0)\n\t\t\tmovetime = 0;\n\t}\n\telse\n\t\tmovetime = host_frametime;\n\n\tif (movetime)\n\t{\n\t\tSVHL_PushMove (ent, movetime);\t// advances ent->v.ltime if not blocked\n\t}\n\n\tif (thinktime > oldltime && thinktime <= ent->v.ltime)\n\t{\n\t\tVectorCopy (ent->v.origin, oldorg);\n\t\tVectorCopy (ent->v.angles, oldang);\n\n\t\tent->v.nextthink = 0;\n\t\tSVHL_Globals.time = sv.world.physicstime;\n\t\tSVHL_GameFuncs.DispatchThink(ent);\n\t\tif (ent->isfree)\n\t\t\treturn;\n\n\t\tVectorSubtract (ent->v.origin, oldorg, move);\n\t\tVectorSubtract (ent->v.angles, oldang, amove);\n\n\t\tl = Length(move)+Length(amove);\n\t\tif (l > 1.0/64)\n\t\t{\n\t\t//\tCon_Printf (\"**** snap: %f\\n\", Length (l));\n\t\t\tVectorCopy (oldorg, ent->v.origin);\n\t\t\tSVHL_Push (ent, move, amove);\n\t\t}\n\n\t}\n\telse if (ent->v.flags & (1<<21))\n\t{\n\t\tent->v.nextthink = 0;\n\t\tSVHL_Globals.time = sv.world.physicstime;\n\t\tSVHL_GameFuncs.DispatchThink(ent);\n\t\tif (ent->isfree)\n\t\t\treturn;\n\t}\n\n}\n\n\n/*\n=============\nSV_Physics_Follow\n\nEntities that are \"stuck\" to another entity\n=============\n*/\nvoid SVHL_Physics_Follow (hledict_t *ent)\n{\n//\tvec3_t vf, vr, vu, angles, v;\n\thledict_t *e;\n\n\t// regular thinking\n\tif (!SVHL_RunThink (ent))\n\t\treturn;\n\n\t// LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects\n\te = ent->v.aiment;\n//\tif (e->v.angles[0] == ent->xv->punchangle[0] && e->v.angles[1] == ent->xv->punchangle[1] && e->v.angles[2] == ent->xv->punchangle[2])\n\t{\n\t\t// quick case for no rotation\n\t\tVectorAdd(e->v.origin, ent->v.view_ofs, ent->v.origin);\n\t}\n/*\telse\n\t{\n\t\tangles[0] = -ent->xv->punchangle[0];\n\t\tangles[1] =  ent->xv->punchangle[1];\n\t\tangles[2] =  ent->xv->punchangle[2];\n\t\tAngleVectors (angles, vf, vr, vu);\n\t\tv[0] = ent->v.view_ofs[0] * vf[0] + ent->v.view_ofs[1] * vr[0] + ent->v.view_ofs[2] * vu[0];\n\t\tv[1] = ent->v.view_ofs[0] * vf[1] + ent->v.view_ofs[1] * vr[1] + ent->v.view_ofs[2] * vu[1];\n\t\tv[2] = ent->v.view_ofs[0] * vf[2] + ent->v.view_ofs[1] * vr[2] + ent->v.view_ofs[2] * vu[2];\n\t\tangles[0] = -e->v.angles[0];\n\t\tangles[1] =  e->v.angles[1];\n\t\tangles[2] =  e->v.angles[2];\n\t\tAngleVectors (angles, vf, vr, vu);\n\t\tent->v.origin[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + e->v.origin[0];\n\t\tent->v.origin[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + e->v.origin[1];\n\t\tent->v.origin[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + e->v.origin[2];\n\t}\n*/\tVectorAdd (e->v.angles, ent->v.v_angle, ent->v.angles);\n\tSVHL_LinkEdict (ent, true);\n}\n\n/*\n=============\nSV_Physics_Noclip\n\nA moving object that doesn't obey physics\n=============\n*/\nvoid SVHL_Physics_Noclip (hledict_t *ent)\n{\n// regular thinking\n\tif (!SVHL_RunThink (ent))\n\t\treturn;\n\n\tVectorMA (ent->v.angles, host_frametime, ent->v.avelocity, ent->v.angles);\n\tVectorMA (ent->v.origin, host_frametime, ent->v.velocity, ent->v.origin);\n\n\tSVHL_LinkEdict (ent, false);\n}\n\n/*\n==============================================================================\n\nTOSS / BOUNCE\n\n==============================================================================\n*/\n\n/*\n=============\nSV_CheckWaterTransition\n\n=============\n*/\nvoid SVHL_CheckWaterTransition (hledict_t *ent)\n{\n\tint\t\tcont;\n\n\tcont = SVHL_PointContents (ent->v.origin);\n\n\t//needs to be q1 progs compatible\n\tif (cont & FTECONTENTS_LAVA)\n\t\tcont = Q1CONTENTS_LAVA;\n\telse if (cont & FTECONTENTS_SLIME)\n\t\tcont = Q1CONTENTS_SLIME;\n\telse if (cont & FTECONTENTS_WATER)\n\t\tcont = Q1CONTENTS_WATER;\n\telse\n\t\tcont = Q1CONTENTS_EMPTY;\n\n\tif (!ent->v.watertype)\n\t{\t// just spawned here\n\t\tent->v.watertype = cont;\n\t\tent->v.waterlevel = 1;\n\t\treturn;\n\t}\n\n\tif (cont <= Q1CONTENTS_WATER)\n\t{\n\t\tif (ent->v.watertype == Q1CONTENTS_EMPTY)\n\t\t{\t// just crossed into water\n\t\t\tSVHL_StartSound (ent, 0, \"misc/h2ohit1.wav\", 255, 1);\n\t\t}\n\t\tent->v.watertype = cont;\n\t\tent->v.waterlevel = 1;\n\t}\n\telse\n\t{\n\t\tif (ent->v.watertype != Q1CONTENTS_EMPTY)\n\t\t{\t// just crossed into open\n\t\t\tSVHL_StartSound (ent, 0, \"misc/h2ohit1.wav\", 255, 1);\n\t\t}\n\t\tent->v.watertype = Q1CONTENTS_EMPTY;\n\t\tent->v.waterlevel = cont;\n\t}\n}\n\n/*\n=============\nSV_Physics_Toss\n\nToss, bounce, and fly movement.  When onground, do nothing.\n=============\n*/\nvoid SVHL_Physics_Toss (hledict_t *ent)\n{\n\ttrace_t\ttrace;\n\tvec3_t\tmove;\n\tfloat\tbackoff;\n\n\tvec3_t temporg;\n\n\tSVHL_CheckVelocity (ent);\n\n// regular thinking\n\tif (!SVHL_RunThink (ent))\n\t\treturn;\n\n// if onground, return without moving\n\tif ( ((int)ent->v.flags & FL_ONGROUND) )\n\t{\n\t\tif (ent->v.velocity[2] >= (1.0f/32.0f))\n\t\t\tent->v.flags = (int)ent->v.flags & ~FL_ONGROUND;\n\t\telse\n\t\t{\n\t\t\tif (sv_gameplayfix_noairborncorpse.value)\n\t\t\t{\n\t\t\t\thledict_t *onent;\n\t\t\t\tonent = ent->v.groundentity;\n\t\t\t\tif (!onent->isfree)\n\t\t\t\t\treturn;\t//don't drop if our fround is still valid\n\t\t\t}\n\t\t\telse\n\t\t\t\treturn;\t//don't drop, even if the item we were on was removed (certain dm maps do this for q3 style stuff).\n\t\t}\n\t}\n\n// add gravity\n\tif (ent->v.movetype != MOVETYPE_FLY\n\t\t&& ent->v.movetype != MOVETYPE_FLYMISSILE\n\t\t&& ent->v.movetype != MOVETYPE_BOUNCEMISSILE\n\t\t&& ent->v.movetype != MOVETYPE_H2SWIM)\n\t\tSVHL_AddGravity (ent, 1.0);\n\n// move angles\n\tVectorMA (ent->v.angles, host_frametime, ent->v.avelocity, ent->v.angles);\n\n// move origin\n\tVectorScale (ent->v.velocity, host_frametime, move);\n\tVectorCopy(ent->v.origin, temporg);\n\tVectorCopy(temporg, ent->v.origin);\n\ttrace = SVHL_PushEntity (ent, move);\n\n\tif (trace.allsolid)\n\t\ttrace.fraction = 0;\n\tif (trace.fraction == 1)\n\t\treturn;\n\tif (ent->isfree)\n\t\treturn;\n\n\tVectorCopy(trace.endpos, move);\n\n\tif (ent->v.movetype == MOVETYPE_BOUNCE)\n\t\tbackoff = 1.5;\n\telse if (ent->v.movetype == MOVETYPE_BOUNCEMISSILE)\n\t\tbackoff = 2;\n\telse\n\t\tbackoff = 1;\n\n\tClipVelocity (ent->v.velocity, trace.plane.normal, ent->v.velocity, backoff);\n\n\n// stop if on ground\n\tif ((trace.plane.normal[2] > 0.7) && (ent->v.movetype != MOVETYPE_BOUNCEMISSILE))\n\t{\n\t\tif (ent->v.velocity[2] < 60 || ent->v.movetype != MOVETYPE_BOUNCE )\n\t\t{\n\t\t\tent->v.flags = (int)ent->v.flags | FL_ONGROUND;\n\t\t\tent->v.groundentity = trace.ent;\n\t\t\tVectorClear (ent->v.velocity);\n\t\t\tVectorClear (ent->v.avelocity);\n\t\t}\n\t}\n\n// check for in water\n\tSVHL_CheckWaterTransition (ent);\n}\n\n/*\n===============================================================================\n\nSTEPPING MOVEMENT\n\n===============================================================================\n*/\n\n/*\n=============\nSV_Physics_Step\n\nMonsters freefall when they don't have a ground entity, otherwise\nall movement is done with discrete steps.\n\nThis is also used for objects that have become still on the ground, but\nwill fall if the floor is pulled out from under them.\nFIXME: is this true?\n=============\n*/\nvoid SVHL_Physics_Step (hledict_t *ent)\n{\n\tqboolean\thitsound;\n\n\tif (ent->v.velocity[2] >= (1.0 / 32.0) && ((int)ent->v.flags & FL_ONGROUND))\n\t\tent->v.flags = (int)ent->v.flags & ~FL_ONGROUND;\n\n// frefall if not onground\n\tif ( ! ((int)ent->v.flags & (FL_ONGROUND | FL_FLY | FL_SWIM) ) )\n\t{\n\t\thitsound = ent->v.velocity[2] < movevars.gravity*-0.1;\n\n\t\tSVHL_AddGravity (ent, 1.0);\n\t\tSVHL_CheckVelocity (ent);\n\t\tSVHL_FlyMove (ent, host_frametime, NULL);\n\t\tSVHL_LinkEdict (ent, true);\n\n\t\tif ( (int)ent->v.flags & FL_ONGROUND )\t// just hit ground\n\t\t{\n\t\t\tif (hitsound)\n\t\t\t{\n\t\t\t\tSVHL_StartSound (ent, 0, \"demon/dland2.wav\", 255, 1);\n\t\t\t}\n\t\t}\n\t}\n\n// regular thinking\n\tSVHL_RunThink (ent);\n\n\tSVHL_CheckWaterTransition (ent);\n}\n\n//============================================================================\n\n\n\n\n\n\n\n\n\n\n\n\n\n/*\n=============\nSV_CheckStuck\n\nThis is a big hack to try and fix the rare case of getting stuck in the world\nclipping hull.\n=============\n*/\nvoid SVHL_CheckStuck (hledict_t *ent)\n{\n\tint\t\ti, j;\n\tint\t\tz;\n\tvec3_t\torg;\n//return;\n\tif (!SVHL_TestEntityPosition(ent))\n\t{\n\t\tVectorCopy (ent->v.origin, ent->v.oldorigin);\n\t\treturn;\n\t}\n\n\tVectorCopy (ent->v.origin, org);\n\tVectorCopy (ent->v.oldorigin, ent->v.origin);\n\tif (!SVHL_TestEntityPosition(ent))\n\t{\n\t\tCon_DPrintf (\"Unstuck.\\n\");\n\t\tSVHL_LinkEdict (ent, true);\n\t\treturn;\n\t}\n\n\tfor (z=0 ; z < movevars.stepheight ; z++)\n\t\tfor (i=-1 ; i <= 1 ; i++)\n\t\t\tfor (j=-1 ; j <= 1 ; j++)\n\t\t\t{\n\t\t\t\tent->v.origin[0] = org[0] + i;\n\t\t\t\tent->v.origin[1] = org[1] + j;\n\t\t\t\tent->v.origin[2] = org[2] + z;\n\t\t\t\tif (!SVHL_TestEntityPosition(ent))\n\t\t\t\t{\n\t\t\t\t\tCon_DPrintf (\"Unstuck.\\n\");\n\t\t\t\t\tSVHL_LinkEdict (ent, true);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\n\tVectorCopy (org, ent->v.origin);\n\tCon_DPrintf (\"player is stuck.\\n\");\n}\n\n/*\n=============\nSV_CheckWater\n=============\n*/\nqboolean SVHL_CheckWater (hledict_t *ent)\n{\n\tvec3_t\tpoint;\n\tint\t\tcont;\n\n\tpoint[0] = ent->v.origin[0];\n\tpoint[1] = ent->v.origin[1];\n\tpoint[2] = ent->v.origin[2] + ent->v.mins[2] + 1;\n\n\tent->v.waterlevel = 0;\n\tent->v.watertype = Q1CONTENTS_EMPTY;\n\tcont = SVHL_PointContents (point);\n\tif (cont & FTECONTENTS_FLUID)\n\t{\n\t\tif (cont & FTECONTENTS_LAVA)\n\t\t\tent->v.watertype = Q1CONTENTS_LAVA;\n\t\telse if (cont & FTECONTENTS_SLIME)\n\t\t\tent->v.watertype = Q1CONTENTS_SLIME;\n\t\telse if (cont & FTECONTENTS_WATER)\n\t\t\tent->v.watertype = Q1CONTENTS_WATER;\n\t\telse\n\t\t\tent->v.watertype = Q1CONTENTS_SKY;\n\t\tent->v.waterlevel = 1;\n\t\tpoint[2] = ent->v.origin[2] + (ent->v.mins[2] + ent->v.maxs[2])*0.5;\n\t\tcont = SVHL_PointContents (point);\n\t\tif (cont & FTECONTENTS_FLUID)\n\t\t{\n\t\t\tent->v.waterlevel = 2;\n\t\t\tpoint[2] = ent->v.origin[2] + ent->v.view_ofs[2];\n\t\t\tcont = SVHL_PointContents (point);\n\t\t\tif (cont & FTECONTENTS_FLUID)\n\t\t\t\tent->v.waterlevel = 3;\n\t\t}\n\t}\n\n\treturn ent->v.waterlevel > 1;\n}\n\n\n/*\n============\nSV_WallFriction\n\n============\n*/\nvoid SVHL_WallFriction (hledict_t *ent, trace_t *trace)\n{\n\tvec3_t\t\tforward, right, up;\n\tfloat\t\td, i;\n\tvec3_t\t\tinto, side;\n\n\tAngleVectors (ent->v.v_angle, forward, right, up);\n\td = DotProduct (trace->plane.normal, forward);\n\n\td += 0.5;\n\tif (d >= 0 || IS_NAN(d))\n\t\treturn;\n\n// cut the tangential velocity\n\ti = DotProduct (trace->plane.normal, ent->v.velocity);\n\tVectorScale (trace->plane.normal, i, into);\n\tVectorSubtract (ent->v.velocity, into, side);\n\n\tent->v.velocity[0] = side[0] * (1 + d);\n\tent->v.velocity[1] = side[1] * (1 + d);\n}\n\n/*\n=====================\nSV_TryUnstick\n\nPlayer has come to a dead stop, possibly due to the problem with limited\nfloat precision at some angle joins in the BSP hull.\n\nTry fixing by pushing one pixel in each direction.\n\nThis is a hack, but in the interest of good gameplay...\n======================\n*/\nint SVHL_TryUnstick (hledict_t *ent, vec3_t oldvel)\n{\n\tint\t\ti;\n\tvec3_t\toldorg;\n\tvec3_t\tdir;\n\tint\t\tclip;\n\ttrace_t\tsteptrace;\n\n\tVectorCopy (ent->v.origin, oldorg);\n\tVectorClear (dir);\n\n\tfor (i=0 ; i<8 ; i++)\n\t{\n// try pushing a little in an axial direction\n\t\tswitch (i)\n\t\t{\n\t\t\tcase 0:\tdir[0] = 2; dir[1] = 0; break;\n\t\t\tcase 1:\tdir[0] = 0; dir[1] = 2; break;\n\t\t\tcase 2:\tdir[0] = -2; dir[1] = 0; break;\n\t\t\tcase 3:\tdir[0] = 0; dir[1] = -2; break;\n\t\t\tcase 4:\tdir[0] = 2; dir[1] = 2; break;\n\t\t\tcase 5:\tdir[0] = -2; dir[1] = 2; break;\n\t\t\tcase 6:\tdir[0] = 2; dir[1] = -2; break;\n\t\t\tcase 7:\tdir[0] = -2; dir[1] = -2; break;\n\t\t}\n\n\t\tSVHL_PushEntity (ent, dir);\n\n// retry the original move\n\t\tent->v.velocity[0] = oldvel[0];\n\t\tent->v. velocity[1] = oldvel[1];\n\t\tent->v. velocity[2] = 0;\n\t\tclip = SVHL_FlyMove (ent, 0.1, &steptrace);\n\n\t\tif ( fabs(oldorg[1] - ent->v.origin[1]) > 4\n\t\t|| fabs(oldorg[0] - ent->v.origin[0]) > 4 )\n\t\t{\n//Con_DPrintf (\"unstuck!\\n\");\n\t\t\treturn clip;\n\t\t}\n\n// go back to the original pos and try again\n\t\tVectorCopy (oldorg, ent->v.origin);\n\t}\n\n\tVectorClear (ent->v.velocity);\n\treturn 7;\t\t// still not moving\n}\n\n/*\n=====================\nSV_WalkMove\n\nOnly used by players\n======================\n*/\n#if 0\n#define\tSMSTEPSIZE\t4\nvoid SVHL_WalkMove (hledict_t *ent)\n{\n\tvec3_t\t\tupmove, downmove;\n\tvec3_t\t\toldorg, oldvel;\n\tvec3_t\t\tnosteporg, nostepvel;\n\tint\t\t\tclip;\n\tint\t\t\toldonground;\n\ttrace_t\t\tsteptrace, downtrace;\n\n//\n// do a regular slide move unless it looks like you ran into a step\n//\n\toldonground = (int)ent->v.flags & FL_ONGROUND;\n\tent->v.flags = (int)ent->v.flags & ~FL_ONGROUND;\n\n\tVectorCopy (ent->v.origin, oldorg);\n\tVectorCopy (ent->v.velocity, oldvel);\n\n\tclip = SVHL_FlyMove (ent, host_frametime, &steptrace);\n\n\tif ( !(clip & 2) )\n\t\treturn;\t\t// move didn't block on a step\n\n\tif (!oldonground && ent->v.waterlevel == 0)\n\t\treturn;\t\t// don't stair up while jumping\n\n\tif (ent->v.movetype != MOVETYPE_WALK)\n\t\treturn;\t\t// gibbed by a trigger\n\n//\tif (sv_nostep.value)\n//\t\treturn;\n\n\tif ( (int)ent->v.flags & FL_WATERJUMP )\n\t\treturn;\n\n\tVectorCopy (ent->v.origin, nosteporg);\n\tVectorCopy (ent->v.velocity, nostepvel);\n\n//\n// try moving up and forward to go up a step\n//\n\tVectorCopy (oldorg, ent->v.origin);\t// back to start pos\n\n\tVectorCopy (vec3_origin, upmove);\n\tVectorCopy (vec3_origin, downmove);\n\tupmove[2] = movevars.stepheight;\n\tdownmove[2] = -movevars.stepheight + oldvel[2]*host_frametime;\n\n// move up\n\tSVHL_PushEntity (ent, upmove);\t// FIXME: don't link?\n\n// move forward\n\tent->v.velocity[0] = oldvel[0];\n\tent->v.velocity[1] = oldvel[1];\n\tent->v.velocity[2] = 0;\n\tclip = SVHL_FlyMove (ent, host_frametime, &steptrace);\n\n// check for stuckness, possibly due to the limited precision of floats\n// in the clipping hulls\n\tif (clip)\n\t{\n\t\tif ( fabs(oldorg[1] - ent->v.origin[1]) < 0.03125\n\t\t&& fabs(oldorg[0] - ent->v.origin[0]) < 0.03125 )\n\t\t{\t// stepping up didn't make any progress\n\t\t\tclip = SVHL_TryUnstick (ent, oldvel);\n\n//\t\t\tCon_Printf(\"Try unstick fwd\\n\");\n\t\t}\n\t}\n\n// extra friction based on view angle\n\tif ( clip & 2 )\n\t{\n\t\tvec3_t lastpos, lastvel, lastdown;\n\n//\t\tCon_Printf(\"couldn't do it\\n\");\n\n\t\t//retry with a smaller step (allows entering smaller areas with a step of 4)\n\t\tVectorCopy (downmove, lastdown);\n\t\tVectorCopy (ent->v.origin, lastpos);\n\t\tVectorCopy (ent->v.velocity, lastvel);\n\n\t//\n\t// try moving up and forward to go up a step\n\t//\n\t\tVectorCopy (oldorg, ent->v.origin);\t// back to start pos\n\n\t\tVectorCopy (vec3_origin, upmove);\n\t\tVectorCopy (vec3_origin, downmove);\n\t\tupmove[2] = SMSTEPSIZE;\n\t\tdownmove[2] = -SMSTEPSIZE + oldvel[2]*host_frametime;\n\n\t// move up\n\t\tSVHL_PushEntity (ent, upmove);\t// FIXME: don't link?\n\n\t// move forward\n\t\tent->v.velocity[0] = oldvel[0];\n\t\tent->v.velocity[1] = oldvel[1];\n\t\tent->v.velocity[2] = 0;\n\t\tclip = SVHL_FlyMove (ent, host_frametime, &steptrace);\n\n\t// check for stuckness, possibly due to the limited precision of floats\n\t// in the clipping hulls\n\t\tif (clip)\n\t\t{\n\t\t\tif ( fabs(oldorg[1] - ent->v.origin[1]) < 0.03125\n\t\t\t&& fabs(oldorg[0] - ent->v.origin[0]) < 0.03125 )\n\t\t\t{\t// stepping up didn't make any progress\n\t\t\t\tclip = SVHL_TryUnstick (ent, oldvel);\n\n//\t\t\t\tCon_Printf(\"Try unstick up\\n\");\n\t\t\t}\n\t\t}\n\n\t\tif ( fabs(oldorg[1] - ent->v.origin[1])+fabs(oldorg[0] - ent->v.origin[0]) < fabs(oldorg[1] - lastpos[1])+fabs(oldorg[1] - lastpos[1]))\n\t\t{\t// stepping up didn't make any progress\n\t\t\t\t//go back\n\t\t\t\tVectorCopy (lastdown, downmove);\n\t\t\t\tVectorCopy (lastpos, ent->v.origin);\n\t\t\t\tVectorCopy (lastvel, ent->v.velocity);\n\n\t\t\t\tSVHL_WallFriction (ent, &steptrace);\n\n//\t\t\t\tCon_Printf(\"wall friction\\n\");\n\t\t\t}\n\n\t\telse if (clip & 2)\n\t\t{\n\t\t\tSVHL_WallFriction (ent, &steptrace);\n//\t\t\tCon_Printf(\"wall friction 2\\n\");\n\t\t}\n\t}\n\n// move down\n\tdowntrace = SVHL_PushEntity (ent, downmove);\t// FIXME: don't link?\n\n\tif (downtrace.plane.normal[2] > 0.7)\n\t{\n\t\tif (ent->v.solid == SOLID_BSP)\n\t\t{\n\t\t\tent->v.flags =\t(int)ent->v.flags | FL_ONGROUND;\n\t\t\tent->v.groundentity = EDICT_TO_PROG(svprogfuncs, downtrace.ent);\n\t\t}\n\t}\n\telse\n\t{\n// if the push down didn't end up on good ground, use the move without\n// the step up.  This happens near wall / slope combinations, and can\n// cause the player to hop up higher on a slope too steep to climb\n\t\tVectorCopy (nosteporg, ent->v.origin);\n\t\tVectorCopy (nostepvel, ent->v.velocity);\n\n//\t\tCon_Printf(\"down not good\\n\");\n\t}\n}\n#else\n\n// 1/32 epsilon to keep floating point happy\n#define\tDIST_EPSILON\t(0.03125)\nint SVHL_SetOnGround (hledict_t *ent)\n{\n\tvec3_t end;\n\ttrace_t trace;\n\tif ((int)ent->v.flags & FL_ONGROUND)\n\t\treturn 1;\n\tend[0] = ent->v.origin[0];\n\tend[1] = ent->v.origin[1];\n\tend[2] = ent->v.origin[2] - 1;\n\ttrace = SVHL_Move(ent->v.origin, ent->v.mins, ent->v.maxs, end, MOVE_NORMAL, 0, ent);\n\tif (trace.fraction <= DIST_EPSILON && trace.plane.normal[2] >= 0.7)\n\t{\n\t\tent->v.flags = (int)ent->v.flags | FL_ONGROUND;\n\t\tent->v.groundentity = trace.ent;\n\t\treturn 1;\n\t}\n\treturn 0;\n}\nvoid SVHL_WalkMove (hledict_t *ent)\n{\n\tint clip, oldonground, originalmove_clip, originalmove_flags;\n\thledict_t *originalmove_groundentity;\n\tvec3_t upmove, downmove, start_origin, start_velocity, originalmove_origin, originalmove_velocity;\n\ttrace_t downtrace, steptrace;\n\n\tSVHL_CheckVelocity(ent);\n\n\t// do a regular slide move unless it looks like you ran into a step\n\toldonground = (int)ent->v.flags & FL_ONGROUND;\n\tent->v.flags = (int)ent->v.flags & ~FL_ONGROUND;\n\n\tVectorCopy (ent->v.origin, start_origin);\n\tVectorCopy (ent->v.velocity, start_velocity);\n\n\tclip = SVHL_FlyMove (ent, host_frametime, NULL);\n\n\tSVHL_SetOnGround (ent);\n\tSVHL_CheckVelocity(ent);\n\n\tVectorCopy(ent->v.origin, originalmove_origin);\n\tVectorCopy(ent->v.velocity, originalmove_velocity);\n\toriginalmove_clip = clip;\n\toriginalmove_flags = (int)ent->v.flags;\n\toriginalmove_groundentity = ent->v.groundentity;\n\n\tif ((int)ent->v.flags & FL_WATERJUMP)\n\t\treturn;\n\n//\tif (sv_nostep.value)\n//\t\treturn;\n\n\t// if move didn't block on a step, return\n\tif (clip & 2)\n\t{\n\t\t// if move was not trying to move into the step, return\n\t\tif (fabs(start_velocity[0]) < 0.03125 && fabs(start_velocity[1]) < 0.03125)\n\t\t\treturn;\n\n\t\tif (ent->v.movetype != MOVETYPE_FLY)\n\t\t{\n\t\t\t// return if gibbed by a trigger\n\t\t\tif (ent->v.movetype != MOVETYPE_WALK)\n\t\t\t\treturn;\n\n\t\t\t// only step up while jumping if that is enabled\n//\t\t\tif (!(sv_jumpstep.value && sv_gameplayfix_stepwhilejumping.value))\n\t\t\t\tif (!oldonground && ent->v.waterlevel == 0)\n\t\t\t\t\treturn;\n\t\t}\n\n\t\t// try moving up and forward to go up a step\n\t\t// back to start pos\n\t\tVectorCopy (start_origin, ent->v.origin);\n\t\tVectorCopy (start_velocity, ent->v.velocity);\n\n\t\t// move up\n\t\tVectorClear (upmove);\n\t\tupmove[2] = movevars.stepheight;\n\t\t// FIXME: don't link?\n\t\tSVHL_PushEntity(ent, upmove);\n\n\t\t// move forward\n\t\tent->v.velocity[2] = 0;\n\t\tclip = SVHL_FlyMove (ent, host_frametime, &steptrace);\n\t\tent->v.velocity[2] += start_velocity[2];\n\n\t\tSVHL_CheckVelocity(ent);\n\n\t\t// check for stuckness, possibly due to the limited precision of floats\n\t\t// in the clipping hulls\n\t\tif (clip\n\t\t && fabs(originalmove_origin[1] - ent->v.origin[1]) < 0.03125\n\t\t && fabs(originalmove_origin[0] - ent->v.origin[0]) < 0.03125)\n\t\t{\n//\t\t\tCon_Printf(\"wall\\n\");\n\t\t\t// stepping up didn't make any progress, revert to original move\n\t\t\tVectorCopy(originalmove_origin, ent->v.origin);\n\t\t\tVectorCopy(originalmove_velocity, ent->v.velocity);\n\t\t\t//clip = originalmove_clip;\n\t\t\tent->v.flags = originalmove_flags;\n\t\t\tent->v.groundentity = originalmove_groundentity;\n\t\t\t// now try to unstick if needed\n\t\t\t//clip = SVHL_TryUnstick (ent, oldvel);\n\t\t\treturn;\n\t\t}\n\n\t\t//Con_Printf(\"step - \");\n\n\t\t// extra friction based on view angle\n\t\tif (clip & 2)// && sv_wallfriction.value)\n\t\t{\n//\t\t\tCon_Printf(\"wall\\n\");\n\t\t\tSVHL_WallFriction (ent, &steptrace);\n\t\t}\n\t}\n\telse if (/*!sv_gameplayfix_stepdown.integer || */!oldonground || start_velocity[2] > 0 || ((int)ent->v.flags & FL_ONGROUND) || ent->v.waterlevel >= 2)\n\t\treturn;\n\n\t// move down\n\tVectorClear (downmove);\n\tdownmove[2] = -movevars.stepheight + start_velocity[2]*host_frametime;\n\t// FIXME: don't link?\n\tdowntrace = SVHL_PushEntity (ent, downmove);\n\n\tif (downtrace.fraction < 1 && downtrace.plane.normal[2] > 0.7)\n\t{\n\t\t// LordHavoc: disabled this check so you can walk on monsters/players\n\t\t//if (ent->v.solid == SOLID_BSP)\n\t\t{\n\t\t\t//Con_Printf(\"onground\\n\");\n\t\t\tent->v.flags =\t(int)ent->v.flags | FL_ONGROUND;\n\t\t\tent->v.groundentity = downtrace.ent;\n\t\t}\n\t}\n\telse\n\t{\n\t\t//Con_Printf(\"slope\\n\");\n\t\t// if the push down didn't end up on good ground, use the move without\n\t\t// the step up.  This happens near wall / slope combinations, and can\n\t\t// cause the player to hop up higher on a slope too steep to climb\n\t\tVectorCopy(originalmove_origin, ent->v.origin);\n\t\tVectorCopy(originalmove_velocity, ent->v.velocity);\n\t\t//clip = originalmove_clip;\n\t\tent->v.flags = originalmove_flags;\n\t\tent->v.groundentity = originalmove_groundentity;\n\t}\n\n\tSVHL_SetOnGround (ent);\n\tSVHL_CheckVelocity(ent);\n}\n#endif\n\n/*\n================\nSV_RunEntity\n\n================\n*/\nvoid SVHL_RunEntity (hledict_t *ent)\n{\n\tif (ent-SVHL_Edict > 0 && ent-SVHL_Edict <= sv.allocated_client_slots)\n\t{\t//a client woo.\n\t/*\tif ( svs.clients[ent->entnum-1].state < cs_spawned )\n\t\t\treturn;\t\t// unconnected slot\n\n\n\t\tif (svs.clients[ent->entnum-1].protocol == SCP_BAD)\n\t\t\tsvs.clients[ent->entnum-1].edict->v.fixangle = 0;\t//bots never get fixangle cleared otherwise\n\n\t\thost_client = &svs.clients[ent->entnum-1];\n\t\tSVHL_ClientThink();\n\n\t//\n\t// call standard client pre-think\n\t//\n\t\tpr_global_struct->time = sv.physicstime;\n\t\tpr_global_struct->self = EDICT_TO_PROG(svprogfuncs, ent);\n#ifdef VM_Q1\n\t\tif (svs.gametype == GT_Q1QVM)\n\t\t\tQ1QVM_PlayerPreThink();\n\t\telse\n#endif\n\t\t\tif (pr_global_struct->PlayerPreThink)\n\t\t\t\tPR_ExecuteProgram (svprogfuncs, pr_global_struct->PlayerPreThink);\n\n\t\t\t*/\n\t}\n\n\tif (ent->v.flags & (1<<30))\n\t{\n\t\tGHL_RemoveEntity(ent);\n\t\treturn;\n\t}\n\n\tswitch ( (int)ent->v.movetype)\n\t{\n\tcase MOVETYPE_PUSH:\n\t\tSVHL_Physics_Pusher (ent);\n\t\tbreak;\n\tcase MOVETYPE_NONE:\n\t\tif (!SVHL_RunThink (ent))\n\t\t\treturn;\n\t\tbreak;\n\tcase MOVETYPE_NOCLIP:\n\t\tSVHL_Physics_Noclip (ent);\n\t\tbreak;\n\tcase MOVETYPE_STEP:\n\tcase MOVETYPE_H2PUSHPULL:\n\t\tSVHL_Physics_Step (ent);\n\t\tbreak;\n\tcase MOVETYPE_FOLLOW:\n\t\tSVHL_Physics_Follow (ent);\n\t\tbreak;\n\tcase MOVETYPE_TOSS:\n\tcase MOVETYPE_BOUNCE:\n\tcase MOVETYPE_BOUNCEMISSILE:\n\tcase MOVETYPE_FLY:\n\tcase MOVETYPE_FLYMISSILE:\n\tcase MOVETYPE_H2SWIM:\n\t\tSVHL_Physics_Toss (ent);\n\t\tbreak;\n\tcase MOVETYPE_WALK:\n\t\tif (!SVHL_RunThink (ent))\n\t\t\treturn;\n\t\tif (!SVHL_CheckWater (ent) && ! ((int)ent->v.flags & FL_WATERJUMP) )\n\t\t\tSVHL_AddGravity (ent, ent->v.gravity);\n\t\tSVHL_CheckStuck (ent);\n\n\t\tSVHL_WalkMove (ent);\n\n//\t\tif (!(ent->entnum > 0 && ent->entnum <= sv.allocated_client_slots))\n\t\t\tSVHL_LinkEdict (ent, true);\n\n\t\tbreak;\n\tdefault:\n\t\tSV_Error (\"SV_Physics: bad movetype %i on %s\", (int)ent->v.movetype, SVHL_Globals.stringbase+ent->v.classname);\n\t}\n\n\t/*\n\tif (ent->entnum > 0 && ent->entnum <= sv.allocated_client_slots)\n\t{\n\t\tSVhL_LinkEdict (ent, true);\n\n\t\tpr_global_struct->time = sv.physicstime;\n\t\tpr_global_struct->self = EDICT_TO_PROG(svprogfuncs, ent);\n#ifdef VM_Q1\n\t\tif (svs.gametype == GT_Q1QVM)\n\t\t\tQ1QVM_PostThink();\n\t\telse\n#endif\n\t\t{\n\t\t\tif (pr_global_struct->PlayerPostThink)\n\t\t\t\tPR_ExecuteProgram (svprogfuncs, pr_global_struct->PlayerPostThink);\n\t\t}\n\t}\n\t*/\n}\n\ntrace_t SVHL_Trace_Toss (hledict_t *tossent, hledict_t *ignore)\n{\n\tint i;\n\tfloat gravity;\n\tvec3_t move, end;\n\ttrace_t trace;\n\n\tvec3_t origin, velocity;\n\n\t// this has to fetch the field from the original edict, since our copy is truncated\n\tgravity = tossent->v.gravity;\n\tif (!gravity)\n\t\tgravity = 1.0;\n\tgravity *= sv_gravity.value * 0.05;\n\n\tVectorCopy (tossent->v.origin, origin);\n\tVectorCopy (tossent->v.velocity, velocity);\n\n\tSVHL_CheckVelocity (tossent);\n\n\tfor (i = 0;i < 200;i++) // LordHavoc: sanity check; never trace more than 10 seconds\n\t{\n\t\tvelocity[2] -= gravity;\n\t\tVectorScale (velocity, 0.05, move);\n\t\tVectorAdd (origin, move, end);\n\t\ttrace = SVHL_Move (origin, tossent->v.mins, tossent->v.maxs, end, MOVE_NORMAL, 0, tossent);\n\t\tVectorCopy (trace.endpos, origin);\n\n\t\tif (trace.fraction < 1 && trace.ent && trace.ent != ignore)\n\t\t\tbreak;\n\n\t\tif (Length(velocity) > sv_maxvelocity.value)\n\t\t{\n//\t\t\tCon_DPrintf(\"Slowing %s\\n\", PR_GetString(svprogfuncs, tossent->v.classname));\n\t\t\tVectorScale (velocity, sv_maxvelocity.value/Length(velocity), velocity);\n\t\t}\n\t}\n\n\ttrace.fraction = 0; // not relevant\n\treturn trace;\n}\n\n/*\n================\nSV_Physics\n\n================\n*/\nvoid SVHL_RunFrame (void)\n{\n\tqboolean retouch;\n\tint i;\n\thledict_t *ent;\n\n\t//only run physics tics if there's a client on the server.\n\t//this fixes the bug where the train moves out before the player spawns, so the player doesn't fall to his death\n\tif (sv.state == ss_active)\n\t{\n\t\tfor (i = 0; i < sv.allocated_client_slots; i++)\n\t\t{\n\t\t\tif (svs.clients[i].state == cs_spawned)\n\t\t\t\tbreak;\n\t\t}\n\t\tif (i == sv.allocated_client_slots)\n\t\t\treturn;\n\t}\n\n\n\tSVHL_Globals.frametime = host_frametime;\n\tSVHL_Globals.time = sv.world.physicstime;\n\n\tSVHL_GameFuncs.StartFrame ();\n\n\n\tretouch = SVHL_Globals.force_retouch;\n\n//\n// treat each object in turn\n// even the world gets a chance to think\n//\n\tfor (i=0 ; i<SVHL_NumActiveEnts ; i++)\n\t{\n\t\tent = &SVHL_Edict[i];\n\t\tif (ent->isfree)\n\t\t\tcontinue;\n\n\t\tif (retouch)\n\t\t\tSVHL_LinkEdict (ent, true);\t// force retouch even for stationary\n\n\t\tif (i > 0 && i <= sv.allocated_client_slots)\n\t\t{\n\t\t\tif (!svs.clients[i-1].isindependant)\n\t\t\t{\n\t\t\t//\tSVHL_RunEntity(ent);\n\t\t\t}\n\t\t\telse\n\t\t\t\tSVHL_LinkEdict(ent, true);\n\t\t\tcontinue;\t\t// clients are run directly from packets\n\t\t}\n\n\t\tSVHL_RunEntity (ent);\n\t}\n\n\tif (retouch)\n\t\tSVHL_Globals.force_retouch-=1;\n}\n\n#endif\n"
  },
  {
    "path": "engine/server/svhl_world.c",
    "content": "/*\r\nCopyright (C) 1996-1997 Id Software, Inc.\r\n\r\nThis program is free software; you can redistribute it and/or\r\nmodify it under the terms of the GNU General Public License\r\nas published by the Free Software Foundation; either version 2\r\nof the License, or (at your option) any later version.\r\n\r\nThis program is distributed in the hope that it will be useful,\r\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\r\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  \r\n\r\nSee the GNU General Public License for more details.\r\n\r\nYou should have received a copy of the GNU General Public License\r\nalong with this program; if not, write to the Free Software\r\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\r\n\r\n*/\r\n// world.c -- world query functions\r\n\r\n#include \"quakedef.h\"\r\n\r\n#ifdef HLSERVER\r\n#include \"svhl_gcapi.h\"\r\n\r\nhull_t\t*World_HullForBox (vec3_t mins, vec3_t maxs);\r\n//qboolean TransformedTrace (struct model_s *model, int hulloverride, int frame, vec3_t start, vec3_t end, vec3_t mins, vec3_t maxs, struct trace_s *trace, vec3_t origin, vec3_t angles, unsigned int hitcontentsmask);\r\n/*\r\n\r\nentities never clip against themselves, or their owner\r\n\r\nline of sight checks trace->crosscontent, but bullets don't\r\n\r\n*/\r\n\r\nextern cvar_t sv_compatiblehulls;\r\n\r\ntypedef struct\r\n{\r\n\tvec3_t\t\tboxmins, boxmaxs;// enclose the test object along entire move\r\n\tfloat\t\t*mins, *maxs;\t// size of the moving object\r\n\tvec3_t\t\tmins2, maxs2;\t// size when clipping against mosnters\r\n\tfloat\t\t*start, *end;\r\n\ttrace_t\t\ttrace;\r\n\tint\t\t\ttype;\r\n\thledict_t\t\t*passedict;\r\n\tint hullnum;\r\n\tunsigned int clipmask;\r\n} hlmoveclip_t;\r\n\r\n/*\r\n===============================================================================\r\n\r\nHULL BOXES\r\n\r\n===============================================================================\r\n*/\r\n\r\n\r\n/*\r\n===============================================================================\r\n\r\nENTITY AREA CHECKING\r\n\r\n===============================================================================\r\n*/\r\n\r\n/*\r\n===============\r\nSV_UnlinkEdict\r\n\r\n===============\r\n*/\r\nvoid SVHL_UnlinkEdict (hledict_t *ent)\r\n{\r\n\tif (!ent->area.prev)\r\n\t\treturn;\t\t// not linked in anywhere\r\n\tRemoveLink (&ent->area);\r\n\tent->area.prev = ent->area.next = NULL;\r\n}\r\n\r\n\r\n/*\r\n====================\r\nSV_TouchLinks\r\n====================\r\n*/\r\n#define MAX_NODELINKS\t256\t//all this means is that any more than this will not touch.\r\nhledict_t *nodelinks[MAX_NODELINKS];\r\nvoid SVHL_TouchLinks ( hledict_t *ent, areanode_t *node )\r\n{\t//Spike: rewritten this function to cope with killtargets used on a few maps.\r\n\tlink_t\t\t*l, *next;\r\n\thledict_t\t\t*touch;\r\n\r\n\tint linkcount = 0, ln;\r\n\r\n\t//work out who they are first.\r\n\tfor (l = node->edicts.next ; l != &node->edicts ; l = next)\r\n\t{\r\n\t\tif (linkcount == MAX_NODELINKS)\r\n\t\t\tbreak;\r\n\t\tnext = l->next;\r\n\t\ttouch = HLEDICT_FROM_AREA(l);\r\n\t\tif (touch == ent)\r\n\t\t\tcontinue;\r\n\r\n\t\tif (touch->v.solid != SOLID_TRIGGER)\r\n\t\t\tcontinue;\r\n\r\n\t\tif (ent->v.absmin[0] > touch->v.absmax[0]\r\n\t\t|| ent->v.absmin[1] > touch->v.absmax[1]\r\n\t\t|| ent->v.absmin[2] > touch->v.absmax[2]\r\n\t\t|| ent->v.absmax[0] < touch->v.absmin[0]\r\n\t\t|| ent->v.absmax[1] < touch->v.absmin[1]\r\n\t\t|| ent->v.absmax[2] < touch->v.absmin[2] )\r\n\t\t\tcontinue;\r\n\r\n//\t\tif (!((int)ent->xv.dimension_solid & (int)touch->xv.dimension_hit))\r\n//\t\t\tcontinue;\r\n\r\n\t\tnodelinks[linkcount++] = touch;\r\n\t}\r\n\r\n\tfor (ln = 0; ln < linkcount; ln++)\r\n\t{\r\n\t\ttouch = nodelinks[ln];\r\n\r\n\t\t//make sure nothing moved it away\r\n\t\tif (touch->isfree)\r\n\t\t\tcontinue;\r\n\t\tif (touch->v.solid != SOLID_TRIGGER)\r\n\t\t\tcontinue;\r\n\t\tif (ent->v.absmin[0] > touch->v.absmax[0]\r\n\t\t|| ent->v.absmin[1] > touch->v.absmax[1]\r\n\t\t|| ent->v.absmin[2] > touch->v.absmax[2]\r\n\t\t|| ent->v.absmax[0] < touch->v.absmin[0]\r\n\t\t|| ent->v.absmax[1] < touch->v.absmin[1]\r\n\t\t|| ent->v.absmax[2] < touch->v.absmin[2] )\r\n\t\t\tcontinue;\r\n\r\n//\t\tif (!((int)ent->xv->dimension_solid & (int)touch->xv->dimension_hit))\t//didn't change did it?...\r\n//\t\t\tcontinue;\r\n\r\n\t\tSVHL_GameFuncs.DispatchTouch(touch, ent);\r\n\r\n\t\tif (ent->isfree)\r\n\t\t\tbreak;\r\n\t}\r\n\r\n\r\n// recurse down both sides\r\n\tif (node->axis == -1 || ent->isfree)\r\n\t\treturn;\r\n\t\r\n\tif ( ent->v.absmax[node->axis] > node->dist )\r\n\t\tSVHL_TouchLinks ( ent, node->children[0] );\r\n\tif ( ent->v.absmin[node->axis] < node->dist )\r\n\t\tSVHL_TouchLinks ( ent, node->children[1] );\r\n}\r\n\r\n/*\r\n===============\r\nSV_LinkEdict\r\n\r\n===============\r\n*/\r\nvoid SVHL_LinkEdict (hledict_t *ent, qboolean touch_triggers)\r\n{\r\n\tareanode_t\t*node;\r\n\t\r\n\tif (ent->area.prev)\r\n\t\tSVHL_UnlinkEdict (ent);\t// unlink from old position\r\n\r\n\tif (ent == &SVHL_Edict[0])\r\n\t\treturn;\t\t// don't add the world\r\n\r\n\tif (ent->isfree)\r\n\t\treturn;\r\n\r\n// set the abs box\r\n\tif (ent->v.solid == SOLID_BSP && \r\n\t(ent->v.angles[0] || ent->v.angles[1] || ent->v.angles[2]) )\r\n\t{\t// expand for rotation\r\n\r\n#if 1\r\n\t\tint i;\r\n\t\tfloat v;\r\n\t\tfloat max;\r\n\t\t//q2 method\r\n\t\tmax = 0;\r\n\t\tfor (i=0 ; i<3 ; i++)\r\n\t\t{\r\n\t\t\tv =fabs( ent->v.mins[i]);\r\n\t\t\tif (v > max)\r\n\t\t\t\tmax = v;\r\n\t\t\tv =fabs( ent->v.maxs[i]);\r\n\t\t\tif (v > max)\r\n\t\t\t\tmax = v;\r\n\t\t}\r\n\t\tfor (i=0 ; i<3 ; i++)\r\n\t\t{\r\n\t\t\tent->v.absmin[i] = ent->v.origin[i] - max;\r\n\t\t\tent->v.absmax[i] = ent->v.origin[i] + max;\r\n\t\t}\r\n#else\r\n\r\n\t\tint\t\t\ti;\r\n\r\n\t\tvec3_t f, r, u;\r\n\t\tvec3_t mn, mx;\r\n\r\n\t\t//we need to link to the correct leaves\r\n\r\n\t\tAngleVectors(ent->v.angles, f,r,u);\r\n\r\n\t\tmn[0] = DotProduct(ent->v.mins, f);\r\n\t\tmn[1] = -DotProduct(ent->v.mins, r);\r\n\t\tmn[2] = DotProduct(ent->v.mins, u);\r\n\r\n\t\tmx[0] = DotProduct(ent->v.maxs, f);\r\n\t\tmx[1] = -DotProduct(ent->v.maxs, r);\r\n\t\tmx[2] = DotProduct(ent->v.maxs, u);\r\n\t\tfor (i = 0; i < 3; i++)\r\n\t\t{\r\n\t\t\tif (mn[i] < mx[i])\r\n\t\t\t{\r\n\t\t\t\tent->v.absmin[i] = ent->v.origin[i]+mn[i]-0.1;\r\n\t\t\t\tent->v.absmax[i] = ent->v.origin[i]+mx[i]+0.1;\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\t//box went inside out\r\n\t\t\t\tent->v.absmin[i] = ent->v.origin[i]+mx[i]-0.1;\r\n\t\t\t\tent->v.absmax[i] = ent->v.origin[i]+mn[i]+0.1;\r\n\t\t\t}\r\n\t\t}\r\n#endif\r\n\t}\r\n\telse\r\n\t{\r\n\t\tVectorAdd (ent->v.origin, ent->v.mins, ent->v.absmin);\t\r\n\t\tVectorAdd (ent->v.origin, ent->v.maxs, ent->v.absmax);\r\n\t}\r\n\r\n//\r\n// to make items easier to pick up and allow them to be grabbed off\r\n// of shelves, the abs sizes are expanded\r\n//\r\n\tif ((int)ent->v.flags & FL_ITEM)\r\n\t{\r\n\t\tent->v.absmin[0] -= 15;\r\n\t\tent->v.absmin[1] -= 15;\r\n\t\tent->v.absmin[2] -= 1;\r\n\t\tent->v.absmax[0] += 15;\r\n\t\tent->v.absmax[1] += 15;\r\n\t\tent->v.absmax[2] += 1;\r\n\t}\r\n\telse\r\n\t{\t// because movement is clipped an epsilon away from an actual edge,\r\n\t\t// we must fully check even when bounding boxes don't quite touch\r\n\t\tent->v.absmin[0] -= 1;\r\n\t\tent->v.absmin[1] -= 1;\r\n\t\tent->v.absmin[2] -= 1;\r\n\t\tent->v.absmax[0] += 1;\r\n\t\tent->v.absmax[1] += 1;\r\n\t\tent->v.absmax[2] += 1;\r\n\t}\r\n\t\r\n// link to PVS leafs\r\n//\tsv.worldmodel->funcs.FindTouchedLeafs_Q1(sv.worldmodel, ent, ent->v.absmin, ent->v.absmax);\r\n\r\n// find the first node that the ent's box crosses\r\n\tnode = sv.world.areanodes;\r\n\twhile (1)\r\n\t{\r\n\t\tif (node->axis == -1)\r\n\t\t\tbreak;\r\n\t\tif (ent->v.absmin[node->axis] > node->dist)\r\n\t\t\tnode = node->children[0];\r\n\t\telse if (ent->v.absmax[node->axis] < node->dist)\r\n\t\t\tnode = node->children[1];\r\n\t\telse\r\n\t\t\tbreak;\t\t// crosses the node\r\n\t}\r\n\t\r\n// link it in\t\r\n\r\n\tInsertLinkBefore (&ent->area, &node->edicts);\r\n\t\r\n// if touch_triggers, touch all entities at this node and decend for more\r\n\tif (touch_triggers)\r\n\t\tSVHL_TouchLinks ( ent, sv.world.areanodes );\r\n}\r\n\r\n\r\n/*\r\n===============================================================================\r\n\r\nPOINT TESTING IN HULLS\r\n\r\n===============================================================================\r\n*/\r\n\r\n/*\r\n==================\r\nSV_PointContents\r\n\r\n==================\r\n*/\r\nint SVHL_PointContents (vec3_t p)\r\n{\r\n\treturn sv.world.worldmodel->funcs.PointContents(sv.world.worldmodel, NULL, p);\r\n}\r\n\r\n//===========================================================================\r\n\r\n/*\r\n============\r\nSV_TestEntityPosition\r\n\r\nA small wrapper around SV_BoxInSolidEntity that never clips against the\r\nsupplied entity.\r\n============\r\n*/\r\nhledict_t\t*SVHL_TestEntityPosition (hledict_t *ent)\r\n{\r\n\ttrace_t\ttrace;\r\n\r\n\ttrace = SVHL_Move (ent->v.origin, ent->v.mins, ent->v.maxs, ent->v.origin, 0, 0, ent);\r\n\t\r\n\tif (trace.startsolid)\r\n\t\treturn &SVHL_Edict[0];\r\n\t\t\r\n\treturn NULL;\r\n}\r\n\r\n/*\r\n==================\r\nSV_ClipMoveToEntity\r\n\r\nHandles selection or creation of a clipping hull, and offseting (and\r\neventually rotation) of the end points\r\n==================\r\n*/\r\ntrace_t SVHL_ClipMoveToEntity (hledict_t *ent, vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, int hullnum, qboolean hitmodel, unsigned int clipmask)\t//hullnum overrides min/max for q1 style bsps\r\n{\r\n\ttrace_t\t\ttrace;\r\n\tmodel_t\t\t*model;\r\n\r\n/*\r\n#ifdef Q2BSPS\r\n\tif (ent->v->solid == SOLID_BSP)\r\n\t\tif (sv.models[(int)ent->v->modelindex] && (sv.models[(int)ent->v->modelindex]->fromgame == fg_quake2 || sv.models[(int)ent->v->modelindex]->fromgame == fg_quake3))\r\n\t\t{\r\n\t\t\ttrace = CM_TransformedBoxTrace (start, end, mins, maxs, sv.models[(int)ent->v->modelindex]->hulls[0].firstclipnode, MASK_PLAYERSOLID, ent->v->origin, ent->v->angles);\r\n\t\t\tif (trace.fraction < 1 || trace.startsolid)\r\n\t\t\t\ttrace.ent = ent;\r\n\t\t\treturn trace;\r\n\t\t}\r\n#endif\r\n*/\r\n\r\n// get the clipping hull\r\n\tif (ent->v.solid == SOLID_BSP)\r\n\t{\r\n\t\tmodel = sv.models[(int)ent->v.modelindex];\r\n\t\tif (!model || (model->type != mod_brush && model->type != mod_heightmap))\r\n\t\t\tSV_Error(\"SOLID_BSP with non bsp model (classname: %s)\", SVHL_Globals.stringbase+ent->v.classname);\r\n\t}\r\n\telse\r\n\t{\r\n\t\tvec3_t boxmins, boxmaxs;\r\n\t\tVectorSubtract (ent->v.mins, maxs, boxmins);\r\n\t\tVectorSubtract (ent->v.maxs, mins, boxmaxs);\r\n\t\tWorld_HullForBox(boxmins, boxmaxs);\r\n\t\tmodel = NULL;\r\n\t}\r\n\r\n// trace a line through the apropriate clipping hull\r\n\tif (ent->v.solid != SOLID_BSP)\r\n\t{\r\n\t\tent->v.angles[0]*=r_meshpitch.value;\t//carmack made bsp models rotate wrongly.\r\n\t\tWorld_TransformedTrace(model, hullnum, ent->v.frame, start, end, mins, maxs, false, &trace, ent->v.origin, ent->v.angles, clipmask);\r\n\t\tent->v.angles[0]*=r_meshpitch.value;\r\n\t}\r\n\telse\r\n\t{\r\n\t\tWorld_TransformedTrace(model, hullnum, ent->v.frame, start, end, mins, maxs, false, &trace, ent->v.origin, ent->v.angles, clipmask);\r\n\t}\r\n\r\n// fix trace up by the offset\r\n\tif (trace.fraction != 1)\r\n\t{\r\n\t\tif (!model && hitmodel && ent->v.solid != SOLID_BSP && ent->v.modelindex > 0)\r\n\t\t{\r\n\t\t\t//okay, we hit the bbox\r\n\r\n\t\t\tmodel_t *model;\r\n\t\t\tif (ent->v.modelindex < 1 || ent->v.modelindex >= MAX_PRECACHE_MODELS)\r\n\t\t\t\tSV_Error(\"SV_ClipMoveToEntity: modelindex out of range\\n\");\r\n\t\t\tmodel = sv.models[ (int)ent->v.modelindex ];\r\n\t\t\tif (!model)\r\n\t\t\t{\t//if the model isn't loaded, load it.\r\n\t\t\t\t//this saves on memory requirements with mods that don't ever use this.\r\n\t\t\t\tmodel = sv.models[(int)ent->v.modelindex] = Mod_ForName(sv.strings.model_precache[(int)ent->v.modelindex], false);\r\n\t\t\t}\r\n\r\n\t\t\tif (model && model->funcs.NativeTrace)\r\n\t\t\t{\r\n\t\t\t\t//do the second trace\r\n\t\t\t\tWorld_TransformedTrace(model, hullnum, ent->v.frame, start, end, mins, maxs, false, &trace, ent->v.origin, ent->v.angles, MASK_WORLDSOLID);\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tif (trace.startsolid)\r\n\t\t{\r\n\t\t\tif (ent != &SVHL_Edict[0])\r\n\t\t\t\tCon_Printf(\"Trace started solid\\n\");\r\n\t\t}\r\n\t}\r\n\r\n// did we clip the move?\r\n\tif (trace.fraction < 1 || trace.startsolid  )\r\n\t\ttrace.ent = ent;\r\n\r\n\treturn trace;\r\n}\r\n\r\n#ifdef Q2BSPS\r\nfloat\t*area_mins, *area_maxs;\r\nhledict_t\t**area_list;\r\nint\t\tarea_count, area_maxcount;\r\nvoid SVHL_AreaEdicts_r (areanode_t *node)\r\n{\r\n\tlink_t\t\t*l, *next, *start;\r\n\thledict_t\t\t*check;\r\n\tint\t\t\tcount;\r\n\r\n\tcount = 0;\r\n\r\n\t// touch linked edicts\r\n\tstart = &node->edicts;\r\n\r\n\tfor (l=start->next  ; l != start ; l = next)\r\n\t{\r\n\t\tnext = l->next;\r\n\t\tcheck = HLEDICT_FROM_AREA(l);\r\n\r\n//\t\tif (check->v.solid == SOLID_NOT)\r\n//\t\t\tcontinue;\t\t// deactivated\r\n\t\tif (check->v.absmin[0] > area_maxs[0]\r\n\t\t|| check->v.absmin[1] > area_maxs[1]\r\n\t\t|| check->v.absmin[2] > area_maxs[2]\r\n\t\t|| check->v.absmax[0] < area_mins[0]\r\n\t\t|| check->v.absmax[1] < area_mins[1]\r\n\t\t|| check->v.absmax[2] < area_mins[2])\r\n\t\t\tcontinue;\t\t// not touching\r\n\r\n\t\tif (area_count == area_maxcount)\r\n\t\t{\r\n\t\t\tCon_Printf (\"SV_AreaEdicts: MAXCOUNT\\n\");\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\tarea_list[area_count] = check;\r\n\t\tarea_count++;\r\n\t}\r\n\t\r\n\tif (node->axis == -1)\r\n\t\treturn;\t\t// terminal node\r\n\r\n\t// recurse down both sides\r\n\tif ( area_maxs[node->axis] > node->dist )\r\n\t\tSVHL_AreaEdicts_r ( node->children[0] );\r\n\tif ( area_mins[node->axis] < node->dist )\r\n\t\tSVHL_AreaEdicts_r ( node->children[1] );\r\n}\r\n\r\n/*\r\n================\r\nSV_AreaEdicts\r\n================\r\n*/\r\nint SVHL_AreaEdicts (vec3_t mins, vec3_t maxs, hledict_t **list, int maxcount)\r\n{\r\n\tarea_mins = mins;\r\n\tarea_maxs = maxs;\r\n\tarea_list = list;\r\n\tarea_count = 0;\r\n\tarea_maxcount = maxcount;\r\n\r\n\tSVHL_AreaEdicts_r (sv.world.areanodes);\r\n\r\n\treturn area_count;\r\n}\r\n\r\n#endif\r\n//===========================================================================\r\n\r\n\r\n/*\r\n====================\r\nSV_ClipToEverything\r\n\r\nlike SV_ClipToLinks, but doesn't use the links part. This can be used for checking triggers, solid entities, not-solid entities.\r\nSounds pointless, I know.\r\n====================\r\n*/\r\nvoid SVHL_ClipToEverything (hlmoveclip_t *clip)\r\n{\r\n\tint e;\r\n\ttrace_t\t\ttrace;\r\n\thledict_t\t\t*touch;\r\n\tfor (e=1 ; e<sv.world.num_edicts ; e++)\r\n\t{\r\n\t\ttouch = &SVHL_Edict[e];\r\n\r\n\t\tif (touch->isfree)\r\n\t\t\tcontinue;                 \r\n\t\tif (touch->v.solid == SOLID_NOT && !((int)touch->v.flags & FL_FINDABLE_NONSOLID))\r\n\t\t\tcontinue;\r\n\t\tif (touch->v.solid == SOLID_TRIGGER && !((int)touch->v.flags & FL_FINDABLE_NONSOLID))\r\n\t\t\tcontinue;\r\n\r\n\t\tif (touch == clip->passedict)\r\n\t\t\tcontinue;\r\n\r\n\t\tif (clip->type & MOVE_NOMONSTERS && touch->v.solid != SOLID_BSP)\r\n\t\t\tcontinue;\r\n\r\n\t\tif (clip->passedict)\r\n\t\t{\r\n\t\t\t// don't clip corpse against character\r\n\t\t\tif (clip->passedict->v.solid == SOLID_CORPSE && (touch->v.solid == SOLID_SLIDEBOX || touch->v.solid == SOLID_CORPSE))\r\n\t\t\t\tcontinue;\r\n\t\t\t// don't clip character against corpse\r\n\t\t\tif (clip->passedict->v.solid == SOLID_SLIDEBOX && touch->v.solid == SOLID_CORPSE)\r\n\t\t\t\tcontinue;\r\n\r\n//\t\t\tif (!((int)clip->passedict->v.dimension_hit & (int)touch->v.dimension_solid))\r\n//\t\t\t\tcontinue;\r\n\t\t}\r\n\r\n\t\tif (clip->boxmins[0] > touch->v.absmax[0]\r\n\t\t\t\t|| clip->boxmins[1] > touch->v.absmax[1]\r\n\t\t\t\t|| clip->boxmins[2] > touch->v.absmax[2]\r\n\t\t\t\t|| clip->boxmaxs[0] < touch->v.absmin[0]\r\n\t\t\t\t|| clip->boxmaxs[1] < touch->v.absmin[1]\r\n\t\t\t\t|| clip->boxmaxs[2] < touch->v.absmin[2] )\r\n\t\t\tcontinue;\r\n\r\n\t\tif (clip->passedict && clip->passedict->v.size[0] && !touch->v.size[0])\r\n\t\t\tcontinue;\t// points never interact\r\n\r\n\t// might intersect, so do an exact clip\r\n\t\tif (clip->trace.allsolid)\r\n\t\t\treturn;\r\n\t\tif (clip->passedict)\r\n\t\t{\r\n\t\t \tif (touch->v.owner == clip->passedict)\r\n\t\t\t\tcontinue;\t// don't clip against own missiles\r\n\t\t\tif (clip->passedict->v.owner == touch)\r\n\t\t\t\tcontinue;\t// don't clip against owner\r\n\t\t}\r\n\r\n\t\tif ((int)touch->v.flags & FL_MONSTER)\r\n\t\t\ttrace = SVHL_ClipMoveToEntity (touch, clip->start, clip->mins2, clip->maxs2, clip->end, clip->hullnum, clip->type & MOVE_HITMODEL, clip->clipmask);\r\n\t\telse\r\n\t\t\ttrace = SVHL_ClipMoveToEntity (touch, clip->start, clip->mins, clip->maxs, clip->end, clip->hullnum, clip->type & MOVE_HITMODEL, clip->clipmask);\r\n\t\tif (trace.allsolid || trace.startsolid ||\r\n\t\t\t\ttrace.fraction < clip->trace.fraction)\r\n\t\t{\r\n\t\t\ttrace.ent = touch;\r\n\t\t\tclip->trace = trace;\r\n\t\t}\r\n\t}\r\n}\r\n\r\n/*\r\n====================\r\nSV_ClipToLinks\r\n\r\nMins and maxs enclose the entire area swept by the move\r\n====================\r\n*/\r\nvoid SVHL_ClipToLinks ( areanode_t *node, hlmoveclip_t *clip )\r\n{\r\n\tlink_t\t\t*l, *next;\r\n\thledict_t\t\t*touch;\r\n\ttrace_t\t\ttrace;\r\n\r\n// touch linked edicts\r\n\tfor (l = node->edicts.next ; l != &node->edicts ; l = next)\r\n\t{\r\n\t\tnext = l->next;\r\n\t\ttouch = HLEDICT_FROM_AREA(l);\r\n\t\tif (touch->v.solid == SOLID_NOT)\r\n\t\t\tcontinue;\r\n\t\tif (touch == clip->passedict)\r\n\t\t\tcontinue;\r\n\t\tif (touch->v.solid == SOLID_TRIGGER)\r\n\t\t\tcontinue;\r\n\r\n\t\tif (clip->type & MOVE_NOMONSTERS && touch->v.solid != SOLID_BSP)\r\n\t\t\tcontinue;\r\n\r\n\t\tif (clip->passedict)\r\n\t\t{\r\n\t\t\t// don't clip corpse against character\r\n\t\t\tif (clip->passedict->v.solid == SOLID_CORPSE && (touch->v.solid == SOLID_SLIDEBOX || touch->v.solid == SOLID_CORPSE))\r\n\t\t\t\tcontinue;\r\n\t\t\t// don't clip character against corpse\r\n\t\t\tif (clip->passedict->v.solid == SOLID_SLIDEBOX && touch->v.solid == SOLID_CORPSE)\r\n\t\t\t\tcontinue;\r\n\r\n//\t\t\tif (!((int)clip->passedict->xv->dimension_hit & (int)touch->xv->dimension_solid))\r\n//\t\t\t\tcontinue;\r\n\t\t}\r\n\r\n\t\tif (clip->boxmins[0] > touch->v.absmax[0]\r\n\t\t|| clip->boxmins[1] > touch->v.absmax[1]\r\n\t\t|| clip->boxmins[2] > touch->v.absmax[2]\r\n\t\t|| clip->boxmaxs[0] < touch->v.absmin[0]\r\n\t\t|| clip->boxmaxs[1] < touch->v.absmin[1]\r\n\t\t|| clip->boxmaxs[2] < touch->v.absmin[2] )\r\n\t\t\tcontinue;\r\n\r\n\t\tif (clip->passedict && clip->passedict->v.size[0] && !touch->v.size[0])\r\n\t\t\tcontinue;\t// points never interact\r\n\r\n\t// might intersect, so do an exact clip\r\n\t\tif (clip->trace.allsolid)\r\n\t\t\treturn;\r\n\t\tif (clip->passedict)\r\n\t\t{\r\n\t\t \tif (touch->v.owner == clip->passedict)\r\n\t\t\t\tcontinue;\t// don't clip against own missiles\r\n\t\t\tif (clip->passedict->v.owner == touch)\r\n\t\t\t\tcontinue;\t// don't clip against owner\r\n\t\t}\r\n\r\n\t\tif ((int)touch->v.flags & FL_MONSTER)\r\n\t\t\ttrace = SVHL_ClipMoveToEntity (touch, clip->start, clip->mins2, clip->maxs2, clip->end, clip->hullnum, clip->type & MOVE_HITMODEL, clip->clipmask);\r\n\t\telse\r\n\t\t\ttrace = SVHL_ClipMoveToEntity (touch, clip->start, clip->mins, clip->maxs, clip->end, clip->hullnum, clip->type & MOVE_HITMODEL, clip->clipmask);\r\n\t\tif (trace.allsolid || trace.startsolid ||\r\n\t\ttrace.fraction < clip->trace.fraction)\r\n\t\t{\r\n\t\t\ttrace.ent = touch;\r\n\t\t\tclip->trace = trace;\r\n\t\t}\r\n\t}\r\n\t\r\n// recurse down both sides\r\n\tif (node->axis == -1)\r\n\t\treturn;\r\n\r\n\tif ( clip->boxmaxs[node->axis] > node->dist )\r\n\t\tSVHL_ClipToLinks ( node->children[0], clip );\r\n\tif ( clip->boxmins[node->axis] < node->dist )\r\n\t\tSVHL_ClipToLinks ( node->children[1], clip );\r\n}\r\n\r\nstatic void SVHL_MoveBounds (vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, vec3_t boxmins, vec3_t boxmaxs)\r\n{\r\n#if 0\r\n// debug to test against everything\r\nboxmins[0] = boxmins[1] = boxmins[2] = -9999;\r\nboxmaxs[0] = boxmaxs[1] = boxmaxs[2] = 9999;\r\n#else\r\n\tint\t\ti;\r\n\t\r\n\tfor (i=0 ; i<3 ; i++)\r\n\t{\r\n\t\tif (end[i] > start[i])\r\n\t\t{\r\n\t\t\tboxmins[i] = start[i] + mins[i] - 1;\r\n\t\t\tboxmaxs[i] = end[i] + maxs[i] + 1;\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tboxmins[i] = end[i] + mins[i] - 1;\r\n\t\t\tboxmaxs[i] = start[i] + maxs[i] + 1;\r\n\t\t}\r\n\t}\r\n#endif\r\n}\r\n\r\n/*\r\n==================\r\nSV_Move\r\n==================\r\n*/\r\ntrace_t SVHL_Move (vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, int type, int forcehull, hledict_t *passedict)\r\n{\r\n\thlmoveclip_t\tclip;\r\n\tint\t\t\ti;\r\n\tint hullnum;\r\n\r\n\tmemset ( &clip, 0, sizeof ( clip ) );\r\n\tif (forcehull)\r\n\t\thullnum = forcehull;\r\n\telse if (sv_compatiblehulls.value)\r\n\t\thullnum = 0;\r\n\telse\r\n\t{\r\n\t\tint diff;\r\n\t\tint best;\r\n\t\thullnum = 0;\r\n\t\tbest = 8192;\r\n\t\t//x/y pos/neg are assumed to be the same magnitute.\r\n\t\t//z pos/height are assumed to be different from all the others.\r\n\t\tfor (i = 0; i < MAX_MAP_HULLSM; i++)\r\n\t\t{\r\n\t\t\tif (!sv.world.worldmodel->hulls[i].available)\r\n\t\t\t\tcontinue;\r\n#define sq(x) ((x)*(x))\r\n\t\t\tdiff = sq(sv.world.worldmodel->hulls[i].clip_maxs[2] - maxs[2]) +\r\n\t\t\t\tsq(sv.world.worldmodel->hulls[i].clip_mins[2] - mins[2]) +\r\n\t\t\t\tsq(sv.world.worldmodel->hulls[i].clip_maxs[1] - maxs[1]) +\r\n\t\t\t\tsq(sv.world.worldmodel->hulls[i].clip_mins[0] - mins[0]);\r\n\t\t\tif (diff < best)\r\n\t\t\t{\r\n\t\t\t\tbest = diff;\r\n\t\t\t\thullnum=i;\r\n\t\t\t}\r\n\t\t}\r\n\t\thullnum++;\r\n\t}\r\n\r\n\tif (type & MOVE_NOMONSTERS)\r\n\t\tclip.clipmask = MASK_WORLDSOLID; /*solid only to world*/\r\n\telse if (maxs[0] - mins[0])\r\n\t\tclip.clipmask = MASK_BOXSOLID;\t/*impacts playerclip*/\r\n\telse\r\n\t\tclip.clipmask = MASK_POINTSOLID;\t\t/*ignores playerclip but hits everything else*/\r\n\r\n// clip to world\r\n\tclip.trace = SVHL_ClipMoveToEntity ( &SVHL_Edict[0], start, mins, maxs, end, hullnum, false, clip.clipmask);\r\n\r\n\tclip.start = start;\r\n\tclip.end = end;\r\n\tclip.mins = mins;\r\n\tclip.maxs = maxs;\r\n\tclip.type = type;\r\n\tclip.passedict = passedict;\r\n\tclip.hullnum = hullnum;\r\n\r\n\tif (type & MOVE_MISSILE)\r\n\t{\r\n\t\tfor (i=0 ; i<3 ; i++)\r\n\t\t{\r\n\t\t\tclip.mins2[i] = -15;\r\n\t\t\tclip.maxs2[i] = 15;\r\n\t\t}\r\n\t}\r\n\telse\r\n\t{\r\n\t\tVectorCopy (mins, clip.mins2);\r\n\t\tVectorCopy (maxs, clip.maxs2);\r\n\t}\r\n\t\r\n// create the bounding box of the entire move\r\n\tSVHL_MoveBounds ( start, clip.mins2, clip.maxs2, end, clip.boxmins, clip.boxmaxs );\r\n\r\n// clip to entities\r\n\tif (clip.type & MOVE_EVERYTHING)\r\n\t\tSVHL_ClipToEverything (&clip);\r\n\telse\r\n\t\tSVHL_ClipToLinks ( sv.world.areanodes, &clip );\r\n\r\n\tif (clip.trace.startsolid)\r\n\t\tclip.trace.fraction = 0;\r\n\r\n\tif (!clip.trace.ent)\r\n\t\treturn clip.trace;\r\n\r\n\treturn clip.trace;\r\n}\r\n\r\n#endif\r\n"
  },
  {
    "path": "engine/server/svq2_ents.c",
    "content": "#include \"quakedef.h\"\n\n#ifndef CLIENTONLY\n\n#define Q2EDICT_NUM(i) (q2edict_t*)((char *)ge->edicts+i*ge->edict_size)\n\n#ifndef Q2SERVER\nvoid SV_WriteFrameToClient (client_t *client, sizebuf_t *msg)\n{\n}\nvoid SV_BuildClientFrame (client_t *client)\n{\n}\n#else\n\nq2entity_state_t *svs_client_entities;//[Q2UPDATE_BACKUP*MAX_PACKET_ENTITIES];\nint svs_num_client_entities;\nint svs_next_client_entities;\n\nq2entity_state_t\tsv_baselines[Q2MAX_EDICTS];\n\n/*\n=============================================================================\n\nEncode a client frame onto the network channel\n\n=============================================================================\n*/\n\nstatic void MSG_WriteSizeQ2E(sizebuf_t *sb, int solid)\n{\t//urgh...\n\tif (solid != ES_SOLID_BSP && solid != ES_SOLID_NOT)\n\t\tsolid = ((solid & 255)<<0)\t//recode fte's sizes to q2ex's...\n\t\t      | ((solid & 255)<<8)\n\t\t      | (((solid>>8) & 255)<<16)\n\t\t      | ((((solid>>16) & 65535) - 32768+32)<<24);\n\tMSG_WriteLong(sb, solid);\n}\n/*\n==================\nMSG_WriteDeltaEntity\n\nWrites part of a packetentities message.\nCan delta from either a baseline or a previous packet_entity\n==================\n*/\nvoid MSGQ2_WriteDeltaEntity (q2entity_state_t *from, q2entity_state_t *to, sizebuf_t *msg, qboolean force, qboolean newentity, qboolean q2ex)\n{\n\tquint64_t\t\tbits;\n\n\tif (!to->number)\n\t\tSys_Error (\"Unset entity number\");\n\tif (to->number >= Q2MAX_EDICTS)\n\t\tSys_Error (\"Entity number >= MAX_EDICTS\");\n\n// send an update\n\tbits = 0;\n\n\tif (to->number >= 256)\n\t\tbits |= Q2U_NUMBER16;\t\t// number8 is implicit otherwise\n\n\tif (to->origin[0] != from->origin[0])\n\t\tbits |= Q2U_ORIGIN1;\n\tif (to->origin[1] != from->origin[1])\n\t\tbits |= Q2U_ORIGIN2;\n\tif (to->origin[2] != from->origin[2])\n\t\tbits |= Q2U_ORIGIN3;\n\n\tif ( to->angles[0] != from->angles[0] )\n\t\tbits |= Q2U_ANGLE1;\t\t\n\tif ( to->angles[1] != from->angles[1] )\n\t\tbits |= Q2U_ANGLE2;\n\tif ( to->angles[2] != from->angles[2] )\n\t\tbits |= Q2U_ANGLE3;\n\t\t\n\tif ( to->skinnum != from->skinnum )\n\t{\n\t\tif ((unsigned)to->skinnum < 256)\n\t\t\tbits |= Q2U_SKIN8;\n\t\telse if ((unsigned)to->skinnum < 0x10000)\n\t\t\tbits |= Q2U_SKIN16;\n\t\telse\n\t\t\tbits |= (Q2U_SKIN8|Q2U_SKIN16);\n\t}\n\t\t\n\tif ( to->frame != from->frame )\n\t{\n\t\tif (to->frame < 256)\n\t\t\tbits |= Q2U_FRAME8;\n\t\telse\n\t\t\tbits |= Q2U_FRAME16;\n\t}\n\n\tif ( to->effects != from->effects )\n\t{\n\t\tif ((uint64_t)to->effects > 0xffffffffu)\n\t\t{\t//this encoding is weird, Q2UEX_EFFECTS64 without any of the other flags is equivelent to the 8|16==32bit flags, so is pointless without the extra ones.\n\t\t\tif (to->effects < (uint64_t)256<<32)\n\t\t\t\tbits |= Q2U_EFFECTS8|Q2UEX_EFFECTS64;\n\t\t\telse if (to->effects < (uint64_t)0x8000<<32)\n\t\t\t\tbits |= Q2U_EFFECTS16|Q2UEX_EFFECTS64;\n\t\t\telse\n\t\t\t\tbits |= Q2U_EFFECTS8|Q2U_EFFECTS16|Q2UEX_EFFECTS64;\n\t\t}\n\t\telse if (to->effects < 256)\n\t\t\tbits |= Q2U_EFFECTS8;\n\t\telse if (to->effects < 0x8000)\n\t\t\tbits |= Q2U_EFFECTS16;\n\t\telse\n\t\t\tbits |= Q2U_EFFECTS8|Q2U_EFFECTS16;\n\t}\n\t\n\tif ( to->renderfx != from->renderfx )\n\t{\n\t\tif (to->renderfx < 256)\n\t\t\tbits |= Q2U_RENDERFX8;\n\t\telse if (to->renderfx < 0x8000)\n\t\t\tbits |= Q2U_RENDERFX16;\n\t\telse\n\t\t\tbits |= Q2U_RENDERFX8|Q2U_RENDERFX16;\n\t}\n\t\n\tif ( to->solid != from->solid )\n\t\tbits |= Q2U_SOLID;\n\n\t// event is not delta compressed, just 0 compressed\n\tif ( to->event  )\n\t\tbits |= Q2U_EVENT;\n\t\n\tif ( to->modelindex != from->modelindex )\n\t{\n\t\tbits |= Q2U_MODEL;\n\t\tif (to->modelindex > 0xff)\n\t\t\tbits |= Q2UX_INDEX16;\n\t}\n\tif ( to->modelindex2 != from->modelindex2 )\n\t{\n\t\tbits |= Q2U_MODEL2;\n\t\tif (to->modelindex2 > 0xff)\n\t\t\tbits |= Q2UX_INDEX16;\n\t}\n\tif ( to->modelindex3 != from->modelindex3 )\n\t{\n\t\tbits |= Q2U_MODEL3;\n\t\tif (to->modelindex3 > 0xff)\n\t\t\tbits |= Q2UX_INDEX16;\n\t}\n\tif ( to->modelindex4 != from->modelindex4 )\n\t{\n\t\tbits |= Q2U_MODEL4;\n\t\tif (to->modelindex4 > 0xff)\n\t\t\tbits |= Q2UX_INDEX16;\n\t}\n\n\tif ( to->sound != from->sound /*||vol  or attn*/)\n\t{\n\t\tbits |= Q2U_SOUND;\n\t\tif (to->sound > 0xff && !q2ex)\n\t\t\tbits |= Q2UX_INDEX16;\n\t}\n\n\n//\tif (to->alpha != from->alpha) bits |= Q2UEX_ALPHA;\n//\tif (to->scale != from->scale)bits |= Q2UEX_SCALE;\n//\tif (to->instance != from->instance) bits |= Q2UEX_INSTANCE;\n//\tif (to->owner != from->owner) bits |= Q2UEX_OWNER;\n//\tif (to->oldframe != from->oldframe) bits |= Q2UEX_OLDFRAME;\n\n\tif (newentity || (to->renderfx & Q2RF_BEAM))\n\t\tbits |= Q2U_OLDORIGIN;\n\n\t//\n\t// write the message\n\t//\n\tif (!bits && !force)\n\t\treturn;\t\t// nothing to send!\n\n\t//----------\n\n\tif (bits & 0xff00000000)\n\t\tbits |= Q2UEX_MOREBITS4 | Q2U_MOREBITS3 | Q2U_MOREBITS2 | Q2U_MOREBITS1;\n\telse if (bits & 0xff000000)\n\t\tbits |= Q2U_MOREBITS3 | Q2U_MOREBITS2 | Q2U_MOREBITS1;\n\telse if (bits & 0x00ff0000)\n\t\tbits |= Q2U_MOREBITS2 | Q2U_MOREBITS1;\n\telse if (bits & 0x0000ff00)\n\t\tbits |= Q2U_MOREBITS1;\n\n\tMSG_WriteByte (msg,\tbits&255 );\n\n\tif (bits & 0xff00000000)\n\t{\n\t\tMSG_WriteByte (msg,\t(bits>>8)&255 );\n\t\tMSG_WriteByte (msg,\t(bits>>16)&255 );\n\t\tMSG_WriteByte (msg,\t(bits>>24)&255 );\n\t\tMSG_WriteByte (msg,\t(bits>>32)&255 );\n\t}\n\telse if (bits & 0xff000000)\n\t{\n\t\tMSG_WriteByte (msg,\t(bits>>8)&255 );\n\t\tMSG_WriteByte (msg,\t(bits>>16)&255 );\n\t\tMSG_WriteByte (msg,\t(bits>>24)&255 );\n\t}\n\telse if (bits & 0x00ff0000)\n\t{\n\t\tMSG_WriteByte (msg,\t(bits>>8)&255 );\n\t\tMSG_WriteByte (msg,\t(bits>>16)&255 );\n\t}\n\telse if (bits & 0x0000ff00)\n\t{\n\t\tMSG_WriteByte (msg,\t(bits>>8)&255 );\n\t}\n\n\t//----------\n\n\tif (bits & Q2U_NUMBER16)\n\t\tMSG_WriteShort (msg, to->number);\n\telse\n\t\tMSG_WriteByte (msg,\tto->number);\n\n\tif (bits & Q2U_MODEL)\n\t{\n\t\tif (bits & Q2UX_INDEX16)\n\t\t\tMSG_WriteShort\t(msg,\tto->modelindex);\n\t\telse\n\t\t\tMSG_WriteByte\t(msg,\tto->modelindex);\n\t}\n\tif (bits & Q2U_MODEL2)\n\t{\n\t\tif (bits & Q2UX_INDEX16)\n\t\t\tMSG_WriteShort\t(msg,\tto->modelindex2);\n\t\telse\n\t\t\tMSG_WriteByte\t(msg,\tto->modelindex2);\n\t}\n\tif (bits & Q2U_MODEL3)\n\t{\n\t\tif (bits & Q2UX_INDEX16)\n\t\t\tMSG_WriteShort\t(msg,\tto->modelindex3);\n\t\telse\n\t\t\tMSG_WriteByte\t(msg,\tto->modelindex3);\n\t}\n\tif (bits & Q2U_MODEL4)\n\t{\n\t\tif (bits & Q2UX_INDEX16)\n\t\t\tMSG_WriteShort\t(msg,\tto->modelindex4);\n\t\telse\n\t\t\tMSG_WriteByte\t(msg,\tto->modelindex4);\n\t}\n\n\tif (bits & Q2U_FRAME8)\n\t\tMSG_WriteByte (msg, to->frame);\n\tif (bits & Q2U_FRAME16)\n\t\tMSG_WriteShort (msg, to->frame);\n\n\tif ((bits & Q2U_SKIN8) && (bits & Q2U_SKIN16))\t\t//used for laser colors\n\t\tMSG_WriteLong (msg, to->skinnum);\n\telse if (bits & Q2U_SKIN8)\n\t\tMSG_WriteByte (msg, to->skinnum);\n\telse if (bits & Q2U_SKIN16)\n\t\tMSG_WriteShort (msg, to->skinnum);\n\n\tif (bits & Q2UEX_EFFECTS64)\n\t{\n\t\tMSG_WriteLong (msg, to->effects);\n\t\tif ( (bits & (Q2U_EFFECTS8|Q2U_EFFECTS16)) == (Q2U_EFFECTS8|Q2U_EFFECTS16) )\n\t\t\tMSG_WriteLong (msg, (quint64_t)to->effects>>32);\n\t\telse if (bits & Q2U_EFFECTS8)\n\t\t\tMSG_WriteByte (msg, (quint64_t)to->effects>>32);\n\t\telse if (bits & Q2U_EFFECTS16)\n\t\t\tMSG_WriteShort (msg, (quint64_t)to->effects>>32);\n\t}\n\telse\n\t{\n\t\tif ( (bits & (Q2U_EFFECTS8|Q2U_EFFECTS16)) == (Q2U_EFFECTS8|Q2U_EFFECTS16) )\n\t\t\tMSG_WriteLong (msg, to->effects);\n\t\telse if (bits & Q2U_EFFECTS8)\n\t\t\tMSG_WriteByte (msg, to->effects);\n\t\telse if (bits & Q2U_EFFECTS16)\n\t\t\tMSG_WriteShort (msg, to->effects);\n\t}\n\n\tif ( (bits & (Q2U_RENDERFX8|Q2U_RENDERFX16)) == (Q2U_RENDERFX8|Q2U_RENDERFX16) )\n\t\tMSG_WriteLong (msg, to->renderfx);\n\telse if (bits & Q2U_RENDERFX8)\n\t\tMSG_WriteByte (msg, to->renderfx);\n\telse if (bits & Q2U_RENDERFX16)\n\t\tMSG_WriteShort (msg, to->renderfx);\n\n\tif (q2ex)\n\t{\n\t\tif (bits & Q2U_SOLID)\n\t\t\tMSG_WriteSizeQ2E(msg, to->solid);\n\n\t\tif (!to->solid)\n\t\t{\t//demos reportedly compress these... not that it makes a difference.\n\t\t\tif (bits & Q2U_ORIGIN1)\n\t\t\t\tMSG_WriteCoord (msg, to->origin[0]);\n\t\t\tif (bits & Q2U_ORIGIN2)\n\t\t\t\tMSG_WriteCoord (msg, to->origin[1]);\n\t\t\tif (bits & Q2U_ORIGIN3)\n\t\t\t\tMSG_WriteCoord (msg, to->origin[2]);\n\n\t\t\tif (bits & Q2U_OLDORIGIN)\n\t\t\t{\n\t\t\t\tMSG_WriteCoord (msg, to->old_origin[0]);\n\t\t\t\tMSG_WriteCoord (msg, to->old_origin[1]);\n\t\t\t\tMSG_WriteCoord (msg, to->old_origin[2]);\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (bits & Q2U_ORIGIN1)\n\t\t\t\tMSG_WriteFloat (msg, to->origin[0]);\n\t\t\tif (bits & Q2U_ORIGIN2)\n\t\t\t\tMSG_WriteFloat (msg, to->origin[1]);\n\t\t\tif (bits & Q2U_ORIGIN3)\n\t\t\t\tMSG_WriteFloat (msg, to->origin[2]);\n\n\t\t\tif (bits & Q2U_OLDORIGIN)\n\t\t\t{\n\t\t\t\tMSG_WriteFloat (msg, to->old_origin[0]);\n\t\t\t\tMSG_WriteFloat (msg, to->old_origin[1]);\n\t\t\t\tMSG_WriteFloat (msg, to->old_origin[2]);\n\t\t\t}\n\t\t}\n\n\t\tif (bits & Q2U_ANGLE1)\n\t\t\tMSG_WriteFloat(msg, to->angles[0]);\t//blatent overkill.\n\t\tif (bits & Q2U_ANGLE2)\n\t\t\tMSG_WriteFloat(msg, to->angles[1]);\n\t\tif (bits & Q2U_ANGLE3)\n\t\t\tMSG_WriteFloat(msg, to->angles[2]);\n\t}\n\telse\n\t{\n\t\tif (bits & Q2U_ORIGIN1)\n\t\t\tMSG_WriteCoord (msg, to->origin[0]);\n\t\tif (bits & Q2U_ORIGIN2)\n\t\t\tMSG_WriteCoord (msg, to->origin[1]);\n\t\tif (bits & Q2U_ORIGIN3)\n\t\t\tMSG_WriteCoord (msg, to->origin[2]);\n\n\t\tif (bits & Q2U_ANGLE1)\n\t\t\tMSG_WriteAngle(msg, to->angles[0]);\n\t\tif (bits & Q2U_ANGLE2)\n\t\t\tMSG_WriteAngle(msg, to->angles[1]);\n\t\tif (bits & Q2U_ANGLE3)\n\t\t\tMSG_WriteAngle(msg, to->angles[2]);\n\n\t\tif (bits & Q2U_OLDORIGIN)\n\t\t{\n\t\t\tMSG_WriteCoord (msg, to->old_origin[0]);\n\t\t\tMSG_WriteCoord (msg, to->old_origin[1]);\n\t\t\tMSG_WriteCoord (msg, to->old_origin[2]);\n\t\t}\n\t}\n\n\tif (bits & Q2U_SOUND)\n\t{\n\t\tif (q2ex)\n\t\t{\n#if 1\n\t\t\tMSG_WriteShort\t(msg,\tto->sound&0x3fff);\n#else\n\t\t\tMSG_WriteShort\t(msg,\tto->sound&0x3fff | ((to->soundvol!=1)?0x4000:0))  | ((to->soundattn!=3)?0x8000:0));\n\t\t\tMSG_WriteByte\t(msg,\tto->soundvol*255);\n\t\t\tMSG_WriteByte\t(msg,\tto->soundattn);\t//this is normally a /64, but oh well...\n#endif\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (bits & Q2UX_INDEX16)\n\t\t\t\tMSG_WriteShort\t(msg,\tto->sound);\n\t\t\telse\n\t\t\t\tMSG_WriteByte\t(msg,\tto->sound);\n\t\t}\n\t}\n\n\tif (bits & Q2U_EVENT)\n\t\tMSG_WriteByte (msg, to->event);\n\n\tif (!q2ex)\n\t\tif (bits & Q2U_SOLID)\n\t\t{\n\t\t\tif (msg->prim.flags & NPQ2_SOLID32)\n\t\t\t\tMSG_WriteLong(msg, to->solid);\n\t\t\telse\n\t\t\t\tMSG_WriteSize16(msg, to->solid);\n\t\t}\n\n\tif (bits & Q2UEX_ALPHA)\t\tMSG_WriteByte(msg, 0);\n\tif (bits & Q2UEX_SCALE)\t\tMSG_WriteByte(msg, 0);\n\tif (bits & Q2UEX_INSTANCE)\tMSG_WriteByte(msg, 0);\n\tif (bits & Q2UEX_OWNER)\t\tMSG_WriteShort(msg, 0);\n\tif (bits & Q2UEX_OLDFRAME)\tMSG_WriteShort(msg, 0);\n}\n\n\n/*\n=============\nSV_EmitPacketEntities\n\nWrites a delta update of an entity_state_t list to the message.\n=============\n*/\nvoid SVQ2_EmitPacketEntities (q2client_frame_t *from, q2client_frame_t *to, sizebuf_t *msg, qboolean q2ex)\n{\n\tq2entity_state_t\t*oldent, *newent;\n\tint\t\toldindex, newindex;\n\tint\t\toldnum, newnum;\n\tint\t\tfrom_num_entities;\n\tint\t\tbits;\n\n\tMSG_WriteByte (msg, svcq2_packetentities);\n\n\tif (!from)\n\t\tfrom_num_entities = 0;\n\telse\n\t\tfrom_num_entities = from->num_entities;\n\n\tnewindex = 0;\n\toldindex = 0;\n\twhile (newindex < to->num_entities || oldindex < from_num_entities)\n\t{\n\t\tif (newindex >= to->num_entities)\n\t\t{\n\t\t\tnewent = NULL;\t//shh compiler, shh...\n\t\t\tnewnum = 9999;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tnewent = &svs_client_entities[(to->first_entity+newindex)%svs_num_client_entities];\n\t\t\tnewnum = newent->number;\n\t\t}\n\n\t\tif (oldindex >= from_num_entities)\n\t\t{\n\t\t\toldent = NULL;\t//shh compiler, shh...\n\t\t\toldnum = 9999;\n\t\t}\n\t\telse\n\t\t{\n\t\t\toldent = &svs_client_entities[(from->first_entity+oldindex)%svs_num_client_entities];\n\t\t\toldnum = oldent->number;\n\t\t}\n\n\t\tif (newnum == oldnum)\n\t\t{\t// delta update from old position\n\t\t\t// because the force parm is false, this will not result\n\t\t\t// in any bytes being emited if the entity has not changed at all\n\t\t\t// note that players are always 'newentities', this updates their oldorigin always\n\t\t\t// and prevents warping\n\t\t\tif (msg->cursize+128 > msg->maxsize)\n\t\t\t\tmemcpy(newent, oldent, sizeof(*newent));\t//too much data, so set the ent up as the same as the old, so it's sent next frame\n\t\t\telse\n\t\t\t\tMSGQ2_WriteDeltaEntity (oldent, newent, msg, false, newent->number <= svs.allocated_client_slots, q2ex);\n\t\t\toldindex++;\n\t\t\tnewindex++;\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (newnum < oldnum)\n\t\t{\t// this is a new entity, send it from the baseline\n\n\t\t\tif (msg->cursize+128 > msg->maxsize)\n\t\t\t{\t//might cause the packet to overflow\n\t\t\t\t//so strip out this ent, we can add it next frame if it's still relevent\n\t\t\t\tto->num_entities--;\n\t\t\t\tmemmove(newent, newent+1, sizeof(*newent) * (to->num_entities-newindex));\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tMSGQ2_WriteDeltaEntity (&sv_baselines[newnum], newent, msg, true, true, q2ex);\n\t\t\t\tnewindex++;\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (newnum > oldnum)\n\t\t{\t// the old entity isn't present in the new message\n\t\t\tbits = Q2U_REMOVE;\n\t\t\tif (oldnum >= 256)\n\t\t\t\tbits |= Q2U_NUMBER16 | Q2U_MOREBITS1;\n\n\t\t\tMSG_WriteByte (msg,\tbits&255 );\n\t\t\tif (bits & 0x0000ff00)\n\t\t\t\tMSG_WriteByte (msg,\t(bits>>8)&255 );\n\n\t\t\tif (bits & Q2U_NUMBER16)\n\t\t\t\tMSG_WriteShort (msg, oldnum);\n\t\t\telse\n\t\t\t\tMSG_WriteByte (msg, oldnum);\n\n\t\t\toldindex++;\n\t\t\tcontinue;\n\t\t}\n\t}\n\n\tMSG_WriteShort (msg, 0);\t// end of packetentities\n\n#if 0\n\tif (numprojs)\n\t\tSV_EmitProjectileUpdate(msg);\n#endif\n}\n\n\n\n/*\n=============\nSV_WritePlayerstateToClient\n\n=============\n*/\nvoid SVQ2_WritePlayerstateToClient (client_t *client, int seat, int extflags, q2client_frame_t *from, q2client_frame_t *to, sizebuf_t *msg)\n{\n\tint\t\t\t\ti;\n\tint\t\t\t\tpflags;\n\tq2player_state_t\t*ps, *ops;\n\tq2player_state_t\tdummy;\n\tint\t\t\t\tstatbits;\n\tunsigned int pext = client->fteprotocolextensions;\n\tunsigned int q2e = client->protocol == SCP_QUAKE2EX;\n\n\n\tps = &to->ps[seat];\n\tif (!from)\n\t{\n\t\tmemset (&dummy, 0, sizeof(dummy));\n\t\tops = &dummy;\n\t}\n\telse\n\t\tops = &from->ps[seat];\n\n\t//\n\t// determine what needs to be sent\n\t//\n\tpflags = 0;\n\n\tif (pext & PEXT_SPLITSCREEN)\n\t\tif (!from || from->clientnum[seat] != to->clientnum[seat])\n\t\t\tpflags |= Q2FTEPS_CLIENTNUM;\n\n\tif (ps->pmove.pm_type != ops->pmove.pm_type)\n\t\tpflags |= Q2PS_M_TYPE;\n\n\tif (ps->pmove.origin[0] != ops->pmove.origin[0]\n\t\t|| ps->pmove.origin[1] != ops->pmove.origin[1]\n\t\t|| ps->pmove.origin[2] != ops->pmove.origin[2] )\n\t\tpflags |= Q2PS_M_ORIGIN;\n\n\tif (ps->pmove.velocity[0] != ops->pmove.velocity[0]\n\t\t|| ps->pmove.velocity[1] != ops->pmove.velocity[1]\n\t\t|| ps->pmove.velocity[2] != ops->pmove.velocity[2] )\n\t\tpflags |= Q2PS_M_VELOCITY;\n\n\tif (ps->pmove.pm_time != ops->pmove.pm_time)\n\t\tpflags |= Q2PS_M_TIME;\n\n\tif (ps->pmove.pm_flags != ops->pmove.pm_flags)\n\t\tpflags |= Q2PS_M_FLAGS;\n\n\tif (ps->pmove.gravity != ops->pmove.gravity)\n\t\tpflags |= Q2PS_M_GRAVITY;\n\n\tif (ps->pmove.delta_angles[0] != ops->pmove.delta_angles[0]\n\t\t|| ps->pmove.delta_angles[1] != ops->pmove.delta_angles[1]\n\t\t|| ps->pmove.delta_angles[2] != ops->pmove.delta_angles[2] )\n\t\tpflags |= Q2PS_M_DELTA_ANGLES;\n\n\n\tif (ps->viewoffset[0] != ops->viewoffset[0]\n\t\t|| ps->viewoffset[1] != ops->viewoffset[1]\n\t\t|| ps->viewoffset[2] != ops->viewoffset[2] )\n\t\tpflags |= Q2PS_VIEWOFFSET;\n\n\tif (ps->viewangles[0] != ops->viewangles[0]\n\t\t|| ps->viewangles[1] != ops->viewangles[1]\n\t\t|| ps->viewangles[2] != ops->viewangles[2] )\n\t\tpflags |= Q2PS_VIEWANGLES;\n\n\tif (ps->kick_angles[0] != ops->kick_angles[0]\n\t\t|| ps->kick_angles[1] != ops->kick_angles[1]\n\t\t|| ps->kick_angles[2] != ops->kick_angles[2] )\n\t\tpflags |= Q2PS_KICKANGLES;\n\n\tif (ps->blend[0] != ops->blend[0]\n\t\t|| ps->blend[1] != ops->blend[1]\n\t\t|| ps->blend[2] != ops->blend[2]\n\t\t|| ps->blend[3] != ops->blend[3] )\n\t\tpflags |= Q2PS_BLEND;\n\n\tif (ps->fov != ops->fov)\n\t\tpflags |= Q2PS_FOV;\n\n\tif (ps->rdflags != ops->rdflags)\n\t\tpflags |= Q2PS_RDFLAGS;\n\n\tif (ps->gunframe != ops->gunframe)\n\t\tpflags |= Q2PS_WEAPONFRAME;\n\n\tif (ps->gunindex != ops->gunindex)\n\t\tpflags |= Q2PS_WEAPONINDEX;\n\n\tif (pext & PEXT_MODELDBL)\n\t{\n\t\tif ((pflags & Q2PS_WEAPONINDEX) && ps->gunindex > 0xff)\n\t\t\tpflags |= Q2FTEPS_INDEX16;\n\t\tif ((pflags & Q2PS_WEAPONFRAME) && ps->gunframe > 0xff)\n\t\t\tpflags |= Q2FTEPS_INDEX16;\n\t}\n\n\tif (pflags > 0xffff)\n\t\tpflags |= Q2PS_EXTRABITS;\n\n\t//\n\t// write it\n\t//\n\tMSG_WriteByte (msg, svcq2_playerinfo);\n\tMSG_WriteShort (msg, pflags&0xffff);\n\tif (pflags & Q2PS_EXTRABITS)\n\t{\n\t\tif (q2e)\n\t\t\tMSG_WriteShort (msg, pflags>>16);\n\t\telse\n\t\t\tMSG_WriteByte (msg, pflags>>16);\n\t}\n\n\t//\n\t// write the pmove_state_t\n\t//\n\tif (pflags & Q2PS_M_TYPE)\n\t{\n\t\ti = ps->pmove.pm_type;\n\t\tif (q2e)\n\t\t{\t//sigh... q2e added some extra pmove types that we don't support, and not on the end. :(\n\t\t\tswitch((q2pmtype_t)i)\n\t\t\t{\n\t\t\tcase Q2PM_NORMAL:\t\ti = Q2EPM_NORMAL;\t\tbreak;\n\t\t\t//case Q2PM_GRAPPLE:\ti = Q2EPM_GRAPPLE;\t\tbreak;\n\t\t\tcase Q2PM_SPECTATOR:\ti = Q2EPM_SPECTATOR;\tbreak;\n\t\t\t//case Q2PM_SPECTATOR2:\ti = Q2EPM_SPECTATOR2;\tbreak;\n\t\t\tcase Q2PM_DEAD:\t\t\ti = Q2EPM_DEAD;\t\t\tbreak;\n\t\t\tcase Q2PM_GIB:\t\t\ti = Q2EPM_GIB;\t\t\tbreak;\n\t\t\tcase Q2PM_FREEZE:\t\ti = Q2EPM_FREEZE;\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tMSG_WriteByte (msg, i);\n\t}\n\n\tif (q2e)\n\t{\n\t\tif (pflags & Q2PS_M_ORIGIN)\n\t\t{\n\t\t\tMSG_WriteFloat (msg, ps->pmove.origin[0]/8.0);\n\t\t\tMSG_WriteFloat (msg, ps->pmove.origin[1]/8.0);\n\t\t\tMSG_WriteFloat (msg, ps->pmove.origin[2]/8.0);\n\t\t}\n\n\t\tif (pflags & Q2PS_M_VELOCITY)\n\t\t{\n\t\t\tMSG_WriteFloat (msg, ps->pmove.velocity[0]/8.0);\n\t\t\tMSG_WriteFloat (msg, ps->pmove.velocity[1]/8.0);\n\t\t\tMSG_WriteFloat (msg, ps->pmove.velocity[2]/8.0);\n\t\t}\n\t}\n\telse\n\t{\n\t\tif (pflags & Q2PS_M_ORIGIN)\n\t\t{\n\t\t\tMSG_WriteShort (msg, ps->pmove.origin[0]);\n\t\t\tMSG_WriteShort (msg, ps->pmove.origin[1]);\n\t\t\tMSG_WriteShort (msg, ps->pmove.origin[2]);\n\t\t}\n\n\t\tif (pflags & Q2PS_M_VELOCITY)\n\t\t{\n\t\t\tMSG_WriteShort (msg, ps->pmove.velocity[0]);\n\t\t\tMSG_WriteShort (msg, ps->pmove.velocity[1]);\n\t\t\tMSG_WriteShort (msg, ps->pmove.velocity[2]);\n\t\t}\n\t}\n\n\tif (pflags & Q2PS_M_TIME)\n\t{\n\t\tif (q2e)\n\t\t\tMSG_WriteShort (msg, ps->pmove.pm_time);\n\t\telse\n\t\t\tMSG_WriteByte (msg, ps->pmove.pm_time);\n\t}\n\n\tif (pflags & Q2PS_M_FLAGS)\n\t{\n\t\tif (q2e)\n\t\t\tMSG_WriteShort (msg, ps->pmove.pm_flags);\n\t\telse\n\t\t\tMSG_WriteByte (msg, ps->pmove.pm_flags);\n\t}\n\n\tif (pflags & Q2PS_M_GRAVITY)\n\t\tMSG_WriteShort (msg, ps->pmove.gravity);\n\n\tif (pflags & Q2PS_M_DELTA_ANGLES)\n\t{\n\t\tif (q2e)\n\t\t{\n\t\t\tMSG_WriteFloat (msg, SHORT2ANGLE(ps->pmove.delta_angles[0]));\n\t\t\tMSG_WriteFloat (msg, SHORT2ANGLE(ps->pmove.delta_angles[1]));\n\t\t\tMSG_WriteFloat (msg, SHORT2ANGLE(ps->pmove.delta_angles[2]));\n\t\t}\n\t\telse\n\t\t{\n\t\t\tMSG_WriteShort (msg, ps->pmove.delta_angles[0]);\n\t\t\tMSG_WriteShort (msg, ps->pmove.delta_angles[1]);\n\t\t\tMSG_WriteShort (msg, ps->pmove.delta_angles[2]);\n\t\t}\n\t}\n\n\t//\n\t// write the rest of the player_state_t\n\t//\n\tif (pflags & Q2PS_VIEWOFFSET)\n\t{\n\t\tif (q2e)\n\t\t{\n\t\t\tMSG_WriteShort (msg, ps->viewoffset[0]*16);\n\t\t\tMSG_WriteShort (msg, ps->viewoffset[1]*16);\n\t\t\tMSG_WriteShort (msg, (ps->viewoffset[2]-DEFAULT_VIEWHEIGHT)*16);\n\t\t\tMSG_WriteChar (msg, DEFAULT_VIEWHEIGHT/*ps->pmove.viewheight*/);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tMSG_WriteChar (msg, ps->viewoffset[0]*4);\n\t\t\tMSG_WriteChar (msg, ps->viewoffset[1]*4);\n\t\t\tMSG_WriteChar (msg, ps->viewoffset[2]*4);\n\t\t}\n\t}\n\n\n\tif (pflags & Q2PS_VIEWANGLES)\n\t{\n\t\tif (q2e)\n\t\t{\n\t\t\tMSG_WriteFloat (msg, ps->viewangles[0]);\n\t\t\tMSG_WriteFloat (msg, ps->viewangles[1]);\n\t\t\tMSG_WriteFloat (msg, ps->viewangles[2]);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tMSG_WriteAngle16 (msg, ps->viewangles[0]);\n\t\t\tMSG_WriteAngle16 (msg, ps->viewangles[1]);\n\t\t\tMSG_WriteAngle16 (msg, ps->viewangles[2]);\n\t\t}\n\t}\n\n\tif (pflags & Q2PS_KICKANGLES)\n\t{\n\t\tif (q2e)\n\t\t{\n\t\t\tMSG_WriteShort (msg, ps->kick_angles[0]*1024);\n\t\t\tMSG_WriteShort (msg, ps->kick_angles[1]*1024);\n\t\t\tMSG_WriteShort (msg, ps->kick_angles[2]*1024);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tMSG_WriteChar (msg, ps->kick_angles[0]*4);\n\t\t\tMSG_WriteChar (msg, ps->kick_angles[1]*4);\n\t\t\tMSG_WriteChar (msg, ps->kick_angles[2]*4);\n\t\t}\n\t}\n\n\tif (pflags & Q2PS_WEAPONINDEX)\n\t{\n\t\tif (q2e)\n\t\t\tMSG_WriteShort(msg, (0/*gunskin*/<<13) | ps->gunindex);\n\t\telse\n\t\t{\n\t\t\tif (pflags & Q2FTEPS_INDEX16)\n\t\t\t\tMSG_WriteShort(msg, ps->gunindex);\n\t\t\telse\n\t\t\t\tMSG_WriteByte (msg, ps->gunindex);\n\t\t}\n\t}\n\n\tif (pflags & Q2PS_WEAPONFRAME)\n\t{\n\t\tif (q2e)\n\t\t{\n\t\t\tunsigned short fl = ps->gunframe&0x1ff;\n\t\t\tif (ps->gunoffset[0]) fl |= 1<<9;\n\t\t\tif (ps->gunoffset[1]) fl |= 1<<10;\n\t\t\tif (ps->gunoffset[2]) fl |= 1<<11;\n\t\t\tif (ps->gunangles[0]) fl |= 1<<12;\n\t\t\tif (ps->gunangles[1]) fl |= 1<<13;\n\t\t\tif (ps->gunangles[2]) fl |= 1<<14;\n\t\t\t//if (ps->gunrate[2]) fl |= 1<<15;\n\n\t\t\tMSG_WriteShort (msg, fl);\n\t\t\tif (fl & (1<<9))\tMSG_WriteFloat(msg, ps->gunoffset[0]);\n\t\t\tif (fl & (1<<10))\tMSG_WriteFloat(msg, ps->gunoffset[1]);\n\t\t\tif (fl & (1<<11))\tMSG_WriteFloat(msg, ps->gunoffset[2]);\n\t\t\tif (fl & (1<<12))\tMSG_WriteFloat(msg, ps->gunangles[0]);\n\t\t\tif (fl & (1<<13))\tMSG_WriteFloat(msg, ps->gunangles[1]);\n\t\t\tif (fl & (1<<14))\tMSG_WriteFloat(msg, ps->gunangles[2]);\n\t\t\t//if (fl & (1<<15))\tMSG_WriteByte(msg, ps->gunrate);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (pflags & Q2FTEPS_INDEX16)\n\t\t\t\tMSG_WriteShort (msg, ps->gunframe);\n\t\t\telse\n\t\t\t\tMSG_WriteByte (msg, ps->gunframe);\n\t\t\tMSG_WriteChar (msg, ps->gunoffset[0]*4);\n\t\t\tMSG_WriteChar (msg, ps->gunoffset[1]*4);\n\t\t\tMSG_WriteChar (msg, ps->gunoffset[2]*4);\n\t\t\tMSG_WriteChar (msg, ps->gunangles[0]*4);\n\t\t\tMSG_WriteChar (msg, ps->gunangles[1]*4);\n\t\t\tMSG_WriteChar (msg, ps->gunangles[2]*4);\n\t\t}\n\t}\n\n\tif (pflags & Q2PS_BLEND)\n\t{\n\t\tMSG_WriteByte (msg, ps->blend[0]*255);\n\t\tMSG_WriteByte (msg, ps->blend[1]*255);\n\t\tMSG_WriteByte (msg, ps->blend[2]*255);\n\t\tMSG_WriteByte (msg, ps->blend[3]*255);\n\t}\n\n\tif (pflags & Q2PS_FOV)\n\t\tMSG_WriteByte (msg, ps->fov);\n\tif (pflags & Q2PS_RDFLAGS)\n\t\tMSG_WriteByte (msg, ps->rdflags);\n\n\t// send stats\n\tstatbits = 0;\n\tfor (i=0 ; i<min(32,Q2MAX_STATS); i++)\n\t\tif (ps->stats[i] != ops->stats[i])\n\t\t\tstatbits |= 1<<i;\n\tMSG_WriteLong (msg, statbits);\n\tfor (i=0 ; i<min(32,Q2MAX_STATS) ; i++)\n\t\tif (statbits & (1<<i) )\n\t\t\tMSG_WriteShort (msg, ps->stats[i]);\n\n\tif (q2e)\n\t{\n\t\tstatbits = 0;\n\t\tfor (i=0 ; i<Q2MAX_STATS-32 ; i++)\n\t\t\tif (ps->stats[32+i] != ops->stats[32+i])\n\t\t\t\tstatbits |= 1<<i;\n\t\tMSG_WriteLong (msg, statbits);\n\t\tfor (i=0 ; i<Q2MAX_STATS-32 ; i++)\n\t\t\tif (statbits & (1<<i) )\n\t\t\t\tMSG_WriteShort (msg, ps->stats[32+i]);\n\n\t\tif (pflags & Q2EXPS_DAMAGEBLEND)\n\t\t{\n\t\t\tMSG_WriteByte(msg, 0*255);\n\t\t\tMSG_WriteByte(msg, 0*255);\n\t\t\tMSG_WriteByte(msg, 0*255);\n\t\t\tMSG_WriteByte(msg, 0*255);\n\t\t}\n\t\tif (pflags & Q2EXPS_TEAMID)\n\t\t\tMSG_WriteByte(msg, 0);\n\t}\n\telse\n\t{\n\t\tif ((extflags & Q2PSX_CLIENTNUM) || (pflags & Q2FTEPS_CLIENTNUM))\n\t\t\tMSG_WriteByte(msg, to->clientnum[seat]);\n\t}\n}\n\n\n/*\n==================\nSV_WriteFrameToClient\n==================\n*/\nvoid SVQ2_WriteFrameToClient (client_t *client, sizebuf_t *msg)\n{\n\tq2client_frame_t\t\t*frame, *oldframe;\n\tint\t\t\t\t\tlastframe;\n\tclient_t\t\t\t*split;\n\tint seat;\n\tint extflags = 0;\n\n//Com_Printf (\"%i -> %i\\n\", client->lastframe, sv.framenum);\n\t// this is the frame we are creating\n\tframe = &client->frameunion.q2frames[sv.framenum & Q2UPDATE_MASK];\n\n\tif (client->delta_sequence <= 0)\n\t{\t// client is asking for a retransmit\n\t\toldframe = NULL;\n\t\tlastframe = -1;\n\t}\n\telse if (sv.framenum - client->delta_sequence >= (Q2UPDATE_BACKUP - 3) )\n\t{\t// client hasn't gotten a good message through in a long time\n//\t\tCom_Printf (\"%s: Delta request from out-of-date packet.\\n\", client->name);\n\t\toldframe = NULL;\n\t\tlastframe = -1;\n\t}\n\telse\n\t{\t// we have a valid message to delta from\n\t\toldframe = &client->frameunion.q2frames[client->delta_sequence & Q2UPDATE_MASK];\n\t\tlastframe = client->delta_sequence;\n\t}\n\n\tMSG_WriteByte (msg, svcq2_frame);\n\tMSG_WriteLong (msg, sv.framenum);\n\tMSG_WriteLong (msg, lastframe);\t// what we are delta'ing from\n\tMSG_WriteByte (msg, client->chokecount&0xff);\t// rate dropped packets\n\textflags |= Q2PSX_OLD;\n\tclient->chokecount = 0;\n\n\tif (client->protocol == SCP_QUAKE2EX)\n\t{\n\t\tfor (split = client, seat = 0; split; split = split->controlled, seat++)\n\t\t{\n\t\t\t// send over the areabits, private per-seat\n\t\t\tMSG_WriteByte (msg, frame->areabytes);\n\t\t\tSZ_Write (msg, frame->areabits, frame->areabytes);\n\n\t\t\t// delta encode the playerstate\n\t\t\tSVQ2_WritePlayerstateToClient (client, seat, extflags, oldframe, frame, msg);\n\t\t}\n\t}\n\telse\n\t{\n\t\t// send over the areabits, shared between all seats\n\t\tMSG_WriteByte (msg, frame->areabytes);\n\t\tSZ_Write (msg, frame->areabits, frame->areabytes);\n\n\t\t// delta encode the playerstate\n\t\tfor (split = client, seat = 0; split; split = split->controlled, seat++)\n\t\t\tSVQ2_WritePlayerstateToClient (client, seat, extflags, oldframe, frame, msg);\n\t}\n\n\t// delta encode the entities\n\tSVQ2_EmitPacketEntities (oldframe, frame, msg, client->protocol==SCP_QUAKE2EX);\n}\n\n\n/*\n=============================================================================\n\nBuild a client frame structure\n\n=============================================================================\n*/\n\n/*\n=============\nSV_BuildClientFrame\n\nDecides which entities are going to be visible to the client, and\ncopies off the playerstat and areabits.\n=============\n*/\nvoid SVQ2_Ents_Init(void);\nvoid SVQ2_BuildClientFrame (client_t *client)\n{\n\tint\t\te, i;\n\tvec3_t org[MAX_SPLITS];\n\tint\t\tclientarea[MAX_SPLITS];\n\tq2edict_t\t*clent[MAX_SPLITS];\n\tclient_t\t*split;\n\tq2edict_t\t*ent;\n\tq2client_frame_t\t*frame;\n\tq2entity_state_t\t*state;\n\tint\t\tl;\n\tint\t\tseat;\n\tint\t\tc_fullsend;\n\tpvsbuffer_t\tclientpvs;\n\tqbyte\t*clientphs = NULL;\n\tint\t\tseats;\n\n\tif (client->state < cs_spawned)\n\t\treturn;\n\n\tSVQ2_Ents_Init();\n\n\tclientpvs.buffer = alloca(clientpvs.buffersize=sv.world.worldmodel->pvsbytes);\n\n#if 0\n\tnumprojs = 0; // no projectiles yet\n#endif\n\n\t// this is the frame the client will be acking (EVIL HACKS!)\n\tframe = &client->frameunion.q2frames[client->netchan.outgoing_sequence & Q2UPDATE_MASK];\n\n\tframe->senttime = realtime*1000; // save it for ping calc later\n\n\t// this is the frame we are creating\n\tframe = &client->frameunion.q2frames[sv.framenum & Q2UPDATE_MASK];\n\n\t// grab the current player_state_t\n\tfor (seat = 0, split = client; split; split = split->controlled, seat++)\n\t{\n\t\tint\t\tclientcluster;\n\n\t\tclent[seat] = split->q2edict;\n\t\tframe->clientnum[seat] = split - svs.clients;\n\n\t\tif (!split->q2edict->client)\n\t\t{\t//shouldn't happen\n\t\t\tVectorClear(org[seat]);\n\t\t\tclientarea[seat] = 0;\n\t\t\tmemset(&frame->ps[seat], 0, sizeof(frame->ps[seat]));\n\t\t\tframe->ps[seat].pmove.pm_type = Q2PM_FREEZE;\n\t\t\tcontinue;\n\t\t}\n\n\t\t// find the client's PVS\n\t\tfor (i=0 ; i<3 ; i++)\n\t\t\torg[seat][i] = clent[seat]->client->ps.pmove.origin[i]*0.125 + clent[seat]->client->ps.viewoffset[i];\n\n\t\tclientcluster = sv.world.worldmodel->funcs.ClusterForPoint (sv.world.worldmodel, org[seat], &clientarea[seat]);\n\n\t\t// calculate the visible areas\n\t\tframe->areabytes = sv.world.worldmodel->funcs.WriteAreaBits (sv.world.worldmodel, frame->areabits, sizeof(frame->areabits), clientarea[seat], seat != 0);\n\n\t\tsv.world.worldmodel->funcs.FatPVS(sv.world.worldmodel, org[seat], &clientpvs, seat!=0);\n\t\tif (seat==0)\t//FIXME\n\t\t\tclientphs = sv.world.worldmodel->funcs.ClusterPHS (sv.world.worldmodel, clientcluster, NULL);\n\n\t\tframe->ps[seat] = clent[seat]->client->ps;\n\t\tif (sv.paused)\n\t\t\tframe->ps[seat].pmove.pm_type = Q2PM_FREEZE;\n\t}\n\tseats = seat;\n\tfor (; seat < MAX_SPLITS; seat++)\n\t{\n\t\tmemset(&frame->ps[seat], 0, sizeof(frame->ps[seat]));\n\t\tframe->clientnum[seat] = 0xff;\t//invalid\n\t}\n\n\t// build up the list of visible entities\n\tframe->num_entities = 0;\n\tframe->first_entity = svs_next_client_entities;\n\n\tc_fullsend = 0;\n\n\tfor (e=1 ; e<ge->num_edicts ; e++)\n\t{\n\t\tent = Q2EDICT_NUM(e);\n\n\t\t// ignore ents without visible models\n\t\tif (ent->svflags & SVF_NOCLIENT)\n\t\t\tcontinue;\n\n\t\t// ignore ents without visible models unless they have an effect\n\t\tif (!ent->s.modelindex && !ent->s.effects && !ent->s.sound\n\t\t\t&& !ent->s.event)\n\t\t\tcontinue;\n\n\t\tfor (seat = 0; seat < seats; seat++)\n\t\t{\n\t\t\t// ignore if not touching a PV leaf\n\t\t\tif (ent != clent[seat])\n\t\t\t{\n\t\t\t\t// check area\n\t\t\t\tif (!sv.world.worldmodel->funcs.AreasConnected (sv.world.worldmodel, clientarea[seat], ent->areanum))\n\t\t\t\t{\t// doors can legally straddle two areas, so\n\t\t\t\t\t// we may need to check another one\n\t\t\t\t\tif (!ent->areanum2\n\t\t\t\t\t\t|| !sv.world.worldmodel->funcs.AreasConnected (sv.world.worldmodel, clientarea[seat], ent->areanum2))\n\t\t\t\t\t\tcontinue;\t\t// blocked by a door\n\t\t\t\t}\n\n\t\t\t\t// beams just check one point for PHS\n\t\t\t\tif (ent->s.renderfx & Q2RF_BEAM)\n\t\t\t\t{\n\t\t\t\t\tl = ent->clusternums[0];\n\t\t\t\t\tif ( !(clientphs[l >> 3] & (1 << (l&7) )) )\n\t\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t// FIXME: if an ent has a model and a sound, but isn't\n\t\t\t\t\t// in the PVS, only the PHS, clear the model\n\n\t\t\t\t\tif (ent->num_clusters == -1)\n\t\t\t\t\t{\t// too many leafs for individual check, go by headnode\n\t\t\t\t\t\tpvscache_t cache;\n\t\t\t\t\t\tcache.num_leafs = -1;\n\t\t\t\t\t\tcache.headnode = ent->headnode;\n\t\t\t\t\t\tif (!sv.world.worldmodel->funcs.EdictInFatPVS(sv.world.worldmodel, &cache, clientpvs.buffer, NULL))\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\tc_fullsend++;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\t// check individual leafs\n\t\t\t\t\t\tfor (i=0 ; i < ent->num_clusters ; i++)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tl = ent->clusternums[i];\n\t\t\t\t\t\t\tif (clientpvs.buffer[l >> 3] & (1 << (l&7) ))\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (i == ent->num_clusters)\n\t\t\t\t\t\t\tcontinue;\t\t// not visible\n\t\t\t\t\t}\n\n\t\t\t\t\tif (!ent->s.modelindex)\n\t\t\t\t\t{\t// don't send sounds if they will be attenuated away\n\t\t\t\t\t\tvec3_t\tdelta;\n\t\t\t\t\t\tfloat\tlen;\n\n\t\t\t\t\t\tVectorSubtract (org[seat], ent->s.origin, delta);\n\t\t\t\t\t\tlen = Length (delta);\n\t\t\t\t\t\tif (len > 400)\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t\tif (seat == seats)\n\t\t\tcontinue;\t//not visible to any seat\n\t\tseat = 0; //FIXME\n\n\t\t// add it to the circular client_entities array\n\t\tstate = &svs_client_entities[svs_next_client_entities%svs_num_client_entities];\n\t\tif (ent->s.number != e)\n\t\t{\n\t\t\tCon_DPrintf (\"FIXING ENT->S.NUMBER!!!\\n\");\n\t\t\tent->s.number = e;\n\t\t}\n\t\t*state = ent->s;\n\n\t\t// don't mark players missiles as solid\n\t\tif (ent->owner == clent[seat])\n\t\t\tstate->solid = 0;\n\n\t\tsvs_next_client_entities++;\n\t\tframe->num_entities++;\n\t}\n}\n\nvoid SVQ2_BuildBaselines(void)\n{\n\tunsigned int e;\n\tq2edict_t\t*ent;\n\tq2entity_state_t\t*base;\n\n\tif (!ge)\n\t\treturn;\n\n\tfor (e=1 ; e<ge->num_edicts ; e++)\n\t{\n\t\tent = Q2EDICT_NUM(e);\n\t\tbase = &ent->s;\n\n\t\tif (base->modelindex || base->sound || base->effects)\n\t\t\tsv_baselines[e] = *base;\n\t}\n}\n\nvoid SVQ2_Ents_Init(void)\n{\n\textern cvar_t\tmaxclients;\n\tif (!svs_client_entities)\n\t{\n\t\tsvs_num_client_entities = maxclients.value*Q2UPDATE_BACKUP*64;\n\t\tsvs_client_entities = Z_Malloc (sizeof(entity_state_t)*svs_num_client_entities);\n\t}\n}\nvoid SVQ2_Ents_Shutdown(void)\n{\n\tif (svs_client_entities)\n\t{\n\t\tZ_Free(svs_client_entities);\n\t\tsvs_client_entities = NULL;\n\t\tsvs_num_client_entities = 0;\n\t}\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "engine/server/svq2_game.c",
    "content": "#include \"quakedef.h\"\n\n#define Q2NUM_FOR_EDICT(ent) (((char *)ent - (char *)ge->edicts) / ge->edict_size)\n\n#ifndef Q2SERVER\nqboolean SVQ2_InitGameProgs(void)\n{\n\treturn false;\n}\n#else\ngame_export_t\t*ge;\nint svq2_maxclients;\n\ndllhandle_t *q2gamedll;\nvoid SVQ2_UnloadGame (void)\n{\n\tge = NULL;\n\tif (q2gamedll)\n\t\tSys_CloseLibrary(q2gamedll);\n\tq2gamedll = NULL;\n}\nvoid *SVQ2_GetGameAPI (void *parms)\n{\n\tvoid *(VARGS *GetGameAPI)(void *);\n\tdllfunction_t funcs[] =\n\t{\n\t\t{(void**)&GetGameAPI, \"GetGameAPI\"},\n\t\t{NULL,NULL}\n\t};\n\n\tchar name[MAX_OSPATH];\n\tchar syspath[MAX_OSPATH];\n\tchar gamepath[MAX_OSPATH];\n\tvoid *iterator;\n\tint o;\n\tconst char *gamename[] = {\n\t\t\"\",\t//binarydir/q2gameCPU_gamedir.ext\n\t\t\"\",\t//homedir/q2gameCPU_gamedir.ext\n\t\t\"\",\t//basedir/q2gameCPU_gamedir.ext\n\t\t\"\",\t//binarydir/libgame_gamedir.ext\n#ifdef _DEBUG\n\t\t\"debug/game\" ARCH_CPU_POSTFIX ARCH_DL_POSTFIX,\n#endif\n#if defined(__linux__) && defined(__i386__)\n\t\t\"game\" \"i386\" ARCH_DL_POSTFIX,\t//compat is often better than consistancy\n#endif\n\t\t\"game\" ARCH_CPU_POSTFIX ARCH_DL_POSTFIX,\n#ifdef ARCH_ALTCPU_POSTFIX\n\t\t\"game\" ARCH_ALTCPU_POSTFIX ARCH_DL_POSTFIX,\n#endif\n\t\t\"game\" ARCH_DL_POSTFIX,\n#if defined(__linux__)\t//FTE doesn't provide gamecode. Borrow someone else's. Lets just hope that its installed.\n//\t\t\"/usr/lib/yamagi-quake2/%s/game.so\",\n#endif\n\t\tNULL\n\t};\n\tvoid *ret;\n\n#ifdef _DEBUG\n\tCon_DPrintf(\"Searching for %s\\n\", gamename[3]);\n#else\n\tCon_DPrintf(\"Searching for %s\\n\", gamename[2]);\n#endif\n\n\titerator = NULL;\n\twhile(COM_IteratePaths(&iterator, syspath, sizeof(syspath), gamepath, sizeof(gamepath)))\n\t{\n\t\tfor (o = 0; gamename[o]; o++)\n\t\t{\n\t\t\tif (o == 0)\n\t\t\t{\t//nice and specific\n\t\t\t\tif (!host_parms.binarydir)\n\t\t\t\t\tcontinue;\n\t\t\t\tQ_snprintfz(name, sizeof(name), \"%sq2game\"ARCH_CPU_POSTFIX\"_%s\"ARCH_DL_POSTFIX, host_parms.binarydir, gamepath);\n\t\t\t}\n\t\t\telse if (o == 1)\n\t\t\t{\t//nice and specific\n\t\t\t\tif (!*com_homepath)\n\t\t\t\t\tcontinue;\n\t\t\t\tQ_snprintfz(name, sizeof(name), \"%sq2game\"ARCH_CPU_POSTFIX\"_%s\"ARCH_DL_POSTFIX, com_homepath, gamepath);\n\t\t\t}\n\t\t\telse if (o == 2)\n\t\t\t{\t//nice and specific\n\t\t\t\tif (!*com_gamepath)\n\t\t\t\t\tcontinue;\n\t\t\t\tQ_snprintfz(name, sizeof(name), \"%sq2game\"ARCH_CPU_POSTFIX\"_%s\"ARCH_DL_POSTFIX, com_gamepath, gamepath);\n\t\t\t}\n\t\t\telse if (o == 3)\n\t\t\t{\t//because some people don't like knowing what cpu arch they're compiling for\n\t\t\t\tif (!host_parms.binarydir)\n\t\t\t\t\tcontinue;\n\t\t\t\tQ_snprintfz(name, sizeof(name), \"%slibgame_%s\"ARCH_DL_POSTFIX, host_parms.binarydir, gamepath);\n\t\t\t}\n\t\t\telse if (*gamename[o] == '/')\n\t\t\t{\t//system path. o.O\n\t\t\t\tif (!com_gamedirnativecode.ival)\t//just in case they match.\n\t\t\t\t\tcontinue;\n\t\t\t\tQ_snprintfz(name, sizeof(name), gamename[o], gamepath);\n\t\t\t}\n\t\t\telse\n\t\t\t{\t//gamedir paths as specified above.\n\t\t\t\tif (!com_gamedirnativecode.ival)\n\t\t\t\t\tcontinue;\n\t\t\t\tQ_snprintfz(name, sizeof(name), \"%s%s\", syspath, gamename[o]);\n\t\t\t}\n\n\t\t\tq2gamedll = Sys_LoadLibrary(name, funcs);\n\t\t\tif (q2gamedll)\n\t\t\t{\n\t\t\t\tret = GetGameAPI(parms);\n\t\t\t\tif (ret)\n\t\t\t\t{\n\t\t\t\t\treturn ret;\n\t\t\t\t}\n\n\t\t\t\tSys_CloseLibrary(q2gamedll);\n\t\t\t\tq2gamedll = 0;\n\t\t\t}\n\t\t}\n\t}\n\n#ifdef _WIN64\n\t//if we found 32bit q2 gamecode that cannot be loaded, print out a warning about it.\n\t//this should make it a little obvious when people try using 64bit builds to run q2.\n\tif (COM_FCheckExists(\"gamex86.dll\"))\n\t\tCon_Printf(CON_ERROR \"WARNING: 32bit q2 gamecode found, but it cannot be used in a 64bit process.\\nIf you wish to run this q2 mod, you will need to use a 32bit engine build.\\n\");\n#endif\n\n\treturn NULL;\n}\n\n/*\n===============\nPF_Unicast\n\nSends the contents of the mutlicast buffer to a single client\n===============\n*/\nstatic void VARGS PFQ2_Unicast (q2edict_t *ent, qboolean reliable)\n{\n\tint\t\tp;\n\tint ctype;\n\tclient_t\t*client;\n\n\tif (!ent)\n\t\treturn;\n\n\tp = Q2NUM_FOR_EDICT(ent);\n\tif (p < 1 || p > svs.allocated_client_slots)\n\t\treturn;\n\n\tclient = svs.clients + (p-1);\n\n\tif (client->state < cs_connected)\n\t\treturn;\n\tctype = client->netchan.message.prim.coordtype==COORDTYPE_FLOAT_32;\n\n\tif (client->controller)\n\t{\n\t\tclient_t\t*peer;\n\t\tfor (p = 0, peer = client->controller; peer; peer = peer->controlled, p++)\n\t\t{\n\t\t\tif (peer == client)\n\t\t\t\tbreak;\n\t\t}\n\t\tclient = client->controller;\n\n\t\tMSG_WriteShort(&sv.q2multicast[ctype], 0);\n\t\tmemmove(sv.q2multicast[ctype].data+2, sv.q2multicast[ctype].data, sv.q2multicast[ctype].cursize-2);\n\t\tif (client->protocol == SCP_QUAKE2EX)\n\t\t{\n\t\t\t//has a dedicated svc. seats are 1-based.\n\t\t\tsv.q2multicast[ctype].data[0] = svcq2ex_splitclient;\n\t\t\tsv.q2multicast[ctype].data[1] = p+1;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t//svcq2_playerinfo is not normally valid except within the svc_frame message.\n\t\t\t//this means its 'free' to repurpose for things like splitscreen. woot.\n\t\t\tsv.q2multicast[ctype].data[0] = svcq2_playerinfo;\n\t\t\tsv.q2multicast[ctype].data[1] = p;\n\t\t}\n\t}\n\n\tif (reliable)\n\t\tSZ_Write (&client->netchan.message, sv.q2multicast[ctype].data, sv.q2multicast[ctype].cursize);\n\telse\n\t\tSZ_Write (&client->datagram, sv.q2multicast[ctype].data, sv.q2multicast[ctype].cursize);\n\n\tSZ_Clear (&sv.q2multicast[0]);\n\tSZ_Clear (&sv.q2multicast[1]);\n}\n\n\n/*\n===============\nPF_dprintf\n\nDebug print to server console\n===============\n*/\nstatic void VARGS PFQ2_dprintf (const char *fmt, ...)\n{\n\tchar\t\tmsg[1024];\n\tva_list\t\targptr;\n\t\n\tva_start (argptr,fmt);\n\tvsnprintf (msg, sizeof(msg), fmt, argptr);\n\tva_end (argptr);\n\n\tCon_Printf (\"%s\", msg);\n}\n\n\n/*\n===============\nPF_cprintf\n\nPrint to a single client\n===============\n*/\nstatic void VARGS PFQ2_cprintf (q2edict_t *ent, int level, const char *fmt, ...)\n{\n\tchar\t\tmsg[1024];\n\tva_list\t\targptr;\n\tint\t\t\tn=0;\n\n\tif (ent)\n\t{\n\t\tn = Q2NUM_FOR_EDICT(ent);\n\t\tif (n < 1 || n > svs.allocated_client_slots)\n\t\t{\n\t\t\tSys_Error (\"cprintf to a non-client\");\n\t\t\treturn;\n\t\t}\n\t\t\n\t\tif (svs.clients[n-1].state < cs_connected)\n\t\t{\n\t\t\tSys_Error (\"cprintf to a disconnected client\");\n\t\t\treturn;\n\t\t}\n\t}\n\n\tva_start (argptr,fmt);\n\tvsnprintf (msg, sizeof(msg), fmt, argptr);\n\tva_end (argptr);\n\n\tif (ent)\n\t\tSV_ClientPrintf (svs.clients+(n-1), level, \"%s\", msg);\n\telse\n\t\tCon_Printf (\"%s\", msg);\n}\n\n\n/*\n===============\nPF_centerprintf\n\ncenterprint to a single client\n===============\n*/\nstatic void VARGS PFQ2_centerprintf (q2edict_t *ent, const char *fmt, ...)\n{\n\tchar\t\tmsg[1024];\n\tva_list\t\targptr;\n\tint\t\t\tn;\n\t\n\tn = Q2NUM_FOR_EDICT(ent);\n\tif (n < 1 || n > svs.allocated_client_slots)\n\t\treturn;\t// Com_Error (ERR_DROP, \"centerprintf to a non-client\");\n\n\tif (svs.clients[n-1].state < cs_connected)\n\t\treturn;\n\n\tva_start (argptr,fmt);\n\tvsprintf (msg, fmt, argptr);\n\tva_end (argptr);\n\n\tMSG_WriteByte (&sv.q2multicast[0],svcq2_centerprint);\n\tMSG_WriteString (&sv.q2multicast[0],msg);\n\tMSG_WriteByte (&sv.q2multicast[1],svcq2_centerprint);\n\tMSG_WriteString (&sv.q2multicast[1],msg);\n\tPFQ2_Unicast (ent, true);\n}\n\n\n/*\n===============\nPF_error\n\nAbort the server with a game error\n===============\n*/\nstatic void VARGS PFQ2_error (const char *fmt, ...)\n{\n\tchar\t\tmsg[1024];\n\tva_list\t\targptr;\n\t\n\tva_start (argptr,fmt);\n\tvsnprintf (msg, sizeof(msg), fmt, argptr);\n\tva_end (argptr);\n\n\tSV_Error(\"Game Error: %s\", msg);\n}\n\n\n\n/*\n===============\nPF_Configstring\n\n===============\n*/\nvoid VARGS PFQ2_Configstring (int i, const char *val)\n{\n\tint msgsize;\n\tint j;\n\tint q2exremap;\n\tclient_t *cl;\n\tif (i < 0 || i >= Q2MAX_CONFIGSTRINGS)\n\t\tHost_EndGame (\"configstring: bad index %i\\n\", i);\n\n\tif (!val)\n\t\tval = \"\";\n\tmsgsize = 3+ strlen(val)+1;\n\n\t{\t//remap from vanilla to q2e\n#define PASTE(a,b) (a##b)\n#define REMAPR(n,l) \t\tif (i >= Q2CS_##n && i < Q2CS_##n+Q2MAX_##l) q2exremap = i-Q2CS_##n+Q2EXCS_##n; else\n#define REMAPS(n)\t\t\tif (i == PASTE(Q2CS_,n)) q2exremap = i-PASTE(Q2CS_,n)+PASTE(Q2EXCS_,n); else\n#define Q2MAX_STATUSBAR (Q2CS_AIRACCEL-Q2CS_STATUSBAR)\n\t\tREMAPS(NAME)\n\t\tREMAPS(CDTRACK)\n\t\tREMAPS(SKY)\n\t\tREMAPS(SKYAXIS)\n\t\tREMAPS(SKYROTATE)\n\t\tREMAPR(STATUSBAR, STATUSBAR)\n\t\tREMAPS(AIRACCEL)\n\t\tREMAPS(MAXCLIENTS)\n\t\tREMAPS(MAPCHECKSUM)\n\t\tREMAPR(MODELS, MODELS)\n\t\tREMAPR(SOUNDS, SOUNDS)\n\t\tREMAPR(IMAGES, IMAGES)\n\t\tREMAPR(LIGHTS, LIGHTSTYLES)\n\t\tREMAPR(ITEMS, ITEMS)\n\t\tREMAPR(PLAYERSKINS, CLIENTS)\n\t\tREMAPR(GENERAL, GENERAL)\n\t\tHost_EndGame (\"configstring %i > Q2MAX_CONFIGSTRINGS\", i);\n\t}\n\n\tZ_Free((char*)sv.strings.configstring[i]);\n\tsv.strings.configstring[i] = Z_StrDup(val);\n\n\tif (i == Q2CS_NAME)\n\t\tQ_strncpyz(sv.mapname, val, sizeof(sv.mapname));\n\n\tif (sv.state != ss_loading)\n\t{\t// send the update to everyone\n\t\tfor (j = 0, cl = svs.clients; j < sv.allocated_client_slots; j++, cl++)\n\t\t{\n\t\t\tif (cl->state != cs_spawned)\n\t\t\t\tcontinue;\n\t\t\tif (cl->controller)\n\t\t\t\tcontinue;\n\t\t\tif (cl->protocol == SCP_QUAKE2)\n\t\t\t{\n\t\t\t\tClientReliableWrite_Begin(cl, svcq2_configstring, msgsize);\n\t\t\t\tClientReliableWrite_Short(cl, i);\n\t\t\t\tClientReliableWrite_String(cl, val);\n\t\t\t}\n\t\t\telse if (cl->protocol == SCP_QUAKE2EX)\n\t\t\t{\n\t\t\t\tClientReliableWrite_Begin(cl, svcq2_configstring, msgsize);\n\t\t\t\tClientReliableWrite_Short(cl, q2exremap);\n\t\t\t\tClientReliableWrite_String(cl, val);\n\t\t\t}\n\t\t}\n\t}\n}\n\nstatic int SVQ2_FindIndex (const char *name, int start, int max, int overflowtype)\n{\n\tint\t\ti;\n\tconst char *strings;\n\t\n\tif (!name || !name[0])\n\t\treturn 0;\n\n\tfor (i=1 ; i<max ; i++)\n\t{\n\t\tstrings = sv.strings.configstring[start+i];\n\t\tif (!strings || !*strings)\n\t\t\tbreak;\n\t\tif (!strcmp(strings, name))\n\t\t\treturn i;\n\t}\n\n\tif (i == max)\n\t{\n\t\tif (overflowtype)\n\t\t{\n\t\t\tconst char **overflowstrings;\n\t\t\tint startq2e;\n\t\t\tswitch(overflowtype)\n\t\t\t{\n\t\t\tcase 1:\n\t\t\t\toverflowstrings = sv.strings.q2_extramodels;\n\t\t\t\tmax = countof(sv.strings.q2_extramodels);\n\t\t\t\ti++;\t//do not allow 255 to be allocated, ever. just live with the gap (255 means special things).\n\t\t\t\tstart = 0x8000;\n\t\t\t\tstartq2e = Q2EXCS_MODELS;\n\t\t\t\tbreak;\n\t\t\tcase 2:\n\t\t\t\toverflowstrings = sv.strings.q2_extrasounds;\n\t\t\t\tmax = countof(sv.strings.q2_extrasounds);\n\t\t\t\tstart = 0x8000|0x4000;\n\t\t\t\tstartq2e = Q2EXCS_SOUNDS;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\toverflowstrings = NULL;\t//ssh\n\t\t\t\tmax = i;\n\t\t\t\tstartq2e=0;//just to kill warnings\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tfor ( ; i<max ; i++)\n\t\t\t{\n\t\t\t\tstrings = overflowstrings[i];\n\t\t\t\tif (!strings || !*strings)\n\t\t\t\t{\n\t\t\t\t\toverflowstrings[i] = Z_StrDup(name);\n\t\t\t\t\tif (sv.state != ss_loading)\n\t\t\t\t\t{\n\t\t\t\t\t\tSZ_Clear (&sv.q2multicast[0]);\n\t\t\t\t\t\tMSG_WriteChar (&sv.q2multicast[0], svcq2_configstring);\n\t\t\t\t\t\tMSG_WriteShort (&sv.q2multicast[0], start+i);\n\t\t\t\t\t\tMSG_WriteString (&sv.q2multicast[0], name);\n\t\t\t\t\t\tSZ_Clear (&sv.q2multicast[1]);\n\t\t\t\t\t\tMSG_WriteChar (&sv.q2multicast[1], svcq2_configstring);\n\t\t\t\t\t\tMSG_WriteShort (&sv.q2multicast[1], startq2e+i);\n\t\t\t\t\t\tMSG_WriteString (&sv.q2multicast[1], name);\n\t\t\t\t\t\tSV_Multicast (vec3_origin, MULTICAST_ALL_R);\n\t\t\t\t\t}\n\t\t\t\t\treturn i;\n\t\t\t\t}\n\t\t\t\tif (!strcmp(strings, name))\n\t\t\t\t\treturn i;\n\t\t\t}\n\t\t}\n\t\tHost_EndGame (\"*Index: overflow\");\n\t}\n\n\tPFQ2_Configstring(start + i, name);\n\n\treturn i;\n}\n\n\nstatic int VARGS SVQ2_ModelIndex (const char *name)\n{\n\t//model 255 is special for players. don't use it.\n\treturn SVQ2_FindIndex (name, Q2CS_MODELS, Q2MAX_MODELS-1, 1);\n}\n\nstatic int VARGS SVQ2_SoundIndex (const char *name)\n{\n\treturn SVQ2_FindIndex (name, Q2CS_SOUNDS, Q2MAX_SOUNDS, 2);\n}\n\nstatic int VARGS SVQ2_ImageIndex (const char *name)\n{\n\treturn SVQ2_FindIndex (name, Q2CS_IMAGES, Q2MAX_IMAGES, 0);\n}\n\n/*\n=================\nPF_setmodel\n\nAlso sets mins and maxs for inline bmodels\n=================\n*/\nstatic void VARGS PFQ2_setmodel (q2edict_t *ent, const char *name)\n{\n\tint\t\ti;\n\tmodel_t\t*mod;\n\n\tif (!name)\n\t{\n\t\tCon_Printf (CON_ERROR \"ERROR: PF_setmodel: NULL\\n\");\n\t\tent->s.modelindex = 0;\n\t\treturn;\n\t}\n\n\ti = SVQ2_ModelIndex (name);\n\t\t\n//\tent->model = name;\n\tent->s.modelindex = i;\n\n// if it is an inline model, get the size information for it\n\tif (name[0] == '*')\n\t{\n\t\tmod = Mod_FindName (Mod_FixName(name, sv.modelname));\n\t\tif (mod->loadstate == MLS_LOADING)\n\t\t\tCOM_WorkerPartialSync(mod, &mod->loadstate, MLS_LOADING);\t//wait for it if needed\n\t\tVectorCopy (mod->mins, ent->mins);\n\t\tVectorCopy (mod->maxs, ent->maxs);\n\t\tWorldQ2_LinkEdict (&sv.world, ent);\n\t}\n\n}\n\n/*\nstatic qboolean\tPFQ2_Q1BSP_AreasConnected (int area1, int area2)\n{\n\treturn true;\n}\n\nstatic qboolean\tCMQ2_Q1BSP_SetAreaPortalState (int portalnum, qboolean open)\n{\n\treturn true;\n}*/\n\nstatic void VARGS PFQ2_WriteChar (int c)\t{c&=0xff;MSG_WriteChar (&sv.q2multicast[0], c);MSG_WriteChar (&sv.q2multicast[1], c);}\nstatic void VARGS PFQ2_WriteByte (int c)\t{c&=0xff;MSG_WriteByte (&sv.q2multicast[0], c);MSG_WriteByte (&sv.q2multicast[1], c);}\nstatic void VARGS PFQ2_WriteShort (int c)\t{c&=0xffff;MSG_WriteShort (&sv.q2multicast[0], c);MSG_WriteShort (&sv.q2multicast[1], c);}\nstatic void VARGS PFQ2_WriteLong (int c)\t{MSG_WriteLong (&sv.q2multicast[0], c);MSG_WriteLong (&sv.q2multicast[1], c);}\nstatic void VARGS PFQ2_WriteFloat (float f) {MSG_WriteFloat (&sv.q2multicast[0], f);MSG_WriteFloat (&sv.q2multicast[1], f);}\nstatic void VARGS PFQ2_WriteString (const char *s) {MSG_WriteString (&sv.q2multicast[0], s);MSG_WriteString (&sv.q2multicast[1], s);}\nstatic void VARGS PFQ2_WriteAngle (float f)\t\t{MSG_WriteAngle (&sv.q2multicast[0], f);MSG_WriteAngle (&sv.q2multicast[1], f);}\nstatic void VARGS PFQ2_WritePos (vec3_t pos) {\tMSG_WriteCoord (&sv.q2multicast[0], pos[0]);\n\t\t\t\t\t\t\t\t\t\t\t\tMSG_WriteCoord (&sv.q2multicast[0], pos[1]);\n\t\t\t\t\t\t\t\t\t\t\t\tMSG_WriteCoord (&sv.q2multicast[0], pos[2]);\n\t\t\t\t\t\t\t\t\t\t\t\tMSG_WriteCoord (&sv.q2multicast[1], pos[0]);\n\t\t\t\t\t\t\t\t\t\t\t\tMSG_WriteCoord (&sv.q2multicast[1], pos[1]);\n\t\t\t\t\t\t\t\t\t\t\t\tMSG_WriteCoord (&sv.q2multicast[1], pos[2]);\n\t\t\t\t\t\t\t\t\t\t\t}\nstatic void VARGS PFQ2_WriteDir (vec3_t dir)\t{MSG_WriteDir (&sv.q2multicast[0], dir);MSG_WriteDir (&sv.q2multicast[1], dir);}\n\n/*\n=================\nPF_inPVS\n\nAlso checks portalareas so that doors block sight\n=================\n*/\nstatic qboolean VARGS PFQ2_inPVS (vec3_t p1, vec3_t p2)\n{\n\tint\t\tcluster;\n\tint\t\tarea1, area2;\n\tqbyte\t*mask;\n\n\tcluster = sv.world.worldmodel->funcs.ClusterForPoint(sv.world.worldmodel, p1, &area1);\n\tmask = sv.world.worldmodel->funcs.ClusterPVS (sv.world.worldmodel, cluster, NULL, PVM_FAST);\n\n\tcluster = sv.world.worldmodel->funcs.ClusterForPoint(sv.world.worldmodel, p2, &area2);\n\tif ( mask && (!(mask[cluster>>3] & (1<<(cluster&7)) ) ) )\n\t\treturn false;\n\tif (!sv.world.worldmodel->funcs.AreasConnected (sv.world.worldmodel, area1, area2))\n\t\treturn false;\t\t// a door blocks sight\n\treturn true;\n}\n\n\n/*\n=================\nPF_inPHS\n\nAlso checks portalareas so that doors block sound\n=================\n*/\nstatic qboolean VARGS PFQ2_inPHS (vec3_t p1, vec3_t p2)\n{\n\tint\t\tcluster;\n\tint\t\tarea1, area2;\n\tqbyte\t*mask;\n\n\tcluster = sv.world.worldmodel->funcs.ClusterForPoint(sv.world.worldmodel, p1, &area1);\n\tmask = sv.world.worldmodel->funcs.ClusterPHS (sv.world.worldmodel, cluster, NULL);\n\n\tcluster = sv.world.worldmodel->funcs.ClusterForPoint(sv.world.worldmodel, p2, &area2);\n\tif ( mask && (!(mask[cluster>>3] & (1<<(cluster&7)) ) ) )\n\t\treturn false;\t\t// more than one bounce away\n\tif (!sv.world.worldmodel->funcs.AreasConnected (sv.world.worldmodel, area1, area2))\n\t\treturn false;\t\t// a door blocks hearing\n\n\treturn true;\n}\n\nqboolean VARGS PFQ2_AreasConnected(unsigned int area1, unsigned int area2)\n{\n\t//FIXME: requires q2/q3 bsp\n\treturn sv.world.worldmodel->funcs.AreasConnected(sv.world.worldmodel, area1, area2);\n}\n\n\n\n\n#define\tQ2ATTN_NONE\t\t\t\t0\t// full volume the entire level\n#define Q2ATTN_NORM\t\t\t\t1/*\n#define Q2CHAN_AUTO   0\n#define Q2CHAN_WEAPON 1\n#define Q2CHAN_VOICE  2\n#define Q2CHAN_ITEM   3\n#define Q2CHAN_BODY   4*/\n#define Q2CHAN_NO_PHS_ADD\t\t8\n#define\tQ2CHAN_RELIABLE\t\t\t16\n\nstatic void VARGS SVQ2_StartSound (vec3_t origin, q2edict_t *entity, int channel,\n\t\t\t\t\tint soundindex, float volume,\n\t\t\t\t\tfloat attenuation, float timeofs)\n{       \n\tint\t\t\tsendchan;\n    int\t\t\tflags;\n    int\t\t\ti;\n\tint\t\t\tent;\n\tvec3_t\t\torigin_v;\n\tqboolean\tuse_phs;\n\tunsigned int needext = 0;\n\tqboolean q2e;\n\n\tif (volume < 0 || volume > 1.0)\n\t\tSys_Error (\"SV_StartSound: volume = %f\", volume);\n\n\tif (attenuation < 0 || attenuation > 4)\n\t\tSys_Error (\"SV_StartSound: attenuation = %f\", attenuation);\n\n//\tif (channel < 0 || channel > 15)\n//\t\tSys_Error (\"SV_StartSound: channel = %i\", channel);\n\n\tif (timeofs < 0 || timeofs > 0.255)\n\t\tSys_Error (\"SV_StartSound: timeofs = %f\", timeofs);\n\n\tent = Q2NUM_FOR_EDICT(entity);\n\n\tif (channel & Q2CHAN_NO_PHS_ADD)\t// no PHS flag\n\t\tuse_phs = false;\n\telse\n\t\tuse_phs = true;\n\n\tsendchan = (ent<<3) | (channel&7);\n\n\tflags = 0;\n\tif (volume != Q2DEFAULT_SOUND_PACKET_VOLUME)\n\t\tflags |= Q2SND_VOLUME;\n\tif (attenuation != Q2DEFAULT_SOUND_PACKET_ATTENUATION)\n\t\tflags |= Q2SND_ATTENUATION;\n\tif (soundindex > 0xff)\n\t{\n\t\tflags |= Q2SNDFTE_LARGEIDX;\n\t\tneedext |= PEXT_SOUNDDBL;\n\t}\n\n\t// the client doesn't know that bmodels have weird origins\n\t// the origin can also be explicitly set\n\tif ( (entity->svflags & SVF_NOCLIENT)\n\t\t|| (entity->solid == Q2SOLID_BSP) \n\t\t|| origin )\n\t\tflags |= Q2SND_POS;\n\n\t// always send the entity number for channel overrides\n\tflags |= Q2SND_ENT;\n\n\tif (timeofs)\n\t\tflags |= Q2SND_OFFSET;\n\n\t// use the entity origin unless it is a bmodel or explicitly specified\n\tif (!origin)\n\t{\n\t\torigin = origin_v;\n\t\tif (entity->solid == Q2SOLID_BSP)\n\t\t{\n\t\t\tfor (i=0 ; i<3 ; i++)\n\t\t\t\torigin_v[i] = entity->s.origin[i]+0.5*(entity->mins[i]+entity->maxs[i]);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tVectorCopy (entity->s.origin, origin_v);\n\t\t}\n\t}\n\n\tfor(q2e=0; ; q2e++)\n\t{\n\t\tMSG_WriteByte (&sv.q2multicast[q2e], svcq2_sound);\n\t\tMSG_WriteByte (&sv.q2multicast[q2e], flags);\n\t\tif (q2e || (flags & Q2SNDFTE_LARGEIDX))\n\t\t\tMSG_WriteShort (&sv.q2multicast[q2e], soundindex);\n\t\telse\n\t\t\tMSG_WriteByte (&sv.q2multicast[q2e], soundindex);\n\n\t\tif (flags & Q2SND_VOLUME)\n\t\t\tMSG_WriteByte (&sv.q2multicast[q2e], volume*255);\n\t\tif (flags & Q2SND_ATTENUATION)\n\t\t\tMSG_WriteByte (&sv.q2multicast[q2e], attenuation*64);\n\t\tif (flags & Q2SND_OFFSET)\n\t\t\tMSG_WriteByte (&sv.q2multicast[q2e], timeofs*1000);\n\n\t\tif (flags & Q2SND_ENT)\n\t\t{\n\t\t\tif (q2e && (flags & Q2SNDEX_LARGEENT))\n\t\t\t\tMSG_WriteLong (&sv.q2multicast[q2e], sendchan);\n\t\t\telse\n\t\t\t\tMSG_WriteShort (&sv.q2multicast[q2e], sendchan);\n\t\t}\n\n\t\tif (flags & Q2SND_POS)\n\t\t{\n\t\t\tMSG_WriteCoord (&sv.q2multicast[q2e], origin[0]);\n\t\t\tMSG_WriteCoord (&sv.q2multicast[q2e], origin[1]);\n\t\t\tMSG_WriteCoord (&sv.q2multicast[q2e], origin[2]);\n\t\t}\n\n\t\tif (q2e)\n\t\t\tbreak;\n\t\telse\n\t\t{\n\t\t\tflags &= ~(Q2SNDFTE_LARGEIDX|Q2SNDEX_LARGEENT);\n\t\t\tif (origin)\n\t\t\t\tflags |= Q2SNDEX_EXPLICITPOS;\n\t\t\tflags |= Q2SND_POS;\t\t//always sends a pos to avoid issues when the ent is not in the client's phs\n\n\t\t\tif (sendchan > 0xffff)\n\t\t\t\tflags |= Q2SNDEX_LARGEENT;\n\t\t}\n\t}\n\n\t// if the sound doesn't attenuate,send it to everyone\n\t// (global radio chatter, voiceovers, etc)\n\tif (attenuation == Q2ATTN_NONE)\n\t\tuse_phs = false;\n\n\tif (channel & Q2CHAN_RELIABLE)\n\t\tSV_MulticastProtExt(origin, use_phs?MULTICAST_PHS_R:MULTICAST_ALL_R, FULLDIMENSIONMASK, needext, 0);\n\telse\n\t\tSV_MulticastProtExt(origin, use_phs?MULTICAST_PHS:MULTICAST_ALL, FULLDIMENSIONMASK, needext, 0);\n}  \n\nstatic void VARGS PFQ2_StartSound (q2edict_t *entity, int channel, int sound_num, float volume,\n    float attenuation, float timeofs)\n{\n\tif (!entity)\n\t\treturn;\n\tSVQ2_StartSound (NULL, entity, channel, sound_num, volume, attenuation, timeofs);\n}\n\nstatic q2trace_t VARGS SVQ2_Trace (vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, q2edict_t *passedict, int contentmask)\n{\n\tq2trace_t ret;\n\ttrace_t tr;\n\tif (!mins)\n\t\tmins = vec3_origin;\n\tif (!maxs)\n\t\tmaxs = vec3_origin;\n\ttr = WorldQ2_Move(&sv.world, start, mins, maxs, end, contentmask, passedict);\n\tret.allsolid = tr.allsolid;\n\tret.startsolid = tr.startsolid;\n\tret.contents = tr.contents;\n\tret.surface = tr.surface;\n\tret.fraction = tr.fraction;\n\tVectorCopy(tr.endpos, ret.endpos);\n\tmemset(&ret.plane, 0, sizeof(ret.plane));\n\tVectorCopy(tr.plane.normal, ret.plane.normal);\n\tret.plane.dist = tr.plane.dist;\n\tret.ent = tr.ent;\n\treturn ret;\n}\n\nstatic int VARGS SVQ2_PointContents (vec3_t p)\n{\n\tq2trace_t tr = SVQ2_Trace(p, vec3_origin, vec3_origin, p, NULL, ~0);\n\treturn tr.contents;\n//\treturn CM_PointContents(p, 0);\n}\n\nstatic cvar_t *VARGS Q2Cvar_Get (const char *var_name, const char *value, int flags)\n{\n\tcvar_t *var;\n\t//q2 gamecode knows about these flags. anything else is probably a bug, or 3rd-party extension.\n\tflags &= (CVAR_NOSET|CVAR_SERVERINFO|CVAR_USERINFO|CVAR_ARCHIVE|CVAR_MAPLATCH);\n\n\tif (!strcmp(var_name, \"gamedir\"))\n\t\tvar_name = \"fs_gamedir\";\n\n\tvar = Cvar_Get(var_name, value, flags, \"Quake2 game variables\");\n\tif (!var)\n\t{\n\t\tCon_Printf(\"Q2Cvar_Get: variable %s not creatable\\n\", var_name);\n\t\treturn NULL;\n\t}\n\n\t//allow this to change all < cvar_latch values.\n\t//this allows q2 dlls to apply different flags to a cvar without destroying our important ones (like cheat).\n\tflags |= var->flags;\n\tif (flags != var->flags)\n\t{\n\t\tvar->flags = flags;\n\t\tCvar_Set(var, var->string);\n\t}\n\treturn var;\n}\n\ncvar_t *VARGS Q2Cvar_Set (const char *var_name, const char *value)\n{\n\tcvar_t *var = Cvar_FindVar(var_name);\n\tif (!var)\n\t{\n\t\tCon_Printf(\"Q2Cvar_Set: variable %s not found\\n\", var_name);\n\t\treturn NULL;\n\t}\n\treturn Cvar_Set(var, value);\n}\ncvar_t *VARGS Q2Cvar_ForceSet (const char *var_name, const char *value)\n{\n\tcvar_t *var = Cvar_FindVar(var_name);\n\tif (!var)\n\t{\n\t\tCon_Printf(\"Q2Cvar_Set: variable %s not found\\n\", var_name);\n\t\treturn NULL;\n\t}\n\treturn Cvar_ForceSet(var, value);\n}\n\n//==============================================\n\n/*\n===============\nSV_ShutdownGameProgs\n\nCalled when either the entire server is being killed, or\nit is changing to a different game directory.\n===============\n*/\nvoid VARGS SVQ2_ShutdownGameProgs (void)\n{\n\tif (!ge)\n\t\treturn;\n\tge->Shutdown ();\n\tSVQ2_UnloadGame ();\n\tge = NULL;\n}\n\nstatic void VARGS AddCommandString(const char *command)\n{\n\tCbuf_AddText(command, RESTRICT_LOCAL);\n}\n\n/*\n===============\nSV_InitGameProgs\n\nInit the game subsystem for a new map\n===============\n*/\n\nstatic void VARGS Q2SCR_DebugGraph(float value, int color)\n{return;}\n\nstatic void\tVARGS SVQ2_LinkEdict (q2edict_t *ent)\n{\n\tWorldQ2_LinkEdict(&sv.world, ent);\n}\nstatic void\tVARGS SVQ2_UnlinkEdict (q2edict_t *ent)\n{\n\tWorldQ2_UnlinkEdict(&sv.world, ent);\n}\nstatic int\tVARGS SVQ2_AreaEdicts (vec3_t mins, vec3_t maxs, q2edict_t **list,\tint maxcount, int areatype)\n{\n\treturn WorldQ2_AreaEdicts(&sv.world, mins, maxs, list, maxcount, areatype);\n}\n\nstatic model_t *QDECL SVQ2_GetCModel(world_t *w, int modelindex)\n{\n\tif ((unsigned int)modelindex < MAX_PRECACHE_MODELS)\n\t\treturn sv.models[modelindex];\n\telse\n\t\treturn NULL;\n}\n\nvoid SVQ2_InitWorld(void)\n{\n\tWorld_ClearWorld_Nodes (&sv.world, false);\n\tsv.world.Get_CModel = SVQ2_GetCModel;\n}\n\nstatic void QDECL PFQ2_SetAreaPortalState(unsigned int p, qboolean s)\n{\n\tif (sv.world.worldmodel->funcs.SetAreaPortalState)\n\t\tsv.world.worldmodel->funcs.SetAreaPortalState(sv.world.worldmodel, p, -1, -1, s);\n}\n\nstatic void *VARGS ZQ2_TagMalloc(int size, int tag)\n{\n\treturn Z_TagMalloc(size, tag);\n}\nqboolean SVQ2_InitGameProgs(void)\n{\n\textern cvar_t maxclients;\n\tstatic volatile game_import_t\timport;\t//volatile because msvc sucks\n\tif (COM_CheckParm(\"-noq2dll\"))\n\t{\n\t\tSVQ2_ShutdownGameProgs();\n\t\treturn false;\n\t}\n\n\tif (ge)\n\t{\n\t\tSVQ2_InitWorld();\n\t\treturn true;\n\t}\n\n\t// calc the imports. \n\timport.multicast\t\t\t= SV_Multicast;\n\timport.unicast\t\t\t\t= PFQ2_Unicast;\n\timport.bprintf\t\t\t\t= SV_BroadcastPrintf;\n\timport.dprintf\t\t\t\t= PFQ2_dprintf;\n\timport.cprintf\t\t\t\t= PFQ2_cprintf;\n\timport.centerprintf\t\t\t= PFQ2_centerprintf;\n\timport.error\t\t\t\t= PFQ2_error;\n\n\timport.linkentity\t\t\t= SVQ2_LinkEdict;\n\timport.unlinkentity\t\t\t= SVQ2_UnlinkEdict;\n\timport.BoxEdicts\t\t\t= SVQ2_AreaEdicts;\n\timport.trace\t\t\t\t= SVQ2_Trace;\n\timport.pointcontents\t\t= SVQ2_PointContents;\n\timport.setmodel\t\t\t\t= PFQ2_setmodel;\n\timport.inPVS\t\t\t\t= PFQ2_inPVS;\n\timport.inPHS\t\t\t\t= PFQ2_inPHS;\n\timport.Pmove\t\t\t\t= Q2_Pmove;\n\n\timport.modelindex\t\t\t= SVQ2_ModelIndex;\n\timport.soundindex\t\t\t= SVQ2_SoundIndex;\n\timport.imageindex\t\t\t= SVQ2_ImageIndex;\n\n\timport.configstring\t\t\t= PFQ2_Configstring;\n\timport.sound\t\t\t\t= PFQ2_StartSound;\n\timport.positioned_sound\t\t= SVQ2_StartSound;\n\n\timport.WriteChar\t\t\t= PFQ2_WriteChar;\n\timport.WriteByte\t\t\t= PFQ2_WriteByte;\n\timport.WriteShort\t\t\t= PFQ2_WriteShort;\n\timport.WriteLong\t\t\t= PFQ2_WriteLong;\n\timport.WriteFloat\t\t\t= PFQ2_WriteFloat;\n\timport.WriteString\t\t\t= PFQ2_WriteString;\n\timport.WritePosition\t\t= PFQ2_WritePos;\n\timport.WriteDir\t\t\t\t= PFQ2_WriteDir;\n\timport.WriteAngle\t\t\t= PFQ2_WriteAngle;\n\n\timport.TagMalloc\t\t\t= ZQ2_TagMalloc;\n\timport.TagFree\t\t\t\t= Z_TagFree;\n\timport.FreeTags\t\t\t\t= Z_FreeTags;\n\n\timport.cvar\t\t\t\t\t= Q2Cvar_Get;\n\timport.cvar_set\t\t\t\t= Q2Cvar_Set;\n\timport.cvar_forceset\t\t= Q2Cvar_ForceSet;\n\n\timport.argc\t\t\t\t\t= Cmd_Argc;\n\timport.argv\t\t\t\t\t= Cmd_Argv;\n\timport.args\t\t\t\t\t= Cmd_Args;\n\timport.AddCommandString\t\t= AddCommandString;\n\n\timport.DebugGraph\t\t\t= Q2SCR_DebugGraph;\n\timport.SetAreaPortalState\t= PFQ2_SetAreaPortalState;\n\timport.AreasConnected\t\t= PFQ2_AreasConnected;\n\n\tge = (game_export_t *)SVQ2_GetGameAPI ((game_import_t*)&import);\n\n\tif (!ge)\n\t\treturn false;\n\tif (ge->apiversion != Q2GAME_API_VERSION)\n\t{\n\t\tCon_Printf(CON_ERROR \"game is version %i, not %i\\n\", ge->apiversion, Q2GAME_API_VERSION);\n\t\tge = NULL;\n\t\tSVQ2_UnloadGame ();\n\t\treturn false;\n\t}\n\n\t//Q2 gamecode depends upon maxclients being set+locked in order to know how many player slots there actually are. It crashes when its wrong.\n\tif (!deathmatch.value && !coop.value)\n\t\tsvq2_maxclients = 1;\n\telse\n\t\tsvq2_maxclients = maxclients.ival;\n#ifdef HAVE_CLIENT\n\tif (cl_splitscreen.ival)\n\t{\n\t\tif (!deathmatch.value && !coop.value)\n\t\t\tCvar_Set(&coop, \"1\");\t//force coop, for coop rules. we get spawn spot problems otherwise.\n\t\tsvq2_maxclients = max(svq2_maxclients, MAX_SPLITS);\n\t}\n#endif\n\tif (svq2_maxclients > MAX_CLIENTS)\n\t\tsvq2_maxclients = MAX_CLIENTS;\n\tif (svq2_maxclients != maxclients.value)\n\t\tCvar_SetValue(&maxclients, svq2_maxclients);\n\n\tmaxclients.flags |= CVAR_MAPLATCH;\n\tdeathmatch.flags |= CVAR_MAPLATCH;\n\tcoop.flags |= CVAR_MAPLATCH;\n\n\tSVQ2_InitWorld();\n\tge->Init ();\n\treturn true;\n}\n\n#endif\n"
  },
  {
    "path": "engine/server/world.c",
    "content": "/*\nCopyright (C) 1996-1997 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  \n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n*/\n// world.c -- world query functions\n\n#include \"quakedef.h\"\n#include \"pr_common.h\"\n\n#if defined(CSQC_DAT) || !defined(CLIENTONLY)\n/*\n\nentities never clip against themselves, or their owner\n\nline of sight checks trace->crosscontent, but bullets don't\n\n*/\n\n#define SOLID_ISTRIGGER(solid) ((solid)==SOLID_TRIGGER||(solid)==SOLID_BSPTRIGGER||(solid)==SOLID_LADDER)\n\nsize_t areagridsequence;\t//used to avoid poking the same ent twice.\n\nextern cvar_t sv_compatiblehulls;\nextern cvar_t sv_gameplayfix_linknonsolid;\n\ntypedef struct\n{\n\tvec3_t\t\tboxmins, boxmaxs;// enclose the test object along entire move\n\tfloat\t\t*mins, *maxs;\t// size of the moving object\n\tvec3_t\t\tmins2, maxs2;\t// size when clipping against mosnters\n\tfloat\t\t*start, *end;\n\ttrace_t\t\ttrace;\n\tint\t\t\ttype;\n\tint\t\t\thitcontentsmask;\n\twedict_t\t\t*passedict;\n#ifdef Q2SERVER\n\tq2edict_t\t*q2passedict;\n#endif\n\tint\t\t\thullnum;\n\tqboolean\tcapsule;\n} moveclip_t;\n\nstatic unsigned int World_ContentsOfAllLinks (world_t *w, vec3_t pos);\n/*\n===============================================================================\n\nHULL BOXES\n\n===============================================================================\n*/\n\n\nstatic\thull_t\t\tbox_hull;\nstatic\tmclipnode_t\tbox_clipnodes[6];\nstatic\tmplane_t\tbox_planes[6];\n\n/*\n===================\nSV_InitBoxHull\n\nSet up the planes and clipnodes so that the six floats of a bounding box\ncan just be stored out and get a proper hull_t structure.\n===================\n*/\nstatic void World_InitBoxHull (void)\n{\n\tint\t\ti;\n\tint\t\tside;\n\n\tbox_hull.clipnodes = box_clipnodes;\n\tbox_hull.planes = box_planes;\n\tbox_hull.firstclipnode = 0;\n\tbox_hull.lastclipnode = 5;\n\n\tfor (i=0 ; i<6 ; i++)\n\t{\n\t\tbox_clipnodes[i].planenum = i;\n\t\t\n\t\tside = i&1;\n\t\t\n\t\tbox_clipnodes[i].children[side] = Q1CONTENTS_EMPTY;\n\t\tif (i != 5)\n\t\t\tbox_clipnodes[i].children[side^1] = i + 1;\n\t\telse\n\t\t\tbox_clipnodes[i].children[side^1] = Q1CONTENTS_SOLID;\n\t\t\n\t\tbox_planes[i].type = i>>1;\n\t\tbox_planes[i].normal[i>>1] = 1;\n\t}\n\t\n}\n\n\n/*\n===================\nSV_HullForBox\n\nTo keep everything totally uniform, bounding boxes are turned into small\nBSP trees instead of being compared directly.\n===================\n*/\nhull_t\t*World_HullForBox (vec3_t mins, vec3_t maxs)\n{\n\tbox_planes[0].dist = maxs[0];\n\tbox_planes[1].dist = mins[0];\n\tbox_planes[2].dist = maxs[1];\n\tbox_planes[3].dist = mins[1];\n\tbox_planes[4].dist = maxs[2];\n\tbox_planes[5].dist = mins[2];\n\n\treturn &box_hull;\n}\n\nstatic model_t mod_capsule;\nqboolean World_BoxTrace(struct model_s *model, int hulloverride, int frame, vec3_t axis[3], vec3_t p1, vec3_t p2, vec3_t mins, vec3_t maxs, unsigned int against, struct trace_s *trace)\n{\n\thull_t *hull = &box_hull;\n\n\t//bbox vs bbox\n\t//capsule vs bbox (NYI)\n\n\tmemset (trace, 0, sizeof(trace_t));\n\ttrace->fraction = 1;\n\ttrace->allsolid = true;\n\n\tVectorCopy (p2, trace->endpos);\n\treturn Q1BSP_RecursiveHullCheck (hull, hull->firstclipnode, p1, p2, against, trace);\n}\nqboolean World_CapsuleTrace(struct model_s *model, int hulloverride, const framestate_t *framestate, const vec3_t axis[3], const vec3_t p1, const vec3_t p2, const vec3_t mins, const vec3_t maxs, qboolean capsule, unsigned int against, struct trace_s *trace)\n{\n\t//bbox vs capsule (NYI)\n\t//capsule vs capsule (NYI)\n\n\tmemset (trace, 0, sizeof(trace_t));\n\ttrace->fraction = 1;\n\ttrace->allsolid = false;\n\tVectorCopy(p2, trace->endpos);\n\n\tif (capsule)\n\t{\t//capsule vs capsule.\n\t\t//no orientation support on either (ignore axis)\n\t\tfloat sr = ((model->maxs[0]-model->mins[0]) + (model->maxs[1]-model->mins[1]))/4.0;\n\t\tfloat sh = (model->maxs[2]-model->mins[2]) - sr*2;\n\t\tfloat mr = ((maxs[0]-mins[0]) + (maxs[1]-mins[1]))/4.0;\n\t\tfloat mh = (maxs[2]-mins[2]) - mr*2;\n\t\tvec3_t sup = {0, 0, 1};\n\t\tvec3_t dir, sright;\n\t\tvec4_t nearestplane;\n\t\tfloat d1, d2;\n\t\tvec3_t nearestpoint;\n\t\tfloat neardist;\n\n\t\t//expand the static capsule's height+radius by the mover's height+radius, so that its point+capsule instead\n\t\tsr += mr;\n\t\tsh += mh;\n\n\t\tVectorSubtract(p1, p2, dir);\n\t\td2=VectorNormalize(dir);\n\t\tCrossProduct(sup, dir, sright);\n\t\tVectorNormalize(sright);\n\t\tCrossProduct(sup, sright, nearestplane);\n\t\tVectorNormalize(nearestplane);\n\t\tnearestplane[3] = DotProduct(vec3_origin, nearestplane);\t//capsule is at 0 0 0\n\t\td1 = DotProduct(nearestplane, p1) - nearestplane[3];\n\t\td2 = DotProduct(nearestplane, p2) - nearestplane[3];\n\t\td2 = -d1 /(d1+d2);\n\t\tVectorInterpolate(p1, d2, p2, nearestpoint);\n\n\t\tneardist = VectorLength(nearestpoint);\n\t\tif (neardist < sr)\n\t\t{\n\t\t\tfloat x, y, oz, nz;\n\t\t\t//sqrt(h*h-(x*x+y*y))=z\n\t\t\t//change the hypotenuse from the messed up value to the actual radius\n\t\t\t//and update z to match the changed h\n\t\t\tx = DotProduct(sup, nearestpoint) - 0;\n\t\t\ty = DotProduct(sright, nearestpoint) - 0;\n\t\t\toz = DotProduct(nearestplane, nearestpoint) - nearestplane[3];\n\t\t\tnz = sqrt(sr*sr - (x*x+y*y));\n\n\t\t\tVectorMA(nearestpoint, nz-oz, dir, trace->endpos);\n\t\t\ttrace->fraction = 0;\n\t\t}\n\t}\n\treturn false;\n}\nmodel_t *World_CapsuleForBox(const vec3_t mins, const vec3_t maxs)\n{\n\tVectorCopy(mins, mod_capsule.mins);\n\tVectorCopy(maxs, mod_capsule.maxs);\n\tmod_capsule.funcs.NativeTrace = World_CapsuleTrace;\n\treturn &mod_capsule;\n}\n/*\n===============================================================================\n\nENTITY AREA CHECKING\n\n===============================================================================\n*/\n\n#if defined(Q2SERVER) || !defined(USEAREAGRID)\n/*\n===============\nSV_CreateAreaNode\n\n===============\n*/\nstatic areanode_t *World_CreateAreaNode (world_t *w, int depth, vec3_t mins, vec3_t maxs)\n{\n\tareanode_t\t*anode;\n\tvec3_t\t\tsize;\n\tvec3_t\t\tmins1, maxs1, mins2, maxs2;\n\n\tanode = &w->areanodes[w->numareanodes];\n\tw->numareanodes++;\n\n\tClearLink (&anode->edicts);\n\n\tVectorSubtract (maxs, mins, size);\n\n\tif (depth == w->areanodedepth || (size[0] < 512 && size[1] < 512))\n\t{\n\t\tanode->axis = -1;\n\t\tanode->children[0] = anode->children[1] = NULL;\n\t\treturn anode;\n\t}\n\n\tif (size[0] > size[1])\n\t\tanode->axis = 0;\n\telse\n\t\tanode->axis = 1;\n\n\tanode->dist = 0.5 * (maxs[anode->axis] + mins[anode->axis]);\n\tVectorCopy (mins, mins1);\t\n\tVectorCopy (mins, mins2);\t\n\tVectorCopy (maxs, maxs1);\t\n\tVectorCopy (maxs, maxs2);\t\n\n\tmaxs1[anode->axis] = mins2[anode->axis] = anode->dist;\n\n\tanode->children[0] = World_CreateAreaNode (w, depth+1, mins2, maxs2);\n\tanode->children[1] = World_CreateAreaNode (w, depth+1, mins1, maxs1);\n\n\treturn anode;\n}\n\n/*\n===============\nSV_ClearWorld\n\n===============\n*/\nvoid World_ClearWorld_Nodes (world_t *w, qboolean relink)\n{\n#if !defined(USEAREAGRID)\n\tint i;\n\twedict_t *ent;\n#endif\n\tint maxdepth;\n\tvec3_t mins, maxs;\n\tif (w->worldmodel)\n\t{\n\t\tVectorCopy(w->worldmodel->mins, mins);\n\t\tVectorCopy(w->worldmodel->maxs, maxs);\n\t}\n\telse\n\t{\n\t\tVectorSet(mins, -4096, -4096, -4096);\n\t\tVectorSet(maxs, 4096, 4096, 4096);\n\t}\n\n\tWorld_InitBoxHull ();\n\n#if !defined(USEAREAGRID)\n\tmemset (&w->portallist, 0, sizeof(w->portallist));\n\tClearLink (&w->portallist.edicts);\n\tw->portallist.axis = -1;\n#endif\n\n\tmaxdepth = 8;\n\n\tif (!w->areanodes || w->areanodedepth != maxdepth)\n\t{\n\t\tZ_Free(w->areanodes);\n\t\tw->areanodedepth = maxdepth;\n\t\tw->areanodes = Z_Malloc(sizeof(*w->areanodes) * (pow(2, w->areanodedepth+1)-1));\n\t}\n\telse\n\t\tmemset (w->areanodes, 0, sizeof(*w->areanodes)*w->numareanodes);\n\tw->numareanodes = 0;\n\tWorld_CreateAreaNode (w, 0, mins, maxs);\n\n#if !defined(USEAREAGRID)\n\tif (relink)\n\t{\n\t\tfor (i=0 ; i<w->num_edicts ; i++)\n\t\t{\n\t\t\tent = WEDICT_NUM_PB(w->progs, i);\n\t\t\tif (!ent)\n\t\t\t\tcontinue;\n\t\t\tent->area.prev = ent->area.next = NULL;\n\t\t\tif (ED_ISFREE(ent))\n\t\t\t\tcontinue;\n\t\t\tWorld_LinkEdict (w, ent, false);\t// relink ents so touch functions continue to work.\n\t\t}\n\t}\n#endif\n}\n#endif\n\n#ifdef USEAREAGRID\nstatic void World_ClearWorld_AreaGrid (world_t *w, qboolean relink)\n{\n\tint numareas = 1;\n\tint i, j;\n\twedict_t *ent;\n\tvec3_t mins, maxs, size;\n\tif (w->worldmodel)\n\t{\n\t\tVectorCopy(w->worldmodel->mins, mins);\n\t\tVectorCopy(w->worldmodel->maxs, maxs);\n\t}\n\telse\n\t{\n\t\tVectorSet(mins, -4096, -4096, -4096);\n\t\tVectorSet(maxs, 4096, 4096, 4096);\n\t}\n\tVector2Set(w->gridsize, 128, 128);\n\tfor (i = 0; i < 2; i++)\n\t{\n\t\tsize[i] = maxs[i] - mins[i];\n\t\tsize[i] /= w->gridsize[i];\n\t//enforce a minimum grid size, so things don't end up getting added to every single node\n\t\tif (size[i] < 128)\n\t\t{\n\t\t\tmins[i] -= (128-size[i])/2 * w->gridsize[i];\n\t\t\tsize[i] = 128;\n\t\t}\n\t\tw->gridscale[i] = size[i];\n\t\tw->gridbias[i] = -mins[i];\n\n\t\tnumareas *= w->gridsize[i];\n\t}\n\n\tWorld_InitBoxHull ();\n\n\tif (w->gridareas)\n\t\tmemset (w->gridareas, 0, sizeof(*w->gridareas)*numareas);\n\telse\n\t\tw->gridareas = Z_Malloc(sizeof(*w->gridareas)*numareas);\n\n\tfor (i = 0; i < numareas; i++)\n\t\tClearLink (&w->gridareas[i].l);\n\tClearLink (&w->jumboarea.l);\n\tClearLink (&w->portallist.l);\n\n\n\tif (relink)\n\t{\n\t\tfor (i=0 ; i<w->num_edicts ; i++)\n\t\t{\n\t\t\tent = WEDICT_NUM_PB(w->progs, i);\n\t\t\tif (!ent)\n\t\t\t\tcontinue;\n\t\t\tfor (j = 0; j < countof(ent->gridareas); j++)\n\t\t\t{\n\t\t\t\tif (!ent->gridareas[j].l.prev)\n\t\t\t\t\tbreak;\t\t// not linked in anywhere\n\t\t\t\tClearLink(&ent->gridareas[j].l);\n\t\t\t}\n\t\t\tif (ED_ISFREE(ent))\n\t\t\t\tcontinue;\n\t\t\tWorld_LinkEdict (w, ent, false);\t// relink ents so touch functions continue to work.\n\t\t}\n\t}\n}\n#endif\n\nvoid World_ClearWorld (world_t *w, qboolean relink)\n{\n#ifdef Q2SERVER\n\tif (w == &sv.world && svs.gametype == GT_QUAKE2)\n\t\tWorld_ClearWorld_Nodes(w, relink);\n\telse\n#endif\n\t{\n#ifdef USEAREAGRID\n\t\tWorld_ClearWorld_AreaGrid(w, relink);\n#else\n\t\tWorld_ClearWorld_Nodes(w, relink);\n#endif\n\t}\n}\n\n#if !defined(USEAREAGRID)\n\n/*\n===============\nSV_UnlinkEdict\n\n===============\n*/\nvoid World_UnlinkEdict (wedict_t *ent)\n{\n\tif (!ent->area.prev)\n\t\treturn;\t\t// not linked in anywhere\n\tRemoveLink (&ent->area);\n\tent->area.prev = ent->area.next = NULL;\n}\n\n/*\n====================\nSV_TouchLinks\n====================\n*/\nvoid World_TouchLinks (world_t *w, wedict_t *ent, areanode_t *node)\n{\n\tstatic wedict_t *nodelinks[256];\t//all this means is that any more than this will not touch. probably you won't have that many valid triggers\n\tlink_t\t\t*l, *next;\n\twedict_t\t\t*touch;\n\n\tint linkcount = 0, ln;\n\n\t//work out who they are first.\n\tfor (l = node->edicts.next ; l != &node->edicts ; l = next)\n\t{\n\t\tif (linkcount == countof(nodelinks))\n\t\t\tbreak;\n\t\tnext = l->next;\n\t\ttouch = EDICT_FROM_AREA(l);\n\t\tif (touch == ent)\n\t\t\tcontinue;\n\n\t\tif (!touch->v->touch || !SOLID_ISTRIGGER(touch->v->solid))\n\t\t\tcontinue;\n\n\t\tif (ent->v->absmin[0] > touch->v->absmax[0]\n\t\t|| ent->v->absmin[1] > touch->v->absmax[1]\n\t\t|| ent->v->absmin[2] > touch->v->absmax[2]\n\t\t|| ent->v->absmax[0] < touch->v->absmin[0]\n\t\t|| ent->v->absmax[1] < touch->v->absmin[1]\n\t\t|| ent->v->absmax[2] < touch->v->absmin[2] )\n\t\t\tcontinue;\n\n\t\tif (!((int)ent->xv->dimension_solid & (int)touch->xv->dimension_hit))\n\t\t\tcontinue;\n\n\t\tnodelinks[linkcount++] = touch;\n\t}\n\n\tfor (ln = 0; ln < linkcount; ln++)\n\t{\n\t\ttouch = nodelinks[ln];\n\n\t\t//make sure nothing moved it away\n\t\tif (ED_ISFREE(touch))\n\t\t\tcontinue;\n\t\tif (!touch->v->touch || !SOLID_ISTRIGGER(touch->v->solid))\n\t\t\tcontinue;\n\n\t\tif (ent->v->absmin[0] > touch->v->absmax[0]\n\t\t|| ent->v->absmin[1] > touch->v->absmax[1]\n\t\t|| ent->v->absmin[2] > touch->v->absmax[2]\n\t\t|| ent->v->absmax[0] < touch->v->absmin[0]\n\t\t|| ent->v->absmax[1] < touch->v->absmin[1]\n\t\t|| ent->v->absmax[2] < touch->v->absmin[2] )\n\t\t\tcontinue;\n\n\t\tif (!((int)ent->xv->dimension_solid & (int)touch->xv->dimension_hit))\t//didn't change did it?...\n\t\t\tcontinue;\n\n\t\tw->Event_Touch(w, touch, ent, NULL);\n\n\t\tif (ED_ISFREE(ent))\n\t\t\tbreak;\n\t}\n\n\n// recurse down both sides\n\tif (node->axis == -1 || ED_ISFREE(ent))\n\t\treturn;\n\t\n\tif (ent->v->absmax[node->axis] > node->dist)\n\t\tWorld_TouchLinks (w, ent, node->children[0]);\n\tif (ent->v->absmin[node->axis] < node->dist)\n\t\tWorld_TouchLinks (w, ent, node->children[1]);\n}\n#endif\n\n/*\n===============\nSV_LinkEdict\n\n===============\n*/\nvoid QDECL World_LinkEdict (world_t *w, wedict_t *ent, qboolean touch_triggers)\n{\n\tpvec_t *mins;\n\tpvec_t *maxs;\n\tint solid;\n\n#ifdef USEAREAGRID\n\tWorld_UnlinkEdict (ent);\t// unlink from old position\n#else\n\tareanode_t\t*node;\n\t\n\tif (ent->area.prev)\n\t\tWorld_UnlinkEdict (ent);\t// unlink from old position\n#endif\n\t\n\tif (ent == w->edicts)\n\t\treturn;\t\t// don't add the world\n\n\tif (ED_ISFREE(ent))\n\t\treturn;\n\n\tmins = ent->v->mins;\n\tmaxs = ent->v->maxs;\n\tif (!ent->v->solid)\n\t\tent->solidsize = ES_SOLID_BSP;\n\telse\n\t{\n\t\tmodel_t *mod;\n\t\tif (ent->v->solid == SOLID_BSP)\n\t\t\tmod = w->Get_CModel(w, ent->v->modelindex);\n\t\telse\n\t\t\tmod = NULL;\n\t\tif (mod && mod->type == mod_brush)\n\t\t{\n\t\t\tmins = mod->mins;\n\t\t\tmaxs = mod->maxs;\n\t\t\tent->solidsize = ES_SOLID_BSP;\n\t\t}\n\t\telse\n\t\t\tent->solidsize = COM_EncodeSize(mins, maxs);\n\t}\n\n// set the abs box\n\tsolid = ent->v->solid;\n\tif ((solid == SOLID_BSP||solid == SOLID_BSPTRIGGER) &&\n\t(ent->v->angles[0] || ent->v->angles[1] || ent->v->angles[2]) )\n\t{\t// expand for rotation\n#if 1\n\t\tint i;\n\t\tfloat v;\n\t\tfloat max;\n\t\t//q2 method\n\t\tmax = 0;\n\t\tfor (i=0 ; i<3 ; i++)\n\t\t{\n\t\t\tv =fabs( mins[i]);\n\t\t\tif (v > max)\n\t\t\t\tmax = v;\n\t\t\tv =fabs( maxs[i]);\n\t\t\tif (v > max)\n\t\t\t\tmax = v;\n\t\t}\n\t\tfor (i=0 ; i<3 ; i++)\n\t\t{\n\t\t\tent->v->absmin[i] = ent->v->origin[i] - max;\n\t\t\tent->v->absmax[i] = ent->v->origin[i] + max;\n\t\t}\n#else\n\n\t\tint\t\t\ti;\n\n\t\tvec3_t f, r, u;\n\t\tvec3_t mn, mx;\n\n\t\t//we need to link to the correct leaves\n\n\t\tAngleVectors(ent->v->angles, f,r,u);\n\n\t\tmn[0] = DotProduct(mins, f);\n\t\tmn[1] = -DotProduct(mins, r);\n\t\tmn[2] = DotProduct(mins, u);\n\n\t\tmx[0] = DotProduct(maxs, f);\n\t\tmx[1] = -DotProduct(maxs, r);\n\t\tmx[2] = DotProduct(maxs, u);\n\t\tfor (i = 0; i < 3; i++)\n\t\t{\n\t\t\tif (mn[i] < mx[i])\n\t\t\t{\n\t\t\t\tent->v->absmin[i] = ent->v->origin[i]+mn[i]-0.1;\n\t\t\t\tent->v->absmax[i] = ent->v->origin[i]+mx[i]+0.1;\n\t\t\t}\n\t\t\telse\n\t\t\t{\t//box went inside out\n\t\t\t\tent->v->absmin[i] = ent->v->origin[i]+mx[i]-0.1;\n\t\t\t\tent->v->absmax[i] = ent->v->origin[i]+mn[i]+0.1;\n\t\t\t}\n\t\t}\n#endif\n\t}\n\telse\n\t{\n\t\tVectorAdd (ent->v->origin, mins, ent->v->absmin);\n\t\tVectorAdd (ent->v->origin, maxs, ent->v->absmax);\n\t}\n\n\t//some fancy things can mean the ent's aabb is larger than its collision box.\n#ifdef USERBE\n//\tif (ent->rbe.body.body)\n//\t\tw->rbe->ExpandBodyAABB(w->rbe, &ent->rbe.body, ent->v->absmin, env->v->absmax);\n#endif\n#ifdef SKELETALOBJECTS\n\tif (ent->xv->skeletonindex)\n\t\tskel_updateentbounds(w, ent);\n#endif\n\n//\n// to make items easier to pick up and allow them to be grabbed off\n// of shelves, the abs sizes are expanded\n//\n\tif ((int)ent->v->flags & FL_ITEM)\n\t{\n\t\tent->v->absmin[0] -= 15;\n\t\tent->v->absmin[1] -= 15;\n\t\tent->v->absmin[2] -= 1;\n\t\tent->v->absmax[0] += 15;\n\t\tent->v->absmax[1] += 15;\n\t\tent->v->absmax[2] += 1;\n\t}\n\telse\n\t{\t// because movement is clipped an epsilon away from an actual edge,\n\t\t// we must fully check even when bounding boxes don't quite touch\n\t\tent->v->absmin[0] -= 1;\n\t\tent->v->absmin[1] -= 1;\n\t\tent->v->absmin[2] -= 1;\n\t\tent->v->absmax[0] += 1;\n\t\tent->v->absmax[1] += 1;\n\t\tent->v->absmax[2] += 1;\n\t}\n\t\n// link to PVS leafs\n\tif (w->worldmodel && w->worldmodel->loadstate == MLS_LOADED && w->worldmodel->funcs.FindTouchedLeafs)\n\t{\n\t\tw->worldmodel->funcs.FindTouchedLeafs(w->worldmodel, &ent->pvsinfo, ent->v->absmin, ent->v->absmax);\n\t}\n\n\tif (ent->v->solid == SOLID_NOT && !sv_gameplayfix_linknonsolid.ival)\n\t\treturn;\n\n#ifdef USEAREAGRID\n\t// find the first node that the ent's box crosses\n\tif (ent->v->solid == SOLID_PORTAL)\n\t{\n\t\tent->gridareas[0].ed = ent;\n\t\tInsertLinkBefore (&ent->gridareas[0].l, &w->portallist.l);\n\t}\n\telse\n\t{\n\t\tint ming[2], maxg[2], g[2], ga;\n\t\tCALCAREAGRIDBOUNDS(w, ent->v->absmin, ent->v->absmax);\n\n\t\tif ((maxg[0]-ming[0])*(maxg[1]-ming[1]) > countof(ent->gridareas)\t\t\t\t//entity is too large to fit in our grid.\n\t\t\t|| ming[0]<0||ming[1]<0||maxg[0]>=w->gridsize[0]||maxg[1]>=w->gridsize[1])\t//entity crosses the boundary of the world.\n\t\t{\t//shove it in the overflow\n\t\t\tent->gridareas[0].ed = ent;\n\t\t\tInsertLinkBefore (&ent->gridareas[0].l, &w->jumboarea.l);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tfor (ga = 0, g[0] = ming[0]; g[0] < maxg[0]; g[0]++)\n\t\t\t\tfor (    g[1] = ming[1]; g[1] < maxg[1]; g[1]++, ga++)\n\t\t\t\t{\n\t\t\t\t\tent->gridareas[ga].ed = ent;\n\t\t\t\t\tInsertLinkBefore (&ent->gridareas[ga].l, &w->gridareas[g[0] + g[1]*w->gridsize[0]].l);\n\t\t\t\t}\n\t\t}\n\t}\n#else\n// find the first node that the ent's box crosses\n\tif (ent->v->solid == SOLID_PORTAL)\n\t\tnode = &w->portallist;\n\telse\n\t{\n\t\tnode = w->areanodes;\n\t\twhile (1)\n\t\t{\n\t\t\tif (node->axis == -1)\n\t\t\t\tbreak;\n\t\t\tif (ent->v->absmin[node->axis] > node->dist)\n\t\t\t\tnode = node->children[0];\n\t\t\telse if (ent->v->absmax[node->axis] < node->dist)\n\t\t\t\tnode = node->children[1];\n\t\t\telse\n\t\t\t\tbreak;\t\t// crosses the node\n\t\t}\n\t}\n\t\n// link it in\t\n\n\tInsertLinkBefore (&ent->area, &node->edicts);\n#endif\n\t\n// if touch_triggers, touch all entities at this node and decend for more\n\tif (touch_triggers && ent->v->solid != SOLID_NOT)\n\t\tWorld_TouchAllLinks (w, ent);\n}\n\n\n#ifdef Q2SERVER\nvoid VARGS WorldQ2_UnlinkEdict(world_t *w, q2edict_t *ent)\n{\n\tif (!ent->area.prev)\n\t\treturn;\t\t// not linked in anywhere\n\tRemoveLink (&ent->area);\n\tent->area.prev = ent->area.next = NULL;\n}\n\nvoid VARGS WorldQ2_LinkEdict(world_t *w, q2edict_t *ent)\n{\n\tareanode_t\t*node;\n\n\tif (ent->area.prev)\n\t\tWorldQ2_UnlinkEdict (w, ent);\t// unlink from old position\n\t\t\n\tif (ent == ge->edicts)\n\t\treturn;\t\t// don't add the world\n\n\tif (!ent->inuse)\n\t\treturn;\n\n\t// set the size\n\tVectorSubtract (ent->maxs, ent->mins, ent->size);\n\t\n\t// encode the size into the entity_state for client prediction\n\tif (ent->solid == Q2SOLID_BBOX && !(ent->svflags & SVF_DEADMONSTER))\n\t{\t// assume that x/y are equal and symetric\n\t\tent->s.solid = COM_EncodeSize(ent->mins, ent->maxs);\n\t}\n\telse if (ent->solid == Q2SOLID_BSP)\n\t{\n\t\tent->s.solid = ES_SOLID_BSP;\t\t// a solid_bbox will never create this value\n\t}\n\telse\n\t\tent->s.solid = 0;\n\n\t// set the abs box\n\tif (ent->solid == Q2SOLID_BSP && \n\t(ent->s.angles[0] || ent->s.angles[1] || ent->s.angles[2]) )\n\t{\t// expand for rotation\n\t\tfloat\t\tmax, v;\n\t\tint\t\t\ti;\n\n\t\tmax = 0;\n\t\tfor (i=0 ; i<3 ; i++)\n\t\t{\n\t\t\tv =fabs( ent->mins[i]);\n\t\t\tif (v > max)\n\t\t\t\tmax = v;\n\t\t\tv =fabs( ent->maxs[i]);\n\t\t\tif (v > max)\n\t\t\t\tmax = v;\n\t\t}\n\t\tfor (i=0 ; i<3 ; i++)\n\t\t{\n\t\t\tent->absmin[i] = ent->s.origin[i] - max;\n\t\t\tent->absmax[i] = ent->s.origin[i] + max;\n\t\t}\n\t}\n\telse\n\t{\t// normal\n\t\tVectorAdd (ent->s.origin, ent->mins, ent->absmin);\t\n\t\tVectorAdd (ent->s.origin, ent->maxs, ent->absmax);\n\t}\n\n\t// because movement is clipped an epsilon away from an actual edge,\n\t// we must fully check even when bounding boxes don't quite touch\n\tent->absmin[0] -= 1;\n\tent->absmin[1] -= 1;\n\tent->absmin[2] -= 1;\n\tent->absmax[0] += 1;\n\tent->absmax[1] += 1;\n\tent->absmax[2] += 1;\n\n// link to PVS leafs\n\t{\n\t\tpvscache_t cache;\n\t\tw->worldmodel->funcs.FindTouchedLeafs(w->worldmodel, &cache, ent->absmin, ent->absmax);\n\n\t\t//evilness: copy into the q2 state (we don't have anywhere else to store it, and there's a chance that the gamecode will care).\n\t\tent->num_clusters = cache.num_leafs;\n\t\tif (ent->num_clusters > (int)countof(ent->clusternums))\n\t\t\tent->num_clusters = (int)countof(ent->clusternums);\n\t\tmemcpy(ent->clusternums, cache.leafnums, min(sizeof(ent->clusternums), sizeof(cache.leafnums)));\n\t\tent->headnode = cache.headnode;\n\t\tent->areanum = cache.areanum;\n\t\tent->areanum2 = cache.areanum2;\n\t}\n\n\t// if first time, make sure old_origin is valid\n\tif (!ent->linkcount)\n\t{\n\t\tVectorCopy (ent->s.origin, ent->s.old_origin);\n\t}\n\tent->linkcount++;\n\n\tif (ent->solid == Q2SOLID_NOT)\n\t\treturn;\n\n// find the first node that the ent's box crosses\n\tnode = w->areanodes;\n\twhile (1)\n\t{\n\t\tif (node->axis == -1)\n\t\t\tbreak;\n\t\tif (ent->absmin[node->axis] > node->dist)\n\t\t\tnode = node->children[0];\n\t\telse if (ent->absmax[node->axis] < node->dist)\n\t\t\tnode = node->children[1];\n\t\telse\n\t\t\tbreak;\t\t// crosses the node\n\t}\n\n\t// link it in\t\n\tInsertLinkBefore (&ent->area, &node->edicts);\n}\n#endif\n\n\n\n\n/*\n===============================================================================\n\nPOINT TESTING IN HULLS\n\n===============================================================================\n*/\n\n/*\n==================\nSV_PointContents\n\n==================\n*/\nint World_PointContentsWorldOnly (world_t *w, vec3_t p)\n{\n\treturn w->worldmodel->funcs.PointContents(w->worldmodel, NULL, p);\n}\n\nint World_PointContentsAllBSPs (world_t *w, vec3_t p)\n{\n\tint c = w->worldmodel->funcs.PointContents(w->worldmodel, NULL, p);\n\tc |= World_ContentsOfAllLinks(w, p);\n\treturn c;\n}\n\n//===========================================================================\n\n/*\n============\nSV_TestEntityPosition\n\nA small wrapper around SV_BoxInSolidEntity that never clips against the\nsupplied entity.\n============\n*/\nwedict_t\t*World_TestEntityPosition (world_t *w, wedict_t *ent)\n{\n\ttrace_t\ttrace;\n\n\ttrace = World_Move (w, ent->v->origin, ent->v->mins, ent->v->maxs, ent->v->origin, ((ent->v->solid == SOLID_NOT || ent->v->solid == SOLID_TRIGGER || ent->v->solid == SOLID_BSPTRIGGER)?MOVE_NOMONSTERS:0), ent);\n\t\n\tif (trace.startsolid || trace.allsolid)\n\t\treturn trace.ent?trace.ent:w->edicts;\n\t\t\n\treturn NULL;\n}\n\n//wrapper function. Rotates the start and end positions around the angles if needed.\n//qboolean TransformedHullCheck (hull_t *hull, vec3_t start, vec3_t end, trace_t *trace, vec3_t angles)\nqboolean World_TransformedTrace (struct model_s *model, int hulloverride, framestate_t *framestate, vec3_t start, vec3_t end, vec3_t mins, vec3_t maxs, qboolean capsule, struct trace_s *trace, vec3_t origin, vec3_t angles, unsigned int hitcontentsmask)\n{\n\tvec3_t\t\tstart_l, end_l;\n\tvec3_t\t\taxis[3];\n\tqboolean\tresult;\n\n\tmemset (trace, 0, sizeof(trace_t));\n\ttrace->fraction = 1;\n\ttrace->allsolid = true;\n\ttrace->startsolid = false;\n\ttrace->inopen = true;\t//probably wrong...\n\tVectorCopy (end, trace->endpos);\n\n\tif (IS_NAN(end[0]) || IS_NAN(end[1]) || IS_NAN(end[2]))\n\t{\n\t\tCon_DPrintf(\"Nan in traceline\\n\");\n\t\treturn false;\n\t}\n\n\t// don't rotate non bsp ents. Too small to bother.\n\tif (model && model->loadstate == MLS_LOADED)\n\t{\n\t\tVectorSubtract (start, origin, start_l);\n\t\tVectorSubtract (end, origin, end_l);\n\n\t\tif (angles[0] || angles[1] || angles[2])\n\t\t{\n\t\t\tif (model->type == mod_alias)\n\t\t\t{\n\t\t\t\taxis[2][0] = angles[0] * r_meshpitch.value;\n\t\t\t\taxis[2][1] = angles[1];\n\t\t\t\taxis[2][2] = angles[2] * r_meshroll.value;\n\t\t\t\tAngleVectors (axis[2], axis[0], axis[1], axis[2]);\n\t\t\t}\n\t\t\telse\n\t\t\t\tAngleVectors (angles, axis[0], axis[1], axis[2]);\n\t\t\tVectorNegate(axis[1], axis[1]);\n\t\t\tresult = model->funcs.NativeTrace (model, hulloverride, framestate, axis, start_l, end_l, mins, maxs, capsule, hitcontentsmask, trace);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tresult = model->funcs.NativeTrace (model, hulloverride, framestate, NULL, start_l, end_l, mins, maxs, capsule, hitcontentsmask, trace);\n\t\t}\n\n\t\tVectorAdd (trace->endpos, origin, trace->endpos);\n\t}\n\telse if (hitcontentsmask & FTECONTENTS_BODY)\n\t{\n\t\thull_t *hull = &box_hull;\n\n\t\tVectorSubtract (start, origin, start_l);\n\t\tVectorSubtract (end, origin, end_l);\n\t\tVectorCopy (end_l, trace->endpos);\n\t\tresult = Q1BSP_RecursiveHullCheck (hull, hull->firstclipnode, start_l, end_l, MASK_PLAYERSOLID, trace);\n\t\tVectorAdd (trace->endpos, origin, trace->endpos);\n\t\tif (trace->contents)\n\t\t\ttrace->contents = FTECONTENTS_BODY;\n\t}\n\telse\n\t\tresult = false;\n\n\treturn result;\n}\n\n/*\n==================\nSV_ClipMoveToEntity\n\nHandles selection or creation of a clipping hull, and offseting (and\neventually rotation) of the end points\n==================\n*/\nstatic trace_t World_ClipMoveToEntity (world_t *w, wedict_t *ent, vec3_t eorg, vec3_t eang, vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, int hullnum, qboolean hitmodel, qboolean capsule, unsigned int hitcontentsmask)\t//hullnum overrides min/max for q1 style bsps\n{\n\ttrace_t\t\ttrace;\n\tmodel_t\t\t*model;\n\tint mdlidx = ent->v->modelindex;\n\tframestate_t framestate;\n\tint solid = ent->v->solid;\n\n// get the clipping hull\n\tif ((solid == SOLID_BSP || solid == SOLID_BSPTRIGGER || solid == SOLID_PORTAL) && mdlidx)\n\t{\n\t\tmodel = w->Get_CModel(w, mdlidx);\n\t\tif (!model || !model->funcs.PointContents)\n\t\t{\n//\t\t\tHost_Error(\"SOLID_BSP with non bsp model (classname: %s)\", PR_GetString(w->progs, ent->v->classname));\n\t\t\tmodel = NULL;\n\t\t}\n\t}\n\telse\n\t\tmodel = NULL;\n\n\tif (!model || model->loadstate != MLS_LOADED)\n\t{\n\t\tvec3_t boxmins, boxmaxs;\n\t\tmodel = NULL;\n\t\tVectorSubtract (ent->v->mins, maxs, boxmins);\n\t\tVectorSubtract (ent->v->maxs, mins, boxmaxs);\n\n//\t\tif (ent->xv->geomtype == GEOMTYPE_CAPSULE && !hitmodel)\n//\t\t\tmodel = World_CapsuleForBox(boxmins, boxmaxs);\n//\t\telse\n\t\t\tWorld_HullForBox(boxmins, boxmaxs);\n\t}\n\n\tw->Get_FrameState(w, ent, &framestate);\n\n// trace a line through the apropriate clipping hull\n\tif (solid == SOLID_PORTAL)\n\t{\n\t\t//solid_portal cares only about origins and as such has no mins/max\n\t\tWorld_TransformedTrace(model, 0, &framestate, start, end, vec3_origin, vec3_origin, capsule, &trace, eorg, eang, hitcontentsmask);\n\t\tif (trace.startsolid)\t//portals should not block traces. this prevents infinite looping\n\t\t\ttrace.startsolid = false;\n\t\thitmodel = false;\n\t}\n\telse if (solid == SOLID_CORPSE && w->usesolidcorpse)\n\t\tgoto scorpse;\n\telse if (ent->v->skin < 0)\n\t{\t//if forcedcontents is set, then ALL brushes in this model are forced to the specified contents value.\n\t\t//we achive this by tracing against ALL then forcing it after.\n\t\tint forcedcontents;\n\t\tsafeswitch((enum q1contents_e)(int)ent->v->skin)\n\t\t{\n\t\tcase Q1CONTENTS_EMPTY:\t\t\tforcedcontents = FTECONTENTS_EMPTY;\t\t\tbreak;\n\t\tcase Q1CONTENTS_SOLID:\t\t\tforcedcontents = FTECONTENTS_SOLID;\t\t\tbreak;\n\t\tcase Q1CONTENTS_WATER:\t\t\tforcedcontents = FTECONTENTS_WATER;\t\t\tbreak;\n\t\tcase Q1CONTENTS_SLIME:\t\t\tforcedcontents = FTECONTENTS_SLIME;\t\t\tbreak;\n\t\tcase Q1CONTENTS_LAVA:\t\t\tforcedcontents = FTECONTENTS_LAVA;\t\t\tbreak;\n\t\tcase Q1CONTENTS_SKY:\t\t\tforcedcontents = FTECONTENTS_SKY;\t\t\tbreak;\n\t\tcase Q1CONTENTS_LADDER:\t\t\tforcedcontents = FTECONTENTS_LADDER;\t\tbreak;\n\t\tcase HLCONTENTS_CLIP:\t\t\tforcedcontents = FTECONTENTS_PLAYERCLIP|FTECONTENTS_MONSTERCLIP;break;\n\t\tcase HLCONTENTS_CURRENT_0:\t\tforcedcontents = FTECONTENTS_WATER|Q2CONTENTS_CURRENT_0;\t\tbreak;\n\t\tcase HLCONTENTS_CURRENT_90:\t\tforcedcontents = FTECONTENTS_WATER|Q2CONTENTS_CURRENT_90;\t\tbreak;\n\t\tcase HLCONTENTS_CURRENT_180:\tforcedcontents = FTECONTENTS_WATER|Q2CONTENTS_CURRENT_180;\t\tbreak;\n\t\tcase HLCONTENTS_CURRENT_270:\tforcedcontents = FTECONTENTS_WATER|Q2CONTENTS_CURRENT_270;\t\tbreak;\n\t\tcase HLCONTENTS_CURRENT_UP:\t\tforcedcontents = FTECONTENTS_WATER|Q2CONTENTS_CURRENT_UP;\t\tbreak;\n\t\tcase HLCONTENTS_CURRENT_DOWN:\tforcedcontents = FTECONTENTS_WATER|Q2CONTENTS_CURRENT_DOWN;\t\tbreak;\n\t\tcase HLCONTENTS_TRANS:\t\t\tforcedcontents = FTECONTENTS_EMPTY;\t\t\tbreak;\n\t\tcase Q1CONTENTS_MONSTERCLIP:\tforcedcontents = FTECONTENTS_MONSTERCLIP;\tbreak;\n\t\tcase Q1CONTENTS_PLAYERCLIP:\t\tforcedcontents = FTECONTENTS_PLAYERCLIP;\tbreak;\n\t\tcase Q1CONTENTS_CORPSE:scorpse: forcedcontents = FTECONTENTS_CORPSE;\t\tbreak;\n\t\tsafedefault:\t\t\t\t\tforcedcontents = 0;\t\t\t\t\t\t\tbreak;\n\t\t}\n\t\tif (hitcontentsmask & forcedcontents)\n\t\t{\n\t\t\tWorld_TransformedTrace(model, hullnum, &framestate, start, end, mins, maxs, capsule, &trace, eorg, eang, ~0u);\n\t\t\tif (trace.contents)\n\t\t\t\ttrace.contents = forcedcontents;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tmemset (&trace, 0, sizeof(trace_t));\n\t\t\ttrace.fraction = trace.truefraction = 1;\n\t\t\ttrace.allsolid = false;\n\t\t\ttrace.startsolid = false;\n\t\t\ttrace.inopen = true;\t//probably wrong...\n\t\t\tVectorCopy (end, trace.endpos);\n\t\t}\n\t}\n\telse\n\t\tWorld_TransformedTrace(model, hullnum, &framestate, start, end, mins, maxs, capsule, &trace, eorg, eang, hitcontentsmask);\n\n// if using hitmodel, we know it hit the bounding box, so try a proper trace now.\n\tif (hitmodel && (trace.fraction != 1 || trace.startsolid) && !model)\n\t{\n\t\t//okay, we hit the bbox\n\t\tmodel = w->Get_CModel(w, mdlidx);\n\n\t\tif (model && model->funcs.NativeTrace && model->loadstate == MLS_LOADED)\n\t\t{\n\t\t\t//do the second trace, using the actual mesh.\n\t\t\tWorld_TransformedTrace(model, hullnum, &framestate, start, end, mins, maxs, capsule, &trace, eorg, eang, hitcontentsmask);\n\t\t}\n\t}\n\n// did we clip the move?\n\tif (trace.fraction < 1 || trace.startsolid || trace.allsolid)\n\t\ttrace.ent = ent;\n\n\treturn trace;\n}\n\n#ifdef USEAREAGRID\n\n/*\n================\nSV_AreaEdicts\n================\n*/\nint World_AreaEdicts (world_t *w, vec3_t mins, vec3_t maxs, wedict_t **list, int maxcount, int areatype)\n{\n\twedict_t *check;\n\tareagridlink_t *start, *l;\n\tsize_t count = 0;\n\tint ming[2], maxg[2], g[2], ga;\n\tCALCAREAGRIDBOUNDS(w, mins, maxs);\n\n\tareagridsequence++;\n\n\t//check ents that are just too large first\n\tstart = &w->jumboarea;\n\tfor (l=(areagridlink_t*)start->l.next  ; l != start ; l = (areagridlink_t*)l->l.next)\n\t{\n\t\tcheck = l->ed;\n\n//\t\tif (check->gridareasequence == areagridsequence)\n//\t\t\tcontinue;\n\t\tcheck->gridareasequence = areagridsequence;\n\t\n\t\tif (areatype != AREA_ALL)\n\t\t{\n\t\t\tif (check->v->solid == SOLID_NOT)\n\t\t\t\tcontinue;\t\t// deactivated\n\n\t\t\tif ((check->v->solid == SOLID_TRIGGER||check->v->solid == SOLID_BSPTRIGGER) != (areatype == AREA_TRIGGER))\n\t\t\t\tcontinue;\n\t\t}\n\n\t\tif (check->v->absmin[0] > maxs[0]\n\t\t|| check->v->absmin[1] > maxs[1]\n\t\t|| check->v->absmin[2] > maxs[2]\n\t\t|| check->v->absmax[0] < mins[0]\n\t\t|| check->v->absmax[1] < mins[1]\n\t\t|| check->v->absmax[2] < mins[2])\n\t\t\tcontinue;\t\t// not touching\n\n\t\tif (count == maxcount)\n\t\t{\n\t\t\tCon_DPrintf (\"World_AreaEdicts: MAXCOUNT\\n\");\n\t\t\treturn count;\n\t\t}\n\n\t\tlist[count] = check;\n\t\tcount++;\n\t}\n\n\t//check the actual grid now.\n\tfor (ga = 0, g[0] = ming[0]; g[0] < maxg[0]; g[0]++)\n\t{\n\t\tfor (    g[1] = ming[1]; g[1] < maxg[1]; g[1]++, ga++)\n\t\t{\n\t\t\tstart = &w->gridareas[g[0] + g[1]*w->gridsize[0]];\n\t\t\tfor (l=(areagridlink_t*)start->l.next  ; l != start ; l = (areagridlink_t*)l->l.next)\n\t\t\t{\n\t\t\t\tcheck = l->ed;\n\n\t\t\t\tif (check->gridareasequence == areagridsequence)\n\t\t\t\t\tcontinue;\n\t\t\t\tcheck->gridareasequence = areagridsequence;\n\t\t\t\n\t\t\t\tif (areatype != AREA_ALL)\n\t\t\t\t{\n\t\t\t\t\tif (check->v->solid == SOLID_NOT)\n\t\t\t\t\t\tcontinue;\t\t// deactivated\n\n\t\t\t\t\tif ((check->v->solid == SOLID_TRIGGER||check->v->solid == SOLID_BSPTRIGGER) != (areatype == AREA_TRIGGER))\n\t\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tif (check->v->absmin[0] > maxs[0]\n\t\t\t\t|| check->v->absmin[1] > maxs[1]\n\t\t\t\t|| check->v->absmin[2] > maxs[2]\n\t\t\t\t|| check->v->absmax[0] < mins[0]\n\t\t\t\t|| check->v->absmax[1] < mins[1]\n\t\t\t\t|| check->v->absmax[2] < mins[2])\n\t\t\t\t\tcontinue;\t\t// not touching\n\n\t\t\t\tif (count == maxcount)\n\t\t\t\t{\n\t\t\t\t\tCon_Printf (\"World_AreaEdicts: MAXCOUNT\\n\");\n\t\t\t\t\treturn count;\n\t\t\t\t}\n\n\t\t\t\tlist[count] = check;\n\t\t\t\tcount++;\n\t\t\t}\n\t\t}\n\t}\n\treturn count;\n}\n\n#else\nstatic float\t*area_mins, *area_maxs;\nstatic wedict_t\t**area_list;\n#ifdef Q2SERVER\nstatic q2edict_t\t**area_q2list;\n#endif\nstatic int\t\tarea_count, area_maxcount;\nstatic int\t\tarea_type;\nstatic void World_AreaEdicts_r (areanode_t *node)\n{\n\tlink_t\t\t*l, *next, *start;\n\twedict_t\t\t*check;\n\n\t// touch linked edicts\n\tstart = &node->edicts;\n\n\tfor (l=start->next  ; l != start ; l = next)\n\t{\n\t\tnext = l->next;\n\t\tcheck = EDICT_FROM_AREA(l);\n\n\t\tif (check->v->solid == SOLID_NOT)\n\t\t\tcontinue;\t\t// deactivated\n\n\t\t/*q2 still has solid/trigger lists, emulate that here*/\n\t\tif ((check->v->solid == SOLID_TRIGGER||check->v->solid == SOLID_BSPTRIGGER) != (area_type == AREA_TRIGGER))\n\t\t\tcontinue;\n\n\t\tif (check->v->absmin[0] > area_maxs[0]\n\t\t|| check->v->absmin[1] > area_maxs[1]\n\t\t|| check->v->absmin[2] > area_maxs[2]\n\t\t|| check->v->absmax[0] < area_mins[0]\n\t\t|| check->v->absmax[1] < area_mins[1]\n\t\t|| check->v->absmax[2] < area_mins[2])\n\t\t\tcontinue;\t\t// not touching\n\n\t\tif (area_count == area_maxcount)\n\t\t{\n\t\t\tCon_Printf (\"SV_AreaEdicts: MAXCOUNT\\n\");\n\t\t\treturn;\n\t\t}\n\n\t\tarea_list[area_count] = check;\n\t\tarea_count++;\n\t}\n\t\n\tif (node->axis == -1)\n\t\treturn;\t\t// terminal node\n\n\t// recurse down both sides\n\tif ( area_maxs[node->axis] > node->dist )\n\t\tWorld_AreaEdicts_r ( node->children[0] );\n\tif ( area_mins[node->axis] < node->dist )\n\t\tWorld_AreaEdicts_r ( node->children[1] );\n}\n\n/*\n================\nSV_AreaEdicts\n================\n*/\nint World_AreaEdicts (world_t *w, vec3_t mins, vec3_t maxs, wedict_t **list, int maxcount, int areatype)\n{\n\tarea_mins = mins;\n\tarea_maxs = maxs;\n\tarea_list = list;\n\tarea_count = 0;\n\tarea_maxcount = maxcount;\n\tarea_type = areatype;\n\n\tWorld_AreaEdicts_r (w->areanodes);\n\n\treturn area_count;\n}\n#endif\n\n#ifdef Q2SERVER\nconst float\t*area_mins, *area_maxs;\nq2edict_t\t**area_q2list;\nint\t\tarea_count, area_maxcount;\nint\t\tarea_type;\nstatic void WorldQ2_AreaEdicts_r (areanode_t *node)\n{\n\tlink_t\t\t*l, *next, *start;\n\tq2edict_t\t\t*check;\n\n\t// touch linked edicts\n\tstart = &node->edicts;\n\n\tfor (l=start->next  ; l != start ; l = next)\n\t{\n\t\tif (!l)\n\t\t{\n\t\t\tint i;\n\t\t\tWorld_ClearWorld(&sv.world, false);\n\t\t\tcheck = ge->edicts;\n\t\t\tfor (i = 0; i < ge->num_edicts; i++, check = (q2edict_t\t*)((char *)check + ge->edict_size))\n\t\t\t\tmemset(&check->area, 0, sizeof(check->area));\n\t\t\tCon_Printf (\"SV_AreaEdicts: Bad links\\n\");\n\t\t\treturn;\n\t\t}\n\t\tnext = l->next;\n\t\tcheck = Q2EDICT_FROM_AREA(l);\n\n\t\tif (check->solid == Q2SOLID_NOT)\n\t\t\tcontinue;\t\t// deactivated\n\n\t\t/*q2 still has solid/trigger lists, emulate that here*/\n\t\tif ((check->solid == Q2SOLID_TRIGGER) != (area_type == AREA_TRIGGER))\n\t\t\tcontinue;\n\n\t\tif (check->absmin[0] > area_maxs[0]\n\t\t|| check->absmin[1] > area_maxs[1]\n\t\t|| check->absmin[2] > area_maxs[2]\n\t\t|| check->absmax[0] < area_mins[0]\n\t\t|| check->absmax[1] < area_mins[1]\n\t\t|| check->absmax[2] < area_mins[2])\n\t\t\tcontinue;\t\t// not touching\n\n\t\tif (area_count == area_maxcount)\n\t\t{\n\t\t\tCon_Printf (\"SV_AreaEdicts: MAXCOUNT\\n\");\n\t\t\treturn;\n\t\t}\n\n\t\tarea_q2list[area_count] = check;\n\t\tarea_count++;\n\t}\n\t\n\tif (node->axis == -1)\n\t\treturn;\t\t// terminal node\n\n\t// recurse down both sides\n\tif ( area_maxs[node->axis] > node->dist )\n\t\tWorldQ2_AreaEdicts_r ( node->children[0] );\n\tif ( area_mins[node->axis] < node->dist )\n\t\tWorldQ2_AreaEdicts_r ( node->children[1] );\n}\n\nint VARGS WorldQ2_AreaEdicts (world_t *w, const vec3_t mins, const vec3_t maxs, q2edict_t **list,\n\tint maxcount, int areatype)\n{\n\tarea_mins = mins;\n\tarea_maxs = maxs;\n\tarea_q2list = list;\n\tarea_count = 0;\n\tarea_maxcount = maxcount;\n\tarea_type = areatype;\n\n\tWorldQ2_AreaEdicts_r (w->areanodes);\n\n\treturn area_count;\n}\n#endif\n\n/*\n================\nSV_HeadnodeForEntity\n\nReturns a headnode that can be used for testing or clipping an\nobject of mins/maxs size.\nOffset is filled in to contain the adjustment that must be added to the\ntesting object's origin to get a point to use with the returned hull.\n================\n*/\n\n#ifdef Q2SERVER\nstatic model_t *WorldQ2_ModelForEntity (world_t *w, q2edict_t *ent)\n{\n\tmodel_t\t*model;\n\n// decide which clipping hull to use, based on the size\n\tif (ent->solid == Q2SOLID_BSP)\n\t{\t// explicit hulls in the BSP model\n\t\tmodel = w->Get_CModel(w, ent->s.modelindex);\n\n\t\tif (!model)\n\t\t\tSV_Error (\"Q2SOLID_BSP with a non bsp model\");\n\n\t\tif (model->loadstate == MLS_LOADED)\n\t\t\treturn model;\n\t}\n\n\t// create a temp hull from bounding box sizes\n\n\treturn CM_TempBoxModel (ent->mins, ent->maxs);\n}\n#endif\n\n#ifdef Q2SERVER\nvoid WorldQ2_ClipMoveToEntities (world_t *w, moveclip_t *clip )\n{\n\tint\t\t\ti, num;\n\tq2edict_t\t\t*touchlist[MAX_Q2EDICTS], *touch;\n\ttrace_t\t\ttrace;\n\tmodel_t\t\t*model;\n\tfloat\t\t*angles;\n\n\tnum = WorldQ2_AreaEdicts (w, clip->boxmins, clip->boxmaxs, touchlist\n\t\t, MAX_Q2EDICTS, AREA_SOLID);\n\n\t// be careful, it is possible to have an entity in this\n\t// list removed before we get to it (killtriggered)\n\tfor (i=0 ; i<num ; i++)\n\t{\n\t\ttouch = touchlist[i];\n\t\tif (touch->solid == Q2SOLID_NOT)\n\t\t\tcontinue;\n\t\tif (touch == clip->q2passedict)\n\t\t\tcontinue;\n\t\tif (clip->trace.allsolid)\n\t\t\treturn;\n\t\tif (clip->q2passedict)\n\t\t{\n\t\t \tif (touch->owner == clip->q2passedict)\n\t\t\t\tcontinue;\t// don't clip against own missiles\n\t\t\tif (clip->q2passedict->owner == touch)\n\t\t\t\tcontinue;\t// don't clip against owner\n\t\t}\n\n\t\tif (touch->svflags & SVF_DEADMONSTER)\n\t\tif ( !(clip->hitcontentsmask & Q2CONTENTS_DEADMONSTER))\n\t\t\t\tcontinue;\n\n\t\t// might intersect, so do an exact clip\n\t\tmodel = WorldQ2_ModelForEntity (w, touch);\n\t\tangles = touch->s.angles;\n\t\tif (touch->solid != Q2SOLID_BSP)\n\t\t\tangles = vec3_origin;\t// boxes don't rotate\n\n\t\tif (touch->svflags & SVF_MONSTER)\n\t\t\tWorld_TransformedTrace (model, 0, NULL, clip->start, clip->end, clip->mins2, clip->maxs2, false, &trace, touch->s.origin, angles, clip->hitcontentsmask);\n\t\telse\n\t\t\tWorld_TransformedTrace (model, 0, NULL, clip->start, clip->end, clip->mins, clip->maxs, false, &trace, touch->s.origin, angles, clip->hitcontentsmask);\n\n\t\tif (trace.allsolid || trace.startsolid ||\n\t\ttrace.fraction < clip->trace.fraction)\n\t\t{\n\t\t\ttrace.ent = (edict_t *)touch;\n\t\t \tif (clip->trace.startsolid)\n\t\t\t{\n\t\t\t\tclip->trace = trace;\n\t\t\t\tclip->trace.startsolid = true;\n\t\t\t}\n\t\t\telse\n\t\t\t\tclip->trace = trace;\n\t\t}\n\t\telse if (trace.startsolid)\n\t\t\tclip->trace.startsolid = true;\n\t}\n#undef ped\n}\n#endif\n//===========================================================================\n\n//a portal is flush with a world surface behind it.\n//this causes problems. namely that we can't pass through the portal plane if the bsp behind it prevents out origin from getting through.\n//so if the trace was clipped and ended infront of the portal, continue the trace to the edges of the portal cutout instead.\nvoid World_PortalCSG(wedict_t *portal, float *trmin, float *trmax, vec3_t start, vec3_t end, trace_t *trace)\n{\n\tvec4_t planes[6];\t//far, near, right, left, up, down\n\tint plane;\n\tvec3_t worldpos;\n\tfloat bestfrac;\n\tint hitplane;\n\tfloat portalradius = portal->v->impulse;\n\t//only run this code if we impacted on the portal's parent.\n\tif (trace->fraction == 1 && !trace->startsolid)\n\t\treturn;\n\tif (!portalradius)\n\t\treturn;\n\t\n\tif (trace->startsolid)\n\t\tVectorCopy(start, worldpos);\t//make sure we use a sane valid position.\n\telse\n\t\tVectorCopy(trace->endpos, worldpos);\n\n\t//determine the csg area. normals should be facing in\n\tAngleVectors(portal->v->angles, planes[1], planes[3], planes[5]);\n\tVectorNegate(planes[1], planes[0]);\n\tVectorNegate(planes[3], planes[2]);\n\tVectorNegate(planes[5], planes[4]);\n\n\tportalradius/=2;\n\tplanes[0][3] = DotProduct(portal->v->origin, planes[0]) - (4.0/32);\n\tplanes[1][3] = DotProduct(portal->v->origin, planes[1]) - (4.0/32);\t//an epsilon beyond the portal\n\tplanes[2][3] = DotProduct(portal->v->origin, planes[2]) - portalradius;\n\tplanes[3][3] = DotProduct(portal->v->origin, planes[3]) - portalradius;\n\tplanes[4][3] = DotProduct(portal->v->origin, planes[4]) - portalradius;\n\tplanes[5][3] = DotProduct(portal->v->origin, planes[5]) - portalradius;\n\n\t//if we're actually inside the csg region\n\tfor (plane = 0; plane < 6; plane++)\n\t{\n\t\tvec3_t nearest;\n\t\tfloat d = DotProduct(worldpos, planes[plane]);\n\t\tint k;\n\t\tfor (k = 0; k < 3; k++)\n\t\t\tnearest[k] = (planes[plane][k]>=0)?trmax[k]:trmin[k];\n\t\tif (!plane)\t//front plane gets further away with side\n\t\t\tplanes[plane][3] -= DotProduct(nearest, planes[plane]);\n\t\telse if (plane>1)\t//side planes get nearer with size\n\t\t\tplanes[plane][3] += 24;//DotProduct(nearest, planes[plane]);\n\t\tif (d - planes[plane][3] >= 0)\n\t\t\tcontinue;\t//endpos is inside\n\t\telse\n\t\t\treturn;\t\t//end is already outside\n\t}\n\t//yup, we're inside, the trace shouldn't end where it actually did\n\tbestfrac = 1;\n\thitplane = -1;\n\tfor (plane = 0; plane < 6; plane++)\n\t{\n\t\tfloat ds = DotProduct(start, planes[plane]) - planes[plane][3];\n\t\tfloat de = DotProduct(end, planes[plane]) - planes[plane][3];\n\t\tfloat frac;\n\t\tif (ds >= 0 && de < 0)\n\t\t{\n\t\t\tfrac = (ds) / (ds - de);\n\t\t\tif (frac < bestfrac)\n\t\t\t{\n\t\t\t\tif (frac < 0)\n\t\t\t\t\tfrac = 0;\n\t\t\t\tbestfrac = frac;\n\t\t\t\thitplane = plane;\n\t\t\t}\n\t\t}\n\t}\n\ttrace->startsolid = trace->allsolid = false;\n\t//if we cross the front of the portal, don't shorten the trace, that will artificially clip us\n\tif (hitplane == 0 && trace->fraction > bestfrac)\n\t\treturn;\n\t//okay, elongate to clip to the portal hole properly.\n\ttrace->fraction = bestfrac;\n\tVectorInterpolate(start, bestfrac, end, trace->endpos);\n\n\tif (hitplane >= 0)\n\t{\n\t\tVectorCopy(planes[hitplane], trace->plane.normal);\n\t\ttrace->plane.dist = planes[hitplane][3];\n\t\tif (hitplane == 1)\n\t\t\ttrace->ent = portal;\n\t}\n}\n\n/*\n====================\nSV_ClipToEverything\n\nlike SV_ClipToLinks, but doesn't use the links part. This can be used for checking triggers, solid entities, not-solid entities.\nSounds pointless, I know.\n====================\n*/\nstatic void World_ClipToEverything (world_t *w, moveclip_t *clip)\n{\n\tint e;\n\ttrace_t\t\ttrace;\n\twedict_t\t\t*touch;\n\tfor (e=1 ; e<w->num_edicts ; e++)\n\t{\n\t\ttouch = (wedict_t*)EDICT_NUM_PB(w->progs, e);\n\n\t\tif (ED_ISFREE(touch))\n\t\t\tcontinue;\n\t\tif (touch->v->solid == SOLID_NOT && !((int)touch->v->flags & FL_FINDABLE_NONSOLID))\n\t\t\tcontinue;\n\t\tif ((touch->v->solid == SOLID_TRIGGER||touch->v->solid == SOLID_BSPTRIGGER) && !((int)touch->v->flags & FL_FINDABLE_NONSOLID))\n\t\t\tcontinue;\n\n\t\tif (touch == clip->passedict)\n\t\t\tcontinue;\n\n\t\tif (clip->type & MOVE_NOMONSTERS && touch->v->solid != SOLID_BSP)\n\t\t\tcontinue;\n\n\t\tif (clip->passedict)\n\t\t{\n\t\t\tif (w->usesolidcorpse)\n\t\t\t{\n\t\t\t\t// don't clip corpse against character\n\t\t\t\tif (clip->passedict->v->solid == SOLID_CORPSE && (touch->v->solid == SOLID_SLIDEBOX || touch->v->solid == SOLID_CORPSE))\n\t\t\t\t\tcontinue;\n\t\t\t\t// don't clip character against corpse\n\t\t\t\tif (clip->passedict->v->solid == SOLID_SLIDEBOX && touch->v->solid == SOLID_CORPSE)\n\t\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (!((int)clip->passedict->xv->dimension_hit & (int)touch->xv->dimension_solid))\n\t\t\t\tcontinue;\n\t\t}\n\n\t\tif (clip->boxmins[0] > touch->v->absmax[0]\n\t\t\t\t|| clip->boxmins[1] > touch->v->absmax[1]\n\t\t\t\t|| clip->boxmins[2] > touch->v->absmax[2]\n\t\t\t\t|| clip->boxmaxs[0] < touch->v->absmin[0]\n\t\t\t\t|| clip->boxmaxs[1] < touch->v->absmin[1]\n\t\t\t\t|| clip->boxmaxs[2] < touch->v->absmin[2] )\n\t\t\tcontinue;\n\n\t\tif (clip->passedict && clip->passedict->v->size[0] && !touch->v->size[0])\n\t\t\tcontinue;\t// points never interact\n\n\t// might intersect, so do an exact clip\n//\t\tif (clip->trace.allsolid)\n//\t\t\treturn;\n\t\tif (clip->passedict)\n\t\t{\n\t\t \tif ((wedict_t*)PROG_TO_EDICT(w->progs, touch->v->owner) == clip->passedict)\n\t\t\t\tcontinue;\t// don't clip against own missiles\n\t\t\tif ((wedict_t*)PROG_TO_EDICT(w->progs, clip->passedict->v->owner) == touch)\n\t\t\t\tcontinue;\t// don't clip against owner\n\t\t}\n\n\t\tif (touch->v->solid == SOLID_PORTAL)\n\t\t{\n\t\t\t//make sure we don't hit the world if we're inside the portal\n\t\t\tWorld_PortalCSG(touch, clip->mins, clip->maxs, clip->start, clip->end, &clip->trace);\n\t\t}\n\n\t\tif ((int)touch->v->flags & FL_MONSTER)\n\t\t\ttrace = World_ClipMoveToEntity (w, touch, touch->v->origin, touch->v->angles, clip->start, clip->mins2, clip->maxs2, clip->end, clip->hullnum, clip->type & MOVE_HITMODEL, clip->capsule, clip->hitcontentsmask);\n\t\telse\n\t\t\ttrace = World_ClipMoveToEntity (w, touch, touch->v->origin, touch->v->angles, clip->start, clip->mins, clip->maxs, clip->end, clip->hullnum, clip->type & MOVE_HITMODEL, clip->capsule, clip->hitcontentsmask);\n\n\t\tif (trace.fraction < clip->trace.fraction)\n\t\t{\n\t\t\t//trace traveled less, but don't forget if we started in a solid.\n\t\t\ttrace.startsolid |= clip->trace.startsolid;\n\t\t\ttrace.allsolid |= clip->trace.allsolid;\n\n\t\t\tif (clip->type & MOVE_ENTCHAIN)\n\t\t\t{\n\t\t\t\ttouch->v->chain = EDICT_TO_PROG(w->progs, clip->trace.ent?clip->trace.ent:w->edicts);\n\t\t\t\tclip->trace.ent = touch;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (clip->trace.startsolid && !trace.startsolid)\n\t\t\t\t\ttrace.ent = clip->trace.ent;\t//something else hit earlier, that one gets the trace entity, but not the fraction. yeah, combining traces like this was always going to be weird.\n\t\t\t\telse\n\t\t\t\t\ttrace.ent = touch;\n\t\t\t\tclip->trace = trace;\n\t\t\t}\n\t\t}\n\t\telse if (trace.startsolid || trace.allsolid)\n\t\t{\n\t\t\t//even if the trace traveled less, we still care if it was in a solid.\n\t\t\tclip->trace.startsolid |= trace.startsolid;\n\t\t\tclip->trace.allsolid |= trace.allsolid;\n\t\t\tclip->trace.contents |= trace.contents;\n\t\t\tif (!clip->trace.ent || trace.fraction == clip->trace.fraction)\t//xonotic requires that second test (DP has no check at all, which would end up reporting mismatched fraction/ent results, so yuck).\n\t\t\t{\n\t\t\t\tclip->trace.ent = touch;\n\t\t\t}\n\t\t}\n\t}\n}\n\n#ifdef USEAREAGRID\n\nvoid World_TouchAllLinks (world_t *w, wedict_t *ent)\n{\n\twedict_t *touchedicts[2048], *touch;\n\tint num, solid;\n\tnum = World_AreaEdicts(w, ent->v->absmin, ent->v->absmax, touchedicts, countof(touchedicts), AREA_TRIGGER);\n\twhile (num-- > 0)\n\t{\n\t\ttouch = touchedicts[num];\n\n\t\t//make sure nothing moved it away\n\t\tif (ED_ISFREE(touch))\n\t\t\tcontinue;\n\t\tsolid =touch->v->solid;\n\t\tif (!touch->v->touch || (solid!= SOLID_TRIGGER && solid!= SOLID_BSPTRIGGER))\n\t\t\tcontinue;\n\t\tif (touch == ent)\n\t\t\tcontinue;\n\n\t\tif (ent->v->absmin[0] > touch->v->absmax[0]\n\t\t|| ent->v->absmin[1] > touch->v->absmax[1]\n\t\t|| ent->v->absmin[2] > touch->v->absmax[2]\n\t\t|| ent->v->absmax[0] < touch->v->absmin[0]\n\t\t|| ent->v->absmax[1] < touch->v->absmin[1]\n\t\t|| ent->v->absmax[2] < touch->v->absmin[2] )\n\t\t\tcontinue;\n\n\t\tif (!((int)ent->xv->dimension_solid & (int)touch->xv->dimension_hit))\t//didn't change did it?...\n\t\t\tcontinue;\n\n\t\tif (solid == SOLID_BSPTRIGGER)\n\t\t{\n\t\t\tif (!World_ClipMoveToEntity(w, touch, touch->v->origin, touch->v->angles, ent->v->origin, ent->v->mins, ent->v->maxs, ent->v->origin, 0, false, (ent->xv->geomtype == GEOMTYPE_CAPSULE), MASK_WORLDSOLID).startsolid)\n\t\t\t\tcontinue;\n\t\t}\n\n\t\tw->Event_Touch(w, touch, ent, NULL);\n\n\t\tif (ED_ISFREE(ent))\n\t\t\tbreak;\n\t}\n}\n\nvoid World_UnlinkEdict (wedict_t *ent)\n{\n\tsize_t i;\n\tfor (i = 0; i < countof(ent->gridareas); i++)\n\t{\n\t\tif (!ent->gridareas[i].l.prev)\n\t\t\treturn;\t\t// not linked in anywhere\n\t\tRemoveLink (&ent->gridareas[i].l);\n\t\tent->gridareas[i].l.prev = ent->gridareas[i].l.next = NULL;\n\t}\n}\n\nstatic void World_ClipToLinks (world_t *w, areagridlink_t *node, moveclip_t *clip)\n{\n\tlink_t\t\t*l, *next;\n\twedict_t\t\t*touch;\n\ttrace_t\t\ttrace;\n\n// touch linked edicts\n\tfor (l = node->l.next ; l != &node->l ; l = next)\n\t{\n\t\tnext = l->next;\n\t\ttouch = ((areagridlink_t*)l)->ed;\n\n\t\tif (touch->gridareasequence == areagridsequence)\n\t\t\tcontinue;\n\t\ttouch->gridareasequence = areagridsequence;\n\n\t\tif (touch->v->solid == SOLID_NOT)\n\t\t\tcontinue;\n\t\tif (touch == clip->passedict)\n\t\t\tcontinue;\n\n\t\t/*if its a trigger, we only clip against it if the flags are aligned*/\n\t\tif (SOLID_ISTRIGGER(touch->v->solid))\n\t\t{\n\t\t\tif (!(clip->type & MOVE_TRIGGERS))\n\t\t\t\tcontinue;\n\t\t\tif (!((int)touch->v->flags & FL_FINDABLE_NONSOLID))\n\t\t\t\tcontinue;\n\t\t}\n\n\t\tif (clip->type & MOVE_LAGGED)\n\t\t{\n\t\t\t//can't touch lagged ents - we do an explicit test for them later.\n\t\t\tif (touch->entnum-1 < w->maxlagents)\n\t\t\t\tif (w->lagents[touch->entnum-1].present)\n\t\t\t\t\tcontinue;\n\t\t}\n\n\t\tif ((clip->type & MOVE_NOMONSTERS) && (touch->v->solid != SOLID_BSP && touch->v->solid != SOLID_PORTAL))\n\t\t\tcontinue;\n\n\t\tif (clip->passedict)\n\t\t{\n\t\t\tif (w->usesolidcorpse)\n\t\t\t{\n#if 1\n//\t\t\t\tif (!(clip->hitcontentsmask & ((touch->v->solid == SOLID_CORPSE)?FTECONTENTS_CORPSE:FTECONTENTS_BODY)))\n//\t\t\t\t\tcontinue;\n#else\n\t\t\t\t// don't clip corpse against character\n\t\t\t\tif (clip->passedict->v->solid == SOLID_CORPSE && (touch->v->solid == SOLID_SLIDEBOX || touch->v->solid == SOLID_CORPSE))\n\t\t\t\t\tcontinue;\n\t\t\t\t// don't clip character against corpse\n\t\t\t\tif (clip->passedict->v->solid == SOLID_SLIDEBOX && touch->v->solid == SOLID_CORPSE)\n\t\t\t\t\tcontinue;\n#endif\n\t\t\t}\n\t\t\tif (!((int)clip->passedict->xv->dimension_hit & (int)touch->xv->dimension_solid))\n\t\t\t\tcontinue;\n\t\t}\n\n\t\tif (clip->boxmins[0] > touch->v->absmax[0]\n\t\t|| clip->boxmins[1] > touch->v->absmax[1]\n\t\t|| clip->boxmins[2] > touch->v->absmax[2]\n\t\t|| clip->boxmaxs[0] < touch->v->absmin[0]\n\t\t|| clip->boxmaxs[1] < touch->v->absmin[1]\n\t\t|| clip->boxmaxs[2] < touch->v->absmin[2] )\n\t\t\tcontinue;\n\n\t\tif (clip->passedict && clip->passedict->v->size[0] && !touch->v->size[0])\n\t\t\tcontinue;\t// points never interact\n\n\t// might intersect, so do an exact clip\n//\t\tif (clip->trace.allsolid)\n//\t\t\treturn;\n\t\tif (clip->passedict)\n\t\t{\n\t\t \tif ((wedict_t*)PROG_TO_EDICT(w->progs, touch->v->owner) == clip->passedict)\n\t\t\t\tcontinue;\t// don't clip against own missiles\n\t\t\tif ((wedict_t*)PROG_TO_EDICT(w->progs, clip->passedict->v->owner) == touch)\n\t\t\t\tcontinue;\t// don't clip against owner\n\t\t}\n\n\t\tif (touch->v->solid == SOLID_PORTAL)\n\t\t{\n\t\t\t//make sure we don't hit the world if we're inside the portal\n\t\t\tWorld_PortalCSG(touch, clip->mins, clip->maxs, clip->start, clip->end, &clip->trace);\n\t\t}\n\n\t\tif ((int)touch->v->flags & FL_MONSTER)\n\t\t\ttrace = World_ClipMoveToEntity (w, touch, touch->v->origin, touch->v->angles, clip->start, clip->mins2, clip->maxs2, clip->end, clip->hullnum, clip->type & MOVE_HITMODEL, clip->capsule, clip->hitcontentsmask);\n\t\telse\n\t\t\ttrace = World_ClipMoveToEntity (w, touch, touch->v->origin, touch->v->angles, clip->start, clip->mins, clip->maxs, clip->end, clip->hullnum, clip->type & MOVE_HITMODEL, clip->capsule, clip->hitcontentsmask);\n\n\t\tif (trace.fraction < clip->trace.fraction)\n\t\t{\n\t\t\t//trace traveled less, but don't forget if we started in a solid.\n\t\t\ttrace.startsolid |= clip->trace.startsolid;\n\t\t\ttrace.allsolid |= clip->trace.allsolid;\n\n\t\t\tif (clip->type & MOVE_ENTCHAIN)\n\t\t\t{\n\t\t\t\ttouch->v->chain = EDICT_TO_PROG(w->progs, clip->trace.ent?clip->trace.ent:w->edicts);\n\t\t\t\tclip->trace.ent = touch;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (clip->trace.startsolid && !trace.startsolid)\n\t\t\t\t\ttrace.ent = clip->trace.ent;\t//something else hit earlier, that one gets the trace entity, but not the fraction. yeah, combining traces like this was always going to be weird.\n\t\t\t\telse\n\t\t\t\t\ttrace.ent = touch;\n\t\t\t\tclip->trace = trace;\n\t\t\t}\n\t\t}\n\t\telse if (trace.startsolid || trace.allsolid)\n\t\t{\n\t\t\t//even if the trace traveled less, we still care if it was in a solid.\n\t\t\tclip->trace.startsolid |= trace.startsolid;\n\t\t\tclip->trace.allsolid |= trace.allsolid;\n\t\t\tclip->trace.contents |= trace.contents;\n\t\t\tif (!clip->trace.ent || trace.fraction == clip->trace.fraction)\t//xonotic requires that second test (DP has no check at all, which would end up reporting mismatched fraction/ent results, so yuck).\n\t\t\t{\n\t\t\t\tclip->trace.ent = touch;\n\t\t\t}\n\t\t}\n\t}\n}\nstatic void World_ClipToAllLinks (world_t *w, moveclip_t *clip)\n{\n\tint ming[2], maxg[2], g[2];\n\tareagridsequence++;\n\tWorld_ClipToLinks(w, &w->jumboarea, clip);\n\n\tCALCAREAGRIDBOUNDS(w, clip->boxmins, clip->boxmaxs);\n\n\tfor (    g[0] = ming[0]; g[0] < maxg[0]; g[0]++)\n\t\tfor (g[1] = ming[1]; g[1] < maxg[1]; g[1]++)\n\t\t{\n\t\t\tWorld_ClipToLinks(w, &w->gridareas[g[0] + g[1]*w->gridsize[0]], clip);\n\t\t}\n}\n\nstatic unsigned int World_ContentsOfLinks (world_t *w, areagridlink_t *node, vec3_t pos)\n{\n\tlink_t\t\t*l, *next;\n\twedict_t\t\t*touch;\n\n\tmodel_t\t\t*model;\n\tint mdlidx;\n\tvec3_t pos_l, axis[3];\n\tunsigned int ret = 0, c;\n\n// touch linked edicts\n\tfor (l = node->l.next ; l != &node->l ; l = next)\n\t{\n\t\tnext = l->next;\n\t\ttouch = ((areagridlink_t*)l)->ed;\n\n\t\tif (touch->gridareasequence == areagridsequence)\n\t\t\tcontinue;\n\t\ttouch->gridareasequence = areagridsequence;\n\n\t\tif (touch->v->solid != SOLID_BSP)\n\t\t\tcontinue;\n\n\t\tif (   pos[0] > touch->v->absmax[0]\n\t\t\t|| pos[1] > touch->v->absmax[1]\n\t\t\t|| pos[2] > touch->v->absmax[2]\n\t\t\t|| pos[0] < touch->v->absmin[0]\n\t\t\t|| pos[1] < touch->v->absmin[1]\n\t\t\t|| pos[2] < touch->v->absmin[2] )\n\t\t\tcontinue;\n\n//\t\tif (touch->v->solid == SOLID_PORTAL)\n//\t\t\t//FIXME: recurse!\n\n\t\tmdlidx = touch->v->modelindex;\n\t\tif (!mdlidx)\n\t\t\tcontinue;\n\t\tmodel = w->Get_CModel(w, mdlidx);\n\t\tif (!model || (model->type != mod_brush && model->type != mod_heightmap) || model->loadstate != MLS_LOADED)\n\t\t\tcontinue;\n\n\t\tVectorSubtract (pos, touch->v->origin, pos_l);\n\t\tif (touch->v->angles[0] || touch->v->angles[1] || touch->v->angles[2])\n\t\t{\n\t\t\tAngleVectors (touch->v->angles, axis[0], axis[1], axis[2]);\n\t\t\tVectorNegate(axis[1], axis[1]);\n\t\t\tc = model->funcs.PointContents(model, axis, pos_l);\n\t\t}\n\t\telse\n\t\t\tc = model->funcs.PointContents(model, NULL, pos_l);\n\n\t\tif (c && touch->v->skin < 0)\n\t\t{\t//if forcedcontents is set, then ALL brushes in this model are forced to the specified contents value.\n\t\t\t//we achive this by tracing against ALL then forcing it after.\n\t\t\tunsigned int forcedcontents;\n\t\t\tsafeswitch((enum q1contents_e)(int)touch->v->skin)\n\t\t\t{\n\t\t\tcase Q1CONTENTS_EMPTY:\t\t\tforcedcontents = FTECONTENTS_EMPTY;\t\t\tbreak;\n\t\t\tcase Q1CONTENTS_SOLID:\t\t\tforcedcontents = FTECONTENTS_SOLID;\t\t\tbreak;\n\t\t\tcase Q1CONTENTS_WATER:\t\t\tforcedcontents = FTECONTENTS_WATER;\t\t\tbreak;\n\t\t\tcase Q1CONTENTS_SLIME:\t\t\tforcedcontents = FTECONTENTS_SLIME;\t\t\tbreak;\n\t\t\tcase Q1CONTENTS_LAVA:\t\t\tforcedcontents = FTECONTENTS_LAVA;\t\t\tbreak;\n\t\t\tcase Q1CONTENTS_SKY:\t\t\tforcedcontents = FTECONTENTS_SKY;\t\t\tbreak;\n\t\t\tcase HLCONTENTS_CLIP:\t\t\tforcedcontents = FTECONTENTS_PLAYERCLIP|FTECONTENTS_MONSTERCLIP;\tbreak;\n\t\t\tcase HLCONTENTS_CURRENT_0:\t\tforcedcontents = FTECONTENTS_WATER|Q2CONTENTS_CURRENT_0;\t\t\tbreak;\n\t\t\tcase HLCONTENTS_CURRENT_90:\t\tforcedcontents = FTECONTENTS_WATER|Q2CONTENTS_CURRENT_90;\t\t\tbreak;\n\t\t\tcase HLCONTENTS_CURRENT_180:\tforcedcontents = FTECONTENTS_WATER|Q2CONTENTS_CURRENT_180;\t\t\tbreak;\n\t\t\tcase HLCONTENTS_CURRENT_270:\tforcedcontents = FTECONTENTS_WATER|Q2CONTENTS_CURRENT_270;\t\t\tbreak;\n\t\t\tcase HLCONTENTS_CURRENT_UP:\t\tforcedcontents = FTECONTENTS_WATER|Q2CONTENTS_CURRENT_UP;\t\t\tbreak;\n\t\t\tcase HLCONTENTS_CURRENT_DOWN:\tforcedcontents = FTECONTENTS_WATER|Q2CONTENTS_CURRENT_DOWN;\t\t\tbreak;\n\t\t\tcase HLCONTENTS_TRANS:\t\t\tforcedcontents = FTECONTENTS_EMPTY;\t\t\tbreak;\n\t\t\tcase Q1CONTENTS_LADDER:\t\t\tforcedcontents = FTECONTENTS_LADDER;\t\tbreak;\n\t\t\tcase Q1CONTENTS_MONSTERCLIP:\tforcedcontents = FTECONTENTS_MONSTERCLIP;\tbreak;\n\t\t\tcase Q1CONTENTS_PLAYERCLIP:\t\tforcedcontents = FTECONTENTS_PLAYERCLIP;\tbreak;\n\t\t\tcase Q1CONTENTS_CORPSE:\t\t\tforcedcontents = FTECONTENTS_CORPSE;\t\tbreak;\n\t\t\tsafedefault:\t\t\t\t\tforcedcontents = 0;\t\t\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tc = forcedcontents;\n\t\t}\n\t\tret |= c;\n\t}\n\treturn ret;\n}\nstatic unsigned int World_ContentsOfAllLinks (world_t *w, vec3_t pos)\n{\n\tint ming[2], maxg[2], g[2];\n\tunsigned int ret;\n\tareagridsequence++;\n\tret = World_ContentsOfLinks(w, &w->jumboarea, pos);\n\n\tCALCAREAGRIDBOUNDS(w, pos, pos);\n\n\tfor (    g[0] = ming[0]; g[0] < maxg[0]; g[0]++)\n\t\tfor (g[1] = ming[1]; g[1] < maxg[1]; g[1]++)\n\t\t{\n\t\t\tret |= World_ContentsOfLinks(w, &w->gridareas[g[0] + g[1]*w->gridsize[0]], pos);\n\t\t}\n\treturn ret;\n}\n#else\n/*\n====================\nSV_ClipToLinks\n\nMins and maxs enclose the entire area swept by the move\n====================\n*/\nstatic void World_ClipToLinks (world_t *w, areanode_t *node, moveclip_t *clip)\n{\n\tlink_t\t\t*l, *next;\n\twedict_t\t\t*touch;\n\ttrace_t\t\ttrace;\n\n// touch linked edicts\n\tfor (l = node->edicts.next ; l != &node->edicts ; l = next)\n\t{\n\t\tnext = l->next;\n\t\ttouch = EDICT_FROM_AREA(l);\n\t\tif (touch->v->solid == SOLID_NOT)\n\t\t\tcontinue;\n\t\tif (touch == clip->passedict)\n\t\t\tcontinue;\n\n\t\t/*if its a trigger, we only clip against it if the flags are aligned*/\n\t\tif (SOLID_ISTRIGGER(touch->v->solid))\n\t\t{\n\t\t\tif (!(clip->type & MOVE_TRIGGERS))\n\t\t\t\tcontinue;\n\t\t\tif (!((int)touch->v->flags & FL_FINDABLE_NONSOLID))\n\t\t\t\tcontinue;\n\t\t}\n\n\t\tif (clip->type & MOVE_LAGGED)\n\t\t{\n\t\t\t//can't touch lagged ents - we do an explicit test for them later.\n\t\t\tif (touch->entnum-1 < w->maxlagents)\n\t\t\t\tif (w->lagents[touch->entnum-1].present)\n\t\t\t\t\tcontinue;\n\t\t}\n\n\t\tif ((clip->type & MOVE_NOMONSTERS) && (touch->v->solid != SOLID_BSP && touch->v->solid != SOLID_PORTAL))\n\t\t\tcontinue;\n\n\t\tif (clip->passedict)\n\t\t{\n\t\t\tif (w->usesolidcorpse)\n\t\t\t{\n#if 1\n//\t\t\t\tif (!(clip->hitcontentsmask & ((touch->v->solid == SOLID_CORPSE)?FTECONTENTS_CORPSE:FTECONTENTS_BODY)))\n//\t\t\t\t\tcontinue;\n#else\n\t\t\t\t// don't clip corpse against character\n\t\t\t\tif (clip->passedict->v->solid == SOLID_CORPSE && (touch->v->solid == SOLID_SLIDEBOX || touch->v->solid == SOLID_CORPSE))\n\t\t\t\t\tcontinue;\n\t\t\t\t// don't clip character against corpse\n\t\t\t\tif (clip->passedict->v->solid == SOLID_SLIDEBOX && touch->v->solid == SOLID_CORPSE)\n\t\t\t\t\tcontinue;\n#endif\n\t\t\t}\n\t\t\tif (!((int)clip->passedict->xv->dimension_hit & (int)touch->xv->dimension_solid))\n\t\t\t\tcontinue;\n\t\t}\n\n\t\tif (clip->boxmins[0] > touch->v->absmax[0]\n\t\t|| clip->boxmins[1] > touch->v->absmax[1]\n\t\t|| clip->boxmins[2] > touch->v->absmax[2]\n\t\t|| clip->boxmaxs[0] < touch->v->absmin[0]\n\t\t|| clip->boxmaxs[1] < touch->v->absmin[1]\n\t\t|| clip->boxmaxs[2] < touch->v->absmin[2] )\n\t\t\tcontinue;\n\n\t\tif (clip->passedict && clip->passedict->v->size[0] && !touch->v->size[0])\n\t\t\tcontinue;\t// points never interact\n\n\t// might intersect, so do an exact clip\n//\t\tif (clip->trace.allsolid)\n//\t\t\treturn;\n\t\tif (clip->passedict)\n\t\t{\n\t\t \tif ((wedict_t*)PROG_TO_EDICT(w->progs, touch->v->owner) == clip->passedict)\n\t\t\t\tcontinue;\t// don't clip against own missiles\n\t\t\tif ((wedict_t*)PROG_TO_EDICT(w->progs, clip->passedict->v->owner) == touch)\n\t\t\t\tcontinue;\t// don't clip against owner\n\t\t}\n\n\t\tif (touch->v->solid == SOLID_PORTAL)\n\t\t{\n\t\t\t//make sure we don't hit the world if we're inside the portal\n\t\t\tWorld_PortalCSG(touch, clip->mins, clip->maxs, clip->start, clip->end, &clip->trace);\n\t\t}\n\n\t\tif ((int)touch->v->flags & FL_MONSTER)\n\t\t\ttrace = World_ClipMoveToEntity (w, touch, touch->v->origin, touch->v->angles, clip->start, clip->mins2, clip->maxs2, clip->end, clip->hullnum, clip->type & MOVE_HITMODEL, clip->capsule, clip->hitcontentsmask);\n\t\telse\n\t\t\ttrace = World_ClipMoveToEntity (w, touch, touch->v->origin, touch->v->angles, clip->start, clip->mins, clip->maxs, clip->end, clip->hullnum, clip->type & MOVE_HITMODEL, clip->capsule, clip->hitcontentsmask);\n\n\t\tif (trace.fraction < clip->trace.fraction)\n\t\t{\n\t\t\t//trace traveled less, but don't forget if we started in a solid.\n\t\t\ttrace.startsolid |= clip->trace.startsolid;\n\t\t\ttrace.allsolid |= clip->trace.allsolid;\n\n\t\t\tif (clip->type & MOVE_ENTCHAIN)\n\t\t\t{\n\t\t\t\ttouch->v->chain = EDICT_TO_PROG(w->progs, clip->trace.ent?clip->trace.ent:w->edicts);\n\t\t\t\tclip->trace.ent = touch;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (clip->trace.startsolid && !trace.startsolid)\n\t\t\t\t\ttrace.ent = clip->trace.ent;\t//something else hit earlier, that one gets the trace entity, but not the fraction. yeah, combining traces like this was always going to be weird.\n\t\t\t\telse\n\t\t\t\t\ttrace.ent = touch;\n\t\t\t\tclip->trace = trace;\n\t\t\t}\n\t\t}\n\t\telse if (trace.startsolid || trace.allsolid)\n\t\t{\n\t\t\t//even if the trace traveled less, we still care if it was in a solid.\n\t\t\tclip->trace.startsolid |= trace.startsolid;\n\t\t\tclip->trace.allsolid |= trace.allsolid;\n\t\t\tclip->trace.contents |= trace.contents;\n\t\t\tif (!clip->trace.ent)\n\t\t\t{\n\t\t\t\tclip->trace.ent = touch;\n\t\t\t}\n\t\t}\n\t}\n\t\n// recurse down both sides\n\tif (node->axis == -1)\n\t\treturn;\n\n\tif ( clip->boxmaxs[node->axis] > node->dist )\n\t\tWorld_ClipToLinks (w, node->children[0], clip );\n\tif ( clip->boxmins[node->axis] < node->dist )\n\t\tWorld_ClipToLinks (w, node->children[1], clip );\n}\n\n\n\nstatic unsigned int World_ContentsOfLinks (world_t *w, areanode_t *node, vec3_t pos)\n{\n\tunsigned int c = 0;\n\tlink_t\t\t*l, *next;\n\twedict_t\t\t*touch;\n\ttrace_t\t\ttrace;\n\n// touch linked edicts\n\tfor (l = node->edicts.next ; l != &node->edicts ; l = next)\n\t{\n\t\tnext = l->next;\n\t\ttouch = EDICT_FROM_AREA(l);\n\t\tif (touch->v->solid == SOLID_NOT)\n\t\t\tcontinue;\n\n\t\t/*if its a trigger, we only clip against it if the flags are aligned*/\n\t\tif (touch->v->solid == SOLID_TRIGGER||touch->v->solid == SOLID_BSPTRIGGER)\n\t\t\tcontinue;\n\n\t\tif (pos[0] > touch->v->absmax[0]\n\t\t|| pos[1] > touch->v->absmax[1]\n\t\t|| pos[2] > touch->v->absmax[2]\n\t\t|| pos[0] < touch->v->absmin[0]\n\t\t|| pos[1] < touch->v->absmin[1]\n\t\t|| pos[2] < touch->v->absmin[2] )\n\t\t\tcontinue;\n\n\t\t/*if (touch->v->solid == SOLID_PORTAL)\n\t\t{\n\t\t\t//make sure we don't hit the world if we're inside the portal\n\t\t\tWorld_PortalCSG(touch, vec3_origin, vec3_origin, pos, pos, &clip->trace);\n\t\t}*/\n\n\t\ttrace = World_ClipMoveToEntity (w, touch, touch->v->origin, touch->v->angles, pos, vec3_origin, vec3_origin, pos, 0, false, false, ~0u);\n\t\tif (trace.startsolid)\n\t\t\tc |= trace.contents;\n\t}\n\n// recurse down both sides\n\tif (node->axis == -1)\n\t\treturn c;\n\n\tif ( pos[node->axis] > node->dist )\n\t\tc |= World_ContentsOfLinks (w, node->children[0], pos );\n\tif ( pos[node->axis] < node->dist )\n\t\tc |= World_ContentsOfLinks (w, node->children[1], pos );\n\n\treturn c;\n}\nstatic unsigned int World_ContentsOfAllLinks (world_t *w, vec3_t pos)\n{\n\treturn World_ContentsOfLinks(w, w->areanodes, pos);\n}\n#endif\n\n#if defined(HAVE_CLIENT) && defined(CSQC_DAT)\n//The logic of this function is seriously handicapped vs the other types of trace we could be doing.\nstatic void World_ClipToNetwork (world_t *w, moveclip_t *clip)\n{\n\tint i;\n\tpacket_entities_t *pe = cl.currentpackentities;\n\tentity_state_t\t*touch;\n\n\tunsigned int touchcontents;\n\tmodel_t *model;\n\tvec3_t bmins, bmaxs;\n\tfloat *ang;\n\ttrace_t trace;\n\tstatic framestate_t framestate;\t//meh\n\tint skip;\n\n\tif ((clip->type & MOVE_ENTCHAIN) || !pe)\n\t\treturn;\n\n\t//lets say that ssqc ents are in dimension 0x1, as far as the csqc can see.\n\tif (clip->passedict)\n\t{\n\t\tif (!((int)clip->passedict->xv->dimension_hit & 1))\n\t\t\treturn;\n\t\tskip = ((csqcedict_t*)clip->passedict)->xv->entnum;\n\t}\n\telse\n\t\tskip = 0;\n\n\tfor (i = 0; i < pe->num_entities; i++)\n\t{\n\t\ttouch = &pe->entities[i];\n\t\tif (touch->number == skip)\n\t\t\tcontinue; //can happen with deltalisten or certain evil hacks.\n\n\t\tif (touch->solidsize == ES_SOLID_BSP)\n\t\t{\n\t\t\tif (touch->modelindex <= 0 || touch->modelindex >= MAX_PRECACHE_MODELS)\n\t\t\t\tcontinue;\t//erk\n\t\t\tmodel = cl.model_precache[touch->modelindex];\n\t\t\tif (!model || model->loadstate != MLS_LOADED || !model->funcs.NativeTrace)\n\t\t\t\tcontinue;\n\t\t\tVectorCopy(model->mins, bmins);\n\t\t\tVectorCopy(model->maxs, bmaxs);\n\t\t\tang = touch->angles;\n\n\t\t\tif (ang[0] || ang[1] || ang[2])\n\t\t\t{\t//expand the size to deal with rotations. lazy method.\n\t\t\t\tint i;\n\t\t\t\tfloat v;\n\t\t\t\tfloat max = 0;\n\t\t\t\tfor (i=0 ; i<3 ; i++)\n\t\t\t\t{\n\t\t\t\t\tv = fabs( bmins[i]);\n\t\t\t\t\tif (v > max)\n\t\t\t\t\t\tmax = v;\n\t\t\t\t\tv = fabs( bmaxs[i]);\n\t\t\t\t\tif (v > max)\n\t\t\t\t\t\tmax = v;\n\t\t\t\t}\n\t\t\t\tVectorSet(bmins, -max,-max,-max);\n\t\t\t\tVectorSet(bmaxs,  max, max, max);\n\t\t\t}\n\n\t\t\tsafeswitch((enum q1contents_e)touch->skinnum)\n\t\t\t{\n\t\t\tcase Q1CONTENTS_EMPTY:\t\t\ttouchcontents = 0;\t\t\t\t\t\t\tbreak;\n\t\t\tcase Q1CONTENTS_SOLID:\t\t\ttouchcontents = FTECONTENTS_SOLID;\t\t\tbreak;\n\t\t\tcase Q1CONTENTS_WATER:\t\t\ttouchcontents = FTECONTENTS_WATER;\t\t\tbreak;\n\t\t\tcase Q1CONTENTS_SLIME:\t\t\ttouchcontents = FTECONTENTS_SLIME;\t\t\tbreak;\n\t\t\tcase Q1CONTENTS_LAVA:\t\t\ttouchcontents = FTECONTENTS_LAVA;\t\t\tbreak;\n\t\t\tcase Q1CONTENTS_SKY:\t\t\ttouchcontents = FTECONTENTS_SKY;\t\t\tbreak;\n\t\t\tcase HLCONTENTS_CLIP:\t\t\ttouchcontents = FTECONTENTS_PLAYERCLIP|FTECONTENTS_MONSTERCLIP;\tbreak;\n\t\t\tcase HLCONTENTS_CURRENT_0:\t\ttouchcontents = FTECONTENTS_WATER|Q2CONTENTS_CURRENT_0;\t\t\tbreak;\n\t\t\tcase HLCONTENTS_CURRENT_90:\t\ttouchcontents = FTECONTENTS_WATER|Q2CONTENTS_CURRENT_90;\t\tbreak;\n\t\t\tcase HLCONTENTS_CURRENT_180:\ttouchcontents = FTECONTENTS_WATER|Q2CONTENTS_CURRENT_180;\t\tbreak;\n\t\t\tcase HLCONTENTS_CURRENT_270:\ttouchcontents = FTECONTENTS_WATER|Q2CONTENTS_CURRENT_270;\t\tbreak;\n\t\t\tcase HLCONTENTS_CURRENT_UP:\t\ttouchcontents = FTECONTENTS_WATER|Q2CONTENTS_CURRENT_UP;\t\tbreak;\n\t\t\tcase HLCONTENTS_CURRENT_DOWN:\ttouchcontents = FTECONTENTS_WATER|Q2CONTENTS_CURRENT_DOWN;\t\tbreak;\n\t\t\tcase HLCONTENTS_TRANS:\t\t\ttouchcontents = FTECONTENTS_EMPTY;\t\t\t\t\t\t\t\tbreak;\n\t\t\tcase Q1CONTENTS_LADDER:\t\t\ttouchcontents = FTECONTENTS_LADDER;\t\t\tbreak;\n\t\t\tcase Q1CONTENTS_MONSTERCLIP:\ttouchcontents = FTECONTENTS_MONSTERCLIP;\tbreak;\n\t\t\tcase Q1CONTENTS_PLAYERCLIP:\t\ttouchcontents = FTECONTENTS_PLAYERCLIP;\t\tbreak;\n\t\t\tcase Q1CONTENTS_CORPSE:\t\t\ttouchcontents = FTECONTENTS_CORPSE;\t\t\tbreak;\n\t\t\tsafedefault:\t\t\t\t\ttouchcontents = ~0;\t\t\t\t\t\t\tbreak;\t//could be anything... :(\n\t\t\t}\n\t\t}\n#if 1\n\t\telse\n\t\t\tcontinue;\t//only hit brush ents.\n#else\n\t\telse if (touch->solidsize == ES_SOLID_NOT)\n\t\t\tcontinue;\n\t\telse\n\t\t{\n\t\t\tif (clip->type & MOVE_NOMONSTERS)\n\t\t\t\tcontinue;\n\t\t\ttouchcontents = FTECONTENTS_BODY;\n\t\t\tmodel = NULL;\n\t\t\tCOM_DecodeSize(touch->solidsize, bmins, bmaxs);\n\t\t\tWorld_HullForBox(bmins, bmaxs);\n\t\t\tang = vec3_origin;\n\t\t}\n#endif\n\t\tif (!(clip->hitcontentsmask & touchcontents))\n\t\t\tcontinue;\n\n\t\tif (   clip->boxmins[0] > touch->origin[0]+bmaxs[0]\n\t\t\t|| clip->boxmins[1] > touch->origin[1]+bmaxs[1]\n\t\t\t|| clip->boxmins[2] > touch->origin[2]+bmaxs[2]\n\t\t\t|| clip->boxmaxs[0] < touch->origin[0]+bmins[0]\n\t\t\t|| clip->boxmaxs[1] < touch->origin[1]+bmins[1]\n\t\t\t|| clip->boxmaxs[2] < touch->origin[2]+bmins[2] )\n\t\t\tcontinue;\n\n\t\tframestate.g[FS_REG].frame[0] = touch->frame;\n\t\tframestate.g[FS_REG].lerpweight[0] = 1;\n\n\t\tif (World_TransformedTrace(model, 0, &framestate, clip->start, clip->end, clip->mins, clip->maxs, clip->capsule, &trace, touch->origin, ang, clip->hitcontentsmask))\n\t\t{\n\t// if using hitmodel, we know it hit the bounding box, so try a proper trace now.\n\t\t\t/*if (clip->type & MOVE_HITMODEL && (trace.fraction != 1 || trace.startsolid) && !model)\n\t\t\t{\n\t\t\t\t//okay, we hit the bbox\n\t\t\t\tmodel = w->Get_CModel(w, mdlidx);\n\n\t\t\t\tif (model && model->funcs.NativeTrace && model->loadstate == MLS_LOADED)\n\t\t\t\t{\n\t\t\t\t\t//do the second trace, using the actual mesh.\n\t\t\t\t\tWorld_TransformedTrace(model, hullnum, &framestate, start, end, mins, maxs, capsule, &trace, eorg, vec3_origin, hitcontentsmask);\n\t\t\t\t}\n\t\t\t}*/\n\t\t}\n\n\t\tif (touchcontents != ~0)\n\t\t\ttrace.contents = touchcontents;\n\n\t\tif (trace.fraction < clip->trace.fraction)\n\t\t{\n\t\t\t//trace traveled less, but don't forget if we started in a solid.\n\t\t\ttrace.startsolid |= clip->trace.startsolid;\n\t\t\ttrace.allsolid |= clip->trace.allsolid;\n\n\t\t\tif (clip->trace.startsolid && !trace.startsolid)\n\t\t\t\ttrace.ent = clip->trace.ent;\t//something else hit earlier, that one gets the trace entity, but not the fraction. yeah, combining traces like this was always going to be weird.\n\t\t\telse\n\t\t\t{\n\t\t\t\ttrace.ent = w->edicts;\t//misreport world\n\t\t\t\ttrace.entnum = touch->number;\t//with an ssqc ent number\n\t\t\t}\n\t\t\tclip->trace = trace;\n\t\t}\n\t\telse if (trace.startsolid || trace.allsolid)\n\t\t{\n\t\t\t//even if the trace traveled less, we still care if it was in a solid.\n\t\t\tclip->trace.startsolid |= trace.startsolid;\n\t\t\tclip->trace.allsolid |= trace.allsolid;\n\t\t\tif (!clip->trace.ent || trace.fraction == clip->trace.fraction)\t//xonotic requires that second test (DP has no check at all, which would end up reporting mismatched fraction/ent results, so yuck).\n\t\t\t{\n\t\t\t\tclip->trace.contents = trace.contents;\n\t\t\t\tclip->trace.ent = w->edicts;\t//misreport world\n\t\t\t\tclip->trace.entnum = touch->number;\t//with an ssqc ent number\n\t\t\t}\n\t\t}\n\t}\n}\n#endif\n\n/*\n==================\nSV_MoveBounds\n==================\n*/\nstatic void World_MoveBounds (vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, vec3_t boxmins, vec3_t boxmaxs)\n{\n#if 0\n// debug to test against everything\nboxmins[0] = boxmins[1] = boxmins[2] = -9999;\nboxmaxs[0] = boxmaxs[1] = boxmaxs[2] = 9999;\n#else\n\tint\t\ti;\n\t\n\tfor (i=0 ; i<3 ; i++)\n\t{\n\t\tif (end[i] > start[i])\n\t\t{\n\t\t\tboxmins[i] = start[i] + mins[i] - 1;\n\t\t\tboxmaxs[i] = end[i] + maxs[i] + 1;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tboxmins[i] = end[i] + mins[i] - 1;\n\t\t\tboxmaxs[i] = start[i] + maxs[i] + 1;\n\t\t}\n\t}\n#endif\n}\n\n#if !defined(CLIENTONLY)\nqboolean SV_AntiKnockBack(world_t *w, client_t *client)\n{\n\tint seq = client->netchan.incoming_acknowledged;\t//our outgoing sequence that was last acked (in qw, this matches the last known-good input frame)\n\tclient_frame_t *frame;\n\tedict_t *ent = client->edict;\n\tif (client->protocol != SCP_QUAKEWORLD || !client->frameunion.frames || !ent)\n\t\treturn false;\t//FIXME: support nq protocols too\n\n\t//reload player state from the journal (the input frame should already have been applied)\n\tframe = &client->frameunion.frames[seq&UPDATE_MASK];\n\tVectorCopy(frame->pmorigin, pmove.origin);\n\tVectorCopy(frame->pmvelocity, pmove.velocity);\n\tpmove.pm_type = frame->pmtype;\n\tpmove.onground = true;//FIXME\n\tpmove.jump_held = frame->pmjumpheld;\n\tpmove.waterjumptime = frame->pmwaterjumptime;\n\tpmove.onladder = frame->pmonladder;\n\n\t//stuff not regenerated properly, shouldn't really be changing much or not very significant.\n\tpmove.world = w;\n\tVectorCopy(ent->v->mins, pmove.player_mins);\n\tVectorCopy(ent->v->maxs, pmove.player_maxs);\n\tpmove.capsule = (ent->xv->geomtype == GEOMTYPE_CAPSULE);\n\tif (ent->xv->gravitydir[2] || ent->xv->gravitydir[1] || ent->xv->gravitydir[0])\n\t\tVectorCopy(ent->xv->gravitydir, pmove.gravitydir);\n\telse\n\t\tVectorCopy(w->g.defaultgravitydir, pmove.gravitydir);\n\n\t//FIXME\n\tVectorCopy(ent->v->oldorigin, pmove.safeorigin);\n\tpmove.safeorigin_known = false;\n\tpmove.jump_msec = 0;\n\tVectorClear(pmove.basevelocity);\n\n\t//and apply each more recent frame\n\twhile (++seq <= client->netchan.incoming_sequence)\n\t{\n\t\tif (frame->sequence != seq)\n\t\t\tcontinue;\t//FIXME: lost\n\n\t\tpmove.cmd = frame->cmd;\n\n//\t\tpmove.angles;\n\n//\t\tpmove.numphysent/physents;\n\n\t\tPM_PlayerMove(sv.gamespeed);\n\t}\n\treturn true;\n}\n#endif\n\n/*\n==================\nSV_Move\n==================\n*/\ntrace_t World_Move (world_t *w, vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, int type, wedict_t *passedict)\n{\n\tmoveclip_t\tclip;\n\tint\t\t\ti;\n\tint hullnum;\n\n\tmemset ( &clip, 0, sizeof ( moveclip_t ) );\n\n\tif (passedict->xv->hull && !(type & MOVE_IGNOREHULL))\n\t\thullnum = passedict->xv->hull;\n#ifdef CLIENTONLY\n\telse\n\t\thullnum = 0;\n#else\n\telse if (sv_compatiblehulls.value)\n\t\thullnum = 0;\n\telse\n\t{\n\t\tint diff;\n\t\tint best;\n\t\thullnum = 0;\n\t\tbest = 8192;\n\t\t//x/y pos/neg are assumed to be the same magnitute.\n\t\t//z pos/height are assumed to be different from all the others.\n\t\tfor (i = 0; i < MAX_MAP_HULLSM; i++)\n\t\t{\n\t\t\tif (!w->worldmodel->hulls[i].available)\n\t\t\t\tcontinue;\n#define sq(x) ((x)*(x))\n\t\t\tdiff = sq(w->worldmodel->hulls[i].clip_maxs[2] - maxs[2]) +\n\t\t\t\tsq(w->worldmodel->hulls[i].clip_mins[2] - mins[2]) +\n\t\t\t\tsq(w->worldmodel->hulls[i].clip_maxs[1] - maxs[1]) +\n\t\t\t\tsq(w->worldmodel->hulls[i].clip_mins[0] - mins[0]);\n\t\t\tif (diff < best)\n\t\t\t{\n\t\t\t\tbest = diff;\n\t\t\t\thullnum=i;\n\t\t\t}\n\t\t}\n\t\thullnum++;\n\t}\n#endif\n\n#if !defined(CLIENTONLY)\n\t//figure out where the firing player was, and re-run their input frames to calculate their position without any velocity/knockback changes.\n\t//then update the start position to compensate.\n\tif ((clip.type & MOVE_LAGGED) && w == &sv.world && passedict->entnum && passedict->entnum <= sv.allocated_client_slots && sv_antilag.ival==3)\n\t{\n\t\tvec3_t nudge;\n\t\tif (SV_AntiKnockBack(w, &svs.clients[passedict->entnum-1]))\n\t\t{\n\t\t\tVectorSubtract(pmove.origin, passedict->v->origin, nudge);\n\n\t\t\tVectorAdd(start, nudge, start);\n\t\t\tVectorAdd(end, nudge, end);\n\t\t}\n\t}\n#endif\n\n\tif (passedict->xv->hitcontentsmaski)\n\t\tclip.hitcontentsmask = passedict->xv->hitcontentsmaski;\n#ifdef HAVE_LEGACY\n\telse if (passedict->xv->dphitcontentsmask)\n\t{\n\t\tunsigned int nm=0, fl = passedict->xv->dphitcontentsmask;\n\t\tif (fl & DPCONTENTS_SOLID)\n\t\t\tnm |= FTECONTENTS_SOLID;\n\t\tif (fl & DPCONTENTS_WATER)\n\t\t\tnm |= FTECONTENTS_WATER;\n\t\tif (fl & DPCONTENTS_SLIME)\n\t\t\tnm |= FTECONTENTS_SLIME;\n\t\tif (fl & DPCONTENTS_LAVA)\n\t\t\tnm |= FTECONTENTS_LAVA;\n\t\tif (fl & DPCONTENTS_SKY)\n\t\t\tnm |= FTECONTENTS_SKY;\n\t\tif (fl & DPCONTENTS_BODY)\n\t\t\tnm |= FTECONTENTS_BODY;\n\t\tif (fl & DPCONTENTS_CORPSE)\n\t\t\tnm |= FTECONTENTS_CORPSE;\n\t\tif (fl & DPCONTENTS_NODROP)\n\t\t\tnm |= Q3CONTENTS_NODROP;\n\t\tif (fl & DPCONTENTS_PLAYERCLIP)\n\t\t\tnm |= FTECONTENTS_PLAYERCLIP;\n\t\tif (fl & DPCONTENTS_MONSTERCLIP)\n\t\t\tnm |= FTECONTENTS_MONSTERCLIP;\n\t\tif (fl & DPCONTENTS_DONOTENTER)\n\t\t\tnm |= Q3CONTENTS_DONOTENTER;\n\t\tif (fl & DPCONTENTS_BOTCLIP)\n\t\t\tnm |= Q3CONTENTS_BOTCLIP;\n//\t\tif (fl & DPCONTENTS_OPAQUE)\n//\t\t\tnm |= DPCONTENTS_OPAQUE;\n\n\t\tclip.hitcontentsmask = nm;\n\t}\n#endif\n/*#ifdef HAVE_LEGACY\n\telse if (passedict->xv->hitcontentsmask)\n\t\tclip.hitcontentsmask = passedict->xv->hitcontentsmask;\n#endif*/\n\telse if (passedict->v->solid == SOLID_SLIDEBOX)\n\t{\n\t\tif ((int)passedict->v->flags & FL_MONSTER)\n\t\t\tclip.hitcontentsmask = FTECONTENTS_SOLID|Q2CONTENTS_WINDOW | FTECONTENTS_BODY | FTECONTENTS_MONSTERCLIP; /*solid only to world*/\n\t\telse if (maxs[0] - mins[0] > 0)\n\t\t\tclip.hitcontentsmask = FTECONTENTS_SOLID|Q2CONTENTS_WINDOW | FTECONTENTS_BODY | FTECONTENTS_PLAYERCLIP;\t/*impacts playerclip*/\n\t\telse\n\t\t\tclip.hitcontentsmask = FTECONTENTS_SOLID|Q2CONTENTS_WINDOW | FTECONTENTS_BODY;\t//slidebox passes through corpses\n\t}\n\telse if (passedict->v->solid == SOLID_CORPSE && w->usesolidcorpse)\n\t\tclip.hitcontentsmask = FTECONTENTS_SOLID|Q2CONTENTS_WINDOW | FTECONTENTS_BODY;\t//corpses ignore corpses\n\telse if (passedict->v->solid == SOLID_TRIGGER||passedict->v->solid == SOLID_BSPTRIGGER)\n\t\tclip.hitcontentsmask = FTECONTENTS_SOLID|Q2CONTENTS_WINDOW | FTECONTENTS_BODY;\t//triggers ignore corpses too, apparently\n\telse\n\t\tclip.hitcontentsmask = FTECONTENTS_SOLID|Q2CONTENTS_WINDOW | FTECONTENTS_BODY | FTECONTENTS_CORPSE; //regular projectiles.\n\tclip.capsule = (passedict->xv->geomtype == GEOMTYPE_CAPSULE);\n\n\tif (type & MOVE_OTHERONLY)\n\t{\n\t\twedict_t *other = WEDICT_NUM_UB(w->progs, *w->g.other);\n\t\treturn World_ClipMoveToEntity (w, other, other->v->origin, other->v->angles, start, mins, maxs, end, hullnum, type & MOVE_HITMODEL, clip.capsule, clip.hitcontentsmask);\n\t}\n#ifdef HAVE_LEGACY\n\tif ((type&MOVE_WORLDONLY) == MOVE_WORLDONLY)\n\t{\t//for compat with DP\n\t\twedict_t *other = w->edicts;\n\t\treturn World_ClipMoveToEntity (w, other, other->v->origin, other->v->angles, start, mins, maxs, end, hullnum, type & MOVE_HITMODEL, clip.capsule, clip.hitcontentsmask);\n\t}\n#endif\n\n// clip to world\n\tclip.trace = World_ClipMoveToEntity (w, w->edicts, w->edicts->v->origin, w->edicts->v->angles, start, mins, maxs, end, hullnum, false, clip.capsule, clip.hitcontentsmask);\n\n\tclip.start = start;\n\tclip.end = end;\n\tclip.mins = mins;\n\tclip.maxs = maxs;\n\tclip.type = type;\n\tclip.passedict = (passedict!=w->edicts)?passedict:NULL;\n\tclip.hullnum = 0;//hullnum; //BUG: hexen2's SV_ClipMoveToEntity's move_ent argument is set inconsistantly. This has the effect that the SOLID_BSP's .hull field is used instead of the SOLID_BBOX entity. We can't fix this because hexen2 depends upon it - this is the 'tibet5' bug.\n#ifdef Q2SERVER\n\tclip.q2passedict = NULL;\n#endif\n\n\tif (type & MOVE_MISSILE)\n\t{\n\t\tif (type & MOVE_NOMONSTERS)\n\t\t\treturn clip.trace;\t//not sure why you'd really want this, but for the sake of dp compat...\n\n\t\tfor (i=0 ; i<3 ; i++)\n\t\t{\n\t\t\tclip.mins2[i] = -15;\n\t\t\tclip.maxs2[i] = 15;\n\t\t}\n\t}\n\telse\n\t{\n\t\tVectorCopy (mins, clip.mins2);\n\t\tVectorCopy (maxs, clip.maxs2);\n\t}\n\t\n// create the bounding box of the entire move\n\tWorld_MoveBounds (start, clip.mins2, clip.maxs2, end, clip.boxmins, clip.boxmaxs );\n\n// clip to entities\n\tif (clip.type & MOVE_EVERYTHING)\n\t\tWorld_ClipToEverything (w, &clip);\n\telse\n\t{\n\t\tif (clip.type & MOVE_LAGGED)\n\t\t{\n\t\t\tclip.type &= ~MOVE_LAGGED;\n#ifndef CLIENTONLY\n\t\t\tif (w == &sv.world)\n\t\t\t{\n\t\t\t\tif (passedict->entnum && passedict->entnum <= sv.allocated_client_slots)\n\t\t\t\t{\n\t\t\t\t\tclip.type |= MOVE_LAGGED;\n\t\t\t\t\tw->lagents = svs.clients[passedict->entnum-1].laggedents;\n\t\t\t\t\tw->maxlagents = svs.clients[passedict->entnum-1].laggedents_count;\n\t\t\t\t\tw->lagentsfrac = svs.clients[passedict->entnum-1].laggedents_frac;\n\t\t\t\t\tw->lagentstime = svs.clients[passedict->entnum-1].laggedents_time;\n\t\t\t\t}\n\t\t\t\telse if (passedict->v->owner)\n\t\t\t\t{\n\t\t\t\t\tif (passedict->v->owner && passedict->v->owner <= sv.allocated_client_slots)\n\t\t\t\t\t{\n\t\t\t\t\t\tclip.type |= MOVE_LAGGED;\n\t\t\t\t\t\tw->lagents = svs.clients[passedict->v->owner-1].laggedents;\n\t\t\t\t\t\tw->maxlagents = svs.clients[passedict->v->owner-1].laggedents_count;\n\t\t\t\t\t\tw->lagentsfrac = svs.clients[passedict->v->owner-1].laggedents_frac;\n\t\t\t\t\t\tw->lagentstime = svs.clients[passedict->v->owner-1].laggedents_time;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n#endif\n\t\t}\n\t\tif (clip.type & MOVE_LAGGED)\n\t\t{\n\t\t\ttrace_t trace;\n\t\t\twedict_t *touch;\n\t\t\tvec3_t lp, la;\n\t\t\tint j;\n\n#ifdef USEAREAGRID\n\t\t\tWorld_ClipToAllLinks (w, &clip);\n#else\n\t\t\tWorld_ClipToLinks (w, w->areanodes, &clip);\n#endif\n\n\t\t\tfor (i = 0; i < w->maxlagents; i++)\n\t\t\t{\n\t\t\t\tif (!w->lagents[i].present)\n\t\t\t\t\tcontinue;\n\t\t\t\tif (clip.trace.allsolid)\n\t\t\t\t\tbreak;\n\n\t\t\t\ttouch = (wedict_t*)EDICT_NUM_PB(w->progs, i+1);\n\t\t\t\tif (touch->v->solid == SOLID_NOT)\n\t\t\t\t\tcontinue;\n\t\t\t\tif (touch == clip.passedict)\n\t\t\t\t\tcontinue;\n\t\t\t\tif (SOLID_ISTRIGGER(touch->v->solid))\n\t\t\t\t{\n\t\t\t\t\tif (!(clip.type & MOVE_TRIGGERS))\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\tif (!((int)touch->v->flags & FL_FINDABLE_NONSOLID))\n\t\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tif (clip.type & MOVE_NOMONSTERS && touch->v->solid != SOLID_BSP)\n\t\t\t\t\tcontinue;\n\n\t\t\t\tif (clip.passedict)\n\t\t\t\t{\n\t\t\t\t\tif (w->usesolidcorpse)\n\t\t\t\t\t{\n\t\t\t\t\t\t// don't clip corpse against character\n\t\t\t\t\t\tif (clip.passedict->v->solid == SOLID_CORPSE && (touch->v->solid == SOLID_SLIDEBOX || touch->v->solid == SOLID_CORPSE))\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t// don't clip character against corpse\n\t\t\t\t\t\tif (clip.passedict->v->solid == SOLID_SLIDEBOX && touch->v->solid == SOLID_CORPSE)\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tif (!((int)clip.passedict->xv->dimension_hit & (int)touch->xv->dimension_solid))\n\t\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tVectorInterpolate(touch->v->origin, w->lagentsfrac, w->lagents[i].origin, lp);\n\t\t\t\t//I hate working with angles\n\t\t\t\tVectorSubtract(w->lagents[i].angles, touch->v->angles, la);\n\t\t\t\tfor (j = 0; j < 3; j++)\n\t\t\t\t{\n\t\t\t\t\tla[j] = (360.0/65536) * ((int)(la[j]*(65536/360.0)) & 65535);\n\t\t\t\t\tif (la[j]<-180)\n\t\t\t\t\t\tla[j] += 360;\n\t\t\t\t\tif (la[j]>180)\n\t\t\t\t\t\tla[j] -= 360;\n\t\t\t\t}\n\t\t\t\tVectorMA(touch->v->angles, 1, la, la);\n\n\t\t\t\tif (clip.boxmins[0] > lp[0]+touch->v->maxs[0]\n\t\t\t\t\t\t|| clip.boxmins[1] > lp[1]+touch->v->maxs[1]\n\t\t\t\t\t\t|| clip.boxmins[2] > lp[2]+touch->v->maxs[2]\n\t\t\t\t\t\t|| clip.boxmaxs[0] < lp[0]+touch->v->mins[0]\n\t\t\t\t\t\t|| clip.boxmaxs[1] < lp[1]+touch->v->mins[1]\n\t\t\t\t\t\t|| clip.boxmaxs[2] < lp[2]+touch->v->mins[2] )\n\t\t\t\t\tcontinue;\n\n\t\t\t\tif (clip.passedict && clip.passedict->v->size[0] && !touch->v->size[0])\n\t\t\t\t\tcontinue;\t// points never interact\n\n\t\t\t\tif (clip.passedict)\n\t\t\t\t{\n\t \t\t\t\tif ((wedict_t*)PROG_TO_EDICT(w->progs, touch->v->owner) == clip.passedict)\n\t\t\t\t\t\tcontinue;\t// don't clip against own missiles\n\t\t\t\t\tif ((wedict_t*)PROG_TO_EDICT(w->progs, clip.passedict->v->owner) == touch)\n\t\t\t\t\t\tcontinue;\t// don't clip against owner\n\t\t\t\t}\n\n\t\t\t\tif ((clip.type & MOVE_HITMODEL) && w->Event_Backdate)\n\t\t\t\t{\n\t\t\t\t\tw->Event_Backdate(w, touch, w->lagentstime);\n\t\t\t\t\ttrace = World_ClipMoveToEntity (w, touch, lp, la, clip.start, clip.mins, clip.maxs, clip.end, clip.hullnum, clip.type & MOVE_HITMODEL, clip.capsule, clip.hitcontentsmask);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\ttrace = World_ClipMoveToEntity (w, touch, lp, la, clip.start, clip.mins, clip.maxs, clip.end, clip.hullnum, clip.type & MOVE_HITMODEL, clip.capsule, clip.hitcontentsmask);\n\n\t\t\t\tif (trace.allsolid || trace.startsolid || trace.fraction < clip.trace.fraction)\n\t\t\t\t{\n\t\t\t\t\tif (clip.type & MOVE_ENTCHAIN)\n\t\t\t\t\t{\n\t\t\t\t\t\ttouch->v->chain = EDICT_TO_PROG(w->progs, clip.trace.ent?clip.trace.ent:w->edicts);\n\t\t\t\t\t\tclip.trace.ent = touch;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\ttrace.ent = touch;\n\t\t\t\t\t\tclip.trace = trace;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t/*else if (w->rbe_hasphysicsents && passedict->rbe.body.body)\n\t\t{\n\t\t\tw->rbe->Trace(w, clip.passedict, clip.start, clip.end, &clip.trace);\n\t\t}*/\n\t\telse\n\t\t{\n#ifdef USEAREAGRID\n\t\t\tWorld_ClipToAllLinks (w, &clip );\n#else\n\t\t\tWorld_ClipToLinks (w, w->areanodes, &clip );\n#endif\n\t\t}\n\t\tWorld_ClipToLinks(w, &w->portallist, &clip);\n\t}\n\n#if defined(HAVE_CLIENT) && defined(CSQC_DAT)\n\t{\n\t\textern world_t csqc_world;\n\t\tif (w == &csqc_world)\n\t\t\tWorld_ClipToNetwork(w, &clip);\n\t}\n#endif\n\n//\tif (clip.trace.startsolid)\n//\t\tclip.trace.fraction = 0;\n\n//\tif (!clip.trace.ent)\n//\t\treturn clip.trace;\n\n\treturn clip.trace;\n}\n#ifdef Q2SERVER\ntrace_t WorldQ2_Move (world_t *w, vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, int hitcontentsmask, q2edict_t *passedict)\n{\n\tmoveclip_t\tclip;\n\n\tmemset ( &clip, 0, sizeof ( moveclip_t ) );\n\n// clip to world\n\tw->worldmodel->funcs.NativeTrace(w->worldmodel, 0, NULLFRAMESTATE, NULL, start, end, mins, maxs, false, hitcontentsmask, &clip.trace);\n\tclip.trace.ent = ge->edicts;\n\n\tif (clip.trace.fraction == 0)\n\t\treturn clip.trace;\n\n\tclip.start = start;\n\tclip.end = end;\n\tclip.mins = mins;\n\tclip.maxs = maxs;\n\tclip.type = MOVE_NORMAL;\n\tclip.hitcontentsmask = hitcontentsmask;\n\tclip.passedict = NULL;\n\tclip.q2passedict = passedict;\n\n\tVectorCopy (mins, clip.mins2);\n\tVectorCopy (maxs, clip.maxs2);\n\t\n// create the bounding box of the entire move\n//FIXME: should we use clip.trace.endpos here?\t\n\tWorld_MoveBounds ( start, clip.mins2, clip.maxs2, end, clip.boxmins, clip.boxmaxs );\n\n// clip to entities\n\tWorldQ2_ClipMoveToEntities(w, &clip);\n\n\treturn clip.trace;\n}\n#endif\n\nstatic void (QDECL *world_current_physics_engine)(world_t*world);\nqboolean QDECL World_RegisterPhysicsEngine(const char *enginename, void(QDECL*startupfunc)(world_t*world))\n{\n\tif (world_current_physics_engine)\n\t\treturn false;\t//no thanks, we already have one.\n\tworld_current_physics_engine = startupfunc;\n\treturn true;\n}\nvoid World_RBE_Shutdown(world_t *world)\n{\n#ifdef USERBE\n\tunsigned int u;\n\twedict_t *ed;\n\tif (!world->rbe)\n\t\treturn;\n\n\tif (world->progs)\n\t{\n\t\tfor (u = 0; u < world->num_edicts; u++)\n\t\t{\n\t\t\ted = WEDICT_NUM_PB(world->progs, u);\n\t\t\tworld->rbe->RemoveJointFromEntity(world, ed);\n\t\t\tworld->rbe->RemoveFromEntity(world, ed);\n\t\t}\n\t}\n\tworld->rbe->End(world);\n\tworld->rbe = NULL;\n#endif\n}\nvoid QDECL World_UnregisterPhysicsEngine(const char *enginename)\n{\n#ifdef RAGDOLL\n\trag_uninstanciateall();\n#endif\n\n#if defined(CSQC_DAT) && !defined(SERVERONLY)\n\t{\n\t\textern world_t csqc_world;\n\t\tWorld_RBE_Shutdown(&csqc_world);\n\t}\n#endif\n#if !defined(CLIENTONLY)\n\tWorld_RBE_Shutdown(&sv.world);\n#endif\n\n\tworld_current_physics_engine = NULL;\n}\nvoid World_RBE_Start(world_t *world)\n{\n\tif (world_current_physics_engine)\n\t{\n\t\tif (world->worldmodel)\n\t\t\tworld_current_physics_engine(world);\n\t}\n}\n\nvoid World_Destroy(world_t *world)\n{\n\tWorld_RBE_Shutdown(world);\n\n#ifdef USEAREAGRID\n\tZ_Free(world->gridareas);\n#else\n\tZ_Free(world->areanodes);\n\tworld->areanodes = NULL;\n\tworld->areanodedepth = 0;\n#endif\n\n\tmemset(world, 0, sizeof(*world));\n}\n\n#ifdef USERBE\nstatic qboolean GenerateCollisionMesh_BSP(world_t *world, model_t *mod, wedict_t *ed, vec3_t geomcenter)\n{\n\tunsigned int sno;\n\tmsurface_t *surf;\n\tmesh_t *mesh;\n\tunsigned int numverts;\n\tunsigned int numindexes,i;\n\tint *ptr_elements;\n\tfloat *ptr_verts;\n\n\tnumverts = 0;\n\tnumindexes = 0;\n\tfor (sno = 0; sno < mod->nummodelsurfaces; sno++)\n\t{\n\t\tsurf = &mod->surfaces[sno+mod->firstmodelsurface];\n\t\tif (surf->flags & (SURF_DRAWSKY|SURF_DRAWTURB))\n\t\t\tcontinue;\n\n\t\tif (surf->mesh)\n\t\t{\n\t\t\tmesh = surf->mesh;\n\t\t\tnumverts += mesh->numvertexes;\n\t\t\tnumindexes += mesh->numindexes;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tnumverts += surf->numedges;\n\t\t\tnumindexes += (surf->numedges-2) * 3;\n\t\t}\n\t}\n\tif (!numindexes)\n\t{\n\t\tCon_DPrintf(\"entity %i (classname %s) has no geometry\\n\", NUM_FOR_EDICT(world->progs, (edict_t*)ed), PR_GetString(world->progs, ed->v->classname));\n\t\treturn false;\n\t}\n\tptr_elements = (int*)BZ_Malloc(numindexes*sizeof(*ptr_elements));\n\tptr_verts = (float*)BZ_Malloc(numverts*sizeof(vec3_t));\n\n\tnumverts = 0;\n\tnumindexes = 0;\n\tfor (sno = 0; sno < mod->nummodelsurfaces; sno++)\n\t{\n\t\tsurf = &mod->surfaces[sno+mod->firstmodelsurface];\n\t\tif (surf->flags & (SURF_DRAWSKY|SURF_DRAWTURB))\n\t\t\tcontinue;\n\n\t\tif (surf->mesh)\n\t\t{\n\t\t\tmesh = surf->mesh;\n\t\t\tfor (i = 0; i < mesh->numvertexes; i++)\n\t\t\t\tVectorSubtract(mesh->xyz_array[i], geomcenter, (ptr_verts + 3*(numverts+i)));\n\t\t\tfor (i = 0; i < mesh->numindexes; i+=3)\n\t\t\t{\n\t\t\t\t//flip the triangles as we go\n\t\t\t\tptr_elements[numindexes+i+0] = numverts+mesh->indexes[i+2];\n\t\t\t\tptr_elements[numindexes+i+1] = numverts+mesh->indexes[i+1];\n\t\t\t\tptr_elements[numindexes+i+2] = numverts+mesh->indexes[i+0];\n\t\t\t}\n\t\t\tnumverts += mesh->numvertexes;\n\t\t\tnumindexes += i;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tfloat *vec;\n\t\t\tmedge_t *edge;\n\t\t\tint lindex;\n\t\t\tfor (i = 0; i < surf->numedges; i++)\n\t\t\t{\n\t\t\t\tlindex = mod->surfedges[surf->firstedge + i];\n\n\t\t\t\tif (lindex > 0)\n\t\t\t\t{\n\t\t\t\t\tedge = &mod->edges[lindex];\n\t\t\t\t\tvec = mod->vertexes[edge->v[0]].position;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tedge = &mod->edges[-lindex];\n\t\t\t\t\tvec = mod->vertexes[edge->v[1]].position;\n\t\t\t\t}\n\t\t\t\n\t\t\t\tVectorSubtract(vec, geomcenter, (ptr_verts + 3*(numverts+i)));\n\t\t\t}\n\t\t\tfor (i = 2; i < surf->numedges; i++)\n\t\t\t{\n\t\t\t\t//quake is backwards, not ode\n\t\t\t\tptr_elements[numindexes++] = numverts+i;\n\t\t\t\tptr_elements[numindexes++] = numverts+i-1;\n\t\t\t\tptr_elements[numindexes++] = numverts;\n\t\t\t}\n\t\t\tnumverts += surf->numedges;\n\t\t}\n\t}\n\n\ted->rbe.element3i = ptr_elements;\n\ted->rbe.vertex3f = ptr_verts;\n\ted->rbe.numvertices = numverts;\n\ted->rbe.numtriangles = numindexes/3;\n\treturn true;\n}\n\n#include \"com_mesh.h\"\nstatic qboolean GenerateCollisionMesh_Alias(world_t *world, model_t *mod, wedict_t *ed, vec3_t geomcenter)\n{\n\tmesh_t mesh;\n\tunsigned int numverts;\n\tunsigned int numindexes,i;\n\tgaliasinfo_t *inf;\n\tunsigned int surfnum = 0;\n\tentity_t re;\n\tint *ptr_elements;\n\tfloat *ptr_verts;\n\n\tnumverts = 0;\n\tnumindexes = 0;\n\n\t//fill in the parts of the entity_t that Alias_GAliasBuildMesh needs.\n\tworld->Get_FrameState(world, ed, &re.framestate);\n\tre.fatness = ed->xv->fatness;\n\tre.model = mod;\n\n\tinf = (galiasinfo_t*)Mod_Extradata (mod);\n\twhile(inf)\n\t{\n\t\tnumverts += inf->numverts;\n\t\tnumindexes += inf->numindexes;\n\t\tinf = inf->nextsurf;\n\t}\n\n\tif (!numindexes)\n\t{\n\t\tCon_DPrintf(\"entity %i (classname %s) has no geometry\\n\", NUM_FOR_EDICT(world->progs, (edict_t*)ed), PR_GetString(world->progs, ed->v->classname));\n\t\treturn false;\n\t}\n\tptr_elements = (int*)BZ_Malloc(numindexes*sizeof(*ptr_elements));\n\tptr_verts = (float*)BZ_Malloc(numverts*sizeof(vec3_t));\n\n\tnumverts = 0;\n\tnumindexes = 0;\n\n\tinf = (galiasinfo_t*)Mod_Extradata (mod);\n\twhile(inf)\n\t{\n\t\tAlias_GAliasBuildMesh(&mesh, NULL, inf, surfnum++, &re, false);\n\t\tfor (i = 0; i < mesh.numvertexes; i++)\n\t\t\tVectorSubtract(mesh.xyz_array[i], geomcenter, (ptr_verts + 3*(numverts+i)));\n\t\tfor (i = 0; i < mesh.numindexes; i+=3)\n\t\t{\n\t\t\t//flip the triangles as we go\n\t\t\tptr_elements[numindexes+i+0] = numverts+mesh.indexes[i+2];\n\t\t\tptr_elements[numindexes+i+1] = numverts+mesh.indexes[i+1];\n\t\t\tptr_elements[numindexes+i+2] = numverts+mesh.indexes[i+0];\n\t\t}\n\t\tnumverts += inf->numverts;\n\t\tnumindexes += inf->numindexes;\n\t\tinf = inf->nextsurf;\n\t}\n\n\tAlias_FlushCache();\t//it got built using an entity on the stack, make sure other stuff doesn't get hurt.\n\n\ted->rbe.element3i = ptr_elements;\n\ted->rbe.vertex3f = ptr_verts;\n\ted->rbe.numvertices = numverts;\n\ted->rbe.numtriangles = numindexes/3;\n\treturn true;\n}\n\n//Bullet has a fit if we have any degenerate triangles, so make sure we can determine some surface normal\nstatic void CollisionMesh_CleanupMesh(wedict_t *ed)\n{\n\tfloat *v1, *v2, *v3;\n\tvec3_t d1, d2, cr;\n\tint in, out;\n\tfor (in = 0, out = 0; in < ed->rbe.numtriangles*3; in+=3)\n\t{\n\t\tv1 = &ed->rbe.vertex3f[ed->rbe.element3i[in+0]*3];\n\t\tv2 = &ed->rbe.vertex3f[ed->rbe.element3i[in+1]*3];\n\t\tv3 = &ed->rbe.vertex3f[ed->rbe.element3i[in+2]*3];\n\t\tVectorSubtract(v3, v1, d1);\n\t\tVectorSubtract(v2, v1, d2);\n\t\tCrossProduct(d1, d2, cr);\n\t\tif (DotProduct(cr,cr) == 0)\n\t\t\tcontinue;\n\t\ted->rbe.element3i[out+0] = ed->rbe.element3i[in+0];\n\t\ted->rbe.element3i[out+1] = ed->rbe.element3i[in+1];\n\t\ted->rbe.element3i[out+2] = ed->rbe.element3i[in+2];\n\t\tout+=3;\n\t}\n\ted->rbe.numtriangles = out/3;\n}\n\nqboolean QDECL World_GenerateCollisionMesh(world_t *world, model_t *mod, wedict_t *ed, vec3_t geomcenter)\n{\n\tqboolean result;\n\tswitch(mod->type)\n\t{\n\tcase mod_brush:\n\t\tresult = GenerateCollisionMesh_BSP(world, mod, ed, geomcenter);\n\t\tbreak;\n\tcase mod_alias:\n\t\tresult = GenerateCollisionMesh_Alias(world, mod, ed, geomcenter);\n\t\tbreak;\n\tcase mod_heightmap:\n\tcase mod_halflife:\n\tcase mod_sprite:\n\tcase mod_dummy:\n\tdefault:\n\t\treturn false;\t//panic!\n\t}\n\n\tif (result)\n\t{\n\t\tCollisionMesh_CleanupMesh(ed);\n\t\tif (ed->rbe.numtriangles > 0)\n\t\t\treturn true;\n\t}\n\treturn false;\n}\nvoid QDECL World_ReleaseCollisionMesh(wedict_t *ed)\n{\n\tBZ_Free(ed->rbe.element3i);\n\ted->rbe.element3i = NULL;\n\tBZ_Free(ed->rbe.vertex3f);\n\ted->rbe.vertex3f = NULL;\n\ted->rbe.numvertices = 0;\n\ted->rbe.numtriangles = 0;\n}\n#endif\n#endif\n"
  },
  {
    "path": "engine/shaders/Makefile",
    "content": "\r\n#NOTE: if you're on windows, you might want to use CC=i686-pc-mingw32-gcc\r\n\r\nCC ?= gcc\r\nVKSDKPATH ?= ~/VulkanSDK/1.1.73.0/x86_64/bin/\r\n\r\nall:\r\n\r\nNAMES=\tfixedemu\tfixedemu_flat altwater\tbloom_blur\tbloom_filter\tbloom_final\tcolourtint\tcrepuscular_opaque\tcrepuscular_rays\tcrepuscular_sky\tdepthonly\tdefault2d\tdefaultadditivesprite\t\tdefaultskin\tdefaultsky\tdefaultskybox\tdefaultfill\tdefaultsprite\tdefaultwall\tdefaultwarp\tdefaultgammacb\tdrawflat_wall\tlpp_depthnorm\tlpp_light\tlpp_wall\tpostproc_fisheye\tpostproc_panorama\tpostproc_laea\tpostproc_stereographic\tpostproc_equirectangular   postproc_panini\tfxaa\tunderwaterwarp\tmenutint\tterrain\trtlight\r\n\r\n\r\nALLNAMES+=$(foreach v,$(NAMES),glsl/$v.glsl)\r\nALLNAMES+=$(foreach v,$(NAMES),hlsl9/$v.hlsl)\r\nALLNAMES+=$(foreach v,$(NAMES),hlsl11/$v.hlsl)\r\nALLNAMES:=$(realpath $(ALLNAMES))\r\n\r\nVKNAMES=$(realpath $(foreach v,$(NAMES),vulkan/$v.glsl))\r\nVKNAMES+=postproc_fisheye\r\nVKNAMES+=rq_rtlight\r\nVKNAMES:=$(foreach v,$(VKNAMES),vulkanblobs/$(notdir $(basename $v)).fvb)\r\nALLNAMES+=$(VKNAMES)\r\n\r\ngeneratebuiltinsl: generatebuiltinsl.c\r\n\t$(CC) $< -o $@\r\n\r\nmakevulkanblob: makevulkanblob.c\r\n\t$(CC) $< -o $@\r\n\r\nvulkanblobs/rq_%.fvb: vulkan/%.glsl makevulkanblob vulkan/sys/defs.h vulkan/sys/fog.h vulkan/sys/offsetmapping.h vulkan/sys/skeletal.h\r\n\t@echo Making $@ from $<\r\n\t@PATH=$(PATH):$(VKSDKPATH) ./makevulkanblob $< $@ rq\r\nvulkanblobs/%.fvb: vulkan/%.glsl makevulkanblob vulkan/sys/defs.h vulkan/sys/fog.h vulkan/sys/offsetmapping.h vulkan/sys/skeletal.h\r\n\t@echo Making $@ from $<\r\n\t@PATH=$(PATH):$(VKSDKPATH) ./makevulkanblob $< $@\r\n\r\n#vulkanblobs/%.fvb: glsl/%.glsl makevulkanblob vulkan/sys/defs.h vulkan/sys/fog.h vulkan/sys/offsetmapping.h vulkan/sys/skeletal.h\r\n#\t./makevulkanblob $< $@\r\n\r\n\r\nall: generatebuiltinsl $(ALLNAMES)\r\n\t./generatebuiltinsl\r\n"
  },
  {
    "path": "engine/shaders/generatebuiltinsl.c",
    "content": "#include <stdio.h>\n#include <string.h>\n#include <stdlib.h>\n\nchar shaders[][64] =\n{\n\t\"fixedemu\",\n\t\"fixedemu_flat\",\n\t\"q3terrain\",\n\t\"altwater\",\n\t\"bloom_blur\",\n\t\"bloom_filter\",\n\t\"bloom_final\",\n\t\"colourtint\",\n\t\"crepuscular_opaque\",\n\t\"crepuscular_rays\",\n\t\"crepuscular_sky\",\n\t\"depthonly\",\n\t\"default2d\",\n\t\"default2danim\",\n\t\"defaultadditivesprite\",\n\t\"defaultskin\",\n\t\"defaultsky\",\n\t\"defaultskybox\",\n\t\"defaultfill\",\n\t\"defaultsprite\",\n\t\"defaultwall\",\n\t\"defaultwarp\",\n\t\"defaultgammacb\",\n\t\"drawflat_wall\",\n\t\"wireframe\",\n\t\"itemtimer\",\n\t\"lpp_depthnorm\",\n\t\"lpp_light\",\n\t\"lpp_wall\",\n\t\"postproc_fisheye\",\n\t\"postproc_panorama\",\n\t\"postproc_laea\",\n\t\"postproc_stereographic\",\n\t\"postproc_equirectangular\",\n\t\"postproc_panini\",\n\t\"postproc_ascii\",\n\t\"fxaa\",\n\t\"underwaterwarp\",\n\t\"menutint\",\n\t\"terrain\",\n\t\"rtlight\",\n\t\"rq_rtlight\",\n\t\"\"\n};\n\nvoid dumpprogstring(FILE *out, FILE *src)\n{\n\tint j;\n\tchar line[1024];\n\n\twhile(fgets(line, sizeof(line), src))\n\t{\n\t\tj = 0;\n\t\twhile (line[j] == ' ' || line[j] == '\\t')\n\t\t\tj++;\n\t\tif ((line[j] == '/' && line[j] == '/') || line[j] == '\\r' || line[j] == '\\n')\n\t\t{\n\t\t\tfor (; line[j]; j++)\n\t\t\t{\n\t\t\t\tif (line[j] != '\\r')\n\t\t\t\t\tfputc(line[j], out);\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tfputc('\\\"', out);\t\t\t\t\n\t\t\twhile (line[j] && line[j] != '\\r' && line[j] != '\\n')\n\t\t\t{\n\t\t\t\tif (line[j] == '\\t')\n\t\t\t\t\tfputc(' ', out);\n\t\t\t\telse if (line[j] == '\\\"')\n\t\t\t\t{\n\t\t\t\t\tfputc('\\\\', out);\n\t\t\t\t\tfputc(line[j], out);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tfputc(line[j], out);\n\t\t\t\tj++;\n\t\t\t}\n\t\t\tfputs(\"\\\\n\\\"\\n\", out);\n\t\t}\n\t}\n\tfflush(out);\n}\nvoid dumpprogblob(FILE *out, FILE *src)\n{\n\tunsigned char *buf;\n\tunsigned int size;\n\tfseek(src, 0, SEEK_END);\n\tsize = ftell(src);\n\tfseek(src, 0, SEEK_SET);\n\tbuf = malloc(size);\n\tfread(buf, size, 1, src);\n\n\tsize_t totallen, i, linelen;\n\ttotallen = 0;\n\tlinelen = 32;\n\tfprintf(out, \"\\\"\");\n\tfor (i=0;i<size;i++)\n\t{\n\t\tfprintf(out, \"\\\\x%02X\",buf[i]);\n\t\tif (i % linelen == linelen - 1)\n\t\t\tfprintf(out, \"\\\"\\n\\\"\");\n\t}\n\tfprintf(out, \"\\\"\");\n}\n\nstatic struct shadertype_s\n{\n\tchar *abrv;\n\tchar *filepattern;\n\tchar *preprocessor;\n\tchar *rendererapi;\n\tint apiversion;\t//-1 is a binary blob.\n} shadertype[] =\n{\n\t{\"GL\",\t\"glsl/%s.glsl\", \t\"GLQUAKE\", \t\"QR_OPENGL\", \t\t110},\t//gl2+\n\t//{\"ES\",\"gles/%s.glsl\", \t\"GLQUAKE\", \t\"QR_OPENGL\", \t\t100},\t//gles\n\t{\"VK\",\t\"vulkanblobs/%s.fvb\",\t\"VKQUAKE\", \t\"QR_VULKAN\", \t\t-1},\t//vulkan\n\t{\"D9\",\t\"hlsl9/%s.hlsl\", \t\"D3D9QUAKE\", \t\"QR_DIRECT3D9\", \t9},\t//d3d9\n\t{\"D11\",\"hlsl11/%s.hlsl\", \t\"D3D11QUAKE\", \t\"QR_DIRECT3D11\", \t11},\t//d3d11\n};\n//tbh we should precompile the d3d shaders.\n\nstatic void dumpprogram(FILE *c, const char *progname)\n{\n\tFILE *s;\n\tchar line[1024];\n\tint a;\n\tprintf(\"%25s: \", progname);\n\tfor (a = 0; a < sizeof(shadertype)/sizeof(shadertype[0]); a++)\n\t{\n\t\tsprintf(line, shadertype[a].filepattern, progname);\n\t\tif (shadertype[a].apiversion == -1)\n\t\t\ts = fopen(line, \"rb\");\n\t\telse\n\t\t\ts = fopen(line, \"rt\");\n\t\tif (!s)\n\t\t{\n\t\t\tprintf(\"%4s\", \"\");\n\t\t\tcontinue;\n\t\t}\n\t\tfprintf(c, \"#ifdef %s\\n\", shadertype[a].preprocessor);\n\t\tfprintf(c, \"{%s, %i, \\\"%s\\\",\\n\", shadertype[a].rendererapi, shadertype[a].apiversion, progname);\n\t\tif (shadertype[a].apiversion == -1)\n\t\t\tdumpprogblob(c,s);\n\t\telse\n\t\t\tdumpprogstring(c, s);\n\t\tfputs(\"},\\n\", c);\n\t\tfprintf(c, \"#endif\\n\");\n\t\tfclose(s);\n\t\tfflush(c);\n\n\t\tprintf(\"%4s\", shadertype[a].abrv);\n\t}\n\tprintf(\"\\n\");\n}\n\nint main(int argc, const char **argv)\n{\n\tFILE *c, *s;\n\tint i, j, a;\n\n\tconst char *outname = ((argc>1)?argv[1]:\"../gl/r_bishaders.h\");\n\n\tc = fopen(outname, \"wt\");\n\n\tif (!c)\n\t{\n\t\tprintf(\"unable to open a file\\n\");\n\t\treturn 0;\n\t}\n\n\tfprintf(c, \"/*\\nWARNING: THIS FILE IS GENERATED BY '\"__FILE__\"'.\\nYOU SHOULD NOT EDIT THIS FILE BY HAND\\n*/\\n\\n\");\n\n\tif (argc>2)\n\t{\t//if we're passed a file list on the commandline then just use that (generally for plugins).\n\t\tfor (i = 2; i < argc; i++)\n\t\t\tdumpprogram(c, argv[i]);\n\t}\n\telse\n\t{\t//use our built in list.\n\t\tfor (i = 0; *shaders[i]; i++)\n\t\t\tdumpprogram(c, shaders[i]);\n\t}\n\n\tfclose(c);\n\n\treturn 0;\n}\n"
  },
  {
    "path": "engine/shaders/glsl/altwater.glsl",
    "content": "!!cvardf r_glsl_turbscale_reflect=1\t//simpler scaler\r\n!!cvardf r_glsl_turbscale_refract=1\t//simpler scaler\r\n!!samps diffuse normalmap\r\n!!samps\t\t\t\trefract=0\t//always present\r\n!!samps =REFLECT\treflect=1\r\n!!samps =RIPPLEMAP\tripplemap=2\r\n!!samps =DEPTH \t\trefractdepth=3\r\n!!permu FOG\r\n\r\n#include \"sys/defs.h\"\r\n\r\n//modifier: REFLECT\t\t(s_t2 is a reflection instead of diffusemap)\r\n//modifier: STRENGTH_REFL\t(distortion strength - 0.1 = fairly gentle, 0.2 = big waves)\r\n//modifier: STRENGTH_REFL\t(distortion strength - 0.1 = fairly gentle, 0.2 = big waves)\r\n//modifier: FRESNEL_EXP\t(5=water)\r\n//modifier: TXSCALE\t\t(wave size - 0.2)\r\n//modifier: RIPPLEMAP\t\t(s_t3 contains a ripplemap\r\n//modifier: TINT_REFR\t\t(some colour value)\r\n//modifier: TINT_REFL\t\t(some colour value)\r\n//modifier: ALPHA\t\t(mix in the normal water texture over the top)\r\n//modifier: USEMODS\t\t(use single-texture scrolling via tcmods - note, also forces the engine to actually use tcmod etc)\r\n\r\n//a few notes on DP compat:\r\n//'dpwater' makes numerous assumptions about DP internals\r\n//by default there is a single pass that uses the pass's normal tcmods\r\n//the fresnel has a user-supplied min+max rather than an exponent\r\n//both parts are tinted individually\r\n//if alpha is enabled, the regular water texture is blended over the top, again using the same crappy tcmods...\r\n\r\n//legacy crap\r\n#ifndef FRESNEL\r\n#define FRESNEL 5.0\r\n#endif\r\n#ifndef TINT\r\n#define TINT 0.7,0.8,0.7\r\n#endif\r\n#ifndef STRENGTH\r\n#define STRENGTH 0.1\r\n#endif\r\n#ifndef TXSCALE\r\n#define TXSCALE 0.2\r\n#endif\r\n\r\n//current values (referring to legacy defaults where needed)\r\n#ifndef FRESNEL_EXP\r\n#define FRESNEL_EXP 5.0\r\n#endif\r\n#ifndef FRESNEL_MIN\r\n#define FRESNEL_MIN 0.0\r\n#endif\r\n#ifndef FRESNEL_RANGE\r\n#define FRESNEL_RANGE 1.0\r\n#endif\r\n#ifndef STRENGTH_REFL\r\n#define STRENGTH_REFL STRENGTH\r\n#endif\r\n#ifndef STRENGTH_REFR\r\n#define STRENGTH_REFR STRENGTH\r\n#endif\r\n#ifndef TXSCALE1\r\n#define TXSCALE1 TXSCALE\r\n#endif\r\n#ifndef TXSCALE2\r\n#define TXSCALE2 TXSCALE\r\n#endif\r\n#ifndef TINT_REFR\r\n#define TINT_REFR TINT\r\n#endif\r\n#ifndef TINT_REFL\r\n#define TINT_REFL 1.0,1.0,1.0\r\n#endif\r\n#ifndef FOGTINT\r\n#define FOGTINT 0.2,0.3,0.2\r\n#endif\r\n\r\nvarying vec2 tc;\r\nvarying vec4 tf;\r\nvarying vec3 norm;\r\nvarying vec3 eye;\r\n#ifdef VERTEX_SHADER\r\nvoid main (void)\r\n{\r\n\ttc = v_texcoord.st;\r\n\ttf = ftetransform();\r\n\tnorm = v_normal;\r\n\teye = e_eyepos - v_position.xyz;\r\n\tgl_Position = ftetransform();\r\n}\r\n#endif\r\n#ifdef FRAGMENT_SHADER\r\n#include \"sys/fog.h\"\r\n\r\n\r\nvoid main (void)\r\n{\r\n\tvec2 stc;\t//screen tex coords\r\n\tvec2 ntc;\t//normalmap/diffuse tex coords\r\n\tvec3 n, refr, refl;\r\n\tfloat fres;\r\n\tfloat depth;\r\n\tstc = (1.0 + (tf.xy / tf.w)) * 0.5;\r\n\t//hack the texture coords slightly so that there are less obvious gaps\r\n\tstc.t -= 1.5*norm.z/1080.0;\r\n\r\n#if 0//def USEMODS\r\n\tntc = tc;\r\n\tn = texture2D(s_normalmap, ntc).xyz - 0.5;\r\n#else\r\n\t//apply q1-style warp, just for kicks\r\n\tntc = tc + sin(tc.ts+e_time)*0.125;\r\n\r\n\t//generate the two wave patterns from the normalmap\r\n\tn = (texture2D(s_normalmap, vec2(TXSCALE1)*tc + vec2(e_time*0.1, 0.0)).xyz);\r\n\tn += (texture2D(s_normalmap, vec2(TXSCALE2)*tc - vec2(0, e_time*0.097)).xyz);\r\n\tn -= 1.0 - 4.0/256.0;\r\n#endif\r\n\r\n#ifdef RIPPLEMAP\r\n\tn += texture2D(s_ripplemap, stc).rgb*3.0;\r\n#endif\r\n\tn = normalize(n);\r\n\r\n\t//the fresnel term decides how transparent the water should be\r\n\tfres = pow(1.0-abs(dot(n, normalize(eye))), float(FRESNEL_EXP)) * float(FRESNEL_RANGE) + float(FRESNEL_MIN);\r\n\r\n#ifdef DEPTH\r\n\tfloat far = #include \"cvar/gl_maxdist\";\r\n\tfloat near = #include \"cvar/gl_mindist\";\r\n\t//get depth value at the surface\r\n\tfloat sdepth = gl_FragCoord.z;\r\n\tsdepth = (2.0*near) / (far + near - sdepth * (far - near));\r\n\tsdepth = mix(near, far, sdepth);\r\n\r\n\t//get depth value at the ground beyond the surface.\r\n\tfloat gdepth = texture2D(s_refractdepth, stc).x;\r\n\tgdepth = (2.0*near) / (far + near - gdepth * (far - near));\r\n\tif (gdepth >= 0.5)\r\n\t{\r\n\t\tgdepth = sdepth;\r\n\t\tdepth = 0.0;\r\n\t}\r\n\telse\r\n\t{\r\n\t\tgdepth = mix(near, far, gdepth);\r\n\t\tdepth = gdepth - sdepth;\r\n\t}\r\n\r\n\t//reduce the normals in shallow water (near walls, reduces the pain of linear sampling)\r\n\tif (depth < 100.0)\r\n\t\tn *= depth/100.0;\r\n#else\r\n\tdepth = 1.0;\r\n#endif \r\n\r\n\r\n\t//refraction image (and water fog, if possible)\r\n\trefr = texture2D(s_refract, stc + n.st*float(STRENGTH_REFR)*float(r_glsl_turbscale_refract)).rgb * vec3(TINT_REFR);\r\n#ifdef DEPTH\r\n\trefr = mix(refr, vec3(FOGTINT), min(depth/4096.0, 1.0));\r\n#endif\r\n\r\n\t//reflection/diffuse\r\n#ifdef REFLECT\r\n\trefl = texture2D(s_reflect, stc - n.st*float(STRENGTH_REFL)*float(r_glsl_turbscale_reflect)).rgb * vec3(TINT_REFL);\r\n#else\r\n\trefl = texture2D(s_diffuse, ntc).xyz * vec3(TINT_REFL);\r\n#endif\r\n\t//interplate by fresnel\r\n\trefr = mix(refr, refl, fres);\r\n\r\n\r\n\r\n\r\n\r\n#ifdef ALPHA\r\n\tvec4 ts = texture2D(s_diffuse, ntc);\r\n\tvec4 surf = fog4blend(vec4(ts.rgb, float(ALPHA)*ts.a));\r\n\trefr = mix(refr, surf.rgb, surf.a);\r\n#else\r\n\trefr = fog3(refr);\t\r\n#endif\r\n\r\n\t//done\r\n\tgl_FragColor = vec4(refr, 1.0);\r\n}\r\n#endif\r\n"
  },
  {
    "path": "engine/shaders/glsl/bloom_blur.glsl",
    "content": "!!samps 1\r\n//apply gaussian filter\r\n\r\nvarying vec2 tc;\r\n\r\n#ifdef VERTEX_SHADER\r\nattribute vec2 v_texcoord;\r\nvoid main ()\r\n{\r\n\ttc = v_texcoord;\r\n\tgl_Position = ftetransform();\r\n}\r\n#endif\r\n#ifdef FRAGMENT_SHADER\r\n/*offset should be 1.2 pixels away from the center*/\r\nuniform vec3 e_glowmod;\r\nvoid main ()\r\n{\r\n\tgl_FragColor =\r\n\t\t0.3125 * texture2D(s_t0, tc - e_glowmod.st) +\r\n\t\t0.375 * texture2D(s_t0, tc) +\r\n\t\t0.3125 * texture2D(s_t0, tc + e_glowmod.st);\r\n}\r\n#endif\r\n"
  },
  {
    "path": "engine/shaders/glsl/bloom_filter.glsl",
    "content": "!!cvarv r_bloom_filter\r\n!!samps 1\r\n//the bloom filter\r\n//filter out any texels which are not to bloom\r\n\r\nvarying vec2 tc;\r\n\r\n#ifdef VERTEX_SHADER\r\nattribute vec2 v_texcoord;\r\nvoid main ()\r\n{\r\n\ttc = v_texcoord;\r\n\tgl_Position = ftetransform();\r\n}\r\n#endif\r\n#ifdef FRAGMENT_SHADER\r\nuniform vec3 cvar_r_bloom_filter;\r\nvoid main ()\r\n{\r\n\tgl_FragColor.rgb = (texture2D(s_t0, tc).rgb - cvar_r_bloom_filter)/(1.0-cvar_r_bloom_filter);\r\n}\r\n#endif\r\n"
  },
  {
    "path": "engine/shaders/glsl/bloom_final.glsl",
    "content": "!!cvarf r_bloom\r\n!!cvarf r_bloom_retain=1.0\r\n!!samps 4\r\n//add them together\r\n//optionally apply tonemapping\r\n\r\nvarying vec2 tc;\r\n\r\n#ifdef VERTEX_SHADER\r\nattribute vec2 v_texcoord;\r\nvoid main ()\r\n{\r\n\ttc = v_texcoord;\r\n\tgl_Position = ftetransform();\r\n}\r\n#endif\r\n#ifdef FRAGMENT_SHADER\r\nuniform float cvar_r_bloom;\r\nuniform float cvar_r_bloom_retain;\r\nvoid main ()\r\n{\r\n\tgl_FragColor = \r\n\t\tcvar_r_bloom_retain * texture2D(s_t0, tc) +\r\n\t\tcvar_r_bloom*(\r\n\t\t\ttexture2D(s_t1, tc) +\r\n\t\t\ttexture2D(s_t2, tc) +\r\n\t\t\ttexture2D(s_t3, tc)\r\n\t\t) ;\r\n}\r\n#endif\r\n"
  },
  {
    "path": "engine/shaders/glsl/colourtint.glsl",
    "content": "!!samps 2\r\n//this glsl shader is useful for cubemapped post processing effects (see csaddon for an example)\r\nvarying vec4 tf;\r\n#ifdef VERTEX_SHADER\r\nvoid main ()\r\n{\r\n\tgl_Position = tf = vec4(v_position.xy,-1.0, 1.0);\r\n}\r\n#endif\r\n#ifdef FRAGMENT_SHADER\r\nvoid main()\r\n{\r\n\tvec2 fc;\r\n\tfc = tf.xy / tf.w;\r\n\tvec3 raw = texture2D(s_t0, (1.0 + fc) / 2.0).rgb;\r\n#define LUTSIZE 16.0\r\n\tvec3 scale = vec3((LUTSIZE-1.0)/LUTSIZE);\r\n\tvec3 bias = vec3(1.0/(2.0*LUTSIZE));\r\n\tgl_FragColor = texture3D(s_t1, raw * scale + bias);\r\n}\r\n#endif\r\n"
  },
  {
    "path": "engine/shaders/glsl/crepuscular_opaque.glsl",
    "content": "//opaque surfaces are drawn to the render target to mask out skies\r\n#ifdef VERTEX_SHADER\r\nvoid main ()\r\n{\r\n\tgl_Position = ftetransform();\r\n}\r\n#endif\r\n#ifdef FRAGMENT_SHADER\r\nvoid main()\r\n{\r\n\tgl_FragColor = vec4(0.0, 0.0, 0.0, 1.0);\r\n}\r\n#endif\r\n"
  },
  {
    "path": "engine/shaders/glsl/crepuscular_rays.glsl",
    "content": "!!cvarf crep_decay\r\n!!cvarf crep_density\r\n!!cvarf crep_weight\r\n!!samps 1\r\n\r\n//this is a post-processing shader, drawn in 2d\r\n//there will be a render target containing sky surfaces drawn with crepuscular_sky, and everything else drawn with crepuscular_opaque (to mask out the sky)\r\n//this shader then just smudges the sky out a bit as though its coming from the sun or whatever through the clouds.\r\n//yoinked from http://fabiensanglard.net/lightScattering/index.php\r\n\r\nvarying vec2 tc;\r\n#ifdef VERTEX_SHADER\r\nattribute vec2 v_texcoord;\r\nvoid main ()\r\n{\r\n\ttc = v_texcoord;\r\n\tgl_Position = vec4(v_position, 1.0);\r\n}\r\n#endif\r\n#ifdef FRAGMENT_SHADER\r\nconst float crep_decay = 0.94;\r\nconst float crep_density = 0.5;\r\nconst float crep_weight = 0.2;\r\nuniform vec3 l_lightcolour;\r\nuniform vec3 l_lightscreen;\r\nconst int NUM_SAMPLES = 100;\r\nvoid main()\r\n{\r\n\tvec2 deltaTextCoord = vec2(tc.st - l_lightscreen.xy);\r\n\tvec2 textCoo = tc.st;\r\n\tdeltaTextCoord *= 1.0 / float(NUM_SAMPLES) * crep_density;\r\n\tfloat illuminationDecay = 1.0;\r\n\tgl_FragColor = vec4(0.0,0.0,0.0,0.0);\r\n\tfor(int i=0; i < NUM_SAMPLES ; i++)\r\n\t{\r\n\t\ttextCoo -= deltaTextCoord;\r\n\t\tvec4 sample = texture2D(s_t0, textCoo);\r\n\t\tsample *= illuminationDecay * crep_weight;\r\n\t\tgl_FragColor += sample;\r\n\t\tilluminationDecay *= crep_decay;\r\n\t}\r\n\tgl_FragColor *= vec4(l_lightcolour, 1.0);\r\n}\r\n#endif\r\n"
  },
  {
    "path": "engine/shaders/glsl/crepuscular_sky.glsl",
    "content": "!!samps 2\r\n//pretty much a regular sky shader\r\n//though in reality we should render a sun circle in the middle.\r\n//still, its kinda cool to have scrolling clouds masking out parts of the sun.\r\n\r\n#ifdef VERTEX_SHADER\r\nvarying vec3 pos;\r\nvoid main ()\r\n{\r\n\tpos = v_position.xyz;\r\n\tgl_Position = ftetransform();\r\n}\r\n#endif\r\n#ifdef FRAGMENT_SHADER\r\nuniform float e_time;\r\nuniform vec3 e_eyepos;\r\nvarying vec3 pos;\r\nvoid main ()\r\n{\r\n\tvec2 tccoord;\r\n\tvec3 dir = pos - e_eyepos;\r\n\tdir.z *= 3.0;\r\n\tdir.xy /= 0.5*length(dir);\r\n\ttccoord = (dir.xy + e_time*0.03125);\r\n\tvec3 solid = vec3(texture2D(s_t0, tccoord));\r\n\ttccoord = (dir.xy + e_time*0.0625);\r\n\tvec4 clouds = texture2D(s_t1, tccoord);\r\n\tgl_FragColor.rgb = (solid.rgb*(1.0-clouds.a)) + (clouds.a*clouds.rgb);\r\n}\r\n#endif\r\n"
  },
  {
    "path": "engine/shaders/glsl/default2d.glsl",
    "content": "!!ver 100-450\r\n!!samps img=0\r\n\r\n//this shader is present for support for gles/gl3core contexts\r\n//it is single-texture-with-vertex-colours, and doesn't do anything special.\r\n//beware that a few things use this, including apparently fonts and bloom rescaling.\r\n//its really not meant to do anything special.\r\n\r\nvarying vec2 tc;\r\nvarying vec4 vc;\r\n\r\n#ifdef VERTEX_SHADER\r\nattribute vec2 v_texcoord;\r\nattribute vec4 v_colour;\r\nvoid main ()\r\n{\r\n\ttc = v_texcoord;\r\n\tvc = v_colour;\r\n\tgl_Position = ftetransform();\r\n}\r\n#endif\r\n#ifdef FRAGMENT_SHADER\r\nvoid main ()\r\n{\r\n\tvec4 f = vc;\r\n#ifdef PREMUL\r\n\tf.rgb *= f.a;\r\n#endif\r\n#ifdef DECLAMP\r\n\tvec2 ntc = fract(tc);\r\n#define tc ntc\r\n#endif\r\n\tf *= texture2D(s_img, tc);\r\n\tgl_FragColor = f;\r\n}\r\n#endif\r\n"
  },
  {
    "path": "engine/shaders/glsl/default2danim.glsl",
    "content": "!!ver 300-450\n!!samps anim:2DArray=0\n\n#include \"sys/defs.h\"\n\n//this shader is present for support for gles/gl3core contexts\n//it is single-texture-with-vertex-colours, and doesn't do anything special.\n//beware that a few things use this, including apparently fonts and bloom rescaling.\n//its really not meant to do anything special.\n\nvarying vec2 tc;\nvarying vec4 vc;\n\n#ifdef VERTEX_SHADER\nvoid main ()\n{\n\ttc = v_texcoord;\n\tvc = v_colour;\n\tgl_Position = ftetransform();\n}\n#endif\n#ifdef FRAGMENT_SHADER\nvoid main ()\n{\n\t//figure out which frame to use.\n    ivec3 sz = textureSize(s_anim, 0);\n    float layer = mod(e_time*10, sz.z-1);\n\n\tvec4 f = vc;\n#ifdef PREMUL\n\tf.rgb *= f.a;\n#endif\n    f *= texture(s_anim, vec3(tc, layer));\n\tgl_FragColor = f;\n}\n#endif\n"
  },
  {
    "path": "engine/shaders/glsl/defaultadditivesprite.glsl",
    "content": "!!permu FOG\r\n!!samps 1\r\n\r\n//meant to be used for additive stuff. presumably particles and sprites. though actually its only flashblend effects that use this at the time of writing.\r\n//includes fog, apparently.\r\n\r\n#include \"sys/fog.h\"\r\n#ifdef VERTEX_SHADER\r\nattribute vec2 v_texcoord;\r\nattribute vec4 v_colour;\r\nvarying vec2 tc;\r\nvarying vec4 vc;\r\nvoid main ()\r\n{\r\n\ttc = v_texcoord;\r\n\tvc = v_colour;\r\n\tgl_Position = ftetransform();\r\n}\r\n#endif\r\n#ifdef FRAGMENT_SHADER\r\nvarying vec2 tc;\r\nvarying vec4 vc;\r\nuniform vec4 e_colourident;\r\nvoid main ()\r\n{\r\n\tgl_FragColor = fog4additive(texture2D(s_t0, tc) * vc * e_colourident);\r\n}\r\n#endif\r\n"
  },
  {
    "path": "engine/shaders/glsl/defaultfill.glsl",
    "content": "!!ver 100-450\r\n\r\n#ifdef VERTEX_SHADER\r\nattribute vec4 v_colour;\r\nvarying vec4 vc;\r\n\r\nvoid main ()\r\n{\r\n\tvc = v_colour;\r\n\tgl_Position = ftetransform();\r\n}\r\n#endif\r\n\r\n#ifdef FRAGMENT_SHADER\r\nvarying vec4 vc;\r\nvoid main ()\r\n{\r\n\tgl_FragColor = vc;\r\n}\r\n#endif"
  },
  {
    "path": "engine/shaders/glsl/defaultgammacb.glsl",
    "content": "!!ver 100-450\r\n!!samps 1\r\n//this shader is applies gamma/contrast/brightness to the source image, and dumps it out.\r\n\r\nvarying vec2 tc;\r\nvarying vec4 vc;\t//gamma, contrast, brightness, contrastboost\r\n\r\n#ifdef VERTEX_SHADER\r\nattribute vec2 v_texcoord;\r\nattribute vec4 v_colour;\r\nvoid main ()\r\n{\r\n\ttc = vec2(v_texcoord.s, 1.0-v_texcoord.t);\r\n\tvc = v_colour;\r\n\tgl_Position = ftetransform();\r\n}\r\n#endif\r\n#ifdef FRAGMENT_SHADER\r\nvoid main ()\r\n{\r\n\tvec3 t = texture2D(s_t0, tc).rgb;\r\n\tt = vc.a * t/((vc.a-1.0)*t + 1.0);\r\n\tgl_FragColor = vec4(pow(t, vec3(vc.r))*vc.g + vc.b, 1.0);\r\n}\r\n#endif\r\n"
  },
  {
    "path": "engine/shaders/glsl/defaultskin.glsl",
    "content": "!!ver 100 150\r\n!!permu TESS\r\n!!permu FULLBRIGHT\r\n!!permu UPPERLOWER\r\n!!permu FRAMEBLEND\r\n!!permu SKELETAL\r\n!!permu FOG\r\n!!permu BUMP\r\n!!permu REFLECTCUBEMASK\r\n!!cvarf r_glsl_offsetmapping_scale\r\n!!cvarf gl_specular\r\n!!cvardf gl_affinemodels=0\r\n!!cvardf r_tessellation_level=5\r\n!!samps !EIGHTBIT diffuse normalmap specular fullbright upper lower reflectmask reflectcube\r\n!!samps =EIGHTBIT paletted 1\r\n!!samps =OCCLUDE occlusion\r\n!!samps =USE_TRANSMISSION transmission\t//only .r valid, multiplier for factor_transmission\r\n!!samps =USE_VOLUME thickness\t\t\t//only .g valid, multiplier for factor_volume_thickness, combined with factor_volume_rgb+factor_volume_distance(average distance travelled in metres)\r\n//!!permu VC\t\t\t// adds rgba vertex colour multipliers\r\n//!!permu SPECULAR\t\t// auto-added when gl_specular>0\r\n//!!permu OFFSETMAPPING\t// auto-added when r_glsl_offsetmapping is set\r\n//!!permu NONORMALS\t\t// states that there's no normals available, which affects lighting.\r\n//!!permu ORM\t\t\t// specularmap is r:Occlusion, g:Roughness, b:Metalness\r\n//!!permu SG\t\t\t// specularmap is rgb:F0, a:Roughness (instead of exponent)\r\n//!!permu PBR\t\t\t// an attempt at pbr logic (enabled from ORM or SG)\r\n//!!permu NOOCCLUDE\t\t// ignores the use of ORM's occlusion... yeah, stupid.\r\n//!!permu OCCLUDE\t\t// use an explicit occlusion texturemap (separate from roughness+metalness).\r\n//!!permu EIGHTBIT\t\t// uses software-style paletted colourmap lookups\r\n//!!permu ALPHATEST\t\t// if defined, this is the required alpha level (more versatile than doing it at the q3shader level)\r\n\r\n#include \"sys/defs.h\"\r\n\r\n//standard shader used for models.\r\n//must support skeletal and 2-way vertex blending or Bad Things Will Happen.\r\n//the vertex shader is responsible for calculating lighting values.\r\n\r\n#if gl_affinemodels==1 && __VERSION__ >= 130 && !defined(GL_ES)\r\n#define affine noperspective\r\n#else\r\n#define affine\r\n#endif\r\n\r\n#if defined(ORM) || defined(SG)\r\n\t#define PBR\r\n#endif\r\n\r\n#ifdef NONORMALS\t//lots of things need normals to work properly. make sure nothing breaks simply because they added an extra texture.\r\n\t#undef BUMP\r\n\t#undef SPECULAR\r\n\t#undef OFFSETMAPPING\r\n\t#undef REFLECTCUBEMASK\r\n#endif\r\n\r\n\r\n\r\n\r\n#ifdef VERTEX_SHADER\r\n#include \"sys/skeletal.h\"\r\n\r\naffine varying vec2 tc;\r\nvarying vec4 light;\r\n#if defined(SPECULAR) || defined(OFFSETMAPPING) || defined(REFLECTCUBEMASK) || defined(PBR)\r\nvarying vec3 eyevector;\r\n#endif\r\n#if defined(PBR)||defined(REFLECTCUBEMASK)\r\n\tvarying mat3 invsurface;\r\n#endif\r\n#ifdef TESS\r\nvarying vec3 vertex;\r\nvarying vec3 normal;\r\n#endif\r\n\r\nvoid main ()\r\n{\r\n\tlight.rgba = vec4(e_light_ambient, 1.0);\r\n\r\n#ifdef NONORMALS\r\n\tvec3 n, w;\r\n\tgl_Position = skeletaltransform_w(w);\r\n\tn = vec3(0.0);\r\n#else\r\n\tvec3 n, s, t, w;\r\n\tgl_Position = skeletaltransform_wnst(w,n,s,t);\r\n\tn = normalize(n);\r\n\ts = normalize(s);\r\n\tt = normalize(t);\r\n\t#ifndef PBR\r\n\t\t#ifdef EIGHTBIT\r\n\t\t\t//doesn't darken in the shade, only gets brighter in the light (overbrighting)\r\n\t\t\tlight.rgb += max(0.0,dot(n,e_light_dir)) * e_light_mul;\r\n\t\t#else\r\n\t\t\t//_DOES_ get darker in the shade, despite the light not lighting it at all....\r\n\t\t\tfloat d = dot(n,e_light_dir);\r\n\t\t\tif (d < 0.0)\r\n\t\t\t\td *= 13.0/44.0;\t//a wtfery factor to approximate glquake's anorm_dots.h\r\n\t\t\tlight.rgb += d * e_light_mul;\r\n\t\t#endif\r\n\t#else\r\n\t\tlight.rgb = vec3(1.0);\r\n\t#endif\r\n#endif\r\n\r\n#if defined(SPECULAR)||defined(OFFSETMAPPING) || defined(REFLECTCUBEMASK) || defined(PBR)\r\n\tvec3 eyeminusvertex = e_eyepos - w.xyz;\r\n\teyevector.x = dot(eyeminusvertex, s.xyz);\r\n\teyevector.y = dot(eyeminusvertex, t.xyz);\r\n\teyevector.z = dot(eyeminusvertex, n.xyz);\r\n#endif\r\n#if defined(PBR) || defined(REFLECTCUBEMASK)\r\n\tinvsurface = mat3(s, t, n);\r\n#endif\r\n\r\n\ttc = v_texcoord;\r\n\r\n#ifdef VC\r\n\tlight *= v_colour;\r\n#endif\r\n\r\n//FIXME: Software rendering imitation should possibly push out normals by half a pixel or something to approximate software's over-estimation of distant model sizes (small models are drawn using JUST their verticies using the nearest pixel, which results in larger meshes)\r\n\r\n#ifdef TESS\r\n\tnormal = n;\r\n\tvertex = w;\r\n#endif\r\n}\r\n#endif\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n#if defined(TESS_CONTROL_SHADER)\r\nlayout(vertices = 3) out;\r\n\r\nin vec3 vertex[];\r\nout vec3 t_vertex[];\r\nin vec3 normal[];\r\nout vec3 t_normal[];\r\naffine in vec2 tc[];\r\naffine out vec2 t_tc[];\r\nin vec4 light[];\r\nout vec4 t_light[];\r\n#if defined(SPECULAR) || defined(OFFSETMAPPING) || defined(REFLECTCUBEMASK) || defined(PBR)\r\nin vec3 eyevector[];\r\nout vec3 t_eyevector[];\r\n#endif\r\n#ifdef REFLECTCUBEMASK\r\nin mat3 invsurface[];\r\nout mat3 t_invsurface[];\r\n#endif\r\nvoid main()\r\n{\r\n\t//the control shader needs to pass stuff through\r\n#define id gl_InvocationID\r\n\tt_vertex[id] = vertex[id];\r\n\tt_normal[id] = normal[id];\r\n\tt_tc[id] = tc[id];\r\n\tt_light[id] = light[id];\r\n#if defined(SPECULAR) || defined(OFFSETMAPPING) || defined(REFLECTCUBEMASK) || defined(PBR)\r\n\tt_eyevector[id] = eyevector[id];\r\n#endif\r\n#ifdef REFLECTCUBEMASK\r\n\tt_invsurface[id][0] = invsurface[id][0];\r\n\tt_invsurface[id][1] = invsurface[id][1];\r\n\tt_invsurface[id][2] = invsurface[id][2];\r\n#endif\r\n\r\n\tgl_TessLevelOuter[0] = float(r_tessellation_level);\r\n\tgl_TessLevelOuter[1] = float(r_tessellation_level);\r\n\tgl_TessLevelOuter[2] = float(r_tessellation_level);\r\n\tgl_TessLevelInner[0] = float(r_tessellation_level);\r\n}\r\n#endif\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n#if defined(TESS_EVALUATION_SHADER)\r\nlayout(triangles) in;\r\n\r\nin vec3 t_vertex[];\r\nin vec3 t_normal[];\r\naffine in vec2 t_tc[];\r\naffine out vec2 tc;\r\nin vec4 t_light[];\r\nout vec4 light;\r\n#if defined(SPECULAR) || defined(OFFSETMAPPING) || defined(REFLECTCUBEMASK) || defined(PBR)\r\nin vec3 t_eyevector[];\r\nout vec3 eyevector;\r\n#endif\r\n#ifdef REFLECTCUBEMASK\r\nin mat3 t_invsurface[];\r\nout mat3 invsurface;\r\n#endif\r\n\r\n#define LERP(a) (gl_TessCoord.x*a[0] + gl_TessCoord.y*a[1] + gl_TessCoord.z*a[2])\r\nvoid main()\r\n{\r\n#define factor 1.0\r\n\ttc = LERP(t_tc);\r\n\tvec3 w = LERP(t_vertex);\r\n\r\n\tvec3 t0 = w - dot(w-t_vertex[0],t_normal[0])*t_normal[0];\r\n\tvec3 t1 = w - dot(w-t_vertex[1],t_normal[1])*t_normal[1];\r\n\tvec3 t2 = w - dot(w-t_vertex[2],t_normal[2])*t_normal[2];\r\n\tw = w*(1.0-factor) + factor*(gl_TessCoord.x*t0+gl_TessCoord.y*t1+gl_TessCoord.z*t2);\r\n\r\n\t//FIXME: we should be recalcing these here, instead of just lerping them\r\n\tlight = LERP(t_light);\r\n#if defined(SPECULAR) || defined(OFFSETMAPPING) || defined(REFLECTCUBEMASK) || defined(PBR)\r\n\teyevector = LERP(t_eyevector);\r\n#endif\r\n#ifdef REFLECTCUBEMASK\r\n\tinvsurface[0] = LERP(t_invsurface[0]);\r\n\tinvsurface[1] = LERP(t_invsurface[1]);\r\n\tinvsurface[2] = LERP(t_invsurface[2]);\r\n#endif\r\n\r\n\tgl_Position = m_modelviewprojection * vec4(w,1.0);\r\n}\r\n#endif\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n#ifdef FRAGMENT_SHADER\r\n\r\n#include \"sys/fog.h\"\r\n\r\n#if defined(SPECULAR)\r\nuniform float cvar_gl_specular;\r\n#endif\r\n\r\n#ifdef OFFSETMAPPING\r\n#include \"sys/offsetmapping.h\"\r\n#endif\r\n\r\n#ifdef EIGHTBIT\r\n#define s_colourmap s_t0\r\n#endif\r\n\r\naffine varying vec2 tc;\r\nvarying vec4 light;\r\n#if defined(SPECULAR) || defined(OFFSETMAPPING) || defined(REFLECTCUBEMASK) || defined(PBR)\r\nvarying vec3 eyevector;\r\n#endif\r\n#if defined(PBR) || defined(REFLECTCUBEMASK)\r\n\tvarying mat3 invsurface;\r\n#endif\r\n\r\n#ifdef PBR\r\n#include \"sys/pbr.h\"\r\n#if 0\r\nvec3 getIBLContribution(PBRInfo pbrInputs, vec3 n, vec3 reflection)\r\n{\r\n    float mipCount = 9.0; // resolution of 512x512\r\n    float lod = (pbrInputs.perceptualRoughness * mipCount);\r\n    // retrieve a scale and bias to F0. See [1], Figure 3\r\n    vec3 brdf = texture2D(u_brdfLUT, vec2(pbrInputs.NdotV, 1.0 - pbrInputs.perceptualRoughness)).rgb;\r\n    vec3 diffuseLight = textureCube(u_DiffuseEnvSampler, n).rgb;\r\n\r\n#ifdef USE_TEX_LOD\r\n    vec3 specularLight = textureCubeLodEXT(u_SpecularEnvSampler, reflection, lod).rgb;\r\n#else\r\n    vec3 specularLight = textureCube(u_SpecularEnvSampler, reflection).rgb;\r\n#endif\r\n\r\n    vec3 diffuse = diffuseLight * pbrInputs.diffuseColor;\r\n    vec3 specular = specularLight * (pbrInputs.specularColor * brdf.x + brdf.y);\r\n\r\n    // For presentation, this allows us to disable IBL terms\r\n    diffuse *= u_ScaleIBLAmbient.x;\r\n    specular *= u_ScaleIBLAmbient.y;\r\n\r\n    return diffuse + specular;\r\n}\r\n#endif\r\n#endif\r\n\r\n\r\nvoid main ()\r\n{\r\n\tvec4 col, sp;\r\n\r\n#ifdef OFFSETMAPPING\r\n\tvec2 tcoffsetmap = offsetmap(s_normalmap, tc, eyevector);\r\n#define tc tcoffsetmap\r\n#endif\r\n\r\n#ifdef EIGHTBIT\r\n\tvec3 lightlev = light.rgb;\r\n\t//FIXME: with this extra flag, half the permutations are redundant.\r\n\tlightlev *= 0.5;\t//counter the fact that the colourmap contains overbright values and logically ranges from 0 to 2 intead of to 1.\r\n\tfloat pal = texture2D(s_paletted, tc).r;\t//the palette index. hopefully not interpolated.\r\n//\tlightlev -= 1.0 / 128.0;\t//software rendering appears to round down, so make sure we favour the lower values instead of rounding to the nearest\r\n\tcol.r = texture2D(s_colourmap, vec2(pal, 1.0-lightlev.r)).r;\t//do 3 lookups. this is to cope with lit files, would be a waste to not support those.\r\n\tcol.g = texture2D(s_colourmap, vec2(pal, 1.0-lightlev.g)).g;\t//its not very softwarey, but re-palettizing is ugly.\r\n\tcol.b = texture2D(s_colourmap, vec2(pal, 1.0-lightlev.b)).b;\t//without lits, it should be identical.\r\n\tcol.a = (pal<1.0)?light.a:0.0;\r\n#else\r\n\tcol = texture2D(s_diffuse, tc);\r\n\t#ifdef UPPER\r\n\t\tvec4 uc = texture2D(s_upper, tc);\r\n\t\tcol.rgb += uc.rgb*e_uppercolour*uc.a;\r\n\t#endif\r\n\t#ifdef LOWER\r\n\t\tvec4 lc = texture2D(s_lower, tc);\r\n\t\tcol.rgb += lc.rgb*e_lowercolour*lc.a;\r\n\t#endif\r\n\r\n\tcol *= factor_base;\r\n\r\n    #ifndef IOR\r\n        #define IOR 1.5 //Index Of Reflection.\r\n    #endif\r\n    #define dielectricSpecular pow(((IOR - 1.0)/(IOR + 1.0)),2.0)\r\n\t#ifdef SPECULAR\r\n\t\tvec4 specs = texture2D(s_specular, tc)*factor_spec;\r\n\t\t#ifdef ORM\r\n\t\t\t#define occlusion specs.r\r\n\t\t\t#define roughness clamp(specs.g, 0.04, 1.0)\r\n\t\t\t#define metalness specs.b\r\n\t\t\t#define gloss 1.0 //sqrt(1.0-roughness)\r\n\t\t\t#define ambientrgb (specrgb+col.rgb)\r\n\t\t\tvec3 specrgb = mix(vec3(dielectricSpecular), col.rgb, metalness);\r\n\t\t\tcol.rgb = col.rgb * (1.0 - dielectricSpecular) * (1.0-metalness);\r\n\t\t#elif defined(SG) //pbr-style specular+glossiness, without occlusion\r\n\t\t\t//occlusion needs to be baked in. :(\r\n\t\t\t#define roughness (1.0-specs.a)\r\n\t\t\t#define gloss (specs.a)\r\n\t\t\t#define specrgb specs.rgb\r\n\t\t\t#define ambientrgb (specrgb+col.rgb)\r\n\t\t#else\t//blinn-phong\r\n\t\t\t#define roughness (1.0-specs.a)\r\n\t\t\t#define gloss specs.a\r\n\t\t\t#define specrgb specs.rgb\r\n\t\t\t#define ambientrgb col.rgb\r\n\t\t#endif\r\n\t#else\r\n\t\t#define roughness 0.3\r\n\t\t#define specrgb vec3(1.0) //vec3(dielectricSpecular)\r\n\t\t#define ambientrgb col.rgb\r\n\t#endif\r\n\r\n\t#ifdef BUMP\r\n\t\t#ifdef PBR\t//to modelspace\r\n\t\t\tvec3 bumps = normalize(invsurface * (texture2D(s_normalmap, tc).rgb*2.0 - 1.0));\r\n\t\t#else\t//stay in tangentspace\r\n\t\t\tvec3 bumps = normalize(vec3(texture2D(s_normalmap, tc)) - 0.5);\r\n\t\t#endif\r\n\t#else\r\n\t\t#ifdef PBR\t//to modelspace\r\n\t\t\t#define bumps normalize(invsurface[2])\r\n\t\t#else\t//tangent space\r\n\t\t\t#define bumps vec3(0.0, 0.0, 1.0)\r\n\t\t#endif\r\n\t#endif\r\n\r\n\t#ifdef PBR\r\n\t\t//move everything to model space\r\n\t\tcol.rgb = DoPBR(bumps, normalize(eyevector), -e_light_dir, roughness, col.rgb, specrgb, vec3(0.0,1.0,1.0))*e_light_mul + e_light_ambient*.25*ambientrgb;\r\n\t#elif defined(gloss)\r\n\t\tvec3 halfdir = normalize(normalize(eyevector) - e_light_dir);\r\n\t\tfloat specmag = pow(max(dot(halfdir, bumps), 0.0), FTE_SPECULAR_EXPONENT * gloss);\r\n\t\tcol.rgb += FTE_SPECULAR_MULTIPLIER * specmag * specrgb;\r\n\t#endif\r\n\r\n\t#ifdef REFLECTCUBEMASK\r\n\t\tvec3 rtc = reflect(-eyevector, bumps);\r\n\t\t#ifndef PBR\r\n\t\t\trtc = rtc.x*invsurface[0] + rtc.y*invsurface[1] + rtc.z*invsurface[2];\r\n\t\t#endif\r\n\t\trtc = (m_model * vec4(rtc.xyz,0.0)).xyz;\r\n\t\tcol.rgb += texture2D(s_reflectmask, tc).rgb * textureCube(s_reflectcube, rtc).rgb;\r\n\t#endif\r\n\r\n#ifdef OCCLUDE\r\n\tcol.rgb *= texture2D(s_occlusion, tc).r;\t\r\n#elif defined(occlusion) && !defined(NOOCCLUDE)\r\n\tcol.rgb *= occlusion;\r\n#endif\r\n\tcol *= light * e_colourident;\r\n\r\n\t#ifdef FULLBRIGHT\r\n\t\tvec4 fb = texture2D(s_fullbright, tc);\r\n//\t\tcol.rgb = mix(col.rgb, fb.rgb, fb.a);\r\n\t\tcol.rgb += fb.rgb * fb.a * e_glowmod.rgb * factor_emit.rgb;\r\n\t#elif defined(PBR)\r\n\t\tcol.rgb += e_glowmod.rgb * factor_emit.rgb;\r\n\t#endif\r\n#endif\r\n\r\n#ifdef ALPHATEST\r\n    if (!(col.a ALPHATEST))\r\n        discard;\r\n#elif defined(MASK)\r\n\t#if defined(MASKLT)\r\n\t\tif (col.a < MASK)\r\n\t\t\tdiscard;\r\n\t#else\r\n\t\tif (col.a >= MASK)\r\n\t\t\tdiscard;\r\n\t#endif\r\n    col.a = 1.0;    //alpha blending AND alpha testing usually looks stupid, plus it screws up our fog.\r\n#endif\r\n\r\n\tgl_FragColor = fog4(col);\r\n}\r\n#endif\r\n\r\n"
  },
  {
    "path": "engine/shaders/glsl/defaultsky.glsl",
    "content": "!!permu FOG\r\n!!samps base=0, cloud=1\r\n!!cvardf r_skyfog=0.5\r\n#include \"sys/fog.h\"\r\n\r\n//regular sky shader for scrolling q1 skies\r\n//the sky surfaces are thrown through this as-is.\r\n\r\n#ifdef VERTEX_SHADER\r\nvarying vec3 pos;\r\nvoid main ()\r\n{\r\n\tpos = v_position.xyz;\r\n\tgl_Position = ftetransform();\r\n}\r\n#endif\r\n#ifdef FRAGMENT_SHADER\r\nuniform float e_time;\r\nuniform vec3 e_eyepos;\r\nvarying vec3 pos;\r\nvoid main ()\r\n{\r\n\tvec2 tccoord;\r\n\tvec3 dir = pos - e_eyepos;\r\n\r\n#ifdef EQUI\r\n#define PI 3.1415926535897932384626433832795\r\n\tdir = normalize(dir);\r\n\ttccoord.x = atan(dir.y,-dir.x) / (PI*2.0);\r\n\ttccoord.y = acos(dir.z) / PI;\r\n\t\r\n\tvec3 sky = vec3(texture2D(s_base, tccoord));\r\n#else\r\n\r\n\tdir.z *= 3.0;\r\n\tdir.xy /= 0.5*length(dir);\r\n\ttccoord = (dir.xy + e_time*0.03125);\r\n\tvec3 sky = vec3(texture2D(s_base, tccoord));\r\n\ttccoord = (dir.xy + e_time*0.0625);\r\n\tvec4 clouds = texture2D(s_cloud, tccoord);\r\n\tsky = (sky.rgb*(1.0-clouds.a)) + (clouds.a*clouds.rgb);\r\n#endif\r\n\r\n#ifdef FOG\r\n\tsky.rgb = mix(sky.rgb, w_fogcolour, float(r_skyfog)*w_fogalpha);\t//flat fog ignoring actual geometry\r\n\t//sky = fog3(sky);\t\t\t\t\t\t\t\t\t\t\t\t\t//fog according to actual geometry\r\n#endif\r\n\tgl_FragColor = vec4(sky, 1.0);\r\n}\r\n#endif\r\n"
  },
  {
    "path": "engine/shaders/glsl/defaultskybox.glsl",
    "content": "!!permu FOG\r\n!!samps reflectcube\r\n!!cvardf r_skyfog=0.5\r\n!!cvard4 r_glsl_skybox_orientation=0 0 0 0\r\n!!cvardf r_glsl_skybox_autorotate=1\r\n#include \"sys/defs.h\"\r\n#include \"sys/fog.h\"\r\n\r\n//simple shader for simple skyboxes.\r\n\r\nvarying vec3 pos;\r\n#ifdef VERTEX_SHADER\r\nmat3 rotateAroundAxis(vec4 axis) //xyz axis, with angle in w\r\n{\r\n\tif (bool(r_glsl_skybox_autorotate))\r\n\t\taxis.w *= e_time;\r\n\taxis.w *= (3.14/180.0);\r\n\taxis.xyz = normalize(axis.xyz);\r\n\tfloat s = sin(axis.w);\r\n\tfloat c = cos(axis.w);\r\n\tfloat oc = 1.0 - c;\r\n\r\n\treturn mat3(oc * axis.x * axis.x + c, oc * axis.x * axis.y - axis.z * s, oc * axis.z * axis.x + axis.y * s,\r\n\t\t\toc * axis.x * axis.y + axis.z * s, oc * axis.y * axis.y + c, oc * axis.y * axis.z - axis.x * s,\r\n\t\t\toc * axis.z * axis.x - axis.y * s, oc * axis.y * axis.z + axis.x * s, oc * axis.z * axis.z + c);\r\n}\r\nvoid main ()\r\n{\r\n\tpos = v_position.xyz - e_eyepos;\r\n\r\n\tif (r_glsl_skybox_orientation.xyz != vec3(0.0))\r\n\t\tpos = pos*rotateAroundAxis(r_glsl_skybox_orientation);\r\n\r\n\tgl_Position = ftetransform();\r\n}\r\n#endif\r\n#ifdef FRAGMENT_SHADER\r\nvoid main ()\r\n{\r\n\tvec4 skybox = textureCube(s_reflectcube, pos);\r\n\r\n\t//Fun question: should sky be fogged as if infinite, or as if an actual surface?\r\n#ifdef FOG\r\n\t#if 1\r\n\t\tskybox.rgb = mix(skybox.rgb, w_fogcolour, float(r_skyfog)*w_fogalpha);\t//flat fog ignoring actual geometry\r\n\t#else\r\n\t\tskybox.rgb = mix(skybox.rgb, fog3(skybox.rgb), float(r_skyfog));\t\t//fog in terms of actual geometry distance\r\n\t#endif\r\n#endif\r\n\tgl_FragColor = skybox;\r\n}\r\n#endif\r\n"
  },
  {
    "path": "engine/shaders/glsl/defaultsprite.glsl",
    "content": "!!permu FOG\r\n!!samps 1\r\n//used by both particles and sprites.\r\n//note the fog blending mode is all that differs from defaultadditivesprite\r\n\r\n#include \"sys/defs.h\"\r\n#include \"sys/fog.h\"\r\nvarying vec2 tc;\r\nvarying vec4 vc;\r\n#ifdef VERTEX_SHADER\r\nvoid main ()\r\n{\r\n\ttc = v_texcoord;\r\n\tvc = v_colour;\r\n\tgl_Position = ftetransform();\r\n}\r\n#endif\r\n#ifdef FRAGMENT_SHADER\r\nuniform vec4 e_vlscale;\r\nvoid main ()\r\n{\r\n\tvec4 col = texture2D(s_t0, tc);\r\n#ifdef MASK\r\n\tif (col.a < float(MASK))\r\n\t\tdiscard;\r\n#endif\r\n\tgl_FragColor = fog4blend(col * vc * e_colourident * e_vlscale);\r\n}\r\n#endif\r\n"
  },
  {
    "path": "engine/shaders/glsl/defaultwall.glsl",
    "content": "!!ver 100 450\r\n!!permu TESS\r\n!!permu DELUXE\r\n!!permu FULLBRIGHT\t//lumas rather than no lightmaps\r\n!!permu FOG\r\n!!permu LIGHTSTYLED\r\n!!permu BUMP\r\n!!permu SPECULAR\r\n!!permu REFLECTCUBEMASK\r\n!!permu FAKESHADOWS\r\n!!cvardf r_glsl_offsetmapping_scale\r\n!!cvardf r_glsl_emissive=1\r\n!!cvardf r_glsl_pcf\r\n!!cvardf r_tessellation_level=5\r\n!!samps diffuse\r\n!!samps !EIGHTBIT =FULLBRIGHT fullbright\r\n!!samps !EIGHTBIT =BUMP normalmap\r\n!!samps !EIGHTBIT =REFLECTCUBEMASK reflectmask reflectcube\r\n//diffuse gives us alpha, and prevents dlight from bugging out when there's no diffuse.\r\n!!samps =EIGHTBIT paletted 1\r\n!!samps =SPECULAR specular\r\n!!samps !VERTEXLIT lightmap\r\n!!samps =LIGHTSTYLED lightmap1 lightmap2 lightmap3\r\n!!samps =DELUXE deluxemap\r\n!!samps =LIGHTSTYLED =DELUXE deluxemap1 deluxemap2 deluxemap3\r\n!!samps =FAKESHADOWS shadowmap\r\n\r\n#if defined(ORM) || defined(SG)\r\n    #define PBR\r\n#endif\r\n\r\n#include \"sys/defs.h\"\r\n\r\n//this is what normally draws all of your walls, even with rtlights disabled\r\n//note that the '286' preset uses drawflat_walls instead.\r\n\r\n#include \"sys/fog.h\"\r\n\r\n#if !defined(TESS_CONTROL_SHADER)\r\n\t#if defined(OFFSETMAPPING) || defined(SPECULAR) || defined(REFLECTCUBEMASK) || defined(PBR)\r\n\t\tvarying vec3 eyevector;\r\n\t#endif\r\n\r\n\t#if defined(REFLECTCUBEMASK) || defined(BUMPMODELSPACE)\r\n\t\tvarying mat3 invsurface;\r\n\t#endif\r\n\r\n\tvarying vec2 tc;\r\n\t#ifdef VERTEXLIT\r\n\t\tvarying vec4 vc;\r\n\t#else\r\n\t\t#ifdef LIGHTSTYLED\r\n\t\t\t//we could use an offset, but that would still need to be per-surface which would break batches\r\n\t\t\t//fixme: merge attributes?\r\n\t\t\tvarying vec2 lm0, lm1, lm2, lm3;\r\n\t\t#else\r\n\t\t\tvarying vec2 lm0;\r\n\t\t#endif\r\n\t#endif\r\n\r\n\t#ifdef FAKESHADOWS\t\r\n\t\tvarying vec4 vtexprojcoord;\r\n\t#endif\r\n#endif\r\n\r\n#ifdef VERTEX_SHADER\r\n#ifdef TESS\r\nvarying vec3 vertex, normal;\r\n#endif\r\nvoid main ()\r\n{\r\n#if defined(OFFSETMAPPING) || defined(SPECULAR) || defined(REFLECTCUBEMASK) || defined(PBR)\r\n\tvec3 eyeminusvertex = e_eyepos - v_position.xyz;\r\n\teyevector.x = dot(eyeminusvertex, v_svector.xyz);\r\n\teyevector.y = dot(eyeminusvertex, v_tvector.xyz);\r\n\teyevector.z = dot(eyeminusvertex, v_normal.xyz);\r\n#endif\r\n#if defined(REFLECTCUBEMASK) || defined(BUMPMODELSPACE)\r\n\tinvsurface = mat3(v_svector, v_tvector, v_normal);\r\n#endif\r\n\ttc = v_texcoord;\r\n#ifdef FLOWV\r\n\ttc.st += e_time * vec2(FLOWV);\r\n#endif\r\n#ifdef FLOW\r\n\ttc.s += e_time * -0.5;\r\n#endif\r\n#ifdef VERTEXLIT\r\n\t#ifdef LIGHTSTYLED\r\n\t//FIXME, only one colour.\r\n\tvc = v_colour * e_lmscale[0];\r\n\t#else\r\n\tvc = v_colour * e_lmscale;\r\n\t#endif\r\n#else\r\n\tlm0 = v_lmcoord;\r\n#ifdef LIGHTSTYLED\r\n\tlm1 = v_lmcoord2;\r\n\tlm2 = v_lmcoord3;\r\n\tlm3 = v_lmcoord4;\r\n#endif\r\n#endif\r\n\r\n#ifdef TESS\r\n\tvertex = v_position;\r\n\tnormal = v_normal;\r\n#endif\r\n\r\n#ifdef FAKESHADOWS\t\r\n\tgl_Position = ftetransform();\r\n\tvtexprojcoord = (l_cubematrix*vec4(v_position.xyz, 1.0));\r\n#else\r\n\tgl_Position = ftetransform();\r\n#endif\r\n}\r\n#endif\r\n\r\n\r\n#if defined(TESS_CONTROL_SHADER)\r\nlayout(vertices = 3) out;\r\n\r\nin vec3 vertex[];\r\nout vec3 t_vertex[];\r\nin vec3 normal[];\r\nout vec3 t_normal[];\r\n#if defined(OFFSETMAPPING) || defined(SPECULAR) || defined(REFLECTCUBEMASK) || defined(PBR)\r\n\tin vec3 eyevector[];\r\n\tout vec3 t_eyevector[];\r\n#endif\r\n#ifdef REFLECTCUBEMASK\r\n\tin mat3 invsurface[];\r\n\tout mat3 t_invsurface[];\r\n#endif\r\nin vec2 tc[];\r\nout vec2 t_tc[];\r\n#ifdef VERTEXLIT\r\n\tin vec4 vc[];\r\n\tout vec4 t_vc[];\r\n#else\r\n\tin vec2 lm0[];\r\n\tout vec2 t_lm0[];\r\n\t#ifdef LIGHTSTYLED\r\n\t\tin vec2 lm1[], lm2[], lm3[];\r\n\t\tout vec2 t_lm1[], t_lm2[], t_lm3[];\r\n\t#endif\r\n#endif\r\nvoid main()\r\n{\r\n\t//the control shader needs to pass stuff through\r\n#define id gl_InvocationID\r\n\tt_vertex[id] = vertex[id];\r\n\tt_normal[id] = normal[id];\r\n\t#ifdef REFLECTCUBEMASK\r\n\t\tt_invsurface[id] = invsurface[id];\r\n\t#endif\r\n\tt_tc[id] = tc[id];\r\n\t#ifdef VERTEXLIT\r\n\t\tt_vc[id] = vc[id];\r\n\t#else\r\n\t\tt_lm0[id] = lm0[id];\r\n\t\t#ifdef LIGHTSTYLED\r\n\t\t\tt_lm1[id] = lm1[id];\r\n\t\t\tt_lm2[id] = lm2[id];\r\n\t\t\tt_lm3[id] = lm3[id];\r\n\t\t#endif\r\n\t#endif\r\n\r\n\t#if defined(SPECULAR) || defined(OFFSETMAPPING) || defined(REFLECTCUBEMASK) || defined(PBR)\r\n\t\tt_eyevector[id] = eyevector[id];\r\n\t#endif\r\n\r\n\tgl_TessLevelOuter[0] = float(r_tessellation_level);\r\n\tgl_TessLevelOuter[1] = float(r_tessellation_level);\r\n\tgl_TessLevelOuter[2] = float(r_tessellation_level);\r\n\tgl_TessLevelInner[0] = float(r_tessellation_level);\r\n}\r\n#endif\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n#if defined(TESS_EVALUATION_SHADER)\r\nlayout(triangles) in;\r\n\r\nin vec3 t_vertex[];\r\nin vec3 t_normal[];\r\n#if defined(OFFSETMAPPING) || defined(SPECULAR) || defined(REFLECTCUBEMASK) || defined(PBR)\r\n\tin vec3 t_eyevector[];\r\n#endif\r\n#ifdef REFLECTCUBEMASK\r\n\tin mat3 t_invsurface[];\r\n#endif\r\nin vec2 t_tc[];\r\n#ifdef VERTEXLIT\r\n\tin vec4 t_vc[];\r\n#else\r\n\t#ifdef LIGHTSTYLED\r\n\t\t//we could use an offset, but that would still need to be per-surface which would break batches\r\n\t\t//fixme: merge attributes?\r\n\t\tin vec2 t_lm0[], t_lm1[], t_lm2[], t_lm3[];\r\n\t#else\r\n\t\tin vec2 t_lm0[];\r\n\t#endif\r\n#endif\r\n\r\n#define LERP(a) (gl_TessCoord.x*a[0] + gl_TessCoord.y*a[1] + gl_TessCoord.z*a[2])\r\nvoid main()\r\n{\r\n#define factor 1.0\r\n\ttc = LERP(t_tc);\r\n\t#ifdef VERTEXLIT\r\n\t\tvc = LERP(t_vc);\r\n\t#else\r\n\t\tlm0 = LERP(t_lm0);\r\n\t\t#ifdef LIGHTSTYLED\r\n\t\t\tlm1 = LERP(t_lm1);\r\n\t\t\tlm2 = LERP(t_lm2);\r\n\t\t\tlm3 = LERP(t_lm3);\r\n\t\t#endif\r\n\t#endif\r\n\tvec3 w = LERP(t_vertex);\r\n\r\n\tvec3 t0 = w - dot(w-t_vertex[0],t_normal[0])*t_normal[0];\r\n\tvec3 t1 = w - dot(w-t_vertex[1],t_normal[1])*t_normal[1];\r\n\tvec3 t2 = w - dot(w-t_vertex[2],t_normal[2])*t_normal[2];\r\n\tw = w*(1.0-factor) + factor*(gl_TessCoord.x*t0+gl_TessCoord.y*t1+gl_TessCoord.z*t2);\r\n\r\n#if defined(PCF) || defined(SPOT) || defined(CUBE)\r\n\t//for texture projections/shadowmapping on dlights\r\n\tvtexprojcoord = (l_cubematrix*vec4(w.xyz, 1.0));\r\n#endif\r\n\r\n\t//FIXME: we should be recalcing these here, instead of just lerping them\r\n#ifdef REFLECTCUBEMASK\r\n\tinvsurface = LERP(t_invsurface);\r\n#endif\r\n#if defined(SPECULAR) || defined(OFFSETMAPPING) || defined(REFLECTCUBEMASK) || defined(PBR)\r\n\teyevector = LERP(t_eyevector);\r\n#endif\r\n\r\n\tgl_Position = m_modelviewprojection * vec4(w,1.0);\r\n}\r\n#endif\r\n\r\n\r\n\r\n\r\n\r\n\r\n#ifdef FRAGMENT_SHADER\r\n#define s_colourmap\ts_t0\r\n\r\n#include \"sys/pbr.h\"\r\n#include \"sys/pcf.h\"\r\n\r\n#ifdef OFFSETMAPPING\r\n#include \"sys/offsetmapping.h\"\r\n#endif\r\nvoid main ()\r\n{\r\n//adjust texture coords for offsetmapping\r\n#ifdef OFFSETMAPPING\r\n\tvec2 tcoffsetmap = offsetmap(s_normalmap, tc, eyevector);\r\n#define tc tcoffsetmap\r\n#endif\r\n\r\n#if defined(EIGHTBIT) && !defined(LIGHTSTYLED)\r\n\t//optional: round the lightmap coords to ensure all pixels within a texel have different lighting values either. it just looks wrong otherwise.\r\n\t//don't bother if its lightstyled, such cases will have unpredictable correlations anyway.\r\n\t//FIXME: this rounding is likely not correct with respect to software rendering. oh well.\r\n#if __VERSION__ >= 130 && !defined(VERTEXLIT)\r\n\tvec2 lmsize = vec2(textureSize(s_lightmap0, 0));\r\n#else\r\n\t#define lmsize vec2(128.0,2048.0)\r\n#endif\r\n#define texelstolightmap (16.0)\r\n\tvec2 lmcoord0 = floor(lm0 * lmsize*texelstolightmap)/(lmsize*texelstolightmap);\r\n#define lm0 lmcoord0\r\n#endif\r\n\r\n\r\n//Read the base texture (with EIGHTBIT only alpha is needed)\r\n\tvec4 col = texture2D(s_diffuse, tc);\r\n\r\n#if defined(BUMP) && (defined(DELUXE) || defined(SPECULAR) || defined(REFLECTCUBEMASK))\r\n\tvec3 norm = normalize(texture2D(s_normalmap, tc).rgb - 0.5);\r\n#elif defined(PBR) || defined(SPECULAR) || defined(DELUXE) || defined(REFLECTCUBEMASK)\r\n\tvec3 norm = vec3(0, 0, 1);\t//specular lighting expects this to exist.\r\n#endif\r\n\r\n//modulate that by the lightmap(s) including deluxemap(s)\r\n#ifdef VERTEXLIT\r\n\t#ifdef LIGHTSTYLED\r\n\tvec3 lightmaps = vc.rgb;\r\n\t#else\r\n\tvec3 lightmaps = vc.rgb;\r\n\t#endif\r\n\t#define deluxe vec3(0.0,0.0,1.0)\r\n#else\r\n\t#ifdef LIGHTSTYLED\r\n\t\t#define deluxe vec3(0.0,0.0,1.0)\r\n\t\tvec3 lightmaps;\r\n\t\t#ifdef DELUXE\r\n\t\t\tlightmaps  = texture2D(s_lightmap0, lm0).rgb * e_lmscale[0].rgb * dot(norm, 2.0*texture2D(s_deluxemap0, lm0).rgb-0.5);\r\n\t\t\tlightmaps += texture2D(s_lightmap1, lm1).rgb * e_lmscale[1].rgb * dot(norm, 2.0*texture2D(s_deluxemap1, lm1).rgb-0.5);\r\n\t\t\tlightmaps += texture2D(s_lightmap2, lm2).rgb * e_lmscale[2].rgb * dot(norm, 2.0*texture2D(s_deluxemap2, lm2).rgb-0.5);\r\n\t\t\tlightmaps += texture2D(s_lightmap3, lm3).rgb * e_lmscale[3].rgb * dot(norm, 2.0*texture2D(s_deluxemap3, lm3).rgb-0.5);\r\n\t\t#else\r\n\t\t\tlightmaps  = texture2D(s_lightmap0, lm0).rgb * e_lmscale[0].rgb;\r\n\t\t\tlightmaps += texture2D(s_lightmap1, lm1).rgb * e_lmscale[1].rgb;\r\n\t\t\tlightmaps += texture2D(s_lightmap2, lm2).rgb * e_lmscale[2].rgb;\r\n\t\t\tlightmaps += texture2D(s_lightmap3, lm3).rgb * e_lmscale[3].rgb;\r\n\t\t#endif\r\n\t#else\r\n\t\tvec3 lightmaps = (texture2D(s_lightmap, lm0) * e_lmscale).rgb;\r\n\t\t//modulate by the  bumpmap dot light\r\n\t\t#ifdef DELUXE\r\n\t\t\tvec3 deluxe = (texture2D(s_deluxemap, lm0).rgb-0.5);\r\n\t\t\t#ifdef BUMPMODELSPACE\r\n\t\t\t\tdeluxe = normalize(deluxe*invsurface);\r\n\t\t\t#else\r\n\t\t\t\tdeluxe = normalize(deluxe);\r\n\t\t\t\tlightmaps *= 2.0 / max(0.25, deluxe.z);\t//counter the darkening from deluxemaps\r\n\t\t\t#endif\r\n\t\t\tlightmaps *= dot(norm, deluxe);\r\n\t\t#else\r\n\t\t\t#define deluxe vec3(0.0,0.0,1.0)\r\n\t\t#endif\r\n\t#endif\r\n#endif\r\n\r\n//\tcol *= factor_base;\r\n\t#ifndef IOR\r\n\t\t#define IOR 1.5\t//Index Of Reflection.\r\n\t#endif\r\n\t#define dielectricSpecular pow(((IOR - 1.0)/(IOR + 1.0)),2.0)\r\n\t#ifdef SPECULAR\r\n\t\tvec4 specs = texture2D(s_specular, tc);//*factor_spec;\r\n\t\t#ifdef ORM\r\n\t\t\t#define occlusion specs.r\r\n\t\t\t#define roughness specs.g\r\n\t\t\t#define metalness specs.b\r\n\t\t\t#define gloss (1.0-roughness)\r\n\t\t\t#define ambientrgb (specrgb+col.rgb)\r\n\t\t\tvec3 specrgb = mix(vec3(dielectricSpecular), col.rgb, metalness);\r\n\t\t\tvec3 albedorgb = col.rgb * (1.0 - dielectricSpecular) * (1.0-metalness);\r\n\t\t#elif defined(SG) //pbr-style specular+glossiness\r\n\t\t\t//occlusion needs to be baked in. :(\r\n\t\t\t#define roughness (1.0-specs.a)\r\n\t\t\t#define gloss specs.a\r\n\t\t\t#define specrgb specs.rgb\r\n\t\t\t#define ambientrgb (specs.rgb+col.rgb)\r\n\t\t\t#define albedorgb col.rgb\r\n\t\t#elif defined(PBR)\t//PBR using legacy texturemaps\r\n\t\t\t#define gloss specs.a\r\n\t\t\t#define roughness (1.0-gloss)\r\n\t\t\t//metalness not relevant\r\n\r\n\t\t\t//our pbr stuff doesn't much like our inputs.\r\n\t\t\tvec3 specrgb, albedorgb;\r\n\t\t\t//if (1==0)\r\n\t\t\t//{\t//metal\r\n\t\t\t//\tspecrgb = col.rgb;//+specs.rgb;\r\n\t\t\t//\talbedorgb = vec3(0.0);\r\n\t\t\t//}\r\n\t\t\t//else\r\n\t\t\t//{\t//non-metal\r\n\t\t\t\tspecrgb = vec3(dielectricSpecular);\r\n\t\t\t\talbedorgb = col.rgb;//+specs.rgb;\r\n\t\t\t//}\r\n\t\t\t#define ambientrgb col.rgb\r\n\t\t#else   //blinn-phong\r\n\t\t\t#define gloss specs.a\r\n\t\t\t//occlusion not defined\r\n\t\t\t#define specrgb specs.rgb\r\n\t\t#endif\r\n\t#else\r\n\t\t//no specular map specified. doesn't mean we shouldn't have any though, at least with pbr enabled.\r\n\t\t#define roughness 0.3\r\n\t\t#define specrgb 1.0 //vec3(dielectricSpecular)\r\n\t\t#define albedorgb col.rgb\r\n\t#endif\r\n\r\n\t//add in specular, if applicable.\r\n\t#ifdef PBR\r\n\t\tcol.rgb = DoPBR(norm, normalize(eyevector), deluxe, roughness, albedorgb, specrgb, vec3(0.0,1.0,1.0));//*e_light_mul + e_light_ambient*.25*ambientrgb;\r\n\t#elif defined(gloss)\r\n\t\tvec3 halfdir = normalize(normalize(eyevector) + deluxe);\t//this norm should be the deluxemap info instead\r\n\t\tfloat spec = pow(max(dot(halfdir, norm), 0.0), FTE_SPECULAR_EXPONENT * gloss);\r\n\t\tspec *= FTE_SPECULAR_MULTIPLIER;\r\n\t//NOTE: rtlights tend to have a *4 scaler here to over-emphasise the effect because it looks cool.\r\n\t//As not all maps will have deluxemapping, and the double-cos from the light util makes everything far too dark anyway,\r\n\t//we default to something that is not garish when the light value is directly infront of every single pixel.\r\n\t//we can justify this difference due to the rtlight editor etc showing the *4.\r\n\t\tcol.rgb += spec * specrgb;\r\n\t#endif\r\n\r\n#ifdef REFLECTCUBEMASK\r\n\tvec3 rtc = reflect(normalize(-eyevector), norm);\r\n\t//todo: parallax correction: https://seblagarde.wordpress.com/2012/09/29/image-based-lighting-approaches-and-parallax-corrected-cubemap/\r\n\t//norm (and also eyevector) are in tangentspace but our cubemap wants worldspace, so convert.\r\n\trtc = rtc.x*invsurface[0] + rtc.y*invsurface[1] + rtc.z*invsurface[2];\r\n\trtc = (m_model * vec4(rtc.xyz,0.0)).xyz;\r\n\tcol.rgb += texture2D(s_reflectmask, tc).rgb * textureCube(s_reflectcube, rtc).rgb;\r\n#endif\r\n\r\n#ifdef EIGHTBIT //FIXME: with this extra flag, half the permutations are redundant.\r\n\tlightmaps *= 0.5;\t//counter the fact that the colourmap contains overbright values and logically ranges from 0 to 2 intead of to 1.\r\n\tfloat pal = texture2D(s_paletted, tc).r;\t//the palette index. hopefully not interpolated.\r\n\tlightmaps -= 1.0 / 128.0;\t//software rendering appears to round down, so make sure we favour the lower values instead of rounding to the nearest\r\n\tcol.r = texture2D(s_colourmap, vec2(pal, 1.0-lightmaps.r)).r;\t//do 3 lookups. this is to cope with lit files, would be a waste to not support those.\r\n\tcol.g = texture2D(s_colourmap, vec2(pal, 1.0-lightmaps.g)).g;\t//its not very softwarey, but re-palettizing is ugly.\r\n\tcol.b = texture2D(s_colourmap, vec2(pal, 1.0-lightmaps.b)).b;\t//without lits, it should be identical.\r\n#else\r\n\t//now we have our diffuse+specular terms, modulate by lightmap values.\r\n\t#if defined(FULLBRIGHT)\r\n\t\tvec4 fb = texture2D(s_fullbright, tc);\r\n\t\t#if r_glsl_emissive==0\t//q2e-like mask that gets darker when lights get overbright.\r\n\t\t\tcol.rgb *= mix(lightmaps.rgb, vec3(1.0), fb.rgb*fb.a);\r\n\t\t#else\t//actually emissive layer\r\n\t\t\tcol.rgb = col.rgb * lightmaps.rgb + fb.rgb*fb.a;\r\n\t\t#endif\r\n\t#else\r\n\t\tcol.rgb *= lightmaps.rgb;\r\n\t#endif\r\n#endif\r\n\r\n//entity modifiers\r\n\tcol *= e_colourident;\r\n\r\n#ifdef FAKESHADOWS\r\n\t/*filter the light by the shadowmap. logically a boolean, but we allow fractions for softer shadows*/\r\n\tcol.rgb *= ShadowmapFilter(s_shadowmap, vtexprojcoord);\r\n\t//col.g = ShadowmapFilter(s_shadowmap, vtexprojcoord);\r\n#endif\r\n\r\n#if defined(MASK)\r\n#if defined(MASKLT)\r\n\tif (col.a < MASK)\r\n\t\tdiscard;\r\n#else\r\n\tif (col.a >= MASK)\r\n\t\tdiscard;\r\n#endif\r\n\tcol.a = 1.0;\t//alpha blending AND alpha testing usually looks stupid, plus it screws up our fog.\r\n#endif\r\n\r\n//and finally hide it all if we're fogged.\r\n\tgl_FragColor = fog4(col);\r\n}\r\n#endif\r\n\r\n"
  },
  {
    "path": "engine/shaders/glsl/defaultwarp.glsl",
    "content": "!!ver 100 450\r\n!!permu FOG\r\n!!samps diffuse lightmap\r\n\r\n#include \"sys/defs.h\"\r\n\r\n//this is the shader that's responsible for drawing default q1 turbulant water surfaces\r\n//this is expected to be moderately fast.\r\n\r\n#include \"sys/fog.h\"\r\n\r\nvarying vec2 tc;\r\n#ifdef LIT\r\nvarying vec2 lm0;\r\n#endif\r\n#ifdef VERTEX_SHADER\r\nvoid main ()\r\n{\r\n\ttc = v_texcoord.st;\r\n\t#ifdef FLOWV\r\n\ttc.st += e_time * vec2(FLOWV);\r\n\t#endif\r\n\t#ifdef FLOW\r\n\ttc.s += e_time * -0.5;\r\n\t#endif\r\n\t#ifdef LIT\r\n\tlm0 = v_lmcoord;\r\n\t#endif\r\n\tgl_Position = ftetransform();\r\n}\r\n#endif\r\n#ifdef FRAGMENT_SHADER\r\nvoid main ()\r\n{\r\n\tvec2 ntc = tc + sin(tc.ts+e_time)*0.125;\r\n\tvec3 ts = vec3(texture2D(s_diffuse, ntc));\r\n\r\n#ifdef LIT\r\n\tts *= (texture2D(s_lightmap, lm0) * e_lmscale).rgb;\r\n#endif\r\n\r\n#ifdef ALPHA\r\n\tgl_FragColor = fog4blend(vec4(ts, float(ALPHA)) * e_colourident);\r\n#else\r\n\tgl_FragColor = fog4(vec4(ts, 1.0) * e_colourident);\r\n#endif\r\n}\r\n#endif\r\n"
  },
  {
    "path": "engine/shaders/glsl/depthonly.glsl",
    "content": "!!ver 100 150\r\n!!permu TESS\r\n!!permu FRAMEBLEND\r\n!!permu SKELETAL\r\n!!cvardf r_tessellation_level=5\r\n\r\n#include \"sys/defs.h\"\r\n\r\n//standard shader used for drawing shadowmap depth.\r\n//also used for masking off portals and other things that want depth and no colour.\r\n//must support skeletal and 2-way vertex blending or Bad Things Will Happen.\r\n//the vertex shader is responsible for calculating lighting values.\r\n\r\n#ifdef VERTEX_SHADER\r\n#include \"sys/skeletal.h\"\r\n#ifdef TESS\r\nvarying vec3 vertex;\r\nvarying vec3 normal;\r\n#endif\r\nvoid main ()\r\n{\r\n#ifdef TESS\r\n\tskeletaltransform_n(normal);\r\n\tvertex = v_position;\r\n#else\r\n\tgl_Position = skeletaltransform();\r\n#endif\r\n}\r\n#endif\r\n\r\n\r\n\r\n#if defined(TESS_CONTROL_SHADER)\r\nlayout(vertices = 3) out;\r\n\r\nin vec3 vertex[];\r\nout vec3 t_vertex[];\r\nin vec3 normal[];\r\nout vec3 t_normal[];\r\n\r\nvoid main()\r\n{\r\n\t//the control shader needs to pass stuff through\r\n#define id gl_InvocationID\r\n\tt_vertex[id] = vertex[id];\r\n\tt_normal[id] = normal[id];\r\n\tgl_TessLevelOuter[0] = float(r_tessellation_level);\r\n\tgl_TessLevelOuter[1] = float(r_tessellation_level);\r\n\tgl_TessLevelOuter[2] = float(r_tessellation_level);\r\n\tgl_TessLevelInner[0] = float(r_tessellation_level);\r\n}\r\n#endif\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n#if defined(TESS_EVALUATION_SHADER)\r\nlayout(triangles) in;\r\n\r\nin vec3 t_vertex[];\r\nin vec3 t_normal[];\r\n\r\n#define LERP(a) (gl_TessCoord.x*a[0] + gl_TessCoord.y*a[1] + gl_TessCoord.z*a[2])\r\nvoid main()\r\n{\r\n#define factor 1.0\r\n\tvec3 w = LERP(t_vertex);\r\n\r\n\tvec3 t0 = w - dot(w-t_vertex[0],t_normal[0])*t_normal[0];\r\n\tvec3 t1 = w - dot(w-t_vertex[1],t_normal[1])*t_normal[1];\r\n\tvec3 t2 = w - dot(w-t_vertex[2],t_normal[2])*t_normal[2];\r\n\tw = w*(1.0-factor) + factor*(gl_TessCoord.x*t0+gl_TessCoord.y*t1+gl_TessCoord.z*t2);\r\n\r\n\tgl_Position = m_modelviewprojection * vec4(w,1.0);\r\n}\r\n#endif\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n#ifdef FRAGMENT_SHADER\r\nvoid main ()\r\n{\r\n\t//must always draw something, supposedly. It might as well be black.\r\n\tgl_FragColor = vec4(0, 0, 0, 1);\r\n}\r\n#endif\r\n\r\n"
  },
  {
    "path": "engine/shaders/glsl/drawflat_wall.glsl",
    "content": "!!cvard_srgb_b r_floorcolor\r\n!!cvard_srgb_b r_wallcolor\r\n!!permu FOG\r\n!!samps lm=0\r\n\r\n//this is for the '286' preset walls, and just draws lightmaps coloured based upon surface normals.\r\n\r\n#include \"sys/fog.h\"\r\nvarying vec4 col;\r\n#ifdef VERTEX_SHADER\r\nattribute vec3 v_normal;\r\nattribute vec2 v_lmcoord;\r\nvarying vec2 lm;\r\nuniform vec4 e_lmscale;\r\nvoid main ()\r\n{\r\n#ifdef LM\r\n\tcol = vec4(1.0);\r\n#else\r\n\tcol = vec4(e_lmscale.rgb * ((v_normal.z < 0.73)?r_wallcolor:r_floorcolor), e_lmscale.a);\r\n#endif\r\n\tlm = v_lmcoord;\r\n\tgl_Position = ftetransform();\r\n}\r\n#endif\r\n#ifdef FRAGMENT_SHADER\r\nvarying vec2 lm;\r\nvoid main ()\r\n{\r\n\tgl_FragColor = fog4(col * texture2D(s_lm, lm));\r\n}\r\n#endif\r\n"
  },
  {
    "path": "engine/shaders/glsl/fixedemu.glsl",
    "content": "!!ver 100-450\r\n!!samps sourcetex=0\r\n\r\n//this shader is present for support for gles/gl3core contexts\r\n//it is single-texture-with-vertex-colours, and doesn't do anything special.\r\n//beware that a few things use this, including apparently fonts and bloom rescaling.\r\n//its really not meant to do anything special.\r\n\r\n#ifdef VERTEX_SHADER\r\nattribute vec2 v_texcoord;\r\nvarying vec2 tc;\r\n#ifndef UC\r\nattribute vec4 v_colour;\r\nvarying vec4 vc;\r\n#endif\r\nvoid main ()\r\n{\r\n\ttc = v_texcoord;\r\n#ifndef UC\r\n\tvc = v_colour;\r\n#endif\r\n\tgl_Position = ftetransform();\r\n}\r\n#endif\r\n#ifdef FRAGMENT_SHADER\r\nvarying vec2 tc;\r\n#ifndef UC\r\nvarying vec4 vc;\r\n#else\r\nuniform vec4 s_colour;\r\n#define vc s_colour\r\n#endif\r\nfloat e_time;\r\nvoid main ()\r\n{\r\n\tvec4 fc = texture2D(s_sourcetex, tc) * vc;\r\n#ifdef ALPHATEST\r\n\tif (!(fc.a ALPHATEST))\r\n\t\tdiscard;\r\n#endif\r\n\tgl_FragColor = fc;\r\n}\r\n#endif\r\n"
  },
  {
    "path": "engine/shaders/glsl/fxaa.glsl",
    "content": "!!samps 1\r\n#include \"sys/defs.h\"\r\n//\r\n//This shader implements super-sampled anti-aliasing.\r\n//\r\n\r\nvarying vec2 texcoord;\r\n\r\n#ifdef VERTEX_SHADER\r\nvoid main()\r\n{\r\n\ttexcoord = v_texcoord.xy;\r\n\ttexcoord.y = 1.0 - texcoord.y;\r\n\tgl_Position = ftetransform();\r\n}\r\n#endif\r\n#ifdef FRAGMENT_SHADER\r\nuniform vec2 e_sourcesize;\r\n\r\nvoid main( void )\r\n{\r\n\tfloat FXAA_SPAN_MAX = 8.0;\r\n\tfloat FXAA_REDUCE_MUL = 1.0/8.0;\r\n\tfloat FXAA_REDUCE_MIN = 1.0/128.0;\r\n\r\n\tvec3 rgbNW=texture2D(s_t0,texcoord+(vec2(-1.0,-1.0)/e_sourcesize)).xyz;\r\n\tvec3 rgbNE=texture2D(s_t0,texcoord+(vec2(1.0,-1.0)/e_sourcesize)).xyz;\r\n\tvec3 rgbSW=texture2D(s_t0,texcoord+(vec2(-1.0,1.0)/e_sourcesize)).xyz;\r\n\tvec3 rgbSE=texture2D(s_t0,texcoord+(vec2(1.0,1.0)/e_sourcesize)).xyz;\r\n\tvec3 rgbM=texture2D(s_t0,texcoord).xyz;\r\n\r\n\tvec3 luma=vec3(0.299, 0.587, 0.114);\r\n\tfloat lumaNW = dot(rgbNW, luma);\r\n\tfloat lumaNE = dot(rgbNE, luma);\r\n\tfloat lumaSW = dot(rgbSW, luma);\r\n\tfloat lumaSE = dot(rgbSE, luma);\r\n\tfloat lumaM  = dot(rgbM,  luma);\r\n\r\n\tfloat lumaMin = min(lumaM, min(min(lumaNW, lumaNE), min(lumaSW, lumaSE)));\r\n\tfloat lumaMax = max(lumaM, max(max(lumaNW, lumaNE), max(lumaSW, lumaSE)));\r\n\r\n\tvec2 dir;\r\n\tdir.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE));\r\n\tdir.y =  ((lumaNW + lumaSW) - (lumaNE + lumaSE));\r\n\r\n\tfloat dirReduce = max((lumaNW + lumaNE + lumaSW + lumaSE) * (0.25 * FXAA_REDUCE_MUL), FXAA_REDUCE_MIN);\r\n\r\n\tfloat rcpDirMin = 1.0/(min(abs(dir.x), abs(dir.y)) + dirReduce);\r\n\r\n\tdir = min(vec2( FXAA_SPAN_MAX,  FXAA_SPAN_MAX),\r\n\tmax(vec2(-FXAA_SPAN_MAX, -FXAA_SPAN_MAX),\r\n\tdir * rcpDirMin)) / e_sourcesize;\r\n\r\n\tvec3 rgbA = (1.0/2.0) * (texture2D(s_t0, texcoord.xy + dir * (1.0/3.0 - 0.5)).xyz +\ttexture2D(s_t0, texcoord.xy + dir * (2.0/3.0 - 0.5)).xyz);\r\n\tvec3 rgbB = rgbA * (1.0/2.0) + (1.0/4.0) * (texture2D(s_t0, texcoord.xy + dir * (0.0/3.0 - 0.5)).xyz + texture2D(s_t0, texcoord.xy + dir * (3.0/3.0 - 0.5)).xyz);\r\n\tfloat lumaB = dot(rgbB, luma);\r\n\r\n\tif((lumaB < lumaMin) || (lumaB > lumaMax))\r\n\t\tgl_FragColor.xyz=rgbA;\r\n\telse\r\n\t\tgl_FragColor.xyz=rgbB;\r\n\tgl_FragColor.a = 1.0;\r\n}\r\n#endif\r\n"
  },
  {
    "path": "engine/shaders/glsl/itemtimer.glsl",
    "content": "!!permu FOG\r\n\r\n#include \"sys/defs.h\"\r\n#include \"sys/fog.h\"\r\n\r\nvarying vec2 tc;\r\nvarying vec4 vc;\r\n\r\n#ifdef VERTEX_SHADER\r\nvoid main ()\r\n{\r\n\ttc = v_texcoord;\r\n\tvc = v_colour;\r\n\tgl_Position = ftetransform();\r\n}\r\n#endif\r\n\r\n\r\n#ifdef FRAGMENT_SHADER\r\nvoid main ()\r\n{\r\n\tgl_FragColor = vec4(0.5,0.5,0.5,1);//texture2D(s_diffuse, tc.xy);\r\n\r\n\tvec2 st = (tc-floor(tc)) - 0.5;\r\n\tst *= 2.0;\r\n\tfloat dist = sqrt(dot(st,st));\r\n\r\n\tfloat ring = 1.0 + smoothstep(0.9, 1.0, dist)\r\n\t\t\t\t - smoothstep(0.8, 0.9, dist);\r\n\r\n\t//fade out the rim\r\n\tif ((atan(st.t, st.s)+3.14)/6.28 > vc.a)\r\n\t\tgl_FragColor.a *= 0.25;\r\n\tgl_FragColor.rgb *= mix(vc.rgb, vec3(0.0), ring);\r\n//gl_FragColor.a;\r\n\r\n//and finally hide it all if we're fogged.\r\n#ifdef FOG\r\n\tgl_FragColor = fog4additive(gl_FragColor);\r\n#endif\r\n}\r\n#endif\r\n\r\n"
  },
  {
    "path": "engine/shaders/glsl/lpp_depthnorm.glsl",
    "content": "!!ver 100 130\r\n!!permu BUMP\r\n!!permu SKELETAL\r\n!!permu FRAMEBLEND\r\n!!cvarf r_glsl_offsetmapping_scale\r\n!!samps normalmap specular\r\n\r\n//light pre-pass rendering (defered lighting)\r\n//this is the initial pass, that draws the surface normals and depth to the initial colour buffer\r\n\r\n#include \"sys/defs.h\"\r\n\r\n#if defined(OFFSETMAPPING)\r\nvarying vec3 eyevector;\r\n#endif\r\n\r\nvarying vec3 norm;\r\n#if defined(BUMP)\r\nvarying vec3 tang, bitang;\r\n#endif\r\n#if defined(BUMP) || defined(SPECULAR)\r\nvarying vec2 tc;\r\n#endif\r\n#ifdef VERTEX_SHADER\r\n#include \"sys/skeletal.h\"\r\n\r\nvoid main()\r\n{\r\n#if defined(BUMP)\r\n\tgl_Position = skeletaltransform_nst(norm, tang, bitang);\r\n#else\r\n\tgl_Position = skeletaltransform_n(norm);\r\n#endif\r\n#if defined(BUMP) || defined(SPECULAR)\r\n\ttc = v_texcoord;\r\n#endif\r\n\r\n#if defined(OFFSETMAPPING)\r\n\tvec3 eyeminusvertex = e_eyepos - v_position.xyz;\r\n\teyevector.x = dot(eyeminusvertex, v_svector.xyz);\r\n\teyevector.y = dot(eyeminusvertex, v_tvector.xyz);\r\n\teyevector.z = dot(eyeminusvertex, v_normal.xyz);\r\n#endif\r\n}\r\n#endif\r\n#ifdef FRAGMENT_SHADER\r\n#ifdef OFFSETMAPPING\r\n#include \"sys/offsetmapping.h\"\r\n#endif\r\nvoid main()\r\n{\r\n//adjust texture coords for offsetmapping\r\n#ifdef OFFSETMAPPING\r\n\tvec2 tcoffsetmap = offsetmap(s_normalmap, tc, eyevector);\r\n#define tc tcoffsetmap\r\n#endif\r\n\r\n\tvec3 onorm;\r\n\tvec4 ospec;\r\n\r\n//need to write surface normals so that light shines on the surfaces properly\r\n#if defined(BUMP)\r\n\tvec3 bm = 2.0*texture2D(s_normalmap, tc).xyz - 1.0;\r\n\tonorm = normalize(bm.x * tang + bm.y * bitang + bm.z * norm);\r\n#else\r\n\tonorm = norm;\r\n#endif\r\n\r\n//we need to write specular exponents if we want per-pixel control over that\r\n#if defined(SPECULAR)\r\n\tospec = texture2D(s_specular, tc);\r\n#else\r\n\tospec = vec4(0.0, 0.0, 0.0, 0.0);\r\n#endif\r\n\r\n\tgl_FragColor = vec4(onorm.xyz, ospec.a * FTE_SPECULAR_EXPONENT);\r\n}\r\n#endif\r\n"
  },
  {
    "path": "engine/shaders/glsl/lpp_light.glsl",
    "content": "//this shader is a light shader. ideally drawn with a quad covering the entire region\r\n//the output is contribution from this light (which will be additively blended)\r\n//you can blame Electro for much of the maths in here.\r\n!!ver 100 450\r\n//FIXME: !!permu FOG\r\n!!samps shadowmap 2\r\n\r\n#include \"sys/defs.h\"\r\n#include \"sys/pcf.h\"\r\n\r\n//s_t0 is the depth\r\n//s_t1 is the normals+spec-exponent\r\n//output should be amount of light hitting the surface.\r\n\r\nvarying vec4 tf;\r\n#ifdef VERTEX_SHADER\r\nvoid main()\r\n{\r\n\ttf = ftetransform();\r\n\tgl_Position = tf;\r\n}\r\n#endif\r\n#ifdef FRAGMENT_SHADER\r\n\r\n#define out_diff fte_fragdata0\r\n#define out_spec fte_fragdata1\r\n\r\nvec3 calcLightWorldPos(vec2 screenPos, float depth)\r\n{\r\n\tvec4 pos = m_invviewprojection * vec4(screenPos.xy, (depth*2.0)-1.0, 1.0);\r\n\treturn pos.xyz / pos.w;\r\n}\r\nvoid main ()\r\n{\r\n\tvec3 lightColour\t\t= l_lightcolour.rgb;\r\n\r\n\tvec2 fc = tf.xy / tf.w;\r\n\tvec2 gc = (1.0 + fc) / 2.0;\r\n\tfloat depth = texture2D(s_t0, gc).r;\r\n\tvec4 data = texture2D(s_t1, gc);\r\n\tvec3 norm = data.xyz;\r\n\tfloat spec_exponent = data.a;\r\n\r\n\t/* calc where the wall that generated this sample came from */\r\n\tvec3 worldPos\t= calcLightWorldPos(fc, depth);\r\n\r\n\t/*we need to know the cube projection (for both cubemaps+shadows)*/\r\n\tvec4 cubeaxis = l_cubematrix*vec4(worldPos.xyz, 1.0);\r\n\r\n\t/*calc ambient lighting term*/\r\n\tvec3 lightDir = l_lightposition - worldPos;\r\n\tfloat atten = max(1.0 - (dot(lightDir, lightDir)/(l_lightradius*l_lightradius)), 0.0);\r\n\r\n\t/*calc diffuse lighting term*/\r\n\tlightDir = normalize(lightDir);\r\n\tfloat nDotL = dot(norm, lightDir);\r\n\tfloat lightDiffuse = max(0.0, nDotL);\r\n\r\n\t/*calc specular lighting term*/\r\n\tvec3 halfdir = normalize(normalize(e_eyepos - worldPos) + lightDir);\t//ASSUMPTION: e_eyepos requires an identity modelmatrix (true for world+sprites, but usually not for models/bsps)\r\n\tfloat spec = pow(max(dot(halfdir, norm), 0.0), spec_exponent);\r\n\r\n\t//fixme: apply fog?\r\n\t//fixme: cubemap filters\r\n\r\n\tfloat shadows = ShadowmapFilter(s_shadowmap, cubeaxis);\r\n\r\n\tout_diff = vec4(lightColour * (l_lightcolourscale.x + l_lightcolourscale.y*lightDiffuse*shadows), 1.0);\r\n\tout_spec = vec4(lightColour * l_lightcolourscale.z*spec*shadows, 1.0);\r\n}\r\n#endif\r\n"
  },
  {
    "path": "engine/shaders/glsl/lpp_wall.glsl",
    "content": "!!ver 100 150\r\n!!permu BUMP\t//for offsetmapping rather than bumpmapping (real bumps are handled elsewhere)\r\n!!cvarf r_glsl_offsetmapping_scale\r\n!!samps diffuse specular fullbright lightmap\r\n!!samps 2\r\n\r\n//the final defered lighting pass.\r\n//the lighting values were written to some render target, which is fed into this shader, and now we draw all the wall textures with it.\r\n\r\n#include \"sys/defs.h\"\r\n\r\n#if defined(OFFSETMAPPING)\r\nvarying vec3 eyevector;\r\n#endif\r\n\r\n\r\nvarying vec2 tc, lm;\r\nvarying vec4 tf;\r\n#ifdef VERTEX_SHADER\r\nvoid main ()\r\n{\r\n\ttc = v_texcoord;\r\n\tlm = v_lmcoord;\r\n\tgl_Position = tf = ftetransform();\r\n\r\n#if defined(OFFSETMAPPING)\r\n\tvec3 eyeminusvertex = e_eyepos - v_position.xyz;\r\n\teyevector.x = dot(eyeminusvertex, v_svector.xyz);\r\n\teyevector.y = dot(eyeminusvertex, v_tvector.xyz);\r\n\teyevector.z = dot(eyeminusvertex, v_normal.xyz);\r\n#endif\r\n}\r\n#endif\r\n#ifdef FRAGMENT_SHADER\r\n#ifdef OFFSETMAPPING\r\n#include \"sys/offsetmapping.h\"\r\n#endif\r\nvoid main ()\r\n{\r\n//adjust texture coords for offsetmapping\r\n#ifdef OFFSETMAPPING\r\n\tvec2 tcoffsetmap = offsetmap(s_normalmap, tc, eyevector);\r\n#define tc tcoffsetmap\r\n#endif\r\n\r\n\tvec2 nst;\r\n\tnst = tf.xy / tf.w;\r\n\tnst = (1.0 + nst) / 2.0;\r\n\tvec4 dl = texture2D(s_t0, nst);\t//diffuse lighting\r\n\tvec4 sl = texture2D(s_t1, nst);\t//specular lighting\r\n\tvec4 c = texture2D(s_diffuse, tc);\r\n\tvec4 s = texture2D(s_specular, tc);\r\n\tvec4 f = texture2D(s_fullbright, tc);\r\n//fixme: top+bottom should add upper+lower colours to c here\r\n\tvec3 lmsamp = texture2D(s_lightmap, lm).rgb*e_lmscale.rgb;\r\n//fixme: fog the legacy lightmap data\r\n\tvec3 diff = dl.rgb + lmsamp;\r\n\tvec3 spec = sl.rgb * float(SPECMUL);\t//should be rgb, but whatever.\r\n\t\r\n//fixme: do specular somehow\r\n\tgl_FragColor = vec4(diff*c.rgb + spec*s.rgb + f.rgb, 1.0);\r\n//fixme: fullbrights should add to the rgb value\r\n}\r\n#endif\r\n"
  },
  {
    "path": "engine/shaders/glsl/menutint.glsl",
    "content": "!!cvari r_menutint_inverse\r\n!!cvard_srgb r_menutint\r\n!!samps 1\r\n\r\n#ifdef VERTEX_SHADER\r\n\t\tattribute vec2 v_texcoord;\r\n\t\tvarying vec2 texcoord;\r\n\t\tuniform vec4 e_rendertexturescale;\r\n\t\tvoid main(void)\r\n\t\t{\r\n\t\t\ttexcoord.x = v_texcoord.x*e_rendertexturescale.x;\r\n\t\t\ttexcoord.y = (1.0-v_texcoord.y)*e_rendertexturescale.y;\r\n\t\t\tgl_Position = ftetransform();\r\n\t\t}\r\n#endif\r\n#ifdef FRAGMENT_SHADER\r\n\r\n\t\tvarying vec2 texcoord;\r\n\t\tuniform int cvar_r_menutint_inverse;\r\n\t\tconst vec3 lumfactors = vec3(0.299, 0.587, 0.114);\r\n\t\tconst vec3 invertvec = vec3(1.0, 1.0, 1.0);\r\n\t\tvoid main(void)\r\n\t\t{\r\n\t\t\tvec3 texcolor = texture2D(s_t0, texcoord).rgb;\r\n\t\t\tfloat luminance = dot(lumfactors, texcolor);\r\n\t\t\ttexcolor = vec3(luminance, luminance, luminance);\r\n\t\t\ttexcolor *= r_menutint;\r\n\t\t\ttexcolor = (cvar_r_menutint_inverse > 0) ? (invertvec - texcolor) : texcolor;\r\n\t\t\tgl_FragColor = vec4(texcolor, 1.0);\r\n\t\t}\r\n#endif\r\n"
  },
  {
    "path": "engine/shaders/glsl/postproc_ascii.glsl",
    "content": "!!cvardf r_glsl_ascii_mono=0\r\n!!samps screen=0\r\n\r\n//derived from https://www.shadertoy.com/view/lssGDj\r\n\r\n#include \"sys/defs.h\"\r\nvarying vec2 texcoord;\r\n\r\n#ifdef VERTEX_SHADER\r\nvoid main()\r\n{\r\n\ttexcoord = v_texcoord.xy;\r\n\ttexcoord.y = 1.0 - texcoord.y;\r\n\tgl_Position = ftetransform();\r\n}\r\n#endif\r\n#ifdef FRAGMENT_SHADER\r\nuniform vec2 e_sourcesize;\r\n\r\nfloat character(float n, vec2 p)\r\n{\r\n\tp = floor(p*vec2(4.0, -4.0) + 2.5);\r\n\tif (clamp(p.x, 0.0, 4.0) == p.x && clamp(p.y, 0.0, 4.0) == p.y)\r\n\t{\r\n\t\tif (int(mod(n/exp2(p.x + 5.0*p.y), 2.0)) == 1) return 1.0;\r\n\t}\t\r\n\treturn 0.0;\r\n}\r\n\r\nvoid main(void)\r\n{\r\n\tvec2 uv = floor(texcoord.xy * e_sourcesize); //in pixels.\r\n\tvec3 col = texture2D(s_screen, (floor(uv/8.0)*8.0+4.0)/e_sourcesize.xy).rgb;\t\r\n\r\n\tfloat gray = 0.3 * col.r + 0.59 * col.g + 0.11 * col.b;\r\n\r\n\tif (float(r_glsl_ascii_mono) != 0.0)\r\n\t\tgray = gray = pow(gray, 0.7);\t//quake is just too dark otherwise.\r\n\telse\r\n\t\tgray = gray = pow(gray, 0.45);\t//col*char is FAR too dark otherwise, and much of the colour will come from the col term anyway.\r\n\r\n\tfloat n =  0.0;              // space\r\n\tif (gray > 0.1) n = 4096.0;\t// .\r\n\tif (gray > 0.2) n = 65600.0;    // :\r\n\tif (gray > 0.3) n = 332772.0;   // *\r\n\tif (gray > 0.4) n = 15255086.0; // o \r\n\tif (gray > 0.5) n = 23385164.0; // &\r\n\tif (gray > 0.6) n = 15252014.0; // 8\r\n\tif (gray > 0.7) n = 13199452.0; // @\r\n\tif (gray > 0.8) n = 11512810.0; // #\r\n\r\n\tvec2 p = mod(uv/4.0, 2.0) - vec2(1.0);\r\n\tif (float(r_glsl_ascii_mono) != 0.0)\r\n\t\tcol = vec3(character(n, p));\r\n\telse\r\n\t\tcol = col*character(n, p);\t//note that this is kinda cheating.\r\n\tgl_FragColor = vec4(col, 1.0);\r\n}\r\n#endif\r\n"
  },
  {
    "path": "engine/shaders/glsl/postproc_equirectangular.glsl",
    "content": "!!cvarf ffov\r\n!!samps screen:samplerCube=0\r\n\r\n//equirectangular view rendering, commonly used for sphere->2d map projections.\r\n\r\n#ifdef VERTEX_SHADER\r\nattribute vec2 v_texcoord;\r\nvarying vec2 texcoord;\r\nvoid main()\r\n{\r\n\ttexcoord = v_texcoord.xy;\r\n\tgl_Position = ftetransform();\r\n}\r\n#endif\r\n#ifdef FRAGMENT_SHADER\r\nvarying vec2 texcoord;\r\nuniform float cvar_ffov;\r\n\r\n#define PI 3.1415926535897932384626433832795\r\nvoid main()\r\n{\r\n\tvec3 tc;\r\n\tfloat lng = (texcoord.x - 0.5) * PI * 2.0;\r\n\tfloat lat = (texcoord.y) * PI * 1.0;\r\n\t\r\n\ttc.z = cos(lng) * sin(lat);\t\r\n\ttc.x = sin(lng) * sin(lat);\r\n\ttc.y = cos(lat);\r\n\tgl_FragColor = textureCube(s_screen, tc);\r\n}\r\n#endif\r\n"
  },
  {
    "path": "engine/shaders/glsl/postproc_fisheye.glsl",
    "content": "!!cvarf ffov\r\n!!samps screen:samplerCube=0\r\n\r\n//fisheye view rendering, for silly fovs that are still playable.\r\n\r\n#ifdef VERTEX_SHADER\r\nattribute vec2 v_texcoord;\r\nvarying vec2 texcoord;\r\nvoid main()\r\n{\r\n\ttexcoord = v_texcoord.xy;\r\n\tgl_Position = ftetransform();\r\n}\r\n#endif\r\n#ifdef FRAGMENT_SHADER\r\nvarying vec2 texcoord;\r\nuniform float cvar_ffov;\r\nvoid main()\r\n{\r\n\tvec3 tc;\t\r\n\tvec2 d;\t\r\n\tvec2 ang;\t\r\n\td = texcoord;\t\r\n\tang.x = sqrt(d.x*d.x+d.y*d.y)*radians(cvar_ffov);\t\r\n\tang.y = -atan(d.y, d.x);\t\r\n\ttc.x = sin(ang.x) * cos(ang.y);\t\r\n\ttc.y = sin(ang.x) * sin(ang.y);\t\r\n\ttc.z = cos(ang.x);\t\r\n\tgl_FragColor = textureCube(s_screen, tc);\r\n}\r\n#endif\r\n"
  },
  {
    "path": "engine/shaders/glsl/postproc_laea.glsl",
    "content": "!!cvarf ffov\r\n!!samps screen:samplerCube=0\r\n\r\n//my attempt at lambert azimuthal equal-area view rendering, because you'll remember that name easily.\r\n\r\n#ifdef VERTEX_SHADER\r\nattribute vec2 v_texcoord;\r\nvarying vec2 texcoord;\r\nuniform float cvar_ffov;\r\nvoid main()\r\n{\r\n\ttexcoord = v_texcoord.xy;\r\n\r\n\t//make sure the ffov cvar actually does something meaningful\r\n\ttexcoord *= cvar_ffov / 90.0;\r\n\r\n\tgl_Position = ftetransform();\r\n}\r\n#endif\r\n#ifdef FRAGMENT_SHADER\r\nvarying vec2 texcoord;\r\nvoid main()\r\n{\r\n\tvec3 tc;\t\r\n\tvec2 d;\t\r\n\tvec2 ang;\t\r\n\td = texcoord;\t\r\n\r\n\t//compute the 2d->3d projection\r\n\tfloat sq = d.x*d.x+d.y*d.y;\r\n\tif (sq > 4.0)\r\n\t\tgl_FragColor = vec4(0,0,0,1);\r\n\telse\r\n\t{\r\n\t\ttc.x = sqrt(1.0-(sq/4.0))*d.x;\r\n\t\ttc.y = sqrt(1.0-(sq/4.0))*d.y;\r\n\t\ttc.z = -1.0 + (sq/2.0);\r\n\r\n\t\ttc.y *= -1.0;\r\n\t\ttc.z *= -1.0;\r\n\r\n\t\tgl_FragColor = textureCube(s_screen, tc);\r\n\t}\r\n}\r\n#endif\r\n"
  },
  {
    "path": "engine/shaders/glsl/postproc_panini.glsl",
    "content": "!!cvarf ffov\r\n!!samps screen:samplerCube=0\r\n\r\n//panini view rendering, for high fovs that are still playable.\r\n\r\n#ifdef VERTEX_SHADER\r\nattribute vec2 v_texcoord;\r\nvarying vec2 texcoord;\r\nuniform float cvar_ffov;\r\nvoid main()\r\n{\r\n\ttexcoord = v_texcoord.xy;\r\n\r\n\t//make sure the ffov cvar actually does something meaningful\r\n\ttexcoord *= cvar_ffov / 90.0;\r\n\r\n\tgl_Position = ftetransform();\r\n}\r\n#endif\r\n#ifdef FRAGMENT_SHADER\r\nvarying vec2 texcoord;\r\nvoid main()\r\n{\r\n\tvec3 tc;\r\n\tvec2 d;\r\n\tvec2 ang;\r\n\td = texcoord;\t\r\n\r\n\t//compute the 2d->3d projection\r\n\tfloat div = 1.0 + d.x*d.x + d.y*d.y;\r\n\ttc.x = 2.0*d.x/div;\r\n\ttc.y = -d.y;\r\n\ttc.z = -(-1.0 + d.x*d.x + d.y*d.y)/div;\r\n\r\n\tgl_FragColor = textureCube(s_screen, tc);\r\n}\r\n#endif\r\n"
  },
  {
    "path": "engine/shaders/glsl/postproc_panorama.glsl",
    "content": "!!cvarf ffov\r\n!!samps screen:samplerCube=0\r\n\r\n//panoramic view rendering, for promo map shots or whatever.\r\n\r\n#ifdef VERTEX_SHADER\r\nattribute vec2 v_texcoord;\r\nvarying vec2 texcoord;\r\nvoid main()\r\n{\r\n\ttexcoord = v_texcoord.xy;\r\n\tgl_Position = ftetransform();\r\n}\r\n#endif\r\n#ifdef FRAGMENT_SHADER\r\nvarying vec2 texcoord;\r\nuniform float cvar_ffov;\r\nvoid main()\r\n{\r\n\tvec3 tc;\t\r\n\tfloat ang;\t\r\n\tang = texcoord.x*radians(cvar_ffov);\t\r\n\ttc.x = sin(ang);\t\r\n\ttc.y = -texcoord.y;\t\r\n\ttc.z = cos(ang);\t\r\n\tgl_FragColor = textureCube(s_screen, tc);\r\n}\r\n#endif\r\n"
  },
  {
    "path": "engine/shaders/glsl/postproc_stereographic.glsl",
    "content": "!!cvarf ffov\r\n!!samps screen:samplerCube=0\r\n\r\n//stereographic view rendering, for high fovs that are still playable.\r\n\r\n#ifdef VERTEX_SHADER\r\nattribute vec2 v_texcoord;\r\nvarying vec2 texcoord;\r\nuniform float cvar_ffov;\r\nvoid main()\r\n{\r\n\ttexcoord = v_texcoord.xy;\r\n\r\n\t//make sure the ffov cvar actually does something meaningful\r\n\ttexcoord *= cvar_ffov / 90.0;\r\n\r\n\tgl_Position = ftetransform();\r\n}\r\n#endif\r\n#ifdef FRAGMENT_SHADER\r\nvarying vec2 texcoord;\r\nvoid main()\r\n{\r\n\tvec3 tc;\r\n\tvec2 d;\r\n\tvec2 ang;\r\n\td = texcoord;\t\r\n\r\n\t//compute the 2d->3d projection\r\n\tfloat div = 1.0 + d.x*d.x + d.y*d.y;\r\n\ttc.x = 2.0*d.x/div;\r\n\ttc.y = -2.0*d.y/div;\r\n\ttc.z = -(-1.0 + d.x*d.x + d.y*d.y)/div;\r\n\r\n\tgl_FragColor = textureCube(s_screen, tc);\r\n}\r\n#endif\r\n"
  },
  {
    "path": "engine/shaders/glsl/rtlight.glsl",
    "content": "!!ver 100 300\r\n!!permu TESS\r\n!!permu BUMP\r\n!!permu FRAMEBLEND\r\n!!permu SKELETAL\r\n!!permu UPPERLOWER\r\n!!permu FOG\r\n!!permu REFLECTCUBEMASK\r\n!!cvarf r_glsl_offsetmapping_scale\r\n!!cvardf r_glsl_pcf\r\n!!cvardf r_tessellation_level=5\r\n!!samps diffuse normalmap specular upper lower reflectcube reflectmask\r\n!!samps =PCF shadowmap\r\n!!samps =CUBE projectionmap\r\n\r\n#if defined(ORM) || defined(SG)\r\n\t#define PBR\r\n#endif\r\n\r\n#include \"sys/defs.h\"\r\n\r\n//this is the main shader responsible for realtime dlights.\r\n\r\n//texture units:\r\n//s0=diffuse, s1=normal, s2=specular, s3=shadowmap\r\n//custom modifiers:\r\n//PCF(shadowmap)\r\n//CUBEPROJ(projected cubemap)\r\n//SPOT(projected circle\r\n//CUBESHADOW\r\n\r\n#if 0 && defined(GL_ARB_texture_gather) && defined(PCF) \r\n#extension GL_ARB_texture_gather : enable\r\n#endif\r\n\r\n#ifdef UPPERLOWER\r\n#define UPPER\r\n#define LOWER\r\n#endif\r\n\r\n//if there's no vertex normals known, disable some stuff.\r\n//FIXME: this results in dupe permutations.\r\n#ifdef NOBUMP\r\n#undef SPECULAR\r\n#undef BUMP\r\n#undef OFFSETMAPPING\r\n#endif\r\n\r\n#if !defined(TESS_CONTROL_SHADER)\r\n\tvarying vec2 tcbase;\r\n\tvarying vec3 lightvector;\r\n\t#if defined(VERTEXCOLOURS)\r\n\t\tvarying vec4 vc;\r\n\t#endif\r\n\t#if defined(SPECULAR) || defined(OFFSETMAPPING) || defined(REFLECTCUBEMASK) || defined(PBR)\r\n\t\tvarying vec3 eyevector;\r\n\t#endif\r\n\t#ifdef REFLECTCUBEMASK\r\n\t\tvarying mat3 invsurface;\r\n\t#endif\r\n\t#if defined(PCF) || defined(CUBE) || defined(SPOT) || defined(ORTHO)\r\n\t\tvarying vec4 vtexprojcoord;\r\n\t#endif\r\n#endif\r\n\r\n\r\n#ifdef VERTEX_SHADER\r\n#ifdef TESS\r\nvarying vec3 vertex, normal;\r\n#endif\r\n#include \"sys/skeletal.h\"\r\nvoid main ()\r\n{\r\n\tvec3 n, s, t, w;\r\n\tgl_Position = skeletaltransform_wnst(w,n,s,t);\r\nn = normalize(n);\r\ns = normalize(s);\r\nt = normalize(t);\r\n\ttcbase = v_texcoord;\t//pass the texture coords straight through\r\n#ifdef ORTHO\r\n\tvec3 lightminusvertex = -l_lightdirection;\r\n\tlightvector.x = dot(lightminusvertex, s.xyz);\r\n\tlightvector.y = dot(lightminusvertex, t.xyz);\r\n\tlightvector.z = dot(lightminusvertex, n.xyz);\r\n#else\r\n\tvec3 lightminusvertex = l_lightposition - w.xyz;\r\n\t#ifdef NOBUMP\r\n\t\t//the only important thing is distance\r\n\t\tlightvector = lightminusvertex;\r\n\t#else\r\n\t\t//the light direction relative to the surface normal, for bumpmapping.\r\n\t\tlightvector.x = dot(lightminusvertex, s.xyz);\r\n\t\tlightvector.y = dot(lightminusvertex, t.xyz);\r\n\t\tlightvector.z = dot(lightminusvertex, n.xyz);\r\n\t#endif\r\n#endif\r\n#if defined(VERTEXCOLOURS)\r\n\tvc = v_colour;\r\n#endif\r\n#if defined(SPECULAR)||defined(OFFSETMAPPING) || defined(REFLECTCUBEMASK) || defined(PBR)\r\n\tvec3 eyeminusvertex = e_eyepos - w.xyz;\r\n\teyevector.x = dot(eyeminusvertex, s.xyz);\r\n\teyevector.y = dot(eyeminusvertex, t.xyz);\r\n\teyevector.z = dot(eyeminusvertex, n.xyz);\r\n#endif\r\n#ifdef REFLECTCUBEMASK\r\n\tinvsurface = mat3(v_svector, v_tvector, v_normal);\r\n#endif\r\n#if defined(PCF) || defined(SPOT) || defined(CUBE) || defined(ORTHO)\r\n\t//for texture projections/shadowmapping on dlights\r\n\tvtexprojcoord = (l_cubematrix*vec4(w.xyz, 1.0));\r\n#endif\r\n\r\n#ifdef TESS\r\n\tvertex = w;\r\n\tnormal = n;\r\n#endif\r\n}\r\n#endif\r\n\r\n\r\n\r\n\r\n\r\n\r\n#if defined(TESS_CONTROL_SHADER)\r\nlayout(vertices = 3) out;\r\n\r\nin vec3 vertex[];\r\nout vec3 t_vertex[];\r\nin vec3 normal[];\r\nout vec3 t_normal[];\r\nin vec2 tcbase[];\r\nout vec2 t_tcbase[];\r\nin vec3 lightvector[];\r\nout vec3 t_lightvector[];\r\n#if defined(VERTEXCOLOURS)\r\nin vec4 vc[];\r\nout vec4 t_vc[];\r\n#endif\r\n#if defined(SPECULAR) || defined(OFFSETMAPPING) || defined(REFLECTCUBEMASK) || defined(PBR)\r\nin vec3 eyevector[];\r\nout vec3 t_eyevector[];\r\n#endif\r\nvoid main()\r\n{\r\n\t//the control shader needs to pass stuff through\r\n#define id gl_InvocationID\r\n\tt_vertex[id] = vertex[id];\r\n\tt_normal[id] = normal[id];\r\n\tt_tcbase[id] = tcbase[id];\r\n\tt_lightvector[id] = lightvector[id];\r\n#if defined(VERTEXCOLOURS)\r\n\tt_vc[id] = vc[id];\r\n#endif\r\n#if defined(SPECULAR) || defined(OFFSETMAPPING) || defined(REFLECTCUBEMASK) || defined(PBR)\r\n\tt_eyevector[id] = eyevector[id];\r\n#endif\r\n\r\n\tgl_TessLevelOuter[0] = float(r_tessellation_level);\r\n\tgl_TessLevelOuter[1] = float(r_tessellation_level);\r\n\tgl_TessLevelOuter[2] = float(r_tessellation_level);\r\n\tgl_TessLevelInner[0] = float(r_tessellation_level);\r\n}\r\n#endif\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n#if defined(TESS_EVALUATION_SHADER)\r\nlayout(triangles) in;\r\n\r\nin vec3 t_vertex[];\r\nin vec3 t_normal[];\r\nin vec2 t_tcbase[];\r\nin vec3 t_lightvector[];\r\n#if defined(VERTEXCOLOURS)\r\nin vec4 t_vc[];\r\n#endif\r\n#if defined(SPECULAR) || defined(OFFSETMAPPING) || defined(REFLECTCUBEMASK) || defined(PBR)\r\nin vec3 t_eyevector[];\r\n#endif\r\n\r\n#define LERP(a) (gl_TessCoord.x*a[0] + gl_TessCoord.y*a[1] + gl_TessCoord.z*a[2])\r\nvoid main()\r\n{\r\n#define factor 1.0\r\n\ttcbase = LERP(t_tcbase);\r\n\tvec3 w = LERP(t_vertex);\r\n\r\n\tvec3 t0 = w - dot(w-t_vertex[0],t_normal[0])*t_normal[0];\r\n\tvec3 t1 = w - dot(w-t_vertex[1],t_normal[1])*t_normal[1];\r\n\tvec3 t2 = w - dot(w-t_vertex[2],t_normal[2])*t_normal[2];\r\n\tw = w*(1.0-factor) + factor*(gl_TessCoord.x*t0+gl_TessCoord.y*t1+gl_TessCoord.z*t2);\r\n\r\n#if defined(PCF) || defined(SPOT) || defined(CUBE) || defined(ORTHO)\r\n\t//for texture projections/shadowmapping on dlights\r\n\tvtexprojcoord = (l_cubematrix*vec4(w.xyz, 1.0));\r\n#endif\r\n\r\n\t//FIXME: we should be recalcing these here, instead of just lerping them\r\n\tlightvector = LERP(t_lightvector);\r\n#if defined(VERTEXCOLOURS)\r\n\tvc = LERP(t_vc);\r\n#endif\r\n#if defined(SPECULAR) || defined(OFFSETMAPPING) || defined(REFLECTCUBEMASK) || defined(PBR)\r\n\teyevector = LERP(t_eyevector);\r\n#endif\r\n\r\n\tgl_Position = m_modelviewprojection * vec4(w,1.0);\r\n}\r\n#endif\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n#ifdef FRAGMENT_SHADER\r\n\r\n#include \"sys/fog.h\"\r\n#include \"sys/pcf.h\"\r\n#ifdef OFFSETMAPPING\r\n#include \"sys/offsetmapping.h\"\r\n#endif\r\n\r\n#include \"sys/pbr.h\"\r\n\r\nvoid main ()\r\n{\r\n#ifdef ORTHO\r\n\tfloat colorscale = 1.0;\r\n#else\r\n\tfloat colorscale = max(1.0 - (dot(lightvector, lightvector)/(l_lightradius*l_lightradius)), 0.0);\r\n#endif\r\n#ifdef PCF\r\n\t/*filter the light by the shadowmap. logically a boolean, but we allow fractions for softer shadows*/\r\n\tcolorscale *= ShadowmapFilter(s_shadowmap, vtexprojcoord);\r\n#endif\r\n#if defined(SPOT)\r\n\t/*filter the colour by the spotlight. discard anything behind the light so we don't get a mirror image*/\r\n\tif (vtexprojcoord.w < 0.0) discard;\r\n\tvec2 spot = ((vtexprojcoord.st)/vtexprojcoord.w);\r\n\tcolorscale*=1.0-(dot(spot,spot));\r\n#endif\r\n\r\n//read raw texture samples (offsetmapping munges the tex coords first)\r\n#ifdef OFFSETMAPPING\r\n\tvec2 tcoffsetmap = offsetmap(s_normalmap, tcbase, eyevector);\r\n#define tcbase tcoffsetmap\r\n#endif\r\n#if defined(FLAT)\r\n\tvec4 bases = vec4(FLAT, FLAT, FLAT, 1.0);\r\n#else\r\n\tvec4 bases = texture2D(s_diffuse, tcbase);\r\n\t#ifdef VERTEXCOLOURS\r\n\t\tbases.rgb *= bases.a;\r\n\t#endif\r\n#endif\r\n#ifdef UPPER\r\n\tvec4 uc = texture2D(s_upper, tcbase);\r\n\tbases.rgb += uc.rgb*e_uppercolour*uc.a;\r\n#endif\r\n#ifdef LOWER\r\n\tvec4 lc = texture2D(s_lower, tcbase);\r\n\tbases.rgb += lc.rgb*e_lowercolour*lc.a;\r\n#endif\r\n#if defined(BUMP) || defined(SPECULAR) || defined(REFLECTCUBEMASK) || defined(PBR)\r\n\tvec3 bumps = normalize(vec3(texture2D(s_normalmap, tcbase)) - 0.5);\r\n#elif defined(REFLECTCUBEMASK)\r\n\tvec3 bumps = vec3(0.0,0.0,1.0);\r\n#endif\r\n#ifdef SPECULAR\r\n\tvec4 specs = texture2D(s_specular, tcbase);\r\n#endif\r\n\r\n\t#define dielectricSpecular 0.04\r\n\t#ifdef SPECULAR\r\n\t\t#ifdef ORM\t//pbr-style occlusion+roughness+metalness\r\n\t\t\t#define occlusion specs.r\r\n\t\t\t#define roughness clamp(specs.g, 0.04, 1.0)\r\n\t\t\t#define metalness specs.b\r\n\t\t\t#define gloss 1.0 //sqrt(1.0-roughness)\r\n\t\t\t#define ambientrgb (specrgb+col.rgb)\r\n\t\t\tvec3 specrgb = mix(vec3(dielectricSpecular), bases.rgb, metalness);\r\n\t\t\tbases.rgb = bases.rgb * (1.0 - dielectricSpecular) * (1.0-metalness);\r\n\t\t#elif defined(SG) //pbr-style specular+glossiness\r\n\t\t\t//occlusion needs to be baked in. :(\r\n\t\t\t#define roughness (1.0-specs.a)\r\n\t\t\t#define gloss specs.a\r\n\t\t\t#define specrgb specs.rgb\r\n\t\t\t#define ambientrgb (specs.rgb+col.rgb)\r\n\t\t#else   //blinn-phong\r\n\t\t\t#define roughness (1.0-specs.a)\r\n\t\t\t#define gloss specs.a\r\n\t\t\t#define specrgb specs.rgb\r\n\t\t\t#define ambientrgb col.rgb\r\n\t\t#endif\r\n\t#else\r\n\t\t#define roughness 0.3\r\n\t\t#define specrgb bases.rgb //vec3(dielectricSpecular)\r\n\t#endif\r\n\r\n#ifdef PBR\r\n\tvec3 diff = DoPBR(bumps, normalize(eyevector), normalize(lightvector), roughness, bases.rgb, specrgb, l_lightcolourscale);\r\n#else\r\n\tvec3 diff;\r\n\t#ifdef NOBUMP\r\n\t\t//surface can only support ambient lighting, even for lights that try to avoid it.\r\n\t\tdiff = bases.rgb * (l_lightcolourscale.x+l_lightcolourscale.y);\r\n\t#else\r\n\t\tvec3 nl = normalize(lightvector);\r\n\t\t#ifdef BUMP\r\n\t\t\tdiff = bases.rgb * (l_lightcolourscale.x + l_lightcolourscale.y * max(dot(bumps, nl), 0.0));\r\n\t\t#else\r\n\t\t\t//we still do bumpmapping even without bumps to ensure colours are always sane. light.exe does it too.\r\n\t\t\tdiff = bases.rgb * (l_lightcolourscale.x + l_lightcolourscale.y * max(dot(vec3(0.0, 0.0, 1.0), nl), 0.0));\r\n\t\t#endif\r\n\t#endif\r\n\t#ifdef SPECULAR\r\n\t\tvec3 halfdir = normalize(normalize(eyevector) + nl);\r\n\t\tfloat spec = pow(max(dot(halfdir, bumps), 0.0), FTE_SPECULAR_EXPONENT * gloss)*float(SPECMUL);\r\n\t\tdiff += l_lightcolourscale.z * spec * specrgb;\r\n\t#endif\r\n#endif\r\n\r\n#ifdef REFLECTCUBEMASK\r\n\tvec3 rtc = reflect(-eyevector, bumps);\r\n\trtc = rtc.x*invsurface[0] + rtc.y*invsurface[1] + rtc.z*invsurface[2];\r\n\trtc = (m_model * vec4(rtc.xyz,0.0)).xyz;\r\n\tdiff += texture2D(s_reflectmask, tcbase).rgb * textureCube(s_reflectcube, rtc).rgb;\r\n#endif\r\n\r\n#ifdef CUBE\r\n\t/*filter the colour by the cubemap projection*/\r\n\tdiff *= textureCube(s_projectionmap, vtexprojcoord.xyz).rgb;\r\n#endif\r\n\r\n#if defined(PROJECTION)\r\n\t/*2d projection, not used*/\r\n//\tdiff *= texture2d(s_projectionmap, shadowcoord);\r\n#endif\r\n#if defined(occlusion) && !defined(NOOCCLUDE)\r\n\tdiff *= occlusion;\r\n#endif\r\n#if defined(VERTEXCOLOURS)\r\n\tdiff *= vc.rgb * vc.a;\r\n#endif\r\n\r\n\tdiff *= colorscale*l_lightcolour;\r\n\tgl_FragColor = vec4(fog3additive(diff), 1.0);\r\n}\r\n#endif\r\n\r\n"
  },
  {
    "path": "engine/shaders/glsl/terrain.glsl",
    "content": "!!ver 100 300\r\n!!permu FOG\r\n//RTLIGHT (+PCF,CUBE,SPOT,etc)\r\n!!samps tr=0 tg=1 tb=2 tx=3 //the four texturemaps\r\n!!samps mix=4\t//how the ground is blended\r\n!!samps =PCF shadowmap\r\n!!samps =CUBE projectionmap\r\n\r\n//light levels\r\n\r\n#include \"sys/fog.h\"\r\nvarying vec2 tc;\r\nvarying vec2 lm;\r\nvarying vec4 vc;\r\n\r\n#ifdef RTLIGHT\r\n\tvarying vec3 lightvector;\r\n//\t#if defined(SPECULAR) || defined(OFFSETMAPPING)\r\n//\t\tvarying vec3 eyevector;\r\n//\t#endif\r\n\t#if defined(PCF) || defined(CUBE) || defined(SPOT)\r\n\t\tvarying vec4 vtexprojcoord;\r\n\t#endif\r\n#endif\r\n\r\n\r\n\r\n\r\n\r\n#ifdef VERTEX_SHADER\r\n\r\n#ifdef RTLIGHT\r\n\tuniform vec3 l_lightposition;\r\n//\t#if defined(SPECULAR) || defined(OFFSETMAPPING)\r\n//\t\tuniform vec3 e_eyepos;\r\n//\t#endif\r\n\t#if defined(PCF) || defined(CUBE) || defined(SPOT)\r\n\t\tuniform mat4 l_cubematrix;\r\n\t#endif\r\n\tattribute vec3 v_normal;\r\n\tattribute vec3 v_svector;\r\n\tattribute vec3 v_tvector;\r\n#endif\r\n\r\nattribute vec2 v_texcoord;\r\nattribute vec2 v_lmcoord;\r\nattribute vec4 v_colour;\r\n\r\nvoid main (void)\r\n{\r\n\ttc = v_texcoord.st;\r\n\tlm = v_lmcoord.st;\r\n\tvc = v_colour;\r\n\tgl_Position = ftetransform();\r\n\r\n\t#ifdef RTLIGHT\r\n\t\t//light position is in model space, which is handy.\r\n\t\tvec3 lightminusvertex = l_lightposition - v_position.xyz;\r\n\r\n\t\t//no bumpmapping, so we can just use distance without regard for actual surface direction. we still do scalecos stuff. you might notice it on steep slopes.\r\n\t\tlightvector = lightminusvertex;\r\n//\t\tlightvector.x = dot(lightminusvertex, v_svector.xyz);\r\n//\t\tlightvector.y = dot(lightminusvertex, v_tvector.xyz);\r\n//\t\tlightvector.z = dot(lightminusvertex, v_normal.xyz);\r\n\r\n//\t\t#if defined(SPECULAR)||defined(OFFSETMAPPING)\r\n//\t\t\tvec3 eyeminusvertex = e_eyepos - v_position.xyz;\r\n//\t\t\teyevector.x = dot(eyeminusvertex, v_svector.xyz);\r\n//\t\t\teyevector.y = dot(eyeminusvertex, v_tvector.xyz);\r\n//\t\t\teyevector.z = dot(eyeminusvertex, v_normal.xyz);\r\n//\t\t#endif\r\n\t\t#if defined(PCF) || defined(SPOT) || defined(CUBE)\r\n\t\t\t//for texture projections/shadowmapping on dlights\r\n\t\t\tvtexprojcoord = (l_cubematrix*vec4(v_position.xyz, 1.0));\r\n\t\t#endif\r\n\t#endif\r\n}\r\n#endif\r\n\r\n\r\n\r\n\r\n#ifdef FRAGMENT_SHADER\r\n#ifdef PCF\r\n\t#include \"sys/pcf.h\"\r\n#endif\r\n\r\n//light levels\r\nuniform vec4 e_lmscale;\r\n\r\n#ifdef RTLIGHT\r\n\tuniform float l_lightradius;\r\n\tuniform vec3 l_lightcolour;\r\n\tuniform vec3 l_lightcolourscale;\r\n#endif\r\n\r\nvoid main (void)\r\n{\r\n\tvec4 r;\r\n\tvec4 m = texture2D(s_mix, lm);\r\n\r\n\tr  = texture2D(s_tr, tc)*m.r;\r\n\tr += texture2D(s_tg, tc)*m.g;\r\n\tr += texture2D(s_tb, tc)*m.b;\r\n\tr += texture2D(s_tx, tc)*(1.0 - (m.r + m.g + m.b));\r\n\r\n\tr.rgb *= 1.0/r.a;\t//fancy maths, so low alpha values give other textures a greater focus\r\n\r\n\t//vertex colours provide a scaler that applies even through rtlights.\r\n\tr *= vc;\r\n\r\n#ifdef RTLIGHT\r\n\tvec3 nl = normalize(lightvector);\r\n\tfloat colorscale = max(1.0 - (dot(lightvector, lightvector)/(l_lightradius*l_lightradius)), 0.0);\r\n\tvec3 diff;\r\n//\t#ifdef BUMP\r\n//\t\tcolorscale *= (l_lightcolourscale.x + l_lightcolourscale.y * max(dot(bumps, nl), 0.0));\r\n//\t#else\r\n\t\tcolorscale *= (l_lightcolourscale.x + l_lightcolourscale.y * max(dot(vec3(0.0, 0.0, 1.0), nl), 0.0));\r\n//\t#endif\r\n\r\n//\t#ifdef SPECULAR\r\n//\t\tvec3 halfdir = normalize(normalize(eyevector) + nl);\r\n//\t\tfloat spec = pow(max(dot(halfdir, bumps), 0.0), 32.0 * specs.a);\r\n//\t\tdiff += l_lightcolourscale.z * spec * specs.rgb;\r\n//\t#endif\r\n\r\n\r\n\r\n\t#if defined(SPOT)\r\n\t\tif (vtexprojcoord.w < 0.0) discard;\r\n\t\tvec2 spot = ((vtexprojcoord.st)/vtexprojcoord.w);\r\n\t\tcolorscale *= 1.0-(dot(spot,spot));\r\n\t#endif\r\n\t#ifdef PCF\r\n\t\tcolorscale *= ShadowmapFilter(s_shadowmap, vtexprojcoord);\r\n\t#endif\r\n\r\n\tr.rgb *= colorscale * l_lightcolour;\r\n\r\n\t#ifdef CUBE\r\n\t\tr.rgb *= textureCube(s_projectionmap, vtexprojcoord.xyz).rgb;\r\n\t#endif\r\n\r\n\tgl_FragColor = fog4additive(r);\r\n#else\r\n\t//lightmap is greyscale in m.a. probably we should just scale the texture mix, but precision errors when editing make me paranoid.\r\n\tr *= e_lmscale*vec4(m.aaa,1.0);\r\n\tgl_FragColor = fog4(r);\r\n#endif\r\n}\r\n#endif\r\n"
  },
  {
    "path": "engine/shaders/glsl/underwaterwarp.glsl",
    "content": "!!cvarf r_waterwarp\r\n!!samps screen=0 warp=1 edge=2\r\n\r\n//this is a post processing shader that is drawn fullscreen whenever the view is underwater.\r\n//its generally expected to warp the view a little.\r\n\r\nvarying vec2 v_stc;\r\nvarying vec2 v_warp;\r\nvarying vec2 v_edge;\r\n#ifdef VERTEX_SHADER\r\nattribute vec2 v_texcoord;\r\nuniform float e_time;\r\nvoid main ()\r\n{\r\n\tgl_Position = ftetransform();\r\n\tv_stc = vec2(v_texcoord.x, 1.0-v_texcoord.y);\r\n\tv_warp.s = e_time * 0.25 + v_texcoord.s;\r\n\tv_warp.t = e_time * 0.25 + v_texcoord.t;\r\n\tv_edge = v_texcoord.xy;\r\n}\r\n#endif\r\n#ifdef FRAGMENT_SHADER\r\nuniform vec4 e_rendertexturescale;\r\nuniform float cvar_r_waterwarp;\r\nvoid main ()\r\n{\r\n\tvec2 amp\t\t= (0.010 / 0.625) * cvar_r_waterwarp * texture2D(s_edge, v_edge).rg;\r\n\tvec3 offset\t= (texture2D(s_warp, v_warp).rgb - 0.5) * 2.0;\r\n\tvec2 temp\t\t= v_stc + offset.xy * amp;\r\n\tgl_FragColor\t= texture2D(s_screen, temp*e_rendertexturescale.st);\r\n}\r\n#endif\r\n"
  },
  {
    "path": "engine/shaders/glsl/wireframe.glsl",
    "content": "!!ver 100 150\r\n!!permu TESS\r\n!!permu FRAMEBLEND\r\n!!permu SKELETAL\r\n!!permu FOG\r\n!!cvardf r_tessellation_level=5\r\n\r\n#include \"sys/defs.h\"\r\n\r\n//standard shader used for wireframe stuff.\r\n//must support skeletal and 2-way vertex blending or Bad Things Will Happen.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n#ifdef VERTEX_SHADER\r\n#include \"sys/skeletal.h\"\r\n\r\n#ifdef TESS\r\nvarying vec3 vertex;\r\nvarying vec3 normal;\r\n#endif\r\n\r\nvoid main ()\r\n{\r\n\tvec3 n, s, t, w;\r\n\tgl_Position = skeletaltransform_wnst(w,n,s,t);\r\n\r\n#ifdef TESS\r\n\tnormal = n;\r\n\tvertex = w;\r\n#endif\r\n}\r\n#endif\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n#if defined(TESS_CONTROL_SHADER)\r\nlayout(vertices = 3) out;\r\n\r\nin vec3 vertex[];\r\nout vec3 t_vertex[];\r\nin vec3 normal[];\r\nout vec3 t_normal[];\r\nvoid main()\r\n{\r\n\t//the control shader needs to pass stuff through\r\n#define id gl_InvocationID\r\n\tt_vertex[id] = vertex[id];\r\n\tt_normal[id] = normal[id];\r\n\r\n\tgl_TessLevelOuter[0] = float(r_tessellation_level);\r\n\tgl_TessLevelOuter[1] = float(r_tessellation_level);\r\n\tgl_TessLevelOuter[2] = float(r_tessellation_level);\r\n\tgl_TessLevelInner[0] = float(r_tessellation_level);\r\n}\r\n#endif\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n#if defined(TESS_EVALUATION_SHADER)\r\nlayout(triangles) in;\r\n\r\nin vec3 t_vertex[];\r\nin vec3 t_normal[];\r\n\r\n#define LERP(a) (gl_TessCoord.x*a[0] + gl_TessCoord.y*a[1] + gl_TessCoord.z*a[2])\r\nvoid main()\r\n{\r\n#define factor 1.0\r\n\tvec3 w = LERP(t_vertex);\r\n\r\n\tvec3 t0 = w - dot(w-t_vertex[0],t_normal[0])*t_normal[0];\r\n\tvec3 t1 = w - dot(w-t_vertex[1],t_normal[1])*t_normal[1];\r\n\tvec3 t2 = w - dot(w-t_vertex[2],t_normal[2])*t_normal[2];\r\n\tw = w*(1.0-factor) + factor*(gl_TessCoord.x*t0+gl_TessCoord.y*t1+gl_TessCoord.z*t2);\r\n\r\n\r\n\tgl_Position = m_modelviewprojection * vec4(w,1.0);\r\n}\r\n#endif\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n#ifdef FRAGMENT_SHADER\r\n\r\n#include \"sys/fog.h\"\r\n\r\nvoid main ()\r\n{\r\n\tgl_FragColor = fog4(vec4(1.0) * e_colourident);\r\n}\r\n#endif\r\n\r\n"
  },
  {
    "path": "engine/shaders/hlsl11/default2d.hlsl",
    "content": "!!samps 1\r\n\r\nstruct a2v\r\n{\r\n\tfloat4 pos: POSITION;\r\n\tfloat2 tc: TEXCOORD0;\r\n\tfloat4 vcol: COLOR0;\r\n};\r\nstruct v2f\r\n{\r\n\tfloat4 pos: SV_POSITION;\r\n\tfloat2 tc: TEXCOORD0;\r\n\tfloat4 vcol: COLOR0;\r\n};\r\n\r\n#include <ftedefs.h>\r\n\r\n#ifdef VERTEX_SHADER\r\n\tv2f main (a2v inp)\r\n\t{\r\n\t\tv2f outp;\r\n\t\toutp.pos = mul(m_projection, inp.pos);\r\n\t\toutp.tc = inp.tc;\r\n\t\toutp.vcol = inp.vcol;\r\n\t\treturn outp;\r\n\t}\r\n#endif\r\n\r\n#ifdef FRAGMENT_SHADER\r\n\tTexture2D t_t0;\r\n\tSamplerState s_t0;\r\n\tfloat4 main (v2f inp) : SV_TARGET\r\n\t{\r\n#ifdef PREMUL\r\n\t\tinp.vcol.rgb *= inp.vcol.a;\r\n#endif\r\n\t\treturn t_t0.Sample(s_t0, inp.tc) * inp.vcol;\r\n\t}\r\n#endif\r\n\r\n"
  },
  {
    "path": "engine/shaders/hlsl11/defaultfill.hlsl",
    "content": "struct a2v\r\n{\r\n\tfloat4 pos: POSITION;\r\n\tfloat4 vcol: COLOR0;\r\n};\r\nstruct v2f\r\n{\r\n\tfloat4 pos: SV_POSITION;\r\n\tfloat4 vcol: COLOR0;\r\n};\r\n\r\n#include <ftedefs.h>\r\n\r\n#ifdef VERTEX_SHADER\r\n\tv2f main (a2v inp)\r\n\t{\r\n\t\tv2f outp;\r\n\t\toutp.pos = mul(m_projection, inp.pos);\r\n\t\toutp.vcol = inp.vcol;\r\n\t\treturn outp;\r\n\t}\r\n#endif\r\n\r\n#ifdef FRAGMENT_SHADER\r\n\tfloat4 main (v2f inp) : SV_TARGET\r\n\t{\r\n\t\treturn inp.vcol;\r\n\t}\r\n#endif\r\n\r\n"
  },
  {
    "path": "engine/shaders/hlsl11/defaultskin.hlsl",
    "content": "!!permu UPPERLOWER\r\n!!samps diffuse upper lower fullbright\r\n\r\nstruct a2v\r\n{\r\n\tfloat4 pos: POSITION;\r\n\tfloat2 tc: TEXCOORD0;\r\n\tfloat3 normal: NORMAL;\r\n};\r\nstruct v2f\r\n{\r\n\tfloat4 pos: SV_POSITION;\r\n\tfloat2 tc: TEXCOORD0;\r\n\tfloat3 light: TEXCOORD1;\r\n};\r\n\r\n#include <ftedefs.h>\r\n\r\n#ifdef VERTEX_SHADER\r\n//attribute vec2 v_texcoord;\r\n//uniform vec3 e_light_dir;\r\n//uniform vec3 e_light_mul;\r\n//uniform vec3 e_light_ambient;\r\n\tv2f main (a2v inp)\r\n\t{\r\n\t\tv2f outp;\r\n\t\toutp.pos = mul(m_model, inp.pos);\r\n\t\toutp.pos = mul(m_view, outp.pos);\r\n\t\toutp.pos = mul(m_projection, outp.pos);\r\n\t\toutp.light = e_light_ambient + (dot(inp.normal,e_light_dir)*e_light_mul);\r\n\t\toutp.tc = inp.tc.xy;\r\n\t\treturn outp;\r\n\t}\r\n#endif\r\n#ifdef FRAGMENT_SHADER\r\n\tTexture2D t_diffuse\t\t: register(t0);\r\n#ifdef UPPER\r\n\tTexture2D t_upper\t\t: register(t1);\r\n\tTexture2D t_lower\t\t: register(t2);\r\n\tTexture2D t_fullbright\t: register(t3);\r\n#else\r\n\tTexture2D t_fullbright\t: register(t1);\r\n#endif\r\n\r\n\tSamplerState SampleType;\r\n\r\n\tfloat4 main (v2f inp) : SV_TARGET\r\n\t{\r\n\t\tfloat4 col;\r\n\t\tcol = t_diffuse.Sample(SampleType, inp.tc);\r\n\r\n\t\t#ifdef MASK\r\n\t\t\t#ifndef MASKOP\r\n\t\t\t\t#define MASKOP >=\t//drawn if (alpha OP ref) is true.\r\n\t\t\t#endif\r\n\t\t\t//support for alpha masking\r\n\t\t\tif (!(col.a MASKOP MASK))\r\n\t\t\t\tdiscard;\r\n\t\t#endif\r\n\r\n#ifdef UPPER\r\n\t\tfloat4 uc = t_upper.Sample(SampleType, inp.tc);\r\n\t\tcol.rgb += uc.rgb*e_uppercolour.rgb*uc.a;\r\n#endif\r\n#ifdef LOWER\r\n\t\tfloat4 lc = t_lower.Sample(SampleType, inp.tc);\r\n\t\tcol.rgb += lc.rgb*e_lowercolour.rgb*lc.a;\r\n#endif\r\n\t\tcol.rgb *= inp.light;\r\n//#ifdef FULLBRIGHT\r\n\t\tfloat4 fb = t_fullbright.Sample(SampleType, inp.tc)*e_glowmod;\r\n//\t\tcol.rgb = lerp(col.rgb, fb.rgb, fb.a);\t//matches vanilla quake...\r\n\t\tcol.rgb += fb.rgb * fb.a;\t\t\t\t//but nothing expects it to.\r\n//#endif\r\n\t\tcol *= e_colourmod;\r\n//\t\tcol = fog4(col);\r\n\t\treturn col;\r\n\t}\r\n#endif\r\n"
  },
  {
    "path": "engine/shaders/hlsl11/defaultsky.hlsl",
    "content": "!!samps diffuse fullbright\r\n\r\n//regular sky shader for scrolling q1 skies\r\n//the sky surfaces are thrown through this as-is.\r\n\r\nstruct a2v\r\n{\r\n\tfloat4 pos: POSITION;\r\n\tfloat2 tc: TEXCOORD0;\r\n};\r\nstruct v2f\r\n{\r\n\tfloat4 pos: SV_POSITION;\r\n\tfloat2 tc: TEXCOORD0;\r\n\tfloat3 mpos: TEXCOORD1;\r\n};\r\n\r\n#include <ftedefs.h>\r\n\r\n#ifdef VERTEX_SHADER\r\n\tv2f main (a2v inp)\r\n\t{\r\n\t\tv2f outp;\r\n\t\toutp.pos = mul(m_model, inp.pos);\r\n\t\toutp.mpos = outp.pos.xyz;\r\n\t\toutp.pos = mul(m_view, outp.pos);\r\n\t\toutp.pos = mul(m_projection, outp.pos);\r\n\t\toutp.tc = inp.tc;\r\n\t\treturn outp;\r\n\t}\r\n#endif\r\n\r\n#ifdef FRAGMENT_SHADER\r\n\tTexture2D t_diffuse \t\t: register(t0);\r\n\tTexture2D t_fullbright\t: register(t1);\r\n\tSamplerState s_diffuse \t: register(s0);\r\n\tSamplerState s_fullbright\t: register(s1);\r\n\r\n\tfloat4 main (v2f inp) : SV_TARGET\r\n\t{\r\n\t\tfloat2 tccoord;\r\n\t\tfloat3 dir = inp.mpos - v_eyepos;\r\n\t\tdir.z *= 3.0;\r\n\t\tdir.xy /= 0.5*length(dir);\r\n\t\ttccoord = (dir.xy + e_time*0.03125);\r\n\t\tfloat4 solid = t_diffuse.Sample(s_diffuse, tccoord);\r\n\t\ttccoord = (dir.xy + e_time*0.0625);\r\n\t\tfloat4 clouds = t_fullbright.Sample(s_fullbright, tccoord);\r\n\t\treturn lerp(solid, clouds, clouds.a);\r\n\t}\r\n#endif"
  },
  {
    "path": "engine/shaders/hlsl11/defaultskybox.hlsl",
    "content": "!!samps reflectcube\r\n\r\n//regular sky shader for scrolling q1 skies\r\n//the sky surfaces are thrown through this as-is.\r\n\r\nstruct a2v\r\n{\r\n\tfloat4 pos: POSITION;\r\n};\r\nstruct v2f\r\n{\r\n\tfloat4 pos: SV_POSITION;\r\n\tfloat3 texc: TEXCOORD0;\r\n};\r\n\r\n#include <ftedefs.h>\r\n\r\n#ifdef VERTEX_SHADER\r\n\tv2f main (a2v inp)\r\n\t{\r\n\t\tv2f outp;\r\n\t\toutp.pos = mul(m_model, inp.pos);\r\n\t\toutp.texc= outp.pos.xyz - v_eyepos;\r\n\t\toutp.pos = mul(m_view, outp.pos);\r\n\t\toutp.pos = mul(m_projection, outp.pos);\r\n\t\treturn outp;\r\n\t}\r\n#endif\r\n\r\n#ifdef FRAGMENT_SHADER\r\n\tTextureCube t_reflectcube\t: register(t0);\r\n\tSamplerState s_reflectcube\t: register(s0);\r\n\r\n\tfloat4 main (v2f inp) : SV_TARGET\r\n\t{\r\n\t\treturn t_reflectcube.Sample(s_reflectcube, inp.texc);\r\n\t}\r\n#endif\r\n"
  },
  {
    "path": "engine/shaders/hlsl11/defaultsprite.hlsl",
    "content": "!!samps 1\r\n\r\nstruct a2v\r\n{\r\n\tfloat4 pos: POSITION;\r\n\tfloat2 tc: TEXCOORD0;\r\n\tfloat4 vcol: COLOR0;\r\n};\r\nstruct v2f\r\n{\r\n\tfloat4 pos: SV_POSITION;\r\n\tfloat2 tc: TEXCOORD0;\r\n\tfloat4 vcol: COLOR0;\r\n};\r\n\r\n#include <ftedefs.h>\r\n\r\n#ifdef VERTEX_SHADER\r\n\tv2f main (a2v inp)\r\n\t{\r\n\t\tv2f outp;\r\n\t\toutp.pos = mul(m_model, inp.pos);\r\n\t\toutp.pos = mul(m_view, outp.pos);\r\n\t\toutp.pos = mul(m_projection, outp.pos);\r\n\t\toutp.tc = inp.tc.xy;\r\n\t\toutp.vcol = inp.vcol;\r\n\t\treturn outp;\r\n\t}\r\n#endif\r\n\r\n#ifdef FRAGMENT_SHADER\r\n\tTexture2D t_diffuse\t\t: register(t0);\r\n\tSamplerState s_diffuse\t: register(s0);\r\n\tfloat4 main (v2f inp) : SV_TARGET\r\n\t{\r\n\t\tfloat4 tex = t_diffuse.Sample(s_diffuse, inp.tc);\r\n#ifdef MASK\r\n\t\tif (tex.a < float(MASK))\r\n\t\t\tdiscard;\r\n#endif\r\n\t\t//FIXME: no fog, no colourmod\r\n\t\treturn tex * inp.vcol;\r\n\t}\r\n#endif\r\n"
  },
  {
    "path": "engine/shaders/hlsl11/defaultwall.hlsl",
    "content": "!!samps diffuse fullbright lightmap\r\n\r\nstruct a2v\r\n{\r\n\tfloat4 pos: POSITION;\r\n\tfloat2 tc: TEXCOORD0;\r\n\tfloat2 lmtc: TEXCOORD1;\r\n};\r\nstruct v2f\r\n{\r\n\tfloat4 pos: SV_POSITION;\r\n\tfloat2 tc: TEXCOORD0;\r\n\tfloat2 lmtc: TEXCOORD1;\r\n};\r\n\r\n#include <ftedefs.h>\r\n\r\n#ifdef VERTEX_SHADER\r\n\tv2f main (a2v inp)\r\n\t{\r\n\t\tv2f outp;\r\n\t\toutp.pos = mul(m_model, inp.pos);\r\n\t\toutp.pos = mul(m_view, outp.pos);\r\n\t\toutp.pos = mul(m_projection, outp.pos);\r\n\t\toutp.tc = inp.tc;\r\n\t\toutp.lmtc = inp.lmtc;\r\n\t\treturn outp;\r\n\t}\r\n#endif\r\n\r\n#ifdef FRAGMENT_SHADER\r\n\tTexture2D t_lightmap : register(t2);\r\n\tSamplerState s_lightmap : register(s2);\r\n\r\n\tTexture2D t_diffuse : register(t0);\r\n\tSamplerState s_diffuse : register(s0);\r\n\r\n\tTexture2D t_fullbright : register(t1);\r\n\tSamplerState s_fullbright : register(s1);\r\n\r\n\tfloat4 main (v2f inp) : SV_TARGET\r\n\t{\r\n\t\tfloat4 result;\r\n\t\tresult = t_diffuse.Sample(s_diffuse, inp.tc);\r\n\t\tresult.rgb *= t_lightmap.Sample(s_lightmap, inp.lmtc).rgb * e_lmscale[0].rgb;\r\n\t\tfloat4 fb = t_fullbright.Sample(s_fullbright, inp.tc);\r\n\t\tresult.rgb += fb.rgb * fb.a;//lerp(result.rgb, fb.rgb, fb.a);\r\n\t\treturn result;\r\n\t}\r\n#endif"
  },
  {
    "path": "engine/shaders/hlsl11/defaultwarp.hlsl",
    "content": "!!samps diffuse\r\n//!!cvarf r_wateralpha\r\n\r\nstruct a2v\r\n{\r\n\tfloat4 pos: POSITION;\r\n\tfloat2 tc: TEXCOORD0;\r\n};\r\nstruct v2f\r\n{\r\n\tfloat4 pos: SV_POSITION;\r\n\tfloat2 tc: TEXCOORD0;\r\n};\r\n\r\n#include <ftedefs.h>\r\n\r\n#ifdef VERTEX_SHADER\r\n\tv2f main (a2v inp)\r\n\t{\r\n\t\tv2f outp;\r\n\t\toutp.pos = mul(m_model, inp.pos);\r\n\t\toutp.pos = mul(m_view, outp.pos);\r\n\t\toutp.pos = mul(m_projection, outp.pos);\r\n\t\toutp.tc = inp.tc;\r\n\t\treturn outp;\r\n\t}\r\n#endif\r\n\r\n#ifdef FRAGMENT_SHADER\r\n//\tfloat cvar_r_wateralpha;\r\n//\tfloat e_time;\r\n\tSamplerState s_diffuse;\r\n\tTexture2D t_diffuse;\r\n\tfloat4 main (v2f inp) : SV_TARGET\r\n\t{\r\n\t\tfloat2 ntc = inp.tc + sin(inp.tc.yx+e_time)*0.125;\r\n\t\tfloat4 r;\r\n\t\tr = t_diffuse.Sample(s_diffuse, ntc);\r\n#ifdef ALPHA\r\n\t\tr.a = float(ALPHA);\r\n#else\r\n//\t\tr.a *= r_wateralpha;\r\n#endif\r\n\t\treturn r;\r\n\t}\r\n#endif\r\n"
  },
  {
    "path": "engine/shaders/hlsl11/depthonly.hlsl",
    "content": "//used for generating shadowmaps and stuff that draws nothing.\r\n\r\nstruct a2v\r\n{\r\n\tfloat4 pos: POSITION;\r\n};\r\nstruct v2f\r\n{\r\n\tfloat3 col: TEXCOORD;\r\n\tfloat4 pos: SV_POSITION;\r\n};\r\n\r\n#include <ftedefs.h>\r\n\r\n#ifdef VERTEX_SHADER\r\n\tv2f main (a2v inp)\r\n\t{\r\n\t\tv2f outp;\r\n\t\toutp.pos = mul(m_model, inp.pos);\r\n\t\toutp.pos = mul(m_view, outp.pos);\r\n\t\toutp.pos = mul(m_projection, outp.pos);\r\n\t\toutp.col = inp.pos.xyz - l_lightposition;\r\n\t\treturn outp;\r\n\t}\r\n#endif\r\n\r\n#ifdef FRAGMENT_SHADER\r\n\t#if LEVEL < 0x10000\r\n\t\t//pre dx10 requires that we ALWAYS write to a target.\r\n\t\tfloat4 main (v2f inp) : SV_TARGET\r\n\t\t{\r\n\t\t\treturn float4(0, 0, 0, 1);\r\n\t\t}\r\n\t#else\r\n\t\t//but on 10, it'll write depth automatically and we don't care about colour.\r\n\t\tvoid main (v2f inp)\r\n\t\t{\r\n\t\t}\r\n\t#endif\r\n#endif\r\n"
  },
  {
    "path": "engine/shaders/hlsl11/drawflat_wall.hlsl",
    "content": "!!samps lightmap\r\n\r\nstruct a2v\r\n{\r\n\tfloat4 pos: POSITION;\r\n\tfloat2 tc: TEXCOORD0;\r\n\tfloat2 lmtc: TEXCOORD1;\r\n\tfloat3 norm: NORMAL;\r\n};\r\nstruct v2f\r\n{\r\n\tfloat4 pos: SV_POSITION;\r\n\tfloat2 tc: TEXCOORD0;\r\n\tfloat2 lmtc: TEXCOORD1;\r\n\tfloat4 col: TEXCOORD2;\r\n};\r\n\r\n#include <ftedefs.h>\r\n\r\n#ifdef VERTEX_SHADER\r\n\tv2f main (a2v inp)\r\n\t{\r\n\t\tv2f outp;\r\n\t\toutp.pos = mul(m_model, inp.pos);\r\n\t\toutp.pos = mul(m_view, outp.pos);\r\n\t\toutp.pos = mul(m_projection, outp.pos);\r\n\t\toutp.tc = inp.tc;\r\n\t\toutp.lmtc = inp.lmtc;\r\n\t\toutp.col = ((inp.norm.z<0.73)?float4(0.5, 0.5, 0.5, 1):float4(0.25, 0.25, 0.5, 1));\r\n\t\treturn outp;\r\n\t}\r\n#endif\r\n\r\n#ifdef FRAGMENT_SHADER\r\n\tTexture2D t_lightmap : register(t0);\r\n\tSamplerState s_lightmap : register(s0);\r\n\r\n\tfloat4 main (v2f inp) : SV_TARGET\r\n\t{\r\n\t\treturn inp.col * t_lightmap.Sample(s_lightmap, inp.lmtc);\r\n\t}\r\n#endif"
  },
  {
    "path": "engine/shaders/hlsl11/fixedemu.hlsl",
    "content": "!!samps 1\r\nstruct a2v\r\n{\r\n\tfloat4 pos: POSITION;\r\n\tfloat2 tc: TEXCOORD0;\r\n\tfloat4 vcol: COLOR0;\r\n};\r\nstruct v2f\r\n{\r\n\tfloat4 pos: SV_POSITION;\r\n\tfloat2 tc: TEXCOORD0;\r\n\tfloat4 vcol: COLOR0;\r\n};\r\n\r\n#include <ftedefs.h>\r\n\r\n#ifdef VERTEX_SHADER\r\n\tv2f main (a2v inp)\r\n\t{\r\n\t\tv2f outp;\r\n\t\toutp.pos = mul(m_model, inp.pos);\r\n\t\toutp.pos = mul(m_view, outp.pos);\r\n\t\toutp.pos = mul(m_projection, outp.pos);\r\n\t\toutp.tc = inp.tc;\r\n\t\toutp.vcol = inp.vcol;\r\n\t\treturn outp;\r\n\t}\r\n#endif\r\n\r\n#ifdef FRAGMENT_SHADER\r\n\tTexture2D t_t0 : register(t0);\r\n\tSamplerState s_t0 : register(s0);\r\n\tfloat4 main (v2f inp) : SV_TARGET\r\n\t{\r\n\t\tfloat4 tc = t_t0.Sample(s_t0, inp.tc).rgba;\r\n\t\ttc.rgba *= inp.vcol.bgra;\r\n#ifdef ALPHATEST\r\n\t\tif (!(tc.a ALPHATEST))\r\n\t\t\tdiscard;\r\n#endif\r\n\t\treturn tc;\r\n\t}\r\n#endif"
  },
  {
    "path": "engine/shaders/hlsl11/menutint.hlsl",
    "content": "!!samps 1\r\n!!cvard3 r_menutint=0.2 0.2 0.2\r\n!!cvardf r_menutint_inverse=0.0\r\n\r\nstruct a2v\r\n{\r\n\tfloat4 pos: POSITION;\r\n\tfloat2 tc: TEXCOORD0;\r\n\tfloat4 vcol: COLOR0;\r\n};\r\nstruct v2f\r\n{\r\n\tfloat4 pos: SV_POSITION;\r\n\tfloat2 tc: TEXCOORD0;\r\n\tfloat4 vcol: COLOR0;\r\n};\r\n\r\n#include <ftedefs.h>\r\n\r\n#ifdef VERTEX_SHADER\r\n\tv2f main (a2v inp)\r\n\t{\r\n\t\tv2f outp;\r\n\t\toutp.pos = mul(m_projection, inp.pos);\r\n\t\toutp.tc = inp.tc;\r\n\t\toutp.vcol = inp.vcol;\r\n\t\treturn outp;\r\n\t}\r\n#endif\r\n\r\n#ifdef FRAGMENT_SHADER\r\n\tTexture2D t_t0;\r\n\tSamplerState s_t0;\r\n\tstatic const float3 lumfactors = float3 (0.299, 0.587, 0.114);\r\n\tfloat4 main (v2f inp) : SV_TARGET\r\n\t{\r\n\t\tfloat4 texcolor = t_t0.Sample(s_t0, inp.tc);\r\n\t\tfloat luminance = dot(lumfactors, texcolor.rgb);\r\n\t\ttexcolor.rgb = float3(luminance, luminance, luminance);\r\n\t\ttexcolor.rgb *= r_menutint;\r\n\t\ttexcolor.rgb = (r_menutint_inverse > 0) ? (1.0 - texcolor.rgb) : texcolor.rgb;\r\n\r\n\t\treturn texcolor * inp.vcol;\r\n\t}\r\n#endif\r\n\r\n"
  },
  {
    "path": "engine/shaders/hlsl11/rtlight.hlsl",
    "content": "!!samps diffuse normalmap specular upper lower shadowmap projectionmap\r\n!!permu BUMP\r\n!!permu FRAMEBLEND\r\n!!permu SKELETAL\r\n!!permu UPPERLOWER\r\n!!permu FOG\r\n!!cvarf r_glsl_offsetmapping_scale\r\n!!cvardf r_glsl_pcf=5\r\n\r\n\r\n//this is the main shader responsible for realtime dlights.\r\n\r\n//texture units:\r\n//s0=diffuse, s1=normal, s2=specular, s3=shadowmap\r\n//custom modifiers:\r\n//PCF(shadowmap)\r\n//CUBEPROJ(projected cubemap)\r\n//SPOT(projected circle\r\n//CUBESHADOW\r\n\r\n#undef CUBE\t//engine cannot load cubemaps properly with d3d yet.\r\n\r\n#ifndef r_glsl_pcf\r\n#error r_glsl_pcf wasn't defined\r\n#endif\r\n#if r_glsl_pcf < 1\r\n\t#undef r_glsl_pcf\r\n\t#define r_glsl_pcf 9\r\n#endif\r\n\r\n#ifdef UPPERLOWER\r\n#define UPPER\r\n#define LOWER\r\n#endif\r\n\r\n\r\nstruct a2v\r\n{\r\n\tfloat4 pos: POSITION;\r\n\tfloat2 tc: TEXCOORD0;\r\n\tfloat3 n: NORMAL;\r\n\tfloat3 s: TANGENT;\r\n\tfloat3 t: BINORMAL;\r\n};\r\nstruct v2f\r\n{\r\n\tfloat4 pos: SV_POSITION;\r\n\tfloat2 tc: TEXCOORD0;\r\n\tfloat3 lightvector: TEXCOORD1;\r\n\tfloat3 eyevector: TEXCOORD2;\r\n\tfloat3 vtexprojcoord: TEXCOORD3;\r\n};\r\n\r\n#include <ftedefs.h>\r\n\r\n#ifdef VERTEX_SHADER\r\n\tv2f main (a2v inp)\r\n\t{\r\n\t\tv2f outp;\r\n\t\tfloat4 wpos;\r\n\t\twpos = mul(m_model, inp.pos);\r\n\t\toutp.pos = mul(m_view, wpos);\r\n\t\toutp.pos = mul(m_projection, outp.pos);\r\n\t\toutp.tc = inp.tc.xy;\r\n\t\tfloat3 lightminusvertex = l_lightposition - wpos.xyz;\r\n\t\toutp.lightvector.x = -dot(lightminusvertex, inp.s.xyz);\r\n\t\toutp.lightvector.y = dot(lightminusvertex, inp.t.xyz);\r\n\t\toutp.lightvector.z = dot(lightminusvertex, inp.n.xyz);\r\n\t\tfloat3 eyeminusvertex = e_eyepos - wpos.xyz;\r\n\t\toutp.eyevector.x = -dot(eyeminusvertex, inp.s.xyz);\r\n\t\toutp.eyevector.y = dot(eyeminusvertex, inp.t.xyz);\r\n\t\toutp.eyevector.z = dot(eyeminusvertex, inp.n.xyz);\r\n\t\toutp.vtexprojcoord = mul(l_cubematrix, wpos).xyz;\r\n\t\treturn outp;\r\n\t}\r\n#endif\r\n\r\n\r\n#ifdef FRAGMENT_SHADER\r\n\r\n\tTexture2D t_shadowmap : register(t0);\r\n\tTextureCube t_projectionmap : register(t1);\r\n\tTexture2D t_diffuse : register(t2);\r\n\tTexture2D t_normalmap : register(t3);\r\n\tTexture2D t_specular : register(t4);\r\n\tTexture2D t_upper : register(t5);\r\n\tTexture2D t_lower : register(t6);\r\n\r\n\tSamplerComparisonState s_shadowmap : register(s0);\r\n\tSamplerState s_projectionmap\t : register(s1);\r\n\tSamplerState s_diffuse : register(s2);\r\n\tSamplerState s_normalmap : register(s3);\r\n\tSamplerState s_specular : register(s4);\r\n\tSamplerState s_upper : register(s5);\r\n\tSamplerState s_lower : register(s6);\r\n\r\n\r\n#ifdef PCF\r\nfloat3 ShadowmapCoord(float3 vtexprojcoord)\r\n{\r\n#ifdef SPOT\r\n\t//bias it. don't bother figuring out which side or anything, its not needed\r\n\t//l_projmatrix contains the light's projection matrix so no other magic needed\r\n\tvtexprojcoord.z -= 0.015;\r\n\treturn (vtexprojcoord.xyz + float3(1.0, 1.0, 1.0)) * float3(0.5, 0.5, 0.5);\r\n//#elif defined(CUBESHADOW)\r\n//\tvec3 shadowcoord = vshadowcoord.xyz / vshadowcoord.w;\r\n//\t#define dosamp(x,y) shadowCube(s_t4, shadowcoord + vec2(x,y)*texscale.xy).r\r\n#else\r\n\t//figure out which axis to use\r\n\t//texture is arranged thusly:\r\n\t//forward left  up\r\n\t//back    right down\r\n\tfloat3 dir = abs(vtexprojcoord.xyz);\r\n\t//assume z is the major axis (ie: forward from the light)\r\n\tfloat3 t = vtexprojcoord.xyz;\r\n\tfloat ma = dir.z;\r\n\tfloat3 axis = float3(0.5/3.0, 0.5/2.0, 0.5);\r\n\tif (dir.x > ma)\r\n\t{\r\n\t\tma = dir.x;\r\n\t\tt = vtexprojcoord.zyx;\r\n\t\taxis.x = 0.5;\r\n\t}\r\n\tif (dir.y > ma)\r\n\t{\r\n\t\tma = dir.y;\r\n\t\tt = vtexprojcoord.xzy;\r\n\t\taxis.x = 2.5/3.0;\r\n\t}\r\n\t//if the axis is negative, flip it.\r\n\tif (t.z > 0.0)\r\n\t{\r\n\t\taxis.y = 1.5/2.0;\r\n\t\tt.z = -t.z;\r\n\t}\r\n\r\n\t//we also need to pass the result through the light's projection matrix too\r\n\t//the 'matrix' we need only contains 5 actual values. and one of them is a -1. So we might as well just use a vec4.\r\n\t//note: the projection matrix also includes scalers to pinch the image inwards to avoid sampling over borders, as well as to cope with non-square source image\r\n\t//the resulting z is prescaled to result in a value between -0.5 and 0.5.\r\n\t//also make sure we're in the right quadrant type thing\r\n\treturn axis + ((l_shadowmapproj.xyz*t.xyz + float3(0.0, 0.0, l_shadowmapproj.w)) / -t.z);\r\n#endif\r\n}\r\n\r\nfloat ShadowmapFilter(float3 vtexprojcoord)\r\n{\r\n\tfloat3 shadowcoord = ShadowmapCoord(vtexprojcoord);\r\n\r\n//\t#define dosamp(x,y) shadow2D(s_t4, shadowcoord.xyz + (vec3(x,y,0.0)*l_shadowmapscale.xyx)).r\r\n\r\n//\t#define dosamp(x,y) (t_shadowmap.Sample(s_shadowmap, shadowcoord.xy + (float2(x,y)*l_shadowmapscale.xy)).r < shadowcoord.z)\r\n\t#define dosamp(x,y) (t_shadowmap.SampleCmpLevelZero(s_shadowmap, shadowcoord.xy+(float2(x,y)*l_shadowmapscale.xy), shadowcoord.z))\r\n\r\n\tfloat s = 0.0;\r\n\t#if r_glsl_pcf >= 1 && r_glsl_pcf < 5\r\n\t\ts += dosamp(0.0, 0.0);\r\n\t\treturn s;\r\n\t#elif r_glsl_pcf >= 5 && r_glsl_pcf < 9\r\n\t\ts += dosamp(-1.0, 0.0);\r\n\t\ts += dosamp(0.0, -1.0);\r\n\t\ts += dosamp(0.0, 0.0);\r\n\t\ts += dosamp(0.0, 1.0);\r\n\t\ts += dosamp(1.0, 0.0);\r\n\t\treturn s * (1.0/5.0);\r\n\t#else\r\n\t\ts += dosamp(-1.0, -1.0);\r\n\t\ts += dosamp(-1.0, 0.0);\r\n\t\ts += dosamp(-1.0, 1.0);\r\n\t\ts += dosamp(0.0, -1.0);\r\n\t\ts += dosamp(0.0, 0.0);\r\n\t\ts += dosamp(0.0, 1.0);\r\n\t\ts += dosamp(1.0, -1.0);\r\n\t\ts += dosamp(1.0, 0.0);\r\n\t\ts += dosamp(1.0, 1.0);\r\n\t\treturn s * (1.0/9.0);\r\n\t#endif\r\n}\r\n#endif\r\n\r\n\r\n\tfloat4 main (v2f inp) : SV_TARGET\r\n\t{\r\n\t\tfloat2 tc = inp.tc;\t//TODO: offsetmapping.\r\n\r\n\t\tfloat4 base = t_diffuse.Sample(s_diffuse, tc);\r\n#ifdef BUMP\r\n\t\tfloat4 bump = t_normalmap.Sample(s_normalmap, tc);\r\n\t\tbump.rgb = normalize(bump.rgb - 0.5);\r\n#else\r\n\t\tfloat4 bump = float4(0, 0, 1, 0);\r\n#endif\r\n\t\tfloat4 spec = t_specular.Sample(s_specular, tc);\r\n#ifdef CUBE\r\n\t\tfloat4 cubemap = t_projectionmap.Sample(s_projectionmap, inp.vtexprojcoord);\r\n#endif\r\n#ifdef LOWER\r\n\t\tfloat4 lower = t_lower.Sample(s_lower, tc);\r\n\t\tbase += lower;\r\n#endif\r\n#ifdef UPPER\r\n\t\tfloat4 upper = t_upper.Sample(s_upper, tc);\r\n\t\tbase += upper;\r\n#endif\r\n\r\n\t\tfloat lightscale = max(1.0 - (dot(inp.lightvector,inp.lightvector)/(l_lightradius*l_lightradius)), 0.0);\r\n\t\tfloat3 nl = normalize(inp.lightvector);\r\n\t\tfloat bumpscale = max(dot(bump.xyz, nl), 0.0);\r\n\t\tfloat3 halfdir = normalize(normalize(inp.eyevector) + nl);\r\n\t\tfloat specscale = pow(max(dot(halfdir, bump.rgb), 0.0), 32.0 * spec.a);\r\n\r\n\t\tfloat4 result;\r\n\t\tresult.a    = base.a;\r\n\t\tresult.rgb  = base.rgb * (l_lightcolourscale.x + l_lightcolourscale.y * bumpscale);\t//amient light + diffuse\r\n\t\tresult.rgb += spec.rgb * l_lightcolourscale.z * specscale;\t//specular\r\n\r\n\t\tresult.rgb *= lightscale * l_colour;\t//fade light by distance and light colour.\r\n\r\n#ifdef CUBE\r\n\t\tresult.rgb *= cubemap.rgb;\t//fade by cubemap\r\n#endif\r\n\r\n#ifdef PCF\r\n\t\tresult.rgb *= ShadowmapFilter(inp.vtexprojcoord);\r\n#endif\r\n\r\n\t\t//TODO: fog\r\n\t\treturn result;\r\n\t}\r\n#endif\r\n"
  },
  {
    "path": "engine/shaders/hlsl11/terrain.hlsl",
    "content": "struct a2v\r\n{\r\n\tfloat4 pos: POSITION;\r\n\tfloat2 tc: TEXCOORD0;\r\n\tfloat2 lmtc: TEXCOORD0;\r\n\tfloat4 vcol: COLOR0;\r\n};\r\nstruct v2f\r\n{\r\n\tfloat4 pos: SV_POSITION;\r\n\tfloat2 tc: TEXCOORD0;\r\n\tfloat2 lmtc: TEXCOORD1;\r\n\tfloat4 vcol: COLOR0;\r\n\tfloat3 vtexprojcoord: TEXCOORD2;\r\n\tfloat3 vtexprojcoord: TEXCOORD2;\r\n};\r\n\r\n#include <ftedefs.h>\r\n\r\n#ifdef VERTEX_SHADER\r\n\tv2f main (a2v inp)\r\n\t{\r\n\t\tv2f outp;\r\n\t\toutp.pos = mul(m_model, inp.pos);\r\n\t\toutp.pos = mul(m_view, outp.pos);\r\n\t\toutp.pos = mul(m_projection, outp.pos);\r\n\t\toutp.tc = inp.tc;\r\n\t\toutp.lmtc = inp.lmtc;\r\n\t\toutp.vcol = inp.vcol;\r\n\t\treturn outp;\r\n\t}\r\n#endif\r\n\r\n#ifdef FRAGMENT_SHADER\r\n\tTexture2D shaderTexture[7];\r\n\tSamplerState SampleType[7];\r\n\r\n\tfloat4 main (v2f inp) : SV_TARGET\r\n\t{\r\n\t\tfloat4 result;\r\n\r\n\t\tfloat4 base = shaderTexture[0].Sample(SampleType[0], inp.tc);\r\n#ifdef BUMP\r\n\t\tfloat4 bump = shaderTexture[1].Sample(SampleType[1], inp.tc);\r\n#else\r\n\t\tfloat4 bump = float4(0, 0, 1, 0);\r\n#endif\r\n\t\tfloat4 spec = shaderTexture[2].Sample(SampleType[2], inp.tc);\r\n#ifdef CUBE\r\n\t\tfloat4 cubemap = shaderTexture[3].Sample(SampleType[3], inp.vtexprojcoord);\r\n#endif\r\n\t\t//shadowmap 2d\r\n#ifdef LOWER\r\n\t\tfloat4 lower = shaderTexture[5].Sample(SampleType[5], inp.tc);\r\n\t\tbase += lower;\r\n#endif\r\n#ifdef UPPER\r\n\t\tfloat4 upper = shaderTexture[6].Sample(SampleType[6], inp.tc);\r\n\t\tbase += upper;\r\n#endif\r\n\r\n\t\tfloat lightscale = max(1.0 - (dot(inp.lightvector,inp.lightvector)/(l_lightradius*l_lightradius)), 0.0);\r\n\t\tfloat3 nl = normalize(inp.lightvector);\r\n\t\tfloat bumpscale = max(dot(bump.xyz, nl), 0.0);\r\n\t\tfloat3 halfdir = normalize(normalize(eyevector) + nl);\r\n\t\tfloat specscale = pow(max(dot(halfdir, bumps), 0.0), 32.0 * spec.a);\r\n\r\n\t\tresult.a = base.a;\r\n\t\tresult.rgb = base.rgb * (l_lightcolourscale.x + l_lightcolourscale.y * bumpscale);\t//amient light + diffuse\r\n\t\tresult.rgb += spec.rgb * l_lightcolourscale.z * specscale;\t//specular\r\n\r\n\t\tresult.rgb *= lightscale;\t//fade light by distance\r\n\r\n#ifdef CUBE\r\n\t\tresult.rgb *= cubemap.rgb;\t//fade by cubemap\r\n#endif\r\n\r\n\t\treturn result;\r\n\t}\r\n#endif"
  },
  {
    "path": "engine/shaders/hlsl11/terraindebug.hlsl",
    "content": "struct a2v\r\n{\r\n\tfloat4 pos: POSITION;\r\n\tfloat2 tc: TEXCOORD0;\r\n};\r\nstruct v2f\r\n{\r\n\tfloat4 pos: SV_POSITION;\r\n\tfloat2 tc: TEXCOORD0;\r\n\tfloat2 lmtc: TEXCOORD1;\r\n};\r\n\r\n#include <ftedefs.h>\r\n\r\n#ifdef VERTEX_SHADER\r\n\tv2f main (a2v inp)\r\n\t{\r\n\t\tv2f outp;\r\n\t\toutp.pos = mul(m_model, inp.pos);\r\n\t\toutp.pos = mul(m_view, outp.pos);\r\n\t\toutp.pos = mul(m_projection, outp.pos);\r\n\t\toutp.tc = inp.tc;\r\n\t\toutp.lmtc = inp.lmtc;\r\n\t\treturn outp;\r\n\t}\r\n#endif\r\n\r\n#ifdef FRAGMENT_SHADER\r\n//\tTexture2D shaderTexture[5];\r\n//\tSamplerState SampleType;\r\n\r\n\tfloat4 main (v2f inp) : SV_TARGET\r\n\t{\r\n\t\treturn float4(1,1,1,1);//shaderTexture[4].Sample(SampleType, inp.lmtc).bgr, 1.0);\r\n\t}\r\n#endif"
  },
  {
    "path": "engine/shaders/hlsl9/defaultskin.hlsl",
    "content": "!!permu FRAMEBLEND\r\n!!permu UPPERLOWER\r\n//!!permu FULLBRIGHT\r\n!!samps diffuse upper lower\r\n// fullbright\r\n\r\nstruct a2v\r\n{\r\n\tfloat3 pos: POSITION0;\r\n#ifdef FRAMEBLEND\r\n\tfloat3 pos2: POSITION1;\r\n#endif\r\n\tfloat2 tc: TEXCOORD0;\r\n\tfloat3 normal: NORMAL;\r\n};\r\nstruct v2f\r\n{\r\n#ifndef FRAGMENT_SHADER\r\n\tfloat4 pos: POSITION;\r\n#endif\r\n\tfloat2 tc: TEXCOORD0;\r\n\tfloat3 light: TEXCOORD1;\r\n};\r\n\r\n//#include <ftedefs.h>\r\n\r\n#ifdef VERTEX_SHADER\r\nfloat3 e_light_dir;\r\nfloat3 e_light_mul;\r\nfloat3 e_light_ambient;\r\nfloat2 e_vblend;\r\nfloat4x4  m_model;\r\nfloat4x4  m_view;\r\nfloat4x4  m_projection;\r\n\tv2f main (a2v inp)\r\n\t{\r\n\t\tv2f outp;\r\n\t\tfloat4 pos;\r\n#ifdef FRAMEBLEND\r\n\t\tpos = float4(e_vblend.x*inp.pos + e_vblend.y*inp.pos2, 1);\r\n#else\r\n\t\tpos = float4(inp.pos, 1);\r\n#endif\r\n\t\toutp.pos = mul(m_model, pos);\r\n\t\toutp.pos = mul(m_view, outp.pos);\r\n\t\toutp.pos = mul(m_projection, outp.pos);\r\n\r\n\t\tfloat d = dot(inp.normal, e_light_dir);\r\n\t\toutp.light = e_light_ambient + (d * e_light_mul);\r\n\t\toutp.tc = inp.tc.xy;\r\n\t\treturn outp;\r\n\t}\r\n#endif\r\n#ifdef FRAGMENT_SHADER\r\nfloat4 e_colourident;\r\nfloat3 e_uppercolour;\r\nfloat3 e_lowercolour;\r\nsampler s_diffuse; /*diffuse*/\r\nsampler s_upper; /*upper*/\r\nsampler s_lower; /*lower*/\r\nsampler s_fullbright; /*fullbright*/\r\n\tfloat4 main (v2f inp) : SV_TARGET\r\n\t{\r\n\t\tfloat4 col;\r\n\t\tcol = tex2D(s_diffuse, inp.tc);\r\n#ifdef UPPER\r\n\t\tfloat4 uc = tex2D(s_upper, inp.tc);\r\n\t\tcol.rgb += uc.rgb*e_uppercolour * uc.a;\r\n#endif\r\n#ifdef LOWER\r\n\t\tfloat4 lc = tex2D(s_lower, inp.tc);\r\n\t\tcol.rgb += lc.rgb*e_lowercolour * lc.a;\r\n#endif\r\n\t\tcol.rgb *= inp.light;\r\n#ifdef FULLBRIGHT\r\n\t\tfloat4 fb = tex2D(s_fullbright, inp.tc);\r\n\t\tcol.rgb = lerp(col.rgb, fb.rgb, fb.a);\r\n#endif\r\n\t\treturn col * e_colourident;\r\n//\t\treturn fog4(col * e_colourident);\r\n\t}\r\n#endif\r\n"
  },
  {
    "path": "engine/shaders/hlsl9/defaultsky.hlsl",
    "content": "!!samps 2\r\n\r\n\tstruct a2v\r\n\t{\r\n\t\tfloat4 pos: POSITION;\r\n\t};\r\n\tstruct v2f\r\n\t{\r\n#ifndef FRAGMENT_SHADER\r\n\t\tfloat4 pos: POSITION;\r\n#endif\r\n\t\tfloat3 vpos: TEXCOORD0;\r\n\t};\r\n\r\n#ifdef VERTEX_SHADER\r\n\tfloat4x4  m_modelviewprojection;\r\n\tv2f main (in a2v inp)\r\n\t{\r\n\t\tv2f outp;\r\n\t\toutp.pos = mul(m_modelviewprojection, inp.pos);\r\n\t\toutp.vpos = inp.pos.xyz;\r\n\t\treturn outp;\r\n\t}\r\n#endif\r\n\r\n#ifdef FRAGMENT_SHADER\r\n\tfloat e_time;\r\n\tfloat3 e_eyepos;\r\n\r\n\tsampler s_diffuse; /*diffuse*/\r\n\tsampler s_fullbright; /*normal*/\r\n\tfloat4 main (v2f inp) : COLOR0\r\n\t{\r\n\t\tfloat2 tccoord;\r\n\r\n\t\tfloat3 dir = inp.vpos - e_eyepos;\r\n\r\n\t\tdir.z *= 3.0;\r\n\t\tdir.xy /= 0.5*length(dir);\r\n\r\n\t\ttccoord = (dir.xy + e_time*0.03125);\r\n\t\tfloat4 solid = tex2D(s_diffuse, tccoord);\r\n\r\n\t\ttccoord = (dir.xy + e_time*0.0625);\r\n\t\tfloat4 clouds = tex2D(s_fullbright, tccoord);\r\n\t\treturn float4((solid.rgb*(1.0-clouds.a)) + (clouds.a*clouds.rgb), 1);\r\n\t}\r\n#endif\r\n"
  },
  {
    "path": "engine/shaders/hlsl9/defaultskybox.hlsl",
    "content": "!!samps reflectcube\r\n\r\n\tstruct a2v\r\n\t{\r\n\t\tfloat4 pos: POSITION;\r\n\t};\r\n\tstruct v2f\r\n\t{\r\n#ifndef FRAGMENT_SHADER\r\n\t\tfloat4 pos: POSITION;\r\n#endif\r\n\t\tfloat3 texc: TEXCOORD0;\r\n\t};\r\n\r\n#ifdef VERTEX_SHADER\r\n\tfloat4x4  m_modelviewprojection;\r\n\tv2f main (a2v inp)\r\n\t{\r\n\t\tv2f outp;\r\n\t\toutp.pos = mul(m_modelviewprojection, inp.pos);\r\n\t\toutp.texc = inp.pos.xyz;\r\n\t\treturn outp;\r\n\t}\r\n#endif\r\n\r\n#ifdef FRAGMENT_SHADER\r\n\tfloat3 e_eyepos;\r\n\tsampler s_reflectcube;\r\n\tfloat4 main (v2f inp) : COLOR0\r\n\t{\r\n\t\tfloat3 tc = inp.texc - e_eyepos.xyz;\r\n\t\treturn texCUBE(s_reflectcube, tc);\r\n\t}\r\n#endif\r\n"
  },
  {
    "path": "engine/shaders/hlsl9/defaultwall.hlsl",
    "content": "//!!ver 100 150\r\n//!!permu DELUXE\r\n!!permu FULLBRIGHT\r\n!!permu FOG\r\n//!!permu LIGHTSTYLED\r\n//!!permu BUMP\r\n//!!permu SPECULAR\r\n//!!permu REFLECTCUBEMASK\r\n//!!cvarf r_glsl_offsetmapping_scale\r\n//!!cvardf r_tessellation_level=5\r\n//!!samps diffuse lightmap specular normalmap fullbright reflectmask reflectcube paletted lightmap1 lightmap2 lightmap3\r\n!!samps diffuse fullbright lightmap\r\n\r\n#undef SPECULAR\r\n\r\n//#include \"sys/defs.h\"\r\n#define vec4 float4\r\n#define vec3 float3\r\n#define vec2 float2\r\n#define texture2D tex2D\r\n#define mat3 float3x3\r\n#define mat4 float4x4\r\n\tstruct a2v\r\n\t{\r\n\t\tvec4 v_position : POSITION;\r\n\t\tvec2 v_texcoord : TEXCOORD0;\r\n\r\n#if defined(OFFSETMAPPING) || defined(SPECULAR) || defined(REFLECTCUBEMASK)\r\n\t\tvec3 v_normal\t: NORMAL;\r\n\t\tvec3 v_svector\t: TANGENT;\r\n\t\tvec3 v_tvector\t: BINORMAL;\r\n#endif\r\n#ifdef VERTEXLIT\r\n\t\tvec4 v_colour\t: COLOR0;\r\n#else\r\n\t\tvec2 v_lmcoord  : TEXCOORD1;\r\n#endif\r\n\t};\r\n\tstruct v2f\r\n\t{\r\n\t\t#ifndef FRAGMENT_SHADER\r\n\t\t\tvec4 pos: POSITION;\r\n\t\t#endif\r\n\r\n\t\t#if defined(OFFSETMAPPING) || defined(SPECULAR) || defined(REFLECTCUBEMASK) || defined(FOG)\r\n\t\t\tvec3 eyevector : TEXCOORD4;\r\n\t\t#endif\r\n\r\n\t\t#if defined(REFLECTCUBEMASK) || defined(BUMPMODELSPACE)\r\n\t\t\tmat3 invsurface : TEXCOORD5;\r\n\t\t#endif\r\n\r\n#ifdef VERTEXLIT\r\n\t\tvec2 tclm : TEXCOORD0;\r\n#else\r\n\t\tvec4 tclm : TEXCOORD0;\r\n#endif\r\n\t\tvec4 vc : COLOR0;\r\n\t\t#ifndef VERTEXLIT\r\n\t\t\t#ifdef LIGHTSTYLED\r\n\t\t\t\t//we could use an offset, but that would still need to be per-surface which would break batches\r\n\t\t\t\t//fixme: merge attributes?\r\n\t\t\t\tvec2 lm1 : TEXCOORD1, lm2 : TEXCOORD2, lm3 : TEXCOORD3;\r\n\t\t\t#endif\r\n\t\t#endif\r\n\t};\r\n\r\n//this is what normally draws all of your walls, even with rtlights disabled\r\n//note that the '286' preset uses drawflat_walls instead.\r\n\r\n#include \"sys/fog.h\"\r\n\r\n#ifdef VERTEX_SHADER\r\nfloat4x4  m_modelviewprojection;\r\nfloat4x4  m_modelview;\r\nvec3 e_eyepos;\r\nvec4 e_lmscale;\r\nv2f main (a2v inp)\r\n{\r\n\tv2f outp;\r\n\r\n#if defined(OFFSETMAPPING) || defined(SPECULAR) || defined(REFLECTCUBEMASK)\r\n\tvec3 eyeminusvertex = e_eyepos - inp.v_position.xyz;\r\n\toutp.eyevector.x = dot(eyeminusvertex, inp.v_svector.xyz);\r\n\toutp.eyevector.y = dot(eyeminusvertex, inp.v_tvector.xyz);\r\n\toutp.eyevector.z = dot(eyeminusvertex, inp.v_normal.xyz);\r\n#elif defined(FOG)\r\n\toutp.eyevector = mul(m_modelview, inp.v_position);\r\n#endif\r\n#if defined(REFLECTCUBEMASK) || defined(BUMPMODELSPACE)\r\n\toutp.invsurface[0] = inp.v_svector;\r\n\toutp.invsurface[1] = inp.v_tvector;\r\n\toutp.invsurface[2] = inp.v_normal;\r\n#endif\r\n\toutp.tclm.xy = inp.v_texcoord;\r\n#ifdef FLOW\r\n//\toutp.tclm.x += e_time * -0.5;\r\n#endif\r\n#ifdef VERTEXLIT\r\n\t#ifdef LIGHTSTYLED\r\n\t\t//FIXME, only one colour.\r\n\t\toutp.vc = inp.v_colour * e_lmscale[0];\r\n\t#else\r\n\t\toutp.vc = inp.v_colour * e_lmscale;\r\n\t#endif\r\n#else\r\n\toutp.vc = e_lmscale;\r\n\toutp.tclm.zw = inp.v_lmcoord;\r\n\t#ifdef LIGHTSTYLED\r\n\t\toutp.lm1 = inp.v_lmcoord2;\r\n\t\toutp.lm2 = inp.v_lmcoord3;\r\n\t\toutp.lm3 = inp.v_lmcoord4;\r\n\t#endif\r\n#endif\r\n\toutp.pos = mul(m_modelviewprojection, inp.v_position);\r\n\r\n\treturn outp;\r\n}\r\n#endif\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n#ifdef FRAGMENT_SHADER\r\nsampler s_diffuse\t : register(s0);\r\n//sampler s_normalmap;\r\n//sampler s_specular;\r\n//sampler s_upper;\r\n//sampler s_lower;\r\nsampler s_fullbright  : register(s1);\r\n//sampler s_paletted;\r\n//sampler s_reflectcube;\r\n//sampler s_reflectmask;\r\nsampler s_lightmap\t : register(s2);\r\n//sampler s_deluxmap;\r\n\r\n\r\n//samplers\r\n#define s_colourmap\ts_t0\r\n//sampler2D\ts_colourmap;\r\n\r\n//vec4 e_lmscale;\r\nvec4 e_colourident;\r\n\r\n#ifdef OFFSETMAPPING\r\n#include \"sys/offsetmapping.h\"\r\n#endif\r\nvec4 main (v2f inp) : COLOR0\r\n{\r\n\tvec4 gl_FragColor;\r\n#define tc (inp.tclm.xy)\r\n#define lm0 (inp.tclm.zw)\r\n\r\n\r\n//adjust texture coords for offsetmapping\r\n#ifdef OFFSETMAPPING\r\n\tvec2 tcoffsetmap = offsetmap(s_normalmap, tc, eyevector);\r\n#define tc tcoffsetmap\r\n#endif\r\n\r\n#if defined(EIGHTBIT) && !defined(LIGHTSTYLED)\r\n\t//optional: round the lightmap coords to ensure all pixels within a texel have different lighting values either. it just looks wrong otherwise.\r\n\t//don't bother if its lightstyled, such cases will have unpredictable correlations anyway.\r\n\t//FIXME: this rounding is likely not correct with respect to software rendering. oh well.\r\n#if __VERSION__ >= 130\r\n\tvec2 lmsize = vec2(textureSize(s_lightmap0, 0));\r\n#else\r\n\t#define lmsize vec2(128.0,2048.0)\r\n#endif\r\n#define texelstolightmap (16.0)\r\n\tvec2 lmcoord0 = floor(lm0 * lmsize*texelstolightmap)/(lmsize*texelstolightmap);\r\n#define lm0 lmcoord0\r\n#endif\r\n\r\n\r\n//yay, regular texture!\r\n\tgl_FragColor = texture2D(s_diffuse, tc);\r\n\r\n#if defined(BUMP) && (defined(DELUXE) || defined(SPECULAR) || defined(REFLECTCUBEMASK))\r\n\tvec3 norm = normalize(texture2D(s_normalmap, tc).rgb - 0.5);\r\n#elif defined(SPECULAR) || defined(DELUXE) || defined(REFLECTCUBEMASK)\r\n\tvec3 norm = vec3(0, 0, 1);\t//specular lighting expects this to exist.\r\n#endif\r\n\r\n//modulate that by the lightmap(s) including deluxemap(s)\r\n#ifdef VERTEXLIT\r\n\t#ifdef LIGHTSTYLED\r\n\tvec3 lightmaps = inp.vc.rgb;\r\n\t#else\r\n\tvec3 lightmaps = inp.vc.rgb;\r\n\t#endif\r\n\t#define delux vec3(0.0,0.0,1.0)\r\n#else\r\n\t#ifdef LIGHTSTYLED\r\n#error foobar\r\n\t\t#define delux vec3(0.0,0.0,1.0)\r\n\t\tvec3 lightmaps;\r\n\t\t#ifdef DELUXE\r\n\t\t\tlightmaps  = texture2D(s_lightmap0, lm0).rgb * e_lmscale[0].rgb * dot(norm, 2.0*texture2D(s_deluxmap0, lm0).rgb-0.5);\r\n\t\t\tlightmaps += texture2D(s_lightmap1, lm1).rgb * e_lmscale[1].rgb * dot(norm, 2.0*texture2D(s_deluxmap1, lm1).rgb-0.5);\r\n\t\t\tlightmaps += texture2D(s_lightmap2, lm2).rgb * e_lmscale[2].rgb * dot(norm, 2.0*texture2D(s_deluxmap2, lm2).rgb-0.5);\r\n\t\t\tlightmaps += texture2D(s_lightmap3, lm3).rgb * e_lmscale[3].rgb * dot(norm, 2.0*texture2D(s_deluxmap3, lm3).rgb-0.5);\r\n\t\t#else\r\n\t\t\tlightmaps  = texture2D(s_lightmap0, lm0).rgb * e_lmscale[0].rgb;\r\n\t\t\tlightmaps += texture2D(s_lightmap1, lm1).rgb * e_lmscale[1].rgb;\r\n\t\t\tlightmaps += texture2D(s_lightmap2, lm2).rgb * e_lmscale[2].rgb;\r\n\t\t\tlightmaps += texture2D(s_lightmap3, lm3).rgb * e_lmscale[3].rgb;\r\n\t\t#endif\r\n\t#else\r\n\t\tvec3 lightmaps = texture2D(s_lightmap, lm0).rgb;\r\n\t\t//modulate by the  bumpmap dot light\r\n\t\t#ifdef DELUXE\r\n#error foobar\r\n\t\t\tvec3 delux = (texture2D(s_deluxmap, lm0).rgb-0.5);\r\n\t\t\t#ifdef BUMPMODELSPACE\r\n\t\t\t\tdelux = normalize(delux*invsurface);\r\n#else\r\n\t\t\t\tlightmaps *= 2.0 / max(0.25, delux.z);\t//counter the darkening from deluxmaps\r\n\t\t\t#endif\r\n\t\t\tlightmaps *= dot(norm, delux);\r\n\t\t#else\r\n\t\t\t#define delux vec3(0.0,0.0,1.0)\r\n\t\t#endif\r\n\t#endif\r\n\tlightmaps *= inp.vc.rgb;\r\n#endif\r\n\r\n//add in specular, if applicable.\r\n#ifdef SPECULAR\r\n\tvec4 specs = texture2D(s_specular, tc);\r\n\tvec3 halfdir = normalize(normalize(eyevector) + delux);\t//this norm should be the deluxemap info instead\r\n\tfloat spec = pow(max(dot(halfdir, norm), 0.0), FTE_SPECULAR_EXPONENT * specs.a);\r\n\tspec *= FTE_SPECULAR_MULTIPLIER;\r\n//NOTE: rtlights tend to have a *4 scaler here to over-emphasise the effect because it looks cool.\r\n//As not all maps will have deluxemapping, and the double-cos from the light util makes everything far too dark anyway,\r\n//we default to something that is not garish when the light value is directly infront of every single pixel.\r\n//we can justify this difference due to the rtlight editor etc showing the *4.\r\n\tgl_FragColor.rgb += spec * specs.rgb;\r\n#endif\r\n\r\n#ifdef REFLECTCUBEMASK\r\n\tvec3 rtc = reflect(normalize(-eyevector), norm);\r\n\trtc = rtc.x*invsurface[0] + rtc.y*invsurface[1] + rtc.z*invsurface[2];\r\n\trtc = (m_model * vec4(rtc.xyz,0.0)).xyz;\r\n\tgl_FragColor.rgb += texture2D(s_reflectmask, tc).rgb * textureCube(s_reflectcube, rtc).rgb;\r\n#endif\r\n\r\n#ifdef EIGHTBIT //FIXME: with this extra flag, half the permutations are redundant.\r\n\tlightmaps *= 0.5;\t//counter the fact that the colourmap contains overbright values and logically ranges from 0 to 2 intead of to 1.\r\n\tfloat pal = texture2D(s_paletted, tc).r;\t//the palette index. hopefully not interpolated.\r\n\tlightmaps -= 1.0 / 128.0;\t//software rendering appears to round down, so make sure we favour the lower values instead of rounding to the nearest\r\n\tgl_FragColor.r = texture2D(s_colourmap, vec2(pal, 1.0-lightmaps.r)).r;\t//do 3 lookups. this is to cope with lit files, would be a waste to not support those.\r\n\tgl_FragColor.g = texture2D(s_colourmap, vec2(pal, 1.0-lightmaps.g)).g;\t//its not very softwarey, but re-palettizing is ugly.\r\n\tgl_FragColor.b = texture2D(s_colourmap, vec2(pal, 1.0-lightmaps.b)).b;\t//without lits, it should be identical.\r\n#else\r\n\t//now we have our diffuse+specular terms, modulate by lightmap values.\r\n\tgl_FragColor.rgb *= lightmaps.rgb;\r\n\r\n//add on the fullbright\r\n#ifdef FULLBRIGHT\r\n\tvec4 fb = texture2D(s_fullbright, tc);\r\n\tgl_FragColor.rgb += fb.rgb*fb.a;\r\n#endif\r\n#endif\r\n\r\n//entity modifiers\r\n\tgl_FragColor = gl_FragColor * e_colourident;\r\n\r\n#if defined(MASK)\r\n#if defined(MASKLT)\r\n\tif (gl_FragColor.a < MASK)\r\n\t\tdiscard;\r\n#else\r\n\tif (gl_FragColor.a >= MASK)\r\n\t\tdiscard;\r\n#endif\r\n\tgl_FragColor.a = 1.0;\t//alpha blending AND alpha testing usually looks stupid, plus it screws up our fog.\r\n#endif\r\n\r\n//and finally hide it all if we're fogged.\r\n#ifdef FOG\r\n\tgl_FragColor = fog4(gl_FragColor, length(inp.eyevector));\r\n#endif\r\n\treturn gl_FragColor;\r\n}\r\n#endif\r\n\r\n"
  },
  {
    "path": "engine/shaders/hlsl9/defaultwarp.hlsl",
    "content": "!!cvarf r_wateralpha\r\n!!samps diffuse\r\n\r\nstruct a2v {\r\n\tfloat4 pos: POSITION;\r\n\tfloat2 tc: TEXCOORD0;\r\n};\r\nstruct v2f {\r\n\t#ifndef FRAGMENT_SHADER\r\n\tfloat4 pos: POSITION;\r\n\t#endif\r\n\tfloat2 tc: TEXCOORD0;\r\n};\r\n#ifdef VERTEX_SHADER\r\n\tfloat4x4  m_modelviewprojection;\r\n\tv2f main (a2v inp)\r\n\t{\r\n\t\tv2f outp;\r\n\t\toutp.pos = mul(m_modelviewprojection, inp.pos);\r\n\t\toutp.tc = inp.tc;\r\n\t\treturn outp;\r\n\t}\r\n#endif\r\n\r\n#ifdef FRAGMENT_SHADER\r\n\tfloat cvar_r_wateralpha;\r\n\tfloat e_time;\r\n\tsampler s_diffuse;\r\n\tfloat4 main (v2f inp) : COLOR0\r\n\t{\r\n\t\tfloat2 ntc = inp.tc + sin(inp.tc.yx+e_time)*0.125;\r\n\t\tfloat3 ts = tex2D(s_diffuse, ntc).xyz;\r\n\r\n#ifdef ALPHA\r\n\t\treturn float4(ts, float(ALPHA));\r\n#else\r\n\t\treturn float4(ts, cvar_r_wateralpha);\r\n#endif\r\n\t}\r\n#endif\r\n"
  },
  {
    "path": "engine/shaders/hlsl9/drawflat_wall.hlsl",
    "content": "!!cvard3 r_floorcolour\r\n!!cvard3 r_wallcolour\r\n!!samps 1\r\n//FIXME !!permu FOG\r\n\r\nstruct a2v {\r\n\tfloat4 pos: POSITION;\r\n\tfloat2 lmtc: TEXCOORD1;\r\n\tfloat3 normal: NORMAL;\r\n};\r\nstruct v2f {\r\n\t#ifndef FRAGMENT_SHADER\r\n\tfloat4 pos: POSITION;\r\n\t#endif\r\n\tfloat2 lmtc: TEXCOORD0;\r\n\tfloat4 col: TEXCOORD1;\t//tc not colour to preserve range for oversaturation\r\n};\r\n#ifdef VERTEX_SHADER\r\n\tfloat4x4  m_modelviewprojection;\r\n\tfloat4 e_lmscale;\r\n\tv2f main (a2v inp)\r\n\t{\r\n\t\tv2f outp;\r\n\t\toutp.pos = mul(m_modelviewprojection, inp.pos);\r\n\t\toutp.lmtc = inp.lmtc;\r\n\t\toutp.col = e_lmscale * float4(((inp.normal.z < 0.73)?r_wallcolour:r_floorcolour)/255.0, 1.0);\r\n\t\treturn outp;\r\n\t}\r\n#endif\r\n\r\n#ifdef FRAGMENT_SHADER\r\n\tsampler s_t0;\r\n\tfloat4 main (v2f inp) : COLOR0\r\n\t{\r\n\t\treturn inp.col * tex2D(s_t0, inp.lmtc).xyzw;\r\n\t}\r\n#endif\r\n"
  },
  {
    "path": "engine/shaders/hlsl9/rtlight.hlsl",
    "content": "!!permu BUMP\r\n!!permu SPECULAR\r\n!!permu OFFSETMAPPING\r\n!!permu SKELETAL\r\n!!permu FOG\r\n!!samps diffuse\r\n\r\n//\ttexture units:\r\n//\ts0=diffuse, s1=normal, s2=specular, s3=shadowmap\r\n//\tcustom modifiers:\r\n//\tPCF(shadowmap)\r\n//\tCUBE(projected cubemap)\r\n\r\n\r\n\r\n\tstruct a2v\r\n\t{\r\n\t\tfloat4 pos: POSITION;\r\n\t\tfloat3 tc: TEXCOORD0;\r\n\t\tfloat3 n: NORMAL0;\r\n\t\tfloat3 s: TANGENT0;\r\n\t\tfloat3 t: BINORMAL0;\r\n\t};\r\n\tstruct v2f\r\n\t{\r\n\t\t#ifndef FRAGMENT_SHADER\r\n\t\tfloat4 pos: POSITION;\r\n\t\t#endif\r\n\t\tfloat3 tc: TEXCOORD0;\r\n\t\tfloat3 lpos: TEXCOORD1;\r\n\t};\r\n\r\n#ifdef VERTEX_SHADER\r\n\tfloat4x4  m_modelviewprojection;\r\n\tfloat3 l_lightposition;\r\n\tv2f main (a2v inp)\r\n\t{\r\n\t\tv2f outp;\r\n\t\toutp.pos = mul(m_modelviewprojection, inp.pos);\r\n\t\toutp.tc = inp.tc;\r\n\r\n\t\tfloat3 lightminusvertex = l_lightposition - inp.pos.xyz;\r\n\t\toutp.lpos.x = dot(lightminusvertex, inp.s.xyz);\r\n\t\toutp.lpos.y = dot(lightminusvertex, inp.t.xyz);\r\n\t\toutp.lpos.z = dot(lightminusvertex, inp.n.xyz);\r\n\t\treturn outp;\r\n\t}\r\n#endif\r\n\r\n#ifdef FRAGMENT_SHADER\r\n\tsampler s_diffuse;\r\n\tfloat l_lightradius;\r\n\tfloat3 l_lightcolour;\r\n\tfloat4 main (v2f inp) : COLOR0\r\n\t{\r\n\t\tfloat3 col = l_lightcolour;\r\n\t\tcol *= max(1.0 - dot(inp.lpos, inp.lpos)/(l_lightradius*l_lightradius), 0.0);\r\n#ifdef FLAT\r\n\t\tfloat3 diff = FLAT;\r\n#else\r\n\t\tfloat3 diff = tex2D(s_diffuse, inp.tc);\r\n#endif\r\n\t\treturn float4(diff * col, 1);\r\n\t}\r\n#endif\r\n"
  },
  {
    "path": "engine/shaders/hlsl9/terrain.hlsl",
    "content": "!!permu FOG\r\n!!samps 5\r\n\r\n#include \"sys/fog.h\"\r\n\r\n//FIXME: too lazy to implement this right now. rtlights on d3d9 are just bad right now.\r\n#undef RTLIGHT\r\n#undef PCF\r\n#undef CUBE\r\n\r\nstruct a2v\r\n{\r\n\tfloat3 pos: POSITION0;\r\n\tfloat2 tc: TEXCOORD0;\r\n\tfloat2 lm: TEXCOORD1;\r\n\tfloat4 vc: COLOR;\r\n\r\n#ifdef RTLIGHT\r\n\tattribute vec3 v_normal;\r\n\tattribute vec3 v_svector;\r\n\tattribute vec3 v_tvector;\r\n#endif\r\n};\r\nstruct v2f\r\n{\r\n\tfloat1 depth: TEXCOORD1;\r\n\tfloat4 tclm: TEXCOORD0;\r\n\tfloat4 vc: COLOR;\r\n\r\n#ifdef RTLIGHT\r\n\tvarying vec3 lightvector;\r\n//\t#if defined(SPECULAR) || defined(OFFSETMAPPING)\r\n//\t\tvarying vec3 eyevector;\r\n//\t#endif\r\n\t#if defined(PCF) || defined(CUBE) || defined(SPOT)\r\n\t\tvarying vec4 vtexprojcoord;\r\n\t#endif\r\n#endif\r\n\r\n#ifndef FRAGMENT_SHADER\r\n\tfloat4 pos: POSITION;\r\n#endif\r\n};\r\n\r\n\r\n\r\n\r\n\r\n#ifdef VERTEX_SHADER\r\n\r\nfloat4x4  m_model;\r\nfloat4x4  m_view;\r\nfloat4x4  m_projection;\r\n\r\n#ifdef RTLIGHT\r\n\tuniform vec3 l_lightposition;\r\n//\t#if defined(SPECULAR) || defined(OFFSETMAPPING)\r\n//\t\tuniform vec3 e_eyepos;\r\n//\t#endif\r\n\t#if defined(PCF) || defined(CUBE) || defined(SPOT)\r\n\t\tuniform mat4 l_cubematrix;\r\n\t#endif\r\n#endif\r\n\r\nfloat3 e_lmscale;\r\n\r\nv2f main (a2v inp)\r\n{\r\n\tv2f outp;\r\n\toutp.tclm = float4(inp.tc, inp.lm);\r\n\toutp.vc = inp.vc;\r\n\r\n\tfloat4 pos = float4(inp.pos, 1);\r\n\tpos = mul(m_model, pos);\r\n\tpos = mul(m_view, pos);\r\n\toutp.depth = pos.z;\r\n\tpos = mul(m_projection, pos);\r\n\toutp.pos = pos;\r\n\r\n\t#ifdef RTLIGHT\r\n\t\t//light position is in model space, which is handy.\r\n\t\tvec3 lightminusvertex = l_lightposition - v_position.xyz;\r\n\r\n\t\t//no bumpmapping, so we can just use distance without regard for actual surface direction. we still do scalecos stuff. you might notice it on steep slopes.\r\n\t\tlightvector = lightminusvertex;\r\n//\t\tlightvector.x = dot(lightminusvertex, v_svector.xyz);\r\n//\t\tlightvector.y = dot(lightminusvertex, v_tvector.xyz);\r\n//\t\tlightvector.z = dot(lightminusvertex, v_normal.xyz);\r\n\r\n//\t\t#if defined(SPECULAR)||defined(OFFSETMAPPING)\r\n//\t\t\tvec3 eyeminusvertex = e_eyepos - v_position.xyz;\r\n//\t\t\teyevector.x = dot(eyeminusvertex, v_svector.xyz);\r\n//\t\t\teyevector.y = dot(eyeminusvertex, v_tvector.xyz);\r\n//\t\t\teyevector.z = dot(eyeminusvertex, v_normal.xyz);\r\n//\t\t#endif\r\n\t\t#if defined(PCF) || defined(SPOT) || defined(CUBE)\r\n\t\t\t//for texture projections/shadowmapping on dlights\r\n\t\t\tvtexprojcoord = (l_cubematrix*vec4(v_position.xyz, 1.0));\r\n\t\t#endif\r\n\t#else\r\n\t\toutp.vc.rgb *= e_lmscale.rgb;\r\n\t#endif\r\n\r\n\treturn outp;\r\n}\r\n#endif\r\n\r\n\r\n\r\n\r\n#ifdef FRAGMENT_SHADER\r\n//four texture passes\r\nsampler s_t0;\r\nsampler s_t1;\r\nsampler s_t2;\r\nsampler s_t3;\r\n\r\n//mix values\r\nsampler s_t4;\r\n\r\n#ifdef PCF\r\n\tuniform sampler2DShadow s_t5;\r\n\t#include \"sys/pcf.h\"\r\n#endif\r\n#ifdef CUBE\r\n\tuniform samplerCube s_t6;\r\n#endif\r\n\r\n#ifdef RTLIGHT\r\n\tuniform float l_lightradius;\r\n\tuniform vec3 l_lightcolour;\r\n\tuniform vec3 l_lightcolourscale;\r\n#endif\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\nfloat4 main (v2f inp) : COLOR\r\n{\r\n\tfloat2 lm = inp.tclm.zw;\r\n\tfloat2 tc = inp.tclm.xy;\r\n\tfloat4 vc = inp.vc;\r\n\r\n\tfloat4 r;\r\n\tfloat4 m = tex2D(s_t4, lm);\r\n\r\n\tr  = tex2D(s_t0, tc)*m.r;\r\n\tr += tex2D(s_t1, tc)*m.g;\r\n\tr += tex2D(s_t2, tc)*m.b;\r\n\tr += tex2D(s_t3, tc)*(1.0 - (m.r + m.g + m.b));\r\n\tr.a = 1.0;\r\n\r\n\t//vertex colours provide a scaler that applies even through rtlights.\r\n\tr *= vc;\r\n\r\n#ifdef RTLIGHT\r\n\tvec3 nl = normalize(lightvector);\r\n\tfloat colorscale = max(1.0 - (dot(lightvector, lightvector)/(l_lightradius*l_lightradius)), 0.0);\r\n\tvec3 diff;\r\n//\t#ifdef BUMP\r\n//\t\tcolorscale *= (l_lightcolourscale.x + l_lightcolourscale.y * max(dot(bumps, nl), 0.0));\r\n//\t#else\r\n\t\tcolorscale *= (l_lightcolourscale.x + l_lightcolourscale.y * max(dot(vec3(0.0, 0.0, 1.0), nl), 0.0));\r\n//\t#endif\r\n\r\n//\t#ifdef SPECULAR\r\n//\t\tvec3 halfdir = normalize(normalize(eyevector) + nl);\r\n//\t\tfloat spec = pow(max(dot(halfdir, bumps), 0.0), 32.0 * specs.a);\r\n//\t\tdiff += l_lightcolourscale.z * spec * specs.rgb;\r\n//\t#endif\r\n\r\n\r\n\r\n\t#if defined(SPOT)\r\n\t\tif (vtexprojcoord.w < 0.0) discard;\r\n\t\tvec2 spot = ((vtexprojcoord.st)/vtexprojcoord.w);\r\n\t\tcolorscale *= 1.0-(dot(spot,spot));\r\n\t#endif\r\n\t#ifdef PCF\r\n\t\tcolorscale *= ShadowmapFilter(s_t5);\r\n\t#endif\r\n\r\n\tr.rgb *= colorscale * l_lightcolour;\r\n\r\n\t#ifdef CUBE\r\n\t\tr.rgb *= textureCube(s_t6, vtexprojcoord.xyz).rgb;\r\n\t#endif\r\n\r\n\tr = fog4additive(r, inp.pos);\r\n#else\r\n\t//lightmap is greyscale in m.a. probably we should just scale the texture mix, but precision errors when editing make me paranoid.\r\n\tr.rgb *= m.aaa;\r\n\tr = fog4(r, inp.depth);\r\n#endif\r\n\treturn r;\r\n}\r\n#endif"
  },
  {
    "path": "engine/shaders/makevulkanblob.c",
    "content": "#include <stdio.h>\r\n#include <stdlib.h>\r\n#include <string.h>\r\n\r\n#include \"../client/quakedef.h\"\r\n#include \"../gl/shader.h\"\r\nvoid dumpprogblob(FILE *out, unsigned char *buf, unsigned int size)\r\n{\r\n\tif (out)\r\n\t{\r\n\t\tfwrite(buf, 1, size, out);\r\n\t\treturn;\r\n\t}\r\n\telse\r\n\t\tout = stdout;\r\n\r\n\tsize_t totallen, i, linelen;\r\n\ttotallen = 0;\r\n\tlinelen = 32;\r\n\tfflush(out);\r\n\tfprintf(out, \"\\\"\");\r\n\tfor (i=0;i<size;i++)\r\n\t{\r\n\t\tfprintf(out, \"\\\\x%02X\",buf[i]);\r\n\t\tif (i % linelen == linelen - 1)\r\n\t\t\tfprintf(out, \"\\\"\\n\\\"\");\r\n\t}\r\n\tfprintf(out, \"\\\"\");\r\n\tfflush(out);\r\n}\r\n\r\nstruct blobheader\r\n{\r\n\tunsigned char blobmagic[4];\t//\\xffSPV\r\n\tunsigned int blobversion;\r\n\tunsigned int defaulttextures;\t//s_diffuse etc flags\r\n\tunsigned int numtextures;\t\t//s_t0 count\r\n\tunsigned int permutations;\t\t//\r\n\r\n\tunsigned int cvarsoffset;\t//double-null terminated string. I,XYZW prefixes\r\n\tunsigned int cvarslength;\r\n\r\n\tunsigned int vertoffset;\r\n\tunsigned int vertlength;\r\n\tunsigned int fragoffset;\r\n\tunsigned int fraglength;\r\n};\r\nvoid generateprogsblob(struct blobheader *prototype, FILE *out, FILE *vert, FILE *frag)\r\n{\r\n\tstruct blobheader *blob;\r\n\tint fraglen, vertlen, blobsize, cvarlen;\r\n\tcvarlen = prototype->cvarslength;\r\n\tcvarlen = (cvarlen + 3) & ~3;\t//round up for padding.\r\n\tfseek(vert, 0, SEEK_END);\r\n\tfseek(frag, 0, SEEK_END);\r\n\tvertlen = ftell(vert);\r\n\tfraglen = ftell(frag);\r\n\tfseek(vert, 0, SEEK_SET);\r\n\tfseek(frag, 0, SEEK_SET);\r\n\tblobsize = sizeof(*blob) + cvarlen + fraglen + vertlen;\r\n\tblob = malloc(blobsize);\r\n\t*blob = *prototype;\r\n\tblob->cvarsoffset = sizeof(*blob);\r\n\tblob->cvarslength = prototype->cvarslength;\t//unpadded length\r\n\tblob->vertoffset = blob->cvarsoffset+cvarlen;\r\n\tblob->vertlength = vertlen;\r\n\tblob->fragoffset = blob->vertoffset+vertlen;\r\n\tblob->fraglength = fraglen;\r\n\tmemcpy((char*)blob+blob->cvarsoffset, (char*)prototype+prototype->cvarsoffset, prototype->cvarslength);\r\n\tfread((char*)blob+blob->vertoffset, blob->vertlength, 1, vert);\r\n\tfread((char*)blob+blob->fragoffset, blob->fraglength, 1, frag);\r\n\tdumpprogblob(out, (unsigned char*)blob, blobsize);\r\n\tfree(blob);\r\n}\r\n\r\n\r\nint generatevulkanblobs(struct blobheader *blob, size_t maxblobsize, const char *glslname, int rayquery)\r\n{\r\n\tchar command[1024];\r\n\tchar tempname[256];\r\n\tchar tempvert[256];\r\n\tchar tempfrag[256];\r\n\tchar incpath[256], *sl;\r\n\tint inheader = 1;\r\n\tint i;\r\n\tunsigned short constid = 256;\t//first few are reserved.\r\n\r\n\tconst char *permutationnames[] =\r\n\t{\r\n\t\t\"BUMP\",\r\n\t\t\"FULLBRIGHT\",\r\n\t\t\"UPPERLOWER\",\r\n\t\t\"REFLECTCUBEMASK\",\r\n\t\t\"SKELETAL\",\r\n\t\t\"FOG\",\r\n\t\t\"FRAMEBLEND\",\r\n\t\t\"LIGHTSTYLED\",\r\n\t\tNULL\r\n\t};\r\n\r\n\tconst char *tmppath = \"/tmp/\";\r\n\r\n\tchar customsamplerlines[16][256];\r\n\tFILE *glsl, *temp;\r\n\r\n\tsnprintf(tempname, sizeof(tempname), \"%stemp.tmp\", tmppath);\r\n\tsnprintf(tempvert, sizeof(tempvert), \"%stemp.vert\", tmppath);\r\n\tsnprintf(tempfrag, sizeof(tempfrag), \"%stemp.frag\", tmppath);\r\n\r\n\tmemcpy(incpath, glslname, sizeof(incpath));\r\n\tif ((sl = strrchr(incpath, '/')))\r\n\t\tsl[1] = 0;\r\n\telse if ((sl = strrchr(incpath, '\\\\')))\t//in case someone is on crappy windows.\r\n\t\tsl[1] = 0;\r\n\r\n\tmemcpy(blob->blobmagic, \"\\xffSPV\", 4);\r\n\tblob->blobversion = 1;\r\n\tblob->defaulttextures = 0;\r\n\tblob->numtextures = 0;\r\n\tblob->permutations = 0;\r\n\tblob->cvarsoffset = sizeof(*blob);\r\n\tblob->cvarslength = 0;\r\n\r\n\tif (!strncmp(glslname, \"rq_\", 3))\r\n\t{\t//hack, to avoid copypasta\r\n\t\trayquery = 2;\r\n\t\tglsl = fopen(glslname+3, \"rt\");\r\n\t}\r\n\telse\r\n\t\tglsl = fopen(glslname, \"rt\");\r\n\tif (!glsl)\r\n\t{\r\n\t\tprintf(\"Unable to read %s\\n\", glslname);\r\n\t\treturn 0;\r\n\t}\r\n\ttemp = fopen(tempname, \"wt\");\r\n\tif (!temp)\r\n\t\tprintf(\"Unable to write %s\\n\", tempname);\r\n\twhile(fgets(command, sizeof(command), glsl))\r\n\t{\r\n\t\tif (inheader && !strncmp(command, \"!!\", 2))\r\n\t\t{\r\n\t\t\tif (!strncmp(command, \"!!cvar\", 6) || !strncmp(command, \"!!arg\", 5))\r\n\t\t\t{\r\n\t\t\t\tunsigned int type;\r\n\t\t\t\tunsigned int size;\r\n\t\t\t\tunion\r\n\t\t\t\t{\r\n\t\t\t\t\tfloat f;\r\n\t\t\t\t\tunsigned int u;\r\n\t\t\t\t} u[4];\r\n\t\t\t\tchar *arg;\r\n\t\t\t\tunsigned char *cb = (unsigned char*)blob + blob->cvarsoffset + blob->cvarslength;\r\n\r\n\t\t\t\tif (command[2] == 'a')\r\n\t\t\t\t{\r\n\t\t\t\t\ttype = command[5] == 'i' || command[5] == 'f' || command[5] == 'b';\r\n\t\t\t\t\tsize = type?1:(command[5]-'0');\r\n\t\t\t\t\targ = strtok(command+7, \" ,=\\r\\n\");\r\n\t\t\t\t\ttype = command[6-type] - 'a' + 'A';\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\ttype = command[6] == 'i' || command[6] == 'f' || command[6] == 'b';\r\n\t\t\t\t\tsize = type?1:(command[6]-'0');\r\n\t\t\t\t\targ = strtok(command+8, \" ,=\\r\\n\");\r\n\t\t\t\t\ttype = command[7-type];\r\n\t\t\t\t}\r\n\r\n\t\t\t\tcb[0] = (constid>>8)&0xff;\r\n\t\t\t\tcb[1] = (constid>>0)&0xff;\r\n\t\t\t\tcb[2] = type;\r\n\t\t\t\tcb[3] = size + '0';\r\n\t\t\t\tcb += 4;\r\n\t\t\t\twhile(*arg)\r\n\t\t\t\t\t*cb++ = *arg++;\r\n\t\t\t\t*cb++ = 0;\r\n\r\n\t\t\t\tfor (i = 0; i < size; i++)\r\n\t\t\t\t{\r\n\t\t\t\t\tif (arg)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\targ = strtok(NULL, \" ,=\\r\\n\");\r\n\t\t\t\t\t\tif (!arg)\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tprintf(\" cvar %s has no default value. Assuming 0\\n\",  (char*)blob + blob->cvarsoffset + blob->cvarslength+4);\r\n\t\t\t\t\t\t\tu[i].u = 0;\t//0 either way.\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\telse if (type == 'f' || type == 'F')\r\n\t\t\t\t\t\t\tu[i].f = atof(arg);\r\n\t\t\t\t\t\telse\r\n\t\t\t\t\t\t\tu[i].u = atoi(arg);\r\n\t\t\t\t\t}\r\n\t\t\t\t\telse\r\n\t\t\t\t\t\tu[i].u = 0;\r\n\t\t\t\t\t*cb++ = (u[i].u>>24)&0xff;\r\n\t\t\t\t\t*cb++ = (u[i].u>>16)&0xff;\r\n\t\t\t\t\t*cb++ = (u[i].u>>8)&0xff;\r\n\t\t\t\t\t*cb++ = (u[i].u>>0)&0xff;\r\n\t\t\t\t}\r\n\t\t\t\tblob->cvarslength = cb - ((unsigned char*)blob + blob->cvarsoffset);\r\n\t\t\t\tconstid += size;\r\n\t\t\t}\r\n\t\t\telse if (!strncmp(command, \"!!tess\", 6))\r\n\t\t\t\tprintf(\"!!tess not supported\\n\");\r\n\t\t\telse if (!strncmp(command, \"!!geom\", 6))\r\n\t\t\t\tprintf(\"!!geom not supported\\n\");\r\n\t\t\telse if (!strncmp(command, \"!!rayquery\", 6))\r\n\t\t\t\trayquery = true;\r\n\t\t\telse if (!strncmp(command, \"!!permu\", 7))\r\n\t\t\t{\r\n\t\t\t\tchar *arg = strtok(command+7, \" ,\\r\\n\");\r\n\t\t\t\tfor (i = 0; permutationnames[i]; i++)\r\n\t\t\t\t{\r\n\t\t\t\t\tif (!strcmp(arg, permutationnames[i]))\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tblob->permutations |= 1u<<i;\r\n\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t\tif (!permutationnames[i])\r\n\t\t\t\t{\r\n\t\t\t\t\tprintf(\"Unknown permutation: \\\"%s\\\"\\n\", arg);\r\n\t\t\t\t\tfor (i = 0; permutationnames[i]; i++)\r\n\t\t\t\t\t\tprintf(\"%s \", permutationnames[i]);\r\n\t\t\t\t\tprintf(\"\\n\");\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\telse if (!strncmp(command, \"!!samps\", 7))\r\n\t\t\t{\r\n\t\t\t\tchar *arg = strtok(command+7, \" ,\\r\\n\");\r\n\t\t\t\tdo\r\n\t\t\t\t{\r\n\t\t\t\t\t//light\r\n\t\t\t\t\tif (!strcasecmp(arg, \"shadowmap\"))\r\n\t\t\t\t\t\tblob->defaulttextures |= 1u<<S_SHADOWMAP;\r\n\t\t\t\t\telse if (!strcasecmp(arg, \"projectionmap\"))\r\n\t\t\t\t\t\tblob->defaulttextures |= 1u<<S_PROJECTIONMAP;\r\n\r\n\t\t\t\t\t//material\r\n\t\t\t\t\telse if (!strcasecmp(arg, \"diffuse\"))\r\n\t\t\t\t\t\tblob->defaulttextures |= 1u<<S_DIFFUSE;\r\n\t\t\t\t\telse if (!strcasecmp(arg, \"normalmap\"))\r\n\t\t\t\t\t\tblob->defaulttextures |= 1u<<S_NORMALMAP;\r\n\t\t\t\t\telse if (!strcasecmp(arg, \"specular\"))\r\n\t\t\t\t\t\tblob->defaulttextures |= 1u<<S_SPECULAR;\r\n\t\t\t\t\telse if (!strcasecmp(arg, \"upper\"))\r\n\t\t\t\t\t\tblob->defaulttextures |= 1u<<S_UPPERMAP;\r\n\t\t\t\t\telse if (!strcasecmp(arg, \"lower\"))\r\n\t\t\t\t\t\tblob->defaulttextures |= 1u<<S_LOWERMAP;\r\n\t\t\t\t\telse if (!strcasecmp(arg, \"fullbright\"))\r\n\t\t\t\t\t\tblob->defaulttextures |= 1u<<S_FULLBRIGHT;\r\n\t\t\t\t\telse if (!strcasecmp(arg, \"paletted\"))\r\n\t\t\t\t\t\tblob->defaulttextures |= 1u<<S_PALETTED;\r\n\t\t\t\t\telse if (!strcasecmp(arg, \"reflectcube\"))\r\n\t\t\t\t\t\tblob->defaulttextures |= 1u<<S_REFLECTCUBE;\r\n\t\t\t\t\telse if (!strcasecmp(arg, \"reflectmask\"))\r\n\t\t\t\t\t\tblob->defaulttextures |= 1u<<S_REFLECTMASK;\r\n\t\t\t\t\telse if (!strcasecmp(arg, \"displacement\"))\r\n\t\t\t\t\t\tblob->defaulttextures |= 1u<<S_DISPLACEMENT;\r\n\t\t\t\t\telse if (!strcasecmp(arg, \"occlusion\"))\r\n\t\t\t\t\t\tblob->defaulttextures |= 1u<<S_OCCLUSION;\r\n\t\t\t\t\telse if (!strcasecmp(arg, \"transmission\"))\r\n\t\t\t\t\t\tblob->defaulttextures |= 1u<<S_TRANSMISSION;\r\n\t\t\t\t\telse if (!strcasecmp(arg, \"thickness\"))\r\n\t\t\t\t\t\tblob->defaulttextures |= 1u<<S_THICKNESS;\r\n\r\n\t\t\t\t\t//batch\r\n\t\t\t\t\telse if (!strcasecmp(arg, \"lightmap\"))\r\n\t\t\t\t\t\tblob->defaulttextures |= 1u<<S_LIGHTMAP0;\r\n\t\t\t\t\telse if (!strcasecmp(arg, \"deluxemap\") || !strcasecmp(arg, \"deluxmap\"))\r\n\t\t\t\t\t\tblob->defaulttextures |= 1u<<S_DELUXEMAP0;\r\n\t\t\t\t\telse if (!strcasecmp(arg, \"lightmaps\"))\r\n\t\t\t\t\t\tblob->defaulttextures |= 1u<<S_LIGHTMAP0 | 1u<<S_LIGHTMAP1 | 1u<<S_LIGHTMAP2 | 1u<<S_LIGHTMAP3;\r\n\t\t\t\t\telse if (!strcasecmp(arg, \"deluxemaps\") || !strcasecmp(arg, \"deluxmaps\"))\r\n\t\t\t\t\t\tblob->defaulttextures |= 1u<<S_DELUXEMAP0 | 1u<<S_DELUXEMAP1 | 1u<<S_DELUXEMAP2 | 1u<<S_DELUXEMAP3;\r\n\r\n\t\t\t\t\t//shader pass\r\n\t\t\t\t\telse if ((i=atoi(arg)))\r\n\t\t\t\t\t{\t//legacy\r\n\t\t\t\t\t\tif (blob->numtextures < i)\r\n\t\t\t\t\t\t\tblob->numtextures = i;\r\n\t\t\t\t\t}\r\n\t\t\t\t\telse\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tchar *eq = strrchr(arg, '=');\r\n\t\t\t\t\t\tif (eq)\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tchar *type = strrchr(arg, ':');\r\n\t\t\t\t\t\t\tint pass = atoi(eq+1);\r\n\t\t\t\t\t\t\t*eq = 0;\r\n\t\t\t\t\t\t\tif (type)\r\n\t\t\t\t\t\t\t\t*type++ = 0;\r\n\t\t\t\t\t\t\telse\r\n\t\t\t\t\t\t\t\ttype = \"2D\";\r\n\t\t\t\t\t\t\tif (pass < 16)\r\n\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\tsnprintf(customsamplerlines[pass], sizeof(customsamplerlines[pass]), \"uniform sampler%s s_%s;\\n\", type, arg);\r\n\t\t\t\t\t\t\t\tif (blob->numtextures < ++pass)\r\n\t\t\t\t\t\t\t\t\tblob->numtextures = pass;\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\telse printf(\"sampler binding too high:   %s:%s=%i\\n\", arg, type, pass);\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\telse\r\n\t\t\t\t\t\t\tprintf(\"Unknown texture: \\\"%s\\\"\\n\", arg);\r\n\t\t\t\t\t}\r\n\t\t\t\t} while((arg = strtok(NULL, \" ,\\r\\n\")));\r\n\t\t\t}\r\n\t\t\tcontinue;\r\n\t\t}\r\n\t\telse if (inheader && !strncmp(command, \"//\", 2))\r\n\t\t\tcontinue;\r\n\t\telse if (inheader)\r\n\t\t{\r\n\t\t\tconst char *specialnames[] =\r\n\t\t\t{\r\n\t\t\t\t//light\r\n\t\t\t\t\"uniform sampler2DShadow s_shadowmap;\\n\",\r\n\t\t\t\t\"uniform samplerCube s_projectionmap;\\n\",\r\n\r\n\t\t\t\t//material\r\n\t\t\t\t\"uniform sampler2D s_diffuse;\\n\",\r\n\t\t\t\t\"uniform sampler2D s_normalmap;\\n\",\r\n\t\t\t\t\"uniform sampler2D s_specular;\\n\",\r\n\t\t\t\t\"uniform sampler2D s_upper;\\n\",\r\n\t\t\t\t\"uniform sampler2D s_lower;\\n\",\r\n\t\t\t\t\"uniform sampler2D s_fullbright;\\n\",\r\n\t\t\t\t\"uniform sampler2D s_paletted;\\n\",\r\n\t\t\t\t\"uniform samplerCube s_reflectcube;\\n\",\r\n\t\t\t\t\"uniform sampler2D s_reflectmask;\\n\",\r\n\t\t\t\t\"uniform sampler2D s_displacement;\\n\",\r\n\t\t\t\t\"uniform sampler2D s_occlusion;\\n\",\r\n\t\t\t\t\"uniform sampler2D s_transmission;\\n\",\r\n\t\t\t\t\"uniform sampler2D s_thickness;\\n\",\r\n\r\n\t\t\t\t//batch\r\n\t\t\t\t\"uniform sampler2D s_lightmap;\\n#define s_lightmap0 s_lightmap\\n\",\r\n\t\t\t\t\"uniform sampler2D s_deluxemap;\\n#define s_deluxemap0 s_deluxemap\\n\",\r\n\t\t\t\t\"uniform sampler2D s_lightmap1;\\n\",\r\n\t\t\t\t\"uniform sampler2D s_lightmap2;\\n\",\r\n\t\t\t\t\"uniform sampler2D s_lightmap3;\\n\",\r\n\t\t\t\t\"uniform sampler2D s_deluxmap1;\\n\",\r\n\t\t\t\t\"uniform sampler2D s_deluxmap2;\\n\",\r\n\t\t\t\t\"uniform sampler2D s_deluxmap3;\\n\"\r\n\t\t\t};\r\n\t\t\tint binding = 2;\t//defined in sys/defs.h\r\n\t\t\tinheader = 0;\r\n\t\t\tif (rayquery == 2)\r\n\t\t\t\tblob->defaulttextures &= ~(1u<<S_SHADOWMAP);\t//part of the earlier hack.\r\n\t\t\tif (rayquery)\r\n\t\t\t\tfprintf(temp, \"#define RAY_QUERY\\n\");\r\n\t\t\tfprintf(temp, \"#define s_deluxmap s_deluxemap\\n\");\r\n\t\t\tfprintf(temp, \"#define OFFSETMAPPING (cvar_r_glsl_offsetmapping>0)\\n\");\r\n\t\t\tfprintf(temp, \"#define SPECULAR (cvar_gl_specular>0)\\n\");\r\n\t\t\tfprintf(temp, \"#ifdef FRAGMENT_SHADER\\n\");\r\n\t\t\tif (rayquery)\r\n\t\t\t\tfprintf(temp, \"layout(set=0, binding=%u) uniform accelerationStructureEXT toplevelaccel;\\n\", binding++);\r\n\t\t\tfor (i = 0; i < sizeof(specialnames)/sizeof(specialnames[0]); i++)\r\n\t\t\t{\r\n\t\t\t\tif (blob->defaulttextures & (1u<<i))\r\n\t\t\t\t\tfprintf(temp, \"layout(set=0, binding=%u) %s\", binding++, specialnames[i]);\r\n\t\t\t}\r\n\t\t\tfor (i = 0; i < blob->numtextures; i++)\r\n\t\t\t{\r\n\t\t\t\tfprintf(temp, \"layout(set=0, binding=%u) \", binding++);\r\n\t\t\t\tif (*customsamplerlines[i])\r\n\t\t\t\t\tfprintf(temp, \"%s\", customsamplerlines[i]);\r\n\t\t\t\telse\r\n\t\t\t\t\tfprintf(temp, \"uniform sampler2D s_t%u;\\n\", i);\r\n\t\t\t}\r\n\t\t\tfprintf(temp, \"#endif\\n\");\r\n\r\n\t\t\t//cvar specialisation constants\r\n\t\t\t{\r\n\t\t\t\tunsigned char *cb = (unsigned char*)blob + blob->cvarsoffset;\r\n\t\t\t\twhile (cb < (unsigned char*)blob + blob->cvarsoffset + blob->cvarslength)\r\n\t\t\t\t{\r\n\t\t\t\t\tunion\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tfloat f;\r\n\t\t\t\t\t\tunsigned int u;\r\n\t\t\t\t\t} u[4];\r\n\t\t\t\t\tunsigned short id;\r\n\t\t\t\t\tunsigned char type;\r\n\t\t\t\t\tunsigned char size;\r\n\t\t\t\t\tchar *name;\r\n\t\t\t\t\tid = *cb++<<8;\r\n\t\t\t\t\tid |= *cb++;\r\n\t\t\t\t\ttype = *cb++;\r\n\t\t\t\t\tsize = (*cb++)-'0';\r\n\t\t\t\t\tname = cb;\r\n\t\t\t\t\tcb += strlen(name)+1;\r\n\t\t\t\t\tfor (i = 0; i < size; i++)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tu[i].u = (cb[0]<<24)|(cb[1]<<16)|(cb[2]<<8)|(cb[3]<<0);\r\n\t\t\t\t\t\tcb+=4;\r\n\t\t\t\t\t}\r\n#if 0 //all is well\r\n\t\t\t\t\tif (size == 1 && type == 'b')\r\n\t\t\t\t\t\tfprintf(temp, \"layout(constant_id=%u) const bool cvar_%s = %s;\\n\", id, name, (int)u[0].u?\"true\":\"false\");\r\n\t\t\t\t\telse if (size == 1 && type == 'i')\r\n\t\t\t\t\t\tfprintf(temp, \"layout(constant_id=%u) const int cvar_%s = %i;\\n\", id, name, (int)u[0].u);\r\n\t\t\t\t\telse if (size == 1 && type == 'f')\r\n\t\t\t\t\t\tfprintf(temp, \"layout(constant_id=%u) const float cvar_%s = %f;\\n\", id, name, u[0].f);\r\n\t\t\t\t\telse if (size == 3 && type == 'f')\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tfprintf(temp, \"layout(constant_id=%u) const float cvar_%s_x = %f;\\n\", id+0, name, u[0].f);\r\n\t\t\t\t\t\tfprintf(temp, \"layout(constant_id=%u) const float cvar_%s_y = %f;\\n\", id+1, name, u[1].f);\r\n\t\t\t\t\t\tfprintf(temp, \"layout(constant_id=%u) const float cvar_%s_z = %f;\\n\", id+2, name, u[2].f);\r\n\t\t\t\t\t\tfprintf(temp, \"vec3 cvar_%s = vec3(cvar_%s_x, cvar_%s_y, cvar_%s_z);\\n\", name, name, name, name);\r\n\t\t\t\t\t}\r\n\t\t\t\t\telse \tif (size == 1 && type == 'B')\r\n\t\t\t\t\t\tfprintf(temp, \"layout(constant_id=%u) const bool arg_%s = %s;\\n\", id, name, (int)u[0].u?\"true\":\"false\");\r\n\t\t\t\t\telse \tif (size == 1 && type == 'I')\r\n\t\t\t\t\t\tfprintf(temp, \"layout(constant_id=%u) const int arg_%s = %i;\\n\", id, name, (int)u[0].u);\r\n\t\t\t\t\telse if (size == 1 && type == 'F')\r\n\t\t\t\t\t\tfprintf(temp, \"layout(constant_id=%u) const float arg_%s = %i;\\n\", id, name, u[0].f);\r\n\t\t\t\t\telse if (size == 3 && type == 'F')\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tfprintf(temp, \"layout(constant_id=%u) const float arg_%s_x = %f;\\n\", id+0, name, u[0].f);\r\n\t\t\t\t\t\tfprintf(temp, \"layout(constant_id=%u) const float arg_%s_y = %f;\\n\", id+1, name, u[1].f);\r\n\t\t\t\t\t\tfprintf(temp, \"layout(constant_id=%u) const float arg_%s_z = %f;\\n\", id+2, name, u[2].f);\r\n\t\t\t\t\t\tfprintf(temp, \"vec3 arg_%s = vec3(arg_%s_x, arg_%s_y, arg_%s_z);\\n\", name, name, name, name);\r\n\t\t\t\t\t}\r\n#else\r\n\t\t\t\t\t//these initialised values are fucked up because glslangvalidator's spirv generator is fucked up and folds specialisation constants.\r\n\t\t\t\t\t//we get around this by ensuring that all such constants are given unique values to prevent them being folded, with the engine overriding everything explicitly.\r\n\t\t\t\t\tif (size == 1 && type == 'b')\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tfprintf(temp, \"layout(constant_id=%u) const int _cvar_%s = %i;\\n\", id, name, id);//(int)u[0].u?\"true\":\"false\");\r\n\t\t\t\t\t\tfprintf(temp, \"#define cvar_%s (_cvar_%s!=0)\\n\", name, name);\r\n\t\t\t\t\t}\r\n\t\t\t\t\telse if (size == 1 && type == 'i')\r\n\t\t\t\t\t\tfprintf(temp, \"layout(constant_id=%u) const int cvar_%s = %i;\\n\", id, name, id);//(int)u[0].u);\r\n\t\t\t\t\telse if (size == 1 && type == 'f')\r\n\t\t\t\t\t\tfprintf(temp, \"layout(constant_id=%u) const float cvar_%s = %i;\\n\", id, name, id);//u[0].f);\r\n\t\t\t\t\telse if (size == 3 && type == 'f')\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tfprintf(temp, \"layout(constant_id=%u) const float cvar_%s_x = %i;\\n\", id+0, name, id+0);//u[0].f);\r\n\t\t\t\t\t\tfprintf(temp, \"layout(constant_id=%u) const float cvar_%s_y = %i;\\n\", id+1, name, id+1);//u[1].f);\r\n\t\t\t\t\t\tfprintf(temp, \"layout(constant_id=%u) const float cvar_%s_z = %i;\\n\", id+2, name, id+2);//u[2].f);\r\n\t\t\t\t\t\tfprintf(temp, \"vec3 cvar_%s = vec3(cvar_%s_x, cvar_%s_y, cvar_%s_z);\\n\", name, name, name, name);\r\n\t\t\t\t\t}\r\n\t\t\t\t\telse \tif (size == 1 && type == 'B')\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tfprintf(temp, \"layout(constant_id=%u) const int _arg_%s = %i;\\n\", id, name, id);//(int)u[0].u?\"true\":\"false\");\r\n\t\t\t\t\t\tfprintf(temp, \"#define arg_%s (_arg_%s!=0)\\n\", name, name);\r\n\t\t\t\t\t}\r\n\t\t\t\t\telse \tif (size == 1 && type == 'I')\r\n\t\t\t\t\t\tfprintf(temp, \"layout(constant_id=%u) const int arg_%s = %i;\\n\", id, name, id);//(int)u[0].u);\r\n\t\t\t\t\telse if (size == 1 && type == 'F')\r\n\t\t\t\t\t\tfprintf(temp, \"layout(constant_id=%u) const float arg_%s = %i;\\n\", id, name, id);//u[0].f);\r\n\t\t\t\t\telse if (size == 3 && type == 'F')\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tfprintf(temp, \"layout(constant_id=%u) const float arg_%s_x = %i;\\n\", id+0, name, id+0);//u[0].f);\r\n\t\t\t\t\t\tfprintf(temp, \"layout(constant_id=%u) const float arg_%s_y = %i;\\n\", id+1, name, id+1);//u[1].f);\r\n\t\t\t\t\t\tfprintf(temp, \"layout(constant_id=%u) const float arg_%s_z = %i;\\n\", id+2, name, id+2);//u[2].f);\r\n\t\t\t\t\t\tfprintf(temp, \"vec3 arg_%s = vec3(arg_%s_x, arg_%s_y, arg_%s_z);\\n\", name, name, name, name);\r\n\t\t\t\t\t}\r\n#endif\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\t//permutation stuff\r\n\t\t\tfor (i = 0; i < sizeof(specialnames)/sizeof(specialnames[0]); i++)\r\n\t\t\t{\r\n\t\t\t\tif (blob->permutations & (1<<i))\r\n\t\t\t\t{\r\n#if 0 //all is well\r\n\t\t\t\t\tfprintf(temp, \"layout(constant_id=%u) const bool %s = %s;\\n\", 16+i, permutationnames[i], \"false\");\r\n#else\r\n\t\t\t\t\tfprintf(temp, \"layout(constant_id=%u) const int _%s = %i;\\n\", 16+i, permutationnames[i], 16+i);\r\n\t\t\t\t\tfprintf(temp, \"#define %s (_%s!=0)\\n\", permutationnames[i], permutationnames[i]);\r\n#endif\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t\tfputs(command, temp);\r\n\t}\r\n\tfclose(temp);\r\n\tfclose(glsl);\r\n\r\n\ttemp = fopen(tempvert, \"wt\");\r\n\tfprintf(temp, \"#version 460 core\\n\");\r\n\tfclose(temp);\r\n\r\n\ttemp = fopen(tempfrag, \"wt\");\r\n\tfprintf(temp, \"#version 460 core\\n\");\r\n\tif (rayquery)\r\n\t\tfprintf(temp, \"#extension GL_EXT_ray_query : require\\n\");\r\n\tfclose(temp);\r\n\r\n\tsnprintf(command, sizeof(command),\r\n\t\t/*preprocess the vertex shader*/\r\n\t\t\"cpp %s -I%s -DVULKAN -DVERTEX_SHADER -P >> %s && \"\r\n\r\n\t\t/*preprocess the fragment shader*/\r\n\t\t\"cpp %s -I%s -DVULKAN -DFRAGMENT_SHADER -P >> %s && \"\r\n\r\n\t\t/*convert to spir-v (annoyingly we have no control over the output file names*/\r\n\t\t\"glslangValidator -g0 -V -l -d %s %s\"\r\n\r\n\t\t/*strip stuff out, so drivers don't glitch out from stuff that we don't use*/\r\n//\t\t\" && spirv-remap -i vert.spv frag.spv -o vulkan/remap\"\r\n\r\n\t\t,tempname, incpath, tempvert\t//vertex shader args\r\n\t\t,tempname, incpath, tempfrag\t//fragment shader args\r\n\t\t,tempvert, tempfrag);\t\t\t//compile/link args.\r\n\r\n\tsystem(command);\r\n\r\n//\tremove(tempname);\r\n//\tremove(tempvert);\r\n//\tremove(tempfrag);\r\n\r\n\tif (rayquery)\r\n\t\tblob->permutations |= 1u<<31;\r\n\treturn 1;\r\n}\r\n\r\nint main(int argc, const char **argv)\r\n{\r\n\tconst char *inname = argv[1];\r\n\tconst char *blobname = argv[2];\r\n\tFILE *v, *f, *o;\r\n\tchar proto[8192];\r\n\tchar line[256];\r\n\tint rayquery = (argc>=4)?!!strstr(argv[3], \"rq\"):0;\r\n\tint r = 1;\r\n\r\n\tif (argc == 1)\r\n\t{\r\n\t\tprintf(\"%s input.glsl output.fvb\\n\", argv[0]);\r\n\t\treturn 1;\r\n\t}\r\n\r\n\tif (!generatevulkanblobs((struct blobheader*)proto, sizeof(proto), inname, rayquery))\r\n\t\treturn 1;\r\n\t//should have generated two files\r\n\tv = fopen(\"vert.spv\", \"rb\");\r\n\tf = fopen(\"frag.spv\", \"rb\");\r\n\tif (f && v)\r\n\t{\r\n\t\tif (argc < 3)\r\n\t\t{\r\n\t\t\tgenerateprogsblob((struct blobheader*)proto, NULL, v, f);\r\n\t\t\tr = 0;\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\to = fopen(blobname, \"wb\");\r\n\t\t\tif (o)\r\n\t\t\t{\r\n\t\t\t\tgenerateprogsblob((struct blobheader*)proto, o, v, f);\r\n\t\t\t\tfclose(o);\r\n\t\t\t\tr = 0;\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t\tprintf(\"Unable to write blob %s\\n\", blobname);\r\n\t\t}\r\n\t}\r\n\tif (f)\r\n\t\tfclose(f);\r\n\telse\r\n\t\tprintf(\"Unable to read frag.spv\\n\");\r\n\tif (v)\r\n\t\tfclose(v);\r\n\telse\r\n\t\tprintf(\"Unable to read vert.spv\\n\");\r\n\tremove(\"vert.spv\");\r\n\tremove(\"frag.spv\");\r\n\r\n\treturn r;\r\n}\r\n"
  },
  {
    "path": "engine/shaders/vulkan/altwater.glsl",
    "content": "!!permu FOG\r\n!!cvarb r_fog_exp2=true\r\n!!cvarb r_fog_linear=false\r\n!!cvarf r_glsl_turbscale_reflect=1\r\n!!cvarf r_glsl_turbscale_refract=1\r\n!!cvarf gl_maxdist=8192\r\n!!cvarf gl_mindist=4\r\n!!samps normalmap diffuse 4\r\n!!argb reflect=0\t\t//s_t1 is a reflection instead of diffusemap\r\n!!argf strength_refl=0.1\t//0.1 = fairly gentle, 0.2 = big waves\r\n!!argf strength_refr=0.1\t//0.1 = fairly gentle, 0.2 = big waves\r\n!!argf fresnel_exp=5.0\t//water should be around 5\r\n!!argf fresnel_range=1.0\r\n!!argf fresnel_min=1.0\r\n!!argf txscale1=0.2\t\t//wave strength\r\n!!argf txscale2=0.2\t\t//wave strength\r\n!!argb ripplemap=0\t\t//s_t2 contains a ripplemap\r\n!!arg3f tint_refr=0.7 0.8 0.7\t//some colour value\r\n!!arg3f tint_refl=0.7 0.8 0.7\t//some colour value\r\n!!argb depth=0\t\t\t//s_t3 is a depth image\r\n!!argf alpha=0\t\t\t//blend in the alpha channel\r\n!!arg3f fogtint=0.2 0.3 0.2 //tints as it gets deeper\r\n\r\n\r\n#include \"sys/defs.h\"\r\n#include \"sys/fog.h\"\r\n\r\nlayout(location=0) varying vec2 tc;\r\nlayout(location=1) varying vec4 tf;\r\nlayout(location=2) varying vec3 norm;\r\nlayout(location=3) varying vec3 eye;\r\n#ifdef VERTEX_SHADER\r\nvoid main (void)\r\n{\r\n\ttc = v_texcoord.st;\r\n\ttf = ftetransform();\r\n\tnorm = v_normal;\r\n\teye = e_eyepos - v_position.xyz;\r\n\tgl_Position = tf;\r\n}\r\n#endif\r\n#ifdef FRAGMENT_SHADER\r\n#define s_refract s_t0\r\n#define s_reflect s_t1\r\n#define s_ripplemap s_t2\r\n#define s_refractdepth s_t3\r\n\r\nvoid main (void)\r\n{\r\n\tvec2 stc, ntc;\r\n\tvec3 n, refr, refl;\r\n\tfloat fres;\r\n\tfloat depth;\r\n\tstc = (1.0 + (tf.xy / tf.w)) * 0.5;\r\n\t//hack the texture coords slightly so that there are no obvious gaps\r\n\tstc.t -= 1.5*norm.z/1080.0;\r\n\r\n\t//apply q1-style warp, just for kicks\r\n\tntc.s = tc.s + sin(tc.t+e_time)*0.125;\r\n\tntc.t = tc.t + sin(tc.s+e_time)*0.125;\r\n\r\n\t//generate the two wave patterns from the normalmap\r\n\tn = (texture2D(s_normalmap, arg_txscale1*tc + vec2(e_time*0.1, 0.0)).xyz);\r\n\tn += (texture2D(s_normalmap, arg_txscale2*tc - vec2(0, e_time*0.097)).xyz);\r\n\tn -= 1.0 - 4.0/256.0;\r\n\r\n\tif (arg_ripplemap)\r\n\t\tn += texture2D(s_ripplemap, stc).rgb*3.0;\r\n\tn = normalize(n);\r\n\r\n\t//the fresnel term decides how transparent the water should be\r\n\tfres = pow(1.0-abs(dot(n, normalize(eye))), arg_fresnel_exp) * arg_fresnel_range + arg_fresnel_min;\r\n\r\n\tif (arg_depth)\r\n\t{\r\n\t\tfloat far = cvar_gl_maxdist;\r\n\t\tfloat near = cvar_gl_mindist;\r\n\t\t//get depth value at the surface\r\n\t\tfloat sdepth = gl_FragCoord.z;\r\n\t\tsdepth = (2.0*near) / (far + near - sdepth * (far - near));\r\n\t\tsdepth = mix(near, far, sdepth);\r\n\r\n\t\t//get depth value at the ground beyond the surface.\r\n\t\tfloat gdepth = texture2D(s_refractdepth, stc).x;\r\n\t\tgdepth = (2.0*near) / (far + near - gdepth * (far - near));\r\n\t\tif (gdepth >= 0.5)\r\n\t\t{\r\n\t\t\tgdepth = sdepth;\r\n\t\t\tdepth = 0.0;\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tgdepth = mix(near, far, gdepth);\r\n\t\t\tdepth = gdepth - sdepth;\r\n\t\t}\r\n\r\n\t\t//reduce the normals in shallow water (near walls, reduces the pain of linear sampling)\r\n\t\tif (depth < 100.0)\r\n\t\t\tn *= depth/100.0;\r\n\t}\r\n\telse\r\n\t\tdepth = 1.0; \r\n\r\n\r\n\t//refraction image (and water fog, if possible)\r\n\trefr = texture2D(s_refract, stc + n.st*arg_strength_refr*cvar_r_glsl_turbscale_refract).rgb * arg_tint_refr;\r\n\tif (arg_depth)\r\n\t\trefr = mix(refr, arg_fogtint, min(depth/4096.0, 1.0));\r\n\r\n\t//reflection/diffuse\r\n\tif (arg_reflect)\r\n\t\trefl = texture2D(s_reflect, stc - n.st*arg_strength_refl*cvar_r_glsl_turbscale_reflect).rgb * arg_tint_refl;\r\n\telse\r\n\t\trefl = texture2D(s_diffuse, ntc).xyz;\r\n\r\n\t//FIXME: add specular tints\r\n\r\n\t//interplate by fresnel\r\n\trefr = mix(refr, refl, fres);\r\n\r\n\tif (arg_alpha != 0)\r\n\t{\r\n\t\tvec4 ts = texture2D(s_diffuse, ntc);\r\n\t\tvec4 surf = fog4blend(vec4(ts.rgb, arg_alpha*ts.a));\r\n\t\trefr = mix(refr, surf.rgb, surf.a);\r\n\t}\r\n\r\n\t//done\r\n\tgl_FragColor = vec4(refr, 1.0);\r\n}\r\n#endif\r\n"
  },
  {
    "path": "engine/shaders/vulkan/bloom_blur.glsl",
    "content": "!!samps 1\r\n#include \"sys/defs.h\"\r\n//apply gaussian filter\r\n\r\nlayout(location=0) varying vec2 tc;\r\n\r\n#ifdef VERTEX_SHADER\r\nvoid main ()\r\n{\r\n\ttc = v_texcoord;\r\n\tgl_Position = ftetransform();\r\n}\r\n#endif\r\n#ifdef FRAGMENT_SHADER\r\n/*offset should be 1.2 pixels away from the center*/\r\nvoid main ()\r\n{\r\n\tgl_FragColor =\r\n\t\t0.3125 * texture2D(s_t0, tc - e_glowmod.st) +\r\n\t\t0.375 * texture2D(s_t0, tc) +\r\n\t\t0.3125 * texture2D(s_t0, tc + e_glowmod.st);\r\n}\r\n#endif\r\n"
  },
  {
    "path": "engine/shaders/vulkan/bloom_filter.glsl",
    "content": "!!samps 1\r\n!!cvar3f r_bloom_filter=.7,.7,.7\r\n#include \"sys/defs.h\"\r\n//the bloom filter\r\n//filter out any texels which are not to bloom\r\n\r\nlayout(location=0) varying vec2 tc;\r\n\r\n#ifdef VERTEX_SHADER\r\nvoid main ()\r\n{\r\n\ttc = v_texcoord;\r\n\tgl_Position = ftetransform();\r\n}\r\n#endif\r\n#ifdef FRAGMENT_SHADER\r\nvoid main ()\r\n{\r\n\tgl_FragColor.rgb = (texture2D(s_t0, tc).rgb - cvar_r_bloom_filter)/(1.0-cvar_r_bloom_filter);\r\n}\r\n#endif\r\n"
  },
  {
    "path": "engine/shaders/vulkan/bloom_final.glsl",
    "content": "!!samps 4\r\n!!cvarf r_bloom=1\r\n!!cvarf r_bloom_retain=1.0\r\n#include \"sys/defs.h\"\r\n//add them together\r\n//optionally apply tonemapping\r\n\r\nlayout(location=0) varying vec2 tc;\r\n\r\n#ifdef VERTEX_SHADER\r\nvoid main ()\r\n{\r\n\ttc = v_texcoord;\r\n\tgl_Position = ftetransform();\r\n}\r\n#endif\r\n#ifdef FRAGMENT_SHADER\r\nvoid main ()\r\n{\r\n\tgl_FragColor = \r\n\t\tcvar_r_bloom_retain * texture2D(s_t0, tc) +\r\n\t\tcvar_r_bloom*(\r\n\t\t\ttexture2D(s_t1, tc) +\r\n\t\t\ttexture2D(s_t2, tc) +\r\n\t\t\ttexture2D(s_t3, tc)\r\n\t\t) ;\r\n}\r\n#endif\r\n"
  },
  {
    "path": "engine/shaders/vulkan/default2d.glsl",
    "content": "!!argb PREMUL=false\r\n!!samps 1\r\n#include \"sys/defs.h\"\r\n\r\n//this shader is present for support for gles/gl3core contexts\r\n//it is single-texture-with-vertex-colours, and doesn't do anything special.\r\n//beware that a few things use this, including apparently fonts and bloom rescaling.\r\n//its really not meant to do anything special.\r\n\r\nlayout(location=0) varying vec2 tc;\r\nlayout(location=1) varying vec4 vc;\r\n\r\n#ifdef VERTEX_SHADER\r\nvoid main ()\r\n{\r\n\ttc = v_texcoord;\r\n\tvc = v_colour;\r\n\tgl_Position = ftetransform();\r\n}\r\n#endif\r\n#ifdef FRAGMENT_SHADER\r\nvoid main ()\r\n{\r\n\tvec4 f = vc;\r\n\tif (arg_PREMUL)\r\n\t\tf.rgb *= f.a;\r\n\tf *= texture2D(s_t0, tc);\r\n\tgl_FragColor = f;\r\n}\r\n#endif\r\n"
  },
  {
    "path": "engine/shaders/vulkan/defaultadditivesprite.glsl",
    "content": "!!permu FOG\r\n!!samps 1\r\n!!argf MASK=0\r\n!!cvarb r_fog_exp2=true\r\n!!cvarb r_fog_linear=false\r\n\r\n//used by both particles and sprites.\r\n//note the fog blending mode is all that differs from defaultadditivesprite\r\n\r\n#include \"sys/defs.h\"\r\n#include \"sys/fog.h\"\r\n\r\nlayout(location=0) varying vec2 tc;\r\nlayout(location=1) varying vec4 vc;\r\n\r\n#ifdef VERTEX_SHADER\r\nvoid main ()\r\n{\r\n\ttc = v_texcoord;\r\n\tvc = v_colour;\r\n\tgl_Position = ftetransform();\r\n}\r\n#endif\r\n#ifdef FRAGMENT_SHADER\r\nvoid main ()\r\n{\r\n\tvec4 col = texture2D(s_t0, tc);\r\n\tif (arg_MASK!=0.0 && col.a < float(arg_MASK))\r\n\t\tdiscard;\r\n\tgl_FragColor = fog4additive(col * vc * e_colourident);\r\n}\r\n#endif\r\n"
  },
  {
    "path": "engine/shaders/vulkan/defaultfill.glsl",
    "content": "#include \"sys/defs.h\"\r\n\r\nlayout(location=0) varying vec4 vc;\r\n\r\n#ifdef VERTEX_SHADER\r\nvoid main ()\r\n{\r\n\tvc = v_colour;\r\n\tgl_Position = ftetransform();\r\n}\r\n#endif\r\n\r\n#ifdef FRAGMENT_SHADER\r\nvoid main ()\r\n{\r\n\tgl_FragColor = vc;\r\n}\r\n#endif\r\n"
  },
  {
    "path": "engine/shaders/vulkan/defaultgammacb.glsl",
    "content": "!!samps 1\r\n\r\n//this shader is applies gamma/contrast/brightness to the source image, and dumps it out.\r\n\r\n#include \"sys/defs.h\"\r\n\r\nlayout(location=0) varying vec2 tc;\r\nlayout(location=1) varying vec4 vc;\r\n\r\n#ifdef VERTEX_SHADER\r\nvoid main ()\r\n{\r\n\ttc = v_texcoord;\r\n\tvc = v_colour;\r\n\tgl_Position = ftetransform();\r\n}\r\n#endif\r\n#ifdef FRAGMENT_SHADER\r\nvoid main ()\r\n{\r\n\tgl_FragColor = pow(texture2D(s_t0, tc) * vc.g, vec4(vc.r)) + vc.b;\r\n}\r\n#endif\r\n"
  },
  {
    "path": "engine/shaders/vulkan/defaultskin.glsl",
    "content": "!!permu FULLBRIGHT\r\n!!permu UPPERLOWER\r\n//!!permu FRAMEBLEND\r\n//!!permu SKELETAL\r\n!!permu FOG\r\n!!permu BUMP\r\n!!permu REFLECTCUBEMASK\r\n!!cvarf r_glsl_offsetmapping=0\r\n!!cvarf r_glsl_offsetmapping_scale=0.04\r\n!!cvarf gl_specular=0\r\n!!cvarb r_fog_exp2=true\r\n!!cvarb r_fog_linear=false\r\n!!samps diffuse normalmap upper lower specular fullbright reflectcube reflectmask\r\n\r\n#include \"sys/defs.h\"\r\n\r\n//standard shader used for models.\r\n//must support skeletal and 2-way vertex blending or Bad Things Will Happen.\r\n//the vertex shader is responsible for calculating lighting values.\r\n\r\nlayout(location=0) varying vec2 tcbase;\r\nlayout(location=1) varying vec3 light;\r\n#if defined(SPECULAR) || defined(OFFSETMAPPING)\r\nlayout(location=2) varying vec3 eyevector;\r\n#endif\r\nlayout(location=3) varying mat3 invsurface;\r\n\r\n\r\n\r\n\r\n#ifdef VERTEX_SHADER\r\n#include \"sys/skeletal.h\"\r\nvoid main ()\r\n{\r\n\tvec3 n;\r\n\tif (SPECULAR||OFFSETMAPPING||REFLECTCUBEMASK)\r\n\t{\r\n\t\tvec3 s, t, w;\r\n\t\tgl_Position = skeletaltransform_wnst(w,n,s,t);\r\n\t\tvec3 eyeminusvertex = e_eyepos - w.xyz;\r\n\t\teyevector.x = dot(eyeminusvertex, s.xyz);\r\n\t\teyevector.y = dot(eyeminusvertex, t.xyz);\r\n\t\teyevector.z = dot(eyeminusvertex, n.xyz);\r\n\r\n\t\tif (REFLECTCUBEMASK)\r\n\t\t{\r\n\t\t\tinvsurface[0] = s;\r\n\t\t\tinvsurface[0] = t;\r\n\t\t\tinvsurface[0] = n;\r\n\t\t}\r\n\t}\r\n\telse\r\n\t{\r\n\t\tgl_Position = skeletaltransform_n(n);\r\n\t}\r\n\r\n\tfloat d = dot(n,e_light_dir);\r\n\tif (d < 0.0)\t\t//vertex shader. this might get ugly, but I don't really want to make it per vertex.\r\n\t\td = 0.0;\t//this avoids the dark side going below the ambient level.\r\n\tlight = e_light_ambient + (dot(n,e_light_dir)*e_light_mul);\r\n\ttcbase = v_texcoord;\r\n}\r\n#endif\r\n#ifdef FRAGMENT_SHADER\r\n#include \"sys/fog.h\"\r\n\r\n#include \"sys/offsetmapping.h\"\r\n\r\nvoid main ()\r\n{\r\n\tvec4 col, sp;\r\n\r\n\tvec2 tc;\r\n\tif (OFFSETMAPPING)\r\n\t\ttc = offsetmap(s_normalmap, tcbase, eyevector);\r\n\telse\r\n\t\ttc = tcbase;\r\n\r\n\tcol = texture2D(s_diffuse, tc);\r\n\r\n\tif (UPPERLOWER)\r\n\t{\r\n\t\tvec4 uc = texture2D(s_upper, tc);\r\n\t\tcol.rgb += uc.rgb*e_uppercolour*uc.a;\r\n\r\n\t\tvec4 lc = texture2D(s_lower, tc);\r\n\t\tcol.rgb += lc.rgb*e_lowercolour*lc.a;\r\n\t}\r\n\r\n\tvec3 bumps = vec3(0,0,1);\r\n\tif (BUMP || SPECULAR)\r\n\t{\r\n\t\tbumps = normalize(vec3(texture2D(s_normalmap, tc)) - 0.5);\r\n\t\tvec4 specs = texture2D(s_specular, tc);\r\n\r\n\t\tvec3 halfdir = normalize(normalize(eyevector) + vec3(0.0, 0.0, 1.0));\r\n\t\tfloat spec = pow(max(dot(halfdir, bumps), 0.0), 32.0 * specs.a);\r\n\t\tcol.rgb += cvar_gl_specular * spec * specs.rgb;\r\n\t}\r\n\r\n\tif (REFLECTCUBEMASK)\r\n\t{\r\n\t\tvec3 rtc = reflect(-eyevector, bumps);\r\n\t\trtc = rtc.x*invsurface[0] + rtc.y*invsurface[1] + rtc.z*invsurface[2];\r\n\t\trtc = (m_model * vec4(rtc.xyz,0.0)).xyz;\r\n\t\tcol.rgb += texture2D(s_reflectmask, tc).rgb * textureCube(s_reflectcube, rtc).rgb;\r\n}\r\n\r\n\tcol.rgb *= light;\r\n\r\n\tif (FULLBRIGHT)\r\n\t{\r\n\t\tvec4 fb = texture2D(s_fullbright, tc);\r\n//\t\tcol.rgb = mix(col.rgb, fb.rgb, fb.a);\r\n\t\tcol.rgb += fb.rgb * fb.a;\r\n\t}\r\n\r\n\tgl_FragColor = fog4(col * e_colourident);\r\n}\r\n#endif\r\n"
  },
  {
    "path": "engine/shaders/vulkan/defaultsky.glsl",
    "content": "!!permu FOG\r\n!!samps 2\r\n!!cvarb r_fog_exp2=true\r\n!!cvarb r_fog_linear=false\r\n\r\n#include \"sys/defs.h\"\r\n#include \"sys/fog.h\"\r\n\r\n//regular sky shader for scrolling q1 skies\r\n//the sky surfaces are thrown through this as-is.\r\n\r\nlayout(location=0) varying vec3 pos;\r\n\r\n#ifdef VERTEX_SHADER\r\nvoid main ()\r\n{\r\n\tpos = v_position.xyz;\r\n\tgl_Position = ftetransform();\r\n}\r\n#endif\r\n#ifdef FRAGMENT_SHADER\r\nvoid main ()\r\n{\r\n\tvec2 tccoord;\r\n\tvec3 dir = pos - e_eyepos;\r\n\tdir.z *= 3.0;\r\n\tdir.xy /= 0.5*length(dir);\r\n\ttccoord = (dir.xy + e_time*0.03125);\r\n\tvec3 solid = vec3(texture2D(s_t0, tccoord));\r\n\ttccoord = (dir.xy + e_time*0.0625);\r\n\tvec4 clouds = texture2D(s_t1, tccoord);\r\n\tgl_FragColor.rgb = fog3((solid.rgb*(1.0-clouds.a)) + (clouds.a*clouds.rgb));\r\n\tgl_FragColor.a = 1.0;\r\n}\r\n#endif\r\n"
  },
  {
    "path": "engine/shaders/vulkan/defaultskybox.glsl",
    "content": "!!permu FOG\r\n!!samps reflectcube\r\n!!cvarb r_fog_exp2=true\r\n!!cvarb r_fog_linear=false\r\n#include \"sys/defs.h\"\r\n#include \"sys/fog.h\"\r\n\r\n//simple shader for simple skyboxes.\r\n\r\nlayout(location=0) varying vec3 pos;\r\n#ifdef VERTEX_SHADER\r\nvoid main ()\r\n{\r\n\tpos = v_position.xyz - e_eyepos;\r\n\tgl_Position = ftetransform();\r\n}\r\n#endif\r\n#ifdef FRAGMENT_SHADER\r\nvoid main ()\r\n{\r\n\tvec4 skybox = textureCube(s_reflectcube, pos);\r\n\tgl_FragColor = vec4(fog3(skybox.rgb), 1.0);\r\n}\r\n#endif\r\n"
  },
  {
    "path": "engine/shaders/vulkan/defaultsprite.glsl",
    "content": "!!permu FOG\r\n!!samps 1\r\n!!argf MASK=0\r\n!!cvarb r_fog_exp2=true\r\n!!cvarb r_fog_linear=false\r\n\r\n//used by both particles and sprites.\r\n//note the fog blending mode is all that differs from defaultadditivesprite\r\n\r\n#include \"sys/defs.h\"\r\n#include \"sys/fog.h\"\r\n\r\nlayout(location=0) varying vec2 tc;\r\nlayout(location=1) varying vec4 vc;\r\n\r\n#ifdef VERTEX_SHADER\r\nvoid main ()\r\n{\r\n\ttc = v_texcoord;\r\n\tvc = v_colour;\r\n\tgl_Position = ftetransform();\r\n}\r\n#endif\r\n#ifdef FRAGMENT_SHADER\r\nvoid main ()\r\n{\r\n\tvec4 col = texture2D(s_t0, tc);\r\n\tif (arg_MASK!=0.0 && col.a < float(arg_MASK))\r\n\t\tdiscard;\r\n\tgl_FragColor = fog4blend(col * vc * e_colourident);\r\n}\r\n#endif\r\n"
  },
  {
    "path": "engine/shaders/vulkan/defaultwall.glsl",
    "content": "!!permu FULLBRIGHT\r\n!!permu BUMP\r\n!!permu REFLECTCUBEMASK\r\n!!cvarf r_glsl_offsetmapping=0.0\r\n!!cvarf r_glsl_offsetmapping_scale=0.04\r\n!!cvarf gl_specular=0.3\r\n!!cvarb r_fog_exp2=true\r\n!!cvarb r_fog_linear=false\r\n!!samps diffuse normalmap specular fullbright lightmap\r\n!!samps deluxemap reflectmask reflectcube\r\n!!argb vertexlit=0\r\n//!!samps =EIGHTBIT paletted 1\r\n//!!argb eightbit=0\r\n!!argf mask=1.0\r\n!!argb masklt=false\r\n!!permu FOG\r\n//!!permu DELUXE\r\n//!!permu LIGHTSTYLED //this seems to be breaking nvidia drivers if set from the engine, despite us not using it...\r\n\r\nconst bool DELUXE = false;\r\n#define SPECULAR (cvar_gl_specular>0)\r\n\r\n#include \"sys/defs.h\"\r\n\r\n//this is what normally draws all of your walls, even with rtlights disabled\r\n//note that the '286' preset uses drawflat_walls instead.\r\n\r\n#include \"sys/fog.h\"\r\nlayout(location=1) varying vec3 eyevector;\r\nlayout(location=2) varying vec2 basetc;\r\nlayout(location=3) varying vec4 vc;\r\nlayout(location=4) varying mat3 invsurface;\r\n#ifdef LIGHTSTYLED\r\n//we could use an offset, but that would still need to be per-surface which would break batches\r\n//fixme: merge attributes?\r\nvarying vec2 lm0, lm1, lm2, lm3;\r\n#else\r\nlayout(location=0) varying vec2 lm0;\r\n#endif\r\n\r\n#ifdef VERTEX_SHADER\r\nvoid main ()\r\n{\r\n\tif (OFFSETMAPPING || SPECULAR || REFLECTCUBEMASK)\r\n\t{\r\n\t\tvec3 eyeminusvertex = e_eyepos - v_position.xyz;\r\n\t\teyevector.x = dot(eyeminusvertex, v_svector.xyz);\r\n\t\teyevector.y = dot(eyeminusvertex, v_tvector.xyz);\r\n\t\teyevector.z = dot(eyeminusvertex, v_normal.xyz);\r\n\t}\r\n\tif (REFLECTCUBEMASK)\r\n\t{\r\n\t\tinvsurface[0] = v_svector;\r\n\t\tinvsurface[1] = v_tvector;\r\n\t\tinvsurface[2] = v_normal;\r\n\t}\r\n\tbasetc = v_texcoord;\r\n\tlm0 = v_lmcoord;\r\n#ifdef LIGHTSTYLED\r\n\tlm1 = v_lmcoord2;\r\n\tlm2 = v_lmcoord3;\r\n\tlm3 = v_lmcoord4;\r\n#endif\r\n\tif (arg_vertexlit)\r\n\t\tvc = v_colour;\r\n\tgl_Position = ftetransform();\r\n}\r\n#endif\r\n\r\n\r\n#ifdef FRAGMENT_SHADER\r\n\r\n//samplers\r\n\r\n//for 8bit\r\n#define s_colourmap\ts_t0\r\n\r\n\r\n#include \"sys/offsetmapping.h\"\r\nvoid main ()\r\n{\r\n//adjust texture coords for offsetmapping\r\n\tvec2 tc = basetc;\r\n\tif (OFFSETMAPPING)\r\n\t\ttc = offsetmap(s_normalmap, tc, eyevector);\r\n\r\n//yay, regular texture!\r\n\tgl_FragColor = texture2D(s_diffuse, tc);\r\n\r\n\tvec3 norm;\r\n\tif (BUMP && (DELUXE || SPECULAR || REFLECTCUBEMASK))\r\n\t\tnorm = normalize(texture2D(s_normalmap, tc).rgb - 0.5);\r\n\telse if (SPECULAR || DELUXE || REFLECTCUBEMASK)\r\n\t\tnorm = vec3(0, 0, 1);\t//specular lighting expects this to exist.\r\n\r\n\tvec3 lightmaps;\r\n\tif (arg_vertexlit)\r\n\t\tlightmaps = vc.rgb * e_lmscale.rgb;\r\n\telse\r\n\t{\r\n\t\t//modulate that by the lightmap(s) including deluxemap(s)\r\n#ifdef LIGHTSTYLED\r\n\t\tif (DELUXE)\r\n\t\t{\r\n\t\t\tlightmaps  = texture2D(s_lightmap0, lm0).rgb * e_lmscale[0].rgb * dot(norm, 2.0*texture2D(s_deluxemap0, lm0).rgb-0.5);\r\n\t\t\tlightmaps += texture2D(s_lightmap1, lm1).rgb * e_lmscale[1].rgb * dot(norm, 2.0*texture2D(s_deluxemap1, lm1).rgb-0.5);\r\n\t\t\tlightmaps += texture2D(s_lightmap2, lm2).rgb * e_lmscale[2].rgb * dot(norm, 2.0*texture2D(s_deluxemap2, lm2).rgb-0.5);\r\n\t\t\tlightmaps += texture2D(s_lightmap3, lm3).rgb * e_lmscale[3].rgb * dot(norm, 2.0*texture2D(s_deluxemap3, lm3).rgb-0.5);\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tlightmaps  = texture2D(s_lightmap0, lm0).rgb * e_lmscale[0].rgb;\r\n\t\t\tlightmaps += texture2D(s_lightmap1, lm1).rgb * e_lmscale[1].rgb;\r\n\t\t\tlightmaps += texture2D(s_lightmap2, lm2).rgb * e_lmscale[2].rgb;\r\n\t\t\tlightmaps += texture2D(s_lightmap3, lm3).rgb * e_lmscale[3].rgb;\r\n\t\t}\r\n#else\r\n\t\t/*if (arg_eightbit)\r\n\t\t{\r\n\t\t\t//optional: round the lightmap coords to ensure all pixels within a texel have different lighting values either. it just looks wrong otherwise.\r\n\t\t\t//don't bother if its lightstyled, such cases will have unpredictable correlations anyway.\r\n\t\t\t//FIXME: this rounding is likely not correct with respect to software rendering. oh well.\r\n\t\t\tvec2 nearestlm0 = floor(lm0 * 256.0*8.0)/(256.0*8.0);\r\n\t\t\tlightmaps = (texture2D(s_lightmap, nearestlm0) * e_lmscale).rgb;\r\n\t\t}\r\n\t\telse*/\r\n\t\t\tlightmaps = (texture2D(s_lightmap, lm0) * e_lmscale).rgb;\r\n\t\t//modulate by the  bumpmap dot light\r\n\t\tif (DELUXE)\r\n\t\t{\r\n\t\t\tvec3 delux = 2.0*(texture2D(s_deluxemap, lm0).rgb-0.5);\r\n\t\t\tlightmaps *= 1.0 / max(0.25, delux.z);\t//counter the darkening from deluxmaps\r\n\t\t\tlightmaps *= dot(norm, delux);\r\n\t\t}\r\n#endif\r\n\t}\r\n\r\n//add in specular, if applicable.\r\n\tif (SPECULAR)\r\n\t{\r\n\t\tvec4 specs = texture2D(s_specular, tc);\r\n\t\tvec3 halfdir;\r\n\t\tif (DELUXE)\r\n\t\t{\r\n//not lightstyled...\r\n\t\t\thalfdir = normalize(normalize(eyevector) + 2.0*(texture2D(s_deluxemap0, lm0).rgb-0.5));\t//this norm should be the deluxemap info instead\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\thalfdir = normalize(normalize(eyevector) + vec3(0.0, 0.0, 1.0));\t//this norm should be the deluxemap info instead\r\n\t\t}\r\n\t\tfloat spec = pow(max(dot(halfdir, norm), 0.0), 32.0 * specs.a);\r\n\t\tspec *= cvar_gl_specular;\r\n//NOTE: rtlights tend to have a *4 scaler here to over-emphasise the effect because it looks cool.\r\n//As not all maps will have deluxemapping, and the double-cos from the light util makes everything far too dark anyway,\r\n//we default to something that is not garish when the light value is directly infront of every single pixel.\r\n//we can justify this difference due to the rtlight editor etc showing the *4.\r\n\t\tgl_FragColor.rgb += spec * specs.rgb;\r\n\t}\r\n\r\n\tif (REFLECTCUBEMASK)\r\n\t{\r\n\t\tvec3 rtc = reflect(-eyevector, norm);\r\n\t\trtc = rtc.x*invsurface[0] + rtc.y*invsurface[1] + rtc.z*invsurface[2];\r\n\t\trtc = (m_model * vec4(rtc.xyz,0.0)).xyz;\r\n\t\tgl_FragColor.rgb += texture2D(s_reflectmask, tc).rgb * textureCube(s_reflectcube, rtc).rgb;\r\n\t}\r\n\r\n\t/*if (arg_eightbit)\r\n\t{\r\n\t\t//FIXME: with this extra flag, half the permutations are redundant.\r\n\t\tlightmaps *= 0.5;\t//counter the fact that the colourmap contains overbright values and logically ranges from 0 to 2 intead of to 1.\r\n\t\tfloat pal = texture2D(s_paletted, tc).r;\t//the palette index. hopefully not interpolated.\r\n\t\tlightmaps -= 1.0 / 128.0;\t//software rendering appears to round down, so make sure we favour the lower values instead of rounding to the nearest\r\n\t\tgl_FragColor.r = texture2D(s_colourmap, vec2(pal, 1.0-lightmaps.r)).r;\t//do 3 lookups. this is to cope with lit files, would be a waste to not support those.\r\n\t\tgl_FragColor.g = texture2D(s_colourmap, vec2(pal, 1.0-lightmaps.g)).g;\t//its not very softwarey, but re-palettizing is ugly.\r\n\t\tgl_FragColor.b = texture2D(s_colourmap, vec2(pal, 1.0-lightmaps.b)).b;\t//without lits, it should be identical.\r\n\t}\r\n\telse*/\r\n\t{\r\n\t\t//now we have our diffuse+specular terms, modulate by lightmap values.\r\n\t\tgl_FragColor.rgb *= lightmaps.rgb;\r\n\r\n\t\t//add on the fullbright\r\n\t\tif (FULLBRIGHT)\r\n\t\t\tgl_FragColor.rgb += texture2D(s_fullbright, tc).rgb;\r\n\t}\r\n\r\n//entity modifiers\r\n\tgl_FragColor = gl_FragColor * e_colourident;\r\n\r\n\tif (arg_mask != 1.0)\r\n\t{\r\n\t\tif (arg_masklt)\r\n\t\t{\r\n\t\t\tif (gl_FragColor.a < arg_mask)\r\n\t\t\t\tdiscard;\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tif (gl_FragColor.a >= arg_mask)\r\n\t\t\t\tdiscard;\r\n\t\t}\r\n\t\tgl_FragColor.a = 1.0;\r\n\t}\r\n\r\n//and finally hide it all if we're fogged.\r\n#ifdef FOG\r\n\tgl_FragColor = fog4(gl_FragColor);\r\n#endif\r\n}\r\n#endif\r\n"
  },
  {
    "path": "engine/shaders/vulkan/defaultwarp.glsl",
    "content": "!!permu FOG\r\n!!cvarf r_wateralpha=1\r\n!!cvarb r_fog_exp2=true\r\n!!cvarb r_fog_linear=false\r\n!!argf alpha=0\r\n!!argb lit=false\r\n!!samps diffuse lightmap\r\n\r\n#include \"sys/defs.h\"\r\n\r\n//this is the shader that's responsible for drawing default q1 turbulant water surfaces\r\n//this is expected to be moderately fast.\r\n\r\n#include \"sys/fog.h\"\r\nlayout(location=0) varying vec2 tc;\r\nlayout(location=1) varying vec2 lmtc;\r\n\r\n#ifdef VERTEX_SHADER\r\nvoid main ()\r\n{\r\n\ttc = v_texcoord.st;\r\n\t#ifdef FLOW\r\n\ttc.s += e_time * -0.5;\r\n\t#endif\r\n\tlmtc = v_lmcoord.st;\r\n\tgl_Position = ftetransform();\r\n}\r\n#endif\r\n#ifdef FRAGMENT_SHADER\r\nvoid main ()\r\n{\r\n\tvec2 ntc = tc + sin(tc.ts+e_time)*0.125;\r\n\tvec3 ts = vec3(texture2D(s_diffuse, ntc));\r\n\r\n\tif (arg_lit)\r\n\t\tts *= (texture2D(s_lightmap, lmtc) * e_lmscale).rgb;\r\n\r\n\tif (arg_alpha != 0.0)\r\n\t\tgl_FragColor = fog4(vec4(ts, arg_alpha));\r\n\telse\r\n\t\tgl_FragColor = fog4(vec4(ts, cvar_r_wateralpha));\r\n}\r\n#endif\r\n"
  },
  {
    "path": "engine/shaders/vulkan/depthonly.glsl",
    "content": "//!!permu FRAMEBLEND\r\n//!!permu SKELETAL\r\n\r\n#include \"sys/defs.h\"\r\n\r\n//standard shader used for drawing shadowmap depth.\r\n//also used for masking off portals and other things that want depth and no colour.\r\n//must support skeletal and 2-way vertex blending or Bad Things Will Happen.\r\n//the vertex shader is responsible for calculating lighting values.\r\n\r\n#ifdef VERTEX_SHADER\r\n#include \"sys/skeletal.h\"\r\nvoid main ()\r\n{\r\n\tgl_Position = skeletaltransform();\r\n}\r\n#endif\r\n\r\n#ifdef FRAGMENT_SHADER\r\nvoid main ()\r\n{\r\n\t//must always draw something, supposedly. It might as well be black.\r\n\t//in gl anyway, vulkan doesn't need it. gl_FragColor = vec4(0, 0, 0, 1);\r\n}\r\n#endif\r\n\r\n"
  },
  {
    "path": "engine/shaders/vulkan/drawflat_wall.glsl",
    "content": "!!permu FOG\r\n!!cvar3f r_floorcolor=0.5,0.5,0.5\r\n!!cvar3f r_wallcolor=0.25,0.25,0.5\r\n!!cvarb r_fog_exp2=true\r\n!!cvarb r_fog_linear=false\r\n!!samps 1\r\n#include \"sys/defs.h\"\r\n\r\n//this is for the '286' preset walls, and just draws lightmaps coloured based upon surface normals.\r\n\r\n#include \"sys/fog.h\"\r\nlayout(location=0) varying vec4 col;\r\nlayout(location=1) varying vec2 lm;\r\n\r\n#ifdef VERTEX_SHADER\r\nvoid main ()\r\n{\r\n\tcol = vec4(e_lmscale.rgb/255.0 * ((v_normal.z < 0.73)?cvar_r_wallcolor:cvar_r_floorcolor), e_lmscale.a);\r\n\tlm = v_lmcoord;\r\n\tgl_Position = ftetransform();\r\n}\r\n#endif\r\n#ifdef FRAGMENT_SHADER\r\nvoid main ()\r\n{\r\n\tgl_FragColor = fog4(col * texture2D(s_t0, lm));\r\n}\r\n#endif\r\n"
  },
  {
    "path": "engine/shaders/vulkan/fixedemu.glsl",
    "content": "!!samps 1\r\n\r\nlayout(constant_id = 0) const int alphatest = 4;\r\n\r\n//this shader is present for support for gles/gl3core contexts\r\n//it is single-texture-with-vertex-colours, and doesn't do anything special.\r\n//beware that a few things use this, including apparently fonts and bloom rescaling.\r\n//its really not meant to do anything special.\r\n\r\n#include \"sys/defs.h\"\r\n\r\nlayout(location=0) varying vec2 tc;\r\nlayout(location=1) varying vec4 vc;\r\n\r\n#ifdef VERTEX_SHADER\r\nvoid main ()\r\n{\r\n\ttc = v_texcoord;\r\n\tvc = v_colour;\r\n\tgl_Position = ftetransform();\r\n}\r\n#endif\r\n\r\n#ifdef FRAGMENT_SHADER\r\nvoid main ()\r\n{\r\n\tvec4 fc = texture2D(s_t0, tc) * vc;\r\n\tif (alphatest == 4)\r\n\t\tdiscard;\r\n\tif (alphatest == 3)\r\n\t{\r\n\t\tif (fc.a < 0.5)\r\n\t\t\tdiscard;\r\n\t}\r\n\telse if (alphatest == 2)\r\n\t{\r\n\t\tif (fc.a <= 0)\r\n\t\t\tdiscard;\r\n\t}\r\n\telse if (alphatest == 1)\r\n\t{\r\n\t\tif (fc.a >= 0.5)\r\n\t\t\tdiscard;\r\n\t}\r\n\tgl_FragColor = fc;\r\n}\r\n#endif\r\n"
  },
  {
    "path": "engine/shaders/vulkan/fixedemu_flat.glsl",
    "content": "!!samps 1\r\n\r\nlayout(constant_id = 0) const int alphatest = 4;\r\n\r\n//this shader is present for support for gles/gl3core contexts\r\n//it is single-texture-with-vertex-colours, and doesn't do anything special.\r\n//beware that a few things use this, including apparently fonts and bloom rescaling.\r\n//its really not meant to do anything special.\r\n\r\n#include \"sys/defs.h\"\r\n\r\nlayout(location=0) varying vec2 tc;\r\n\r\n#ifdef VERTEX_SHADER\r\nvoid main ()\r\n{\r\n\ttc = v_texcoord;\r\n\tgl_Position = ftetransform();\r\n}\r\n#endif\r\n\r\n#ifdef FRAGMENT_SHADER\r\nlayout(push_constant) uniform pushintf\r\n{\r\n\tvec4 colour;\t\r\n} push;\r\n\r\nvoid main ()\r\n{\r\n\tvec4 fc = texture2D(s_t0, tc) * push.colour;\r\n\tif (alphatest == 4)\r\n\t\tdiscard;\r\n\tif (alphatest == 3)\r\n\t{\r\n\t\tif (fc.a < 0.5)\r\n\t\t\tdiscard;\r\n\t}\r\n\telse if (alphatest == 2)\r\n\t{\r\n\t\tif (fc.a <= 0)\r\n\t\t\tdiscard;\r\n\t}\r\n\telse if (alphatest == 1)\r\n\t{\r\n\t\tif (fc.a >= 0.5)\r\n\t\t\tdiscard;\r\n\t}\r\n\tgl_FragColor = fc;\r\n}\r\n#endif\r\n"
  },
  {
    "path": "engine/shaders/vulkan/fxaa.glsl",
    "content": "!!samps 1\r\n#include \"sys/defs.h\"\r\n//\r\n//This shader implements super-sampled anti-aliasing.\r\n//\r\n\r\nlayout(location=0) varying vec2 texcoord;\r\nlayout(location=1) varying vec2 e_sourcesize;\r\n\r\n#ifdef VERTEX_SHADER\r\nvoid main()\r\n{\r\n\ttexcoord = v_texcoord.xy;\r\n\ttexcoord.y = 1.0 - texcoord.y;\r\n\te_sourcesize = v_colour.rg;\r\n\tgl_Position = ftetransform();\r\n}\r\n#endif\r\n#ifdef FRAGMENT_SHADER\r\n//uniform vec2 e_sourcesize;\r\n\r\nvoid main( void )\r\n{\r\n\tfloat FXAA_SPAN_MAX = 8.0;\r\n\tfloat FXAA_REDUCE_MUL = 1.0/8.0;\r\n\tfloat FXAA_REDUCE_MIN = 1.0/128.0;\r\n\r\n\tvec3 rgbNW=texture2D(s_t0,texcoord+(vec2(-1.0,-1.0)/e_sourcesize)).xyz;\r\n\tvec3 rgbNE=texture2D(s_t0,texcoord+(vec2(1.0,-1.0)/e_sourcesize)).xyz;\r\n\tvec3 rgbSW=texture2D(s_t0,texcoord+(vec2(-1.0,1.0)/e_sourcesize)).xyz;\r\n\tvec3 rgbSE=texture2D(s_t0,texcoord+(vec2(1.0,1.0)/e_sourcesize)).xyz;\r\n\tvec3 rgbM=texture2D(s_t0,texcoord).xyz;\r\n\r\n\tvec3 luma=vec3(0.299, 0.587, 0.114);\r\n\tfloat lumaNW = dot(rgbNW, luma);\r\n\tfloat lumaNE = dot(rgbNE, luma);\r\n\tfloat lumaSW = dot(rgbSW, luma);\r\n\tfloat lumaSE = dot(rgbSE, luma);\r\n\tfloat lumaM  = dot(rgbM,  luma);\r\n\r\n\tfloat lumaMin = min(lumaM, min(min(lumaNW, lumaNE), min(lumaSW, lumaSE)));\r\n\tfloat lumaMax = max(lumaM, max(max(lumaNW, lumaNE), max(lumaSW, lumaSE)));\r\n\r\n\tvec2 dir;\r\n\tdir.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE));\r\n\tdir.y =  ((lumaNW + lumaSW) - (lumaNE + lumaSE));\r\n\r\n\tfloat dirReduce = max((lumaNW + lumaNE + lumaSW + lumaSE) * (0.25 * FXAA_REDUCE_MUL), FXAA_REDUCE_MIN);\r\n\r\n\tfloat rcpDirMin = 1.0/(min(abs(dir.x), abs(dir.y)) + dirReduce);\r\n\r\n\tdir = min(vec2( FXAA_SPAN_MAX,  FXAA_SPAN_MAX),\r\n\tmax(vec2(-FXAA_SPAN_MAX, -FXAA_SPAN_MAX),\r\n\tdir * rcpDirMin)) / e_sourcesize;\r\n\r\n\tvec3 rgbA = (1.0/2.0) * (texture2D(s_t0, texcoord.xy + dir * (1.0/3.0 - 0.5)).xyz +\ttexture2D(s_t0, texcoord.xy + dir * (2.0/3.0 - 0.5)).xyz);\r\n\tvec3 rgbB = rgbA * (1.0/2.0) + (1.0/4.0) * (texture2D(s_t0, texcoord.xy + dir * (0.0/3.0 - 0.5)).xyz + texture2D(s_t0, texcoord.xy + dir * (3.0/3.0 - 0.5)).xyz);\r\n\tfloat lumaB = dot(rgbB, luma);\r\n\r\n\tif((lumaB < lumaMin) || (lumaB > lumaMax))\r\n\t\tgl_FragColor.xyz=rgbA;\r\n\telse\r\n\t\tgl_FragColor.xyz=rgbB;\r\n\tgl_FragColor.a = 1.0;\r\n}\r\n#endif\r\n"
  },
  {
    "path": "engine/shaders/vulkan/menutint.glsl",
    "content": "!!cvari r_menutint_inverse=0\r\n!!cvar3f r_menutint=0.68 0.4 0.13\r\n!!samps 1\r\n\r\n#include \"sys/defs.h\"\r\n\r\nconst vec4 e_rendertexturescale = vec4(1,1,1,1);\r\n\r\nlayout(location=0) varying vec2 texcoord;\r\n#ifdef VERTEX_SHADER\r\n\t\tvoid main(void)\r\n\t\t{\r\n#ifdef VULKAN\r\n\t\t\ttexcoord.xy = v_texcoord.xy*e_rendertexturescale.xy;\r\n#else\r\n\t\t\ttexcoord.x = v_texcoord.x*e_rendertexturescale.x;\r\n\t\t\ttexcoord.y = (1.0-v_texcoord.y)*e_rendertexturescale.y;\r\n#endif\r\n\t\t\tgl_Position = ftetransform();\r\n\t\t}\r\n#endif\r\n#ifdef FRAGMENT_SHADER\r\n\t\tconst vec3 lumfactors = vec3(0.299, 0.587, 0.114);\r\n\t\tconst vec3 invertvec = vec3(1.0, 1.0, 1.0);\r\n\t\tvoid main(void)\r\n\t\t{\r\n\t\t\tvec3 texcolor = texture2D(s_t0, texcoord).rgb;\r\n\t\t\tfloat luminance = dot(lumfactors, texcolor);\r\n\t\t\ttexcolor = vec3(luminance, luminance, luminance);\r\n\t\t\ttexcolor *= cvar_r_menutint;\r\n\t\t\ttexcolor = (cvar_r_menutint_inverse > 0) ? (invertvec - texcolor) : texcolor;\r\n\t\t\tgl_FragColor = vec4(texcolor, 1.0);\r\n\t\t}\r\n#endif"
  },
  {
    "path": "engine/shaders/vulkan/postproc_fisheye.glsl",
    "content": "!!cvarf ffov=360\r\n!!samps reflectcube\r\n#include \"sys/defs.h\"\r\n\r\n\r\n//fisheye view rendering, for silly fovs that are still playable.\r\n\r\nlayout(location=0) varying vec2 texcoord;\r\n#ifdef VERTEX_SHADER\r\nvoid main()\r\n{\r\n\ttexcoord = v_texcoord.xy;\r\n\tgl_Position = ftetransform();\r\n}\r\n#endif\r\n#ifdef FRAGMENT_SHADER\r\nvoid main()\r\n{\r\n\tvec3 tc;\t\r\n\tvec2 d;\t\r\n\tvec2 ang;\t\r\n\td = texcoord;\t\r\n\tang.x = sqrt(d.x*d.x+d.y*d.y)*radians(cvar_ffov);\t\r\n\tang.y = -atan(d.y, d.x);\t\r\n\ttc.x = sin(ang.x) * cos(ang.y);\t\r\n\ttc.y = sin(ang.x) * sin(ang.y);\t\r\n\ttc.z = cos(ang.x);\t\r\n\tgl_FragColor = textureCube(s_reflectcube, tc);\r\n}\r\n#endif\r\n"
  },
  {
    "path": "engine/shaders/vulkan/postproc_panorama.glsl",
    "content": "!!cvarf ffov=360\r\n!!samps reflectcube\r\n#include \"sys/defs.h\"\r\n\r\n//panoramic view rendering, for promo map shots or whatever.\r\n\r\nlayout(location=0) varying vec2 texcoord;\r\n#ifdef VERTEX_SHADER\r\nvoid main()\r\n{\r\n\ttexcoord = v_texcoord.xy;\r\n\tgl_Position = ftetransform();\r\n}\r\n#endif\r\n#ifdef FRAGMENT_SHADER\r\nvoid main()\r\n{\r\n\tvec3 tc;\t\r\n\tfloat ang;\t\r\n\tang = texcoord.x*radians(cvar_ffov);\t\r\n\ttc.x = sin(ang);\t\r\n\ttc.y = -texcoord.y;\t\r\n\ttc.z = cos(ang);\t\r\n\tgl_FragColor = textureCube(s_reflectcube, tc);\r\n}\r\n#endif\r\n"
  },
  {
    "path": "engine/shaders/vulkan/postproc_stereographic.glsl",
    "content": "!!cvarf ffov=0\r\n!!samps reflectcube\r\n#include \"sys/defs.h\"\r\n\r\n//stereographic view rendering, for high fovs that are still playable.\r\n\r\nlayout(location=0) varying vec2 texcoord;\r\n#ifdef VERTEX_SHADER\r\n//uniform float cvar_ffov;\r\nvoid main()\r\n{\r\n\ttexcoord = v_texcoord.xy;\r\n\r\n\t//make sure the ffov cvar actually does something meaningful\r\n\ttexcoord *= cvar_ffov / 90.0;\r\n\r\n\tgl_Position = ftetransform();\r\n}\r\n#endif\r\n#ifdef FRAGMENT_SHADER\r\nvoid main()\r\n{\r\n\tvec3 tc;\t\r\n\tvec2 d;\t\r\n\tvec2 ang;\t\r\n\td = texcoord;\t\r\n\r\n\t//compute the 2d->3d projection\r\n\tfloat div = 1.0 + d.x*d.x + d.y*d.y;\r\n\ttc.x = 2.0*d.x/div;\r\n\ttc.y = -2.0*d.y/div;\r\n\ttc.z = -(-1.0 + d.x*d.x + d.y*d.y)/div;\r\n\r\n\tgl_FragColor = textureCube(s_reflectcube, tc);\r\n}\r\n#endif\r\n"
  },
  {
    "path": "engine/shaders/vulkan/rtlight.glsl",
    "content": "!!samps diffuse normalmap specular shadowmap upper lower reflectmask reflectcube projectionmap\r\n!!cvarf r_glsl_offsetmapping=0\r\n!!cvarf gl_specular=0\r\n!!cvarf r_glsl_offsetmapping_scale=0.04\r\n!!cvari r_glsl_pcf=5\r\n!!permu BUMP\r\n!!permu UPPERLOWER\r\n!!permu REFLECTCUBEMASK\r\n!!argb pcf=0\r\n!!argb spot=0\r\n!!argb cube=0\r\n!!permu FOG\r\n!!cvarb r_fog_exp2=true\r\n!!cvarb r_fog_linear=false\r\n//!!permu FRAMEBLEND\r\n//!!permu SKELETAL\r\n\r\n#include \"sys/defs.h\"\r\n//const bool UPPERLOWER = false;\r\n//const bool REFLECTCUBEMASK = false;\r\n//const bool BUMP = true;\r\n//const float cvar_gl_specular = 1.0;\r\n//const int cvar_r_glsl_pcf = 5;\r\n//const float cvar_r_glsl_offsetmapping = 0.0;\r\n//const float cvar_r_glsl_offsetmapping_scale = 0.04;\r\n//layout(constant_id=1) const bool arg_pcf = true;\r\n//layout(constant_id=2) const bool arg_spot = false;\r\n//layout(constant_id=3) const bool arg_cube = false;\r\n\r\n#define USE_ARB_SHADOW\r\n\r\n#ifndef USE_ARB_SHADOW\r\n//fall back on regular samplers if we must\r\n#define sampler2DShadow sampler2D\r\n#else\r\n#define shadow2D texture\r\n#endif\r\n\r\n//this is the main shader responsible for realtime dlights.\r\n\r\n//texture units:\r\n//s0=diffuse, s1=normal, s2=specular, s3=shadowmap\r\n//custom modifiers:\r\n//PCF(shadowmap)\r\n//CUBEPROJ(projected cubemap)\r\n//SPOT(projected circle\r\n//CUBESHADOW\r\n\r\n#if 0 && defined(GL_ARB_texture_gather) && defined(PCF) \r\n#extension GL_ARB_texture_gather : enable\r\n#endif\r\n\r\n//if there's no vertex normals known, disable some stuff.\r\n//FIXME: this results in dupe permutations.\r\n#ifdef NOBUMP\r\n#undef SPECULAR\r\n#undef BUMP\r\n#undef OFFSETMAPPING\r\n#endif\r\n\r\n\r\nlayout(location = 0) varying vec2 tc;\r\nlayout(location = 1) varying vec3 lightvector;\r\nlayout(location = 2) varying vec3 eyevector;\r\nlayout(location = 3) varying vec4 vtexprojcoord;\r\nlayout(location = 4) varying mat3 invsurface;\r\n#ifdef RAY_QUERY\r\nlayout(location = 7) varying vec3 wcoord;\r\n#endif\r\n\r\n\r\n#ifdef VERTEX_SHADER\r\n#include \"sys/skeletal.h\"\r\nvoid main ()\r\n{\r\n\tvec3 n, s, t, w;\r\n\tgl_Position = skeletaltransform_wnst(w,n,s,t);\r\n\ttc = v_texcoord;\t//pass the texture coords straight through\r\n\tvec3 lightminusvertex = (m_modelinv*vec4(l_lightposition,1.0)).xyz - w.xyz;\r\n\tif (true)//BUMP || SPECULAR)\r\n\t{\r\n\t\t//the light direction relative to the surface normal, for bumpmapping.\r\n\t\tlightvector.x = dot(lightminusvertex, s.xyz);\r\n\t\tlightvector.y = dot(lightminusvertex, t.xyz);\r\n\t\tlightvector.z = dot(lightminusvertex, n.xyz);\r\n\t}\r\n\telse\r\n\t\tlightvector = lightminusvertex;\r\n\tif (SPECULAR || OFFSETMAPPING || REFLECTCUBEMASK)\r\n\t{\r\n\t\tvec3 eyeminusvertex = e_eyepos - w.xyz;\r\n\t\teyevector.x = dot(eyeminusvertex, s.xyz);\r\n\t\teyevector.y = dot(eyeminusvertex, t.xyz);\r\n\t\teyevector.z = dot(eyeminusvertex, n.xyz);\r\n\t}\r\n\tif (REFLECTCUBEMASK)\r\n\t{\r\n\t\tinvsurface[0] = v_svector;\r\n\t\tinvsurface[1] = v_tvector;\r\n\t\tinvsurface[2] = v_normal;\r\n\t}\r\n\r\n\tif (arg_pcf || arg_spot || arg_cube)\r\n\t{\r\n\t\t//for texture projections/shadowmapping on dlights\r\n\t\tvtexprojcoord = l_cubematrix*m_model*vec4(w.xyz, 1.0);\r\n\t}\r\n#ifdef RAY_QUERY\r\n\twcoord = vec3(m_model*vec4(w+n*0.1, 1.0));\t//push it half a qu away from the face, so we're less likely to get precision errors in the rays.\r\n#endif\r\n}\r\n#endif\r\n\r\n\r\n\r\n\r\n#ifdef FRAGMENT_SHADER\r\n#include \"sys/fog.h\"\r\n\r\n#ifdef RAY_QUERY\r\nfloat RayQueryFilter(void)\r\n{\r\n\trayQueryEXT rq;\r\n//FIXME: no ortho\r\n#define l_origin e_eyepos\r\n\trayQueryInitializeEXT(rq, toplevelaccel, gl_RayFlagsTerminateOnFirstHitEXT, 0xFF, l_lightposition, 0.0, wcoord-l_lightposition, 1.0);\r\n\trayQueryProceedEXT(rq);\r\n//TODO: filter it through blended stuff, and alpha-tested stuff.\r\n\treturn float(rayQueryGetIntersectionTypeEXT(rq, true) == gl_RayQueryCommittedIntersectionNoneEXT);\r\n}\r\n#else\r\n//uniform vec4 l_shadowmapproj; //light projection matrix info\r\n//uniform vec2 l_shadowmapscale;\t//xy are the texture scale, z is 1, w is the scale.\r\nvec3 ShadowmapCoord(void)\r\n{\r\n\tif (arg_spot)\r\n\t{\r\n\t\t//bias it. don't bother figuring out which side or anything, its not needed\r\n\t\t//l_projmatrix contains the light's projection matrix so no other magic needed\r\n\t\treturn ((vtexprojcoord.yxz-vec3(0.0,0.0,0.015))/vtexprojcoord.w + vec3(1.0, -1.0, 1.0)) * vec3(0.5, -0.5, 0.5);\r\n\t}\r\n//\telse if (CUBESHADOW)\r\n//\t{\r\n//\t\tvec3 shadowcoord = vshadowcoord.xyz / vshadowcoord.w;\r\n//\t\t#define dosamp(x,y) shadowCube(s_shadowmap, shadowcoord + vec2(x,y)*texscale.xy).r\r\n//\t}\r\n\t//figure out which axis to use\r\n\t//texture is arranged thusly:\r\n\t//forward left  up\r\n\t//back    right down\r\n\tvec3 dir = abs(vtexprojcoord.xyz);\r\n\t//assume z is the major axis (ie: forward from the light)\r\n\tvec3 t = vtexprojcoord.xyz;\r\n\tfloat ma = dir.z;\r\n\tvec3 axis = vec3(0.5/3.0, 0.5/2.0, 0.5);\r\n\tif (dir.x > ma)\r\n\t{\r\n\t\tma = dir.x;\r\n\t\tt = vtexprojcoord.zyx;\r\n\t\taxis.x = 0.5;\r\n\t}\r\n\tif (dir.y > ma)\r\n\t{\r\n\t\tma = dir.y;\r\n\t\tt = vtexprojcoord.xzy;\r\n\t\taxis.x = 2.5/3.0;\r\n\t}\r\n\t//if the axis is negative, flip it.\r\n\tif (t.z > 0.0)\r\n\t{\r\n\t\taxis.y = 1.5/2.0;\r\n\t\tt.z = -t.z;\r\n\t}\r\n\r\n\t//we also need to pass the result through the light's projection matrix too\r\n\t//the 'matrix' we need only contains 5 actual values. and one of them is a -1. So we might as well just use a vec4.\r\n\t//note: the projection matrix also includes scalers to pinch the image inwards to avoid sampling over borders, as well as to cope with non-square source image\r\n\t//the resulting z is prescaled to result in a value between -0.5 and 0.5.\r\n\t//also make sure we're in the right quadrant type thing\r\n\treturn axis + ((l_shadowmapproj.xyz*t.xyz + vec3(0.0, 0.0, l_shadowmapproj.w)) / -t.z);\r\n}\r\n\r\nfloat ShadowmapFilter(void)\r\n{\r\n\tvec3 shadowcoord = ShadowmapCoord();\r\n\r\n\t#if 0//def GL_ARB_texture_gather\r\n\t\tvec2 ipart, fpart;\r\n\t\t#define dosamp(x,y) textureGatherOffset(s_shadowmap, ipart.xy, vec2(x,y)))\r\n\t\tvec4 tl = step(shadowcoord.z, dosamp(-1.0, -1.0));\r\n\t\tvec4 bl = step(shadowcoord.z, dosamp(-1.0, 1.0));\r\n\t\tvec4 tr = step(shadowcoord.z, dosamp(1.0, -1.0));\r\n\t\tvec4 br = step(shadowcoord.z, dosamp(1.0, 1.0));\r\n\t\t//we now have 4*4 results, woo\r\n\t\t//we can just average them for 1/16th precision, but that's still limited graduations\r\n\t\t//the middle four pixels are 'full strength', but we interpolate the sides to effectively give 3*3\r\n\t\tvec4 col =     vec4(tl.ba, tr.ba) + vec4(bl.rg, br.rg) + //middle two rows are full strength\r\n\t\t\t\tmix(vec4(tl.rg, tr.rg), vec4(bl.ba, br.ba), fpart.y); //top+bottom rows\r\n\t\treturn dot(mix(col.rgb, col.agb, fpart.x), vec3(1.0/9.0));\t//blend r+a, gb are mixed because its pretty much free and gives a nicer dot instruction instead of lots of adds.\r\n\r\n\t#else\r\n#ifdef USE_ARB_SHADOW\r\n\t\t//with arb_shadow, we can benefit from hardware acclerated pcf, for smoother shadows\r\n\t\t#define dosamp(x,y) shadow2D(s_shadowmap, shadowcoord.xyz + (vec3(x,y,0.0)*l_shadowmapscale.xyx)).r\r\n#else\r\n\t\t//this will probably be a bit blocky.\r\n\t\t#define dosamp(x,y) float(texture2D(s_shadowmap, shadowcoord.xy + (vec2(x,y)*l_shadowmapscale.xy)).r >= shadowcoord.z)\r\n#endif\r\n\t\tfloat s = 0.0;\r\n\t\tif (cvar_r_glsl_pcf < 5)\r\n\t\t{\r\n\t\t\ts += dosamp(0.0, 0.0);\r\n\t\t\treturn s;\r\n\t\t}\r\n\t\telse if (cvar_r_glsl_pcf < 9)\r\n\t\t{\r\n\t\t\ts += dosamp(-1.0, 0.0);\r\n\t\t\ts += dosamp(0.0, -1.0);\r\n\t\t\ts += dosamp(0.0, 0.0);\r\n\t\t\ts += dosamp(0.0, 1.0);\r\n\t\t\ts += dosamp(1.0, 0.0);\r\n\t\t\treturn s/5.0;\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\ts += dosamp(-1.0, -1.0);\r\n\t\t\ts += dosamp(-1.0, 0.0);\r\n\t\t\ts += dosamp(-1.0, 1.0);\r\n\t\t\ts += dosamp(0.0, -1.0);\r\n\t\t\ts += dosamp(0.0, 0.0);\r\n\t\t\ts += dosamp(0.0, 1.0);\r\n\t\t\ts += dosamp(1.0, -1.0);\r\n\t\t\ts += dosamp(1.0, 0.0);\r\n\t\t\ts += dosamp(1.0, 1.0);\r\n\t\t\treturn s/9.0;\r\n}\r\n\t#endif\r\n}\r\n#endif\r\n\r\n\r\n#include \"sys/offsetmapping.h\"\r\n\r\nvoid main ()\r\n{\r\n//read raw texture samples (offsetmapping munges the tex coords first)\r\n\tvec2 tcbase;\r\n\tif (OFFSETMAPPING)\r\n\t\ttcbase = offsetmap(s_normalmap, tc, eyevector);\r\n\telse\r\n\t\ttcbase = tc;\r\n#if defined(FLAT)\r\n\tvec3 bases = vec3(1.0);\r\n#else\r\n\tvec3 bases = vec3(texture2D(s_diffuse, tcbase));\r\n#endif\r\n\tif (UPPERLOWER)\r\n\t{\r\n\t\tvec4 uc = texture2D(s_upper, tcbase);\r\n\t\tbases.rgb += uc.rgb*e_uppercolour*uc.a;\r\n\r\n\t\tvec4 lc = texture2D(s_lower, tcbase);\r\n\t\tbases.rgb += lc.rgb*e_lowercolour*lc.a;\r\n\t}\r\n\tvec3 bumps;\r\n\tif (BUMP)\r\n\t\tbumps = normalize(vec3(texture2D(s_normalmap, tcbase)) - 0.5);\r\n\telse\r\n\t\tbumps = vec3(0.0,0.0,1.0);\r\n\r\n\tfloat colorscale = max(1.0 - (dot(lightvector, lightvector)/(l_lightradius*l_lightradius)), 0.0);\r\n\tvec3 diff;\r\n#ifdef NOBUMP\r\n\t//surface can only support ambient lighting, even for lights that try to avoid it.\r\n\tdiff = bases * (l_lightcolourscale.x+l_lightcolourscale.y);\r\n#else\r\n\tvec3 nl = normalize(lightvector);\r\n\tif (BUMP)\r\n\t\tdiff = bases * (l_lightcolourscale.x + l_lightcolourscale.y * max(dot(bumps, nl), 0.0));\r\n\telse\r\n\t{\r\n\t\t//we still do bumpmapping even without bumps to ensure colours are always sane. light.exe does it too.\r\n\t\tdiff = bases * (l_lightcolourscale.x + l_lightcolourscale.y * max(dot(vec3(0.0, 0.0, 1.0), nl), 0.0));\r\n\t}\r\n#endif\r\n\r\n\r\n\tif (SPECULAR)\r\n\t{\r\n\t\tvec4 specs = texture2D(s_specular, tcbase);\r\n\t\tvec3 halfdir = normalize(normalize(eyevector) + nl);\r\n\t\tfloat spec = pow(max(dot(halfdir, bumps), 0.0), 32.0 * specs.a);\r\n\t\tdiff += l_lightcolourscale.z * spec * specs.rgb;\r\n\t}\r\n\r\n\tif (REFLECTCUBEMASK)\r\n\t{\r\n\t\tvec3 rtc = reflect(-eyevector, bumps);\r\n\t\trtc = rtc.x*invsurface[0] + rtc.y*invsurface[1] + rtc.z*invsurface[2];\r\n\t\trtc = (m_model * vec4(rtc.xyz,0.0)).xyz;\r\n\t\tdiff += texture2D(s_reflectmask, tcbase).rgb * textureCube(s_reflectcube, rtc).rgb;\r\n\t}\r\n\r\n\tif (arg_cube)\r\n\t{\r\n\t\t/*filter the colour by the cubemap projection*/\r\n\t\tdiff *= textureCube(s_projectionmap, vtexprojcoord.xyz).rgb;\r\n\t}\r\n\r\n\tif (arg_spot)\r\n\t{\r\n\t\t/*filter the colour by the spotlight. discard anything behind the light so we don't get a mirror image*/\r\n\t\tif (vtexprojcoord.w < 0.0) discard;\r\n\t\tvec2 spot = ((vtexprojcoord.st)/vtexprojcoord.w);\r\n\t\tcolorscale*=1.0-(dot(spot,spot));\r\n\t}\r\n\r\n#ifdef RAY_QUERY\r\n\tcolorscale *= RayQueryFilter();\r\n#else\r\n\tif (arg_pcf)\r\n\t{\r\n\t\t/*filter the light by the shadowmap. logically a boolean, but we allow fractions for softer shadows*/\r\n//diff.rgb = (vtexprojcoord.xyz/vtexprojcoord.w) * 0.5 + 0.5;\r\n\t\tcolorscale *= ShadowmapFilter();\r\n//\t\tdiff = ShadowmapCoord();\r\n\t}\r\n#endif\r\n\r\n#if defined(PROJECTION)\r\n\t/*2d projection, not used*/\r\n//\tdiff *= texture2d(s_projectionmap, shadowcoord);\r\n#endif\r\n\r\n\tgl_FragColor.rgb = fog3additive(diff*colorscale*l_lightcolour);\r\n\r\n\tgl_FragColor.a = 1.0;\r\n}\r\n#endif\r\n\r\n"
  },
  {
    "path": "engine/shaders/vulkan/sys/defs.h",
    "content": "#ifdef VERTEX_SHADER\r\n#define attribute in\r\n#define varying out\r\n\r\nout gl_PerVertex\r\n{\r\n  vec4 gl_Position;\r\n};\r\n\r\n#else\r\n#define varying in\r\n#endif\r\n\r\nlayout(std140, binding=0) uniform entityblock\r\n{\r\n\tmat4 m_modelviewproj;\r\n\tmat4 m_model;\r\n\tmat4 m_modelinv;\r\n\tvec3 e_eyepos;\r\n\tfloat e_time;\r\n\tvec3 e_light_ambient;\tfloat epad1;\r\n\tvec3 e_light_dir;\tfloat epad2;\r\n\tvec3 e_light_mul;\tfloat epad3;\r\n\tvec4 e_lmscales[4];\r\n\tvec3 e_uppercolour;\tfloat epad4;\r\n\tvec3 e_lowercolour;\tfloat epad5;\r\n\tvec3 e_glowmod;\t\tfloat epad6;\r\n\tvec4 e_colourident;\r\n\tvec4 w_fogcolours;\r\n\tfloat w_fogdensity;\tfloat w_fogdepthbias;\t vec2 epad7;\r\n};\r\n#define e_lmscale (e_lmscales[0])\r\n\r\nlayout(std140, binding=1) uniform lightblock\r\n{\r\n\tmat4 l_cubematrix;\r\n\tvec3 l_lightposition; \tfloat lpad1;\r\n\tvec3 l_lightcolour; \t\tfloat lpad2;\r\n\tvec3 l_lightcolourscale; \tfloat l_lightradius;\r\n\tvec4 l_shadowmapproj;\r\n\tvec2 l_shadowmapscale;\tvec2 lpad3;\r\n};\r\n\r\n\r\n#ifdef VERTEX_SHADER\r\nlayout(location=0) attribute vec3 v_position;\r\nlayout(location=1) attribute vec2 v_texcoord;\r\nlayout(location=2) attribute vec4 v_colour;\r\nlayout(location=3) attribute vec2 v_lmcoord;\r\nlayout(location=4) attribute vec3 v_normal;\r\nlayout(location=5) attribute vec3 v_svector;\r\nlayout(location=6) attribute vec3 v_tvector;\r\n#endif\r\n#ifdef FRAGMENT_SHADER\r\nlayout(location=0) out vec4 outcolour;\r\n#define gl_FragColor outcolour\r\n#endif\r\n\r\n#define texture2D texture\r\n#define textureCube texture\r\n\r\n/*defined by front-end, with bindings that suit us\r\nuniform sampler2D s_t0;\r\nuniform sampler2D s_t1;\r\nuniform sampler2D s_t2;\r\nuniform sampler2D s_t3;\r\nuniform sampler2D s_t4;\r\nuniform sampler2D s_t5;\r\nuniform sampler2D s_t6;\r\nuniform sampler2D s_t7;\r\nuniform sampler2D s_diffuse;\r\nuniform sampler2D s_normalmap;\r\nuniform sampler2D s_specular;\r\nuniform sampler2D s_upper;\r\nuniform sampler2D s_lower;\r\nuniform sampler2D s_fullbright;\r\nuniform sampler2D s_paletted;\r\nuniform sampler2D s_shadowmap;\r\nuniform samplerCube s_projectionmap;\r\nuniform samplerCube s_reflectcube;\r\nuniform sampler2D s_reflectmask;\r\nuniform sampler2D s_lightmap;\r\n#define s_lightmap0 s_lightmap\r\nuniform sampler2D s_deluxmap;\r\n#define s_deluxmap0 s_deluxmap\r\nuniform sampler2D s_lightmap1;\r\nuniform sampler2D s_lightmap2;\r\nuniform sampler2D s_lightmap3;\r\nuniform sampler2D s_deluxmap1;\r\nuniform sampler2D s_deluxmap2\r\nuniform sampler2D s_deluxmap3;\r\n*/\r\n\r\n#ifdef VERTEX_SHADER\r\nvec4 ftetransform()\r\n{\r\n\tvec4 proj = (m_modelviewproj*vec4(v_position,1.0));\r\n\tproj.y *= -1;\r\n\tproj.z = (proj.z + proj.w) / 2.0;\r\n\treturn proj;\r\n}\r\n#endif\r\n"
  },
  {
    "path": "engine/shaders/vulkan/sys/fog.h",
    "content": "#ifdef FRAGMENT_SHADER\r\n\t#define w_fogcolour\t\tw_fogcolours.rgb\r\n\t#define w_fogalpha\t\tw_fogcolours.a\r\n\r\n\tvec3 fog3(in vec3 regularcolour)\r\n\t{\r\n\t\tif (!FOG)\r\n\t\t\treturn regularcolour;\r\n\n\t\tfloat z;\n\t\tfloat fac;\n\n\t\tif (cvar_r_fog_linear) {\n\t\t\tz = gl_FragCoord.z / gl_FragCoord.w;\r\n\t\t\tfac = (w_fogdensity - z) / (w_fogdensity - w_fogdepthbias);\n\t\t} else {\n\t\t\tz = w_fogdensity * gl_FragCoord.z / gl_FragCoord.w;\r\n\t\t\tz = max(0.0,z-w_fogdepthbias);\r\n\t\t\tif (cvar_r_fog_exp2)\r\n\t\t\t\tz *= z;\r\n\t\t\tfac = exp2(-(z * 1.442695));\r\n\t\t}\n\n\t\tfac = (1.0-w_fogalpha) + (clamp(fac, 0.0, 1.0)*w_fogalpha);\r\n\t\treturn mix(w_fogcolour, regularcolour, fac);\r\n\t}\r\n\tvec3 fog3additive(in vec3 regularcolour)\r\n\t{\r\n\t\tif (!FOG)\r\n\t\t\treturn regularcolour;\r\n\n\t\tfloat z;\n\t\tfloat fac;\n\n\t\tif (cvar_r_fog_linear) {\n\t\t\tz = gl_FragCoord.z / gl_FragCoord.w;\r\n\t\t\tfac = (w_fogdensity - z) / (w_fogdensity - w_fogdepthbias);\n\t\t} else {\n\t\t\tz = w_fogdensity * gl_FragCoord.z / gl_FragCoord.w;\r\n\t\t\tz = max(0.0,z-w_fogdepthbias);\r\n\t\t\tif (cvar_r_fog_exp2)\r\n\t\t\t\tz *= z;\r\n\t\t\tfac = exp2(-(z * 1.442695));\r\n\t\t}\n\n\t\tfac = (1.0-w_fogalpha) + (clamp(fac, 0.0, 1.0)*w_fogalpha);\r\n\t\treturn regularcolour * fac;\r\n\t}\r\n\tvec4 fog4(in vec4 regularcolour)\r\n\t{\r\n\t\tif (!FOG)\r\n\t\t\treturn regularcolour;\r\n\t\treturn vec4(fog3(regularcolour.rgb), 1.0) * regularcolour.a;\r\n\t}\r\n\tvec4 fog4additive(in vec4 regularcolour)\r\n\t{\r\n\t\tif (!FOG)\r\n\t\t\treturn regularcolour;\r\n\n\t\tfloat z;\n\t\tfloat fac;\n\n\t\tif (cvar_r_fog_linear) {\n\t\t\tz = gl_FragCoord.z / gl_FragCoord.w;\r\n\t\t\tfac = (w_fogdensity - z) / (w_fogdensity - w_fogdepthbias);\n\t\t} else {\n\t\t\tz = w_fogdensity * gl_FragCoord.z / gl_FragCoord.w;\r\n\t\t\tz = max(0.0,z-w_fogdepthbias);\r\n\t\t\tif (cvar_r_fog_exp2)\r\n\t\t\t\tz *= z;\r\n\t\t\tfac = exp2(-(z * 1.442695));\r\n\t\t}\n\n\t\tfac = (1.0-w_fogalpha) + (clamp(fac, 0.0, 1.0)*w_fogalpha);\r\n\t\treturn regularcolour * vec4(fac, fac, fac, 1.0);\r\n\t}\r\n\tvec4 fog4blend(in vec4 regularcolour)\r\n\t{\r\n\t\tif (!FOG)\r\n\t\t\treturn regularcolour;\r\n\n\t\tfloat z;\n\t\tfloat fac;\n\n\t\tif (cvar_r_fog_linear) {\n\t\t\tz = gl_FragCoord.z / gl_FragCoord.w;\r\n\t\t\tfac = (w_fogdensity - z) / (w_fogdensity - w_fogdepthbias);\n\t\t} else {\n\t\t\tz = w_fogdensity * gl_FragCoord.z / gl_FragCoord.w;\r\n\t\t\tz = max(0.0,z-w_fogdepthbias);\r\n\t\t\tif (cvar_r_fog_exp2)\r\n\t\t\t\tz *= z;\r\n\t\t\tfac = exp2(-(z * 1.442695));\r\n\t\t}\n\n\t\tfac = (1.0-w_fogalpha) + (clamp(fac, 0.0, 1.0)*w_fogalpha);\r\n\t\treturn regularcolour * vec4(1.0, 1.0, 1.0, fac);\r\n\t}\r\n#endif"
  },
  {
    "path": "engine/shaders/vulkan/sys/offsetmapping.h",
    "content": "vec2 offsetmap(sampler2D normtex, vec2 base, vec3 eyevector)\r\n{\r\n#if !defined(OFFSETMAPPING_SCALE)\r\n\t#define OFFSETMAPPING_SCALE 1.0\r\n#endif\r\n\tif (false)//(RELIEFMAPPING)\r\n\t{\r\n\t\tfloat i, f;\r\n\t\tvec3 OffsetVector = vec3(normalize(eyevector.xyz).xy * cvar_r_glsl_offsetmapping_scale * OFFSETMAPPING_SCALE * vec2(-1.0, 1.0), -1.0);\r\n\t\tvec3 RT = vec3(vec2(base.xy/* - OffsetVector.xy*OffsetMapping_Bias*/), 1.0);\r\n\t\tOffsetVector /= 10.0;\r\n\t\tfor(i = 1.0; i < 10.0; ++i)\r\n\t\t\tRT += OffsetVector *  step(texture2D(normtex, RT.xy).a, RT.z);\r\n\t\tfor(i = 0.0, f = 1.0; i < 5.0; ++i, f *= 0.5)\r\n\t\t\tRT += OffsetVector * (step(texture2D(normtex, RT.xy).a, RT.z) * f - 0.5 * f);\r\n\t\treturn RT.xy;\r\n\t}\r\n\telse if (OFFSETMAPPING)\r\n\t{\r\n\t\tvec2 OffsetVector = normalize(eyevector).xy * cvar_r_glsl_offsetmapping_scale * OFFSETMAPPING_SCALE * vec2(-1.0, 1.0);\r\n\t\tvec2 tc = base;\r\n\t\ttc += OffsetVector;\r\n\t\tOffsetVector *= 0.333;\r\n\t\ttc -= OffsetVector * texture2D(normtex, tc).w;\r\n\t\ttc -= OffsetVector * texture2D(normtex, tc).w;\r\n\t\ttc -= OffsetVector * texture2D(normtex, tc).w;\r\n\t\treturn tc;\r\n\t}\r\n\treturn base;\r\n}\r\n"
  },
  {
    "path": "engine/shaders/vulkan/sys/skeletal.h",
    "content": "#ifdef SKELETAL\r\n\tvec4 skeletaltransform()\r\n\t{\r\n\t\tmat3x4 wmat;\r\n\t\twmat = m_bones[int(v_bone.x)] * v_weight.x;\r\n\t\twmat += m_bones[int(v_bone.y)] * v_weight.y;\r\n\t\twmat += m_bones[int(v_bone.z)] * v_weight.z;\r\n\t\twmat += m_bones[int(v_bone.w)] * v_weight.w;\r\n\t\treturn m_modelviewprojection * vec4(vec4(v_position.xyz, 1.0) * wmat, 1.0);\r\n\t}\r\n\tvec4 skeletaltransform_nst(out vec3 n, out vec3 t, out vec3 b)\r\n\t{\r\n\t\tmat3x4 wmat;\r\n\t\twmat = m_bones[int(v_bone.x)] * v_weight.x;\r\n\t\twmat += m_bones[int(v_bone.y)] * v_weight.y;\r\n\t\twmat += m_bones[int(v_bone.z)] * v_weight.z;\r\n\t\twmat += m_bones[int(v_bone.w)] * v_weight.w;\r\n\t\tn = vec4(v_normal.xyz, 0.0) * wmat;\r\n\t\tt = vec4(v_svector.xyz, 0.0) * wmat;\r\n\t\tb = vec4(v_tvector.xyz, 0.0) * wmat;\r\n\t\treturn m_modelviewprojection * vec4(vec4(v_position.xyz, 1.0) * wmat, 1.0);\r\n\t}\r\n\tvec4 skeletaltransform_wnst(out vec3 w, out vec3 n, out vec3 t, out vec3 b)\r\n\t{\r\n\t\tmat3x4 wmat;\r\n\t\twmat = m_bones[int(v_bone.x)] * v_weight.x;\r\n\t\twmat += m_bones[int(v_bone.y)] * v_weight.y;\r\n\t\twmat += m_bones[int(v_bone.z)] * v_weight.z;\r\n\t\twmat += m_bones[int(v_bone.w)] * v_weight.w;\r\n\t\tn = vec4(v_normal.xyz, 0.0) * wmat;\r\n\t\tt = vec4(v_svector.xyz, 0.0) * wmat;\r\n\t\tb = vec4(v_tvector.xyz, 0.0) * wmat;\r\n\t\tw = vec4(v_position.xyz, 1.0) * wmat;\r\n\t\treturn m_modelviewprojection * vec4(w, 1.0);\r\n\t}\r\n\tvec4 skeletaltransform_n(out vec3 n)\r\n\t{\r\n\t\tmat3x4 wmat;\r\n\t\twmat = m_bones[int(v_bone.x)] * v_weight.x;\r\n\t\twmat += m_bones[int(v_bone.y)] * v_weight.y;\r\n\t\twmat += m_bones[int(v_bone.z)] * v_weight.z;\r\n\t\twmat += m_bones[int(v_bone.w)] * v_weight.w;\r\n\t\tn = vec4(v_normal.xyz, 0.0) * wmat;\r\n\t\treturn m_modelviewprojection * vec4(vec4(v_position.xyz, 1.0) * wmat, 1.0);\r\n\t}\r\n#else\r\n\t#define skeletaltransform ftetransform\r\n\tvec4 skeletaltransform_wnst(out vec3 w, out vec3 n, out vec3 t, out vec3 b)\r\n\t{\r\n\t\tn = v_normal;\r\n\t\tt = v_svector;\r\n\t\tb = v_tvector;\r\n\t\tw = v_position.xyz;\r\n\t\treturn ftetransform();\r\n\t}\r\n\tvec4 skeletaltransform_nst(out vec3 n, out vec3 t, out vec3 b)\r\n\t{\r\n\t\tn = v_normal;\r\n\t\tt = v_svector;\r\n\t\tb = v_tvector;\r\n\t\treturn ftetransform();\r\n\t}\r\n\tvec4 skeletaltransform_n(out vec3 n)\r\n\t{\r\n\t\tn = v_normal;\r\n\t\treturn ftetransform();\r\n\t}\r\n#endif"
  },
  {
    "path": "engine/shaders/vulkan/underwaterwarp.glsl",
    "content": "!!cvarf r_waterwarp=1\r\n!!samps 3\r\n\r\n#include \"sys/defs.h\"\r\n\r\n//this is a post processing shader that is drawn fullscreen whenever the view is underwater.\r\n//its generally expected to warp the view a little.\r\n\r\nlayout(location=0) varying vec2 v_stc;\r\nlayout(location=1) varying vec2 v_warp;\r\nlayout(location=2) varying vec2 v_edge;\r\n\r\n#ifdef VERTEX_SHADER\r\nvoid main ()\r\n{\r\n\tgl_Position = ftetransform();\r\n\tv_stc = vec2(v_texcoord.x, /*1.0-*/v_texcoord.y);\r\n\tv_warp.s = e_time * 0.25 + v_texcoord.s;\r\n\tv_warp.t = e_time * 0.25 + v_texcoord.t;\r\n\tv_edge = v_texcoord.xy;\r\n}\r\n#endif\r\n#ifdef FRAGMENT_SHADER\r\n//uniform sampler2D s_t0;/*$currentrender*/\r\n//uniform sampler2D s_t1;/*warp image*/\r\n//uniform sampler2D s_t2;/*edge image*/\r\n//uniform vec4 e_rendertexturescale;\r\n//uniform float cvar_r_waterwarp;\r\n#define e_rendertexturescale vec4(1.0,1.0,0.0,0.0)\r\nvoid main ()\r\n{\r\n\tvec2 amp\t\t= (0.010 / 0.625) * cvar_r_waterwarp * texture2D(s_t2, v_edge).rg;\r\n\tvec3 offset\t= (texture2D(s_t1, v_warp).rgb - 0.5) * 2.0;\r\n\tvec2 temp\t\t= v_stc + offset.xy * amp;\r\n\tgl_FragColor\t= texture2D(s_t0, temp*e_rendertexturescale.st);\r\n}\r\n#endif\r\n"
  },
  {
    "path": "engine/sw/sw.h",
    "content": "\ntypedef struct\n{\n\tint pwidth;\n\tint pheight;\n\tint pwidthmask;\n\tint pheightmask;\n\tint pitch;\n\tunsigned int data[1];\n} swimage_t;\n\ntypedef struct \n{\n\tfloat matrix[16];\n\tvec4_t viewplane;\n} swuniforms_t;\n\ntypedef struct\n{\n\tvolatile unsigned int readpoint;\t//the command queue point its reading from\n\tvoid *thread;\n\n#ifdef _DEBUG\n\tfloat idletime;\n\tfloat activetime;\n#endif\n\n\tunsigned int interlaceline;\n\tunsigned int interlacemod;\n\tunsigned int threadnum;\t//for relocating viewport info\n\tunsigned int *vpdbuf;\n\tunsigned int *vpcbuf;\n\tunsigned int vpwidth;\n\tunsigned int vpheight;\n\tswuniforms_t u;\n\tqintptr_t vpcstride;\n\tstruct workqueue_s *wq;\n} swthread_t;\n\ntypedef struct\n{\n\tint scoord[2];\n\tfloat zicoord;\n\tvec4_t vcoord;\n\tvec2_t tccoord;\n\tvec2_t lmcoord;\n\tbyte_vec4_t colour;\n\tunsigned int clipflags;\t/*1=left,2=right,4=top,8=bottom,16=near*/\n} swvert_t;\n\n#define WQ_SIZE 1024*1024*8\n#define WQ_MASK (WQ_SIZE-1)\n#define WQ_MAXTHREADS 64\nstruct workqueue_s\n{\n\tunsigned int numthreads;\n\tqbyte queue[WQ_SIZE];\n\tvolatile unsigned int pos;\n\n\tswthread_t swthreads[WQ_MAXTHREADS];\n};\nextern struct workqueue_s commandqueue;\n\n\n\nenum wqcmd_e\n{\n\tWTC_DIE,\n\tWTC_SYNC,\n\tWTC_NEWFRAME,\n\tWTC_NOOP,\n\tWTC_VIEWPORT,\n\tWTC_TRIFAN,\n\tWTC_TRISOUP,\n\tWTC_SPANS,\n\tWTC_UNIFORMS\n};\n\nenum\n{\n\tCLIP_LEFT_FLAG\t\t= 1,\n\tCLIP_RIGHT_FLAG\t\t= 2,\n\tCLIP_TOP_FLAG\t\t= 4,\n\tCLIP_BOTTOM_FLAG\t= 8,\n\tCLIP_NEAR_FLAG\t\t= 16,\n\tCLIP_FAR_FLAG\t\t= 32\n};\n\ntypedef union\n{\n\tunsigned char align[16];\n\n\tstruct wqcom_s\n\t{\n\t\tenum wqcmd_e command;\n\t\tunsigned int cmdsize;\n\t} com;\n\tstruct\n\t{\n\t\tstruct wqcom_s com;\n\n\t\tswimage_t *texture;\n\t\tint numverts;\n\t\tswvert_t verts[1];\n\t} trifan;\n\tstruct\n\t{\n\t\tstruct wqcom_s com;\n\n\t\tswimage_t *texture;\n\t\tint numverts;\n\t\tint numidx;\n\t\tswvert_t verts[1];\n\t} trisoup;\n\tstruct\n\t{\n\t\tstruct wqcom_s com;\n\n\t\tunsigned int *cbuf;\n\t\tunsigned int *dbuf;\n\t\tunsigned int width;\n\t\tunsigned int height;\n\t\tqintptr_t stride;\n\t\tunsigned int interlace;\n\t\tunsigned int framenum;\n\n\t\tqboolean cleardepth;\n\t\tqboolean clearcolour;\n\t} viewport;\n\tstruct\n\t{\n\t\tstruct wqcom_s com;\n\t\tswuniforms_t u;\n\t} uniforms;\n\tstruct\n\t{\n\t\tint foo;\n\t} spans;\n} wqcom_t;\n\n\n\nvoid SWRast_EndCommand(struct workqueue_s *wq, wqcom_t *com);\nwqcom_t *SWRast_BeginCommand(struct workqueue_s *wq, int cmdtype, unsigned int size);\nvoid SWRast_Sync(struct workqueue_s *wq);\n\n\n\nqboolean SW_VID_Init(rendererstate_t *info, unsigned char *palette);\nvoid SW_VID_DeInit(void);\nqboolean SW_VID_ApplyGammaRamps\t\t(unsigned int rampcount, unsigned short *ramps);\nchar *SW_VID_GetRGBInfo(int *bytestride, int *truevidwidth, int *truevidheight, enum uploadfmt *fmt);\nvoid SW_VID_SetWindowCaption(const char *msg);\nvoid SW_VID_SwapBuffers(void);\nvoid SW_VID_UpdateViewport(wqcom_t *com);\n\n\n\n\nvoid\t\tSW_UpdateFiltering\t\t(image_t *imagelist, int filtermip[3], int filterpic[3], int mipcap[2], float anis);\nqboolean\tSW_LoadTextureMips\t\t(texid_t tex, const struct pendingtextureinfo *mips);\nvoid\t\tSW_DestroyTexture\t\t(texid_t tex);\n\n\nvoid SWBE_SelectMode(backendmode_t mode);\nvoid SWBE_DrawMesh_List(shader_t *shader, int nummeshes, struct mesh_s **mesh, struct vbo_s *vbo, struct texnums_s *texnums, unsigned int be_flags);\nvoid SWBE_DrawMesh_Single(shader_t *shader, struct mesh_s *meshchain, struct vbo_s *vbo, unsigned int be_flags);\nvoid SWBE_SubmitBatch(struct batch_s *batch);\nstruct batch_s *SWBE_GetTempBatch(void);\nvoid SWBE_DrawWorld(batch_t **worldbatches);\nvoid SWBE_Init(void);\nvoid SWBE_GenBrushModelVBO(struct model_s *mod);\nvoid SWBE_ClearVBO(struct vbo_s *vbo, qboolean dataonly);\nvoid SWBE_UploadAllLightmaps(void);\nvoid SWBE_SelectEntity(struct entity_s *ent);\nqboolean SWBE_SelectDLight(struct dlight_s *dl, vec3_t colour, vec3_t axis[3], unsigned int lmode);\nqboolean SWBE_LightCullModel(vec3_t org, struct model_s *model);\nvoid SWBE_RenderToTextureUpdate2d(qboolean destchanged);\nvoid SWBE_Set2D(void);\n"
  },
  {
    "path": "engine/sw/sw_backend.c",
    "content": "#include \"quakedef.h\"\r\n#ifdef SWQUAKE\r\n#include \"sw.h\"\r\n#include \"shader.h\"\r\n#include \"glquake.h\"\r\n\r\nvecV_t vertbuf[65535];\r\n\r\nswimage_t sw_nulltex =\r\n{\r\n\t1, 1, 0, 0, 0, 0\r\n};\r\n\r\nstatic struct\r\n{\r\n\tint foo;\r\n\tint numrthreads;\r\n\tvoid *threads[4];\r\n\tbackendmode_t mode;\r\n\r\n\tfloat m_mvp[16];\r\n\tvec4_t viewplane;\r\n\r\n\tentity_t *curentity;\r\n\tshader_t *curshader;\r\n\r\n\tfloat curtime;\r\n\t//this stuff should probably be moved out of the backend\r\n\tint wbatch;\r\n\tint maxwbatches;\r\n\tbatch_t *wbatches;\r\n} shaderstate;\r\n\r\n////////////////////////////////////////////////////////////////\r\n//start generic tables \r\n#define frand() (rand()*(1.0/RAND_MAX))\r\n#define FTABLE_SIZE\t\t1024\r\n#define FTABLE_CLAMP(x)\t(((int)((x)*FTABLE_SIZE) & (FTABLE_SIZE-1)))\r\n#define FTABLE_EVALUATE(table,x) (table ? table[FTABLE_CLAMP(x)] : frand()*((x)-floor(x)))\r\n\r\nstatic\tfloat\tr_sintable[FTABLE_SIZE];\r\nstatic\tfloat\tr_triangletable[FTABLE_SIZE];\r\nstatic\tfloat\tr_squaretable[FTABLE_SIZE];\r\nstatic\tfloat\tr_sawtoothtable[FTABLE_SIZE];\r\nstatic\tfloat\tr_inversesawtoothtable[FTABLE_SIZE];\r\n\r\nstatic float *FTableForFunc ( unsigned int func )\r\n{\r\n\tswitch (func)\r\n\t{\r\n\t\tcase SHADER_FUNC_SIN:\r\n\t\t\treturn r_sintable;\r\n\r\n\t\tcase SHADER_FUNC_TRIANGLE:\r\n\t\t\treturn r_triangletable;\r\n\r\n\t\tcase SHADER_FUNC_SQUARE:\r\n\t\t\treturn r_squaretable;\r\n\r\n\t\tcase SHADER_FUNC_SAWTOOTH:\r\n\t\t\treturn r_sawtoothtable;\r\n\r\n\t\tcase SHADER_FUNC_INVERSESAWTOOTH:\r\n\t\t\treturn r_inversesawtoothtable;\r\n\t}\r\n\r\n\t//bad values allow us to crash (so I can debug em)\r\n\treturn NULL;\r\n}\r\n\r\nstatic void BE_InitTables(void)\r\n{\r\n\tint i;\r\n\tdouble t;\r\n\tfor (i = 0; i < FTABLE_SIZE; i++)\r\n\t{\r\n\t\tt = (double)i / (double)FTABLE_SIZE;\r\n\r\n\t\tr_sintable[i] = sin(t * 2*M_PI);\r\n\r\n\t\tif (t < 0.25)\r\n\t\t\tr_triangletable[i] = t * 4.0;\r\n\t\telse if (t < 0.75)\r\n\t\t\tr_triangletable[i] = 2 - 4.0 * t;\r\n\t\telse\r\n\t\t\tr_triangletable[i] = (t - 0.75) * 4.0 - 1.0;\r\n\r\n\t\tif (t < 0.5)\r\n\t\t\tr_squaretable[i] = 1.0f;\r\n\t\telse\r\n\t\t\tr_squaretable[i] = -1.0f;\r\n\r\n\t\tr_sawtoothtable[i] = t;\r\n\t\tr_inversesawtoothtable[i] = 1.0 - t;\r\n\t}\r\n}\r\n#define R_FastSin(x) sin((x)*(2*M_PI))\t//fixme: use r_sintable instead!\r\n\r\n//end generic tables \r\n////////////////////////////////////////////////////////////////\r\n//start matrix functions\r\n\r\ntypedef vec3_t mat3_t[3];\r\nstatic mat3_t axisDefault={{1, 0, 0},\r\n\t\t\t\t\t{0, 1, 0},\r\n\t\t\t\t\t{0, 0, 1}};\r\n\r\nstatic void Matrix3_Transpose (mat3_t in, mat3_t out)\r\n{\r\n\tout[0][0] = in[0][0];\r\n\tout[1][1] = in[1][1];\r\n\tout[2][2] = in[2][2];\r\n\r\n\tout[0][1] = in[1][0];\r\n\tout[0][2] = in[2][0];\r\n\tout[1][0] = in[0][1];\r\n\tout[1][2] = in[2][1];\r\n\tout[2][0] = in[0][2];\r\n\tout[2][1] = in[1][2];\r\n}\r\nstatic void Matrix3_Multiply_Vec3 (mat3_t a, vec3_t b, vec3_t product)\r\n{\r\n\tproduct[0] = a[0][0]*b[0] + a[0][1]*b[1] + a[0][2]*b[2];\r\n\tproduct[1] = a[1][0]*b[0] + a[1][1]*b[1] + a[1][2]*b[2];\r\n\tproduct[2] = a[2][0]*b[0] + a[2][1]*b[1] + a[2][2]*b[2];\r\n}\r\n\r\n//static int Matrix3_Compare(mat3_t in, mat3_t out)\r\n//{\r\n//\treturn memcmp(in, out, sizeof(mat3_t));\r\n//}\r\n\r\n//end matrix functions\r\n////////////////////////////////////////////////////////////////\r\n//start xyz\r\n\r\nstatic void deformgen(const deformv_t *deformv, int cnt, vecV_t *src, vecV_t *dst, const mesh_t *mesh)\r\n{\r\n\tfloat *table;\r\n\tint j, k;\r\n\tfloat args[4];\r\n\tfloat deflect;\r\n\tswitch (deformv->type)\r\n\t{\r\n\tdefault:\r\n\tcase DEFORMV_NONE:\r\n\t\tif (src != dst)\r\n\t\t\tmemcpy(dst, src, sizeof(*src)*cnt);\r\n\t\tbreak;\r\n\r\n\tcase DEFORMV_WAVE:\r\n\t\tif (!mesh->normals_array)\r\n\t\t{\r\n\t\t\tif (src != dst)\r\n\t\t\t\tmemcpy(dst, src, sizeof(*src)*cnt);\r\n\t\t\treturn;\r\n\t\t}\r\n\t\targs[0] = deformv->func.args[0];\r\n\t\targs[1] = deformv->func.args[1];\r\n\t\targs[3] = deformv->func.args[2] + deformv->func.args[3] * shaderstate.curtime;\r\n\t\ttable = FTableForFunc(deformv->func.type);\r\n\r\n\t\tfor ( j = 0; j < cnt; j++ )\r\n\t\t{\r\n\t\t\tdeflect = deformv->args[0] * (src[j][0]+src[j][1]+src[j][2]) + args[3];\r\n\t\t\tdeflect = FTABLE_EVALUATE(table, deflect) * args[1] + args[0];\r\n\r\n\t\t\t// Deflect vertex along its normal by wave amount\r\n\t\t\tVectorMA(src[j], deflect, mesh->normals_array[j], dst[j]);\r\n\t\t}\r\n\t\tbreak;\r\n\r\n\tcase DEFORMV_NORMAL:\r\n\t\t//normal does not actually move the verts, but it does change the normals array\r\n\t\t//we don't currently support that.\r\n\t\tif (src != dst)\r\n\t\t\tmemcpy(dst, src, sizeof(*src)*cnt);\r\n/*\r\n\t\targs[0] = deformv->args[1] * shaderstate.curtime;\r\n\r\n\t\tfor ( j = 0; j < cnt; j++ )\r\n\t\t{\r\n\t\t\targs[1] = normalsArray[j][2] * args[0];\r\n\r\n\t\t\tdeflect = deformv->args[0] * R_FastSin(args[1]);\r\n\t\t\tnormalsArray[j][0] *= deflect;\r\n\t\t\tdeflect = deformv->args[0] * R_FastSin(args[1] + 0.25);\r\n\t\t\tnormalsArray[j][1] *= deflect;\r\n\t\t\tVectorNormalizeFast(normalsArray[j]);\r\n\t\t}\r\n*/\t\tbreak;\r\n\r\n\tcase DEFORMV_MOVE:\r\n\t\ttable = FTableForFunc(deformv->func.type);\r\n\t\tdeflect = deformv->func.args[2] + shaderstate.curtime * deformv->func.args[3];\r\n\t\tdeflect = FTABLE_EVALUATE(table, deflect) * deformv->func.args[1] + deformv->func.args[0];\r\n\r\n\t\tfor ( j = 0; j < cnt; j++ )\r\n\t\t\tVectorMA(src[j], deflect, deformv->args, dst[j]);\r\n\t\tbreak;\r\n\r\n\tcase DEFORMV_BULGE:\r\n\t\targs[0] = deformv->args[0]/(2*M_PI);\r\n\t\targs[1] = deformv->args[1];\r\n\t\targs[2] = shaderstate.curtime * deformv->args[2]/(2*M_PI);\r\n\r\n\t\tfor (j = 0; j < cnt; j++)\r\n\t\t{\r\n\t\t\tdeflect = R_FastSin(mesh->st_array[j][0]*args[0] + args[2])*args[1];\r\n\t\t\tdst[j][0] = src[j][0]+deflect*mesh->normals_array[j][0];\r\n\t\t\tdst[j][1] = src[j][1]+deflect*mesh->normals_array[j][1];\r\n\t\t\tdst[j][2] = src[j][2]+deflect*mesh->normals_array[j][2];\r\n\t\t}\r\n\t\tbreak;\r\n\r\n\tcase DEFORMV_AUTOSPRITE:\r\n\t\tif (mesh->numindexes < 6)\r\n\t\t\tbreak;\r\n\r\n\t\tfor (j = 0; j < cnt-3; j+=4, src+=4, dst+=4)\r\n\t\t{\r\n\t\t\tvec3_t mid, d;\r\n\t\t\tfloat radius;\r\n\t\t\tmid[0] = 0.25*(src[0][0] + src[1][0] + src[2][0] + src[3][0]);\r\n\t\t\tmid[1] = 0.25*(src[0][1] + src[1][1] + src[2][1] + src[3][1]);\r\n\t\t\tmid[2] = 0.25*(src[0][2] + src[1][2] + src[2][2] + src[3][2]);\r\n\t\t\tVectorSubtract(src[0], mid, d);\r\n\t\t\tradius = 2*VectorLength(d);\r\n\r\n\t\t\tfor (k = 0; k < 4; k++)\r\n\t\t\t{\r\n\t\t\t\tdst[k][0] = mid[0] + radius*((mesh->st_array[j+k][0]-0.5)*r_refdef.m_view[0+0]-(mesh->st_array[j+k][1]-0.5)*r_refdef.m_view[0+1]);\r\n\t\t\t\tdst[k][1] = mid[1] + radius*((mesh->st_array[j+k][0]-0.5)*r_refdef.m_view[4+0]-(mesh->st_array[j+k][1]-0.5)*r_refdef.m_view[4+1]);\r\n\t\t\t\tdst[k][2] = mid[2] + radius*((mesh->st_array[j+k][0]-0.5)*r_refdef.m_view[8+0]-(mesh->st_array[j+k][1]-0.5)*r_refdef.m_view[8+1]);\r\n\t\t\t}\r\n\t\t}\r\n\t\tbreak;\r\n\r\n\tcase DEFORMV_AUTOSPRITE2:\r\n\t\tif (mesh->numindexes < 6)\r\n\t\t\tbreak;\r\n\r\n\t\tfor (k = 0; k < mesh->numindexes; k += 6)\r\n\t\t{\r\n\t\t\tint long_axis, short_axis;\r\n\t\t\tvec3_t axis;\r\n\t\t\tfloat len[3];\r\n\t\t\tmat3_t m0, m1, m2, result;\r\n\t\t\tfloat *quad[4];\r\n\t\t\tvec3_t rot_centre, tv, tv2;\r\n\r\n\t\t\tquad[0] = (float *)(src + mesh->indexes[k+0]);\r\n\t\t\tquad[1] = (float *)(src + mesh->indexes[k+1]);\r\n\t\t\tquad[2] = (float *)(src + mesh->indexes[k+2]);\r\n\r\n\t\t\tfor (j = 2; j >= 0; j--)\r\n\t\t\t{\r\n\t\t\t\tquad[3] = (float *)(src + mesh->indexes[k+3+j]);\r\n\t\t\t\tif (!VectorEquals (quad[3], quad[0]) &&\r\n\t\t\t\t\t!VectorEquals (quad[3], quad[1]) &&\r\n\t\t\t\t\t!VectorEquals (quad[3], quad[2]))\r\n\t\t\t\t{\r\n\t\t\t\t\tbreak;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\t// build a matrix were the longest axis of the billboard is the Y-Axis\r\n\t\t\tVectorSubtract(quad[1], quad[0], m0[0]);\r\n\t\t\tVectorSubtract(quad[2], quad[0], m0[1]);\r\n\t\t\tVectorSubtract(quad[2], quad[1], m0[2]);\r\n\t\t\tlen[0] = DotProduct(m0[0], m0[0]);\r\n\t\t\tlen[1] = DotProduct(m0[1], m0[1]);\r\n\t\t\tlen[2] = DotProduct(m0[2], m0[2]);\r\n\r\n\t\t\tif ((len[2] > len[1]) && (len[2] > len[0]))\r\n\t\t\t{\r\n\t\t\t\tif (len[1] > len[0])\r\n\t\t\t\t{\r\n\t\t\t\t\tlong_axis = 1;\r\n\t\t\t\t\tshort_axis = 0;\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\tlong_axis = 0;\r\n\t\t\t\t\tshort_axis = 1;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\telse if ((len[1] > len[2]) && (len[1] > len[0]))\r\n\t\t\t{\r\n\t\t\t\tif (len[2] > len[0])\r\n\t\t\t\t{\r\n\t\t\t\t\tlong_axis = 2;\r\n\t\t\t\t\tshort_axis = 0;\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\tlong_axis = 0;\r\n\t\t\t\t\tshort_axis = 2;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\telse //if ( (len[0] > len[1]) && (len[0] > len[2]) )\r\n\t\t\t{\r\n\t\t\t\tif (len[2] > len[1])\r\n\t\t\t\t{\r\n\t\t\t\t\tlong_axis = 2;\r\n\t\t\t\t\tshort_axis = 1;\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\tlong_axis = 1;\r\n\t\t\t\t\tshort_axis = 2;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\tif (DotProduct(m0[long_axis], m0[short_axis]))\r\n\t\t\t{\r\n\t\t\t\tVectorNormalize2(m0[long_axis], axis);\r\n\t\t\t\tVectorCopy(axis, m0[1]);\r\n\r\n\t\t\t\tif (axis[0] || axis[1])\r\n\t\t\t\t{\r\n\t\t\t\t\tVectorVectors(m0[1], m0[2], m0[0]);\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\tVectorVectors(m0[1], m0[0], m0[2]);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tVectorNormalize2(m0[long_axis], axis);\r\n\t\t\t\tVectorNormalize2(m0[short_axis], m0[0]);\r\n\t\t\t\tVectorCopy(axis, m0[1]);\r\n\t\t\t\tCrossProduct(m0[0], m0[1], m0[2]);\r\n\t\t\t}\r\n\r\n\t\t\tfor (j = 0; j < 3; j++)\r\n\t\t\t\trot_centre[j] = (quad[0][j] + quad[1][j] + quad[2][j] + quad[3][j]) * 0.25;\r\n\r\n\t\t\tif (shaderstate.curentity)\r\n\t\t\t{\r\n\t\t\t\tVectorAdd(shaderstate.curentity->origin, rot_centre, tv);\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tVectorCopy(rot_centre, tv);\r\n\t\t\t}\r\n\t\t\tVectorSubtract(r_origin, tv, tv);\r\n\r\n\t\t\t// filter any longest-axis-parts off the camera-direction\r\n\t\t\tdeflect = -DotProduct(tv, axis);\r\n\r\n\t\t\tVectorMA(tv, deflect, axis, m1[2]);\r\n\t\t\tVectorNormalizeFast(m1[2]);\r\n\t\t\tVectorCopy(axis, m1[1]);\r\n\t\t\tCrossProduct(m1[1], m1[2], m1[0]);\r\n\r\n\t\t\tMatrix3_Transpose(m1, m2);\r\n\t\t\tMatrix3_Multiply(m2, m0, result);\r\n\r\n\t\t\tfor (j = 0; j < 4; j++)\r\n\t\t\t{\r\n\t\t\t\tint v = ((vecV_t*)quad[j]-src);\r\n\t\t\t\tVectorSubtract(quad[j], rot_centre, tv);\r\n\t\t\t\tMatrix3_Multiply_Vec3(result, tv, tv2);\r\n\t\t\t\tVectorAdd(rot_centre, tv2, dst[v]);\r\n\t\t\t}\r\n\t\t}\r\n\t\tbreak;\r\n\r\n//\tcase DEFORMV_PROJECTION_SHADOW:\r\n//\t\tbreak;\r\n\t}\r\n}\r\n//end xyz\r\n////////////////////////////////////////////////////////////////\r\n\r\nvoid SWBE_SelectMode(backendmode_t mode)\r\n{\r\n}\r\n\r\nvoid SWBE_TransformVerticies(swvert_t *v, mesh_t *mesh)\r\n{\r\n\tint i;\r\n\r\n\tvecV_t *xyz;\r\n\r\n\t/*generate vertex blends*/\r\n\tif (mesh->xyz2_array)\r\n\t{\r\n\t\txyz = vertbuf;\r\n\t\tfor (i = 0; i < mesh->numvertexes; i++)\r\n\t\t{\r\n\t\t\tVectorInterpolate(mesh->xyz_array[i], mesh->xyz_blendw[1], mesh->xyz2_array[i], xyz[i]);\r\n\t\t}\r\n\t}\r\n\t/*else if (skeletal)\r\n\t{\r\n\t}\r\n\t*/\r\n\telse\r\n\t{\r\n\t\txyz = mesh->xyz_array;\r\n\t}\r\n\r\n\t/*now apply any shader deforms*/\r\n\tif (shaderstate.curshader->numdeforms)\r\n\t{\r\n\t\tdeformgen(&shaderstate.curshader->deforms[0], mesh->numvertexes, xyz, vertbuf, mesh);\r\n\t\txyz = vertbuf;\r\n\t\tfor (i = 1; i < shaderstate.curshader->numdeforms; i++)\r\n\t\t{\r\n\t\t\tdeformgen(&shaderstate.curshader->deforms[i], mesh->numvertexes, xyz, xyz, mesh);\r\n\t\t}\r\n\t}\r\n\r\n\tfor (i = 0; i < mesh->numvertexes; i++, v++)\r\n\t{\r\n\t\tVectorCopy(xyz[i], v->vcoord);\r\n\r\n\t\tVector2Copy(mesh->st_array[i], v->tccoord);\r\n\r\n//\t\tv->colour[0] = mesh->colors4b_array[i][0];\r\n//\t\tv->colour[1] = mesh->colors4b_array[i][1];\r\n//\t\tv->colour[2] = mesh->colors4b_array[i][2];\r\n//\t\tv->colour[3] = mesh->colors4b_array[i][3];\r\n\t}\r\n}\r\n\r\nstatic void SWBE_DrawMesh_Internal(shader_t *shader, mesh_t *mesh, struct vbo_s *vbo, struct texnums_s *texnums, unsigned int be_flags)\r\n{\r\n\twqcom_t *com;\r\n\r\n\tif (!texnums)\r\n\t{\r\n//\t\tif (shader->numdefaulttextures)\r\n//\t\t\ttexnums = shader->defaulttextures + ;\r\n//\t\telse\r\n\t\t\ttexnums = shader->defaulttextures;\r\n\t}\r\n\r\n\tshaderstate.curshader = shader;\r\n\r\n\tif (mesh->istrifan)\r\n\t{\r\n\t\tcom = SWRast_BeginCommand(&commandqueue, WTC_TRIFAN, mesh->numvertexes*sizeof(swvert_t) + sizeof(com->trifan) - sizeof(com->trifan.verts));\r\n\r\n\t\tif (texnums->base)\r\n\t\t\tcom->trifan.texture = texnums->base->ptr;\r\n\t\telse\r\n\t\t\tcom->trifan.texture = &sw_nulltex;\r\n\t\tcom->trifan.numverts = mesh->numvertexes;\r\n\r\n\t\tSWBE_TransformVerticies(com->trifan.verts, mesh);\r\n\r\n\t\tSWRast_EndCommand(&commandqueue, com);\r\n\t}\r\n\telse\r\n\t{\r\n\t\tcom = SWRast_BeginCommand(&commandqueue, WTC_TRISOUP, (mesh->numvertexes*sizeof(swvert_t)) + sizeof(com->trisoup) - sizeof(com->trisoup.verts) + (sizeof(index_t)*mesh->numindexes));\r\n\t\t\r\n\t\tif (texnums->base)\r\n\t\t\tcom->trisoup.texture = texnums->base->ptr;\r\n\t\telse\r\n\t\t\tcom->trisoup.texture = &sw_nulltex;\r\n\t\tcom->trisoup.numverts = mesh->numvertexes;\r\n\t\tcom->trisoup.numidx = mesh->numindexes;\r\n\r\n\t\tSWBE_TransformVerticies(com->trisoup.verts, mesh);\r\n\t\tmemcpy(com->trisoup.verts + mesh->numvertexes, mesh->indexes, sizeof(index_t)*mesh->numindexes);\r\n\r\n\t\tSWRast_EndCommand(&commandqueue, com);\r\n\t}\r\n}\r\nvoid SWBE_DrawMesh_List(shader_t *shader, int nummeshes, struct mesh_s **mesh, struct vbo_s *vbo, struct texnums_s *texnums, unsigned int be_flags)\r\n{\r\n\twhile(nummeshes-->0)\r\n\t{\r\n\t\tSWBE_DrawMesh_Internal(shader, *mesh++, vbo, texnums, be_flags);\r\n\t}\r\n}\r\nvoid SWBE_DrawMesh_Single(shader_t *shader, mesh_t *mesh, struct vbo_s *vbo, unsigned int be_flags)\r\n{\r\n\tSWBE_DrawMesh_Internal(shader, mesh, vbo, NULL, be_flags);\r\n}\r\nvoid SWBE_SubmitBatch(struct batch_s *batch)\r\n{\r\n\tint m;\r\n\tSWBE_SelectEntity(batch->ent);\r\n\tfor (m = 0; m < batch->meshes; m++)\r\n\t{\r\n\t\tSWBE_DrawMesh_Internal(batch->shader, batch->mesh[m], batch->vbo, batch->skin, batch->flags);\r\n\t}\r\n}\r\nstruct batch_s *SWBE_GetTempBatch(void)\r\n{\r\n\tif (shaderstate.wbatch >= shaderstate.maxwbatches)\r\n\t{\r\n\t\tshaderstate.wbatch++;\r\n\t\treturn NULL;\r\n\t}\r\n\treturn &shaderstate.wbatches[shaderstate.wbatch++];\r\n}\r\n\r\nstatic void SWBE_SubmitMeshesSortList(batch_t *sortlist)\r\n{\r\n\tbatch_t *batch;\r\n\tfor (batch = sortlist; batch; batch = batch->next)\r\n\t{\r\n\t\tif (batch->meshes == batch->firstmesh)\r\n\t\t\tcontinue;\r\n\r\n\t\tif (batch->flags & BEF_NODLIGHT)\r\n\t\t\tif (shaderstate.mode == BEM_LIGHT)\r\n\t\t\t\tcontinue;\r\n\t\tif (batch->flags & BEF_NOSHADOWS)\r\n\t\t\tif (shaderstate.mode == BEM_STENCIL)\r\n\t\t\t\tcontinue;\r\n\r\n\t\tif (batch->buildmeshes)\r\n\t\t\tbatch->buildmeshes(batch);\r\n\r\n\t\tif (batch->shader->flags & SHADER_NODRAW)\r\n\t\t\tcontinue;\r\n\t\tif (batch->shader->flags & SHADER_NODLIGHT)\r\n\t\t\tif (shaderstate.mode == BEM_LIGHT)\r\n\t\t\t\tcontinue;\r\n\t\tif (batch->shader->flags & SHADER_SKY)\r\n\t\t{\r\n\t\t\tif (shaderstate.mode == BEM_STANDARD || shaderstate.mode == BEM_DEPTHDARK)\r\n\t\t\t{\r\n\t\t\t\tif (!batch->shader->prog)\r\n\t\t\t\t{\r\n\t\t\t\t\tR_DrawSkyChain (batch);\r\n\t\t\t\t\tcontinue;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\telse if (shaderstate.mode != BEM_FOG && shaderstate.mode != BEM_CREPUSCULAR)\r\n\t\t\t\tcontinue;\r\n\t\t}\r\n\r\n\t\tSWBE_SubmitBatch(batch);\r\n\t}\r\n}\r\n\r\nvoid SWBE_SubmitMeshes (batch_t **worldbatches, batch_t **blist, int start, int stop)\r\n{\r\n\tint i;\r\n\r\n\tfor (i = start; i <= stop; i++)\r\n\t{\r\n\t\tif (worldbatches)\r\n\t\t{\r\n//\t\t\tif (i == SHADER_SORT_PORTAL && !r_noportals.ival && !r_refdef.recurse)\r\n//\t\t\t\tSWBE_SubmitMeshesPortals(worldbatches, blist[i]);\r\n\r\n\t\t\tSWBE_SubmitMeshesSortList(worldbatches[i]);\r\n\t\t}\r\n\t\tSWBE_SubmitMeshesSortList(blist[i]);\r\n\t}\r\n}\r\n\r\nstatic void SWBE_UpdateUniforms(void)\r\n{\r\n\twqcom_t *com;\r\n\tcom = SWRast_BeginCommand(&commandqueue, WTC_UNIFORMS, sizeof(com->uniforms));\r\n\r\n\tmemcpy(com->uniforms.u.matrix, shaderstate.m_mvp, sizeof(com->uniforms.u.matrix));\r\n\tVector4Copy(shaderstate.viewplane, com->uniforms.u.viewplane);\r\n\r\n\tSWRast_EndCommand(&commandqueue, com);\r\n}\r\nvoid SWBE_Set2D(void)\r\n{\r\n\textern cvar_t gl_screenangle;\r\n\tfloat ang, rad, w, h;\r\n\tfloat tmp[16];\r\n\tfloat tmp2[16];\r\n\r\n\tvid.fbvwidth = vid.width;\r\n\tvid.fbvheight = vid.height;\r\n\tvid.fbpwidth = vid.pixelwidth;\r\n\tvid.fbpheight = vid.pixelheight;\r\n\r\n\tang = (gl_screenangle.value>0?(gl_screenangle.value+45):(gl_screenangle.value-45))/90;\r\n\tang = (int)ang * 90;\r\n\tif (ang)\r\n\t{ /*more expensive maths*/\r\n\t\trad = (ang * M_PI) / 180;\r\n\r\n\t\tw = fabs(cos(rad)) * (vid.width) + fabs(sin(rad)) * (vid.height);\r\n\t\th = fabs(sin(rad)) * (vid.width) + fabs(cos(rad)) * (vid.height);\r\n\r\n\t\tMatrix4x4_CM_Orthographic(r_refdef.m_projection_std, w/-2.0f, w/2.0f, h/2.0f, h/-2.0f, -99999, 99999);\r\n\r\n\t\tMatrix4x4_Identity(tmp);\r\n\t\tMatrix4_Multiply(Matrix4x4_CM_NewTranslation((vid.width/-2.0f), (vid.height/-2.0f), 0), tmp, tmp2);\r\n\t\tMatrix4_Multiply(Matrix4x4_CM_NewRotation(-ang,  0, 0, 1), tmp2, r_refdef.m_view);\r\n\t}\r\n\telse\r\n\t{\r\n\t\tif (0)\r\n\t\t\tMatrix4x4_CM_Orthographic(r_refdef.m_projection_std, 0, vid.width, 0, vid.height, 0, 99999);\r\n\t\telse\r\n\t\t\tMatrix4x4_CM_Orthographic(r_refdef.m_projection_std, 0, vid.width, vid.height, 0, 0, 99999);\r\n\t\tMatrix4x4_Identity(r_refdef.m_view);\r\n\t}\r\n\r\n\tmemcpy(shaderstate.m_mvp, r_refdef.m_projection_std, sizeof(shaderstate.m_mvp));\r\n\r\n\tshaderstate.viewplane[0] = -r_refdef.m_view[0*4+2];\r\n\tshaderstate.viewplane[1] = -r_refdef.m_view[1*4+2];\r\n\tshaderstate.viewplane[2] = -r_refdef.m_view[2*4+2];\r\n\tVectorNormalize(shaderstate.viewplane);\r\n\tVectorScale(shaderstate.viewplane, 1.0/99999, shaderstate.viewplane);\r\n\tshaderstate.viewplane[3] = DotProduct(vec3_origin, shaderstate.viewplane);// - 0.5;\r\n\r\n\tSWBE_UpdateUniforms();\r\n}\r\nvoid SWBE_DrawWorld(batch_t **worldbatches)\r\n{\r\n\tbatch_t *batches[SHADER_SORT_COUNT];\r\n\r\n\tif (!r_refdef.recurse)\r\n\t{\r\n\t\tif (shaderstate.wbatch + 50 > shaderstate.maxwbatches)\r\n\t\t{\r\n\t\t\tint newm = shaderstate.wbatch + 100;\r\n\t\t\tshaderstate.wbatches = BZ_Realloc(shaderstate.wbatches, newm * sizeof(*shaderstate.wbatches));\r\n\t\t\tmemset(shaderstate.wbatches + shaderstate.maxwbatches, 0, (newm - shaderstate.maxwbatches) * sizeof(*shaderstate.wbatches));\r\n\t\t\tshaderstate.maxwbatches = newm;\r\n\t\t}\r\n\r\n\t\tshaderstate.wbatch = 0;\r\n\t}\r\n\tBE_GenModelBatches(batches, NULL, shaderstate.mode, r_refdef.scenevis);\r\n//\tR_GenDlightBatches(batches);\r\n\r\n\tshaderstate.curentity = NULL;\r\n\tSWBE_SelectEntity(&r_worldentity);\r\n\r\n\tSWBE_SubmitMeshes(worldbatches, batches, SHADER_SORT_PORTAL, SHADER_SORT_NEAREST);\r\n\r\n\tSWBE_Set2D();\r\n}\r\nvoid SWBE_Init(void)\r\n{\r\n\tmemset(&sh_config, 0, sizeof(sh_config));\r\n\tsh_config.texfmt[PTI_BGRA8] = true;\r\n\tsh_config.texfmt[PTI_BGRX8] = true;\r\n\tsh_config.texfmt[PTI_RGBA8] = true;\r\n\tsh_config.texfmt[PTI_RGBX8] = true;\r\n\tBE_InitTables();\r\n}\r\nvoid SWBE_GenBrushModelVBO(struct model_s *mod)\r\n{\r\n}\r\nvoid SWBE_ClearVBO(struct vbo_s *vbo, qboolean dataonly)\r\n{\r\n}\r\nvoid SWBE_UploadAllLightmaps(void)\r\n{\r\n}\r\nstatic void SWR_RotateForEntity (float *m, float *modelview, const entity_t *e, const model_t *mod)\r\n{\r\n\tif ((e->flags & RF_WEAPONMODEL) && r_refdef.playerview->viewentity > 0)\r\n\t{\r\n\t\tfloat em[16];\r\n\t\tfloat vm[16];\r\n\r\n\t\tif (e->flags & RF_WEAPONMODELNOBOB)\r\n\t\t{\r\n\t\t\tvm[0] = vpn[0];\r\n\t\t\tvm[1] = vpn[1];\r\n\t\t\tvm[2] = vpn[2];\r\n\t\t\tvm[3] = 0;\r\n\r\n\t\t\tvm[4] = -vright[0];\r\n\t\t\tvm[5] = -vright[1];\r\n\t\t\tvm[6] = -vright[2];\r\n\t\t\tvm[7] = 0;\r\n\r\n\t\t\tvm[8] = vup[0];\r\n\t\t\tvm[9] = vup[1];\r\n\t\t\tvm[10] = vup[2];\r\n\t\t\tvm[11] = 0;\r\n\r\n\t\t\tvm[12] = r_refdef.vieworg[0];\r\n\t\t\tvm[13] = r_refdef.vieworg[1];\r\n\t\t\tvm[14] = r_refdef.vieworg[2];\r\n\t\t\tvm[15] = 1;\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tvm[0] = r_refdef.playerview->vw_axis[0][0];\r\n\t\t\tvm[1] = r_refdef.playerview->vw_axis[0][1];\r\n\t\t\tvm[2] = r_refdef.playerview->vw_axis[0][2];\r\n\t\t\tvm[3] = 0;\r\n\r\n\t\t\tvm[4] = r_refdef.playerview->vw_axis[1][0];\r\n\t\t\tvm[5] = r_refdef.playerview->vw_axis[1][1];\r\n\t\t\tvm[6] = r_refdef.playerview->vw_axis[1][2];\r\n\t\t\tvm[7] = 0;\r\n\r\n\t\t\tvm[8] = r_refdef.playerview->vw_axis[2][0];\r\n\t\t\tvm[9] = r_refdef.playerview->vw_axis[2][1];\r\n\t\t\tvm[10] = r_refdef.playerview->vw_axis[2][2];\r\n\t\t\tvm[11] = 0;\r\n\r\n\t\t\tvm[12] = r_refdef.playerview->vw_origin[0];\r\n\t\t\tvm[13] = r_refdef.playerview->vw_origin[1];\r\n\t\t\tvm[14] = r_refdef.playerview->vw_origin[2];\r\n\t\t\tvm[15] = 1;\r\n\t\t}\r\n\r\n\t\tem[0] = e->axis[0][0];\r\n\t\tem[1] = e->axis[0][1];\r\n\t\tem[2] = e->axis[0][2];\r\n\t\tem[3] = 0;\r\n\r\n\t\tem[4] = e->axis[1][0];\r\n\t\tem[5] = e->axis[1][1];\r\n\t\tem[6] = e->axis[1][2];\r\n\t\tem[7] = 0;\r\n\r\n\t\tem[8] = e->axis[2][0];\r\n\t\tem[9] = e->axis[2][1];\r\n\t\tem[10] = e->axis[2][2];\r\n\t\tem[11] = 0;\r\n\r\n\t\tem[12] = e->origin[0];\r\n\t\tem[13] = e->origin[1];\r\n\t\tem[14] = e->origin[2];\r\n\t\tem[15] = 1;\r\n\r\n\t\tMatrix4_Multiply(vm, em, m);\r\n\t}\r\n\telse\r\n\t{\r\n\t\tm[0] = e->axis[0][0];\r\n\t\tm[1] = e->axis[0][1];\r\n\t\tm[2] = e->axis[0][2];\r\n\t\tm[3] = 0;\r\n\r\n\t\tm[4] = e->axis[1][0];\r\n\t\tm[5] = e->axis[1][1];\r\n\t\tm[6] = e->axis[1][2];\r\n\t\tm[7] = 0;\r\n\r\n\t\tm[8] = e->axis[2][0];\r\n\t\tm[9] = e->axis[2][1];\r\n\t\tm[10] = e->axis[2][2];\r\n\t\tm[11] = 0;\r\n\r\n\t\tm[12] = e->origin[0];\r\n\t\tm[13] = e->origin[1];\r\n\t\tm[14] = e->origin[2];\r\n\t\tm[15] = 1;\r\n\t}\r\n\r\n\tif (e->scale != 1 && e->scale != 0)\t//hexen 2 stuff\r\n\t{\r\n#ifdef HEXEN2\r\n\t\tfloat z;\r\n\t\tfloat escale;\r\n\t\tescale = e->scale;\r\n\t\tswitch(e->drawflags&SCALE_TYPE_MASK)\r\n\t\t{\r\n\t\tdefault:\r\n\t\tcase SCALE_TYPE_UNIFORM:\r\n\t\t\tVectorScale((m+0), escale, (m+0));\r\n\t\t\tVectorScale((m+4), escale, (m+4));\r\n\t\t\tVectorScale((m+8), escale, (m+8));\r\n\t\t\tbreak;\r\n\t\tcase SCALE_TYPE_XYONLY:\r\n\t\t\tVectorScale((m+0), escale, (m+0));\r\n\t\t\tVectorScale((m+4), escale, (m+4));\r\n\t\t\tbreak;\r\n\t\tcase SCALE_TYPE_ZONLY:\r\n\t\t\tVectorScale((m+8), escale, (m+8));\r\n\t\t\tbreak;\r\n\t\t}\r\n\t\tif (mod && (e->drawflags&SCALE_TYPE_MASK) != SCALE_TYPE_XYONLY)\r\n\t\t{\r\n\t\t\tswitch(e->drawflags&SCALE_ORIGIN_MASK)\r\n\t\t\t{\r\n\t\t\tcase SCALE_ORIGIN_CENTER:\r\n\t\t\t\tz = ((mod->maxs[2] + mod->mins[2]) * (1-escale))/2;\r\n\t\t\t\tVectorMA((m+12), z, e->axis[2], (m+12));\r\n\t\t\t\tbreak;\r\n\t\t\tcase SCALE_ORIGIN_BOTTOM:\r\n\t\t\t\tVectorMA((m+12), mod->mins[2]*(1-escale), e->axis[2], (m+12));\r\n\t\t\t\tbreak;\r\n\t\t\tcase SCALE_ORIGIN_TOP:\r\n\t\t\t\tVectorMA((m+12), -mod->maxs[2], e->axis[2], (m+12));\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\t\t}\r\n#else\r\n\t\tVectorScale((m+0), e->scale, (m+0));\r\n\t\tVectorScale((m+4), e->scale, (m+4));\r\n\t\tVectorScale((m+8), e->scale, (m+8));\r\n#endif\r\n\t}\r\n\telse if (mod && !strcmp(mod->name, \"progs/eyes.mdl\"))\r\n\t{\r\n\t\t/*resize eyes, to make them easier to see*/\r\n\t\tm[14] -= (22 + 8);\r\n\t\tVectorScale((m+0), 2, (m+0));\r\n\t\tVectorScale((m+4), 2, (m+4));\r\n\t\tVectorScale((m+8), 2, (m+8));\r\n\t}\r\n\tif (mod && !ruleset_allow_larger_models.ival && mod->clampscale != 1 && mod->type == mod_alias)\r\n\t{\t//possibly this should be on a per-frame basis, but that's a real pain to do\r\n\t\tCon_DPrintf(\"Rescaling %s by %f\\n\", mod->name, mod->clampscale);\r\n\t\tVectorScale((m+0), mod->clampscale, (m+0));\r\n\t\tVectorScale((m+4), mod->clampscale, (m+4));\r\n\t\tVectorScale((m+8), mod->clampscale, (m+8));\r\n\t}\r\n\r\n\tMatrix4_Multiply(r_refdef.m_view, m, modelview);\r\n}\r\nvoid SWBE_SelectEntity(struct entity_s *ent)\r\n{\r\n\tfloat modelmatrix[16];\r\n\tfloat modelviewmatrix[16];\r\n\tvec3_t vieworg;\r\n\r\n\tif (shaderstate.curentity == ent)\r\n\t\treturn;\r\n\tshaderstate.curentity = ent;\r\n\r\n\tSWR_RotateForEntity(modelmatrix, modelviewmatrix, shaderstate.curentity, shaderstate.curentity->model);\r\n\tMatrix4_Multiply(r_refdef.m_projection_std, modelviewmatrix, shaderstate.m_mvp);\r\n\tshaderstate.viewplane[0] = vpn[0];//-modelviewmatrix[0];//0*4+2];\r\n\tshaderstate.viewplane[1] = vpn[1];//-modelviewmatrix[1];//1*4+2];\r\n\tshaderstate.viewplane[2] = vpn[2];//-modelviewmatrix[2];//2*4+2];\r\n\tVectorNormalize(shaderstate.viewplane);\r\n\tVectorScale(shaderstate.viewplane, 1.0/8192, shaderstate.viewplane);\r\n\tvieworg[0] = modelviewmatrix[3*4+0];\r\n\tvieworg[1] = modelviewmatrix[3*4+1];\r\n\tvieworg[2] = modelviewmatrix[3*4+2];\r\n\tVectorMA(r_refdef.vieworg, 256, shaderstate.viewplane, vieworg);\r\n\tshaderstate.viewplane[3] = DotProduct(vieworg, shaderstate.viewplane);\r\n\r\n\tSWBE_UpdateUniforms();\r\n}\r\nqboolean SWBE_SelectDLight(struct dlight_s *dl, vec3_t colour, vec3_t axis[3], unsigned int lmode)\r\n{\r\n\treturn false;\r\n}\r\nqboolean SWBE_LightCullModel(vec3_t org, struct model_s *model)\r\n{\r\n\treturn false;\r\n}\r\n\r\nvoid SWBE_RenderToTextureUpdate2d(qboolean destchanged)\r\n{\r\n}\r\n#endif\r\n"
  },
  {
    "path": "engine/sw/sw_image.c",
    "content": "#include \"quakedef.h\"\r\n#ifdef SWQUAKE\r\n#include \"sw.h\"\r\n\r\nvoid SW_NukeAlpha(swimage_t *img)\r\n{\r\n\tint x, y;\r\n\tunsigned int *d = img->data;\r\n\tfor (y = 0; y < img->pheight; y++)\r\n\t{\r\n\t\tfor (x = 0; x < img->pwidth; x++)\r\n\t\t{\r\n\t\t\td[x] |= 0xff000000;\r\n\t\t}\r\n\t\td += img->pitch;\r\n\t}\r\n}\r\n\r\nvoid SW_DestroyTexture(texid_t tex)\r\n{\r\n\tswimage_t *img = tex->ptr;\r\n\ttex->ptr = NULL;\r\n\r\n\t/*make sure its not in use by the renderer*/\r\n\tSWRast_Sync(&commandqueue);\r\n\r\n\t/*okay, it can be killed*/\r\n\tBZ_Free(img);\r\n}\r\n\r\n\r\n\r\nvoid\t\tSW_UpdateFiltering\t\t(image_t *imagelist, int filtermip[3], int filterpic[3], int mipcap[2], float lodbias, float anis)\r\n{\r\n\t//always nearest...\r\n}\r\n\r\nqboolean\tSW_LoadTextureMips\t\t(texid_t tex, const struct pendingtextureinfo *mips)\r\n{\r\n\tswimage_t *img;\r\n\tint i;\r\n\tint nw = mips->mip[0].width;\r\n\tint nh = mips->mip[0].height;\r\n\tqbyte *indata = mips->mip[0].data;\r\n\tqbyte *imgdata;\r\n\r\n\r\n\tif (mips->type != PTI_2D)\r\n\t\treturn false;\r\n\r\n\t//only accept formats that actually make sense here.\r\n\tswitch(mips->encoding)\r\n\t{\r\n\tcase PTI_RGBA8:\r\n\tcase PTI_RGBX8:\r\n\tcase PTI_BGRA8:\r\n\tcase PTI_BGRX8:\r\n\t\tbreak;\r\n\tdefault:\r\n\t\treturn false;\r\n\t}\r\n\r\n\timg = BZ_Malloc(sizeof(*img) - sizeof(img->data) + (nw * nh * 4));\r\n\timgdata = (qbyte*)(img+1) - sizeof(img->data);\r\n\ttex->ptr = img;\r\n\r\n\timg->pwidth = nw;\r\n\timg->pheight = nh;\r\n\timg->pitch = nw;\r\n\r\n\t//precalculated\r\n\timg->pwidthmask = nw-1;\r\n\timg->pheightmask = nh-1;\r\n\r\n\tif (mips->encoding == PTI_RGBA8 || mips->encoding == PTI_RGBX8)\r\n\t{\t//assuming PC hardware is bgr\r\n\t\tif (mips->encoding == PTI_RGBX8)\r\n\t\t{\r\n\t\t\tfor (i = 0; i < nw*nh*4; i+=4)\r\n\t\t\t{\r\n\t\t\t\timgdata[i+0] = indata[i+2];\r\n\t\t\t\timgdata[i+1] = indata[i+1];\r\n\t\t\t\timgdata[i+2] = indata[i+0];\r\n\t\t\t\timgdata[i+3] = 255;\r\n\t\t\t}\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tfor (i = 0; i < nw*nh*4; i+=4)\r\n\t\t\t{\r\n\t\t\t\timgdata[i+0] = indata[i+2];\r\n\t\t\t\timgdata[i+1] = indata[i+1];\r\n\t\t\t\timgdata[i+2] = indata[i+0];\r\n\t\t\t\timgdata[i+3] = indata[i+3];\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\telse\r\n\t{\r\n\t\tmemcpy(imgdata, indata, (nw * nh * 4));\r\n\t\tif (mips->encoding == PTI_BGRX8)\r\n\t\t\tfor (i = 0; i < nw*nh*4; i+=4)\r\n\t\t\t\timgdata[i+3] = 255;\r\n\t}\r\n\r\n//\tfor (i = 0; i < mips->mipcount; i++)\r\n//\t\tif (mips->mip[i].needfree)\r\n//\t\t\tZ_Free(mips->mip[i].data);\r\n//\tif (mips->extrafree)\r\n//\t\tZ_Free(mips->extrafree);\r\n\r\n\treturn true;\r\n}\r\n#endif\r\n"
  },
  {
    "path": "engine/sw/sw_rast.c",
    "content": "#include \"quakedef.h\"\r\n#ifdef SWQUAKE\r\n#include \"sw.h\"\r\n#include \"gl_draw.h\"\r\n#include \"shader.h\"\r\n#include \"renderque.h\"\r\n#include \"glquake.h\"\r\n\r\n#if __STDC_VERSION__ >= 199901L\r\n\t//no need to do anything\r\n#elif defined(_MSC_VER)\r\n\t#define restrict __restrict\r\n#else\r\n\t#define restrict\r\n#endif\r\n\r\n#define ZI_MAX\t0xffff\r\n\r\n/*\r\nOur software rendering basically works like this:\r\n\r\nmain thread builds command:\r\n\tcommand contains vertex data in the command block\r\n\tmain thread runs the vertex programs (much like q3) and performs matrix transforms (much like d3d)\r\n\r\nworker threads read each command sequentially:\r\n\tclip to viewport\r\n\r\ndivision of labour between worker threads works by interlacing.\r\neach thread gets a different set of scanlines to render.\r\nwe can also trivially implement interlacing with this method\r\n\r\n*/\r\n\r\ncvar_t sw_interlace = CVAR(\"sw_interlace\", \"0\");\r\ncvar_t sw_vthread = CVAR(\"sw_vthread\", \"0\");\r\ncvar_t sw_fthreads = CVAR(\"sw_fthreads\", \"0\");\r\n\r\nstruct workqueue_s commandqueue;\r\nstruct workqueue_s spanqueue;\r\n\r\nstatic void WT_Triangle(swthread_t *th, swimage_t *img, swvert_t *v1, swvert_t *v2, swvert_t *v3)\r\n{\r\n\t//affine vs correct:\r\n\t//to correct perspective, divide interpolants by z.\r\n\t//per pixel, divide by interpolated 1 (actually 1/z)\r\n\r\n\tunsigned int tpix;\r\n#if 1\r\n\t#define PERSPECTIVE(v) (v>>16)\r\n#else\r\n\t#define PERSPECTIVE(v) (v/zi)\r\n\t#define SPAN_ZI\r\n#endif\r\n#define SPAN_ST\r\n\r\n#define SPAN_Z\r\n#define PLOT_PIXEL(o) \\\r\n\t{\t\\\r\n\t\tif (*zb >= z)\t\\\r\n\t\t{\t\\\r\n\t\t\t*zb = z;\t\\\r\n\t\ttpix = img->data[\t\\\r\n\t\t\t\t\t((unsigned)PERSPECTIVE(s)&img->pwidthmask)\t\\\r\n\t\t\t\t\t+ (((unsigned)PERSPECTIVE(t)&img->pheightmask) * img->pitch)\t\\\r\n\t\t\t\t];\t\\\r\n\t\tif (tpix&0xff000000) \\\r\n\t\t\to = tpix; \\\r\n\t\t}\t\\\r\n\t}\r\n\r\n#ifdef MSVCWORKSPROPERLY\r\n#include \"sw_spans.h\"\r\n#else\r\n/*\r\nthis file is expected to be #included as the body of a real function\r\nto define create a new pixel shader, define PLOT_PIXEL(outval) at the top of your function and you're good to go\r\n\r\n//modifiers:\r\nSPAN_ST - interpolates S+T across the span. access with 'sc' and 'tc'\r\n\t\taffine... no perspective correction.\r\n\r\n\r\n*/\r\n\r\n{\r\n\tswvert_t *vt;\r\n\tint y;\r\n\tint secondhalf;\r\n\r\n\t//l=value on left\r\n\t//ld=change per y (on left)\r\n\t//d=change per x\r\n\tint xl,xld, xr,xrd;\r\n#ifdef SPAN_ST\r\n\tint sl,sld, sd;\r\n\tint tl,tld, td;\r\n#endif\r\n#ifdef SPAN_ZI\r\n\tint zil, zild, zid;\r\n#endif\r\n#ifdef SPAN_Z\r\n\tint zl,zld, zd;\r\n#endif\r\n\tunsigned int *restrict outbuf;\r\n\tunsigned int *restrict ti;\r\n\tint i;\r\n\tconst swvert_t *vlt,*vlb,*vrt,*vrb;\r\n\tint spanlen;\r\n\tint numspans;\r\n\tunsigned int *vplout;\r\n\tint dx, dy;\r\n\tint recalcside;\r\n\tint interlace;\r\n\r\n\tfloat fdx1,fdy1,fdx2,fdy2,fz,d1,d2;\r\n\r\n\tif (!img)\r\n\t\treturn;\r\n\r\n\t/*we basically render a diamond\r\n\tthat is, the single triangle is split into two triangles, outwards towards the midpoint and inwards to the final position.\r\n\t*/\r\n\r\n\t/*reorder the verticies for height*/\r\n\tif (v1->scoord[1] > v2->scoord[1])\r\n\t{\r\n\t\tvt = v1;\r\n\t\tv1 = v2;\r\n\t\tv2 = vt;\r\n\t}\r\n\tif (v1->scoord[1] > v3->scoord[1])\r\n\t{\r\n\t\tvt = v1;\r\n\t\tv1 = v3;\r\n\t\tv3 = vt;\r\n\t}\r\n\tif (v2->scoord[1] > v3->scoord[1])\r\n\t{\r\n\t\tvt = v3;\r\n\t\tv3 = v2;\r\n\t\tv2 = vt;\r\n\t}\r\n\r\n\t{\r\n\t\tconst swvert_t *v[3];\r\n\r\n\t\tv[0] = v1;\r\n\t\tv[1] = v2;\r\n\t\tv[2] = v3;\r\n\r\n\t\t//reject triangles with any point offscreen, for now\r\n\t\tfor (i = 0; i < 3; i++)\r\n\t\t{\r\n\t\t\tif (v[i]->scoord[0] < 0 || v[i]->scoord[0] > th->vpwidth)\r\n\t\t\t\treturn;\r\n\t\t\tif (v[i]->scoord[1] < 0 || v[i]->scoord[1] > th->vpheight)\r\n\t\t\t\treturn;\r\n\t\t\tif (v[i]->zicoord < 0)\r\n\t\t\t\treturn;\r\n\t\t}\r\n\r\n\t\tfor (i = 0; i < 2; i++)\r\n\t\t{\r\n\t\t\tif (v[i]->scoord[1] > v[i+1]->scoord[1])\r\n\t\t\t\treturn;\r\n\t\t}\r\n\t}\r\n\r\n\tfdx1 = v2->scoord[0] - v1->scoord[0];\r\n\tfdy1 = v2->scoord[1] - v1->scoord[1];\r\n\r\n\tfdx2 = v3->scoord[0] - v1->scoord[0];\r\n\tfdy2 = v3->scoord[1] - v1->scoord[1];\r\n\r\n\tfz = fdx1*fdy2 - fdx2*fdy1;\r\n\r\n\tif (fz == 0)\r\n\t{\r\n\t\t//weird angle...\r\n\t\treturn;\r\n\t}\r\n\r\n\tfz = 1.0 / fz;\r\n\tfdx1 *= fz;\r\n\tfdy1 *= fz;\r\n\tfdx2 *= fz;\r\n\tfdy2 *= fz;\r\n\r\n#ifdef SPAN_ST\t//affine\r\n\td1 = (v2->tccoord[0] - v1->tccoord[0])*(img->pwidth<<16);\r\n\td2 = (v3->tccoord[0] - v1->tccoord[0])*(img->pwidth<<16);\r\n\tsld = fdx1*d2 - fdx2*d1;\r\n\tsd = fdy2*d1 - fdy1*d2;\r\n\r\n\td1 = (v2->tccoord[1] - v1->tccoord[1])*(img->pheight<<16);\r\n\td2 = (v3->tccoord[1] - v1->tccoord[1])*(img->pheight<<16);\r\n\ttld = fdx1*d2 - fdx2*d1;\r\n\ttd = fdy2*d1 - fdy1*d2;\r\n#endif\r\n#ifdef SPAN_ZI\r\n\td1 = (1<<16);\r\n\td2 = (1<<16);\r\n\tzild = 0;//fdx1*d2 - fdx2*d1;\r\n\tzid = 0;//fdy2*d1 - fdy1*d2;\r\n#endif\r\n#ifdef SPAN_Z\r\n\td1 = (v2->zicoord - v1->zicoord)*(1<<16);\r\n\td2 = (v3->zicoord - v1->zicoord)*(1<<16);\r\n\tzld = fdx1*d2 - fdx2*d1;\r\n\tzd = fdy2*d1 - fdy1*d2;\r\n#endif\r\n\r\n\tti = img->data;\r\n\r\n\ty = v1->scoord[1];\r\n\r\n\tfor (secondhalf = 0; secondhalf <= 1; secondhalf++)\r\n\t{\r\n\t\tif (secondhalf)\r\n\t\t{\r\n\t\t//\treturn;\r\n\t\t\tif (numspans < 0)\r\n\t\t\t{\r\n\t\t\t\tinterlace = -numspans;\r\n\t\t\t\ty+=interlace;\r\n\t\t\t\tnumspans-=interlace;\r\n\r\n\t\t\t\txl += xld*interlace;\r\n\t\t\t\txr += xrd*interlace;\r\n\t\t\t\tvplout += th->vpcstride*interlace;\r\n\r\n#ifdef SPAN_ST\r\n\t\t\t\tsl += sld*interlace;\r\n\t\t\t\ttl += tld*interlace;\r\n#endif\r\n#ifdef SPAN_ZI\r\n\t\t\t\tzil += zild*interlace;\r\n#endif\r\n#ifdef SPAN_Z\r\n\t\t\t\tzl += zld*interlace;\r\n#endif\r\n\t\t\t}\r\n\r\n\t\t\t/*v2->v3*/\r\n\t\t\tif (fz <= 0)\r\n\t\t\t{\r\n\t\t\t\tvlt = v2;\r\n\t\t\t\t//vrt == v1;\r\n\t\t\t\tvlb = v3;\r\n\t\t\t\t//vrb == v3;\r\n\r\n\t\t\t\trecalcside = 1;\r\n\r\n#ifdef SPAN_ST\r\n\t\t\t\tsld -= (((long long)sd*xld)>>16);\r\n\t\t\t\ttld -= (((long long)td*xld)>>16);\r\n#endif\r\n#ifdef SPAN_ZI\r\n\t\t\t\tzild -= (((long long)zid*xld)>>16);\r\n#endif\r\n#ifdef SPAN_Z\r\n\t\t\t\tzld -= (((long long)zd*xld)>>16);\r\n#endif\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\t//vlt == v1;\r\n\t\t\t\tvrt = v2;\r\n\t\t\t\t///vlb == v3;\r\n\t\t\t\tvrb = v3;\r\n\r\n\t\t\t\trecalcside = 2;\r\n\t\t\t}\r\n\r\n\t\t\t//flip the triangle to keep it facing the screen (we swapped the verts almost randomly)\r\n\t\t\tnumspans = v3->scoord[1] - y;\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tvlt = v1;\r\n\t\t\tvrt = v1;\r\n\t\t\t/*v1->v2*/\r\n\t\t\tif (fz < 0)\r\n\t\t\t{\r\n\t\t\t\tvlb = v2;\r\n\t\t\t\tvrb = v3;\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tvlb = v3;\r\n\t\t\t\tvrb = v2;\r\n\t\t\t}\r\n\t\t\trecalcside = 3;\r\n\r\n\t\t\t//flip the triangle to keep it facing the screen (we swapped the verts almost randomly)\r\n\t\t\tnumspans = v2->scoord[1] - y;\r\n\t\t}\r\n\r\n\t\tif (recalcside & 1)\r\n\t\t{\r\n\t\t\tdx = (vlb->scoord[0] - vlt->scoord[0]);\r\n\t\t\tdy = (vlb->scoord[1] - vlt->scoord[1]);\r\n\t\t\tif (dy > 0)\r\n\t\t\t\txld = (dx<<16) / dy;\r\n\t\t\telse\r\n\t\t\t\txld = 0;\r\n\t\t\txl = (int)vlt->scoord[0]<<16;\r\n\r\n#ifdef SPAN_ST\r\n\t\t\tsl = vlt->tccoord[0] * (img->pwidth<<16);\r\n\t\t\tsld = sld + (((long long)sd*xld+32767)>>16);\r\n\t\t\ttl = vlt->tccoord[1] * (img->pheight<<16);\r\n\t\t\ttld = tld + (((long long)td*xld+32767)>>16);\r\n#endif\r\n#ifdef SPAN_ZI\r\n\t\t\tzil = (1<<16);///vlt->zicoord;\r\n\t\t\tzild = zild + (((long long)zid*xld)>>16);\r\n#endif\r\n#ifdef SPAN_Z\r\n\t\t\tzl = vlt->zicoord * (1<<16);\r\n\t\t\tzld = zld + (((long long)zd*xld)>>16);\r\n#endif\r\n\t\t}\r\n\r\n\t\tif (recalcside & 2)\r\n\t\t{\r\n\t\t\tdx = (vrb->scoord[0] - vrt->scoord[0]);\r\n\t\t\tdy = (vrb->scoord[1] - vrt->scoord[1]);\r\n\t\t\tif (dy)\r\n\t\t\t\txrd = (dx<<16) / dy;\r\n\t\t\telse\r\n\t\t\t\txrd = 0;\r\n\t\t\txr = (int)vrt->scoord[0]<<16;\r\n\t\t}\r\n\r\n\t\tif (y + numspans > th->vpheight)\r\n\t\t\tnumspans = th->vpheight - y;\r\n\r\n\t\tif (numspans <= 0)\r\n\t\t\tcontinue;\r\n\r\n\r\n\t\tvplout = th->vpcbuf + y * th->vpcstride;\t//this is a pointer to the left of the viewport buffer.\r\n\r\n\t\tinterlace = ((y + th->interlaceline) % th->interlacemod);\r\n\t\tif (interlace)\r\n\t\t{\r\n\t\t\tif (interlace > numspans)\r\n\t\t\t{\r\n\t\t\t\tinterlace = numspans;\r\n\t\t\t\ty+=interlace;\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\ty+=interlace;\r\n\t\t\t\tnumspans-=interlace;\r\n\t\t\t}\r\n\t\t\txl += xld*interlace;\r\n\t\t\txr += xrd*interlace;\r\n\t\t\tvplout += th->vpcstride*interlace;\r\n\r\n#ifdef SPAN_ST\r\n\t\t\tsl += sld*interlace;\r\n\t\t\ttl += tld*interlace;\r\n#endif\r\n#ifdef SPAN_ZI\r\n\t\t\tzil += zild*interlace;\r\n#endif\r\n#ifdef SPAN_Z\r\n\t\t\tzl += zld*interlace;\r\n#endif\r\n\t\t}\r\n\r\n\t\tfor (; numspans > 0; \r\n\t\t\tnumspans -= th->interlacemod\r\n\t\t\t,xl += xld*th->interlacemod\r\n\t\t\t,xr += xrd*th->interlacemod\r\n\t\t\t,vplout += th->vpcstride*th->interlacemod\r\n\t\t\t,y += th->interlacemod\r\n\r\n#ifdef SPAN_ST\r\n\t\t\t,sl += sld*th->interlacemod\r\n\t\t\t,tl += tld*th->interlacemod\r\n#endif\r\n#ifdef SPAN_ZI\r\n\t\t\t,zil += zild*th->interlacemod\r\n#endif\r\n#ifdef SPAN_Z\r\n\t\t\t,zl += zld*th->interlacemod\r\n#endif\r\n\t\t\t)\r\n\t\t{\r\n#ifdef SPAN_ST\r\n\t\t\tunsigned int s = sl;\r\n\t\t\tunsigned int t = tl;\r\n#endif\r\n#ifdef SPAN_ZI\r\n\t\t\tunsigned int zi = zil;\r\n#else\r\n\t\t\tconst unsigned int zi = (1<<16);\r\n#endif\r\n#ifdef SPAN_Z\r\n\t\t\tunsigned int z = zl;\r\n\t\t\tunsigned int *restrict zb = th->vpdbuf + y * th->vpwidth + (xl>>16);\r\n#endif\r\n\r\n\t\t\tspanlen = (xr - xl)>>16;\r\n\t\t\toutbuf = vplout + (xl>>16);\r\n\r\n\t\t\twhile(spanlen-->0)\r\n\t\t\t{\r\n\t\t\t\tPLOT_PIXEL(*outbuf);\r\n\t\t\t\toutbuf++;\r\n\r\n#ifdef SPAN_ST\r\n\t\t\t\ts += sd;\r\n\t\t\t\tt += td;\r\n#endif\r\n#ifdef SPAN_ZI\r\n\t\t\t\tzi += zid;\r\n#endif\r\n#ifdef SPAN_Z\r\n\t\t\t\tz += zd;\r\n\t\t\t\tzb++;\r\n#endif\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n}\r\n\r\n#undef SPAN_ST\r\n#undef PLOT_PIXEL\r\n#endif\r\n\t\r\n}\r\n\r\nstatic void WT_Clip_Top(swthread_t *th, swvert_t *out, swvert_t *in, swvert_t *result)\r\n{\r\n\tfloat frac;\r\n\tfrac =\t(0 - in->scoord[1]) /\r\n\t\t\t(float)(out->scoord[1] - in->scoord[1]);\r\n\tVector2Interpolate(in->scoord, frac, out->scoord, result->scoord);\r\n\tFloatInterpolate(in->zicoord, frac, out->zicoord, result->zicoord);\r\n\tresult->scoord[1] = 0;\r\n\tVector2Interpolate(in->tccoord, frac, out->tccoord, result->tccoord);\r\n}\r\nstatic void WT_Clip_Bottom(swthread_t *th, swvert_t *out, swvert_t *in, swvert_t *result)\r\n{\r\n\tfloat frac;\r\n\tfrac =\t((th->vpheight) - in->scoord[1]) /\r\n\t\t\t(float)(out->scoord[1] - in->scoord[1]);\r\n\tVector2Interpolate(in->scoord, frac, out->scoord, result->scoord);\r\n\tFloatInterpolate(in->zicoord, frac, out->zicoord, result->zicoord);\r\n\tresult->scoord[1] = th->vpheight;\r\n\tVector2Interpolate(in->tccoord, frac, out->tccoord, result->tccoord);\r\n}\r\nstatic void WT_Clip_Left(swthread_t *th, swvert_t *out, swvert_t *in, swvert_t *result)\r\n{\r\n\tfloat frac;\r\n\tfrac =\t(0 - in->scoord[0]) /\r\n\t\t\t(float)(out->scoord[0] - in->scoord[0]);\r\n\tVector2Interpolate(in->scoord, frac, out->scoord, result->scoord);\r\n\tFloatInterpolate(in->zicoord, frac, out->zicoord, result->zicoord);\r\n\tresult->scoord[0] = 0;\r\n\tVector2Interpolate(in->tccoord, frac, out->tccoord, result->tccoord);\r\n}\r\nstatic void WT_Clip_Right(swthread_t *th, swvert_t *out, swvert_t *in, swvert_t *result)\r\n{\r\n\tfloat frac;\r\n\tfrac =\t((th->vpwidth) - in->scoord[0]) /\r\n\t\t\t(float)(out->scoord[0] - in->scoord[0]);\r\n\tVector2Interpolate(in->scoord, frac, out->scoord, result->scoord);\r\n\tFloatInterpolate(in->zicoord, frac, out->zicoord, result->zicoord);\r\n\tresult->scoord[0] = th->vpwidth;\r\n\tVector2Interpolate(in->tccoord, frac, out->tccoord, result->tccoord);\r\n}\r\nstatic void WT_Clip_Near(swthread_t *th, swvert_t *out, swvert_t *in, swvert_t *result)\r\n{\r\n\tfloat nearclip = 0;\r\n\tdouble frac;\r\n\tfrac =\t(nearclip - in->zicoord) /\r\n\t\t\t(out->zicoord - in->zicoord);\r\n\tVectorInterpolate(in->vcoord, frac, out->vcoord, result->vcoord);\r\n\tFloatInterpolate(in->zicoord, frac, out->zicoord, result->zicoord);\r\n\tresult->zicoord = nearclip;\r\n\tVector2Interpolate(in->tccoord, frac, out->tccoord, result->tccoord);\r\n}\r\n\r\nstatic void WT_Clip_Far(swthread_t *th, swvert_t *out, swvert_t *in, swvert_t *result)\r\n{\r\n\tfloat farclip = 1;\r\n\tdouble frac;\r\n\tfrac =\t(farclip - in->zicoord) /\r\n\t\t\t(out->zicoord - in->zicoord);\r\n\tVectorInterpolate(in->vcoord, frac, out->vcoord, result->vcoord);\r\n\tFloatInterpolate(in->zicoord, frac, out->zicoord, result->zicoord);\r\n\tresult->zicoord = farclip;\r\n\tVector2Interpolate(in->tccoord, frac, out->tccoord, result->tccoord);\r\n}\r\n\r\nstatic int WT_ClipPoly(swthread_t *th, int incount, swvert_t *inv, swvert_t *outv, int flag, void (*clip)(swthread_t *th, swvert_t *out, swvert_t *in, swvert_t *result))\r\n{\r\n\tint p, c;\r\n\tint result = 0;\r\n\tint pf, cf;\r\n\tif (incount < 3)\r\n\t\treturn 0;\r\n\r\n\tfor (p = incount - 1, c = 0; c < incount; p = c, c++)\r\n\t{\r\n\t\tpf = inv[p].clipflags & flag;\r\n\t\tcf = inv[c].clipflags & flag;\r\n\r\n\t\tif (pf && cf)\r\n\t\t\tcontinue;\t//both clipped, skip it now\r\n\t\tif (pf ^ cf)\r\n\t\t{\r\n\t\t\t//crossed... emit a new vertex on the boundary\r\n\t\t\tif (cf)\t//new is offscreen\r\n\t\t\t\tclip(th, &inv[c], &inv[p], &outv[result]);\r\n\t\t\telse\r\n\t\t\t\tclip(th, &inv[p], &inv[c], &outv[result]);\r\n\t\t\toutv[result].clipflags = 0;\r\n\r\n\t\t\tif (outv[result].scoord[0] < 0)\r\n\t\t\t\toutv[result].clipflags |= CLIP_LEFT_FLAG;\r\n\t\t\tif (outv[result].scoord[0] > th->vpwidth)\r\n\t\t\t\toutv[result].clipflags |= CLIP_RIGHT_FLAG;\r\n\t\t\tif (outv[result].scoord[1] < 0)\r\n\t\t\t\toutv[result].clipflags |= CLIP_TOP_FLAG;\r\n\t\t\tif (outv[result].scoord[1] > th->vpheight)\r\n\t\t\t\toutv[result].clipflags |= CLIP_BOTTOM_FLAG;\r\n\r\n\t\t\tif (outv[result].zicoord < 0)\r\n\t\t\t\toutv[result].clipflags |= CLIP_NEAR_FLAG;\r\n\t\t\tif (outv[result].zicoord > ZI_MAX)\r\n\t\t\t\toutv[result].clipflags |= CLIP_FAR_FLAG;\r\n\r\n\t\t\tresult++;\r\n\t\t}\r\n\t\tif (!cf)\r\n\t\t{\r\n\t\t\toutv[result] = inv[c];\r\n\t\t\tresult++;\r\n\t\t}\r\n\t}\r\n\treturn result;\r\n}\r\n\r\n//transform the vertex and calculate its final position.\r\nstatic int WT_TransformVertXY(swthread_t *th, swvert_t *v)\r\n{\r\n\tint result = 0;\r\n\tvec4_t tr;\r\n\tMatrix4x4_CM_Transform34(th->u.matrix, v->vcoord, tr);\r\n\tif (tr[3] != 1)\r\n\t{\r\n\t\ttr[0] /= tr[3];\r\n\t\ttr[1] /= tr[3];\r\n\t\ttr[2] /= tr[3];\r\n\t}\r\n\r\n\tv->scoord[0] = (tr[0]+1)/2 * th->vpwidth;\r\n\tif (v->scoord[0] < 0)\r\n\t\tresult |= CLIP_LEFT_FLAG;\r\n\tif (v->scoord[0] > th->vpwidth)\r\n\t\tresult |= CLIP_RIGHT_FLAG;\r\n\r\n\tv->scoord[1] = (tr[1]+1)/2 * th->vpheight;\r\n\tif (v->scoord[1] < 0)\r\n\t\tresult |= CLIP_TOP_FLAG;\r\n\tif (v->scoord[1] > th->vpheight)\r\n\t\tresult |= CLIP_BOTTOM_FLAG;\r\n\r\n\tv->clipflags = result;\r\n\r\n\treturn result;\r\n}\r\n\r\nstatic void WT_ClipTriangle(swthread_t *th, swimage_t *img, swvert_t *v1, swvert_t *v2, swvert_t *v3)\r\n{\r\n\tunsigned int cflags;\r\n\tswvert_t final[2][64];\r\n\tint list = 0;\r\n\tint i;\r\n\tint count;\r\n\r\n\t//check the near/far planes.\r\n\tv1->zicoord = DotProduct(v1->vcoord, th->u.viewplane) - th->u.viewplane[3];\r\n\tif (v1->zicoord < 0) v1->clipflags = CLIP_NEAR_FLAG; else if (v1->zicoord >= ZI_MAX) v1->clipflags = CLIP_FAR_FLAG; else v1->clipflags = 0;\r\n\tv2->zicoord = DotProduct(v2->vcoord, th->u.viewplane) - th->u.viewplane[3];\r\n\tif (v2->zicoord < 0) v2->clipflags = CLIP_NEAR_FLAG; else if (v2->zicoord >= ZI_MAX) v2->clipflags = CLIP_FAR_FLAG; else v2->clipflags = 0;\r\n\tv3->zicoord = DotProduct(v3->vcoord, th->u.viewplane) - th->u.viewplane[3];\r\n\tif (v3->zicoord < 0) v3->clipflags = CLIP_NEAR_FLAG; else if (v3->zicoord >= ZI_MAX) v3->clipflags = CLIP_FAR_FLAG; else v3->clipflags = 0;\r\n\r\n\tif (v1->clipflags & v2->clipflags & v3->clipflags)\r\n\t\treturn;\t//all verticies are off at least one plane\r\n\tcflags = v1->clipflags | v2->clipflags | v3->clipflags;\r\n\r\n\tif (0)//!cflags)\r\n\t{\r\n\t\t//figure out the final 2d positions\r\n\t\tcflags = 0;\r\n\t\tfor (i = 0; i < count; i++)\r\n\t\t\tcflags |= WT_TransformVertXY(th, &final[list][i]);\r\n\r\n\t}\r\n\telse\r\n\t{\r\n\t\tfinal[list][0] = *v1;\r\n\t\tfinal[list][1] = *v2;\r\n\t\tfinal[list][2] = *v3;\r\n\t\tcount = 3;\r\n\r\n\t\t//clip to the screen\r\n\t\tif (cflags & CLIP_NEAR_FLAG)\r\n\t\t{\r\n//\t\t\treturn;\r\n\t\t\tcount = WT_ClipPoly(th, count, final[list], final[list^1], CLIP_NEAR_FLAG, WT_Clip_Near);\r\n\t\t\tlist ^= 1;\r\n\t\t}\r\n\t\tif (cflags & CLIP_FAR_FLAG)\r\n\t\t{\r\n\t\t\tcount = WT_ClipPoly(th, count, final[list], final[list^1], CLIP_FAR_FLAG, WT_Clip_Far);\r\n\t\t\tlist ^= 1;\r\n\t\t}\r\n\r\n\t\t//figure out the final 2d positions\r\n\t\tcflags = 0;\r\n\t\tfor (i = 0; i < count; i++)\r\n\t\t\tcflags |= WT_TransformVertXY(th, &final[list][i]);\r\n\t}\r\n\r\n\t//and clip those by the screen (instead of by plane, to try to prevent crashes)\r\n\tif (cflags & CLIP_TOP_FLAG)\r\n\t{\r\n\t\tcount = WT_ClipPoly(th, count, final[list], final[list^1], CLIP_TOP_FLAG, WT_Clip_Top);\r\n\t\tlist ^= 1;\r\n\t}\r\n\tif (cflags & CLIP_BOTTOM_FLAG)\r\n\t{\r\n\t\tcount = WT_ClipPoly(th, count, final[list], final[list^1], CLIP_BOTTOM_FLAG, WT_Clip_Bottom);\r\n\t\tlist ^= 1;\r\n\t}\r\n\tif (cflags & CLIP_LEFT_FLAG)\r\n\t{\r\n\t\tcount = WT_ClipPoly(th, count, final[list], final[list^1], CLIP_LEFT_FLAG, WT_Clip_Left);\r\n\t\tlist ^= 1;\r\n\t}\r\n\tif (cflags & CLIP_RIGHT_FLAG)\r\n\t{\r\n\t\tcount = WT_ClipPoly(th, count, final[list], final[list^1], CLIP_RIGHT_FLAG, WT_Clip_Right);\r\n\t\tlist ^= 1;\r\n\t}\r\n\r\n\t//draw the damn thing. FIXME: generate spans and push to a fragment thread.\r\n\tfor (i = 2; i < count; i++)\r\n\t{\r\n\t\tWT_Triangle(th, img, &final[list][0], &final[list][i-1], &final[list][i]);\r\n\t}\r\n}\r\n\r\nvoid WQ_ClearBuffer(swthread_t *t, unsigned int *mbuf, qintptr_t stride, unsigned int clearval)\r\n{\r\n\tint y;\r\n\tint x;\r\n\tunsigned int *buf;\r\n\r\n\tfor (y = t->interlaceline; y < t->vpheight; y += t->interlacemod)\r\n\t{\r\n\t\tbuf = mbuf + stride*y;\r\n\t\tfor (x = 0; x < (t->vpwidth & ~15);)\r\n\t\t{\r\n\t\t\tbuf[x++] = clearval;\r\n\t\t\tbuf[x++] = clearval;\r\n\t\t\tbuf[x++] = clearval;\r\n\t\t\tbuf[x++] = clearval;\r\n\t\t\tbuf[x++] = clearval;\r\n\t\t\tbuf[x++] = clearval;\r\n\t\t\tbuf[x++] = clearval;\r\n\t\t\tbuf[x++] = clearval;\r\n\t\t\tbuf[x++] = clearval;\r\n\t\t\tbuf[x++] = clearval;\r\n\t\t\tbuf[x++] = clearval;\r\n\t\t\tbuf[x++] = clearval;\r\n\t\t\tbuf[x++] = clearval;\r\n\t\t\tbuf[x++] = clearval;\r\n\t\t\tbuf[x++] = clearval;\r\n\t\t\tbuf[x++] = clearval;\r\n\t\t}\r\n\t\tfor (; x < t->vpwidth; )\r\n\t\t\tbuf[x++] = clearval;\r\n\t}\r\n}\r\n\r\nqboolean WT_HandleCommand(swthread_t *t, wqcom_t *com)\r\n{\r\n\tindex_t *idx;\r\n\tint i;\r\n\tswitch(com->com.command)\r\n\t{\r\n\tcase WTC_DIE:\r\n\t\tt->readpoint += com->com.cmdsize;\r\n\t\treturn 1;\r\n\tcase WTC_NOOP:\r\n\t\tbreak;\r\n\tcase WTC_NEWFRAME:\r\n\t\tbreak;\r\n\tcase WTC_UNIFORMS:\r\n\t\tmemcpy(&t->u, &com->uniforms.u, sizeof(t->u));\r\n\t\tbreak;\r\n\tcase WTC_VIEWPORT:\r\n\t\tt->vpcbuf = com->viewport.cbuf;\r\n\t\tt->vpdbuf = com->viewport.dbuf;\r\n\t\tt->vpwidth = com->viewport.width;\r\n\t\tt->vpheight = com->viewport.height;\r\n\t\tt->vpcstride = com->viewport.stride;\r\n\t\tif (!t->wq->numthreads)\r\n\t\t{\r\n\t\t\tt->interlacemod = com->viewport.interlace;\t//this many vthreads\r\n\t\t\tt->interlaceline = com->viewport.framenum%com->viewport.interlace;\t//this vthread\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tt->interlacemod = t->wq->numthreads*com->viewport.interlace;\t//this many vthreads\r\n\t\t\tt->interlaceline = (t->threadnum*com->viewport.interlace) + (com->viewport.framenum%com->viewport.interlace);\t//this vthread\r\n\t\t}\r\n\r\n\t\tif (com->viewport.clearcolour)\r\n\t\t{\r\n\t\t\tWQ_ClearBuffer(t, t->vpcbuf, t->vpcstride, 0);\r\n\t\t}\r\n\t\tif (com->viewport.cleardepth)\r\n\t\t{\r\n\t\t\tWQ_ClearBuffer(t, t->vpdbuf, t->vpwidth, ~0u);\r\n\t\t}\r\n\t\tbreak;\r\n\tcase WTC_TRIFAN:\r\n\t\tfor (i = 2; i < com->trifan.numverts; i++)\r\n\t\t{\r\n\t\t\tWT_ClipTriangle(t, com->trifan.texture, &com->trifan.verts[0], &com->trifan.verts[i-1], &com->trifan.verts[i]);\r\n\t\t}\r\n\t\tbreak;\r\n\tcase WTC_TRISOUP:\r\n\t\tidx = (index_t*)(com->trisoup.verts + com->trisoup.numverts);\r\n\t\tfor (i = 0; i < com->trisoup.numidx; i+=3, idx+=3)\r\n\t\t{\r\n\t\t\tWT_ClipTriangle(t, com->trisoup.texture, &com->trisoup.verts[idx[0]], &com->trisoup.verts[idx[1]], &com->trisoup.verts[idx[2]]);\r\n\t\t}\r\n\t\tbreak;\r\n\tcase WTC_SPANS:\r\n\t\tbreak;\r\n\tdefault:\r\n\t\tSys_Printf(\"Unknown render command!\\n\");\r\n\t\tbreak;\r\n\t}\r\n\tt->readpoint += com->com.cmdsize;\r\n\treturn false;\r\n}\r\n\r\nint WT_Main(void *ptr)\r\n{\r\n\twqcom_t *com;\r\n\tswthread_t *t = ptr;\r\n\tfor(;;)\r\n\t{\r\n\t\tif (t->readpoint == t->wq->pos)\r\n\t\t{\r\n\t\t\tSys_Sleep(0);\r\n\t\t\tcontinue;\r\n\t\t}\r\n\t\tcom = (wqcom_t*)&t->wq->queue[t->readpoint & WQ_MASK];\r\n\t\tif (WT_HandleCommand(t, com))\r\n\t\t\tbreak;\r\n\t}\r\n\treturn 0;\r\n}\r\nvoid SWRast_EndCommand(struct workqueue_s *wq, wqcom_t *com)\r\n{\r\n\twq->pos += com->com.cmdsize;\r\n\r\n\tif (!wq->numthreads)\r\n\t{\r\n\t\t//immediate mode\r\n\t\tWT_HandleCommand(wq->swthreads, com);\r\n\t}\r\n}\r\nwqcom_t *SWRast_BeginCommand(struct workqueue_s *wq, int cmdtype, unsigned int size)\r\n{\r\n\twqcom_t *com;\r\n\t//round the command size up, so we always have space for a noop/wrap if needed\r\n\tsize = (size + sizeof(com->align)) & ~(sizeof(com->align)-1);\r\n\r\n\t//generate a noop if we don't have enough space for the command\r\n\tif ((wq->pos&WQ_MASK) + size > WQ_SIZE)\r\n\t{\r\n//\t\tSWRast_Sync();\r\n\t\tcom = (wqcom_t *)&wq->queue[wq->pos&WQ_MASK];\r\n\t\tcom->com.cmdsize = WQ_SIZE - wq->pos&WQ_MASK;\r\n\t\tcom->com.command = WTC_NOOP;\r\n\t\tSWRast_EndCommand(wq, com);\r\n\t}\r\n\r\n\tcom = (wqcom_t *)&wq->queue[wq->pos&WQ_MASK];\r\n\tcom->com.cmdsize = size;\r\n\tcom->com.command = cmdtype;\r\n\r\n\treturn com;\r\n}\r\nvoid SWRast_Sync(struct workqueue_s *wq)\r\n{\r\n\tint i;\r\n\tswthread_t *t;\r\n\r\n\tfor (i = 0; i < wq->numthreads; i++)\r\n\t{\r\n\t\tt = &wq->swthreads[i];\r\n\t\twhile (t->readpoint != wq->pos)\r\n\t\t\t;\r\n\t}\r\n\r\n\t//all worker threads are up to speed\r\n}\r\nvoid SWRast_CreateThreadPool(struct workqueue_s *wq, int numthreads)\r\n{\r\n\tint i = 0;\r\n\tswthread_t *t;\r\n\twq->pos = 0;\r\n\tnumthreads = ((numthreads > WQ_MAXTHREADS)?WQ_MAXTHREADS:numthreads);\r\n#ifdef MULTITHREAD\r\n\tfor (i = 0; i < numthreads; i++)\r\n\t{\r\n\t\tt = &wq->swthreads[i];\r\n\t\tt->threadnum = i;\r\n\t\tt->thread = Sys_CreateThread(\"swrast\", WT_Main, t, THREADP_NORMAL, 0);\r\n\t\tif (!t->thread)\r\n\t\t\tbreak;\r\n\t}\r\n#else\r\n\tnumthreads = 0;\r\n#endif\r\n\twq->numthreads = i;\r\n\r\n\tif (i == 0)\r\n\t\tnumthreads = 1;\r\n\telse\r\n\t\tnumthreads = i;\r\n\tfor (i = 0; i < numthreads; i++)\r\n\t{\r\n\t\twq->swthreads[i].readpoint = wq->pos;\r\n\t\twq->swthreads[i].wq = wq;\r\n\t}\r\n}\r\nvoid SWRast_TerminateThreadPool(struct workqueue_s *wq)\r\n{\r\n\tint i;\r\n\twqcom_t *com = SWRast_BeginCommand(wq, WTC_DIE, sizeof(com->com));\r\n\tSWRast_EndCommand(wq, com);\r\n#ifdef MULTITHREAD\r\n\tfor (i = 0; i < wq->numthreads; i++)\r\n\t{\r\n\t\tSys_WaitOnThread(wq->swthreads[i].thread);\r\n\t}\r\n#endif\r\n\twq->numthreads = 0;\r\n}\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\nvoid SW_Draw_Init(void)\r\n{\r\n\tR2D_Init();\r\n\r\n\tR_InitFlashblends();\r\n}\r\nvoid SW_Draw_Shutdown(void)\r\n{\r\n\tR2D_Shutdown();\r\n}\r\nvoid SW_R_Init(void)\r\n{\r\n\tSWRast_CreateThreadPool(&commandqueue, sw_vthread.ival?1:0);\r\n\tsw_vthread.modified = true;\r\n}\r\nvoid SW_R_DeInit(void)\r\n{\r\n\tSWRast_TerminateThreadPool(&commandqueue);\r\n}\r\nvoid SW_R_RenderView(void)\r\n{\r\n\textern cvar_t gl_screenangle;\r\n\textern cvar_t gl_mindist;\r\n\tvec3_t newa;\r\n\tint tmpvisents = cl_numvisedicts;\t/*world rendering is allowed to add additional ents, but we don't want to keep them for recursive views*/\r\n\tif (!cl.worldmodel || (!cl.worldmodel->nodes && cl.worldmodel->type != mod_heightmap))\r\n\t\tr_refdef.flags |= RDF_NOWORLDMODEL;\r\n\r\n\t//no fbos here\r\n\tvid.fbvwidth = vid.width;\r\n\tvid.fbvheight = vid.height;\r\n\tvid.fbpwidth = vid.pixelwidth;\r\n\tvid.fbpheight = vid.pixelheight;\r\n\r\n\t{\r\n\t\t//figure out the viewport that we should be using.\r\n\t\tint x = floor(r_refdef.vrect.x * (float)vid.fbpwidth/(float)vid.width);\r\n\t\tint x2 = ceil((r_refdef.vrect.x + r_refdef.vrect.width) * (float)vid.fbpwidth/(float)vid.width);\r\n\t\tint y = floor(r_refdef.vrect.y * (float)vid.fbpheight/(float)vid.height);\r\n\t\tint y2 = ceil((r_refdef.vrect.y + r_refdef.vrect.height) * (float)vid.fbpheight/(float)vid.height);\r\n\t\tint w = x2 - x;\r\n\t\tint h = y2 - y;\r\n\t\tr_refdef.pxrect.x = x;\r\n\t\tr_refdef.pxrect.y = y;\r\n\t\tr_refdef.pxrect.width = w;\r\n\t\tr_refdef.pxrect.height = h;\r\n\t\tr_refdef.pxrect.maxheight = vid.fbpheight;\r\n\t}\r\n\r\n\tAngleVectors (r_refdef.viewangles, vpn, vright, vup);\r\n\tVectorCopy (r_refdef.vieworg, r_origin);\r\n\tif (r_refdef.useperspective)\r\n\t\tMatrix4x4_CM_Projection_Inf(r_refdef.m_projection_std, r_refdef.fov_x, r_refdef.fov_y, r_refdef.mindist, false);\r\n\telse\r\n\t\tMatrix4x4_CM_Orthographic(r_refdef.m_projection_std, -r_refdef.fov_x/2, r_refdef.fov_x/2, -r_refdef.fov_y/2, r_refdef.fov_y/2, r_refdef.mindist, r_refdef.maxdist>=1?r_refdef.maxdist:9999);\r\n\tVectorCopy(r_refdef.viewangles, newa);\r\n\tnewa[0] = r_refdef.viewangles[0];\r\n\tnewa[1] = r_refdef.viewangles[1];\r\n\tnewa[2] = r_refdef.viewangles[2] + gl_screenangle.value;\r\n\tMatrix4x4_CM_ModelViewMatrix(r_refdef.m_view, newa, r_refdef.vieworg);\r\n\r\n\tR_SetFrustum (r_refdef.m_projection_std, r_refdef.m_view);\r\n\r\n\tRQ_BeginFrame();\r\n\r\n\tSurf_DrawWorld ();\t\t// adds static entities to the list\r\n\r\n\tS_ExtraUpdate ();\t// don't let sound get messed up if going slow\r\n\r\n//\tR_DrawDecals();\r\n\r\n\tR_RenderDlights ();\r\n\r\n\tRQ_RenderBatchClear();\r\n\r\n\tcl_numvisedicts = tmpvisents;\r\n}\r\nqboolean SW_SCR_UpdateScreen(void)\r\n{\r\n\twqcom_t *com;\r\n\r\n\textern cvar_t gl_screenangle;\r\n\tfloat w = vid.width, h = vid.height;\r\n\r\n\tr_refdef.time = realtime;\r\n\r\n\tSWBE_Set2D();\r\n\r\n\tSWRast_Sync(&commandqueue);\r\n\tSWRast_Sync(&spanqueue);\r\n\tSW_VID_SwapBuffers();\r\n\tif (sw_vthread.modified)\r\n\t{\r\n\t\tSWRast_TerminateThreadPool(&commandqueue);\r\n\t\tSWRast_CreateThreadPool(&commandqueue, sw_vthread.ival?1:0);\r\n\t\tsw_vthread.modified = false;\r\n\t}\r\n\tif (sw_fthreads.modified)\r\n\t{\r\n\t\tSWRast_TerminateThreadPool(&spanqueue);\r\n\t\tSWRast_CreateThreadPool(&spanqueue, sw_fthreads.ival);\r\n\t\tsw_fthreads.modified = false;\r\n\t}\r\n\r\n\tcom = SWRast_BeginCommand(&commandqueue, WTC_VIEWPORT, sizeof(com->viewport));\r\n\tcom->viewport.interlace = bound(0, sw_interlace.ival, 15)+1;\r\n\tcom->viewport.clearcolour = r_clear.ival;\r\n\tcom->viewport.cleardepth = true;\r\n\tSW_VID_UpdateViewport(com);\r\n\tSWRast_EndCommand(&commandqueue, com);\r\n\r\n\tShader_DoReload();\r\n\r\n\tR2D_Font_Changed();\r\n\r\n\t//FIXME: playfilm/editor+q3ui\r\n\tSCR_SetUpToDrawConsole ();\r\n\r\n\tif (cls.state == ca_active)\r\n\t{\r\n\t\tif (!CSQC_DrawView())\r\n\t\t\tV_RenderView (false);\r\n\r\n\t\tR2D_BrightenScreen();\r\n\t}\r\n\r\n\tSCR_DrawTwoDimensional(0, 0);\r\n\r\n\tV_UpdatePalette (false);\r\n\treturn true;\r\n}\r\n\r\nvoid SW_VBO_Begin(vbobctx_t *ctx, size_t maxsize)\r\n{\r\n}\r\nvoid SW_VBO_Data(vbobctx_t *ctx, void *data, size_t size, vboarray_t *varray)\r\n{\r\n}\r\nvoid SW_VBO_Finish(vbobctx_t *ctx, void *edata, size_t esize, vboarray_t *earray, void **vbomem, void **ebomem)\r\n{\r\n}\r\nvoid SW_VBO_Destroy(vboarray_t *vearray, void *mem)\r\n{\r\n}\r\nvoid SWBE_Scissor(srect_t *rect)\r\n{\r\n}\r\n\r\nrendererinfo_t swrendererinfo =\r\n{\r\n\t\"Software Renderer\",\r\n\t{\r\n\t\t\"sw\",\r\n\t\t\"Software\",\r\n\t\t\"SoftRast\",\r\n\t},\r\n\tQR_SOFTWARE,\r\n\r\n\tSW_Draw_Init,\r\n\tSW_Draw_Shutdown,\r\n\r\n\tSW_UpdateFiltering,\r\n\tSW_LoadTextureMips,\r\n\tSW_DestroyTexture,\r\n\r\n\tSW_R_Init,\r\n\tSW_R_DeInit,\r\n\tSW_R_RenderView,\r\n\r\n\tSW_VID_Init,\r\n\tSW_VID_DeInit,\r\n\tSW_VID_SwapBuffers,\r\n\tSW_VID_ApplyGammaRamps,\r\n\tNULL,\r\n\tNULL,\r\n\tNULL,\r\n\tSW_VID_SetWindowCaption,\r\n\tSW_VID_GetRGBInfo,\r\n\r\n\tSW_SCR_UpdateScreen,\r\n\r\n\tSWBE_SelectMode,\r\n\tSWBE_DrawMesh_List,\r\n\tSWBE_DrawMesh_Single,\r\n\tSWBE_SubmitBatch,\r\n\tSWBE_GetTempBatch,\r\n\tSWBE_DrawWorld,\r\n\tSWBE_Init,\r\n\tSWBE_GenBrushModelVBO,\r\n\tSWBE_ClearVBO,\r\n\tSWBE_UploadAllLightmaps,\r\n\tSWBE_SelectEntity,\r\n\tSWBE_SelectDLight,\r\n\tSWBE_Scissor,\r\n\tSWBE_LightCullModel,\r\n\r\n\tSW_VBO_Begin,\r\n\tSW_VBO_Data,\r\n\tSW_VBO_Finish,\r\n\tSW_VBO_Destroy,\r\n\r\n\tSWBE_RenderToTextureUpdate2d,\r\n\r\n\t\"no more\"\r\n};\r\n#endif\r\n"
  },
  {
    "path": "engine/sw/sw_spans.h",
    "content": "/*\r\nthis file is expected to be #included as the body of a real function\r\nto define create a new pixel shader, define PLOT_PIXEL(outval) at the top of your function and you're good to go\r\n\r\n//modifiers:\r\nSPAN_ST - interpolates S+T across the span. access with 'sc' and 'tc'\r\n\t\taffine... no perspective correction.\r\n\r\n\r\n*/\r\n\r\n{\r\n\tswvert_t *vt;\r\n\tint y;\r\n\tint secondhalf;\r\n\tint xl,xld, xr,xrd;\r\n#ifdef SPAN_ST\r\n\tfloat sl,sld, sd;\r\n\tfloat tl,tld, td;\r\n#endif\r\n#ifdef SPAN_Z\r\n\tunsigned int zl,zld, zd;\r\n#endif\r\n\tunsigned int *restrict outbuf;\r\n\tunsigned int *restrict ti;\r\n\tint i;\r\n\tconst swvert_t *vlt,*vlb,*vrt,*vrb;\r\n\tint spanlen;\r\n\tint numspans;\r\n\tunsigned int *vplout;\r\n\tint dx, dy;\r\n\tint recalcside;\r\n\tint interlace;\r\n\r\n\tfloat fdx1,fdy1,fdx2,fdy2,fz,d1,d2;\r\n\r\n\tif (!img)\r\n\t\treturn;\r\n\r\n\t/*we basically render a diamond\r\n\tthat is, the single triangle is split into two triangles, outwards towards the midpoint and inwards to the final position.\r\n\t*/\r\n\r\n\t/*reorder the verticies for height*/\r\n\tif (v1->vcoord[1] > v2->vcoord[1])\r\n\t{\r\n\t\tvt = v1;\r\n\t\tv1 = v2;\r\n\t\tv2 = vt;\r\n\t}\r\n\tif (v1->vcoord[1] > v3->vcoord[1])\r\n\t{\r\n\t\tvt = v1;\r\n\t\tv1 = v3;\r\n\t\tv3 = vt;\r\n\t}\r\n\tif (v2->vcoord[1] > v3->vcoord[1])\r\n\t{\r\n\t\tvt = v3;\r\n\t\tv3 = v2;\r\n\t\tv2 = vt;\r\n\t}\r\n\r\n\t{\r\n\t\tconst swvert_t *v[3];\r\n\r\n\t\tv[0] = v1;\r\n\t\tv[1] = v2;\r\n\t\tv[2] = v3;\r\n\r\n\t\t//reject triangles with any point offscreen, for now\r\n\t\tfor (i = 0; i < 3; i++)\r\n\t\t{\r\n\t\t\tif (v[i]->vcoord[0] < 0 || v[i]->vcoord[0] >= th->vpwidth)\r\n\t\t\t\treturn;\r\n\t\t\tif (v[i]->vcoord[1] < 0 || v[i]->vcoord[1] >= th->vpheight)\r\n\t\t\t\treturn;\r\n\t\t\tif (v[i]->vcoord[2] < 0)\r\n\t\t\t\treturn;\r\n\t\t}\r\n\r\n\t\tfor (i = 0; i < 2; i++)\r\n\t\t{\r\n\t\t\tif (v[i]->vcoord[1] > v[i+1]->vcoord[1])\r\n\t\t\t\treturn;\r\n\t\t}\r\n\t}\r\n\r\n\tfdx1 = v2->vcoord[0] - v1->vcoord[0];\r\n\tfdy1 = v2->vcoord[1] - v1->vcoord[1];\r\n\r\n\tfdx2 = v3->vcoord[0] - v1->vcoord[0];\r\n\tfdy2 = v3->vcoord[1] - v1->vcoord[1];\r\n\r\n\tfz = fdx1*fdy2 - fdx2*fdy1;\r\n\r\n\tif (fz == 0)\r\n\t{\r\n\t\t//weird angle...\r\n\t\treturn;\r\n\t}\r\n\r\n\tfz = 1.0 / fz;\r\n\tfdx1 *= fz;\r\n\tfdy1 *= fz;\r\n\tfdx2 *= fz;\r\n\tfdy2 *= fz;\r\n\r\n#ifdef SPAN_ST\t//affine\r\n\td1 = v2->tccoord[0] - v1->tccoord[0];\r\n\td2 = v3->tccoord[0] - v1->tccoord[0];\r\n\tsld = fdx1*d2 - fdx2*d1;\r\n\tsd = fdy2*d1 - fdy1*d2;\r\n\r\n\td1 = v2->tccoord[1] - v1->tccoord[1];\r\n\td2 = v3->tccoord[1] - v1->tccoord[1];\r\n\ttld = fdx1*d2 - fdx2*d1;\r\n\ttd = fdy2*d1 - fdy1*d2;\r\n#endif\r\n#ifdef SPAN_Z\r\n\td1 = (v2->vcoord[2] - v1->vcoord[2])*UINT_MAX;\r\n\td2 = (v3->vcoord[2] - v1->vcoord[2])*UINT_MAX;\r\n\tzld = fdx1*d2 - fdx2*d1;\r\n\tzd = fdy2*d1 - fdy1*d2;\r\n#endif\r\n\r\n\tti = img->data;\r\n\r\n\ty = v1->vcoord[1];\r\n\r\n\tfor (secondhalf = 0; secondhalf <= 1; secondhalf++)\r\n\t{\r\n\t\tif (secondhalf)\r\n\t\t{\r\n\t\t\tif (numspans < 0)\r\n\t\t\t{\r\n\t\t\t\tinterlace = -numspans;\r\n\t\t\t\ty+=interlace;\r\n\t\t\t\tnumspans-=interlace;\r\n\r\n\t\t\t\txl += xld*interlace;\r\n\t\t\t\txr += xrd*interlace;\r\n\t\t\t\tvplout += th->vpcstride*interlace;\r\n\r\n#ifdef SPAN_ST\r\n\t\t\t\tsl += sld*interlace;\r\n\t\t\t\ttl += tld*interlace;\r\n#endif\r\n#ifdef SPAN_Z\r\n\t\t\t\tzl += zld*interlace;\r\n#endif\r\n\t\t\t}\r\n\r\n\t\t\t/*v2->v3*/\r\n\t\t\tif (fz <= 0)\r\n\t\t\t{\r\n\t\t\t\tvlt = v2;\r\n\t\t\t\t//vrt == v1;\r\n\t\t\t\tvlb = v3;\r\n\t\t\t\t//vrb == v3;\r\n\r\n\t\t\t\trecalcside = 1;\r\n\r\n#ifdef SPAN_ST\r\n\t\t\t\tsld -= sd*xld/(float)(1<<16);\r\n\t\t\t\ttld -= td*xld/(float)(1<<16);\r\n#endif\r\n#ifdef SPAN_Z\r\n\t\t\t\tzld -= zd*xld/(float)(1<<16);\r\n#endif\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\t//vlt == v1;\r\n\t\t\t\tvrt = v2;\r\n\t\t\t\t///vlb == v3;\r\n\t\t\t\tvrb = v3;\r\n\r\n\t\t\t\trecalcside = 2;\r\n\t\t\t}\r\n\r\n\t\t\t//flip the triangle to keep it facing the screen (we swapped the verts almost randomly)\r\n\t\t\tnumspans = v3->vcoord[1] - y;\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tvlt = v1;\r\n\t\t\tvrt = v1;\r\n\t\t\t/*v1->v2*/\r\n\t\t\tif (fz < 0)\r\n\t\t\t{\r\n\t\t\t\tvlb = v2;\r\n\t\t\t\tvrb = v3;\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tvlb = v3;\r\n\t\t\t\tvrb = v2;\r\n\t\t\t}\r\n\t\t\trecalcside = 3;\r\n\r\n\t\t\t//flip the triangle to keep it facing the screen (we swapped the verts almost randomly)\r\n\t\t\tnumspans = v2->vcoord[1] - y;\r\n\t\t}\r\n\r\n\t\tif (recalcside & 1)\r\n\t\t{\r\n\t\t\tdx = (vlb->vcoord[0] - vlt->vcoord[0]);\r\n\t\t\tdy = (vlb->vcoord[1] - vlt->vcoord[1]);\r\n\t\t\tif (dy > 0)\r\n\t\t\t\txld = (dx<<16) / dy;\r\n\t\t\telse\r\n\t\t\t\txld = 0;\r\n\t\t\txl = (int)vlt->vcoord[0]<<16;\r\n\r\n#ifdef SPAN_ST\r\n\t\t\tsl = vlt->tccoord[0];\r\n\t\t\tsld = sld + sd*xld/(float)(1<<16);\r\n\t\t\ttl = vlt->tccoord[1];\r\n\t\t\ttld = tld + td*xld/(float)(1<<16);\r\n#endif\r\n#ifdef SPAN_Z\r\n\t\t\tzl = vlt->vcoord[2]*UINT_MAX;\r\n\t\t\tzld = zld + zd*xld/(float)(1<<16);\r\n#endif\r\n\t\t}\r\n\r\n\t\tif (recalcside & 2)\r\n\t\t{\r\n\t\t\tdx = (vrb->vcoord[0] - vrt->vcoord[0]);\r\n\t\t\tdy = (vrb->vcoord[1] - vrt->vcoord[1]);\r\n\t\t\tif (dy)\r\n\t\t\t\txrd = (dx<<16) / dy;\r\n\t\t\telse\r\n\t\t\t\txrd = 0;\r\n\t\t\txr = (int)vrt->vcoord[0]<<16;\r\n\t\t}\r\n\r\n\r\n\r\n\t\tif (y + numspans >= th->vpheight)\r\n\t\t\tnumspans = th->vpheight - y - 1;\r\n\r\n\t\tif (numspans <= 0)\r\n\t\t\tcontinue;\r\n\r\n\r\n\t\tvplout = th->vpcbuf + y * th->vpcstride;\t//this is a pointer to the left of the viewport buffer.\r\n\r\n\t\tinterlace = ((y + th->interlaceline) % th->interlacemod);\r\n\t\tif (interlace)\r\n\t\t{\r\n\t\t\tif (interlace > numspans)\r\n\t\t\t{\r\n\t\t\t\tinterlace = numspans;\r\n\t\t\t\ty+=interlace;\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\ty+=interlace;\r\n\t\t\t\tnumspans-=interlace;\r\n\t\t\t}\r\n\t\t\txl += xld*interlace;\r\n\t\t\txr += xrd*interlace;\r\n\t\t\tvplout += th->vpcstride*interlace;\r\n\r\n#ifdef SPAN_ST\r\n\t\t\tsl += sld*interlace;\r\n\t\t\ttl += tld*interlace;\r\n#endif\r\n#ifdef SPAN_Z\r\n\t\t\tzl += zld*interlace;\r\n#endif\r\n\t\t}\r\n\r\n\t\tfor (; numspans > 0; \r\n\t\t\tnumspans -= th->interlacemod\r\n\t\t\t,xl += xld*th->interlacemod\r\n\t\t\t,xr += xrd*th->interlacemod\r\n\t\t\t,vplout += th->vpcstride*th->interlacemod\r\n\t\t\t,y += th->interlacemod\r\n\r\n#ifdef SPAN_ST\r\n\t\t\t,sl += sld*th->interlacemod\r\n\t\t\t,tl += tld*th->interlacemod\r\n#endif\r\n#ifdef SPAN_Z\r\n\t\t\t,zl += zld*th->interlacemod\r\n#endif\r\n\t\t\t)\r\n\t\t{\r\n#ifdef SPAN_ST\r\n\t\t\tfloat s = sl;\r\n\t\t\tfloat t = tl;\r\n#endif\r\n#ifdef SPAN_Z\r\n\t\t\tunsigned int z = zl;\r\n\t\t\tunsigned int *restrict zb = th->vpdbuf + y * th->vpwidth + (xl>>16);\r\n#endif\r\n\r\n\t\t\tspanlen = (xr - xl)>>16;\r\n\t\t\toutbuf = vplout + (xl>>16);\r\n\r\n\t\t\twhile(spanlen-->=0)\r\n\t\t\t{\r\n\t\t\t\tPLOT_PIXEL(*outbuf);\r\n\t\t\t\toutbuf++;\r\n\r\n#ifdef SPAN_ST\r\n\t\t\t\ts += sd;\r\n\t\t\t\tt += td;\r\n#endif\r\n#ifdef SPAN_Z\r\n\t\t\t\tz += zd;\r\n\t\t\t\tzb++;\r\n#endif\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n}\r\n\r\n#undef SPAN_ST\r\n#undef PLOT_PIXEL\r\n"
  },
  {
    "path": "engine/sw/sw_viddos.c",
    "content": "#include \"quakedef.h\"\r\n#ifdef SWQUAKE\r\n#include \"sw.h\"\r\n\r\n\r\n\r\n\r\n/* from http://www.delorie.com/djgpp/doc/ug/graphics/vesa.html */\r\ntypedef struct VESA_INFO\r\n{\r\n\tunsigned char  VESASignature[4];\r\n\tunsigned short VESAVersion          __attribute__ ((packed));\r\n\tunsigned long  OEMStringPtr         __attribute__ ((packed));\r\n\tunsigned char  Capabilities[4];\r\n\tunsigned long  VideoModePtr         __attribute__ ((packed));\r\n\tunsigned short TotalMemory          __attribute__ ((packed));\r\n\tunsigned short OemSoftwareRev       __attribute__ ((packed));\r\n\tunsigned long  OemVendorNamePtr     __attribute__ ((packed));\r\n\tunsigned long  OemProductNamePtr    __attribute__ ((packed));\r\n\tunsigned long  OemProductRevPtr     __attribute__ ((packed));\r\n\tunsigned char  Reserved[222];\r\n\tunsigned char  OemData[256];\r\n} VESA_INFO;\r\n\r\n\r\n\r\n#include <dpmi.h>\r\n#include <go32.h>\r\n#include <sys/farptr.h>\r\n\r\n\r\nstatic VESA_INFO vesa_info;\r\n\r\n\r\nstatic int get_vesa_info()\r\n{\r\n\t__dpmi_regs r;\r\n\tlong dosbuf;\r\n\tint c;\r\n\r\n\t/* use the conventional memory transfer buffer */\r\n\tdosbuf = __tb & 0xFFFFF;\r\n\r\n\t/* initialize the buffer to zero */\r\n\tfor (c=0; c<sizeof(VESA_INFO); c++)\r\n\t\t_farpokeb(_dos_ds, dosbuf+c, 0);\r\n\r\n\tdosmemput(\"VBE2\", 4, dosbuf);\r\n\r\n\t/* call the VESA function */\r\n\tr.x.ax = 0x4F00;\r\n\tr.x.di = dosbuf & 0xF;\r\n\tr.x.es = (dosbuf>>4) & 0xFFFF;\r\n\t__dpmi_int(0x10, &r);\r\n\r\n\t/* quit if there was an error */\r\n\tif (r.h.ah)\r\n\t\treturn -1;\r\n\r\n\t/* copy the resulting data into our structure */\r\n\tdosmemget(dosbuf, sizeof(VESA_INFO), &vesa_info);\r\n\r\n\t/* check that we got the right magic marker value */\r\n\tif (strncmp(vesa_info.VESASignature, \"VESA\", 4) != 0)\r\n\t\treturn -1;\r\n\r\n\t/* it worked! */\r\n\treturn 0;\r\n}\r\n\r\n\r\ntypedef struct MODE_INFO\r\n{\r\n\tunsigned short ModeAttributes       __attribute__ ((packed));\r\n\tunsigned char  WinAAttributes;\r\n\tunsigned char  WinBAttributes;\r\n\tunsigned short WinGranularity       __attribute__ ((packed));\r\n\tunsigned short WinSize              __attribute__ ((packed));\r\n\tunsigned short WinASegment          __attribute__ ((packed));\r\n\tunsigned short WinBSegment          __attribute__ ((packed));\r\n\tunsigned long  WinFuncPtr           __attribute__ ((packed));\r\n\tunsigned short BytesPerScanLine     __attribute__ ((packed));\r\n\tunsigned short XResolution          __attribute__ ((packed));\r\n\tunsigned short YResolution          __attribute__ ((packed));\r\n\tunsigned char  XCharSize;\r\n\tunsigned char  YCharSize;\r\n\tunsigned char  NumberOfPlanes;\r\n\tunsigned char  BitsPerPixel;\r\n\tunsigned char  NumberOfBanks;\r\n\tunsigned char  MemoryModel;\r\n\tunsigned char  BankSize;\r\n\tunsigned char  NumberOfImagePages;\r\n\tunsigned char  Reserved_page;\r\n\tunsigned char  RedMaskSize;\r\n\tunsigned char  RedMaskPos;\r\n\tunsigned char  GreenMaskSize;\r\n\tunsigned char  GreenMaskPos;\r\n\tunsigned char  BlueMaskSize;\r\n\tunsigned char  BlueMaskPos;\r\n\tunsigned char  ReservedMaskSize;\r\n\tunsigned char  ReservedMaskPos;\r\n\tunsigned char  DirectColorModeInfo;\r\n\tunsigned long  PhysBasePtr          __attribute__ ((packed));\r\n\tunsigned long  OffScreenMemOffset   __attribute__ ((packed));\r\n\tunsigned short OffScreenMemSize     __attribute__ ((packed));\r\n\tunsigned char  Reserved[206];\r\n} MODE_INFO;\r\n\r\n\r\nstatic MODE_INFO mode_info;\r\n\r\n\r\nstatic int get_mode_info(int mode)\r\n{\r\n\t__dpmi_regs r;\r\n\tlong dosbuf;\r\n\tint c;\r\n\r\n\t/* use the conventional memory transfer buffer */\r\n\tdosbuf = __tb & 0xFFFFF;\r\n\r\n\t/* initialize the buffer to zero */\r\n\tfor (c=0; c<sizeof(MODE_INFO); c++)\r\n\t\t_farpokeb(_dos_ds, dosbuf+c, 0);\r\n\r\n\t/* call the VESA function */\r\n\tr.x.ax = 0x4F01;\r\n\tr.x.di = dosbuf & 0xF;\r\n\tr.x.es = (dosbuf>>4) & 0xFFFF;\r\n\tr.x.cx = mode;\r\n\t__dpmi_int(0x10, &r);\r\n\r\n\t/* quit if there was an error */\r\n\tif (r.h.ah)\r\n\t\treturn -1;\r\n\r\n\t/* copy the resulting data into our structure */\r\n\tdosmemget(dosbuf, sizeof(MODE_INFO), &mode_info);\r\n\r\n\t/* it worked! */\r\n\treturn 0;\r\n}\r\n\r\n\r\nstatic int find_vesa_mode(int w, int h, int bpp)\r\n{\r\n\tint mode_list[256];\r\n\tint number_of_modes;\r\n\tlong mode_ptr;\r\n\tint c;\r\n\r\n\t/* check that the VESA driver exists, and get information about it */\r\n\tif (get_vesa_info() != 0)\r\n\t\treturn 0;\r\n\r\n\t/* convert the mode list pointer from seg:offset to a linear address */\r\n\tmode_ptr = ((vesa_info.VideoModePtr & 0xFFFF0000) >> 12) + (vesa_info.VideoModePtr & 0xFFFF);\r\n\r\n\tnumber_of_modes = 0;\r\n\r\n\t/* read the list of available modes */\r\n\twhile (_farpeekw(_dos_ds, mode_ptr) != 0xFFFF)\r\n\t{\r\n\t\tmode_list[number_of_modes] = _farpeekw(_dos_ds, mode_ptr);\r\n\t\tnumber_of_modes++;\r\n\t\tmode_ptr += 2;\r\n\t}\r\n\r\n\t/* scan through the list of modes looking for the one that we want */\r\n\tfor (c=0; c<number_of_modes; c++)\r\n\t{\r\n\t\t/* get information about this mode */\r\n\t\tif (get_mode_info(mode_list[c]) != 0)\r\n\t\t\tcontinue;\r\n\r\n\t\t/* check the flags field to make sure this is a color graphics mode,\r\n\t\t* and that it is supported by the current hardware */\r\n\t\tif ((mode_info.ModeAttributes & 0x19) != 0x19)\r\n\t\t\tcontinue;\r\n\r\n\t\t/* check that this mode is the right size */\r\n\t\tif ((mode_info.XResolution != w) || (mode_info.YResolution != h))\r\n\t\t\tcontinue;\r\n\r\n\t\t/* check that there is only one color plane */\r\n\t\tif (mode_info.NumberOfPlanes != 1)\r\n\t\t\tcontinue;\r\n\r\n\t\t/* check that it is a packed-pixel mode (other values are used for\r\n\t\t* different memory layouts, eg. 6 for a truecolor resolution) */\r\n\t\tif (mode_info.MemoryModel != ((bpp==8)?4:6))\r\n\t\t\tcontinue;\r\n\r\n\t\t/* check that this is an 8-bit (256 color) mode */\r\n\t\tif (mode_info.BitsPerPixel != bpp)\r\n\t\t\tcontinue;\r\n\r\n\t\t/* if it passed all those checks, this must be the mode we want! */\r\n\t\treturn mode_list[c];\r\n\t}\r\n\r\n\t/* oh dear, there was no mode matching the one we wanted! */\r\n\treturn 0; \r\n}\r\n\r\n\r\nstatic int set_vesa_mode(int w, int h, int bpp)\r\n{\r\n\t__dpmi_regs r;\r\n\tint mode_number;\r\n\r\n\t/* find the number for this mode */\r\n\tmode_number = find_vesa_mode(w, h, bpp);\r\n\tif (!mode_number)\r\n\t\treturn -1;\r\n\r\n\t/* call the VESA mode set function */\r\n\tr.x.ax = 0x4F02;\r\n\tr.x.bx = mode_number;\r\n\t__dpmi_int(0x10, &r);\r\n\tif (r.h.ah)\r\n\t\treturn -1;\r\n\r\n\t/* it worked! */\r\n\treturn 0;\r\n}\r\n\r\nvoid set_vesa_bank(int bank_number)\r\n{\r\n\t__dpmi_regs r;\r\n\r\n\tr.x.ax = 0x4F05;\r\n\tr.x.bx = 0;\r\n\tr.x.dx = bank_number;\r\n\t__dpmi_int(0x10, &r);\r\n}\r\n\r\nstatic void copy_to_vesa_screen(char *memory_buffer, int screen_size)\r\n{\r\n\t//FIXME: use OffScreenMemOffset if possible.\r\n\r\n\tint bank_size = mode_info.WinSize*1024;\r\n\tint bank_granularity = mode_info.WinGranularity*1024;\r\n\tint bank_number = 0;\r\n\tint todo = screen_size;\r\n\tint copy_size;\r\n\r\n\twhile (todo > 0)\r\n\t{\r\n\t\t/* select the appropriate bank */\r\n\t\tset_vesa_bank(bank_number);\r\n\r\n\t\t/* how much can we copy in one go? */\r\n\t\tif (todo > bank_size)\r\n\t\t\tcopy_size = bank_size;\r\n\t\telse\r\n\t\t\tcopy_size = todo;\r\n\r\n\t\t/* copy a bank of data to the screen */\r\n\t\tdosmemput(memory_buffer, copy_size, 0xA0000);\r\n\r\n\t\t/* move on to the next bank of data */\r\n\t\ttodo -= copy_size;\r\n\t\tmemory_buffer += copy_size;\r\n\t\tbank_number += bank_size/bank_granularity;\r\n\t}\r\n}\r\n\r\n\r\nextern int nostdout;\t//we flag with 0x800 to disable printfs while displaying stuff.\r\nstatic qboolean videoatexitregistered;\r\nstatic void videoatexit(void)\r\n{\r\n\tif (nostdout & 0x800)\r\n\t{\r\n\t\tnostdout &= ~0x800;\r\n\r\n\t\t__dpmi_regs r;\r\n\t\tr.x.ax = 0x0000 | 3;\r\n\t\t__dpmi_int(0x10, &r);\r\n\t}\r\n}\r\n\r\n\r\nstatic unsigned int *backbuffer;\r\nstatic unsigned int *depthbuffer;\r\nstatic unsigned int framenumber;\r\n\r\n//#define NORENDER\r\n\r\nqboolean SW_VID_Init(rendererstate_t *info, unsigned char *palette)\r\n{\r\n\tint bpp = info->bpp;\r\n\tvid.pixelwidth = info->width;\r\n\tvid.pixelheight = info->height;\r\n\r\n\tif (bpp != 32)\r\n\t\tbpp = 32; //sw renderer supports only this\r\n\r\n#ifndef NORENDER\r\n\tnostdout |= 0x800;\r\n\tif (set_vesa_mode(vid.pixelwidth, vid.pixelheight, bpp) < 0)\r\n\t\treturn false;\r\n#endif\r\n\r\n\tif (!videoatexitregistered)\r\n\t{\r\n\t\tvideoatexitregistered = true;\r\n\t\tatexit(videoatexit);\r\n\t}\r\n\r\n\tbackbuffer = BZ_Malloc(vid.pixelwidth * vid.pixelheight * sizeof(*backbuffer));\r\n\tif (!backbuffer)\r\n\t\treturn false;\r\n\tdepthbuffer = BZ_Malloc(vid.pixelwidth * vid.pixelheight * sizeof(*depthbuffer));\r\n\tif (!depthbuffer)\r\n\t\treturn false;\r\n\r\n\treturn true;\r\n}\r\nvoid SW_VID_DeInit(void)\r\n{\r\n\tBZ_Free(backbuffer);\r\n\tbackbuffer = NULL;\r\n\tBZ_Free(depthbuffer);\r\n\tdepthbuffer = NULL;\r\n}\r\nqboolean SW_VID_ApplyGammaRamps\t\t(unsigned int rampcount, unsigned short *ramps)\r\n{\t//no gamma ramps with VESA.\r\n\treturn false;\r\n}\r\nchar *SW_VID_GetRGBInfo(int *bytestride, int *truevidwidth, int *truevidheight, enum uploadfmt *fmt)\r\n{\r\n\tvoid *ret = BZ_Malloc(vid.pixelwidth*vid.pixelheight*4);\r\n\tif (!ret)\r\n\t\treturn NULL;\r\n\r\n\tmemcpy(ret, backbuffer, vid.pixelwidth*vid.pixelheight*4);\r\n\t*bytestride = vid.pixelwidth*4;\r\n\t*truevidwidth = vid.pixelwidth;\r\n\t*truevidheight = vid.pixelheight;\r\n\t*fmt = TF_BGRX32;\r\n\treturn ret;\r\n}\r\nvoid SW_VID_SetWindowCaption(const char *msg)\r\n{\r\n}\r\nvoid SW_VID_SwapBuffers(void)\r\n{\r\n#ifndef NORENDER\r\n\tcopy_to_vesa_screen((char*)backbuffer, vid.pixelwidth*vid.pixelheight*4);\r\n#endif\r\n\tframenumber++;\r\n}\r\nvoid SW_VID_UpdateViewport(wqcom_t *com)\r\n{\r\n\tcom->viewport.cbuf = backbuffer + vid.pixelwidth*(vid.pixelheight-1);\r\n\tcom->viewport.dbuf = depthbuffer;\r\n\tcom->viewport.width = vid.pixelwidth;\r\n\tcom->viewport.height = vid.pixelheight;\r\n\tcom->viewport.stride = -vid.pixelwidth;\t//this is in pixels. which is stupid.\r\n\tcom->viewport.framenum = framenumber;\r\n}\r\n\r\n#endif\r\n"
  },
  {
    "path": "engine/sw/sw_vidwin.c",
    "content": "#include \"quakedef.h\"\n#ifdef SWQUAKE\n#include \"sw.h\"\n\n#include \"winquake.h\"\n/*Fixup outdated windows headers*/\n#ifndef WM_XBUTTONDOWN\n   #define WM_XBUTTONDOWN      0x020B\n   #define WM_XBUTTONUP      0x020C\n#endif\n#ifndef MK_XBUTTON1\n   #define MK_XBUTTON1         0x0020\n#endif\n#ifndef MK_XBUTTON2\n   #define MK_XBUTTON2         0x0040\n#endif\n// copied from DarkPlaces in an attempt to grab more buttons\n#ifndef MK_XBUTTON3\n   #define MK_XBUTTON3         0x0080\n#endif\n#ifndef MK_XBUTTON4\n   #define MK_XBUTTON4         0x0100\n#endif\n#ifndef MK_XBUTTON5\n   #define MK_XBUTTON5         0x0200\n#endif\n#ifndef MK_XBUTTON6\n   #define MK_XBUTTON6         0x0400\n#endif\n#ifndef MK_XBUTTON7\n   #define MK_XBUTTON7         0x0800\n#endif\n#ifndef WM_INPUT\n\t#define WM_INPUT 255\n#endif\n\n\n\ntypedef struct dibinfo\n{\n\tBITMAPINFOHEADER\theader;\n\tRGBQUAD\t\t\t\tacolors[256];\n} dibinfo_t;\n\n//struct\n//{\n\tHBITMAP hDIBSection;\n\tqbyte *pDIBBase;\n\n\tHDC mainhDC;\n\tHDC hdcDIBSection;\n\tHGDIOBJ previously_selected_GDI_obj;\n\n\tint framenumber;\n\tvoid *screenbuffer;\n\tqintptr_t screenpitch;\n\tint window_x, window_y;\n\tunsigned int *depthbuffer;\nstruct\n{\n\tqboolean isfullscreen;\n} w32sw;\nHWND mainwindow;\n\nvoid DIB_Shutdown(void)\n{\n\tif (depthbuffer)\n\t{\n\t\tBZ_Free(depthbuffer);\n\t\tdepthbuffer = NULL;\n\t}\n\n\tif (hdcDIBSection)\n\t{\n\t\tSelectObject(hdcDIBSection, previously_selected_GDI_obj);\n\t\tDeleteDC(hdcDIBSection);\n\t\thdcDIBSection = NULL;\n\t}\n\n\tif (hDIBSection)\n\t{\n\t\tDeleteObject(hDIBSection);\n\t\thDIBSection = NULL;\n\t\tpDIBBase = NULL;\n\t}\n\n\tif (mainhDC)\n\t{\n\t\tReleaseDC(mainwindow, mainhDC);\n\t\tmainhDC = 0;\n\t}\n}\nqboolean DIB_Init(void)\n{\n\tdibinfo_t   dibheader;\n\tBITMAPINFO *pbmiDIB = ( BITMAPINFO * ) &dibheader;\n\tint i;\n\n\tmemset( &dibheader, 0, sizeof( dibheader ) );\n\n\t/*\n\t** grab a DC\n\t*/\n\tif ( !mainhDC )\n\t{\n\t\tif ( ( mainhDC = GetDC( mainwindow ) ) == NULL )\n\t\t\treturn false;\n\t}\n\n\t/*\n\t** fill in the BITMAPINFO struct\n\t*/\n\tpbmiDIB->bmiHeader.biSize          = sizeof(BITMAPINFOHEADER);\n\tpbmiDIB->bmiHeader.biWidth         = vid.pixelwidth;\n\tpbmiDIB->bmiHeader.biHeight        = vid.pixelheight;\n\tpbmiDIB->bmiHeader.biPlanes        = 1;\n\tpbmiDIB->bmiHeader.biBitCount      = 32;\n\tpbmiDIB->bmiHeader.biCompression   = BI_RGB;\n\tpbmiDIB->bmiHeader.biSizeImage     = 0;\n\tpbmiDIB->bmiHeader.biXPelsPerMeter = 0;\n\tpbmiDIB->bmiHeader.biYPelsPerMeter = 0;\n\tpbmiDIB->bmiHeader.biClrUsed       = 0;\n\tpbmiDIB->bmiHeader.biClrImportant  = 0;\n\n\t/*\n\t** fill in the palette\n\t*/\n\tfor ( i = 0; i < 256; i++ )\n\t{\n\t\tdibheader.acolors[i].rgbRed   = ( d_8to24rgbtable[i] >> 0 )  & 0xff;\n\t\tdibheader.acolors[i].rgbGreen = ( d_8to24rgbtable[i] >> 8 )  & 0xff;\n\t\tdibheader.acolors[i].rgbBlue  = ( d_8to24rgbtable[i] >> 16 ) & 0xff;\n\t}\n\n\t/*\n\t** create the DIB section\n\t*/\n\thDIBSection = CreateDIBSection( mainhDC,\n\t\t\t\t\t\t\t\t\t\t\t pbmiDIB,\n\t\t\t\t\t\t\t\t\t\t\t DIB_RGB_COLORS,\n\t\t\t\t\t\t\t\t\t\t\t (void**)&pDIBBase,\n\t\t\t\t\t\t\t\t\t\t\t NULL,\n\t\t\t\t\t\t\t\t\t\t\t 0 );\n\n\tif ( hDIBSection == NULL )\n\t{\n\t\tCon_Printf( \"DIB_Init() - CreateDIBSection failed\\n\" );\n\t\tgoto fail;\n\t}\n\n\tif (pbmiDIB->bmiHeader.biHeight < 0)\n\t{\n\t\t// bottom up\n\t\tscreenbuffer\t= pDIBBase + ( vid.pixelheight - 1 ) * vid.pixelwidth * 4;\n\t\tscreenpitch\t\t= -(int)vid.pixelwidth;\n\t}\n\telse\n\t{\n\t\t// top down\n\t\tscreenbuffer\t= pDIBBase;\n\t\tscreenpitch\t\t= vid.pixelwidth;\n\t}\n\n\t/*\n\t** clear the DIB memory buffer\n\t*/\n\tmemset( pDIBBase, 0, vid.pixelwidth * vid.pixelheight * 4);\n\n\tif ( ( hdcDIBSection = CreateCompatibleDC( mainhDC ) ) == NULL )\n\t{\n\t\tCon_Printf( \"DIB_Init() - CreateCompatibleDC failed\\n\" );\n\t\tgoto fail;\n\t}\n\tif ( ( previously_selected_GDI_obj = SelectObject( hdcDIBSection, hDIBSection ) ) == NULL )\n\t{\n\t\tCon_Printf( \"DIB_Init() - SelectObject failed\\n\" );\n\t\tgoto fail;\n\t}\n\n\tdepthbuffer = BZ_Malloc(vid.pixelwidth * vid.pixelheight * sizeof(*depthbuffer));\n\tif (depthbuffer)\n\t\treturn true;\n\nfail:\n\tDIB_Shutdown();\n\treturn false;\n}\nvoid DIB_SwapBuffers(void)\n{\n//\textern float usingstretch;\n\n//\tif (usingstretch == 1)\n\t\tBitBlt( mainhDC,\n\t\t\t\t0, 0,\n\t\t\t\tvid.pixelwidth,\n\t\t\t\tvid.pixelheight,\n\t\t\t\thdcDIBSection,\n\t\t\t\t0, 0,\n\t\t\t\tSRCCOPY );\n/*\telse\n\t\tStretchBlt( mainhDC,\t//Why is StretchBlt not optimised for a scale of 2? Surly that would be a frequently used quantity?\n\t\t\t0, 0,\n\t\t\tvid.width*usingstretch,\n\t\t\tvid.height*usingstretch,\n\t\t\thdcDIBSection,\n\t\t\t0, 0,\n\t\t\tvid.width, vid.height,\n\t\t\tSRCCOPY );\n*/\n}\n\nstatic int window_width;\nstatic int window_height;\nvoid SWV_UpdateWindowStatus(void)\n{\n\tPOINT p;\n\tRECT nr;\n\tRECT WindowRect;\n\tGetClientRect(mainwindow, &nr);\n\n\t//if its bad then we're probably minimised\n\tif (nr.right <= nr.left)\n\t\treturn;\n\tif (nr.bottom <= nr.top)\n\t\treturn;\n\n\tWindowRect = nr;\n\tp.x = 0;\n\tp.y = 0;\n\tClientToScreen(mainwindow, &p);\n\twindow_x = p.x;\n\twindow_y = p.y;\n\twindow_width = WindowRect.right - WindowRect.left;\n\twindow_height = WindowRect.bottom - WindowRect.top;\n\n\twindow_rect.left = window_x;\n\twindow_rect.top = window_y;\n\twindow_rect.right = window_x + window_width;\n\twindow_rect.bottom = window_y + window_height;\n\twindow_center_x = (window_rect.left + window_rect.right) / 2;\n\twindow_center_y = (window_rect.top + window_rect.bottom) / 2;\n\n\tINS_UpdateClipCursor ();\n}\n\n\nqboolean SWAppActivate(BOOL fActive, BOOL minimize)\n/****************************************************************************\n*\n* Function:     AppActivate\n* Parameters:   fActive - True if app is activating\n*\n* Description:  If the application is activating, then swap the system\n*               into SYSPAL_NOSTATIC mode so that our palettes will display\n*               correctly.\n*\n****************************************************************************/\n{\n\tstatic BOOL\tsound_active;\n\n\tif (vid.activeapp == fActive && Minimized == minimize)\n\t\treturn false;\t//so windows doesn't crash us over and over again.\n\n\tvid.activeapp = fActive;\n\tMinimized = minimize;\n\n// enable/disable sound on focus gain/loss\n\tif (!vid.activeapp && sound_active)\n\t{\n\t\tS_BlockSound ();\n\t\tsound_active = false;\n\t}\n\telse if (vid.activeapp && !sound_active)\n\t{\n\t\tS_UnblockSound ();\n\t\tsound_active = true;\n\t}\n\n\tINS_UpdateGrabs(false, vid.activeapp);\n\n/*\n\tif (fActive)\n\t{\n\t\tif (modestate != MS_WINDOWED)\n\t\t{\n\t\t\tif (vid_canalttab && vid_wassuspended) {\n\t\t\t\tvid_wassuspended = false;\n\t\t\t\tChangeDisplaySettings (&gdevmode, CDS_FULLSCREEN);\n\t\t\t\tShowWindow(mainwindow, SW_SHOWNORMAL);\n\t\t\t}\n\t\t}\n\t}\n\tif (!fActive)\n\t{\n\t\tif (modestate != MS_WINDOWED)\n\t\t{\n\t\t\tif (vid_canalttab) {\n\t\t\t\tChangeDisplaySettings (NULL, 0);\n\t\t\t\tvid_wassuspended = true;\n\t\t\t}\n\t\t}\n\t}\n*/\n\treturn true;\n}\n\nLONG WINAPI MainWndProc (\n\tHWND    hWnd,\n\tUINT    uMsg,\n\tWPARAM  wParam,\n\tLPARAM  lParam)\n{\n\tLONG\t\t\tlRet = 0;\n\tint\t\t\t\tfActive, fMinimized, temp;\n\tHDC\t\t\t\thdc;\n\tPAINTSTRUCT\t\tps;\n\textern unsigned int uiWheelMessage;\n//\tstatic int\t\trecursiveflag;\n\n\tif ( uMsg == uiWheelMessage ) {\n\t\tuMsg = WM_MOUSEWHEEL;\n\t\twParam <<= 16;\n\t}\n\n\tswitch (uMsg)\n\t{\n\t\tcase WM_CREATE:\n\t\t\tbreak;\n\n\t\tcase WM_SYSCOMMAND:\n\n\t\t// Check for maximize being hit\n\t\t\tswitch (wParam & ~0x0F)\n\t\t\t{\n\t\t\t\tcase SC_MAXIMIZE:\n\t\t\t\t\tCbuf_AddText(\"vid_fullscreen 1;vid_restart\\n\", RESTRICT_LOCAL);\n\t\t\t\t// if minimized, bring up as a window before going fullscreen,\n\t\t\t\t// so MGL will have the right state to restore\n/*\t\t\t\t\tif (Minimized)\n\t\t\t\t\t{\n\t\t\t\t\t\tforce_mode_set = true;\n\t\t\t\t\t\tVID_SetMode (vid_modenum, vid_curpal);\n\t\t\t\t\t\tforce_mode_set = false;\n\t\t\t\t\t}\n\n\t\t\t\t\tVID_SetMode ((int)vid_fullscreen_mode.value, vid_curpal);\n*/\t\t\t\t\tbreak;\n\n\t\t\t\tcase SC_SCREENSAVE:\n\t\t\t\tcase SC_MONITORPOWER:\n\t\t\t\t\tif (w32sw.isfullscreen)\n\t\t\t\t\t{\n\t\t\t\t\t// don't call DefWindowProc() because we don't want to start\n\t\t\t\t\t// the screen saver fullscreen\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t// fall through windowed and allow the screen saver to start\n\n\t\t\t\tdefault:\n//\t\t\t\tif (!vid_initializing)\n//\t\t\t\t{\n//\t\t\t\t\tS_BlockSound ();\t\t\t\t\t\n//\t\t\t\t}\n\n\t\t\t\tlRet = DefWindowProc (hWnd, uMsg, wParam, lParam);\n\n//\t\t\t\tif (!vid_initializing)\n//\t\t\t\t{\n//\t\t\t\t\tS_UnblockSound ();\n//\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase WM_MOVE:\n\t\t\tSWV_UpdateWindowStatus ();\n\n//\t\t\tif ((modestate == MS_WINDOWED) && !in_mode_set && !Minimized)\n//\t\t\t\tVID_RememberWindowPos ();\n\n\t\t\tbreak;\n\n\t\tcase WM_SIZE:\n\t\t\tSWV_UpdateWindowStatus ();\n/*\t\t\tMinimized = false;\n\t\t\t\n\t\t\tif (!(wParam & SIZE_RESTORED))\n\t\t\t{\n\t\t\t\tif (wParam & SIZE_MINIMIZED)\n\t\t\t\t\tMinimized = true;\n\t\t\t}\n\n\t\t\tif (!Minimized && !w32sw.isfullscreen)\n\t\t\t{\n\t\t\t\tint nt, nl;\n\t\t\t\tint nw, nh;\n\t\t\t\tqboolean move = false;\n\t\t\t\tRECT r;\n\t\t\t\tGetClientRect (hWnd, &r);\n\t\t\t\tnw = (int)(r.right - r.left)&~3;\n\t\t\t\tnh = (int)(r.bottom - r.top)&~3;\n\n\t\t\t\twindow_width = nw;\n\t\t\t\twindow_height = nh;\n\t\t\t\tVID2_UpdateWindowStatus();\n\n\t\t\t\tif (nw < 320)\n\t\t\t\t{\n\t\t\t\t\tmove = true;\n\t\t\t\t\tnw = 320;\n\t\t\t\t}\n\t\t\t\tif (nh < 200)\n\t\t\t\t{\n\t\t\t\t\tmove = true;\n\t\t\t\t\tnh = 200;\n\t\t\t\t}\n\t\t\t\tif (nh > MAXHEIGHT)\n\t\t\t\t{\n\t\t\t\t\tmove = true;\n\t\t\t\t\tnh = MAXHEIGHT;\n\t\t\t\t}\n\t\t\t\tif ((r.right - r.left) & 3)\n\t\t\t\t\tmove = true;\n\t\t\t\tif ((r.bottom - r.top) & 3)\n\t\t\t\t\tmove = true;\n\n\t\t\t\tGetWindowRect (hWnd, &r);\n\t\t\t\tnl = r.left;\n\t\t\t\tnt = r.top;\n\t\t\t\tr.left =0;\n\t\t\t\tr.top = 0;\n\t\t\t\tr.right = nw*usingstretch;\n\t\t\t\tr.bottom = nh*usingstretch;\n\t\t\t\tAdjustWindowRectEx(&r, WS_OVERLAPPEDWINDOW, FALSE, 0);\n\t\t\t\tif (move)\n\t\t\t\t\tMoveWindow(hWnd, nl, nt, r.right - r.left, r.bottom - r.top, true);\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tif (vid.width != nw || vid.height != nh)\n\t\t\t\t\t{\n\t\t\t\t\t\tM_RemoveAllMenus();\t//can cause probs\n\t\t\t\t\t\tDIB_Shutdown();\n\t\t\t\t\t\tvid.conwidth = vid.width = nw;//vid_stretch.value;\n\t\t\t\t\t\tvid.conheight = vid.height = nh;///vid_stretch.value;\n\t\t\t\t\t\t\n\t\t\t\t\t\tDIB_Init( &vid.buffer, &vid.rowbytes );\n\t\t\t\t\t\tvid.conbuffer = vid.buffer;\n\t\t\t\t\t\tvid.conrowbytes = vid.rowbytes;\t\n\n\t\t\t\t\t\tif (VID_AllocBuffers(vid.width, vid.height, r_pixbytes))\n\t\t\t\t\t\t\tD_InitCaches (vid_surfcache, vid_surfcachesize);\n\n\t\t\t\t\t\tSCR_UpdateWholeScreen();\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\tSCR_UpdateWholeScreen();\n\t\t\t\t}\n\n\t\t\t}\n*/\n\t\t\tbreak;\n\n\t\tcase WM_SYSCHAR:\n\t\t// keep Alt-Space from happening\n\t\t\tbreak;\n\n\t\tcase WM_ACTIVATE:\n\t\t\tfActive = LOWORD(wParam);\n\t\t\tfMinimized = (BOOL) HIWORD(wParam);\n\t\t\tSWAppActivate(!(fActive == WA_INACTIVE), fMinimized);\n\n\t\t// fix the leftover Alt from any Alt-Tab or the like that switched us away\n//\t\t\tClearAllStates ();\n\n\t\t\tbreak;\n\n\t\tcase WM_PAINT:\n\t\t\thdc = BeginPaint(hWnd, &ps);\n\n//\t\t\tif (!in_mode_set && host_initialized)\n//\t\t\t\tSCR_UpdateWholeScreen ();\n\n\t\t\tEndPaint(hWnd, &ps);\n\t\t\tbreak;\n\n\t\tcase WM_KEYDOWN:\n\t\tcase WM_SYSKEYDOWN:\n//\t\t\tif (!vid_initializing)\n\t\t\t\tINS_TranslateKeyEvent(wParam, lParam, true, 0, false);\n\t\t\tbreak;\n\n\t\tcase WM_KEYUP:\n\t\tcase WM_SYSKEYUP:\n//\t\t\tif (!vid_initializing)\n\t\t\t\tINS_TranslateKeyEvent(wParam, lParam, false, 0, false);\n\t\t\tbreak;\n\n\t\tcase WM_APPCOMMAND:\n\t\t\tlRet = INS_AppCommand(lParam);\n\t\t\tbreak;\n\n\t// this is complicated because Win32 seems to pack multiple mouse events into\n\t// one update sometimes, so we always check all states and look for events\n\t\tcase WM_LBUTTONDOWN:\n\t\tcase WM_LBUTTONUP:\n\t\tcase WM_RBUTTONDOWN:\n\t\tcase WM_RBUTTONUP:\n\t\tcase WM_MBUTTONDOWN:\n\t\tcase WM_MBUTTONUP:\n\t\tcase WM_MOUSEMOVE:\n\t\tcase WM_XBUTTONDOWN:\n\t\tcase WM_XBUTTONUP:\n//\t\t\tif (!vid_initializing)\n\t\t\t{\n\t\t\t\ttemp = 0;\n\n\t\t\t\tif (wParam & MK_LBUTTON)\n\t\t\t\t\ttemp |= 1;\n\n\t\t\t\tif (wParam & MK_RBUTTON)\n\t\t\t\t\ttemp |= 2;\n\n\t\t\t\tif (wParam & MK_MBUTTON)\n\t\t\t\t\ttemp |= 4;\n\n\t\t\t\t// extra buttons\n\t\t\t\tif (wParam & MK_XBUTTON1)\n\t\t\t\t\ttemp |= 8;\n\n\t\t\t\tif (wParam & MK_XBUTTON2)\n\t\t\t\t\ttemp |= 16;\n\n\t\t\t\tif (wParam & MK_XBUTTON3)\n\t\t\t\t\ttemp |= 32;\n\n\t\t\t\tif (wParam & MK_XBUTTON4)\n\t\t\t\t\ttemp |= 64;\n\n\t\t\t\tif (wParam & MK_XBUTTON5)\n\t\t\t\t\ttemp |= 128;\n\n\t\t\t\tif (wParam & MK_XBUTTON6)\n\t\t\t\t\ttemp |= 256;\n\n\t\t\t\tif (wParam & MK_XBUTTON7)\n\t\t\t\t\ttemp |= 512;\n\n\t\t\t\tINS_MouseEvent (temp);\n\t\t\t}\n\t\t\tbreak;\n\t\t// JACK: This is the mouse wheel with the Intellimouse\n\t\t// Its delta is either positive or neg, and we generate the proper\n\t\t// Event.\n\t\tcase WM_MOUSEWHEEL: \n\t\t\t{\n\t\t\t\tif ((short) HIWORD(wParam) > 0)\n\t\t\t\t{\n\t\t\t\t\tKey_Event(0, K_MWHEELUP, 0, true);\n\t\t\t\t\tKey_Event(0, K_MWHEELUP, 0, false);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tKey_Event(0, K_MWHEELDOWN, 0, true);\n\t\t\t\t\tKey_Event(0, K_MWHEELDOWN, 0, false);\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\t\tcase WM_INPUT:\n\t\t\t// raw input handling\n\t\t\tINS_RawInput_Read((HANDLE)lParam);\n\t\t\tbreak;\n\t\tcase WM_DEVICECHANGE:\n\t\t\tCOM_AddWork(WG_MAIN, INS_DeviceChanged, NULL, NULL, uMsg, 0);\n\t\t\tlRet = TRUE;\n\t\t\tbreak;\n/*\t\tcase WM_DISPLAYCHANGE:\n\t\t\tif (!in_mode_set && (modestate == MS_WINDOWED) && !vid_fulldib_on_focus_mode)\n\t\t\t{\n\t\t\t\tforce_mode_set = true;\n\t\t\t\tVID_SetMode (vid_modenum, vid_curpal);\n\t\t\t\tforce_mode_set = false;\n\t\t\t}\n\t\t\tbreak;\n*/\n\t\tcase WM_CLOSE:\n\t\t// this causes Close in the right-click task bar menu not to work, but right\n\t\t// now bad things happen if Close is handled in that case (garbage and a\n\t\t// crash on Win95)\n//\t\t\tif (!vid_initializing)\n\t\t\t{\n\t\t\t\tif (MessageBox (mainwindow, \"Are you sure you want to quit?\", \"Confirm Exit\",\n\t\t\t\t\t\t\tMB_YESNO | MB_SETFOREGROUND | MB_ICONQUESTION) == IDYES)\n\t\t\t\t{\n\t\t\t\t\tCbuf_AddText(\"\\nquit\\n\", RESTRICT_LOCAL);\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\n#ifdef HAVE_CDPLAYER\n\t\tcase MM_MCINOTIFY:\n\t\t\tlRet = CDAudio_MessageHandler (hWnd, uMsg, wParam, lParam);\n\t\t\tbreak;\n#endif\n\n\t\tdefault:\n\t\t\t/* pass all unhandled messages to DefWindowProc */\n\t\t\tlRet = DefWindowProc (hWnd, uMsg, wParam, lParam);\n\t\t\tbreak;\n\t}\n\n\t/* return 0 if handled message, 1 if not */\n\treturn lRet;\n}\n\n\n\n/*\n** VID_CreateWindow\n*/\n#define\tWINDOW_CLASS_NAME FULLENGINENAME\n#define\tWINDOW_TITLE_NAME FULLENGINENAME\n\n\nvoid VID_CreateWindow(int width, int height, qboolean fullscreen)\n{\n\tWNDCLASS\t\twc;\n\tRECT\t\t\tr;\n\tint\t\t\t\tx, y, w, h;\n\tint\t\t\t\texstyle;\n\tint stylebits;\n\n\n\tif (fullscreen)\n\t{\n\t\texstyle = WS_EX_TOPMOST;\n\t\tstylebits = WS_POPUP;\n\t}\n\telse\n\t{\n\t\texstyle = 0;\n\t\tstylebits = WS_OVERLAPPEDWINDOW;\n\t}\n\n\t/* Register the frame class */\n\twc.style         = 0;\n\twc.lpfnWndProc   = (WNDPROC)MainWndProc;\n\twc.cbClsExtra    = 0;\n\twc.cbWndExtra    = 0;\n\twc.hInstance     = global_hInstance;\n\twc.hIcon         = 0;\n\twc.hCursor       = LoadCursor (NULL,IDC_ARROW);\n\twc.hbrBackground = (void *)COLOR_GRAYTEXT;\n\twc.lpszMenuName  = 0;\n\twc.lpszClassName = WINDOW_CLASS_NAME;\n\n\tRegisterClass(&wc);\n\n\tr.left = 0;\n\tr.top = 0;\n\tr.right  = width;\n\tr.bottom = height;\n\n\tAdjustWindowRectEx (&r, stylebits, FALSE, exstyle);\n\n\twindow_rect = r;\n\n\tw = r.right - r.left;\n\th = r.bottom - r.top;\n\tx = 0;//vid_xpos.value;\n\ty = 0;//vid_ypos.value;\n\n\tmainwindow = CreateWindowEx(\n\t\texstyle,\n\t\t WINDOW_CLASS_NAME,\n\t\t WINDOW_TITLE_NAME,\n\t\t stylebits,\n\t\t x, y, w, h,\n\t\t NULL,\n\t\t NULL,\n\t\t global_hInstance,\n\t\t NULL);\n\n\tif (!mainwindow)\n\t\tSys_Error(\"Couldn't create window\");\n\t\n\tShowWindow(mainwindow, SW_SHOWNORMAL);\n\tUpdateWindow(mainwindow);\n\tSetForegroundWindow(mainwindow);\n\tSetFocus(mainwindow);\n\n\tSWV_UpdateWindowStatus();\n}\n\n\n\n\n\n\n\n\nvoid SW_VID_UpdateViewport(wqcom_t *com)\n{\n\tcom->viewport.cbuf = screenbuffer;\n\tcom->viewport.dbuf = depthbuffer;\n\tcom->viewport.width = vid.pixelwidth;\n\tcom->viewport.height = vid.pixelheight;\n\tcom->viewport.stride = screenpitch;\n\tcom->viewport.framenum = framenumber;\n}\n\nvoid SW_VID_SwapBuffers(void)\n{\n\textern cvar_t vid_conautoscale;\n\tDIB_SwapBuffers();\n\tframenumber++;\n\n\tINS_UpdateGrabs(false, vid.activeapp);\n\n//\tmemset( pDIBBase, 0, vid.pixelwidth * vid.pixelheight * 4);\n\n\n\tif (window_width != vid.pixelwidth || window_height != vid.pixelheight)\n\t{\n\t\tDIB_Shutdown();\n\t\tvid.pixelwidth = window_width;\n\t\tvid.pixelheight = window_height;\n\t\tif (!DIB_Init())\n\t\t\tSys_Error(\"resize reinitialization error\");\n\n\t\tCvar_ForceCallback(&vid_conautoscale);\n\t}\n}\n\nqboolean SW_VID_Init(rendererstate_t *info, unsigned char *palette)\n{\n\tvid.pixelwidth = info->width;\n\tvid.pixelheight = info->height;\n\n\tif (info->fullscreen)\t//don't do this with d3d - d3d should set it's own video mode.\n\t{\t//make windows change res.\n\t\tDEVMODE gdevmode;\n\t\tgdevmode.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT;\n\t\tif (info->bpp)\n\t\t\tgdevmode.dmFields |= DM_BITSPERPEL;\n\t\tif (info->rate)\n\t\t\tgdevmode.dmFields |= DM_DISPLAYFREQUENCY;\n\t\tgdevmode.dmBitsPerPel = info->bpp;\n\t\tif (info->bpp && (gdevmode.dmBitsPerPel < 15))\n\t\t{\n\t\t\tCon_Printf(\"Forcing at least 16bpp\\n\");\n\t\t\tgdevmode.dmBitsPerPel = 16;\n\t\t}\n\t\tgdevmode.dmDisplayFrequency = info->rate;\n\t\tgdevmode.dmPelsWidth = info->width;\n\t\tgdevmode.dmPelsHeight = info->height;\n\t\tgdevmode.dmSize = sizeof (gdevmode);\n\n\t\tif (ChangeDisplaySettings (&gdevmode, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL)\n\t\t{\n\t\t\tCon_SafePrintf((gdevmode.dmFields&DM_DISPLAYFREQUENCY)?\"Windows rejected mode %i*%i*%i*%i\\n\":\"Windows rejected mode %i*%i*%i\\n\", (int)gdevmode.dmPelsWidth, (int)gdevmode.dmPelsHeight, (int)gdevmode.dmBitsPerPel, (int)gdevmode.dmDisplayFrequency);\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tVID_CreateWindow(vid.pixelwidth, vid.pixelheight, info->fullscreen);\n\n\tif (!DIB_Init())\n\t\treturn false;\n\n\treturn true;\n}\nvoid SW_VID_DeInit(void)\n{\n\tImage_Shutdown();\n\n\tDIB_Shutdown();\n\tDestroyWindow(mainwindow);\n\n\tChangeDisplaySettings (NULL, 0);\n}\nqboolean SW_VID_ApplyGammaRamps\t\t(unsigned int gammarampsize, unsigned short *ramps)\n{\n\treturn false;\n}\nchar *SW_VID_GetRGBInfo(int *bytestride, int *truevidwidth, int *truevidheight, enum uploadfmt *fmt)\n{\n\tchar *buf = NULL;\n\tchar *src, *dst;\n\tint w, h;\n\tbuf = BZ_Malloc((vid.pixelwidth * vid.pixelheight * 3));\n\tdst = buf;\n\tfor (h = 0; h < vid.pixelheight; h++)\n\t{\n\t\tfor (w = 0, src = (char*)screenbuffer + (h * vid.pixelwidth*4); w < vid.pixelwidth; w++, dst += 3, src += 4)\n\t\t{\n\t\t\tdst[0] = src[2];\n\t\t\tdst[1] = src[1];\n\t\t\tdst[2] = src[0];\n\t\t}\n\t}\n\t*bytestride = vid.pixelwidth*3;\n\t*truevidwidth = vid.pixelwidth;\n\t*truevidheight = vid.pixelheight;\n\t*fmt = TF_BGR24;\n\treturn buf;\n}\nvoid SW_VID_SetWindowCaption(const char *msg)\n{\n}\n#endif\n"
  },
  {
    "path": "engine/vk/vk_backend.c",
    "content": "#include \"quakedef.h\"\n#ifdef VKQUAKE\n#include \"vkrenderer.h\"\n#include \"glquake.h\"\n#include \"gl_draw.h\"\n#include \"shader.h\"\n#include \"renderque.h\"\n\n//FIXME: instead of switching rendertargets and back, we should be using an alternative queue.\n\n#define PERMUTATION_BEM_VR (1u<<11)\n#define PERMUTATION_BEM_FP16 (1u<<12)\n#define PERMUTATION_BEM_MULTISAMPLE (1u<<13)\n#define PERMUTATION_BEM_DEPTHONLY (1u<<14)\n#define PERMUTATION_BEM_WIREFRAME (1u<<15)\n\n#undef BE_Init\n#undef BE_SelectMode\n#undef BE_GenBrushModelVBO\n#undef BE_ClearVBO\n#undef BE_UploadAllLightmaps\n#undef BE_LightCullModel\n#undef BE_SelectEntity\n#undef BE_SelectDLight\n#undef BE_GetTempBatch\n#undef BE_SubmitBatch\n#undef BE_DrawMesh_List\n#undef BE_DrawMesh_Single\n#undef BE_SubmitMeshes\n#undef BE_DrawWorld\n#undef BE_VBO_Begin\n#undef BE_VBO_Data\n#undef BE_VBO_Finish\n#undef BE_VBO_Destroy\n#undef BE_Scissor\n\n#undef BE_RenderToTextureUpdate2d\n\nextern cvar_t r_shadow_realtime_world_lightmaps;\nextern cvar_t gl_overbright;\nextern cvar_t r_portalrecursion;\n\nextern cvar_t r_polygonoffset_shadowmap_offset, r_polygonoffset_shadowmap_factor;\nextern cvar_t r_wireframe;\nextern cvar_t vk_stagingbuffers;\n\nstatic unsigned int vk_usedynamicstaging;\n\n#ifdef RTLIGHTS\nstatic void VK_TerminateShadowMap(void);\n#endif\nvoid VKBE_BeginShadowmapFace(void);\n\nstatic void R_DrawPortal(batch_t *batch, batch_t **blist, batch_t *depthmasklist[2], int portaltype);\n\n#define MAX_TMUS 32\n\nextern texid_t r_whiteimage, missing_texture_gloss, missing_texture_normal;\nextern texid_t r_blackimage, r_blackcubeimage, r_whitecubeimage;\n\nstatic void BE_RotateForEntity (const entity_t *fte_restrict e, const model_t *fte_restrict mod);\nstatic void VKBE_SetupLightCBuffer(dlight_t *l, vec3_t colour, vec3_t axis[3]);\n\n#ifdef VK_EXT_debug_utils\nstatic void DebugSetName(VkObjectType objtype, uint64_t handle, const char *name)\n{\n\tif (vkSetDebugUtilsObjectNameEXT)\n\t{\n\t\tVkDebugUtilsObjectNameInfoEXT info =\n\t\t{\n\t\t\tVK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT,\n\t\t\tNULL,\n\t\t\tobjtype,\n\t\t\thandle,\n\t\t\tname?name:\"UNNAMED\"\n\t\t};\n\t\tvkSetDebugUtilsObjectNameEXT(vk.device, &info);\n\t}\n}\n#else\n#define DebugSetName(t,h,n)\n#endif\n\n/*========================================== tables for deforms =====================================*/\n#define frand() (rand()*(1.0/RAND_MAX))\n#define FTABLE_SIZE\t\t1024\n#define FTABLE_CLAMP(x)\t(((int)((x)*FTABLE_SIZE) & (FTABLE_SIZE-1)))\n#define FTABLE_EVALUATE(table,x) (table ? table[FTABLE_CLAMP(x)] : frand()*((x)-floor(x)))\n#define R_FastSin(x) r_sintable[FTABLE_CLAMP(x)]\n\nstatic\tfloat\tr_sintable[FTABLE_SIZE];\nstatic\tfloat\tr_triangletable[FTABLE_SIZE];\nstatic\tfloat\tr_squaretable[FTABLE_SIZE];\nstatic\tfloat\tr_sawtoothtable[FTABLE_SIZE];\nstatic\tfloat\tr_inversesawtoothtable[FTABLE_SIZE];\n\nstatic float *FTableForFunc ( unsigned int func )\n{\n\tswitch (func)\n\t{\n\t\tcase SHADER_FUNC_SIN:\n\t\t\treturn r_sintable;\n\n\t\tcase SHADER_FUNC_TRIANGLE:\n\t\t\treturn r_triangletable;\n\n\t\tcase SHADER_FUNC_SQUARE:\n\t\t\treturn r_squaretable;\n\n\t\tcase SHADER_FUNC_SAWTOOTH:\n\t\t\treturn r_sawtoothtable;\n\n\t\tcase SHADER_FUNC_INVERSESAWTOOTH:\n\t\t\treturn r_inversesawtoothtable;\n\t}\n\n\t//bad values allow us to crash (so I can debug em)\n\treturn NULL;\n}\n\nstatic void FTable_Init(void)\n{\n\tunsigned int i;\n\tdouble t;\n\tfor (i = 0; i < FTABLE_SIZE; i++)\n\t{\n\t\tt = (double)i / (double)FTABLE_SIZE;\n\n\t\tr_sintable[i] = sin(t * 2*M_PI);\n\n\t\tif (t < 0.25)\n\t\t\tr_triangletable[i] = t * 4.0;\n\t\telse if (t < 0.75)\n\t\t\tr_triangletable[i] = 2 - 4.0 * t;\n\t\telse\n\t\t\tr_triangletable[i] = (t - 0.75) * 4.0 - 1.0;\n\n\t\tif (t < 0.5)\n\t\t\tr_squaretable[i] = 1.0f;\n\t\telse\n\t\t\tr_squaretable[i] = -1.0f;\n\n\t\tr_sawtoothtable[i] = t;\n\t\tr_inversesawtoothtable[i] = 1.0 - t;\n\t}\n}\n\ntypedef vec3_t mat3_t[3];\nstatic mat3_t axisDefault={{1, 0, 0},\n\t\t\t\t\t{0, 1, 0},\n\t\t\t\t\t{0, 0, 1}};\n\nstatic void Matrix3_Transpose (mat3_t in, mat3_t out)\n{\n\tout[0][0] = in[0][0];\n\tout[1][1] = in[1][1];\n\tout[2][2] = in[2][2];\n\n\tout[0][1] = in[1][0];\n\tout[0][2] = in[2][0];\n\tout[1][0] = in[0][1];\n\tout[1][2] = in[2][1];\n\tout[2][0] = in[0][2];\n\tout[2][1] = in[1][2];\n}\nstatic void Matrix3_Multiply_Vec3 (const mat3_t a, const vec3_t b, vec3_t product)\n{\n\tproduct[0] = a[0][0]*b[0] + a[0][1]*b[1] + a[0][2]*b[2];\n\tproduct[1] = a[1][0]*b[0] + a[1][1]*b[1] + a[1][2]*b[2];\n\tproduct[2] = a[2][0]*b[0] + a[2][1]*b[1] + a[2][2]*b[2];\n}\n\nstatic int Matrix3_Compare(const mat3_t in, const mat3_t out)\n{\n\treturn !memcmp(in, out, sizeof(mat3_t));\n}\n\n/*================================================*/\n\n//dlight-specific constant-buffer\ntypedef struct\n{\n\tfloat l_cubematrix[16];\n\tvec3_t l_lightposition; float padl1;\n\tvec3_t l_colour; float pad2;\n\tvec3_t l_lightcolourscale; float l_lightradius;\n\tvec4_t l_shadowmapproj;\n\tvec2_t l_shadowmapscale; vec2_t pad3;\n} vkcbuf_light_t;\n\n//entity-specific constant-buffer\ntypedef struct\n{\n\tfloat m_modelviewproj[16];\n\tfloat m_model[16];\n\tfloat m_modelinv[16];\n\tvec3_t e_eyepos;\n\tfloat e_time;\n\tvec3_t e_light_ambient; float pad1;\n\tvec3_t e_light_dir;\t\tfloat pad2;\n\tvec3_t e_light_mul;\t\tfloat pad3;\n\tvec4_t e_lmscale[4];\n\tvec3_t e_uppercolour;\tfloat pad4;\n\tvec3_t e_lowercolour;\tfloat pad5;\n\tvec3_t e_glowmod;\t\tfloat pad6;\n\tvec4_t e_colourident;\n\tvec4_t w_fogcolours;\n\tfloat w_fogdensity;\t\tfloat w_fogdepthbias;\tvec2_t pad7;\n} vkcbuf_entity_t;\n\nenum \n{\n\tVK_BUFF_POS,\n\tVK_BUFF_TC,\n\tVK_BUFF_COL,\n\tVK_BUFF_LMTC,\n\tVK_BUFF_NORM,\n\tVK_BUFF_SDIR,\n\tVK_BUFF_TDIR,\n\tVK_BUFF_MAX\n};\n\ntypedef struct\n{\t//there should be only one copy of this struct for each thread that renders anything in vulkan.\n\n\t//descriptor sets are: 0) entity+light 1) batch textures + pass textures\n\tVkDescriptorSet descriptorsets[1];\n\n\t//commandbuffer state, to avoid redundant state changes.\n\tVkPipeline activepipeline;\n\n\tfloat depthrange;\n\n} vkrendercontext_t;\n\ntypedef struct\n{\n\tunsigned int inited;\n\n\tbackendmode_t mode;\n\tunsigned int modepermutation;\n\tunsigned int flags;\n\tunsigned int forcebeflags;\n\n\tfloat\tidentitylighting;\n\tfloat\tidentitylightmap;\n\tfloat\t\tcurtime;\n\tconst entity_t\t*curentity;\n\tconst dlight_t\t*curdlight;\n\tshader_t\t*curshader;\n\tshader_t\t*depthonly;\n\ttexnums_t\t*curtexnums;\n\tvbo_t *batchvbo;\n\tbatch_t *curbatch;\n\tbatch_t dummybatch;\n\tvec4_t lightshadowmapproj;\n\tvec2_t lightshadowmapscale;\n\n\tunsigned int curlmode;\n\tshader_t\t*shader_rtlight[LSHADER_MODES];\n\n\tprogram_t\t\t\t*programfixedemu[2];\n\n\tmesh_t\t\t**meshlist;\n\tunsigned int nummeshes;\n\n\tunsigned int wbatch;\n\tunsigned int maxwbatches;\n\tbatch_t *wbatches;\n\n\tVkDescriptorBufferInfo ubo_entity;\n\tVkDescriptorBufferInfo ubo_light;\n\tvec4_t lightinfo;\t//org+radius\n\n\tVkBuffer staticbuf;\t//holds fallback vertex info so we don't crash from it\n\tvk_poolmem_t staticbufmem;\n\n\ttexid_t tex_currentrender;\n\n\tstruct vk_rendertarg rt_reflection;\n\tstruct vk_rendertarg rt_refraction;\n\ttexid_t tex_refraction;\t//separate from rt_reflection, because $reasons\n\ttexid_t tex_ripplemap;\n\n\tvkrendercontext_t rc;\n\n\tstruct shadowmaps_s\n\t{\n\t\tuint32_t width;\n\t\tuint32_t height;\n\t\tVkImage image;\t//array. multiple allows for things to happen out of order, which should help to avoid barrier stalls.\n\t\tVkDeviceMemory memory;\n\n\t\tuint32_t seq;\n\t\tstruct\n\t\t{\n\t\t\tVkFramebuffer framebuffer;\n\t\t\timage_t qimage;\t\t//this is silly, but whatever.\n\t\t\tvk_image_t vimage;\n\t\t} buf[8];\n\t} shadow[2]; //omni, spot\n\ttexid_t currentshadowmap;\n\n\tVkDescriptorSetLayout textureLayout;\n\n#ifdef VK_KHR_acceleration_structure\n\tqboolean needtlas;\t//frame delay, urgh...\n\tVkAccelerationStructureKHR tlas;\n#endif\n} vkbackend_t;\n\n#define VERTEXSTREAMSIZE (1024*1024*2)\t//2mb = 1 PAE jumbo page\n\n#define DYNVBUFFSIZE 65536\n#define DYNIBUFFSIZE 65536\n\nstatic vecV_t tmpbuf[65536];\t//max verts per mesh\n\nstatic vkbackend_t shaderstate;\n\nextern int be_maxpasses;\n\nstruct blobheader\n{\n\tunsigned char blobmagic[4];\n\tunsigned int blobversion;\n\tunsigned int defaulttextures;\t//s_diffuse etc flags\n\tunsigned int numtextures;\t\t//s_t0 count\n\tunsigned int permutations;\t\t//\n\n\tunsigned int cvarsoffset;\n\tunsigned int cvarslength;\n\n\tunsigned int vertoffset;\n\tunsigned int vertlength;\n\tunsigned int fragoffset;\n\tunsigned int fraglength;\n};\n\nstatic float VK_ShaderReadArgument(const char *arglist, const char *arg, char type, qbyte size, void *out)\n{\n\tqbyte i;\n\tconst char *var;\n\tint arglen = strlen(arg);\n\n\t//grab an argument instead, otherwise 0\n\tvar = arglist;\n\twhile((var = strchr(var, '#')))\n\t{\n\t\tif (!Q_strncasecmp(var+1, arg, arglen))\n\t\t{\n\t\t\tif (var[1+arglen] == '=')\n\t\t\t{\n\t\t\t\tvar = var+arglen+2;\n\t\t\t\tfor (i = 0; i < size; i++)\n\t\t\t\t{\n\t\t\t\t\twhile (*var == ' ' || *var == '\\t' || *var == ',')\n\t\t\t\t\t\tvar++;\n\n\t\t\t\t\tif (type == 'F')\n\t\t\t\t\t\t((float*)out)[i] = BigFloat(strtod(var, (char**)&var));\n\t\t\t\t\telse\n\t\t\t\t\t\t((int*)out)[i] = BigLong(strtol(var, (char**)&var, 0));\n\t\t\t\t\tif (!var)\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\treturn 1;\n\t\t\t}\n\t\t\tif (var[1+arglen] == '#' || !var[1+arglen])\n\t\t\t{\n\t\t\t\tfor (i = 0; i < size; i++)\n\t\t\t\t{\n\t\t\t\t\tif (type == 'F')\n\t\t\t\t\t\t((float*)out)[i] = BigFloat(1);\n\t\t\t\t\telse\n\t\t\t\t\t\t((int*)out)[i] = BigLong(1);\n\t\t\t\t}\n\t\t\t\treturn 1;\t//present, but no value\n\t\t\t}\n\t\t}\n\t\tvar++;\n\t}\n\treturn 0;\t//not present.\n}\n\n#if 0\n//this should use shader pass flags, but those are specific to the shader, not the program, which makes this awkward.\nstatic VkSampler VK_GetSampler(unsigned int flags)\n{\n\tstatic VkSampler ret;\n\tqboolean clamptoedge = flags & IF_CLAMP;\n\tVkSamplerCreateInfo lmsampinfo = {VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO};\n\tif (ret)\n\t\treturn ret;\n\n\tif (flags & IF_LINEAR)\n\t{\n\t\tlmsampinfo.minFilter = lmsampinfo.magFilter = VK_FILTER_LINEAR;\n\t\tlmsampinfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;\n\t}\n\telse if (flags & IF_NEAREST)\n\t{\n\t\tlmsampinfo.minFilter = lmsampinfo.magFilter = VK_FILTER_NEAREST;\n\t\tlmsampinfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST;\n\t}\n\telse\n\t{\n\t\tint *filter = (flags & IF_UIPIC)?vk.filterpic:vk.filtermip;\n\t\tif (filter[0])\n\t\t\tlmsampinfo.minFilter = VK_FILTER_LINEAR;\n\t\telse\n\t\t\tlmsampinfo.minFilter = VK_FILTER_NEAREST;\n\t\tif (filter[1])\n\t\t\tlmsampinfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;\n\t\telse\n\t\t\tlmsampinfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST;\n\t\tif (filter[2])\n\t\t\tlmsampinfo.magFilter = VK_FILTER_LINEAR;\n\t\telse\n\t\t\tlmsampinfo.magFilter = VK_FILTER_NEAREST;\n\t}\n\n\tlmsampinfo.addressModeU = clamptoedge?VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE:VK_SAMPLER_ADDRESS_MODE_REPEAT;\n\tlmsampinfo.addressModeV = clamptoedge?VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE:VK_SAMPLER_ADDRESS_MODE_REPEAT;\n\tlmsampinfo.addressModeW = clamptoedge?VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE:VK_SAMPLER_ADDRESS_MODE_REPEAT;\n\tlmsampinfo.mipLodBias = 0.0;\n\tlmsampinfo.anisotropyEnable = (flags & IF_NEAREST)?false:(vk.max_anistophy > 1);\n\tlmsampinfo.maxAnisotropy = vk.max_anistophy;\n\tlmsampinfo.compareEnable = VK_FALSE;\n\tlmsampinfo.compareOp = VK_COMPARE_OP_NEVER;\n\tlmsampinfo.minLod = vk.mipcap[0];\t//this isn't quite right\n\tlmsampinfo.maxLod = vk.mipcap[1];\n\tlmsampinfo.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK;\n\tlmsampinfo.unnormalizedCoordinates = VK_FALSE;\n\tVkAssert(vkCreateSampler(vk.device, &lmsampinfo, NULL, &ret));\n\n\treturn ret;\n}\n#endif\n\n//creates the layout stuff for the prog.\nstatic void VK_FinishProg(program_t *prog, const char *name)\n{\n\t{\n\t\tVkDescriptorSetLayout desclayout;\n\t\tVkDescriptorSetLayoutCreateInfo descSetLayoutCreateInfo = {VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO};\n\t\tVkDescriptorSetLayoutBinding dbs[3+MAX_TMUS], *db = dbs;\n\t\tuint32_t i;\n\t\t//VkSampler samp = VK_GetSampler(0);\n\n\t\tdb->binding = db-dbs;\n\t\tdb->descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;\n\t\tdb->descriptorCount = 1;\n\t\tdb->stageFlags = VK_SHADER_STAGE_VERTEX_BIT|VK_SHADER_STAGE_FRAGMENT_BIT;\n\t\tdb->pImmutableSamplers = NULL;\n\t\tdb++;\n\n\t\tdb->binding = db-dbs;\n\t\tdb->descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;\n\t\tdb->descriptorCount = 1;\n\t\tdb->stageFlags = VK_SHADER_STAGE_VERTEX_BIT|VK_SHADER_STAGE_FRAGMENT_BIT;\n\t\tdb->pImmutableSamplers = NULL;\n\t\tdb++;\n\n#ifdef VK_KHR_acceleration_structure\n\t\tif (prog->rayquery)\n\t\t{\n\t\t\tdb->binding = db-dbs;\n\t\t\tdb->descriptorType = VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR;\n\t\t\tdb->descriptorCount = 1;\n\t\t\tdb->stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;\n\t\t\tdb->pImmutableSamplers = NULL;\n\t\t\tdb++;\n\t\t}\n#endif\n\n\t\tfor (i = 0; i < 32; i++)\n\t\t{\n\t\t\tif (!(prog->defaulttextures & (1u<<i)))\n\t\t\t\tcontinue;\n\t\t\tdb->binding = db-dbs;\n\t\t\tdb->descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;\n\t\t\tdb->descriptorCount = 1;\n\t\t\tdb->stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;\n\t\t\tdb->pImmutableSamplers = NULL;//&samp;\n\t\t\tdb++;\n\t\t}\n\n\t\tfor (i = 0; i < prog->numsamplers; i++)\n\t\t{\n\t\t\tdb->binding = db-dbs;\n\t\t\tdb->descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;\n\t\t\tdb->descriptorCount = 1;\n\t\t\tdb->stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;\n\t\t\tdb->pImmutableSamplers = NULL;//&samp;\n\t\t\tdb++;\n\t\t}\n\n\t\tdescSetLayoutCreateInfo.bindingCount = db-dbs;\n\t\tdescSetLayoutCreateInfo.pBindings = dbs;\n\t\tif (vk.khr_push_descriptor)\n\t\t\tdescSetLayoutCreateInfo.flags |= VK_DESCRIPTOR_SET_LAYOUT_CREATE_PUSH_DESCRIPTOR_BIT_KHR;\n\t\tVkAssert(vkCreateDescriptorSetLayout(vk.device, &descSetLayoutCreateInfo, NULL, &desclayout));\n\t\tprog->desclayout = desclayout;\n\t}\n\n\t{\n\t\tVkDescriptorSetLayout sets[1] = {prog->desclayout};\n\t\tVkPipelineLayout layout;\n\t\tVkPushConstantRange push[1];\n\t\tVkPipelineLayoutCreateInfo pipeLayoutCreateInfo = {VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO};\n\t\tpush[0].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;\n\t\tpush[0].offset = 0;\n\t\tpush[0].size = sizeof(vec4_t);\n\n\t\tpipeLayoutCreateInfo.flags = 0;\n\t\tpipeLayoutCreateInfo.setLayoutCount = countof(sets);\n\t\tpipeLayoutCreateInfo.pSetLayouts = sets;\n\t\tpipeLayoutCreateInfo.pushConstantRangeCount = !strcmp(name, \"fixedemu_flat\");\n\t\tpipeLayoutCreateInfo.pPushConstantRanges = push;\n\t\tVkAssert(vkCreatePipelineLayout(vk.device, &pipeLayoutCreateInfo, vkallocationcb, &layout));\n\t\tprog->layout = layout;\n\t}\n}\n\nqboolean VK_LoadBlob(program_t *prog, void *blobdata, const char *name)\n{\n\t//fixme: should validate that the offset+lengths are within the blobdata.\n\tstruct blobheader *blob = blobdata;\n\tVkShaderModuleCreateInfo info = {VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO};\n\tVkShaderModule vert, frag;\n\tunsigned char *cvardata;\n\n\tif (blob->blobmagic[0] != 0xff || blob->blobmagic[1] != 'S' || blob->blobmagic[2] != 'P' || blob->blobmagic[3] != 'V')\n\t{\n\t\tCon_Printf(CON_ERROR\"Blob %s is outdated\\n\", name);\n\t\treturn false;\t//bad magic.\n\t}\n\tif (blob->blobversion != 1)\n\t{\n\t\tCon_Printf(CON_ERROR\"Blob %s is outdated\\n\", name);\n\t\treturn false;\n\t}\n\n\tprog->supportedpermutations = blob->permutations;\n#define VKPERMUTATION_RAYQUERY\t\t\t(1u<<31)\n\tif (blob->permutations&~((PERMUTATIONS-1)|VKPERMUTATION_RAYQUERY))\n\t{\n\t\tCon_Printf(\"Blob %s has unknown permutations\\n\", name);\n\t\treturn false;\n\t}\n\tif (prog->supportedpermutations&VKPERMUTATION_RAYQUERY)\n\t{\t//not really a permutation.\n#ifndef VK_KHR_ray_query\n\t\tCon_Printf(CON_ERROR\"Blob %s requires vk_khr_ray_query\\n\", name);\n\t\treturn false;\n#else\n\t\tif (!vk.khr_ray_query)\n\t\t{\t//the actual spv extension. let compiling catch it?\n\t\t\tCon_Printf(CON_ERROR\"Blob %s requires vk_khr_ray_query\\n\", name);\n\t\t\treturn false;\n\t\t}\n\t\tif (!vk.khr_acceleration_structure)\n\t\t{\t//what we're meant to be using to feed it... *sigh*\n\t\t\tCon_Printf(CON_ERROR\"Blob %s requires vk_khr_acceleration_structure\\n\", name);\n\t\t\treturn false;\n\t\t}\n\t\tprog->supportedpermutations&=~VKPERMUTATION_RAYQUERY;\n\t\tprog->rayquery = true;\n#endif\n\t}\n\telse\n\t\tprog->rayquery = false;\n\n\tinfo.flags = 0;\n\tinfo.codeSize = blob->vertlength;\n\tinfo.pCode = (uint32_t*)((char*)blob+blob->vertoffset);\n\tVkAssert(vkCreateShaderModule(vk.device, &info, vkallocationcb, &vert));\n\tDebugSetName(VK_OBJECT_TYPE_SHADER_MODULE, (uint64_t)vert, name);\n\n\tinfo.flags = 0;\n\tinfo.codeSize = blob->fraglength;\n\tinfo.pCode = (uint32_t*)((char*)blob+blob->fragoffset);\n\tVkAssert(vkCreateShaderModule(vk.device, &info, vkallocationcb, &frag));\n\tDebugSetName(VK_OBJECT_TYPE_SHADER_MODULE, (uint64_t)frag, name);\n\n\tprog->vert = vert;\n\tprog->frag = frag;\n\tprog->numsamplers = blob->numtextures;\n\tprog->defaulttextures = blob->defaulttextures;\n\n\tif (blob->cvarslength)\n\t{\n\t\tprog->cvardata = BZ_Malloc(blob->cvarslength);\n\t\tprog->cvardatasize = blob->cvarslength;\n\t\tmemcpy(prog->cvardata, (char*)blob+blob->cvarsoffset, blob->cvarslength);\n\t}\n\telse\n\t{\n\t\tprog->cvardata = NULL;\n\t\tprog->cvardatasize = 0;\n\t}\n\n\t//go through the cvars and a) validate them. b) create them with the right defaults.\n\t//FIXME: validate\n\tfor (cvardata = prog->cvardata; cvardata < prog->cvardata + prog->cvardatasize; )\n\t{\n\t\tunsigned char type = cvardata[2], size = cvardata[3]-'0';\n\t\tconst char *cvarname;\n\t\tcvar_t *var;\n\n\t\tcvardata += 4;\n\t\tcvarname = cvardata;\n\t\tcvardata += strlen(cvarname)+1;\n\n\t\tif (type >= 'A' && type <= 'Z')\n\t\t{\t//args will be handled by the blob loader.\n\t\t\t//the blob contains default values, overwrite them with the user's preferences...\n\t\t\tVK_ShaderReadArgument(name, cvarname, type, size, cvardata);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tvar = Cvar_FindVar(cvarname);\n\t\t\tif (var)\n\t\t\t\tvar->flags |= CVAR_SHADERSYSTEM;\t//just in case\n\t\t\telse\n\t\t\t{\n\t\t\t\tunion\n\t\t\t\t{\n\t\t\t\t\tint i;\n\t\t\t\t\tfloat f;\n\t\t\t\t} u;\n\t\t\t\tchar value[128];\n\t\t\t\tuint32_t i;\n\t\t\t\t*value = 0;\n\t\t\t\tfor (i = 0; i < size; i++)\n\t\t\t\t{\n\t\t\t\t\tu.i = (cvardata[i*4+0]<<24)|(cvardata[i*4+1]<<16)|(cvardata[i*4+2]<<8)|(cvardata[i*4+3]<<0);\n\t\t\t\t\tif (i)\n\t\t\t\t\t\tQ_strncatz(value, \" \", sizeof(value));\n\t\t\t\t\tif (type == 'i' || type == 'b')\n\t\t\t\t\t\tQ_strncatz(value, va(\"%i\", u.i), sizeof(value));\n\t\t\t\t\telse\n\t\t\t\t\t\tQ_strncatz(value, va(\"%f\", u.f), sizeof(value));\n\t\t\t\t}\n\t\t\t\tCvar_Get(cvarname, value, CVAR_SHADERSYSTEM, \"GLSL Settings\");\n\t\t\t}\n\t\t}\n\t\tcvardata += 4*size;\n\t}\n\n\tVK_FinishProg(prog, name);\n\n\tprog->pipelines = NULL;\t//generated as needed, depending on blend states etc.\n\treturn true;\n}\nstatic void VKBE_ReallyDeleteProg(void *vprog)\n{\t//nothing else is refering to this data any more, its safe to obliterate it.\n\tprogram_t *prog = vprog;\n\tstruct pipeline_s *pipe;\n\twhile(prog->pipelines)\n\t{\n\t\tpipe = prog->pipelines;\n\t\tprog->pipelines = pipe->next;\n\n\t\tif (pipe->pipeline)\n\t\t\tvkDestroyPipeline(vk.device, pipe->pipeline, vkallocationcb);\n\t\tZ_Free(pipe);\n\t}\n\tif (prog->layout)\n\t\tvkDestroyPipelineLayout(vk.device, prog->layout, vkallocationcb);\n\tif (prog->desclayout)\n\t\tvkDestroyDescriptorSetLayout(vk.device, prog->desclayout, vkallocationcb);\n\tif (prog->vert)\n\t\tvkDestroyShaderModule(vk.device, prog->vert, vkallocationcb);\n\tif (prog->frag)\n\t\tvkDestroyShaderModule(vk.device, prog->frag, vkallocationcb);\n}\n\nvoid VKBE_DeleteProg(program_t *prog)\n{\n\t//schedule the deletes when its safe to do so.\n\tVK_AtFrameEnd(VKBE_ReallyDeleteProg, prog, sizeof(*prog));\n\n\t//clear stuff out so that the caller doesn't get confused.\n\tZ_Free(prog->cvardata);\n\tprog->cvardata = NULL;\n\tprog->pipelines = NULL;\n\tprog->layout = VK_NULL_HANDLE;\n\tprog->desclayout = VK_NULL_HANDLE;\n\tprog->vert = VK_NULL_HANDLE;\n\tprog->frag = VK_NULL_HANDLE;\n}\n\nstatic unsigned int VKBE_ApplyShaderBits(unsigned int bits)\n{\n\tif (shaderstate.flags & (BEF_FORCEADDITIVE|BEF_FORCETRANSPARENT|BEF_FORCENODEPTH|BEF_FORCEDEPTHTEST|BEF_FORCEDEPTHWRITE|BEF_LINES))\n\t{\n\t\tif (shaderstate.flags & BEF_FORCEADDITIVE)\n\t\t\tbits = (bits & ~(SBITS_MISC_DEPTHWRITE|SBITS_BLEND_BITS|SBITS_ATEST_BITS))\n\t\t\t\t\t\t| (SBITS_SRCBLEND_SRC_ALPHA | SBITS_DSTBLEND_ONE);\n\t\telse if (shaderstate.flags & BEF_FORCETRANSPARENT)\n\t\t{\n\t\t\tif ((bits & SBITS_BLEND_BITS) == (SBITS_SRCBLEND_ONE|SBITS_DSTBLEND_ZERO) || !(bits & SBITS_BLEND_BITS) || (bits&SBITS_ATEST_GE128)) \t/*if transparency is forced, clear alpha test bits*/\n\t\t\t\tbits = (bits & ~(SBITS_MISC_DEPTHWRITE|SBITS_BLEND_BITS|SBITS_ATEST_BITS))\n\t\t\t\t\t\t\t| (SBITS_SRCBLEND_SRC_ALPHA | SBITS_DSTBLEND_ONE_MINUS_SRC_ALPHA);\n\t\t}\n\n\t\tif (shaderstate.flags & BEF_FORCENODEPTH) \t/*EF_NODEPTHTEST dp extension*/\n\t\t\tbits |= SBITS_MISC_NODEPTHTEST;\n\t\telse\n\t\t{\n\t\t\tif (shaderstate.flags & BEF_FORCEDEPTHTEST)\n\t\t\t\tbits &= ~SBITS_MISC_NODEPTHTEST;\n\t\t\tif (shaderstate.flags & BEF_FORCEDEPTHWRITE)\n\t\t\t\tbits |= SBITS_MISC_DEPTHWRITE;\n\t\t}\n\n\t\tif (shaderstate.flags & BEF_LINES)\n\t\t\tbits |= SBITS_LINES;\n\t}\n\treturn bits;\n}\n\nstatic const char LIGHTPASS_SHADER[] = \"\\\n{\\n\\\n\tprogram rtlight\\n\\\n\t{\\n\\\n\t\tnodepth\\n\\\n\t\tblendfunc add\\n\\\n\t}\\n\\\n}\";\nstatic const char LIGHTPASS_SHADER_RQ[] = \"\\\n{\\n\\\n\tprogram rq_rtlight\\n\\\n\t{\\n\\\n\t\tnodepth\\n\\\n\t\tblendfunc add\\n\\\n\t}\\n\\\n}\";\n\nvoid VKBE_Init(void)\n{\n\tint i;\n\tchar *c;\n\n\tsh_config.pDeleteProg = VKBE_DeleteProg;\n\n\tbe_maxpasses = 1;\n\tmemset(&shaderstate, 0, sizeof(shaderstate));\n\tshaderstate.inited = true;\n\tfor (i = 0; i < MAXRLIGHTMAPS; i++)\n\t\tshaderstate.dummybatch.lightmap[i] = -1;\n\n\tshaderstate.identitylighting = 1;\n\tshaderstate.identitylightmap = 1;\n\n\t//make sure the world draws correctly\n\tr_worldentity.shaderRGBAf[0] = 1;\n\tr_worldentity.shaderRGBAf[1] = 1;\n\tr_worldentity.shaderRGBAf[2] = 1;\n\tr_worldentity.shaderRGBAf[3] = 1;\n\tr_worldentity.axis[0][0] = 1;\n\tr_worldentity.axis[1][1] = 1;\n\tr_worldentity.axis[2][2] = 1;\n\tr_worldentity.light_avg[0] = 1;\n\tr_worldentity.light_avg[1] = 1;\n\tr_worldentity.light_avg[2] = 1;\n\n\tFTable_Init();\n\n\tshaderstate.depthonly = R_RegisterShader(\"depthonly\", SUF_NONE, \n\t\t\t\t\"{\\n\"\n\t\t\t\t\t\"program depthonly\\n\"\n\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\"depthwrite\\n\"\n\t\t\t\t\t\t\"maskcolor\\n\"\n\t\t\t\t\t\"}\\n\"\n\t\t\t\t\"}\\n\");\n\n\n\tshaderstate.programfixedemu[0] = Shader_FindGeneric(\"fixedemu\", QR_VULKAN);\n\tshaderstate.programfixedemu[1] = Shader_FindGeneric(\"fixedemu_flat\", QR_VULKAN);\n\n\tR_InitFlashblends();\n\n/*\n\t{\n\t\tVkDescriptorPoolCreateInfo dpi = {VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO};\n\t\tVkDescriptorPoolSize dpisz[2];\n\t\tdpi.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT;\n\t\tdpi.maxSets = 512;\n\t\tdpi.poolSizeCount = countof(dpisz);\n\t\tdpi.pPoolSizes = dpisz;\n\n\t\tdpisz[0].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;\n\t\tdpisz[0].descriptorCount = 2;\n\n\t\tdpisz[1].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;\n\t\tdpisz[1].descriptorCount = MAX_TMUS;\n\n\t\tVkAssert(vkCreateDescriptorPool(vk.device, &dpi, NULL, &shaderstate.texturedescpool));\n\t}\n*/\n\t{\n\t\tstruct stagingbuf lazybuf;\n\t\tvoid *buffer = VKBE_CreateStagingBuffer(&lazybuf, sizeof(vec4_t)*65536+sizeof(vec3_t)*3*65536, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT);\n\t\tvec4_t *col = buffer;\n\t\tvec3_t *norm = (vec3_t*)(col+65536);\n\t\tvec3_t *sdir = norm+65536;\n\t\tvec3_t *tdir = sdir+65536;\n\t\tfor (i = 0; i < 65536; i++)\n\t\t{\n\t\t\tVector4Set(col[i], 1, 1, 1, 1);\n\t\t\tVectorSet(norm[i], 1, 0, 0);\n\t\t\tVectorSet(sdir[i], 0, 1, 0);\n\t\t\tVectorSet(tdir[i], 0, 0, 1);\n\t\t}\n\t\tshaderstate.staticbuf = VKBE_FinishStaging(&lazybuf, &shaderstate.staticbufmem);\n\t}\n\n\n\tc = vk_stagingbuffers.string;\n\tif (*c)\n\t{\n\t\tvk_usedynamicstaging = 0;\n\t\twhile (*c)\n\t\t{\n\t\t\tif (*c == 'u')\n\t\t\t\tvk_usedynamicstaging |= 1u<<DB_UBO;\n\t\t\telse if (*c == 'e' || *c == 'i')\n\t\t\t\tvk_usedynamicstaging |= 1u<<DB_EBO;\n\t\t\telse if (*c == 'v')\n\t\t\t\tvk_usedynamicstaging |= 1u<<DB_VBO;\n\t\t\telse if (*c == '0')\n\t\t\t\tvk_usedynamicstaging |= 0;\t//for explicly none.\n\t\t\telse\n\t\t\t\tCon_Printf(\"%s: unknown char %c\\n\", vk_stagingbuffers.string, *c);\n\t\t\tc++;\n\t\t}\n\t}\n\telse\n\t\tvk_usedynamicstaging = 0u;\n}\n\nstatic struct descpool *VKBE_CreateDescriptorPool(void)\n{\n\tstruct descpool *np = Z_Malloc(sizeof(*np));\n\t\n\tVkDescriptorPoolCreateInfo dpi = {VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO};\n\tVkDescriptorPoolSize dpisz[3];\n\tdpi.flags = 0;\n\tdpi.maxSets = np->totalsets = 512;\n\tdpi.poolSizeCount = 0;\n\tdpi.pPoolSizes = dpisz;\n\n\tdpisz[dpi.poolSizeCount].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;\n\tdpisz[dpi.poolSizeCount].descriptorCount = 2*dpi.maxSets;\n\tdpi.poolSizeCount++;\n\n\tdpisz[dpi.poolSizeCount].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;\n\tdpisz[dpi.poolSizeCount].descriptorCount = MAX_TMUS*dpi.maxSets;\n\tdpi.poolSizeCount++;\n\n#ifdef VK_KHR_acceleration_structure\n\tif (vk.khr_acceleration_structure)\n\t{\n\t\tdpisz[dpi.poolSizeCount].type = VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR;\n\t\tdpisz[dpi.poolSizeCount].descriptorCount = dpi.maxSets;\n\t\tdpi.poolSizeCount++;\n\t}\n#endif\n\n\tVkAssert(vkCreateDescriptorPool(vk.device, &dpi, NULL, &np->pool));\n\n\treturn np;\n}\nstatic VkDescriptorSet VKBE_TempDescriptorSet(VkDescriptorSetLayout layout)\n{\n\tVkDescriptorSet ret;\n\tVkDescriptorSetAllocateInfo setinfo = {VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO};\n\n\tif (vk.descpool->availsets == 0)\n\t{\n\t\tif (vk.descpool->next)\n\t\t\tvk.descpool = vk.descpool->next;\n\t\telse\n\t\t\tvk.descpool = vk.descpool->next = VKBE_CreateDescriptorPool();\n\t\tvkResetDescriptorPool(vk.device, vk.descpool->pool, 0);\n\t\tvk.descpool->availsets = vk.descpool->totalsets;\n\t}\n\tvk.descpool->availsets--;\n\n\tsetinfo.descriptorPool = vk.descpool->pool;\n\tsetinfo.descriptorSetCount = 1;\n\tsetinfo.pSetLayouts = &layout;\n\tvkAllocateDescriptorSets(vk.device, &setinfo, &ret);\n\n\treturn ret;\n}\n\nstatic const struct\n{\n\tconst char *name;\n\tVkBufferUsageFlags usage;\n\tqboolean nomap;\n\tVkDeviceSize align;\n} dynbuf_info[DB_MAX] =\n{\t//FIXME: set alignment properly.\n\t{\"DB_VBO\",\tVK_BUFFER_USAGE_VERTEX_BUFFER_BIT},\n\t{\"DB_EBO\",\tVK_BUFFER_USAGE_INDEX_BUFFER_BIT},\n\t{\"DB_UBO\",\tVK_BUFFER_USAGE_UNIFORM_BUFFER_BIT},\n\t{\"DB_STAGING\",\tVK_BUFFER_USAGE_TRANSFER_SRC_BIT},\n#ifdef VK_KHR_acceleration_structure\n\t{\"DB_ACCELERATIONSTRUCT\",\tVK_BUFFER_USAGE_ACCELERATION_STRUCTURE_STORAGE_BIT_KHR|VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, true, 256},\n\t{\"DB_ACCELERATIONSCRATCH\",\tVK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, true},\n\t{\"DB_ACCELERATIONMESHDATA\",\tVK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT},\n\t{\"DB_ACCELERATIONINSTANCE\",\tVK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT},\n#endif\n};\n//creates a new dynamic buffer for us to use while streaming. because spoons.\nstatic struct dynbuffer *VKBE_AllocNewStreamingBuffer(struct dynbuffer **link, enum dynbuf_e type, VkDeviceSize minsize)\n{\n\tVkBufferCreateInfo bufinf = {VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO};\n\tVkMemoryRequirements mem_reqs;\n\tVkMemoryAllocateInfo memAllocInfo = {VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO};\n\tVkMemoryAllocateFlagsInfo memAllocFlagsInfo = {VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO};\n\tstruct dynbuffer *n = Z_Malloc(sizeof(*n));\n\tqboolean usestaging = (vk_usedynamicstaging & (1u<<type))!=0;\n\n\twhile(1)\n\t{\n\t\tbufinf.flags = 0;\n\t\tbufinf.size = n->size = (1u<<20);\n\t\tbufinf.sharingMode = VK_SHARING_MODE_EXCLUSIVE;\n\t\tbufinf.queueFamilyIndexCount = 0;\n\t\tbufinf.pQueueFamilyIndices = NULL;\n\n\t\twhile (bufinf.size < minsize)\n\t\t\tbufinf.size *= 2;\n\n\t\tn->size = bufinf.size;\n\n\t\tbufinf.usage = dynbuf_info[type].usage;\n\t\tif (type != DB_STAGING && usestaging)\n\t\t{\n\t\t\t//create two buffers, one staging/host buffer and one device buffer\n\t\t\tbufinf.usage |= VK_BUFFER_USAGE_TRANSFER_DST_BIT;\n\t\t\tvkCreateBuffer(vk.device, &bufinf, vkallocationcb, &n->devicebuf);\n\t\t\tDebugSetName(VK_OBJECT_TYPE_BUFFER, (uint64_t)n->devicebuf, dynbuf_info[type].name);\n\t\t\tbufinf.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;\n\t\t\tvkCreateBuffer(vk.device, &bufinf, vkallocationcb, &n->stagingbuf);\n\t\t\tDebugSetName(VK_OBJECT_TYPE_BUFFER, (uint64_t)n->devicebuf, \"DB_AUTOSTAGING\");\n\n\t\t\tvkGetBufferMemoryRequirements(vk.device, n->devicebuf, &mem_reqs);\n\t\t\tn->align = mem_reqs.alignment-1;\n\t\t\tmemAllocInfo.allocationSize = mem_reqs.size;\n\t\t\tmemAllocInfo.memoryTypeIndex = vk_find_memory_require(mem_reqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);\n\t\t\tVkAssert(vkAllocateMemory(vk.device, &memAllocInfo, vkallocationcb, &n->devicememory));\n\t\t\tDebugSetName(VK_OBJECT_TYPE_DEVICE_MEMORY, (uint64_t)n->devicememory, \"DB_AUTOSTAGING\");\n\t\t\tVkAssert(vkBindBufferMemory(vk.device, n->devicebuf, n->devicememory, 0));\n\n\t\t\tn->renderbuf = n->devicebuf;\n\t\t}\n\t\telse\n\t\t{\t//single buffer. we'll write directly to the buffer.\n\t\t\tvkCreateBuffer(vk.device, &bufinf, vkallocationcb, &n->stagingbuf);\n\t\t\tDebugSetName(VK_OBJECT_TYPE_BUFFER, (uint64_t)n->stagingbuf, dynbuf_info[type].name);\n\n\t\t\tn->renderbuf = n->stagingbuf;\n\t\t}\n\n\t\t//now allocate some host-visible memory for the buffer that we're going to map.\n\t\tvkGetBufferMemoryRequirements(vk.device, n->stagingbuf, &mem_reqs);\n\t\tn->align = mem_reqs.alignment-1;\n\t\tmemAllocInfo.allocationSize = mem_reqs.size;\n\t\tmemAllocInfo.memoryTypeIndex = ~0;\n\t\tif (memAllocInfo.memoryTypeIndex == ~0 && dynbuf_info[type].nomap)\n\t\t\tmemAllocInfo.memoryTypeIndex = vk_find_memory_try(mem_reqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);\n\t//\tif (memAllocInfo.memoryTypeIndex == ~0)\n\t//\t\tmemAllocInfo.memoryTypeIndex = vk_find_memory_try(mem_reqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT|VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);\n\t\tif (memAllocInfo.memoryTypeIndex == ~0 && n->renderbuf == n->stagingbuf)\t//probably won't get anything, but whatever.\n\t\t\tmemAllocInfo.memoryTypeIndex = vk_find_memory_try(mem_reqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT|VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);\n\t\tif (memAllocInfo.memoryTypeIndex == ~0)\n\t\t\tmemAllocInfo.memoryTypeIndex = vk_find_memory_try(mem_reqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);\n\t\tif (memAllocInfo.memoryTypeIndex == ~0)\n\t\t{\t//if we can't find any usable memory, force staging instead.\n\t\t\tvkDestroyBuffer(vk.device, n->stagingbuf, vkallocationcb);\n\t\t\tif (usestaging)\n\t\t\t\tSys_Error(\"Unable to allocate buffer memory\");\n\t\t\tusestaging = true;\n\t\t\tcontinue;\n\t\t}\n\t\tmemAllocFlagsInfo.flags = 0;\n\t\tif (bufinf.usage & VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT)\n\t\t\tmemAllocFlagsInfo.flags |= VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT;\n\t\tif (memAllocFlagsInfo.flags)\n\t\t\tmemAllocInfo.pNext = &memAllocFlagsInfo;\n\t\tVkAssert(vkAllocateMemory(vk.device, &memAllocInfo, vkallocationcb, &n->stagingmemory));\n\t\tDebugSetName(VK_OBJECT_TYPE_DEVICE_MEMORY, (uint64_t)n->stagingmemory, dynbuf_info[type].name);\n\t\tVkAssert(vkBindBufferMemory(vk.device, n->stagingbuf, n->stagingmemory, 0));\n\n\t\tif (dynbuf_info[type].nomap)\n\t\t{\n\t\t\tn->ptr = NULL;\t//don't want to map this.\n\t\t\tn->stagingcoherent = true;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tVkAssert(vkMapMemory(vk.device, n->stagingmemory, 0, n->size, 0, &n->ptr));\t//persistent-mapped.\n\n\t\t\tn->stagingcoherent = !!(vk.memory_properties.memoryTypes[memAllocInfo.memoryTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);\n\t\t}\n\t\tn->next = *link;\n\t\t*link = n;\n\t\treturn n;\n\t}\n}\nstatic void *fte_restrict VKBE_AllocateStreamingSpace(enum dynbuf_e type, size_t datasize, VkBuffer *buf, VkDeviceSize *offset)\n{\t//FIXME: ubos need alignment\n\tstruct dynbuffer *b = vk.dynbuf[type];\n\tvoid *ret;\n\tif (!b)\n\t{\n\t\tif (!vk.frame->dynbufs[type])\n\t\t\tVKBE_AllocNewStreamingBuffer(&vk.frame->dynbufs[type], type, datasize);\n\t\tb = vk.dynbuf[type] = vk.frame->dynbufs[type];\n\t\tb->offset = b->flushed = 0;\n\t}\n\n\tif (offset?\t//urgh...\n\t\tb->offset + datasize > b->size:\t\t//regular offsetable buffer...\n\t\t(b->offset || datasize > b->size))\t//stoopid buffer space that must have the whole buffer to itself for some reason.\n\t{\n\t\t//flush the old one, just in case.\n\t\tif (!b->stagingcoherent)\n\t\t{\n\t\t\tVkMappedMemoryRange range = {VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE};\n\t\t\trange.offset = b->flushed;\n\t\t\trange.size = b->offset-b->flushed;\n\t\t\trange.memory = b->stagingmemory;\n\t\t\tvkFlushMappedMemoryRanges(vk.device, 1, &range);\n\t\t}\n\n\t\tif (b->devicebuf != VK_NULL_HANDLE)\n\t\t{\n\t\t\tstruct vk_fencework *fence = VK_FencedBegin(NULL, 0);\n\t\t\tVkBufferCopy bcr = {0};\n\t\t\tbcr.srcOffset = b->flushed;\n\t\t\tbcr.dstOffset = b->flushed;\n\t\t\tbcr.size = b->offset-b->flushed;\n\t\t\tvkCmdCopyBuffer(fence->cbuf, b->stagingbuf, b->devicebuf, 1, &bcr);\n\t\t\tVK_FencedSubmit(fence);\n\t\t}\n\n\t\tif (!b->next)\n\t\t\tVKBE_AllocNewStreamingBuffer(&b->next, type, datasize);\n\t\tb = vk.dynbuf[type] = b->next;\n\t\tb->offset = 0;\n\t\tb->flushed = 0;\n\t}\n\n\t*buf = b->renderbuf;\n\tif (offset)\n\t\t*offset = b->offset;\n\n\tret = (qbyte*)b->ptr + b->offset;\n\tb->offset += datasize;\t//FIXME: alignment\n\tif (dynbuf_info[type].align)\n\t{\n\t\tb->offset += dynbuf_info[type].align;\n\t\tb->offset &= ~(dynbuf_info[type].align-1);\n\t}\n\treturn ret;\n}\n\n//called when a new swapchain has been created.\n//makes sure there's no nulls or anything.\nvoid VKBE_InitFramePools(struct vkframe *frame)\n{\n\tuint32_t i;\n\tfor (i = 0; i < DB_MAX; i++)\n\t\tframe->dynbufs[i] = NULL;\n\tframe->descpools = vk.khr_push_descriptor?NULL:VKBE_CreateDescriptorPool();\n\n\n\tframe->numcbufs = 0;\n\tframe->maxcbufs = 0;\n\tframe->cbufs = NULL;\n\t/*{\n\t\tVkCommandBufferAllocateInfo cbai = {VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO};\n\t\tcbai.commandPool = vk.cmdpool;\n\t\tcbai.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;\n\t\tcbai.commandBufferCount = frame->maxcbufs;\n\t\tVkAssert(vkAllocateCommandBuffers(vk.device, &cbai, frame->cbufs));\n\t}*/\n\n\t{\n\t\tVkFenceCreateInfo fci = {VK_STRUCTURE_TYPE_FENCE_CREATE_INFO};\n\t\tfci.flags = VK_FENCE_CREATE_SIGNALED_BIT;\n\t\tVkAssert(vkCreateFence(vk.device,&fci,vkallocationcb,&frame->finishedfence));\n\t}\n}\n\n//called just before submits\n//makes sure that our persistent-mapped memory writes can actually be seen by the hardware.\nvoid VKBE_FlushDynamicBuffers(void)\n{\n\tstruct vk_fencework *fence = NULL;\n\tuint32_t i;\n\tstruct dynbuffer *d;\n\tVkMappedMemoryRange range = {VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE};\n\n\tfor (i = 0; i < DB_MAX; i++)\n\t{\n\t\td = vk.dynbuf[i];\n\t\tif (!d || d->flushed == d->offset)\n\t\t\tcontinue;\n\n\t\tif (!d->stagingcoherent)\n\t\t{\n\t\t\trange.offset = d->flushed;\n\t\t\trange.size = d->offset - d->flushed;\n\t\t\trange.memory = d->stagingmemory;\n\t\t\tvkFlushMappedMemoryRanges(vk.device, 1, &range);\n\t\t}\n\n\t\tif (d->devicebuf != VK_NULL_HANDLE)\n\t\t{\t\n\t\t\tVkBufferCopy bcr = {0};\n\t\t\tbcr.srcOffset = d->flushed;\n\t\t\tbcr.dstOffset = d->flushed;\n\t\t\tbcr.size = d->offset - d->flushed;\n\t\t\tif (!fence)\n\t\t\t\tfence = VK_FencedBegin(NULL, 0);\n\t\t\tvkCmdCopyBuffer(fence->cbuf, d->stagingbuf, d->devicebuf, 1, &bcr);\n\t\t}\n\t\td->flushed = d->offset;\n\t}\n\n\tif (fence)\n\t\tVK_FencedSubmit(fence);\n}\n\nvoid VKBE_Set2D(qboolean twodee)\n{\n\tif (twodee)\n\t\tshaderstate.forcebeflags = BEF_FORCENODEPTH;\n\telse\n\t\tshaderstate.forcebeflags = 0;\n\tshaderstate.curtime = realtime;\n}\n\n#ifdef VK_KHR_acceleration_structure\nstatic void VKBE_DestroyTLAS(void *ctx)\n{\n\tVkAccelerationStructureKHR *tlas = ctx;\n\tvkDestroyAccelerationStructureKHR(vk.device, *tlas, vkallocationcb);\n}\nstatic VkDeviceAddress VKBE_GetBufferDeviceAddress(VkBuffer buf)\n{\n\tVkBufferDeviceAddressInfo info = {VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO, NULL, buf};\n\treturn vkGetBufferDeviceAddress(vk.device, &info);\n}\n\nstruct blasgeom_s\n{\n\tVkBuffer vertbuf;\n\tVkBuffer idxbuf;\n\tVkDeviceSize vertoffset;\n\tVkDeviceSize idxoffset;\n\tsize_t numverts;\n\tsize_t numtris;\n};\nstatic qboolean VKBE_GenerateAccelerationMesh_BSP(model_t *mod, struct blasgeom_s *geom)\n{\n\tunsigned int sno;\n\tmsurface_t *surf;\n\tmesh_t *mesh;\n\tunsigned int numverts;\n\tunsigned int numindexes,i;\n\tunsigned int *ptr_elements;\n\tvec3_t *ptr_verts;\n\n\tnumverts = 0;\n\tnumindexes = 0;\n\tfor (sno = 0; sno < mod->nummodelsurfaces; sno++)\n\t{\n\t\tsurf = &mod->surfaces[sno+mod->firstmodelsurface];\n\t\tif (surf->flags & (SURF_DRAWSKY|SURF_DRAWTURB))\n\t\t\tcontinue;\n\n\t\tif (surf->mesh)\n\t\t{\n\t\t\tmesh = surf->mesh;\n\t\t\tnumverts += mesh->numvertexes;\n\t\t\tnumindexes += mesh->numindexes;\n\t\t}\n\t\telse if (surf->numedges > 2)\n\t\t{\n\t\t\tnumverts += surf->numedges;\n\t\t\tnumindexes += (surf->numedges-2) * 3;\n\t\t}\n\t}\n\tif (!numindexes)\n\t\treturn false;\n\ngeom->idxoffset = geom->vertoffset = 0;\n\tptr_elements = VKBE_AllocateStreamingSpace(DB_ACCELERATIONMESHDATA, sizeof(*ptr_elements)*numindexes, &geom->idxbuf, &geom->idxoffset);\n\tptr_verts = VKBE_AllocateStreamingSpace(DB_ACCELERATIONMESHDATA, sizeof(*ptr_verts)*numverts, &geom->vertbuf, &geom->vertoffset);\n\n\tnumverts = 0;\n\tnumindexes = 0;\n\n\tfor (sno = 0; sno < mod->nummodelsurfaces; sno++)\n\t{\n\t\tsurf = &mod->surfaces[sno+mod->firstmodelsurface];\n\t\tif (surf->flags & (SURF_DRAWSKY|SURF_DRAWTURB))\n\t\t\tcontinue;\n\n\t\tif (surf->mesh)\n\t\t{\n\t\t\tmesh = surf->mesh;\n\t\t\tfor (i = 0; i < mesh->numvertexes; i++)\n\t\t\t\tVectorCopy(mesh->xyz_array[i], ptr_verts[numverts+i]);\n\t\t\tfor (i = 0; i < mesh->numindexes; i+=3)\n\t\t\t{\n\t\t\t\t//flip the triangles as we go\n\t\t\t\tptr_elements[numindexes+i+0] = numverts+mesh->indexes[i+2];\n\t\t\t\tptr_elements[numindexes+i+1] = numverts+mesh->indexes[i+1];\n\t\t\t\tptr_elements[numindexes+i+2] = numverts+mesh->indexes[i+0];\n\t\t\t}\n\t\t\tnumverts += mesh->numvertexes;\n\t\t\tnumindexes += i;\n\t\t}\n\t\telse if (surf->numedges > 2)\n\t\t{\n\t\t\tfloat *vec;\n\t\t\tmedge_t *edge;\n\t\t\tint lindex;\n\t\t\tfor (i = 0; i < surf->numedges; i++)\n\t\t\t{\n\t\t\t\tlindex = mod->surfedges[surf->firstedge + i];\n\n\t\t\t\tif (lindex > 0)\n\t\t\t\t{\n\t\t\t\t\tedge = &mod->edges[lindex];\n\t\t\t\t\tvec = mod->vertexes[edge->v[0]].position;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tedge = &mod->edges[-lindex];\n\t\t\t\t\tvec = mod->vertexes[edge->v[1]].position;\n\t\t\t\t}\n\n\t\t\t\tVectorCopy(vec, ptr_verts[numverts+i]);\n\t\t\t}\n\t\t\tfor (i = 2; i < surf->numedges; i++)\n\t\t\t{\n\t\t\t\t//quake is backwards, not ode\n\t\t\t\tptr_elements[numindexes++] = numverts+i;\n\t\t\t\tptr_elements[numindexes++] = numverts+i-1;\n\t\t\t\tptr_elements[numindexes++] = numverts;\n\t\t\t}\n\t\t\tnumverts += surf->numedges;\n\t\t}\n\t}\n\n\tgeom->numverts = numverts;\n\tgeom->numtris = numindexes/3;\n\treturn true;\n}\nstatic VkAccelerationStructureKHR VKBE_GenerateBLAS(model_t *mod)\n{\n\tstruct blasgeom_s geom = {VK_NULL_HANDLE,VK_NULL_HANDLE,0,0};\n\tVkAccelerationStructureCreateInfoKHR asci = {VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_CREATE_INFO_KHR};\n\tVkAccelerationStructureBuildGeometryInfoKHR asbgi = {VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_BUILD_GEOMETRY_INFO_KHR};\n\tuint32_t maxPrimitiveCounts[1] = {0};\n\tVkAccelerationStructureGeometryKHR asg[1];\n\tVkAccelerationStructureBuildRangeInfoKHR asbri[1];\n\tVkAccelerationStructureBuildRangeInfoKHR const *const asbrip = {asbri};\n\n\tVkAccelerationStructureBuildSizesInfoKHR asbsi = {VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_BUILD_SIZES_INFO_KHR};\n\n\tVkBuffer transformbuf, scratchbuf;\n\tVkDeviceSize transformoffset = 0;\n\n\t//this is stupid. oh well.\n\tVkTransformMatrixKHR *transform;\n\ttransform = VKBE_AllocateStreamingSpace(DB_ACCELERATIONINSTANCE, sizeof(*transform), &transformbuf, &transformoffset);\n\tVector4Set(transform->matrix[0], 1,0,0,0);\n\tVector4Set(transform->matrix[1], 0,1,0,0);\n\tVector4Set(transform->matrix[2], 0,0,1,0);\n\n\t//FIXME: use of VKBE_AllocateStreamingSpace on the geomdata, transform, and blas storage itself mean we can only use this for a single frame, regenerating each time. which is wasteful for a blas that contains the entire worldmodel.\n\tVKBE_GenerateAccelerationMesh_BSP(mod, &geom);\n\n\tasg[0].sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_KHR;\n\tasg[0].pNext = NULL;\n\tasg[0].flags = VK_GEOMETRY_OPAQUE_BIT_KHR;\n\tasg[0].geometryType = VK_GEOMETRY_TYPE_TRIANGLES_KHR;\n\tasg[0].geometry.triangles.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_TRIANGLES_DATA_KHR;\n\tasg[0].geometry.triangles.pNext = NULL;\n\tasg[0].geometry.triangles.vertexFormat = VK_FORMAT_R32G32B32_SFLOAT;\n\tasg[0].geometry.triangles.vertexData.deviceAddress = VKBE_GetBufferDeviceAddress(geom.vertbuf);\n\tasg[0].geometry.triangles.vertexStride = sizeof(vec3_t);\n\tasg[0].geometry.triangles.maxVertex = geom.numverts;\n\tasg[0].geometry.triangles.indexType = VK_INDEX_TYPE_UINT32;\n\tasg[0].geometry.triangles.indexData.deviceAddress = VKBE_GetBufferDeviceAddress(geom.idxbuf);\n\tasg[0].geometry.triangles.transformData.deviceAddress = VKBE_GetBufferDeviceAddress(transformbuf);\n\n\tasbri[0].firstVertex = geom.vertoffset/sizeof(vec3_t);\n\tasbri[0].primitiveCount = maxPrimitiveCounts[0] = geom.numtris;\n\tasbri[0].primitiveOffset = geom.idxoffset;\n\tasbri[0].transformOffset = transformoffset;\n\n\tasci.createFlags = 0;\n\tasci.type = VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_KHR;\n\tasci.deviceAddress = 0;\t//no overriding here.\n\n\tasbgi.type = asci.type;\n\tasbgi.flags = VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_TRACE_BIT_KHR /* | VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_UPDATE_BIT_KHR*/;\n\tasbgi.mode = VK_BUILD_ACCELERATION_STRUCTURE_MODE_BUILD_KHR;\n\tasbgi.srcAccelerationStructure = VK_NULL_HANDLE; //ignored here\n\tasbgi.dstAccelerationStructure = VK_NULL_HANDLE; //filled in later\n\tasbgi.geometryCount = countof(asg);\n\tasbgi.pGeometries = asg;\n\tasbgi.ppGeometries = NULL;\t//too much indirection! oh noes!\n\n\tVKBE_FlushDynamicBuffers();\n\n\tvkGetAccelerationStructureBuildSizesKHR(vk.device, VK_ACCELERATION_STRUCTURE_BUILD_TYPE_DEVICE_KHR, &asbgi, maxPrimitiveCounts, &asbsi);\n\tVKBE_AllocateStreamingSpace(DB_ACCELERATIONSTRUCT, asci.size = asbsi.accelerationStructureSize, &asci.buffer, &asci.offset);\n\tVKBE_AllocateStreamingSpace(DB_ACCELERATIONSCRATCH, asbsi.buildScratchSize, &scratchbuf, NULL);\n\tasbgi.scratchData.deviceAddress = VKBE_GetBufferDeviceAddress(scratchbuf);\n\n\tvkCreateAccelerationStructureKHR(vk.device, &asci, vkallocationcb, &asbgi.dstAccelerationStructure);\n\tDebugSetName(VK_OBJECT_TYPE_ACCELERATION_STRUCTURE_KHR, (uint64_t)asbgi.dstAccelerationStructure, \"ShadowBLAS\");\n\tvkCmdBuildAccelerationStructuresKHR(vk.rendertarg->cbuf, 1, &asbgi, &asbrip);\n\n\t{\n\t\tVkMemoryBarrier membarrier\t= {VK_STRUCTURE_TYPE_MEMORY_BARRIER};\n\t\tmembarrier.srcAccessMask\t= VK_ACCESS_ACCELERATION_STRUCTURE_WRITE_BIT_KHR;\n\t\tmembarrier.dstAccessMask\t= VK_ACCESS_ACCELERATION_STRUCTURE_READ_BIT_KHR;\n\t\tvkCmdPipelineBarrier(vk.rendertarg->cbuf, VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR, VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR, 0, 1, &membarrier, 0, NULL, 0, NULL);\n\t}\n\treturn asbgi.dstAccelerationStructure;\n}\nstatic VkAccelerationStructureKHR VKBE_GenerateTLAS(void)\n{\n\tVkAccelerationStructureKHR blas = VKBE_GenerateBLAS(r_worldentity.model);\n\tVkAccelerationStructureDeviceAddressInfoKHR blasinfo = {VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_DEVICE_ADDRESS_INFO_KHR, NULL, blas};\n\tVkAccelerationStructureCreateInfoKHR asci = {VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_CREATE_INFO_KHR};\n\tVkAccelerationStructureBuildGeometryInfoKHR asbgi = {VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_BUILD_GEOMETRY_INFO_KHR};\n\tuint32_t maxPrimitiveCounts[1] = {0};\n\tVkAccelerationStructureGeometryKHR asg[1];\n\tVkAccelerationStructureBuildRangeInfoKHR asbri[1];\n\tVkAccelerationStructureBuildRangeInfoKHR const *const asbrip = {asbri};\n\n\tVkAccelerationStructureBuildSizesInfoKHR asbsi = {VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_BUILD_SIZES_INFO_KHR};\n\n\tVkBuffer instancesbuf, scratchbuf;\n\tVkDeviceSize instancesofs = 0;\n\tsize_t numinstances = 1;\n\tVkAccelerationStructureInstanceKHR *instances = VKBE_AllocateStreamingSpace(DB_ACCELERATIONINSTANCE, sizeof(*instances)*numinstances, &instancesbuf, &instancesofs);\n\n#if 0\n\tbatch_t **worldbatches = r_worldentity.model->batches; //FIXME\n\tbatch_t *batch;\n\tint i, id = 0;\n\tfor (i = 0; i < SHADER_SORT_COUNT; i++)\n\t{\n\t\tif (worldbatches)\n\t\t{\n\t\t\tfor (batch = worldbatches[i]; batch; batch = batch->next)\n\t\t\t{\n\t\t\t\tif (batch->meshes == batch->firstmesh)\n\t\t\t\t\tcontinue;\t//nothing to do...\n\n\t\t\t\tif (batch->buildmeshes)\n\t\t\t\t\tbatch->buildmeshes(batch);\n\n\t\t\t\t{\n\t\t\t\t\tshader_t *shader = batch->shader;\n\t\t\t\t\tunsigned int bf;\n\t\t\t\t\tunsigned int nummeshes = batch->meshes - batch->firstmesh;\n\t\t\t\t\tif (!nummeshes)\n\t\t\t\t\t\tcontinue;\n\n\t\t\t\t\t//ubo[id].stuff = ...;\n\n\t\t\t\t\tVectorCopy(batch->ent->axis[0], instances->transform.matrix[0]);\tinstances->transform.matrix[0][3] = batch->ent->origin[0];\n\t\t\t\t\tVectorCopy(batch->ent->axis[1], instances->transform.matrix[1]);\tinstances->transform.matrix[1][3] = batch->ent->origin[1];\n\t\t\t\t\tVectorCopy(batch->ent->axis[2], instances->transform.matrix[2]);\tinstances->transform.matrix[2][3] = batch->ent->origin[2];\n\t\t\t\t\tinstances->instanceCustomIndex = id++;\t//extra info\n\t\t\t\t\tif (batch->shader->flags & SHADER_SKY)\n\t\t\t\t\t\tinstances->mask = 0x2;\n\t\t\t\t\telse if (batch->shader->sort > SHADER_SORT_OPAQUE)\n\t\t\t\t\t\tinstances->mask = 0x4;\n\t\t\t\t\telse\n\t\t\t\t\t\tinstances->mask = 0x1;\n\t\t\t\t\tinstances->instanceShaderBindingTableRecordOffset = shader->id;\t//material id\n\t\t\t\t\tif (shader->flags & SHADER_CULL_FRONT)\n\t\t\t\t\t\tinstances->flags = VK_GEOMETRY_INSTANCE_TRIANGLE_FRONT_COUNTERCLOCKWISE_BIT_KHR;\n\t\t\t\t\telse if (shader->flags & SHADER_CULL_BACK)\n\t\t\t\t\t\tinstances->flags = 0;\n\t\t\t\t\telse\n\t\t\t\t\t\tinstances->flags = VK_GEOMETRY_INSTANCE_TRIANGLE_FACING_CULL_DISABLE_BIT_KHR;\n\t\t\t\t\tinstances->accelerationStructureReference = batch->blas;\t//no half measures\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t//and non-world too... may require temporary blas for lerping/skeletal models.\n\t}\n#else\n\tVector4Set(instances->transform.matrix[0], 1,0,0,0);\n\tVector4Set(instances->transform.matrix[1], 0,1,0,0);\n\tVector4Set(instances->transform.matrix[2], 0,0,1,0);\n\tinstances->instanceCustomIndex = 0; //index into our ssbo... if we had one...\n\tinstances->mask = 0x01;\n\tinstances->instanceShaderBindingTableRecordOffset = 0;\t//FIXME: alphamasked stuff needs a texture somehow\n\tinstances->flags = VK_GEOMETRY_INSTANCE_TRIANGLE_FACING_CULL_DISABLE_BIT_KHR; //FIXME: optimise\n\tinstances->accelerationStructureReference = vkGetAccelerationStructureDeviceAddressKHR(vk.device, &blasinfo);\n#endif\n\tasg[0].sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_KHR;\n\tasg[0].pNext = NULL;\n\tasg[0].flags = VK_GEOMETRY_OPAQUE_BIT_KHR;\n\tasg[0].geometryType = VK_GEOMETRY_TYPE_INSTANCES_KHR;\n\tasg[0].geometry.instances.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_INSTANCES_DATA_KHR;\n\tasg[0].geometry.instances.pNext = NULL;\n\tasg[0].geometry.instances.arrayOfPointers = false;\n\tasg[0].geometry.instances.data.deviceAddress = VKBE_GetBufferDeviceAddress(instancesbuf);\n\n\tasbri[0].firstVertex = 0;\n\tasbri[0].primitiveCount = maxPrimitiveCounts[0] = numinstances;\n\tasbri[0].primitiveOffset = instancesofs;\n\tasbri[0].transformOffset = 0;\n\n\tasci.createFlags = 0;\n\tasci.type = VK_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL_KHR;\n\tasci.deviceAddress = 0;\t//no overriding here.\n\n\tasbgi.type = asci.type;\n\tasbgi.flags = VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_TRACE_BIT_KHR /* | VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_UPDATE_BIT_KHR*/;\n\tasbgi.mode = VK_BUILD_ACCELERATION_STRUCTURE_MODE_BUILD_KHR;\n\tasbgi.srcAccelerationStructure = VK_NULL_HANDLE; //ignored here\n\tasbgi.dstAccelerationStructure = VK_NULL_HANDLE; //filled in later\n\tasbgi.geometryCount = countof(asg);\n\tasbgi.pGeometries = asg;\n\tasbgi.ppGeometries = NULL;\t//too much indirection! oh noes!\n\n\tVKBE_FlushDynamicBuffers();\n\tvkGetAccelerationStructureBuildSizesKHR(vk.device, VK_ACCELERATION_STRUCTURE_BUILD_TYPE_DEVICE_KHR, &asbgi, maxPrimitiveCounts, &asbsi);\n\tVKBE_AllocateStreamingSpace(DB_ACCELERATIONSTRUCT, asci.size = asbsi.accelerationStructureSize, &asci.buffer, &asci.offset);\n\tVKBE_AllocateStreamingSpace(DB_ACCELERATIONSCRATCH, asbsi.buildScratchSize, &scratchbuf, NULL);\n\tasbgi.scratchData.deviceAddress = VKBE_GetBufferDeviceAddress(scratchbuf);\n\n\tvkCreateAccelerationStructureKHR(vk.device, &asci, vkallocationcb, &asbgi.dstAccelerationStructure);\n\tDebugSetName(VK_OBJECT_TYPE_ACCELERATION_STRUCTURE_KHR, (uint64_t)asbgi.dstAccelerationStructure, \"ShadowTLAS\");\n\tvkCmdBuildAccelerationStructuresKHR(vk.rendertarg->cbuf, 1, &asbgi, &asbrip);\n\tVK_AtFrameEnd(VKBE_DestroyTLAS, &asbgi.dstAccelerationStructure, sizeof(asbgi.dstAccelerationStructure));\t//clean up the tlas, each frame gets a new one.\n\tVK_AtFrameEnd(VKBE_DestroyTLAS, &blas, sizeof(blas));\t//clean up the tlas, each frame gets a new one.\n\n\t{\n\t\tVkMemoryBarrier membarrier\t= {VK_STRUCTURE_TYPE_MEMORY_BARRIER};\n\t\tmembarrier.srcAccessMask\t= VK_ACCESS_ACCELERATION_STRUCTURE_WRITE_BIT_KHR;\n\t\tmembarrier.dstAccessMask\t= VK_ACCESS_ACCELERATION_STRUCTURE_READ_BIT_KHR;\n\t\tvkCmdPipelineBarrier(vk.rendertarg->cbuf, VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR, VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR, 0, 1, &membarrier, 0, NULL, 0, NULL);\n\t}\n\t//FIXME: use a compute cbuf, add a fence to block the rtlight queries until this BVH is done.\n\t//vkResetEvent\n\t//vkCmdSetEvent\n\t//vkCmdPipelineBarrier\n\treturn asbgi.dstAccelerationStructure;\n}\n#endif\n\n//called at the start of each frame\n//resets the working dynamic buffers to this frame's storage, to avoid stepping on frames owned by the gpu\nvoid VKBE_RestartFrame(void)\n{\n\tuint32_t i;\n\tfor (i = 0; i < DB_MAX; i++)\n\t\tvk.dynbuf[i] = NULL;\n\n\tshaderstate.rc.activepipeline = VK_NULL_HANDLE;\n\tvk.descpool = vk.frame->descpools;\n\tif (vk.descpool)\n\t{\n\t\tvkResetDescriptorPool(vk.device, vk.descpool->pool, 0);\n\t\tvk.descpool->availsets = vk.descpool->totalsets;\n\t}\n\n#ifdef VK_KHR_acceleration_structure\n\tif (vk.khr_ray_query && r_worldentity.model && shaderstate.needtlas)\n\t\tshaderstate.tlas = VKBE_GenerateTLAS();\n\telse\n\t\tshaderstate.tlas = VK_NULL_HANDLE;\n\tshaderstate.needtlas = false;\n#endif\n}\n\nvoid VKBE_ShutdownFramePools(struct vkframe *frame)\n{\n\tstruct dynbuffer *db;\n\tstruct descpool *dp;\n\tuint32_t i;\n\n\tfor (i = 0; i < DB_MAX; i++)\n\t{\n\t\twhile(frame->dynbufs[i])\n\t\t{\n\t\t\tdb = frame->dynbufs[i];\n\t\t\tvkDestroyBuffer(vk.device, db->stagingbuf, vkallocationcb);\n\t\t\tvkFreeMemory(vk.device, db->stagingmemory, vkallocationcb);\n\t\t\tif (db->devicebuf != VK_NULL_HANDLE)\n\t\t\t{\n\t\t\t\tvkDestroyBuffer(vk.device, db->devicebuf, vkallocationcb);\n\t\t\t\tvkFreeMemory(vk.device, db->devicememory, vkallocationcb);\n\t\t\t}\n\t\t\tframe->dynbufs[i] = db->next;\n\t\t\tZ_Free(db);\n\t\t}\n\t}\n\n\twhile(frame->descpools)\n\t{\n\t\tdp = frame->descpools;\n\t\tvkDestroyDescriptorPool(vk.device, dp->pool, vkallocationcb);\n\t\tframe->descpools = dp->next;\n\t\tZ_Free(dp);\n\t}\n}\n\nvoid VKBE_Shutdown(void)\n{\n\tif (!shaderstate.inited)\n\t\treturn;\n\n#ifdef RTLIGHTS\n\tSh_Shutdown();\n#endif\n\n\tShader_ReleaseGeneric(shaderstate.programfixedemu[0]);\n\tShader_ReleaseGeneric(shaderstate.programfixedemu[1]);\n\n\tshaderstate.inited = false;\n#ifdef RTLIGHTS\n\tVK_TerminateShadowMap();\n#endif\n\tZ_Free(shaderstate.wbatches);\n\tshaderstate.wbatches = NULL;\n\n\tvkDestroyBuffer(vk.device, shaderstate.staticbuf, vkallocationcb);\n\tVK_ReleasePoolMemory(&shaderstate.staticbufmem);\n}\n\nstatic texid_t SelectPassTexture(const shaderpass_t *pass)\n{\n\tsafeswitch(pass->texgen)\n\t{\n\tcase T_GEN_DIFFUSE:\n\t\treturn shaderstate.curtexnums->base;\n\tcase T_GEN_NORMALMAP:\n\t\tif (TEXLOADED(shaderstate.curtexnums->bump))\n\t\t\treturn shaderstate.curtexnums->bump;\n\t\telse\n\t\t\treturn missing_texture_normal;\n\tcase T_GEN_SPECULAR:\n\t\tif (TEXLOADED(shaderstate.curtexnums->specular))\n\t\t\treturn shaderstate.curtexnums->specular;\n\t\telse\n\t\t\treturn missing_texture_gloss;\n\tcase T_GEN_UPPEROVERLAY:\n\t\treturn shaderstate.curtexnums->upperoverlay;\n\tcase T_GEN_LOWEROVERLAY:\n\t\treturn shaderstate.curtexnums->loweroverlay;\n\tcase T_GEN_FULLBRIGHT:\n\t\treturn shaderstate.curtexnums->fullbright;\n\tcase T_GEN_PALETTED:\n\t\treturn shaderstate.curtexnums->paletted;\n\tcase T_GEN_REFLECTCUBE:\n\t\tif (TEXLOADED(shaderstate.curtexnums->reflectcube))\n\t\t\treturn shaderstate.curtexnums->reflectcube;\n\t\telse if (shaderstate.curbatch->envmap)\n\t\t\treturn shaderstate.curbatch->envmap;\n\t\telse\n\t\t\treturn r_blackcubeimage;\t//FIXME\n\tcase T_GEN_REFLECTMASK:\n\t\treturn shaderstate.curtexnums->reflectmask;\n\tcase T_GEN_DISPLACEMENT:\n\t\treturn shaderstate.curtexnums->displacement;\n\tcase T_GEN_OCCLUSION:\n\t\treturn shaderstate.curtexnums->occlusion;\n\tcase T_GEN_TRANSMISSION:\n\t\treturn shaderstate.curtexnums->transmission;\n\tcase T_GEN_THICKNESS:\n\t\treturn shaderstate.curtexnums->thickness;\n\n\tcase T_GEN_ANIMMAP:\n\t\treturn pass->anim_frames[(int)(pass->anim_fps * shaderstate.curtime) % pass->anim_numframes];\n\tcase T_GEN_SINGLEMAP:\n\t\treturn pass->anim_frames[0];\n\tcase T_GEN_DELUXMAP:\n\t\t{\n\t\t\tint lmi = shaderstate.curbatch->lightmap[0];\n\t\t\tif (lmi < 0 || !lightmap[lmi]->hasdeluxe)\n\t\t\t\treturn r_nulltex;\n\t\t\telse\n\t\t\t{\n\t\t\t\tlmi+=1;\n\t\t\t\treturn lightmap[lmi]->lightmap_texture;\n\t\t\t}\n\t\t}\n\tcase T_GEN_LIGHTMAP:\n\t\t{\n\t\t\tint lmi = shaderstate.curbatch->lightmap[0];\n\t\t\tif (lmi < 0)\n\t\t\t\treturn r_whiteimage;\n\t\t\telse\n\t\t\t\treturn lightmap[lmi]->lightmap_texture;\n\t\t}\n\n\tcase T_GEN_CURRENTRENDER:\n\t\treturn shaderstate.tex_currentrender;\n#ifdef HAVE_MEDIA_DECODER\n\tcase T_GEN_VIDEOMAP:\n\t\tif (pass->cin)\n\t\t\treturn Media_UpdateForShader(pass->cin);\n\t\treturn r_nulltex;\n#endif\n\n\tcase T_GEN_LIGHTCUBEMAP:\t//light's projected cubemap\n\t\tif (shaderstate.curdlight)\n\t\t\treturn shaderstate.curdlight->cubetexture;\n\t\telse\n\t\t\treturn r_blackcubeimage;\n\n\tcase T_GEN_SHADOWMAP:\t//light's depth values.\n\t\treturn shaderstate.currentshadowmap;\n\n\tcase T_GEN_REFLECTION:\t//reflection image (mirror-as-fbo)\n\t\treturn &shaderstate.rt_reflection.q_colour;\n\tcase T_GEN_REFRACTION:\t//refraction image (portal-as-fbo)\n\t\treturn shaderstate.tex_refraction;\n\tcase T_GEN_REFRACTIONDEPTH:\t//refraction image (portal-as-fbo)\n\t\treturn &shaderstate.rt_refraction.q_depth;\n\tcase T_GEN_RIPPLEMAP:\t//ripplemap image (water surface distortions-as-fbo)\n\t\treturn shaderstate.tex_ripplemap;\n\n\tcase T_GEN_SOURCECOLOUR: //used for render-to-texture targets\n\t\treturn vk.sourcecolour;\n\tcase T_GEN_SOURCEDEPTH:\t//used for render-to-texture targets\n\t\treturn vk.sourcedepth;\n\n\tcase T_GEN_SOURCECUBE:\t//used for render-to-texture targets\n\t\treturn r_blackcubeimage;\n\n\tcase T_GEN_GBUFFER0:\n\tcase T_GEN_GBUFFER1:\n\tcase T_GEN_GBUFFER2:\n\tcase T_GEN_GBUFFER3:\n\tcase T_GEN_GBUFFER4:\n\tcase T_GEN_GBUFFER5:\n\tcase T_GEN_GBUFFER6:\n\tcase T_GEN_GBUFFER7:\n\tsafedefault:\n\t\treturn r_nulltex;\n\t}\n\treturn r_nulltex;\n}\n\nstatic void T_Gen_CurrentRender(void)\n{\n\tvk_image_t *img;\n\t/*gah... I pitty the gl drivers*/\n\tif (!shaderstate.tex_currentrender)\n\t{\n\t\tshaderstate.tex_currentrender = Image_CreateTexture(\"***$currentrender***\", NULL, 0);\n\t\tshaderstate.tex_currentrender->vkimage = Z_Malloc(sizeof(*shaderstate.tex_currentrender->vkimage));\n\t}\n\timg = shaderstate.tex_currentrender->vkimage;\n\tif (img->width != vid.fbpwidth || img->height != vid.fbpheight)\n\t{\n\t\t//FIXME: free the old image when its safe to do so.\n\t\t*img = VK_CreateTexture2DArray(vid.fbpwidth, vid.fbpheight, 1, 1, -vk.backbufformat, PTI_2D, false, shaderstate.tex_currentrender->ident);\n\n\t\tif (!img->sampler)\n\t\t\tVK_CreateSampler(shaderstate.tex_currentrender->flags, img);\n\t}\n\n\n\tvkCmdEndRenderPass(vk.rendertarg->cbuf);\n\t\n\t//submit now?\n\n\t//copy the backbuffer to our image\n\t{\n\t\tVkImageCopy region;\n\t\tregion.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;\n\t\tregion.srcSubresource.mipLevel = 0;\n\t\tregion.srcSubresource.baseArrayLayer = 0;\n\t\tregion.srcSubresource.layerCount = 1;\n\t\tregion.srcOffset.x = 0;\n\t\tregion.srcOffset.y = 0;\n\t\tregion.srcOffset.z = 0;\n\t\tregion.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;\n\t\tregion.dstSubresource.mipLevel = 0;\n\t\tregion.dstSubresource.baseArrayLayer = 0;\n\t\tregion.dstSubresource.layerCount = 1;\n\t\tregion.dstOffset.x = 0;\n\t\tregion.dstOffset.y = 0;\n\t\tregion.dstOffset.z = 0;\n\t\tregion.extent.width = vid.fbpwidth;\n\t\tregion.extent.height = vid.fbpheight;\n\t\tregion.extent.depth = 1;\n\n\t\tset_image_layout(vk.rendertarg->cbuf, vk.frame->backbuf->colour.image, VK_IMAGE_ASPECT_COLOR_BIT,\n\t\t\t\tVK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,\tVK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,\tVK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,\n\t\t\t\tVK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,\t\tVK_ACCESS_TRANSFER_READ_BIT,\t\tVK_PIPELINE_STAGE_TRANSFER_BIT);\n\t\tset_image_layout(vk.rendertarg->cbuf, img->image, VK_IMAGE_ASPECT_COLOR_BIT,\n\t\t\t\tVK_IMAGE_LAYOUT_UNDEFINED,\t\t\t0,\t\t\t\t\tVK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,\n\t\t\t\tVK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,\t\tVK_ACCESS_TRANSFER_WRITE_BIT,\t\tVK_PIPELINE_STAGE_TRANSFER_BIT);\n\t\tvkCmdCopyImage(vk.rendertarg->cbuf, vk.frame->backbuf->colour.image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, img->image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &region);\n\t\tset_image_layout(vk.rendertarg->cbuf, img->image, VK_IMAGE_ASPECT_COLOR_BIT,\n\t\t\t\tVK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,\t\tVK_ACCESS_TRANSFER_WRITE_BIT,\t\tVK_PIPELINE_STAGE_TRANSFER_BIT,\n\t\t\t\tVK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,\tVK_ACCESS_SHADER_READ_BIT,\t\tVK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);\n\t\tset_image_layout(vk.rendertarg->cbuf, vk.frame->backbuf->colour.image, VK_IMAGE_ASPECT_COLOR_BIT,\n\t\t\t\tVK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,\t\tVK_ACCESS_TRANSFER_READ_BIT,\t\tVK_PIPELINE_STAGE_TRANSFER_BIT,\n\t\t\t\tVK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,\tVK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,\tVK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT);\n\n\t\timg->layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;\n\t}\n\n\n\t//submit now?\n\t//barrier?\n\tvkCmdBeginRenderPass(vk.rendertarg->cbuf, &vk.rendertarg->restartinfo, VK_SUBPASS_CONTENTS_INLINE);\n\t//fixme: viewport+scissor?\n}\n\nstatic void R_FetchPlayerColour(unsigned int cv, vec3_t rgb)\n{\n\tint i;\n\n\tif (cv >= 16)\n\t{\n\t\trgb[0] = (((cv&0xff0000)>>16)**((unsigned char*)&d_8to24rgbtable[15]+0)) / (256.0*256);\n\t\trgb[1] = (((cv&0x00ff00)>>8)**((unsigned char*)&d_8to24rgbtable[15]+1)) / (256.0*256);\n\t\trgb[2] = (((cv&0x0000ff)>>0)**((unsigned char*)&d_8to24rgbtable[15]+2)) / (256.0*256);\n\t\treturn;\n\t}\n\ti = cv;\n\tif (i >= 8)\n\t{\n\t\ti<<=4;\n\t}\n\telse\n\t{\n\t\ti<<=4;\n\t\ti+=15;\n\t}\n\ti*=3;\n\trgb[0] = host_basepal[i+0] / 255.0;\n\trgb[1] = host_basepal[i+1] / 255.0;\n\trgb[2] = host_basepal[i+2] / 255.0;\n/*\tif (!gammaworks)\n\t{\n\t\t*retred = gammatable[*retred];\n\t\t*retgreen = gammatable[*retgreen];\n\t\t*retblue = gammatable[*retblue];\n\t}*/\n}\n\n//source is always packed\n//dest is packed too\nstatic void colourgen(const shaderpass_t *pass, int cnt, byte_vec4_t *srcb, avec4_t *srcf, vec4_t *dst, const mesh_t *mesh)\n{\n\tswitch (pass->rgbgen)\n\t{\n\tcase RGB_GEN_ENTITY:\n\t\twhile((cnt)--)\n\t\t{\n\t\t\tdst[cnt][0] = shaderstate.curentity->shaderRGBAf[0];\n\t\t\tdst[cnt][1] = shaderstate.curentity->shaderRGBAf[1];\n\t\t\tdst[cnt][2] = shaderstate.curentity->shaderRGBAf[2];\n\t\t}\n\t\tbreak;\n\tcase RGB_GEN_ONE_MINUS_ENTITY:\n\t\twhile((cnt)--)\n\t\t{\n\t\t\tdst[cnt][0] = 1-shaderstate.curentity->shaderRGBAf[0];\n\t\t\tdst[cnt][1] = 1-shaderstate.curentity->shaderRGBAf[1];\n\t\t\tdst[cnt][2] = 1-shaderstate.curentity->shaderRGBAf[2];\n\t\t}\n\t\tbreak;\n\tcase RGB_GEN_VERTEX_LIGHTING:\n#if MAXRLIGHTMAPS > 1\n\t\tif (mesh->colors4f_array[1])\n\t\t{\n\t\t\tfloat lm[MAXRLIGHTMAPS];\n\t\t\tlm[0] = d_lightstylevalue[shaderstate.curbatch->vtlightstyle[0]]/256.0f*shaderstate.identitylighting;\n\t\t\tlm[1] = d_lightstylevalue[shaderstate.curbatch->vtlightstyle[1]]/256.0f*shaderstate.identitylighting;\n\t\t\tlm[2] = d_lightstylevalue[shaderstate.curbatch->vtlightstyle[2]]/256.0f*shaderstate.identitylighting;\n\t\t\tlm[3] = d_lightstylevalue[shaderstate.curbatch->vtlightstyle[3]]/256.0f*shaderstate.identitylighting;\n\t\t\twhile((cnt)--)\n\t\t\t{\n\t\t\t\tVectorScale(\t\tmesh->colors4f_array[0][cnt], lm[0], dst[cnt]);\n\t\t\t\tVectorMA(dst[cnt],\tlm[1], mesh->colors4f_array[1][cnt], dst[cnt]);\n\t\t\t\tVectorMA(dst[cnt],\tlm[2], mesh->colors4f_array[2][cnt], dst[cnt]);\n\t\t\t\tVectorMA(dst[cnt],\tlm[3], mesh->colors4f_array[3][cnt], dst[cnt]);\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n#endif\n\n\t\tif (shaderstate.identitylighting != 1)\n\t\t{\n\t\t\tif (srcf)\n\t\t\t{\n\t\t\t\twhile((cnt)--)\n\t\t\t\t{\n\t\t\t\t\tdst[cnt][0] = srcf[cnt][0]*shaderstate.identitylighting;\n\t\t\t\t\tdst[cnt][1] = srcf[cnt][1]*shaderstate.identitylighting;\n\t\t\t\t\tdst[cnt][2] = srcf[cnt][2]*shaderstate.identitylighting;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (srcb)\n\t\t\t{\n\t\t\t\tfloat t = shaderstate.identitylighting * (1/255.0);\n\t\t\t\twhile((cnt)--)\n\t\t\t\t{\n\t\t\t\t\tdst[cnt][0] = srcb[cnt][0]*t;\n\t\t\t\t\tdst[cnt][1] = srcb[cnt][1]*t;\n\t\t\t\t\tdst[cnt][2] = srcb[cnt][2]*t;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\twhile((cnt)--)\n\t\t\t\t{\n\t\t\t\t\tdst[cnt][0] = shaderstate.identitylighting;\n\t\t\t\t\tdst[cnt][1] = shaderstate.identitylighting;\n\t\t\t\t\tdst[cnt][2] = shaderstate.identitylighting;\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\tcase RGB_GEN_VERTEX_EXACT:\n\t\tif (srcf)\n\t\t{\n\t\t\twhile((cnt)--)\n\t\t\t{\n\t\t\t\tdst[cnt][0] = srcf[cnt][0];\n\t\t\t\tdst[cnt][1] = srcf[cnt][1];\n\t\t\t\tdst[cnt][2] = srcf[cnt][2];\n\t\t\t}\n\t\t}\n\t\telse if (srcb)\n\t\t{\n\t\t\tfloat t = 1/255.0;\n\t\t\twhile((cnt)--)\n\t\t\t{\n\t\t\t\tdst[cnt][0] = srcb[cnt][0]*t;\n\t\t\t\tdst[cnt][1] = srcb[cnt][1]*t;\n\t\t\t\tdst[cnt][2] = srcb[cnt][2]*t;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\twhile((cnt)--)\n\t\t\t{\n\t\t\t\tdst[cnt][0] = 1;\n\t\t\t\tdst[cnt][1] = 1;\n\t\t\t\tdst[cnt][2] = 1;\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t\tbreak;\n\tcase RGB_GEN_ONE_MINUS_VERTEX:\n\t\tif (srcf)\n\t\t{\n\t\t\twhile((cnt)--)\n\t\t\t{\n\t\t\t\tdst[cnt][0] = 1-srcf[cnt][0];\n\t\t\t\tdst[cnt][1] = 1-srcf[cnt][1];\n\t\t\t\tdst[cnt][2] = 1-srcf[cnt][2];\n\t\t\t}\n\t\t}\n\t\tbreak;\n\tcase RGB_GEN_IDENTITY_LIGHTING:\n\t\tif (shaderstate.curbatch->vtlightstyle[0] != 255 && d_lightstylevalue[shaderstate.curbatch->vtlightstyle[0]] != 256)\n\t\t{\n\t\t\tvec_t val = shaderstate.identitylighting * d_lightstylevalue[shaderstate.curbatch->vtlightstyle[0]]/256.0f;\n\t\t\twhile((cnt)--)\n\t\t\t{\n\t\t\t\tdst[cnt][0] = val;\n\t\t\t\tdst[cnt][1] = val;\n\t\t\t\tdst[cnt][2] = val;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\t//compensate for overbrights\n\t\t\twhile((cnt)--)\n\t\t\t{\n\t\t\t\tdst[cnt][0] = shaderstate.identitylighting;\n\t\t\t\tdst[cnt][1] = shaderstate.identitylighting;\n\t\t\t\tdst[cnt][2] = shaderstate.identitylighting;\n\t\t\t}\n\t\t}\n\t\tbreak;\n\tcase RGB_GEN_IDENTITY_OVERBRIGHT:\n\t\twhile((cnt)--)\n\t\t{\n\t\t\tdst[cnt][0] = shaderstate.identitylightmap;\n\t\t\tdst[cnt][1] = shaderstate.identitylightmap;\n\t\t\tdst[cnt][2] = shaderstate.identitylightmap;\n\t\t}\n\t\tbreak;\n\tdefault:\n\tcase RGB_GEN_IDENTITY:\n\t\twhile((cnt)--)\n\t\t{\n\t\t\tdst[cnt][0] = shaderstate.identitylighting;\n\t\t\tdst[cnt][1] = shaderstate.identitylighting;\n\t\t\tdst[cnt][2] = shaderstate.identitylighting;\n\t\t}\n\t\tbreak;\n\tcase RGB_GEN_CONST:\n\t\twhile((cnt)--)\n\t\t{\n\t\t\tdst[cnt][0] = pass->rgbgen_func.args[0];\n\t\t\tdst[cnt][1] = pass->rgbgen_func.args[1];\n\t\t\tdst[cnt][2] = pass->rgbgen_func.args[2];\n\t\t}\n\t\tbreak;\n\tcase RGB_GEN_ENTITY_LIGHTING_DIFFUSE:\n\t\tR_LightArrays(shaderstate.curentity, mesh->xyz_array, dst, cnt, mesh->normals_array, shaderstate.identitylighting, true);\n\t\tbreak;\n\tcase RGB_GEN_LIGHTING_DIFFUSE:\n\t\tR_LightArrays(shaderstate.curentity, mesh->xyz_array, dst, cnt, mesh->normals_array, shaderstate.identitylighting, false);\n\t\tbreak;\n\tcase RGB_GEN_WAVE:\n\t\t{\n\t\t\tfloat *table;\n\t\t\tfloat c;\n\n\t\t\ttable = FTableForFunc(pass->rgbgen_func.type);\n\t\t\tc = pass->rgbgen_func.args[2] + shaderstate.curtime * pass->rgbgen_func.args[3];\n\t\t\tc = FTABLE_EVALUATE(table, c) * pass->rgbgen_func.args[1] + pass->rgbgen_func.args[0];\n\t\t\tc = bound(0.0f, c, 1.0f);\n\n\t\t\twhile((cnt)--)\n\t\t\t{\n\t\t\t\tdst[cnt][0] = c;\n\t\t\t\tdst[cnt][1] = c;\n\t\t\t\tdst[cnt][2] = c;\n\t\t\t}\n\t\t}\n\t\tbreak;\n\n\tcase RGB_GEN_TOPCOLOR:\n\t\tif (cnt)\n\t\t{\n\t\t\tvec3_t rgb;\n\t\t\tR_FetchPlayerColour(shaderstate.curentity->topcolour, rgb);\n\t\t\twhile((cnt)--)\n\t\t\t{\n\t\t\t\tdst[cnt][0] = rgb[0];\n\t\t\t\tdst[cnt][1] = rgb[1];\n\t\t\t\tdst[cnt][2] = rgb[2];\n\t\t\t}\n\t\t}\n\t\tbreak;\n\tcase RGB_GEN_BOTTOMCOLOR:\n\t\tif (cnt)\n\t\t{\n\t\t\tvec3_t rgb;\n\t\t\tR_FetchPlayerColour(shaderstate.curentity->bottomcolour, rgb);\n\t\t\twhile((cnt)--)\n\t\t\t{\n\t\t\t\tdst[cnt][0] = rgb[0];\n\t\t\t\tdst[cnt][1] = rgb[1];\n\t\t\t\tdst[cnt][2] = rgb[2];\n\t\t\t}\n\t\t}\n\t\tbreak;\n\t}\n}\nstatic void alphagen(const shaderpass_t *pass, int cnt, byte_vec4_t *srcb, avec4_t *srcf, avec4_t *dst, const mesh_t *mesh)\n{\n\tfloat *table;\n\tfloat t;\n\tfloat f;\n\tvec3_t v1, v2;\n\tint i;\n\n\tswitch (pass->alphagen)\n\t{\n\tdefault:\n\tcase ALPHA_GEN_IDENTITY:\n\t\tif (shaderstate.flags & BEF_FORCETRANSPARENT)\n\t\t{\n\t\t\twhile(cnt--)\n\t\t\t\tdst[cnt][3] = shaderstate.curentity->shaderRGBAf[3];\n\t\t}\n\t\telse\n\t\t{\n\t\t\twhile(cnt--)\n\t\t\t\tdst[cnt][3] = 1;\n\t\t}\n\t\tbreak;\n\n\tcase ALPHA_GEN_CONST:\n\t\tt = pass->alphagen_func.args[0];\n\t\twhile(cnt--)\n\t\t\tdst[cnt][3] = t;\n\t\tbreak;\n\n\tcase ALPHA_GEN_WAVE:\n\t\ttable = FTableForFunc(pass->alphagen_func.type);\n\t\tf = pass->alphagen_func.args[2] + shaderstate.curtime * pass->alphagen_func.args[3];\n\t\tf = FTABLE_EVALUATE(table, f) * pass->alphagen_func.args[1] + pass->alphagen_func.args[0];\n\t\tt = bound(0.0f, f, 1.0f);\n\t\twhile(cnt--)\n\t\t\tdst[cnt][3] = t;\n\t\tbreak;\n\n\tcase ALPHA_GEN_PORTAL:\n\t\t//FIXME: should this be per-vert?\n\t\tif (r_refdef.recurse)\n\t\t\tf = 1;\n\t\telse\n\t\t{\n\t\t\tVectorAdd(mesh->xyz_array[0], shaderstate.curentity->origin, v1);\n\t\t\tVectorSubtract(r_origin, v1, v2);\n\t\t\tf = VectorLength(v2) * (1.0 / shaderstate.curshader->portaldist);\n\t\t\tf = bound(0.0f, f, 1.0f);\n\t\t}\n\n\t\twhile(cnt--)\n\t\t\tdst[cnt][3] = f;\n\t\tbreak;\n\n\tcase ALPHA_GEN_VERTEX:\n\t\tif (srcf)\n\t\t{\n\t\t\twhile(cnt--)\n\t\t\t{\n\t\t\t\tdst[cnt][3] = srcf[cnt][3];\n\t\t\t}\n\t\t}\n\t\telse if (srcb)\n\t\t{\n\t\t\tfloat t = 1/255.0;\n\t\t\twhile(cnt--)\n\t\t\t{\n\t\t\t\tdst[cnt][3] = srcb[cnt][3]*t;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\twhile(cnt--)\n\t\t\t{\n\t\t\t\tdst[cnt][3] = 1;\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t\tbreak;\n\n\tcase ALPHA_GEN_ENTITY:\n\t\tf = bound(0, shaderstate.curentity->shaderRGBAf[3], 1);\n\t\twhile(cnt--)\n\t\t{\n\t\t\tdst[cnt][3] = f;\n\t\t}\n\t\tbreak;\n\n\n\tcase ALPHA_GEN_SPECULAR:\n\t\t{\n\t\t\tVectorSubtract(r_origin, shaderstate.curentity->origin, v1);\n\n\t\t\tif (!Matrix3_Compare((const vec3_t*)shaderstate.curentity->axis, (const vec3_t*)axisDefault))\n\t\t\t{\n\t\t\t\tMatrix3_Multiply_Vec3(shaderstate.curentity->axis, v1, v2);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tVectorCopy(v1, v2);\n\t\t\t}\n\n\t\t\tfor (i = 0; i < cnt; i++)\n\t\t\t{\n\t\t\t\tVectorSubtract(v2, mesh->xyz_array[i], v1);\n\t\t\t\tf = DotProduct(v1, mesh->normals_array[i] ) * Q_rsqrt(DotProduct(v1,v1));\n\t\t\t\tf = f * f * f * f * f;\n\t\t\t\tdst[i][3] = bound (0.0f, f, 1.0f);\n\t\t\t}\n\t\t}\n\t\tbreak;\n\t}\n}\n\n//true if we used an array (flag to use uniforms for it instead if false)\nstatic void BE_GenerateColourMods(unsigned int vertcount, const shaderpass_t *pass, VkBuffer *buffer, VkDeviceSize *offset)\n{\n\tconst mesh_t *m = shaderstate.meshlist[0];\n//\tif (pass->flags & SHADER_PASS_NOCOLORARRAY)\n//\t\terror\n\tif (\t\t\t\t\t   ((pass->rgbgen == RGB_GEN_VERTEX_LIGHTING) ||\n\t\t\t\t\t\t\t\t(pass->rgbgen == RGB_GEN_VERTEX_EXACT) ||\n\t\t\t\t\t\t\t\t(pass->rgbgen == RGB_GEN_ONE_MINUS_VERTEX)) &&\n\t\t\t\t\t\t\t\t(pass->alphagen == ALPHA_GEN_VERTEX))\n\t{\n\t\tif (shaderstate.batchvbo)\n\t\t{\t//just use the colour vbo provided\n\t\t\t*buffer = shaderstate.batchvbo->colours[0].vk.buff;\n\t\t\t*offset = shaderstate.batchvbo->colours[0].vk.offs;\n\t\t}\n\t\telse\n\t\t{\t//we can't use the vbo due to gaps that we don't want to have to deal with\n\t\t\t//we can at least ensure that the data is written in one go to aid cpu cache.\n\t\t\tvec4_t *fte_restrict map;\n\t\t\tunsigned int mno;\n\t\t\tmap = VKBE_AllocateStreamingSpace(DB_VBO, vertcount * sizeof(vec4_t), buffer, offset);\n\t\t\tif (m->colors4f_array[0])\n\t\t\t{\n\t\t\t\tfor (mno = 0; mno < shaderstate.nummeshes; mno++)\n\t\t\t\t{\n\t\t\t\t\tm = shaderstate.meshlist[mno];\n\t\t\t\t\tmemcpy(map, m->colors4f_array[0], m->numvertexes * sizeof(vec4_t));\n\t\t\t\t\tmap += m->numvertexes;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (m->colors4b_array)\n\t\t\t{\n\t\t\t\tfor (mno = 0; mno < shaderstate.nummeshes; mno++)\n\t\t\t\t{\n\t\t\t\t\tuint32_t v;\n\t\t\t\t\tm = shaderstate.meshlist[mno];\n\t\t\t\t\tfor (v = 0; v < m->numvertexes; v++)\n\t\t\t\t\t\tVector4Scale(m->colors4b_array[v], 1.0/255, map[v]);\n\t\t\t\t\tmap += m->numvertexes;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tfor (mno = 0; mno < vertcount; mno++)\n\t\t\t\t\tVector4Set(map[mno], 1, 1, 1, 1);\n\t\t\t}\n\t\t}\n\t}\n\telse\n\t{\n\t\tvec4_t *fte_restrict map;\n\t\tunsigned int mno;\n\t\tmap = VKBE_AllocateStreamingSpace(DB_VBO, vertcount * sizeof(vec4_t), buffer, offset);\n\t\tfor (mno = 0; mno < shaderstate.nummeshes; mno++)\n\t\t{\n\t\t\tm = shaderstate.meshlist[mno];\n\t\t\tcolourgen(pass, m->numvertexes, m->colors4b_array, m->colors4f_array[0], map, m);\n\t\t\talphagen(pass, m->numvertexes, m->colors4b_array, m->colors4f_array[0], map, m);\n\t\t\tmap += m->numvertexes;\n\t\t}\n\t}\n}\n\n/*********************************************************************************************************/\n/*========================================== texture coord generation =====================================*/\nstatic void tcgen_environment(float *st, unsigned int numverts, float *xyz, float *normal)\n{\n\tint\t\t\ti;\n\tvec3_t\t\tviewer, reflected;\n\tfloat\t\td;\n\n\tvec3_t\t\trorg;\n\n\tRotateLightVector(shaderstate.curentity->axis, shaderstate.curentity->origin, r_origin, rorg);\n\n\tfor (i = 0 ; i < numverts ; i++, xyz += sizeof(vecV_t)/sizeof(vec_t), normal += 3, st += 2 )\n\t{\n\t\tVectorSubtract (rorg, xyz, viewer);\n\t\tVectorNormalizeFast (viewer);\n\n\t\td = DotProduct (normal, viewer);\n\n\t\treflected[0] = normal[0]*2*d - viewer[0];\n\t\treflected[1] = normal[1]*2*d - viewer[1];\n\t\treflected[2] = normal[2]*2*d - viewer[2];\n\n\t\tst[0] = 0.5 + reflected[1] * 0.5;\n\t\tst[1] = 0.5 - reflected[2] * 0.5;\n\t}\n}\n\nstatic float *tcgen(const shaderpass_t *pass, int cnt, float *dst, const mesh_t *mesh)\n{\n\tint i;\n\tvecV_t *src;\n\tswitch (pass->tcgen)\n\t{\n\tdefault:\n\tcase TC_GEN_BASE:\n\t\treturn (float*)mesh->st_array;\n\tcase TC_GEN_LIGHTMAP:\n\t\treturn (float*)mesh->lmst_array[0];\n\tcase TC_GEN_NORMAL:\n\t\treturn (float*)mesh->normals_array;\n\tcase TC_GEN_SVECTOR:\n\t\treturn (float*)mesh->snormals_array;\n\tcase TC_GEN_TVECTOR:\n\t\treturn (float*)mesh->tnormals_array;\n\tcase TC_GEN_ENVIRONMENT:\n\t\tif (!mesh->normals_array)\n\t\t\treturn (float*)mesh->st_array;\n\t\ttcgen_environment(dst, cnt, (float*)mesh->xyz_array, (float*)mesh->normals_array);\n\t\treturn dst;\n\n\tcase TC_GEN_DOTPRODUCT:\n\t\treturn dst;//mesh->st_array[0];\n\tcase TC_GEN_VECTOR:\n\t\tsrc = mesh->xyz_array;\n\t\tfor (i = 0; i < cnt; i++, dst += 2)\n\t\t{\n\t\t\tdst[0] = DotProduct(pass->tcgenvec[0], src[i]);\n\t\t\tdst[1] = DotProduct(pass->tcgenvec[1], src[i]);\n\t\t}\n\t\treturn dst;\n\t}\n}\n\n/*src and dst can be the same address when tcmods are chained*/\nstatic void tcmod(const tcmod_t *tcmod, int cnt, const float *src, float *dst, const mesh_t *mesh)\n{\n\tfloat *table;\n\tfloat t1, t2;\n\tfloat cost, sint;\n\tint j;\n\n\tswitch (tcmod->type)\n\t{\n\t\tcase SHADER_TCMOD_ROTATE:\n\t\t\tcost = tcmod->args[0] * shaderstate.curtime;\n\t\t\tsint = R_FastSin(cost);\n\t\t\tcost = R_FastSin(cost + 0.25);\n\n\t\t\tfor (j = 0; j < cnt; j++, dst+=2,src+=2)\n\t\t\t{\n\t\t\t\tt1 = cost * (src[0] - 0.5f) - sint * (src[1] - 0.5f) + 0.5f;\n\t\t\t\tt2 = cost * (src[1] - 0.5f) + sint * (src[0] - 0.5f) + 0.5f;\n\t\t\t\tdst[0] = t1;\n\t\t\t\tdst[1] = t2;\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase SHADER_TCMOD_SCALE:\n\t\t\tt1 = tcmod->args[0];\n\t\t\tt2 = tcmod->args[1];\n\n\t\t\tfor (j = 0; j < cnt; j++, dst+=2,src+=2)\n\t\t\t{\n\t\t\t\tdst[0] = src[0] * t1;\n\t\t\t\tdst[1] = src[1] * t2;\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase SHADER_TCMOD_TURB:\n\t\t\tt1 = tcmod->args[2] + shaderstate.curtime * tcmod->args[3];\n\t\t\tt2 = tcmod->args[1];\n\n\t\t\tfor (j = 0; j < cnt; j++, dst+=2,src+=2)\n\t\t\t{\n\t\t\t\tdst[0] = src[0] + R_FastSin (src[0]*t2+t1) * t2;\n\t\t\t\tdst[1] = src[1] + R_FastSin (src[1]*t2+t1) * t2;\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase SHADER_TCMOD_STRETCH:\n\t\t\ttable = FTableForFunc(tcmod->args[0]);\n\t\t\tt2 = tcmod->args[3] + shaderstate.curtime * tcmod->args[4];\n\t\t\tt1 = FTABLE_EVALUATE(table, t2) * tcmod->args[2] + tcmod->args[1];\n\t\t\tt1 = t1 ? 1.0f / t1 : 1.0f;\n\t\t\tt2 = 0.5f - 0.5f * t1;\n\t\t\tfor (j = 0; j < cnt; j++, dst+=2,src+=2)\n\t\t\t{\n\t\t\t\tdst[0] = src[0] * t1 + t2;\n\t\t\t\tdst[1] = src[1] * t1 + t2;\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase SHADER_TCMOD_SCROLL:\n\t\t\tt1 = tcmod->args[0] * shaderstate.curtime;\n\t\t\tt2 = tcmod->args[1] * shaderstate.curtime;\n\n\t\t\tfor (j = 0; j < cnt; j++, dst += 2, src+=2)\n\t\t\t{\n\t\t\t\tdst[0] = src[0] + t1;\n\t\t\t\tdst[1] = src[1] + t2;\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase SHADER_TCMOD_TRANSFORM:\n\t\t\tfor (j = 0; j < cnt; j++, dst+=2, src+=2)\n\t\t\t{\n\t\t\t\tt1 = src[0];\n\t\t\t\tt2 = src[1];\n\t\t\t\tdst[0] = t1 * tcmod->args[0] + t2 * tcmod->args[2] + tcmod->args[4];\n\t\t\t\tdst[1] = t1 * tcmod->args[1] + t1 * tcmod->args[3] + tcmod->args[5];\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase SHADER_TCMOD_PAGE:\n\t\tdefault:\n\t\t\tfor (j = 0; j < cnt; j++, dst += 2, src+=2)\n\t\t\t{\n\t\t\t\tdst[0] = src[0];\n\t\t\t\tdst[1] = src[1];\n\t\t\t}\n\t\t\tbreak;\n\t}\n}\n\nstatic void BE_GenerateTCMods(const shaderpass_t *pass, float *dest)\n{\n\tmesh_t *mesh;\n\tunsigned int mno;\n\tint i;\n\tfloat *src;\n\tfor (mno = 0; mno < shaderstate.nummeshes; mno++)\n\t{\n\t\tmesh = shaderstate.meshlist[mno];\n\t\tsrc = tcgen(pass, mesh->numvertexes, dest, mesh);\n\t\t//tcgen might return unmodified info\n\t\tif (pass->numtcmods)\n\t\t{\n\t\t\ttcmod(&pass->tcmods[0], mesh->numvertexes, src, dest, mesh);\n\t\t\tfor (i = 1; i < pass->numtcmods; i++)\n\t\t\t{\n\t\t\t\ttcmod(&pass->tcmods[i], mesh->numvertexes, dest, dest, mesh);\n\t\t\t}\n\t\t}\n\t\telse if (src != dest)\n\t\t{\n\t\t\tmemcpy(dest, src, sizeof(vec2_t)*mesh->numvertexes);\n\t\t}\n\t\tdest += mesh->numvertexes*2;\n\t}\n}\n\n//end texture coords\n/*******************************************************************************************************************/\nstatic void deformgen(const deformv_t *deformv, int cnt, vecV_t *src, vecV_t *dst, const mesh_t *mesh)\n{\n\tfloat *table;\n\tint j, k;\n\tfloat args[4];\n\tfloat deflect;\n\tswitch (deformv->type)\n\t{\n\tdefault:\n\tcase DEFORMV_NONE:\n\t\tif (src != dst)\n\t\t\tmemcpy(dst, src, sizeof(*src)*cnt);\n\t\tbreak;\n\n\tcase DEFORMV_WAVE:\n\t\tif (!mesh->normals_array)\n\t\t{\n\t\t\tif (src != dst)\n\t\t\t\tmemcpy(dst, src, sizeof(*src)*cnt);\n\t\t\treturn;\n\t\t}\n\t\targs[0] = deformv->func.args[0];\n\t\targs[1] = deformv->func.args[1];\n\t\targs[3] = deformv->func.args[2] + deformv->func.args[3] * shaderstate.curtime;\n\t\ttable = FTableForFunc(deformv->func.type);\n\n\t\tfor ( j = 0; j < cnt; j++ )\n\t\t{\n\t\t\tdeflect = deformv->args[0] * (src[j][0]+src[j][1]+src[j][2]) + args[3];\n\t\t\tdeflect = FTABLE_EVALUATE(table, deflect) * args[1] + args[0];\n\n\t\t\t// Deflect vertex along its normal by wave amount\n\t\t\tVectorMA(src[j], deflect, mesh->normals_array[j], dst[j]);\n\t\t}\n\t\tbreak;\n\n\tcase DEFORMV_NORMAL:\n\t\t//normal does not actually move the verts, but it does change the normals array\n\t\t//we don't currently support that.\n\t\tif (src != dst)\n\t\t\tmemcpy(dst, src, sizeof(*src)*cnt);\n/*\n\t\targs[0] = deformv->args[1] * shaderstate.curtime;\n\n\t\tfor ( j = 0; j < cnt; j++ )\n\t\t{\n\t\t\targs[1] = normalsArray[j][2] * args[0];\n\n\t\t\tdeflect = deformv->args[0] * R_FastSin(args[1]);\n\t\t\tnormalsArray[j][0] *= deflect;\n\t\t\tdeflect = deformv->args[0] * R_FastSin(args[1] + 0.25);\n\t\t\tnormalsArray[j][1] *= deflect;\n\t\t\tVectorNormalizeFast(normalsArray[j]);\n\t\t}\n*/\t\tbreak;\n\n\tcase DEFORMV_MOVE:\n\t\ttable = FTableForFunc(deformv->func.type);\n\t\tdeflect = deformv->func.args[2] + shaderstate.curtime * deformv->func.args[3];\n\t\tdeflect = FTABLE_EVALUATE(table, deflect) * deformv->func.args[1] + deformv->func.args[0];\n\n\t\tfor ( j = 0; j < cnt; j++ )\n\t\t\tVectorMA(src[j], deflect, deformv->args, dst[j]);\n\t\tbreak;\n\n\tcase DEFORMV_BULGE:\n\t\targs[0] = deformv->args[0]/(2*M_PI);\n\t\targs[1] = deformv->args[1];\n\t\targs[2] = shaderstate.curtime * deformv->args[2]/(2*M_PI);\n\n\t\tfor (j = 0; j < cnt; j++)\n\t\t{\n\t\t\tdeflect = R_FastSin(mesh->st_array[j][0]*args[0] + args[2])*args[1];\n\t\t\tdst[j][0] = src[j][0]+deflect*mesh->normals_array[j][0];\n\t\t\tdst[j][1] = src[j][1]+deflect*mesh->normals_array[j][1];\n\t\t\tdst[j][2] = src[j][2]+deflect*mesh->normals_array[j][2];\n\t\t}\n\t\tbreak;\n\n\tcase DEFORMV_AUTOSPRITE:\n\t\tif (mesh->numindexes < 6)\n\t\t\tbreak;\n\n\t\tfor (j = 0; j < cnt-3; j+=4, src+=4, dst+=4)\n\t\t{\n\t\t\tvec3_t mid, d;\n\t\t\tfloat radius;\n\t\t\tmid[0] = 0.25*(src[0][0] + src[1][0] + src[2][0] + src[3][0]);\n\t\t\tmid[1] = 0.25*(src[0][1] + src[1][1] + src[2][1] + src[3][1]);\n\t\t\tmid[2] = 0.25*(src[0][2] + src[1][2] + src[2][2] + src[3][2]);\n\t\t\tVectorSubtract(src[0], mid, d);\n\t\t\tradius = 2*VectorLength(d);\n\n\t\t\tfor (k = 0; k < 4; k++)\n\t\t\t{\n\t\t\t\tdst[k][0] = mid[0] + radius*((mesh->st_array[j+k][0]-0.5)*r_refdef.m_view[0+0]-(mesh->st_array[j+k][1]-0.5)*r_refdef.m_view[0+1]);\n\t\t\t\tdst[k][1] = mid[1] + radius*((mesh->st_array[j+k][0]-0.5)*r_refdef.m_view[4+0]-(mesh->st_array[j+k][1]-0.5)*r_refdef.m_view[4+1]);\n\t\t\t\tdst[k][2] = mid[2] + radius*((mesh->st_array[j+k][0]-0.5)*r_refdef.m_view[8+0]-(mesh->st_array[j+k][1]-0.5)*r_refdef.m_view[8+1]);\n\t\t\t}\n\t\t}\n\t\tbreak;\n\n\tcase DEFORMV_AUTOSPRITE2:\n\t\tif (mesh->numindexes < 6)\n\t\t\tbreak;\n\n\t\tfor (k = 0; k < mesh->numindexes; k += 6)\n\t\t{\n\t\t\tint long_axis, short_axis;\n\t\t\tvec3_t axis;\n\t\t\tfloat len[3];\n\t\t\tmat3_t m0, m1, m2, result;\n\t\t\tfloat *quad[4];\n\t\t\tvec3_t rot_centre, tv, tv2;\n\n\t\t\tquad[0] = (float *)(src + mesh->indexes[k+0]);\n\t\t\tquad[1] = (float *)(src + mesh->indexes[k+1]);\n\t\t\tquad[2] = (float *)(src + mesh->indexes[k+2]);\n\n\t\t\tfor (j = 2; j >= 0; j--)\n\t\t\t{\n\t\t\t\tquad[3] = (float *)(src + mesh->indexes[k+3+j]);\n\t\t\t\tif (!VectorEquals (quad[3], quad[0]) &&\n\t\t\t\t\t!VectorEquals (quad[3], quad[1]) &&\n\t\t\t\t\t!VectorEquals (quad[3], quad[2]))\n\t\t\t\t{\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// build a matrix were the longest axis of the billboard is the Y-Axis\n\t\t\tVectorSubtract(quad[1], quad[0], m0[0]);\n\t\t\tVectorSubtract(quad[2], quad[0], m0[1]);\n\t\t\tVectorSubtract(quad[2], quad[1], m0[2]);\n\t\t\tlen[0] = DotProduct(m0[0], m0[0]);\n\t\t\tlen[1] = DotProduct(m0[1], m0[1]);\n\t\t\tlen[2] = DotProduct(m0[2], m0[2]);\n\n\t\t\tif ((len[2] > len[1]) && (len[2] > len[0]))\n\t\t\t{\n\t\t\t\tif (len[1] > len[0])\n\t\t\t\t{\n\t\t\t\t\tlong_axis = 1;\n\t\t\t\t\tshort_axis = 0;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tlong_axis = 0;\n\t\t\t\t\tshort_axis = 1;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if ((len[1] > len[2]) && (len[1] > len[0]))\n\t\t\t{\n\t\t\t\tif (len[2] > len[0])\n\t\t\t\t{\n\t\t\t\t\tlong_axis = 2;\n\t\t\t\t\tshort_axis = 0;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tlong_axis = 0;\n\t\t\t\t\tshort_axis = 2;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse //if ( (len[0] > len[1]) && (len[0] > len[2]) )\n\t\t\t{\n\t\t\t\tif (len[2] > len[1])\n\t\t\t\t{\n\t\t\t\t\tlong_axis = 2;\n\t\t\t\t\tshort_axis = 1;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tlong_axis = 1;\n\t\t\t\t\tshort_axis = 2;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (DotProduct(m0[long_axis], m0[short_axis]))\n\t\t\t{\n\t\t\t\tVectorNormalize2(m0[long_axis], axis);\n\t\t\t\tVectorCopy(axis, m0[1]);\n\n\t\t\t\tif (axis[0] || axis[1])\n\t\t\t\t{\n\t\t\t\t\tVectorVectors(m0[1], m0[2], m0[0]);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tVectorVectors(m0[1], m0[0], m0[2]);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tVectorNormalize2(m0[long_axis], axis);\n\t\t\t\tVectorNormalize2(m0[short_axis], m0[0]);\n\t\t\t\tVectorCopy(axis, m0[1]);\n\t\t\t\tCrossProduct(m0[0], m0[1], m0[2]);\n\t\t\t}\n\n\t\t\tfor (j = 0; j < 3; j++)\n\t\t\t\trot_centre[j] = (quad[0][j] + quad[1][j] + quad[2][j] + quad[3][j]) * 0.25;\n\n\t\t\tif (shaderstate.curentity)\n\t\t\t{\n\t\t\t\tVectorAdd(shaderstate.curentity->origin, rot_centre, tv);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tVectorCopy(rot_centre, tv);\n\t\t\t}\n\t\t\tVectorSubtract(r_origin, tv, tv);\n\n\t\t\t// filter any longest-axis-parts off the camera-direction\n\t\t\tdeflect = -DotProduct(tv, axis);\n\n\t\t\tVectorMA(tv, deflect, axis, m1[2]);\n\t\t\tVectorNormalizeFast(m1[2]);\n\t\t\tVectorCopy(axis, m1[1]);\n\t\t\tCrossProduct(m1[1], m1[2], m1[0]);\n\n\t\t\tMatrix3_Transpose(m1, m2);\n\t\t\tMatrix3_Multiply(m2, m0, result);\n\n\t\t\tfor (j = 0; j < 4; j++)\n\t\t\t{\n\t\t\t\tint v = ((vecV_t*)quad[j]-src);\n\t\t\t\tVectorSubtract(quad[j], rot_centre, tv);\n\t\t\t\tMatrix3_Multiply_Vec3((void *)result, tv, tv2);\n\t\t\t\tVectorAdd(rot_centre, tv2, dst[v]);\n\t\t\t}\n\t\t}\n\t\tbreak;\n\n//\tcase DEFORMV_PROJECTION_SHADOW:\n//\t\tbreak;\n\t}\n}\n\nstatic void BE_CreatePipeline(program_t *p, unsigned int shaderflags, unsigned int blendflags, unsigned int permu)\n{\n\tstruct pipeline_s *pipe;\n\tVkDynamicState dynamicStateEnables[2]={0};\n\tVkPipelineDynamicStateCreateInfo dyn = {VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO};\n\tVkVertexInputBindingDescription vbinds[VK_BUFF_MAX] = {{0}};\n\tVkVertexInputAttributeDescription vattrs[VK_BUFF_MAX] = {{0}};\n\tVkPipelineVertexInputStateCreateInfo vi = {VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO};\n\tVkPipelineInputAssemblyStateCreateInfo ia = {VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO};\n\tVkPipelineViewportStateCreateInfo vp = {VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO};\n\tVkPipelineRasterizationStateCreateInfo rs = {VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO};\n\tVkPipelineMultisampleStateCreateInfo ms = {VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO};\n\tVkPipelineDepthStencilStateCreateInfo ds = {VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO};\n\tVkPipelineColorBlendStateCreateInfo cb = {VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO};\n\tVkPipelineColorBlendAttachmentState att_state[1];\n\tVkGraphicsPipelineCreateInfo pipeCreateInfo = {VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO};\n\tVkPipelineShaderStageCreateInfo shaderStages[2] = {{0}};\n\tVkPipelineRasterizationStateRasterizationOrderAMD ro = {VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_RASTERIZATION_ORDER_AMD};\t//long enough names for you?\n#ifdef VK_KHR_fragment_shading_rate\n\tVkPipelineFragmentShadingRateStateCreateInfoKHR shadingrate = {VK_STRUCTURE_TYPE_PIPELINE_FRAGMENT_SHADING_RATE_STATE_CREATE_INFO_KHR};\n#endif\n\tstruct specdata_s\n\t{\n\t\tint alphamode;\n\t\tint permu[16];\n\t\tunion\n\t\t{\n\t\t\tfloat f;\n\t\t\tint i;\n\t\t} cvars[64];\n\t} specdata;\n\tVkSpecializationMapEntry specentries[256] = {{0}};\n\tVkSpecializationInfo specInfo = {0}, *bugsbeware;\n\tVkResult err;\n\tuint32_t i, s;\n\tunsigned char *cvardata;\n\n\tif (!p->vert || !p->frag)\n\t\tSys_Error(\"program missing required shader\\n\");\t//PANIC\n\n\n\tpipe = Z_Malloc(sizeof(*pipe));\n\tif (!p->pipelines)\n\t\tp->pipelines = pipe;\n\telse\n\t{\t//insert at end. if it took us a while to realise that we needed it, chances are its not that common.\n\t\t//so don't cause the other pipelines to waste cycles for it.\n\t\tstruct pipeline_s *prev;\n\t\tfor (prev = p->pipelines; ; prev = prev->next)\n\t\t\tif (!prev->next)\n\t\t\t\tbreak;\n\t\tprev->next = pipe;\n\t}\n\n\tpipe->flags = shaderflags;\n\tpipe->blendbits = blendflags;\n\tpipe->permu = permu;\n\n\tif (permu&PERMUTATION_BEM_WIREFRAME)\n\t{\n\t\tblendflags |= SBITS_MISC_NODEPTHTEST;\n\t\tblendflags &= ~SBITS_MISC_DEPTHWRITE;\n\n\t\tblendflags &= ~(SHADER_CULL_FRONT|SHADER_CULL_BACK);\n\t}\n\n\tdyn.flags = 0;\n\tdyn.dynamicStateCount = 0;\n\tdyn.pDynamicStates = dynamicStateEnables;\n\n\t//it wasn't supposed to be like this!\n\t//this stuff gets messy with tcmods and rgbgen/alphagen stuff\n\tvbinds[VK_BUFF_POS].binding = VK_BUFF_POS;\n\tvbinds[VK_BUFF_POS].stride = sizeof(vecV_t);\n\tvbinds[VK_BUFF_POS].inputRate = VK_VERTEX_INPUT_RATE_VERTEX;\n\tvattrs[VK_BUFF_POS].binding = vbinds[VK_BUFF_POS].binding;\n\tvattrs[VK_BUFF_POS].location = VK_BUFF_POS;\n\tvattrs[VK_BUFF_POS].format = VK_FORMAT_R32G32B32_SFLOAT;\n\tvattrs[VK_BUFF_POS].offset = 0;\n\tvbinds[VK_BUFF_TC].binding = VK_BUFF_TC;\n\tvbinds[VK_BUFF_TC].stride = sizeof(vec2_t);\n\tvbinds[VK_BUFF_TC].inputRate = VK_VERTEX_INPUT_RATE_VERTEX;\n\tvattrs[VK_BUFF_TC].binding = vbinds[VK_BUFF_TC].binding;\n\tvattrs[VK_BUFF_TC].location = VK_BUFF_TC;\n\tvattrs[VK_BUFF_TC].format = VK_FORMAT_R32G32_SFLOAT;\n\tvattrs[VK_BUFF_TC].offset = 0;\n\tvbinds[VK_BUFF_COL].binding = VK_BUFF_COL;\n\tvbinds[VK_BUFF_COL].stride = sizeof(vec4_t);\n\tvbinds[VK_BUFF_COL].inputRate = VK_VERTEX_INPUT_RATE_VERTEX;\n\tvattrs[VK_BUFF_COL].binding = vbinds[VK_BUFF_COL].binding;\n\tvattrs[VK_BUFF_COL].location = VK_BUFF_COL;\n\tvattrs[VK_BUFF_COL].format = VK_FORMAT_R32G32B32A32_SFLOAT;\n\tvattrs[VK_BUFF_COL].offset = 0;\n\tvbinds[VK_BUFF_LMTC].binding = VK_BUFF_LMTC;\n\tvbinds[VK_BUFF_LMTC].stride = sizeof(vec2_t);\n\tvbinds[VK_BUFF_LMTC].inputRate = VK_VERTEX_INPUT_RATE_VERTEX;\n\tvattrs[VK_BUFF_LMTC].binding = vbinds[VK_BUFF_LMTC].binding;\n\tvattrs[VK_BUFF_LMTC].location = VK_BUFF_LMTC;\n\tvattrs[VK_BUFF_LMTC].format = VK_FORMAT_R32G32_SFLOAT;\n\tvattrs[VK_BUFF_LMTC].offset = 0;\n\n\t//fixme: in all seriousness, why is this not a single buffer?\n\tvbinds[VK_BUFF_NORM].binding = VK_BUFF_NORM;\n\tvbinds[VK_BUFF_NORM].stride = sizeof(vec3_t);\n\tvbinds[VK_BUFF_NORM].inputRate = VK_VERTEX_INPUT_RATE_VERTEX;\n\tvattrs[VK_BUFF_NORM].binding = vbinds[VK_BUFF_NORM].binding;\n\tvattrs[VK_BUFF_NORM].location = VK_BUFF_NORM;\n\tvattrs[VK_BUFF_NORM].format = VK_FORMAT_R32G32B32_SFLOAT;\n\tvattrs[VK_BUFF_NORM].offset = 0;\n\tvbinds[VK_BUFF_SDIR].binding = VK_BUFF_SDIR;\n\tvbinds[VK_BUFF_SDIR].stride = sizeof(vec3_t);\n\tvbinds[VK_BUFF_SDIR].inputRate = VK_VERTEX_INPUT_RATE_VERTEX;\n\tvattrs[VK_BUFF_SDIR].binding = vbinds[VK_BUFF_SDIR].binding;\n\tvattrs[VK_BUFF_SDIR].location = VK_BUFF_SDIR;\n\tvattrs[VK_BUFF_SDIR].format = VK_FORMAT_R32G32B32_SFLOAT;\n\tvattrs[VK_BUFF_SDIR].offset = 0;\n\tvbinds[VK_BUFF_TDIR].binding = VK_BUFF_TDIR;\n\tvbinds[VK_BUFF_TDIR].stride = sizeof(vec3_t);\n\tvbinds[VK_BUFF_TDIR].inputRate = VK_VERTEX_INPUT_RATE_VERTEX;\n\tvattrs[VK_BUFF_TDIR].binding = vbinds[VK_BUFF_TDIR].binding;\n\tvattrs[VK_BUFF_TDIR].location = VK_BUFF_TDIR;\n\tvattrs[VK_BUFF_TDIR].format = VK_FORMAT_R32G32B32_SFLOAT;\n\tvattrs[VK_BUFF_TDIR].offset = 0;\n\n\tvi.vertexBindingDescriptionCount = countof(vbinds);\n\tvi.pVertexBindingDescriptions = vbinds;\n\tvi.vertexAttributeDescriptionCount = countof(vattrs);\n\tvi.pVertexAttributeDescriptions = vattrs;\n\n\tia.topology = (blendflags&SBITS_LINES)?VK_PRIMITIVE_TOPOLOGY_LINE_LIST:VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;\n\tvp.viewportCount = 1;\n\tdynamicStateEnables[dyn.dynamicStateCount++] =\tVK_DYNAMIC_STATE_VIEWPORT;\n\tvp.scissorCount = 1;\n\tdynamicStateEnables[dyn.dynamicStateCount++] =\tVK_DYNAMIC_STATE_SCISSOR;\n\t//FIXME: fillModeNonSolid might mean mode_line is not supported.\n\trs.polygonMode = (permu&PERMUTATION_BEM_WIREFRAME)?VK_POLYGON_MODE_LINE:VK_POLYGON_MODE_FILL;\n\trs.lineWidth = 1;\n\trs.cullMode = ((shaderflags&SHADER_CULL_FRONT)?VK_CULL_MODE_FRONT_BIT:0) | ((shaderflags&SHADER_CULL_BACK)?VK_CULL_MODE_BACK_BIT:0);\n\trs.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;\n\trs.depthClampEnable = VK_FALSE;\n\trs.rasterizerDiscardEnable = VK_FALSE;\n\tif (shaderflags & SHADER_POLYGONOFFSET)\n\t{\n\t\trs.depthBiasEnable = VK_TRUE;\n\t\trs.depthBiasConstantFactor = -25;//shader->polyoffset.unit;\n\t\trs.depthBiasClamp = 0;\n\t\trs.depthBiasSlopeFactor = -0.05;//shader->polyoffset.factor;\n\t}\n\telse\n\t\trs.depthBiasEnable = VK_FALSE;\n\n\tif (vk.amd_rasterization_order)\n\t{\n\t\tunsigned int b = blendflags & SBITS_BLEND_BITS;\n\t\t//we potentially allow a little z-fighting if they're equal. a single batch shouldn't really have such primitives.\n\t\t//must be no blending, or additive blending.\n\t\tswitch(blendflags & SBITS_DEPTHFUNC_BITS)\n\t\t{\n\t\tcase SBITS_DEPTHFUNC_EQUAL:\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tif ((blendflags&(SBITS_MISC_NODEPTHTEST|SBITS_MISC_DEPTHWRITE)) == SBITS_MISC_DEPTHWRITE &&\n\t\t\t\t(!b || b == (SBITS_SRCBLEND_ONE|SBITS_DSTBLEND_ZERO) || b == SBITS_DSTBLEND_ONE))\n\t\t\t{\n\t\t\t\trs.pNext = &ro;\n\t\t\t\tro.rasterizationOrder = VK_RASTERIZATION_ORDER_RELAXED_AMD;\n\t\t\t}\n\t\t}\n\t}\n\n\tms.pSampleMask = NULL;\n\tif (permu & PERMUTATION_BEM_MULTISAMPLE)\n\t\tms.rasterizationSamples = vk.multisamplebits;\n\telse\n\t\tms.rasterizationSamples = 1;\n//\tms.sampleShadingEnable = VK_TRUE;\t//call the fragment shader multiple times, instead of just once per final pixel\n//\tms.minSampleShading = 0.25;\n\tds.depthTestEnable = (blendflags&SBITS_MISC_NODEPTHTEST)?VK_FALSE:VK_TRUE;\n\tds.depthWriteEnable = (blendflags&SBITS_MISC_DEPTHWRITE)?VK_TRUE:VK_FALSE;\n\tswitch(blendflags & SBITS_DEPTHFUNC_BITS)\n\t{\n\tdefault:\n\tcase SBITS_DEPTHFUNC_CLOSEREQUAL:\tds.depthCompareOp = VK_COMPARE_OP_LESS_OR_EQUAL;\tbreak;\n\tcase SBITS_DEPTHFUNC_EQUAL:\t\t\tds.depthCompareOp = VK_COMPARE_OP_EQUAL;\t\t\tbreak;\n\tcase SBITS_DEPTHFUNC_CLOSER:\t\tds.depthCompareOp = VK_COMPARE_OP_LESS;\t\t\t\tbreak;\n\tcase SBITS_DEPTHFUNC_FURTHER:\t\tds.depthCompareOp = VK_COMPARE_OP_GREATER;\t\t\tbreak;\n\t}\n\tds.depthBoundsTestEnable = VK_FALSE;\n\tds.back.failOp = VK_STENCIL_OP_KEEP;\n\tds.back.passOp = VK_STENCIL_OP_KEEP;\n\tds.back.compareOp = VK_COMPARE_OP_NEVER;//VK_COMPARE_OP_ALWAYS;\n\tds.stencilTestEnable = VK_FALSE;\n\tds.front = ds.back;\n\tmemset(att_state, 0, sizeof(att_state));\n\tatt_state[0].colorWriteMask =\n\t\t((blendflags&SBITS_MASK_RED)?0:VK_COLOR_COMPONENT_R_BIT) |\n\t\t((blendflags&SBITS_MASK_GREEN)?0:VK_COLOR_COMPONENT_G_BIT) |\n\t\t((blendflags&SBITS_MASK_BLUE)?0:VK_COLOR_COMPONENT_B_BIT) |\n\t\t((blendflags&SBITS_MASK_ALPHA)?0:VK_COLOR_COMPONENT_A_BIT);\n\n\tif ((blendflags & SBITS_BLEND_BITS) && (blendflags & SBITS_BLEND_BITS)!=(SBITS_SRCBLEND_ONE|SBITS_DSTBLEND_ZERO))\n\t{\n\t\tswitch(blendflags & SBITS_SRCBLEND_BITS)\n\t\t{\n\t\tcase SBITS_SRCBLEND_ZERO:\t\t\t\t\tatt_state[0].srcColorBlendFactor = VK_BLEND_FACTOR_ZERO;\t\t\t\tatt_state[0].srcAlphaBlendFactor = VK_BLEND_FACTOR_ZERO;\t\t\t\tbreak;\n\t\tcase SBITS_SRCBLEND_ONE:\t\t\t\t\tatt_state[0].srcColorBlendFactor = VK_BLEND_FACTOR_ONE;\t\t\t\t\tatt_state[0].srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE;\t\t\t\t\tbreak;\n\t\tcase SBITS_SRCBLEND_DST_COLOR:\t\t\t\tatt_state[0].srcColorBlendFactor = VK_BLEND_FACTOR_DST_COLOR;\t\t\tatt_state[0].srcAlphaBlendFactor = VK_BLEND_FACTOR_DST_ALPHA;\t\t\tbreak;\n\t\tcase SBITS_SRCBLEND_ONE_MINUS_DST_COLOR:\tatt_state[0].srcColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_DST_COLOR;\tatt_state[0].srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA;\tbreak;\n\t\tcase SBITS_SRCBLEND_SRC_ALPHA:\t\t\t\tatt_state[0].srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;\t\t\tatt_state[0].srcAlphaBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;\t\t\tbreak;\n\t\tcase SBITS_SRCBLEND_ONE_MINUS_SRC_ALPHA:\tatt_state[0].srcColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;\tatt_state[0].srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;\tbreak;\n\t\tcase SBITS_SRCBLEND_DST_ALPHA:\t\t\t\tatt_state[0].srcColorBlendFactor = VK_BLEND_FACTOR_DST_ALPHA;\t\t\tatt_state[0].srcAlphaBlendFactor = VK_BLEND_FACTOR_DST_ALPHA;\t\t\tbreak;\n\t\tcase SBITS_SRCBLEND_ONE_MINUS_DST_ALPHA:\tatt_state[0].srcColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA;\tatt_state[0].srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA;\tbreak;\n\t\tcase SBITS_SRCBLEND_ALPHA_SATURATE:\t\t\tatt_state[0].srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA_SATURATE;\tatt_state[0].srcAlphaBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA_SATURATE;\tbreak;\n\t\tdefault:\tSys_Error(\"Bad shader blend src\\n\"); return;\n\t\t}\n\t\tswitch(blendflags & SBITS_DSTBLEND_BITS)\n\t\t{\n\t\tcase SBITS_DSTBLEND_ZERO:\t\t\t\t\tatt_state[0].dstColorBlendFactor = VK_BLEND_FACTOR_ZERO;\t\t\t\tatt_state[0].dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO;\t\t\t\tbreak;\n\t\tcase SBITS_DSTBLEND_ONE:\t\t\t\t\tatt_state[0].dstColorBlendFactor = VK_BLEND_FACTOR_ONE;\t\t\t\t\tatt_state[0].dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE;\t\t\t\t\tbreak;\n\t\tcase SBITS_DSTBLEND_SRC_ALPHA:\t\t\t\tatt_state[0].dstColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;\t\t\tatt_state[0].dstAlphaBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;\t\t\tbreak;\n\t\tcase SBITS_DSTBLEND_ONE_MINUS_SRC_ALPHA:\tatt_state[0].dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;\tatt_state[0].dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;\tbreak;\n\t\tcase SBITS_DSTBLEND_DST_ALPHA:\t\t\t\tatt_state[0].dstColorBlendFactor = VK_BLEND_FACTOR_DST_ALPHA;\t\t\tatt_state[0].dstAlphaBlendFactor = VK_BLEND_FACTOR_DST_ALPHA;\t\t\tbreak;\n\t\tcase SBITS_DSTBLEND_ONE_MINUS_DST_ALPHA:\tatt_state[0].dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA;\tatt_state[0].dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA;\tbreak;\n\t\tcase SBITS_DSTBLEND_SRC_COLOR:\t\t\t\tatt_state[0].dstColorBlendFactor = VK_BLEND_FACTOR_SRC_COLOR;\t\t\tatt_state[0].dstAlphaBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;\t\t\tbreak;\n\t\tcase SBITS_DSTBLEND_ONE_MINUS_SRC_COLOR:\tatt_state[0].dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_COLOR;\tatt_state[0].dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;\tbreak;\n\t\tdefault:\tSys_Error(\"Bad shader blend dst\\n\"); return;\n\t\t}\n\t\tatt_state[0].colorBlendOp = VK_BLEND_OP_ADD;\n\t\tatt_state[0].alphaBlendOp = VK_BLEND_OP_ADD;\n\t\tatt_state[0].blendEnable = VK_TRUE;\n\t}\n\telse\n\t{\n\t\tatt_state[0].blendEnable = VK_FALSE;\n\t}\n\tif (permu&PERMUTATION_BEM_DEPTHONLY)\n\t\tcb.attachmentCount = 0;\n\telse\n\t\tcb.attachmentCount = 1;\n\tcb.pAttachments = att_state;\n\n\n\ts = 0;\n\tspecentries[s].constantID = 0;\n\tspecentries[s].offset = offsetof(struct specdata_s, alphamode);\n\tspecentries[s].size = sizeof(specdata.alphamode);\n\ts++;\n\tif (blendflags & SBITS_ATEST_GE128)\n\t\tspecdata.alphamode = 3;\n\telse if (blendflags & SBITS_ATEST_GT0)\n\t\tspecdata.alphamode = 2;\n\telse if (blendflags & SBITS_ATEST_LT128)\n\t\tspecdata.alphamode = 1;\n\telse //if (blendflags & SBITS_ATEST_NONE)\n\t\tspecdata.alphamode = 0;\n\n\tfor (i = 0; i < countof(specdata.permu); i++)\n\t{\n\t\tspecentries[s].constantID = 16+i;\n\t\tspecentries[s].offset = offsetof(struct specdata_s, permu[i]);\n\t\tspecentries[s].size = sizeof(specdata.permu[i]);\n\t\ts++;\n\t\tspecdata.permu[i] = !!(permu & (1u<<i));\n\t}\n\n\t//cvars\n\tfor (cvardata = p->cvardata, i = 0; cvardata < p->cvardata + p->cvardatasize; )\n\t{\n\t\tunsigned short id = (cvardata[0]<<8)|cvardata[1];\n\t\tunsigned char type = cvardata[2], size = cvardata[3]-'0';\n\t\tchar *name;\n\t\tcvar_t *var;\n\t\tunsigned int u;\n\n\t\tcvardata += 4;\n\t\tname = cvardata;\n\t\tcvardata += strlen(name)+1;\n\n\t\tif (i + size > countof(specdata.cvars))\n\t\t\tbreak;\t//error\n\n\t\tif (type >= 'A' && type <= 'Z')\n\t\t{\t//args will be handled by the blob loader.\n\t\t\tfor (u = 0; u < size && u < 4; u++)\n\t\t\t{\n\t\t\t\tspecentries[s].constantID = id;\n\t\t\t\tspecentries[s].offset = offsetof(struct specdata_s, cvars[i]);\n\t\t\t\tspecentries[s].size = sizeof(specdata.cvars[i]);\n\n\t\t\t\tspecdata.cvars[i].i = (cvardata[u*4+0]<<24)|(cvardata[u*4+1]<<16)|(cvardata[u*4+2]<<8)|(cvardata[u*4+3]<<0);\n\t\t\t\ts++;\n\t\t\t\ti++;\n\t\t\t\tid++;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tvar = Cvar_FindVar(name);\n\t\t\tif (var)\n\t\t\t{\n\t\t\t\tfor (u = 0; u < size && u < 4; u++)\n\t\t\t\t{\n\t\t\t\t\tspecentries[s].constantID = id;\n\t\t\t\t\tspecentries[s].offset = offsetof(struct specdata_s, cvars[i]);\n\t\t\t\t\tspecentries[s].size = sizeof(specdata.cvars[i]);\n\n\t\t\t\t\tif (type == 'i')\n\t\t\t\t\t\tspecdata.cvars[i].i = var->ival;\n\t\t\t\t\telse\n\t\t\t\t\t\tspecdata.cvars[i].f = var->vec4[u];\n\t\t\t\t\ts++;\n\t\t\t\t\ti++;\n\t\t\t\t\tid++;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tcvardata += 4*size;\n\t}\n\n\tspecInfo.mapEntryCount = s;\n\tspecInfo.pMapEntries = specentries;\n\tspecInfo.dataSize = sizeof(specdata);\n\tspecInfo.pData = &specdata;\n\n#if 0//def _DEBUG\n\t//vk_layer_lunarg_drawstate fucks up and pokes invalid bits of stack.\n\tbugsbeware = Z_Malloc(sizeof(*bugsbeware) + sizeof(*specentries)*s + sizeof(specdata));\n\t*bugsbeware = specInfo;\n\tbugsbeware->pData = bugsbeware+1;\n\tbugsbeware->pMapEntries = (VkSpecializationMapEntry*)((char*)bugsbeware->pData + specInfo.dataSize);\n\tmemcpy((void*)bugsbeware->pData, specInfo.pData, specInfo.dataSize);\n\tmemcpy((void*)bugsbeware->pMapEntries, specInfo.pMapEntries, sizeof(*specInfo.pMapEntries)*specInfo.mapEntryCount);\n#else\n\tbugsbeware = &specInfo;\n#endif\n\t//fixme: add more specialisations for custom cvars (yes, this'll flush+reload pipelines if they're changed)\n\t//fixme: add specialisations for permutations I guess\n\t//fixme: add geometry+tesselation support. because we can.\n\n\tshaderStages[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;\n\tshaderStages[0].stage = VK_SHADER_STAGE_VERTEX_BIT;\n\tshaderStages[0].module = p->vert;\n\tshaderStages[0].pName = \"main\";\n\tshaderStages[0].pSpecializationInfo = bugsbeware;\n\tshaderStages[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;\n\tshaderStages[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT;\n\tshaderStages[1].module = p->frag;\n\tshaderStages[1].pName = \"main\";\n\tshaderStages[1].pSpecializationInfo = bugsbeware;\n\n\tpipeCreateInfo.flags\t\t\t\t= 0;\n\tpipeCreateInfo.stageCount\t\t\t= countof(shaderStages);\n\tpipeCreateInfo.pStages\t\t\t\t= shaderStages;\n\tpipeCreateInfo.pVertexInputState\t= &vi;\n\tpipeCreateInfo.pInputAssemblyState\t= &ia;\n\tpipeCreateInfo.pTessellationState\t= NULL;\t//null is okay!\n\tpipeCreateInfo.pViewportState\t\t= &vp;\n\tpipeCreateInfo.pRasterizationState\t= &rs;\n\tpipeCreateInfo.pMultisampleState\t= &ms;\n\tpipeCreateInfo.pDepthStencilState\t= &ds;\n\tpipeCreateInfo.pColorBlendState\t\t= &cb;\n\tpipeCreateInfo.pDynamicState\t\t= &dyn;\n\tpipeCreateInfo.layout\t\t\t\t= p->layout;\n\ti = (permu&PERMUTATION_BEM_DEPTHONLY)?RP_DEPTHONLY:RP_FULLCLEAR;\n\tif (permu&PERMUTATION_BEM_MULTISAMPLE)\n\t\ti |= RP_MULTISAMPLE;\n\tif (permu&PERMUTATION_BEM_FP16)\n\t\ti |= RP_FP16;\n\tif (permu&PERMUTATION_BEM_VR)\n\t\ti |= RP_VR;\n\tpipeCreateInfo.renderPass\t\t\t= VK_GetRenderPass(i);\n\tpipeCreateInfo.subpass\t\t\t\t= 0;\n\tpipeCreateInfo.basePipelineHandle\t= VK_NULL_HANDLE;\n\tpipeCreateInfo.basePipelineIndex\t= -1;\t//used to create derivatives for pipelines created in the same call.\n\n//\tpipeCreateInfo.flags = VK_PIPELINE_CREATE_ALLOW_DERIVATIVES_BIT;\n\n#ifdef VK_KHR_fragment_shading_rate\n\tif (vk.khr_fragment_shading_rate)\n\t{\n\t\t//three ways to specify rates... we need to set which one wins here. we only do pipeline rates.\n\t\tshadingrate.combinerOps[0] = VK_FRAGMENT_SHADING_RATE_COMBINER_OP_KEEP_KHR;//pipeline vs primitive\n\t\tshadingrate.combinerOps[1] = VK_FRAGMENT_SHADING_RATE_COMBINER_OP_KEEP_KHR;//previous vs attachment\n\t\tif (blendflags & SBITS_MISC_FULLRATE)\n\t\t{\n\t\t\tshadingrate.fragmentSize.width = 1;\n\t\t\tshadingrate.fragmentSize.height = 1;\n\t\t}\n\t\telse\n\t\t{\t//actually this is more quater-rate. oh well.\n\t\t\tshadingrate.fragmentSize.width = 2;\n\t\t\tshadingrate.fragmentSize.height = 2;\n\t\t}\n\n\t\tshadingrate.pNext = pipeCreateInfo.pNext;\n\t\tpipeCreateInfo.pNext = &shadingrate;\n\t}\n#endif\n\n\terr = vkCreateGraphicsPipelines(vk.device, vk.pipelinecache, 1, &pipeCreateInfo, vkallocationcb, &pipe->pipeline);\n\tDebugSetName(VK_OBJECT_TYPE_PIPELINE, (uint64_t)pipe->pipeline, p->name);\n\n\tif (err)\n\t{\t//valid err values are VK_ERROR_OUT_OF_HOST_MEMORY, VK_ERROR_OUT_OF_DEVICE_MEMORY, VK_ERROR_INVALID_SHADER_NV\n\t\t//VK_INCOMPLETE is a Qualcom bug with certain spirv-opt optimisations.\n\t\tshaderstate.rc.activepipeline = VK_NULL_HANDLE;\n\t\tSys_Error(\"%s creating pipeline %s for material %s. Check spir-v modules / drivers.\\n\", VK_VKErrorToString(err), p->name, shaderstate.curshader->name);\n\t\treturn;\n\t}\n\n\tvkCmdBindPipeline(vk.rendertarg->cbuf, VK_PIPELINE_BIND_POINT_GRAPHICS, shaderstate.rc.activepipeline=pipe->pipeline);\n}\nstatic void BE_BindPipeline(program_t *p, unsigned int shaderflags, unsigned int blendflags, unsigned int permu)\n{\n\tstruct pipeline_s *pipe;\n\tblendflags &=\t0\n\t\t\t\t\t| SBITS_SRCBLEND_BITS | SBITS_DSTBLEND_BITS | SBITS_MASK_BITS | SBITS_ATEST_BITS\n\t\t\t\t\t| SBITS_MISC_DEPTHWRITE | SBITS_MISC_NODEPTHTEST | SBITS_DEPTHFUNC_BITS\n\t\t\t\t\t| SBITS_LINES | SBITS_MISC_FULLRATE\n\t\t\t\t\t;\n\tshaderflags &= 0\n\t\t\t\t\t| SHADER_CULL_FRONT | SHADER_CULL_BACK\n\t\t\t\t\t| SHADER_POLYGONOFFSET\n\t\t\t\t\t;\n\tpermu |= shaderstate.modepermutation;\n\n\tif (shaderflags & (SHADER_CULL_FRONT | SHADER_CULL_BACK))\n\t\tshaderflags ^= r_refdef.flipcull;\n\n\tfor (pipe = p->pipelines; pipe; pipe = pipe->next)\n\t{\n\t\tif (pipe->flags == shaderflags)\n\t\t\tif (pipe->blendbits == blendflags)\n\t\t\t\tif (pipe->permu == permu)\n\t\t\t\t{\n\t\t\t\t\tif (pipe->pipeline != shaderstate.rc.activepipeline)\n\t\t\t\t\t{\n\t\t\t\t\t\tshaderstate.rc.activepipeline = pipe->pipeline;\n\t\t\t\t\t\tif (shaderstate.rc.activepipeline)\n\t\t\t\t\t\t\tvkCmdBindPipeline(vk.rendertarg->cbuf, VK_PIPELINE_BIND_POINT_GRAPHICS, shaderstate.rc.activepipeline);\n\t\t\t\t\t}\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t}\n\n\t//oh look. we need to build an entirely new pipeline object. hurrah... not.\n\t//split into a different function because of abusive stack combined with windows stack probes.\n\tBE_CreatePipeline(p, shaderflags, blendflags, permu);\n}\n\nstatic void BE_SetupTextureDescriptor(texid_t tex, texid_t fallbacktex, VkDescriptorSet set, VkWriteDescriptorSet *firstdesc, VkWriteDescriptorSet *desc, VkDescriptorImageInfo *img)\n{\n\tif (!tex || !tex->vkimage)\n\t\ttex = fallbacktex;\n\n\tdesc->sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;\n\tdesc->pNext = NULL;\n\tdesc->dstSet = set;\n\tdesc->dstBinding = desc-firstdesc;\n\tdesc->dstArrayElement = 0;\n\tdesc->descriptorCount = 1;\n\tdesc->descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;\n\timg->imageLayout = tex->vkimage->layout;\n\timg->imageView = tex->vkimage->view;\n\timg->sampler = tex->vkimage->sampler;\n\tdesc->pImageInfo = img;\n\tdesc->pBufferInfo = NULL;\n\tdesc->pTexelBufferView = NULL;\n}\nstatic void BE_SetupUBODescriptor(VkDescriptorSet set, VkWriteDescriptorSet *firstdesc, VkWriteDescriptorSet *desc, VkDescriptorBufferInfo *info)\n{\n\tdesc->sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;\n\tdesc->pNext = NULL;\n\tdesc->dstSet = set;\n\tdesc->dstBinding = desc-firstdesc;\n\tdesc->dstArrayElement = 0;\n\tdesc->descriptorCount = 1;\n\tdesc->descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;\n\tdesc->pImageInfo = NULL;\n\tdesc->pBufferInfo = info;\n\tdesc->pTexelBufferView = NULL;\n}\n#ifdef VK_KHR_acceleration_structure\nstatic void BE_SetupAccelerationDescriptor(VkDescriptorSet set, VkWriteDescriptorSet *firstdesc, VkWriteDescriptorSet *desc, VkWriteDescriptorSetAccelerationStructureKHR *descas)\n{\n\tdesc->sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;\n\tdescas->sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET_ACCELERATION_STRUCTURE_KHR;\n\tdesc->pNext = descas;\n\tdescas->pNext = NULL;\n\tdesc->dstSet = set;\n\tdesc->dstBinding = desc-firstdesc;\n\tdesc->dstArrayElement = 0;\n\tdesc->descriptorCount = descas->accelerationStructureCount = 1;\n\tdesc->descriptorType = VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR;\n\tdesc->pImageInfo = NULL;\n\tdesc->pBufferInfo = NULL;\n\tdesc->pTexelBufferView = NULL;\n\tdescas->pAccelerationStructures = &shaderstate.tlas;\n}\n#endif\n\nstatic qboolean BE_SetupMeshProgram(program_t *p, shaderpass_t *pass, unsigned int shaderbits, unsigned int idxcount)\n{\n\tint perm = 0;\n\tif (!p)\n\t\treturn false;\n\n\tif (TEXLOADED(shaderstate.curtexnums->bump))\n\t\tperm |= PERMUTATION_BUMPMAP;\n\tif (TEXLOADED(shaderstate.curtexnums->fullbright))\n\t\tperm |= PERMUTATION_FULLBRIGHT;\n\tif (TEXLOADED(shaderstate.curtexnums->upperoverlay) || TEXLOADED(shaderstate.curtexnums->loweroverlay))\n\t\tperm |= PERMUTATION_UPPERLOWER;\n\tif (TEXLOADED(shaderstate.curtexnums->reflectcube) || TEXLOADED(shaderstate.curtexnums->reflectmask))\n\t\tperm |= PERMUTATION_REFLECTCUBEMASK;\n\tif (r_refdef.globalfog.density)\n\t\tperm |= PERMUTATION_FOG;\n//\tif (r_glsl_offsetmapping.ival && TEXLOADED(shaderstate.curtexnums->bump))\n//\t\tperm |= PERMUTATION_OFFSET;\n\tperm &= p->supportedpermutations;\n\n\tBE_BindPipeline(p, shaderbits, VKBE_ApplyShaderBits(pass->shaderbits), perm);\n\tif (!shaderstate.rc.activepipeline)\n\t\treturn false;\t//err, something bad happened.\n\n\t//most gpus will have a fairly low descriptor set limit of 4 (this is the minimum required)\n\t//that isn't enough for all our textures, so we need to make stuff up as required.\n\t{\n\t\tVkDescriptorSet set = shaderstate.rc.descriptorsets[0] = vk.khr_push_descriptor?VK_NULL_HANDLE:VKBE_TempDescriptorSet(p->desclayout);\n\t\tVkWriteDescriptorSet descs[MAX_TMUS], *desc = descs;\n#ifdef VK_KHR_acceleration_structure\n\t\tVkWriteDescriptorSetAccelerationStructureKHR descas;\n#endif\n\t\tVkDescriptorImageInfo imgs[MAX_TMUS], *img = imgs;\n\t\tunsigned int i;\n\t\ttexid_t t;\n\t\t//why do I keep wanting to write 'desk'? its quite annoying.\n\n\t\t//light / scene\n\t\tBE_SetupUBODescriptor(set, descs, desc++, &shaderstate.ubo_entity);\n\t\tBE_SetupUBODescriptor(set, descs, desc++, &shaderstate.ubo_light);\n#ifdef VK_KHR_acceleration_structure\n\t\tif (p->rayquery)\t//an alternative to shadowmaps...\n\t\t{\n\t\t\tshaderstate.needtlas = true;\n\t\t\tif (!shaderstate.tlas)\n\t\t\t\treturn false;\t//nope... maybe next frame\n\t\t\tBE_SetupAccelerationDescriptor(set, descs, desc++, &descas);\n\t\t}\n#endif\n\t\tif (p->defaulttextures & (1u<<S_SHADOWMAP))\n\t\t\tBE_SetupTextureDescriptor(shaderstate.currentshadowmap, r_whiteimage, set, descs, desc++, img++);\n\t\tif (p->defaulttextures & (1u<<S_PROJECTIONMAP))\n\t\t\tBE_SetupTextureDescriptor(shaderstate.curdlight?shaderstate.curdlight->cubetexture:r_nulltex, r_whitecubeimage, set, descs, desc++, img++);\n\n\t\t//material\n\t\tif (p->defaulttextures & (1u<<S_DIFFUSE))\n\t\t\tBE_SetupTextureDescriptor(shaderstate.curtexnums->base, r_blackimage, set, descs, desc++, img++);\n\t\tif (p->defaulttextures & (1u<<S_NORMALMAP))\n\t\t\tBE_SetupTextureDescriptor(shaderstate.curtexnums->bump, missing_texture_normal, set, descs, desc++, img++);\n\t\tif (p->defaulttextures & (1u<<S_SPECULAR))\n\t\t\tBE_SetupTextureDescriptor(shaderstate.curtexnums->specular, missing_texture_gloss, set, descs, desc++, img++);\n\t\tif (p->defaulttextures & (1u<<S_UPPERMAP))\n\t\t\tBE_SetupTextureDescriptor(shaderstate.curtexnums->upperoverlay, r_blackimage, set, descs, desc++, img++);\n\t\tif (p->defaulttextures & (1u<<S_LOWERMAP))\n\t\t\tBE_SetupTextureDescriptor(shaderstate.curtexnums->loweroverlay, r_blackimage, set, descs, desc++, img++);\n\t\tif (p->defaulttextures & (1u<<S_FULLBRIGHT))\n\t\t\tBE_SetupTextureDescriptor(shaderstate.curtexnums->fullbright, r_blackimage, set, descs, desc++, img++);\n\t\tif (p->defaulttextures & (1u<<S_PALETTED))\n\t\t\tBE_SetupTextureDescriptor(shaderstate.curtexnums->paletted, r_blackimage, set, descs, desc++, img++);\n\t\tif (p->defaulttextures & (1u<<S_REFLECTCUBE))\n\t\t{\n\t\t\tif (shaderstate.curtexnums && TEXLOADED(shaderstate.curtexnums->reflectcube))\n\t\t\t\tt = shaderstate.curtexnums->reflectcube;\n\t\t\telse if (shaderstate.curbatch->envmap)\n\t\t\t\tt = shaderstate.curbatch->envmap;\n\t\t\telse\n\t\t\t\tt = r_blackcubeimage;\t//FIXME\n\t\t\tBE_SetupTextureDescriptor(t, r_blackcubeimage, set, descs, desc++, img++);\n\t\t}\n\t\tif (p->defaulttextures & (1u<<S_REFLECTMASK))\n\t\t\tBE_SetupTextureDescriptor(shaderstate.curtexnums->reflectmask, r_whiteimage, set, descs, desc++, img++);\n\t\tif (p->defaulttextures & (1u<<S_DISPLACEMENT))\n\t\t\tBE_SetupTextureDescriptor(shaderstate.curtexnums->displacement, r_whiteimage, set, descs, desc++, img++);\n\t\tif (p->defaulttextures & (1u<<S_OCCLUSION))\n\t\t\tBE_SetupTextureDescriptor(shaderstate.curtexnums->occlusion, r_whiteimage, set, descs, desc++, img++);\n\t\tif (p->defaulttextures & (1u<<S_TRANSMISSION))\n\t\t\tBE_SetupTextureDescriptor(shaderstate.curtexnums->transmission, r_whiteimage, set, descs, desc++, img++);\n\t\tif (p->defaulttextures & (1u<<S_THICKNESS))\n\t\t\tBE_SetupTextureDescriptor(shaderstate.curtexnums->thickness, r_whiteimage, set, descs, desc++, img++);\n\n\t\t//batch\n\t\tif (p->defaulttextures & (1u<<S_LIGHTMAP0))\n\t\t{\n\t\t\tunsigned int lmi = shaderstate.curbatch->lightmap[0];\n\t\t\tBE_SetupTextureDescriptor((lmi<numlightmaps)?lightmap[lmi]->lightmap_texture:NULL, r_whiteimage, set, descs, desc++, img++);\n\t\t}\n\t\tif (p->defaulttextures & (1u<<S_DELUXEMAP0))\n\t\t{\n\t\t\ttexid_t delux = NULL;\n\t\t\tunsigned int lmi = shaderstate.curbatch->lightmap[0];\n\t\t\tif (lmi<numlightmaps && lightmap[lmi]->hasdeluxe)\n\t\t\t\tdelux = lightmap[lmi+1]->lightmap_texture;\n\t\t\tBE_SetupTextureDescriptor(delux, r_whiteimage, set, descs, desc++, img++);\n\t\t}\n#if MAXRLIGHTMAPS > 1\n\t\tif (p->defaulttextures & ((1u<<S_LIGHTMAP1)|(1u<<S_LIGHTMAP2)|(1u<<S_LIGHTMAP3)))\n\t\t{\n\t\t\tint lmi = shaderstate.curbatch->lightmap[1];\n\t\t\tBE_SetupTextureDescriptor((lmi<numlightmaps)?lightmap[lmi]->lightmap_texture:NULL, r_whiteimage, set, descs, desc++, img++);\n\t\t\tlmi = shaderstate.curbatch->lightmap[2];\n\t\t\tBE_SetupTextureDescriptor((lmi<numlightmaps)?lightmap[lmi]->lightmap_texture:NULL, r_whiteimage, set, descs, desc++, img++);\n\t\t\tlmi = shaderstate.curbatch->lightmap[3];\n\t\t\tBE_SetupTextureDescriptor((lmi<numlightmaps)?lightmap[lmi]->lightmap_texture:NULL, r_whiteimage, set, descs, desc++, img++);\n\t\t}\n\t\tif (p->defaulttextures & ((1u<<S_DELUXEMAP1)|(1u<<S_DELUXEMAP2)|(1u<<S_DELUXEMAP3)))\n\t\t{\n\t\t\tint lmi = shaderstate.curbatch->lightmap[1];\n\t\t\tif (lmi<numlightmaps && lightmap[lmi]->hasdeluxe)\n\t\t\t{\n\t\t\t\tBE_SetupTextureDescriptor((lmi+1<numlightmaps)?lightmap[lmi+1]->lightmap_texture:NULL, r_whiteimage, set, descs, desc++, img++);\n\t\t\t\tlmi = shaderstate.curbatch->lightmap[2];\n\t\t\t\tBE_SetupTextureDescriptor((lmi+1<numlightmaps)?lightmap[lmi+1]->lightmap_texture:NULL, r_whiteimage, set, descs, desc++, img++);\n\t\t\t\tlmi = shaderstate.curbatch->lightmap[3];\n\t\t\t\tBE_SetupTextureDescriptor((lmi+1<numlightmaps)?lightmap[lmi+1]->lightmap_texture:NULL, r_whiteimage, set, descs, desc++, img++);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tBE_SetupTextureDescriptor(NULL, r_whiteimage, set, descs, desc++, img++);\n\t\t\t\tBE_SetupTextureDescriptor(NULL, r_whiteimage, set, descs, desc++, img++);\n\t\t\t\tBE_SetupTextureDescriptor(NULL, r_whiteimage, set, descs, desc++, img++);\n\t\t\t}\n\t\t}\n#endif\n\n\t\t//shader / pass\n\t\tfor (i = 0; i < p->numsamplers; i++)\n\t\t\tBE_SetupTextureDescriptor(SelectPassTexture(pass+i), r_blackimage, set, descs, desc++, img++);\n\n\t\tif (!set)\n\t\t\tvkCmdPushDescriptorSetKHR(vk.rendertarg->cbuf, VK_PIPELINE_BIND_POINT_GRAPHICS, p->layout, 0, desc-descs, descs);\n\t\telse\n\t\t\tvkUpdateDescriptorSets(vk.device, desc-descs, descs, 0, NULL);\n\t}\n\tif (!vk.khr_push_descriptor)\n\t\tvkCmdBindDescriptorSets(vk.rendertarg->cbuf, VK_PIPELINE_BIND_POINT_GRAPHICS, p->layout, 0, countof(shaderstate.rc.descriptorsets), shaderstate.rc.descriptorsets, 0, NULL);\n\n\tRQuantAdd(RQUANT_PRIMITIVEINDICIES, idxcount);\n\tRQuantAdd(RQUANT_DRAWS, 1);\n\n\treturn true;\n}\n\nstatic void BE_DrawMeshChain_Internal(void)\n{\n\tshader_t *altshader;\n\tunsigned int vertcount, idxcount, idxfirst;\n\tmesh_t *m;\n\tqboolean vblends;\t//software\n//\tvoid *map;\n//\tint i;\n\tunsigned int mno;\n\tunsigned int passno;\n\t//extern cvar_t r_polygonoffset_submodel_factor;\n//\tfloat pushdepth;\n//\tfloat pushfactor;\n\n\t//I wasn't going to do this... but gah.\n\tVkBuffer vertexbuffers[VK_BUFF_MAX];\n\tVkDeviceSize vertexoffsets[VK_BUFF_MAX];\n\n\taltshader = shaderstate.curshader;\n\tswitch (shaderstate.mode)\n\t{\n\tcase BEM_LIGHT:\n\t\taltshader = shaderstate.shader_rtlight[shaderstate.curlmode];\n\t\tbreak;\n\tcase BEM_DEPTHONLY:\n\t\taltshader = shaderstate.curshader->bemoverrides[bemoverride_depthonly];\n\t\tif (!altshader)\n\t\t\taltshader = shaderstate.depthonly;\n\t\tbreak;\n\tcase BEM_WIREFRAME:\n\t\taltshader = R_RegisterShader(\"wireframe\", SUF_NONE, \n\t\t\t\"{\\n\"\n\t\t\t\t\"{\\n\"\n\t\t\t\t\t\"map $whiteimage\\n\"\n\t\t\t\t\"}\\n\"\n\t\t\t\"}\\n\"\n\t\t\t);\n\t\tbreak;\n\tdefault:\n\tcase BEM_STANDARD:\n\t\taltshader = shaderstate.curshader;\n\t\tbreak;\n\t}\n\tif (!altshader)\n\t\treturn;\n\n\tif (shaderstate.forcebeflags & BEF_FORCENODEPTH)\n\t{\n\t\tRQuantAdd(RQUANT_2DBATCHES, 1);\n\t}\n\telse if (shaderstate.curentity == &r_worldentity)\n\t{\n\t\tRQuantAdd(RQUANT_WORLDBATCHES, 1);\n\t}\n\telse\n\t{\n\t\tRQuantAdd(RQUANT_ENTBATCHES, 1);\n\t}\n\n\tif (altshader->flags & SHADER_HASCURRENTRENDER)\n\t\tT_Gen_CurrentRender();\t//requires lots of pass-related work...\n\n\t//if this flag is set, then we have to generate our own arrays. to avoid processing extra verticies this may require that we re-pack the verts\n\tif (shaderstate.meshlist[0]->xyz2_array)// && !altshader->prog)\n\t{\n\t\tvblends = true;\n\t\tshaderstate.batchvbo = NULL;\n\t}\n\telse\n\t{\n\t\tvblends = false;\n\t\tif (altshader->flags & SHADER_NEEDSARRAYS)\n\t\t\tshaderstate.batchvbo = NULL;\n\t\telse if (shaderstate.curshader->numdeforms)\n\t\t\tshaderstate.batchvbo = NULL;\n\t}\n\n\t/*index buffers are common to all passes*/\n\tif (shaderstate.batchvbo)\n\t{\n\t\t/*however, we still want to try to avoid discontinuities, because that would otherwise be more draw calls. we can have gaps in verts though*/\n\t\tif (shaderstate.nummeshes == 1)\n\t\t{\n\t\t\tm = shaderstate.meshlist[0];\n\n\t\t\tvkCmdBindIndexBuffer(vk.rendertarg->cbuf, shaderstate.batchvbo->indicies.vk.buff, shaderstate.batchvbo->indicies.vk.offs, VK_INDEX_TYPE);\n\t\t\tidxfirst = m->vbofirstelement;\n\n\t\t\tvertcount = m->vbofirstvert + m->numvertexes;\n\t\t\tidxcount = m->numindexes;\n\t\t}\n\t\telse if (0)//shaderstate.nummeshes == shaderstate.curbatch->maxmeshes)\n\t\t{\n\t\t\tidxfirst = 0;\n\t\t\tvertcount = shaderstate.batchvbo->vertcount;\n\t\t\tidxcount = shaderstate.batchvbo->indexcount;\n\n\t\t\tvkCmdBindIndexBuffer(vk.rendertarg->cbuf, shaderstate.batchvbo->indicies.vk.buff, shaderstate.batchvbo->indicies.vk.offs, VK_INDEX_TYPE);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tindex_t *fte_restrict map;\n\t\t\tVkBuffer buf;\n\t\t\tunsigned int i;\n\t\t\tVkDeviceSize offset;\n\t\t\tvertcount = shaderstate.batchvbo->vertcount;\n\t\t\tfor (mno = 0, idxcount = 0; mno < shaderstate.nummeshes; mno++)\n\t\t\t{\n\t\t\t\tm = shaderstate.meshlist[mno];\n\t\t\t\tidxcount += m->numindexes;\n\t\t\t}\n\t\t\tmap = VKBE_AllocateStreamingSpace(DB_EBO, idxcount * sizeof(*map), &buf, &offset);\n\t\t\tfor (mno = 0; mno < shaderstate.nummeshes; mno++)\n\t\t\t{\n\t\t\t\tm = shaderstate.meshlist[mno];\n\t\t\t\tfor (i = 0; i < m->numindexes; i++)\n\t\t\t\t\tmap[i] = m->indexes[i]+m->vbofirstvert;\n\t\t\t\tmap += m->numindexes;\n\t\t\t}\n\t\t\tvkCmdBindIndexBuffer(vk.rendertarg->cbuf, buf, offset, VK_INDEX_TYPE);\n\t\t\tidxfirst = 0;\n\t\t}\n\t}\n\telse\n\t{\t/*we're going to be using dynamic array stuff here, so generate an index array list that has no vertex gaps*/\n\t\tindex_t *fte_restrict map;\n\t\tVkBuffer buf;\n\t\tunsigned int i;\n\t\tVkDeviceSize offset;\n\t\tfor (mno = 0, vertcount = 0, idxcount = 0; mno < shaderstate.nummeshes; mno++)\n\t\t{\n\t\t\tm = shaderstate.meshlist[mno];\n\t\t\tvertcount += m->numvertexes;\n\t\t\tidxcount += m->numindexes;\n\t\t}\n\n\t\tmap = VKBE_AllocateStreamingSpace(DB_EBO, idxcount * sizeof(*map), &buf, &offset);\n\t\tfor (mno = 0, vertcount = 0; mno < shaderstate.nummeshes; mno++)\n\t\t{\n\t\t\tm = shaderstate.meshlist[mno];\n\t\t\tif (!vertcount)\n\t\t\t\tmemcpy(map, m->indexes, sizeof(index_t)*m->numindexes);\n\t\t\telse\n\t\t\t{\n\t\t\t\tfor (i = 0; i < m->numindexes; i++)\n\t\t\t\t\tmap[i] = m->indexes[i]+vertcount;\n\t\t\t}\n\t\t\tmap += m->numindexes;\n\t\t\tvertcount += m->numvertexes;\n\t\t}\n\t\tvkCmdBindIndexBuffer(vk.rendertarg->cbuf, buf, offset, VK_INDEX_TYPE);\n\t\tidxfirst = 0;\n\t}\n\n\t/*vertex buffers are common to all passes*/\n\tif (shaderstate.batchvbo && !vblends)\n\t{\n\t\tvertexbuffers[VK_BUFF_POS] = shaderstate.batchvbo->coord.vk.buff;\n\t\tvertexoffsets[VK_BUFF_POS] = shaderstate.batchvbo->coord.vk.offs;\n\t}\n\telse\n\t{\n\t\tvecV_t *fte_restrict map;\n\t\tconst mesh_t *m;\n\t\tunsigned int mno;\n\t\tunsigned int i;\n\n\t\tmap = VKBE_AllocateStreamingSpace(DB_VBO, vertcount * sizeof(vecV_t), &vertexbuffers[VK_BUFF_POS], &vertexoffsets[VK_BUFF_POS]);\n\t\t\n\t\tif (vblends)\n\t\t{\n\t\t\tfor (mno = 0; mno < shaderstate.nummeshes; mno++)\n\t\t\t{\n\t\t\t\tconst mesh_t *m = shaderstate.meshlist[mno];\n\t\t\t\tvecV_t *ov = shaderstate.curshader->numdeforms?tmpbuf:map;\n\t\t\t\tvecV_t *iv1 = m->xyz_array;\n\t\t\t\tvecV_t *iv2 = m->xyz2_array;\n\t\t\t\tfloat w1 = m->xyz_blendw[0];\n\t\t\t\tfloat w2 = m->xyz_blendw[1];\n\t\t\t\tfor (i = 0; i < m->numvertexes; i++)\n\t\t\t\t{\n\t\t\t\t\tov[i][0] = iv1[i][0]*w1 + iv2[i][0]*w2;\n\t\t\t\t\tov[i][1] = iv1[i][1]*w1 + iv2[i][1]*w2;\n\t\t\t\t\tov[i][2] = iv1[i][2]*w1 + iv2[i][2]*w2;\n\t\t\t\t}\n\t\t\t\tif (shaderstate.curshader->numdeforms)\n\t\t\t\t{\n\t\t\t\t\tfor (i = 0; i < shaderstate.curshader->numdeforms-1; i++)\n\t\t\t\t\t\tdeformgen(&shaderstate.curshader->deforms[i], m->numvertexes, tmpbuf, tmpbuf, m);\n\t\t\t\t\tdeformgen(&shaderstate.curshader->deforms[i], m->numvertexes, tmpbuf, map, m);\n\t\t\t\t}\n\t\t\t\tmap += m->numvertexes;\n\t\t\t}\n\t\t}\n\t\telse if (shaderstate.curshader->numdeforms > 1)\n\t\t{\t//horrible code, because multiple deforms would otherwise READ from the gpu memory\n\t\t\tfor (mno = 0; mno < shaderstate.nummeshes; mno++)\n\t\t\t{\n\t\t\t\tm = shaderstate.meshlist[mno];\n\t\t\t\tdeformgen(&shaderstate.curshader->deforms[0], m->numvertexes, m->xyz_array, tmpbuf, m);\n\t\t\t\tfor (i = 1; i < shaderstate.curshader->numdeforms-1; i++)\n\t\t\t\t\tdeformgen(&shaderstate.curshader->deforms[i], m->numvertexes, tmpbuf, tmpbuf, m);\n\t\t\t\tdeformgen(&shaderstate.curshader->deforms[i], m->numvertexes, tmpbuf, map, m);\n\t\t\t\tmap += m->numvertexes;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tfor (mno = 0; mno < shaderstate.nummeshes; mno++)\n\t\t\t{\n\t\t\t\tm = shaderstate.meshlist[mno];\n\t\t\t\tdeformgen(&shaderstate.curshader->deforms[0], m->numvertexes, m->xyz_array, map, m);\n\t\t\t\tmap += m->numvertexes;\n\t\t\t}\n\t\t}\n\t}\n\n\tif (altshader->prog)\n\t{\n\t\tif (shaderstate.batchvbo)\n\t\t{\n\t\t\tvertexbuffers[VK_BUFF_COL] = shaderstate.batchvbo->colours[0].vk.buff;\n\t\t\tvertexoffsets[VK_BUFF_COL] = shaderstate.batchvbo->colours[0].vk.offs;\n\t\t\tvertexbuffers[VK_BUFF_TC]  = shaderstate.batchvbo->texcoord.vk.buff;\n\t\t\tvertexoffsets[VK_BUFF_TC]  = shaderstate.batchvbo->texcoord.vk.offs;\n\t\t\tvertexbuffers[VK_BUFF_LMTC]= shaderstate.batchvbo->lmcoord[0].vk.buff;\n\t\t\tvertexoffsets[VK_BUFF_LMTC]= shaderstate.batchvbo->lmcoord[0].vk.offs;\n\n\t\t\tvertexbuffers[VK_BUFF_NORM]= shaderstate.batchvbo->normals.vk.buff;\n\t\t\tvertexoffsets[VK_BUFF_NORM]= shaderstate.batchvbo->normals.vk.offs;\n\t\t\tvertexbuffers[VK_BUFF_SDIR]= shaderstate.batchvbo->svector.vk.buff;\n\t\t\tvertexoffsets[VK_BUFF_SDIR]= shaderstate.batchvbo->svector.vk.offs;\n\t\t\tvertexbuffers[VK_BUFF_TDIR]= shaderstate.batchvbo->tvector.vk.buff;\n\t\t\tvertexoffsets[VK_BUFF_TDIR]= shaderstate.batchvbo->tvector.vk.offs;\n\n\t\t\tif (!vertexbuffers[VK_BUFF_COL])\n\t\t\t{\n\t\t\t\tvertexbuffers[VK_BUFF_COL] = shaderstate.staticbuf;\n\t\t\t\tvertexoffsets[VK_BUFF_COL] = 0;\n\t\t\t}\n\t\t\tif (!vertexbuffers[VK_BUFF_LMTC])\n\t\t\t{\n\t\t\t\tvertexbuffers[VK_BUFF_LMTC] = vertexbuffers[VK_BUFF_TC];\n\t\t\t\tvertexoffsets[VK_BUFF_LMTC] = vertexoffsets[VK_BUFF_TC];\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tconst mesh_t *m;\n\t\t\tunsigned int mno;\n\t\t\tunsigned int i;\n\n\t\t\tif (shaderstate.meshlist[0]->normals_array[0])\n\t\t\t{\n\t\t\t\tvec4_t *fte_restrict map = VKBE_AllocateStreamingSpace(DB_VBO, vertcount * sizeof(vec3_t), &vertexbuffers[VK_BUFF_NORM], &vertexoffsets[VK_BUFF_NORM]);\n\t\t\t\tfor (mno = 0; mno < shaderstate.nummeshes; mno++)\n\t\t\t\t{\n\t\t\t\t\tm = shaderstate.meshlist[mno];\n\t\t\t\t\tmemcpy(map, m->normals_array[0], sizeof(vec3_t)*m->numvertexes);\n\t\t\t\t\tmap += m->numvertexes;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tvertexbuffers[VK_BUFF_NORM] = shaderstate.staticbuf;\n\t\t\t\tvertexoffsets[VK_BUFF_NORM] = sizeof(vec4_t)*65536;\n\t\t\t}\n\n\t\t\tif (shaderstate.meshlist[0]->snormals_array[0])\n\t\t\t{\n\t\t\t\tvec4_t *fte_restrict map = VKBE_AllocateStreamingSpace(DB_VBO, vertcount * sizeof(vec3_t), &vertexbuffers[VK_BUFF_SDIR], &vertexoffsets[VK_BUFF_SDIR]);\n\t\t\t\tfor (mno = 0; mno < shaderstate.nummeshes; mno++)\n\t\t\t\t{\n\t\t\t\t\tm = shaderstate.meshlist[mno];\n\t\t\t\t\tmemcpy(map, m->snormals_array[0], sizeof(vec3_t)*m->numvertexes);\n\t\t\t\t\tmap += m->numvertexes;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tvertexbuffers[VK_BUFF_SDIR] = shaderstate.staticbuf;\n\t\t\t\tvertexoffsets[VK_BUFF_SDIR] = sizeof(vec4_t)*65536 + sizeof(vec3_t)*65536;\n\t\t\t}\n\n\t\t\tif (shaderstate.meshlist[0]->tnormals_array[0])\n\t\t\t{\n\t\t\t\tvec4_t *fte_restrict map = VKBE_AllocateStreamingSpace(DB_VBO, vertcount * sizeof(vec3_t), &vertexbuffers[VK_BUFF_TDIR], &vertexoffsets[VK_BUFF_TDIR]);\n\t\t\t\tfor (mno = 0; mno < shaderstate.nummeshes; mno++)\n\t\t\t\t{\n\t\t\t\t\tm = shaderstate.meshlist[mno];\n\t\t\t\t\tmemcpy(map, m->tnormals_array[0], sizeof(vec3_t)*m->numvertexes);\n\t\t\t\t\tmap += m->numvertexes;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tvertexbuffers[VK_BUFF_TDIR] = shaderstate.staticbuf;\n\t\t\t\tvertexoffsets[VK_BUFF_TDIR] = sizeof(vec4_t)*65536 + sizeof(vec3_t)*65536 + sizeof(vec3_t)*65536;\n\t\t\t}\n\n\t\t\tif (shaderstate.meshlist[0]->colors4f_array[0])\n\t\t\t{\n\t\t\t\tvec4_t *fte_restrict map = VKBE_AllocateStreamingSpace(DB_VBO, vertcount * sizeof(vec4_t), &vertexbuffers[VK_BUFF_COL], &vertexoffsets[VK_BUFF_COL]);\n\t\t\t\tfor (mno = 0; mno < shaderstate.nummeshes; mno++)\n\t\t\t\t{\n\t\t\t\t\tm = shaderstate.meshlist[mno];\n\t\t\t\t\tmemcpy(map, m->colors4f_array[0], sizeof(vec4_t)*m->numvertexes);\n\t\t\t\t\tmap += m->numvertexes;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (shaderstate.meshlist[0]->colors4b_array)\n\t\t\t{\n\t\t\t\tvec4_t *fte_restrict map = VKBE_AllocateStreamingSpace(DB_VBO, vertcount * sizeof(vec4_t), &vertexbuffers[VK_BUFF_COL], &vertexoffsets[VK_BUFF_COL]);\n\t\t\t\tfor (mno = 0; mno < shaderstate.nummeshes; mno++)\n\t\t\t\t{\n\t\t\t\t\tm = shaderstate.meshlist[mno];\n\t\t\t\t\tfor (i = 0; i < m->numvertexes; i++)\n\t\t\t\t\t{\n\t\t\t\t\t\tVector4Scale(m->colors4b_array[i], (1/255.0), map[i]);\n\t\t\t\t\t}\n\t\t\t\t\tmap += m->numvertexes;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\t//FIXME: use some predefined buffer\n\t\t\t\tvec4_t *fte_restrict map = VKBE_AllocateStreamingSpace(DB_VBO, vertcount * sizeof(vec4_t), &vertexbuffers[VK_BUFF_COL], &vertexoffsets[VK_BUFF_COL]);\n\t\t\t\tfor (i = 0; i < vertcount; i++)\n\t\t\t\t{\n\t\t\t\t\tVector4Set(map[i], 1, 1, 1, 1);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (shaderstate.meshlist[0]->lmst_array[0])\n\t\t\t{\n\t\t\t\tvec2_t *fte_restrict map = VKBE_AllocateStreamingSpace(DB_VBO, vertcount * sizeof(vec2_t), &vertexbuffers[VK_BUFF_TC], &vertexoffsets[VK_BUFF_TC]);\n\t\t\t\tvec2_t *fte_restrict lmmap = VKBE_AllocateStreamingSpace(DB_VBO, vertcount * sizeof(vec2_t), &vertexbuffers[VK_BUFF_LMTC], &vertexoffsets[VK_BUFF_LMTC]);\n\t\t\t\tfor (mno = 0; mno < shaderstate.nummeshes; mno++)\n\t\t\t\t{\n\t\t\t\t\tm = shaderstate.meshlist[mno];\n\t\t\t\t\tmemcpy(map, m->st_array, sizeof(vec2_t)*m->numvertexes);\n\t\t\t\t\tmemcpy(lmmap, m->lmst_array[0], sizeof(vec2_t)*m->numvertexes);\n\t\t\t\t\tmap += m->numvertexes;\n\t\t\t\t\tlmmap += m->numvertexes;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tvec2_t *fte_restrict map = VKBE_AllocateStreamingSpace(DB_VBO, vertcount * sizeof(vec2_t), &vertexbuffers[VK_BUFF_TC], &vertexoffsets[VK_BUFF_TC]);\n\t\t\t\tfor (mno = 0; mno < shaderstate.nummeshes; mno++)\n\t\t\t\t{\n\t\t\t\t\tm = shaderstate.meshlist[mno];\n\t\t\t\t\tmemcpy(map, m->st_array, sizeof(*m->st_array)*m->numvertexes);\n\t\t\t\t\tmap += m->numvertexes;\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tvertexbuffers[VK_BUFF_LMTC] = vertexbuffers[VK_BUFF_TC];\n\t\t\t\tvertexoffsets[VK_BUFF_LMTC] = vertexoffsets[VK_BUFF_TC];\n\t\t\t}\n\t\t}\n\n\t\tvkCmdBindVertexBuffers(vk.rendertarg->cbuf, 0, VK_BUFF_MAX, vertexbuffers, vertexoffsets);\n\t\tif (BE_SetupMeshProgram(altshader->prog, altshader->passes, altshader->flags, idxcount))\n\t\t{\n//\t\t\tvkCmdPushConstants(vk.rendertarg->cbuf, altshader->prog->layout, VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(shaderstate.curtexnums->factors), shaderstate.curtexnums->factors);\n\t\t\tvkCmdDrawIndexed(vk.rendertarg->cbuf, idxcount, 1, idxfirst, 0, 0);\n\t\t}\n\t}\n\telse if (1)\n\t{\n\t\tshaderpass_t *p;\n\n\t\t//Vulkan has no fixed function pipeline. we emulate it if we were given no spir-v to run.\n\n\t\tfor (passno = 0; passno < altshader->numpasses; passno += p->numMergedPasses)\n\t\t{\n\t\t\tp = &altshader->passes[passno];\n\n\t\t\tif (p->texgen == T_GEN_UPPEROVERLAY && !TEXLOADED(shaderstate.curtexnums->upperoverlay))\n\t\t\t\tcontinue;\n\t\t\tif (p->texgen == T_GEN_LOWEROVERLAY && !TEXLOADED(shaderstate.curtexnums->loweroverlay))\n\t\t\t\tcontinue;\n\t\t\tif (p->texgen == T_GEN_FULLBRIGHT && !TEXLOADED(shaderstate.curtexnums->fullbright))\n\t\t\t\tcontinue;\n\n\t\t\tif (p->prog)\n\t\t\t{\n\t\t\t\tif (shaderstate.batchvbo)\n\t\t\t\t{\n\t\t\t\t\tvertexbuffers[VK_BUFF_TC] = shaderstate.batchvbo->texcoord.vk.buff;\n\t\t\t\t\tvertexoffsets[VK_BUFF_TC] = shaderstate.batchvbo->texcoord.vk.offs;\n\t\t\t\t\tvertexbuffers[VK_BUFF_LMTC] = shaderstate.batchvbo->lmcoord[0].vk.buff;\n\t\t\t\t\tvertexoffsets[VK_BUFF_LMTC] = shaderstate.batchvbo->lmcoord[0].vk.offs;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tfloat *map;\n\t\t\t\t\tmap = VKBE_AllocateStreamingSpace(DB_VBO, vertcount * sizeof(vec2_t), &vertexbuffers[VK_BUFF_TC], &vertexoffsets[VK_BUFF_TC]);\n\t\t\t\t\tBE_GenerateTCMods(p, map);\n\n\t\t\t\t\tvertexbuffers[VK_BUFF_LMTC] = vertexbuffers[VK_BUFF_TC];\n\t\t\t\t\tvertexoffsets[VK_BUFF_LMTC] = vertexoffsets[VK_BUFF_TC];\n\t\t\t\t}\n\n\t\t\t\tBE_GenerateColourMods(vertcount, p, &vertexbuffers[VK_BUFF_COL], &vertexoffsets[VK_BUFF_COL]);\n\n\t\t\t\tvertexbuffers[VK_BUFF_NORM] = shaderstate.staticbuf;\n\t\t\t\tvertexoffsets[VK_BUFF_NORM] = sizeof(vec4_t)*65536;\n\t\t\t\tvertexbuffers[VK_BUFF_SDIR] = shaderstate.staticbuf;\n\t\t\t\tvertexoffsets[VK_BUFF_SDIR] = vertexoffsets[VK_BUFF_NORM] + sizeof(vec3_t)*65536;\n\t\t\t\tvertexbuffers[VK_BUFF_TDIR] = shaderstate.staticbuf;\n\t\t\t\tvertexoffsets[VK_BUFF_TDIR] = vertexoffsets[VK_BUFF_SDIR] + sizeof(vec3_t)*65536;\n\n\t\t\t\tvkCmdBindVertexBuffers(vk.rendertarg->cbuf, 0, VK_BUFF_MAX, vertexbuffers, vertexoffsets);\n\t\t\t\tif (BE_SetupMeshProgram(p->prog, p, altshader->flags, idxcount))\n\t\t\t\t\tvkCmdDrawIndexed(vk.rendertarg->cbuf, idxcount, 1, idxfirst, 0, 0);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (shaderstate.batchvbo)\n\t\t\t{\t//texcoords are all compatible with static arrays, supposedly\n\t\t\t\tif (p->tcgen == TC_GEN_LIGHTMAP)\n\t\t\t\t{\n\t\t\t\t\tvertexbuffers[VK_BUFF_TC] = shaderstate.batchvbo->lmcoord[0].vk.buff;\n\t\t\t\t\tvertexoffsets[VK_BUFF_TC] = shaderstate.batchvbo->lmcoord[0].vk.offs;\n\t\t\t\t}\n\t\t\t\telse if (p->tcgen == TC_GEN_BASE)\n\t\t\t\t{\n\t\t\t\t\tvertexbuffers[VK_BUFF_TC] = shaderstate.batchvbo->texcoord.vk.buff;\n\t\t\t\t\tvertexoffsets[VK_BUFF_TC] = shaderstate.batchvbo->texcoord.vk.offs;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tSys_Error(\"tcgen %u not supported\\n\", p->tcgen);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tfloat *map;\n\t\t\t\tmap = VKBE_AllocateStreamingSpace(DB_VBO, vertcount * sizeof(vec2_t), &vertexbuffers[VK_BUFF_TC], &vertexoffsets[VK_BUFF_TC]);\n\t\t\t\tBE_GenerateTCMods(p, map);\n\t\t\t}\n\n\t\t\tvertexbuffers[VK_BUFF_LMTC] = vertexbuffers[VK_BUFF_TC];\n\t\t\tvertexoffsets[VK_BUFF_LMTC] = vertexoffsets[VK_BUFF_TC];\n\n\t\t\tvertexbuffers[VK_BUFF_NORM] = shaderstate.staticbuf;\n\t\t\tvertexoffsets[VK_BUFF_NORM] = sizeof(vec4_t)*65536;\n\t\t\tvertexbuffers[VK_BUFF_SDIR] = shaderstate.staticbuf;\n\t\t\tvertexoffsets[VK_BUFF_SDIR] = vertexoffsets[VK_BUFF_NORM] + sizeof(vec3_t)*65536;\n\t\t\tvertexbuffers[VK_BUFF_TDIR] = shaderstate.staticbuf;\n\t\t\tvertexoffsets[VK_BUFF_TDIR] = vertexoffsets[VK_BUFF_SDIR] + sizeof(vec3_t)*65536;\n\n\t\t\tif (p->flags & SHADER_PASS_NOCOLORARRAY)\n\t\t\t{\n\t\t\t\tavec4_t passcolour;\n\t\t\t\tstatic avec4_t fakesource = {1,1,1,1};\n\t\t\t\tm = shaderstate.meshlist[0];\n\t\t\t\tcolourgen(p, 1, NULL, &fakesource, &passcolour, m);\n\t\t\t\talphagen(p, 1, NULL, &fakesource, &passcolour, m);\n\n\t\t\t\t//make sure nothing bugs out... this should be pure white.\n\t\t\t\tvertexbuffers[VK_BUFF_COL] = shaderstate.staticbuf;\n\t\t\t\tvertexoffsets[VK_BUFF_COL] = 0;\n\n\t\t\t\tvkCmdBindVertexBuffers(vk.rendertarg->cbuf, 0, VK_BUFF_MAX, vertexbuffers, vertexoffsets);\n\t\t\t\tif (BE_SetupMeshProgram(shaderstate.programfixedemu[1], p, altshader->flags, idxcount))\n\t\t\t\t{\n\t\t\t\t\tvkCmdPushConstants(vk.rendertarg->cbuf, shaderstate.programfixedemu[1]->layout, VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(passcolour), passcolour);\n\t\t\t\t\tvkCmdDrawIndexed(vk.rendertarg->cbuf, idxcount, 1, idxfirst, 0, 0);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tBE_GenerateColourMods(vertcount, p, &vertexbuffers[VK_BUFF_COL], &vertexoffsets[VK_BUFF_COL]);\n\t\t\t\tvkCmdBindVertexBuffers(vk.rendertarg->cbuf, 0, VK_BUFF_MAX, vertexbuffers, vertexoffsets);\n\t\t\t\tif (BE_SetupMeshProgram(shaderstate.programfixedemu[0], p, altshader->flags, idxcount))\n\t\t\t\t\tvkCmdDrawIndexed(vk.rendertarg->cbuf, idxcount, 1, idxfirst, 0, 0);\n\t\t\t}\n\t\t}\n\t}\n}\n\nvoid VKBE_SelectMode(backendmode_t mode)\n{\n\tshaderstate.mode = mode;\n\tshaderstate.modepermutation = 0;\n\n\tif (vk.rendertarg->rpassflags & RP_MULTISAMPLE)\n\t\tshaderstate.modepermutation |= PERMUTATION_BEM_MULTISAMPLE;\n\tif (vk.rendertarg->rpassflags & RP_FP16)\n\t\tshaderstate.modepermutation |= PERMUTATION_BEM_FP16;\n\tif (vk.rendertarg->rpassflags & RP_VR)\n\t\tshaderstate.modepermutation |= PERMUTATION_BEM_VR;\n\n\tswitch(mode)\n\t{\n\tdefault:\n\t\tbreak;\n\n\tcase BEM_DEPTHONLY:\n\t\tshaderstate.modepermutation |= PERMUTATION_BEM_DEPTHONLY;\n\t\tbreak;\n\n\tcase BEM_WIREFRAME:\n\t\tshaderstate.modepermutation |= PERMUTATION_BEM_WIREFRAME;\n\t\tbreak;\n\n\tcase BEM_LIGHT:\n\t\t//fixme: is this actually needed, or just a waste of time?\n\t\tVKBE_SelectEntity(&r_worldentity);\n\t\tbreak;\n\t}\n}\nqboolean VKBE_GenerateRTLightShader(unsigned int lmode)\n{\n\tif (!shaderstate.shader_rtlight[lmode])\n\t{\n#ifdef VK_KHR_acceleration_structure\n\t\tif (lmode & LSHADER_RAYQUERY)\n\t\t\tshaderstate.shader_rtlight[lmode] = R_RegisterShader(va(\"rq_rtlight%s%s\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(lmode & LSHADER_SPOT)?\"#SPOT=1\":\"#SPOT=0\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(lmode & LSHADER_CUBE)?\"#CUBE=1\":\"#CUBE=0\")\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t, SUF_NONE, LIGHTPASS_SHADER_RQ);\n\t\telse\n#endif\n\t\t\tshaderstate.shader_rtlight[lmode] = R_RegisterShader(va(\"rtlight%s%s%s\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(lmode & LSHADER_SMAP)?\"#PCF=1\":\"#PCF=0\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(lmode & LSHADER_SPOT)?\"#SPOT=1\":\"#SPOT=0\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(lmode & LSHADER_CUBE)?\"#CUBE=1\":\"#CUBE=0\")\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t, SUF_NONE, LIGHTPASS_SHADER);\n\t}\n\tif (shaderstate.shader_rtlight[lmode]->flags & SHADER_NODRAW)\n\t\treturn false;\n\treturn true;\n}\nqboolean VKBE_SelectDLight(dlight_t *dl, vec3_t colour, vec3_t axis[3], unsigned int lmode)\n{\n\tif (dl && TEXLOADED(dl->cubetexture))\n\t\tlmode |= LSHADER_CUBE;\n\n\tif (!VKBE_GenerateRTLightShader(lmode))\n\t{\n\t\tlmode &= ~(LSHADER_SMAP|LSHADER_CUBE);\n\t\tif (!VKBE_GenerateRTLightShader(lmode))\n\t\t{\n\t\t\tVKBE_SetupLightCBuffer(NULL, colour, NULL);\n\t\t\treturn false;\n\t\t}\n\t}\n\tshaderstate.curdlight = dl;\n\tshaderstate.curlmode = lmode;\n\n\tVKBE_SetupLightCBuffer(dl, colour, axis);\n\treturn true;\n}\n\nvoid VKBE_SelectEntity(entity_t *ent)\n{\n\tBE_RotateForEntity(ent, ent->model);\n}\n\n//fixme: create allocations within larger ring buffers, use separate staging.\nvoid *VKBE_CreateStagingBuffer(struct stagingbuf *n, size_t size, VkBufferUsageFlags usage)\n{\n\tvoid *ptr;\n\tVkBufferCreateInfo bufinf = {VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO};\n\tVkMemoryRequirements mem_reqs;\n\tVkMemoryAllocateInfo memAllocInfo = {VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO};\n\n\tmemset(&n->mem, 0, sizeof(n->mem));\n\n\tn->retbuf = VK_NULL_HANDLE;\n\tn->usage = usage | VK_BUFFER_USAGE_TRANSFER_DST_BIT;\n\tbufinf.flags = 0;\n\tbufinf.size = n->size = size;\n\tbufinf.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;\n\tbufinf.sharingMode = VK_SHARING_MODE_EXCLUSIVE;\n\tbufinf.queueFamilyIndexCount = 0;\n\tbufinf.pQueueFamilyIndices = NULL;\n\tvkCreateBuffer(vk.device, &bufinf, vkallocationcb, &n->buf);\n\n\tvkGetBufferMemoryRequirements(vk.device, n->buf, &mem_reqs);\n\n\tmemAllocInfo.allocationSize = mem_reqs.size;\n\tmemAllocInfo.memoryTypeIndex = vk_find_memory_require(mem_reqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);\n\tif (memAllocInfo.memoryTypeIndex == ~0)\n\t\tSys_Error(\"Unable to allocate buffer memory\");\n\n\tVkAssert(vkAllocateMemory(vk.device, &memAllocInfo, vkallocationcb, &n->mem.memory));\n\tDebugSetName(VK_OBJECT_TYPE_DEVICE_MEMORY, (uint64_t)n->mem.memory, \"VKBE_CreateStagingBuffer\");\n\tVkAssert(vkBindBufferMemory(vk.device, n->buf, n->mem.memory, n->mem.offset));\n\tVkAssert(vkMapMemory(vk.device, n->mem.memory, 0, n->size, 0, &ptr));\n\n\treturn ptr;\n}\n\nstruct fencedbufferwork\n{\n\tstruct vk_fencework fw;\n\n\tVkBuffer buf;\n\tvk_poolmem_t mem;\n};\nstatic void VKBE_DoneBufferStaging(void *staging)\n{\n\tstruct fencedbufferwork *n = staging;\n\tvkDestroyBuffer(vk.device, n->buf, vkallocationcb);\n\tVK_ReleasePoolMemory(&n->mem);\n}\nVkBuffer VKBE_FinishStaging(struct stagingbuf *n, vk_poolmem_t *mem)\n{\n\tstruct fencedbufferwork *fence;\n\tVkBuffer retbuf;\n\t\n\t//caller filled the staging buffer, and now wants to copy stuff to the gpu.\n\tvkUnmapMemory(vk.device, n->mem.memory);\n\n\t//create the hardware buffer\n\tif (n->retbuf)\n\t\tretbuf = n->retbuf;\n\telse\n\t{\n\t\tVkBufferCreateInfo bufinf = {VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO};\n\n\t\tbufinf.flags = 0;\n\t\tbufinf.size = n->size;\n\t\tbufinf.usage = n->usage;\n\t\tbufinf.sharingMode = VK_SHARING_MODE_EXCLUSIVE;\n\t\tbufinf.queueFamilyIndexCount = 0;\n\t\tbufinf.pQueueFamilyIndices = NULL;\n\t\tvkCreateBuffer(vk.device, &bufinf, vkallocationcb, &retbuf);\n\t}\n\n\t//sort out its memory\n\t{\n\t\tVkMemoryRequirements mem_reqs;\n\t\tvkGetBufferMemoryRequirements(vk.device, retbuf, &mem_reqs);\n\t\tif (!VK_AllocatePoolMemory(vk_find_memory_require(mem_reqs.memoryTypeBits, 0), mem_reqs.size, mem_reqs.alignment, mem))\n\t\t{\n\t\t\tVkMemoryAllocateInfo memAllocInfo = {VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO};\n\t\t\tVkMemoryDedicatedAllocateInfoKHR khr_mdai = {VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR};\n\n\t\t\t//shouldn't really happen, but just in case...\n\t\t\tmem_reqs.size = max(1,mem_reqs.size);\n\n\t\t\tmemAllocInfo.allocationSize = mem_reqs.size;\n\t\t\tmemAllocInfo.memoryTypeIndex = vk_find_memory_require(mem_reqs.memoryTypeBits, 0);\n\t\t\tif (vk.khr_dedicated_allocation)\n\t\t\t{\n\t\t\t\tkhr_mdai.buffer = retbuf;\n\t\t\t\tkhr_mdai.pNext = memAllocInfo.pNext;\n\t\t\t\tmemAllocInfo.pNext = &khr_mdai;\n\t\t\t}\n\n\t\t\tmem->pool = NULL;\n\t\t\tmem->offset = 0;\n\t\t\tmem->size = mem_reqs.size;\n\t\t\tmem->memory = VK_NULL_HANDLE;\n\n\t\t\tVkAssert(vkAllocateMemory(vk.device, &memAllocInfo, vkallocationcb, &mem->memory));\n\t\t\tDebugSetName(VK_OBJECT_TYPE_DEVICE_MEMORY, (uint64_t)mem->memory, \"VKBE_FinishStaging\");\n\t\t}\n\t\tVkAssert(vkBindBufferMemory(vk.device, retbuf, mem->memory, mem->offset));\n\t}\n\t\n\n\tfence = VK_FencedBegin(VKBE_DoneBufferStaging, sizeof(*fence));\n\tfence->buf = n->buf;\n\tfence->mem = n->mem;\n\n\t//FIXME: barrier?\n\n\t//add the copy command\n\t{\n\t\tVkBufferCopy bcr = {0};\n\t\tbcr.srcOffset = 0;\n\t\tbcr.dstOffset = 0;\n\t\tbcr.size = n->size;\n\t\tvkCmdCopyBuffer(fence->fw.cbuf, n->buf, retbuf, 1, &bcr);\n\t}\n\n\t//FIXME: barrier?\n\n\tVK_FencedSubmit(fence);\n\n\treturn retbuf;\n}\n\nvoid VKBE_GenBatchVBOs(vbo_t **vbochain, batch_t *firstbatch, batch_t *stopbatch)\n{\n\tint maxvboelements;\n\tint maxvboverts;\n\tint vert = 0, idx = 0;\n\tbatch_t *batch;\n\tvbo_t *vbo;\n\tint i, j;\n\tmesh_t *m;\n\tindex_t *vboedata;\n\tqbyte *vbovdatastart, *vbovdata;\n\tstruct stagingbuf vbuf, ebuf;\n\tvk_poolmem_t *poolmem;\n\n\tvbo = Z_Malloc(sizeof(*vbo));\n\n\tmaxvboverts = 0;\n\tmaxvboelements = 0;\n\tfor(batch = firstbatch; batch != stopbatch; batch = batch->next)\n\t{\n\t\tfor (i=0 ; i<batch->maxmeshes ; i++)\n\t\t{\n\t\t\tm = batch->mesh[i];\n\t\t\tmaxvboelements += m->numindexes;\n\t\t\tmaxvboverts += m->numvertexes;\n\t\t}\n\t}\n\n\tif (!maxvboverts || !maxvboelements)\n\t\treturn;\n\n\t//determine array offsets.\n\tvbovdatastart = vbovdata = NULL;\n\tvbo->coord.vk.offs = vbovdata-vbovdatastart;\t\tvbovdata += sizeof(vecV_t)*maxvboverts;\n\tvbo->texcoord.vk.offs = vbovdata-vbovdatastart;\t\tvbovdata += sizeof(vec2_t)*maxvboverts;\n\tvbo->lmcoord[0].vk.offs = vbovdata-vbovdatastart;\tvbovdata += sizeof(vec2_t)*maxvboverts;\n\tvbo->normals.vk.offs = vbovdata-vbovdatastart;\t\tvbovdata += sizeof(vec3_t)*maxvboverts;\n\tvbo->svector.vk.offs = vbovdata-vbovdatastart;\t\tvbovdata += sizeof(vec3_t)*maxvboverts;\n\tvbo->tvector.vk.offs = vbovdata-vbovdatastart;\t\tvbovdata += sizeof(vec3_t)*maxvboverts;\n\tvbo->colours[0].vk.offs = vbovdata-vbovdatastart;\tvbovdata += sizeof(vec4_t)*maxvboverts;\n\n\tvbovdatastart = vbovdata = VKBE_CreateStagingBuffer(&vbuf, vbovdata-vbovdatastart, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT);\n\tvboedata = VKBE_CreateStagingBuffer(&ebuf, sizeof(*vboedata) * maxvboelements, VK_BUFFER_USAGE_INDEX_BUFFER_BIT);\n\n\tvbo->indicies.vk.offs = 0;\n\n\tfor(batch = firstbatch; batch != stopbatch; batch = batch->next)\n\t{\n\t\tbatch->vbo = vbo;\n\t\tfor (j=0 ; j<batch->maxmeshes ; j++)\n\t\t{\n\t\t\tm = batch->mesh[j];\n\t\t\tm->vbofirstvert = vert;\n\n\t\t\tif (m->xyz_array)\n\t\t\t\tmemcpy(vbovdata + vbo->coord.vk.offs\t\t+ vert*sizeof(vecV_t), m->xyz_array,\t\tsizeof(vecV_t)*m->numvertexes);\n\t\t\tif (m->st_array)\n\t\t\t\tmemcpy(vbovdata + vbo->texcoord.vk.offs\t\t+ vert*sizeof(vec2_t), m->st_array,\t\t\tsizeof(vec2_t)*m->numvertexes);\n\t\t\tif (m->lmst_array[0])\n\t\t\t\tmemcpy(vbovdata + vbo->lmcoord[0].vk.offs\t+ vert*sizeof(vec2_t), m->lmst_array[0],\tsizeof(vec2_t)*m->numvertexes);\n\t\t\tif (m->normals_array)\n\t\t\t\tmemcpy(vbovdata + vbo->normals.vk.offs\t\t+ vert*sizeof(vec3_t), m->normals_array,\tsizeof(vec3_t)*m->numvertexes);\n\t\t\tif (m->snormals_array)\n\t\t\t\tmemcpy(vbovdata + vbo->svector.vk.offs\t\t+ vert*sizeof(vec3_t), m->snormals_array,\tsizeof(vec3_t)*m->numvertexes);\n\t\t\tif (m->tnormals_array)\n\t\t\t\tmemcpy(vbovdata + vbo->tvector.vk.offs\t\t+ vert*sizeof(vec3_t), m->tnormals_array,\tsizeof(vec3_t)*m->numvertexes);\n\t\t\tif (m->colors4f_array[0])\n\t\t\t\tmemcpy(vbovdata + vbo->colours[0].vk.offs\t+ vert*sizeof(vec4_t), m->colors4f_array[0],sizeof(vec4_t)*m->numvertexes);\n\n\t\t\tm->vbofirstelement = idx;\n\t\t\tfor (i = 0; i < m->numindexes; i++)\n\t\t\t{\n\t\t\t\t*vboedata++ = vert + m->indexes[i];\n\t\t\t}\n\t\t\tidx += m->numindexes;\n\t\t\tvert += m->numvertexes;\n\t\t}\n\t}\n\n\tvbo->vbomem = poolmem = Z_Malloc(sizeof(*poolmem));\n\tvbo->coord.vk.buff = \n\tvbo->texcoord.vk.buff = \n\tvbo->lmcoord[0].vk.buff = \n\tvbo->normals.vk.buff = \n\tvbo->svector.vk.buff = \n\tvbo->tvector.vk.buff = \n\tvbo->colours[0].vk.buff = VKBE_FinishStaging(&vbuf, poolmem);\n\n\tvbo->ebomem = poolmem = Z_Malloc(sizeof(*poolmem));\n\tvbo->indicies.vk.buff = VKBE_FinishStaging(&ebuf, poolmem);\n\tvbo->indicies.vk.offs = 0;\n\n\tvbo->indexcount = maxvboelements;\n\tvbo->vertcount = maxvboverts;\n\n\tvbo->next = *vbochain;\n\t*vbochain = vbo;\n}\n\nvoid VKBE_GenBrushModelVBO(model_t *mod)\n{\n\tunsigned int vcount, cvcount;\n\n\tbatch_t *batch, *fbatch;\n\tint sortid;\n\tint i;\n\n\tfbatch = NULL;\n\tvcount = 0;\n\tfor (sortid = 0; sortid < SHADER_SORT_COUNT; sortid++)\n\t{\n\t\tif (!mod->batches[sortid])\n\t\t\tcontinue;\n\n\t\tfor (fbatch = batch = mod->batches[sortid]; batch != NULL; batch = batch->next)\n\t\t{\n\t\t\tfor (i = 0, cvcount = 0; i < batch->maxmeshes; i++)\n\t\t\t\tcvcount += batch->mesh[i]->numvertexes;\n\n\t\t\tif (vcount + cvcount > MAX_INDICIES)\n\t\t\t{\n\t\t\t\tVKBE_GenBatchVBOs(&mod->vbos, fbatch, batch);\n\t\t\t\tfbatch = batch;\n\t\t\t\tvcount = 0;\n\t\t\t}\n\n\t\t\tvcount += cvcount;\n\t\t}\n\n\t\tVKBE_GenBatchVBOs(&mod->vbos, fbatch, batch);\n\t}\n}\n\nstruct vkbe_clearvbo\n{\n\tstruct vk_frameend fe;\n\tvbo_t *vbo;\n};\nstatic void VKBE_SafeClearVBO(void *vboptr)\n{\n\tvbo_t *vbo = *(vbo_t**)vboptr;\n\n\tif (vbo->indicies.vk.buff)\n\t{\n\t\tvkDestroyBuffer(vk.device, vbo->indicies.vk.buff, vkallocationcb);\n\t\tVK_ReleasePoolMemory(vbo->ebomem);\n\t\tBZ_Free(vbo->ebomem);\n\t}\n\n\tif (vbo->coord.vk.buff)\n\t{\n\t\tvkDestroyBuffer(vk.device, vbo->coord.vk.buff, vkallocationcb);\n\t\tVK_ReleasePoolMemory(vbo->vbomem);\n\t\tBZ_Free(vbo->vbomem);\n\t}\n\n\tBZ_Free(vbo);\n}\n/*Wipes a vbo*/\nvoid VKBE_ClearVBO(vbo_t *vbo, qboolean dataonly)\n{\n\tif (dataonly)\n\t{\n\t\t//create one for the safe callback to clear.\n\t\tvbo_t *nvbo = BZ_Malloc(sizeof(*vbo));\n\t\tnvbo->indicies = vbo->indicies;\n\t\tnvbo->coord = vbo->coord;\n\n\t\t//scrub it now\n\t\tmemset(&vbo->indicies, 0, sizeof(vbo->indicies));\n\t\tmemset(&vbo->coord, 0, sizeof(vbo->coord));\n\t\tvbo = nvbo;\n\t}\n\tVK_AtFrameEnd(VKBE_SafeClearVBO, &vbo, sizeof(vbo));\n}\n\nvoid VK_UploadLightmap(lightmapinfo_t *lm)\n{\n\textern cvar_t r_lightmap_nearest;\n\tstruct pendingtextureinfo mips;\n\timage_t *tex;\n\n\tlm->modified = false;\n\tif (!TEXVALID(lm->lightmap_texture))\n\t{\n\t\tlm->lightmap_texture = Image_CreateTexture(\"***lightmap***\", NULL, (r_lightmap_nearest.ival?IF_NEAREST:IF_LINEAR));\n\t\tif (!lm->lightmap_texture)\n\t\t\treturn;\n\t}\n\ttex = lm->lightmap_texture;\n\n\tif (0)//vk.frame && tex->vkimage)\n\t{\t//the inline streaming path.\n\t\t//the double-copy sucks but at least ensures that the dma copies stuff from THIS frame and not some of the next one too.\n\t\tint *data;\n\t\tVkBufferImageCopy bic;\n\t\tVkBuffer buf;\n\t\t//size_t x = 0, w = lm->width;\n\t\tsize_t x = lm->rectchange.l, w = lm->rectchange.r - lm->rectchange.l;\n\t\tsize_t y = lm->rectchange.t, h = lm->rectchange.b - lm->rectchange.t, i;\n\n\t\tdata = VKBE_AllocateStreamingSpace(DB_STAGING, w * h * 4, &buf, &bic.bufferOffset);\n\t\tbic.bufferRowLength = w;\n\t\tbic.bufferImageHeight = h;\n\t\tbic.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;\n\t\tbic.imageSubresource.mipLevel = 0;\n\t\tbic.imageSubresource.baseArrayLayer = 0;\n\t\tbic.imageSubresource.layerCount = 1;\n\t\tbic.imageOffset.x = x;\n\t\tbic.imageOffset.y = y;\n\t\tbic.imageOffset.z = 0;\n\t\tbic.imageExtent.width = w;\n\t\tbic.imageExtent.height = h;\n\t\tbic.imageExtent.depth = 1;\n\n\t\tif (w == lm->width)\t//can just copy the lot in a single call.\n\t\t\tmemcpy(data, lm->lightmaps + 4*(y * lm->width), w*h*4);\n\t\telse\n\t\t{\t//there's unused data on each row, oh well.\n\t\t\tfor (i = 0; i < h; i++)\n\t\t\t\tmemcpy(data + i * w, lm->lightmaps + 4*((y+i) * lm->width + x), w*4);\n\t\t}\n\t\tvkCmdCopyBufferToImage(vk.rendertarg->cbuf, buf, tex->vkimage->image, tex->vkimage->layout, 1, &bic);\n\t}\n\telse\n\t{\t//the slow out-of-frame generic path.\n\t\tmips.extrafree = NULL;\n\t\tmips.type = PTI_2D;\n\t\tmips.mip[0].data = lm->lightmaps;\n\t\tmips.mip[0].needfree = false;\n\t\tmips.mip[0].width = lm->width;\n\t\tmips.mip[0].height = lm->height;\n\t\tmips.mip[0].depth = 1;\n\t\tswitch(lm->fmt)\n\t\t{\n\t\tdefault:\n\t\tcase PTI_A2BGR10:\n\t\tcase PTI_E5BGR9:\n\t\tcase PTI_RGBA16F:\n\t\tcase PTI_RGBA32F:\n\t\tcase PTI_L8:\n\t\t\tmips.encoding = lm->fmt;\n\t\t\tbreak;\n\t\tcase PTI_BGRA8:\n\t\t\tmips.encoding = PTI_BGRX8;\n\t\t\tbreak;\n\t\tcase TF_BGR24:\t//shouldn't happen\n\t\t\tmips.encoding = PTI_R8;\n\t\t\tbreak;\n\t\t}\n\t\tmips.mipcount = 1;\n\t\tVK_LoadTextureMips(tex, &mips);\n\t\ttex->status = TEX_LOADED;\n\t\ttex->width = lm->width;\n\t\ttex->height = lm->height;\n\t}\n\n\t//invert the size so we're not always updating the entire thing.\n\tlm->rectchange.l = lm->width;\n\tlm->rectchange.t = lm->height;\n\tlm->rectchange.r = 0;\n\tlm->rectchange.b = 0;\n\tlm->modified = false;\n}\n/*upload all lightmaps at the start to reduce lags*/\nstatic void BE_UploadLightmaps(qboolean force)\n{\n\tint i;\n\n\tfor (i = 0; i < numlightmaps; i++)\n\t{\n\t\tif (!lightmap[i])\n\t\t\tcontinue;\n\n\t\tif (force && !lightmap[i]->external)\n\t\t{\n\t\t\tlightmap[i]->rectchange.l = 0;\n\t\t\tlightmap[i]->rectchange.t = 0;\n\t\t\tlightmap[i]->rectchange.r = lightmap[i]->width;\n\t\t\tlightmap[i]->rectchange.b = lightmap[i]->height;\n\t\t\tlightmap[i]->modified = true;\n\t\t}\n\n\t\tif (lightmap[i]->modified)\n\t\t{\n\t\t\tVK_UploadLightmap(lightmap[i]);\n\t\t}\n\t}\n}\n\nvoid VKBE_UploadAllLightmaps(void)\n{\n\tBE_UploadLightmaps(true);\n}\n\nqboolean VKBE_LightCullModel(vec3_t org, model_t *model)\n{\n#ifdef RTLIGHTS\n\tif ((shaderstate.mode == BEM_LIGHT || shaderstate.mode == BEM_STENCIL || shaderstate.mode == BEM_DEPTHONLY))\n\t{\n\t\tfloat dist;\n\t\tvec3_t disp;\n\t\tif (model->type == mod_alias)\n\t\t{\n\t\t\tVectorSubtract(org, shaderstate.lightinfo, disp);\n\t\t\tdist = DotProduct(disp, disp);\n\t\t\tif (dist > model->radius*model->radius + shaderstate.lightinfo[3]*shaderstate.lightinfo[3])\n\t\t\t\treturn true;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tint i;\n\n\t\t\tfor (i = 0; i < 3; i++)\n\t\t\t{\n\t\t\t\tif (shaderstate.lightinfo[i]-shaderstate.lightinfo[3] > org[i] + model->maxs[i])\n\t\t\t\t\treturn true;\n\t\t\t\tif (shaderstate.lightinfo[i]+shaderstate.lightinfo[3] < org[i] + model->mins[i])\n\t\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t}\n#endif\n\treturn false;\n}\n\nbatch_t *VKBE_GetTempBatch(void)\n{\n\tif (shaderstate.wbatch >= shaderstate.maxwbatches)\n\t{\n\t\tshaderstate.wbatch++;\n\t\treturn NULL;\n\t}\n\treturn &shaderstate.wbatches[shaderstate.wbatch++];\n}\n\nstatic void VKBE_SetupLightCBuffer(dlight_t *dl, vec3_t colour, vec3_t axis[3])\n{\n#ifdef RTLIGHTS\n\textern cvar_t gl_specular;\n#endif\n\tvkcbuf_light_t *cbl = VKBE_AllocateStreamingSpace(DB_UBO, (sizeof(*cbl) + 0x0ff) & ~0xff, &shaderstate.ubo_light.buffer, &shaderstate.ubo_light.offset);\n\tshaderstate.ubo_light.range = sizeof(*cbl);\n\n\tif (!dl)\n\t{\n\t\tmemset(cbl, 0, sizeof(*cbl));\n\n\t\tVector4Set(shaderstate.lightinfo, 0, 0, 0, 0);\n\t\treturn;\n\t}\n\n\n\tcbl->l_lightradius = dl->radius;\n\n#ifdef RTLIGHTS\n\tif (shaderstate.curlmode & LSHADER_ORTHO)\n\t{\n\t\tfloat view[16];\n\t\tfloat proj[16];\n\t\tfloat xmin = -dl->radius;\n\t\tfloat ymin = -dl->radius;\n\t\tfloat znear = -dl->radius;\n\t\tfloat xmax = dl->radius;\n\t\tfloat ymax = dl->radius;\n\t\tfloat zfar = dl->radius;\n\t\tMatrix4x4_CM_Orthographic(proj, xmin, xmax, ymax, ymin, znear, zfar);\n\t\tMatrix4x4_CM_ModelViewMatrixFromAxis(view, axis[0], axis[2], axis[1], dl->origin);\n\t\tMatrix4_Multiply(proj, view, cbl->l_cubematrix);\n//\t\tMatrix4x4_CM_LightMatrixFromAxis(cbl->l_cubematrix, axis[0], axis[1], axis[2], dl->origin);\n\t}\n\telse if (shaderstate.curlmode & LSHADER_SPOT)\n\t{\n\t\tfloat view[16];\n\t\tfloat proj[16];\n\t\textern cvar_t r_shadow_shadowmapping_nearclip;\n\t\tMatrix4x4_CM_Projection_Far(proj, dl->fov, dl->fov, dl->nearclip?dl->nearclip:r_shadow_shadowmapping_nearclip.value, dl->radius, false);\n\t\tMatrix4x4_CM_ModelViewMatrixFromAxis(view, axis[0], axis[1], axis[2], dl->origin);\n\t\tMatrix4_Multiply(proj, view, cbl->l_cubematrix);\n\t}\n\telse\n#endif\n\t\tMatrix4x4_CM_LightMatrixFromAxis(cbl->l_cubematrix, axis[0], axis[1], axis[2], dl->origin);\n\tVectorCopy(dl->origin, cbl->l_lightposition);\n\tcbl->padl1 = 0;\n\tVectorCopy(colour, cbl->l_colour);\n#ifdef RTLIGHTS\n\tVectorCopy(dl->lightcolourscales, cbl->l_lightcolourscale);\n\tcbl->l_lightcolourscale[0] = dl->lightcolourscales[0];\n\tcbl->l_lightcolourscale[1] = dl->lightcolourscales[1];\n\tcbl->l_lightcolourscale[2] = dl->lightcolourscales[2] * gl_specular.value;\n#endif\n\tcbl->l_lightradius = dl->radius;\n\tVector4Copy(shaderstate.lightshadowmapproj, cbl->l_shadowmapproj);\n\tVector2Copy(shaderstate.lightshadowmapscale, cbl->l_shadowmapscale);\n\n\tVectorCopy(dl->origin, shaderstate.lightinfo);\n\tshaderstate.lightinfo[3] = dl->radius;\n}\n\n\n//also updates the entity constant buffer\nstatic void BE_RotateForEntity (const entity_t *fte_restrict e, const model_t *fte_restrict mod)\n{\n\tint i;\n\tfloat modelmatrix[16];\n\tfloat *m = modelmatrix;\n\tfloat *proj;\n\tvkcbuf_entity_t *fte_restrict cbe = VKBE_AllocateStreamingSpace(DB_UBO, (sizeof(*cbe) + 0x0ff) & ~0xff, &shaderstate.ubo_entity.buffer, &shaderstate.ubo_entity.offset);\n\tshaderstate.ubo_entity.range = sizeof(*cbe);\n\n\tshaderstate.curentity = e;\n\n\tif (e->flags & RF_DEPTHHACK)\n\t\tproj = r_refdef.m_projection_view;\n\telse\n\t\tproj = r_refdef.m_projection_std;\n\n\tif ((e->flags & RF_WEAPONMODEL) && r_refdef.playerview->viewentity > 0)\n\t{\n\t\tfloat em[16];\n\t\tfloat vm[16];\n\n\t\tif (e->flags & RF_WEAPONMODELNOBOB)\n\t\t{\n\t\t\tvm[0] = r_refdef.weaponmatrix[0][0];\n\t\t\tvm[1] = r_refdef.weaponmatrix[0][1];\n\t\t\tvm[2] = r_refdef.weaponmatrix[0][2];\n\t\t\tvm[3] = 0;\n\n\t\t\tvm[4] = r_refdef.weaponmatrix[1][0];\n\t\t\tvm[5] = r_refdef.weaponmatrix[1][1];\n\t\t\tvm[6] = r_refdef.weaponmatrix[1][2];\n\t\t\tvm[7] = 0;\n\n\t\t\tvm[8]  = r_refdef.weaponmatrix[2][0];\n\t\t\tvm[9]  = r_refdef.weaponmatrix[2][1];\n\t\t\tvm[10] = r_refdef.weaponmatrix[2][2];\n\t\t\tvm[11] = 0;\n\n\t\t\tvm[12] = r_refdef.weaponmatrix[3][0];\n\t\t\tvm[13] = r_refdef.weaponmatrix[3][1];\n\t\t\tvm[14] = r_refdef.weaponmatrix[3][2];\n\t\t\tvm[15] = 1;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tvm[0] = r_refdef.weaponmatrix_bob[0][0];\n\t\t\tvm[1] = r_refdef.weaponmatrix_bob[0][1];\n\t\t\tvm[2] = r_refdef.weaponmatrix_bob[0][2];\n\t\t\tvm[3] = 0;\n\n\t\t\tvm[4] = r_refdef.weaponmatrix_bob[1][0];\n\t\t\tvm[5] = r_refdef.weaponmatrix_bob[1][1];\n\t\t\tvm[6] = r_refdef.weaponmatrix_bob[1][2];\n\t\t\tvm[7] = 0;\n\n\t\t\tvm[8] = r_refdef.weaponmatrix_bob[2][0];\n\t\t\tvm[9] = r_refdef.weaponmatrix_bob[2][1];\n\t\t\tvm[10] = r_refdef.weaponmatrix_bob[2][2];\n\t\t\tvm[11] = 0;\n\n\t\t\tvm[12] = r_refdef.weaponmatrix_bob[3][0];\n\t\t\tvm[13] = r_refdef.weaponmatrix_bob[3][1];\n\t\t\tvm[14] = r_refdef.weaponmatrix_bob[3][2];\n\t\t\tvm[15] = 1;\n\t\t}\n\n\t\tem[0] = e->axis[0][0];\n\t\tem[1] = e->axis[0][1];\n\t\tem[2] = e->axis[0][2];\n\t\tem[3] = 0;\n\n\t\tem[4] = e->axis[1][0];\n\t\tem[5] = e->axis[1][1];\n\t\tem[6] = e->axis[1][2];\n\t\tem[7] = 0;\n\n\t\tem[8] = e->axis[2][0];\n\t\tem[9] = e->axis[2][1];\n\t\tem[10] = e->axis[2][2];\n\t\tem[11] = 0;\n\n\t\tem[12] = e->origin[0];\n\t\tem[13] = e->origin[1];\n\t\tem[14] = e->origin[2];\n\t\tem[15] = 1;\n\n\t\tMatrix4_Multiply(vm, em, m);\n\t}\n\telse\n\t{\n\t\tm[0] = e->axis[0][0];\n\t\tm[1] = e->axis[0][1];\n\t\tm[2] = e->axis[0][2];\n\t\tm[3] = 0;\n\n\t\tm[4] = e->axis[1][0];\n\t\tm[5] = e->axis[1][1];\n\t\tm[6] = e->axis[1][2];\n\t\tm[7] = 0;\n\n\t\tm[8] = e->axis[2][0];\n\t\tm[9] = e->axis[2][1];\n\t\tm[10] = e->axis[2][2];\n\t\tm[11] = 0;\n\n\t\tm[12] = e->origin[0];\n\t\tm[13] = e->origin[1];\n\t\tm[14] = e->origin[2];\n\t\tm[15] = 1;\n\t}\n\n\tif (e->scale != 1 && e->scale != 0)\t//hexen 2 stuff\n\t{\n#ifdef HEXEN2\n\t\tfloat z;\n\t\tfloat escale;\n\t\tescale = e->scale;\n\t\tswitch(e->drawflags&SCALE_TYPE_MASK)\n\t\t{\n\t\tdefault:\n\t\tcase SCALE_TYPE_UNIFORM:\n\t\t\tVectorScale((m+0), escale, (m+0));\n\t\t\tVectorScale((m+4), escale, (m+4));\n\t\t\tVectorScale((m+8), escale, (m+8));\n\t\t\tbreak;\n\t\tcase SCALE_TYPE_XYONLY:\n\t\t\tVectorScale((m+0), escale, (m+0));\n\t\t\tVectorScale((m+4), escale, (m+4));\n\t\t\tbreak;\n\t\tcase SCALE_TYPE_ZONLY:\n\t\t\tVectorScale((m+8), escale, (m+8));\n\t\t\tbreak;\n\t\t}\n\t\tif (mod && (e->drawflags&SCALE_TYPE_MASK) != SCALE_TYPE_XYONLY)\n\t\t{\n\t\t\tswitch(e->drawflags&SCALE_ORIGIN_MASK)\n\t\t\t{\n\t\t\tcase SCALE_ORIGIN_CENTER:\n\t\t\t\tz = ((mod->maxs[2] + mod->mins[2]) * (1-escale))/2;\n\t\t\t\tVectorMA((m+12), z, e->axis[2], (m+12));\n\t\t\t\tbreak;\n\t\t\tcase SCALE_ORIGIN_BOTTOM:\n\t\t\t\tVectorMA((m+12), mod->mins[2]*(1-escale), e->axis[2], (m+12));\n\t\t\t\tbreak;\n\t\t\tcase SCALE_ORIGIN_TOP:\n\t\t\t\tVectorMA((m+12), -mod->maxs[2], e->axis[2], (m+12));\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n#else\n\t\tVectorScale((m+0), e->scale, (m+0));\n\t\tVectorScale((m+4), e->scale, (m+4));\n\t\tVectorScale((m+8), e->scale, (m+8));\n#endif\n\t}\n\telse if (mod && !strcmp(mod->name, \"progs/eyes.mdl\"))\n\t{\n\t\t/*resize eyes, to make them easier to see*/\n\t\tm[14] -= (22 + 8);\n\t\tVectorScale((m+0), 2, (m+0));\n\t\tVectorScale((m+4), 2, (m+4));\n\t\tVectorScale((m+8), 2, (m+8));\n\t}\n\tif (mod && !ruleset_allow_larger_models.ival && mod->clampscale != 1)\n\t{\t//possibly this should be on a per-frame basis, but that's a real pain to do\n\t\tCon_DPrintf(\"Rescaling %s by %f\\n\", mod->name, mod->clampscale);\n\t\tVectorScale((m+0), mod->clampscale, (m+0));\n\t\tVectorScale((m+4), mod->clampscale, (m+4));\n\t\tVectorScale((m+8), mod->clampscale, (m+8));\n\t}\n\n\t{\n\t\tfloat modelview[16];\n\t\tMatrix4_Multiply(r_refdef.m_view, m, modelview);\n\t\tMatrix4_Multiply(proj, modelview, cbe->m_modelviewproj);\n\t}\n\tmemcpy(cbe->m_model, m, sizeof(cbe->m_model));\n\tMatrix4_Invert(modelmatrix, cbe->m_modelinv);\n\tMatrix4x4_CM_Transform3(cbe->m_modelinv, r_origin, cbe->e_eyepos);\n\n\tcbe->e_time = shaderstate.curtime = r_refdef.time - shaderstate.curentity->shaderTime;\n\n\tVectorCopy(e->light_avg, cbe->e_light_ambient);\tcbe->pad1 = 0;\n\tVectorCopy(e->light_dir, cbe->e_light_dir);\t\tcbe->pad2 = 0;\n\tVectorCopy(e->light_range, cbe->e_light_mul);\tcbe->pad3 = 0;\n\n\tfor (i = 0; i < MAXRLIGHTMAPS ; i++)\n\t{\n\t\t//FIXME: this is fucked, the batch isn't known yet.\n\t\t#if 0\n\t\textern cvar_t gl_overbright;\n\t\tunsigned char s = shaderstate.curbatch?shaderstate.curbatch->lmlightstyle[i]:0;\n\t\tfloat sc;\n\t\tif (s == 255)\n\t\t{\n\t\t\tif (i == 0)\n\t\t\t{\n\t\t\t\tif (shaderstate.curentity->model && shaderstate.curentity->model->engineflags & MDLF_NEEDOVERBRIGHT)\n\t\t\t\t\tsc = (1<<bound(0, gl_overbright.ival, 2)) * shaderstate.identitylighting;\n\t\t\t\telse\n\t\t\t\t\tsc = shaderstate.identitylighting;\n\t\t\t\tcbe->e_lmscale[i][0] = sc;\n\t\t\t\tcbe->e_lmscale[i][1] = sc;\n\t\t\t\tcbe->e_lmscale[i][2] = sc;\n\t\t\t\tcbe->e_lmscale[i][3] = 1;\n\t\t\t\ti++;\n\t\t\t}\n\t\t\tfor (; i < MAXRLIGHTMAPS ; i++)\n\t\t\t{\n\t\t\t\tcbe->e_lmscale[i][0] = 0;\n\t\t\t\tcbe->e_lmscale[i][1] = 0;\n\t\t\t\tcbe->e_lmscale[i][2] = 0;\n\t\t\t\tcbe->e_lmscale[i][3] = 1;\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t\t#else\n\t\tfloat sc = 1;\n\t\t#endif\n\t\tif (shaderstate.curentity->model && shaderstate.curentity->model->engineflags & MDLF_NEEDOVERBRIGHT)\n\t\t\tsc = (1<<bound(0, gl_overbright.ival, 2)) * shaderstate.identitylighting;\n\t\telse\n\t\t\tsc = shaderstate.identitylighting;\n//\t\tsc *= d_lightstylevalue[s]/256.0f;\n\n\t\tVector4Set(cbe->e_lmscale[i], sc, sc, sc, 1);\n\t}\n\n\tR_FetchPlayerColour(e->topcolour, cbe->e_uppercolour);\t\tcbe->pad4 = 0;\n\tR_FetchPlayerColour(e->bottomcolour, cbe->e_lowercolour);\tcbe->pad5 = 0;\n\tVectorCopy(e->glowmod, cbe->e_glowmod);\t\t\t\t\t\tcbe->pad6 = 0;\n\tif (shaderstate.flags & BEF_FORCECOLOURMOD)\n\t\tVector4Copy(e->shaderRGBAf, cbe->e_colourident);\n\telse\n\t\tVector4Set(cbe->e_colourident, 1, 1, 1, e->shaderRGBAf[3]);\n\n\tVectorCopy(r_refdef.globalfog.colour, cbe->w_fogcolours);\n\tcbe->w_fogcolours[3] = r_refdef.globalfog.alpha;\n\n\tcbe->w_fogdensity = r_refdef.globalfog.density;\n\tcbe->w_fogdepthbias = r_refdef.globalfog.depthbias;\n\tVector2Set(cbe->pad7, 0, 0);\n\n\t/*ndr = (e->flags & RF_DEPTHHACK)?0.333:1;\n\tif (ndr != shaderstate.rc.depthrange)\n\t{\n\t\tVkViewport viewport;\n\t\tshaderstate.rc.depthrange = ndr;\n\n\t\tviewport.x = r_refdef.pxrect.x;\n\t\tviewport.y = r_refdef.pxrect.y;\n\t\tviewport.width = r_refdef.pxrect.width;\n\t\tviewport.height = r_refdef.pxrect.height;\n\t\tviewport.minDepth = 0;\n\t\tviewport.maxDepth = ndr;\n\t\tvkCmdSetViewport(vk.rendertarg->cbuf, 0, 1, &viewport);\n\t}*/\n}\n\nvoid VKBE_SubmitBatch(batch_t *batch)\n{\n\tshader_t *shader = batch->shader;\n\tunsigned int bf;\n\tshaderstate.nummeshes = batch->meshes - batch->firstmesh;\n\tif (!shaderstate.nummeshes)\n\t\treturn;\n\tshaderstate.curbatch = batch;\n\tshaderstate.batchvbo = batch->vbo;\n\tshaderstate.meshlist = batch->mesh + batch->firstmesh;\n\tshaderstate.curshader = shader->remapto;\n\tbf = batch->flags | shaderstate.forcebeflags;\n\tif (shaderstate.curentity != batch->ent || shaderstate.flags != bf)\n\t{\n\t\tshaderstate.flags = bf;\n\t\tBE_RotateForEntity(batch->ent, batch->ent->model);\n\t\tshaderstate.curtime = r_refdef.time - shaderstate.curentity->shaderTime;\n\t}\n\tif (batch->skin)\n\t\tshaderstate.curtexnums = batch->skin;\n\telse if (shader->numdefaulttextures)\n\t\tshaderstate.curtexnums = shader->defaulttextures + ((int)(shader->defaulttextures_fps * shaderstate.curtime) % shader->numdefaulttextures);\n\telse\n\t\tshaderstate.curtexnums = shader->defaulttextures;\n\n\tBE_DrawMeshChain_Internal();\n}\n\nvoid VKBE_DrawMesh_List(shader_t *shader, int nummeshes, mesh_t **meshlist, vbo_t *vbo, texnums_t *texnums, unsigned int beflags)\n{\n\tshaderstate.curbatch = &shaderstate.dummybatch;\n\tshaderstate.batchvbo = vbo;\n\tshaderstate.curshader = shader->remapto;\n\tif (texnums)\n\t\tshaderstate.curtexnums = texnums;\n\telse if (shader->numdefaulttextures)\n\t\tshaderstate.curtexnums = shader->defaulttextures + ((int)(shader->defaulttextures_fps * shaderstate.curtime) % shader->numdefaulttextures);\n\telse\n\t\tshaderstate.curtexnums = shader->defaulttextures;\n\tshaderstate.meshlist = meshlist;\n\tshaderstate.nummeshes = nummeshes;\n\tshaderstate.flags = beflags | shaderstate.forcebeflags;\n\n\tBE_DrawMeshChain_Internal();\n}\n\nvoid VKBE_DrawMesh_Single(shader_t *shader, mesh_t *meshchain, vbo_t *vbo, unsigned int beflags)\n{\n\tshaderstate.curbatch = &shaderstate.dummybatch;\n\tshaderstate.batchvbo = vbo;\n\tshaderstate.curtime = realtime;\n\tshaderstate.curshader = shader->remapto;\n\tif (shader->numdefaulttextures)\n\t\tshaderstate.curtexnums = shader->defaulttextures + ((int)(shader->defaulttextures_fps * shaderstate.curtime) % shader->numdefaulttextures);\n\telse\n\t\tshaderstate.curtexnums = shader->defaulttextures;\n\tshaderstate.meshlist = &meshchain;\n\tshaderstate.nummeshes = 1;\n\tshaderstate.flags = beflags | shaderstate.forcebeflags;\n\n\tBE_DrawMeshChain_Internal();\n}\n\nvoid VKBE_RT_Destroy(struct vk_rendertarg *targ)\n{\n\tif (targ->framebuffer)\n\t{\n\t\tvkDestroyFramebuffer(vk.device, targ->framebuffer, vkallocationcb);\n\t\tVK_DestroyVkTexture(&targ->depth);\n\t\tVK_DestroyVkTexture(&targ->colour);\n\t}\n\tmemset(targ, 0, sizeof(*targ));\n}\n\n\nstruct vkbe_rtpurge\n{\n\tVkFramebuffer framebuffer;\n\tvk_image_t colour;\n\tvk_image_t mscolour;\n\tvk_image_t depth;\n};\nstatic void VKBE_RT_Purge(void *ptr)\n{\n\tstruct vkbe_rtpurge *ctx = ptr;\n\tvkDestroyFramebuffer(vk.device, ctx->framebuffer, vkallocationcb);\n\tVK_DestroyVkTexture(&ctx->depth);\n\tVK_DestroyVkTexture(&ctx->mscolour);\n\tVK_DestroyVkTexture(&ctx->colour);\n}\nvoid VKBE_RT_Gen(struct vk_rendertarg *targ, vk_image_t *colour, uint32_t width, uint32_t height, qboolean clear, unsigned int flags)\n{\n\t//sooooo much work...\n\tVkImageCreateInfo colour_imginfo = {VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO};\n\tVkImageCreateInfo mscolour_imginfo;\n\tVkImageCreateInfo depth_imginfo;\n\tstruct vkbe_rtpurge *purge;\n\tstatic VkClearValue clearvalues[3];\n\n\tif (colour)\n\t{\t//override the width+height if we already have an image to draw to.\n\t\twidth = colour->width;\n\t\theight = colour->height;\n\t}\n\n\ttarg->restartinfo.clearValueCount = 3;\n\ttarg->depthcleared = true;\t//will be once its activated.\n\n\tif (targ->width == width && targ->height == height && targ->q_colour.flags == flags && (!(targ->rpassflags&RP_MULTISAMPLE))==(targ->mscolour.image==VK_NULL_HANDLE))\n\t{\n\t\tif ((colour && colour->image == targ->colour.image) || (!colour && !targ->externalimage))\n\t\t{\n\t\t\tif (width == 0 || height == 0)\n\t\t\t\ttarg->restartinfo.renderPass = VK_NULL_HANDLE;\t//illegal combination used for destruction.\n\t\t\telse if (clear || targ->firstuse)\n\t\t\t\ttarg->restartinfo.renderPass = VK_GetRenderPass(RP_FULLCLEAR|targ->rpassflags);\n\t\t\telse\n\t\t\t\ttarg->restartinfo.renderPass = VK_GetRenderPass(RP_DEPTHCLEAR|targ->rpassflags);\t//don't care\n\t\t\treturn;\t//no work to do.\n\t\t}\n\t}\n\n\tif (targ->framebuffer || targ->externalimage)\n\t{\t//schedule the old one to be destroyed at the end of the current frame. DIE OLD ONE, DIE!\n\t\tpurge = VK_AtFrameEnd(VKBE_RT_Purge, NULL, sizeof(*purge));\n\t\tpurge->framebuffer = targ->framebuffer; \n\t\tpurge->colour = targ->colour;\n\t\tif (targ->externalimage)\n\t\t\tpurge->colour.image = VK_NULL_HANDLE;\n\t\tpurge->mscolour = targ->mscolour;\n\t\tpurge->depth = targ->depth;\n\t\tmemset(&targ->colour, 0, sizeof(targ->colour));\n\t\tmemset(&targ->mscolour, 0, sizeof(targ->mscolour));\n\t\tmemset(&targ->depth, 0, sizeof(targ->depth));\n\t\ttarg->framebuffer = VK_NULL_HANDLE;\n\t}\n\n\ttarg->externalimage = !!colour;\n\ttarg->q_colour.vkimage = &targ->colour;\n\ttarg->q_depth.vkimage = &targ->depth;\n\ttarg->q_colour.status = TEX_LOADED;\n\ttarg->q_colour.width = width;\n\ttarg->q_colour.height = height;\n\ttarg->q_colour.flags = flags;\n\n\ttarg->width = width;\n\ttarg->height = height;\n\n\tif (width == 0 && height == 0)\n\t{\n\t\ttarg->restartinfo.renderPass = VK_NULL_HANDLE;\n\t\treturn;\t//destroyed\n\t}\n\tif (targ->externalimage)\n\t{\n\t\tVkFormat old = vk.backbufformat;\n\t\tcolour_imginfo.format = vk.backbufformat = colour->vkformat;\n\t\ttarg->rpassflags |= RP_VR;\n\t\ttarg->restartinfo.renderPass = VK_GetRenderPass(RP_FULLCLEAR|targ->rpassflags);\n\n\t\tvk.backbufformat = old;\n\t}\n\telse\n\t{\n\t\ttarg->rpassflags &= ~RP_VR;\n\t\ttarg->restartinfo.renderPass = VK_GetRenderPass(RP_FULLCLEAR|targ->rpassflags);\n\n\t\tcolour_imginfo.format = (targ->rpassflags&RP_FP16)?VK_FORMAT_R16G16B16A16_SFLOAT:vk.backbufformat;\n\t}\n\tcolour_imginfo.flags = 0;\n\tcolour_imginfo.imageType = VK_IMAGE_TYPE_2D;\n\tcolour_imginfo.extent.width = width;\n\tcolour_imginfo.extent.height = height;\n\tcolour_imginfo.extent.depth = 1;\n\tcolour_imginfo.mipLevels = 1;\n\tcolour_imginfo.arrayLayers = 1;\n\t//colour buffer is always 1 sample. if multisampling then we have a hidden 'mscolour' image that is paired with the depth, resolving to the 'colour' image.\n\tcolour_imginfo.samples = VK_SAMPLE_COUNT_1_BIT;\n\tcolour_imginfo.tiling = VK_IMAGE_TILING_OPTIMAL;\n\tcolour_imginfo.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT|VK_IMAGE_USAGE_SAMPLED_BIT;\n\tcolour_imginfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;\n\tcolour_imginfo.queueFamilyIndexCount = 0;\n\tcolour_imginfo.pQueueFamilyIndices = NULL;\n\tcolour_imginfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;\n\tif (targ->externalimage)\n\t{\n\t\ttarg->colour.image = colour->image;\n\t}\n\telse\n\t{\n\t\tVkAssert(vkCreateImage(vk.device, &colour_imginfo, vkallocationcb, &targ->colour.image));\n\t\tDebugSetName(VK_OBJECT_TYPE_IMAGE, (uint64_t)targ->colour.image, \"RT Colour\");\n\t}\n\n\tdepth_imginfo = colour_imginfo;\n\tdepth_imginfo.format = vk.depthformat;\n\tdepth_imginfo.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT|VK_IMAGE_USAGE_SAMPLED_BIT;\n\tif (targ->rpassflags&RP_MULTISAMPLE)\n\t{\n\t\tmscolour_imginfo = colour_imginfo;\n\t\tdepth_imginfo.samples = mscolour_imginfo.samples = vk.multisamplebits;\n\t\tVkAssert(vkCreateImage(vk.device, &mscolour_imginfo, vkallocationcb, &targ->mscolour.image));\n\t\tDebugSetName(VK_OBJECT_TYPE_IMAGE, (uint64_t)targ->mscolour.image, \"RT MS Colour\");\n\t\tVK_AllocateBindImageMemory(&targ->mscolour, true);\n\t}\n\tVkAssert(vkCreateImage(vk.device, &depth_imginfo, vkallocationcb, &targ->depth.image));\n\tDebugSetName(VK_OBJECT_TYPE_IMAGE, (uint64_t)targ->depth.image, \"RT Depth\");\n\n\tif (targ->externalimage)\t//an external image is assumed to already have memory bound. don't allocate it elsewhere.\n\t\tmemset(&targ->colour.mem, 0, sizeof(targ->colour.mem));\n\telse\n\t\tVK_AllocateBindImageMemory(&targ->colour, true);\n\tVK_AllocateBindImageMemory(&targ->depth, true);\n\n\t{\n\t\tVkImageViewCreateInfo ivci = {VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO};\n\t\tivci.components.r = VK_COMPONENT_SWIZZLE_R;\n\t\tivci.components.g = VK_COMPONENT_SWIZZLE_G;\n\t\tivci.components.b = VK_COMPONENT_SWIZZLE_B;\n\t\tivci.components.a = VK_COMPONENT_SWIZZLE_A;\n\t\tivci.subresourceRange.baseMipLevel = 0;\n\t\tivci.subresourceRange.levelCount = 1;\n\t\tivci.subresourceRange.baseArrayLayer = 0;\n\t\tivci.subresourceRange.layerCount = 1;\n\t\tivci.viewType = VK_IMAGE_VIEW_TYPE_2D;\n\t\tivci.flags = 0;\n\n\t\tivci.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;\n\t\tivci.format = colour_imginfo.format;\n\t\tivci.image = targ->colour.image;\n\t\tVkAssert(vkCreateImageView(vk.device, &ivci, vkallocationcb, &targ->colour.view));\n\n\t\tif (targ->rpassflags&RP_MULTISAMPLE)\n\t\t{\n\t\t\tivci.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;\n\t\t\tivci.format = mscolour_imginfo.format;\n\t\t\tivci.image = targ->mscolour.image;\n\t\t\tVkAssert(vkCreateImageView(vk.device, &ivci, vkallocationcb, &targ->mscolour.view));\n\t\t}\n\n\t\tivci.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;\n\t\tivci.format = depth_imginfo.format;\n\t\tivci.image = targ->depth.image;\n\t\tVkAssert(vkCreateImageView(vk.device, &ivci, vkallocationcb, &targ->depth.view));\n\t}\n\n\t{\n\t\tVkSamplerCreateInfo lmsampinfo = {VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO};\n\t\tlmsampinfo.minFilter = lmsampinfo.magFilter = (flags&IF_NEAREST)?VK_FILTER_NEAREST:VK_FILTER_LINEAR;\n\t\tlmsampinfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;\n\t\tlmsampinfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;\n\t\tlmsampinfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;\n\t\tlmsampinfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;\n\t\tlmsampinfo.mipLodBias = 0.0;\n\t\tlmsampinfo.anisotropyEnable = VK_FALSE;\n\t\tlmsampinfo.maxAnisotropy = 1.0;\n\t\tlmsampinfo.compareOp = VK_COMPARE_OP_LESS_OR_EQUAL;\n\t\tlmsampinfo.minLod = 0;\n\t\tlmsampinfo.maxLod = 0;\n\t\tlmsampinfo.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE;\n\t\tlmsampinfo.unnormalizedCoordinates = VK_FALSE;\n\n\t\tlmsampinfo.compareEnable = VK_FALSE;\n\t\tVK_CreateSamplerInfo(&lmsampinfo, &targ->colour);\n\n\t\tlmsampinfo.compareEnable = VK_TRUE;\n\t\tVK_CreateSamplerInfo(&lmsampinfo, &targ->depth);\n\t}\n\n\ttarg->colour.layout = VK_IMAGE_LAYOUT_UNDEFINED;\n\ttarg->mscolour.layout = VK_IMAGE_LAYOUT_UNDEFINED;\n\ttarg->depth.layout = VK_IMAGE_LAYOUT_UNDEFINED;\n\n\t{\n\t\tVkFramebufferCreateInfo fbinfo = {VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO};\n\t\tVkImageView attachments[3] = {targ->colour.view, targ->depth.view, targ->mscolour.view};\n\t\tfbinfo.flags = 0;\n\t\tfbinfo.renderPass = targ->restartinfo.renderPass;\n\t\tfbinfo.attachmentCount = (targ->rpassflags&RP_MULTISAMPLE)?3:2;\n\t\tfbinfo.pAttachments = attachments;\n\t\tfbinfo.width = width;\n\t\tfbinfo.height = height;\n\t\tfbinfo.layers = 1;\n\t\tVkAssert(vkCreateFramebuffer(vk.device, &fbinfo, vkallocationcb, &targ->framebuffer));\n\t}\n\n\ttarg->restartinfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;\n\ttarg->restartinfo.pNext = NULL;\n\ttarg->restartinfo.framebuffer = targ->framebuffer;\n\ttarg->restartinfo.renderArea.offset.x = 0;\n\ttarg->restartinfo.renderArea.offset.y = 0;\n\ttarg->restartinfo.renderArea.extent.width = width;\n\ttarg->restartinfo.renderArea.extent.height = height;\n\ttarg->restartinfo.pClearValues = clearvalues;\n\tclearvalues[1].depthStencil.depth = 1;\n}\n\nstruct vkbe_rtpurge_cube\n{\n\tvk_image_t colour;\n\tvk_image_t depth;\n\tstruct\n\t{\n\t\tVkFramebuffer framebuffer;\n\t\tVkImageView iv[2];\n\t} face[6];\n};\nstatic void VKBE_RT_Purge_Cube(void *ptr)\n{\n\tuint32_t f;\n\tstruct vkbe_rtpurge_cube *ctx = ptr;\n\tfor (f = 0; f < 6; f++)\n\t{\n\t\tvkDestroyFramebuffer(vk.device, ctx->face[f].framebuffer, vkallocationcb);\n\t\tvkDestroyImageView(vk.device, ctx->face[f].iv[0], vkallocationcb);\n\t\tvkDestroyImageView(vk.device, ctx->face[f].iv[1], vkallocationcb);\n\t}\n\tVK_DestroyVkTexture(&ctx->depth);\n\tVK_DestroyVkTexture(&ctx->colour);\n}\n//generate a cubemap-compatible 2d array, set up 6 render targets that render to their own views\nvoid VKBE_RT_Gen_Cube(struct vk_rendertarg_cube *targ, uint32_t size, qboolean clear)\n{\n\tVkImageCreateInfo colour_imginfo = {VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO};\n\tVkImageCreateInfo depth_imginfo;\n\tstruct vkbe_rtpurge_cube *purge;\n\tuint32_t f;\n\tstatic VkClearValue clearvalues[2];\n\n\tif (targ->size == size && !clear)\n\t\treturn;\t//no work to do.\n\n\tfor (f = 0; f < 6; f++)\n\t{\n\t\tif (clear)\n\t\t\ttarg->face[f].restartinfo.renderPass = VK_GetRenderPass(RP_FULLCLEAR|targ->face[f].rpassflags);\n\t\telse\n\t\t\ttarg->face[f].restartinfo.renderPass = VK_GetRenderPass(RP_DEPTHCLEAR|targ->face[f].rpassflags);\t//don't care\n\t\ttarg->face[f].restartinfo.clearValueCount = 2;\n\t}\n\n\tif (targ->size == size)\n\t\treturn;\t//no work to do.\n\n\tif (targ->size)\n\t{\t//schedule the old one to be destroyed at the end of the current frame. DIE OLD ONE, DIE!\n\t\tpurge = VK_AtFrameEnd(VKBE_RT_Purge_Cube, NULL, sizeof(*purge));\n\t\tfor (f = 0; f < 6; f++)\n\t\t{\n\t\t\tpurge->face[f].framebuffer = targ->face[f].framebuffer;\n\t\t\ttarg->face[f].framebuffer = VK_NULL_HANDLE;\n\t\t\tpurge->face[f].iv[0] = targ->face[f].colour.view;\n\t\t\tpurge->face[f].iv[1] = targ->face[f].depth.view;\n\t\t\ttarg->face[f].colour.view = VK_NULL_HANDLE;\n\t\t\ttarg->face[f].depth.view = VK_NULL_HANDLE;\n\t\t}\n\t\tpurge->colour = targ->colour;\n\t\tpurge->depth = targ->depth;\n\t\tmemset(&targ->colour, 0, sizeof(targ->colour));\n\t\tmemset(&targ->depth, 0, sizeof(targ->depth));\n\t}\n\n\ttarg->size = size;\n\tif (!size)\n\t\treturn;\n\n\ttarg->q_colour.vkimage = &targ->colour;\n\ttarg->q_depth.vkimage = &targ->depth;\n\n\tcolour_imginfo.format = VK_FORMAT_R8G8B8A8_UNORM;\n\tcolour_imginfo.flags = VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT;\n\tcolour_imginfo.imageType = VK_IMAGE_TYPE_2D;\n\tcolour_imginfo.extent.width = size;\n\tcolour_imginfo.extent.height = size;\n\tcolour_imginfo.mipLevels = 1;\n\tcolour_imginfo.arrayLayers = 6;\n\tcolour_imginfo.samples = VK_SAMPLE_COUNT_1_BIT;\n\tcolour_imginfo.tiling = VK_IMAGE_TILING_OPTIMAL;\n\tcolour_imginfo.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT|VK_IMAGE_USAGE_SAMPLED_BIT;\n\tcolour_imginfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;\n\tcolour_imginfo.queueFamilyIndexCount = 0;\n\tcolour_imginfo.pQueueFamilyIndices = NULL;\n\tcolour_imginfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;\n\tVkAssert(vkCreateImage(vk.device, &colour_imginfo, vkallocationcb, &targ->colour.image));\n\tDebugSetName(VK_OBJECT_TYPE_IMAGE, (uint64_t)targ->colour.image, \"RT Cube Colour\");\n\n\tdepth_imginfo = colour_imginfo;\n\tdepth_imginfo.format = vk.depthformat;\n\tdepth_imginfo.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT|VK_IMAGE_USAGE_SAMPLED_BIT;\n\tVkAssert(vkCreateImage(vk.device, &depth_imginfo, vkallocationcb, &targ->depth.image));\n\tDebugSetName(VK_OBJECT_TYPE_IMAGE, (uint64_t)targ->depth.image, \"RT Cube Depth\");\n\n\tVK_AllocateBindImageMemory(&targ->colour, true);\n\tVK_AllocateBindImageMemory(&targ->depth, true);\n\n//\t\tset_image_layout(vk.frame->cbuf, targ->colour.image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);\n//\t\tset_image_layout(vk.frame->cbuf, targ->depth.image, VK_IMAGE_ASPECT_DEPTH_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL);\n\n\t//public sampler\n\t{\n\t\tVkSamplerCreateInfo lmsampinfo = {VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO};\n\t\tlmsampinfo.minFilter = lmsampinfo.magFilter = VK_FILTER_LINEAR;\n\t\tlmsampinfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;\n\t\tlmsampinfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;\n\t\tlmsampinfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;\n\t\tlmsampinfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;\n\t\tlmsampinfo.mipLodBias = 0.0;\n\t\tlmsampinfo.anisotropyEnable = VK_FALSE;\n\t\tlmsampinfo.maxAnisotropy = 1.0;\n\t\tlmsampinfo.compareOp = VK_COMPARE_OP_LESS_OR_EQUAL;\n\t\tlmsampinfo.minLod = 0;\n\t\tlmsampinfo.maxLod = 0;\n\t\tlmsampinfo.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE;\n\t\tlmsampinfo.unnormalizedCoordinates = VK_FALSE;\n\n\t\tlmsampinfo.compareEnable = VK_FALSE;\n\t\tVkAssert(vkCreateSampler(vk.device, &lmsampinfo, NULL, &targ->colour.sampler));\n\n\t\tlmsampinfo.compareEnable = VK_TRUE;\n\t\tVkAssert(vkCreateSampler(vk.device, &lmsampinfo, NULL, &targ->depth.sampler));\n\t}\n\n\t//public cubemap views\n\t{\n\t\tVkImageViewCreateInfo ivci = {VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO};\n\t\tivci.components.r = VK_COMPONENT_SWIZZLE_R;\n\t\tivci.components.g = VK_COMPONENT_SWIZZLE_G;\n\t\tivci.components.b = VK_COMPONENT_SWIZZLE_B;\n\t\tivci.components.a = VK_COMPONENT_SWIZZLE_A;\n\t\tivci.subresourceRange.baseMipLevel = 0;\n\t\tivci.subresourceRange.levelCount = 1;\n\t\tivci.subresourceRange.baseArrayLayer = 0;\n\t\tivci.subresourceRange.layerCount = 6;\n\t\tivci.viewType = VK_IMAGE_VIEW_TYPE_CUBE;\n\t\tivci.flags = 0;\n\n\t\tivci.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;\n\t\tivci.format = colour_imginfo.format;\n\t\tivci.image = targ->colour.image;\n\t\tVkAssert(vkCreateImageView(vk.device, &ivci, vkallocationcb, &targ->colour.view));\n\n\t\tivci.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;\n\t\tivci.format = depth_imginfo.format;\n\t\tivci.image = targ->depth.image;\n\t\tVkAssert(vkCreateImageView(vk.device, &ivci, vkallocationcb, &targ->depth.view));\n\t}\n\n\tfor (f = 0; f < 6; f++)\n\t{\n\t\ttarg->face[f].width = targ->face[f].height = size;\n\n\t\t//per-face view for the framebuffer\n\t\t{\n\t\t\tVkImageViewCreateInfo ivci = {VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO};\n\t\t\tivci.components.r = VK_COMPONENT_SWIZZLE_R;\n\t\t\tivci.components.g = VK_COMPONENT_SWIZZLE_G;\n\t\t\tivci.components.b = VK_COMPONENT_SWIZZLE_B;\n\t\t\tivci.components.a = VK_COMPONENT_SWIZZLE_A;\n\t\t\tivci.subresourceRange.baseMipLevel = 0;\n\t\t\tivci.subresourceRange.levelCount = 1;\n\t\t\tivci.subresourceRange.baseArrayLayer = f;\n\t\t\tivci.subresourceRange.layerCount = 1;\n\t\t\tivci.viewType = VK_IMAGE_VIEW_TYPE_2D;\n\t\t\tivci.flags = 0;\n\n\t\t\tivci.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;\n\t\t\tivci.format = colour_imginfo.format;\n\t\t\tivci.image = targ->colour.image;\n\t\t\tVkAssert(vkCreateImageView(vk.device, &ivci, vkallocationcb, &targ->face[f].colour.view));\n\n\t\t\tivci.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;\n\t\t\tivci.format = depth_imginfo.format;\n\t\t\tivci.image = targ->depth.image;\n\t\t\tVkAssert(vkCreateImageView(vk.device, &ivci, vkallocationcb, &targ->face[f].depth.view));\n\t\t}\n\n\t\ttarg->colour.layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;\n\t\ttarg->depth.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL;\n\n\t\t{\n\t\t\tVkFramebufferCreateInfo fbinfo = {VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO};\n\t\t\tVkImageView attachments[2] = {targ->face[f].colour.view, targ->face[f].depth.view};\n\t\t\tfbinfo.flags = 0;\n\t\t\tfbinfo.renderPass = VK_GetRenderPass(RP_FULLCLEAR|targ->face[f].rpassflags);\n\t\t\tfbinfo.attachmentCount = countof(attachments);\n\t\t\tfbinfo.pAttachments = attachments;\n\t\t\tfbinfo.width = size;\n\t\t\tfbinfo.height = size;\n\t\t\tfbinfo.layers = 1;\n\t\t\tVkAssert(vkCreateFramebuffer(vk.device, &fbinfo, vkallocationcb, &targ->face[f].framebuffer));\n\t\t}\n\n\t\ttarg->face[f].restartinfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;\n\t\ttarg->face[f].restartinfo.pNext = NULL;\n\t\ttarg->face[f].restartinfo.framebuffer = targ->face[f].framebuffer;\n\t\ttarg->face[f].restartinfo.renderArea.offset.x = 0;\n\t\ttarg->face[f].restartinfo.renderArea.offset.y = 0;\n\t\ttarg->face[f].restartinfo.renderArea.extent.width = size;\n\t\ttarg->face[f].restartinfo.renderArea.extent.height = size;\n\t\ttarg->face[f].restartinfo.pClearValues = clearvalues;\n\t}\n\tclearvalues[1].depthStencil.depth = 1;\n}\n\nvoid VKBE_RT_Begin(struct vk_rendertarg *targ)\n{\n\tif (vk.rendertarg == targ)\n\t\treturn;\n\n\tr_refdef.pxrect.x = 0;\n\tr_refdef.pxrect.y = 0;\n\tr_refdef.pxrect.width = targ->width;\n\tr_refdef.pxrect.height = targ->height;\n\tr_refdef.pxrect.maxheight = targ->height;\n\n\tvid.fbpwidth = targ->width;\n\tvid.fbpheight = targ->height;\n\n#if 0\n\ttarg->cbuf = vk.rendertarg->cbuf;\n\tif (vk.rendertarg)\n\t\tvkCmdEndRenderPass(vk.rendertarg->cbuf);\n#else\n\tshaderstate.rc.activepipeline = VK_NULL_HANDLE;\n\ttarg->cbuf = VK_AllocFrameCBuf();\n\n\t{\n\t\tVkCommandBufferBeginInfo begininf = {VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO};\n\t\tVkCommandBufferInheritanceInfo inh = {VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO};\n\t\tbegininf.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;\n\t\tbegininf.pInheritanceInfo = &inh;\n\t\tinh.renderPass = VK_NULL_HANDLE;\t//unused\n\t\tinh.subpass = 0;\t\t\t\t\t//unused\n\t\tinh.framebuffer = VK_NULL_HANDLE;\t//unused\n\t\tinh.occlusionQueryEnable = VK_FALSE;\n\t\tinh.queryFlags = 0;\n\t\tinh.pipelineStatistics = 0;\n\t\tvkBeginCommandBuffer(targ->cbuf, &begininf);\n\t}\n#endif\n\n\ttarg->prevtarg = vk.rendertarg;\n\tvk.rendertarg = targ;\n\n\tVKBE_SelectMode(shaderstate.mode);\n\n\tvkCmdBeginRenderPass(vk.rendertarg->cbuf, &targ->restartinfo, VK_SUBPASS_CONTENTS_INLINE);\n\t//future reuse shouldn't clear stuff\n\tif (targ->restartinfo.clearValueCount)\n\t{\n\t\ttarg->depthcleared = true;\n\t\ttarg->restartinfo.renderPass = VK_GetRenderPass(RP_RESUME|targ->rpassflags);\n\t\ttarg->restartinfo.clearValueCount = 0;\n\t}\n\n\t{\n\t\tVkRect2D wrekt;\n\t\tVkViewport viewport;\n\t\tviewport.x = r_refdef.pxrect.x;\n\t\tviewport.y = r_refdef.pxrect.y;\n\t\tviewport.width = r_refdef.pxrect.width;\n\t\tviewport.height = r_refdef.pxrect.height;\n\t\tviewport.minDepth = 0;\n\t\tviewport.maxDepth = 1;\n\t\tvkCmdSetViewport(vk.rendertarg->cbuf, 0, 1, &viewport);\n\t\twrekt.offset.x = viewport.x;\n\t\twrekt.offset.y = viewport.y;\n\t\twrekt.extent.width = viewport.width;\n\t\twrekt.extent.height = viewport.height;\n\t\tvkCmdSetScissor(vk.rendertarg->cbuf, 0, 1, &wrekt);\n\t}\n}\n\nvoid VKBE_RT_End(struct vk_rendertarg *targ)\n{\n\tif (R2D_Flush)\n\t\tR2D_Flush();\n\n\tvk.rendertarg = vk.rendertarg->prevtarg;\n\n\tvid.fbpwidth = vk.rendertarg->width;\n\tvid.fbpheight = vk.rendertarg->height;\n\n#if 0\n#else\n\tshaderstate.rc.activepipeline = VK_NULL_HANDLE;\n\tvkCmdEndRenderPass(targ->cbuf);\n\tvkEndCommandBuffer(targ->cbuf);\n\n\tVK_Submit_Work(targ->cbuf, VK_NULL_HANDLE, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_NULL_HANDLE, VK_NULL_HANDLE, NULL, NULL);\n\t\n//\t\tVK_Submit_Work(VkCommandBuffer cmdbuf, VkSemaphore semwait, VkPipelineStageFlags semwaitstagemask, VkSemaphore semsignal, VkFence fencesignal, struct vkframe *presentframe, struct vk_fencework *fencedwork)\n#endif\n\n\tVKBE_SelectMode(shaderstate.mode);\n}\n\nstatic qboolean BE_GenerateRefraction(batch_t *batch, shader_t *bs)\n{\n\tfloat oldil;\n\tint oldbem;\n//\tstruct vk_rendertarg *targ;\n\t//these flags require rendering some view as an fbo\n\tif (r_refdef.recurse)\n\t\treturn false;\n\tif (r_refdef.recurse == r_portalrecursion.ival || r_refdef.recurse == R_MAX_RECURSE)\n\t\treturn false;\n\tif (shaderstate.mode != BEM_STANDARD && shaderstate.mode != BEM_DEPTHDARK)\n\t\treturn false;\n\toldbem = shaderstate.mode;\n\toldil = shaderstate.identitylighting;\n//\ttarg = vk.rendertarg;\n\n\tif (bs->flags & SHADER_HASREFLECT)\n\t{\n\t\tvrect_t orect = r_refdef.vrect;\n\t\tpxrect_t oprect = r_refdef.pxrect;\n\n\t\tr_refdef.vrect.x = 0;\n\t\tr_refdef.vrect.y = 0;\n\t\tr_refdef.vrect.width = max(1, vid.fbvwidth*bs->portalfboscale);\n\t\tr_refdef.vrect.height = max(1, vid.fbvheight*bs->portalfboscale);\n\t\tVKBE_RT_Gen(&shaderstate.rt_reflection, NULL, r_refdef.vrect.width, r_refdef.vrect.height, false, RT_IMAGEFLAGS);\n\t\tVKBE_RT_Begin(&shaderstate.rt_reflection);\n\t\tR_DrawPortal(batch, cl.worldmodel->batches, NULL, 1);\n\t\tVKBE_RT_End(&shaderstate.rt_reflection);\n\t\tr_refdef.vrect = orect;\n\t\tr_refdef.pxrect = oprect;\n\t}\n\tif (bs->flags & (SHADER_HASREFRACT|SHADER_HASREFRACTDEPTH))\n\t{\n\t\textern cvar_t r_refract_fbo;\n\t\tif (r_refract_fbo.ival || (bs->flags & SHADER_HASREFRACTDEPTH))\n\t\t{\n\t\t\tvrect_t ovrect = r_refdef.vrect;\n\t\t\tpxrect_t oprect = r_refdef.pxrect;\n\n\t\t\tr_refdef.vrect.x = 0;\n\t\t\tr_refdef.vrect.y = 0;\n\t\t\tr_refdef.vrect.width = vid.fbvwidth/2;\n\t\t\tr_refdef.vrect.height = vid.fbvheight/2;\n\t\t\tVKBE_RT_Gen(&shaderstate.rt_refraction, NULL, r_refdef.vrect.width, r_refdef.vrect.height, false, RT_IMAGEFLAGS);\n\t\t\tVKBE_RT_Begin(&shaderstate.rt_refraction);\n\t\t\tR_DrawPortal(batch, cl.worldmodel->batches, NULL, ((bs->flags & SHADER_HASREFRACTDEPTH)?3:2));\t//fixme\n\t\t\tVKBE_RT_End(&shaderstate.rt_refraction);\n\t\t\tr_refdef.vrect = ovrect;\n\t\t\tr_refdef.pxrect = oprect;\n\n\t\t\tshaderstate.tex_refraction = &shaderstate.rt_refraction.q_colour;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tR_DrawPortal(batch, cl.worldmodel->batches, NULL, 3);\n\t\t\tT_Gen_CurrentRender();\n\t\t\tshaderstate.tex_refraction = shaderstate.tex_currentrender;\n\t\t}\n\t}\n\t/*\n\tif (bs->flags & SHADER_HASRIPPLEMAP)\n\t{\n\t\tvrect_t orect = r_refdef.vrect;\n\t\tpxrect_t oprect = r_refdef.pxrect;\n\t\tr_refdef.vrect.x = 0;\n\t\tr_refdef.vrect.y = 0;\n\t\tr_refdef.vrect.width = vid.fbvwidth/2;\n\t\tr_refdef.vrect.height = vid.fbvheight/2;\n\t\tr_refdef.pxrect.x = 0;\n\t\tr_refdef.pxrect.y = 0;\n\t\tr_refdef.pxrect.width = vid.fbpwidth/2;\n\t\tr_refdef.pxrect.height = vid.fbpheight/2;\n\n\t\tif (!shaderstate.tex_ripplemap)\n\t\t{\n\t\t\t//FIXME: can we use RGB8 instead?\n\t\t\tshaderstate.tex_ripplemap = Image_CreateTexture(\"***tex_ripplemap***\", NULL, 0);\n\t\t\tif (!shaderstate.tex_ripplemap->num)\n\t\t\t\tqglGenTextures(1, &shaderstate.tex_ripplemap->num);\n\t\t}\n\t\tif (shaderstate.tex_ripplemap->width != r_refdef.pxrect.width || shaderstate.tex_ripplemap->height != r_refdef.pxrect.height)\n\t\t{\n\t\t\tshaderstate.tex_ripplemap->width = r_refdef.pxrect.width;\n\t\t\tshaderstate.tex_ripplemap->height = r_refdef.pxrect.height;\n\t\t\tGL_MTBind(0, GL_TEXTURE_2D, shaderstate.tex_ripplemap);\n\t\t\tqglTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F_ARB, r_refdef.pxrect.width, r_refdef.pxrect.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);\n\t\t\tqglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);\n\t\t\tqglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);\n\t\t\tqglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);\n\t\t\tqglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);\n\t\t}\n\t\toldfbo = GLBE_FBO_Update(&shaderstate.fbo_reflectrefrac, 0, &shaderstate.tex_ripplemap, 1, r_nulltex, r_refdef.pxrect.width, r_refdef.pxrect.height, 0);\n\t\tr_refdef.pxrect.maxheight = shaderstate.fbo_reflectrefrac.rb_size[1];\n\t\tGL_ViewportUpdate();\n\n\t\tqglClearColor(0, 0, 0, 1);\n\t\tqglClear(GL_COLOR_BUFFER_BIT);\n\n\t\tr_refdef.vrect.x = 0;\n\t\tr_refdef.vrect.y = 0;\n\t\tr_refdef.vrect.width = vid.fbvwidth;\n\t\tr_refdef.vrect.height = vid.fbvheight;\n\t\tBE_RT_Begin(&shaderstate.rt_refraction, vid.fbpwidth, vid.fbpheight);\n\n\t\tr_refdef.recurse+=1; //paranoid, should stop potential infinite loops\n\t\tGLBE_SubmitMeshes(cl.worldmodel->batches, SHADER_SORT_RIPPLE, SHADER_SORT_RIPPLE);\n\t\tr_refdef.recurse-=1;\n\n\t\tr_refdef.vrect = orect;\n\t\tr_refdef.pxrect = oprect;\n\t\tBE_RT_End();\n\t}\n\t*/\n\tVKBE_SelectMode(oldbem);\n\tshaderstate.identitylighting = oldil;\n\n\treturn true;\n}\n\nstatic void BE_SubmitMeshesSortList(batch_t *sortlist)\n{\n\tbatch_t *batch;\n\tfor (batch = sortlist; batch; batch = batch->next)\n\t{\n\t\tif (batch->meshes == batch->firstmesh)\n\t\t\tcontinue;\n\n\t\tif (batch->buildmeshes)\n\t\t\tbatch->buildmeshes(batch);\n\n\t\tif (batch->shader->flags & SHADER_NODLIGHT)\n\t\t\tif (shaderstate.mode == BEM_LIGHT)\n\t\t\t\tcontinue;\n\n\t\tif (batch->shader->flags & SHADER_SKY)\n\t\t{\n\t\t\tif (shaderstate.mode == BEM_STANDARD || shaderstate.mode == BEM_DEPTHDARK)\n\t\t\t{\n\t\t\t\tif (R_DrawSkyChain (batch))\n\t\t\t\t\tcontinue;\n\t\t\t}\n\t\t\telse if (shaderstate.mode != BEM_FOG && shaderstate.mode != BEM_CREPUSCULAR && shaderstate.mode != BEM_WIREFRAME)\n\t\t\t\tcontinue;\n\t\t}\n\n\t\tif ((batch->shader->flags & (SHADER_HASREFLECT | SHADER_HASREFRACT | SHADER_HASRIPPLEMAP)) && shaderstate.mode != BEM_WIREFRAME)\n\t\t\tif (!BE_GenerateRefraction(batch, batch->shader))\n\t\t\t\tcontinue;\n\n\t\tVKBE_SubmitBatch(batch);\n\t}\n}\n\n\n/*generates a new modelview matrix, as well as vpn vectors*/\nstatic void R_MirrorMatrix(plane_t *plane)\n{\n\tfloat mirror[16];\n\tfloat view[16];\n\tfloat result[16];\n\n\tvec3_t pnorm;\n\tVectorNegate(plane->normal, pnorm);\n\n\tmirror[0] = 1-2*pnorm[0]*pnorm[0];\n\tmirror[1] = -2*pnorm[0]*pnorm[1];\n\tmirror[2] = -2*pnorm[0]*pnorm[2];\n\tmirror[3] = 0;\n\n\tmirror[4] = -2*pnorm[1]*pnorm[0];\n\tmirror[5] = 1-2*pnorm[1]*pnorm[1];\n\tmirror[6] = -2*pnorm[1]*pnorm[2] ;\n\tmirror[7] = 0;\n\n\tmirror[8]  = -2*pnorm[2]*pnorm[0];\n\tmirror[9]  = -2*pnorm[2]*pnorm[1];\n\tmirror[10] = 1-2*pnorm[2]*pnorm[2];\n\tmirror[11] = 0;\n\n\tmirror[12] = -2*pnorm[0]*plane->dist;\n\tmirror[13] = -2*pnorm[1]*plane->dist;\n\tmirror[14] = -2*pnorm[2]*plane->dist;\n\tmirror[15] = 1;\n\n\tview[0] = vpn[0];\n\tview[1] = vpn[1];\n\tview[2] = vpn[2];\n\tview[3] = 0;\n\n\tview[4] = -vright[0];\n\tview[5] = -vright[1];\n\tview[6] = -vright[2];\n\tview[7] = 0;\n\n\tview[8]  = vup[0];\n\tview[9]  = vup[1];\n\tview[10] = vup[2];\n\tview[11] = 0;\n\n\tview[12] = r_refdef.vieworg[0];\n\tview[13] = r_refdef.vieworg[1];\n\tview[14] = r_refdef.vieworg[2];\n\tview[15] = 1;\n\n\tVectorMA(r_refdef.vieworg, 0.25, plane->normal, r_refdef.pvsorigin);\n\n\tMatrix4_Multiply(mirror, view, result);\n\n\tvpn[0] = result[0];\n\tvpn[1] = result[1];\n\tvpn[2] = result[2];\n\n\tvright[0] = -result[4];\n\tvright[1] = -result[5];\n\tvright[2] = -result[6];\n\n\tvup[0] = result[8];\n\tvup[1] = result[9];\n\tvup[2] = result[10];\n\n\tr_refdef.vieworg[0] = result[12];\n\tr_refdef.vieworg[1] = result[13];\n\tr_refdef.vieworg[2] = result[14];\n}\nstatic entity_t *R_NearestPortal(plane_t *plane)\n{\n\tint i;\n\tentity_t *best = NULL;\n\tfloat dist, bestd = 0;\n\t//for q3-compat, portals on world scan for a visedict to use for their view.\n\tfor (i = 0; i < cl_numvisedicts; i++)\n\t{\n\t\tif (cl_visedicts[i].rtype == RT_PORTALSURFACE)\n\t\t{\n\t\t\tdist = DotProduct(cl_visedicts[i].origin, plane->normal)-plane->dist;\n\t\t\tdist = fabs(dist);\n\t\t\tif (dist < 64 && (!best || dist < bestd))\n\t\t\t\tbest = &cl_visedicts[i];\n\t\t}\n\t}\n\treturn best;\n}\n\nstatic void TransformCoord(vec3_t in, vec3_t planea[3], vec3_t planeo, vec3_t viewa[3], vec3_t viewo, vec3_t result)\n{\n\tint\t\ti;\n\tvec3_t\tlocal;\n\tvec3_t\ttransformed;\n\tfloat\td;\n\n\tlocal[0] = in[0] - planeo[0];\n\tlocal[1] = in[1] - planeo[1];\n\tlocal[2] = in[2] - planeo[2];\n\n\tVectorClear(transformed);\n\tfor ( i = 0 ; i < 3 ; i++ )\n\t{\n\t\td = DotProduct(local, planea[i]);\n\t\tVectorMA(transformed, d, viewa[i], transformed);\n\t}\n\n\tresult[0] = transformed[0] + viewo[0];\n\tresult[1] = transformed[1] + viewo[1];\n\tresult[2] = transformed[2] + viewo[2];\n}\nstatic void TransformDir(vec3_t in, vec3_t planea[3], vec3_t viewa[3], vec3_t result)\n{\n\tint\t\ti;\n\tfloat\td;\n\tvec3_t tmp;\n\n\tVectorCopy(in, tmp);\n\n\tVectorClear(result);\n\tfor ( i = 0 ; i < 3 ; i++ )\n\t{\n\t\td = DotProduct(tmp, planea[i]);\n\t\tVectorMA(result, d, viewa[i], result);\n\t}\n}\n\nvoid R_ObliqueNearClip(float *viewmat, mplane_t *wplane);\nvoid CL_DrawDebugPlane(float *normal, float dist, float r, float g, float b, qboolean enqueue);\nstatic void R_DrawPortal(batch_t *batch, batch_t **blist, batch_t *depthmasklist[2], int portaltype)\n{\n\tentity_t *view;\n\tplane_t plane, oplane;\n\tfloat vmat[16];\n\trefdef_t oldrefdef;\n\tvec3_t r;\n\tint i;\n\tmesh_t *mesh = batch->mesh[batch->firstmesh];\n\tpvsbuffer_t newvis;\n\tfloat ivmat[16], trmat[16];\n\n\tif (r_refdef.recurse >= R_MAX_RECURSE-1)\n\t\treturn;\n\n\tif (!mesh->xyz_array)\n\t\treturn;\n\n\tif (!mesh->normals_array)\n\t{\n\t\tVectorSet(plane.normal, 0, 0, 1);\n\t}\n\telse\n\t{\n\t\tVectorCopy(mesh->normals_array[0], plane.normal);\n\t}\n\n\tif (batch->ent == &r_worldentity)\n\t{\n\t\tplane.dist = DotProduct(mesh->xyz_array[0], plane.normal);\n\t}\n\telse\n\t{\n\t\tvec3_t point;\n\t\tVectorCopy(plane.normal, oplane.normal);\n\t\t//rotate the surface normal around its entity's matrix\n\t\tplane.normal[0] = oplane.normal[0]*batch->ent->axis[0][0] + oplane.normal[1]*batch->ent->axis[1][0] + oplane.normal[2]*batch->ent->axis[2][0];\n\t\tplane.normal[1] = oplane.normal[0]*batch->ent->axis[0][1] + oplane.normal[1]*batch->ent->axis[1][1] + oplane.normal[2]*batch->ent->axis[2][1];\n\t\tplane.normal[2] = oplane.normal[0]*batch->ent->axis[0][2] + oplane.normal[1]*batch->ent->axis[1][2] + oplane.normal[2]*batch->ent->axis[2][2];\n\n\t\t//rotate some point on the mesh around its entity's matrix\n\t\tpoint[0] = mesh->xyz_array[0][0]*batch->ent->axis[0][0] + mesh->xyz_array[0][1]*batch->ent->axis[1][0] + mesh->xyz_array[0][2]*batch->ent->axis[2][0] + batch->ent->origin[0];\n\t\tpoint[1] = mesh->xyz_array[0][0]*batch->ent->axis[0][1] + mesh->xyz_array[0][1]*batch->ent->axis[1][1] + mesh->xyz_array[0][2]*batch->ent->axis[2][1] + batch->ent->origin[1];\n\t\tpoint[2] = mesh->xyz_array[0][0]*batch->ent->axis[0][2] + mesh->xyz_array[0][1]*batch->ent->axis[1][2] + mesh->xyz_array[0][2]*batch->ent->axis[2][2] + batch->ent->origin[2];\n\n\t\t//now we can figure out the plane dist\n\t\tplane.dist = DotProduct(point, plane.normal);\n\t}\n\n\t//if we're too far away from the surface, don't draw anything\n\tif (batch->shader->flags & SHADER_AGEN_PORTAL)\n\t{\n\t\t/*there's a portal alpha blend on that surface, that fades out after this distance*/\n\t\tif (DotProduct(r_refdef.vieworg, plane.normal)-plane.dist > batch->shader->portaldist)\n\t\t\treturn;\n\t}\n\t//if we're behind it, then also don't draw anything. for our purposes, behind is when the entire near clipplane is behind.\n\tif (DotProduct(r_refdef.vieworg, plane.normal)-plane.dist < -r_refdef.mindist)\n\t\treturn;\n\n\tTRACE((\"R_DrawPortal: portal type %i\\n\", portaltype));\n\n\toldrefdef = r_refdef;\n\tr_refdef.recurse+=1;\n\n\tr_refdef.externalview = true;\n\n\tswitch(portaltype)\n\t{\n\tcase 1: /*fbo explicit mirror (fucked depth, working clip plane)*/\n\t\t//fixme: pvs is surely wrong?\n//\t\tr_refdef.flipcull ^= SHADER_CULL_FLIP;\n\t\tR_MirrorMatrix(&plane);\n\t\tMatrix4x4_CM_ModelViewMatrixFromAxis(vmat, vpn, vright, vup, r_refdef.vieworg);\n\n\t\tVectorCopy(mesh->xyz_array[0], r_refdef.pvsorigin);\n\t\tfor (i = 1; i < mesh->numvertexes; i++)\n\t\t\tVectorAdd(r_refdef.pvsorigin, mesh->xyz_array[i], r_refdef.pvsorigin);\n\t\tVectorScale(r_refdef.pvsorigin, 1.0/mesh->numvertexes, r_refdef.pvsorigin);\n\t\tbreak;\n\t\n\tcase 2:\t/*fbo refraction (fucked depth, working clip plane)*/\n\tcase 3:\t/*screen copy refraction (screen depth, fucked clip planes)*/\n\t\t/*refraction image (same view, just with things culled*/\n\t\tr_refdef.externalview = oldrefdef.externalview;\n\t\tVectorNegate(plane.normal, plane.normal);\n\t\tplane.dist = -plane.dist;\n\n\t\t//use the player's origin for r_viewleaf, because there's not much we can do anyway*/\n\t\tVectorCopy(r_origin, r_refdef.pvsorigin);\n\n\t\tif (cl.worldmodel && cl.worldmodel->funcs.ClusterPVS && !r_novis.ival)\n\t\t{\n\t\t\tint clust, i, j;\n\t\t\tfloat d;\n\t\t\tvec3_t point;\n\t\t\tr_refdef.forcevis = true;\n\t\t\tr_refdef.forcedvis = NULL;\n\t\t\tnewvis.buffer = alloca(newvis.buffersize=cl.worldmodel->pvsbytes);\n\t\t\tfor (i = batch->firstmesh; i < batch->meshes; i++)\n\t\t\t{\n\t\t\t\tmesh = batch->mesh[i];\n\t\t\t\tVectorClear(point);\n\t\t\t\tfor (j = 0; j < mesh->numvertexes; j++)\n\t\t\t\t\tVectorAdd(point, mesh->xyz_array[j], point);\n\t\t\t\tVectorScale(point, 1.0f/mesh->numvertexes, point);\n\t\t\t\td = DotProduct(point, plane.normal) - plane.dist;\n\t\t\t\td += 0.1;\t//an epsilon on the far side\n\t\t\t\tVectorMA(point, d, plane.normal, point);\n\n\t\t\t\tclust = cl.worldmodel->funcs.ClusterForPoint(cl.worldmodel, point, NULL);\n\t\t\t\tif (i == batch->firstmesh)\n\t\t\t\t\tr_refdef.forcedvis = cl.worldmodel->funcs.ClusterPVS(cl.worldmodel, clust, &newvis, PVM_REPLACE);\n\t\t\t\telse\n\t\t\t\t\tr_refdef.forcedvis = cl.worldmodel->funcs.ClusterPVS(cl.worldmodel, clust, &newvis, PVM_MERGE);\n\t\t\t}\n//\t\t\tmemset(newvis, 0xff, pvsbytes);\n\t\t}\n\t\tMatrix4x4_CM_ModelViewMatrixFromAxis(vmat, vpn, vright, vup, r_refdef.vieworg);\n\t\tbreak;\n\n\tcase 0:\t\t/*q3 portal*/\n\tdefault:\n#ifdef CSQC_DAT\n\t\tif (CSQC_SetupToRenderPortal(batch->ent->keynum))\n\t\t{\n\t\t\toplane = plane;\n\n\t\t\t//transform the old surface plane into the new view matrix\n\t\t\tMatrix4_Invert(r_refdef.m_view, ivmat);\n\t\t\tMatrix4x4_CM_ModelViewMatrixFromAxis(vmat, vpn, vright, vup, r_refdef.vieworg);\n\t\t\tMatrix4_Multiply(ivmat, vmat, trmat);\n\t\t\tplane.normal[0] = -(oplane.normal[0] * trmat[0] + oplane.normal[1] * trmat[1] + oplane.normal[2] * trmat[2]);\n\t\t\tplane.normal[1] = -(oplane.normal[0] * trmat[4] + oplane.normal[1] * trmat[5] + oplane.normal[2] * trmat[6]);\n\t\t\tplane.normal[2] = -(oplane.normal[0] * trmat[8] + oplane.normal[1] * trmat[9] + oplane.normal[2] * trmat[10]);\n\t\t\tplane.dist = -oplane.dist + trmat[12]*oplane.normal[0] + trmat[13]*oplane.normal[1] + trmat[14]*oplane.normal[2];\n\n\t\t\tif (Cvar_Get(\"temp_useplaneclip\", \"1\", 0, \"temp\")->ival)\n\t\t\t\tportaltype = 1;\t//make sure the near clipplane is used.\n\t\t}\n\t\telse\n#endif\n\t\t\tif (!(view = R_NearestPortal(&plane)) || VectorCompare(view->origin, view->oldorigin))\n\t\t{\n\t\t\t//a portal with no portal entity, or a portal rentity with an origin equal to its oldorigin, is a mirror.\n//\t\t\tr_refdef.flipcull ^= SHADER_CULL_FLIP;\n\t\t\tR_MirrorMatrix(&plane);\n\t\t\tMatrix4x4_CM_ModelViewMatrixFromAxis(vmat, vpn, vright, vup, r_refdef.vieworg);\n\n\t\t\tVectorCopy(mesh->xyz_array[0], r_refdef.pvsorigin);\n\t\t\tfor (i = 1; i < mesh->numvertexes; i++)\n\t\t\t\tVectorAdd(r_refdef.pvsorigin, mesh->xyz_array[i], r_refdef.pvsorigin);\n\t\t\tVectorScale(r_refdef.pvsorigin, 1.0/mesh->numvertexes, r_refdef.pvsorigin);\n\n\t\t\tportaltype = 1;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tfloat d;\n\t\t\tvec3_t paxis[3], porigin, vaxis[3], vorg;\n\t\t\tvoid PerpendicularVector( vec3_t dst, const vec3_t src );\n\n\t\t\toplane = plane;\n\n\t\t\t/*calculate where the surface is meant to be*/\n\t\t\tVectorCopy(mesh->normals_array[0], paxis[0]);\n\t\t\tPerpendicularVector(paxis[1], paxis[0]);\n\t\t\tCrossProduct(paxis[0], paxis[1], paxis[2]);\n\t\t\td = DotProduct(view->origin, plane.normal) - plane.dist;\n\t\t\tVectorMA(view->origin, -d, paxis[0], porigin);\n\n\t\t\t/*grab the camera origin*/\n\t\t\tVectorNegate(view->axis[0], vaxis[0]);\n\t\t\tVectorNegate(view->axis[1], vaxis[1]);\n\t\t\tVectorCopy(view->axis[2], vaxis[2]);\n\t\t\tVectorCopy(view->oldorigin, vorg);\n\n\t\t\tVectorCopy(vorg, r_refdef.pvsorigin);\n\n\t\t\t/*rotate it a bit*/\n\t\t\tif (view->framestate.g[FS_REG].frame[1])\t//oldframe\n\t\t\t{\n\t\t\t\tif (view->framestate.g[FS_REG].frame[0])\t//newframe\n\t\t\t\t\td = realtime * view->framestate.g[FS_REG].frame[0];\t//newframe\n\t\t\t\telse\n\t\t\t\t\td = view->skinnum + sin(realtime)*4;\n\t\t\t}\n\t\t\telse\n\t\t\t\td = view->skinnum;\n\n\t\t\tif (d)\n\t\t\t{\n\t\t\t\tvec3_t rdir;\n\t\t\t\tVectorCopy(vaxis[1], rdir);\n\t\t\t\tRotatePointAroundVector(vaxis[1], vaxis[0], rdir, d);\n\t\t\t\tCrossProduct(vaxis[0], vaxis[1], vaxis[2]);\n\t\t\t}\n\n\t\t\tTransformCoord(oldrefdef.vieworg, paxis, porigin, vaxis, vorg, r_refdef.vieworg);\n\t\t\tTransformDir(vpn, paxis, vaxis, vpn);\n\t\t\tTransformDir(vright, paxis, vaxis, vright);\n\t\t\tTransformDir(vup, paxis, vaxis, vup);\n\t\t\tMatrix4x4_CM_ModelViewMatrixFromAxis(vmat, vpn, vright, vup, r_refdef.vieworg);\n\n\t\t\t//transform the old surface plane into the new view matrix\n\t\t\tif (Matrix4_Invert(r_refdef.m_view, ivmat))\n\t\t\t{\n\t\t\t\tMatrix4_Multiply(ivmat, vmat, trmat);\n\t\t\t\tplane.normal[0] = -(oplane.normal[0] * trmat[0] + oplane.normal[1] * trmat[1] + oplane.normal[2] * trmat[2]);\n\t\t\t\tplane.normal[1] = -(oplane.normal[0] * trmat[4] + oplane.normal[1] * trmat[5] + oplane.normal[2] * trmat[6]);\n\t\t\t\tplane.normal[2] = -(oplane.normal[0] * trmat[8] + oplane.normal[1] * trmat[9] + oplane.normal[2] * trmat[10]);\n\t\t\t\tplane.dist = -oplane.dist + trmat[12]*oplane.normal[0] + trmat[13]*oplane.normal[1] + trmat[14]*oplane.normal[2];\n\t\t\t\tportaltype = 1;\n\t\t\t}\n\t\t}\n\t\tbreak;\n\t}\n\n\t/*FIXME: can we get away with stenciling the screen?*/\n\t/*Add to frustum culling instead of clip planes?*/\n/*\tif (qglClipPlane && portaltype)\n\t{\n\t\tGLdouble glplane[4];\n\t\tglplane[0] = plane.normal[0];\n\t\tglplane[1] = plane.normal[1];\n\t\tglplane[2] = plane.normal[2];\n\t\tglplane[3] = plane.dist;\n\t\tqglClipPlane(GL_CLIP_PLANE0, glplane);\n\t\tqglEnable(GL_CLIP_PLANE0);\n\t}\n*/\t//fixme: we can probably scissor a smaller frusum\n\tR_SetFrustum (r_refdef.m_projection_std, vmat);\n\tif (r_refdef.frustum_numplanes < MAXFRUSTUMPLANES)\n\t{\n\t\textern int SignbitsForPlane (mplane_t *out);\n\t\tmplane_t fp;\n\t\tVectorCopy(plane.normal, fp.normal);\n\t\tfp.dist = plane.dist;\n\n//\t\tif (DotProduct(fp.normal, vpn) < 0)\n//\t\t{\n//\t\t\tVectorNegate(fp.normal, fp.normal);\n//\t\t\tfp.dist *= -1;\n//\t\t}\n\n\t\tfp.type = PLANE_ANYZ;\n\t\tfp.signbits = SignbitsForPlane (&fp);\n\n\t\tif (portaltype == 1 || portaltype == 2)\n\t\t\tR_ObliqueNearClip(vmat, &fp);\n\n\t\t//our own culling should be an epsilon forwards so we don't still draw things behind the line due to precision issues.\n\t\tfp.dist += 0.01;\n\t\tr_refdef.frustum[r_refdef.frustum_numplanes++] = fp;\n\t}\n\n\t//force culling to update to match the new front face.\n//\tmemcpy(r_refdef.m_view, vmat, sizeof(float)*16);\n#if 0\n\tif (depthmasklist)\n\t{\n\t\t/*draw already-drawn portals as depth-only, to ensure that their contents are not harmed*/\n\t\t/*we can only do this AFTER the oblique perspective matrix is calculated, to avoid depth inconsistancies, while we still have the old view matrix*/\n\t\tint i;\n\t\tbatch_t *dmask = NULL;\n\t\t//portals to mask are relative to the old view still.\n\t\tGLBE_SelectEntity(&r_worldentity);\n\t\tcurrententity = NULL;\n\t\tif (gl_config.arb_depth_clamp)\n\t\t\tqglEnable(GL_DEPTH_CLAMP_ARB);\t//ignore the near clip plane(ish), this means nearer portals can still mask further ones.\n\t\tGL_ForceDepthWritable();\n\t\tGLBE_SelectMode(BEM_DEPTHONLY);\n\t\tfor (i = 0; i < 2; i++)\n\t\t{\n\t\t\tfor (dmask = depthmasklist[i]; dmask; dmask = dmask->next)\n\t\t\t{\n\t\t\t\tif (dmask == batch)\n\t\t\t\t\tcontinue;\n\t\t\t\tif (dmask->meshes == dmask->firstmesh)\n\t\t\t\t\tcontinue;\n\t\t\t\tGLBE_SubmitBatch(dmask);\n\t\t\t}\n\t\t}\n\t\tGLBE_SelectMode(BEM_STANDARD);\n\t\tif (gl_config.arb_depth_clamp)\n\t\t\tqglDisable(GL_DEPTH_CLAMP_ARB);\n\n\t\tcurrententity = NULL;\n\t}\n#endif\n\n\tcurrententity = NULL;\n\n\t//now determine the stuff the backend will use.\n\tmemcpy(r_refdef.m_view, vmat, sizeof(float)*16);\n\tVectorAngles(vpn, vup, r_refdef.viewangles, false);\n\tVectorCopy(r_refdef.vieworg, r_origin);\n\n\t//determine r_refdef.flipcull & SHADER_CULL_FLIP based upon whether right is right or not.\n\tCrossProduct(vpn, vup, r);\n\tif (DotProduct(r, vright) < 0)\n\t\tr_refdef.flipcull |= SHADER_CULL_FLIP;\n\telse\n\t\tr_refdef.flipcull &= ~SHADER_CULL_FLIP;\n\tif (r_refdef.m_projection_std[5]<0)\n\t\tr_refdef.flipcull ^= SHADER_CULL_FLIP;\n\n\tVKBE_SelectEntity(&r_worldentity);\n\n\tSurf_SetupFrame();\n\tSurf_DrawWorld();\n\t//FIXME: just call Surf_DrawWorld instead?\n//\tR_RenderScene();\n\n#if 0\n\tif (r_portaldrawplanes.ival)\n\t{\n\t\t//the front of the plane should generally point away from the camera, and will be drawn in bright green. woo\n\t\tCL_DrawDebugPlane(plane.normal, plane.dist+0.01, 0.0, 0.5, 0.0, false);\n\t\tCL_DrawDebugPlane(plane.normal, plane.dist-0.01, 0.0, 0.5, 0.0, false);\n\t\t//the back of the plane points towards the camera, and will be drawn in blue, for the luls\n\t\tVectorNegate(plane.normal, plane.normal);\n\t\tplane.dist *= -1;\n\t\tCL_DrawDebugPlane(plane.normal, plane.dist+0.01, 0.0, 0.0, 0.2, false);\n\t\tCL_DrawDebugPlane(plane.normal, plane.dist-0.01, 0.0, 0.0, 0.2, false);\n\t}\n#endif\n\n\n\tr_refdef = oldrefdef;\n\n\t/*broken stuff*/\n\tAngleVectors (r_refdef.viewangles, vpn, vright, vup);\n\tVectorCopy (r_refdef.vieworg, r_origin);\n\n\tVKBE_SelectEntity(&r_worldentity);\n\n\tTRACE((\"GLR_DrawPortal: portal drawn\\n\"));\n\n\tcurrententity = NULL;\n}\n\nstatic void BE_SubmitMeshesPortals(batch_t **worldlist, batch_t *dynamiclist)\n{\n\tbatch_t *batch, *old;\n\tint i;\n\t/*attempt to draw portal shaders*/\n\tif (shaderstate.mode == BEM_STANDARD)\n\t{\n\t\tfor (i = 0; i < 2; i++)\n\t\t{\n\t\t\tfor (batch = i?dynamiclist:worldlist[SHADER_SORT_PORTAL]; batch; batch = batch->next)\n\t\t\t{\n\t\t\t\tif (batch->meshes == batch->firstmesh)\n\t\t\t\t\tcontinue;\n\n\t\t\t\tif (batch->buildmeshes)\n\t\t\t\t\tbatch->buildmeshes(batch);\n\n\t\t\t\t/*draw already-drawn portals as depth-only, to ensure that their contents are not harmed*/\n\t\t\t\tVKBE_SelectMode(BEM_DEPTHONLY);\n\t\t\t\tfor (old = worldlist[SHADER_SORT_PORTAL]; old && old != batch; old = old->next)\n\t\t\t\t{\n\t\t\t\t\tif (old->meshes == old->firstmesh)\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\tVKBE_SubmitBatch(old);\n\t\t\t\t}\n\t\t\t\tif (!old)\n\t\t\t\t{\n\t\t\t\t\tfor (old = dynamiclist; old != batch; old = old->next)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (old->meshes == old->firstmesh)\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\tVKBE_SubmitBatch(old);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tVKBE_SelectMode(BEM_STANDARD);\n\n\t\t\t\tR_DrawPortal(batch, worldlist, NULL, 0);\n\n\t\t\t\t{\n\t\t\t\t\tVkClearAttachment clr;\n\t\t\t\t\tVkClearRect rect;\n\t\t\t\t\tclr.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;\n\t\t\t\t\tclr.clearValue.depthStencil.depth = 1;\n\t\t\t\t\tclr.clearValue.depthStencil.stencil = 0;\n\t\t\t\t\tclr.colorAttachment = 1;\n\t\t\t\t\trect.rect.offset.x = r_refdef.pxrect.x;\n\t\t\t\t\trect.rect.offset.y = r_refdef.pxrect.y;\n\t\t\t\t\trect.rect.extent.width = r_refdef.pxrect.width;\n\t\t\t\t\trect.rect.extent.height = r_refdef.pxrect.height;\n\t\t\t\t\trect.layerCount = 1;\n\t\t\t\t\trect.baseArrayLayer = 0;\n\t\t\t\t\tvkCmdClearAttachments(vk.rendertarg->cbuf, 1, &clr, 1, &rect);\n\t\t\t\t}\n\t\t\t\tVKBE_SelectMode(BEM_DEPTHONLY);\n\t\t\t\tVKBE_SubmitBatch(batch);\n\t\t\t\tVKBE_SelectMode(BEM_STANDARD);\n\t\t\t}\n\t\t}\n\t}\n}\n\nvoid VKBE_SubmitMeshes (batch_t **worldbatches, batch_t **blist, int first, int stop)\n{\n\tint i;\n\n\tfor (i = first; i < stop; i++)\n\t{\n\t\tif (worldbatches)\n\t\t{\n\t\t\tif (i == SHADER_SORT_PORTAL  && !r_refdef.recurse)\n\t\t\t\tBE_SubmitMeshesPortals(worldbatches, blist[i]);\n\n\t\t\tBE_SubmitMeshesSortList(worldbatches[i]);\n\t\t}\n\t\tBE_SubmitMeshesSortList(blist[i]);\n\t}\n}\n\n#ifdef RTLIGHTS\n//FIXME: needs context for threading\nvoid VKBE_BaseEntTextures(const qbyte *scenepvs, const int *sceneareas)\n{\n\tbatch_t *batches[SHADER_SORT_COUNT];\n\tBE_GenModelBatches(batches, shaderstate.curdlight, shaderstate.mode, scenepvs, sceneareas);\n\tVKBE_SubmitMeshes(NULL, batches, SHADER_SORT_PORTAL, SHADER_SORT_SEETHROUGH+1);\n\tVKBE_SelectEntity(&r_worldentity);\n}\n\nstruct vk_shadowbuffer\n{\n\tqboolean isstatic;\n\n\tVkBuffer vbuffer;\n\tVkDeviceSize voffset;\n\tvk_poolmem_t vmemory;\n\tunsigned int numverts;\n\n\tVkBuffer ibuffer;\n\tVkDeviceSize ioffset;\n\tvk_poolmem_t imemory;\n\tunsigned int numindicies;\n};\n//FIXME: needs context for threading\nstruct vk_shadowbuffer *VKBE_GenerateShadowBuffer(vecV_t *verts, int numverts, index_t *indicies, int numindicies, qboolean istemp)\n{\n\tstatic struct vk_shadowbuffer tempbuf;\n\tif (!numverts || !numindicies)\n\t\treturn NULL;\n\tif (istemp)\n\t{\n\t\tstruct vk_shadowbuffer *buf = &tempbuf;\n\t\tvoid *fte_restrict map;\n\n\t\tmap = VKBE_AllocateStreamingSpace(DB_VBO, sizeof(*verts)*numverts, &buf->vbuffer, &buf->voffset);\n\t\tmemcpy(map, verts, sizeof(*verts)*numverts);\n\t\tbuf->numverts = numverts;\n\n\t\tmap = VKBE_AllocateStreamingSpace(DB_EBO, sizeof(*indicies)*numindicies, &buf->ibuffer, &buf->ioffset);\n\t\tmemcpy(map, indicies, sizeof(*indicies)*numindicies);\n\t\tbuf->numindicies = numindicies;\n\t\treturn buf;\n\t}\n\telse\n\t{\n\t\t//FIXME: these buffers should really be some subsection of a larger buffer\n\t\tstruct vk_shadowbuffer *buf = BZ_Malloc(sizeof(*buf));\n\t\tstruct stagingbuf vbuf;\n\t\tvoid *fte_restrict map;\n\t\tbuf->isstatic = true;\n\n\t\tmap = VKBE_CreateStagingBuffer(&vbuf, sizeof(*verts) * numverts, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT);\n\t\tmemcpy(map, verts, sizeof(*verts) * numverts);\n\t\tbuf->vbuffer = VKBE_FinishStaging(&vbuf, &buf->vmemory);\n\t\tbuf->voffset = 0;\n\t\tbuf->numverts = numverts;\n\n\t\tmap = VKBE_CreateStagingBuffer(&vbuf, sizeof(*indicies) * numindicies, VK_BUFFER_USAGE_INDEX_BUFFER_BIT);\n\t\tmemcpy(map, indicies, sizeof(*indicies) * numindicies);\n\t\tbuf->ibuffer = VKBE_FinishStaging(&vbuf, &buf->imemory);\n\t\tbuf->ioffset = 0;\n\t\tbuf->numindicies = numindicies;\n\t\treturn buf;\n\t}\n}\nstatic void VKBE_DestroyShadowBuffer_Delayed(void *ctx)\n{\n\tstruct vk_shadowbuffer *buf = ctx;\n\tvkDestroyBuffer(vk.device, buf->vbuffer, vkallocationcb);\n\tvkDestroyBuffer(vk.device, buf->ibuffer, vkallocationcb);\n\tVK_ReleasePoolMemory(&buf->vmemory);\n\tVK_ReleasePoolMemory(&buf->imemory);\n}\nvoid VKBE_DestroyShadowBuffer(struct vk_shadowbuffer *buf)\n{\n\tif (buf && buf->isstatic)\n\t{\n\t\tVK_AtFrameEnd(VKBE_DestroyShadowBuffer_Delayed, buf, sizeof(*buf));\n\t\tZ_Free(buf);\n\t}\n}\n\n//draws all depth-only surfaces from the perspective of the light.\n//FIXME: needs context for threading\nvoid VKBE_RenderShadowBuffer(struct vk_shadowbuffer *buf)\n{\n\tshader_t *depthonlyshader;\n\tif (!buf)\n\t\treturn;\n\n\tdepthonlyshader = R_RegisterShader(\"depthonly\", SUF_NONE,\n\t\t\t\t\"{\\n\"\n\t\t\t\t\t\"program depthonly\\n\"\n\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\"depthwrite\\n\"\n\t\t\t\t\t\t\"maskcolor\\n\"\n\t\t\t\t\t\"}\\n\"\n\t\t\t\t\"}\\n\"\n\t\t\t);\n\n\tvkCmdBindVertexBuffers(vk.rendertarg->cbuf, 0, 1, &buf->vbuffer, &buf->voffset);\n\tvkCmdBindIndexBuffer(vk.rendertarg->cbuf, buf->ibuffer, buf->ioffset, VK_INDEX_TYPE);\n\tif (BE_SetupMeshProgram(depthonlyshader->prog, depthonlyshader->passes, 0, buf->numindicies))\n\t\tvkCmdDrawIndexed(vk.rendertarg->cbuf, buf->numindicies, 1, 0, 0, 0);\n}\n\n\nstatic void VK_TerminateShadowMap(void)\n{\n\tstruct shadowmaps_s *shad;\n\tunsigned int sbuf, i;\n\n\tfor (sbuf = 0; sbuf < countof(shaderstate.shadow); sbuf++)\n\t{\n\t\tshad = &shaderstate.shadow[sbuf];\n\t\tif (!shad->image)\n\t\t\tcontinue;\n\n\t\tfor (i = 0; i < countof(shad->buf); i++)\n\t\t{\n\t\t\tvkDestroyImageView(vk.device, shad->buf[i].vimage.view, vkallocationcb);\n\t\t\tvkDestroySampler(vk.device, shad->buf[i].vimage.sampler, vkallocationcb);\n\t\t\tvkDestroyFramebuffer(vk.device, shad->buf[i].framebuffer, vkallocationcb);\n\t\t}\n\t\tvkDestroyImage(vk.device, shad->image, vkallocationcb);\n\t\tvkFreeMemory(vk.device, shad->memory, vkallocationcb);\n\n\t\tshad->width = 0;\n\t\tshad->height = 0;\n\t}\n}\n\nqboolean VKBE_BeginShadowmap(qboolean isspot, uint32_t width, uint32_t height)\n{\n\tstruct shadowmaps_s *shad = &shaderstate.shadow[isspot];\n\tunsigned int sbuf;\n\n//\tconst qboolean altqueue = false;\n\n//\tif (!altqueue)\n\t\tvkCmdEndRenderPass(vk.rendertarg->cbuf);\n\n\tif (shad->width != width || shad->height != height)\n\t{\n\t\t//actually, this will really only happen once per.\n\t\t//so we can be lazy and not free here... check out validation/leak warnings if this changes...\n\n\t\tunsigned int i;\n\t\tVkFramebufferCreateInfo fbinfo = {VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO};\n\t\tVkImageCreateInfo imginfo = {VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO};\n\t\timginfo.format = vk.depthformat;\n\t\timginfo.flags = 0;\n\t\timginfo.imageType = VK_IMAGE_TYPE_2D;\n\t\timginfo.extent.width = width;\n\t\timginfo.extent.height = height;\n\t\timginfo.extent.depth = 1;\n\t\timginfo.mipLevels = 1;\n\t\timginfo.arrayLayers = countof(shad->buf);\n\t\timginfo.samples = VK_SAMPLE_COUNT_1_BIT;\n\t\timginfo.tiling = VK_IMAGE_TILING_OPTIMAL;\n\t\timginfo.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT|VK_IMAGE_USAGE_SAMPLED_BIT;\n\t\timginfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;\n\t\timginfo.queueFamilyIndexCount = 0;\n\t\timginfo.pQueueFamilyIndices = NULL;\n\t\timginfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;\n\t\tVkAssert(vkCreateImage(vk.device, &imginfo, vkallocationcb, &shad->image));\n\t\tDebugSetName(VK_OBJECT_TYPE_IMAGE, (uint64_t)shad->image, \"Shadowmap\");\n\n\t\t{\n\t\t\tVkMemoryRequirements mem_reqs;\n\t\t\tVkMemoryAllocateInfo memAllocInfo = {VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO};\n\t\t\tvkGetImageMemoryRequirements(vk.device, shad->image, &mem_reqs);\n\t\t\tmemAllocInfo.allocationSize = mem_reqs.size;\n\t\t\tmemAllocInfo.memoryTypeIndex = vk_find_memory_try(mem_reqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);\n\t\t\tif (memAllocInfo.memoryTypeIndex == ~0)\n\t\t\t\tmemAllocInfo.memoryTypeIndex = vk_find_memory_require(mem_reqs.memoryTypeBits, 0);\n\t\t\tVkAssert(vkAllocateMemory(vk.device, &memAllocInfo, vkallocationcb, &shad->memory));\n\t\t\tDebugSetName(VK_OBJECT_TYPE_DEVICE_MEMORY, (uint64_t)shad->memory, \"VKBE_BeginShadowmap\");\n\t\t\tVkAssert(vkBindImageMemory(vk.device, shad->image, shad->memory, 0));\n\t\t}\n\n\t\tfbinfo.flags = 0;\n\t\tfbinfo.renderPass = VK_GetRenderPass(RP_DEPTHONLY);\n\t\tfbinfo.attachmentCount = 1;\n\t\tfbinfo.width = width;\n\t\tfbinfo.height = height;\n\t\tfbinfo.layers = 1;\n\t\tfor (i = 0; i < countof(shad->buf); i++)\n\t\t{\n\t\t\tVkImageViewCreateInfo ivci = {VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO};\n\t\t\tivci.format = imginfo.format;\n\t\t\tivci.components.r = VK_COMPONENT_SWIZZLE_R;\n\t\t\tivci.components.g = VK_COMPONENT_SWIZZLE_G;\n\t\t\tivci.components.b = VK_COMPONENT_SWIZZLE_B;\n\t\t\tivci.components.a = VK_COMPONENT_SWIZZLE_A;\n\t\t\tivci.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;\n\t\t\tivci.subresourceRange.baseMipLevel = 0;\n\t\t\tivci.subresourceRange.levelCount = 1;\n\t\t\tivci.subresourceRange.baseArrayLayer = i;\n\t\t\tivci.subresourceRange.layerCount = 1;\n\t\t\tivci.viewType = VK_IMAGE_VIEW_TYPE_2D;\n\t\t\tivci.flags = 0;\n\t\t\tivci.image = shad->image;\n\t\t\tshad->buf[i].vimage.image = shad->image;\n\t\t\tVkAssert(vkCreateImageView(vk.device, &ivci, vkallocationcb, &shad->buf[i].vimage.view));\n\n\t\t\t{\n\t\t\t\tVkSamplerCreateInfo lmsampinfo = {VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO};\n\t\t\t\tlmsampinfo.minFilter = lmsampinfo.magFilter = VK_FILTER_LINEAR;\n\t\t\t\tlmsampinfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;\n\t\t\t\tlmsampinfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;\n\t\t\t\tlmsampinfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;\n\t\t\t\tlmsampinfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;\n\t\t\t\tlmsampinfo.mipLodBias = 0.0;\n\t\t\t\tlmsampinfo.anisotropyEnable = VK_FALSE;\n\t\t\t\tlmsampinfo.maxAnisotropy = 1.0;\n\t\t\t\tlmsampinfo.compareEnable = VK_TRUE;\n\t\t\t\tlmsampinfo.compareOp = VK_COMPARE_OP_LESS_OR_EQUAL;\n\t\t\t\tlmsampinfo.minLod = 0;\n\t\t\t\tlmsampinfo.maxLod = 0;\n\t\t\t\tlmsampinfo.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE;\n\t\t\t\tlmsampinfo.unnormalizedCoordinates = VK_FALSE;\n\t\t\t\tVkAssert(vkCreateSampler(vk.device, &lmsampinfo, NULL, &shad->buf[i].vimage.sampler));\n\t\t\t}\n\n\t\t\tshad->buf[i].qimage.vkimage = &shad->buf[i].vimage;\n\t\t\tshad->buf[i].vimage.layout = VK_IMAGE_LAYOUT_UNDEFINED;\n\n\t\t\tfbinfo.pAttachments = &shad->buf[i].vimage.view;\n\t\t\tVkAssert(vkCreateFramebuffer(vk.device, &fbinfo, vkallocationcb, &shad->buf[i].framebuffer));\n\t\t}\n\n\t\tshad->width = width;\n\t\tshad->height = height;\n\t}\n\n\tsbuf = shad->seq++%countof(shad->buf);\n\tshaderstate.currentshadowmap = &shad->buf[sbuf].qimage;\n\n/*\tset_image_layout(vk.rendertarg->cbuf, shaderstate.currentshadowmap->vkimage->image, VK_IMAGE_ASPECT_DEPTH_BIT,\n\t\tshaderstate.currentshadowmap->vkimage->layout,\t\tVK_ACCESS_SHADER_READ_BIT,\t\t\t\t\t\tVK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,\n\t\tVK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,\tVK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,\tVK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT|VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT);\n\tshaderstate.currentshadowmap->vkimage->layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;\n*/\n\t{\n\t\tVkClearValue clearval;\n\t\tVkRenderPassBeginInfo rpass = {VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO};\n\t\tclearval.depthStencil.depth = 1;\n\t\tclearval.depthStencil.stencil = 0;\n\t\trpass.renderPass = VK_GetRenderPass(RP_DEPTHONLY);\n\t\trpass.framebuffer = shad->buf[sbuf].framebuffer;\n\t\trpass.renderArea.offset.x = 0;\n\t\trpass.renderArea.offset.y = 0;\n\t\trpass.renderArea.extent.width = width;\n\t\trpass.renderArea.extent.height = height;\n\t\trpass.clearValueCount = 1;\n\t\trpass.pClearValues = &clearval;\n\t\tvkCmdBeginRenderPass(vk.rendertarg->cbuf, &rpass, VK_SUBPASS_CONTENTS_INLINE);\n\t}\n\tshaderstate.currentshadowmap->vkimage->layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;\t//renderpass should transition it for us.\n\n\t//viewport+scissor will be done elsewhere\n\t//that wasn't too painful, was it?...\n\treturn true;\n}\n\nvoid VKBE_DoneShadows(void)\n{\n//\tstruct shadowmaps_s *shad = &shaderstate.shadow[isspot];\n\tVkViewport viewport;\n\n//\tconst qboolean altqueue = false;\n\n\t//we've rendered the shadowmap, but now we need to blit it to the screen\n\t//so set stuff back to the main view. FIXME: do these in batches to ease the load on tilers.\n\tvkCmdEndRenderPass(vk.rendertarg->cbuf);\n\n\t/*if (altqueue)\n\t{\n\t\tvkCmdSetEvent(alt, shadowcompleteevent);\n\t\tVKBE_FlushDynamicBuffers();\n\t\tVK_Submit_Work();\n\t\tvkCmdWaitEvents(main, 1, &shadowcompleteevent, barrierstuff);\n\t\tvkCmdResetEvent(main, shadowcompleteevent);\n\t}\n\telse*/\n\t{\n\t\tset_image_layout(vk.rendertarg->cbuf, shaderstate.currentshadowmap->vkimage->image, VK_IMAGE_ASPECT_DEPTH_BIT,\n\t\t\tshaderstate.currentshadowmap->vkimage->layout, VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT, VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT|VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT,\n\t\t\tVK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);\n\t\tshaderstate.currentshadowmap->vkimage->layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;\n\n\t\tvkCmdBeginRenderPass(vk.rendertarg->cbuf, &vk.rendertarg->restartinfo, VK_SUBPASS_CONTENTS_INLINE);\n\n\t\tviewport.x = r_refdef.pxrect.x;\n\t\tviewport.y = r_refdef.pxrect.y;//r_refdef.pxrect.maxheight - (r_refdef.pxrect.y+r_refdef.pxrect.height);\t//silly GL...\n\t\tviewport.width = r_refdef.pxrect.width;\n\t\tviewport.height = r_refdef.pxrect.height;\n\t\tviewport.minDepth = 0;\n\t\tviewport.maxDepth = 1;\n\t\tvkCmdSetViewport(vk.rendertarg->cbuf, 0, 1, &viewport);\n\t}\n\n\tVKBE_SelectEntity(&r_worldentity);\n}\n\nvoid VKBE_SetupForShadowMap(dlight_t *dl, int texwidth, int texheight, float shadowscale)\n{\n#define SHADOWMAP_SIZE 512\n\textern cvar_t r_shadow_shadowmapping_nearclip, r_shadow_shadowmapping_bias;\n\tfloat nc = dl->nearclip?dl->nearclip:r_shadow_shadowmapping_nearclip.value;\n\tfloat bias = r_shadow_shadowmapping_bias.value;\n\n\t//much of the projection matrix cancels out due to symmetry and stuff\n\t//we need to scale between -0.5,0.5 within the sub-image. the fragment shader will center on the subimage based upon the major axis.\n\t//in d3d, the depth value is scaled between 0 and 1 (gl is -1 to 1).\n\t//d3d's framebuffer is upside down or something annoying like that.\n\tshaderstate.lightshadowmapproj[0] = shadowscale * (1.0-(1.0/texwidth)) * 0.5/3.0;\t//pinch x inwards\n\tshaderstate.lightshadowmapproj[1] = -shadowscale * (1.0-(1.0/texheight)) * 0.5/2.0;\t//pinch y inwards\n\tshaderstate.lightshadowmapproj[2] = 0.5*(dl->radius+nc)/(nc-dl->radius);\t//proj matrix 10\n\tshaderstate.lightshadowmapproj[3] = (dl->radius*nc)/(nc-dl->radius) - bias*nc*(1024/texheight);\t//proj matrix 14\t\n\n\tshaderstate.lightshadowmapscale[0] = 1.0/(SHADOWMAP_SIZE*3);\n\tshaderstate.lightshadowmapscale[1] = -1.0/(SHADOWMAP_SIZE*2);\n}\n\n//FIXME: needs context for threading\nvoid VKBE_BeginShadowmapFace(void)\n{\n\tVkRect2D wrekt;\n\tVkViewport viewport;\n\n\tviewport.x = r_refdef.pxrect.x;\n\tviewport.y = r_refdef.pxrect.maxheight - (r_refdef.pxrect.y+r_refdef.pxrect.height);\t//silly GL...\n\tviewport.width = r_refdef.pxrect.width;\n\tviewport.height = r_refdef.pxrect.height;\n\tviewport.minDepth = 0;\n\tviewport.maxDepth = 1;\n\tvkCmdSetViewport(vk.rendertarg->cbuf, 0, 1, &viewport);\n\n\twrekt.offset.x = viewport.x;\n\twrekt.offset.y = viewport.y;\n\twrekt.extent.width = viewport.width;\n\twrekt.extent.height = viewport.height;\n\tvkCmdSetScissor(vk.rendertarg->cbuf, 0, 1, &wrekt);\n\n\t//shadowmaps never multisample...\n\tshaderstate.modepermutation &= ~(PERMUTATION_BEM_MULTISAMPLE|PERMUTATION_BEM_FP16|PERMUTATION_BEM_VR);\n}\n#endif\n\nvoid VKBE_DrawWorld (batch_t **worldbatches)\n{\n\tbatch_t *batches[SHADER_SORT_COUNT];\n\tRSpeedLocals();\n\n\tshaderstate.curentity = NULL;\n\n\t{\n\t\tVkViewport viewport;\n\n\t\tviewport.x = r_refdef.pxrect.x;\n\t\tviewport.y = r_refdef.pxrect.y;\n\t\tviewport.width = r_refdef.pxrect.width;\n\t\tviewport.height = r_refdef.pxrect.height;\n\t\tviewport.minDepth = 0;\n\t\tviewport.maxDepth = 1;\n\t\tvkCmdSetViewport(vk.rendertarg->cbuf, 0, 1, &viewport);\n\t}\n\n\tif (!r_refdef.recurse)\n\t{\n\t\tif (shaderstate.wbatch > shaderstate.maxwbatches)\n\t\t{\n\t\t\tint newm = shaderstate.wbatch;\n\t\t\tZ_Free(shaderstate.wbatches);\n\t\t\tshaderstate.wbatches = Z_Malloc(newm * sizeof(*shaderstate.wbatches));\n\t\t\tmemset(shaderstate.wbatches + shaderstate.maxwbatches, 0, (newm - shaderstate.maxwbatches) * sizeof(*shaderstate.wbatches));\n\t\t\tshaderstate.maxwbatches = newm;\n\t\t}\n\t\tshaderstate.wbatch = 0;\n\t}\n\n\tRSpeedRemark();\n\n\tshaderstate.curdlight = NULL;\n\t//fixme: figure out some way to safely orphan this data so that we can throw the rest to a worker.\n\tBE_GenModelBatches(batches, shaderstate.curdlight, BEM_STANDARD, r_refdef.scenevis, r_refdef.sceneareas);\n\n\tBE_UploadLightmaps(false);\n\tif (r_refdef.scenevis)\n\t{\n\t\t//make sure the world draws correctly\n\t\tr_worldentity.shaderRGBAf[0] = 1;\n\t\tr_worldentity.shaderRGBAf[1] = 1;\n\t\tr_worldentity.shaderRGBAf[2] = 1;\n\t\tr_worldentity.shaderRGBAf[3] = 1;\n\t\tr_worldentity.axis[0][0] = 1;\n\t\tr_worldentity.axis[1][1] = 1;\n\t\tr_worldentity.axis[2][2] = 1;\n\n#ifdef RTLIGHTS\n\t\tif (r_refdef.scenevis && r_shadow_realtime_world.ival)\n\t\t\tshaderstate.identitylighting = r_shadow_realtime_world_lightmaps.value;\n\t\telse\n#endif\n\t\t\tshaderstate.identitylighting = r_lightmap_scale.value;\n\t\tshaderstate.identitylighting *= r_refdef.hdr_value;\n\t\tshaderstate.identitylightmap = shaderstate.identitylighting / (1<<gl_overbright.ival);\n\n\t\tif (r_lightprepass)\n\t\t{\n\t\t\t//set up render target for gbuffer\n\t\t\t//draw opaque gbuffers\n\t\t\t//switch render targets to lighting (renderpasses?)\n\t\t\t//draw lpp lights\n\t\t\t//revert to screen\n\t\t\t//draw opaques again.\n\t\t}\n\t\telse\n\t\t{\n\t\t\tVKBE_SelectMode(BEM_STANDARD);\n\n\t\t\t\n\t\t\tVKBE_SubmitMeshes(worldbatches, batches, SHADER_SORT_PORTAL, SHADER_SORT_SEETHROUGH+1);\n\t\t\tRSpeedEnd(RSPEED_OPAQUE);\n\n#ifdef RTLIGHTS\n\t\t\tRSpeedRemark();\n\t\t\tVKBE_SelectEntity(&r_worldentity);\n\t\t\tSh_DrawLights(r_refdef.scenevis);\n\t\t\tRSpeedEnd(RSPEED_RTLIGHTS);\n#endif\n\t\t}\n\n\t\tRSpeedRemark();\n\t\tVKBE_SubmitMeshes(worldbatches, batches, SHADER_SORT_SEETHROUGH+1, SHADER_SORT_COUNT);\n\t\tRSpeedEnd(RSPEED_TRANSPARENTS);\n\n\t\tif (r_wireframe.ival)\n\t\t{\n\t\t\tVKBE_SelectMode(BEM_WIREFRAME);\n\t\t\tVKBE_SubmitMeshes(worldbatches, batches, SHADER_SORT_PORTAL, SHADER_SORT_NEAREST);\n\t\t\tVKBE_SelectMode(BEM_STANDARD);\n\t\t}\n\t}\n\telse\n\t{\n\t\tshaderstate.identitylighting = 1;\n\t\tshaderstate.identitylightmap = 1;\n\t\tVKBE_SubmitMeshes(NULL, batches, SHADER_SORT_PORTAL, SHADER_SORT_COUNT);\n\t\tRSpeedEnd(RSPEED_TRANSPARENTS);\n\t}\n\n\tR_RenderDlights ();\n\n\tshaderstate.identitylighting = 1;\n\tBE_RotateForEntity(&r_worldentity, NULL);\n\n\tif (r_refdef.recurse)\n\t\tRQ_RenderBatch();\n\telse\n\t\tRQ_RenderBatchClear();\n}\n\nvoid VKBE_VBO_Begin(vbobctx_t *ctx, size_t maxsize)\n{\n\tstruct stagingbuf *n = Z_Malloc(sizeof(*n));\n\tctx->vboptr[0] = n;\n\tctx->maxsize = maxsize;\n\tctx->pos = 0;\n\n\tctx->fallback = VKBE_CreateStagingBuffer(n, maxsize, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT);\n\n\t//preallocate the target buffer, so we can prematurely refer to it.\n\t{\n\t\tVkBufferCreateInfo bufinf = {VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO};\n\n\t\tbufinf.flags = 0;\n\t\tbufinf.size = n->size;\n\t\tbufinf.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT|VK_BUFFER_USAGE_TRANSFER_DST_BIT;\n\t\tbufinf.sharingMode = VK_SHARING_MODE_EXCLUSIVE;\n\t\tbufinf.queueFamilyIndexCount = 0;\n\t\tbufinf.pQueueFamilyIndices = NULL;\n\t\tvkCreateBuffer(vk.device, &bufinf, vkallocationcb, &n->retbuf);\n\t}\n}\nvoid VKBE_VBO_Data(vbobctx_t *ctx, void *data, size_t size, vboarray_t *varray)\n{\n\tstruct stagingbuf *n = ctx->vboptr[0];\n\tvarray->vk.offs = ctx->pos;\n\tvarray->vk.buff = n->retbuf;\n\tctx->pos += size;\n\n\tmemcpy((char*)ctx->fallback + varray->vk.offs, data, size);\n}\nvoid VKBE_VBO_Finish(vbobctx_t *ctx, void *edata, size_t esize, vboarray_t *earray, void **vbomem, void **ebomem)\n{\n\tstruct stagingbuf *n;\n\tstruct stagingbuf ebo;\n\tvk_poolmem_t *poolmem;\n\tindex_t *map = VKBE_CreateStagingBuffer(&ebo, esize, VK_BUFFER_USAGE_INDEX_BUFFER_BIT);\n\tmemcpy(map, edata, esize);\n\t*ebomem = poolmem = Z_Malloc(sizeof(*poolmem));\n\tearray->vk.buff = VKBE_FinishStaging(&ebo, poolmem);\n\tearray->vk.offs = 0;\n\n\tif (ctx)\n\t{\n\t\tn = ctx->vboptr[0];\n\t\t*vbomem = poolmem = Z_Malloc(sizeof(*poolmem));\n\t\t/*buffer was pre-created*/VKBE_FinishStaging(n, poolmem);\n\t\tZ_Free(n);\n\t}\n}\nvoid VKBE_VBO_Destroy(vboarray_t *vearray, void *mem)\n{\n\tvk_poolmem_t *poolmem = mem;\n\tstruct fencedbufferwork *fence;\n\tif (!vearray->vk.buff)\n\t\treturn;\t//not actually allocated...\n\n\tfence = VK_AtFrameEnd(VKBE_DoneBufferStaging, NULL, sizeof(*fence));\n\tfence->buf = vearray->vk.buff;\n\tfence->mem = *poolmem;\n\n\tZ_Free(poolmem);\n}\n\nvoid VKBE_Scissor(srect_t *rect)\n{\n\tVkRect2D wrekt;\n\tif (rect)\n\t{\n\t\twrekt.offset.x = rect->x * vid.fbpwidth;\n\t\twrekt.offset.y = (1 - (rect->height + rect->y))*vid.fbpheight;  //our api was made for gl. :(\n\t\twrekt.extent.width = rect->width * vid.fbpwidth;\n\t\twrekt.extent.height = rect->height * vid.fbpheight;\n\n\t\tif (wrekt.offset.x+wrekt.extent.width > vid.fbpwidth)\n\t\t\twrekt.extent.width = vid.fbpwidth - wrekt.offset.x;\n\t\tif (wrekt.offset.y+wrekt.extent.height > vid.fbpheight)\n\t\t\twrekt.extent.height = vid.fbpheight - wrekt.offset.y;\n\t\tif (wrekt.offset.x < 0)\n\t\t{\n\t\t\twrekt.extent.width += wrekt.offset.x;\n\t\t\twrekt.offset.x = 0;\n\t\t}\n\t\tif (wrekt.offset.y < 0)\n\t\t{\n\t\t\twrekt.extent.height += wrekt.offset.x;\n\t\t\twrekt.offset.y = 0;\n\t\t}\n\t}\n\telse\n\t{\n\t\twrekt.offset.x = 0;\n\t\twrekt.offset.y = 0;\n\t\twrekt.extent.width = vid.fbpwidth;\n\t\twrekt.extent.height = vid.fbpheight;\n\t}\n\n\tvkCmdSetScissor(vk.rendertarg->cbuf, 0, 1, &wrekt);\n}\n\n#endif\n"
  },
  {
    "path": "engine/vk/vk_init.c",
    "content": "#include \"quakedef.h\"\n#ifdef VKQUAKE\n#include \"vkrenderer.h\"\n#include \"gl_draw.h\"\n#include \"shader.h\"\n#include \"renderque.h\"\t//is anything still using this?\n\n#include \"vr.h\"\n\nextern qboolean vid_isfullscreen;\n\ncvar_t vk_stagingbuffers\t\t\t\t\t\t= CVARFD (\"vk_stagingbuffers\",\t\t\t\"\", CVAR_RENDERERLATCH, \"Configures which dynamic buffers are copied into gpu memory for rendering, instead of reading from shared memory. Empty for default settings.\\nAccepted chars are u(niform), e(lements), v(ertex), 0(none).\");\nstatic cvar_t vk_submissionthread\t\t\t\t= CVARD\t(\"vk_submissionthread\",\t\t\t\"\", \"Execute submits+presents on a thread dedicated to executing them. This may be a significant speedup on certain drivers.\");\nstatic cvar_t vk_debug\t\t\t\t\t\t\t= CVARFD(\"vk_debug\",\t\t\t\t\t\"0\", CVAR_VIDEOLATCH, \"Register a debug handler to display driver/layer messages. 2 enables the standard validation layers.\");\nstatic cvar_t vk_dualqueue\t\t\t\t\t\t= CVARFD(\"vk_dualqueue\",\t\t\t\t\"\", CVAR_VIDEOLATCH, \"Attempt to use a separate queue for presentation. Blank for default.\");\nstatic cvar_t vk_busywait\t\t\t\t\t\t= CVARD (\"vk_busywait\",\t\t\t\t\t\"\", \"Force busy waiting until the GPU finishes doing its thing.\");\nstatic cvar_t vk_waitfence\t\t\t\t\t\t= CVARD (\"vk_waitfence\",\t\t\t\t\"\", \"Waits on fences, instead of semaphores. This is more likely to result in gpu stalls while the cpu waits.\");\nstatic cvar_t vk_usememorypools\t\t\t\t\t= CVARFD(\"vk_usememorypools\",\t\t\t\"\",\tCVAR_VIDEOLATCH, \"Allocates memory pools for sub allocations. Vulkan has a limit to the number of memory allocations allowed so this should always be enabled, however at this time FTE is unable to reclaim pool memory, and would require periodic vid_restarts to flush them.\");\nstatic cvar_t vk_khr_get_memory_requirements2\t= CVARFD(\"vk_khr_get_memory_requirements2\", \"\", CVAR_VIDEOLATCH, \"Enable extended memory info querires\");\nstatic cvar_t vk_khr_dedicated_allocation\t\t= CVARFD(\"vk_khr_dedicated_allocation\",\t\"\", CVAR_VIDEOLATCH, \"Flag vulkan memory allocations as dedicated, where applicable.\");\nstatic cvar_t vk_khr_push_descriptor\t\t\t= CVARFD(\"vk_khr_push_descriptor\",\t\t\"\", CVAR_VIDEOLATCH, \"Enables better descriptor streaming.\");\nstatic cvar_t vk_amd_rasterization_order\t\t= CVARFD(\"vk_amd_rasterization_order\",\t\"\",\tCVAR_VIDEOLATCH, \"Enables the use of relaxed rasterization ordering, for a small speedup at the minor risk of a little zfighting.\");\n#ifdef VK_KHR_fragment_shading_rate\nstatic cvar_t vK_khr_fragment_shading_rate\t\t= CVARFD(\"vK_khr_fragment_shading_rate\",\"\",\tCVAR_VIDEOLATCH, \"Enables the use of variable shading rates.\");\n#endif\n#ifdef VK_EXT_astc_decode_mode\nstatic cvar_t vk_ext_astc_decode_mode\t\t\t= CVARFD(\"vk_ext_astc_decode_mode\",\t\t\"\",\tCVAR_VIDEOLATCH, \"Enables reducing texture cache sizes for LDR ASTC-compressed textures.\");\n#endif\n#ifdef VK_KHR_ray_query\nstatic cvar_t vk_khr_ray_query\t\t\t\t\t= CVARFD(\"vk_khr_ray_query\",\t\t\t\"\",\tCVAR_VIDEOLATCH, \"Required for the use of hardware raytraced shadows.\");\n#endif\nextern cvar_t vid_srgb, vid_vsync, vid_triplebuffer, r_stereo_method, vid_multisample, vid_bpp;\n\ntexid_t r_blackcubeimage, r_whitecubeimage;\n\n\nvoid VK_RegisterVulkanCvars(void)\n{\n#define VKRENDEREROPTIONS\t\"Vulkan-Specific Renderer Options\"\n\tCvar_Register (&vk_stagingbuffers,\t\t\tVKRENDEREROPTIONS);\n\tCvar_Register (&vk_submissionthread,\t\tVKRENDEREROPTIONS);\n\tCvar_Register (&vk_debug,\t\t\t\t\tVKRENDEREROPTIONS);\n\tCvar_Register (&vk_dualqueue,\t\t\t\tVKRENDEREROPTIONS);\n\tCvar_Register (&vk_busywait,\t\t\t\tVKRENDEREROPTIONS);\n\tCvar_Register (&vk_waitfence,\t\t\t\tVKRENDEREROPTIONS);\n\tCvar_Register (&vk_usememorypools,\t\t\tVKRENDEREROPTIONS);\n\n\tCvar_Register (&vk_khr_get_memory_requirements2,VKRENDEREROPTIONS);\n\tCvar_Register (&vk_khr_dedicated_allocation,\tVKRENDEREROPTIONS);\n\tCvar_Register (&vk_khr_push_descriptor,\t\t\tVKRENDEREROPTIONS);\n\tCvar_Register (&vk_amd_rasterization_order,\t\tVKRENDEREROPTIONS);\n#ifdef VK_KHR_fragment_shading_rate\n\tCvar_Register (&vK_khr_fragment_shading_rate,\tVKRENDEREROPTIONS);\n#endif\n#ifdef VK_EXT_astc_decode_mode\n\tCvar_Register (&vk_ext_astc_decode_mode,\tVKRENDEREROPTIONS);\n#endif\n#ifdef VK_KHR_ray_query\n\tCvar_Register (&vk_khr_ray_query,\t\t\tVKRENDEREROPTIONS);\n#endif\n}\nvoid R2D_Console_Resize(void);\nstatic void VK_DestroySampler(VkSampler s);\n\nextern qboolean\t\tscr_con_forcedraw;\n\n#ifndef MULTITHREAD\n#define Sys_LockConditional(c)\n#define Sys_UnlockConditional(c)\n#endif\n\nstatic const char *vklayerlist[] =\n{\n#if 1\n\t\"VK_LAYER_KHRONOS_validation\"\n#elif 1\n\t\"VK_LAYER_LUNARG_standard_validation\"\n#else\n\t\t//older versions of the sdk were crashing out on me,\n//\t\"VK_LAYER_LUNARG_api_dump\",\n\"VK_LAYER_LUNARG_device_limits\",\n//\"VK_LAYER_LUNARG_draw_state\",\n\"VK_LAYER_LUNARG_image\",\n//\"VK_LAYER_LUNARG_mem_tracker\",\n\"VK_LAYER_LUNARG_object_tracker\",\n\"VK_LAYER_LUNARG_param_checker\",\n\"VK_LAYER_LUNARG_screenshot\",\n\"VK_LAYER_LUNARG_swapchain\",\n\"VK_LAYER_GOOGLE_threading\",\n\"VK_LAYER_GOOGLE_unique_objects\",\n//\"VK_LAYER_LUNARG_vktrace\",\n#endif\n};\n#define vklayercount (vk_debug.ival>1?countof(vklayerlist):0)\n\n\n//code to initialise+destroy vulkan contexts.\n//this entire file is meant to be platform-agnostic.\n//the vid code still needs to set up vkGetInstanceProcAddr, and do all the window+input stuff.\n\n#ifdef VK_NO_PROTOTYPES\n\t#define VKFunc(n) PFN_vk##n vk##n;\n\t#ifdef VK_EXT_debug_utils\n\t\tstatic VKFunc(CreateDebugUtilsMessengerEXT)\n\t\tstatic VKFunc(DestroyDebugUtilsMessengerEXT)\n\t#endif\n\t#ifdef VK_EXT_debug_report\n\t\tstatic VKFunc(CreateDebugReportCallbackEXT)\n\t\tstatic VKFunc(DestroyDebugReportCallbackEXT)\n\t#endif\n\tVKFuncs\n\t#undef VKFunc\n#endif\n\nvoid VK_Submit_Work(VkCommandBuffer cmdbuf, VkSemaphore semwait, VkPipelineStageFlags semwaitstagemask, VkSemaphore semsignal, VkFence fencesignal, struct vkframe *presentframe, struct vk_fencework *fencedwork);\n#ifdef MULTITHREAD\nstatic int VK_Submit_Thread(void *arg);\n#endif\nstatic void VK_Submit_DoWork(void);\n\nstatic void VK_DestroyRenderPasses(void);\nVkRenderPass VK_GetRenderPass(int pass);\nstatic void VK_Shutdown_PostProc(void);\n\t\t\nstruct vulkaninfo_s vk;\nstatic struct vk_rendertarg postproc[4];\nstatic unsigned int postproc_buf;\nstatic struct vk_rendertarg_cube vk_rt_cubemap;\n\nqboolean VK_SCR_GrabBackBuffer(void);\n\n#if defined(__linux__) && defined(__GLIBC__)\n#include <execinfo.h>\n#define DOBACKTRACE()\t\t\t\t\t\\\ndo {\t\t\t\t\t\t\t\\\n\tvoid *bt[16];\t\t\t\t\t\\\n\tint i, fr = backtrace(bt, countof(bt));\t\t\\\n\tchar **strings = backtrace_symbols(bt, fr);\t\\\n\tfor (i = 0; i < fr; i++)\t\t\t\\\n\t\tif (strings)\t\t\t\t\\\n\t\t\tCon_Printf(\"\\t%s\\n\", strings[i]);\t\\\n\t\telse\t\t\t\t\t\\\n\t\t\tCon_Printf(\"\\t%p\\n\", bt[i]);\t\\\n\tfree(strings);\t\t\t\t\t\\\n} while(0)\n#else\n#define DOBACKTRACE()\n#endif\n\nchar *VK_VKErrorToString(VkResult err)\n{\n\tswitch(err)\n\t{\n\t//positive codes\n\tcase VK_SUCCESS:\t\t\t\t\t\treturn \"VK_SUCCESS\";\n\tcase VK_NOT_READY:\t\t\t\t\t\treturn \"VK_NOT_READY\";\n\tcase VK_TIMEOUT:\t\t\t\t\t\treturn \"VK_TIMEOUT\";\n\tcase VK_EVENT_SET:\t\t\t\t\t\treturn \"VK_EVENT_SET\";\n\tcase VK_EVENT_RESET:\t\t\t\t\treturn \"VK_EVENT_RESET\";\n\tcase VK_INCOMPLETE:\t\t\t\t\t\treturn \"VK_INCOMPLETE\";\n\n\t//core errors\n\tcase VK_ERROR_OUT_OF_HOST_MEMORY:\t\treturn \"VK_ERROR_OUT_OF_HOST_MEMORY\";\n\tcase VK_ERROR_OUT_OF_DEVICE_MEMORY:\t\treturn \"VK_ERROR_OUT_OF_DEVICE_MEMORY\";\n\tcase VK_ERROR_INITIALIZATION_FAILED:\treturn \"VK_ERROR_INITIALIZATION_FAILED\";\n\tcase VK_ERROR_DEVICE_LOST:\t\t\t\treturn \"VK_ERROR_DEVICE_LOST\";\t//by far the most common.\n\tcase VK_ERROR_MEMORY_MAP_FAILED:\t\treturn \"VK_ERROR_MEMORY_MAP_FAILED\";\n\tcase VK_ERROR_LAYER_NOT_PRESENT:\t\treturn \"VK_ERROR_LAYER_NOT_PRESENT\";\n\tcase VK_ERROR_EXTENSION_NOT_PRESENT:\treturn \"VK_ERROR_EXTENSION_NOT_PRESENT\";\n\tcase VK_ERROR_FEATURE_NOT_PRESENT:\t\treturn \"VK_ERROR_FEATURE_NOT_PRESENT\";\n\tcase VK_ERROR_INCOMPATIBLE_DRIVER:\t\treturn \"VK_ERROR_INCOMPATIBLE_DRIVER\";\n\tcase VK_ERROR_TOO_MANY_OBJECTS:\t\t\treturn \"VK_ERROR_TOO_MANY_OBJECTS\";\n\tcase VK_ERROR_FORMAT_NOT_SUPPORTED:\t\treturn \"VK_ERROR_FORMAT_NOT_SUPPORTED\";\n\tcase VK_ERROR_FRAGMENTED_POOL:\t\t\treturn \"VK_ERROR_FRAGMENTED_POOL\";\n\n\tcase VK_ERROR_SURFACE_LOST_KHR:\t\t\treturn \"VK_ERROR_SURFACE_LOST_KHR\";\n\tcase VK_ERROR_NATIVE_WINDOW_IN_USE_KHR: return \"VK_ERROR_NATIVE_WINDOW_IN_USE_KHR\";\n\tcase VK_SUBOPTIMAL_KHR:\t\t\t\t\treturn \"VK_SUBOPTIMAL_KHR\";\n\tcase VK_ERROR_OUT_OF_DATE_KHR:\t\t\treturn \"VK_ERROR_OUT_OF_DATE_KHR\";\n\tcase VK_ERROR_INCOMPATIBLE_DISPLAY_KHR:\treturn \"VK_ERROR_INCOMPATIBLE_DISPLAY_KHR\";\n\n\tcase VK_ERROR_VALIDATION_FAILED_EXT:\treturn \"VK_ERROR_VALIDATION_FAILED_EXT\";\n\tcase VK_ERROR_INVALID_SHADER_NV:\t\treturn \"VK_ERROR_INVALID_SHADER_NV\";\n\tcase VK_ERROR_OUT_OF_POOL_MEMORY_KHR:\treturn \"VK_ERROR_OUT_OF_POOL_MEMORY_KHR\";\n\tcase VK_ERROR_INVALID_EXTERNAL_HANDLE_KHR:\treturn \"VK_ERROR_INVALID_EXTERNAL_HANDLE_KHR\";\n\n#ifdef VK_EXT_image_drm_format_modifier\n    case VK_ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT:\t\treturn \"VK_ERROR_OUT_OF_POOL_MEMORY_KHR\";\n#endif\n#ifdef VK_EXT_descriptor_indexing\n    case VK_ERROR_FRAGMENTATION_EXT:\t\t\treturn \"VK_ERROR_OUT_OF_POOL_MEMORY_KHR\";\n#endif\n#ifdef VK_EXT_global_priority\n    case VK_ERROR_NOT_PERMITTED_EXT:\t\t\treturn \"VK_ERROR_OUT_OF_POOL_MEMORY_KHR\";\n#endif\n#ifdef VK_EXT_buffer_device_address\n    case VK_ERROR_INVALID_DEVICE_ADDRESS_EXT:\treturn \"VK_ERROR_OUT_OF_POOL_MEMORY_KHR\";\n#endif\n\n\t//irrelevant parts of the enum\n//\tcase VK_RESULT_RANGE_SIZE:\n\tcase VK_RESULT_MAX_ENUM:\n\tdefault:\n\t\tbreak;\n\t}\n\treturn va(\"%d\", (int)err);\n}\n#ifdef VK_EXT_debug_utils\nstatic void DebugSetName(VkObjectType objtype, uint64_t handle, const char *name)\n{\n\tif (vkSetDebugUtilsObjectNameEXT)\n\t{\n\t\tVkDebugUtilsObjectNameInfoEXT info =\n\t\t{\n\t\t\tVK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT,\n\t\t\tNULL,\n\t\t\tobjtype,\n\t\t\thandle,\n\t\t\tname\n\t\t};\n\t\tvkSetDebugUtilsObjectNameEXT(vk.device, &info);\n\t}\n}\nstatic VkDebugUtilsMessengerEXT vk_debugucallback;\nchar *DebugAnnotObjectToString(VkObjectType t)\n{\n\tswitch(t)\n\t{\n\tcase VK_OBJECT_TYPE_UNKNOWN:\t\t\t\t\t\treturn \"VK_OBJECT_TYPE_UNKNOWN\";\n    case VK_OBJECT_TYPE_INSTANCE:\t\t\t\t\t\treturn \"VK_OBJECT_TYPE_INSTANCE\";\n\tcase VK_OBJECT_TYPE_PHYSICAL_DEVICE:\t\t\t\treturn \"VK_OBJECT_TYPE_PHYSICAL_DEVICE\";\n\tcase VK_OBJECT_TYPE_DEVICE:\t\t\t\t\t\t\treturn \"VK_OBJECT_TYPE_DEVICE\";\n\tcase VK_OBJECT_TYPE_QUEUE:\t\t\t\t\t\t\treturn \"VK_OBJECT_TYPE_QUEUE\";\n\tcase VK_OBJECT_TYPE_SEMAPHORE:\t\t\t\t\t\treturn \"VK_OBJECT_TYPE_SEMAPHORE\";\n\tcase VK_OBJECT_TYPE_COMMAND_BUFFER:\t\t\t\t\treturn \"VK_OBJECT_TYPE_COMMAND_BUFFER\";\n\tcase VK_OBJECT_TYPE_FENCE:\t\t\t\t\t\t\treturn \"VK_OBJECT_TYPE_FENCE\";\n\tcase VK_OBJECT_TYPE_DEVICE_MEMORY:\t\t\t\t\treturn \"VK_OBJECT_TYPE_DEVICE_MEMORY\";\n\tcase VK_OBJECT_TYPE_BUFFER:\t\t\t\t\t\t\treturn \"VK_OBJECT_TYPE_BUFFER\";\n\tcase VK_OBJECT_TYPE_IMAGE:\t\t\t\t\t\t\treturn \"VK_OBJECT_TYPE_IMAGE\";\n\tcase VK_OBJECT_TYPE_EVENT:\t\t\t\t\t\t\treturn \"VK_OBJECT_TYPE_EVENT\";\n\tcase VK_OBJECT_TYPE_QUERY_POOL:\t\t\t\t\t\treturn \"VK_OBJECT_TYPE_QUERY_POOL\";\n\tcase VK_OBJECT_TYPE_BUFFER_VIEW:\t\t\t\t\treturn \"VK_OBJECT_TYPE_BUFFER_VIEW\";\n\tcase VK_OBJECT_TYPE_IMAGE_VIEW:\t\t\t\t\t\treturn \"VK_OBJECT_TYPE_IMAGE_VIEW\";\n\tcase VK_OBJECT_TYPE_SHADER_MODULE:\t\t\t\t\treturn \"VK_OBJECT_TYPE_SHADER_MODULE\";\n\tcase VK_OBJECT_TYPE_PIPELINE_CACHE:\t\t\t\t\treturn \"VK_OBJECT_TYPE_PIPELINE_CACHE\";\n\tcase VK_OBJECT_TYPE_PIPELINE_LAYOUT:\t\t\t\treturn \"VK_OBJECT_TYPE_PIPELINE_LAYOUT\";\n\tcase VK_OBJECT_TYPE_RENDER_PASS:\t\t\t\t\treturn \"VK_OBJECT_TYPE_RENDER_PASS\";\n\tcase VK_OBJECT_TYPE_PIPELINE:\t\t\t\t\t\treturn \"VK_OBJECT_TYPE_PIPELINE\";\n\tcase VK_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT:\t\t\treturn \"VK_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT\";\n\tcase VK_OBJECT_TYPE_SAMPLER:\t\t\t\t\t\treturn \"VK_OBJECT_TYPE_SAMPLER\";\n\tcase VK_OBJECT_TYPE_DESCRIPTOR_POOL:\t\t\t\treturn \"VK_OBJECT_TYPE_DESCRIPTOR_POOL\";\n\tcase VK_OBJECT_TYPE_DESCRIPTOR_SET:\t\t\t\t\treturn \"VK_OBJECT_TYPE_DESCRIPTOR_SET\";\n\tcase VK_OBJECT_TYPE_FRAMEBUFFER:\t\t\t\t\treturn \"VK_OBJECT_TYPE_FRAMEBUFFER\";\n\tcase VK_OBJECT_TYPE_COMMAND_POOL:\t\t\t\t\treturn \"VK_OBJECT_TYPE_COMMAND_POOL\";\n\tcase VK_OBJECT_TYPE_SAMPLER_YCBCR_CONVERSION:\t\treturn \"VK_OBJECT_TYPE_SAMPLER_YCBCR_CONVERSION\";\n\tcase VK_OBJECT_TYPE_DESCRIPTOR_UPDATE_TEMPLATE:\t\treturn \"VK_OBJECT_TYPE_DESCRIPTOR_UPDATE_TEMPLATE\";\n\tcase VK_OBJECT_TYPE_SURFACE_KHR:\t\t\t\t\treturn \"VK_OBJECT_TYPE_SURFACE_KHR\";\n\tcase VK_OBJECT_TYPE_SWAPCHAIN_KHR:\t\t\t\t\treturn \"VK_OBJECT_TYPE_SWAPCHAIN_KHR\";\n\tcase VK_OBJECT_TYPE_DISPLAY_KHR:\t\t\t\t\treturn \"VK_OBJECT_TYPE_DISPLAY_KHR\";\n\tcase VK_OBJECT_TYPE_DISPLAY_MODE_KHR:\t\t\t\treturn \"VK_OBJECT_TYPE_DISPLAY_MODE_KHR\";\n\tcase VK_OBJECT_TYPE_DEBUG_REPORT_CALLBACK_EXT:\t\treturn \"VK_OBJECT_TYPE_DEBUG_REPORT_CALLBACK_EXT\";\n#ifdef VK_NVX_device_generated_commands\n\tcase VK_OBJECT_TYPE_OBJECT_TABLE_NVX:\t\t\t\treturn \"VK_OBJECT_TYPE_OBJECT_TABLE_NVX\";\n\tcase VK_OBJECT_TYPE_INDIRECT_COMMANDS_LAYOUT_NVX:\treturn \"VK_OBJECT_TYPE_INDIRECT_COMMANDS_LAYOUT_NVX\";\n#endif\n\tcase VK_OBJECT_TYPE_DEBUG_UTILS_MESSENGER_EXT:\t\treturn \"VK_OBJECT_TYPE_DEBUG_UTILS_MESSENGER_EXT\";\n\tcase VK_OBJECT_TYPE_VALIDATION_CACHE_EXT:\t\t\treturn \"VK_OBJECT_TYPE_VALIDATION_CACHE_EXT\";\n#ifdef VK_NV_ray_tracing\n\tcase VK_OBJECT_TYPE_ACCELERATION_STRUCTURE_NV:\t\treturn \"VK_OBJECT_TYPE_ACCELERATION_STRUCTURE_NV\";\n#endif\n#ifdef VK_KHR_acceleration_structure\n\tcase VK_OBJECT_TYPE_ACCELERATION_STRUCTURE_KHR:\t\treturn \"VK_OBJECT_TYPE_ACCELERATION_STRUCTURE_KHR\";\n#endif\n//\tcase VK_OBJECT_TYPE_RANGE_SIZE:\n    case VK_OBJECT_TYPE_MAX_ENUM:\n\t\tbreak;\n\tdefault:\n\t\tbreak;\n\t}\n\treturn \"UNKNOWNTYPE\";\n}\nstatic VKAPI_ATTR VkBool32 VKAPI_CALL mydebugutilsmessagecallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, VkDebugUtilsMessageTypeFlagsEXT messageType, const VkDebugUtilsMessengerCallbackDataEXT*pCallbackData, void* pUserData)\n{\n\tchar prefix[64];\n\tint l = 0;\t//developer level\n\tif (messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT)\n\t{\t//spam?\n\t\tstrcpy(prefix, \"VERBOSE:\");\n\t\tl = 2;\n\t}\n\telse if (messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT)\n\t{\t//generally stuff like 'object created'\n\t\tstrcpy(prefix, \"INFO:\");\n\t\tl = 1;\n\t}\n\telse if (messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT)\n\t\tstrcpy(prefix, CON_WARNING\"WARNING:\");\n\telse if (messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT)\n\t\tstrcpy(prefix, CON_ERROR \"ERROR:\");\n\n\tif (messageType & VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT)\n\t\tstrcat(prefix, \"GENERAL\");\n\telse\n\t{\n\t\tif (messageType & VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT)\n\t\t\tstrcat(prefix, \"SPEC\");\n\t\tif (messageType & VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT)\n\t\t{\n\t\t\tif (messageType & VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT)\n\t\t\t{\n\t\t\t\tstrcat(prefix, \"|\");\n\t\t\t}\n\t\t\tstrcat(prefix,\"PERF\");\n\t\t}\n\t}\n\tCon_DLPrintf(l, \"%s[%d] %s - %s\\n\", prefix, pCallbackData->messageIdNumber, pCallbackData->pMessageIdName?pCallbackData->pMessageIdName:\"\", pCallbackData->pMessage);\n\n\tif (pCallbackData->objectCount > 0)\n\t{\n\t\tuint32_t object;\n\t\tfor(object = 0; object < pCallbackData->objectCount; ++object)\n\t\t\tCon_DLPrintf(l, \"       Object[%d] - Type %s, Value %\"PRIx64\", Name \\\"%s\\\"\\n\", object,\n\t\t\t\t\t\tDebugAnnotObjectToString(pCallbackData->pObjects[object].objectType),\n\t\t\t\t\t\tpCallbackData->pObjects[object].objectHandle,\n\t\t\t\t\t\tpCallbackData->pObjects[object].pObjectName);\n\t}\n\n\tif (pCallbackData->cmdBufLabelCount > 0)\n\t{\n\t\tuint32_t label;\n\t\tfor (label = 0; label < pCallbackData->cmdBufLabelCount; ++label)\n\t\t\tCon_DLPrintf(l, \"       Label[%d] - %s { %f, %f, %f, %f}\\n\", label,\n\t\t\t\t\t\tpCallbackData->pCmdBufLabels[label].pLabelName,\n\t\t\t\t\t\tpCallbackData->pCmdBufLabels[label].color[0],\n\t\t\t\t\t\tpCallbackData->pCmdBufLabels[label].color[1],\n\t\t\t\t\t\tpCallbackData->pCmdBufLabels[label].color[2],\n\t\t\t\t\t\tpCallbackData->pCmdBufLabels[label].color[3]);\n\t}\n\treturn false;\n}\n#else\n#define DebugSetName(objtype,handle,name)\n#endif\n#ifdef VK_EXT_debug_report\nstatic VkDebugReportCallbackEXT vk_debugcallback;\nstatic VkBool32 VKAPI_PTR mydebugreportcallback(\n\t\t\t\tVkDebugReportFlagsEXT                       flags,\n\t\t\t\tVkDebugReportObjectTypeEXT                  objectType,\n\t\t\t\tuint64_t                                    object,\n\t\t\t\tsize_t                                      location,\n\t\t\t\tint32_t                                     messageCode,\n\t\t\t\tconst char*                                 pLayerPrefix,\n\t\t\t\tconst char*                                 pMessage,\n\t\t\t\tvoid*                                       pUserData)\n{\n\tif (flags & VK_DEBUG_REPORT_ERROR_BIT_EXT)\n\t{\n\t\tCon_Printf(\"ERR: %s: %s\\n\", pLayerPrefix, pMessage);\n//\t\tDOBACKTRACE();\n\t}\n\telse if (flags & VK_DEBUG_REPORT_WARNING_BIT_EXT)\n\t{\n\t\tif (!strncmp(pMessage, \"Additional bits in Source accessMask\", 36) && strstr(pMessage, \"VK_IMAGE_LAYOUT_UNDEFINED\"))\n\t\t\treturn false;\t//I don't give a fuck. undefined can be used to change layouts on a texture that already exists too.\n\t\tCon_Printf(\"WARN: %s: %s\\n\", pLayerPrefix, pMessage);\n\t\tDOBACKTRACE();\n\t}\n\telse if (flags & VK_DEBUG_REPORT_DEBUG_BIT_EXT)\n\t{\n\t\tCon_DPrintf(\"DBG: %s: %s\\n\", pLayerPrefix, pMessage);\n//\t\tDOBACKTRACE();\n\t}\n\telse if (flags & VK_DEBUG_REPORT_INFORMATION_BIT_EXT)\n\t{\n#ifdef _WIN32\n//\t\tOutputDebugString(va(\"INF: %s\\n\", pMessage));\n#else\n\t\tCon_Printf(\"INF: %s: %s\\n\", pLayerPrefix, pMessage);\n//\t\tDOBACKTRACE();\n#endif\n\t}\n\telse if (flags & VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT)\n\t{\n\t\tCon_Printf(\"PERF: %s: %s\\n\", pLayerPrefix, pMessage);    \n\t\tDOBACKTRACE();\n\t}\n\telse\n\t{\n\t\tCon_Printf(\"OTHER: %s: %s\\n\", pLayerPrefix, pMessage);\n\t\tDOBACKTRACE();\n\t}\n\treturn false;\n}\n#endif\n\n//typeBits is some vulkan requirement thing (like textures must be device-local).\n//requirements_mask are things that the engine may require (like host-visible).\n//note that there is absolutely no guarentee that hardware requirements will match what the host needs.\n//thus you may need to use staging.\nuint32_t vk_find_memory_try(uint32_t typeBits, VkFlags requirements_mask)\n{\n\tuint32_t i;\n\tfor (i = 0; i < 32; i++)\n\t{\n\t\tif ((typeBits & 1) == 1)\n\t\t{\n\t\t\tif ((vk.memory_properties.memoryTypes[i].propertyFlags & requirements_mask) == requirements_mask)\n\t\t\t\treturn i;\n\t\t}\n\t\ttypeBits >>= 1;\n\t}\n\treturn ~0u;\n}\nuint32_t vk_find_memory_require(uint32_t typeBits, VkFlags requirements_mask)\n{\n\tuint32_t ret = vk_find_memory_try(typeBits, requirements_mask);\n\tif (ret == ~0)\n\t\tSys_Error(\"Unable to find suitable vulkan memory pool\\n\");\n\treturn ret;\n}\n\nvoid VK_DestroyVkTexture(vk_image_t *img)\n{\n\tif (!img)\n\t\treturn;\n\tif (img->sampler)\n\t\tVK_DestroySampler(img->sampler);\n\tif (img->view)\n\t\tvkDestroyImageView(vk.device, img->view, vkallocationcb);\n\tif (img->image)\n\t\tvkDestroyImage(vk.device, img->image, vkallocationcb);\n\tVK_ReleasePoolMemory(&img->mem);\n}\nstatic void VK_DestroyVkTexture_Delayed(void *w)\n{\n\tVK_DestroyVkTexture(w);\n}\n\nstatic void VK_DestroySwapChain(void)\n{\n\tuint32_t i;\n\n#ifdef MULTITHREAD\n\tif (vk.submitcondition)\n\t{\n\t\tSys_LockConditional(vk.submitcondition);\n\t\tvk.neednewswapchain = true;\n\t\tSys_ConditionSignal(vk.submitcondition);\n\t\tSys_UnlockConditional(vk.submitcondition);\n\t}\n\tif (vk.submitthread)\n\t{\n\t\tSys_WaitOnThread(vk.submitthread);\n\t\tvk.submitthread = NULL;\n\t}\n#endif\n\twhile (vk.work)\n\t{\n\t\tSys_LockConditional(vk.submitcondition);\n\t\tVK_Submit_DoWork();\n\t\tSys_UnlockConditional(vk.submitcondition);\n\t}\n\tif (vk.dopresent)\n\t\tvk.dopresent(NULL);\n\tif (vk.device)\n\t\tvkDeviceWaitIdle(vk.device);\n\t/*while (vk.aquirenext < vk.aquirelast)\n\t{\n\t\tVkWarnAssert(vkWaitForFences(vk.device, 1, &vk.acquirefences[vk.aquirenext%ACQUIRELIMIT], VK_FALSE, UINT64_MAX));\n\t\tvk.aquirenext++;\n\t}*/\n\tVK_FencedCheck();\n\twhile(vk.frameendjobs)\n\t{\t//we've fully synced the gpu now, we can clean up any resources that were pending but not assigned yet.\n\t\tstruct vk_frameend *job = vk.frameendjobs;\n\t\tvk.frameendjobs = job->next;\n\t\tjob->FrameEnded(job+1);\n\t\tZ_Free(job);\n\t}\n\n\tif (vk.frame)\n\t{\n\t\tvk.frame->next = vk.unusedframes;\n\t\tvk.unusedframes = vk.frame;\n\t\tvk.frame = NULL;\n\t}\n\n\tif (vk.dopresent)\n\t\tvk.dopresent(NULL);\n\n\t//wait for it to all finish first...\n\tif (vk.device)\n\t\tvkDeviceWaitIdle(vk.device);\n#if 0\t//don't bother waiting as they're going to be destroyed anyway, and we're having a lot of fun with drivers that don't bother signalling them on teardown\n\tvk.acquirenext = vk.acquirelast;\n#else\n\t//clean up our acquires so we know the driver isn't going to update anything.\n\twhile (vk.acquirenext < vk.acquirelast)\n\t{\n\t\tif (vk.acquirefences[vk.acquirenext%ACQUIRELIMIT])\n\t\t\tVkWarnAssert(vkWaitForFences(vk.device, 1, &vk.acquirefences[vk.acquirenext%ACQUIRELIMIT], VK_FALSE, 1000000000u));\t//drivers suck, especially in times of error, and especially if its nvidia's vulkan driver.\n\t\tvk.acquirenext++;\n\t}\n#endif\n\tfor (i = 0; i < ACQUIRELIMIT; i++)\n\t{\n\t\tif (vk.acquirefences[i])\n\t\t\tvkDestroyFence(vk.device, vk.acquirefences[i], vkallocationcb);\n\t\tvk.acquirefences[i] = VK_NULL_HANDLE;\n\t}\n\n\tfor (i = 0; i < vk.backbuf_count; i++)\n\t{\n\t\t//swapchain stuff\n\t\tif (vk.backbufs[i].framebuffer)\n\t\t\tvkDestroyFramebuffer(vk.device, vk.backbufs[i].framebuffer, vkallocationcb);\n\t\tvk.backbufs[i].framebuffer = VK_NULL_HANDLE;\n\t\tif (vk.backbufs[i].colour.view)\n\t\t\tvkDestroyImageView(vk.device, vk.backbufs[i].colour.view, vkallocationcb);\n\t\tvk.backbufs[i].colour.view = VK_NULL_HANDLE;\n\t\tVK_DestroyVkTexture(&vk.backbufs[i].depth);\n\t\tVK_DestroyVkTexture(&vk.backbufs[i].mscolour);\n\t\tvkDestroySemaphore(vk.device, vk.backbufs[i].presentsemaphore, vkallocationcb);\n\t}\n\n\twhile(vk.unusedframes)\n\t{\n\t\tstruct vkframe *frame = vk.unusedframes;\n\t\tvk.unusedframes = frame->next;\n\n\t\tVKBE_ShutdownFramePools(frame);\n\n\t\tvkFreeCommandBuffers(vk.device, vk.cmdpool, frame->maxcbufs, frame->cbufs);\n\t\tBZ_Free(frame->cbufs);\n\t\tvkDestroyFence(vk.device, frame->finishedfence, vkallocationcb);\n\t\tZ_Free(frame);\n\t}\n\n\tif (vk.swapchain)\n\t{\n\t\tvkDestroySwapchainKHR(vk.device, vk.swapchain, vkallocationcb);\n\t\tvk.swapchain = VK_NULL_HANDLE;\n\t}\n\n\tif (vk.backbufs)\n\t\tfree(vk.backbufs);\n\tvk.backbufs = NULL;\n\tvk.backbuf_count = 0;\n}\n\nstatic qboolean VK_CreateSwapChain(void)\n{\n\tqboolean reloadshaders = false;\n\tuint32_t fmtcount;\n\tVkSurfaceFormatKHR *surffmts;\n\tuint32_t presentmodes;\n\tVkPresentModeKHR *presentmode;\n\tVkSurfaceCapabilitiesKHR surfcaps;\n\tVkSwapchainCreateInfoKHR swapinfo = {VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR};\n\tuint32_t i, curpri, preaquirecount;\n\tVkSwapchainKHR newvkswapchain;\n\tVkImage *images;\n\tVkDeviceMemory *memories;\n\tVkImageView attachments[3];\n\tVkFramebufferCreateInfo fb_info = {VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO};\n\tVkSampleCountFlagBits oldms;\n\tuint32_t rpassflags = 0;\n\tVkResult err;\n\n\tVkFormat oldformat = vk.backbufformat;\n\tVkFormat olddepthformat = vk.depthformat;\n\n\tvk.dopresent(NULL);\t//make sure they're all pushed through.\n\n\n\tvid_vsync.modified = false;\n\tvid_triplebuffer.modified = false;\n\tvid_srgb.modified = false;\n\tvk_submissionthread.modified = false;\n\tvk_waitfence.modified = false;\n\tvid_multisample.modified = false;\n\n\tvk.triplebuffer = vid_triplebuffer.ival;\n\tvk.vsync = vid_vsync.ival;\n\n\tif (!vk.khr_swapchain)\n\t{\t//headless\n\t\tif (vk.swapchain || vk.backbuf_count)\n\t\t\tVK_DestroySwapChain();\n\n\t\tvk.backbufformat = ((vid.flags&VID_SRGBAWARE)||vid_srgb.ival)?VK_FORMAT_B8G8R8A8_SRGB:VK_FORMAT_B8G8R8A8_UNORM;\n\t\tvk.backbuf_count = 4;\n\n\t\tswapinfo.imageExtent.width = vid.pixelwidth;\n\t\tswapinfo.imageExtent.height = vid.pixelheight;\n\n\t\timages = malloc(sizeof(VkImage)*vk.backbuf_count);\n\t\tmemset(images, 0, sizeof(VkImage)*vk.backbuf_count);\n\t\tmemories = malloc(sizeof(VkDeviceMemory)*vk.backbuf_count);\n\t\tmemset(memories, 0, sizeof(VkDeviceMemory)*vk.backbuf_count);\n\n\t\tvk.acquirelast = vk.acquirenext = 0;\n\t\tfor (i = 0; i < ACQUIRELIMIT; i++)\n\t\t{\n\t\t\tif (1)\n\t\t\t{\n\t\t\t\tVkFenceCreateInfo fci = {VK_STRUCTURE_TYPE_FENCE_CREATE_INFO};\n\t\t\t\tfci.flags = VK_FENCE_CREATE_SIGNALED_BIT;\n\t\t\t\tVkAssert(vkCreateFence(vk.device,&fci,vkallocationcb,&vk.acquirefences[i]));\n\t\t\t\tvk.acquiresemaphores[i] = VK_NULL_HANDLE;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tVkSemaphoreCreateInfo sci = {VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO};\n\t\t\t\tVkAssert(vkCreateSemaphore(vk.device, &sci, vkallocationcb, &vk.acquiresemaphores[i]));\n\t\t\t\tDebugSetName(VK_OBJECT_TYPE_SEMAPHORE, (uint64_t)vk.acquiresemaphores[i], \"vk.acquiresemaphores\");\n\t\t\t\tvk.acquirefences[i] = VK_NULL_HANDLE;\n\t\t\t}\n\n\t\t\tvk.acquirebufferidx[vk.acquirelast%ACQUIRELIMIT] = vk.acquirelast%vk.backbuf_count;\n\t\t\tvk.acquirelast++;\n\t\t}\n\n\t\tfor (i = 0; i < vk.backbuf_count; i++)\n\t\t{\n\t\t\tVkMemoryRequirements mem_reqs;\n\t\t\tVkMemoryAllocateInfo memAllocInfo = {VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO};\n\t\t\tVkMemoryDedicatedAllocateInfoKHR khr_mdai = {VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR};\n\t\t\tVkImageCreateInfo ici = {VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO};\n\n\t\t\tici.flags = 0;\n\t\t\tici.imageType = VK_IMAGE_TYPE_2D;\n\t\t\tici.format = vk.backbufformat;\n\t\t\tici.extent.width = vid.pixelwidth;\n\t\t\tici.extent.height = vid.pixelheight;\n\t\t\tici.extent.depth = 1;\n\t\t\tici.mipLevels = 1;\n\t\t\tici.arrayLayers = 1;\n\t\t\tici.samples = VK_SAMPLE_COUNT_1_BIT;\n\t\t\tici.tiling = VK_IMAGE_TILING_OPTIMAL;\n\t\t\tici.usage = VK_IMAGE_USAGE_SAMPLED_BIT|VK_IMAGE_USAGE_TRANSFER_SRC_BIT|VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;\n\t\t\tici.sharingMode = VK_SHARING_MODE_EXCLUSIVE;\n\t\t\tici.queueFamilyIndexCount = 0;\n\t\t\tici.pQueueFamilyIndices = NULL;\n\t\t\tici.initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;\n\n\t\t\tVkAssert(vkCreateImage(vk.device, &ici, vkallocationcb, &images[i]));\n\t\t\tDebugSetName(VK_OBJECT_TYPE_IMAGE, (uint64_t)images[i], \"backbuffer\");\n\n\t\t\tvkGetImageMemoryRequirements(vk.device, images[i], &mem_reqs);\n\n\t\t\tmemAllocInfo.allocationSize = mem_reqs.size;\n\t\t\tmemAllocInfo.memoryTypeIndex = vk_find_memory_try(mem_reqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT|VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);\n\t\t\tif (memAllocInfo.memoryTypeIndex == ~0)\n\t\t\t\tmemAllocInfo.memoryTypeIndex = vk_find_memory_try(mem_reqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);\n\t\t\tif (memAllocInfo.memoryTypeIndex == ~0)\n\t\t\t\tmemAllocInfo.memoryTypeIndex = vk_find_memory_try(mem_reqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);\n\t\t\tif (memAllocInfo.memoryTypeIndex == ~0)\n\t\t\t\tmemAllocInfo.memoryTypeIndex = vk_find_memory_require(mem_reqs.memoryTypeBits, 0);\n\n\t\t\tif (vk.khr_dedicated_allocation)\n\t\t\t{\n\t\t\t\tkhr_mdai.pNext = memAllocInfo.pNext;\n\t\t\t\tkhr_mdai.image = images[i];\n\t\t\t\tmemAllocInfo.pNext = &khr_mdai;\n\t\t\t}\n\n\t\t\tVkAssert(vkAllocateMemory(vk.device, &memAllocInfo, vkallocationcb, &memories[i]));\n\t\t\tDebugSetName(VK_OBJECT_TYPE_DEVICE_MEMORY, (uint64_t)memories[i], \"VK_CreateSwapChain\");\n\t\t\tVkAssert(vkBindImageMemory(vk.device, images[i], memories[i], 0));\n\t\t}\n\t}\n\telse\n\t{\t//using vulkan's presentation engine.\n\t\tint BOOST_UNORM, BOOST_SNORM, BOOST_SRGB, BOOST_UFLOAT, BOOST_SFLOAT;\n\n\t\tif (vid_srgb.ival > 1)\n\t\t{\t//favour float formats, then srgb, then unorms\n\t\t\tBOOST_UNORM\t\t= 0;\n\t\t\tBOOST_SNORM\t\t= 0;\n\t\t\tBOOST_SRGB\t\t= 128;\n\t\t\tBOOST_UFLOAT\t= 256;\n\t\t\tBOOST_SFLOAT\t= 256;\n\t\t}\n\t\telse if (vid_srgb.ival)\n\t\t{\n\t\t\tBOOST_UNORM\t\t= 0;\n\t\t\tBOOST_SNORM\t\t= 0;\n\t\t\tBOOST_SRGB\t\t= 256;\n\t\t\tBOOST_UFLOAT\t= 128;\n\t\t\tBOOST_SFLOAT\t= 128;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tBOOST_UNORM\t\t= 256;\n\t\t\tBOOST_SNORM\t\t= 256;\n\t\t\tBOOST_SRGB\t\t= 0;\n\t\t\tBOOST_UFLOAT\t= 128;\n\t\t\tBOOST_SFLOAT\t= 128;\n\t\t}\n\n\t\tVkAssert(vkGetPhysicalDeviceSurfaceFormatsKHR(vk.gpu, vk.surface, &fmtcount, NULL));\n\t\tsurffmts = malloc(sizeof(VkSurfaceFormatKHR)*fmtcount);\n\t\tVkAssert(vkGetPhysicalDeviceSurfaceFormatsKHR(vk.gpu, vk.surface, &fmtcount, surffmts));\n\n\t\tVkAssert(vkGetPhysicalDeviceSurfacePresentModesKHR(vk.gpu, vk.surface, &presentmodes, NULL));\n\t\tpresentmode = malloc(sizeof(VkPresentModeKHR)*presentmodes);\n\t\tVkAssert(vkGetPhysicalDeviceSurfacePresentModesKHR(vk.gpu, vk.surface, &presentmodes, presentmode));\n\n\t\tvkGetPhysicalDeviceSurfaceCapabilitiesKHR(vk.gpu, vk.surface, &surfcaps);\n\n\t\tswapinfo.surface = vk.surface;\n\t\tswapinfo.minImageCount = surfcaps.minImageCount+vk.triplebuffer;\n\t\tif (swapinfo.minImageCount > surfcaps.maxImageCount)\n\t\t\tswapinfo.minImageCount = surfcaps.maxImageCount;\n\t\tif (swapinfo.minImageCount < surfcaps.minImageCount)\n\t\t\tswapinfo.minImageCount = surfcaps.minImageCount;\n\n\t\t// With offscreen rendering, the size is not known at first\n\t\tif (surfcaps.currentExtent.width == UINT32_MAX && surfcaps.currentExtent.height == UINT32_MAX)\n\t\t{\n\t\t\tswapinfo.imageExtent.width = bound(surfcaps.minImageExtent.width, vid.pixelwidth, surfcaps.maxImageExtent.width);\n\t\t\tswapinfo.imageExtent.height = bound(surfcaps.minImageExtent.height, vid.pixelheight, surfcaps.maxImageExtent.height);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tswapinfo.imageExtent.width = surfcaps.currentExtent.width;\n\t\t\tswapinfo.imageExtent.height = surfcaps.currentExtent.height;\n\t\t}\n\n\t\tswapinfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT|VK_IMAGE_USAGE_TRANSFER_SRC_BIT;\n\t\tswapinfo.preTransform = surfcaps.currentTransform;//VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;\n\t\tif (surfcaps.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR)\n\t\t\tswapinfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;\n\t\telse if (surfcaps.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR)\n\t\t{\n\t\t\tswapinfo.compositeAlpha = VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR;\n\t\t\tCon_Printf(CON_WARNING\"Vulkan swapchain using composite alpha premultiplied\\n\");\n\t\t}\n\t\telse if (surfcaps.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR)\n\t\t{\n\t\t\tswapinfo.compositeAlpha = VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR;\n\t\t\tCon_Printf(CON_WARNING\"Vulkan swapchain using composite alpha postmultiplied\\n\");\n\t\t}\n\t\telse\n\t\t{\n\t\t\tswapinfo.compositeAlpha = VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR;\t//erk?\n\t\t\tCon_Printf(CON_WARNING\"composite alpha inherit\\n\");\n\t\t}\n\t\tswapinfo.imageArrayLayers = /*(r_stereo_method.ival==1)?2:*/1;\n\t\tswapinfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;\n\t\tswapinfo.queueFamilyIndexCount = 0;\n\t\tswapinfo.pQueueFamilyIndices = NULL;\n\t\tswapinfo.clipped = vid_isfullscreen?VK_FALSE:VK_TRUE;\t//allow fragment shaders to be skipped on parts that are obscured by another window. screenshots might get weird, so use proper captures if required/automagic.\n\n\t\tswapinfo.presentMode = VK_PRESENT_MODE_FIFO_KHR;\t//support is guarenteed by spec, in theory.\n\t\tfor (i = 0, curpri = 0; i < presentmodes; i++)\n\t\t{\n\t\t\tuint32_t priority = 0;\n\t\t\tswitch(presentmode[i])\n\t\t\t{\n\t\t\tdefault://ignore it if we don't know it.\n\t\t\t\tbreak;\n\t\t\t\t//this is awkward. normally we use vsync<0 to allow tearing-with-vsync, but that leaves us with a problem as far as what 0 should signify - tearing or not.\n\t\t\t\t//if we're using mailbox then we could instead discard the command buffers and skip rendering of the actual scenes.\n\t\t\t\t//we could have our submission thread wait some time period after the last vswap (ie: before the next) before submitting the command.\n\t\t\t\t//this could reduce gpu load at higher resolutions without lying too much about cpu usage...\n\t\t\tcase VK_PRESENT_MODE_IMMEDIATE_KHR:\n\t\t\t\tpriority = (vk.vsync?0:2) + 2;\t//for most quake players, latency trumps tearing.\n\t\t\t\tbreak;\n\t\t\tcase VK_PRESENT_MODE_MAILBOX_KHR:\n\t\t\t\tpriority = (vk.vsync?0:2) + 1;\n\t\t\t\tbreak;\n\t\t\tcase VK_PRESENT_MODE_FIFO_KHR:\n\t\t\t\tpriority = (vk.vsync?2:0) + 1;\n\t\t\t\tbreak;\n\t\t\tcase VK_PRESENT_MODE_FIFO_RELAXED_KHR:\n\t\t\t\tpriority = (vk.vsync?2:0) + 2;\t//strict vsync results in weird juddering if rtlights etc caues framerates to drop below the refreshrate. and nvidia just suck with vsync, so I'm not taking any chances.\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (priority > curpri)\n\t\t\t{\n\t\t\t\tcurpri = priority;\n\t\t\t\tswapinfo.presentMode = presentmode[i];\n\t\t\t}\n\t\t}\n\n\t\tif (!vk.vsync && swapinfo.presentMode != VK_PRESENT_MODE_IMMEDIATE_KHR)\n\t\t\tif (!vk.swapchain)\t//only warn on vid_restart, otherwise its annoying when resizing.\n\t\t\t\tCon_Printf(\"Warning: vulkan graphics driver does not support VK_PRESENT_MODE_IMMEDIATE_KHR.\\n\");\n\n\t\tvk.srgbcapable = false;\n\t\tswapinfo.imageColorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR;\n\t\tswapinfo.imageFormat = VK_FORMAT_UNDEFINED;\n\t\tfor (i = 0, curpri = 0; i < fmtcount; i++)\n\t\t{\n\t\t\tuint32_t priority = 0;\n\n\t\t\tswitch(surffmts[i].format)\n\t\t\t{\n\t\t\tcase VK_FORMAT_B8G8R8A8_UNORM:\n\t\t\tcase VK_FORMAT_R8G8B8A8_UNORM:\n\t\t\tcase VK_FORMAT_A8B8G8R8_UNORM_PACK32:\n\t\t\t\tpriority = ((vid_bpp.ival>=24)?24:11)+BOOST_UNORM;\n\t\t\t\tbreak;\n\t\t\tcase VK_FORMAT_B8G8R8A8_SNORM:\n\t\t\tcase VK_FORMAT_R8G8B8A8_SNORM:\n\t\t\tcase VK_FORMAT_A8B8G8R8_SNORM_PACK32:\n\t\t\t\tpriority = ((vid_bpp.ival>=21)?21:2)+BOOST_SNORM;\n\t\t\t\tbreak;\n\t\t\tcase VK_FORMAT_B8G8R8A8_SRGB:\n\t\t\tcase VK_FORMAT_R8G8B8A8_SRGB:\n\t\t\tcase VK_FORMAT_A8B8G8R8_SRGB_PACK32:\n\t\t\t\tpriority = ((vid_bpp.ival>=24)?24:11)+BOOST_SRGB;\n\t\t\t\tvk.srgbcapable = true;\n\t\t\t\tbreak;\n\t\t\tcase VK_FORMAT_A2B10G10R10_UNORM_PACK32:\n\t\t\tcase VK_FORMAT_A2R10G10B10_UNORM_PACK32:\n\t\t\t\tpriority = ((vid_bpp.ival==30)?30:10)+BOOST_UNORM;\n\t\t\t\tbreak;\n\n\t\t\tcase VK_FORMAT_B10G11R11_UFLOAT_PACK32:\n\t\t\t\tpriority = ((vid_srgb.ival>=3||vid_bpp.ival==32)?32:11)+BOOST_UFLOAT;\n\t\t\t\tbreak;\n\t\t\tcase VK_FORMAT_R16G16B16A16_SFLOAT:\t//16bit per-channel formats\n\t\t\t\tpriority = ((vid_srgb.ival>=3||vid_bpp.ival>=48)?48:9)+BOOST_SFLOAT;\n\t\t\t\tbreak;\n\t\t\tcase VK_FORMAT_R16G16B16A16_UNORM:\n\t\t\t\tpriority = ((vid_srgb.ival>=3||vid_bpp.ival>=48)?48:9)+BOOST_UNORM;\n\t\t\t\tbreak;\n\t\t\tcase VK_FORMAT_R16G16B16A16_SNORM:\n\t\t\t\tpriority = ((vid_srgb.ival>=3||vid_bpp.ival>=48)?48:9)+BOOST_SFLOAT;\n\t\t\t\tbreak;\n\t\t\tcase VK_FORMAT_R32G32B32A32_SFLOAT:\t//32bit per-channel formats\n\t\t\t\tpriority = ((vid_bpp.ival>=47)?96:8)+BOOST_SFLOAT;\n\t\t\t\tbreak;\n\n\t\t\tcase VK_FORMAT_B5G6R5_UNORM_PACK16:\n\t\t\tcase VK_FORMAT_R5G6B5_UNORM_PACK16:\n\t\t\t\tpriority = 16+BOOST_UNORM;\n\t\t\t\tbreak;\n\t\t\tcase VK_FORMAT_R4G4B4A4_UNORM_PACK16:\n\t\t\tcase VK_FORMAT_B4G4R4A4_UNORM_PACK16:\n\t\t\t\tpriority = 12+BOOST_UNORM;\n\t\t\t\tbreak;\n\t\t\tcase VK_FORMAT_A1R5G5B5_UNORM_PACK16:\n\t\t\tcase VK_FORMAT_R5G5B5A1_UNORM_PACK16:\n\t\t\tcase VK_FORMAT_B5G5R5A1_UNORM_PACK16:\n\t\t\t\tpriority = 15+BOOST_UNORM;\n\t\t\t\tbreak;\n\n\t\t\tdefault:\t//no idea, use as lowest priority.\n\t\t\t\tpriority = 1;\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tif (surffmts[i].colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR &&\t\t\t\t//sRGB\n\t\t\t\tsurffmts[i].colorSpace == VK_COLOR_SPACE_EXTENDED_SRGB_NONLINEAR_EXT &&\t//scRGB\n\t\t\t\tsurffmts[i].colorSpace == VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT)\t\t\t//linear vaugely like sRGB\n\t\t\t\tpriority += 512;\t//always favour supported colour spaces.\n\n\t\t\tif (priority > curpri)\n\t\t\t{\n\t\t\t\tcurpri = priority;\n\t\t\t\tswapinfo.imageColorSpace = surffmts[i].colorSpace;\n\t\t\t\tswapinfo.imageFormat = surffmts[i].format;\n\t\t\t}\n\t\t}\n\n\t\tif (swapinfo.imageFormat == VK_FORMAT_UNDEFINED)\n\t\t{\t//if we found this format then it means the drivers don't really give a damn. pick a real format.\n\t\t\tif (vid_srgb.ival > 1 && swapinfo.imageColorSpace == VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT)\n\t\t\t\tswapinfo.imageFormat = VK_FORMAT_R16G16B16A16_SFLOAT;\n\t\t\telse if (vid_srgb.ival)\n\t\t\t\tswapinfo.imageFormat = VK_FORMAT_R8G8B8A8_SRGB;\n\t\t\telse\n\t\t\t\tswapinfo.imageFormat = VK_FORMAT_R8G8B8A8_UNORM;\n\t\t}\n\n\t\tif (vk.backbufformat != swapinfo.imageFormat)\n\t\t{\n\t\t\tVK_DestroyRenderPasses();\n\t\t\treloadshaders = true;\n\t\t}\n\t\tvk.backbufformat = swapinfo.imageFormat;\n\n\t\t//VK_COLORSPACE_SRGB_NONLINEAR means the presentation engine will interpret the image as SRGB whether its a UNORM or SRGB format or not.\n\t\t//an SRGB format JUST means rendering converts linear->srgb and does not apply to the presentation engine.\n\t\tvid.flags &= ~VID_SRGB_FB;\n\t\tif (swapinfo.imageColorSpace == VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT)\n\t\t\tvid.flags |= VID_SRGB_FB_LINEAR;\n\t\telse\n\t\t{\n\t\t\tswitch(vk.backbufformat)\n\t\t\t{\n\t\t\tcase VK_FORMAT_R8G8B8_SRGB:\n\t\t\tcase VK_FORMAT_B8G8R8_SRGB:\n\t\t\tcase VK_FORMAT_B8G8R8A8_SRGB:\n\t\t\tcase VK_FORMAT_R8G8B8A8_SRGB:\n\t\t\tcase VK_FORMAT_A8B8G8R8_SRGB_PACK32:\n\t\t\t\tvid.flags |= VID_SRGB_FB_LINEAR;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tbreak;\t//non-srgb (or compressed)\n\t\t\t}\n\t\t}\n\n\t\tfree(presentmode);\n\t\tfree(surffmts);\n\n\t\tif (vid_isfullscreen)\t//nvidia really doesn't like this. its fine when windowed though.\n\t\t\tVK_DestroySwapChain();\n\t\tswapinfo.oldSwapchain = vk.swapchain;\n\n\t\tnewvkswapchain = VK_NULL_HANDLE;\n\t\terr = vkCreateSwapchainKHR(vk.device, &swapinfo, vkallocationcb, &newvkswapchain);\n\t\tswitch(err)\n\t\t{\n\t\tcase VK_SUCCESS:\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tSys_Error(\"vkCreateSwapchainKHR returned undocumented error!\\n\");\n\t\tcase VK_ERROR_OUT_OF_HOST_MEMORY:\n        case VK_ERROR_OUT_OF_DEVICE_MEMORY:\n        case VK_ERROR_DEVICE_LOST:\n        case VK_ERROR_SURFACE_LOST_KHR:\n        case VK_ERROR_NATIVE_WINDOW_IN_USE_KHR:\n        case VK_ERROR_INITIALIZATION_FAILED:\n\t\t\tif (swapinfo.oldSwapchain)\n\t\t\t\tCon_Printf(CON_WARNING\"vkCreateSwapchainKHR(%u * %u) failed with error %s\\n\", swapinfo.imageExtent.width, swapinfo.imageExtent.height, VK_VKErrorToString(err));\n\t\t\telse\n\t\t\t\tSys_Error(\"vkCreateSwapchainKHR(%u * %u) failed with error %s\\n\", swapinfo.imageExtent.width, swapinfo.imageExtent.height, VK_VKErrorToString(err));\n\t\t\tVK_DestroySwapChain();\n\t\t\treturn false;\n        }\n\t\tif (!newvkswapchain)\n\t\t\treturn false;\n\t\tif (vk.swapchain)\n\t\t{\n\t\t\tVK_DestroySwapChain();\n\t\t}\n\t\tvk.swapchain = newvkswapchain;\n\n\t\tVkAssert(vkGetSwapchainImagesKHR(vk.device, vk.swapchain, &vk.backbuf_count, NULL));\n\t\timages = malloc(sizeof(VkImage)*vk.backbuf_count);\n\t\tmemories = NULL;\n\t\tVkAssert(vkGetSwapchainImagesKHR(vk.device, vk.swapchain, &vk.backbuf_count, images));\n\n\t\tvk.acquirelast = vk.acquirenext = 0;\n\t\tfor (i = 0; i < ACQUIRELIMIT; i++)\n\t\t{\n\t\t\tif (vk_waitfence.ival || !*vk_waitfence.string)\n\t\t\t{\n\t\t\t\tVkFenceCreateInfo fci = {VK_STRUCTURE_TYPE_FENCE_CREATE_INFO};\n\t\t\t\tVkAssert(vkCreateFence(vk.device,&fci,vkallocationcb,&vk.acquirefences[i]));\n\t\t\t\tvk.acquiresemaphores[i] = VK_NULL_HANDLE;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tVkSemaphoreCreateInfo sci = {VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO};\n\t\t\t\tVkAssert(vkCreateSemaphore(vk.device, &sci, vkallocationcb, &vk.acquiresemaphores[i]));\n\t\t\t\tDebugSetName(VK_OBJECT_TYPE_SEMAPHORE, (uint64_t)vk.acquiresemaphores[i], \"vk.acquiresemaphores\");\n\t\t\t\tvk.acquirefences[i] = VK_NULL_HANDLE;\n\t\t\t}\n\t\t}\n\t\tif (!vk_submissionthread.value && *vk_submissionthread.string)\n\t\t\tpreaquirecount = 1;\t//no real point asking for more.\n\t\telse\n\t\t\tpreaquirecount = vk.backbuf_count;\n\t\t/*-1 to hide any weird thread issues*/\n\t\twhile (vk.acquirelast < ACQUIRELIMIT-1 && vk.acquirelast < preaquirecount && vk.acquirelast <= vk.backbuf_count-surfcaps.minImageCount)\n\t\t{\n\t\t\tVkAssert(vkAcquireNextImageKHR(vk.device, vk.swapchain, UINT64_MAX, vk.acquiresemaphores[vk.acquirelast%ACQUIRELIMIT], vk.acquirefences[vk.acquirelast%ACQUIRELIMIT], &vk.acquirebufferidx[vk.acquirelast%ACQUIRELIMIT]));\n\t\t\tvk.acquirelast++;\n\t\t}\n\t}\n\n\toldms = vk.multisamplebits;\n\n\tvk.multisamplebits = VK_SAMPLE_COUNT_1_BIT;\n\tif (vid_multisample.ival>1)\n\t{\n\t\tVkSampleCountFlags fl = vk.limits.framebufferColorSampleCounts & vk.limits.framebufferDepthSampleCounts;\n//\t\tCon_Printf(\"Warning: vulkan multisample does not work with rtlights or render targets etc etc\\n\");\n\t\tfor (i = 1; i < 30; i++)\n\t\t\tif ((fl & (1<<i)) && (1<<i) <= vid_multisample.ival)\n\t\t\t\tvk.multisamplebits = (1<<i);\n\t}\n\n\trpassflags = RP_PRESENTABLE;\n\n\t//destroy+recreate the renderpass if something changed that prevents them being compatible (this also requires rebuilding all the pipelines too, which sucks).\n\tif (oldms != vk.multisamplebits || oldformat != vk.backbufformat || olddepthformat != vk.depthformat)\n\t{\n\t\tVK_DestroyRenderPasses();\n\t\treloadshaders = true;\n\t}\n\n\tif (reloadshaders)\n\t{\n\t\tShader_NeedReload(true);\n\t\tShader_DoReload();\n\t}\n\n\tattachments[0] = VK_NULL_HANDLE;\t//colour\n\tattachments[1] = VK_NULL_HANDLE;\t//depth\n\tattachments[2] = VK_NULL_HANDLE;\t//mscolour\n\n\tif (rpassflags & RP_MULTISAMPLE)\n\t\tfb_info.attachmentCount = 3;\n\telse\n\t{\n\t\trpassflags &= ~RP_PRESENTABLE;\n\t\tfb_info.attachmentCount = 2;\n\t}\n\tfb_info.renderPass = VK_GetRenderPass(RP_FULLCLEAR|rpassflags);\n\tfb_info.pAttachments = attachments;\n\tfb_info.width = swapinfo.imageExtent.width;\n\tfb_info.height = swapinfo.imageExtent.height;\n\tfb_info.layers = 1;\n\n\n\tvk.backbufs = malloc(sizeof(*vk.backbufs)*vk.backbuf_count);\n\tmemset(vk.backbufs, 0, sizeof(*vk.backbufs)*vk.backbuf_count);\n\tfor (i = 0; i < vk.backbuf_count; i++)\n\t{\n\t\tVkImageViewCreateInfo ivci = {VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO};\n\n\t\tvk.backbufs[i].colour.image = images[i];\n\t\tDebugSetName(VK_OBJECT_TYPE_IMAGE, (uint64_t)vk.backbufs[i].colour.image, \"backbuffer\");\n\n\t\tivci.format = vk.backbufformat;\n//\t\tivci.components.r = VK_COMPONENT_SWIZZLE_R;\n//\t\tivci.components.g = VK_COMPONENT_SWIZZLE_G;\n//\t\tivci.components.b = VK_COMPONENT_SWIZZLE_B;\n//\t\tivci.components.a = VK_COMPONENT_SWIZZLE_A;\n\t\tivci.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;\n\t\tivci.subresourceRange.baseMipLevel = 0;\n\t\tivci.subresourceRange.levelCount = 1;\n\t\tivci.subresourceRange.baseArrayLayer = 0;\n\t\tivci.subresourceRange.layerCount = 1;\n\t\tivci.viewType = VK_IMAGE_VIEW_TYPE_2D;\n\t\tivci.flags = 0;\n\t\tivci.image = images[i];\n\t\tif (memories)\n\t\t\tvk.backbufs[i].colour.mem.memory = memories[i];\n\t\tvk.backbufs[i].colour.width = swapinfo.imageExtent.width;\n\t\tvk.backbufs[i].colour.height = swapinfo.imageExtent.height;\n\t\tVkAssert(vkCreateImageView(vk.device, &ivci, vkallocationcb, &vk.backbufs[i].colour.view));\n\n\t\tvk.backbufs[i].firstuse = true;\n\n\t\t//create the depth buffer texture. possibly multisampled.\n\t\t{\n\t\t\t//depth image\n\t\t\t{\n\t\t\t\tVkImageCreateInfo depthinfo = {VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO};\n\t\t\t\tdepthinfo.flags = 0;\n\t\t\t\tdepthinfo.imageType = VK_IMAGE_TYPE_2D;\n\t\t\t\tdepthinfo.format = vk.depthformat;\n\t\t\t\tdepthinfo.extent.width = swapinfo.imageExtent.width;\n\t\t\t\tdepthinfo.extent.height = swapinfo.imageExtent.height;\n\t\t\t\tdepthinfo.extent.depth = 1;\n\t\t\t\tdepthinfo.mipLevels = 1;\n\t\t\t\tdepthinfo.arrayLayers = 1;\n\t\t\t\tdepthinfo.samples = (rpassflags & RP_MULTISAMPLE)?vk.multisamplebits:VK_SAMPLE_COUNT_1_BIT;\n\t\t\t\tdepthinfo.tiling = VK_IMAGE_TILING_OPTIMAL;\n\t\t\t\tdepthinfo.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;\n\t\t\t\tdepthinfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;\n\t\t\t\tdepthinfo.queueFamilyIndexCount = 0;\n\t\t\t\tdepthinfo.pQueueFamilyIndices = NULL;\n\t\t\t\tdepthinfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;\n\t\t\t\tVkAssert(vkCreateImage(vk.device, &depthinfo, vkallocationcb, &vk.backbufs[i].depth.image));\n\t\t\t\tDebugSetName(VK_OBJECT_TYPE_IMAGE, (uint64_t)vk.backbufs[i].depth.image, \"backbuffer depth\");\n\t\t\t}\n\n\t\t\t//depth memory\n\t\t\tVK_AllocateBindImageMemory(&vk.backbufs[i].depth, true);\n\n\t\t\t//depth view\n\t\t\t{\n\t\t\t\tVkImageViewCreateInfo depthviewinfo = {VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO};\n\t\t\t\tdepthviewinfo.format = vk.depthformat;\n\t\t\t\tdepthviewinfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;\n\t\t\t\tdepthviewinfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;\n\t\t\t\tdepthviewinfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;\n\t\t\t\tdepthviewinfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;\n\t\t\t\tdepthviewinfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;//|VK_IMAGE_ASPECT_STENCIL_BIT;\n\t\t\t\tdepthviewinfo.subresourceRange.baseMipLevel = 0;\n\t\t\t\tdepthviewinfo.subresourceRange.levelCount = 1;\n\t\t\t\tdepthviewinfo.subresourceRange.baseArrayLayer = 0;\n\t\t\t\tdepthviewinfo.subresourceRange.layerCount = 1;\n\t\t\t\tdepthviewinfo.viewType = VK_IMAGE_VIEW_TYPE_2D;\n\t\t\t\tdepthviewinfo.flags = 0;\n\t\t\t\tdepthviewinfo.image = vk.backbufs[i].depth.image;\n\t\t\t\tVkAssert(vkCreateImageView(vk.device, &depthviewinfo, vkallocationcb, &vk.backbufs[i].depth.view));\n\t\t\t\tattachments[1] = vk.backbufs[i].depth.view;\n\t\t\t}\n\t\t}\n\n\t\t//if we're using multisampling, create the intermediate multisample texture that we're actually going to render to.\n\t\tif (rpassflags & RP_MULTISAMPLE)\n\t\t{\n\t\t\t//mscolour image\n\t\t\t{\n\t\t\t\tVkImageCreateInfo mscolourinfo = {VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO};\n\t\t\t\tmscolourinfo.flags = 0;\n\t\t\t\tmscolourinfo.imageType = VK_IMAGE_TYPE_2D;\n\t\t\t\tmscolourinfo.format = vk.backbufformat;\n\t\t\t\tmscolourinfo.extent.width = swapinfo.imageExtent.width;\n\t\t\t\tmscolourinfo.extent.height = swapinfo.imageExtent.height;\n\t\t\t\tmscolourinfo.extent.depth = 1;\n\t\t\t\tmscolourinfo.mipLevels = 1;\n\t\t\t\tmscolourinfo.arrayLayers = 1;\n\t\t\t\tmscolourinfo.samples = vk.multisamplebits;\n\t\t\t\tmscolourinfo.tiling = VK_IMAGE_TILING_OPTIMAL;\n\t\t\t\tmscolourinfo.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;\n\t\t\t\tmscolourinfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;\n\t\t\t\tmscolourinfo.queueFamilyIndexCount = 0;\n\t\t\t\tmscolourinfo.pQueueFamilyIndices = NULL;\n\t\t\t\tmscolourinfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;\n\t\t\t\tVkAssert(vkCreateImage(vk.device, &mscolourinfo, vkallocationcb, &vk.backbufs[i].mscolour.image));\n\t\t\t\tDebugSetName(VK_OBJECT_TYPE_IMAGE, (uint64_t)vk.backbufs[i].mscolour.image, \"multisample\");\n\t\t\t}\n\n\t\t\t//mscolour memory\n\t\t\tVK_AllocateBindImageMemory(&vk.backbufs[i].mscolour, true);\n\n\t\t\t//mscolour view\n\t\t\t{\n\t\t\t\tVkImageViewCreateInfo mscolourviewinfo = {VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO};\n\t\t\t\tmscolourviewinfo.format = vk.backbufformat;\n\t\t\t\tmscolourviewinfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;\n\t\t\t\tmscolourviewinfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;\n\t\t\t\tmscolourviewinfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;\n\t\t\t\tmscolourviewinfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;\n\t\t\t\tmscolourviewinfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;\n\t\t\t\tmscolourviewinfo.subresourceRange.baseMipLevel = 0;\n\t\t\t\tmscolourviewinfo.subresourceRange.levelCount = 1;\n\t\t\t\tmscolourviewinfo.subresourceRange.baseArrayLayer = 0;\n\t\t\t\tmscolourviewinfo.subresourceRange.layerCount = 1;\n\t\t\t\tmscolourviewinfo.viewType = VK_IMAGE_VIEW_TYPE_2D;\n\t\t\t\tmscolourviewinfo.flags = 0;\n\t\t\t\tmscolourviewinfo.image = vk.backbufs[i].mscolour.image;\n\t\t\t\tVkAssert(vkCreateImageView(vk.device, &mscolourviewinfo, vkallocationcb, &vk.backbufs[i].mscolour.view));\n\t\t\t\tattachments[2] = vk.backbufs[i].mscolour.view;\n\t\t\t}\n\t\t}\n\n\n\t\tattachments[0] = vk.backbufs[i].colour.view;\n\t\tVkAssert(vkCreateFramebuffer(vk.device, &fb_info, vkallocationcb, &vk.backbufs[i].framebuffer));\n\n\t\t{\n\t\t\tVkSemaphoreCreateInfo seminfo = {VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO};\n\t\t\tVkAssert(vkCreateSemaphore(vk.device, &seminfo, vkallocationcb, &vk.backbufs[i].presentsemaphore));\n\t\t\tDebugSetName(VK_OBJECT_TYPE_SEMAPHORE, (uint64_t)vk.backbufs[i].presentsemaphore, \"vk.backbufs.presentsemaphore\");\n\t\t}\n\t}\n\tfree(images);\n\tfree(memories);\n\n\tvid.pixelwidth = swapinfo.imageExtent.width;\n\tvid.pixelheight = swapinfo.imageExtent.height;\n\tR2D_Console_Resize();\n\n\treturn true;\n}\n\n\t\nvoid\tVK_Draw_Init(void)\n{\n\tR2D_Init();\n}\nvoid\tVK_Draw_Shutdown(void)\n{\n\tR2D_Shutdown();\n\tShader_Shutdown();\n\tImage_Shutdown();\n}\n\nstatic void VK_DestroySampler(VkSampler s)\n{\n\tstruct vksamplers_s *ref;\n\tfor (ref = vk.samplers; ref; ref = ref->next)\n\t{\n\t\tif (ref->samp == s)\n\t\t{\n\t\t\tif (--ref->usages == 0)\n\t\t\t{\n\t\t\t\tvkDestroySampler(vk.device, ref->samp, vkallocationcb);\n\t\t\t\t*ref->link = ref->next;\n\t\t\t\tif (ref->next)\n\t\t\t\t\tref->next->link = ref->link;\n\t\t\t\tZ_Free(ref);\n\t\t\t}\n\t\t}\n\t}\n}\nstatic void VK_DestroySampler_FrameEnd(void *w)\n{\n\tVK_DestroySampler(*(VkSampler*)w);\n}\n\nvoid VK_CreateSamplerInfo(VkSamplerCreateInfo *info, vk_image_t *img)\n{\n\tunsigned int flags = IF_RENDERTARGET;\n\tstruct vksamplers_s *ref;\n\n\tif (img->sampler)\n\t\tVK_DestroySampler(img->sampler);\n\n\n\tfor (ref = vk.samplers; ref; ref = ref->next)\n\t\tif (ref->flags == flags)\n\t\t\tif (!memcmp(&ref->props, info, sizeof(*info)))\n\t\t\t\tbreak;\n\n\tif (!ref)\n\t{\n\t\tref = Z_Malloc(sizeof(*ref));\n\t\tref->flags = flags;\n\t\tref->props = *info;\n\t\tref->next = vk.samplers;\n\t\tref->link = &vk.samplers;\n\t\tif (vk.samplers)\n\t\t\tvk.samplers->link = &ref->next;\n\t\tvk.samplers = ref;\n\t\tVkAssert(vkCreateSampler(vk.device, &ref->props, NULL, &ref->samp));\n\t}\n\tref->usages++;\n\timg->sampler = ref->samp;\n}\nvoid VK_CreateSampler(unsigned int flags, vk_image_t *img)\n{\n\tstruct vksamplers_s *ref;\n\tqboolean clamptoedge = flags & IF_CLAMP;\n\tVkSamplerCreateInfo lmsampinfo = {VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO};\n\n\n\tif (img->sampler)\n\t\tVK_DestroySampler(img->sampler);\n\n\tif (flags & IF_LINEAR)\n\t{\n\t\tlmsampinfo.minFilter = lmsampinfo.magFilter = VK_FILTER_LINEAR;\n\t\tlmsampinfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;\n\t}\n\telse if (flags & IF_NEAREST)\n\t{\n\t\tlmsampinfo.minFilter = lmsampinfo.magFilter = VK_FILTER_NEAREST;\n\t\tlmsampinfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST;\n\t}\n\telse\n\t{\n\t\tint *filter = (flags & IF_UIPIC)?vk.filterpic:vk.filtermip;\n\t\tif (filter[0])\n\t\t\tlmsampinfo.minFilter = VK_FILTER_LINEAR;\n\t\telse\n\t\t\tlmsampinfo.minFilter = VK_FILTER_NEAREST;\n\t\tif (filter[1])\n\t\t\tlmsampinfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;\n\t\telse\n\t\t\tlmsampinfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST;\n\t\tif (filter[2])\n\t\t\tlmsampinfo.magFilter = VK_FILTER_LINEAR;\n\t\telse\n\t\t\tlmsampinfo.magFilter = VK_FILTER_NEAREST;\n\t}\n\n\tlmsampinfo.addressModeU = clamptoedge?VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE:VK_SAMPLER_ADDRESS_MODE_REPEAT;\n\tlmsampinfo.addressModeV = clamptoedge?VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE:VK_SAMPLER_ADDRESS_MODE_REPEAT;\n\tlmsampinfo.addressModeW = clamptoedge?VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE:VK_SAMPLER_ADDRESS_MODE_REPEAT;\n\tlmsampinfo.mipLodBias = vk.lodbias;\n\tlmsampinfo.anisotropyEnable = (flags & IF_NEAREST)?false:(vk.max_anistophy > 1);\n\tlmsampinfo.maxAnisotropy = vk.max_anistophy;\n\tlmsampinfo.compareEnable = VK_FALSE;\n\tlmsampinfo.compareOp = VK_COMPARE_OP_NEVER;\n\tlmsampinfo.minLod = vk.mipcap[0];\t//this isn't quite right\n\tlmsampinfo.maxLod = vk.mipcap[1];\n\tlmsampinfo.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK;\n\tlmsampinfo.unnormalizedCoordinates = VK_FALSE;\n\n\tfor (ref = vk.samplers; ref; ref = ref->next)\n\t\tif (ref->flags == flags)\n\t\t\tif (!memcmp(&ref->props, &lmsampinfo, sizeof(lmsampinfo)))\n\t\t\t\tbreak;\n\n\tif (!ref)\n\t{\n\t\tref = Z_Malloc(sizeof(*ref));\n\t\tref->flags = flags;\n\t\tref->props = lmsampinfo;\n\t\tref->next = vk.samplers;\n\t\tref->link = &vk.samplers;\n\t\tif (vk.samplers)\n\t\t\tvk.samplers->link = &ref->next;\n\t\tvk.samplers = ref;\n\t\tVkAssert(vkCreateSampler(vk.device, &ref->props, NULL, &ref->samp));\n\t}\n\tref->usages++;\n\timg->sampler = ref->samp;\n}\n\nvoid VK_UpdateFiltering(image_t *imagelist, int filtermip[3], int filterpic[3], int mipcap[2], float lodbias, float anis)\n{\n\tuint32_t i;\n\tfor (i = 0; i < countof(vk.filtermip); i++)\n\t\tvk.filtermip[i] = filtermip[i];\n\tfor (i = 0; i < countof(vk.filterpic); i++)\n\t\tvk.filterpic[i] = filterpic[i];\n\tfor (i = 0; i < countof(vk.mipcap); i++)\n\t\tvk.mipcap[i] = mipcap[i];\n\tvk.lodbias = lodbias;\n\tvk.max_anistophy = bound(1.0, anis, vk.limits.maxSamplerAnisotropy);\n\n\twhile(imagelist)\n\t{\n\t\tif (imagelist->vkimage)\n\t\t{\n\t\t\tif (imagelist->vkimage->sampler)\n\t\t\t{\t//the sampler might still be in use, so clean it up at the end of the frame.\n\t\t\t\t//all this to avoid syncing all the queues...\n\t\t\t\tVK_AtFrameEnd(VK_DestroySampler_FrameEnd, &imagelist->vkimage->sampler, sizeof(imagelist->vkimage->sampler));\n\t\t\t\timagelist->vkimage->sampler = VK_NULL_HANDLE;\n\t\t\t}\n\t\t\tVK_CreateSampler(imagelist->flags, imagelist->vkimage);\n\t\t}\n\t\timagelist = imagelist->next;\n\t}\n}\n\nqboolean VK_AllocatePoolMemory(uint32_t pooltype, VkDeviceSize memsize, VkDeviceSize poolalignment, vk_poolmem_t *mem)\n{\n\tstruct vk_mempool_s *p;\n\tVkDeviceSize pad;\n\n\tif (!vk_usememorypools.ival)\n\t\treturn false;\n\n//\tif (memsize > 1024*1024*4)\n//\t\treturn false;\n\tfor (p = vk.mempools; p; p = p->next)\n\t{\n\t\tif (p->memtype == pooltype)\n\t\t{\n\t\t\tif (p->memoryoffset + poolalignment + memsize < p->memorysize)\n\t\t\t\tbreak;\n\t\t}\n\t}\n\tif (!p)\n\t{\n\t\tVkMemoryAllocateInfo poolai = {VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO};\n\t\tp = Z_Malloc(sizeof(*p));\n\t\tp->memorysize = poolai.allocationSize = 512*1024*1024;\t//lets just allocate big...\n\t\tp->memtype = poolai.memoryTypeIndex = pooltype;\n\n\t\tif (VK_SUCCESS != vkAllocateMemory(vk.device, &poolai, vkallocationcb, &p->memory))\n\t\t{\t//out of memory? oh well, a smaller dedicated allocation might still work.\n\t\t\tZ_Free(p);\n\t\t\treturn false;\n\t\t}\n\t\tDebugSetName(VK_OBJECT_TYPE_DEVICE_MEMORY, (uint64_t)p->memory, \"VK_AllocatePoolMemory\");\n\t\tp->next = vk.mempools;\n\t\tvk.mempools = p;\n\t}\n\tpad = ((p->memoryoffset+poolalignment-1)&~(poolalignment-1)) - p->memoryoffset;\n\tp->memoryoffset = (p->memoryoffset+poolalignment-1)&~(poolalignment-1);\n\tp->gaps += pad;\n\tmem->offset = p->memoryoffset;\n\tmem->size = memsize;\t//FIXME: we have no way to deal with gaps due to alignment\n\tmem->memory = p->memory;\n\tmem->pool = p;\n\n\tp->memoryoffset += memsize;\n\treturn true;\n}\nvoid VK_ReleasePoolMemory(vk_poolmem_t *mem)\n{\n\tif (mem->pool)\n\t{\n\t\t//FIXME: track power-of-two holes?\n\t\tmem->pool->gaps += mem->size;\n\t\tmem->pool = NULL;\n\t\tmem->memory = VK_NULL_HANDLE;\n\t}\n\telse if (mem->memory)\n\t{\n\t\tvkFreeMemory(vk.device, mem->memory, vkallocationcb);\n\t\tmem->memory = VK_NULL_HANDLE;\n\t}\n}\n\n\n//does NOT bind.\n//image memory is NOT expected to be host-visible. you'll get what vulkan gives you.\nqboolean VK_AllocateImageMemory(VkImage image, qboolean dedicated, vk_poolmem_t *mem)\n{\n\tuint32_t pooltype;\n\tVkMemoryRequirements2KHR mem_reqs2 = {VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR};\n\n\tif (!dedicated && vk.khr_get_memory_requirements2)\n\t{\n\t\tVkImageMemoryRequirementsInfo2KHR imri = {VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2_KHR};\n\t\tVkMemoryDedicatedRequirementsKHR mdr = {VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS_KHR};\n\t\timri.image = image;\n\t\tif (vk.khr_dedicated_allocation)\n\t\t\tmem_reqs2.pNext = &mdr;\t//chain the result struct\n\t\tvkGetImageMemoryRequirements2KHR(vk.device, &imri, &mem_reqs2);\n\n\t\t//and now we know if it should be dedicated or not.\n\t\tdedicated |= mdr.prefersDedicatedAllocation || mdr.requiresDedicatedAllocation;\n\t}\n\telse\n\t\tvkGetImageMemoryRequirements(vk.device, image, &mem_reqs2.memoryRequirements);\n\n\tpooltype = vk_find_memory_try(mem_reqs2.memoryRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);\n\tif (pooltype == ~0)\n\t\tpooltype = vk_find_memory_require(mem_reqs2.memoryRequirements.memoryTypeBits, 0);\n\n\tif (!dedicated && VK_AllocatePoolMemory(pooltype, mem_reqs2.memoryRequirements.size, mem_reqs2.memoryRequirements.alignment, mem))\n\t\treturn true;\t//got a shared allocation.\n\telse\n\t{\t//make it dedicated one way or another.\n\t\tVkMemoryAllocateInfo memAllocInfo = {VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO};\n\t\tVkMemoryDedicatedAllocateInfoKHR khr_mdai = {VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR};\n\t\tVkResult err;\n\n\t\t//shouldn't really happen, but just in case...\n\t\tmem_reqs2.memoryRequirements.size = max(1,mem_reqs2.memoryRequirements.size);\n\n\t\tmemAllocInfo.allocationSize = mem_reqs2.memoryRequirements.size;\n\t\tmemAllocInfo.memoryTypeIndex = pooltype;\n\t\tif (vk.khr_dedicated_allocation)\n\t\t{\n\t\t\tkhr_mdai.image = image;\n\t\t\tkhr_mdai.pNext = memAllocInfo.pNext;\n\t\t\tmemAllocInfo.pNext = &khr_mdai;\n\t\t}\n\n\t\tmem->pool = NULL;\n\t\tmem->offset = 0;\n\t\tmem->size = mem_reqs2.memoryRequirements.size;\n\t\tmem->memory = VK_NULL_HANDLE;\n\n\t\terr = vkAllocateMemory(vk.device, &memAllocInfo, vkallocationcb, &mem->memory);\n\t\tif (err != VK_SUCCESS)\n\t\t\treturn false;\n\t\tDebugSetName(VK_OBJECT_TYPE_DEVICE_MEMORY, (uint64_t)mem->memory, \"VK_AllocateImageMemory\");\n\t\treturn true;\n\t}\n}\nqboolean VK_AllocateBindImageMemory(vk_image_t *image, qboolean dedicated)\n{\n\tif (VK_AllocateImageMemory(image->image, dedicated, &image->mem))\n\t{\n\t\tVkAssert(vkBindImageMemory(vk.device, image->image, image->mem.memory, image->mem.offset));\n\t\treturn true;\n\t}\n\treturn false;\t//out of memory?\n}\n\n\nvk_image_t VK_CreateTexture2DArray(uint32_t width, uint32_t height, uint32_t layers, uint32_t mips, uploadfmt_t encoding, unsigned int type, qboolean rendertarget, const char *debugname)\n{\n\tvk_image_t ret;\n\tVkImageViewCreateInfo viewInfo = {VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO};\n\tVkImageCreateInfo ici = {VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO};\n\tVkFormat format = VK_FORMAT_UNDEFINED;\n#ifdef VK_EXT_astc_decode_mode\n\tVkImageViewASTCDecodeModeEXT astcmode;\n#endif\n\n\tret.width = width;\n\tret.height = height;\n\tret.layers = layers;\n\tret.mipcount = mips;\n\tret.encoding = encoding;\n\tret.type = type;\n\tret.layout = VK_IMAGE_LAYOUT_UNDEFINED;\n\n\t//vulkan expresses packed formats in terms of native endian (if big-endian, then everything makes sense), non-packed formats are expressed in byte order (consistent with big-endian).\n\t//PTI formats are less well-defined...\n\tif ((int)encoding < 0) \n\t\tformat = -(int)encoding;\n\telse switch(encoding)\n\t{\n\t//16bit formats.\n\tcase PTI_RGB565:\t\t\tformat = VK_FORMAT_R5G6B5_UNORM_PACK16;\t\t\tbreak;\n\tcase PTI_RGBA4444:\t\t\tformat = VK_FORMAT_R4G4B4A4_UNORM_PACK16;\t\tbreak;\n\tcase PTI_ARGB4444:\t\t\t/*format = VK_FORMAT_A4R4G4B4_UNORM_PACK16;*/\tbreak;\n\tcase PTI_RGBA5551:\t\t\tformat = VK_FORMAT_R5G5B5A1_UNORM_PACK16;\t\tbreak;\n\tcase PTI_ARGB1555:\t\t\tformat = VK_FORMAT_A1R5G5B5_UNORM_PACK16;\t\tbreak;\n\tcase PTI_R16:\t\t\t\tformat = VK_FORMAT_R16_UNORM;\t\t\t\t\tbreak;\n\tcase PTI_RGBA16:\t\t\tformat = VK_FORMAT_R16G16B16A16_UNORM;\t\t\tbreak;\n\t//float formats\n\tcase PTI_R16F:\t\t\t\tformat = VK_FORMAT_R16_SFLOAT;\t\t\t\t\tbreak;\n\tcase PTI_R32F:\t\t\t\tformat = VK_FORMAT_R32_SFLOAT;\t\t\t\t\tbreak;\n\tcase PTI_RGBA16F:\t\t\tformat = VK_FORMAT_R16G16B16A16_SFLOAT;\t\t\tbreak;\n\tcase PTI_RGBA32F:\t\t\tformat = VK_FORMAT_R32G32B32A32_SFLOAT;\t\t\tbreak;\n\t//weird formats\n\tcase PTI_P8:\n\tcase PTI_R8:\t\t\t\tformat = VK_FORMAT_R8_UNORM;\t\t\t\t\tbreak;\n\tcase PTI_RG8:\t\t\t\tformat = VK_FORMAT_R8G8_UNORM;\t\t\t\t\tbreak;\n\tcase PTI_R8_SNORM:\t\t\tformat = VK_FORMAT_R8_SNORM;\t\t\t\t\tbreak;\n\tcase PTI_RG8_SNORM:\t\t\tformat = VK_FORMAT_R8G8_SNORM;\t\t\t\t\tbreak;\n\tcase PTI_A2BGR10:\t\t\tformat = VK_FORMAT_A2B10G10R10_UNORM_PACK32;\tbreak;\n\tcase PTI_E5BGR9:\t\t\tformat = VK_FORMAT_E5B9G9R9_UFLOAT_PACK32;\t\tbreak;\n\tcase PTI_B10G11R11F:\t\tformat = VK_FORMAT_B10G11R11_UFLOAT_PACK32;\t\tbreak;\n\t//swizzled/legacy formats\n\tcase PTI_L8:\t\t\t\tformat = VK_FORMAT_R8_UNORM;\t\t\t\t\tbreak;\n\tcase PTI_L8A8:\t\t\t\tformat = VK_FORMAT_R8G8_UNORM;\t\t\t\t\tbreak;\n\tcase PTI_L8_SRGB:\t\t\tformat = VK_FORMAT_R8_SRGB;\t\t\t\t\t\tbreak;\n\tcase PTI_L8A8_SRGB:\t\t\t/*unsupportable*/\t\t\t\t\t\t\t\tbreak;\n\t//compressed formats\n\tcase PTI_BC1_RGB:\t\t\tformat = VK_FORMAT_BC1_RGB_UNORM_BLOCK;\t\t\tbreak;\n\tcase PTI_BC1_RGB_SRGB:\t\tformat = VK_FORMAT_BC1_RGB_SRGB_BLOCK;\t\t\tbreak;\n\tcase PTI_BC1_RGBA:\t\t\tformat = VK_FORMAT_BC1_RGBA_UNORM_BLOCK;\t\tbreak;\n\tcase PTI_BC1_RGBA_SRGB:\t\tformat = VK_FORMAT_BC1_RGBA_SRGB_BLOCK;\t\t\tbreak;\n\tcase PTI_BC2_RGBA:\t\t\tformat = VK_FORMAT_BC2_UNORM_BLOCK;\t\t\t\tbreak;\n\tcase PTI_BC2_RGBA_SRGB:\t\tformat = VK_FORMAT_BC2_SRGB_BLOCK;\t\t\t\tbreak;\n\tcase PTI_BC3_RGBA:\t\t\tformat = VK_FORMAT_BC3_UNORM_BLOCK;\t\t\t\tbreak;\n\tcase PTI_BC3_RGBA_SRGB:\t\tformat = VK_FORMAT_BC3_SRGB_BLOCK;\t\t\t\tbreak;\n\tcase PTI_BC4_R:\t\t\t\tformat = VK_FORMAT_BC4_UNORM_BLOCK;\t\t\t\tbreak;\n\tcase PTI_BC4_R_SNORM:\t\tformat = VK_FORMAT_BC4_SNORM_BLOCK;\t\t\t\tbreak;\n\tcase PTI_BC5_RG:\t\t\tformat = VK_FORMAT_BC5_UNORM_BLOCK;\t\t\t\tbreak;\n\tcase PTI_BC5_RG_SNORM:\t\tformat = VK_FORMAT_BC5_SNORM_BLOCK;\t\t\t\tbreak;\n\tcase PTI_BC6_RGB_UFLOAT:\tformat = VK_FORMAT_BC6H_UFLOAT_BLOCK;\t\t\tbreak;\n\tcase PTI_BC6_RGB_SFLOAT:\tformat = VK_FORMAT_BC6H_SFLOAT_BLOCK;\t\t\tbreak;\n\tcase PTI_BC7_RGBA:\t\t\tformat = VK_FORMAT_BC7_UNORM_BLOCK;\t\t\t\tbreak;\n\tcase PTI_BC7_RGBA_SRGB:\t\tformat = VK_FORMAT_BC7_SRGB_BLOCK;\t\t\t\tbreak;\n\tcase PTI_ETC1_RGB8:\t\t\tformat = VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK;\t\tbreak;\t//vulkan doesn't support etc1, but etc2 is a superset so its all okay.\n\tcase PTI_ETC2_RGB8:\t\t\tformat = VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK;\t\tbreak;\n\tcase PTI_ETC2_RGB8_SRGB:\tformat = VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK;\t\tbreak;\n\tcase PTI_ETC2_RGB8A1:\t\tformat = VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK;\tbreak;\n\tcase PTI_ETC2_RGB8A1_SRGB:\tformat = VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK;\tbreak;\n\tcase PTI_ETC2_RGB8A8:\t\tformat = VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK;\tbreak;\n\tcase PTI_ETC2_RGB8A8_SRGB:\tformat = VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK;\tbreak;\n\tcase PTI_EAC_R11:\t\t\tformat = VK_FORMAT_EAC_R11_UNORM_BLOCK;\t\t\tbreak;\n\tcase PTI_EAC_R11_SNORM:\t\tformat = VK_FORMAT_EAC_R11_SNORM_BLOCK;\t\t\tbreak;\n\tcase PTI_EAC_RG11:\t\t\tformat = VK_FORMAT_EAC_R11G11_UNORM_BLOCK;\t\tbreak;\n\tcase PTI_EAC_RG11_SNORM:\tformat = VK_FORMAT_EAC_R11G11_SNORM_BLOCK;\t\tbreak;\n\n\tcase PTI_ASTC_4X4_LDR:\t\tformat = VK_FORMAT_ASTC_4x4_UNORM_BLOCK;\t\tbreak;\n\tcase PTI_ASTC_4X4_SRGB:\t\tformat = VK_FORMAT_ASTC_4x4_SRGB_BLOCK;\t\t\tbreak;\n\tcase PTI_ASTC_5X4_LDR:\t\tformat = VK_FORMAT_ASTC_5x4_UNORM_BLOCK;\t\tbreak;\n\tcase PTI_ASTC_5X4_SRGB:\t\tformat = VK_FORMAT_ASTC_5x4_SRGB_BLOCK;\t\t\tbreak;\n\tcase PTI_ASTC_5X5_LDR:\t\tformat = VK_FORMAT_ASTC_5x5_UNORM_BLOCK;\t\tbreak;\n\tcase PTI_ASTC_5X5_SRGB:\t\tformat = VK_FORMAT_ASTC_5x5_SRGB_BLOCK;\t\t\tbreak;\n\tcase PTI_ASTC_6X5_LDR:\t\tformat = VK_FORMAT_ASTC_6x5_UNORM_BLOCK;\t\tbreak;\n\tcase PTI_ASTC_6X5_SRGB:\t\tformat = VK_FORMAT_ASTC_6x5_SRGB_BLOCK;\t\t\tbreak;\n\tcase PTI_ASTC_6X6_LDR:\t\tformat = VK_FORMAT_ASTC_6x6_UNORM_BLOCK;\t\tbreak;\n\tcase PTI_ASTC_6X6_SRGB:\t\tformat = VK_FORMAT_ASTC_6x6_SRGB_BLOCK;\t\t\tbreak;\n\tcase PTI_ASTC_8X5_LDR:\t\tformat = VK_FORMAT_ASTC_8x5_UNORM_BLOCK;\t\tbreak;\n\tcase PTI_ASTC_8X5_SRGB:\t\tformat = VK_FORMAT_ASTC_8x5_SRGB_BLOCK;\t\t\tbreak;\n\tcase PTI_ASTC_8X6_LDR:\t\tformat = VK_FORMAT_ASTC_8x6_UNORM_BLOCK;\t\tbreak;\n\tcase PTI_ASTC_8X6_SRGB:\t\tformat = VK_FORMAT_ASTC_8x6_SRGB_BLOCK;\t\t\tbreak;\n\tcase PTI_ASTC_8X8_LDR:\t\tformat = VK_FORMAT_ASTC_8x8_UNORM_BLOCK;\t\tbreak;\n\tcase PTI_ASTC_8X8_SRGB:\t\tformat = VK_FORMAT_ASTC_8x8_SRGB_BLOCK;\t\t\tbreak;\n\tcase PTI_ASTC_10X5_LDR:\t\tformat = VK_FORMAT_ASTC_10x5_UNORM_BLOCK;\t\tbreak;\n\tcase PTI_ASTC_10X5_SRGB:\tformat = VK_FORMAT_ASTC_10x5_SRGB_BLOCK;\t\tbreak;\n\tcase PTI_ASTC_10X6_LDR:\t\tformat = VK_FORMAT_ASTC_10x6_UNORM_BLOCK;\t\tbreak;\n\tcase PTI_ASTC_10X6_SRGB:\tformat = VK_FORMAT_ASTC_10x6_SRGB_BLOCK;\t\tbreak;\n\tcase PTI_ASTC_10X8_LDR:\t\tformat = VK_FORMAT_ASTC_10x8_UNORM_BLOCK;\t\tbreak;\n\tcase PTI_ASTC_10X8_SRGB:\tformat = VK_FORMAT_ASTC_10x8_SRGB_BLOCK;\t\tbreak;\n\tcase PTI_ASTC_10X10_LDR:\tformat = VK_FORMAT_ASTC_10x10_UNORM_BLOCK;\t\tbreak;\n\tcase PTI_ASTC_10X10_SRGB:\tformat = VK_FORMAT_ASTC_10x10_SRGB_BLOCK;\t\tbreak;\n\tcase PTI_ASTC_12X10_LDR:\tformat = VK_FORMAT_ASTC_12x10_UNORM_BLOCK;\t\tbreak;\n\tcase PTI_ASTC_12X10_SRGB:\tformat = VK_FORMAT_ASTC_12x10_SRGB_BLOCK;\t\tbreak;\n\tcase PTI_ASTC_12X12_LDR:\tformat = VK_FORMAT_ASTC_12x12_UNORM_BLOCK;\t\tbreak;\n\tcase PTI_ASTC_12X12_SRGB:\tformat = VK_FORMAT_ASTC_12x12_SRGB_BLOCK;\t\tbreak;\n#ifdef VK_EXT_texture_compression_astc_hdr\n\tcase PTI_ASTC_4X4_HDR:\t\tformat = VK_FORMAT_ASTC_4x4_SFLOAT_BLOCK_EXT;\tbreak;\n\tcase PTI_ASTC_5X4_HDR:\t\tformat = VK_FORMAT_ASTC_5x4_SFLOAT_BLOCK_EXT;\tbreak;\n\tcase PTI_ASTC_5X5_HDR:\t\tformat = VK_FORMAT_ASTC_5x5_SFLOAT_BLOCK_EXT;\tbreak;\n\tcase PTI_ASTC_6X5_HDR:\t\tformat = VK_FORMAT_ASTC_6x5_SFLOAT_BLOCK_EXT;\tbreak;\n\tcase PTI_ASTC_6X6_HDR:\t\tformat = VK_FORMAT_ASTC_6x6_SFLOAT_BLOCK_EXT;\tbreak;\n\tcase PTI_ASTC_8X5_HDR:\t\tformat = VK_FORMAT_ASTC_8x5_SFLOAT_BLOCK_EXT;\tbreak;\n\tcase PTI_ASTC_8X6_HDR:\t\tformat = VK_FORMAT_ASTC_8x6_SFLOAT_BLOCK_EXT;\tbreak;\n\tcase PTI_ASTC_8X8_HDR:\t\tformat = VK_FORMAT_ASTC_8x8_SFLOAT_BLOCK_EXT;\tbreak;\n\tcase PTI_ASTC_10X5_HDR:\t\tformat = VK_FORMAT_ASTC_10x5_SFLOAT_BLOCK_EXT;\tbreak;\n\tcase PTI_ASTC_10X6_HDR:\t\tformat = VK_FORMAT_ASTC_10x6_SFLOAT_BLOCK_EXT;\tbreak;\n\tcase PTI_ASTC_10X8_HDR:\t\tformat = VK_FORMAT_ASTC_10x8_SFLOAT_BLOCK_EXT;\tbreak;\n\tcase PTI_ASTC_10X10_HDR:\tformat = VK_FORMAT_ASTC_10x10_SFLOAT_BLOCK_EXT;\tbreak;\n\tcase PTI_ASTC_12X10_HDR:\tformat = VK_FORMAT_ASTC_12x10_SFLOAT_BLOCK_EXT;\tbreak;\n\tcase PTI_ASTC_12X12_HDR:\tformat = VK_FORMAT_ASTC_12x12_SFLOAT_BLOCK_EXT;\tbreak;\n#else\t//better than crashing.\n\tcase PTI_ASTC_4X4_HDR:\t\tformat = VK_FORMAT_ASTC_4x4_UNORM_BLOCK;\tbreak;\n\tcase PTI_ASTC_5X4_HDR:\t\tformat = VK_FORMAT_ASTC_5x4_UNORM_BLOCK;\tbreak;\n\tcase PTI_ASTC_5X5_HDR:\t\tformat = VK_FORMAT_ASTC_5x5_UNORM_BLOCK;\tbreak;\n\tcase PTI_ASTC_6X5_HDR:\t\tformat = VK_FORMAT_ASTC_6x5_UNORM_BLOCK;\tbreak;\n\tcase PTI_ASTC_6X6_HDR:\t\tformat = VK_FORMAT_ASTC_6x6_UNORM_BLOCK;\tbreak;\n\tcase PTI_ASTC_8X5_HDR:\t\tformat = VK_FORMAT_ASTC_8x5_UNORM_BLOCK;\tbreak;\n\tcase PTI_ASTC_8X6_HDR:\t\tformat = VK_FORMAT_ASTC_8x6_UNORM_BLOCK;\tbreak;\n\tcase PTI_ASTC_8X8_HDR:\t\tformat = VK_FORMAT_ASTC_8x8_UNORM_BLOCK;\tbreak;\n\tcase PTI_ASTC_10X5_HDR:\t\tformat = VK_FORMAT_ASTC_10x5_UNORM_BLOCK;\tbreak;\n\tcase PTI_ASTC_10X6_HDR:\t\tformat = VK_FORMAT_ASTC_10x6_UNORM_BLOCK;\tbreak;\n\tcase PTI_ASTC_10X8_HDR:\t\tformat = VK_FORMAT_ASTC_10x8_UNORM_BLOCK;\tbreak;\n\tcase PTI_ASTC_10X10_HDR:\tformat = VK_FORMAT_ASTC_10x10_UNORM_BLOCK;\tbreak;\n\tcase PTI_ASTC_12X10_HDR:\tformat = VK_FORMAT_ASTC_12x10_UNORM_BLOCK;\tbreak;\n\tcase PTI_ASTC_12X12_HDR:\tformat = VK_FORMAT_ASTC_12x12_UNORM_BLOCK;\tbreak;\n#endif\n\n#ifdef ASTC3D\n\tcase PTI_ASTC_3X3X3_HDR:\t//vulkan doesn't support these for some reason\n\tcase PTI_ASTC_4X3X3_HDR:\n\tcase PTI_ASTC_4X4X3_HDR:\n\tcase PTI_ASTC_4X4X4_HDR:\n\tcase PTI_ASTC_5X4X4_HDR:\n\tcase PTI_ASTC_5X5X4_HDR:\n\tcase PTI_ASTC_5X5X5_HDR:\n\tcase PTI_ASTC_6X5X5_HDR:\n\tcase PTI_ASTC_6X6X5_HDR:\n\tcase PTI_ASTC_6X6X6_HDR:\n\tcase PTI_ASTC_3X3X3_LDR:\n\tcase PTI_ASTC_4X3X3_LDR:\n\tcase PTI_ASTC_4X4X3_LDR:\n\tcase PTI_ASTC_4X4X4_LDR:\n\tcase PTI_ASTC_5X4X4_LDR:\n\tcase PTI_ASTC_5X5X4_LDR:\n\tcase PTI_ASTC_5X5X5_LDR:\n\tcase PTI_ASTC_6X5X5_LDR:\n\tcase PTI_ASTC_6X6X5_LDR:\n\tcase PTI_ASTC_6X6X6_LDR:\n\tcase PTI_ASTC_3X3X3_SRGB:\n\tcase PTI_ASTC_4X3X3_SRGB:\n\tcase PTI_ASTC_4X4X3_SRGB:\n\tcase PTI_ASTC_4X4X4_SRGB:\n\tcase PTI_ASTC_5X4X4_SRGB:\n\tcase PTI_ASTC_5X5X4_SRGB:\n\tcase PTI_ASTC_5X5X5_SRGB:\n\tcase PTI_ASTC_6X5X5_SRGB:\n\tcase PTI_ASTC_6X6X5_SRGB:\n\tcase PTI_ASTC_6X6X6_SRGB:\tbreak;\n#endif\n\n\t//depth formats\n\tcase PTI_DEPTH16:\t\t\tformat = VK_FORMAT_D16_UNORM;\t\t\t\t\tbreak;\n\tcase PTI_DEPTH24:\t\t\tformat = VK_FORMAT_X8_D24_UNORM_PACK32;\t\t\tbreak;\n\tcase PTI_DEPTH32:\t\t\tformat = VK_FORMAT_D32_SFLOAT;\t\t\t\t\tbreak;\n\tcase PTI_DEPTH24_8:\t\t\tformat = VK_FORMAT_D24_UNORM_S8_UINT;\t\t\tbreak;\n\t//srgb formats\n\tcase PTI_BGRA8_SRGB:\n\tcase PTI_BGRX8_SRGB:\t\tformat = VK_FORMAT_B8G8R8A8_SRGB;\t\t\t\tbreak;\n\tcase PTI_RGBA8_SRGB:\n\tcase PTI_RGBX8_SRGB:\t\tformat = VK_FORMAT_R8G8B8A8_SRGB;\t\t\t\tbreak;\n\t//standard formats\n\tcase PTI_BGRA8:\n\tcase PTI_BGRX8:\t\t\t\tformat = VK_FORMAT_B8G8R8A8_UNORM;\t\t\t\tbreak;\n\tcase PTI_RGBA8:\n\tcase PTI_RGBX8:\t\t\t\tformat = VK_FORMAT_R8G8B8A8_UNORM;\t\t\t\tbreak;\n\t//misaligned formats\n\tcase PTI_RGB8:\t\t\t\tformat = VK_FORMAT_R8G8B8_UNORM;\t\t\t\tbreak;\n\tcase PTI_BGR8:\t\t\t\tformat = VK_FORMAT_B8G8R8_UNORM;\t\t\t\tbreak;\n\tcase PTI_RGB32F:\t\t\tformat = VK_FORMAT_R32G32B32_SFLOAT;\t\t\tbreak;\n\n\tcase PTI_RGB8_SRGB:\t\t\tformat = VK_FORMAT_R8G8B8_SRGB;\t\t\t\t\tbreak;\n\tcase PTI_BGR8_SRGB:\t\t\tformat = VK_FORMAT_B8G8R8_SRGB;\t\t\t\t\tbreak;\n\n\t//unsupported 'formats'\n\tcase PTI_MAX:\n#ifdef FTE_TARGET_WEB\n\tcase PTI_WHOLEFILE:\n#endif\n\tcase PTI_EMULATED:\n\t\tbreak;\n\t}\n\tif (format == VK_FORMAT_UNDEFINED)\t//no default case means warnings for unsupported formats above.\n\t\tSys_Error(\"VK_CreateTexture2DArray: Unsupported image encoding: %u(%s)\\n\", encoding, Image_FormatName(encoding));\n\n\tici.flags = (ret.type==PTI_CUBE)?VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT:0;\n\tici.imageType = VK_IMAGE_TYPE_2D;\n\tici.format = format;\n\tici.extent.width = width;\n\tici.extent.height = height;\n\tici.extent.depth = 1;\n\tici.mipLevels = mips;\n\tici.arrayLayers = layers;\n\tici.samples = VK_SAMPLE_COUNT_1_BIT;\n\tici.tiling = VK_IMAGE_TILING_OPTIMAL;\n\tici.usage = VK_IMAGE_USAGE_SAMPLED_BIT|(rendertarget?0:VK_IMAGE_USAGE_TRANSFER_DST_BIT);\n\tici.sharingMode = VK_SHARING_MODE_EXCLUSIVE;\n\tici.queueFamilyIndexCount = 0;\n\tici.pQueueFamilyIndices = NULL;\n\tici.initialLayout = ret.layout;\n\n\tVkAssert(vkCreateImage(vk.device, &ici, vkallocationcb, &ret.image));\n\tDebugSetName(VK_OBJECT_TYPE_IMAGE, (uint64_t)ret.image, debugname);\n\n\tret.view = VK_NULL_HANDLE;\n\tret.sampler = VK_NULL_HANDLE;\n\n\tif (!VK_AllocateBindImageMemory(&ret, false))\n\t\treturn ret;\t//oom?\n\n\n\tviewInfo.flags = 0;\n\tviewInfo.image = ret.image;\n\tswitch(ret.type)\n\t{\n\tdefault:\n\t\treturn ret;\n\tcase PTI_CUBE:\n\t\tviewInfo.viewType = VK_IMAGE_VIEW_TYPE_CUBE;\n\t\tbreak;\n\tcase PTI_2D:\n\t\tviewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;\n\t\tbreak;\n\tcase PTI_2D_ARRAY:\n\t\tviewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY;\n\t\tbreak;\n\t}\n\tviewInfo.format = format;\n\tswitch(encoding)\n\t{\n\t//formats that explicitly drop the alpha\n\tcase PTI_BC1_RGB:\n\tcase PTI_BC1_RGB_SRGB:\n\tcase PTI_RGBX8:\n\tcase PTI_RGBX8_SRGB:\n\tcase PTI_BGRX8:\n\tcase PTI_BGRX8_SRGB:\n\t\tviewInfo.components.r = VK_COMPONENT_SWIZZLE_R;\n\t\tviewInfo.components.g = VK_COMPONENT_SWIZZLE_G;\n\t\tviewInfo.components.b = VK_COMPONENT_SWIZZLE_B;\n\t\tviewInfo.components.a = VK_COMPONENT_SWIZZLE_ONE;\n\t\tbreak;\n\tcase PTI_L8:\t\t//must be an R8 texture\n\tcase PTI_L8_SRGB:\t//must be an R8 texture\n\t\tviewInfo.components.r = VK_COMPONENT_SWIZZLE_R;\n\t\tviewInfo.components.g = VK_COMPONENT_SWIZZLE_R;\n\t\tviewInfo.components.b = VK_COMPONENT_SWIZZLE_R;\n\t\tviewInfo.components.a = VK_COMPONENT_SWIZZLE_ONE;\n\t\tbreak;\n\tcase PTI_L8A8:\t//must be an RG8 texture\n\t\tviewInfo.components.r = VK_COMPONENT_SWIZZLE_R;\n\t\tviewInfo.components.g = VK_COMPONENT_SWIZZLE_R;\n\t\tviewInfo.components.b = VK_COMPONENT_SWIZZLE_R;\n\t\tviewInfo.components.a = VK_COMPONENT_SWIZZLE_G;\n\t\tbreak;\n\tdefault:\n\t\tviewInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;\n\t\tviewInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;\n\t\tviewInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;\n\t\tviewInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;\n\t\tbreak;\n\n#ifdef VK_EXT_astc_decode_mode\n\tcase PTI_ASTC_4X4_LDR:\t//set these to use rgba8 decoding, because we know they're not hdr and the format is basically 8bit anyway.\n\tcase PTI_ASTC_5X4_LDR:\t//we do NOT do this for the hdr, as that would cause data loss.\n\tcase PTI_ASTC_5X5_LDR:\t//we do NOT do this for sRGB because its pointless.\n\tcase PTI_ASTC_6X5_LDR:\n\tcase PTI_ASTC_6X6_LDR:\n\tcase PTI_ASTC_8X5_LDR:\n\tcase PTI_ASTC_8X6_LDR:\n\tcase PTI_ASTC_8X8_LDR:\n\tcase PTI_ASTC_10X5_LDR:\n\tcase PTI_ASTC_10X6_LDR:\n\tcase PTI_ASTC_10X8_LDR:\n\tcase PTI_ASTC_10X10_LDR:\n\tcase PTI_ASTC_12X10_LDR:\n\tcase PTI_ASTC_12X12_LDR:\n\t\tviewInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;\n\t\tviewInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;\n\t\tviewInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;\n\t\tviewInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;\n\t\tif (vk.ext_astc_decode_mode)\n\t\t{\n\t\t\tastcmode.pNext = viewInfo.pNext;\n\t\t\tastcmode.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_ASTC_DECODE_MODE_EXT;\n\t\t\tastcmode.decodeMode = VK_FORMAT_R8G8B8A8_UNORM;\n\t\t\tviewInfo.pNext = &astcmode;\n\t\t}\n\t\tbreak;\n#endif\n\t}\n\tviewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;\n\tviewInfo.subresourceRange.baseMipLevel = 0;\n\tviewInfo.subresourceRange.levelCount = mips;\n\tviewInfo.subresourceRange.baseArrayLayer = 0;\n\tviewInfo.subresourceRange.layerCount = layers;\n\tVkAssert(vkCreateImageView(vk.device, &viewInfo, NULL, &ret.view));\n\n\treturn ret;\n}\nvoid set_image_layout(VkCommandBuffer cmd, VkImage image, VkImageAspectFlags aspectMask, \n\t\tVkImageLayout old_image_layout, VkAccessFlags srcaccess, VkPipelineStageFlagBits srcstagemask,\n\t       \tVkImageLayout new_image_layout, VkAccessFlags dstaccess, VkPipelineStageFlagBits dststagemask)\n{\n\t//images have weird layout representations.\n\t//we need to use a side-effect of memory barriers in order to convert from one layout to another, so that we can actually use the image.\n\tVkImageMemoryBarrier imgbarrier = {VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER};\n\timgbarrier.pNext = NULL;\n\timgbarrier.srcAccessMask = srcaccess;\n\timgbarrier.dstAccessMask = dstaccess;\n\timgbarrier.oldLayout = old_image_layout;\n\timgbarrier.newLayout = new_image_layout;\n\timgbarrier.image = image;\n\timgbarrier.subresourceRange.aspectMask = aspectMask;\n\timgbarrier.subresourceRange.baseMipLevel = 0;\n\timgbarrier.subresourceRange.levelCount = 1;\n\timgbarrier.subresourceRange.baseArrayLayer = 0;\n\timgbarrier.subresourceRange.layerCount = 1;\n\timgbarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;\n\timgbarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;\n/*\n\tif (new_image_layout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL)\t// Make sure anything that was copying from this image has completed\n\t\timgbarrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;\n\telse if (new_image_layout == VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL)\t// Make sure anything that was copying from this image has completed\n\t\timgbarrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;\n\telse if (new_image_layout == VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL)\n\t\timgbarrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;\n\telse if (new_image_layout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL)\n\t\timgbarrier.dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;\n\telse if (new_image_layout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) // Make sure any Copy or CPU writes to image are flushed \n\t\timgbarrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_INPUT_ATTACHMENT_READ_BIT;\n\n\tif (old_image_layout == VK_IMAGE_LAYOUT_PREINITIALIZED)\n\t\timgbarrier.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT;\n\telse if (old_image_layout == VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL)\n\t\timgbarrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT;\n\telse if (old_image_layout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL)\n\t\timgbarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;\n*/\n\tvkCmdPipelineBarrier(cmd, srcstagemask, dststagemask, 0, 0, NULL, 0, NULL, 1, &imgbarrier);\n}\n\nvoid VK_FencedCheck(void)\n{\n\twhile(vk.fencework)\n\t{\n\t\tSys_LockConditional(vk.submitcondition);\n\t\tif (VK_SUCCESS == vkGetFenceStatus(vk.device, vk.fencework->fence))\n\t\t{\n\t\t\tstruct vk_fencework *w;\n\t\t\tw = vk.fencework;\n\t\t\tvk.fencework = w->next;\n\t\t\tif (!vk.fencework)\n\t\t\t\tvk.fencework_last = NULL;\n\t\t\tSys_UnlockConditional(vk.submitcondition);\n\n\t\t\tif (w->Passed)\n\t\t\t\tw->Passed(w);\n\t\t\tif (w->cbuf)\n\t\t\t\tvkFreeCommandBuffers(vk.device, vk.cmdpool, 1, &w->cbuf);\n\t\t\tif (w->fence)\n\t\t\t\tvkDestroyFence(vk.device, w->fence, vkallocationcb);\n\t\t\tZ_Free(w);\n\t\t\tcontinue;\n\t\t}\n\t\tSys_UnlockConditional(vk.submitcondition);\n\t\tbreak;\n\t}\n}\n//allocate and begin a commandbuffer so we can do the copies\nvoid *VK_FencedBegin(void (*passed)(void *work), size_t worksize)\n{\n\tstruct vk_fencework *w = BZ_Malloc(worksize?worksize:sizeof(*w));\n\n\tVkCommandBufferAllocateInfo cbai = {VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO};\n\tVkCommandBufferInheritanceInfo cmdinh = {VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO};\n\tVkCommandBufferBeginInfo cmdinf = {VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO};\n\tcbai.commandPool = vk.cmdpool;\n\tcbai.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;\n\tcbai.commandBufferCount = 1;\n\tVkAssert(vkAllocateCommandBuffers(vk.device, &cbai, &w->cbuf));\n\tDebugSetName(VK_OBJECT_TYPE_COMMAND_BUFFER, (uint64_t)w->cbuf, \"VK_FencedBegin\");\n\tcmdinf.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;\n\tcmdinf.pInheritanceInfo = &cmdinh;\n\tvkBeginCommandBuffer(w->cbuf, &cmdinf);\n\n\tw->Passed = passed;\n\tw->next = NULL;\n\n\treturn w;\n}\n//end+submit a commandbuffer, and set up a fence so we know when its complete. this is not within the context of any frame, so make sure any textures are safe to rewrite early...\n//completion can be signalled before the current frame finishes, so watch out for that too.\nvoid VK_FencedSubmit(void *work)\n{\n\tstruct vk_fencework *w = work;\n\tVkFenceCreateInfo fenceinfo = {VK_STRUCTURE_TYPE_FENCE_CREATE_INFO};\n\n\tif (w->cbuf)\n\t\tvkEndCommandBuffer(w->cbuf);\n\n\t//check if we can release anything yet.\n\tVK_FencedCheck();\n\n\t//FIXME: this seems to be an excessively expensive function.\n\tvkCreateFence(vk.device, &fenceinfo, vkallocationcb, &w->fence);\n\n\tVK_Submit_Work(w->cbuf, VK_NULL_HANDLE, 0, VK_NULL_HANDLE, w->fence, NULL, w);\n}\n\nvoid VK_FencedSync(void *work)\n{\n\tstruct vk_fencework *w = work;\n\tVK_FencedSubmit(w);\n\n#ifdef MULTITHREAD\n\t//okay, this is crazy, but it ensures that the work was submitted BEFORE the WaitForFence call.\n\t//we should probably come up with a better sync method.\n\tif (vk.submitthread)\n\t{\n\t\tqboolean nnsc = vk.neednewswapchain;\n\t\tvk.neednewswapchain = true;\n\t\tSys_LockConditional(vk.submitcondition);\t//annoying, but required for it to be reliable with respect to other things.\n\t\tSys_ConditionSignal(vk.submitcondition);\n\t\tSys_UnlockConditional(vk.submitcondition);\n\t\tSys_WaitOnThread(vk.submitthread);\n\t\tvk.submitthread = NULL;\n\n\t\twhile (vk.work)\n\t\t{\n\t\t\tSys_LockConditional(vk.submitcondition);\n\t\t\tVK_Submit_DoWork();\n\t\t\tSys_UnlockConditional(vk.submitcondition);\n\t\t}\n\n\t\t//we know all work is synced now...\n\n\t\tvk.neednewswapchain = nnsc;\n\t\tvk.submitthread = Sys_CreateThread(\"vksubmission\", VK_Submit_Thread, NULL, THREADP_HIGHEST, 0);\n\t}\n#endif\n\n\t//fixme: waiting for the fence while it may still be getting created by the worker is unsafe.\n\tvkWaitForFences(vk.device, 1, &w->fence, VK_FALSE, UINT64_MAX);\n}\n\n//called to schedule the release of a resource that may be referenced by an active command buffer.\n//the command buffer in question may even have not yet been submitted yet.\nvoid *VK_AtFrameEnd(void (*frameended)(void *work), void *workdata, size_t worksize)\n{\n\tstruct vk_frameend *w = Z_Malloc(sizeof(*w) + worksize);\n\n\tw->FrameEnded = frameended;\n\tw->next = vk.frameendjobs;\n\tvk.frameendjobs = w;\n\n\tif (workdata)\n\t\tmemcpy(w+1, workdata, worksize);\n\n\treturn w+1;\n}\n\nstruct texturefence\n{\n\tstruct vk_fencework w;\n\n\tint mips;\n\tVkBuffer stagingbuffer;\n\tVkDeviceMemory stagingmemory;\n};\nstatic void VK_TextureLoaded(void *ctx)\n{\n\tstruct texturefence *w = ctx;\n\tvkDestroyBuffer(vk.device, w->stagingbuffer, vkallocationcb);\n\tvkFreeMemory(vk.device, w->stagingmemory, vkallocationcb);\n}\nqboolean VK_LoadTextureMips (texid_t tex, const struct pendingtextureinfo *mips)\n{\n\tVkBufferCreateInfo bci = {VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO};\n\tVkMemoryRequirements mem_reqs;\n\tVkMemoryAllocateInfo memAllocInfo = {VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO};\n\tvoid *mapdata;\n\n\tstruct texturefence *fence;\n\tVkCommandBuffer vkloadcmd;\n\tvk_image_t target;\n\tuint32_t i;\n\tuint32_t blockwidth, blockheight, blockdepth;\n\tuint32_t blockbytes;\n\tuint32_t layers;\n\tuint32_t mipcount = mips->mipcount;\n\tswitch(mips->type)\n\t{\n\tcase PTI_2D:\n\t\tif (!mipcount || mips->mip[0].width == 0 || mips->mip[0].height == 0 || mips->mip[0].depth != 1)\n\t\t\treturn false;\n\t\tbreak;\n\tcase PTI_2D_ARRAY:\n\t\tif (!mipcount || mips->mip[0].width == 0 || mips->mip[0].height == 0 || mips->mip[0].depth == 0)\n\t\t\treturn false;\n\t\tbreak;\n\tcase PTI_CUBE:\n\t\tif (!mipcount || mips->mip[0].width == 0 || mips->mip[0].height == 0 || mips->mip[0].depth != 6)\n\t\t\treturn false;\n\t\tbreak;\n\tdefault:\n\t\treturn false;\n\t}\n\n\tlayers = mips->mip[0].depth;\n\n\tif (layers == 1 && mipcount > 1)\n\t{\t//npot mipmapped textures are awkward.\n\t\t//vulkan floors.\n\t\tfor (i = 1; i < mipcount; i++)\n\t\t{\n\t\t\tif (mips->mip[i].width != max(1,(mips->mip[i-1].width>>1)) ||\n\t\t\t\tmips->mip[i].height != max(1,(mips->mip[i-1].height>>1)))\n\t\t\t{\t//okay, this mip looks like it was sized wrongly.\n\t\t\t\tmipcount = i;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\tImage_BlockSizeForEncoding(mips->encoding, &blockbytes, &blockwidth, &blockheight, &blockdepth);\n\n\tfence = VK_FencedBegin(VK_TextureLoaded, sizeof(*fence));\n\tfence->mips = mipcount;\n\tvkloadcmd = fence->w.cbuf;\n\n\t//create our target image\n\n\tif (tex->vkimage)\n\t{\n\t\tif (tex->vkimage->width != mips->mip[0].width ||\n\t\t\ttex->vkimage->height != mips->mip[0].height ||\n\t\t\ttex->vkimage->layers != layers ||\n\t\t\ttex->vkimage->mipcount != mipcount ||\n\t\t\ttex->vkimage->encoding != mips->encoding ||\n\t\t\ttex->vkimage->type != mips->type)\n\t\t{\n\t\t\tVK_AtFrameEnd(VK_DestroyVkTexture_Delayed, tex->vkimage, sizeof(*tex->vkimage));\n//\t\t\tvkDeviceWaitIdle(vk.device);\t//erk, we can't cope with a commandbuffer poking the texture while things happen\n//\t\t\tVK_FencedCheck();\n//\t\t\tVK_DestroyVkTexture(tex->vkimage);\n\t\t\tZ_Free(tex->vkimage);\n\t\t\ttex->vkimage = NULL;\n\t\t}\n\t}\n\n\tif (tex->vkimage)\n\t{\n\t\ttarget = *tex->vkimage;\t//can reuse it\n\t\tZ_Free(tex->vkimage);\n\t\t//we're meant to be replacing the entire thing, so we can just transition from undefined here\n//\t\tset_image_layout(vkloadcmd, target.image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_ACCESS_SHADER_READ_BIT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_ACCESS_TRANSFER_WRITE_BIT);\n\n\t\t{\n\t\t\t//images have weird layout representations.\n\t\t\t//we need to use a side-effect of memory barriers in order to convert from one layout to another, so that we can actually use the image.\n\t\t\tVkImageMemoryBarrier imgbarrier = {VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER};\n\t\t\timgbarrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;\n\t\t\timgbarrier.newLayout = target.layout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;\n\t\t\timgbarrier.image = target.image;\n\t\t\timgbarrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;\n\t\t\timgbarrier.subresourceRange.baseMipLevel = 0;\n\t\t\timgbarrier.subresourceRange.levelCount = mipcount;\n\t\t\timgbarrier.subresourceRange.baseArrayLayer = 0;\n\t\t\timgbarrier.subresourceRange.layerCount = layers;\n\t\t\timgbarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;\n\t\t\timgbarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;\n\n\t\t\timgbarrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT;\n\t\t\timgbarrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;\n\t\t\tvkCmdPipelineBarrier(vkloadcmd, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, NULL, 0, NULL, 1, &imgbarrier);\n\t\t}\n\t}\n\telse\n\t{\n\t\ttarget = VK_CreateTexture2DArray(mips->mip[0].width, mips->mip[0].height, layers, mipcount, mips->encoding, mips->type, !!(tex->flags&IF_RENDERTARGET), tex->ident);\n\n\t\tif (target.mem.memory == VK_NULL_HANDLE)\n\t\t{\n\t\t\tVK_DestroyVkTexture(&target);\n\t\t\treturn false;\t//the alloc failed? can't copy to that which does not exist.\n\t\t}\n\n\t\t{\n\t\t\t//images have weird layout representations.\n\t\t\t//we need to use a side-effect of memory barriers in order to convert from one layout to another, so that we can actually use the image.\n\t\t\tVkImageMemoryBarrier imgbarrier = {VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER};\n\t\t\timgbarrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;\n\t\t\timgbarrier.newLayout = target.layout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;\n\t\t\timgbarrier.image = target.image;\n\t\t\timgbarrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;\n\t\t\timgbarrier.subresourceRange.baseMipLevel = 0;\n\t\t\timgbarrier.subresourceRange.levelCount = mipcount;\n\t\t\timgbarrier.subresourceRange.baseArrayLayer = 0;\n\t\t\timgbarrier.subresourceRange.layerCount = layers;\n\t\t\timgbarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;\n\t\t\timgbarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;\n\n\t\t\timgbarrier.srcAccessMask = 0;\n\t\t\timgbarrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;\n\t\t\tvkCmdPipelineBarrier(vkloadcmd, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, NULL, 0, NULL, 1, &imgbarrier);\n\t\t}\n\t}\n\n\t//figure out how big our staging buffer needs to be\n\tbci.size = 0;\n\tfor (i = 0; i < mipcount; i++)\n\t{\n\t\tuint32_t blockswidth = (mips->mip[i].width+blockwidth-1) / blockwidth;\n\t\tuint32_t blocksheight = (mips->mip[i].height+blockheight-1) / blockheight;\n\t\tuint32_t blocksdepth = (mips->mip[i].depth+blockdepth-1) / blockdepth;\n\t\tbci.size += blockswidth*blocksheight*blocksdepth*blockbytes;\n\t}\n\tbci.flags = 0;\n\tbci.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT;\n\tbci.sharingMode = VK_SHARING_MODE_EXCLUSIVE;\n\tbci.queueFamilyIndexCount = 0;\n\tbci.pQueueFamilyIndices = NULL;\n\n\t//FIXME: nvidia's vkCreateBuffer ends up calling NtYieldExecution.\n\t//which is basically a waste of time, and its hurting framerates.\n\n\t//create+map the staging buffer\n\tVkAssert(vkCreateBuffer(vk.device, &bci, vkallocationcb, &fence->stagingbuffer));\n\tvkGetBufferMemoryRequirements(vk.device, fence->stagingbuffer, &mem_reqs);\n\tmemAllocInfo.allocationSize = mem_reqs.size;\n\tmemAllocInfo.memoryTypeIndex = vk_find_memory_require(mem_reqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);\n\tif (VK_SUCCESS != vkAllocateMemory(vk.device, &memAllocInfo, vkallocationcb, &fence->stagingmemory))\n\t{\n\t\tVK_FencedSubmit(fence);\n\t\treturn false;\t//some sort of oom error?\n\t}\n\tDebugSetName(VK_OBJECT_TYPE_DEVICE_MEMORY, (uint64_t)fence->stagingmemory, \"VK_LoadTextureMips\");\n\tVkAssert(vkBindBufferMemory(vk.device, fence->stagingbuffer, fence->stagingmemory, 0));\n\tVkAssert(vkMapMemory(vk.device, fence->stagingmemory, 0, bci.size, 0, &mapdata));\n\tif (!mapdata)\n\t\tSys_Error(\"Unable to map staging image\\n\");\n\n\tbci.size = 0;\n\tfor (i = 0; i < mipcount; i++)\n\t{\n\t\tsize_t mipofs = 0;\n\t\tVkBufferImageCopy region;\n\t\t//figure out the number of 'blocks' in the image.\n\t\t//for non-compressed formats this is just the width directly.\n\t\t//for compressed formats (ie: s3tc/dxt) we need to round up to deal with npot.\n\t\tuint32_t blockswidth = (mips->mip[i].width+blockwidth-1) / blockwidth;\n\t\tuint32_t blocksheight = (mips->mip[i].height+blockheight-1) / blockheight;\n\t\tuint32_t blocksdepth = (mips->mip[i].depth+blockdepth-1) / blockdepth, z;\n\n\t\t//build it in layers...\n\t\tfor (z = 0; z < blocksdepth; z++)\n\t\t{\n\t\t\tif (mips->mip[i].data)\n\t\t\t\tmemcpy((char*)mapdata + bci.size, (char*)mips->mip[i].data+mipofs, blockswidth*blockbytes*blocksheight*blockdepth);\n\t\t\telse\n\t\t\t\tmemset((char*)mapdata + bci.size, 0, blockswidth*blockbytes*blocksheight*blockdepth);\n\n\t\t\t//queue up a buffer->image copy for this mip\n\t\t\tregion.bufferOffset = bci.size;\n\t\t\tregion.bufferRowLength = blockswidth*blockwidth;\n\t\t\tregion.bufferImageHeight = blocksheight*blockheight;\n\t\t\tregion.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;\n\t\t\tregion.imageSubresource.mipLevel = i;\n\t\t\tregion.imageSubresource.baseArrayLayer = z*blockdepth;\n\t\t\tregion.imageSubresource.layerCount = blockdepth;\n\t\t\tregion.imageOffset.x = 0;\n\t\t\tregion.imageOffset.y = 0;\n\t\t\tregion.imageOffset.z = 0;\n\t\t\tregion.imageExtent.width = mips->mip[i].width;\n\t\t\tregion.imageExtent.height = mips->mip[i].height;\n\t\t\tregion.imageExtent.depth = blockdepth;\n\n\t\t\tvkCmdCopyBufferToImage(vkloadcmd, fence->stagingbuffer, target.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &region);\n\t\t\tbci.size += blockdepth*blockswidth*blocksheight*blockbytes;\n\t\t\tmipofs += blockdepth*blockswidth*blocksheight*blockbytes;\n\t\t}\n\t}\n\tvkUnmapMemory(vk.device, fence->stagingmemory);\n\n\t//layouts are annoying. and weird.\n\t{\n\t\t//images have weird layout representations.\n\t\t//we need to use a side-effect of memory barriers in order to convert from one layout to another, so that we can actually use the image.\n\t\tVkImageMemoryBarrier imgbarrier = {VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER};\n\t\timgbarrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;\n\t\timgbarrier.newLayout = target.layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;\n\t\timgbarrier.image = target.image;\n\t\timgbarrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;\n\t\timgbarrier.subresourceRange.baseMipLevel = 0;\n\t\timgbarrier.subresourceRange.levelCount = mipcount;\n\t\timgbarrier.subresourceRange.baseArrayLayer = 0;\n\t\timgbarrier.subresourceRange.layerCount = layers;\n\t\timgbarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;\n\t\timgbarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;\n\n\t\timgbarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;\n\t\timgbarrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_INPUT_ATTACHMENT_READ_BIT;\n\t\tvkCmdPipelineBarrier(vkloadcmd, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, NULL, 0, NULL, 1, &imgbarrier);\n\t}\n\n\tVK_FencedSubmit(fence);\n\n\t//FIXME: should probably reuse these samplers.\n\tif (!target.sampler)\n\t\tVK_CreateSampler(tex->flags, &target);\n\n\ttex->vkdescriptor = VK_NULL_HANDLE;\n\n\ttex->vkimage = Z_Malloc(sizeof(*tex->vkimage));\n\t*tex->vkimage = target;\n\n\treturn true;\n}\nvoid    VK_DestroyTexture\t\t\t(texid_t tex)\n{\n\tif (tex->vkimage)\n\t{\n\t\tVK_DestroyVkTexture(tex->vkimage);\n\t\tZ_Free(tex->vkimage);\n\t\ttex->vkimage = NULL;\n\t}\n\ttex->vkdescriptor = VK_NULL_HANDLE;\n}\n\n\n\n\nvoid\tVK_R_Init\t\t\t\t\t(void)\n{\n\tuint32_t white[6] = {~0u,~0u,~0u,~0u,~0u,~0u};\n\tr_blackcubeimage = Image_CreateTexture(\"***blackcube***\", NULL, IF_NEAREST|IF_TEXTYPE_CUBE|IF_NOPURGE);\n\tImage_Upload(r_blackcubeimage, TF_RGBX32, NULL, NULL, 1, 1, 6, IF_NEAREST|IF_NOMIPMAP|IF_NOGAMMA|IF_TEXTYPE_CUBE);\n\n\tr_whitecubeimage = Image_CreateTexture(\"***whitecube***\", NULL, IF_NEAREST|IF_TEXTYPE_CUBE|IF_NOPURGE);\n\tImage_Upload(r_whitecubeimage, TF_RGBX32, white, NULL, 1, 1, 6, IF_NEAREST|IF_NOMIPMAP|IF_NOGAMMA|IF_TEXTYPE_CUBE);\n}\nvoid\tVK_R_DeInit\t\t\t\t\t(void)\n{\n\tR_GAliasFlushSkinCache(true);\n\tSurf_DeInit();\n\tVK_Shutdown_PostProc();\n\tVK_DestroySwapChain();\n\tVKBE_Shutdown();\n\n\tR2D_Shutdown();\n\tShader_Shutdown();\n\tImage_Shutdown();\n}\n\nvoid VK_SetupViewPortProjection(qboolean flipy, const float eyematrix[12], const float fovoverrides[4], const float projmatrix[16])\n{\n\tfloat fov_x, fov_y;\n\tfloat fovv_x, fovv_y;\n\n\tfloat fov_l, fov_r, fov_d, fov_u;\n\n\tif (eyematrix)\n\t{\n\t\textern cvar_t in_vraim;\n\t\tmatrix3x4 basematrix;\n\t\tmatrix3x4 viewmatrix;\n\t\tvec3_t newa;\n\n\t\tif (r_refdef.base_known)\n\t\t{\t//mod is specifying its own base ang+org.\n\t\t\tMatrix3x4_RM_FromAngles(r_refdef.base_angles, r_refdef.base_origin, basematrix[0]);\n\t\t}\n\t\telse\n\t\t{\t//mod provides no info.\n\t\t\t//client will fiddle with input_angles\n\t\t\tnewa[0] = newa[2] = 0;\t//ignore player pitch+roll. sorry. apply the eye's transform on top.\n\t\t\tnewa[1] = r_refdef.viewangles[1];\n\t\t\tif (in_vraim.ival)\n\t\t\t\tnewa[1] -= SHORT2ANGLE(r_refdef.playerview->vrdev[VRDEV_HEAD].angles[YAW]);\n\t\t\tMatrix3x4_RM_FromAngles(newa, r_refdef.vieworg, basematrix[0]);\n\t\t}\n\t\tMatrix3x4_Multiply(eyematrix, basematrix[0], viewmatrix[0]);\n\t\tMatrix3x4_RM_ToVectors(viewmatrix[0], vpn, vright, vup, r_origin);\n\t\tVectorNegate(vright, vright);\n\n\t}\n\telse\n\t{\n\t\tAngleVectors (r_refdef.viewangles, vpn, vright, vup);\n\t\tVectorCopy (r_refdef.vieworg, r_origin);\n\t}\n\n//\tscreenaspect = (float)r_refdef.vrect.width/r_refdef.vrect.height;\n\n\t/*view matrix*/\n\tif (flipy)\t//mimic gl and give bottom-up\n\t{\n\t\tvec3_t down;\n\t\tVectorNegate(vup, down);\n\t\tVectorCopy(down, vup);\n\t\tMatrix4x4_CM_ModelViewMatrixFromAxis(r_refdef.m_view, vpn, vright, down, r_refdef.vieworg);\n\t\tr_refdef.flipcull = SHADER_CULL_FRONT | SHADER_CULL_BACK;\n\t}\n\telse\n\t{\n\t\tMatrix4x4_CM_ModelViewMatrixFromAxis(r_refdef.m_view, vpn, vright, vup, r_refdef.vieworg);\n\t\tr_refdef.flipcull = 0;\n\t}\n\n\tif (projmatrix)\n\t{\n\t\tmemcpy(r_refdef.m_projection_std, projmatrix, sizeof(r_refdef.m_projection_std));\n\t\tmemcpy(r_refdef.m_projection_view, projmatrix, sizeof(r_refdef.m_projection_std));\n\t}\n\telse\n\t{\n\t\tfov_x = r_refdef.fov_x;//+sin(cl.time)*5;\n\t\tfov_y = r_refdef.fov_y;//-sin(cl.time+1)*5;\n\t\tfovv_x = r_refdef.fovv_x;\n\t\tfovv_y = r_refdef.fovv_y;\n\t\tif ((r_refdef.flags & RDF_UNDERWATER) && !(r_refdef.flags & RDF_WATERWARP))\n\t\t{\n\t\t\tfov_x *= 1 + (((sin(cl.time * 4.7) + 1) * 0.015) * r_waterwarp.value);\n\t\t\tfov_y *= 1 + (((sin(cl.time * 3.0) + 1) * 0.015) * r_waterwarp.value);\n\t\t\tfovv_x *= 1 + (((sin(cl.time * 4.7) + 1) * 0.015) * r_waterwarp.value);\n\t\t\tfovv_y *= 1 + (((sin(cl.time * 3.0) + 1) * 0.015) * r_waterwarp.value);\n\t\t}\n\t\tif (fovoverrides)\n\t\t{\n\t\t\tfov_l = fovoverrides[0];\n\t\t\tfov_r = fovoverrides[1];\n\t\t\tfov_d = fovoverrides[2];\n\t\t\tfov_u = fovoverrides[3];\n\n\t\t\tfov_x = fov_r-fov_l;\n\t\t\tfov_y = fov_u-fov_d;\n\n\t\t\tfovv_x = fov_x;\n\t\t\tfovv_y = fov_y;\n\t\t\tr_refdef.flipcull = ((fov_u < fov_d)^(fov_r < fov_l))?SHADER_CULL_FLIP:0;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tfov_l = -fov_x / 2;\n\t\t\tfov_r = fov_x / 2;\n\t\t\tfov_d = -fov_y / 2;\n\t\t\tfov_u = fov_y / 2;\n\t\t}\n\n\t\tif (r_xflip.ival)\n\t\t{\n\t\t\tfloat t = fov_l;\n\t\t\tfov_l = fov_r;\n\t\t\tfov_r = t;\n\t\t\tr_refdef.flipcull ^= SHADER_CULL_FLIP;\n\t\t\tfovv_x *= -1;\n\t\t}\n\n\t\tMatrix4x4_CM_Projection_Offset(r_refdef.m_projection_std, fov_l, fov_r, fov_d, fov_u, r_refdef.mindist, r_refdef.maxdist, false);\n\t\tMatrix4x4_CM_Projection_Offset(r_refdef.m_projection_view, -fovv_x/2, fovv_x/2, -fovv_y/2, fovv_y/2, r_refdef.mindist, r_refdef.maxdist, false);\n\t}\n\n\tr_refdef.m_projection_view[2+4*0] *= 0.333;\n\tr_refdef.m_projection_view[2+4*1] *= 0.333;\n\tr_refdef.m_projection_view[2+4*2] *= 0.333;\n\tr_refdef.m_projection_view[2+4*3] *= 0.333;\n}\n\nvoid VK_Set2D(void)\n{\n\tvid.fbvwidth = vid.width;\n\tvid.fbvheight = vid.height;\n\tvid.fbpwidth = vid.pixelwidth;\n\tvid.fbpheight = vid.pixelheight;\n\n\tr_refdef.pxrect.x = 0;\n\tr_refdef.pxrect.y = 0;\n\tr_refdef.pxrect.width = vid.fbpwidth;\n\tr_refdef.pxrect.height = vid.fbpheight;\n\tr_refdef.pxrect.maxheight = vid.pixelheight;\n\n/*\n\t{\n\t\tVkClearDepthStencilValue val;\n\t\tVkImageSubresourceRange range;\n\t\tval.depth = 1;\n\t\tval.stencil = 0;\n\t\trange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;\n\t\trange.baseArrayLayer = 0;\n\t\trange.baseMipLevel = 0;\n\t\trange.layerCount = 1;\n\t\trange.levelCount = 1;\n\t\tvkCmdClearDepthStencilImage(vk.frame->cbuf, vk.depthbuf.image, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, &val, 1, &range);\n\t}\n*/\n\t\n/*\n\tvkCmdEndRenderPass(vk.frame->cbuf);\n\t{\n\t\tVkRenderPassBeginInfo rpiinfo = {VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO};\n\t\tVkClearValue\tclearvalues[1];\n\t\tclearvalues[0].depthStencil.depth = 1.0;\n\t\tclearvalues[0].depthStencil.stencil = 0;\n\t\trpiinfo.renderPass = VK_GetRenderPass(RP_CLEARDEPTH);\n\t\trpiinfo.renderArea.offset.x = r_refdef.pxrect.x;\n\t\trpiinfo.renderArea.offset.y = r_refdef.pxrect.y;\n\t\trpiinfo.renderArea.extent.width = r_refdef.pxrect.width;\n\t\trpiinfo.renderArea.extent.height = r_refdef.pxrect.height;\n\t\trpiinfo.framebuffer = vk.frame->backbuf->framebuffer;\n\t\trpiinfo.clearValueCount = 1;\n\t\trpiinfo.pClearValues = clearvalues;\n\t\tvkCmdBeginRenderPass(vk.frame->cbuf, &rpiinfo, VK_SUBPASS_CONTENTS_INLINE);\n\t}\n*/\n\t{\n\t\tVkViewport vp[1];\n\t\tVkRect2D scissor[1];\n\t\tvp[0].x = r_refdef.pxrect.x;\n\t\tvp[0].y = r_refdef.pxrect.y;\n\t\tvp[0].width = r_refdef.pxrect.width;\n\t\tvp[0].height = r_refdef.pxrect.height;\n\t\tvp[0].minDepth = 0.0;\n\t\tvp[0].maxDepth = 1.0;\n\t\tscissor[0].offset.x = r_refdef.pxrect.x;\n\t\tscissor[0].offset.y = r_refdef.pxrect.y;\n\t\tscissor[0].extent.width = r_refdef.pxrect.width;\n\t\tscissor[0].extent.height = r_refdef.pxrect.height;\n\t\tvkCmdSetViewport(vk.rendertarg->cbuf, 0, countof(vp), vp);\n\t\tvkCmdSetScissor(vk.rendertarg->cbuf, 0, countof(scissor), scissor);\n\t}\n\n\tVKBE_Set2D(true);\n\n\tr_refdef.flipcull = 0;\n\tif (0)\n\t\tMatrix4x4_CM_Orthographic(r_refdef.m_projection_std, 0, vid.fbvwidth, 0, vid.fbvheight, -99999, 99999);\n\telse\n\t\tMatrix4x4_CM_Orthographic(r_refdef.m_projection_std, 0, vid.fbvwidth, vid.fbvheight, 0, -99999, 99999);\n\tMatrix4x4_Identity(r_refdef.m_view);\n\n\tBE_SelectEntity(&r_worldentity);\n}\n\nstatic void VK_Shutdown_PostProc(void)\n{\n\tunsigned int i;\n\tif (vk.device)\n\t{\n\t\tfor (i = 0; i < countof(postproc); i++)\n\t\t\tVKBE_RT_Gen(&postproc[i], NULL, 0, 0, true, RT_IMAGEFLAGS);\n\t\tVK_R_BloomShutdown();\n\t}\n\n\tvk.scenepp_waterwarp = NULL;\n\tvk.scenepp_antialias = NULL;\n}\nstatic void VK_Init_PostProc(void)\n{\n\ttexid_t scenepp_texture_warp, scenepp_texture_edge;\n\t//this block liberated from the opengl code\n\t{\n#define PP_WARP_TEX_SIZE 64\n#define PP_AMP_TEX_SIZE 64\n#define PP_AMP_TEX_BORDER 4\n\t\tint i, x, y;\n\t\tunsigned char pp_warp_tex[PP_WARP_TEX_SIZE*PP_WARP_TEX_SIZE*4];\n\t\tunsigned char pp_edge_tex[PP_AMP_TEX_SIZE*PP_AMP_TEX_SIZE*4];\n\n//\t\tscenepp_postproc_cube = r_nulltex;\n\n//\t\tTEXASSIGN(sceneblur_texture, Image_CreateTexture(\"***postprocess_blur***\", NULL, 0));\n\n\t\tTEXASSIGN(scenepp_texture_warp, Image_CreateTexture(\"***postprocess_warp***\", NULL, IF_NOMIPMAP|IF_NOGAMMA|IF_LINEAR));\n\t\tTEXASSIGN(scenepp_texture_edge, Image_CreateTexture(\"***postprocess_edge***\", NULL, IF_NOMIPMAP|IF_NOGAMMA|IF_LINEAR));\n\n\t\t// init warp texture - this specifies offset in\n\t\tfor (y=0; y<PP_WARP_TEX_SIZE; y++)\n\t\t{\n\t\t\tfor (x=0; x<PP_WARP_TEX_SIZE; x++)\n\t\t\t{\n\t\t\t\tfloat fx, fy;\n\n\t\t\t\ti = (x + y*PP_WARP_TEX_SIZE) * 4;\n\n\t\t\t\tfx = sin(((double)y / PP_WARP_TEX_SIZE) * M_PI * 2);\n\t\t\t\tfy = cos(((double)x / PP_WARP_TEX_SIZE) * M_PI * 2);\n\n\t\t\t\tpp_warp_tex[i  ] = (fx+1.0f)*127.0f;\n\t\t\t\tpp_warp_tex[i+1] = (fy+1.0f)*127.0f;\n\t\t\t\tpp_warp_tex[i+2] = 0;\n\t\t\t\tpp_warp_tex[i+3] = 0xff;\n\t\t\t}\n\t\t}\n\n\t\tImage_Upload(scenepp_texture_warp, TF_RGBX32, pp_warp_tex, NULL, PP_WARP_TEX_SIZE, PP_WARP_TEX_SIZE, 1, IF_LINEAR|IF_NOMIPMAP|IF_NOGAMMA);\n\n\t\t// TODO: init edge texture - this is ampscale * 2, with ampscale calculated\n\t\t// init warp texture - this specifies offset in\n\t\tfor (y=0; y<PP_AMP_TEX_SIZE; y++)\n\t\t{\n\t\t\tfor (x=0; x<PP_AMP_TEX_SIZE; x++)\n\t\t\t{\n\t\t\t\tfloat fx = 1, fy = 1;\n\n\t\t\t\ti = (x + y*PP_AMP_TEX_SIZE) * 4;\n\n\t\t\t\tif (x < PP_AMP_TEX_BORDER)\n\t\t\t\t{\n\t\t\t\t\tfx = (float)x / PP_AMP_TEX_BORDER;\n\t\t\t\t}\n\t\t\t\tif (x > PP_AMP_TEX_SIZE - PP_AMP_TEX_BORDER)\n\t\t\t\t{\n\t\t\t\t\tfx = (PP_AMP_TEX_SIZE - (float)x) / PP_AMP_TEX_BORDER;\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tif (y < PP_AMP_TEX_BORDER)\n\t\t\t\t{\n\t\t\t\t\tfy = (float)y / PP_AMP_TEX_BORDER;\n\t\t\t\t}\n\t\t\t\tif (y > PP_AMP_TEX_SIZE - PP_AMP_TEX_BORDER)\n\t\t\t\t{\n\t\t\t\t\tfy = (PP_AMP_TEX_SIZE - (float)y) / PP_AMP_TEX_BORDER;\n\t\t\t\t}\n\n\t\t\t\t//avoid any sudden changes.\n\t\t\t\tfx=sin(fx*M_PI*0.5);\n\t\t\t\tfy=sin(fy*M_PI*0.5);\n\n\t\t\t\t//lame\n\t\t\t\tfx = fy = min(fx, fy);\n\n\t\t\t\tpp_edge_tex[i  ] = fx * 255;\n\t\t\t\tpp_edge_tex[i+1] = fy * 255;\n\t\t\t\tpp_edge_tex[i+2] = 0;\n\t\t\t\tpp_edge_tex[i+3] = 0xff;\n\t\t\t}\n\t\t}\n\n\t\tImage_Upload(scenepp_texture_edge, TF_RGBX32, pp_edge_tex, NULL, PP_AMP_TEX_SIZE, PP_AMP_TEX_SIZE, 1, IF_LINEAR|IF_NOMIPMAP|IF_NOGAMMA);\n\t}\n\n\n\tvk.scenepp_waterwarp = R_RegisterShader(\"waterwarp\", SUF_NONE,\n\t\t\"{\\n\"\n\t\t\t\"program underwaterwarp\\n\"\n\t\t\t\"{\\n\"\n\t\t\t\t\"map $sourcecolour\\n\"\n\t\t\t\"}\\n\"\n\t\t\t\"{\\n\"\n\t\t\t\t\"map $upperoverlay\\n\"\n\t\t\t\"}\\n\"\n\t\t\t\"{\\n\"\n\t\t\t\t\"map $loweroverlay\\n\"\n\t\t\t\"}\\n\"\n\t\t\"}\\n\"\n\t\t);\n\tvk.scenepp_waterwarp->defaulttextures->upperoverlay = scenepp_texture_warp;\n\tvk.scenepp_waterwarp->defaulttextures->loweroverlay = scenepp_texture_edge;\n\n\tvk.scenepp_antialias = R_RegisterShader(\"fte_ppantialias\", 0, \n\t\t\"{\\n\"\n\t\t\t\"program fxaa\\n\"\n\t\t\t\"{\\n\"\n\t\t\t\t\"map $sourcecolour\\n\"\n\t\t\t\"}\\n\"\n\t\t\"}\\n\"\n\t\t);\n}\n\n\n\nstatic qboolean VK_R_RenderScene_Cubemap(struct vk_rendertarg *fb)\n{\n\tint cmapsize = 512;\n\tint i;\n\tstatic vec3_t ang[6] =\n\t\t\t\t{\t{0, -90, 0}, {0, 90, 0},\n\t\t\t\t\t{90, 0, 0}, {-90, 0, 0},\n\t\t\t\t\t{0, 0, 0}, {0, -180, 0}\t};\n\tvec3_t saveang;\n\tvec3_t saveorg;\n\n\tvrect_t vrect;\n\tpxrect_t prect;\n\textern cvar_t ffov;\n\n\tshader_t *shader;\n\tint facemask;\n\textern cvar_t r_projection;\n\tint osm;\n\tstruct vk_rendertarg_cube *rtc = &vk_rt_cubemap;\n\n\tif (!*ffov.string || !strcmp(ffov.string, \"0\"))\n\t{\n\t\tif (ffov.vec4[0] != scr_fov.value)\n\t\t{\n\t\t\tffov.value = ffov.vec4[0] = scr_fov.value;\n\t\t\tShader_NeedReload(false);\t//gah!\n\t\t}\n\t}\n\n\tfacemask = 0;\n\tswitch(r_projection.ival)\n\t{\n\tdefault:\t//invalid.\n\t\treturn false;\n\tcase PROJ_STEREOGRAPHIC:\n\t\tshader = R_RegisterShader(\"postproc_stereographic\", SUF_NONE,\n\t\t\t\t\"{\\n\"\n\t\t\t\t\t\"program postproc_stereographic\\n\"\n\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\"map $sourcecube\\n\"\n\t\t\t\t\t\"}\\n\"\n\t\t\t\t\"}\\n\"\n\t\t\t\t);\n\n\t\tfacemask |= 1<<4; /*front view*/\n\t\tif (ffov.value > 70)\n\t\t{\n\t\t\tfacemask |= (1<<0) | (1<<1); /*side/top*/\n\t\t\tif (ffov.value > 85)\n\t\t\t\tfacemask |= (1<<2) | (1<<3); /*bottom views*/\n\t\t\tif (ffov.value > 300)\n\t\t\t\tfacemask |= 1<<5; /*back view*/\n\t\t}\n\t\tbreak;\n\tcase PROJ_FISHEYE:\n\t\tshader = R_RegisterShader(\"postproc_fisheye\", SUF_NONE,\n\t\t\t\t\"{\\n\"\n\t\t\t\t\t\"program postproc_fisheye\\n\"\n\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\"map $sourcecube\\n\"\n\t\t\t\t\t\"}\\n\"\n\t\t\t\t\"}\\n\"\n\t\t\t\t);\n\n\t\t//fisheye view sees up to a full sphere\n\t\tfacemask |= 1<<4; /*front view*/\n\t\tif (ffov.value > 77)\n\t\t\tfacemask |= (1<<0) | (1<<1) | (1<<2) | (1<<3); /*side/top/bottom views*/\n\t\tif (ffov.value > 270)\n\t\t\tfacemask |= 1<<5; /*back view*/\n\t\tbreak;\n\tcase PROJ_PANORAMA:\n\t\tshader = R_RegisterShader(\"postproc_panorama\", SUF_NONE,\n\t\t\t\t\"{\\n\"\n\t\t\t\t\t\"program postproc_panorama\\n\"\n\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\"map $sourcecube\\n\"\n\t\t\t\t\t\"}\\n\"\n\t\t\t\t\"}\\n\"\n\t\t\t\t);\n\n\t\t//panoramic view needs at most the four sides\n\t\tfacemask |= 1<<4; /*front view*/\n\t\tif (ffov.value > 90)\n\t\t{\n\t\t\tfacemask |= (1<<0) | (1<<1); /*side views*/\n\t\t\tif (ffov.value > 270)\n\t\t\t\tfacemask |= 1<<5; /*back view*/\n\t\t}\n\t\tfacemask = 0x3f;\n\t\tbreak;\n\tcase PROJ_LAEA:\n\t\tshader = R_RegisterShader(\"postproc_laea\", SUF_NONE,\n\t\t\t\t\"{\\n\"\n\t\t\t\t\t\"program postproc_laea\\n\"\n\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\"map $sourcecube\\n\"\n\t\t\t\t\t\"}\\n\"\n\t\t\t\t\"}\\n\"\n\t\t\t\t);\n\n\t\tfacemask |= 1<<4; /*front view*/\n\t\tif (ffov.value > 90)\n\t\t{\n\t\t\tfacemask |= (1<<0) | (1<<1) | (1<<2) | (1<<3); /*side/top/bottom views*/\n\t\t\tif (ffov.value > 270)\n\t\t\t\tfacemask |= 1<<5; /*back view*/\n\t\t}\n\t\tbreak;\n\n\tcase PROJ_EQUIRECTANGULAR:\n\t\tshader = R_RegisterShader(\"postproc_equirectangular\", SUF_NONE,\n\t\t\t\t\"{\\n\"\n\t\t\t\t\t\"program postproc_equirectangular\\n\"\n\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\"map $sourcecube\\n\"\n\t\t\t\t\t\"}\\n\"\n\t\t\t\t\"}\\n\"\n\t\t\t\t);\n\n\t\tfacemask = 0x3f;\n#if 0\n\t\tfacemask |= 1<<4; /*front view*/\n\t\tif (ffov.value > 90)\n\t\t{\n\t\t\tfacemask |= (1<<0) | (1<<1) | (1<<2) | (1<<3); /*side/top/bottom views*/\n\t\t\tif (ffov.value > 270)\n\t\t\t\tfacemask |= 1<<5; /*back view*/\n\t\t}\n#endif\n\t\tbreak;\n\tcase PROJ_PANINI:\n\t\tshader = R_RegisterShader(\"postproc_panini\", SUF_NONE,\n\t\t\t\t\"{\\n\"\n\t\t\t\t\t\"program postproc_panini\\n\"\n\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\"map $sourcecube\\n\"\n\t\t\t\t\t\"}\\n\"\n\t\t\t\t\"}\\n\"\n\t\t\t\t);\n\n\t\tfacemask |= 1<<4; /*front view*/\n\t\tif (ffov.value > 70)\n\t\t{\n\t\t\tfacemask |= (1<<0) | (1<<1); /*side/top*/\n\t\t\tif (ffov.value > 85)\n\t\t\t\tfacemask |= (1<<2) | (1<<3); /*bottom views*/\n\t\t\tif (ffov.value > 300)\n\t\t\t\tfacemask |= 1<<5; /*back view*/\n\t\t}\n\t\tbreak;\n\t}\n\n\tif (!shader || !shader->prog)\n\t\treturn false;\t//erk. shader failed.\n\n\t//FIXME: we should be able to rotate the view\n\n\tvrect = r_refdef.vrect;\n\tprect = r_refdef.pxrect;\n//\tprect.x = (vrect.x * vid.pixelwidth)/vid.width;\n//\tprect.width = (vrect.width * vid.pixelwidth)/vid.width;\n//\tprect.y = (vrect.y * vid.pixelheight)/vid.height;\n//\tprect.height = (vrect.height * vid.pixelheight)/vid.height;\n\n\tif (sh_config.texture_non_power_of_two_pic)\n\t{\n\t\tcmapsize = prect.width > prect.height?prect.width:prect.height;\n\t\tif (cmapsize > 4096)//sh_config.texture_maxsize)\n\t\t\tcmapsize = 4096;//sh_config.texture_maxsize;\n\t}\n\n\n\tr_refdef.flags |= RDF_FISHEYE;\n\tvid.fbpwidth = vid.fbpheight = cmapsize;\n\n\t//FIXME: gl_max_size\n\n\tVectorCopy(r_refdef.vieworg, saveorg);\n\tVectorCopy(r_refdef.viewangles, saveang);\n\tsaveang[2] = 0;\n\n\tosm = r_refdef.stereomethod;\n\tr_refdef.stereomethod = STEREO_OFF;\n\n\tVKBE_RT_Gen_Cube(rtc, cmapsize, r_clear.ival?true:false);\n\n\tvrect = r_refdef.vrect;\t//save off the old vrect\n\n\tr_refdef.vrect.width = (cmapsize * vid.fbvwidth) / vid.fbpwidth;\n\tr_refdef.vrect.height = (cmapsize * vid.fbvheight) / vid.fbpheight;\n\tr_refdef.vrect.x = 0;\n\tr_refdef.vrect.y = prect.y;\n\n\tang[0][0] = -saveang[0];\n\tang[0][1] = -90;\n\tang[0][2] = -saveang[0];\n\n\tang[1][0] = -saveang[0];\n\tang[1][1] = 90;\n\tang[1][2] = saveang[0];\n\tang[5][0] = -saveang[0]*2;\n\n\t//in theory, we could use a geometry shader to duplicate the polygons to each face.\n\t//that would of course require that every bit of glsl had such a geometry shader.\n\t//it would at least reduce cpu load quite a bit.\n\tfor (i = 0; i < 6; i++)\n\t{\n\t\tif (!(facemask & (1<<i)))\n\t\t\tcontinue;\n\n\t\tVKBE_RT_Begin(&rtc->face[i]);\n\n\t\tr_refdef.fov_x = 90;\n\t\tr_refdef.fov_y = 90;\n\t\tr_refdef.viewangles[0] = saveang[0]+ang[i][0];\n\t\tr_refdef.viewangles[1] = saveang[1]+ang[i][1];\n\t\tr_refdef.viewangles[2] = saveang[2]+ang[i][2];\n\n\n\t\tVK_SetupViewPortProjection(true, NULL, NULL, NULL);\n\n\t\t/*if (!vk.rendertarg->depthcleared)\n\t\t{\n\t\t\tVkClearAttachment clr;\n\t\t\tVkClearRect rect;\n\t\t\tclr.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;\n\t\t\tclr.clearValue.depthStencil.depth = 1;\n\t\t\tclr.clearValue.depthStencil.stencil = 0;\n\t\t\tclr.colorAttachment = 1;\n\t\t\trect.rect.offset.x = r_refdef.pxrect.x;\n\t\t\trect.rect.offset.y = r_refdef.pxrect.y;\n\t\t\trect.rect.extent.width = r_refdef.pxrect.width;\n\t\t\trect.rect.extent.height = r_refdef.pxrect.height;\n\t\t\trect.layerCount = 1;\n\t\t\trect.baseArrayLayer = 0;\n\t\t\tvkCmdClearAttachments(vk.frame->cbuf, 1, &clr, 1, &rect);\n\t\t\tvk.rendertarg->depthcleared = true;\n\t\t}*/\n\n\t\tVKBE_SelectEntity(&r_worldentity);\n\n\t\tR_SetFrustum (r_refdef.m_projection_std, r_refdef.m_view);\n\t\tRQ_BeginFrame();\n//\t\tif (!(r_refdef.flags & RDF_NOWORLDMODEL))\n//\t\t{\n//\t\t\tif (cl.worldmodel)\n//\t\t\t\tP_DrawParticles ();\n//\t\t}\n\t\tSurf_DrawWorld();\n\t\tRQ_RenderBatchClear();\n\n\t\tvk.rendertarg->depthcleared = false;\n\n\t\tif (R2D_Flush)\n\t\t\tCon_Printf(\"no flush\\n\");\n\n\t\tVKBE_RT_End(&rtc->face[i]);\n\t}\n\n\tr_refdef.vrect = vrect;\n\tr_refdef.pxrect = prect;\n\tVectorCopy(saveorg, r_refdef.vieworg);\n\tr_refdef.stereomethod = osm;\n\n\tVKBE_RT_Begin(fb);\n\n\tr_refdef.flipcull = 0;\n\tVK_Set2D();\n\n\tshader->defaulttextures->reflectcube = &rtc->q_colour;\n\n\t// draw it through the shader\n\tif (r_projection.ival == PROJ_EQUIRECTANGULAR)\n\t{\n\t\t//note vr screenshots have requirements here\n\t\tR2D_Image(vrect.x, vrect.y, vrect.width, vrect.height, 0, 1, 1, 0, shader);\n\t}\n\telse if (r_projection.ival == PROJ_PANORAMA)\n\t{\n\t\tfloat saspect = .5;\n\t\tfloat taspect = vrect.height / vrect.width * ffov.value / 90;//(0.5 * vrect.width) / vrect.height;\n\t\tR2D_Image(vrect.x, vrect.y, vrect.width, vrect.height, -saspect, taspect, saspect, -taspect, shader);\n\t}\n\telse if (vrect.width > vrect.height)\n\t{\n\t\tfloat aspect = (0.5 * vrect.height) / vrect.width;\n\t\tR2D_Image(vrect.x, vrect.y, vrect.width, vrect.height, -0.5, aspect, 0.5, -aspect, shader);\n\t}\n\telse\n\t{\n\t\tfloat aspect = (0.5 * vrect.width) / vrect.height;\n\t\tR2D_Image(vrect.x, vrect.y, vrect.width, vrect.height, -aspect, 0.5, aspect, -0.5, shader);\n\t}\n\n\tif (R2D_Flush)\n\t\tR2D_Flush();\n\n\treturn true;\n}\n\nvoid VK_R_RenderEye(texid_t image, const pxrect_t *viewport, const vec4_t fovoverride, const float projmatrix[16], const float eyematrix[12])\n{\n\tstruct vk_rendertarg *rt;\n\n\tVK_SetupViewPortProjection(false, eyematrix, fovoverride, projmatrix);\n\n\trt = &postproc[postproc_buf++%countof(postproc)];\n\trt->rpassflags |= RP_VR;\n\tVKBE_RT_Gen(rt, image?image->vkimage:NULL, 320, 200, false, RT_IMAGEFLAGS);\n\tVKBE_RT_Begin(rt);\n\n\n\tif (!vk.rendertarg->depthcleared)\n\t{\n\t\tVkClearAttachment clr;\n\t\tVkClearRect rect;\n\t\tclr.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;\n\t\tclr.clearValue.depthStencil.depth = 1;\n\t\tclr.clearValue.depthStencil.stencil = 0;\n\t\tclr.colorAttachment = 1;\n\t\trect.rect.offset.x = r_refdef.pxrect.x;\n\t\trect.rect.offset.y = r_refdef.pxrect.y;\n\t\trect.rect.extent.width = r_refdef.pxrect.width;\n\t\trect.rect.extent.height = r_refdef.pxrect.height;\n\t\trect.layerCount = 1;\n\t\trect.baseArrayLayer = 0;\n\t\tvkCmdClearAttachments(vk.rendertarg->cbuf, 1, &clr, 1, &rect);\n\t\tvk.rendertarg->depthcleared = true;\n\t}\n\n\tVKBE_SelectEntity(&r_worldentity);\n\n\tR_SetFrustum (r_refdef.m_projection_std, r_refdef.m_view);\n\tRQ_BeginFrame();\n//\tif (!(r_refdef.flags & RDF_NOWORLDMODEL))\n//\t{\n//\t\tif (cl.worldmodel)\n//\t\t\tP_DrawParticles ();\n//\t}\n\tSurf_DrawWorld();\n\tRQ_RenderBatchClear();\n\n\tvk.rendertarg->depthcleared = false;\n\n\tVKBE_RT_End(rt);\n\trt->rpassflags &= ~RP_VR;\n}\n\nvoid\tVK_R_RenderView\t\t\t\t(void)\n{\n\textern unsigned int r_viewcontents;\n\tstruct vk_rendertarg *rt, *rtscreen = vk.rendertarg;\n\textern cvar_t r_fxaa;\n\textern\tcvar_t r_renderscale, r_postprocshader;\n\tfloat renderscale = r_renderscale.value;\n\tshader_t *custompostproc;\n\n\tif (r_norefresh.value || !vid.fbpwidth || !vid.fbpwidth)\n\t{\n\t\tVK_Set2D ();\n\t\treturn;\n\t}\n\n\tVKBE_Set2D(false);\n\n\tSurf_SetupFrame();\n\n\tif (vid.vr && vid.vr->Render(VK_R_RenderEye))\n\t{\n\t\tVK_Set2D ();\n\t\treturn;\n\t}\n\n\t//check if we can do underwater warp\n\tif (cls.protocol != CP_QUAKE2)\t//quake2 tells us directly\n\t{\n\t\tif (r_viewcontents & FTECONTENTS_FLUID)\n\t\t\tr_refdef.flags |= RDF_UNDERWATER;\n\t\telse\n\t\t\tr_refdef.flags &= ~RDF_UNDERWATER;\n\t}\n\tif (r_refdef.flags & RDF_UNDERWATER)\n\t{\n\t\textern cvar_t r_projection;\n\t\tif (!r_waterwarp.value || r_projection.ival)\n\t\t\tr_refdef.flags &= ~RDF_UNDERWATER;\t//no warp at all\n\t\telse if (r_waterwarp.value > 0)\n\t\t\tr_refdef.flags |= RDF_WATERWARP;\t//try fullscreen warp instead if we can\n\t}\n\n\tif (!r_refdef.globalfog.density)\n\t{\n\t\textern cvar_t r_fog_linear;\n\n\t\tint fogtype = ((r_refdef.flags & RDF_UNDERWATER) && cl.fog[FOGTYPE_WATER].density)?FOGTYPE_WATER:FOGTYPE_AIR;\n\t\tCL_BlendFog(&r_refdef.globalfog, &cl.oldfog[fogtype], realtime, &cl.fog[fogtype]);\n\t\tif (!r_fog_linear.ival)\n\t\t\tr_refdef.globalfog.density /= 64;\t//FIXME\n\t}\n\n\tcustompostproc = NULL;\n\tif (r_refdef.flags & RDF_NOWORLDMODEL)\n\t\trenderscale = 1;\t//with no worldmodel, this is probably meant to be transparent so make sure that there's no post-proc stuff messing up transparencies.\n\telse\n\t{\n\t\tif (*r_postprocshader.string)\n\t\t{\n\t\t\tcustompostproc = R_RegisterCustom(NULL, r_postprocshader.string, SUF_NONE, NULL, NULL);\n\t\t\tif (custompostproc)\n\t\t\t\tr_refdef.flags |= RDF_CUSTOMPOSTPROC;\n\t\t}\n\n\t\tif (r_fxaa.ival) //overlays will have problems.\n\t\t\tr_refdef.flags |= RDF_ANTIALIAS;\n\n\t\tif (R_CanBloom())\n\t\t\tr_refdef.flags |= RDF_BLOOM;\n\n\t\tif (vid_hardwaregamma.ival == 4 && (v_gamma.value!=1||v_contrast.value!=1||v_contrastboost.value!=1||v_brightness.value!=0))\n\t\t\tr_refdef.flags |= RDF_SCENEGAMMA;\n\t}\n\n//\tif (vk.multisamplebits != VK_SAMPLE_COUNT_1_BIT)\t//these are unsupported right now.\n//\t\tr_refdef.flags &= ~(RDF_CUSTOMPOSTPROC|RDF_ANTIALIAS|RDF_BLOOM);\n\n\t//\n\t// figure out the viewport\n\t//\n\t{\n\t\tint x = r_refdef.vrect.x * vid.pixelwidth/(int)vid.width;\n\t\tint x2 = (r_refdef.vrect.x + r_refdef.vrect.width) * vid.pixelwidth/(int)vid.width;\n\t\tint y = (r_refdef.vrect.y) * vid.pixelheight/(int)vid.height;\n\t\tint y2 = ((int)(r_refdef.vrect.y + r_refdef.vrect.height)) * vid.pixelheight/(int)vid.height;\n\n\t\t// fudge around because of frac screen scale\n\t\tif (x > 0)\n\t\t\tx--;\n\t\tif (x2 < vid.pixelwidth)\n\t\t\tx2++;\n\t\tif (y < 0)\n\t\t\ty--;\n\t\tif (y2 < vid.pixelheight)\n\t\t\ty2++;\n\n\t\tr_refdef.pxrect.x = x;\n\t\tr_refdef.pxrect.y = y;\n\t\tr_refdef.pxrect.width = x2 - x;\n\t\tr_refdef.pxrect.height = y2 - y;\n\t\tr_refdef.pxrect.maxheight = vid.pixelheight;\n\t}\n\n\tif (renderscale != 1.0 || vk.multisamplebits != VK_SAMPLE_COUNT_1_BIT)\n\t{\n\t\tr_refdef.flags |= RDF_RENDERSCALE;\n\t\tif (renderscale < 0)\n\t\t\trenderscale *= -1;\n\t\tr_refdef.pxrect.width *= renderscale;\n\t\tr_refdef.pxrect.height *= renderscale;\n\t\tr_refdef.pxrect.maxheight = r_refdef.pxrect.height;\n\t}\n\n\tif (r_refdef.pxrect.width <= 0 || r_refdef.pxrect.height <= 0)\n\t\treturn;\t//you're not allowed to do that, dude.\n\n\t//FIXME: VF_RT_*\n\t//FIXME: if we're meant to be using msaa, render the scene to an msaa target and then resolve.\n\n\tpostproc_buf = 0;\n\tif (r_refdef.flags & (RDF_ALLPOSTPROC|RDF_RENDERSCALE|RDF_SCENEGAMMA))\n\t{\n\t\tr_refdef.pxrect.x = 0;\n\t\tr_refdef.pxrect.y = 0;\n\t\trt = &postproc[postproc_buf++%countof(postproc)];\n\t\trt->rpassflags = 0;\n\t\tif (vk.multisamplebits!=VK_SAMPLE_COUNT_1_BIT)\n\t\t\trt->rpassflags |= RP_MULTISAMPLE;\n\t\tif (r_refdef.flags&RDF_SCENEGAMMA)\t//if we're doing scenegamma here, use an fp16 target for extra precision\n\t\t\trt->rpassflags |= RP_FP16;\n\t\tVKBE_RT_Gen(rt, NULL, r_refdef.pxrect.width, r_refdef.pxrect.height, false, (r_renderscale.value < 0)?RT_IMAGEFLAGS-IF_LINEAR+IF_NEAREST:RT_IMAGEFLAGS);\n\t}\n\telse\n\t\trt = rtscreen;\n\n\tif (!(r_refdef.flags & RDF_NOWORLDMODEL) && VK_R_RenderScene_Cubemap(rt))\n\t{\n\t}\n\telse\n\t{\n\t\tVK_SetupViewPortProjection(false, NULL, NULL, NULL);\n\n\t\tif (rt != rtscreen)\n\t\t\tVKBE_RT_Begin(rt);\n\t\telse\n\t\t{\n\t\t\tVkViewport vp[1];\n\t\t\tVkRect2D scissor[1];\n\t\t\tvp[0].x = r_refdef.pxrect.x;\n\t\t\tvp[0].y = r_refdef.pxrect.y;\n\t\t\tvp[0].width = r_refdef.pxrect.width;\n\t\t\tvp[0].height = r_refdef.pxrect.height;\n\t\t\tvp[0].minDepth = 0.0;\n\t\t\tvp[0].maxDepth = 1.0;\n\t\t\tscissor[0].offset.x = r_refdef.pxrect.x;\n\t\t\tscissor[0].offset.y = r_refdef.pxrect.y;\n\t\t\tscissor[0].extent.width = r_refdef.pxrect.width;\n\t\t\tscissor[0].extent.height = r_refdef.pxrect.height;\n\t\t\tvkCmdSetViewport(vk.rendertarg->cbuf, 0, countof(vp), vp);\n\t\t\tvkCmdSetScissor(vk.rendertarg->cbuf, 0, countof(scissor), scissor);\n\t\t}\n\n\t\tif (!vk.rendertarg->depthcleared)\n\t\t{\n\t\t\tVkClearAttachment clr;\n\t\t\tVkClearRect rect;\n\t\t\tclr.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;\n\t\t\tclr.clearValue.depthStencil.depth = 1;\n\t\t\tclr.clearValue.depthStencil.stencil = 0;\n\t\t\tclr.colorAttachment = 1;\n\t\t\trect.rect.offset.x = r_refdef.pxrect.x;\n\t\t\trect.rect.offset.y = r_refdef.pxrect.y;\n\t\t\trect.rect.extent.width = r_refdef.pxrect.width;\n\t\t\trect.rect.extent.height = r_refdef.pxrect.height;\n\t\t\trect.layerCount = 1;\n\t\t\trect.baseArrayLayer = 0;\n\t\t\tvkCmdClearAttachments(vk.rendertarg->cbuf, 1, &clr, 1, &rect);\n\t\t\tvk.rendertarg->depthcleared = true;\n\t\t}\n\n\t\tVKBE_SelectEntity(&r_worldentity);\n\n\t\tR_SetFrustum (r_refdef.m_projection_std, r_refdef.m_view);\n\t\tRQ_BeginFrame();\n//\t\tif (!(r_refdef.flags & RDF_NOWORLDMODEL))\n//\t\t{\n//\t\t\tif (cl.worldmodel)\n//\t\t\t\tP_DrawParticles ();\n//\t\t}\n\t\tSurf_DrawWorld();\n\t\tRQ_RenderBatchClear();\n\n\t\tvk.rendertarg->depthcleared = false;\n\n\t\tVK_Set2D ();\n\n\t\tif (rt != rtscreen)\n\t\t\tVKBE_RT_End(rt);\n\t}\n\n\tif (r_refdef.flags & RDF_ALLPOSTPROC)\n\t{\n\t\tif (!vk.scenepp_waterwarp)\n\t\t\tVK_Init_PostProc();\n\t\t//FIXME: chain renderpasses as required.\n\n\t\tif (r_refdef.flags & RDF_SCENEGAMMA)\n\t\t{\n\t\t\tshader_t *s = R_RegisterShader(\"fte_scenegamma\", 0,\n\t\t\t\t\"{\\n\"\n\t\t\t\t\t\"program defaultgammacb\\n\"\n\t\t\t\t\t\"affine\\n\"\n\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\"map $sourcecolour\\n\"\n\t\t\t\t\t\t\"nodepthtest\\n\"\n\t\t\t\t\t\"}\\n\"\n\t\t\t\t\"}\\n\"\n\t\t\t\t);\n\n\t\t\tr_refdef.flags &= ~RDF_SCENEGAMMA;\n\t\t\tvk.sourcecolour = &rt->q_colour;\n\t\t\tif (r_refdef.flags & RDF_ALLPOSTPROC)\n\t\t\t{\n\t\t\t\trt = &postproc[postproc_buf++];\n\t\t\t\trt->rpassflags = 0;\n\t\t\t\tVKBE_RT_Gen(rt, NULL, 320, 200, false, RT_IMAGEFLAGS);\n\t\t\t}\n\t\t\telse\n\t\t\t\trt = rtscreen;\n\t\t\tif (rt != rtscreen)\n\t\t\t\tVKBE_RT_Begin(rt);\n\t\t\tR2D_ImageColours (v_gammainverted.ival?v_gamma.value:(1/v_gamma.value), v_contrast.value, v_brightness.value, v_contrastboost.value);\n\t\t\tR2D_Image(r_refdef.vrect.x, r_refdef.vrect.y, r_refdef.vrect.width, r_refdef.vrect.height, 0, 0, 1, 1, s);\n\t\t\tR2D_ImageColours (1, 1, 1, 1);\n\t\t\tR2D_Flush();\n\t\t\tif (rt != rtscreen)\n\t\t\t\tVKBE_RT_End(rt);\n\t\t}\n\n\t\tif (r_refdef.flags & RDF_WATERWARP)\n\t\t{\n\t\t\tr_refdef.flags &= ~RDF_WATERWARP;\n\t\t\tvk.sourcecolour = &rt->q_colour;\n\t\t\tif (r_refdef.flags & RDF_ALLPOSTPROC)\n\t\t\t{\n\t\t\t\trt = &postproc[postproc_buf++];\n\t\t\t\trt->rpassflags = 0;\n\t\t\t\tVKBE_RT_Gen(rt, NULL, 320, 200, false, RT_IMAGEFLAGS);\n\t\t\t}\n\t\t\telse\n\t\t\t\trt = rtscreen;\n\t\t\tif (rt != rtscreen)\n\t\t\t\tVKBE_RT_Begin(rt);\n\t\t\tR2D_Image(r_refdef.vrect.x, r_refdef.vrect.y, r_refdef.vrect.width, r_refdef.vrect.height, 0, 0, 1, 1, vk.scenepp_waterwarp);\n\t\t\tR2D_Flush();\n\t\t\tif (rt != rtscreen)\n\t\t\t\tVKBE_RT_End(rt);\n\t\t}\n\t\tif (r_refdef.flags & RDF_CUSTOMPOSTPROC)\n\t\t{\n\t\t\tr_refdef.flags &= ~RDF_CUSTOMPOSTPROC;\n\t\t\tvk.sourcecolour = &rt->q_colour;\n\t\t\tif (r_refdef.flags & RDF_ALLPOSTPROC)\n\t\t\t{\n\t\t\t\trt = &postproc[postproc_buf++];\n\t\t\t\trt->rpassflags = 0;\n\t\t\t\tVKBE_RT_Gen(rt, NULL, 320, 200, false, RT_IMAGEFLAGS);\n\t\t\t}\n\t\t\telse\n\t\t\t\trt = rtscreen;\n\t\t\tif (rt != rtscreen)\n\t\t\t\tVKBE_RT_Begin(rt);\n\t\t\tR2D_Image(r_refdef.vrect.x, r_refdef.vrect.y, r_refdef.vrect.width, r_refdef.vrect.height, 0, 1, 1, 0, custompostproc);\n\t\t\tR2D_Flush();\n\t\t\tif (rt != rtscreen)\n\t\t\t\tVKBE_RT_End(rt);\n\t\t}\n\t\tif (r_refdef.flags & RDF_ANTIALIAS)\n\t\t{\n\t\t\tr_refdef.flags &= ~RDF_ANTIALIAS;\n\t\t\tR2D_ImageColours(rt->width, rt->height, 1, 1);\n\t\t\tvk.sourcecolour = &rt->q_colour;\n\t\t\tif (r_refdef.flags & RDF_ALLPOSTPROC)\n\t\t\t{\n\t\t\t\trt = &postproc[postproc_buf++];\n\t\t\t\trt->rpassflags = 0;\n\t\t\t\tVKBE_RT_Gen(rt, NULL, 320, 200, false, RT_IMAGEFLAGS);\n\t\t\t}\n\t\t\telse\n\t\t\t\trt = rtscreen;\n\t\t\tif (rt != rtscreen)\n\t\t\t\tVKBE_RT_Begin(rt);\n\t\t\tR2D_Image(r_refdef.vrect.x, r_refdef.vrect.y, r_refdef.vrect.width, r_refdef.vrect.height, 0, 1, 1, 0, vk.scenepp_antialias);\n\t\t\tR2D_ImageColours(1, 1, 1, 1);\n\t\t\tR2D_Flush();\n\t\t\tif (rt != rtscreen)\n\t\t\t\tVKBE_RT_End(rt);\n\t\t}\n\t\tif (r_refdef.flags & RDF_BLOOM)\n\t\t{\n\t\t\tVK_R_BloomBlend(&rt->q_colour, r_refdef.vrect.x, r_refdef.vrect.y, r_refdef.vrect.width, r_refdef.vrect.height);\n\t\t\trt = rtscreen;\n\t\t}\n\t}\n\telse if (r_refdef.flags & RDF_RENDERSCALE)\n\t{\n\t\tif (!vk.scenepp_rescale)\n\t\t\tvk.scenepp_rescale = R_RegisterShader(\"fte_rescaler\", 0, \n\t\t\t\t\"{\\n\"\n\t\t\t\t\t\"program default2d\\n\"\n\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\"map $sourcecolour\\n\"\n\t\t\t\t\t\"}\\n\"\n\t\t\t\t\"}\\n\"\n\t\t\t\t);\n\t\tvk.sourcecolour = &rt->q_colour;\n\t\trt = rtscreen;\n\t\tR2D_Image(r_refdef.vrect.x, r_refdef.vrect.y, r_refdef.vrect.width, r_refdef.vrect.height, 0, 0, 1, 1, vk.scenepp_rescale);\n\t\tR2D_Flush();\n\t}\n\tvk.sourcecolour = r_nulltex;\n}\n\n\ntypedef struct\n{\n\tuint32_t imageformat;\n\tuint32_t imagestride;\n\tuint32_t imagewidth;\n\tuint32_t imageheight;\n\tVkBuffer buffer;\n\tsize_t memsize;\n\tVkDeviceMemory memory;\n\tvoid (*gotrgbdata) (void *rgbdata, intptr_t bytestride, size_t width, size_t height, enum uploadfmt fmt);\n} vkscreencapture_t;\n\nstatic void VKVID_CopiedRGBData (void*ctx)\n{\t//some fence got hit, we did our copy, data is now cpu-visible, cache-willing.\n\tvkscreencapture_t *capt = ctx;\n\tvoid *imgdata;\n\tVkAssert(vkMapMemory(vk.device, capt->memory, 0, capt->memsize, 0, &imgdata));\n\tcapt->gotrgbdata(imgdata, capt->imagestride, capt->imagewidth, capt->imageheight, capt->imageformat);\n\tvkUnmapMemory(vk.device, capt->memory);\n\tvkDestroyBuffer(vk.device, capt->buffer, vkallocationcb);\n\tvkFreeMemory(vk.device, capt->memory, vkallocationcb);\n}\nvoid VKVID_QueueGetRGBData\t\t\t(void (*gotrgbdata) (void *rgbdata, intptr_t bytestride, size_t width, size_t height, enum uploadfmt fmt))\n{\n\t//should be half way through rendering\n\tvkscreencapture_t *capt;\n\n\tVkBufferImageCopy icpy;\n\n\tVkMemoryRequirements mem_reqs;\n\tVkMemoryAllocateInfo memAllocInfo = {VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO};\n\tVkBufferCreateInfo bci = {VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO};\n\n\n\tif (!VK_SCR_GrabBackBuffer())\n\t\treturn;\n\n\tif (!vk.frame->backbuf->colour.width || !vk.frame->backbuf->colour.height)\n\t\treturn; //erm, some kind of error?\n\n\tcapt = VK_AtFrameEnd(VKVID_CopiedRGBData, NULL, sizeof(*capt));\n\tcapt->gotrgbdata = gotrgbdata;\n\n\t//FIXME: vkCmdBlitImage the image to convert it from half-float or whatever to a format that our screenshot etc code can cope with.\n\tcapt->imageformat = TF_BGRA32;\n\tcapt->imagestride = vk.frame->backbuf->colour.width*4;\t//vulkan is top-down, so this should be positive.\n\tcapt->imagewidth = vk.frame->backbuf->colour.width;\n\tcapt->imageheight = vk.frame->backbuf->colour.height;\n\n\tbci.flags = 0;\n\tbci.size = capt->memsize = capt->imagewidth*capt->imageheight*4;\n\tbci.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT;\n\tbci.sharingMode = VK_SHARING_MODE_EXCLUSIVE;\n\tbci.queueFamilyIndexCount = 0;\n\tbci.pQueueFamilyIndices = NULL;\n\n\tVkAssert(vkCreateBuffer(vk.device, &bci, vkallocationcb, &capt->buffer));\n\tvkGetBufferMemoryRequirements(vk.device, capt->buffer, &mem_reqs);\n\tmemAllocInfo.allocationSize = mem_reqs.size;\n\tmemAllocInfo.memoryTypeIndex = vk_find_memory_try(mem_reqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT|VK_MEMORY_PROPERTY_HOST_CACHED_BIT);\n\tif (memAllocInfo.memoryTypeIndex == ~0u)\n\t\tmemAllocInfo.memoryTypeIndex = vk_find_memory_require(mem_reqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);\n\tVkAssert(vkAllocateMemory(vk.device, &memAllocInfo, vkallocationcb, &capt->memory));\n\tDebugSetName(VK_OBJECT_TYPE_DEVICE_MEMORY, (uint64_t)capt->memory, \"VKVID_QueueGetRGBData\");\n\tVkAssert(vkBindBufferMemory(vk.device, capt->buffer, capt->memory, 0));\n\n\tset_image_layout(vk.rendertarg->cbuf, vk.frame->backbuf->colour.image, VK_IMAGE_ASPECT_COLOR_BIT,\n\t\t\tVK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,\n\t\t\tVK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_ACCESS_TRANSFER_READ_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT);\n\n\ticpy.bufferOffset = 0;\n\ticpy.bufferRowLength = 0;\t//packed\n\ticpy.bufferImageHeight = 0;\t//packed\n\ticpy.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;\n\ticpy.imageSubresource.mipLevel = 0;\n\ticpy.imageSubresource.baseArrayLayer = 0;\n\ticpy.imageSubresource.layerCount = 1;\n\ticpy.imageOffset.x = 0;\n\ticpy.imageOffset.y = 0;\n\ticpy.imageOffset.z = 0;\n\ticpy.imageExtent.width = capt->imagewidth;\n\ticpy.imageExtent.height = capt->imageheight;\n\ticpy.imageExtent.depth = 1;\n\n\tvkCmdCopyImageToBuffer(vk.rendertarg->cbuf, vk.frame->backbuf->colour.image, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, capt->buffer, 1, &icpy);\n\n\tset_image_layout(vk.rendertarg->cbuf, vk.frame->backbuf->colour.image, VK_IMAGE_ASPECT_COLOR_BIT,\n\t\t\tVK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_ACCESS_TRANSFER_READ_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,\n\t\t\tVK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT);\n}\n\nchar\t*VKVID_GetRGBInfo\t\t\t(int *bytestride, int *truevidwidth, int *truevidheight, enum uploadfmt *fmt)\n{\n\t//in order to deal with various backbuffer formats (like half-float) etc, we play safe and blit the framebuffer to a safe format.\n\t//we then transfer that into a buffer that we can then directly read.\n\t//and then we allocate a C buffer that we then copy it into...\n\t//so yeah, 3 copies. life sucks.\n\t//blit requires support for VK_IMAGE_USAGE_TRANSFER_DST_BIT on our image, which means we need optimal, which means we can't directly map it, which means we need the buffer copy too.\n\t//this might be relaxed on mobile, but who really takes screenshots on mobiles anyway?!? anyway, video capture shouldn't be using this either way so top performance isn't a concern\n\tif (VK_SCR_GrabBackBuffer())\n\t{\n\t\tVkImageLayout framebufferlayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;//vk.frame->backbuf->colour.layout;\n\n\t\tvoid *imgdata, *outdata;\n\t\tstruct vk_fencework *fence = VK_FencedBegin(NULL, 0);\n\t\tVkImage tempimage;\n\t\tVkDeviceMemory tempmemory;\n\t\tVkBufferCreateInfo bci = {VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO};\n\t\tVkBuffer tempbuffer;\n\t\tVkDeviceMemory tempbufmemory;\n\t\tVkMemoryRequirements mem_reqs;\n\t\tVkMemoryAllocateInfo memAllocInfo = {VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO};\n\t\tVkImageCreateInfo ici = {VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO};\n\t\t//VkFormatProperties vkfmt;\n\n\t\tici.flags = 0;\n\t\tici.imageType = VK_IMAGE_TYPE_2D;\n\t\t/*vkGetPhysicalDeviceFormatProperties(vk.gpu, VK_FORMAT_B8G8R8_UNORM, &vkfmt);\n\t\tif ((vkfmt.optimalTilingFeatures & VK_FORMAT_FEATURE_BLIT_DST_BIT) && (vkfmt.optimalTilingFeatures & VK_FORMAT_FEATURE_TRANSFER_SRC_BIT_KHR))\n\t\t{\t//if we can do BGR, then use it, because that's what most PC file formats use, like tga.\n\t\t\t//we don't really want alpha data anyway.\n\t\t\tif (vid.flags & VID_SRGB_FB)\n\t\t\t\tici.format = VK_FORMAT_B8G8R8_SRGB;\n\t\t\telse\n\t\t\t\tici.format = VK_FORMAT_B8G8R8_UNORM;\n\t\t}\n\t\telse*/\n\t\t{\t//otherwise lets just get bgra data.\n\t\t\tif (vid.flags & VID_SRGB_FB)\n\t\t\t\tici.format = VK_FORMAT_B8G8R8A8_SRGB;\n\t\t\telse\n\t\t\t\tici.format = VK_FORMAT_B8G8R8A8_UNORM;\n\t\t}\n\t\tici.extent.width = vid.pixelwidth;\n\t\tici.extent.height = vid.pixelheight;\n\t\tici.extent.depth = 1;\n\t\tici.mipLevels = 1;\n\t\tici.arrayLayers = 1;\n\t\tici.samples = VK_SAMPLE_COUNT_1_BIT;\n\t\tici.tiling = VK_IMAGE_TILING_OPTIMAL;\n\t\tici.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT|VK_IMAGE_USAGE_TRANSFER_SRC_BIT;\n\t\tici.sharingMode = VK_SHARING_MODE_EXCLUSIVE;\n\t\tici.queueFamilyIndexCount = 0;\n\t\tici.pQueueFamilyIndices = NULL;\n\t\tici.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;\n\t\tVkAssert(vkCreateImage(vk.device, &ici, vkallocationcb, &tempimage));\n\t\tDebugSetName(VK_OBJECT_TYPE_IMAGE, (uint64_t)tempimage, \"VKVID_GetRGBInfo staging\");\n\t\tvkGetImageMemoryRequirements(vk.device, tempimage, &mem_reqs);\n\t\tmemAllocInfo.allocationSize = mem_reqs.size;\n\t\tmemAllocInfo.memoryTypeIndex = vk_find_memory_require(mem_reqs.memoryTypeBits, 0);\n\t\tVkAssert(vkAllocateMemory(vk.device, &memAllocInfo, vkallocationcb, &tempmemory));\n\t\tDebugSetName(VK_OBJECT_TYPE_DEVICE_MEMORY, (uint64_t)tempmemory, \"VKVID_GetRGBInfo staging\");\n\t\tVkAssert(vkBindImageMemory(vk.device, tempimage, tempmemory, 0));\n\n\t\tbci.flags = 0;\n\t\tbci.size = vid.pixelwidth*vid.pixelheight*4;\n\t\tbci.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT;\n\t\tbci.sharingMode = VK_SHARING_MODE_EXCLUSIVE;\n\t\tbci.queueFamilyIndexCount = 0;\n\t\tbci.pQueueFamilyIndices = NULL;\n\n\t\tVkAssert(vkCreateBuffer(vk.device, &bci, vkallocationcb, &tempbuffer));\n\t\tDebugSetName(VK_OBJECT_TYPE_BUFFER, (uint64_t)tempbuffer, \"VKVID_GetRGBInfo buffer\");\n\t\tvkGetBufferMemoryRequirements(vk.device, tempbuffer, &mem_reqs);\n\t\tmemAllocInfo.allocationSize = mem_reqs.size;\n\t\tmemAllocInfo.memoryTypeIndex = vk_find_memory_try(mem_reqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT|VK_MEMORY_PROPERTY_HOST_CACHED_BIT);\n\t\tif (memAllocInfo.memoryTypeIndex == ~0u)\n\t\t\tmemAllocInfo.memoryTypeIndex = vk_find_memory_require(mem_reqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);\n\t\tVkAssert(vkAllocateMemory(vk.device, &memAllocInfo, vkallocationcb, &tempbufmemory));\n\t\tDebugSetName(VK_OBJECT_TYPE_DEVICE_MEMORY, (uint64_t)tempbufmemory, \"VKVID_GetRGBInfo buffer\");\n\t\tVkAssert(vkBindBufferMemory(vk.device, tempbuffer, tempbufmemory, 0));\n\n\n\t\tset_image_layout(fence->cbuf, vk.frame->backbuf->colour.image, VK_IMAGE_ASPECT_COLOR_BIT,\n\t\t\t\tframebufferlayout, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,\n\t\t\t\tVK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_ACCESS_TRANSFER_READ_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT);\n\t\tset_image_layout(fence->cbuf, tempimage, VK_IMAGE_ASPECT_COLOR_BIT,\n\t\t\t\tVK_IMAGE_LAYOUT_UNDEFINED, 0, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,\n\t\t\t\tVK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_ACCESS_TRANSFER_WRITE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT);\n\t\t{\n\t\t\tVkImageBlit iblt;\n\t\t\tiblt.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;\n\t\t\tiblt.srcSubresource.mipLevel = 0;\n\t\t\tiblt.srcSubresource.baseArrayLayer = 0;\n\t\t\tiblt.srcSubresource.layerCount = 1;\n\t\t\tiblt.srcOffsets[0].x = 0;\n\t\t\tiblt.srcOffsets[0].y = 0;\n\t\t\tiblt.srcOffsets[0].z = 0;\n\t\t\tiblt.srcOffsets[1].x = vid.pixelwidth;\n\t\t\tiblt.srcOffsets[1].y = vid.pixelheight;\n\t\t\tiblt.srcOffsets[1].z = 1;\n\t\t\tiblt.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;\n\t\t\tiblt.dstSubresource.mipLevel = 0;\n\t\t\tiblt.dstSubresource.baseArrayLayer = 0;\n\t\t\tiblt.dstSubresource.layerCount = 1;\n\t\t\tiblt.dstOffsets[0].x = 0;\n\t\t\tiblt.dstOffsets[0].y = 0;\n\t\t\tiblt.dstOffsets[0].z = 0;\n\t\t\tiblt.dstOffsets[1].x = vid.pixelwidth;\n\t\t\tiblt.dstOffsets[1].y = vid.pixelheight;\n\t\t\tiblt.dstOffsets[1].z = 1;\n\n\t\t\tvkCmdBlitImage(fence->cbuf, vk.frame->backbuf->colour.image, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, tempimage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &iblt, VK_FILTER_LINEAR);\n\t\t}\n\t\tset_image_layout(fence->cbuf, vk.frame->backbuf->colour.image, VK_IMAGE_ASPECT_COLOR_BIT,\n\t\t\t\tVK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_ACCESS_TRANSFER_READ_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,\n\t\t\t\tframebufferlayout, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT);\n\t\tset_image_layout(fence->cbuf, tempimage, VK_IMAGE_ASPECT_COLOR_BIT,\n\t\t\t\tVK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_ACCESS_TRANSFER_WRITE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,\n\t\t\t\tVK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_ACCESS_TRANSFER_READ_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT);\n\n\t\t{\n\t\t\tVkBufferImageCopy icpy;\n\t\t\ticpy.bufferOffset = 0;\n\t\t\ticpy.bufferRowLength = 0;\t//packed\n\t\t\ticpy.bufferImageHeight = 0;\t//packed\n\t\t\ticpy.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;\n\t\t\ticpy.imageSubresource.mipLevel = 0;\n\t\t\ticpy.imageSubresource.baseArrayLayer = 0;\n\t\t\ticpy.imageSubresource.layerCount = 1;\n\t\t\ticpy.imageOffset.x = 0;\n\t\t\ticpy.imageOffset.y = 0;\n\t\t\ticpy.imageOffset.z = 0;\n\t\t\ticpy.imageExtent.width = ici.extent.width;\n\t\t\ticpy.imageExtent.height = ici.extent.height;\n\t\t\ticpy.imageExtent.depth = 1;\n\n\t\t\tvkCmdCopyImageToBuffer(fence->cbuf, tempimage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, tempbuffer, 1, &icpy);\n\t\t}\n\n\t\tVK_FencedSync(fence);\n\n\t\toutdata = BZ_Malloc(4*ici.extent.width*ici.extent.height);\n\t\tif (ici.format == VK_FORMAT_B8G8R8_SRGB || ici.format == VK_FORMAT_B8G8R8_UNORM)\n\t\t\t*fmt = PTI_BGR8;\n\t\telse if (ici.format == VK_FORMAT_R8G8B8_SRGB || ici.format == VK_FORMAT_R8G8B8_UNORM)\n\t\t\t*fmt = PTI_RGB8;\n\t\telse if (ici.format == VK_FORMAT_R8G8B8A8_SRGB || ici.format == VK_FORMAT_R8G8B8A8_UNORM)\n\t\t\t*fmt = PTI_RGBA8;\n\t\telse\n\t\t\t*fmt = PTI_BGRA8;\n\t\t*bytestride = ici.extent.width*4;\n\t\t*truevidwidth = ici.extent.width;\n\t\t*truevidheight = ici.extent.height;\n\n\t\tVkAssert(vkMapMemory(vk.device, tempbufmemory, 0, 4*ici.extent.width*ici.extent.height, 0, &imgdata));\n\t\tmemcpy(outdata, imgdata, 4*ici.extent.width*ici.extent.height);\n\t\tvkUnmapMemory(vk.device, tempbufmemory);\n\n\t\tvkDestroyImage(vk.device, tempimage, vkallocationcb);\n\t\tvkFreeMemory(vk.device, tempmemory, vkallocationcb);\n\n\t\tvkDestroyBuffer(vk.device, tempbuffer, vkallocationcb);\n\t\tvkFreeMemory(vk.device, tempbufmemory, vkallocationcb);\n\n\t\treturn outdata;\n\t}\n\treturn NULL;\n}\n\nstatic void VK_PaintScreen(void)\n{\n\tqboolean nohud;\n\tqboolean noworld;\n\n\t\n\tvid.fbvwidth = vid.width;\n\tvid.fbvheight = vid.height;\n\tvid.fbpwidth = vid.pixelwidth;\n\tvid.fbpheight = vid.pixelheight;\n\n\tr_refdef.pxrect.x = 0;\n\tr_refdef.pxrect.y = 0;\n\tr_refdef.pxrect.width = vid.fbpwidth;\n\tr_refdef.pxrect.height = vid.fbpheight;\n\tr_refdef.pxrect.maxheight = vid.pixelheight;\n\n\tvid.numpages = vk.backbuf_count + 1;\n\n\tR2D_Font_Changed();\n\n\tVK_Set2D ();\n\n\tShader_DoReload();\n\n\tif (scr_disabled_for_loading)\n\t{\n\t\textern float scr_disabled_time;\n\t\tif (Sys_DoubleTime() - scr_disabled_time > 60 || !Key_Dest_Has(~kdm_game))\n\t\t{\n\t\t\t//FIXME: instead of reenabling the screen, we should just draw the relevent things skipping only the game.\n\t\t\tscr_disabled_for_loading = false;\n\t\t}\n\t\telse\n\t\t{\n//\t\t\tscr_drawloading = true;\n\t\t\tSCR_DrawLoading (true);\n//\t\t\tscr_drawloading = false;\n\t\t\treturn;\n\t\t}\n\t}\n\n/*\tif (!scr_initialized || !con_initialized)\n\t{\n\t\tRSpeedEnd(RSPEED_TOTALREFRESH);\n\t\treturn;                         // not initialized yet\n\t}\n*/\n\n#ifdef TEXTEDITOR\n\tif (editormodal)\n\t{\n\t\tEditor_Draw();\n\t\tV_UpdatePalette (false);\n#if defined(_WIN32) && defined(GLQUAKE)\n\t\tMedia_RecordFrame();\n#endif\n\t\tR2D_BrightenScreen();\n\n\t\tif (key_dest_mask & kdm_console)\n\t\t\tCon_DrawConsole(vid.height/2, false);\n\t\telse\n\t\t\tCon_DrawConsole(0, false);\n//\t\tSCR_DrawCursor();\n\t\treturn;\n\t}\n#endif\n\n//\n// do 3D refresh drawing, and then update the screen\n//\n\tSCR_SetUpToDrawConsole ();\n\n\tnoworld = false;\n\tnohud = false;\n\n\tif (topmenu && topmenu->isopaque)\n\t\tnohud = true;\n#ifdef VM_CG\n\telse if (q3 && q3->cg.Redraw(cl.time))\n\t\tnohud = true;\n#endif\n#ifdef CSQC_DAT\n\telse if (CSQC_DrawView())\n\t\tnohud = true;\n#endif\n\telse\n\t{\n\t\tif (r_worldentity.model && cls.state == ca_active)\n\t\t\tV_RenderView (nohud);\n\t\telse\n\t\t{\n\t\t\tnoworld = true;\n\t\t}\n\t}\n\n\tscr_con_forcedraw = false;\n\tif (noworld)\n\t{\n\t\t//draw the levelshot or the conback fullscreen\n\t\tif (R2D_DrawLevelshot())\n\t\t\t;\n\t\telse if (scr_con_current != vid.height)\n\t\t{\n#ifdef HAVE_LEGACY\n\t\t\textern cvar_t dpcompat_console;\n\t\t\tif (dpcompat_console.ival)\n\t\t\t{\n\t\t\t\tR2D_ImageColours(0,0,0,1);\n\t\t\t\tR2D_FillBlock(0, 0, vid.width, vid.height);\n\t\t\t\tR2D_ImageColours(1,1,1,1);\n\t\t\t}\n\t\t\telse\n#endif\n\t\t\t\tR2D_ConsoleBackground(0, vid.height, true);\n\t\t}\n\t\telse\n\t\t\tscr_con_forcedraw = true;\n\n\t\tnohud = true;\n\t}\n\n\tr_refdef.playerview = &cl.playerview[0];\n\tif (!vrui.enabled)\n\t\tSCR_DrawTwoDimensional(nohud);\n\n\tV_UpdatePalette (false);\n\tR2D_BrightenScreen();\n\n#if defined(_WIN32) && defined(GLQUAKE)\n\tMedia_RecordFrame();\n#endif\n\n\tRSpeedShow();\n}\n\nVkCommandBuffer VK_AllocFrameCBuf(void)\n{\n\tstruct vkframe *frame = vk.frame;\n\tif (frame->numcbufs == frame->maxcbufs)\n\t{\n\t\tVkCommandBufferAllocateInfo cbai = {VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO};\n\n\t\tframe->maxcbufs++;\n\t\tframe->cbufs = BZ_Realloc(frame->cbufs, sizeof(*frame->cbufs)*frame->maxcbufs);\n\n\t\tcbai.commandPool = vk.cmdpool;\n\t\tcbai.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;\n\t\tcbai.commandBufferCount = frame->maxcbufs - frame->numcbufs;\n\t\tVkAssert(vkAllocateCommandBuffers(vk.device, &cbai, frame->cbufs+frame->numcbufs));\n\t\tDebugSetName(VK_OBJECT_TYPE_COMMAND_BUFFER, (uint64_t)frame->cbufs[frame->numcbufs], \"VK_AllocFrameCBuf\");\n\t}\n\treturn frame->cbufs[frame->numcbufs++];\n}\n\nqboolean VK_SCR_GrabBackBuffer(void)\n{\n\tVkSemaphore sem;\n\tRSpeedLocals();\n\n\tif (vk.frame)\t//erk, we already have one...\n\t\treturn true;\n\n\n\tRSpeedRemark();\n\n\tVK_FencedCheck();\n\n\tif (!vk.unusedframes)\n\t{\n\t\tstruct vkframe *newframe = Z_Malloc(sizeof(*vk.frame));\n\t\tVKBE_InitFramePools(newframe);\n\t\tnewframe->next = vk.unusedframes;\n\t\tvk.unusedframes = newframe;\n\t}\n\n\twhile (vk.acquirenext == vk.acquirelast)\n\t{\t//we're still waiting for the render thread to increment acquirelast.\n\t\t//shouldn't really happen, but can if the gpu is slow.\n\t\tif (vk.neednewswapchain)\n\t\t{\t//the render thread is is likely to have died... don't loop until infinity.\n#ifdef MULTITHREAD\n\t\t\tif (vk.submitthread)\n\t\t\t{\n\t\t\t\t//signal its condition, in case its sleeping, so we don't wait for infinity\n\t\t\t\tSys_LockConditional(vk.submitcondition);\n\t\t\t\tSys_ConditionSignal(vk.submitcondition);\n\t\t\t\tSys_UnlockConditional(vk.submitcondition);\n\n\t\t\t\t//now wait+clean up the thread\n\t\t\t\tSys_WaitOnThread(vk.submitthread);\n\t\t\t\tvk.submitthread = NULL;\n\t\t\t}\n#endif\n\t\t\treturn false;\n\t\t}\n\t\tSys_Sleep(0);\t//o.O\n#ifdef _WIN32\n\t\tSys_SendKeyEvents();\n#endif\n\t}\n\n\tif (vk.acquirefences[vk.acquirenext%ACQUIRELIMIT] != VK_NULL_HANDLE)\n\t{\n\t\t//wait for the queued acquire to actually finish\n\t\tif (vk_busywait.ival)\n\t\t{\t//busy wait, to try to get the highest fps possible\n\t\t\tfor (;;)\n\t\t\t{\n\t\t\t\tswitch(vkGetFenceStatus(vk.device, vk.acquirefences[vk.acquirenext%ACQUIRELIMIT]))\n\t\t\t\t{\n\t\t\t\tcase VK_SUCCESS:\n\t\t\t\t\tbreak;\t//hurrah\n\t\t\t\tcase VK_NOT_READY:\n\t\t\t\t\tcontinue;\t//keep going until its actually signaled. submission thread is probably just slow.\n\t\t\t\tcase VK_TIMEOUT:\n\t\t\t\t\tcontinue;\t//erk? this isn't a documented result here.\n\t\t\t\tcase VK_ERROR_DEVICE_LOST:\n\t\t\t\t\tSys_Error(\"Vulkan device lost\");\n\t\t\t\tdefault:\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\t//friendly wait\n\t\t\tint failures = 0;\n\t\t\tfor(;;)\n\t\t\t{\n\t\t\t\tVkResult err = vkWaitForFences(vk.device, 1, &vk.acquirefences[vk.acquirenext%ACQUIRELIMIT], VK_FALSE, 1000000000);\n\n\t\t\t\tif (err == VK_SUCCESS)\n\t\t\t\t\tbreak;\n\t\t\t\telse if (err == VK_TIMEOUT)\n\t\t\t\t{\n\t\t\t\t\tif (++failures == 5)\n\t\t\t\t\t\tSys_Error(\"waiting for fence for over 5 seconds. Assuming bug.\");\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\telse if (err == VK_ERROR_DEVICE_LOST)\n\t\t\t\t\tSys_Error(\"Vulkan device lost\");\n\t\t\t\telse if (err != VK_ERROR_OUT_OF_HOST_MEMORY && err != VK_ERROR_OUT_OF_DEVICE_MEMORY)\n\t\t\t\t\tSys_Error(\"vkWaitForFences returned unspecified result: %s\", VK_VKErrorToString(err));\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\tVkAssert(vkResetFences(vk.device, 1, &vk.acquirefences[vk.acquirenext%ACQUIRELIMIT]));\n\t}\n\tvk.bufferidx = vk.acquirebufferidx[vk.acquirenext%ACQUIRELIMIT];\n\n\tsem = vk.acquiresemaphores[vk.acquirenext%ACQUIRELIMIT];\n\tvk.acquirenext++;\n\n\t//grab the first unused\n\tSys_LockConditional(vk.submitcondition);\n\tvk.frame = vk.unusedframes;\n\tvk.unusedframes = vk.frame->next;\n\tvk.frame->next = NULL;\n\tSys_UnlockConditional(vk.submitcondition);\n\n\tVkAssert(vkResetFences(vk.device, 1, &vk.frame->finishedfence));\n\n\tvk.frame->backbuf = &vk.backbufs[vk.bufferidx];\n\tvk.rendertarg = vk.frame->backbuf;\n\n\tvk.frame->numcbufs = 0;\n\tvk.rendertarg->cbuf = VK_AllocFrameCBuf();\n\tvk.frame->acquiresemaphore = sem;\n\n\tRSpeedEnd(RSPEED_SETUP);\n\n\n\n\n\n\t\n\t{\n\t\tVkCommandBufferBeginInfo begininf = {VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO};\n\t\tVkCommandBufferInheritanceInfo inh = {VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO};\n\t\tbegininf.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;\n\t\tbegininf.pInheritanceInfo = &inh;\n\t\tinh.renderPass = VK_NULL_HANDLE;\t//unused\n\t\tinh.subpass = 0;\t\t\t\t\t//unused\n\t\tinh.framebuffer = VK_NULL_HANDLE;\t//unused\n\t\tinh.occlusionQueryEnable = VK_FALSE;\n\t\tinh.queryFlags = 0;\n\t\tinh.pipelineStatistics = 0;\n\t\tvkBeginCommandBuffer(vk.rendertarg->cbuf, &begininf);\n\t}\n\n//\tVK_DebugFramerate();\n\n//\tvkCmdWriteTimestamp(vk.frame->cbuf, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, querypool, vk.bufferidx*2+0);\n\n\tif (!(vk.rendertarg->rpassflags & RP_PRESENTABLE))\n\t{\n\t\tVkImageMemoryBarrier imgbarrier = {VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER};\n\t\timgbarrier.pNext = NULL;\n\t\timgbarrier.srcAccessMask = 0;//VK_ACCESS_MEMORY_READ_BIT;\n\t\timgbarrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;\n\t\timgbarrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;//vk.rendertarg->colour.layout;\t//'Alternately, oldLayout can be VK_IMAGE_LAYOUT_UNDEFINED, if the image's contents need not be preserved.'\n\t\timgbarrier.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;\n\t\timgbarrier.image = vk.frame->backbuf->colour.image;\n\t\timgbarrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;\n\t\timgbarrier.subresourceRange.baseMipLevel = 0;\n\t\timgbarrier.subresourceRange.levelCount = 1;\n\t\timgbarrier.subresourceRange.baseArrayLayer = 0;\n\t\timgbarrier.subresourceRange.layerCount = 1;\n\t\timgbarrier.srcQueueFamilyIndex = vk.queuefam[1];\n\t\timgbarrier.dstQueueFamilyIndex = vk.queuefam[0];\n\t\tif (vk.frame->backbuf->firstuse)\n\t\t{\n\t\t\timgbarrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;\n\t\t\timgbarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;\n\t\t\timgbarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;\n\t\t}\n\t\tvk.rendertarg->colour.layout = imgbarrier.newLayout;\n\t\tvkCmdPipelineBarrier(vk.rendertarg->cbuf, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, 0, 0, NULL, 0, NULL, 1, &imgbarrier);\n\t}\n\t{\n\t\tVkImageMemoryBarrier imgbarrier = {VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER};\n\t\timgbarrier.pNext = NULL;\n\t\timgbarrier.srcAccessMask = 0;\n\t\timgbarrier.dstAccessMask = 0;//VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;\n\t\timgbarrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;\n\t\timgbarrier.newLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;\n\t\timgbarrier.image = vk.frame->backbuf->depth.image;\n\t\timgbarrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;\n\t\timgbarrier.subresourceRange.baseMipLevel = 0;\n\t\timgbarrier.subresourceRange.levelCount = 1;\n\t\timgbarrier.subresourceRange.baseArrayLayer = 0;\n\t\timgbarrier.subresourceRange.layerCount = 1;\n\t\timgbarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;\n\t\timgbarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;\n\t\tvkCmdPipelineBarrier(vk.rendertarg->cbuf, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, 0, 0, NULL, 0, NULL, 1, &imgbarrier);\n\t}\n\tVKBE_RestartFrame();\n\n\t{\n\t\tint rp = vk.frame->backbuf->rpassflags;\n\t\tVkClearValue\tclearvalues[3];\n\t\textern cvar_t r_clear;\n\t\tVkRenderPassBeginInfo rpbi = {VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO};\n\n\t\t//attachments are: screen[1], depth[msbits], (screen[msbits])\n\n\t\tclearvalues[0].color.float32[0] = !!(r_clear.ival & 1);\n\t\tclearvalues[0].color.float32[1] = !!(r_clear.ival & 2);\n\t\tclearvalues[0].color.float32[2] = !!(r_clear.ival & 4);\n\t\tclearvalues[0].color.float32[3] = 1;\n\t\tclearvalues[1].depthStencil.depth = 1.0;\n\t\tclearvalues[1].depthStencil.stencil = 0;\n\n\t\tif (rp & RP_MULTISAMPLE)\n\t\t{\n\t\t\tclearvalues[2].color.float32[0] = !!(r_clear.ival & 1);\n\t\t\tclearvalues[2].color.float32[1] = !!(r_clear.ival & 2);\n\t\t\tclearvalues[2].color.float32[2] = !!(r_clear.ival & 4);\n\t\t\tclearvalues[2].color.float32[3] = 1;\n\t\t\trpbi.clearValueCount = 3;\n\t\t}\n\t\telse\n\t\t\trpbi.clearValueCount = 2;\n\n\t\tif (r_clear.ival || vk.frame->backbuf->firstuse)\n\t\t\trpbi.renderPass = VK_GetRenderPass(RP_FULLCLEAR|rp);\n\t\telse\n\t\t\trpbi.renderPass = VK_GetRenderPass(RP_DEPTHCLEAR|rp);\n\t\trpbi.framebuffer = vk.frame->backbuf->framebuffer;\n\t\trpbi.renderArea.offset.x = 0;\n\t\trpbi.renderArea.offset.y = 0;\n\t\trpbi.renderArea.extent.width = vk.frame->backbuf->colour.width;\n\t\trpbi.renderArea.extent.height = vk.frame->backbuf->colour.height;\n\t\trpbi.pClearValues = clearvalues;\n\t\tvkCmdBeginRenderPass(vk.rendertarg->cbuf, &rpbi, VK_SUBPASS_CONTENTS_INLINE);\n\n\t\tvk.frame->backbuf->width = rpbi.renderArea.extent.width;\n\t\tvk.frame->backbuf->height = rpbi.renderArea.extent.height;\n\n\t\trpbi.clearValueCount = 0;\n\t\trpbi.pClearValues = NULL;\n\t\trpbi.renderPass = VK_GetRenderPass(RP_RESUME|rp);\n\t\tvk.rendertarg->restartinfo = rpbi;\n\t\tvk.rendertarg->depthcleared = true;\n\t}\n\tvk.frame->backbuf->firstuse = false;\n\treturn true;\n}\n\nstruct vk_presented\n{\n\tstruct vk_fencework fw;\n\tstruct vkframe *frame;\n};\nvoid VK_Presented(void *fw)\n{\n\tstruct vk_presented *pres = fw;\n\tstruct vkframe *frame = pres->frame;\n\tpres->fw.fence = VK_NULL_HANDLE;\t//don't allow that to be freed.\n\n\twhile(frame->frameendjobs)\n\t{\n\t\tstruct vk_frameend *job = frame->frameendjobs;\n\t\tframe->frameendjobs = job->next;\n\t\tjob->FrameEnded(job+1);\n\t\tZ_Free(job);\n\t}\n\n\tframe->next = vk.unusedframes;\n\tvk.unusedframes = frame;\n}\n\n#if 0\nvoid VK_DebugFramerate(void)\n{\n\tstatic double lastupdatetime;\n\tstatic double lastsystemtime;\n\tdouble t;\n\textern int fps_count;\n\tfloat lastfps;\n\n\tfloat frametime;\n\n\tt = Sys_DoubleTime();\n\tif ((t - lastupdatetime) >= 1.0)\n\t{\n\t\tlastfps = fps_count/(t - lastupdatetime);\n\t\tfps_count = 0;\n\t\tlastupdatetime = t;\n\n\t\tOutputDebugStringA(va(\"%g fps\\n\", lastfps));\n\t}\n\tframetime = t - lastsystemtime;\n\tlastsystemtime = t;\n}\n#endif\n\nqboolean\tVK_SCR_UpdateScreen\t\t\t(void)\n{\n\tVkImageLayout fblayout;\n\n\tVK_FencedCheck();\n\n\t//a few cvars need some extra work if they're changed\n\tif ((vk.allowsubmissionthread && vk_submissionthread.modified) || vid_vsync.modified || vk_waitfence.modified || vid_triplebuffer.modified || vid_srgb.modified || vid_multisample.modified)\n\t\tvk.neednewswapchain = true;\n\n\tif (vk.devicelost)\n\t{\t//vkQueueSubmit returning vk_error_device_lost means we give up and try resetting everything.\n\t\t//if someone's installing new drivers then wait a little time before reloading everything, in the hope that any other dependant files got copied. or something.\n\t\t//fixme: don't allow this to be spammed...\n\t\tSys_Sleep(5);\n\t\tCon_Printf(\"Device was lost. Restarting video\\n\");\n\t\tCmd_ExecuteString(\"vid_restart\", RESTRICT_LOCAL);\n\t\treturn false;\n\t}\n\n\tif (vk.neednewswapchain && !vk.frame)\n\t{\n#ifdef MULTITHREAD\n\t\t//kill the thread\n\t\tif (vk.submitthread)\n\t\t{\n\t\t\tSys_LockConditional(vk.submitcondition);\t//annoying, but required for it to be reliable with respect to other things.\n\t\t\tSys_ConditionSignal(vk.submitcondition);\n\t\t\tSys_UnlockConditional(vk.submitcondition);\n\t\t\tSys_WaitOnThread(vk.submitthread);\n\t\t\tvk.submitthread = NULL;\n\t\t}\n#endif\n\t\t//make sure any work is actually done BEFORE the swapchain gets destroyed\n\t\twhile (vk.work)\n\t\t{\n\t\t\tSys_LockConditional(vk.submitcondition);\n\t\t\tVK_Submit_DoWork();\n\t\t\tSys_UnlockConditional(vk.submitcondition);\n\t\t}\n\t\tif (vk.dopresent)\n\t\t\tvk.dopresent(NULL);\n\t\tvkDeviceWaitIdle(vk.device);\n\t\tif (!VK_CreateSwapChain())\n\t\t\treturn false;\n\t\tvk.neednewswapchain = false;\n\n#ifdef MULTITHREAD\n\t\tif (vk.allowsubmissionthread && (vk_submissionthread.ival || !*vk_submissionthread.string))\n\t\t{\n\t\t\tvk.submitthread = Sys_CreateThread(\"vksubmission\", VK_Submit_Thread, NULL, THREADP_HIGHEST, 0);\n\t\t}\n#endif\n\t}\n\n\tif (!VK_SCR_GrabBackBuffer())\n\t\treturn false;\n\n\tVKBE_Set2D(true);\n\tVKBE_SelectDLight(NULL, vec3_origin, NULL, 0);\n\n\tVK_PaintScreen();\n\n\tif (R2D_Flush)\n\t\tR2D_Flush();\n\n\tvkCmdEndRenderPass(vk.rendertarg->cbuf);\n\n\tfblayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;\n\t/*if (0)\n\t{\n\t\tvkscreencapture_t *capt = VK_AtFrameEnd(atframeend, sizeof(vkscreencapture_t));\n\t\tVkImageMemoryBarrier imgbarrier = {VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER};\n\t\tVkBufferImageCopy region;\n\t\timgbarrier.pNext = NULL;\n\t\timgbarrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;\n\t\timgbarrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;\n\t\timgbarrier.oldLayout = fblayout;\n\t\timgbarrier.newLayout = fblayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;\n\t\timgbarrier.image = vk.frame->backbuf->colour.image;\n\t\timgbarrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;\n\t\timgbarrier.subresourceRange.baseMipLevel = 0;\n\t\timgbarrier.subresourceRange.levelCount = 1;\n\t\timgbarrier.subresourceRange.baseArrayLayer = 0;\n\t\timgbarrier.subresourceRange.layerCount = 1;\n\t\timgbarrier.srcQueueFamilyIndex = vk.queuefam[0];\n\t\timgbarrier.dstQueueFamilyIndex = vk.queuefam[0];\n\t\tvkCmdPipelineBarrier(vk.frame->cbuf, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, 0, 0, NULL, 0, NULL, 1, &imgbarrier);\n\n\t\tregion.bufferOffset = 0;\n\t\tregion.bufferRowLength = 0;\t\t//tightly packed\n\t\tregion.bufferImageHeight = 0;\t//tightly packed\n\t\tregion.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;\n\t\tregion.imageSubresource.mipLevel = 0;\n\t\tregion.imageSubresource.baseArrayLayer = 0;\n\t\tregion.imageSubresource.layerCount = 1;\n\t\tregion.imageOffset.x = 0;\n\t\tregion.imageOffset.y = 0;\n\t\tregion.imageOffset.z = 0;\n\t\tregion.imageExtent.width = capt->imagewidth = vk.frame->backbuf->colour.width;\n\t\tregion.imageExtent.height = capt->imageheight = vk.frame->backbuf->colour.height;\n\t\tregion.imageExtent.depth = 1;\n\t\tvkCmdCopyImageToBuffer(vk.frame->cbuf, vk.frame->backbuf->colour.image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, buffer, 1, &region);\n\t}*/\n\n\tif (!(vk.frame->backbuf->rpassflags & RP_PRESENTABLE))\n\t{\n\t\tVkImageMemoryBarrier imgbarrier = {VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER};\n\t\timgbarrier.pNext = NULL;\n\t\timgbarrier.srcAccessMask = /*VK_ACCESS_TRANSFER_READ_BIT|*/VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;\n\t\timgbarrier.dstAccessMask = 0;\n\t\timgbarrier.oldLayout = fblayout;\n\t\timgbarrier.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;\n\t\timgbarrier.image = vk.frame->backbuf->colour.image;\n\t\timgbarrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;\n\t\timgbarrier.subresourceRange.baseMipLevel = 0;\n\t\timgbarrier.subresourceRange.levelCount = 1;\n\t\timgbarrier.subresourceRange.baseArrayLayer = 0;\n\t\timgbarrier.subresourceRange.layerCount = 1;\n\t\timgbarrier.srcQueueFamilyIndex = vk.queuefam[0];\n\t\timgbarrier.dstQueueFamilyIndex = vk.queuefam[1];\n\t\tvkCmdPipelineBarrier(vk.rendertarg->cbuf,  VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, NULL, 0, NULL, 1, &imgbarrier);\n\t\tvk.rendertarg->colour.layout = imgbarrier.newLayout;\n\t}\n\n//\tvkCmdWriteTimestamp(vk.rendertarg->cbuf, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, querypool, vk.bufferidx*2+1);\n\tvkEndCommandBuffer(vk.rendertarg->cbuf);\n\n\tVKBE_FlushDynamicBuffers();\n\n\t{\n\t\tstruct vk_presented *fw = Z_Malloc(sizeof(*fw));\n\t\tfw->fw.Passed = VK_Presented;\n\t\tfw->fw.fence = vk.frame->finishedfence;\n\t\tfw->frame = vk.frame;\n\t\t//hand over any post-frame jobs to the frame in question.\n\t\tvk.frame->frameendjobs = vk.frameendjobs;\n\t\tvk.frameendjobs = NULL;\n\n\t\tVK_Submit_Work(vk.rendertarg->cbuf, vk.frame->acquiresemaphore, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, vk.frame->backbuf->presentsemaphore, vk.frame->finishedfence, vk.frame, &fw->fw);\n\t}\n\n\t//now would be a good time to do any compute work or lightmap updates...\n\n\tvk.frame = NULL;\n\n\tVK_FencedCheck();\n\n\tVID_SwapBuffers();\n\n#ifdef TEXTEDITOR\n\tif (editormodal)\n\t{\t//FIXME\n\t\tVK_SCR_GrabBackBuffer();\n\t}\n#endif\n\treturn true;\n}\n\nvoid\tVKBE_RenderToTextureUpdate2d(qboolean destchanged)\n{\n}\n\nstatic void VK_DestroyRenderPasses(void)\n{\n\tint i;\n\tfor (i = 0; i < countof(vk.renderpass); i++)\n\t{\n\t\tif (vk.renderpass[i] != VK_NULL_HANDLE)\n\t\t{\n\t\t\tvkDestroyRenderPass(vk.device, vk.renderpass[i], vkallocationcb);\n\t\t\tvk.renderpass[i] = VK_NULL_HANDLE;\n\t\t}\n\t}\n}\nVkRenderPass VK_GetRenderPass(int pass)\n{\n\tint numattachments;\n\tstatic\tVkAttachmentReference color_reference;\n\tstatic\tVkAttachmentReference depth_reference;\n\tstatic\tVkAttachmentReference resolve_reference;\n\tstatic\tVkAttachmentDescription attachments[3] = {{0}};\n\tstatic\tVkSubpassDescription subpass = {0};\n\tstatic \tVkRenderPassCreateInfo rp_info = {VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO};\n\n//two render passes are compatible for piplines when they match exactly except for:\n//initial and final layouts in attachment descriptions.\n//load and store operations in attachment descriptions.\n//image layouts in attachment references.\n\n\tif (vk.multisamplebits == VK_SAMPLE_COUNT_1_BIT)\n\t\tpass &= ~RP_MULTISAMPLE;\t//no difference\n\n\tif (vk.renderpass[pass] != VK_NULL_HANDLE)\n\t\treturn vk.renderpass[pass];\t//already built\n\n\tnumattachments = 0;\n\tif ((pass&3)==RP_DEPTHONLY)\n\t\tcolor_reference.attachment = ~(uint32_t)0;\t//no colour buffer...\n\telse\n\t\tcolor_reference.attachment = numattachments++;\n\tdepth_reference.attachment = numattachments++;\n\tresolve_reference.attachment = ~(uint32_t)0;\n\tif ((pass & RP_MULTISAMPLE) && color_reference.attachment != ~(uint32_t)0)\n\t{\t//if we're using multisample, then render to a third texture, with a resolve to the original colour texture.\n\t\tresolve_reference.attachment = color_reference.attachment;\n\t\tcolor_reference.attachment = numattachments++;\n\t}\n\n\tcolor_reference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;\n\tdepth_reference.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;\n\tresolve_reference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;\n\n\tif (color_reference.attachment != ~(uint32_t)0)\n\t{\n\t\tif (pass&RP_FP16)\n\t\t\tattachments[color_reference.attachment].format = VK_FORMAT_R16G16B16A16_SFLOAT;\n\t\telse if (pass&RP_VR)\n\t\t\tattachments[color_reference.attachment].format = vk.backbufformat;\t//FIXME\n\t\telse\n\t\t\tattachments[color_reference.attachment].format = vk.backbufformat;\n\t\tattachments[color_reference.attachment].samples = (pass & RP_MULTISAMPLE)?vk.multisamplebits:VK_SAMPLE_COUNT_1_BIT;\n//\t\tattachments[color_reference.attachment].loadOp = pass?VK_ATTACHMENT_LOAD_OP_LOAD:VK_ATTACHMENT_LOAD_OP_DONT_CARE;\n\t\tattachments[color_reference.attachment].storeOp = VK_ATTACHMENT_STORE_OP_STORE;\n\t\tattachments[color_reference.attachment].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;\n\t\tattachments[color_reference.attachment].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;\n\t\tattachments[color_reference.attachment].initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;\n\t\tattachments[color_reference.attachment].finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;\n\t}\n\n\tif (depth_reference.attachment != ~(uint32_t)0)\n\t{\n\t\tattachments[depth_reference.attachment].format = vk.depthformat;\n\t\tattachments[depth_reference.attachment].samples = (pass & RP_MULTISAMPLE)?vk.multisamplebits:VK_SAMPLE_COUNT_1_BIT;\n//\t\tattachments[depth_reference.attachment].loadOp = pass?VK_ATTACHMENT_LOAD_OP_LOAD:VK_ATTACHMENT_LOAD_OP_CLEAR;\n\t\tattachments[depth_reference.attachment].storeOp = VK_ATTACHMENT_STORE_OP_STORE;//VK_ATTACHMENT_STORE_OP_DONT_CARE;\n\t\tattachments[depth_reference.attachment].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;\n\t\tattachments[depth_reference.attachment].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;\n\t\tattachments[depth_reference.attachment].initialLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;\n\t\tattachments[depth_reference.attachment].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;\n\t}\n\n\tif (resolve_reference.attachment != ~(uint32_t)0)\n\t{\n\t\tattachments[resolve_reference.attachment].format = vk.backbufformat;\n\t\tattachments[resolve_reference.attachment].samples = VK_SAMPLE_COUNT_1_BIT;\n\t\tattachments[resolve_reference.attachment].loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;\n\t\tattachments[resolve_reference.attachment].storeOp = VK_ATTACHMENT_STORE_OP_STORE;\n\t\tattachments[resolve_reference.attachment].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;\n\t\tattachments[resolve_reference.attachment].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;\n\t\tattachments[resolve_reference.attachment].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;\n\t\tattachments[resolve_reference.attachment].finalLayout = (pass&RP_PRESENTABLE)?VK_IMAGE_LAYOUT_PRESENT_SRC_KHR:VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;\n\t}\n\n\tsubpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;\n\tsubpass.flags = 0;\n\tsubpass.inputAttachmentCount = 0;\n\tsubpass.pInputAttachments = NULL;\n\tsubpass.colorAttachmentCount = 1;\n\tsubpass.pColorAttachments = &color_reference;\n\tsubpass.pResolveAttachments = (resolve_reference.attachment != ~(uint32_t)0)?&resolve_reference:NULL;\n\tsubpass.pDepthStencilAttachment = &depth_reference;\n\tsubpass.preserveAttachmentCount = 0;\n\tsubpass.pPreserveAttachments = NULL;\n\n\trp_info.attachmentCount = numattachments;\n\trp_info.pAttachments = attachments;\n\trp_info.subpassCount = 1;\n\trp_info.pSubpasses = &subpass;\n\trp_info.dependencyCount = 0;\n\trp_info.pDependencies = NULL;\n\n\tswitch(pass&3)\n\t{\n\tcase RP_RESUME:\n\t\t//nothing cleared, both are just re-loaded.\n\t\tattachments[color_reference.attachment].loadOp = VK_ATTACHMENT_LOAD_OP_LOAD;\n\t\tattachments[depth_reference.attachment].loadOp = VK_ATTACHMENT_LOAD_OP_LOAD;\n\t\tbreak;\n\tcase RP_DEPTHCLEAR:\n\t\t//depth cleared, colour is whatever.\n\t\tattachments[depth_reference.attachment].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;\n\t\tattachments[color_reference.attachment].loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;\n\t\tattachments[depth_reference.attachment].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;\n\t\tbreak;\n\tcase RP_FULLCLEAR:\n\t\t//both cleared\n\t\tattachments[color_reference.attachment].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;\n\t\tattachments[depth_reference.attachment].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;\n\t\tattachments[color_reference.attachment].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;\n\t\tattachments[depth_reference.attachment].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;\n\t\tbreak;\n\tcase RP_DEPTHONLY:\n\t\tattachments[depth_reference.attachment].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;\n//\t\tattachments[color_reference.attachment].loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;\n\t\tattachments[depth_reference.attachment].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;\n\n\t\tattachments[depth_reference.attachment].finalLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;\n\t}\n\n\tVkAssert(vkCreateRenderPass(vk.device, &rp_info, vkallocationcb, &vk.renderpass[pass]));\n\tDebugSetName(VK_OBJECT_TYPE_RENDER_PASS, (uint64_t)vk.renderpass[pass], va(\"RP%i\", pass));\n\treturn vk.renderpass[pass];\n}\n\nvoid VK_DoPresent(struct vkframe *theframe)\n{\n\tVkResult err;\n\tuint32_t framenum;\n\tVkPresentInfoKHR presinfo = {VK_STRUCTURE_TYPE_PRESENT_INFO_KHR};\n\tif (!theframe)\n\t\treturn;\t//used to ensure that the queue is flushed at shutdown\n\tframenum = theframe->backbuf - vk.backbufs;\n\tpresinfo.waitSemaphoreCount = 1;\n\tpresinfo.pWaitSemaphores = &theframe->backbuf->presentsemaphore;\n\tpresinfo.swapchainCount = 1;\n\tpresinfo.pSwapchains = &vk.swapchain;\n\tpresinfo.pImageIndices = &framenum;\n\n\t{\n\t\tRSpeedMark();\n\t\terr = vkQueuePresentKHR(vk.queue_present, &presinfo);\n\t\tRSpeedEnd(RSPEED_PRESENT);\n\t}\n\t{\n\t\tRSpeedMark();\n\t\tif (err)\n\t\t{\n\t\t\tif (err == VK_SUBOPTIMAL_KHR)\n\t\t\t\tCon_DPrintf(\"vkQueuePresentKHR: VK_SUBOPTIMAL_KHR\\n\");\n\t\t\telse if (err == VK_ERROR_OUT_OF_DATE_KHR)\n\t\t\t\tCon_DPrintf(\"vkQueuePresentKHR: VK_ERROR_OUT_OF_DATE_KHR\\n\");\n\t\t\telse\n\t\t\t\tCon_Printf(\"ERROR: vkQueuePresentKHR: %i\\n\", err);\n\t\t\tvk.neednewswapchain = true;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tint r = vk.acquirelast%ACQUIRELIMIT;\n\t\t\tuint64_t timeout = (vk.acquirelast==vk.acquirenext)?UINT64_MAX:0;\t//\n\t\t\terr = vkAcquireNextImageKHR(vk.device, vk.swapchain, timeout, vk.acquiresemaphores[r], vk.acquirefences[r], &vk.acquirebufferidx[r]);\n\t\t\tswitch(err)\n\t\t\t{\n\t\t\tcase VK_SUBOPTIMAL_KHR:\t//success, but with a warning.\n\t\t\t\tvk.neednewswapchain = true;\n\t\t\t\tvk.acquirelast++;\n\t\t\t\tbreak;\n\t\t\tcase VK_SUCCESS:\t//success\n\t\t\t\tvk.acquirelast++;\n\t\t\t\tbreak;\n\n\t\t\t//we gave the presentation engine an image, but its refusing to give us one back.\n\t\t\t//logically this means the implementation lied about its VkSurfaceCapabilitiesKHR::minImageCount\n\t\t\tcase VK_TIMEOUT:\t//'success', yet still no result\n\t\t\tcase VK_NOT_READY:\n\t\t\t\t//no idea how to handle. let it slip?\n\t\t\t\tif (vk.acquirelast == vk.acquirenext)\n\t\t\t\t\tvk.neednewswapchain = true;\t//slipped too much\n\t\t\t\tbreak;\n\n\t\t\tcase VK_ERROR_OUT_OF_DATE_KHR:\n\t\t\t\t//unable to present, but we at least don't need to throw everything away.\n\t\t\t\tvk.neednewswapchain = true;\n\t\t\t\tbreak;\n\t\t\tcase VK_ERROR_DEVICE_LOST:\n\t\t\tcase VK_ERROR_OUT_OF_HOST_MEMORY:\n\t\t\tcase VK_ERROR_OUT_OF_DEVICE_MEMORY:\n\t\t\tcase VK_ERROR_SURFACE_LOST_KHR:\n\t\t\t\t//something really bad happened.\n\t\t\t\tCon_Printf(\"ERROR: vkAcquireNextImageKHR: %s\\n\", VK_VKErrorToString(err));\n\t\t\t\tvk.neednewswapchain = true;\n\t\t\t\tvk.devicelost = true;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t//case VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT:\n\t\t\t\t//we don't know why we're getting this. vendor problem.\n\t\t\t\tCon_Printf(\"ERROR: vkAcquireNextImageKHR: undocumented/extended %s\\n\", VK_VKErrorToString(err));\n\t\t\t\tvk.neednewswapchain = true;\n\t\t\t\tvk.devicelost = true;\t//this might be an infinite loop... no idea how to handle it.\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tRSpeedEnd(RSPEED_ACQUIRE);\n\t}\n}\n\nstatic void VK_Submit_DoWork(void)\n{\n\tVkCommandBuffer cbuf[64];\n\tVkSemaphore wsem[64];\n\tVkPipelineStageFlags wsemstageflags[64];\n\tVkSemaphore ssem[64];\n\n\tVkQueue\tsubqueue = NULL;\n\tVkSubmitInfo subinfo[64];\n\tunsigned int subcount = 0;\n\tstruct vkwork_s *work;\n\tstruct vkframe *present = NULL;\n\tVkFence waitfence = VK_NULL_HANDLE;\n\tVkResult err;\n\tstruct vk_fencework *fencedwork = NULL;\n\tqboolean errored = false;\n\n\twhile(vk.work && !present && !waitfence && !fencedwork && subcount < countof(subinfo))\n\t{\n\t\twork = vk.work;\n\t\tif (subcount && subqueue != work->queue)\n\t\t\tbreak;\n\t\tsubinfo[subcount].sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;\n\t\tsubinfo[subcount].pNext = NULL;\n\t\tsubinfo[subcount].waitSemaphoreCount = work->semwait?1:0;\n\t\tsubinfo[subcount].pWaitSemaphores = &wsem[subcount];\n\t\twsem[subcount] = work->semwait;\n\t\tsubinfo[subcount].pWaitDstStageMask = &wsemstageflags[subcount];\n\t\twsemstageflags[subcount] = work->semwaitstagemask;\n\t\tsubinfo[subcount].commandBufferCount = work->cmdbuf?1:0;\n\t\tsubinfo[subcount].pCommandBuffers = &cbuf[subcount];\n\t\tcbuf[subcount] = work->cmdbuf;\n\t\tsubinfo[subcount].signalSemaphoreCount = work->semsignal?1:0;\n\t\tsubinfo[subcount].pSignalSemaphores = &ssem[subcount];\n\t\tssem[subcount] = work->semsignal;\n\t\twaitfence = work->fencesignal;\n\t\tfencedwork = work->fencedwork;\n\t\tsubqueue = work->queue;\n\n\t\tsubcount++;\n\n\t\tpresent = work->present;\n\n\t\tvk.work = work->next;\n\t\tZ_Free(work);\n\t}\n\n\tSys_UnlockConditional(vk.submitcondition);\t//don't block people giving us work while we're occupied\n\tif (subcount || waitfence)\n\t{\n\t\tRSpeedMark();\n\t\terr = vkQueueSubmit(subqueue, subcount, subinfo, waitfence);\n\t\tif (err)\n\t\t{\n\t\t\tif (!vk.devicelost)\n\t\t\t\tCon_Printf(CON_ERROR \"ERROR: vkQueueSubmit: %s\\n\", VK_VKErrorToString(err));\n\t\t\terrored = vk.neednewswapchain = true;\n\t\t\tvk.devicelost |= (err==VK_ERROR_DEVICE_LOST);\n\t\t}\n\t\tRSpeedEnd(RSPEED_SUBMIT);\n\t}\n\n\tif (present && !errored)\n\t{\n\t\tvk.dopresent(present);\n\t}\n\t\n\tSys_LockConditional(vk.submitcondition);\n\n\tif (fencedwork)\n\t{\t//this is used for loading and cleaning up things after the gpu has consumed it.\n\t\tif (vk.fencework_last)\n\t\t{\n\t\t\tvk.fencework_last->next = fencedwork;\n\t\t\tvk.fencework_last = fencedwork;\n\t\t}\n\t\telse\n\t\t\tvk.fencework_last = vk.fencework = fencedwork;\n\t}\n}\n\n#ifdef MULTITHREAD\n//oh look. a thread.\n//nvidia's drivers seem to like doing a lot of blocking in queuesubmit and queuepresent(despite the whole QUEUE thing).\n//so thread this work so the main thread doesn't have to block so much.\nint VK_Submit_Thread(void *arg)\n{\n\tSys_LockConditional(vk.submitcondition);\n\twhile(!vk.neednewswapchain)\n\t{\n\t\tif (!vk.work)\n\t\t\tSys_ConditionWait(vk.submitcondition);\n\n\t\tVK_Submit_DoWork();\n\t}\n\tSys_UnlockConditional(vk.submitcondition);\n\treturn true;\n}\n#endif\n\nvoid VK_Submit_Work(VkCommandBuffer cmdbuf, VkSemaphore semwait, VkPipelineStageFlags semwaitstagemask, VkSemaphore semsignal, VkFence fencesignal, struct vkframe *presentframe, struct vk_fencework *fencedwork)\n{\n\tstruct vkwork_s *work = Z_Malloc(sizeof(*work));\n\tstruct vkwork_s **link;\n\n\twork->queue = vk.queue_render;\n\twork->cmdbuf = cmdbuf;\n\twork->semwait = semwait;\n\twork->semwaitstagemask = semwaitstagemask;\n\twork->semsignal = semsignal;\n\twork->fencesignal = fencesignal;\n\twork->present = presentframe;\n\twork->fencedwork = fencedwork;\n\n\tSys_LockConditional(vk.submitcondition);\n\n#ifdef MULTITHREAD\n\tif (vk.neednewswapchain && vk.submitthread)\n\t{\t//if we're trying to kill the submission thread, don't post work to it - instead wait for it to die cleanly then do it ourselves.\n\t\tSys_ConditionSignal(vk.submitcondition);\n\t\tSys_UnlockConditional(vk.submitcondition);\n\t\tSys_WaitOnThread(vk.submitthread);\n\t\tvk.submitthread = NULL;\n\t\tSys_LockConditional(vk.submitcondition);\t//annoying, but required for it to be reliable with respect to other things.\n\t}\n#endif\n\n\t//add it on the end in a lazy way.\n\tfor (link = &vk.work; *link; link = &(*link)->next)\n\t\t;\n\t*link = work;\n\n#ifdef MULTITHREAD\n\tif (vk.submitthread)\n\t\tSys_ConditionSignal(vk.submitcondition);\n\telse\n#endif\n\t\tVK_Submit_DoWork();\n\tSys_UnlockConditional(vk.submitcondition);\n}\n\nvoid VK_Submit_Sync(void)\n{\n\tSys_LockConditional(vk.submitcondition);\n\t//FIXME: \n\tvkDeviceWaitIdle(vk.device); //just in case\n\tSys_UnlockConditional(vk.submitcondition);\n}\n\nvoid VK_CheckTextureFormats(void)\n{\n\tstruct {\n\t\tunsigned int pti;\n\t\tVkFormat vulkan;\n\t\tunsigned int needextra;\n\t} texfmt[] =\n\t{\n\t\t{PTI_RGBA8,\t\t\t\tVK_FORMAT_R8G8B8A8_UNORM,\t\t\tVK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT},\n\t\t{PTI_RGBX8,\t\t\t\tVK_FORMAT_R8G8B8A8_UNORM,\t\t\tVK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT},\n\t\t{PTI_BGRA8,\t\t\t\tVK_FORMAT_B8G8R8A8_UNORM,\t\t\tVK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT},\n\t\t{PTI_BGRX8,\t\t\t\tVK_FORMAT_B8G8R8A8_UNORM,\t\t\tVK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT},\n\n\t\t{PTI_RGB8,\t\t\t\tVK_FORMAT_R8G8B8_UNORM,\t\t\t\tVK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT},\n\t\t{PTI_BGR8,\t\t\t\tVK_FORMAT_B8G8R8_UNORM,\t\t\t\tVK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT},\n\n\t\t{PTI_RGBA8_SRGB,\t\tVK_FORMAT_R8G8B8A8_SRGB,\t\t\tVK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT|VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT},\n\t\t{PTI_RGBX8_SRGB,\t\tVK_FORMAT_R8G8B8A8_SRGB,\t\t\tVK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT|VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT},\n\t\t{PTI_BGRA8_SRGB,\t\tVK_FORMAT_B8G8R8A8_SRGB,\t\t\tVK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT|VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT},\n\t\t{PTI_BGRX8_SRGB,\t\tVK_FORMAT_B8G8R8A8_SRGB,\t\t\tVK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT|VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT},\n\n\t\t{PTI_E5BGR9,\t\t\tVK_FORMAT_E5B9G9R9_UFLOAT_PACK32,\tVK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT|VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT},\n\t\t{PTI_B10G11R11F,\t\tVK_FORMAT_B10G11R11_UFLOAT_PACK32,\tVK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT|VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT},\n\t\t{PTI_A2BGR10,\t\t\tVK_FORMAT_A2B10G10R10_UNORM_PACK32,\tVK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT|VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT},\n\t\t{PTI_RGB565,\t\t\tVK_FORMAT_R5G6B5_UNORM_PACK16,\t\tVK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT},\n\t\t{PTI_RGBA4444,\t\t\tVK_FORMAT_R4G4B4A4_UNORM_PACK16,\tVK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT},\n#ifdef VK_EXT_4444_formats\n\t\t{PTI_ARGB4444,\t\t\tVK_FORMAT_A4R4G4B4_UNORM_PACK16_EXT,VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT},\n#endif\n\t\t{PTI_RGBA5551,\t\t\tVK_FORMAT_R5G5B5A1_UNORM_PACK16,\tVK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT},\n\t\t{PTI_ARGB1555,\t\t\tVK_FORMAT_A1R5G5B5_UNORM_PACK16,\tVK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT},\n\t\t{PTI_RGBA16F,\t\t\tVK_FORMAT_R16G16B16A16_SFLOAT,\t\tVK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT|VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT|VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT},\n\t\t{PTI_RGBA32F,\t\t\tVK_FORMAT_R32G32B32A32_SFLOAT,\t\tVK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT|VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT|VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT},\n\t\t{PTI_RGB32F,\t\t\tVK_FORMAT_R32G32B32_SFLOAT,\t\t\tVK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT},\n\t\t{PTI_L8,\t\t\t\tVK_FORMAT_R8_UNORM,\t\t\t\t\tVK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT},\n\t\t{PTI_L8A8,\t\t\t\tVK_FORMAT_R8G8_UNORM,\t\t\t\tVK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT},\n\t\t{PTI_L8_SRGB,\t\t\tVK_FORMAT_R8_SRGB,\t\t\t\t\tVK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT},\n\t\t{PTI_R8,\t\t\t\tVK_FORMAT_R8_UNORM,\t\t\t\t\tVK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT},\n\t\t{PTI_RG8,\t\t\t\tVK_FORMAT_R8G8_UNORM,\t\t\t\tVK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT},\n\t\t{PTI_R8_SNORM,\t\t\tVK_FORMAT_R8_SNORM,\t\t\t\t\tVK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT},\n\t\t{PTI_RG8_SNORM,\t\t\tVK_FORMAT_R8G8_SNORM,\t\t\t\tVK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT},\n\t\t{PTI_R16,\t\t\t\tVK_FORMAT_R16_UNORM,\t\t\t\tVK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT},\n\t\t{PTI_RGBA16,\t\t\tVK_FORMAT_R16G16B16A16_UNORM,\t\tVK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT},\n\t\t{PTI_R16F,\t\t\t\tVK_FORMAT_R16_SFLOAT,\t\t\t\tVK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT},\n\t\t{PTI_R32F,\t\t\t\tVK_FORMAT_R32_SFLOAT,\t\t\t\tVK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT},\n\n\t\t{PTI_DEPTH16,\t\t\tVK_FORMAT_D16_UNORM,\t\t\t\tVK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT},\n\t\t{PTI_DEPTH24,\t\t\tVK_FORMAT_X8_D24_UNORM_PACK32,\t\tVK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT},\n\t\t{PTI_DEPTH32,\t\t\tVK_FORMAT_D32_SFLOAT,\t\t\t\tVK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT},\n\t\t{PTI_DEPTH24_8,\t\t\tVK_FORMAT_D24_UNORM_S8_UINT,\t\tVK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT},\n\n\t\t{PTI_BC1_RGB,\t\t\tVK_FORMAT_BC1_RGB_UNORM_BLOCK,\t\tVK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT},\n\t\t{PTI_BC1_RGBA,\t\t\tVK_FORMAT_BC1_RGBA_UNORM_BLOCK,\t\tVK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT},\n\t\t{PTI_BC2_RGBA,\t\t\tVK_FORMAT_BC2_UNORM_BLOCK,\t\t\tVK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT},\n\t\t{PTI_BC3_RGBA,\t\t\tVK_FORMAT_BC3_UNORM_BLOCK,\t\t\tVK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT},\n\t\t{PTI_BC1_RGB_SRGB,\t\tVK_FORMAT_BC1_RGB_SRGB_BLOCK,\t\tVK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT},\n\t\t{PTI_BC1_RGBA_SRGB,\t\tVK_FORMAT_BC1_RGBA_SRGB_BLOCK,\t\tVK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT},\n\t\t{PTI_BC2_RGBA_SRGB,\t\tVK_FORMAT_BC2_SRGB_BLOCK,\t\t\tVK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT},\n\t\t{PTI_BC3_RGBA_SRGB,\t\tVK_FORMAT_BC3_SRGB_BLOCK,\t\t\tVK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT},\n\t\t{PTI_BC4_R,\t\t\t\tVK_FORMAT_BC4_UNORM_BLOCK,\t\t\tVK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT},\n\t\t{PTI_BC4_R_SNORM,\t\tVK_FORMAT_BC4_SNORM_BLOCK,\t\t\tVK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT},\n\t\t{PTI_BC5_RG,\t\t\tVK_FORMAT_BC5_UNORM_BLOCK,\t\t\tVK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT},\n\t\t{PTI_BC5_RG_SNORM,\t\tVK_FORMAT_BC5_SNORM_BLOCK,\t\t\tVK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT},\n\t\t{PTI_BC6_RGB_UFLOAT,\tVK_FORMAT_BC6H_UFLOAT_BLOCK,\t\tVK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT},\n\t\t{PTI_BC6_RGB_SFLOAT,\tVK_FORMAT_BC6H_SFLOAT_BLOCK,\t\tVK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT},\n\t\t{PTI_BC7_RGBA,\t\t\tVK_FORMAT_BC7_UNORM_BLOCK,\t\t\tVK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT},\n\t\t{PTI_BC7_RGBA_SRGB,\t\tVK_FORMAT_BC7_SRGB_BLOCK,\t\t\tVK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT},\n\t\t{PTI_ETC1_RGB8,\t\t\tVK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK,\tVK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT},\t//vulkan doesn't support etc1 (but that's okay, because etc2 is a superset).\n\t\t{PTI_ETC2_RGB8,\t\t\tVK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK,\tVK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT},\n\t\t{PTI_ETC2_RGB8A1,\t\tVK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK,VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT},\n\t\t{PTI_ETC2_RGB8A8,\t\tVK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK,VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT},\n\t\t{PTI_ETC2_RGB8_SRGB,\tVK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK,\tVK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT},\n\t\t{PTI_ETC2_RGB8A1_SRGB,\tVK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK,\tVK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT},\n\t\t{PTI_ETC2_RGB8A8_SRGB,\tVK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK,\tVK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT},\n\t\t{PTI_EAC_R11,\t\t\tVK_FORMAT_EAC_R11_UNORM_BLOCK,\t\tVK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT},\n\t\t{PTI_EAC_R11_SNORM,\t\tVK_FORMAT_EAC_R11_SNORM_BLOCK,\t\tVK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT},\n\t\t{PTI_EAC_RG11,\t\t\tVK_FORMAT_EAC_R11G11_UNORM_BLOCK,\tVK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT},\n\t\t{PTI_EAC_RG11_SNORM,\tVK_FORMAT_EAC_R11G11_SNORM_BLOCK,\tVK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT},\n\t\t{PTI_ASTC_4X4_LDR,\t\tVK_FORMAT_ASTC_4x4_UNORM_BLOCK,\t\tVK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT},\n\t\t{PTI_ASTC_5X4_LDR,\t\tVK_FORMAT_ASTC_5x4_UNORM_BLOCK,\t\tVK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT},\n\t\t{PTI_ASTC_5X5_LDR,\t\tVK_FORMAT_ASTC_5x5_UNORM_BLOCK,\t\tVK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT},\n\t\t{PTI_ASTC_6X5_LDR,\t\tVK_FORMAT_ASTC_6x5_UNORM_BLOCK,\t\tVK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT},\n\t\t{PTI_ASTC_6X6_LDR,\t\tVK_FORMAT_ASTC_6x6_UNORM_BLOCK,\t\tVK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT},\n\t\t{PTI_ASTC_8X5_LDR,\t\tVK_FORMAT_ASTC_8x5_UNORM_BLOCK,\t\tVK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT},\n\t\t{PTI_ASTC_8X6_LDR,\t\tVK_FORMAT_ASTC_8x6_UNORM_BLOCK,\t\tVK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT},\n\t\t{PTI_ASTC_8X8_LDR,\t\tVK_FORMAT_ASTC_8x8_UNORM_BLOCK,\t\tVK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT},\n\t\t{PTI_ASTC_10X5_LDR,\t\tVK_FORMAT_ASTC_10x5_UNORM_BLOCK,\tVK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT},\n\t\t{PTI_ASTC_10X6_LDR,\t\tVK_FORMAT_ASTC_10x6_UNORM_BLOCK,\tVK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT},\n\t\t{PTI_ASTC_10X8_LDR,\t\tVK_FORMAT_ASTC_10x8_UNORM_BLOCK,\tVK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT},\n\t\t{PTI_ASTC_10X10_LDR,\tVK_FORMAT_ASTC_10x10_UNORM_BLOCK,\tVK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT},\n\t\t{PTI_ASTC_12X10_LDR,\tVK_FORMAT_ASTC_12x10_UNORM_BLOCK,\tVK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT},\n\t\t{PTI_ASTC_12X12_LDR,\tVK_FORMAT_ASTC_12x12_UNORM_BLOCK,\tVK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT},\n\t\t{PTI_ASTC_4X4_SRGB,\t\tVK_FORMAT_ASTC_4x4_SRGB_BLOCK,\t\tVK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT},\n\t\t{PTI_ASTC_5X4_SRGB,\t\tVK_FORMAT_ASTC_5x4_SRGB_BLOCK,\t\tVK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT},\n\t\t{PTI_ASTC_5X5_SRGB,\t\tVK_FORMAT_ASTC_5x5_SRGB_BLOCK,\t\tVK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT},\n\t\t{PTI_ASTC_6X5_SRGB,\t\tVK_FORMAT_ASTC_6x5_SRGB_BLOCK,\t\tVK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT},\n\t\t{PTI_ASTC_6X6_SRGB,\t\tVK_FORMAT_ASTC_6x6_SRGB_BLOCK,\t\tVK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT},\n\t\t{PTI_ASTC_8X5_SRGB,\t\tVK_FORMAT_ASTC_8x5_SRGB_BLOCK,\t\tVK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT},\n\t\t{PTI_ASTC_8X6_SRGB,\t\tVK_FORMAT_ASTC_8x6_SRGB_BLOCK,\t\tVK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT},\n\t\t{PTI_ASTC_8X8_SRGB,\t\tVK_FORMAT_ASTC_8x8_SRGB_BLOCK,\t\tVK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT},\n\t\t{PTI_ASTC_10X5_SRGB,\tVK_FORMAT_ASTC_10x5_SRGB_BLOCK,\t\tVK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT},\n\t\t{PTI_ASTC_10X6_SRGB,\tVK_FORMAT_ASTC_10x6_SRGB_BLOCK,\t\tVK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT},\n\t\t{PTI_ASTC_10X8_SRGB,\tVK_FORMAT_ASTC_10x8_SRGB_BLOCK,\t\tVK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT},\n\t\t{PTI_ASTC_10X10_SRGB,\tVK_FORMAT_ASTC_10x10_SRGB_BLOCK,\tVK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT},\n\t\t{PTI_ASTC_12X10_SRGB,\tVK_FORMAT_ASTC_12x10_SRGB_BLOCK,\tVK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT},\n\t\t{PTI_ASTC_12X12_SRGB,\tVK_FORMAT_ASTC_12x12_SRGB_BLOCK,\tVK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT},\n\n#ifdef VK_EXT_texture_compression_astc_hdr\n\t\t{PTI_ASTC_4X4_HDR,\t\tVK_FORMAT_ASTC_4x4_SFLOAT_BLOCK_EXT,\tVK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT},\n\t\t{PTI_ASTC_5X4_HDR,\t\tVK_FORMAT_ASTC_5x4_SFLOAT_BLOCK_EXT,\tVK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT},\n\t\t{PTI_ASTC_5X5_HDR,\t\tVK_FORMAT_ASTC_5x5_SFLOAT_BLOCK_EXT,\tVK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT},\n\t\t{PTI_ASTC_6X5_HDR,\t\tVK_FORMAT_ASTC_6x5_SFLOAT_BLOCK_EXT,\tVK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT},\n\t\t{PTI_ASTC_6X6_HDR,\t\tVK_FORMAT_ASTC_6x6_SFLOAT_BLOCK_EXT,\tVK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT},\n\t\t{PTI_ASTC_8X5_HDR,\t\tVK_FORMAT_ASTC_8x5_SFLOAT_BLOCK_EXT,\tVK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT},\n\t\t{PTI_ASTC_8X6_HDR,\t\tVK_FORMAT_ASTC_8x6_SFLOAT_BLOCK_EXT,\tVK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT},\n\t\t{PTI_ASTC_8X8_HDR,\t\tVK_FORMAT_ASTC_8x8_SFLOAT_BLOCK_EXT,\tVK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT},\n\t\t{PTI_ASTC_10X5_HDR,\t\tVK_FORMAT_ASTC_10x5_SFLOAT_BLOCK_EXT,\tVK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT},\n\t\t{PTI_ASTC_10X6_HDR,\t\tVK_FORMAT_ASTC_10x6_SFLOAT_BLOCK_EXT,\tVK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT},\n\t\t{PTI_ASTC_10X8_HDR,\t\tVK_FORMAT_ASTC_10x8_SFLOAT_BLOCK_EXT,\tVK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT},\n\t\t{PTI_ASTC_10X10_HDR,\tVK_FORMAT_ASTC_10x10_SFLOAT_BLOCK_EXT,\tVK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT},\n\t\t{PTI_ASTC_12X10_HDR,\tVK_FORMAT_ASTC_12x10_SFLOAT_BLOCK_EXT,\tVK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT},\n\t\t{PTI_ASTC_12X12_HDR,\tVK_FORMAT_ASTC_12x12_SFLOAT_BLOCK_EXT,\tVK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT},\n#endif\n\t};\n\tunsigned int i;\n\tVkPhysicalDeviceProperties props;\n\n\tvkGetPhysicalDeviceProperties(vk.gpu, &props);\n\tvk.limits = props.limits;\n\n\tsh_config.texture2d_maxsize = props.limits.maxImageDimension2D;\n\tsh_config.texturecube_maxsize = props.limits.maxImageDimensionCube;\n\tsh_config.texture2darray_maxlayers = props.limits.maxImageArrayLayers;\n\n\tfor (i = 0; i < countof(texfmt); i++)\n\t{\n\t\tunsigned int need = /*VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT |*/ texfmt[i].needextra;\n\t\tVkFormatProperties fmt;\n\t\tvkGetPhysicalDeviceFormatProperties(vk.gpu, texfmt[i].vulkan, &fmt);\n\n\t\tif ((fmt.optimalTilingFeatures & need) == need)\n\t\t\tsh_config.texfmt[texfmt[i].pti] = true;\n\t}\n\n\tif (sh_config.texfmt[PTI_BC1_RGBA] && sh_config.texfmt[PTI_BC2_RGBA] && sh_config.texfmt[PTI_BC3_RGBA] && sh_config.texfmt[PTI_BC5_RG] && sh_config.texfmt[PTI_BC7_RGBA])\n\t\tsh_config.hw_bc = 3;\n\tif (sh_config.texfmt[PTI_ETC2_RGB8] && sh_config.texfmt[PTI_ETC2_RGB8A1] && sh_config.texfmt[PTI_ETC2_RGB8A8] && sh_config.texfmt[PTI_EAC_RG11])\n\t\tsh_config.hw_etc = 2;\n\tif (sh_config.texfmt[PTI_ASTC_4X4_LDR])\n\t\tsh_config.hw_astc = 1;\t//the core vulkan formats refer to the ldr profile. hdr is a separate extension, which is still not properly specified..\n\tif (sh_config.texfmt[PTI_ASTC_4X4_HDR])\n\t\tsh_config.hw_astc = 2;\t//the core vulkan formats refer to the ldr profile. hdr is a separate extension, which is still not properly specified..\n}\n\n//creates a vulkan instance with the additional extensions, and hands a copy of the instance to the caller.\nqboolean VK_CreateInstance(vrsetup_t *info, char *vrexts, void *result)\n{\n\tVkInstanceCreateInfo inst_info = *(VkInstanceCreateInfo*)info->userctx;\n\tVkResult err;\n\tconst char *ext[64];\n\tunsigned int numext = inst_info.enabledExtensionCount;\n\tmemcpy(ext, inst_info.ppEnabledExtensionNames, numext*sizeof(*ext));\n\twhile (vrexts && numext < countof(ext))\n\t{\n\t\text[numext++] = vrexts;\n\t\tvrexts = strchr(vrexts, ' ');\n\t\tif (!vrexts)\n\t\t\tbreak;\n\t\t*vrexts++ = 0;\n\t}\n\n\terr = vkCreateInstance(&inst_info, vkallocationcb, &vk.instance);\n\tif (err == VK_ERROR_LAYER_NOT_PRESENT && inst_info.enabledLayerCount>0 && !strcmp(inst_info.ppEnabledLayerNames[inst_info.enabledLayerCount-1], \"VK_LAYER_KHRONOS_validation\"))\n\t{\t//if we can't do debugging then just try to create a context without it.\n\t\tCon_Printf(CON_WARNING\"VK_ERROR_LAYER_NOT_PRESENT... trying again without debug layers\\n\");\n\t\tinst_info.enabledLayerCount--;\n\t\terr = vkCreateInstance(&inst_info, vkallocationcb, &vk.instance);\n\t}\n\tswitch(err)\n\t{\n\tcase VK_ERROR_INCOMPATIBLE_DRIVER:\n\t\tCon_Printf(CON_ERROR\"VK_ERROR_INCOMPATIBLE_DRIVER: please install an appropriate vulkan driver\\n\");\n\t\treturn false;\n\tcase VK_ERROR_EXTENSION_NOT_PRESENT:\n\t\tCon_Printf(CON_ERROR\"VK_ERROR_EXTENSION_NOT_PRESENT: something on a system level is probably misconfigured\\n\");\n\t\treturn false;\n\tcase VK_ERROR_LAYER_NOT_PRESENT:\n\t\tCon_Printf(CON_ERROR\"VK_ERROR_LAYER_NOT_PRESENT: requested layer is not known/usable\\n\");\n\t\treturn false;\n\tdefault:\n\t\tCon_Printf(CON_ERROR\"Unknown vulkan instance creation error: %x\\n\", err);\n\t\treturn false;\n\tcase VK_SUCCESS:\n\t\tbreak;\n\t}\n\n\tif (result)\n\t{\n\t\t*(VkInstance*)result = vk.instance;\n\t\t((VkInstanceCreateInfo*)info->userctx)->enabledLayerCount = inst_info.enabledLayerCount;\n\t}\n\treturn true;\n}\n\nqboolean VK_EnumerateDevices (void *usercontext, void(*callback)(void *context, const char *devicename, const char *outputname, const char *desc), const char *descprefix, PFN_vkGetInstanceProcAddr vk_GetInstanceProcAddr)\n{\n\tVkInstance vk_instance;\n\n\tVkApplicationInfo app;\n\tVkInstanceCreateInfo inst_info;\n\n\t#if 0\t//for quicky debugging...\n\t\t#define VKFunc(n) int vk##n;\n\t\t\tVKFuncs\n\t\t#undef VKFunc\n\t#endif\n\n\t#define VKFunc(n) PFN_vk##n vk_##n;\n\t\tVKFunc(CreateInstance)\n\n\t\tVKFunc(DestroyInstance)\n\t\tVKFunc(EnumeratePhysicalDevices)\n\t\tVKFunc(GetPhysicalDeviceProperties)\n\t#undef VKFunc\n\n\t//get second set of pointers... (instance-level)\n#ifdef VK_NO_PROTOTYPES\n\tif (!vk_GetInstanceProcAddr)\n\t\treturn false;\n\t#define VKFunc(n) vk_##n = (PFN_vk##n)vk_GetInstanceProcAddr(VK_NULL_HANDLE, \"vk\"#n);\n\t\t//VKFunc(EnumerateInstanceLayerProperties)\n\t\t//VKFunc(EnumerateInstanceExtensionProperties)\n\t\tVKFunc(CreateInstance)\n\t#undef VKFunc\n#endif\n\n\tmemset(&app, 0, sizeof(app));\n\tapp.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;\n\tapp.pNext = NULL;\n\tapp.pApplicationName = FULLENGINENAME;\n\tapp.applicationVersion = revision_number(false);\n\tapp.pEngineName = \"FTE Quake\";\n\tapp.engineVersion = VK_MAKE_VERSION(FTE_VER_MAJOR, FTE_VER_MINOR, 0);\n\tapp.apiVersion = VK_API_VERSION_1_0;\t//make sure it works...\n\n\tmemset(&inst_info, 0, sizeof(inst_info));\n\tinst_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;\n\tinst_info.pApplicationInfo = &app;\n\tinst_info.enabledLayerCount = vklayercount;\n\tinst_info.ppEnabledLayerNames = vklayerlist;\n\tinst_info.enabledExtensionCount = 0;\n\tinst_info.ppEnabledExtensionNames = NULL;\n\n\tif (vk_CreateInstance(&inst_info, vkallocationcb, &vk_instance) != VK_SUCCESS)\n\t\treturn false;\n\n\t//third set of functions...\n#ifdef VK_NO_PROTOTYPES\n\t//vk_GetInstanceProcAddr = (PFN_vkGetInstanceProcAddr)vk_GetInstanceProcAddr(vk_instance, \"vkGetInstanceProcAddr\");\n\t#define VKFunc(n) vk_##n = (PFN_vk##n)vk_GetInstanceProcAddr(vk_instance, \"vk\"#n);\n\t\tVKFunc(DestroyInstance)\n\t\tVKFunc(EnumeratePhysicalDevices)\n\t\tVKFunc(GetPhysicalDeviceProperties)\n\t#undef VKFunc\n#endif\n\n\t//enumerate the gpus\n\t{\n\t\tuint32_t gpucount = 0, i;\n\t\tVkPhysicalDevice *devs;\n\t\tchar gpuname[64];\n\n\t\tvk_EnumeratePhysicalDevices(vk_instance, &gpucount, NULL);\n\t\tif (!gpucount)\n\t\t\treturn false;\n\t\tdevs = malloc(sizeof(VkPhysicalDevice)*gpucount);\n\t\tvk_EnumeratePhysicalDevices(vk_instance, &gpucount, devs);\n\t\tfor (i = 0; i < gpucount; i++)\n\t\t{\n\t\t\tVkPhysicalDeviceProperties props;\n\t\t\tvk_GetPhysicalDeviceProperties(devs[i], &props);\n\n\t\t\tQ_snprintfz(gpuname, sizeof(gpuname), \"GPU%u\", i);\n\t\t\t//FIXME: make sure its not a number or GPU#...\n\t\t\tcallback(usercontext, gpuname, \"\", va(\"%s%s\", descprefix, props.deviceName));\n\t\t}\n\t\tfree(devs);\n\t}\n\n\tvk_DestroyInstance(vk_instance, vkallocationcb);\n\treturn true;\n}\n\n//initialise the vulkan instance, context, device, etc.\nqboolean VK_Init(rendererstate_t *info, const char *const*sysextnames, unsigned int numsysext, qboolean (*createSurface)(void), void (*dopresent)(struct vkframe *theframe))\n{\n\tVkQueueFamilyProperties *queueprops;\n\tVkResult err;\n\tVkApplicationInfo app;\n\tVkInstanceCreateInfo inst_info;\n\tint gpuidx = 0;\n\tconst char *extensions[8];\n\tuint32_t extensions_count = 0;\n\n\tqboolean\tignorequeuebugs = false;\n\tqboolean okay;\n\tvrsetup_t vrsetup = {sizeof(vrsetup)};\n\n\t//device extensions that want to enable\n\t//initialised in reverse order, so superseeded should name later extensions.\n\tstruct\n\t{\n\t\tqboolean *flag;\n\t\tconst char *name;\n\t\tcvar_t *var;\n\t\tqboolean def;\t\t\t\t//default value when the cvar is empty.\n\t\tqboolean *superseeded;\t\t//if this is set then the extension will not be enabled after all\n\t\tconst char *warningtext;\t//printed if the extension is requested but not supported by the device\n\t\tqboolean supported;\n\t} knowndevexts[] =\n\t{\n\t\t{&vk.khr_swapchain,\t\t\t\t\tVK_KHR_SWAPCHAIN_EXTENSION_NAME,\t\t\t\tNULL,\t\t\t\t\t\t\ttrue, NULL, \" Nothing will be drawn!\"},\n\t\t{&vk.khr_get_memory_requirements2,\tVK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME,&vk_khr_get_memory_requirements2,true, NULL, NULL},\n\t\t{&vk.khr_dedicated_allocation,\t\tVK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME,\t\t&vk_khr_dedicated_allocation,\ttrue, NULL, NULL},\n\t\t{&vk.khr_push_descriptor,\t\t\tVK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME,\t\t\t&vk_khr_push_descriptor,\t\ttrue, NULL, NULL},\n\t\t{&vk.amd_rasterization_order,\t\tVK_AMD_RASTERIZATION_ORDER_EXTENSION_NAME,\t\t&vk_amd_rasterization_order,\tfalse, NULL, NULL},\n#ifdef VK_KHR_fragment_shading_rate\n\t\t{&vk.khr_fragment_shading_rate,\t\tVK_KHR_FRAGMENT_SHADING_RATE_EXTENSION_NAME,\t&vK_khr_fragment_shading_rate,\ttrue, NULL, NULL},\n#endif\n#ifdef VK_EXT_astc_decode_mode\n\t\t{&vk.ext_astc_decode_mode,\t\t\tVK_EXT_ASTC_DECODE_MODE_EXTENSION_NAME,\t\t\t&vk_ext_astc_decode_mode,\t\ttrue,  NULL, NULL},\n#endif\n#ifdef VK_KHR_acceleration_structure\n\t\t{&vk.khr_deferred_host_operations,  VK_KHR_DEFERRED_HOST_OPERATIONS_EXTENSION_NAME,\t&vk_khr_ray_query,\t\t\t\ttrue,  NULL, NULL},\t//dependancy of khr_acceleration_structure\n\t\t{&vk.khr_acceleration_structure,\tVK_KHR_ACCELERATION_STRUCTURE_EXTENSION_NAME,\t&vk_khr_ray_query,\t\t\t\ttrue,  NULL, NULL},\n#endif\n#ifdef VK_KHR_ray_query\n\t\t{&vk.khr_ray_query,\t\t\t\t\tVK_KHR_RAY_QUERY_EXTENSION_NAME,\t\t\t\t&vk_khr_ray_query,\t\t\t\ttrue,  NULL, NULL},\n#endif\n\t};\n\tsize_t e;\n\n\tfor (e = 0; e < countof(knowndevexts); e++)\n\t\t*knowndevexts[e].flag = false;\n\tvk.neednewswapchain = true;\n\tvk.triplebuffer = info->triplebuffer;\n\tvk.vsync = info->wait;\n\tvk.dopresent = dopresent?dopresent:VK_DoPresent;\n\tmemset(&sh_config, 0, sizeof(sh_config));\n\n\n\t//get second set of pointers... (instance-level)\n#ifdef VK_NO_PROTOTYPES\n\tif (!vkGetInstanceProcAddr)\n\t{\n\t\tCon_Printf(CON_ERROR\"vkGetInstanceProcAddr is null\\n\");\n\t\treturn false;\n\t}\n#define VKFunc(n) vk##n = (PFN_vk##n)vkGetInstanceProcAddr(VK_NULL_HANDLE, \"vk\"#n);\n\tVKInstFuncs\n#undef VKFunc\n#endif\n\n\t//try and enable some instance extensions...\n\t{\n\t\tqboolean surfext = false;\n\t\tuint32_t count, i, j;\n\t\tVkExtensionProperties *ext;\n#ifdef VK_EXT_debug_utils\n\t\tqboolean havedebugutils = false;\n#endif\n#ifdef VK_EXT_debug_report\n\t\tqboolean havedebugreport = false;\n#endif\n\t\tif (VK_SUCCESS!=vkEnumerateInstanceExtensionProperties(NULL, &count, NULL))\n\t\t\tcount = 0;\n\t\text = malloc(sizeof(*ext)*count);\n\t\tif (!ext || VK_SUCCESS!=vkEnumerateInstanceExtensionProperties(NULL, &count, ext))\n\t\t\tcount = 0;\n\t\tfor (i = 0; i < count && extensions_count < countof(extensions); i++)\n\t\t{\n\t\t\tCon_DLPrintf(2, \" vki: %s\\n\", ext[i].extensionName);\n#ifdef VK_EXT_debug_utils\n\t\t\tif (!strcmp(ext[i].extensionName, VK_EXT_DEBUG_REPORT_EXTENSION_NAME))\n\t\t\t\thavedebugutils = true;\n#endif\n#ifdef VK_EXT_debug_report\n\t\t\tif (!strcmp(ext[i].extensionName, VK_EXT_DEBUG_REPORT_EXTENSION_NAME))\n\t\t\t\thavedebugreport = true;\n#endif\n\t\t\tif (!strcmp(ext[i].extensionName, VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME))\n\t\t\t\textensions[extensions_count++] = VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME;\n\t\t\telse if (sysextnames && !strcmp(ext[i].extensionName, VK_KHR_SURFACE_EXTENSION_NAME))\n\t\t\t{\n\t\t\t\textensions[extensions_count++] = VK_KHR_SURFACE_EXTENSION_NAME;\n\t\t\t\tsurfext = true;\n\t\t\t}\n\t\t\telse if (sysextnames)\n\t\t\t{\n\t\t\t\tfor (j = 0; j < numsysext; j++)\n\t\t\t\t{\n\t\t\t\t\tif (!sysextnames[j])\n\t\t\t\t\t{\n\t\t\t\t\t\tif (numsysext == UINT_MAX)\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tif (!strcmp(ext[i].extensionName, sysextnames[j]))\n\t\t\t\t\t{\n\t\t\t\t\t\textensions[extensions_count++] = sysextnames[j];\n\t\t\t\t\t\tvk.khr_swapchain = true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tfree(ext);\n\n\t\tif (!vk_debug.ival)\n\t\t\t;\n#ifdef VK_EXT_debug_utils\n\t\telse if (havedebugutils)\n\t\t\textensions[extensions_count++] = VK_EXT_DEBUG_UTILS_EXTENSION_NAME;\n#endif\n#ifdef VK_EXT_debug_report\n\t\telse if (havedebugreport)\n\t\t\textensions[extensions_count++] = VK_EXT_DEBUG_REPORT_EXTENSION_NAME;\n#endif\n\n\t\tif (sysextnames && (!vk.khr_swapchain || !surfext))\n\t\t{\n\t\t\tCon_TPrintf(CON_ERROR\"Vulkan instance lacks driver support for %s\\n\", sysextnames[0]);\n\t\t\treturn false;\n\t\t}\n\t}\n\n#define ENGINEVERSION 1\n\tmemset(&app, 0, sizeof(app));\n\tapp.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;\n\tapp.pNext = NULL;\n\tapp.pApplicationName = FULLENGINENAME;\n\tapp.applicationVersion = revision_number(false);\n\tapp.pEngineName = \"FTE Quake\";\n\tapp.engineVersion = VK_MAKE_VERSION(FTE_VER_MAJOR, FTE_VER_MINOR, 0);\n\tapp.apiVersion = VK_API_VERSION_1_0;\n\tif (vkEnumerateInstanceVersion)\n\t{\n\t\tvkEnumerateInstanceVersion(&app.apiVersion);\n#ifdef VK_API_VERSION_1_2\n\t\tif (app.apiVersion > VK_API_VERSION_1_2)\n\t\t\tapp.apiVersion = VK_API_VERSION_1_2;\n#else\n\t\tif (app.apiVersion > VK_API_VERSION_1_0)\n\t\t\tapp.apiVersion = VK_API_VERSION_1_0;\n#endif\n\t}\n\n\tmemset(&inst_info, 0, sizeof(inst_info));\n\tinst_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;\n\tinst_info.pApplicationInfo = &app;\n\tinst_info.enabledLayerCount = vklayercount;\n\tinst_info.ppEnabledLayerNames = vklayerlist;\n\tinst_info.enabledExtensionCount = extensions_count;\n\tinst_info.ppEnabledExtensionNames = extensions;\n\n\tvrsetup.vrplatform = VR_VULKAN;\n\tvrsetup.userctx = &inst_info;\n\tvrsetup.createinstance = VK_CreateInstance;\n\tif (info->vr)\n\t{\n\t\tokay = info->vr->Prepare(&vrsetup);\n\t\tif (!okay)\n\t\t{\n\t\t\tinfo->vr->Shutdown();\n\t\t\tinfo->vr = NULL;\n\t\t}\n\t}\n\telse\n\t\tokay = false;\n\tif (!okay)\n\t\tokay = vrsetup.createinstance(&vrsetup, NULL, NULL);\n\tif (!okay)\n\t{\n\t\tCon_TPrintf(CON_ERROR\"Unable to create vulkan instance\\n\");\n\t\tif (info->vr)\n\t\t\tinfo->vr->Shutdown();\n\t\treturn false;\n\t}\n\tvid.vr = info->vr;\n\n#ifdef MULTITHREAD\n\tvk.allowsubmissionthread = !vid.vr;\n#endif\n\n\t//third set of functions...\n#ifdef VK_NO_PROTOTYPES\n\t//vkGetInstanceProcAddr = (PFN_vkGetInstanceProcAddr)vkGetInstanceProcAddr(vk.instance, \"vkGetInstanceProcAddr\");\n#define VKFunc(n) vk##n = (PFN_vk##n)vkGetInstanceProcAddr(vk.instance, \"vk\"#n);\n\tVKInst2Funcs\n#undef VKFunc\n#endif\n\n\t//set up debug callbacks\n\tif (vk_debug.ival)\n\t{\n#ifdef VK_EXT_debug_utils\n\t\tvkCreateDebugUtilsMessengerEXT = (PFN_vkCreateDebugUtilsMessengerEXT)vkGetInstanceProcAddr(vk.instance, \"vkCreateDebugUtilsMessengerEXT\");\n\t\tvkDestroyDebugUtilsMessengerEXT = (PFN_vkDestroyDebugUtilsMessengerEXT)vkGetInstanceProcAddr(vk.instance, \"vkDestroyDebugUtilsMessengerEXT\");\n\t\tif (vkCreateDebugUtilsMessengerEXT)\n\t\t{\n\t\t\tVkDebugUtilsMessengerCreateInfoEXT dbgCreateInfo;\n\t\t\tmemset(&dbgCreateInfo, 0, sizeof(dbgCreateInfo));\n\t\t\tdbgCreateInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;\n\t\t\tdbgCreateInfo.pfnUserCallback = mydebugutilsmessagecallback;\n\t\t\tdbgCreateInfo.pUserData = NULL;\n\t\t\tdbgCreateInfo.messageSeverity =\tVK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT\t|\n\t\t\t\t\t\t\t\t\t\t\tVK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT\t|\n\t\t\t\t\t\t\t\t\t\t\tVK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT\t|\n\t\t\t\t\t\t\t\t\t\t\tVK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT;\n\t\t\tdbgCreateInfo.messageType =\t    VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT |\n\t\t\t\t\t\t\t\t\t\t\tVK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT |\n\t\t\t\t\t\t\t\t\t\t\tVK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT;\n\t\t\tvkCreateDebugUtilsMessengerEXT(vk.instance, &dbgCreateInfo, vkallocationcb, &vk_debugucallback);\n\t\t}\n#endif\n#ifdef VK_EXT_debug_report\n\t\tvkCreateDebugReportCallbackEXT = (PFN_vkCreateDebugReportCallbackEXT)vkGetInstanceProcAddr(vk.instance, \"vkCreateDebugReportCallbackEXT\");\n\t\tvkDestroyDebugReportCallbackEXT = (PFN_vkDestroyDebugReportCallbackEXT)vkGetInstanceProcAddr(vk.instance, \"vkDestroyDebugReportCallbackEXT\");\n\t\tif (vkCreateDebugReportCallbackEXT && vkDestroyDebugReportCallbackEXT)\n\t\t{\n\t\t\tVkDebugReportCallbackCreateInfoEXT dbgCreateInfo;\n\t\t\tmemset(&dbgCreateInfo, 0, sizeof(dbgCreateInfo));\n\t\t\tdbgCreateInfo.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT;\n\t\t\tdbgCreateInfo.pfnCallback = mydebugreportcallback;\n\t\t\tdbgCreateInfo.pUserData = NULL;\n\t\t\tdbgCreateInfo.flags =\tVK_DEBUG_REPORT_ERROR_BIT_EXT |\n\t\t\t\t\t\t\t\t\tVK_DEBUG_REPORT_WARNING_BIT_EXT\t|\n/*\t\t\t\t\t\t\t\t\tVK_DEBUG_REPORT_INFORMATION_BIT_EXT\t| */\n\t\t\t\t\t\t\t\t\tVK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT |\n\t\t\t\t\t\t\t\t\tVK_DEBUG_REPORT_DEBUG_BIT_EXT;\n\t\t\tvkCreateDebugReportCallbackEXT(vk.instance, &dbgCreateInfo, vkallocationcb, &vk_debugcallback);\n\t\t}\n#endif\n\t}\n\n\t//create the platform-specific surface\n\tcreateSurface();\n\n\t//figure out which gpu we're going to use\n\t{\n\t\tuint32_t gpucount = 0, i;\n\t\tuint32_t bestpri = ~0u, pri;\n\t\tVkPhysicalDevice *devs;\n\t\tchar *s = info->subrenderer;\n\t\tint wantdev = -1;\n\t\tif (*s)\n\t\t{\n\t\t\tif (!Q_strncasecmp(s, \"GPU\", 3))\n\t\t\t\ts += 3;\n\t\t\twantdev = strtoul(s, &s, 0);\n\t\t\tif (*s)\t//its a named device.\n\t\t\t\twantdev = -1;\n\t\t}\n\n\t\tvkEnumeratePhysicalDevices(vk.instance, &gpucount, NULL);\n\t\tif (!gpucount)\n\t\t{\n\t\t\tCon_Printf(CON_ERROR\"vulkan: no devices known!\\n\");\n\t\t\treturn false;\n\t\t}\n\t\tdevs = malloc(sizeof(VkPhysicalDevice)*gpucount);\n\t\tvkEnumeratePhysicalDevices(vk.instance, &gpucount, devs);\n\t\tfor (i = 0; i < gpucount; i++)\n\t\t{\n\t\t\tVkPhysicalDeviceProperties props;\n\t\t\tuint32_t j, queue_count = 0;\n\t\t\tvkGetPhysicalDeviceProperties(devs[i], &props);\n\t\t\tvkGetPhysicalDeviceQueueFamilyProperties(devs[i], &queue_count, NULL);\n\n\t\t\tif (vk.khr_swapchain)\n\t\t\t{\n\t\t\t\tfor (j = 0; j < queue_count; j++)\n\t\t\t\t{\n\t\t\t\t\tVkBool32 supportsPresent = false;\n\t\t\t\t\tVkAssert(vkGetPhysicalDeviceSurfaceSupportKHR(devs[i], j, vk.surface, &supportsPresent));\n\t\t\t\t\tif (supportsPresent)\n\t\t\t\t\t\tbreak;\t//okay, this one should be usable\n\t\t\t\t}\n\t\t\t\tif (j == queue_count)\n\t\t\t\t{\n\t\t\t\t\tif ((wantdev >= 0 && i==wantdev) || (wantdev==-1 && *info->subrenderer && !Q_strcasecmp(props.deviceName, info->subrenderer)))\n\t\t\t\t\t{\n\t\t\t\t\t\tCon_Printf(CON_WARNING\"vulkan: attempting to use device \\\"%s\\\" despite no device queues being able to present to window surface\\n\", props.deviceName);\n\t\t\t\t\t\tignorequeuebugs = true;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\t//no queues can present to that surface, so I guess we can't use that device\n\t\t\t\t\t\tCon_DLPrintf((wantdev != i)?1:0, \"vulkan: ignoring device \\\"%s\\\" as it can't present to window\\n\", props.deviceName);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tCon_DPrintf(\"Found Vulkan Device \\\"%s\\\"\\n\", props.deviceName);\n\n\t\t\tif (!vk.gpu)\n\t\t\t{\n\t\t\t\tgpuidx = i;\n\t\t\t\tvk.gpu = devs[i];\n\t\t\t}\n\t\t\tswitch(props.deviceType)\n\t\t\t{\n\t\t\tdefault:\n\t\t\tcase VK_PHYSICAL_DEVICE_TYPE_OTHER:\n\t\t\t\tpri = 5;\n\t\t\t\tbreak;\n\t\t\tcase VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU:\n\t\t\t\tpri = 2;\n\t\t\t\tbreak;\n\t\t\tcase VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU:\n\t\t\t\tpri = 1;\n\t\t\t\tbreak;\n\t\t\tcase VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU:\n\t\t\t\tpri = 3;\n\t\t\t\tbreak;\n\t\t\tcase VK_PHYSICAL_DEVICE_TYPE_CPU:\n\t\t\t\tpri = 4;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (vrsetup.vk.physicaldevice != VK_NULL_HANDLE)\n\t\t\t{\t//if we're using vr, use the gpu our vr context requires.\n\t\t\t\tif (devs[i] == vrsetup.vk.physicaldevice)\n\t\t\t\t\tpri = 0;\n\t\t\t}\n\t\t\telse if (wantdev >= 0)\n\t\t\t{\n\t\t\t\tif (wantdev == i)\n\t\t\t\t\tpri = 0;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (!Q_strcasecmp(props.deviceName, info->subrenderer))\n\t\t\t\t\tpri = 0;\n\t\t\t}\n\n\t\t\tif (pri < bestpri)\n\t\t\t{\n\t\t\t\tgpuidx = i;\n\t\t\t\tvk.gpu = devs[gpuidx];\n\t\t\t\tbestpri = pri;\n\t\t\t}\n\t\t}\n\t\tfree(devs);\n\n\t\tif (!vk.gpu)\n\t\t{\n\t\t\tCon_Printf(CON_ERROR\"vulkan: unable to pick a usable device\\n\");\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t{\n\t\tchar *vendor, *type;\n#ifdef VK_API_VERSION_1_2\n\t\tVkPhysicalDeviceVulkan12Properties props12 = {VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_PROPERTIES};\n\t\tVkPhysicalDeviceProperties2 props = {VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2, &props12};\n#else\n\t\tstruct {VkPhysicalDeviceProperties properties;} props;\n#endif\n\t\tvkGetPhysicalDeviceProperties(vk.gpu, &props.properties);\t//legacy\n\n\t\tvk.apiversion = props.properties.apiVersion;\n\t\tif (vk.apiversion > app.apiVersion)\n\t\t\tvk.apiversion = app.apiVersion;\t//cap it to the instance version...\n\n#ifdef VK_API_VERSION_1_2\n\t\tif (vk.apiversion >= VK_API_VERSION_1_2)\n\t\t{\n\t\t\tPFN_vkGetPhysicalDeviceProperties2 vkGetPhysicalDeviceProperties2 = (PFN_vkGetPhysicalDeviceProperties2)vkGetInstanceProcAddr(vk.instance, \"vkGetPhysicalDeviceProperties2\");\n\t\t\tif (vkGetPhysicalDeviceProperties2)\n\t\t\t\tvkGetPhysicalDeviceProperties2(vk.gpu, &props);\n\t\t}\n\t\n\t\tif (*props12.driverName)\n\t\t\tvendor = props12.driverName;\n\t\telse\n#endif\n\t\tswitch(props.properties.vendorID)\n\t\t{\n\t\t//explicit registered vendors\n\t\tcase 0x10001: vendor = \"Vivante\";\t\tbreak;\n\t\tcase 0x10002: vendor = \"VeriSilicon\";\tbreak;\n\t\tcase 0x10003: vendor = \"Kazan\";\t\tbreak;\n\t\tcase 0x10004: vendor = \"Codeplay\";\t\tbreak;\n\t\tcase /*VK_VENDOR_ID_MESA*/0x10005: vendor = \"MESA\";\tbreak;\n\n\t\t//pci vendor ids\n\t\t//there's a lot of pci vendors, some even still exist, but not all of them actually have 3d hardware.\n\t\t//many of these probably won't even be used... Oh well.\n\t\t//anyway, here's some of the ones that are listed\n\t\tcase 0x1002: vendor = \"AMD\";\t\tbreak;\n\t\tcase 0x10DE: vendor = \"NVIDIA\";\t\tbreak;\n\t\tcase 0x8086: vendor = \"Intel\";\t\tbreak; //cute\n\t\tcase 0x13B5: vendor = \"ARM\";\t\tbreak;\n\t\tcase 0x5143: vendor = \"Qualcomm\";\tbreak;\n\t\tcase 0x1AEE: vendor = \"Imagination\";break;\n\t\tcase 0x1957: vendor = \"Freescale\";\tbreak;\n\n\t\t//I really have no idea who makes mobile gpus nowadays, but lets make some guesses.\n\t\tcase 0x1AE0: vendor = \"Google\";\t\tbreak;\n\t\tcase 0x5333: vendor = \"S3\";\t\t\tbreak;\n\t\tcase 0xA200: vendor = \"NEC\";\t\tbreak;\n\t\tcase 0x0A5C: vendor = \"Broadcom\";\tbreak;\n\t\tcase 0x1131: vendor = \"NXP\";\t\tbreak;\n\t\tcase 0x1099: vendor = \"Samsung\";\tbreak;\n\t\tcase 0x10C3: vendor = \"Samsung\";\tbreak;\n\t\tcase 0x11E2: vendor = \"Samsung\";\tbreak;\n\t\tcase 0x1249: vendor = \"Samsung\";\tbreak;\n\t\t\n\t\tdefault:\tvendor = va(\"VEND_%x\", props.properties.vendorID); break;\n\t\t}\n\n\t\tswitch(props.properties.deviceType)\n\t\t{\n\t\tdefault:\n\t\tcase VK_PHYSICAL_DEVICE_TYPE_OTHER:\t\t\t\ttype = \"(other)\"; break;\n\t\tcase VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU:\ttype = \"integrated\"; break;\n\t\tcase VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU:\t\ttype = \"discrete\"; break;\n\t\tcase VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU:\t\ttype = \"virtual\"; break;\n\t\tcase VK_PHYSICAL_DEVICE_TYPE_CPU:\t\t\t\ttype = \"software\"; break;\n\t\t}\n\n#ifdef VK_API_VERSION_1_2\n\t\tif (*props12.driverInfo)\n\t\t{\n\t\t\tCon_TPrintf(\"Vulkan Driver Name: %s\\n\"\n\t\t\t\t\t\t\"Vulkan Device (GPU%i): %s\\n\"\n\t\t\t\t\t\t\"Vulkan Driver Info: %s\\n\",\n\t\t\t\t\t\tvendor,\n\t\t\t\t\t\tgpuidx, props.properties.deviceName,\n\t\t\t\t\t\tprops12.driverInfo );\n\t\t}\n\t\telse\n#endif\n\t\t{\n\t\t\tCon_TPrintf(\"Vulkan %u.%u.%u: GPU%i %s %s %s (%u.%u.%u)\\n\", VK_VERSION_MAJOR(props.properties.apiVersion), VK_VERSION_MINOR(props.properties.apiVersion), VK_VERSION_PATCH(props.properties.apiVersion),\n\t\t\t\tgpuidx, type, vendor, props.properties.deviceName,\n\t\t\t\tVK_VERSION_MAJOR(props.properties.driverVersion), VK_VERSION_MINOR(props.properties.driverVersion), VK_VERSION_PATCH(props.properties.driverVersion)\n\t\t\t\t);\n\t\t}\n\t}\n\n\t//figure out which of the device's queue's we're going to use\n\t{\n\t\tuint32_t queue_count, i;\n\t\tvkGetPhysicalDeviceQueueFamilyProperties(vk.gpu, &queue_count, NULL);\n\t\tqueueprops = malloc(sizeof(VkQueueFamilyProperties)*queue_count);\t//Oh how I wish I was able to use C99.\n\t\tvkGetPhysicalDeviceQueueFamilyProperties(vk.gpu, &queue_count, queueprops);\n\n\t\tvk.queuefam[0] = ~0u;\n\t\tvk.queuefam[1] = ~0u;\n\t\tvk.queuenum[0] = 0;\n\t\tvk.queuenum[1] = 0;\n\n\t\t/*\n\t\t//try to find a 'dedicated' present queue\n\t\tfor (i = 0; i < queue_count; i++)\n\t\t{\n\t\t\tVkBool32 supportsPresent = FALSE;\n\t\t\tVkAssert(vkGetPhysicalDeviceSurfaceSupportKHR(vk.gpu, i, vk.surface, &supportsPresent));\n\n\t\t\tif (supportsPresent && !(queueprops[i].queueFlags & VK_QUEUE_GRAPHICS_BIT))\n\t\t\t{\n\t\t\t\tvk.queuefam[1] = i;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tif (vk.queuefam[1] != ~0u)\n\t\t{\t//try to find a good graphics queue\n\t\t\tfor (i = 0; i < queue_count; i++)\n\t\t\t{\n\t\t\t\tif (queueprops[i].queueFlags & VK_QUEUE_GRAPHICS_BIT)\n\t\t\t\t{\n\t\t\t\t\tvk.queuefam[0] = i;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse*/\n\t\t{\n\t\t\tfor (i = 0; i < queue_count; i++)\n\t\t\t{\n\t\t\t\tVkBool32 supportsPresent = false;\n\t\t\t\tif (!vk.khr_swapchain)\n\t\t\t\t\tsupportsPresent = true;\t//won't be used anyway.\n\t\t\t\telse\n\t\t\t\t\tVkAssert(vkGetPhysicalDeviceSurfaceSupportKHR(vk.gpu, i, vk.surface, &supportsPresent));\n\n\t\t\t\tif ((queueprops[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) && supportsPresent)\n\t\t\t\t{\n\t\t\t\t\tvk.queuefam[0] = i;\n\t\t\t\t\tvk.queuefam[1] = i;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\telse if (vk.queuefam[0] == ~0u && (queueprops[i].queueFlags & VK_QUEUE_GRAPHICS_BIT))\n\t\t\t\t\tvk.queuefam[0] = i;\n\t\t\t\telse if (vk.queuefam[1] == ~0u && supportsPresent)\n\t\t\t\t\tvk.queuefam[1] = i;\n\t\t\t}\n\t\t}\n\n\n\t\tif (vk.queuefam[0] == ~0u || vk.queuefam[1] == ~0u)\n\t\t{\n\t\t\tif (ignorequeuebugs && queue_count>0)\n\t\t\t{\n\t\t\t\tvk.queuefam[0] = vk.queuefam[1] = 0;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tfree(queueprops);\n\t\t\t\tCon_Printf(CON_ERROR\"vulkan: unable to find suitable queues\\n\");\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t}\n\n\t{\n\t\tuint32_t extcount = 0, i;\n\t\tVkExtensionProperties *ext;\n\t\tvkEnumerateDeviceExtensionProperties(vk.gpu, NULL, &extcount, NULL);\n\t\text = malloc(sizeof(*ext)*extcount);\n\t\tvkEnumerateDeviceExtensionProperties(vk.gpu, NULL, &extcount, ext);\n\t\tfor (i = 0; i < extcount; i++)\n\t\t\tCon_DLPrintf(2, \" vkd: %s\\n\", ext[i].extensionName);\n\t\twhile (extcount --> 0)\n\t\t{\n\t\t\tfor (e = 0; e < countof(knowndevexts); e++)\n\t\t\t{\n\t\t\t\tif (!strcmp(ext[extcount].extensionName, knowndevexts[e].name))\n\t\t\t\t{\n\t\t\t\t\tif (knowndevexts[e].var)\n\t\t\t\t\t\t*knowndevexts[e].flag = !!knowndevexts[e].var->ival || (!*knowndevexts[e].var->string && knowndevexts[e].def);\n\t\t\t\t\tknowndevexts[e].supported = true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tfree(ext);\n\t}\n\n#ifdef VK_KHR_ray_query\n\tif ((vk.khr_ray_query && !vk.khr_acceleration_structure) || vk.apiversion < VK_API_VERSION_1_2)\n\t\tvk.khr_ray_query = false;\t//doesn't make sense.\n#endif\n#ifdef VK_KHR_acceleration_structure\n\tif ((vk.khr_acceleration_structure && !vk.khr_ray_query) || vk.apiversion < VK_API_VERSION_1_2)\n\t\tvk.khr_acceleration_structure = false;\t//not useful.\n#endif\n#ifdef VK_KHR_fragment_shading_rate\n\tif (vk.apiversion < VK_API_VERSION_1_2)\t//too lazy to check its requesite extensions. vk12 is enough.\n\t\tvk.khr_fragment_shading_rate = false;\n#endif\n\n\t{\n\t\tconst char *devextensions[1+countof(knowndevexts)];\n\t\tsize_t numdevextensions = 0;\n\t\tfloat queue_priorities[2] = {0.8, 1.0};\n\t\tVkDeviceQueueCreateInfo queueinf[2] = {{VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO},{VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO}};\n\t\tVkDeviceCreateInfo devinf = {VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO};\n\t\tVkPhysicalDeviceFeatures features;\n\t\tVkPhysicalDeviceFeatures avail;\n\t\tvoid *next = NULL;\n#ifdef VK_KHR_fragment_shading_rate\n\t\tVkPhysicalDeviceFragmentShadingRateFeaturesKHR shadingrate = {VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADING_RATE_FEATURES_KHR};\n#endif\n#ifdef VK_KHR_ray_query\n\t\tVkPhysicalDeviceRayQueryFeaturesKHR rayquery = {VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_QUERY_FEATURES_KHR};\n#endif\n#ifdef VK_KHR_acceleration_structure\n\t\tVkPhysicalDeviceAccelerationStructureFeaturesKHR accelstruct = {VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ACCELERATION_STRUCTURE_FEATURES_KHR};\n#endif\n#ifdef VK_API_VERSION_1_2\n\t\tVkPhysicalDeviceVulkan12Features vk12features = {VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES};\n#endif\n\t\tmemset(&features, 0, sizeof(features));\n\n\t\tvkGetPhysicalDeviceFeatures(vk.gpu, &avail);\n\n\t\t//try to enable whatever we can use, if we can.\n\t\tfeatures.robustBufferAccess\t\t\t= avail.robustBufferAccess;\n\t\tfeatures.textureCompressionBC\t\t= avail.textureCompressionBC;\n\t\tfeatures.textureCompressionETC2\t\t= avail.textureCompressionETC2;\n\t\tfeatures.textureCompressionASTC_LDR\t= avail.textureCompressionASTC_LDR;\n\t\tfeatures.samplerAnisotropy\t\t\t= avail.samplerAnisotropy;\n\t\tfeatures.geometryShader\t\t\t\t= avail.geometryShader;\n\t\tfeatures.tessellationShader\t\t\t= avail.tessellationShader;\n\n\t\t//Add in the extensions we support\n\t\tfor (e = 0; e < countof(knowndevexts); e++)\n\t\t{\t//prints are to let the user know what's going on. only warn if its explicitly enabled\n\t\t\tif (knowndevexts[e].superseeded && *knowndevexts[e].superseeded)\n\t\t\t{\n\t\t\t\tCon_DPrintf(\"Superseeded %s.\\n\", knowndevexts[e].name);\n\t\t\t\t*knowndevexts[e].flag = false;\n\t\t\t}\n\t\t\telse if (*knowndevexts[e].flag)\n\t\t\t{\n\t\t\t\tCon_DPrintf(\"Using %s.\\n\", knowndevexts[e].name);\n\t\t\t\tdevextensions[numdevextensions++] = knowndevexts[e].name;\n\t\t\t}\n\t\t\telse if (knowndevexts[e].var && knowndevexts[e].var->ival)\n\t\t\t\tCon_Printf(CON_WARNING\"unable to enable %s extension.%s\\n\", knowndevexts[e].name, knowndevexts[e].warningtext?knowndevexts[e].warningtext:\"\");\n\t\t\telse if (knowndevexts[e].supported)\n\t\t\t\tCon_DPrintf(\"Ignoring %s.\\n\", knowndevexts[e].name);\n\t\t\telse\n\t\t\t\tCon_DPrintf(\"Unavailable %s.\\n\", knowndevexts[e].name);\n\t\t}\n\n\t\tqueueinf[0].pNext = NULL;\n\t\tqueueinf[0].queueFamilyIndex = vk.queuefam[0];\n\t\tqueueinf[0].queueCount = 1;\n\t\tqueueinf[0].pQueuePriorities = &queue_priorities[0];\n\t\tqueueinf[1].pNext = NULL;\n\t\tqueueinf[1].queueFamilyIndex = vk.queuefam[1];\n\t\tqueueinf[1].queueCount = 1;\n\t\tqueueinf[1].pQueuePriorities = &queue_priorities[1];\n\n\t\tif (vk.queuefam[0] == vk.queuefam[1])\n\t\t{\n\t\t\tdevinf.queueCreateInfoCount = 1;\n\n\t\t\tif (queueprops[queueinf[0].queueFamilyIndex].queueCount >= 2 && vk_dualqueue.ival)\n\t\t\t{\n\t\t\t\tqueueinf[0].queueCount = 2;\n\t\t\t\tvk.queuenum[1] = 1; \n\t\t\t\tCon_DPrintf(\"Using duel queue\\n\");\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tqueueinf[0].queueCount = 1;\n\t\t\t\tif (vk.khr_swapchain)\n\t\t\t\t\tvk.dopresent = VK_DoPresent;\t//can't split submit+present onto different queues, so do these on a single thread.\n\t\t\t\tCon_DPrintf(\"Using single queue\\n\");\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tdevinf.queueCreateInfoCount = 2;\n\t\t\tCon_DPrintf(\"Using separate queue families\\n\");\n\t\t}\n\n\t\tfree(queueprops);\n\n\t\tdevinf.pQueueCreateInfos = queueinf;\n\t\tdevinf.enabledLayerCount = inst_info.enabledLayerCount;//vklayercount;\n\t\tdevinf.ppEnabledLayerNames = vklayerlist;\n\t\tdevinf.enabledExtensionCount = numdevextensions;\n\t\tdevinf.ppEnabledExtensionNames = devextensions;\n\t\tdevinf.pEnabledFeatures = &features;\n\n#ifdef VK_KHR_fragment_shading_rate\n\t\tif (vk.khr_fragment_shading_rate)\n\t\t{\n\t\t\tshadingrate.pNext = next;\n\t\t\tnext = &shadingrate;\t//now linked\n\t\t\tshadingrate.pipelineFragmentShadingRate = true;\n\t\t\tshadingrate.primitiveFragmentShadingRate = false;\n\t\t\tshadingrate.attachmentFragmentShadingRate = false;\n\t\t}\n#endif\n#ifdef VK_KHR_ray_query\n\t\tif (vk.khr_ray_query)\n\t\t{\n\t\t\trayquery.pNext = next;\n\t\t\tnext = &rayquery;\t//now linked\n\t\t\trayquery.rayQuery = true;\n\t\t}\n#endif\n#ifdef VK_KHR_acceleration_structure\n\t\tif (vk.khr_acceleration_structure)\n\t\t{\n\t\t\taccelstruct.pNext = next;\n\t\t\tnext = &accelstruct;\t//now linked\n\t\t\taccelstruct.accelerationStructure = true;\n\t\t\taccelstruct.accelerationStructureCaptureReplay = false;\n\t\t\taccelstruct.accelerationStructureIndirectBuild = false;\n\t\t\taccelstruct.accelerationStructureHostCommands = false;\n\t\t\taccelstruct.descriptorBindingAccelerationStructureUpdateAfterBind = false;\n\n\t\t\tvk12features.bufferDeviceAddress = true;\t//we also need this feature.\n\t\t}\n#endif\n#ifdef VK_API_VERSION_1_2\n\t\tif (vk.apiversion >= VK_API_VERSION_1_2)\n\t\t{\n\t\t\tvk12features.pNext = next;\n\t\t\tnext = &vk12features;\n\t\t}\n#endif\n\t\tdevinf.pNext = next;\n\n#if 0\n\t\tif (vkEnumeratePhysicalDeviceGroupsKHR && vk_afr.ival)\n\t\t{\t\n\t\t\t//'Every physical device must be in exactly one device group'. So we can just use the first group that lists it and automatically get AFR.\n\t\t\tuint32_t gpugroups = 0;\n\t\t\tVkDeviceGroupDeviceCreateInfoKHX dgdci = {VK_STRUCTURE_TYPE_DEVICE_GROUP_DEVICE_CREATE_INFO_KHR};\n\n\t\t\tVkPhysicalDeviceGroupPropertiesKHR *groups;\n\t\t\tvkEnumeratePhysicalDeviceGroupsKHR(vk.instance, &gpugroups, NULL);\n\t\t\tgroups = malloc(sizeof(*groups)*gpugroups);\n\t\t\tvkEnumeratePhysicalDeviceGroupsKHR(vk.instance, &gpugroups, groups);\n\t\t\tfor (i = 0; i < gpugroups; i++)\n\t\t\t{\n\t\t\t\tfor (j = 0; j < groups[i].physicalDeviceCount; j++)\n\t\t\t\t\tif (groups[i].physicalDevices[j] == vk.gpu)\n\t\t\t\t\t{\n\t\t\t\t\t\tdgdci.physicalDeviceCount = groups[i].physicalDeviceCount;\n\t\t\t\t\t\tdgdci.pPhysicalDevices = groups[i].physicalDevices;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\tif (dgdci.physicalDeviceCount > 1)\n\t\t\t{\n\t\t\t\tvk.subdevices = dgdci.physicalDeviceCount;\n\t\t\t\tdgdci.pNext = devinf.pNext;\n\t\t\t\tdevinf.pNext = &dgdci;\n\t\t\t}\n\t\t\n\t\t\terr = vkCreateDevice(vk.gpu, &devinf, NULL, &vk.device);\n\n\t\t\tfree(groups);\n\t\t}\n\t\telse\n#endif\n\t\t\terr = vkCreateDevice(vk.gpu, &devinf, NULL, &vk.device);\n\n\t\tswitch(err)\n\t\t{\n\t\tcase VK_ERROR_INCOMPATIBLE_DRIVER:\n\t\t\tCon_TPrintf(CON_ERROR\"VK_ERROR_INCOMPATIBLE_DRIVER: please install an appropriate vulkan driver\\n\");\n\t\t\treturn false;\n\t\tcase VK_ERROR_EXTENSION_NOT_PRESENT:\n\t\tcase VK_ERROR_FEATURE_NOT_PRESENT:\n\t\tcase VK_ERROR_INITIALIZATION_FAILED:\n\t\tcase VK_ERROR_DEVICE_LOST:\n        case VK_ERROR_OUT_OF_HOST_MEMORY:\n        case VK_ERROR_OUT_OF_DEVICE_MEMORY:\n\t\t\tCon_Printf(CON_ERROR\"%s: something on a system level is probably misconfigured\\n\", VK_VKErrorToString(err));\n\t\t\treturn false;\n\t\tdefault:\n\t\t\tCon_Printf(CON_ERROR\"Unknown vulkan device creation error: %x\\n\", err);\n\t\t\treturn false;\n\t\tcase VK_SUCCESS:\n\t\t\tbreak;\n\t\t}\n\t}\n\n#ifdef VK_NO_PROTOTYPES\n\tvkGetDeviceProcAddr = (PFN_vkGetDeviceProcAddr)vkGetInstanceProcAddr(vk.instance, \"vkGetDeviceProcAddr\");\n#define VKFunc(n) vk##n = (PFN_vk##n)vkGetDeviceProcAddr(vk.device, \"vk\"#n);\n\tVKDevFuncs\n#ifdef VK_KHR_acceleration_structure\n\tif (vk.khr_acceleration_structure) { VKAccelStructFuncs }\n#endif\n#undef VKFunc\n#endif\n\n\tvkGetDeviceQueue(vk.device, vk.queuefam[0], vk.queuenum[0], &vk.queue_render);\n\tvkGetDeviceQueue(vk.device, vk.queuefam[1], vk.queuenum[1], &vk.queue_present);\n\n\tvrsetup.vk.instance = vk.instance;\n\tvrsetup.vk.device = vk.device;\n\tvrsetup.vk.physicaldevice = vk.gpu;\n\tvrsetup.vk.queuefamily = vk.queuefam[1];\n\tvrsetup.vk.queueindex = vk.queuenum[1];\n\tif (vid.vr)\n\t{\n\t\tif (!vid.vr->Init(&vrsetup, info))\n\t\t{\n\t\t\tvid.vr->Shutdown();\n\t\t\tvid.vr = NULL;\n\t\t}\n\t}\n\n\tvkGetPhysicalDeviceMemoryProperties(vk.gpu, &vk.memory_properties);\n\n\t{\n\t\tVkCommandPoolCreateInfo cpci = {VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO};\n\t\tcpci.queueFamilyIndex = vk.queuefam[0];\n\t\tcpci.flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT|VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;\n\t\tVkAssert(vkCreateCommandPool(vk.device, &cpci, vkallocationcb, &vk.cmdpool));\n\t}\n\n\t\n\tsh_config.progpath = \"vulkan/%s.fvb\";\n\tsh_config.blobpath = NULL;\t//just use general pipeline cache instead.\n\tsh_config.shadernamefmt = NULL;//\"_vulkan\";\n\n\tsh_config.progs_supported = true;\n\tsh_config.progs_required = true;\n\tsh_config.minver = -1;\n\tsh_config.maxver = -1;\n\n\tsh_config.texture_allow_block_padding = true;\n\tsh_config.texture_non_power_of_two = true;\t//is this always true?\n\tsh_config.texture_non_power_of_two_pic = true;\t//probably true...\n\tsh_config.npot_rounddown = false;\n\tsh_config.tex_env_combine = false;\t\t//fixme: figure out what this means...\n\tsh_config.nv_tex_env_combine4 = false;\t//fixme: figure out what this means...\n\tsh_config.env_add = false;\t\t\t\t//fixme: figure out what this means...\n\n\tsh_config.can_mipcap = true;\n\tsh_config.havecubemaps = true;\n\n\tVK_CheckTextureFormats();\n\n\n\tsh_config.pDeleteProg = NULL;\n\tsh_config.pLoadBlob = NULL;\n\tsh_config.pCreateProgram = NULL;\n\tsh_config.pValidateProgram = NULL;\n\tsh_config.pProgAutoFields = NULL;\n\n\tif (info->depthbits == 16 && sh_config.texfmt[PTI_DEPTH16])\n\t\tvk.depthformat = VK_FORMAT_D16_UNORM;\n\telse if (info->depthbits == 32 && sh_config.texfmt[PTI_DEPTH32])\n\t\tvk.depthformat = VK_FORMAT_D32_SFLOAT;\n//\telse if (info->depthbits == 32 && sh_config.texfmt[PTI_DEPTH32_8])\n//\t\tvk.depthformat = VK_FORMAT_D32_SFLOAT_S8_UINT;\n\telse if (info->depthbits == 24 && sh_config.texfmt[PTI_DEPTH24_8])\n\t\tvk.depthformat = VK_FORMAT_D24_UNORM_S8_UINT;\n\telse if (info->depthbits == 24 && sh_config.texfmt[PTI_DEPTH24])\n\t\tvk.depthformat = VK_FORMAT_X8_D24_UNORM_PACK32;\n\n\telse if (sh_config.texfmt[PTI_DEPTH24])\n\t\tvk.depthformat = VK_FORMAT_X8_D24_UNORM_PACK32;\n\telse if (sh_config.texfmt[PTI_DEPTH24_8])\n\t\tvk.depthformat = VK_FORMAT_D24_UNORM_S8_UINT;\n\telse if (sh_config.texfmt[PTI_DEPTH32])\t//nvidia: \"Don’t use 32-bit floating point depth formats, due to the performance cost, unless improved precision is actually required\"\n\t\tvk.depthformat = VK_FORMAT_D32_SFLOAT;\n\telse\t//16bit depth is guarenteed in vulkan\n\t\tvk.depthformat = VK_FORMAT_D16_UNORM;\n\n#ifdef MULTITHREAD\n\tvk.submitcondition = Sys_CreateConditional();\n#endif\n\n\t{\n\t\tVkPipelineCacheCreateInfo pci = {VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO};\n\t\tqofs_t size = 0;\n\t\tpci.pInitialData = FS_MallocFile(\"vulkan.pcache\", FS_ROOT, &size);\n\t\tpci.initialDataSize = size;\n\t\tVkAssert(vkCreatePipelineCache(vk.device, &pci, vkallocationcb, &vk.pipelinecache));\n\t\tFS_FreeFile((void*)pci.pInitialData);\n\t}\n\n\tif (VK_CreateSwapChain())\n\t{\n\t\tvk.neednewswapchain = false;\n\n#ifdef MULTITHREAD\n\t\tif (vk.allowsubmissionthread && (vk_submissionthread.ival || !*vk_submissionthread.string))\n\t\t{\n\t\t\tvk.submitthread = Sys_CreateThread(\"vksubmission\", VK_Submit_Thread, NULL, THREADP_HIGHEST, 0);\n\t\t}\n#endif\n\t}\n\tif (info->srgb > 0 && (vid.flags & VID_SRGB_FB))\n\t\tvid.flags |= VID_SRGBAWARE;\n\n\tQ_snprintfz(info->subrenderer, sizeof(info->subrenderer), \"GPU%i\", gpuidx);\n\n\tif (!vk.khr_fragment_shading_rate)\n\t\tCvar_LockUnsupportedRendererCvar(&r_halfrate, \"0\");\n\tif (!vk.khr_ray_query)\n\t\tCvar_LockUnsupportedRendererCvar(&r_shadow_raytrace, \"0\");\n\treturn true;\n}\nvoid VK_Shutdown(void)\n{\n\tuint32_t i;\n\n\tVK_DestroySwapChain();\n\n\tfor (i = 0; i < countof(postproc); i++)\n\t\tVKBE_RT_Gen(&postproc[i], NULL, 0, 0, false, RT_IMAGEFLAGS);\n\tVKBE_RT_Gen_Cube(&vk_rt_cubemap, 0, false);\n\tVK_R_BloomShutdown();\n\n\tif (vk.cmdpool)\n\t\tvkDestroyCommandPool(vk.device, vk.cmdpool, vkallocationcb);\n\tVK_DestroyRenderPasses();\n\n\tif (vk.pipelinecache)\n\t{\n\t\tsize_t size;\n\t\tif (VK_SUCCESS == vkGetPipelineCacheData(vk.device, vk.pipelinecache, &size, NULL))\n\t\t{\n\t\t\tvoid *ptr = Z_Malloc(size);\t//valgrind says nvidia isn't initialising this.\n\t\t\tif (VK_SUCCESS == vkGetPipelineCacheData(vk.device, vk.pipelinecache, &size, ptr))\n\t\t\t\tFS_WriteFile(\"vulkan.pcache\", ptr, size, FS_ROOT);\n\t\t\tZ_Free(ptr);\n\t\t}\n\t\tvkDestroyPipelineCache(vk.device, vk.pipelinecache, vkallocationcb);\n\t\tvk.pipelinecache = VK_NULL_HANDLE;\n\t}\n\n\twhile(vk.mempools)\n\t{\n\t\tvoid *l;\n\t\tvkFreeMemory(vk.device, vk.mempools->memory, vkallocationcb);\n\t\tl = vk.mempools;\n\t\tvk.mempools = vk.mempools->next;\n\t\tZ_Free(l);\n\t}\n\n\tif (vk.device)\n\t\tvkDestroyDevice(vk.device, vkallocationcb);\n#ifdef VK_EXT_debug_utils\n\tif (vk_debugucallback)\n\t{\n\t\tvkDestroyDebugUtilsMessengerEXT(vk.instance, vk_debugucallback, vkallocationcb);\n\t\tvk_debugucallback = VK_NULL_HANDLE;\n\t}\n#endif\n#ifdef VK_EXT_debug_report\n\tif (vk_debugcallback)\n\t{\n\t\tvkDestroyDebugReportCallbackEXT(vk.instance, vk_debugcallback, vkallocationcb);\n\t\tvk_debugcallback = VK_NULL_HANDLE;\n\t}\n#endif\n\n\tif (vk.surface)\n\t\tvkDestroySurfaceKHR(vk.instance, vk.surface, vkallocationcb);\n\tif (vk.instance)\n\t\tvkDestroyInstance(vk.instance, vkallocationcb);\n#ifdef MULTITHREAD\n\tif (vk.submitcondition)\n\t\tSys_DestroyConditional(vk.submitcondition);\n#endif\n\n\tmemset(&vk, 0, sizeof(vk));\n\n#ifdef VK_NO_PROTOTYPES\n\t#define VKFunc(n) vk##n = NULL;\n\tVKFuncs\n\t#undef VKFunc\n#endif\n\n}\n#endif\n"
  },
  {
    "path": "engine/vk/vkrenderer.h",
    "content": "//#include \"glquake.h\"\n\n#if defined(_WIN32)\n\t#define WIN32_LEAN_AND_MEAN\n\t#define VK_USE_PLATFORM_WIN32_KHR\n\t#define VKInstWin32Funcs VKFunc(CreateWin32SurfaceKHR)\n#elif defined(ANDROID)\n\t#define VK_USE_PLATFORM_ANDROID_KHR\n\t#define VKInstXLibFuncs VKFunc(CreateAndroidSurfaceKHR)\n#elif defined(__linux__)\n\t#ifndef NO_X11\n\t\t#define VK_USE_PLATFORM_XLIB_KHR\n\t\t#define VKInstXLibFuncs VKFunc(CreateXlibSurfaceKHR)\n\n\t\t#define VK_USE_PLATFORM_XCB_KHR\n\t\t#define VKInstXCBFuncs VKFunc(CreateXcbSurfaceKHR)\n\t#endif\n\t#ifdef WAYLANDQUAKE\n\t\t#define VK_USE_PLATFORM_WAYLAND_KHR\n\t\t#define VKInstWaylandFuncs VKFunc(CreateWaylandSurfaceKHR)\n\t#endif\n#elif defined(__FreeBSD__) || defined(__OpenBSD__)\n\t#define VK_USE_PLATFORM_XLIB_KHR\n\t#define VKInstXLibFuncs VKFunc(CreateXlibSurfaceKHR)\n\n\t#define VK_USE_PLATFORM_XCB_KHR\n\t#define VKInstXCBFuncs VKFunc(CreateXcbSurfaceKHR)\n#endif\n\n#define VK_NO_PROTOTYPES\n#include <vulkan/vulkan.h>\n\n#if defined(_MSC_VER) && !defined(UINT64_MAX)\n#define UINT64_MAX _UI64_MAX\n#ifndef _UI64_MAX\n#define _UI64_MAX 0xffffffffffffffffui64\n#endif\n#endif\n\n#ifndef VKInstWin32Funcs\n#define VKInstWin32Funcs\n#endif\n#ifndef VKInstXLibFuncs\n#define VKInstXLibFuncs\n#endif\n#ifndef VKInstXCBFuncs\n#define VKInstXCBFuncs\n#endif\n#ifndef VKInstWaylandFuncs\n#define VKInstWaylandFuncs\n#endif\n#define VKInstArchFuncs VKInstWin32Funcs VKInstXLibFuncs VKInstXCBFuncs VKInstWaylandFuncs\n\n#ifdef VK_EXT_debug_utils\n#define VKDebugFuncs\t\\\n\tVKFunc(SetDebugUtilsObjectNameEXT)\n#else\n#define VKDebugFuncs\n#endif\n\n//funcs needed for creating an instance\n#define VKInstFuncs \\\n\tVKFunc(EnumerateInstanceLayerProperties)\t\t\\\n\tVKFunc(EnumerateInstanceExtensionProperties)\t\\\n\tVKFunc(EnumerateInstanceVersion)\t\t\t\t\\\n\tVKFunc(CreateInstance)\n\n//funcs specific to an instance\n#define VKInst2Funcs \\\n\tVKFunc(EnumeratePhysicalDevices)\t\t\t\t\\\n\tVKFunc(EnumerateDeviceExtensionProperties)\t\t\\\n\tVKFunc(GetPhysicalDeviceProperties)\t\t\t\t\\\n\tVKFunc(GetPhysicalDeviceQueueFamilyProperties)\t\\\n\tVKFunc(GetPhysicalDeviceSurfaceSupportKHR)\t\t\\\n\tVKFunc(GetPhysicalDeviceSurfaceFormatsKHR)\t\t\\\n\tVKFunc(GetPhysicalDeviceSurfacePresentModesKHR)\t\\\n\tVKFunc(GetPhysicalDeviceSurfaceCapabilitiesKHR)\t\\\n\tVKFunc(GetPhysicalDeviceMemoryProperties)\t\t\\\n\tVKFunc(GetPhysicalDeviceFormatProperties)\t\t\\\n\tVKFunc(GetPhysicalDeviceFeatures)\t\t\t\t\\\n\tVKFunc(DestroySurfaceKHR)\t\t\t\t\t\t\\\n\tVKFunc(CreateDevice)\t\t\t\t\t\t\t\\\n\tVKFunc(DestroyInstance)\t\t\t\t\t\t\t\\\n\tVKDebugFuncs\t\t\t\t\t\t\t\t\t\\\n\tVKInstArchFuncs\n\n//funcs specific to a device\n#define VKDevFuncs \\\n\tVKFunc(AcquireNextImageKHR)\t\t\t\\\n\tVKFunc(QueuePresentKHR)\t\t\t\t\\\n\tVKFunc(CreateSwapchainKHR)\t\t\t\\\n\tVKFunc(GetSwapchainImagesKHR)\t\t\\\n\tVKFunc(DestroySwapchainKHR)\t\t\t\\\n\tVKFunc(CmdBeginRenderPass)\t\t\t\\\n\tVKFunc(CmdEndRenderPass)\t\t\t\\\n\tVKFunc(CmdBindPipeline)\t\t\t\t\\\n\tVKFunc(CmdDrawIndexedIndirect)\t\t\\\n\tVKFunc(CmdDraw)\t\t\t\t\t\t\\\n\tVKFunc(CmdDrawIndexed)\t\t\t\t\\\n\tVKFunc(CmdSetViewport)\t\t\t\t\\\n\tVKFunc(CmdSetScissor)\t\t\t\t\\\n\tVKFunc(CmdBindDescriptorSets)\t\t\\\n\tVKFunc(CmdBindIndexBuffer)\t\t\t\\\n\tVKFunc(CmdBindVertexBuffers)\t\t\\\n\tVKFunc(CmdPushConstants)\t\t\t\\\n\tVKFunc(CmdPushDescriptorSetKHR)\t\t\\\n\tVKFunc(CmdClearAttachments)\t\t\t\\\n\tVKFunc(CmdClearColorImage)\t\t\t\\\n\tVKFunc(CmdClearDepthStencilImage)\t\\\n\tVKFunc(CmdCopyImage)\t\t\t\t\\\n\tVKFunc(CmdCopyBuffer)\t\t\t\t\\\n\tVKFunc(CmdCopyImageToBuffer)\t\t\\\n\tVKFunc(CmdCopyBufferToImage)\t\t\\\n\tVKFunc(CmdBlitImage)\t\t\t\t\\\n\tVKFunc(CmdPipelineBarrier)\t\t\t\\\n\tVKFunc(CmdSetEvent)\t\t\t\t\t\\\n\tVKFunc(CmdResetEvent)\t\t\t\t\\\n\tVKFunc(CmdWaitEvents)\t\t\t\t\\\n\tVKFunc(CreateDescriptorSetLayout)\t\\\n\tVKFunc(DestroyDescriptorSetLayout)\t\\\n\tVKFunc(CreatePipelineLayout)\t\t\\\n\tVKFunc(DestroyPipelineLayout)\t\t\\\n\tVKFunc(CreateShaderModule)\t\t\t\\\n\tVKFunc(DestroyShaderModule)\t\t\t\\\n\tVKFunc(CreateGraphicsPipelines)\t\t\\\n\tVKFunc(DestroyPipeline)\t\t\t\t\\\n\tVKFunc(CreatePipelineCache)\t\t\t\\\n\tVKFunc(GetPipelineCacheData)\t\t\\\n\tVKFunc(DestroyPipelineCache)\t\t\\\n\tVKFunc(QueueSubmit)\t\t\t\t\t\\\n\tVKFunc(QueueWaitIdle)\t\t\t\t\\\n\tVKFunc(DeviceWaitIdle)\t\t\t\t\\\n\tVKFunc(BeginCommandBuffer)\t\t\t\\\n\tVKFunc(ResetCommandBuffer)\t\t\t\\\n\tVKFunc(EndCommandBuffer)\t\t\t\\\n\tVKFunc(DestroyDevice)\t\t\t\t\\\n\tVKFunc(GetDeviceQueue)\t\t\t\t\\\n\tVKFunc(GetBufferMemoryRequirements)\t\\\n\tVKFunc(GetImageMemoryRequirements)\t\\\n\tVKFunc(GetImageMemoryRequirements2KHR)\t\\\n\tVKFunc(GetImageSubresourceLayout)\t\\\n\tVKFunc(CreateFramebuffer)\t\t\t\\\n\tVKFunc(DestroyFramebuffer)\t\t\t\\\n\tVKFunc(CreateCommandPool)\t\t\t\\\n\tVKFunc(ResetCommandPool)\t\t\t\\\n\tVKFunc(DestroyCommandPool)\t\t\t\\\n\tVKFunc(CreateDescriptorPool)\t\t\\\n\tVKFunc(ResetDescriptorPool)\t\t\t\\\n\tVKFunc(DestroyDescriptorPool)\t\t\\\n\tVKFunc(AllocateDescriptorSets)\t\t\\\n\tVKFunc(CreateSampler)\t\t\t\t\\\n\tVKFunc(DestroySampler)\t\t\t\t\\\n\tVKFunc(CreateImage)\t\t\t\t\t\\\n\tVKFunc(DestroyImage)\t\t\t\t\\\n\tVKFunc(CreateBuffer)\t\t\t\t\\\n\tVKFunc(DestroyBuffer)\t\t\t\t\\\n\tVKFunc(AllocateMemory)\t\t\t\t\\\n\tVKFunc(FreeMemory)\t\t\t\t\t\\\n\tVKFunc(BindBufferMemory)\t\t\t\\\n\tVKFunc(BindImageMemory)\t\t\t\t\\\n\tVKFunc(MapMemory)\t\t\t\t\t\\\n\tVKFunc(FlushMappedMemoryRanges)\t\t\\\n\tVKFunc(UnmapMemory)\t\t\t\t\t\\\n\tVKFunc(UpdateDescriptorSets)\t\t\\\n\tVKFunc(AllocateCommandBuffers)\t\t\\\n\tVKFunc(FreeCommandBuffers)\t\t\t\\\n\tVKFunc(CreateRenderPass)\t\t\t\\\n\tVKFunc(DestroyRenderPass)\t\t\t\\\n\tVKFunc(CreateSemaphore)\t\t\t\t\\\n\tVKFunc(DestroySemaphore)\t\t\t\\\n\tVKFunc(CreateFence)\t\t\t\t\t\\\n\tVKFunc(GetFenceStatus)\t\t\t\t\\\n\tVKFunc(WaitForFences)\t\t\t\t\\\n\tVKFunc(ResetFences)\t\t\t\t\t\\\n\tVKFunc(DestroyFence)\t\t\t\t\\\n\tVKFunc(CreateImageView)\t\t\t\t\\\n\tVKFunc(DestroyImageView)\n\n//funcs for ray query's acceleration structures\n#ifdef VK_KHR_acceleration_structure\n#define VKAccelStructFuncs \\\n\tVKFunc(GetBufferDeviceAddress)/*1.2*/\t\t\t\t\\\n\tVKFunc(GetAccelerationStructureBuildSizesKHR)\t\t\\\n\tVKFunc(CreateAccelerationStructureKHR)\t\t\t\t\\\n\tVKFunc(GetAccelerationStructureDeviceAddressKHR)\t\\\n\tVKFunc(DestroyAccelerationStructureKHR)\t\t\t\t\\\n\tVKFunc(CmdBuildAccelerationStructuresKHR)\n#else\n#define VKAccelStructFuncs\n#endif\n\n//all vulkan funcs\n#define VKFuncs \\\n\tVKInstFuncs\t\t\\\n\tVKInst2Funcs\t\\\n\tVKDevFuncs\t\t\\\n\tVKAccelStructFuncs \\\n\tVKFunc(GetInstanceProcAddr)\\\n\tVKFunc(GetDeviceProcAddr)\n\n\n#ifdef VK_NO_PROTOTYPES\n\t#define VKFunc(n) extern PFN_vk##n vk##n;\n\tVKFuncs\n\t#undef VKFunc\n#else\n//\t#define VKFunc(n) static const PFN_vk##n vk##n = vk##n;\n//\tVKFuncs\n//\t#undef VKFunc\n#endif\n\n#define vkallocationcb NULL\n#ifdef _DEBUG\n#define VkAssert(f) do {VkResult err = f; if (err) Sys_Error(\"%s == %s\", #f, VK_VKErrorToString(err)); } while(0)\n#define VkWarnAssert(f) do {VkResult err = f; if (err) Con_Printf(\"%s == %s\\n\", #f, VK_VKErrorToString(err)); } while(0)\n#else\n#define VkAssert(f) f\n#define VkWarnAssert(f) f\n#endif\n\ntypedef struct\n{\n\tstruct vk_mempool_s *pool;\n\tVkDeviceMemory memory;\n\tsize_t size;\n\tsize_t offset;\n} vk_poolmem_t;\n\ntypedef struct vk_image_s\n{\n\tVkImage image;\n\tvk_poolmem_t mem;\n\tVkImageView view;\n\tVkSampler sampler;\n\tVkImageLayout layout;\n\n\tVkFormat vkformat;\n\tuint32_t width;\n\tuint32_t height;\n\tuint32_t layers;\n\tuint32_t mipcount;\n\tuint32_t encoding;\n\tuint32_t type;\t//PTI_2D/3D/CUBE\n} vk_image_t;\nenum dynbuf_e\n{\n\tDB_VBO,\n\tDB_EBO,\n\tDB_UBO,\n\tDB_STAGING,\n#ifdef VK_KHR_acceleration_structure\n\tDB_ACCELERATIONSTRUCT,\n\tDB_ACCELERATIONSCRATCH,\n\tDB_ACCELERATIONMESHDATA,\n\tDB_ACCELERATIONINSTANCE,\n#endif\n\tDB_MAX\n};\nstruct vk_rendertarg\n{\n\tVkCommandBuffer cbuf;\t//cbuf allocated for this render target.\n\tVkFramebuffer framebuffer;\n\tvk_image_t colour, depth, mscolour;\n\n\timage_t q_colour, q_depth, q_mscolour;\t//extra sillyness...\n\n\tuint32_t width;\n\tuint32_t height;\n\n\tuint32_t rpassflags;\n\tqboolean depthcleared;\t//starting a new gameview needs cleared depth relative to other views, but the first probably won't.\n\n\tVkRenderPassBeginInfo restartinfo;\n\tVkSemaphore presentsemaphore;\n\tqboolean firstuse;\n\tqboolean externalimage;\n\n\tstruct vk_rendertarg *prevtarg;\n};\nstruct vk_rendertarg_cube\n{\n\tuint32_t size;\n\timage_t q_colour, q_depth;\t//extra sillyness...\n\tvk_image_t colour, depth;\n\tstruct vk_rendertarg face[6];\n};\n\n#define VQ_RENDER 0\n#define VQ_PRESENT 1\n#define VQ_ALTRENDER 2\n#define VQ_ALTRENDER_COUNT 16\n#define VQ_COUNT 3\nextern struct vulkaninfo_s\n{\n\tunsigned short\ttriplebuffer;\n\tqboolean\t\tvsync;\n\tqboolean\t\tallowsubmissionthread;\n\n\tqboolean\t\tkhr_swapchain;\t\t\t\t\t//aka: not headless. we're actually rendering stuff!\n\tqboolean\t\tkhr_get_memory_requirements2;\t//slightly richer info\n\tqboolean\t\tkhr_dedicated_allocation;\t\t//standardised version of the above where the driver decides whether a resource is worth a dedicated allocation.\n\tqboolean\t\tkhr_push_descriptor;\t\t\t//more efficient descriptor streaming\n\tqboolean\t\tamd_rasterization_order;\t\t//allows primitives to draw in any order\n#ifdef VK_EXT_astc_decode_mode\n\tqboolean\t\text_astc_decode_mode;\t\t\t//small perf boost\n#endif\n#ifdef VK_KHR_fragment_shading_rate\n\tqboolean\t\tkhr_fragment_shading_rate;\t\t//small perf boost. probably more useful for battery.\n#endif\n#ifdef VK_KHR_ray_query\n\tqboolean\t\tkhr_ray_query;\n#endif\n#ifdef VK_KHR_acceleration_structure\n\tqboolean\t\tkhr_acceleration_structure;\n\tqboolean\t\tkhr_deferred_host_operations;\t//need to enable it, we don't make use of it though.\n#endif\n\n\tVkInstance instance;\n\tVkDevice device;\n\tVkPhysicalDevice gpu;\n\tuint32_t apiversion;\t//the device api, capped by instance version, capped by our own version... sigh\n\tVkSurfaceKHR surface;\n\tuint32_t queuefam[VQ_COUNT];\n\tuint32_t queuenum[VQ_COUNT];\n\tVkQueue queue_render;\n\tVkQueue queue_present;\n\tVkQueue queue_alt[1];\n\tVkPhysicalDeviceMemoryProperties memory_properties;\n\tVkCommandPool cmdpool;\n\tVkPhysicalDeviceLimits limits;\n\n\t//we have a ringbuffer for acquires\n#define ACQUIRELIMIT 8\n\tVkSemaphore acquiresemaphores[ACQUIRELIMIT];\n\tVkFence acquirefences[ACQUIRELIMIT];\n\tuint32_t acquirebufferidx[ACQUIRELIMIT];\n\tunsigned int acquirenext;\t\t\t//first usable buffer, but we still need to wait on its fence (accessed on main thread).\n\tvolatile unsigned int acquirelast;\t//last buffer that we have successfully asked to aquire (set inside the submission thread).\n\t//acquirenext <= acquirelast, acquirelast-acquirenext<=ACQUIRELIMIT\n\n\tVkPipelineCache pipelinecache;\n\n\tstruct vk_fencework \n\t{\n\t\tVkFence fence;\n\t\tstruct vk_fencework *next;\n\t\tvoid (*Passed) (void*);\n\t\tVkCommandBuffer cbuf;\n\t} *fencework, *fencework_last;\t//callback for each fence as its passed. mostly for loading code or freeing memory.\n\n\tint filtermip[3];\n\tint filterpic[3];\n\tint mipcap[2];\n\tfloat lodbias;\n\tfloat max_anistophy;\t//limits.maxSamplerAnistrophy\n\n\tstruct vk_mempool_s\n\t{\n\t\tstruct vk_mempool_s *next;\n\n\t\tuint32_t memtype;\n\t\tVkDeviceMemory memory;\n\n\t\t//FIXME: replace with an ordered list of free blocks.\n\t\tVkDeviceSize gaps;\n\t\tVkDeviceSize memoryoffset;\n\t\tVkDeviceSize memorysize;\n\t} *mempools;\n\n\tstruct descpool\n\t{\n\t\tVkDescriptorPool pool;\n\t\tint availsets;\n\t\tint totalsets;\n\t\tstruct descpool *next;\n\t} *descpool;\n\tstruct dynbuffer\n\t{\n\t\tsize_t flushed;\t//size already copied to the gpu\n\t\tsize_t offset;\t//size written by the cpu (that might not yet be flushed)\n\t\tsize_t size;\t//maximum buffer size\n\t\tsize_t align;\n\t\tqboolean stagingcoherent;\n\t\tVkBuffer stagingbuf;\n\t\tVkDeviceMemory stagingmemory;\n\t\tVkBuffer devicebuf;\n\t\tVkDeviceMemory devicememory;\n\t\tVkBuffer renderbuf;\t//either staging or device. this is the buffer that we tell vulkan about\n\t\tvoid *ptr;\n\n\t\tstruct dynbuffer *next;\n\t} *dynbuf[DB_MAX];\n\tstruct vk_rendertarg *backbufs;\n\tstruct vk_rendertarg *rendertarg;\n\tstruct vkframe {\n\t\tstruct vkframe *next;\n\t\tstruct dynbuffer *dynbufs[DB_MAX];\n\t\tstruct descpool *descpools;\n\t\tVkSemaphore acquiresemaphore;\n\t\tVkCommandBuffer *cbufs;\n\t\tsize_t\t\t\tnumcbufs;\n\t\tsize_t\t\t\tmaxcbufs;\n\t\tVkFence finishedfence;\n\t\tstruct vk_frameend {\n\t\t\tstruct vk_frameend *next;\n\t\t\tvoid (*FrameEnded) (void*);\n\t\t} *frameendjobs;\n\n\t\tstruct vk_rendertarg *backbuf;\n\t} *frame, *unusedframes;\n\tstruct vk_frameend *frameendjobs;\n\tuint32_t backbuf_count;\n\n#define RP_RESUME\t\t0\n#define RP_DEPTHCLEAR\t1\t//\n#define RP_FULLCLEAR\t2\n#define RP_DEPTHONLY\t3\t//shadowmaps (clears depth)\n#define RP_MULTISAMPLE\t(1u<<2)\n#define RP_PRESENTABLE\t(1u<<3)\n#define RP_FP16\t\t\t(1u<<4)\n#define RP_VR\t\t\t(1u<<5)\t//potentially a different colour format.\n\tVkRenderPass renderpass[1u<<6];\n\tVkSwapchainKHR swapchain;\n\tuint32_t bufferidx;\n\n\tVkSampleCountFlagBits multisamplebits;\n\tVkFormat depthformat;\n\tVkFormat backbufformat;\n\tqboolean srgbcapable;\n\n\tqboolean neednewswapchain;\t//something changed that invalidates the old one.\n\tqboolean devicelost;\t\t//we seriously fucked up somewhere. or the gpu is shite.\n\n\tstruct vksamplers_s\n\t{\n\t\tVkSampler samp;\n\t\tunsigned int usages;\t//refcounted.\n\t\tunsigned int flags;\n\t\tVkSamplerCreateInfo props;\n\t\tstruct vksamplers_s *next;\n\t\tstruct vksamplers_s **link;\n\t} *samplers;\n\n\tstruct vkwork_s\n\t{\n\t\tstruct vkwork_s *next;\n\t\tVkQueue queue;\n\t\tVkCommandBuffer cmdbuf;\n\t\tVkSemaphore semwait;\n\t\tVkPipelineStageFlags semwaitstagemask;\n\t\tVkSemaphore semsignal;\n\t\tVkFence fencesignal;\n\n\t\tstruct vk_fencework *fencedwork;\n\t\tstruct vkframe *present;\n\t} *work;\n\tvoid *submitthread;\n\tvoid *submitcondition;\n\tvoid (*dopresent)(struct vkframe *theframe);\n\n\ttexid_t sourcecolour;\n\ttexid_t sourcedepth;\n\n\tshader_t *scenepp_waterwarp;\n\tshader_t *scenepp_antialias;\n\tshader_t *scenepp_rescale;\n} vk;\n\nstruct pipeline_s\n{\n\tstruct pipeline_s *next;\n\tunsigned int permu:16;\t//matches the permutation (masked by permutations that are supposed to be supported)\n\tunsigned int flags:16;\t//matches the shader flags (cull etc)\n\tunsigned int blendbits; //matches blend state.\n\tVkPipeline pipeline;\n};\n\nuint32_t vk_find_memory_try(uint32_t typeBits, VkFlags requirements_mask);\nuint32_t vk_find_memory_require(uint32_t typeBits, VkFlags requirements_mask);\n\nvoid VK_DoPresent(struct vkframe *theframe);\n\nqboolean VK_EnumerateDevices (void *usercontext, void(*callback)(void *context, const char *devicename, const char *outputname, const char *desc), const char *descprefix, PFN_vkGetInstanceProcAddr vk_GetInstanceProcAddr);\nqboolean VK_Init(rendererstate_t *info, const char *const*sysextname, unsigned int numsysext, qboolean (*createSurface)(void), void (*dopresent)(struct vkframe *theframe));\nvoid VK_Shutdown(void);\n\nvoid VK_R_BloomBlend (texid_t source, int x, int y, int w, int h);\nvoid VK_R_BloomShutdown(void);\nqboolean R_CanBloom(void);\n\nstruct programshared_s;\nstruct programpermu_s;\nqboolean VK_LoadGLSL(struct programshared_s *prog, struct programpermu_s *permu, int ver, const char **precompilerconstants, const char *vert, const char *tcs, const char *tes, const char *geom, const char *frag, qboolean noerrors, vfsfile_t *blobfile);\n\nVkCommandBuffer VK_AllocFrameCBuf(void);\nvoid VK_Submit_Work(VkCommandBuffer cmdbuf, VkSemaphore semwait, VkPipelineStageFlags semwaitstagemask, VkSemaphore semsignal, VkFence fencesignal, struct vkframe *presentframe, struct vk_fencework *fencedwork);\n\nvoid VKBE_Init(void);\nvoid VKBE_InitFramePools(struct vkframe *frame);\nvoid VKBE_RestartFrame(void);\nvoid VKBE_FlushDynamicBuffers(void);\nvoid VKBE_Set2D(qboolean twodee);\nvoid VKBE_ShutdownFramePools(struct vkframe *frame);\nvoid VKBE_Shutdown(void);\nvoid VKBE_SelectMode(backendmode_t mode);\nvoid VKBE_DrawMesh_List(shader_t *shader, int nummeshes, mesh_t **mesh, vbo_t *vbo, texnums_t *texnums, unsigned int beflags);\nvoid VKBE_DrawMesh_Single(shader_t *shader, mesh_t *meshchain, vbo_t *vbo, unsigned int beflags);\nvoid VKBE_SubmitBatch(batch_t *batch);\nbatch_t *VKBE_GetTempBatch(void);\nvoid VKBE_GenBrushModelVBO(model_t *mod);\nvoid VKBE_ClearVBO(vbo_t *vbo, qboolean dataonly);\nvoid VKBE_UploadAllLightmaps(void);\nvoid VKBE_DrawWorld (batch_t **worldbatches);\nqboolean VKBE_LightCullModel(vec3_t org, model_t *model);\nvoid VKBE_SelectEntity(entity_t *ent);\nqboolean VKBE_SelectDLight(dlight_t *dl, vec3_t colour, vec3_t axis[3], unsigned int lmode);\nvoid VKBE_VBO_Begin(vbobctx_t *ctx, size_t maxsize);\nvoid VKBE_VBO_Data(vbobctx_t *ctx, void *data, size_t size, vboarray_t *varray);\nvoid VKBE_VBO_Finish(vbobctx_t *ctx, void *edata, size_t esize, vboarray_t *earray, void **vbomem, void **ebomem);\nvoid VKBE_VBO_Destroy(vboarray_t *vearray, void *mem);\nvoid VKBE_Scissor(srect_t *rect);\nvoid VKBE_BaseEntTextures(const qbyte *scenepvs, const int *sceneareas);\n\nstruct vk_shadowbuffer;\nstruct vk_shadowbuffer *VKBE_GenerateShadowBuffer(vecV_t *verts, int numverts, index_t *indicies, int numindicies, qboolean istemp);\nvoid VKBE_DestroyShadowBuffer(struct vk_shadowbuffer *buf);\nvoid VKBE_RenderShadowBuffer(struct vk_shadowbuffer *buf);\nvoid VKBE_SetupForShadowMap(dlight_t *dl, int texwidth, int texheight, float shadowscale);\nqboolean VKBE_BeginShadowmap(qboolean isspot, uint32_t width, uint32_t height);\nvoid VKBE_BeginShadowmapFace(void);\nvoid VKBE_DoneShadows(void);\n\nvoid VKBE_RT_Gen_Cube(struct vk_rendertarg_cube *targ, uint32_t size, qboolean clear);\nvoid VKBE_RT_Gen(struct vk_rendertarg *targ, vk_image_t *colour, uint32_t width, uint32_t height, qboolean clear, unsigned int flags);\nvoid VKBE_RT_Begin(struct vk_rendertarg *targ);\nvoid VKBE_RT_End(struct vk_rendertarg *targ);\nvoid VKBE_RT_Destroy(struct vk_rendertarg *targ);\n\nchar *VK_VKErrorToString(VkResult err);\t//helper for converting vulkan error codes to strings, if we get something unexpected.\n\nqboolean VK_AllocatePoolMemory(uint32_t pooltype, VkDeviceSize memsize, VkDeviceSize poolalignment, vk_poolmem_t *mem);\nvoid VK_ReleasePoolMemory(vk_poolmem_t *mem);\nqboolean VK_AllocateImageMemory(VkImage image, qboolean dedicated, vk_poolmem_t *mem);\t//dedicated should normally be TRUE for render targets\nqboolean VK_AllocateBindImageMemory(vk_image_t *image, qboolean dedicated);\t//dedicated should normally be TRUE for render targets\nstruct stagingbuf\n{\n\tVkBuffer buf;\n\tVkBuffer retbuf;\n\tvk_poolmem_t mem;\n\tsize_t size;\n\tVkBufferUsageFlags usage;\n};\nvk_image_t VK_CreateTexture2DArray(uint32_t width, uint32_t height, uint32_t layers, uint32_t mips, uploadfmt_t encoding, unsigned int type, qboolean rendertarget, const char *debugname);\nvoid set_image_layout(VkCommandBuffer cmd, VkImage image, VkImageAspectFlags aspectMask, VkImageLayout old_image_layout, VkAccessFlags srcaccess, VkPipelineStageFlagBits srcstagemask, VkImageLayout new_image_layout, VkAccessFlags dstaccess, VkPipelineStageFlagBits dststagemask);\nvoid VK_CreateSampler(unsigned int flags, vk_image_t *img);\nvoid VK_CreateSamplerInfo(VkSamplerCreateInfo *info, vk_image_t *img);\nvoid *VKBE_CreateStagingBuffer(struct stagingbuf *n, size_t size, VkBufferUsageFlags usage);\nVkBuffer VKBE_FinishStaging(struct stagingbuf *n, vk_poolmem_t *memptr);\nvoid *VK_FencedBegin(void (*passed)(void *work), size_t worksize);\nvoid VK_FencedSubmit(void *work);\nvoid VK_FencedCheck(void);\nvoid *VK_AtFrameEnd(void (*passed)(void *work), void *data, size_t worksize);\n\n\n\nvoid\tVK_Draw_Init(void);\nvoid\tVK_Draw_Shutdown(void);\n\nvoid\tVK_UpdateFiltering\t\t\t(image_t *imagelist, int filtermip[3], int filterpic[3], int mipcap[2], float lodbias, float anis);\nqboolean VK_LoadTextureMips\t\t\t(texid_t tex, const struct pendingtextureinfo *mips);\nvoid    VK_DestroyTexture\t\t\t(texid_t tex);\nvoid\tVK_DestroyVkTexture\t\t\t(vk_image_t *img);\n\nvoid\tVK_R_Init\t\t\t\t\t(void);\nvoid\tVK_R_DeInit\t\t\t\t\t(void);\nvoid\tVK_R_RenderView\t\t\t\t(void);\n\nchar\t*VKVID_GetRGBInfo\t\t\t(int *bytestride, int *truevidwidth, int *truevidheight, enum uploadfmt *fmt);\n\nqboolean\tVK_SCR_UpdateScreen\t\t\t(void);\n\nvoid\tVKBE_RenderToTextureUpdate2d(qboolean destchanged);\nVkRenderPass VK_GetRenderPass(int pass);\n\n//improved rgb get that calls the callback when the data is actually available. used for video capture.\nvoid VKVID_QueueGetRGBData\t\t\t(void (*gotrgbdata) (void *rgbdata, qintptr_t bytestride, size_t width, size_t height, enum uploadfmt fmt));\n"
  },
  {
    "path": "engine/web/fs_web.c",
    "content": "#include \"quakedef.h\"\r\n#include \"fs.h\"\r\n\r\n#if defined(FTE_TARGET_WEB)\r\n#define FSWEB_OpenPath VFSOS_OpenPath\r\n#define FSWEB_OpenTemp FS_OpenTemp\r\n\r\ntypedef struct {\r\n\tsearchpathfuncs_t pub;\r\n\tint depth;\r\n\tvoid (QDECL *AddFileHash)(int depth, const char *fname, fsbucket_t *filehandle, void *pathhandle);\r\n\tchar rootpath[1];\r\n} webpath_t;\r\ntypedef struct {\r\n\tvfsfile_t funcs;\r\n\tqofs_t offset;\r\n\tint handle;\r\n} vfswebfile_t;\r\nstatic int QDECL VFSWEB_ReadBytes (struct vfsfile_s *file, void *buffer, int bytestoread)\r\n{\r\n\tvfswebfile_t *intfile = (vfswebfile_t*)file;\r\n\tmemset(buffer, 'e', bytestoread);\r\n\tint len = emscriptenfte_buf_read(intfile->handle, intfile->offset, buffer, bytestoread);\r\n\tintfile->offset += len;\r\n\treturn len;\r\n}\r\nstatic int QDECL VFSWEB_WriteBytes (struct vfsfile_s *file, const void *buffer, int bytestoread)\r\n{\r\n\tvfswebfile_t *intfile = (vfswebfile_t*)file;\r\n\tint len = emscriptenfte_buf_write(intfile->handle, intfile->offset, buffer, bytestoread);\r\n\tintfile->offset += len;\r\n\treturn len;\r\n}\r\nstatic qboolean QDECL VFSWEB_Seek (struct vfsfile_s *file, qofs_t pos)\r\n{\r\n\tvfswebfile_t *intfile = (vfswebfile_t*)file;\r\n//\tif (pos < 0)\r\n//\t\treturn 0;\r\n\tintfile->offset = pos;\r\n\treturn true;\r\n}\r\nstatic qofs_t QDECL VFSWEB_Tell (struct vfsfile_s *file)\r\n{\r\n\tvfswebfile_t *intfile = (vfswebfile_t*)file;\r\n\treturn intfile->offset;\r\n}\r\nstatic void QDECL VFSWEB_Flush(struct vfsfile_s *file)\r\n{\r\n//\tvfswebfile_t *intfile = (vfswebfile_t*)file;\r\n}\r\nstatic qofs_t QDECL VFSWEB_GetSize (struct vfsfile_s *file)\r\n{\r\n\tvfswebfile_t *intfile = (vfswebfile_t*)file;\r\n\tunsigned long l;\r\n\tl = emscriptenfte_buf_getsize(intfile->handle);\r\n\treturn l;\r\n}\r\nstatic qboolean QDECL VFSWEB_Close(vfsfile_t *file)\r\n{\r\n\tvfswebfile_t *intfile = (vfswebfile_t*)file;\r\n\temscriptenfte_buf_release(intfile->handle);\r\n\tZ_Free(file);\r\n\treturn true;\r\n}\r\nstatic qboolean QDECL VFSWEB_ClosePersist(vfsfile_t *file)\r\n{\r\n\tvfswebfile_t *intfile = (vfswebfile_t*)file;\r\n#ifdef _DEBUG\r\n\tCon_DPrintf(\"Persisting file %s\\n\", file->dbgname);\r\n#endif\r\n\temscriptenfte_buf_pushtolocalstore(intfile->handle);\r\n\treturn VFSWEB_Close(file);\r\n}\r\n\r\nvfsfile_t *FSWEB_OpenTempHandle(int f)\r\n{\r\n\tvfswebfile_t *file;\r\n\r\n\tif (f == -1)\r\n\t{\r\n\t\tCon_Printf(\"FSWEB_OpenTemp failed\\n\");\r\n\t\treturn NULL;\r\n\t}\r\n\r\n\tfile = Z_Malloc(sizeof(vfswebfile_t));\r\n\tfile->funcs.Close = VFSWEB_Close;\r\n#ifdef _DEBUG\r\n\tQ_strncpyz(file->funcs.dbgname, \"FSWEB_OpenTemp\", sizeof(file->funcs.dbgname));\r\n#endif\r\n\tfile->funcs.ReadBytes = VFSWEB_ReadBytes;\r\n\tfile->funcs.WriteBytes = VFSWEB_WriteBytes;\r\n\tfile->funcs.Seek = VFSWEB_Seek;\r\n\tfile->funcs.Tell = VFSWEB_Tell;\r\n\tfile->funcs.GetLen = VFSWEB_GetSize;\r\n\tfile->funcs.Flush = VFSWEB_Flush;\r\n\tfile->handle = f;\r\n\r\n\treturn &file->funcs;\r\n}\r\n\r\nvfsfile_t *FSWEB_OpenTemp(void)\r\n{\r\n\treturn FSWEB_OpenTempHandle(emscriptenfte_buf_create());\r\n}\r\n\r\nvfsfile_t *VFSWEB_Open(const char *osname, const char *mode, qboolean *needsflush)\r\n{\r\n\tint f;\r\n\tvfswebfile_t *file;\r\n\t//qboolean read = !!strchr(mode, 'r');\r\n\tqboolean write = !!strchr(mode, 'w');\r\n\tqboolean update = !!strchr(mode, '+');\r\n\tqboolean append = !!strchr(mode, 'a');\r\n\tqboolean persist = !!strchr(mode, 'p');\r\n\r\n\tif (needsflush)\r\n\t\t*needsflush = false;\r\n\tf = emscriptenfte_buf_open(osname, (write && !update)?2:(write||append));\r\n\tif (f == -1)\r\n\t\treturn NULL;\r\n\r\n\tif (write || append)\r\n\t{\r\n\t\tif (needsflush)\r\n\t\t\t*needsflush = true;\r\n\t}\r\n\r\n\tfile = Z_Malloc(sizeof(vfswebfile_t));\r\n#ifdef _DEBUG\r\n\tQ_strncpyz(file->funcs.dbgname, osname, sizeof(file->funcs.dbgname));\r\n#endif\r\n\tfile->funcs.ReadBytes = VFSWEB_ReadBytes;\r\n\tfile->funcs.WriteBytes = VFSWEB_WriteBytes;\r\n\tfile->funcs.Seek = VFSWEB_Seek;\r\n\tfile->funcs.Tell = VFSWEB_Tell;\r\n\tfile->funcs.GetLen = VFSWEB_GetSize;\r\n\tif (persist && (write || append))\r\n\t\tfile->funcs.Close = VFSWEB_ClosePersist;\r\n\telse\r\n\t\tfile->funcs.Close = VFSWEB_Close;\r\n\tfile->funcs.Flush = VFSWEB_Flush;\r\n\tfile->handle = f;\r\n\r\n\tif (append)\r\n\t\tfile->offset = VFSWEB_GetSize(&file->funcs);\r\n\r\n\treturn &file->funcs;\r\n}\r\n\r\nvfsfile_t *VFSOS_Open(const char *osname, const char *mode)\r\n{\r\n\tvfsfile_t *f;\r\n\tqboolean needsflush;\r\n\tf = VFSWEB_Open(osname, mode, &needsflush);\r\n\tif (needsflush)\r\n\t\tFS_FlushFSHashFull();\r\n\treturn f;\r\n}\r\n\r\nstatic vfsfile_t *QDECL FSWEB_OpenVFS(searchpathfuncs_t *handle, flocation_t *loc, const char *mode)\r\n{\r\n\tvfsfile_t *f;\r\n\twebpath_t *sp = (void*)handle;\r\n\tqboolean needsflush;\r\n\r\n\tf = VFSWEB_Open(loc->rawname, mode, &needsflush);\r\n\tif (needsflush && sp->AddFileHash)\r\n\t\tsp->AddFileHash(sp->depth, loc->rawname, NULL, sp);\r\n\treturn f;\r\n}\r\n\r\nstatic void QDECL FSWEB_ClosePath(searchpathfuncs_t *handle)\r\n{\r\n\tZ_Free(handle);\r\n}\r\nstatic qboolean QDECL FSWEB_PollChanges(searchpathfuncs_t *handle)\r\n{\r\n//\twebpath_t *np = handle;\r\n\treturn true;\t//can't verify that or not, so we have to assume the worst\r\n}\r\nstatic int QDECL FSWEB_RebuildFSHash(const char *filename, qofs_t filesize, time_t mtime, void *data, searchpathfuncs_t *spath)\r\n{\r\n\twebpath_t *sp = (void*)spath;\r\n\tvoid (QDECL *AddFileHash)(int depth, const char *fname, fsbucket_t *filehandle, void *pathhandle) = data;\r\n\tif (filename[strlen(filename)-1] == '/')\r\n\t{\t//this is actually a directory\r\n\r\n\t\tchar childpath[256];\r\n\t\tQ_snprintfz(childpath, sizeof(childpath), \"%s*\", filename);\r\n\t\tSys_EnumerateFiles(sp->rootpath, childpath, FSWEB_RebuildFSHash, data, spath);\r\n\t\treturn true;\r\n\t}\r\n\tAddFileHash(sp->depth, filename, NULL, sp);\r\n\treturn true;\r\n}\r\nstatic void QDECL FSWEB_BuildHash(searchpathfuncs_t *handle, int depth, void (QDECL *AddFileHash)(int depth, const char *fname, fsbucket_t *filehandle, void *pathhandle))\r\n{\r\n\twebpath_t *sp = (void*)handle;\r\n\tsp->depth = depth;\r\n\tsp->AddFileHash = AddFileHash;\r\n\tSys_EnumerateFiles(sp->rootpath, \"*\", FSWEB_RebuildFSHash, AddFileHash, handle);\r\n}\r\nstatic qboolean QDECL FSWEB_FLocate(searchpathfuncs_t *handle, flocation_t *loc, const char *filename, void *hashedresult)\r\n{\r\n\twebpath_t *sp = (void*)handle;\r\n\tint len;\r\n\tchar netpath[MAX_OSPATH];\r\n\r\n\tif (hashedresult && (void *)hashedresult != handle)\r\n\t\treturn false;\r\n\r\n/*\r\n\tif (!static_registered)\r\n\t{\t// if not a registered version, don't ever go beyond base\r\n\t\tif ( strchr (filename, '/') || strchr (filename,'\\\\'))\r\n\t\t\tcontinue;\r\n\t}\r\n*/\r\n\r\n// check a file in the directory tree\r\n\tsnprintf (netpath, sizeof(netpath)-1, \"%s/%s\", sp->rootpath, filename);\r\n\r\n\t{\r\n\t\tvfsfile_t *f = VFSWEB_Open(netpath, \"rb\", NULL);\r\n\t\tif (!f)\r\n\t\t\treturn false;\r\n\t\tlen = VFS_GETLEN(f);\r\n\t\tVFS_CLOSE(f);\r\n\t}\r\n\tif (loc)\r\n\t{\r\n\t\tloc->len = len;\r\n\t\tloc->offset = 0;\r\n\t\tloc->fhandle = NULL;\r\n\t\tQ_strncpyz(loc->rawname, netpath, sizeof(loc->rawname));\r\n\t}\r\n\treturn true;\r\n}\r\nstatic void QDECL FSWEB_ReadFile(searchpathfuncs_t *handle, flocation_t *loc, char *buffer)\r\n{\r\n\tvfsfile_t *f;\r\n\tsize_t result;\r\n\r\n\tf = VFSWEB_Open(loc->rawname, \"rb\", NULL);\r\n\tif (!f)\t//err...\r\n\t\treturn;\r\n\tVFS_SEEK(f, loc->offset);\r\n\tresult = VFS_READ(f, buffer, loc->len); // do soemthing with result\r\n\r\n\tif (result != loc->len)\r\n\t\tCon_Printf(\"FSWEB_ReadFile() fread: Filename: %s, expected %u, result was %u \\n\",loc->rawname,(unsigned int)loc->len,(unsigned int)result);\r\n\r\n\tVFS_CLOSE(f);\r\n}\r\nstatic int QDECL FSWEB_EnumerateFiles (searchpathfuncs_t *handle, const char *match, int (QDECL *func)(const char *, qofs_t, time_t mtime, void *, searchpathfuncs_t *spath), void *parm)\r\n{\r\n\twebpath_t *sp = (webpath_t*)handle;\r\n\treturn Sys_EnumerateFiles(sp->rootpath, match, func, parm, handle);\r\n}\r\n\r\n\r\nsearchpathfuncs_t *QDECL FSWEB_OpenPath(vfsfile_t *mustbenull,  searchpathfuncs_t *parent, const char *filename, const char *desc, const char *prefix)\r\n{\r\n\twebpath_t *np;\r\n\tint dlen = strlen(desc);\r\n\tif (mustbenull)\r\n\t\treturn NULL;\r\n\tnp = Z_Malloc(sizeof(*np) + dlen);\r\n\tif (np)\r\n\t{\r\n\t\tnp->depth = 0;\r\n\t\tmemcpy(np->rootpath, desc, dlen+1);\r\n\t}\r\n\r\n\tnp->pub.fsver\t\t\t= FSVER;\r\n\tnp->pub.ClosePath\t\t= FSWEB_ClosePath;\r\n\tnp->pub.BuildHash\t\t= FSWEB_BuildHash;\r\n\tnp->pub.FindFile\t\t= FSWEB_FLocate;\r\n\tnp->pub.ReadFile\t\t= FSWEB_ReadFile;\r\n\tnp->pub.EnumerateFiles\t= FSWEB_EnumerateFiles;\r\n\tnp->pub.OpenVFS\t\t\t= FSWEB_OpenVFS;\r\n\tnp->pub.PollChanges\t\t= FSWEB_PollChanges;\r\n\treturn &np->pub;\r\n}\r\n\r\n#endif\r\n\r\n"
  },
  {
    "path": "engine/web/fte_pwa.json",
    "content": "{\n\t\"name\": \"FTE-In-Browser\",\n\t\"short_name\": \"FTEQW\",\n\t\"description\": \"Awesome games in your Web Browser!\",\n\t\"categories\": [\"games\"],\n\t\"id\": \"./\",\n\t\"start_url\": \"./\",\n\t\"display\": \"standalone\",\n\t\"orientation\": \"landscape\",\n\t\"icons\":\n\t[\n    \t{\n\t\t\t\"src\": \"https://fteqw.org/favicon.png\",\n\t\t\t\"type\": \"image/png\",\n\t\t\t\"sizes\": \"128x128\"\n    \t},\n    \t{\n\t\t\t\"src\": \"icon_144.png\",\n\t\t\t\"type\": \"image/png\",\n\t\t\t\"sizes\": \"144x144\"\n    \t}\n\t],\n\t\"protocol_handlers\":\n\t[\n\t\t{\n\t\t\t\"protocol\": \"web+quake\",\n\t\t\t\"url\": \"./?%s\"\n\t\t},\n\t\t{\n\t\t\t\"protocol\": \"web+qw\",\n\t\t\t\"url\": \"./?%s\"\n\t\t}\n\t],\n\t\"serviceworker\":\n\t{\n\t\t\"scope\": \"./\",\n\t\t\"src\": \"./fte_pwa_sw.js\",\n\t\t\"use_cache\": true\n\t}\n}\n\n"
  },
  {
    "path": "engine/web/fte_pwa_sw.js",
    "content": "const addResourcesToCache = async (resources) =>\n{\n\tconst cache = await caches.open('v1');\n\tawait cache.addAll(resources);\n};\n\nconst putInCache = async (request, response) =>\n{\n\tconst cache = await caches.open('v1');\n\tawait cache.put(request, response);\n};\nfunction shouldcache(name)\n{\n\tif (name.indexOf(\"/raw/\"))\n\t\treturn false;\n\tif (name.indexOf(\"/downloadables\"))\n\t\treturn false;\n\treturn true;\n};\n\nconst cacheFirst = async ({ request, preloadResponsePromise }) =>\n{\n\t// First try to get the resource from the cache\n\tconst responseFromCache = await caches.match(request);\n\tif (responseFromCache)\n\t{\n//\t\tconsole.log(\"cacheFirst - from cache\", request, preloadResponsePromise, responseFromCache);\n\t\treturn responseFromCache;\n\t}\n\n\t// Next try to use the preloaded response, if it's there\n\tconst preloadResponse = await preloadResponsePromise;\n\tif (preloadResponse)\n\t{\n//\t\tconsole.info('using preload response', preloadResponse);\n\t\tif (shouldcache(request.url))\n\t\t\tputInCache(request, preloadResponse.clone());\n\t\treturn preloadResponse;\n\t}\n\n\t// Next try to get the resource from the network\n\ttry\n\t{\n\t\tconst responseFromNetwork = await fetch(request.clone());\n\t\t// response may be used only once\n\t\t// we need to save clone to put one copy in cache\n\t\t// and serve second one\n\t\tif (shouldcache(request.url))\n\t\t\tputInCache(request, responseFromNetwork.clone());\n//\t\tconsole.info('fetching from network', responseFromNetwork);\n\t\treturn responseFromNetwork;\n\t}\n\tcatch (error)\n\t{\n//\t\tconsole.info('failure', preloadResponse);\n\t\t// there is nothing we can do, but we must always\n\t\t// return a Response object\n\t\treturn new Response('Network error happened',\n\t\t{\n\t\t\tstatus: 408,\n\t\t\theaders: { 'Content-Type': 'text/plain' },\n\t\t});\n\t}\n};\n\nconst enableNavigationPreload = async () =>\n{\n\tif (self.registration.navigationPreload)\n\t{\n\t\t// Enable navigation preloads!\n\t\tawait self.registration.navigationPreload.enable();\n\t}\n};\n\nself.addEventListener('activate', (event) =>\n{\n\tevent.waitUntil(enableNavigationPreload());\n});\n\n\nself.addEventListener('install', (event) =>\n{\n\tevent.waitUntil(\n\t\taddResourcesToCache([\n\t\t\t'./',\n\t\t\t'./fte_pwa.json',\n\t\t\t'./fte_pwa_sw.js',\n\t\t\t'./ftewebgl.js',\n\t\t\t'./ftewebgl.wasm'\n\t\t])\n\t);\n});\n\nself.addEventListener('fetch', (event) =>\n{\n\tevent.respondWith(cacheFirst(\n\t{\n\t\trequest: event.request,\n\t\tpreloadResponsePromise: event.preloadResponse\n\t}));\n});\n"
  },
  {
    "path": "engine/web/ftejslib.h",
    "content": "//emscripten's download mechanism lacks usable progress indicators.\r\nvoid emscriptenfte_async_wget_data2(const char *url, void *postdata, int postlen, char *postmimetype, void *ctx, void (*onload)(void*ctx,int buf), void (*onerror)(void*ctx,int code), void (*onprogress)(void*ctx,int prog,int total));\r\n\r\n//changes the page away from quake (oh noes!) or downloads something.\r\nvoid emscriptenfte_window_location(const char *url);\r\n\r\n//filesystem buffers are implemented in javascript so that we are not bound by power-of-two heap limitations quite so much.\r\n//also, we can't use emscripten's stdio because it reserves 16m file handles or something.\r\n//these buffers do not track file offsets nor file access permissions.\r\nint emscriptenfte_buf_create(void);\r\nint emscriptenfte_buf_open(const char *name, int createifneeded);\t//open\r\nint emscriptenfte_buf_rename(const char *oldname, const char *newname);\t//rename files (if it was open, the handle now refers to the new file instead)\r\nint emscriptenfte_buf_delete(const char *fname);\t\t\t//delete the named file. there may be problems if its currently open\r\nvoid emscriptenfte_buf_release(int handle);\t\t\t\t//close\r\nvoid emscriptenfte_buf_pushtolocalstore(int handle);\t\t\t//make a copy in the browser's local storage, if possible.\r\nunsigned int emscriptenfte_buf_getsize(int handle);\t\t\t//get the size of the file buffer\r\nint emscriptenfte_buf_read(int handle, int offset, void *data, int len);//read data\r\nint emscriptenfte_buf_write(int handle, int offset, const void *data, int len);//write data. no access checks.\r\nvoid emscritenfte_buf_enumerate(void (*Sys_EnumeratedFile)(void *ctx, size_t fsize), void *ctx, size_t namesize);\r\nvoid emscriptenfte_openfile(void);\r\n\r\n//websocket is implemented in javascript because there is no usable C api (emscripten's javascript implementation is shite and has fatal errors).\r\nint emscriptenfte_ws_connect(const char *url, const char *wsprotocol);\t//open a websocket connection to a specific host\r\nvoid emscriptenfte_ws_close(int sockid);\t\t\t\t\t\t\t\t//close it again\r\nint emscriptenfte_ws_cansend(int sockid, int extra, int maxpending);\t//returns false if we're blocking for some reason. avoids overflowing. everything is otherwise reliable.\r\nint emscriptenfte_ws_send(int sockid, const void *data, int len);\t\t//send data to the peer. queues data. never dropped.\r\nint emscriptenfte_ws_recv(int sockid, void *data, int len);\t\t\t\t//receive data from the peer.\r\n\r\nint emscriptenfte_rtc_create(int clientside, void *ctxp, int ctxi, void(*cb)(void *ctxp, int ctxi, int type, const char *data), const char *json_config);\t\t\t\t\t//open a webrtc connection to a specific broker url\r\nvoid emscriptenfte_rtc_offer(int sock, const char *offer, const char *sdptype);//sets the remote sdp.\r\nint emscriptenfte_rtc_candidate(int sock, const char *offer);\t\t\t\t//adds a remote candidate.\r\n\r\n//misc stuff for printf replacements\r\nvoid emscriptenfte_alert(const char *msg);\r\nvoid emscriptenfte_print(const char *msg);\r\nvoid emscriptenfte_setupmainloop(int(*mainloop)(double timestamp));\r\nNORETURN void emscriptenfte_abortmainloop(const char *caller, int fatal);\r\n\r\n//we're trying to avoid including libpng+libjpeg+libogg in javascript due to it being redundant bloat.\r\n//to use such textures/sounds, we can just 'directly' load them via webgl\r\nvoid emscriptenfte_gl_loadtexturefile(int gltexid, int *width, int *height, void *data, int datasize, const char *fname, int premul, int genmips);\r\nvoid emscriptenfte_al_loadaudiofile(int al_buf, void *data, int datasize);\r\nint emscriptenfte_pcm_loadaudiofile(void *ctx, void(*callbackfunc)(void *ctx, void *dataptr, int frames, int channels, float rate), void *dataptr, size_t datasize, int snd_speed);\r\n\r\n//avoid all of emscripten's sdl emulation.\r\n//this resolves input etc issues.\r\nunsigned long emscriptenfte_ticks_ms(void);\r\ndouble emscriptenfte_uptime_ms(void);\r\nvoid emscriptenfte_updatepointerlock(int wantpointerlock, int hidecursor);\r\nvoid emscriptenfte_polljoyevents(void);\r\nvoid emscriptenfte_settitle(const char *text);\r\nint emscriptenfte_setupcanvas(\r\n\tint width,\r\n\tint height,\r\n\tvoid(*Resized)(int newwidth, int newheight, float scale),\r\n\tvoid(*Mouse)(unsigned int devid,int abs,float x,float y,float z,float size),\r\n\tvoid(*Button)(unsigned int devid, int down, int mbutton),\r\n\tint(*Keyboard)(unsigned int devid, int down, int keycode, int unicode),\r\n\tvoid(*LoadFile)(char *url, char *mime, int filehandle),\r\n\tvoid(*CbufAdd)(const char *text),\r\n\tvoid(*buttonevent)(int joydev, int button, int ispressed, int isstandard),\r\n\tvoid(*axisevent)(int joydev, int axis, float value, int isstandard),\r\n\tvoid(*orientation)(int joydev, float px,float py,float pz, float qx,float qy,float qz,float qw),\r\n\tint (*ShouldSwitchToFullscreen)(void)\r\n\t);\r\n\r\nstruct webxrinfo_s {\r\n\tint fbo;\t//panic!\r\n\tint viewport[4];\r\n\tfloat projmatrix[16];\r\n\tfloat transform[16];\r\n};\r\nint emscriptenfte_xr_geteyeinfo(int maxeyes, struct webxrinfo_s *vreyes);\r\nunsigned int emscriptenfte_xr_issupported(void);\t\t//0 for can't support it, -1 for unknown support, otherwise bitmask of supported modes\r\nint emscriptenfte_xr_isactive(void);\t//true if we're actually using webvr or not.\r\nvoid emscriptenfte_xr_setup(int mode);\r\n\t#define WXRM_TOGGLE -3\r\n\t#define WXRM_AUTO -2\r\n\t#define WXRM_DISABLE -1\r\n\t#define WXRM_INLINE 0\r\n\t#define WXRM_VR 1\r\n\t#define WXRM_AR 2\r\nvoid emscriptenfte_xr_shutdown(void);\r\n"
  },
  {
    "path": "engine/web/ftejslib.js",
    "content": "\r\nmergeInto(LibraryManager.library,\r\n{\r\n\t//generic handles array\r\n\t//yeah, I hope you don't use-after-free. hopefully that sort of thing will be detected on systems with easier non-mangled debuggers.\r\n//\t$FTEH__deps: [],\r\n//\t$FTEH: {\r\n//\t\th: [],\r\n//\t\tf: {}\r\n//\t},\r\n\r\n\t//FIXME: split+merge by \\n\r\n\temscriptenfte_print : function(msg)\r\n\t{\r\n\t\tFTEC.linebuffer += UTF8ToString(msg);\r\n\t\tfor(;;)\r\n\t\t{\r\n\t\t\tnl = FTEC.linebuffer.indexOf(\"\\n\");\r\n\t\t\tif (nl == -1)\r\n\t\t\t\tbreak;\r\n\t\t\tconsole.log(FTEC.linebuffer.substring(0, nl));\r\n\t\t\tFTEC.linebuffer = FTEC.linebuffer.substring(nl+1);\r\n\t\t}\r\n\t},\r\n\temscriptenfte_alert : function(msg)\r\n\t{\r\n\t\tmsg = UTF8ToString(msg);\r\n\t\tconsole.log(msg);\r\n\t\talert(msg);\r\n\t},\r\n\t\r\n\temscriptenfte_window_location : function(msg)\r\n\t{\r\n\t\tmsg = UTF8ToString(msg);\r\n\t\tconsole.log(\"Redirecting page to \" + msg);\r\n\t\twindow.location = msg;\r\n\t},\r\n\r\n//\temscriptenfte_handle_alloc__deps : ['$FTEH'],\r\n\temscriptenfte_handle_alloc : function(h)\r\n\t{\r\n\t\tfor (var i = 0; FTEH.h.length; i+=1)\r\n\t\t{\r\n\t\t\tif (FTEH.h[i] == null)\r\n\t\t\t{\r\n\t\t\t\tFTEH.h[i] = h;\r\n\t\t\t\treturn i;\r\n\t\t\t}\r\n\t\t}\r\n\t\ti = FTEH.h.length;\r\n\t\tFTEH.h[i] = h;\r\n\t\treturn i;\r\n\t},\r\n\r\n\t//temp files\r\n\temscriptenfte_buf_createfromarraybuf__deps : ['emscriptenfte_handle_alloc'],\r\n\temscriptenfte_buf_createfromarraybuf : function(buf)\r\n\t{\r\n\t\tbuf = new Uint8Array(buf);\r\n\t\tvar len = buf.length;\r\n\t\tvar b = {h:-1, r:1, l:len,m:len,d:buf, n:null};\r\n\t\tb.h = _emscriptenfte_handle_alloc(b);\r\n\t\treturn b.h;\r\n\t},\r\n\r\n\t$FTEC__deps : ['emscriptenfte_buf_createfromarraybuf'],\r\n\t$FTEC:\r\n\t{\r\n\t\tctxwarned:0,\r\n\t\tpointerislocked:0,\r\n\t\tpointerwantlock:0,\r\n\t\tclipboard:\"\",\r\n\t\tlinebuffer:'',\r\n\t\tlocalstorefailure:false,\r\n\t\tdovsync:false,\r\n\t\twakelock:null,\r\n\t\txrsupport:-1,\r\n\t\txrsession:null,\r\n\t\txrframe:null,\r\n\t\treferenceSpace:null,\r\n\t\tw: -1,\r\n\t\th: -1,\r\n\t\tdonecb:0,\r\n\t\tevcb: {\r\n\t\t\tresize:0,\r\n\t\t\tmouse:0,\r\n\t\t\tbutton:0,\r\n\t\t\tkey:0,\r\n\t\t\tloadfile:0,\r\n\t\t\tcbufaddtext:0,\r\n\t\t\tjbutton:0,\r\n\t\t\tjaxis:0,\r\n\t\t\tjorientation:0,\r\n\t\t\twantfullscreen:0,\r\n\t\t\tframe:0\r\n\t\t},\r\n\r\n\t\tloadurl : function(url, mime, arraybuf)\r\n\t\t{\r\n\t\t\tif (FTEC.evcb.loadfile != 0)\r\n\t\t\t{\r\n\t\t\t\tlet handle = -1;\r\n\t\t\t\tif (arraybuf !== undefined)\r\n\t\t\t\t\thandle = _emscriptenfte_buf_createfromarraybuf(arraybuf);\t\r\n\t\t\t\tlet blen = lengthBytesUTF8(url)+1;\r\n\t\t\t\tlet urlptr = _malloc(blen);\r\n\t\t\t\tstringToUTF8(url, urlptr, blen);\r\n\t\t\t\tblen = lengthBytesUTF8(mime)+1;\r\n\t\t\t\tlet mimeptr = _malloc(blen);\r\n\t\t\t\tstringToUTF8(mime, mimeptr,blen);\r\n\t\t\t\t{{{makeDynCall('viii','FTEC.evcb.loadfile')}}}(urlptr, mimeptr, handle);\r\n\t\t\t\t_free(mimeptr);\r\n\t\t\t\t_free(urlptr);\r\n\t\t\t\twindow.focus();\r\n\t\t\t}\r\n\t\t},\r\n\t\tcbufadd : function(command)\r\n\t\t{\r\n\t\t\tif (FTEC.evcb.cbufaddtext != 0)\r\n\t\t\t{\r\n\t\t\t\tlet handle = -1;\r\n\t\t\t\tlet blen = lengthBytesUTF8(command)+1;\r\n\t\t\t\tlet ptr = _malloc(blen);\r\n\t\t\t\tstringToUTF8(command, ptr, blen);\r\n\t\t\t\t{{{makeDynCall('vi','FTEC.evcb.cbufaddtext')}}}(ptr);\r\n\t\t\t\t_free(ptr);\r\n\t\t\t\twindow.focus();\r\n\t\t\t}\r\n\t\t},\r\n\r\n\t\tstep : function(timestamp)\r\n\t\t{\r\n\t\t\tif (FTEC.aborted)\r\n\t\t\t\treturn;\r\n\r\n\t\t\t//do this first in the hope that it'll make firefox a smidge smoother.\r\n\t\t\tif (FTEC.dovsync)\r\n\t\t\t\twindow.requestAnimationFrame(FTEC.step);\r\n\t\t\telse\r\n\t\t\t\tsetTimeout(FTEC.step, 0, performance.now());\r\n\r\n\t\t\tif (FTEC.xrsession)\r\n\t\t\t\treturn;\t//keep ticking, cos its safer. :\\\r\n\r\n\t\t\ttry\t//this try is needed to handle Host_EndGame properly.\r\n\t\t\t{\r\n\t\t\t\tFTEC.dovsync = {{{makeDynCall('if','FTEC.evcb.frame')}}}(timestamp);\r\n\t\t\t}\r\n\t\t\tcatch(err)\r\n\t\t\t{\r\n\t\t\t\tconsole.log(err);\r\n\t\t\t}\r\n\t\t},\r\n\t\tdoxrframe : function(timestamp, frame)\r\n\t\t{\r\n\t\t\tif (FTEC.aborted || FTEC.xrsession == null)\r\n\t\t\t\treturn;\r\n\r\n\t\t\t//do this first in the hope that it'll make firefox a smidge smoother.\r\n\t\t\tFTEC.xrsession.requestAnimationFrame(FTEC.doxrframe);\r\n\r\n\t\t\tFTEC.xrframe = frame;\r\n\t\t\ttry\t//this try is needed to handle Host_EndGame properly.\r\n\t\t\t{\r\n\t\t\t\tFTEC.dovsync = {{{makeDynCall('if','FTEC.evcb.frame')}}}(timestamp);\r\n\t\t\t}\r\n\t\t\tcatch(err)\r\n\t\t\t{\r\n\t\t\t\tconsole.log(err);\r\n\t\t\t}\r\n\t\t\tFTEC.xrframe = null;\r\n\t\t},\r\n\r\n\t\thandleevent : function(event)\r\n\t\t{\r\n\t\t\tswitch(event.type)\r\n\t\t\t{\r\n\t\t\t\tcase 'message':\r\n\t\t\t\t\tconsole.log(event);\r\n\t\t\t\t\tconsole.log(event.data);\r\n\t\t\t\t\tFTEC.loadurl(event.data.url, event.data.cmd, undefined);\r\n\t\t\t\t\tbreak;\r\n\t\t\t\tcase 'resize':\r\n\t\t\t\t\tif (FTEC.evcb.resize != 0)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\t{{{makeDynCall('vii','FTEC.evcb.resize')}}}(Module['canvas'].width, Module['canvas'].height);\r\n\t\t\t\t\t}\r\n\t\t\t\t\tbreak;\r\n\t\t\t\tcase 'mousemove':\r\n\t\t\t\t\tif (FTEC.evcb.mouse != 0)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tif (Browser.pointerLock)\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tif (typeof event.movementX === 'undefined')\r\n\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\tevent.movementX = event.mozMovementX;\r\n\t\t\t\t\t\t\t\tevent.movementY = event.mozMovementY;\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\tif (typeof event.movementX === 'undefined')\r\n\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\tevent.movementX = event.webkitMovementX;\r\n\t\t\t\t\t\t\t\tevent.movementY = event.webkitMovementY;\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t{{{makeDynCall('viiffff','FTEC.evcb.mouse')}}}(0, false, event.movementX, event.movementY, 0, 0);\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\telse\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tvar rect = Module['canvas'].getBoundingClientRect();\r\n\t\t\t\t\t\t\t{{{makeDynCall('viiffff','FTEC.evcb.mouse')}}}(0, true, (event.clientX - rect.left)*(Module['canvas'].width/rect.width), (event.clientY - rect.top)*(Module['canvas'].height/rect.height), 0, 0);\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t}\r\n\t\t\t\t\tbreak;\r\n\t\t\t\tcase 'mousedown':\r\n\t\t\t\t\twindow.focus();\r\n\t\t\t\t\t//Mozilla docs say do the pointerlock request first...\r\n\t\t\t\t\t//older browsers only allowed pointer lock when fullscreen. maybe it'll need two clicks. sucks to be you.\r\n\t\t\t\t\tif (FTEC.pointerwantlock != 0 && FTEC.pointerislocked == 0)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tvar v;\r\n\t\t\t\t\t\ttry\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tFTEC.pointerislocked = -1;  //don't repeat the request on every click. firefox has a fit at that, so require the mouse to leave the element or something before we retry.\r\n\t\t\t\t\t\t\tv = Module['canvas'].requestPointerLock({unadjustedMovement: true});\r\n\t\t\t\t\t\t\tif (v !== undefined)\r\n\t\t\t\t\t\t\t{\t//fuck sake, this is chrome being shitty.\r\n\t\t\t\t\t\t\t\t//this is all bullshit.\r\n\t\t\t\t\t\t\t\t//requestPointerLock spec does not return a promise. but chrome does it anyway, and returns its errors that way. and it eerrors a LOT, in system-specific ways, resulting in pointer locks failing entirely.\r\n\t\t\t\t\t\t\t\tv.catch((e)=>\r\n\t\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\t\tif (e.name == \"NotSupportedError\")\r\n\t\t\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\t\t\tModule['canvas'].requestPointerLock().then(()=>{\r\n\t\t\t\t\t\t\t\t\t\t\tconsole.log(\"Shitty browser forces mouse accel. Expect a shit experience.\");\r\n\t\t\t\t\t\t\t\t\t\t}).catch(()=>{\r\n\t\t\t\t\t\t\t\t\t\t\tconsole.log(\"Your defective browser forces can't handle mouse look. Expect a truely dire experience. Give up now.\");\r\n\t\t\t\t\t\t\t\t\t\t});\r\n\t\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t\t\telse\r\n\t\t\t\t\t\t\t\t\t\tconsole.log(\"Your defective browser forces can't handle mouse look. Expect a truely dire experience. Give up now.\");\r\n\t\t\t\t\t\t\t\t});\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\tcatch(e)\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\ttry {\r\n\t\t\t\t\t\t\t\tModule['canvas'].requestPointerLock();\r\n\t\t\t\t\t\t\t\tconsole.log(\"Your shitty browser doesn't support disabling mouse acceleration.\");\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\tcatch(e)\r\n\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\tconsole.log(\"Your shitty browser doesn't support mouse grabs.\");\r\n\t\t\t\t\t\t\t\tFTEC.pointerislocked = -1;  //don't repeat the request on every click. firefox has a fit at that, so require the mouse to leave the element or something before we retry.\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t}\r\n\t\t\t\t\t//older browsers need fullscreen in order for requestPointerLock to work. Which seems to be deprecated cos of how shitty an experience it is whenever you hit escape to load a menu or w/e\r\n\t\t\t\t\t//newer browsers can still break pointer locks when alt-tabbing, even without breaking fullscreen, so lets spam requests for it. enjoy.\r\n\t\t\t\t\tif (!document.fullscreenElement)\r\n\t\t\t\t\t\tif (FTEC.evcb.wantfullscreen != 0)\r\n\t\t\t\t\t\t\tif ({{{makeDynCall('i','FTEC.evcb.wantfullscreen')}}}())\r\n\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\ttry\r\n\t\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\t\tModule['canvas'].requestFullscreen();\r\n\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t\tcatch(e)\r\n\t\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\t\tconsole.log(\"requestFullscreen:\");\r\n\t\t\t\t\t\t\t\t\tconsole.log(e);\r\n\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t//fallthrough\r\n\t\t\t\tcase 'mouseup':\r\n\t\t\t\t\tif (FTEC.evcb.button != 0)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\t{{{makeDynCall('viii','FTEC.evcb.button')}}}(0, event.type=='mousedown', event.button);\r\n\t\t\t\t\t\tevent.preventDefault();\r\n\t\t\t\t\t}\r\n\t\t\t\t\tbreak;\r\n\t\t\t\tcase 'mousewheel':\r\n\t\t\t\tcase 'wheel':\r\n\t\t\t\t\tif (FTEC.evcb.button != 0)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\t{{{makeDynCall('viii','FTEC.evcb.button')}}}(0, 2, event.deltaY);\r\n\t\t\t\t\t\tevent.preventDefault();\r\n\t\t\t\t\t}\r\n\t\t\t\t\tbreak;\r\n\t\t\t\tcase 'mouseout':\r\n\t\t\t\t\tif (FTEC.evcb.button != 0)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tfor (let i = 0; i < 8; i++)\r\n\t\t\t\t\t\t\t{{{makeDynCall('viii','FTEC.evcb.button')}}}(0, false, i);\r\n\t\t\t\t\t}\r\n\t\t\t\t\tif (FTEC.pointerislocked == -1)\r\n\t\t\t\t\t\tFTEC.pointerislocked = 0;\r\n\t\t\t\t\tbreak;\r\n\t\t\t\tcase 'visibilitychange':\r\n\t\t\t\t\ttry{\r\n\t\t\t\t\t\tif (!FTEC.wakelock && navigator.wakeLock && document.visibilityState === \"visible\")\r\n\t\t\t\t\t\t\tnavigator.wakeLock.request(\"screen\").then((value)=>{FTEC.wakelock = value;value.addEventListener(\"release\", ()=>{FTEC.wakelock=null;});}).catch(()=>{});\r\n\t\t\t\t\t}catch(e){console.log(e);}\r\n\t\t\t\t\tbreak;\r\n\t\t\t\tcase 'focus':\r\n\t\t\t\tcase 'blur':\r\n\t\t\t\t\t{{{makeDynCall('iiiii','FTEC.evcb.key')}}}(0, false, 16, 0); //shift\r\n\t\t\t\t\t{{{makeDynCall('iiiii','FTEC.evcb.key')}}}(0, false, 17, 0); //alt\r\n\t\t\t\t\t{{{makeDynCall('iiiii','FTEC.evcb.key')}}}(0, false, 18, 0); //ctrl\r\n\t\t\t\t\tif (FTEC.pointerislocked == -1)\r\n\t\t\t\t\t\tFTEC.pointerislocked = 0;\r\n\t\t\t\t\tbreak;\r\n\t\t\t\tcase 'keypress':\r\n\t\t\t\t\tif (FTEC.evcb.key != 0)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tif (event.charCode >= 122 && event.charCode <= 123)\t//no f11/f12\r\n\t\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t\t{{{makeDynCall('iiiii','FTEC.evcb.key')}}}(0, 1, 0, event.charCode);\r\n\t\t\t\t\t\t{{{makeDynCall('iiiii','FTEC.evcb.key')}}}(0, 0, 0, event.charCode);\r\n\t\t\t\t\t\tevent.preventDefault();\r\n\t\t\t\t\t\tevent.stopPropagation();\r\n\t\t\t\t\t}\r\n\t\t\t\t\tbreak;\r\n\t\t\t\tcase 'keydown':\r\n\t\t\t\tcase 'keyup':\r\n\t\t\t\t\t//122 is 'toggle fullscreen'.\r\n\t\t\t\t\t//we don't steal that because its impossible to leave it again once used.\r\n\t\t\t\t\tif (FTEC.evcb.key != 0 && event.keyCode != 122)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tconst codepoint = event.key.codePointAt(1)?0:event.key.codePointAt(0); // only if its a single codepoint - none of this 'Return' nonsense.\r\n\t\t\t\t\t\tif (codepoint < ' ') codepoint = 0; //don't give a codepoint for c0 chars - like tab.\r\n\t\t\t\t\t\tif ({{{makeDynCall('iiiii','FTEC.evcb.key')}}}(0, event.type=='keydown', event.keyCode, codepoint))\r\n\t\t\t\t\t\t\tevent.preventDefault();\r\n\t\t\t\t\t}\r\n\t\t\t\t\tbreak;\r\n\t\t\t\tcase 'touchstart':\r\n\t\t\t\tcase 'touchend':\r\n\t\t\t\tcase 'touchcancel':\r\n\t\t\t\tcase 'touchleave':\r\n\t\t\t\tcase 'touchmove':\r\n\t\t\t\t\tevent.preventDefault();\r\n\t\t\t\t\tconst touches = event.changedTouches;\r\n\t\t\t\t\tfor (let i = 0; i < touches.length; i++)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tconst t = touches[i];\r\n\t\t\t\t\t\tif (FTEC.evcb.mouse)\r\n\t\t\t\t\t\t\t{{{makeDynCall('viiffff','FTEC.evcb.mouse')}}}(t.identifier+1, true, t.pageX, t.pageY, 0, Math.sqrt(t.radiusX*t.radiusX+t.radiusY*t.radiusY));\r\n\t\t\t\t\t\tif (FTEC.evcb.button)\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tif (event.type == 'touchstart')\r\n\t\t\t\t\t\t\t\t{{{makeDynCall('viii','FTEC.evcb.button')}}}(t.identifier+1, 1, -1);\r\n\t\t\t\t\t\t\telse if (event.type != 'touchmove')\t//cancel/end/leave...\r\n\t\t\t\t\t\t\t\t{{{makeDynCall('viii','FTEC.evcb.button')}}}(t.identifier+1, 0, -1);\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t}\r\n\t\t\t\t\tbreak;\r\n\t\t\t\tcase 'dragenter':\r\n\t\t\t\tcase 'dragover':\r\n\t\t\t\t\tevent.stopPropagation();\r\n\t\t\t\t\tevent.preventDefault();\r\n\t\t\t\t\tbreak;\r\n\t\t\t\tcase 'drop':\r\n\t\t\t\t\tevent.stopPropagation();\r\n\t\t\t\t\tevent.preventDefault();\r\n\t\t\t\t\tlet files = event.dataTransfer.files;\r\n\t\t\t\t\tfor (let i = 0; i < files.length; i++)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tconst file = files[i];\r\n\t\t\t\t\t\tconst reader = new FileReader();\r\n\t\t\t\t\t\treader.onload = function(evt)\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tFTEC.loadurl(files[i].name, \"\", evt.target.result);\r\n\t\t\t\t\t\t};\r\n\t\t\t\t\t\treader.readAsArrayBuffer(file);\r\n\t\t\t\t\t}\r\n\t\t\t\t\tbreak;\r\n\t\t\t\tcase 'gamepadconnected':\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tconst gp = event.gamepad;\r\n\t\t\t\t\t\tif (FTEH.gamepads === undefined)\r\n\t\t\t\t\t\t\tFTEH.gamepads = [];\r\n\t\t\t\t\t\tFTEH.gamepads[gp.index] = gp;\r\n\t\t\t\t\t\tconsole.log(\"Gamepad connected at index %d: %s. %d buttons, %d axes.\", gp.index, gp.id, gp.buttons.length, gp.axes.length);\r\n\t\t\t\t\t}\r\n\t\t\t\t\tbreak;\r\n\t\t\t\tcase 'gamepaddisconnected':\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tconst gp = event.gamepad;\r\n\t\t\t\t\t\tdelete FTEH.gamepads[gp.index];\r\n\t\t\t\t\t\tif (FTEC.evcb.jaxis)\t//try and clear out the axis when released.\r\n\t\t\t\t\t\t\tfor (let j = 0; j < 6; j+=1)\r\n\t\t\t\t\t\t\t\t{{{makeDynCall('viifi','FTEC.evcb.jaxis')}}}(gp.index, j, 0, true);\r\n\t\t\t\t\t\tif (FTEC.evcb.jbutton)\t//try and clear out the axis when released.\r\n\t\t\t\t\t\t\tfor (let j = 0; j < 32+4; j+=1)\r\n\t\t\t\t\t\t\t\t{{{makeDynCall('viiii','FTEC.evcb.jbutton')}}}(gp.index, j, 0, true);\r\n\t\t\t\t\t\tconsole.log(\"Gamepad disconnected from index %d: %s\", gp.index, gp.id);\r\n\t\t\t\t\t}\r\n\t\t\t\t\tbreak;\r\n\t\t\t\tcase 'pointerlockerror':\r\n\t\t\t\tcase 'pointerlockchange':\r\n\t\t\t\tcase 'mozpointerlockchange':\r\n\t\t\t\tcase 'webkitpointerlockchange':\r\n\t\t\t\t\tFTEC.pointerislocked =\tdocument.pointerLockElement === Module['canvas'] ||\r\n\t\t\t\t\t\t\t\t\t\t\tdocument.mozPointerLockElement === Module['canvas'] ||\r\n\t\t\t\t\t\t\t\t\t\t\tdocument.webkitPointerLockElement === Module['canvas'];\r\n//\t\t\t\t\tconsole.log(\"Pointer lock now \" + FTEC.pointerislocked);\r\n\t\t\t\t\tbreak;\r\n\t\t\t\t\t\r\n\t\t\t\tcase 'beforeunload':\r\n\t\t\t\t\tevent.preventDefault();\r\n\t\t\t\t\treturn 'quit this game like everything else?';\r\n\t\t\t\tdefault:\r\n\t\t\t\t\tconsole.log(event);\r\n\t\t\t\t\tbreak;\r\n\t\t\t}\r\n\t\t}\r\n\t},\r\n\temscriptenfte_updatepointerlock : function(wantlock, softcursor)\r\n\t{\r\n\t\tFTEC.pointerwantlock = wantlock;\r\n\t\t//we can only apply locks when we're clicked, but should be able to unlock any time.\r\n\t\tif (wantlock == 0 && FTEC.pointerislocked != 0)\r\n\t\t{\r\n\t\t\tdocument.exitPointerLock =\tdocument.exitPointerLock    ||\r\n\t\t\t\t\t\t\t\t\t\tdocument.mozExitPointerLock ||\r\n\t\t\t\t\t\t\t\t\t\tdocument.webkitExitPointerLock;\r\n\t\t\tFTEC.pointerislocked = 0;\r\n\t\t\tif (document.exitPointerLock)\r\n\t\t\t\tdocument.exitPointerLock();\r\n\t\t}\r\n\t\tif (softcursor)\r\n\t\t\tModule.canvas.style.cursor = \"none\";\t//hide the cursor, we'll do a soft-cursor when one is needed.\r\n\t\telse\r\n\t\t\tModule.canvas.style.cursor = \"default\";\t//restore the cursor\r\n\t},\r\n\temscriptenfte_polljoyevents : function()\r\n\t{\r\n\t\t//with events, we can do unplug stuff properly.\r\n\t\t//otherwise hot unplug might be buggy.\r\n\t\tlet gamepads;\r\n//\t\tif (FTEH.gamepads !== undefined)\r\n//\t\t\tgamepads = FTEH.gamepads;\r\n//\t\telse\r\n\t\ttry\r\n\t\t{\r\n\t\t\tgamepads = navigator.getGamepads ? navigator.getGamepads() : [];\r\n\t\t}\r\n\t\tcatch(e){}\r\n\r\n\t\tif (gamepads !== undefined)\r\n\t\t{\r\n\t\t\tfor (let i = 0; i < gamepads.length; i+=1)\r\n\t\t\t{\r\n\t\t\t\tconst gp = gamepads[i];\r\n\t\t\t\tif (gp === undefined)\r\n\t\t\t\t\tcontinue;\r\n\t\t\t\tif (gp == null)\r\n\t\t\t\t\tcontinue;\t//xbox controllers tend to have 4 and exactly 4. on the plus side indexes won't change.\r\n\t\t\t\tfor (let j = 0; j < gp.buttons.length; j+=1)\r\n\t\t\t\t{\r\n\t\t\t\t\tconst b = gp.buttons[j];\r\n\t\t\t\t\tlet p;\r\n\t\t\t\t\tif (typeof(b) == \"object\")\r\n\t\t\t\t\t\tp = b.pressed || (b.value > 0.5);\t//.value is a fractional thing. oh well.\r\n\t\t\t\t\telse\r\n\t\t\t\t\t\tp = b > 0.5;\t//old chrome bug\r\n\r\n\t\t\t\t\tif (b.lastframe != p)\r\n\t\t\t\t\t{\t//cache it to avoid spam\r\n\t\t\t\t\t\tb.lastframe = p;\r\n\t\t\t\t\t\t{{{makeDynCall('viiii','FTEC.evcb.jbutton')}}}(gp.index, j, p, gp.mapping==\"standard\");\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t\tfor (let j = 0; j < gp.axes.length; j+=1)\r\n\t\t\t\t\t{{{makeDynCall('viifi','FTEC.evcb.jaxis')}}}(gp.index, j, gp.axes[j], gp.mapping==\"standard\");\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tif (FTEC.xrsession != null && FTEC.xrframe != null && FTEC.referenceSpace != null)\r\n\t\t{\r\n\t\t\t//try and figure out the head angles according to where we're told the eyes are\r\n\t\t\tlet count = 0;\r\n\t\t\tlet org={x:0,y:0,z:0}, quat={x:0,y:0,z:0,w:0};\r\n\t\t\tconst pose = FTEC.xrframe.getViewerPose(FTEC.referenceSpace);\r\n\t\t\tif (pose)\r\n\t\t\t{\r\n\t\t\t\tfor (let view of pose.views)\r\n\t\t\t\t{\r\n\t\t\t\t\torg.x += view.transform.position.x;\r\n\t\t\t\t\torg.y += view.transform.position.y;\r\n\t\t\t\t\torg.z += view.transform.position.z;\r\n\t\t\t\t\tquat.x += view.transform.orientation.x;\r\n\t\t\t\t\tquat.y += view.transform.orientation.y;\r\n\t\t\t\t\tquat.z += view.transform.orientation.z;\r\n\t\t\t\t\tquat.w += view.transform.orientation.w;\r\n\t\t\t\t\tcount++;\r\n\t\t\t\t}\r\n\t\t\t\tif (count)\r\n\t\t\t\t{\r\n\t\t\t\t\torg.x /= count;\r\n\t\t\t\t\torg.y /= count;\r\n\t\t\t\t\torg.z /= count;\r\n\t\t\t\t\tquat.x /= count;\r\n\t\t\t\t\tquat.y /= count;\r\n\t\t\t\t\tquat.z /= count;\r\n\t\t\t\t\tquat.w /= count;\r\n\r\n//const idx=-3;\r\n//console.log(\"jorientation dev:\" + idx + \" org:\"+org.x+\",\"+org.y+\",\"+org.z+\" quat:\"+quat.x+\",\"+quat.y+\",\"+quat.z+\",\"+quat.w);\r\n\t\t\t\t\t{{{makeDynCall('vifffffff','FTEC.evcb.jorientation')}}}(-3, org.x,org.y,org.z, quat.x,quat.y,quat.z,quat.w);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\tfor (const is of FTEC.xrsession.inputSources)\r\n\t\t\t{\r\n\t\t\t\tif (is === undefined || is == null)\r\n\t\t\t\t\tcontinue;\r\n\r\n\t\t\t\t//webxr doesn't really do indexes, so make em up from hands..\r\n\t\t\t\tlet idx;\r\n\t\t\t\tif (is.handedness == \"right\")\t//is.targetRayMode==\"tracked-pointer\"\r\n\t\t\t\t\tidx = -1;\r\n\t\t\t\telse if (is.handedness == \"left\")\t//is.targetRayMode==\"tracked-pointer\"\r\n\t\t\t\t\tidx = -2;\r\n\t\t\t\t//else if (is.handedness == \"head\")\t//is.targetRayMode==\"???\"... handled above.\r\n\t\t\t\t//\tidx = -3;\r\n\t\t\t\telse if (is.handedness == \"none\" && (is.targetRayMode==\"gaze\" || is.targetRayMode==\"screen\"))\r\n\t\t\t\t\tidx = -4;\r\n\t\t\t\telse\r\n\t\t\t\t\tcontinue;\t//wut?\r\n\r\n\t\t\t\t//tell the engine its orientation.\r\n\t\t\t\tconst targetRayPose = FTEC.xrframe.getPose(is.targetRaySpace, FTEC.referenceSpace);\r\n\t\t\t\tif (targetRayPose)\r\n\t\t\t\t{\r\n\t\t\t\t\tconst org = targetRayPose.transform.position;\r\n\t\t\t\t\tconst quat = targetRayPose.transform.orientation;\r\n//console.log(\"jorientation dev:\" + idx + \" org:\"+org.x+\",\"+org.y+\",\"+org.z+\" quat:\"+quat.x+\",\"+quat.y+\",\"+quat.z+\",\"+quat.w);\r\n\t\t\t\t\t{{{makeDynCall('vifffffff','FTEC.evcb.jorientation')}}}(idx, org.x,org.y,org.z, quat.x,quat.y,quat.z,quat.w);\r\n\t\t\t\t}\r\n\r\n\t\t\t\t//if it has a usable gamepad then use it.\r\n\t\t\t\tconst gp = is.gamepad;\r\n\t\t\t\tif (gp == null)\r\n\t\t\t\t\tcontinue;\r\n\t\t\t\tif (gp.mapping != \"xr-standard\")\r\n\t\t\t\t\tcontinue;\r\n\r\n\t\t\t\tfor (let j = 0; j < gp.buttons.length; j+=1)\r\n\t\t\t\t{\r\n\t\t\t\t\tconst b = gp.buttons[j];\r\n\t\t\t\t\tlet p;\r\n\t\t\t\t\tp = b.pressed;\t//.value is a fractional thing. oh well.\r\n\r\n\t\t\t\t\tif (b.lastframe != p)\r\n\t\t\t\t\t{\t//cache it to avoid spam\r\n\t\t\t\t\t\tb.lastframe = p;\r\n//console.log(\"jbutton dev:\" + idx + \" btn:\"+j+\" dn:\"+p+\" mapping:\"+gp.mapping);\r\n\t\t\t\t\t\t{{{makeDynCall('viiii','FTEC.evcb.jbutton')}}}(idx, j, p, gp.mapping==\"standard\");\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t\tfor (let j = 0; j < gp.axes.length; j+=1)\r\n\t\t\t\t{\r\n//console.log(\"jaxis dev:\" + idx + \" axis:\"+j+\" val:\"+gp.axes[j]+\" mapping:\"+gp.mapping);\r\n\t\t\t\t\t{{{makeDynCall('viifi','FTEC.evcb.jaxis')}}}(idx, j, gp.axes[j], gp.mapping==\"standard\");\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t},\r\n\temscriptenfte_setupcanvas__deps: ['$FTEC', '$Browser', 'emscriptenfte_buf_createfromarraybuf'],\r\n\temscriptenfte_setupcanvas : function(nw,nh,evresize,evmouse,evmbutton,evkey,evfile,evcbufadd,evjbutton,evjaxis,evjorientation,evwantfullscreen)\r\n\t{\r\n\t\ttry\r\n\t\t{\r\n\t\tFTEC.evcb.resize = evresize;\r\n\t\tFTEC.evcb.mouse = evmouse;\r\n\t\tFTEC.evcb.button = evmbutton;\r\n\t\tFTEC.evcb.key = evkey;\r\n\t\tFTEC.evcb.loadfile = evfile;\r\n\t\tFTEC.evcb.cbufaddtext = evcbufadd;\r\n\t\tFTEC.evcb.jbutton = evjbutton;\r\n\t\tFTEC.evcb.jaxis = evjaxis;\r\n\t\tFTEC.evcb.jorientation = evjorientation;\r\n\t\tFTEC.evcb.wantfullscreen = evwantfullscreen;\r\n\r\n\t\tif ('GamepadEvent' in window)\r\n\t\t\tFTEH.gamepads = [];\t//don't bother ever trying to poll if we can use gamepad events. this will hopefully avoid weirdness.\r\n\r\n\t\tif (!FTEC.donecb)\r\n\t\t{\r\n\t\t\tFTEC.donecb = 1;\r\n\t\t\tvar events = ['mousedown', 'mouseup', 'mousemove', 'wheel', 'mousewheel', 'mouseout', \r\n\t\t\t\t\t\t'keypress', 'keydown', 'keyup', \r\n\t\t\t\t\t\t'touchstart', 'touchend', 'touchcancel', 'touchleave', 'touchmove',\r\n\t\t\t\t\t\t'dragenter', 'dragover', 'drop',\r\n\t\t\t\t\t\t'message', 'resize',\r\n\t\t\t\t\t\t'pointerlockerror', 'pointerlockchange', 'mozpointerlockchange', 'webkitpointerlockchange',\r\n\t\t\t\t\t\t'focus', 'blur'];   //try to fix alt-tab\r\n\t\t\tevents.forEach(function(event)\r\n\t\t\t{\r\n\t\t\t\tModule['canvas'].addEventListener(event, FTEC.handleevent, {capture:true, passive:false});\r\n\t\t\t});\r\n\r\n\t\t\tvar docevents = ['keypress', 'keydown', 'keyup',\r\n\t\t\t\t\t\t\t'pointerlockerror', 'pointerlockchange', 'mozpointerlockchange', 'webkitpointerlockchange', 'visibilitychange'];\r\n\t\t\tdocevents.forEach(function(event)\r\n\t\t\t{\r\n\t\t\t\tdocument.addEventListener(event, FTEC.handleevent, true);\r\n\t\t\t});\r\n\r\n\t\t\tvar windowevents = ['message','gamepadconnected', 'gamepaddisconnected', 'beforeunload', 'focus', 'blur'];\r\n\t\t\twindowevents.forEach(function(event)\r\n\t\t\t{\r\n\t\t\t\twindow.addEventListener(event, FTEC.handleevent, true);\r\n\t\t\t});\r\n\r\n//\t\t\tBrowser.resizeListeners.push(function(w, h) {\r\n//\t\t\t\tFTEC.handleevent({\r\n//\t\t\t\t\ttype: 'resize',\r\n//\t\t\t\t});\r\n//\t\t\t});\r\n\t\t}\r\n\t\tvar ctx = Browser.createContext(Module['canvas'], true, true, {});\r\n\t\tif (ctx == null)\r\n\t\t{\r\n\t\t\tvar msg = \"Unable to set up webgl context.\\n\\nPlease use a browser that supports it and has it enabled\\nYour graphics drivers may also be blacklisted, so try updating those too. woo, might as well update your entire operating system while you're at it.\\nIt'll be expensive, but hey, its YOUR money, not mine.\\nYou can probably just disable the blacklist, but please don't moan at me when your computer blows up, seriously, make sure those drivers are not too buggy.\\nI knew a guy once. True story. Boring, but true.\\nYou're probably missing out on something right now. Don't you just hate it when that happens?\\nMeh, its probably just tinkertoys, right?\\n\\nYou know, you could always try Internet Explorer, you never know, hell might have frozen over.\\nDon't worry, I wasn't serious.\\n\\nTum te tum. Did you get it working yet?\\nDude, fix it already.\\n\\nThis message was brought to you by Sleep Deprivation, sponsoring quake since I don't know when\";\r\n\t\t\tif (FTEC.ctxwarned == 0)\r\n\t\t\t{\r\n\t\t\t\tFTEC.ctxwarned = 1;\r\n\t\t\t\tconsole.log(msg);\r\n\t\t\t\talert(msg);\r\n\t\t\t}\r\n\t\t\treturn 0;\r\n\t\t}\r\n//\t\tBrowser.setCanvasSize(nw, nh, false);\r\n\r\n\t\twindow.onresize = function()\r\n\t\t{\r\n\t\t\tlet scale = window.devicePixelRatio;\t//urgh. haxx.\r\n\t\t\tif (scale <= 0)\r\n\t\t\t\tscale = 1;\r\n\t\t\t//emscripten's browser library will revert sizes wrongly or something when we're fullscreen, so make sure that doesn't happen.\r\n//\t\t\tif (Browser.isFullScreen)\r\n//\t\t\t{\r\n//\t\t\t\tBrowser.windowedWidth = window.innerWidth;\r\n//\t\t\t\tBrowser.windowedHeight = window.innerHeight;\r\n//\t\t\t}\r\n//\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tlet rect = Module['canvas'].getBoundingClientRect();\r\n\t\t\t\tBrowser.setCanvasSize(rect.width*scale, rect.height*scale, false);\r\n\t\t\t}\r\n\t\t\tif (FTEC.evcb.resize != 0)\r\n\t\t\t\t{{{makeDynCall('viif','FTEC.evcb.resize')}}}(Module['canvas'].width, Module['canvas'].height, scale);\r\n\t\t};\r\n\t\twindow.onresize();\r\n\r\n\t\tif (FTEC.evcb.hashchange)\r\n\t\t{\r\n\t\t\twindow.onhashchange = function()\r\n\t\t\t{\r\n\t\t\t\tFTEC.loadurl(location.hash.substring(1), \"\", undefined);\r\n\t\t\t};\r\n\t\t}\r\n\r\n\t\t//try to grab the mouse if we can\r\n\t\ttry{\r\n\t\t\t_emscriptenfte_updatepointerlock(false, false);\r\n\t\t}catch(e){console.log(e);}\r\n\r\n\t\t//stop the screen from turning off.\r\n\r\n\t\ttry{\r\n\t\t\tif (!FTEC.wakelock && navigator.wakeLock)\r\n\t\t\t\tnavigator.wakeLock.request(\"screen\").then((value)=>{FTEC.wakelock = value;value.addEventListener(\"release\", ()=>{FTEC.wakelock=null;});}).catch(()=>{});\r\n\t\t}catch(e){console.log(e);}\r\n\r\n\t\t} catch(e)\r\n\t\t{\r\n\t\tconsole.log(e);\r\n\t\t}\r\n\r\n\t\tFTEC.xrsupport = 0;\r\n\t\tif (navigator.xr)\r\n\t\t{\r\n\t\t\tfunction rechecksupport()\r\n\t\t\t{\r\n\t\t\t\tnavigator.xr.isSessionSupported(\"inline\",\t\t{}).then((works) => {FTEC.xrsupport = ((FTEC.xrsupport&~1)<<0)|(works<<0);});\r\n\t\t\t\tnavigator.xr.isSessionSupported(\"immersive-vr\",\t{}).then((works) => {FTEC.xrsupport = ((FTEC.xrsupport&~1)<<1)|(works<<1);});\r\n\t\t\t\tnavigator.xr.isSessionSupported(\"immersive-ar\",\t{}).then((works) => {FTEC.xrsupport = ((FTEC.xrsupport&~1)<<2)|(works<<2);});\r\n\t\t\t};\r\n\r\n\t\t\t//check it now\r\n\t\t\trechecksupport();\r\n\r\n\t\t\t//and keep it up to date.\r\n\t\t\tnavigator.xr.addEventListener('devicechange', (e)=>{rechecksupport();});\r\n\t\t}\r\n\r\n\t\treturn 1;\r\n\t},\r\n\temscriptenfte_xr_issupported : function()\r\n\t{\r\n\t\tif (navigator.xr)\r\n\t\t\treturn FTEC.xrsupport;\r\n\t\treturn 0;\r\n\t},\r\n\temscriptenfte_xr_isactive : function()\r\n\t{\r\n\t\tvar ret = 0;\r\n\t\tif (FTEC.xrsession!=null)\r\n\t\t{\r\n\t\t\tret |= 1;\r\n\r\n\t\t\tconst pose = FTEC.xrframe.getViewerPose(FTEC.referenceSpace);\r\n\t\t\tif (pose.views.length)\r\n\t\t\t\tret |= 2;\r\n\t\t}\r\n\t\treturn ret;\r\n\t},\r\n\temscriptenfte_xr_setup : function(mode)\r\n\t{\r\n\t\tif (mode == -3)\r\n\t\t\tmode = ((FTEC.xrsession!=null)?-1:-2);\r\n\t\tif (mode == -2)\r\n\t\t{\t//pick some suitable mode\r\n\t\t\tif (FTEC.xrsupport & (1<<2))\r\n\t\t\t\tmode = 2;\t//ar\r\n\t\t\telse if (FTEC.xrsupport & (1<<1))\r\n\t\t\t\tmode = 1;\t//vr\r\n\t\t\telse if (FTEC.xrsupport & (1<<0))\r\n\t\t\t\tmode = 0;\t//inline\r\n\t\t}\r\n\t\tif (mode == -1)\t//kill any current session.\r\n\t\t\t_emscriptenfte_xr_shutdown();\r\n\t\telse if (FTEC.xrsession == null && navigator.xr && mode >= 0 && mode < 3)\r\n\t\t{\r\n\t\t\tconst modes = [\"inline\", \"immersive-vr\", \"immersive-ar\"];\r\n\t\t\tnavigator.xr.requestSession(modes[mode], {optionalFeatures:[\"local\"]}).then((session) => {\r\n\t\t\t\tFTEC.xrsession = session;\r\n\r\n\t\t\t\tsession.addEventListener('end', ()=>{console.log(\"Session ended\"); _emscriptenfte_xr_shutdown();});\r\n\t\t\t\tsession.addEventListener('inputsourcechange', (e)=>{console.log(\"inputsourcechange\", e);});\r\n\t\t\t\tsession.addEventListener('select', (e)=>{console.log(\"select\", e);});\r\n\t\t\t\tsession.addEventListener('selectstart', (e)=>{console.log(\"selectstart\", e);});\r\n\t\t\t\tsession.addEventListener('selectend', (e)=>{console.log(\"selectend\", e);});\r\n\t\t\t\tsession.addEventListener('squeeze', (e)=>{console.log(\"squeeze\", e);});\r\n\t\t\t\tsession.addEventListener('squeezestart', (e)=>{console.log(\"squeezestart\", e);});\r\n\t\t\t\tsession.addEventListener('squeezeend', (e)=>{console.log(\"squeezeend\", e);});\r\n\r\n\t\t\t\t/*Module['canvas'].addEventListener(\"webglcontextlost\", (event) => {\r\n\t\t\t\t\tevent.canceled = true;\r\n\t\t\t\t\tconsole.log(\"webglcontextlost\");\r\n\t\t\t\t});\r\n\t\t\t\tModule['canvas'].addEventListener(\"webglcontextrestored\", (event) => {\r\n\t\t\t\t\tconsole.log(\"webglcontextrestored\");\r\n\t\t\t\t});*/\r\n\r\n\t\t\t\tvar madecompatible = function()\r\n\t\t\t\t{\r\n\t\t\t\t\tsession.updateRenderState({baseLayer: new XRWebGLLayer(session, Module.ctx), depthFar:8192, depthNear:1});\r\n\r\n\t\t\t\t\tsession.requestReferenceSpace(\"local\").then((refspace) => {\r\n\t\t\t\t\t\tFTEC.referenceSpace = refspace;\r\n\t\t\t\t\t\tFTEC.xrsession.requestAnimationFrame(FTEC.doxrframe);\r\n\t\t\t\t\t}).catch((e)=>{\t//fall back to viewer if that failed.\r\n\t\t\t\t\t\tsession.requestReferenceSpace(\"viewer\").then((refspace) => {\r\n\t\t\t\t\t\t\tFTEC.referenceSpace = refspace;\r\n\t\t\t\t\t\t\tFTEC.xrsession.requestAnimationFrame(FTEC.doxrframe);\r\n\t\t\t\t\t\t}).catch((e)=>{console.error(\"requestReferenceSpace\",e); _emscriptenfte_xr_shutdown();});\t//and just in case...\r\n\t\t\t\t\t});\r\n\t\t\t\t};\r\n\r\n\t\t\t\tif (mode == 0)\r\n\t\t\t\t\tmadecompatible();\t//chrome88+ throws a hissy fit if we makeXRCompatible on an inline context.\r\n\t\t\t\telse\r\n\t\t\t\t\tModule.ctx.makeXRCompatible().then(madecompatible).catch((e)=>{console.error(\"makeXRCompatible\", e);_emscriptenfte_xr_shutdown();});\r\n\t\t\t}).catch((e)=>{console.error(\"requestSession\", e);_emscriptenfte_xr_shutdown();});\r\n\t\t\treturn true;\r\n        }\r\n        return false;   //not really success, more that it can't possibly work.\r\n\t},\r\n\temscriptenfte_xr_geteyeinfo : function(maxeyes, eyeptr)\r\n\t{\r\n\t\tif (!FTEC.xrframe || !FTEC.xrsession)\r\n\t\t\treturn 0;\t//nope.\r\n\r\n\t\tconst pose = FTEC.xrframe.getViewerPose(FTEC.referenceSpace);\r\n\t\tif (!pose)\r\n\t\t\treturn 0;\t//nope.\r\n\t\tconst layer = FTEC.xrsession.renderState.baseLayer;\r\n\t\tvar e = 0;\r\n\r\n\t\tif (!FTEC.xrfbo)\t//make the fbo handle available to the C code\r\n\t\t\tFTEC.xrfbo = GL.framebuffers.length;\t//alloc a new one.\r\n\t\tGL.framebuffers[FTEC.xrfbo] = layer.framebuffer;\t//update it so the engine can actually use it...\r\n\r\n\t\teyeptr>>=2;\r\n\t\tfor (const view of pose.views)\r\n\t\t{\r\n\t\t\tif (e == maxeyes)\r\n\t\t\t\tbreak;\r\n\t\t\tconst viewport = layer.getViewport(view);\r\n\r\n\t\t\tHEAP32[eyeptr+0] = FTEC.xrfbo;\r\n\t\t\teyeptr+=1;\r\n\r\n\t\t\tHEAP32[eyeptr+0] = viewport.x;\r\n\t\t\tHEAP32[eyeptr+1] = viewport.y;\r\n\t\t\tHEAP32[eyeptr+2] = viewport.width;\r\n\t\t\tHEAP32[eyeptr+3] = viewport.height;\r\n\t\t\teyeptr+=4;\r\n\r\n\t\t\tHEAPF32[eyeptr+ 0] = view.projectionMatrix[0];\r\n\t\t\tHEAPF32[eyeptr+ 1] = view.projectionMatrix[1];\r\n\t\t\tHEAPF32[eyeptr+ 2] = view.projectionMatrix[2];\r\n\t\t\tHEAPF32[eyeptr+ 3] = view.projectionMatrix[3];\r\n\t\t\tHEAPF32[eyeptr+ 4] = view.projectionMatrix[4];\r\n\t\t\tHEAPF32[eyeptr+ 5] = view.projectionMatrix[5];\r\n\t\t\tHEAPF32[eyeptr+ 6] = view.projectionMatrix[6];\r\n\t\t\tHEAPF32[eyeptr+ 7] = view.projectionMatrix[7];\r\n\t\t\tHEAPF32[eyeptr+ 8] = view.projectionMatrix[8];\r\n\t\t\tHEAPF32[eyeptr+ 9] = view.projectionMatrix[9];\r\n\t\t\tHEAPF32[eyeptr+10] = view.projectionMatrix[10];\r\n\t\t\tHEAPF32[eyeptr+11] = view.projectionMatrix[11];\r\n\t\t\tHEAPF32[eyeptr+12] = view.projectionMatrix[12];\r\n\t\t\tHEAPF32[eyeptr+13] = view.projectionMatrix[13];\r\n\t\t\tHEAPF32[eyeptr+14] = view.projectionMatrix[14];\r\n\t\t\tHEAPF32[eyeptr+15] = view.projectionMatrix[15];\r\n\t\t\teyeptr+=16;\r\n\r\n\t\t\tHEAPF32[eyeptr+ 0] = view.transform.matrix[0];\r\n\t\t\tHEAPF32[eyeptr+ 1] = view.transform.matrix[1];\r\n\t\t\tHEAPF32[eyeptr+ 2] = view.transform.matrix[2];\r\n\t\t\tHEAPF32[eyeptr+ 3] = view.transform.matrix[3];\r\n\t\t\tHEAPF32[eyeptr+ 4] = view.transform.matrix[4];\r\n\t\t\tHEAPF32[eyeptr+ 5] = view.transform.matrix[5];\r\n\t\t\tHEAPF32[eyeptr+ 6] = view.transform.matrix[6];\r\n\t\t\tHEAPF32[eyeptr+ 7] = view.transform.matrix[7];\r\n\t\t\tHEAPF32[eyeptr+ 8] = view.transform.matrix[8];\r\n\t\t\tHEAPF32[eyeptr+ 9] = view.transform.matrix[9];\r\n\t\t\tHEAPF32[eyeptr+10] = view.transform.matrix[10];\r\n\t\t\tHEAPF32[eyeptr+11] = view.transform.matrix[11];\r\n\t\t\tHEAPF32[eyeptr+12] = view.transform.matrix[12];\r\n\t\t\tHEAPF32[eyeptr+13] = view.transform.matrix[13];\r\n\t\t\tHEAPF32[eyeptr+14] = view.transform.matrix[14];\r\n\t\t\tHEAPF32[eyeptr+15] = view.transform.matrix[15];\r\n\t\t\teyeptr+=16;\r\n\r\n\t\t\te++;\r\n\t\t}\r\n\t\treturn e;\r\n\t},\r\n\temscriptenfte_xr_shutdown : function()\r\n\t{\r\n\t\tif (FTEC.xrfbo)\r\n\t\t{\t//destroy that handle\r\n\t\t\tGL.framebuffers[FTEC.xrfbo] = null;\r\n\t\t\tFTEC.xrfbo = 0;\r\n\t\t}\r\n\r\n\t\tif (FTEC.xrsession)\r\n\t\t{\r\n\t\t\tFTEC.xrsession.end();\r\n\t\t\tFTEC.xrsession = null;\r\n\t\t}\r\n\t\tFTEC.xrframe = null;\r\n\t},\r\n\temscriptenfte_settitle : function(txt)\r\n\t{\r\n\t\tdocument.title = UTF8ToString(txt);\r\n\t},\r\n\temscriptenfte_abortmainloop : function(fname, fatal)\r\n\t{\r\n\t\tfname = UTF8ToString(fname);\r\n\t\tif (fatal)\r\n\t\t\tFTEC.aborted = true;\r\n\t\tif (Module['stackTrace'])\r\n\t\t\tthrow 'oh noes! something bad happened in ' + fname + '!\\n' + Module['stackTrace']();\r\n\t\tthrow 'oh noes! something bad happened!\\n';\r\n\t},\r\n\r\n\temscriptenfte_setupmainloop__deps: ['$FTEC'],\r\n\temscriptenfte_setupmainloop : function(fnc)\r\n\t{\r\n\t\tModule['noExitRuntime'] = true;\r\n\t\tFTEC.aborted = fnc==0;\r\n\r\n\t\tModule[\"sched\"] = FTEC.step;\t//this is stupid.\r\n\r\n\t\tFTEC.evcb.frame = fnc;\r\n\t\tif (fnc)\r\n\t\t{\r\n\t\t\t//don't start it instantly, so we can distinguish between types of errors (emscripten sucks!).\r\n\t\t\tsetTimeout(FTEC.step, 1, performance.now());\r\n\t\t}\r\n\t\telse if (Module[\"close\"])\r\n\t\t\tModule[\"close\"]();\r\n\t\telse if (Module[\"quiturl\"])\r\n\t\t\tdocument.location.replace(Module[\"quiturl\"]);\r\n\t\telse\r\n\t\t{\r\n\t\t\ttry\r\n\t\t\t{\r\n\t\t\t\tif (history.length == 1)\r\n\t\t\t\t\twindow.close();\r\n\t\t\t\telse\r\n\t\t\t\t\thistory.back();\t//can't close. go back to the previous page though.\r\n\t\t\t}\r\n\t\t\tcatch(e)\r\n\t\t\t{\r\n\t\t\t}\r\n\t\t}\r\n\t\t//else kill it?\r\n\t},\r\n\r\n\temscriptenfte_ticks_ms : function()\r\n\t{\t//milliseconds...\r\n\t\treturn Date.now();\r\n\t},\r\n\temscriptenfte_uptime_ms : function()\r\n\t{\t//milliseconds...\r\n\t\treturn performance.now();\r\n\t},\r\n\r\n\temscriptenfte_openfile : function()\r\n\t{\r\n\t\tif (FTEC.evcb.loadfile != 0)\r\n\t\t{\r\n\t\t\twindow.showOpenFilePicker(\r\n\t\t\t\t{\ttypes:[\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tdescription: \"Packages\",\r\n\t\t\t\t\t\t\taccept:{\"text/*\":[\".pk3\", \".pak\", \".pk4\", \".zip\"]}\r\n\t\t\t\t\t\t},\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tdescription: \"Maps\",\r\n\t\t\t\t\t\t\taccept:{\"text/*\":[\".bsp.gz\", \".bsp\", \".map\", \".hmp\"]}\r\n\t\t\t\t\t\t},\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tdescription: \"Demos\",\r\n\t\t\t\t\t\t\taccept:{\"application/*\":[\".mvd.gz\", \".qwd.gz\", \".dem.gz\", \".mvd\", \".qwd\", \".dem\"]}\t//dm2?\r\n\t\t\t\t\t\t},\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tdescription: \"QuakeTV Info\",\r\n\t\t\t\t\t\t\taccept:{\"application/*\":[\".qtv\"]}\r\n\t\t\t\t\t\t},\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tdescription: \"FTE Manifest\",\r\n\t\t\t\t\t\t\taccept:{\"text/*\":[\".fmf\"]}\r\n\t\t\t\t\t\t},\r\n\t\t\t\t\t\t//model formats?... nah, too many/weird. they can always\r\n\t\t\t\t\t\t//audio formats?\teww\r\n\t\t\t\t\t\t//image formats?\tdouble eww!\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tdescription: \"Configs\",\r\n\t\t\t\t\t\t\taccept:{\"text/*\":[\".cfg\", \".rc\"]}\r\n\t\t\t\t\t\t}],\r\n\t\t\t\t\texcludeAcceptAllOption:false,\t//let em pick anything. we actually support more than listed here (and bitrot...)\r\n\t\t\t\t\tid:\"openfile\",\t//remember the dir we were in for the next invocation\r\n\t\t\t\t\tmultiple:true\t//does this make sense? not for a demo but does for *.pak or *.bsp\r\n\t\t\t\t}).then((r)=>\r\n\t\t\t\t{\r\n\t\t\t\t\tfor (let i of r)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\ti.getFile().then((f)=>\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tf.arrayBuffer().then((arraybuf)=>\r\n\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\tif (FTEC.evcb.loadfile != 0)\r\n\t\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\t\tconst handle = _emscriptenfte_buf_createfromarraybuf(arraybuf);\r\n\t\t\t\t\t\t\t\t\tlet blen = lengthBytesUTF8(i.name)+1;\r\n\t\t\t\t\t\t\t\t\tlet urlptr = _malloc(blen);\r\n\t\t\t\t\t\t\t\t\tstringToUTF8(f.name, urlptr, blen);\r\n\t\t\t\t\t\t\t\t\tblen = lengthBytesUTF8(f.type)+1;\r\n\t\t\t\t\t\t\t\t\tlet mimeptr = _malloc(blen);\r\n\t\t\t\t\t\t\t\t\tstringToUTF8(f.type, mimeptr,blen);\r\n\t\t\t\t\t\t\t\t\t{{{makeDynCall('viii','FTEC.evcb.loadfile')}}}(urlptr, mimeptr, handle);\r\n\t\t\t\t\t\t\t\t\t_free(mimeptr);\r\n\t\t\t\t\t\t\t\t\t_free(urlptr);\r\n\t\t\t\t\t\t\t\t\twindow.focus();\r\n\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t});\r\n\t\t\t\t\t\t}).catch((e)=>{console.error(\"getFile() failed:\");console.error(e);});\r\n\t\t\t\t\t}\r\n\t\t\t\t}).catch((e)=>{console.log(\"showOpenFilePicker() aborted\", e);});\r\n\t\t}\r\n\t},\r\n\r\n\temscriptenfte_buf_create__deps : ['emscriptenfte_handle_alloc'],\r\n\temscriptenfte_buf_create : function()\r\n\t{\r\n\t\tvar b = {h:-1, r:1, l:0,m:4096,d:new Uint8Array(4096), n:null};\r\n\t\tb.h = _emscriptenfte_handle_alloc(b);\r\n\t\treturn b.h;\r\n\t},\r\n\t//filesystem emulation\r\n\temscriptenfte_buf_open__deps : ['emscriptenfte_buf_create'],\r\n\temscriptenfte_buf_open : function(name, createifneeded)\r\n\t{\r\n\t\tname = UTF8ToString(name);\r\n\t\tvar f = FTEH.f[name];\r\n\t\tvar r = -1;\r\n\t\tif (f == null)\r\n\t\t{\r\n\t\t\tif (!FTEC.localstorefailure)\r\n\t\t\t{\r\n\t\t\t\ttry\r\n\t\t\t\t{\r\n\t\t\t\t\tif (localStorage && createifneeded != 2)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tvar str = localStorage.getItem(name);\r\n\t\t\t\t\t\tif (str != null)\r\n\t\t\t\t\t\t{\r\n\t\t//\t\t\t\t\tconsole.log('read file '+name+': ' + str);\r\n\r\n\t\t\t\t\t\t\tvar len = str.length;\r\n\t\t\t\t\t\t\tvar buf = new Uint8Array(len);\r\n\t\t\t\t\t\t\tfor (var i = 0; i < len; i++)\r\n\t\t\t\t\t\t\t\tbuf[i] = str.charCodeAt(i);\r\n\r\n\t\t\t\t\t\t\tvar b = {h:-1, r:2, l:len,m:len,d:buf, n:name};\r\n\t\t\t\t\t\t\tr = b.h = _emscriptenfte_handle_alloc(b);\r\n\t\t\t\t\t\t\tFTEH.f[name] = b;\r\n\t\t\t\t\t\t\treturn b.h;\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t\tcatch(e)\r\n\t\t\t\t{\r\n\t\t\t\t\tconsole.log('exception while trying to read local storage for ' + name);\r\n\t\t\t\t\tconsole.log(e);\r\n\t\t\t\t\tconsole.log('disabling further attempts to access local storage');\r\n\t\t\t\t\tFTEC.localstorefailure = true;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\tif (createifneeded)\r\n\t\t\t\tr = _emscriptenfte_buf_create();\r\n\t\t\tif (r != -1)\r\n\t\t\t{\r\n\t\t\t\tf = FTEH.h[r];\r\n\t\t\t\tf.r+=1;\r\n\t\t\t\tf.n = name;\r\n\t\t\t\tFTEH.f[name] = f;\r\n\t\t\t\tif (FTEH.f[name] != f || f.n != name)\r\n\t\t\t\t\tconsole.log('error creating file '+name);\r\n\t\t\t}\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tf.r+=1;\r\n\t\t\tr = f.h;\r\n\t\t}\r\n\t\tif (f != null && createifneeded == 2)\r\n\t\t\tf.l = 0;  //truncate it.\r\n\t\treturn r;\r\n\t},\r\n\temscriptenfte_buf_rename : function(oldname, newname)\r\n\t{\r\n\t\toldname = UTF8ToString(oldname);\r\n\t\tnewname = UTF8ToString(newname);\r\n\t\tvar f = FTEH.f[oldname];\r\n\t\tif (f == null)\r\n\t\t\treturn 0;\r\n\t\tif (FTEH.f[newname] != null)\r\n\t\t\treturn 0;\r\n\t\tFTEH.f[newname] = f;\r\n\t\tdelete FTEH.f[oldname];\r\n\t\tf.n = newname;\r\n\r\n\t\tif (Module['cache'])\r\n\t\t{\r\n\t\t\tModule['cache'].match(\"/_/\"+oldname).then((oldresp)=>{\r\n\t\t\t\tif (oldresp === undefined)\r\n\t\t\t\t\tModule['cache'].delete(\"/_/\"+newname);\t//'overwrite' the new name.\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\tModule['cache'].put(\"/_/\"+newname, oldresp);\r\n\t\t\t\t\tModule['cache'].delete(\"/_/\"+oldname);\r\n\t\t\t\t}\r\n\t\t\t}).catch(()=>{\r\n\t\t\t\tModule['cache'].delete(\"/_/\"+oldname);\r\n\t\t\t\tModule['cache'].delete(\"/_/\"+newname);\t//if we're overwriting with a file we don't have then just wipe the old one.\r\n\t\t\t});\r\n\t\t}\r\n\t\treturn 1;\r\n\t},\r\n\temscriptenfte_buf_delete : function(name)\r\n\t{\r\n\t\tname = UTF8ToString(name);\r\n\t\tvar f = FTEH.f[name];\r\n\t\tif (f)\r\n\t\t{\r\n\t\t\tdelete FTEH.f[name];\r\n\t\t\tf.n = null;\r\n\t\t\t_emscriptenfte_buf_release(f.h);\r\n\r\n\t\t\tif (Module['cache'])\r\n\t\t\t\tModule['cache'].delete(\"/_/\"+name);\r\n\t\t\treturn 1;\r\n\t\t}\r\n\t\treturn 0;\r\n\t},\r\n\temscritenfte_buf_enumerate : function(cb, ctx, sz)\r\n\t{\r\n\t\tvar n = Object.keys(FTEH.f);\r\n\t\tvar c = n.length, i;\r\n\t\tfor (i = 0; i < c; i++)\r\n\t\t{\r\n\t\t\tstringToUTF8(n[i], ctx, sz);\r\n\t\t\t{{{makeDynCall('vii','cb')}}}(ctx, FTEH.f[n[i]].l);\r\n\t\t}\r\n\t},\r\n\temscriptenfte_buf_pushtolocalstore : function(handle)\r\n\t{\r\n\t\tvar b = FTEH.h[handle];\r\n\t\tif (b == null)\r\n\t\t{\r\n\t\t\tModule.printError('emscriptenfte_buf_pushtolocalstore with invalid handle');\r\n\t\t\treturn;\r\n\t\t}\r\n\t\tif (b.n == null)\r\n\t\t\treturn;\r\n\t\tvar data = b.d;\r\n\t\tvar len = b.l;\r\n\t\ttry\r\n\t\t{\r\n\t\t\tif (b.n.endsWith(\".pak\") || b.n.endsWith(\".pk3\") || b.n.endsWith(\".kpf\") || b.n.indexOf(\"/dlcache/\")>0)\r\n\t\t\t{\r\n\t\t\t\tif (Module['cache'])\r\n\t\t\t\t{\r\n\t\t\t\t\tconsole.log(\"Saving \"+b.n+\" to cache (\"+len+\" bytes).\");\r\n\t\t\t\t\tModule['cache'].put(\"/_/\"+b.n, new Response(data, {\"headers\":{\"Content-Type\":\"application/octet-stream\", \"Content-Length\":len}}));\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t\tconsole.log(\"cache not available\");\r\n\t\t\t}\r\n\t\t\telse if (localStorage)\r\n\t\t\t{\r\n\t\t\t\tvar foo = \"\";\r\n\t\t\t\t//use a divide and conquer implementation instead for speed?\r\n\t\t\t\tfor (var i = 0; i < len; i++)\r\n\t\t\t\t\tfoo += String.fromCharCode(data[i]);\r\n\t\t\t\tlocalStorage.setItem(b.n, foo);\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t\tconsole.log('local storage not supported');\r\n\t\t}\r\n\t\tcatch (e)\r\n\t\t{\r\n\t\t\tconsole.log('exception while trying to save ' + b.n, e);\r\n\t\t}\r\n\t},\r\n\temscriptenfte_buf_release : function(handle)\r\n\t{\r\n\t\tvar b = FTEH.h[handle];\r\n\t\tif (b == null)\r\n\t\t{\r\n\t\t\tModule.printError('emscriptenfte_buf_release with invalid handle');\r\n\t\t\treturn;\r\n\t\t}\r\n\t\tb.r -= 1;\r\n\t\tif (b.r == 0)\r\n\t\t{\r\n\t\t\tif (b.n != null)\r\n\t\t\t\tdelete FTEH.f[b.n];\r\n\t\t\tdelete FTEH.h[handle];\r\n\t\t\tb.d = null;\r\n\t\t}\r\n\t},\r\n\temscriptenfte_buf_getsize : function(handle)\r\n\t{\r\n\t\tvar b = FTEH.h[handle];\r\n\t\treturn b.l;\r\n\t},\r\n\temscriptenfte_buf_read : function(handle, offset, data, len)\r\n\t{\r\n\t\tvar b = FTEH.h[handle];\r\n\t\tif (offset+len > b.l)\t//clamp the read\r\n\t\t\tlen = b.l - offset;\r\n\t\tif (len < 0)\r\n\t\t{\r\n\t\t\tlen = 0;\r\n\t\t\tif (offset+len >= b.l)\r\n\t\t\t\treturn -1;\r\n\t\t}\r\n\t\tHEAPU8.set(b.d.subarray(offset, offset+len), data);\r\n\t\treturn len;\r\n\t},\r\n\temscriptenfte_buf_write : function(handle, offset, data, len)\r\n\t{\r\n\t\tvar b = FTEH.h[handle];\r\n\t\tif (len < 0)\r\n\t\t\tlen = 0;\r\n\t\tif (offset+len > b.m)\r\n\t\t{\t//extend it if needed.\r\n\t\t\tb.m = offset + len + 4095;\r\n\t\t\tb.m = b.m & ~4095;\r\n\t\t\tvar nd = new Uint8Array(b.m);\r\n\t\t\tnd.set(b.d, 0);\r\n\t\t\tb.d = nd;\r\n\t\t}\r\n\t\tb.d.set(HEAPU8.subarray(data, data+len), offset);\r\n\t\tif (offset + len > b.l)\r\n\t\t\tb.l = offset + len;\r\n\t\treturn len;\r\n\t},\r\n\r\n\temscriptenfte_ws_connect__deps: ['emscriptenfte_handle_alloc'],\r\n\temscriptenfte_ws_connect : function(brokerurl, protocolname)\r\n\t{\r\n\t\tvar _url = UTF8ToString(brokerurl);\r\n\t\tvar _protocol = UTF8ToString(protocolname);\r\n\t\tvar s = {ws:null, inq:[], err:0, con:0};\r\n\t\ttry {\r\n\t\t\ts.ws = new WebSocket(_url, _protocol);\r\n\t\t} catch(err) { console.log(err); }\r\n\t\tif (s.ws === undefined)\r\n\t\t\treturn -1;\r\n\t\tif (s.ws == null)\r\n\t\t\treturn -1;\r\n\t\ts.ws.binaryType = 'arraybuffer';\r\n\t\ts.ws.onerror = function(event) {s.con = 0; s.err = 1;};\r\n\t\ts.ws.onclose = function(event) {s.con = 0; s.err = 1;};\r\n\t\ts.ws.onopen = function(event) {s.con = 1;};\r\n\t\ts.ws.onmessage = function(event)\r\n\t\t\t{\r\n\t\t\t\tassert(typeof event.data !== 'string' && event.data.byteLength, 'websocket data is not usable');\r\n\t\t\t\ts.inq.push(new Uint8Array(event.data));\r\n\t\t\t};\r\n\r\n\t\treturn _emscriptenfte_handle_alloc(s);\r\n\t},\r\n\temscriptenfte_ws_close : function(sockid)\r\n\t{\r\n\t\tvar s = FTEH.h[sockid];\r\n\t\tif (s === undefined)\r\n\t\t\treturn -1;\r\n\r\n\t\ts.callcb = null;\r\n\r\n\t\tif (s.ws != null)\r\n\t\t{\r\n\t\t\ts.ws.close();\r\n\t\t\ts.ws = null;\t//make sure to avoid circular references\r\n\t\t}\r\n\r\n\t\tif (s.pc != null)\r\n\t\t{\r\n\t\t\ts.pc.close();\r\n\t\t\ts.pc = null;\t//make sure to avoid circular references\r\n\t\t}\r\n\r\n\t\tif (s.broker != null)\r\n\t\t{\r\n\t\t\ts.broker.close();\r\n\t\t\ts.broker = null;\t//make sure to avoid circular references\r\n\t\t}\r\n\t\tdelete FTEH.h[sockid];\t//socked is no longer accessible.\r\n\t\treturn 0;\r\n\t},\r\n\t//separate call allows for more sane flood control when fragmentation is involved.\r\n\temscriptenfte_ws_cansend : function(sockid, extra, maxpending)\r\n\t{\r\n\t\tvar s = FTEH.h[sockid];\r\n\t\tif (s === undefined)\r\n\t\t\treturn 1;\t//go on punk, make my day.\r\n\t\treturn ((s.ws.bufferedAmount+extra) < maxpending);\r\n\t},\r\n\temscriptenfte_ws_send : function(sockid, data, len)\r\n\t{\r\n\t\tvar s = FTEH.h[sockid];\r\n\t\tif (s === undefined)\r\n\t\t\treturn -1;\r\n\t\tif (s.con == 0)\r\n\t\t\treturn 0; //not connected yet\r\n\t\tif (s.err != 0)\r\n\t\t\treturn -1;\r\n\t\tif (len == 0)\r\n\t\t\treturn 0; //...\r\n\t\ts.ws.send(HEAPU8.subarray(data, data+len));\r\n\t\treturn len;\r\n\t},\r\n\temscriptenfte_ws_recv : function(sockid, data, len)\r\n\t{\r\n\t\tvar s = FTEH.h[sockid];\r\n\t\tif (s === undefined)\r\n\t\t\treturn -1;\r\n\t\tvar inp = s.inq.shift();\r\n\t\tif (inp)\r\n\t\t{\r\n\t\t\tif (inp.length > len)\r\n\t\t\t\tinp.length = len;\r\n\t\t\tHEAPU8.set(inp, data);\r\n\t\t\treturn inp.length;\r\n\t\t}\r\n\t\tif (s.err)\r\n\t\t\treturn -1;\r\n\t\treturn 0;\r\n\t},\r\n\r\n\temscriptenfte_rtc_create__deps: ['emscriptenfte_handle_alloc'],\r\n\temscriptenfte_rtc_create : function(clientside, ctxp, ctxi, callback, pcconfig)\r\n\t{\r\n\t\ttry {\r\n\t\t\tpcconfig = JSON.parse(UTF8ToString(pcconfig));\r\n\t\t} catch(err) {pcconfig = {};}\r\n\r\n\t\tvar dcconfig = {ordered: false, maxRetransmits: 0, reliable:false};\r\n\r\n\t\tvar s = {pc:null, ws:null, inq:[], err:0, con:0, isclient:clientside, callcb:\r\n\t\t\tfunction(evtype,stringdata)\r\n\t\t\t{\t//private helper\r\n\r\n//console.log(\"emscriptenfte_rtc_create callback: \" + evtype);\r\n\r\n\t\t\t\tvar stringlen = (stringdata.length*3)+1;\r\n\t\t\t\tvar dataptr = _malloc(stringlen);\r\n\t\t\t\tstringToUTF8(stringdata, dataptr, stringlen);\r\n\t\t\t\t{{{makeDynCall('viiii','callback')}}}(ctxp,ctxi,evtype,dataptr);\r\n\t\t\t\t_free(dataptr);\r\n\t\t\t}\r\n\t\t};\r\n\r\n\t\tif (RTCPeerConnection === undefined)\r\n\t\t{\t//IE or something.\r\n\t\t\tconsole.log(\"RTCPeerConnection undefined\");\r\n\t\t\treturn -1;\r\n\t\t}\r\n\r\n\t\ts.pc = new RTCPeerConnection(pcconfig);\r\n\t\tif (s.pc === undefined)\r\n\t\t{\r\n\t\t\tconsole.log(\"webrtc failed to create RTCPeerConnection\");\r\n\t\t\treturn -1;\r\n\t\t}\r\n\r\n//create the dataconnection\r\n\t\ts.ws = s.pc.createDataChannel('quake', dcconfig);\r\n\t\ts.ws.binaryType = 'arraybuffer';\r\n\t\ts.ws.onclose = function(event)\r\n\t\t\t{\r\n\t\t\t\ts.con = 0;\r\n\t\t\t\ts.err = 1;\r\n\t\t\t};\r\n\t\ts.ws.onopen = function(event)\r\n\t\t\t{\r\n\t\t\t\ts.con = 1;\r\n\t\t\t};\r\n\t\ts.ws.onmessage = function(event)\r\n\t\t\t{\r\n\t\t\t\tassert(typeof event.data !== 'string' && event.data.byteLength);\r\n\t\t\t\ts.inq.push(new Uint8Array(event.data));\r\n\t\t\t};\r\n\r\n\t\ts.pc.onicecandidate = function(e)\r\n\t\t\t{\r\n\t\t\t\tvar desc;\r\n\t\t\t\tif (1)\r\n\t\t\t\t\tdesc = JSON.stringify(e.candidate);\r\n\t\t\t\telse\r\n\t\t\t\t\tdesc = e.candidate.candidate;\r\n\t\t\t\tif (desc == null)\r\n\t\t\t\t\treturn;\t//no more...\r\n\t\t\t\ts.callcb(4, desc);\r\n\t\t\t};\r\n\t\ts.pc.ondatachannel = function(e)\r\n\t\t\t{\r\n\t\t\t\ts.recvchan = e.channel;\r\n\t\t\t\ts.recvchan.binaryType = 'arraybuffer';\r\n\t\t\t\ts.recvchan.onmessage = s.ws.onmessage;\r\n\t\t\t};\r\n\t\ts.pc.onconnectionstatechange = function(e)\r\n\t\t\t{\r\n//console.log(s.pc.connectionState);\r\n//console.log(e);\r\n\t\t\t\tswitch (s.pc.connectionState)\r\n\t\t\t\t{\r\n\t\t\t\t//case \"new\":\r\n\t\t\t\t//case \"checking\":\r\n\t\t\t\t//case \"connected\":\r\n\t\t\t\tcase \"disconnected\":\r\n\t\t\t\t\ts.err = 1;\r\n\t\t\t\t\tbreak;\r\n\t\t\t\tcase \"closed\":\r\n\t\t\t\t\ts.con = 0;\r\n\t\t\t\t\ts.err = 1;\r\n\t\t\t\t\tbreak;\r\n\t\t\t\tcase \"failed\":\r\n\t\t\t\t\ts.err = 1;\r\n\t\t\t\t\tbreak;\r\n\t\t\t\tdefault:\r\n\t\t\t\t\tbreak;\r\n\t\t\t\t}\r\n\t\t\t};\r\n\r\n\t\tif (clientside)\r\n\t\t{\r\n\t\t\ts.pc.createOffer().then(\r\n\t\t\t\tfunction(desc)\r\n\t\t\t\t{\r\n\t\t\t\t\ts.pc.setLocalDescription(desc);\r\n\r\n\t\t\t\t\tif (1)\r\n\t\t\t\t\t\tdesc = JSON.stringify(desc);\r\n\t\t\t\t\telse\r\n\t\t\t\t\t\tdesc = desc.sdp;\r\n\r\n\t\t\t\t\ts.callcb(3, desc);\r\n\t\t\t\t},\r\n\t\t\t\tfunction(event)\r\n\t\t\t\t{\r\n\t\t\t\t\ts.err = 1;\r\n\t\t\t\t}\r\n\t\t\t);\r\n\t\t}\r\n\r\n\t\treturn _emscriptenfte_handle_alloc(s);\r\n\t},\r\n\temscriptenfte_rtc_offer : function(sockid, offer, offertype)\r\n\t{\r\n\t\tvar desc;\r\n\t\tvar s = FTEH.h[sockid];\r\n\t\toffer = UTF8ToString(offer);\r\n\t\toffertype = UTF8ToString(offertype);\r\n\t\tif (s === undefined)\r\n\t\t\treturn -1;\r\n\r\n\t\ttry\r\n\t\t{\r\n\t\t\ttry\r\n\t\t\t{\r\n\t\t\t\tdesc = JSON.parse(offer);\r\n\t\t\t}\r\n\t\t\tcatch(e)\r\n\t\t\t{\r\n\t\t\t\tdesc = {sdp:offer, type:offertype};\r\n\t\t\t}\r\n\r\n\t\t\ts.pc.setRemoteDescription(desc).then(() =>\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tif (!s.isclient)\r\n\t\t\t\t\t\t{\t//server must give a response.\r\n\t\t\t\t\t\t\ts.pc.createAnswer().then(\r\n\t\t\t\t\t\t\t\tfunction(desc)\r\n\t\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\t\ts.pc.setLocalDescription(desc);\r\n\r\n\t\t\t\t\t\t\t\t\tif (1)\r\n\t\t\t\t\t\t\t\t\t\tdesc = JSON.stringify(desc);\r\n\t\t\t\t\t\t\t\t\telse\r\n\t\t\t\t\t\t\t\t\t\tdesc = desc.sdp;\r\n\r\n\t\t\t\t\t\t\t\t\ts.callcb(3, desc);\r\n\t\t\t\t\t\t\t\t},\r\n\t\t\t\t\t\t\t\tfunction(event)\r\n\t\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\t\ts.err = 1;\r\n\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t);\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t}, err =>\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tconsole.log(desc);\r\n\t\t\t\t\t\tconsole.log(err);\r\n\t\t\t\t\t});\r\n\t\t} catch(err) { console.log(err); }\r\n\r\n\t},\r\n\temscriptenfte_rtc_candidate : function(sockid, offer)\r\n\t{\r\n\t\tvar s = FTEH.h[sockid];\r\n\t\toffer = UTF8ToString(offer);\r\n\t\tif (s === undefined)\r\n\t\t\treturn -1;\r\n\r\n\t\ttry\t//don't screw up if the peer is trying to screw with us.\r\n\t\t{\r\n\t\t\tvar desc;\r\n\t\t\ttry\r\n\t\t\t{\r\n\t\t\t\tdesc = JSON.parse(offer);\r\n\t\t\t}\r\n\t\t\tcatch(e)\r\n\t\t\t{\r\n\t\t\t\tdesc = {candidate:offer, sdpMid:null, sdpMLineIndex:0};\r\n\t\t\t}\r\n\t\t\ts.pc.addIceCandidate(desc);\r\n\t\t} catch(err) { console.log(err); }\r\n\t\treturn 0;\r\n\t},\r\n\r\n\temscriptenfte_async_wget_data2 : function(url, postdata, postlen, postmimetype, ctx, onload, onerror, onprogress)\r\n\t{\r\n\t\tvar _url = UTF8ToString(url);\r\n\t\tvar http = new XMLHttpRequest();\r\n\t\ttry\r\n\t\t{\r\n\t\t\tif (postdata)\r\n\t\t\t{\r\n\t\t\t\thttp.open('POST', _url, true);\r\n\r\n\t\t\t\tif (postmimetype)\r\n\t\t\t\t\thttp.setRequestHeader(\"Content-Type\", UTF8ToString(postmimetype));\r\n\t\t\t\telse\r\n\t\t\t\t\thttp.setRequestHeader(\"Content-Type\", \"application/x-www-form-urlencoded\");\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\thttp.open('GET', _url, true);\r\n\t\t\t}\r\n\t\t}\r\n\t\tcatch(e)\r\n\t\t{\r\n\t\t\tif (onerror)\r\n\t\t\t\t{{{makeDynCall('vii','onerror')}}}(ctx, 404);\r\n\t\t\treturn;\r\n\t\t}\r\n\t\thttp.responseType = 'arraybuffer';\r\n\r\n\t\thttp.onload = function(e)\r\n\t\t{\r\n\t\t\tif (http.status == 200)\r\n\t\t\t{\r\n\t\t\t\tif (onload)\r\n\t\t\t\t\t{{{makeDynCall('vii','onload')}}}(ctx, _emscriptenfte_buf_createfromarraybuf(http.response));\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tif (onerror)\r\n\t\t\t\t\t{{{makeDynCall('vii','onerror')}}}(ctx, http.status);\r\n\t\t\t}\r\n\t\t};\r\n\r\n\t\thttp.onerror = function(e)\r\n\t\t{\r\n\t\t\t//Note: Unfortunately it is not possible to distinguish between dns, network, certificate, or CORS errors (other than viewing the browser's log).\r\n\t\t\t//      This is apparently intentional to prevent sites probing lans - cors will make them all seem dead and thus uninteresting targets.\r\n\t\t\tif (onerror)\r\n\t\t\t\t{{{makeDynCall('vii','onerror')}}}(ctx, 0);\r\n\t\t};\r\n\r\n\t\thttp.onprogress = function(e)\r\n\t\t{\r\n\t\t\tif (onprogress)\r\n\t\t\t\t{{{makeDynCall('viii','onprogress')}}}(ctx, e.loaded, e.total);\r\n\t\t};\r\n\r\n\t\ttry\t//ffs\r\n\t\t{\r\n\t\t\tif (postdata)\r\n\t\t\t{\r\n\t\t\t\thttp.send(HEAPU8.subarray(postdata, postdata+postlen));\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\thttp.send(null);\r\n\t\t\t}\r\n\t\t}\r\n\t\tcatch(e)\r\n\t\t{\r\n\t\t\tconsole.log(e);\r\n\t\t\thttp.onerror(e);\r\n\t\t}\r\n\t},\r\n\r\n\temscriptenfte_al_loadaudiofile : function(buf, dataptr, datasize)\r\n\t{\r\n\t\tvar ctx = AL;\r\n\t\t//match emscripten's openal support.\r\n\t\tif (!buf)\r\n\t\t\treturn;\r\n\r\n\t\tvar albuf = AL.buffers[buf];\r\n\t\tAL.buffers[buf] = null; //alIsBuffer will report it as invalid now\r\n\r\n\t\ttry\r\n\t\t{\r\n\t\t\t//its async, so it needs its own copy of an arraybuffer, not just a view.\r\n\t\t\tvar abuf = new ArrayBuffer(datasize);\r\n\t\t\tvar rbuf = new Uint8Array(abuf);\r\n\t\t\trbuf.set(HEAPU8.subarray(dataptr, dataptr+datasize));\r\n\t\t\tAL.currentCtx.audioCtx.decodeAudioData(abuf,\r\n\t\t\t\t\tfunction(buffer)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\t//Warning: This depends upon emscripten's specific implementation of alBufferData\r\n\t\t\t\t\t\talbuf.bytesPerSample = 2;\r\n\t\t\t\t\t\talbuf.channels = 1;\r\n\t\t\t\t\t\talbuf.length = buffer.length;\r\n\t\t\t\t\t\talbuf.frequency = buffer.sampleRate;\r\n\t\t\t\t\t\talbuf.audioBuf = buffer;\r\n\r\n\t\t\t\t\t\tctx.buffers[buf] = albuf;\t//and its valid again!\r\n\t\t\t\t\t},\r\n\t\t\t\t\tfunction()\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tconsole.log(\"Audio Callback failed!\");\r\n\t\t\t\t\t\tctx.buffers[buf] = albuf;\r\n\t\t\t\t\t}\r\n\t\t\t\t);\r\n\t\t}\r\n\t\tcatch (e)\r\n\t\t{\r\n\t\t\tconsole.log(\"unable to decode audio data\");\r\n\t\t\tconsole.log(e);\r\n\t\t\tctx.buffers[buf] = albuf;\r\n\t\t}\r\n\t},\r\n\temscriptenfte_pcm_loadaudiofile : function(ctx, callback, dataptr, datasize, snd_speed)\r\n\t{\r\n\t\tconst successcb = function(buffer)\r\n\t\t{\r\n\t\t\tconst frames = buffer.length;\r\n\t\t\tconst chans = buffer.numberOfChannels;\r\n\t\t\tconst rate = buffer.sampleRate;\r\n\t\t\tconst outptr = _malloc(2*frames*chans);\r\n\t\t\tif (!outptr)\r\n\t\t\t\treturn; //it went away?\r\n\t\t\tconst dst = HEAP16.subarray(outptr>>1, (outptr+2*frames*chans)>>1);\r\n\r\n\t\t\t/*if (buffer.numberOfChannels == 1)\r\n\t\t\t\tdst.set(buffer.getChannelData(0));\r\n\t\t\telse*/ for (let c = 0; c < buffer.numberOfChannels; c++)\r\n\t\t\t{\r\n\t\t\t\tconst src = buffer.getChannelData(c);\r\n\t\t\t\tfor (let f = 0; f < frames; f++)\r\n\t\t\t\t\tdst[f*chans+c] = 16384*src[f];\t//docs imply it should be 32767 but I'm getting unhealthy clipping\r\n\t\t\t}\r\n\t\t\t{{{makeDynCall('viiif','callback')}}}(ctx, outptr, frames, chans, rate);\r\n\t\t\t_free(outptr);\r\n\t\t};\r\n\t\tconst failurecb = function(buffer)\r\n\t\t{\r\n\t\t\t{{{makeDynCall('viiif','callback')}}}(ctx, 0, 0, 0, 0);\r\n\t\t};\r\n\r\n\t\ttry{\r\n\r\n\t\t\tconst abuf = new ArrayBuffer(datasize);\r\n\t\t\tconst rbuf = new Uint8Array(abuf);\r\n\t\t\trbuf.set(HEAPU8.subarray(dataptr, dataptr+datasize));\r\n\t\t\tconst ac = new AudioContext({sampleRate:snd_speed});\r\n\t\t\tac.decodeAudioData(abuf, successcb, failurecb);\t//do the decode\r\n\t\t\r\n\t\t\treturn true;\r\n\t\t}\r\n\t\tcatch(e)\r\n\t\t{\r\n\t\t\tconsole.log(\"emscriptenfte_pcm_loadaudiofile failure :(\");\r\n\t\t}\r\n\t\treturn false;\r\n\t},\r\n\r\n\temscriptenfte_gl_loadtexturefile : function(texid, widthptr, heightptr, dataptr, datasize, fname, dopremul, genmips)\r\n\t{\r\n\t\tfunction encode64(data) {\r\n\t\t\tvar BASE = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';\r\n\t\t\tvar PAD = '=';\r\n\t\t\tvar ret = '';\r\n\t\t\tvar leftchar = 0;\r\n\t\t\tvar leftbits = 0;\r\n\t\t\tfor (var i = 0; i < data.length; i++) {\r\n\t\t\t\tleftchar = (leftchar << 8) | data[i];\r\n\t\t\t\tleftbits += 8;\r\n\t\t\t\twhile (leftbits >= 6) {\r\n\t\t\t\t\tvar curr = (leftchar >> (leftbits-6)) & 0x3f;\r\n\t\t\t\t\tleftbits -= 6;\r\n\t\t\t\t\tret += BASE[curr];\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\tif (leftbits == 2) {\r\n\t\t\t\tret += BASE[(leftchar&3) << 4];\r\n\t\t\t\tret += PAD + PAD;\r\n\t\t\t} else if (leftbits == 4) {\r\n\t\t\t\tret += BASE[(leftchar&0xf) << 2];\r\n\t\t\t\tret += PAD;\r\n\t\t\t}\r\n\t\t\treturn ret;\r\n\t\t}\r\n\r\n\t\t//make sure the texture is defined before its loaded, so we get no errors\r\n\t\tGLctx.texImage2D(GLctx.TEXTURE_2D, 0, GLctx.RGBA, 1,1,0,GLctx.RGBA, GLctx.UNSIGNED_BYTE, null);\r\n\r\n\t\tvar img = new Image();\r\n\t\tvar gltex = GL.textures[texid];\r\n\t\timg.name = UTF8ToString(fname);\r\n\t\timg.onload = function()\r\n\t\t{\r\n\t\t\tif (img.width < 1 || img.height < 1)\r\n\t\t\t{\r\n\t\t\t\tconsole.log(\"emscriptenfte_gl_loadtexturefile(\"+img.name+\"): bad image size\\n\");\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\t\t\tvar oldtex = GLctx.getParameter(GLctx.TEXTURE_BINDING_2D);\t//blurgh, try to avoid breaking anything in this unexpected event.\r\n\t\t\tGLctx.bindTexture(GLctx.TEXTURE_2D, gltex);\r\n\t\t\tif (dopremul)\r\n\t\t\t\tGLctx.pixelStorei(GLctx.UNPACK_PREMULTIPLY_ALPHA_WEBGL, true);\r\n\t\t\tGLctx.texImage2D(GLctx.TEXTURE_2D, 0, GLctx.RGBA, GLctx.RGBA, GLctx.UNSIGNED_BYTE, img);\r\n\t\t\tif (dopremul)\r\n\t\t\t\tGLctx.pixelStorei(GLctx.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false);\r\n\t\t\tif (genmips)\r\n\t\t\t\tGLctx.generateMipmap(GLctx.TEXTURE_2D);\r\n\t\t\tGLctx.bindTexture(GLctx.TEXTURE_2D, oldtex);\r\n\t\t};\r\n\t\timg.crossorigin = true;\r\n\t\timg.src = \"data:image/png;base64,\" + encode64(HEAPU8.subarray(dataptr, dataptr+datasize));\t//png... jpeg... browsers don't seem to actually care\r\n\t},\r\n\r\n\tSys_Clipboard_PasteText: function(cbt, callback, ctx)\r\n\t{\r\n\t\tif (cbt != 0)\r\n\t\t\treturn;\t//don't do selections.\r\n\r\n\t\tlet docallback = function(text)\r\n\t\t{\r\n\t\t\tFTEC.clipboard = text;\r\n\t\t\ttry{\r\n\t\t\t\tlet stringlen = (text.length*3)+1;\r\n\t\t\t\tlet dataptr = _malloc(stringlen);\r\n\t\t\t\tstringToUTF8(text, dataptr, stringlen);\r\n\t\t\t\t{{{makeDynCall('vii','callback')}}}(ctx, dataptr);\r\n\t\t\t\t_free(dataptr);\r\n\t\t\t}catch(e){\r\n\t\t\t}\r\n\t\t};\r\n\r\n\t\t//try pasting. if it fails then use our internal string.\r\n\t\ttry\r\n\t\t{\r\n\t\t\tnavigator.clipboard.readText()\r\n\t\t\t\t.then(docallback)\r\n\t\t\t\t.catch((e)=>{docallback(FTEC.clipboard)});\r\n\t\t}\r\n\t\tcatch(e)\r\n\t\t{\t//clipboard API not supported at all.\r\n\t\t\tconsole.log(e);\t//happens in firefox. lets print it so we know WHY its failing.\r\n\t\t\tdocallback(FTEC.clipboard);\r\n\t\t}\r\n\t},\r\n\tSys_SaveClipboard: function(cbt, text)\r\n\t{\r\n\t\tif (cbt != 0)\r\n\t\t\treturn;\t//don't do selections.\r\n\r\n\t\tFTEC.clipboard = UTF8ToString(text);\r\n\r\n\t\ttry\r\n\t\t{\r\n\t\t\t//try and copy it to the system clipboard too.\r\n\t\t\tnavigator.clipboard.writeText(FTEC.clipboard);\r\n\t\t}\r\n\t\tcatch {}\r\n\t}\r\n});\r\n\r\n"
  },
  {
    "path": "engine/web/fteshell.html",
    "content": "<!doctype html>\r\n<html lang=\"en-us\">\r\n  <head>\r\n\t<meta charset=\"utf-8\">\r\n\t<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">\r\n\t<link rel=\"manifest\" href=\"fte_pwa.json\" />\r\n\t<meta name=viewport content=\"width=device-width, initial-scale=1\">\r\n\t<title>FTE Engine</title>\r\n\t<style>\r\n\thtml,body { background-color:#000000; color:#808080; height:100%;width:100%;margin:0;padding:0;}\r\n\t.emscripten { padding-right: 0; margin-left: auto; margin-right: auto; display: block; }\r\n\tdiv.emscripten { text-align: center; padding:0; margin: 0;}\r\n\t/* the canvas *must not* have any border or padding, or mouse coords will be wrong */\r\n\tcanvas.emscripten { border: 0px none; width:100%; height:100%; padding:0; margin: 0;}\r\n\t</style>\r\n  </head>\r\n  <body ondrop=\"gotdrop(event);\" ondragover=\"event.preventDefault()\">\r\n\t<div class=\"emscripten\" id=\"status\">Please allow/unblock our javascript to play.</div>\r\n\t<div id=\"dropzone\" ondrop=\"gotdrop(event);\" ondragover=\"event.preventDefault()\" hidden=1>Drop Zone</div>\r\n\t<button type=\"button\" onclick=\"adduserfile()\" id=\"addfile\" hidden=1>Add File(s)</button>\r\n\t<button type=\"button\" onclick=\"begin()\" id=\"begin\" hidden=1>Click To Begin!</button>\r\n\t<div class=\"emscripten\">\r\n\t\t<progress value=\"0\" max=\"100\" id=\"progress\" hidden=1></progress>  \r\n\t</div>\r\n\t<canvas class=\"emscripten\" id=\"canvas\" oncontextmenu=\"event.preventDefault()\" hidden=1></canvas>\r\n\t<script type='text/javascript'>\r\n\r\n//set up a service worker, so that we can actually be installed if so, instead of living in the more general browser cache. Yay for running an online game offline...\r\nif (\"serviceWorker\" in navigator)\r\n    navigator.serviceWorker.register(\"fte_pwa_sw.js\", { scope: \"./\" }).catch((error) => {console.error(`Service worker registration failed: ${error}`);});\r\n\r\nfunction str2ab(str)\r\n{   //helper function. Not utf-8, so stick to ascii chars\r\n    var buf = new ArrayBuffer(str.length);\r\n    var bufView = new Uint8Array(buf);\r\n    for (var i=0, strLen=str.length; i < strLen; i++)\r\n        bufView[i] = str.charCodeAt(i);\r\n    return buf;\r\n}\r\n\r\n// connect to canvas\r\nvar Module = {\r\n\tfiles:\r\n\t{\t//these can be arraybuffers(you'll need a helper to define those) or promises(fte will block till they complete), or strings (which will be interpretted as urls and downloaded before any C code is run)\r\n\t\t//note that the code below will skip the file-drop prompt if there's any files specified here (or there's a #foo.fmf file specified)\r\n\t\t//string values are deemed to be URLs, so use the str2ab(\"\") helper if you want to embed raw file data instead. \r\n//\t\t\"default.fmf\": \"default.fmf\",\r\n//\t\t\"id1/pak0.pak\": \"pak0.pak\",\r\n//\t\t\"id1/default.cfg\": \"webdefaults.cfg\",\t//autoexec.cfg is evil.\r\n/*\t\t\"id1/touch.cfg\": str2ab(\"showpic_removeall\\n\"\r\n\t\t\t\t\t\t\t\t\"showpic touch_moveforward.tga\tfwd\t\t-128\t-112 bm\t32\t32\t+forward\t5\\n\"\r\n\t\t\t\t\t\t\t\t\"showpic touch_moveback.tga\t\tback\t-128\t-80\tbm\t32\t32\t+back\t\t5\\n\"\r\n\t\t\t\t\t\t\t\t\"showpic touch_moveleft.tga\t\tleft\t-160\t-88\tbm\t32\t32\t+moveleft\t5\\n\"\r\n\t\t\t\t\t\t\t\t\"showpic touch_moveright.tga\trght\t-96\t\t-88\tbm\t32\t32\t+moveright\t5\\n\"\r\n\t\t\t\t\t\t\t\t\"showpic touch_attack.tga\t\tfire\t-160\t-160 bm\t32\t32\t+attack\t\t5\\n\"\r\n\t\t\t\t\t\t\t\t\"showpic touch_jump.tga\t\t\tjump\t128\t\t-80\tbm\t32\t32\t+jump\t\t5\\n\"\r\n\t\t\t\t\t\t\t\t\"showpic touch_weapons.tga\t\tweap\t80\t\t-80\tbm\t32\t32\t+weaponwheel\t5\\n\"\r\n\t\t\t\t\t\t\t\t\"showpic touch_menu.tga\t\t\tmenu\t-32\t\t0\ttr\t32\t32\ttogglemenu 10\\n\"),\r\n*/\t},\r\n//\tautostart: true,\t//uncomment to start up straight away without asking about files or not. hope your files are okay.\r\n//\tquiturl: \"/\",\t//url to jump to when 'quitting' (otherwise uses history.back).\r\n//\targuments:[\"+alias\",\"f_startup\",\"connect\",\"wss://theservertojoin\", \"-manifest\",\"default.fmf\"], //beware the scheme registration stuff (pwa+js methods).\r\n//\tmanifest: \"index.html.fmf\", // '-manifest' arg if args are not explicit. also inhibits the #foo.fmf thing.\r\n\tprint: function(msg)\r\n\t{\t//stdout...\r\n\t\tconsole.log(msg);\r\n\t},\r\n\tprintErr: function(text)\r\n\t{\t//stderr...\r\n\t\tconsole.log(text);\r\n\t},\r\n\tcanvas: document.getElementById('canvas'),\t//for webgl to attach to\r\n\tsetStatus: function(text)\r\n\t{\t//gets spammed some prints during startup. blame emscripten.\r\n\t\tif (Module.setStatus.interval)\r\n\t\t\tclearInterval(Module.setStatus.interval);\r\n\t\tvar m = text.match(/([^(]+)\\((\\d+(\\.\\d+)?)\\/(\\d+)\\)/);\r\n\t\tvar statusElement = document.getElementById('status');\r\n\t\tvar progressElement = document.getElementById('progress');\r\n\t\tif (m) {\r\n\t\t\ttext = m[1];\r\n\t\t\tprogressElement.value = parseInt(m[2])*100;\r\n\t\t\tprogressElement.max = parseInt(m[4])*100;\r\n\t\t\tprogressElement.hidden = false;\r\n\t\t} else {\r\n\t\t\tprogressElement.value = null;\r\n\t\t\tprogressElement.max = null;\r\n\t\t\tprogressElement.hidden = true;\r\n\t\t}\r\n\t\tstatusElement.innerHTML = text;\r\n\t\tstatusElement.hidden = text.length==0;\r\n\t},\r\n//\tpreRun: [],\r\n\ttotalDependencies: 0,\r\n\tmonitorRunDependencies: function(left)\r\n\t{\t//progress is progress...\r\n\t\tthis.totalDependencies = Math.max(this.totalDependencies, left);\r\n\t\tModule.setStatus(left ? 'Preparing... (' + (this.totalDependencies-left) + '/' + this.totalDependencies + ')' : 'All downloads complete.');\r\n\t},\r\n//\tonRuntimeInitialized: function(){},\r\n\tpostRun:\r\n\t[\t//each of these are called after main was run. we should have our mainloop set up now\r\n\t\tfunction()\r\n\t\t{\r\n\t\t\tif (Module[\"sched\"] === undefined)\r\n\t\t\t{\t//our main function failed to set up the main loop. ie: main didn't get called. panic.\r\n\t\t\t\talert(\"Unable to initialise. You may need to restart your browser. If you get this often and inconsistently, consider using a 64bit browser instead.\");\r\n\t\t\t\tModule.setStatus(\"Initialisation Failure\");\r\n\t\t\t}\r\n\t\t}\r\n\t],\r\n};\r\nfunction begin()\r\n{\r\n\tif (Module.began)\r\n\t\treturn;\r\n\tModule.began = true;\r\n\tdocument.getElementById('dropzone').hidden = true;\r\n\tdocument.getElementById('addfile').hidden = true;\r\n\tdocument.getElementById('begin').hidden = true;\r\n\tModule.setStatus('Downloading...');\r\n\r\n\t// make a script. do it the hard way for the error.\r\n\tvar s = document.createElement('script');\r\n\t// set it up\r\n\ts.setAttribute('src',\"ftewebgl.js\");\r\n\ts.setAttribute('type',\"text/javascript\");\r\n\ts.setAttribute('charset',\"utf-8\");\r\n\ts.addEventListener('error', function() {alert(\"Oh noes! we got an error!\"); Module.setStatus(\"Unable to download engine javascript\");}, false);\r\n\t// add to DOM\r\n\tdocument.head.appendChild(s);\r\n}\r\n\r\n//stuff to facilitate our drag+drop filesystem support\r\nfunction fixupfilepath(fname, path)\r\n{\t//we just have a filename, try to guess where to put it.\r\n\tif (path != \"\")\r\n\t\treturn path+fname;\t//already has a path. use it. this allows people to drag+drop gamedirs.\r\n\tvar ext = fname.substr(fname.lastIndexOf('.') + 1);\r\n\tif (ext == 'fmf' || ext == 'kpf')\t//these are the only files that really make sense in the root.\r\n\t\treturn fname;\r\n\tif (ext == 'bsp' || ext == 'map' || ext == 'lit' || ext == 'lux')\r\n\t\treturn \"id1/maps/\" + fname;\t//bsps get their own extra subdir\r\n\treturn \"id1/\" + fname;\t//probably a pak. maybe a cfg, no idea really.\r\n}\r\nfunction remfile(fname)\r\n{\r\n\tdelete Module.files[fname];\r\n\tshowfiles();\t//repaint\r\n}\r\nfunction renamefile(fname)\r\n{\r\n\tconst nname = prompt(\"Please enter new name\", fname);\r\n\tif (nname != null)\r\n\t{\r\n\t\tModule.files[nname] = Module.files[fname];\r\n\t\tdelete Module.files[fname];\r\n\t\tshowfiles();\r\n\t}\r\n}\r\nfunction remcfile(fname)\r\n{\r\n\tModule['cache'].delete(\"/_/\"+fname);\r\n\tModule['cache'].keys().then((keys)=>{Module['cachekeys'] = keys; showfiles();}); //repaint\r\n}\r\nfunction remlfile(fname)\r\n{\r\n\twindow.localStorage.removeItem(fname);\r\n\tshowfiles(); //repaint\r\n}\r\nfunction savelfile(fname)\r\n{\r\n\twindow.showSaveFilePicker({id: \"openfile\", startIn: \"documents\", suggestedName: fname})\r\n\t.then(async (h)=>{\r\n\t\tlet data = await window.localStorage.getitem(fname);\r\n\t\tlet f = await h.createWriteable();\r\n\t\tawait f.write(data);\r\n\t\tawait f.close();\r\n\t});\r\n}\r\nfunction adduserfile()\r\n{\r\n\twindow.showOpenFilePicker(\r\n\t\t{   types:[\r\n\t\t\t\t{\r\n\t\t\t\t\tdescription: \"Packages\",\r\n\t\t\t\t\taccept:{\"text/*\":[\".pk3\", \".pak\", \".pk4\", \".zip\"]}\r\n\t\t\t\t},\r\n\t\t\t\t{\r\n\t\t\t\t\tdescription: \"Maps\",\r\n\t\t\t\t\taccept:{\"text/*\":[\".bsp.gz\", \".bsp\", \".map\"]}\r\n\t\t\t\t},\r\n\t\t\t\t{\r\n\t\t\t\t\tdescription: \"Demos\",\r\n\t\t\t\t\taccept:{\"application/*\":[\".mvd.gz\", \".qwd.gz\", \".dem.gz\", \".mvd\", \".qwd\", \".dem\"]}  //dm2?\r\n\t\t\t\t},\r\n\t\t\t\t{\r\n\t\t\t\t\tdescription: \"FTE Manifest\",\r\n\t\t\t\t\taccept:{\"text/*\":[\".fmf\"]}\r\n\t\t\t\t},\r\n\t\t\t\t//model formats?... nah, too many/weird. they can always\r\n\t\t\t\t//audio formats?    eww\r\n\t\t\t\t//image formats?    double eww!\r\n\t\t\t\t{\r\n\t\t\t\t\tdescription: \"Configs\",\r\n\t\t\t\t\taccept:{\"text/*\":[\".cfg\", \".rc\"]}\r\n\t\t\t\t}],\r\n\t\t\texcludeAcceptAllOption:false,   //let em pick anything. we actually support more than listed here (and bitrot...)\r\n\t\t\tid:\"openfile\",  //remember the dir we were in for the next invocation\r\n\t\t\tmultiple:true\r\n\t\t})\r\n\t.then((r)=>\r\n\t\t{\r\n\t\t\tlet gamedir = prompt(\"Please enter gamedir\", \"id1\");\r\n\t\t\tif (gamedir != \"\")\r\n\t\t\t\tgamedir = gamedir+\"/\";\r\n\t\t\tfor (let i of r)\r\n\t\t\t{\r\n\t\t\t\ti.getFile().then((f)=>\r\n\t\t\t\t{\r\n\t\t\t\t\tvar n = fixupfilepath(f.name, gamedir);\r\n\t\t\t\t\tModule.files[n]=f.arrayBuffer();\t//actually a promise...\r\n\t\t\t\t\tModule.files[n].then(buf=>{Module.files[n]=buf;showfiles();});\t//try and resolve it now.\r\n\t\t\t\t});\r\n\t\t\t}\r\n\t\t}).catch((e)=>{console.log(\"showOpenFilePicker() aborted\", e);});\r\n}\r\nfunction showfiles()\r\n{\t//print the pending file list in some pretty way\r\n\tif (Module.began)\r\n\t\treturn;\r\n\tModule.setStatus('');\r\n\tdocument.getElementById('dropzone').hidden = false;\r\n\tdocument.getElementById('begin').hidden = false;\r\n\tlet nt = \"<H1>FTE Engine (Browser Port)</H1>\";\r\n\tnt = nt + \"Drag gamedirs or individual package files here to make them available!<pre>\";\r\n\tlet keys = Object.keys(Module.files);\r\n\tnt += \"Session Files (\"+keys.length+\"):<br/>\";\r\n\tfor(let i = 0; i < keys.length; i++)\r\n\t{\r\n\t\tlet rem = \t\" <a href=\\\"javascript:remfile('\"+keys[i]+\"');\\\">[forget]</a>\" +\r\n\t\t\t\t\t\" <a href=\\\"javascript:renamefile('\"+keys[i]+\"');\\\">[rename]</a>\";\r\n\t\tif (Module.files[keys[i]] instanceof ArrayBuffer)\r\n\t\t{\r\n\t\t\tlet sz = Module.files[keys[i]].byteLength;\r\n\t\t\tif (sz > 512*1024)\r\n\t\t\t\tsz = (sz / (1024*1024)) + \"mb\";\r\n\t\t\telse if (sz > 512)\r\n\t\t\t\tsz = (sz / 1024) + \"kb\";\r\n\t\t\telse\r\n\t\t\t\tsz = (sz) + \" bytes\";\r\n\t\t\tnt += \"    \" + keys[i] + \" (\"+sz+\")\"+rem+\"<br/>\";\r\n\t\t}\r\n\t\telse\r\n\t\t\tnt += \"    \" + keys[i] + rem + \"<br/>\";\r\n\t}\r\n\r\n\t//cache is for large data files. for any pk3s the user might add in-engine. large stuff that's easy to fix if it gets wiped.\r\n\tconst cache = Module['cache'];\r\n\tconst ckeys = Module['cachekeys'];\r\n\tif (ckeys !== undefined && ckeys.length)\r\n\t{\r\n\t\tnt += \"<br/>Cached Files (\"+ckeys.length+\"):<br/>\";\r\n\t\tfor(let r of ckeys)\r\n\t\t{\r\n\t\t\tconst idx = r.url.indexOf(\"/_/\")\r\n\t\t\tif (idx < 0)\r\n\t\t\t\tcontinue;   //wtf? that entry should not have been in this cache object.\r\n\t\t\tconst fn = r.url.substr(idx+3);\r\n\t\t\tlet rem = \" <a href=\\\"javascript:remcfile('\"+fn+\"');\\\">[forget]</a>\";\r\n\t\t\tnt += \"    \" + fn + rem + \"<br/>\";\r\n\t\t}\r\n\t}\r\n\r\n\t//local storage is used for slightly more persistent things, like user configs and saved games. we have quite limited storage, and this is basically text only.\r\n\ttry\r\n\t{\r\n\t\tconst ls = window.localStorage;\r\n\t\tif (ls && ls.length)\r\n\t\t{\r\n\t\t\tnt += \"<br/>Local Files (\"+ls.length+\"):<br/>\";\r\n\t\t\tfor (let i = 0; i < ls.length; i++)\r\n\t\t\t{\r\n\t\t\t\tconst fn = ls.key(i);\r\n\t\t\t\tconst rem = \" <a href=\\\"javascript:remlfile('\"+fn+\"');\\\">[forget]</a>\" + (window.showSaveFilePicker!==undefined?\" <a href=\\\"javascript:savelfile('\"+fn+\"');\\\">[export]</a>\":\"\");\r\n\t\t\t\tnt += \"    \" + fn + rem + \"<br/>\";\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\tcatch(e){}\r\n\r\n\tnt += \"</pre>\";\r\n\r\n\tnt += \"<p/>Cookie Disclaimer:<small> This page does not use cookies, however it does use local storage to save configs+games (consistent with natively installed games).<br/>frag-net (our matchmaking service) does not utilise any tracking beyond the session in question, but it does allow connecting to third-party servers which may incorporate ranking systems or accounts or other tracking according to that server's privacy/tracking policies.</small>\"\r\n\tdocument.getElementById('dropzone').innerHTML = nt;\r\n}\r\nfunction scanfiles(item,path)\r\n{\t//for directory drops\r\n\tif (item.isFile)\r\n\t{\r\n\t\tif (path==\"\")\r\n\t\t{\r\n\t\t\tpath = prompt(\"Please enter gamedir\", \"id1\");\r\n\t\t\tif (path != \"\")\r\n\t\t\t\tpath = path+\"/\";\r\n\t\t}\r\n\t\titem.file(function(f)\r\n\t\t{\r\n\t\t\tlet n = fixupfilepath(f.name, path);\r\n\t\t\tModule.files[n]=f.arrayBuffer();\t//actually a promise...\r\n\t\t\tModule.files[n].then(buf=>{Module.files[n]=buf;showfiles();});\t//try and resolve it now.\r\n    \t});\r\n\t}\r\n\telse if (item.isDirectory)\r\n\t{\r\n\t\t// Get folder contents\r\n\t\tvar dirReader = item.createReader();\r\n\t\tdirReader.readEntries(function(entries)\r\n\t\t{\r\n\t\t\tfor (var i=0; i<entries.length; i++)\r\n\t\t\t\tscanfiles(entries[i], path + item.name + \"/\");\r\n\t\t});\r\n\t}\r\n}\r\nfunction gotdrop(ev)\r\n{\t//user drag+dropped something. \r\n\tev.preventDefault();\r\n\tfor (var i = 0; i < ev.dataTransfer.items.length; i++)\r\n\t\tif (ev.dataTransfer.items[i].webkitGetAsEntry)\r\n\t\t{\r\n\t\t\tvar d = ev.dataTransfer.items[i].webkitGetAsEntry();\r\n\t\t\tif (d)\r\n\t\t\t\tscanfiles(d, \"\");\r\n\t\t}\r\n\t\telse if (ev.dataTransfer.items[i].kind === 'file')\r\n\t\t{\r\n\t\t\tvar f = ev.dataTransfer.items[i].getAsFile();\r\n\t\t\tvar n = fixupfilepath(f.name, \"\");\r\n\t\t\tModule.files[n]=f.arrayBuffer();\t//actually a promise...\r\n\t\t\tModule.files[n].then(buf=>{Module.files[n]=buf;showfiles();});\t//try and resolve it now.\r\n\t\t}\r\n\tshowfiles();\r\n}\r\nif (window.showOpenFilePicker)\r\n\taddfile.hidden = false;\r\nif (window.location.hash != \"\" || Module[\"autostart\"])\r\n\tbegin();\t\t//if the url has a #foo.fmf then just begin instantly, \r\nelse\r\n{\r\n\ttry {\r\n\t\tcaches.open('user').then((c)=>{Module['cache']=c;return c.keys();}).then((keys)=>{Module['cachekeys'] = keys; showfiles();});\r\n\t} catch(e){\r\n\t} finally {\r\n\t\tshowfiles();\t//otherwise show our lame file dropper and wait for the user to click 'go'.\r\n\t}\r\n}\r\n    </script>      \r\n  </body>\r\n</html>\r\n"
  },
  {
    "path": "engine/web/gl_vidweb.c",
    "content": "#include \"quakedef.h\"\n#include \"glquake.h\"\n#include \"vr.h\"\n#include \"shader.h\"\nvfsfile_t *FSWEB_OpenTempHandle(int f);\n\nextern cvar_t gl_lateswap;\nextern qboolean gammaworks;\n\nextern qboolean vid_isfullscreen;\n\nqboolean mouseactive;\nextern qboolean mouseusedforgui;\n\nstatic struct\n{\n\tint id;\n\tunsigned axistobuttonp;\t//bitmask of whether we're currently reporting each axis as pressed. without saving values.\n\tunsigned axistobuttonn;\n\tint repeatkey;\n    float repeattime;\n} gamepaddevices[] = {{DEVID_UNSET},{DEVID_UNSET},{DEVID_UNSET},{DEVID_UNSET},{DEVID_UNSET},{DEVID_UNSET},{DEVID_UNSET},{DEVID_UNSET}};\nstatic int keyboardid[] = {0};\nstatic int mouseid[] = {0,1,2,3,4,5,6,7};\n\nstatic cvar_t *xr_enable;\t//refrains from starting up when 0 and closes it too, and forces it off too\nstatic cvar_t *xr_metresize;\nstatic cvar_t *xr_skipregularview;\n\nstatic void WebXR_Toggle_f(void)\n{\n\temscriptenfte_xr_setup(-3);\t//toggle it.\n}\nstatic void WebXR_Start_f(void)\n{\n\temscriptenfte_xr_setup(-2);\t//start it (any mode)\n}\nstatic void WebXR_End_f(void)\n{\n\temscriptenfte_xr_setup(-1);\t//end it (if running)\n}\nstatic void WebXR_Start_Inline_f(void)\n{\n\temscriptenfte_xr_setup(-1);\t//end it (if running)\n\temscriptenfte_xr_setup(0);\t//start it (inline mode)\n}\nstatic void WebXR_Start_VR_f(void)\n{\n\temscriptenfte_xr_setup(-1);\t//end it (if running)\n\temscriptenfte_xr_setup(1);\t//start it (inline mode)\n}\nstatic void WebXR_Start_AR_f(void)\n{\n\temscriptenfte_xr_setup(-1);\t//end it (if running)\n\temscriptenfte_xr_setup(2);\t//start it (inline mode)\n}\nstatic void WebXR_Info_f(void)\n{\n\tint modes = emscriptenfte_xr_issupported();\n\tif (modes == 0)\n\t{\n\t\tCon_Printf(S_COLOR_RED\"WebXR is unavailable\\n\");\n\t\treturn;\n\t}\n\tif (modes < 0)\n\t\tCon_Printf(\"WebXR availability is unknown\\n\");\n\telse\n\t{\n\t\tCon_Printf(\"WebXR-inline is %savailable\\n\",\t\t\t(modes & (1<<0))?S_COLOR_GREEN:S_COLOR_RED\"un\");\n\t\tCon_Printf(\"WebXR-immersive-vr is %savailable\\n\",\t(modes & (1<<1))?S_COLOR_GREEN:S_COLOR_RED\"un\");\n\t\tCon_Printf(\"WebXR-immersive-ar is %savailable\\n\",\t(modes & (1<<2))?S_COLOR_GREEN:S_COLOR_RED\"un\");\n\n\t\tif (emscriptenfte_xr_isactive())\n\t\t\tCon_Printf(\"WebXR is active\\n\");\n\t\telse\n\t\t\tCon_Printf(\"WebXR is inactive\\n\");\n\t}\n}\n\nstatic qboolean\tWebXR_Prepare\t(vrsetup_t *setupinfo)\n{\t//called before graphics context init. basically just checks if we can do vr.\n\txr_enable\t\t\t= Cvar_Get2(\"xr_enable\",\t\t\t\"1\",\t\t\tCVAR_SEMICHEAT,\t\"Controls whether to use webxr rendering or not.\",\t\t\t\"WebXR configuration\");\n\txr_metresize\t\t= Cvar_Get2(\"xr_metresize\",\t\t\t\"26.24671916\",\tCVAR_ARCHIVE,\t\"Size of a metre in game units\",\t\t\t\t\t\t\t\"WebXR configuration\");\n\txr_skipregularview\t= Cvar_Get2(\"xr_skipregularview\",\t\"1\",\t\t\tCVAR_ARCHIVE,\t\"Skip rendering the regular view when OpenXR is active\",\t\"WebXR configuration\");\n\n\treturn xr_enable->ival;//emscriptenfte_xr_issupported() != 0;\n}\nstatic qboolean\tWebXR_Init\t\t(vrsetup_t *setupinfo, rendererstate_t *info)\n{\t//called after graphics context init.\n\tCmd_AddCommand(\"xr_toggle\", WebXR_Toggle_f);\n\tCmd_AddCommand(\"xr_start\",\tWebXR_Start_f);\n\tCmd_AddCommand(\"xr_end\",\tWebXR_End_f);\n\tCmd_AddCommand(\"xr_start_inline\", WebXR_Start_Inline_f);\n\tCmd_AddCommand(\"xr_start_vr\", WebXR_Start_VR_f);\n\tCmd_AddCommand(\"xr_start_ar\", WebXR_Start_AR_f);\n\tCmd_AddCommand(\"xr_info\", WebXR_Info_f);\n\treturn true;\t//we don't have much control here, so we have no failure paths here. probably the session isn't even created.\n}\nstatic unsigned int\tWebXR_SyncFrame\t(double *frametime)\n{\t//called in the client's main loop, to block/tweak frame times. True means the game should render as fast as possible.\n\treturn emscriptenfte_xr_isactive();\t//we're using openxr's session's sync stuff when this is active.\n}\nstatic void WebXR_MatrixToQuake(const float in[16], float out[12])\n{\n\tfloat tempmat[16], tempmat2[16];\n\tconst float fixupmat[16]\t= { 0, 0, -1, 0,   -1,  0,  0,  0,    0,  1,  0,  0,    0,  0,  0,  1};\n\tconst float reorient[16]\t= { 0, -1, 0, 0,    0,  0,  1,  0,   -1,  0,  0,  0,    0,  0,  0,  1};\n\n\tMatrix4_Multiply(in, fixupmat, tempmat2);//we want z-up...\n\tMatrix4_Multiply(reorient, tempmat2, tempmat);//rotate it.\n\n\t//transpose it cos smaller? urgh.\n\tout[ 0] = tempmat[0];\n\tout[ 1] = tempmat[4];\n\tout[ 2] = tempmat[8];\n\tout[ 3] = tempmat[12];\n\tout[ 4] = tempmat[1];\n\tout[ 5] = tempmat[5];\n\tout[ 6] = tempmat[9];\n\tout[ 7] = tempmat[13];\n\tout[ 8] = tempmat[2];\n\tout[ 9] = tempmat[6];\n\tout[10] = tempmat[10];\n\tout[11] = tempmat[14];\n\n\t//fix up offset scaling parts\n\tout[3] *= xr_metresize->value;\n\tout[7] *= xr_metresize->value;\n\tout[11]*= xr_metresize->value;\n}\nstatic qboolean\tWebXR_Render\t(void(*rendereye)(texid_t tex, const pxrect_t *viewport, const vec4_t fovoverride, const float projmatrix[16], const float eyematrix[12]))\n{\t//calls rendereye for each view we're meant to be drawing.\n\t//webxr uses separate viewpoints on the same fbo\n\tstruct webxrinfo_s eye[16];\n\tfbostate_t fbo;\n\tint oldfbo = 0;\n\tfloat eyematrix[12];\n\tpxrect_t vp;\n\n\tint e, eyes = emscriptenfte_xr_geteyeinfo(countof(eye), eye);\n\tif (!eyes)\n\t\treturn false;\t//erk? lets just do normal drawing.\n\n\tfor (e = 0; e < eyes; e++)\n\t{\n\t\tfbo.fbo = eye[e].fbo;\n\t\tif (!e)\n\t\t\toldfbo = GLBE_FBO_Push(&fbo);\n\t\telse\n\t\t\tGLBE_FBO_Push(&fbo);\n\n\t\tif (!eye[e].viewport[2] || !eye[e].viewport[3])\n\t\t\tcontinue;\t//no pixels getting drawn... don't waste time (emulators that feel an urge to give a dodgy eye to report two eyes instead of one)\n\n\t\tvp.x\t\t= eye[e].viewport[0];\n\t\tvp.width\t= eye[e].viewport[2];\n\t\tvp.height\t= eye[e].viewport[3];\n\t\tvp.maxheight = vp.y+vp.height;\t//negatives suck.\n\t\tvp.y = vp.maxheight-eye[e].viewport[1]-vp.height;\t//opengl sucks\n\n\t\tWebXR_MatrixToQuake(eye[e].transform, eyematrix);\n\n\t\t//really just a pointer to R_RenderEyeScene...\n\t\trendereye(NULL, &vp, NULL, eye[e].projmatrix, eyematrix);\n\t}\n\tGLBE_FBO_Pop(oldfbo);\n\n\tif (!xr_enable->ival)\n\t\temscriptenfte_xr_shutdown();\n\n\tif (eye[e].fbo==0)\n\t\treturn true;\t//always skip the non-vr screen when we're fighting over the same FB.\n\treturn xr_skipregularview->ival;\t//skip non-vr rendering.\n}\nstatic void\t\tWebXR_Shutdown\t(void)\n{\n\tCmd_RemoveCommand(\"xr_toggle\");\n\tCmd_RemoveCommand(\"xr_start\");\n\tCmd_RemoveCommand(\"xr_end\");\n\tCmd_RemoveCommand(\"xr_start_inline\");\n\tCmd_RemoveCommand(\"xr_start_vr\");\n\tCmd_RemoveCommand(\"xr_start_ar\");\n\tCmd_RemoveCommand(\"xr_info\");\n\temscriptenfte_xr_shutdown();\n}\nstatic struct plugvrfuncs_s webxrfuncs = {\n\t\"WebXR\",\n\tWebXR_Prepare,\n\tWebXR_Init,\n\tWebXR_SyncFrame,\n\tWebXR_Render,\n\tWebXR_Shutdown,\n};\n\nstatic void *GLVID_getwebglfunction(char *functionname)\n{\n\treturn NULL;\n}\n\n//the enumid is the value for the open function rather than the working id.\nstatic int J_AllocateDevID(void)\n{\n    extern cvar_t in_skipplayerone;\n    unsigned int id = (in_skipplayerone.ival?1:0), j;\n    for (j = 0; j < countof(gamepaddevices);)\n    {\n        if (gamepaddevices[j++].id == id)\n        {\n            j = 0;\n            id++;\n        }\n    }\n\n\treturn id;\n}\n\nstatic void IN_GamePadButtonEvent(int joydevid, int button, int ispressed, int isstandardmapping)\n{\n\t//note that the gamepad API handles 'buttons' as float values, so triggers are here instead of as 'axis' values (unlike other APIs). on the plus side, we're no longer responsible for figuring out the required threshold value to denote a 'press', but we're not tracking half-presses and that's our fault and we don't care.\n\tstatic const int standardmapping[] =\n\t{\t//the order of these keys is different from that of xinput\n\t\t//however, the quake button codes should be the same. I really ought to define some K_ aliases for them.\n\t\tK_GP_A,\n\t\tK_GP_B,\n\t\tK_GP_X,\n\t\tK_GP_Y,\n\t\tK_GP_LEFT_SHOULDER,\n\t\tK_GP_RIGHT_SHOULDER,\n\t\tK_GP_LEFT_TRIGGER,\n\t\tK_GP_RIGHT_TRIGGER,\n\t\tK_GP_BACK,\n\t\tK_GP_START,\n\t\tK_GP_LEFT_STICK,\n\t\tK_GP_RIGHT_STICK,\n\t\tK_GP_DPAD_UP,\n\t\tK_GP_DPAD_DOWN,\n\t\tK_GP_DPAD_LEFT,\n\t\tK_GP_DPAD_RIGHT,\n\t\tK_GP_GUIDE,\n\t\t//K_GP_UNKNOWN\n\t};\n\n\tif (joydevid < 0)\n\t{\n\t\tstatic const int standardxrmapping[] =\n\t\t{\n\t\t\t//Right\t\t\t\t\tLeft\n\t\t\tK_GP_RIGHT_TRIGGER,\t\tK_GP_LEFT_TRIGGER,\t//Primary trigger/button\n\t\t\tK_GP_RIGHT_SHOULDER,\tK_GP_LEFT_SHOULDER,\t//Primary squeeze\n\t\t\tK_GP_TOUCHPAD,\t\t\tK_GP_MISC1,\t\t\t//Primary touchpad\n\t\t\tK_GP_RIGHT_STICK,\t\tK_GP_LEFT_STICK,\t//Primary thumbstick\n\n\t\t\t//'Additional inputs may be exposed after', which should be in some pseudo-prioritised order with the awkward ones last.\n\t\t\tK_GP_A,\t\t\t\t\tK_GP_DPAD_DOWN,\n\t\t\tK_GP_B,\t\t\t\t\tK_GP_DPAD_RIGHT,\n\t\t\tK_GP_X,\t\t\t\t\tK_GP_DPAD_LEFT,\n\t\t\tK_GP_Y,\t\t\t\t\tK_GP_DPAD_UP,\n\t\t};\n\t\tbutton = button*2 + (joydevid != -1);\t//munge them into a single array cos I cba with all the extra conditionals\n\t\tif (button < countof(standardxrmapping))\n\t\t\tbutton = standardxrmapping[button];\n\t\telse\n\t\t\treturn; //err...\n\n\t\tjoydevid = countof(gamepaddevices)-1;\n\t}\n\telse if (isstandardmapping && button < countof(standardmapping))\n\t\tbutton = standardmapping[button];\n\telse if (button < 32+4)\n\t\tbutton = K_JOY1+button;\n\telse\n\t\treturn;\t//err...\n\n\tif (joydevid < countof(gamepaddevices))\n\t{\n\t\tif (DEVID_UNSET == gamepaddevices[joydevid].id)\n\t\t{\n\t\t\tif (!ispressed)\n\t\t\t\treturn;\t//don't send axis events until its enabled.\n\t\t\tgamepaddevices[joydevid].id = J_AllocateDevID();\n\t\t}\n\t\tif (ispressed)\n\t\t{\n\t\t\tgamepaddevices[joydevid].repeatkey = button;\n\t\t\tgamepaddevices[joydevid].repeattime = 1.0;\n\t\t}\n\t\telse if (gamepaddevices[joydevid].repeatkey == button)\n\t\t\tgamepaddevices[joydevid].repeatkey = 0;\n\t\tjoydevid = gamepaddevices[joydevid].id;\n\t}\n\n\tIN_KeyEvent(joydevid, ispressed, button, 0);\n}\nstatic void IN_GamePadButtonRepeats(void)\n{\n\tint j;\n\tfor (j = 0; j < countof(gamepaddevices); j++)\n\t{\n\t\tif (gamepaddevices[j].id == DEVID_UNSET)\n\t\t\tcontinue;\n\t\tif (!gamepaddevices[j].repeatkey)\n\t\t\tcontinue;\n\t\tgamepaddevices[j].repeattime -= host_frametime;\n\t\tif (gamepaddevices[j].repeattime < 0)\n\t\t{\t//it is time!\n\t\t\tgamepaddevices[j].repeattime = 0.25; //faster re-repeat than the initial delay.\n\t\t\tIN_KeyEvent(gamepaddevices[j].id, true, gamepaddevices[j].repeatkey, 0);\t//an extra down. no ups.\n\t\t}\n\t}\n}\n\nstatic void IN_GamePadAxisEvent(int joydevid, int axis, float value, int isstandardmapping)\n{\n\tstatic const struct\n\t{\n\t\tint axis;\n\t\tint poskey;\t//mostly for navigating menus, but oh well.\n\t\tint negkey;\n\t} standardmapping[] =\n\t{\n\t\t{GPAXIS_LT_RIGHT,\tK_GP_LEFT_THUMB_RIGHT,\tK_GP_LEFT_THUMB_LEFT},\n\t\t{GPAXIS_LT_DOWN,\tK_GP_LEFT_THUMB_DOWN,\tK_GP_LEFT_THUMB_UP},\n\t\t{GPAXIS_RT_RIGHT,\tK_GP_RIGHT_THUMB_RIGHT,\tK_GP_RIGHT_THUMB_LEFT},\n\t\t{GPAXIS_RT_DOWN,\tK_GP_RIGHT_THUMB_DOWN,\tK_GP_RIGHT_THUMB_UP},\n\n\t\t//this seems fucked. only 4 axis are defined as part of the standard mapping. triggers are implemented as buttons with a .value (instead of .pressed) but they don't seem to work at all.\n\t\t//emulating here should be giving dupes, but I don't know how else to get this shite to work properly.\n\t\t{GPAXIS_LT_AUX,\t\tK_GP_LEFT_TRIGGER,0},\n\t\t{GPAXIS_RT_AUX,\t\tK_GP_RIGHT_TRIGGER,0},\n\t};\n\tint qdevid;\n\tint pos=0, neg=0;\n\tint qaxis;\n\tif (joydevid < 0)\n\t{\n\t\tstatic const int standardxrmapping[] =\n\t\t{\n\t\t\t//Right\t\t\t\t\tLeft\n\t\t\t-1,\t\t\t\t\t\t-1,\t\t\t\t\t//Primary touchpad X\n\t\t\t-1,\t\t\t\t\t\t-1,\t\t\t\t\t//Primary touchpad Y\n\t\t\tGPAXIS_RT_RIGHT,\t\tGPAXIS_LT_RIGHT,\t//Primary thumbstick X\n\t\t\tGPAXIS_RT_DOWN,\t\t\tGPAXIS_LT_DOWN,\t\t//Primary thumbstick Y\n\n\t\t\t//'Additional inputs may be exposed after', which should be in some pseudo-prioritised order with the awkward ones last.\n\t\t};\n\t\tqaxis = axis*2 + (joydevid != -1);\t//munge them into a single array cos I cba with all the extra conditionals\n\t\tif (qaxis < countof(standardxrmapping))\n\t\t\tqaxis = standardxrmapping[qaxis];\n\t\telse\n\t\t\treturn; //err...\n\n\t\tjoydevid = countof(gamepaddevices)-1;\n\t}\n\telse if (isstandardmapping)\n\t{\n\t\tif (axis >= 0 && axis < countof(standardmapping))\n\t\t{\n\t\t\tpos = standardmapping[axis].poskey;\n\t\t\tneg = standardmapping[axis].negkey;\n\t\t\tqaxis = standardmapping[axis].axis;\n\t\t}\n\t\telse\n\t\t\tqaxis = axis;\n\t}\n\telse\n\t\treturn;\t//random mappings? erk?\n\n\tif (joydevid < countof(gamepaddevices))\n\t{\n\t\tqdevid = gamepaddevices[joydevid].id;\n\t\tif (qdevid == DEVID_UNSET)\n\t\t{\n\t\t\tif (value < -0.9 || value > 0.9)\n\t\t\t\tgamepaddevices[joydevid].id = J_AllocateDevID();\n\t\t\treturn;\t//don't send axis events until its enabled.\n\t\t}\n\n\t\tif (value > 0.5 && pos)\n\t\t{\n\t\t\tif (!(gamepaddevices[joydevid].axistobuttonp & (1u<<axis)))\n\t\t\t{\n\t\t\t\tIN_KeyEvent(qdevid, true, pos, 0);\n\t\t\t\tgamepaddevices[joydevid].repeatkey = pos;\n\t\t\t\tgamepaddevices[joydevid].repeattime = 1.0;\n\t\t\t}\n\t\t\tgamepaddevices[joydevid].axistobuttonp |= 1u<<axis;\n\t\t}\n\t\telse if (gamepaddevices[joydevid].axistobuttonp & (1u<<axis))\n\t\t{\n\t\t\tIN_KeyEvent(qdevid, false, pos, 0);\n\t\t\tgamepaddevices[joydevid].axistobuttonp &= ~(1u<<axis);\n\t\t\tif (gamepaddevices[joydevid].repeatkey == pos)\n\t\t\t\tgamepaddevices[joydevid].repeatkey = 0;\n\t\t}\n\n\t\tif (value < -0.5 && neg)\n\t\t{\n\t\t\tif (!(gamepaddevices[joydevid].axistobuttonn & (1u<<axis)))\n\t\t\t{\n\t\t\t\tIN_KeyEvent(qdevid, true, neg, 0);\n\t\t\t\tgamepaddevices[joydevid].repeatkey = neg;\n\t\t\t\tgamepaddevices[joydevid].repeattime = 1.0;\n\t\t\t}\n\t\t\tgamepaddevices[joydevid].axistobuttonn |= 1u<<axis;\n\t\t}\n\t\telse if (gamepaddevices[joydevid].axistobuttonn & (1u<<axis))\n\t\t{\n\t\t\tIN_KeyEvent(qdevid, false, neg, 0);\n\t\t\tgamepaddevices[joydevid].axistobuttonn &= ~(1u<<axis);\n\t\t\tif (gamepaddevices[joydevid].repeatkey == neg)\n\t\t\t\tgamepaddevices[joydevid].repeatkey = 0;\n\t\t}\n\t}\n\telse\n\t\tqdevid = joydevid;\n\n\tIN_JoystickAxisEvent(qdevid, qaxis, value);\n}\n\nstatic void IN_GamePadOrientationEvent(int joydevid, float px,float py,float pz, float qx,float qy,float qz,float qw)\n{\t//(some) vr controllers only\n\tvec3_t org, ang;\n\tconst char *dev;\n\n\tconst float sqw = qw * qw;\n\tconst float sqx = qx * qx;\n\tconst float sqy = qy * qy;\n\tconst float sqz = qz * qz;\n\n\tang[PITCH] = -asin(-2 * (qy * qz - qw * qx)) * (180/M_PI);\n\tang[YAW] = atan2(2 * (qx * qz + qw * qy), sqw - sqx - sqy + sqz) * (180/M_PI);\n\tang[ROLL] = -atan2(2 * (qx * qy + qw * qz), sqw - sqx + sqy - sqz) * (180/M_PI);\n\n\torg[0] = -pz * xr_metresize->value;\n\torg[1] = -px * xr_metresize->value;\n\torg[2] = py * xr_metresize->value;\n\n\tif (joydevid == -1)\n\t\tdev = \"right\";\n\telse if (joydevid == -2)\n\t\tdev = \"left\";\n\telse if (joydevid == -3)\n\t\tdev = \"head\";\n\telse if (joydevid == -4)\n\t\tdev = \"gaze\";\n\telse\n\t\treturn;\n\n\tIN_SetHandPosition(dev, org, ang, NULL, NULL);\n}\n\nstatic void VID_Resized(int width, int height, float scale)\n{\n\textern cvar_t vid_conautoscale, vid_conwidth;\n\textern cvar_t vid_dpi_x, vid_dpi_y;\n\tvid.pixelwidth = width;\n\tvid.pixelheight = height;\n//Con_Printf(\"Resized: %i %i\\n\", vid.pixelwidth, vid.pixelheight);\n\n\t//if you're zooming in, it should stay looking like its zoomed.\n\tvid.dpi_x = 96*scale;\n\tvid.dpi_y = 96*scale;\n\tCvar_ForceSetValue(&vid_dpi_x, vid.dpi_x);\n\tCvar_ForceSetValue(&vid_dpi_y, vid.dpi_y);\n\n\tCvar_ForceCallback(&vid_conautoscale);\n\tCvar_ForceCallback(&vid_conwidth);\n}\nstatic unsigned int domkeytoquake(unsigned int code)\n{\n\tstatic const unsigned short tab[256] =\n\t{\n\t\t/*  0*/ 0,0,0,0,0,0,0,0,                K_BACKSPACE,K_TAB,0,0,0,K_ENTER,0,0,\n\t\t/* 16*/ K_SHIFT,K_CTRL,K_ALT,K_PAUSE,K_CAPSLOCK,0,0,0,0,0,0,K_ESCAPE,0,0,0,0,\n\t\t/* 32*/ ' ',K_PGUP,K_PGDN,K_END,K_HOME,K_LEFTARROW,K_UPARROW,K_RIGHTARROW,              K_DOWNARROW,0,0,0,K_PRINTSCREEN,K_INS,K_DEL,0,\n\t\t/* 48*/ '0','1','2','3','4','5','6','7',                '8','9',0,';',0,'=',0,0,\n\n\t\t/* 64*/ 0,'a','b','c','d','e','f','g',          'h','i','j','k','l','m','n','o',\n\t\t/* 80*/ 'p','q','r','s','t','u','v','w',                'x','y','z',K_LWIN,K_RWIN,K_APP,0,0,\n\t\t/* 96*/ K_KP_INS,K_KP_END,K_KP_DOWNARROW,K_KP_PGDN,K_KP_LEFTARROW,K_KP_5,K_KP_RIGHTARROW,K_KP_HOME,             K_KP_UPARROW,K_KP_PGDN,K_KP_STAR,K_KP_PLUS,0,K_KP_MINUS,K_KP_DEL,K_KP_SLASH,\n\t\t/*112*/ K_F1,K_F2,K_F3,K_F4,K_F5,K_F6,K_F7,K_F8,K_F9,K_F10,K_F11,K_F12,0,0,0,0,\n\t\t/*128*/ 0,0,0,0,0,0,0,0,                0,0,0,0,0,0,0,0,\n\t\t/*144*/ K_KP_NUMLOCK,K_SCRLCK,0,0,0,0,0,0,              0,0,0,0,0,0,0,0,\n\t\t/*160*/ 0,0,0,'#',0,0,0,0,                0,0,0,0,0,'-',0,0,\n\t\t/*176*/ 0,0,0,0,0,0,0,0,                0,0,';','=',',','-','.','/',\n\t\t/*192*/ '`',0,0,0,0,0,0,0,             0,0,0,0,0,0,0,0,\n\t\t/*208*/ 0,0,0,0,0,0,0,0,                0,0,0,'[','\\\\',']','\\'','`',\n\t\t/*224*/ 0,0,0,0,0,0,0,0,                0,0,0,0,0,0,0,0,\n\t\t/*240*/ 0,0,0,0,0,0,0,0,                0,0,0,0,0,0,0,0,\n\t};\n\tif (!code)\n\t\treturn 0;\n\tif (code >= sizeof(tab)/sizeof(tab[0]))\n\t{\n\t\tCon_DPrintf(\"You just pressed key %u, but I don't know what its meant to be\\n\", code);\n\t\treturn 0;\n\t}\n\tif (!tab[code])\n\t\tCon_DPrintf(\"You just pressed key %u, but I don't know what its meant to be\\n\", code);\n\n//\tCon_DPrintf(\"You just pressed dom key %u, which is quake key %u\\n\", code, tab[code]);\n\treturn tab[code];\n}\nstatic int DOM_KeyEvent(unsigned int devid, int down, int scan, int uni)\n{\n\textern int\t\tshift_down;\n//\tCon_Printf(\"Key %s %i %i:%c\\n\", down?\"down\":\"up\", scan, uni, uni?(char)uni:' ');\n\tif (shift_down)\n\t{\n\t\tscan = domkeytoquake(scan);\n\t}\n\telse\n\t{\n\t\tscan = domkeytoquake(scan);\n\t}\n\tIN_KeyEvent(keyboardid[devid], down, scan, uni);\n\t//Chars which don't map to some printable ascii value get preventDefaulted.\n\t//This is to stop fucking annoying fucking things like backspace randomly destroying the page and thus game.\n\t//And it has to be conditional, or we don't get any unicode chars at all.\n\t//The behaviour browsers seem to give is retardedly unhelpful, and just results in hacks to detect keys that appear to map to ascii...\n\t//Preventing the browser from leaving the page etc should NOT mean I can no longer get ascii/unicode values, only that the browser stops trying to do something random due to the event.\n\t//If you are the person that decreed that this is the holy way, then please castrate yourself now.\n//\tif (scan == K_BACKSPACE || scan == K_LCTRL || scan == K_LALT || scan == K_LSHIFT || scan == K_RCTRL || scan == K_RALT || scan == K_RSHIFT)\n\t\treturn true;\n//\treturn false;\n}\nstatic int RemapTouchId(int id, qboolean final)\n{\n\tstatic int touchids[countof(mouseid)];\n\tint i;\n\tif (!id)\n\t\treturn id;\n\tfor (i = 1; i < countof(touchids); i++)\n\t\tif (touchids[i] == id)\n\t\t{\n\t\t\tif (final)\t\n\t\t\t\ttouchids[i] = 0;\n\t\t\treturn mouseid[i];\n\t\t}\n\tfor (i = 1; i < countof(touchids); i++)\n\t\tif (touchids[i] == 0)\n\t\t{\n\t\t\tif (!final)\n\t\t\t\ttouchids[i] = id;\n\t\t\tif (mouseid[i] == DEVID_UNSET)\n\t\t\t\tmouseid[i] = i;\n\t\t\treturn mouseid[i];\n\t\t}\n\treturn id;\n}\nstatic void DOM_ButtonEvent(unsigned int devid, int down, int button)\n{\n\tdevid = RemapTouchId(devid, !down);\n\tif (down == 2)\n\t{\n\t\t//fixme: the event is a float. we ignore that.\n\t\twhile(button < 0)\n\t\t{\n\t\t\tIN_KeyEvent(devid, true, K_MWHEELUP, 0);\n\t\t\tIN_KeyEvent(devid, false, K_MWHEELUP, 0);\n\t\t\tbutton += 1;\n\t\t}\n\t\twhile(button > 0)\n\t\t{\n\t\t\tIN_KeyEvent(devid, true, K_MWHEELDOWN, 0);\n\t\t\tIN_KeyEvent(devid, false, K_MWHEELUP, 0);\n\t\t\tbutton -= 1;\n\t\t}\n\t}\n\telse\n\t{\n\t\t//swap buttons 2 and 3, so rmb is still +forward by default and not +mlook.\n\t\tif (button == 2)\n\t\t\tbutton = 1;\n\t\telse if (button == 1)\n\t\t\tbutton = 2;\n\n\t\tif (button < 0)\n\t\t\tbutton = K_TOUCH;\n\t\telse\n\t\t\tbutton += K_MOUSE1;\n\t\tIN_KeyEvent(devid, down, button, 0);\n\t}\n}\nstatic void DOM_MouseMove(unsigned int devid, int abs, float x, float y, float z, float size)\n{\n\tdevid = RemapTouchId(devid, false);\n\tIN_MouseMove(devid, abs, x, y, z, size);\n}\n\nstatic void DOM_LoadFile(char *loc, char *mime, int handle)\n{\n\tvfsfile_t *file = NULL;\n\tif (handle != -1)\n\t\tfile = FSWEB_OpenTempHandle(handle);\n\telse\n\t{\n\t\tchar str[1024];\n\t\tif (!strcmp(mime, \"joinurl\") || !strcmp(mime, \"observeurl\")  || !strcmp(mime, \"connecturl\"))\n\t\t{\n\t\t\textern cvar_t spectator;\n\t\t\tif (!strcmp(mime, \"joinurl\"))\n\t\t\t\tCvar_Set(&spectator, \"0\");\n\t\t\tif (!strcmp(mime, \"observeurl\"))\n\t\t\t\tCvar_Set(&spectator, \"1\");\n\t\t\tCbuf_AddText(va(\"connect %s\\n\", COM_QuotedString(loc, str, sizeof(str), false)), RESTRICT_INSECURE);\n\t\t\treturn;\n\t\t}\n\t\tif (!strcmp(mime, \"demourl\"))\n\t\t{\n\t\t\tCbuf_AddText(va(\"qtvplay %s\\n\", COM_QuotedString(loc, str, sizeof(str), false)), RESTRICT_INSECURE);\n\t\t\treturn;\n\t\t}\n\t}\n\t//try and open it. generally downloading it from the server.\n\tif (!Host_RunFile(loc, strlen(loc), file))\n\t{\n\t\tif (file)\n\t\t\tVFS_CLOSE(file);\n\t}\n}\nstatic void DOM_CbufAddText(const char *text)\n{\n\tCbuf_AddText(text, RESTRICT_LOCAL);\n}\nstatic int VID_ShouldSwitchToFullscreen(void)\n{\t//if false, mouse grabs won't work and we'll be forced to touchscreen mode.\n\t//we can only go fullscreen when the user clicks something.\n\t//this means that the user will get pissed off at the fullscreen state changing when they first click on the menus after it loading up.\n\t//this is confounded by escape bringing up the menu. <ESC>GRR IT CHANGED MODE!<options>WTF IT CHANGED AGAIN FUCKING PIECE OF SHIT!.\n\t//annoying, but that's web browsers for you. the best thing we can do is to not regrab until they next click while actually back in the game.\n\textern cvar_t vid_fullscreen;\n\treturn !!vid_fullscreen.value && !Key_MouseShouldBeFree();\n}\nqboolean GLVID_Init (rendererstate_t *info, unsigned char *palette)\n{\n\tvid_isfullscreen = true;\n\n\tif (!emscriptenfte_setupcanvas(\n\t\tinfo->width,\n\t\tinfo->height,\n\t\tVID_Resized,\n\t\tDOM_MouseMove,\n\t\tDOM_ButtonEvent,\n\t\tDOM_KeyEvent,\n\t\tDOM_LoadFile,\n\t\tDOM_CbufAddText,\n\t\tIN_GamePadButtonEvent,\n\t\tIN_GamePadAxisEvent,\n\t\tIN_GamePadOrientationEvent,\n\t\tVID_ShouldSwitchToFullscreen\n\t\t))\n\t{\n\t\tCon_Printf(\"Couldn't set up canvas\\n\");\n\t\treturn false;\n\t}\n\n\tvid.activeapp = true;\n\n\tif (info->vr && !info->vr->Prepare(NULL))\n\t\tinfo->vr = NULL;\t//not available.\n\n\tif (!GL_Init(info, GLVID_getwebglfunction))\n\t\treturn false;\t\t\n\tif (info->vr && !info->vr->Init(NULL, info))\n\t\treturn false;\n\tvid.vr = info->vr;\n\n\tqglViewport (0, 0, vid.pixelwidth, vid.pixelheight);\n\n\tVID_Resized(vid.pixelwidth, vid.pixelheight, 1);\n\n\tmouseactive = false;\n\n\treturn true;\n}\n\nvoid GLVID_DeInit (void)\n{\n\tvid.activeapp = false;\n\n\temscriptenfte_setupcanvas(-1, -1, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);\n\n\tGL_ForgetPointers();\n}\n\n\nvoid GLVID_SwapBuffers (void)\n{\n\t//webgl doesn't support swapbuffers.\n\t//you can't use it for loading screens.\n\t//such things must result in waiting until the following frame.\n\t//although there IS a swapped-buffers event, which we should probably use in preference to requestanimationframe or whatever the call is.\n\n/*\n\tif (!vid_isfullscreen)\n\t{\n\t\tif (!in_windowed_mouse.value)\n\t\t{\n\t\t\tif (mouseactive)\n\t\t\t{\n\t\t\t\tIN_DeactivateMouse ();\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif ((key_dest == key_game||mouseusedforgui) && vid.activeapp)\n\t\t\t\tIN_ActivateMouse ();\n\t\t\telse if (!(key_dest == key_game || mouseusedforgui) || !vid.activeapp)\n\t\t\t\tIN_DeactivateMouse ();\n\t\t}\n\t}\n*/\n}\n\nqboolean GLVID_ApplyGammaRamps (unsigned int gammarampsize, unsigned short *ramps)\n{\n\tgammaworks = false;\n\treturn gammaworks;\n}\n\nvoid GLVID_SetCaption(const char *text)\n{\n\temscriptenfte_settitle(text);\n}\n\nvoid Sys_SendKeyEvents(void)\n{\n\t/*most callbacks happen outside our code, we don't need to poll for events - except for joysticks*/\n\tqboolean shouldbefree = Key_MouseShouldBeFree();\n\temscriptenfte_updatepointerlock(in_windowed_mouse.ival && !shouldbefree, shouldbefree);\n\temscriptenfte_polljoyevents();\n\n\tIN_GamePadButtonRepeats();\n}\n/*various stuff for joysticks, which we don't support in this port*/\nvoid INS_Shutdown (void)\n{\n}\nvoid INS_ReInit (void)\n{\n}\nvoid INS_Move(void)\n{\n}\nvoid INS_Init (void)\n{\n\t//mneh, handy enough\n\tR_RegisterVRDriver(NULL, &webxrfuncs);\n}\nvoid INS_Accumulate(void)\n{\n}\nvoid INS_Commands (void)\n{\n}\nvoid INS_EnumerateDevices(void *ctx, void(*callback)(void *ctx, const char *type, const char *devicename, unsigned int *qdevid))\n{\n\tsize_t i;\n\tchar foobar[64];\n\tfor (i = 0; i < countof(gamepaddevices); i++)\n\t{\n\t\tQ_snprintfz(foobar, sizeof(foobar), \"gp%i\", (int)i);\n\t\tcallback(ctx, \"gamepad\", foobar, &gamepaddevices[i].id);\n\t}\n\tfor (i = 0; i < countof(mouseid); i++)\n\t{\n\t\tQ_snprintfz(foobar, sizeof(foobar), \"m%i\", (int)i);\n\t\tcallback(ctx, \"mouse\", foobar, &mouseid[i]);\n\t}\n\tfor (i = 0; i < countof(keyboardid); i++)\n\t{\n\t\tQ_snprintfz(foobar, sizeof(foobar), \"kb%i\", (int)i);\n\t\tcallback(ctx, \"keyboard\", foobar, &keyboardid[i]);\n\t}\n}\n\nenum controllertype_e INS_GetControllerType(int id)\n{\n\tsize_t i;\n\tfor (i = 0; i < countof(gamepaddevices); i++)\n\t{\n\t\tif (id == gamepaddevices[i].id)\n\t\t\treturn CONTROLLER_UNKNOWN;\t//browsers don't really like providing more info, to thwart fingerprinting. shame. you should just use generic glyphs.\n\t}\n\treturn CONTROLLER_NONE;\t//nuffin here. yay fingerprinting?\n}\nvoid INS_Rumble(int joy, quint16_t amp_low, quint16_t amp_high, quint32_t duration)\n{\n}\nvoid INS_RumbleTriggers(int joy, quint16_t left, quint16_t right, quint32_t duration)\n{\n}\nvoid INS_SetLEDColor(int id, vec3_t color)\n{\n}\nvoid INS_SetTriggerFX(int id, const void *data, size_t size)\n{\n}\n\n"
  },
  {
    "path": "engine/web/prejs.js",
    "content": "//Populate our filesystem from Module['files']\r\nFTEH = {h: [],\r\n\t\tf: {}};\r\nFTE_SW=null;\r\n\r\nif (!Module['canvas'])\r\n{\t//we need a canvas to throw our webgl crap at...\r\n\tModule['canvas'] = document.getElementById('canvas');\r\n\tif (!Module['canvas'])\r\n\t{\r\n\t\tconsole.log(\"No canvas element defined yet.\");\r\n\t\tModule.canvas = document.createElement(\"canvas\");\r\n\t\tModule.canvas.style.width=\"100%\";\r\n\t\tModule.canvas.style.height=\"100%\";\r\n\t\tdocument.body.appendChild(Module['canvas']);\r\n\t}\r\n}\r\n\r\nModule['loadcachedfiles'] = function()\r\n{\t//recover any previously saved files they might have drag+dropped.\r\n\taddRunDependency(\"loadcachedfiles\");\r\n\ttry\r\n\t{\r\n\t\tcaches.open('user').then((c)=>{Module['cache']=c;return c.keys();}).then((keys)=>{\r\n\t\t\tconst cache = Module['cache'];\r\n\t\t\tfor(var r of keys)\r\n\t\t\t{\r\n\t\t\t\tconst idx = r.url.indexOf(\"/_/\")\r\n\t\t\t\tif (idx < 0)\r\n\t\t\t\t\tcontinue;\t//wtf? that entry should not have been in this cache object.\r\n\t\t\t\tconst fn = r.url.substr(idx+3);\r\n\t\t\t\taddRunDependency(fn);\r\n\t\t\t\tconst response = cache.match(r).then((response)=>{return response.arrayBuffer();}).then((buffer)=>{\r\n\t\t\t\t\tlet b = FTEH.h[_emscriptenfte_buf_createfromarraybuf(buffer)];\r\n\t\t\t\t\tb.n = fn;\r\n\t\t\t\t\tFTEH.f[b.n] = b;\r\n\t\t\t\t}).finally(()=>{removeRunDependency(fn);});\r\n\t\t\t}\r\n\t\t}).finally(()=>{removeRunDependency(\"loadcachedfiles\");});\r\n\t}\r\n\tcatch(e)\r\n\t{\r\n\t\tremoveRunDependency(\"loadcachedfiles\");\r\n\t}\r\n};\r\n\r\nModule['preRun'] = Module['loadcachedfiles'];\r\nif (typeof Module['files'] !== \"undefined\" && Object.keys(Module['files']).length>0)\r\n{\r\n\tModule['preRun'] = function()\r\n\t{\r\n\t\tModule['loadcachedfiles']();\r\n\r\n\t\tModule['curfile'] = undefined;\r\n\r\n\t\tlet files = Module['files'];\r\n\t\tlet names = Object.keys(files);\r\n\t\tfor (let i = 0; i < names.length; i++)\r\n\t\t{\r\n\t\t\tlet ab = files[names[i]];\r\n\t\t\tlet n = names[i];\r\n\t\t\tif (typeof ab == \"string\")\r\n\t\t\t{\t//if its a string, assume it to be a url of some kind for us to resolve.\r\n\t\t\t\taddRunDependency(n);\r\n\r\n\t\t\t\tlet xhr = new XMLHttpRequest();\r\n\t\t\t\txhr.responseType = \"arraybuffer\";\r\n\t\t\t\txhr.open(\"GET\", ab);\r\n\t\t\t\txhr.onload = function ()\r\n\t\t\t\t{\r\n\t\t\t\t\tif (Module['curfile'] == n)\r\n\t\t\t\t\t\tModule['curfile'] = undefined;\r\n\t\t\t\t\tif (this.status >= 200 && this.status < 300)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tlet b = FTEH.h[_emscriptenfte_buf_createfromarraybuf(this.response)];\r\n\t\t\t\t\t\tb.n = n;\r\n\t\t\t\t\t\tFTEH.f[b.n] = b;\r\n\t\t\t\t\t\tremoveRunDependency(n);\r\n\t\t\t\t\t}\r\n\t\t\t\t\telse\r\n\t\t\t\t\t\tremoveRunDependency(n);\r\n\t\t\t\t};\r\n\t\t\t\txhr.onprogress = function(e)\r\n\t\t\t\t{\r\n\t\t\t\t\tif (typeof Module['curfile'] == \"undefined\")\r\n\t\t\t\t\t\tModule['curfile'] = n;\t//take it.\r\n\t\t\t\t\tif (Module['setStatus'] && Module['curfile']==n)\r\n\t\t\t\t        Module['setStatus'](n + ' (' + e.loaded + '/' + e.total + ')');\r\n\t\t\t\t};\r\n\t\t\t\txhr.onerror = function ()\r\n\t\t\t\t{\r\n\t\t\t\t\tif (Module['curfile'] == n)\r\n\t\t\t\t\t\tModule['curfile'] = undefined;\r\n\t\t\t\t\tremoveRunDependency(n);\r\n\t\t\t\t};\r\n\t\t\t\txhr.send();\r\n\t\t\t}\r\n\t\t\telse if (typeof ab.then == \"function\")\r\n\t\t\t{\t//a 'thenable' thing... assume it'll resolve into an arraybuffer.\r\n\t\t\t\taddRunDependency(n);\r\n\t\t\t\tab.then(\r\n\t\t\t\t\tvalue =>\r\n\t\t\t\t\t{\t//success\r\n\t\t\t\t\t\tlet b = FTEH.h[_emscriptenfte_buf_createfromarraybuf(value)];\r\n\t\t\t\t\t\tb.n = n;\r\n\t\t\t\t\t\tFTEH.f[b.n] = b;\r\n\t\t\t\t\t\tremoveRunDependency(n);\r\n\t\t\t\t\t},\r\n\t\t\t\t\treason =>\r\n\t\t\t\t\t{\t//failure\r\n\t\t\t\t\t\tconsole.log(reason);\r\n\t\t\t\t\t\tremoveRunDependency(n);\r\n\t\t\t\t\t}\r\n\t\t\t\t\t);\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\t//otherwise assume array buffer.\r\n\t\t\t\tlet b = FTEH.h[_emscriptenfte_buf_createfromarraybuf(ab)];\r\n\t\t\t\tb.n = n;\r\n\t\t\t\tFTEH.f[b.n] = b;\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n}\r\nelse if (!Module['manifest'])\r\n{\r\n\tlet man = window.location.protocol + \"//\" + window.location.host + window.location.pathname;\r\n\tif (man.substr(-1) != '/')\r\n\t\tman += \".fmf\";\r\n\telse\r\n\t\tman += \"index.fmf\";\r\n\tModule['manifest'] = man;\r\n\t\r\n\tif (window.location.hash != \"\")\r\n\t\tModule['manifest'] = window.location.hash.substring(1);\r\n}\r\n\r\nif (!Module['arguments'])\t//the html can be explicit about its args if it sets this to an empty array or w/e\r\n{\r\n\tModule['arguments'] = [];\r\n\r\n\t// use query string in URL as command line\r\n\tconst qstrings = decodeURIComponent(window.location.search.substring(1));\r\n\tif (qstrings != \"\")\r\n\t{\r\n\t\tconst qstring = qstrings.split(\" \");\r\n\t\tfor (let i = 0; i < qstring.length; i++)\r\n\t\t{\r\n\t\t\tif (qstring[i] == '-manifest')\r\n\t\t\t\tman = undefined; //don't do double manifest args...\r\n\t\t\tif ((qstring[i] == '+sv_port_rtc' || qstring[i] == '+connect' || qstring[i] == '+join' || qstring[i] == '+observe' || qstring[i] == '+qtvplay') && i+1 < qstring.length)\r\n\t\t\t{\r\n\t\t\t\tModule['arguments'] = Module['arguments'].concat(qstring[i+0], qstring[i+1]);\r\n\t\t\t\ti++;\r\n\t\t\t}\r\n\t\t\telse if (!document.referrer)\t//ignore args from referers in order to try to protect against dodgy srgs a little.\r\n\t\t\t\tModule['arguments'] = Module['arguments'].concat(qstring[i]);\r\n\t\t}\r\n\t}\r\n\r\n\tif (Module['manifest'] != undefined)\r\n\t\tModule['arguments'] = Module['arguments'].concat(['-manifest', Module['manifest']]);\r\n\r\n\t//registerProtocolHandler needs to be able to pass it through to us... so only allow it if we're parsing args from the url.\r\n\tModule['mayregisterscemes'] = true;\r\n}\r\n"
  },
  {
    "path": "engine/web/sys_web.c",
    "content": "#include \"quakedef.h\"\r\n#include <emscripten/emscripten.h>\r\n\r\n#ifndef isDedicated\r\nqboolean isDedicated;\r\n#endif\r\n\r\nquakeparms_t\tparms;\r\n\r\nvoid Sys_Error (const char *error, ...)\r\n{\r\n\tva_list argptr;\r\n\tchar string[1024];\r\n\r\n\tva_start (argptr,error);\r\n\tvsnprintf (string, sizeof (string), error, argptr);\r\n\tva_end (argptr);\r\n\tCOM_WorkerAbort(string);\r\n\tSys_Printf(\"Error: %s\\n\", string);\r\n\r\n\tCon_Print (\"Quake Error: \");\r\n\tCon_Print (string);\r\n\tCon_Print (\"\\n\");\r\n\r\n\tHost_Shutdown ();\r\n\temscriptenfte_alert(string);\r\n\temscriptenfte_abortmainloop(\"Sys_Error\", true);\r\n\texit (1);\r\n}\r\n\r\nvoid Sys_RecentServer(char *command, char *target, char *title, char *desc)\r\n{\r\n}\r\n\r\nqboolean Sys_RandomBytes(qbyte *string, int len)\r\n{\r\n\treturn false;\r\n}\r\n\r\nstatic qboolean sys_supportsansi;\r\nstatic char ansiremap[8] = {'0', '4', '2', '6', '1', '5', '3', '7'};\r\nstatic size_t ApplyColour(char *out, size_t outsize, unsigned int chrflags)\r\n{\r\n\tchar *s, *e;\r\n\tstatic int oldflags = CON_WHITEMASK;\r\n\tint bg, fg;\r\n\r\n\tif (!sys_supportsansi)\r\n\t\treturn 0;\r\n\tif (oldflags == chrflags)\r\n\t\treturn 0;\r\n\ts = out;\r\n\te = out+outsize;\r\n\toldflags = chrflags;\r\n\r\n\t*out++='\\x1b';\r\n\t*out++='[';\r\n\t*out++='0';\r\n\t*out++=';';\r\n\tif (chrflags & CON_BLINKTEXT)\r\n\t{\t//set blink flag\r\n\t\t*out++='5';\r\n\t\t*out++=';';\r\n\t}\r\n\r\n\tbg = (chrflags & CON_BGMASK) >> CON_BGSHIFT;\r\n\tfg = (chrflags & CON_FGMASK) >> CON_FGSHIFT;\r\n\r\n\t// don't handle intensive bit for background\r\n\t// as terminals differ too much in displaying \\e[1;7;3?m\r\n\tbg &= 0x7;\r\n\r\n\tif (chrflags & CON_NONCLEARBG)\r\n\t{\r\n\t\tif (fg & 0x8) // intensive bit set for foreground\r\n\t\t{\t// set bold/intensity ansi flag\r\n\t\t\t*out++='1';\r\n\t\t\t*out++=';';\r\n\t\t\tfg &= 0x7; // strip intensive bit\r\n\t\t}\r\n\r\n\t\t// set foreground and background colors\r\n\t\t*out++='3';\r\n\t\t*out++=ansiremap[fg];\r\n\t\t*out++=';';\r\n\t\t*out++='4';\r\n\t\t*out++=ansiremap[bg];\r\n\t\t*out++='m';\r\n\t}\r\n\telse\r\n\t{\r\n\t\tswitch(fg)\r\n\t\t{\r\n\t\t//to get around wierd defaults (like a white background) we have these special hacks for colours 0 and 7\r\n\t\tcase COLOR_BLACK:\r\n\t\t\t// set inverse\r\n\t\t\t*out++='7';\r\n\t\t\t*out++='m';\r\n\t\t\tbreak;\r\n\t\tcase COLOR_GREY:\r\n\t\t\t*out++='1';\r\n\t\t\t*out++=';';\r\n\t\t\t*out++='3';\r\n\t\t\t*out++='0';\r\n\t\t\t*out++='m';\r\n\t\t\tbreak;\r\n\t\tcase COLOR_WHITE:\r\n\t\t\t// set nothing else\r\n\t\t\t*out++='m';\r\n\t\t\tbreak;\r\n\t\tdefault:\r\n\t\t\tif (fg & 0x8) // intensive bit set for foreground\r\n\t\t\t{\r\n\t\t\t\t// set bold/intensity ansi flag\r\n\t\t\t\t*out++='1';\r\n\t\t\t\t*out++=';';\r\n\t\t\t\tfg &= 0x7; // strip intensive bit\r\n\t\t\t}\r\n\r\n\t\t\t*out++='3';\r\n\t\t\t*out++=ansiremap[fg];\r\n\t\t\t*out++='m';\r\n\t\t\tbreak;\r\n\t\t}\r\n\t}\r\n\treturn out-s;\r\n}\r\n//print into stdout\r\nvoid Sys_Printf (char *fmt, ...)\r\n{\r\n\tva_list\t\targptr;\t\r\n\tchar text[65536];\r\n\tconchar_t\tctext[countof(text)], *e, *c;\r\n\tunsigned int len = 0;\r\n\tunsigned int w, codeflags;\r\n\t\t\r\n\tva_start (argptr,fmt);\r\n\tvsnprintf (text, sizeof(text), fmt, argptr);\r\n\tva_end (argptr);\r\n\r\n\t//make sense of any markup\r\n\te = COM_ParseFunString(CON_WHITEMASK, text, ctext, sizeof(ctext), false);\r\n\r\n\t//convert to utf-8 for the js to make sense of\r\n\tfor (c = ctext; c < e; )\r\n\t{\r\n\t\tc = Font_Decode(c, &codeflags, &w);\r\n\t\tif (codeflags & CON_HIDDEN)\r\n\t\t\tcontinue;\r\n\r\n\t\tif ((codeflags&CON_RICHFORECOLOUR) || (w == '\\n' && (codeflags&CON_NONCLEARBG)))\r\n\t\t\tcodeflags = CON_WHITEMASK;\t//make sure we don't get annoying backgrounds on other lines.\r\n\t\tlen+=ApplyColour(text+len,sizeof(text)-1-len, codeflags);\r\n\r\n\t\t//dequake it as required, so its only codepoints the browser will understand. should probably deal with linefeeds specially.\r\n\t\tif (w >= 0xe000 && w < 0xe100)\r\n\t\t{\t//quake-encoded mess\r\n\t\t\tif ((w & 0x7f) >= 0x20)\r\n\t\t\t\tw &= 0x7f;\t//regular (discoloured) ascii\r\n\t\t\telse if (w & 0x80)\r\n\t\t\t{\t//c1 glyphs\r\n\t\t\t\tstatic char tab[32] = \"---#@.@@@@ # >..\" \"[]0123456789.---\";\r\n\t\t\t\tw = tab[w&31];\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\t//c0 glyphs\r\n\t\t\t\tstatic char tab[32] = \".####.#### # >..\" \"[]0123456789.---\";\r\n\t\t\t\tw = tab[w&31];\r\n\t\t\t}\r\n\t\t}\r\n\t\telse if (w < ' ' && w != '\\t' && w != '\\r' && w != '\\n')\r\n\t\t\tw = '?';\t//c0 chars are awkward\r\n\t\r\n\t\tlen += utf8_encode(text+len, w, sizeof(text)-1-len);\r\n\t}\r\n\r\n\tlen+=ApplyColour(text+len,sizeof(text)-1-len, CON_WHITEMASK);\t//force it back to white at the end of the print... just in case\r\n\r\n\ttext[len] = 0;\r\n\t\r\n\r\n\t//now throw it at the browser's console.log.\r\n\temscriptenfte_print(text);\r\n}\r\n\r\n#if 1\r\n//use Performance.now() instead of Date.now() - its likely to both provide higher precision and no NTP/etc issues.\r\ndouble Sys_DoubleTime (void)\r\n{\r\n\tdouble t = emscriptenfte_uptime_ms()/1000;\t//we need it as seconds...\r\n\tstatic double old = -99999999;\r\n\tif (t < old)\r\n\t\tt = old;\t//don't let t step backwards, ever. this shouldn't happen, but some CPUs don't keep their high-precision timers synced properly.\r\n\treturn old=t;\r\n}\r\nunsigned int Sys_Milliseconds(void)\r\n{\r\n\treturn Sys_DoubleTime() * (uint64_t)1000;\r\n}\r\n#else\r\nunsigned int Sys_Milliseconds(void)\r\n{\r\n\tstatic int first = true;\r\n\tstatic unsigned long oldtime = 0, curtime = 0;\r\n\tunsigned long newtime;\r\n\r\n\tnewtime = emscriptenfte_ticks_ms();\t//return Date.now()\r\n\r\n\tif (first)\r\n\t{\r\n\t\tfirst = false;\r\n\t\toldtime = newtime;\r\n\t}\r\n\tif (newtime < oldtime)\r\n\t\tCon_Printf(\"Sys_Milliseconds stepped backwards!\\n\");\r\n\telse\r\n\t\tcurtime += newtime - oldtime;\r\n\toldtime = newtime;\r\n\treturn curtime;\r\n}\r\n\r\n//return the current time, in the form of a double\r\ndouble Sys_DoubleTime (void)\r\n{\r\n\treturn Sys_Milliseconds() / 1000.0;\r\n}\r\n#endif\r\n\r\n//create a directory. we don't do dirs.\r\nvoid Sys_mkdir (const char *path)\r\n{\r\n}\r\nqboolean Sys_rmdir (const char *path)\r\n{\r\n\treturn true;\r\n}\r\n\r\n//unlink a file\r\nqboolean Sys_remove (const char *path)\r\n{\r\n\temscriptenfte_buf_delete(path);\r\n\treturn true;\r\n}\r\n\r\nqboolean Sys_Rename (const char *oldfname, const char *newfname)\r\n{\r\n\treturn emscriptenfte_buf_rename(oldfname, newfname);\r\n}\r\nqboolean Sys_GetFreeDiskSpace(const char *path, quint64_t *freespace)\r\n{\t//not implemented. we could try querying local storage quotas, but our filesystem is otherwise purely ram so doesn't have much of a limit in 64bit browsers. hurrah for swap space.\r\n\t*freespace = 0;\t//just in case.\r\n\treturn false;\r\n}\r\n\r\n//someone used the 'quit' command\r\n#include \"glquake.h\"\r\nvoid Sys_Quit (void)\r\n{\r\n\tif (host_initialized)\r\n\t{\r\n\t\tqglClearColor(0,0,0,1);\r\n\t\tqglClear(GL_COLOR_BUFFER_BIT);\r\n\t\tDraw_FunString (0, 0, \"Reload the page to restart\");\r\n\r\n\t\tHost_Shutdown();\r\n\t}\r\n\r\n\texit (0);\r\n}\r\n\r\n\r\nstruct enumctx_s\r\n{\r\n\tchar name[MAX_OSPATH];\r\n\tconst char *gpath;\r\n\tsize_t gpathlen;\r\n\tconst char *match;\r\n\tint (*callback)(const char *, qofs_t, time_t mtime, void *, searchpathfuncs_t *);\r\n\tvoid *ctx;\r\n\tsearchpathfuncs_t *spath;\r\n\tint ret;\r\n};\r\nstatic void Sys_EnumeratedFile(void *vctx, size_t fsize)\r\n{\t//called for each enumerated file.\r\n\t//we don't need the whole EnumerateFiles2 thing as our filesystem is flat, so */* isn't an issue for us (we don't expect a lot of different 'files' if only because they're a pain to download).\r\n\tstruct enumctx_s *ctx = vctx;\r\n\tif (!ctx->ret)\r\n\t\treturn;\t//we're meant to stop when if it returns false...\r\n\tif (!strncmp(ctx->name, ctx->gpath, ctx->gpathlen))\t\t//ignore any gamedir prefix\r\n\t\tif (wildcmp(ctx->match, ctx->name+ctx->gpathlen))\t//match it within the searched gamedir...\r\n\t\t\tctx->ret = ctx->callback(ctx->name+ctx->gpathlen, fsize, 0, ctx->ctx, ctx->spath);\t//call the callback\r\n}\r\nint Sys_EnumerateFiles (const char *gpath, const char *match, int (*func)(const char *, qofs_t, time_t mtime, void *, searchpathfuncs_t *), void *parm, searchpathfuncs_t *spath)\r\n{\r\n\tstruct enumctx_s ctx;\r\n\tchar tmp[MAX_OSPATH];\r\n\tif (!gpath)\r\n\t\tgpath = \"\";\r\n\tctx.gpathlen = strlen(gpath);\r\n\tif (ctx.gpathlen && gpath[ctx.gpathlen-1] != '/')\r\n\t{\t//make sure gpath is /-terminated.\r\n\t\tif (ctx.gpathlen >= sizeof(tmp)-1)\r\n\t\t\treturn false;\t//just no...\r\n\t\tQ_strncpyz(tmp, gpath, sizeof(tmp));\r\n\t\tgpath = tmp;\r\n\t\ttmp[ctx.gpathlen++] = '/';\r\n\t}\r\n\tctx.gpath = gpath;\r\n\tctx.match = match;\r\n\tctx.callback = func;\r\n\tctx.ctx = parm;\r\n\tctx.spath = spath;\r\n\tctx.ret = true;\r\n\temscritenfte_buf_enumerate(Sys_EnumeratedFile, &ctx, sizeof(ctx.name));\r\n\treturn ctx.ret;\r\n}\r\n\r\n//blink window if possible (it's not)\r\nvoid Sys_ServerActivity(void)\r\n{\r\n}\r\n\r\nvoid Sys_CloseLibrary(dllhandle_t *lib)\r\n{\r\n}\r\ndllhandle_t *Sys_LoadLibrary(const char *name, dllfunction_t *funcs)\r\n{\r\n\treturn NULL;\r\n}\r\nvoid *Sys_GetAddressForName(dllhandle_t *module, const char *exportname)\r\n{\r\n\treturn NULL;\r\n}\r\n\r\n\r\n\r\nvoid Sys_BrowserRedirect_f(void)\r\n{\r\n\temscriptenfte_window_location(Cmd_Argv(1));\r\n}\r\nvoid Sys_OpenFile_f(void)\r\n{\r\n\temscriptenfte_openfile();\r\n}\r\n\r\n\r\nstatic void Sys_Register_File_Associations_f(void)\r\n{\t//we should be able to register 'web+foo://' schemes here. we can't skip the web+ part though, which is a shame.\r\n\tconst char *s;\r\n\tchar scheme[MAX_OSPATH];\r\n\tconst char *schemes = fs_manifest->schemes;\r\n\tfor (s = schemes; (s=COM_ParseOut(s,scheme,sizeof(scheme)));)\r\n\t{\r\n\t\tEM_ASM({\r\n\t\t\ttry{\r\n\t\t\tif (navigator.registerProtocolHandler)\r\n\t\t\tnavigator.registerProtocolHandler(\r\n\t\t\t\tUTF8ToString($0),\r\n\t\t\t\tdocument.location.origin+document.location.pathname+\"?%s\"+document.location.hash,\r\n\t\t\t\tUTF8ToString($1));\r\n\t\t\t} catch(e){}\r\n\t\t\t}, va(\"%s%s\", strncmp(scheme,\"web+\",4)?\"web+\":\"\", scheme), fs_manifest->formalname?fs_manifest->formalname:fs_manifest->installation);\r\n\t}\r\n}\r\nchar *Sys_URIScheme_NeedsRegistering(void)\r\n{\t//we have no way to query if we're registered or not. cl_main will default to bypassing this.\r\n\treturn NULL;\r\n}\r\n\r\nvoid Sys_Init(void)\r\n{\r\n\textern cvar_t vid_width, vid_height, vid_fullscreen;\r\n\t//vid_fullscreen takes effect only on mouse clicks, any suggestion to do a vid_restart is pointless.\r\n\tvid_fullscreen.flags &= ~CVAR_VIDEOLATCH;\r\n\t//these are not really supported. so silence any spam that suggests we do something about something not even supported.\r\n\tvid_width.flags &= ~CVAR_VIDEOLATCH;\r\n\tvid_height.flags &= ~CVAR_VIDEOLATCH;\r\n\r\n\tCmd_AddCommandD(\"sys_browserredirect\", Sys_BrowserRedirect_f, \"Navigates the browser to a different url. For sites using quake maps as a more interesting sitemap.\");\r\n\tif (EM_ASM_INT(return window.showOpenFilePicker!=undefined;))\t//doesn't work in firefox.\r\n\t\tCmd_AddCommandD(\"sys_openfile\", Sys_OpenFile_f, \"Opens a file picker\");\t//opens file picker\r\n\tif (EM_ASM_INT(return Module['mayregisterscemes'] != false;))\t//needs to be able to pass args via the url. don't bother adding the command if it'll fail. hurrah for checkcmd\r\n\t\tCmd_AddCommandD(\"sys_register_file_associations\", Sys_Register_File_Associations_f, \"Register this page as the default handler for web+scheme handlers.\\n\");\r\n\r\n\t//can't really do feature detection for this... either we spit out unreadable text or we don't...\r\n\tsys_supportsansi = EM_ASM_INT(return navigator.userAgent.indexOf(\"FireFox\")!=-1;);\r\n}\r\nvoid Sys_Shutdown(void)\r\n{\r\n\temscriptenfte_setupmainloop(NULL);\r\n}\r\n\r\n\r\n\r\nint VARGS Sys_DebugLog(char *file, char *fmt, ...)\r\n{\r\n\treturn 0;\r\n};\r\n\r\n\r\n\r\nqboolean Sys_InitTerminal(void)\r\n{\r\n\treturn true;\r\n}\r\nchar *Sys_ConsoleInput(void)\r\n{\r\n\treturn NULL;\r\n}\r\nvoid Sys_CloseTerminal (void)\r\n{\r\n}\r\n\r\nint Sys_MainLoop(double newtime)\r\n{\r\n\textern cvar_t vid_vsync;\r\n\tstatic double oldtime;\r\n\tdouble time;\r\n\r\n\tif (newtime)\r\n\t\tnewtime /= 1000;\t//use RAF's timing for slightly greater precision.\r\n\telse\r\n\t\tnewtime = Sys_DoubleTime ();\t//otherwise fall back on internally consistent timing...\r\n\tif (newtime < oldtime)\r\n\t\tnewtime = oldtime;\t//don't let ourselves go backwards...\r\n\tif (!oldtime)\r\n\t\toldtime = newtime;\r\n\ttime = newtime - oldtime;\r\n\tif (!host_initialized)\r\n\t{\r\n\t\tSys_Printf (\"Starting \"FULLENGINENAME\"\\n\");\r\n\t\tHost_Init (&parms);\r\n\t\treturn 1;\r\n\t}\r\n\r\n\toldtime = newtime;\r\n\tHost_Frame (time);\r\n\r\n\treturn vid_vsync.ival;\r\n}\r\n\r\nint QDECL main(int argc, char **argv)\r\n{\r\n\tmemset(&parms, 0, sizeof(parms));\r\n\r\n\tparms.basedir = \"\";\r\n\r\n\tparms.argc = argc;\r\n\tparms.argv = (const char**)argv;\r\n#ifdef CONFIG_MANIFEST_TEXT\r\n\tparms.manifest = CONFIG_MANIFEST_TEXT;\r\n#endif\r\n\r\n\tCOM_InitArgv (parms.argc, parms.argv);\r\n\r\n\tTL_InitLanguages(\"\");\r\n\r\n\temscriptenfte_setupmainloop(Sys_MainLoop);\r\n\treturn 0;\r\n}\r\n\r\nqboolean Sys_GetDesktopParameters(int *width, int *height, int *bpp, int *refreshrate)\r\n{\r\n\treturn false;\r\n}\r\n\r\n#ifdef WEBCLIENT\r\nqboolean Sys_RunInstaller(void)\r\n{       //not implemented\r\n\treturn false;\r\n}\r\n#endif\r\n\r\n/*static char *clipboard_buffer;\r\nvoid Sys_Clipboard_PasteText(clipboardtype_t cbt, void (*callback)(void *cb, const char *utf8), void *ctx)\r\n{\r\n\tcallback(ctx, clipboard_buffer);\r\n}\r\nvoid Sys_SaveClipboard(clipboardtype_t cbt, const char *text)\r\n{\r\n\tfree(clipboard_buffer);\r\n\tclipboard_buffer = strdup(text);\r\n}*/\r\n\r\n#ifdef MULTITHREAD\r\n#include <SDL_thread.h>\t//FIXME: swap this out for sys_linux_threads.c (our pthreads code)\r\n/* Thread creation calls */\r\nvoid *Sys_CreateThread(char *name, int (*func)(void *), void *args, int priority, int stacksize)\r\n{\r\n\t// SDL threads do not support setting thread stack size\r\n\treturn (void *)SDL_CreateThread(func, args);\r\n}\r\n\r\nvoid Sys_WaitOnThread(void *thread)\r\n{\r\n\tSDL_WaitThread((SDL_Thread *)thread, NULL);\r\n}\r\n\r\n\r\n/* Mutex calls */\r\n// SDL mutexes don't have try-locks for mutexes in the spec so we stick with 1-value semaphores\r\nvoid *Sys_CreateMutex(void)\r\n{\r\n\treturn (void *)SDL_CreateSemaphore(1);\r\n}\r\n\r\nqboolean Sys_TryLockMutex(void *mutex)\r\n{\r\n\treturn !SDL_SemTryWait(mutex);\r\n}\r\n\r\nqboolean Sys_LockMutex(void *mutex)\r\n{\r\n\treturn !SDL_SemWait(mutex);\r\n}\r\n\r\nqboolean Sys_UnlockMutex(void *mutex)\r\n{\r\n\treturn !SDL_SemPost(mutex);\r\n}\r\n\r\nvoid Sys_DestroyMutex(void *mutex)\r\n{\r\n\tSDL_DestroySemaphore(mutex);\r\n}\r\n\r\n/* Conditional wait calls */\r\ntypedef struct condvar_s\r\n{\r\n\tSDL_mutex *mutex;\r\n\tSDL_cond *cond;\r\n} condvar_t;\r\n\r\nvoid *Sys_CreateConditional(void)\r\n{\r\n\tcondvar_t *condv;\r\n\tSDL_mutex *mutex;\r\n\tSDL_cond *cond;\r\n\t\r\n\tcondv = (condvar_t *)malloc(sizeof(condvar_t));\r\n\tif (!condv)\r\n\t\treturn NULL;\r\n\t\t\r\n\tmutex = SDL_CreateMutex();\r\n\tcond = SDL_CreateCond();\r\n\t\r\n\tif (mutex)\r\n\t{\r\n\t\tif (cond)\r\n\t\t{\r\n\t\t\tcondv->cond = cond;\r\n\t\t\tcondv->mutex = mutex;\r\n\t\t\r\n\t\t\treturn (void *)condv;\r\n\t\t}\r\n\t\telse\r\n\t\t\tSDL_DestroyMutex(mutex);\r\n\t}\r\n\t\r\n\tfree(condv);\r\n\treturn NULL;\t\r\n}\r\n\r\nqboolean Sys_LockConditional(void *condv)\r\n{\r\n\treturn !SDL_mutexP(((condvar_t *)condv)->mutex);\r\n}\r\n\r\nqboolean Sys_UnlockConditional(void *condv)\r\n{\r\n\treturn !SDL_mutexV(((condvar_t *)condv)->mutex);\r\n}\r\n\r\nqboolean Sys_ConditionWait(void *condv)\r\n{\r\n\treturn !SDL_CondWait(((condvar_t *)condv)->cond, ((condvar_t *)condv)->mutex);\r\n}\r\n\r\nqboolean Sys_ConditionSignal(void *condv)\r\n{\r\n\treturn !SDL_CondSignal(((condvar_t *)condv)->cond);\r\n}\r\n\r\nqboolean Sys_ConditionBroadcast(void *condv)\r\n{\r\n\treturn !SDL_CondBroadcast(((condvar_t *)condv)->cond);\r\n}\r\n\r\nvoid Sys_DestroyConditional(void *condv)\r\n{\r\n\tcondvar_t *cv = (condvar_t *)condv;\r\n\t\r\n\tSDL_DestroyCond(cv->cond);\r\n\tSDL_DestroyMutex(cv->mutex);\r\n\tfree(cv);\r\n}\r\n#endif\r\n\r\nvoid Sys_Sleep (double seconds)\r\n{\r\n\t//SDL_Delay(seconds * 1000);\r\n}\r\n\r\n"
  },
  {
    "path": "engine/xdk/FTEQW_XDK.sln",
    "content": "Microsoft Visual Studio Solution File, Format Version 8.00\r\nProject(\"{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}\") = \"FTEQW_XDK\", \"FTEQW_XDK.vcproj\", \"{BF8776BA-CCAB-4B5F-AE88-784B2215C589}\"\r\n\tProjectSection(ProjectDependencies) = postProject\r\n\tEndProjectSection\r\nEndProject\r\nGlobal\r\n\tGlobalSection(SolutionConfiguration) = preSolution\r\n\t\tDebug = Debug\r\n\t\tProfile = Profile\r\n\t\tProfile_FastCap = Profile_FastCap\r\n\t\tRelease = Release\r\n\t\tRelease_LTCG = Release_LTCG\r\n\tEndGlobalSection\r\n\tGlobalSection(ProjectConfiguration) = postSolution\r\n\t\t{BF8776BA-CCAB-4B5F-AE88-784B2215C589}.Debug.ActiveCfg = Debug|Xbox\r\n\t\t{BF8776BA-CCAB-4B5F-AE88-784B2215C589}.Debug.Build.0 = Debug|Xbox\r\n\t\t{BF8776BA-CCAB-4B5F-AE88-784B2215C589}.Profile.ActiveCfg = Profile|Xbox\r\n\t\t{BF8776BA-CCAB-4B5F-AE88-784B2215C589}.Profile.Build.0 = Profile|Xbox\r\n\t\t{BF8776BA-CCAB-4B5F-AE88-784B2215C589}.Profile_FastCap.ActiveCfg = Profile_FastCap|Xbox\r\n\t\t{BF8776BA-CCAB-4B5F-AE88-784B2215C589}.Profile_FastCap.Build.0 = Profile_FastCap|Xbox\r\n\t\t{BF8776BA-CCAB-4B5F-AE88-784B2215C589}.Release.ActiveCfg = Release|Xbox\r\n\t\t{BF8776BA-CCAB-4B5F-AE88-784B2215C589}.Release.Build.0 = Release|Xbox\r\n\t\t{BF8776BA-CCAB-4B5F-AE88-784B2215C589}.Release_LTCG.ActiveCfg = Release_LTCG|Xbox\r\n\t\t{BF8776BA-CCAB-4B5F-AE88-784B2215C589}.Release_LTCG.Build.0 = Release_LTCG|Xbox\r\n\tEndGlobalSection\r\n\tGlobalSection(ExtensibilityGlobals) = postSolution\r\n\tEndGlobalSection\r\n\tGlobalSection(ExtensibilityAddIns) = postSolution\r\n\tEndGlobalSection\r\nEndGlobal\r\n"
  },
  {
    "path": "engine/xdk/FTEQW_XDK.vcproj",
    "content": "<?xml version=\"1.0\" encoding=\"Windows-1252\"?>\r\n<VisualStudioProject\r\n\tProjectType=\"Visual C++\"\r\n\tVersion=\"7.10\"\r\n\tName=\"FTEQW_XDK\"\r\n\tProjectGUID=\"{BF8776BA-CCAB-4B5F-AE88-784B2215C589}\"\r\n\tKeyword=\"XboxProj\">\r\n\t<Platforms>\r\n\t\t<Platform\r\n\t\t\tName=\"Xbox\"/>\r\n\t</Platforms>\r\n\t<Configurations>\r\n\t\t<Configuration\r\n\t\t\tName=\"Debug|Xbox\"\r\n\t\t\tOutputDirectory=\"Debug\"\r\n\t\t\tIntermediateDirectory=\"Debug\"\r\n\t\t\tConfigurationType=\"1\"\r\n\t\t\tCharacterSet=\"2\">\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCCLCompilerTool\"\r\n\t\t\t\tOptimization=\"0\"\r\n\t\t\t\tOptimizeForProcessor=\"2\"\r\n\t\t\t\tAdditionalIncludeDirectories=\"..\\qclib;..\\server;..\\gl;..\\common;..\\client\"\r\n\t\t\t\tPreprocessorDefinitions=\"_DEBUG;_XBOX\"\r\n\t\t\t\tMinimalRebuild=\"TRUE\"\r\n\t\t\t\tBasicRuntimeChecks=\"3\"\r\n\t\t\t\tRuntimeLibrary=\"1\"\r\n\t\t\t\tEnableEnhancedInstructionSet=\"1\"\r\n\t\t\t\tUsePrecompiledHeader=\"0\"\r\n\t\t\t\tPrecompiledHeaderThrough=\"\"\r\n\t\t\t\tPrecompiledHeaderFile=\"$(OutDir)/$(ProjectName).pch\"\r\n\t\t\t\tWarningLevel=\"3\"\r\n\t\t\t\tDetect64BitPortabilityProblems=\"FALSE\"\r\n\t\t\t\tDebugInformationFormat=\"4\"/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCCustomBuildTool\"/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCLinkerTool\"\r\n\t\t\t\tAdditionalDependencies=\"xapilibd.lib d3d8d.lib d3dx8d.lib xgraphicsd.lib dsoundd.lib dmusicd.lib xactengd.lib xsndtrkd.lib xvoiced.lib xonlined.lib xboxkrnl.lib xbdm.lib\"\r\n\t\t\t\tOutputFile=\"$(OutDir)/$(ProjectName).exe\"\r\n\t\t\t\tLinkIncremental=\"2\"\r\n\t\t\t\tGenerateDebugInformation=\"TRUE\"\r\n\t\t\t\tProgramDatabaseFile=\"$(OutDir)/$(ProjectName).pdb\"\r\n\t\t\t\tSubSystem=\"2\"\r\n\t\t\t\tOptimizeForWindows98=\"1\"\r\n\t\t\t\tTargetMachine=\"1\"/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCPostBuildEventTool\"/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCPreBuildEventTool\"/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCPreLinkEventTool\"/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"XboxDeploymentTool\"/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"XboxImageTool\"\r\n\t\t\t\tStackSize=\"65536\"\r\n\t\t\t\tIncludeDebugInfo=\"TRUE\"\r\n\t\t\t\tNoLibWarn=\"TRUE\"/>\r\n\t\t</Configuration>\r\n\t\t<Configuration\r\n\t\t\tName=\"Profile|Xbox\"\r\n\t\t\tOutputDirectory=\"Profile\"\r\n\t\t\tIntermediateDirectory=\"Profile\"\r\n\t\t\tConfigurationType=\"1\"\r\n\t\t\tCharacterSet=\"2\">\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCCLCompilerTool\"\r\n\t\t\t\tOptimization=\"3\"\r\n\t\t\t\tOmitFramePointers=\"TRUE\"\r\n\t\t\t\tOptimizeForProcessor=\"2\"\r\n\t\t\t\tPreprocessorDefinitions=\"NDEBUG;_XBOX;PROFILE\"\r\n\t\t\t\tStringPooling=\"TRUE\"\r\n\t\t\t\tRuntimeLibrary=\"0\"\r\n\t\t\t\tBufferSecurityCheck=\"TRUE\"\r\n\t\t\t\tEnableFunctionLevelLinking=\"TRUE\"\r\n\t\t\t\tEnableEnhancedInstructionSet=\"1\"\r\n\t\t\t\tUsePrecompiledHeader=\"3\"\r\n\t\t\t\tPrecompiledHeaderFile=\"$(OutDir)/$(ProjectName).pch\"\r\n\t\t\t\tWarningLevel=\"3\"\r\n\t\t\t\tDetect64BitPortabilityProblems=\"FALSE\"\r\n\t\t\t\tDebugInformationFormat=\"3\"/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCCustomBuildTool\"/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCLinkerTool\"\r\n\t\t\t\tAdditionalDependencies=\"xapilib.lib d3d8i.lib d3dx8.lib xgraphics.lib dsound.lib dmusici.lib xactengi.lib xsndtrk.lib xvoice.lib xonlines.lib xboxkrnl.lib xbdm.lib xperf.lib\"\r\n\t\t\t\tOutputFile=\"$(OutDir)/$(ProjectName).exe\"\r\n\t\t\t\tLinkIncremental=\"1\"\r\n\t\t\t\tGenerateDebugInformation=\"TRUE\"\r\n\t\t\t\tProgramDatabaseFile=\"$(OutDir)/$(ProjectName).pdb\"\r\n\t\t\t\tSubSystem=\"2\"\r\n\t\t\t\tOptimizeReferences=\"2\"\r\n\t\t\t\tEnableCOMDATFolding=\"2\"\r\n\t\t\t\tOptimizeForWindows98=\"1\"\r\n\t\t\t\tSetChecksum=\"TRUE\"\r\n\t\t\t\tTargetMachine=\"1\"/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCPostBuildEventTool\"/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCPreBuildEventTool\"/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCPreLinkEventTool\"/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"XboxDeploymentTool\"/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"XboxImageTool\"\r\n\t\t\t\tStackSize=\"65536\"\r\n\t\t\t\tIncludeDebugInfo=\"TRUE\"\r\n\t\t\t\tNoLibWarn=\"TRUE\"/>\r\n\t\t</Configuration>\r\n\t\t<Configuration\r\n\t\t\tName=\"Profile_FastCap|Xbox\"\r\n\t\t\tOutputDirectory=\"Profile_FastCap\"\r\n\t\t\tIntermediateDirectory=\"Profile_FastCap\"\r\n\t\t\tConfigurationType=\"1\"\r\n\t\t\tCharacterSet=\"2\">\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCCLCompilerTool\"\r\n\t\t\t\tOptimization=\"3\"\r\n\t\t\t\tOmitFramePointers=\"TRUE\"\r\n\t\t\t\tOptimizeForProcessor=\"2\"\r\n\t\t\t\tPreprocessorDefinitions=\"NDEBUG;_XBOX;PROFILE;FASTCAP\"\r\n\t\t\t\tStringPooling=\"TRUE\"\r\n\t\t\t\tRuntimeLibrary=\"0\"\r\n\t\t\t\tBufferSecurityCheck=\"TRUE\"\r\n\t\t\t\tEnableFunctionLevelLinking=\"TRUE\"\r\n\t\t\t\tEnableEnhancedInstructionSet=\"1\"\r\n\t\t\t\tUsePrecompiledHeader=\"3\"\r\n\t\t\t\tPrecompiledHeaderFile=\"$(OutDir)/$(ProjectName).pch\"\r\n\t\t\t\tWarningLevel=\"3\"\r\n\t\t\t\tDetect64BitPortabilityProblems=\"FALSE\"\r\n\t\t\t\tDebugInformationFormat=\"3\"\r\n\t\t\t\tFastCAP=\"TRUE\"/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCCustomBuildTool\"/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCLinkerTool\"\r\n\t\t\t\tAdditionalDependencies=\"xapilib.lib d3d8i.lib d3dx8.lib xgraphics.lib dsound.lib dmusici.lib xactengi.lib xsndtrk.lib xvoice.lib xonlines.lib xboxkrnl.lib xbdm.lib xperf.lib\"\r\n\t\t\t\tOutputFile=\"$(OutDir)/$(ProjectName).exe\"\r\n\t\t\t\tLinkIncremental=\"1\"\r\n\t\t\t\tGenerateDebugInformation=\"TRUE\"\r\n\t\t\t\tProgramDatabaseFile=\"$(OutDir)/$(ProjectName).pdb\"\r\n\t\t\t\tSubSystem=\"2\"\r\n\t\t\t\tOptimizeReferences=\"2\"\r\n\t\t\t\tEnableCOMDATFolding=\"2\"\r\n\t\t\t\tOptimizeForWindows98=\"1\"\r\n\t\t\t\tSetChecksum=\"TRUE\"\r\n\t\t\t\tTargetMachine=\"1\"/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCPostBuildEventTool\"/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCPreBuildEventTool\"/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCPreLinkEventTool\"/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"XboxDeploymentTool\"/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"XboxImageTool\"\r\n\t\t\t\tStackSize=\"65536\"\r\n\t\t\t\tIncludeDebugInfo=\"TRUE\"\r\n\t\t\t\tNoLibWarn=\"TRUE\"/>\r\n\t\t</Configuration>\r\n\t\t<Configuration\r\n\t\t\tName=\"Release|Xbox\"\r\n\t\t\tOutputDirectory=\"Release\"\r\n\t\t\tIntermediateDirectory=\"Release\"\r\n\t\t\tConfigurationType=\"1\"\r\n\t\t\tCharacterSet=\"2\">\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCCLCompilerTool\"\r\n\t\t\t\tOptimization=\"3\"\r\n\t\t\t\tOmitFramePointers=\"TRUE\"\r\n\t\t\t\tOptimizeForProcessor=\"2\"\r\n\t\t\t\tAdditionalIncludeDirectories=\"..\\qclib;..\\server;..\\gl;..\\common;..\\client\"\r\n\t\t\t\tPreprocessorDefinitions=\"NDEBUG;_XBOX\"\r\n\t\t\t\tStringPooling=\"TRUE\"\r\n\t\t\t\tRuntimeLibrary=\"0\"\r\n\t\t\t\tBufferSecurityCheck=\"TRUE\"\r\n\t\t\t\tEnableFunctionLevelLinking=\"TRUE\"\r\n\t\t\t\tEnableEnhancedInstructionSet=\"1\"\r\n\t\t\t\tUsePrecompiledHeader=\"0\"\r\n\t\t\t\tPrecompiledHeaderThrough=\"\"\r\n\t\t\t\tPrecompiledHeaderFile=\"$(OutDir)/$(ProjectName).pch\"\r\n\t\t\t\tWarningLevel=\"3\"\r\n\t\t\t\tDetect64BitPortabilityProblems=\"FALSE\"\r\n\t\t\t\tDebugInformationFormat=\"3\"/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCCustomBuildTool\"/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCLinkerTool\"\r\n\t\t\t\tAdditionalDependencies=\"xapilib.lib d3d8.lib d3dx8.lib xgraphics.lib dsound.lib dmusic.lib xacteng.lib xsndtrk.lib xvoice.lib xonlines.lib xboxkrnl.lib\"\r\n\t\t\t\tOutputFile=\"$(OutDir)/$(ProjectName).exe\"\r\n\t\t\t\tLinkIncremental=\"1\"\r\n\t\t\t\tGenerateDebugInformation=\"TRUE\"\r\n\t\t\t\tProgramDatabaseFile=\"$(OutDir)/$(ProjectName).pdb\"\r\n\t\t\t\tSubSystem=\"2\"\r\n\t\t\t\tOptimizeReferences=\"2\"\r\n\t\t\t\tEnableCOMDATFolding=\"2\"\r\n\t\t\t\tOptimizeForWindows98=\"1\"\r\n\t\t\t\tSetChecksum=\"TRUE\"\r\n\t\t\t\tTargetMachine=\"1\"/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCPostBuildEventTool\"/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCPreBuildEventTool\"/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCPreLinkEventTool\"/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"XboxDeploymentTool\"/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"XboxImageTool\"\r\n\t\t\t\tStackSize=\"65536\"/>\r\n\t\t</Configuration>\r\n\t\t<Configuration\r\n\t\t\tName=\"Release_LTCG|Xbox\"\r\n\t\t\tOutputDirectory=\"Release_LTCG\"\r\n\t\t\tIntermediateDirectory=\"Release_LTCG\"\r\n\t\t\tConfigurationType=\"1\"\r\n\t\t\tCharacterSet=\"2\"\r\n\t\t\tWholeProgramOptimization=\"TRUE\">\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCCLCompilerTool\"\r\n\t\t\t\tOptimization=\"3\"\r\n\t\t\t\tOmitFramePointers=\"TRUE\"\r\n\t\t\t\tOptimizeForProcessor=\"2\"\r\n\t\t\t\tPreprocessorDefinitions=\"NDEBUG;_XBOX;LTCG\"\r\n\t\t\t\tStringPooling=\"TRUE\"\r\n\t\t\t\tRuntimeLibrary=\"0\"\r\n\t\t\t\tBufferSecurityCheck=\"TRUE\"\r\n\t\t\t\tEnableFunctionLevelLinking=\"TRUE\"\r\n\t\t\t\tEnableEnhancedInstructionSet=\"1\"\r\n\t\t\t\tUsePrecompiledHeader=\"3\"\r\n\t\t\t\tPrecompiledHeaderFile=\"$(OutDir)/$(ProjectName).pch\"\r\n\t\t\t\tWarningLevel=\"3\"\r\n\t\t\t\tDetect64BitPortabilityProblems=\"FALSE\"\r\n\t\t\t\tDebugInformationFormat=\"3\"/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCCustomBuildTool\"/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCLinkerTool\"\r\n\t\t\t\tAdditionalDependencies=\"xapilib.lib d3d8ltcg.lib d3dx8.lib xgraphicsltcg.lib dsound.lib dmusicltcg.lib xactengltcg.lib xsndtrk.lib xvoice.lib xonlines.lib xboxkrnl.lib\"\r\n\t\t\t\tOutputFile=\"$(OutDir)/$(ProjectName).exe\"\r\n\t\t\t\tLinkIncremental=\"1\"\r\n\t\t\t\tGenerateDebugInformation=\"TRUE\"\r\n\t\t\t\tProgramDatabaseFile=\"$(OutDir)/$(ProjectName).pdb\"\r\n\t\t\t\tSubSystem=\"2\"\r\n\t\t\t\tOptimizeReferences=\"2\"\r\n\t\t\t\tEnableCOMDATFolding=\"2\"\r\n\t\t\t\tOptimizeForWindows98=\"1\"\r\n\t\t\t\tSetChecksum=\"TRUE\"\r\n\t\t\t\tTargetMachine=\"1\"/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCPostBuildEventTool\"/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCPreBuildEventTool\"/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCPreLinkEventTool\"/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"XboxDeploymentTool\"/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"XboxImageTool\"\r\n\t\t\t\tStackSize=\"65536\"/>\r\n\t\t</Configuration>\r\n\t</Configurations>\r\n\t<References>\r\n\t</References>\r\n\t<Files>\r\n\t\t<Filter\r\n\t\t\tName=\"Source Files\"\r\n\t\t\tFilter=\"cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx\"\r\n\t\t\tUniqueIdentifier=\"{4FC737F1-C7A5-4376-A066-2A32D752A2FF}\">\r\n\t\t\t<Filter\r\n\t\t\t\tName=\"client\"\r\n\t\t\t\tFilter=\"\">\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\client\\cd_null.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\client\\cl_cam.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\client\\cl_cg.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\client\\cl_demo.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\client\\cl_ents.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\client\\cl_ignore.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\client\\cl_input.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\client\\cl_main.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\client\\cl_parse.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\client\\cl_pred.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\client\\cl_screen.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\client\\cl_tent.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\client\\cl_ui.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\client\\console.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\client\\fragstats.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\http\\httpclient.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\client\\image.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\client\\in_generic.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\client\\keys.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\client\\m_download.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\client\\m_items.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\client\\m_master.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\client\\m_mp3.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\client\\m_multi.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\client\\m_options.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\client\\m_script.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\client\\m_single.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\client\\menu.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\client\\net_master.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\client\\p_classic.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\client\\p_null.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\client\\p_script.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\client\\pr_clcmd.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\client\\pr_csqc.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\client\\pr_menu.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\client\\pr_skelobj.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\client\\r_2d.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\client\\r_part.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\client\\r_partset.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\client\\r_surf.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\client\\renderer.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\client\\renderque.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\client\\sbar.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\client\\skin.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\client\\snd_dma.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\client\\snd_mem.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\client\\snd_mix.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\client\\sys_xdk.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\client\\teamplay.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\client\\valid.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\client\\view.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\client\\wad.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\client\\zqtp.c\">\r\n\t\t\t\t</File>\r\n\t\t\t</Filter>\r\n\t\t\t<Filter\r\n\t\t\t\tName=\"common\"\r\n\t\t\t\tFilter=\"\">\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\common\\cmd.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\common\\com_mesh.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\common\\common.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\common\\crc.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\common\\cvar.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\common\\fs.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\common\\fs_pak.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\common\\fs_stdio.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\common\\fs_xz.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\common\\fs_zip.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\common\\gl_q2bsp.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\common\\huff.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\common\\log.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\common\\mathlib.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\common\\md4.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\common\\net_chan.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\common\\net_wins.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\common\\plugin.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\common\\pmove.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\common\\pmovetst.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\common\\pr_bgcmd.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\common\\q1bsp.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\common\\q2pmove.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\common\\q3common.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\common\\qvm.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\common\\sha1.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\common\\translate.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\common\\zone.c\">\r\n\t\t\t\t</File>\r\n\t\t\t</Filter>\r\n\t\t\t<Filter\r\n\t\t\t\tName=\"d3d\"\r\n\t\t\t\tFilter=\"\">\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\d3d\\d3d8_backend.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\d3d\\d3d8_image.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\d3d\\vid_d3d8.c\">\r\n\t\t\t\t</File>\r\n\t\t\t</Filter>\r\n\t\t\t<Filter\r\n\t\t\t\tName=\"qcclib\"\r\n\t\t\t\tFilter=\"\">\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\qclib\\comprout.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\qclib\\hash.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\qclib\\initlib.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\qclib\\pr_edict.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\qclib\\pr_exec.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\qclib\\pr_multi.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\qclib\\pr_x86.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\qclib\\qcc_cmdlib.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\qclib\\qcc_pr_comp.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\qclib\\qcc_pr_lex.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\qclib\\qccmain.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\qclib\\qcd_main.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\qclib\\qcdecomp.c\">\r\n\t\t\t\t</File>\r\n\t\t\t</Filter>\r\n\t\t\t<Filter\r\n\t\t\t\tName=\"server\"\r\n\t\t\t\tFilter=\"\">\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\server\\net_preparse.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\server\\pr_cmds.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\server\\pr_q1qvm.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\server\\savegame.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\server\\sv_ccmds.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\server\\sv_chat.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\server\\sv_cluster.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\server\\sv_demo.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\server\\sv_ents.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\server\\sv_init.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\server\\sv_main.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\server\\sv_master.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\server\\sv_move.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\server\\sv_mvd.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\server\\sv_nchan.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\server\\sv_phys.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\server\\sv_rankin.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\server\\sv_send.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\server\\sv_user.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\server\\world.c\">\r\n\t\t\t\t</File>\r\n\t\t\t</Filter>\r\n\t\t\t<Filter\r\n\t\t\t\tName=\"gl\"\r\n\t\t\t\tFilter=\"\">\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\gl\\gl_alias.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\gl\\gl_font.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\gl\\gl_hlmdl.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\gl\\gl_model.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\gl\\gl_ngraph.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\gl\\gl_rlight.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\gl\\gl_shader.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\gl\\gl_shadow.c\">\r\n\t\t\t\t</File>\r\n\t\t\t\t<File\r\n\t\t\t\t\tRelativePath=\"..\\gl\\gl_warp.c\">\r\n\t\t\t\t</File>\r\n\t\t\t</Filter>\r\n\t\t</Filter>\r\n\t\t<Filter\r\n\t\t\tName=\"Header Files\"\r\n\t\t\tFilter=\"h;hpp;hxx;hm;inl;inc;xsd\"\r\n\t\t\tUniqueIdentifier=\"{93995380-89BD-4b04-88EB-625FBE52EBFB}\">\r\n\t\t</Filter>\r\n\t</Files>\r\n\t<Globals>\r\n\t</Globals>\r\n</VisualStudioProject>\r\n"
  },
  {
    "path": "fte.m4",
    "content": "dnl\ndnl This file will be processed to provide relevant package lists for the various packages built from FTE's source.\ndnl The output will need to be combined with any other packages, and then signed for these packages to be considered valid.\ndnl Users can add extra sources with the `pkg addsource URL' console command (will show a prompt, to try to avoid exploits).\ndnl\ndefine(`DATE',FTE_DATE)dnl\ndefine(`REVISION',FTE_REVISION)dnl\ndefine(`DLSIZE',`esyscmd(`stat --printf=\"%s\" $1')')dnl\ndefine(`SHA512',`esyscmd(`fteqw -sha512 $1')')dnl\ndefine(`URL',`url\t\t\"$1\"\n\t\tdlsize\t\t\"DLSIZE($1)\"\n \t\tsha512\t\t\"SHA512($1)\" ')dnl\ndefine(`CAT',`$1$2$3$4$5$6$7$8$9')dnl\ndefine(`ZIP',`unzipfile\t\"$2\"\n\t\tURL($1$3)')dnl\ndefine(`FILE',`file\t\t\"$1$2$3$4$5$6$7$8$9\"')dnl\ndefine(`WINENGINE',`category\t\"Engine\"\n\tver\t\t\t\"REVISION\"\n\tgamedir\t\t\"\"\n\tlicense\t\t\"GPLv2\"\n\t{\n\t\tarch\t\t\"win_x86-FTE$1\"\n\t\tFILE(fteqw_,REVISION,_32.exe)\n\t\tZIP(win32/,CAT($2,.exe),CAT($2,_win32.zip))\n\t}\n\t{\n\t\tarch\t\t\"win_x64-FTE$1\"\n\t\tFILE(fteqw_,REVISION,_64.exe)\n\t\tZIP(win64/,CAT($2,64.exe),CAT($2,_win64.zip))\n\t}')dnl\ndefine(`LINENGINE2',`{\n\t\tarch\t\t\"linux_amd64-FTE$1\"\n\t\tFILE(fteqw_,REVISION,_64.bin)\n\t\tZIP(linux_amd64/,CAT($2,64),$3)\n\t}')dnl\ndefine(`LINENGINE',`LINENGINE2($1,$2,CAT($2_lin64.zip))')dnl\ndefine(`HIDE',)dnl\ndefine(`GAME',`ifelse(FTE_GAME,`$1',`$2'\n,)')dnl\ndefine(`TEST',`ifelse(FTE_TEST,`1',`\ttest\t\t\"1\"\n',`\ttest\t\t\"0\"\n')')dnl\n{\n\tpackage\t\"fte_cl\"\n\tWINENGINE(-m,fteqw)\n\tLINENGINE(-m,fteqw)\n\ttitle\t\t\"CAT(`FTE Engine ',DATE)\"\n\tdesc\t\t\"The awesome FTE engine (multi-renderer build)\"\nTEST()dnl\n}\nHIDE(`\n{\n\tpackage\t\"fte_cl_gl\"\ndnl\tWINENGINE(-gl,fteglqw)\ndnl\t//don't bother advertising it on linux\ndnl\ttitle\t\t\"CAT(`FTE Engine ',DATE,` - OpenGL'\")\n\tdesc\t\t\"The awesome FTE engine (OpenGL-only build)\"\nTEST()dnl\n}\n{\n\tpackage\t\"fte_cl_vk\"\ndnl\tWINENGINE(-vk,ftevkqw)\ndnl\t//don't bother advertising it on linux\ndnl\ttitle\t\t\"CAT(`FTE Engine ',DATE,` - Vulkan')\"\n\tdesc\t\t\"The awesome FTE engine (Vulkan-only build)\"\nTEST()dnl\n}\n{\n\tpackage\t\"fte_cl_d3d\"\ndnl\tWINENGINE(-d3d,fted3dqw)\n\t//no d3d on linux\ndnl\ttitle\t\t\"CAT(`FTE Engine ',DATE,` - Direct3D')\"\n\tdesc\t\t\"The awesome FTE engine (Direct3D-only build)\"\nTEST()dnl\n}')dnl\n{\n\tpackage\t\"fte_sv\"\n\tWINENGINE(-sv,fteqwsv)\n\tLINENGINE2(-sv,fteqw-sv,fteqwsv_lin64.zip)\n\ttitle\t\t\"CAT(`FTE Engine ',DATE,` - Server')\"\n\tdesc\t\t\"The awesome FTE engine (server-only build)\"\nTEST()dnl\n}\ndefine(`WINPLUG',`category\t\"Plugins\"\n\tver\t\t\t\"REVISION\"\n\tgamedir\t\t\"\"\n\tlicense\t\t\"GPLv2\"\n\t{\n\t\tarch\t\t\"win_x86\"\n\t\tFILE(fteplug_$1_x86.REVISION.dll)\n\t\tURL(win32/fteplug_$1_x86.dll)\n\t}\n\t{\n\t\tarch\t\t\"win_x64\"\n\t\tFILE(fteplug_$1_x64.REVISION.dll)\n\t\tURL(win64/fteplug_$1_x64.dll)\n\t}')dnl\ndefine(`WIN64PLUG',`category\t\"Plugins\"\n\tver\t\t\t\"REVISION\"\n\tgamedir\t\t\"\"\n\tlicense\t\t\"GPLv2\"\n\t{\n\t\tarch\t\t\"win_x64\"\n\t\tFILE(fteplug_$1_x64.REVISION.dll)\n\t\tURL(win64/fteplug_$1_x64.dll)\n\t}')dnl\ndefine(`LINPLUG',`{\n\t\tarch\t\t\"linux_amd64\"\n\t\tFILE(fteplug_$1_amd64.REVISION,.so)\n\t\tURL(linux_amd64/fteplug_$1_amd64.so)\n\t}')dnl\nGAME(quake,\n`{\n\tpackage \"fteplug_ezhud\"\n\tWINPLUG(ezhud)\n\tLINPLUG(ezhud)\n\ttitle\t\t\t\"EzHud Plugin\"\n\treplace\t\t\t\"ezhud\"\n\tdesc\t\t\t\"Some lame alternative configurable hud.\"\nTEST()dnl\n}')dnl\nGAME(quake,\n`{\n\tpackage \t\t\"fteplug_qi\"\n\tWINPLUG(qi)\n\tLINPLUG(qi)\n\tcategory\t\t\"Plugins\"\n\ttitle\t\t\t\"Quake Injector Plugin\"\n\treplace\t\t\t\"Quake Injector Plugin\"\n\tauthor\t\t\t\"Spike\"\n\twebsite\t\t\t\"https://www.quaddicted.com/reviews/\"\n\tdesc\t\t\t\"Provides a way to quickly list+install+load numerous different maps and mods. Some better than others.\"\n\tdesc\t\t\t\"If youre a single-player fan then these will keep you going for quite some time.\"\n\tdesc\t\t\t\"The database used is from quaddicted.com.\"\nTEST()dnl\n}')dnl\n{\n\tpackage \"fteplug_irc\"\n\tWINPLUG(irc)\n\tLINPLUG(irc)\n\ttitle\t\t\t\"IRC Plugin\"\n\treplace\t\t\t\"IRC Plugin\"\n\tdesc\t\t\t\"Allows you to converse on IRC servers in-game.\"\n\tdesc\t\t\t\"Requires manual configuration.\"\nTEST()dnl\n}\n{\n\tpackage \"fteplug_xmpp\"\n\tWINPLUG(xmpp)\n\tLINPLUG(xmpp)\n\ttitle\t\t\t\"XMPP Plugin\"\n\tdesc\t\t\t\"Allows you to converse on XMPP servers. This also includes a method for NAT holepunching between contacts.\"\n\tdesc\t\t\t\"Requires manual configuration.\"\nTEST()dnl\n}\n{\n\tpackage \"fteplug_openssl\"\n\tWINPLUG(openssl)\n\tlicense\t\t\t\"GPLv3\"\t//Apache2+GPLv2=GPLv3\n\ttitle\t\t\t\"OpenSSL Plugin\"\n\tauthor\t\t\t\"Spike\"\n\tdesc\t\t\t\"Provides TLS and DTLS support, instead of using Microsoft's probably-outdated libraries.\"\n\tdesc\t\t\t\"Required for fully functional DTLS support on windows.\"\n\tdesc\t\t\t\"Connecting to QEx servers requires additional setup.\"\nTEST()dnl\n}\n{\n\tpackage \"fteplug_hl2\"\n\tWINPLUG(hl2)\n\tLINPLUG(hl2)\n\ttitle\t\t\t\"HalfLife2 Formats Plugin\"\n\tdesc\t\t\t\"Provides support for HalfLife2 bsp and texture formats.\"\n\tdesc\t\t\t\"Some related games may work, but this is not guarenteed.\"\n\tdesc\t\t\t\"Requires mod support for full functionality.\"\nTEST()dnl\n}\nHIDE(`\n{\n\tpackage \t\t\"fteplug_models\"\n\tWINPLUG(models)\n\tLINPLUG(models)\n\ttitle\t\t\t\"Model exporter plugin\"\n\tdesc\t\t\t\"\"\nTEST()dnl\n}\n{\t\n\tpackage fteplug_ode\nTEST()dnl\n}\n{\n\tpackage fteplug_cef\nTEST()dnl\n}\n{\n\tpackage\t\t\"fteplug_ffmpeg\"\n\t{\n\t\tarch\t\"win_x86\"\n\t\tfile\t\"fteplug_ffmpeg_x86.\"#REVISION#\".dll\"\n\t\turl\t\t\"win32/fteplug_ffmpeg_x86.dll\"\n\t}\n\t{\n\t\tarch\t\"win_x64\"\n\t\tfile\t\"fteplug_ffmpeg_x64.\"#REVISION#\".dll\"\n\t\turl\t\t\"win64/fteplug_ffmpeg_x64.dll\"\n\t}\n//\t{\n//\t\tarch \t\"linux_x64\"\n//\t\tfile\t\"fteplug_ffmpeg_amd64.\"#REVISION#\".so\"\n//\t\turl\t\t\"linux_amd64/fteplug_ffmpeg_amd64.so\"\n//\t}\n\tver\t\t\tREVISION\n\tcategory\t\"Plugins\"\n\ttitle\t\t\"FFmpeg Plugin\"\n\tfile\t\t\"fteplug_ffmpeg\"\n\tgamedir\t\t\"\"\nTEST()dnl\n}\n{\n\tpackage\t\t\"libffmpeg\"\n\t{\n\t\tarch\t\"win_x86\"\n\t\turl\t\t\"win32/ffmpeg-4.0-x86.zip\"\n\t}\n\t{\n\t\tarch\t\"win_x64\"\n\t\turl\t\t\"win64/ffmpeg-4.0-x64.zip\"\n\t}\n//\t{\n//\t\tarch\t\"linux_x64\"\n//\t\turl\t\t\"linux_amd64/ffmpeg-4.0-fteplug_ezhud_amd64.so\"\n//\t}\n\tver\t\t\t\"4.0\"\n\tcategory\t\"Plugins\"\n\ttitle\t\t\"FFmpeg Library\"\n\textract\t\t\"zip\"\n\tgamedir\t\t\"\"\n}\n')dnl\nGAME(quake2,\n`{\n\tpackage\t\t\t\"q2game_baseq2\"\n\t{\n\t\tarch\t\t\"win_x86\"\n\t\tFILE(q2gamex86_baseq2.dll)\n\t\tURL(win32/q2gamex86_baseq2.dll)\n\t}\n\t{\n\t\tarch\t\t\"win_x64\"\n\t\tFILE(q2gamex64_baseq2.dll)\n\t\tURL(win64/q2gamex64_baseq2.dll)\n\t}\n\t{\n\t\tarch\t\t\"linux_amd64\"\n\t\tFILE(q2gameamd64_baseq2.so)\n\t\tURL(linux_amd64/q2gameamd64_baseq2.so)\n\t}\n\tver\t\t\t\t\"20190606\"\n\tcategory\t\t\"Mods\"\n\ttitle\t\t\t\"Gamecode: Base Game\"\n\tlicense\t\t\t\"GPLv2\"\n\twebsite\t\t\t\"https://github.com/yquake2/yquake2\"\n\tdesc\t\t\t\"Quake2 Gamecode (from yamagiq2). Required for single player or servers.\"\nTEST()dnl\n}')dnl\nGAME(quake,\n`{\n\tpackage\t\t\t\"fte_csaddon\"\n\tcategory\t\t\"Plugins\"\n\ttitle\t\t\t\"Ingame Map Editor\"\n\tver\t\t\t\tREVISION\n\tgamedir\t\t\t\"fte\"\n\tFILE(csaddon.dat)\n\tURL(csaddon/csaddon.dat)\n\tdesc\t\t\t\"This is Spikes map editing user interface. It is only active while running singleplayer (or sv_cheats is enabled).\"\n\tdesc\t\t\t\"To activate, set the ca_show cvar to 1 (suggestion: ^abind c toggle ca_show^a).\"\n\tlicense\t\t\t\"GPLv2, source on the fte svn\"\n\tauthor\t\t\t\"Spike\"\nTEST()dnl\n}')dnl\nGAME(quake,\n`{\n\tpackage\t\t\t\"fte_menusys\"\n\tcategory\t\t\"AfterQuake\"\n\ttitle\t\t\t\"Replacement Menus\"\n\tver\t\t\t\tREVISION\n\tgamedir\t\t\t\"fte\"\n\tFILE(menu.dat)\n\tURL(csaddon/menu.dat)\n\tdesc\t\t\t\"This provides a more modern mouse-first menu system.\"\n\tlicense\t\t\t\"GPLv2, source on the fte svn\"\n\tauthor\t\t\t\"Spike\"\nTEST()dnl\n}')dnl\n\n"
  },
  {
    "path": "ftechrootbuild.sh",
    "content": "#!/bin/sh\n#Script to set up a debian chroot suitable for compiling fte's public builds.\n#deterministic builds are attempted but also requires:\n#\tgcc/etc versions must match exactly (we use debian oldstable, which should reduce issues...)\n#\tsourcecode must be unmodified, particuarly if 'svnversion' reports modified even in an irrelevant file then embedded revision numbers will be wrong.\n#\tthird party dependancies need to work and not get messed up (either me failing to re-run makelibs, random wget failures, or outdated revisions being removed from public access)\n#\tobtained sourcecode revision must match the binary you're trying to duplicate (pre-5601 will insist on updating to latest svn (which may not even have a public build), so expect problems trying to duplicate older builds when the scripts instead try to grab the most recent build).\n#for regular use you should probably set up schroot so you don't need to remember so many args\n#requires about 2.3gb for the chroot+win64 build.\n#android and emscripten targets require proper mounting of /proc and /dev and are NOT supported by this script. don't try enabling them\n\nFTEBUILD=/tmp/ftebuild #change freely\nCHUID=1000 #should generally be your non-root user id, giving you the same access in or out of the chroot...\nCHUSER=spike #sadly this matters. youll just need to pretend to be me inside your chroot for now.\nDEBIANMIRROR=http://ftp.uk.debian.org/debian/\nDEBIANVERSION=stretch\t#oldstable now... should update to stable, but paranoid to update due to portability of glibc symbols.\nLANG= #no language packs installed, so would be spammy if the following rules inherit the host lang\n#FTEREVISON=\"-r 5601\"\t#earlier than 5601 will fail (scripts will try to update to latest)\n#THREADS=\"-j 8\" #override number of threads to compile with, if you have a decent cpu.\n\n#package lists\n#general packages required to get the build system working (python+unzip+etc is for third-party dependancies)\nGENERALPACKAGES= subversion build-essential automake ca-certificates unzip p7zip-full zip libtool python pkg-config\n#package list needed to crosscompile for windows\nWINTARGETPACKAGES= mingw-w64\n#dev packages required to compile the linux build properly. Comment out for less downloads/diskusage\nLINUXTARGETPACKAGES= gcc-multilib g++-multilib mesa-common-dev libasound2-dev libxcursor-dev libgnutls28-dev\n\n#NOTE: chroot does NOT wipe all environment settings. some get carried over. This is a problem if your distro has a default PATH that excludes the system programs on debian, so this is included to be sure.\nexport PATH=/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games\n\n#Set up our chroot (you can skip this part entirely if you're preconfigured a VM)\n#make sure debootstrap is installed, without erroring out if you're not on debian-derivative (NOTE: such users will needs to manually install it first from somewhere!)\n(which apt-get>/dev/null) && apt-get install --no-install-recommends debootstrap\n#create the new debian chroot. it should receive the most recent versions of packages.\ndebootstrap $DEBIANVERSION $FTEBUILD $DEBIANMIRROR\necho \"FTEBuild\">$FTEBUILD/etc/debian_chroot\t\t\t\t\t\t\t\t\t\t\t\t\t\t#optional, so it shows if you decide to run a bash prompt inside the chroot.\nchroot $FTEBUILD adduser --uid $CHUID $CHUSER \t\t\t\t\t\t\t\t\t\t\t\t\t#create a user (with a homedir), so we dont depend upon root inside the guest, where possible\n\n#Install the extra packages needed to build\nchroot $FTEBUILD apt-get install --no-install-recommends $GENERALPACKAGES $WINTARGETPACKAGES $LINUXTARGETPACKAGES\n#NOW we finally start with non-debian downloads by grabbing the FTE sourcecode\nchroot $FTEBUILD su $CHUSER -c \"svn checkout https://svn.code.sf.net/p/fteqw/code/trunk ~/quake/fteqw-code $FTEREVISON\"\t#grab all the source code.\n#FTE has some setup bollocks, which does some toolchain checks and such. You can choose which targets to build here.\n#NOTE: the following line will download third-party packages.\nchroot $FTEBUILD su $CHUSER -c \"cd ~/quake/fteqw-code && ./build_setup.sh --noupdate\"\n#And finally the main rebuild thing. drop the --noupdate part if you want to build the latest-available revision.\nchroot $FTEBUILD su $CHUSER -c \"cd ~/quake/fteqw-code && ./build_wip.sh --noupdate $THREADS\"\n\n\n#to remove your chroot afterwards:\n#rm --one-file-system -rf $FTEBUILD\n"
  },
  {
    "path": "fteqtv/LICENSE",
    "content": "GNU GENERAL PUBLIC LICENSE\nVersion 2, June 1991 \n\nCopyright (C) 1989, 1991 Free Software Foundation, Inc.  \n59 Temple Place - Suite 330, Boston, MA  02111-1307, USA\n\nEveryone is permitted to copy and distribute verbatim copies\nof this license document, but changing it is not allowed.\nPreamble\nThe licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. \n\nWhen we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. \n\nTo protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. \n\nFor example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. \n\nWe protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. \n\nAlso, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. \n\nFinally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. \n\nThe precise terms and conditions for copying, distribution and modification follow. \n\nTERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION\n0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The \"Program\", below, refers to any such program or work, and a \"work based on the Program\" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term \"modification\".) Each licensee is addressed as \"you\". \n\nActivities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. \n\n1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. \n\nYou may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. \n\n2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: \n\n\na) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. \n\nb) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. \n\nc) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this 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.) \nThese 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 themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. \nThus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. \n\nIn addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. \n\n3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: \n\na) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, \n\nb) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, \n\nc) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) \nThe source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. \nIf distribution of executable or object code is made by offering 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. \n\n4. 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 void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. \n\n5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. \n\n6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. \n\n7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. \n\nIf any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. \n\nIt is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. \n\nThis section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. \n\n8. 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 may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. \n\n9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. \n\nEach version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and \"any later version\", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. \n\n10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. \n\nNO WARRANTY\n\n11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. \n\n12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. \n\n\nEND OF TERMS AND CONDITIONS\n"
  },
  {
    "path": "fteqtv/Makefile",
    "content": "CC=$(TOOLPREFIX)gcc\nAR=$(TOOLPREFIX)ar\nRANLIB=$(TOOLPREFIX)ranlib\nSTRIP=$(TOOLPREFIX)strip\n\nSPIKEISALAZYBUGGERCFLAGS=-Wall -O2\nSTRIPFLAGS=--strip-unneeded --remove-section=.comment\n\n# 'cause Microsoft suck.\nCLIBNAME = c\n\nVPATH=../engine/common\n\nOBJS = netchan.o parse.o msg.o qw.o source.o bsp.o rcon.o mdfour.o crc.o control.o forward.o pmove.o menu.o httpsv.o sha1.o\n\n#I hate this big long list.\nINCLUDES=-I../engine/common -I../engine/client -I../engine/server -I../engine/qclib -I../engine/gl\nREVISION:=-DSVNREVISION=$(shell svnversion)\n\nall: qtv\n\n%.o: %.c\n\t$(CC) $(SPIKEISALAZYBUGGERCFLAGS) $(CFLAGS) $(INCLUDES) $(REVISION) $< -c -o $@\n\nlibqtvc:\n\t$(MAKE) -C libqtvc CC=\"$(CC)\" CFLAGS=\"$(CFLAGS) $(SPIKEISALAZYBUGGERCFLAGS)\" AR=\"$(AR)\" RANLIB=\"$(RANLIB)\"\n\nlibqtvc/libqtvc.a: libqtvc\n\nqtv: libqtvc/libqtvc.a $(OBJS) qtv.h\n\t$(CC) $(SPIKEISALAZYBUGGERCFLAGS) $(CFLAGS) $(OBJS) $(LDFLAGS) -o $@.db -lm -l$(CLIBNAME) -Llibqtvc -lqtvc\n\t$(STRIP) $(STRIPFLAGS) $@.db -o $@\n\nqtv.exe: *.c *.h\n\t$(MAKE) qtv CFLAGS=-mno-cygwin LDFLAGS=\"-lwsock32 -lwinmm\" CLIBNAME=msvcrt\n\tmv qtv qtv.exe\n\nclean:\n\trm -rf qtv qtv.exe qtv.db *.o\n\t$(MAKE) -C libqtvc clean\n\n.PHONY: libqtvc\n\n"
  },
  {
    "path": "fteqtv/bsd_string.h",
    "content": "#include <sys/types.h>\n#include <string.h>\n\nsize_t strlcpy(char *dst, const char *src, size_t siz);\n\n"
  },
  {
    "path": "fteqtv/bsp.c",
    "content": "/*\nCopyright (C) 1996-1997 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  \n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n*/\n\n#include \"qtv.h\"\n\n#define\tMAX_MAP_LEAFS\t\t32767\n\ntypedef struct {\n\tfloat planen[3];\n\tfloat planedist;\n\tint child[2];\n} node_t;\nstruct bsp_s {\n\tunsigned int fullchecksum;\n\tunsigned int visedchecksum;\n\tnode_t *nodes;\n\tunsigned char *pvslump;\n\tunsigned char **pvsofs;\n\n\tunsigned char decpvs[(MAX_MAP_LEAFS+7)/8];\t//decompressed pvs\n\tint pvsbytecount;\n\n\n\n\tint numintermissionspots;\n\tintermission_t intermissionspot[8];\n};\n\nstatic const intermission_t nullintermissionspot = {{0}};\n\n\ntypedef struct\n{\n\tint\t\t\tcontents;\n\tint\t\t\tvisofs;\t\t\t\t// -1 = no visibility info\n\n\tshort\t\tmins[3];\t\t\t// for frustum culling\n\tshort\t\tmaxs[3];\n\n\tunsigned short\t\tfirstmarksurface;\n\tunsigned short\t\tnummarksurfaces;\n#define NUM_AMBIENTS 4\n\tunsigned char\t\tambient_level[NUM_AMBIENTS];\n} dleaf_t;\ntypedef struct\n{\n\tunsigned int\t\t\tplanenum;\n\tshort\t\tchildren[2];\t// negative numbers are -(leafs+1), not nodes\n\tshort\t\tmins[3];\t\t// for sphere culling\n\tshort\t\tmaxs[3];\n\tunsigned short\tfirstface;\n\tunsigned short\tnumfaces;\t// counting both sides\n} dnode_t;\ntypedef struct\n{\n\tfloat\tnormal[3];\n\tfloat\tdist;\n\tunsigned int\t\ttype;\t\t// PLANE_X - PLANE_ANYZ ?remove? trivial to regenerate\n} dplane_t;\ntypedef struct\n{\n\tint\t\tfileofs, filelen;\n} lump_t;\n#define LUMP_ENTITIES\t\t0\n#define\tLUMP_PLANES\t\t1\n#define\tLUMP_VISIBILITY\t4\n#define\tLUMP_NODES\t\t5\n#define\tLUMP_LEAFS\t\t10\n#define\tHEADER_LUMPS\t15\ntypedef struct\n{\n\tint\t\t\tversion;\t\n\tlump_t\t\tlumps[HEADER_LUMPS];\n} dheader_t;\n\n\n\nvoid DecompressVis(unsigned char *in, unsigned char *out, int bytecount)\n{\n\tint c;\n\tunsigned char *end;\n\n\tfor (end = out + bytecount; out < end; )\n\t{\n\t\tc = *in;\n\t\tif (!c)\n\t\t{\t//a 0 is always followed by the count of 0s.\n\t\t\tc = in[1];\n\t\t\tin += 2;\n\t\t\tfor (; c > 4; c-=4)\n\t\t\t{\n\t\t\t\t*(unsigned int*)out = 0;\n\t\t\t\tout+=4;\n\t\t\t}\n\t\t\tfor (; c; c--)\n\t\t\t\t*out++ = 0;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tin++;\n\t\t\t*out++ = c;\n\t\t}\n\t}\n}\n\nvoid BSP_LoadEntities(bsp_t *bsp, char *entitydata)\n{\n\tchar *v;\n\tchar key[2048];\n\tchar value[2048];\n\n\tenum {et_random, et_startspot, et_primarystart, et_intermission} etype;\n\n\tfloat org[3];\n\tfloat angles[3];\n\n\tqboolean foundstartspot = false;\n\tfloat startspotorg[3];\n\tfloat startspotangles[3];\n\n\t//char *COM_ParseToken (char *data, char *out, int outsize, const char *punctuation)\n\twhile (entitydata)\n\t{\n\t\tentitydata = COM_ParseToken(entitydata, key, sizeof(key), NULL);\n\t\tif (!entitydata)\n\t\t\tbreak;\n\n\t\tif (!strcmp(key, \"{\"))\n\t\t{\n\t\t\torg[0] = 0;\n\t\t\torg[1] = 0;\n\t\t\torg[2] = 0;\n\n\t\t\tangles[0] = 0;\n\t\t\tangles[1] = 0;\n\t\t\tangles[2] = 0;\n\t\t\tetype = et_random;\n\n\t\t\tfor(;;)\n\t\t\t{\n\n\t\t\t\tif(!entitydata)\n\t\t\t\t{\n\t\t\t\t\tprintf(\"unexpected eof in bsp entities section\\n\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tentitydata = COM_ParseToken(entitydata, key, sizeof(key), NULL);\n\t\t\t\tif (!strcmp(key, \"}\"))\n\t\t\t\t\tbreak;\n\t\t\t\t\n\t\t\t\tentitydata = COM_ParseToken(entitydata, value, sizeof(value), NULL);\n\n\t\t\t\tif (!strcmp(key, \"origin\"))\n\t\t\t\t{\n\t\t\t\t\tv = value;\n\t\t\t\t\tv = COM_ParseToken(v, key, sizeof(key), NULL);\n\t\t\t\t\torg[0] = atof(key);\n\t\t\t\t\tv = COM_ParseToken(v, key, sizeof(key), NULL);\n\t\t\t\t\torg[1] = atof(key);\n\t\t\t\t\tv = COM_ParseToken(v, key, sizeof(key), NULL);\n\t\t\t\t\torg[2] = atof(key);\n\t\t\t\t}\n\n\t\t\t\tif (!strcmp(key, \"angles\") || !strcmp(key, \"angle\") || !strcmp(key, \"mangle\"))\n\t\t\t\t{\n\t\t\t\t\tv = value;\n\t\t\t\t\tv = COM_ParseToken(v, key, sizeof(key), NULL);\n\t\t\t\t\tangles[0] = atof(key);\n\t\t\t\t\tv = COM_ParseToken(v, key, sizeof(key), NULL);\n\t\t\t\t\tif (v)\n\t\t\t\t\t{\n\t\t\t\t\t\tangles[1] = atof(key);\n\t\t\t\t\t\tv = COM_ParseToken(v, key, sizeof(key), NULL);\n\t\t\t\t\t\tangles[2] = atof(key);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tangles[1] = angles[0];\n\t\t\t\t\t\tangles[0] = 0;\n\t\t\t\t\t\tangles[2] = 0;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\n\t\t\t\tif (!strcmp(key, \"classname\"))\n\t\t\t\t{\n\t\t\t\t\tif (!strcmp(value, \"info_player_start\"))\n\t\t\t\t\t\tetype = et_primarystart;\n\t\t\t\t\tif (!strcmp(value, \"info_deathmatch_start\"))\n\t\t\t\t\t\tetype = et_startspot;\n\t\t\t\t\tif (!strcmp(value, \"info_intermission\"))\n\t\t\t\t\t\tetype = et_intermission;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tswitch (etype)\n\t\t\t{\n\t\t\tcase et_random: //a random (unknown) entity\n\t\t\t\tbreak;\n\t\t\tcase et_primarystart:\t//a single player start\n\t\t\t\tmemcpy(startspotorg, org, sizeof(startspotorg));\n\t\t\t\tmemcpy(startspotangles, angles, sizeof(startspotangles));\n\t\t\t\tfoundstartspot = true;\n\t\t\t\tbreak;\n\t\t\tcase et_startspot:\n\t\t\t\tif (!foundstartspot)\n\t\t\t\t{\n\t\t\t\t\tmemcpy(startspotorg, org, sizeof(startspotorg));\n\t\t\t\t\tmemcpy(startspotangles, angles, sizeof(startspotangles));\n\t\t\t\t\tfoundstartspot = true;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase et_intermission:\n\t\t\t\tif (bsp->numintermissionspots < sizeof(bsp->intermissionspot)/sizeof(bsp->intermissionspot[0]))\n\t\t\t\t{\n\t\t\t\t\tbsp->intermissionspot[bsp->numintermissionspots].pos[0] = org[0];\n\t\t\t\t\tbsp->intermissionspot[bsp->numintermissionspots].pos[1] = org[1];\n\t\t\t\t\tbsp->intermissionspot[bsp->numintermissionspots].pos[2] = org[2];\n\n\t\t\t\t\tbsp->intermissionspot[bsp->numintermissionspots].angle[0] = angles[0];\n\t\t\t\t\tbsp->intermissionspot[bsp->numintermissionspots].angle[1] = angles[1];\n\t\t\t\t\tbsp->intermissionspot[bsp->numintermissionspots].angle[2] = angles[2];\n\t\t\t\t\tbsp->numintermissionspots++;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tprintf(\"data not expected here\\n\");\n\t\t\treturn;\n\t\t}\n\t}\n\n\tif (foundstartspot && !bsp->numintermissionspots)\n\t{\n\t\tbsp->intermissionspot[bsp->numintermissionspots].pos[0] = startspotorg[0];\n\t\tbsp->intermissionspot[bsp->numintermissionspots].pos[1] = startspotorg[1];\n\t\tbsp->intermissionspot[bsp->numintermissionspots].pos[2] = startspotorg[2];\n\n\t\tbsp->intermissionspot[bsp->numintermissionspots].angle[0] = startspotangles[0];\n\t\tbsp->intermissionspot[bsp->numintermissionspots].angle[1] = startspotangles[1];\n\t\tbsp->intermissionspot[bsp->numintermissionspots].angle[2] = startspotangles[2];\n\t\tbsp->numintermissionspots++;\n\t}\n}\n\nbsp_t *BSP_LoadModel(cluster_t *cluster, char *gamedir, char *bspname)\n{\n\tunsigned char *data;\n\tunsigned int size;\n\tchar *entdata;\n\n\tdheader_t *header;\n\tdplane_t *planes;\n\tdnode_t *nodes;\n\tdleaf_t *leaf;\n\n\tint numnodes, i;\n\tint numleafs;\n\tunsigned int chksum;\n\n\tbsp_t *bsp;\n\n\tdata = FS_ReadFile(gamedir, bspname, &size);\n\tif (!data)\n\t{\n\t\tSys_Printf(cluster, \"Couldn't open bsp file \\\"%s\\\" (gamedir \\\"%s\\\")\\n\", bspname, gamedir);\n\t\treturn NULL;\n\t}\n\n\n\theader = (dheader_t*)data;\n\tif (size < sizeof(dheader_t) || data[0] != 29)\n\t{\n\t\tfree(data);\n\t\tSys_Printf(cluster, \"BSP not version 29 (%s in %s)\\n\", bspname, gamedir);\n\t\treturn NULL;\n\t}\n\n\tfor (i = 0; i < HEADER_LUMPS; i++)\n\t{\n\t\tif (LittleLong(header->lumps[i].fileofs) + LittleLong(header->lumps[i].filelen) > size)\n\t\t{\n\t\t\tfree(data);\n\t\t\tSys_Printf(cluster, \"BSP appears truncated (%s in gamedir %s)\\n\", bspname, gamedir);\n\t\t\treturn NULL;\n\t\t}\n\t}\n\n\tplanes = (dplane_t*)(data+LittleLong(header->lumps[LUMP_PLANES].fileofs));\n\tnodes = (dnode_t*)(data+LittleLong(header->lumps[LUMP_NODES].fileofs));\n\tleaf = (dleaf_t*)(data+LittleLong(header->lumps[LUMP_LEAFS].fileofs));\n\n\tentdata = (char*)(data+LittleLong(header->lumps[LUMP_ENTITIES].fileofs));\n\n\tnumnodes = LittleLong(header->lumps[LUMP_NODES].filelen)/sizeof(dnode_t);\n\tnumleafs = LittleLong(header->lumps[LUMP_LEAFS].filelen)/sizeof(dleaf_t);\n\n\tbsp = malloc(sizeof(bsp_t) + sizeof(node_t)*numnodes + LittleLong(header->lumps[LUMP_VISIBILITY].filelen) + sizeof(unsigned char *)*numleafs);\n\tbsp->numintermissionspots = 0;\n\tif (bsp)\n\t{\n\t\tbsp->fullchecksum = 0;\n\t\tbsp->visedchecksum = 0;\n\t\tfor (i = 0; i < HEADER_LUMPS; i++)\n\t\t{\n\t\t\tif (i == LUMP_ENTITIES)\n\t\t\t\tcontinue;\t//entities never appear in any checksums\n\n\t\t\tchksum = Com_BlockChecksum(data + LittleLong(header->lumps[i].fileofs), LittleLong(header->lumps[i].filelen));\n\t\t\tbsp->fullchecksum ^= chksum;\n\t\t\tif (i == LUMP_VISIBILITY || i == LUMP_LEAFS || i == LUMP_NODES)\n\t\t\t\tcontinue;\n\t\t\tbsp->visedchecksum ^= chksum;\n\t\t}\n\t\tbsp->nodes = (node_t*)(bsp+1);\n\t\tbsp->pvsofs = (unsigned char**)(bsp->nodes+numnodes);\n\t\tbsp->pvslump = (unsigned char*)(bsp->pvsofs+numleafs);\n\n\t\tbsp->pvsbytecount = (numleafs+7)/8;\n\n\t\tfor (i = 0; i < numnodes; i++)\n\t\t{\n\t\t\tbsp->nodes[i].child[0] = LittleShort(nodes[i].children[0]);\n\t\t\tbsp->nodes[i].child[1] = LittleShort(nodes[i].children[1]);\n\t\t\tbsp->nodes[i].planedist = planes[LittleLong(nodes[i].planenum)].dist;\n\t\t\tbsp->nodes[i].planen[0] = planes[LittleLong(nodes[i].planenum)].normal[0];\n\t\t\tbsp->nodes[i].planen[1] = planes[LittleLong(nodes[i].planenum)].normal[1];\n\t\t\tbsp->nodes[i].planen[2] = planes[LittleLong(nodes[i].planenum)].normal[2];\n\t\t}\n\t\tmemcpy(bsp->pvslump, data+LittleLong(header->lumps[LUMP_VISIBILITY].fileofs), LittleLong(header->lumps[LUMP_VISIBILITY].filelen));\n\n\t\tfor (i = 0; i < numleafs; i++)\n\t\t{\n\t\t\tif (leaf[i].visofs < 0)\n\t\t\t\tbsp->pvsofs[i] = NULL;\n\t\t\telse\n\t\t\t\tbsp->pvsofs[i] = bsp->pvslump+leaf[i].visofs;\n\t\t}\n\t}\n\n\tBSP_LoadEntities(bsp, entdata);\n\n\tfree(data);\n\n\treturn bsp;\n}\n\nvoid BSP_Free(bsp_t *bsp)\n{\n\tfree(bsp);\n}\n\nint BSP_SphereLeafNums_r(bsp_t *bsp, int first, int maxleafs, unsigned short *list, float *pos, float radius)\n{\n\tnode_t *node;\n\tfloat dot;\n\tint rn;\n\tint numleafs = 0;\n\n\tif (!bsp)\n\t\treturn 0;\n\n\tfor(rn = first;rn >= 0;)\n\t{\n\t\tnode = &bsp->nodes[rn];\n\t\tdot = (node->planen[0]*pos[0] + node->planen[1]*pos[1] + node->planen[2]*pos[2]) - node->planedist;\n\t\tif (dot < -radius)\n\t\t\trn = node->child[1];\n\t\telse if (dot > radius)\n\t\t\trn = node->child[0];\n\t\telse\n\t\t{\n\t\t\trn = BSP_SphereLeafNums_r(bsp, node->child[0], maxleafs-numleafs, list+numleafs, pos, radius);\n\t\t\tif (rn < 0)\n\t\t\t\treturn -1;\t//ran out, so don't use pvs for this entity.\n\t\t\telse\n\t\t\t\tnumleafs += rn;\n\t\t\trn = node->child[1];\t//both sides\n\t\t}\n\t}\n\n\trn = -1-rn;\n\n\tif (rn <= 0)\n\t\t;\t//leaf 0 has no pvs info, so don't add it.\n\telse if (maxleafs>numleafs)\n\t{\n\t\tlist[numleafs] = rn-1;\n\t\tnumleafs++;\n\t}\n\telse\n\t\treturn -1;\t//there are just too many\n\n\treturn numleafs;\n}\n\nunsigned int BSP_Checksum(bsp_t *bsp)\n{\n\tif (!bsp)\n\t\treturn 0;\n\treturn bsp->visedchecksum;\n}\n\nint BSP_SphereLeafNums(bsp_t *bsp, int maxleafs, unsigned short *list, float x, float y, float z, float radius)\n{\n\tfloat pos[3];\n\tpos[0] = x;\n\tpos[1] = y;\n\tpos[2] = z;\n\treturn BSP_SphereLeafNums_r(bsp, 0, maxleafs, list, pos, radius);\n}\n\nint BSP_LeafNum(bsp_t *bsp, float x, float y, float z)\n{\n\tnode_t *node;\n\tfloat dot;\n\tint rn;\n\n\tif (!bsp)\n\t\treturn 0;\n\n\tfor(rn = 0;rn >= 0;)\n\t{\n\t\tnode = &bsp->nodes[rn];\n\t\tdot = node->planen[0]*x + node->planen[1]*y + node->planen[2]*z;\n\t\trn = node->child[(dot-node->planedist) <= 0];\n\t}\n\n\treturn -1-rn;\n}\n\nqboolean BSP_Visible(bsp_t *bsp, int leafcount, unsigned short *list)\n{\n\tint i;\n\tif (!bsp)\n\t\treturn true;\n\n\tif (leafcount < 0)\t//too many, so pvs was switched off.\n\t\treturn true;\n\n\tfor (i = 0; i < leafcount; i++)\n\t{\n\t\tif (bsp->decpvs[list[i]>>3] & (1<<(list[i]&7)))\n\t\t\treturn true;\n\t}\n\treturn false;\n}\n\nvoid BSP_SetupForPosition(bsp_t *bsp, float x, float y, float z)\n{\n\tint leafnum;\n\tif (!bsp)\n\t\treturn;\n\n\tleafnum = BSP_LeafNum(bsp, x, y, z);\n\tDecompressVis(bsp->pvsofs[leafnum], bsp->decpvs, bsp->pvsbytecount);\n}\n\nconst intermission_t *BSP_IntermissionSpot(bsp_t *bsp)\n{\n\tint spotnum;\n\tif (bsp)\n\t{\n\t\tif (bsp->numintermissionspots>0)\n\t\t{\n\t\t\tspotnum = rand()%bsp->numintermissionspots;\n\t\t\treturn &bsp->intermissionspot[spotnum];\n\t\t}\n\t}\n\treturn &nullintermissionspot;\n}\n\n"
  },
  {
    "path": "fteqtv/cmd.h",
    "content": "#define MAX_ARGS 8\n#define ARG_LEN 512\n\ntypedef struct cmdctxt_s cmdctxt_t;\nstruct cmdctxt_s {\n\tcluster_t *cluster;\n\tsv_t *qtv;\n\tint streamid;\t//streamid, which is valid even if qtv is not, for specifying the streamid to use on connects\n\tchar *arg[MAX_ARGS];\n\tint argc;\n\tvoid (*printfunc)(cmdctxt_t *ctx, char *str);\n\tvoid *printcookie;\n\tint printcookiesize;\t//tis easier\n\tqboolean localcommand;\n};\n\ntypedef void (*consolecommand_t) (cmdctxt_t *ctx);\n\nvoid Cmd_Printf(cmdctxt_t *ctx, char *fmt, ...) PRINTFWARNING(2);\n#define Cmd_Argc(ctx) ctx->argc\n#define Cmd_Argv(ctx, num) (((unsigned int)ctx->argc <= (unsigned int)(num))?\"\": ctx->arg[num])\n#define Cmd_IsLocal(ctx) ctx->localcommand\n\n\nvoid Cmd_ExecuteNow(cmdctxt_t *ctx, char *command);\nchar *Rcon_Command(cluster_t *cluster, sv_t *source, char *command, char *resultbuffer, int resultbuffersize, int islocalcommand);//prints the command prints to an internal buffer\n"
  },
  {
    "path": "fteqtv/control.c",
    "content": "/*\r\nContains the control routines that handle both incoming and outgoing stuff\r\n*/\r\n\r\n#include \"qtv.h\"\r\n#include <signal.h>\r\n#include \"bsd_string.h\"\r\n\r\n#ifndef _WIN32\r\n#include <sys/stat.h>\r\n#include <dirent.h>\r\n#else\r\n#include <direct.h>\r\n#endif\r\n\r\ntypedef struct {\r\n\tchar name[56];\r\n\tint offset;\r\n\tint length;\r\n} pakfile;\r\n// PACK, offset, lengthofpakfiles\r\nFILE *FindInPaks(char *gamedir, char *filename, int *size)\r\n{\r\n\tFILE *f;\r\n\tchar fname[1024];\r\n\tint i, j;\r\n\tint numfiles;\r\n\tunsigned int header[3];\r\n\r\n\tpakfile pf;\r\n\r\n\tfor (i = 0; ; i++)\r\n\t{\r\n\t\tsprintf(fname, \"%s/pak%i.pak\", gamedir, i);\r\n\t\tf = fopen(fname, \"rb\");\r\n\t\tif (!f)\r\n\t\t\treturn NULL;\t//ran out of possible pak files.\r\n\r\n\t\tfread(header, 1, sizeof(header), f);\r\n\t\tif (header[0] != *(unsigned int*)\"PACK\")\r\n\t\t{\t//err... hmm.\r\n\t\t\tfclose(f);\r\n\t\t\tcontinue;\r\n\t\t}\r\n\t\tnumfiles = LittleLong(header[2])/sizeof(pakfile);\r\n\t\tfseek(f, LittleLong(header[1]), SEEK_SET);\r\n\t\tfor (j = 0; j < numfiles; j++)\r\n\t\t{\r\n\t\t\tfread(&pf, 1, sizeof(pf), f);\r\n\t\t\tif (!strcmp(pf.name, filename))\r\n\t\t\t{\r\n\t\t\t\tfseek(f, LittleLong(pf.offset), 0);\r\n\t\t\t\tif (size)\r\n\t\t\t\t\t*size = LittleLong(pf.length);\r\n\t\t\t\treturn f;\r\n\t\t\t}\r\n\t\t}\r\n\t\tfclose(f);\r\n\t\t//not found\r\n\t}\r\n\treturn NULL;\r\n}\r\n\r\nunsigned char *FS_ReadFile2(char *gamedir, char *filename, unsigned int *sizep)\r\n{\r\n\tint size;\r\n\tunsigned char *data;\r\n\r\n\tFILE *f;\r\n\tchar fname[1024];\r\n\r\n\tif (!*filename)\r\n\t\treturn NULL;\r\n\r\n\t//try and read it straight out of the file system\r\n\tsprintf(fname, \"%s/%s\", gamedir, filename);\r\n\tf = fopen(fname, \"rb\");\r\n\tif (!f)\r\n\t\tf = fopen(filename, \"rb\");\t//see if we're being run from inside the gamedir\r\n\tif (!f)\r\n\t{\r\n\t\tf = FindInPaks(gamedir, filename, &size);\r\n\t\tif (!f)\r\n\t\t\tf = FindInPaks(\"id1\", filename, &size);\r\n\t\tif (!f)\r\n\t\t{\r\n\t\t\treturn NULL;\r\n\t\t}\r\n\t}\r\n\telse\r\n\t{\r\n\t\tfseek(f, 0, SEEK_END);\r\n\t\tsize = ftell(f);\r\n\t\tfseek(f, 0, SEEK_SET);\r\n\t}\r\n\tdata = malloc(size);\r\n\tif (data)\r\n\t\tfread(data, 1, size, f);\r\n\tfclose(f);\r\n\r\n\tif (sizep)\r\n\t\t*sizep = size;\r\n\treturn data;\r\n}\r\n\r\nunsigned char *FS_ReadFile(char *gamedir, char *filename, unsigned int *size)\r\n{\r\n\tunsigned char *data;\r\n\tif (!gamedir || !*gamedir || !strcmp(gamedir, \"qw\"))\r\n\t\tdata = NULL;\r\n\telse\r\n\t\tdata = FS_ReadFile2(gamedir, filename, size);\r\n\tif (!data)\r\n\t{\r\n\t\tdata = FS_ReadFile2(\"qw\", filename, size);\r\n\t\tif (!data)\r\n\t\t{\r\n\t\t\tdata = FS_ReadFile2(\"id1\", filename, size);\r\n\t\t\tif (!data)\r\n\t\t\t{\r\n\t\t\t\treturn NULL;\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\treturn data;\r\n}\r\n\r\n#ifndef _WIN32\r\n#define _cdecl\r\n#endif\r\nint _cdecl SortFilesByDate(const void *a, const void *b)\r\n{\r\n\tif (((const availdemo_t*)a)->time < ((const availdemo_t*)b)->time)\r\n\t\treturn 1;\r\n\tif (((const availdemo_t*)a)->time > ((const availdemo_t*)b)->time)\r\n\t\treturn -1;\r\n\r\n\tif (((const availdemo_t*)a)->smalltime < ((const availdemo_t*)b)->smalltime)\r\n\t\treturn 1;\r\n\tif (((const availdemo_t*)a)->smalltime > ((const availdemo_t*)b)->smalltime)\r\n\t\treturn -1;\r\n\treturn 0;\r\n}\r\n\r\nvoid Cluster_BuildAvailableDemoList(cluster_t *cluster)\r\n{\r\n\tcluster->availdemoscount = 0;\r\n\r\n#ifdef _WIN32\r\n\t{\r\n\t\tWIN32_FIND_DATA ffd;\r\n\t\tHANDLE h;\r\n\t\tchar path[512];\r\n\t\tsnprintf(path, sizeof(path), \"%s*.mvd\", cluster->demodir);\r\n\t\th = FindFirstFile(path, &ffd);\r\n\t\tif (h != INVALID_HANDLE_VALUE)\r\n\t\t{\r\n\t\t\tdo\r\n\t\t\t{\r\n\t\t\t\tif (cluster->availdemoscount == sizeof(cluster->availdemos)/sizeof(cluster->availdemos[0]))\r\n\t\t\t\t\tbreak;\r\n\t\t\t\tstrlcpy(cluster->availdemos[cluster->availdemoscount].name, ffd.cFileName, sizeof(cluster->availdemos[0].name));\r\n\t\t\t\tcluster->availdemos[cluster->availdemoscount].size = ffd.nFileSizeLow;\r\n\t\t\t\tcluster->availdemos[cluster->availdemoscount].time = ffd.ftLastWriteTime.dwHighDateTime;\r\n\t\t\t\tcluster->availdemos[cluster->availdemoscount].smalltime = ffd.ftLastWriteTime.dwLowDateTime;\r\n\t\t\t\tcluster->availdemoscount++;\r\n\t\t\t} while(FindNextFile(h, &ffd));\r\n\t\t\tFindClose(h);\r\n\t\t}\r\n\t}\r\n#else\r\n\t{\r\n\t\tDIR *dir;\r\n\t\tstruct dirent *ent;\r\n\t\tstruct stat sb;\r\n\t\tchar fullname[512];\r\n\t\tdir = opendir(cluster->demodir);\t//yeek!\r\n\t\tif (dir)\r\n\t\t{\r\n\t\t\tfor(;;)\r\n\t\t\t{\r\n\t\t\t\tif (cluster->availdemoscount == sizeof(cluster->availdemos)/sizeof(cluster->availdemos[0]))\r\n\t\t\t\t\tbreak;\r\n\t\t\t\tent = readdir(dir);\r\n\t\t\t\tif (!ent)\r\n\t\t\t\t\tbreak;\r\n\t\t\t\tif (*ent->d_name == '.')\r\n\t\t\t\t\tcontinue;\t//ignore 'hidden' files\r\n\t\t\t\tsnprintf(fullname, sizeof(fullname), \"%s%s\", cluster->demodir, ent->d_name);\r\n\t\t\t\tif (stat(fullname, &sb))\r\n\t\t\t\t\tcontinue;\t//some kind of error\r\n\t\t\t\tstrlcpy(cluster->availdemos[cluster->availdemoscount].name, ent->d_name, sizeof(cluster->availdemos[0].name));\r\n\t\t\t\tcluster->availdemos[cluster->availdemoscount].size = sb.st_size;\r\n\t\t\t\tcluster->availdemos[cluster->availdemoscount].time = sb.st_mtime;\r\n\t\t\t\tcluster->availdemoscount++;\r\n\t\t\t}\r\n\t\t\tclosedir(dir);\r\n\t\t}\r\n\t\telse\r\n\t\t\tSys_Printf(cluster, \"Couldn't open dir %s for demo listings\\n\", cluster->demodir);\r\n\t}\r\n#endif\r\n\r\n\tqsort(cluster->availdemos, cluster->availdemoscount, sizeof(cluster->availdemos[0]), SortFilesByDate);\r\n}\r\n\r\nvoid Cluster_Run(cluster_t *cluster, qboolean dowait)\r\n{\r\n\toproxy_t *pend, *pend2, *pend3;\r\n\tsv_t *sv, *old;\r\n\ttcpconnect_t *tc;\r\n\r\n\tint m;\r\n\tstruct timeval timeout;\r\n\tfd_set socketset;\r\n\tfd_set socketset_wr;\r\n\r\n\tif (dowait)\r\n\t{\r\n\t\t//FIXME: use poll or epoll to work around FD_SETSIZE limits, though we're mostly only doing this for the sleeping.\r\n\r\n\t\tFD_ZERO(&socketset);\r\n\t\tFD_ZERO(&socketset_wr);\r\n\t\tm = 0;\r\n\t\tif (cluster->qwdsocket[0] != INVALID_SOCKET)\r\n\t\t{\r\n\t\t\tif (cluster->qwdsocket[0] < FD_SETSIZE)\r\n\t\t\t{\r\n\t\t\t\tFD_SET(cluster->qwdsocket[0], &socketset);\r\n\t\t\t\tif (cluster->qwdsocket[0] >= m)\r\n\t\t\t\t\tm = cluster->qwdsocket[0]+1;\r\n\t\t\t}\r\n\t\t}\r\n\t\tif (cluster->qwdsocket[1] != INVALID_SOCKET)\r\n\t\t{\r\n\t\t\tif (cluster->qwdsocket[1] < FD_SETSIZE)\r\n\t\t\t{\r\n\t\t\t\tFD_SET(cluster->qwdsocket[1], &socketset);\r\n\t\t\t\tif (cluster->qwdsocket[1] >= m)\r\n\t\t\t\t\tm = cluster->qwdsocket[1]+1;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tfor (sv = cluster->servers; sv; sv = sv->next)\r\n\t\t{\r\n\t\t\tif (sv->usequakeworldprotocols && sv->sourcesock != INVALID_SOCKET)\r\n\t\t\t{\r\n\t\t\t\tif (sv->sourcesock >= FD_SETSIZE)\r\n\t\t\t\t\tcontinue;\t//panic...\r\n\t\t\t\tFD_SET(sv->sourcesock, &socketset);\r\n\t\t\t\tif (sv->sourcesock >= m)\r\n\t\t\t\t\tm = sv->sourcesock+1;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tfor (tc = cluster->tcpconnects; tc; tc = tc->next)\r\n\t\t{\r\n\t\t\tif (tc->sock != INVALID_SOCKET && tc->sock < FD_SETSIZE)\r\n\t\t\t{\r\n\t\t\t\tFD_SET(tc->sock, &socketset);\r\n\t\t\t\tif (tc->sock >= m)\r\n\t\t\t\t\tm = tc->sock+1;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tTURN_AddFDs(cluster, &socketset, &m);\r\n\r\n\t\tfor (pend = cluster->pendingproxies; pend; pend = pend->next)\r\n\t\t{\r\n\t\t\tif (pend->sock != INVALID_SOCKET && pend->sock < FD_SETSIZE)\r\n\t\t\t{\r\n\t\t\t\tFD_SET(pend->sock, &socketset);\r\n\t\t\t\tif (pend->file)\t//also wake up if we're doing some (large) file transfer and we can give them a bit more.\r\n\t\t\t\t\tFD_SET(pend->sock, &socketset_wr);\r\n\t\t\t\tif (pend->sock >= m)\r\n\t\t\t\t\tm = pend->sock+1;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t#ifndef _WIN32\r\n\t\t#ifndef STDIN\r\n\t\t\t#define STDIN 0\r\n\t\t#endif\r\n\t\tFD_SET(STDIN, &socketset);\r\n\t\tif (STDIN >= m)\r\n\t\t\tm = STDIN+1;\r\n\t#endif\r\n\r\n\t\tif (cluster->viewserver)\r\n\t\t{\r\n\t\t\ttimeout.tv_sec = 0;\r\n\t\t\ttimeout.tv_usec = 1000;\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\ttimeout.tv_sec = 10/1000;\r\n\t\t\ttimeout.tv_usec = (100%1000)*1000;\r\n\t\t}\r\n\r\n\t\tm = select(m, &socketset, &socketset_wr, NULL, &timeout);\r\n\r\n#ifdef _WIN32\r\n\t\tfor (;;)\r\n\t\t{\r\n\t\t\tchar buffer[8192];\r\n\t\t\tchar *result;\r\n\t\t\tchar c;\r\n\r\n\t\t\tif (!_kbhit())\r\n\t\t\t\tbreak;\r\n\t\t\tc = _getch();\r\n\r\n\t\t\tif (c == '\\n' || c == '\\r')\r\n\t\t\t{\r\n\t\t\t\tSys_Printf(cluster, \"\\n\");\r\n\t\t\t\tif (cluster->inputlength)\r\n\t\t\t\t{\r\n\t\t\t\t\tcluster->commandinput[cluster->inputlength] = '\\0';\r\n\t\t\t\t\tresult = Rcon_Command(cluster, NULL, cluster->commandinput, buffer, sizeof(buffer), true);\r\n\t\t\t\t\tSys_Printf(cluster, \"%s\", result);\r\n\t\t\t\t\tcluster->inputlength = 0;\r\n\t\t\t\t\tcluster->commandinput[0] = '\\0';\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\telse if (c == '\\b')\r\n\t\t\t{\r\n\t\t\t\tif (cluster->inputlength > 0)\r\n\t\t\t\t{\r\n\t\t\t\t\tSys_Printf(cluster, \"%c\", c);\r\n\t\t\t\t\tSys_Printf(cluster, \" \");\r\n\t\t\t\t\tSys_Printf(cluster, \"%c\", c);\r\n\r\n\t\t\t\t\tcluster->inputlength--;\r\n\t\t\t\t\tcluster->commandinput[cluster->inputlength] = '\\0';\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tSys_Printf(cluster, \"%c\", c);\r\n\t\t\t\tif (cluster->inputlength < sizeof(cluster->commandinput)-1)\r\n\t\t\t\t{\r\n\t\t\t\t\tcluster->commandinput[cluster->inputlength++] = c;\r\n\t\t\t\t\tcluster->commandinput[cluster->inputlength] = '\\0';\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n#else\r\n\t\tif (FD_ISSET(STDIN, &socketset))\r\n\t\t{\r\n\t\t\tchar buffer[8192];\r\n\t\t\tchar *result;\r\n\t\t\tcluster->inputlength = read (STDIN, cluster->commandinput, sizeof(cluster->commandinput));\r\n\t\t\tif (cluster->inputlength >= 1)\r\n\t\t\t{\r\n\t\t\t\tcluster->commandinput[cluster->inputlength-1] = 0;        // rip off the /n and terminate\r\n\t\t\t\tcluster->inputlength--;\r\n\r\n\t\t\t\tif (cluster->inputlength)\r\n\t\t\t\t{\r\n\t\t\t\t\tcluster->commandinput[cluster->inputlength] = '\\0';\r\n\t\t\t\t\tresult = Rcon_Command(cluster, NULL, cluster->commandinput, buffer, sizeof(buffer), true);\r\n\t\t\t\t\tprintf(\"%s\", result);\r\n\t\t\t\t\tcluster->inputlength = 0;\r\n\t\t\t\t\tcluster->commandinput[0] = '\\0';\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n#endif\r\n\t}\r\n\r\n\r\n\r\n\tcluster->curtime = Sys_Milliseconds();\r\n\r\n\tfor (sv = cluster->servers; sv; )\r\n\t{\r\n\t\told = sv;\r\n\t\tsv = sv->next;\r\n\t\tQTV_Run(old);\r\n\t}\r\n\r\n\tTURN_CheckFDs(cluster);\r\n\r\n\tSV_FindProxies(cluster->tcpsocket[0], cluster, NULL);\t//look for any other proxies wanting to muscle in on the action.\r\n\tSV_FindProxies(cluster->tcpsocket[1], cluster, NULL);\t//look for any other proxies wanting to muscle in on the action.\r\n\r\n\tQW_UpdateUDPStuff(cluster);\r\n\r\n\twhile(cluster->pendingproxies)\r\n\t{\r\n\t\tpend2 = cluster->pendingproxies->next;\r\n\t\tif (SV_ReadPendingProxy(cluster, cluster->pendingproxies))\r\n\t\t\tcluster->pendingproxies = pend2;\r\n\t\telse\r\n\t\t\tbreak;\r\n\t}\r\n\tif (cluster->pendingproxies)\r\n\t{\r\n\t\tfor(pend = cluster->pendingproxies; pend && pend->next; )\r\n\t\t{\r\n\t\t\tpend2 = pend->next;\r\n\t\t\tpend3 = pend2->next;\r\n\t\t\tif (SV_ReadPendingProxy(cluster, pend2))\r\n\t\t\t{\r\n\t\t\t\tpend->next = pend3;\r\n\t\t\t\tpend = pend3;\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tpend = pend2;\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n}\r\n\r\n\r\n\r\n\r\n\r\nvoid DoCommandLine(cluster_t *cluster, int argc, char **argv)\r\n{\r\n\tint i;\r\n\tchar commandline[8192];\r\n\tchar *result;\r\n\tchar *arg;\r\n\tchar buffer[8192];\r\n\r\n//exec the - commands\r\n\tcommandline[0] = '\\0';\r\n\tfor (i = 1; i <= argc; i++)\r\n\t{\r\n\t\tif (i == argc)\r\n\t\t\targ = \"\";\r\n\t\telse\r\n\t\t{\r\n\t\t\targ = argv[i];\r\n\t\t\tif (!arg)\t//NeXT can do this supposedly\r\n\t\t\t\targ = \"\";\r\n\t\t}\r\n\t\tif(i == argc || *arg == '+' || *arg == '-')\r\n\t\t{\r\n\t\t\tif (commandline[0] == '-')\r\n\t\t\t{\r\n\t\t\t\tresult = Rcon_Command(cluster, NULL, commandline+1, buffer, sizeof(buffer), true);\r\n\t\t\t\tSys_Printf(cluster, \"%s\", result);\r\n\t\t\t}\r\n\r\n\t\t\tcommandline[0] = '\\0';\r\n\t\t}\r\n\t\tstrcat(commandline, arg);\r\n\t\tstrcat(commandline, \" \");\r\n\t}\r\n\r\n//exec the configs\r\n\tresult = Rcon_Command(cluster, NULL, \"exec qtv.cfg\", buffer, sizeof(buffer), true);\r\n\tSys_Printf(cluster, \"%s\", result);\r\n\r\n\r\n//exec the + commands\r\n\tcommandline[0] = '\\0';\r\n\tfor (i = 1; i <= argc; i++)\r\n\t{\r\n\t\tif (i == argc)\r\n\t\t\targ = \"\";\r\n\t\telse\r\n\t\t{\r\n\t\t\targ = argv[i];\r\n\t\t\tif (!arg)\t//NeXT can do this supposedly\r\n\t\t\t\targ = \"\";\r\n\t\t}\r\n\t\tif(i == argc || *arg == '+' || *arg == '-')\r\n\t\t{\r\n\t\t\tif (commandline[0] == '+')\r\n\t\t\t{\r\n\t\t\t\tresult = Rcon_Command(cluster, NULL, commandline+1, buffer, sizeof(buffer), true);\r\n\t\t\t\tSys_Printf(cluster, \"%s\", result);\r\n\t\t\t}\r\n\r\n\t\t\tcommandline[0] = '\\0';\r\n\t\t}\r\n\t\tstrcat(commandline, arg);\r\n\t\tstrcat(commandline, \" \");\r\n\t}\r\n}\r\n\r\n#ifndef LIBQTV\r\nint main(int argc, char **argv)\r\n{\r\n\tcluster_t *cluster;\r\n\r\n//\tsoundtest();\r\n\r\n#ifdef SIGPIPE\r\n\tsignal(SIGPIPE, SIG_IGN);\r\n#endif\r\n\r\n#ifdef _WIN32\r\n\t{\r\n\t\tWSADATA discard;\r\n\t\tWSAStartup(MAKEWORD(1,1), &discard);\r\n\t}\r\n#endif\r\n\r\n\tcluster = malloc(sizeof(*cluster));\r\n\tif (cluster)\r\n\t{\r\n\t\tint j;\r\n\t\tmemset(cluster, 0, sizeof(*cluster));\r\n\r\n\t\tfor (j = 0; j < SOCKETGROUPS; j++)\r\n\t\t{\r\n\t\t\tcluster->qwdsocket[j] = INVALID_SOCKET;\r\n\t\t\tcluster->tcpsocket[j] = INVALID_SOCKET;\r\n\t\t}\r\n\t\tcluster->anticheattime = 1*1000;\r\n\t\tcluster->tooslowdelay = 100;\r\n\t\tcluster->qwlistenportnum = 0;\r\n\t\tcluster->allownqclients = true;\r\n\t\tstrcpy(cluster->hostname, DEFAULT_HOSTNAME);\r\n\t\tcluster->maxproxies = -1;\r\n\r\n\t\t//master protocol setup\r\n\t\tcluster->protocolname = strdup(\"FTE-Quake\");\r\n\t\tcluster->protocolver = 3;\r\n\t\tstrlcpy(cluster->master, \"master.frag-net.com:27950\", sizeof(cluster->master));\t//default to eukara's master server.\r\n\t\tcluster->mastersendtime = cluster->curtime;\r\n\r\n\t\tcluster->relayenabled = true;\t\t//allow qtv\r\n\t\tcluster->pingtreeenabled = false;\t//spammy.\r\n\t\tcluster->turnenabled = false;\t\t//leave turn off by default. we need to know a usable inbound port range, we can't depend on just outgoing ephemerial ones. misconfigured relays will result in failures so don't default this to on.\r\n\r\n#ifdef HAVE_EPOLL\r\n\t\tcluster->epfd = epoll_create1(0);\r\n#endif\r\n\r\n\t\tstrcpy(cluster->demodir, \"qw/demos/\");\r\n\r\n\t\tSys_Printf(cluster, \"QTV \"QTV_VERSION_STRING\"\\n\");\r\n\r\n\t\tDoCommandLine(cluster, argc, argv);\r\n\r\n\t\tif (!cluster->numservers)\r\n\t\t{\t//probably running on a home user's computer\r\n\t\t\tif (cluster->qwdsocket[SG_IPV4] == INVALID_SOCKET && cluster->qwdsocket[SG_IPV6] == INVALID_SOCKET && !cluster->qwlistenportnum)\r\n\t\t\t{\r\n\t\t\t\tcluster->qwlistenportnum = 27599;\r\n\t\t\t\tNET_InitUDPSocket(cluster, cluster->qwlistenportnum, SG_IPV6);\r\n\t\t\t\tNET_InitUDPSocket(cluster, cluster->qwlistenportnum, SG_IPV4);\r\n\t\t\t}\r\n\t\t\tif (cluster->tcpsocket[SG_IPV4] == INVALID_SOCKET && cluster->tcpsocket[SG_IPV6] == INVALID_SOCKET && !cluster->tcplistenportnum)\r\n\t\t\t{\r\n\t\t\t\tcluster->tcplistenportnum = 27599;\r\n\t\t\t\tNet_TCPListen(cluster, cluster->tcplistenportnum, SG_IPV6);\r\n\t\t\t\tNet_TCPListen(cluster, cluster->tcplistenportnum, SG_IPV4);\r\n\t\t\t}\r\n\t\t\tNet_TCPListen(cluster, 1, SG_UNIX);\r\n\r\n\t\t\tSys_Printf(cluster, \"\\n\"\r\n\t\t\t\t\"Welcome to QTV\\n\"\r\n\t\t\t\t\"Please type\\n\"\r\n\t\t\t\t\"qtv server:port\\n\"\r\n\t\t\t\t\" to connect to a tcp server.\\n\"\r\n\t\t\t\t\"qw server:port\\n\"\r\n\t\t\t\t\" to connect to a regular qw server.\\n\"\r\n\t\t\t\t\"demo qw/example.mvd\\n\"\r\n\t\t\t\t\" to play a demo from an mvd.\\n\"\r\n\t\t\t\t\"\\n\");\r\n\t\t}\r\n\r\n//\t\tCluster_BuildAvailableDemoList(cluster);\r\n\r\n\t\twhile (!cluster->wanttoexit)\r\n\t\t{\r\n\t\t\tCluster_Run(cluster, true);\r\n#ifdef VIEWER\r\n\t\t\tDemoViewer_Update(cluster->viewserver);\r\n#endif\r\n\t\t}\r\n\r\n\t\tfree(cluster);\r\n\t}\r\n\r\n\treturn 0;\r\n}\r\n#endif\r\n\r\nvoid QTV_Printf(sv_t *qtv, char *fmt, ...)\r\n{\r\n\tva_list\t\targptr;\r\n\tchar\t\tstring[2048];\r\n\r\n\tva_start (argptr, fmt);\r\n\tvsnprintf (string, sizeof(string)-1, fmt,argptr);\r\n\tstring[sizeof(string)-1] = 0;\r\n\tva_end (argptr);\r\n\r\n\tif (qtv->silentstream)\r\n\t\treturn;\r\n\r\n\tSys_Printf(qtv->cluster, \"%s\", string);\r\n}\r\n\r\n//#ifdef LIBQTV\r\n//#ifndef _WIN32\r\n//#define _cdecl\r\n//#endif\r\n//void _cdecl Con_Printf(char *fmt, ...);\r\n//#endif\r\n\r\nvoid Sys_Printf(cluster_t *cluster, char *fmt, ...)\r\n{\r\n\tva_list\t\targptr;\r\n\tchar\t\tstring[2048];\r\n\tunsigned char *t;\r\n\r\n\tva_start (argptr, fmt);\r\n\tvsnprintf (string, sizeof(string)-1, fmt,argptr);\r\n\tstring[sizeof(string)-1] = 0;\r\n\tva_end (argptr);\r\n\r\n//#ifdef LIBQTV\r\n//\tCon_Printf(\"QTV: %s\", string);\r\n//#endif\r\n\r\n\tfor (t = (unsigned char*)string; *t; t++)\r\n\t{\r\n\t\tif (*t >= 146 && *t < 156)\r\n\t\t\t*t = *t - 146 + '0';\r\n\t\tif (*t == 143)\r\n\t\t\t*t = '.';\r\n\t\tif (*t == 157 || *t == 158 || *t == 159)\r\n\t\t\t*t = '-';\r\n\t\tif (*t >= 128)\r\n\t\t\t*t -= 128;\r\n\t\tif (*t == 16)\r\n\t\t\t*t = '[';\r\n\t\tif (*t == 17)\r\n\t\t\t*t = ']';\r\n\t\tif (*t == 29)\r\n\t\t\t*t = '-';\r\n\t\tif (*t == 30)\r\n\t\t\t*t = '-';\r\n\t\tif (*t == 31)\r\n\t\t\t*t = '-';\r\n\t\tif (*t == '\\a')\t//doh. :D\r\n\t\t\t*t = ' ';\r\n\t}\r\n\r\n\tprintf(\"%s\", string);\r\n}\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n//FIXME: move this to an appropriate place\r\n#ifdef _WIN32\r\nvoid Sys_mkdir(char *name)\r\n{\r\n\t_mkdir(name);\r\n}\r\n#elif defined(__linux__)\r\nvoid Sys_mkdir(char *name)\r\n{\r\n\tmkdir(name, 0777);\r\n}\r\n#else\r\n#warning no Sys_mkdir function defined, hope the default works for you\r\nvoid Sys_mkdir(char *name)\r\n{\r\n\tmkdir(name, 0777);\r\n}\r\n#endif\r\n\r\nvoid QTV_mkdir(char *path)\r\n{\r\n\tchar\t*ofs;\r\n\r\n\tfor (ofs = path+1 ; *ofs ; ofs++)\r\n\t{\r\n\t\tif (*ofs == '/')\r\n\t\t{\t// create the directory\r\n\t\t\t*ofs = 0;\r\n\t\t\tSys_mkdir (path);\r\n\t\t\t*ofs = '/';\r\n\t\t}\r\n\t}\r\n}\r\n\r\n\r\n\r\n/*\r\n\r\n\r\nunsigned char *FS_ReadFile2(char *gamedir, char *filename, unsigned int *sizep)\r\n{\r\n\tint size;\r\n\tunsigned char *data;\r\n\r\n\tFILE *f;\r\n\tchar fname[1024];\r\n\r\n\tif (!*filename)\r\n\t\treturn NULL;\r\n\r\n\t//try and read it straight out of the file system\r\n\tsprintf(fname, \"%s/%s\", gamedir, filename);\r\n\tf = fopen(fname, \"rb\");\r\n\tif (!f)\r\n\t\tf = fopen(filename, \"rb\");\t//see if we're being run from inside the gamedir\r\n\tif (!f)\r\n\t{\r\n\t\tf = FindInPaks(gamedir, filename, &size);\r\n\t\tif (!f)\r\n\t\t\tf = FindInPaks(\"id1\", filename, &size);\r\n\t\tif (!f)\r\n\t\t{\r\n\t\t\treturn NULL;\r\n\t\t}\r\n\t}\r\n\telse\r\n\t{\r\n\t\tfseek(f, 0, SEEK_END);\r\n\t\tsize = ftell(f);\r\n\t\tfseek(f, 0, SEEK_SET);\r\n\t}\r\n\tdata = malloc(size);\r\n\tif (data)\r\n\t\tfread(data, 1, size, f);\r\n\tfclose(f);\r\n\r\n\tif (sizep)\r\n\t\t*sizep = size;\r\n\treturn data;\r\n}\r\n\r\nunsigned char *FS_ReadFile(char *gamedir, char *filename, unsigned int *size)\r\n{\r\n\tchar *data;\r\n\tif (!gamedir || !*gamedir || !strcmp(gamedir, \"qw\"))\r\n\t\tdata = NULL;\r\n\telse\r\n\t\tdata = FS_ReadFile2(gamedir, filename, size);\r\n\tif (!data)\r\n\t{\r\n\t\tdata = FS_ReadFile2(\"qw\", filename, size);\r\n\t\tif (!data)\r\n\t\t{\r\n\t\t\tdata = FS_ReadFile2(\"id1\", filename, size);\r\n\t\t\tif (!data)\r\n\t\t\t{\r\n\t\t\t\treturn NULL;\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\treturn data;\r\n}\r\n\r\nvoid Cluster_Run(cluster_t *cluster, qboolean dowait)\r\n{\r\n\toproxy_t *pend, *pend2, *pend3;\r\n\tsv_t *sv, *old;\r\n\r\n\tint m;\r\n\tstruct timeval timeout;\r\n\tfd_set socketset;\r\n\r\n\tif (dowait)\r\n\t{\r\n\r\n\t\tFD_ZERO(&socketset);\r\n\t\tm = 0;\r\n\t\tif (cluster->qwdsocket != INVALID_SOCKET)\r\n\t\t{\r\n\t\t\tFD_SET(cluster->qwdsocket, &socketset);\r\n\t\t\tif (cluster->qwdsocket >= m)\r\n\t\t\t\tm = cluster->qwdsocket+1;\r\n\t\t}\r\n\r\n\t\tfor (sv = cluster->servers; sv; sv = sv->next)\r\n\t\t{\r\n\t\t\tif (sv->usequkeworldprotocols && sv->sourcesock != INVALID_SOCKET)\r\n\t\t\t{\r\n\t\t\t\tFD_SET(sv->sourcesock, &socketset);\r\n\t\t\t\tif (sv->sourcesock >= m)\r\n\t\t\t\t\tm = sv->sourcesock+1;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t#ifndef _WIN32\r\n\t\t#ifndef STDIN\r\n\t\t\t#define STDIN 0\r\n\t\t#endif\r\n\t\tFD_SET(STDIN, &socketset);\r\n\t\tif (STDIN >= m)\r\n\t\t\tm = STDIN+1;\r\n\t#endif\r\n\r\n\t\tif (cluster->viewserver)\r\n\t\t{\r\n\t\t\ttimeout.tv_sec = 0;\r\n\t\t\ttimeout.tv_usec = 1000;\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\ttimeout.tv_sec = 100/1000;\r\n\t\t\ttimeout.tv_usec = (100%1000)*1000;\r\n\t\t}\r\n\r\n\t\tm = select(m, &socketset, NULL, NULL, &timeout);\r\n\r\n#ifdef _WIN32\r\n\t\tfor (;;)\r\n\t\t{\r\n\t\t\tchar buffer[8192];\r\n\t\t\tchar *result;\r\n\t\t\tchar c;\r\n\r\n\t\t\tif (!_kbhit())\r\n\t\t\t\tbreak;\r\n\t\t\tc = _getch();\r\n\r\n\t\t\tif (c == '\\n' || c == '\\r')\r\n\t\t\t{\r\n\t\t\t\tSys_Printf(cluster, \"\\n\");\r\n\t\t\t\tif (cluster->inputlength)\r\n\t\t\t\t{\r\n\t\t\t\t\tcluster->commandinput[cluster->inputlength] = '\\0';\r\n\t\t\t\t\tresult = Rcon_Command(cluster, NULL, cluster->commandinput, buffer, sizeof(buffer), true);\r\n\t\t\t\t\tSys_Printf(cluster, \"%s\", result);\r\n\t\t\t\t\tcluster->inputlength = 0;\r\n\t\t\t\t\tcluster->commandinput[0] = '\\0';\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\telse if (c == '\\b')\r\n\t\t\t{\r\n\t\t\t\tif (cluster->inputlength > 0)\r\n\t\t\t\t{\r\n\t\t\t\t\tSys_Printf(cluster, \"%c\", c);\r\n\t\t\t\t\tSys_Printf(cluster, \" \", c);\r\n\t\t\t\t\tSys_Printf(cluster, \"%c\", c);\r\n\r\n\t\t\t\t\tcluster->inputlength--;\r\n\t\t\t\t\tcluster->commandinput[cluster->inputlength] = '\\0';\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tSys_Printf(cluster, \"%c\", c);\r\n\t\t\t\tif (cluster->inputlength < sizeof(cluster->commandinput)-1)\r\n\t\t\t\t{\r\n\t\t\t\t\tcluster->commandinput[cluster->inputlength++] = c;\r\n\t\t\t\t\tcluster->commandinput[cluster->inputlength] = '\\0';\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n#else\r\n\t\tif (FD_ISSET(STDIN, &socketset))\r\n\t\t{\r\n\t\t\tchar buffer[8192];\r\n\t\t\tchar *result;\r\n\t\t\tcluster->inputlength = read (0, cluster->commandinput, sizeof(cluster->commandinput));\r\n\t\t\tif (cluster->inputlength >= 1)\r\n\t\t\t{\r\n\t\t\t\tcluster->commandinput[cluster->inputlength-1] = 0;        // rip off the /n and terminate\r\n\t\t\t\tcluster->inputlength--;\r\n\r\n\t\t\t\tif (cluster->inputlength)\r\n\t\t\t\t{\r\n\t\t\t\t\tcluster->commandinput[cluster->inputlength] = '\\0';\r\n\t\t\t\t\tresult = Rcon_Command(cluster, NULL, cluster->commandinput, buffer, sizeof(buffer), true);\r\n\t\t\t\t\tprintf(\"%s\", result);\r\n\t\t\t\t\tcluster->inputlength = 0;\r\n\t\t\t\t\tcluster->commandinput[0] = '\\0';\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n#endif\r\n\t}\r\n\r\n\r\n\r\n\tcluster->curtime = Sys_Milliseconds();\r\n\r\n\tfor (sv = cluster->servers; sv; )\r\n\t{\r\n\t\told = sv;\r\n\t\tsv = sv->next;\r\n\t\tQTV_Run(old);\r\n\t}\r\n\r\n\tSV_FindProxies(cluster->tcpsocket, cluster, NULL);\t//look for any other proxies wanting to muscle in on the action.\r\n\r\n\tQW_UpdateUDPStuff(cluster);\r\n\r\n\twhile(cluster->pendingproxies)\r\n\t{\r\n\t\tpend2 = cluster->pendingproxies->next;\r\n\t\tif (SV_ReadPendingProxy(cluster, cluster->pendingproxies))\r\n\t\t\tcluster->pendingproxies = pend2;\r\n\t\telse\r\n\t\t\tbreak;\r\n\t}\r\n\tif (cluster->pendingproxies)\r\n\t{\r\n\t\tfor(pend = cluster->pendingproxies; pend && pend->next; )\r\n\t\t{\r\n\t\t\tpend2 = pend->next;\r\n\t\t\tpend3 = pend2->next;\r\n\t\t\tif (SV_ReadPendingProxy(cluster, pend2))\r\n\t\t\t{\r\n\t\t\t\tpend->next = pend3;\r\n\t\t\t\tpend = pend3;\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tpend = pend2;\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n}\r\n\r\n\r\n\r\n\r\n\r\n*/"
  },
  {
    "path": "fteqtv/crc.c",
    "content": "/*\nCopyright (C) 1996-1997 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  \n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n*/\n/* crc.c */\n\n#include \"qtv.h\"\n\n\n\n// this is a 16 bit, non-reflected CRC using the polynomial 0x1021\n// and the initial and final xor values shown below...  in other words, the\n// CCITT standard CRC used by XMODEM\n\n#define QCRC_INIT_VALUE\t0xffff\n#define QCRC_XOR_VALUE\t0x0000\n\nstatic const unsigned short crctable[256] =\n{\n\t0x0000,\t0x1021,\t0x2042,\t0x3063,\t0x4084,\t0x50a5,\t0x60c6,\t0x70e7,\n\t0x8108,\t0x9129,\t0xa14a,\t0xb16b,\t0xc18c,\t0xd1ad,\t0xe1ce,\t0xf1ef,\n\t0x1231,\t0x0210,\t0x3273,\t0x2252,\t0x52b5,\t0x4294,\t0x72f7,\t0x62d6,\n\t0x9339,\t0x8318,\t0xb37b,\t0xa35a,\t0xd3bd,\t0xc39c,\t0xf3ff,\t0xe3de,\n\t0x2462,\t0x3443,\t0x0420,\t0x1401,\t0x64e6,\t0x74c7,\t0x44a4,\t0x5485,\n\t0xa56a,\t0xb54b,\t0x8528,\t0x9509,\t0xe5ee,\t0xf5cf,\t0xc5ac,\t0xd58d,\n\t0x3653,\t0x2672,\t0x1611,\t0x0630,\t0x76d7,\t0x66f6,\t0x5695,\t0x46b4,\n\t0xb75b,\t0xa77a,\t0x9719,\t0x8738,\t0xf7df,\t0xe7fe,\t0xd79d,\t0xc7bc,\n\t0x48c4,\t0x58e5,\t0x6886,\t0x78a7,\t0x0840,\t0x1861,\t0x2802,\t0x3823,\n\t0xc9cc,\t0xd9ed,\t0xe98e,\t0xf9af,\t0x8948,\t0x9969,\t0xa90a,\t0xb92b,\n\t0x5af5,\t0x4ad4,\t0x7ab7,\t0x6a96,\t0x1a71,\t0x0a50,\t0x3a33,\t0x2a12,\n\t0xdbfd,\t0xcbdc,\t0xfbbf,\t0xeb9e,\t0x9b79,\t0x8b58,\t0xbb3b,\t0xab1a,\n\t0x6ca6,\t0x7c87,\t0x4ce4,\t0x5cc5,\t0x2c22,\t0x3c03,\t0x0c60,\t0x1c41,\n\t0xedae,\t0xfd8f,\t0xcdec,\t0xddcd,\t0xad2a,\t0xbd0b,\t0x8d68,\t0x9d49,\n\t0x7e97,\t0x6eb6,\t0x5ed5,\t0x4ef4,\t0x3e13,\t0x2e32,\t0x1e51,\t0x0e70,\n\t0xff9f,\t0xefbe,\t0xdfdd,\t0xcffc,\t0xbf1b,\t0xaf3a,\t0x9f59,\t0x8f78,\n\t0x9188,\t0x81a9,\t0xb1ca,\t0xa1eb,\t0xd10c,\t0xc12d,\t0xf14e,\t0xe16f,\n\t0x1080,\t0x00a1,\t0x30c2,\t0x20e3,\t0x5004,\t0x4025,\t0x7046,\t0x6067,\n\t0x83b9,\t0x9398,\t0xa3fb,\t0xb3da,\t0xc33d,\t0xd31c,\t0xe37f,\t0xf35e,\n\t0x02b1,\t0x1290,\t0x22f3,\t0x32d2,\t0x4235,\t0x5214,\t0x6277,\t0x7256,\n\t0xb5ea,\t0xa5cb,\t0x95a8,\t0x8589,\t0xf56e,\t0xe54f,\t0xd52c,\t0xc50d,\n\t0x34e2,\t0x24c3,\t0x14a0,\t0x0481,\t0x7466,\t0x6447,\t0x5424,\t0x4405,\n\t0xa7db,\t0xb7fa,\t0x8799,\t0x97b8,\t0xe75f,\t0xf77e,\t0xc71d,\t0xd73c,\n\t0x26d3,\t0x36f2,\t0x0691,\t0x16b0,\t0x6657,\t0x7676,\t0x4615,\t0x5634,\n\t0xd94c,\t0xc96d,\t0xf90e,\t0xe92f,\t0x99c8,\t0x89e9,\t0xb98a,\t0xa9ab,\n\t0x5844,\t0x4865,\t0x7806,\t0x6827,\t0x18c0,\t0x08e1,\t0x3882,\t0x28a3,\n\t0xcb7d,\t0xdb5c,\t0xeb3f,\t0xfb1e,\t0x8bf9,\t0x9bd8,\t0xabbb,\t0xbb9a,\n\t0x4a75,\t0x5a54,\t0x6a37,\t0x7a16,\t0x0af1,\t0x1ad0,\t0x2ab3,\t0x3a92,\n\t0xfd2e,\t0xed0f,\t0xdd6c,\t0xcd4d,\t0xbdaa,\t0xad8b,\t0x9de8,\t0x8dc9,\n\t0x7c26,\t0x6c07,\t0x5c64,\t0x4c45,\t0x3ca2,\t0x2c83,\t0x1ce0,\t0x0cc1,\n\t0xef1f,\t0xff3e,\t0xcf5d,\t0xdf7c,\t0xaf9b,\t0xbfba,\t0x8fd9,\t0x9ff8,\n\t0x6e17,\t0x7e36,\t0x4e55,\t0x5e74,\t0x2e93,\t0x3eb2,\t0x0ed1,\t0x1ef0\n};\n\nvoid QCRC_Init(unsigned short *crcvalue)\n{\n\t*crcvalue = QCRC_INIT_VALUE;\n}\n\nvoid QCRC_ProcessByte(unsigned short *crcvalue, unsigned char data)\n{\n\t*crcvalue = (*crcvalue << 8) ^ crctable[(*crcvalue >> 8) ^ data];\n}\n\nunsigned short QCRC_Value(unsigned short crcvalue)\n{\n\treturn crcvalue ^ QCRC_XOR_VALUE;\n}\n\nunsigned short QCRC_Block (void *start, int count)\n{\n\tunsigned char *data = start;\n\tunsigned short\tcrc;\n\n\tQCRC_Init (&crc);\n\twhile (count--)\n\t\tcrc = (crc << 8) ^ crctable[(crc >> 8) ^ *data++];\n\n\treturn crc;\n}\n\n"
  },
  {
    "path": "fteqtv/forward.c",
    "content": "/*\r\nCopyright (C) 1996-1997 Id Software, Inc.\r\n\r\nThis program is free software; you can redistribute it and/or\r\nmodify it under the terms of the GNU General Public License\r\nas published by the Free Software Foundation; either version 2\r\nof the License, or (at your option) any later version.\r\n\r\nThis program is distributed in the hope that it will be useful,\r\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\r\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\r\n\r\nSee the included (GNU.txt) GNU General Public License for more details.\r\n\r\nYou should have received a copy of the GNU General Public License\r\nalong with this program; if not, write to the Free Software\r\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\r\n*/\r\n\r\n/*\r\nThis is the file responsible for handling incoming tcp connections.\r\nThis includes mvd recording.\r\nPassword checks and stuff are implemented here. This i server side stuff.\r\n\r\n*/\r\n\r\n#include \"qtv.h\"\r\n#include \"time.h\"\r\n\r\n\r\n#undef IN\r\n#define IN(x) buffer[(x)&(MAX_PROXY_BUFFER-1)]\r\n\r\nvoid CheckMVDConsistancy(unsigned char *buffer, int pos, int size)\r\n{\r\n/*\r\n\tint length;\r\n\tint msec, type;\r\n\twhile(pos < size)\r\n\t{\r\n\t\tmsec = IN(pos++);\r\n\t\ttype = IN(pos++);\r\n\t\tif (type == dem_set)\r\n\t\t{\r\n\t\t\tpos+=8;\r\n\t\t\tcontinue;\r\n\t\t}\r\n\t\tif (type == dem_multiple)\r\n\t\t\tpos+=4;\r\n\t\tlength = (IN(pos+0)<<0) + (IN(pos+1)<<8) + (IN(pos+2)<<16) + (IN(pos+3)<<24);\r\n\t\tpos+=4;\r\n\t\tif (length > MAX_MSGLEN)\r\n\t\t\tprintf(\"too big (%i)\\n\", length);\r\n\t\tpos += length;\r\n\t}\r\n\tif (pos != size)\r\n\t\tprintf(\"pos != size\\n\");\r\n\t*/\r\n}\r\n\r\n\r\n\r\n\r\n\r\nvoid SV_FindProxies(SOCKET sock, cluster_t *cluster, sv_t *defaultqtv)\r\n{\r\n\tunsigned long nonblocking = true;\r\n\toproxy_t *prox;\r\n\r\n\tif (sock == INVALID_SOCKET)\r\n\t\treturn;\r\n\tsock = accept(sock, NULL, NULL);\r\n\tif (sock == INVALID_SOCKET)\r\n\t\treturn;\r\n\r\n\tif (ioctlsocket (sock, FIONBIO, &nonblocking) == -1)\r\n\t{\r\n\t\tSys_Printf(cluster, \"failed to set client socket to nonblocking. dropping.\\n\");\r\n\t\tclosesocket(sock);\t//failed...\r\n\t\treturn;\r\n\t}\r\n\r\n\tif (cluster->maxproxies >= 0 && cluster->numproxies >= cluster->maxproxies)\r\n\t{\r\n\t\tconst char buffer[] = {dem_all, 1, 'P','r','o','x','y',' ','i','s',' ','f','u','l','l','.'};\r\n\t\tsend(sock, buffer, sizeof(buffer), 0);\r\n\t\tclosesocket(sock);\r\n\t\treturn;\r\n\t}\r\n\r\n\tprox = malloc(sizeof(*prox));\r\n\tif (!prox)\r\n\t{//out of mem?\r\n\t\tclosesocket(sock);\r\n\t\treturn;\r\n\t}\r\n\tmemset(prox, 0, sizeof(*prox));\r\n\tprox->sock = sock;\r\n\tprox->file = NULL;\r\n\r\n\tcluster->numproxies++;\r\n\r\n\tprox->droptime = cluster->curtime + 5*1000;\r\n#if 1\r\n\tprox->defaultstream = defaultqtv;\r\n\r\n\tprox->next = cluster->pendingproxies;\r\n\tcluster->pendingproxies = prox;\r\n#else\r\n\tprox->next = qtv->pendingproxies;\r\n\tqtv->pendingproxies = prox;\r\n\tNet_SendConnectionMVD(qtv, prox);\r\n#endif\r\n}\r\n\r\n\r\nvoid Fwd_ParseCommands(cluster_t *cluster, oproxy_t *prox)\r\n{\r\n\tnetmsg_t buf;\r\n\tint packetlength;\r\n\tint bytes;\r\n\tbytes = NET_WebSocketRecv(prox->sock, &prox->websocket, prox->inbuffer+prox->inbuffersize, sizeof(prox->inbuffer)-prox->inbuffersize, NULL);\r\n\tif (bytes < 0)\r\n\t{\r\n\t\tif (qerrno != NET_EWOULDBLOCK && qerrno != NET_EAGAIN)\t//not a problem, so long as we can flush it later.\r\n\t\t{\r\n\t\t\tSys_Printf(cluster, \"network error from client proxy\\n\");\r\n\t\t\tprox->drop = true;\t//drop them if we get any errors\r\n\t\t\treturn;\r\n\t\t}\r\n\t\tbytes = 0;\r\n\t}\r\n\telse if (bytes == 0)\r\n\t{\r\n\t\tprox->drop = true;\r\n\t\treturn;\r\n\t}\r\n\r\n\tprox->inbuffersize += bytes;\r\n\r\n\tfor(;;)\r\n\t{\r\n\t\tif (prox->inbuffersize < 2)\t//we do need at least 3 bytes for anything useful\r\n\t\t\tbreak;\r\n\r\n\t\tpacketlength = prox->inbuffer[0] + (prox->inbuffer[1]<<8);\r\n\t\tpacketlength -= 2;\t//qqshka's inconsistent-upstream-sizes stupidity.\r\n\t\tif (packetlength+2 > prox->inbuffersize)\r\n\t\t\tbreak;\r\n\r\n\t\tInitNetMsg(&buf, prox->inbuffer+2, packetlength);\r\n\t\tbuf.cursize = packetlength;\r\n\r\n\t\twhile(buf.readpos < buf.cursize)\r\n\t\t{\r\n\t\t\tswitch (ReadByte(&buf))\r\n\t\t\t{\r\n\t\t\tcase qtv_clc_stringcmd:\r\n\t\t\t\t{\r\n\t\t\t\t\tchar stringbuf[1024];\r\n\t\t\t\t\tReadString(&buf, stringbuf, sizeof(stringbuf));\r\n\t\t\t\t\tQTV_Printf(prox->stream, \"ds: %s\\n\", stringbuf);\r\n\t\t\t\t}\r\n\t\t\t\r\n\t\t\t\tbreak;\r\n\t\t\tdefault:\r\n\t\t\t\tSys_Printf(cluster, \"Received unrecognized packet type from downstream proxy.\\n\");\r\n\t\t\t\tbuf.readpos = buf.cursize;\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\t\t}\r\n\t\tpacketlength+=2;\r\n\t\tmemmove(prox->inbuffer, prox->inbuffer+packetlength, prox->inbuffersize - packetlength);\r\n\t\tprox->inbuffersize -= packetlength;\r\n\t}\r\n}\r\n\r\nvoid Net_TryFlushProxyBuffer(cluster_t *cluster, oproxy_t *prox)\r\n{\r\n\tunsigned char *buffer;\r\n\tint length;\r\n\tint bufpos;\r\n\r\n//\tif (prox->drop)\r\n//\t\treturn;\r\n\r\n\twhile (prox->bufferpos >= MAX_PROXY_BUFFER)\r\n\t{\t//so we never get any issues with wrapping..\r\n\t\tprox->bufferpos -= MAX_PROXY_BUFFER;\r\n\t\tprox->buffersize -= MAX_PROXY_BUFFER;\r\n\t}\r\n\r\n\tbufpos = prox->bufferpos&(MAX_PROXY_BUFFER-1);\r\n\tlength = prox->buffersize - prox->bufferpos;\r\n\tif (length > MAX_PROXY_BUFFER-bufpos)\t//cap the length correctly.\r\n\t\tlength = MAX_PROXY_BUFFER-bufpos;\r\n\tif (!length)\r\n\t\treturn;\t//already flushed.\r\n\tbuffer = prox->buffer + bufpos;\r\n\r\n//\tCheckMVDConsistancy(prox->buffer, prox->bufferpos, prox->buffersize);\r\n\r\n\tif (bufpos+length > MAX_PROXY_BUFFER)\r\n\t\tSys_Printf(cluster, \"oversize flush\\n\");\r\n\r\n\tif (prox->file)\r\n\t\tlength = fwrite(buffer, 1, length, prox->file);\r\n\telse\r\n\t\tlength = send(prox->sock, buffer, length, 0);\r\n\r\n\r\n\tswitch (length)\r\n\t{\r\n\tcase 0:\t//eof / they disconnected\r\n\t\tprox->drop = true;\r\n\t\tprox->flushing = false;\r\n\t\tbreak;\r\n\tcase -1:\r\n\t\tlength = qerrno;\r\n\t\tif (length != NET_EWOULDBLOCK && length != NET_EAGAIN)\t//not a problem, so long as we can flush it later.\r\n\t\t{\r\n\t\t\tSys_Printf(cluster, \"network error from client proxy\\n\");\r\n\t\t\tprox->drop = true;\t//drop them if we get any errors\r\n\t\t\tprox->flushing = false;\r\n\t\t}\r\n\t\tbreak;\r\n\tdefault:\r\n\t\tprox->bufferpos += length;\r\n\t}\r\n}\r\n\r\nvoid Net_ProxySendString(cluster_t *cluster, oproxy_t *prox, void *buffer)\r\n{\r\n\tNet_ProxySend(cluster, prox, buffer, strlen(buffer));\r\n}\r\n\r\nvoid Net_ProxySend(cluster_t *cluster, oproxy_t *prox, void *buffer, int length)\r\n{\r\n\tint wrap;\r\n\r\n\tif (!length)\r\n\t\treturn;\r\n\r\n\tif (prox->websocket.websocket)\r\n\t{\r\n\t\tunsigned int c;\r\n\t\tint enclen = 0;\r\n\t\tint datatype = 2;\t//1=utf-8, 2=binary\r\n\r\n\t\t/*work out how much buffer space we'll need*/\r\n\t\tif (datatype == 2)\r\n\t\t\tenclen += length;\r\n\t\telse\r\n\t\t{\r\n\t\t\tfor (c = 0; c < length; c++)\r\n\t\t\t{\r\n\t\t\t\tif (((unsigned char*)buffer)[c] == 0 || ((unsigned char*)buffer)[c] >= 0x80)\r\n\t\t\t\t\tenclen += 2;\r\n\t\t\t\telse\r\n\t\t\t\t\tenclen += 1;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tif (prox->buffersize-prox->bufferpos + (enclen+4) > MAX_PROXY_BUFFER)\r\n\t\t{\r\n\t\t\tNet_TryFlushProxyBuffer(cluster, prox);\t//try flushing\r\n\t\t\tif (prox->buffersize-prox->bufferpos + (enclen+4) > MAX_PROXY_BUFFER)\t//damn, still too big.\r\n\t\t\t{\t//they're too slow. hopefully it was just momentary lag\r\n\t\t\t\tif (!prox->flushing)\r\n\t\t\t\t{\r\n\t\t\t\t\tprintf(\"QTV client is too lagged\\n\");\r\n\t\t\t\t\tprox->flushing = true;\r\n\t\t\t\t}\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tif (enclen >= 126)\r\n\t\t{\r\n\t\t\tprox->buffer[prox->buffersize++&(MAX_PROXY_BUFFER-1)] = 0x80|datatype;\r\n\t\t\tprox->buffer[prox->buffersize++&(MAX_PROXY_BUFFER-1)] = 126;\r\n\t\t\tprox->buffer[prox->buffersize++&(MAX_PROXY_BUFFER-1)] = enclen>>8;\r\n\t\t\tprox->buffer[prox->buffersize++&(MAX_PROXY_BUFFER-1)] = enclen;\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tprox->buffer[prox->buffersize++&(MAX_PROXY_BUFFER-1)] = 0x80|datatype;\r\n\t\t\tprox->buffer[prox->buffersize++&(MAX_PROXY_BUFFER-1)] = enclen;\r\n\t\t}\r\n\t\tif (datatype == 2)\r\n\t\t{\r\n\t\t\tfor(; length-->0; buffer = (char*)buffer+1)\r\n\t\t\t\tprox->buffer[prox->buffersize++&(MAX_PROXY_BUFFER-1)] = *(unsigned char*)buffer;\r\n\t\t}\r\n\t\telse\r\n\t\t{\t//utf-8 (or really just bytes with upper bits truncated. sue me.\r\n\t\t\twhile(length-->0)\r\n\t\t\t{\r\n\t\t\t\tc = *(unsigned char*)buffer;\r\n\t\t\t\tbuffer = (char*)buffer+1;\r\n\t\t\t\tif (!c)\r\n\t\t\t\t\tc |= 0x100;\t/*will get truncated at the other end*/\r\n\t\t\t\tif (c >= 0x80)\r\n\t\t\t\t{\r\n\t\t\t\t\tprox->buffer[prox->buffersize++&(MAX_PROXY_BUFFER-1)] = 0xc0 | (c>>6);\r\n\t\t\t\t\tprox->buffer[prox->buffersize++&(MAX_PROXY_BUFFER-1)] = 0x80 | (c & 0x3f);\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t\tprox->buffer[prox->buffersize++&(MAX_PROXY_BUFFER-1)] = c;\r\n\t\t\t}\r\n\t\t}\r\n\t\tNet_TryFlushProxyBuffer(cluster, prox);\t//try flushing in a desperate attempt to reduce bugs in google chrome.\r\n\t\treturn;\r\n\t}\r\n\r\n\tif (prox->buffersize-prox->bufferpos + length > MAX_PROXY_BUFFER)\r\n\t{\r\n\t\tNet_TryFlushProxyBuffer(cluster, prox);\t//try flushing\r\n\t\tif (prox->buffersize-prox->bufferpos + length > MAX_PROXY_BUFFER)\t//damn, still too big.\r\n\t\t{\t//they're too slow. hopefully it was just momentary lag\r\n\t\t\tif (!prox->flushing)\r\n\t\t\t{\r\n\t\t\t\tprintf(\"QTV client is too lagged\\n\");\r\n\t\t\t\tprox->flushing = true;\r\n\t\t\t}\r\n\t\t\treturn;\r\n\t\t}\r\n\t}\r\n#if 1\r\n\t//just simple\r\n\tprox->buffersize+=length;\r\n\tfor (wrap = prox->buffersize-length; wrap < prox->buffersize; wrap++)\r\n\t{\r\n\t\tprox->buffer[wrap&(MAX_PROXY_BUFFER-1)] = *(unsigned char*)buffer;\r\n\t\tbuffer = (char*)buffer+1;\r\n\t}\r\n#else\r\n\t//we don't do multiple wrappings, the above check cannot succeed if it were required.\r\n\r\n\t//find the wrap point\r\n\twrap = prox->buffersize-(prox->buffersize&(MAX_PROXY_BUFFER-1)) + MAX_PROXY_BUFFER;\r\n\twrap = wrap - (prox->buffersize&(MAX_PROXY_BUFFER-1));\t//the ammount of data we can fit before wrapping.\r\n\r\n\tif (wrap > length)\r\n\t{\t//we don't wrap afterall\r\n\t\tmemcpy(prox->buffer+(prox->buffersize)&(MAX_PROXY_BUFFER-1), buffer, length);\r\n\t\tprox->buffersize+=length;\r\n\t\treturn;\r\n\t}\r\n\tmemcpy(prox->buffer+prox->buffersize&(MAX_PROXY_BUFFER-1), buffer, wrap);\r\n\tbuffer += wrap;\r\n\tlength -= wrap;\r\n\tmemcpy(prox->buffer, buffer, length);\r\n\r\n\tprox->buffersize+=length;\r\n#endif\r\n}\r\n\r\nvoid Prox_SendMessage(cluster_t *cluster, oproxy_t *prox, char *buf, int length, int dem_type, unsigned int playermask)\r\n{\r\n\tnetmsg_t msg;\r\n\tchar tbuf[16];\r\n\tInitNetMsg(&msg, tbuf, sizeof(tbuf));\r\n\tWriteByte(&msg, 0);\r\n\tWriteByte(&msg, dem_type);\r\n\tWriteLong(&msg, length);\r\n\tif (dem_type == dem_multiple)\r\n\t\tWriteLong(&msg, playermask);\r\n\r\n\tif (prox->buffersize-prox->bufferpos + length + msg.cursize > MAX_PROXY_BUFFER)\r\n\t{\r\n\t\tNet_TryFlushProxyBuffer(cluster, prox);\t//try flushing\r\n\t\tif (prox->buffersize-prox->bufferpos + length + msg.cursize > MAX_PROXY_BUFFER)\t//damn, still too big.\r\n\t\t{\t//they're too slow. hopefully it was just momentary lag\r\n\t\t\tprox->flushing = true;\r\n\t\t\treturn;\r\n\t\t}\r\n\t}\r\n\r\n\r\n\tNet_ProxySend(cluster, prox, msg.data, msg.cursize);\r\n\r\n\tNet_ProxySend(cluster, prox, buf, length);\r\n}\r\n\r\nvoid Fwd_SendDownstream(sv_t *qtv, void *buffer, int length)\r\n{\t//broadcasts data to all client proxies, with dont-buffer\r\n\toproxy_t *prox;\r\n\tfor (prox = qtv->proxies; prox; prox = prox->next)\r\n\t{\r\n\t\tProx_SendMessage(qtv->cluster, prox, buffer, length, dem_qtvdata, (unsigned int)-1);\r\n\t}\r\n}\r\n\r\nvoid Fwd_SayToDownstream(sv_t *qtv, char *message)\r\n{\r\n\tnetmsg_t msg;\r\n\tchar buffer[1024];\r\n\r\n\tInitNetMsg(&msg, buffer, sizeof(buffer));\r\n\tWriteByte(&msg, svc_print);\r\n\tWriteByte(&msg, PRINT_CHAT);\r\n\tWriteString2(&msg, \"[QTV]\");\r\n\tWriteString(&msg, message);\r\n\r\n\tFwd_SendDownstream(qtv, msg.data, msg.cursize);\r\n}\r\n\r\nvoid Prox_SendPlayerStats(sv_t *qtv, oproxy_t *prox)\r\n{\r\n\tchar buffer[MAX_MSGLEN];\r\n\tnetmsg_t msg;\r\n\tint player, snum;\r\n\r\n\tInitNetMsg(&msg, buffer, sizeof(buffer));\r\n\r\n\tfor (player = 0; player < MAX_CLIENTS; player++)\r\n\t{\r\n\t\tfor (snum = 0; snum < MAX_STATS; snum++)\r\n\t\t{\r\n\t\t\tif (qtv->map.players[player].stats[snum])\r\n\t\t\t{\r\n\t\t\t\tif ((unsigned)qtv->map.players[player].stats[snum] > 255)\r\n\t\t\t\t{\r\n\t\t\t\t\tWriteByte(&msg, svc_updatestatlong);\r\n\t\t\t\t\tWriteByte(&msg, snum);\r\n\t\t\t\t\tWriteLong(&msg, qtv->map.players[player].stats[snum]);\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\tWriteByte(&msg, svc_updatestat);\r\n\t\t\t\t\tWriteByte(&msg, snum);\r\n\t\t\t\t\tWriteByte(&msg, qtv->map.players[player].stats[snum]);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tif (msg.cursize)\r\n\t\t{\r\n\t\t\tProx_SendMessage(qtv->cluster, prox, msg.data, msg.cursize, dem_stats|(player<<3), (1<<player));\r\n\t\t\tmsg.cursize = 0;\r\n\t\t}\r\n\t}\r\n}\r\n\r\nvoid Prox_SendInitialPlayers(sv_t *qtv, oproxy_t *prox, netmsg_t *msg)\r\n{\r\n\tint i, j, flags;\r\n\tchar buffer[64];\r\n\r\n\tfor (i = 0; i < MAX_CLIENTS; i++)\r\n\t{\r\n\t\tif (!qtv->map.players[i].active) // interesting, is this set to false if player disconnect from server?\r\n\t\t\tcontinue;\r\n\r\n\t\tflags =   (DF_ORIGIN << 0) | (DF_ORIGIN << 1) | (DF_ORIGIN << 2)\r\n\t\t\t\t| (DF_ANGLES << 0) | (DF_ANGLES << 1) | (DF_ANGLES << 2) // angles is something what changed frequently, so may be not send it?\r\n\t\t\t\t| DF_EFFECTS\r\n\t\t\t\t| DF_SKINNUM // though it rare thingie, so better send it?\r\n\t\t\t\t| (qtv->map.players[i].dead   ? DF_DEAD : 0)\r\n\t\t\t\t| (qtv->map.players[i].gibbed ? DF_GIB  : 0)\r\n\t\t\t\t| DF_WEAPONFRAME // do we so really need it?\r\n\t\t\t\t| DF_MODEL; // generally, that why we wrote this function, so YES send this\r\n\r\n\t\tif (*qtv->map.players[i].userinfo && atoi(Info_ValueForKey(qtv->map.players[i].userinfo, \"*spectator\", buffer, sizeof(buffer))))\r\n\t\t\tflags = DF_MODEL; // oh, that spec, just sent his model, may be even better ignore him?\r\n\r\n\t\tWriteByte (msg, svc_playerinfo);\r\n\t\tWriteByte (msg, i);\r\n\t\tWriteShort (msg, flags);\r\n\r\n\t\tWriteByte (msg, qtv->map.players[i].current.frame); // always sent\r\n\r\n\t\tfor (j = 0 ; j < 3 ; j++)\r\n\t\t\tif (flags & (DF_ORIGIN << j))\r\n\t\t\t\tWriteCoord (msg, qtv->map.players[i].current.origin[j], qtv->pext1);\r\n\r\n\t\tfor (j = 0 ; j < 3 ; j++)\r\n\t\t\tif (flags & (DF_ANGLES << j))\r\n\t\t\t\tWriteShort (msg, (qtv->map.players[i].current.angles[j]/360.0f)*0x10000);\r\n\r\n\t\tif (flags & DF_MODEL) // generally, that why we wrote this function, so YES send this\r\n\t\t\tWriteByte (msg, qtv->map.players[i].current.modelindex);\r\n\r\n\t\tif (flags & DF_SKINNUM)\r\n\t\t\tWriteByte (msg, qtv->map.players[i].current.skinnum);\r\n\r\n\t\tif (flags & DF_EFFECTS)\r\n\t\t\tWriteByte (msg, qtv->map.players[i].current.effects);\r\n\r\n\t\tif (flags & DF_WEAPONFRAME)\r\n\t\t\tWriteByte (msg, qtv->map.players[i].current.weaponframe);\r\n\t}\r\n}\r\n\r\nvoid Net_GreetingMessage(oproxy_t *prox)\r\n{\r\n\tchar buffer[1024];\r\n\tnetmsg_t msg;\r\n\r\n\tInitNetMsg(&msg, buffer, sizeof(buffer));\r\n\tWriteByte(&msg, svc_print);\r\n\tWriteByte(&msg, PRINT_HIGH);\r\n\tWriteString2(&msg, \"Welcome to \");\r\n\tWriteString2(&msg, prox->stream->cluster->hostname);\r\n\tWriteString(&msg, \"\\n\");\r\n\r\n\tProx_SendMessage(prox->stream->cluster, prox, msg.data, msg.cursize, dem_qtvdata, (unsigned)-1);\r\n}\r\n\r\nvoid Net_SendConnectionMVD(sv_t *qtv, oproxy_t *prox)\r\n{\r\n\tchar buffer[MAX_MSGLEN*8];\r\n\tnetmsg_t msg;\r\n\tint prespawn;\r\n\r\n\t//only send connection data if there's actual data to be sent\r\n\t//if not, the other end will get the data when we receive it anyway.\r\n\tif (!*qtv->map.mapname)\r\n\t\treturn;\r\n\r\n\tInitNetMsg(&msg, buffer, sizeof(buffer));\r\n\r\n\tprox->flushing = false;\r\n\r\n\tBuildServerData(qtv, &msg, 0, NULL);\r\n\tProx_SendMessage(qtv->cluster, prox, msg.data, msg.cursize, dem_read, (unsigned)-1);\r\n\tmsg.cursize = 0;\r\n\r\n\tfor (prespawn = 0;prespawn >= 0;)\r\n\t{\r\n\t\tprespawn = SendList(qtv, prespawn, qtv->map.soundlist, svc_soundlist, &msg);\r\n\t\tProx_SendMessage(qtv->cluster, prox, msg.data, msg.cursize, dem_read, (unsigned)-1);\r\n\t\tmsg.cursize = 0;\r\n\t}\r\n\r\n\tfor (prespawn = 0;prespawn >= 0;)\r\n\t{\r\n\t\tprespawn = SendList(qtv, prespawn, qtv->map.modellist, svc_modellist, &msg);\r\n\t\tProx_SendMessage(qtv->cluster, prox, msg.data, msg.cursize, dem_read, (unsigned)-1);\r\n\t\tmsg.cursize = 0;\r\n\t}\r\n\r\n\tNet_TryFlushProxyBuffer(qtv->cluster, prox);\t//that should be enough data to fill a packet.\r\n\r\n\tfor(prespawn = 0;prespawn>=0;)\r\n\t{\r\n\t\tprespawn = Prespawn(qtv, 0, &msg, prespawn, MAX_CLIENTS-1);\r\n\r\n\t\tProx_SendMessage(qtv->cluster, prox, msg.data, msg.cursize, dem_read, (unsigned)-1);\r\n\t\tmsg.cursize = 0;\r\n\t}\r\n\r\n\t//playerstates are delta-compressed, unfortunatly this isn't qwd (thanks to qqshka for showing my folly)\r\n\tProx_SendInitialPlayers(qtv, prox, &msg);\r\n\tProx_SendMessage(qtv->cluster, prox, msg.data, msg.cursize, dem_read, (unsigned)-1);\r\n\tmsg.cursize = 0;\r\n\r\n\t//we do need to send entity states.\r\n\tProx_SendInitialEnts(qtv, prox, &msg);\r\n\tProx_SendMessage(qtv->cluster, prox, msg.data, msg.cursize, dem_read, (unsigned)-1);\r\n\tmsg.cursize = 0;\r\n\r\n\tWriteByte(&msg, svc_stufftext);\r\n\tWriteString(&msg, \"skins\\n\");\r\n\tProx_SendMessage(qtv->cluster, prox, msg.data, msg.cursize, dem_read, (unsigned)-1);\r\n\tmsg.cursize = 0;\r\n\r\n\tNet_TryFlushProxyBuffer(qtv->cluster, prox);\r\n\r\n\tProx_SendPlayerStats(qtv, prox);\r\n\tNet_TryFlushProxyBuffer(qtv->cluster, prox);\r\n\r\n\tif (!qtv->cluster->lateforward)\r\n\t\tNet_ProxySend(qtv->cluster, prox, qtv->buffer, qtv->forwardpoint);\t//send all the info we've not yet processed (but have already forwarded).\r\n\r\n\r\n\tif (prox->flushing)\r\n\t{\r\n\t\tSys_Printf(qtv->cluster, \"Connection data is too big, dropping proxy client\\n\");\r\n\t\tprox->drop = true;\t//this is unfortunate...\r\n\t}\r\n\telse\r\n\t\tNet_TryFlushProxyBuffer(qtv->cluster, prox);\r\n}\r\n\r\n\r\noproxy_t *Net_FileProxy(sv_t *qtv, char *filename)\r\n{\r\n\toproxy_t *prox;\r\n\tFILE *f;\r\n\r\n\tf = fopen(filename, \"wb\");\r\n\tif (!f)\r\n\t\treturn NULL;\r\n\r\n\t//no full proxy check, this is going to be used by proxy admins, who won't want to have to raise the limit to start recording.\r\n\r\n\tprox = malloc(sizeof(*prox));\r\n\tif (!prox)\r\n\t\treturn NULL;\r\n\tmemset(prox, 0, sizeof(*prox));\r\n\r\n\tprox->sock = INVALID_SOCKET;\r\n\tprox->file = f;\r\n\r\n\tprox->next = qtv->proxies;\r\n\tqtv->proxies = prox;\r\n\r\n\tqtv->cluster->numproxies++;\r\n\r\n\tNet_SendConnectionMVD(qtv, prox);\r\n\r\n\treturn prox;\r\n}\r\n\r\nqboolean Net_StopFileProxy(sv_t *qtv)\r\n{\r\n\toproxy_t *prox;\r\n\tfor (prox = qtv->proxies; prox; prox = prox->next)\r\n\t{\r\n\t\tif (prox->file)\r\n\t\t{\r\n\t\t\tprox->drop = true;\r\n\t\t\treturn true;\r\n\t\t}\r\n\t}\r\n\treturn false;\r\n}\r\n\r\n\r\n\r\nvoid SV_ForwardStream(sv_t *qtv, void *buffer, int length)\r\n{\t//forward the stream on to connected clients\r\n\toproxy_t *prox, *next, *fre;\r\n\r\n\tCheckMVDConsistancy(buffer, 0, length);\r\n\r\n\r\n\twhile (qtv->proxies && qtv->proxies->drop)\r\n\t{\r\n\t\tnext = qtv->proxies->next;\r\n\t\tfre = qtv->proxies;\r\n\t\tif (fre->file)\r\n\t\t\tfclose(fre->file);\r\n\t\telse\r\n\t\t\tclosesocket(fre->sock);\r\n\t\tfree(fre);\r\n\t\tqtv->cluster->numproxies--;\r\n\t\tqtv->proxies = next;\r\n\t}\r\n\r\n\tfor (prox = qtv->proxies; prox; prox = prox->next)\r\n\t{\r\n\t\twhile (prox->next && prox->next->drop)\r\n\t\t{\r\n\t\t\tnext = prox->next->next;\r\n\t\t\tfre = prox->next;\r\n\t\t\tif (fre->file)\r\n\t\t\t\tfclose(fre->file);\r\n\t\t\telse\r\n\t\t\t\tclosesocket(fre->sock);\r\n\t\t\tif (fre->srcfile)\r\n\t\t\t\tfclose(fre->srcfile);\r\n\t\t\tfree(fre);\r\n\t\t\tqtv->cluster->numproxies--;\r\n\t\t\tprox->next = next;\r\n\t\t}\r\n\r\n\t\tif (prox->flushing)\t//don't send it if we're trying to empty thier buffer.\r\n\t\t{\r\n\t\t\tif (prox->buffersize == prox->bufferpos)\r\n\t\t\t{\r\n\t\t\t\tif (!qtv->parsingconnectiondata)\r\n\t\t\t\t\tNet_SendConnectionMVD(qtv, prox);\t//they're up to date, resend the connection info.\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tNet_TryFlushProxyBuffer(qtv->cluster, prox);\t//try and flush it.\r\n\t\t\t\tcontinue;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tif (prox->drop)\r\n\t\t\tcontinue;\r\n\r\n\t\t//add the new data\r\n\t\tNet_ProxySend(qtv->cluster, prox, buffer, length);\r\n\r\n\t\tNet_TryFlushProxyBuffer(qtv->cluster, prox);\r\n//\t\tNet_TryFlushProxyBuffer(qtv->cluster, prox);\r\n//\t\tNet_TryFlushProxyBuffer(qtv->cluster, prox);\r\n\r\n#ifndef _MSC_VER\r\n\t#warning This is not the place for this\r\n#endif\r\n\t\tif (prox->sock != INVALID_SOCKET)\r\n\t\t{\r\n\t\t\tFwd_ParseCommands(qtv->cluster, prox);\r\n\t\t}\r\n\t}\r\n}\r\n\r\n/*wrapper around recv to handle websocket connections*/\r\nint NET_WebSocketRecv(SOCKET sock, wsrbuf_t *ws, unsigned char *out, unsigned int outlen, int *clen)\r\n{\r\n\tunsigned int mask = 0;\r\n\tunsigned int paylen;\r\n\tint len, i;\r\n\r\n\tif (clen)\r\n\t\t*clen = -1;\r\n\tif (!ws->websocket)\r\n\t\treturn recv(sock, out, outlen, 0);\r\n\r\n\tif (!ws->wsbuflen)\r\n\t{\r\n\t\tlen = recv(sock, ws->wsbuf, 2, 0);\r\n\t\tif (len > 0)\r\n\t\t\tws->wsbuflen+=2;\r\n\t\telse\r\n\t\t\treturn len;\r\n\t}\r\n\tif (ws->wsbuflen >= 2)\r\n\t{\r\n\t\tunsigned short ctrl = (ws->wsbuf[0]<<8) | ws->wsbuf[1];\r\n\t\tlen = 2;\r\n\t\tif ((ctrl & 0x7f) == 127)\r\n\t\t\tlen += 8;\r\n\t\telse if ((ctrl & 0x7f) == 126)\r\n\t\t\tlen += 2;\r\n\t\tif (ctrl & 0x80)\r\n\t\t\tlen += 4;\r\n\t\tif (ws->wsbuflen < len)\r\n\t\t{\r\n\t\t\tpaylen = recv(sock, ws->wsbuf+ws->wsbuflen, len - ws->wsbuflen, 0);\r\n\t\t\tif (paylen > 0)\r\n\t\t\t\tws->wsbuflen += paylen;\r\n\t\t\telse\r\n\t\t\t\treturn paylen;\r\n\t\t}\r\n\r\n\t\t/*headers are complete*/\r\n\t\tif (ws->wsbuflen >= len)\r\n\t\t{\r\n\t\t\tif ((ctrl & 0x7f) == 127)\r\n\t\t\t{\r\n//\t\t\t\tpaylen = paylen = ((ws->wsbuf[2]<<56) | (ws->wsbuf[3]<<48) | (ws->wsbuf[4]<<40) | (ws->wsbuf[5]<<32) | (ws->wsbuf[6]<<24) | (ws->wsbuf[7]<<16) | (ws->wsbuf[8]<<8) | (ws->wsbuf[9]<<0);\r\n//\t\t\t\tif (paylen < 65536)\r\n\t\t\t\t\treturn 0;\t//error\r\n\t\t\t}\r\n\t\t\telse if ((ctrl & 0x7f) == 126)\r\n\t\t\t{\r\n\t\t\t\tpaylen = (ws->wsbuf[2]<<8) | ws->wsbuf[3];\r\n\t\t\t\tif (paylen < 126)\r\n\t\t\t\t\treturn 0;\t//error\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t\tpaylen = ctrl & 0x7f;\r\n\r\n\t\t\tif (ctrl & 0x80)\r\n\t\t\t{\r\n\t\t\t\t((unsigned char*)&mask)[0] = ws->wsbuf[ws->wsbuflen-4];\r\n\t\t\t\t((unsigned char*)&mask)[1] = ws->wsbuf[ws->wsbuflen-3];\r\n\t\t\t\t((unsigned char*)&mask)[2] = ws->wsbuf[ws->wsbuflen-2];\r\n\t\t\t\t((unsigned char*)&mask)[3] = ws->wsbuf[ws->wsbuflen-1];\r\n\t\t\t}\r\n\t\t\tif (!(ctrl & 0x8000))\r\n\t\t\t\treturn 0;\t//can't handle fragmented frames\r\n\r\n\t\t\tswitch((ctrl>>8) & 0xf)\r\n\t\t\t{\r\n\t\t\tcase 1: /*text frame*/\r\n\t\t\t\tlen = 0;\r\n\t\t\t\twhile (outlen>len && ws->wspushed < paylen)\r\n\t\t\t\t{\r\n\t\t\t\t\tunsigned char n;\r\n\t\t\t\t\tctrl = recv(sock, &n, 1, 0);\t//FIXME: not my fastest code...\r\n\t\t\t\t\tif (ctrl <= 0)\r\n\t\t\t\t\t\treturn len>0?len:ctrl;\r\n\r\n\t\t\t\t\tn ^= ((unsigned char*)&mask)[(ws->wspushed++)&3];\r\n\t\t\t\t\t\r\n\t\t\t\t\tif (ws->wsbits)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\t*out++ = ((ws->wsbits&0x1f)<<6) | (n & 0x3f);\r\n\t\t\t\t\t\tlen++;\r\n\t\t\t\t\t\tws->wsbits = 0;\r\n\t\t\t\t\t}\r\n\t\t\t\t\telse if ((n & 0xe0) == 0xc0)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tws->wsbits = n;\r\n\t\t\t\t\t}\r\n\t\t\t\t\telse if (n & 0x80)\r\n\t\t\t\t\t\treturn 0; //error\r\n\t\t\t\t\telse\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\t*out++ = n;\r\n\t\t\t\t\t\tlen++;\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t\tif (ws->wspushed == paylen)\r\n\t\t\t\t{\r\n\t\t\t\t\tif (ws->wsbits)\r\n\t\t\t\t\t\treturn 0; //error\r\n\t\t\t\t\tif (clen)\r\n\t\t\t\t\t\t*clen = ws->wspushed;\r\n\t\t\t\t\tws->wspushed = 0;\r\n\t\t\t\t\tws->wsbuflen = 0;\r\n\t\t\t\t}\r\n\t\t\t\treturn len;\r\n\t\t\tcase 2: /*binary frame*/\r\n\t\t\t\tif (outlen > paylen - ws->wspushed)\r\n\t\t\t\t\toutlen = paylen - ws->wspushed;\r\n\t\t\t\tlen = recv(sock, out, outlen, 0);\r\n\t\t\t\tif (len > 0)\r\n\t\t\t\t{\r\n\t\t\t\t\tfor(i = 0; i < len; i++)\r\n\t\t\t\t\t\tout[i] ^= ((unsigned char*)&mask)[(ws->wspushed+i)&3];\r\n\t\t\t\t\tws->wspushed += len;\r\n\t\t\t\t}\r\n\t\t\t\tif (paylen == ws->wspushed)\r\n\t\t\t\t{\r\n\t\t\t\t\t/*success! move on to the next*/\r\n\t\t\t\t\tif (clen)\r\n\t\t\t\t\t\t*clen = ws->wspushed;\r\n\t\t\t\t\tws->wspushed = 0;\r\n\t\t\t\t\tws->wsbuflen = 0;\r\n\t\t\t\t}\r\n\t\t\t\treturn len;\r\n\t\t\tcase 8: /*close*/\r\n\t\t\tcase 9: /*ping*/\r\n\t\t\tcase 10: /*pong*/\r\n\t\t\tdefault:\r\n\t\t\t\treturn 0; //unsupported\r\n\t\t\t}\r\n\t\t\treturn 0;\r\n\t\t}\r\n\t\treturn 0;\r\n\t}\r\n\telse\r\n\t\treturn 0;\r\n}\r\n\r\n//returns true if the pending proxy should be unlinked\r\n//truth does not imply that it should be freed/released, just unlinked.\r\nqboolean SV_ReadPendingProxy(cluster_t *cluster, oproxy_t *pend)\r\n{\r\n\t//drop: ignored if flushing is still set. clear flushing and set drop on errors.\r\n\tchar tempbuf[512];\r\n\tunsigned char *s;\r\n\tunsigned char *e;\r\n\tchar *colon;\r\n\tfloat clientversion = 0;\r\n\tint len;\r\n\tqboolean eoh;\r\n\tint headersize;\r\n\tqboolean raw;\r\n\tsv_t *qtv;\r\n\r\n\tif (pend->drop && !pend->flushing)\r\n\t{\r\n\t\tprintf(\"pending drop\\n\");\r\n\t\tif (pend->srcfile)\r\n\t\t\tfclose(pend->srcfile);\r\n\t\tif (pend->sock != INVALID_SOCKET)\r\n\t\t\tclosesocket(pend->sock);\r\n\t\tfree(pend);\r\n\t\tcluster->numproxies--;\r\n\r\n\t\t//REMEMBER TO PUT ANY CLEANUP INSIDE QW_TCPConnection TOO!\r\n\t\treturn true;\r\n\t}\r\n#define QTVSVHEADER \"QTVSV 1.1\\n\"\r\n\r\n\tNet_TryFlushProxyBuffer(cluster, pend);\r\n\r\n\tif (pend->flushing)\r\n\t{\r\n\t\tif (pend->srcfile)\r\n\t\t{\r\n#if 0\r\n\t\t\t//bufferend = transmit point\r\n\t\t\t//buffersize = write point\r\n\t\t\tif (bufferend < buffersize)\r\n\t\t\t\tspace = (MAX_PROXY_BUFFER - pend->buffersize) + pend->bufferend;\r\n\t\t\telse\r\n\t\t\t\tspace = pend->bufferend - pend->buffersize;\r\n\r\n\t\t\tif (space < 256)\t/*don't bother reading if we're dribbling*/\r\n\t\t\t\treturn false;\r\n\t\t\tif (space > 0)\t/*never fully saturate so as to not confuse the ring*/\r\n\t\t\t\tspace--;\r\n\r\n\t\t\tif (space > MAX_PROXY_BUFFER - \r\n\t\t\tfread(prox->buffer + pend->buffersize, 1, space, pend->srcfile);\r\n#else\r\n\t\t\twhile (pend->bufferpos == pend->buffersize)\r\n\t\t\t{\r\n\t\t\t\tchar buffer[MAX_PROXY_BUFFER/2];\r\n\t\t\t\tpend->droptime = cluster->curtime + 5*1000;\r\n\t\t\t\tlen = fread(buffer, 1, sizeof(buffer), pend->srcfile);\r\n\t\t\t\tif (!len)\r\n\t\t\t\t{\r\n\t\t\t\t\tfclose(pend->srcfile);\r\n\t\t\t\t\tpend->srcfile = NULL;\r\n\t\t\t\t\tbreak;\r\n\t\t\t\t}\r\n\t\t\t\tNet_ProxySend(cluster, pend, buffer, len);\r\n\t\t\t\tNet_TryFlushProxyBuffer(cluster, pend);\r\n\t\t\t}\r\n#endif\r\n\t\t\treturn false;\t//don't try reading anything yet\r\n\t\t}\r\n\r\n\t\tif (pend->bufferpos != pend->buffersize)\r\n\t\t\treturn false;\r\n\t\tpend->flushing = false;\r\n\t\tif (pend->drop)\r\n\t\t\treturn false;\r\n\t}\r\n\r\n\tif (pend->droptime < cluster->curtime)\r\n\t{\r\n\t\tprintf(\"pending timeout\\n\");\r\n\t\tpend->drop = true;\r\n\t\tpend->flushing = false;\r\n\t\treturn false;\r\n\t}\r\n\r\n\tlen = sizeof(pend->inbuffer) - pend->inbuffersize - 1;\r\n\tlen = NET_WebSocketRecv(pend->sock, &pend->websocket, pend->inbuffer+pend->inbuffersize, len, NULL);\r\n\tif (len == 0)\r\n\t{\r\n\t\tpend->drop = true;\r\n\t\tprintf(\"pending EOF\\n\");\r\n\t\treturn false;\r\n\t}\r\n\telse if (len > 0)\r\n\t\tpend->droptime = cluster->curtime + 5*1000;\r\n\tif (len < 0)\r\n\t{\r\n\t\treturn false;\r\n\t}\r\n\r\n\tpend->inbuffersize += len;\r\n\tpend->inbuffer[pend->inbuffersize] = '\\0';\r\n\r\n\tif (pend->inbuffersize >= 4)\r\n\t{\r\n\t\tif (!strncmp(pend->inbuffer, \"qizmo\\n\", 6))\r\n\t\t{\r\n\t\t\t//carries unreliable packets\r\n\t\t\tprintf(\"tcpconnect\\n\");\r\n\t\t\tsend(pend->sock, \"qizmo\\n\", 6, 0);\r\n\t\t\tQW_TCPConnection(cluster, pend, NULL);\r\n\r\n\t\t\treturn true;\r\n\t\t}\r\n\t\tif (ustrncmp(pend->inbuffer, \"QTV\\r\", 4) && ustrncmp(pend->inbuffer, \"QTV\\n\", 4) && ustrncmp(pend->inbuffer, \"GET \", 4) && ustrncmp(pend->inbuffer, \"POST \", 5))\r\n\t\t{\t//I have no idea what the smeg you are.\r\n\t\t\tpend->drop = true;\r\n\r\n\t\t\tpend->inbuffer[16] = 0;\r\n\t\t\tSys_Printf(cluster, \"pending proxy: Connect for unrecognized protocol %s\\n\", pend->inbuffer);\r\n\t\t\treturn false;\r\n\t\t}\r\n\t}\r\n\r\n\t//make sure there's a double \\n somewhere\r\n\r\n\teoh = false;\r\n\tfor (s = pend->inbuffer; s<=pend->inbuffer+pend->inbuffersize; s++)\r\n\t{\r\n\t\tif (s[0] == '\\n' && s[1] == '\\n')\r\n\t\t{\r\n\t\t\ts += 2;\r\n\t\t\teoh = true;\r\n\t\t\tbreak;\r\n\t\t}\r\n\t\tif (s[0] == '\\n' && s[1] == '\\r' && s[2] == '\\n')\r\n\t\t{\r\n\t\t\ts += 3;\r\n\t\t\teoh = true;\r\n\t\t\tbreak;\r\n\t\t}\r\n\t}\r\n\tif (!eoh)\r\n\t\treturn false;\t//don't have enough yet\r\n\theadersize = s - pend->inbuffer;\r\n\r\n\tif (!ustrncmp(pend->inbuffer, \"POST \", 5))\r\n\t{\r\n\t\tHTTPSV_PostMethod(cluster, pend, (char*)s);\r\n\r\n\t\treturn false;\t//not keen on this..\r\n\t}\r\n\telse if (!ustrncmp(pend->inbuffer, \"GET \", 4))\r\n\t{\r\n\t\tpend->drop = true;\r\n\t\tcolon = HTTPSV_GetMethod(cluster, pend);\r\n\t\tpend->flushing = true;\r\n\t\tmemmove(pend->inbuffer, pend->inbuffer+headersize, pend->inbuffersize-headersize);\r\n\t\tpend->inbuffersize -= headersize;\r\n\r\n\t\tif (colon)\r\n\t\t{\r\n\t\t\t//carries unreliable packets\r\n\t\t\tprintf(\"wsconnect\\n\");\r\n\t\t\tQW_TCPConnection(cluster, pend, colon);\r\n\t\t\treturn true;\r\n\t\t}\r\n\t\treturn SV_ReadPendingProxy(cluster, pend);\r\n\t}\r\n\r\n\traw = false;\r\n\r\n\tqtv = pend->defaultstream;\r\n\r\n\te = pend->inbuffer;\r\n\ts = e;\r\n\twhile(*e)\r\n\t{\r\n\t\tif (*e == '\\n' || *e == '\\r')\r\n\t\t{\r\n\t\t\t*e = '\\0';\r\n\t\t\tcolon = strchr((char*)s, ':');\r\n\t\t\tif (*s)\r\n\t\t\t{\r\n\t\t\t\tif (!colon)\r\n\t\t\t\t{\r\n\t\t\t\t\tif (!ustrcmp(s, \"QTV\"))\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\t//just a qtv request (as in, not http or some other protocol)\r\n\t\t\t\t\t}\r\n\t\t\t\t\telse if (!ustrcmp(s, \"SOURCELIST\"))\r\n\t\t\t\t\t{\t//lists sources that are currently playing\r\n\t\t\t\t\t\tNet_ProxySendString(cluster, pend, QTVSVHEADER);\r\n\t\t\t\t\t\tif (!cluster->servers)\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tNet_ProxySendString(cluster, pend, \"PERROR: No sources currently available\\n\");\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\telse\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tfor (qtv = cluster->servers; qtv; qtv = qtv->next)\r\n\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\tif (clientversion > 1)\r\n\t\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\t\tint plyrs = 0;\r\n\t\t\t\t\t\t\t\t\tint prox = 0;\r\n\t\t\t\t\t\t\t\t\tint i;\r\n\t\t\t\t\t\t\t\t\toproxy_t *o;\r\n\t\t\t\t\t\t\t\t\tfor (i = 0; i < MAX_CLIENTS; i++)\r\n\t\t\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\t\t\tif (*qtv->map.players[i].userinfo)\r\n\t\t\t\t\t\t\t\t\t\t\tplyrs++;\r\n\t\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t\t\tfor (o = qtv->proxies; o; o = o->next)\r\n\t\t\t\t\t\t\t\t\t\tprox++;\r\n\t\t\t\t\t\t\t\t\tsprintf(tempbuf, \"SRCSRV: %s\\n\", qtv->server);\r\n\t\t\t\t\t\t\t\t\tNet_ProxySendString(cluster, pend, tempbuf);\r\n\t\t\t\t\t\t\t\t\tsprintf(tempbuf, \"SRCHOST: %s\\n\", qtv->map.hostname);\r\n\t\t\t\t\t\t\t\t\tNet_ProxySendString(cluster, pend, tempbuf);\r\n\t\t\t\t\t\t\t\t\tsprintf(tempbuf, \"SRCPLYRS: %i\\n\", plyrs);\r\n\t\t\t\t\t\t\t\t\tNet_ProxySendString(cluster, pend, tempbuf);\r\n\t\t\t\t\t\t\t\t\tsprintf(tempbuf, \"SRCVIEWS: %i\\n\", qtv->numviewers+prox);\r\n\t\t\t\t\t\t\t\t\tNet_ProxySendString(cluster, pend, tempbuf);\r\n\t\t\t\t\t\t\t\t\tsprintf(tempbuf, \"SRCID: %i\\n\", qtv->streamid);\t//final part of each source\r\n\t\t\t\t\t\t\t\t\tNet_ProxySendString(cluster, pend, tempbuf);\r\n\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t\telse\r\n\t\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\t\tsprintf(tempbuf, \"ASOURCE: %i: %15s: %15s\\n\", qtv->streamid, qtv->server, qtv->map.hostname);\r\n\t\t\t\t\t\t\t\t\tNet_ProxySendString(cluster, pend, tempbuf);\r\n\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\tqtv = NULL;\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\tNet_ProxySendString(cluster, pend, \"\\n\");\r\n\t\t\t\t\t\tpend->flushing = true;\r\n\t\t\t\t\t}\r\n\t\t\t\t\telse if (!ustrcmp(s, \"REVERSE\"))\r\n\t\t\t\t\t{\t//this is actually a server trying to connect to us\r\n\t\t\t\t\t\t//start up a new stream\r\n\r\n\t\t\t\t\t\tif (cluster->reverseallowed)\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tqtv = QTV_NewServerConnection(cluster, 0, \"reverse\"/*server*/, \"\", true, AD_REVERSECONNECT, false, 0);\r\n\r\n\t\t\t\t\t\t\tNet_ProxySendString(cluster, pend, QTVSVHEADER);\r\n\t\t\t\t\t\t\tNet_ProxySendString(cluster, pend, \"VERSION: 1\\n\");\r\n\t\t\t\t\t\t\tNet_ProxySendString(cluster, pend, \"REVERSED\\n\");\r\n\t\t\t\t\t\t\tNet_ProxySendString(cluster, pend, \"\\n\");\r\n\r\n\t\t\t\t\t\t\t//switch over the socket to the actual source connection rather than the pending\r\n\t\t\t\t\t\t\tNet_TryFlushProxyBuffer(cluster, pend); //flush anything... this isn't ideal, but should be small enough\r\n\t\t\t\t\t\t\tqtv->sourcesock = pend->sock;\r\n\t\t\t\t\t\t\tpend->sock = INVALID_SOCKET;\r\n\r\n\t\t\t\t\t\t\tmemcpy(qtv->buffer, pend->inbuffer + headersize, pend->inbuffersize - headersize);\r\n\t\t\t\t\t\t\tqtv->parsingqtvheader = true;\r\n\t\t\t\t\t\t\treturn false;\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\telse\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tNet_ProxySendString(cluster, pend, QTVSVHEADER\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"PERROR: Reverse connections are disabled on this proxy\\n\");\r\n\t\t\t\t\t\t\tpend->flushing = true;\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t}\r\n\t\t\t\t\telse if (!ustrcmp(s, \"RECEIVE\"))\r\n\t\t\t\t\t{\t//a client connection request without a source\r\n\t\t\t\t\t\tif (cluster->numservers == 1)\r\n\t\t\t\t\t\t{\t//only one stream anyway\r\n\t\t\t\t\t\t\tqtv = cluster->servers;\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\telse\r\n\t\t\t\t\t\t{\t//try and hunt down an explicit stream (rather than a user-recorded one)\r\n\t\t\t\t\t\t\tint numfound = 0;\r\n\t\t\t\t\t\t\tsv_t *suitable = NULL;\t//shush noisy compilers\r\n\t\t\t\t\t\t\tfor (qtv = cluster->servers; qtv; qtv = qtv->next)\r\n\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\tif (qtv->autodisconnect == AD_NO)\r\n\t\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\t\tsuitable = qtv;\r\n\t\t\t\t\t\t\t\t\tnumfound++;\r\n\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\tif (numfound == 1)\r\n\t\t\t\t\t\t\t\tqtv = suitable;\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\tif (!qtv)\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tNet_ProxySendString(cluster, pend, QTVSVHEADER);\r\n\t\t\t\t\t\t\tNet_ProxySendString(cluster, pend, \"PERROR: Multiple streams are currently playing\\n\");\r\n\t\t\t\t\t\t\tNet_ProxySendString(cluster, pend, \"\\n\");\r\n\t\t\t\t\t\t\tpend->flushing = true;\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t}\r\n\t\t\t\t\telse if (!ustrcmp(s, \"DEMOLIST\"))\r\n\t\t\t\t\t{\t//lists sources that are currently playing\r\n\t\t\t\t\t\tint i;\r\n\r\n\t\t\t\t\t\tCluster_BuildAvailableDemoList(cluster);\r\n\r\n\t\t\t\t\t\tNet_ProxySendString(cluster, pend, QTVSVHEADER);\r\n\t\t\t\t\t\tif (!cluster->availdemoscount)\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tNet_ProxySendString(cluster, pend, \"PERROR: No demos currently available\\n\");\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\telse\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tfor (i = 0; i < cluster->availdemoscount; i++)\r\n\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\tsprintf(tempbuf, \"ADEMO: %i: %15s\\n\", cluster->availdemos[i].size, cluster->availdemos[i].name);\r\n\t\t\t\t\t\t\t\tNet_ProxySendString(cluster, pend, tempbuf);\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\tqtv = NULL;\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\tNet_ProxySendString(cluster, pend, \"\\n\");\r\n\t\t\t\t\t\tpend->flushing = true;\r\n\t\t\t\t\t}\r\n\t\t\t\t\telse if (!ustrcmp(s, \"AUTH\"))\r\n\t\t\t\t\t{\t//lists the demos available on this proxy\r\n\t\t\t\t\t\t//part of the connection process, can be ignored if there's no password\r\n\t\t\t\t\t}\r\n\t\t\t\t\telse\r\n\t\t\t\t\t\tprintf(\"Unrecognized token in QTV connection request (%s)\\n\", s);\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\t*colon++ = '\\0';\r\n\t\t\t\t\tif (!ustrcmp(s, \"VERSION\"))\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tclientversion = atof(colon);\r\n\t\t\t\t\t}\r\n\t\t\t\t\telse if (!ustrcmp(s, \"RAW\"))\r\n\t\t\t\t\t\traw = atoi(colon);\r\n\t\t\t\t\t/*else if (!ustrcmp(s, \"ROUTE\"))\r\n\t\t\t\t\t{\t//pure rewroute...\r\n\t\t\t\t\t\t//is this safe? probably not.\r\n\t\t\t\t\t\ts = QTVSVHEADER\r\n\t\t\t\t\t\t\t\"PERROR: ROUTE command not yet implemented\\n\"\r\n\t\t\t\t\t\t\t\"\\n\";\r\n\t\t\t\t\t\tNet_ProxySend(cluster, pend, s, ustrlen(s));\r\n\t\t\t\t\t\tpend->flushing = true;\r\n\t\t\t\t\t}\r\n\t\t\t\t\t*/\r\n\t\t\t\t\telse if (!ustrcmp(s, \"SOURCE\"))\r\n\t\t\t\t\t{\t//connects, creating a new source\r\n\t\t\t\t\t\tchar *t;\r\n\t\t\t\t\t\twhile (*colon == ' ')\r\n\t\t\t\t\t\t\tcolon++;\r\n\t\t\t\t\t\tfor (t = colon; *t; t++)\r\n\t\t\t\t\t\t\tif (*t < '0' || *t > '9')\r\n\t\t\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t\tif (*t)\r\n\t\t\t\t\t\t\tqtv = QTV_NewServerConnection(cluster, 0, colon, \"\", false, AD_WHENEMPTY, true, false);\r\n\t\t\t\t\t\telse\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t//numerical source, use a stream id.\r\n\t\t\t\t\t\t\tfor (qtv = cluster->servers; qtv; qtv = qtv->next)\r\n\t\t\t\t\t\t\t\tif (qtv->streamid == atoi(colon))\r\n\t\t\t\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t}\r\n\t\t\t\t\telse if (!ustrcmp(s, \"DEMO\"))\r\n\t\t\t\t\t{\t//starts a demo off the server... source does the same thing though...\r\n\t\t\t\t\t\tchar buf[256];\r\n\t\r\n\t\t\t\t\t\tsnprintf(buf, sizeof(buf), \"demo:%s\", colon);\r\n\t\t\t\t\t\tqtv = QTV_NewServerConnection(cluster, 0, buf, \"\", false, AD_WHENEMPTY, true, false);\r\n\t\t\t\t\t\tif (!qtv)\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tNet_ProxySendString(cluster, pend,\tQTVSVHEADER\r\n\t\t\t\t\t\t\t\t\t\t\t\t\"PERROR: couldn't open demo\\n\"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\"\\n\");\r\n\t\t\t\t\t\t\tpend->flushing = true;\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t}\r\n\t\t\t\t\telse if (!ustrcmp(s, \"AUTH\"))\r\n\t\t\t\t\t{\t//lists the demos available on this proxy\r\n\t\t\t\t\t\t//part of the connection process, can be ignored if there's no password\r\n\t\t\t\t\t}\r\n\t\t\t\t\telse\r\n\t\t\t\t\t\tprintf(\"Unrecognized token in QTV connection request (%s)\\n\", s);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\ts = e+1;\r\n\t\t}\r\n\r\n\t\te++;\r\n\t}\r\n\r\n\tif (!pend->flushing)\r\n\t{\r\n\t\tif (clientversion < 1)\r\n\t\t{\r\n\t\t\tNet_ProxySendString(cluster, pend,\tQTVSVHEADER\r\n\t\t\t\t\t\t\t\t\"PERROR: Requested protocol version not supported\\n\"\r\n\t\t\t\t\t\t\t\t\"\\n\");\r\n\t\t\tpend->flushing = true;\r\n\t\t}\r\n\t\telse if (!qtv)\r\n\t\t{\r\n\t\t\tNet_ProxySendString(cluster, pend,\tQTVSVHEADER\r\n\t\t\t\t\t\t\t\t\"PERROR: No stream selected\\n\"\r\n\t\t\t\t\t\t\t\t\"\\n\");\r\n\t\t\tpend->flushing = true;\r\n\t\t}\r\n\t}\r\n\tif (pend->flushing)\r\n\t\treturn false;\r\n\r\n\r\n\tif (qtv->usequakeworldprotocols)\r\n\t{\r\n\t\tNet_ProxySendString(cluster, pend,\tQTVSVHEADER\r\n\t\t\t\t\t\t\t\"PERROR: This version of QTV is unable to convert QuakeWorld to QTV protocols\\n\"\r\n\t\t\t\t\t\t\t\"\\n\");\r\n\t\tpend->flushing = true;\r\n\t\treturn false;\r\n\t}\r\n\tif (cluster->maxproxies>=0 && cluster->numproxies >= cluster->maxproxies)\r\n\t{\r\n\t\tNet_ProxySendString(cluster, pend,\tQTVSVHEADER\r\n\t\t\t\t\t\t\t\"TERROR: This QTV has reached it's connection limit\\n\"\r\n\t\t\t\t\t\t\t\"\\n\");\r\n\t\tpend->flushing = true;\r\n\t\treturn false;\r\n\t}\r\n\r\n\tpend->next = qtv->proxies;\r\n\tqtv->proxies = pend;\r\n\r\n\tif (!raw)\r\n\t{\r\n\t\tNet_ProxySendString(cluster, pend,\tQTVSVHEADER);\r\n\t\tNet_ProxySendString(cluster, pend,\t\"BEGIN: \");\r\n\t\tNet_ProxySendString(cluster, pend,\tqtv->server);\r\n\t\tNet_ProxySendString(cluster, pend,\t\"\\n\\n\");\r\n\t}\r\n//\telse if (passwordprotected)\t//raw mode doesn't support passwords, so reject them\r\n//\t{\r\n//\t\tpend->flushing = true;\r\n//\t\treturn;\r\n//\t}\r\n\r\n\tpend->stream = qtv;\r\n\r\n\tmemmove(pend->inbuffer, pend->inbuffer+headersize, pend->inbuffersize-headersize);\r\n\tpend->inbuffersize -= headersize;\r\n\r\n\tNet_GreetingMessage(pend);\r\n\tNet_SendConnectionMVD(qtv, pend);\r\n\r\n\treturn true;\r\n}\r\n"
  },
  {
    "path": "fteqtv/httpsv.c",
    "content": "#include \"qtv.h\"\r\n\r\n#define WEBPORTMANIFEST\t\"https://triptohell.info/demo.fmf\"\r\n\r\n//#define WEBPORTMANIFEST\t\"http://localhost:8080/demo.fmf\"\r\n\r\n//main reason to use connection close is because we're lazy and don't want to give sizes in advance (yes, we could use chunks..)\r\n\r\nsize_t SHA1(unsigned char *digest, size_t maxdigestsize, const unsigned char *string, size_t stringlen);\r\n\r\nvoid tobase64(unsigned char *out, int outlen, unsigned char *in, int inlen)\r\n{\r\n\tstatic unsigned char tab[64] =\r\n\t{\r\n\t\t'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P',\r\n\t\t'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f',\r\n\t\t'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v',\r\n\t\t'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/'\r\n\t};\r\n\tunsigned int usedbits = 0;\r\n\tunsigned int val = 0;\r\n\toutlen--;\r\n\twhile(inlen)\r\n\t{\r\n\t\twhile(usedbits < 24 && inlen)\r\n\t\t{\r\n\t\t\tval <<= 8;\r\n\t\t\tval |= (*in++);\r\n\t\t\tinlen--;\r\n\t\t\tusedbits += 8;\r\n\t\t}\r\n\t\tif (outlen < 4)\r\n\t\t\treturn;\r\n\t\tval <<= 24 - usedbits;\r\n\r\n\t\t*out++ = (usedbits > 0)?tab[(val>>18)&0x3f]:'=';\r\n\t\t*out++ = (usedbits > 6)?tab[(val>>12)&0x3f]:'=';\r\n\t\t*out++ = (usedbits > 12)?tab[(val>>6)&0x3f]:'=';\r\n\t\t*out++ = (usedbits > 18)?tab[(val>>0)&0x3f]:'=';\r\n\t\tval=0;\r\n\t\tusedbits = 0;\r\n\t}\r\n\t*out = 0;\r\n}\r\n\r\nstatic const char qfont_table[256] = {\r\n\t'\\0', '#', '#', '#', '#', '.', '#', '#',\r\n\t'#', 9, 10, '#', ' ', 13, '.', '.',\r\n\t'[', ']', '0', '1', '2', '3', '4', '5',\r\n\t'6', '7', '8', '9', '.', '<', '=', '>',\r\n\t' ', '!', '\"', '#', '$', '%', '&', '\\'',\r\n\t'(', ')', '*', '+', ',', '-', '.', '/',\r\n\t'0', '1', '2', '3', '4', '5', '6', '7',\r\n\t'8', '9', ':', ';', '<', '=', '>', '?',\r\n\t'@', 'A', 'B', 'C', 'D', 'E', 'F', 'G',\r\n\t'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',\r\n\t'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',\r\n\t'X', 'Y', 'Z', '[', '\\\\', ']', '^', '_',\r\n\t'`', 'a', 'b', 'c', 'd', 'e', 'f', 'g',\r\n\t'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',\r\n\t'p', 'q', 'r', 's', 't', 'u', 'v', 'w',\r\n\t'x', 'y', 'z', '{', '|', '}', '~', '<',\r\n \r\n\t'<', '=', '>', '#', '#', '.', '#', '#',\r\n\t'#', '#', ' ', '#', ' ', '>', '.', '.',\r\n\t'[', ']', '0', '1', '2', '3', '4', '5',\r\n\t'6', '7', '8', '9', '.', '<', '=', '>',\r\n\t' ', '!', '\"', '#', '$', '%', '&', '\\'',\r\n\t'(', ')', '*', '+', ',', '-', '.', '/',\r\n\t'0', '1', '2', '3', '4', '5', '6', '7',\r\n\t'8', '9', ':', ';', '<', '=', '>', '?',\r\n\t'@', 'A', 'B', 'C', 'D', 'E', 'F', 'G',\r\n\t'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',\r\n\t'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',\r\n\t'X', 'Y', 'Z', '[', '\\\\', ']', '^', '_',\r\n\t'`', 'a', 'b', 'c', 'd', 'e', 'f', 'g',\r\n\t'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',\r\n\t'p', 'q', 'r', 's', 't', 'u', 'v', 'w',\r\n\t'x', 'y', 'z', '{', '|', '}', '~', '<'\r\n};\r\n\r\nstatic void HTMLprintf(char *outb, int outl, char *fmt, ...)\r\n{\r\n\tva_list val;\r\n\tchar qfmt[8192*4];\r\n\tchar *inb = qfmt;\r\n\tunsigned char inchar;\r\n\r\n\tva_start(val, fmt);\r\n\tvsnprintf(qfmt, sizeof(qfmt), fmt, val);\r\n\tva_end(val);\r\n\tqfmt[sizeof(qfmt)-1] = 0;\r\n\r\n\toutl--;\r\n\toutl -= 5;\r\n\twhile (outl > 0 && *inb)\r\n\t{\r\n\t\tinchar = qfont_table[*(unsigned char*)inb];\r\n\t\tif (inchar == '<')\r\n\t\t{\r\n\t\t\t*outb++ = '&';\r\n\t\t\t*outb++ = 'l';\r\n\t\t\t*outb++ = 't';\r\n\t\t\t*outb++ = ';';\r\n\t\t\toutl -= 4;\r\n\t\t}\r\n\t\telse if (inchar == '>')\r\n\t\t{\r\n\t\t\t*outb++ = '&';\r\n\t\t\t*outb++ = 'g';\r\n\t\t\t*outb++ = 't';\r\n\t\t\t*outb++ = ';';\r\n\t\t\toutl -= 4;\r\n\t\t}\r\n\t\telse if (inchar == '\\n')\r\n\t\t{\r\n\t\t\t*outb++ = '<';\r\n\t\t\t*outb++ = 'b';\r\n\t\t\t*outb++ = 'r';\r\n\t\t\t*outb++ = '/';\r\n\t\t\t*outb++ = '>';\r\n\t\t\toutl -= 5;\r\n\t\t}\r\n\t\telse if (inchar == '&')\r\n\t\t{\r\n\t\t\t*outb++ = '&';\r\n\t\t\t*outb++ = 'a';\r\n\t\t\t*outb++ = 'm';\r\n\t\t\t*outb++ = 'p';\r\n\t\t\t*outb++ = ';';\r\n\t\t\toutl -= 5;\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\t*outb++ = inchar;\r\n\t\t\toutl -= 1;\r\n\t\t}\r\n\t\tinb++;\r\n\t}\r\n\t*outb++ = 0;\r\n}\r\n\r\nstatic void HTTPSV_SendHTTPHeader(cluster_t *cluster, oproxy_t *dest, char *error_code, char *content_type, int csize, qboolean nocache)\r\n{\r\n\tchar *s;\r\n\tchar buffer[2048];\r\n\r\n\tif (nocache)\r\n\t{\r\n\t\tif (csize >= 0)\r\n\t\t{\r\n\t\t\ts =\t\"HTTP/1.1 %s OK\\r\\n\"\r\n\t\t\t\t\"Content-Type: %s\\r\\n\"\r\n\t\t\t\t\"Content-Length: %i\\r\\n\"\r\n\t\t\t\t\"Cache-Control: no-cache, must-revalidate\\r\\n\"\r\n\t\t\t\t\"Expires: Mon, 26 Jul 1997 05:00:00 GMT\\r\\n\"\r\n\t\t\t\t\"\\r\\n\";\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\ts =\t\"HTTP/1.1 %s OK\\r\\n\"\r\n\t\t\t\t\"Content-Type: %s\\r\\n\"\r\n\t\t\t\t\"Cache-Control: no-cache, must-revalidate\\r\\n\"\r\n\t\t\t\t\"Expires: Mon, 26 Jul 1997 05:00:00 GMT\\r\\n\"\r\n\t\t\t\t\"Connection: close\\r\\n\"\r\n\t\t\t\t\"\\r\\n\";\r\n\t\t}\r\n\t}\r\n\telse\r\n\t{\r\n\t\tif (csize >= 0)\r\n\t\t{\r\n\t\t\ts =\t\"HTTP/1.1 %s OK\\r\\n\"\r\n\t\t\t\t\"Cache-Control: public, max-age=3600\\r\\n\"\r\n\t\t\t\t\"Content-Type: %s\\r\\n\"\r\n\t\t\t\t\"Content-Length: %i\\r\\n\"\r\n\t\t\t\t\"\\r\\n\";\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\ts =\t\"HTTP/1.1 %s OK\\r\\n\"\r\n\t\t\t\t\"Cache-Control: public, max-age=3600\\r\\n\"\r\n\t\t\t\t\"Content-Type: %s\\r\\n\"\r\n\t\t\t\t\"Connection: close\\r\\n\"\r\n\t\t\t\t\"\\r\\n\";\r\n\t\t}\r\n\t}\r\n\r\n\tsnprintf(buffer, sizeof(buffer), s, error_code, content_type, csize);\r\n\r\n\tNet_ProxySend(cluster, dest, buffer, strlen(buffer));\r\n}\r\n\r\nstatic void HTTPSV_SendHTMLHeader(cluster_t *cluster, oproxy_t *dest, char *title, char *args)\r\n{\r\n\tchar *s;\r\n\tchar buffer[2048];\r\n\r\n\tqboolean plugin = false;\r\n\twhile (*args && *args != ' ')\r\n\t{\r\n\t\tif (*args == 'p')\r\n\t\t\t plugin = true;\r\n\t\targs++;\r\n\t}\r\n\r\n\ts =\t\"<!DOCTYPE HTML>\\n\"\r\n\t\t\"<html>\\n\"\r\n\t\t\"<head>\\n\"\r\n\t\t\"  <meta http-equiv=\\\"content-type\\\" content=\\\"text/html; charset=iso-8859-1\\\">\\n\"\r\n\t\t\"  <title>%s</title>\\n\"\r\n\t\t\"  <link rel=\\\"StyleSheet\\\" href=\\\"/style.css\\\" type=\\\"text/css\\\" />\\n\"\r\n\t\t\"</head>\\n\"\r\n\t\t\"<body><div id=\\\"navigation\\\"><ul>\"\r\n\t\t\"<li><a href=\\\"/nowplaying.html%s\\\">Live</a></li>\"\r\n\t\t\"<li><a href=\\\"/demos.html%s\\\">Demos</a></li>\"\r\n\t\t\"%s\"\r\n\t\t/*\"%s\"*/\r\n\t\t\"</ul></div>\";\r\n\r\n\tsnprintf(buffer, sizeof(buffer), s, title, \r\n\r\n\t\tplugin?\"?p\":\"\", \r\n\t\tplugin?\"?p\":\"\", \r\n\t\t(!*cluster->adminpassword)?\"\":(plugin?\"<li><a href=\\\"/admin.html?p\\\">Admin</a></li>\":\"<li><a href=\\\"/admin.html\\\">Admin</a></li>\")/*,\r\n\t\tplugin?\"<li><a target=\\\"_top\\\" href=\\\"/nowplaying.html\\\">Basic</a></li>\":\"<li><a href=\\\"/plugin.html\\\">Plugin</a></li>\"*/\r\n\t\t\r\n\t\t);\r\n\r\n\tNet_ProxySend(cluster, dest, buffer, strlen(buffer));\r\n}\r\n\r\nstatic void HTTPSV_SendHTMLFooter(cluster_t *cluster, oproxy_t *dest)\r\n{\r\n\tchar *s;\r\n\tchar buffer[2048];\r\n\r\n\t/*Proxy version*/\r\n\tsnprintf(buffer, sizeof(buffer), \"<br/>Server Version: \"QTV_VERSION_STRING\" <a href=\\\"\"PROXYWEBSITE\"\\\" target=\\\"_blank\\\">\"PROXYWEBSITE\"</a>\");\r\n\tNet_ProxySend(cluster, dest, buffer, strlen(buffer));\r\n\r\n\t/*terminate html page*/\r\n\ts = \"</body>\\n\"\r\n\t\t\"</html>\\n\";\r\n\tNet_ProxySend(cluster, dest, s, strlen(s));\r\n}\r\n\r\n#define HTMLPRINT(str) Net_ProxySend(cluster, dest, str \"\\n\", strlen(str \"\\n\"))\r\n#define HTTPPRINT(str) Net_ProxySend(cluster, dest, str, strlen(str))\r\n\r\nstatic void HTTPSV_GenerateNowPlaying(cluster_t *cluster, oproxy_t *dest, char *args)\r\n{\r\n\tint player;\r\n\tchar *s;\r\n\tchar buffer[1024];\r\n\tchar plname[64];\r\n\tsv_t *streams;\r\n\tqboolean plugin = false;\r\n\tqboolean activeonly = false;\r\n\r\n\tHTTPSV_SendHTTPHeader(cluster, dest, \"200\", \"text/html\", -1, true);\r\n\tHTTPSV_SendHTMLHeader(cluster, dest, \"QuakeTV: Now Playing\", args);\r\n\r\n\twhile (*args && *args != ' ')\r\n\t{\r\n\t\tif (*args == 'p')\r\n\t\t\t plugin = true;\r\n\t\telse if (*args == 'a')\r\n\t\t\t activeonly = true;\r\n\t\targs++;\r\n\t}\r\n\r\n\tif (!strcmp(cluster->hostname, DEFAULT_HOSTNAME))\r\n\t\tsnprintf(buffer, sizeof(buffer), \"<h1>QuakeTV: Now Playing</h1>\");\t//don't show the hostname if its set to the default\r\n\telse\r\n\t\tsnprintf(buffer, sizeof(buffer), \"<h1>%s: Now Playing</h1>\", cluster->hostname);\r\n\tNet_ProxySend(cluster, dest, buffer, strlen(buffer));\r\n\r\n\tHTMLPRINT(\"<dl class=\\\"nowplaying\\\">\");\r\n\tfor (streams = cluster->servers; streams; streams = streams->next)\r\n\t{\r\n\t\tif (activeonly)\r\n\t\t{\r\n\t\t\tfor (player = 0; player < MAX_CLIENTS; player++)\r\n\t\t\t{\r\n\t\t\t\tif (streams->isconnected && streams->map.thisplayer == player)\r\n\t\t\t\t\tcontinue;\r\n\t\t\t\tif (*streams->map.players[player].userinfo)\r\n\t\t\t\t{\r\n\t\t\t\t\tbreak;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\tif (player == MAX_CLIENTS)\r\n\t\t\t\tcontinue;\r\n\t\t}\r\n\t\tHTMLPRINT(\"<dt>\");\r\n\t\tHTMLprintf(buffer, sizeof(buffer), \"%s (%s: %s)\", streams->server, streams->map.gamedir, streams->map.mapname);\r\n\t\tNet_ProxySend(cluster, dest, buffer, strlen(buffer));\r\n\t\tif (plugin && !strncmp(streams->server, \"tcp:\", 4))\r\n\t\t{\r\n\t\t\tsnprintf(buffer, sizeof(buffer), \"<span class=\\\"qtvfile\\\"> [ <a onclick=\\\"parent.joinstream('%s')\\\">Join</a> ]</span>\", streams->server);\r\n\t\t\tNet_ProxySend(cluster, dest, buffer, strlen(buffer));\r\n\t\t}\r\n\t\tif (plugin && !strncmp(streams->server, \"udp:\", 4))\r\n\t\t\tsnprintf(buffer, sizeof(buffer), \"<span class=\\\"qtvfile\\\"> [ <a href=\\\"javascript:parent.joinserver('%s')\\\">Join</a> ]</span>\", streams->server+4);\r\n\t\telse\r\n\t\t\tsnprintf(buffer, sizeof(buffer), \"<span class=\\\"qtvfile\\\"> [ <a href=\\\"/watch.qtv?sid=%i\\\">Watch Now</a>]</span>\", streams->streamid);\r\n\t\tNet_ProxySend(cluster, dest, buffer, strlen(buffer));\r\n\t\tHTMLPRINT(\"</dt><dd><ul class=\\\"playerslist\\\">\");\r\n\r\n\t\tfor (player = 0; player < MAX_CLIENTS; player++)\r\n\t\t{\r\n\t\t\tif (streams->isconnected && streams->map.thisplayer == player)\r\n\t\t\t\tcontinue;\r\n\t\t\tif (*streams->map.players[player].userinfo)\r\n\t\t\t{\r\n\t\t\t\tInfo_ValueForKey(streams->map.players[player].userinfo, \"name\", plname, sizeof(plname));\r\n\r\n\t\t\t\tif (streams->map.players[player].frags < -90)\r\n\t\t\t\t{\r\n\t\t\t\t\tHTMLPRINT(\"<li class=\\\"spectator\\\">\");\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\tHTMLPRINT(\"<li class=\\\"player\\\">\");\r\n\t\t\t\t}\r\n\r\n\t\t\t\tHTMLprintf(buffer, sizeof(buffer), \"%s\", plname);\r\n\t\t\t\tNet_ProxySend(cluster, dest, buffer, strlen(buffer));\r\n\t\t\t\tHTMLPRINT(\"</li>\");\r\n\t\t\t}\r\n\t\t}\r\n\t\tHTMLPRINT(\"</ul></dd>\");\r\n\t}\r\n\tHTMLPRINT(\"</dl>\");\r\n\tif (!cluster->servers)\r\n\t{\r\n\t\ts = \"No streams are currently being played<br />\";\r\n\t\tNet_ProxySend(cluster, dest, s, strlen(s));\r\n\t}\r\n\r\n\tHTTPSV_SendHTMLFooter(cluster, dest);\r\n}\r\n\r\nstatic void HTTPSV_GenerateCSSFile(cluster_t *cluster, oproxy_t *dest)\r\n{\r\n    HTTPSV_SendHTTPHeader(cluster, dest, \"200\", \"text/css\", -1, false);\r\n\r\n    HTMLPRINT(\"* { font-family: Verdana, Helvetica, sans-serif; }\");\r\n    HTMLPRINT(\"body { color: #000; background-color: #fff; padding: 0 0px; }\");\r\n    HTMLPRINT(\"a { color: #00f; }\");\r\n    HTMLPRINT(\"div.topdiv { display:flex; align-items: stretch; position: absolute; top: 0; right: 0; bottom: 0; left: 0; }\");\r\n    HTMLPRINT(\"div.left { resize: horizontal; overflow: auto; flex 0 0 25%;}\");\r\n    HTMLPRINT(\"div.right { padding:0; margin: 0; flex: auto; }\");\r\n    HTMLPRINT(\"a.qtvfile { font-weight: bold; }\");\r\n    HTMLPRINT(\"a:visited { color: #00f; }\");\r\n    HTMLPRINT(\"a:hover { background-color: black; color: yellow; }\");\r\n    HTMLPRINT(\"li.spectator { color: #666; font-size: 0.9ex; }\");\r\n    HTMLPRINT(\"dl.nowplaying dd { margin: 0 0 2em 0; }\");\r\n    HTMLPRINT(\"dl.nowplaying dt { margin: 1em 0 0 0; font-size: 1.1em; font-weight: bold; }\");\r\n    HTMLPRINT(\"dl.nowplaying li { list-style: none; margin: 0 0 0 1em; padding: 0; }\");\r\n    HTMLPRINT(\"dl.nowplaying ul { margin: 0 0 0 1em; padding: 0; }\");\r\n    HTMLPRINT(\"canvas.emscripten { border: 0px none; padding:0; margin: 0; width: 100%; height: 100%;}\");\r\n    HTMLPRINT(\"#navigation { background-color: #eef; }\");\r\n    HTMLPRINT(\"#navigation li { display: inline; list-style: none; margin: 0 3em; }\");\r\n}\r\n\r\nstatic qboolean HTTPSV_GetHeaderField(char *s, char *field, char *buffer, int buffersize)\r\n{\r\n\tchar *e;\r\n\tchar *colon;\r\n\tint fieldnamelen = strlen(field);\r\n\r\n\tbuffer[0] = 0;\r\n\r\n\te = s;\r\n\twhile(*e)\r\n\t{\r\n\t\tif (*e == '\\n')\r\n\t\t{\r\n\t\t\t*e = '\\0';\r\n\t\t\tcolon = strchr(s, ':');\r\n\t\t\t*e = '\\n';\r\n\t\t\tif (!colon)\r\n\t\t\t{\r\n\t\t\t\tif (!strncmp(field, s, fieldnamelen))\r\n\t\t\t\t{\r\n\t\t\t\t\tif (s[fieldnamelen] <= ' ')\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\treturn true;\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tif (fieldnamelen == colon - s)\r\n\t\t\t\t{\r\n\t\t\t\t\tif (!strncmp(field, s, colon-s))\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tcolon++;\r\n\t\t\t\t\t\twhile (*colon == ' ')\r\n\t\t\t\t\t\t\tcolon++;\r\n\t\t\t\t\t\twhile (buffersize > 2)\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tif (!*colon || *colon == '\\r' || *colon == '\\n')\r\n\t\t\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t\t\t*buffer++ = *colon++;\r\n\t\t\t\t\t\t\tbuffersize--;\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\t*buffer = 0;\r\n\t\t\t\t\t\treturn true;\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\r\n\t\t\t}\r\n\t\t\ts = e+1;\r\n\t\t}\r\n\r\n\t\te++;\r\n\t}\r\n\treturn false;\r\n}\r\nstatic qboolean HTTPSV_GetHeaderCommaField(char *s, char **ctx, char *field, char *buffer, int buffersize)\r\n{\t//some http header fields are a,b,c,d\\ne,f type lists. this function will read one token despite them being split between multiple headers.\r\n\tchar *e;\t//end of current line...\r\n\tchar *colon;\r\n\tint fieldnamelen = strlen(field);\r\n\r\n\tbuffer[0] = 0;\r\n\r\n\tif (*ctx)\r\n\t\te = *ctx;\r\n\telse\r\n\t\te = s;\r\n\tif (*e == ',')\r\n\t{\r\n\t\tcolon = e;\r\n\t\tgoto foundfield;\r\n\t}\r\n\twhile(*e)\r\n\t{\r\n\t\tif (*e == '\\n')\r\n\t\t{\r\n\t\t\t*e = '\\0';\r\n\t\t\tcolon = strchr(s, ':');\r\n\t\t\t*e = '\\n';\r\n\t\t\tif (colon)\r\n\t\t\t{\r\n\t\t\t\tif (fieldnamelen == colon - s)\r\n\t\t\t\t{\r\n\t\t\t\t\tif (!strncmp(field, s, colon-s))\r\n\t\t\t\t\t{\r\nfoundfield:\r\n\t\t\t\t\t\tcolon++;\r\n\t\t\t\t\t\twhile (*colon == ' ')\r\n\t\t\t\t\t\t\tcolon++;\r\n\t\t\t\t\t\twhile (buffersize > 2)\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tif (!*colon || *colon == '\\r' || *colon == '\\n' || *colon == ',')\r\n\t\t\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t\t\t*buffer++ = *colon++;\r\n\t\t\t\t\t\t\tbuffersize--;\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\t*buffer = 0;\r\n\t\t\t\t\t\t*ctx = colon;\r\n\t\t\t\t\t\treturn true;\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\r\n\t\t\t}\r\n\t\t\ts = e+1;\r\n\t\t}\r\n\r\n\t\te++;\r\n\t}\r\n\treturn false;\r\n}\r\n\r\nstatic void HTTPSV_GenerateQTVStub(cluster_t *cluster, oproxy_t *dest, char *streamtype, char *streamid)\r\n{\r\n\tchar *s;\r\n\tchar hostname[128];\r\n\tchar buffer[1024];\r\n\r\n\r\n\tchar fname[256];\r\n\ts = fname;\r\n\twhile (*streamid > ' ')\r\n\t{\r\n\t\tif (s > fname + sizeof(fname)-4)\t//4 cos I'm too lazy to work out what the actual number should be\r\n\t\t\tbreak;\r\n\t\tif (*streamid == '%')\r\n\t\t{\r\n\t\t\t*s = 0;\r\n\t\t\tstreamid++;\r\n\t\t\tif (*streamid >= 'a' && *streamid <= 'f')\r\n\t\t\t\t*s += 10 + *streamid-'a';\r\n\t\t\telse if (*streamid >= 'A' && *streamid <= 'F')\r\n\t\t\t\t*s += 10 + *streamid-'A';\r\n\t\t\telse if (*streamid >= '0' && *streamid <= '9')\r\n\t\t\t\t*s += *streamid-'0';\r\n\t\t\telse\r\n\t\t\t\tbreak;\r\n\r\n\t\t\t*s <<= 4;\r\n\r\n\t\t\tstreamid++;\r\n\t\t\tif (*streamid >= 'a' && *streamid <= 'f')\r\n\t\t\t\t*s += 10 + *streamid-'a';\r\n\t\t\telse if (*streamid >= 'A' && *streamid <= 'F')\r\n\t\t\t\t*s += 10 + *streamid-'A';\r\n\t\t\telse if (*streamid >= '0' && *streamid <= '9')\r\n\t\t\t\t*s += *streamid-'0';\r\n\t\t\telse\r\n\t\t\t\tbreak;\r\n\r\n\t\t\t//don't let hackers try adding extra commands to it.\r\n\t\t\tif (*s == '$' || *s == ';' || *s == '\\r' || *s == '\\n')\r\n\t\t\t\tcontinue;\r\n\r\n\t\t\tstreamid++;\r\n\t\t\ts++;\r\n\t\t}\r\n\t\telse if (*streamid == '$' || *streamid == ';' || *streamid == '\\r' || *streamid == '\\n')\r\n\t\t{\r\n\t\t\t//don't let hackers try adding extra commands to it.\r\n\t\t\tstreamid++;\r\n\t\t}\r\n\t\telse\r\n\t\t\t*s++ = *streamid++;\r\n\t}\r\n\t*s = 0;\r\n\tstreamid = fname;\r\n\r\n\r\n\tif (!HTTPSV_GetHeaderField((char*)dest->inbuffer, \"Host\", hostname, sizeof(hostname)))\r\n\t{\r\n\t\tHTTPSV_SendHTTPHeader(cluster, dest, \"400\", \"text/html\", -1, true);\r\n\t\tHTTPSV_SendHTMLHeader(cluster, dest, \"QuakeTV: Error\", \"\");\r\n\r\n\t\ts = \"Your client did not send a Host field, which is required in HTTP/1.1\\n<BR />\"\r\n\t\t\t\"Please try a different browser.\\n\"\r\n\t\t\t\"</BODY>\"\r\n\t\t\t\"</HTML>\";\r\n\r\n\t\tNet_ProxySend(cluster, dest, s, strlen(s));\r\n\t\treturn;\r\n\t}\r\n\t/*if there's a port number on there, strip it*/\r\n\tif (strchr(hostname, ':'))\r\n\t\t*strchr(hostname, ':') = 0;\r\n\r\n\tsnprintf(buffer, sizeof(buffer), \"[QTV]\\r\\n\"\r\n\t\t\t\t\t\"Stream: %s%s@%s:%i\\r\\n\"\r\n\t\t\t\t\t\"\", \r\n\t\t\t\t\t//5, \t\t256, \t\t64.\tsnprintf is not required, but paranoia is a wonderful thing.\r\n\t\t\t\t\tstreamtype, streamid, hostname, cluster->tcplistenportnum);\r\n\r\n\tHTTPSV_SendHTTPHeader(cluster, dest, \"200\", \"text/x-quaketvident\", strlen(buffer), false);\r\n\tNet_ProxySend(cluster, dest, buffer, strlen(buffer));\r\n}\r\n\r\nstatic void HTTPSV_GenerateQWSVStub(cluster_t *cluster, oproxy_t *dest, char *method, char *streamid)\r\n{\r\n\tchar *s;\r\n\tchar buffer[1024];\r\n\r\n\r\n\tchar fname[256];\r\n\ts = fname;\r\n\twhile (*streamid > ' ')\r\n\t{\r\n\t\tif (s > fname + sizeof(fname)-4)\t//4 cos I'm too lazy to work out what the actual number should be\r\n\t\t\tbreak;\r\n\t\tif (*streamid == '%')\r\n\t\t{\r\n\t\t\t*s = 0;\r\n\t\t\tstreamid++;\r\n\t\t\tif (*streamid >= 'a' && *streamid <= 'f')\r\n\t\t\t\t*s += 10 + *streamid-'a';\r\n\t\t\telse if (*streamid >= 'A' && *streamid <= 'F')\r\n\t\t\t\t*s += 10 + *streamid-'A';\r\n\t\t\telse if (*streamid >= '0' && *streamid <= '9')\r\n\t\t\t\t*s += *streamid-'0';\r\n\t\t\telse\r\n\t\t\t\tbreak;\r\n\r\n\t\t\t*s <<= 4;\r\n\r\n\t\t\tstreamid++;\r\n\t\t\tif (*streamid <= ' ')\r\n\t\t\t\tbreak;\r\n\t\t\telse if (*streamid >= 'a' && *streamid <= 'f')\r\n\t\t\t\t*s += 10 + *streamid-'a';\r\n\t\t\telse if (*streamid >= 'A' && *streamid <= 'F')\r\n\t\t\t\t*s += 10 + *streamid-'A';\r\n\t\t\telse if (*streamid >= '0' && *streamid <= '9')\r\n\t\t\t\t*s += *streamid-'0';\r\n\t\t\telse\r\n\t\t\t\tbreak;\r\n\r\n\t\t\tstreamid++;\r\n\t\t\ts++;\r\n\t\t}\r\n\t\telse\r\n\t\t\t*s++ = *streamid++;\r\n\t}\r\n\t*s = 0;\r\n\tstreamid = fname;\r\n\r\n\tsnprintf(buffer, sizeof(buffer), \"[QTV]\\r\\n\"\r\n\t\t\t\t\t\"%s: %s\\r\\n\"\r\n\t\t\t\t\t\"\", \r\n\t\t\t\t\tmethod, streamid);\r\n\r\n\tHTTPSV_SendHTTPHeader(cluster, dest, \"200\", \"text/x-quaketvident\", strlen(buffer), false);\r\n\tNet_ProxySend(cluster, dest, buffer, strlen(buffer));\r\n}\r\n\r\nstatic char *HTTPSV_ParsePOST(char *post, char *buffer, int buffersize)\r\n{\r\n\twhile(*post && *post != '&')\r\n\t{\r\n\t\tif (--buffersize>0)\r\n\t\t{\r\n\t\t\tif (*post == '+')\r\n\t\t\t\t*buffer++ = ' ';\r\n\t\t\telse if  (*post == '%')\r\n\t\t\t{\r\n\t\t\t\t*buffer = 0;\r\n\t\t\t\tpost++;\r\n\t\t\t\tif (*post == '\\0' || *post == '&')\r\n\t\t\t\t\tbreak;\r\n\t\t\t\telse if (*post >= 'a' && *post <= 'f')\r\n\t\t\t\t\t*buffer += 10 + *post-'a';\r\n\t\t\t\telse if (*post >= 'A' && *post <= 'F')\r\n\t\t\t\t\t*buffer += 10 + *post-'A';\r\n\t\t\t\telse if (*post >= '0' && *post <= '9')\r\n\t\t\t\t\t*buffer += *post-'0';\r\n\r\n\t\t\t\t*buffer <<= 4;\r\n\r\n\t\t\t\tpost++;\r\n\t\t\t\tif (*post == '\\0' || *post == '&')\r\n\t\t\t\t\tbreak;\r\n\t\t\t\telse if (*post >= 'a' && *post <= 'f')\r\n\t\t\t\t\t*buffer += 10 + *post-'a';\r\n\t\t\t\telse if (*post >= 'A' && *post <= 'F')\r\n\t\t\t\t\t*buffer += 10 + *post-'A';\r\n\t\t\t\telse if (*post >= '0' && *post <= '9')\r\n\t\t\t\t\t*buffer += *post-'0';\r\n\r\n\t\t\t\tbuffer++;\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t\t*buffer++ = *post;\r\n\t\t}\r\n\t\tpost++;\r\n\t}\r\n\t*buffer = 0;\r\n\r\n\treturn post;\r\n}\r\nstatic void HTTPSV_GenerateAdmin(cluster_t *cluster, oproxy_t *dest, int streamid, char *postbody, char *args)\r\n{\r\n\tchar pwd[64];\r\n\tchar cmd[256];\r\n\tchar result[8192];\r\n\tchar *s;\r\n\tchar *o;\r\n\tint passwordokay = false;\r\n\r\n\tif (!*cluster->adminpassword)\r\n\t{\r\n\t\tHTTPSV_SendHTTPHeader(cluster, dest, \"403\", \"text/html\", -1, true);\r\n\t\tHTTPSV_SendHTMLHeader(cluster, dest, \"QuakeTV: Admin Error\", args);\r\n\r\n\t\ts = \"The admin password is disabled. You may not log in remotely.</body></html>\\n\";\r\n\t\tNet_ProxySend(cluster, dest, s, strlen(s));\r\n\t\treturn;\r\n\t}\r\n\t\t\r\n\r\n\tpwd[0] = 0;\r\n\tcmd[0] = 0;\r\n\tif (postbody)\r\n\twhile (*postbody)\r\n\t{\r\n\t\tif (!strncmp(postbody, \"pwd=\", 4))\r\n\t\t{\r\n\t\t\tpostbody = HTTPSV_ParsePOST(postbody+4, pwd, sizeof(pwd));\r\n\t\t}\r\n\t\telse if (!strncmp(postbody, \"cmd=\", 4))\r\n\t\t{\r\n\t\t\tpostbody = HTTPSV_ParsePOST(postbody+4, cmd, sizeof(cmd));\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\twhile(*postbody && *postbody != '&')\r\n\t\t\t{\r\n\t\t\t\tpostbody++;\r\n\t\t\t}\r\n\t\t\tif (*postbody == '&')\r\n\t\t\t\tpostbody++;\r\n\t\t}\r\n\t}\r\n\r\n\tif (!*pwd)\r\n\t{\r\n\t\tif (postbody)\r\n\t\t\to = \"No Password\";\r\n\t\telse\r\n\t\t\to = \"\";\r\n\t}\r\n\telse if (!strcmp(pwd, cluster->adminpassword))\r\n\t{\r\n\t\tpasswordokay = true;\r\n\t\t//small hack (as http connections are considered non-connected proxies)\r\n\t\tcluster->numproxies--;\r\n\t\tif (*cmd)\r\n\t\t\to = Rcon_Command(cluster, NULL, cmd, result, sizeof(result), false);\r\n\t\telse\r\n\t\t\to = \"\";\r\n\t\tcluster->numproxies++;\r\n\t}\r\n\telse\r\n\t{\r\n\t\to = \"Bad Password\";\r\n\t}\r\n\tif (o != result)\r\n\t{\r\n\t\tstrlcpy(result, o, sizeof(result));\r\n\t\to = result;\r\n\t}\r\n\r\n\tHTTPSV_SendHTTPHeader(cluster, dest, \"200\", \"text/html\", -1, true);\r\n\tHTTPSV_SendHTMLHeader(cluster, dest, \"QuakeTV: Admin\", args);\r\n\r\n\ts = \"<H1>QuakeTV Admin: \";\r\n\tNet_ProxySend(cluster, dest, s, strlen(s));\r\n\ts = cluster->hostname;\r\n\tNet_ProxySend(cluster, dest, s, strlen(s));\r\n\ts = \"</H1>\";\r\n\tNet_ProxySend(cluster, dest, s, strlen(s));\r\n\r\n\r\n\ts =\t\"<FORM action=\\\"admin.html\\\" method=\\\"post\\\" name=f>\"\r\n\t\t\"<CENTER>\"\r\n\t\t\"Password <input name=pwd value=\\\"\";\r\n\r\n\tNet_ProxySend(cluster, dest, s, strlen(s));\r\n\tif (passwordokay)\r\n\t\tNet_ProxySend(cluster, dest, pwd, strlen(pwd));\r\n\r\n\t\t\t\r\n\ts =\t\"\\\">\"\r\n\t\t\"<BR />\"\r\n\t\t\"Command <input name=cmd maxsize=255 size=40 value=\\\"\\\">\"\r\n\t\t\"<input type=submit value=\\\"Submit\\\" name=btn>\"\r\n\t\t\"</CENTER>\"\r\n\t\t\"</FORM>\";\r\n\tNet_ProxySend(cluster, dest, s, strlen(s));\r\n\r\n\tif (passwordokay)\r\n\t\tHTMLPRINT(\"<script>document.forms[0].elements[1].focus();</script>\");\r\n\telse\r\n\t\tHTMLPRINT(\"<script>document.forms[0].elements[0].focus();</script>\");\r\n\r\n\twhile(*o)\r\n\t{\r\n\t\ts = strchr(o, '\\n');\r\n\t\tif (s)\r\n\t\t\t*s = 0;\r\n\t\tHTMLprintf(cmd, sizeof(cmd), \"%s\", o);\r\n\t\tNet_ProxySend(cluster, dest, cmd, strlen(cmd));\r\n\t\tNet_ProxySend(cluster, dest, \"<BR />\", 6);\r\n\t\tif (!s)\r\n\t\t\tbreak;\r\n\t\to = s+1;\r\n\t}\r\n\r\n\tHTTPSV_SendHTMLFooter(cluster, dest);\r\n}\r\n\r\nstatic void HTTPSV_GenerateDemoListing(cluster_t *cluster, oproxy_t *dest, char *args)\r\n{\r\n\tint i;\r\n\tchar link[512];\r\n\tchar *s;\r\n\tqboolean plugframe = false;\r\n\tfor (s=args; *s && *s != ' ';)\r\n\t{\r\n\t\tif (*s == 'p')\r\n\t\t\t plugframe = true;\r\n\t\ts++;\r\n\t}\r\n\r\n\tHTTPSV_SendHTTPHeader(cluster, dest, \"200\", \"text/html\", -1, true);\r\n\tHTTPSV_SendHTMLHeader(cluster, dest, \"QuakeTV: Demos\", args);\r\n\r\n\ts = \"<h1>QuakeTV: Demo Listing</h1>\";\r\n\tNet_ProxySend(cluster, dest, s, strlen(s));\r\n\r\n\tCluster_BuildAvailableDemoList(cluster);\r\n\tfor (i = 0; i < cluster->availdemoscount; i++)\r\n\t{\r\n\t\tif (plugframe)\r\n\t\t{\r\n\t\t\tsnprintf(link, sizeof(link), \"<A HREF=\\\"javascript:parent.playdemo('%s')\\\">%s</A> (%ikb)<br/>\", cluster->availdemos[i].name, cluster->availdemos[i].name, cluster->availdemos[i].size/1024);\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tsnprintf(link, sizeof(link), \"<A HREF=\\\"/watch.qtv?demo=%s\\\">%s</A> (%ikb)<br/>\", cluster->availdemos[i].name, cluster->availdemos[i].name, cluster->availdemos[i].size/1024);\r\n\t\t}\r\n\t\tNet_ProxySend(cluster, dest, link, strlen(link));\r\n\t}\r\n\r\n\tsnprintf(link, sizeof(link), \"<P>Total: %i demos</P>\", cluster->availdemoscount);\r\n\tNet_ProxySend(cluster, dest, link, strlen(link));\r\n\r\n\tHTTPSV_SendHTMLFooter(cluster, dest);\r\n}\r\n\r\nstatic void HTTPSV_GeneratePlugin(cluster_t *cluster, oproxy_t *dest)\r\n{\r\n\tchar hostname[1024];\r\n\tchar *html;\r\n\tif (!HTTPSV_GetHeaderField((char*)dest->inbuffer, \"Host\", hostname, sizeof(hostname)))\r\n\t{\r\n\t\tHTTPSV_SendHTTPHeader(cluster, dest, \"400\", \"text/html\", -1, true);\r\n\t\tHTTPSV_SendHTMLHeader(cluster, dest, \"QuakeTV: Error\", \"p\");\r\n\r\n\t\thtml = \"Your client did not send a Host field, which is required in HTTP/1.1\\n<BR />\"\r\n\t\t\t\"Please try a different browser.\\n\"\r\n\t\t\t\"</BODY>\"\r\n\t\t\t\"</HTML>\";\r\n\r\n\t\tNet_ProxySend(cluster, dest, html, strlen(html));\r\n\t\treturn;\r\n\t}\r\n\r\n\thtml = \"<!DOCTYPE HTML>\\n\"\r\n\t\t\"<HTML><HEAD><TITLE>QuakeTV With Plugin</TITLE>\"\r\n\t\t\t\t\"  <link rel=\\\"StyleSheet\\\" href=\\\"/style.css\\\" type=\\\"text/css\\\" />\\n\"\r\n\t\t\t\t\"<meta charset='utf-8' />\"\r\n\t\t\t\t\"</HEAD><body>\"\r\n\t\"<div class='topdiv'>\"\r\n\t\t\"<div class='left' id=optdiv>\"\r\n\t\t\t\t\t\"<iframe frameborder=0 src=\\\"nowplaying.html?p\\\" width=\\\"100%\\\" height=\\\"100%\\\">\"\r\n\t\t\t\t\t\"oh dear. your browser doesn't support this site\"\r\n\t\t\t\t\t\"</iframe>\"\r\n\t\t\"</div>\"\r\n\t\t\"<div class='right' id=plugdiv>\"\r\n\t\t\t\"<canvas class='emscripten' id='canvas' oncontextmenu='event.preventDefault()'>Canvas not supported</canvas>\"\r\n\t\t\"</div>\"\r\n\t\"</div>\"\r\n\t\"<script type='text/javascript'>\"\r\n\t\t\"var Module = {\"\r\n\t\t\t\"canvas: document.getElementById('canvas'),\"\r\n\t\t\t\"print: function(msg)\"\r\n\t\t\t\"{\"\r\n\t\t\t\t\"console.log(msg);\"\r\n\t\t\t\"},\"\r\n\t\t\t\"printErr: function(text)\"\r\n\t\t\t\"{\"\r\n\t\t\t\t\"if (text.substr(0, 28) == 'Cannot enlarge memory arrays')\"\r\n\t\t\t\t\t\"alert('Memory full/fragmented. Please reload the page.');\"\r\n\t\t\t\t\"else \"\r\n\t\t\t\t\t\"console.log(text);\"\r\n\t\t\t\"},\"\r\n\t\t\t\"arguments: [\"\r\n\t\t\t\t\"'-nohome',\"\r\n\t\t\t\t\"'-manifest',\"\r\n\t\t\t\t\"'\"WEBPORTMANIFEST\"',\"\r\n\t\t\t\t\"'+connect',\"\r\n\t\t\t\t\"(window.location.protocol=='https:'?'wss://':'ws://')+window.location.host\"\r\n\t\t\t\"]\"\r\n\t\t\"};\"\r\n\r\n\t\t//create the script object\r\n\t\t\"var s = document.createElement('script');\"\r\n\t\t// set it up\r\n\t\t\"s.setAttribute('src','/ftewebgl.js');\"\r\n\t\t\"s.setAttribute('type','text/javascript');\"\r\n\t\t\"s.setAttribute('charset','utf-8');\"\r\n\t\t\"s.addEventListener('error', function() {alert('Error loading game script.');}, false);\"\r\n\t\t// add to DOM\r\n\t\t\"document.head.appendChild(s);\"\r\n\r\n\t\t//set up some functions that the embedded stuff can use\r\n\t\t\"parent.joinserver = function(d)\"\r\n\t\t\"{\"\r\n\t\t\t\"d = '\\\\nconnect '+d+'\\\\n';\"\r\n\t\t\t\"FTEC.cbufadd(d);\"\r\n\t\t\"}\\n\"\r\n\t\t\"parent.joinstream = function(d)\"\r\n\t\t\"{\"\r\n\t\t\t\"joinserver((window.location.protocol=='https:'?'wss://':'ws://')+window.location.host+'/'+d);\"\r\n\t\t\"}\\n\"\r\n\t\t\"parent.playdemo = function(d)\"\r\n\t\t\"{\"\r\n\t\t\t\"alert('playdemo'+d);\"\r\n\t\t\"}\\n\"\r\n\r\n\t\"</script>\"\r\n\t\"<noscript>\"\r\n\t\t\"It looks like you have javascript disabled.<br/>\"\r\n\t\t\"If you want to run a javascript port of a game engine, it helps to have javascript enabled. Just saying.<br/>\"\r\n\t\t\"<br/>\"\r\n\t\"</noscript>\"\r\n\r\n\t\"</body></HTML>\";\r\n\r\n\tHTTPSV_SendHTTPHeader(cluster, dest, \"200\", \"text/html\", strlen(html), true);\r\n\tNet_ProxySend(cluster, dest, html, strlen(html));\r\n}\r\n\r\nstatic void HTTPSV_GenerateDownload(cluster_t *cluster, oproxy_t *dest, char *filename, char *svroot, char *redir)\r\n{\r\n\tchar fname[256];\r\n\tchar link[512];\r\n\tchar *s, *suppliedname;\r\n\tint len;\r\n\r\n\tchar *mime = \"application/x-forcedownload\";\r\n\tif (!strcmp(filename, \"/ftewebgl.wasm\"))\r\n\t\tmime = \"application/wasm\";\r\n\telse if (!strcmp(filename, \"/ftewebgl.js\"))\r\n\t\tmime = \"text/javascript\";\r\n\r\n\tif (!svroot || !*svroot)\r\n\t{\r\n\t\tif (redir)\r\n\t\t{\r\n\t\t\tHTTPPRINT(\"HTTP/1.0 302 Found\\nLocation: \");\r\n\t\t\tHTTPPRINT(redir);\r\n\t\t\tHTTPPRINT(\"\\n\\n\");\r\n\t\t\treturn;\r\n\t\t}\r\n\t\tHTTPSV_SendHTTPHeader(cluster, dest, \"403\", \"text/html\", -1, true);\r\n\t\tHTTPSV_SendHTMLHeader(cluster, dest, \"Permission denied\", \"\");\r\n\t\tHTMLPRINT(\"<h1>403: Forbidden</h1>\");\r\n\t\tHTMLPRINT(\"File downloads from this proxy are currently not permitted.\");\r\n\t\tHTTPSV_SendHTMLFooter(cluster, dest);\r\n\t\treturn;\r\n\t}\r\n\r\n\tsuppliedname = s = fname + strlcpy(fname, svroot, sizeof(fname));\r\n\twhile (*filename > ' ')\r\n\t{\r\n\t\tif (s > fname + sizeof(fname)-4)\t//4 cos I'm too lazy to work out what the actual number should be\r\n\t\t\tbreak;\r\n\t\tif (*filename == '%')\r\n\t\t{\r\n\t\t\t*s = 0;\r\n\t\t\tfilename++;\r\n\t\t\tif (*filename <= ' ')\r\n\t\t\t\tbreak;\r\n\t\t\telse if (*filename >= 'a' && *filename <= 'f')\r\n\t\t\t\t*s += 10 + *filename-'a';\r\n\t\t\telse if (*filename >= 'A' && *filename <= 'F')\r\n\t\t\t\t*s += 10 + *filename-'A';\r\n\t\t\telse if (*filename >= '0' && *filename <= '9')\r\n\t\t\t\t*s += *filename-'0';\r\n\r\n\t\t\t*s <<= 4;\r\n\r\n\t\t\tfilename++;\r\n\t\t\tif (*filename <= ' ')\r\n\t\t\t\tbreak;\r\n\t\t\telse if (*filename >= 'a' && *filename <= 'f')\r\n\t\t\t\t*s += 10 + *filename-'a';\r\n\t\t\telse if (*filename >= 'A' && *filename <= 'F')\r\n\t\t\t\t*s += 10 + *filename-'A';\r\n\t\t\telse if (*filename >= '0' && *filename <= '9')\r\n\t\t\t\t*s += *filename-'0';\r\n\r\n\t\t\ts++;\r\n\t\t}\r\n\t\telse\r\n\t\t\t*s++ = *filename++;\r\n\t}\r\n\t*s = 0;\r\n\r\n\tif (!redir && (*suppliedname == '\\\\' || *suppliedname == '/' || strstr(suppliedname, \"..\") || strstr(suppliedname, \"//\") || strstr(suppliedname, \"\\\\\\\\\") || strchr(suppliedname, ':')))\r\n\t{\r\n\t\tHTTPSV_SendHTTPHeader(cluster, dest, \"403\", \"text/html\", -1, true);\r\n\t\tHTTPSV_SendHTMLHeader(cluster, dest, \"Permission denied\", \"\");\r\n\t\tHTMLPRINT(\"<h1>403: Forbidden</h1>\");\r\n\r\n\t\tHTMLPRINT(\"<p>\");\r\n\t\tHTMLprintf(link, sizeof(link), \"The filename '%s' names an absolute path.\", suppliedname);\r\n\t\tNet_ProxySend(cluster, dest, link, strlen(link));\r\n\t\tHTMLPRINT(\"</p>\");\r\n\t\treturn;\r\n\t}\r\n\tlen = strlen(fname);\r\n\tif (len > 4)\r\n\t{\r\n\t\t/*protect id's content (prevent downloading of bsps from pak files - we don't do pak files so just prevent the entire pak)*/\r\n\t\tif (!stricmp(link+len-4, \".pak\"))\r\n\t\t{\r\n\t\t\tHTTPSV_SendHTTPHeader(cluster, dest, \"403\", \"text/html\", -1, true);\r\n\t\t\tHTTPSV_SendHTMLHeader(cluster, dest, \"Permission denied\", \"\");\r\n\t\t\tHTMLPRINT(\"<h1>403: Forbidden</h1>\");\r\n\t\t\r\n\t\t\tHTMLPRINT(\"<p>\");\r\n\t\t\tHTMLprintf(link, sizeof(link), \"Pak files may not be downloaded.\", suppliedname);\r\n\t\t\tNet_ProxySend(cluster, dest, link, strlen(link));\r\n\t\t\tHTMLPRINT(\"</p>\");\r\n\t\t\treturn;\r\n\t\t}\t\t\r\n\t}\r\n\r\n\tprintf(\"Download request for %s\\n\", fname);\r\n\tdest->srcfile = fopen(fname, \"rb\");\r\n\r\n\tif (dest->srcfile)\r\n\t{\r\n\t\tlong size;\r\n\t\tfseek(dest->srcfile, 0, SEEK_END);\r\n\t\tsize = ftell(dest->srcfile);\r\n\t\tfseek(dest->srcfile, 0, SEEK_SET);\r\n\t\tHTTPSV_SendHTTPHeader(cluster, dest, \"200\", mime, size, false);\r\n\t}\r\n\telse if (redir)\r\n\t{\r\n\t\tHTTPPRINT(\"HTTP/1.0 302 Found\\nLocation: \");\r\n\t\tHTTPPRINT(redir);\r\n\t\tHTTPPRINT(\"\\n\\n\");\r\n\t}\r\n\telse\r\n\t{\r\n\t\tHTTPSV_SendHTTPHeader(cluster, dest, \"404\", \"text/html\", -1, true);\r\n\t\tHTTPSV_SendHTMLHeader(cluster, dest, \"File not found\", \"\");\r\n\t\tHTMLPRINT(\"<h1>404: File not found</h1>\");\r\n\r\n\t\tHTMLPRINT(\"<p>\");\r\n\t\tHTMLprintf(link, sizeof(link), \"The file '%s' could not be found on this server\", suppliedname);\r\n\t\tNet_ProxySend(cluster, dest, link, strlen(link));\r\n\t\tHTMLPRINT(\"</p>\");\r\n\r\n\t\tHTTPSV_SendHTMLFooter(cluster, dest);\r\n\t}\r\n}\r\n\r\n\r\n\r\n\r\nstatic qboolean urimatch(char *uri, char *match, int urilen)\r\n{\r\n\tint mlen = strlen(match);\r\n\tif (urilen < mlen)\r\n\t\treturn false;\r\n\tif (strncmp(uri, match, mlen))\r\n\t\treturn false;\r\n\tif (urilen == mlen)\r\n\t\treturn true;\r\n\tif (uri[mlen] == '?')\r\n\t\treturn true;\r\n\treturn false;\r\n}\r\nstatic qboolean uriargmatch(char *uri, char *match, int urilen, char **args)\r\n{\r\n\tint mlen = strlen(match);\r\n\tif (urilen < mlen)\r\n\t\treturn false;\r\n\tif (strncmp(uri, match, mlen))\r\n\t\treturn false;\r\n\tif (urilen == mlen)\r\n\t{\r\n\t\t*args = \"\";\r\n\t\treturn true;\r\n\t}\r\n\tif (uri[mlen] == '?')\r\n\t{\r\n\t\t*args = uri+mlen+1;\r\n\t\treturn true;\r\n\t}\r\n\treturn false;\r\n}\r\n\r\n\r\nvoid HTTPSV_PostMethod(cluster_t *cluster, oproxy_t *pend, char *postdata)\r\n{\r\n\tchar tempbuf[512];\r\n\tchar *s;\r\n\tint len;\r\n\r\n\tchar *uri, *uriend;\r\n\tchar *args;\r\n\tint urilen;\r\n\turi = pend->inbuffer+4;\r\n\twhile (*uri == ' ')\r\n\t\turi++;\r\n\turiend = strchr(uri, '\\n');\r\n\ts = strchr(uri, ' ');\r\n\tif (s && s < uriend)\r\n\t\turiend = s;\r\n\turilen = uriend - uri;\r\n\r\n\tif (!HTTPSV_GetHeaderField((char*)pend->inbuffer, \"Content-Length\", tempbuf, sizeof(tempbuf)))\r\n\t{\r\n\t\ts = \"HTTP/1.1 411 OK\\n\"\r\n\t\t\t\"Content-Type: text/html\\n\"\r\n\t\t\t\"Connection: close\\n\"\r\n\t\t\t\"\\n\"\r\n\t\t\t\"<html><HEAD><TITLE>QuakeTV</TITLE></HEAD><BODY>No Content-Length was provided.</BODY>\\n\";\r\n\t\tNet_ProxySend(cluster, pend, s, strlen(s));\r\n\t\tpend->flushing = true;\r\n\t\treturn;\r\n\t}\r\n\tlen = atoi(tempbuf);\r\n\tif (pend->inbuffersize + len >= sizeof(pend->inbuffer)-20)\r\n\t{\t//too much data\r\n\t\tpend->flushing = true;\r\n\t\treturn;\r\n\t}\r\n\tlen = postdata - (char*)pend->inbuffer + len;\r\n\tif (len > pend->inbuffersize)\r\n\t\treturn;\t//still need the body\r\n\r\n//\tif (len <= pend->inbuffersize)\r\n\t{\r\n\t\tif (uriargmatch(uri, \"/admin.html\", urilen, &args))\r\n\t\t{\r\n\t\t\tHTTPSV_GenerateAdmin(cluster, pend, 0, postdata, args);\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\ts = \"HTTP/1.1 404 OK\\n\"\r\n\t\t\t\t\"Content-Type: text/html\\n\"\r\n\t\t\t\t\"Connection: close\\n\"\r\n\t\t\t\t\"\\n\"\r\n\t\t\t\t\"<html><HEAD><TITLE>QuakeTV</TITLE></HEAD><BODY>That HTTP method is not supported for that URL.</BODY></html>\\n\";\r\n\t\t\tNet_ProxySend(cluster, pend, s, strlen(s));\r\n\t\r\n\t\t}\r\n\t\tpend->flushing = true;\r\n\t\treturn;\r\n\t}\r\n}\r\n\r\n#define REDIRECTIF(uri_,url_)\t\\\r\n\tif (urimatch(uri, uri_, urilen))\t\\\r\n\t{\t\\\r\n\t\ts = \"HTTP/1.0 302 Found\\n\"\t\\\r\n\t\t\t\"Location: \" url_ \"\\n\"\t\\\r\n\t\t\t\"\\n\";\t\\\r\n\t\tNet_ProxySend(cluster, pend, s, strlen(s));\t\\\r\n\t}\r\n#define REDIRECTIFNEEDED(uri_,url_)\t\\\r\n\tif (urimatch(uri, uri_, urilen))\t\\\r\n\t{\t\\\r\n\t\tHTTPSV_GenerateDownload(cluster, pend, uri_, cluster->downloaddir, url_);\\\r\n\t}\r\n\r\nchar *HTTPSV_GetMethod(cluster_t *cluster, oproxy_t *pend)\r\n{\r\n\tchar connection[64];\r\n\tchar upgrade[64];\r\n\r\n\tchar *s;\r\n\tchar *uri, *uriend;\r\n\tchar *args;\r\n\tint urilen;\r\n\turi = pend->inbuffer+4;\r\n\twhile (*uri == ' ')\r\n\t\turi++;\r\n\turiend = strchr(uri, '\\n');\r\n\ts = strchr(uri, ' ');\r\n\tif (s && s < uriend)\r\n\t\turiend = s;\r\n\turilen = uriend - uri;\r\n\r\n\tif (!pend->websocket.websocket && uri[0] == '/' && HTTPSV_GetHeaderField((char*)pend->inbuffer, \"Connection\", connection, sizeof(connection)) && strstr(connection, \"Upgrade\"))\r\n\t{\r\n\t\tif (HTTPSV_GetHeaderField((char*)pend->inbuffer, \"Upgrade\", upgrade, sizeof(upgrade)) && !stricmp(upgrade, \"websocket\"))\r\n\t\t{\r\n\t\t\tchar wsprot[64];\r\n\t\t\tchar ver[64];\r\n\t\t\tchar key[64];\r\n\t\t\tchar *ctx;\r\n\t\t\tHTTPSV_GetHeaderField((char*)pend->inbuffer, \"Sec-WebSocket-Key\", key, sizeof(key));\r\n\r\n\t\t\tfor (ctx = NULL; HTTPSV_GetHeaderCommaField((char*)pend->inbuffer, &ctx, \"Sec-WebSocket-Protocol\", wsprot, sizeof(wsprot)); )\r\n\t\t\t{\r\n\t\t\t\t//if (!strcmp(wsprot, \"quake\"))\t//webquake. we don't support this! (no OOB and missing header flags and some screwy sequence numbers)\r\n\t\t\t\tif (!strcmp(wsprot, \"fteqw\") ||\t//as a client\r\n\t\t\t\t\t(!strcmp(wsprot, \"faketcp\") && urilen==1&&!strncmp(uri,\"/\",1)))\t//as a qtv proxy (eztv style, but websocked). we are NOT proxying tcp. require a qtv handshake over the resulting websocket connection.\r\n\t\t\t\t\tbreak;\t//break out on the first one we know. this is the recommended way...\r\n\t\t\t}\r\n\r\n\t\t\tif (!*wsprot)\r\n\t\t\t{\r\n\t\t#define dest pend\r\n\t\t\t\tHTTPSV_SendHTTPHeader(cluster, pend, \"404\", \"text/html\", -1, true);\r\n\t\t\t\tHTTPSV_SendHTMLHeader(cluster, pend, \"Websocket SubProtocol not recognised\", \"\");\r\n\t\t\t\tHTMLPRINT(\"<h1>Websocket SubProtocol not recognised</h1>\");\r\n\t\t\t\tHTTPSV_SendHTMLFooter(cluster, pend);\r\n\t\t\t}\r\n\t\t\telse if (HTTPSV_GetHeaderField((char*)pend->inbuffer, \"Sec-WebSocket-Version\", ver, sizeof(ver)) && atoi(ver) != 13)\r\n\t\t\t{\r\n\t\t\t\ts =\t\"HTTP/1.1 426 Upgrade Required\\r\\n\"\r\n\t\t\t\t\t\"Sec-WebSocket-Version: 13\\r\\n\"\r\n\t\t\t\t\t\"Access-Control-Allow-Origin: *\\r\\n\"\t//allow cross-origin requests. this means you can use any domain to play on any public server.\r\n\t\t\t\t\t\"\\r\\n\";\r\n\t\t\t\tNet_ProxySend(cluster, pend, s, strlen(s));\r\n\t\t\t\treturn NULL;\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tchar acceptkey[20*2];\r\n\t\t\t\tunsigned char sha1digest[20];\r\n\t\t\t\tchar padkey[512];\r\n\t\t\t\tsnprintf(padkey, sizeof(padkey), \"%s258EAFA5-E914-47DA-95CA-C5AB0DC85B11\", key);\r\n\t\t\t\ttobase64(acceptkey, sizeof(acceptkey), sha1digest, CalcHash(&hash_sha1, sha1digest, sizeof(sha1digest), padkey, strlen(padkey)));\r\n\r\n\t\t\t\tsnprintf(padkey, sizeof(padkey), \r\n\t\t\t\t\t\t\t\"HTTP/1.1 101 Switching Protocols\\r\\n\"\r\n\t\t\t\t\t\t\t\"Upgrade: websocket\\r\\n\"\r\n\t\t\t\t\t\t\t\"Connection: Upgrade\\r\\n\"\r\n\t\t\t\t\t\t\t\"Access-Control-Allow-Origin: *\\r\\n\"\t//allow cross-origin requests. this means you can use any domain to play on any public server.\r\n\t\t\t\t\t\t\t\"Sec-WebSocket-Accept: %s\\r\\n\"\r\n\t\t\t\t\t\t\t\"Sec-WebSocket-Protocol: %s\\r\\n\"\r\n\t\t\t\t\t\t\t\"\\r\\n\", acceptkey, wsprot);\r\n\t\t\t\t//send the websocket handshake response.\r\n\t\t\t\tNet_ProxySend(cluster, pend, padkey, strlen(padkey));\r\n\t\t\t\tpend->websocket.websocket = true;\r\n\r\n\t\t\t\tpend->droptime = cluster->curtime + 20*1000;\r\n\r\n\t\t\t\tprintf(\"websocket upgrade\\n\");\r\n\t\t\t\tpend->drop = false;\r\n\r\n\t\t\t\turi[urilen] = 0;\t//make sure its null terminated\r\n\t\t\t\tif (!strcmp(wsprot, \"faketcp\"))\r\n\t\t\t\t\treturn NULL;\t//we're using websockets, don't treat it as a client\r\n\t\t\t\telse\r\n\t\t\t\t\treturn strdup(uri+1);\t//its a client, track the url as the initial stream.\r\n\t\t\t}\r\n\t\t\treturn NULL;\r\n\t\t}\r\n\t}\r\n\r\n\tif (urimatch(uri, \"/plugin.html\", urilen) || urimatch(uri, \"/\", urilen))\r\n\t{\r\n\t\tHTTPSV_GeneratePlugin(cluster, pend);\r\n\t}\r\n\telse if (uriargmatch(uri, \"/nowplaying.html\", urilen, &args))\r\n\t{\r\n\t\tHTTPSV_GenerateNowPlaying(cluster, pend, args);\r\n\t}\r\n\telse if (!strncmp(uri, \"/watch.qtv?sid=\", 15))\r\n\t{\r\n\t\tHTTPSV_GenerateQTVStub(cluster, pend, \"\", (char*)pend->inbuffer+19);\r\n\t}\r\n\telse if (!strncmp(uri, \"/watch.qtv?demo=\", 16))\r\n\t{\r\n\t\tHTTPSV_GenerateQTVStub(cluster, pend, \"file:\", (char*)pend->inbuffer+20);\r\n\t}\r\n\telse if (!strncmp(uri, \"/watch.qtv?join=\", 16))\r\n\t{\r\n\t\tHTTPSV_GenerateQWSVStub(cluster, pend, \"Join\", (char*)pend->inbuffer+16);\r\n\t}\r\n\telse if (!strncmp(uri, \"/watch.qtv?obsv=\", 16))\r\n\t{\r\n\t\tHTTPSV_GenerateQWSVStub(cluster, pend, \"Observe\", (char*)pend->inbuffer+16);\r\n\t}\r\n//\telse if (!strncmp((char*)pend->inbuffer+4, \"/demo/\", 6))\r\n//\t{\t//fixme: make this send the demo as an http download\r\n//\t\tHTTPSV_GenerateQTVStub(cluster, pend, \"file:\", (char*)pend->inbuffer+10);\r\n//\t}\r\n\telse if (uriargmatch(uri, \"/admin.html\", urilen, &args))\r\n\t{\r\n\t\tHTTPSV_GenerateAdmin(cluster, pend, 0, NULL, args);\r\n\t}\r\n#if defined(_DEBUG) || defined(DEBUG)\r\n\telse REDIRECTIF(\"/\", \"/plugin.html\")\r\n#endif\r\n\telse REDIRECTIF(\"/\", \"/nowplaying.html\")\r\n\telse REDIRECTIFNEEDED(\"/about.html\", PROXYWEBSITE)\r\n\telse REDIRECTIFNEEDED(\"/favicon.ico\",\t\"https://fte.triptohell.info/favicon.ico\")\r\n\telse REDIRECTIFNEEDED(\"/ftewebgl.js\",\t\"https://fte.triptohell.info/ftewebgl.js\")\r\n\telse REDIRECTIFNEEDED(\"/ftewebgl.wasm\",\t\"https://fte.triptohell.info/ftewebgl.wasm\")\r\n\telse if (uriargmatch(uri, \"/demos.html\", urilen, &args))\r\n\t{\r\n\t\tHTTPSV_GenerateDemoListing(cluster, pend, args);\r\n\t}\r\n\telse if (!strncmp((char*)pend->inbuffer+4, \"/file/\", 6))\r\n\t{\r\n\t\tHTTPSV_GenerateDownload(cluster, pend, (char*)pend->inbuffer+10, cluster->downloaddir, NULL);\r\n\t}\r\n\telse if (urimatch(uri, \"/style.css\", urilen))\r\n\t{\r\n\t\tHTTPSV_GenerateCSSFile(cluster, pend);\r\n\t}\r\n\telse\r\n\t{\r\n#define dest pend\r\n\t\tHTTPSV_SendHTTPHeader(cluster, dest, \"404\", \"text/html\", -1, true);\r\n\t\tHTTPSV_SendHTMLHeader(cluster, dest, \"Address not recognised\", \"\");\r\n\t\tHTMLPRINT(\"<h1>Address not recognised</h1>\");\r\n\t\tHTTPSV_SendHTMLFooter(cluster, dest);\r\n\t}\r\n\treturn NULL;\r\n}\r\n"
  },
  {
    "path": "fteqtv/libqtvc/Makefile",
    "content": "\nOBJS = msvc_sucks.o glibc_sucks.o\n\nall: libqtvc.a\n\nlibqtvc.a: $(OBJS)\n\trm -f $@\n\t$(AR) r $@ $^\n\t$(RANLIB) $@\n\nclean:\n\trm -f $(OBJS) libqtvc.a\n\n"
  },
  {
    "path": "fteqtv/libqtvc/glibc_sucks.c",
    "content": "/*\t$OpenBSD: strlcpy.c,v 1.11 2006/05/05 15:27:38 millert Exp $\t*/\n\n/*\n * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>\n *\n * Permission to use, copy, modify, and distribute this software for any\n * purpose with or without fee is hereby granted, provided that the above\n * copyright notice and this permission notice appear in all copies.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\n * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR\n * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN\n * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF\n * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n */\n\n#include <sys/types.h>\n#include <string.h>\n\n/*\n * Copy src to string dst of size siz.  At most siz-1 characters\n * will be copied.  Always NUL terminates (unless siz == 0).\n * Returns strlen(src); if retval >= siz, truncation occurred.\n */\nsize_t\nstrlcpy(char *dst, const char *src, size_t siz)\n{\n\tchar *d = dst;\n\tconst char *s = src;\n\tsize_t n = siz;\n\n\t/* Copy as many bytes as will fit */\n\tif (n != 0) {\n\t\twhile (--n != 0) {\n\t\t\tif ((*d++ = *s++) == '\\0')\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\t/* Not enough room in dst, add NUL and traverse rest of src */\n\tif (n == 0) {\n\t\tif (siz != 0)\n\t\t\t*d = '\\0';\t\t/* NUL-terminate dst */\n\t\twhile (*s++)\n\t\t\t;\n\t}\n\n\treturn(s - src - 1);\t/* count does not include NUL */\n}\n\n"
  },
  {
    "path": "fteqtv/libqtvc/msvc_sucks.c",
    "content": "/*\nCopyright (C) 2007 Mark Olsen\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\nSee the included (LICENSE) GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n*/\n\n#ifdef _WIN32\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <stdarg.h>\n\n#if _MSC_VER >= 1300\r\n\t#define vsnprintf q_vsnprintf /*msvc doesn't null terminate. its insecute and thus useless*/\n#endif\n\nstatic int vsnprintf_calcsize(const char *fmt, va_list va)\n{\n\tvoid *mem;\n\tunsigned int memsize;\n\tint ret;\n\n\tmemsize = 1024;\n\n\tdo\n\t{\n\t\tmem = malloc(memsize);\n\n\t\tret = _vsnprintf(mem, memsize-1, fmt,va);\n\t\tif (ret == -1)\n\t\t\tmemsize*= 2;\n\n\t\tfree(mem);\n\t} while(ret == -1 && memsize);\n\n\treturn ret;\n}\n\nint vsnprintf(char *buf, size_t buflen, const char *fmt, va_list va)\n{\n\tint ret;\n\n\tif (buflen == 0)\n\t\treturn vsnprintf_calcsize(fmt, va);\n\t\n\tret = _vsnprintf(buf, buflen-1, fmt, va);\n\tbuf[buflen-1] = 0;\n\n\tif (ret == -1)\n\t\treturn vsnprintf_calcsize(fmt, va);\n\t\n\treturn ret;\n}\n\nint snprintf(char *buf, size_t buflen, const char *fmt, ...)\n{\n\tint ret;\n\tva_list va;\n\n\tva_start(va, fmt);\n\tret = vsnprintf(buf, buflen, fmt, va);\n\tva_end(va);\n\n\treturn ret;\n}\n\n#endif\n\n"
  },
  {
    "path": "fteqtv/mdfour.c",
    "content": "/*\n\tmdfour.c\n\n\tAn implementation of MD4 designed for use in the samba SMB\n\tauthentication protocol\n\n\tCopyright (C) 1997-1998  Andrew Tridgell\n\n\tThis program is free software; you can redistribute it and/or\n\tmodify it under the terms of the GNU General Public License\n\tas published by the Free Software Foundation; either version 2\n\tof the License, or (at your option) any later version.\n\n\tThis program is distributed in the hope that it will be useful,\n\tbut WITHOUT ANY WARRANTY; without even the implied warranty of\n\tMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\n\tSee the GNU General Public License for more details.\n\n\tYou should have received a copy of the GNU General Public License\n\talong with this program; if not, write to:\n\n\t\tFree Software Foundation, Inc.\n\t\t59 Temple Place - Suite 330\n\t\tBoston, MA  02111-1307, USA\n\n\t$Id$\n*/\n\n#include \"qtv.h\"\n\n#include <string.h>\t\t/* XoXus: needed for memset call */\n\n\n\n\n#ifndef _MDFOUR_H\n#define _MDFOUR_H\n\n#ifndef int32\n#define int32 int\n#endif\n\n#if SIZEOF_INT > 4\n#define LARGE_INT32\n#endif\n\n#ifndef uint32\n#define uint32 unsigned int32\n#endif\n\nstruct mdfour {\n\tuint32 A, B, C, D;\n\tuint32 totalN;\n};\n\nstatic void mdfour_begin(struct mdfour *md); // old: MD4Init\nstatic void mdfour_update(struct mdfour *md, unsigned char *in, int n); //old: MD4Update\nstatic void mdfour_result(struct mdfour *md, unsigned char *out); // old: MD4Final\nstatic void mdfour(unsigned char *out, unsigned char *in, int n);\n\n#endif\t// _MDFOUR_H\n\n\n\n/* NOTE: This code makes no attempt to be fast!\n\n   It assumes that a int is at least 32 bits long\n*/\n\n#define F(X,Y,Z) (((X)&(Y)) | ((~(X))&(Z)))\n#define G(X,Y,Z) (((X)&(Y)) | ((X)&(Z)) | ((Y)&(Z)))\n#define H(X,Y,Z) ((X)^(Y)^(Z))\n#ifdef LARGE_INT32\n#define lshift(x,s) ((((x)<<(s))&0xFFFFFFFF) | (((x)>>(32-(s)))&0xFFFFFFFF))\n#else\n#define lshift(x,s) (((x)<<(s)) | ((x)>>(32-(s))))\n#endif\n\n#define ROUND1(a,b,c,d,k,s) a = lshift(a + F(b,c,d) + X[k], s)\n#define ROUND2(a,b,c,d,k,s) a = lshift(a + G(b,c,d) + X[k] + 0x5A827999,s)\n#define ROUND3(a,b,c,d,k,s) a = lshift(a + H(b,c,d) + X[k] + 0x6ED9EBA1,s)\n\n/* this applies md4 to 64 byte chunks */\nstatic void mdfour64(struct mdfour *m, uint32 *M)\n{\n\tint j;\n\tuint32 AA, BB, CC, DD;\n\tuint32 X[16];\n\tuint32 A,B,C,D;\n\n\tfor (j=0;j<16;j++)\n\t\tX[j] = M[j];\n\n\tA = m->A; B = m->B; C = m->C; D = m->D;\n\tAA = A; BB = B; CC = C; DD = D;\n\n        ROUND1(A,B,C,D,  0,  3);  ROUND1(D,A,B,C,  1,  7);\n\tROUND1(C,D,A,B,  2, 11);  ROUND1(B,C,D,A,  3, 19);\n        ROUND1(A,B,C,D,  4,  3);  ROUND1(D,A,B,C,  5,  7);\n\tROUND1(C,D,A,B,  6, 11);  ROUND1(B,C,D,A,  7, 19);\n        ROUND1(A,B,C,D,  8,  3);  ROUND1(D,A,B,C,  9,  7);\n\tROUND1(C,D,A,B, 10, 11);  ROUND1(B,C,D,A, 11, 19);\n        ROUND1(A,B,C,D, 12,  3);  ROUND1(D,A,B,C, 13,  7);\n\tROUND1(C,D,A,B, 14, 11);  ROUND1(B,C,D,A, 15, 19);\n\n        ROUND2(A,B,C,D,  0,  3);  ROUND2(D,A,B,C,  4,  5);\n\tROUND2(C,D,A,B,  8,  9);  ROUND2(B,C,D,A, 12, 13);\n        ROUND2(A,B,C,D,  1,  3);  ROUND2(D,A,B,C,  5,  5);\n\tROUND2(C,D,A,B,  9,  9);  ROUND2(B,C,D,A, 13, 13);\n        ROUND2(A,B,C,D,  2,  3);  ROUND2(D,A,B,C,  6,  5);\n\tROUND2(C,D,A,B, 10,  9);  ROUND2(B,C,D,A, 14, 13);\n        ROUND2(A,B,C,D,  3,  3);  ROUND2(D,A,B,C,  7,  5);\n\tROUND2(C,D,A,B, 11,  9);  ROUND2(B,C,D,A, 15, 13);\n\n\tROUND3(A,B,C,D,  0,  3);  ROUND3(D,A,B,C,  8,  9);\n\tROUND3(C,D,A,B,  4, 11);  ROUND3(B,C,D,A, 12, 15);\n        ROUND3(A,B,C,D,  2,  3);  ROUND3(D,A,B,C, 10,  9);\n\tROUND3(C,D,A,B,  6, 11);  ROUND3(B,C,D,A, 14, 15);\n        ROUND3(A,B,C,D,  1,  3);  ROUND3(D,A,B,C,  9,  9);\n\tROUND3(C,D,A,B,  5, 11);  ROUND3(B,C,D,A, 13, 15);\n        ROUND3(A,B,C,D,  3,  3);  ROUND3(D,A,B,C, 11,  9);\n\tROUND3(C,D,A,B,  7, 11);  ROUND3(B,C,D,A, 15, 15);\n\n\tA += AA; B += BB; C += CC; D += DD;\n\n#ifdef LARGE_INT32\n\tA &= 0xFFFFFFFF; B &= 0xFFFFFFFF;\n\tC &= 0xFFFFFFFF; D &= 0xFFFFFFFF;\n#endif\n\n\tfor (j=0;j<16;j++)\n\t\tX[j] = 0;\n\n\tm->A = A; m->B = B; m->C = C; m->D = D;\n}\n\nstatic void copy64(uint32 *M, unsigned char *in)\n{\n\tint i;\n\n\tfor (i=0;i<16;i++)\n\t\tM[i] = (in[i*4+3]<<24) | (in[i*4+2]<<16) |\n\t\t\t(in[i*4+1]<<8) | (in[i*4+0]<<0);\n}\n\nstatic void copy4(unsigned char *out,uint32 x)\n{\n\tout[0] = x&0xFF;\n\tout[1] = (x>>8)&0xFF;\n\tout[2] = (x>>16)&0xFF;\n\tout[3] = (x>>24)&0xFF;\n}\n\nstatic void mdfour_begin(struct mdfour *md)\n{\n\tmd->A = 0x67452301;\n\tmd->B = 0xefcdab89;\n\tmd->C = 0x98badcfe;\n\tmd->D = 0x10325476;\n\tmd->totalN = 0;\n}\n\n\nstatic void mdfour_tail(struct mdfour *m, unsigned char *in, int n)\n{\n\tunsigned char buf[128];\n\tuint32 M[16];\n\tuint32 b;\n\n\tm->totalN += n;\n\n\tb = m->totalN * 8;\n\n\tmemset(buf, 0, 128);\n\tif (n) memcpy(buf, in, n);\n\tbuf[n] = 0x80;\n\n\tif (n <= 55)\n\t{\n\t\tcopy4(buf+56, b);\n\t\tcopy64(M, buf);\n\t\tmdfour64(m, M);\n\t}\n\telse\n\t{\n\t\tcopy4(buf+120, b);\n\t\tcopy64(M, buf);\n\t\tmdfour64(m, M);\n\t\tcopy64(M, buf+64);\n\t\tmdfour64(m, M);\n\t}\n}\n\nstatic void mdfour_update(struct mdfour *m, unsigned char *in, int n)\n{\n\tuint32 M[16];\n\n//\tif (n == 0) mdfour_tail(in, n); //Spike: This is where the bug was.\n\n\twhile (n >= 64)\n\t{\n\t\tcopy64(M, in);\n\t\tmdfour64(m, M);\n\t\tin += 64;\n\t\tn -= 64;\n\t\tm->totalN += 64;\n\t}\n\n\tmdfour_tail(m, in, n);\n}\n\n\nstatic void mdfour_result(struct mdfour *m, unsigned char *out)\n{\n\tcopy4(out, m->A);\n\tcopy4(out+4, m->B);\n\tcopy4(out+8, m->C);\n\tcopy4(out+12, m->D);\n}\n\n\nstatic void mdfour(unsigned char *out, unsigned char *in, int n)\n{\n\tstruct mdfour md;\n\tmdfour_begin(&md);\n\tmdfour_update(&md, in, n);\n\tmdfour_result(&md, out);\n}\n\n///////////////////////////////////////////////////////////////\n//\tMD4-based checksum utility functions\n//\n//\tCopyright (C) 2000       Jeff Teunissen <d2deek@pmail.net>\n//\n//\tAuthor: Jeff Teunissen\t<d2deek@pmail.net>\n//\tDate: 01 Jan 2000\n\nunsigned Com_BlockChecksum (void *buffer, int length)\n{\n\tint\t\t\t\tdigest[4];\n\tunsigned \t\tval;\n\n\tmdfour ( (unsigned char *) digest, (unsigned char *) buffer, length );\n\n\tval = digest[0] ^ digest[1] ^ digest[2] ^ digest[3];\n\n\treturn val;\n}\n\nvoid Com_BlockFullChecksum (void *buffer, int len, unsigned char *outbuf)\n{\n\tmdfour ( outbuf, (unsigned char *) buffer, len );\n}\n"
  },
  {
    "path": "fteqtv/menu.c",
    "content": "#include \"qtv.h\"\r\n\r\n#define CENTERTIME 1.5\r\n\r\nvoid Menu_Enter(cluster_t *cluster, viewer_t *viewer, int buttonnum)\r\n{\r\n\t//build a possible message, even though it'll probably not be sent\r\n\r\n\tsv_t *sv;\r\n\tint i, min;\r\n\r\n\tswitch(viewer->menunum)\r\n\t{\r\n\tdefault:\r\n\t\tif (buttonnum < 0)\r\n\t\t\tQW_SetMenu(viewer, MENU_MAIN);\t//no other sort of back button...\r\n\t\tbreak;\r\n\r\n\tcase MENU_MAIN:\r\n\t\tif (buttonnum < 0)\r\n\t\t\tviewer->menuop -= (MENU_MAIN_ITEMCOUNT + 1)/2;\r\n\t\telse if (buttonnum > 0)\r\n\t\t\tviewer->menuop += (MENU_MAIN_ITEMCOUNT + 1)/2;\r\n\t\telse if (buttonnum == 0)\r\n\t\t{\r\n\t\t\tswitch(viewer->menuop)\r\n\t\t\t{\r\n\t\t\tcase MENU_MAIN_STREAMS: //Streams\r\n\t\t\t\tQW_SetMenu(viewer, MENU_SERVERS);\r\n\t\t\t\tbreak;\r\n\t\t\tcase MENU_MAIN_CLIENTLIST://Client List\r\n\t\t\t\tQW_SetMenu(viewer, MENU_CLIENTS);\r\n\t\t\t\tbreak;\r\n\r\n\t\t\tcase MENU_MAIN_NEWSTREAM://New Stream\r\n\t\t\t\tQW_PrintfToViewer(viewer, \"Not implemented yet\\n\");\r\n\t\t\t\tbreak;\r\n\t\t\tcase MENU_MAIN_DEMOS://Demos\r\n\t\t\t\tCluster_BuildAvailableDemoList(cluster);\r\n\t\t\t\tQW_SetMenu(viewer, MENU_DEMOS);\r\n\t\t\t\tbreak;\r\n\r\n\t\t\tcase MENU_MAIN_SERVERBROWSER://Server Browser\r\n\t\t\t\tQW_PrintfToViewer(viewer, \"Not implemented yet\\n\");\r\n\t\t\t\tbreak;\r\n\t\t\tcase MENU_MAIN_ADMIN://Admin\r\n\t\t\t\tQW_SetMenu(viewer, MENU_ADMIN);\r\n\t\t\t\tbreak;\r\n\r\n\t\t\tcase MENU_MAIN_PREVPROX://Previous Proxy\r\n\t\t\t\tif (viewer->isproxy)\r\n\t\t\t\t{\r\n\t\t\t\t\tQW_SetMenu(viewer, MENU_NONE);\r\n\t\t\t\t\tQW_StuffcmdToViewer(viewer, \"say proxy:menu\\n\");\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t\tQW_PrintfToViewer(viewer, \"No client proxy detected\\n\");\r\n\t\t\t\tbreak;\r\n\t\t\tcase MENU_MAIN_NEXTPROX://Next Proxy\r\n\t\t\t\tif (viewer->server && viewer->server->serverisproxy && viewer->server->controller == viewer)\r\n\t\t\t\t{\r\n\t\t\t\t\tviewer->server->proxyisselected = false;\r\n\t\t\t\t\tQW_SetMenu(viewer, MENU_NONE);\r\n\t\t\t\t\tSendClientCommand(viewer->server, \"say .menu\");\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t\tQW_PrintfToViewer(viewer, \"No server proxy detected\\n\");\r\n\t\t\t\tbreak;\r\n\t\t\t\r\n\t\t\tcase MENU_MAIN_HELP://Help Menu\r\n\t\t\t\tQW_PrintfToViewer(viewer, \"Not implemented yet\\n\");\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\t\t}\r\n\t\tbreak;\r\n\r\n\tcase MENU_CLIENTS:\r\n\t\tif (buttonnum >= 0)\r\n\t\t{\r\n\t\t\tviewer_t *v = cluster->viewers;\r\n\t\t\tfor (i = 0; i < viewer->menuop && v; i++)\r\n\t\t\t\tv = v->next;\r\n\t\t\tif (!v)\r\n\t\t\t\tbreak;\r\n\t\t\tif (v == viewer)\r\n\t\t\t{\r\n\t\t\t\tif (viewer->commentator)\r\n\t\t\t\t\tQW_SetCommentator(cluster, viewer, NULL);\r\n\t\t\t\telse\r\n\t\t\t\t\tQW_PrintfToViewer(viewer, \"Please stop touching yourself\\n\");\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t\tQW_SetCommentator(cluster, viewer, v);\r\n\t\t}\r\n\t\telse\r\n\t\t\tQW_SetMenu(viewer, MENU_MAIN);\r\n\t\tbreak;\r\n\r\n\tcase MENU_DEMOS:\r\n\t\tif (buttonnum >= 0)\r\n\t\t\tQW_StuffcmdToViewer(viewer, \"say .demo %s\\n\", cluster->availdemos[viewer->menuop].name);\r\n\t\telse\r\n\t\t\tQW_SetMenu(viewer, MENU_MAIN);\r\n\t\tbreak;\r\n\r\n\tcase MENU_ADMINSERVER:\r\n\t\tif (viewer->server)\r\n\t\t{\r\n\t\t\ti = 0;\r\n\t\t\tsv = viewer->server;\r\n\t\t\tif (i++ == viewer->menuop)\r\n\t\t\t{\t//auto disconnect\r\n\t\t\t\tsv->autodisconnect = sv->autodisconnect?AD_NO:AD_WHENEMPTY;\r\n\t\t\t}\r\n\t\t\tif (i++ == viewer->menuop)\r\n\t\t\t{\t//disconnect\r\n\t\t\t\tQTV_ShutdownStream(viewer->server);\r\n\t\t\t}\r\n\t\t\tif (i++ == viewer->menuop)\r\n\t\t\t{\r\n\t\t\t\tif (sv->controller == viewer)\r\n\t\t\t\t\tsv->controller = NULL;\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\tsv->controller = viewer;\r\n\t\t\t\t\tsv->controllersquencebias = viewer->netchan.outgoing_sequence - sv->netchan.outgoing_sequence;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\tif (i++ == viewer->menuop)\r\n\t\t\t{\t//back\r\n\t\t\t\tQW_SetMenu(viewer, MENU_ADMIN);\r\n\t\t\t}\r\n\t\t\tbreak;\r\n\t\t}\r\n\t\t//fallthrough\r\n\tcase MENU_SERVERS:\r\n\t\tif (buttonnum < 0)\r\n\t\t\tQW_SetMenu(viewer, MENU_MAIN);\r\n\t\telse if (!cluster->servers)\r\n\t\t{\r\n\t\t\tQW_StuffcmdToViewer(viewer, \"echo Please enter a server ip\\nmessagemode\\n\");\r\n\t\t\tstrcpy(viewer->expectcommand, \"insecadddemo\");\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tif (viewer->menuop < 0)\r\n\t\t\t\tviewer->menuop = 0;\r\n\t\t\ti = 0;\r\n\t\t\tmin = viewer->menuop - 10;\r\n\t\t\tif (min < 0)\r\n\t\t\t\tmin = 0;\r\n\t\t\tfor (sv = cluster->servers; sv && i<min; sv = sv->next, i++)\r\n\t\t\t{//skip over the early connections.\r\n\t\t\t}\r\n\t\t\tmin+=20;\r\n\t\t\tfor (; sv && i < min; sv = sv->next, i++)\r\n\t\t\t{\r\n\t\t\t\tif (i == viewer->menuop)\r\n\t\t\t\t{\r\n\t\t\t\t\t/*if (sv->parsingconnectiondata || !sv->modellist[1].name[0])\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tQW_PrintfToViewer(viewer, \"But that stream isn't connected\\n\");\r\n\t\t\t\t\t}\r\n\t\t\t\t\telse*/\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tQW_SetViewersServer(cluster, viewer, sv);\r\n\t\t\t\t\t\tQW_SetMenu(viewer, MENU_NONE);\r\n\t\t\t\t\t\tviewer->thinksitsconnected = false;\r\n\t\t\t\t\t}\r\n\t\t\t\t\tbreak;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t\tbreak;\r\n\tcase MENU_ADMIN:\r\n\t\ti = 0;\r\n\t\tif (i++ == viewer->menuop)\r\n\t\t{\t//connection stuff\r\n\t\t\tQW_SetMenu(viewer, MENU_ADMINSERVER);\r\n\t\t}\r\n\t\tif (i++ == viewer->menuop)\r\n\t\t{\t//qw port\r\n\t\t\tQW_StuffcmdToViewer(viewer, \"echo You will need to reconnect\\n\");\r\n\t\t\tcluster->qwlistenportnum += (buttonnum<0)?-1:1;\r\n\t\t}\r\n\t\tif (i++ == viewer->menuop)\r\n\t\t{\t//hostname\r\n\t\t\tstrcpy(viewer->expectcommand, \"hostname\");\r\n\t\t\tQW_StuffcmdToViewer(viewer, \"echo Please enter the new hostname\\nmessagemode\\n\");\r\n\t\t}\r\n\t\tif (i++ == viewer->menuop)\r\n\t\t{\t//master\r\n\t\t\tstrcpy(viewer->expectcommand, \"master\");\r\n\t\t\tQW_StuffcmdToViewer(viewer, \"echo Please enter the master dns or ip\\necho Enter '.' for masterless mode\\nmessagemode\\n\");\r\n\t\t}\r\n\t\tif (i++ == viewer->menuop)\r\n\t\t{\t//password\r\n\t\t\tstrcpy(viewer->expectcommand, \"password\");\r\n\t\t\tQW_StuffcmdToViewer(viewer, \"echo Please enter the new rcon password\\nmessagemode\\n\");\r\n\t\t}\r\n\t\tif (i++ == viewer->menuop)\r\n\t\t{\t//add server\r\n\t\t\tstrcpy(viewer->expectcommand, \"messagemode\");\r\n\t\t\tQW_StuffcmdToViewer(viewer, \"echo Please enter the new qtv server dns or ip\\naddserver\\n\");\r\n\t\t}\r\n\t\tif (i++ == viewer->menuop)\r\n\t\t{\t//add demo\r\n\t\t\tstrcpy(viewer->expectcommand, \"adddemo\");\r\n\t\t\tQW_StuffcmdToViewer(viewer, \"echo Please enter the name of the demo to play\\nmessagemode\\n\");\r\n\t\t}\r\n\t\tif (i++ == viewer->menuop)\r\n\t\t{\t//choke\r\n\t\t\tcluster->chokeonnotupdated ^= 1;\r\n\t\t}\r\n\t\tif (i++ == viewer->menuop)\r\n\t\t{\t//late forwarding\r\n\t\t\tcluster->lateforward ^= 1;\r\n\t\t}\r\n\t\tif (i++ == viewer->menuop)\r\n\t\t{\t//no talking\r\n\t\t\tcluster->notalking ^= 1;\r\n\t\t}\r\n\t\tif (i++ == viewer->menuop)\r\n\t\t{\t//nobsp\r\n\t\t\tcluster->nobsp ^= 1;\r\n\t\t}\r\n\t\tif (i++ == viewer->menuop)\r\n\t\t{\t//back\r\n\t\t\tQW_SetMenu(viewer, MENU_NONE);\r\n\t\t}\r\n\r\n\t\tbreak;\r\n\t}\r\n}\r\n\r\nvoid WriteStringSelection(netmsg_t *b, qboolean selected, const char *str)\r\n{\r\n\tif (selected)\r\n\t{\r\n\t\tWriteByte(b, 13);\r\n\t\twhile(*str)\r\n\t\t\tWriteByte(b, 128|*str++);\r\n\t}\r\n\telse\r\n\t{\r\n\t\tWriteByte(b, ' ');\r\n\t\twhile(*str)\r\n\t\t\tWriteByte(b, *str++);\r\n\t}\r\n}\r\n\r\nvoid Menu_Draw(cluster_t *cluster, viewer_t *viewer)\r\n{\r\n\tchar buffer[2048];\r\n\tchar str[256];\r\n\tsv_t *sv;\r\n\tint i, min;\r\n\tunsigned char *s;\r\n\r\n\tnetmsg_t m;\r\n\r\n\tif (viewer->backbuffered)\r\n\t\treturn;\r\n\r\n\tif (viewer->menunum == MENU_FORWARDING)\r\n\t\treturn;\r\n\r\n\tif (viewer->menuspamtime > cluster->curtime && viewer->menuspamtime < cluster->curtime + CENTERTIME*2000)\r\n\t\treturn;\r\n\tviewer->menuspamtime = cluster->curtime + CENTERTIME*1000;\r\n\r\n\tInitNetMsg(&m, buffer, sizeof(buffer));\r\n\r\n\tWriteByte(&m, svc_centerprint);\r\n\r\n\tWriteString2(&m, \"/PFTEQTV \"QTV_VERSION_STRING\"\\n\");\r\n\tWriteString2(&m, PROXYWEBSITE\"\\n\");\r\n\tWriteString2(&m, \"-------------\\n\");\r\n\t\r\n\tif (strcmp(cluster->hostname, DEFAULT_HOSTNAME))\r\n\t\tWriteString2(&m, cluster->hostname);\r\n\r\n\tswitch(viewer->menunum)\r\n\t{\r\n\tdefault:\r\n\t\tWriteString2(&m, \"bad menu\");\r\n\t\tbreak;\r\n\r\n\tcase MENU_MAIN:\r\n\t\t{\r\n\t\t\tWriteString2(&m, \"\\n\\x1d\\x1e\\x1e\\x1e\\x1e\\x1e\\x1e\\x1e\\x1e\\x1e\\x1e\\x1e\\x1e\\x1e\\x1e\\x1e\\x1e\\x1e\\x1e\\x1e\\x1e\\x1e\\x1e\\x1e\\x1e\\x1e\\x1e\\x1e\\x1e\\x1e\\x1e\\x1e\\x1e\\x1e\\x1f\\n\");\r\n\t\t\twhile (viewer->menuop < 0)\r\n\t\t\t\tviewer->menuop += MENU_MAIN_ITEMCOUNT;\r\n\t\t\twhile (viewer->menuop >= MENU_MAIN_ITEMCOUNT)\r\n\t\t\t\tviewer->menuop -= MENU_MAIN_ITEMCOUNT;\r\n\t\t\ti = viewer->menuop;\t\r\n\r\n\t\t\tWriteStringSelection(&m, i==MENU_MAIN_STREAMS,\t\t\"Streams          \");\r\n\t\t\tWriteStringSelection(&m, i==MENU_MAIN_CLIENTLIST,\t\"Client List      \");\r\n\t\t\tWriteByte(&m, '\\n');\r\n\t\t\tWriteStringSelection(&m, i==MENU_MAIN_NEWSTREAM,\t\"New Stream       \");\r\n\t\t\tWriteStringSelection(&m, i==MENU_MAIN_DEMOS,\t\t\"Demos            \");\r\n\t\t\tWriteByte(&m, '\\n');\r\n\t\t\tWriteStringSelection(&m, i==MENU_MAIN_SERVERBROWSER,\"Server Browser   \");\r\n\t\t\tWriteStringSelection(&m, i==MENU_MAIN_ADMIN,\t\t\"Admin            \");\r\n\t\t\tWriteByte(&m, '\\n');\r\n\t\t\tWriteStringSelection(&m, i==MENU_MAIN_PREVPROX,\t\t\"Previous Proxy   \");\r\n\t\t\tWriteStringSelection(&m, i==MENU_MAIN_NEXTPROX,\t\t\"Next Proxy       \");\r\n\t\t\tWriteByte(&m, '\\n');\r\n\t\t\tWriteStringSelection(&m, i==MENU_MAIN_HELP,\t\t\t\"Help             \");\r\n\t\t\tWriteString2(&m,\t\t\t\t\t\t\t\t\t\"                  \");\r\n\r\n\t\t\tWriteString2(&m, \"\\n\\x1d\\x1e\\x1e\\x1e\\x1e\\x1e\\x1e\\x1e\\x1e\\x1e\\x1e\\x1e\\x1e\\x1e\\x1e\\x1e\\x1e\\x1e\\x1e\\x1e\\x1e\\x1e\\x1e\\x1e\\x1e\\x1e\\x1e\\x1e\\x1e\\x1e\\x1e\\x1e\\x1e\\x1e\\x1f\\n\");\r\n\t\t}\r\n\t\tbreak;\r\n\r\n\tcase MENU_CLIENTS:\r\n\t\t{\r\n\t\t\tint start;\r\n\t\t\tviewer_t *v;\r\n\t\t\tchar *srv;\r\n\t\t\tint c;\r\n\t\t\tv = cluster->viewers;\r\n\r\n\t\t\tif (viewer->menuop < 0)\r\n\t\t\t\tviewer->menuop = 0;\r\n\t\t\tif (viewer->menuop > cluster->numviewers - 1)\r\n\t\t\t\tviewer->menuop = cluster->numviewers - 1;\r\n\r\n\t\t\tWriteString2(&m, \"\\nActive Clients\\n\\n\");\r\n\r\n\t\t\tstart = viewer->menuop & ~7;\r\n\t\t\tfor (i = 0; i < start && v; i++)\r\n\t\t\t\tv = v->next;\r\n\t\t\tfor (i = start; i < start+8 && v; i++, v = v->next)\r\n\t\t\t{\r\n\t\t\t\tfor (c = strlen(v->name); c < 14; c++)\r\n\t\t\t\t\tWriteByte(&m, ' ');\r\n\t\t\t\tWriteStringSelection(&m, viewer->menuop == i, v->name);\r\n\t\t\t\tWriteString2(&m, \": \");\r\n\t\t\t\tif (v->server)\r\n\t\t\t\t{\r\n\t\t\t\t\tif (!v->server->sourcefile && !v->server->parsingconnectiondata)\r\n\t\t\t\t\t\tsrv = v->server->map.hostname;\r\n\t\t\t\t\telse\r\n\t\t\t\t\t\tsrv = v->server->server;\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t\tsrv = \"None\";\r\n\t\t\t\tfor (c = 0; c < 20; c++)\r\n\t\t\t\t{\r\n\t\t\t\t\tif (*srv)\r\n\t\t\t\t\t\tWriteByte(&m, *srv++);\r\n\t\t\t\t\telse\r\n\t\t\t\t\t\tWriteByte(&m, ' ');\r\n\t\t\t\t}\r\n\r\n\t\t\t\tWriteByte(&m, '\\n');\r\n\t\t\t}\r\n\t\t\tfor (; i < start+8; i++)\r\n\t\t\t\tWriteByte(&m, '\\n');\r\n\t\t}\r\n\t\tbreak;\r\n\r\n\r\n\tcase MENU_DEMOS:\r\n\t\t{\r\n\t\t\tint start;\r\n\r\n\t\t\tWriteString2(&m, \"\\nAvailable Demos\\n\\n\");\r\n\r\n\t\t\tif (cluster->availdemoscount == 0)\r\n\t\t\t{\r\n\t\t\t\tWriteString2(&m, \"No demos are available\");\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\t\t\t\r\n\t\t\tif (viewer->menuop < 0)\r\n\t\t\t\tviewer->menuop = 0;\r\n\t\t\tif (viewer->menuop > cluster->availdemoscount-1)\r\n\t\t\t\tviewer->menuop = cluster->availdemoscount-1;\r\n\r\n\t\t\tstart = viewer->menuop & ~7;\r\n\t\t\tfor (i = start; i < start+8; i++)\r\n\t\t\t{\r\n\t\t\t\tchar cleanname[128];\r\n\t\t\t\tchar *us;\r\n\t\t\t\tstrlcpy(cleanname, cluster->availdemos[i].name, sizeof(cleanname));\r\n\t\t\t\tfor (us = cleanname; *us; us++)\r\n\t\t\t\t\tif (*us == '_')\r\n\t\t\t\t\t\t*us = ' ';\r\n\t\t\t\tWriteStringSelection(&m, i == viewer->menuop,\tcleanname);\r\n\t\t\t\tWriteByte(&m, '\\n');\r\n\t\t\t}\r\n\t\t}\r\n\t\tbreak;\r\n\r\n\tcase MENU_ADMINSERVER:\t//per-connection options\r\n\t\tif (viewer->server)\r\n\t\t{\r\n\t\t\tsv = viewer->server;\r\n\t\t\tWriteString2(&m, \"\\n\\nConnection Admin\\n\");\r\n\t\t\tWriteString2(&m, sv->map.hostname);\r\n\t\t\tif (sv->sourcefile)\r\n\t\t\t\tWriteString2(&m, \" (demo)\");\r\n\t\t\tWriteString2(&m, \"\\n\\n\");\r\n\r\n\t\t\tif (viewer->menuop < 0)\r\n\t\t\t\tviewer->menuop = 0;\r\n\r\n\t\t\ti = 0;\r\n\r\n\t\t\tWriteString2(&m, \" auto disconnect\");\r\n\t\t\tWriteString2(&m, (viewer->menuop==(i++))?\" \\r \":\" : \");\r\n\t\t\tswitch(viewer->server->autodisconnect)\r\n\t\t\t{\r\n\t\t\tdefault:\r\n\t\t\tcase AD_NO:\r\n\t\t\t\tsprintf(str, \"%-20s\", \"permanent connection\");\r\n\t\t\t\tbreak;\r\n\t\t\tcase AD_REVERSECONNECT:\r\n\t\t\t\tsprintf(str, \"%-20s\", \"when server disconnects\");\r\n\t\t\t\tbreak;\r\n\t\t\tcase AD_WHENEMPTY:\r\n\t\t\t\tsprintf(str, \"%-20s\", \"when inactive\");\r\n\t\t\t\tbreak;\r\n\t\t\tcase AD_STATUSPOLL:\r\n\t\t\t\tsprintf(str, \"%-20s\", \"idle when empty\");\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\t\t\tWriteString2(&m, str);\r\n\t\t\tWriteString2(&m, \"\\n\");\r\n\r\n\t\t\tWriteString2(&m, \"force disconnect\");\r\n\t\t\tWriteString2(&m, (viewer->menuop==(i++))?\" \\r \":\" : \");\r\n\t\t\tsprintf(str, \"%-20s\", \"...\");\r\n\t\t\tWriteString2(&m, str);\r\n\t\t\tWriteString2(&m, \"\\n\");\r\n\r\n\t\t\tWriteString2(&m, \"    take control\");\r\n\t\t\tWriteString2(&m, (viewer->menuop==(i++))?\" \\r \":\" : \");\r\n\t\t\tsprintf(str, \"%-20s\", \"...\");\r\n\t\t\tWriteString2(&m, str);\r\n\t\t\tWriteString2(&m, \"\\n\");\r\n\r\n\t\t\tWriteString2(&m, \"            back\");\r\n\t\t\tWriteString2(&m, (viewer->menuop==(i++))?\" \\r \":\" : \");\r\n\t\t\tsprintf(str, \"%-20s\", \"...\");\r\n\t\t\tWriteString2(&m, str);\r\n\t\t\tWriteString2(&m, \"\\n\");\r\n\r\n\t\t\tif (viewer->menuop >= i)\r\n\t\t\t\tviewer->menuop = i - 1;\r\n\r\n\t\t\tWriteString2(&m, \"\\n\");\r\n\r\n\t\t\tWriteString2(&m, \"          status\");\r\n\t\t\tWriteString2(&m, \" : \");\r\n\t\t\tsprintf(str, \"%-20s\", viewer->server->status);\r\n\t\t\tWriteString2(&m, str);\r\n\t\t\tWriteString2(&m, \"\\n\");\r\n\r\n\t\t\tbreak;\r\n\t\t}\r\n\t\t//fallthrough\r\n\tcase MENU_SERVERS:\t//connections list\r\n\r\n\t\tWriteString2(&m, \"\\n\\nServers\\n\\n\");\r\n\r\n\t\tif (!cluster->servers)\r\n\t\t{\r\n\t\t\tWriteString2(&m, \"No active connections\");\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tif (viewer->menuop < 0)\r\n\t\t\t\tviewer->menuop = 0;\r\n\t\t\ti = 0;\r\n\t\t\tmin = viewer->menuop - 10;\r\n\t\t\tif (min < 0)\r\n\t\t\t\tmin = 0;\r\n\t\t\tfor (sv = cluster->servers; sv && i<min; sv = sv->next, i++)\r\n\t\t\t{//skip over the early connections.\r\n\t\t\t}\r\n\t\t\tmin+=20;\r\n\t\t\tfor (; sv && i < min; sv = sv->next, i++)\r\n\t\t\t{\r\n\t\t\t\t//Info_ValueForKey(sv->serverinfo, \"hostname\", str, sizeof(str));\r\n\t\t\t\t//if (sv->parsingconnectiondata || !sv->modellist[1].name[0])\r\n\t\t\t\t//\tsnprintf(str, sizeof(str), \"%s\", sv->server);\r\n\t\t\t\tsnprintf(str, sizeof(str), \"%s\", *sv->map.hostname?sv->map.hostname:sv->server);\r\n\r\n\t\t\t\tif (i == viewer->menuop)\r\n\t\t\t\t\tfor (s = (unsigned char *)str; *s; s++)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tif ((unsigned)*s >= ' ')\r\n\t\t\t\t\t\t\t*s = 128 | (*s&~128);\r\n\t\t\t\t\t}\r\n\t\t\t\tWriteString2(&m, str);\r\n\t\t\t\tWriteString2(&m, \"\\n\");\r\n\t\t\t}\r\n\t\t}\r\n\t\tbreak;\r\n\r\n\tcase MENU_ADMIN:\t//admin menu\r\n\r\n\t\tWriteString2(&m, \"\\n\\nCluster Admin\\n\\n\");\r\n\r\n\t\tif (viewer->menuop < 0)\r\n\t\t\tviewer->menuop = 0;\r\n\t\ti = 0;\r\n\r\n\t\tWriteString2(&m, \" this connection\");\r\n\t\tWriteString2(&m, (viewer->menuop==(i++))?\" \\r \":\" : \");\r\n\t\tsprintf(str, \"%-20s\", \"...\");\r\n\t\tWriteString2(&m, str);\r\n\t\tWriteString2(&m, \"\\n\");\r\n\r\n\t\tWriteString2(&m, \"            port\");\r\n\t\tWriteString2(&m, (viewer->menuop==(i++))?\" \\r \":\" : \");\r\n\t\tsprintf(str, \"%-20i\", cluster->qwlistenportnum);\r\n\t\tWriteString2(&m, str);\r\n\t\tWriteString2(&m, \"\\n\");\r\n\r\n\t\tWriteString2(&m, \"        hostname\");\r\n\t\tWriteString2(&m, (viewer->menuop==(i++))?\" \\r \":\" : \");\r\n\t\tsprintf(str, \"%-20s\", cluster->hostname);\r\n\t\tWriteString2(&m, str);\r\n\t\tWriteString2(&m, \"\\n\");\r\n\r\n\t\tWriteString2(&m, \"          master\");\r\n\t\tWriteString2(&m, (viewer->menuop==(i++))?\" \\r \":\" : \");\r\n\t\tsprintf(str, \"%-20s\", cluster->master);\r\n\t\tWriteString2(&m, str);\r\n\t\tWriteString2(&m, \"\\n\");\r\n\r\n\t\tWriteString2(&m, \"        password\");\r\n\t\tWriteString2(&m, (viewer->menuop==(i++))?\" \\r \":\" : \");\r\n\t\tsprintf(str, \"%-20s\", \"...\");\r\n\t\tWriteString2(&m, str);\r\n\t\tWriteString2(&m, \"\\n\");\r\n\r\n\t\tWriteString2(&m, \"      add server\");\r\n\t\tWriteString2(&m, (viewer->menuop==(i++))?\" \\r \":\" : \");\r\n\t\tsprintf(str, \"%-20s\", \"...\");\r\n\t\tWriteString2(&m, str);\r\n\t\tWriteString2(&m, \"\\n\");\r\n\r\n\t\tWriteString2(&m, \"        add demo\");\r\n\t\tWriteString2(&m, (viewer->menuop==(i++))?\" \\r \":\" : \");\r\n\t\tsprintf(str, \"%-20s\", \"...\");\r\n\t\tWriteString2(&m, str);\r\n\t\tWriteString2(&m, \"\\n\");\r\n\r\n\t\tWriteString2(&m, \"           choke\");\r\n\t\tWriteString2(&m, (viewer->menuop==(i++))?\" \\r \":\" : \");\r\n\t\tsprintf(str, \"%-20s\", cluster->chokeonnotupdated?\"yes\":\"no\");\r\n\t\tWriteString2(&m, str);\r\n\t\tWriteString2(&m, \"\\n\");\r\n\r\n\t\tWriteString2(&m, \"delay forwarding\");\r\n\t\tWriteString2(&m, (viewer->menuop==(i++))?\" \\r \":\" : \");\r\n\t\tsprintf(str, \"%-20s\", cluster->lateforward?\"yes\":\"no\");\r\n\t\tWriteString2(&m, str);\r\n\t\tWriteString2(&m, \"\\n\");\r\n\r\n\t\tWriteString2(&m, \"         talking\");\r\n\t\tWriteString2(&m, (viewer->menuop==(i++))?\" \\r \":\" : \");\r\n\t\tsprintf(str, \"%-20s\", cluster->notalking?\"no\":\"yes\");\r\n\t\tWriteString2(&m, str);\r\n\t\tWriteString2(&m, \"\\n\");\r\n\r\n\t\tWriteString2(&m, \"           nobsp\");\r\n\t\tWriteString2(&m, (viewer->menuop==(i++))?\" \\r \":\" : \");\r\n\t\tsprintf(str, \"%-20s\", cluster->nobsp?\"yes\":\"no\");\r\n\t\tWriteString2(&m, str);\r\n\t\tWriteString2(&m, \"\\n\");\r\n\r\n\t\tWriteString2(&m, \"            back\");\r\n\t\tWriteString2(&m, (viewer->menuop==(i++))?\" \\r \":\" : \");\r\n\t\tsprintf(str, \"%-20s\", \"...\");\r\n\t\tWriteString2(&m, str);\r\n\t\tWriteString2(&m, \"\\n\");\r\n\r\n\t\tif (viewer->menuop >= i)\r\n\t\t\tviewer->menuop = i - 1;\r\n\t\tbreak;\r\n\t}\r\n\r\n\r\n\tWriteByte(&m, 0);\r\n\tSendBufferToViewer(viewer, m.data, m.cursize, true);\r\n}"
  },
  {
    "path": "fteqtv/msg.c",
    "content": "#include \"qtv.h\"\n\nvoid InitNetMsg(netmsg_t *b, void *buffer, int bufferlength)\n{\n\tb->data = buffer;\n\tb->maxsize = bufferlength;\n\tb->readpos = 0;\n\tb->cursize = 0;\n}\n\nunsigned char ReadByte(netmsg_t *b)\n{\n\tif (b->readpos >= b->cursize)\n\t{\n\t\tb->readpos = b->cursize+1;\n\t\treturn 0;\n\t}\n\treturn ((unsigned char *)b->data)[b->readpos++];\n}\nunsigned short ReadShort(netmsg_t *b)\n{\n\tint b1, b2;\n\tb1 = ReadByte(b);\n\tb2 = ReadByte(b);\n\n\treturn b1 | (b2<<8);\n}\nunsigned short ReadBigShort(netmsg_t *b)\n{\n\tint b1, b2;\n\tb1 = ReadByte(b);\n\tb2 = ReadByte(b);\n\n\treturn (b1<<8) | b2;\n}\nunsigned int ReadLong(netmsg_t *b)\n{\n\tint s1, s2;\n\ts1 = ReadShort(b);\n\ts2 = ReadShort(b);\n\n\treturn s1 | (s2<<16);\n}\nunsigned int ReadBigLong(netmsg_t *b)\n{\n\tunsigned int s1, s2;\n\ts1 = ReadBigShort(b);\n\ts2 = ReadBigShort(b);\n\n\treturn (s1<<16) | s2;\n}\n\nunsigned int BigLong(unsigned int val)\n{\n\tunion {\n\t\tunsigned int i;\n\t\tunsigned char c[4];\n\t} v;\n\n\tv.i = val;\n\treturn (v.c[0]<<24) | (v.c[1] << 16) | (v.c[2] << 8) | (v.c[3] << 0);\n}\n\nunsigned int SwapLong(unsigned int val)\n{\n\tunion {\n\t\tunsigned int i;\n\t\tunsigned char c[4];\n\t} v;\n\tunsigned char s;\n\n\tv.i = val;\n\ts = v.c[0];\n\tv.c[0] = v.c[3];\n\tv.c[3] = s;\n\ts = v.c[1];\n\tv.c[1] = v.c[2];\n\tv.c[2] = s;\n\n\treturn v.i;\n}\n\nfloat ReadFloat(netmsg_t *b)\n{\n\tunion {\n\t\tunsigned int i;\n\t\tfloat f;\n\t} u;\n\n\tu.i = ReadLong(b);\n\treturn u.f;\n}\nvoid ReadString(netmsg_t *b, char *string, int maxlen)\n{\n\tmaxlen--;\t//for null terminator\n\twhile(maxlen)\n\t{\n\t\t*string = ReadByte(b);\n\t\tif (!*string)\n\t\t\treturn;\n\t\tstring++;\n\t\tmaxlen--;\n\t}\n\t*string++ = '\\0';\t//add the null\n\n\tprintf(\"ReadString: buffer is too small\\n\");\n\twhile(ReadByte(b))\t//finish reading the string, even if we will loose part of it\n\t\t;\n}\nfloat ReadCoord(netmsg_t *b, unsigned int pext1)\n{\n\tif (pext1 & PEXT_FLOATCOORDS)\n\t\treturn ReadFloat(b);\n\telse\n\t\treturn (short)ReadShort(b) / 8.0;\n}\nfloat ReadAngle(netmsg_t *b, unsigned int pext1)\n{\n\tif (pext1 & PEXT_FLOATCOORDS)\n\t\treturn (ReadShort(b) * 360.0) / 0x10000;\n\telse\n\t\treturn (ReadByte(b) * 360.0) / 0x100;\n}\n\nvoid WriteByte(netmsg_t *b, unsigned char c)\n{\n\tif (b->cursize>=b->maxsize)\n\t\treturn;\n\t((unsigned char*)b->data)[b->cursize++] = c;\n}\nvoid WriteShort(netmsg_t *b, unsigned short l)\n{\n\tWriteByte(b, (l&0x00ff)>>0);\n\tWriteByte(b, (l&0xff00)>>8);\n}\nvoid WriteBigShort(netmsg_t *b, unsigned short l)\n{\n\tWriteByte(b, (l&0xff00)>>8);\n\tWriteByte(b, (l&0x00ff)>>0);\n}\nvoid WriteLong(netmsg_t *b, unsigned int l)\n{\n\tWriteByte(b, (l&0x000000ff)>>0);\n\tWriteByte(b, (l&0x0000ff00)>>8);\n\tWriteByte(b, (l&0x00ff0000)>>16);\n\tWriteByte(b, (l&0xff000000)>>24);\n}\nvoid WriteBigLong(netmsg_t *b, unsigned int l)\n{\n\tWriteByte(b, (l&0xff000000)>>24);\n\tWriteByte(b, (l&0x00ff0000)>>16);\n\tWriteByte(b, (l&0x0000ff00)>>8);\n\tWriteByte(b, (l&0x000000ff)>>0);\n}\nvoid WriteFloat(netmsg_t *b, float f)\n{\n\tunion {\n\t\tunsigned int i;\n\t\tfloat f;\n\t} u;\n\n\tu.f = f;\n\tWriteLong(b, u.i);\n}\nvoid WriteCoord(netmsg_t *b, float c, unsigned int pext)\n{\n\tif (pext & PEXT_FLOATCOORDS)\n\t\tWriteFloat(b, c);\n\telse\n\t\tWriteShort(b, (short)(c*8));\n}\nvoid WriteAngle(netmsg_t *b, float a, unsigned int pext)\n{\n\tif (pext & PEXT_FLOATCOORDS)\n\t\tWriteShort(b, (a/360.0)*0x10000);\n\telse\n\t\tWriteByte(b, (a/360.0)*0x100 + 0.49);\t//round better, to avoid rounding bias\n}\nvoid WriteString2(netmsg_t *b, const char *str)\n{\t//no null terminator, convienience function.\n\twhile(*str)\n\t\tWriteByte(b, *str++);\n}\nvoid WriteString(netmsg_t *b, const char *str)\n{\n\twhile(*str)\n\t\tWriteByte(b, *str++);\n\tWriteByte(b, 0);\n}\nvoid WriteData(netmsg_t *b, const void *data, int length)\n{\n\tint i;\n\tunsigned char *buf;\n\n\tif (b->cursize + length > b->maxsize)\t//urm, that's just too big. :(\n\t\treturn;\n\tbuf = (unsigned char*)b->data+b->cursize;\n\tfor (i = 0; i < length; i++)\n\t\t*buf++ = ((const unsigned char*)data)[i];\n\tb->cursize+=length;\n}\nvoid WriteCoordf(netmsg_t *b, unsigned int pext, float fl)\n{\n\tif (pext & PEXT_FLOATCOORDS)\n\t\tWriteFloat(b, fl);\n\telse\n\t\tWriteShort(b, fl*8);\n}\nvoid WriteAnglef(netmsg_t *b, unsigned int pext, float fl)\n{\n\tif (pext & PEXT_FLOATCOORDS)\n\t\tWriteShort(b, (fl/360)*0x10000);\n\telse\n\t\tWriteByte(b, (fl/360)*0x100);\n}"
  },
  {
    "path": "fteqtv/net_qtv.h",
    "content": "/*\r\nThis file is intended as a set of exports for an NQ-based engine.\r\nThis is supported _purely_ for clients, and will not work for servers (safe no-op).\r\n\r\n\r\n\r\n[EndUser] how to use:\r\nto join a qw server: connect \"udp:127.0.0.1:27500\"\r\nto watch a qtv stream: connect \"tcp:3@127.0.0.1:27599\"  (where '3' is the streamid)\r\nto watch an mvd demo: connect \"demo:blahblah.mvd\" - the demo will be loaded from $WORKINGDIR/qw/demos/ - note that $WORKINGDIR is NOT always the same dir\r\n\t\tas your engine is running from. The -basedir argument will break it, or engines that hunt down a 'proper' installation of quake instead.\r\n\r\n\r\n[Developer] how to incorporate into an nq engine:\r\ncopy this file (net_qtv.h) into your source directory, next to your other net_*.h files\r\nload up net_win.c\r\nfind the #include \"net_wins.h\" line.\r\ndupe it, call it net_qtv.h\r\nfind the net_landrivers array. Dupe the first block, then edit the first block to be all QTV_foo functions.\r\nbump net_numlandrivers.\r\nFor non-window operating systems, you'll need to do the same, just figure out which net_win.c equivelent function it uses first. :P\r\ncertain engines may do weird things with the port. probably its best to just use Cmd_Args() for the connect command instead of Cmd_Argv(1), and to add\r\n\t\tport parsing to XXXX_GetAddrFromName instead of messing around with port cvars etc and ruining server configs.\r\n\t\tIf your engine already has weird port behaviour, then its entirely your problem to fix. :P\r\nYou probably want to tweak your menus a little to clean up the nq/qw/qtv connection distinctions.\r\nIf you do want to make changes to libqtv, please consider joining the FTE team (or at least the irc channel) in order to contribute without forking.\r\n\r\n[Developer] how to compile libqtv:\r\ncflags MUST define 'LIBQTV' or it won't compile properly.\r\nThe relevent exports are all tagged as 'EXPORT void PUBLIC fname(...)' (dllexport+cdecl in windows), feel free to define those properly if you're making a linux shared object without exporting all (potentially conflicting) internals.\r\nThis means you can compile it as a dll without any issues, one with a standardized interface. Any libqtv-specific bugfixes can be released independantly from engine(s).\r\nCompiling a dll with msvc will generally automatically produce a .lib which you can directly link against. Alternatively, include both projects in your workspace and set up dependancies properly and it'll be automatically imported.\r\n\r\n[PowerUser] issues:\r\nits a full qtv proxy, but you can't get admin/rcon access to it.\r\nit doesn't read any configs, and has no console, thus you cannot set an rcon password/port.\r\nwithout console/rcon, you cannot enable any listening ports for other users.\r\nif you need a public qtv proxy, use a standalone version.\r\n*/\r\n\r\n//#define EXPORT is defined declspec export for dll builds.\r\n#ifdef _WIN32\r\n#define PUBLIC __cdecl\r\n#endif\r\n\r\n#ifndef EXPORT\r\n#define EXPORT\r\n#endif\r\n#ifndef PUBLIC\r\n#define PUBLIC\r\n#endif\r\n#define PUBEXPORT EXPORT\r\n\r\n//vanilla 'net_win.c' datagram driver function listings\r\nPUBEXPORT int PUBLIC QTV_Init (void);\r\nPUBEXPORT void PUBLIC QTV_Shutdown (void);\r\nPUBEXPORT void PUBLIC QTV_Listen (qboolean state);\r\nPUBEXPORT int PUBLIC QTV_OpenSocket (int port);\r\nPUBEXPORT int PUBLIC QTV_CloseSocket (int socket);\r\nPUBEXPORT int PUBLIC QTV_Connect (int socket, struct qsockaddr *addr);\r\nPUBEXPORT int PUBLIC QTV_CheckNewConnections (void);\r\nPUBEXPORT int PUBLIC QTV_Read (int socket, byte *buf, int len, struct qsockaddr *addr);\r\nPUBEXPORT int PUBLIC QTV_Write (int socket, byte *buf, int len, struct qsockaddr *addr);\r\nPUBEXPORT int PUBLIC QTV_Broadcast (int socket, byte *buf, int len);\r\nPUBEXPORT char *PUBLIC QTV_AddrToString (struct qsockaddr *addr);\r\nPUBEXPORT int PUBLIC QTV_StringToAddr (char *string, struct qsockaddr *addr);\t//port is part of the string. libqtv will use its own default port.\r\nPUBEXPORT int PUBLIC QTV_GetSocketAddr (int socket, struct qsockaddr *addr);\r\nPUBEXPORT int PUBLIC QTV_GetNameFromAddr (struct qsockaddr *addr, char *name);\r\nPUBEXPORT int PUBLIC QTV_GetAddrFromName (char *name, struct qsockaddr *addr);\r\nPUBEXPORT int PUBLIC QTV_AddrCompare (struct qsockaddr *addr1, struct qsockaddr *addr2);\r\nPUBEXPORT int PUBLIC QTV_GetSocketPort (struct qsockaddr *addr);\r\nPUBEXPORT int PUBLIC QTV_SetSocketPort (struct qsockaddr *addr, int port);\r\n\r\n//additional functions for enhanced engines that changed the net_dgrm system api or other uses.\r\nPUBEXPORT void PUBLIC QTV_Command (char *command, char *resulttext, int resultlen);\t//sends a console command to the qtv proxy. you can make it a formal proxy with this. must have been inited.\r\nPUBEXPORT void PUBLIC QTV_RunFrame (void);\t//runs a proxy frame without needing a connection to be active.\r\nPUBEXPORT int PUBLIC QTV_StringPortToAddr (char *string, int port, struct qsockaddr *addr);\t//port may be part of the string. the specified port will be used, or just pass 0 for libqtv to make it up. generally you'll want to pass the 'port' cvar for port, and the exact argument that was typed in the string arg."
  },
  {
    "path": "fteqtv/netchan.c",
    "content": "/*\nCopyright (C) 1996-1997 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\nSee the included (GNU.txt) GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n*/\n\n\n#include \"qtv.h\"\n#include <string.h>\n\n#define curtime Sys_Milliseconds()\n\n\nvoid NET_InitUDPSocket(cluster_t *cluster, int port, int socketid)\n{\n\tint sock;\n\n\tint pf;\n\tstruct sockaddr *address;\n\tstruct sockaddr_in\taddress4;\n\tstruct sockaddr_in6\taddress6;\n\tint addrlen;\n\n\tunsigned long nonblocking = true;\n\tunsigned long v6only = false;\n\n#pragma message(\"fixme\")\n\tswitch(socketid)\n\t{\n\tcase SG_IPV6:\n\t\tpf = PF_INET6;\n\t\tmemset(&address6, 0, sizeof(address6));\n\t\taddress6.sin6_family = AF_INET6;\n\t\taddress6.sin6_port = htons((u_short)port);\n\t\taddress = (struct sockaddr*)&address6;\n\t\taddrlen = sizeof(address6);\n\t\tbreak;\n\tcase SG_IPV4:\n\t\tpf = PF_INET;\n\t\taddress4.sin_family = AF_INET;\n\t\taddress4.sin_addr.s_addr = INADDR_ANY;\n\t\taddress4.sin_port = htons((u_short)port);\n\t\taddress = (struct sockaddr*)&address4;\n\t\taddrlen = sizeof(address4);\n\t\tbreak;\n\tdefault:\n\t\treturn;\t//erk\n\t}\n\n\tif (socketid == SG_IPV4 && !v6only && cluster->qwdsocket[SG_IPV6] != INVALID_SOCKET)\n\t{\n\t\tint sz = sizeof(v6only);\n\t\tif (getsockopt(cluster->qwdsocket[SG_IPV6], IPPROTO_IPV6, IPV6_V6ONLY, (char *)&v6only, &sz) == 0 && !v6only)\n\t\t\tport = 0;\n\t}\n\n\tif (!port)\n\t{\n\t\tif (cluster->qwdsocket[socketid] != INVALID_SOCKET)\n\t\t{\n\t\t\tclosesocket(cluster->qwdsocket[socketid]);\n\t\t\tcluster->qwdsocket[socketid] = INVALID_SOCKET;\n\t\t\tSys_Printf(cluster, \"closed udp%i port\\n\", socketid?6:4);\n\t\t}\n\t\treturn;\n\t}\n\n\tif ((sock = socket (pf, SOCK_DGRAM, IPPROTO_UDP)) == INVALID_SOCKET)\n\t{\n\t\treturn;\n\t}\n\n\tif (ioctlsocket (sock, FIONBIO, &nonblocking) == -1)\n\t{\n\t\tclosesocket(sock);\n\t\treturn;\n\t}\n\n\tif (pf == AF_INET6)\n\t\tif (setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&v6only, sizeof(v6only)) == -1)\n\t\t\tv6only = true;\n\n\tif (bind (sock, (void *)address, addrlen) == -1)\n\t{\n\t\tprintf(\"socket bind error %i (%s)\\n\", qerrno, strerror(qerrno));\n\t\tclosesocket(sock);\n\t\treturn;\n\t}\n\n\tif (cluster->qwdsocket[socketid] != INVALID_SOCKET)\n\t{\n\t\tclosesocket(cluster->qwdsocket[socketid]);\n\t\tSys_Printf(cluster, \"closed udp%i port\\n\", socketid?6:4);\n\t}\n\tcluster->qwdsocket[socketid] = sock;\n\tif (v6only)\n\t\tSys_Printf(cluster, \"opened udp%i port %i\\n\", socketid?6:4, port);\n\telse\n\t\tSys_Printf(cluster, \"opened udp port %i\\n\", port);\n}\n\nSOCKET NET_ChooseSocket(SOCKET sock[SOCKETGROUPS], netadr_t *toadr, netadr_t ina)\n{\n#ifdef AF_INET6\n\tif (((struct sockaddr *)ina.sockaddr)->sa_family == AF_INET6)\n\t{\n\t\t*toadr = ina;\n\t\treturn sock[SG_IPV6];\n\t}\n\tif (sock[SG_IPV4] == INVALID_SOCKET && sock[SG_IPV6] != INVALID_SOCKET)\n\t{\n\t\tstruct sockaddr_in6 *out = (struct sockaddr_in6*)toadr->sockaddr;\n\t\tstruct sockaddr_in *in = (struct sockaddr_in*)ina.sockaddr;\n\t\ttoadr->tcpcon = ina.tcpcon;\n\n\t\tmemset(out, 0, sizeof(*out));\n\t\tout->sin6_family = AF_INET6;\n\t\t*(short*)&out->sin6_addr.s6_addr[10] = 0xffff;\n\t\t*(int*)&out->sin6_addr.s6_addr[12] = in->sin_addr.s_addr;\n\t\tout->sin6_port = in->sin_port;\n\t\treturn sock[SG_IPV6];\n\t}\n#endif\n\t*toadr = ina;\n\treturn sock[SG_IPV4];\n}\n\n#ifdef LIBQTV\nvoid QTV_DoReceive(void *data, int length);\n#endif\nvoid NET_SendPacket(cluster_t *cluster, SOCKET sock, int length, void *data, netadr_t adr)\n{\n\tint ret;\n\tint alen;\n\n#ifdef LIBQTV\n\tif (((struct sockaddr *)&adr.sockaddr)->sa_family == AF_UNSPEC)\n\t{\n\t\tQTV_DoReceive(data, length);\n\t\treturn;\n\t}\n#endif\n#ifdef AF_INET6\n\tif (((struct sockaddr *)&adr.sockaddr)->sa_family == AF_INET6)\n\t\talen = sizeof(struct sockaddr_in6);\n\telse\n#endif\n\t\talen = sizeof(struct sockaddr_in);\n\n\tif (adr.tcpcon)\n\t{\n\t\ttcpconnect_t *dest;\n\t\tfor (dest = cluster->tcpconnects; dest; dest = dest->next)\n\t\t{\n\t\t\tif (dest == adr.tcpcon)\n\t\t\t\tbreak;\n\t\t}\n\n\t\tif (dest)\n\t\t{\n\t\t\tint l;\n\n\t\t\tif (dest->websocket.websocket)\n\t\t\t{\n\t\t\t\tint datatype = 2; //1=utf-8, 2=binary\n\t\t\t\tint enclen = 0, c;\n\n\t\t\t\tif (datatype == 2)\n\t\t\t\t\tenclen = length;\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tfor (c = 0; c < length; c++)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (((unsigned char*)data)[c] == 0 || ((unsigned char*)data)[c] >= 0x80)\n\t\t\t\t\t\t\tenclen += 2;\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tenclen += 1;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (dest->outbuffersize + 4+enclen < sizeof(dest->outbuffer))\n\t\t\t\t{\n\t\t\t\t\tif (enclen >= 126)\n\t\t\t\t\t{\n\t\t\t\t\t\tdest->outbuffer[dest->outbuffersize++] = 0x80|datatype;\n\t\t\t\t\t\tdest->outbuffer[dest->outbuffersize++] = 126;\n\t\t\t\t\t\tdest->outbuffer[dest->outbuffersize++] = enclen>>8;\n\t\t\t\t\t\tdest->outbuffer[dest->outbuffersize++] = enclen;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tdest->outbuffer[dest->outbuffersize++] = 0x80|datatype;\n\t\t\t\t\t\tdest->outbuffer[dest->outbuffersize++] = enclen;\n\t\t\t\t\t}\n\t\t\t\t\tif (datatype == 2)\n\t\t\t\t\t{\n\t\t\t\t\t\tmemcpy(dest->outbuffer+dest->outbuffersize, data, enclen);\n\t\t\t\t\t\tdest->outbuffersize += enclen;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\twhile(length-->0)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tc = *(unsigned char*)data;\n\t\t\t\t\t\t\tdata = (char*)data+1;\n\t\t\t\t\t\t\tif (!c)\n\t\t\t\t\t\t\t\tc |= 0x100;\t/*will get truncated at the other end*/\n\t\t\t\t\t\t\tif (c >= 0x80)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tdest->outbuffer[dest->outbuffersize++] = 0xc0 | (c>>6);\n\t\t\t\t\t\t\t\tdest->outbuffer[dest->outbuffersize++] = 0x80 | (c & 0x3f);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\tdest->outbuffer[dest->outbuffersize++] = c;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (dest->outbuffersize + length < sizeof(dest->outbuffer))\n\t\t\t\t{\n\t\t\t\t\tdest->outbuffer[dest->outbuffersize++] = length>>8;\n\t\t\t\t\tdest->outbuffer[dest->outbuffersize++] = length&0xff;\n\t\t\t\t\tmemcpy(dest->outbuffer+dest->outbuffersize, data, length);\n\t\t\t\t\tdest->outbuffersize += length;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (dest->outbuffersize)\n\t\t\t{\n\t\t\t\tl = send(dest->sock, dest->outbuffer, dest->outbuffersize, 0);\n\t\t\t\tif (l > 0)\n\t\t\t\t{\n\t\t\t\t\tmemmove(dest->outbuffer, dest->outbuffer+l, dest->outbuffersize-l);\n\t\t\t\t\tdest->outbuffersize-=l;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn;\n\t}\n\n\tret = sendto(sock, data, length, 0, (struct sockaddr *)&adr.sockaddr, alen);\n\tif (ret < 0)\n\t{\n\t\tint er = qerrno;\n\t\tif (er == NET_EWOULDBLOCK || er == NET_EAGAIN)\n\t\t\treturn;\n\n\t\tSys_Printf(cluster, \"udp send error %i (%s)\\n\", er, strerror(er));\n\t}\n}\n\n#if 0\nint Netchan_IsLocal (netadr_t adr)\n{\n\tstruct sockaddr_in *sadr = (struct sockaddr_in *)&adr;\n\tunsigned char *bytes;\n\tswitch(((struct sockaddr *)&adr)->sa_family)\n\t{\n\tcase AF_INET:\n\t\tbytes = (unsigned char *)&((struct sockaddr_in *)&adr)->sin_addr;\n\t\tif (bytes[0] == 127 &&\t/*actualy, it should be only the first octet, but hey*/\n\t\t\tbytes[1] == 0 &&\n\t\t\tbytes[2] == 0 &&\n\t\t\tbytes[3] == 1)\n\t\t\treturn true;\n\t\treturn false;\n\tcase AF_INET6:\n\t\tbytes = (unsigned char *)&((struct sockaddr_in6 *)&adr)->sin6_addr;\n\t\tif (bytes[ 0] == 0 &&\n\t\t\tbytes[ 1] == 0 &&\n\t\t\tbytes[ 2] == 0 &&\n\t\t\tbytes[ 3] == 0 &&\n\t\t\tbytes[ 4] == 0 &&\n\t\t\tbytes[ 5] == 0 &&\n\t\t\tbytes[ 6] == 0 &&\n\t\t\tbytes[ 7] == 0 &&\n\t\t\tbytes[ 8] == 0 &&\n\t\t\tbytes[ 9] == 0 &&\n\t\t\tbytes[10] == 0 &&\n\t\t\tbytes[11] == 0 &&\n\t\t\tbytes[12] == 0 &&\n\t\t\tbytes[13] == 0 &&\n\t\t\tbytes[14] == 0 &&\n\t\t\tbytes[15] == 1)\n\t\t\treturn true;\n\t\treturn false;\n\tdefault:\n\t\treturn false;\n\t}\n}\n#endif\n\n\n\n\n\n\n\n\n#define\tPACKET_HEADER\t8\n\n/*\n\npacket header\n-------------\n31\tsequence\n1\tdoes this message contain a reliable payload\n31\tacknowledge sequence\n1\tacknowledge receipt of even/odd message\n16  qport\n\nThe remote connection never knows if it missed a reliable message, the\nlocal side detects that it has been dropped by seeing a sequence acknowledge\nhigher than the last reliable sequence, but without the correct even/odd\nbit for the reliable set.\n\nIf the sender notices that a reliable message has been dropped, it will be\nretransmitted.  It will not be retransmitted again until a message after\nthe retransmit has been acknowledged and the reliable still failed to get there.\n\nIf the sequence number is -1, the packet should be handled without a netcon.\n\nThe reliable message can be added to at any time by doing\nMSG_Write* (&netchan->message, <data>).\n\nIf the message buffer is overflowed, either by a single message, or by\nmultiple frames worth piling up while the last reliable transmit goes\nunacknowledged, the netchan signals a fatal error.\n\nReliable messages are always placed first in a packet, then the unreliable\nmessage is included if there is sufficient room.\n\nTo the receiver, there is no distinction between the reliable and unreliable\nparts of the message, they are just processed out as a single larger message.\n\nIllogical packet sequence numbers cause the packet to be dropped, but do\nnot kill the connection.  This, combined with the tight window of valid\nreliable acknowledgement numbers provides protection against malicious\naddress spoofing.\n\nThe qport field is a workaround for bad address translating routers that\nsometimes remap the client's source port on a packet during gameplay.\n\nIf the base part of the net address matches and the qport matches, then the\nchannel matches even if the IP port differs.  The IP port should be updated\nto the new value before sending out any replies.\n\n\n*/\n\n\n/*\n===============\nNetchan_OutOfBand\n\nSends an out-of-band datagram\n================\n*/\nvoid Netchan_OutOfBandSocket (cluster_t *cluster, SOCKET sock, netadr_t *adr, int length, void *data)\n{\n\tnetmsg_t\tsend;\n\tunsigned char\t\tsend_buf[MAX_MSGLEN + PACKET_HEADER];\n\n// write the packet header\n\tInitNetMsg (&send, send_buf, sizeof(send_buf));\n\n\tWriteLong (&send, -1);\t// -1 sequence means out of band\n\tWriteData (&send, data, length);\n\n// send the datagram\n\tNET_SendPacket (cluster, sock, send.cursize, send.data, *adr);\n}\n\n/*\n===============\nNetchan_OutOfBand\n\nSends an out-of-band datagram\n================\n*/\nvoid Netchan_OutOfBand (cluster_t *cluster, netadr_t adr, int length, void *data)\n{\n\tnetadr_t realadr;\n\tNetchan_OutOfBandSocket(cluster, NET_ChooseSocket(cluster->qwdsocket, &realadr, adr), &realadr, length, data);\n}\n\n/*\n===============\nNetchan_OutOfBandPrint\n\nSends a text message in an out-of-band datagram\n================\n*/\nvoid Netchan_OutOfBandPrint (cluster_t *cluster, netadr_t adr, char *format, ...)\n{\n\tva_list\t\targptr;\n\tchar\t\tstring[8192];\n\n\tva_start (argptr, format);\n#ifdef _WIN32\n\t_vsnprintf (string, sizeof(string) - 1, format, argptr);\n\tstring[sizeof(string) - 1] = '\\0';\n#else\n\tvsnprintf (string, sizeof(string), format, argptr);\n#endif // _WIN32\n\tva_end (argptr);\n\n\tNetchan_OutOfBandSocket(cluster, NET_ChooseSocket(cluster->qwdsocket, &adr, adr), &adr, strlen(string), (unsigned char *)string);\n}\n\n/*\n===============\nNetchan_Init\n\n===============\n*/\nvoid Netchan_Init (netadr_t adr)\n{\n\n}\n\n\n/*\n==============\nNetchan_Setup\n\ncalled to open a channel to a remote system\n==============\n*/\nvoid Netchan_Setup (SOCKET sock, netchan_t *chan, netadr_t adr, int qport, qboolean isclient)\n{\n\tmemset (chan, 0, sizeof(*chan));\n\n\tchan->sock = sock;\n\tmemcpy(&chan->remote_address, &adr, sizeof(netadr_t));\n\tchan->qport = qport;\n\tchan->isclient = isclient;\n\n\tchan->last_received = curtime;\n\n\tInitNetMsg(&chan->message, chan->message_buf, sizeof(chan->message_buf));\n\n\tchan->message.allowoverflow = true;\n\n\tchan->rate = 10000*1000;\n}\n\n\n/*\n===============\nNetchan_CanPacket\n\nReturns true if the bandwidth choke isn't active\n================\n*/\nqboolean Netchan_CanPacket (netchan_t *chan)\n{\n\tunsigned int t;\n\t// unlimited bandwidth for local client\n//\tif (chan->remote_address.type == NA_LOOPBACK)\n//\t\treturn true;\n\n\tt = curtime;\n\tif (chan->cleartime < t)\n\t\treturn true;\n\treturn false;\n}\n\n\n/*\n===============\nNetchan_CanReliable\n\nReturns true if the bandwidth choke isn't\n================\n*/\nqboolean Netchan_CanReliable (netchan_t *chan)\n{\n\tif (chan->reliable_length)\n\t\treturn false;\t\t\t// waiting for ack\n\treturn Netchan_CanPacket (chan);\n}\n\n/*\n===============\nNetchan_Transmit\n\ntries to send an unreliable message to a connection, and handles the\ntransmition / retransmition of the reliable messages.\n\nA 0 length will still generate a packet and deal with the reliable messages.\n================\n*/\nvoid Netchan_Transmit (cluster_t *cluster, netchan_t *chan, int length, const void *data)\n{\n\tunsigned int t;\n\tnetmsg_t\tsend;\n\tunsigned char\tsend_buf[MAX_NQMSGLEN + PACKET_HEADER];\n\tqboolean\t\tsend_reliable;\n\tunsigned\tw1, w2;\n\n\tif (chan->isnqprotocol)\n\t{\n\t\tint i;\n\n\t\tsend.data = send_buf;\n\t\tsend.maxsize = MAX_NQMSGLEN + PACKET_HEADER;\n\t\tsend.cursize = 0;\n\n\t\tif (!chan->reliable_length && chan->message.cursize)\n\t\t{\n\t\t\tmemcpy (chan->reliable_buf, chan->message_buf, chan->message.cursize);\n\t\t\tchan->reliable_length = chan->message.cursize;\n\t\t\tchan->reliable_start = 0;\n\t\t\tchan->message.cursize = 0;\n\t\t}\n\n\t\ti = chan->reliable_length - chan->reliable_start;\n\t\tif (i>0)\n\t\t{\n\t\t\tWriteLong(&send, 0);\n\t\t\tWriteLong(&send, SwapLong(chan->reliable_sequence));\n\t\t\tif (i > MAX_NQDATAGRAM)\n\t\t\t\ti = MAX_NQDATAGRAM;\n\n\t\t\tWriteData (&send, chan->reliable_buf+chan->reliable_start, i);\n//\t\t\tif (length && send.cursize + length < send.maxsize)\n//\t\t\t{\t//throw the unreliable packet into the same one as the reliable (but not sent reliably)\n//\t\t\t\tWriteData (&send, data, length);\n//\t\t\t\tlength = 0;\n//\t\t\t}\n\n\n\t\t\tif (chan->reliable_start+i == chan->reliable_length)\n\t\t\t\t*(int*)send_buf = BigLong(NETFLAG_DATA | NETFLAG_EOM | send.cursize);\n\t\t\telse\n\t\t\t\t*(int*)send_buf = BigLong(NETFLAG_DATA | send.cursize);\n\t\t\tNET_SendPacket(cluster, chan->sock, send.cursize, send.data, chan->remote_address);\n\t\t\tsend.cursize = 0;\n\n\t\t\tif (chan->cleartime < curtime)\n\t\t\t\tchan->cleartime = curtime + (int)(send.cursize*chan->rate);\n\t\t\telse\n\t\t\t\tchan->cleartime += (int)(send.cursize*chan->rate);\n\t\t}\n//\t\telse if (!length)\n//\t\t{\n//\t\t\tlength = 1;\n//\t\t\tdata = \"\\x01\";\n//\t\t}\n\n\t\t//send out the unreliable (if still unsent)\n\t\tif (length)\n\t\t{\n\t\t\tWriteLong(&send, 0);\n\t\t\tWriteLong(&send, SwapLong(chan->outgoing_unreliable));\n\t\t\tchan->outgoing_unreliable++;\n\n\t\t\tWriteData (&send, data, length);\n\n\t\t\t*(int*)send_buf = BigLong(NETFLAG_UNRELIABLE | send.cursize);\n\t\t\tNET_SendPacket (cluster, chan->sock, send.cursize, send.data, chan->remote_address);\n\n\t\t\tif (chan->cleartime < curtime)\n\t\t\t\tchan->cleartime = (int)(curtime + send.cursize*chan->rate);\n\t\t\telse\n\t\t\t\tchan->cleartime += (int)(send.cursize*chan->rate);\n\n\t\t\tsend.cursize = 0;\n\t\t}\n\t\treturn;\n\t}\n\n\n\n\n\n\n// check for message overflow\n\tif (chan->message.overflowed)\n\t{\n\t\tchan->drop = true;\n//\t\tprintf (\"%s:Outgoing message overflow\\n\"\n//\t\t\t, NET_AdrToString (chan->remote_address));\n\t\treturn;\n\t}\n\n// if the remote side dropped the last reliable message, resend it\n\tsend_reliable = false;\n\n\tif (chan->incoming_acknowledged > chan->last_reliable_sequence\n\t&& chan->incoming_reliable_acknowledged != chan->reliable_sequence)\n\t\tsend_reliable = true;\n\n// if the reliable transmit buffer is empty, copy the current message out\n\tif (!chan->reliable_length && chan->message.cursize)\n\t{\n\t\tmemcpy (chan->reliable_buf, chan->message_buf, chan->message.cursize);\n\t\tchan->reliable_length = chan->message.cursize;\n\t\tchan->message.cursize = 0;\n\t\tchan->reliable_sequence ^= 1;\n\t\tsend_reliable = true;\n\t}\n\n// write the packet header\n\tsend.data = send_buf;\n\tsend.maxsize = sizeof(send_buf);\n\tsend.readpos = send.cursize = 0;\n\n\tw1 = chan->outgoing_sequence | (send_reliable<<31);\n\tw2 = chan->incoming_sequence | (chan->incoming_reliable_sequence<<31);\n\n\tchan->outgoing_sequence++;\n\n\tWriteLong (&send, w1);\n\tWriteLong (&send, w2);\n\n\t// send the qport if we are a client\n\tif (chan->isclient)\n\t\tWriteShort (&send, chan->qport);\n\n// copy the reliable message to the packet first\n\tif (send_reliable)\n\t{\n\t\tWriteData (&send, chan->reliable_buf, chan->reliable_length);\n\t\tchan->last_reliable_sequence = chan->outgoing_sequence;\n\t}\n\n// add the unreliable part if space is available\n\tif (send.maxsize - send.cursize >= length)\n\t\tWriteData (&send, data, length);\n\n// send the datagram\n//\ti = chan->outgoing_sequence & (MAX_LATENT-1);\n//\tchan->outgoing_size[i] = send.cursize;\n//\tchan->outgoing_time[i] = curtime;\n\n\tNET_SendPacket (cluster, chan->sock, send.cursize, send.data, chan->remote_address);\n\n\tt = curtime;\n\tif (chan->cleartime < t)\n\t\tchan->cleartime = t + (int)(send.cursize*chan->rate);\n\telse\n\t\tchan->cleartime += (int)(send.cursize*chan->rate);\n#ifndef CLIENTONLY\n//\tif (chan->sock == NS_SERVER && sv_paused.value)\n//\t\tchan->cleartime = curtime;\n#endif\n\n/*\tif (showpackets.value)\n\t\tCom_Printf (\"--> s=%i(%i) a=%i(%i) %i\\n\"\n\t\t\t, chan->outgoing_sequence\n\t\t\t, send_reliable\n\t\t\t, chan->incoming_sequence\n\t\t\t, chan->incoming_reliable_sequence\n\t\t\t, send.cursize);\n*/\n}\n\n\n\n\nqboolean NQNetchan_Process(cluster_t *cluster, netchan_t *chan, netmsg_t *msg)\n{\n\tint header;\n\tint sequence;\n\tint drop;\n\n\tmsg->readpos = 0;\n\n\theader = SwapLong(ReadLong(msg));\n\tif (msg->cursize != (header & NETFLAG_LENGTH_MASK))\n\t\treturn false;\t//size was wrong, couldn't have been ours.\n\n\tif (header & NETFLAG_CTL)\n\t\treturn false;\t//huh?\n\n\tsequence = SwapLong(ReadLong(msg));\n\n\tif (header & NETFLAG_ACK)\n\t{\n\t\tif (sequence == chan->reliable_sequence)\n\t\t{\n\t\t\tchan->reliable_start += MAX_NQDATAGRAM;\n\t\t\tif (chan->reliable_start >= chan->reliable_length)\n\t\t\t{\n\t\t\t\tchan->reliable_length = 0;\t//they got the entire message\n\t\t\t\tchan->reliable_start = 0;\n\t\t\t}\n\t\t\tchan->incoming_reliable_acknowledged = chan->reliable_sequence;\n\t\t\tchan->reliable_sequence++;\n\n\t\t\tchan->last_received = curtime;\n\t\t}\n//\t\telse if (sequence < chan->reliable_sequence)\n//\t\t\tCon_DPrintf(\"Stale ack recieved\\n\");\n//\t\telse if (sequence > chan->reliable_sequence)\n//\t\t\tCon_Printf(\"Future ack recieved\\n\");\n\n\t\treturn false;\t//don't try execing the 'payload'. I hate ack packets.\n\t}\n\n\tif (header & NETFLAG_UNRELIABLE)\n\t{\n\t\tif (sequence < chan->incoming_unreliable)\n\t\t{\n//\t\t\tCon_DPrintf(\"Stale datagram recieved\\n\");\n\t\t\treturn false;\n\t\t}\n\t\tdrop = sequence - chan->incoming_unreliable - 1;\n\t\tif (drop > 0)\n\t\t{\n//\t\t\tCon_DPrintf(\"Dropped %i datagrams\\n\", drop);\n//\t\t\tchan->drop_count += drop;\n\t\t}\n\t\tchan->incoming_unreliable = sequence;\n\n\t\tchan->last_received = curtime;\n\n\t\tchan->incoming_acknowledged++;\n//\t\tchan->good_count++;\n\t\treturn 1;\n\t}\n\tif (header & NETFLAG_DATA)\n\t{\n\t\tint runt[2];\n\t\t//always reply. a stale sequence probably means our ack got lost.\n\t\trunt[0] = BigLong(NETFLAG_ACK | 8);\n\t\trunt[1] = BigLong(sequence);\n\t\tNET_SendPacket (cluster, chan->sock, 8, (void*)runt, chan->remote_address);\n\n\t\tchan->last_received = curtime;\n\t\tif (sequence == chan->incoming_reliable_sequence)\n\t\t{\n\t\t\tchan->incoming_reliable_sequence++;\n\n\t\t\tif (chan->in_fragment_length + msg->cursize-8 >= sizeof(chan->in_fragment_buf))\n\t\t\t{\n\t\t\t\tchan->drop = true;\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tmemcpy(chan->in_fragment_buf + chan->in_fragment_length, (char*)msg->data+8, msg->cursize-8);\n\t\t\tchan->in_fragment_length += msg->cursize-8;\n\n\t\t\tif (header & NETFLAG_EOM)\n\t\t\t{\n\t\t\t\tmsg->cursize = 0;\n\t\t\t\tWriteData(msg, chan->in_fragment_buf, chan->in_fragment_length);\n\t\t\t\tchan->in_fragment_length = 0;\n\t\t\t\tmsg->readpos = 0;\n\t\t\t\treturn 2;\t//we can read it now\n\t\t\t}\n\t\t}\n//\t\telse\n//\t\t\tCon_DPrintf(\"Stale reliable (%i)\\n\", sequence);\n\t\treturn false;\n\t}\n\n\treturn false;\t//not supported.\n}\n\n\n/*\n=================\nNetchan_Process\n\ncalled when the current net_message is from remote_address\nmodifies net_message so that it points to the packet payload\n=================\n*/\nqboolean Netchan_Process (netchan_t *chan, netmsg_t *msg)\n{\n\tunsigned\t\tsequence, sequence_ack;\n\tunsigned\t\treliable_ack, reliable_message;\n\n// get sequence numbers\n\tmsg->readpos = 0;\n\tsequence = ReadLong (msg);\n\tsequence_ack = ReadLong (msg);\n\n\t// read the qport if we are a server\n\tif (!chan->isclient)\n\t\tif (chan->qport != ReadShort (msg))\n\t\t\treturn false;\n\n\treliable_message = sequence >> 31;\n\treliable_ack = sequence_ack >> 31;\n\n\tsequence &= ~(1<<31);\n\tsequence_ack &= ~(1<<31);\n\n/*\tif (showpackets.value)\n\t\tCom_Printf (\"<-- s=%i(%i) a=%i(%i) %i\\n\"\n\t\t\t, sequence\n\t\t\t, reliable_message\n\t\t\t, sequence_ack\n\t\t\t, reliable_ack\n\t\t\t, net_message.cursize);\n\t*/\n\n//\n// discard stale or duplicated packets\n//\n\tif (sequence <= (unsigned)chan->incoming_sequence)\n\t{\n/*\t\tif (showdrop.value)\n\t\t\tCom_Printf (\"%s:Out of order packet %i at %i\\n\"\n\t\t\t\t, NET_AdrToString (chan->remote_address)\n\t\t\t\t,  sequence\n\t\t\t\t, chan->incoming_sequence);\n\t\t*/\n\t\treturn false;\n\t}\n\n//\n// dropped packets don't keep the message from being used\n//\n/*\tchan->dropped = sequence - (chan->incoming_sequence+1);\n\tif (chan->dropped > 0)\n\t{\n\t\tchan->drop_count += 1;\n\n\t\tif (showdrop.value)\n\t\t\tCom_Printf (\"%s:Dropped %i packets at %i\\n\"\n\t\t\t, NET_AdrToString (chan->remote_address)\n\t\t\t, chan->dropped\n\t\t\t, sequence);\n\t}\n*/\n\n\n//\n// if the current outgoing reliable message has been acknowledged\n// clear the buffer to make way for the next\n//\n\tif (reliable_ack == (unsigned)chan->reliable_sequence)\n\t\tchan->reliable_length = 0;\t// it has been received\n\n//\n// if this message contains a reliable message, bump incoming_reliable_sequence\n//\n\tchan->incoming_sequence = sequence;\n\tchan->incoming_acknowledged = sequence_ack;\n\tchan->incoming_reliable_acknowledged = reliable_ack;\n\tif (reliable_message)\n\t\tchan->incoming_reliable_sequence ^= 1;\n\n//\n// the message can now be read from the current message pointer\n// update statistics counters\n//\n//\tchan->frame_latency = chan->frame_latency*OLD_AVG\n//\t\t+ (chan->outgoing_sequence-sequence_ack)*(1.0-OLD_AVG);\n//\tchan->frame_rate = chan->frame_rate*OLD_AVG\n//\t\t+ (curtime - chan->last_received)*(1.0-OLD_AVG);\n//\tchan->good_count += 1;\n\n\tchan->last_received = curtime;\n\n\treturn true;\n}\n"
  },
  {
    "path": "fteqtv/nq_api.c",
    "content": "/*\r\nThis file is intended as a set of exports for an NQ-based engine.\r\nThis is supported _purely_ for clients, and will not work for servers.\r\n\r\ndocumentation can be found inside net_qtv.h\r\n*/\r\n\r\n\r\n\r\n#include \"qtv.h\"\r\nint build_number(void);\r\n\r\nstatic cluster_t *cluster;\r\n\r\n//note that a qsockaddr is only 16 bytes.\r\n//this is not enough for ipv6 etc.\r\nstruct qsockaddr\r\n{\r\n\tint ipid;\r\n};\r\nstatic char resolvedadrstring[128];\r\nstatic int lastadrid;\r\n\r\n#ifdef _WIN32\r\n#define EXPORT __declspec(dllexport)\r\n#endif\r\n\r\n#include \"net_qtv.h\"\r\n\r\n\r\nPUBEXPORT void PUBLIC QTV_Command (char *command, char *results, int resultlen)\r\n{\r\n\tif (!cluster)\r\n\t\treturn;\r\n\tRcon_Command(cluster, NULL, command, results, resultlen, true);\r\n}\r\nPUBEXPORT void PUBLIC QTV_RunFrame (void)\r\n{\r\n\tif (!cluster)\r\n\t\treturn;\r\n\tCluster_Run(cluster, false);\r\n}\r\n\r\n\r\nEXPORT int PUBLIC QTV_Init (void)\r\n{\r\n\tif (cluster)\r\n\t\treturn 0;\r\n\r\n\tcluster = malloc(sizeof(*cluster));\r\n\tif (cluster)\r\n\t{\r\n\t\tmemset(cluster, 0, sizeof(*cluster));\r\n\r\n\t\tcluster->qwdsocket[0] = INVALID_SOCKET;\r\n\t\tcluster->qwdsocket[1] = INVALID_SOCKET;\r\n\t\tcluster->tcpsocket[0] = INVALID_SOCKET;\r\n\t\tcluster->tcpsocket[1] = INVALID_SOCKET;\r\n\t\tcluster->anticheattime = 1*1000;\r\n\t\tcluster->tooslowdelay = 100;\r\n\t\tcluster->qwlistenportnum = 0;\r\n\t\tcluster->allownqclients = true;\r\n\t\tstrcpy(cluster->hostname, DEFAULT_HOSTNAME);\r\n\t\tcluster->buildnumber = build_number();\r\n\t\tcluster->maxproxies = -1;\r\n\r\n\t\tstrcpy(cluster->demodir, \"qw/demos/\");\r\n\t\treturn 0;\r\n\t}\r\n\r\n\treturn -1;\r\n}\r\nEXPORT void PUBLIC QTV_Shutdown (void)\r\n{\r\n}\r\nEXPORT void PUBLIC QTV_Listen (qboolean state)\r\n{\r\n}\r\nEXPORT int PUBLIC QTV_OpenSocket (int port)\r\n{\r\n\treturn 0;\r\n}\r\nEXPORT int PUBLIC QTV_CloseSocket (int socket)\r\n{\r\n\t//give it a chance to close any server connections from us disconnecting (should have already send disconnect message, but won't have run the server so not noticed the lack of viewers)\r\n\tCluster_Run(cluster, false);\r\n\treturn 0;\r\n}\r\nEXPORT int PUBLIC QTV_Connect (int socket, struct qsockaddr *addr)\r\n{\r\n\tif (addr->ipid == lastadrid)\r\n\t{\r\n\t\tstrlcpy(cluster->autojoinadr, resolvedadrstring, sizeof(cluster->autojoinadr));\r\n\t\treturn 0;\r\n\t}\r\n\telse\r\n\t{\r\n\t\tcluster->autojoinadr[0] = 0;\r\n\t\treturn -1;\r\n\t}\r\n\treturn 0;\r\n}\r\nEXPORT int PUBLIC QTV_CheckNewConnections (void)\r\n{\r\n\treturn -1;\r\n}\r\n\r\nstatic byte pendingbuf[8][1032];\r\nstatic int pendinglen[8];\r\nstatic unsigned int pendingin, pendingout;\r\nvoid QTV_DoReceive(void *data, int length)\r\n{\r\n\tint idx;\r\n\tif (length > sizeof(pendingbuf[0]))\r\n\t\treturn;\r\n\tidx = pendingout++;\r\n\tidx &= 7;\r\n\tmemcpy(pendingbuf[idx], data, length);\r\n\tpendinglen[idx] = length;\r\n}\r\nEXPORT int PUBLIC QTV_Read (int socket, byte *buf, int len, struct qsockaddr *addr)\r\n{\r\n\tif (pendingout == pendingin)\r\n\t{\r\n\t\tCluster_Run(cluster, false);\r\n\t\tCluster_Run(cluster, false);\r\n\t}\r\n\r\n\twhile (pendingin != pendingout)\r\n\t{\r\n\t\tint idx = pendingin++;\r\n\t\tidx &= 7;\r\n\t\tif (pendinglen[idx] > len)\r\n\t\t\tcontinue;\t//error\r\n\t\tmemcpy(buf, pendingbuf[idx], pendinglen[idx]);\r\n\t\treturn pendinglen[idx];\r\n\t}\r\n\treturn 0;\r\n}\r\nEXPORT int PUBLIC QTV_Write (int socket, byte *buf, int len, struct qsockaddr *addr)\r\n{\r\n\tnetmsg_t m;\r\n\tnetadr_t from;\r\n\tfrom.tcpcon = NULL;\r\n\t((struct sockaddr*)from.sockaddr)->sa_family = AF_UNSPEC;\r\n\r\n\tm.cursize = len;\r\n\tm.data = buf;\r\n\tm.readpos = 0;\r\n\r\n\tQW_ProcessUDPPacket(cluster, &m, from);\r\n\r\n\tif (pendingout == pendingin)\r\n\t\tCluster_Run(cluster, false);\r\n\r\n\treturn 0;\r\n}\r\nEXPORT int PUBLIC QTV_Broadcast (int socket, byte *buf, int len)\r\n{\r\n\tnetmsg_t m;\r\n\tnetadr_t from;\r\n\tfrom.tcpcon = NULL;\r\n\t((struct sockaddr*)from.sockaddr)->sa_family = AF_UNSPEC;\r\n\r\n\tm.cursize = len;\r\n\tm.data = buf;\r\n\tm.readpos = 0;\r\n\r\n\tQW_ProcessUDPPacket(cluster, &m, from);\r\n\r\n\treturn 0;\r\n}\r\nEXPORT char *PUBLIC QTV_AddrToString (struct qsockaddr *addr)\r\n{\r\n\treturn 0;\r\n}\r\nEXPORT int PUBLIC QTV_StringPortToAddr (char *string, int port, struct qsockaddr *addr)\r\n{\r\n\tif (!strncmp(string, \"file:\", 5) || !strncmp(string, \"demo:\", 5))\r\n\t{\r\n\t\tsnprintf(resolvedadrstring, sizeof(resolvedadrstring), \"%s\", string);\r\n\t\taddr->ipid = ++lastadrid;\r\n\t\treturn 0;\r\n\t}\r\n\tif (!strncmp(string, \"qw:\", 3) || !strncmp(string, \"udp:\", 4) || !strncmp(string, \"tcp:\", 4))\r\n\t{\r\n\t\tif (port)\r\n\t\t\tsnprintf(resolvedadrstring, sizeof(resolvedadrstring), \"%s:%i\", string, port);\r\n\t\telse\r\n\t\t\tsnprintf(resolvedadrstring, sizeof(resolvedadrstring), \"%s\", string);\r\n\t\taddr->ipid = ++lastadrid;\r\n\t\treturn 0;\r\n\t}\r\n\treturn -1;\r\n}\r\nEXPORT int PUBLIC QTV_StringToAddr (char *string, struct qsockaddr *addr)\r\n{\r\n\treturn QTV_StringPortToAddr(string, 0, addr);\r\n}\r\nEXPORT int PUBLIC QTV_GetSocketAddr (int socket, struct qsockaddr *addr)\r\n{\r\n\treturn 0;\r\n}\r\nEXPORT int PUBLIC QTV_GetNameFromAddr (struct qsockaddr *addr, char *name)\r\n{\r\n\treturn 0;\r\n}\r\nEXPORT int PUBLIC QTV_GetAddrFromName (char *name, struct qsockaddr *addr)\r\n{\r\n\treturn QTV_StringToAddr(name, addr);\r\n}\r\nEXPORT int PUBLIC QTV_AddrCompare (struct qsockaddr *addr1, struct qsockaddr *addr2)\r\n{\r\n\treturn 0;\r\n}\r\nEXPORT int PUBLIC QTV_GetSocketPort (struct qsockaddr *addr)\r\n{\r\n\treturn 0;\r\n}\r\nEXPORT int PUBLIC QTV_SetSocketPort (struct qsockaddr *addr, int port)\r\n{\r\n\treturn 0;\r\n}\r\n"
  },
  {
    "path": "fteqtv/parse.c",
    "content": "/*\nCopyright (C) 1996-1997 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\nSee the included (GNU.txt) GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n*/\n\n\n#include \"qtv.h\"\n\n#include \"bsd_string.h\"\n\n#define ParseError(m) (m)->readpos = (m)->cursize+1\t//\n\nstatic const entity_state_t null_entity_state;\n\nvoid SendBufferToViewer(viewer_t *v, const char *buffer, int length, qboolean reliable)\n{\n\tif (reliable)\n\t{\n\t\t//try and put it in the normal reliable\n\t\tif (!v->backbuffered && v->netchan.message.cursize+length < v->netchan.message.maxsize)\n\t\t\tWriteData(&v->netchan.message, buffer, length);\n\t\telse if (v->backbuffered>0 && v->backbuf[v->backbuffered-1].cursize+length < v->backbuf[v->backbuffered-1].maxsize)\t//try and put it in the current backbuffer\n\t\t\tWriteData(&v->backbuf[v->backbuffered-1], buffer, length);\n\t\telse if (v->backbuffered == MAX_BACK_BUFFERS)\n\t\t{\n\t\t\tv->netchan.message.cursize = 0;\n\t\t\tWriteByte(&v->netchan.message, svc_print);\n\t\t\tif (!v->netchan.isnqprotocol)\n\t\t\t\tWriteByte(&v->netchan.message, PRINT_HIGH);\n\t\t\tWriteString(&v->netchan.message, \"backbuffer overflow\\n\");\n\t\t\tif (!v->drop)\n\t\t\t\tSys_Printf(NULL, \"%s backbuffers overflowed\\n\", v->name);\t//FIXME\n\t\t\tv->drop = true;\t//we would need too many backbuffers.\n\t\t}\n\t\telse\n\t\t{\n\n\t\t\t//create a new backbuffer\n\t\t\tif (!v->backbuf[v->backbuffered].data)\n\t\t\t{\n\t\t\t\tInitNetMsg(&v->backbuf[v->backbuffered], (unsigned char *)malloc(MAX_BACKBUF_SIZE), MAX_BACKBUF_SIZE);\n\t\t\t}\n\t\t\tv->backbuf[v->backbuffered].cursize = 0;\t//make sure it's empty\n\t\t\tWriteData(&v->backbuf[v->backbuffered], buffer, length);\n\t\t\tv->backbuffered++;\n\t\t}\n\t}\n}\n\nvoid Multicast(sv_t *tv, void *buffer, int length, int to, unsigned int playermask, int suitablefor)\n{\n\tviewer_t *v;\n\tswitch(to)\n\t{\n\tcase dem_multiple:\n\tcase dem_single:\n\tcase dem_stats:\n\t\t//check and send to them only if they're tracking this player(s).\n\t\tfor (v = tv->cluster->viewers; v; v = v->next)\n\t\t{\n\t\t\tif (v->thinksitsconnected||suitablefor&CONNECTING)\n\t\t\t\tif (v->server == tv)\n\t\t\t\t\tif (v->trackplayer>=0)\n\t\t\t\t\t\tif ((1<<v->trackplayer)&playermask)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (suitablefor&(v->netchan.isnqprotocol?NQ:QW))\n\t\t\t\t\t\t\t\tSendBufferToViewer(v, buffer, length, true);\t//FIXME: change the reliable depending on message type\n\t\t\t\t\t\t}\n\t\t}\n\t\tbreak;\n\tdefault:\n\t\t//send to all\n\t\tfor (v = tv->cluster->viewers; v; v = v->next)\n\t\t{\n\t\t\tif (v->thinksitsconnected||suitablefor&CONNECTING)\n\t\t\t\tif (v->server == tv)\n\t\t\t\t\tif (suitablefor&(v->netchan.isnqprotocol?NQ:QW))\n\t\t\t\t\t\tSendBufferToViewer(v, buffer, length, true);\t//FIXME: change the reliable depending on message type\n\t\t}\n\t\tbreak;\n\t}\n}\nvoid Broadcast(cluster_t *cluster, void *buffer, int length, int suitablefor)\n{\n\tviewer_t *v;\n\tfor (v = cluster->viewers; v; v = v->next)\n\t{\n\t\tif (suitablefor&(v->netchan.isnqprotocol?NQ:QW))\n\t\t\tSendBufferToViewer(v, buffer, length, true);\n\t}\n}\n\nvoid ConnectionData(sv_t *tv, void *buffer, int length, int to, unsigned int playermask, int suitablefor)\n{\n\tif (!tv->parsingconnectiondata)\n\t\tMulticast(tv, buffer, length, to, playermask, suitablefor);\n\telse if (tv->controller)\n\t{\n\t\tif (suitablefor&(tv->controller->netchan.isnqprotocol?NQ:QW))\n\t\t\tSendBufferToViewer(tv->controller, buffer, length, true);\n\t}\n}\n\nstatic void ParseServerData(sv_t *tv, netmsg_t *m, int to, unsigned int playermask)\n{\n\tunsigned int protocol;\n\tunsigned int supported;\n\tviewer_t *v;\n\n\t//free the old map state\n\tQTV_CleanupMap(tv);\n\n\ttv->pext1 = 0;\n\ttv->pext2 = 0;\n\ttv->pexte = 0;\n\n\t//when it comes to QTV, the proxy 'blindly' forwards the data after parsing the header, so we need to support EVERYTHING the original server might.\n\t//and if we don't, then we might have troubles.\n\tfor(;;)\n\t{\n\t\tprotocol = ReadLong(m);\n\t\tswitch (protocol)\n\t\t{\n\t\tcase PROTOCOL_VERSION:\n\t\t\tbreak;\n\t\tcase PROTOCOL_VERSION_FTE:\n\t\t\tprotocol = ReadLong(m);\n\t\t\ttv->pext1 = protocol;\n\n\t\t\t//HAVE\n\t\t\tsupported = PEXT_SETVIEW|PEXT_ACCURATETIMINGS; /*simple forwarding*/\n\t\t\tsupported |= PEXT_256PACKETENTITIES|PEXT_VIEW2|PEXT_HLBSP|PEXT_Q2BSP|PEXT_Q3BSP;\t//features other than the protocol (stats, simple limits etc)\n\n\t\t\tsupported |= PEXT_FLOATCOORDS|PEXT_SPAWNSTATIC2;\t//working\n//\t\t\tsupported |= PEXT_CHUNKEDDOWNLOADS;\t\t\t\t\t//shouldn't be relevant...\n\t\t\tsupported |= PEXT_TRANS|PEXT_MODELDBL|PEXT_ENTITYDBL|PEXT_ENTITYDBL2|PEXT_SOUNDDBL;\n\n\t\t\t//replaced by replacementdeltas.\n\t\t\tsupported |= PEXT_SCALE|PEXT_TRANS|PEXT_FATNESS|PEXT_COLOURMOD|PEXT_HEXEN2|PEXT_SETATTACHMENT|PEXT_DPFLAGS;\n\n\t\t\t//stuff that we ought to handle, but don't currently\n\t\t\t//PEXT_LIGHTSTYLECOL\t- woo, fancy rgb colours\n\t\t\t//PEXT_CUSTOMTEMPEFFECTS - required for hexen2's effects. kinda messy.\n\t\t\t//PEXT_TE_BULLET\t\t- implies nq tents too.\n\n\t\t\t//HARD...\n\t\t\t//PEXT_CSQC -- all bets are off if we receive a csqc ent update\n\n\t\t\t//totally optional... so will probably never be added...\n\t\t\t//PEXT_HULLSIZE\t\t\t- bigger players... maybe. like anyone can depend on this... not supported with mvd players so w/e\n\t\t\t//PEXT_CHUNKEDDOWNLOADS\t- not sure there's much point\n\t\t\t//PEXT_SPLITSCREEN\t\t- irrelevant for mvds. might be useful as a qw client, but who cares. not enough servers have it active.\n\t\t\t//PEXT_SHOWPIC\t\t\t- rare, lame, limited. just yuck.\n\n\t\t\tif (protocol & ~supported)\n\t\t\t{\n\t\t\t\tint i;\n\t\t\t\tconst char *names[] = {\n\t\t\t\t\t\"PEXT_SETVIEW\",\t\t\t\t\"PEXT_SCALE\",\t\t\t\"PEXT_LIGHTSTYLECOL\",\t\"PEXT_TRANS\",\n\t\t\t\t\t\"PEXT_VIEW2\",\t\t\t\t\"0x00000020\",\t\t\t\"PEXT_ACCURATETIMINGS\", \"PEXT_SOUNDDBL\",\n\t\t\t\t\t\"PEXT_FATNESS\",\t\t\t\t\"PEXT_HLBSP\",\t\t\t\"PEXT_TE_BULLET\",\t\t\"PEXT_HULLSIZE\",\n\t\t\t\t\t\"PEXT_MODELDBL\",\t\t\t\"PEXT_ENTITYDBL\",\t\t\"PEXT_ENTITYDBL2\",\t\t\"PEXT_FLOATCOORDS\",\n\t\t\t\t\t\"0x00010000\",\t\t\t\t\"PEXT_Q2BSP\",\t\t\t\"PEXT_Q3BSP\",\t\t\t\"PEXT_COLOURMOD\",\n\t\t\t\t\t\"PEXT_SPLITSCREEN\",\t\t\t\"PEXT_HEXEN2\",\t\t\t\"PEXT_SPAWNSTATIC2\",\t\"PEXT_CUSTOMTEMPEFFECTS\",\n\t\t\t\t\t\"PEXT_256PACKETENTITIES\",\t\"0x02000000\",\t\t\t\"PEXT_SHOWPIC\",\t\t\t\"PEXT_SETATTACHMENT\",\n\t\t\t\t\t\"0x10000000\",\t\t\t\t\"PEXT_CHUNKEDDOWNLOADS\",\"PEXT_CSQC\",\t\t\t\"PEXT_DPFLAGS\",\n\t\t\t\t};\n\t\t\t\tfor (i = 0; i < sizeof(names)/sizeof(names[0]); i++)\n\t\t\t\t{\n\t\t\t\t\tif (protocol & ~supported & (1u<<i))\n\t\t\t\t\t{\n\t\t\t\t\t\tSys_Printf(tv->cluster, \"ParseMessage: PROTOCOL_VERSION_FTE (%s) not supported\\n\", names[i]);\n\t\t\t\t\t\tsupported |= (1u<<i);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (protocol & ~supported)\n\t\t\t\t\tSys_Printf(tv->cluster, \"ParseMessage: PROTOCOL_VERSION_FTE (%x) not supported\\n\", protocol & ~supported);\n\t\t\t}\n\t\t\tcontinue;\n\t\tcase PROTOCOL_VERSION_FTE2:\n\t\t\tprotocol = ReadLong(m);\n\t\t\ttv->pext2 = protocol;\n\t\t\tsupported = 0;\n//\t\t\tsupported |= PEXT2_PRYDONCURSOR|PEXT2_VOICECHAT|PEXT2_SETANGLEDELTA|PEXT2_REPLACEMENTDELTAS|PEXT2_MAXPLAYERS;\n\n\t\t\t//FIXME: handle the svc and clc if they arrive.\n\t\t\tsupported |= PEXT2_VOICECHAT;\n\n\t\t\t//WANT\n\t\t\t//PEXT2_SETANGLEDELTA\n\t\t\t//PEXT2_REPLACEMENTDELTAS\n\t\t\t//PEXT2_SETANGLEDELTA\n\t\t\t//PEXT2_PREDINFO\n\t\t\t//PEXT2_PRYDONCURSOR\n\n\t\t\tif (protocol & ~supported)\n\t\t\t\tSys_Printf(tv->cluster, \"ParseMessage: PROTOCOL_VERSION_FTE2 (%x) not supported\\n\", protocol & ~supported);\n\t\t\tcontinue;\n\t\tcase PROTOCOL_VERSION_EZQUAKE1:\n\t\t\ttv->pexte = protocol = ReadLong(m);\n\t\t\tsupported = PEXTE_HIDDENMESSAGES;\n\t\t\tif (protocol & ~supported)\n\t\t\t\tSys_Printf(tv->cluster, \"ParseMessage: Unsupported MVD1 protocol flags %#x\\n\", protocol);\n\t\t\tcontinue;\n\t\tcase PROTOCOL_VERSION_HUFFMAN:\n\t\t\tSys_Printf(tv->cluster, \"ParseMessage: PROTOCOL_VERSION_HUFFMAN not supported\\n\");\n\t\t\tParseError(m);\n\t\t\treturn;\n\t\tcase PROTOCOL_VERSION_VARLENGTH:\n\t\t\t{\n\t\t\t\tint len = ReadLong(m);\n\t\t\t\tif (len < 0 || len > 8192)\n\t\t\t\t{\n\t\t\t\t\tSys_Printf(tv->cluster, \"ParseMessage: PROTOCOL_VERSION_VARLENGTH invalid\\n\");\n\t\t\t\t\tParseError(m);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tprotocol = ReadLong(m);/*ident*/\n\t\t\t\tswitch(protocol)\n\t\t\t\t{\n\t\t\t\tdefault:\n\t\t\t\t\tm->readpos += len;\n\n\t\t\t\t\tSys_Printf(tv->cluster, \"ParseMessage: PROTOCOL_VERSION_VARLENGTH (%x) not supported\\n\", protocol);\n\t\t\t\t\tParseError(m);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t\tcontinue;\n\t\tcase PROTOCOL_VERSION_FRAGMENT:\n\t\t\tprotocol = ReadLong(m);\n\t\t\tSys_Printf(tv->cluster, \"ParseMessage: PROTOCOL_VERSION_FRAGMENT not supported\\n\");\n\t\t\tParseError(m);\n\t\t\treturn;\n\t\tdefault:\n\t\t\tSys_Printf(tv->cluster, \"ParseMessage: Unknown protocol version %#x\\n\", protocol);\n\t\t\tParseError(m);\n\t\t\treturn;\n\t\t}\n\t\tbreak;\n\t}\n\n\ttv->mapstarttime = tv->parsetime;\n\ttv->parsingconnectiondata = true;\n\n\ttv->clservercount = ReadLong(m);\t//we don't care about server's servercount, it's all reliable data anyway.\n\n\ttv->map.trackplayer = -1;\n\n\tReadString(m, tv->map.gamedir, sizeof(tv->map.gamedir));\n#define DEFAULTGAMEDIR \"qw\"\n\tif (strchr(tv->map.gamedir, ':'))\t//nuke any multiple gamedirs - we need to read maps which would fail if its not a valid single path.\n\t\t*strchr(tv->map.gamedir, ';') = 0;\n\tif (!*tv->map.gamedir)\n\t\tstrcpy(tv->map.gamedir, DEFAULTGAMEDIR);\n\tif (!*tv->map.gamedir\n\t\t|| *tv->map.gamedir == '.'\n\t\t|| !strcmp(tv->map.gamedir, \".\")\n\t\t|| strstr(tv->map.gamedir, \"..\")\n\t\t|| strstr(tv->map.gamedir, \"/\")\n\t\t|| strstr(tv->map.gamedir, \"\\\\\")\n\t\t|| strstr(tv->map.gamedir, \":\")\n\t\t)\n\t{\n\t\tQTV_Printf(tv, \"Ignoring unsafe gamedir: \\\"%s\\\"\\n\", tv->map.gamedir);\n\t\tstrcpy(tv->map.gamedir, DEFAULTGAMEDIR);\n\t}\n\n\tif (tv->usequakeworldprotocols)\n\t\ttv->map.thisplayer = ReadByte(m)&~128;\n\telse\n\t{\n\t\ttv->map.thisplayer = MAX_CLIENTS-1;\n\t\t/*tv->servertime =*/ ReadFloat(m);\n\t}\n\tif (tv->controller)\n\t\ttv->controller->thisplayer = tv->map.thisplayer;\n\tReadString(m, tv->map.mapname, sizeof(tv->map.mapname));\n\n\tQTV_Printf(tv, \"Gamedir: %s\\n\", tv->map.gamedir);\n\tQTV_Printf(tv, \"---------------------\\n\");\n\tSys_Printf(tv->cluster, \"Stream %i: %s\\n\", tv->streamid, tv->map.mapname);\n\tQTV_Printf(tv, \"---------------------\\n\");\n\n\t// get the movevars\n\ttv->map.movevars.gravity\t\t\t= ReadFloat(m);\n\ttv->map.movevars.stopspeed\t\t\t= ReadFloat(m);\n\ttv->map.movevars.maxspeed\t\t\t= ReadFloat(m);\n\ttv->map.movevars.spectatormaxspeed\t= ReadFloat(m);\n\ttv->map.movevars.accelerate\t\t\t= ReadFloat(m);\n\ttv->map.movevars.airaccelerate\t\t= ReadFloat(m);\n\ttv->map.movevars.wateraccelerate\t= ReadFloat(m);\n\ttv->map.movevars.friction\t\t\t= ReadFloat(m);\n\ttv->map.movevars.waterfriction\t\t= ReadFloat(m);\n\ttv->map.movevars.entgrav\t\t\t= ReadFloat(m);\n\n\tfor (v = tv->cluster->viewers; v; v = v->next)\n\t{\n\t\tif (v->server == tv)\n\t\t\tv->thinksitsconnected = false;\n\t}\n\n\tif ((!tv->controller || tv->controller->netchan.isnqprotocol) && tv->usequakeworldprotocols)\n\t{\n\t\ttv->netchan.message.cursize = 0;\t//mvdsv sucks\n\t\tSendClientCommand(tv, \"soundlist %i 0\\n\", tv->clservercount);\n\t}\n\telse\n\t\tConnectionData(tv, (void*)((char*)m->data+m->startpos), m->readpos - m->startpos, to, dem_read, QW);\n\n\tif (tv->controller)\n\t{\n\t\tQW_ClearViewerState(tv->controller);\n\t\ttv->controller->trackplayer = tv->map.thisplayer;\n\t}\n\n\tstrcpy(tv->status, \"Receiving soundlist\\n\");\n}\n\n/*called if the server changed the map.serverinfo, so we can corrupt it again*/\nvoid QTV_UpdatedServerInfo(sv_t *tv)\n{\n\tqboolean fromproxy;\n\tchar text[1024];\n\tchar value[256];\n\n\tInfo_ValueForKey(tv->map.serverinfo, \"*qtv\", value, sizeof(value));\n\tif (*value)\n\t{\n\t\tfromproxy = true;\n\t\ttv->serverisproxy = fromproxy;\n\t}\n\telse\n\t\tfromproxy = false;\n\n\t//add on our extra infos\n\tInfo_SetValueForStarKey(tv->map.serverinfo, \"*qtv\", QTV_VERSION_STRING, sizeof(tv->map.serverinfo));\n\tInfo_SetValueForStarKey(tv->map.serverinfo, \"*z_ext\", Z_EXT_STRING, sizeof(tv->map.serverinfo));\n\n\tInfo_ValueForKey(tv->map.serverinfo, \"hostname\", tv->map.hostname, sizeof(tv->map.hostname));\n\n\t//change the hostname (the qtv's hostname with the server's hostname in brackets)\n\tInfo_ValueForKey(tv->map.serverinfo, \"hostname\", value, sizeof(value));\n\tif (fromproxy && strchr(value, '(') && value[strlen(value)-1] == ')')\t//already has brackets\n\t{\t//the fromproxy check is because it's fairly common to find a qw server with brackets after it's name.\n\t\tchar *s;\n\t\ts = strchr(value, '(');\t//so strip the parent proxy's hostname, and put our hostname first, leaving the origional server's hostname within the brackets\n\t\tsnprintf(text, sizeof(text), \"%s %s\", tv->cluster->hostname, s);\n\t}\n\telse\n\t{\n\t\tif (tv->sourcefile)\n\t\t\tsnprintf(text, sizeof(text), \"%s (recorded from: %s)\", tv->cluster->hostname, value);\n\t\telse\n\t\t\tsnprintf(text, sizeof(text), \"%s (live: %s)\", tv->cluster->hostname, value);\n\t}\n\tInfo_SetValueForStarKey(tv->map.serverinfo, \"hostname\", text, sizeof(tv->map.serverinfo));\n}\n\nstatic void ParseCDTrack(sv_t *tv, netmsg_t *m, int to, unsigned int mask)\n{\n\tchar nqversion[3];\n\ttv->map.cdtrack = ReadByte(m);\n\n\tConnectionData(tv, (void*)((char*)m->data+m->startpos), m->readpos - m->startpos, to, mask, QW);\n\n\tnqversion[0] = svc_cdtrack;\n\tnqversion[1] = tv->map.cdtrack;\n\tnqversion[2] = tv->map.cdtrack;\n\tConnectionData(tv, nqversion, 3, to, mask, NQ);\n}\nstatic void ParseStufftext(sv_t *tv, netmsg_t *m, int to, unsigned int mask)\n{\n\tviewer_t *v;\n\tchar text[1024];\n\n\tReadString(m, text, sizeof(text));\n//\tSys_Printf(tv->cluster, \"stuffcmd: %s\", text);\n\tif (!strcmp(text, \"say proxy:menu\\n\"))\n\t{\t//qizmo's 'previous proxy' message\n\t\ttv->proxyisselected = true;\n\t\tif (tv->controller)\n\t\t\tQW_SetMenu(tv->controller, MENU_MAIN);\n\t\ttv->serverisproxy = true;\t//FIXME: Detect this properly on qizmo\n\t}\n\telse if (!strncmp(text, \"//I am a proxy\", 14))\n\t\ttv->serverisproxy = true;\n\telse if (!strncmp(text, \"//set prox_inmenu \", 18))\n\t{\n\t\tif (tv->controller)\n\t\t\tQW_SetMenu(tv->controller, atoi(text+18)?MENU_FORWARDING:MENU_NONE);\n\t}\n//\telse if (!strncmp(text, \"//set protocolname \", 19))\n//\telse if (!strncmp(text, \"//set recorddate \", 17))\t//reports when the demo was originally recorded, without needing to depend upon metadata.\n//\telse if (!strncmp(text, \"//paknames \", 11))\n//\telse if (!strncmp(text, \"//paks \", 7))\n//\telse if (!strncmp(text, \"//vwep \", 7))\n\telse if (strstr(text, \"screenshot\"))\n\t{\n\t\tif (tv->controller)\n\t\t{\t//let it through to the controller\n\t\t\tSendBufferToViewer(tv->controller, (char*)m->data+m->startpos, m->readpos - m->startpos, true);\n\t\t}\n\t\treturn;\t//this was generating far too many screenshots when watching demos\n\t}\n\telse if (!strcmp(text, \"skins\\n\"))\n\t{\n\t\tconst char newcmd[10] = {svc_stufftext, 'c', 'm', 'd', ' ', 'n','e','w','\\n','\\0'};\n\t\ttv->parsingconnectiondata = false;\n\n\t\tstrcpy(tv->status, \"On server\\n\");\n\n\t\tfor (v = tv->cluster->viewers; v; v = v->next)\n\t\t{\n\t\t\tif (v->server == tv && (v != tv->controller || v->netchan.isnqprotocol))\n\t\t\t{\n\t\t\t\tv->servercount++;\n\t\t\t\tSendBufferToViewer(v, newcmd, sizeof(newcmd), true);\n\t\t\t}\n\t\t}\n\n\t\tif (tv->controller && !tv->controller->netchan.isnqprotocol)\n\t\t\tSendBufferToViewer(tv->controller, (char*)m->data+m->startpos, m->readpos - m->startpos, true);\n\t\telse if (tv->usequakeworldprotocols)\n\t\t\tSendClientCommand(tv, \"begin %i\\n\", tv->clservercount);\n\t\treturn;\n\t}\n\telse if (!strncmp(text, \"fullserverinfo \", 15))\n\t{\n\t\t/*strip newline*/\n\t\ttext[strlen(text)-1] = '\\0';\n\t\t/*strip trailing quote*/\n\t\ttext[strlen(text)-1] = '\\0';\n\n\t\t//copy over the server's serverinfo\n\t\tstrlcpy(tv->map.serverinfo, text+16, sizeof(tv->map.serverinfo));\n\n\t\tQTV_UpdatedServerInfo(tv);\n\n\t\tif (tv->controller && (tv->controller->netchan.isnqprotocol == false))\n\t\t\tSendBufferToViewer(tv->controller, (char*)m->data+m->startpos, m->readpos - m->startpos, true);\n\t\treturn;\n\t}\n\telse if (!strncmp(text, \"cmd prespawn \", 13))\n\t{\n\t\tif (tv->usequakeworldprotocols)\n\t\t\tSendClientCommand(tv, \"%s\", text+4);\n\t\treturn;\t//commands the game server asked for are pointless.\n\t}\n\telse if (!strncmp(text, \"cmd spawn \", 10))\n\t{\n\t\tif (tv->usequakeworldprotocols)\n\t\t\tSendClientCommand(tv, \"%s\", text+4);\n\n\t\treturn;\t//commands the game server asked for are pointless.\n\t}\n\telse if (!strncmp(text, \"cmd \", 4))\n\t{\n\t\tif (tv->controller)\n\t\t\tSendBufferToViewer(tv->controller, (char*)m->data+m->startpos, m->readpos - m->startpos, true);\n\t\telse if (tv->usequakeworldprotocols)\n\t\t\tSendClientCommand(tv, \"%s\", text+4);\n\t\treturn;\t//commands the game server asked for are pointless.\n\t}\n\telse if (!strncmp(text, \"reconnect\", 9))\n\t{\n\t\tif (tv->controller)\n\t\t\tSendBufferToViewer(tv->controller, (char*)m->data+m->startpos, m->readpos - m->startpos, true);\n\t\telse if (tv->usequakeworldprotocols)\n\t\t\tSendClientCommand(tv, \"new\\n\");\n\t\treturn;\n\t}\n\telse if (!strncmp(text, \"packet \", 7))\n\t{\n\t\tif (tv->controller)\n\t\t{\t//if we're acting as a proxy, forward the realip packets, and ONLY to the controller\n\t\t\t//quakeworld proxies are usually there for routing or protocol advantages, NOT privacy\n\t\t\t//(client can always ignore it themselves, but a server might ban you, but at least they'll be less inclined to ban the proxy).\n\t\t\tSendBufferToViewer(tv->controller, (char*)m->data+m->startpos, m->readpos - m->startpos, true);\n\t\t\treturn;\n\t\t}\n\t\tif(tv->usequakeworldprotocols)\n\t\t{//eeeevil hack for proxy-spectating\n\t\t\tchar *ptr;\n\t\t\tchar arg[3][ARG_LEN];\n\t\t\tnetadr_t adr;\n\t\t\tptr = text;\n\t\t\tptr = COM_ParseToken(ptr, arg[0], ARG_LEN, \"\");\n\t\t\tptr = COM_ParseToken(ptr, arg[1], ARG_LEN, \"\");\n\t\t\tptr = COM_ParseToken(ptr, arg[2], ARG_LEN, \"\");\n\t\t\tNET_StringToAddr(arg[1], &adr, PROX_DEFAULTSERVERPORT);\n\t\t\tNetchan_OutOfBandSocket(tv->cluster, tv->sourcesock, &adr, strlen(arg[2]), arg[2]);\n\n\t\t\t//this is an evil hack\n\t\t\tSendClientCommand(tv, \"new\\n\");\n\t\t\treturn;\n\t\t}\n\t\tSys_Printf(tv->cluster, \"packet stuffcmd in an mvd\\n\");\t//shouldn't ever happen, try ignoring it.\n\t\treturn;\n\t}\n\telse if (tv->usequakeworldprotocols && !strncmp(text, \"setinfo \", 8))\n\t{\n\t\tMulticast(tv, (char*)m->data+m->startpos, m->readpos - m->startpos, to, mask, Q1);\n\t\tif (!tv->controller)\n\t\t\tSendClientCommand(tv, \"%s\", text);\n\t}\n\telse\n\t{\n\t\tMulticast(tv, (char*)m->data+m->startpos, m->readpos - m->startpos, to, mask, Q1);\n\t\treturn;\n\t}\n}\n\nstatic void ParseSetInfo(sv_t *tv, netmsg_t *m)\n{\n\tint pnum;\n\tchar key[64];\n\tchar value[256];\n\tpnum = ReadByte(m);\n\tReadString(m, key, sizeof(key));\n\tReadString(m, value, sizeof(value));\n\n\tif (pnum < MAX_CLIENTS)\n\t\tInfo_SetValueForStarKey(tv->map.players[pnum].userinfo, key, value, sizeof(tv->map.players[pnum].userinfo));\n\n\tConnectionData(tv, (char*)m->data+m->startpos, m->readpos - m->startpos, dem_all, (unsigned)-1, QW);\n}\n\nstatic void ParseServerinfo(sv_t *tv, netmsg_t *m)\n{\n\tchar key[64];\n\tchar value[256];\n\tReadString(m, key, sizeof(key));\n\tReadString(m, value, sizeof(value));\n\n\tif (strcmp(key, \"hostname\"))\t//don't allow the hostname to change, but allow the server to change other serverinfos.\n\t\tInfo_SetValueForStarKey(tv->map.serverinfo, key, value, sizeof(tv->map.serverinfo));\n\n\tConnectionData(tv, (char*)m->data+m->startpos, m->readpos - m->startpos, dem_all, (unsigned)-1, QW);\n}\n\nstatic void ParsePrint(sv_t *tv, netmsg_t *m, int to, unsigned int mask)\n{\n\tchar text[1024];\n\tchar nqbuffer[1024];\n\tint level;\n\n\tlevel = ReadByte(m);\n\tReadString(m, text, sizeof(text)-2);\n\n\tif (level == 3)\n\t{\n\t\t//FIXME: that number shouldn't be hard-coded\n\t\tif (!strncmp(text, \"#0:qtv_say:#\", 12) || !strncmp(text, \"#0:qtv_say_game:#\", 17) || !strncmp(text, \"#0:qtv_say_team_game:#\", 22))\n\t\t{\n\t\t\tchar *colon;\n\t\t\tcolon = strchr(text, ':');\n\t\t\tcolon = strchr(colon+1, ':');\n\t\t\tcolon = strchr(colon+1, ':');\n\t\t\tif (colon)\n\t\t\t{\n\t\t\t\t//de-fuck qqshka's extra gibberish.\n\t\t\t\tsnprintf(nqbuffer, sizeof(nqbuffer), \"%c%c[QTV]%s\\n\", svc_print, 3, colon+1);\n\t\t\t\tMulticast(tv, nqbuffer, strlen(nqbuffer)+1, to, mask, QW|CONNECTING);\n\t\t\t\tsnprintf(nqbuffer, sizeof(nqbuffer), \"%c%c[QTV]%s\\n\", svc_print, 1, colon+1);\n\t\t\t\tMulticast(tv, nqbuffer, strlen(nqbuffer)+1, to, mask, NQ|CONNECTING);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tstrlcpy(nqbuffer+2, text, sizeof(nqbuffer)-2);\n\t\tnqbuffer[1] = 1;\t//nq chat is prefixed with a 1\n\t}\n\telse\n\t{\n\t\tstrlcpy(nqbuffer+1, text, sizeof(nqbuffer)-1);\n\t}\n\tnqbuffer[0] = svc_print;\n\n\tif ((to&dem_mask) == dem_all || to == dem_read)\n\t{\n\t\tif (level > 1)\n\t\t{\n\t\t\tQTV_Printf(tv, \"%s\", text);\n\t\t}\n\t}\n\n\tMulticast(tv, (char*)m->data+m->startpos, m->readpos - m->startpos, to, mask, QW|CONNECTING);\n\tMulticast(tv, nqbuffer, strlen(nqbuffer)+1, to, mask, NQ|CONNECTING);\n}\nstatic void ParseCenterprint(sv_t *tv, netmsg_t *m, int to, unsigned int mask)\n{\n\tviewer_t *v;\n\tchar text[1024];\n\tReadString(m, text, sizeof(text));\n\n\n\n\n\tswitch(to)\n\t{\n\tcase dem_multiple:\n\tcase dem_single:\n\tcase dem_stats:\n\t\t//check and send to them only if they're tracking this player(s).\n\t\tfor (v = tv->cluster->viewers; v; v = v->next)\n\t\t{\n\t\t\tif (!v->menunum || v->menunum == MENU_FORWARDING)\n\t\t\tif (v->thinksitsconnected)\n\t\t\t\tif (v->server == tv)\n\t\t\t\t\tif (v->trackplayer>=0)\n\t\t\t\t\t\tif ((1<<v->trackplayer)&mask)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tSendBufferToViewer(v, (char*)m->data+m->startpos, m->readpos - m->startpos, true);\t//FIXME: change the reliable depending on message type\n\t\t\t\t\t\t}\n\t\t}\n\t\tbreak;\n\tdefault:\n\t\t//send to all\n\t\tfor (v = tv->cluster->viewers; v; v = v->next)\n\t\t{\n\t\t\tif (!v->menunum || v->menunum == MENU_FORWARDING)\n\t\t\tif (v->thinksitsconnected)\n\t\t\t\tif (v->server == tv)\n\t\t\t\t\tSendBufferToViewer(v, (char*)m->data+m->startpos, m->readpos - m->startpos, true);\t//FIXME: change the reliable depending on message type\n\t\t}\n\t\tbreak;\n\t}\n}\nstatic int ParseList(sv_t *tv, netmsg_t *m, filename_t *list, int to, unsigned int mask, qboolean big)\n{\n\tint first;\n\n\tif (big)\n\t\tfirst = ReadShort(m)+1;\n\telse\n\t\tfirst = ReadByte(m)+1;\n\tfor (; first < MAX_LIST; first++)\n\t{\n\t\tReadString(m, list[first].name, sizeof(list[first].name));\n//\t\tprintf(\"read %i: %s\\n\", first, list[first].name);\n\t\tif (!*list[first].name)\n\t\t\tbreak;\n//\t\tprintf(\"%i: %s\\n\", first, list[first].name);\n\t}\n\n\treturn ReadByte(m);\n}\n\nstatic void ParseEntityState(sv_t *tv, entity_state_t *es, netmsg_t *m)\t//for baselines/static entities\n{\n\tint i;\n\n\tes->modelindex = ReadByte(m);\n\tes->frame = ReadByte(m);\n\tes->colormap = ReadByte(m);\n\tes->skinnum = ReadByte(m);\n\tfor (i = 0; i < 3; i++)\n\t{\n\t\tes->origin[i] = ReadCoord(m, tv->pext1);\n\t\tes->angles[i] = ReadAngle(m, tv->pext1);\n\t}\n}\n\nstatic void ParseStaticSound(sv_t *tv, netmsg_t *m, int to, unsigned int mask)\n{\n\tif (tv->map.staticsound_count == MAX_STATICSOUNDS)\n\t{\n\t\ttv->map.staticsound_count--;\t// don't be fatal.\n\t\tSys_Printf(tv->cluster, \"Too many static sounds\\n\");\n\t}\n\n\ttv->map.staticsound[tv->map.staticsound_count].origin[0] = ReadCoord(m, tv->pext1);\n\ttv->map.staticsound[tv->map.staticsound_count].origin[1] = ReadCoord(m, tv->pext1);\n\ttv->map.staticsound[tv->map.staticsound_count].origin[2] = ReadCoord(m, tv->pext1);\n\ttv->map.staticsound[tv->map.staticsound_count].soundindex = ReadByte(m);\n\ttv->map.staticsound[tv->map.staticsound_count].volume = ReadByte(m);\n\ttv->map.staticsound[tv->map.staticsound_count].attenuation = ReadByte(m);\n\n\ttv->map.staticsound_count++;\n\n\tConnectionData(tv, (char*)m->data+m->startpos, m->readpos - m->startpos, to, mask, Q1);\n}\n\nstatic void ParseIntermission(sv_t *tv, netmsg_t *m, int to, unsigned int mask)\n{\n\tReadShort(m);\n\tReadShort(m);\n\tReadShort(m);\n\tReadByte(m);\n\tReadByte(m);\n\tReadByte(m);\n\n\tMulticast(tv, (char*)m->data+m->startpos, m->readpos - m->startpos, to, mask, QW);\n}\n\nextern const usercmd_t nullcmd;\nstatic void ParsePlayerInfo(sv_t *tv, netmsg_t *m, qboolean clearoldplayers)\n{\n\tusercmd_t nonnullcmd;\n\tint flags;\n\tint num;\n\tint i;\n\n\tif (clearoldplayers)\n\t{\n\t\tfor (i = 0; i < MAX_CLIENTS; i++)\n\t\t{\t//hide players\n\t\t\t//they'll be sent after this packet.\n\t\t\ttv->map.players[i].oldactive = tv->map.players[i].active;\n\t\t\ttv->map.players[i].active = false;\n\t\t}\n\t}\n\n\tnum = ReadByte(m);\n\tif (num >= MAX_CLIENTS)\n\t{\n\t\tnum = 0;\t// don't be fatal.\n\t\tSys_Printf(tv->cluster, \"Too many svc_playerinfos, wrapping\\n\");\n\t}\n\ttv->map.players[num].old = tv->map.players[num].current;\n\n\tif (tv->usequakeworldprotocols)\n\t{\n\t\tflags = (unsigned short)ReadShort (m);\n\n\t\ttv->map.players[num].current.origin[0] = ReadCoord (m, tv->pext1);\n\t\ttv->map.players[num].current.origin[1] = ReadCoord (m, tv->pext1);\n\t\ttv->map.players[num].current.origin[2] = ReadCoord (m, tv->pext1);\n\n\t\ttv->map.players[num].current.frame = ReadByte(m);\n\n\t\tif (flags & PF_MSEC)\n\t\t\tReadByte (m);\n\n\t\tif (flags & PF_COMMAND)\n\t\t{\n\t\t\tReadDeltaUsercmd(m, &nullcmd, &nonnullcmd);\n\t\t\ttv->map.players[num].current.angles[0] = nonnullcmd.angles[0];\n\t\t\ttv->map.players[num].current.angles[1] = nonnullcmd.angles[1];\n\t\t\ttv->map.players[num].current.angles[2] = nonnullcmd.angles[2];\n\t\t}\n\t\telse\n\t\t{\t//the only reason we'd not get a command is if it's us.\n\t\t\tif (tv->controller)\n\t\t\t{\n\t\t\t\ttv->map.players[num].current.angles[0] = tv->controller->ucmds[2].angles[0];\n\t\t\t\ttv->map.players[num].current.angles[1] = tv->controller->ucmds[2].angles[1];\n\t\t\t\ttv->map.players[num].current.angles[2] = tv->controller->ucmds[2].angles[2];\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\ttv->map.players[num].current.angles[0] = tv->proxyplayerangles[0];\n\t\t\t\ttv->map.players[num].current.angles[1] = tv->proxyplayerangles[1];\n\t\t\t\ttv->map.players[num].current.angles[2] = tv->proxyplayerangles[2];\n\t\t\t}\n\t\t}\n\n\t\tfor (i=0 ; i<3 ; i++)\n\t\t{\n\t\t\tif (flags & (PF_VELOCITY1<<i) )\n\t\t\t\ttv->map.players[num].current.velocity[i] = ReadShort(m);\n\t\t\telse\n\t\t\t\ttv->map.players[num].current.velocity[i] = 0;\n\t\t}\n\n\t\ttv->map.players[num].gibbed = !!(flags & PF_GIB);\n\t\ttv->map.players[num].dead = !!(flags & PF_DEAD);\n\n\t\tif (flags & PF_MODEL)\n\t\t\ttv->map.players[num].current.modelindex = ReadByte (m);\n\t\telse\n\t\t\ttv->map.players[num].current.modelindex = tv->map.modelindex_player;\n\n\t\tif (flags & PF_SKINNUM)\n\t\t\ttv->map.players[num].current.skinnum = ReadByte (m);\n\t\telse\n\t\t\ttv->map.players[num].current.skinnum = 0;\n\n\t\tif (flags & PF_EFFECTS)\n\t\t\ttv->map.players[num].current.effects = ReadByte (m);\n\t\telse\n\t\t\ttv->map.players[num].current.effects = 0;\n\n\t\tif (flags & PF_WEAPONFRAME)\n\t\t\ttv->map.players[num].current.weaponframe = ReadByte (m);\n\t\telse\n\t\t\ttv->map.players[num].current.weaponframe = 0;\n\n\t\ttv->map.players[num].active = true;\n\t}\n\telse\n\t{\n\t\tflags = ReadShort(m);\n\t\ttv->map.players[num].gibbed = !!(flags & DF_GIB);\n\t\ttv->map.players[num].dead = !!(flags & DF_DEAD);\n\t\ttv->map.players[num].current.frame = ReadByte(m);\n\n\t\tfor (i = 0; i < 3; i++)\n\t\t{\n\t\t\tif (flags & (DF_ORIGIN << i))\n\t\t\t\ttv->map.players[num].current.origin[i] = ReadCoord (m, tv->pext1);\n\t\t}\n\n\t\tfor (i = 0; i < 3; i++)\n\t\t{\n\t\t\tif (flags & (DF_ANGLES << i))\n\t\t\t{\n\t\t\t\ttv->map.players[num].current.angles[i] = (ReadShort(m)/(float)0x10000)*360;\n\t\t\t}\n\t\t}\n\n\t\tif (flags & DF_MODEL)\n\t\t\ttv->map.players[num].current.modelindex = ReadByte (m);\n\n\t\tif (flags & DF_SKINNUM)\n\t\t\ttv->map.players[num].current.skinnum = ReadByte (m);\n\n\t\tif (flags & DF_EFFECTS)\n\t\t\ttv->map.players[num].current.effects = ReadByte (m);\n\n\t\tif (flags & DF_WEAPONFRAME)\n\t\t\ttv->map.players[num].current.weaponframe = ReadByte (m);\n\n\t\ttv->map.players[num].active = true;\n\n\t}\n\n\ttv->map.players[num].leafcount = BSP_SphereLeafNums(tv->map.bsp,\tMAX_ENTITY_LEAFS, tv->map.players[num].leafs,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\ttv->map.players[num].current.origin[0],\n\t\t\t\t\t\t\t\t\t\t\t\t\t\ttv->map.players[num].current.origin[1],\n\t\t\t\t\t\t\t\t\t\t\t\t\t\ttv->map.players[num].current.origin[2], 32);\n}\n\nstatic int readentitynum(netmsg_t *m, unsigned int *retflags)\n{\n\tint entnum;\n\tunsigned int flags;\n\tflags = ReadShort(m);\n\tif (!flags)\n\t{\n\t\t*retflags = 0;\n\t\treturn 0;\n\t}\n\n\tentnum = flags&511;\n\tflags &= ~511;\n\n\tif (flags & U_MOREBITS)\n\t{\n\t\tflags |= ReadByte(m);\n\n\t\tif (flags & UX_EVENMORE)\n\t\t\tflags |= ReadByte(m)<<16;\n\t\tif (flags & UX_YETMORE)\n\t\t\tflags |= ReadByte(m)<<24;\n\t}\n\n\tif (flags & UX_ENTITYDBL)\n\t\tentnum += 512;\n\tif (flags & UX_ENTITYDBL2)\n\t\tentnum += 1024;\n\n\t*retflags = flags;\n\n\treturn entnum;\n}\n\nstatic void ParseEntityDelta(sv_t *tv, netmsg_t *m, const entity_state_t *old, entity_state_t *new, unsigned int flags, entity_t *ent, qboolean forcerelink)\n{\n\tmemcpy(new, old, sizeof(entity_state_t));\n\n\tif (flags & U_MODEL)\n\t{\n\t\tif (flags & UX_MODELDBL)\n\t\t\tnew->modelindex = ReadByte(m)|0x100;\t//doubled limit...\n\t\telse\n\t\t\tnew->modelindex = ReadByte(m);\n\t}\n\telse if (flags & UX_MODELDBL)\n\t\tnew->modelindex = ReadShort(m);\t//more sane path...\n\tif (flags & U_FRAME)\n\t\tnew->frame = ReadByte(m);\n\tif (flags & U_COLORMAP)\n\t\tnew->colormap = ReadByte(m);\n\tif (flags & U_SKIN)\n\t\tnew->skinnum = ReadByte(m);\n\tif (flags & U_EFFECTS)\n\t\tnew->effects = (new->effects&0xff00)|ReadByte(m);\n\n\tif (flags & U_ORIGIN1)\n\t\tnew->origin[0] = ReadCoord(m, tv->pext1);\n\tif (flags & U_ANGLE1)\n\t\tnew->angles[0] = ReadAngle(m, tv->pext1);\n\tif (flags & U_ORIGIN2)\n\t\tnew->origin[1] = ReadCoord(m, tv->pext1);\n\tif (flags & U_ANGLE2)\n\t\tnew->angles[1] = ReadAngle(m, tv->pext1);\n\tif (flags & U_ORIGIN3)\n\t\tnew->origin[2] = ReadCoord(m, tv->pext1);\n\tif (flags & U_ANGLE3)\n\t\tnew->angles[2] = ReadAngle(m, tv->pext1);\n\n\tif (flags & UX_SCALE)\n\t\tnew->scale = ReadByte(m);\n\tif (flags & UX_ALPHA)\n\t\tnew->alpha = ReadByte(m);\n\tif (flags & UX_FATNESS)\n\t\tnew->fatness = (signed char)ReadByte(m);\n\tif (flags & UX_DRAWFLAGS)\n\t\tnew->drawflags = ReadByte(m);\n\tif (flags & UX_ABSLIGHT)\n\t\tnew->abslight = ReadByte(m);\n\tif (flags & UX_COLOURMOD)\n\t{\n\t\tnew->colormod[0] = ReadByte(m);\n\t\tnew->colormod[1] = ReadByte(m);\n\t\tnew->colormod[2] = ReadByte(m);\n\t}\n\tif (flags & UX_DPFLAGS)\n\t{\t// these are bits for the 'flags' field of the entity_state_t\n\t\tnew->dpflags = ReadByte(m);\n\t}\n\tif (flags & UX_TAGINFO)\n\t{\n\t\tnew->tagentity = ReadShort(m);\n\t\tnew->tagindex = ReadShort(m);\n\t}\n\tif (flags & UX_LIGHT)\n\t{\n\t\tnew->light[0] = ReadShort(m);\n\t\tnew->light[1] = ReadShort(m);\n\t\tnew->light[2] = ReadShort(m);\n\t\tnew->light[3] = ReadShort(m);\n\t\tnew->lightstyle = ReadByte(m);\n\t\tnew->lightpflags = ReadByte(m);\n\t}\n\tif (flags & UX_EFFECTS16)\n\t\tnew->effects = (new->effects&0x00ff)|(ReadByte(m)<<8);\n\n\n\tif (forcerelink || (flags & (U_ORIGIN1|U_ORIGIN2|U_ORIGIN3|U_MODEL)))\n\t{\n\t\tif (ent)\n\t\t\tent->leafcount = \n\t\t\t\t\tBSP_SphereLeafNums(tv->map.bsp, MAX_ENTITY_LEAFS, ent->leafs,\n\t\t\t\t\tnew->origin[0],\n\t\t\t\t\tnew->origin[1],\n\t\t\t\t\tnew->origin[2], 32);\n\t}\n}\n\nstatic int ExpandFrame(unsigned int newmax, frame_t *frame)\n{\n\tentity_state_t *newents;\n\tunsigned short *newnums;\n\n\tif (newmax < frame->maxents)\n\t\treturn true;\n\n\tnewmax += 16;\n\n\tnewents = malloc(sizeof(*newents) * newmax);\n\tif (!newents)\n\t\treturn false;\n\tnewnums = malloc(sizeof(*newnums) * newmax);\n\tif (!newnums)\n\t{\n\t\tfree(newents);\n\t\treturn false;\n\t}\n\n\tmemcpy(newents, frame->ents, sizeof(*newents) * frame->maxents);\n\tmemcpy(newnums, frame->entnums, sizeof(*newnums) * frame->maxents);\n\n\tif (frame->ents)\n\t\tfree(frame->ents);\n\tif (frame->entnums)\n\t\tfree(frame->entnums);\n\t\n\tframe->ents = newents;\n\tframe->entnums = newnums;\n\tframe->maxents = newmax;\n\treturn true;\n}\n\nstatic void ParsePacketEntities(sv_t *tv, netmsg_t *m, int deltaframe)\n{\n\tframe_t *newframe;\n\tframe_t *oldframe;\n\tint oldcount;\n\tint newnum, oldnum;\n\tint newindex, oldindex;\n\tunsigned int flags;\n\n\tviewer_t *v;\n\n\ttv->map.nailcount = 0;\n\n\ttv->physicstime = tv->curtime;\n\n\tif (tv->cluster->chokeonnotupdated)\n\t{\n\t\tfor (v = tv->cluster->viewers; v; v = v->next)\n\t\t{\n\t\t\tif (v->server == tv)\n\t\t\t\tv->chokeme = false;\n\t\t}\n\t\tfor (v = tv->cluster->viewers; v; v = v->next)\n\t\t{\n\t\t\tif (v->server == tv && v->netchan.isnqprotocol)\n\t\t\t\tv->maysend = true;\n\t\t}\n\t}\n\n\n\tif (deltaframe != -1)\n\t\tdeltaframe &= (ENTITY_FRAMES-1);\n\n\tif (tv->usequakeworldprotocols)\n\t{\n\t\tnewframe = &tv->map.frame[tv->netchan.incoming_sequence & (ENTITY_FRAMES-1)];\n\n\t\tif (tv->netchan.outgoing_sequence - tv->netchan.incoming_sequence >= ENTITY_FRAMES - 1)\n\t\t{\n\t\t\t//should drop it\n\t\t\tSys_Printf(tv->cluster, \"Outdated frames\\n\");\n\t\t}\n\t\telse if (deltaframe != -1 && newframe->oldframe != deltaframe)\n\t\t\tSys_Printf(tv->cluster, \"Mismatching delta frames\\n\");\n\t}\n\telse\n\t{\n\t\tdeltaframe = tv->netchan.incoming_sequence & (ENTITY_FRAMES-1);\n\t\ttv->netchan.incoming_sequence++;\n\t\tnewframe = &tv->map.frame[tv->netchan.incoming_sequence & (ENTITY_FRAMES-1)];\n\t}\n\tif (deltaframe != -1)\n\t{\n\t\toldframe = &tv->map.frame[deltaframe];\n\t\toldcount = oldframe->numents;\n\t}\n\telse\n\t{\n\t\toldframe = NULL;\n\t\toldcount = 0;\n\t}\n\n\toldindex = 0;\n\tnewindex = 0;\n\n//printf(\"frame\\n\");\n\n\tfor(;;)\n\t{\n\t\tnewnum = readentitynum(m, &flags);\n\t\tif (!newnum)\n\t\t{\n\t\t\t//end of packet\n\t\t\t//any remaining old ents need to be copied to the new frame\n\t\t\twhile (oldindex < oldcount)\n\t\t\t{\n//printf(\"Propogate (spare)\\n\");\n\t\t\t\tif (!ExpandFrame(newindex, newframe))\n\t\t\t\t\tbreak;\n\n\t\t\t\tmemcpy(&newframe->ents[newindex], &oldframe->ents[oldindex], sizeof(entity_state_t));\n\t\t\t\tnewframe->entnums[newindex] = oldframe->entnums[oldindex];\n\t\t\t\tnewindex++;\n\t\t\t\toldindex++;\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\n\t\tif (oldindex >= oldcount)\n\t\t\toldnum = 0xffff;\n\t\telse\n\t\t\toldnum = oldframe->entnums[oldindex];\n\t\twhile(newnum > oldnum)\n\t\t{\n//printf(\"Propogate (unchanged)\\n\");\n\t\t\tif (!ExpandFrame(newindex, newframe))\n\t\t\t\tbreak;\n\n\t\t\tmemcpy(&newframe->ents[newindex], &oldframe->ents[oldindex], sizeof(entity_state_t));\n\t\t\tnewframe->entnums[newindex] = oldframe->entnums[oldindex];\n\t\t\tnewindex++;\n\t\t\toldindex++;\n\n\t\t\tif (oldindex >= oldcount)\n\t\t\t\toldnum = 0xffff;\n\t\t\telse\n\t\t\t\toldnum = oldframe->entnums[oldindex];\n\t\t}\n\n\t\tif (newnum < oldnum)\n\t\t{\t//this ent wasn't in the last packet\n//printf(\"add\\n\");\n\t\t\tif (flags & U_REMOVE)\n\t\t\t{\t//remove this ent... just don't copy it across.\n\t\t\t\t//printf(\"add\\n\");\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (!ExpandFrame(newindex, newframe))\n\t\t\t\tbreak;\n\t\t\tParseEntityDelta(tv, m, &tv->map.entity[newnum].baseline, &newframe->ents[newindex], flags, &tv->map.entity[newnum], true);\n\t\t\tnewframe->entnums[newindex] = newnum;\n\t\t\tnewindex++;\n\t\t}\n\t\telse if (newnum == oldnum)\n\t\t{\n\t\t\tif (flags & U_REMOVE)\n\t\t\t{\t//remove this ent... just don't copy it across.\n\t\t\t\t//printf(\"add\\n\");\n\t\t\t\toldindex++;\n\t\t\t\tcontinue;\n\t\t\t}\n//printf(\"Propogate (changed)\\n\");\n\t\t\tif (!ExpandFrame(newindex, newframe))\n\t\t\t\tbreak;\n\t\t\tParseEntityDelta(tv, m, &oldframe->ents[oldindex], &newframe->ents[newindex], flags, &tv->map.entity[newnum], false);\n\t\t\tnewframe->entnums[newindex] = newnum;\n\t\t\tnewindex++;\n\t\t\toldindex++;\n\t\t}\n\n\t}\n\n\tnewframe->numents = newindex;\nreturn;\n\n/*\n\n\t//luckilly, only updated entities are here, so that keeps cpu time down a bit.\n\tfor (;;)\n\t{\n\t\tflags = ReadShort(m);\n\t\tif (!flags)\n\t\t\tbreak;\n\n\t\tentnum = flags & 511;\n\t\tif (tv->maxents < entnum)\n\t\t\ttv->maxents = entnum;\n\t\tflags &= ~511;\n\t\tmemcpy(&tv->entity[entnum].old, &tv->entity[entnum].current, sizeof(entity_state_t));\t//ow.\n\t\tif (flags & U_REMOVE)\n\t\t{\n\t\t\ttv->entity[entnum].current.modelindex = 0;\n\t\t\tcontinue;\n\t\t}\n\t\tif (!tv->entity[entnum].current.modelindex)\t//lerp from baseline\n\t\t{\n\t\t\tmemcpy(&tv->entity[entnum].current, &tv->entity[entnum].baseline, sizeof(entity_state_t));\n\t\t\tforcerelink = true;\n\t\t}\n\t\telse\n\t\t\tforcerelink = false;\n\n\t\tif (flags & U_MOREBITS)\n\t\t\tflags |= ReadByte(m);\n\t\tif (flags & U_MODEL)\n\t\t\ttv->entity[entnum].current.modelindex = ReadByte(m);\n\t\tif (flags & U_FRAME)\n\t\t\ttv->entity[entnum].current.frame = ReadByte(m);\n\t\tif (flags & U_COLORMAP)\n\t\t\ttv->entity[entnum].current.colormap = ReadByte(m);\n\t\tif (flags & U_SKIN)\n\t\t\ttv->entity[entnum].current.skinnum = ReadByte(m);\n\t\tif (flags & U_EFFECTS)\n\t\t\ttv->entity[entnum].current.effects = ReadByte(m);\n\n\t\tif (flags & U_ORIGIN1)\n\t\t\ttv->entity[entnum].current.origin[0] = ReadShort(m);\n\t\tif (flags & U_ANGLE1)\n\t\t\ttv->entity[entnum].current.angles[0] = ReadByte(m);\n\t\tif (flags & U_ORIGIN2)\n\t\t\ttv->entity[entnum].current.origin[1] = ReadShort(m);\n\t\tif (flags & U_ANGLE2)\n\t\t\ttv->entity[entnum].current.angles[1] = ReadByte(m);\n\t\tif (flags & U_ORIGIN3)\n\t\t\ttv->entity[entnum].current.origin[2] = ReadShort(m);\n\t\tif (flags & U_ANGLE3)\n\t\t\ttv->entity[entnum].current.angles[2] = ReadByte(m);\n\n\t\ttv->entity[entnum].updatetime = tv->curtime;\n\t\tif (!tv->entity[entnum].old.modelindex)\t//no old state\n\t\t\tmemcpy(&tv->entity[entnum].old, &tv->entity[entnum].current, sizeof(entity_state_t));\t//copy the new to the old, so we don't end up with interpolation glitches\n\n\n\t\tif ((flags & (U_ORIGIN1 | U_ORIGIN2 | U_ORIGIN3)) || forcerelink)\n\t\t\ttv->entity[entnum].leafcount = BSP_SphereLeafNums(tv->bsp, MAX_ENTITY_LEAFS, tv->entity[entnum].leafs,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttv->entity[entnum].current.origin[0],\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttv->entity[entnum].current.origin[1],\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttv->entity[entnum].current.origin[2], 32);\n\t}\n*/\n}\n\nvoid ParseSpawnStatic(sv_t *tv, netmsg_t *m, int to, unsigned int mask, qboolean delta)\n{\n\tif (tv->map.spawnstatic_count == MAX_STATICENTITIES)\n\t{\n\t\ttv->map.spawnstatic_count--;\t// don't be fatal.\n\t\tSys_Printf(tv->cluster, \"Too many static entities\\n\");\n\t}\n\n\tif (delta)\n\t{\n\t\tunsigned int flags;\n\t\treadentitynum(m, &flags);\n\t\tParseEntityDelta(tv, m, &null_entity_state, &tv->map.spawnstatic[tv->map.spawnstatic_count], flags, NULL, false);\n\t}\n\telse\n\t\tParseEntityState(tv, &tv->map.spawnstatic[tv->map.spawnstatic_count], m);\n\n\ttv->map.spawnstatic_count++;\n\n\tConnectionData(tv, (char*)m->data+m->startpos, m->readpos - m->startpos, to, mask, Q1);\n}\n\nstatic void ParseBaseline(sv_t *tv, netmsg_t *m, int to, unsigned int mask, qboolean delta)\n{\n\tunsigned int entnum;\n\tif (delta)\n\t{\n\t\tentity_state_t es;\n\t\tunsigned int flags;\n\t\tentnum = readentitynum(m, &flags);\n\t\tParseEntityDelta(tv, m, &null_entity_state, &es, flags, NULL, false);\n\n\t\tif (entnum >= MAX_ENTITIES)\n\t\t{\n\t\t\tParseError(m);\n\t\t\treturn;\n\t\t}\n\t\ttv->map.entity[entnum].baseline = es;\n\t}\n\telse\n\t{\n\t\tentnum = ReadShort(m);\n\t\tif (entnum >= MAX_ENTITIES)\n\t\t{\n\t\t\tParseError(m);\n\t\t\treturn;\n\t\t}\n\t\tParseEntityState(tv, &tv->map.entity[entnum].baseline, m);\n\t}\n\t\n\tConnectionData(tv, (char*)m->data+m->startpos, m->readpos - m->startpos, to, mask, Q1);\n}\n\nstatic void ParseUpdatePing(sv_t *tv, netmsg_t *m, int to, unsigned int mask)\n{\n\tint pnum;\n\tint ping;\n\tpnum = ReadByte(m);\n\tping = ReadShort(m);\n\n\tif (pnum < MAX_CLIENTS)\n\t\ttv->map.players[pnum].ping = ping;\n\telse\n\t\tSys_Printf(tv->cluster, \"svc_updateping: invalid player number\\n\");\n\n\tMulticast(tv, (char*)m->data+m->startpos, m->readpos - m->startpos, to, mask, QW);\n}\n\nstatic void ParseUpdateFrags(sv_t *tv, netmsg_t *m, int to, unsigned int mask)\n{\n\tint pnum;\n\tint frags;\n\tpnum = ReadByte(m);\n\tfrags = (signed short)ReadShort(m);\n\n\tif (pnum < MAX_CLIENTS)\n\t\ttv->map.players[pnum].frags = frags;\n\telse\n\t\tSys_Printf(tv->cluster, \"svc_updatefrags: invalid player number\\n\");\n\n\tMulticast(tv, (char*)m->data+m->startpos, m->readpos - m->startpos, to, mask, (pnum < 16)?Q1:QW);\n}\n\nstatic void ParseUpdateStat(sv_t *tv, netmsg_t *m, int to, unsigned int mask)\n{\n\tunsigned int pnum;\n\tint value;\n\tint statnum;\n\n\tstatnum = ReadByte(m);\n\tvalue = ReadByte(m);\n\n\tif (statnum < MAX_STATS)\n\t{\n\t\tfor (pnum = 0; pnum < MAX_CLIENTS; pnum++)\n\t\t{\n\t\t\tif (mask & (1<<pnum))\n\t\t\t\ttv->map.players[pnum].stats[statnum] = value;\n\t\t}\n\t}\n\telse\n\t\tSys_Printf(tv->cluster, \"svc_updatestat: invalid stat number\\n\");\n\n//\tMulticast(tv, (char*)m->data+m->startpos, m->readpos - m->startpos, to, mask, QW);\n}\nstatic void ParseUpdateStatLong(sv_t *tv, netmsg_t *m, int to, unsigned int mask)\n{\n\tunsigned int pnum;\n\tint value;\n\tint statnum;\n\n\tstatnum = ReadByte(m);\n\tvalue = ReadLong(m);\n\n\tif (statnum < MAX_STATS)\n\t{\n\t\tfor (pnum = 0; pnum < MAX_CLIENTS; pnum++)\n\t\t{\n\t\t\tif (mask & (1<<pnum))\n\t\t\t\ttv->map.players[pnum].stats[statnum] = value;\n\t\t}\n\t}\n\telse\n\t\tSys_Printf(tv->cluster, \"svc_updatestatlong: invalid stat number\\n\");\n\n//\tMulticast(tv, (char*)m->data+m->startpos, m->readpos - m->startpos, to, mask, QW);\n}\n\nstatic void ParseUpdateUserinfo(sv_t *tv, netmsg_t *m, int to, unsigned int mask)\n{\n\tint pnum;\n\tpnum = ReadByte(m);\n\tReadLong(m);\n\tif (pnum < MAX_CLIENTS)\n\t\tReadString(m, tv->map.players[pnum].userinfo, sizeof(tv->map.players[pnum].userinfo));\n\telse\n\t{\n\t\tSys_Printf(tv->cluster, \"svc_updateuserinfo: invalid player number\\n\");\n\t\twhile (ReadByte(m))\t//suck out the message.\n\t\t{\n\t\t}\n\t}\n\n\tConnectionData(tv, (char*)m->data+m->startpos, m->readpos - m->startpos, to, mask, QW);\n}\n\nstatic void ParsePacketloss(sv_t *tv, netmsg_t *m, int to, unsigned int mask)\n{\n\tunsigned int pnum;\n\tint value;\n\n\tpnum = ReadByte(m)%MAX_CLIENTS;\n\tvalue = ReadByte(m);\n\n\tif (pnum < MAX_CLIENTS)\n\t\ttv->map.players[pnum].packetloss = value;\n\telse\n\t\tSys_Printf(tv->cluster, \"svc_updatepl: invalid player number\\n\");\n\n\tMulticast(tv, (char*)m->data+m->startpos, m->readpos - m->startpos, to, mask, QW);\n}\n\nstatic void ParseUpdateEnterTime(sv_t *tv, netmsg_t *m, int to, unsigned int mask)\n{\n\tunsigned int pnum;\n\tfloat value;\n\n\tpnum = ReadByte(m)%MAX_CLIENTS;\n\tvalue = ReadFloat(m);\n\n\tif (pnum < MAX_CLIENTS)\n\t\ttv->map.players[pnum].entertime = value;\n\telse\n\t\tSys_Printf(tv->cluster, \"svc_updateentertime: invalid player number\\n\");\n\n\tConnectionData(tv, (char*)m->data+m->startpos, m->readpos - m->startpos, to, mask, QW);\n}\n\nstatic void ParseSound(sv_t *tv, netmsg_t *m, int to, unsigned int mask)\n{\n#define\tSND_VOLUME\t\t(1<<15)\t\t// a qbyte\n#define\tSND_ATTENUATION\t(1<<14)\t\t// a qbyte\n\n#define DEFAULT_SOUND_PACKET_VOLUME 255\n#define DEFAULT_SOUND_PACKET_ATTENUATION 1.0\n\tint i;\n\tint channel;\n\tunsigned char vol;\n\tunsigned char atten;\n\tunsigned char sound_num;\n\tfloat org[3];\n\tint ent;\n\n\n\tnetmsg_t nqversion;\n\tunsigned char nqbuffer[64];\n\tInitNetMsg(&nqversion, nqbuffer, sizeof(nqbuffer));\n\n\tchannel = (unsigned short)ReadShort(m);\n\n\n\tif (channel & SND_VOLUME)\n\t\tvol = ReadByte (m);\n\telse\n\t\tvol = DEFAULT_SOUND_PACKET_VOLUME;\n\n\tif (channel & SND_ATTENUATION)\n\t\tatten = ReadByte (m) / 64.0;\n\telse\n\t\tatten = DEFAULT_SOUND_PACKET_ATTENUATION;\n\n\tsound_num = ReadByte (m);\n\n\tent = (channel>>3)&1023;\n\tchannel &= 7;\n\n\tfor (i=0 ; i<3 ; i++)\n\t\torg[i] = ReadCoord (m, tv->pext1);\n\n\tMulticast(tv, (char*)m->data+m->startpos, m->readpos - m->startpos, to, mask, QW);\n\n\n\tWriteByte(&nqversion, svc_sound);\n\ti = 0;\n\tif (vol != DEFAULT_SOUND_PACKET_VOLUME)\n\t\ti |= 1;\n\tif (atten != DEFAULT_SOUND_PACKET_ATTENUATION)\n\t\ti |= 2;\n\tif (ent > 8191 || channel > 7)\n\t\ti |= 8;\n\tif (sound_num > 255)\n\t\ti |= 16;\n\tWriteByte(&nqversion, i);\n\tif (i & 1)\n\t\tWriteByte(&nqversion, vol);\n\tif (i & 2)\n\t\tWriteByte(&nqversion, atten*64);\n\tif (i & 8)\n\t{\n\t\tWriteShort(&nqversion, ent);\n\t\tWriteByte(&nqversion, channel);\n\t}\n\telse\n\t\tWriteShort(&nqversion, (ent<<3) | channel);\n\tif (i & 16)\n\t\tWriteShort(&nqversion, sound_num);\n\telse\n\t\tWriteByte(&nqversion, sound_num);\n\tWriteCoord(&nqversion, org[0], tv->pext1);\n\tWriteCoord(&nqversion, org[1], tv->pext1);\n\tWriteCoord(&nqversion, org[2], tv->pext1);\n\n\tMulticast(tv, nqversion.data, nqversion.cursize, to, mask, NQ);\n}\n\nstatic void ParseDamage(sv_t *tv, netmsg_t *m, int to, unsigned int mask)\n{\n\tReadByte (m);\n\tReadByte (m);\n\tReadCoord (m, tv->pext1);\n\tReadCoord (m, tv->pext1);\n\tReadCoord (m, tv->pext1);\n\tMulticast(tv, (char*)m->data+m->startpos, m->readpos - m->startpos, to, mask, QW);\n}\n\nenum {\n\tTE_SPIKE\t\t\t= 0,\n\tTE_SUPERSPIKE\t\t= 1,\n\tTE_GUNSHOT\t\t\t= 2,\n\tTE_EXPLOSION\t\t= 3,\n\tTE_TAREXPLOSION\t\t= 4,\n\tTE_LIGHTNING1\t\t= 5,\n\tTE_LIGHTNING2\t\t= 6,\n\tTE_WIZSPIKE\t\t\t= 7,\n\tTE_KNIGHTSPIKE\t\t= 8,\n\tTE_LIGHTNING3\t\t= 9,\n\tTE_LAVASPLASH\t\t= 10,\n\tTE_TELEPORT\t\t\t= 11,\n\n\tTE_BLOOD\t\t\t= 12,\n\tTE_LIGHTNINGBLOOD\t= 13,\n};\nstatic void ParseTempEntity(sv_t *tv, netmsg_t *m, int to, unsigned int mask)\n{\n\tint i;\n\tint dest = QW;\n\tchar nqversion[64];\n\tint nqversionlength=0;\n\n\ti = ReadByte (m);\n\tswitch(i)\n\t{\n\tcase TE_SPIKE:\n\t\tReadCoord (m, tv->pext1);\n\t\tReadCoord (m, tv->pext1);\n\t\tReadCoord (m, tv->pext1);\n\t\tdest |= NQ;\n\t\tbreak;\n\tcase TE_SUPERSPIKE:\n\t\tReadCoord (m, tv->pext1);\n\t\tReadCoord (m, tv->pext1);\n\t\tReadCoord (m, tv->pext1);\n\t\tdest |= NQ;\n\t\tbreak;\n\tcase TE_GUNSHOT:\n\t\tReadByte (m);\n\n\t\tnqversion[0] = svc_temp_entity;\n\t\tnqversion[1] = TE_GUNSHOT;\n\t\tif (tv->pext1 & PEXT_FLOATCOORDS)\n\t\t\tnqversionlength = 2+3*4;\n\t\telse\n\t\t\tnqversionlength = 2+3*2;\n\t\tfor (i = 2; i < nqversionlength; i++)\n\t\t\tnqversion[i] = ReadByte (m);\n\t\tbreak;\n\tcase TE_EXPLOSION:\n\t\tReadCoord (m, tv->pext1);\n\t\tReadCoord (m, tv->pext1);\n\t\tReadCoord (m, tv->pext1);\n\t\tdest |= NQ;\n\t\tbreak;\n\tcase TE_TAREXPLOSION:\n\t\tReadCoord (m, tv->pext1);\n\t\tReadCoord (m, tv->pext1);\n\t\tReadCoord (m, tv->pext1);\n\t\tdest |= NQ;\n\t\tbreak;\n\tcase TE_LIGHTNING1:\n\tcase TE_LIGHTNING2:\n\tcase TE_LIGHTNING3:\n\t\tReadShort (m);\n\n\t\tReadCoord (m, tv->pext1);\n\t\tReadCoord (m, tv->pext1);\n\t\tReadCoord (m, tv->pext1);\n\n\t\tReadCoord (m, tv->pext1);\n\t\tReadCoord (m, tv->pext1);\n\t\tReadCoord (m, tv->pext1);\n\t\tdest |= NQ;\n\t\tbreak;\n\tcase TE_WIZSPIKE:\n\t\tReadCoord (m, tv->pext1);\n\t\tReadCoord (m, tv->pext1);\n\t\tReadCoord (m, tv->pext1);\n\t\tdest |= NQ;\n\t\tbreak;\n\tcase TE_KNIGHTSPIKE:\n\t\tReadCoord (m, tv->pext1);\n\t\tReadCoord (m, tv->pext1);\n\t\tReadCoord (m, tv->pext1);\n\t\tdest |= NQ;\n\t\tbreak;\n\tcase TE_LAVASPLASH:\n\t\tReadCoord (m, tv->pext1);\n\t\tReadCoord (m, tv->pext1);\n\t\tReadCoord (m, tv->pext1);\n\t\tdest |= NQ;\n\t\tbreak;\n\tcase TE_TELEPORT:\n\t\tReadCoord (m, tv->pext1);\n\t\tReadCoord (m, tv->pext1);\n\t\tReadCoord (m, tv->pext1);\n\t\tdest |= NQ;\n\t\tbreak;\n\tcase TE_BLOOD:\n\t\tReadByte (m);\n\t\tReadCoord (m, tv->pext1);\n\t\tReadCoord (m, tv->pext1);\n\t\tReadCoord (m, tv->pext1);\n\t\t//FIXME: generate svc_particle for nq\n\t\tbreak;\n\tcase TE_LIGHTNINGBLOOD:\n\t\tReadCoord (m, tv->pext1);\n\t\tReadCoord (m, tv->pext1);\n\t\tReadCoord (m, tv->pext1);\n\t\t//FIXME: generate svc_particle for nq\n\t\tbreak;\n\tdefault:\n\t\tSys_Printf(tv->cluster, \"temp entity %i not recognised\\n\", i);\n\t\treturn;\n\t}\n\n\tMulticast(tv, (char*)m->data+m->startpos, m->readpos - m->startpos, to, mask, dest);\n\n\tif (nqversionlength)\n\t\tMulticast(tv, nqversion, nqversionlength, to, mask, NQ);\n}\n\nvoid ParseLightstyle(sv_t *tv, netmsg_t *m)\n{\n\tint style;\n\tstyle = ReadByte(m);\n\tif (style < MAX_LIGHTSTYLES)\n\t\tReadString(m, tv->map.lightstyle[style].name, sizeof(tv->map.lightstyle[style].name));\n\telse\n\t{\n\t\tSys_Printf(tv->cluster, \"svc_lightstyle: invalid lightstyle index (%i)\\n\", style);\n\t\twhile (ReadByte(m))\t//suck out the message.\n\t\t{\n\t\t}\n\t}\n\n\tMulticast(tv, (char*)m->data+m->startpos, m->readpos - m->startpos, dem_read, (unsigned)-1, Q1);\n}\n\nvoid ParseNails(sv_t *tv, netmsg_t *m, qboolean nails2)\n{\n\tint count;\n\tint i;\n\tcount = (unsigned char)ReadByte(m);\n\twhile(count > sizeof(tv->map.nails) / sizeof(tv->map.nails[0]))\n\t{//they sent too many, suck it out.\n\t\tcount--;\n\t\tif (nails2)\n\t\t\tReadByte(m);\n\t\tfor (i = 0; i < 6; i++)\n\t\t\tReadByte(m);\n\t}\n\n\ttv->map.nailcount = count;\n\twhile(count-- > 0)\n\t{\n\t\tif (nails2)\n\t\t\ttv->map.nails[count].number = ReadByte(m);\n\t\telse\n\t\t\ttv->map.nails[count].number = count;\n\t\tfor (i = 0; i < 6; i++)\n\t\t\ttv->map.nails[count].bits[i] = ReadByte(m);\n\t}\n}\n\nvoid ParseDownload(sv_t *tv, netmsg_t *m)\n{\n//warning this needs looking at (controller downloads)\n\tint size, b;\n\tunsigned int percent;\n\tchar buffer[2048];\n\n\tsize = (signed short)ReadShort(m);\n\tpercent = ReadByte(m);\n\n\tif (size < 0)\n\t{\n\t\tSys_Printf(tv->cluster, \"Downloading failed\\n\");\n\t\tif (tv->downloadfile)\n\t\t\tfclose(tv->downloadfile);\n\t\ttv->downloadfile = NULL;\n\t\ttv->errored = ERR_PERMANENT;\n\t\tQW_StreamPrint(tv->cluster, tv, NULL, \"Map download failed\\n\");\n\t\treturn;\n\t}\n\n\tfor (b = 0; b < size; b++)\n\t\tbuffer[b] = ReadByte(m);\n\n\tif (!tv->downloadfile)\n\t{\n\t\tSys_Printf(tv->cluster, \"Not downloading anything\\n\");\n\t\ttv->errored = ERR_PERMANENT;\n\t\treturn;\n\t}\n\tfwrite(buffer, 1, size, tv->downloadfile);\n\n\tif (percent == 100)\n\t{\n\t\tfclose(tv->downloadfile);\n\t\ttv->downloadfile = NULL;\n\n\t\tsnprintf(buffer, sizeof(buffer), \"%s/%s\", (*tv->map.gamedir)?tv->map.gamedir:\"id1\", tv->map.modellist[1].name);\n\t\trename(tv->downloadname, buffer);\n\n\t\tSys_Printf(tv->cluster, \"Download complete\\n\");\n\n\t\ttv->map.bsp = BSP_LoadModel(tv->cluster, tv->map.gamedir, tv->map.modellist[1].name);\n\t\tif (!tv->map.bsp)\n\t\t{\n\t\t\tSys_Printf(tv->cluster, \"Failed to read BSP\\n\");\n\t\t\ttv->errored = ERR_PERMANENT;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tSendClientCommand(tv, \"prespawn %i 0 %i\\n\", tv->clservercount, LittleLong(BSP_Checksum(tv->map.bsp)));\n\t\t\tstrcpy(tv->status, \"Prespawning\\n\");\n\t\t}\n\t}\n\telse\n\t{\n\t\tsnprintf(tv->status, sizeof(tv->status), \"Downloading map, %i%%\\n\", percent);\n\t\tSendClientCommand(tv, \"nextdl\\n\");\n\t}\n}\n\nvoid ParseMessage(sv_t *tv, void *buffer, int length, int to, int mask)\n{\n\tint lastsvc;\n\tint svc = -1;\n\tint i;\n\tnetmsg_t buf;\n\tqboolean clearoldplayers = true;\n\tbuf.cursize = length;\n\tbuf.maxsize = length;\n\tbuf.readpos = 0;\n\tbuf.data = buffer;\n\tbuf.startpos = 0;\n\twhile(buf.readpos < buf.cursize)\n\t{\n\t\tlastsvc = svc;\n\t\tif (buf.readpos > buf.cursize)\n\t\t{\n\t\t\tSys_Printf(tv->cluster, \"Read past end of parse buffer\\n, last was %i\\n\", lastsvc);\n\t\t\treturn;\n\t\t}\n\t\tbuf.startpos = buf.readpos;\n\t\tsvc = ReadByte(&buf);\n//\t\tprintf(\"%i\\n\", svc);\n\t\tswitch (svc)\n\t\t{\n\t\tcase svc_bad:\n\t\t\tParseError(&buf);\n\t\t\tSys_Printf(tv->cluster, \"ParseMessage: svc_bad, last was %i\\n\", lastsvc);\n\t\t\treturn;\n\t\tcase svc_nop:\t//quakeworld isn't meant to send these.\n\t\t\tQTV_Printf(tv, \"nop\\n\");\n\t\t\tbreak;\n\n\t\tcase svc_disconnect:\n\t\t\t//mvdsv safely terminates it's mvds with an svc_disconnect.\n\t\t\t//the client is meant to read that and disconnect without reading the intentionally corrupt packet following it.\n\t\t\t//however, our demo playback is chained and looping and buffered.\n\t\t\t//so we've already found the end of the source file and restarted parsing.\n\t\t\t\n\t\t\t//in fte at least, the server does give the packet the correct length\n\t\t\t//I hope mvdsv is the same\n\t\t\tif (tv->sourcetype != SRC_DEMO)\n\t\t\t{\n#ifndef _MSC_VER\n\t#warning QTV is meant to disconnect when servers tells it to.\n#endif\n\t\t\t\tQTV_Printf(tv, \"Maliciously ignoring svc_disconnect from upstream...\\n\");\t//ideally the client would be the one to close() the socket first so its the one that gets stuck in TIME_WAIT instead of the server.\n\t\t\t\t// FIXME: Servers are today sending the svc_disconnect in a non-standard way, which makes QTV drop when it shouldn't.\n\t\t\t\t// Tell the server developers to fix the servers.\n\t\t\t\t//tv->drop = true;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\twhile(ReadByte(&buf))\n\t\t\t\t\t;\n\t\t\t}\n\t\t\treturn;\n\n\t\tcase svc_updatestat:\n\t\t\tParseUpdateStat(tv, &buf, to, mask);\n\t\t\tbreak;\n\n//#define\tsvc_version\t\t\t4\t// [long] server version\n\t\tcase svc_nqsetview:\n\t\t\tReadShort(&buf);\n//no actual handling is done!\n\t\t\tbreak;\n\t\tcase svc_sound:\n\t\t\tParseSound(tv, &buf, to, mask);\n\t\t\tbreak;\n\t\tcase svc_nqtime:\n\t\t\tReadFloat(&buf);\n//no actual handling is done!\n\t\t\tbreak;\n\n\t\tcase svc_print:\n\t\t\tParsePrint(tv, &buf, to, mask);\n\t\t\tbreak;\n\n\t\tcase svc_stufftext:\n\t\t\tParseStufftext(tv, &buf, to, mask);\n\t\t\tbreak;\n\n\t\tcase svc_setangle:\n\t\t\tif (!tv->usequakeworldprotocols)\n\t\t\t\tReadByte(&buf);\n\t\t\ttv->proxyplayerangles[0] = ReadAngle(&buf, tv->pext1);\n\t\t\ttv->proxyplayerangles[1] = ReadAngle(&buf, tv->pext1);\n\t\t\ttv->proxyplayerangles[2] = ReadAngle(&buf, tv->pext1);\n\n\t\t\tif (tv->usequakeworldprotocols && tv->controller)\n\t\t\t\tSendBufferToViewer(tv->controller, (char*)buf.data+buf.startpos, buf.readpos - buf.startpos, true);\n\n\t\t\t/*{\n\t\t\t\tchar nq[7];\n\t\t\t\tnq[0] = svc_setangle;\n\t\t\t\tnq[1] = tv->proxyplayerangles[0];\n\t\t\t\tnq[2] = tv->proxyplayerangles[1];\n\t\t\t\tnq[3] = tv->proxyplayerangles[2];\n//\t\t\t\tMulticast(tv, nq, 4, to, mask, Q1);\n\t\t\t}*/\n\t\t\tbreak;\n\n\t\tcase svc_serverdata:\n\t\t\tParseServerData(tv, &buf, to, mask);\n\t\t\tbreak;\n\n\t\tcase svc_lightstyle:\n\t\t\tParseLightstyle(tv, &buf);\n\t\t\tbreak;\n\n//#define\tsvc_updatename\t\t13\t// [qbyte] [string]\n\n\t\tcase svc_updatefrags:\n\t\t\tParseUpdateFrags(tv, &buf, to, mask);\n\t\t\tbreak;\n\n//#define\tsvc_clientdata\t\t15\t// <shortbits + data>\n//#define\tsvc_stopsound\t\t16\t// <see code>\n//#define\tsvc_updatecolors\t17\t// [qbyte] [qbyte] [qbyte]\n\n\t\tcase svc_particle:\n\t\t\tReadCoord(&buf, tv->pext1);\n\t\t\tReadCoord(&buf, tv->pext1);\n\t\t\tReadCoord(&buf, tv->pext1);\n\t\t\tReadByte(&buf);\n\t\t\tReadByte(&buf);\n\t\t\tReadByte(&buf);\n\t\t\tReadByte(&buf);\n\t\t\tReadByte(&buf);\n\t\t\tMulticast(tv, (char*)buf.data+buf.startpos, buf.readpos - buf.startpos, dem_read, (unsigned)-1, Q1);\n\t\t\tbreak;\n\n\t\tcase svc_damage:\n\t\t\tParseDamage(tv, &buf, to, mask);\n\t\t\tbreak;\n\n\t\tcase svc_spawnstatic:\n\t\t\tParseSpawnStatic(tv, &buf, to, mask, false);\n\t\t\tbreak;\n\n\t\tcase svcfte_spawnstatic2:\n\t\t\tif (tv->pext1 & PEXT_SPAWNSTATIC2)\n\t\t\t\tParseSpawnStatic(tv, &buf, to, mask, true);\n\t\t\telse\n\t\t\t\tgoto badsvc;\n\t\t\tbreak;\n\n\t\tcase svc_spawnbaseline:\n\t\t\tParseBaseline(tv, &buf, to, mask, false);\n\t\t\tbreak;\n\t\tcase svcfte_spawnbaseline2:\n\t\t\tif (tv->pext1 & PEXT_SPAWNSTATIC2)\n\t\t\t\tParseBaseline(tv, &buf, to, mask, true);\n\t\t\telse\n\t\t\t\tgoto badsvc;\n\t\t\tbreak;\n\n\t\tcase svc_temp_entity:\n\t\t\tParseTempEntity(tv, &buf, to, mask);\n\t\t\tbreak;\n\n\t\tcase svc_setpause:\t// [qbyte] on / off\n\t\t\ttv->map.ispaused = ReadByte(&buf);\n\t\t\tMulticast(tv, (char*)buf.data+buf.startpos, buf.readpos - buf.startpos, dem_read, (unsigned)-1, Q1);\n\t\t\tbreak;\n\n//#define\tsvc_signonnum\t\t25\t// [qbyte]  used for the signon sequence\n\n\t\tcase svc_centerprint:\n\t\t\tParseCenterprint(tv, &buf, to, mask);\n\t\t\tbreak;\n\n\t\tcase svc_spawnstaticsound:\n\t\t\tParseStaticSound(tv, &buf, to, mask);\n\t\t\tbreak;\n\n\t\tcase svc_intermission:\n\t\t\tParseIntermission(tv, &buf, to, mask);\n\t\t\tbreak;\n\n\t\tcase svc_finale:\n\t\t\twhile(ReadByte(&buf))\n\t\t\t\t;\n\t\t\tMulticast(tv, (char*)buf.data+buf.startpos, buf.readpos - buf.startpos, dem_read, (unsigned)-1, Q1);\n\t\t\tbreak;\n\n\t\tcase svc_cdtrack:\n\t\t\tParseCDTrack(tv, &buf, to, mask);\n\t\t\tbreak;\n\n\t\tcase svc_sellscreen:\n\t\t\tMulticast(tv, (char*)buf.data+buf.startpos, buf.readpos - buf.startpos, dem_read, (unsigned)-1, Q1);\n\t\t\tbreak;\n\n//#define svc_cutscene\t\t34\t//hmm... nq only... added after qw tree splitt?\n\n\t\tcase svc_smallkick:\n\t\tcase svc_bigkick:\n\t\t\tMulticast(tv, (char*)buf.data+buf.startpos, buf.readpos - buf.startpos, to, mask, QW);\n\t\t\tbreak;\n\n\t\tcase svc_updateping:\n\t\t\tParseUpdatePing(tv, &buf, to, mask);\n\t\t\tbreak;\n\n\t\tcase svc_updateentertime:\n\t\t\tParseUpdateEnterTime(tv, &buf, to, mask);\n\t\t\tbreak;\n\n\t\tcase svc_updatestatlong:\n\t\t\tParseUpdateStatLong(tv, &buf, to, mask);\n\t\t\tbreak;\n\n\t\tcase svc_muzzleflash:\n\t\t\tReadShort(&buf);\n\t\t\tMulticast(tv, (char*)buf.data+buf.startpos, buf.readpos - buf.startpos, to, mask, QW);\n\t\t\tbreak;\n\n\t\tcase svc_updateuserinfo:\n\t\t\tParseUpdateUserinfo(tv, &buf, to, mask);\n\t\t\tbreak;\n\n\t\tcase svc_download:\t// [short] size [size bytes]\n\t\t\tParseDownload(tv, &buf);\n\t\t\tbreak;\n\n\t\tcase svc_playerinfo:\n\t\t\tParsePlayerInfo(tv, &buf, clearoldplayers);\n\t\t\tclearoldplayers = false;\n\t\t\tbreak;\n\n\t\tcase svc_nails:\n\t\t\tParseNails(tv, &buf, false);\n\t\t\tbreak;\n\t\tcase svc_chokecount:\n\t\t\tReadByte(&buf);\n\t\t\tbreak;\n\n\t\tcase svcfte_modellistshort:\n\t\tcase svc_modellist:\n\t\t\ti = ParseList(tv, &buf, tv->map.modellist, to, mask, svc==svcfte_modellistshort);\n\t\t\tif (!i)\n\t\t\t{\n\t\t\t\tint j;\n\t\t\t\tif (tv->map.bsp)\n\t\t\t\t\tBSP_Free(tv->map.bsp);\n\n\t\t\t\tif (tv->cluster->nobsp)// || !tv->usequkeworldprotocols)\n\t\t\t\t\ttv->map.bsp = NULL;\n\t\t\t\telse\n\t\t\t\t\ttv->map.bsp = BSP_LoadModel(tv->cluster, tv->map.gamedir, tv->map.modellist[1].name);\n\n\t\t\t\ttv->map.numinlines = 0;\n\t\t\t\tfor (j = 2; j < 256; j++)\n\t\t\t\t{\n\t\t\t\t\tif (*tv->map.modellist[j].name != '*')\n\t\t\t\t\t\tbreak;\n\t\t\t\t\ttv->map.numinlines = j;\n\t\t\t\t}\n\n\t\t\t\ttv->map.modelindex_player = 0;\n\t\t\t\ttv->map.modelindex_spike = 0;\n\t\t\t\tfor (j = 2; j < 256; j++)\n\t\t\t\t{\n\t\t\t\t\tif (!*tv->map.modellist[j].name)\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tif (!strcmp(tv->map.modellist[j].name, \"progs/player.mdl\"))\n\t\t\t\t\t\ttv->map.modelindex_player = j;\n\t\t\t\t\tif (!strcmp(tv->map.modellist[j].name, \"progs/spike.mdl\"))\n\t\t\t\t\t\ttv->map.modelindex_spike = j;\n\t\t\t\t}\n\t\t\t\tstrcpy(tv->status, \"Prespawning\\n\");\n\t\t\t}\n\t\t\tConnectionData(tv, (void*)((char*)buf.data+buf.startpos), buf.readpos - buf.startpos, to, mask, QW);\n\t\t\tif ((!tv->controller || tv->controller->netchan.isnqprotocol) && tv->usequakeworldprotocols)\n\t\t\t{\n\t\t\t\tif (i)\n\t\t\t\t\tSendClientCommand(tv, \"modellist %i %i\\n\", tv->clservercount, i);\n\t\t\t\telse if (!tv->map.bsp && !tv->cluster->nobsp)\n\t\t\t\t{\n\t\t\t\t\tif (tv->downloadfile)\n\t\t\t\t\t{\n\t\t\t\t\t\tfclose(tv->downloadfile);\n\t\t\t\t\t\tunlink(tv->downloadname);\n\t\t\t\t\t\tSys_Printf(tv->cluster, \"Was already downloading %s\\nOld download canceled\\n\", tv->downloadname);\n\t\t\t\t\t\ttv->downloadfile = NULL;\n\t\t\t\t\t}\n\t\t\t\t\tsnprintf(tv->downloadname, sizeof(tv->downloadname), \"%s/%s.tmp\", (*tv->map.gamedir)?tv->map.gamedir:\"id1\", tv->map.modellist[1].name);\n\t\t\t\t\tQTV_mkdir(tv->downloadname);\n\t\t\t\t\ttv->downloadfile = fopen(tv->downloadname, \"wb\");\n\t\t\t\t\tif (!tv->downloadfile)\n\t\t\t\t\t{\n\t\t\t\t\t\tSys_Printf(tv->cluster, \"Couldn't open temporary file %s\\n\", tv->downloadname);\n\n\t\t\t\t\t\tSendClientCommand(tv, \"prespawn %i 0 %i\\n\", tv->clservercount, LittleLong(BSP_Checksum(tv->map.bsp)));\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tchar buffer[512];\n\n\t\t\t\t\t\tstrcpy(tv->status, \"Downloading map\\n\");\n\t\t\t\t\t\tSys_Printf(tv->cluster, \"Attempting download of %s\\n\", tv->downloadname);\n\t\t\t\t\t\tSendClientCommand(tv, \"download %s\\n\", tv->map.modellist[1].name);\n\n\t\t\t\t\t\tsnprintf(buffer, sizeof(buffer), \"[QTV] Attempting map download (%s)\\n\", tv->map.modellist[1].name);\n\t\t\t\t\t\tQW_StreamPrint(tv->cluster, tv, NULL, buffer);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tSendClientCommand(tv, \"prespawn %i 0 %i\\n\", tv->clservercount, LittleLong(BSP_Checksum(tv->map.bsp)));\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\t\tcase svcfte_soundlistshort:\n\t\tcase svc_soundlist:\n\t\t\ti = ParseList(tv, &buf, tv->map.soundlist, to, mask, svc==svcfte_soundlistshort);\n\t\t\tif (!i)\n\t\t\t\tstrcpy(tv->status, \"Receiving modellist\\n\");\n\t\t\tConnectionData(tv, (void*)((char*)buf.data+buf.startpos), buf.readpos - buf.startpos, to, mask, QW);\n\t\t\tif ((!tv->controller || tv->controller->netchan.isnqprotocol) && tv->usequakeworldprotocols)\n\t\t\t{\n\t\t\t\tif (i)\n\t\t\t\t\tSendClientCommand(tv, \"soundlist %i %i\\n\", tv->clservercount, i);\n\t\t\t\telse\n\t\t\t\t\tSendClientCommand(tv, \"modellist %i 0\\n\", tv->clservercount);\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase svc_packetentities:\n//\t\t\tFlushPacketEntities(tv);\n\t\t\tParsePacketEntities(tv, &buf, -1);\n\t\t\tbreak;\n\t\tcase svc_deltapacketentities:\n\t\t\tParsePacketEntities(tv, &buf, ReadByte(&buf));\n\t\t\tbreak;\n\n\t\tcase svc_entgravity:\t\t// gravity change, for prediction\n\t\t\tReadFloat(&buf);\n\t\t\tMulticast(tv, (char*)buf.data+buf.startpos, buf.readpos - buf.startpos, to, mask, QW);\n\t\t\tbreak;\n\t\tcase svc_maxspeed:\t\t\t// maxspeed change, for prediction\n\t\t\tReadFloat(&buf);\n\t\t\tMulticast(tv, (char*)buf.data+buf.startpos, buf.readpos - buf.startpos, to, mask, QW);\n\t\t\tbreak;\n\t\tcase svc_setinfo:\n\t\t\tParseSetInfo(tv, &buf);\n\t\t\tbreak;\n\t\tcase svc_serverinfo:\n\t\t\tParseServerinfo(tv, &buf);\n\t\t\tbreak;\n\t\tcase svc_updatepl:\n\t\t\tParsePacketloss(tv, &buf, to, mask);\n\t\t\tbreak;\n\t\tcase svc_nails2:\n\t\t\tParseNails(tv, &buf, true);\n\t\t\tbreak;\n\n\t\tcase svc_killedmonster:\n\t\t\tMulticast(tv, (char*)buf.data+buf.startpos, buf.readpos - buf.startpos, to, mask, Q1);\n\t\t\tbreak;\n\t\tcase svc_foundsecret:\n\t\t\tMulticast(tv, (char*)buf.data+buf.startpos, buf.readpos - buf.startpos, to, mask, Q1);\n\t\t\tbreak;\n\n\t\tdefault:\n\t\tbadsvc:\n\t\t\tbuf.readpos = buf.startpos;\n\t\t\tSys_Printf(tv->cluster, \"Can't handle svc %i, last was %i\\n\", (unsigned int)ReadByte(&buf), lastsvc);\n\t\t\treturn;\n\t\t}\n\t}\n}\n\n"
  },
  {
    "path": "fteqtv/pmove.c",
    "content": "#include \"qtv.h\"\n#include <math.h>\n\n#ifndef M_PI\n#define M_PI 3.1415926535897932384626433832795\n#endif\n\nvoid AngleVectors (vec3_t angles, float *forward, float *right, float *up)\n{\n\tfloat\t\tangle;\n\tfloat\t\tsr, sp, sy, cr, cp, cy;\n\n\tangle = angles[1] * (M_PI*2 / 360);\n\tsy = sin(angle);\n\tcy = cos(angle);\n\tangle = angles[0] * (M_PI*2 / 360);\n\tsp = sin(angle);\n\tcp = cos(angle);\n\tangle = angles[2] * (M_PI*2 / 360);\n\tsr = sin(angle);\n\tcr = cos(angle);\n\n\tforward[0] = cp*cy;\n\tforward[1] = cp*sy;\n\tforward[2] = -sp;\n\tright[0] = (-1*sr*sp*cy+-1*cr*-sy);\n\tright[1] = (-1*sr*sp*sy+-1*cr*cy);\n\tright[2] = -1*sr*cp;\n\tup[0] = (cr*sp*cy+-sr*-sy);\n\tup[1] = (cr*sp*sy+-sr*cy);\n\tup[2] = cr*cp;\n}\n\n\n\n\n#define DotProduct(a,b) ((a[0]*b[0]) + (a[1]*b[1]) + (a[2]*b[2]))\n#define VectorCopy(a,b) do{b[0]=a[0];b[1]=a[1];b[2]=a[2];}while(0)\n#define VectorClear(v) do{v[0]=0;v[1]=0;v[2]=0;}while(0)\n#define VectorScale(i,s,o) do{o[0]=i[0]*s;o[1]=i[1]*s;o[2]=i[2]*s;}while(0)\n#define Length(v) sqrt(DotProduct(v, v))\n#define VectorMA(base,s,m,out) do{out[0]=base[0]+s*m[0];out[1]=base[1]+s*m[1];out[2]=base[2]+s*m[2];}while(0)\n#define SHORT2ANGLE(s) ((s*360.0f)/65536)\n\nfloat VectorNormalize(vec3_t v)\n{\n\tfloat len, ilen;\n\tlen = Length(v);\n\tif (len)\n\t{\n\t\tilen = 1/len;\n\t\tv[0] *= ilen;\n\t\tv[1] *= ilen;\n\t\tv[2] *= ilen;\n\t}\n\treturn len;\n}\n\n\nvoid PM_SpectatorMove (pmove_t *pmove)\n{\n\tfloat\tspeed, drop, friction, control, newspeed;\n\tfloat\tcurrentspeed, addspeed, accelspeed;\n\tint\t\t\ti;\n\tvec3_t\t\twishvel;\n\tfloat\t\tfmove, smove;\n\tvec3_t\t\twishdir;\n\tfloat\t\twishspeed;\n\n\t// friction\n\n\tspeed = Length (pmove->velocity);\n\tif (speed < 1)\n\t{\n\t\tVectorClear (pmove->velocity);\n\t}\n\telse\n\t{\n\t\tdrop = 0;\n\n\t\tfriction = pmove->movevars.friction*1.5;\t// extra friction\n\t\tcontrol = speed < pmove->movevars.stopspeed ? pmove->movevars.stopspeed : speed;\n\t\tdrop += control*friction*pmove->frametime;\n\n\t\t// scale the velocity\n\t\tnewspeed = speed - drop;\n\t\tif (newspeed < 0)\n\t\t\tnewspeed = 0;\n\t\tnewspeed /= speed;\n\n\t\tVectorScale (pmove->velocity, newspeed, pmove->velocity);\n\t}\n\n\t// accelerate\n\tfmove = pmove->cmd.forwardmove;\n\tsmove = pmove->cmd.sidemove;\n\n\tVectorNormalize (pmove->forward);\n\tVectorNormalize (pmove->right);\n\n\tfor (i=0 ; i<3 ; i++)\n\t\twishvel[i] = pmove->forward[i]*fmove + pmove->right[i]*smove;\n\twishvel[2] += pmove->cmd.upmove;\n\n\tVectorCopy (wishvel, wishdir);\n\twishspeed = VectorNormalize(wishdir);\n\n\t//\n\t// clamp to server defined max speed\n\t//\n\tif (wishspeed > pmove->movevars.spectatormaxspeed)\n\t{\n\t\tVectorScale (wishvel, pmove->movevars.spectatormaxspeed/wishspeed, wishvel);\n\t\twishspeed = pmove->movevars.spectatormaxspeed;\n\t}\n\n\tcurrentspeed = DotProduct(pmove->velocity, wishdir);\n\taddspeed = wishspeed - currentspeed;\n\n\t// Buggy QW spectator mode, kept for compatibility\n//\tif (pmove->pm_type == PM_OLD_SPECTATOR)\n\t{\n\t\tif (addspeed <= 0)\n\t\t\treturn;\n\t}\n\n\tif (addspeed > 0)\n\t{\n\t\taccelspeed = pmove->movevars.accelerate*pmove->frametime*wishspeed;\n\t\tif (accelspeed > addspeed)\n\t\t\taccelspeed = addspeed;\n\n\t\tfor (i=0 ; i<3 ; i++)\n\t\t\tpmove->velocity[i] += accelspeed*wishdir[i];\n\t}\n\n\t// move\n\tVectorMA (pmove->origin, pmove->frametime, pmove->velocity, pmove->origin);\n}\n\nvoid PM_PlayerMove (pmove_t *pmove)\n{\n\tpmove->frametime = pmove->cmd.msec * 0.001;\n/*\n\tif (pmove.pm_type == PM_NONE || pmove.pm_type == PM_FREEZE)\n\t{\n\t\tPM_CategorizePosition ();\n\t\treturn;\n\t}\n*/\n\t// take angles directly from command\n\tpmove->angles[0] = pmove->cmd.angles[0];\n\tpmove->angles[1] = pmove->cmd.angles[1];\n\tpmove->angles[2] = pmove->cmd.angles[2];\n\n\tAngleVectors (pmove->angles, pmove->forward, pmove->right, pmove->up);\n\n//\tif (pmove->pm_type == PM_SPECTATOR || pmove->pm_type == PM_OLD_SPECTATOR)\n\t{\n\t\tPM_SpectatorMove (pmove);\n//\t\tpmove->onground = false;\n\t\treturn;\n\t}\n}\n"
  },
  {
    "path": "fteqtv/protocol.h",
    "content": "\r\n\r\n//limitations of the protocol\r\n#define\tMAX_SERVERINFO_STRING\t1024\t//standard quakeworld has 512 here.\r\n#define MAX_USERINFO 1024\t//standard quakeworld has 192 here.\r\n#define MAX_CLIENTS 32\r\n#define MAX_LIST 256\r\n#define MAX_MODELS MAX_LIST\r\n#define MAX_SOUNDS MAX_LIST\r\n#define MAX_ENTITIES 512\r\n#define MAX_STATICSOUNDS 256\r\n#define MAX_STATICENTITIES 128\r\n#define MAX_LIGHTSTYLES 64\r\n\r\n#define MAX_PROXY_INBUFFER 4096\t\t//max bytes from a downstream proxy.\r\n#define MAX_PROXY_BUFFER (1<<17)\t//must be power-of-two (buffer sizes for downstream, both sv/cl)\r\n#define PREFERRED_PROXY_BUFFER\t4096 //the ammount of data we try to leave in our input buffer (must be large enough to contain any single mvd frame)\r\n\r\n#define ENTS_PER_FRAME 64 //max number of entities per frame (OUCH!).\r\n#define ENTITY_FRAMES 64 //number of frames to remember for deltaing\r\n\r\n\r\n#define Z_EXT_SERVERTIME\t(1<<3)\t// STAT_TIME\r\n#define Z_EXT_STRING \"8\"\r\n\r\n//qw specific\r\n#define PRINT_CHAT 3\r\n#define PRINT_HIGH 2\r\n#define PRINT_MEDIUM 1\r\n#define PRINT_LOW 0\r\n\r\n\r\n#define MAX_STATS 32\r\n#define\tSTAT_HEALTH\t\t\t0\r\n#define\tSTAT_FRAGS\t\t\t1\r\n#define\tSTAT_WEAPONMODELI\t2\r\n#define\tSTAT_AMMO\t\t\t3\r\n#define\tSTAT_ARMOR\t\t\t4\r\n#define\tSTAT_WEAPONFRAME\t5\r\n#define\tSTAT_SHELLS\t\t\t6\r\n#define\tSTAT_NAILS\t\t\t7\r\n#define\tSTAT_ROCKETS\t\t8\r\n#define\tSTAT_CELLS\t\t\t9\r\n#define\tSTAT_ACTIVEWEAPON\t10\r\n#define\tSTAT_TOTALSECRETS\t11\r\n#define\tSTAT_TOTALMONSTERS\t12\r\n#define\tSTAT_SECRETS\t\t13\t\t// bumped on client side by svc_foundsecret\r\n#define\tSTAT_MONSTERS\t\t14\t\t// bumped by svc_killedmonster\r\n#define STAT_ITEMS\t\t\t15\r\n\r\n#define STAT_TIME 17\t//A ZQ hack, sending time via a stat.\r\n\t\t\t\t\t\t//this allows l33t engines to interpolate properly without spamming at a silly high fps.\r\n\r\n\r\n\r\n\r\n//limits\r\n#define NQ_PACKETS_PER_SECOND 20\r\n#define MAX_MSGLEN 8192\t\t//the biggest datagram size we allow\r\n#define MAX_NQMSGLEN 8000\t//nq has large reliable packets for the connection data\r\n#define MAX_QWMSGLEN 1450\t//qw is fully split into individual packets\r\n#define MAX_NQDATAGRAM 1024\t//nq datagrams are only 1k\r\n#define MAX_BACKBUF_SIZE 1000\t//this is smaller so we don't loose too many entities when lagging\r\n\r\n\r\n//NQ transport layer defines\r\n#define NETFLAG_LENGTH_MASK\t0x0000ffff\r\n#define NETFLAG_DATA\t\t0x00010000\r\n#define NETFLAG_ACK\t\t\t0x00020000\r\n#define NETFLAG_NAK\t\t\t0x00040000\r\n#define NETFLAG_EOM\t\t\t0x00080000\r\n#define NETFLAG_UNRELIABLE\t0x00100000\r\n#define NETFLAG_CTL\t\t\t0x80000000\r\n\r\n#define CCREQ_CONNECT\t\t0x01\r\n#define CCREQ_SERVER_INFO\t0x02\r\n\r\n#define CCREP_ACCEPT\t\t0x81\r\n#define CCREP_REJECT\t\t0x82\r\n#define CCREP_SERVER_INFO\t0x83\r\n\r\n#define NQ_NETCHAN_GAMENAME\t\t\"QUAKE\"\r\n#define NQ_NETCHAN_VERSION\t3\r\n//end NQ specific\r\n\r\n\r\n\r\n\r\n//the clcs sent via the udp connections\r\nenum {\r\n\tclc_bad\t\t= 0,\r\n\tclc_nop\t\t= 1,\r\n\tclc_disconnect\t= 2,\t\t//NQ only\r\n\tclc_move\t= 3,\t\t// [[usercmd_t]\r\n\tclc_stringcmd\t= 4,\t\t// [string] message\r\n\tclc_delta\t= 5,\t\t// [byte] sequence number, requests delta compression of message\r\n\tclc_tmove\t= 6,\t\t// teleport request, spectator only\r\n\tclc_upload\t= 7\t\t// teleport request, spectator only\r\n};\r\n\r\n//these are the clcs sent upstream via the tcp streams\r\nenum {\r\n\tqtv_clc_bad = 0,\r\n\tqtv_clc_stringcmd = 1,\r\n\tqtv_clc_commentarydata = 8\r\n};\r\n\r\n\r\n\r\n\r\n\r\n\r\n#define\tsvc_bad\t\t\t\t0\r\n#define\tsvc_nop\t\t\t\t1\r\n#define\tsvc_disconnect\t\t2\r\n#define\tsvc_updatestat\t\t3\t// [qbyte] [qbyte]\r\n//#define\tsvc_version\t\t\t4\t// [long] server version (not used anywhere)\r\n#define\tsvc_nqsetview\t\t\t5\t// [short] entity number\r\n#define\tsvc_sound\t\t\t6\t// <see code>\r\n#define\tsvc_nqtime\t\t\t7\t// [float] server time\r\n#define\tsvc_print\t\t\t8\t// [qbyte] id [string] null terminated string\r\n#define\tsvc_stufftext\t\t9\t// [string] stuffed into client's console buffer\r\n\t\t\t\t\t\t\t\t// the string should be \\n terminated\r\n#define\tsvc_setangle\t\t10\t// [angle3] set the view angle to this absolute value\r\n\r\n#define\tsvc_serverdata\t\t11\t// [long] protocol ...\r\n#define\tsvc_lightstyle\t\t12\t// [qbyte] [string]\r\n#define\tsvc_nqupdatename\t\t13\t// [qbyte] [string]\r\n#define\tsvc_updatefrags\t\t14\t// [qbyte] [short]\r\n#define\tsvc_nqclientdata\t\t15\t// <shortbits + data>\r\n//#define\tsvc_stopsound\t\t16\t// <see code> (not used anywhere)\r\n#define\tsvc_nqupdatecolors\t17\t// [qbyte] [qbyte] [qbyte]\r\n#define\tsvc_particle\t\t18\t// [vec3] <variable>\r\n#define\tsvc_damage\t\t\t19\r\n\r\n#define\tsvc_spawnstatic\t\t20\r\n//#define\tsvc_spawnstatic2\t21 (not used anywhere)\r\n#define\tsvcfte_spawnstatic2\t21\r\n#define\tsvc_spawnbaseline\t22\r\n\r\n#define\tsvc_temp_entity\t\t23\t// variable\r\n#define\tsvc_setpause\t\t24\t// [qbyte] on / off\r\n#define\tsvc_nqsignonnum\t\t25\t// [qbyte]  used for the signon sequence\r\n\r\n#define\tsvc_centerprint\t\t26\t// [string] to put in center of the screen\r\n\r\n#define\tsvc_killedmonster\t27\r\n#define\tsvc_foundsecret\t\t28\r\n\r\n#define\tsvc_spawnstaticsound\t29\t// [coord3] [qbyte] samp [qbyte] vol [qbyte] aten\r\n\r\n#define\tsvc_intermission\t30\t\t// [vec3_t] origin [vec3_t] angle (show scoreboard and stuff)\r\n#define\tsvc_finale\t\t\t31\t\t// [string] text ('congratulations blah blah')\r\n\r\n#define\tsvc_cdtrack\t\t\t32\t\t// [qbyte] track\r\n#define svc_sellscreen\t\t33\r\n\r\n//#define svc_cutscene\t\t34\t//hmm... nq only... added after qw tree splitt? (intermission without the scores)\r\n\r\n#define\tsvc_smallkick\t\t34\t\t// set client punchangle to 2\r\n#define\tsvc_bigkick\t\t\t35\t\t// set client punchangle to 4\r\n\r\n#define\tsvc_updateping\t\t36\t\t// [qbyte] [short]\r\n#define\tsvc_updateentertime\t37\t\t// [qbyte] [float]\r\n\r\n#define\tsvc_updatestatlong\t38\t\t// [qbyte] [long]\r\n\r\n#define\tsvc_muzzleflash\t\t39\t\t// [short] entity\r\n\r\n#define\tsvc_updateuserinfo\t40\t\t// [qbyte] slot [long] uid\r\n\t\t\t\t\t\t\t\t\t// [string] userinfo\r\n\r\n#define\tsvc_download\t\t41\t\t// [short] size [size bytes]\r\n#define\tsvc_playerinfo\t\t42\t\t// variable\r\n#define\tsvc_nails\t\t\t43\t\t// [qbyte] num [48 bits] xyzpy 12 12 12 4 8\r\n#define\tsvc_chokecount\t\t44\t\t// [qbyte] packets choked\r\n#define\tsvc_modellist\t\t45\t\t// [strings]\r\n#define\tsvc_soundlist\t\t46\t\t// [strings]\r\n#define\tsvc_packetentities\t47\t\t// [...]\r\n#define\tsvc_deltapacketentities\t48\t\t// [...]\r\n#define svc_maxspeed\t\t49\t\t// maxspeed change, for prediction\r\n#define svc_entgravity\t\t50\t\t// gravity change, for prediction\r\n#define svc_setinfo\t\t\t51\t\t// setinfo on a client\r\n#define svc_serverinfo\t\t52\t\t// serverinfo\r\n#define svc_updatepl\t\t53\t\t// [qbyte] [qbyte]\r\n#define svc_nails2\t\t\t54\t\t//mvd only - [qbyte] num [52 bits] nxyzpy 8 12 12 12 4 8\r\n\r\n#define svcfte_soundlistshort\t\t56\r\n#define\tsvcfte_modellistshort\t\t60\r\n#define svcfte_spawnbaseline2\t\t66\r\n\r\n\r\n\r\n#define dem_audio\t\t0\r\n\r\n#define dem_cmd\t\t\t0\r\n#define dem_read\t\t1\r\n#define dem_set\t\t\t2\r\n#define dem_multiple\t3\r\n#define dem_single\t\t4\r\n#define dem_stats\t\t5\r\n#define dem_all\t\t\t6\r\n\r\n#define dem_qtvdata\t\t(dem_all | (1<<4))\t//special packet that contains qtv data (spectator chat, etc. clients need to parse this as soon as it is sent to them, which might or might not be awkward for them)\r\n\r\n#define dem_mask\t\t7\r\n\r\n\r\n#define PROTOCOL_VERSION_NQ\t15\r\n#define\tPROTOCOL_VERSION\t28\r\n\r\n#define PROTOCOL_VERSION_FTE\t\t\t(('F'<<0) + ('T'<<8) + ('E'<<16) + ('X' << 24))\t//fte extensions.\r\n#define PROTOCOL_VERSION_FTE2\t\t\t(('F'<<0) + ('T'<<8) + ('E'<<16) + ('2' << 24))\t//fte extensions.\r\n#define PROTOCOL_VERSION_EZQUAKE1\t\t(('M'<<0) + ('V'<<8) + ('D'<<16) + ('1' << 24)) //ezquake/mvdsv extensions\r\n#define PROTOCOL_VERSION_HUFFMAN\t\t(('H'<<0) + ('U'<<8) + ('F'<<16) + ('F' << 24))\t//packet compression\r\n#define PROTOCOL_VERSION_VARLENGTH\t\t(('v'<<0) + ('l'<<8) + ('e'<<16) + ('n' << 24))\t//variable length handshake\r\n#define PROTOCOL_VERSION_FRAGMENT\t\t(('F'<<0) + ('R'<<8) + ('A'<<16) + ('G' << 24))\t//supports fragmentation/packets larger than 1450\r\n\r\n\r\n\r\n#define PEXT_SETVIEW\t\t\t0x00000001\r\n#define PEXT_SCALE\t\t\t\t0x00000002\t//obsoleted by PEXT2_REPLACEMENTDELTAS\r\n#define PEXT_LIGHTSTYLECOL\t\t0x00000004\r\n#define PEXT_TRANS\t\t\t\t0x00000008\t//obsoleted by PEXT2_REPLACEMENTDELTAS\r\n#define PEXT_VIEW2\t\t\t\t0x00000010\r\n//#define PEXT_BULLETENS\t\t\t0x00000020 //no longer supported\r\n#define PEXT_ACCURATETIMINGS\t0x00000040\r\n#define PEXT_SOUNDDBL\t\t\t0x00000080\t//revised startsound protocol\r\n#define PEXT_FATNESS\t\t\t0x00000100\t//obsoleted by PEXT2_REPLACEMENTDELTAS\r\n#define PEXT_HLBSP\t\t\t\t0x00000200\r\n#define PEXT_TE_BULLET\t\t\t0x00000400\t//obsoleted by fully custom particle effects. its an ooold extension, okay? :/\r\n#define PEXT_HULLSIZE\t\t\t0x00000800\t//obsoleted by PEXT2_REPLACEMENTDELTAS\r\n#define PEXT_MODELDBL\t\t\t0x00001000\t//obsoleted by PEXT2_REPLACEMENTDELTAS\r\n#define PEXT_ENTITYDBL\t\t\t0x00002000\t//max of 1024 ents instead of 512. obsoleted by PEXT2_REPLACEMENTDELTAS\r\n#define PEXT_ENTITYDBL2\t\t\t0x00004000\t//max of 2048 ents instead of 512\r\n#define PEXT_FLOATCOORDS\t\t0x00008000\t//supports floating point origins.\r\n//#define PEXT_VWEAP\t\t\t\t0x00010000\t//cause an extra qbyte to be sent, and an extra list of models for vweaps.\r\n#define PEXT_Q2BSP\t\t\t\t0x00020000\r\n#define PEXT_Q3BSP\t\t\t\t0x00040000\r\n#define PEXT_COLOURMOD\t\t\t0x00080000\t//this replaces an older value which would rarly have caried any actual data.\r\n#define PEXT_SPLITSCREEN\t\t0x00100000\r\n#define PEXT_HEXEN2\t\t\t\t0x00200000\t//more stats and working particle builtin.\r\n#define PEXT_SPAWNSTATIC2\t\t0x00400000\t//Sends an entity delta instead of a baseline. obsoleted by PEXT2_REPLACEMENTDELTAS\r\n#define PEXT_CUSTOMTEMPEFFECTS\t0x00800000\t//supports custom temp ents.\r\n#define PEXT_256PACKETENTITIES\t0x01000000\t//Client can recieve 256 packet entities. obsoleted by PEXT2_REPLACEMENTDELTAS\r\n//#define PEXT_NEVERUSED\t\t0x02000000\r\n#define PEXT_SHOWPIC\t\t\t0x04000000\r\n#define PEXT_SETATTACHMENT\t\t0x08000000\t//md3 tags (needs networking, they need to lerp).\r\n//#define PEXT_NEVERUSED\t\t0x10000000\r\n#define PEXT_CHUNKEDDOWNLOADS\t0x20000000\t//alternate file download method. Hopefully it'll give quadroupled download speed, especially on higher pings.\r\n#define PEXT_CSQC\t\t\t\t0x40000000\t//csqc additions. extra stats, particle svcs\r\n#define PEXT_DPFLAGS\t\t\t0x80000000\t//extra flags for viewmodel/externalmodel and possible other persistant style flags. obsoleted by PEXT2_REPLACEMENTDELTAS\r\n\r\n#define PEXT2_PRYDONCURSOR\t\t\t0x00000001\r\n#define PEXT2_VOICECHAT\t\t\t\t0x00000002\r\n#define PEXT2_SETANGLEDELTA\t\t\t0x00000004\r\n#define PEXT2_OLDREPLACEMENTDELTAS\t0x00000008\t//weaponframe was part of the entity state. that flag is now the player's v_angle.\r\n#define PEXT2_MAXPLAYERS\t\t\t0x00000010\t//Client is able to cope with more players than 32. abs max becomes 255, due to colormap issues.\r\n#define PEXT2_PREDINFO\t\t\t\t0x00000020\t//movevar stats, NQ input sequences+acks.\r\n#define PEXT2_NEWSIZEENCODING\t\t0x00000040\t//richer size encoding.\r\n#define PEXT2_INFOBLOBS\t\t\t\t0x00000080\t//serverinfo+userinfo lengths can be MUCH higher (protocol is unbounded, but expect low sanity limits on userinfo), and contain nulls etc.\r\n//#define PEXT2_PK3DOWNLOADS\t\t0x10000000\t//retrieve a list of pk3s/pk3s/paks for downloading (with optional URL and crcs)\r\n\r\n#define PEXTE_HIDDENMESSAGES\t0x20\t//random demo metadata...\r\n\r\n//flags on entities\r\n#define\tU_ORIGIN1\t(1<<9)\r\n#define\tU_ORIGIN2\t(1<<10)\r\n#define\tU_ORIGIN3\t(1<<11)\r\n#define\tU_ANGLE2\t(1<<12)\r\n#define\tU_FRAME\t\t(1<<13)\r\n#define\tU_REMOVE\t(1<<14)\t\t// REMOVE this entity, don't add it\r\n#define\tU_MOREBITS\t(1<<15)\r\n\r\n// if MOREBITS is set, these additional flags are read in next\r\n#define\tU_ANGLE1\t(1<<0)\r\n#define\tU_ANGLE3\t(1<<1)\r\n#define\tU_MODEL\t\t(1<<2)\r\n#define\tU_COLORMAP\t(1<<3)\r\n#define\tU_SKIN\t\t(1<<4)\r\n#define\tU_EFFECTS\t(1<<5)\r\n#define\tU_SOLID\t\t(1<<6)\t\t// the entity should be solid for prediction\r\n#define\tUX_EVENMORE\t(1<<7)\r\n\r\n#define UX_SCALE\t\t(1<<16)\t//scaler of alias models\r\n#define UX_ALPHA\t\t(1<<17)\t//transparency value\r\n#define UX_FATNESS\t\t(1<<18)\t//qbyte describing how fat an alias model should be. (moves verticies along normals). Useful for vacuum chambers...\r\n#define UX_MODELDBL\t\t(1<<19)\t//extra bit for modelindexes\r\n#define UX_UNUSED1\t\t(1<<20)\r\n#define UX_ENTITYDBL\t(1<<21)\t//use an extra qbyte for origin parts, cos one of them is off\r\n#define UX_ENTITYDBL2\t(1<<22)\t//use an extra qbyte for origin parts, cos one of them is off\r\n#define UX_YETMORE\t\t(1<<23)\t//even more extension info stuff.\r\n#define UX_DRAWFLAGS\t(1<<24)\t//use an extra qbyte for origin parts, cos one of them is off\r\n#define UX_ABSLIGHT\t\t(1<<25)\t//Force a lightlevel\r\n#define UX_COLOURMOD\t(1<<26)\t//rgb\r\n#define UX_DPFLAGS\t\t(1<<27)\r\n#define UX_TAGINFO\t\t(1<<28)\r\n#define UX_LIGHT\t\t(1<<29)\r\n#define\tUX_EFFECTS16\t(1<<30)\r\n#define UX_FARMORE\t\t(1<<31)\r\n\r\n\r\n\r\n//flags on players\r\n#define\tPF_MSEC\t\t\t(1<<0)\r\n#define\tPF_COMMAND\t\t(1<<1)\r\n#define\tPF_VELOCITY1\t(1<<2)\r\n#define\tPF_VELOCITY2\t(1<<3)\r\n#define\tPF_VELOCITY3\t(1<<4)\r\n#define\tPF_MODEL\t\t(1<<5)\r\n#define\tPF_SKINNUM\t\t(1<<6)\r\n#define\tPF_EFFECTS\t\t(1<<7)\r\n#define\tPF_WEAPONFRAME\t(1<<8)\t\t// only sent for view player\r\n#define\tPF_DEAD\t\t\t(1<<9)\t\t// don't block movement any more\r\n#define\tPF_GIB\t\t\t(1<<10)\t\t// offset the view height differently\r\n\r\n//flags on players in mvds\r\n#define DF_ORIGIN\t\t1\r\n#define DF_ANGLES\t\t(1<<3)\r\n#define DF_EFFECTS\t\t(1<<6)\r\n#define DF_SKINNUM\t\t(1<<7)\r\n#define DF_DEAD\t\t\t(1<<8)\r\n#define DF_GIB\t\t\t(1<<9)\r\n#define DF_WEAPONFRAME\t(1<<10)\r\n#define DF_MODEL\t\t(1<<11)\r\n\r\n\r\n\r\n\r\n"
  },
  {
    "path": "fteqtv/qtv.h",
    "content": "/*\nCopyright (C) 1996-1997 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\nSee the included (GNU.txt) GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n*/\n\n#ifdef __GNUC__\n\t#define LittleLong(x) ({ typeof(x) _x = (x); _x = (((unsigned char *)&_x)[0]|(((unsigned char *)&_x)[1]<<8)|(((unsigned char *)&_x)[2]<<16)|(((unsigned char *)&_x)[3]<<24)); _x; })\n\t#define LittleShort(x) ({ typeof(x) _x = (x); _x = (((unsigned char *)&_x)[0]|(((unsigned char *)&_x)[1]<<8)); _x; })\n#else\n\t#define LittleLong(x) (x)\n\t#define LittleShort(x) (x)\n#endif\n\n//this is for a future version\n//#define COMMENTARY\n\n//each server that we are connected to has it's own state.\n//it should be easy enough to use one thread per server.\n\n//mvd info is forwarded to other proxies instantly\n//qwd stuff is buffered and delayed. :(\n\n//this means that when a new proxy connects, we have to send initial state as well as a chunk of pending state, expect to need to send new data before the proxy even has all the init stuff. We may need to raise MAX_PROXY_BUFFER to be larger than on the server\n\n#ifdef __GNUC__\n\t#define PRINTFWARNING(x) __attribute__((format(printf, x, (x+1))))\n#else\n\t#define PRINTFWARNING(x) /*nothing*/\n#endif\n\n//how does multiple servers work\n//each proxy acts as a cluster of connections to servers\n//when a viewer connects, they are given a list of active server connections\n//if there's only one server connection, they are given that one automatically.\n\n#if defined(__APPLE__) && defined(__MACH__)\n#define MACOSX\n#endif\n\n#include <stdio.h>\n/*work around fucked MSVC functions. we use our own for these*/\n#if _MSC_VER >= 1300 && _MSC_VER < 1900\n\t#include <string.h>\n\t#ifndef _CRT_SECURE_NO_WARNINGS\n\t\t#define _CRT_SECURE_NO_WARNINGS\n\t#endif\n\t#define vsnprintf q_vsnprintf /*msvc doesn't null terminate. its insecute and thus useless*/\n\t#define stricmp _stricmp /*msvc just doesn't work properly*/\n\t#define chdir _chdir\n\t#define gwtcwd _getcwd\n#endif\n\n#ifdef _WIN32\n\t#include <conio.h>\n\t#include <winsock2.h>\t//this includes windows.h and is the reason for much compiling slowness with windows builds.\n\t#ifdef IPPROTO_IPV6\n\t\t#include <ws2tcpip.h>\n\t#else\n\t\t#define\tIPPROTO_IPV6 41\n\t\t#ifndef EAI_NONAME\n\t\t#define EAI_NONAME 8\n\t\t#endif\n\t\tstruct ip6_scope_id\n\t\t{\n\t\t\tunion\n\t\t\t{\n\t\t\t\tstruct\n\t\t\t\t{\n\t\t\t\t\tu_long  Zone : 28;\n\t\t\t\t\tu_long  Level : 4;\n\t\t\t\t};\n\t\t\t\tu_long  Value;\n\t\t\t};\n\t\t};\n\t\tstruct in6_addr\n\t\t{\n\t\t\tu_char\ts6_addr[16];\t/* IPv6 address */\n\t\t};\n\t\ttypedef struct sockaddr_in6\n\t\t{\n\t\t\tshort  sin6_family;\n\t\t\tu_short  sin6_port;\n\t\t\tu_long  sin6_flowinfo;\n\t\t\tstruct in6_addr  sin6_addr;\n\t\t\tunion\n\t\t\t{\n\t\t\t\tu_long  sin6_scope_id;\n\t\t\t\tstruct ip6_scope_id  sin6_scope_struct; \n\t\t\t};\n\t\t};\n\t\t#if !(_MSC_VER >= 1500)\n\t\t\tstruct addrinfo\n\t\t\t{\n\t\t\t  int ai_flags;\n\t\t\t  int ai_family;\n\t\t\t  int ai_socktype;\n\t\t\t  int ai_protocol;\n\t\t\t  size_t ai_addrlen;\n\t\t\t  char* ai_canonname;\n\t\t\t  struct sockaddr * ai_addr;\n\t\t\t  struct addrinfo * ai_next;\n\t\t\t};\n\t\t#endif\n\t#endif\n\t#ifdef _MSC_VER\n\t\t#pragma comment (lib, \"wsock32.lib\")\n\t#endif\n\t#define qerrno WSAGetLastError()\n\t#define NET_EWOULDBLOCK WSAEWOULDBLOCK\n\t#define NET_EINPROGRESS WSAEINPROGRESS\n\t#define NET_ECONNREFUSED WSAECONNREFUSED\n\t#define NET_ENOTCONN WSAENOTCONN\n\n\t//we have special functions to properly terminate sprintf buffers in windows.\n\t//we assume other systems are designed with even a minor thought to security.\n\t#if !defined(__MINGW32__)\n\t\t#define unlink _unlink\t//why do MS have to be so awkward?\n\t\tint snprintf(char *buffer, int buffersize, char *format, ...) PRINTFWARNING(3);\n\t\tint vsnprintf(char *buffer, int buffersize, const char *format, va_list argptr);\n\t#else\n\t\t#define unlink remove\t//seems mingw misses something\n\t#endif\n\n\t#ifdef _MSC_VER\n\t\t//okay, so warnings are here to help... they're ugly though.\n\t\t#pragma warning(disable: 4761)\t//integral size mismatch in argument\n\t\t#pragma warning(disable: 4244)\t//conversion from float to short\n\t\t#pragma warning(disable: 4018)\t//signed/unsigned mismatch\n\t#endif\n\n#elif defined(__CYGWIN__)\n\n\t#include <sys/time.h>\n\t#include <sys/types.h>\n\t#include <sys/socket.h>\n\t#include <sys/errno.h>\n\t#include <arpa/inet.h>\n\t#include <stdarg.h>\n\t#include <netdb.h>\n\t#include <sys/ioctl.h>\n\t#include <unistd.h>\n\n\t#define ioctlsocket ioctl\n\t#define closesocket close\n\n#elif (defined(unix) && !defined(__CYGWIN__)) || defined(ixemul) // I hope by adding MACOSX here it doesnt stop it from being natively built on macosx\n\t#include <sys/time.h>\n\t#include <sys/types.h>\n\t#include <sys/socket.h>\n\t#include <netinet/in.h>\n\t#include <netinet/tcp.h>\n\t#include <arpa/inet.h>\n\t#include <netdb.h>\n\t#include <stdarg.h>\n\t#include <stdlib.h>\n\t#include <string.h>\n\t#include <errno.h>\n\t#include <sys/ioctl.h>\n\t#include <unistd.h>\n\n\t#define ioctlsocket ioctl\n\t#define closesocket close\n\n\t#if defined(__linux__) && !defined(ANDROID)\n//\t\t#define HAVE_EPOLL\n\t#endif\n\t#ifdef HAVE_EPOLL\n\t\t#include <sys/epoll.h>\n\t#endif\n#elif (defined(__MORPHOS__) && !defined(ixemul))\n\t#include <stdlib.h>\n\t#include <unistd.h>\n\n\t#include <sys/socket.h>\n\t#include <sys/ioctl.h>\n\t#include <netinet/in.h>\n\t#include <netinet/tcp.h>\n\t#include <netdb.h>\n\t#include <errno.h>\n\n\t#define qerrno Errno()\n\t#define ioctlsocket IoctlSocket\n\t#define closesocket CloseSocket\n#else\n#error \"Please insert required headers here\"\n//try the cygwin ones\n#endif\n\n#ifndef NET_EWOULDBLOCK\n#define NET_EWOULDBLOCK EWOULDBLOCK\n#define NET_EINPROGRESS EINPROGRESS\n#define NET_ECONNREFUSED ECONNREFUSED\n#define NET_ENOTCONN ENOTCONN\n#endif\n\n#ifndef NET_EAGAIN\n#define NET_EAGAIN NET_EWOULDBLOCK\n#endif\n#ifndef IPV6_V6ONLY\n\t#define IPV6_V6ONLY 27\n#endif\n\n#ifndef pgetaddrinfo\n\t#ifndef _WIN32\n\t\t#define pgetaddrinfo getaddrinfo\n\t\t#define pfreeaddrinfo freeaddrinfo\n\t#endif\n#endif\n#ifndef SOCKET\n\t#define SOCKET int\n#endif\n#ifndef INVALID_SOCKET\n\t#define INVALID_SOCKET -1\n#endif\n#ifndef qerrno\n\t#define qerrno errno\n#endif\n\n\n#include <stdio.h>\n#include <string.h>\n\n#ifndef _WIN32\n//stricmp is ansi, strcasecmp is unix.\n\t#define stricmp strcasecmp\n\t#define strnicmp strncasecmp\n#endif\n\n#define ustrlen(s) strlen((char*)(s))\n#define ustrcmp(s1,s2) strcmp((char*)(s1),(char*)(s2))\n#define ustrncmp(s1,s2,l) strncmp((char*)(s1),(char*)(s2),l)\n\n\nsize_t strlcpy(char *dst, const char *src, size_t siz);\n\n\ntypedef struct\n{\n\tunsigned int digestsize;\n\tunsigned int contextsize;\t//you need to alloca(te) this much memory...\n\tvoid (*init) (void *context);\n\tvoid (*process) (void *context, const void *data, size_t datasize);\n\tvoid (*terminate) (unsigned char *digest, void *context);\n} hashfunc_t;\nextern hashfunc_t hash_md5;\nextern hashfunc_t hash_sha1;\n/*extern hashfunc_t hash_sha2_224;\nextern hashfunc_t hash_sha2_256;\nextern hashfunc_t hash_sha2_384;\nextern hashfunc_t hash_sha2_512;*/\nsize_t CalcHash(hashfunc_t *hash, unsigned char *digest, size_t maxdigestsize, const unsigned char *string, size_t stringlen);\nunsigned int CalcHashInt(const hashfunc_t *hash, const void *data, size_t datasize);\nsize_t CalcHMAC(hashfunc_t *hashfunc, unsigned char *digest, size_t maxdigestsize, const unsigned char *data, size_t datalen, const unsigned char *key, size_t keylen);\n\n\n#ifdef LIBQTV\n#define Sys_Printf QTVSys_Printf\n#endif\n\n#ifndef STRINGIFY\n#define STRINGIFY2(s) #s\n#define STRINGIFY(s) STRINGIFY2(s)\n#endif\n#ifdef SVNREVISION\n#define QTV_VERSION_STRING STRINGIFY(SVNREVISION)\n#else\n//#include \"../engine/common/bothdefs.h\"\n//#define QTV_VERSION_STRING STRINGIFY(FTE_VER_MAJOR)\".\"STRINGIFY(FTE_VER_MINOR)\n#define QTV_VERSION_STRING \"v?\"\"?\"\"?\"\n#endif\n\n#define PROX_DEFAULTSERVERPORT 27500\n#define PROX_DEFAULTLISTENPORT 27501\n#define PROX_DEFAULTSERVER \"localhost:27500\"\n\n#define DEFAULT_HOSTNAME \"FTEQTV\"\n#define PROXYWEBSITE \"http://fte.triptohell.info\"\t//url for program\n\n#define MAX_ENTITY_LEAFS 32\n\n\n#include \"protocol.h\"\n\n\n#if !defined(__cplusplus) && (!defined(__STDC_VERSION__) || (defined(__STDC_VERSION__) && (__STDC_VERSION__ < 202311L)))\ntypedef enum {false, true} qboolean;\n#else\ntypedef int qboolean;\n#endif\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef struct\n{\n\tvoid *tcpcon;\t/*value not indirected, only compared*/\n\tchar sockaddr[64];\n} netadr_t;\n\n\n#ifdef COMMENTARY\ntypedef struct soundcapt_s {\n\tint (*update)(struct soundcapt_s *ghnd, int samplechunks, char *buffer);\n\tvoid (*close)(struct soundcapt_s *ptr);\n} soundcapt_t;\ntypedef struct soundplay_s {\n\tint (*update)(struct soundplay_s *ghnd, int samplechunks, char *buffer);\n\tvoid (*close)(struct soundplay_s *ptr);\n} soundplay_t;\n#endif\n\ntypedef struct {\n\tunsigned int readpos;\n\tunsigned int cursize;\n\tunsigned int maxsize;\n\tvoid *data;\n\tunsigned int startpos;\n\tqboolean overflowed;\n\tqboolean allowoverflow;\n} netmsg_t;\n\ntypedef struct {\n\tSOCKET sock;\n\tnetadr_t remote_address;\n\tunsigned short qport;\n\tunsigned int last_received;\n\tunsigned int cleartime;\n\n\tint maxdatagramlen;\n\tint maxreliablelen;\n\n\tint reliable_length;\n\tqboolean drop;\n\tqboolean isclient;\n\tqboolean isnqprotocol;\n\n\tnetmsg_t message;\n\tchar message_buf[MAX_MSGLEN];\t//reliable message being built\n\tchar reliable_buf[MAX_MSGLEN];\t//reliable message that we're making sure arrives.\n\tfloat rate;\n\n\n\tunsigned int incoming_acknowledged;\n\tunsigned int last_reliable_sequence;\n\tunsigned int incoming_reliable_acknowledged;\n\tunsigned int incoming_reliable_sequence;\n\tunsigned int reliable_sequence;\n\n\tunsigned int incoming_sequence;\n\tunsigned int outgoing_sequence;\n\n\n\n\tunsigned int reliable_start;\n\tunsigned int outgoing_unreliable;\n\tunsigned int incoming_unreliable;\n\tunsigned int in_fragment_length;\n\n\tchar in_fragment_buf[MAX_NQMSGLEN];\n} netchan_t;\n\ntypedef struct {\n#define MAX_QPATH 64\n\tchar name[MAX_QPATH];\n} filename_t;\n\ntypedef struct {\n\tunsigned char\tframe;\n\tunsigned short\tmodelindex;\n\tunsigned char\tcolormap;\n\tunsigned char\tskinnum;\n\n\tfloat origin[3];\n\tfloat angles[3];\n\n\tunsigned short\teffects;\n\tunsigned char\tscale;\n\tunsigned char\tfatness;\n\n\tunsigned char\tcolormod[3];\n\tunsigned char\talpha;\n\n\tunsigned short\tlight[4];\n\n\tunsigned char\tlightstyle;\n\tunsigned char\tlightpflags;\n\tunsigned char\tabslight;\n\tunsigned char\tdrawflags;\n\n\tunsigned char\tdpflags;\n\tunsigned char\ttagindex;\t//networked as a short, should have been a byte to match dpp5+.\n\tunsigned short\ttagentity;\n} entity_state_t;\ntypedef struct {\t//qw does players weirdly.\n\tunsigned char frame;\n\tunsigned char modelindex;\n\t//colormap\n\tunsigned char skinnum;\n\tfloat origin[3];\n\tfloat angles[3];\n\tunsigned char effects;\n\n\tshort velocity[3];\n\tunsigned char weaponframe;\n} player_state_t;\ntypedef struct {\n\tunsigned int stats[MAX_STATS];\n\tchar userinfo[MAX_USERINFO];\n\n\tint ping;\n\tint packetloss;\n\tint frags;\n\tfloat entertime;\n\n\tint leafcount;\n\tunsigned short leafs[MAX_ENTITY_LEAFS];\n\n\tqboolean oldactive:1;\n\tqboolean active:1;\n\tqboolean gibbed:1;\n\tqboolean dead:1;\n\tplayer_state_t current;\n\tplayer_state_t old;\n} playerinfo_t;\n\ntypedef struct {\n\tentity_state_t ents[ENTS_PER_FRAME];\t//ouchie ouchie!\n\tunsigned short entnum[ENTS_PER_FRAME];\n\tint numents;\n} packet_entities_t;\n\ntypedef struct {\n\tunsigned char msec;\n\tfloat angles[3];\n\tshort forwardmove, sidemove, upmove;\n\tunsigned char buttons;\n\tunsigned char impulse;\n} usercmd_t;\nextern const usercmd_t nullcmd;\n\n\ntypedef float vec3_t[3];\ntypedef struct {\n\tfloat gravity;\n\tfloat maxspeed;\n\tfloat spectatormaxspeed;\n\tfloat accelerate;\n\tfloat airaccelerate;\n\tfloat waterfriction;\n\tfloat entgrav;\n\tfloat stopspeed;\n\tfloat wateraccelerate;\n\tfloat friction;\n} movevars_t;\ntypedef struct {\n\t//in / out\n\tvec3_t origin;\n\tvec3_t velocity;\n\n\t//in\n\tusercmd_t cmd;\n\tmovevars_t movevars;\n\n\t//internal\n\tvec3_t angles;\n\tfloat frametime;\n\tvec3_t forward, right, up;\n} pmove_t;\n\n\n#define MBTN_UP\t\t(1u<<0)\n#define MBTN_DOWN\t(1u<<1)\n#define MBTN_LEFT\t(1u<<2)\n#define MBTN_RIGHT\t(1u<<3)\n#define MBTN_ENTER\t(1u<<4)\n\n#define MAX_BACK_BUFFERS\t16\ntypedef struct sv_s sv_t;\ntypedef struct cluster_s cluster_t;\ntypedef struct viewer_s {\n\t//viewers are regular clients connected over udp.\n\t//they may be watching a communal stream, or they might themselves be playing through the proxy, directly controlling the stream.\n\tqboolean drop;\n\tunsigned int timeout;\n\tunsigned int nextpacket;\t//for nq clients\n\tnetchan_t netchan;\n\tqboolean maysend;\n\tqboolean chokeme;\n\tqboolean thinksitsconnected;\n\tqboolean conmenussupported;\n\tqboolean isproxy;\n\tunsigned int pext1, pext2;\n\n\tint servercount;\n\n\tnetmsg_t backbuf[MAX_BACK_BUFFERS];\t//note data is malloced!\n\tint backbuffered;\n\n\tunsigned int currentstats[MAX_STATS];\n\tint trackplayer;\n\tint thisplayer;\n\tint userid;\n\n\tpacket_entities_t frame[ENTITY_FRAMES];\n\tint delta_frames[ENTITY_FRAMES];\n\n\tstruct viewer_s *next;\n\tstruct viewer_s *commentator;\n\n\tchar name[32];\n\tchar userinfo[1024];\n\n\tint lost;\t//packets\n\tusercmd_t ucmds[3];\n\tunsigned int lasttime;\n\tunsigned int menubuttons;\n\n\n\tint settime;\t//the time that we last told the client.\n\n\tvec3_t velocity;\n\tvec3_t origin;\n\n\tint isadmin;\n\tchar expectcommand[16];\n\n\tsv_t *server;\n\n\tint menuspamtime;\n\tint menunum;\n\tint menuop;\n\tint fwdval;\t//for scrolling up/down the menu using +forward/+back :)\n\tint firstconnect;\n} viewer_t;\n\ntypedef struct\n{\n\tqboolean websocket;\t//true if we need to use special handling\n\tunsigned char wsbuf[16];\n\tint wsbuflen;\n\tint wspushed;\n\tint wsbits;\n} wsrbuf_t;\n\n//'other proxy', these are mvd stream clients.\ntypedef struct oproxy_s {\n\tint authkey;\n\tunsigned int droptime;\n\n\tqboolean flushing;\n\tqboolean drop;\n\n\tsv_t *defaultstream;\n\tsv_t *stream;\n\n\tFILE *srcfile;\t//buffer is padded with data from this file when its empty\n\tFILE *file;\t\t//recording a demo (written to)\n\tSOCKET sock;\t//playing to a proxy\n\twsrbuf_t websocket;\n\n\tunsigned char inbuffer[MAX_PROXY_INBUFFER];\n\tunsigned int inbuffersize;\t//amount of data available.\n\n\tunsigned char buffer[MAX_PROXY_BUFFER];\n\tunsigned int buffersize;\t//use cyclic buffering.\n\tunsigned int bufferpos;\n\tstruct oproxy_s *next;\n} oproxy_t;\n\ntypedef struct tcpconnect_s\n{\n\tstruct tcpconnect_s *next;\n\tSOCKET sock;\n\twsrbuf_t websocket;\n\tnetadr_t peeraddr;\n\tchar *initialstreamname;\n\n\tunsigned char inbuffer[MAX_PROXY_INBUFFER];\n\tunsigned int inbuffersize;\t//amount of data available.\n\n\tunsigned char outbuffer[MAX_PROXY_INBUFFER];\n\tunsigned int outbuffersize;\t//amount of data available.\n} tcpconnect_t;\n\ntypedef struct {\n\tfloat origin[3];\n\tunsigned short soundindex;\n\tunsigned char volume;\n\tunsigned char attenuation;\n} staticsound_t;\n\ntypedef struct bsp_s bsp_t;\n\ntypedef struct {\n\tentity_state_t baseline;\n//\tentity_state_t current;\n//\tentity_state_t old;\n//\tunsigned int updatetime;\t//to stop lerping when it's an old entity (bodies, stationary grenades, ...)\n\n\tint leafcount;\n\tunsigned short leafs[MAX_ENTITY_LEAFS];\n} entity_t;\n\n#define MAX_ENTITY_FRAMES 64\ntypedef struct {\n\tint oldframe;\n\tint numents;\n\tint maxents;\n\tentity_state_t *ents;\t\t//dynamically allocated\n\tunsigned short *entnums;\t//dynamically allocated\n} frame_t;\n\ntypedef struct {\n\tunsigned char number;\n\tchar bits[6];\n} nail_t;\n\ntypedef struct {\n\tfloat pos[3];\n\tfloat angle[3];\n} intermission_t;\n\ntypedef enum {\n\tSRC_BAD,\n\tSRC_DEMO,\n\tSRC_DEMODIR,\n\tSRC_UDP,\n\tSRC_TCP\n} sourcetype_t;\n\ntypedef enum {\n\tERR_NONE,\t//stream is fine\n\tERR_PAUSED,\n\tERR_RECONNECT,\t//stream needs to reconnect\n\tERR_PERMANENT,\t//permanent error, transitioning to disabled next frame\n\tERR_DISABLED,\t//stream is disabled, can be set to reconnect by admin\n\tERR_DROP\t//stream _will_ be forgotten about next frame\n} errorstate_t;\n\nstruct sv_s {\t//details about a server connection (also known as stream)\n\tchar connectpassword[64];\t//password given to server\n\tnetadr_t serveraddress;\n\tnetchan_t netchan;\n\tqboolean serverquery;\n\n\tsourcetype_t sourcetype;\n\n\t//proxy chaining\n\tqboolean serverisproxy;\n\tqboolean proxyisselected;\n\tqboolean upstreamacceptschat;\n\tqboolean upstreamacceptsdownload;\n\t//\n\n\tqboolean parsingqtvheader;\n\n\tunsigned char upstreambuffer[2048];\n\tint upstreambuffersize;\n\n\tunsigned int parsetime;\n\tunsigned int parsespeed;\n\n\tFILE *downloadfile;\n\tchar downloadname[256];\n\n\tchar status[64];\n\n\tqboolean silentstream;\n\n\tqboolean usequakeworldprotocols;\n\tunsigned int pext1;\n\tunsigned int pext2;\n\tunsigned int pexte;\n\tint challenge;\n\tunsigned short qport;\n\tint isconnected;\n\tint clservercount;\n\tunsigned int nextsendpings;\n\n\tunsigned int timeout;\n\tunsigned int packetratelimiter;\n\n\tviewer_t *controller;\n\tint controllersquencebias;\n\n\tqboolean proxyplayer;\t//a player is actually playing on the proxy.\n\tusercmd_t proxyplayerucmds[3];\n\tint proxyplayerucmdnum;\n\tint proxyplayerbuttons;\n\tfloat proxyplayerangles[3];\n\tfloat proxyplayerimpulse;\n\n\tqboolean maysend;\n\n\tFILE *sourcefile;\n\tunsigned int filelength;\n\tSOCKET sourcesock;\n\tint last_random_number;\t// for demo directories randomizing stuff\n\n//\tSOCKET tcpsocket;\t//tcp + mvd protocol\n//\tint tcplistenportnum;\n\n\toproxy_t *proxies;\n\n\tqboolean parsingconnectiondata;\t//so reject any new connects for now\n\n\tunsigned int mapstarttime;\n\tunsigned int physicstime;\t//the last time all the ents moved.\n\tunsigned int simtime;\n\tunsigned int curtime;\n\tunsigned int oldpackettime;\n\tunsigned int nextpackettime;\n\tunsigned int nextconnectattempt;\n\n\n\n\terrorstate_t errored;\n\tenum autodisconnect_e {\n\t\tAD_NO,\n\t\tAD_WHENEMPTY,\n\t\tAD_REVERSECONNECT,\n\t\tAD_STATUSPOLL\n\t} autodisconnect;\n\tunsigned int numviewers;\n\n\tcluster_t *cluster;\n\tsv_t *next;\t//next proxy->server connection\n\n#ifdef COMMENTARY\n\t//audio stuff\n\tsoundcapt_t *comentrycapture;\n#endif\n\n\t//options:\n\tchar server[MAX_QPATH];\n\tint streamid;\n\n\tstruct mapstate_s\n\t{\n\t\t//this structure is freed+memset in QTV_CleanupMap\n\n\t\tbsp_t *bsp;\n\t\tint numinlines;\n\n\t\tnail_t nails[32];\n\t\tint nailcount;\n\n\t\tchar gamedir[MAX_QPATH];\n\t\tchar mapname[256];\t//world.message\n\t\tmovevars_t movevars;\n\t\tint cdtrack;\n\t\tentity_t entity[MAX_ENTITIES];\n\t\tframe_t frame[MAX_ENTITY_FRAMES];\n\t//\tint maxents;\n\t\tstaticsound_t staticsound[MAX_STATICSOUNDS];\n\t\tint staticsound_count;\n\t\tentity_state_t spawnstatic[MAX_STATICENTITIES];\n\t\tint spawnstatic_count;\n\t\tfilename_t lightstyle[MAX_LIGHTSTYLES];\n\n\t\tchar serverinfo[MAX_SERVERINFO_STRING];\n\t\tchar hostname[MAX_QPATH];\n\t\tplayerinfo_t players[MAX_CLIENTS];\n\n\t\tfilename_t modellist[MAX_MODELS];\n\t\tfilename_t soundlist[MAX_SOUNDS];\n\t\tint modelindex_spike;\t// qw is wierd.\n\t\tint modelindex_player;\t// qw is wierd.\n\n\t\tint trackplayer;\n\t\tint thisplayer;\n\t\tqboolean ispaused;\n\t} map;\n\n\tunsigned char buffer[MAX_PROXY_BUFFER];\t//this doesn't cycle.\n\tint buffersize;\t//it memmoves down\n\tint forwardpoint;\t//the point in the stream that we've forwarded up to.\n};\n\ntypedef struct {\n\tchar name[128];\n\tint size;\n\tint time, smalltime;\n} availdemo_t;\n\nenum\n{\n\tSG_IPV4,\n\tSG_IPV6,\n\tSG_UNIX,\n\tSOCKETGROUPS\n};\n\ntypedef struct turnclient_s turnclient_t;\nstruct cluster_s {\n\tSOCKET qwdsocket[SOCKETGROUPS];\t//udp + quakeworld protocols\n\tSOCKET tcpsocket[SOCKETGROUPS];\t//tcp listening socket (for mvd and listings and stuff)\n\ttcpconnect_t *tcpconnects;\t//'tcpconnect' qizmo-compatible quakeworld-over-tcp connection\n\n\tchar commandinput[512];\n\tint inputlength;\n\n\tunsigned int mastersendtime;\n\tunsigned int mastersequence;\n\tunsigned int curtime;\n\n#ifdef HAVE_EPOLL\n\tint epfd;\n#endif\n\tunsigned int numrelays;\n\tturnclient_t *turns;\n\tchar chalkey[64];\t\t//to identify the master properly. probably kinda pointless. base64 encoded.\n\tunsigned char turnkey[32];\t//raw key shared with broker to prove TURN identity was given by broker. NOTE: we are not verifying each, so we depend on clockskew to prevent any longterm abuse. there's no accounts anywhere though so anyone can get a key if they ask properly.\n\tqboolean turnenabled;\n\tunsigned short turn_minport, turn_maxport;\t//set to 0 to let the OS decide.\n\tchar *protocolname;\n\tint protocolver;\n\tunsigned char turn_ipv4[4];\n\tunsigned char turn_ipv6[16];\n\tunsigned int numpeers;\n\tstruct relaypeer_s *relaypeer;\n\tunsigned int relay_lastping;\n\tunsigned int relay_lastquery;\n\tqboolean relayenabled;\n\tqboolean pingtreeenabled;\n\n\tviewer_t *viewers;\n\tint numviewers;\n\tsv_t *servers;\n\tint numservers;\n\tint nextstreamid;\n\tint nextuserid;\n\n\tsv_t *viewserver;\n\n\t//options\n\tchar autojoinadr[128];\t//new clients automatically .join this server\n\tint qwlistenportnum;\n\tint tcplistenportnum;\n\tchar adminpassword[256];//password required for rcon etc\n\tchar qtvpassword[256];\t//password required to connect a proxy\n\tchar hostname[256];\n\tchar master[MAX_QPATH];\n\tchar demodir[MAX_QPATH];\n\tchar downloaddir[MAX_QPATH];\t//must be slash terminated, or empty.\n\tchar plugindatasource[256];\t//sued by the http server for use with npfte\n\tchar mapsource[256];\t//sued by the http server for use with npfte\n\tqboolean chokeonnotupdated;\n\tqboolean lateforward;\n\tqboolean notalking;\n\tqboolean nobsp;\n\tqboolean allownqclients;\t//nq clients require no challenge\n\tqboolean nouserconnects;\t//prohibit users from connecting to new streams.\n\tqboolean reverseallowed;\t//demos can be submitted from servers via 'qtvreverse' without needing to keep idle connections live.\n\tint anticheattime;\t//intial connection buffer delay (set high to block specing enemies)\n\tint tooslowdelay;\t//if stream ran out of data, stop parsing for this long\n\n\tint maxviewers;\n\n\tint numproxies;\n\tint maxproxies;\n\n\tqboolean wanttoexit;\n\n\toproxy_t *pendingproxies;\n\n\tavaildemo_t availdemos[2048];\n\tint availdemoscount;\n};\n\n#define MENU_NONE\t\t\t0\n#define\tMENU_MAIN\t\t\t1//MENU_SERVERS\n#define MENU_SERVERS\t\t2\n#define MENU_CLIENTS\t\t3\n#define MENU_ADMIN\t\t\t4\n#define MENU_ADMINSERVER\t5\n#define MENU_DEMOS\t\t\t6\n#define MENU_FORWARDING\t\t7\n#define MENU_SERVERBROWSER\t8\n#define MENU_HELP\t\t\t9\n\n#define\tMENU_DEFAULT\t\tMENU_MAIN\n\n\nenum {\n\tMENU_MAIN_STREAMS,\n\tMENU_MAIN_NEWSTREAM,\n\tMENU_MAIN_SERVERBROWSER,\n\tMENU_MAIN_PREVPROX,\n\tMENU_MAIN_HELP,\n\n\tMENU_MAIN_CLIENTLIST,\n\tMENU_MAIN_DEMOS,\n\tMENU_MAIN_ADMIN,\n\tMENU_MAIN_NEXTPROX,\n\n\tMENU_MAIN_ITEMCOUNT\n};\n\n\n\nunsigned char ReadByte(netmsg_t *b);\nunsigned short ReadShort(netmsg_t *b);\nunsigned short ReadBigShort(netmsg_t *b);\nunsigned int ReadLong(netmsg_t *b);\nunsigned int ReadBigLong(netmsg_t *b);\nfloat ReadFloat(netmsg_t *b);\nvoid ReadString(netmsg_t *b, char *string, int maxlen);\nfloat ReadCoord(netmsg_t *b, unsigned int pext);\nfloat ReadAngle(netmsg_t *b, unsigned int pext);\n\nunsigned int SwapLong(unsigned int val);\nunsigned int BigLong(unsigned int val);\n\n\n\n\n\n\n//flags for where a message can be sent, for easy broadcasting\n#define Q1 (NQ|QW)\n#define QW 1\n#define NQ 2\n#define CONNECTING 4\n#include \"cmd.h\"\n\n\nvoid InitNetMsg(netmsg_t *b, void *buffer, int bufferlength);\nunsigned char ReadByte(netmsg_t *b);\nunsigned short ReadShort(netmsg_t *b);\nunsigned int ReadLong(netmsg_t *b);\nfloat ReadFloat(netmsg_t *b);\nvoid ReadString(netmsg_t *b, char *string, int maxlen);\nvoid WriteByte(netmsg_t *b, unsigned char c);\nvoid WriteShort(netmsg_t *b, unsigned short l);\nvoid WriteBigShort(netmsg_t *b, unsigned short l);\nvoid WriteLong(netmsg_t *b, unsigned int l);\nvoid WriteBigLong(netmsg_t *b, unsigned int l);\nvoid WriteFloat(netmsg_t *b, float f);\nvoid WriteCoord(netmsg_t *b, float c, unsigned int pext);\nvoid WriteAngle(netmsg_t *b, float a, unsigned int pext);\nvoid WriteString2(netmsg_t *b, const char *str);\nvoid WriteString(netmsg_t *b, const char *str);\nvoid WriteData(netmsg_t *b, const void *data, int length);\n\nvoid Multicast(sv_t *tv, void *buffer, int length, int to, unsigned int playermask,int suitablefor);\nvoid Broadcast(cluster_t *cluster, void *buffer, int length, int suitablefor);\nvoid ParseMessage(sv_t *tv, void *buffer, int length, int to, int mask);\nvoid BuildServerData(sv_t *tv, netmsg_t *msg, int servercount, viewer_t *spectatorflag);\nvoid BuildNQServerData(sv_t *tv, netmsg_t *msg, qboolean mvd, int servercount);\nvoid QW_UpdateUDPStuff(cluster_t *qtv);\nvoid QW_TCPConnection(cluster_t *cluster, oproxy_t *sock, char *initialstringname/*strduped*/);\nunsigned int Sys_Milliseconds(void);\nvoid Prox_SendInitialEnts(sv_t *qtv, oproxy_t *prox, netmsg_t *msg);\nqboolean QTV_ConnectStream(sv_t *qtv, char *serverurl);\nvoid QTV_ShutdownStream(sv_t *qtv);\nqboolean\tNET_StringToAddr (char *s, netadr_t *sadr, int defaultport);\nvoid QTV_Printf(sv_t *qtv, char *format, ...) PRINTFWARNING(2);\nvoid QTV_UpdatedServerInfo(sv_t *tv);\nvoid QTV_CleanupMap(sv_t *qtv);\n\nvoid SendBufferToViewer(viewer_t *v, const char *buffer, int length, qboolean reliable);\nvoid QW_PrintfToViewer(viewer_t *v, char *format, ...) PRINTFWARNING(2);\nvoid QW_StuffcmdToViewer(viewer_t *v, char *format, ...) PRINTFWARNING(2);\nvoid QW_StreamPrint(cluster_t *cluster, sv_t *server, viewer_t *allbut, char *message);\nvoid QW_StreamStuffcmd(cluster_t *cluster, sv_t *server, char *fmt, ...) PRINTFWARNING(3);\nvoid QTV_SayCommand(cluster_t *cluster, sv_t *qtv, viewer_t *v, char *fullcommand);\t//execute a command from a view\nvoid QW_SetViewersServer(cluster_t *cluster, viewer_t *viewer, sv_t *sv);\nvoid QW_SetMenu(viewer_t *v, int menunum);\nvoid QW_SetCommentator(cluster_t *cluster, viewer_t *v, viewer_t *commentator);\nvoid QW_FreeViewer(cluster_t *cluster, viewer_t *viewer);\nvoid QW_ClearViewerState(viewer_t *viewer);\n\nvoid PM_PlayerMove (pmove_t *pmove);\n\nvoid Netchan_Setup (SOCKET sock, netchan_t *chan, netadr_t adr, int qport, qboolean isclient);\nvoid Netchan_OutOfBandPrint (cluster_t *cluster, netadr_t adr, char *format, ...) PRINTFWARNING(3);\n//int Netchan_IsLocal (netadr_t adr);\nvoid NET_InitUDPSocket(cluster_t *cluster, int port, int socketid);\nvoid NET_SendPacket(cluster_t *cluster, SOCKET sock, int length, void *data, netadr_t adr);\nSOCKET NET_ChooseSocket(SOCKET sock[SOCKETGROUPS], netadr_t *toadr, netadr_t in);\nqboolean Net_CompareAddress(netadr_t *s1, netadr_t *s2, int qp1, int qp2);\nqboolean Netchan_Process (netchan_t *chan, netmsg_t *msg);\nqboolean NQNetchan_Process(cluster_t *cluster, netchan_t *chan, netmsg_t *msg);\nvoid Netchan_Transmit (cluster_t *cluster, netchan_t *chan, int length, const void *data);\nvoid Netchan_OutOfBandSocket (cluster_t *cluster, SOCKET sock, netadr_t *adr, int length, void *data);\nvoid Netchan_OutOfBand(cluster_t *cluster, netadr_t adr, int length, void *data);\nqboolean Netchan_CanPacket (netchan_t *chan);\nint NET_WebSocketRecv(SOCKET sock, wsrbuf_t *ws, unsigned char *out, unsigned int outlen, int *wslen);\n\nint SendList(sv_t *qtv, int first, const filename_t *list, int svc, netmsg_t *msg);\nint Prespawn(sv_t *qtv, int curmsgsize, netmsg_t *msg, int bufnum, int thisplayer);\n\nbsp_t *BSP_LoadModel(cluster_t *cluster, char *gamedir, char *bspname);\nvoid BSP_Free(bsp_t *bsp);\nint BSP_LeafNum(bsp_t *bsp, float x, float y, float z);\nunsigned int BSP_Checksum(bsp_t *bsp);\nint BSP_SphereLeafNums(bsp_t *bsp, int maxleafs, unsigned short *list, float x, float y, float z, float radius);\nqboolean BSP_Visible(bsp_t *bsp, int leafcount, unsigned short *list);\nvoid BSP_SetupForPosition(bsp_t *bsp, float x, float y, float z);\nconst intermission_t *BSP_IntermissionSpot(bsp_t *bsp);\n\nunsigned short QCRC_Block (void *start, int count);\nunsigned short QCRC_Value(unsigned short crcvalue);\nvoid WriteDeltaUsercmd (netmsg_t *m, const usercmd_t *from, usercmd_t *move);\nvoid SendClientCommand(sv_t *qtv, char *fmt, ...) PRINTFWARNING(2);\nvoid QTV_Run(sv_t *qtv);\n\nchar *COM_ParseToken (char *data, char *out, int outsize, const char *punctuation);\nchar *Info_ValueForKey (char *s, const char *key, char *buffer, int buffersize);\nvoid Info_SetValueForStarKey (char *s, const char *key, const char *value, int maxsize);\nvoid ReadDeltaUsercmd (netmsg_t *m, const usercmd_t *from, usercmd_t *move);\nunsigned Com_BlockChecksum (void *buffer, int length);\nvoid Com_BlockFullChecksum (void *buffer, int len, unsigned char *outbuf);\nvoid Cluster_BuildAvailableDemoList(cluster_t *cluster);\n\nvoid Sys_Printf(cluster_t *cluster, char *fmt, ...) PRINTFWARNING(2);\n//void Sys_mkdir(char *path);\nvoid QTV_mkdir(char *path);\n\nvoid Net_ProxySend(cluster_t *cluster, oproxy_t *prox, void *buffer, int length);\noproxy_t *Net_FileProxy(sv_t *qtv, char *filename);\nsv_t *QTV_NewServerConnection(cluster_t *cluster, int streamid, char *server, char *password, qboolean force, enum autodisconnect_e autodisconnect, qboolean noduplicates, qboolean query);\nvoid Net_TCPListen(cluster_t *cluster, int port, int socketid);\nqboolean Net_StopFileProxy(sv_t *qtv);\n\n\nvoid SV_FindProxies(SOCKET sock, cluster_t *cluster, sv_t *defaultqtv);\nqboolean SV_ReadPendingProxy(cluster_t *cluster, oproxy_t *pend);\nvoid SV_ForwardStream(sv_t *qtv, void *buffer, int length);\nint SV_SayToUpstream(sv_t *qtv, char *message);\nvoid SV_SayToViewers(sv_t *qtv, char *message);\n\nunsigned char *FS_ReadFile(char *gamedir, char *filename, unsigned int *size);\n\nvoid ChooseFavoriteTrack(sv_t *tv);\n\nvoid DemoViewer_Update(sv_t *svtest);\nvoid Fwd_SayToDownstream(sv_t *qtv, char *message);\n\n\n//httpsv.c\nchar *HTTPSV_GetMethod(cluster_t *cluster, oproxy_t *pend);\t//if a websocket request, return value is the stream name\nvoid HTTPSV_PostMethod(cluster_t *cluster, oproxy_t *pend, char *postdata);\nvoid tobase64(unsigned char *out, int outlen, unsigned char *in, int inlen);\n\n//menu.c\nvoid Menu_Enter(cluster_t *cluster, viewer_t *viewer, int buttonnum);\nvoid Menu_Draw(cluster_t *cluster, viewer_t *viewer);\n\n//relay.c\nvoid TURN_CheckFDs(cluster_t *cluster);\nvoid TURN_AddFDs(cluster_t *cluster, fd_set *set, int *m);\nqboolean TURN_IsRequest(cluster_t *cluster, netmsg_t *m, netadr_t *from);\t//handles both TURN/STUN packets, and relays inbound qwfwd connections too.\nvoid Fwd_NewQWFwd(cluster_t *cluster, netadr_t *from, char *targ);\t\t\t//creates a new qwfwd context.\nvoid TURN_RelayStatus(cmdctxt_t *ctx);\nvoid Fwd_PingStatus(cluster_t *cluster, netadr_t *from, qboolean ext);\nvoid Fwd_ParseServerList(cluster_t *cluster, netmsg_t *m, int af);\nvoid Fwd_PingResponse(cluster_t *cluster, netadr_t *from);\n\n#ifdef __cplusplus\n}\n#endif\n\n"
  },
  {
    "path": "fteqtv/qw.c",
    "content": "/*\nCopyright (C) 1996-1997 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\nSee the included (GNU.txt) GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n*/\n\n\n#include \"qtv.h\"\n#include <string.h>\n#include <time.h>\n\nstatic const filename_t ConnectionlessModelList[] = {{\"\"}, {\"maps/start.bsp\"}, {\"progs/player.mdl\"}, {\"\"}};\nstatic const filename_t ConnectionlessSoundList[] = {{\"\"}, {\"\"}};\nconst entity_state_t nullentstate = {0};\nvoid SV_WriteDelta(int entnum, const entity_state_t *from, const entity_state_t *to, netmsg_t *msg, qboolean force, unsigned int pext);\n\nconst intermission_t nullstreamspot = {{544, 288, 64}, {0, 90, 0}};\n\nvoid QTV_Say(cluster_t *cluster, sv_t *qtv, viewer_t *v, char *message, qboolean noupwards);\n\nvoid QTV_DefaultMovevars(movevars_t *vars)\n{\n\tvars->gravity = 800;\n\tvars->maxspeed = 320;\n\tvars->spectatormaxspeed = 500;\n\tvars->accelerate = 10;\n\tvars->airaccelerate = 0.7f;\n\tvars->waterfriction = 4;\n\tvars->entgrav = 1;\n\tvars->stopspeed = 10;\n\tvars->wateraccelerate = 10;\n\tvars->friction = 4;\n}\n\n\nconst usercmd_t nullcmd = {0};\n\n#define\tCM_ANGLE1 \t(1<<0)\n#define\tCM_ANGLE3 \t(1<<1)\n#define\tCM_FORWARD\t(1<<2)\n#define\tCM_SIDE\t\t(1<<3)\n#define\tCM_UP\t\t(1<<4)\n#define\tCM_BUTTONS\t(1<<5)\n#define\tCM_IMPULSE\t(1<<6)\n#define\tCM_ANGLE2 \t(1<<7)\nvoid ReadDeltaUsercmd (netmsg_t *m, const usercmd_t *from, usercmd_t *move)\n{\n\tint bits;\n\n\tmemcpy (move, from, sizeof(*move));\n\n\tbits = ReadByte (m);\n\n// read current angles\n\tif (bits & CM_ANGLE1)\n\t\tmove->angles[0] = (ReadShort (m)/(float)0x10000)*360;\n\tif (bits & CM_ANGLE2)\n\t\tmove->angles[1] = (ReadShort (m)/(float)0x10000)*360;\n\tif (bits & CM_ANGLE3)\n\t\tmove->angles[2] = (ReadShort (m)/(float)0x10000)*360;\n\n// read movement\n\tif (bits & CM_FORWARD)\n\t\tmove->forwardmove = ReadShort(m);\n\tif (bits & CM_SIDE)\n\t\tmove->sidemove = ReadShort(m);\n\tif (bits & CM_UP)\n\t\tmove->upmove = ReadShort(m);\n\n// read buttons\n\tif (bits & CM_BUTTONS)\n\t\tmove->buttons = ReadByte (m);\n\n\tif (bits & CM_IMPULSE)\n\t\tmove->impulse = ReadByte (m);\n\n// read time to run command\n\tmove->msec = ReadByte (m);\t\t// always sent\n}\n\nvoid WriteDeltaUsercmd (netmsg_t *m, const usercmd_t *from, usercmd_t *move)\n{\n\tint bits = 0;\n\n\tif (move->angles[0] != from->angles[0])\n\t\tbits |= CM_ANGLE1;\n\tif (move->angles[1] != from->angles[1])\n\t\tbits |= CM_ANGLE2;\n\tif (move->angles[2] != from->angles[2])\n\t\tbits |= CM_ANGLE3;\n\n\tif (move->forwardmove != from->forwardmove)\n\t\tbits |= CM_FORWARD;\n\tif (move->sidemove != from->sidemove)\n\t\tbits |= CM_SIDE;\n\tif (move->upmove != from->upmove)\n\t\tbits |= CM_UP;\n\n\tif (move->buttons != from->buttons)\n\t\tbits |= CM_BUTTONS;\n\tif (move->impulse != from->impulse)\n\t\tbits |= CM_IMPULSE;\n\n\n\tWriteByte (m, bits);\n\n// read current angles\n\tif (bits & CM_ANGLE1)\n\t\tWriteShort (m, (move->angles[0]/360.0)*0x10000);\n\tif (bits & CM_ANGLE2)\n\t\tWriteShort (m, (move->angles[1]/360.0)*0x10000);\n\tif (bits & CM_ANGLE3)\n\t\tWriteShort (m, (move->angles[2]/360.0)*0x10000);\n\n// read movement\n\tif (bits & CM_FORWARD)\n\t\tWriteShort(m, move->forwardmove);\n\tif (bits & CM_SIDE)\n\t\tWriteShort(m, move->sidemove);\n\tif (bits & CM_UP)\n\t\tWriteShort(m, move->upmove);\n\n// read buttons\n\tif (bits & CM_BUTTONS)\n\t\tWriteByte (m, move->buttons);\n\n\tif (bits & CM_IMPULSE)\n\t\tWriteByte (m, move->impulse);\n\n// read time to run command\n\tWriteByte (m, move->msec);\t\t// always sent\n}\n\n\n\n\n\n\n\n\n\n\n\n\nvoid BuildServerData(sv_t *tv, netmsg_t *msg, int servercount, viewer_t *viewer)\n{\n\tmovevars_t movevars;\n\tWriteByte(msg, svc_serverdata);\n\tif (viewer)\n\t{\t//its for an actual viewer, tailor the extensions...\n\t\tif (viewer->pext1)\n\t\t{\n\t\t\tWriteLong(msg, PROTOCOL_VERSION_FTE);\n\t\t\tWriteLong(msg, viewer->pext1);\n\t\t}\n\t\tif (viewer->pext2)\n\t\t{\n\t\t\tWriteLong(msg, PROTOCOL_VERSION_FTE2);\n\t\t\tWriteLong(msg, viewer->pext2);\n\t\t}\n\t}\n\telse\n\t{\t//we're just forwarding, use the same extensions our source used.\n\t\tif (tv->pext1)\n\t\t{\n\t\t\tWriteLong(msg, PROTOCOL_VERSION_FTE);\n\t\t\tWriteLong(msg, tv->pext1);\n\t\t}\n\t\tif (tv->pext2)\n\t\t{\n\t\t\tWriteLong(msg, PROTOCOL_VERSION_FTE2);\n\t\t\tWriteLong(msg, tv->pext2);\n\t\t}\n\t\tif (tv->pexte)\n\t\t{\n\t\t\tWriteLong(msg, PROTOCOL_VERSION_EZQUAKE1);\n\t\t\tWriteLong(msg, tv->pexte);\n\t\t}\n\t}\n\tWriteLong(msg, PROTOCOL_VERSION);\n\tWriteLong(msg, servercount);\n\n\tif (!tv)\n\t{\n\t\t//dummy connection, for choosing a game to watch.\n\t\tWriteString(msg, \"qw\");\n\n\t\tif (!viewer)\n\t\t\tWriteFloat(msg, 0);\n\t\telse\n\t\t\tWriteByte(msg, (MAX_CLIENTS-1) | (128));\n\t\tWriteString(msg, \"FTEQTV Proxy\");\n\n\n\t\t// get the movevars\n\t\tQTV_DefaultMovevars(&movevars);\n\t\tWriteFloat(msg, movevars.gravity);\n\t\tWriteFloat(msg, movevars.stopspeed);\n\t\tWriteFloat(msg, movevars.maxspeed);\n\t\tWriteFloat(msg, movevars.spectatormaxspeed);\n\t\tWriteFloat(msg, movevars.accelerate);\n\t\tWriteFloat(msg, movevars.airaccelerate);\n\t\tWriteFloat(msg, movevars.wateraccelerate);\n\t\tWriteFloat(msg, movevars.friction);\n\t\tWriteFloat(msg, movevars.waterfriction);\n\t\tWriteFloat(msg, movevars.entgrav);\n\n\n\n\t\tWriteByte(msg, svc_stufftext);\n\t\tWriteString2(msg, \"fullserverinfo \\\"\");\n\t\tWriteString2(msg, \"\\\\*QTV\\\\\"QTV_VERSION_STRING);\n\t\tWriteString(msg, \"\\\"\\n\");\n\n\t}\n\telse\n\t{\n\t\tWriteString(msg, tv->map.gamedir);\n\n\t\tif (!viewer)\n\t\t\tWriteFloat(msg, 0);\n\t\telse\n\t\t{\n\t\t\tif (tv->controller == viewer)\n\t\t\t\tWriteByte(msg, viewer->thisplayer);\n\t\t\telse\n\t\t\t\tWriteByte(msg, viewer->thisplayer | 128);\n\t\t}\n\t\tWriteString(msg, tv->map.mapname);\n\n\n\t\t// get the movevars\n\t\tWriteFloat(msg, tv->map.movevars.gravity);\n\t\tWriteFloat(msg, tv->map.movevars.stopspeed);\n\t\tWriteFloat(msg, tv->map.movevars.maxspeed);\n\t\tWriteFloat(msg, tv->map.movevars.spectatormaxspeed);\n\t\tWriteFloat(msg, tv->map.movevars.accelerate);\n\t\tWriteFloat(msg, tv->map.movevars.airaccelerate);\n\t\tWriteFloat(msg, tv->map.movevars.wateraccelerate);\n\t\tWriteFloat(msg, tv->map.movevars.friction);\n\t\tWriteFloat(msg, tv->map.movevars.waterfriction);\n\t\tWriteFloat(msg, tv->map.movevars.entgrav);\n\n\n\n\t\tWriteByte(msg, svc_stufftext);\n\t\tWriteString2(msg, \"fullserverinfo \\\"\");\n\t\tWriteString2(msg, tv->map.serverinfo);\n\t\tWriteString(msg, \"\\\"\\n\");\n\t}\n}\nvoid BuildNQServerData(sv_t *tv, netmsg_t *msg, qboolean mvd, int playernum)\n{\n\tint i;\n\tWriteByte(msg, svc_serverdata);\n\tif (tv && tv->pext1 & PEXT_FLOATCOORDS)\n\t{\n\t\tWriteLong(msg, 999);\n\t\tWriteLong(msg, (1<<1)|(1<<4));\t//short angles, float coords, same as PEXT_FLOATCOORDS\n\t}\n\telse\n\t\tWriteLong(msg, PROTOCOL_VERSION_NQ);\n\tWriteByte(msg, 16);\t//MAX_CLIENTS\n\tWriteByte(msg, 1);\t//game type\n\n\tif (!tv || tv->parsingconnectiondata )\n\t{\n\t\t//dummy connection, for choosing a game to watch.\n\t\tWriteString(msg, \"FTEQTV Proxy\");\n\n\n\t\t//modellist\n\t\tfor (i = 1; *ConnectionlessModelList[i].name; i++)\n\t\t{\n\t\t\tWriteString(msg, ConnectionlessModelList[i].name);\n\t\t}\n\t\tWriteString(msg, \"\");\n\n\t\t//soundlist\n\t\tfor (i = 1; *ConnectionlessSoundList[i].name; i++)\n\t\t{\n\t\t\tWriteString(msg, ConnectionlessSoundList[i].name);\n\t\t}\n\t\tWriteString(msg, \"\");\n\n\t\tWriteByte(msg, svc_cdtrack);\n\t\tWriteByte(msg, 0);\t//two of them, yeah... weird, eh?\n\t\tWriteByte(msg, 0);\n\n\t\tWriteByte(msg, svc_nqsetview);\n\t\tWriteShort(msg, playernum+1);\n\n\t\tWriteByte(msg, svc_nqsignonnum);\n\t\tWriteByte(msg, 1);\n\t}\n\telse\n\t{\n\t\t//dummy connection, for choosing a game to watch.\n\t\tWriteString(msg, tv->map.mapname);\n\n\n\t\t//modellist\n\t\tfor (i = 1; *tv->map.modellist[i].name; i++)\n\t\t{\n\t\t\tWriteString(msg, tv->map.modellist[i].name);\n\t\t}\n\t\tWriteString(msg, \"\");\n\n\t\t//soundlist\n\t\tfor (i = 1; *tv->map.soundlist[i].name; i++)\n\t\t{\n\t\t\tWriteString(msg, tv->map.soundlist[i].name);\n\t\t}\n\t\tWriteString(msg, \"\");\n\n\t\tWriteByte(msg, svc_cdtrack);\n\t\tWriteByte(msg, tv->map.cdtrack);\t//two of them, yeah... weird, eh?\n\t\tWriteByte(msg, tv->map.cdtrack);\n\n\t\tWriteByte(msg, svc_nqsetview);\n\t\tWriteShort(msg, playernum+1);\n\n\t\tWriteByte(msg, svc_nqsignonnum);\n\t\tWriteByte(msg, 1);\n\t}\n}\n\n\nvoid QW_ClearViewerState(viewer_t *viewer)\n{\n\tmemset(viewer->currentstats, 0, sizeof(viewer->currentstats));\n}\n\nvoid SendServerData(sv_t *tv, viewer_t *viewer)\n{\n\tnetmsg_t msg;\n\tchar buffer[MAX_MSGLEN];\n\n\tInitNetMsg(&msg, buffer, viewer->netchan.maxreliablelen);\n\n\tif (tv)\n\t{\n\t\tviewer->pext1 = tv->pext1;\n\t\tviewer->pext2 = tv->pext2;\n\t}\n\telse\n\t\tviewer->pext1 = viewer->pext2 = 0;\n\n\tif (tv && (tv->controller == viewer || !tv->controller))\n\t\tviewer->thisplayer = tv->map.thisplayer;\n\telse\n\t\tviewer->thisplayer = viewer->netchan.isnqprotocol?15:MAX_CLIENTS-1;\n\tif (viewer->netchan.isnqprotocol)\n\t\tBuildNQServerData(tv, &msg, false, viewer->thisplayer);\n\telse\n\t\tBuildServerData(tv, &msg, viewer->servercount, viewer);\n\n\tSendBufferToViewer(viewer, msg.data, msg.cursize, true);\n\n\tviewer->thinksitsconnected = false;\n\tif (tv && (tv->controller == viewer) && !viewer->netchan.isnqprotocol)\n\t\tviewer->thinksitsconnected = true;\n\n\tQW_ClearViewerState(viewer);\n}\n\nvoid SendNQSpawnInfoToViewer(cluster_t *cluster, viewer_t *viewer, netmsg_t *msg)\n{\n\tchar buffer[64];\n\tint i;\n\tint colours;\n\tsv_t *tv = viewer->server;\n\tWriteByte(msg, svc_nqtime);\n\tWriteFloat(msg, (cluster->curtime - (tv?tv->mapstarttime:0))/1000.0f);\n\n\tif (tv)\n\t{\n\t\tfor (i=0; i<MAX_CLIENTS && i < 16; i++)\n\t\t{\n\t\t\tWriteByte (msg, svc_nqupdatename);\n\t\t\tWriteByte (msg, i);\n\t\t\tInfo_ValueForKey(tv->map.players[i].userinfo, \"name\", buffer, sizeof(buffer));\n\t\t\tWriteString (msg, buffer);\t//fixme\n\n\t\t\tWriteByte (msg, svc_updatefrags);\n\t\t\tWriteByte (msg, i);\n\t\t\tWriteShort (msg, tv->map.players[i].frags);\n\n\t\t\tInfo_ValueForKey(tv->map.players[i].userinfo, \"bottomcolor\", buffer, sizeof(buffer));\n\t\t\tcolours = atoi(buffer);\n\t\t\tInfo_ValueForKey(tv->map.players[i].userinfo, \"topcolor\", buffer, sizeof(buffer));\n\t\t\tcolours |= atoi(buffer)*16;\n\t\t\tWriteByte (msg, svc_nqupdatecolors);\n\t\t\tWriteByte (msg, i);\n\t\t\tWriteByte (msg, colours);\n\t\t}\n\t}\n\telse\n\t{\n\t\tfor (i=0; i < 16; i++)\n\t\t{\n\t\t\tWriteByte (msg, svc_nqupdatename);\n\t\t\tWriteByte (msg, i);\n\t\t\tWriteString (msg, \"\");\n\t\t\tWriteByte (msg, svc_updatefrags);\n\t\t\tWriteByte (msg, i);\n\t\t\tWriteShort (msg, 0);\n\t\t\tWriteByte (msg, svc_nqupdatecolors);\n\t\t\tWriteByte (msg, i);\n\t\t\tWriteByte (msg, 0);\n\t\t}\n\t}\n\n\tWriteByte(msg, svc_nqsignonnum);\n\tWriteByte(msg, 3);\n}\n\nint SendCurrentUserinfos(sv_t *tv, int cursize, netmsg_t *msg, int i, int thisplayer)\n{\n\tchar name[1024];\n\n\tif (i < 0)\n\t\treturn i;\n\tif (i >= MAX_CLIENTS)\n\t\treturn i;\n\n\tfor (; i < MAX_CLIENTS; i++)\n\t{\n\t\tif (i == thisplayer && (!tv || !(tv->controller || tv->proxyplayer)))\n\t\t{\n\t\t\tWriteByte(msg, svc_updateuserinfo);\n\t\t\tWriteByte(msg, i);\n\t\t\tWriteLong(msg, i+1);\n\t\t\tWriteString2(msg, \"\\\\*spectator\\\\1\\\\name\\\\\");\n\n\t\t\t// Print the number of people on QTV along with the hostname\n\t\t\tif (tv && tv->map.hostname[0])\n\t\t\t{\n\t\t\t\tif (tv->map.hostname[0])\n\t\t\t\t\tsnprintf(name, sizeof(name), \"[%d] %s\", tv->numviewers, tv->map.hostname);\n\t\t\t\telse\n\t\t\t\t\tsnprintf(name, sizeof(name), \"[%d] FTEQTV\", tv->numviewers);\n\t\t\t}\n\t\t\telse\n\t\t\t\tsnprintf(name, sizeof(name), \"FTEQTV\");\n\n\t\t\t/*\n\t\t\tif (tv)\n\t\t\t{\n\t\t\t\tchar tmp[MAX_QPATH];\n\t\t\t\tsnprintf(tmp\n\t\t\t\tstrlcat(name, itoa(tv->numviewers), sizeof(name));\n\t\t\t\t//snprintf(name, sizeof(name), \"%s %d\", name, tv->numviewers);\n\t\t\t}\n\t\t\t*/\n\t\t\tWriteString(msg, name);\n\n\t\t\tWriteByte(msg, svc_updatefrags);\n\t\t\tWriteByte(msg, i);\n\t\t\tWriteShort(msg, 9999);\n\n\t\t\tWriteByte(msg, svc_updateping);\n\t\t\tWriteByte(msg, i);\n\t\t\tWriteShort(msg, 0);\n\n\t\t\tWriteByte(msg, svc_updatepl);\n\t\t\tWriteByte(msg, i);\n\t\t\tWriteByte(msg, 0);\n\n\t\t\tcontinue;\n\t\t}\n\t\tif (!tv)\n\t\t\tcontinue;\n\t\tif (msg->cursize+cursize+strlen(tv->map.players[i].userinfo) > 768)\n\t\t{\n\t\t\treturn i;\n\t\t}\n\t\tWriteByte(msg, svc_updateuserinfo);\n\t\tWriteByte(msg, i);\n\t\tWriteLong(msg, i+1);\n\t\tWriteString(msg, tv->map.players[i].userinfo);\n\n\t\tWriteByte(msg, svc_updatefrags);\n\t\tWriteByte(msg, i);\n\t\tWriteShort(msg, tv->map.players[i].frags);\n\n\t\tWriteByte(msg, svc_updateping);\n\t\tWriteByte(msg, i);\n\t\tWriteShort(msg, tv->map.players[i].ping);\n\n\t\tWriteByte(msg, svc_updatepl);\n\t\tWriteByte(msg, i);\n\t\tWriteByte(msg, tv->map.players[i].packetloss);\n\t}\n\n\ti++;\n\n\treturn i;\n}\nvoid WriteEntityState(netmsg_t *msg, entity_state_t *es, unsigned int pext)\n{\n\tint i;\n\tWriteByte(msg, es->modelindex);\n\tWriteByte(msg, es->frame);\n\tWriteByte(msg, es->colormap);\n\tWriteByte(msg, es->skinnum);\n\tfor (i = 0; i < 3; i++)\n\t{\n\t\tWriteCoord(msg, es->origin[i], pext);\n\t\tWriteAngle(msg, es->angles[i], pext);\n\t}\n}\nint SendCurrentBaselines(sv_t *tv, int cursize, netmsg_t *msg, int maxbuffersize, int i)\n{\n\n\tif (i < 0 || i >= MAX_ENTITIES)\n\t\treturn i;\n\n\tfor (; i < MAX_ENTITIES; i++)\n\t{\n\t\tif (msg->cursize+cursize+16 > maxbuffersize)\n\t\t{\n\t\t\treturn i;\n\t\t}\n\n\t\tif (tv->map.entity[i].baseline.modelindex)\n\t\t{\n\t\t\tif (tv->pext1 & PEXT_SPAWNSTATIC2)\n\t\t\t{\n\t\t\t\tWriteByte(msg, svcfte_spawnbaseline2);\n\t\t\t\tSV_WriteDelta(i, &nullentstate, &tv->map.entity[i].baseline, msg, true, tv->pext1);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tWriteByte(msg, svc_spawnbaseline);\n\t\t\t\tWriteShort(msg, i);\n\t\t\t\tWriteEntityState(msg, &tv->map.entity[i].baseline, tv->pext1);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn i;\n}\nint SendCurrentLightmaps(sv_t *tv, int cursize, netmsg_t *msg, int maxbuffersize, int i)\n{\n\tif (i < 0 || i >= MAX_LIGHTSTYLES)\n\t\treturn i;\n\n\tfor (; i < MAX_LIGHTSTYLES; i++)\n\t{\n\t\tif (msg->cursize+cursize+strlen(tv->map.lightstyle[i].name) > maxbuffersize)\n\t\t{\n\t\t\treturn i;\n\t\t}\n\t\tWriteByte(msg, svc_lightstyle);\n\t\tWriteByte(msg, i);\n\t\tWriteString(msg, tv->map.lightstyle[i].name);\n\t}\n\treturn i;\n}\nint SendStaticSounds(sv_t *tv, int cursize, netmsg_t *msg, int maxbuffersize, int i)\n{\n\tif (i < 0 || i >= MAX_STATICSOUNDS)\n\t\treturn i;\n\n\tfor (; i < MAX_STATICSOUNDS; i++)\n\t{\n\t\tif (msg->cursize+cursize+16 > maxbuffersize)\n\t\t{\n\t\t\treturn i;\n\t\t}\n\t\tif (!tv->map.staticsound[i].soundindex)\n\t\t\tcontinue;\n\n\t\tWriteByte(msg, svc_spawnstaticsound);\n\t\tWriteCoord(msg, tv->map.staticsound[i].origin[0], tv->pext1);\n\t\tWriteCoord(msg, tv->map.staticsound[i].origin[1], tv->pext1);\n\t\tWriteCoord(msg, tv->map.staticsound[i].origin[2], tv->pext1);\n\t\tWriteByte(msg, tv->map.staticsound[i].soundindex);\n\t\tWriteByte(msg, tv->map.staticsound[i].volume);\n\t\tWriteByte(msg, tv->map.staticsound[i].attenuation);\n\t}\n\n\treturn i;\n}\nint SendStaticEntities(sv_t *tv, int cursize, netmsg_t *msg, int maxbuffersize, int i)\n{\n\tif (i < 0 || i >= MAX_STATICENTITIES)\n\t\treturn i;\n\n\tfor (; i < MAX_STATICENTITIES; i++)\n\t{\n\t\tif (msg->cursize+cursize+16 > maxbuffersize)\n\t\t{\n\t\t\treturn i;\n\t\t}\n\t\tif (!tv->map.spawnstatic[i].modelindex)\n\t\t\tcontinue;\n\n\t\tif (tv->pext1 & PEXT_SPAWNSTATIC2)\n\t\t{\n\t\t\tWriteByte(msg, svcfte_spawnstatic2);\n\t\t\tSV_WriteDelta(i, &nullentstate, &tv->map.spawnstatic[i], msg, true, tv->pext1);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tWriteByte(msg, svc_spawnstatic);\n\t\t\tWriteEntityState(msg, &tv->map.spawnstatic[i], tv->pext1);\n\t\t}\n\t}\n\n\treturn i;\n}\n\nint SendList(sv_t *qtv, int first, const filename_t *list, int svc, netmsg_t *msg)\n{\n\tint i;\n\n\tWriteByte(msg, svc);\n\tWriteByte(msg, first);\n\tfor (i = first+1; i < 256; i++)\n\t{\n//\t\tprintf(\"write %i: %s\\n\", i, list[i].name);\n\t\tWriteString(msg, list[i].name);\n\t\tif (!*list[i].name)\t//fixme: this probably needs testing for where we are close to the limit\n\t\t{\t//no more\n\t\t\tWriteByte(msg, 0);\n\t\t\treturn -1;\n\t\t}\n\n\t\tif (msg->cursize > 768)\n\t\t{\t//truncate\n\t\t\ti--;\n\t\t\tbreak;\n\t\t}\n\t}\n\tWriteByte(msg, 0);\n\tWriteByte(msg, i);\n\n\treturn i;\n}\n\nvoid QW_StreamPrint(cluster_t *cluster, sv_t *server, viewer_t *allbut, char *message)\n{\n\tviewer_t *v;\n\n\tfor (v = cluster->viewers; v; v = v->next)\n\t{\n\t\tif (v->server == server)\n\t\t{\n\t\t\tif (v == allbut)\n\t\t\t\tcontinue;\n\t\t\tQW_PrintfToViewer(v, \"%s\", message);\n\t\t}\n\t}\n}\n\n\nvoid QW_StreamStuffcmd(cluster_t *cluster, sv_t *server, char *fmt, ...)\n{\n\tviewer_t *v;\n\tva_list\t\targptr;\n\tchar buf[1024];\n\tchar cmd[512];\n\n\tnetmsg_t msg;\n\n\tva_start (argptr, fmt);\n\tvsnprintf (cmd, sizeof(cmd), fmt, argptr);\n\tva_end (argptr);\n\n\tInitNetMsg(&msg, buf, sizeof(buf));\n\tWriteByte(&msg, svc_stufftext);\n\tWriteString(&msg, cmd);\n\n\n\tfor (v = cluster->viewers; v; v = v->next)\n\t{\n\t\tif (v->server == server)\n\t\t{\n\t\t\tSendBufferToViewer(v, msg.data, msg.cursize, true);\n\t\t}\n\t}\n}\n\n\n\nvoid QW_SetViewersServer(cluster_t *cluster, viewer_t *viewer, sv_t *sv)\n{\n\tchar buffer[1024];\n\tsv_t *oldserver;\n\toldserver = viewer->server;\n\tif (viewer->server)\n\t\tviewer->server->numviewers--;\n\tviewer->server = sv;\n\tif (viewer->server)\n\t\tviewer->server->numviewers++;\n\tif (!sv || !sv->parsingconnectiondata)\n\t{\n\t\tif (sv != oldserver)\n\t\t\tQW_StuffcmdToViewer(viewer, \"cmd new\\n\");\n\t\tviewer->thinksitsconnected = false;\n\t}\n\tviewer->servercount++;\n\tviewer->origin[0] = 0;\n\tviewer->origin[1] = 0;\n\tviewer->origin[2] = 0;\n\n\tif (sv != oldserver)\n\t{\n\t\tif (sv && oldserver)\n\t\t{\n\t\t\tsnprintf(buffer, sizeof(buffer), \"%cQTV%c%s leaves to watch %s (%i)\\n\", 91+128, 93+128, viewer->name, *sv->map.hostname?sv->map.hostname:sv->server, sv->streamid);\n\t\t\tQW_StreamPrint(cluster, oldserver, viewer, buffer);\n\t\t}\n\t\tsnprintf(buffer, sizeof(buffer), \"%cQTV%c%s joins the stream\\n\", 91+128, 93+128, viewer->name);\n\t\tQW_StreamPrint(cluster, sv, viewer, buffer);\n\t}\n}\n\n//fixme: will these want to have state?..\nint NewChallenge(cluster_t *cluster, netadr_t *addr)\n{\n\tunsigned int r = 0, l;\n\tunsigned char *digest;\n\tvoid *ctx;\n\thashfunc_t *func = &hash_sha1;\n\tstatic time_t t;\n\n\t//reminder: Challenges exist so clients can't spoof their source address and waste our ram without us being able to ban them without banning everyone.\n\tsize_t sz = 0;\n\tif (((struct sockaddr*)addr->sockaddr)->sa_family == AF_INET)\n\t\tsz = sizeof(struct sockaddr_in);\n\telse if (((struct sockaddr*)addr->sockaddr)->sa_family == AF_INET6)\n\t\tsz = sizeof(struct sockaddr_in6);\n\t//else error\n\n\tctx = alloca(func->contextsize);\n\tfunc->init(ctx);\n\tif (!t)\t//must be constant, so only do this if its still 0.\n\t\tt = time(NULL);\n\tfunc->process(ctx, addr, sz);\t\t//hash their address primarily.\n\tfunc->process(ctx, cluster->turnkey, sizeof(cluster->turnkey));\t//might not be set...\n\tfunc->process(ctx, &t, sizeof(t));\t//extra privacy, sizeof doesn't matter as its only our process that cares\n\t//func->process(ctx, cluster, sizeof(cluster));\t//a random pointer too, because zomgwtf\n\n\tdigest = alloca(func->digestsize);\n\tfunc->terminate(digest, ctx);\n\tfor (l = 0; l < func->digestsize; l++)\n\t\tr ^= digest[l]<<((l%sizeof(r))*8);\n\treturn r;\n}\nqboolean ChallengePasses(cluster_t *cluster, netadr_t *addr, int challenge)\n{\n\tif (challenge == NewChallenge(cluster, addr))\n\t\treturn true;\n\treturn false;\n}\n\nvoid NewClient(cluster_t *cluster, viewer_t *viewer)\n{\n\tsv_t *initialserver;\n\tinitialserver = NULL;\n\n\tif (viewer->netchan.remote_address.tcpcon)\n\t{\n\t\ttcpconnect_t *dest;\n\t\tfor (dest = cluster->tcpconnects; dest; dest = dest->next)\n\t\t{\n\t\t\tif (dest == viewer->netchan.remote_address.tcpcon)\n\t\t\t\tbreak;\n\t\t}\n\n\t\tif (*dest->initialstreamname)\n\t\t{\n\t\t\tinitialserver = QTV_NewServerConnection(cluster, 0, dest->initialstreamname, \"\", false, AD_WHENEMPTY, true, false);\n\t\t\tif (initialserver && initialserver->sourcetype == SRC_UDP)\n\t\t\t\tinitialserver->controller = viewer;\n\t\t}\n\t}\n\n\tif (initialserver)\n\t\t;\t//already picked it via websocket resources.\n\telse if (*cluster->autojoinadr)\n\t{\n\t\tinitialserver = QTV_NewServerConnection(cluster, 0, cluster->autojoinadr, \"\", false, AD_WHENEMPTY, true, false);\n\t\tif (initialserver && initialserver->sourcetype == SRC_UDP)\n\t\t\tinitialserver->controller = viewer;\n\t}\n\telse if (cluster->nouserconnects && cluster->numservers == 1)\n\t{\n\t\tinitialserver = cluster->servers;\n\t\tif (!initialserver->map.modellist[1].name[0])\n\t\t\tinitialserver = NULL;\t//damn, that server isn't ready\n\t}\n\n\tQW_SetViewersServer(cluster, viewer, initialserver);\n\n\tviewer->userid = ++cluster->nextuserid;\n\tviewer->timeout = cluster->curtime + 15*1000;\n\tviewer->trackplayer = -1;\n\n\tviewer->menunum = -1;\n\tQW_SetMenu(viewer, MENU_NONE);\n\n\n#ifndef LIBQTV\n\tQW_PrintfToViewer(viewer, \"Welcome to FTEQTV %s\\n\", QTV_VERSION_STRING);\n\tQW_StuffcmdToViewer(viewer, \"alias admin \\\"cmd admin\\\"\\n\");\n\n\t\tQW_StuffcmdToViewer(viewer, \"alias \\\"proxy:up\\\" \\\"say proxy:menu up\\\"\\n\");\n\t\tQW_StuffcmdToViewer(viewer, \"alias \\\"proxy:down\\\" \\\"say proxy:menu down\\\"\\n\");\n\t\tQW_StuffcmdToViewer(viewer, \"alias \\\"proxy:right\\\" \\\"say proxy:menu right\\\"\\n\");\n\t\tQW_StuffcmdToViewer(viewer, \"alias \\\"proxy:left\\\" \\\"say proxy:menu left\\\"\\n\");\n\n\t\tQW_StuffcmdToViewer(viewer, \"alias \\\"proxy:select\\\" \\\"say proxy:menu select\\\"\\n\");\n\n\t\tQW_StuffcmdToViewer(viewer, \"alias \\\"proxy:home\\\" \\\"say proxy:menu home\\\"\\n\");\n\t\tQW_StuffcmdToViewer(viewer, \"alias \\\"proxy:end\\\" \\\"say proxy:menu end\\\"\\n\");\n\t\tQW_StuffcmdToViewer(viewer, \"alias \\\"proxy:menu\\\" \\\"say proxy:menu\\\"\\n\");\n\t\tQW_StuffcmdToViewer(viewer, \"alias \\\"proxy:backspace\\\" \\\"say proxy:menu backspace\\\"\\n\");\n\n\t\tQW_StuffcmdToViewer(viewer, \"alias \\\".help\\\" \\\"say .help\\\"\\n\");\n\t\tQW_StuffcmdToViewer(viewer, \"alias \\\".disconnect\\\" \\\"say .disconnect\\\"\\n\");\n\t\tQW_StuffcmdToViewer(viewer, \"alias \\\".menu\\\" \\\"say .menu\\\"\\n\");\n\t\tQW_StuffcmdToViewer(viewer, \"alias \\\".admin\\\" \\\"say .admin\\\"\\n\");\n\t\tQW_StuffcmdToViewer(viewer, \"alias \\\".reset\\\" \\\"say .reset\\\"\\n\");\n\t\tQW_StuffcmdToViewer(viewer, \"alias \\\".clients\\\" \\\"say .clients\\\"\\n\");\n//\t\tQW_StuffcmdToViewer(viewer, \"alias \\\".qtv\\\" \\\"say .qtv\\\"\\n\");\n//\t\tQW_StuffcmdToViewer(viewer, \"alias \\\".join\\\" \\\"say .join\\\"\\n\");\n//\t\tQW_StuffcmdToViewer(viewer, \"alias \\\".observe\\\" \\\"say .observe\\\"\\n\");\n\n\tQW_PrintfToViewer(viewer, \"Type admin for the admin menu\\n\");\n#endif\n}\n\nvoid ParseUserInfo(cluster_t *cluster, viewer_t *viewer)\n{\n\tchar buf[1024];\n\tfloat rate;\n\tchar temp[64];\n\n\tviewer->isproxy = false;\n\n\tInfo_ValueForKey(viewer->userinfo, \"*qtv\", temp, sizeof(temp));\n\tif (*temp)\n\t\tviewer->isproxy = true;\n\tInfo_ValueForKey(viewer->userinfo, \"Qizmo\", temp, sizeof(temp));\n\tif (*temp)\n\t\tviewer->isproxy = true;\n\n\tInfo_ValueForKey(viewer->userinfo, \"name\", temp, sizeof(temp));\n\n\tif (!*temp)\n\t\tstrcpy(temp, \"unnamed\");\n\tif (!*viewer->name)\n\t\tSys_Printf(cluster, \"Viewer %s connected\\n\", temp);\n\n\tif (strcmp(viewer->name, temp))\n\t{\n\t\tif (*viewer->name)\n\t\t{\n\t\t\tsnprintf(buf, sizeof(buf), \"%cQTV%c%s changed name to %cQTV%c%s\\n\",\n\t\t\t\t\t91+128, 93+128, viewer->name,\n\t\t\t\t\t91+128, 93+128, temp\n\t\t\t\t\t);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tsnprintf(buf, sizeof(buf), \"%cQTV%c%s joins the stream\\n\",\n\t\t\t\t\t91+128, 93+128, temp\n\t\t\t\t\t);\n\n\t\t}\n\n\t\tif (!viewer->server || viewer->server->controller != viewer)\n\t\t\tQW_StreamPrint(cluster, viewer->server, NULL, buf);\n\t}\n\n\tstrlcpy(viewer->name, temp, sizeof(viewer->name));\n\n\tInfo_ValueForKey(viewer->userinfo, \"rate\", temp, sizeof(temp));\n\trate = atof(temp);\n\tif (!rate)\n\t\trate = 2500;\n\tif (rate < 250)\n\t\trate = 250;\n\tif (rate > 10000)\n\t\trate = 10000;\n\tviewer->netchan.rate = 1000.0f / rate;\n}\n\nvoid NewNQClient(cluster_t *cluster, netadr_t *addr)\n{\n//\tsv_t *initialserver;\n\tint header;\n\tint len;\n\tint i;\n\tunsigned char buffer[64];\n\tviewer_t *viewer = NULL;\n\n\n\tif (cluster->numviewers >= cluster->maxviewers && cluster->maxviewers)\n\t{\n\t\tbuffer[4] = CCREP_REJECT;\n\t\tstrcpy((char*)buffer+5, \"Sorry, proxy is full.\\n\");\n\t\tlen = strlen((char*)buffer+5)+5;\n\t}\n/*\telse\n\t{\n\t\tbuffer[4] = CCREP_REJECT;\n\t\tstrcpy((char*)buffer+5, \"NQ not supported yet\\n\");\n\t\tlen = strlen((char*)buffer+5)+5;\n\t}*/\n\telse if (!(viewer = malloc(sizeof(viewer_t))))\n\t{\n\t\tbuffer[4] = CCREP_REJECT;\n\t\tstrcpy((char*)buffer+5, \"Out of memory\\n\");\n\t\tlen = strlen((char*)buffer+5)+5;\n\t}\n\telse\n\t{\n\t\tbuffer[4] = CCREP_ACCEPT;\n\t\tbuffer[5] = (cluster->qwlistenportnum&0x00ff)>>0;\n\t\tbuffer[6] = (cluster->qwlistenportnum&0xff00)>>8;\n\t\tbuffer[7] = 0;\n\t\tbuffer[8] = 0;\n\t\tlen = 4+1+4;\n\t}\n\n\t*(int*)buffer = NETFLAG_CTL | len;\n\theader = (buffer[0]<<24) + (buffer[1]<<16) + (buffer[2]<<8) + buffer[3];\n\t*(int*)buffer = header;\n\n\tNET_SendPacket (cluster, NET_ChooseSocket(cluster->qwdsocket, addr, *addr), len, buffer, *addr);\n\n\tif (!viewer)\n\t\treturn;\n\n\n\tmemset(viewer, 0, sizeof(*viewer));\n\n\n\tNetchan_Setup (NET_ChooseSocket(cluster->qwdsocket, addr, *addr), &viewer->netchan, *addr, 0, false);\n\tviewer->netchan.isnqprotocol = true;\n\tviewer->netchan.maxdatagramlen = MAX_NQDATAGRAM;\n\tviewer->netchan.maxreliablelen = MAX_NQMSGLEN;\n\n\tviewer->firstconnect = true;\n\n\tviewer->next = cluster->viewers;\n\tcluster->viewers = viewer;\n\tfor (i = 0; i < ENTITY_FRAMES; i++)\n\t\tviewer->delta_frames[i] = -1;\n\n\tcluster->numviewers++;\n\n\tsprintf(viewer->userinfo, \"\\\\name\\\\%s\", \"unnamed\");\n\n\tParseUserInfo(cluster, viewer);\n\n\tNewClient(cluster, viewer);\n\n\tif (!viewer->server)\n\t\tQW_StuffcmdToViewer(viewer, \"cmd new\\n\");\n}\n\nvoid NewQWClient(cluster_t *cluster, netadr_t *addr, char *connectmessage)\n{\n//\tsv_t *initialserver;\n\tviewer_t *viewer;\n\n\tchar qport[32];\n\tchar challenge[32];\n\tchar infostring[1024];\n\tchar prx[256];\n\tint i;\n\n\tconnectmessage+=11;\n\n\tconnectmessage = COM_ParseToken(connectmessage, qport, sizeof(qport), \"\");\n\tconnectmessage = COM_ParseToken(connectmessage, challenge, sizeof(challenge), \"\");\n\tconnectmessage = COM_ParseToken(connectmessage, infostring, sizeof(infostring), \"\");\n\n\tif (!ChallengePasses(cluster, addr, atoi(challenge)))\n\t{\n\t\tNetchan_OutOfBandPrint(cluster, *addr, \"n\" \"Bad challenge\");\n\t\treturn;\n\t}\n\n\tInfo_ValueForKey(infostring, \"prx\", prx,sizeof(prx));\n\tif (*prx)\n\t{\n\t\tFwd_NewQWFwd(cluster, addr, prx);\n\t\treturn;\n\t}\n\n\n\tviewer = malloc(sizeof(viewer_t));\n\tif (!viewer)\n\t{\n\t\tNetchan_OutOfBandPrint(cluster, *addr, \"n\" \"Out of memory\");\n\t\treturn;\n\t}\n\tmemset(viewer, 0, sizeof(viewer_t));\n\n\tNetchan_Setup (NET_ChooseSocket(cluster->qwdsocket, addr, *addr), &viewer->netchan, *addr, atoi(qport), false);\n\tviewer->netchan.message.maxsize = MAX_QWMSGLEN;\n\tviewer->netchan.maxdatagramlen = MAX_QWMSGLEN;\n\tviewer->netchan.maxreliablelen = MAX_QWMSGLEN;\n\n\tviewer->firstconnect = true;\n\n\tviewer->next = cluster->viewers;\n\tcluster->viewers = viewer;\n\tfor (i = 0; i < ENTITY_FRAMES; i++)\n\t\tviewer->delta_frames[i] = -1;\n\n\tcluster->numviewers++;\n\n\tstrlcpy(viewer->userinfo, infostring, sizeof(viewer->userinfo));\n\tParseUserInfo(cluster, viewer);\n\n\tNetchan_OutOfBandPrint(cluster, *addr, \"j\");\n\n\tNewClient(cluster, viewer);\n}\n\nvoid QW_SetMenu(viewer_t *v, int menunum)\n{\n\tif ((v->menunum==MENU_NONE) != (menunum==MENU_NONE))\n\t{\n\t\tif (v->isproxy)\n\t\t{\n\t\t\tif (menunum != MENU_NONE)\n\t\t\t\tQW_StuffcmdToViewer(v, \"//set prox_inmenu 1\\n\");\n\t\t\telse\n\t\t\t\tQW_StuffcmdToViewer(v, \"//set prox_inmenu 0\\n\");\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (menunum != MENU_NONE)\n\t\t\t{\n\t\t\t\tQW_StuffcmdToViewer(v, \"//set prox_inmenu 1\\n\");\n\n\t\t\t\tQW_StuffcmdToViewer(v, \"alias \\\"+proxjump\\\" \\\"say proxy:menu enter\\\"\\n\");\n\t\t\t\tQW_StuffcmdToViewer(v, \"alias \\\"+proxfwd\\\" \\\"say proxy:menu up\\\"\\n\");\n\t\t\t\tQW_StuffcmdToViewer(v, \"alias \\\"+proxback\\\" \\\"say proxy:menu down\\\"\\n\");\n\t\t\t\tQW_StuffcmdToViewer(v, \"alias \\\"+proxleft\\\" \\\"say proxy:menu left\\\"\\n\");\n\t\t\t\tQW_StuffcmdToViewer(v, \"alias \\\"+proxright\\\" \\\"say proxy:menu right\\\"\\n\");\n\n\t\t\t\tQW_StuffcmdToViewer(v, \"alias \\\"-proxjump\\\" \\\" \\\"\\n\");\n\t\t\t\tQW_StuffcmdToViewer(v, \"alias \\\"-proxfwd\\\" \\\" \\\"\\n\");\n\t\t\t\tQW_StuffcmdToViewer(v, \"alias \\\"-proxback\\\" \\\" \\\"\\n\");\n\t\t\t\tQW_StuffcmdToViewer(v, \"alias \\\"-proxleft\\\" \\\" \\\"\\n\");\n\t\t\t\tQW_StuffcmdToViewer(v, \"alias \\\"-proxright\\\" \\\" \\\"\\n\");\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tQW_StuffcmdToViewer(v, \"//set prox_inmenu 0\\n\");\n\n\t\t\t\tQW_StuffcmdToViewer(v, \"alias \\\"+proxjump\\\" \\\"+jump\\\"\\n\");\n\t\t\t\tQW_StuffcmdToViewer(v, \"alias \\\"+proxfwd\\\" \\\"+forward\\\"\\n\");\n\t\t\t\tQW_StuffcmdToViewer(v, \"alias \\\"+proxback\\\" \\\"+back\\\"\\n\");\n\t\t\t\tQW_StuffcmdToViewer(v, \"alias \\\"+proxleft\\\" \\\"+moveleft\\\"\\n\");\n\t\t\t\tQW_StuffcmdToViewer(v, \"alias \\\"+proxright\\\" \\\"+moveright\\\"\\n\");\n\n\t\t\t\tQW_StuffcmdToViewer(v, \"alias \\\"-proxjump\\\" \\\"-jump\\\"\\n\");\n\t\t\t\tQW_StuffcmdToViewer(v, \"alias \\\"-proxfwd\\\" \\\"-forward\\\"\\n\");\n\t\t\t\tQW_StuffcmdToViewer(v, \"alias \\\"-proxback\\\" \\\"-back\\\"\\n\");\n\t\t\t\tQW_StuffcmdToViewer(v, \"alias \\\"-proxleft\\\" \\\"-moveleft\\\"\\n\");\n\t\t\t\tQW_StuffcmdToViewer(v, \"alias \\\"-proxright\\\" \\\"-moveright\\\"\\n\");\n\t\t\t}\n\t\t\tQW_StuffcmdToViewer(v, \"-forward\\n\");\n\t\t\tQW_StuffcmdToViewer(v, \"-back\\n\");\n\t\t\tQW_StuffcmdToViewer(v, \"-moveleft\\n\");\n\t\t\tQW_StuffcmdToViewer(v, \"-moveright\\n\");\n\t\t}\n\t}\n\n\tif (v->server && v->server->controller == v)\n\t{\n\t\tif (menunum==MENU_NONE || menunum==MENU_FORWARDING)\n\t\t\tv->server->proxyisselected = false;\n\t\telse\n\t\t\tv->server->proxyisselected = true;\n\t}\n\n\tv->menunum = menunum;\n\tv->menuop = 0;\n\n\tv->menuspamtime = 0;\n}\n\nvoid QTV_Rcon(cluster_t *cluster, char *message, netadr_t *from)\n{\n\tchar buffer[8192];\n\n\tchar *command;\n\tint passlen;\n\n\tif (!*cluster->adminpassword)\n\t{\n\t\tNetchan_OutOfBandPrint(cluster, *from, \"n\" \"Bad rcon_password.\\n\");\n\t\treturn;\n\t}\n\n\twhile(*message > '\\0' && *message <= ' ')\n\t\tmessage++;\n\n\tcommand = strchr(message, ' ');\n\tpasslen = command-message;\n\tif (passlen != strlen(cluster->adminpassword) || strncmp(message, cluster->adminpassword, passlen))\n\t{\n\t\tNetchan_OutOfBandPrint(cluster, *from, \"n\" \"Bad rcon_password.\\n\");\n\t\treturn;\n\t}\n\n\tNetchan_OutOfBandPrint(cluster, *from, \"n%s\", Rcon_Command(cluster, NULL, command, buffer, sizeof(buffer), false));\n}\n\nvoid QTV_Status(cluster_t *cluster, netadr_t *from)\n{\n\tint i;\n\tchar buffer[8192];\n\tsv_t *sv;\n\n\tnetmsg_t msg;\n\tchar elem[256];\n\tInitNetMsg(&msg, buffer, sizeof(buffer));\n\tWriteLong(&msg, -1);\n\tWriteByte(&msg, 'n');\n\n\tWriteString2(&msg, \"\\\\*QTV\\\\\");\n\tWriteString2(&msg, QTV_VERSION_STRING);\n\n\tif (cluster->numservers==1)\n\t{\t//show this server's info\n\t\tsv = cluster->servers;\n\n\t\tWriteString2(&msg, sv->map.serverinfo);\n\t\tWriteString2(&msg, \"\\n\");\n\n\t\tfor (i = 0;i < MAX_CLIENTS; i++)\n\t\t{\n\t\t\tif (i == sv->map.thisplayer)\n\t\t\t\tcontinue;\n\t\t\tif (!sv->map.players[i].active)\n\t\t\t\tcontinue;\n\t\t\t//userid\n\t\t\tsprintf(elem, \"%i\", i);\n\t\t\tWriteString2(&msg, elem);\n\t\t\tWriteString2(&msg, \" \");\n\n\t\t\t//frags\n\t\t\tsprintf(elem, \"%i\", sv->map.players[i].frags);\n\t\t\tWriteString2(&msg, elem);\n\t\t\tWriteString2(&msg, \" \");\n\n\t\t\t//time (minuites)\n\t\t\tsprintf(elem, \"%i\", 0);\n\t\t\tWriteString2(&msg, elem);\n\t\t\tWriteString2(&msg, \" \");\n\n\t\t\t//ping\n\t\t\tsprintf(elem, \"%i\", sv->map.players[i].ping);\n\t\t\tWriteString2(&msg, elem);\n\t\t\tWriteString2(&msg, \" \");\n\n\t\t\t//name\n\t\t\tInfo_ValueForKey(sv->map.players[i].userinfo, \"name\", elem, sizeof(elem));\n\t\t\tWriteString2(&msg, \"\\\"\");\n\t\t\tWriteString2(&msg, elem);\n\t\t\tWriteString2(&msg, \"\\\" \");\n\n\t\t\t//skin\n\t\t\tInfo_ValueForKey(sv->map.players[i].userinfo, \"skin\", elem, sizeof(elem));\n\t\t\tWriteString2(&msg, \"\\\"\");\n\t\t\tWriteString2(&msg, elem);\n\t\t\tWriteString2(&msg, \"\\\" \");\n\t\t\tWriteString2(&msg, \" \");\n\n\t\t\t//tc\n\t\t\tInfo_ValueForKey(sv->map.players[i].userinfo, \"topcolor\", elem, sizeof(elem));\n\t\t\tWriteString2(&msg, elem);\n\t\t\tWriteString2(&msg, \" \");\n\n\t\t\t//bc\n\t\t\tInfo_ValueForKey(sv->map.players[i].userinfo, \"bottomcolor\", elem, sizeof(elem));\n\t\t\tWriteString2(&msg, elem);\n\t\t\tWriteString2(&msg, \" \");\n\n\t\t\tWriteString2(&msg, \"\\n\");\n\t\t}\n\t}\n\telse\n\t{\n\t\tWriteString2(&msg, \"\\\\hostname\\\\\");\n\t\tWriteString2(&msg, cluster->hostname);\n\n\t\tfor (sv = cluster->servers, i = 0; sv; sv = sv->next, i++)\n\t\t{\n\t\t\tsprintf(elem, \"\\\\%i\\\\\", sv->streamid);\n\t\t\tWriteString2(&msg, elem);\n\t\t\tWriteString2(&msg, sv->server);\n//\t\t\tsprintf(elem, \" (%s)\", sv->serveraddress);\n//\t\t\tWriteString2(&msg, elem);\n\t\t}\n\n\t\tWriteString2(&msg, \"\\n\");\n\t}\n\n\tWriteByte(&msg, 0);\n\tNET_SendPacket(cluster, NET_ChooseSocket(cluster->qwdsocket, from, *from), msg.cursize, msg.data, *from);\n}\nstatic void QTV_GetInfo(cluster_t *cluster, netadr_t *from, char *args)\n{\n\t//ftemaster support\n\tchar challenge[256], tmp[64];\n\tchar protocolname[MAX_QPATH];\n\tchar buffer[8192];\n\tnetmsg_t msg;\n\tqboolean authed = false;\n\tInitNetMsg(&msg, buffer, sizeof(buffer));\n\n\targs = COM_ParseToken(args, challenge, sizeof(challenge), \"\");\n\twhile((args = COM_ParseToken(args, tmp, sizeof(tmp), \"\")))\n\t{\n\t\tif (!strncmp(tmp, \"c=\",2) && !strcmp(tmp+2, cluster->chalkey))\n\t\t\tauthed = true;\t//they're able to read our outgoing packets. assume not intercepted (at least blocks spoofed packets). should really use (d)tls. this is more to protect our resources than anything else though, so doesn't need to be strong.\n\t\telse if (!strncmp(tmp, \"a=\",2) && authed)\n\t\t{\n\t\t\tnetadr_t adr;\n\t\t\tif (NET_StringToAddr(tmp+2, &adr, 0))\n\t\t\t{\t//master told us our IP. we can use that to report to turn clients\n\t\t\t\tif (((struct sockaddr*)&adr.sockaddr)->sa_family == AF_INET)\n\t\t\t\t\tmemcpy(cluster->turn_ipv4, &((struct sockaddr_in*)&adr.sockaddr)->sin_addr, 4);\n\t\t\t\telse if (((struct sockaddr*)&adr.sockaddr)->sa_family == AF_INET6)\n\t\t\t\t\tmemcpy(cluster->turn_ipv6, &((struct sockaddr_in6*)&adr.sockaddr)->sin6_addr, 16);\n\t\t\t}\n\t\t}\n\t}\n\tCOM_ParseToken(cluster->protocolname?cluster->protocolname:\"FTE-Quake\", protocolname, sizeof(protocolname), \"\");\t//we can only report one, so report the first.\n\n\t//response packet header\n\tWriteLong(&msg, ~0u);\n//\tif (fullstatus)\n//\t\tWriteString2(&msg, \"statusResponse\\n\");\n//\telse\n\t\tWriteString2(&msg, \"infoResponse\\n\");\n\n\t//first line contains the serverinfo, or some form of it\n\tWriteString2(&msg, \"\\\\*QTV\\\\\");\t\t\t\tWriteString2(&msg, QTV_VERSION_STRING);\n//\tWriteString2(&msg, \"\\\\*fp\\\\\");\t\t\t\tWriteString2(&msg, hash(cert));\n\tif (authed)\n\t{\t//only reported to the master server to generate time-based auth tokens.\n\t\ttobase64(tmp,sizeof(tmp), cluster->turnkey, sizeof(cluster->turnkey));\n\t\tWriteString2(&msg, \"\\\\_turnkey\\\\\");\t\t\tWriteString2(&msg, tmp);\n\t}\n\tWriteString2(&msg, \"\\\\challenge\\\\\");\t\tWriteString2(&msg, challenge);\n\tWriteString2(&msg, \"\\\\gamename\\\\\");\t\t\tWriteString2(&msg, protocolname);\n\tsnprintf(tmp, sizeof(tmp), \"%i%s\", cluster->protocolname?cluster->protocolver:3, \"t\"); //'w':quakeworld, 'n'/'d':netquake, 'x':qe, 't':qtv, 'r':turnrelay, 'f':fwd\n\tWriteString2(&msg, \"\\\\protocol\\\\\");\t\t\tWriteString2(&msg, tmp);\n\tWriteString2(&msg, \"\\\\clients\\\\\");\t\t\tWriteString2(&msg, \"0\");\n\tWriteString2(&msg, \"\\\\sv_maxclients\\\\\");\tWriteString2(&msg, \"0\");\n\tWriteString2(&msg, \"\\\\modname\\\\\");\t\t\tWriteString2(&msg, \"QTV\");\n\tWriteString2(&msg, \"\\\\mapname\\\\\");\t\t\tWriteString2(&msg, \"QTV\");\n\tWriteString2(&msg, \"\\\\hostname\\\\\");\t\t\tWriteString2(&msg, cluster->hostname);\n\tsnprintf(tmp, sizeof(tmp), \"%i\", cluster->tcplistenportnum);\n\tWriteString2(&msg, \"\\\\sv_port_tcp\\\\\");\t\tWriteString2(&msg, tmp);\n\n\t/*if (fullstatus)\n\t{\n\t\tclient_t *cl;\n\t\tchar *start = resp;\n\n\t\tif (resp != response+sizeof(response))\n\t\t{\n\t\t\tresp[-1] = '\\n';\t//replace the null terminator that we already wrote\n\n\t\t\t//on the following lines we have an entry for each client\n\t\t\tfor (i=0 ; i<svs.allocated_client_slots ; i++)\n\t\t\t{\n\t\t\t\tcl = &svs.clients[i];\n\t\t\t\tif ((cl->state == cs_connected || cl->state == cs_spawned || cl->name[0]) && !cl->spectator)\n\t\t\t\t{\n\t\t\t\t\tQ_strncpyz(resp, va(\n\t\t\t\t\t\t\t\t\t\"%d %d \\\"%s\\\" \\\"%s\\\"\\n\"\n\t\t\t\t\t\t\t\t\t,\n\t\t\t\t\t\t\t\t\tcl->old_frags,\n\t\t\t\t\t\t\t\t\tSV_CalcPing(cl, false),\n\t\t\t\t\t\t\t\t\tcl->team,\n\t\t\t\t\t\t\t\t\tcl->name\n\t\t\t\t\t\t\t\t\t), sizeof(response) - (resp-response));\n\t\t\t\t\tresp += strlen(resp);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t*resp++ = 0;\t//this might not be a null\n\t\t\tif (resp == response+sizeof(response))\n\t\t\t{\n\t\t\t\t//we're at the end of the buffer, it's full. bummer\n\t\t\t\t//replace 12 bytes with infoResponse\n\t\t\t\tmemcpy(response+4, \"infoResponse\", 12);\n\t\t\t\t//move down by len(statusResponse)-len(infoResponse) bytes\n\t\t\t\tmemmove(response+4+12, response+4+14, resp-response-(4+14));\n\t\t\t\tstart -= 14-12; //fix this pointer\n\n\t\t\t\tresp = start;\n\t\t\t\tresp[-1] = 0;\t//reset the \\n\n\t\t\t}\n\t\t}\n\t}*/\n\n\tWriteByte(&msg, 0);\n\n\tNET_SendPacket(cluster, NET_ChooseSocket(cluster->qwdsocket, from, *from), msg.cursize, msg.data, *from);\n}\n\nvoid QTV_StatusResponse(cluster_t *cluster, char *msg, netadr_t *from)\n{\n\tint p, tc, bc;\n\tchar name[64], skin[64], token[64];\n\tsv_t *sv;\n\n\tchar *eol;\n\n\tfor (sv = cluster->servers; sv; sv = sv->next)\n\t{\n\t\t/*ignore connected streams*/\n\t\tif (sv->isconnected)\n\t\t\tcontinue;\n\t\t/*and only streams that we could have requested this from*/\n\t\tif (sv->autodisconnect != AD_STATUSPOLL)\n\t\t\tcontinue;\n\n\t\tif (Net_CompareAddress(&sv->serveraddress, from, 0, 1))\n\t\t\tbreak;\n\t}\n\t/*not a valid server... weird.*/\n\tif (!sv)\n\t\treturn;\n\n\t/*skip the n directive*/\n\tmsg++;\n\teol = strchr(msg, '\\n');\n\tif (!eol)\n\t\treturn;\n\t*eol = 0;\n\n\tstrlcpy(sv->map.serverinfo, msg, sizeof(sv->map.serverinfo));\n\tQTV_UpdatedServerInfo(sv);\n\n//\tInfo_ValueForKey(sv->map.serverinfo, \"map\", sv->map.mapname, sizeof(sv->map.mapname));\n\tInfo_ValueForKey(sv->map.serverinfo, \"*gamedir\", sv->map.gamedir, sizeof(sv->map.gamedir));\n\tif (!*sv->map.gamedir)\n\t\tstrlcpy(sv->map.gamedir, \"qw\", sizeof(sv->map.gamedir));\n\n\tfor(p = 0; p < MAX_CLIENTS; p++)\n\t{\n\t\tmsg = eol+1;\n\t\teol = strchr(msg, '\\n');\n\t\tif (!eol)\n\t\t\tbreak;\n\t\t*eol = 0;\n\n\t\tsv->map.players[p].active = false;\n\n\t\t//userid\n\t\tmsg = COM_ParseToken(msg, token, sizeof(token), NULL);\n\n\t\t//frags\n\t\tmsg = COM_ParseToken(msg, token, sizeof(token), NULL);\n\t\tsv->map.players[p].frags = atoi(token);\n\n\t\t//time (minuites)\n\t\tmsg = COM_ParseToken(msg, token, sizeof(token), NULL);\n\n\t\t//ping\n\t\tmsg = COM_ParseToken(msg, token, sizeof(token), NULL);\n\t\tsv->map.players[p].ping = atoi(token);\n\n\t\t//name\n\t\tmsg = COM_ParseToken(msg, name, sizeof(name), NULL);\n\n\t\t//skin\n\t\tmsg = COM_ParseToken(msg, skin, sizeof(skin), NULL);\n\n\t\t//tc\n\t\tmsg = COM_ParseToken(msg, token, sizeof(token), NULL);\n\t\ttc = atoi(token);\n\n\t\t//bc\n\t\tmsg = COM_ParseToken(msg, token, sizeof(token), NULL);\n\t\tbc = atoi(token);\n\n\t\tsnprintf(sv->map.players[p].userinfo, sizeof(sv->map.players[p].userinfo), \"\\\\name\\\\%s\\\\skin\\\\%s\\\\topcolor\\\\%i\\\\bottomcolor\\\\%i\", name, skin, tc, bc);\n\t}\n\tfor(; p < MAX_CLIENTS; p++)\n\t{\n\t\tsv->map.players[p].active = false;\n\t\t*sv->map.players[p].userinfo = 0;\n\t}\n}\n\nvoid ConnectionlessPacket(cluster_t *cluster, netadr_t *from, netmsg_t *m)\n{\n\tchar buffer[MAX_QWMSGLEN];\n\tint i;\n\n\tReadLong(m);\n\tReadString(m, buffer, sizeof(buffer));\n\n\tif (!strncmp(buffer, \"n\\\\\", 2))\n\t{\n\t\tQTV_StatusResponse(cluster, buffer, from);\n\t\treturn;\n\t}\n\tif (!strncmp(buffer, \"rcon \", 5))\n\t{\n\t\tQTV_Rcon(cluster, buffer+5, from);\n\t\treturn;\n\t}\n\tif (!strncmp(buffer, \"ping\", 4))\n\t{\t//ack\n\t\tNetchan_OutOfBandPrint(cluster, *from, \"l\");\n\t\treturn;\n\t}\n\tif (!strncmp(buffer, \"status\", 6))\n\t{\n\t\tQTV_Status(cluster, from);\n\t\treturn;\n\t}\n\tif (!strncmp(buffer, \"getinfo\", 7))\n\t{\n\t\tQTV_GetInfo(cluster, from, buffer+7);\n\t\treturn;\n\t}\n\tif (!strncmp(buffer, \"getchallenge\", 12))\n\t{\n\t\ti = NewChallenge(cluster, from);\n\t\tif (!cluster->relayenabled)\n\t\t\tNetchan_OutOfBandPrint(cluster, *from, \"c%i\", i);\n\t\telse\n\t\t{\t//special response to say we don't support dtls, but can proxy it, so use dtlsconnect without needing to send any private info until the final target is determined.\n\t\t\tsnprintf(buffer, sizeof(buffer), \"c%i%cDTLS\\xff\\xff\\xff\\xff\", i, 0);\t//PROTOCOL_VERSION_DTLSUPGRADE\n\t\t\tNetchan_OutOfBand(cluster, *from, strlen(buffer)+9, buffer);\n\t\t}\n\t\treturn;\n\t}\n\tif (!strncmp(buffer, \"connect 28 \", 11))\n\t{\n\t\tif (cluster->numviewers >= cluster->maxviewers && cluster->maxviewers)\n\t\t\tNetchan_OutOfBandPrint(cluster, *from, \"n\" \"Sorry, proxy is full.\\n\");\n\t\telse\n\t\t\tNewQWClient(cluster, from, buffer);\n\t\treturn;\n\t}\n\tif (!strncmp(buffer, \"getserversExtResponse\", 21) && cluster->pingtreeenabled)\n\t{\t//q3-style serverlist response\n\t\tm->readpos = 4+21;\n\t\tFwd_ParseServerList(cluster, m, -1);\n\t\treturn;\n\t}\n\tif (!strncmp(buffer, \"d\\n\", 2) && cluster->pingtreeenabled)\n\t{\t//legacy qw serverlist response\n\t\tm->readpos = 4+2;\n\t\tFwd_ParseServerList(cluster, m, AF_INET);\n\t\treturn;\n\t}\n\tif (!strcmp(buffer, \"l\") && cluster->pingtreeenabled)\n\t{\t//qw ping response\n\t\tFwd_PingResponse(cluster, from);\n\t\treturn;\n\t}\n\tif (!strncmp(buffer, \"pingstatus\", 10) && cluster->pingtreeenabled)\n\t{\n\t\tint ext = false;\n\t\tchar arg[64];\n\t\tif (buffer[10] == ' ')\n\t\t{\n\t\t\tchar *s = buffer + 11;\n\t\t\twhile (*s)\n\t\t\t{\n\t\t\t\ts = COM_ParseToken(s, arg,sizeof(arg), \"\");\t//\n\t\t\t\tif (!strcmp(arg, \"ext\"))\n\t\t\t\t\text = true;\n\t\t\t}\n\t\t}\n\t\tFwd_PingStatus(cluster, from, ext);\n\t\treturn;\n\t}\n\tif (!strncmp(buffer, \"dtlsconnect \", 12) && cluster->relayenabled)\n\t{\t//dtlsconnect challenge [finalip@middleip@targetip]\n\t\tchar challenge[64];\n\t\tchar *s = COM_ParseToken(buffer+12, challenge,sizeof(challenge), \"\");\t//\n\t\tif (ChallengePasses(cluster, from, atoi(challenge)))\n\t\t{\n\t\t\twhile(*s == ' ')\n\t\t\t\ts++;\n\t\t\tFwd_NewQWFwd(cluster, from, s);\t//will send a challenge to the target.\n\t\t\t//the relay code will pass the response to the client triggering a new dtlsconnect.\n\t\t\t//eventually punching all the way through to the target which will respond with a dtlsopened.\n\t\t\t//the client will then be free to send its dtls handshakes, with the server's certificate matched against the fingerprint reported by the master.\n\t\t\t//this should ensure there's no tampering.\n\t\t\t//note that we cannot read any disconnect hints when they're encrypted, so we'll be depending on timeouts (which also avoids malicious disconnect spoofs, yay?)\n\t\t}\n\t\treturn;\n\t}\n//\tif (buffer[0] == 'l' && (!buffer[1] || buffer[1] == '\\n'))\n//\t{\n//\t\tSys_Printf(cluster, \"Ack from %s\\n\", );\n//\t}\n}\n\n\nvoid SV_WriteDelta(int entnum, const entity_state_t *from, const entity_state_t *to, netmsg_t *msg, qboolean force, unsigned int pext)\n{\n\tunsigned int i;\n\tunsigned int bits;\n\n\tbits = 0;\n\n\tif (entnum >= 2048)\n\t{\t//panic\n\t\tif (!force)\n\t\t\treturn;\n\t\t//erk! oh noes! woe is me!\n\t}\n\telse if (entnum >= 1024+512)\n\t\tbits |= UX_ENTITYDBL|UX_ENTITYDBL2;\n\telse if (entnum >= 1024)\n\t\tbits |= UX_ENTITYDBL2;\n\telse if (entnum >= 512)\n\t\tbits |= UX_ENTITYDBL;\n\n\tif (from->angles[0] != to->angles[0])\n\t\tbits |= U_ANGLE1;\n\tif (from->angles[1] != to->angles[1])\n\t\tbits |= U_ANGLE2;\n\tif (from->angles[2] != to->angles[2])\n\t\tbits |= U_ANGLE3;\n\n\tif (from->origin[0] != to->origin[0])\n\t\tbits |= U_ORIGIN1;\n\tif (from->origin[1] != to->origin[1])\n\t\tbits |= U_ORIGIN2;\n\tif (from->origin[2] != to->origin[2])\n\t\tbits |= U_ORIGIN3;\n\n\tif (from->colormap != to->colormap)\n\t\tbits |= U_COLORMAP;\n\tif (from->skinnum != to->skinnum)\n\t\tbits |= U_SKIN;\n\tif (from->modelindex != to->modelindex)\n\t{\n\t\tif (to->modelindex > 0xff)\n\t\t{\n\t\t\tif (to->modelindex <= 0x1ff)\n\t\t\t\tbits |= U_MODEL|UX_MODELDBL;\t//0x100|byte\n\t\t\telse\n\t\t\t\tbits |= UX_MODELDBL;\t\t\t//short\n\t\t}\n\t\telse\n\t\t\tbits |= U_MODEL;\n\t}\n\tif (from->frame != to->frame)\n\t\tbits |= U_FRAME;\n\tif ((from->effects&0xff) != (to->effects&0xff))\n\t\tbits |= U_EFFECTS;\n\n\tif ((from->effects&0xff00) != (to->effects&0xff00) &&\n\t\tpext & PEXT_DPFLAGS)\n\t\tbits |= UX_EFFECTS16;\n\tif (from->alpha != to->alpha &&\n\t\tpext & PEXT_TRANS)\n\t\tbits |= UX_ALPHA;\n\tif (from->scale != to->scale &&\n\t\tpext & PEXT_SCALE)\n\t\tbits |= UX_SCALE;\n\tif (from->fatness != to->fatness &&\n\t\tpext & PEXT_FATNESS)\n\t\tbits |= UX_FATNESS;\n\tif (from->drawflags != to->drawflags &&\n\t\tpext & PEXT_HEXEN2)\n\t\tbits |= UX_DRAWFLAGS;\n\tif (from->abslight != to->abslight &&\n\t\tpext & PEXT_HEXEN2)\n\t\tbits |= UX_ABSLIGHT;\n\tif ((from->colormod[0]!= to->colormod[0] ||\n\t\tfrom->colormod[1] != to->colormod[1] ||\n\t\tfrom->colormod[2] != to->colormod[2]) &&\n\t\tpext & PEXT_COLOURMOD)\n\t\tbits |= UX_COLOURMOD;\n\tif (from->dpflags != to->dpflags &&\n\t\tpext & PEXT_DPFLAGS)\n\t\tbits |= UX_DPFLAGS;\n\tif ((from->tagentity != to->tagentity ||\n\t\tfrom->tagindex != to->tagindex) &&\n\t\tpext & PEXT_SETATTACHMENT)\n\t\tbits |= UX_TAGINFO;\n\tif ((from->light[0] != to->light[0] ||\n\t\tfrom->light[1] != to->light[1] ||\n\t\tfrom->light[2] != to->light[2] ||\n\t\tfrom->light[3] != to->light[3] ||\n\t\tfrom->lightstyle != to->lightstyle ||\n\t\tfrom->lightpflags != to->lightpflags) &&\n\t\tpext & PEXT_DPFLAGS)\n\t\tbits |= UX_LIGHT;\n\n\tif (bits & 0xff000000)\n\t\tbits |= UX_YETMORE;\n\tif (bits & 0x00ff0000)\n\t\tbits |= UX_EVENMORE;\n\tif (bits & 0x000000ff)\n\t\tbits |= U_MOREBITS;\n\n\n\n\tif (!bits && !force)\n\t\treturn;\n\n\ti = (entnum&511) | (bits&~511);\n\tWriteShort (msg, i);\n\n\tif (bits & U_MOREBITS)\n\t\tWriteByte (msg, bits&255);\n\n\tif (bits & UX_EVENMORE)\n\t\tWriteByte (msg, bits>>16);\n\tif (bits & UX_YETMORE)\n\t\tWriteByte (msg, bits>>24);\n\n\tif (bits & U_MODEL)\n\t\tWriteByte (msg,\tto->modelindex&255);\n\telse if (bits & UX_MODELDBL)\n\t\tWriteShort(msg,\tto->modelindex&0xffff);\n\tif (bits & U_FRAME)\n\t\tWriteByte (msg, to->frame);\n\tif (bits & U_COLORMAP)\n\t\tWriteByte (msg, to->colormap);\n\tif (bits & U_SKIN)\n\t\tWriteByte (msg, to->skinnum);\n\tif (bits & U_EFFECTS)\n\t\tWriteByte (msg, to->effects&0x00ff);\n\tif (bits & U_ORIGIN1)\n\t\tWriteCoord(msg, to->origin[0], pext);\n\tif (bits & U_ANGLE1)\n\t\tWriteAngle(msg, to->angles[0], pext);\n\tif (bits & U_ORIGIN2)\n\t\tWriteCoord(msg, to->origin[1], pext);\n\tif (bits & U_ANGLE2)\n\t\tWriteAngle(msg, to->angles[1], pext);\n\tif (bits & U_ORIGIN3)\n\t\tWriteCoord(msg, to->origin[2], pext);\n\tif (bits & U_ANGLE3)\n\t\tWriteAngle(msg, to->angles[2], pext);\n\n\tif (bits & UX_SCALE)\n\t\tWriteByte (msg, to->scale);\n\tif (bits & UX_ALPHA)\n\t\tWriteByte (msg, to->alpha);\n\tif (bits & UX_FATNESS)\n\t\tWriteByte (msg, to->fatness);\n\tif (bits & UX_DRAWFLAGS)\n\t\tWriteByte (msg, to->drawflags);\n\tif (bits & UX_ABSLIGHT)\n\t\tWriteByte (msg, to->abslight);\n\tif (bits & UX_COLOURMOD)\n\t{\n\t\tWriteByte (msg, to->colormod[0]);\n\t\tWriteByte (msg, to->colormod[1]);\n\t\tWriteByte (msg, to->colormod[2]);\n\t}\n\tif (bits & UX_DPFLAGS)\n\t{\t// these are bits for the 'flags' field of the entity_state_t\n\t\tWriteByte (msg, to->dpflags);\n\t}\n\tif (bits & UX_TAGINFO)\n\t{\n\t\tWriteShort (msg, to->tagentity);\n\t\tWriteShort (msg, to->tagindex);\n\t}\n\tif (bits & UX_LIGHT)\n\t{\n\t\tWriteShort (msg, to->light[0]);\n\t\tWriteShort (msg, to->light[1]);\n\t\tWriteShort (msg, to->light[2]);\n\t\tWriteShort (msg, to->light[3]);\n\t\tWriteByte (msg, to->lightstyle);\n\t\tWriteByte (msg, to->lightpflags);\n\t}\n\tif (bits & UX_EFFECTS16)\n\t\tWriteByte (msg, to->effects>>8);\n}\n\nvoid SV_EmitPacketEntities (const sv_t *qtv, const viewer_t *v, const packet_entities_t *to, netmsg_t *msg)\n{\n\tconst entity_state_t *baseline;\n\tconst packet_entities_t *from;\n\tint\t\toldindex, newindex;\n\tint\t\toldnum, newnum;\n\tint\t\toldmax;\n\tint\t\tdelta_frame;\n\n\tdelta_frame = v->delta_frames[v->netchan.outgoing_sequence&(ENTITY_FRAMES-1)];\n\n\t// this is the frame that we are going to delta update from\n\tif (delta_frame != -1)\n\t{\n\t\tfrom = &v->frame[delta_frame & (ENTITY_FRAMES-1)];\n\t\toldmax = from->numents;\n\n\t\tWriteByte (msg, svc_deltapacketentities);\n\t\tWriteByte (msg, delta_frame);\n\t}\n\telse\n\t{\n\t\toldmax = 0;\t// no delta update\n\t\tfrom = NULL;\n\n\t\tWriteByte (msg, svc_packetentities);\n\t}\n\n\tnewindex = 0;\n\toldindex = 0;\n//Con_Printf (\"---%i to %i ----\\n\", client->delta_sequence & UPDATE_MASK\n//\t\t\t, client->netchan.outgoing_sequence & UPDATE_MASK);\n\twhile (newindex < to->numents || oldindex < oldmax)\n\t{\n\t\tnewnum = newindex >= to->numents ? 9999 : to->entnum[newindex];\n\t\toldnum = oldindex >= oldmax ? 9999 : from->entnum[oldindex];\n\n\t\tif (newnum == oldnum)\n\t\t{\t// delta update from old position\n//Con_Printf (\"delta %i\\n\", newnum);\n\t\t\tSV_WriteDelta (newnum, &from->ents[oldindex], &to->ents[newindex], msg, false, qtv->pext1);\n\n\t\t\toldindex++;\n\t\t\tnewindex++;\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (newnum < oldnum)\n\t\t{\t// this is a new entity, send it from the baseline\n\t\t\tbaseline = &qtv->map.entity[newnum].baseline;\n//Con_Printf (\"baseline %i\\n\", newnum);\n\t\t\tSV_WriteDelta (newnum, baseline, &to->ents[newindex], msg, true, qtv->pext1);\n\n\t\t\tnewindex++;\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (newnum > oldnum)\n\t\t{\t// the old entity isn't present in the new message\n//Con_Printf (\"remove %i\\n\", oldnum);\n\t\t\tWriteShort (msg, oldnum | U_REMOVE);\n\t\t\toldindex++;\n\t\t\tcontinue;\n\t\t}\n\t}\n\n\tWriteShort (msg, 0);\t// end of packetentities\n}\n\nvoid Prox_SendInitialEnts(sv_t *qtv, oproxy_t *prox, netmsg_t *msg)\n{\n\tframe_t *frame;\n\tint i, entnum;\n\tWriteByte(msg, svc_packetentities);\n\tframe = &qtv->map.frame[qtv->netchan.incoming_sequence & (ENTITY_FRAMES-1)];\n\tfor (i = 0; i < frame->numents; i++)\n\t{\n\t\tentnum = frame->entnums[i];\n\t\tSV_WriteDelta(entnum, &qtv->map.entity[entnum].baseline, &frame->ents[i], msg, true, qtv->pext1);\n\t}\n\tWriteShort(msg, 0);\n}\n\nstatic float InterpolateAngle(float current, float ideal, float fraction)\n{\n\tfloat move;\n\n\tmove = ideal - current;\n\tif (move >= 180)\n\t\tmove -= 360;\n\telse if (move <= -180)\n\t\tmove += 360;\n\n\treturn current + fraction * move;\n}\n\nvoid SendLocalPlayerState(sv_t *tv, viewer_t *v, int playernum, netmsg_t *msg)\n{\n\tint flags;\n\tint j;\n\tunsigned int pext1 = tv?tv->pext1:0;\n\n\tWriteByte(msg, svc_playerinfo);\n\tWriteByte(msg, playernum);\n\n\tif (tv && tv->controller == v)\n\t{\t//we're the one that is actually playing.\n\t\tv->trackplayer = tv->map.thisplayer;\n\t\tflags = 0;\n\t\tif (tv->map.players[tv->map.thisplayer].current.weaponframe)\n\t\t\tflags |= PF_WEAPONFRAME;\n\t\tif (tv->map.players[tv->map.thisplayer].current.effects)\n\t\t\tflags |= PF_EFFECTS;\n\n\t\tif (tv->map.players[tv->map.thisplayer].dead)\n\t\t\tflags |= PF_DEAD;\n\t\tif (tv->map.players[tv->map.thisplayer].gibbed)\n\t\t\tflags |= PF_GIB;\n\n\t\tfor (j=0 ; j<3 ; j++)\n\t\t\tif (tv->map.players[tv->map.thisplayer].current.velocity[j])\n\t\t\t\tflags |= (PF_VELOCITY1<<j);\n\n\t\tWriteShort(msg, flags);\n\t\tWriteCoord(msg, tv->map.players[tv->map.thisplayer].current.origin[0], tv->pext1);\n\t\tWriteCoord(msg, tv->map.players[tv->map.thisplayer].current.origin[1], tv->pext1);\n\t\tWriteCoord(msg, tv->map.players[tv->map.thisplayer].current.origin[2], tv->pext1);\n\t\tWriteByte(msg, tv->map.players[tv->map.thisplayer].current.frame);\n\n\t\tfor (j=0 ; j<3 ; j++)\n\t\t\tif (flags & (PF_VELOCITY1<<j) )\n\t\t\t\tWriteShort (msg, tv->map.players[tv->map.thisplayer].current.velocity[j]);\n\n\t\tif (flags & PF_MODEL)\n\t\t\tWriteByte(msg, tv->map.players[tv->map.thisplayer].current.modelindex);\n\t\tif (flags & PF_SKINNUM)\n\t\t\tWriteByte(msg, tv->map.players[tv->map.thisplayer].current.skinnum);\n\t\tif (flags & PF_EFFECTS)\n\t\t\tWriteByte(msg, tv->map.players[tv->map.thisplayer].current.effects);\n\t\tif (flags & PF_WEAPONFRAME)\n\t\t\tWriteByte(msg, tv->map.players[tv->map.thisplayer].current.weaponframe);\n\t}\n\telse\n\t{\n\t\tflags = 0;\n\n\t\tfor (j=0 ; j<3 ; j++)\n\t\t\tif ((int)v->velocity[j])\n\t\t\t\tflags |= (PF_VELOCITY1<<j);\n\n\t\tWriteShort(msg, flags);\n\t\tWriteCoord(msg, v->origin[0], pext1);\n\t\tWriteCoord(msg, v->origin[1], pext1);\n\t\tWriteCoord(msg, v->origin[2], pext1);\n\t\tWriteByte(msg, 0);\n\n\t\tfor (j=0 ; j<3 ; j++)\n\t\t\tif (flags & (PF_VELOCITY1<<j) )\n\t\t\t\tWriteShort (msg, v->velocity[j]);\n\t}\n}\n\n#define\tUNQ_MOREBITS\t(1<<0)\n#define\tUNQ_ORIGIN1\t(1<<1)\n#define\tUNQ_ORIGIN2\t(1<<2)\n#define\tUNQ_ORIGIN3\t(1<<3)\n#define\tUNQ_ANGLE2\t(1<<4)\n#define\tUNQ_NOLERP\t(1<<5)\t\t// don't interpolate movement\n#define\tUNQ_FRAME\t\t(1<<6)\n#define UNQ_SIGNAL\t(1<<7)\t\t// just differentiates from other updates\n\n// svc_update can pass all of the fast update bits, plus more\n#define\tUNQ_ANGLE1\t(1<<8)\n#define\tUNQ_ANGLE3\t(1<<9)\n#define\tUNQ_MODEL\t\t(1<<10)\n#define\tUNQ_COLORMAP\t(1<<11)\n#define\tUNQ_SKIN\t\t(1<<12)\n#define\tUNQ_EFFECTS\t(1<<13)\n#define\tUNQ_LONGENTITY\t(1<<14)\n#define UNQ_UNUSED\t(1<<15)\n\n\n#define\tSU_VIEWHEIGHT\t(1<<0)\n#define\tSU_IDEALPITCH\t(1<<1)\n#define\tSU_PUNCH1\t\t(1<<2)\n#define\tSU_PUNCH2\t\t(1<<3)\n#define\tSU_PUNCH3\t\t(1<<4)\n#define\tSU_VELOCITY1\t(1<<5)\n#define\tSU_VELOCITY2\t(1<<6)\n#define\tSU_VELOCITY3\t(1<<7)\n//define\tSU_AIMENT\t\t(1<<8)  AVAILABLE BIT\n#define\tSU_ITEMS\t\t(1<<9)\n#define\tSU_ONGROUND\t\t(1<<10)\t\t// no data follows, the bit is it\n#define\tSU_INWATER\t\t(1<<11)\t\t// no data follows, the bit is it\n#define\tSU_WEAPONFRAME\t(1<<12)\n#define\tSU_ARMOR\t\t(1<<13)\n#define\tSU_WEAPON\t\t(1<<14)\n\nvoid SendNQClientData(sv_t *tv, viewer_t *v, netmsg_t *msg)\n{\n\tplayerinfo_t *pl;\n\tint bits;\n\tint i;\n\n\tif (!tv)\n\t\treturn;\n\n\tif (v->trackplayer < 0)\n\t{\n\t\tWriteByte (msg, svc_nqclientdata);\n\t\tWriteShort (msg, SU_VIEWHEIGHT|SU_ITEMS);\n\t\tWriteByte (msg, 22); //viewheight\n\t\tWriteLong (msg, 0);\t//items\n\t\tWriteShort (msg, 1000);\t//health\n\t\tWriteByte (msg, 0);\t//currentammo\n\t\tWriteByte (msg, 0);\t//shells\n\t\tWriteByte (msg, 0);\t//nails\n\t\tWriteByte (msg, 0);\t//rockets\n\t\tWriteByte (msg, 0);\t//cells\n\t\tWriteByte (msg, 0); //active weapon\n\t\treturn;\n\t}\n\telse\n\t\tpl = &tv->map.players[v->trackplayer];\n\n\tbits = 0;\n\n\tif (!pl->dead)\n\t\tbits |= SU_VIEWHEIGHT;\n\n\tif (0)\n\t\tbits |= SU_IDEALPITCH;\n\n\tbits |= SU_ITEMS;\n\n\tif ( 0)\n\t\tbits |= SU_ONGROUND;\n\n\tif ( 0 )\n\t\tbits |= SU_INWATER;\n\n\tfor (i=0 ; i<3 ; i++)\n\t{\n\t\tif (0)\n\t\t\tbits |= (SU_PUNCH1<<i);\n\t\tif (0)\n\t\t\tbits |= (SU_VELOCITY1<<i);\n\t}\n\n\tif (pl->current.weaponframe)\n\t\tbits |= SU_WEAPONFRAME;\n\n\tif (pl->stats[STAT_ARMOR])\n\t\tbits |= SU_ARMOR;\n\n//\tif (pl->stats[STAT_WEAPON])\n\t\tbits |= SU_WEAPON;\n\n// send the data\n\n\tWriteByte (msg, svc_nqclientdata);\n\tWriteShort (msg, bits);\n\n\tif (bits & SU_VIEWHEIGHT)\n\t\tWriteByte (msg, 22);\n\n\tif (bits & SU_IDEALPITCH)\n\t\tWriteByte (msg, 0);\n\n\tfor (i=0 ; i<3 ; i++)\n\t{\n\t\tif (bits & (SU_PUNCH1<<i))\n\t\t\tWriteByte (msg, 0);\n\t\tif (bits & (SU_VELOCITY1<<i))\n\t\t\tWriteByte (msg, 0);\n\t}\n\n\tif (bits & SU_ITEMS)\n\t\tWriteLong (msg, pl->stats[STAT_ITEMS]);\n\n\tif (bits & SU_WEAPONFRAME)\n\t\tWriteByte (msg, pl->current.weaponframe);\n\tif (bits & SU_ARMOR)\n\t\tWriteByte (msg, pl->stats[STAT_ARMOR]);\n\tif (bits & SU_WEAPON)\n\t\tWriteByte (msg, pl->stats[STAT_WEAPONMODELI]);\n\n\tWriteShort (msg, pl->stats[STAT_HEALTH]);\n\tWriteByte (msg, pl->stats[STAT_AMMO]);\n\tWriteByte (msg, pl->stats[STAT_SHELLS]);\n\tWriteByte (msg, pl->stats[STAT_NAILS]);\n\tWriteByte (msg, pl->stats[STAT_ROCKETS]);\n\tWriteByte (msg, pl->stats[STAT_CELLS]);\n\n\tWriteByte (msg, pl->stats[STAT_ACTIVEWEAPON]);\n}\n\nvoid SendNQPlayerStates(cluster_t *cluster, sv_t *tv, viewer_t *v, netmsg_t *msg)\n{\n\tint e;\n\tint i;\n\tusercmd_t to;\n\tfloat lerp;\n\tint bits;\n\tfloat org[3];\n\tentity_t *ent;\n\tplayerinfo_t *pl;\n\tunsigned int pext1;\n\n\tmemset(&to, 0, sizeof(to));\n\n\tif (tv)\n\t{\n\t\tpext1 = tv->pext1;\n\t\tWriteByte(msg, svc_nqtime);\n\t\tWriteFloat(msg, (tv->physicstime - tv->mapstarttime)/1000.0f);\n\n\t\tBSP_SetupForPosition(tv->map.bsp, v->origin[0], v->origin[1], v->origin[2]);\n\n\t\tlerp = ((tv->simtime - tv->oldpackettime)/1000.0f) / ((tv->nextpackettime - tv->oldpackettime)/1000.0f);\n\t\tlerp = 1;\n\n//\t\tif (tv->controller == v)\n//\t\t\tlerp = 1;\n\t}\n\telse\n\t{\n\t\tWriteByte(msg, svc_nqtime);\n\t\tWriteFloat(msg, (cluster->curtime)/1000.0f);\n\n\t\tlerp = 1;\n\t\tpext1 = 0;\n\t}\n\n\tSendNQClientData(tv, v, msg);\n\n\tif (tv)\n\t{\n\t\tif (v != tv->controller)\n\t\t{\n\t\t\tif (v->trackplayer >= 0)\n\t\t\t{\n\t\t\t\tWriteByte(msg, svc_nqsetview);\n\t\t\t\tWriteShort(msg, v->trackplayer+1);\n\n\t\t\t\tWriteByte(msg, svc_setangle);\n\t\t\t\tWriteAngle(msg, InterpolateAngle(tv->map.players[v->trackplayer].old.angles[0], tv->map.players[v->trackplayer].current.angles[0], lerp), tv->pext1);\n\t\t\t\tWriteAngle(msg, InterpolateAngle(tv->map.players[v->trackplayer].old.angles[1], tv->map.players[v->trackplayer].current.angles[1], lerp), tv->pext1);\n\t\t\t\tWriteAngle(msg, InterpolateAngle(tv->map.players[v->trackplayer].old.angles[2], tv->map.players[v->trackplayer].current.angles[2], lerp), tv->pext1);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tWriteByte(msg, svc_nqsetview);\n\t\t\t\tWriteShort(msg, v->thisplayer+1);\n\t\t\t}\n\t\t}\n\n\t\tfor (e = 0; e < MAX_CLIENTS; e++)\n\t\t{\n\t\t\tpl = &tv->map.players[e];\n\t\t\tent = &tv->map.entity[e+1];\n\n\t\t\tif (e == v->thisplayer && v->trackplayer < 0)\n\t\t\t{\n\t\t\t\tbits = UNQ_ORIGIN1 | UNQ_ORIGIN2 | UNQ_ORIGIN3 | UNQ_COLORMAP;\n\n\n  \t\t\t\tif (e+1 > 255)\n\t\t\t\t\tbits |= UNQ_LONGENTITY;\n\n\t\t\t\tif (bits > 255)\n\t\t\t\t\tbits |= UNQ_MOREBITS;\n\t\t\t\tWriteByte (msg,bits | UNQ_SIGNAL);\n\t\t\t\tif (bits & UNQ_MOREBITS)\n\t\t\t\t\tWriteByte (msg, bits>>8);\n\t\t\t\tif (bits & UNQ_LONGENTITY)\n\t\t\t\t\tWriteShort (msg,e+1);\n\t\t\t\telse\n\t\t\t\t\tWriteByte (msg,e+1);\n\n\t\t\t\tif (bits & UNQ_MODEL)\n\t\t\t\t\tWriteByte (msg,\t0);\n\t\t\t\tif (bits & UNQ_FRAME)\n\t\t\t\t\tWriteByte (msg, 0);\n\t\t\t\tif (bits & UNQ_COLORMAP)\n\t\t\t\t\tWriteByte (msg, 0);\n\t\t\t\tif (bits & UNQ_SKIN)\n\t\t\t\t\tWriteByte (msg, 0);\n\t\t\t\tif (bits & UNQ_EFFECTS)\n\t\t\t\t\tWriteByte (msg, 0);\n\t\t\t\tif (bits & UNQ_ORIGIN1)\n\t\t\t\t\tWriteCoord (msg, v->origin[0], tv->pext1);\n\t\t\t\tif (bits & UNQ_ANGLE1)\n\t\t\t\t\tWriteAngle(msg, -v->ucmds[2].angles[0], tv->pext1);\n\t\t\t\tif (bits & UNQ_ORIGIN2)\n\t\t\t\t\tWriteCoord (msg, v->origin[1], tv->pext1);\n\t\t\t\tif (bits & UNQ_ANGLE2)\n\t\t\t\t\tWriteAngle(msg, v->ucmds[2].angles[1], tv->pext1);\n\t\t\t\tif (bits & UNQ_ORIGIN3)\n\t\t\t\t\tWriteCoord (msg, v->origin[2], tv->pext1);\n\t\t\t\tif (bits & UNQ_ANGLE3)\n\t\t\t\t\tWriteAngle(msg, v->ucmds[2].angles[2], tv->pext1);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (!pl->active)\n\t\t\t\tcontinue;\n\n\t\t\tif (v != tv->controller && e != v->trackplayer)\n\t\t\t\tif (pl->current.modelindex >= tv->map.numinlines && !BSP_Visible(tv->map.bsp, pl->leafcount, pl->leafs))\t//don't cull bsp objects, like nq...\n\t\t\t\t\tcontinue;\n\n// send an update\n\t\t\tbits = 0;\n\n\t\t\tfor (i=0 ; i<3 ; i++)\n\t\t\t{\n\t\t\t\torg[i] = (lerp)*pl->current.origin[i] + (1-lerp)*pl->old.origin[i];\n\t\t\t\tbits |= UNQ_ORIGIN1<<i;\n\t\t\t}\n\n\t\t\tif ( pl->current.angles[0] != ent->baseline.angles[0] )\n\t\t\t\tbits |= UNQ_ANGLE1;\n\n\t\t\tif ( pl->current.angles[1] != ent->baseline.angles[1] )\n\t\t\t\tbits |= UNQ_ANGLE2;\n\n\t\t\tif ( pl->current.angles[2] != ent->baseline.angles[2] )\n\t\t\t\tbits |= UNQ_ANGLE3;\n\n//\t\t\tif (pl->v.movetype == MOVETYPE_STEP)\n//\t\t\t\tbits |= UNQ_NOLERP;\t// don't mess up the step animation\n\n\t\t\tif (ent->baseline.colormap != e+1 || ent->baseline.colormap > 15)\n\t\t\t\tbits |= UNQ_COLORMAP;\n\n\t\t\tif (ent->baseline.skinnum != pl->current.skinnum)\n\t\t\t\tbits |= UNQ_SKIN;\n\n\t\t\tif (ent->baseline.frame != pl->current.frame)\n\t\t\t\tbits |= UNQ_FRAME;\n\n\t\t\tif (ent->baseline.effects != pl->current.effects)\n\t\t\t\tbits |= UNQ_EFFECTS;\n\n\t\t\tif (ent->baseline.modelindex != pl->current.modelindex)\n\t\t\t\tbits |= UNQ_MODEL;\n\n\t\t\tif (e+1 > 255)\n\t\t\t\tbits |= UNQ_LONGENTITY;\n\n\t\t\tif (bits > 255)\n\t\t\t\tbits |= UNQ_MOREBITS;\n\n\t\t//\n\t\t// write the message\n\t\t//\n\t\t\tWriteByte (msg,bits | UNQ_SIGNAL);\n\n\t\t\tif (bits & UNQ_MOREBITS)\n\t\t\t\tWriteByte (msg, bits>>8);\n\t\t\tif (bits & UNQ_LONGENTITY)\n\t\t\t\tWriteShort (msg,e+1);\n\t\t\telse\n\t\t\t\tWriteByte (msg,e+1);\n\n\t\t\tif (bits & UNQ_MODEL)\n\t\t\t\tWriteByte (msg,\tpl->current.modelindex);\n\t\t\tif (bits & UNQ_FRAME)\n\t\t\t\tWriteByte (msg, pl->current.frame);\n\t\t\tif (bits & UNQ_COLORMAP)\n\t\t\t\tWriteByte (msg, (e>=15)?0:(e+1));\n\t\t\tif (bits & UNQ_SKIN)\n\t\t\t\tWriteByte (msg, pl->current.skinnum);\n\t\t\tif (bits & UNQ_EFFECTS)\n\t\t\t\tWriteByte (msg, pl->current.effects);\n\t\t\tif (bits & UNQ_ORIGIN1)\n\t\t\t\tWriteCoord (msg, org[0], tv->pext1);\n\t\t\tif (bits & UNQ_ANGLE1)\n\t\t\t\tWriteAngle(msg, -pl->current.angles[0], tv->pext1);\n\t\t\tif (bits & UNQ_ORIGIN2)\n\t\t\t\tWriteCoord (msg, org[1], tv->pext1);\n\t\t\tif (bits & UNQ_ANGLE2)\n\t\t\t\tWriteAngle(msg, pl->current.angles[1], tv->pext1);\n\t\t\tif (bits & UNQ_ORIGIN3)\n\t\t\t\tWriteCoord (msg, org[2], tv->pext1);\n\t\t\tif (bits & UNQ_ANGLE3)\n\t\t\t\tWriteAngle(msg, pl->current.angles[2], tv->pext1);\n\t\t}\n\n\n\t\t{\n\t\t\tint newindex = 0;\n\t\t\tentity_state_t *newstate;\n\t\t\tint newnum;\n\t\t\tframe_t *topacket;\n\t\t\tint snapdist = 128;\t//in quake units\n\t\t\tint miss;\n\n\t\t\tsnapdist = snapdist*8;\n\t\t\tsnapdist = snapdist*snapdist;\n\n\t\t\ttopacket = &tv->map.frame[tv->netchan.incoming_sequence&(ENTITY_FRAMES-1)];\n\n\t\t\tfor (newindex = 0; newindex < topacket->numents; newindex++)\n\t\t\t{\n\t\t\t\t//don't pvs cull bsp models\n\t\t\t\t//pvs cull everything else\n\t\t\t\tnewstate = &topacket->ents[newindex];\n\t\t\t\tnewnum = topacket->entnums[newindex];\n\t\t\t\tif (v != tv->controller)\n\t\t\t\t\tif (newstate->modelindex >= tv->map.numinlines && !BSP_Visible(tv->map.bsp, tv->map.entity[newnum].leafcount, tv->map.entity[newnum].leafs))\n\t\t\t\t\t\tcontinue;\n\n\t\t\t\tif (msg->cursize + 128 > msg->maxsize)\n\t\t\t\t\tbreak;\n\n\t\t// send an update\n\t\t\t\tbits = 0;\n\n\t\t\t\tfor (i=0 ; i<3 ; i++)\n\t\t\t\t{\n\t\t\t\t\tmiss = (newstate->origin[i]) - ent->baseline.origin[i];\n\t\t\t\t\tif ( miss <= -1/8.0 || miss >= 1/8.0 )\n\t\t\t\t\t\tbits |= UNQ_ORIGIN1<<i;\n\t\t\t\t}\n\n\t\t\t\tif (newstate->angles[0] != ent->baseline.angles[0])\n\t\t\t\t\tbits |= UNQ_ANGLE1;\n\n\t\t\t\tif (newstate->angles[1] != ent->baseline.angles[1])\n\t\t\t\t\tbits |= UNQ_ANGLE2;\n\n\t\t\t\tif (newstate->angles[2] != ent->baseline.angles[2])\n\t\t\t\t\tbits |= UNQ_ANGLE3;\n\n\t\t//\t\t\tif (ent->v.movetype == MOVETYPE_STEP)\n\t\t//\t\t\t\tbits |= UNQ_NOLERP;\t// don't mess up the step animation\n\n\t\t\t\tif (newstate->colormap != ent->baseline.colormap || ent->baseline.colormap > 15)\n\t\t\t\t\tbits |= UNQ_COLORMAP;\n\n\t\t\t\tif (newstate->skinnum != ent->baseline.skinnum)\n\t\t\t\t\tbits |= UNQ_SKIN;\n\n\t\t\t\tif (newstate->frame != ent->baseline.frame)\n\t\t\t\t\tbits |= UNQ_FRAME;\n\n\t\t\t\tif (newstate->effects != ent->baseline.effects)\n\t\t\t\t\tbits |= UNQ_EFFECTS;\n\n\t\t\t\tif (newstate->modelindex != ent->baseline.modelindex)\n\t\t\t\t\tbits |= UNQ_MODEL;\n\n\t\t\t\tif (newnum >= 256)\n\t\t\t\t\tbits |= UNQ_LONGENTITY;\n\n\t\t\t\tif (bits >= 256)\n\t\t\t\t\tbits |= UNQ_MOREBITS;\n\n\t\t\t//\n\t\t\t// write the message\n\t\t\t//\n\t\t\t\tWriteByte (msg,bits | UNQ_SIGNAL);\n\n\t\t\t\tif (bits & UNQ_MOREBITS)\n\t\t\t\t\tWriteByte (msg, bits>>8);\n\t\t\t\tif (bits & UNQ_LONGENTITY)\n\t\t\t\t\tWriteShort (msg,newnum);\n\t\t\t\telse\n\t\t\t\t\tWriteByte (msg,newnum);\n\n\t\t\t\tif (bits & UNQ_MODEL)\n\t\t\t\t\tWriteByte (msg,\tnewstate->modelindex);\n\t\t\t\tif (bits & UNQ_FRAME)\n\t\t\t\t\tWriteByte (msg, newstate->frame);\n\t\t\t\tif (bits & UNQ_COLORMAP)\n\t\t\t\t\tWriteByte (msg, (newstate->colormap>15)?0:(newstate->colormap));\n\t\t\t\tif (bits & UNQ_SKIN)\n\t\t\t\t\tWriteByte (msg, newstate->skinnum);\n\t\t\t\tif (bits & UNQ_EFFECTS)\n\t\t\t\t\tWriteByte (msg, newstate->effects);\n\t\t\t\tif (bits & UNQ_ORIGIN1)\n\t\t\t\t\tWriteCoord (msg, newstate->origin[0], pext1);\n\t\t\t\tif (bits & UNQ_ANGLE1)\n\t\t\t\t\tWriteAngle(msg, newstate->angles[0], pext1);\n\t\t\t\tif (bits & UNQ_ORIGIN2)\n\t\t\t\t\tWriteCoord (msg, newstate->origin[1], pext1);\n\t\t\t\tif (bits & UNQ_ANGLE2)\n\t\t\t\t\tWriteAngle(msg, newstate->angles[1], pext1);\n\t\t\t\tif (bits & UNQ_ORIGIN3)\n\t\t\t\t\tWriteCoord (msg, newstate->origin[2], pext1);\n\t\t\t\tif (bits & UNQ_ANGLE3)\n\t\t\t\t\tWriteAngle(msg, newstate->angles[2], pext1);\n\t\t\t}\n\t\t}\n\t}\n\telse\n\t{\n\t\tWriteByte(msg, svc_nqsetview);\n\t\tWriteShort(msg, v->thisplayer+1);\n\n\t\tWriteShort (msg,UNQ_MOREBITS|UNQ_MODEL|UNQ_ORIGIN1 | UNQ_ORIGIN2 | UNQ_ORIGIN3 | UNQ_SIGNAL);\n\t\tWriteByte (msg, v->thisplayer+1);\n\t\tWriteByte (msg, 2);\t//model\n\t\tWriteCoord (msg, v->origin[0], pext1);\n\t\tWriteCoord (msg, v->origin[1], pext1);\n\t\tWriteCoord (msg, v->origin[2], pext1);\n\t}\n}\n\n//returns self, or the final commentator\nviewer_t *GetCommentator(viewer_t *v)\n{\n\tviewer_t *orig = v;\n\tint runaway = 10;\n\n\twhile(runaway-- > 0)\n\t{\n\t\tif (!v->commentator)\n\t\t\tbreak;\n\t\tif (v->commentator->thinksitsconnected == false)\n\t\t\tbreak;\n\t\tif (v->commentator->server != orig->server)\n\t\t\tbreak;\n\t\tv = v->commentator;\n\t}\n\treturn v;\n}\n\nvoid SendPlayerStates(sv_t *tv, viewer_t *v, netmsg_t *msg)\n{\n\tbsp_t *bsp = (!tv || tv->controller == v ? NULL : tv->map.bsp);\n\tviewer_t *cv;\n\tpacket_entities_t *e;\n\tint i;\n\tusercmd_t to;\n\tunsigned short flags;\n\tfloat interp;\n\tfloat lerp;\n\tint track;\n\n\tint snapdist = 128;\t//in quake units\n\n\tsnapdist = snapdist*8;\n\tsnapdist = snapdist*snapdist;\n\n\n\tmemset(&to, 0, sizeof(to));\n\n\tif (tv)\n\t{\n\t\tif (tv->physicstime != v->settime)// && tv->cluster->chokeonnotupdated)\n\t\t{\n\t\t\tWriteByte(msg, svc_updatestatlong);\n\t\t\tWriteByte(msg, STAT_TIME);\n\t\t\tWriteLong(msg, v->settime);\n\n\t\t\tv->settime = tv->physicstime;\n\t\t}\n\n\t\tBSP_SetupForPosition(bsp, v->origin[0], v->origin[1], v->origin[2]);\n\n\t\tlerp = ((tv->simtime - tv->oldpackettime)/1000.0f) / ((tv->nextpackettime - tv->oldpackettime)/1000.0f);\n\t\tif (lerp < 0)\n\t\t\tlerp = 0;\n\t\tif (lerp > 1)\n\t\t\tlerp = 1;\n\n\t\tif (tv->controller == v)\n\t\t{\n\t\t\tlerp = 1;\n\t\t\ttrack = tv->map.thisplayer;\n\t\t\tv->trackplayer = tv->map.thisplayer;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tcv = GetCommentator(v);\n\t\t\ttrack = cv->trackplayer;\n\n\t\t\tif (cv != v && track < 0)\n\t\t\t{\t//following a commentator\n\t\t\t\ttrack = MAX_CLIENTS-2;\n\t\t\t}\n\n\t\t\tif (v->trackplayer != track)\n\t\t\t\tQW_StuffcmdToViewer (v, \"track %i\\n\", track);\n\n\t\t\tif (!v->commentator && track >= 0 && !v->backbuffered)\n\t\t\t{\n\t\t\t\tif (v->trackplayer != tv->map.trackplayer && tv->usequakeworldprotocols)\n\t\t\t\t\tif (!tv->map.players[v->trackplayer].active && tv->map.players[tv->map.trackplayer].active)\n\t\t\t\t\t{\n\t\t\t\t\t\tQW_StuffcmdToViewer (v, \"track %i\\n\", tv->map.trackplayer);\n\t\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tfor (i = 0; i < MAX_CLIENTS; i++)\n\t\t{\n\t\t\tif (i == v->thisplayer)\n\t\t\t{\n\t\t\t\tSendLocalPlayerState(tv, v, i, msg);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (!tv->map.players[i].active && track != i)\n\t\t\t\tcontinue;\n\n\t\t\t//bsp cull. currently tracked player is always visible\n\t\t\tif (track != i && !BSP_Visible(bsp, tv->map.players[i].leafcount, tv->map.players[i].leafs))\n\t\t\t\tcontinue;\n\n\t\t\tflags = PF_COMMAND;\n\t\t\tif (track == i && tv->map.players[i].current.weaponframe)\n\t\t\t\tflags |= PF_WEAPONFRAME;\n\t\t\tif (tv->map.players[i].current.modelindex != tv->map.modelindex_player)\n\t\t\t\tflags |= PF_MODEL;\n\t\t\tif (tv->map.players[i].dead || !tv->map.players[i].active)\n\t\t\t\tflags |= PF_DEAD;\n\t\t\tif (tv->map.players[i].gibbed || !tv->map.players[i].active)\n\t\t\t\tflags |= PF_GIB;\n\t\t\tif (tv->map.players[i].current.effects != 0)\n\t\t\t\tflags |= PF_EFFECTS;\n\t\t\tif (tv->map.players[i].current.skinnum != 0)\n\t\t\t\tflags |= PF_SKINNUM;\n\t\t\tif (tv->map.players[i].current.velocity[0])\n\t\t\t\tflags |= PF_VELOCITY1;\n\t\t\tif (tv->map.players[i].current.velocity[1])\n\t\t\t\tflags |= PF_VELOCITY2;\n\t\t\tif (tv->map.players[i].current.velocity[2])\n\t\t\t\tflags |= PF_VELOCITY3;\n\n\t\t\tWriteByte(msg, svc_playerinfo);\n\t\t\tWriteByte(msg, i);\n\t\t\tWriteShort(msg, flags);\n\n\t\t\tif (!tv->map.players[i].active || !tv->map.players[i].oldactive ||\n\t\t\t\t(tv->map.players[i].current.origin[0] - tv->map.players[i].old.origin[0])*(tv->map.players[i].current.origin[0] - tv->map.players[i].old.origin[0]) > snapdist ||\n\t\t\t\t(tv->map.players[i].current.origin[1] - tv->map.players[i].old.origin[1])*(tv->map.players[i].current.origin[1] - tv->map.players[i].old.origin[1]) > snapdist ||\n\t\t\t\t(tv->map.players[i].current.origin[2] - tv->map.players[i].old.origin[2])*(tv->map.players[i].current.origin[2] - tv->map.players[i].old.origin[2]) > snapdist)\n\t\t\t{\t//teleported (or respawned), so don't interpolate\n\t\t\t\tWriteCoord(msg, tv->map.players[i].current.origin[0], tv->pext1);\n\t\t\t\tWriteCoord(msg, tv->map.players[i].current.origin[1], tv->pext1);\n\t\t\t\tWriteCoord(msg, tv->map.players[i].current.origin[2], tv->pext1);\n\t\t\t}\n\t\t\telse\n\t\t\t{\t//send interpolated angles\n\t\t\t\tinterp = (lerp)*tv->map.players[i].current.origin[0] + (1-lerp)*tv->map.players[i].old.origin[0];\n\t\t\t\tWriteCoord(msg, interp, tv->pext1);\n\t\t\t\tinterp = (lerp)*tv->map.players[i].current.origin[1] + (1-lerp)*tv->map.players[i].old.origin[1];\n\t\t\t\tWriteCoord(msg, interp, tv->pext1);\n\t\t\t\tinterp = (lerp)*tv->map.players[i].current.origin[2] + (1-lerp)*tv->map.players[i].old.origin[2];\n\t\t\t\tWriteCoord(msg, interp, tv->pext1);\n\t\t\t}\n\n\t\t\tWriteByte(msg, tv->map.players[i].current.frame);\n\n\n\t\t\tif (flags & PF_MSEC)\n\t\t\t{\n\t\t\t\tWriteByte(msg, 0);\n\t\t\t}\n\t\t\tif (flags & PF_COMMAND)\n\t\t\t{\n\t\t\t\tif (!tv->map.players[i].active || !tv->map.players[i].oldactive)\n\t\t\t\t{\n\t\t\t\t\tto.angles[0] = tv->map.players[i].current.angles[0];\n\t\t\t\t\tto.angles[1] = tv->map.players[i].current.angles[1];\n\t\t\t\t\tto.angles[2] = tv->map.players[i].current.angles[2];\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tto.angles[0] = InterpolateAngle(tv->map.players[i].old.angles[0], tv->map.players[i].current.angles[0], lerp);\n\t\t\t\t\tto.angles[1] = InterpolateAngle(tv->map.players[i].old.angles[1], tv->map.players[i].current.angles[1], lerp);\n\t\t\t\t\tto.angles[2] = InterpolateAngle(tv->map.players[i].old.angles[2], tv->map.players[i].current.angles[2], lerp);\n\t\t\t\t}\n\t\t\t\tWriteDeltaUsercmd(msg, &nullcmd, &to);\n\t\t\t}\n\t\t\t//vel\n\t\t\tif (flags & PF_VELOCITY1)\n\t\t\t\tWriteShort(msg, tv->map.players[i].current.velocity[0]);\n\t\t\tif (flags & PF_VELOCITY2)\n\t\t\t\tWriteShort(msg, tv->map.players[i].current.velocity[1]);\n\t\t\tif (flags & PF_VELOCITY3)\n\t\t\t\tWriteShort(msg, tv->map.players[i].current.velocity[2]);\n\t\t\t//model\n\t\t\tif (flags & PF_MODEL)\n\t\t\t\tWriteByte(msg, tv->map.players[i].current.modelindex);\n\t\t\t//skin\n\t\t\tif (flags & PF_SKINNUM)\n\t\t\t\tWriteByte (msg, tv->map.players[i].current.skinnum);\n\t\t\t//effects\n\t\t\tif (flags & PF_EFFECTS)\n\t\t\t\tWriteByte (msg, tv->map.players[i].current.effects);\n\t\t\t//weaponframe\n\t\t\tif (flags & PF_WEAPONFRAME)\n\t\t\t\tWriteByte(msg, tv->map.players[i].current.weaponframe);\n\t\t}\n\t}\n\telse\n\t{\n\t\tlerp = 1;\n\n\t\tSendLocalPlayerState(tv, v, v->thisplayer, msg);\n\t}\n\n\n\te = &v->frame[v->netchan.outgoing_sequence&(ENTITY_FRAMES-1)];\n\te->numents = 0;\n\tif (tv)\n\t{\n\t\tint oldindex = 0, newindex = 0;\n\t\tentity_state_t *newstate;\n\t\tint newnum, oldnum;\n\t\tframe_t *frompacket, *topacket;\n\t\ttopacket = &tv->map.frame[tv->netchan.incoming_sequence&(ENTITY_FRAMES-1)];\n\t\tif (tv->usequakeworldprotocols)\n\t\t{\n\t\t\tfrompacket = &tv->map.frame[(topacket->oldframe)&(ENTITY_FRAMES-1)];\n\t\t}\n\t\telse\n\t\t{\n\t\t\tfrompacket = &tv->map.frame[(tv->netchan.incoming_sequence-1)&(ENTITY_FRAMES-1)];\n\t\t}\n\n\t\tfor (newindex = 0; newindex < topacket->numents; newindex++)\n\t\t{\n\t\t\t//don't pvs cull bsp models\n\t\t\t//pvs cull everything else\n\t\t\tnewstate = &topacket->ents[newindex];\n\t\t\tnewnum = topacket->entnums[newindex];\n\t\t\tif (newstate->modelindex >= tv->map.numinlines && !BSP_Visible(bsp, tv->map.entity[newnum].leafcount, tv->map.entity[newnum].leafs))\n\t\t\t\tcontinue;\n\n\t\t\te->entnum[e->numents] = newnum;\n\t\t\tmemcpy(&e->ents[e->numents], newstate, sizeof(entity_state_t));\n\n\t\t\tif (frompacket != topacket)\t//optimisation for qw protocols\n\t\t\t{\n\t\t\t\tentity_state_t *oldstate;\n\n\t\t\t\tif (oldindex < frompacket->numents)\n\t\t\t\t{\n\t\t\t\t\toldnum = frompacket->entnums[oldindex];\n\n\t\t\t\t\twhile(oldnum < newnum)\n\t\t\t\t\t{\n\t\t\t\t\t\toldindex++;\n\t\t\t\t\t\tif (oldindex >= frompacket->numents)\n\t\t\t\t\t\t\tbreak;\t//no more\n\t\t\t\t\t\toldnum = frompacket->entnums[oldindex];\n\t\t\t\t\t}\n\t\t\t\t\tif (oldnum == newnum)\n\t\t\t\t\t{\n\t\t\t\t\t\t//ent exists in old packet\n\t\t\t\t\t\toldstate = &frompacket->ents[oldindex];\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\toldstate = newstate;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\t//reached end, definatly not in packet\n\t\t\t\t\toldstate = newstate;\n\t\t\t\t}\n\n\n\t\t\t\tif ((newstate->origin[0] - oldstate->origin[0])*(newstate->origin[0] - oldstate->origin[0]) > snapdist ||\n\t\t\t\t\t(newstate->origin[1] - oldstate->origin[1])*(newstate->origin[1] - oldstate->origin[1]) > snapdist ||\n\t\t\t\t\t(newstate->origin[2] - oldstate->origin[2])*(newstate->origin[2] - oldstate->origin[2]) > snapdist)\n\t\t\t\t{\t//teleported (or respawned), so don't interpolate\n\t\t\t\t\te->ents[e->numents].origin[0] = newstate->origin[0];\n\t\t\t\t\te->ents[e->numents].origin[1] = newstate->origin[1];\n\t\t\t\t\te->ents[e->numents].origin[2] = newstate->origin[2];\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\te->ents[e->numents].origin[0] = (lerp)*newstate->origin[0] + (1-lerp)*oldstate->origin[0];\n\t\t\t\t\te->ents[e->numents].origin[1] = (lerp)*newstate->origin[1] + (1-lerp)*oldstate->origin[1];\n\t\t\t\t\te->ents[e->numents].origin[2] = (lerp)*newstate->origin[2] + (1-lerp)*oldstate->origin[2];\n\t\t\t\t}\n\t\t\t}\n\n\t\t\te->numents++;\n\n\t\t\tif (e->numents == ENTS_PER_FRAME)\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\tSV_EmitPacketEntities(tv, v, e, msg);\n\n\tif (tv && tv->map.nailcount)\n\t{\n\t\tWriteByte(msg, svc_nails);\n\t\tWriteByte(msg, tv->map.nailcount);\n\t\tfor (i = 0; i < tv->map.nailcount; i++)\n\t\t{\n\t\t\tWriteByte(msg, tv->map.nails[i].bits[0]);\n\t\t\tWriteByte(msg, tv->map.nails[i].bits[1]);\n\t\t\tWriteByte(msg, tv->map.nails[i].bits[2]);\n\t\t\tWriteByte(msg, tv->map.nails[i].bits[3]);\n\t\t\tWriteByte(msg, tv->map.nails[i].bits[4]);\n\t\t\tWriteByte(msg, tv->map.nails[i].bits[5]);\n\t\t}\n\t}\n}\n\nvoid UpdateStats(sv_t *qtv, viewer_t *v)\n{\n\tviewer_t *cv;\n\tnetmsg_t msg;\n\tchar buf[6];\n\tint i;\n\tstatic const unsigned int nullstats[MAX_STATS] = {1000};\n\n\tconst unsigned int *stats;\n\n\tInitNetMsg(&msg, buf, sizeof(buf));\n\n\tif (v->commentator && v->thinksitsconnected)\n\t\tcv = v->commentator;\n\telse\n\t\tcv = v;\n\n\tif (qtv && qtv->controller == cv)\n\t\tstats = qtv->map.players[qtv->map.thisplayer].stats;\n\telse if (cv->trackplayer == -1 || !qtv)\n\t\tstats = nullstats;\n\telse\n\t\tstats = qtv->map.players[cv->trackplayer].stats;\n\n\tfor (i = 0; i < MAX_STATS; i++)\n\t{\n\t\tif (v->currentstats[i] != stats[i])\n\t\t{\n\t\t\tif (v->netchan.isnqprotocol)\n\t\t\t{\t//nq only supports 32bit stats\n\t\t\t\tWriteByte(&msg, svc_updatestat);\n\t\t\t\tWriteByte(&msg, i);\n\t\t\t\tWriteLong(&msg, stats[i]);\n\t\t\t}\n\t\t\telse if (stats[i] < 256)\n\t\t\t{\n\t\t\t\tWriteByte(&msg, svc_updatestat);\n\t\t\t\tWriteByte(&msg, i);\n\t\t\t\tWriteByte(&msg, stats[i]);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tWriteByte(&msg, svc_updatestatlong);\n\t\t\t\tWriteByte(&msg, i);\n\t\t\t\tWriteLong(&msg, stats[i]);\n\t\t\t}\n\t\t\tSendBufferToViewer(v, msg.data, msg.cursize, true);\n\t\t\tmsg.cursize = 0;\n\t\t\tv->currentstats[i] = stats[i];\n\t\t}\n\t}\n}\n\n//returns the next prespawn 'buffer' number to use, or -1 if no more\n//FIXME: viewer may support fewer/different extensions vs the the stream.\nint Prespawn(sv_t *qtv, int curmsgsize, netmsg_t *msg, int bufnum, int thisplayer)\n{\n\tint r, ni;\n\tr = bufnum;\n\n\tni = SendCurrentUserinfos(qtv, curmsgsize, msg, bufnum, thisplayer);\n\tr += ni - bufnum;\n\tbufnum = ni;\n\tbufnum -= MAX_CLIENTS;\n\n\tni = SendCurrentBaselines(qtv, curmsgsize, msg, 768, bufnum);\n\tr += ni - bufnum;\n\tbufnum = ni;\n\tbufnum -= MAX_ENTITIES;\n\n\tni = SendCurrentLightmaps(qtv, curmsgsize, msg, 768, bufnum);\n\tr += ni - bufnum;\n\tbufnum = ni;\n\tbufnum -= MAX_LIGHTSTYLES;\n\n\tni = SendStaticSounds(qtv, curmsgsize, msg, 768, bufnum);\n\tr += ni - bufnum;\n\tbufnum = ni;\n\tbufnum -= MAX_STATICSOUNDS;\n\n\tni = SendStaticEntities(qtv, curmsgsize, msg, 768, bufnum);\n\tr += ni - bufnum;\n\tbufnum = ni;\n\tbufnum -= MAX_STATICENTITIES;\n\n\tif (bufnum == 0)\n\t\treturn -1;\n\n\treturn r;\n}\n\nvoid PMove(viewer_t *v, usercmd_t *cmd)\n{\n\tsv_t *qtv;\n\tpmove_t pmove;\n\tif (v->server && v->server->controller == v)\n\t{\n\t\tv->origin[0] = v->server->map.players[v->server->map.thisplayer].current.origin[0];\n\t\tv->origin[1] = v->server->map.players[v->server->map.thisplayer].current.origin[1];\n\t\tv->origin[2] = v->server->map.players[v->server->map.thisplayer].current.origin[2];\n\n\t\tv->velocity[0] = v->server->map.players[v->server->map.thisplayer].current.velocity[0];\n\t\tv->velocity[1] = v->server->map.players[v->server->map.thisplayer].current.velocity[1];\n\t\tv->velocity[2] = v->server->map.players[v->server->map.thisplayer].current.velocity[2];\n\t\treturn;\n\t}\n\tpmove.origin[0] = v->origin[0];\n\tpmove.origin[1] = v->origin[1];\n\tpmove.origin[2] = v->origin[2];\n\n\tpmove.velocity[0] = v->velocity[0];\n\tpmove.velocity[1] = v->velocity[1];\n\tpmove.velocity[2] = v->velocity[2];\n\n\tpmove.cmd = *cmd;\n\tqtv = v->server;\n\tif (qtv)\n\t{\n\t\tpmove.movevars = qtv->map.movevars;\n\t}\n\telse\n\t{\n\t\tQTV_DefaultMovevars(&pmove.movevars);\n\t}\n\tPM_PlayerMove(&pmove);\n\n\tv->origin[0] = pmove.origin[0];\n\tv->origin[1] = pmove.origin[1];\n\tv->origin[2] = pmove.origin[2];\n\n\tv->velocity[0] = pmove.velocity[0];\n\tv->velocity[1] = pmove.velocity[1];\n\tv->velocity[2] = pmove.velocity[2];\n}\n\nvoid QW_SetCommentator(cluster_t *cluster, viewer_t *v, viewer_t *commentator)\n{\n//\tif (v->commentator == commentator)\n//\t\treturn;\n\n\tWriteByte(&v->netchan.message, svc_setinfo);\n\tWriteByte(&v->netchan.message, MAX_CLIENTS-2);\n\tWriteString(&v->netchan.message, \"name\");\n\tif (commentator)\n\t{\n\t\tWriteString(&v->netchan.message, commentator->name);\n\t\tQW_StuffcmdToViewer(v, \"track %i\\n\", MAX_CLIENTS-2);\n\t\tQW_PrintfToViewer(v, \"Following commentator %s\\n\", commentator->name);\n\n\t\tif (v->server != commentator->server)\n\t\t\tQW_SetViewersServer(cluster, v, commentator->server);\n\t}\n\telse\n\t{\n\t\tWriteString(&v->netchan.message, \"\");\n\t\tif (v->commentator )\n\t\t\tQW_PrintfToViewer(v, \"Commentator disabled\\n\");\n\t}\n\tv->commentator = commentator;\n}\n\nvoid QTV_SayCommand(cluster_t *cluster, sv_t *qtv, viewer_t *v, char *fullcommand)\n{\n\tchar command[256];\n\tchar *args;\n\targs = COM_ParseToken(fullcommand, command, sizeof(command), NULL);\n\tif (!args)\n\t\targs = \"\";\n\twhile(*args && *args <= ' ')\n\t\targs++;\n\n#pragma message(\"fixme: These all need testing\")\n\tif (!strcmp(command, \"help\"))\n\t{\n\t\tQW_PrintfToViewer(v,\t\"Website: \"PROXYWEBSITE\"\\n\"\n\t\t\t\t\t\"Commands:\\n\"\n\t\t\t\t\t\".bind\\n\"\n\t\t\t\t\t\"  Bind your keys to drive the menu.\\n\"\n\t\t\t\t\t\".clients\\n\"\n\t\t\t\t\t\"  Lists the users connected to this\\n\"\n\t\t\t\t\t\"  proxy.\\n\"\n\t\t\t\t\t\".qtvinfo\\n\"\n\t\t\t\t\t\"  Print info about the current QTV\\n\"\n\t\t\t\t\t\"  you're on.\\n\"\n\t\t\t\t\t\".demo gamedir/demoname.mvd \\n\"\n\t\t\t\t\t\"  Start a new stream on the specified\\n\"\n\t\t\t\t\t\"  demo.\\n\"\n\t\t\t\t\t\".disconnect\\n\"\n\t\t\t\t\t\"  Disconnect from any server or\\n\"\n\t\t\t\t\t\"  stream you're on.\\n\"\n\t\t\t\t\t\".join qwserver:port\\n\"\n\t\t\t\t\t\"  Play on the specified server.\\n\"\n\t\t\t\t\t\".observe qwserver:port\\n\"\n\t\t\t\t\t\"  Spectate on the specified server.\\n\"\n\t\t\t\t\t\".qtv tcpserver:port\\n\"\n\t\t\t\t\t\"  Start a new stream on the specified\\n\"\n\t\t\t\t\t\"  server.\\n\"\n\t\t\t\t\t\".guimenu\\n\"\n\t\t\t\t\t\"  Bring up the GUI-based menu\\n\"\n\t\t\t\t\t\"  interface.\\n\"\n\t\t\t\t\t\".tuimenu\\n\"\n\t\t\t\t\t\"  Bring up the text-based menu\\n\"\n\t\t\t\t\t\"  interface.\\n\"\n\t\t\t\t\t\".menu\\n\"\n\t\t\t\t\t\"  Automatically chooses an interface\\n\"\n\t\t\t\t\t\"  that your client supports.\\n\"\n\t\t\t\t\t\".admin\\n\"\n\t\t\t\t\t\"  Log in to administrate this QTV\\n\"\n\t\t\t\t\t\"  proxy.\\n\"\n\t\t\t\t);\n\t}\n\telse if (!strcmp(command, \"qtvinfo\"))\n\t{\n\t\tchar buf[256];\n\t\tnetadr_t addr;\n\t\tunsigned char *ip;\n\t\tgethostname(buf, sizeof(buf));\t//ask the operating system for the local dns name\n\t\tNET_StringToAddr(buf, &addr, 0);\t//look that up\n\t\tip = (unsigned char*)&((struct sockaddr_in *)&addr)->sin_addr;\n\t\tQW_PrintfToViewer(v, \"[QuakeTV] %s | %i.%i.%i.%i\\n\", cluster->hostname, ip[0], ip[1], ip[2], ip[3]);\n\t}\n\telse if (!strcmp(command, \"menu\"))\n\t{\n\t\tv->menuspamtime = cluster->curtime-1;\n\n\t\tCOM_ParseToken(args, command, sizeof(command), NULL);\n\t\tif (!strcmp(command, \"up\"))\n\t\t{\n\t\t\tv->menuop -= 1;\n\t\t}\n\t\telse if (!strcmp(command, \"down\"))\n\t\t{\n\t\t\tv->menuop += 1;\n\t\t}\n\t\telse if (!strcmp(command, \"enter\"))\n\t\t{\n\t\t\tMenu_Enter(cluster, v, 0);\n\t\t}\n\t\telse if (!strcmp(command, \"use\"))\n\t\t{\n\t\t\tMenu_Enter(cluster, v, 0);\n\t\t}\n\t\telse if (!strcmp(command, \"right\"))\n\t\t{\n\t\t\tMenu_Enter(cluster, v, 1);\n\t\t}\n\t\telse if (!strcmp(command, \"left\"))\n\t\t{\n\t\t\tMenu_Enter(cluster, v, -1);\n\t\t}\n\t\telse if (!strcmp(command, \"select\"))\n\t\t{\n\t\t\tMenu_Enter(cluster, v, 0);\n\t\t}\n\t\telse if (!strcmp(command, \"home\"))\n\t\t{\n\t\t\tv->menuop -= 100000;\n\t\t}\n\t\telse if (!strcmp(command, \"end\"))\n\t\t{\n\t\t\tv->menuop += 100000;\n\t\t}\n\t\telse if (!strcmp(command, \"back\"))\n\t\t{\n\t\t\tQW_SetMenu(v, MENU_DEFAULT);\n\t\t}\n\t\telse if (!strcmp(command, \"enter\"))\n\t\t{\n\t\t\tif (v->menunum)\n\t\t\t\tMenu_Enter(cluster, v, 0);\n\t\t\telse\n\t\t\t\tQW_SetMenu(v, MENU_SERVERS);\n\t\t}\n\t\telse if (!strcmp(command, \"bind\") || !strcmp(command, \"bindstd\"))\n\t\t{\n\t\t\tQW_StuffcmdToViewer(v, \"bind uparrow \\\"say proxy:menu up\\\"\\n\");\n\t\t\tQW_StuffcmdToViewer(v, \"bind downarrow \\\"say proxy:menu down\\\"\\n\");\n\t\t\tQW_StuffcmdToViewer(v, \"bind rightarrow \\\"say proxy:menu right\\\"\\n\");\n\t\t\tQW_StuffcmdToViewer(v, \"bind leftarrow \\\"say proxy:menu left\\\"\\n\");\n\n\t\t\tQW_StuffcmdToViewer(v, \"bind enter \\\"say proxy:menu select\\\"\\n\");\n\n\t\t\tQW_StuffcmdToViewer(v, \"bind home \\\"say proxy:menu home\\\"\\n\");\n\t\t\tQW_StuffcmdToViewer(v, \"bind end \\\"say proxy:menu end\\\"\\n\");\n\t\t\tQW_StuffcmdToViewer(v, \"bind pause \\\"say proxy:menu\\\"\\n\");\n\t\t\tQW_StuffcmdToViewer(v, \"bind backspace \\\"say proxy:menu back\\\"\\n\");\n\n\t\t\tQW_PrintfToViewer(v, \"All keys bound\\n\");\n\t\t}\n\t\telse if (!*command)\n\t\t{\n\t\t\tif (v->menunum)\n\t\t\t\tQW_SetMenu(v, MENU_NONE);\n\t\t\telse if (v->conmenussupported)\n\t\t\t\tgoto guimenu;\n\t\t\telse\n\t\t\t\tgoto tuimenu;\n\t\t}\n\t\telse\n\t\t\tQW_PrintfToViewer(v, \"\\\"menu %s\\\" not recognised\\n\", command);\n\t}\n\n\telse if (!strcmp(command, \"tuimenu\"))\n\t{\ntuimenu:\n\t\tif (v->menunum)\n\t\t\tQW_SetMenu(v, MENU_NONE);\n\t\telse\n\t\t\tQW_SetMenu(v, MENU_MAIN);\n\t}\n\telse if (!strcmp(command, \"guimenu\"))\n\t{\n\t\tsv_t *sv;\n\t\tint y;\n\t\tqboolean shownheader;\n\nguimenu:\n\n\t\tQW_SetMenu(v, MENU_NONE);\n\n\t\tshownheader = false;\n\n/*\nI've removed the following from this function as it covered the menu (~Moodles):\n\t\t\t\"menupic 0 4 gfx/qplaque.lmp\\n\"\n\t\t\t\"menupic 96 4 gfx/p_option.lmp\\n\"\n*/\n\t\tQW_StuffcmdToViewer(v,\n\n\t\t\t\"alias menucallback\\n\"\n\t\t\t\"{\\n\"\n\t\t\t\t\"menuclear\\n\"\n\t\t\t\t\"if (option == \\\"OBSERVE\\\")\\n\"\n\t\t\t\t\t\"{\\necho Spectating server $_server\\nsay .observe $_server\\n}\\n\"\n\t\t\t\t\"if (option == \\\"QTV\\\")\\n\"\n\t\t\t\t\t\"{\\necho Streaming from qtv at $_server\\nsay .qtv $_server\\n}\\n\"\n\t\t\t\t\"if (option == \\\"JOIN\\\")\\n\"\n\t\t\t\t\t\"{\\necho Joining game at $_server\\nsay .join $_server\\n}\\n\"\n\t\t\t\t\"if (option == \\\"ADMIN\\\")\\n\"\n\t\t\t\t\t\"{\\nsay .guiadmin\\n}\\n\"\n\t\t\t\t\"if (option == \\\"DEMOS\\\")\\n\"\n\t\t\t\t\t\"{\\nsay .demos\\n}\\n\"\n\t\t\t\t\"if (\\\"stream \\\" isin option)\\n\"\n\t\t\t\t\t\"{\\necho Changing stream\\nsay .$option\\n}\\n\"\n\t\t\t\"}\\n\"\n\n\t\t\t\"conmenu menucallback\\n\"\n\n\t\t\t\"menuedit 48 36 \\\"^aServer:\\\" \\\"_server\\\"\\n\"\n\n\t\t\t\"menutext 48 52 \\\"Demos\\\" DEMOS\\n\"\n\n\t\t\t\"menutext 104 52 \\\"Join\\\" JOIN\\n\"\n\n\t\t\t\"menutext 152 52 \\\"Observe\\\" OBSERVE\\n\"\n\n\t\t\t\"menutext 224 52 \\\"QTV\\\" QTV\\n\"\n\n\n\n\t\t\t\"menutext 48 84 \\\"Admin\\\" ADMIN\\n\"\n\n\t\t\t\"menutext 48 92 \\\"Close Menu\\\" cancel\\n\"\n\n\n\n\t\t\t\"menutext 48 116 \\\"Type in a server address and\\\"\\n\"\n\t\t\t\"menutext 48 124 \\\"click join to play in the game,\\\"\\n\"\n\t\t\t\"menutext 48 132 \\\"observe(udp) to watch, or qtv(tcp)\\\"\\n\"\n\t\t\t\"menutext 48 140 \\\"to connect to a stream or proxy.\\\"\\n\"\n\t\t\t);\n\n\t\ty = 140+16;\n\t\tfor (sv = cluster->servers; sv; sv = sv->next)\n\t\t{\n\t\t\tif (!shownheader)\n\t\t\t{\n\t\t\t\tshownheader = true;\n\n\t\t\t\tQW_StuffcmdToViewer(v, \"menutext 72 %i \\\"^aActive Games:\\\"\\n\", y);\n\t\t\t\ty+=8;\n\t\t\t}\n\t\t\tQW_StuffcmdToViewer(v, \"menutext 32 %i \\\"%30s\\\" \\\"stream %i\\\"\\n\", y, *sv->map.hostname?sv->map.hostname:sv->server, sv->streamid);\n\t\t\ty+=8;\n\t\t}\n\t\tif (!shownheader)\n\t\t\tQW_StuffcmdToViewer(v, \"menutext 72 %i \\\"There are no active games\\\"\\n\", y);\n\n\t}\n\n\telse if (!strcmp(command, \"demos\"))\n\t{\n\t\tif (v->conmenussupported)\n\t\t\tgoto guidemos;\n\t\telse\n\t\t\tgoto tuidemos;\n\t}\n\telse if (!strcmp(command, \"guidemos\"))\n\t{\n\t\tint maxshowndemos;\n\t\tchar sizestr[13];\n\t\tint start;\n\t\tint i;\n\nguidemos:\n\t\tmaxshowndemos = 12;\n\n\t\tif (!*args)\n\t\t\tCluster_BuildAvailableDemoList(cluster);\n\n\t\tstart = atoi(args);\t//FIXME\n\t\tQW_SetMenu(v, MENU_NONE);\n\n/*\nI've removed the following from this function as it covered the menu (~Moodles):\n\t\t\t\"menupic 0 4 gfx/qplaque.lmp\\n\"\n\t\t\t\"menupic 96 4 gfx/p_option.lmp\\n\"\n*/\n\t\tQW_StuffcmdToViewer(v,\n\n\t\t\t\"alias menucallback\\n\"\n\t\t\t\"{\\n\"\n\t\t\t\t\"menuclear\\n\"\n\t\t\t\t\"if (option == \\\"PREV\\\")\\n\"\n\t\t\t\t\t\"{\\nsay .demos %i\\n}\\n\"\n\t\t\t\t\"if (option == \\\"NEXT\\\")\\n\"\n\t\t\t\t\t\"{\\nsay .demos %i\\n}\\n\"\n\t\t\t\t\"if (\\\"demo \\\" isin option)\\n\"\n\t\t\t\t\t\"{\\necho Changing stream\\nsay .$option\\n}\\n\"\n\t\t\t\"}\\n\"\n\n\t\t\t\"conmenu menucallback\\n\",\n\t\t\tstart - maxshowndemos, start + maxshowndemos\n\t\t);\n\n\t\tif (start < 0)\n\t\t\tstart = 0;\n\n\t\tif (start-maxshowndemos >= 0)\n\t\t\tQW_StuffcmdToViewer(v, \"menutext 48 52 \\\"Prev\\\" \\\"PREV\\\"\\n\");\n\t\tif (start+maxshowndemos <= cluster->availdemoscount)\n\t\t\tQW_StuffcmdToViewer(v, \"menutext 152 52 \\\"Next\\\" \\\"NEXT\\\"\\n\");\n\n\t\tfor (i = start; i < start+maxshowndemos; i++)\n\t\t{\n\t\t\tif (i >= cluster->availdemoscount)\n\t\t\t\tbreak;\n\t\t\tif (cluster->availdemos[i].size < 1024)\n\t\t\t\tsnprintf(sizestr, sizeof(sizestr), \"%4ib\", cluster->availdemos[i].size);\n\t\t\telse if (cluster->availdemos[i].size < 1024*1024)\n\t\t\t\tsnprintf(sizestr, sizeof(sizestr), \"%4ikb\", cluster->availdemos[i].size/1024);\n\t\t\telse if (cluster->availdemos[i].size < 1024*1024*1024)\n\t\t\t\tsnprintf(sizestr, sizeof(sizestr), \"%4imb\", cluster->availdemos[i].size/(1024*1024));\n\t\t\telse// if (cluster->availdemos[i].size < 1024*1024*1024*1024)\n\t\t\t\tsnprintf(sizestr, sizeof(sizestr), \"%4igb\", cluster->availdemos[i].size/(1024*1024*1024));\n//\t\t\telse\n//\t\t\t\t*sizestr = 0;\n\t\t\tQW_StuffcmdToViewer(v, \"menutext 32 %i \\\"%6s %-30s\\\" \\\"demo %s\\\"\\n\", (i-start)*8 + 52+16, sizestr, cluster->availdemos[i].name, cluster->availdemos[i].name);\n\t\t}\n\t}\n\telse if (!strncmp(command, \".tuidemos\", 9))\n\t{\ntuidemos:\n\t\tif (!*args)\n\t\t\tCluster_BuildAvailableDemoList(cluster);\n\n\t\tif (v->menunum == MENU_DEMOS)\n\t\t\tQW_SetMenu(v, MENU_NONE);\n\t\telse\n\t\t\tQW_SetMenu(v, MENU_DEMOS);\n\t}\n\n\telse if (!strcmp(command, \"admin\"))\n\t{\n\t\tif (v->conmenussupported)\n\t\t\tgoto guiadmin;\n\t\telse\n\t\t\tgoto tuiadmin;\n\t}\n\n\telse if (!strcmp(command, \"guiadmin\"))\n\t{\nguiadmin:\n\t\tif (!*cluster->adminpassword)\n\t\t{\n/*\nI've removed the following from this function as it covered the menu (~Moodles):\n\t\t\t\"menupic 16 4 gfx/qplaque.lmp\\n\"\n\t\t\t\"menupic - 4 gfx/p_option.lmp\\n\"\n*/\n\t\t\tQW_StuffcmdToViewer(v,\n\n\t\t\t\t\"alias menucallback\\n\"\n\t\t\t\t\"{\\n\"\n\t\t\t\t\t\"menuclear\\n\"\n\t\t\t\t\"}\\n\"\n\n\t\t\t\t\"conmenu menucallback\\n\"\n\n\t\t\t\t\"menutext 72 48 \\\"No admin password is set\\\"\\n\"\n\t\t\t\t\"menutext 72 56 \\\"Admin access is prohibited\\\"\\n\"\n\t\t\t\t);\n\t\t}\n\t\telse if (v->isadmin)\n\t\t\t//already an admin, so don't show admin login screen\n\t\t\tQW_SetMenu(v, MENU_ADMIN);\n\t\telse\n\t\t{\n/*\nI've removed the following from this function as it covered the menu (~Moodles):\n\t\t\t\t\"menupic 16 4 gfx/qplaque.lmp\\n\"\n\t\t\t\t\"menupic - 4 gfx/p_option.lmp\\n\"\n*/\n\t\t\tQW_StuffcmdToViewer(v,\n\n\t\t\t\t\"alias menucallback\\n\"\n\t\t\t\t\"{\\n\"\n\t\t\t\t\t\"menuclear\\n\"\n\t\t\t\t\t\"if (option == \\\"log\\\")\\n\"\n\t\t\t\t\t\t\"{\\nsay $_password\\n}\\n\"\n\t\t\t\t\t\"set _password \\\"\\\"\\n\"\n\t\t\t\t\"}\\n\"\n\n\t\t\t\t\"conmenu menucallback\\n\"\n\n\t\t\t\t\"menuedit 16 32 \\\"        Password\\\" \\\"_password\\\"\\n\"\n\n\t\t\t\t\"menutext 72 48 \\\"Log in QW\\\" log\\n\"\n\t\t\t\t\"menutext 192 48 \\\"Cancel\\\" cancel\\n\"\n\t\t\t\t);\n\n\t\t\tstrcpy(v->expectcommand, \"admin\");\n\t\t}\n\t}\n\n\telse if (!strcmp(command, \"tuiadmin\"))\n\t{\ntuiadmin:\n\t\tif (!*cluster->adminpassword)\n\t\t{\n\t\t\t/*if (Netchan_IsLocal(v->netchan.remote_address))\n\t\t\t{\n\t\t\t\tSys_Printf(cluster, \"Local player %s logs in as admin\\n\", v->name);\n\t\t\t\tQW_SetMenu(v, MENU_ADMIN);\n\t\t\t\tv->isadmin = true;\n\t\t\t}\n\t\t\telse*/\n\t\t\t\tQW_PrintfToViewer(v, \"There is no admin password set\\nYou may not log in.\\n\");\n\t\t}\n\t\telse if (v->isadmin)\n\t\t\tQW_SetMenu(v, MENU_ADMIN);\n\t\telse\n\t\t{\n\t\t\tstrcpy(v->expectcommand, \"admin\");\n\t\t\tQW_StuffcmdToViewer(v, \"echo Please enter the rcon password\\nmessagemode\\n\");\n\t\t}\n\t}\n\n\telse if (!strcmp(command, \"reset\"))\n\t{\n\t\tQW_SetCommentator(cluster, v, NULL);\n\t\tQW_SetViewersServer(cluster, v, NULL);\n\t\tQW_SetMenu(v, MENU_SERVERS);\n\t}\n\telse if (!strcmp(command, \"connect\") || !strcmp(command, \"qw\") || !strcmp(command, \"observe\") || !strcmp(command, \"join\"))\n\t{\n\t\tchar buf[256];\n\t\tint isjoin = false;\n\n\t\tif (!strcmp(command, \"join\") || !strcmp(command, \"connect\"))\n\t\t\tisjoin = true;\n\n\t\tsnprintf(buf, sizeof(buf), \"udp:%s\", args);\n\t\tqtv = QTV_NewServerConnection(cluster, 0, buf, \"\", false, AD_WHENEMPTY, !isjoin, false);\n\t\tif (qtv)\n\t\t{\n\t\t\tQW_SetMenu(v, MENU_NONE);\n\t\t\tQW_SetViewersServer(cluster, v, qtv);\n\t\t\tif (isjoin)\n\t\t\t\tqtv->controller = v;\n\t\t\tQW_PrintfToViewer(v, \"Connected to %s\\n\", qtv->server);\n\t\t}\n\t\telse if (cluster->nouserconnects)\n\t\t\tQW_PrintfToViewer(v, \"you may not do that here\\n\");\n\t\telse\n\t\t\tQW_PrintfToViewer(v, \"Failed to connect to server \\\"%s\\\", connection aborted\\n\", buf);\n\t}\n\telse if (!strcmp(command, \"qtv\"))\n\t{\n\t\tchar buf[256];\n\n\t\tsnprintf(buf, sizeof(buf), \"tcp:%s\", args);\n\t\tqtv = QTV_NewServerConnection(cluster, 0, buf, \"\", false, AD_WHENEMPTY, true, false);\n\t\tif (qtv)\n\t\t{\n\t\t\tQW_SetMenu(v, MENU_NONE);\n\t\t\tQW_SetViewersServer(cluster, v, qtv);\n\t\t\tQW_PrintfToViewer(v, \"Connected to %s\\n\", qtv->server);\n\t\t}\n\t\telse if (cluster->nouserconnects)\n\t\t\tQW_PrintfToViewer(v, \"Ask an admin to connect first\\n\");\n\t\telse\n\t\t\tQW_PrintfToViewer(v, \"Failed to connect to server \\\"%s\\\", connection aborted\\n\", buf);\n\t}\n\telse if (!strcmp(command, \"qtvinfo\"))\n\t{\n\t\tchar buf[256];\n\n\t\tsnprintf(buf, sizeof(buf), \"[QuakeTV] %s\\n\", qtv->server);\n\t\t// Print a short line with info about the server\n\t\tQW_PrintfToViewer(v, \"%s\", buf);\n\t}\n\telse if (!strcmp(command, \"stream\"))\n\t{\n\t\tint id;\n\t\tid = atoi(args);\n\t\tfor (qtv = cluster->servers; qtv; qtv = qtv->next)\n\t\t{\n\t\t\tif (qtv->streamid == id)\n\t\t\t{\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (qtv)\n\t\t{\n\t\t\tQW_SetMenu(v, MENU_NONE);\n\t\t\tQW_SetViewersServer(cluster, v, qtv);\n\t\t\tQW_PrintfToViewer(v, \"Watching to %s\\n\", qtv->server);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tQW_PrintfToViewer(v, \"Stream \\\"%s\\\" not recognised. Stream id is invalid or terminated.\\n\", args);\n\t\t}\n\t}\n\telse if (!strcmp(command, \"demo\"))\n\t{\n\t\tchar buf[256];\n\t\tsnprintf(buf, sizeof(buf), \"file:%s\", args);\n\t\tqtv = QTV_NewServerConnection(cluster, 0, buf, \"\", false, AD_WHENEMPTY, true, false);\n\t\tif (qtv)\n\t\t{\n\t\t\tQW_SetMenu(v, MENU_NONE);\n\t\t\tQW_SetViewersServer(cluster, v, qtv);\n\t\t\tQW_PrintfToViewer(v, \"Streaming from %s\\n\", qtv->server);\n\t\t}\n\t\telse\n\t\t\tQW_PrintfToViewer(v, \"Demo \\\"%s\\\" does not exist on proxy\\n\", buf);\n\t}\n\telse if (!strcmp(command, \"disconnect\"))\n\t{\n\t\tQW_SetMenu(v, MENU_SERVERS);\n\t\tQW_SetViewersServer(cluster, v, NULL);\n\t\tQW_PrintfToViewer(v, \"Connected\\n\");\n\t}\n\telse if (!strcmp(command, \"clients\"))\n\t{\n\t\tviewer_t *ov;\n\t\tint skipfirst = 0;\n\t\tint printable = 30;\n\t\tint remaining = 0;\n\t\tfor (ov = cluster->viewers; ov; ov = ov->next)\n\t\t{\n\t\t\tif (skipfirst > 0)\n\t\t\t{\n\t\t\t\tskipfirst--;\n\t\t\t}\n\t\t\telse if (printable > 0)\n\t\t\t{\n\t\t\t\tprintable--;\n\t\t\t\tif (ov->server)\n\t\t\t\t{\n\t\t\t\t\tif (ov->server->controller == ov)\n\t\t\t\t\t\tQW_PrintfToViewer(v, \"%i: %s: *%s\\n\", ov->userid, ov->name, ov->server->server);\n\t\t\t\t\telse\n\t\t\t\t\t\tQW_PrintfToViewer(v, \"%i: %s: %s\\n\", ov->userid, ov->name, ov->server->server);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tQW_PrintfToViewer(v, \"%i: %s: %s\\n\", ov->userid, ov->name, \"None\");\n\t\t\t}\n\t\t\telse\n\t\t\t\tremaining++;\n\t\t}\n\t\tif (remaining)\n\t\t\tQW_PrintfToViewer(v, \"%i clients not shown\\n\", remaining);\n\t}\n\telse if (!strcmp(command, \"followid\"))\n\t{\n\t\tint id = atoi(args);\n\t\tviewer_t *cv;\n\n\t\tfor (cv = cluster->viewers; cv; cv = cv->next)\n\t\t{\n\t\t\tif (cv->userid == id)\n\t\t\t{\n\t\t\t\tQW_SetCommentator(cluster, v, cv);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tQW_PrintfToViewer(v, \"Couldn't find that player\\n\");\n\t\tQW_SetCommentator(cluster, v, NULL);\n\t}\n\telse if (!strcmp(command, \"follow\"))\n\t{\n\t\tint id = atoi(args);\n\t\tviewer_t *cv;\n\n\t\tfor (cv = cluster->viewers; cv; cv = cv->next)\n\t\t{\n\t\t\tif (!strcmp(cv->name, args))\n\t\t\t{\n\t\t\t\tQW_SetCommentator(cluster, v, cv);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tif (id)\n\t\t{\n\t\t\tfor (cv = cluster->viewers; cv; cv = cv->next)\n\t\t\t{\n\t\t\t\tif (cv->userid == id)\n\t\t\t\t{\n\t\t\t\t\tQW_SetCommentator(cluster, v, cv);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tQW_PrintfToViewer(v, \"Couldn't find that player\\n\");\n\t\tQW_SetCommentator(cluster, v, NULL);\n\t}\n\telse if (!strcmp(command, \"follow\"))\n\t{\n\t\tQW_SetCommentator(cluster, v, NULL);\n\t}\n\telse if (!strcmp(command, \"bind\"))\n\t{\n\t\tQW_StuffcmdToViewer(v, \"bind uparrow +proxfwd\\n\");\n\t\tQW_StuffcmdToViewer(v, \"bind downarrow +proxback\\n\");\n\t\tQW_StuffcmdToViewer(v, \"bind rightarrow +proxright\\n\");\n\t\tQW_StuffcmdToViewer(v, \"bind leftarrow +proxleft\\n\");\n\t\tQW_PrintfToViewer(v, \"Keys bound\\n\");\n\t}\n\telse if (!strcmp(command, \"bsay\"))\n\t{\n\t\tchar buf[1024];\n\t\tnetmsg_t msg;\n\n\t\tviewer_t *ov;\n\t\tif (cluster->notalking)\n\t\t\treturn;\n\n\t\tfor (ov = cluster->viewers; ov; ov = ov->next)\n\t\t{\n\t\t\tInitNetMsg(&msg, buf, sizeof(buf));\n\n\t\t\tWriteByte(&msg, svc_print);\n\n\t\t\tif (ov->netchan.isnqprotocol)\n\t\t\t\tWriteByte(&msg, 1);\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (ov->conmenussupported)\n\t\t\t\t{\n\t\t\t\t\tWriteByte(&msg, 3);\t//PRINT_CHAT\n\t\t\t\t\tWriteString2(&msg, \"[^sBQTV^s]^s^5\");\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tWriteByte(&msg, 2);\t//PRINT_HIGH\n\t\t\t\t\tWriteByte(&msg, 91+128);\n\t\t\t\t\tWriteString2(&msg, \"BQTV\");\n\t\t\t\t\tWriteByte(&msg, 93+128);\n\t\t\t\t\tWriteByte(&msg, 0);\n\n\t\t\t\t\tWriteByte(&msg, svc_print);\n\t\t\t\t\tWriteByte(&msg, 3);\t//PRINT_CHAT\n\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tWriteString2(&msg, v->name);\n\t\t\tWriteString2(&msg, \": \");\n//\t\t\t\tWriteString2(&msg, \"\\x8d \");\n\t\t\tWriteString2(&msg, args);\n\t\t\tWriteString(&msg, \"\\n\");\n\n\t\t\tif (msg.maxsize == msg.cursize)\n\t\t\t\treturn;\n\t\t\tSendBufferToViewer(ov, msg.data, msg.cursize, true);\n\t\t}\n\t}\n\telse\n\t{\n\t\tQW_PrintfToViewer(v, \"QTV Proxy command not recognised\\n\");\n\t}\n}\n\nstatic void QTV_DoSay(cluster_t *cluster, sv_t *qtv, const char *viewername, char *message)\n{\n\tchar buf[1024];\n\tnetmsg_t msg;\n\tviewer_t *ov;\n\n\tif (cluster->notalking)\n\t\treturn;\n\n\tif (qtv)\n\t\tSV_SayToUpstream(qtv, message);\n\n\tfor (ov = cluster->viewers; ov; ov = ov->next)\n\t{\n\t\tif (ov->server != qtv)\n\t\t\tcontinue;\n\n\t\tInitNetMsg(&msg, buf, sizeof(buf));\n\n\t\tWriteByte(&msg, svc_print);\n\n\t\tif (ov->netchan.isnqprotocol)\n\t\t{\n\t\t\tWriteByte(&msg, 1);\n\t\t\tWriteByte(&msg, '[');\n\t\t\tWriteString2(&msg, \"QTV\");\n\t\t\tWriteByte(&msg, ']');\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (ov->conmenussupported)\n\t\t\t{\n\t\t\t\tWriteByte(&msg, 2);\t//PRINT_HIGH\n\t\t\t\tWriteByte(&msg, 91+128);\n\t\t\t\tWriteString2(&msg, \"QTV\");\n\t\t\t\tWriteByte(&msg, 93+128);\n\t\t\t\tWriteString2(&msg, \"^5\");\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tWriteByte(&msg, 2);\t//PRINT_HIGH\n\t\t\t\tWriteByte(&msg, 91+128);\n\t\t\t\tWriteString2(&msg, \"QTV\");\n\t\t\t\tWriteByte(&msg, 93+128);\n\t\t\t\tWriteByte(&msg, 0);\n\n\t\t\t\tWriteByte(&msg, svc_print);\n\t\t\t\tWriteByte(&msg, 3);\t//PRINT_CHAT\n\n\t\t\t}\n\t\t}\n\n\t\tWriteString2(&msg, viewername);\n\t\tWriteString2(&msg, \": \");\n//\t\tWriteString2(&msg, \"\\x8d \");\n\t\tif (ov->conmenussupported)\n\t\t\tWriteString2(&msg, \"^s\");\n\t\tWriteString2(&msg, message);\n\t\tWriteString(&msg, \"\\n\");\n\n\t\tSendBufferToViewer(ov, msg.data, msg.cursize, true);\n\t}\n}\n\nvoid QTV_Say(cluster_t *cluster, sv_t *qtv, viewer_t *v, char *message, qboolean noupwards)\n{\n\tchar buf[1024];\n\n\tif (message[strlen(message)-1] == '\\\"')\n\t\tmessage[strlen(message)-1] = '\\0';\n\n\tif (*v->expectcommand)\n\t{\n\t\tbuf[sizeof(buf)-1] = '\\0';\n\t\tif (!strcmp(v->expectcommand, \"hostname\"))\n\t\t{\n\t\t\tstrlcpy(cluster->hostname, message, sizeof(cluster->hostname));\n\t\t}\n\t\telse if (!strcmp(v->expectcommand, \"master\"))\n\t\t{\n\t\t\tstrlcpy(cluster->master, message, sizeof(cluster->master));\n\t\t\tif (!strcmp(cluster->master, \".\"))\n\t\t\t\t*cluster->master = '\\0';\n\t\t\tcluster->mastersendtime = cluster->curtime;\n\t\t}\n\t\telse if (!strcmp(v->expectcommand, \"addserver\"))\n\t\t{\n\t\t\tsnprintf(buf, sizeof(buf), \"tcp:%s\", message);\n\t\t\tqtv = QTV_NewServerConnection(cluster, 0, buf, \"\", false, AD_NO, false, false);\n\t\t\tif (qtv)\n\t\t\t{\n\t\t\t\tQW_SetViewersServer(cluster, v, qtv);\n\t\t\t\tQW_PrintfToViewer(v, \"Connected to \\\"%s\\\"\\n\", message);\n\t\t\t}\n\t\t\telse\n\t\t\t\tQW_PrintfToViewer(v, \"Failed to connect to server \\\"%s\\\", connection aborted\\n\", message);\n\t\t}\n\t\telse if (!strcmp(v->expectcommand, \"admin\"))\n\t\t{\n\t\t\tif (!strcmp(message, cluster->adminpassword))\n\t\t\t{\n\t\t\t\tQW_SetMenu(v, MENU_ADMIN);\n\t\t\t\tv->isadmin = true;\n\t\t\t\tSys_Printf(cluster, \"Player %s logs in as admin\\n\", v->name);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tQW_PrintfToViewer(v, \"Admin password incorrect\\n\");\n\t\t\t\tSys_Printf(cluster, \"Player %s gets incorrect admin password\\n\", v->name);\n\t\t\t}\n\t\t}\n\t\telse if (!strcmp(v->expectcommand, \"insecadddemo\"))\n\t\t{\n\t\t\tsnprintf(buf, sizeof(buf), \"file:%s\", message);\n\t\t\tqtv = QTV_NewServerConnection(cluster, 0, buf, \"\", false, AD_NO, false, false);\n\t\t\tif (!qtv)\n\t\t\t\tQW_PrintfToViewer(v, \"Failed to play demo \\\"%s\\\"\\n\", message);\n\t\t\telse\n\t\t\t{\n\t\t\t\tQW_SetViewersServer(cluster, v, qtv);\n\t\t\t\tQW_PrintfToViewer(v, \"Opened demo file \\\"%s\\\".\\n\", message);\n\t\t\t}\n\t\t}\n\n\t\telse if (!strcmp(v->expectcommand, \"adddemo\"))\n\t\t{\n\t\t\tsnprintf(buf, sizeof(buf), \"file:%s\", message);\n\t\t\tqtv = QTV_NewServerConnection(cluster, 0, buf, \"\", false, AD_NO, false, false);\n\t\t\tif (!qtv)\n\t\t\t\tQW_PrintfToViewer(v, \"Failed to play demo \\\"%s\\\"\\n\", message);\n\t\t\telse\n\t\t\t{\n\t\t\t\tQW_SetViewersServer(cluster, v, qtv);\n\t\t\t\tQW_PrintfToViewer(v, \"Opened demo file \\\"%s\\\".\\n\", message);\n\t\t\t}\n\t\t}\n\t\telse if (!strcmp(v->expectcommand, \"setmvdport\"))\n\t\t{\n\t\t\tint newp = atoi(message);\n\n\t\t\tNet_TCPListen(cluster, newp, true);\n\t\t\tNet_TCPListen(cluster, newp, false);\n\t\t\tcluster->tcplistenportnum = newp;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tQW_PrintfToViewer(v, \"Command %s was not recognised\\n\", v->expectcommand);\n\t\t}\n\n\t\t*v->expectcommand = '\\0';\n\t\treturn;\n\t}\n\n\tif (*message == '.')\n\t{\n\t\tif (message[1] == '.')\t//double it up to say it\n\t\t\tmessage++;\n\t\telse\n\t\t{\n\t\t\t//this is always execed (. is local server)\n\t\t\tQTV_SayCommand(cluster, qtv, v, message+1);\n\t\t\treturn;\n\t\t}\n\t}\n\telse if (!strncmp(message, \"proxy:\", 6))\n\t{\n\t\t//this is execed on the 'active' server\n\t\tif (qtv && (qtv->controller == v && !qtv->proxyisselected))\n\t\t\tSendClientCommand(qtv, \"say \\\"%s\\\"\", message);\n\t\telse\n\t\t\tQTV_SayCommand(cluster, qtv, v, message+6);\n\t\treturn;\n\t}\n\telse if (*message == ',')\n\t{\n\t\tif (message[1] == ',')\t//double up to say it\n\t\t\tmessage++;\n\t\telse\n\t\t{\n\t\t\tif (qtv && (qtv->controller == v && qtv->serverisproxy))\n\t\t\t\tSendClientCommand(qtv, \"say \\\"%s\\\"\", message);\n\t\t\telse\n\t\t\t\tQTV_SayCommand(cluster, qtv, v, message+1);\n\t\t\treturn;\n\t\t}\n\t}\n\n\n\n\tif (!strncmp(message, \".\", 1))\n\t\tmessage++;\n\t*v->expectcommand = '\\0';\n\n\tif (qtv && qtv->usequakeworldprotocols && !noupwards)\n\t{\n\t\tif (qtv->controller == v)\n\t\t{\n\t\t\tSendClientCommand(qtv, \"say \\\"%s\\\"\", message);\n\n\t\t\tif (cluster->notalking)\n\t\t\t\treturn;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (cluster->notalking)\n\t\t\t\treturn;\n\t\t\tSendClientCommand(qtv, \"say \\\"[%s]: %s\\\"\", v->name, message);\n\t\t}\n\n\t\t//FIXME: we ought to broadcast this to everyone not watching that qtv.\n\t}\n\telse\n\t{\n\t\t// If the current viewer is the player, pass on the say_team\n\t\tif (qtv && qtv->controller == v)\n\t\t{\n\t\t\tSendClientCommand(qtv, \"say_team \\\"%s\\\"\", message);\n\t\t\treturn;\n\t\t}\n\n\t\tQTV_DoSay(cluster, v->server, v->name, message);\n\t}\n}\n\nviewer_t *QW_IsOn(cluster_t *cluster, char *name)\n{\n\tviewer_t *v;\n\tfor (v = cluster->viewers; v; v = v->next)\n\t\tif (!stricmp(v->name, name))\t\t//this needs to allow dequakified names.\n\t\t\treturn v;\n\n\treturn NULL;\n}\n\nvoid QW_PrintfToViewer(viewer_t *v, char *format, ...)\n{\n\tint pos = 0;\n\tva_list\t\targptr;\n\tchar buf[1024];\n\n\tbuf[pos++] = svc_print;\n\tif (!v->netchan.isnqprotocol)\n\t\tbuf[pos++] = 2;\t//PRINT_HIGH\n\n\tva_start (argptr, format);\n\tvsnprintf (buf+pos, sizeof(buf)-pos, format, argptr);\n\tva_end (argptr);\n\n\tSendBufferToViewer(v, buf, strlen(buf)+1, true);\n}\n\n\nvoid QW_StuffcmdToViewer(viewer_t *v, char *format, ...)\n{\n\tva_list\t\targptr;\n\tchar buf[1024];\n\n\tva_start (argptr, format);\n\tvsnprintf (buf+1, sizeof(buf)-1, format, argptr);\n\tva_end (argptr);\n\n\tbuf[0] = svc_stufftext;\n\tSendBufferToViewer(v, buf, strlen(buf)+1, true);\n}\n\nvoid QW_PositionAtIntermission(sv_t *qtv, viewer_t *v)\n{\n\tnetmsg_t msg;\n\tchar buf[7];\n\tconst intermission_t *spot;\n\tunsigned int pext1;\n\n\n\tif (qtv)\n\t{\n\t\tspot = BSP_IntermissionSpot(qtv->map.bsp);\n\t\tpext1 = qtv->pext1;\n\t}\n\telse\n\t{\n\t\tspot = &nullstreamspot;\n\t\tpext1 = 0;\n\t}\n\n\n\tv->origin[0] = spot->pos[0];\n\tv->origin[1] = spot->pos[1];\n\tv->origin[2] = spot->pos[2];\n\n\tInitNetMsg(&msg, buf, sizeof(buf));\n\n\tWriteByte (&msg, svc_setangle);\n\tWriteAngle(&msg, spot->angle[0], pext1);\n\tWriteAngle(&msg, spot->angle[1], pext1);\n\tWriteAngle(&msg, 0, pext1);\n\n\tSendBufferToViewer(v, msg.data, msg.cursize, true);\n}\n\nvoid ParseNQC(cluster_t *cluster, sv_t *qtv, viewer_t *v, netmsg_t *m)\n{\n\tchar buf[MAX_NQMSGLEN];\n\tnetmsg_t msg;\n\tint mtype;\n\n\twhile (m->readpos < m->cursize)\n\t{\n\t\tswitch ((mtype=ReadByte(m)))\n\t\t{\n\t\tcase clc_nop:\n\t\t\tbreak;\n\t\tcase clc_stringcmd:\n\t\t\tReadString (m, buf, sizeof(buf));\n\t\t\tprintf(\"stringcmd: %s\\n\", buf);\n\n\t\t\tif (!strcmp(buf, \"new\"))\n\t\t\t{\n\t\t\t\tif (qtv && qtv->parsingconnectiondata)\n\t\t\t\t\tQW_StuffcmdToViewer(v, \"cmd new\\n\");\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tSendServerData(qtv, v);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (!strncmp(buf, \"prespawn\", 8))\n\t\t\t{\n\t\t\t\tmsg.data = buf;\n\t\t\t\tmsg.maxsize = sizeof(buf);\n\t\t\t\tmsg.cursize = 0;\n\t\t\t\tmsg.overflowed = 0;\n\n\t\t\t\tif (qtv)\n\t\t\t\t{\n\t\t\t\t\tSendCurrentBaselines(qtv, 64, &msg, msg.maxsize, 0);\n\t\t\t\t\tSendCurrentLightmaps(qtv, 64, &msg, msg.maxsize, 0);\n\n\t\t\t\t\tSendStaticSounds(qtv, 64, &msg, msg.maxsize, 0);\n\n\t\t\t\t\tSendStaticEntities(qtv, 64, &msg, msg.maxsize, 0);\n\t\t\t\t}\n\t\t\t\tWriteByte (&msg, svc_nqsignonnum);\n\t\t\t\tWriteByte (&msg, 2);\n\t\t\t\tSendBufferToViewer(v, msg.data, msg.cursize, true);\n\t\t\t}\n\n\t\t\telse if (!strncmp(buf, \"setinfo\", 5))\n\t\t\t{\n\t\t\t\t#define TOKENIZE_PUNCTUATION \"\"\n\n\t\t\t\tint i;\n\t\t\t\tchar arg[3][ARG_LEN];\n\t\t\t\tchar *command = buf;\n\n\t\t\t\tfor (i = 0; i < 3; i++)\n\t\t\t\t{\n\t\t\t\t\tcommand = COM_ParseToken(command, arg[i], ARG_LEN, TOKENIZE_PUNCTUATION);\n\t\t\t\t}\n\n\t\t\t\tInfo_SetValueForStarKey(v->userinfo, arg[1], arg[2], sizeof(v->userinfo));\n\t\t\t\tParseUserInfo(cluster, v);\n//\t\t\t\tInfo_ValueForKey(v->userinfo, \"name\", v->name, sizeof(v->name));\n\n\t\t\t\tif (v->server && v->server->controller == v)\n\t\t\t\t\tSendClientCommand(v->server, \"%s\", buf);\n\t\t\t}\n\n\t\t\telse if (!strncmp(buf, \"name \", 5))\n\t\t\t{\n\t\t\t\tInfo_SetValueForStarKey(v->userinfo, \"name\", buf+5, sizeof(v->userinfo));\n\t\t\t\tParseUserInfo(cluster, v);\n\n\t\t\t\tif (v->server && v->server->controller == v)\n\t\t\t\t\tSendClientCommand(v->server, \"setinfo name \\\"%s\\\"\", v->name);\n\t\t\t}\n\t\t\telse if (!strncmp(buf, \"color \", 6))\n\t\t\t{\n\t\t\t\t/*\n\t\t\t\tfixme\n\t\t\t\t*/\n\t\t\t}\n\t\t\telse if (!strncmp(buf, \"spawn\", 5))\n\t\t\t{\n\t\t\t\tmsg.data = buf;\n\t\t\t\tmsg.maxsize = sizeof(buf);\n\t\t\t\tmsg.cursize = 0;\n\t\t\t\tmsg.overflowed = 0;\n\t\t\t\tSendNQSpawnInfoToViewer(cluster, v, &msg);\n\t\t\t\tSendBufferToViewer(v, msg.data, msg.cursize, true);\n\n\t\t\t\tQW_PositionAtIntermission(qtv, v);\n\n\t\t\t\tv->thinksitsconnected = true;\n\t\t\t}\n\t\t\telse if (!strncmp(buf, \"begin\", 5))\n\t\t\t{\n\t\t\t\tint oldmenu;\n\t\t\t\tv->thinksitsconnected = true;\n\n\t\t\t\toldmenu = v->menunum;\n\t\t\t\tQW_SetMenu(v, MENU_NONE);\n\t\t\t\tQW_SetMenu(v, oldmenu);\n\n\t\t\t\tif (!v->server)\n\t\t\t\t\tQTV_Say(cluster, v->server, v, \".menu\", false);\n\t\t\t}\n\n\t\t\telse if (!strncmp(buf, \"say \\\".\", 6))\n\t\t\t\tQTV_Say(cluster, qtv, v, buf+5, false);\n\t\t\telse if (!strncmp(buf, \"say .\", 5))\n\t\t\t\tQTV_Say(cluster, qtv, v, buf+4, false);\n\n\t\t\telse if (v->server && v == v->server->controller)\n\t\t\t\tSendClientCommand(v->server, \"%s\", buf);\n\n\t//\t\telse if (!strcmp(buf, \"pause\"))\n\t//\t\t\tqtv->errored = ERR_PAUSED;\n\n\t\t\telse if (!strncmp(buf, \"say \\\"\", 5))\n\t\t\t\tQTV_Say(cluster, qtv, v, buf+5, false);\n\t\t\telse if (!strncmp(buf, \"say \", 4))\n\t\t\t\tQTV_Say(cluster, qtv, v, buf+4, false);\n\n\t\t\telse if (!strncmp(buf, \"say_team \\\"\", 10))\n\t\t\t\tQTV_Say(cluster, qtv, v, buf+10, true);\n\t\t\telse if (!strncmp(buf, \"say_team \", 9))\n\t\t\t\tQTV_Say(cluster, qtv, v, buf+9, true);\n\n\n\t\t\telse\n\t\t\t{\n\t\t\t\tQW_PrintfToViewer(v, \"Command not recognised\\n\");\n\t\t\t\tSys_Printf(cluster, \"NQ client sent unrecognized stringcmd %s\\n\", buf);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase clc_disconnect:\n\t\t\tif (!v->drop)\n\t\t\t\tSys_Printf(cluster, \"NQ viewer %s disconnects\\n\", v->name);\n\t\t\tv->drop = true;\n\t\t\treturn;\n\t\tcase clc_move:\n\t\t\tv->ucmds[0] = v->ucmds[1];\n\t\t\tv->ucmds[1] = v->ucmds[2];\n\t\t\tReadFloat(m);\t//time, for pings\n\t\t\t//three angles\n\t\t\t{\n\t\t\t\tunsigned int pext1;\n\t\t\t\tif (v->server)\n\t\t\t\t\tpext1 = v->server->pext1;\n\t\t\t\telse\n\t\t\t\t\tpext1 = 0;\n\t\t\t\tv->ucmds[2].angles[0] = ReadAngle(m, pext1);\n\t\t\t\tv->ucmds[2].angles[1] = ReadAngle(m, pext1);\n\t\t\t\tv->ucmds[2].angles[2] = ReadAngle(m, pext1);\n\t\t\t}\n\t\t\t//three direction values\n\t\t\tv->ucmds[2].forwardmove = ReadShort(m);\n\t\t\tv->ucmds[2].sidemove = ReadShort(m);\n\t\t\tv->ucmds[2].upmove = ReadShort(m);\n\n\t\t\t//one button\n\t\t\tv->ucmds[2].buttons = ReadByte(m);\n\t\t\t//one impulse\n\t\t\tv->ucmds[2].impulse = ReadByte(m);\n\n\t\t\tv->ucmds[2].msec = cluster->curtime - v->lasttime;\n\t\t\tv->lasttime = cluster->curtime;\n\n\t\t\tif (v->menunum)\n\t\t\t{\n\t\t\t\tint mb = 0;\n\t\t\t\tif (v->ucmds[2].forwardmove > 0)\tmb = MBTN_UP;\n\t\t\t\tif (v->ucmds[2].forwardmove < 0)\tmb = MBTN_DOWN;\n\t\t\t\tif (v->ucmds[2].sidemove > 0)\t\tmb = MBTN_RIGHT;\n\t\t\t\tif (v->ucmds[2].sidemove < 0)\t\tmb = MBTN_LEFT;\n\t\t\t\tif (v->ucmds[2].buttons & 2)\t\tmb = MBTN_ENTER;\n\t\t\t\tif (mb & ~v->menubuttons & MBTN_UP)\t\tv->menuop -= 1;\n\t\t\t\tif (mb & ~v->menubuttons & MBTN_DOWN)\tv->menuop += 1;\n\t\t\t\tif (mb & ~v->menubuttons & MBTN_RIGHT)\tMenu_Enter(cluster, v, 1);\n\t\t\t\tif (mb & ~v->menubuttons & MBTN_LEFT)\tMenu_Enter(cluster, v, -1);\n\t\t\t\tif (mb & ~v->menubuttons & MBTN_ENTER)\tMenu_Enter(cluster, v, 0);\n\t\t\t\tif (v->menubuttons != mb)\n\t\t\t\t\tv->menuspamtime = cluster->curtime-1;\n\t\t\t\tv->ucmds[2].forwardmove = 0;\n\t\t\t\tv->ucmds[2].sidemove = 0;\n\t\t\t\tv->ucmds[2].buttons = 0;\n\t\t\t\tv->menubuttons = mb;\n\t\t\t}\n\t\t\telse\n\t\t\t\tv->menubuttons = ~0;\t//so nothing gets instantly flagged once we enter a menu.\n\n\t\t\tif (v->server && v->server->controller == v)\n\t\t\t\treturn;\n\n\t\t\tPMove(v, &v->ucmds[2]);\n\n\t\t\tif ((v->ucmds[1].buttons&1) != (v->ucmds[2].buttons&1) && (v->ucmds[2].buttons&1))\n\t\t\t{\n\t\t\t\tif(v->server)\n\t\t\t\t{\n\t\t\t\t\tint t;\n\n\t\t\t\t\tfor (t = v->trackplayer+1; t < MAX_CLIENTS; t++)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (v->server->map.players[t].active)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}/*\n\t\t\t\t\tif (t == MAX_CLIENTS)\n\t\t\t\t\tfor (t = 0; t <= v->trackplayer; t++)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (v->server->players[t].active)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t*/\n\t\t\t\t\tif (t >= MAX_CLIENTS)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (v->trackplayer >= 0)\n\t\t\t\t\t\t\tQW_PrintfToViewer(v, \"Stopped tracking\\n\");\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tQW_PrintfToViewer(v, \"Not tracking\\n\");\n\t\t\t\t\t\tv->trackplayer = -1;\t//no trackable players found\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tv->trackplayer = t;\n\t\t\t\t\t\tInfo_ValueForKey(v->server->map.players[t].userinfo, \"name\", buf, sizeof(buf));\n\t\t\t\t\t\tQW_PrintfToViewer(v, \"Now tracking: %s\\n\", buf);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif ((v->ucmds[1].buttons&2) != (v->ucmds[2].buttons&2) && (v->ucmds[2].buttons&2))\n\t\t\t{\n\t\t\t\tif (!v->server && !v->menunum)\n\t\t\t\t\tQW_SetMenu(v, MENU_DEFAULT);\n\n\t\t\t\tif(v->server)\n\t\t\t\t{\n\t\t\t\t\tint t;\n\t\t\t\t\tif (v->trackplayer < 0)\n\t\t\t\t\t{\n\t\t\t\t\t\tfor (t = MAX_CLIENTS-1; t >= v->trackplayer; t--)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (v->server->map.players[t].active)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tfor (t = v->trackplayer-1; t >= 0; t--)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (v->server->map.players[t].active)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif (t < 0)\n\t\t\t\t\t{\n\t\t\t\t\t\tv->trackplayer = -1;\t//no trackable players found\n\t\t\t\t\t\tQW_PrintfToViewer(v, \"Not tracking\\n\");\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tv->trackplayer = t;\n\t\t\t\t\t\tInfo_ValueForKey(v->server->map.players[t].userinfo, \"name\", buf, sizeof(buf));\n\t\t\t\t\t\tQW_PrintfToViewer(v, \"Now tracking: %s\\n\", buf);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (v->trackplayer > -1 && v->server)\n\t\t\t{\n\t\t\t\tv->origin[0] = v->server->map.players[v->trackplayer].current.origin[0];\n\t\t\t\tv->origin[1] = v->server->map.players[v->trackplayer].current.origin[1];\n\t\t\t\tv->origin[2] = v->server->map.players[v->trackplayer].current.origin[2];\n\t\t\t}\n\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tSys_Printf(cluster, \"Bad message type %i\\n\", mtype);\n\t\t\treturn;\n\t\t}\n\t}\n}\nvoid ParseQWC(cluster_t *cluster, sv_t *qtv, viewer_t *v, netmsg_t *m)\n{\n//\tusercmd_t\toldest, oldcmd, newcmd;\n\tchar buf[1024];\n\tnetmsg_t msg;\n\tint i;\n\tint iscont;\n\n\tv->delta_frames[v->netchan.incoming_sequence & (ENTITY_FRAMES-1)] = -1;\n\n\twhile (m->readpos < m->cursize)\n\t{\n\t\ti = ReadByte(m);\n\t\tswitch (i)\n\t\t{\n\t\tcase clc_nop:\n\t\t\treturn;\n\t\tcase clc_delta:\n\t\t\tv->delta_frames[v->netchan.incoming_sequence & (ENTITY_FRAMES-1)] = ReadByte(m);\n\t\t\tbreak;\n\t\tcase clc_stringcmd:\n\t\t\tReadString (m, buf, sizeof(buf));\n\n\t\t\tiscont = v->server && v->server->controller == v;\n\n\t\t\tif (!strncmp(buf, \"cmd \", 4))\n\t\t\t{\n\t\t\t\tif (v->server && v->server->controller == v)\n\t\t\t\t\tSendClientCommand(v->server, \"%s\", buf+4);\n\t\t\t}\n\t\t\telse if (iscont && !strncmp(buf, \"pext \", 5))\n\t\t\t{\t//FIXME: include ones we can parse properly...\n\t\t\t\tSendClientCommand(v->server, \"pext\");\n\t\t\t}\n\t\t\telse if (!iscont && !strcmp(buf, \"new\"))\n\t\t\t{\n\t\t\t\tif (qtv && qtv->parsingconnectiondata)\n\t\t\t\t\tQW_StuffcmdToViewer(v, \"cmd new\\n\");\n\t\t\t\telse\n\t\t\t\t{\n//\t\t\t\t\tQW_StuffcmdToViewer(v, \"//querycmd conmenu\\n\");\n\t\t\t\t\tSendServerData(qtv, v);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (!iscont && !strncmp(buf, \"modellist \", 10))\n\t\t\t{\n\t\t\t\tchar *cmd = buf+10;\n\t\t\t\tint svcount = atoi(cmd);\n\t\t\t\tint first;\n\n\t\t\t\twhile((*cmd >= '0' && *cmd <= '9') || *cmd == '-')\n\t\t\t\t\tcmd++;\n\t\t\t\tfirst = atoi(cmd);\n\n\t\t\t\tInitNetMsg(&msg, buf, sizeof(buf));\n\n\t\t\t\tif (svcount != v->servercount)\n\t\t\t\t{\t//looks like we changed map without them.\n\t\t\t\t\tSendServerData(qtv, v);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tif (!qtv)\n\t\t\t\t\tSendList(qtv, first, ConnectionlessModelList, svc_modellist, &msg);\n\t\t\t\telse\n\t\t\t\t\tSendList(qtv, first, qtv->map.modellist, svc_modellist, &msg);\n\t\t\t\tSendBufferToViewer(v, msg.data, msg.cursize, true);\n\t\t\t}\n\t\t\telse if (!iscont && !strncmp(buf, \"soundlist \", 10))\n\t\t\t{\n\t\t\t\tchar *cmd = buf+10;\n\t\t\t\tint svcount = atoi(cmd);\n\t\t\t\tint first;\n\n\t\t\t\twhile((*cmd >= '0' && *cmd <= '9') || *cmd == '-')\n\t\t\t\t\tcmd++;\n\t\t\t\tfirst = atoi(cmd);\n\n\t\t\t\tInitNetMsg(&msg, buf, sizeof(buf));\n\n\t\t\t\tif (svcount != v->servercount)\n\t\t\t\t{\t//looks like we changed map without them.\n\t\t\t\t\tSendServerData(qtv, v);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tif (!qtv)\n\t\t\t\t\tSendList(qtv, first, ConnectionlessSoundList, svc_soundlist, &msg);\n\t\t\t\telse\n\t\t\t\t\tSendList(qtv, first, qtv->map.soundlist, svc_soundlist, &msg);\n\t\t\t\tSendBufferToViewer(v, msg.data, msg.cursize, true);\n\t\t\t}\n\t\t\telse if (!iscont && !strncmp(buf, \"prespawn\", 8))\n\t\t\t{\n\t\t\t\tchar skin[128];\n\n\t\t\t\tif (atoi(buf + 9) != v->servercount)\n\t\t\t\t\tSendServerData(qtv, v);\t//we're old.\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tint crc;\n\t\t\t\t\tint r;\n\t\t\t\t\tchar *s;\n\t\t\t\t\ts = buf+8;\n\t\t\t\t\twhile(*s == ' ')\n\t\t\t\t\t\ts++;\n\t\t\t\t\twhile((*s >= '0' && *s <= '9') || *s == '-')\n\t\t\t\t\t\ts++;\n\t\t\t\t\twhile(*s == ' ')\n\t\t\t\t\t\ts++;\n\t\t\t\t\tr = atoi(s);\n\n\t\t\t\t\tif (r == 0)\n\t\t\t\t\t{\n\t\t\t\t\t\twhile((*s >= '0' && *s <= '9') || *s == '-')\n\t\t\t\t\t\t\ts++;\n\t\t\t\t\t\twhile(*s == ' ')\n\t\t\t\t\t\t\ts++;\n\t\t\t\t\t\tcrc = atoi(s);\n\n\t\t\t\t\t\tif (qtv && qtv->controller == v)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (!qtv->map.bsp)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t//warning do we still actually need to do this ourselves? Or can we just forward what the user stated?\n\t\t\t\t\t\t\t\tQW_PrintfToViewer(v, \"QTV doesn't have that map (%s), sorry.\\n\", qtv->map.modellist[1].name);\n\t\t\t\t\t\t\t\tqtv->errored = ERR_DROP;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse if (crc != BSP_Checksum(qtv->map.bsp))\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tQW_PrintfToViewer(v, \"QTV's map (%s) does not match the servers\\n\", qtv->map.modellist[1].name);\n\t\t\t\t\t\t\t\tqtv->errored = ERR_DROP;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tInitNetMsg(&msg, buf, sizeof(buf));\n\n\t\t\t\t\tif (qtv)\n\t\t\t\t\t{\n\t\t\t\t\t\tr = Prespawn(qtv, v->netchan.message.cursize, &msg, r, v->thisplayer);\n\t\t\t\t\t\tSendBufferToViewer(v, msg.data, msg.cursize, true);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tr = SendCurrentUserinfos(qtv, v->netchan.message.cursize, &msg, r, v->thisplayer);\n\t\t\t\t\t\tif (r > MAX_CLIENTS)\n\t\t\t\t\t\t\tr = -1;\n\t\t\t\t\t\tSendBufferToViewer(v, msg.data, msg.cursize, true);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (r < 0)\n\t\t\t\t\t\tsprintf(skin, \"%ccmd spawn\\n\", svc_stufftext);\n\t\t\t\t\telse\n\t\t\t\t\t\tsprintf(skin, \"%ccmd prespawn %i %i\\n\", svc_stufftext, v->servercount, r);\n\n\t\t\t\t\tSendBufferToViewer(v, skin, strlen(skin)+1, true);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (!iscont && !strncmp(buf, \"spawn\", 5))\n\t\t\t{\n\t\t\t\tchar skin[64];\n\t\t\t\tsprintf(skin, \"%cskins\\n\", svc_stufftext);\n\t\t\t\tSendBufferToViewer(v, skin, strlen(skin)+1, true);\n\n\t\t\t\tQW_PositionAtIntermission(qtv, v);\n\t\t\t}\n\t\t\telse if (iscont && !strncmp(buf, \"begin\", 5))\n\t\t\t{\t//the client made it!\n\t\t\t\tv->thinksitsconnected = true;\n\t\t\t\tqtv->parsingconnectiondata = false;\n\t\t\t\tSendClientCommand(v->server, \"%s\", buf);\n\t\t\t}\n\t\t\telse if (!iscont && !strncmp(buf, \"begin\", 5))\n\t\t\t{\n\t\t\t\tint oldmenu;\n\t\t\t\tviewer_t *com;\n\t\t\t\tif (atoi(buf+6) != v->servercount)\n\t\t\t\t\tSendServerData(qtv, v);\t//this is unfortunate!\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tv->thinksitsconnected = true;\n\t\t\t\t\tif (qtv && qtv->map.ispaused)\n\t\t\t\t\t{\n\t\t\t\t\t\tchar msgb[] = {svc_setpause, 1};\n\t\t\t\t\t\tSendBufferToViewer(v, msgb, sizeof(msgb), true);\n\t\t\t\t\t}\n\n\t\t\t\t\toldmenu = v->menunum;\n\t\t\t\t\tQW_SetMenu(v, MENU_NONE);\n\t\t\t\t\tQW_SetMenu(v, oldmenu);\n\n\n\t\t\t\t\tcom = v->commentator;\n\t\t\t\t\tv->commentator = NULL;\n\t\t\t\t\tQW_SetCommentator(cluster, v, com);\n\n\t\t\t\t\tif (v->firstconnect)\n\t\t\t\t\t{\n\t\t\t\t\t\tQW_StuffcmdToViewer(v, \"f_qtv\\n\");\n\t\t\t\t\t\tv->firstconnect = false;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (!v->server)\n\t\t\t\t\t\tQTV_Say(cluster, v->server, v, \".menu\", false);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (!strncmp(buf, \"download\", 8))\n\t\t\t{\n\t\t\t\tnetmsg_t m;\n\t\t\t\tInitNetMsg(&m, buf, sizeof(buf));\n\t\t\t\tWriteByte(&m, svc_download);\n\t\t\t\tWriteShort(&m, -1);\n\t\t\t\tWriteByte(&m, 0);\n\t\t\t\tSendBufferToViewer(v, m.data, m.cursize, true);\n\t\t\t}\n\t\t\telse if (!strncmp(buf, \"drop\", 4))\n\t\t\t{\n\t\t\t\tif (!v->drop)\n\t\t\t\t\tSys_Printf(cluster, \"QW viewer %s disconnects\\n\", v->name);\n\t\t\t\tv->drop = true;\n\t\t\t}\n\t\t\telse if (!strcmp(buf, \"pause\"))\n\t\t\t{\n\t\t\t\tif (qtv->errored == ERR_PAUSED)\n\t\t\t\t\tqtv->errored = ERR_NONE;\n\t\t\t\telse if (qtv->sourcetype == SRC_DEMO && (1 || v->isadmin))\n\t\t\t\t\tqtv->errored = ERR_PAUSED;\n\t\t\t\telse\n\t\t\t\t\tQW_PrintfToViewer(v, \"You may not pause this stream\\n\");\n\t\t\t}\n\t\t\telse if (!strncmp(buf, \"ison\", 4))\n\t\t\t{\n\t\t\t\tviewer_t *other;\n\t\t\t\tif ((other = QW_IsOn(cluster, buf+5)))\n\t\t\t\t{\n\t\t\t\t\tif (!other->server)\n\t\t\t\t\t\tQW_PrintfToViewer(v, \"%s is on the proxy, but not yet watching a game\\n\", other->name);\n\t\t\t\t\telse\n\t\t\t\t\t\tQW_PrintfToViewer(v, \"%s is watching %s\\n\", buf+5, other->server->server);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tQW_PrintfToViewer(v, \"%s is not on the proxy, sorry\\n\", buf+5);\t//the apology is to make the alternatives distinct.\n\t\t\t}\n\t\t\telse if (!strncmp(buf, \"ptrack \", 7))\n\t\t\t{\n\t\t\t\tv->trackplayer = atoi(buf+7);\n//\t\t\t\tif (v->trackplayer != MAX_CLIENTS-2)\n//\t\t\t\t\tQW_SetCommentator(v, NULL);\n\t\t\t}\n\t\t\telse if (!strncmp(buf, \"ptrack\", 6))\n\t\t\t{\n\t\t\t\tv->trackplayer = -1;\n\t\t\t\tQW_SetCommentator(cluster, v, NULL);\t//clicking out will stop the client from tracking thier commentator\n\t\t\t}\n\t\t\telse if (!iscont && !strncmp(buf, \"pings\", 5))\n\t\t\t{\n\t\t\t}\n\t\t\telse if (!strncmp(buf, \"say \\\"\", 5))\n\t\t\t\tQTV_Say(cluster, qtv, v, buf+5, false);\n\t\t\telse if (!strncmp(buf, \"say \", 4))\n\t\t\t\tQTV_Say(cluster, qtv, v, buf+4, false);\n\n\t\t\telse if (!strncmp(buf, \"say_team \\\"\", 10))\n\t\t\t\tQTV_Say(cluster, qtv, v, buf+10, true);\n\t\t\telse if (!strncmp(buf, \"say_team \", 9))\n\t\t\t\tQTV_Say(cluster, qtv, v, buf+9, true);\n\n\t\t\telse if (!strncmp(buf, \"servers\", 7))\n\t\t\t{\n\t\t\t\tQW_SetMenu(v, MENU_SERVERS);\n\t\t\t}\n\n\t\t\telse if (!strncmp(buf, \"setinfo\", 5))\n\t\t\t{\n\t\t\t\t#define TOKENIZE_PUNCTUATION \"\"\n\n\t\t\t\tint i;\n\t\t\t\tchar arg[3][ARG_LEN];\n\t\t\t\tchar *command = buf;\n\n\t\t\t\tfor (i = 0; i < 3; i++)\n\t\t\t\t{\n\t\t\t\t\tcommand = COM_ParseToken(command, arg[i], ARG_LEN, TOKENIZE_PUNCTUATION);\n\t\t\t\t}\n\n\t\t\t\tInfo_SetValueForStarKey(v->userinfo, arg[1], arg[2], sizeof(v->userinfo));\n\t\t\t\tParseUserInfo(cluster, v);\n//\t\t\t\tInfo_ValueForKey(v->userinfo, \"name\", v->name, sizeof(v->name));\n\n\t\t\t\tif (v->server && v->server->controller == v)\n\t\t\t\t\tSendClientCommand(v->server, \"%s\", buf);\n\t\t\t}\n\t\t\telse if (!strncmp(buf, \"cmdsupported \", 13))\n\t\t\t{\n\t\t\t\tif (!strcmp(buf+13, \"conmenu\"))\n\t\t\t\t\tv->conmenussupported = true;\n\t\t\t\telse if (v->server && v->server->controller == v)\n\t\t\t\t\tSendClientCommand(v->server, \"%s\", buf);\n\t\t\t}\n\t\t\telse if (!qtv)\n\t\t\t{\n\t\t\t\t//all the other things need an active server.\n\t\t\t\tQW_PrintfToViewer(v, \"Choose a server first    DEBUG:(%s)\\n\", buf);\n\t\t\t}\n\t\t\telse if (!strncmp(buf, \"serverinfo\", 5))\n\t\t\t{\n\t\t\t\tchar *key, *value, *end;\n\t\t\t\tint len;\n\t\t\t\tnetmsg_t m;\n\t\t\t\tInitNetMsg(&m, buf, sizeof(buf));\n\t\t\t\tWriteByte(&m, svc_print);\n\t\t\t\tWriteByte(&m, 2);\n\t\t\t\tend = qtv->map.serverinfo;\n\t\t\t\tfor(;;)\n\t\t\t\t{\n\t\t\t\t\tif (!*end)\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tkey = end;\n\t\t\t\t\tvalue = strchr(key+1, '\\\\');\n\t\t\t\t\tif (!value)\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tend = strchr(value+1, '\\\\');\n\t\t\t\t\tif (!end)\n\t\t\t\t\t\tend = value+strlen(value);\n\n\t\t\t\t\tlen = value-key;\n\n\t\t\t\t\tkey++;\n\t\t\t\t\twhile(*key != '\\\\' && *key)\n\t\t\t\t\t\tWriteByte(&m, *key++);\n\n\t\t\t\t\tfor (; len < 20; len++)\n\t\t\t\t\t\tWriteByte(&m, ' ');\n\n\t\t\t\t\tvalue++;\n\t\t\t\t\twhile(*value != '\\\\' && *value)\n\t\t\t\t\t\tWriteByte(&m, *value++);\n\t\t\t\t\tWriteByte(&m, '\\n');\n\t\t\t\t}\n\t\t\t\tWriteByte(&m, 0);\n\n//\t\t\t\tWriteString(&m, qtv->serverinfo);\n\t\t\t\tSendBufferToViewer(v, m.data, m.cursize, true);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (v->server && v->server->controller == v)\n\t\t\t\t{\n\t\t\t\t\tSendClientCommand(v->server, \"%s\", buf);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tSys_Printf(cluster, \"Client sent unknown string command: %s\\n\", buf);\n\t\t\t}\n\n\t\t\tbreak;\n\n\t\tcase clc_move:\n\t\t\tv->lost = ReadByte(m);\n\t\t\tReadByte(m);\n\t\t\tReadDeltaUsercmd(m, &nullcmd, &v->ucmds[0]);\n\t\t\tReadDeltaUsercmd(m, &v->ucmds[0], &v->ucmds[1]);\n\t\t\tReadDeltaUsercmd(m, &v->ucmds[1], &v->ucmds[2]);\n\n\t\t\tPMove(v, &v->ucmds[2]);\n\n\t\t\tif (v->ucmds[0].buttons & 2)\n\t\t\t{\n\t\t\t\tif (!v->server && !v->menunum)\n\t\t\t\t\tQW_SetMenu(v, MENU_DEFAULT);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase clc_tmove:\n\t\t\tv->origin[0] = ReadCoord(m, v->pext1);\n\t\t\tv->origin[1] = ReadCoord(m, v->pext1);\n\t\t\tv->origin[2] = ReadCoord(m, v->pext1);\n\t\t\tbreak;\n\n\t\tcase clc_upload:\n\t\t\tSys_Printf(cluster, \"Client uploads are not supported from %s\\n\", v->name);\n\t\t\tv->drop = true;\n\t\t\treturn;\n\n\t\tdefault:\n\t\t\tSys_Printf(cluster, \"bad clc from %s\\n\", v->name);\n\t\t\tv->drop = true;\n\t\t\treturn;\n\t\t}\n\t}\n}\n\nstatic const char dropcmd[] = {svc_stufftext, 'd', 'i', 's', 'c', 'o', 'n', 'n', 'e', 'c', 't', '\\n', '\\0'};\n\nvoid QW_FreeViewer(cluster_t *cluster, viewer_t *viewer)\n{\n\tchar buf[1024];\n\tviewer_t *oview;\n\tint i;\n\t//note: unlink them yourself.\n\n\tsnprintf(buf, sizeof(buf), \"%cQTV%c%s leaves the proxy\\n\", 91+128, 93+128, viewer->name);\n\tQW_StreamPrint(cluster, viewer->server, NULL, buf);\n\n\tSys_Printf(cluster, \"Dropping viewer %s\\n\", viewer->name);\n\n\t//spam them thrice, then forget about them\n\tNetchan_Transmit(cluster, &viewer->netchan, strlen(dropcmd)+1, dropcmd);\n\tNetchan_Transmit(cluster, &viewer->netchan, strlen(dropcmd)+1, dropcmd);\n\tNetchan_Transmit(cluster, &viewer->netchan, strlen(dropcmd)+1, dropcmd);\n\n\tfor (i = 0; i < MAX_BACK_BUFFERS; i++)\n\t{\n\t\tif (viewer->backbuf[i].data)\n\t\t\tfree(viewer->backbuf[i].data);\n\t}\n\n\tif (viewer->server)\n\t{\n\t\tif (viewer->server->controller == viewer)\n\t\t{\n\t\t\tif (viewer->server->autodisconnect == AD_WHENEMPTY)\n\t\t\t\tviewer->server->errored = ERR_DROP;\n\t\t\telse\n\t\t\t\tviewer->server->controller = NULL;\n\t\t}\n\n\t\tviewer->server->numviewers--;\n\t}\n\n\tfor (oview = cluster->viewers; oview; oview = oview->next)\n\t{\n\t\tif (oview->commentator == viewer)\n\t\t\tQW_SetCommentator(cluster, oview, NULL);\n\t}\n\n\tfree(viewer);\n\n\tcluster->numviewers--;\n}\n\nvoid SendViewerPackets(cluster_t *cluster, viewer_t *v)\n{\n\tchar buffer[MAX_MSGLEN];\n\tnetmsg_t m;\n\tint read;\n\tsv_t *useserver;\n\n\tv->drop |= v->netchan.drop;\n\n\tif (v->timeout < cluster->curtime)\n\t{\n\t\tSys_Printf(cluster, \"Viewer %s timed out\\n\", v->name);\n\t\tv->drop = true;\n\t}\n\n\tif (v->netchan.isnqprotocol && (v->server == NULL || v->server->parsingconnectiondata))\n\t{\n\t\tv->maysend = (v->nextpacket < cluster->curtime);\n\t}\n\tif (!Netchan_CanPacket(&v->netchan))\n\t{\n\t\treturn;\n\t}\n\tif (v->maysend)\t//don't send incompleate connection data.\n\t{\n//\t\tprintf(\"maysend (%i, %i)\\n\", cluster->curtime, v->nextpacket);\n\t\tv->nextpacket = v->nextpacket + 1000/NQ_PACKETS_PER_SECOND;\n\t\tif (v->nextpacket < cluster->curtime)\n\t\t\tv->nextpacket = cluster->curtime;\n\t\tif (v->nextpacket > cluster->curtime+1000/NQ_PACKETS_PER_SECOND)\n\t\t\tv->nextpacket = cluster->curtime+1000/NQ_PACKETS_PER_SECOND;\n\n\t\tuseserver = v->server;\n\t\tif (useserver && useserver->controller == v)\n\t\t\tv->netchan.outgoing_sequence = useserver->netchan.incoming_sequence;\n\t\telse\n\t\t{\n\t\t\tif (useserver && useserver->parsingconnectiondata)\n\t\t\t\tuseserver = NULL;\n\t\t}\n\n\t\tv->maysend = false;\n\t\tInitNetMsg(&m, buffer, v->netchan.maxdatagramlen);\n\t\tm.cursize = 0;\n\t\tif (v->thinksitsconnected)\n\t\t{\n\t\t\tif (v->netchan.isnqprotocol)\n\t\t\t\tSendNQPlayerStates(cluster, useserver, v, &m);\n\t\t\telse\n\t\t\t\tSendPlayerStates(useserver, v, &m);\n\t\t\tUpdateStats(useserver, v);\n\t\t}\n\t\tif (v->menunum)\n\t\t\tMenu_Draw(cluster, v);\n\t\telse if (v->server && v->server->parsingconnectiondata && v->server->controller != v)\n\t\t{\n\t\t\tWriteByte(&m, svc_centerprint);\n\t\t\tWriteString(&m, v->server->status);\n\t\t}\n\n\t\tif (v->server && v->server->controller == v)\n\t\t{\n\t\t\tint saved;\n\t\t\tsaved = v->netchan.incoming_sequence;\n\t\t\tv->netchan.incoming_sequence = v->server->netchan.incoming_sequence;\n\t\t\tNetchan_Transmit(cluster, &v->netchan, m.cursize, m.data);\n\t\t\tv->netchan.incoming_sequence = saved;\n\t\t}\n\t\telse\n\t\t\tNetchan_Transmit(cluster, &v->netchan, m.cursize, m.data);\n\n\t\tif (!v->netchan.message.cursize && v->backbuffered)\n\t\t{//shift the backbuffers around\n\t\t\tmemcpy(v->netchan.message.data, v->backbuf[0].data,  v->backbuf[0].cursize);\n\t\t\tv->netchan.message.cursize = v->backbuf[0].cursize;\n\t\t\tfor (read = 0; read < v->backbuffered; read++)\n\t\t\t{\n\t\t\t\tif (read == v->backbuffered-1)\n\t\t\t\t{\n\t\t\t\t\tv->backbuf[read].cursize = 0;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tmemcpy(v->backbuf[read].data, v->backbuf[read+1].data,  v->backbuf[read+1].cursize);\n\t\t\t\t\tv->backbuf[read].cursize = v->backbuf[read+1].cursize;\n\t\t\t\t}\n\t\t\t}\n\t\t\tv->backbuffered--;\n\t\t}\n\t}\n//\telse\n//\t\tprintf(\"maynotsend (%i, %i)\\n\", cluster->curtime, v->nextpacket);\n}\n\nvoid QW_ProcessUDPPacket(cluster_t *cluster, netmsg_t *m, netadr_t from)\n{\n\tchar tempbuffer[256];\n\tint qport;\n\n\tviewer_t *v;\n\tsv_t *useserver;\n\n\tif (*(int*)m->data == -1)\n\t{\t//connectionless message\n\t\tif (TURN_IsRequest(cluster, m, &from))\n\t\t\treturn;\n\t\tm->readpos = 0;\n\n\t\tConnectionlessPacket(cluster, &from, m);\n\t\treturn;\n\t}\n\n\tif (m->cursize < 10)\t//otherwise it's a runt or bad.\n\t{\n\t\tqport = 0;\n\t}\n\telse\n\t{\n\t\t//read the qport\n\t\tReadLong(m);\n\t\tReadLong(m);\n\t\tqport = ReadShort(m);\n\t}\n\n\tfor (v = cluster->viewers; v; v = v->next)\n\t{\n\t\tif (v->netchan.isnqprotocol)\n\t\t{\n\t\t\tif (Net_CompareAddress(&v->netchan.remote_address, &from, 0, 1))\n\t\t\t{\n\t\t\t\tif (NQNetchan_Process(cluster, &v->netchan, m))\n\t\t\t\t{\n\t\t\t\t\tuseserver = v->server;\n\t\t\t\t\tif (useserver && useserver->parsingconnectiondata)\n\t\t\t\t\t\tuseserver = NULL;\n\n\t\t\t\t\tv->timeout = cluster->curtime + 15*1000;\n\n\t\t\t\t\tParseNQC(cluster, useserver, v, m);\n\n\t\t\t\t\tif (v->server && v->server->controller == v)\n\t\t\t\t\t{\n\t\t\t\t\t\tQTV_Run(v->server);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (Net_CompareAddress(&v->netchan.remote_address, &from, v->netchan.qport, qport))\n\t\t\t{\n\t\t\t\tif (v->server && v->server->controller == v && v->maysend)\n\t\t\t\t\tSendViewerPackets(cluster, v);\t//do this before we read the new sequences\n\n\t\t\t\tif (Netchan_Process(&v->netchan, m))\n\t\t\t\t{\n\t\t\t\t\tuseserver = v->server;\n\t\t\t\t\tif (useserver && useserver->parsingconnectiondata && useserver->controller != v)\n\t\t\t\t\t\tuseserver = NULL;\n\n\t\t\t\t\tv->timeout = cluster->curtime + 15*1000;\n\n\t\t\t\t\tif (v->server && v->server->controller == v)\n\t\t\t\t\t{\n//\t\t\t\t\t\tv->maysend = true;\n\t\t\t\t\t\tv->server->maysend = true;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tv->netchan.outgoing_sequence = v->netchan.incoming_sequence;\t//compensate for client->server packetloss.\n\t\t\t\t\t\tif (!v->server)\n\t\t\t\t\t\t\tv->maysend = true;\n\t\t\t\t\t\telse if (!v->chokeme || !cluster->chokeonnotupdated)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tv->maysend = true;\n\t\t\t\t\t\t\tv->chokeme = cluster->chokeonnotupdated;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tParseQWC(cluster, useserver, v, m);\n\n\t\t\t\t\tif (v->server && v->server->controller == v)\n\t\t\t\t\t{\n\t\t\t\t\t\tQTV_Run(v->server);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n\tm->readpos = 0;\n\n\tif (TURN_IsRequest(cluster, m, &from))\n\t\treturn;\n\tm->readpos = 0;\n\n\tif (cluster->allownqclients)\n\t{\n\t\tunsigned int ctrl;\n\t\t//NQ connectionless packet?\n\t\tctrl = ReadLong(m);\n\t\tctrl = SwapLong(ctrl);\n\t\tif (ctrl & NETFLAG_CTL)\n\t\t{\t//looks hopeful\n\t\t\tswitch(ReadByte(m))\n\t\t\t{\n\t\t\tcase CCREQ_SERVER_INFO:\n\t\t\t\tReadString(m, tempbuffer, sizeof(tempbuffer));\n\t\t\t\tif (!strcmp(tempbuffer, NQ_NETCHAN_GAMENAME))\n\t\t\t\t{\n\t\t\t\t\tm->cursize = 0;\n\t\t\t\t\tWriteLong(m, 0);\n\t\t\t\t\tWriteByte(m, CCREP_SERVER_INFO);\n\t\t\t\t\tWriteString(m, \"??\");\n\t\t\t\t\tWriteString(m, cluster->hostname);\n\t\t\t\t\tWriteString(m, \"Quake TV\");\n\t\t\t\t\tWriteByte(m, cluster->numviewers>255?255:cluster->numviewers);\n\t\t\t\t\tWriteByte(m, cluster->maxviewers>255?255:cluster->maxviewers);\n\t\t\t\t\tWriteByte(m, NQ_NETCHAN_VERSION);\n\t\t\t\t\t*(int*)m->data = BigLong(NETFLAG_CTL | m->cursize);\n\t\t\t\t\tNET_SendPacket(cluster, NET_ChooseSocket(cluster->qwdsocket, &from, from), m->cursize, m->data, from);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase CCREQ_CONNECT:\n\t\t\t\tReadString(m, tempbuffer, sizeof(tempbuffer));\n\t\t\t\tif (!strcmp(tempbuffer, NQ_NETCHAN_GAMENAME))\n\t\t\t\t{\n\t\t\t\t\tif (ReadByte(m) == NQ_NETCHAN_VERSION)\n\t\t\t\t\t{\n\t\t\t\t\t\t//proquake extensions\n\t\t\t\t\t\t/*int mod =*/ ReadByte(m);\n\t\t\t\t\t\t/*int modver =*/ ReadByte(m);\n\t\t\t\t\t\t/*int flags =*/ ReadByte(m);\n\t\t\t\t\t\t/*int passwd =*/ ReadLong(m);\n\n\t\t\t\t\t\t//fte extension, sent so that dual-protocol servers will not create connections for dual-protocol clients\n\t\t\t\t\t\t//the connectnq command disables this (as well as the qw hand shake) if you really want to use nq protocols with fte clients\n\t\t\t\t\t\tReadString(m, tempbuffer, sizeof(tempbuffer));\n\t\t\t\t\t\tif (!strncmp(tempbuffer, \"getchallenge\", 12))\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\t//drop any old nq clients from this address\n\t\t\t\t\t\tfor (v = cluster->viewers; v; v = v->next)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (v->netchan.isnqprotocol)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tif (Net_CompareAddress(&v->netchan.remote_address, &from, 0, 1))\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tSys_Printf(cluster, \"Dup connect from %s\\n\", v->name);\n\t\t\t\t\t\t\t\t\tv->drop = true;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tNewNQClient(cluster, &from);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n}\n\nvoid QW_TCPConnection(cluster_t *cluster, oproxy_t *sock, char *initialstreamname)\n{\n\tint alen;\n\ttcpconnect_t *tc;\n\n\t//clean up the pending source a bit...\n\tif (sock->srcfile) fclose(sock->srcfile), sock->srcfile = NULL;\n\n\tif (sock->drop)\n\t\ttc = NULL;\t//FIXME\n\telse\n\t\ttc = malloc(sizeof(*tc));\n\tif (!tc)\n\t{\n\t\tclosesocket(sock->sock);\n\t\tfree(initialstreamname);\n\t}\n\telse\n\t{\t//okay, we're adding this as a client\n\t\t//try and disable nagle, we don't really want to be wasting time not sending anything.\n\t\tint _true = 1;\n\t\tsetsockopt(sock->sock, IPPROTO_TCP, TCP_NODELAY, (char *)&_true, sizeof(_true));\n\n\t\ttc->sock = sock->sock;\n\t\ttc->websocket = sock->websocket;\t//copy it over\n\n\t\ttc->inbuffersize = sock->inbuffersize;\n\t\tmemcpy(tc->inbuffer, sock->inbuffer, tc->inbuffersize);\n\t\ttc->outbuffersize = sock->buffersize;\n\t\tmemcpy(tc->outbuffer, sock->buffer+sock->bufferpos, tc->outbuffersize);\n\n\t\tmemset(&tc->peeraddr, 0, sizeof(tc->peeraddr));\n\t\ttc->peeraddr.tcpcon = tc;\n\n\t\talen = sizeof(tc->peeraddr.sockaddr);\n\t\tgetpeername(sock->sock, (struct sockaddr*)&tc->peeraddr.sockaddr, &alen);\n\n\t\ttc->initialstreamname = initialstreamname;\n\t\ttc->next = cluster->tcpconnects;\n\t\tcluster->tcpconnects = tc;\n\t}\n\n\t//okay, we're done with it.\n\tfree(sock);\n\tcluster->numproxies--;\n}\n\nvoid QW_UpdateUDPStuff(cluster_t *cluster)\n{\n\tchar buffer[MAX_MSGLEN];\t//contains read info\n\tnetadr_t from;\n\tint fromsize = sizeof(from.sockaddr);\n\tint read;\n\tnetmsg_t m;\n\tint socketno;\n\ttcpconnect_t *tc, **l;\n\n\tviewer_t *v, *f;\n\n\tif (*cluster->master && (cluster->curtime > cluster->mastersendtime || cluster->mastersendtime > cluster->curtime + 4*1000*60))\t//urm... time wrapped?\n\t{\n\t\tif (NET_StringToAddr(cluster->master, &from, 27000))\n\t\t{\n\t\t\tif (cluster->turnenabled)\n\t\t\t\tsprintf(buffer, \"\\377\\377\\377\\377\"\"heartbeat FTEMaster c=%s\\n\", cluster->chalkey);\t//fill buffer with a heartbeat\n\t\t\telse if (cluster->protocolname)\n\t\t\t\tsprintf(buffer, \"\\377\\377\\377\\377\"\"heartbeat Darkplaces\\n\");\t//older, broader compatibility.\n\t\t\telse\n\t\t\t\tsprintf(buffer, \"a\\n%i\\n0\\n\", cluster->mastersequence++);\t//fill buffer with a heartbeat\n//why is there no \\xff\\xff\\xff\\xff ?..\n\t\t\tNET_SendPacket(cluster, NET_ChooseSocket(cluster->qwdsocket, &from, from), strlen(buffer), buffer, from);\n\t\t}\n\t\telse\n\t\t\tSys_Printf(cluster, \"Cannot resolve master %s\\n\", cluster->master);\n\n\t\tcluster->mastersendtime = cluster->curtime + 3*1000*60;\t//3 minuites.\n\t}\n\n\t/* initialised for reading */\n\tInitNetMsg(&m, buffer, sizeof(buffer));\n\n\tsocketno = 0;\n\tfor (;;)\n\t{\n\t\tif (cluster->qwdsocket[socketno] == INVALID_SOCKET)\n\t\t{\n\t\t\tsocketno++;\n\t\t\tif (socketno >= SOCKETGROUPS)\n\t\t\t\tbreak;\n\t\t\tcontinue;\n\t\t}\n\t\tmemset(&from, 0, sizeof(from));\n\t\tread = recvfrom(cluster->qwdsocket[socketno], buffer, sizeof(buffer)-1, 0, (struct sockaddr*)&from.sockaddr, (unsigned*)&fromsize);\n\n\t\tif (read < 0)\t//it's bad.\n\t\t{\n\t\t\tsocketno++;\n\t\t\tif (socketno >= SOCKETGROUPS)\n\t\t\t\tbreak;\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (read <= 5)\t//otherwise it's a runt or bad.\n\t\t{\n\t\t\tif (read == 1 && *buffer == 'l')\n\t\t\t{\t//ffs. easier to just fix it up here.\n\t\t\t\tbuffer[0] =\n\t\t\t\tbuffer[1] =\n\t\t\t\tbuffer[2] =\n\t\t\t\tbuffer[3] = 0xff;\n\t\t\t\tbuffer[4] = 'l';\n\t\t\t\tread = 5;\n\t\t\t}\n\t\t\telse\n\t\t\t\tcontinue;\n\t\t}\n\n\t\tm.cursize = read;\n\t\tm.data = buffer;\n\t\tm.readpos = 0;\n\n\t\tbuffer[m.cursize] = 0;\t//make sure its null terminated.\n\t\tQW_ProcessUDPPacket(cluster, &m, from);\n\t}\n\n\tfor (tc = cluster->tcpconnects; tc; tc = tc->next)\n\t{\n\t\tif (tc->outbuffersize)\n\t\t{\n\t\t\tint clen = send(tc->sock, tc->outbuffer, tc->outbuffersize, 0);\n\t\t\tif (clen > 0)\n\t\t\t{\n\t\t\t\tmemmove(tc->outbuffer, tc->outbuffer+clen, tc->outbuffersize-clen);\n\t\t\t\ttc->outbuffersize-=clen;\n\t\t\t}\n\t\t}\n\t}\n\n\tfor (l = &cluster->tcpconnects; *l; )\n\t{\n\t\tint clen;\n\t\ttc = *l;\n\t\tread = sizeof(tc->inbuffer) - tc->inbuffersize;\n\t\tread = NET_WebSocketRecv(tc->sock, &tc->websocket, tc->inbuffer+tc->inbuffersize, read, &clen);\n\t\tif (read > 0)\n\t\t\ttc->inbuffersize += read;\n\t\tif (read == 0 || read < 0)\n\t\t{\n\t\t\tif (read == 0 || qerrno != NET_EWOULDBLOCK)\n\t\t\t{\n\t\t\t\t*l = tc->next;\n\t\t\t\tif (tc->sock != INVALID_SOCKET)\n\t\t\t\t\tclosesocket(tc->sock);\n\t\t\t\tfree(tc->initialstreamname);\n\t\t\t\tfree(tc);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\t\t\n\t\tif (clen >= 0)\n\t\t{\n\t\t\t/*if it really is a webclient connection, then the stream will be packetized already\n\t\t\tso we don't waste extra space*/\n\t\t\tm.data = tc->inbuffer;\n\t\t\tm.readpos = 0;\n\t\t\tm.cursize = read;\n\n\t\t\tQW_ProcessUDPPacket(cluster, &m, tc->peeraddr);\n\n\t\t\tmemmove(tc->inbuffer, tc->inbuffer+read, tc->inbuffersize - (read));\n\t\t\ttc->inbuffersize -= read;\n\t\t\tcontinue;\t//ask to read the next packet\n\t\t}\n\t\telse\n\t\t{\n\t\t\twhile (tc->inbuffersize >= 2)\n\t\t\t{\n\t\t\t\tread = (tc->inbuffer[0]<<8) | tc->inbuffer[1];\n\t\t\t\tif (tc->inbuffersize >= 2+read)\n\t\t\t\t{\n\t\t\t\t\tm.data = tc->inbuffer+2;\n\t\t\t\t\tm.readpos = 0;\n\t\t\t\t\tm.cursize = read;\n\n\t\t\t\t\tQW_ProcessUDPPacket(cluster, &m, tc->peeraddr);\n\n\t\t\t\t\tmemmove(tc->inbuffer, tc->inbuffer+2+read, tc->inbuffersize - (2+read));\n\t\t\t\t\ttc->inbuffersize -= 2+read;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\n\t\tl = &(*l)->next;\n\t}\n\n\tif (cluster->viewers && cluster->viewers->drop)\n\t{\n//\t\tSys_Printf(cluster, \"Dropping viewer %s\\n\", v->name);\n\t\tf = cluster->viewers;\n\t\tcluster->viewers = f->next;\n\n\t\tQW_FreeViewer(cluster, f);\n\t}\n\n\tfor (v = cluster->viewers; v; v = v->next)\n\t{\n\t\tif (v->next && v->next->drop)\n\t\t{\t//free the next/\n//\t\t\tSys_Printf(cluster, \"Dropping viewer %s\\n\", v->name);\n\t\t\tf = v->next;\n\t\t\tv->next = f->next;\n\n\t\t\tQW_FreeViewer(cluster, f);\n\t\t}\n\n\t\tSendViewerPackets(cluster, v);\n\t}\n}\n"
  },
  {
    "path": "fteqtv/rcon.c",
    "content": "/*\nCopyright (C) 1996-1997 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n*/\n\n#include \"qtv.h\"\n#include <time.h>\n#ifdef _WIN32\n#include <direct.h>\n#else\n#include <unistd.h>\n#endif\n\n#include \"bsd_string.h\"\n\n#define MAX_INFO_KEY 64\n\n#if defined(SVNREVISION) && defined(SVNDATE)\n\t#define QTVBUILD STRINGIFY(SVNREVISION)\", \"STRINGIFY(SVNDATE)\n#elif defined(SVNREVISION)\n\t#define QTVBUILD STRINGIFY(SVNREVISION)\", \"__DATE__\n#else\n\t#define QTVBUILD __DATE__\n#endif\n\n//I apologise for this if it breaks your formatting or anything\n#define HELPSTRING \"\\\nFTEQTV proxy commands: (build \"QTVBUILD\")\\n\\\n----------------------\\n\\\nconnect, qtv, addserver\\n\\\n  connect to a MVD stream (TCP)\\n\\\nqtvlist\\n\\\n  lists available streams on a proxy\\n\\\nqw\\n\\\n  connect to a server as a player (UDP)\\n\\\nadddemo\\n\\\n  play a demo from a MVD file\\n\\\nport\\n\\\n  UDP port for QuakeWorld client connections\\n\\\nmvdport\\n\\\n  specify TCP port for MVD broadcasting\\n\\\nmaxviewers, maxproxies\\n\\\n  limit number of connections\\n\\\nstatus, choke, late, talking, nobsp, reconnect, exec, password, master, hostname, record, stop, quit\\n\\\n  other random commands\\n\\\n\\n\"\n\n\n\n\n\nchar *Info_ValueForKey (char *s, const char *key, char *buffer, int buffersize)\n{\n\tchar\tpkey[1024];\n\tchar\t*o;\n\n\tif (*s == '\\\\')\n\t\ts++;\n\twhile (1)\n\t{\n\t\to = pkey;\n\t\twhile (*s != '\\\\')\n\t\t{\n\t\t\tif (!*s)\n\t\t\t{\n\t\t\t\t*buffer='\\0';\n\t\t\t\treturn buffer;\n\t\t\t}\n\t\t\t*o++ = *s++;\n\t\t\tif (o+2 >= pkey+sizeof(pkey))\t//hrm. hackers at work..\n\t\t\t{\n\t\t\t\t*buffer='\\0';\n\t\t\t\treturn buffer;\n\t\t\t}\n\t\t}\n\t\t*o = 0;\n\t\ts++;\n\n\t\to = buffer;\n\n\t\twhile (*s != '\\\\' && *s)\n\t\t{\n\t\t\tif (!*s)\n\t\t\t{\n\t\t\t\t*buffer='\\0';\n\t\t\t\treturn buffer;\n\t\t\t}\n\t\t\t*o++ = *s++;\n\n\t\t\tif (o+2 >= buffer+buffersize)\t//hrm. hackers at work..\n\t\t\t{\n\t\t\t\t*buffer='\\0';\n\t\t\t\treturn buffer;\n\t\t\t}\n\t\t}\n\t\t*o = 0;\n\n\t\tif (!strcmp (key, pkey) )\n\t\t\treturn buffer;\n\n\t\tif (!*s)\n\t\t{\n\t\t\t*buffer='\\0';\n\t\t\treturn buffer;\n\t\t}\n\t\ts++;\n\t}\n}\n\nvoid Info_RemoveKey (char *s, const char *key)\n{\n\tchar\t*start;\n\tchar\tpkey[1024];\n\tchar\tvalue[1024];\n\tchar\t*o;\n\n\tif (strstr (key, \"\\\\\"))\n\t{\n//\t\tprintf (\"Key has a slash\\n\");\n\t\treturn;\n\t}\n\n\twhile (1)\n\t{\n\t\tstart = s;\n\t\tif (*s == '\\\\')\n\t\t\ts++;\n\t\to = pkey;\n\t\twhile (*s != '\\\\')\n\t\t{\n\t\t\tif (!*s)\n\t\t\t\treturn;\n\t\t\t*o++ = *s++;\n\t\t}\n\t\t*o = 0;\n\t\ts++;\n\n\t\to = value;\n\t\twhile (*s != '\\\\' && *s)\n\t\t{\n\t\t\tif (!*s)\n\t\t\t\treturn;\n\t\t\t*o++ = *s++;\n\t\t}\n\t\t*o = 0;\n\n\t\tif (!strcmp (key, pkey) )\n\t\t{\n\t\t\t//strip out the value by copying the next string over the top of this one\n\t\t\t//(we were using strcpy, but valgrind moans and glibc fucks it up. they should not overlap... so use our own crappy inefficient alternative)\n\t\t\twhile(*s)\n\t\t\t\t*start++ = *s++;\n\t\t\t*start = 0;\n\t\t\treturn;\n\t\t}\n\n\t\tif (!*s)\n\t\t\treturn;\n\t}\n\n}\n\nvoid Info_SetValueForStarKey (char *s, const char *key, const char *value, int maxsize)\n{\n\tchar\tnewv[1024], *v;\n\tint\t\tc;\n#ifdef SERVERONLY\n\textern cvar_t sv_highchars;\n#endif\n\n\tif (strstr (key, \"\\\\\") || strstr (value, \"\\\\\") )\n\t{\n//\t\tprintf (\"Key has a slash\\n\");\n\t\treturn;\n\t}\n\n\tif (strstr (key, \"\\\"\") || strstr (value, \"\\\"\") )\n\t{\n//\t\tprintf (\"Key has a quote\\n\");\n\t\treturn;\n\t}\n\n\tif (strlen(key) >= MAX_INFO_KEY || strlen(value) >= MAX_INFO_KEY)\n\t{\n//\t\tprintf (\"Key or value is too long\\n\");\n\t\treturn;\n\t}\n\n\t// this next line is kinda trippy\n\tif (*(v = Info_ValueForKey(s, key, newv, sizeof(newv))))\n\t{\n\t\t// key exists, make sure we have enough room for new value, if we don't,\n\t\t// don't change it!\n\t\tif (strlen(value) - strlen(v) + strlen(s) + 1 > maxsize)\n\t\t{\n\t//\t\tCon_Printf (\"Info string length exceeded\\n\");\n\t\t\treturn;\n\t\t}\n\t}\n\n\n\tInfo_RemoveKey (s, key);\n\tif (!value || !strlen(value))\n\t\treturn;\n\n\tsnprintf (newv, sizeof(newv)-1, \"\\\\%s\\\\%s\", key, value);\n\n\tif ((int)(strlen(newv) + strlen(s) + 1) > maxsize)\n\t{\n//\t\tprintf (\"info buffer is too small\\n\");\n\t\treturn;\n\t}\n\n\t// only copy ascii values\n\ts += strlen(s);\n\tv = newv;\n\twhile (*v)\n\t{\n\t\tc = (unsigned char)*v++;\n\n//\t\tc &= 127;\t\t// strip high bits\n\t\tif (c > 13) // && c < 127)\n\t\t\t*s++ = c;\n\t}\n\t*s = 0;\n}\n\n\n\n\n#define DEFAULT_PUNCTUATION \"(,{})(\\':;=!><&|+\"\n\nchar *COM_ParseToken (char *data, char *out, int outsize, const char *punctuation)\n{\n\tint\t\tc;\n\tint\t\tlen;\n\n\tif (!punctuation)\n\t\tpunctuation = DEFAULT_PUNCTUATION;\n\n\tlen = 0;\n\tout[0] = 0;\n\n\tif (!data)\n\t\treturn NULL;\n\n// skip whitespace\nskipwhite:\n\twhile ( (c = *data) <= ' ')\n\t{\n\t\tif (c == 0)\n\t\t\treturn NULL;\t\t\t// end of file;\n\t\tdata++;\n\t}\n\n// skip // comments\n\tif (c=='/')\n\t{\n\t\tif (data[1] == '/')\n\t\t{\n\t\t\twhile (*data && *data != '\\n')\n\t\t\t\tdata++;\n\t\t\tgoto skipwhite;\n\t\t}\n\t\telse if (data[1] == '*')\n\t\t{\n\t\t\tdata+=2;\n\t\t\twhile (*data && (*data != '*' || data[1] != '/'))\n\t\t\t\tdata++;\n\t\t\tdata+=2;\n\t\t\tgoto skipwhite;\n\t\t}\n\t}\n\n\n// handle quoted strings specially\n\tif (c == '\\\"')\n\t{\n\t\tdata++;\n\t\twhile (1)\n\t\t{\n\t\t\tif (len >= outsize-1)\n\t\t\t{\n\t\t\t\tout[len] = '\\0';\n\t\t\t\treturn data;\n\t\t\t}\n\t\t\tc = *data++;\n\t\t\tif (c=='\\\"' || !c)\n\t\t\t{\n\t\t\t\tout[len] = 0;\n\t\t\t\treturn data;\n\t\t\t}\n\t\t\tout[len] = c;\n\t\t\tlen++;\n\t\t}\n\t}\n\n// parse single characters\n\tif (strchr(punctuation, c))\n\t{\n\t\tout[len] = c;\n\t\tlen++;\n\t\tout[len] = 0;\n\t\treturn data+1;\n\t}\n\n// parse a regular word\n\tdo\n\t{\n\t\tif (len >= outsize-1)\n\t\t\tbreak;\n\t\tout[len] = c;\n\t\tdata++;\n\t\tlen++;\n\t\tc = *data;\n\t\tif (strchr(punctuation, c))\n\t\t\tbreak;\n\t} while (c>32);\n\n\tout[len] = 0;\n\treturn data;\n}\n\n\n\n\nvoid Cmd_Printf(cmdctxt_t *ctx, char *fmt, ...)\n{\n\tva_list\t\targptr;\n\tchar\t\tstring[2048];\n\n\tva_start (argptr, fmt);\n\tvsnprintf (string, sizeof(string)-1, fmt,argptr);\n\tstring[sizeof(string)-1] = 0;\n\tva_end (argptr);\n\n\tif (ctx->printfunc)\n\t\tctx->printfunc(ctx, string);\n\telse if (ctx->qtv)\n\t\tQTV_Printf(ctx->qtv, \"%s\", string);\n\telse\n\t\tSys_Printf(ctx->cluster, \"%s\", string);\n}\n\nvoid Cmd_Hostname(cmdctxt_t *ctx)\n{\n\tif (Cmd_Argc(ctx) < 2)\n\t{\n\t\tif (*ctx->cluster->hostname)\n\t\t\tCmd_Printf(ctx, \"Current hostname is \\\"%s\\\"\\n\", ctx->cluster->hostname);\n\t\telse\n\t\t\tCmd_Printf(ctx, \"No master server is currently set.\\n\");\n\t}\n\telse\n\t{\n\t\tstrlcpy(ctx->cluster->hostname, Cmd_Argv(ctx, 1), sizeof(ctx->cluster->hostname));\n\n\t\tCmd_Printf(ctx, \"hostname set to \\\"%s\\\"\\n\", ctx->cluster->hostname);\n\t}\n}\n\nvoid Cmd_Master(cmdctxt_t *ctx)\n{\n\tSOCKET s;\n\tchar *newval = Cmd_Argv(ctx, 1);\n\tnetadr_t addr;\n\n\tif (Cmd_Argc(ctx) < 2)\n\t{\n\t\tif (*ctx->cluster->master)\n\t\t\tCmd_Printf(ctx, \"Subscribed to a master server (use '-' to clear)\\n\");\n\t\telse\n\t\t\tCmd_Printf(ctx, \"No master server is currently set.\\n\");\n\t\treturn;\n\t}\n\n\tif (!strcmp(newval, \"-\"))\n\t{\n\t\tstrlcpy(ctx->cluster->master, \"\", sizeof(ctx->cluster->master));\n\t\tCmd_Printf(ctx, \"Master server cleared\\n\");\n\t\treturn;\n\t}\n\n\tif (!NET_StringToAddr(newval, &addr, 27000))\t//send a ping like a qw server does. this is kinda pointless of course.\n\t{\n\t\tCmd_Printf(ctx, \"Couldn't resolve address\\n\");\n\t\treturn;\n\t}\n\n\tstrlcpy(ctx->cluster->master, newval, sizeof(ctx->cluster->master));\n\tctx->cluster->mastersendtime = ctx->cluster->curtime;\n\n\ts = NET_ChooseSocket(ctx->cluster->qwdsocket, &addr, addr);\n\tif (s != INVALID_SOCKET)\n\t\tNET_SendPacket (ctx->cluster, s, 1, \"k\", addr);\n\tCmd_Printf(ctx, \"Master server set.\\n\");\n}\n\nvoid Cmd_UDPPort(cmdctxt_t *ctx)\n{\n\tint newp = atoi(Cmd_Argv(ctx, 1));\n\tctx->cluster->qwlistenportnum = newp;\n\tNET_InitUDPSocket(ctx->cluster, newp, SG_IPV6);\n\tNET_InitUDPSocket(ctx->cluster, newp, SG_IPV4);\n}\nvoid Cmd_AdminPassword(cmdctxt_t *ctx)\n{\n\tif (!Cmd_IsLocal(ctx))\n\t{\n\t\tCmd_Printf(ctx, \"Rejecting remote password change.\\n\");\n\t\treturn;\n\t}\n\n\tif (Cmd_Argc(ctx) < 2)\n\t{\n\t\tif (*ctx->cluster->adminpassword)\n\t\t\tCmd_Printf(ctx, \"An admin password is currently set\\n\");\n\t\telse\n\t\t\tCmd_Printf(ctx, \"No admin password is currently set\\n\");\n\t}\n\telse\n\t{\n\t\tstrlcpy(ctx->cluster->adminpassword, Cmd_Argv(ctx, 1), sizeof(ctx->cluster->adminpassword));\n\t\tCmd_Printf(ctx, \"Password changed.\\n\");\n\t}\n}\n\nvoid Cmd_GenericQuery(cmdctxt_t *ctx, int dataset)\n{\n\tchar *method = \"tcp:\";\n\tchar *address, *password;\n\tif (Cmd_Argc(ctx) < 2)\n\t{\n\t\tCmd_Printf(ctx, \"%s requires an ip:port parameter\\n\", Cmd_Argv(ctx, 0));\n\t\treturn;\n\t}\n\n\taddress = Cmd_Argv(ctx, 1);\n\tpassword = Cmd_Argv(ctx, 2);\n\t//this is evil\n\t/* Holy crap, Spike... Holy crap. */\n\tmemmove(address+strlen(method), address, ARG_LEN-(1+strlen(method)));\n\tstrncpy(address, method, strlen(method));\n\n\tif (!QTV_NewServerConnection(ctx->cluster, ctx->streamid, address, password, false, AD_NO, false, dataset))\n\t\tCmd_Printf(ctx, \"Failed to connect to \\\"%s\\\", connection aborted\\n\", address);\n\n\tCmd_Printf(ctx, \"Querying \\\"%s\\\"\\n\", address);\n}\n\n\nvoid Cmd_QTVList(cmdctxt_t *ctx)\n{\n\tCmd_GenericQuery(ctx, 1);\n}\nvoid Cmd_QTVDemoList(cmdctxt_t *ctx)\n{\n\tCmd_GenericQuery(ctx, 2);\n}\n\nvoid Cmd_GenericConnect(cmdctxt_t *ctx, char *method, enum autodisconnect_e autoclose)\n{\n\tsv_t *sv;\n\tchar *address, *password;\n\tif (Cmd_Argc(ctx) < 2)\n\t{\n\t\tif (!strncmp(method, \"file\", 4))\n\t\t\tCmd_Printf(ctx, \"%s requires a demo name parameter\\n\", Cmd_Argv(ctx, 0));\n\t\telse if (!strncmp(method, \"dir\", 3))\n\t\t\tCmd_Printf(ctx, \"%s requires a demo directory parameter\\n\", Cmd_Argv(ctx, 0));\n\t\telse\n\t\t\tCmd_Printf(ctx, \"%s requires an ip:port parameter\\n\", Cmd_Argv(ctx, 0));\n\t\treturn;\n\t}\n\n\taddress = Cmd_Argv(ctx, 1);\n\tpassword = Cmd_Argv(ctx, 2);\n\t//this is evil\n\t/* Holy crap, Spike... Holy crap. */\n\tmemmove(address+strlen(method), address, ARG_LEN-(1+strlen(method)));\n\tstrncpy(address, method, strlen(method));\n\n\tsv = QTV_NewServerConnection(ctx->cluster, ctx->streamid?ctx->streamid:1, address, password, false, autoclose, false, false);\n\tif (!sv)\n\t\tCmd_Printf(ctx, \"Failed to connect to \\\"%s\\\", connection aborted\\n\", address);\n\telse\n\t\tCmd_Printf(ctx, \"Source registered \\\"%s\\\" as stream %i\\n\", address, sv->streamid);\n}\n\nvoid Cmd_QTVConnect(cmdctxt_t *ctx)\n{\n\tCmd_GenericConnect(ctx, \"tcp:\", AD_NO);\n}\nvoid Cmd_QWConnect(cmdctxt_t *ctx)\n{\n\tCmd_GenericConnect(ctx, \"udp:\", AD_STATUSPOLL);\n}\nvoid Cmd_MVDConnect(cmdctxt_t *ctx)\n{\n\tCmd_GenericConnect(ctx, \"file:\", AD_NO);\n}\nvoid Cmd_DirMVDConnect(cmdctxt_t *ctx)\n{\n\tsrand(time(NULL));\n\tCmd_GenericConnect(ctx, \"dir:\", AD_NO);\n}\n\nvoid Cmd_Exec(cmdctxt_t *ctx)\n{\n\tFILE *f;\n\tchar line[512], *l;\n\tchar *fname = Cmd_Argv(ctx, 1);\n\n\tif (!Cmd_IsLocal(ctx))\n\t{\n\t\tif (*fname == '\\\\' || *fname == '/' || strstr(fname, \"..\") || fname[1] == ':')\n\t\t{\n\t\t\tCmd_Printf(ctx, \"Absolute paths are prohibited.\\n\");\n\t\t\treturn;\n\t\t}\n\t\tif (!strncmp(fname, \"usercfg/\", 8))\t//this is how we stop users from execing a 50gb pk3..\n\t\t{\n\t\t\tCmd_Printf(ctx, \"Remote-execed configs must be in the usercfg directory\\n\");\n\t\t\treturn;\n\t\t}\n\t}\n\n\tf = fopen(fname, \"rt\");\n\tif (!f)\n\t{\n\t\tCmd_Printf(ctx, \"Couldn't exec \\\"%s\\\"\\n\", fname);\n\t\treturn;\n\t}\n\telse\n\t{\n\t\tCmd_Printf(ctx, \"Execing \\\"%s\\\"\\n\", fname);\n\t\twhile(fgets(line, sizeof(line)-1, f))\n\t\t{\n\t\t\tl = line;\n\t\t\twhile(*(unsigned char*)l <= ' ' && *l)\n\t\t\t\tl++;\n\t\t\tif (*l && l[0] != '/' && l[1] != '/')\n\t\t\t{\n\t\t\t\tCmd_ExecuteNow(ctx, l);\n\t\t\t}\n\t\t}\n\t\tfclose(f);\n\t}\n}\n\nvoid catbuffer(char *buffer, int bufsize, char *format, ...)\n{\n\tva_list argptr;\n\tunsigned int buflen;\n\n\tbuflen = strlen(buffer);\n\n\tva_start(argptr, format);\n\tvsnprintf(buffer + buflen, bufsize - buflen, format, argptr);\n\tva_end(argptr);\n}\n\nvoid Cmd_Say(cmdctxt_t *ctx)\n{\n\tint i;\n\tviewer_t *v;\n\tchar message[8192];\n\tmessage[0] = '\\0';\n\n\tfor (i = 1; i < Cmd_Argc(ctx); i++)\n\t\tcatbuffer(message, sizeof(message)-1, \"%s%s\", i==1?\"\":\" \", Cmd_Argv(ctx, i));\n\n\tif (ctx->qtv)\n\t{\n\t\tif (!SV_SayToUpstream(ctx->qtv, message))\n\t\t\tSV_SayToViewers(ctx->qtv, message);\n\t}\n\telse\n\t{\n\t\t//we don't have to remember the client proxies here... no streams = no active client proxies\n\t\tfor (v = ctx->cluster->viewers; v; v = v->next)\n\t\t{\n\t\t\tQW_PrintfToViewer(v, \"proxy: %s\\n\", message);\n\t\t}\n\t}\n\n\tCmd_Printf(ctx, \"proxy: %s\\n\", message);\n}\n\nvoid Cmd_Status(cmdctxt_t *ctx)\n{\n\tCmd_Printf(ctx, \"QTV Status:\\n\");\n\tCmd_Printf(ctx, \" %i sources%s\\n\", ctx->cluster->numservers, ctx->cluster->nouserconnects?\" (admin only)\":\" (user allowed)\");\n\tCmd_Printf(ctx, \" %i udp clients %s\\n\", ctx->cluster->numviewers, ctx->cluster->allownqclients?\" (qw+nq)\":\" (qw only)\");\n\tif (ctx->cluster->maxproxies)\n\t\tCmd_Printf(ctx, \" %i tcp clients (of %i)\\n\", ctx->cluster->numproxies, ctx->cluster->maxproxies);\n\telse\n\t\tCmd_Printf(ctx, \" %i tcp clients\\n\", ctx->cluster->numproxies);\n\tTURN_RelayStatus(ctx);\n\n\tCmd_Printf(ctx, \"Common Options:\\n\");\n\tCmd_Printf(ctx, \" Hostname %s\\n\", ctx->cluster->hostname);\n\n\tif (ctx->cluster->chokeonnotupdated)\n\t\tCmd_Printf(ctx, \" Choke\\n\");\n\tif (ctx->cluster->lateforward)\n\t\tCmd_Printf(ctx, \" Late forwarding (delayed streams)\\n\");\n\tif (!ctx->cluster->notalking)\n\t\tCmd_Printf(ctx, \" Talking allowed\\n\");\n\tif (ctx->cluster->nobsp)\n\t\tCmd_Printf(ctx, \" No BSP loading\\n\");\n\tif (ctx->cluster->tcpsocket[SG_UNIX] != INVALID_SOCKET)\n\t\tCmd_Printf(ctx, \" unix socket open\\n\");\n\tif (ctx->cluster->tcpsocket[SG_IPV4] != INVALID_SOCKET || ctx->cluster->tcpsocket[SG_IPV6] != INVALID_SOCKET)\n\t\tCmd_Printf(ctx, \" tcp port %i\\n\", ctx->cluster->tcplistenportnum);\n\tif (ctx->cluster->qwdsocket[SG_IPV4] != INVALID_SOCKET || ctx->cluster->qwdsocket[SG_IPV6] != INVALID_SOCKET)\n\t\tCmd_Printf(ctx, \" udp port %i\\n\", ctx->cluster->qwlistenportnum);\n\tCmd_Printf(ctx, \"\\n\");\n\n\n\tif (ctx->qtv)\n\t{\n\t\tCmd_Printf(ctx, \"Selected server: %s\\n\", ctx->qtv->server);\n\t\tif (ctx->qtv->sourcefile)\n\t\t\tCmd_Printf(ctx, \" Playing from file\\n\");\n\t\tif (ctx->qtv->sourcesock != INVALID_SOCKET)\n\t\t\tCmd_Printf(ctx, \" Connected\\n\");\n\t\tif (ctx->qtv->parsingqtvheader || ctx->qtv->parsingconnectiondata)\n\t\t\tCmd_Printf(ctx, \" Waiting for gamestate\\n\");\n\t\tif (ctx->qtv->usequakeworldprotocols)\n\t\t{\n\t\t\tCmd_Printf(ctx, \" QuakeWorld protocols\\n\");\n\t\t\tif (ctx->qtv->controller)\n\t\t\t{\n\t\t\t\tCmd_Printf(ctx, \" Controlled by %s\\n\", ctx->qtv->controller->name);\n\t\t\t}\n\t\t}\n\t\telse if (ctx->qtv->sourcesock == INVALID_SOCKET && !ctx->qtv->sourcefile)\n\t\t\tCmd_Printf(ctx, \" Connection not established\\n\");\n\n\t\tif (*ctx->qtv->map.modellist[1].name)\n\t\t{\n\t\t\tCmd_Printf(ctx, \" Map name %s\\n\", ctx->qtv->map.modellist[1].name);\n\t\t}\n\t\tif (*ctx->qtv->connectpassword)\n\t\t\tCmd_Printf(ctx, \" Using a password\\n\");\n\n\t\tif (ctx->qtv->errored == ERR_DISABLED)\n\t\t\tCmd_Printf(ctx, \" Stream is disabled\\n\");\n\n\t\tif (ctx->qtv->autodisconnect == AD_WHENEMPTY)\n\t\t\tCmd_Printf(ctx, \" Stream is user created\\n\");\n\t\telse if (ctx->qtv->autodisconnect == AD_REVERSECONNECT)\n\t\t\tCmd_Printf(ctx, \" Stream is server created\\n\");\n\n/*\t\tif (ctx->qtv->tcpsocket != INVALID_SOCKET)\n\t\t{\n\t\t\tCmd_Printf(ctx, \" Listening for proxies (%i)\\n\", ctx->qtv->tcplistenportnum);\n\t\t}\n*/\n\n\t\tif (ctx->qtv->map.bsp)\n\t\t{\n\t\t\tCmd_Printf(ctx, \" BSP (%s) is loaded\\n\", ctx->qtv->map.mapname);\n\t\t}\n\t}\n\n}\n\nvoid Cmd_UserConnects(cmdctxt_t *ctx)\n{\n\tif (Cmd_Argc(ctx) < 2)\n\t{\n\t\tCmd_Printf(ctx, \"userconnects is set to %i\\n\", !ctx->cluster->nouserconnects);\n\t}\n\telse\n\t{\n\t\tctx->cluster->nouserconnects = !atoi(Cmd_Argv(ctx, 1));\n\t\tCmd_Printf(ctx, \"userconnects is now %i\\n\", !ctx->cluster->nouserconnects);\n\t}\n}\nvoid Cmd_Choke(cmdctxt_t *ctx)\n{\n\tif (Cmd_Argc(ctx) < 2)\n\t{\n\t\tif (ctx->cluster->chokeonnotupdated)\n\t\t\tCmd_Printf(ctx, \"proxy will not interpolate packets\\n\");\n\t\telse\n\t\t\tCmd_Printf(ctx, \"proxy will smooth action at the expense of extra packets\\n\");\n\t\treturn;\n\t}\n\tctx->cluster->chokeonnotupdated = !!atoi(Cmd_Argv(ctx, 1));\n\tCmd_Printf(ctx, \"choke-until-update set to %i\\n\", ctx->cluster->chokeonnotupdated);\n}\nvoid Cmd_Late(cmdctxt_t *ctx)\n{\n\tif (Cmd_Argc(ctx) < 2)\n\t{\n\t\tif (ctx->cluster->lateforward)\n\t\t\tCmd_Printf(ctx, \"forwarded streams will be artificially delayed\\n\");\n\t\telse\n\t\t\tCmd_Printf(ctx, \"forwarded streams are forwarded immediatly\\n\");\n\t\treturn;\n\t}\n\tctx->cluster->lateforward = !!atoi(Cmd_Argv(ctx, 1));\n\tCmd_Printf(ctx, \"late forwarding set\\n\");\n}\nvoid Cmd_ReverseAllowed(cmdctxt_t *ctx)\n{\n\tif (Cmd_Argc(ctx) >= 2)\n\t\tctx->cluster->reverseallowed = !!atoi(Cmd_Argv(ctx, 1));\n\tCmd_Printf(ctx, \"reverse connections are %s\\n\", ctx->cluster->reverseallowed?\"enabled\":\"disabled\");\n}\n\nvoid Cmd_Talking(cmdctxt_t *ctx)\n{\n\tif (Cmd_Argc(ctx) < 2)\n\t{\n\t\tif (ctx->cluster->notalking)\n\t\t\tCmd_Printf(ctx, \"viewers may not talk\\n\");\n\t\telse\n\t\t\tCmd_Printf(ctx, \"viewers may talk freely\\n\");\n\t\treturn;\n\t}\n\tctx->cluster->notalking = !atoi(Cmd_Argv(ctx, 1));\n\tCmd_Printf(ctx, \"talking permissions set\\n\");\n}\nvoid Cmd_NoBSP(cmdctxt_t *ctx)\n{\n\tchar *val = Cmd_Argv(ctx, 1);\n\tif (!*val)\n\t{\n\t\tif (ctx->cluster->nobsp)\n\t\t\tCmd_Printf(ctx, \"no bsps will be loaded\\n\");\n\t\telse\n\t\t\tCmd_Printf(ctx, \"attempting to load bsp files\\n\");\n\t}\n\telse\n\t{\n\t\tctx->cluster->nobsp = !!atoi(val);\n\t\tCmd_Printf(ctx, \"nobsp will change at start of next map\\n\");\n\t}\n}\n\nvoid Cmd_MaxViewers(cmdctxt_t *ctx)\n{\n\tchar *val = Cmd_Argv(ctx, 1);\n\tif (!*val)\n\t{\n\t\tif (ctx->cluster->maxviewers)\n\t\t\tCmd_Printf(ctx, \"maxviewers is currently %i\\n\", ctx->cluster->maxviewers);\n\t\telse\n\t\t\tCmd_Printf(ctx, \"maxviewers is currently unlimited\\n\");\n\t}\n\telse\n\t{\n\t\tctx->cluster->maxviewers = atoi(val);\n\t\tCmd_Printf(ctx, \"maxviewers set\\n\");\n\t}\n}\nvoid Cmd_AllowNQ(cmdctxt_t *ctx)\n{\n\tchar *val = Cmd_Argv(ctx, 1);\n\tif (!*val)\n\t{\n\t\tCmd_Printf(ctx, \"allownq is currently %i\\n\", ctx->cluster->allownqclients);\n\t}\n\telse\n\t{\n\t\tctx->cluster->allownqclients = !!atoi(val);\n\t\tCmd_Printf(ctx, \"allownq set\\n\");\n\t}\n}\n\nvoid Cmd_InitialDelay(cmdctxt_t *ctx)\n{\n\tchar *val = Cmd_Argv(ctx, 1);\n\tif (!*val)\n\t{\n\t\tCmd_Printf(ctx, \"initialdelay is currently %g seconds\\n\", ctx->cluster->anticheattime/1000.f);\n\t}\n\telse\n\t{\n\t\tctx->cluster->anticheattime = atof(val)*1000;\n\t\tif (ctx->cluster->anticheattime < 1)\n\t\t\tctx->cluster->anticheattime = 1;\n\t\tCmd_Printf(ctx, \"initialdelay set\\n\");\n\t}\n}\n\nvoid Cmd_SlowDelay(cmdctxt_t *ctx)\n{\n\tchar *val = Cmd_Argv(ctx, 1);\n\tif (!*val)\n\t{\n\t\tCmd_Printf(ctx, \"slowdelay is currently %g seconds\\n\", ctx->cluster->tooslowdelay/1000.f);\n\t}\n\telse\n\t{\n\t\tctx->cluster->tooslowdelay = atof(val)*1000;\n\t\tif (ctx->cluster->tooslowdelay < 1)\n\t\t\tctx->cluster->tooslowdelay = 1;\n\t\tCmd_Printf(ctx, \"slowdelay set\\n\");\n\t}\n}\n\nvoid Cmd_MaxProxies(cmdctxt_t *ctx)\n{\n\tchar *val = Cmd_Argv(ctx, 1);\n\tif (!*val)\n\t{\n\t\tif (ctx->cluster->maxproxies)\n\t\t\tCmd_Printf(ctx, \"maxproxies is currently %i\\n\", ctx->cluster->maxproxies);\n\t\telse\n\t\t\tCmd_Printf(ctx, \"maxproxies is currently unlimited\\n\");\n\t}\n\telse\n\t{\n\t\tctx->cluster->maxproxies = atoi(val);\n\t\tCmd_Printf(ctx, \"maxproxies set\\n\");\n\t}\n}\n\n\nvoid Cmd_Ping(cmdctxt_t *ctx)\n{\n\tnetadr_t addr;\n\tchar *val = Cmd_Argv(ctx, 1);\n\tif (NET_StringToAddr(val, &addr, 27500))\n\t{\n\t\tNET_SendPacket (ctx->cluster, NET_ChooseSocket(ctx->cluster->qwdsocket, &addr, addr), 1, \"k\", addr);\n\t\tCmd_Printf(ctx, \"pinged\\n\");\n\t}\n\tCmd_Printf(ctx, \"couldn't resolve\\n\");\n}\n\nvoid Cmd_Help(cmdctxt_t *ctx)\n{\n\tCmd_Printf(ctx, HELPSTRING);\n}\n\nvoid Cmd_Echo(cmdctxt_t *ctx)\n{\n\tCmd_Printf(ctx, \"%s\", Cmd_Argv(ctx, 1));\n}\n\nvoid Cmd_Quit(cmdctxt_t *ctx)\n{\n\tif (!Cmd_IsLocal(ctx))\n\t\tCmd_Printf(ctx, \"Remote shutdown refused.\\n\");\n\tctx->cluster->wanttoexit = true;\n\tCmd_Printf(ctx, \"Shutting down.\\n\");\n}\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nvoid Cmd_Streams(cmdctxt_t *ctx)\n{\n\tsv_t *qtv;\n\tchar *status;\n\tCmd_Printf(ctx, \"Streams:\\n\");\n\n\tfor (qtv = ctx->cluster->servers; qtv; qtv = qtv->next)\n\t{\n\t\tswitch (qtv->errored)\n\t\t{\n\t\tcase ERR_NONE:\n\t\t\tif (qtv->controller)\n\t\t\t\tstatus = \" (player controlled)\";\n\t\t\telse if (qtv->autodisconnect == AD_STATUSPOLL)\n\t\t\t\tstatus = \" (polling)\";\n\t\t\telse if (qtv->parsingconnectiondata)\n\t\t\t\tstatus = \" (connecting)\";\n\t\t\telse\n\t\t\t\tstatus = \"\";\n\t\t\tbreak;\n\t\tcase ERR_PAUSED:\n\t\t\tstatus = \" (paused)\";\n\t\t\tbreak;\n\t\tcase ERR_DISABLED:\n\t\t\tstatus = \" (disabled)\";\n\t\t\tbreak;\n\t\tcase ERR_DROP:\t//a user should never normally see this, but there is a chance\n\t\t\tstatus = \" (dropping)\";\n\t\t\tbreak;\n\t\tcase ERR_RECONNECT:\t//again, rare\n\t\t\tstatus = \" (reconnecting)\";\n\t\t\tbreak;\n\t\tdefault:\t//some other kind of error, transitioning\n\t\t\tstatus = \" (errored)\";\n\t\t\tbreak;\n\t\t}\n\t\tCmd_Printf(ctx, \"%i: %s%s\\n\", qtv->streamid, qtv->server, status);\n\n\t\tif (qtv->upstreamacceptschat)\n\t\t\tCmd_Printf(ctx, \"  (dbg) can chat!\\n\");\n\t}\n}\n\n\n\n\nvoid Cmd_DemoSpeed(cmdctxt_t *ctx)\n{\n\tchar *val = Cmd_Argv(ctx, 1);\n\tif (*val)\n\t{\n\t\tctx->qtv->parsespeed = atof(val)*1000;\n\t\tCmd_Printf(ctx, \"Setting demo speed to %f\\n\", ctx->qtv->parsespeed/1000.0f);\n\t}\n\telse\n\t\tCmd_Printf(ctx, \"Playing demo at %f speed\\n\", ctx->qtv->parsespeed/1000.0f);\n}\n\nvoid Cmd_Disconnect(cmdctxt_t *ctx)\n{\n\tQTV_ShutdownStream(ctx->qtv);\n\tCmd_Printf(ctx, \"Disconnected\\n\");\n}\n\nvoid Cmd_Halt(cmdctxt_t *ctx)\n{\n\tif (ctx->qtv->errored == ERR_DISABLED || ctx->qtv->errored == ERR_PERMANENT)\n\t{\n\t\tCmd_Printf(ctx, \"Stream is already halted\\n\");\n\t}\n\telse\n\t{\n\t\tctx->qtv->errored = ERR_PERMANENT;\n\t\tCmd_Printf(ctx, \"Stream will disconnect\\n\");\n\t}\n}\n\nvoid Cmd_Pause(cmdctxt_t *ctx)\n{\n\tif (ctx->qtv->errored == ERR_PAUSED)\n\t{\n\t\tctx->qtv->errored = ERR_NONE;\n\t\tCmd_Printf(ctx, \"Stream unpaused.\\n\");\n\t}\n\telse if (ctx->qtv->errored == ERR_NONE)\n\t{\n\t\tif (ctx->qtv->sourcetype == SRC_DEMO)\n\t\t{\n\t\t\tctx->qtv->errored = ERR_PAUSED;\n\t\t\tCmd_Printf(ctx, \"Stream paused.\\n\");\n\t\t}\n\t\telse\n\t\t\tCmd_Printf(ctx, \"Sorry, only demos may be paused.\\n\");\n\t}\n}\n\nvoid Cmd_Resume(cmdctxt_t *ctx)\n{\n\tif (ctx->qtv->errored == ERR_PAUSED)\n\t{\n\t\tctx->qtv->errored = ERR_NONE;\n\t\tCmd_Printf(ctx, \"Stream unpaused.\\n\");\n\t}\n\telse\n\t{\n\t\tif (ctx->qtv->errored == ERR_NONE)\n\t\t\tCmd_Printf(ctx, \"Stream is already functional\\n\");\n\n\t\tctx->qtv->errored = ERR_RECONNECT;\n\t\tCmd_Printf(ctx, \"Stream will attempt to reconnect\\n\");\n\t}\n}\n\nvoid Cmd_Record(cmdctxt_t *ctx)\n{\n\tchar *fname = Cmd_Argv(ctx, 1);\n\tif (!*fname)\n\t\tCmd_Printf(ctx, \"record requires a filename on the proxy's machine\\n\");\n\n\tif (!Cmd_IsLocal(ctx))\n\t{\n\t\tif (*fname == '\\\\' || *fname == '/' || strstr(fname, \"..\") || fname[1] == ':')\n\t\t{\n\t\t\tCmd_Printf(ctx, \"Absolute paths are prohibited.\\n\");\n\t\t\treturn;\n\t\t}\n\t}\n\n\tif (Net_FileProxy(ctx->qtv, fname))\n\t\tCmd_Printf(ctx, \"Recording to disk\\n\");\n\telse\n\t\tCmd_Printf(ctx, \"Failed to open file\\n\");\n}\nvoid Cmd_Stop(cmdctxt_t *ctx)\n{\n\tif (Net_StopFileProxy(ctx->qtv))\n\t\tCmd_Printf(ctx, \"stopped\\n\");\n\telse\n\t\tCmd_Printf(ctx, \"not recording to disk\\n\");\n}\n\nvoid Cmd_Reconnect(cmdctxt_t *ctx)\n{\n\tif (ctx->qtv->autodisconnect == AD_REVERSECONNECT)\n\t\tCmd_Printf(ctx, \"Stream is a reverse connection (command rejected)\\n\");\n//\telse if (ctx->qtv->autodisconnect == AD_STATUSPOLL && !ctx->qtv->numviewers && !ctx->qtv->proxies)\n//\t\tCmd_Printf(ctx, \"Not reconnecting to idle server\\n\");\n\telse if (QTV_ConnectStream(ctx->qtv, ctx->qtv->server))\n\t\tCmd_Printf(ctx, \"Reconnected\\n\");\n\telse\n\t\tCmd_Printf(ctx, \"Failed to reconnect (will keep trying)\\n\");\n}\n\nvoid Cmd_MVDPort(cmdctxt_t *ctx)\n{\n\tchar *val = Cmd_Argv(ctx, 1);\n\tint newp = atoi(val);\n\n\tif (!*val)\n\t{\n\t\tCmd_Printf(ctx, \"Listening for tcp connections on port %i\\n\", ctx->cluster->tcplistenportnum);\n\t\treturn;\n\t}\n\n\tif (!newp)\n\t{\n\t\tif (ctx->cluster->tcpsocket[0] != INVALID_SOCKET && ctx->cluster->tcpsocket[1] != INVALID_SOCKET)\n\t\t\tCmd_Printf(ctx, \"Already closed\\n\");\n\t}\n\n\tNet_TCPListen(ctx->cluster, newp, true);\n\tNet_TCPListen(ctx->cluster, newp, false);\n\tctx->cluster->tcplistenportnum = newp;\n}\n\nvoid Cmd_DemoList(cmdctxt_t *ctx)\n{\n\tint i;\n\tint count;\n\tCluster_BuildAvailableDemoList(ctx->cluster);\n\n\tcount = ctx->cluster->availdemoscount;\n\tCmd_Printf(ctx, \"%i demos\\n\", count);\n\tfor (i = 0; i < count; i++)\n\t{\n\t\tCmd_Printf(ctx, \" %7i %s\\n\", ctx->cluster->availdemos[i].size, ctx->cluster->availdemos[i].name);\n\t}\n}\n\nvoid Cmd_BaseDir(cmdctxt_t *ctx)\n{\n\tchar *val;\n\tval = Cmd_Argv(ctx, 1);\n\tif (!Cmd_IsLocal(ctx))\n\t\tCmd_Printf(ctx, \"Sorry, you may not use this command remotely\\n\");\n\n\tif (*val)\n\t\tchdir(val);\n\telse\n\t{\n\t\tchar buffer[256];\n\t\tval = getcwd(buffer, sizeof(buffer));\n\t\tif (val)\n\t\t\tCmd_Printf(ctx, \"basedir is: %s\\n\", val);\n\t\telse\n\t\t\tCmd_Printf(ctx, \"system error getting basedir\\n\");\n\t}\n}\n\nvoid Cmd_DemoDir(cmdctxt_t *ctx)\n{\n\tchar *val;\n\tval = Cmd_Argv(ctx, 1);\n\n\tif (*val)\n\t{\n\t\tif (!Cmd_IsLocal(ctx))\n\t\t{\n\t\t\tCmd_Printf(ctx, \"Sorry, but I don't trust this code that well!\\n\");\n\t\t\treturn;\n\t\t}\n\t\twhile (*val > 0 &&*val <= ' ')\n\t\t\tval++;\n\n\t\tif (strchr(val, '.') || strchr(val, ':') || *val == '/')\n\t\t\tCmd_Printf(ctx, \"Rejecting path\\n\");\n\t\telse\n\t\t{\n\t\t\tstrlcpy(ctx->cluster->demodir, val, sizeof(ctx->cluster->demodir));\n\t\t\tCmd_Printf(ctx, \"Changed demo dir to \\\"%s\\\"\\n\", ctx->cluster->demodir);\n\t\t}\n\t}\n\telse\n\t{\n\t\tCmd_Printf(ctx, \"Current demo directory is \\\"%s\\\"\\n\", ctx->cluster->demodir);\n\t}\n}\n\nvoid Cmd_DLDir(cmdctxt_t *ctx)\n{\n\tchar *val;\n\tval = Cmd_Argv(ctx, 1);\n\n\tif (!Cmd_IsLocal(ctx))\n\t{\n\t\tCmd_Printf(ctx, \"dldir may not be used remotely\\n\");\n\t\treturn;\n\t}\n\n\tif (*val)\n\t{\n\t\twhile (*val > 0 &&*val <= ' ')\n\t\t\tval++;\n\n//\t\tif (strchr(val, '.') || strchr(val, ':') || *val == '/')\n//\t\t\tCmd_Printf(ctx, \"Rejecting path\\n\");\n//\t\telse\n\t\t{\n\t\t\tstrlcpy(ctx->cluster->downloaddir, val, sizeof(ctx->cluster->downloaddir));\n\t\t\tCmd_Printf(ctx, \"Changed download dir to \\\"%s\\\"\\n\", ctx->cluster->downloaddir);\n\t\t}\n\t}\n\telse\n\t{\n\t\tCmd_Printf(ctx, \"Current download directory is \\\"%s\\\"\\n\", ctx->cluster->downloaddir);\n\t}\n}\n\nvoid Cmd_PluginDataSource(cmdctxt_t *ctx)\n{\n\tchar *val;\n\tval = Cmd_Argv(ctx, 1);\n\n\tif (!Cmd_IsLocal(ctx))\n\t{\n\t\tCmd_Printf(ctx, \"plugindatasource may not be used remotely\\n\");\n\t\treturn;\n\t}\n\n\tif (*val)\n\t{\n\t\twhile (*val > 0 &&*val <= ' ')\n\t\t\tval++;\n\n\t\tstrlcpy(ctx->cluster->plugindatasource, val, sizeof(ctx->cluster->plugindatasource));\n\t\tCmd_Printf(ctx, \"Changed plugindatasource to \\\"%s\\\"\\n\", ctx->cluster->plugindatasource);\n\t}\n\telse\n\t{\n\t\tCmd_Printf(ctx, \"Current plugindatasource is \\\"%s\\\"\\n\", ctx->cluster->plugindatasource);\n\t}\n}\n\nvoid Cmd_MapSource(cmdctxt_t *ctx)\n{\n\tchar *val;\n\tval = Cmd_Argv(ctx, 1);\n\n\tif (!Cmd_IsLocal(ctx))\n\t{\n\t\tCmd_Printf(ctx, \"mapsource may not be used remotely\\n\");\n\t\treturn;\n\t}\n\n\tif (*val)\n\t{\n\t\twhile (*val > 0 &&*val <= ' ')\n\t\t\tval++;\n\n\t\tstrlcpy(ctx->cluster->mapsource, val, sizeof(ctx->cluster->mapsource));\n\t\tCmd_Printf(ctx, \"Changed mapsource url to \\\"%s\\\"\\n\", ctx->cluster->mapsource);\n\t}\n\telse\n\t{\n\t\tCmd_Printf(ctx, \"Current mapsource url is \\\"%s\\\"\\n\", ctx->cluster->mapsource);\n\t}\n}\n\nvoid Cmd_MuteStream(cmdctxt_t *ctx)\n{\n\tchar *val;\n\tval = Cmd_Argv(ctx, 1);\n\tif (*val)\n\t{\n\t\tctx->qtv->silentstream = atoi(val);\n\t\tCmd_Printf(ctx, \"Stream is now %smuted\\n\", ctx->qtv->silentstream?\"\":\"un\");\n\t}\n\telse\n\t\tCmd_Printf(ctx, \"Stream is currently %smuted\\n\", ctx->qtv->silentstream?\"\":\"un\");\n}\n\n#ifdef VIEWER\nvoid Cmd_Watch(cmdctxt_t *ctx)\n{\n\tif (!localcommand)\n\t{\n\t\tCmd_Printf(ctx, \"watch is not permitted remotly\\n\");\n\t}\n\n\tif (cluster->viewserver == qtv)\n\t{\n\t\tcluster->viewserver = NULL;\n\t\tCmd_Printf(ctx, \"Stopped watching\\n\");\n\t}\n\telse\n\t{\n\t\tcluster->viewserver = qtv;\n\t\tCmd_Printf(ctx, \"Watching\\n\");\n\t}\n}\n#endif\n\n#ifdef __linux__\n#include <fcntl.h>\nqboolean Sys_RandomBytes(unsigned char *out, int len)\n{\n\tqboolean res;\n\tint fd = open(\"/dev/urandom\", 0);\n\tres = (read(fd, out, len) == len);\n\tclose(fd);\n\treturn res;\n}\n#else\nqboolean Sys_RandomBytes(unsigned char *out, int len)\n{\n\treturn false;\n}\n#endif\n\nstatic void Cmd_Turn(cmdctxt_t *ctx)\n{\n\tif (Cmd_Argc(ctx) < 2)\n\t{\n\t\tif (ctx->cluster->turnenabled && ctx->cluster->turn_minport)\n\t\t\tCmd_Printf(ctx, \"turn is enabled, using ports %i-%i\\n\", ctx->cluster->turn_minport, ctx->cluster->turn_maxport);\n\t\telse if (ctx->cluster->turnenabled)\n\t\t\tCmd_Printf(ctx, \"turn is enabled, using ephemerial ports\\n\");\n\t\telse\n\t\t\tCmd_Printf(ctx, \"turn is disabled\\n\");\n\t\treturn;\n\t}\n\tif (!Cmd_IsLocal(ctx))\n\t{\n\t\tCmd_Printf(ctx, \"turn support may not be configured remotely\\n\");\n\t\treturn;\n\t}\n\n\tif (Cmd_Argc(ctx) >= 3)\n\t{\t//two args - assume a two number range, so turn it on.\n\t\tctx->cluster->turnenabled = true;\n\t\tctx->cluster->turn_minport = atoi(Cmd_Argv(ctx, 1));\n\t\tctx->cluster->turn_maxport = atoi(Cmd_Argv(ctx, 2));\n\t}\n\telse if ( atoi(Cmd_Argv(ctx, 1)))\t//a boolean. turn it back on..\n\t\tctx->cluster->turnenabled = true;\t//switch it back on with whatever port range it previously had. probably 0-0 for ephemerial. probably bad for the relay's firewalls...\n\telse\n\t\tctx->cluster->turnenabled = false;\t//and off.\n\n\tif (!*ctx->cluster->chalkey && ctx->cluster->turnenabled)\n\t{\n\t\tunsigned char chalkey[12];\n\t\tif (!Sys_RandomBytes(chalkey, sizeof(chalkey)) ||\n\t\t\t!Sys_RandomBytes(ctx->cluster->turnkey, sizeof(ctx->cluster->turnkey)))\n\t\t{\n\t\t\tCmd_Printf(ctx, \"no random generator\\n\");\n\t\t\tctx->cluster->turnenabled = false;\n\t\t\treturn;\n\t\t}\n\t\ttobase64(ctx->cluster->chalkey,sizeof(ctx->cluster->chalkey), chalkey, sizeof(chalkey));\n\t}\n\n\tif (ctx->cluster->turnenabled && ctx->cluster->turn_minport)\n\t\tCmd_Printf(ctx, \"turn keys updated, using ports %i-%i\\n\", ctx->cluster->turn_minport, ctx->cluster->turn_maxport);\n\telse if (ctx->cluster->turnenabled)\n\t\tCmd_Printf(ctx, \"turn keys updated, using ephemerial ports\\n\");\n\telse\n\t\tCmd_Printf(ctx, \"turn disabled\\n\");\n}\nstatic void Cmd_Relay(cmdctxt_t *ctx)\n{\n\tif (Cmd_Argc(ctx) >= 2)\n\t{\n\t\tif (Cmd_IsLocal(ctx))\n\t\t{\n\t\t\tCmd_Printf(ctx, \"relay support may not be configured remotely\\n\");\n\t\t\treturn;\n\t\t}\n\t\tswitch(atoi(Cmd_Argv(ctx, 1)))\n\t\t{\n\t\tcase 0:\n\t\t\tctx->cluster->relayenabled = ctx->cluster->pingtreeenabled = false;\n\t\t\tCmd_Printf(ctx, \"turn disabled\\n\");\n\t\t\tbreak;\n\t\tcase 1:\n\t\t\tctx->cluster->relayenabled = ctx->cluster->pingtreeenabled = true;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tctx->cluster->relayenabled = true;\n\t\t\tctx->cluster->pingtreeenabled = false;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tif (ctx->cluster->relayenabled && ctx->cluster->pingtreeenabled)\n\t\tCmd_Printf(ctx, \"relay is enabled (with pinging)\\n\");\n\telse if (ctx->cluster->relayenabled)\n\t\tCmd_Printf(ctx, \"relay is enabled, WITHOUT pinging\\n\");\n\telse\n\t\tCmd_Printf(ctx, \"relay is disabled\\n\");\n}\nstatic void Cmd_ProtocolName(cmdctxt_t *ctx)\n{\n\tfree(ctx->cluster->protocolname);\n\tctx->cluster->protocolname = strdup(Cmd_Argv(ctx, 1));\n\tctx->cluster->protocolver = atoi(Cmd_Argv(ctx, 2));\n}\n\ntypedef struct rconcommands_s {\n\tchar *name;\n\tqboolean serverspecific;\t//works within a qtv context\n\tqboolean clusterspecific;\t//works without a qtv context (ignores context)\n\tconsolecommand_t func;\n\tchar *description;\n} rconcommands_t;\n\nextern const rconcommands_t rconcommands[];\n\nvoid Cmd_Commands(cmdctxt_t *ctx)\n{\n\tconst rconcommands_t *cmd;\n\tconsolecommand_t lastfunc = NULL;\n\n\tCmd_Printf(ctx, \"Say Commands:\\n\");\n\tfor (cmd = rconcommands; cmd->name; cmd++)\n\t{\n\t\tif (cmd->func == lastfunc)\n\t\t\tcontinue;\t//no spamming alternative command names\n\n\t\tCmd_Printf(ctx, \"%s: %s\\n\", cmd->name, cmd->description?cmd->description:\"no description available\");\n\t\tlastfunc = cmd->func;\n\t}\n}\n\nconst rconcommands_t rconcommands[] =\n{\n\t{\"exec\",\t\t1, 1, Cmd_Exec,\t\t\"executes a config file\"},\n\t{\"status\",\t\t1, 1, Cmd_Status,\t\"prints proxy/stream status\" },\n\t{\"say\",\t\t\t1, 1, Cmd_Say,\t\t\"says to a stream\"},\n\n\t{\"help\",\t\t0, 1, Cmd_Help,\t\t\"shows the brief intro help text\"},\n\t{\"commands\",\t\t0, 1, Cmd_Commands,\t\"prints the list of commands\"},\n\t{\"apropos\",\t\t0, 1, Cmd_Commands,\t\"prints all commands\"},\n\t{\"hostname\",\t\t0, 1, Cmd_Hostname,\t\"changes the hostname seen in server browsers\"},\n\t{\"master\",\t\t0, 1, Cmd_Master,\t\"specifies which master server to use\"},\n\t{\"udpport\",\t\t0, 1, Cmd_UDPPort,\t\"specifies to listen on a provided udp port for regular qw clients\"},\n\t {\"port\",\t\t0, 1, Cmd_UDPPort},\n\t{\"adminpassword\",\t0, 1, Cmd_AdminPassword,\"specifies the password for qtv administrators\"},\n\t {\"rconpassword\",\t0, 1, Cmd_AdminPassword},\n\t{\"qtvlist\",\t\t0, 1, Cmd_QTVList,\t\"queries a seperate proxy for a list of available streams\"},\n\t{\"qtvdemolist\",\t\t0, 1, Cmd_QTVDemoList,\t\"queries a seperate proxy for a list of available demos\"},\n\t{\"qtv\",\t\t\t0, 1, Cmd_QTVConnect,\t\"adds a new tcp/qtv stream\"},\n\t {\"addserver\",\t\t0, 1, Cmd_QTVConnect},\n\t {\"connect\",\t\t0, 1, Cmd_QTVConnect},\n\t{\"qw\",\t\t\t0, 1, Cmd_QWConnect,\t\"adds a new udp/qw stream\"},\n\t {\"observe\",\t\t0, 1, Cmd_QWConnect},\n\t{\"demos\",\t\t0, 1, Cmd_DemoList,\t\"shows the list of demos available on this proxy\"},\n\t{\"demo\",\t\t0, 1, Cmd_MVDConnect,\t\"adds a demo as a new stream\"},\n\t {\"playdemo\",\t\t0, 1, Cmd_MVDConnect},\n\t{\"dir\",\t\t\t0, 1, Cmd_DirMVDConnect, \"adds a directory of demos as a new stream\"},\n\t {\"playdir\",\t\t0, 1, Cmd_DirMVDConnect},\n\t{\"choke\",\t\t0, 1, Cmd_Choke,\t\"chokes packets to the data rate in the stream, disables proxy-side interpolation\"},\n\t{\"late\",\t\t0, 1, Cmd_Late,\t\t\"enforces a time delay on packets sent through this proxy\"},\n\t{\"talking\",\t\t0, 1, Cmd_Talking,\t\"permits viewers to talk to each other\"},\n\t{\"nobsp\",\t\t0, 1, Cmd_NoBSP,\t\"disables loading of bsp files\"},\n\t{\"userconnects\",\t0, 1, Cmd_UserConnects,\t\"prevents users from creating thier own streams\"},\n\t{\"maxviewers\",\t\t0, 1, Cmd_MaxViewers,\t\"sets a limit on udp/qw client connections\"},\n\t{\"maxproxies\",\t\t0, 1, Cmd_MaxProxies,\t\"sets a limit on tcp/qtv client connections\"},\n\t{\"demodir\",\t\t0, 1, Cmd_DemoDir,\t\"specifies where to get the demo list from\"},\n\t{\"basedir\",\t\t0, 1, Cmd_BaseDir,\t\"specifies where to get any files required by the game. this is prefixed to the server-specified game dir.\"},\n\t{\"ping\",\t\t0, 1, Cmd_Ping,\t\t\"sends a udp ping to a qtv proxy or server\"},\n\t{\"reconnect\",\t\t0, 1, Cmd_Reconnect,\t\"forces a stream to reconnect to its server (restarts demos)\"},\n\t{\"echo\",\t\t0, 1, Cmd_Echo,\t\t\"a useless command that echos a string\"},\n\t{\"quit\",\t\t0, 1, Cmd_Quit,\t\t\"closes the qtv\"},\n\t{\"exit\",\t\t0, 1, Cmd_Quit},\n\t{\"streams\",\t\t0, 1, Cmd_Streams,\t\"shows a list of active streams\"},\n\t{\"allownq\",\t\t0, 1, Cmd_AllowNQ,\t\"permits nq clients to connect. This can be disabled as this code is less tested than the rest\"},\n\t{\"initialdelay\",0, 1, Cmd_InitialDelay, \"Specifies the duration for which new connections will be buffered. Large values prevents players from spectating their enemies as a cheap wallhack.\"},\n\t{\"slowdelay\",\t0, 1, Cmd_SlowDelay,\t\"If a server is not sending enough data, the proxy will delay parsing for this long.\"},\n\n\t{\"turn\",\t\t0, 1, Cmd_Turn,\t\t\t\"Controls whether we accept turn requests.\"},\n\t{\"relay\",\t\t0, 1, Cmd_Relay,\t\t\"Controls whether we accept qwfwd-style relay requests.\"},\n\t {\"qwfwd\",\t\t0, 1, Cmd_Relay},\n\t{\"protocolname\",0, 1, Cmd_ProtocolName,\t\"Protocol Name:Version used to register with master.\"},\n\n\n\t{\"halt\",\t\t1, 0, Cmd_Halt,\t\t\"disables a stream, preventing it from reconnecting until someone tries watching it anew. Boots current spectators\"},\n\t{\"disable\",\t\t1, 0, Cmd_Halt},\n\t{\"pause\",\t\t1, 0, Cmd_Pause,\t\t\"Pauses a demo stream.\"},\n\t{\"resume\",\t\t1, 0, Cmd_Resume,\t\"reactivates a stream, allowing it to reconnect\"},\n\t{\"enable\",\t\t1, 0, Cmd_Resume},\n\t{\"mute\",\t\t1, 0, Cmd_MuteStream,\t\"hides prints that come from the game server\"},\n\t{\"mutestream\",\t\t1, 0, Cmd_MuteStream},\n\t{\"disconnect\",\t\t1, 0, Cmd_Disconnect,\t\"fully closes a stream\"},\n\t{\"record\",\t\t1, 0, Cmd_Record,\t\"records a stream to a demo\"},\n\t{\"stop\",\t\t1, 0, Cmd_Stop,\t\t\"stops recording of a demo\"},\n\t{\"demospeed\",\t\t1, 0, Cmd_DemoSpeed,\t\"changes the rate the demo is played at\"},\n\t{\"tcpport\",\t\t0, 1, Cmd_MVDPort,\t\"specifies which port to listen on for tcp/qtv connections\"},\n\t {\"mvdport\",\t\t0, 1, Cmd_MVDPort},\n\n\t{\"dldir\",\t\t0, 1, Cmd_DLDir,\t\"specifies the path to download stuff from (http://server/file/ maps to this native path)\"},\n\t{\"plugindatasource\",0,1,Cmd_PluginDataSource, \"Specifies the dataDownload property for plugins in the web server\"},\n\t{\"mapsource\",\t0, 1, Cmd_MapSource,\"Public URL for where to download missing maps from\"},\n\n#ifdef VIEWER\n\t{\"watch\",\t\t1, 0, Cmd_Watch,\t\"specifies to watch that stream in the built-in viewer\"},\n#endif\n\n\t{NULL}\n};\n\nvoid Cmd_ExecuteNow(cmdctxt_t *ctx, char *command)\n{\n#define TOKENIZE_PUNCTUATION \"\"\n\n\tint i;\n\tchar arg[MAX_ARGS][ARG_LEN];\n\tchar *sid;\n\tchar *cmdname;\n\n\tfor (sid = command; *sid; sid++)\n\t{\n\t\tif (*sid == ':')\n\t\t\tbreak;\n\t\tif (*sid < '0' || *sid > '9')\n\t\t\tbreak;\n\t}\n\tif (*sid == ':')\n\t{\n\t\ti = atoi(command);\n\t\tcommand = sid+1;\n\n\t\tctx->streamid = i;\n\n\t\tfor (ctx->qtv = ctx->cluster->servers; ctx->qtv; ctx->qtv = ctx->qtv->next)\n\t\t\tif (ctx->qtv->streamid == i)\n\t\t\t\tbreak;\n\t}\n\telse\n\t\tctx->streamid = 0;\n\n\tctx->argc = 0;\n\tfor (i = 0; i < MAX_ARGS; i++)\n\t{\n\t\tcommand = COM_ParseToken(command, arg[i], ARG_LEN, TOKENIZE_PUNCTUATION);\n\t\tctx->arg[i] = arg[i];\n\t\tif (command)\n\t\t\tctx->argc++;\n\t}\n\n\tcmdname = Cmd_Argv(ctx, 0);\n\n\t//if there's only one stream, set that as the selected stream\n\tif (!ctx->qtv && ctx->cluster->numservers==1)\n\t\tctx->qtv = ctx->cluster->servers;\n\n\tif (ctx->qtv)\n\t{\t//if there is a specific connection targetted\n\n\t\tfor (i = 0; rconcommands[i].name; i++)\n\t\t{\n\t\t\tif (rconcommands[i].serverspecific)\n\t\t\t\tif (!strcmp(rconcommands[i].name, cmdname))\n\t\t\t\t{\n\t\t\t\t\trconcommands[i].func(ctx);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t}\n\t}\n\n\tfor (i = 0; rconcommands[i].name; i++)\n\t{\n\t\tif (!strcmp(rconcommands[i].name, cmdname))\n\t\t{\n\t\t\tif (rconcommands[i].clusterspecific)\n\t\t\t{\n\t\t\t\trconcommands[i].func(ctx);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\telse if (rconcommands[i].serverspecific)\n\t\t\t{\n\t\t\t\tCmd_Printf(ctx, \"Command \\\"%s\\\" requires a targeted server.\\n\", cmdname);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n\n\n\tCmd_Printf(ctx, \"Command \\\"%s\\\" not recognised.\\n\", cmdname);\n}\n\nvoid Rcon_PrintToBuffer(cmdctxt_t *ctx, char *msg)\n{\n\tif (ctx->printcookiesize < 1)\n\t\treturn;\n\twhile (ctx->printcookiesize>2 && *msg)\n\t{\n\t\tctx->printcookiesize--;\n\t\t*(char*)ctx->printcookie = *msg++;\n\t\tctx->printcookie = ((char*)ctx->printcookie)+1;\n\t}\n\n\tctx->printcookiesize--;\n\t*(char*)ctx->printcookie = 0;\n}\n\nchar *Rcon_Command(cluster_t *cluster, sv_t *source, char *command, char *resultbuffer, int resultbuffersize, int islocalcommand)\n{\n\tcmdctxt_t ctx;\n\tctx.cluster = cluster;\n\tctx.qtv = source;\n\tctx.argc = 0;\n\tctx.printfunc = Rcon_PrintToBuffer;\n\tctx.printcookie = resultbuffer;\n\tctx.printcookiesize = resultbuffersize;\n\tctx.localcommand = islocalcommand;\n\t*(char*)ctx.printcookie = 0;\n\tCmd_ExecuteNow(&ctx, command);\n\n\treturn resultbuffer;\n}\n"
  },
  {
    "path": "fteqtv/relay.c",
    "content": "/*\nCopyright (C) 2024 'Spoike'.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\nSee the included (GNU.txt) GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n*/\n\n/*\nwe have two types of relay here.\n1) TURN - blind(ish) forwarding.\n\tstandard protocol for use as ICE/WebRTC relays.\n\tsee TURN_IS_NOT_BLIND\n2) qwfwd - compatible with qqshka's fork of the original qwfwd.\n\trelay incercepts `prx` userinfo in the connect requests to forward to the real peer.\n\tunlike qqshka's version we relay the next hop's extension handshakes instead of doing them on the proxy.\n\twe can also relay 'dtlsconnect' requests.\n\tfor use with `connectbr` and compat with things that expect it.\n*/\n\n#include \"qtv.h\"\n#include <string.h>\n#include <time.h>\n#include <zlib.h>\n\n#include \"bsd_string.h\"\n\n#define QWFWDTIMEOUT\t(30*1000)\t//how long the relay will stay open for when doing qw-specific forwarding.\n#define TURN_IS_NOT_BLIND\t\t\t//filters what's allowed through - we must have seen a stun binding request/response from the peer before we allow non-binding traffic through. This should help protect the owner's lan a bit.\n#if defined(_DEBUG) && !defined(LIBQTV)\t//unsafe stuff I'm leaving enabled in debug builds cos debugging is painful when they're protected.\n\t#define ALLOWPRIVATE\t\t\t//allow relaying to private lan addresses, so I can test with hosting on localhost\n#endif\n\n#define QRY_SERVERLISTINTERVAL\t60*1000\t\t//interval between asking for server listings.\n#define QRY_REPINGINTERVAL\t\t60*1000\t\t//don't ever spam any server faster than this.\n#define QRY_PINGINTERVAL\t\t100\t\t\t//time between outgoing pings\n#define QRY_TIMEOUT\t\t\t\t20*60*1000\t//servers will be removed if not seen in any server listings for this long.\n\n\nstatic void Fwd_DoPing(cluster_t *cluster);\n#define countof(x) (sizeof(x)/sizeof((x)[0]))\n\nstruct turnclient_s\n{\t//this is the end that opened the connection\n\tstruct turnclient_s *next;\n\n\tSOCKET remotesock;\t//the udp socket we're sending to\n\tstruct {\n\t\tunsigned int timeout;\t//reset to 5 mins on refresh.\n\t\tnetadr_t remoteaddr;\n#ifdef TURN_IS_NOT_BLIND\n\t\tqboolean seenstun;\t\t//this is for ICE support. so we can impose a rule that the peer MUST have sent a stun packet before we allow the client to send any non-stun packets. this reduces the chance of being abused for attacks. can potentially still ddos with stun, but shouldn't be an amplification at least.\n#endif\n\t} remotes[8];\n\tint udpsockid;\t\t//-1 for private tcp\n\tSOCKET clientsock;\t//stoopid tcp clients...\n\tnetadr_t relayaddr;\t//yay for udp...\n\tnetadr_t clientaddr;\t//yay for udp...\n\tunsigned int timeout;\n\tchar *username;\t\t//username they authed with. may not switch users once established.\n\tchar *auth;\t\t\t//auth token generated by the master (recomputed).\n\n\tunsigned char key[64];\n\tunsigned int keysize;\n\n\tqboolean isfwd;\n};\n\ntypedef struct\n{\n\tunsigned short msgtype;\n\tunsigned short msglen;\n\tunsigned int magiccookie;\n\tunsigned int transactid[3];\n} stunhdr_t;\n//class\n#define STUN_REQUEST\t0x0000\n#define STUN_REPLY\t\t0x0100\n#define STUN_ERROR\t\t0x0110\n#define STUN_INDICATION\t0x0010\n//request\n#define STUN_BINDING\t0x0001\n#define STUN_ALLOCATE\t0x0003 //TURN\n#define STUN_REFRESH\t0x0004 //TURN\n#define STUN_SEND\t\t0x0006 //TURN\tsend indications contain data to forward\n#define STUN_DATA\t\t0x0007 //TURN\tdata indications contain reply data.\n#define STUN_CREATEPERM\t0x0008 //TURN\n#define STUN_CHANBIND\t0x0009 //TURN\n\n//misc stuff...\n#define STUN_MAGIC_COOKIE\t0x2112a442\n\n//attributes\n#define STUNATTR_MAPPED_ADDRESS\t\t\t0x0001\n//#define STUNATTR_RESPONSE_ADDRESS\t\t0x0002\n//#define STUNATTR_CHANGE_REQUEST\t\t0x0003\n//#define STUNATTR_SOURCE_ADDRESS\t\t0x0004\n//#define STUNATTR_CHANGED_ADDRESS\t\t0x0005\n#define STUNATTR_USERNAME\t\t\t\t0x0006\n//#define STUNATTR_PASSWORD\t\t\t\t0x0007\n#define STUNATTR_MSGINTEGRITIY_SHA1\t\t0x0008\n#define STUNATTR_ERROR_CODE\t\t\t\t0x0009\n//#define STUNATTR_UNKNOWN_ATTRIBUTES\t0x000a\n//#define STUNATTR_REFLECTED_FROM\t\t0x000b\n//#define STUNATTR_CHANNELNUMBER\t\t0x000c\t//TURN\n#define STUNATTR_LIFETIME\t\t\t\t0x000d\t//TURN\n//#define STUNATTR_\t\t\t\t\t\t0x000e\n//#define STUNATTR_\t\t\t\t\t\t0x000f\n//#define STUNATTR_\t\t\t\t\t\t0x0010\n//#define STUNATTR_\t\t\t\t\t\t0x0011\n#define STUNATTR_XOR_PEER_ADDRESS\t\t0x0012\t//TURN\n#define STUNATTR_DATA\t\t\t\t\t0x0013\t//TURN\n#define STUNATTR_REALM\t\t\t\t\t0x0014\t//TURN\n#define STUNATTR_NONCE\t\t\t\t\t0x0015\t//TURN\n#define STUNATTR_XOR_RELAYED_ADDRESS\t0x0016\t//TURN\n#define STUNATTR_REQUESTED_ADDRFAM\t\t0x0017\t//TURN\n//#define STUNATTR_EVEN_PORT\t\t\t0x0018\t//TURN\n#define STUNATTR_REQUESTED_TRANSPORT\t0x0019\t//TURN\n#define STUNATTR_DONT_FRAGMENT\t\t\t0x001a\t//TURN\n#define STUNATTR_MSGINTEGRITIY_SHA2_256\t0x001c\n#define STUNATTR_XOR_MAPPED_ADDRESS\t\t0x0020\n//#define STUNATTR_ICE_PRIORITY\t\t\t0x0024\t//ICE\n//#define STUNATTR_ICE_USE_CANDIDATE\t0x0025\t//ICE\n\n//0x8000 attributes are optional, and may be silently ignored without issue.\n#define STUNATTR_ADDITIONAL_ADDRFAM\t\t0x8000\t//TURN -- listen for ipv6 in addition to ipv4\n#define STUNATTR_SOFTWARE\t\t\t\t0x8022\t//TURN\n#define STUNATTR_FINGERPRINT\t\t\t0x8028\n//#define STUNATTR_ICE_CONTROLLED\t\t0x8029\t//ICE\n//#define STUNATTR_ICE_CONTROLLING\t\t0x802A\t//ICE\n\nstatic void TURN_Send(cluster_t *cluster, netmsg_t *m, netadr_t *clientadr)\n{\n\tnetadr_t sockaddr;\n\tSOCKET sock = NET_ChooseSocket(cluster->qwdsocket, &sockaddr, *clientadr);\n\tunsigned char *bytes = m->data;\n\t//update the size.\n\tbytes[2] = (m->cursize-20)>>8;\n\tbytes[3] = (m->cursize-20)&0xff;\n\t//send it.\n\tNET_SendPacket(cluster, sock, m->cursize, bytes, sockaddr);\n}\nstatic int TURN_FindPermission(cluster_t *cluster, struct turnclient_s *t, netadr_t *peeraddr)\n{\t//we only allow packets to/from authorised peers.\n\tint i;\n\tfor (i = 0; i < countof(t->remotes); i++)\n\t{\n\t\tif ((signed int)(t->remotes[i].timeout-cluster->curtime) < 0)\n\t\t\tcontinue;\t//look for a live one...\n\t\tif (Net_CompareAddress(&t->remotes[i].remoteaddr,peeraddr, 0,1))\n\t\t\treturn i;\n\t}\n\treturn -1;\n}\nstatic qboolean TURN_Permissable(netadr_t *adr)\n{\t//we only allow packets to/from authorised peers. we don't worry about inbound ports to give symetric-nat peers a chance.\n\tif (((struct sockaddr *)adr->sockaddr)->sa_family == AF_INET)\n\t{\n\t\tstruct sockaddr_in *a = (struct sockaddr_in *)&adr->sockaddr;\n\t\tunsigned int ip = a->sin_addr.s_addr;\n\t\tif ((ip&BigLong(0xffff0000)) == BigLong(0xA9FE0000))\t//169.254.x.x/16\n\t\t\treturn false;//link-local\n\t\telse if ((ip&BigLong(0xff000000)) == BigLong(0x0a000000))\t//10.x.x.x/8\n\t\t\treturn false;//private\n\t\telse if ((ip&BigLong(0xff000000)) == BigLong(0x7f000000))\t//127.x.x.x/8\n\t\t\treturn false;//localhost\n\t\telse if ((ip&BigLong(0xfff00000)) == BigLong(0xac100000))\t//172.16.x.x/12\n\t\t\treturn false;//private\n\t\telse if ((ip&BigLong(0xffff0000)) == BigLong(0xc0a80000))\t//192.168.x.x/16\n\t\t\treturn false;//private\n//\t\telse if ((ip&BigLong(0xffc00000)) == BigLong(0x64400000))\t//100.64.x.x/10\n//\t\t\treturn false;//CGNAT\n\t\telse if (ip == BigLong(0x00000000))\t//0.0.0.0/32\n\t\t\treturn false;//inaddr_any\n\t}\n\tif (((struct sockaddr *)adr->sockaddr)->sa_family == AF_INET6)\n\t{\n\t\tstruct sockaddr_in6 *a = (struct sockaddr_in6 *)&adr->sockaddr;\n\t\tunsigned char *ip6 = a->sin6_addr.s6_addr;\n\t\tif ((*(int*)ip6&BigLong(0xffc00000)) == BigLong(0xfe800000))\t//fe80::/10\n\t\t\treturn false;//link-local\n\t\telse if ((*(int*)ip6&BigLong(0xfe000000)) == BigLong(0xfc00000))\t//fc::/7\n\t\t\treturn false;//ULA/private\n\t\telse if (*(int*)ip6 == BigLong(0x20010000)) //2001::/32\n\t\t\treturn false;//toredo\n\t\telse if ((*(int*)ip6&BigLong(0xffff0000)) == BigLong(0x20020000)) //2002::/16\n\t\t\treturn false;//6to4\n\t\telse if (memcmp(ip6, \"\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\1\", 16) == 0)\t//::1\n\t\t\treturn false;//localhost\n\t\telse if (memcmp(ip6, \"\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\", 16) == 0)\t//::\n\t\t\treturn false;//inaddr_any\n\t\telse if (memcmp(ip6, \"\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\xff\\xff\", 12) == 0)\t//::ffff:x.y.z.w\n\t\t\treturn false;\t//no bypassing ipv4 checks\n\t}\n\treturn true;\n}\n#ifdef ALLOWPRIVATE\nstatic qboolean TURN_PermissableIgnore(netadr_t *adr)\n{\n\tif (TURN_Permissable(adr))\n\t\treturn true;\n\n\t//its private... but we're allowing it anyway - with warning\n\tfprintf(stderr, \"WARNING: Allowing relay to access private address\\n\");\n\treturn true;\n}\n#define TURN_Permissable TURN_PermissableIgnore\n#endif\n\nvoid TURN_AddFDs(cluster_t *cluster, fd_set *set, int *m)\n{\n\tstruct turnclient_s *t;\n\tfor (t = cluster->turns; t; t = t->next)\n\t{\n\t\tif (t->remotesock < FD_SETSIZE)\n\t\t{\n\t\t\tFD_SET(t->remotesock, set);\n\t\t\tif (t->remotesock >= *m)\n\t\t\t\t*m = t->remotesock+1;\n\t\t}\n\t}\n}\nstatic void TURN_AddXorAddress(netmsg_t *m, int atrtype, netadr_t *adr)\n{\n\tunsigned short xoredport;\n\tunsigned int xoredaddr;\n\tunsigned char *xor = ((unsigned char*)m->data+4);\n\tif (((struct sockaddr *)adr->sockaddr)->sa_family == AF_INET)\n\t{\n\t\tstruct sockaddr_in *out = (struct sockaddr_in *)&adr->sockaddr;\n\t\tWriteBigShort(m, atrtype);\n\t\tWriteBigShort(m, 4+4);\n\t\tWriteBigShort(m, 1);\t//ipv4\n\t\txoredport = out->sin_port ^ *(unsigned short*)xor; WriteData(m, &xoredport, sizeof(xoredport));\n\t\txoredaddr = out->sin_addr.s_addr ^ *(unsigned int*)xor; WriteData(m, &xoredaddr, sizeof(xoredaddr));\n\t}\n\tif (((struct sockaddr *)adr->sockaddr)->sa_family == AF_INET6)\n\t{\n\t\tstruct sockaddr_in6 *out = (struct sockaddr_in6 *)&adr->sockaddr;\n\t\tif (((unsigned short*)&out->sin6_addr)[0] == 0 &&\n\t\t\t((unsigned short*)&out->sin6_addr)[1] == 0 &&\n\t\t\t((unsigned short*)&out->sin6_addr)[2] == 0 &&\n\t\t\t((unsigned short*)&out->sin6_addr)[3] == 0 &&\n\t\t\t((unsigned short*)&out->sin6_addr)[4] == 0 &&\n\t\t\t((unsigned short*)&out->sin6_addr)[5] == 0xffff)\n\t\t{\t//we're listening on a hybrid socket, so if we get an ipv4 packet reply with a proper ipv4 address.\n\t\t\tWriteBigShort(m, atrtype);\n\t\t\tWriteBigShort(m, 4+4);\n\t\t\tWriteBigShort(m, 1);\t//ipv4\n\t\t\txoredport = out->sin6_port ^ *(unsigned short*)xor; WriteData(m, &xoredport, sizeof(xoredport));\n\t\t\txoredaddr = ((unsigned int*)&out->sin6_addr)[3] ^ *(unsigned int*)xor; WriteData(m, &xoredaddr, sizeof(xoredaddr));\n\t\t}\n\t\telse\n\t\t{\n\t\t\tWriteBigShort(m, atrtype);\n\t\t\tWriteBigShort(m, 4+16);\n\t\t\tWriteBigShort(m, 2);\t//ipv6\n\t\t\txoredport = out->sin6_port ^ *(unsigned short*)xor; WriteData(m, &xoredport, sizeof(xoredport));\n\t\t\txoredaddr = ((unsigned int*)&out->sin6_addr)[0] ^ ((unsigned int*)xor)[0]; WriteData(m, &xoredaddr, sizeof(xoredaddr));\n\t\t\txoredaddr = ((unsigned int*)&out->sin6_addr)[1] ^ ((unsigned int*)xor)[1]; WriteData(m, &xoredaddr, sizeof(xoredaddr));\n\t\t\txoredaddr = ((unsigned int*)&out->sin6_addr)[2] ^ ((unsigned int*)xor)[2]; WriteData(m, &xoredaddr, sizeof(xoredaddr));\n\t\t\txoredaddr = ((unsigned int*)&out->sin6_addr)[3] ^ ((unsigned int*)xor)[3]; WriteData(m, &xoredaddr, sizeof(xoredaddr));\n\t\t}\n\t}\n}\nstatic void TURN_ReadXorAddress(netmsg_t *m, netadr_t *adr)\n{\n\tint type = ReadBigShort(m)&0xff;\n\tunsigned char *xor = ((unsigned char*)m->data+4);\n\tmemset(adr, 0, sizeof(*adr));\n\tif (type == 1)\n\t{\n\t\tstruct sockaddr_in *out = (struct sockaddr_in *)&adr->sockaddr;\n\t\tmemset(out, 0, sizeof(*out));\n\t\tout->sin_family = AF_INET;\n\t\tout->sin_port = *(unsigned short*)((unsigned char*)m->data+m->readpos) ^ *(unsigned short*)xor;\n\t\tm->readpos += 2;\n\t\tout->sin_addr.s_addr = *(unsigned int*)((unsigned char*)m->data+m->readpos) ^ *(unsigned int*)xor;\n\t\tm->readpos += 4;\n\t}\n\telse if (type == 2)\n\t{\n\t\tstruct sockaddr_in6 *out = (struct sockaddr_in6 *)&adr->sockaddr;\n\t\tmemset(out, 0, sizeof(*out));\n\t\tout->sin6_family = AF_INET6;\n\t\tout->sin6_port = *(unsigned short*)((unsigned char*)m->data+m->cursize) ^ *(unsigned short*)xor;\n\t\tm->readpos += 2;\n\t\t((unsigned int*)&out->sin6_addr)[0] = ((unsigned int*)((unsigned char*)m->data+m->readpos))[0] ^ ((unsigned int*)xor)[0];\n\t\t((unsigned int*)&out->sin6_addr)[1] = ((unsigned int*)((unsigned char*)m->data+m->readpos))[1] ^ ((unsigned int*)xor)[1];\n\t\t((unsigned int*)&out->sin6_addr)[2] = ((unsigned int*)((unsigned char*)m->data+m->readpos))[2] ^ ((unsigned int*)xor)[2];\n\t\t((unsigned int*)&out->sin6_addr)[3] = ((unsigned int*)((unsigned char*)m->data+m->readpos))[3] ^ ((unsigned int*)xor)[3];\n\t\tm->readpos += 16;\n\t}\n}\n#ifdef TURN_IS_NOT_BLIND\nstatic qboolean TURN_PacketIsStun(unsigned char *msg, size_t sz)\n{\n\t//used to check if a packet going through the relay is a stun packet.\n\t//we require the peer to 'reply' with a stun packet before we allow non-stun packets through.\n\t//this prevents us from being used to attack other services, for the most part.\n\tif (sz < 20)\n\t\treturn false;\t//too small for even just the header\n\tif ((((msg[0]<<8)|msg[1])&~STUN_ERROR) != STUN_BINDING)\n\t\treturn false;\t//not valid for opening the connection... don't let turn allocation requests through...\n\tif (((msg[2]<<8)|(msg[3]<<0)) != sz-20)\n\t\treturn false;\t//bad size\n\tif (((msg[4]<<24)|(msg[5]<<16)|(msg[6]<<8)|(msg[7]<<0)) != STUN_MAGIC_COOKIE)\n\t\treturn false;\t//could actually be stun, but w/e\n\n\t//that's probably enough checks.\n\treturn true;\n}\nstatic qboolean TURN_PacketIsBanned(unsigned char *msg, size_t sz)\n{\n\t//if it looks like a stun packet, and isn't a binding, assume its a TURN packet and block it. no nesting turn packets over turn.\n\tif (sz >= 20)\n\t\tif (((msg[4]<<24)|(msg[5]<<16)|(msg[6]<<8)|(msg[7]<<0)) == STUN_MAGIC_COOKIE)\n\t\t\tif (((msg[0]<<8)|msg[1]) != STUN_BINDING)\n\t\t\t\tif (((msg[2]<<8)|(msg[3]<<0)) == sz-20)\n\t\t\t\t\treturn true;\n\n\tif (TURN_PacketIsStun(msg,sz))\n\t\treturn false;\t//allow the ICE probes.\n\tif (sz && (msg[0] >= 20 && msg[0] <= 63))\n\t\treturn false;\t//always allow dtls\n\treturn true;\t\t//block everything else. we're expecting these to be run by arbitrary third parties, so unencrypted stuff is --ed.\n}\n#endif\n\nvoid TURN_CheckFDs(cluster_t *cluster)\n{\n\tint ofs = 20 + 4+20 + 4;\n\tchar buf[8192];\n\tint len;\n\tint addrlen;\n\tstruct turnclient_s *t, **link;\n\tnetadr_t from = {NULL};\n\n//FIXME: use epoll or something. this loop is stupid.\n\tfor (link = &cluster->turns; (t = *link);)\n\t{\n\t\tif ((int)(t->timeout-cluster->curtime) < 0)\n\t\t{\t//check timeouts, kill if expired.\n\t\t\tcluster->numrelays--;\n//printf(\"relay %s timeout\\n\", t->username);\n\t\t\t*link = t->next; //remove it\n\t\t\tclosesocket(t->remotesock);\n\t\t\tfree(t);\n\t\t\tcontinue;\n\t\t}\n\n\t\tlink = &t->next;\n\t\taddrlen = sizeof(from.sockaddr);\n\t\tlen = recvfrom(t->remotesock, buf+ofs, sizeof(buf)-ofs, 0, (struct sockaddr*)&from.sockaddr, &addrlen);\n\t\tif (len > 0)\n\t\t{\n\t\t\tint perm = TURN_FindPermission(cluster, t, &from);\n\t\t\tif (perm < 0)\n\t\t\t{\n//Sys_Printf(cluster, \"TURN: (inbound) peer not authorised\\n\");\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (t->isfwd)\n\t\t\t{\t//just directly forward it to the client.\n\t\t\t\tnetadr_t fup;\n\t\t\t\tif (len > 4 && *(unsigned int*)(buf+ofs) == ~0)\n\t\t\t\t\tt->remotes[perm].seenstun = true;\t//proper out of band packets, woo... assume its okay.\n\t\t\t\telse if (!t->remotes[perm].seenstun)\n\t\t\t\t\tcontinue;\t//err... its giving weird responses...\n\t\t\t\tNET_SendPacket(cluster, NET_ChooseSocket(cluster->qwdsocket, &fup, t->clientaddr), len, buf+ofs, fup);\n\t\t\t\tcontinue;\n\t\t\t}\n\n#ifdef TURN_IS_NOT_BLIND\n\t\t\tif (!t->remotes[perm].seenstun)\n\t\t\t{\n\t\t\t\tif (TURN_PacketIsStun(buf+ofs, len))\n\t\t\t\t\tt->remotes[perm].seenstun = true;\t//its an ICE/stun binding packet. peer is a genuine ice peer, open it up.\n\t\t\t\telse\n\t\t\t\t\tcontinue;\t//don't let anything through at all until we've seen a stun-binding packet from the peer (required as part of ICE).\n\t\t\t}\n\t\t\telse if (TURN_PacketIsBanned(buf+ofs, len))\n\t\t\t\tcontinue;\n#endif\n\n\t\t\t{\n\t\t\t\tnetmsg_t o = {0};\n\t\t\t\to.maxsize = ofs;\n\t\t\t\tofs -= 4;\n\t\t\t\tif (((struct sockaddr*)from.sockaddr)->sa_family == AF_INET)\n\t\t\t\t\tofs -= 4+8;\n\t\t\t\telse if (((struct sockaddr*)from.sockaddr)->sa_family == AF_INET6)\n\t\t\t\t\tofs -= 4+20;\n\t\t\t\tofs -= 20;\n\t\t\t\to.maxsize -= ofs;\n\t\t\t\to.data = buf+ofs;\n\t\t\t\to.cursize=0;\n\t\t\t\tWriteBigShort(&o, STUN_DATA|STUN_INDICATION);\n\t\t\t\tWriteBigShort(&o, 0); //size (filled in later)\n\t\t\t\tWriteBigLong(&o, STUN_MAGIC_COOKIE);\n\t\t\t\tWriteBigLong(&o, 0);\n\t\t\t\tWriteBigLong(&o, 0);\n\t\t\t\tWriteBigLong(&o, 0);\n\t\t\t\tTURN_AddXorAddress(&o, STUNATTR_XOR_PEER_ADDRESS, &from);\n\t\t\t\tWriteBigShort(&o, STUNATTR_DATA);\n\t\t\t\tWriteBigShort(&o, len);\n\t\t\t\t//should be at our original write pos now...\n\t\t\t\to.cursize += len;\n\t\t\t\to.maxsize = sizeof(buf)-ofs;\n\t\t\t\twhile(o.cursize&3)\n\t\t\t\t\tWriteByte(&o, 0);\t//pad it to 4 bytes, to make chrome happy.\n\n\t\t\t\tTURN_Send(cluster, &o, &t->clientaddr);\n\t\t\t}\n\t\t}\n\t}\n\n\tFwd_DoPing(cluster);\n}\n\nstatic struct turnclient_s *TURN_Allocate(cluster_t *cluster, netadr_t *clientadr, int fam, const char *username, const char *realm)\n{\n\tunsigned char dig[64];\n\tstruct turnclient_s *t;\n\tSOCKET sock;\n\n\tsize_t i;\n\tint pf;\n\tstruct sockaddr *address;\n\tstruct sockaddr_in\taddress4;\n\tstruct sockaddr_in6\taddress6;\n\tsocklen_t addrlen;\n\n\tunsigned long nonblocking = true;\n\tunsigned long v6only = false;\n\tunsigned short *port;\n\tunsigned int tries = 5;\n\n\tswitch(fam)\n\t{\n\tcase 2:\n\t\tpf = PF_INET6;\n\t\tmemset(&address6, 0, sizeof(address6));\n\t\taddress6.sin6_family = AF_INET6;\n\t\tport = &address6.sin6_port;\n\t\taddress = (struct sockaddr*)&address6;\n\t\taddrlen = sizeof(address6);\n\t\tbreak;\n\tcase 1:\n\t\tpf = PF_INET;\n\t\taddress4.sin_family = AF_INET;\n\t\taddress4.sin_addr.s_addr = INADDR_ANY;\n\t\tport = &address4.sin_port;\n\t\taddress = (struct sockaddr*)&address4;\n\t\taddrlen = sizeof(address4);\n\t\tbreak;\n\tdefault:\n\t\treturn NULL;\t//erk\n\t}\n\tif ((sock = socket (pf, SOCK_DGRAM, IPPROTO_UDP)) == INVALID_SOCKET)\n\t\treturn NULL;\n\n#if defined(FD_SETSIZE) && !defined(HAVE_EPOLL)\n\tif (sock >= FD_SETSIZE)\n\t{\t//'select' cannot cope with this fd. dom't bug out.\n\t\tclosesocket(sock);\n\t\treturn NULL;\n\t}\n#endif\n\n\tif (ioctlsocket (sock, FIONBIO, &nonblocking) == -1)\n\t{\n\t\tclosesocket(sock);\n\t\treturn NULL;\n\t}\n\n\tif (pf == AF_INET6)\n\t\tsetsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&v6only, sizeof(v6only));\n\n#if defined(_WIN32) && defined(SO_EXCLUSIVEADDRUSE)\n\t//win32 is so fucked up\n\tsetsockopt(newsocket, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (char *)&_true, sizeof(_true));\n#endif\n\n\tfor (tries = 5; ; tries--)\n\t{\n\t\tif (!realm)\t//qwfwd always uses ephemerial ports. they're outgoing and thus don't need special attention to work around the relay's firewall.\n\t\t\t*port = 0;\n\t\telse if (cluster->turn_maxport <= cluster->turn_minport)\t//something screwy\n\t\t\t*port = htons(cluster->turn_minport);\n\t\telse\n\t\t\t*port = htons(cluster->turn_minport + rand()%(cluster->turn_maxport-cluster->turn_minport));\t//pick a random port from the allowed range (constrained by firewall/router settings).\n\t\tif (bind (sock, (void *)address, addrlen) != -1)\n\t\t\tbreak;\t//success.\n\n\t\tif (tries <= 0 || *port==0)\n\t\t{\t//ran out of attempts to pick a random usable port.\n\t\t\tclosesocket(sock);\n\t\t\treturn NULL;\n\t\t}\n\t}\n\n\tt = calloc(1, sizeof(*t) + 64 + strlen(username)+1);\n\tt->remotesock = sock;\n\n\tmemcpy(t->relayaddr.sockaddr, address, addrlen);\n\tgetsockname(sock, (struct sockaddr*)t->relayaddr.sockaddr, &addrlen);\t//find how it was actually bound\n\n\t//report the public address reported by the master.\n\tif (((struct sockaddr *)t->relayaddr.sockaddr)->sa_family == AF_INET)\n\t\tmemcpy(&((struct sockaddr_in *)t->relayaddr.sockaddr)->sin_addr, cluster->turn_ipv4, sizeof(cluster->turn_ipv4));\n\telse if (((struct sockaddr *)t->relayaddr.sockaddr)->sa_family == AF_INET6)\n\t\tmemcpy(&((struct sockaddr_in6 *)t->relayaddr.sockaddr)->sin6_addr, cluster->turn_ipv6, sizeof(cluster->turn_ipv6));\n\n\tt->clientaddr = *clientadr;\n\n\tt->timeout = cluster->curtime + 5*60*1000;\n\tfor (i = 0; i < countof(t->remotes); i++)\n\t\tt->remotes[i].timeout = cluster->curtime - 1; //some time in the past.\n\n\tt->auth = (char*)(t+1);\n\tt->username = t->auth+64;\n\tstrcpy(t->username, username);\n\ttobase64(t->auth,64, dig, CalcHMAC(&hash_sha1, dig,sizeof(dig), t->username,strlen(t->username), cluster->turnkey,sizeof(cluster->turnkey)));\n\n\t//compute our 'long-term' key. stoopid md5. so we can send the right responses.\n\tif (realm)\n\t{\n\t\thashfunc_t *pwdhash = &hash_md5;\n\t\tsize_t usersz = strlen(t->username);\n\t\tsize_t realmsz = strlen(realm);\n\t\tsize_t authsz = strlen(t->auth);\n\t\tchar *tmpkey = alloca(usersz + realmsz + authsz + 3);\n\t\tmemcpy(tmpkey+0,                  t->username, usersz);\n\t\t\t   tmpkey[usersz] = ':';\n\t\tmemcpy(tmpkey+usersz+1,           realm, realmsz);\n\t\t\t   tmpkey[usersz+1+realmsz] = ':';\n\t\tmemcpy(tmpkey+usersz+1+realmsz+1, t->auth, authsz);\n\t\t\t   tmpkey[usersz+1+realmsz+1+authsz] = '\\0';\n\t\tt->keysize = CalcHash(pwdhash, t->key,sizeof(t->key), tmpkey,strlen(tmpkey));\n\t}\n\telse\n\t\tt->isfwd = true;\n\n\tcluster->numrelays++;\n\tt->next = cluster->turns;\n\tcluster->turns = t;\n\n#ifdef HAVE_EPOLL\n\t{\n\t\tstruct epoll_event ev;\n\t\tev.data.ptr = t;\n\t\tev.events = EPOLLIN;\n\t\tepoll_ctl(cluster->epfd, EPOLL_CTL_ADD, t->remotesock, &ev);\n\t}\n#endif\n\treturn t;\n}\n\nstatic size_t TURN_GenerateNonce(netadr_t *adr, char *buf, size_t bufsize)\n{\t//needs to be reproducible.\n\t//also pretty much needs to be base64. stoopid stun rules.\n\tchar key[64];\n\tsize_t keysize;\n\thashfunc_t *func = &hash_sha1;\t//any.\n\tvoid *ctx = alloca(func->contextsize);\n\tif (sizeof(key) < func->digestsize)\n\t\treturn 0;\t//panic\n\tfunc->init(ctx);\n\tfunc->process(ctx, \"weewoo\", 6);\n\tif (((struct sockaddr *)adr->sockaddr)->sa_family == AF_INET)\n\t{\n\t\tstruct sockaddr_in *out = (struct sockaddr_in *)&adr->sockaddr;\n\t\tfunc->process(ctx, (void*)out,sizeof(*out));\n\t}\n\telse if (((struct sockaddr *)adr->sockaddr)->sa_family == AF_INET6)\n\t{\n\t\tstruct sockaddr_in6 *out = (struct sockaddr_in6 *)&adr->sockaddr;\n\t\tfunc->process(ctx, (void*)out,sizeof(*out));\n\t}\n\telse\n\t\treturn 0;\t//shouldn't be possible... should probably put an assert here.\n\n\tfunc->terminate(key, ctx);\n\tkeysize = func->digestsize;\n\n\ttobase64(buf,bufsize, key,keysize);\n\treturn strlen(buf);\n}\nstatic qboolean TURN_ValidateNonce(unsigned char *nonce, netadr_t *adr)\n{\n\tunsigned char buf[20];\n\tsize_t insz = (nonce?nonce[-1]:0); //read the attribute's length. lazily.\n\tsize_t needsz = TURN_GenerateNonce(adr, buf, sizeof(buf));\n\tif (insz != needsz)\n\t\treturn false;\t//screwy size... reject it.\n\telse\n\t\treturn !memcmp(nonce, buf, needsz);\n}\nstatic qboolean TURN_ValidateIntegrity(cluster_t *cluster, netmsg_t *m, const unsigned char *user, const unsigned char *realm, const unsigned char *integritycheck, const struct turnclient_s *t)\n{\n\tint attroffset = (integritycheck-(unsigned char*)m->data);\n\tunsigned char needintegrity[32];\n\n\tsize_t keysize;\n\tunsigned char key[32];\n\tchar authbuf[64];\n\tchar *auth;\n\n\thashfunc_t *pwdhash = &hash_md5;\n\thashfunc_t *hash;\n\tif (((integritycheck[-4]<<8)|integritycheck[-3]) == STUNATTR_MSGINTEGRITIY_SHA1)\n\t\thash = &hash_sha1;\n//\telse if (((integritycheck[-4]<<8)|integritycheck[-3]) == STUNATTR_MSGINTEGRITIY_SHA2_256)\n//\t\thash = &hash_sha2_256;\n\telse\n\t\treturn false;\t//can't validate this\n\n\tif (((integritycheck[-2]<<8)|integritycheck[-1]) != hash->digestsize)\n\t\treturn false;\t//nope... screwy.\n\n\tif (t)\n\t{\t//we already validated the username... make sure the connection stays using the same one.\n\t\tif (strcmp(user, t->username))\n\t\t\treturn false;\t//err... it changed? no, get lost.\n\t\tauth = t->auth;\t\t//still using the same password, too\n\t}\n\telse\n\t{\t//validate the username... we just check the timestamp bit.\n\t\ttime_t timestamp = strtoull(user, NULL, 0);\n\t\ttime_t now = time(NULL);\n\t\tunsigned char dig[32];\n\n\t\tif (timestamp > now+10)\n\t\t\treturn false;\t//clockskew? reject timestamps in the future...\n\t\tif (timestamp+60*60 < now)\n\t\t\treturn false; //more than an hour old, you'll need to get a new authorisation.\n\n\t\t//figure out what the correct auth string should be.\n\t\tkeysize = CalcHMAC(&hash_sha1, dig,sizeof(dig), user,strlen(user), cluster->turnkey,sizeof(cluster->turnkey));\n\t\ttobase64(authbuf,sizeof(authbuf), dig, keysize);\n\t\tauth = authbuf;\t//this should match what the client was told by the broker.\n\t}\n\n\t//compute our 'long-term' key. stoopid md5.\n\t{\n\t\tsize_t usersz = strlen(user);\n\t\tsize_t realmsz = strlen(realm);\n\t\tsize_t authsz = strlen(auth);\n\t\tchar *tmpkey = alloca(usersz + realmsz + authsz + 3);\n\t\tmemcpy(tmpkey+0,                  user, usersz);\n\t\t\t   tmpkey[usersz] = ':';\n\t\tmemcpy(tmpkey+usersz+1,           realm, realmsz);\n\t\t\t   tmpkey[usersz+1+realmsz] = ':';\n\t\tmemcpy(tmpkey+usersz+1+realmsz+1, auth, authsz);\n\t\t\t   tmpkey[usersz+1+realmsz+1+authsz] = '\\0';\n\t\tkeysize = CalcHash(pwdhash, key,sizeof(key), tmpkey,strlen(tmpkey));\n\t}\n\n\t//message integrity is a bit annoying\n\t((unsigned char*)m->data)[2] = ((attroffset+hash->digestsize-20)>>8)&0xff;\t//hashed header length is up to the end of the hmac attribute\n\t((unsigned char*)m->data)[3] = ((attroffset+hash->digestsize-20)>>0)&0xff;\n\tattroffset-=4;\t//but the hash is to the start of the attribute's header\n\n\t//compute the hmac that we're actually checking, success if it matches.\n\tkeysize = CalcHMAC(hash, needintegrity, sizeof(needintegrity), m->data, attroffset, key,keysize);\n\treturn !memcmp(needintegrity, integritycheck, keysize);\n}\nstatic void TURN_AddIntegrity(cluster_t *cluster, netmsg_t *m, const struct turnclient_s *t)\n{\n\tsize_t keysize;\n\thashfunc_t *hash = &hash_sha1;\n\tunsigned char integrity[64];\n\tunsigned int crc;\n\n\tif (t)\n\t{\n\t\t//message integrity is a bit annoying\n\t\t((unsigned char*)m->data)[2] = ((m->cursize+4+hash->digestsize-20)>>8)&0xff;\t//hashed header length is up to the end of the hmac attribute\n\t\t((unsigned char*)m->data)[3] = ((m->cursize+4+hash->digestsize-20)>>0)&0xff;\n\t\tkeysize = CalcHMAC(hash, integrity, sizeof(integrity), m->data, m->cursize, t->key,t->keysize);\n\t\tWriteBigShort(m, STUNATTR_MSGINTEGRITIY_SHA1);\n\t\tWriteBigShort(m, keysize);\n\t\tWriteData(m, integrity, keysize);\n\t}\n\n#ifndef NO_ZLIB\n\t((unsigned char*)m->data)[2] = ((m->cursize+8-20)>>8)&0xff;\t//dummy length\n\t((unsigned char*)m->data)[3] = ((m->cursize+8-20)>>0)&0xff;\n\tcrc = crc32(0, m->data, m->cursize)^0x5354554e;\n\tWriteBigShort(m, STUNATTR_FINGERPRINT);\n\tWriteBigShort(m, sizeof(crc));\n\tWriteBigLong(m, crc);\n#endif\n}\nstatic qboolean TURN_ValidateFingerprint(cluster_t *cluster, netmsg_t *m, unsigned int needcrc)\n{\t//this just verifies that its actually a correct non-corrupted STUN packet. otherwise assume some other multiplexed protocol.\n\tunsigned int crc;\n\n\tif (m->cursize != m->readpos)\n\t\treturn false;\t//must be the last attribute. so don't actually need to fiddle the size.\n\n\t((unsigned char*)m->data)[2] = (((m->readpos-20)>>8)&0xff);\t//restore the size (integrity might have fucked with it)\n\t((unsigned char*)m->data)[3] = (((m->readpos-20)>>0)&0xff);\n\tcrc = crc32(0, m->data, m->readpos-8)^0x5354554e;\n\tif (needcrc == crc)\n\t\treturn true;\t//needs to match.\n\treturn false;\t\t//oops.\n}\nqboolean TURN_IsRequest(cluster_t *cluster, netmsg_t *m, netadr_t *from)\n{\n\tstunhdr_t hdr;\n\tunsigned short atr, len, prot;\n\tunsigned int nextread;\n\tunsigned int error = 0;\n\tvoid *data;\n\tint fam = 1;\n\tsize_t datasize;\n\tsize_t lifetime;\n\tnetadr_t peeraddr[8];\n\tint inpeers;\n\tstruct turnclient_s *t;\n\tunsigned char *innonce, *inrealm, *inuser, *inintegrity;\n\tunsigned char noncebuf[64];\n\n\t//try and identify them first.\n\tfor (t = cluster->turns; t; t = t->next)\n\t{\n\t\tif (Net_CompareAddress(&t->clientaddr, from, 0, 1))\n\t\t{\t//forward it as-is... mostly.\n\t\t\tif (!t->isfwd)\t//turn... check the packet for stun/turn stuff. we now know their connection. woo.\n\t\t\t\tbreak;\n\t\t\tif (m->cursize>=11 && !memcmp(\"\\xff\\xff\\xff\\xff\"\"connect \", m->data, 11))\n\t\t\t{\t//connect ver qport challenge info\n\t\t\t\t//we need to pop one proxy from the 'prx' list so the next proxy doesn't try to connect to itself.\n\t\t\t\tchar userinfo[1024];\n\t\t\t\tchar prx[256];\n\t\t\t\tchar *s = (char*)m->data+11;\n\t\t\t\tchar *at;\n\t\t\t\tchar *infostart;\n\t\t\t\tsize_t pre,infolen,trail;\n\n\t\t\t\t//parse it a bit\n\t\t\t\ts = COM_ParseToken(s, noncebuf,sizeof(noncebuf), \"\");\t//read the ver\n\t\t\t\tif (atoi(noncebuf)!=28)\n\t\t\t\t\treturn true;\t//that protocol ain't supported... nor allowed. get lost. we won't be able to block it over dtls but we also shouldn't need to care (yay for using dtlsconnect instead, which leaks no userinfo beyond the target chain)\n\t\t\t\ts = COM_ParseToken(s, noncebuf,sizeof(noncebuf), \"\");\t//read the qport\n\t\t\t\tinfostart = s= COM_ParseToken(s, noncebuf,sizeof(noncebuf), \"\");\t//read the challenge (probably not ours)\n\t\t\t\ts = COM_ParseToken(s, userinfo,sizeof(userinfo), \"\");\t//read the challenge (probably not ours)\n\n\t\t\t\t//change the userinfo\n\t\t\t\tInfo_ValueForKey(userinfo, \"prx\", prx,sizeof(prx));\n\t\t\t\tat = strrchr(prx, '@');\n\t\t\t\tif (!*prx)\n\t\t\t\t\treturn true;\t//err... no next hop? we're meant to be handling it? wtf?\n\t\t\t\telse if (at)\n\t\t\t\t\t*at = 0;\t//strip off the last proxy (the one we're meant to be proxying to)\n\t\t\t\telse\n\t\t\t\t\t*prx = 0;\t//clear it entirely. next hop is the final server. lucky client.\n\t\t\t\tInfo_SetValueForStarKey(userinfo, \"prx\", prx, sizeof(userinfo));\n\t\t\t\t//we could set some \"*clientip\" key, appending 'from', so the server can use that instead of realip mess, but that'd require the server to trust us when banning.\n\n\t\t\t\t//hack up the packet to include the new info (with any trailing data still in place, ready for the next hop to see.\n\t\t\t\tpre = (infostart-(char*)m->data);\n\t\t\t\tinfolen = strlen(userinfo);\n\t\t\t\ttrail = m->cursize-(s-(char*)m->data);\n\t\t\t\tm->cursize = pre+2+infolen+1+trail;\n\t\t\t\tif (m->cursize > m->maxsize)\n\t\t\t\t\treturn true;\t//don't crash.\n\t\t\t\tmemmove(&((char*)m->data)[pre+infolen+3], s, trail);\n\t\t\t\t((char*)m->data)[pre] = ' ';\n\t\t\t\t((char*)m->data)[pre+1] = '\\\"';\n\t\t\t\tmemcpy(&((char*)m->data)[pre+2], userinfo, infolen);\n\t\t\t\t((char*)m->data)[pre+2+infolen] = '\\\"';\n\t\t\t\t//should have wiped the userinfo part...\n\t\t\t\t//((char*)m->data)[m->cursize] = 0;\t//for debugging.\n\t\t\t}\n\t\t\telse if (m->cursize>=15 && !memcmp(\"\\xff\\xff\\xff\\xff\"\"dtlsconnect \", m->data, 15))\n\t\t\t{\t//dtlsconnect challenge target\n\t\t\t\t//we need to strip the target. the connect will then be hidden from us.\n\t\t\t\tchar *peer = COM_ParseToken((char*)m->data+15, noncebuf,sizeof(noncebuf), \"\");\t//read the peer's challenge.\n\t\t\t\tchar *at = strrchr(peer, '@');\n\t\t\t\tif (!*peer)\n\t\t\t\t\treturn true;\t//no next hop? wtf?\n\t\t\t\tpeer = (at?at:peer);\n\t\t\t\tm->cursize = peer-(char*)m->data;\t//just truncate it.\n\n\t\t\t\t//while (*peer == '@' || *peer == ' ')\n\t\t\t\t//\tpeer++;\n\t\t\t\t//NET_StringToAddr(peer, &peeraddr[0], 27500);\n\t\t\t\t//if (!Net_CompareAddress(&peeraddr[0], t->remotes[0].remoteaddr))\n\t\t\t\t//\treturn true;\t//tried connecting to somewhere else... doesn't make sense.\n\t\t\t}\n\t\t\tt->timeout = t->remotes[0].timeout = cluster->curtime + QWFWDTIMEOUT;\t//renew it from c2s packets.\n\t\t\tif (!t->remotes[0].seenstun && m->cursize < 4 && *(unsigned int*)m->data!=~0)\n\t\t\t\treturn true;\t//don't allow it through until we get a proper response from the target to know its actually valid and not a ddos.\n\t\t\tNET_SendPacket(cluster, t->remotesock, m->cursize, m->data, t->remotes[0].remoteaddr);\n\t\t\treturn true;\n\t\t}\n\t}\n\n\tif (m->cursize < 20 || !cluster->turnenabled)\n\t\treturn false;\n\thdr.msgtype = ReadBigShort(m);\n//\tif (hdr.msgtype != STUN_ALLOCATE)\n//\t\treturn false;\n\thdr.msglen = ReadBigShort(m);\n\tif (20+hdr.msglen != m->cursize)\n\t\treturn false;\t//nope... sized wrong. must be something else.\n\thdr.magiccookie = ReadBigLong(m);\n\tif (hdr.magiccookie != STUN_MAGIC_COOKIE)\n\t\treturn false;\t//might be an older version, but we don't care. don't let it multiplex badly.\n\thdr.transactid[0] = ReadBigLong(m);\n\thdr.transactid[1] = ReadBigLong(m);\n\thdr.transactid[2] = ReadBigLong(m);\n\n\tt = NULL;\n\tprot = 0;\n\tlifetime = 0;\n\tdata = NULL;\n\tdatasize = 0;\n\tinuser = inrealm = innonce = inintegrity = NULL;\n\tinpeers = 0;\n\n//Sys_Printf(cluster, \"TURN: msgtype %x\\n\", hdr.msgtype);\n\tfor(; m->readpos < m->cursize; m->readpos = nextread)\n\t{\n\t\tatr = ReadBigShort(m);\n\t\tlen = ReadBigShort(m);\n\t\tnextread = m->readpos + ((len+3)&~3);\n\t\tif (nextread > m->cursize)\n\t\t\treturn false;\t//nope, corrupt\n//Sys_Printf(cluster, \" TURN: attribute %04x\\n\", atr);\n\t\tif (atr == STUNATTR_FINGERPRINT)\n\t\t{\n#ifdef NO_ZLIB\n\t\t\tcontinue;\n#else\n\t\t\tif (!TURN_ValidateFingerprint(cluster, m, ReadBigLong(m)))\n\t\t\t{\n//Sys_Printf(cluster, \" TURN: Invalid fingerprint\\n\");\n\t\t\t\treturn false;\n\t\t\t}\n#endif\n\t\t}\n\t\t/*else if (atr == STUNATTR_MSGINTEGRITIY_SHA2_256 && (!inintegrity || (inintegrity[-4]<<8)|inintegrity[-3]==STUNATTR_MSGINTEGRITIY_SHA1))\n\t\t{\n\t\t\tinintegrity = (char*)m->data + m->readpos;\n\t\t}*/\n\t\telse if (inintegrity)\n\t\t{\n//Sys_Printf(cluster, \" TURN: Ignoring post-integrity %04x\\n\", atr);\n\t\t\tcontinue;\t//anything after the integrity must be ignored (except those things above).\n\t\t}\n\t\telse if (atr == STUNATTR_MSGINTEGRITIY_SHA1)\n\t\t\tinintegrity = (char*)m->data + m->readpos;\n\t\telse if (atr == STUNATTR_REQUESTED_TRANSPORT)\n\t\t\tprot = ReadBigLong(m)>>24;\n\t\telse if (atr == STUNATTR_LIFETIME)\n\t\t\tlifetime = ReadBigLong(m)*1000;\n\t\telse if (atr == STUNATTR_REQUESTED_ADDRFAM)\n\t\t\tfam = ReadBigLong(m);\n\t\telse if (atr == STUNATTR_ADDITIONAL_ADDRFAM)\n\t\t{\t//optional\n\t\t\t//fam = ReadBigLong(m);\n\t\t}\n\t\telse if (atr == STUNATTR_DONT_FRAGMENT)\n\t\t\t;\n\t\telse if (atr == STUNATTR_SOFTWARE)\n\t\t\t;\n\t\telse if (atr == STUNATTR_USERNAME)\n\t\t\tinuser = (char*)m->data + m->readpos;\n\t\telse if (atr == STUNATTR_REALM)\n\t\t\tinrealm = (char*)m->data + m->readpos;\n\t\telse if (atr == STUNATTR_NONCE)\n\t\t\tinnonce = (char*)m->data + m->readpos;\n\t\telse if (atr == STUNATTR_DATA && hdr.msgtype == (STUN_INDICATION|STUN_SEND))\n\t\t{\n\t\t\tdata = (char*)m->data + m->readpos;\n\t\t\tdatasize = len;\n\t\t}\n\t\telse if (atr == STUNATTR_XOR_PEER_ADDRESS && (\n\t\t\t\t\thdr.msgtype == (STUN_INDICATION|STUN_SEND) ||\n\t\t\t\t\thdr.msgtype == STUN_CREATEPERM))\n\t\t{\n\t\t\tif (inpeers < countof(peeraddr))\n\t\t\t\tTURN_ReadXorAddress(m, &peeraddr[inpeers++]);\t//FIXME: we should support multiple of these.\n\t\t}\n\t\telse if (atr & 0x8000)\n\t\t{\n\t\t\tcontinue;\t\t//unknown optional attributes\n\t\t}\n\t\telse\n\t\t\terror = 420;\n\t}\n\tif (!error && prot != 17 && hdr.msgtype == STUN_ALLOCATE)\t//only udp supported between relay and remote peer.\n\t\terror = 442;\n\n\tif (!error)\n\t{\n\t\tif (hdr.msgtype == STUN_CREATEPERM || hdr.msgtype == STUN_REFRESH || hdr.msgtype == STUN_ALLOCATE)\n\t\t{\n\t\t\tif (!inuser || !inrealm || !innonce || !inintegrity)\n\t\t\t\terror = 401;\t//something is null...\n\t\t\telse if (!TURN_ValidateNonce(innonce, from))\n\t\t\t\terror = 438;\t//'Stale Nonce' shouldn't really be happening... client somehow changed address?\n\t\t\telse if (t && strcmp(inuser, t->username))\n\t\t\t\terror = 441;\t//'Wrong Credentials' - no changing usernames!\n\t\t\telse if (!TURN_ValidateIntegrity(cluster, m, inuser, inrealm, inintegrity, t))\n\t\t\t{\n//Sys_Printf(cluster, \" TURN: Validation failed\\n\");\n\t\t\t\terror = 401;\t//'Unauthorised'\n\t\t\t}\n\t\t\telse if (!t && hdr.msgtype == STUN_ALLOCATE)\n\t\t\t{\n\t\t\t\tt = TURN_Allocate(cluster, from, fam, inuser, inrealm);\n\t\t\t\tif (!t)\n\t\t\t\t\terror = 508; //'Insufficient Capacity'\n\t\t\t}\n\t\t}\n\t}\n\tif (!t && !error)\n\t\terror = 437; //'Allocation Mismatch'\n\n\tif (hdr.msgtype == STUN_ALLOCATE || hdr.msgtype == STUN_REFRESH || hdr.msgtype == STUN_CREATEPERM)\n\t{\n\t\tstruct{\n\t\t\tstunhdr_t hdr;\n\t\t\tunsigned int data[64];\n\t\t} pkt;\n\t\tnetmsg_t o = {0, 0, sizeof(pkt), &pkt};\n\n\t\tif (t && !error && hdr.msgtype == STUN_CREATEPERM)\n\t\t{\t//FIXME: request could include multiple peers\n\t\t\tint i, p;\n\t\t\tfor (p = 0; p < inpeers; p++)\n\t\t\t\tif (!TURN_Permissable(&peeraddr[p]))\n\t\t\t\t\terror = 403;\t//no relaying to localhost/etc. no bypassing firewalls.\n\t\t\tif (!error)\n\t\t\t{\n\t\t\t\tfor (p = 0; p < inpeers; p++)\n\t\t\t\t{\n\t\t\t\t\tfor (i = 0; i < countof(t->remotes); i++)\n\t\t\t\t\t{\n\t\t\t\t\t\tif ((signed int)(t->remotes[i].timeout-cluster->curtime) < 0)\n\t\t\t\t\t\t\tcontinue;\t//look for a live one...\n\t\t\t\t\t\tif (Net_CompareAddress(&t->remotes[i].remoteaddr, &peeraddr[p], 0,1))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tt->remotes[i].timeout = cluster->curtime + 5*60*1000;\t//bump it.\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif (i == countof(t->remotes))\n\t\t\t\t\t{\n\t\t\t\t\t\tfor (i = 0; i < countof(t->remotes); i++)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif ((signed int)(t->remotes[i].timeout-cluster->curtime) < 0)\n\t\t\t\t\t\t\t{\t//this one's dead...\n\t\t\t\t\t\t\t\tt->remotes[i].remoteaddr = peeraddr[p];\n\t\t\t\t\t\t\t\tt->remotes[i].timeout = cluster->curtime + 5*60*1000;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (i == countof(t->remotes))\n\t\t\t\t\t\t\terror = 508;\t//'Insufficient Capacity'\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (error)\n\t\t\tWriteBigShort(&o, STUN_ERROR|hdr.msgtype);\n\t\telse\n\t\t\tWriteBigShort(&o, STUN_REPLY|hdr.msgtype);\n\t\tWriteBigShort(&o, 0);\n\t\tWriteBigLong(&o, hdr.magiccookie);\n\t\tWriteBigLong(&o, hdr.transactid[0]);\n\t\tWriteBigLong(&o, hdr.transactid[1]);\n\t\tWriteBigLong(&o, hdr.transactid[2]);\n\t\tif (error)\n\t\t{\n\t\t\t//WriteBigShort(STUNATTR_SOFTWARE);\n\t\t\tWriteBigShort(&o, STUNATTR_ERROR_CODE);\n\t\t\tWriteBigShort(&o, 4);\n\t\t\tWriteBigLong(&o, ((error/100)<<8) | (error%100));\t//this is stupid.\n\t\t\tif (error == 420)\n\t\t\t\t;//WriteBigShort(STUNATTR_UNKNOWN_ATTRIBUTES);\n\n\t\t\tif ((error == 401 && !innonce) || error == 438)\n\t\t\t{\n\t\t\t\tchar *realm = \"fteqtv\";\n\t\t\t\tsize_t sz = TURN_GenerateNonce(from, noncebuf, sizeof(noncebuf));\n\t\t\t\tWriteBigShort(&o, STUNATTR_NONCE);\n\t\t\t\tWriteBigShort(&o, sz);\n\t\t\t\tWriteData(&o, noncebuf, sz);\n\t\t\t\twhile (o.cursize&3)\n\t\t\t\t\tWriteByte(&o, 0);\n\n\t\t\t\tsz = strlen(realm);\n\t\t\t\tWriteBigShort(&o, STUNATTR_REALM);\n\t\t\t\tWriteBigShort(&o, sz);\n\t\t\t\tWriteData(&o, realm, sz);\n\t\t\t\twhile (o.cursize&3)\n\t\t\t\t\tWriteByte(&o, 0);\n\t\t\t}\n\t\t}\n\t\telse if (t)\n\t\t{\n\t\t\tif (hdr.msgtype == STUN_ALLOCATE || hdr.msgtype == STUN_REFRESH)\n\t\t\t{\n\t\t\t\tif (lifetime > 5*60*1000)\n\t\t\t\t\tlifetime = 5*60*1000;\n\t\t\t\tif (lifetime && t->timeout < lifetime)\n\t\t\t\t\tt->timeout = lifetime;\n\t\t\t}\n\t\t\tlifetime = t->timeout;\n\t\t\t//WriteBigShort(STUNATTR_SOFTWARE);\n\t\t\tif (hdr.msgtype == STUN_ALLOCATE || hdr.msgtype == STUN_REFRESH)\n\t\t\t{\n\t\t\t\tWriteBigShort(&o, STUNATTR_LIFETIME);\n\t\t\t\tWriteBigShort(&o, 4);\n\t\t\t\tWriteBigLong(&o, lifetime/1000);\n\t\t\t}\n\t\t\tif (hdr.msgtype == STUN_ALLOCATE)\n\t\t\t{\n\t\t\t\tTURN_AddXorAddress(&o, STUNATTR_XOR_RELAYED_ADDRESS, &t->relayaddr);\n\t\t\t\tTURN_AddXorAddress(&o, STUNATTR_XOR_MAPPED_ADDRESS, from);\n\t\t\t}\n\t\t\tif (hdr.msgtype == STUN_ALLOCATE || hdr.msgtype == STUN_REFRESH || hdr.msgtype == STUN_CREATEPERM)\n\t\t\t\tTURN_AddIntegrity(cluster, &o, t);\n\t\t}\n\t\telse\n\t\t\treturn true;\t//wut?\n\n\t\tTURN_Send(cluster, &o, from);\n\t\treturn true;\n\t}\n\telse if (hdr.msgtype == (STUN_INDICATION|STUN_SEND))\n\t{\n\t\t//just sending to the other end\n\t\tif (t && inpeers == 1)\n\t\t{\n\t\t\tint perm = TURN_FindPermission(cluster, t, &peeraddr[0]); //sends do NOT refresh.\n\t\t\tif (perm >= 0)\n\t\t\t{\n#ifdef TURN_IS_NOT_BLIND\n\t\t\t\tif (!t->remotes[perm].seenstun && !TURN_PacketIsStun(data, datasize))\n\t\t\t\t\t;\t//don't relay non-stun packets until we know the peer is running a stun server too.\n\t\t\t\telse if (TURN_PacketIsBanned(data, datasize))\n\t\t\t\t\t;\n\t\t\t\telse\n#endif\n\t\t\t\t\tNET_SendPacket(cluster, t->remotesock, datasize, data, peeraddr[0]);\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\telse\n\t\treturn false;\t//dunno what that rubbish is.\n}\n\nvoid Fwd_NewQWFwd(cluster_t *cluster, netadr_t *from, char *targ)\n{\n\tchar *rechallenge = \"\\xff\\xff\\xff\\xffgetchallenge\\n\";\n\tstruct turnclient_s *t;\n\tnetadr_t adr;\n\tchar *at = strrchr(targ, '@');\n\tif (at)\n\t\ttarg = at+1;\t//we only care about the next hop here, not the full route.\n\n\tif (NET_StringToAddr(targ, &adr, 27500))\n\t{\n\t\tif (!cluster->relayenabled)\n\t\t{\n\t\t\tNetchan_OutOfBandPrint(cluster, *from, \"n\" \"Relay not enabled.\\n\");\n\t\t\treturn;\t//don't allow it.\n\t\t}\n\t\telse if (!TURN_Permissable(&adr))\n\t\t{\t//don't route to 127.* or 192.168.* etc.\n\t\t\tNetchan_OutOfBandPrint(cluster, *from, \"n\" \"Target address is private\\n\");\n\t\t\treturn;\n\t\t}\n\t\telse\n\t\t\tt = TURN_Allocate(cluster, from, (((struct sockaddr*)adr.sockaddr)->sa_family==AF_INET6)?2:1, \"fwd\", NULL);\n\t\tif (t)\n\t\t{\n\t\t\tt->remotes[0].remoteaddr = adr;\n\t\t\tt->remotes[0].timeout = cluster->curtime + QWFWDTIMEOUT;\n\n//Netchan_OutOfBandPrint(cluster, *from, \"n\" \"relay established\\n\");\n\n\t\t\t//send a quick challenge to the remote as if it came from the client. the client can then deal with it when it comes back, instead of waiting for the client's next getchallenge timeout.\n\t\t\tNET_SendPacket(cluster, t->remotesock, strlen(rechallenge), rechallenge, t->remotes[0].remoteaddr);\n\n\t\t\treturn;\n\t\t}\n\t\telse\n\t\t\tNetchan_OutOfBandPrint(cluster, *from, \"n\" \"Unable to set up relay\\n\");\t//ran out of FDs?\n\t}\n\telse\n\t\tNetchan_OutOfBandPrint(cluster, *from, \"n\" \"Unanle to resolve target address: %s\\n\", targ);\n}\n\nstruct relaypeer_s\n{\n\tnetadr_t adr;\n\tunsigned int lastalive;\t//last time it was reported by a master (removed after timeout)\n\tunsigned int lastpong;\t//when we last saw a response from them\n\tunsigned int lastping;\t//timestamp we last sent a ping\n\tunsigned int curping;\t//ping time from last pong (or PING_UNRESPONSIVE)\n#define PING_UNRESPONSIVE (~0u)\n\tstruct relaypeer_s *next;\n};\nstatic void Fwd_AddPeer(cluster_t *cluster, netadr_t *a)\n{\n\tstruct relaypeer_s *rp;\n\t//this loop could be improved with a hash table.\n\tfor (rp = cluster->relaypeer; rp; rp = rp->next)\n\t{\n\t\tif (Net_CompareAddress(&rp->adr, a, 0, 1))\n\t\t\tbreak;\n\t}\n\n\tif (!rp)\n\t{\t//add it...\n\t\tif (!TURN_Permissable(a))\n\t\t\treturn;\t//unless its a private address.\n\t\trp = malloc(sizeof(*rp));\n\t\trp->adr = *a;\n\t\trp->lastpong = cluster->curtime;\n\t\trp->lastping = cluster->curtime+0x80000000;\t//something that's expired.\n\t\trp->curping = PING_UNRESPONSIVE;\t//don't know yet.\n\t\trp->next = cluster->relaypeer;\n\t\tcluster->relaypeer = rp;\n\n\t\tcluster->numpeers += 1;\n\t}\n\n\trp->lastalive = cluster->curtime;\t//mark as still alive.\n}\nvoid Fwd_ParseServerList(cluster_t *cluster, netmsg_t *m, int af)\n{\t//response from master\n\tnetadr_t a;\n\tint j;\n\tchar t;\n\twhile(m->readpos < m->cursize)\n\t{\n\t\tif (af == AF_INET)\n\t\t\tt = '\\\\';\n\t\telse if (af == AF_INET6)\n\t\t\tt = '/';\n\t\telse\n\t\t\tt = ReadByte(m);\t//fancy protocol that has leading ids\n\n\t\tif (t == '\\\\')\n\t\t{\t//ipv4\n\t\t\tstruct sockaddr_in *o = (struct sockaddr_in *)a.sockaddr;\n\t\t\tif (m->readpos+6 > m->cursize)\n\t\t\t\tbreak;\t//eof?\n\t\t\tmemset(&a, 0, sizeof(a));\n\t\t\to->sin_family = AF_INET;\n\t\t\tfor (j = 0; j < 4; j++)\n\t\t\t\t((char*)&o->sin_addr)[j] = ReadByte(m);\n\t\t\t((char*)&o->sin_port)[0] = ReadByte(m);\n\t\t\t((char*)&o->sin_port)[1] = ReadByte(m);\n\t\t}\n\t\telse if (t == '/')\n\t\t{\t//ipv6\n\t\t\tstruct sockaddr_in6 *o = (struct sockaddr_in6 *)a.sockaddr;\n\t\t\tif (m->readpos+18 > m->cursize)\n\t\t\t\tbreak;\t//eof?\n\t\t\tmemset(&a, 0, sizeof(a));\n\t\t\to->sin6_family = AF_INET6;\n\t\t\tfor (j = 0; j < 16; j++)\n\t\t\t\t((char*)&o->sin6_addr)[j] = ReadByte(m);\n\t\t\t((char*)&o->sin6_port)[0] = ReadByte(m);\n\t\t\t((char*)&o->sin6_port)[1] = ReadByte(m);\n\n\t\t\tcontinue;\t//pingstatus does not support ipv6, so don't bother pinging any.\n\t\t}\n\t\telse\n\t\t\tbreak;\t//erk?\n\t\tFwd_AddPeer(cluster, &a);\n\t}\n}\nvoid Fwd_PingResponse(cluster_t *cluster, netadr_t *from)\n{\n\tstruct relaypeer_s *rp;\n\t//this loop could be improved with a hash table.\n\tfor (rp = cluster->relaypeer; rp; rp = rp->next)\n\t{\n\t\tif (Net_CompareAddress(&rp->adr, from, 0, 1))\n\t\t{\n\t\t\t//we have no way to verify the peer.\n\t\t\t//its probably better to allow an attacker to force a large ping rather than a low one. its more obvious.\n\t\t\t//the real target should respond eventually giving an upper bound for the ping value.\n\t\t\t//don't extend aliveness though, if a peer stops heartbeating then we just let it hide. only the master responses may extend its lifetime.\n\t\t\trp->curping = cluster->curtime - rp->lastping;\n\t\t\trp->lastpong = cluster->curtime;\n\t\t\treturn;\n\t\t}\n\t}\n}\nstatic void Fwd_DoPing(cluster_t *cluster)\n{\n\tstruct relaypeer_s *rp, **l;\n\tif (!cluster->pingtreeenabled || !cluster->relayenabled)\n\t\treturn;\t//don't spam at all.\n\tif (cluster->curtime-cluster->relay_lastping < QRY_PINGINTERVAL)\n\t\treturn; //don't burst\n\tcluster->relay_lastping = cluster->curtime;\n\n\tif (cluster->curtime-cluster->relay_lastquery >= QRY_SERVERLISTINTERVAL)\n\t{\n\t\tnetadr_t adr;\n\t\tcluster->relay_lastquery = cluster->curtime;\n\t\tif (NET_StringToAddr(cluster->master, &adr, 27950))\n\t\t{\n\t\t\tif (((struct sockaddr_in *)adr.sockaddr)->sin_family == AF_INET &&\n\t\t\t\t((struct sockaddr_in *)adr.sockaddr)->sin_port == htons(27000))\n\t\t\t{\n\t\t\t\tnetadr_t realadr;\n\t\t\t\tNET_SendPacket (cluster, NET_ChooseSocket(cluster->qwdsocket, &realadr, adr), 2, \"c\\n\", realadr); //legacy. screwy. no 0xff prefix.\n\t\t\t}\n\t\t\telse\n\t\t\t\tNetchan_OutOfBandPrint(cluster, adr, \"getserversExt %s %u empty full\"/*\" ipv6\"*/, cluster->protocolname, cluster->protocolver);\n\t\t}\n\t\treturn;\t//space it out with pings.\n\t}\n\n\tfor (l = &cluster->relaypeer; (rp=*l);)\n\t{\n\t\tif (cluster->curtime-rp->lastping >= QRY_REPINGINTERVAL)\t//its been long enough since this one was pinged...\n\t\t{\n\t\t\tif (cluster->curtime - rp->lastalive > QRY_TIMEOUT)\n\t\t\t{\t//its been too long. cut our losses. its dead jim.\n\t\t\t\t*l = rp->next;\n\t\t\t\tfree(rp);\n\t\t\t\tcluster->numpeers -= 1;\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t//okay, we're pinging this one.\n\t\t\trp->lastping = cluster->curtime;\n\t\t\tNetchan_OutOfBandPrint(cluster, rp->adr, \"k\");\t//tiny unrealistic ping...\n\n\t\t\t//unlink it... walk em till the end, and then insert there. if there's 10000 servers then it'll just ping slower... much slower... and fail to report them all in a single udp packet, but hey.\n\t\t\tfor (*l = rp->next; *l; l = &(*l)->next)\n\t\t\t\t;\n\t\t\t*l = rp;\n\t\t\trp->next = NULL;\n\n\t\t\treturn;\t//only ping one.\n\t\t}\n\t\tl = &rp->next;\n\t}\n}\nvoid Fwd_PingStatus(cluster_t *cluster, netadr_t *from, qboolean ext)\n{\n\tstruct relaypeer_s *rp;\n\tchar buffer[8192];\n\tnetmsg_t send;\n\tnetadr_t fixup;\n\tint j;\n\tint resetsize;\n\n\tif (!cluster->pingtreeenabled || !cluster->relayenabled)\n\t\treturn;\t//don't bother responding.\n\n\tInitNetMsg (&send, buffer, sizeof(buffer));\n\n\t//small prefix...\n\tWriteLong (&send, -1);\t// -1 sequence means out of band\n\tif (ext)\n\t\tWriteString2 (&send, \"pinglist\");\n\telse\n\t\tWriteByte (&send, 'n');\t// 'a2c_print'... yeah, this is fucked.\n\n\tresetsize = send.cursize;\n\n\tfor (rp = cluster->relaypeer; rp; rp = rp->next)\n\t{\n\t\tif (rp->lastping > 0x7fff)\n\t\t\tcontinue;\t//don't report dead ones. don't do negatives either!\n\t\tif (((struct sockaddr*)rp->adr.sockaddr)->sa_family == AF_INET)\n\t\t{\n\t\t\tstruct sockaddr_in *a = ((struct sockaddr_in*)rp->adr.sockaddr);\n\t\t\tif (send.cursize > 1400)\n\t\t\t{\n\t\t\t\tNET_SendPacket (cluster, NET_ChooseSocket(cluster->qwdsocket, &fixup, *from), send.cursize, send.data, fixup);\n\t\t\t\tsend.cursize = resetsize;\n\t\t\t}\n\n\t\t\tif (ext)\n\t\t\t\tWriteByte (&send, '\\\\');\n\t\t\tfor (j = 0; j < 4; j++)\n\t\t\t\tWriteByte (&send, ((char*)&a->sin_addr)[j]);\n\t\t\tWriteShort(&send, ntohs(a->sin_port));\n\t\t\tWriteShort(&send, rp->curping);\n\t\t}\n\t\telse if (((struct sockaddr*)rp->adr.sockaddr)->sa_family == AF_INET6)\n\t\t{\n\t\t\tstruct sockaddr_in6 *a = ((struct sockaddr_in6*)rp->adr.sockaddr);\n\t\t\tif (send.cursize > 1400)\n\t\t\t{\n\t\t\t\tNET_SendPacket (cluster, NET_ChooseSocket(cluster->qwdsocket, &fixup, *from), send.cursize, send.data, fixup);\n\t\t\t\tsend.cursize = resetsize;\n\t\t\t}\n\n\t\t\tif (ext)\n\t\t\t\tWriteByte (&send, '/');\n\t\t\telse\n\t\t\t\tcontinue;\n\t\t\tfor (j = 0; j < 16; j++)\n\t\t\t\tWriteByte (&send, ((char*)&a->sin6_addr)[j]);\n\t\t\tWriteShort(&send, ntohs(a->sin6_port));\n\t\t\tWriteShort(&send, rp->curping);\n\t\t}\n\t}\n\n// send the datagram\n\tNET_SendPacket (cluster, NET_ChooseSocket(cluster->qwdsocket, &fixup, *from), send.cursize, send.data, fixup);\n}\n\nvoid TURN_RelayStatus(cmdctxt_t *ctx)\n{\n\tcluster_t *cluster = ctx->cluster;\n\tunsigned int live=0, unreach=0, dead=0;\n\tstruct relaypeer_s *rp;\n\tunsigned int turns=0, fwds=0;\n\tturnclient_t *t;\n\tfor (t = cluster->turns; t; t = t->next)\n\t{\n\t\tif (t->isfwd)\n\t\t\tfwds++;\n\t\telse\n\t\t\tturns++;\n\t}\n\tfor (rp = cluster->relaypeer; rp; rp = rp->next)\n\t{\n\t\tif (rp->curping == PING_UNRESPONSIVE)\n\t\t\tunreach++;\t//never got a response.\n\t\telse if (cluster->curtime-rp->lastpong > 125*1000)\n\t\t\tdead++;\t//no response in the last 2 mins\n\t\telse\n\t\t\tlive++;\n\t}\n\n\tCmd_Printf(ctx, \" %i relays (%i TURN%s, %i fwd%s)\\n\", cluster->numrelays, turns, cluster->turnenabled?\"\":\"[OFF]\", fwds, cluster->relayenabled?\"\":\"[OFF]\");\n\tif (ctx->cluster->turnenabled)\n\t\tCmd_Printf(ctx, \" relaying through %i.%i.%i.%i %i-%i\\n\", ctx->cluster->turn_ipv4[0],ctx->cluster->turn_ipv4[1],ctx->cluster->turn_ipv4[2],ctx->cluster->turn_ipv4[3], ctx->cluster->turn_minport, ctx->cluster->turn_maxport);\n\telse if (cluster->relayenabled)\n\t\tCmd_Printf(ctx, \" relaying through %i.%i.%i.%i\\n\", ctx->cluster->turn_ipv4[0],ctx->cluster->turn_ipv4[1],ctx->cluster->turn_ipv4[2],ctx->cluster->turn_ipv4[3]);\n\tif (cluster->relayenabled)\n\t{\n\t\tif (cluster->pingtreeenabled)\n\t\t\tCmd_Printf(ctx, \" %i peers (%i live, %i stale, %i unreach)\\n\", cluster->numpeers, live, dead, unreach);\n\t\telse\n\t\t\tCmd_Printf(ctx, \" pinging disabled\\n\");\n\t}\n}"
  },
  {
    "path": "fteqtv/sc_dsound.c",
    "content": "#include \"qtv.h\"\r\n\r\n#ifdef COMMENTARY\r\n#include <dsound.h>\r\n\r\nstatic HANDLE hInstDS;\r\nstatic HRESULT (WINAPI *pDirectSoundCaptureCreate)(GUID FAR *lpGUID, LPDIRECTSOUNDCAPTURE FAR *lplpDS, IUnknown FAR *pUnkOuter);\r\n\r\ntypedef struct {\r\n\tsoundcapt_t funcs;\r\n\r\n\tLPDIRECTSOUNDCAPTURE DSCapture;\r\n\tLPDIRECTSOUNDCAPTUREBUFFER DSCaptureBuffer;\r\n\tlong lastreadpos;\r\n\r\n\tWAVEFORMATEX  wfxFormat;\r\n\r\n\tlong bufferbytes;\r\n} dscapture_t;\r\n\r\n\r\nvoid DSOUND_CloseCapture(soundcapt_t *ghnd)\r\n{\r\n\tdscapture_t *hnd = (dscapture_t*)ghnd;\r\n\r\n\tif (hnd->DSCaptureBuffer)\r\n\t{\r\n\t\tIDirectSoundCaptureBuffer_Stop(hnd->DSCaptureBuffer);\r\n\t\tIDirectSoundCaptureBuffer_Release(hnd->DSCaptureBuffer);\r\n\t\thnd->DSCaptureBuffer=NULL;\r\n\t}\r\n\tif (hnd->DSCapture)\r\n\t{\r\n\t\tIDirectSoundCapture_Release(hnd->DSCapture);\r\n\t\thnd->DSCapture=NULL;\r\n\t}\r\n\r\n\tfree(hnd);\r\n}\r\n\r\nint DSOUND_UpdateCapture(soundcapt_t *ghnd, int samplechunks, char *buffer)\r\n{\r\n\tdscapture_t *hnd = (dscapture_t*)ghnd;\r\n\tHRESULT hr;\r\n\tLPBYTE lpbuf1 = NULL;\r\n\tLPBYTE lpbuf2 = NULL;\r\n\tDWORD dwsize1 = 0;\r\n\tDWORD dwsize2 = 0;\r\n\r\n\tDWORD capturePos;\r\n\tDWORD readPos;\r\n\tlong  filled;\r\n\r\n\tint inputbytes;\r\n\r\n\tinputbytes = hnd->wfxFormat.wBitsPerSample/8;\r\n\r\n\r\n// Query to see how much data is in buffer.\r\n\thr = IDirectSoundCaptureBuffer_GetCurrentPosition(hnd->DSCaptureBuffer, &capturePos, &readPos );\r\n\tif( hr != DS_OK )\r\n\t{\r\n\t\treturn 0;\r\n\t}\r\n\tfilled = readPos - hnd->lastreadpos;\r\n\tif( filled < 0 )\r\n\t\tfilled += hnd->bufferbytes; // unwrap offset\r\n\r\n\tif (filled > samplechunks)\t//figure out how much we need to empty it by, and if that's enough to be worthwhile.\r\n\t\tfilled = samplechunks;\r\n\telse if (filled < samplechunks)\r\n\t\treturn 0;\r\n\r\n\tif ((filled/inputbytes) & 1)\t//force even numbers of samples\r\n\t\tfilled -= inputbytes;\r\n\r\n\t// Lock free space in the DS\r\n\thr = IDirectSoundCaptureBuffer_Lock ( hnd->DSCaptureBuffer, hnd->lastreadpos, filled, (void **) &lpbuf1, &dwsize1,\r\n\t\t(void **) &lpbuf2, &dwsize2, 0);\r\n\tif (hr == DS_OK)\r\n\t{\r\n\t\t// Copy from DS to the buffer\r\n\t\tmemcpy( buffer, lpbuf1, dwsize1);\r\n\t\tif(lpbuf2 != NULL)\r\n\t\t{\r\n\t\t\tmemcpy( buffer+dwsize1, lpbuf2, dwsize2);\r\n\t\t}\r\n\t\t// Update our buffer offset and unlock sound buffer\r\n \t\thnd->lastreadpos = (hnd->lastreadpos + dwsize1 + dwsize2) % (hnd->bufferbytes);\r\n\t\tIDirectSoundCaptureBuffer_Unlock ( hnd->DSCaptureBuffer, lpbuf1, dwsize1, lpbuf2, dwsize2);\r\n\t}\r\n\telse\r\n\t{\r\n\t\treturn 0;\r\n\t}\r\n\treturn filled/inputbytes;\r\n}\r\n\r\n\r\nsoundcapt_t *SND_InitCapture (int speed, int bits)\r\n{\r\n\tdscapture_t *hnd;\r\n\tDSCBUFFERDESC bufdesc;\r\n\r\n\tif (!hInstDS)\r\n\t{\r\n\t\thInstDS = LoadLibrary(\"dsound.dll\");\r\n\t\t\r\n\t\tif (hInstDS == NULL)\r\n\t\t{\r\n\t\t\tprintf (\"Couldn't load dsound.dll\\n\");\r\n\t\t\treturn NULL;\r\n\t\t}\r\n\t}\r\n\tif (!pDirectSoundCaptureCreate)\r\n\t{\r\n\t\tpDirectSoundCaptureCreate = (void *)GetProcAddress(hInstDS,\"DirectSoundCaptureCreate\");\r\n\r\n\t\tif (!pDirectSoundCaptureCreate)\r\n\t\t{\r\n\t\t\tprintf (\"Couldn't get DS proc addr (DirectSoundCaptureCreate)\\n\");\r\n\t\t\treturn NULL;\r\n\t\t}\r\n\r\n\t}\r\n\r\n\r\n\thnd = malloc(sizeof(dscapture_t));\r\n\tmemset(hnd, 0, sizeof(*hnd));\r\n\r\n\thnd->wfxFormat.wFormatTag = WAVE_FORMAT_PCM;\r\n    hnd->wfxFormat.nChannels = 1;\r\n    hnd->wfxFormat.nSamplesPerSec = speed;\r\n\thnd->wfxFormat.wBitsPerSample = bits;\r\n    hnd->wfxFormat.nBlockAlign = hnd->wfxFormat.nChannels * (hnd->wfxFormat.wBitsPerSample / 8);\r\n\thnd->wfxFormat.nAvgBytesPerSec = hnd->wfxFormat.nSamplesPerSec * hnd->wfxFormat.nBlockAlign;\r\n    hnd->wfxFormat.cbSize = 0;\r\n\r\n\tbufdesc.dwSize = sizeof(bufdesc);\r\n\tbufdesc.dwBufferBytes = hnd->bufferbytes = speed*bits/8;\t//1 sec\r\n\tbufdesc.dwFlags = 0;\r\n\tbufdesc.dwReserved = 0;\r\n\tbufdesc.lpwfxFormat = &hnd->wfxFormat;\r\n\r\n\r\n\r\n\r\n\tpDirectSoundCaptureCreate(NULL, &hnd->DSCapture, NULL);\r\n\r\n\tif (FAILED(IDirectSoundCapture_CreateCaptureBuffer(hnd->DSCapture, &bufdesc, &hnd->DSCaptureBuffer, NULL)))\r\n\t{\r\n\t\tprintf (\"Couldn't create a capture buffer\\n\");\r\n\t\tIDirectSoundCapture_Release(hnd->DSCapture);\r\n\t\tfree(hnd);\r\n\t\treturn NULL;\r\n\t}\r\n\r\n\tIDirectSoundCaptureBuffer_Start(hnd->DSCaptureBuffer, DSBPLAY_LOOPING);\r\n\r\n\thnd->lastreadpos = 0;\r\n\r\n\thnd->funcs.update = DSOUND_UpdateCapture;\r\n\thnd->funcs.close = DSOUND_CloseCapture;\r\n\r\n\treturn &hnd->funcs;\r\n}\r\n\r\n\r\n\r\n\r\n/*\r\nvoid soundtestcallback (char *buffer, int samples, int bitspersample)\r\n{\r\n\tFILE *f;\r\n\tf = fopen(\"c:/test.raw\", \"at\");\r\n\tfseek(f, 0, SEEK_END);\r\n\tfwrite(buffer, samples, bitspersample/8, f);\r\n\tfclose(f);\r\n}\r\n\r\nvoid soundtest(void)\r\n{\r\n\tsoundcapt_t *capt;\r\n\r\n\tcapt = SNDDMA_InitCapture(11025, 8);\r\n\twhile(1)\r\n\t\tcapt->update(capt, 1400, soundtestcallback);\r\n\tcapt->close(capt);\r\n}\r\n*/\r\n#endif\r\n"
  },
  {
    "path": "fteqtv/source.c",
    "content": "/*\nCopyright (C) 1996-1997 Id Software, Inc.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\nSee the included (GNU.txt) GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n*/\n\n\n//connection notes\n//The connection is like http.\n//The stream starts with a small header.\n//The header is a list of 'key: value' pairs, separated by new lines.\n//The header ends with a totally blank line.\n//to record an mvd from telnet or somesuch, you would use:\n//\"QTV\\nRAW: 1\\n\\n\"\n\n//VERSION: a list of the different qtv protocols supported. Multiple versions can be specified. The first is assumed to be the preferred version.\n//RAW: if non-zero, send only a raw mvd with no additional markup anywhere (for telnet use). Doesn't work with challenge-based auth, so will only be accepted when proxy passwords are not required.\n//AUTH: specifies an auth method, the exact specs varies based on the method\n//\t\tPLAIN: the password is sent as a PASSWORD line\n//\t\tMD4: the server responds with an \"AUTH: MD4\\n\" line as well as a \"CHALLENGE: somerandomchallengestring\\n\" line, the client sends a new 'initial' request with CHALLENGE: MD4\\nRESPONSE: hexbasedmd4checksumhere\\n\"\n//\t\tetc: same idea as md4\n//\t\tCCITT: same as md4, but using the CRC stuff common to all quake engines. should not be used.\n//\t\tif the supported/allowed auth methods don't match, the connection is silently dropped.\n//SOURCE: which stream to play from, DEFAULT is special. Without qualifiers, it's assumed to be a tcp address.\n//COMPRESSION: Suggests a compression method (multiple are allowed). You'll get a COMPRESSION response, and compression will begin with the binary data.\n//SOURCELIST: Asks for a list of active sources from the proxy.\n//DEMOLIST:\tAsks for a list of available mvd demos.\n\n//Response:\n//if using RAW, there will be no header or anything\n//Otherwise you'll get a QTVSV %f response (%f being the protocol version being used)\n//same structure, terminated by a \\n\n//AUTH: Server requires auth before proceeding. If you don't support the method the server says, then, urm, the server shouldn't have suggested it.\n//CHALLENGE: used with auth\n//COMPRESSION: Method of compression used. Compression begins with the raw data after the connection process.\n//ASOURCE: names a source\n//ADEMO: gives a demo file name\n\n\n\n\n#include \"qtv.h\"\n#include <string.h>\n\n#include \"bsd_string.h\"\n\n#ifndef _WIN32\n#include <dirent.h>\n#include <signal.h>\n#endif\n\n#include <stddef.h>\n#ifdef UNIXSOCKETS\n#include <sys/un.h>\n#endif\n\n#define RECONNECT_TIME (1000*30)\n#define RECONNECT_TIME_DEMO (1000*5)\n#define UDPRECONNECT_TIME (1000)\n#define STATUSPOLL_TIME 1000*30\n#define PINGSINTERVAL_TIME (1000*5)\n#define UDPTIMEOUT_LENGTH (1000*20)\n#define UDPPACKETINTERVAL (1000/72)\n\nvoid Net_SendConnectionMVD(sv_t *qtv, oproxy_t *prox);\nvoid Net_QueueUpstream(sv_t *qtv, int size, char *buffer);\n\n\nqboolean\tNET_StringToAddr (char *s, netadr_t *sadr, int defaultport)\n{\n\tstruct hostent\t*h;\n\tchar\t*colon;\n\tchar\tcopy[128];\n\n\tmemset (sadr, 0, sizeof(netadr_t));\n\n#ifdef USEIPX\n\tif ((strlen(s) >= 23) && (s[8] == ':') && (s[21] == ':'))\t// check for an IPX address\n\t{\n\t\tunsigned int val;\n\n\t\t((struct sockaddr_ipx *)sadr)->sa_family = AF_IPX;\n\t\tcopy[2] = 0;\n\t\tDO(0, sa_netnum[0]);\n\t\tDO(2, sa_netnum[1]);\n\t\tDO(4, sa_netnum[2]);\n\t\tDO(6, sa_netnum[3]);\n\t\tDO(9, sa_nodenum[0]);\n\t\tDO(11, sa_nodenum[1]);\n\t\tDO(13, sa_nodenum[2]);\n\t\tDO(15, sa_nodenum[3]);\n\t\tDO(17, sa_nodenum[4]);\n\t\tDO(19, sa_nodenum[5]);\n\t\tsscanf (&s[22], \"%u\", &val);\n\t\t((struct sockaddr_ipx *)sadr)->sa_socket = htons((unsigned short)val);\n\t}\n\telse\n#endif\n#ifndef _WIN32\n\t\tif (1)\n\t{//ipv6 method (can return ipv4 addresses too)\n\t\tstruct addrinfo *addrinfo, *pos;\n\t\tstruct addrinfo udp6hint;\n\t\tint error;\n\t\tchar *port;\n\t\tchar dupbase[256];\n\t\tint len;\n\n\t\tmemset(&udp6hint, 0, sizeof(udp6hint));\n\t\tudp6hint.ai_family = 0;//Any... we check for AF_INET6 or 4\n\t\tudp6hint.ai_socktype = SOCK_DGRAM;\n\t\tudp6hint.ai_protocol = IPPROTO_UDP;\n\n\t\tif (*s == '[')\n\t\t{\n\t\t\ts++;\n\t\t\tcolon = strchr(s, ']');\n\t\t\tif (!colon || colon-s >= sizeof(copy))\n\t\t\t\treturn false;\t//too long to handle.\n\t\t\tmemcpy(copy, s, colon-s);\n\t\t\tcopy[colon-s] = 0;\n\t\t\tcolon++;\n\t\t\tif (*colon == ':')\n\t\t\t\tport = colon;\n\t\t\telse\n\t\t\t\tport = NULL;\n\t\t\ts = copy;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tport = s + strlen(s);\n\t\t\twhile(port >= s)\n\t\t\t{\n\t\t\t\tif (*port == ':')\n\t\t\t\tbreak;\n\t\t\t\tport--;\n\t\t\t}\n\t\t}\n\n\t\tif (port == s)\n\t\t\tport = NULL;\n\t\tif (port)\n\t\t{\n\t\t\tlen = port - s;\n\t\t\tif (len > sizeof(dupbase)-1)\n\t\t\t\tlen = sizeof(dupbase)-1;\n\t\t\tmemcpy(dupbase, s, len);\n\t\t\tdupbase[len] = 0;\n\t\t\terror = getaddrinfo(dupbase, port+1, &udp6hint, &addrinfo);\n\t\t}\n\t\telse\n\t\t\terror = EAI_NONAME, addrinfo=NULL;\n\t\tif (error)\t//failed, try string with no port.\n\t\t\terror = getaddrinfo(s, NULL, &udp6hint, &addrinfo);\t//remember, this func will return any address family that could be using the udp protocol... (ip4 or ip6)\n\t\tif (error)\n\t\t{\n\t\t\treturn false;\n\t\t}\n\t\t((struct sockaddr*)sadr->sockaddr)->sa_family = 0;\n\t\tfor (pos = addrinfo; pos; pos = pos->ai_next)\n\t\t{\n\t\t\tswitch(pos->ai_family)\n\t\t\t{\n\t\t\tcase AF_INET6:\n\t\t\t\tif (((struct sockaddr_in *)sadr->sockaddr)->sin_family == AF_INET6)\n\t\t\t\t\tbreak;\t//first one should be best...\n\t\t\t\t//fallthrough\n\t\t\tcase AF_INET:\n\t\t\t\tmemcpy(sadr->sockaddr, addrinfo->ai_addr, addrinfo->ai_addrlen);\n\t\t\t\tif (pos->ai_family == AF_INET)\n\t\t\t\t\tgoto dblbreak;\t//don't try finding any more, this is quake, they probably prefer ip4...\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\ndblbreak:\n\t\tpfreeaddrinfo (addrinfo);\n\t\tif (!((struct sockaddr*)sadr->sockaddr)->sa_family)\t//none suitablefound\n\t\t\treturn false;\n\t}\n\telse\n#endif\n\t{ //old fashioned method\n\t\tstruct sockaddr_in *sin = (struct sockaddr_in *)sadr->sockaddr;\n\t\tsin->sin_family = AF_INET;\n\n\t\tsin->sin_port = htons(defaultport);\n\n\t\tstrcpy (copy, s);\n\t\t// strip off a trailing :port if present\n\t\tfor (colon = copy ; *colon ; colon++)\n\t\t\tif (*colon == ':')\n\t\t\t{\n\t\t\t\t*colon = 0;\n\t\t\t\tsin->sin_port = htons((short)atoi(colon+1));\n\t\t\t}\n\n\t\tif (copy[0] >= '0' && copy[0] <= '9')\t//this is the wrong way to test. a server name may start with a number.\n\t\t{\n\t\t\t*(int *)&sin->sin_addr = inet_addr(copy);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (! (h = gethostbyname(copy)) )\n\t\t\t\treturn 0;\n\t\t\tif (h->h_addrtype != AF_INET)\n\t\t\t\treturn 0;\n\t\t\t*(int *)&sin->sin_addr = *(int *)h->h_addr_list[0];\n\t\t}\n\t}\n\n\treturn true;\n}\n\nqboolean Net_CompareAddress(netadr_t *s1, netadr_t *s2, int qp1, int qp2)\n{\n\tstruct sockaddr *g1=(void*)s1->sockaddr, *g2=(void*)s2->sockaddr;\n\tif (g1->sa_family != g2->sa_family)\n\t{\t//urgh...\n\t\tif (g1->sa_family == AF_INET6 && g2->sa_family == AF_INET && (\n\t\t\t((unsigned int*)&((struct sockaddr_in6 *)g1)->sin6_addr)[0] == 0 &&\n\t\t\t((unsigned int*)&((struct sockaddr_in6 *)g1)->sin6_addr)[1] == 0 &&\n\t\t\t((unsigned short*)&((struct sockaddr_in6 *)g1)->sin6_addr)[4] == 0 &&\n\t\t\t((unsigned short*)&((struct sockaddr_in6 *)g1)->sin6_addr)[5] == 0xffff))\n\t\t{\n\t\t\tstruct sockaddr_in6 *i1=(void*)s1->sockaddr;\n\t\t\tstruct sockaddr_in *i2=(void*)s2->sockaddr;\n\t\t\tif (((unsigned int*)&i1->sin6_addr)[3] != *(unsigned int*)&i2->sin_addr)\n\t\t\t\treturn false;\n\t\t\tif (i1->sin6_port != i2->sin_port && qp1 != qp2)\t//allow qports to match instead of ports, if required.\n\t\t\t\treturn false;\n\t\t\treturn true;\n\t\t}\n\t\tif (g1->sa_family == AF_INET && g2->sa_family == AF_INET6 && (\n\t\t\t((unsigned int*)&((struct sockaddr_in6 *)g2)->sin6_addr)[0] == 0 &&\n\t\t\t((unsigned int*)&((struct sockaddr_in6 *)g2)->sin6_addr)[1] == 0 &&\n\t\t\t((unsigned short*)&((struct sockaddr_in6 *)g2)->sin6_addr)[4] == 0 &&\n\t\t\t((unsigned short*)&((struct sockaddr_in6 *)g2)->sin6_addr)[5] == 0xffff))\n\t\t{\n\t\t\tstruct sockaddr_in6 *i1=(void*)s2->sockaddr;\n\t\t\tstruct sockaddr_in *i2=(void*)s1->sockaddr;\n\t\t\tif (((unsigned int*)&i1->sin6_addr)[3] != *(unsigned int*)&i2->sin_addr)\n\t\t\t\treturn false;\n\t\t\tif (i1->sin6_port != i2->sin_port && qp1 != qp2)\t//allow qports to match instead of ports, if required.\n\t\t\t\treturn false;\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\tswitch(g1->sa_family)\n\t{\n\tdefault:\n\t\treturn true;\n\tcase AF_INET:\n\t\t{\n\t\t\tstruct sockaddr_in *i1=(void*)s1->sockaddr, *i2=(void*)s2->sockaddr;\n\t\t\tif (*(unsigned int*)&i1->sin_addr != *(unsigned int*)&i2->sin_addr)\n\t\t\t\treturn false;\n\t\t\tif (i1->sin_port != i2->sin_port && qp1 != qp2)\t//allow qports to match instead of ports, if required.\n\t\t\t\treturn false;\n\t\t\treturn true;\n\t\t}\n\n\tcase AF_INET6:\n\t\t{\n\t\t\tstruct sockaddr_in6 *i1=(void*)s1->sockaddr, *i2=(void*)s2->sockaddr;\n\t\t\tif (((unsigned int*)&i1->sin6_addr)[0] != ((unsigned int*)&i2->sin6_addr)[0])\n\t\t\t\treturn false;\n\t\t\tif (((unsigned int*)&i1->sin6_addr)[1] != ((unsigned int*)&i2->sin6_addr)[1])\n\t\t\t\treturn false;\n\t\t\tif (((unsigned int*)&i1->sin6_addr)[2] != ((unsigned int*)&i2->sin6_addr)[2])\n\t\t\t\treturn false;\n\t\t\tif (((unsigned int*)&i1->sin6_addr)[3] != ((unsigned int*)&i2->sin6_addr)[3])\n\t\t\t\treturn false;\n\t\t\tif (i1->sin6_port != i2->sin6_port && qp1 != qp2)\t//allow qports to match instead of ports, if required.\n\t\t\t\treturn false;\n\t\t\treturn true;\n\t\t}\n\t}\n\treturn false;\n}\n\nvoid Net_TCPListen(cluster_t *cluster, int port, int socketid)\n{\n\tSOCKET sock;\n\n\tunion\n\t{\n\t\tstruct sockaddr\t\ts;\n\t\tstruct sockaddr_in\tipv4;\n\t\tstruct sockaddr_in6\tipv6;\n#ifdef UNIXSOCKETS\n\t\tstruct sockaddr_un\tun;\n#endif\n\t} address;\n\tint prot;\n\tint addrsize;\n\tint _true = true;\n//\tint fromlen;\n\n\tunsigned long nonblocking = true;\n\tunsigned long v6only = false;\n\tconst char *famname;\n\n\tswitch(socketid)\n\t{\n#ifdef UNIXSOCKETS\n\tcase SG_UNIX:\n\t\tprot = AF_UNIX;\n\t\tmemset(&address.un, 0, sizeof(address.un));\n\t\taddress.un.sun_family = prot;\n\t\tmemcpy(address.un.sun_path, \"\\0qtv\", 4);\n\t\taddrsize = offsetof(struct sockaddr_un, sun_path[4]);\n\t\tfamname = \"unix\";\n\t\tbreak;\n#endif\n\tcase SG_IPV6:\n\t\tprot = AF_INET6;\n\t\tmemset(&address.ipv6, 0, sizeof(address.ipv6));\n\t\taddress.ipv6.sin6_family = prot;\n\t\taddress.ipv6.sin6_port = htons((u_short)port);\n\t\taddrsize = sizeof(struct sockaddr_in6);\n\t\tif (v6only)\n\t\t\tfamname = \"tcp6\";\n\t\telse\n\t\t\tfamname = \"tcp\";\n\t\tbreak;\n\tcase SG_IPV4:\n\t\tprot = AF_INET;\n\t\taddress.ipv4.sin_family = prot;\n\t\taddress.ipv4.sin_addr.s_addr = INADDR_ANY;\n\t\taddress.ipv4.sin_port = htons((u_short)port);\n\t\taddrsize = sizeof(struct sockaddr_in);\n\t\tfamname = \"tcp4\";\n\t\tbreak;\n\tdefault:\n\t\treturn;\t//some kind of error. avoid unintialised warnings.\n\t}\n\n\tif (socketid==SG_IPV4 && !v6only && cluster->tcpsocket[SG_IPV6] != INVALID_SOCKET)\n\t{\t//if we already have a hybrid ipv6 socket, don't bother with ipv4 too\n\t\tint sz = sizeof(v6only);\n\t\tif (getsockopt(cluster->tcpsocket[1], IPPROTO_IPV6, IPV6_V6ONLY, (char *)&v6only, &sz) == 0 && !v6only)\n\t\t\tport = 0;\n\t}\n\n\tif (cluster->tcpsocket[socketid] != INVALID_SOCKET)\n\t{\n\t\tclosesocket(cluster->tcpsocket[socketid]);\n\t\tcluster->tcpsocket[socketid] = INVALID_SOCKET;\n\n\t\tSys_Printf(cluster, \"closed %s port\\n\", famname);\n\t}\n\tif (!port)\n\t\treturn;\n\n\tif ((sock = socket (prot, SOCK_STREAM, 0)) == INVALID_SOCKET)\n\t{\n\t\tcluster->tcpsocket[socketid] = INVALID_SOCKET;\n\t\treturn;\n\t}\n\n\tif (ioctlsocket (sock, FIONBIO, &nonblocking) == -1)\n\t{\n\t\tcluster->tcpsocket[socketid] = INVALID_SOCKET;\n\t\tclosesocket(sock);\n\t\treturn;\n\t}\n\n#ifdef _WIN32 \n\t//win32 is so fucked up\n//\tsetsockopt(newsocket, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (char *)&_true, sizeof(_true));\n#endif\n\tsetsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char *)&_true, sizeof(_true));\n\n\n\tif (socketid == SG_IPV6)\n\t{\n\t\tif (setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&v6only, sizeof(v6only)) == -1)\n\t\t\tv6only = true;\n\t\tif (v6only)\n\t\t\tfamname = \"tcp6\";\n\t\telse\n\t\t\tfamname = \"tcp\";\n\t}\n\n\tif (bind (sock, &address.s, addrsize) == -1)\n\t{\n\t\tprintf(\"socket bind error %i (%s)\\n\", qerrno, strerror(qerrno));\n\t\tclosesocket(sock);\n\t\treturn;\n\t}\n\n\tlisten(sock, 2);\t//don't listen for too many clients at once.\n\n\tSys_Printf(cluster, \"opened %s port %i\\n\", famname, port);\n\n\tcluster->tcpsocket[socketid] = sock;\n}\n\nchar *strchrrev(char *str, char chr)\n{\n\tchar *firstchar = str;\n\tfor (str = str + strlen(str)-1; str>=firstchar; str--)\n\t\tif (*str == chr)\n\t\t\treturn str;\n\n\treturn NULL;\n}\n\nvoid Net_SendQTVConnectionRequest(sv_t *qtv, char *authmethod, char *challenge)\n{\n\tchar *at;\n\tchar *str;\n\tchar hash[512];\n\n\t//due to mvdsv sucking and stuff, we try using raw connections where possibleso that we don't end up expecting a header.\n\t//at some point, this will be forced to 1\n\tqtv->parsingqtvheader = true;//!!*qtv->connectpassword;\n\tqtv->buffersize = 0;\n\tqtv->forwardpoint = 0;\n\n\tstr =\t\"QTV\\n\";\t\t\tNet_QueueUpstream(qtv, strlen(str), str);\n\tstr =\t\"VERSION: 1\\n\";\t\tNet_QueueUpstream(qtv, strlen(str), str);\n\n\tif (qtv->serverquery)\n\t{\n\t\tif (qtv->serverquery == 2)\n\t\t{\n\t\t\tstr =\t\"DEMOLIST\\n\";\t\tNet_QueueUpstream(qtv, strlen(str), str);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tstr =\t\"SOURCELIST\\n\";\t\tNet_QueueUpstream(qtv, strlen(str), str);\n\t\t}\n\t}\n\telse\n\t{\n\n\t\tat = strchrrev(qtv->server, '@');\n\t\tif (at)\n\t\t{\n\t\t\t*at = '\\0';\n\t\t\tstr =\t\"SOURCE: \";\t\tNet_QueueUpstream(qtv, strlen(str), str);\n\n\t\t\tif (strncmp(qtv->server, \"tcp:\", 4))\n\t\t\t{\n\t\t\t\tstr = qtv->server;\n\t\t\t\tNet_QueueUpstream(qtv, strlen(str), str);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tstr = strchr(qtv->server, ':');\n\t\t\t\tif (str)\n\t\t\t\t{\n\t\t\t\t\tstr++;\n\t\t\t\t\tNet_QueueUpstream(qtv, strlen(str), str);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tstr =\t\"\\n\";\t\t\tNet_QueueUpstream(qtv, strlen(str), str);\n\t\t\t*at = '@';\n\t\t}\n\t\telse\n\t\t{\n\t\t\tstr =\t\"RECEIVE\\n\";\tNet_QueueUpstream(qtv, strlen(str), str);\n\t\t}\n\n\t\tif (!qtv->parsingqtvheader)\n\t\t{\n\t\t\tstr =\t\"RAW: 1\\n\";\t\tNet_QueueUpstream(qtv, strlen(str), str);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (authmethod)\n\t\t\t{\n\t\t\t\tif (!strcmp(authmethod, \"PLAIN\"))\n\t\t\t\t{\n\t\t\t\t\tstr = \"AUTH: PLAIN\\n\";\tNet_QueueUpstream(qtv, strlen(str), str);\n\t\t\t\t\tstr = \"PASSWORD: \\\"\";\tNet_QueueUpstream(qtv, strlen(str), str);\n\t\t\t\t\tstr = qtv->connectpassword;\tNet_QueueUpstream(qtv, strlen(str), str);\n\t\t\t\t\tstr = \"\\\"\\n\";\t\t\tNet_QueueUpstream(qtv, strlen(str), str);\n\t\t\t\t}\n\t\t\t\t/*else if (challenge && strlen(challenge)>=32 && !strcmp(authmethod, \"CCITT\"))\n\t\t\t\t{\n\t\t\t\t\tunsigned short crcvalue;\n\t\t\t\t\tstr = \"AUTH: CCITT\\n\";\tNet_QueueUpstream(qtv, strlen(str), str);\n\t\t\t\t\tstr = \"PASSWORD: \\\"\";\tNet_QueueUpstream(qtv, strlen(str), str);\n\n\t\t\t\t\tsnprintf(hash, sizeof(hash), \"%s%s\", challenge, qtv->connectpassword);\n\t\t\t\t\tcrcvalue = QCRC_Block(hash, strlen(hash));\n\t\t\t\t\tsprintf(hash, \"0x%X\", (unsigned int)QCRC_Value(crcvalue));\n\n\t\t\t\t\tstr = hash;\t\t\t\tNet_QueueUpstream(qtv, strlen(str), str);\n\t\t\t\t\tstr = \"\\\"\\n\";\t\t\tNet_QueueUpstream(qtv, strlen(str), str);\n\t\t\t\t}*/\n\t\t\t\telse if (challenge && strlen(challenge)>=8 && !strcmp(authmethod, \"MD4\"))\n\t\t\t\t{\n\t\t\t\t\tunsigned int md4sum[4];\n\t\t\t\t\tstr = \"AUTH: MD4\\n\";\tNet_QueueUpstream(qtv, strlen(str), str);\n\t\t\t\t\tstr = \"PASSWORD: \\\"\";\tNet_QueueUpstream(qtv, strlen(str), str);\n\n\t\t\t\t\tsnprintf(hash, sizeof(hash), \"%s%s\", challenge, qtv->connectpassword);\n\t\t\t\t\tCom_BlockFullChecksum (hash, strlen(hash), (unsigned char*)md4sum);\n\t\t\t\t\tsprintf(hash, \"%X%X%X%X\", md4sum[0], md4sum[1], md4sum[2], md4sum[3]);\t//FIXME: bad formatting!\n\n\t\t\t\t\tstr = hash;\t\t\t\tNet_QueueUpstream(qtv, strlen(str), str);\n\t\t\t\t\tstr = \"\\\"\\n\";\t\t\tNet_QueueUpstream(qtv, strlen(str), str);\n\t\t\t\t}\n\t\t\t\telse if (challenge && strlen(challenge)>=8 && !strcmp(authmethod, \"SHA1\"))\n\t\t\t\t{\n\t\t\t\t\tunsigned char digest[20];\n\t\t\t\t\tstr = \"AUTH: SHA1\\n\";\tNet_QueueUpstream(qtv, strlen(str), str);\n\t\t\t\t\tstr = \"PASSWORD: \\\"\";\tNet_QueueUpstream(qtv, strlen(str), str);\n\n\t\t\t\t\tsnprintf(hash, sizeof(hash), \"%s%s\", challenge, qtv->connectpassword);\n\t\t\t\t\tCalcHash(&hash_sha1, (unsigned char*)digest, sizeof(digest), hash, strlen(hash));\n\t\t\t\t\ttobase64(hash, sizeof(hash), digest, hash_sha1.digestsize);\n\n\t\t\t\t\tstr = hash;\t\t\t\tNet_QueueUpstream(qtv, strlen(str), str);\n\t\t\t\t\tstr = \"\\\"\\n\";\t\t\tNet_QueueUpstream(qtv, strlen(str), str);\n\t\t\t\t}\n\t\t\t\telse if (!strcmp(authmethod, \"NONE\"))\n\t\t\t\t{\n\t\t\t\t\tstr = \"AUTH: NONE\\n\";\tNet_QueueUpstream(qtv, strlen(str), str);\n\t\t\t\t\tstr = \"PASSWORD: \\n\";\tNet_QueueUpstream(qtv, strlen(str), str);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tqtv->errored = ERR_PERMANENT;\n\t\t\t\t\tqtv->upstreambuffersize = 0;\n\t\t\t\t\tSys_Printf(qtv->cluster, \"Auth method %s was not usable\\n\", authmethod);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tstr = \"AUTH: SHA1\\n\";\t\tNet_QueueUpstream(qtv, strlen(str), str);\n\t\t\t\tstr = \"AUTH: MD4\\n\";\t\tNet_QueueUpstream(qtv, strlen(str), str);\n//\t\t\t\tstr = \"AUTH: CCITT\\n\";\t\tNet_QueueUpstream(qtv, strlen(str), str);\n\t\t\t\tstr = \"AUTH: PLAIN\\n\";\t\tNet_QueueUpstream(qtv, strlen(str), str);\n\t\t\t\tstr = \"AUTH: NONE\\n\";\t\tNet_QueueUpstream(qtv, strlen(str), str);\n\t\t\t}\n\t\t}\n\t}\n\tstr =\t\"\\n\";\t\tNet_QueueUpstream(qtv, strlen(str), str);\n}\n\nqboolean Net_ConnectToTCPServer(sv_t *qtv, char *ip)\n{\n\tint err;\n\tnetadr_t from;\n\tunsigned long nonblocking = true;\n\tint afam;\n\tint pfam;\n\tint asz;\n\n\tif (!NET_StringToAddr(ip, &qtv->serveraddress, 27500))\n\t{\n\t\tSys_Printf(qtv->cluster, \"Stream %i: Unable to resolve %s\\n\", qtv->streamid, ip);\n\t\tstrcpy(qtv->status, \"Unable to resolve server\\n\");\n\t\treturn false;\n\t}\n\tafam = ((struct sockaddr*)&qtv->serveraddress.sockaddr)->sa_family;\n\tpfam = ((afam==AF_INET6)?PF_INET6:PF_INET);\n\tasz = ((afam==AF_INET6)?sizeof(struct sockaddr_in6):sizeof(struct sockaddr_in));\n\tqtv->sourcesock = socket(pfam, SOCK_STREAM, IPPROTO_TCP);\n\tif (qtv->sourcesock == INVALID_SOCKET)\n\t{\n\t\tstrcpy(qtv->status, \"Network error\\n\");\n\t\treturn false;\n\t}\n\n\tif (afam == AF_INET6)\n\t{\n\t\tqboolean v6only = true;\n\t\tsetsockopt(qtv->sourcesock, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&v6only, sizeof(v6only));\n\t}\n\n\tmemset(&from, 0, sizeof(from));\n\t((struct sockaddr*)&from)->sa_family = afam;\n\tif (bind(qtv->sourcesock, (struct sockaddr *)&from, sizeof(from)) == -1)\n\t{\n\t\tclosesocket(qtv->sourcesock);\n\t\tqtv->sourcesock = INVALID_SOCKET;\n\t\tstrcpy(qtv->status, \"Network error\\n\");\n\t\treturn false;\n\t}\n\n\tif (ioctlsocket (qtv->sourcesock, FIONBIO, &nonblocking) == -1)\n\t{\n\t\tclosesocket(qtv->sourcesock);\n\t\tqtv->sourcesock = INVALID_SOCKET;\n\t\tstrcpy(qtv->status, \"Network error\\n\");\n\t\treturn false;\n\t}\n\n\tif (connect(qtv->sourcesock, (struct sockaddr *)&qtv->serveraddress.sockaddr, asz) == INVALID_SOCKET)\n\t{\n\t\terr = qerrno;\n\t\tif (err != NET_EINPROGRESS && err != NET_EAGAIN && err != NET_EWOULDBLOCK)\t//bsd sockets are meant to return EINPROGRESS, but some winsock drivers use EWOULDBLOCK instead. *sigh*...\n\t\t{\n\t\t\tclosesocket(qtv->sourcesock);\n\t\t\tqtv->sourcesock = INVALID_SOCKET;\n\t\t\tstrcpy(qtv->status, \"Connection failed\\n\");\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t//make sure the buffers are empty. we could have disconnected prematurly\n\tqtv->upstreambuffersize = 0;\n\tqtv->buffersize = 0;\n\tqtv->forwardpoint = 0;\n\n\t//read the notes at the start of this file for what these text strings mean\n\tNet_SendQTVConnectionRequest(qtv, NULL, NULL);\n\treturn true;\n}\nqboolean Net_ConnectToUDPServer(sv_t *qtv, char *ip)\n{\n\tnetadr_t from;\n\tunsigned long nonblocking = true;\n\tint afam, pfam, asz;\n\n\tif (!NET_StringToAddr(ip, &qtv->serveraddress, 27500))\n\t{\n\t\tSys_Printf(qtv->cluster, \"Stream %i: Unable to resolve %s\\n\", qtv->streamid, ip);\n\t\treturn false;\n\t}\n\tafam = ((struct sockaddr*)&qtv->serveraddress.sockaddr)->sa_family;\n\tpfam = ((afam==AF_INET6)?PF_INET6:PF_INET);\n\tasz = ((afam==AF_INET6)?sizeof(struct sockaddr_in6):sizeof(struct sockaddr_in));\n\tqtv->sourcesock = socket(pfam, SOCK_DGRAM, IPPROTO_UDP);\n\tif (qtv->sourcesock == INVALID_SOCKET)\n\t\treturn false;\n\n\tif (afam == AF_INET6)\n\t{\n\t\tqboolean v6only = true;\n\t\tsetsockopt(qtv->sourcesock, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&v6only, sizeof(v6only));\n\t}\n\n\tmemset(&from, 0, sizeof(from));\n\t((struct sockaddr*)&from)->sa_family = afam;\n\tif (bind(qtv->sourcesock, (struct sockaddr *)&from, asz) == -1)\n\t{\n\t\tclosesocket(qtv->sourcesock);\n\t\tqtv->sourcesock = INVALID_SOCKET;\n\t\treturn false;\n\t}\n\n\tif (ioctlsocket (qtv->sourcesock, FIONBIO, &nonblocking) == -1)\n\t{\n\t\tclosesocket(qtv->sourcesock);\n\t\tqtv->sourcesock = INVALID_SOCKET;\n\t\treturn false;\n\t}\n\n\tqtv->qport = Sys_Milliseconds()*1000+Sys_Milliseconds();\n\n\treturn true;\n}\n\nqboolean DemoFilenameIsOkay(sv_t* qtv, char *fname, char *dir)\n{\n\tint len;\n\n\tif (dir)\n\t{\n\t\tif (strchr(dir, '/'))\n\t\t\treturn false;\t//unix path seperator\n\t\tif (strchr(dir, '\\\\'))\n\t\t\treturn false;\t//windows path seperator\n\t\tif (strchr(dir, ':'))\n\t\t\treturn false;\t//mac path seperator\n\t\tif (*(dir) == '.')\n\t\t\treturn false;\n\t}\n\n\tif (strchr(fname, '/'))\n\t\treturn false;\t//unix path seperator\n\tif (strchr(fname, '\\\\'))\n\t\treturn false;\t//windows path seperator\n\tif (strchr(fname, ':'))\n\t\treturn false;\t//mac path seperator\n\tif (*(fname) == '.')\n\t\treturn false;\n\n\t//now make certain that the last four characters are '.mvd' and not something like '.cfg' perhaps\n\tlen = strlen(fname);\n\tif (len < 5)\n\t\treturn false;\n\tif (strcmp(fname+len-4, \".mvd\"))\n\t\treturn false;\n\n\treturn true;\n\n/*\n\tif (strchr(fname, '\\\\'))\n\t{\n\t\tchar *s;\n\t\tCon_Printf(\"Warning: \\\\ characters in filename %s\\n\", fname);\n\t\twhile((s = strchr(fname, '\\\\')))\n\t\t\t*s = '/';\n\t}\n\n\tif (strstr(fname, \"..\"))\n\t{\n\t\tCon_Printf(\"Error: '..' characters in filename %s\\n\", fname);\n\t}\n\telse if (fname[0] == '/')\n\t{\n\t\tCon_Printf(\"Error: absolute path in filename %s\\n\", fname);\n\t}\n\telse if (strstr(fname, \":\")) //win32 drive seperator (or mac path seperator, but / works there and they're used to it)\n\t{\n\t\tCon_Printf(\"Error: absolute path in filename %s\\n\", fname);\n\t}\n\telse\n\t\treturn false;\n\treturn true;\n*/\n}\n\nqboolean Net_ConnectToDemoServer(sv_t* qtv, char* ip, char* dir)\n{\n\tchar fullname[512];\n\tqtv->sourcesock = INVALID_SOCKET;\n\tif (DemoFilenameIsOkay(qtv, ip, dir))\n\t{\n\t\tif (!dir)\n\t\t\tsnprintf(fullname, sizeof(fullname), \"%s%s\", qtv->cluster->demodir, ip);\n\t\telse\n\t\t\tsnprintf(fullname, sizeof(fullname), \"%s%s/%s\", qtv->cluster->demodir, dir, ip);\n\t\tqtv->sourcefile = fopen(fullname, \"rb\");\n\t}\n\telse\n\t\tqtv->sourcefile = NULL;\n\n\tif (qtv->sourcefile)\n\t{\n\t\tchar smallbuffer[17];\n\t\tfseek(qtv->sourcefile, 0, SEEK_END);\n\t\tqtv->filelength = ftell(qtv->sourcefile);\n\n\t\t//attempt to detect the end of the file\n\t\tfseek(qtv->sourcefile, 0-(long)sizeof(smallbuffer), SEEK_CUR);\n\t\tfread(smallbuffer, 1, 17, qtv->sourcefile);\n\t\t//0 is the time\n\t\tif (smallbuffer[1] == dem_all || smallbuffer[1] == dem_read) //mvdsv changed it to read...\n\t\t{\n\t\t\t//2,3,4,5 are the length\n\t\t\tif (smallbuffer[6] == svc_disconnect)\n\t\t\t{\n\t\t\t\tif (!strcmp(smallbuffer+7, \"EndOfDemo\"))\n\t\t\t\t{\n\t\t\t\t\tqtv->filelength -= 17;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tfseek(qtv->sourcefile, 0, SEEK_SET);\n\t\treturn true;\n\t}\n\n\tif (!dir)\n\t\tSys_Printf(qtv->cluster, \"Stream %i: Unable to open file %s\\n\", qtv->streamid, ip);\n\telse\n\t\tSys_Printf(qtv->cluster, \"Stream %i: Unable to open file %s in directory %s\\n\", qtv->streamid, ip, dir);\n\treturn false;\n}\n\nqboolean Net_ConnectToDemoDirServer(sv_t* qtv, char *ip)\n{\n\tchar fullname[512];\n\tqtv->sourcesock = INVALID_SOCKET;\n\tsnprintf(fullname, sizeof(fullname), \"%s%s\", qtv->cluster->demodir, ip);\n\n\t#ifdef _WIN32\n\t// TODO: code for Windows directories goes here\n\t// TODO: possible to do this without copy-pasting the entire GNU/Linux code below?\n\t// TODO: also, what about MAC OS X?\n\tSys_Printf(qtv->cluster, \"Windows support coming soon!\\n\");\n\treturn false;\n\t#else\n\t{\n\t\tDIR *dir;\n\t\tstruct dirent* ent;\n\n\t\tdir = opendir(fullname);\n\t\tif (dir)\n\t\t{\n\t\t\tchar demoname[512];\n\t\t\tint current_demo = 0;\n\t\t\tint file_count = 0;\n\t\t\tint random_number = 1; // always this value if the directory contains one file\n\n\t\t\t// count the files, important for determining a random demo file\n\t\t\twhile ((ent = readdir(dir)) != NULL)\n\t\t\t{\n\t\t\t\tint len;\n\n\t\t\t\t// only count files neding in .mvd\n\t\t\t\tlen = strlen(ent->d_name);\n\t\t\t\tif (len < 5)\n\t\t\t\t{\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (strcmp(ent->d_name+len-4, \".mvd\"))\n\t\t\t\t{\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tif (ent->d_type == DT_REG && *(ent->d_name) != '.')\n\t\t\t\t\tfile_count++;\t// only add non-hidden and regular files\n\t\t\t}\n\n\t\t\tif (file_count == 0)\n\t\t\t{\n\t\t\t\t// empty directory\n\t\t\t\tSys_Printf(qtv->cluster, \"Stream %i: Error: Directory has no demos.\\n\", qtv->streamid);\n\t\t\t\tclosedir(dir);\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tclosedir(dir);\n\t\t\tdir = opendir(fullname);\n\n\t\t\t// FIXME: not sure if srand should only be called once somewhere?\n\t\t\t// FIXME: this is not really shuffling the demos, but does introduce some variety\n\t\t\tif (file_count > 1)\n\t\t\t{\n\t\t\t\t//srand(time(NULL));\n\t\t\t\twhile ((random_number = rand()%file_count + 1) == qtv->last_random_number);\n\t\t\t\tqtv->last_random_number = random_number;\n\t\t\t}\n\n\t\t\twhile (1) {\n\t\t\t\tint len;\n\n\t\t\t\tent = readdir(dir);\n\t\t\t\tif (!ent)\n\t\t\t\t{\n\t\t\t\t\t// reached the end of the directory, shouldn't happen\n\t\t\t\t\tSys_Printf(qtv->cluster, \"Stream %i: Error: Reached end of directory (%s%s)\\n\", qtv->streamid, qtv->cluster->demodir, ip);\n\t\t\t\t\tclosedir(dir);\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\n\t\t\t\tif (ent->d_type != DT_REG || *(ent->d_name) == '.')\n\t\t\t\t{\n\t\t\t\t\tcontinue;\t// ignore hidden and non-regular files\n\t\t\t\t}\n\n\t\t\t\t//now make certain that the last four characters are '.mvd' and not something like '.cfg' perhaps\n\t\t\t\tlen = strlen(ent->d_name);\n\t\t\t\tif (len < 5)\n\t\t\t\t{\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (strcmp((ent->d_name)+len-4, \".mvd\"))\n\t\t\t\t{\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tif (++current_demo != random_number)\n\t\t\t\t\tcontinue;\n\n\t\t\t\tsnprintf(demoname, sizeof(demoname), \"%s/%s\", ip, ent->d_name);\n\t\t\t\tqtv->sourcefile = fopen(demoname, \"rb\");\n\t\t\t\tclosedir(dir);\n\t\t\t\tif (Net_ConnectToDemoServer(qtv, ent->d_name, ip) == true)\n\t\t\t\t{\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t\tclosedir(dir);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tSys_Printf(qtv->cluster, \"Stream %i: Unable to open directory %s\\n\", qtv->streamid, qtv->cluster->demodir);\n\t\t\treturn false;\n\t\t}\n\t}\n\t#endif\n\n\treturn false;\n}\n\n/*figures out the ip to connect to, and decides the protocol for it*/\nchar *Net_DiagnoseProtocol(sv_t *qtv)\n{\n\tchar *at;\n\tsourcetype_t type = SRC_BAD;\n\tchar *ip = qtv->server;\n\n\tif (!strncmp(ip, \"udp:\", 4))\n\t{\n\t\ttype = SRC_UDP;\n\t\tip += 4;\n\t}\n\telse if (!strncmp(ip, \"tcp:\", 4))\n\t{\n\t\ttype = SRC_TCP;\n\t\tip += 4;\n\t}\n\telse if (!strncmp(ip, \"demo:\", 5))\n\t{\n\t\ttype = SRC_DEMO;\n\t\tip += 5;\n\t}\n\telse if (!strncmp(ip, \"file:\", 5))\n\t{\n\t\ttype = SRC_DEMO;\n\t\tip += 5;\n\t}\n\telse if (!strncmp(ip, \"dir:\", 4))\n\t{\n\t\ttype = SRC_DEMODIR;\n\t\tip += 4;\n\t}\n\n\tat = strchrrev(ip, '@');\n\tif (at && (type == SRC_DEMO || type == SRC_DEMODIR || type == SRC_TCP))\n\t{\n\t\tif (type == SRC_DEMO || type == SRC_DEMODIR)\n\t\t\ttype = SRC_TCP;\n\t\tip = at+1;\n\t}\n\n\tqtv->sourcetype = type;\n\treturn ip;\n}\n\nqboolean Net_ConnectToServer(sv_t *qtv)\n{\n\tchar *ip = Net_DiagnoseProtocol(qtv);\n\n\tqtv->usequakeworldprotocols = false;\n\tqtv->pext1 = 0;\n\tqtv->pext2 = 0;\n\n\tif (qtv->sourcetype == SRC_DEMO || qtv->sourcetype == SRC_DEMODIR)\n\t{\n\t\tqtv->nextconnectattempt = qtv->curtime + RECONNECT_TIME_DEMO;\t//wait half a minuite before trying to reconnect\n\t}\n\telse\n\t\tqtv->nextconnectattempt = qtv->curtime + RECONNECT_TIME;\t//wait half a minuite before trying to reconnect\n\n\tswitch(\tqtv->sourcetype)\n\t{\n\tcase SRC_DEMO:\n\t\treturn Net_ConnectToDemoServer(qtv, ip, NULL);\n\n\n\tcase SRC_DEMODIR:\n\t\treturn Net_ConnectToDemoDirServer(qtv, ip);\n\n\n\tcase SRC_UDP:\n\t\tqtv->usequakeworldprotocols = true;\n\t\treturn Net_ConnectToUDPServer(qtv, ip);\n\n\tcase SRC_TCP:\n\t\treturn Net_ConnectToTCPServer(qtv, ip);\n\n\tdefault:\n\t\tSys_Printf(qtv->cluster, \"Unknown source type %s\\n\", ip);\n\t\treturn false;\n\t}\n}\n\nvoid Net_QueueUpstream(sv_t *qtv, int size, char *buffer)\n{\n\tif (qtv->usequakeworldprotocols)\n\t\treturn;\n\n\tif (qtv->upstreambuffersize + size > sizeof(qtv->upstreambuffer))\n\t{\n\t\tSys_Printf(qtv->cluster, \"Stream %i: Upstream queue overflowed for %s\\n\", qtv->streamid, qtv->server);\n\t\tstrcpy(qtv->status, \"Upstream overflow\");\n\t\tqtv->errored = ERR_RECONNECT;\n\t\treturn;\n\t}\n\tmemcpy(qtv->upstreambuffer + qtv->upstreambuffersize, buffer, size);\n\tqtv->upstreambuffersize += size;\n}\n\nqboolean Net_WriteUpstream(sv_t *qtv)\n{\n\tint len;\n\n\tif (qtv->upstreambuffersize && qtv->sourcesock != INVALID_SOCKET)\n\t{\n\t\tlen = send(qtv->sourcesock, qtv->upstreambuffer, qtv->upstreambuffersize, 0);\n\t\tif (len == 0)\n\t\t\treturn false;\n\t\tif (len < 0)\n\t\t{\n\t\t\tint err = qerrno;\n\t\t\tif (err != NET_EWOULDBLOCK && err != NET_EAGAIN && err != NET_ENOTCONN)\n\t\t\t{\n\t\t\t\tif (err)\n\t\t\t\t{\n\t\t\t\t\tSys_Printf(qtv->cluster, \"Stream %i: Error: source socket error %i (%s)\\n\", qtv->streamid, err, strerror(err));\n\t\t\t\t\tstrcpy(qtv->status, \"Network error\\n\");\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tSys_Printf(qtv->cluster, \"Stream %i: Error: server %s disconnected\\n\", qtv->streamid, qtv->server);\n\t\t\t\t\tstrcpy(qtv->status, \"Server disconnected\");\n\t\t\t\t}\n\t\t\t\tqtv->errored = ERR_RECONNECT;\t//if the server is down, we'll detect it on reconnect\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\t\tqtv->upstreambuffersize -= len;\n\t\tmemmove(qtv->upstreambuffer, qtv->upstreambuffer + len, qtv->upstreambuffersize);\n\t}\n\treturn true;\n}\n\nvoid SV_SendUpstream(sv_t *qtv, netmsg_t *nm)\n{\n\tchar size[2];\n\tint ffs = nm->cursize+2;\t//qq fucked up and made his upstream inconsistent with downstream. ffs.\n\n\tsize[0] = (ffs&0x00ff)>>0;\n\tsize[1] = (ffs&0xff00)>>8;\n\tNet_QueueUpstream(qtv, 2, size);\n\tNet_QueueUpstream(qtv, nm->cursize, nm->data);\n\tNet_WriteUpstream(qtv);\t//try and flush it\n}\n\nint SV_SayToUpstream(sv_t *qtv, char *message)\n{\n\tchar buffer[1024];\n\tnetmsg_t nm;\n\n\tif (!qtv->upstreamacceptschat)\n\t{\n#ifndef _MSC_VER\n#warning This is incomplete!\n#endif\n\t\t//Sys_Printf(qtv->cluster, \"not forwarding say\\n\");\n\t\treturn 0;\n\t}\n\n\tInitNetMsg(&nm, buffer, sizeof(buffer));\n\n\tWriteByte(&nm, qtv_clc_stringcmd);\n\tWriteString2(&nm, \"say \");\n\tWriteString(&nm, message);\n\tSV_SendUpstream(qtv, &nm);\n\n\treturn 1;\n}\n\nvoid SV_SayToViewers(sv_t *qtv, char *message)\n{\n\tFwd_SayToDownstream(qtv, message);\n#ifndef _MSC_VER\n\t#warning Send to viewers here too\n#endif\n}\n\n//This function 1: parses the 'don't delay' packets in the stream\n//              2: returns the length of continuous data (that is, whole-packet bytes that have not been truncated by the networking layer)\n//                 this means we know that the client proxies have valid data, at least from our side.\nint SV_EarlyParse(sv_t *qtv, unsigned char *buffer, int remaining)\n{\n\tint lengthofs;\n\tint length;\n\tint available = 0;\n\twhile(1)\n\t{\n\t\tif (remaining < 2)\n\t\t\treturn available;\n\n\t\t//buffer[0] is time\n\n\t\tswitch (buffer[1]&dem_mask)\n\t\t{\n\t\tcase dem_set:\n\t\t\tlengthofs = 0;\t//to silence gcc, nothing more\n\t\t\tbreak;\n\t\tcase dem_multiple:\n\t\t\tlengthofs = 6;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tlengthofs = 2;\n\t\t\tbreak;\n\t\t}\n\n\t\tif (lengthofs > 0)\n\t\t{\n\t\t\tif (lengthofs+4 > remaining)\n\t\t\t\treturn available;\n\n\t\t\tlength = (buffer[lengthofs]<<0) + (buffer[lengthofs+1]<<8) + (buffer[lengthofs+2]<<16) + (buffer[lengthofs+3]<<24);\n\n\t\t\tlength += lengthofs+4;\n\t\t\tif (length > MAX_MSGLEN)\n\t\t\t\tprintf(\"Probably corrupt mvd (length %i)\\n\", length);\n\t\t}\n\t\telse\n\t\t\tlength = 10;\n\n\t\tif (remaining < length)\n\t\t\treturn available;\n\n\t\tif ((buffer[1]&dem_mask) == dem_all && (buffer[1] & ~dem_mask) && qtv->sourcetype != SRC_DEMO)\t//dem_qtvdata\n\t\t{\n\t\t\tParseMessage(qtv, buffer+lengthofs+4, length - (lengthofs+4), buffer[1], 0xffffffff);\n\t\t}\n\n\t\tremaining -= length;\n\t\tavailable += length;\n\t\tbuffer += length;\n\t}\n}\n\nqboolean Net_ReadStream(sv_t *qtv)\n{\n\tint maxreadable;\n\tint read;\n\tvoid *buffer;\n\tint err;\n\n\tmaxreadable = MAX_PROXY_BUFFER - qtv->buffersize;\n\tif (!maxreadable)\n\t\treturn true;\t//this is bad!\n\tbuffer = qtv->buffer + qtv->buffersize;\n\n\tif (qtv->sourcefile)\n\t{\n\t\tif (maxreadable > PREFERRED_PROXY_BUFFER-qtv->buffersize)\n\t\t\tmaxreadable = PREFERRED_PROXY_BUFFER-qtv->buffersize;\n\t\tif (maxreadable<=0)\n\t\t\treturn true;\n\n\t\t//reuse read a little...\n\t\tread = ftell(qtv->sourcefile);\n\t\tif (read+maxreadable > qtv->filelength)\n\t\t\tmaxreadable = qtv->filelength-read;\t//clamp to the end of the file\n\t\t\t\t\t\t\t\t//even if that 'end' is before the svc_disconnect\n\n\t\tread = fread(buffer, 1, maxreadable, qtv->sourcefile);\n\t}\n\telse\n\t{\n\t\tunsigned int errsize;\n\t\terrsize = sizeof(err);\n\t\terr = 0;\n\t\tgetsockopt(qtv->sourcesock, SOL_SOCKET, SO_ERROR, (char*)&err, &errsize);\n\t\tif (err == NET_ECONNREFUSED)\n\t\t{\n\t\t\tSys_Printf(qtv->cluster, \"Stream %i: Error: server %s refused connection\\n\", qtv->streamid, qtv->server);\n\t\t\tclosesocket(qtv->sourcesock);\n\t\t\tqtv->sourcesock = INVALID_SOCKET;\n\t\t\tqtv->upstreambuffersize = 0;\t//probably contains initial connection request info\n\t\t\treturn false;\n\t\t}\n\n\t\tread = recv(qtv->sourcesock, buffer, maxreadable, 0);\n\t}\n\tif (read > 0)\n\t{\n\t\tqtv->buffersize += read;\n\t\tif (!qtv->cluster->lateforward && !qtv->parsingqtvheader)\t//qtv header being the auth part of the connection rather than the stream\n\t\t{\n\t\t\tint forwardable;\n\t\t\t//this has the effect of not only parsing early packets, but also saying how much complete data there is.\n\t\t\tforwardable = SV_EarlyParse(qtv, qtv->buffer+qtv->forwardpoint, qtv->buffersize - qtv->forwardpoint);\n\t\t\tif (forwardable > 0)\n\t\t\t{\n\t\t\t\tSV_ForwardStream(qtv, qtv->buffer+qtv->forwardpoint, forwardable);\n\t\t\t\tqtv->forwardpoint += forwardable;\n\t\t\t}\n\t\t}\n\t}\n\telse\n\t{\n\t\tif (read == 0)\n\t\t\terr = 0;\n\t\telse\n\t\t\terr = qerrno;\n\t\tif (read == 0 || (err != NET_EWOULDBLOCK && err != NET_EAGAIN && err != NET_ENOTCONN))\t//ENOTCONN can be returned whilst waiting for a connect to finish.\n\t\t{\n\t\t\tif (qtv->sourcefile)\n\t\t\t\tSys_Printf(qtv->cluster, \"Stream %i: Error: End of file\\n\", qtv->streamid);\n\t\t\telse if (read)\n\t\t\t\tSys_Printf(qtv->cluster, \"Stream %i: Error: source socket error %i (%s)\\n\", qtv->streamid, qerrno, strerror(qerrno));\n\t\t\telse\n\t\t\t\tSys_Printf(qtv->cluster, \"Stream %i: Error: server %s disconnected\\n\", qtv->streamid, qtv->server);\n\t\t\tif (qtv->sourcesock != INVALID_SOCKET)\n\t\t\t{\n\t\t\t\tclosesocket(qtv->sourcesock);\n\t\t\t\tqtv->sourcesock = INVALID_SOCKET;\n\t\t\t}\n\t\t\tif (qtv->sourcefile)\n\t\t\t{\n\t\t\t\tfclose(qtv->sourcefile);\n\t\t\t\tqtv->sourcefile = NULL;\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\t}\n\treturn true;\n}\n\nunsigned int Sys_Milliseconds(void)\n{\n#ifdef _WIN32\n\t#ifdef _MSC_VER\n\t\t#pragma comment(lib, \"winmm.lib\")\n\t#endif\n\n#if 0\n\tstatic firsttime = 1;\n\tstatic starttime;\n\tif (firsttime)\n\t{\n\t\tstarttime = timeGetTime() + 1000*20;\n\t\tfirsttime = 0;\n\t}\n\treturn timeGetTime() - starttime;\n#endif\n\n\n\n\treturn timeGetTime();\n#else\n\t//assume every other system follows standards.\n\tunsigned int t;\n\tstruct timeval tv;\n\n\tgettimeofday(&tv, NULL);\n\tt = ((unsigned int)tv.tv_sec)*1000 + (((unsigned int)tv.tv_usec)/1000);\n\treturn t;\n#endif\n}\n/*\nvoid NetSleep(sv_t *tv)\n{\n\tint m;\n\tstruct timeval timeout;\n\tfd_set socketset;\n\n\tFD_ZERO(&socketset);\n\tm = 0;\n\tif (tv->sourcesock != INVALID_SOCKET)\n\t{\n\t\tFD_SET(tv->sourcesock, &socketset);\n\t\tif (tv->sourcesock >= m)\n\t\t\tm = tv->sourcesock+1;\n\t}\n\tif (tv->qwdsocket != INVALID_SOCKET)\n\t{\n\t\tFD_SET(tv->qwdsocket, &socketset);\n\t\tif (tv->sourcesock >= m)\n\t\t\tm = tv->sourcesock+1;\n\t}\n\n#ifndef _WIN32\n\t#ifndef STDIN\n\t\t#define STDIN 0\n\t#endif\n\tFD_SET(STDIN, &socketset);\n\tif (STDIN >= m)\n\t\tm = STDIN+1;\n#endif\n\n\ttimeout.tv_sec = 100/1000;\n\ttimeout.tv_usec = (100%1000)*1000;\n\n\tselect(m, &socketset, NULL, NULL, &timeout);\n\n#ifdef _WIN32\n\tfor (;;)\n\t{\n\t\tchar buffer[8192];\n\t\tchar *result;\n\t\tchar c;\n\n\t\tif (!kbhit())\n\t\t\tbreak;\n\t\tc = getch();\n\n\t\tif (c == '\\n' || c == '\\r')\n\t\t{\n\t\t\tprintf(\"\\n\");\n\t\t\tif (tv->inputlength)\n\t\t\t{\n\t\t\t\ttv->commandinput[tv->inputlength] = '\\0';\n\t\t\t\tresult = Rcon_Command(tv->cluster, tv, tv->commandinput, buffer, sizeof(buffer), true);\n\t\t\t\tprintf(\"%s\", result);\n\t\t\t\ttv->inputlength = 0;\n\t\t\t\ttv->commandinput[0] = '\\0';\n\t\t\t}\n\t\t}\n\t\telse if (c == '\\b')\n\t\t{\n\t\t\tif (tv->inputlength > 0)\n\t\t\t{\n\t\t\t\ttv->inputlength--;\n\t\t\t\ttv->commandinput[tv->inputlength] = '\\0';\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (tv->inputlength < sizeof(tv->commandinput)-1)\n\t\t\t{\n\t\t\t\ttv->commandinput[tv->inputlength++] = c;\n\t\t\t\ttv->commandinput[tv->inputlength] = '\\0';\n\t\t\t}\n\t\t}if (FD_ISSET(STDIN, &socketset))\n\t\tprintf(\"\\r%s \\b\", tv->commandinput);\n\t}\n#else\n\tif (FD_ISSET(STDIN, &socketset))\n\t{\n\t\tchar buffer[8192];\n\t\tchar *result;\n\t\ttv->inputlength = read (0, tv->commandinput, sizeof(tv->commandinput));\n\t\tif (tv->inputlength >= 1)\n\t\t{\n\t\t\ttv->commandinput[tv->inputlength-1] = 0;        // rip off the /n and terminate\n\n\t\t\tif (tv->inputlength)\n\t\t\t{\n\t\t\t\ttv->commandinput[tv->inputlength] = '\\0';\n\t\t\t\tresult = Rcon_Command(tv, tv->commandinput, buffer, sizeof(buffer), true);\n\t\t\t\tprintf(\"%s\", result);\n\t\t\t\ttv->inputlength = 0;\n\t\t\t\ttv->commandinput[0] = '\\0';\n\t\t\t}\n\t\t}\n\t}\n#endif\n}\n*/\n\nvoid Trim(char *s)\n{\n\tchar *f;\n\tf = s;\n\twhile(*f <= ' ' && *f > '\\0')\n\t\tf++;\n\twhile(*f > ' ')\n\t\t*s++ = *f++;\n\t*s = '\\0';\n}\n\nqboolean QTV_ConnectStream(sv_t *qtv, char *serverurl)\n{\n\tif (qtv->sourcesock != INVALID_SOCKET)\n\t{\n\t\tclosesocket(qtv->sourcesock);\n\t\tqtv->sourcesock = INVALID_SOCKET;\n\t}\n\n\tif (qtv->sourcefile)\n\t{\n\t\tfclose(qtv->sourcefile);\n\t\tqtv->sourcefile = NULL;\n\t}\n\n\tmemcpy(qtv->server, serverurl, sizeof(qtv->server)-1);\n\tif (qtv->autodisconnect == AD_STATUSPOLL && !qtv->numviewers && !qtv->proxies)\n\t{\n\t\tchar *ip;\n\t\tip = Net_DiagnoseProtocol(qtv);\n\t\tif (!NET_StringToAddr(ip, &qtv->serveraddress, 27500))\n\t\t{\n\t\t\tSys_Printf(qtv->cluster, \"Stream %i: Unable to resolve %s\\n\", qtv->streamid, ip);\n\t\t\treturn false;\n\t\t}\n\t\treturn true;\n\t}\n\n\t*qtv->map.serverinfo = '\\0';\n\tInfo_SetValueForStarKey(qtv->map.serverinfo, \"*version\",\t\"FTEQTV\",\tsizeof(qtv->map.serverinfo));\n\tInfo_SetValueForStarKey(qtv->map.serverinfo, \"*qtv\",\t\tQTV_VERSION_STRING,\tsizeof(qtv->map.serverinfo));\n\tInfo_SetValueForStarKey(qtv->map.serverinfo, \"hostname\",\tqtv->cluster->hostname,\tsizeof(qtv->map.serverinfo));\n\tInfo_SetValueForStarKey(qtv->map.serverinfo, \"maxclients\",\t\"99\",\tsizeof(qtv->map.serverinfo));\n\tif (!strncmp(qtv->server, \"file:\", 5))\n\t\tInfo_SetValueForStarKey(qtv->map.serverinfo, \"server\",\t\t\"file\",\tsizeof(qtv->map.serverinfo));\n\telse\n\t\tInfo_SetValueForStarKey(qtv->map.serverinfo, \"server\",\t\tqtv->server,\tsizeof(qtv->map.serverinfo));\n\n\tif (qtv->autodisconnect == AD_REVERSECONNECT)\n\t{\t//added because of paranoia rather than need. Should never occur.\n\t\tprintf(\"bug: autoclose==2\\n\");\n\t\tstrcpy(qtv->status, \"Network error\\n\");\n\t\treturn false;\n\t}\n\telse if (!Net_ConnectToServer(qtv))\n\t{\n\t\tSys_Printf(qtv->cluster, \"Stream %i: Couldn't connect (%s)\\n\", qtv->streamid, qtv->server);\n\t\treturn false;\n\t}\n\n\tif (qtv->sourcesock == INVALID_SOCKET)\n\t{\n\t\tqtv->parsetime = Sys_Milliseconds();\n//\t\tSys_Printf(qtv->cluster, \"Stream %i: Playing from file\\n\", qtv->streamid);\n\t}\n\telse\n\t{\n\t\tqtv->parsetime = Sys_Milliseconds() + qtv->cluster->anticheattime;\n\t}\n\treturn true;\n}\n\nvoid QTV_CleanupMap(sv_t *qtv)\n{\n\tint i;\n\n\t//free the bsp\n\tBSP_Free(qtv->map.bsp);\n\tqtv->map.bsp = NULL;\n\n\t//clean up entity state\n\tfor (i = 0; i < ENTITY_FRAMES; i++)\n\t{\n\t\tif (qtv->map.frame[i].ents)\n\t\t{\n\t\t\tfree(qtv->map.frame[i].ents);\n\t\t\tqtv->map.frame[i].ents = NULL;\n\t\t}\n\t\tif (qtv->map.frame[i].entnums)\n\t\t{\n\t\t\tfree(qtv->map.frame[i].entnums);\n\t\t\tqtv->map.frame[i].entnums = NULL;\n\t\t}\n\t}\n\tmemset(&qtv->map, 0, sizeof(qtv->map));\n}\n\nvoid QTV_DisconnectFromSource(sv_t *qtv)\n{\n\t\t// close the source handle\n\tif (qtv->sourcesock != INVALID_SOCKET)\n\t{\n\t\tif (qtv->usequakeworldprotocols)\n\t\t{\n\t\t\tchar dying[] = {clc_stringcmd, 'd', 'r', 'o', 'p', '\\0'};\n\t\t\tNetchan_Transmit (qtv->cluster, &qtv->netchan, sizeof(dying), dying);\n\t\t\tNetchan_Transmit (qtv->cluster, &qtv->netchan, sizeof(dying), dying);\n\t\t\tNetchan_Transmit (qtv->cluster, &qtv->netchan, sizeof(dying), dying);\n\t\t}\n\t\tclosesocket(qtv->sourcesock);\n\t\tqtv->sourcesock = INVALID_SOCKET;\n\t}\n\tif (qtv->sourcefile)\n\t{\n\t\tfclose(qtv->sourcefile);\n\t\tqtv->sourcefile = NULL;\n\t}\n\n\t//cancel downloads\n\tif (qtv->downloadfile)\n\t{\n\t\tfclose(qtv->downloadfile);\n\t\tqtv->downloadfile = NULL;\n\t\tunlink(qtv->downloadname);\n\t\t*qtv->downloadname = '\\0';\n\t}\n}\n\nvoid QTV_Cleanup(sv_t *qtv, qboolean leaveadmins)\n{\t//disconnects the stream\n\tviewer_t *v;\n\tcluster_t *cluster;\n\toproxy_t *prox;\n\toproxy_t *old;\n\n\tcluster = qtv->cluster;\n\n\t//set connected viewers to a different stream\n\tif (cluster->viewserver == qtv)\n\t\tcluster->viewserver = NULL;\n\tfor (v = cluster->viewers; v; v = v->next)\n\t{\n\t//warning fixme: honour leaveadmins\n\t\tif (v->server == qtv)\n\t\t{\t//they were watching this one\n\t\t\tQW_SetViewersServer(qtv->cluster, v, NULL);\n\t\t\tQW_SetMenu(v, MENU_NONE);\n\t\t\tQTV_SayCommand(cluster, v->server, v, \"menu\");\n\t\t\tQW_PrintfToViewer(v, \"Stream %s is closing\\n\", qtv->server);\n\t\t}\n\t}\n\n\tQTV_DisconnectFromSource(qtv);\n\n\tQTV_CleanupMap(qtv);\n\n\t//boot connected downstream proxies\n\tfor (prox = qtv->proxies; prox; )\n\t{\n\t\tif (prox->file)\n\t\t\tfclose(prox->file);\n\t\tif (prox->sock != INVALID_SOCKET)\n\t\t\tclosesocket(prox->sock);\n\t\told = prox;\n\t\tprox = prox->next;\n\t\tfree(old);\n\t\tcluster->numproxies--;\n\t}\n}\n\nvoid QTV_ShutdownStream(sv_t *qtv)\n{\n\tsv_t *peer;\n\tcluster_t *cluster;\n\tSys_Printf(qtv->cluster, \"Stream %i: Closing source %s\\n\", qtv->streamid, qtv->server);\n\n\tQTV_Cleanup(qtv, false);\n\n\t//unlink it\n\tcluster = qtv->cluster;\n\tif (cluster->servers == qtv)\n\t\tcluster->servers = qtv->next;\n\telse\n\t{\n\t\tfor (peer = cluster->servers; peer->next; peer = peer->next)\n\t\t{\n\t\t\tif (peer->next == qtv)\n\t\t\t{\n\t\t\t\tpeer->next = qtv->next;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\tfree(qtv);\n\tcluster->numservers--;\n}\n\n\n\n\n\n\n\n\n\nvoid SendClientCommand(sv_t *qtv, char *fmt, ...)\n{\n\tva_list\t\targptr;\n\tchar buf[1024];\n\n\tva_start (argptr, fmt);\n\tvsnprintf (buf, sizeof(buf), fmt, argptr);\n\tva_end (argptr);\n\n\tWriteByte(&qtv->netchan.message, clc_stringcmd);\n\tWriteString(&qtv->netchan.message, buf);\n}\n\n\n\n\n\n\nvoid ChooseFavoriteTrack(sv_t *tv)\n{\n\tint frags, best, pnum;\n\tchar buffer[64];\n\n\tfrags = -10000;\n\tbest = -1;\n\tif (tv->controller || tv->proxyplayer)\n\t\tbest = tv->map.trackplayer;\n\telse\n\t{\n\t\tfor (pnum = 0; pnum < MAX_CLIENTS; pnum++)\n\t\t{\n\t\t\tif (*tv->map.players[pnum].userinfo && !atoi(Info_ValueForKey(tv->map.players[pnum].userinfo, \"*spectator\", buffer, sizeof(buffer))))\n\t\t\t{\n\t\t\t\tif (tv->map.thisplayer == pnum)\n\t\t\t\t\tcontinue;\n\t\t\t\tif (frags < tv->map.players[pnum].frags)\n\t\t\t\t{\n\t\t\t\t\tbest = pnum;\n\t\t\t\t\tfrags = tv->map.players[pnum].frags;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tif (best != tv->map.trackplayer)\n\t{\n\t\tSendClientCommand (tv, \"ptrack %i\\n\", best);\n\t\ttv->map.trackplayer = best;\n\n\t\tif (tv->usequakeworldprotocols)\n\t\t\tQW_StreamStuffcmd(tv->cluster, tv, \"track %i\\n\", best);\n\t}\n}\n\n\n\n\n\n\nstatic const unsigned char chktbl[1024] = {\n0x78,0xd2,0x94,0xe3,0x41,0xec,0xd6,0xd5,0xcb,0xfc,0xdb,0x8a,0x4b,0xcc,0x85,0x01,\n0x23,0xd2,0xe5,0xf2,0x29,0xa7,0x45,0x94,0x4a,0x62,0xe3,0xa5,0x6f,0x3f,0xe1,0x7a,\n0x64,0xed,0x5c,0x99,0x29,0x87,0xa8,0x78,0x59,0x0d,0xaa,0x0f,0x25,0x0a,0x5c,0x58,\n0xfb,0x00,0xa7,0xa8,0x8a,0x1d,0x86,0x80,0xc5,0x1f,0xd2,0x28,0x69,0x71,0x58,0xc3,\n0x51,0x90,0xe1,0xf8,0x6a,0xf3,0x8f,0xb0,0x68,0xdf,0x95,0x40,0x5c,0xe4,0x24,0x6b,\n0x29,0x19,0x71,0x3f,0x42,0x63,0x6c,0x48,0xe7,0xad,0xa8,0x4b,0x91,0x8f,0x42,0x36,\n0x34,0xe7,0x32,0x55,0x59,0x2d,0x36,0x38,0x38,0x59,0x9b,0x08,0x16,0x4d,0x8d,0xf8,\n0x0a,0xa4,0x52,0x01,0xbb,0x52,0xa9,0xfd,0x40,0x18,0x97,0x37,0xff,0xc9,0x82,0x27,\n0xb2,0x64,0x60,0xce,0x00,0xd9,0x04,0xf0,0x9e,0x99,0xbd,0xce,0x8f,0x90,0x4a,0xdd,\n0xe1,0xec,0x19,0x14,0xb1,0xfb,0xca,0x1e,0x98,0x0f,0xd4,0xcb,0x80,0xd6,0x05,0x63,\n0xfd,0xa0,0x74,0xa6,0x86,0xf6,0x19,0x98,0x76,0x27,0x68,0xf7,0xe9,0x09,0x9a,0xf2,\n0x2e,0x42,0xe1,0xbe,0x64,0x48,0x2a,0x74,0x30,0xbb,0x07,0xcc,0x1f,0xd4,0x91,0x9d,\n0xac,0x55,0x53,0x25,0xb9,0x64,0xf7,0x58,0x4c,0x34,0x16,0xbc,0xf6,0x12,0x2b,0x65,\n0x68,0x25,0x2e,0x29,0x1f,0xbb,0xb9,0xee,0x6d,0x0c,0x8e,0xbb,0xd2,0x5f,0x1d,0x8f,\n0xc1,0x39,0xf9,0x8d,0xc0,0x39,0x75,0xcf,0x25,0x17,0xbe,0x96,0xaf,0x98,0x9f,0x5f,\n0x65,0x15,0xc4,0x62,0xf8,0x55,0xfc,0xab,0x54,0xcf,0xdc,0x14,0x06,0xc8,0xfc,0x42,\n0xd3,0xf0,0xad,0x10,0x08,0xcd,0xd4,0x11,0xbb,0xca,0x67,0xc6,0x48,0x5f,0x9d,0x59,\n0xe3,0xe8,0x53,0x67,0x27,0x2d,0x34,0x9e,0x9e,0x24,0x29,0xdb,0x69,0x99,0x86,0xf9,\n0x20,0xb5,0xbb,0x5b,0xb0,0xf9,0xc3,0x67,0xad,0x1c,0x9c,0xf7,0xcc,0xef,0xce,0x69,\n0xe0,0x26,0x8f,0x79,0xbd,0xca,0x10,0x17,0xda,0xa9,0x88,0x57,0x9b,0x15,0x24,0xba,\n0x84,0xd0,0xeb,0x4d,0x14,0xf5,0xfc,0xe6,0x51,0x6c,0x6f,0x64,0x6b,0x73,0xec,0x85,\n0xf1,0x6f,0xe1,0x67,0x25,0x10,0x77,0x32,0x9e,0x85,0x6e,0x69,0xb1,0x83,0x00,0xe4,\n0x13,0xa4,0x45,0x34,0x3b,0x40,0xff,0x41,0x82,0x89,0x79,0x57,0xfd,0xd2,0x8e,0xe8,\n0xfc,0x1d,0x19,0x21,0x12,0x00,0xd7,0x66,0xe5,0xc7,0x10,0x1d,0xcb,0x75,0xe8,0xfa,\n0xb6,0xee,0x7b,0x2f,0x1a,0x25,0x24,0xb9,0x9f,0x1d,0x78,0xfb,0x84,0xd0,0x17,0x05,\n0x71,0xb3,0xc8,0x18,0xff,0x62,0xee,0xed,0x53,0xab,0x78,0xd3,0x65,0x2d,0xbb,0xc7,\n0xc1,0xe7,0x70,0xa2,0x43,0x2c,0x7c,0xc7,0x16,0x04,0xd2,0x45,0xd5,0x6b,0x6c,0x7a,\n0x5e,0xa1,0x50,0x2e,0x31,0x5b,0xcc,0xe8,0x65,0x8b,0x16,0x85,0xbf,0x82,0x83,0xfb,\n0xde,0x9f,0x36,0x48,0x32,0x79,0xd6,0x9b,0xfb,0x52,0x45,0xbf,0x43,0xf7,0x0b,0x0b,\n0x19,0x19,0x31,0xc3,0x85,0xec,0x1d,0x8c,0x20,0xf0,0x3a,0xfa,0x80,0x4d,0x2c,0x7d,\n0xac,0x60,0x09,0xc0,0x40,0xee,0xb9,0xeb,0x13,0x5b,0xe8,0x2b,0xb1,0x20,0xf0,0xce,\n0x4c,0xbd,0xc6,0x04,0x86,0x70,0xc6,0x33,0xc3,0x15,0x0f,0x65,0x19,0xfd,0xc2,0xd3\n};\n\n\nunsigned char\tCOM_BlockSequenceCRCByte (void *base, int length, int sequence)\n{\n\tunsigned short crc;\n\tconst unsigned char\t*p;\n\tunsigned char chkb[60 + 4];\n\n\tp = chktbl + (sequence % (sizeof(chktbl) - 4));\n\n\tif (length > 60)\n\t\tlength = 60;\n\tmemcpy (chkb, base, length);\n\n\tchkb[length] = (sequence & 0xff) ^ p[0];\n\tchkb[length+1] = p[1];\n\tchkb[length+2] = ((sequence>>8) & 0xff) ^ p[2];\n\tchkb[length+3] = p[3];\n\n\tlength += 4;\n\n\tcrc = QCRC_Block(chkb, length);\n\n\tcrc &= 0xff;\n\n\treturn crc;\n}\nvoid SetMoveCRC(sv_t *qtv, netmsg_t *msg)\n{\n\tchar *outbyte;\n\toutbyte = (char*)msg->data + msg->startpos+1;\n\n\t*outbyte = COM_BlockSequenceCRCByte(\n\t\t\t\toutbyte+1, msg->cursize - (msg->startpos+2),\n\t\t\t\tqtv->netchan.outgoing_sequence);\n}\n\n\n\n\n\nvoid QTV_ParseQWStream(sv_t *qtv)\n{\n\tchar buffer[1500];\n\tnetadr_t from;\n\tunsigned int fromlen;\n\tint readlen;\n\tnetmsg_t msg;\n\tfromlen = sizeof(from.sockaddr);\t//bug: this won't work on (free)bsd\n\n\tfor (;;)\n\t{\n\t\tfrom.tcpcon = NULL;\n\t\treadlen = recvfrom(qtv->sourcesock, buffer, sizeof(buffer)-1, 0, (struct sockaddr*)&from.sockaddr, &fromlen);\n\t\tif (readlen < 0)\n\t\t{\n\t\t\t//FIXME: Check for error\n\t\t\tbreak;\n\t\t}\n\t\tif (readlen > sizeof(buffer)-1)\n\t\t\tbreak;\t//oversized!\n\n\t\tbuffer[readlen] = 0;\n\t\tif (*(int*)buffer == -1)\n\t\t{\n\t\t\tif (buffer[4] == 'c')\n\t\t\t{\t//got a challenge\n\t\t\t\tstrcpy(qtv->status, \"Attemping connection\\n\");\n\t\t\t\tqtv->challenge = atoi(buffer+5);\n\t\t\t\tif (qtv->controller)\n\t\t\t\t\tsprintf(buffer, \"connect %i %i %i \\\"%s\\\\*qtv\\\\1\\\\Qizmo\\\\2.9 notimer\\\"\", 28, qtv->qport, qtv->challenge, qtv->controller->userinfo);\n\t\t\t\telse if (qtv->proxyplayer)\n\t\t\t\t\tsprintf(buffer, \"connect %i %i %i \\\"%s\\\\name\\\\%s\\\"\", 28, qtv->qport, qtv->challenge, \"\\\\*ver\\\\fteqtv\\\\spectator\\\\0\\\\rate\\\\10000\", qtv->cluster->hostname);\n\t\t\t\telse\n\t\t\t\t\tsprintf(buffer, \"connect %i %i %i \\\"%s\\\\name\\\\%s\\\"\", 28, qtv->qport, qtv->challenge, \"\\\\*ver\\\\fteqtv\\\\spectator\\\\1\\\\rate\\\\10000\", qtv->cluster->hostname);\n\t\t\t\tNetchan_OutOfBandSocket(qtv->cluster, qtv->sourcesock, &qtv->serveraddress, strlen(buffer), buffer);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (buffer[4] == 'n')\n\t\t\t{\n\t\t\t\tstrlcpy(qtv->status, buffer+5, sizeof(qtv->status));\n\t\t\t\tSys_Printf(qtv->cluster, \"%s: %s\", qtv->server, buffer+5);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (buffer[4] == 'j')\n\t\t\t{\n\t\t\t\tstrcpy(qtv->status, \"Waiting for gamestate\\n\");\n\t\t\t\tNetchan_Setup(qtv->sourcesock, &qtv->netchan, qtv->serveraddress, qtv->qport, true);\n\n\t\t\t\tqtv->map.trackplayer = -1;\n\n\t\t\t\tqtv->isconnected = true;\n\t\t\t\tqtv->timeout = qtv->curtime + UDPTIMEOUT_LENGTH;\n\t\t\t\tSendClientCommand(qtv, \"new\");\n\t\t\t\tSys_Printf(qtv->cluster, \"Stream %i: Connected!\\n\", qtv->streamid);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tSys_Printf(qtv->cluster, \"Stream %i: %s: unrecognized connectionless packet:\\n%s\\n\", qtv->streamid, qtv->server, buffer+4);\n\t\t\tcontinue;\n\t\t}\n\t\tmemset(&msg, 0, sizeof(msg));\n\t\tmsg.cursize = readlen;\n\t\tmsg.data = buffer;\n\t\tmsg.maxsize = readlen;\n\t\tqtv->timeout = qtv->curtime + UDPTIMEOUT_LENGTH;\n\t\tif (!Netchan_Process(&qtv->netchan, &msg))\n\t\t\tcontinue;\n\t\tParseMessage(qtv, (char*)msg.data + msg.readpos, msg.cursize - msg.readpos, dem_all, -1);\n\n\t\tqtv->oldpackettime = qtv->nextpackettime;\n\t\tqtv->nextpackettime = qtv->parsetime;\n\t\tqtv->parsetime = qtv->curtime;\n\n\t\tif (qtv->simtime < qtv->oldpackettime)\n\t\t\tqtv->simtime = qtv->oldpackettime;\t//too old\n\n\t\tif (qtv->controller)\n\t\t{\n\t\t\tqtv->controller->maysend = true;\n//if (qtv->controller->netchan.outgoing_sequence != qtv->controller->netchan.incoming_sequence)\n//printf(\"bug is here\\n\");\n\t\t}\n\t}\n}\n\n#ifdef COMMENTARY\n#include <speex/speex.h>\n#endif\n\nvoid QTV_CollectCommentry(sv_t *qtv)\n{\n#define usespeex 0\n#ifdef COMMENTARY\n\tint samps;\n\tunsigned char buffer[8192+6];\n\tunsigned char *uchar;\n\tsigned char *schar;\n\tint bytesleft;\n\tif (!qtv->comentrycapture)\n\t{\n\t\tif (0)\n\t\t{\n//\t\t\tif (usespeex)\n//\t\t\t\tqtv->comentrycapture = SND_InitCapture(11025, 16);\n//\t\t\telse\n\t\t\t\tqtv->comentrycapture = SND_InitCapture(11025, 8);\n\t\t}\n\t\treturn;\n\t}\n\n\twhile(1)\n\t{\n\t\t//the protocol WILL be different. Don't add compatibility for this code.\n\t\tbuffer[0] = 0;\n\t\tbuffer[1] = dem_audio;\n\t\tbuffer[2] = 255;\n\t\tbuffer[3] = 255;\n\t\tbuffer[4] = 8;\n\t\tbuffer[5] = 11*5;\n\n\t/*\tif (usespeex)\n\t\t{\n\n\t\t\tSpeexBits bits;\n\t\t\tvoid *enc_state;\n\n\t\t\tint frame_size;\n\n\t\t\tspx_int16_t pcmdata[8192/2];\n\n\t\t\tsamps=qtv->comentrycapture->update(qtv->comentrycapture, 2048, (char*)pcmdata);\n\n\n\t\t\tspeex_bits_init(&bits);\n\n\t\t\tenc_state = speex_encoder_init(&speex_nb_mode);\n\n\n\t\t\tspeex_encoder_ctl(enc_state,SPEEX_GET_FRAME_SIZE,&frame_size);\n\n\n\t\t\tspeex_bits_reset(&bits);\n\n\t\t\tspeex_encode_int(enc_state, (spx_int16_t*)pcmdata, &bits);\n\n\t\t\tsamps = speex_bits_write(&bits, buffer+6, sizeof(buffer)-6);\n\n\n\t\t\tspeex_bits_destroy(&bits);\n\n\t\t\tspeex_encoder_destroy(enc_state);\n\n\t\t}\n\t\telse*/\n\t\t{\n\t\t\tsamps=qtv->comentrycapture->update(qtv->comentrycapture, 2048, buffer+6);\n\n\t\t\tbytesleft = samps;\n\t\t\tschar = buffer+6;\n\t\t\tuchar = buffer+6;\n\t\t\twhile(bytesleft-->0)\n\t\t\t{\n\t\t\t\t*schar++ = *uchar++ - 128;\n\t\t\t}\n\t\t}\n\n\t\tbuffer[2] = samps&255;\n\t\tbuffer[3] = samps>>8;\n\n\t\tif (samps)\n\t\t\tSV_ForwardStream(qtv, buffer, 6 + samps);\n\n\t\tif (samps < 64)\n\t\t\tbreak;\n\t}\n#endif\n}\n\nvoid QTV_Run(sv_t *qtv)\n{\n\tint from;\n\tint to;\n\tint lengthofs;\n\tunsigned int length;\n\tunsigned char *buffer;\n\tint oldcurtime;\n\tint packettime;\n\n\tif (qtv->numviewers == 0 && qtv->proxies == NULL)\n\t{\n\t\tif (qtv->autodisconnect == AD_WHENEMPTY)\n\t\t{\n\t\t\tSys_Printf(qtv->cluster, \"Stream %i: %s became inactive\\n\", qtv->streamid, qtv->server);\n\t\t\tqtv->errored = ERR_DROP;\n\t\t}\n\t\telse if (qtv->autodisconnect == AD_STATUSPOLL && qtv->isconnected)\n\t\t{\n\t\t\t/*switch to status polling instead of packet spamming*/\n\t\t\tqtv->errored = ERR_RECONNECT;\n\t\t}\n\t}\n\tif (qtv->errored)\n\t{\n\t\tif (qtv->errored == ERR_DISABLED)\n\t\t{\n\t\t\t//this keeps any connected proxies ticking over.\n\t\t\t//probably we should drop them instead - the connection will only be revived if one of them reconnects.\n\t\t\tSV_ForwardStream(qtv, NULL, 0);\n\t\t\treturn;\n\t\t}\n\t\telse if (qtv->errored == ERR_PERMANENT)\n\t\t{\n\t\t\tQTV_Cleanup(qtv, false);\t//frees various pieces of context\n\t\t\tqtv->errored = ERR_DISABLED;\n\t\t\treturn;\n\t\t}\n\t\telse if (qtv->errored == ERR_DROP)\n\t\t{\n\t\t\tQTV_ShutdownStream(qtv);\t//destroys the stream\n\t\t\treturn;\n\t\t}\n\t}\n\n\n\n//we will read out as many packets as we can until we're up to date\n//note: this can cause real issues when we're overloaded for any length of time\n//each new packet comes with a leading msec byte (msecs from last packet)\n//then a type, an optional destination mask, and a 4byte size.\n//the 4 byte size is probably excessive, a short would do.\n//some of the types have thier destination mask encoded inside the type byte, yielding 8 types, and 32 max players.\n\n\n//if we've no got enough data to read a new packet, we print a message and wait an extra two seconds. this will add a pause, connected clients will get the same pause, and we'll just try to buffer more of the game before playing.\n//we'll stay 2 secs behind when the tcp stream catches up, however. This could be bad especially with long up-time.\n//All timings are in msecs, which is in keeping with the mvd times, but means we might have issues after 72 or so days.\n//the following if statement will reset the parse timer. It might cause the game to play too soon, the buffersize checks in the rest of the function will hopefully put it back to something sensible.\n\n\toldcurtime = qtv->curtime;\n\tqtv->curtime = Sys_Milliseconds();\n\tif (oldcurtime > qtv->curtime)\n\t{\n\t\tSys_Printf(qtv->cluster, \"Time wrapped\\n\");\n\t\tqtv->parsetime = qtv->curtime;\n\t}\n\n\n\tif (qtv->errored == ERR_PAUSED)\n\t{\n\t\tif (!qtv->parsingconnectiondata)\n\t\t\tqtv->parsetime = qtv->curtime;\n\t}\n\n\tif (qtv->errored == ERR_RECONNECT)\n\t{\n\t\tqtv->buffersize = 0;\n\t\tqtv->forwardpoint = 0;\n\t\tQTV_DisconnectFromSource(qtv);\n\t\tqtv->isconnected = 0;\n\t\tqtv->errored = ERR_NONE;\n\t\tqtv->nextconnectattempt = qtv->curtime;\t//make the reconnect happen _now_\n\t}\n\n\n\tif (qtv->sourcetype == SRC_UDP)\n\t{\n\t\tqtv->simtime += qtv->curtime - oldcurtime;\n\n\t\tif (qtv->simtime > qtv->nextpackettime)\n\t\t\tqtv->simtime = qtv->nextpackettime;\t//too old\n\n\t\tif (!qtv->isconnected && (qtv->curtime >= qtv->nextconnectattempt || qtv->curtime < qtv->nextconnectattempt - (UDPRECONNECT_TIME+STATUSPOLL_TIME)))\n\t\t{\n\t\t\tif (qtv->errored == ERR_DISABLED)\n\t\t\t{\n\t\t\t\tstrcpy(qtv->status, \"Given up connecting\\n\");\n\t\t\t}\n\t\t\telse if (qtv->autodisconnect == AD_STATUSPOLL)\n\t\t\t{\n\t\t\t\tQTV_DisconnectFromSource(qtv);\n\t\t\t\tNetchan_OutOfBand(qtv->cluster, qtv->serveraddress, 13, \"status\\n\");\n\t\t\t\tqtv->nextconnectattempt = qtv->curtime + STATUSPOLL_TIME;\n\t\t\t\treturn;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tstrcpy(qtv->status, \"Attemping challenge\\n\");\n\t\t\t\tif (qtv->sourcesock == INVALID_SOCKET && !qtv->sourcefile)\n\t\t\t\t{\n\t\t\t\t\tif (!QTV_ConnectStream(qtv, qtv->server))\t//reconnect it\n\t\t\t\t\t{\n\t\t\t\t\t\tqtv->errored = ERR_PERMANENT;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (qtv->errored == ERR_NONE)\n\t\t\t\t\tNetchan_OutOfBandSocket(qtv->cluster, qtv->sourcesock, &qtv->serveraddress, 13, \"getchallenge\\n\");\n\t\t\t}\n\t\t\tqtv->nextconnectattempt = qtv->curtime + UDPRECONNECT_TIME;\n\t\t}\n\t\tif (qtv->sourcesock == INVALID_SOCKET && !qtv->sourcefile)\n\t\t\treturn;\n\n\t\tQTV_ParseQWStream(qtv);\n\n\t\tif (qtv->isconnected)\n\t\t{\n\t\t\tchar buffer[128];\n\t\t\tnetmsg_t msg;\n\t\t\tmemset(&msg, 0, sizeof(msg));\n\t\t\tmsg.data = buffer;\n\t\t\tmsg.maxsize = sizeof(buffer);\n\n\t\t\tif (qtv->curtime >= qtv->timeout || qtv->curtime < qtv->timeout - UDPTIMEOUT_LENGTH*2)\n\t\t\t{\n\t\t\t\tSys_Printf(qtv->cluster, \"Stream %i: Timeout\\n\", qtv->streamid);\n\t\t\t\tqtv->isconnected = false;\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (qtv->controller && !qtv->controller->netchan.isnqprotocol)\n\t\t\t{\n\t\t\t\tqtv->netchan.outgoing_sequence = qtv->controller->netchan.incoming_sequence;\n\t\t\t\tif (qtv->maysend)\n\t\t\t\t{\n\t\t\t\t\tqtv->maysend = false;\n\t\t\t\t\tqtv->packetratelimiter = qtv->curtime;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tqtv->packetratelimiter = qtv->curtime + 1;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (qtv->curtime < qtv->packetratelimiter - UDPPACKETINTERVAL*2)\n\t\t\t\t\tqtv->packetratelimiter = qtv->curtime;\n\t\t\t}\n\t\t\tif (qtv->curtime >= qtv->packetratelimiter)\n\t\t\t{\n\t\t\t\tif (qtv->curtime >= qtv->nextsendpings || qtv->curtime < qtv->nextsendpings - PINGSINTERVAL_TIME*2)\n\t\t\t\t{\n\t\t\t\t\tqtv->nextsendpings = qtv->curtime + PINGSINTERVAL_TIME;\n\t\t\t\t\tSendClientCommand(qtv, \"pings\\n\");\n\n\t\t\t\t}\n\t\t\t\tChooseFavoriteTrack(qtv);\n\n\t\t\t\t//if we froze somehow, don't speedcheat by a burst of 10000+ packets while we were frozen in a debugger or disk spinup or whatever\n\t\t\t\tif (qtv->packetratelimiter < qtv->curtime - UDPPACKETINTERVAL*2)\n\t\t\t\t\tqtv->packetratelimiter = qtv->curtime;\n\n\t\t\t\tif (qtv->map.trackplayer >= 0)\n\t\t\t\t{\n\t\t\t\t\tqtv->packetratelimiter += UDPPACKETINTERVAL;\n\n\t\t\t\t\tWriteByte(&msg, clc_tmove);\n\t\t\t\t\tWriteShort(&msg, qtv->map.players[qtv->map.trackplayer].current.origin[0]);\n\t\t\t\t\tWriteShort(&msg, qtv->map.players[qtv->map.trackplayer].current.origin[1]);\n\t\t\t\t\tWriteShort(&msg, qtv->map.players[qtv->map.trackplayer].current.origin[2]);\n\t\t\t\t}\n\t\t\t\telse if (qtv->controller)\n\t\t\t\t{\n\t\t\t\t\tqtv->packetratelimiter += UDPPACKETINTERVAL;\n\n\t\t\t\t\tif (qtv->controller->netchan.isnqprotocol)\n\t\t\t\t\t{\n\t\t\t\t\t\tmemcpy(&qtv->controller->ucmds[0], &qtv->controller->ucmds[1], sizeof(qtv->controller->ucmds[0]));\n\t\t\t\t\t\tmemcpy(&qtv->controller->ucmds[1], &qtv->controller->ucmds[2], sizeof(qtv->controller->ucmds[0]));\n\t\t\t\t\t\tqtv->controller->ucmds[2].msec = UDPPACKETINTERVAL;\n\t\t\t\t\t}\n\n\t\t\t\t\tWriteByte(&msg, clc_tmove);\n\t\t\t\t\tWriteShort(&msg, qtv->controller->origin[0]);\n\t\t\t\t\tWriteShort(&msg, qtv->controller->origin[1]);\n\t\t\t\t\tWriteShort(&msg, qtv->controller->origin[2]);\n\n/*\t\t\t\t\tqtv->controller->ucmds[0].angles[1] = qtv->curtime*120;\n\t\t\t\t\tqtv->controller->ucmds[1].angles[1] = qtv->curtime*120;\n\t\t\t\t\tqtv->controller->ucmds[2].angles[1] = qtv->curtime*120;\n*/\n\t\t\t\t\tmsg.startpos = msg.cursize;\n\t\t\t\t\tWriteByte(&msg, clc_move);\n\t\t\t\t\tWriteByte(&msg, 0);\n\t\t\t\t\tWriteByte(&msg, 0);\n\t\t\t\t\tWriteDeltaUsercmd(&msg, &nullcmd, &qtv->controller->ucmds[0]);\n\t\t\t\t\tWriteDeltaUsercmd(&msg, &qtv->controller->ucmds[0], &qtv->controller->ucmds[1]);\n\t\t\t\t\tWriteDeltaUsercmd(&msg, &qtv->controller->ucmds[1], &qtv->controller->ucmds[2]);\n\n\t\t\t\t\tSetMoveCRC(qtv, &msg);\n\t\t\t\t}\n\t\t\t\telse if (qtv->proxyplayer || qtv->map.trackplayer < 0)\n\t\t\t\t{\n\t\t\t\t\tusercmd_t *cmd[3];\n\t\t\t\t\tcmd[0] = &qtv->proxyplayerucmds[(qtv->proxyplayerucmdnum-2)%3];\n\t\t\t\t\tcmd[1] = &qtv->proxyplayerucmds[(qtv->proxyplayerucmdnum-1)%3];\n\t\t\t\t\tcmd[2] = &qtv->proxyplayerucmds[(qtv->proxyplayerucmdnum-0)%3];\n\n\t\t\t\t\tcmd[2]->angles[0] = (qtv->proxyplayerangles[0]/360)*0x10000;\n\t\t\t\t\tcmd[2]->angles[1] = (qtv->proxyplayerangles[1]/360)*0x10000;\n\t\t\t\t\tcmd[2]->angles[2] = (qtv->proxyplayerangles[2]/360)*0x10000;\n\t\t\t\t\tcmd[2]->buttons = qtv->proxyplayerbuttons & 255;\n\t\t\t\t\tcmd[2]->forwardmove = (qtv->proxyplayerbuttons & (1<<8))?800:0 + (qtv->proxyplayerbuttons & (1<<9))?-800:0;\n\t\t\t\t\tcmd[2]->sidemove = (qtv->proxyplayerbuttons & (1<<11))?800:0 + (qtv->proxyplayerbuttons & (1<<10))?-800:0;\n\t\t\t\t\tcmd[2]->msec = qtv->curtime - qtv->packetratelimiter;\n\t\t\t\t\tcmd[2]->impulse = qtv->proxyplayerimpulse;\n\t\t\t\t\tif (cmd[2]->msec < 13)\n\t\t\t\t\t\tcmd[2]->msec = 13;\n\t\t\t\t\tqtv->packetratelimiter += cmd[2]->msec;\n\t\t\t\t\tqtv->proxyplayerimpulse = 0;\n\n\n\t\t\t\t\tmsg.startpos = msg.cursize;\n\t\t\t\t\tWriteByte(&msg, clc_move);\n\t\t\t\t\tWriteByte(&msg, 0);\n\t\t\t\t\tWriteByte(&msg, 0);\n\t\t\t\t\tWriteDeltaUsercmd(&msg, &nullcmd, cmd[0]);\n\t\t\t\t\tWriteDeltaUsercmd(&msg, cmd[0], cmd[1]);\n\t\t\t\t\tWriteDeltaUsercmd(&msg, cmd[1], cmd[2]);\n\t\t\t\t\tqtv->proxyplayerucmdnum++;\n\n\t\t\t\t\tSetMoveCRC(qtv, &msg);\n\t\t\t\t}\n\n\t\t\t\tto = qtv->netchan.outgoing_sequence & (ENTITY_FRAMES-1);\n\t\t\t\tfrom = qtv->netchan.incoming_sequence & (ENTITY_FRAMES-1);\n\t\t\t\tif (qtv->map.frame[from].numents)\n\t\t\t\t{\n\t\t\t\t\t//remember which one we came from\n\t\t\t\t\tqtv->map.frame[to].oldframe = from;\n\n\t\t\t\t\tWriteByte(&msg, clc_delta);\n\t\t\t\t\tWriteByte(&msg, qtv->map.frame[to].oldframe);\t//let the server know\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tqtv->map.frame[to].oldframe = -1;\n\n\t\t\t\tqtv->map.frame[to].numents = 0;\n\n\t\t\t\tNetchan_Transmit(qtv->cluster, &qtv->netchan, msg.cursize, msg.data);\n\t\t\t}\n\t\t}\n\t\treturn;\n\t}\n\telse\n\t\tqtv->simtime = qtv->curtime;\n\n\n\tif (qtv->sourcesock == INVALID_SOCKET && !qtv->sourcefile)\n\t{\n\t\tif (qtv->errored == ERR_DISABLED)\n\t\t\treturn;\n\n\t\tif (qtv->sourcetype == SRC_DEMODIR || qtv->curtime >= qtv->nextconnectattempt || qtv->curtime < qtv->nextconnectattempt - RECONNECT_TIME*2)\n\t\t{\n\t\t\tif (qtv->autodisconnect == AD_REVERSECONNECT)\t//2 means a reverse connection\n\t\t\t{\n\t\t\t\tqtv->errored = ERR_DROP;\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (!QTV_ConnectStream(qtv, qtv->server))\t//reconnect it\n\t\t\t{\n\t\t\t\tqtv->errored = ERR_PERMANENT;\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n\n\n//\tSV_FindProxies(qtv->tcpsocket, qtv->cluster, qtv);\t//look for any other proxies wanting to muscle in on the action.\n\n\tif (qtv->sourcefile || qtv->sourcesock != INVALID_SOCKET)\n\t{\n\t\tif (!Net_ReadStream(qtv))\n\t\t{\t//if we have an error reading it\n\t\t\t//if it's valid, give up\n\t\t\t//what should we do here?\n\t\t\t//obviously, we need to keep reading the stream to keep things smooth\n\t\t}\n\n\t\tNet_WriteUpstream(qtv);\n\t}\n\n\n\tif (qtv->parsingqtvheader)\n\t{\n\t\tfloat svversion;\n\t\tint length;\n\t\tchar *start;\n\t\tchar *nl;\n\t\tchar *colon;\n\t\tchar *end;\n\t\tchar value[128];\n\t\tchar challenge[128];\n\t\tchar authmethod[128];\n\n//\t\tqtv->buffer[qtv->buffersize] = 0;\n//\t\tSys_Printf(qtv->cluster, \"msg: ---%s---\\n\", qtv->buffer);\n\n\t\t*authmethod = 0;\n\n\t\tqtv->parsetime = qtv->curtime;\n\t\tlength = qtv->buffersize;\n\t\tif (length > 6)\n\t\t\tlength = 6;\n\t\tif (ustrncmp(qtv->buffer, \"QTVSV \", length))\n\t\t{\n\t\t\tSys_Printf(qtv->cluster, \"Stream %i: Server is not a QTV server (or is incompatible)\\n\", qtv->streamid);\n//printf(\"%i, %s\\n\", qtv->buffersize, qtv->buffer);\n\t\t\tqtv->errored = ERR_PERMANENT;\n\t\t\treturn;\n\t\t}\n\t\tif (length < 6)\n\t\t\treturn;\t//not ready yet\n\t\tend = (char*)qtv->buffer + qtv->buffersize - 1;\n\t\tfor (nl = (char*)qtv->buffer; nl < end; nl++)\n\t\t{\n\t\t\tif (nl[0] == '\\n' && nl[1] == '\\n')\n\t\t\t\tbreak;\n\t\t}\n\t\tif (nl == end)\n\t\t\treturn;\t//we need more header still\n\n\t\t//we now have a complete packet.\n\n\t\tsvversion = atof((char*)qtv->buffer + 6);\n\t\tif ((int)svversion != 1)\n\t\t{\n\t\t\tSys_Printf(qtv->cluster, \"Stream %i: QTV server doesn't support a compatible protocol version (returned %i)\\n\", qtv->streamid, atoi((char*)qtv->buffer + 6));\n\t\t\tqtv->errored = ERR_PERMANENT;\n\t\t\treturn;\n\t\t}\n\n\n\t\tqtv->upstreamacceptschat = svversion>=1.1;\n\t\tqtv->upstreamacceptsdownload = svversion>=1.1;\n\n\t\tlength = (nl - (char*)qtv->buffer) + 2;\n\t\tend = nl;\n\t\tnl[1] = '\\0';\n\t\tstart = strchr((char*)qtv->buffer, '\\n')+1;\n\n\t\twhile((nl = strchr(start, '\\n')))\n\t\t{\n\t\t\t*nl = '\\0';\n\t\t\tcolon = strchr(start, ':');\n\t\t\tif (colon)\n\t\t\t{\n\t\t\t\t*colon = '\\0';\n\t\t\t\tcolon++;\n\t\t\t\twhile (*colon == ' ')\n\t\t\t\t\tcolon++;\n\t\t\t\tCOM_ParseToken(colon, value, sizeof(value), NULL);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tcolon = \"\";\n\t\t\t\t*value = '\\0';\n\t\t\t}\n\n\n\t\t\t//read the notes at the top of this file for which messages to expect\n\t\t\tif (!strcmp(start, \"AUTH\"))\n\t\t\t\tstrcpy(authmethod, value);\n\t\t\telse if (!strcmp(start, \"CHALLENGE\"))\n\t\t\t\tstrcpy(challenge, colon);\n\t\t\telse if (!strcmp(start, \"COMPRESSION\"))\n\t\t\t{\t//we don't support compression, we didn't ask for it.\n\t\t\t\tSys_Printf(qtv->cluster, \"Stream %i: QTV server wrongly used compression\\n\", qtv->streamid);\n\t\t\t\tqtv->errored = ERR_PERMANENT;\n\t\t\t\treturn;\n\t\t\t}\n\t\t\telse if (!strcmp(start, \"PERROR\"))\n\t\t\t{\n\t\t\t\tSys_Printf(qtv->cluster, \"\\nStream %i: Server PERROR from %s: %s\\n\\n\", qtv->streamid, qtv->server, colon);\n\t\t\t\tqtv->errored = ERR_PERMANENT;\n\t\t\t\tqtv->buffersize = 0;\n\t\t\t\tqtv->forwardpoint = 0;\n\t\t\t\treturn;\n\t\t\t}\n\t\t\telse if (!strcmp(start, \"TERROR\") || !strcmp(start, \"ERROR\"))\n\t\t\t{\t//we don't support compression, we didn't ask for it.\n\t\t\t\tSys_Printf(qtv->cluster, \"\\nStream %i: Server TERROR from %s: %s\\n\\n\", qtv->streamid, qtv->server, colon);\n\t\t\t\tqtv->buffersize = 0;\n\t\t\t\tqtv->forwardpoint = 0;\n\n\t\t\t\tif (qtv->autodisconnect == AD_WHENEMPTY || qtv->autodisconnect == AD_REVERSECONNECT)\n\t\t\t\t\tqtv->errored = ERR_DROP;\t//if its a user registered stream, drop it immediatly\n\t\t\t\telse\n\t\t\t\t{\t//otherwise close the socket (this will result in a timeout and reconnect)\n\t\t\t\t\tif (qtv->sourcesock != INVALID_SOCKET)\n\t\t\t\t\t{\n\t\t\t\t\t\tclosesocket(qtv->sourcesock);\n\t\t\t\t\t\tqtv->sourcesock = INVALID_SOCKET;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\t\t\telse if (!strcmp(start, \"ASOURCE\"))\n\t\t\t{\n\t\t\t\tSys_Printf(qtv->cluster, \"SRC: %s\\n\", colon);\n\t\t\t}\n\t\t\telse if (!strcmp(start, \"ADEMO\"))\n\t\t\t{\n\t\t\t\tint size;\n\t\t\t\tsize = atoi(colon);\n\t\t\t\tcolon = strchr(colon, ':');\n\t\t\t\tif (!colon)\n\t\t\t\t\tcolon = \"\";\n\t\t\t\telse\n\t\t\t\t\tcolon = colon+1;\n\t\t\t\twhile(*colon == ' ')\n\t\t\t\t\tcolon++;\n\t\t\t\tif (size > 1024*1024)\n\t\t\t\t\tSys_Printf(qtv->cluster, \"DEMO: (%3imb) %s\\n\", size/(1024*1024), colon);\n\t\t\t\telse\n\t\t\t\t\tSys_Printf(qtv->cluster, \"DEMO: (%3ikb) %s\\n\", size/1024, colon);\n\t\t\t}\n\t\t\telse if (!strcmp(start, \"PRINT\"))\n\t\t\t{\n\t\t\t\tSys_Printf(qtv->cluster, \"Stream %i: QTV server: %s\\n\", qtv->streamid, colon);\n\t\t\t}\n\t\t\telse if (!strcmp(start, \"BEGIN\"))\n\t\t\t{\n\t\t\t\tqtv->parsingqtvheader = false;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tSys_Printf(qtv->cluster, \"DBG: QTV server responded with a %s key\\n\", start);\n\t\t\t}\n\n\t\t\tstart = nl+1;\n\t\t}\n\n\t\tqtv->buffersize -= length;\n\t\tmemmove(qtv->buffer, qtv->buffer + length, qtv->buffersize);\n\n\t\tif (qtv->serverquery)\n\t\t{\n\t\t\tSys_Printf(qtv->cluster, \"End of list\\n\");\n\t\t\tqtv->errored = ERR_DROP;\n\t\t\tqtv->buffersize = 0;\n\t\t\tqtv->forwardpoint = 0;\n\t\t\treturn;\n\t\t}\n\t\telse if (*authmethod)\n\t\t{\t//we need to send a challenge response now.\n\t\t\tNet_SendQTVConnectionRequest(qtv, authmethod, challenge);\n\t\t\treturn;\n\t\t}\n\t\telse if (qtv->parsingqtvheader)\n\t\t{\n\t\t\tSys_Printf(qtv->cluster, \"Stream %i: QTV server sent no begin command - assuming incompatible\\n\\n\", qtv->streamid);\n\t\t\tqtv->errored = ERR_PERMANENT;\n\t\t\tqtv->buffersize = 0;\n\t\t\tqtv->forwardpoint = 0;\n\t\t\treturn;\n\t\t}\n\n\t\tqtv->parsetime = Sys_Milliseconds() + qtv->cluster->anticheattime;\n\t\tif (!qtv->usequakeworldprotocols)\n\t\t\tSys_Printf(qtv->cluster, \"Stream %i: Connection established, buffering for %g seconds\\n\", qtv->streamid, qtv->cluster->anticheattime/1000.0f);\n\n\t\tSV_ForwardStream(qtv, qtv->buffer, qtv->forwardpoint);\n\t}\n\n\tQTV_CollectCommentry(qtv);\n\n\twhile (qtv->curtime >= qtv->parsetime)\n\t{\n\t\tif (qtv->buffersize < 2)\n\t\t{\t//not enough stuff to play.\n\t\t\tif (qtv->parsetime < qtv->curtime)\n\t\t\t{\n\t\t\t\tqtv->parsetime = qtv->curtime + qtv->cluster->tooslowdelay;\n//\t\t\t\tif (qtv->sourcefile || qtv->sourcesock != INVALID_SOCKET)\n//\t\t\t\t\tQTV_Printf(qtv, \"Stream %i: Not enough buffered\\n\", qtv->streamid);\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\n\t\tbuffer = qtv->buffer;\n\n\t\tswitch (qtv->buffer[1]&dem_mask)\n\t\t{\n\t\tcase dem_set:\n\t\t\tlength = 10;\n\t\t\tif (qtv->buffersize < length)\n\t\t\t{\t//not enough stuff to play.\n\t\t\t\tqtv->parsetime = qtv->curtime + qtv->cluster->tooslowdelay;\n//\t\t\t\tif (qtv->sourcefile || qtv->sourcesock != INVALID_SOCKET)\n//\t\t\t\t\tQTV_Printf(qtv, \"Stream %i: Not enough buffered\\n\", qtv->streamid);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tqtv->parsetime += buffer[0];\t//well this was pointless\n\n\t\t\tif (qtv->forwardpoint < length)\t//we're about to destroy this data, so it had better be forwarded by now!\n\t\t\t{\n\t\t\t\tSV_ForwardStream(qtv, qtv->buffer, length);\n\t\t\t\tqtv->forwardpoint += length;\n\t\t\t}\n\n\t\t\tmemmove(qtv->buffer, qtv->buffer+10, qtv->buffersize-(length));\n\t\t\tqtv->buffersize -= length;\n\t\t\tqtv->forwardpoint -= length;\n\t\t\tcontinue;\n\t\tcase dem_multiple:\n\t\t\tlengthofs = 6;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tlengthofs = 2;\n\t\t\tbreak;\n\t\t}\n\n\t\tif (qtv->buffersize < lengthofs+4)\n\t\t{\t//the size parameter doesn't fit.\n//\t\t\tif (qtv->sourcefile || qtv->sourcesock != INVALID_SOCKET)\n//\t\t\t\tQTV_Printf(qtv, \"Stream %i: Not enough buffered\\n\", qtv->streamid);\n\t\t\tqtv->parsetime = qtv->curtime + qtv->cluster->tooslowdelay;\n\t\t\tbreak;\n\t\t}\n\n\n\t\tlength = (buffer[lengthofs]<<0) + (buffer[lengthofs+1]<<8) + (buffer[lengthofs+2]<<16) + (buffer[lengthofs+3]<<24);\n\t\tif (length > MAX_MSGLEN)\n\t\t{\t//THIS SHOULDN'T HAPPEN!\n\t\t\t//Blame the upstream proxy!\n\t\t\tSys_Printf(qtv->cluster, \"Stream %i: Warning: corrupt input packet (%i bytes) too big! Flushing and reconnecting!\\n\", qtv->streamid, length);\n\t\t\tif (qtv->sourcefile)\n\t\t\t{\n\t\t\t\tfclose(qtv->sourcefile);\n\t\t\t\tqtv->sourcefile = NULL;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tclosesocket(qtv->sourcesock);\n\t\t\t\tqtv->sourcesock = INVALID_SOCKET;\n\t\t\t}\n\t\t\tqtv->buffersize = 0;\n\t\t\tqtv->forwardpoint = 0;\n\t\t\tbreak;\n\t\t}\n\n\t\tif (length+lengthofs+4 > qtv->buffersize)\n\t\t{\n//\t\t\tif (qtv->sourcefile || qtv->sourcesock != INVALID_SOCKET)\n//\t\t\t\tQTV_Printf(qtv, \"Stream %i: Not enough buffered\\n\", qtv->streamid);\n\t\t\tqtv->parsetime = qtv->curtime + qtv->cluster->tooslowdelay;\t//add two seconds\n\t\t\tbreak;\t//can't parse it yet.\n\t\t}\n\n//\t\tif (qtv->sourcesock != INVALID_SOCKET)\n//\t\t{\n//\t\t\tQTV_Printf(qtv, \"Forcing demo speed to play at 100% speed\\n\");\n//\t\t\tqtv->parsespeed = 1000;\t//no speeding up/slowing down routed demos\n//\t\t}\n\n\t\tpackettime = buffer[0];\n\t\tif (qtv->parsespeed>0)\n\t\t\tpackettime = ((1000*packettime) / qtv->parsespeed);\n\t\tqtv->nextpackettime = qtv->parsetime + packettime;\n\n\t\tif (qtv->nextpackettime < qtv->curtime)\n\t\t{\n\t\t\tswitch(qtv->buffer[1]&dem_mask)\n\t\t\t{\n\t\t\tcase dem_multiple:\n\t\t\t\tif ((qtv->pexte&PEXTE_HIDDENMESSAGES) &&\n\t\t\t\t\t0 == (buffer[lengthofs-4]<<0) + (buffer[lengthofs-3]<<8) + (buffer[lengthofs-2]<<16) + (buffer[lengthofs-1]<<24))\n\t\t\t\t\t;\t//fucked hidden message crap. don't trip up on it.\n\t\t\t\telse\n\t\t\t\t\tParseMessage(qtv, buffer+lengthofs+4, length, qtv->buffer[1]&dem_mask, (buffer[lengthofs-4]<<0) + (buffer[lengthofs-3]<<8) + (buffer[lengthofs-2]<<16) + (buffer[lengthofs-1]<<24));\n\t\t\t\tbreak;\n\t\t\tcase dem_single:\n\t\t\tcase dem_stats:\n\t\t\t\tParseMessage(qtv, buffer+lengthofs+4, length, qtv->buffer[1]&dem_mask, 1<<(qtv->buffer[1]>>3));\n\t\t\t\tbreak;\n\t\t\tcase dem_all:\n\t\t\t\tif (qtv->buffer[1] & ~dem_mask)\t//dem_qtvdata\n\t\t\t\t\tif (qtv->sourcetype != SRC_DEMO)\n\t\t\t\t\t\tbreak;\n\t\t\t\t//fallthrough\n\t\t\tcase dem_read:\n\t\t\t\tParseMessage(qtv, buffer+lengthofs+4, length, qtv->buffer[1], 0xffffffff);\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tSys_Printf(qtv->cluster, \"Message type %i\\n\", qtv->buffer[1]&dem_mask);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tlength = lengthofs+4+length;\t//make length be the length of the entire packet\n\n\t\t\tqtv->oldpackettime = qtv->curtime;\n\n\t\t\tif (qtv->buffersize)\n\t\t\t{\t//svc_disconnect can flush our input buffer (to prevent the EndOfDemo part from interfering)\n\n\t\t\t\tif (qtv->forwardpoint < length)\t//we're about to destroy this data, so it had better be forwarded by now!\n\t\t\t\t{\n\t\t\t\t\tSV_ForwardStream(qtv, qtv->buffer, length);\n\t\t\t\t\tqtv->forwardpoint += length;\n\t\t\t\t}\n\n\t\t\t\tmemmove(qtv->buffer, qtv->buffer+length, qtv->buffersize-(length));\n\t\t\t\tqtv->buffersize -= length;\n\t\t\t\tqtv->forwardpoint -= length;\n\t\t\t}\n\n\t\t\tif (qtv->sourcefile)\n\t\t\t{\n\t\t\t\tNet_ReadStream(qtv);\n\t\t\t\tqtv->nextconnectattempt = qtv->curtime + RECONNECT_TIME_DEMO;\n\t\t\t}\n\t\t\telse\n\t\t\t\tqtv->nextconnectattempt = qtv->curtime + RECONNECT_TIME;\n\n\t\t\tqtv->parsetime += packettime;\n\t\t}\n\t\telse\n\t\t\tbreak;\n\t}\n}\n\nsv_t *QTV_NewServerConnection(cluster_t *cluster, int newstreamid, char *server, char *password, qboolean force, enum autodisconnect_e autoclose, qboolean noduplicates, qboolean query)\n{\n\tsv_t *qtv;\n\n\tif (noduplicates)\n\t{\n\t\tfor (qtv = cluster->servers; qtv; qtv = qtv->next)\n\t\t{\n\t\t\tif (!strcmp(qtv->server, server))\n\t\t\t{\t//if the stream detected some permanent/config error, try reconnecting again (of course this only happens when someone tries using the stream)\n//warning review this logic\n\t\t\t\tif (qtv->errored == ERR_DISABLED)\n\t\t\t\t{\n\t\t\t\t\tif (!(!QTV_ConnectStream(qtv, server) && !force))\t//try and wake it up\n\t\t\t\t\t\tqtv->errored = ERR_NONE;\n\t\t\t\t}\n\t\t\t\treturn qtv;\n\t\t\t}\n\t\t}\n\t}\n\tif (!newstreamid)\t//no fixed id? generate a default id\n\t\tnewstreamid = 100;\n\t//make sure it doesn't conflict\n\tfor(;;newstreamid++)\n\t{\n\t\tfor (qtv = cluster->servers; qtv; qtv = qtv->next)\n\t\t{\n\t\t\tif (qtv->streamid == newstreamid)\n\t\t\t\tbreak;\n\t\t}\n\t\tif (!qtv)\n\t\t\tbreak;\n\t}\n\n\tif (autoclose)\n\t\tif (cluster->nouserconnects)\n\t\t\treturn NULL;\n\n\tqtv = malloc(sizeof(sv_t));\n\tif (!qtv)\n\t\treturn NULL;\n\n\tmemset(qtv, 0, sizeof(*qtv));\n\t//set up a default config\n//\tqtv->tcplistenportnum = PROX_DEFAULTLISTENPORT;\n\tstrcpy(qtv->server, PROX_DEFAULTSERVER);\n\n\tmemcpy(qtv->connectpassword, password, sizeof(qtv->connectpassword)-1);\n\n//\tqtv->tcpsocket = INVALID_SOCKET;\n\tqtv->sourcesock = INVALID_SOCKET;\n\tqtv->autodisconnect = autoclose;\n\tqtv->parsingconnectiondata = true;\n\tqtv->serverquery = query;\n\tqtv->silentstream = true;\n\tqtv->parsespeed = 1000;\n\n\tqtv->streamid = newstreamid;\n\n\tqtv->cluster = cluster;\n\tqtv->next = cluster->servers;\n\n\tif (autoclose != AD_REVERSECONNECT)\t//2 means reverse connection (don't ever try reconnecting)\n\t{\n\t\tif (!QTV_ConnectStream(qtv, server) && !force)\n\t\t{\n\t\t\tQTV_Cleanup(qtv, false);\n\t\t\tfree(qtv);\n\t\t\treturn NULL;\n\t\t}\n\t}\n\tcluster->servers = qtv;\n\tcluster->numservers++;\n\n\treturn qtv;\n}\n"
  },
  {
    "path": "fteqtv/sp_dsound.c",
    "content": "#include \"qtv.h\"\r\n#ifdef COMMENTARY\r\n#include <dsound.h>\r\n\r\nstatic HANDLE hInstDS;\r\nstatic HRESULT (WINAPI *pDirectSoundCreate)(LPGUID, LPDIRECTSOUND *, LPUNKNOWN);\r\n\r\ntypedef struct {\r\n\tsoundplay_t funcs;\r\n\r\n\tLPDIRECTSOUND ds;\r\n\tLPDIRECTSOUNDBUFFER dsbuf;\r\n\r\n\tint buffersize;\r\n\r\n\tint writepos;\r\n\tint readpos;\r\n\r\n\tint sampbytes;\r\n\r\n} dsplay_t;\r\n\r\nint DSOUND_UpdatePlayback(soundplay_t *sp, int samplechunks, char *buffer)\r\n{\r\n\tint ret;\r\n\tdsplay_t *dsp = (dsplay_t*)sp;\r\n\tchar *sbuf;\r\n\tint sbufsize;\r\n\tint writable;\r\n\tint remaining = samplechunks;\r\n\r\n\tif (!samplechunks)\r\n\t\treturn 0;\r\n\r\n\tIDirectSoundBuffer_GetCurrentPosition(dsp->dsbuf, &dsp->readpos, NULL);\r\n\tdsp->readpos /= dsp->sampbytes;\r\n\r\n\twhile (ret = IDirectSoundBuffer_Lock(dsp->dsbuf, 0, dsp->buffersize*dsp->sampbytes, (void**)&sbuf, &sbufsize, NULL, NULL, 0))\r\n\t{\r\n\t\tif (!FAILED(ret))\r\n\t\t\tbreak;\r\n\t\tif (ret == DSERR_BUFFERLOST)\r\n\t\t\tprintf(\"Buffer lost\\n\");\r\n\t\telse\r\n\t\t\tbreak;\r\n\r\n//\t\t\tif (FAILED(IDirectSoundBuffer_Resore(dsp->dsbuf)))\r\n//\t\t\t\treturn 0;\r\n\t}\r\n\t//memset(sbuf, 0, sbufsize);\r\n\twritable = remaining;\r\n\tif (writable > sbufsize/dsp->sampbytes - dsp->writepos)\r\n\t\twritable = sbufsize/dsp->sampbytes - dsp->writepos;\r\n\tmemcpy(sbuf+dsp->writepos*dsp->sampbytes, buffer, writable*dsp->sampbytes);\r\n\tremaining -= writable;\r\n\tbuffer += writable*dsp->sampbytes;\r\n\tdsp->writepos += writable;\r\n\tdsp->writepos %= dsp->buffersize;\r\n\tif (samplechunks > 0)\r\n\t{\r\n\t\twritable = remaining;\r\n\t\tif (writable > dsp->readpos)\r\n\t\t\twritable = dsp->readpos;\r\n\t\tmemcpy(sbuf, buffer, writable*dsp->sampbytes);\r\n\t\tremaining -= writable;\r\n\t\tdsp->writepos += writable;\r\n\t\tdsp->writepos %= dsp->buffersize;\r\n\t}\r\n\tIDirectSoundBuffer_Unlock(dsp->dsbuf, sbuf, sbufsize, NULL, 0);\r\n\r\n\tprintf(\"%i %i\\n\", 100*dsp->readpos / dsp->buffersize, 100*dsp->writepos / dsp->buffersize);\r\n\r\n\treturn samplechunks - remaining;\r\n}\r\n\r\nvoid DSOUND_Shutdown(soundplay_t *dsp)\r\n{\r\n}\r\n\r\nsoundplay_t *SND_InitPlayback(int speed, int bits)\r\n{\r\n\tint ret;\r\n\tDSBCAPS caps;\r\n\tDSBUFFERDESC bufdesc;\r\n\tLPDIRECTSOUND ds;\r\n\tdsplay_t *hnd;\r\n\tWAVEFORMATEX\tformat; \r\n\r\n\tif (!hInstDS)\r\n\t{\r\n\t\thInstDS = LoadLibrary(\"dsound.dll\");\r\n\t\t\r\n\t\tif (hInstDS == NULL)\r\n\t\t{\r\n\t\t\tprintf (\"Couldn't load dsound.dll\\n\");\r\n\t\t\treturn NULL;\r\n\t\t}\r\n\r\n\t\tpDirectSoundCreate = (void *)GetProcAddress(hInstDS,\"DirectSoundCreate\");\r\n\r\n\t\tif (!pDirectSoundCreate)\r\n\t\t{\r\n\t\t\tprintf (\"Couldn't get DS proc addr\\n\");\r\n\t\t\treturn NULL;\r\n\t\t}\r\n\t\t\t\t\r\n//\t\tpDirectSoundEnumerate = (void *)GetProcAddress(hInstDS,\"DirectSoundEnumerateA\");\r\n\t}\r\n\r\n\tds = NULL;\r\n\tpDirectSoundCreate(NULL, &ds, NULL);\r\n\r\n\tif (!ds)\r\n\t\treturn NULL;\r\n\thnd = malloc(sizeof(*hnd));\r\n\tmemset(hnd, 0, sizeof(*hnd));\r\n\r\n\thnd->funcs.update = DSOUND_UpdatePlayback;\r\n\thnd->funcs.close = DSOUND_Shutdown;\r\n\r\n\thnd->ds = ds;\r\n\thnd->sampbytes = bits/8;\r\n\r\n\tif (FAILED(IDirectSound_SetCooperativeLevel (hnd->ds, GetDesktopWindow(), DSSCL_EXCLUSIVE)))\r\n\t\tprintf(\"SetCooperativeLevel failed\\n\");\r\n\r\n\tmemset(&bufdesc, 0, sizeof(bufdesc));\r\n\tbufdesc.dwSize = sizeof(bufdesc);\r\n//\tbufdesc.dwFlags |= DSBCAPS_GLOBALFOCUS;\t//so we hear it if quake is loaded\r\n\tbufdesc.dwFlags |= DSBCAPS_PRIMARYBUFFER; //so we can set speed\r\n\tbufdesc.dwFlags |= DSBCAPS_CTRLVOLUME;\r\n\tbufdesc.lpwfxFormat = NULL;\r\n\tbufdesc.dwBufferBytes = 0;\r\n\r\n\tformat.wFormatTag = WAVE_FORMAT_PCM;\r\n\tformat.cbSize = 0;\r\n\r\n\tformat.nChannels = 1;\r\n    format.wBitsPerSample = bits;\r\n    format.nSamplesPerSec = speed;\r\n    format.nBlockAlign = format.nChannels * format.wBitsPerSample / 8;\r\n    format.nAvgBytesPerSec = format.nSamplesPerSec * format.nBlockAlign;\r\n\r\n\tret = IDirectSound_CreateSoundBuffer(hnd->ds, &bufdesc, &hnd->dsbuf, NULL);\r\n\r\n\tif (!hnd->dsbuf)\r\n\t{\r\n\t\tprintf(\"Couldn't create primary buffer\\n\");\r\n\t\tDSOUND_Shutdown(&hnd->funcs);\r\n\t\treturn NULL;\r\n\t}\r\n\r\n\tif (FAILED(IDirectSoundBuffer_SetFormat(hnd->dsbuf, &format)))\r\n\t\tprintf(\"SetFormat failed\\n\");\r\n\r\n\t//and now make a secondary buffer\r\n\tbufdesc.dwFlags = 0;\r\n\tbufdesc.dwFlags |= DSBCAPS_CTRLFREQUENCY;\r\n\tbufdesc.dwFlags |= DSBCAPS_LOCSOFTWARE;\r\n\tbufdesc.dwFlags |= DSBCAPS_GLOBALFOCUS;\r\n\tbufdesc.dwBufferBytes = speed * format.nChannels * hnd->sampbytes;\r\n\tbufdesc.lpwfxFormat = &format;\r\n\r\n\tret = IDirectSound_CreateSoundBuffer(hnd->ds, &bufdesc, &hnd->dsbuf, NULL);\r\n\tif (!hnd->dsbuf)\r\n\t{\r\n\t\tprintf(\"Couldn't create secondary buffer\\n\");\r\n\t\tDSOUND_Shutdown(&hnd->funcs);\r\n\t\treturn NULL;\r\n\t}\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\tmemset(&caps, 0, sizeof(caps));\r\n\tcaps.dwSize = sizeof(caps);\r\n\tIDirectSoundBuffer_GetCaps(hnd->dsbuf, &caps);\r\n\thnd->buffersize = caps.dwBufferBytes / hnd->sampbytes;\r\n\r\n\t//clear out the buffer\r\n\t{\r\n\t\tchar *buffer;\r\n\t\tint buffersize=0;\r\n\t\tIDirectSoundBuffer_Play(hnd->dsbuf, 0, 0, DSBPLAY_LOOPING);\r\n\t\tret = IDirectSoundBuffer_Lock(hnd->dsbuf, 0, hnd->buffersize*hnd->sampbytes, (void**)&buffer, &buffersize, NULL, NULL, 0);\r\n\t\tmemset(buffer, 0, buffersize);\r\n\t\tIDirectSoundBuffer_Unlock(hnd->dsbuf, buffer, buffersize, NULL, 0);\r\n\t\tIDirectSoundBuffer_Stop(hnd->dsbuf);\r\n\t}\r\n//DSERR_INVALIDPARAM\r\n\tIDirectSoundBuffer_Play(hnd->dsbuf, 0, 0, DSBPLAY_LOOPING);\r\n\t\r\n\r\n\tIDirectSoundBuffer_GetCurrentPosition(hnd->dsbuf, &hnd->readpos, &hnd->writepos);\r\n\r\n\thnd->writepos = hnd->readpos + speed / 2;\t//position our write position a quater of a second infront of the read position\r\n\r\n\tprintf(\"%i %i\\n\", 100*hnd->readpos / hnd->buffersize, 100*hnd->writepos / hnd->buffersize);\r\n\r\n\treturn &hnd->funcs;\r\n}\r\n\r\n\r\n/*\r\n\r\n\r\nvoid soundtest(void)\r\n{\r\n\tint speed = 22100*2;\r\n\tint bits = 16;\r\n\r\n\tint i;\r\n\r\n\tint sampsavailable;\r\n\tshort buffer[1024];\r\n\tsoundcapt_t *capt;\r\n\tsoundplay_t *play;\r\n\r\n\tcapt = SND_InitCapture (speed, bits);\r\n\tif (!capt)\r\n\t{\r\n\t\tprintf(\"Failed to init capturer\\n\");\r\n\t\texit(0);\r\n\t}\r\n\r\n\tplay = SND_InitPlayback (speed, bits);\r\n\tif (!play)\r\n\t{\r\n\t\tprintf(\"Failed to init playback\\n\");\r\n\t\texit(0);\r\n\t}\r\n\r\n\r\n\r\n\r\n\tfor(;;)\r\n\t{\r\n\t\tfor (i = 0; i < sizeof(buffer)/sizeof(buffer[0]); i++)\r\n\t\t\tbuffer[i] = rand();\r\n\t\tsampsavailable = capt->update(capt, sizeof(buffer)/(bits/8), (char*)buffer);\r\n\t\tplay->update(play, sampsavailable, (char*)buffer);\r\n\r\n\t\tSleep(1);\r\n\t}\r\n}\r\n*/\r\n#endif\r\n"
  },
  {
    "path": "fteqw_readme.txt",
    "content": "FTEQW README\n============\n\nThis file contains the following sections:\n\n1. ABOUT\n2. FEATURES\n3. INSTALLING\n4. NETWORK QUICK-START\n5. TWEAKS\n6. TROUBLESHOOTING\n7. CONTACT\n8. LICENSE\n\n1. ABOUT\n========\n\nFTEQW is the advanced, portable Quake engine.\nIt supports multiple games running on idTech, plus its own set of\ngames that developers have created. Due to the vast amount of supported\nformats, features and innovations inside the engine and its very own\nQuakeC compiler (fteqcc), it's very much considered the swiss-army\nknife of Quake engines.\n\n2. FEATURES\n===========\n\n- Portable engine that runs on x86, amd64, ARM/64, PPC64LE and Web\n- Hybrid protocol engine that supports multiple games\n- Rendering API support for D3D8, D3D9, D3D11, OpenGL, Vulkan\n- Splitscreen support for Quake and most mods\n- In-game voice chat powered by either Opus or Speex\n- Advanced renderer features, powered by a strong material system\n  and support for both HLSL and GLSL shader code\n- Multiple audio backends, from OSS to SDL_Sound and OpenAL Soft,\n  plus API so developers can take advantage of AL EAX reverb features.\n  And yes, DirectSound.\n- Integrated next-generation QuakeC compiler and debugger\n  with support for breakpoints, real-time ingame attribute debugging\n  and much more\n- Support for IPv4 and IPv6\n- Video output presets, to make your games either look like the\n  original versions, or more modern with real-time lighting and more\n  accurate shading\n- Support for CD-DA/Red Book music replacement in a variety of formats,\n  such as Vorbis, MPEG-3, WAVE and FLAC (ffmpeg plugin required)\n\n3. INSTALLING\n=============\n\nPut the engine binary and desired plugins for your platform into the\nroot of the game directory.\n\nSupported games:\n    Quake and its Missionpacks\n    QuakeWorld\n    Quake II and its Missionpacks\n    Quake III Arena and Team Arena\n    HeXen II and its Missionpack\n\nIf you want to be explicit about the game you're starting, you can pass\nthe command-line parameters '-quake2', '-quake3' or '-hexen2' for the\nrespective game. This will make sure that in a crowded universal game\ndirectory, FTE starts the right game.\n\nIf you want to install music replacement files, you put them into the\nmusic/ folder with the trackXX naming convention, starting with track02.\n\nImportant note regarding Quake II based support\n-----------------------------------------------\n\nIf you're running a 64-bit version of FTEQW, then you also need 64-bit\ngame-logic for Quake II. We recommend getting the game .dll/.so from the\nYamagi Quake II project for your respective platform. It's recommended\nthat you do for win32 as well, as that will ensure that save games work\nproperly and you can stop worrying about them becoming incompatible\nbetween other machines.\n\n4. NETWORK QUICK-START\n======================\n\nUpon launching FTEQW, you can use the multiplayer menu's own built-in\nserver browser to join and connect to a vast array of matches across all\nthe different protocols. No more QuakeSpy/GameSpy 3D client required!\n\nIf you want to host a game, you can either run a listen server with\nthe ports forwarded, or host a listen server using frag-net.com's online\nservice. Start a new multiplayer server, change the \"Public\" setting to\n\"Holepunch\" and players will automatically see your game in their\nserver-browser once it's started.\n\nYou can also set FTEQW to run in a terminal/command-prompt for hosting\na dedicated server session.\nSimply pass the command-line argument like so:\n\n    fteqw -dedicated\n\nThis will create an interactive shell reminiscent to the console that's\naccessible in-game.\n\nYou can host a game directly with frag-net.com without having to worry\nabout port-forwarding and have a map up and running like so:\n\n    fteqw +set sv_public 2 +set sv_playerslots 8 +map dm4\n\n5. TWEAKS\n=========\n\nYou can apply tweaks by opening the console (SHIFT+ESC) and entering\ncommands into the line buffer. In there you can enter console variables\n(cvars) that affect how the game behaves, as well as enter console\ncommands that trigger an action in the engine. \n\nYou can always find a list of both of these with the console commands \n'cvarlist' and 'cmdlist' respectively.\n\nSome example commands\n------------------------\nbind <key> <command> Binds a command to a key, e.g. bind F12 quit\nmap <mapname>        Starts a new game on <mapname>, e.g. map dm4\nconnect <address>    Establishes a connection to the specified \n                     IP/hostname address.\ndisconnect           Closes and remote or local game session.\ncfg_save             Save amy unsaved configuration changes.\nquit                 Quits the game.\n\nAbout console variables\n-----------------------\nUnlike other Quake engines, FTEs console variable system is more akin to\nthose of later idTech engines. Console variables can be set temporarily\n(until the next engine restart) or permanently.\n\nset sv_port 26000    Sets the current port to 26000 for this session\nseta sv_port 26000   Sets the current port gets set to 26000 and makes\n                     sure it will get 'archived', aka saved.\n\nSometimes, you'll be able to change a cvar without entering 'set' or\n'seta' beforehand. This is due to the cvar/cmd suggestion system.\nWhen you simply set a cvar that way, 'set' is assumed.\nSo those changes are only temporary.\n\nCommandline arguments\n---------------------\n\nYou can pass cvars and commands directly to the engine binary, prefixed\nwith a plus symbol like so:\n\n    fteqw +set sv_port 26000 +map dm4\n\nThere's also other interesting commandline only arguments you can pass:\n\n-nohome         Don't attempt to save configs, saves, screenshots in the\n                home or user directory.\n-basedir <path> Specifies the root game directory path.\n-basegame <dir> Specifies which folder to look in for main game data.\n-game    <dir>  Specifies which mod folder to load over the game data\n-window         Tells the renderer to run in a window\n-manifest <fmf> Specifies a game manifest to load.\n                This is for advanced game and mod switching.\n-dedicated      Run the engine in dedicated server mode, no video out.\n\nChanging games and mods\n-----------------------\n\nLaunching a Quake 1 mod can be done like so:\n\n    fteqw -game fortress\n\nThat will assume a basegame of 'id1' and the mod 'fortress' will be\nloaded on top of it.\nHowever, if you're playing a game that uses no data from Quake 1:\n\n    fteqw -basegame openquartz\n\nThen it'll never even touch or peek into the folder 'id1'.\nOf course you can pass -game after that, too.\n\nUnderstanding manifests (advanced users)\n----------------------------------------\n\nYou can setup custom game configurations with FTE's manifest files.\nThose can be quite advanced, as they might inherit multiple directories,\nchange the name of the window title, set binds, aliases and cvars ahead\nof time and much more.\n\n    FTEMANIFEST 1\n    GAME funky\n    NAME \"Funky QW Game\"\n    BASEGAME id1\n    BASEGAME qw\n    BASEGAME funky\n\nAn example manifest that will load id1, qw and then funky.\nIf you wanted, you can set default cvars and aliases in there like so:\n\n    -set sv_example 1234\n    -seta cl_foobar 5678\n    -alias funky1 \"impulse 416\"\n    +bind g funky1\n\nNote the dash and plus symbols, they actually notate when to execute the\ncommand in question. '-' means before the engine loads the game config\nwhereas '+' notates it will override anything that will usually be set\nby the game.\n\nYou'd then save this as, for example, funky.fmf and load the manifest\nvia the command-line:\n\n    fteqw -manifest funky.fmf\n\n6. TROUBLESHOOTING\n==================\n\nIf you're running FTEQW on an older machine with Intel GMA graphics,\nyou probably want to try running the engine in D3D9 mode if you're\nencountering any graphical issues:\n\n    fteqw +set vid_renderer d3d9\n\nIf you can only run OpenGL but still have graphical issues, try forcing\nsupport for the builtin GLSL off:\n\n    fteqw +set gl_blacklist_debug_glsl 1\n\nIf FTEQW is seemingly not saving your settings, make sure you tell it to\nsave your config when you quit the game. If it still does not work\nsomehow, enter the console command 'cfg_save' into the console. It\nshould output where the file gets saved or if there's any problems\nwriting the configuration file.\n\nIf OpenAL is causing crashes at launch (happens with some distributions,\nthat is out of our control) then try starting FTEQW with:\n\n     fteqw +set s_al_disable 1\n\n7. CONTACT\n==========\n\nIf you need more help, have suggestions or want to hang out with the\ndevelopers that make FTEQW what it is, join us on IRC!\nNo account required.\n\nirc.quakenet.org #fte\n\nBug reports welcome!\n\n8. LICENSE\n==========\n\nCopyright (c) 2004-2020 FTE's team and its contributors\nQuake source (c) 1999 id Software\n\nFTEQW is supplied to you under the terms of the same license as the\noriginal Quake sources, the GNU General Public License Version 2.\nPlease read the 'LICENSE' file for details.\n\nThe latest source is always available at:\n    http://svn.code.sf.net/p/fteqw/code/trunk/\n\nBinaries are usually built at:\n    https://fte.triptohell.info/moodles/\n"
  },
  {
    "path": "ftetools_readme.txt",
    "content": "FTE TOOLS README\n============\n\nThis file contains the following sections:\n\n1. FTEQCC\n2. PACKAGE MANAGEMENT\n3. IQM TOOL\n4. IMAGE TOOL\n5. CONTACT\n6. LICENSE\n\n1. FTEQCC\n========\n\nFTEQCC is used to compile \nFull FTEQCC documentation can be found here: https://fte.triptohell.info/moodles/fteqcc/README.html\n\n2. PACKAGE MANAGEMENT\n===========\n\nThe commandline version of fteqcc doubles up as a pak/pk3 creator/extractor\n`fteqcc -l PACKAGENAME`\n\tLists the contents of a file on the stdout.\n`fteqcc -x PACKAGENAME`\n\tExtracts all files from the named package to the working directory. You should normally use ../foo.pak in order to avoid overwriting files unintentionally.\n`fteqcc -x PACKAGENAME SUBDIR`\n\tExtracts specific files from the named package.\n`fteqcc -0 SUBDIR`\n\tGenerates a `subdir.pak` file with the contents of that subdir. Unlike most pak generators, the pak will also be openable with any zip tool for users to easily view files (they shouldn't edit them though).\n`fteqcc -9 SUBDIR`\n\tGenerates a `subdir.pk3` file with the contents of that subdir. Contents will be compressed to reduce filesize, but won't work in vanilla quake.\n`fteqcc -z SUBDIR`\n\tGenerate a `subdir.pk3` dictionary file, and an (additional) `subdir.pXX` file (name generated sequentially). The dictionary file will be versioned with different servers potentially using different versions. This prevents re-downloading redundant copies of the same files for each different version. The spanned data files are not versioned, so these should only be generated by authoritive developers (ie: not same-filename forks).\n\nMany engines support interpretting `foo.pk3dir` subdirs as proto-packages, which makes it easier to test packages without constant re-compression.\n\n3. IQM TOOL\n===========\n\nAbout:\n\tThis is a commandline tool for creating iqm files from intermediate files exported from modelling programs.\n\nPossible Inputs:\n\tsmd\n\tgltf\n\tglb\n\tiqe\n\tmd5mesh\n\tmd5anim\n\tfbx\n\tobj\n\nOutputs:\n\tiqm / vvm\n\t\tThe default format. 'vvm' is a moniker for the backwards-compatible extensions fte supports which are not supported by other engines/tools.\n\tmdl\n\t\tquake1's model format. Horrible format, but a handy fallback for people who refuse to use better engines.\n\nUsage:\n\tIQM tool Can be used two ways.\n\t`iqm foo.cmd` uses a command script to decide which files to read, modifiers for each sequence or mesh, etc.\n\t`iqm foo.iqm foo.gltf` converts the gltf file to an iqm file. Animation rate will be guessed.\n\t`iqm foo.mdl foo.gltf` writes out a quake1 mdl file. Framegroups will be used for each animation sequence.\n\nCommand script:\n\t#COMMENT\n\t//COMMENT\n\t\tJust a comment, ignored.\n\texec filename\n\t\tInvokes an external script (useful when you have multiple models with the same animations or so).\n\tmodelflags BITS\n\t\tSets the output model's flags. Queryable in gamecode.\n\tmesh MESHNAME ATTRIBUTELIST\n\t\tOverrides attributes of an imported mesh to use for the visible part of the model. Use the `import` command to actually import the geometry.\n\t\tAttributes are:\n\t\t\tcontents BITSORNAMES\n\t\t\t\tControls which traces may impact the imported surfaces. You should normally specify 0 if you are using hitboxes. Default is CONTENTS_BODY.\n\t\t\tsurfaceflags BITSORNAMES\n\t\t\t\tMisc info eg reported by tracelines.\n\t\t\tbody NUM\n\t\t\t\tThe 'body' number which can be queried in gamecode (typically used to report eg headshots).\n\t\t\tgeomset SET ID\n\t\t\t\tSets this mesh to only draw when geomset SET has been configured as ID.\n\t\t\tlodrange MIN MAX\n\t\t\t\tThis mesh will only be drawn when the screen coverage is within the specified range. Exact interpretation can vary according to engine cvars.\n\thitbox BODYNUM BONENAME MINS MAXS\n\t\tCreates a cuboid mesh around the named bone (as a box in the base pose). The mesh will be given CONTENTS_BODY such that it will be hit by hitmesh traces. The bodynum arg can be queried via gamecode.\n\tbone NAME ATTRIBUTELIST\n\t\trename NEWNAME\n\t\t\tThe bone read from input files will be written as NEWNAME in the output file (so gamecode can manipulate things consistently).\n\t\tgroup GROUPNUM\n\t\t\tBones can be reordered in the output according to their group numbers. Only the root bone of each group needs its group specified (children will inherit). This simplies bone range operations in gamecode\n\timport FILENAME ATTRIBUTELIST\n\tmodel FILENAME ATTRIBUTELIST\n\tscene FILENAME ATTRIBUTELIST\n\tanimation FILENAME ATTRIBUTELIST\n\t\tLoads model AND animation data from an imported file.\n\t\tFile formats must match the given filename extensions.\n\t\tAttributes are:\n\t\t\tAny attributes supported by the mesh command (such attributes here define the default values for imported meshes).\n\t\t\tname NEWNAME\n\t\t\t\tThe animation will be queryable to gamecode as the NEWNAME.\n\t\t\tfps RATE\n\t\t\t\tOverrides the frame rate of the animation.\n\t\t\tloop\n\t\t\t\tSpecifies that the animation must loop.\n\t\t\tclamp\n\t\t\t\tSpecifies that the animation must NOT loop (animation will stop once it reaches the last pose).\n\t\t\tunpack\n\t\t\t\tGenerates single-pose animations, consistent with the way vanilla QC animates.\n\t\t\tpack\n\t\t\t\tGenerates multiple-pose animations, gamecode will be able to specify animation and time separately.\n\t\t\tnomesh\n\t\t\t\tDo NOT generate meshes from this file.\n\t\t\tnoanim\n\t\t\t\tDo NOT generate animations from this file.\n\t\t\tmaterialprefix PATH\n\t\t\t\tPrefixes the imported texture with an additional path (to avoid conflicts with other files).\n\t\t\tstart FRAME\n\t\t\tend\tFRAME\n\t\t\t\tLimits inputed data to the specified poses.\n\t\t\tscale SCALE\n\t\t\t\tResizes the input data.\n\t\t\trotate PITCH ROLL YAW\n\t\t\t\tRotates the imported data (quake is +x=forward, +y=left, +z=up).\n\t\t\ttranslate X Y Z\n\t\t\t\tMoves the mesh+bones around a bit.\n\t\t\tevent [SEQUENCE:]POSE EVCODE EVSTRING\n\t\t\t\tDefines a model event at the specified timestamp.\n\toutput FILENAME\n\toutput_iqm FILENAME\n\toutput_vvm FILENAME\n\toutput_qmdl FILENAME\n\toutput_md16 FILENAME\n\t\tSpecifies the file to be written. You may have one per supported output model format, each will contain roughly equivelent data (where supported).\n\n4. IMAGE TOOL\n=============\n\nAbout:\n\tThis is a tool for converting various image file types to more favourable ones.\n\tThis includes generating wad files, cubemap images, etc.\n\nWad Examples:\n\t`imgtool --ext mip [--PIXFMT] [--resize W H] *.EXT`\n\t\tConverts the input files to quake's miptex format. Input files will not be changed.\n\t\tIf compression was specified, the resulting miptex will contain an additional high-colour alternative.\n\t\tIf --resize is used, the paletted data will be resized without affecting the high-colour alternative.\n\t\tSupported pixel formats:\n\t\t\trgba8,rgb8, rgb565,rgba5551,rgba4444, l8, bc1-7, etc1,etc2,etcp,etca, astc*x*, e5bgr9\n\t\tNOTE: use of alternative formats requires a qbsp which does NOT strip this information. The vanilla qbsp will work fine for this purpose, but more advanced qbsp utils have a tendancy to strip the info (including ericw's, sadly).\n\n\t`imgtool -w WADFILE *.mip`\n\t\tPacks the specified miptex files into the named wad file.\n\n\t`imgtool -x --ext EXT WADORBSP`\n\t\tExtracts the textures from the specified wad or bsp file, saving them as EXT files.\n\nGeneral Examples:\n\t`imgtool --help`\n\t\tShows a list of compressed pixel formats, and supported file formats.\n\n\t`imgtool -i FILE`\n\t\tShows info about the named file(s).\n\n\t`imgtool --ext ktx [--PIXFMT] *.EXT`\n\t`imgtool --ext dds [--PIXFMT] *.EXT`\n\t\tConverts the input files to a hardware-friendly file format (with the specified pixel format). Input files will not be changed.\n\t\tktx supports all recognised hardware formats. dds is more limited (and eg doesn't support etc or astc).\n\t\thud artwork should generally be astc4x4 or bc7 for best quality.\n\t\tmodels and walls can be of lower quality, astc6x6, etc2, or bc1 are good choices.\n\t\tfor hdr pixel formats, try astc4x4_hdr or bc6.\n\t\n\t`imgtool --ext png *.EXT`\n\t\tConverts the input files to png format for viewing/editing. Input files will not be changed.\n\n\t`imgtool --cube [--PIXFMT] -o mysky.dds mysky_*.tga`\n\t\tConverts 6 input files to a single cubemap file.\n\t\tInputs will be reordered according to quake's typical cubemap postfixes (and flipped as appropriate), otherwise be careful with wildcards.\n\n\t`imgtool --2darray [--PIXFMT] -o foo.ktx IDX0 IDX1 IDXN`\n\t\tConverts the input textures into a 2d texture array. Input file order matters.\n\t\n\t`imgtool --3d [--PIXFMT] -o foo.ktx LAYER0 LAYER1 LAYERN`\n\t\tConverts the input textures into a 3d texture. Input file order matters.\n\nWhich pixel format to use:\n\tASTC:\n\t\tThe ldr-only profile is part of the gles3.2 spec (but likely to be emulated on nvidia).\n\t\tASTC has a range of block sizes with each block being 16 bytes, thus larger block sizes yield greater compression.\n\t\tThis format supports multiple planes, which reduces issues with multiple gradients in a block, which means it can cope with pixel art better than eg s3tc with larger block sizes.\n\t\tBits are distributed according to usage per block, this means it can use more bits for the rgb channels where alpha is constant, giving it more useful bits than eg bc3.\n\t\tASTC is able to use a second set of weights for any single channel (not just alpha), which makes it suitable for encoding normalmaps for instance.\n\t\tTo encode ASTC pixel formats, you will need to install astcenc - https://github.com/ARM-software/astc-encoder/releases (or use astc-encoded source files).\n\tETC2:\n\t\tPart of the gles3.0/gl4.3 spec\n\t\tETC2 is a superset of ETC1, and has been somewhat obsoleted by ASTC.\n\tBC1/2/3:\n\t\tAKA s3tc, AKA dxt, available only via optional extensions, but is mandated by d3d 9_1 feature level.\n\t\tThese formats all encode two 565 colours per 4*4 block with 2-bits per pixel for interpolation, which can result in discolouration.\n\t\tbc2 uses an additional 64bit block to encode 4bit alpha.\n\t\tbc3 also uses an additional block for alpha, but does so simiarly to its rgb values (two 8bit alpha values, with 3-way interpolation).\n\t\tTo encode s3tc pixel formats, you will need to install libnvtt-bin - https://github.com/castano/nvidia-texture-tools (or export from image editors as dds or ktx files).\n\tBC4/5:\n\t\tAKA rgtc, part of the gl3.0 spec, or d3d10.\n\t\tThese formats reuse bc3's 'alpha' compression to encode one or two channels respectively. BC1 can be handy for heightmaps/greyscale/etc, while BC5 can be useful for 2-channel normalmaps. They are not generally useful as eg wall textures.\n\tBC6/7:\n\t\tAKA bptc, part of the gl4.2, or d3d11.\n\t\tThese formats support one or two planes per block, which means they can cope with pixel art quite a bit better than bc1/3.\n\t\tWhile they're the same size as bc3 (twice that of bc1), they have multiple modes per block that allow them to eg avoid wasting bits on unused alpha data.\n\t\tThe difference is that BC7 encodes RGBA ldr data, while BC6 encodes RGB hdr data.\n\t\tTo encode s3tc pixel formats, you will need to install (debian experimental) libnvtt-bin - https://github.com/castano/nvidia-texture-tools (or export from image editors as dds or ktx files).\n\n\tToo Long didn't read:\n\t\tUse ASTC if you're targetting modern mobile devices (astc6x6 for walls, astc4x4 for huds, or something).\n\t\tUse ETC2 if you're targetting older mobile users.\n\t\tUse BC1 for desktop model/wall textures, and bc3 for things with non-binary alpha channels.\n\t\tUse BC7 for desktop hud textures or other textures where BC1 does a terrible job.\n\t\tUse jpegs if you just want to get filesize down without caring about performance.\n\n\t\tIf the user's hardware doesn't support the used formats then FTE can software-decode, so if you're expecting both mobile+desktop users with a single set of textures then favour mobile (desktop GPUs have better memory bandwidth).\n\t\n5. CONTACT\n==========\n\nIf you need more help, have suggestions or want to hang out with the\ndevelopers that make FTE what it is, join us on IRC!\nNo account required.\n\nirc.quakenet.org #fte\n\nBug reports welcome!\n\n6. LICENSE\n==========\n\nCopyright (c) 2004-2020 FTE's team and its contributors\nQuake source (c) 1999 id Software\n\nFTEQW is supplied to you under the terms of the same license as the\noriginal Quake sources, the GNU General Public License Version 2.\nPlease read the 'LICENSE' file for details.\n\nThe latest source is always available at:\n    http://svn.code.sf.net/p/fteqw/code/trunk/\n\nBinaries are usually built at:\n    https://fte.triptohell.info/moodles/\n"
  },
  {
    "path": "games/fortressone.fmf",
    "content": "FTEMANIFEST 1\ngame quake\nname \"FortressOne\"\ngamedir fortress\nprotocolname FortressOne\n\n//mixing fortressone and other fortress mods in the same gamedir gets messy (especially when protocolnames are being overriden inside configs)\nmainconfig \"fo.cfg\"\n\n//homedir usage is used only where required (or previously created). things like flatpak require its use.\n\n//see quake-demo.fmf\narchivedpackage id1/pak0.pak 0x4f069cac id1/pak0.pak https://updates.triptohell.info/moodles/live/QUAKE_SW.zip\n//this is made complicated because we can't just use the pk3 as-is etc (engine will sandbox the menuqc if not signed).\npackage fortress/fortressone.pk3 mirror \"https://github.com/FortressOne/fte-config/releases/download/1.1.1-beta.1/fortressone-fte-linux-1.1.1-beta.1-portable.zip\" crc 0x7d74337f prefix \"/FortressOne/fortress\" filesize 197849205 sha512 \"42a0deaa571f30c56dac08603d94d968a603a6c2d64db29473cb25f7e1db29fe63260cc8cda2e41dbbb3decd5e1c5440aa6bbceeae45c3225dad359fe0559021\" signature \"Spike:OrVRb1AuVmPGSmPlMS37DmEs1UIOeAZpSFj8s9jweUDbSqbkBnY6+5tBI43MxqDtIRMhK1+zDoaRxyvR2HErks2hf1wVoDnwpVACi893tvhSKQ0yfKUfkdpqm8aQM7AU/22ZGj5zav6RtxoX+np/7rzfET0fHzCSaQS1d6/TeJaQ5rMPX13Bgu0CenuOD1rvVNXXPMD5d887Kd+y/kz4OVUH0/xkjua5LHsWwDroC5AwzQI/EsWbcJx+xppihKNdMboAr51dALDuWLeSYzswPoJZWppv9D0WbjLohiZU04gmys7JdLn1cclz5MvIinLNFK//adQHrISqDFJqBygni8k90A0BNwSjdVzf9f4bYtzwS5qFRNgYtONyb380cpFZtMMKLdLRne1K8Y4FljzaJztcbm5qBQCLjAaK+C405tUDddDToCyyGsO6Zxf7yzhlSXNTWv/qRgKx91w5pJNyACgYnspxr+xOX+6ewchZZi07uPOPwVrxoAkuIrkiJtUe\"\n\n"
  },
  {
    "path": "games/freehl.fmf",
    "content": "//Mostly just defers to the engine's `-halflife` rules.\nFTEMANIFEST 1\nGAME halflife\nNAME \"Half-Life (FreeHL)\"\n"
  },
  {
    "path": "games/hexen2-demo.fmf",
    "content": "//Note - the hexen2 demo is an unmaintained variation of an earlier build, incompatible with the later game. Any basedirs must be entirely separate.\n\ngame hexen2\nname \"Hexen2 Demo\"\n\npackage \"data/pak0.pak\" crc 0x3bbcb56c mirror \"unzip:H2Demo/Install/Hexen2/data1/pak0.pak,https://www.quaddicted.com/files/idgames2/idstuff/hexen2/H2Demo.exe\"\n\n"
  },
  {
    "path": "games/ktx.fmf",
    "content": "//this file is intended for dedicated servers, but I guess clients might have reason to use it too. expect quirks though.\n\nFTEMANIFEST 1\ngame quake\nname \"nQuakeSV-KTX\"\ngamedir ktx\n\nhomedirmode never\t//auto/always\n\ndownloadsurl https://fte.triptohell.info/moodles/ktx.meta\ninstall quake_shareware\t\t//we need(ish) a pak0!\ninstall nquakesv_configs\t//ktx breaks without some configs\ninstall nquakesv_gamecode\t//can't run ktx without ktx!\n//install nquakesv_maps\t\t//urgh, massive bloated download.\n\n//gah, make sure stuff still works when embedded in a browser.\narchivedpackage id1/pak0.pak 0x4f069cac id1/pak0.pak https://fte.triptohell.info/qsw106.zip\n\n"
  },
  {
    "path": "games/quake-demo.fmf",
    "content": "//This can skip most of its details as the important stuff is baked into the engine and its best to avoid dupes.\n//View some other file for a real example.\n\n//Registered Quake is a strict superset of the shareware version, so we don't really need to care about whether its a demo or not.\n\nFTEManifestVer 1\ngame quake\nname Quake\n\n//the shareware license requires distribution in whole... so we'll just download the whole shareware zip and extract only what we need - assuming the user does not already have a copy.\narchivedpackage id1/pak0.pak 0x4f069cac id1/pak0.pak https://fte.triptohell.info/moodles/live/QUAKE_SW.zip\n\n"
  },
  {
    "path": "games/xonotic_85.fmf",
    "content": "//-prefixed lines are effectively inserted before the default.cfg\r\n//+prefixed lines are inserted AFTER default.cfg and will thus conflict/override the mod's own settings\r\n\r\ngame xonotic\r\nname \"FTEized Xonotic (0.8.5)\"\r\nprotocolname \"Xonotic\"\r\nbasegame data\r\nbasegame *ftedata\t//so stuff gets written here instead.\r\n\r\n//xonotic 0.8.5 packages.\r\n//package \"data/font-unifont-20220627.pk3\"\t\t\tcrc 0xa39ce3ad mirror \"unzip:Xonotic/data/font-unifont-20220627.pk3,https://github.com/garymoon/xonotic/releases/download/xonotic-v0.8.5/xonotic-0.8.5.zip\"\r\n//package \"data/font-xolonium-20220627.pk3\"\t\t\tcrc 0x9553d8a4 mirror \"unzip:Xonotic/data/font-xolonium-20220627.pk3,https://github.com/garymoon/xonotic/releases/download/xonotic-v0.8.5/xonotic-0.8.5.zip\"\r\n//package \"data/xonotic-20220627-data.pk3\"\t\t\tcrc 0x57a1ba9c mirror \"unzip:Xonotic/data/xonotic-20220627-data.pk3,https://github.com/garymoon/xonotic/releases/download/xonotic-v0.8.5/xonotic-0.8.5.zip\"\r\n//package \"data/xonotic-20220627-maps.pk3\"\t\t\tcrc 0x1d3d7cf1 mirror \"unzip:Xonotic/data/xonotic-20220627-maps.pk3,https://github.com/garymoon/xonotic/releases/download/xonotic-v0.8.5/xonotic-0.8.5.zip\"\r\n//package \"data/xonotic-20220627-music.pk3\"\t\t\tcrc 0x5d1dd373 mirror \"unzip:Xonotic/data/xonotic-20220627-music.pk3,https://github.com/garymoon/xonotic/releases/download/xonotic-v0.8.5/xonotic-0.8.5.zip\"\r\n//package \"data/xonotic-20220627-nexcompat.pk3\"\t\tcrc 0x83f613b9 mirror \"unzip:Xonotic/data/xonotic-20220627-nexcompat.pk3,https://github.com/garymoon/xonotic/releases/download/xonotic-v0.8.5/xonotic-0.8.5.zip\"\r\n\r\n//This sucks. overrides the *.dat files to work around gmqcc bugs (also smaller dat files at runtime, but still stupid and annoying and means compat hits the fan when using the standard csprogs on dp servers)\r\n//  cd qcsrc && make QCC=fteqcc QCCFLAGS_WERROR= && zip xonotic-fixups-9.8.5.pk3 ../*.dat ../*.lno\r\n//package \"data/xonotic-fixups-0.8.5.pk3\"\tcrc 0xe27b8ad3 //mirror \"xonotic-fixups-0.8.5.pk3\"\r\n//-set pr_fixbrokenqccarrays 2\t//this can be used instead, but can cause its own problems.\r\n\r\n\r\n\r\n-set dpcompat_set 1\t\t//gah\r\n-set dpcompat_console 1\t//DP's $ stuff works differently from quakeworld. definitely more annoying, but xonotic's configs expect dp behaviour\r\n-set dpcompat_smallerfonts 1 //in case its needed.\r\n-set dpcompat_strcat_limit 16383\t//work around xonotic network compatibility issue.\r\n\r\n-set v_gammainverted 1\r\n-set con_stayhidden 0\r\n-set allow_download_pakcontents 1\r\n-set allow_download_refpackages 0\r\n-set sv_bigcoords \"\"\r\n-set map_autoopenportals 1\r\n-set sv_port 26000\r\n-set cl_defaultport 26000\r\n-set r_particlesdesc effectinfo\r\n\r\n-if ($dedicated < 1) then set qport 654\t\t//xonotic expects this cvar to exist only in clients (and uses it to detect client vs dedicated server). this entirely ignores 'setrenderer sv' of course. good luck with that one.\r\n\r\n-set gl_info_extensions \"GL_EXT_texture_compression_s3tc GL_ARB_texture_compression\" //fte doesn't have a cvar that contains opengl extensions, in part because they don't apply to other renderers thus making it kinda useless. and xonotic likes spamming warning messages, even though they're invalid half the time, and impossible for a user to work around the rest of the time.\r\n\r\n-set cl_movement 1\t\t//xonotic's physics are inconsistent with itself, but it judders even without prediction so we might as well enable it\r\n-set cl_movement_replay \"\"\t//just to silence spam. actual value doesn't change anything in fte, and xonotic keeps changing it randomly anyway.\r\n-set sv_nqplayerphysics 0\t//xonotic runs async player physics. we've no need to force anything.\r\n-set pr_enable_uriget 0 //enabling this causes xonotic's menu to get pissy about updates, which probably won't work very well with fte, so best to block all that crap. sadly this also breaks the stats stuff.\r\n-set cl_lerp_smooth 0\t//don't run in the past. DP has nothing like that the whole servertime/serverprevtime stuff will just get confused like hell.\r\n-set r_shadow_realtime_nonworld_lightmaps 2 //DP's q3bsp lighting is doubled relative to q3, for some reason.\r\n\r\n-set con_chatsize 8 \t//doesn't exist in FTE, resulting in qc division by 0 if not set.\r\n-set con_textsize 12\t//keep the console sized at a fixed 12pt point, regardless of actual res.\r\n//con_stayhidden 1\t//don't pop up the console randomly.\r\n-set dpcompat_makeshitup 2 //ignore most of what the shaders are saying and just make shit up, adding deluxe+specular+fullbrights+reflectcube etc.\r\n-set dpcompat_findradiusarealinks 1 //matches dp. should help performance.\r\n-set sv_gameplayfix_spawnbeforethinks 1 //some nq mods actually break without this glitch. DP forces a fix, so match that behaviour because xonotic needs it.\r\n\r\n//I copied this list out of the dp sourcecode. I don't know if xonotic needs them, but it normally has them anyway.\r\n//fte doesn't even implement them all either, so that's fun. still, if it ever does then they'll at least get their expected values.\r\n-set sv_gameplayfix_blowupfallenzombies 1\r\n-set sv_gameplayfix_findradiusdistancetobox 1\r\n-set sv_gameplayfix_grenadebouncedownslopes 1\r\n-set sv_gameplayfix_slidemoveprojectiles 1\r\n-set sv_gameplayfix_upwardvelocityclearsongroundflag 1\r\n-set sv_gameplayfix_setmodelrealbox 1\r\n-set sv_gameplayfix_droptofloorstartsolid 1\r\n-set sv_gameplayfix_droptofloorstartsolid_nudgetocorrect 1\r\n-set sv_gameplayfix_noairborncorpse 1\r\n-set sv_gameplayfix_noairborncorpse_allowsuspendeditems 1\r\n-set sv_gameplayfix_easierwaterjump 1\r\n-set sv_gameplayfix_delayprojectiles 1\r\n-set sv_gameplayfix_multiplethinksperframe 1\r\n-set sv_gameplayfix_fixedcheckwatertransition 1\r\n-set sv_gameplayfix_q1bsptracelinereportstexture 1\r\n-set sv_gameplayfix_swiminbmodels 1\r\n-set sv_gameplayfix_downtracesupportsongroundflag 1\r\n-set sv_gameplayfix_q2airaccelerate 0\r\n\r\n//most of these cvars are not defined by FTE, but xonotic expects them anyway. so make sure they're there for the pmove code.\r\n-set sv_jumpvelocity 270\r\n-set sv_maxairspeed 30\r\n-set sv_nostep 0\r\n-set sv_jumpstep 1\r\n-set sv_wateraccelerate -1\r\n-set sv_waterfriction -1\r\n-set sv_airaccel_sideways_friction 0\r\n-set sv_airaccel_qw 1\r\n-set sv_airaccel_qw_stretchfactor 0\r\n-set sv_airstopaccelerate 0\r\n-set sv_airstrafeaccelerate 0\r\n-set sv_maxairstrafespeed 0\r\n-set sv_airstrafeaccel_qw 0\r\n-set sv_aircontrol 0\r\n-set sv_aircontrol_penalty 0\r\n-set sv_aircontrol_power 2\r\n-set sv_aircontrol_backwards 0\r\n-set sv_airspeedlimit_nonqw 0\r\n-set sv_warsowbunny_turnaccel 0\r\n-set sv_warsowbunny_accel 0.1585\r\n-set sv_warsowbunny_topspeed 925\r\n-set sv_warsowbunny_backtosideratio 0.8\r\n-set sv_airaccelerate -1\t//dp's default... to mean defer to sv_accelerate.\r\n\r\n//all these cvars are implemented using a custom conback shader.\r\n-set scr_conalphafactor 1\r\n-set scr_conalpha2factor 0\r\n-set scr_conalpha3factor 0\r\n-set scr_conbrightness 1\r\n-set scr_conscroll_x 0\r\n-set scr_conscroll_y 0\r\n-set scr_conscroll2_x 0\r\n-set scr_conscroll2_y 0\r\n-set scr_conscroll3_x 0\r\n-set scr_conscroll3_y 0\r\n-set r_textcontrast 1 //fixes colourpicker widget\r\n\r\n-set com_parseutf8 1\t//fte's name. interpret text as utf-8 when printing.\r\n-set utf8_enable 1\t//not fte's cvar name. have the qc builtins operate on codepoints rather than bytes (slower, still unaware of emoji, accents, etc).\r\n\r\n-alias playermodel model\t//FTE uses userinfo stuff for this stuff\r\n-alias playerskin skin\t\t//FTE uses userinfo stuff for this stuff\r\n\r\n-set pr_csqc_memsize \"64m\" //xonotic is shit and inefficient. it really does need FAR too much memory.\r\n-set pr_ssqc_memsize \"96m\" //xonotic is shit and inefficient. it really does need FAR too much memory.\r\n\r\n-set r_deluxemapping 1 //load deluxemaps, cos they're a little prettier\r\n-set dpcompat_nopremulpics 1 //0 has problems with dds files etc, which is frankly a shame.\r\n\r\n//this won't force the menu to use a less laggy cursor, but should at least give the console a little more personality.\r\n-set cl_cursor \"gfx/menu/luma/cursor.jpg\"\r\n-set cl_cursor_scale 0.333\r\n-set cl_cursor_bias_x 10\r\n-set cl_cursor_bias_y 3.33\r\n\r\n-set vid_gl20 1\t//make sure various menu options are not greyed out\r\n\r\n-set gl_specular 1\t\t\t//DP's default, that makes things too shiny (because if you're going to make a texture then sadly most people want it to be seen)\r\n-set dpcompat_corruptglobals 1 //stomp on random qc globals that were meant to be cachable from one frame to the next.\r\n-set vid_pixelheight 1\t\t//DP mods have a nasty tendancy to get confused when this cvar doesn't exist.\r\n\r\n-set s_al_disable 1\t\t\t//xonotic seems to clog ALL the openal audio buffers with useless sounds, so disable openal to prevent that from happening.\r\n\r\n-set dpcompat_nopreparse 1 \t//No unicasts and stuff split over packets mean lengths of custom stuff cannot be determined, resulting in translation of other things failing to translate.\r\n-set cl_loopbackprotocol dpp7\t//needs to be some sort of nq protocol due to nopreparse. might as well match dp and network player velocity entirely separately from its position...\r\n-set sv_listen_dp 1\t\t//listen for dp-protocol client connections\r\n-set sv_listen_qw 0\t\t//ignore standard QW clients (they'll just get dp responses which they'll ignore)\r\n-set sv_listen_nq 0\t\t//nq-protocol clients will just be ignored.\r\n-set sv_bigcoords 1\t\t//kinda required for dpp7 to function correctly.\r\n\r\n-set r_particlesdesc effectinfo\r\n-set sv_mintic 0.0333\t\t//should match dp's rate.\r\n-set sv_maxtic 0 //fixed tick rates\r\n-set cl_nolerp 0\r\n\r\n-set sv_cullentities_trace 0 //still needs work. fte still has performance issues with tracing through patches\r\n\r\n\r\n-set sv_curl_serverpackages \"\"\t//not in FTE, but xonotic warns when missing.\r\n\r\n\r\n-set _cl_name \"Stalking is illegal\"\r\n-set pr_autocreatecvars 0 //so we're more likely to notice unknown cvars.\r\n\r\n//misc cvars that the gamecode checks for for unknowable reasons\r\n//set r_texture_dds_load 1\r\n//set vid_desktopfullscreen ${vid_fullscreen==2}\r\n//set hud_panel_notify_print\r\n//set con_chattime\r\n//set con_chatsound\r\n//set net_slist_pause\r\n//set r_viewfbo ${r_hdr_framebuffer}\r\n//set r_depthfirst\r\n//set gl_vbo\t//removed from dp too\r\n//set v_glslgamma\r\n//set r_glsl_saturation\r\n//set r_hdr_scenebrightness\r\n//set sys_memsize_virtual\r\n//set sys_memsize_physical\r\n//set mod_q3bsp_nolightmaps\r\n//set r_shadow_gloss ${gl_specular}\r\n//set r_water ${r_portalrecursion>0}\r\n//set r_water_resolutionmultiplier ${r_reflectrefract_scale}\r\n//set cl_decals\r\n//set cl_decals_models\r\n//set r_drawdecals_drawdistance\r\n//set cl_decals_fadetime\r\n//set r_shadow_usenormalmap\r\n//set r_coronas_occlusionquery\r\n//set r_motionblur\r\n//set cl_particles\r\n//set r_drawparticles_drawdistance\r\n//set snd_staticvolume\r\n//set snd_channel0volume\r\n//set snd_channel3volume\r\n//set snd_channel6volume\r\n//set snd_channel7volume\r\n//set snd_channel4volume\r\n//set snd_channel2volume\r\n//set snd_channel1volume\r\n//set snd_mutewhenidle ${snd_inactive==0}\r\n//set snd_speed ${snd_khz}\r\n//set snd_channels ${snd_numspeakers}\r\n//set snd_swapstereo ${snd_leftisright}\r\n//set snd_spatialization_control\r\n//set m_accelerate\r\n//set con_closeontoggleconsole\r\n//set cl_movement_track_canjump\r\n//set _cl_rate ${rate}\r\n//set cl_curl_maxdownloads\r\n//set cl_curl_maxspeed\r\n//set shownetgraph ${r_netgraph}\r\n//set cl_minfps\r\n//set cl_maxidlefps ${cl_idlefps}\r\n//set showtime\r\n//set cl_capturevideo\r\n//set g_campaignxonoticbeta_won\r\n//set r_glsl_postprocess\r\n//set joy_active\r\n//set con_chatrect_x\r\n//set con_chatrect_y\r\n//set con_chatwidth\r\n//set net_slist_favorites\r\n//set cl_bobfall\r\n//set cl_smoothviewheight\r\n\r\n-maxplayers 16\t\t\t\t\t\t\t//xonotic doesn't like empty to mean ((deathmatch||coop)?32:1), so be explicit.\r\n-deathmatch 1\t\t\t\t\t\t\t//normally gets set to 1 in dp when maxplayers is forced (coop be damned).\r\n\r\n-pr_precachepic_slow 1\t\t\t\t\t//xonotic sucks for this. its abuse of drawgetimagesize slows stuff down too.\r\n-set mod_precache 2\t\t\t\t\t\t//disabling this slashes ram usage. there might be some stutter from loading textures, but no outright stalls at least. the prediction misses are worse.\r\n-set mod_precache 0\t\t\t\t\t\t//FIXME: override sv_precacheplayermodels instead.\r\n//FIXME: -set _q3bsp_bihtraces 1\t\t//work around q3map2 being so defective and throwing 5000 brushes into a single leaf.\r\n-set sv_gameplayfix_setmodelrealbox 0\t//setting this to 0 prevents the server from stalling waiting for the thing to load.\r\n+set sv_precacheplayermodels 0\t\t\t//fte has loader threads, and the extra upfront load time is just annoying (override the mod's default). \r\n-set s_precache 0\t\t\t\t\t\t//mneh, sounds are overrated anyway.\r\n\r\n"
  },
  {
    "path": "imgtool.c",
    "content": "#include \"quakedef.h\"\n#include \"shader.h\"\n#undef stderr\n#define stderr stdout\n\n#include <limits.h>\n#include <ctype.h>\n#ifdef _WIN32\n#include <windows.h>\n#endif\nstatic qboolean verbose;\nvoid VARGS Sys_Error (const char *fmt, ...)\n{\n\tva_list\t\targptr;\n\n\tva_start (argptr,fmt);\n\tvfprintf (stderr,fmt,argptr);\n\tva_end (argptr);\n\tfflush(stderr);\n\n\texit(1);\n}\nvoid VARGS Con_Printf (const char *fmt, ...)\n{\n\tva_list\t\targptr;\n\n\tva_start (argptr,fmt);\n\tvfprintf (stderr,fmt,argptr);\n\tva_end (argptr);\n\tfflush(stderr);\n}\nvoid VARGS Con_TPrintf (const char *fmt, ...)\n{\n\tva_list\t\targptr;\n\n\tva_start (argptr,fmt);\n\tvfprintf (stderr,fmt,argptr);\n\tva_end (argptr);\n\tfflush(stderr);\n}\nvoid VARGS Con_DPrintf (const char *fmt, ...)\n{\n\tva_list\t\targptr;\n\tif (!verbose)\n\t\treturn;\n\n\tva_start (argptr,fmt);\n\tvfprintf (stderr,fmt,argptr);\n\tva_end (argptr);\n\tfflush(stderr);\n}\nvoid VARGS Con_ThrottlePrintf (float *timer, int developerlevel, const char *fmt, ...)\n{\n\tva_list\t\targptr;\n\n\tva_start (argptr,fmt);\n\tvfprintf (stderr,fmt,argptr);\n\tva_end (argptr);\n\tfflush(stderr);\n}\n\nvoid *ZF_Malloc(size_t size)\n{\n#if defined(__linux__)\n\tvoid *ret = NULL;\n\tif (!posix_memalign(&ret, max(sizeof(float)*4, sizeof(void*)), size))\n\t\tmemset(ret, 0, size);\n\treturn ret;\n#else\n\treturn calloc(size, 1);\n#endif\n}\nvoid *Z_Malloc(size_t size)\n{\n\tvoid *r = ZF_Malloc(size);\n\tif (!r)\n\t\texit(1);\n\treturn r;\n}\nvoid *BZ_Malloc(size_t size)\n{\n\treturn Z_Malloc(size);\n}\nvoid *BZF_Malloc(size_t size)\n{\n\treturn Z_Malloc(size);\n}\nvoid BZ_Free(void *p)\n{\n\tfree(p);\n}\nvoid Z_Free(void *p)\n{\n\tfree(p);\n}\n#ifdef _WIN32\n// don't use these functions in MSVC8\n#if (_MSC_VER < 1400)\nint QDECL linuxlike_snprintf(char *buffer, int size, const char *format, ...)\n{\n#undef _vsnprintf\n\tint ret;\n\tva_list\t\targptr;\n\n\tif (size <= 0)\n\t\treturn 0;\n\tsize--;\n\n\tva_start (argptr, format);\n\tret = _vsnprintf (buffer,size, format,argptr);\n\tva_end (argptr);\n\n\tbuffer[size] = '\\0';\n\n\treturn ret;\n}\nint QDECL linuxlike_vsnprintf(char *buffer, int size, const char *format, va_list argptr)\n{\n#undef _vsnprintf\n\tint ret;\n\n\tif (size <= 0)\n\t\treturn 0;\n\tsize--;\n\n\tret = _vsnprintf (buffer,size, format,argptr);\n\n\tbuffer[size] = '\\0';\n\n\treturn ret;\n}\n#elif (_MSC_VER < 1900)\nint VARGS linuxlike_snprintf_vc8(char *buffer, int size, const char *format, ...)\n{\n\tint ret;\n\tva_list\t\targptr;\n\n\tva_start (argptr, format);\n\tret = vsnprintf_s (buffer,size, _TRUNCATE, format,argptr);\n\tva_end (argptr);\n\n\treturn ret;\n}\n#endif\n#endif\n\n\n\n\n#include <sys/stat.h>\n\nvoid FS_CreatePath(const char *pname, enum fs_relative relativeto)\n{\n\tchar *t = strdup(pname), *sl = t;\n\twhile ((sl=strchr(sl, '/')))\n\t{\n\t\t*sl=0;\n#ifdef _WIN32\n\t\tCreateDirectoryA(t, NULL);\n#else\n\t\tmkdir(t, 0777);\n#endif\n\t\t*sl++='/';\n\t}\n\tfree(t);\n}\nqboolean FS_Remove (const char *path, enum fs_relative relativeto)\n{\n\t//remove is part of c89.\n\tif (remove(path) == -1)\n\t\treturn false;\n\treturn true;\n}\nqboolean FS_SystemPath(const char *fname, enum fs_relative relativeto, char *out, int outlen)\n{\n\tQ_strncpyz(out, fname, outlen);\n\treturn true;\n}\nchar *COM_SkipPath (const char *pathname)\n{\n\tconst char\t*last;\n\n\tlast = pathname;\n\twhile (*pathname)\n\t{\n\t\tif (*pathname=='/' || *pathname == '\\\\')\n\t\t\tlast = pathname+1;\n\t\tpathname++;\n\t}\n\treturn (char *)last;\n}\n\n#ifdef __unix__\n#include <sys/stat.h>\n#endif\nvoid *FS_LoadMallocFile (const char *path, size_t *fsize)\n{\n\tqbyte *data = NULL;\n\tFILE *f;\n#ifdef __unix__\n\tstruct stat sb;\n\tif (stat(path, &sb) < 0)\n\t\treturn NULL;\n\tif ((sb.st_mode&S_IFMT) != S_IFREG)\n\t\treturn NULL;\n#endif\n\n\tf = fopen(path, \"rb\");\n\tif (f)\n\t{\n\t\tlong int sz;\n\t\tif (fseek(f, 0, SEEK_END) >= 0)\n\t\t{\n\t\t\tsz = ftell(f);\n\t\t\tif (sz >= 0)\n\t\t\t{\n\t\t\t\t*fsize = sz;\n\t\t\t\tfseek(f, 0, SEEK_SET);\n\t\t\t\tdata = ZF_Malloc(*fsize+1);\n\t\t\t\tif (data)\n\t\t\t\t{\n\t\t\t\t\tdata[*fsize] = 0;\n\t\t\t\t\tfread(data, 1, *fsize, f);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tCon_Printf(\"Unable to allocate memory for %s\\n\", path);\n\t\t\t}\n\t\t}\n\t\tfclose(f);\n\t}\n\treturn data;\n}\n#ifdef _WIN32\ndllhandle_t *Sys_LoadLibrary(const char *name, dllfunction_t *funcs)\n{\n\treturn NULL;\n}\n#else\n#include <dlfcn.h>\nvoid Sys_CloseLibrary(dllhandle_t *lib)\n{\n\tdlclose((void*)lib);\n}\ndllhandle_t *Sys_LoadLibrary(const char *name, dllfunction_t *funcs)\n{\n\tint i;\n\tdllhandle_t *lib;\n\n\tlib = NULL;\n\tif (!lib)\n\t\tlib = dlopen (name, RTLD_LOCAL|RTLD_LAZY);\n//\tif (!lib && !strstr(name, \".so\"))\n//\t\tlib = dlopen (va(\"%s.so\", name), RTLD_LOCAL|RTLD_LAZY);\n\tif (!lib)\n\t{\n//\t\tCon_DPrintf(\"%s\\n\", dlerror());\n\t\treturn NULL;\n\t}\n\n\tif (funcs)\n\t{\n\t\tfor (i = 0; funcs[i].name; i++)\n\t\t{\n\t\t\t*funcs[i].funcptr = dlsym(lib, funcs[i].name);\n\t\t\tif (!*funcs[i].funcptr)\n\t\t\t\tbreak;\n\t\t}\n\t\tif (funcs[i].name)\n\t\t{\n\t\t\tCon_DPrintf(\"Unable to find symbol \\\"%s\\\" in \\\"%s\\\"\\n\", funcs[i].name, name);\n\t\t\tSys_CloseLibrary((dllhandle_t*)lib);\n\t\t\tlib = NULL;\n\t\t}\n\t}\n\n\treturn (dllhandle_t*)lib;\n}\n#endif\n\nstruct imgfile_s\n{\n\tvfsfile_t pub;\n\tFILE *f;\n};\nstatic qboolean QDECL ImgFile_Close(struct vfsfile_s *file)\n{\n\tstruct imgfile_s *f = (struct imgfile_s*)file;\n\tfclose(f->f);\n\tfree(f);\n\treturn true;\n}\nstatic int QDECL ImgFile_WriteBytes(struct vfsfile_s *file, const void *buffer, int bytestowrite)\n{\n\tstruct imgfile_s *f = (struct imgfile_s*)file;\n\treturn fwrite(buffer, 1, bytestowrite, f->f);\n}\nstatic qboolean QDECL ImgFile_Seek(struct vfsfile_s *file, qofs_t newofs)\n{\n\tstruct imgfile_s *f = (struct imgfile_s*)file;\n\tif (fseek(f->f, newofs, SEEK_SET)==0)\n\t\treturn true;\t//success\n\treturn false;\n}\nstatic qofs_t QDECL ImgFile_Tell(struct vfsfile_s *file)\n{\n\tstruct imgfile_s *f = (struct imgfile_s*)file;\n\treturn ftell(f->f);\n}\nvfsfile_t *QDECL FS_OpenVFS(const char *filename, const char *mode, enum fs_relative relativeto)\n{\n\tif (!strcmp(mode, \"wb\"))\n\t{\n\t\tstruct imgfile_s *r = malloc(sizeof(*r));\n\t\tr->f = fopen(filename, mode);\n\t\tr->pub.seekstyle = SS_UNSEEKABLE;\n\t\tr->pub.Close = ImgFile_Close;\n\t\tr->pub.WriteBytes = ImgFile_WriteBytes;\n\t\tr->pub.Seek = ImgFile_Seek;\n\t\tr->pub.Tell = ImgFile_Tell;\n\t\tif (r->f)\n\t\t\treturn &r->pub;\n\t\tfree(r);\n\t}\n\treturn NULL;\n}\nqboolean COM_WriteFile (const char *filename, enum fs_relative fsroot, const void *data, int len)\n{\n\tvfsfile_t *f = FS_OpenVFS(filename, \"wb\", fsroot);\n\tqboolean ret = false;\n\tif (f)\n\t{\n\t\tret = len==VFS_WRITE(f, data, len);\n\t\tif (!VFS_CLOSE(f))\n\t\t\tret = false;\n\t}\n\treturn ret;\n}\nvoid QDECL Q_strncpyz(char *d, const char *s, int n)\n{\n\tint i;\n\tn--;\n\tif (n < 0)\n\t\treturn;\t//this could be an error\n\n\tfor (i=0; *s; i++)\n\t{\n\t\tif (i == n)\n\t\t\tbreak;\n\t\t*d++ = *s++;\n\t}\n\t*d='\\0';\n}\n\nqboolean VARGS Q_vsnprintfz (char *dest, size_t size, const char *fmt, va_list argptr)\n{\n\tsize_t ret;\n#ifdef _WIN32\n\t//doesn't null terminate.\n\t//returns -1 on truncation\n\tret = _vsnprintf (dest, size, fmt, argptr);\n\tdest[size-1] = 0;\t//shitty paranoia\n#else\n\t//always null terminates.\n\t//returns length regardless of truncation.\n\tret = vsnprintf (dest, size, fmt, argptr);\n#endif\n#ifdef _DEBUG\n\tif (ret>=size)\n\t\tSys_Error(\"Q_vsnprintfz: Truncation\\n\");\n#endif\n\t//if ret is -1 (windows oversize, or general error) then it'll be treated as unsigned so really long. this makes the following check quite simple.\n\treturn ret>=size;\n}\n\nqboolean VARGS Q_snprintfz (char *dest, size_t size, const char *fmt, ...)\n{\n\tva_list\t\targptr;\n\tqboolean ret;\n\n\tva_start (argptr, fmt);\n\tret = Q_vsnprintfz(dest, size, fmt, argptr);\n\tva_end (argptr);\n\treturn ret;\n}\n\n//palette data is used in lmps, as well as written into pcxes or wads, probably some other things.\nqbyte\t\t*host_basepal;\nunsigned int\td_8to24rgbtable[256];\nunsigned int\td_8to24bgrtable[256];\nstatic qbyte default_quakepal[768] =\n{\t//the quake palette was released into the public domain (or at least gpl) to ease development of tools writing quake-format data.\n0,0,0,15,15,15,31,31,31,47,47,47,63,63,63,75,75,75,91,91,91,107,107,107,123,123,123,139,139,139,155,155,155,171,171,171,187,187,187,203,203,203,219,219,219,235,235,235,15,11,7,23,15,11,31,23,11,39,27,15,47,35,19,55,43,23,63,47,23,75,55,27,83,59,27,91,67,31,99,75,31,107,83,31,115,87,31,123,95,35,131,103,35,143,111,35,11,11,15,19,19,27,27,27,39,39,39,51,47,47,63,55,55,75,63,63,87,71,71,103,79,79,115,91,91,127,99,99,\n139,107,107,151,115,115,163,123,123,175,131,131,187,139,139,203,0,0,0,7,7,0,11,11,0,19,19,0,27,27,0,35,35,0,43,43,7,47,47,7,55,55,7,63,63,7,71,71,7,75,75,11,83,83,11,91,91,11,99,99,11,107,107,15,7,0,0,15,0,0,23,0,0,31,0,0,39,0,0,47,0,0,55,0,0,63,0,0,71,0,0,79,0,0,87,0,0,95,0,0,103,0,0,111,0,0,119,0,0,127,0,0,19,19,0,27,27,0,35,35,0,47,43,0,55,47,0,67,\n55,0,75,59,7,87,67,7,95,71,7,107,75,11,119,83,15,131,87,19,139,91,19,151,95,27,163,99,31,175,103,35,35,19,7,47,23,11,59,31,15,75,35,19,87,43,23,99,47,31,115,55,35,127,59,43,143,67,51,159,79,51,175,99,47,191,119,47,207,143,43,223,171,39,239,203,31,255,243,27,11,7,0,27,19,0,43,35,15,55,43,19,71,51,27,83,55,35,99,63,43,111,71,51,127,83,63,139,95,71,155,107,83,167,123,95,183,135,107,195,147,123,211,163,139,227,179,151,\n171,139,163,159,127,151,147,115,135,139,103,123,127,91,111,119,83,99,107,75,87,95,63,75,87,55,67,75,47,55,67,39,47,55,31,35,43,23,27,35,19,19,23,11,11,15,7,7,187,115,159,175,107,143,163,95,131,151,87,119,139,79,107,127,75,95,115,67,83,107,59,75,95,51,63,83,43,55,71,35,43,59,31,35,47,23,27,35,19,19,23,11,11,15,7,7,219,195,187,203,179,167,191,163,155,175,151,139,163,135,123,151,123,111,135,111,95,123,99,83,107,87,71,95,75,59,83,63,\n51,67,51,39,55,43,31,39,31,23,27,19,15,15,11,7,111,131,123,103,123,111,95,115,103,87,107,95,79,99,87,71,91,79,63,83,71,55,75,63,47,67,55,43,59,47,35,51,39,31,43,31,23,35,23,15,27,19,11,19,11,7,11,7,255,243,27,239,223,23,219,203,19,203,183,15,187,167,15,171,151,11,155,131,7,139,115,7,123,99,7,107,83,0,91,71,0,75,55,0,59,43,0,43,31,0,27,15,0,11,7,0,0,0,255,11,11,239,19,19,223,27,27,207,35,35,191,43,\n43,175,47,47,159,47,47,143,47,47,127,47,47,111,47,47,95,43,43,79,35,35,63,27,27,47,19,19,31,11,11,15,43,0,0,59,0,0,75,7,0,95,7,0,111,15,0,127,23,7,147,31,7,163,39,11,183,51,15,195,75,27,207,99,43,219,127,59,227,151,79,231,171,95,239,191,119,247,211,139,167,123,59,183,155,55,199,195,55,231,227,87,127,191,255,171,231,255,215,255,255,103,0,0,139,0,0,179,0,0,215,0,0,255,0,0,255,243,147,255,247,199,255,255,255,159,91,83\n};\nqbyte GetPaletteIndexNoFB(int red, int green, int blue)\n{\n\tint i;\n\tint best=0;\n\tint bestdist=INT_MAX;\n\tint dist;\n\tfor (i = 0; i < 256-32; i++)\n\t{\n\t\tconst int diff[3] = {\n\t\t\thost_basepal[i*3+0]-red,\n\t\t\thost_basepal[i*3+1]-green,\n\t\t\thost_basepal[i*3+2]-blue};\n\t\tdist = DotProduct(diff,diff);\n\t\tif (dist < bestdist)\n\t\t{\n\t\t\tbestdist = dist;\n\t\t\tbest = i;\n\t\t\tif (!dist)\n\t\t\t\tbreak;\n\t\t}\n\t}\n\treturn best;\n}\nqbyte GetPaletteIndexRange(int first, int stop, int red, int green, int blue)\n{\n\tint i;\n\tint best=0;\n\tint bestdist=INT_MAX;\n\tint dist;\n\tfor (i = first; i < stop; i++)\n\t{\n\t\tconst int diff[3] = {\n\t\t\thost_basepal[i*3+0]-red,\n\t\t\thost_basepal[i*3+1]-green,\n\t\t\thost_basepal[i*3+2]-blue};\n\t\tdist = DotProduct(diff,diff);\n\t\tif (dist < bestdist)\n\t\t{\n\t\t\tbestdist = dist;\n\t\t\tbest = i;\n\t\t\tif (!dist)\n\t\t\t\tbreak;\n\t\t}\n\t}\n\treturn best;\n}\n\nconst char *palette = NULL;\n\nsh_config_t sh_config;\nviddef_t vid;\n\nvoid ImgTool_SetupPalette(void)\n{\n\tint i;\n\tFILE *fPAL;\n\tstatic qbyte cust_pal[768];\n\n\thost_basepal = default_quakepal;\n\n\tif (palette)\n\t{\n\t\tfPAL = fopen(palette, \"rb\");\n\t\n\t\tif (fPAL != NULL)\n\t\t{\n\t\t\tCon_Printf(\"using user-specified palette for files using palette.lmp\\n\");\n\t\t\tfread(cust_pal, 1, 768, fPAL);\n\t\t\tfclose(fPAL);\n\t\t\thost_basepal = cust_pal;\n\t\t}\n\t\telse\n\t\t\tCon_Printf(\"cannot find palette file %s\\n\", palette);\n\t}\n\telse\n\t\tCon_Printf(\"using built-in Quake palette for files using palette.lmp\\n\");\n\n\tfor (i = 0; i < 256; i++)\n\t{\n\t\td_8to24rgbtable[i] = (host_basepal[i*3+0]<<0)|(host_basepal[i*3+1]<<8)|(host_basepal[i*3+2]<<16);\n\t\td_8to24bgrtable[i] = (host_basepal[i*3+0]<<16)|(host_basepal[i*3+1]<<8)|(host_basepal[i*3+2]<<0);\n\t}\n\n\tsh_config.texture2d_maxsize = 1u<<31;\n\tsh_config.texture3d_maxsize = 1u<<31;\n\tsh_config.texture2darray_maxlayers = 1u<<31;\n\tsh_config.texturecube_maxsize = 8192;\n\tsh_config.texture_non_power_of_two = true;\n\tsh_config.texture_non_power_of_two_pic = true;\n\tsh_config.texture_allow_block_padding = true;\n\tsh_config.npot_rounddown = true;\t//shouldn't be relevant\n\tsh_config.havecubemaps = true;\t//I don't think this matters.\n\n\tImage_Init();\n}\n#ifdef IMGTOOL\nstatic void ImgTool_FreeMips(struct pendingtextureinfo *mips)\n{\n\tsize_t i;\n\tif (mips)\n\t{\n\t\tfor (i = 0; i < mips->mipcount; i++)\n\t\t\tif (mips->mip[i].needfree)\n\t\t\t\tBZ_Free(mips->mip[i].data);\n\t\tif (mips->extrafree)\n\t\t\tBZ_Free(mips->extrafree);\n\t\tBZ_Free(mips);\n\t}\n}\n\ntypedef struct\n{\n   unsigned int offset;\t\t\t\t// Position of the entry in WAD\n   unsigned int dsize;\t\t\t\t// Size of the entry in WAD file\n   unsigned int size;\t\t\t\t// Size of the entry in memory\n   char type;\t\t\t\t\t\t// type of entry\n   char cmprs;\t\t\t\t\t\t// Compression. 0 if none.\n   short dummy;\t\t\t\t\t\t// Not used\n   char name[16];\t\t\t\t\t// we use only first 8\n} wad2entry_t;\ntypedef struct\n{\n   char magic[4];\t\t\t\t\t//should be WAD2\n   unsigned int num;\t\t\t\t//number of entries\n   unsigned int offset;\t\t\t\t//location of directory\n} wad2_t;\n\nstatic const char *imagetypename[] = {\"2D\", \"3D\", \"Cube\", \"2DArray\", \"CubemapArray\", \"INVALID\", \"INVALID\", \"INVALID\", \"INVALID\", \"INVALID\"};\nstruct opts_s\n{\n\tint textype;\n\tconst char *defaultext;\t\t//.dds or whatever when the output's extension is not explicitly given.\n\tunsigned int flags;\t\t\t//image flags to use (affects how textures get interpreted a little)\n\tunsigned int mipnum;\t\t//when exporting to a mipless format, this is the mip level that is actually written. default 0.\n\tuploadfmt_t newpixelformat;\t//try to convert to this pixel format on export.\n\n\tint width, height;\n};\n\nstatic qboolean ImgTool_MipExport(struct opts_s *args, vfsfile_t *outfile, struct pendingtextureinfo *in, const char *mipname, int wadtype);\nstatic struct pendingtextureinfo *ImgTool_DecodeMiptex(struct opts_s *args, miptex_t *mip, size_t fsize, qbyte *basepal);\nvoid Image_GenerateMips(struct pendingtextureinfo *mips, unsigned int flags);\nint Image_WritePNG (const char *filename, enum fs_relative fsroot, int compression, void **buffers, int numbuffers, qintptr_t bufferstride, int width, int height, enum uploadfmt fmt, qboolean writemetadata);\nqboolean WriteTGA(const char *filename, enum fs_relative fsroot, const qbyte *fte_restrict rgb_buffer, qintptr_t bytestride, int width, int height, enum uploadfmt fmt);\n\nstatic enum uploadfmt ImgTool_ASTCToLDR(uploadfmt_t fmt)\n{\n\tif (fmt >= PTI_ASTC_FIRST && fmt <= PTI_ASTC_LAST)\n\t{\n\t\tif (fmt >= PTI_ASTC_4X4_HDR)\n\t\t\treturn (fmt-PTI_ASTC_4X4_HDR)+PTI_ASTC_4X4_LDR;\n\t\tif (fmt >= PTI_ASTC_4X4_SRGB)\n\t\t\treturn (fmt-PTI_ASTC_4X4_SRGB)+PTI_ASTC_4X4_LDR;\n\t}\n\tif (fmt == PTI_BC1_RGB)\n\t\treturn PTI_BC1_RGBA;\n\treturn fmt;\n}\n#ifdef _WIN32\nstatic void FS_MakeTempName(char *out, size_t outsize, char *prefix, char *suffix)\n{\n\tstatic char temp_path[MAX_PATH];\n\tchar temp_file_name[MAX_PATH];\n\tif (!*temp_path && !GetTempPathA(sizeof(temp_path), temp_path))\n\t\tSys_Error(\"FS_MakeTempName failed to get temp path\\n\");\n\tif (!GetTempFileNameA(temp_path, prefix, 0, temp_file_name))\n\t\tSys_Error(\"FS_MakeTempName failed\\n\");\n\n\tQ_snprintfz(out, outsize, \"%s%s\", temp_file_name, suffix);\n}\n#else\n#include <unistd.h>\nstatic void FS_MakeTempName(char *out, size_t outsize, char *prefix, char *suffix)\n{\n\tsnprintf(out, outsize, \"/tmp/%sXXXXXX%s\", prefix, suffix);\n\tclose(mkstemps(out, strlen(suffix)));\t//bsd4.3/posix1-2001\n}\n#endif\n\nstatic qboolean ImgTool_HasAlpha(struct pendingtextureinfo *mips)\n{\n\tif (mips->encoding == PTI_RGBA8 || mips->encoding == PTI_BGRA8 || mips->encoding == PTI_LLLA8 || mips->encoding == PTI_RGBA8_SRGB || mips->encoding == PTI_BGRA8_SRGB)\n\t{\n\t\tsize_t l = 0, pixels, p;\n\t\tqbyte *d;\n\t\tfor (l = 0; l < mips->mipcount; l++)\n\t\t{\n\t\t\tpixels = mips->mip[l].width * mips->mip[l].height * mips->mip[l].depth * 4;\n\t\t\td = mips->mip[l].data;\n\t\t\td+=3;\n\t\t\tfor (p = 0; p < pixels; p+=4)\n\t\t\t\tif (d[p] != 255)\n\t\t\t\t\treturn true;\t//a transparent pixel!\n\t\t}\n\t\treturn false;\n\t}\n\telse if (mips->encoding == PTI_L8A8 || mips->encoding == PTI_L8A8_SRGB)\n\t{\n\t\tsize_t l = 0, pixels, p;\n\t\tqbyte *d;\n\t\tfor (l = 0; l < mips->mipcount; l++)\n\t\t{\n\t\t\tpixels = mips->mip[l].width * mips->mip[l].height * mips->mip[l].depth * 2;\n\t\t\td = mips->mip[l].data;\n\t\t\td+=1;\n\t\t\tfor (p = 0; p < pixels; p+=2)\n\t\t\t\tif (d[p] != 255)\n\t\t\t\t\treturn true;\t//a transparent pixel!\n\t\t}\n\t\treturn false;\n\t}\n\telse if (mips->encoding == PTI_RGBA16)\n\t{\n\t\tsize_t l = 0, pixels, p;\n\t\tunsigned short *d;\n\t\tfor (l = 0; l < mips->mipcount; l++)\n\t\t{\n\t\t\tpixels = mips->mip[l].width * mips->mip[l].height * mips->mip[l].depth * 4;\n\t\t\td = mips->mip[l].data;\n\t\t\td+=3;\n\t\t\tfor (p = 0; p < pixels; p+=4)\n\t\t\t\tif (d[p] != 0xffff)\n\t\t\t\t\treturn true;\t//a transparent pixel!\n\t\t}\n\t\treturn false;\n\t}\n\telse\n\t\treturn Image_FormatHasAlpha(mips->encoding);\n}\n\n//copys all the data out and everything!\nstatic struct pendingtextureinfo *ImgTool_DupeMipchain(struct pendingtextureinfo *src)\n{\n\tstruct pendingtextureinfo *dest;\n\tqbyte *data;\n\tsize_t size = 0;\n\tsize_t m;\n\tfor(m = 0; m < src->mipcount; m++)\n\t\tsize += src->mip[m].datasize;\n\tdest = Z_Malloc(sizeof(*dest)+size);\n\t*dest = *src;\n\tdata = (qbyte*)(dest+1);\n\tfor(m = 0; m < src->mipcount; m++)\n\t{\n\t\tdest->mip[m].data = data;\n\t\tdest->mip[m].needfree = false;\n\t\tmemcpy(data, src->mip[m].data, src->mip[m].datasize);\n\t\tdata += src->mip[m].datasize;\n\t}\n\tdest->extrafree = NULL;\t//part of the texinfo itself.\n\treturn dest;\n}\n\nstatic qboolean ImgTool_ConvertPixelFormat(struct opts_s *args, const char *inname, struct pendingtextureinfo *mips)\n{\n\tstruct pendingtextureinfo tmp, *ret;\n\tsize_t m;\n\tchar raw[MAX_OSPATH];\n\tchar comp[MAX_OSPATH];\n\tchar command[MAX_OSPATH*3];\n\n\tqbyte *fdata;\n\tsize_t fsize;\n\tint bb,bw,bh,bd;\n\tqboolean canktx = false;\n\tuploadfmt_t targfmt = args->newpixelformat;\n\tint d,l, layers, r;\n\n\t//force it to bc1 if bc2 or bc3 with no alpha channel.\n\tif ((targfmt == PTI_BC2_RGBA || targfmt == PTI_BC3_RGBA) && !ImgTool_HasAlpha(mips))\n\t\ttargfmt = PTI_BC1_RGB;\n\n\tif (targfmt >= PTI_ASTC_FIRST && targfmt <= PTI_ASTC_LAST)\n\t{\n\t\tQ_snprintfz(command, sizeof(command), \"astcenc -c\");\n\t\tcanktx = true;\n\t}\n\telse if (targfmt == PTI_BC1_RGB)\n\t\tQ_snprintfz(command, sizeof(command), \"nvcompress -bc1%s\", (args->flags&IF_TRYBUMP)?\"n\":\"\");\n\telse if (targfmt == PTI_BC1_RGB_SRGB)\n\t\tQ_snprintfz(command, sizeof(command), \"nvcompress -bc1%s -srgb -dds10\", (args->flags&IF_TRYBUMP)?\"n\":\"\");\n\telse if (targfmt == PTI_BC1_RGBA)\n\t\tQ_snprintfz(command, sizeof(command), \"nvcompress -bc1a\");\n\telse if (targfmt == PTI_BC1_RGBA_SRGB)\n\t\tQ_snprintfz(command, sizeof(command), \"nvcompress -bc1a -srgb -dds10\");\n\telse if (targfmt == PTI_BC2_RGBA)\n\t\tQ_snprintfz(command, sizeof(command), \"nvcompress -bc2\");\n\telse if (targfmt == PTI_BC2_RGBA_SRGB)\n\t\tQ_snprintfz(command, sizeof(command), \"nvcompress -bc2 -srgb -dds10\");\n\telse if (targfmt == PTI_BC3_RGBA)\n\t\tQ_snprintfz(command, sizeof(command), \"nvcompress -bc3%s\", (args->flags&IF_TRYBUMP)?\"n\":\"\");\n\telse if (targfmt == PTI_BC3_RGBA_SRGB)\n\t\tQ_snprintfz(command, sizeof(command), \"nvcompress -bc3%s -srgb -dds10\", (args->flags&IF_TRYBUMP)?\"n\":\"\");\n\telse if (targfmt == PTI_BC4_R)\n\t\tQ_snprintfz(command, sizeof(command), \"nvcompress -bc4\");\n\telse if (targfmt == PTI_BC5_RG)\n\t\tQ_snprintfz(command, sizeof(command), \"nvcompress -bc5\");\n\telse if (targfmt == PTI_BC6_RGB_SFLOAT || targfmt == PTI_BC6_RGB_UFLOAT)\n\t\tQ_snprintfz(command, sizeof(command), \"nvcompress -bc6\");\n\telse if (targfmt == PTI_BC7_RGBA)\n\t\tQ_snprintfz(command, sizeof(command), \"nvcompress -bc7\");\n\telse if (targfmt == PTI_BC7_RGBA_SRGB)\n\t\tQ_snprintfz(command, sizeof(command), \"nvcompress -bc7 -srgb\");\n\telse\n\t{\n\t\tif (mips->encoding != targfmt)\n\t\t{\n\t\t\tqboolean forceformats[PTI_MAX];\n\t\t\tfor (m = 0; m < PTI_MAX; m++)\n\t\t\t\tforceformats[m] = (m == targfmt);\n\t\t\tImage_ChangeFormat(mips, forceformats, PTI_INVALID, inname);\n\t\t\tif (mips->encoding == targfmt)\n\t\t\t\treturn true;\n\n\t\t\t//switch to common formats...\n\t\t\tfor (m = 0; m < PTI_MAX; m++)\n\t\t\t\tforceformats[m] = (m == targfmt) || (m==PTI_RGBA8) || (m==PTI_RGBA32F);\n\t\t\tImage_ChangeFormat(mips, forceformats, PTI_INVALID, inname);\n\t\t\t//and try again...\n\t\t\tfor (m = 0; m < PTI_MAX; m++)\n\t\t\t\tforceformats[m] = (m == targfmt);\n\t\t\tImage_ChangeFormat(mips, forceformats, PTI_INVALID, inname);\n\n\t\t\treturn (mips->encoding == targfmt);\n\t\t}\n\t\treturn true;\n\t}\n\tif (canktx)\n\t\tFS_MakeTempName(raw, sizeof(raw), \"itr\", \".ktx\");\n\telse\n\t\tFS_MakeTempName(raw, sizeof(raw), \"itr\", \".png\");\n\tFS_MakeTempName(comp, sizeof(comp), \"itc\", \".ktx\");\n\n\ttmp.type = mips->type;\n\ttmp.encoding = mips->encoding;\n\ttmp.extrafree = NULL;\n\ttmp.mipcount = 1;\n\n\tImage_BlockSizeForEncoding(targfmt, &bb, &bw, &bh, &bd);\n\tQ_snprintfz(command+strlen(command), sizeof(command)-strlen(command), \" \\\"%s\\\" \\\"%s\\\"\", raw, comp);\n\tif (targfmt >= PTI_ASTC_FIRST && targfmt <= PTI_ASTC_LAST)\n\t{\n\t\tif (bd!=1)\n\t\t\tQ_snprintfz(command+strlen(command), sizeof(command)-strlen(command), \" %ix%ix%i -exhaustive\", bw, bh, bd);\n\t\telse\n\t\t\tQ_snprintfz(command+strlen(command), sizeof(command)-strlen(command), \" %ix%i -exhaustive\", bw, bh);\n\t}\n\tif (targfmt >= PTI_ASTC_4X4_SRGB && targfmt <= PTI_ASTC_12X12_SRGB)\n\t\tQ_strncatz(command, \" -srgb\", sizeof(command));\n\tif (targfmt >= PTI_ASTC_4X4_HDR && targfmt <= PTI_ASTC_12X12_HDR)\n\t\tQ_strncatz(command, \" -hdr\", sizeof(command));\n\tif (targfmt >= PTI_BC1_RGB && targfmt <= PTI_BC7_RGBA_SRGB && (strstr(inname, \"_n.\")||strstr(inname, \"_norm.\")))\n\t\tQ_strncatz(command, \" -normal\", sizeof(command));\t//looks like a normalmap... tweak metrics to favour normalised results.\n\n#ifdef _WIN32\n   Q_strncatz(command, \"> NUL 2>&1\", sizeof(command));\n#else\n   Q_strncatz(command, \">> /dev/null\", sizeof(command));\n#endif\n\n\tif (!canktx)\n\t{\n\t\tqboolean allowformats[PTI_MAX];\n\t\t//make sure the source pixel format is acceptable if we're forced to write a png\n\t\tfor (m = 0; m < PTI_MAX; m++)\n\t\t\tallowformats[m] =\n\t\t\t\t\t(m == PTI_RGBA8) || (m == PTI_RGBX8) ||\n\t\t\t\t\t(m == PTI_BGRA8) || (m == PTI_BGRX8) ||\n\t\t\t\t\t(m == PTI_LLLA8) || (m == PTI_LLLX8) ||\n\t\t\t\t\t(m == PTI_RGBA16) ||\n\t\t\t\t\t(m == PTI_L8) || (m == PTI_L8A8) ||\n\t\t\t\t\t/*(m == PTI_L16) ||*/\n\t\t\t\t\t(m == PTI_BGR8) || (m == PTI_BGR8) ||\n\t\t\t\t\t0;\n\t\tImage_ChangeFormat(mips, allowformats, PTI_INVALID, inname);\n\t}\n\n//\tCon_Printf(\"%s: Compressing %u mips\\n\", inname, mips->mipcount);\n\n\tImage_BlockSizeForEncoding(mips->encoding, &bb, &bw, &bh, &bd);\n\tfor (m = 0; m < mips->mipcount; m++)\n\t{\n\t\tqbyte *srcdata = mips->mip[m].data;\n\t\tsize_t srcsize = mips->mip[m].datasize;\n\t\tif (mips->type == PTI_3D)\n\t\t{\n\t\t\tlayers = 1;\n\t\t\td = mips->mip[m].depth;\n\t\t\ttmp.type = PTI_2D;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tlayers = mips->mip[m].depth;\n\t\t\td = 1;\n\t\t\ttmp.type = PTI_2D;\n\t\t}\n\t\tfor (l = 0; l < layers; l++)\n\t\t{\n//\t\t\tCon_DPrintf(\"Compressing %s mip %u, layer %u\\n\", inname, (unsigned)m, l);\n\t\t\ttmp.mip[0] = mips->mip[m];\n\t\t\ttmp.mip[0].needfree = false;\n\t\t\ttmp.mip[0].depth = d;\n\t\t\ttmp.mip[0].datasize = srcsize/layers;\n\t\t\ttmp.mip[0].data = srcdata + l * tmp.mip[0].datasize;\n\t\t\t(void)tmp;\n\n\t\t\tif (canktx)\n\t\t\t{\n\t#ifdef IMAGEFMT_KTX\n\t\t\t\tif (!Image_WriteKTXFile(raw, FS_SYSTEM, &tmp))\n\t#endif\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t#ifdef AVAIL_PNGLIB\n\t\t\t\tif (!Image_WritePNG(raw, FS_SYSTEM, 0, &tmp.mip[0].data, 1, tmp.mip[0].width*bb, tmp.mip[0].width, tmp.mip[0].height, tmp.encoding, false))\n\t#endif\n\t\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tr = system(command);\n\t\t\tif (r != EXIT_SUCCESS)\n\t\t\t{\n\t\t\t\tCon_Printf(\"The following system command failed with code %i: %s\\n\", r, command);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tfdata = FS_LoadMallocFile(comp, &fsize);\n\t\t\tret = Image_LoadMipsFromMemory(IF_NOMIPMAP, comp, comp, fdata, fsize);\n\t\t\tif (ret &&\tret->mip[0].width == mips->mip[m].width &&\n\t\t\t\t\t\tret->mip[0].height == mips->mip[m].height &&\n\t\t\t\t\t\tret->mip[0].depth == d &&\n\t\t\t\t\t\tImgTool_ASTCToLDR(ret->encoding) == ImgTool_ASTCToLDR(targfmt))\n\t\t\t{\n\t\t\t\tif (layers == 1)\t//just copy it over. FIXME: memory leak\n\t\t\t\t\tmips->mip[m] = ret->mip[0];\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tif (!l)\n\t\t\t\t\t{\n\t\t\t\t\t\tmips->mip[m].datasize = ret->mip[0].datasize * layers;\n\t\t\t\t\t\tmips->mip[m].data = BZ_Malloc(mips->mip[m].datasize);\n\t\t\t\t\t\tmips->mip[m].needfree = true;\n\t\t\t\t\t}\n\t\t\t\t\telse if (ret->mip[0].datasize != mips->mip[m].datasize/layers)\n\t\t\t\t\t\tbreak;\t//erk..?\n\t\t\t\t\tmemcpy((qbyte*)mips->mip[m].data + l * ret->mip[0].datasize, ret->mip[0].data, ret->mip[0].datasize);\n\t\t\t\t}\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\telse if (!ret)\n\t\t\t\tCon_Printf(\"Failed to read intermediate file %s\\n\", comp);\n\t\t\telse\n\t\t\t\tCon_Printf(\"intermediate file %s has unexpected size/depth/format %s:%i*%i*%i vs %s:%i*%i*%i\\n\", comp,\n\t\t\t\t\t\tImage_FormatName(ret->encoding), ret->mip[0].width, ret->mip[0].height, ret->mip[0].depth,\n\t\t\t\t\t\tImage_FormatName(targfmt), mips->mip[m].width, mips->mip[m].height, d);\n\t\t\tbreak;\n\t\t}\n\t\tif (l != layers)\n\t\t\tbreak;\n\t}\n\n\tmips->encoding = targfmt;\n\tmips->mipcount = m;\n\n\tif (mips->mipcount && targfmt >= PTI_BC1_RGB && targfmt <= PTI_BC7_RGBA_SRGB)\n\t{\t//d3d has some annoying limitations.\n\t\t//do not warn for astc files, their block sizes are too weird.\n\t\tImage_BlockSizeForEncoding(targfmt, &bb, &bw, &bh, &bd);\n\t\tif (mips->mip[0].width%bw || mips->mip[0].height%bh || mips->mip[0].depth%bd)\n\t\t\tCon_Printf(\"%s: mip0 of %i*%i is not a multiple of %i*%i (d3d warning)\\n\", inname, mips->mip[0].width, mips->mip[0].height, bw, bh);\n\t}\n\n\tFS_Remove(raw, FS_SYSTEM);\n\tFS_Remove(comp, FS_SYSTEM);\n\treturn true;\n}\n\nconst char *COM_GetFileExtension (const char *in, const char *term)\n{\n\tconst char *dot;\n\n\tif (!term)\n\t\tterm = in + strlen(in);\n\n\tfor (dot = term-1; dot >= in && *dot != '/' && *dot != '\\\\'; dot--)\n\t{\n\t\tif (*dot == '.')\n\t\t\treturn dot;\n\t}\n\treturn term+strlen(term);\n}\nstatic struct pendingtextureinfo *ImgTool_Read(struct opts_s *args, const char *inname)\n{\n\tqbyte *indata;\n\tsize_t fsize;\n\tstruct pendingtextureinfo *in;\n\tindata = FS_LoadMallocFile(inname, &fsize);\n\tif (!indata)\n\t\tCon_Printf(\"%s: unable to read\\n\", inname);\n\telse\n\t{\n\t\tconst char *ex = COM_GetFileExtension(inname, NULL);\n\t\tif (!strcasecmp(ex, \".mip\"))\n\t\t\tin = ImgTool_DecodeMiptex(args, (miptex_t*)indata, fsize, NULL);\n\t\telse\n\t\t\tin = Image_LoadMipsFromMemory(args->flags|IF_NOMIPMAP, inname, inname, indata, fsize);\n\t\tif (!in)\n\t\t{\n\t\t\tCon_Printf(\"%s: unsupported format\\n\", inname);\n\t\t\tBZ_Free(indata);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tCon_DPrintf(\"%s: %s %s, %i*%i, %i mips\\n\", inname, imagetypename[in->type], Image_FormatName(in->encoding), in->mip[0].width, in->mip[0].height, in->mipcount);\n\t\t\treturn in;\n\t\t}\n\t}\n\treturn NULL;\n}\nstatic struct pendingtextureinfo *ImgTool_Combine(struct opts_s *args, const char **namelist, unsigned int filecount)\n{\n\tstruct pendingtextureinfo *r, *t;\n\tunsigned int i;\n\tunsigned int layers = 0;\n\tunsigned int j = 0;\n\n\tstruct\n\t{\n\t\tconst char *fname;\n\t\tstruct pendingtextureinfo *in;\n\t} *srcs, *tmpsrcs;\n\n\tif (args->textype == PTI_3D)\n\t\targs->flags |= IF_NOMIPMAP;\t//generate any mipmaps after...\n\n\tsrcs = alloca(sizeof(*srcs)*filecount);\n\tfor (i = 0, j = 0; i < filecount; i++)\n\t{\n\t\tsrcs[j].in = t = ImgTool_Read(args, namelist[i]);\n\t\tif (srcs[j].in)\n\t\t{\n\t\t\tif (!j)\n\t\t\t{\n\t\t\t\t//get the image loader to massage pixel formats...\n\t\t\t\tmemset(sh_config.texfmt, 0, sizeof(sh_config.texfmt));\n\t\t\t\tsh_config.texfmt[srcs[j].in->encoding] = true;\n\t\t\t}\n\t\t\telse if (!t->mipcount || !t->mip[0].data)\n\t\t\t{\n\t\t\t\tCon_Printf(\"%s: no valid image data\\n\", namelist[i]);\n\t\t\t\tImgTool_FreeMips(srcs[j].in);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\telse if (t->encoding != srcs[0].in->encoding)\n\t\t\t{\n\t\t\t\tCon_Printf(\"%s: mismatched pixel format, (%s not %s) cannot combine\\n\", namelist[i], Image_FormatName(t->encoding), Image_FormatName(srcs[0].in->encoding));\n\t\t\t\tImgTool_FreeMips(srcs[j].in);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\telse if (t->type == PTI_CUBE && t->mip[0].depth != 6)\n\t\t\t{\n\t\t\t\tCon_Printf(\"%s: incorrect cubemap data\\n\", namelist[i]);\n\t\t\t\tImgTool_FreeMips(srcs[j].in);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\telse if (srcs[0].in->mip[0].width != t->mip[0].width || srcs[0].in->mip[0].height != t->mip[0].height)\n\t\t\t{\n\t\t\t\tCon_Printf(\"%s: incorrect image size\\n\", namelist[i]);\n\t\t\t\tImgTool_FreeMips(srcs[j].in);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\telse if (t->mip[0].depth == 0)\n\t\t\t{\n\t\t\t\tCon_Printf(\"%s: no layers\\n\", namelist[i]);\n\t\t\t\tImgTool_FreeMips(srcs[j].in);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tlayers += t->mip[0].depth;\n\t\t\tsrcs[j++].fname = namelist[i];\n\t\t}\n\t}\n\tfilecount = j;\n\n\t//FIXME: reorder input images to handle ft/bk/lt/rt/up/dn, and flip+rotate+etc to match quake\n\n\tif (args->textype == PTI_CUBE)\n\t{\n\t\tint facetype[6];\n\t\tstatic const struct {qboolean flipx, flipy, flipd;} skyboxflips[] ={\n\t\t\t{true,  false, true},\n\t\t\t{false, true,  true},\n\t\t\t{true,  true,  false},\n\t\t\t{false, false, false},\n\t\t\t{true,  false, true},\n\t\t\t{true,  false, true}\n\t\t};\n\t\tfor (i = 0; i < 6; i++)\n\t\t\tfacetype[i] = 0;\n\t\tfor (i = 0; i < filecount; i++)\n\t\t{\n\t\t\tconst char *ex = COM_GetFileExtension(srcs[i].fname, NULL);\n\t\t\tif (ex && ex-srcs[i].fname > 2 && srcs[i].in->mip[0].depth == 1)\n\t\t\t{\n\t\t\t\tif (!strncasecmp(ex-2, \"rt\", 2))\n\t\t\t\t\tfacetype[0] = -i-1;\n\t\t\t\telse if (!strncasecmp(ex-2, \"lf\", 2))\n\t\t\t\t\tfacetype[1] = -i-1;\n\t\t\t\telse if (!strncasecmp(ex-2, \"ft\", 2))\n\t\t\t\t\tfacetype[2] = -i-1;\n\t\t\t\telse if (!strncasecmp(ex-2, \"bk\", 2))\n\t\t\t\t\tfacetype[3] = -i-1;\n\t\t\t\telse if (!strncasecmp(ex-2, \"up\", 2))\n\t\t\t\t\tfacetype[4] = -i-1;\n\t\t\t\telse if (!strncasecmp(ex-2, \"dn\", 2))\n\t\t\t\t\tfacetype[5] = -i-1;\n\n\t\t\t\telse if (!strncasecmp(ex-2, \"px\", 2))\n\t\t\t\t\tfacetype[0] = i+1;\n\t\t\t\telse if (!strncasecmp(ex-2, \"nx\", 2))\n\t\t\t\t\tfacetype[1] = i+1;\n\t\t\t\telse if (!strncasecmp(ex-2, \"py\", 2))\n\t\t\t\t\tfacetype[2] = i+1;\n\t\t\t\telse if (!strncasecmp(ex-2, \"ny\", 2))\n\t\t\t\t\tfacetype[3] = i+1;\n\t\t\t\telse if (!strncasecmp(ex-2, \"pz\", 2))\n\t\t\t\t\tfacetype[4] = i+1;\n\t\t\t\telse if (!strncasecmp(ex-2, \"nz\", 2))\n\t\t\t\t\tfacetype[5] = i+1;\n\t\t\t}\n\t\t}\n\t\tif (facetype[0] && facetype[1] && facetype[2] && facetype[3] && facetype[4] && facetype[5])\n\t\t{\n\t\t\tCon_Printf(\"Reordering images to match cubemap\\n\");\n\t\t\ttmpsrcs = alloca(sizeof(*tmpsrcs)*filecount);\n\t\t\tmemcpy(tmpsrcs, srcs, sizeof(*tmpsrcs)*filecount);\n\t\t\tfor (i = 0; i < 6; i++)\n\t\t\t{\n\t\t\t\tif (facetype[i] < 0)\n\t\t\t\t{\t//flip to match legacy skyboxes\n\t\t\t\t\tunsigned bb,bw,bh,bd;\n\t\t\t\t\tsrcs[i] = tmpsrcs[-facetype[i]-1];\n\t\t\t\t\tt = srcs[i].in;\n\t\t\t\t\tImage_BlockSizeForEncoding(t->encoding, &bb,&bw,&bh,&bd);\n\t\t\t\t\tif (bw == 1 && bh == 1 && bd == 1)\n\t\t\t\t\t{\n\t\t\t\t\t\tfor (j = 0; j < t->mipcount; j++)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tvoid *data = Image_FlipImage(t->mip[j].data, BZ_Malloc(t->mip[j].datasize), &t->mip[j].width, &t->mip[j].height, bb, skyboxflips[i].flipx, skyboxflips[i].flipy, skyboxflips[i].flipd);\n\t\t\t\t\t\t\tif (t->mip[j].needfree)\n\t\t\t\t\t\t\t\tZ_Free(t->mip[j].data);\n\t\t\t\t\t\t\tt->mip[j].data = data;\n\t\t\t\t\t\t\tt->mip[j].needfree = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tsrcs[i] = tmpsrcs[facetype[i]-1];\n\t\t\t}\n\t\t}\n\t\telse\n\t\t\tCon_Printf(\"WARNING: Cubemap ordering unknown!\\n\");\n\t}\n\n\tif (!filecount)\n\t\tCon_Printf(\"no valid input files\\n\");\n\telse if (!layers)\n\t\tCon_Printf(\"Images must have at least one layer\\n\");\n\telse if (args->textype == PTI_2D && layers != 1)\n\t\tCon_Printf(\"2D images must have one layer exactly, sorry\\n\");\n\telse if (args->textype == PTI_CUBE && layers != 6)\n\t\tCon_Printf(\"Cubemaps must have 6 layers exactly\\n\");\n\telse if (args->textype == PTI_CUBE_ARRAY && layers % 6)\n\t\tCon_Printf(\"Cubemap arrays must have a multiple of 6 layers exactly\\n\");\n\telse\n\t{\n\t\tt = srcs[0].in;\n\t\tr = Z_Malloc(sizeof(*t));\n\t\tr->type = args->textype;\n\t\tr->extrafree = NULL;\n\t\tr->encoding = t->encoding;\n\n\t\tif (args->textype == PTI_3D)\n\t\t\tr->mipcount = 1;\n\t\telse\n\t\t\tr->mipcount = t->mipcount;\n\t\tfor (j = 0; j < t->mipcount; j++)\n\t\t{\n\t\t\tr->mip[j].datasize = t->mip[j].datasize*layers;\n\t\t\tr->mip[j].width = t->mip[j].width;\n\t\t\tr->mip[j].height = t->mip[j].height;\n\t\t\tr->mip[j].depth = 0;\n\t\t\tr->mip[j].needfree = true;\n\t\t\tr->mip[j].data = BZ_Malloc(r->mip[j].datasize);\n\t\t}\n\n\t\tfor (i = 0, j = 0; i < filecount; i++)\n\t\t{\n\t\t\tt = srcs[i].in;\n\t\t\tif (!t)\n\t\t\t{\n\t\t\t\tImgTool_FreeMips(r);\n\t\t\t\treturn NULL;\n\t\t\t}\n\n\t\t\tfor (j = 0; j < r->mipcount; j++)\n\t\t\t{\n\t\t\t\tif (r->mip[j].width != t->mip[j].width || r->mip[j].height != t->mip[j].height || t->mip[j].depth != 1)\n\t\t\t\t{\n\t\t\t\t\tCon_Printf(\"%s: mismatched mipmap sizes\\n\", namelist[i]);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tmemcpy((qbyte*)r->mip[j].data + t->mip[j].datasize*r->mip[j].depth, t->mip[j].data, t->mip[j].datasize);\n\t\t\t\tr->mip[j].depth++;\n\t\t\t}\n\t\t}\n\n\t\tfor (i = 0; i < filecount; i++)\n\t\t\tImgTool_FreeMips(srcs[i].in);\n\n\t\tprintf(\"%s: %s %s, %i*%i, %i mips\\n\", \"combined\", imagetypename[r->type], Image_FormatName(r->encoding), r->mip[0].width, r->mip[0].height, r->mipcount);\n\t\treturn r;\n\t}\n\n\tfor (i = 0; i < filecount; i++)\n\t\tImgTool_FreeMips(srcs[i].in);\n\treturn NULL;\n}\nstatic void ImgTool_Convert(struct opts_s *args, struct pendingtextureinfo *in, const char *inname, const char *outname)\n{\n\tsize_t k;\n\tconst char *outext;\n\tqboolean allowcompressed = false;\n\tchar newout[MAX_OSPATH];\n\n\tif (!outname)\n\t{\n\t\toutext = COM_GetFileExtension(inname, NULL);\n\t\tk = min(MAX_OSPATH-2-strlen(args->defaultext), outext-inname);\n\t\tmemcpy(newout, inname, k);\n\t\tnewout[k++] = '.';\n\t\tstrcpy(newout+k, args->defaultext);\n\t\toutname = newout;\n\t}\n\n\n\n\toutext = COM_GetFileExtension(outname, NULL);\n\tif (!strcmp(outext, \".dds\") || !strcmp(outext, \".ktx\"))\n\t\tallowcompressed = true;\n\n\tif (in)\n\t{\n\t\tif (!strcmp(outext, \".ktx\") || !strcmp(outext, \".dds\") || args->mipnum >= in->mipcount)\n\t\t{\n\t\t\tif (!(args->flags & IF_NOMIPMAP) && in->mipcount == 1)\n\t\t\t\tImage_GenerateMips(in, args->flags);\n\t\t}\n\n\t\tif (args->mipnum >= in->mipcount)\n\t\t{\n\t\t\tImgTool_FreeMips(in);\n\t\t\tCon_Printf(\"%s: Requested output mip number was out of bounds %i >= %i\\n\", outname, args->mipnum, in->mipcount);\n\t\t\treturn;\n\t\t}\n\t\tfor (k = 0; k < args->mipnum; k++)\n\t\t{\n\t\t\tif (in->mip[k].needfree)\n\t\t\t\tBZ_Free(in->mip[k].data);\n\t\t}\n\t\tin->mipcount -= k;\n\t\tmemmove(in->mip, &in->mip[k], sizeof(in->mip[0])*in->mipcount);\n\n\t\tCon_Printf(\"%s(%s)->\", inname, Image_FormatName(in->encoding));\n\n\t\tif (args->newpixelformat != PTI_INVALID && (args->newpixelformat < PTI_BC1_RGB || allowcompressed) && ImgTool_ConvertPixelFormat(args, inname, in))\n\t\t\tCon_Printf(\"(%s)->\", Image_FormatName(in->encoding));\n\n\t\tif (!in->mipcount)\n\t\t\tCon_Printf(\"%s: no image data\\n\", inname);\n\t\telse if (!strcasecmp(outext, \".mip\"))\n\t\t{\n\t\t\tvfsfile_t *fs = FS_OpenVFS(outname, \"wb\", FS_SYSTEM);\n\t\t\tif (!fs)\n\t\t\t\tCon_Printf(\"%s(%s): Write failed\\n\", outname, Image_FormatName(in->encoding));\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (!ImgTool_MipExport(args, fs, in, outname, 2))\n\t\t\t\t\tCon_Printf(\"%s: export failed\\n\", outname);\n\t\t\t\tVFS_CLOSE(fs);\n\t\t\t}\n\t\t}\n#ifdef IMAGEFMT_KTX\n\t\telse if (!strcasecmp(outext, \".ktx\"))\n\t\t{\n\t\t\tif (!Image_WriteKTXFile(outname, FS_SYSTEM, in))\n\t\t\t\tCon_Printf(\"%s(%s): Write failed\\n\", outname, Image_FormatName(in->encoding));\n\t\t}\n#endif\n#ifdef IMAGEFMT_DDS\n\t\telse if (!strcasecmp(outext, \".dds\"))\n\t\t{\n\t\t\tif (!Image_WriteDDSFile(outname, FS_SYSTEM, in))\n\t\t\t\tCon_Printf(\"%s(%s): Write failed\\n\", outname, Image_FormatName(in->encoding));\n\t\t}\n#endif\n\t\telse\n\t\t{\n\t\t\tint bb,bw,bh,bd;\n\n\t\t\tif (in->type != PTI_2D)\n\t\t\t\tCon_Printf(\"%s: Unable to write %s file to 2d image format\\n\", outname, imagetypename[in->type]);\n#ifdef IMAGEFMT_PNG\n\t\t\telse if (!strcasecmp(outext, \".png\"))\n\t\t\t{\n#ifdef AVAIL_PNGLIB\n\t\t\t\tqboolean outformats[PTI_MAX];\n\t\t\t\t//force the format, because we can.\n\t\t\t\tfor (k = 0; k < PTI_MAX; k++)\n\t\t\t\t\toutformats[k] =\n\t\t\t\t\t\t\t(k == PTI_RGBA8) || (k == PTI_RGBX8) ||\n\t\t\t\t\t\t\t(k == PTI_BGRA8) || (k == PTI_BGRX8) ||\n\t\t\t\t\t\t\t(k == PTI_LLLA8) || (k == PTI_LLLX8) ||\n\t\t\t\t\t\t\t(k == PTI_RGBA16) || (k == PTI_P8) ||\n\t\t\t\t\t\t\t(k == PTI_L8) || (k == PTI_L8A8) ||\n\t\t\t\t\t\t\t/*(k == PTI_L16) ||*/\n\t\t\t\t\t\t\t(k == PTI_BGR8) || (k == PTI_BGR8) ||\n\t\t\t\t\t\t\t0;\n\t\t\t\tif (!outformats[in->encoding])\n\t\t\t\t\tImage_ChangeFormat(in, outformats, PTI_INVALID, outname);\n\t\t\t\tImage_BlockSizeForEncoding(in->encoding, &bb, &bw,&bh,&bd);\n\t\t\t\tif (!Image_WritePNG(outname, FS_SYSTEM, 0, &in->mip[0].data, 1, in->mip[0].width*bb, in->mip[0].width, in->mip[0].height, in->encoding, false))\n#endif\n\t\t\t\t\tCon_Printf(\"%s(%s): Write failed\\n\", outname, Image_FormatName(in->encoding));\n\t\t\t}\n#endif\n#ifdef IMAGEFMT_TGA\n\t\t\telse if (!strcasecmp(outext, \".tga\"))\n\t\t\t{\n\t\t\t\tqboolean outformats[PTI_MAX];\n\t\t\t\tfor (k = 0; k < PTI_MAX; k++)\n\t\t\t\t\toutformats[k] =\n\t\t\t\t\t\t\t(k == PTI_RGBA8) || (k == PTI_RGBX8) ||\n\t\t\t\t\t\t\t(k == PTI_BGRA8) || (k == PTI_BGRX8) ||\n\t\t\t\t\t\t\t(k == PTI_LLLA8) || (k == PTI_LLLX8) ||\n\t\t\t\t\t\t\t(k == PTI_RGBA16F) || (k == PTI_R16F) ||\t//half-float tgas is a format extension, but allow it.\n\t\t\t\t\t\t\t(k == PTI_L8) || (k == PTI_L8A8) ||\n\t\t\t\t\t\t\t/*(k == PTI_L16) ||*/\n\t\t\t\t\t\t\t(k == PTI_BGR8) || (k == PTI_BGR8) ||\n\t\t\t\t\t\t\t(k == PTI_ARGB1555) ||\n\t\t\t\t\t\t\t0;\n\t\t\t\tif (!outformats[in->encoding])\n\t\t\t\t\tImage_ChangeFormat(in, outformats, PTI_INVALID, outname);\n\t\t\t\tImage_BlockSizeForEncoding(in->encoding, &bb, &bw,&bh,&bd);\n\t\t\t\tif (!WriteTGA(outname, FS_SYSTEM, in->mip[0].data, in->mip[0].width*bb, in->mip[0].width, in->mip[0].height, in->encoding))\n\t\t\t\t\tCon_Printf(\"%s(%s): Write failed\\n\", outname, Image_FormatName(in->encoding));\n\t\t\t}\n#endif\n#ifdef IMAGEFMT_PCX\n\t\t\telse if (!strcasecmp(outext, \".pcx\"))\n\t\t\t{\n\t\t\t\tqboolean outformats[PTI_MAX];\n\t\t\t\tfor (k = 0; k < PTI_MAX; k++)\n\t\t\t\t\toutformats[k] =\n\t\t\t\t\t\t\t(k == PTI_P8) ||\n\t\t\t\t\t\t\t(k == TF_SOLID8) ||\n\t\t\t\t\t\t\t(k == TF_TRANS8) ||\n\t\t\t\t\t\t\t(k == TF_H2_TRANS8_0) ||\n\t\t\t\t\t\t\t0;\n\t\t\t\tif (!outformats[in->encoding])\n\t\t\t\t\tImage_ChangeFormat(in, outformats, PTI_INVALID, outname);\n\t\t\t\tImage_BlockSizeForEncoding(in->encoding, &bb, &bw,&bh,&bd);\n\t\t\t\tif (!WritePCXfile(outname, FS_SYSTEM, in->mip[0].data, in->mip[0].width, in->mip[0].height, in->mip[0].width*bb, host_basepal, false))\n\t\t\t\t\tCon_Printf(\"%s(%s): Write failed\\n\", outname, Image_FormatName(in->encoding));\n\t\t\t}\n#endif\n\t\t\telse if (!strcasecmp(outext, \".lmp\"))\n\t\t\t{\n\t\t\t\tint i;\n\t\t\t\tvfsfile_t *f = NULL;\n\t\t\t\tqboolean outformats[PTI_MAX];\n\t\t\t\tfor (k = 0; k < PTI_MAX; k++)\n\t\t\t\t\toutformats[k] =\n\t\t\t\t\t\t\t(k == PTI_P8) ||\n\t\t\t\t\t\t\t(k == TF_SOLID8) ||\n\t\t\t\t\t\t\t(k == TF_TRANS8) ||\n\t\t\t\t\t\t\t(k == TF_H2_TRANS8_0) ||\n\t\t\t\t\t\t\t0;\n\t\t\t\tif (!outformats[in->encoding])\n\t\t\t\t\tImage_ChangeFormat(in, outformats, PTI_INVALID, outname);\n\t\t\t\tImage_BlockSizeForEncoding(in->encoding, &bb, &bw,&bh,&bd);\n\n\t\t\t\tif (!outformats[in->encoding])\n\t\t\t\t\tCon_Printf(\"%s(%s): couldn't palettize\\n\", outname, Image_FormatName(in->encoding));\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tf = FS_OpenVFS(outname, \"wb\", FS_SYSTEM);\n\t\t\t\t\tif (f)\n\t\t\t\t\t{\n\t\t\t\t\t\ti = LittleLong(in->mip[0].width);\n\t\t\t\t\t\tVFS_WRITE(f, &i, sizeof(i));\n\t\t\t\t\t\ti = LittleLong(in->mip[0].height);\n\t\t\t\t\t\tVFS_WRITE(f, &i, sizeof(i));\n\t\t\t\t\t\tVFS_WRITE(f, in->mip[0].data, in->mip[0].width*bb*in->mip[0].height);\n\t\t\t\t\t\tVFS_CLOSE(f);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\tCon_Printf(\"%s(%s): Couldn't open for writing\\n\", outname, Image_FormatName(in->encoding));\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t\tCon_Printf(\"%s: Unknown output file format\\n\", outname);\n\t\t}\n\n\t\tif (in->mipcount > 1)\n\t\t{\n\t\t\tCon_Printf(\"%s(%s): %s %i*%i*%i, %i mips\\n\", outname, Image_FormatName(in->encoding), imagetypename[in->type], in->mip[0].width, in->mip[0].height, in->mip[0].depth, in->mipcount);\n\t\t\tfor (k = 0; k < in->mipcount; k++)\n\t\t\t\tif (in->mip[k].depth == 1 && in->type == PTI_2D)\n\t\t\t\t\tCon_DPrintf(\"\\t%u: %i*%i, %u\\n\", (unsigned)k, in->mip[k].width, in->mip[k].height, (unsigned)in->mip[k].datasize);\n\t\t\t\telse\n\t\t\t\t\tCon_DPrintf(\"\\t%u: %i*%i*%i, %u\\n\", (unsigned)k, in->mip[k].width, in->mip[k].height, in->mip[k].depth, (unsigned)in->mip[k].datasize);\n\t\t}\n\t\telse\n\t\t\tCon_Printf(\"%s(%s): %s %i*%i*%i, %u bytes\\n\", outname, Image_FormatName(in->encoding), imagetypename[in->type], in->mip[0].width, in->mip[0].height, in->mip[0].depth, (unsigned)in->mip[0].datasize);\n\n\t\tImgTool_FreeMips(in);\n\t}\n\tfflush(stdout);\n}\n\nstatic struct pendingtextureinfo *ImgTool_DecodeMiptex(struct opts_s *args, miptex_t *mip, size_t size, qbyte *pal)\n{\n\tqbyte *data = (qbyte*)mip + (mip->offsets[3]?mip->offsets[3] + (mip->width>>3)*(mip->height>>3):sizeof(miptex_t));\n\tqbyte *dataend = (qbyte*)mip + size;\n\tstruct pendingtextureinfo *out = Z_Malloc(sizeof(*out));\n\tqbyte *newdata = NULL;\n\tint neww=0, newh=0, sz;\n\tunsigned int bw,bh,bb,bd, i;\n\tout->type = PTI_2D;\n\n\tout->encoding = PTI_INVALID;\n\n\tCon_DPrintf(\"%s: width %u, height %u, size %u\\n\", mip->name, mip->width, mip->height, (unsigned int)size);\n\n\t//header [legacymip0 legacymip1 legacymip2] [extsize extcode extdata]*n [legacymip3]\n\t//extcode NAME: extdata-8 bytes of replacement name\n\t//extdata pixelformats, extdata is: Width Height newmip0...N where N is 1*1 mip, using round-down logic.\n\t//compressed and legacy data ommitted means all 4 offsets are 0.\n\t//compressed-only data has offset[3] state the termination position, but offset[0,1,2] MUST be 0 still (extension data starts right after the header, offset3 points to the end of the miptex and has no data there.\n\t//legacy-only data is densely packed or whatever.\n\t//half-life palette data might be glued onto the end. we don't care to handle that.\n\tCon_DPrintf(\"%i bytes of extended data\\n\", (unsigned)(dataend-data));\n\tCon_DPrintf(\"offset[0]: %u\\n\", mip->offsets[0]);\n\tCon_DPrintf(\"offset[1]: %u\\n\", mip->offsets[1]);\n\tCon_DPrintf(\"offset[2]: %u\\n\", mip->offsets[2]);\n\tCon_DPrintf(\"offset[3]: %u\\n\", mip->offsets[3]);\n\tif (data+4 < dataend && data[0]==0x00 && data[1]==0xfb&&data[2]==0x2b&&data[3]==0xaf)\t//magic id to say that there's actually extensions here...\n\t{\n\t\tdata+=4;\n\t\tfor (; data+4 < dataend; data += sz)\n\t\t{\t//we could recognise more,\n\t\t\tuploadfmt_t fmt = PTI_INVALID;\n\t\t\tsize_t csz, w, h;\n\t\t\tsz = (data[0]<<0) | (data[1]<<8) | (data[2]<<16) | (data[3]<<24);\n\n\t\t\tif (sz < 4 || sz > dataend-data)\t\t{\tCon_Printf(\"%s: Invalid miptex extension\\n\", mip->name);\tbreak;}\n\t\t\telse if (sz > 8 && !strncmp(data+4, \"NAME\", 4))\t\tcontinue;\t//FIXME\n\t\t\telse if (sz > 16 && !strncmp(data+4, \"RGBA\", 4))\tfmt = PTI_RGBA8;\n\t\t\telse if (sz > 16 && !strncmp(data+4, \"RGB\", 4))\t\tfmt = PTI_RGB8;\n\t\t\telse if (sz > 16 && !strncmp(data+4, \"565\", 4))\t\tfmt = PTI_RGB565;\n\t\t\telse if (sz > 16 && !strncmp(data+4, \"5551\", 4))\tfmt = PTI_RGBA5551;\n\t\t\telse if (sz > 16 && !strncmp(data+4, \"4444\", 4))\tfmt = PTI_RGBA4444;\n\t\t\telse if (sz > 16 && !strncmp(data+4, \"LUM8\", 4))\tfmt = PTI_L8;\t//greyscale. because why not.\n\t\t\telse if (sz > 16 && !strncmp(data+4, \"BC1\", 4))\t\tfmt = PTI_BC1_RGBA;\n\t\t\telse if (sz > 16 && !strncmp(data+4, \"BC2\", 4))\t\tfmt = PTI_BC2_RGBA;\n\t\t\telse if (sz > 16 && !strncmp(data+4, \"BC3\", 4))\t\tfmt = PTI_BC3_RGBA;\n\t\t\telse if (sz > 16 && !strncmp(data+4, \"BC4\", 4))\t\tfmt = PTI_BC4_R;\n\t\t\telse if (sz > 16 && !strncmp(data+4, \"BC5\", 4))\t\tfmt = PTI_BC5_RG;\n\t\t\telse if (sz > 16 && !strncmp(data+4, \"BC6\", 4))\t\tfmt = PTI_BC6_RGB_UFLOAT;\n\t\t\telse if (sz > 16 && !strncmp(data+4, \"BC7\", 4))\t\tfmt = PTI_BC7_RGBA;\n\t\t\telse if (sz > 16 && !strncmp(data+4, \"ETC1\", 4))\tfmt = PTI_ETC1_RGB8;\n\t\t\telse if (sz > 16 && !strncmp(data+4, \"ETC2\", 4))\tfmt = PTI_ETC2_RGB8;\n\t\t\telse if (sz > 16 && !strncmp(data+4, \"ETCP\", 4))\tfmt = PTI_ETC2_RGB8A1;\n\t\t\telse if (sz > 16 && !strncmp(data+4, \"ETCA\", 4))\tfmt = PTI_ETC2_RGB8A8;\n\t\t\telse if (sz > 16 && !strncmp(data+4, \"AST4\", 4))\tfmt = PTI_ASTC_4X4_LDR;\n\t\t\telse if (sz > 16 && !strncmp(data+4, \"AS54\", 4))\tfmt = PTI_ASTC_5X4_LDR;\n\t\t\telse if (sz > 16 && !strncmp(data+4, \"AST5\", 4))\tfmt = PTI_ASTC_5X5_LDR;\n\t\t\telse if (sz > 16 && !strncmp(data+4, \"AS65\", 4))\tfmt = PTI_ASTC_6X5_LDR;\n\t\t\telse if (sz > 16 && !strncmp(data+4, \"AS85\", 4))\tfmt = PTI_ASTC_8X5_LDR;\n\t\t\telse if (sz > 16 && !strncmp(data+4, \"AS05\", 4))\tfmt = PTI_ASTC_10X5_LDR;\n\t\t\telse if (sz > 16 && !strncmp(data+4, \"AST6\", 4))\tfmt = PTI_ASTC_6X6_LDR;\n\t\t\telse if (sz > 16 && !strncmp(data+4, \"AS86\", 4))\tfmt = PTI_ASTC_8X6_LDR;\n\t\t\telse if (sz > 16 && !strncmp(data+4, \"AS06\", 4))\tfmt = PTI_ASTC_10X6_LDR;\n\t\t\telse if (sz > 16 && !strncmp(data+4, \"AST8\", 4))\tfmt = PTI_ASTC_8X8_LDR;\t\t\t\n\t\t\telse if (sz > 16 && !strncmp(data+4, \"AS08\", 4))\tfmt = PTI_ASTC_10X8_LDR;\n\t\t\telse if (sz > 16 && !strncmp(data+4, \"AST0\", 4))\tfmt = PTI_ASTC_10X10_LDR;\n\t\t\telse if (sz > 16 && !strncmp(data+4, \"AS20\", 4))\tfmt = PTI_ASTC_12X10_LDR;\n\t\t\telse if (sz > 16 && !strncmp(data+4, \"AST2\", 4))\tfmt = PTI_ASTC_12X12_LDR;\n\t\t\telse if (sz > 16 && !strncmp(data+4, \"EXP5\", 4))\tfmt = PTI_E5BGR9;\n\t\t\telse {Con_Printf(\"%s: Unknown miptex extension %4s\\n\", mip->name, data+4);continue;}\n\n\t\t\tif (out->encoding != PTI_INVALID)\t//use the first format we support, allowing prioritisation.\n\t\t\t\tcontinue;\n\n\t\t\tImage_BlockSizeForEncoding(fmt, &bb, &bw, &bh, &bd);\n\t\t\tw = data[ 8] | (data[ 9]<<8) | (data[10]<<16) | (data[11]<<24);\n\t\t\th = data[12] | (data[13]<<8) | (data[14]<<16) | (data[15]<<24);\n\t\t\tfor (csz = 16; w || h; w>>=1, h>>=1)\n\t\t\t{\n\t\t\t\tw = max(1, w);\n\t\t\t\th = max(1, h);\n\t\t\t\tcsz += bb*((w+bw-1)/bw)*((h+bh-1)/bh);\n\t\t\t}\n\t\t\tif (sz == csz)\n\t\t\t{\t//mip chain is complete etc\n\t\t\t\tout->encoding = fmt;\n\t\t\t\tnewdata = data+16;\n\t\t\t\tneww = data[ 8] | (data[ 9]<<8) | (data[10]<<16) | (data[11]<<24);\n\t\t\t\tnewh = data[12] | (data[13]<<8) | (data[14]<<16) | (data[15]<<24);\n\t\t\t}\n\t\t\telse\n\t\t\t\tCon_Printf(\"%s: Chain size of %u doesn't match expected %u\\n\", mip->name, (unsigned)csz-16, sz-16);\n\t\t}\n\t}\n\n\t//only use our if there were no corrupt sections.\n\tif (data == dataend && newdata && neww && newh)\n\t{\n\t\tImage_BlockSizeForEncoding(out->encoding, &bb, &bw, &bh, &bd);\n\t\tfor (out->mipcount = 0; out->mipcount < countof(out->mip) && neww && newh; out->mipcount++, neww>>=1, newh>>=1)\n\t\t{\n\t\t\tneww = max(1, neww);\n\t\t\tnewh = max(1, newh);\n\t\t\tout->mip[out->mipcount].width = neww;\n\t\t\tout->mip[out->mipcount].height = newh;\n\t\t\tout->mip[out->mipcount].depth = 1;\n\t\t\tout->mip[out->mipcount].datasize = bb;\n\t\t\tout->mip[out->mipcount].datasize *= (out->mip[out->mipcount].width + bw-1)/bw;\n\t\t\tout->mip[out->mipcount].datasize *= (out->mip[out->mipcount].height + bh-1)/bh;\n\t\t\tout->mip[out->mipcount].datasize *= (out->mip[out->mipcount].depth + bd-1)/bd;\n\t\t\tout->mip[out->mipcount].data = newdata;\n\t\t\tnewdata += out->mip[out->mipcount].datasize;\n\t\t}\n\t}\n\telse\n\t{\n\t\tif ((((dataend-data)+3)&~3) == (((256*3+2)+3)&~3) && data[0]==0&&data[1]==1)\n\t\t\tpal = data+2;\t//halflife format...\n\n\t\tif (*mip->name == '{')\n\t\t\tout->encoding = TF_TRANS8;\n\t\telse if (!strncasecmp(mip->name, \"sky\", 3))\n\t\t\tout->encoding = TF_H2_TRANS8_0;\n\t\telse\n\t\t\tout->encoding = PTI_P8;\n\n\t\tif (pal)\n\t\t{\t//bake the palette directly. rgb(a) data out.\n\t\t\tqbyte *idx, *rgb, *pi, *tr;\n\t\t\tsize_t s;\n\t\t\tif (out->encoding == TF_TRANS8)\n\t\t\t\tout->encoding = PTI_RGBA8, tr = pal+255;\n\t\t\telse if (out->encoding == TF_H2_TRANS8_0)\n\t\t\t\tout->encoding = PTI_RGBA8, tr = pal+0;\n\t\t\telse //if (out->encoding == PTI_P8)\n\t\t\t\tout->encoding = PTI_RGBX8, tr = NULL;\n\t\t\tfor (out->mipcount = 0; out->mipcount < 4 && mip->offsets[out->mipcount]; out->mipcount++)\n\t\t\t{\n\t\t\t\tout->mip[out->mipcount].width = mip->width>>out->mipcount;\n\t\t\t\tout->mip[out->mipcount].height = mip->height>>out->mipcount;\n\t\t\t\tout->mip[out->mipcount].depth = 1;\n\t\t\t\ts = out->mip[out->mipcount].width*(size_t)out->mip[out->mipcount].height*out->mip[out->mipcount].depth;\n\t\t\t\tout->mip[out->mipcount].datasize = s*4;\n\t\t\t\trgb = out->mip[out->mipcount].data = BZ_Malloc(out->mip[out->mipcount].datasize);\n\t\t\t\tidx = (char*)mip + mip->offsets[out->mipcount];\n\t\t\t\tif (mip->offsets[out->mipcount]+s > size)\n\t\t\t\t{\n\t\t\t\t\tout->mip[out->mipcount].data = mip;\n\t\t\t\t\tout->mip[out->mipcount].datasize = 0;\n\t\t\t\t\tCon_Printf(\"%s: Mip%i offsets/size exceed size of texture\\n\", mip->name, out->mipcount);\n\t\t\t\t}\n\t\t\t\telse while (s-->0)\n\t\t\t\t{\n\t\t\t\t\tpi = pal+3**idx++;\n\t\t\t\t\t*rgb++ = pi[0];\n\t\t\t\t\t*rgb++ = pi[1];\n\t\t\t\t\t*rgb++ = pi[2];\n\t\t\t\t\t*rgb++ = (pi==tr)?0:255;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tfor (out->mipcount = 0; out->mipcount < 4 && mip->offsets[out->mipcount]; out->mipcount++)\n\t\t\t{\n\t\t\t\tout->mip[out->mipcount].width = mip->width>>out->mipcount;\n\t\t\t\tout->mip[out->mipcount].height = mip->height>>out->mipcount;\n\t\t\t\tout->mip[out->mipcount].depth = 1;\n\t\t\t\tout->mip[out->mipcount].datasize = out->mip[out->mipcount].width*(size_t)out->mip[out->mipcount].height*out->mip[out->mipcount].depth;\n\t\t\t\tout->mip[out->mipcount].data = (char*)mip + mip->offsets[out->mipcount];\n\n\t\t\t\tif (mip->offsets[out->mipcount]+out->mip[out->mipcount].datasize > size)\n\t\t\t\t{\n\t\t\t\t\tout->mip[out->mipcount].data = mip;\n\t\t\t\t\tout->mip[out->mipcount].datasize = 0;\n\t\t\t\t\tCon_Printf(\"%s: Mip%i offsets/size exceed size of texture\\n\", mip->name, out->mipcount);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tif (*mip->name == '*')\n\t\t*mip->name = '#';\t//convert from * to #, so its a valid file name.\n\n\tif (args)\n\t{\n\t\tif (args->defaultext && !strcasecmp(args->defaultext, \"mip\"))\n\t\t{\n\t\t\tchar newout[MAX_OSPATH];\n\t\t\tsize_t k = strlen(mip->name);\n\t\t\tvfsfile_t *fs;\n\t\t\tmemcpy(newout, mip->name, k);\n\t\t\tnewout[k++] = '.';\n\t\t\tstrcpy(newout+k, args->defaultext);\n\n\t\t\tfs = FS_OpenVFS(newout, \"wb\", FS_SYSTEM);\n\t\t\tif (!fs)\n\t\t\t\tCon_Printf(\"%s(%s): Write failed\\n\", newout, Image_FormatName(out->encoding));\n\t\t\telse\n\t\t\t{\n\t\t\t\tVFS_WRITE(fs, mip, size);\n\t\t\t\tVFS_CLOSE(fs);\n\t\t\t}\n\n\t\t\tfflush(stdout);\n\t\t}\n\t\telse\n\t\t\tImgTool_Convert(args, out, mip->name, NULL);\n\t\treturn NULL;\n\t}\n\treturn out;\n}\nstatic void ImgTool_PrintInfo(const char *inname, struct pendingtextureinfo *in)\n{\n\tsize_t m;\n\tif (in->mipcount == 1 && in->type == PTI_2D && in->mip[0].depth == 1)\n\t\tprintf(\"%-20s(%s): %4i*%-4i\\n\", inname, Image_FormatName(in->encoding), in->mip[0].width, in->mip[0].height);\n\telse if (in->mipcount == 1)\n\t\tprintf(\"%-20s(%s): %s, %i*%i*%i, %u bytes\\n\", inname, Image_FormatName(in->encoding), imagetypename[in->type], in->mip[0].width, in->mip[0].height, in->mip[0].depth, (unsigned)in->mip[0].datasize);\n\telse\n\t{\n\t\t/*if (mip)\n\t\t\tprintf(\"%-20s(%s): \\\"%s\\\"%s %i*%i, %i mips\\n\", inname, Image_FormatName(in->encoding), mip->name, mip->offsets[0]?\"\":\" (stripped)\", mip->width, mip->height, in->mipcount);\n\t\telse*/\n\t\t\tprintf(\"%-20s(%s): %s, %i*%i*%i, %i mips\\n\", inname, Image_FormatName(in->encoding), imagetypename[in->type], in->mip[0].width, in->mip[0].height, in->mip[0].depth, in->mipcount);\n\t\tif (verbose)\n\t\t\tfor (m = 0; m < in->mipcount; m++)\n\t\t\t\tprintf(\"\\t%u: %i*%i*%i, %u\\n\", (unsigned)m, in->mip[m].width, in->mip[m].height, in->mip[m].depth, (unsigned)in->mip[m].datasize);\n\t}\n}\nstatic void ImgTool_Enumerate(struct opts_s *args, const char *inname, void(*callback)(const char *name, struct pendingtextureinfo *mips))\n{\n\tqbyte *indata;\n\tsize_t fsize;\n\tsize_t m;\n\tstruct pendingtextureinfo *in;\n\tqbyte *basepal = NULL;\n\tindata = FS_LoadMallocFile(inname, &fsize);\n\tif (!indata)\n\t\tprintf(\"%s: unable to read\\n\", inname);\n\telse if (fsize >= sizeof(wad2_t) && indata[0] == 'W' && indata[1] == 'A' && indata[2] == 'D')\n\t{\n\t\tconst wad2_t *w = (const wad2_t *)indata;\n\t\tconst wad2entry_t *e = (const wad2entry_t *)(indata+w->offset);\n\t\tprintf(\"%s: wad%c file with %i entries\\n\", inname, w->magic[3], w->num);\n\t\tfor (m = 0; m < w->num; m++, e++)\n\t\t{\n\t\t\tswitch(e->type)\n\t\t\t{\n\t\t\tcase TYP_QPIC:\n\t\t\t\t{\n\t\t\t\t\tsize_t sz=min(e->size, e->dsize);\n\t\t\t\t\tunsigned int w=0;\n\t\t\t\t\tunsigned int h=0;\n\t\t\t\t\tin = NULL;\n\t\t\t\t\tif (!strcasecmp(e->name, \"CONCHARS\") && (e->size==128*128 || e->size==128*128+8))\n\t\t\t\t\t{\t//special hack for buggy conchars, which is listed as a miptex for some reason, with no qpic header (it not being a qpic lump)\n\t\t\t\t\t\tprintf(\"\\t%16.16s: corrupt CONCHARS lump - wrongly marked as qpic. Treating as a legacy engine would...\\n\", e->name);\n\t\t\t\t\t\tin = Z_Malloc(sizeof(*in));\n\t\t\t\t\t\tin->encoding = TF_H2_TRANS8_0;\n\t\t\t\t\t\tin->mip[0].data = indata+e->offset;\n\t\t\t\t\t\tin->mip[0].datasize = 128*128;\n\t\t\t\t\t\tw = 128;\n\t\t\t\t\t\th = 128;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tif (sz >= 8)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tw = (indata[e->offset+0]<<0)|(indata[e->offset+1]<<8)|(indata[e->offset+2]<<16)|(indata[e->offset+3]<<24);\n\t\t\t\t\t\t\th = (indata[e->offset+4]<<0)|(indata[e->offset+5]<<8)|(indata[e->offset+6]<<16)|(indata[e->offset+7]<<24);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (sz == w*h+8)\n\t\t\t\t\t\t{\t//quake\n\t\t\t\t\t\t\tin = Z_Malloc(sizeof(*in));\n\t\t\t\t\t\t\tin->encoding = TF_TRANS8;\n\t\t\t\t\t\t\tin->mip[0].data = indata+e->offset+8;\n\t\t\t\t\t\t\tin->mip[0].datasize = w*h;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (((sz+3)&~3) == (((w*h+10+768+3)&~3)))\n\t\t\t\t\t\t{\t//halflife\n\t\t\t\t\t\t\tconst unsigned char *src = indata+e->offset+8;\n\t\t\t\t\t\t\tconst unsigned char *pal = indata+e->offset+8+w*h+2, *p;\n\t\t\t\t\t\t\tunsigned char *dst;\n\t\t\t\t\t\t\tsz = w*h;\n\t\t\t\t\t\t\tin = Z_Malloc(sizeof(*in)+w*h*3);\n\t\t\t\t\t\t\tin->encoding = PTI_RGB8;\n\t\t\t\t\t\t\tin->mip[0].data = dst = (unsigned char*)(in+1);\n\t\t\t\t\t\t\tin->mip[0].datasize = sz*3;\n\n\t\t\t\t\t\t\twhile (sz --> 0)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tp = pal+*src++*3;\n\t\t\t\t\t\t\t\t*dst++ = *p++;\n\t\t\t\t\t\t\t\t*dst++ = *p++;\n\t\t\t\t\t\t\t\t*dst++ = *p++;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tprintf(\"\\t%16.16s: missized qpic (%u %u, %u bytes)\\n\", e->name, w, h, (unsigned int)sz);\n\t\t\t\t\t}\n\t\t\t\t\tif (in)\n\t\t\t\t\t{\n//\t\t\t\t\t\tprintf(\"\\n\");\n\t\t\t\t\t\tin->type = PTI_2D;\n\t\t\t\t\t\tin->mipcount = 1;\n\t\t\t\t\t\tin->mip[0].width = w;\n\t\t\t\t\t\tin->mip[0].height = h;\n\t\t\t\t\t\tin->mip[0].depth = 1;\n\t\t\t\t\t\tin->mip[0].needfree = false;\n\n\t\t\t\t\t\tcallback(e->name, in);\n\t\t\t\t\t\tImgTool_FreeMips(in);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase 67:\t//hl...\n\t\t\tcase TYP_MIPTEX:\n\t\t\t\t{\n\t\t\t\t\tmiptex_t *mip = (miptex_t *)(indata+e->offset);\n\n\t\t\t\t\tif (!strcasecmp(e->name, \"CONCHARS\") && e->size==128*128)\n\t\t\t\t\t{\t//special hack for conchars, which is listed as a miptex for some reason, with no qpic header (it not being a qpic lump)\n\t\t\t\t\t\tin = Z_Malloc(sizeof(*in));\n\t\t\t\t\t\tin->encoding = TF_H2_TRANS8_0;\n\t\t\t\t\t\tin->mip[0].data = indata+e->offset+8;\n\t\t\t\t\t\tin->mip[0].datasize = 128*128;\n\t\t\t\t\t\tin->type = PTI_2D;\n\t\t\t\t\t\tin->mipcount = 1;\n\t\t\t\t\t\tin->mip[0].width = 128;\n\t\t\t\t\t\tin->mip[0].height = 128;\n\t\t\t\t\t\tin->mip[0].depth = 1;\n\t\t\t\t\t\tin->mip[0].needfree = false;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\tin = ImgTool_DecodeMiptex(NULL, mip, min(e->size, e->dsize), basepal);\n\n\t\t\t\t\tif (in)\n\t\t\t\t\t\tcallback(e->name, in);\n/*\n\t\t\t\t\t//mip name SHOULD match entry name... but gah!\n\t\t\t\t\tif (strcasecmp(e->name, mip->name))\n\t\t\t\t\t\tprintf(\"\\t%16.16s (%s): \", e->name, mip->name);\n\t\t\t\t\telse\n\t\t\t\t\t\tprintf(\"\\t%16.16s: \", mip->name);\n\t\t\t\t\tprintf(\"%u*%u%s\", mip->width, mip->height, mip->offsets[0]?\"\":\" (omitted)\");\n\n\t\t\t\t\tif (in->encoding != PTI_P8)\n\t\t\t\t\t\tprintf(\" (%s %u*%u)\", Image_FormatName(in->encoding), in->mip[0].width, in->mip[0].height);*/\n//\t\t\t\t\tprintf(\"\\n\");\n\t\t\t\t\tImgTool_FreeMips(in);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase TYP_PALETTE:\n\t\t\t\tif (e->size == 768)\n\t\t\t\t{\n\t\t\t\t\tbasepal = indata+e->offset;\n\t\t\t\t\tif (!memcmp(basepal, host_basepal, 768))\n\t\t\t\t\t\tbasepal = NULL;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tprintf(\"\\t%16.16s: palette - %u bytes\\n\", e->name, e->size);\n\t\t\t\tbreak;\n\t\t\tcase 70:\n\t\t\t\tprintf(\"\\t%16.16s: Halflife Font (%u bytes)\\n\", e->name, e->size);\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tprintf(\"\\t%16.16s: ENTRY TYPE %u (%u bytes)\\n\", e->name, e->type, e->size);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\telse if (fsize >= sizeof(dheader_t) && (\n\t\t!memcmp(indata, BSPVERSION)\t\t||\n\t\t!memcmp(indata, BSPVERSIONHL)\t||\n\t\t!memcmp(indata, BSPVERSIONPREREL)||\n\t\t!memcmp(indata, BSPVERSION_LONG1)||\n\t\t!memcmp(indata, BSPVERSION_LONG2)))\n\t{\t//q1bsp\n\t\tdheader_t *bsp = (dheader_t*)indata;\n\t\tdmiptexlump_t *texlump = (dmiptexlump_t*)(indata + bsp->lumps[LUMP_TEXTURES].fileofs);\n\t\tmiptex_t *miptex;\n\t\tsize_t i;\n\t\tsize_t sz = bsp->lumps[LUMP_TEXTURES].filelen;\n\t\tprintf(\"%-20s: bsp file (%u textures)\\n\", inname, texlump->nummiptex);\n\t\tfor (i = texlump->nummiptex; i --> 0; )\n\t\t{\n\t\t\tif (texlump->dataofs[i] < 0 || texlump->dataofs[i] >= bsp->lumps[LUMP_TEXTURES].filelen)\n\t\t\t{\n\t\t\t\tchar syn[MAX_QPATH];\n\t\t\t\tsprintf(syn, \"unnamed%u\", (unsigned)i);\n\t\t\t\tprintf(\"%-20s---<NO DATA>--- %d\\n\", syn, texlump->dataofs[i]);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tmiptex = (miptex_t*)((qbyte*)texlump + texlump->dataofs[i]);\n\n\t\t\tin = ImgTool_DecodeMiptex(NULL, miptex, sz - texlump->dataofs[i], NULL);\n\t\t\tsz = texlump->dataofs[i];\n\t\t\tcallback(miptex->name, in);\n/*\t\t\tif (in->encoding != PTI_P8)\n\t\t\t\tprintf(\"\\t%16.16s: %u*%u%s (%s: %i*%i)\\n\", miptex->name, miptex->width, miptex->height, miptex->offsets[0]?\"\":\" (external data)\", Image_FormatName(in->encoding), in->mip[0].width, in->mip[0].height);\n\t\t\telse\n\t\t\t\tprintf(\"\\t%16.16s: %u*%u%s\\n\", miptex->name, miptex->width, miptex->height, miptex->offsets[0]?\"\":\" (external data)\");\n*/\t\t\tImgTool_FreeMips(in);\n\t\t}\n\t}\n\telse if (fsize >= sizeof(dmdl_t) && (\n\t\t!memcmp(indata,IDPOLYHEADER) && (((dmdl_t*)indata)->version == ALIAS_VERSION || ((dmdl_t*)indata)->version == 50)))\n\t{\t//quake's mdl format. also hexen2's missionpack format.\n\t\tint i, j, numframes;\n\t\tchar skinname[256];\n\t\tdmdl_t *mdl = (dmdl_t *)indata;\n\t\tdaliasskintype_t *pskintype = (daliasskintype_t*)(indata + sizeof(*mdl)-((((dmdl_t*)indata)->version == 50)?0:sizeof(int)));\n\t\tdaliasskingroup_t *pskingroup;\n\t\tdaliasskininterval_t *intervals;\n\t\tuploadfmt_t encoding;\n\n\t\tstruct pendingtextureinfo *out = Z_Malloc(sizeof(*out));\n\n\t\tif( mdl->flags & MFH2_TRANSPARENT )\n\t\t\tencoding = TF_H2_T7G1;\t//hexen2\n\t\telse if( mdl->flags & MFH2_HOLEY )\n\t\t\tencoding = TF_H2_TRANS8_0;\t//hexen2\n\t\telse if( mdl->flags & MFH2_SPECIAL_TRANS )\n\t\t\tencoding = TF_H2_T4A4;\t//hexen2\n\t\telse\n\t\t\tencoding = TF_SOLID8;\n\n\t\tprintf(\"%-20s: mdl file (%u skingroups)\\n\", inname, mdl->numskins);\n\t\tfor (i = 0; i < mdl->numskins; i++)\n\t\t{\n\t\t\tswitch(LittleLong(pskintype->type))\n\t\t\t{\n\t\t\tcase ALIAS_SKIN_SINGLE:\n\t\t\t\tout = Z_Malloc(sizeof(*out));\n\t\t\t\tout->type = PTI_2D;\n\t\t\t\tout->encoding = encoding;\n\t\t\t\tout->mipcount = 1;\n\t\t\t\tout->mip[0].datasize = mdl->skinwidth*mdl->skinheight;\n\t\t\t\tout->mip[0].width = mdl->skinwidth;\n\t\t\t\tout->mip[0].height = mdl->skinheight;\n\t\t\t\tout->mip[0].depth = 1;\n\t\t\t\tout->mip[0].needfree = false;\n\t\t\t\tout->mip[0].data = (qbyte*)(pskintype+1);\n\n\t\t\t\tpskintype = (daliasskintype_t *)((char *)out->mip[0].data+out->mip[0].datasize);\n\t\t\t\tprintf(\"\\t%s_%u: %i*%i\\n\", inname, i, mdl->skinwidth, mdl->skinheight);\n\t\t\t\tsnprintf(skinname, sizeof(skinname), \"%s_%u\", inname, i);\n\t\t\t\tcallback(skinname, out);\n\t\t\t\tImgTool_FreeMips(out);\n\t\t\t\tbreak;\n\n\t\t\tdefault:\n\t\t\t\tpskingroup = (daliasskingroup_t*)(pskintype+1);\n\t\t\t\tintervals = (daliasskininterval_t *)(pskingroup+1);\n\t\t\t\tnumframes = LittleLong(pskingroup->numskins);\n\t\t\t\tfor (j = 0; j < numframes; j++)\n\t\t\t\t{\n\t\t\t\t\tout = Z_Malloc(sizeof(*out));\n\t\t\t\t\tout->type = PTI_2D;\n\t\t\t\t\tout->encoding = encoding;\n\t\t\t\t\tout->mipcount = 1;\n\t\t\t\t\tout->mip[0].datasize = mdl->skinwidth*mdl->skinheight;\n\t\t\t\t\tout->mip[0].width = mdl->skinwidth;\n\t\t\t\t\tout->mip[0].height = mdl->skinheight;\n\t\t\t\t\tout->mip[0].depth = 1;\n\t\t\t\t\tout->mip[0].needfree = false;\n\t\t\t\t\tout->mip[0].data = (qbyte *)(intervals + numframes) + mdl->skinwidth*mdl->skinheight*j;\n\n\t\t\t\t\tprintf(\"\\t\\t%s_%u_%u: %i*%i @ %g\\n\", inname, i, j, mdl->skinwidth, mdl->skinheight, intervals[j].interval);\n\t\t\t\t\tsnprintf(skinname, sizeof(skinname), \"%s_%u_%u\", inname, i, j);\n\t\t\t\t\tcallback(skinname, out);\n\t\t\t\t\tImgTool_FreeMips(out);\n\t\t\t\t}\n\t\t\t\tpskintype = (daliasskintype_t *)((qbyte *)(intervals+numframes) + mdl->skinwidth*mdl->skinheight*numframes);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\t//else spr\n\telse\n\t{\n\t\tconst miptex_t *mip = NULL;\n\t\tconst char *ex = COM_GetFileExtension(inname, NULL);\n\t\tif (!strcasecmp(ex, \".mip\"))\n\t\t{\n\t\t\tin = ImgTool_DecodeMiptex(NULL, (miptex_t*)indata, fsize, NULL);\n\t\t\tif (fsize >= sizeof(miptex_t))\n\t\t\t\tmip = (const miptex_t*)indata;\n\t\t}\n\t\telse\n\t\t\tin = Image_LoadMipsFromMemory(args->flags|IF_NOMIPMAP, inname, inname, indata, fsize);\n\t\tif (!in)\n\t\t\tprintf(\"%-20s: unsupported format\\n\", inname);\n\t\telse\n\t\t\tcallback(inname, in);\n\t\t(void)mip;\n\t\tImgTool_FreeMips(in);\n\t}\n\tfflush(stdout);\n}\n\nstruct filelist_s\n{\n\tconst char **exts;\n\tsize_t numfiles;\n\tstruct {\n\t\tconst char *rootpath; //the basepath that was passed to the filelist scan.\n\t\tchar *name;\n\t\tsize_t baselen; //length up to but not including the filename extension.\n\t} *file;\n\tsize_t maxfiles; //to avoid reallocs\n};\nstatic void FileList_Release(struct filelist_s *list)\n{\n\tsize_t i;\n\tfor (i = 0; i < list->numfiles; i++)\n\t\tfree(list->file[i].name);\n\tfree(list->file);\n\tlist->numfiles = 0;\n\tlist->maxfiles = 0;\n}\nstatic void FileList_Add(struct filelist_s *list, const char *rootpath, const char *fname)\n{\n\tsize_t i;\n\tsize_t baselen;\n\tconst char *ext = COM_GetFileExtension(fname, NULL);\n\tfor (i = 0; ; i++)\n\t{\n\t\tif (!list->exts[i])\n\t\t\treturn;\t//extension wasn't in the list.\n\t\tif (!strcmp(list->exts[i], ext))\n\t\t\tbreak;\t//one of the accepted extensions\n\t}\n\tbaselen = ext?ext-fname:strlen(fname);\n\tfor (i = 0; i < list->numfiles; i++)\n\t{\n\t\tif (list->file[i].baselen == baselen && !strncasecmp(list->file[i].name, fname, baselen))\n\t\t{\n\t\t\tif (strcasecmp(list->file[i].name+baselen, fname+baselen) > 0)\n\t\t\t{\n\t\t\t\tCon_Printf(\"Ignoring dupe file %s (using %s)\\n\", list->file[i].name, fname);\n\t\t\t\t//use the 'lower' one instead, for consistency between systems.\n\t\t\t\tlist->file[i].rootpath = rootpath;\n\t\t\t\tlist->file[i].name = strdup(fname);\n\t\t\t\tlist->file[i].baselen = baselen;\n\t\t\t}\n\t\t\telse\n\t\t\t\tCon_Printf(\"Ignoring dupe file %s (using %s)\\n\", fname, list->file[i].name);\n\t\t\treturn; //file already listed, but maybe with a different extension\n\t\t}\n\t}\n\tif (i == list->maxfiles)\n\t{\n\t\tlist->maxfiles += 64;\n\t\tlist->file = realloc(list->file, sizeof(*list->file)*list->maxfiles);\n\t}\n\tlist->file[i].rootpath = rootpath;\n\tlist->file[i].name = strdup(fname);\n\tlist->file[i].baselen = baselen;\n\tlist->numfiles++;\n}\n#ifdef _WIN32\nstatic void ImgTool_TreeScan(struct filelist_s *list, const char *rootpath, const char *subpath)\n{\t//FIXME: convert to utf-8.\n\tHANDLE h;\n\tWIN32_FIND_DATAA fd;\n\tchar file[MAX_OSPATH];\n\n\tif (subpath && *subpath)\n\t\tQ_snprintfz(file, sizeof(file), \"%s/%s\", rootpath, subpath);\n\telse\n\t\tQ_snprintfz(file, sizeof(file), \"%s\", rootpath);\n\tif (GetFileAttributesA(file) & FILE_ATTRIBUTE_DIRECTORY)\t//if its a directory then scan it.\n\t\tQ_snprintfz(file+strlen(file), sizeof(file)-strlen(file), \"/*\");\n\n\th = FindFirstFileA(file, &fd);\n\tif (h != INVALID_HANDLE_VALUE)\n\t{\n\t\tdo\n\t\t{\n\t\t\tif (*fd.cFileName == '.')\n\t\t\t\tcontinue;\t//skip .. (and unix hidden files, because urgh)\n\n\t\t\tif (subpath && *subpath)\n\t\t\t\tQ_snprintfz(file, sizeof(file), \"%s/%s\", subpath, fd.cFileName);\n\t\t\telse\n\t\t\t\tQ_snprintfz(file, sizeof(file), \"%s\", fd.cFileName);\n\n\t\t\tif (fd.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)\n\t\t\t\t; //don't report hidden entries.\n\t\t\telse if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)\n\t\t\t\tImgTool_TreeScan(list, rootpath, file);\n\t\t\telse\n\t\t\t\tFileList_Add(list, rootpath, file);\n\t\t} while(FindNextFileA(h, &fd));\n\t\tFindClose(h);\n\t}\n}\n#else\n#include <dirent.h>\n#include <fnmatch.h>\nstatic void ImgTool_TreeScan(struct filelist_s *list, const char *basepath, const char *subpath)\n{\n\tDIR *dir;\n\tchar file[MAX_OSPATH];\n\tstruct dirent *ent;\n\tstruct stat sb;\n\n\tif (subpath && *subpath)\n\t\tQ_snprintfz(file, sizeof(file), \"%s/%s\", basepath, subpath);\n\telse\n\t\tQ_snprintfz(file, sizeof(file), \"%s\", basepath);\n\tstat(file, &sb);\n\tif ((sb.st_mode & S_IFMT) == S_IFDIR)\n\t{\n\t\tdir = opendir(file);\n\t\tif (!dir)\n\t\t{\n\t\t\tCon_Printf(\"Failed to open dir %s\\n\", file);\n\t\t\treturn;\n\t\t}\n\t\tfor (;;)\n\t\t{\n\t\t\tent = readdir(dir);\n\t\t\tif (!ent)\n\t\t\t\tbreak;\n\t\t\tif (*ent->d_name == '.')\n\t\t\t\tcontinue;\n\t\t\telse if (ent->d_type == DT_DIR)\n\t\t\t{\n\t\t\t\tif (!subpath)\n\t\t\t\t\tcontinue;\n\t\t\t\tif (*subpath)\n\t\t\t\t\tQ_snprintfz(file, sizeof(file), \"%s/%s\", subpath, ent->d_name);\n\t\t\t\telse\n\t\t\t\t\tQ_snprintfz(file, sizeof(file), \"%s\", ent->d_name);\n\t\t\t\tImgTool_TreeScan(list, basepath, file);\n\t\t\t}\n\t\t\telse if (ent->d_type == DT_REG)\n\t\t\t{\n\t\t\t\tif (subpath && *subpath)\n\t\t\t\t\tQ_snprintfz(file, sizeof(file), \"%s/%s\", subpath, ent->d_name);\n\t\t\t\telse\n\t\t\t\t\tQ_snprintfz(file, sizeof(file), \"%s\", ent->d_name);\n\t\t\t\tFileList_Add(list, basepath, file);\n\t\t\t}\n\t\t}\n\t\tclosedir(dir);\n\t}\n\telse\n\t{\n\t\tif (*file == '/')\n\t\t\tFileList_Add(list, \"\", file);\n\t\telse\n\t\t\tFileList_Add(list, \".\", file);\n\t}\n}\n#endif\nstatic void ImgTool_TreeConvert(struct opts_s *args, const char *destpath, const char *srcpath)\n{\n\tsize_t newfiles=0, skippedfiles=0, processedfiles=0;\n\tchar file[MAX_OSPATH];\n\tchar dest[MAX_OSPATH];\n\tconst char *exts[] = {\".png\", \".bmp\", \".tga\", \".jpg\", \".exr\", \".hdr\", \".pcx\", NULL};\n\tstruct filelist_s list = {exts};\n\tsize_t i, destlen = strlen(destpath)+1;\n\tImgTool_TreeScan(&list, srcpath, \"\");\n\n\tif (!list.numfiles)\n\t\tCon_Printf(\"No suitable files found in directory: %s\\n\", srcpath);\n\n\tfor (i = 0; i < list.numfiles; i++)\n\t{\n\t\tstruct stat statsrc, statdst;\n\t\tQ_snprintfz(file, sizeof(file), \"%s/%s\", srcpath, list.file[i].name);\n\t\tQ_snprintfz(dest, sizeof(dest), \"%s/%s\", destpath, list.file[i].name);\n\t\tQ_snprintfz(dest+destlen+list.file[i].baselen, sizeof(dest)-destlen-list.file[i].baselen, \".dds\");\n\n\t\tif (stat(file, &statsrc) < 0)\n\t\t{\n\t\t\tCon_Printf(\"stat(\\\"%s\\\") failed...\\n\", file);\n\t\t\tcontinue;\n\t\t}\n\t\tif (stat(dest, &statdst) < 0)\n\t\t{\n\t\t\tstatdst.st_mtime = INT_MIN; //make it look old\n\t\t\tnewfiles++;\n\t\t}\n\t\tif (statdst.st_mtime <= statsrc.st_mtime)\n\t\t{\n\t\t\tprocessedfiles++;\n//\t\t\tCon_Printf(\"Image file %s -> %s\\n\", file, dest);\n\t\t\tFS_CreatePath(dest, FS_SYSTEM);\n\t\t\tImgTool_Convert(args, ImgTool_Read(args, file), file, dest);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tskippedfiles++;\n//\t\t\tCon_Printf(\"Unmodified image file %s -> %s\\n\", file, dest);\n\t\t}\n\t}\n\tCon_Printf(\"found: %u, processed: %u, skipped: %u, new: %u\\n\", (unsigned int)list.numfiles, (unsigned int)processedfiles, (unsigned int)skippedfiles, (unsigned int)newfiles);\n\tFileList_Release(&list);\n\treturn;\n}\n\nstatic void ImgTool_WadExtract(struct opts_s *args, const char *wadname)\n{\n\tqbyte *indata;\n\tsize_t fsize;\n\tsize_t m;\n\tqbyte *basepal = NULL;\n\tindata = FS_LoadMallocFile(wadname, &fsize);\n\tif (!indata)\n\t\tprintf(\"%s: unable to read\\n\", wadname);\n\telse if (fsize >= sizeof(wad2_t) && indata[0] == 'W' && indata[1] == 'A' && indata[2] == 'D')\n\t{\n\t\tconst wad2_t *w = (const wad2_t *)indata;\n\t\tconst wad2entry_t *e = (const wad2entry_t *)(indata+w->offset);\n\t\tint i;\n\t\tchar clean[sizeof(e->name)+1];\n\n\t\tfor (m = 0; m < w->num; m++, e++)\n\t\t{\n\t\t\tswitch(e->type)\n\t\t\t{\n\t\t\tcase 67:\t//hl...\n\t\t\tcase TYP_MIPTEX:\n\t\t\t\t{\n\t\t\t\t\tmiptex_t *mip = (miptex_t *)(indata+e->offset);\n\n\t\t\t\t\tif (!strcasecmp(e->name, \"CONCHARS\") && e->size==128*128)\n\t\t\t\t\t{\t//special hack for conchars, which is listed as a miptex for some reason, with no qpic header (it not being a qpic lump)\n\t\t\t\t\t\tstruct pendingtextureinfo *out = Z_Malloc(sizeof(*out));\n\t\t\t\t\t\tout->encoding = TF_H2_TRANS8_0;\n\t\t\t\t\t\tout->type = PTI_2D;\n\t\t\t\t\t\tout->mip[0].width = 128;\n\t\t\t\t\t\tout->mip[0].height = 128;\n\t\t\t\t\t\tout->mip[0].depth = 1;\n\t\t\t\t\t\tout->mip[0].datasize = out->mip[0].width*out->mip[0].height*out->mip[0].depth;\n\t\t\t\t\t\tout->mip[0].data = (char*)mip;\n\t\t\t\t\t\tout->mipcount = 1;\n\t\t\t\t\t\tImgTool_Convert(args, out, \"conchars\", NULL);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\tImgTool_DecodeMiptex(args, mip, e->dsize, basepal);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase TYP_QPIC:\n\t\t\t\t{\n\t\t\t\t\tint *qpic = (int *)(indata+e->offset);\n\t\t\t\t\tstruct pendingtextureinfo *out = Z_Malloc(sizeof(*out));\n\t\t\t\t\tsize_t sz;\n\t\t\t\t\tqbyte *p;\n\n\t\t\t\t\tif (e->size < 8 || 8+qpic[0]*qpic[1] != e->size)\n\t\t\t\t\t{\n\t\t\t\t\t\tprintf(\"invalid size/header for qpic lump: %s\\n\", e->name);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\tout->type = PTI_2D;\n\t\t\t\t\tout->mip[0].width = LittleLong(qpic[0]);\n\t\t\t\t\tout->mip[0].height = LittleLong(qpic[1]);\n\t\t\t\t\tout->mip[0].depth = 1;\n\t\t\t\t\tout->mip[0].datasize = out->mip[0].width*out->mip[0].height*out->mip[0].depth;\n\t\t\t\t\tout->mip[0].data = (char*)(qpic+2);\n\t\t\t\t\tout->mipcount = 1;\n\n\t\t\t\t\tfor (sz = 0, p = out->mip[0].data; sz < out->mip[0].datasize; sz++)\n\t\t\t\t\t\tif (p[sz] == 255)\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\tout->encoding = sz<out->mip[0].datasize?TF_TRANS8:PTI_P8;\n\n\t\t\t\t\tfor (i = 0; i < sizeof(e->name); i++)\n\t\t\t\t\t{\t//lowercase it.\n\t\t\t\t\t\tif (e->name[i] >= 'A' && e->name[i] <= 'Z')\n\t\t\t\t\t\t\tclean[i] = (e->name[i]-'A')+'a';\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tclean[i] = e->name[i];\n\t\t\t\t\t}\n\t\t\t\t\tclean[sizeof(e->name)] = 0;\n\n\t\t\t\t\tImgTool_Convert(args, out, clean, NULL);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase TYP_PALETTE:\n\t\t\t\tif (e->size == 768)\n\t\t\t\t{\n\t\t\t\t\tbasepal = indata+e->offset;\n\t\t\t\t\tif (!memcmp(basepal, host_basepal, 768))\n\t\t\t\t\t\tbasepal = NULL;\n\t\t\t\t}\n\t\t\t\tFALLTHROUGH\n\t\t\tdefault:\n\t\t\t\tprintf(\"skipping %s\\n\", e->name);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\telse if (fsize >= sizeof(dheader_t) && (\n\t\t!memcmp(indata, BSPVERSION)\t\t||\n//\t\t!memcmp(indata, BSPVERSIONHL)\t||\n\t\t!memcmp(indata, BSPVERSIONPREREL)||\n\t\t!memcmp(indata, BSPVERSION_LONG1)||\n\t\t!memcmp(indata, BSPVERSION_LONG2)))\n\t{\t//q1bsp\n\t\tdheader_t *bsp = (dheader_t*)indata;\n\t\tdmiptexlump_t *texlump = (dmiptexlump_t*)(indata + bsp->lumps[LUMP_TEXTURES].fileofs);\n\t\tmiptex_t *miptex;\n\t\tsize_t i;\n\t\tsize_t sz = bsp->lumps[LUMP_TEXTURES].filelen;\n\t\tfor (i = texlump->nummiptex; i --> 0; )\n\t\t{\n\t\t\tif (texlump->dataofs[i] < 0 || texlump->dataofs[i] >= bsp->lumps[LUMP_TEXTURES].filelen)\n\t\t\t\tcontinue;\n\n\t\t\tmiptex = (miptex_t*)((qbyte*)texlump + texlump->dataofs[i]);\n\t\t\tif (*miptex->name && miptex->width && miptex->height && miptex->offsets[0]>0)\n\t\t\t{\n\t\t\t\tImgTool_DecodeMiptex(args, miptex, sz - texlump->dataofs[i], basepal);\n\t\t\t\tsz = texlump->dataofs[i];\n\t\t\t}\n\t\t}\n\t}\n\telse if (fsize >= sizeof(dmdl_t) && (\n\t\t!memcmp(indata,IDPOLYHEADER) && ((dmdl_t*)indata)->version == ALIAS_VERSION))\n\t{\n\t\tchar imgname[1024];\n\t\tint i, j, numframes;\n\t\tdmdl_t *mdl = (dmdl_t *)indata;\n\t\tdaliasskintype_t *pskintype = (daliasskintype_t*)(indata + sizeof(*mdl));\n\t\tdaliasskingroup_t *pskingroup;\n\t\tdaliasskininterval_t *intervals;\n\n\t\tstruct pendingtextureinfo *out = Z_Malloc(sizeof(*out));\n\t\tout->type = PTI_2D;\n\n\t#ifdef HEXEN2\n\t\tif( mdl->flags & MFH2_TRANSPARENT )\n\t\t\tout->encoding = TF_H2_T7G1;\t//hexen2\n\t\telse\n\t#endif\n\t\t if( mdl->flags & MFH2_HOLEY )\n\t\t\tout->encoding = TF_H2_TRANS8_0;\t//hexen2\n\t#ifdef HEXEN2\n\t\telse if( mdl->flags & MFH2_SPECIAL_TRANS )\n\t\t\tout->encoding = TF_H2_T4A4;\t//hexen2\n\t#endif\n\t\telse\n\t\t\tout->encoding = TF_SOLID8;\n\n\t\tout->mipcount = 1;\n\t\tout->mip[0].datasize = mdl->skinwidth*mdl->skinheight;\n\t\tout->mip[0].width = mdl->skinwidth;\n\t\tout->mip[0].height = mdl->skinheight;\n\t\tout->mip[0].depth = 1;\n\n\t\tfor (i = 0; i < mdl->numskins; i++)\n\t\t{\n\t\t\tswitch(LittleLong(pskintype->type))\n\t\t\t{\n\t\t\tcase ALIAS_SKIN_SINGLE:\n\t\t\t\tout->mip[0].data = (qbyte*)(pskintype+1);\n\t\t\t\tQ_snprintfz(imgname, sizeof(imgname), \"%s_%i.\", wadname, i);\n\t\t\t\tImgTool_Convert(args, out, imgname, NULL);\n\t\t\t\tpskintype = (daliasskintype_t *)((char *)out->mip[0].data+out->mip[0].datasize);\n\t\t\t\tbreak;\n\n\t\t\tdefault:\n\t\t\t\tpskingroup = (daliasskingroup_t*)(pskintype+1);\n\t\t\t\tintervals = (daliasskininterval_t *)(pskingroup+1);\n\t\t\t\tnumframes = LittleLong(pskingroup->numskins);\n\t\t\t\tout->mip[0].data = (qbyte *)(intervals + numframes);\n\n\t\t\t\tfor (j = 0; j < numframes; j++,out->mip[0].data=(char*)out->mip[0].data+out->mip[0].datasize)\n\t\t\t\t{\n\t\t\t\t\tQ_snprintfz(imgname, sizeof(imgname), \"%s_%i_%i.\", wadname, i, j);\n\t\t\t\t\tImgTool_Convert(args, out, imgname, NULL);\n\t\t\t\t}\n\t\t\t\tpskintype = (daliasskintype_t *)out->mip[0].data;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\telse\n\t\tprintf(\"%s: does not appear to be a wad file\\n\", wadname);\n}\nint Image_SortPalette(const void *av, const void *bv)\n{\n\tconst struct\n\t{\n\t\tqbyte r,g,b;\n\t\tint count;\n\t} *a=av, *b=bv;\n\treturn b->count - a->count;\n}\nstatic qbyte *Image_GenPalette(struct pendingtextureinfo *in, const char *name)\n{\n\tunsigned int t;\n\tqbyte *pal = Z_Malloc(768);\n\tqbyte *d;\n\tqboolean hasalpha = false;\n\tstruct\n\t{\n\t\tqbyte r,g,b;\n\t\tint count;\n\t} *p = NULL;\n\tsize_t nump=0, maxp=0, i;\n\tstatic qboolean mippixelformats[PTI_MAX] = {[PTI_RGBA8]=true};\n\tImage_ChangeFormat(in, mippixelformats, PTI_INVALID, name); //make sure its rgbx\n\tt = in->mip[0].width*in->mip[0].height*in->mip[0].depth;\n\td = in->mip[0].data;\n\tfor(; t --> 0; d += 4)\n\t{\n\t\tfor(i = 0; i < nump; i++)\n\t\t{\n\t\t\tif (p[i].r == d[0] &&\n\t\t\t\tp[i].g == d[1] &&\n\t\t\t\tp[i].b == d[2])\n\t\t\t{\n\t\t\t\tp[i].count++;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (i == nump)\n\t\t{\t//new rgb value.\n\t\t\tif (nump == maxp)\n\t\t\t{\t//urgh. need more.\n\t\t\t\tmaxp += 64;\n\t\t\t\tp = realloc(p, sizeof(*p)*maxp);\n\t\t\t}\n\t\t\tp[i].r = d[0];\n\t\t\tp[i].g = d[1];\n\t\t\tp[i].b = d[2];\n\t\t\tp[i].count = 1;\n\t\t\tnump++;\n\t\t}\n\t}\n\tqsort(p, nump, sizeof(*p), Image_SortPalette);\n\tif (nump > 255)\n\t\tCon_Printf(\"%s: %u unique colours\\n\", name, (unsigned)nump);\n\telse\n\t\tCon_DPrintf(\"%s: %u unique colours\\n\", name, (unsigned)nump);\n\tif (nump >= 256)\n\t\tnump = 256;\n\telse\n\t\tmemset(pal, 0, 768); //make sure its set to something.\n\tfor (i = 0; i < nump; i++)\n\t{\t//fill in the 256 most common colours. should probably compress them a bit if there's more.\n\t\tpal[i*3+0] = p[i].r;\n\t\tpal[i*3+1] = p[i].g;\n\t\tpal[i*3+2] = p[i].b;\n\t}\n\n\tif (hasalpha == true) {\n\t\tpal[255*3+0] = 0;\n\t\tpal[255*3+1] = 0;\n\t\tpal[255*3+2] = 255;\n\t}\n\n\tfree(p);\n\treturn pal;\n}\n//spits out our extended .mip format\nstatic qboolean ImgTool_MipExport(struct opts_s *args, vfsfile_t *outfile, struct pendingtextureinfo *in, const char *mipname, int wadtype)\n{\n\tstruct pendingtextureinfo *highcolour = NULL;\n\tchar *highcode = NULL, *ext;\n\tsize_t u;\n\tunsigned int m, tsz;\n\tmiptex_t mip;\n\tqbyte *pal = host_basepal;\n\n\tstatic qboolean mippixelformats[PTI_MAX] = {[PTI_P8]=true};\n\n\tif (!in || !in->mipcount)\n\t{\n\t\tCon_Printf(\"%s: unable to load any mips\\n\", mipname);\n\t\treturn false;\n\t}\n\n\tin = ImgTool_DupeMipchain(in);\n\tif (in->mipcount < 4)\n\t\tImage_GenerateMips(in, args->flags);\n\tif (in->encoding == PTI_P8 || args->newpixelformat == PTI_INVALID)\n\t{\n\t\thighcode = NULL; //no, don't store it weirdly...\n\t}\n\telse\n\t{\n\t\thighcolour = ImgTool_DupeMipchain(in);\n\t\tImage_GenerateMips(highcolour, args->flags);\n\t\tfor (u = 1; u < countof(sh_config.texfmt); u++)\n\t\t\tsh_config.texfmt[u] = true;\n\t\tif (!ImgTool_ConvertPixelFormat(args, mipname, highcolour))\n\t\t{\n\t\t\tCon_Printf(\"%s: Unable to convert to requested pixel format\\n\", mipname);\n\t\t\tImgTool_FreeMips(highcolour);\n\t\t\thighcolour = NULL;\n\t\t}\n\t\telse if (highcolour->mip[highcolour->mipcount-1].width != 1 || highcolour->mip[highcolour->mipcount-1].height != 1)\n\t\t{\n\t\t\tCon_Printf(\"%s: Mipchain truncated\\n\", mipname);\n\t\t\tImgTool_FreeMips(highcolour);\n\t\t\thighcolour = NULL;\n\t\t}\n\t\telse for (u = 1; u < highcolour->mipcount; u++)\n\t\t{\t//mip chain must round down consistently.\n\t\t\tif (highcolour->mip[u].width != max(1,highcolour->mip[u-1].width>>1) ||\n\t\t\t\thighcolour->mip[u].height!= max(1,highcolour->mip[u-1].height>>1))\n\t\t\t{\n\t\t\t\tCon_Printf(\"%s: Mipchain sized wrongly\\n\", mipname);\n\t\t\t\tImgTool_FreeMips(highcolour);\n\t\t\t\thighcolour = NULL;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tif (highcolour)\tswitch(highcolour->encoding)\n\t\t{\n\t\tcase PTI_BC1_RGB:\n\t\tcase PTI_BC1_RGBA:\t\thighcode = \"BC1\";\tbreak;\t//not in any core gl, but uniquitous on desktop, but not mobile.\n\t\tcase PTI_BC2_RGBA:\t\thighcode = \"BC2\";\tbreak;\n\t\tcase PTI_BC3_RGBA:\t\thighcode = \"BC3\";\tbreak;\n\t\tcase PTI_BC4_R:\t\t\thighcode = \"BC4\";\tbreak;\n\t\tcase PTI_BC5_RG:\t\thighcode = \"BC5\";\tbreak;\n\t\tcase PTI_BC6_RGB_UFLOAT:highcode = \"BC6\";\tbreak;\t//aka bptc, core in gl4.2 (not gles)\n\t\tcase PTI_BC7_RGBA:\t\thighcode = \"BC7\";\tbreak;\t//aka bptc, core in gl4.2 (not gles)\n\t\tcase PTI_ETC1_RGB8:\t\thighcode = \"ETC1\";\tbreak;\t//available on most gles2 devices.\n\t\tcase PTI_ETC2_RGB8:\t\thighcode = \"ETC2\";\tbreak;\t//core in gles3 (or gl4.3)\n\t\tcase PTI_ETC2_RGB8A1:\thighcode = \"ETCP\";\tbreak;\t//core in gles3 (or gl4.3)\n\t\tcase PTI_ETC2_RGB8A8:\thighcode = \"ETCA\";\tbreak;\t//core in gles3 (or gl4.3)\n\t\tcase PTI_ASTC_4X4_LDR:\thighcode = \"AST4\";\tbreak;\t//core in gles3.2\n\t\tcase PTI_ASTC_5X4_LDR:\thighcode = \"AS54\";\tbreak;\t//core in gles3.2\n\t\tcase PTI_ASTC_5X5_LDR:\thighcode = \"AST5\";\tbreak;\t//core in gles3.2\n\t\tcase PTI_ASTC_6X5_LDR:\thighcode = \"AS65\";\tbreak;\t//core in gles3.2\n\t\tcase PTI_ASTC_6X6_LDR:\thighcode = \"AST6\";\tbreak;\t//core in gles3.2\n\t\tcase PTI_ASTC_8X5_LDR:\thighcode = \"AS85\";\tbreak;\t//core in gles3.2\n\t\tcase PTI_ASTC_8X6_LDR:\thighcode = \"AS86\";\tbreak;\t//core in gles3.2\n\t\tcase PTI_ASTC_10X5_LDR:\thighcode = \"AS05\";\tbreak;\t//core in gles3.2\n\t\tcase PTI_ASTC_10X6_LDR:\thighcode = \"AS06\";\tbreak;\t//core in gles3.2\n\t\tcase PTI_ASTC_8X8_LDR:\thighcode = \"AST8\";\tbreak;\t//core in gles3.2\n\t\tcase PTI_ASTC_10X8_LDR:\thighcode = \"AS08\";\tbreak;\t//core in gles3.2\n\t\tcase PTI_ASTC_10X10_LDR:highcode = \"AST0\";\tbreak;\t//core in gles3.2\n\t\tcase PTI_ASTC_12X10_LDR:highcode = \"AS20\";\tbreak;\t//core in gles3.2\n\t\tcase PTI_ASTC_12X12_LDR:highcode = \"AST2\";\tbreak;\t//core in gles3.2\n\t\tcase PTI_RGB565:\t\thighcode = \"565\";\tbreak;\n\t\tcase PTI_RGBA5551:\t\thighcode = \"5551\";\tbreak;\n\t\tcase PTI_RGBA4444:\t\thighcode = \"4444\";\tbreak;\n\t\tcase PTI_RGB8:\t\t\thighcode = \"RGB\";\tbreak;\t//generally needs reformatting to rgbx.\n\t\tcase PTI_RGBA8:\t\t\thighcode = \"RGBA\";\tbreak;\t//bloaty\n\t\tcase PTI_L8:\t\t\thighcode = \"LUM8\";\tbreak;\n\t\tcase PTI_E5BGR9:\t\thighcode = \"EXP5\";\tbreak;\t//gl3+\n\t\tdefault:\n\t\t\tCon_Printf(\"%s: unsupported pixel format(%s) for miptex\\n\", mipname, Image_FormatName(highcolour->encoding));\n\t\t\tImgTool_FreeMips(highcolour);\n\t\t\thighcolour = NULL;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tif (args->width && args->height && in->mipcount >= 1)\n\t{\n\t\tqbyte *newimg;\n\t\tunsigned int bb, bw, bh, bd;\n\t\tImage_BlockSizeForEncoding(in->encoding, &bb, &bw, &bh, &bd);\n\t\tnewimg = Image_ResampleTexture(in->encoding, in->mip[0].data, in->mip[0].width, in->mip[0].height, NULL, args->width, args->height);\n\t\tif (newimg)\n\t\t{\n\t\t\tin->mipcount = 1;\t//urgh\n\t\t\tif (in->mip[0].needfree)\n\t\t\t\tBZ_Free(in->mip[0].data);\n\t\t\tin->mip[0].data = newimg;\n\t\t\tin->mip[0].needfree = true;\n\t\t\tin->mip[0].width = args->width;\n\t\t\tin->mip[0].height = args->height;\n\t\t\tin->mip[0].depth = 1;\n\t\t\tin->mip[0].datasize = bb*((in->mip[0].width+bw-1)/bw)*((in->mip[0].height+bh-1)/bh)*((in->mip[0].depth+bd-1)/bd);\n\n\t\t\tImage_GenerateMips(in, args->flags);\n\t\t}\n\t\telse\n\t\t\tCon_Printf(\"%s: unable to resize %s\\n\", mipname, Image_FormatName(in->encoding));\n\t}\n\n\tif (args->mipnum >= in->mipcount)\n\t{\n\t\tCon_Printf(\"%s: not enough mips\\n\", mipname);\n\t\tImgTool_FreeMips(in);\n\t\tImgTool_FreeMips(highcolour);\n\t\treturn false;\n\t}\n\n\t//strip out all but the 4 mip levels we care about.\n\tfor (u = 0; u < in->mipcount; u++)\n\t{\n\t\tif (u >= args->mipnum && u < args->mipnum+4)\n\t\t{\n\t\t\tif (wadtype<2)\n\t\t\t{\t//if we're stripping out the wad data (so that the engine ends up requiring external textures) then do it now before palettizing, for efficiency.\n\t\t\t\tif (in->mip[u].needfree)\n\t\t\t\t\tBZ_Free(in->mip[u].data);\n\t\t\t\tin->mip[u].data = NULL;\n\t\t\t\tin->mip[u].datasize = 0;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (in->mip[u].needfree)\n\t\t\t\tBZ_Free(in->mip[u].data);\n\t\t\tmemset(&in->mip[u], 0, sizeof(in->mip[u]));\n\t\t}\n\t}\n\tin->mipcount -= args->mipnum;\n\tif (in->mipcount > 4)\n\t\tin->mipcount = 4;\n\tmemmove(&in->mip[0], &in->mip[args->mipnum], sizeof(in->mip[0])*in->mipcount);\n\tmemset(&in->mip[in->mipcount], 0, sizeof(in->mip[0])*((args->mipnum+4)-in->mipcount)); //null it out, just in case.\n\n\tif (in->mip[0].data)\n\t{\n\t\tif (in->encoding != PTI_P8)\n\t\t{\n\t\t\tqbyte *basepal = host_basepal;\n\t\t\tif (wadtype == 3) {\n\t\t\t\tpal = host_basepal = Image_GenPalette(in, mipname);\n\t\t\t}\n\n\t\t\tif (*mipname=='{') {\n\t\t\t\tmemset(mippixelformats, 0, sizeof(mippixelformats));\n\t\t\t\tmippixelformats[TF_TRANS8] = true;\n\t\t\t}\n\n\t\t\tImage_ChangeFormat(in, mippixelformats, (*mipname=='{')?TF_TRANS8:PTI_INVALID, mipname);\n\t\t\thost_basepal = basepal;\n\t\t}\n\t\tif (!(in->encoding == PTI_P8 || in->encoding == TF_TRANS8))\n\t\t{\t//erk! we failed to palettize...\n\t\t\tImgTool_FreeMips(in);\n\t\t\tImgTool_FreeMips(highcolour);\n\t\t\tCon_Printf(\"%s: paletizing error (source format %s)\\n\", mipname, Image_FormatName(in->encoding));\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tif (!in->mip[0].width || (in->mip[0].width & 15))\n\t\tCon_Printf(\"%s(%i): WARNING: miptex width is not a multiple of 16 - %i*%i\\n\", mipname, args->mipnum, in->mip[0].width, in->mip[0].height);\n\tif (!in->mip[0].height || (in->mip[0].height & 15))\n\t\tCon_Printf(\"%s(%i): WARNING: miptex height is not a multiple of 16 - %i*%i\\n\", mipname, args->mipnum, in->mip[0].width, in->mip[0].height);\n\n\tmemset(mip.name, 0, sizeof(mip.name));\n\tQ_strncpyz(mip.name, mipname, sizeof(mip.name));\n\text = (char*)COM_GetFileExtension (mip.name, NULL);\n\twhile (*ext) *ext++=0;\n\tif (*mip.name == '#')\n\t\t*mip.name = '*';\t//make it a proper turb\n\tmip.width = in->mip[0].width;\n\tmip.height = in->mip[0].height;\n\tmip.offsets[0] = in->mip[0].datasize?sizeof(mip):0;\n\tmip.offsets[1] = in->mip[1].datasize?mip.offsets[0]+in->mip[0].datasize:0;\n\tmip.offsets[2] = in->mip[2].datasize?mip.offsets[1]+in->mip[1].datasize:0;\n\tmip.offsets[3] = in->mip[3].datasize?mip.offsets[2]+in->mip[2].datasize:0;\n\n\ttsz = sizeof(mip)+in->mip[0].datasize+in->mip[1].datasize+in->mip[2].datasize+in->mip[3].datasize;\n\tVFS_WRITE(outfile, &mip, sizeof(mip));\n\tVFS_WRITE(outfile, in->mip[0].data, in->mip[0].datasize);\n\tVFS_WRITE(outfile, in->mip[1].data, in->mip[1].datasize);\n\tVFS_WRITE(outfile, in->mip[2].data, in->mip[2].datasize);\n\tVFS_WRITE(outfile, in->mip[3].data, in->mip[3].datasize);\n\tif (wadtype == 3)\n\t{\n\t\ttsz += 2 + 256*3;\n\t\tVFS_WRITE(outfile, \"\\x00\\x01\", 2);\n\t\tVFS_WRITE(outfile, pal, 256*3);\n\t}\n\n\tif (highcolour)\n\t{\n\t\tunsigned int highsize;\n\t\tVFS_WRITE(outfile, \"\\x00\\xfb\\x2b\\xaf\", 4);\t//magic id to say that there's actually extensions here...\n\t\ttsz += 4;\n\t\t//spit out our high-colour lump here\n\t\tfor (highsize = 16, m = 0; m < highcolour->mipcount; m++)\n\t\t\thighsize += highcolour->mip[m].datasize;\n\t\tVFS_WRITE(outfile, &highsize, 4);\n\t\tVFS_WRITE(outfile, highcode, 4);\n\t\tVFS_WRITE(outfile, &highcolour->mip[0].width, 4);\n\t\tVFS_WRITE(outfile, &highcolour->mip[0].height, 4);\n\t\tfor (m = 0; m < highcolour->mipcount; m++)\n\t\t\tVFS_WRITE(outfile, highcolour->mip[m].data, highcolour->mip[m].datasize);\n\t\ttsz += highsize;\n\n\t\tImgTool_FreeMips(highcolour);\n\n\t\tCon_Printf(\"%s: %ix%i (%s: %ix%i %i)\\n\", mip.name, mip.width, mip.height, highcode, highcolour->mip[0].width, highcolour->mip[0].height, highcolour->mipcount);\n\t}\n\telse\n\t\tCon_Printf(\"%s: %ix%i%s\\n\", mip.name, mip.width, mip.height, wadtype==3?\"+pal\":\"\");\n\n\t//and pad it, just in case.\n\tif (tsz & 3)\n\t\tVFS_WRITE(outfile, \"\\0\\0\\0\\0\", 4-(tsz&3));\n\n\tImgTool_FreeMips(in);\n\treturn true;\n}\nstatic void ImgTool_WadConvert(struct opts_s *args, const char *destpath, const char **srcpaths, size_t numpaths, int wadtype/*x,2,3*/)\n{\n\tchar file[MAX_OSPATH];\n\tconst char *exts[] = {\".mip\", \".png\", \".bmp\", \".tga\", \".exr\", \".hdr\", \".dds\", \".ktx\", \".xcf\", \".pcx\", \".jpg\", NULL};\n\tstruct filelist_s list = {exts};\n\tsize_t i, u;\n\tvfsfile_t *f;\n\tchar *inname;\n\tqbyte *indata;\n\tsize_t fsize;\n\twad2_t wad2;\n\twad2entry_t *wadentries = NULL, *entry;\n\tsize_t maxentries = 0;\n\tqboolean qpics;\n\tstruct pendingtextureinfo *in;\n\tif (!numpaths)\n\t\tImgTool_TreeScan(&list, \".\", NULL);\n\telse while(numpaths --> 0)\n\t\tImgTool_TreeScan(&list, *srcpaths++, NULL);\n\n\tif (!list.numfiles)\n\t{\n\t\tprintf(\"%s: No files specified\\n\", destpath);\n\t\treturn;\n\t}\n\n\tif (!strcasecmp(COM_GetFileExtension(destpath, NULL), \".bsp\"))\n\t{\n\t}\n\telse\n\t{\n\t\tqpics = !strcasecmp(\"gfx.wad\", COM_SkipPath(destpath));\n\n\t\tf = FS_OpenVFS(destpath, \"wb\", FS_SYSTEM);\n\t\twad2.magic[0] = 'W';\n\t\twad2.magic[1] = 'A';\n\t\twad2.magic[2] = 'D';\n\t\twad2.magic[3] = (wadtype==3)?'3':'2';\t//wad3 instead of 2, so we can include a palette for tools to validate against\n\t\twad2.num = 0;\n\t\twad2.offset = 0;\n\t\tVFS_WRITE(f, &wad2, 12);\n\n\t\tif (wadtype == 2 && !qpics)\n\t\t{\t//WAD2 texture files generally have a palette lump.\n\t\t\tif (wad2.num == maxentries)\n\t\t\t{\n\t\t\t\tmaxentries += 64;\n\t\t\t\twadentries = realloc(wadentries, sizeof(*wadentries)*maxentries);\n\t\t\t}\n\t\t\tentry = &wadentries[wad2.num++];\n\t\t\tmemset(entry, 0, sizeof(*entry));\n\t\t\tQ_strncpyz(entry->name, \"PALETTE\", 16);\n\t\t\tentry->type = TYP_PALETTE;\n\t\t\tentry->offset = VFS_TELL(f);\n\n\t\t\t//and the lump data.\n\t\t\tVFS_WRITE(f, host_basepal, 256*3);\n\n\t\t\tentry->size = entry->dsize = VFS_TELL(f)-entry->offset;\n\t\t}\n\n\t\tfor (i = 0; i < list.numfiles; i++)\n\t\t{\n\t\t\tQ_snprintfz(file, sizeof(file), \"%s/%s\", list.file[i].rootpath, list.file[i].name);\n\t\t\tinname = list.file[i].name;\n\t\t\tif (list.file[i].baselen > 15)\n\t\t\t{\n\t\t\t\tCon_Printf(\"Path too long for wad - %s\\n\", inname);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tindata = FS_LoadMallocFile(file, &fsize);\n\t\t\tif (!indata)\n\t\t\t{\n\t\t\t\tCon_Printf(\"Unable to open %s\\n\", inname);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (wad2.num == maxentries)\n\t\t\t{\n\t\t\t\tmaxentries += 64;\n\t\t\t\twadentries = realloc(wadentries, sizeof(*wadentries)*maxentries);\n\t\t\t}\n\t\t\tentry = &wadentries[wad2.num];\n\t\t\tmemset(entry, 0, sizeof(*entry));\n\t\t\tQ_strncpyz(entry->name, inname, 16);\n\t\t\tif (list.file[i].baselen < sizeof(entry->name))\n\t\t\t\tentry->name[list.file[i].baselen] = 0; //kill any .tga\n\t\t\tif (*entry->name == '#')\n\t\t\t\t*entry->name = '*';\t//* is not valid in a filename, yet needed for turbs, so by convention # is used instead. this is only relevant for the first char.\n\t\t\tentry->type = TYP_MIPTEX;\n\t\t\tentry->offset = VFS_TELL(f);\n\n\t\t\tif (!strcasecmp(COM_GetFileExtension(file, NULL), \".mip\"))\n\t\t\t{\t//.mip files can just be loaded directly\n\t\t\t\t//I just hope they are actually q1 format and not hl, for instance.\n\t\t\t\tif (wadtype == 3)\n\t\t\t\t{\n\t\t\t\t\tCon_Printf(\"refusing to inject q1 miptex into halflife wad-3 file\\n\");\n\t\t\t\t\tZ_Free(indata);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tVFS_WRITE(f, indata, fsize);\n\t\t\t\twad2.num++;\n\t\t\t\tentry->size = entry->dsize = VFS_TELL(f)-entry->offset;\n\t\t\t\tZ_Free(indata);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (wadtype == 3)\n\t\t\t\t{\n\t\t\t\t\tfor (u = 0; u < sizeof(entry->name); u++)\n\t\t\t\t\t\tentry->name[u] = toupper(entry->name[u]);\n\t\t\t\t\tentry->type = 67;\t//halflife's mips actually use a different type from q1 ones.\n\t\t\t\t}\n\n\t\t\t\t//try to decompress everything to a nice friendly palletizable range.\n\t\t\t\tfor (u = 1; u < countof(sh_config.texfmt); u++)\n\t\t\t\t\tsh_config.texfmt[u] = (u==PTI_RGBA8)||(u==PTI_RGBX8)||(u==PTI_P8)||(u==args->newpixelformat);\n\t\t\t\tin = Image_LoadMipsFromMemory(args->flags, inname, file, indata, fsize);\n\n\t\t\t\tif (qpics)\n\t\t\t\t{\n\t\t\t\t\tqboolean mippixelformats[PTI_MAX];\n\t\t\t\t\tmemset(mippixelformats, 0, sizeof(mippixelformats));\n\n\t\t\t\t\tif (!strcasecmp(entry->name, \"CONCHARS\") && in->mip[0].width==128&&in->mip[0].height==128)\n\t\t\t\t\t{\n\t\t\t\t\t\tmippixelformats[TF_H2_TRANS8_0] = true;\n\n\t\t\t\t\t\tentry->type = TYP_MIPTEX;\t//yes, weird. match vanilla quake. explicitly avoid qpic to avoid corruption in the first 8 bytes (due to the engine's early endian swapping)\n\t\t\t\t\t\t\t\t\t\t\t\t//FIXME: encoding should be pti_trans8_0...\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tmippixelformats[TF_TRANS8] = true;\n\t\t\t\t\t\tentry->type = TYP_QPIC;\n\t\t\t\t\t\t//qpics need a header\n\t\t\t\t\t\tVFS_WRITE(f, &in->mip[0].width, sizeof(int));\n\t\t\t\t\t\tVFS_WRITE(f, &in->mip[0].height, sizeof(int));\n\t\t\t\t\t}\n\t\t\t\t\tif (!mippixelformats[in->encoding])\n\t\t\t\t\t\tImage_ChangeFormat(in, mippixelformats, PTI_INVALID, entry->name);\n\t\t\t\t\tif (!mippixelformats[in->encoding])\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t//and now the 8bit pixel data itself\n\t\t\t\t\tVFS_WRITE(f, in->mip[0].data, in->mip[0].datasize);\n\t\t\t\t}\n\t\t\t\telse if (!ImgTool_MipExport(args, f, in, entry->name, wadtype))\n\t\t\t\t\tcontinue;\n\n\t\t\t\twad2.num++;\n\t\t\t\tentry->size = entry->dsize = VFS_TELL(f)-entry->offset;\n\t\t\t}\n\t\t}\n\t\twad2.offset = VFS_TELL(f);\n\t\tVFS_WRITE(f, wadentries, sizeof(*wadentries)*wad2.num);\n\t\tVFS_SEEK(f, 0);\n\t\tVFS_WRITE(f, &wad2, sizeof(wad2));\n\t\tVFS_CLOSE(f);\n\t}\n\n\tfree(wadentries);\n\tFileList_Release(&list);\n}\n\n#ifdef FTE_SDL\nstatic void SDLL_Loop(void);\nstatic void ImgTool_View(const char *inname, struct pendingtextureinfo *in);\n#endif\nint main(int argc, const char **argv)\n{\n\tstatic const struct\n\t{\n\t\tconst char *alias;\n\t\tuploadfmt_t fmt;\n\t} fmtaliases[] = {\n\t\t{\"BC1\", PTI_BC1_RGBA},\n\t\t{\"BC2\", PTI_BC2_RGBA},\n\t\t{\"BC3\", PTI_BC3_RGBA},\n\t\t{\"BC4\", PTI_BC4_R},\n\t\t{\"BC5\", PTI_BC5_RG},\n\t\t{\"BC6\", PTI_BC6_RGB_UFLOAT},\n\t\t{\"BC7\", PTI_BC7_RGBA},\n\t\t{\"ETC1\", PTI_ETC1_RGB8},\n\t\t{\"ETC2\", PTI_ETC2_RGB8},\n\t\t{\"ETCP\", PTI_ETC2_RGB8A1},\n\t\t{\"ETCA\", PTI_ETC2_RGB8A8},\n\t\t{\"ASTC4x4\",\tPTI_ASTC_4X4_LDR},\n\t\t{\"ASTC5x4\", PTI_ASTC_5X4_LDR},\n\t\t{\"ASTC5x5\",\tPTI_ASTC_5X5_LDR},\n\t\t{\"ASTC6x5\", PTI_ASTC_6X5_LDR},\n\t\t{\"ASTC6x6\",\tPTI_ASTC_6X6_LDR},\n\t\t{\"ASTC8x5\", PTI_ASTC_8X5_LDR},\n\t\t{\"ASTC8x6\", PTI_ASTC_8X6_LDR},\n\t\t{\"ASTC10x5\",PTI_ASTC_10X5_LDR},\n\t\t{\"ASTC10x6\",PTI_ASTC_10X6_LDR},\n\t\t{\"ASTC8x8\",\tPTI_ASTC_8X8_LDR},\n\t\t{\"ASTC10x8\",PTI_ASTC_10X8_LDR},\n\t\t{\"ASTC10x10\",PTI_ASTC_10X10_LDR},\n\t\t{\"ASTC12x10\",PTI_ASTC_12X10_LDR},\n\t\t{\"ASTC12x12\",PTI_ASTC_12X12_LDR},\n\t\t{\"LUM8\", PTI_L8},\n\t\t{\"RGBA\", PTI_RGBA8},\n\t\t{\"RGB\", PTI_RGB8},\n\t};\n\tenum\n\t{\n\t\tmode_unspecified,\n\t\tmode_info,\n#ifdef FTE_SDL\n\t\tmode_view,\n#endif\n\t\tmode_convert,\n\t\tmode_autotree,\n\t\tmode_genwadx,\n\t\tmode_genwad2,\n\t\tmode_genwad3,\n\t\tmode_extractwad,\n\t} mode = mode_unspecified;\n\tsize_t u, f;\n\tqboolean nomoreopts = false;\n\tstruct opts_s args;\n\tsize_t files = 0;\n\tconst char *outname = NULL;\n\n\tfor (u = 1; u < countof(sh_config.texfmt); u++)\n\t\tsh_config.texfmt[u] = true;\n\n\targs.flags = 0;\n\targs.newpixelformat = PTI_INVALID;\n\targs.mipnum = 0;\n\targs.textype = PTI_ANY;\n\targs.defaultext = NULL;\n\targs.width = args.height = 0;\n\n\tif (argc==1)\n\t\tgoto showhelp;\n\n\tfor (u = 1; u < argc; u++)\n\t{\n\t\tif (*argv[u] == '-' && !nomoreopts)\n\t\t{\n\t\t\tif (!strcmp(argv[u], \"--\"))\n\t\t\t\tnomoreopts = true;\n\t\t\telse if (!strcmp(argv[u], \"-?\") || !strcmp(argv[u], \"--help\"))\n\t\t\t{\nshowhelp:\n\t\t\t\tCon_Printf(DISTRIBUTION \" Image Tool\\n\");\n\t\t\t\tCon_Printf(\"show info  : %s [-i] in.ktx [in2.ext ...]\\n\", argv[0]);\n\t\t\t\tCon_Printf(\"compress   : %s [-c] --ext ktx --astc_6x6_ldr [--nomips] in.png [in2.png ...]\\n\", argv[0]);\n\t\t\t\tCon_Printf(\"compress   : %s [-c] --ext dds --bc3 [--premul] [--nomips] in.png\\n\\tConvert pixel format (to bc3 aka dxt5) before writing to output file.\\n\", argv[0]);\n\t\t\t\tCon_Printf(\"convert    : %s [-c] --ext png in.exr [in2.pcx ...]\\n\\tConvert input file(s) to different file format, while trying to preserve pixel formats.\\n\", argv[0]);\n\t\t\t\tCon_Printf(\"merge      : %s -o output [--cube|--3d|--2darray|--cubearray] [--bc1] foo_*.png\\n\\tConvert to different file format, while trying to preserve pixel formats.\\n\", argv[0]);\n\t\t\t\tCon_Printf(\"recursive  : %s -r [--ext dds] --astc_6x6_ldr destdir srcdir\\n\\tCompresses the files to dds (writing to an optionally different directory)\\n\", argv[0]);\n\t\t\t\tCon_Printf(\"decompress : %s --decompress [--exportmip 0] [--nomips] in.ktx out.png\\n\\tDecompresses any block-compressed pixel data.\\n\", argv[0]);\n\t\t\t\tCon_Printf(\"create mips: %s [-c] --ext mip [--bc1] [--resize width height] [--exportmip 2] *.dds\\n\", argv[0]);\n\t\t\t\tCon_Printf(\"create xwad: %s --genwadx [--exportmip 2] [--bc1] out.wad srcdir\\n\", argv[0]);\n\t\t\t\tCon_Printf(\"create wad : %s -w [--exportmip 2] out.wad *.mipsrcdir\\n\", argv[0]);\n\t\t\t\tCon_Printf(\"extract wad: %s -x [--ext png] src.wad\\n\", argv[0]);\n\t\t\t\tCon_Printf(\"extract bsp: %s -x [--ext png] src.bsp\\n\", argv[0]);\n\n\t\t\t\tImage_PrintInputFormatVersions();\n\t\t\t\tCon_Printf(\"Supported compressed/interesting pixelformats are:\\n\");\n\t\t\t\tfor (f = 0; f < PTI_MAX; f++)\n\t\t\t\t{\n\t\t\t\t\tint bb,bw,bh,bd;\n\t\t\t\t\tImage_BlockSizeForEncoding(f, &bb,&bw,&bh,&bd);\n\t\t\t\t\tif (f >= PTI_ASTC_FIRST && f <= PTI_ASTC_LAST)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (f >= PTI_ASTC_4X4_SRGB)\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\tCon_Printf(\" --%-16s %5.3g-bpp (requires astcenc)\\n\", Image_FormatName(f), 8*(float)bb/(bw*bh*bd));\n\t\t\t\t\t}\n\t\t\t\t\telse if (f==PTI_BC1_RGB||f==PTI_BC1_RGBA||f==PTI_BC2_RGBA||f==PTI_BC3_RGBA||f==PTI_BC4_R||f==PTI_BC5_RG)\n\t\t\t\t\t\tCon_Printf(\" --%-16s %5.3g-bpp (requires nvcompress)\\n\", Image_FormatName(f), 8*(float)bb/(bw*bh*bd));\n\t\t\t\t\telse if (f==PTI_BC6_RGB_UFLOAT || f==PTI_BC6_RGB_SFLOAT || f==PTI_BC7_RGBA)\n\t\t\t\t\t\tCon_Printf(\" --%-16s %5.3g-bpp (requires nvcompress 2.1+)\\n\", Image_FormatName(f), 8*(float)bb/(bw*bh*bd));\n\t\t\t\t\telse if (\tf==PTI_RGBA16F ||\n\t\t\t\t\t\t\t\tf==PTI_RGBA32F ||\n\t\t\t\t\t\t\t\tf==PTI_E5BGR9 ||\n\t\t\t\t\t\t\t\tf==PTI_B10G11R11F ||\n\t\t\t\t\t\t\t\tf==PTI_RGB565 ||\n\t\t\t\t\t\t\t\tf==PTI_RGBA4444 ||\n\t\t\t\t\t\t\t\tf==PTI_ARGB4444 ||\n\t\t\t\t\t\t\t\tf==PTI_RGBA5551 ||\n\t\t\t\t\t\t\t\tf==PTI_ARGB1555 ||\n\t\t\t\t\t\t\t\tf==PTI_A2BGR10 ||\n//\t\t\t\t\t\t\t\tf==PTI_R8 ||\n//\t\t\t\t\t\t\t\tf==PTI_R16 ||\n//\t\t\t\t\t\t\t\tf==PTI_R16F ||\n//\t\t\t\t\t\t\t\tf==PTI_R32F ||\n\t\t\t\t\t\t\t\tf==PTI_RG8 ||\n\t\t\t\t\t\t\t\tf==PTI_L8 ||\n\t\t\t\t\t\t\t\tf==PTI_L8A8 ||\n\t\t\t\t\t\t\t0)\n\t\t\t\t\t\tCon_Printf(\" --%-16s %5.3g-bpp\\n\", Image_FormatName(f), 8*(float)bb/(bw*bh*bd));\n//\t\t\t\t\telse\n//\t\t\t\t\t\tCon_DPrintf(\" --%-16s %5.3g-bpp (unsupported)\\n\", Image_FormatName(f), 8*(float)bb/(bw*bh*bd));\n\t\t\t\t}\n\t\t\t\treturn EXIT_SUCCESS;\n\t\t\t}\n\t\t\telse if (!files && (!strcmp(argv[u], \"-c\") || !strcmp(argv[u], \"--convert\")))\n\t\t\t\tmode = mode_convert;\n\t\t\telse if (!files && (!strcmp(argv[u], \"-d\") || !strcmp(argv[u], \"--decompress\")))\n\t\t\t{\t//remove any (weird) gpu formats\n\t\t\t\tfor (f = PTI_BC1_RGB; f < PTI_ASTC_LAST; f++)\n\t\t\t\t\tsh_config.texfmt[f] = false;\n\t\t\t\tmode = mode_convert;\n\t\t\t}\n\t\t\telse if (!files && (!strcmp(argv[u], \"-r\") || !strcmp(argv[u], \"--auto\")))\n\t\t\t\tmode = mode_autotree;\n\t\t\telse if (!files && (!strcmp(argv[u], \"-i\") || !strcmp(argv[u], \"--info\")))\n\t\t\t\tmode = mode_info;\n#ifdef FTE_SDL\n\t\t\telse if (!files && (!strcmp(argv[u], \"-v\") || !strcmp(argv[u], \"--view\")))\n\t\t\t\tmode = mode_view;\n#endif\n\t\t\telse if (!files && (!strcmp(argv[u], \"-w\") || !strcmp(argv[u], \"--genwad2\")))\n\t\t\t\tmode = mode_genwad2;\n\t\t\telse if (!files && (!strcmp(argv[u], \"-w\") || !strcmp(argv[u], \"--genwad3\")))\n\t\t\t\tmode = mode_genwad3;\n\t\t\telse if (!files && (!strcmp(argv[u], \"-w\") || !strcmp(argv[u], \"--genwadx\")))\n\t\t\t\tmode = mode_genwadx;\n\t\t\telse if (!files && (!strcmp(argv[u], \"-x\") || !strcmp(argv[u], \"--extractwad\")))\n\t\t\t\tmode = mode_extractwad;\n\t\t\telse if (!files && (!strcmp(argv[u], \"-v\") || !strcmp(argv[u], \"--verbose\")))\n\t\t\t\tverbose = true;\n\t\t\telse if (!files && (!strcmp(argv[u], \"-o\") || !strcmp(argv[u], \"--outfile\")))\n\t\t\t{\n\t\t\t\tif (u+1 < argc)\n\t\t\t\t\toutname = argv[++u];\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tCon_Printf(\"--outfile requires output filename\\n\");\n\t\t\t\t\treturn 1;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (!files && (!strcmp(argv[u], \"-p\") || !strcmp(argv[u], \"--palette\")))\n\t\t\t{\n\t\t\t\tif (u+1 < argc)\n\t\t\t\t\tpalette = argv[++u];\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tCon_Printf(\"--palette requires palette filename\\n\");\n\t\t\t\t\treturn 1;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (!strcmp(argv[u], \"--resize\"))\n\t\t\t{\n\t\t\t\tif (u+2 < argc)\n\t\t\t\t{\n\t\t\t\t\targs.width = atoi(argv[++u]);\n\t\t\t\t\targs.height = atoi(argv[++u]);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tCon_Printf(\"--resize requires width+height values\\n\");\n\t\t\t\t\treturn 1;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (!strcmp(argv[u], \"--2d\"))\n\t\t\t\targs.textype = PTI_2D;\n\t\t\telse if (!strcmp(argv[u], \"--3d\"))\n\t\t\t\targs.textype = PTI_3D;\n\t\t\telse if (!strcmp(argv[u], \"--cube\"))\n\t\t\t\targs.textype = PTI_CUBE;\n\t\t\telse if (!strcmp(argv[u], \"--2darray\"))\n\t\t\t\targs.textype = PTI_2D_ARRAY;\n\t\t\telse if (!strcmp(argv[u], \"--cubearray\"))\n\t\t\t\targs.textype = PTI_CUBE_ARRAY;\n\t\t\telse if (!strcmp(argv[u], \"--nomips\")\t)\n\t\t\t\targs.flags |= IF_NOMIPMAP;\n\t\t\telse if (!strcmp(argv[u], \"--mips\"))\n\t\t\t\targs.flags &= ~IF_NOMIPMAP;\n\t\t\telse if (!strcmp(argv[u], \"--premul\")\t)\n\t\t\t\targs.flags |= IF_PREMULTIPLYALPHA;\n\t\t\telse if (!strcmp(argv[u], \"--nopremul\"))\n\t\t\t\targs.flags &= ~IF_PREMULTIPLYALPHA;\n\t\t\telse if (!strcmp(argv[u], \"--ext\"))\n\t\t\t{\n\t\t\t\tif (mode == mode_unspecified)\n\t\t\t\t\tmode = mode_convert;\n\t\t\t\tif (u+1 < argc)\n\t\t\t\t\targs.defaultext = argv[++u];\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tCon_Printf(\"--ext requires output extension\\n\");\n\t\t\t\t\treturn 1;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (!strcmp(argv[u], \"--exportmip\"))\n\t\t\t{\n\t\t\t\tchar *e = \"erk\";\n\t\t\t\tif (u+1 < argc)\n\t\t\t\t\targs.mipnum = strtoul(argv[++u], &e, 10);\n\t\t\t\tif (*e)\n\t\t\t\t{\n\t\t\t\t\tCon_Printf(\"--exportmip requires trailing numeric argument\\n\");\n\t\t\t\t\treturn 1;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (argv[u][1] == '-')\n\t\t\t\t{\n\t\t\t\t\t//try aliases first.\n\t\t\t\t\tfor (f = 0; f < countof(fmtaliases); f++)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (!strcasecmp(argv[u]+2, fmtaliases[f].alias))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\targs.newpixelformat = fmtaliases[f].fmt;\n\t\t\t\t\t\t\tif (mode == mode_unspecified)\n\t\t\t\t\t\t\t\tmode = mode_convert;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif (f < countof(fmtaliases))\n\t\t\t\t\t\tcontinue;\n\n\t\t\t\t\t//now try our formal format names\n\t\t\t\t\tfor (f = 0; f < PTI_MAX; f++)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (!strcasecmp(argv[u]+2, Image_FormatName(f)))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\targs.newpixelformat = f;\n\t\t\t\t\t\t\tif (mode == mode_unspecified)\n\t\t\t\t\t\t\t\tmode = mode_convert;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif (f < PTI_MAX)\n\t\t\t\t\t\tcontinue;\n\n\t\t\t\t\t//nope, not a format name\n\t\t\t\t}\n\t\t\t\tCon_Printf(\"Unknown arg %s\\n\", argv[u]);\n\t\t\t\tgoto showhelp;\n\t\t\t}\n\t\t\targv[u] = NULL;\n\t\t}\n\t\telse\n\t\t\targv[files++] = argv[u];\n\t}\n\n\tImgTool_SetupPalette();\n\n\tif (mode == mode_unspecified && args.textype!=PTI_ANY)\n\t\tmode = mode_convert;\n\n\tif (!args.defaultext)\n\t{\n\t\tif (mode == mode_unspecified)\n\t\t{\n#ifdef FTE_SDL\n\t\t\tmode = mode_view;\n#else\n\t\t\tmode = mode_info;\n#endif\n\t\t}\n\n\t\tif (mode == mode_extractwad)\n\t\t\targs.defaultext = \"png\";\t//something the user expects to be able to view easily (and lossless)\n\t\telse if (args.newpixelformat >= PTI_BC1_RGB && args.newpixelformat < PTI_BC4_R)\n\t\t\targs.defaultext = \"dds\";\n\t\telse\n\t\t\targs.defaultext = \"ktx\";\n\t}\n\telse\n\t{\n\t\tif (mode == mode_unspecified)\n\t\t\tmode = mode_convert;\n\t}\n\n\tif (mode == mode_info)\n\t{\t//just print info about each listed file.\n\t\tfor (u = 0; u < files; u++)\n\t\t\tImgTool_Enumerate(&args, argv[u], ImgTool_PrintInfo);\n\t}\n\telse if (mode == mode_convert && args.textype!=PTI_ANY && outname)\t//overwrite input\n\t{\n\t\tImgTool_Convert(&args, ImgTool_Combine(&args, argv, files), \"combined\", outname);\n\t}\n\telse if (mode == mode_convert && args.textype==PTI_ANY && (!outname||files==1))\t//list of files (output filenames will be generated according to -ext arg)\n\t{\n\t\t//-c src1 src2 src3\n\t\tfor (u = 0; u < files; u++)\n\t\t\tImgTool_Convert(&args, ImgTool_Read(&args, argv[u]), argv[u], NULL);\n\t}\n\telse if (mode == mode_autotree && files == 2)\n\t\tImgTool_TreeConvert(&args, argv[0], argv[1]);\n\telse if ((mode == mode_genwad2 || mode == mode_genwad3 || mode == mode_genwadx))\n\t\tImgTool_WadConvert(&args, argv[0], argv+1, files-1, mode-(mode_genwadx-1));\n\telse if ((mode == mode_extractwad) && files == 1)\n\t\tImgTool_WadExtract(&args, argv[0]);\n#ifdef FTE_SDL\n\telse if (mode == mode_view)\n\t{\n\t\tfor (u = 0; u < files; u++)\n\t\t\tImgTool_Enumerate(&args, argv[u], ImgTool_View);\n\t\tSDLL_Loop();\n\t}\n#endif\n\telse\n\t{\n\t\tprintf(\"%u files\\n\", (int)files);\n\t\tprintf(\"unsupported arg count for mode\\n\");\n\t\treturn EXIT_FAILURE;\n\t}\n\treturn EXIT_SUCCESS;\n}\n\n#ifdef FTE_SDL\n#include <SDL.h>\n\nstruct sdlwindow_s\n{\n\tSDL_Window *w;\n\tSDL_Renderer *r;\n\tint width;\n\tint height;\n\tfloat scale;\n\n\tsize_t texshown;\n\tsize_t texcount;\n\tstruct\n\t{\n\t\tchar *name;\n\t\tsize_t w, h;\n\t\tuploadfmt_t fmt;\n\t\tSDL_Texture *t;\n\t} *tex;\n};\nstatic struct\n{\n\tqboolean inited;\n\tqboolean tried;\n\tsize_t windowcount;\n\n\tint\t\t\t\t(SDLCALL *Init)\t\t\t\t(Uint32 flags);\n\tSDL_Window *\t(SDLCALL *CreateWindow)\t\t(const char *title, int x, int y, int w, int h, Uint32 flags);\n\tvoid *\t\t\t(SDLCALL *SetWindowData)\t(SDL_Window * window, const char *name, void *userdata);\n\tvoid *\t\t\t(SDLCALL *GetWindowData)\t(SDL_Window * window, const char *name);\n\tvoid\t\t\t(SDLCALL *SetWindowTitle)\t(SDL_Window * window, const char *title);\n\tvoid\t\t\t(SDLCALL *SetWindowSize)\t(SDL_Window * window, int w, int h);\n\tUint32\t\t\t(SDLCALL *GetWindowFlags)\t(SDL_Window * window);\n\tSDL_Renderer *\t(SDLCALL *CreateRenderer)\t(SDL_Window * window, int index, Uint32 flags);\n\tint\t\t\t\t(SDLCALL *GetRendererInfo)\t(SDL_Renderer * renderer, SDL_RendererInfo * info);\n\tSDL_Texture *\t(SDLCALL *CreateTexture)\t(SDL_Renderer * renderer, Uint32 format, int access, int w, int h);\n\tint\t\t\t\t(SDLCALL *UpdateTexture)\t(SDL_Texture * texture, const SDL_Rect * rect, const void *pixels, int pitch);\n\tint\t\t\t\t(SDLCALL *RenderCopy)\t\t(SDL_Renderer * renderer, SDL_Texture * texture, const SDL_Rect * srcrect, const SDL_Rect * dstrect);\n\tvoid\t\t\t(SDLCALL *RenderPresent)\t(SDL_Renderer * renderer);\n\tint\t\t\t\t(SDLCALL *WaitEvent)\t\t(SDL_Event * event);\n\tint\t\t\t\t(SDLCALL *PollEvent)\t\t(SDL_Event * event);\n\tSDL_Window *\t(SDLCALL *GetWindowFromID)\t(Uint32 id);\n\tvoid\t\t\t(SDLCALL *DestroyTexture)\t(SDL_Texture * texture);\n\tvoid\t\t\t(SDLCALL *DestroyRenderer)\t(SDL_Renderer * renderer);\n\tvoid\t\t\t(SDLCALL *DestroyWindow)\t(SDL_Window * window);\n\tvoid\t\t\t(SDLCALL *Quit)\t\t\t\t(void);\n\n\tstruct sdlwindow_s *texview;\n} sdl;\nstatic qboolean SDLL_Setup(void)\n{\n\tstatic dllfunction_t funcs[] =\n\t{\n\t\t{(void**)&sdl.Init,\t\t\t\t\"SDL_Init\"},\n\t\t{(void**)&sdl.CreateWindow,\t\t\"SDL_CreateWindow\"},\n\t\t{(void**)&sdl.SetWindowData,\t\"SDL_SetWindowData\"},\n\t\t{(void**)&sdl.GetWindowData,\t\"SDL_GetWindowData\"},\n\t\t{(void**)&sdl.SetWindowTitle,\t\"SDL_SetWindowTitle\"},\n\t\t{(void**)&sdl.SetWindowSize,\t\"SDL_SetWindowSize\"},\n\t\t{(void**)&sdl.GetWindowFlags,\t\"SDL_GetWindowFlags\"},\n\t\t{(void**)&sdl.CreateRenderer,\t\"SDL_CreateRenderer\"},\n\t\t{(void**)&sdl.GetRendererInfo,\t\"SDL_GetRendererInfo\"},\n\t\t{(void**)&sdl.CreateTexture,\t\"SDL_CreateTexture\"},\n\t\t{(void**)&sdl.UpdateTexture,\t\"SDL_UpdateTexture\"},\n\t\t{(void**)&sdl.RenderCopy,\t\t\"SDL_RenderCopy\"},\n\t\t{(void**)&sdl.RenderPresent,\t\"SDL_RenderPresent\"},\n\t\t{(void**)&sdl.WaitEvent,\t\t\"SDL_WaitEvent\"},\n\t\t{(void**)&sdl.PollEvent,\t\t\"SDL_PollEvent\"},\n\t\t{(void**)&sdl.GetWindowFromID,\t\"SDL_GetWindowFromID\"},\n\t\t{(void**)&sdl.DestroyTexture,\t\"SDL_DestroyTexture\"},\n\t\t{(void**)&sdl.DestroyRenderer,\t\"SDL_DestroyRenderer\"},\n\t\t{(void**)&sdl.DestroyWindow,\t\"SDL_DestroyWindow\"},\n\t\t{(void**)&sdl.Quit,\t\t\t\t\"SDL_Quit\"},\n\t\t{NULL,NULL}\n\t};\n\n\tif (!sdl.tried)\n\t{\n\t\tsdl.tried = true;\n\n#ifdef _WIN32\n\t\tif (!Sys_LoadLibrary(\"SDL2.dll\", funcs))\n#else\n\t\tif (!Sys_LoadLibrary(\"libSDL2-2.0.so.0\", funcs))\n\t\tif (!Sys_LoadLibrary(\"libSDL2-2.0.so\", funcs))\n\t\tif (!Sys_LoadLibrary(\"libSDL2-2.so\", funcs))\n\t\tif (!Sys_LoadLibrary(\"libSDL2.so\", funcs))\n\t\tif (!Sys_LoadLibrary(\"SDL2.so\", funcs))\n#endif\n\t\t{\n\t\t\tprintf(\"Unable to load SDL2 library\\n\");\n\t\t\treturn sdl.inited;\n\t\t}\n\n\t\tsdl.Init(SDL_INIT_VIDEO);\n\t\tsdl.inited = true;\n\t}\n\treturn sdl.inited;\n}\nstatic void SDLL_KillWindow(struct sdlwindow_s *wc)\n{\n\twhile (wc->texcount --> 0)\n\t{\n\t\tif (wc->tex[wc->texcount].t)\n\t\t\tsdl.DestroyTexture(wc->tex[wc->texcount].t);\n\t\tBZ_Free(wc->tex[wc->texcount].name);\n\t}\n\tBZ_Free(wc->tex);\n\tif (wc->r)\n\t\tsdl.DestroyRenderer(wc->r);\n\tif (wc->w)\n\t\tsdl.DestroyWindow(wc->w);\n\tZ_Free(wc);\n\tsdl.windowcount--;\n\tif (sdl.texview == wc)\n\t\tsdl.texview = NULL;\n}\nstatic void SDLL_RepaintWindow(struct sdlwindow_s *wc)\n{\n\tif (wc->texshown < wc->texcount)\n\t{\n\t\tSDL_Rect dest;\n\t\tdest.x=dest.y=0;\n\t\tdest.w=dest.h=1;\n\t\tsdl.RenderCopy(wc->r, wc->tex[wc->texshown].t, &dest, NULL);\n\n\t\tdest.w = wc->tex[wc->texshown].w*wc->scale;\n\t\tdest.h = wc->tex[wc->texshown].h*wc->scale;\n\t\tdest.x = (wc->width - dest.w)/2;\n\t\tdest.y = (wc->height - dest.h)/2;\n\t\tsdl.RenderCopy(wc->r, wc->tex[wc->texshown].t, NULL, &dest);\n\t\tsdl.RenderPresent(wc->r);\n\t}\n}\nstatic void SDLL_Change(struct sdlwindow_s *wc, size_t newshown)\n{\n\tif (newshown < wc->texcount)\n\t{\n\t\tint w, h;\n\t\tchar title[512];\n\t\twc->texshown = newshown;\n\t\tif (wc->texcount==1)\n\t\t\tsnprintf(title, sizeof(title), \"%s %s\", wc->tex[wc->texshown].name, Image_FormatName(wc->tex[wc->texshown].fmt));\n\t\telse\n\t\t\tsnprintf(title, sizeof(title), \"[%u/%u] %s %s\", 1+(unsigned int)newshown, (unsigned int)wc->texcount, wc->tex[wc->texshown].name, Image_FormatName(wc->tex[wc->texshown].fmt));\n\t\tsdl.SetWindowTitle(wc->w, title);\n\n\t\tw = wc->tex[wc->texshown].w * wc->scale;\n\t\th = wc->tex[wc->texshown].h * wc->scale;\n\t\tif (w > 1024)\n\t\t\tw = 1024;\n\t\tif (h > 768)\n\t\t\th = 768;\n\t\tif (wc->width < w || wc->height < h)\n\t\t\tif (!(sdl.GetWindowFlags(wc->w) & (SDL_WINDOW_MAXIMIZED|SDL_WINDOW_FULLSCREEN)))\t//SetWindowSize seems to bug out on linux when its maximized.\n\t\t\t{\n\t\t\t\twc->width = w;\n\t\t\t\twc->height = h;\n\t\t\t\tsdl.SetWindowSize(wc->w, w, h);\n\t\t\t}\n\t\tSDLL_RepaintWindow(wc);\n\t}\n}\nstatic void SDLL_Event(SDL_Event *ev)\n{\n\tstruct sdlwindow_s *wc;\n\tswitch (ev->type)\n\t{\n\tcase SDL_KEYDOWN:\n\t\twc = sdl.GetWindowData(sdl.GetWindowFromID(ev->key.windowID), \"uptr\");\n\t\tif (!wc) break;\n\t\tswitch(ev->key.keysym.sym)\n\t\t{\n\t\tcase SDLK_ESCAPE:\n\t\tcase SDLK_q:\n\t\t\tSDLL_KillWindow(wc);\n\t\t\tbreak;\n\t\tcase SDLK_LEFT:\n\t\t\twc->scale = 1;\n\t\t\tSDLL_Change(wc, wc->texshown-1);\n\t\t\tbreak;\n\t\tcase SDLK_RIGHT:\n\t\t\twc->scale = 1;\n\t\t\tSDLL_Change(wc, wc->texshown+1);\n\t\t\tbreak;\n\t\tcase SDLK_UP:\n\t\t\twc->scale /= 0.9;\n\t\t\tif (wc->scale > 4)\n\t\t\t\twc->scale = 4;\n\t\t\tSDLL_Change(wc, wc->texshown);\n\t\t\tbreak;\n\t\tcase SDLK_DOWN:\n\t\t\twc->scale *= 0.9;\n\t\t\tif (wc->scale < 0.25)\n\t\t\t\twc->scale = 0.25;\n\t\t\tSDLL_Change(wc, wc->texshown);\n\t\t\tbreak;\n\t\t}\n\t\tbreak;\n\tcase SDL_WINDOWEVENT:\n\t\twc = sdl.GetWindowData(sdl.GetWindowFromID(ev->window.windowID), \"uptr\");\n\t\tif (!wc) break;\n\t\tswitch (ev->window.event)\n\t\t{\n\t\tcase SDL_WINDOWEVENT_CLOSE:\n\t\t\tSDLL_KillWindow(wc);\n\t\t\tbreak;\n\t\tcase SDL_WINDOWEVENT_SIZE_CHANGED:\n\t\t\twc->width = ev->window.data1;\n\t\t\twc->height = ev->window.data2;\n\t\t\tbreak;\n\t\tcase SDL_WINDOWEVENT_EXPOSED:\n\t\t\tSDLL_RepaintWindow(wc);\n\t\t\tbreak;\n\t\t}\n\t\tbreak;\n\t//don't bother with SDL_QUIT, it doesn't get sent if we kill the last window via a keypress. just count live windows instead.\n\tdefault:\n//\t\tprintf(\"event type %x\\n\", ev->type);\n\t\tbreak;\n\t}\n}\nstatic void SDLL_Loop(void)\n{\n\tSDL_Event ev;\n\tif (!sdl.inited)\n\t\treturn;\n\n\tif (sdl.texview)\n\t\tSDLL_Change(sdl.texview, sdl.texview->texshown);\n\n\twhile (sdl.windowcount && sdl.WaitEvent(&ev))\n\t\tSDLL_Event(&ev);\n\n\tsdl.Quit();\n}\nstatic void ImgTool_View(const char *inname, struct pendingtextureinfo *in)\n{\n\tunsigned int sdlfmt;\n\tSDL_RendererInfo rinfo;\n\tint s;\n\tqboolean outformats[PTI_MAX] = {false};\n\tstruct sdlwindow_s *wc;\n\tSDL_Event ev;\n\tuploadfmt_t origencoding;\n\n\tif (!in || in->mipcount < 1 || in->mip[0].width <= 0 || in->mip[0].height <= 0)\n\t\treturn;\n\torigencoding = in->encoding;\n\n\tif (!SDLL_Setup())\n\t{\n\t\tImgTool_PrintInfo(inname, in);\n\t\treturn;\n\t}\n\n\twhile (sdl.windowcount>=64 && sdl.WaitEvent(&ev))\n\t\tSDLL_Event(&ev);\n\n\tif (sdl.texview)\n\t\twc = sdl.texview;\n\telse\n\t{\n\t\tsdl.texview = wc = Z_Malloc(sizeof(*wc));\n\t\twc->scale = 2;\n\n\t\ts = 1;\n\t\twhile (\t (in->mip[0].width*s < 256 && in->mip[0].height*s < 512)||\n\t\t\t\t (in->mip[0].height*s < 256 && in->mip[0].width*s < 512))\n\t\t\ts<<=1;\n\t\twc->w = sdl.CreateWindow(\"textureview\", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 64, 64, SDL_WINDOW_RESIZABLE);\n\t\tsdl.windowcount++;\n\t\tif (wc->w)\n\t\t{\n\t\t\tsdl.SetWindowData(wc->w, \"uptr\", wc);\n\n\t\t\t//needs a rendering context too\n\t\t\twc->r = sdl.CreateRenderer(wc->w, -1, SDL_RENDERER_SOFTWARE);\n\t\t\tif (!wc->r)\n\t\t\t{\n\t\t\t\tprintf(\"Unable to create rendering context\\n\");\n\t\t\t\tSDLL_KillWindow(wc);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n\tif (wc->r)\n\t{\n\t\t//figure out which formats we can pass to sdl\n\t\tsdl.GetRendererInfo(wc->r, &rinfo);\n\t\twhile (rinfo.num_texture_formats --> 0)\n\t\t{\n\t\t\tswitch(rinfo.texture_formats[rinfo.num_texture_formats])\n\t\t\t{\n\t\t\t//packed formats use hex ordering in both apis.\n\t\t\tcase SDL_PIXELFORMAT_RGB565:\t\toutformats[PTI_RGB565] = true;\tbreak;\n//\t\t\tcase SDL_PIXELFORMAT_BGR565:\t\toutformats[PTI_BGR565] = true;\tbreak;\n\t\t\tcase SDL_PIXELFORMAT_RGBA4444:\t\toutformats[PTI_RGBA4444] = true;\tbreak;\n\t\t\tcase SDL_PIXELFORMAT_ABGR4444:\t\toutformats[PTI_ARGB4444] = true;\tbreak;\n\t\t\tcase SDL_PIXELFORMAT_RGBA5551:\t\toutformats[PTI_RGBA5551] = true;\tbreak;\n\t\t\tcase SDL_PIXELFORMAT_ARGB1555:\t\toutformats[PTI_ARGB1555] = true;\tbreak;\n//\t\t\tcase SDL_PIXELFORMAT_ARGB2101010:\toutformats[PTI_A2RGB10] = true;\tbreak;\n//\t\t\tcase SDL_PIXELFORMAT_ABGR2101010:\toutformats[PTI_A2BGR10] = true;\tbreak;\n\n\t\t\t//these sdl aliases are for explicit byte orders, rather than packed.\n\t\t\tcase SDL_PIXELFORMAT_RGBA32:\t\toutformats[PTI_RGBA8] = true;\tbreak;\n\t\t\tcase SDL_PIXELFORMAT_BGRA32:\t\toutformats[PTI_BGRA8] = true;\tbreak;\n\t\t\tcase SDL_PIXELFORMAT_BGR24:\t\t\toutformats[PTI_RGB8] = true;\tbreak;\n\t\t\tcase SDL_PIXELFORMAT_RGB24:\t\t\toutformats[PTI_BGR8] = true;\tbreak;\n//\t\t\tcase SDL_PIXELFORMAT_ARGB32:\t\toutformats[PTI_ARGB8] = true;\tbreak;\n//\t\t\tcase SDL_PIXELFORMAT_ABGR32:\t\toutformats[PTI_ABGR8] = true;\tbreak;\n/*#if SDL_BYTEORDER == SDL_BIG_ENDIAN\n\t\t\tcase SDL_PIXELFORMAT_RGBX8888:\t\toutformats[PTI_RGBX8] = true;\tbreak;\n\t\t\tcase SDL_PIXELFORMAT_BGRX8888:\t\toutformats[PTI_BGRX8] = true;\tbreak;\n#else\n\t\t\tcase SDL_PIXELFORMAT_XBGR8888:\t\toutformats[PTI_RGBX8] = true;\tbreak;\n\t\t\tcase SDL_PIXELFORMAT_XRGB8888:\t\toutformats[PTI_BGRX8] = true;\tbreak;\n#endif*/\n\t\t\t}\n\t\t}\n\n\t\t//convert our image, if needed.\n\t\tif (!outformats[in->encoding])\n\t\t\tImage_ChangeFormat(in, outformats, PTI_INVALID, inname);\n\t\tif (!outformats[in->encoding])\n\t\t\tsdlfmt = SDL_PIXELFORMAT_UNKNOWN,printf(\"Unable to convert to usable pixel format\\n\");\n\t\telse switch(in->encoding)\n\t\t{\n\t\t//packed formats\n\t\tcase PTI_RGB565:\tsdlfmt = SDL_PIXELFORMAT_RGB565;\t\tbreak;\n//\t\tcase PTI_BGR565:\tsdlfmt = SDL_PIXELFORMAT_BGR565;\t\tbreak;\n\t\tcase PTI_RGBA4444:\tsdlfmt = SDL_PIXELFORMAT_RGBA4444;\t\tbreak;\n\t\tcase PTI_ARGB4444:\tsdlfmt = SDL_PIXELFORMAT_ARGB4444;\t\tbreak;\n\t\tcase PTI_RGBA5551:\tsdlfmt = SDL_PIXELFORMAT_RGBA5551;\t\tbreak;\n\t\tcase PTI_ARGB1555:\tsdlfmt = SDL_PIXELFORMAT_ARGB1555;\t\tbreak;\n//\t\tcase PTI_A2RGB10:\tsdlfmt = SDL_PIXELFORMAT_ARGB2101010;\tbreak;\n//\t\tcase PTI_A2BGR10:\tsdlfmt = SDL_PIXELFORMAT_ABGR2101010;\tbreak;\n\n\t\t//byte-ordered formats.\n\t\tcase PTI_RGBA8:\t\tsdlfmt = SDL_PIXELFORMAT_RGBA32;\t\tbreak;\n\t\tcase PTI_BGRA8:\t\tsdlfmt = SDL_PIXELFORMAT_BGRA32;\t\tbreak;\n\t\tcase PTI_RGB8:\t\tsdlfmt = SDL_PIXELFORMAT_RGB24;\t\t\tbreak;\n\t\tcase PTI_BGR8:\t\tsdlfmt = SDL_PIXELFORMAT_BGR24;\t\t\tbreak;\n/*#if SDL_BYTEORDER == SDL_BIG_ENDIAN\n\t\tcase PTI_RGBX8:\t\tsdlfmt = SDL_PIXELFORMAT_RGBX8888;\t\tbreak;\n\t\tcase PTI_BGRX8:\t\tsdlfmt = SDL_PIXELFORMAT_BGRX8888;\t\tbreak;\n#else\n\t\tcase PTI_RGBX8:\t\tsdlfmt = SDL_PIXELFORMAT_XBGR8888;\t\tbreak;\n\t\tcase PTI_BGRX8:\t\tsdlfmt = SDL_PIXELFORMAT_XRGB8888;\t\tbreak;\n#endif*/\n\n\t\tdefault:\t\t\tsdlfmt = SDL_PIXELFORMAT_UNKNOWN;\t\t break;\t//shouldn't happen.\n\t\t}\n\n\t\twc->tex = realloc(wc->tex, sizeof(*wc->tex)*(wc->texcount+1));\n\t\twc->tex[wc->texcount].name = Z_StrDup(inname);\n\t\twc->tex[wc->texcount].w = in->mip[0].width;\n\t\twc->tex[wc->texcount].h = in->mip[0].height;\n\t\twc->tex[wc->texcount].fmt = origencoding;\n\t\twc->tex[wc->texcount].t = sdl.CreateTexture(wc->r, sdlfmt, SDL_TEXTUREACCESS_STATIC, in->mip[0].width, in->mip[0].height);\t//which needs a texture...\n\t\tif (wc->tex[wc->texcount].t)\n\t\t{\n\t\t\tsdl.UpdateTexture(wc->tex[wc->texcount].t, NULL, in->mip[0].data, in->mip[0].datasize/in->mip[0].height);\t\t\t\t//with our image data\n\t\t\tif (!wc->texcount++)\n\t\t\t\tSDLL_Change(wc, 0);\n\n\t\t\twhile (sdl.PollEvent(&ev))\n\t\t\t\tSDLL_Event(&ev);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tprintf(\"Unable to create texture %s %u*%u\\n\", Image_FormatName(in->encoding), in->mip[0].width, in->mip[0].height);\n\t\t\tBZ_Free(wc->tex[wc->texcount].name);\n\t\t}\n\t}\n\telse\n\t{\n\t\tprintf(\"Unable to create window\\n\");\n\t\tSDLL_KillWindow(wc);\n\t}\n}\n#endif\n#endif\n"
  },
  {
    "path": "iqm/LICENSE",
    "content": "Copyright (c) 2010-2016 Lee Salzman\n\nPermission 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:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE 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.\n"
  },
  {
    "path": "iqm/Makefile",
    "content": "#Note: This makefile builds the iqm tool without any fte dependancies\n#      This means no .mdl export nor extended model format support (read: no gltf/glb import)\n\nCXXFLAGS= -O3 -fomit-frame-pointer\noverride CXXFLAGS+= -Wall -fsigned-char\n\nIQM_OBJS= \\\n\tiqm.o\nUPGRADE_OBJS= \\\n\tupgrade.o\n\ndefault: all\n\nall: iqmtool #upgrade\n\nclean:\n\t-$(RM) $(IQM_OBJS) $(UPGRADE_OBJS) iqmtool upgrade\n\niqmtool: $(IQM_OBJS)\n\t$(CXX) $(CXXFLAGS) -o iqmtool $(IQM_OBJS)\nupgrade: $(UPGRADE_OBJS)\n\t$(CXX) $(CXXFLAGS) -o upgrade $(UPGRADE_OBJS)\n\n%.o : %.cpp\n\t$(CXX) -o $@ -c $<\n"
  },
  {
    "path": "iqm/Makefile.mingw",
    "content": "CXXFLAGS= -O3 -fomit-frame-pointer\noverride CXXFLAGS+= -Wall -fsigned-char\n\nPLATFORM= $(shell uname -s)\nifeq (,$(findstring MINGW,$(PLATFORM)))\nCXX=i686-w64-mingw32-g++\nendif\n\nLIBS= -mwindows -static -static-libgcc -static-libstdc++\nIQM_OBJS= \\\n\tiqm.o\nUPGRADE_OBJS= \\\n\tupgrade.o\n\ndefault: all\n\nall: iqmtool.exe #upgrade.exe\n\nclean:\n\t-$(RM) $(IQM_OBJS) $(UPGRADE_OBJS) iqmtool.exe upgrade.exe\n\niqmtool.exe: $(IQM_OBJS)\n\t$(CXX) $(CXXFLAGS) -o iqmtool.exe $(IQM_OBJS) $(LIBS)\nupgrade.exe: $(UPGRADE_OBJS)\n\t$(CXX) $(CXXFLAGS) -o upgrade.exe $(UPGRADE_OBJS) $(LIBS)\n\n"
  },
  {
    "path": "iqm/README.txt",
    "content": "FTE's fork of Lee Salzman's commandline IQM exporter, based upon 'IQM Developer Kit 2015-08-03'.\nthere is no blender integration - export your files to a supported format first.\n\nMain changes:\nnow utilises command files instead of needing the weird commandline stuff (although that should still mostly work).\nadditional mesh properties and multiple mesh files, providing for proper hitmesh support, as well as some other things.\nmore verbose text output, additional shader prefixes.\nbone renaming+regrouping\nanimation unpacking, for qc mods that still insist on animating the lame way.\n\nSupported import formats:\n.md5mesh\n.md5anim\n.iqe\n.smd\n.fbx\n.obj\n\nUnless you're doing complex stuff like any of the above, there's probably not all that much difference. There may be some commandline behaviour differences.\n\n\nCommand File Format:\n\toutput <FILENAME> - specifies the output file name. you should only have one of each type of output.\n\toutput_qmdl <FILENAME> - specifies which file to write a quake1-format model. May only occur once.\n\toutput_md16 <FILENAME> - specifies the filename to write a quakeforge 16-bit md16 model to (a upgraded variation of quake's format). May only occur once.\n\toutput_md3 <FILENAME> - specifies the filename to write a quake3 md3 file to. May only occur once.\n\texec <FILENAME> - exec the specified command file, before parsing the rest of the current file.\n\thitbox <BODY NUM> <BONE NAME> <MIN POS VECTOR> <MAX POS VECTOR> - generates a hitmesh as a bbox centered around the bone in the base pose (the hitbox will rotate/move with animations). The bodynum will be visible to gamecode, and may merge with other hitboxes with the same group.\n\tmodelflags <NAME OR HEX> - enables the specified bit in the iqm header. supported names include q1_rocket, q1_grenade, q1_gib, q1_rotate, q1_tracer1, q1_zomgib, q1_tracer2, q1_tracer3\n\t<MESH PROPERTY> - defined below and applied as the defaults to the following import lines as well as mesh lines.\n\tmesh <NAME> [MESH PROPERTIES LIST] - provides overrides for a single named mesh (properties used will be those as they're already defined, if not otherwise listed).\n\tbone <SOURCENAME> [rename <NEWNAME>] [group <GROUPNUM>] - provides bone renaming and grouping. try to avoid renaming two bones to the same resulting name... groups may have limitations if a parent/child relationship cannot be honoured. lowest group numbers come before higher groups. by default bones will inherit their group from their parent.\n\t<IMPORT PROPERTY> - defined below and applied as the defaults to the following import lines.\n\timport <FILENAME> [IMPORT PROPERTIES] [MESH PROPERTIES] - imports the meshes and animations from the specified file.\n\nMesh Properties:\n\tcontents <NAMES OR 0xBITS> - 'body' or 'empty' are the two that are most likely to be used. 'solid' may also be desired, or possibly also 'corpse'.\n\tsurfaceflags <NAMES OR 0xBITS> - 'q3_nodraw/fte_nodraw'\n\tbody <NUMBER> - this is the 'body' value reported to gamecode when a trace hits this surface.\n\tgeomset <GEOMGROUP> <GEOMID> - by configuring this, you can have models display different body parts for different character models.\n\tlodrange <MINDIST> <MAXDIST> - not yet implemented by the engine. 0 for both is the default.\n\nAnim/Import Properties:\n\tname <NAME> - the imported animations will be assigned this name. May be problematic if the imported file(s) share the same name, so try to avoid using this at global scope.\n\tfps <RATE> - framerate for any imported animations.\n\tloop - flags animations as looping.\n\tclamp - disables looping.\n\tunpack - seperates each pose of the animations into a seperate single-pose animation for compat with q1 or q2-style model animations.\n\tpack - disables unpacking again.\n\tnomesh <1|0> - discards all meshed from the affected files.\n\tnoanim <1|0> - discards animations from the affected files, does not disclude the base pose.\n\tmaterialprefix <PREFIX/> - provides a text prefix on the material name, which should make it easier for editors while still honouring shader paths.\n\tstart <FIRSTPOSE> - the fist pose to import.\n\tend <LASTPOSE> - the last pose to import.\n\trotate <PITCH> <YAW> <ROLL> - rotates the model\n\tscale <SCALER> - rescales the model\n\torigin <X> <Y> <Z> - moves the thing\n\tevent [ANIM,]<POSE[.FRAC]> <EVENTCODE> <\"EVENTDATA\"> - embeds event info within the animation, for stuff like footsteps. How this is used depends on the engine... If used at global scope, can be reset with 'event reset' in order to not apply to later files.\n\t\n"
  },
  {
    "path": "iqm/iqm.cpp",
    "content": "#ifdef IQMTOOL\n\t//building as part of fte. we can pull in fte components for extra features.\n\t#define FTEPLUGIN\n\t#ifndef GLQUAKE\n\t\t#define GLQUAKE\t//this is shit, but ensures index sizes come out the right size\n\t#endif\n\t#include \"../plugins/plugin.h\"\n\t#include \"com_mesh.h\"\n\n\t#define IQMTOOL_MDLEXPORT\n#else\n\t//building standalone. any fte modules cannot be used.\n#endif\n\n#include \"util.h\"\n\n#define IQM_UNPACK (1u<<31)\t//animations will be unpacked into individual frames-as-animations (ie: no more framegroups)\n#define IQM_ALLPRIVATE (IQM_UNPACK)\nbool noext = false;\nbool verbose = false;\nbool quiet = false;\nbool stripbones = false; //strip all bone+anim info.\n\nstruct ejoint\n{\n\tconst char *name;\n\tint parent;\n\n\tejoint() : name(NULL), parent(-1) {}\n};\n\n\n\n\nstruct triangle { uint vert[3]; triangle() {} triangle(uint v0, uint v1, uint v2) { vert[0] = v0; vert[1] = v1; vert[2] = v2; } };\nvector<triangle> triangles, neighbors;\n\nstruct mesh { uint name, material, numskins; uint firstvert, numverts; uint firsttri, numtris; mesh() : name(0), material(0), firstvert(0), numverts(0), firsttri(0), numtris(0) {} };\nvector<mesh> meshes;\n\nstruct meshprop\n{\n\tuint contents;\n\tuint surfaceflags;\n\tuint body;\n\tuint geomset;\n\tuint geomid;\n\tfloat mindist;\n\tfloat maxdist;\n\tmeshprop() : contents(0x02000000), surfaceflags(0), body(0), geomset(~0u), geomid(0), mindist(0), maxdist(0) {};\n};\nvector<meshprop> meshes_fte;\t//extra crap\nuint modelflags;\t//q1 uses this.\n\nstruct event_fte\n{\n\tuint anim;\n\tfloat timestamp;\t//pose indexes.\n\tuint evcode;\n\tconst char *evdata_str;\n\tuint evdata_idx;\n};\nvector<event_fte> events_fte;\n\nvector<iqmext_fte_skin_meshskin> meshskins;\nvector<iqmext_fte_skin_skinframe> skinframes;\n\nstruct anim { uint name; uint firstframe, numframes; float fps; uint flags; anim() : name(0), firstframe(0), numframes(0), fps(0), flags(0) {} };\nvector<anim> anims;\n\nstruct joint { int group; uint name; int parent; float pos[3], orient[4], scale[3]; joint() : name(0), parent(-1) { memset(pos, 0, sizeof(pos)); memset(orient, 0, sizeof(orient)); memset(scale, 0, sizeof(scale)); } };\nvector<joint> joints;\t//for meshes\n\nstruct pose { const char *name; int parent; uint flags; float offset[10], scale[10]; pose() : name(NULL), parent(-1), flags(0) { memset(offset, 0, sizeof(offset)); memset(scale, 0, sizeof(scale)); } };\nvector<pose> poses;\t\t//aka: animation joints\n\nstruct framebounds { Vec3 bbmin, bbmax; double xyradius, radius; framebounds() : bbmin(0, 0, 0), bbmax(0, 0, 0), xyradius(0), radius(0) {} };\nvector<framebounds> bounds;\n\nstruct transform\n{\n\tVec3 pos;\n\tQuat orient;\n\tVec3 scale;\n\n\ttransform() {}\n\ttransform(const Vec3 &pos, const Quat &orient, const Vec3 &scale = Vec3(1, 1, 1)) : pos(pos), orient(orient), scale(scale) {}\n};\nstruct frame\n{\n\tstruct framepose\n\t{\n\t\tint remap;\n\t\tconst char *bonename;\n\t\tint boneparent;\n\t\ttransform tr;\n\n\t\tframepose() : bonename(\"\"),boneparent(-1),tr() {}\n\t\tframepose(ejoint &j, transform t) : bonename(j.name),boneparent(j.parent),tr(t) {}\n\t};\n\tvector<framepose> pose;\n};\nvector<frame> frames;\n\nvector<char> stringdata, commentdata;\n\nuint numfverts; //verts generated so far\n\nstruct boneoverride\n{\n\tconst char *name;\n\tbool used;\n\tstruct prop\n\t{\n\t\tconst char *rename;\n\t\tint group;\n\n\t\tprop() : rename(NULL), group(-1) {}\n\t} props;\n\n\tboneoverride() : used(false), props(){}\n};\nvector<boneoverride> boneoverrides;\nstruct meshoverride\n{\n\tconst char *name;\n\tmeshprop props;\n};\nvector<meshoverride> meshoverrides;\n\n\nstruct hitbox\n{\n\tint body;\n\tconst char *bone;\n\tVec3 mins, maxs;\n};\n\nstruct filespec\n{\n\tconst char *file;\n\tconst char *name;\n\tdouble fps;\n\tuint flags;\n\tint startframe;\n\tint endframe;\n\tmeshprop meshprops;\n\tconst char *materialprefix;\n\tconst char *materialsuffix;\n\tbool ignoresurfname;\n\tQuat rotate;\n\tfloat scale;\n\tVec3 translate;\n\tbool nomesh;\n\tbool noanim;\n\tvector<event_fte> events;\n\n\tfilespec() { reset(); }\n\n\tvoid reset()\n\t{\n\t\tfile = NULL;\n\t\tname = NULL;\n\t\tfps = 24;\n\t\tflags = 0;\n\t\tstartframe = 0;\n\t\tendframe = -1;\n\t\tmeshprops = meshprop();\n\t\tmaterialprefix = NULL;\n\t\tmaterialsuffix = NULL;\n\t\tignoresurfname = false;\n\t\trotate = Quat(0, 0, 0, 1);\n\t\tscale = 1;\n\t\ttranslate = Vec3(0,0,0);\n\t\tnomesh = false;\n\t\tnoanim = false;\n\t\tevents.setsize(0);\n\t}\n};\n\nstruct sharedstring\n{\n\tuint offset;\n\tsharedstring() {}\n\tsharedstring(const char *s) : offset(stringdata.length()) { stringdata.put(s, strlen(s)+1); }\n}; \n\nstatic inline bool htcmp(const char *x, const sharedstring &s)\n{\n\treturn htcmp(x, &stringdata[s.offset]);\n}\n\nhashtable<sharedstring, uint> stringoffsets;\n\nuint sharestring(const char *s)\n{\n\tif(stringdata.empty()) stringoffsets.access(\"\", 0);\n\treturn stringoffsets.access(s ? s : \"\", stringdata.length());\n}\n\nstruct blendcombo\n{\n\tint sorted;\n\tdouble weights[4];\n\tuchar bones[4];\n\n\tblendcombo() : sorted(0) {}\n\n\tvoid reset() { sorted = 0; }\n\n\tvoid addweight(double weight, int bone)\n\t{\n\t\tif(weight <= 1e-3) return;\n\t\tloopk(sorted) if(weight > weights[k])\n\t\t{\n\t\t\tfor(int l = min(sorted-1, 2); l >= k; l--)\n\t\t\t{\n\t\t\t\tweights[l+1] = weights[l];\n\t\t\t\tbones[l+1] = bones[l];\n\t\t\t}\n\t\t\tweights[k] = weight;\n\t\t\tbones[k] = bone;\n\t\t\tif(sorted<4) sorted++;\n\t\t\treturn;\n\t\t}\n\t\tif(sorted>=4) return;\n\t\tweights[sorted] = weight;\n\t\tbones[sorted] = bone;\n\t\tsorted++;\n\t}\n\n\tvoid finalize()\n\t{\n\t\tloopj(4-sorted) { weights[sorted+j] = 0; bones[sorted+j] = 0; }\n\t\tif(sorted <= 0) return;\n\t\tdouble total = 0;\n\t\tloopj(sorted) total += weights[j];\n\t\ttotal = 1.0/total;\n\t\tloopj(sorted) weights[j] *= total;\n\t}\n\n\tvoid serialize(uchar *vweights) const\n\t{\n\t\tint total = 0;\n\t\tloopk(4) total += (vweights[k] = uchar(0.5 + weights[k]*255));\n\t\tif(sorted <= 0) return;\n\t\twhile(total > 255)\n\t\t{\n\t\t\tloopk(4) if(vweights[k] > 0 && total > 255) { vweights[k]--; total--; }\n\t\t}\n\t\twhile(total < 255)\n\t\t{\n\t\t\tloopk(4) if(vweights[k] < 255 && total < 255) { vweights[k]++; total++; }\n\t\t}\n\t}\n\n\tbool operator==(const blendcombo &c) { loopi(4) if(weights[i] != c.weights[i] || bones[i] != c.bones[i]) return false; return true; }\n\tbool operator!=(const blendcombo &c) { loopi(4) if(weights[i] != c.weights[i] || bones[i] != c.bones[i]) return true; return false; }\n};\nvector<Vec4> mpositions;\nvector<blendcombo> mblends;\n\nstatic bool parseindex(char *&c, int &val)\n{ \n\twhile(isspace(*c)) c++;\n\tchar *end = NULL;\n\tint rval = strtol(c, &end, 10);\n\tif(c == end) return false;\n\tval = rval;\n\tc = end;\n\treturn true;\n}\n\nstatic double parseattrib(char *&c, double ival = 0)\n{\n\twhile(isspace(*c)) c++;\n\tchar *end = NULL;\n\tdouble val = strtod(c, &end);\n\tif(c == end) val = ival;\n\telse c = end;\n\treturn val;\n}\n\nstatic bool maybeparseattrib(char *&c, double &result)\n{\n\twhile(isspace(*c)) c++;\n\tchar *end = NULL;\n\tdouble val = strtod(c, &end);\n\tif(c == end) return false;\n\tc = end;\n\tresult = val;\n\treturn true;\n}\n\n#if 0\nstatic bool parsename(char *&c, char *buf, int bufsize = sizeof(string))\n{\n\twhile(isspace(*c)) c++;\n\tchar *end;\n\tif(*c == '\"')\n\t{\n\t\tc++;\n\t\tend = c;\n\t\twhile(*end && *end != '\"') end++;\n\t\tcopystring(buf, c, min(int(end-c+1), bufsize));\n\t\tif(*end == '\"') end++;\n\t}\n\telse\n\t{\n\t\tend = c;\n\t\twhile(*end && !isspace(*end)) end++;\n\t\tcopystring(buf, c, min(int(end-c+1), bufsize));\n\t}\n\tif(c == end) return false;\n\tc = end;\n\treturn true;\n}\n#endif\n\nstatic char *trimname(char *&c)\n{\n\twhile(isspace(*c)) c++;\n\tchar *start, *end;\n\tif(*c == '\"')\n\t{\n\t\tc++;\n\t\tstart = end = c;\n\t\twhile(*end && *end != '\"') end++;\n\t\tif(*end) { *end = '\\0'; end++; }\n\t}\n\telse\n\t{\n\t\tstart = end = c;\n\t\twhile(*end && !isspace(*end)) end++;\n\t\tif(*end) { *end = '\\0'; end++; }\n\t}\n\tc = end;\n\treturn start;\n}\n\nstatic Vec4 parseattribs4(char *&c, const Vec4 &ival = Vec4(0, 0, 0, 0))\n{\n\tVec4 val;\n\tloopk(4) val[k] = parseattrib(c, ival[k]);\n\treturn val;\n}\n\nstatic Vec3 parseattribs3(char *&c, const Vec3 &ival = Vec3(0, 0, 0))\n{\n\tVec3 val;\n\tloopk(3) val[k] = parseattrib(c, ival[k]);\n\treturn val;\n}\n\nstatic blendcombo parseblends(char *&c)\n{\n\tblendcombo b;\n\tint index;\n\twhile(parseindex(c, index))\n\t{\n\t\tdouble weight = parseattrib(c, 0);\n\t\tb.addweight(weight, index);\n\t}\n\tb.finalize();\n\treturn b;\n}\n\nstruct eanim\n{\n\tconst char *name;\n\tint startframe, endframe;\n\tdouble fps;\n\tuint flags;\n\n\teanim() : name(NULL), startframe(0), endframe(INT_MAX), fps(0), flags(0) {}\n};\n\nstruct emesh\n{\n\tconst char *name, *material;\n\tint firsttri;\n\tbool used;\n\tbool hasexplicits;\n\tmeshprop explicits;\n\n\temesh() : name(NULL), material(NULL), firsttri(0), used(false), hasexplicits(false) {}\n\temesh(const char *name, const char *material, int firsttri = 0) : name(name), material(material), firsttri(firsttri), used(false), hasexplicits(false) {}\n};\n\nstruct evarray\n{\n\tstring name;\n\tuint type, format, size;\n\n\tevarray() : type(IQM_POSITION), format(IQM_FLOAT), size(3) { name[0] = '\\0'; }\n\tevarray(uint type, uint format, uint size, const char *initname = \"\") : type(type), format(format), size(size) { copystring(name, initname); }\n};\n\nstruct esmoothgroup\n{\n\tenum\n\t{\n\t\tF_USED     = 1<<0,\n\t\tF_UVSMOOTH = 1<<1\n\t};\n\n\tint key;\n\tfloat angle;\n\tint flags;\n\n\tesmoothgroup() : key(-1), angle(-1), flags(0) {}\n};\n\nstruct etriangle\n{\n\tint smoothgroup;\n\tuint vert[3], weld[3];\n\n\tetriangle()\n\t\t: smoothgroup(-1)\n\t{\n\t}\n\tetriangle(int v0, int v1, int v2, int smoothgroup = -1)\n\t\t: smoothgroup(smoothgroup)\n\t{\n\t\tvert[0] = v0;\n\t\tvert[1] = v1;\n\t\tvert[2] = v2;\n\t}\n};\n\nvector<Vec4> epositions, etexcoords, etangents, ecolors, ecustom[10];\nvector<Vec3> enormals, ebitangents;\nvector<blendcombo> eblends;\nvector<etriangle> etriangles;\nvector<esmoothgroup> esmoothgroups;\nvector<int> esmoothindexes;\nvector<uchar> esmoothedges;\nvector<ejoint> ejoints;\nvector<transform> eposes;\nvector<Matrix3x4> mjoints;\nvector<int> eframes;\nvector<eanim> eanims;\nvector<emesh> emeshes;\nvector<evarray> evarrays;\nhashtable<const char *, char *> enames;\n\nconst char *getnamekey(const char *name)\n{\n\tchar **exists = enames.access(name);\n\tif(exists) return *exists;\n\tchar *key = newstring(name);\n\tenames[key] = key;\n\treturn key;\n}\n\nstruct weldinfo\n{\n\tint tri, vert;\n\tweldinfo *next;\n};\n\nvoid weldvert(const vector<Vec3> &norms, const Vec4 &pos, weldinfo *welds, int &numwelds, unionfind<int> &welder)\n{\n\twelder.clear();\n\tint windex = 0;\n\tfor(weldinfo *w = welds; w; w = w->next, windex++)\n\t{\n\t\tetriangle &wt = etriangles[w->tri];\n\t\tesmoothgroup &wg = esmoothgroups[wt.smoothgroup];\n\t\tint vindex = windex + 1;\n\t\tfor(weldinfo *v = w->next; v; v = v->next, vindex++)\n\t\t{\n\t\t\tetriangle &vt = etriangles[v->tri];\n\t\t\tesmoothgroup &vg = esmoothgroups[vt.smoothgroup];\n\t\t\tif(wg.key != vg.key) continue;\n\t\t\tif(norms[w->tri].dot(norms[v->tri]) < max(wg.angle, vg.angle)) continue;\n\t\t\tif(((wg.flags | vg.flags) & esmoothgroup::F_UVSMOOTH) &&\n\t\t\t   etexcoords[wt.vert[w->vert]] != etexcoords[vt.vert[v->vert]])\n\t\t\t\tcontinue;\n\t\t\tif(esmoothindexes.length() > max(w->vert, v->vert) && esmoothindexes[w->vert] != esmoothindexes[v->vert])\n\t\t\t\tcontinue;\n\t\t\tif(esmoothedges.length())\n\t\t\t{\n\t\t\t\tint w0 = w->vert, w1 = (w->vert+1)%3, w2 = (w->vert+2)%3;\n\t\t\t\tconst Vec4 &wp1 = epositions[wt.vert[w1]],\n\t\t\t\t\t\t   &wp2 = epositions[wt.vert[w2]];\n\t\t\t\tint v0 = v->vert, v1 = (v->vert+1)%3, v2 = (v->vert+2)%3;\n\t\t\t\tconst Vec4 &vp1 = epositions[vt.vert[v1]],\n\t\t\t\t\t\t   &vp2 = epositions[vt.vert[v2]];\n\t\t\t\tint wf = esmoothedges[w->tri], vf = esmoothedges[v->tri];\n\t\t\t\tif((wp1 != vp1 || !(((wf>>w0)|(vf>>v0))&1)) &&\n\t\t\t\t   (wp1 != vp2 || !(((wf>>w0)|(vf>>v2))&1)) &&\n\t\t\t\t   (wp2 != vp1 || !(((wf>>w2)|(vf>>v0))&1)) &&\n\t\t\t\t   (wp2 != vp2 || !(((wf>>w2)|(vf>>v2))&1)))\n\t\t\t\t\tcontinue;\n\t\t\t}\n\t\t\twelder.unite(windex, vindex, -1);\n\t\t}\n\t}\n\twindex = 0;\n\tfor(weldinfo *w = welds; w; w = w->next, windex++)\n\t{\n\t\tetriangle &wt = etriangles[w->tri];\n\t\twt.weld[w->vert] = welder.find(windex, -1, numwelds);\n\t\tif(wt.weld[w->vert] == uint(numwelds)) numwelds++;\n\t}\n}\n\nvoid smoothverts(bool areaweight = true)\n{\n\tif(etriangles.empty()) return;\n\n\tif(enormals.length())\n\t{\n\t\tloopv(etriangles)\n\t\t{\n\t\t\tetriangle &t = etriangles[i];\n\t\t\tloopk(3) t.weld[k] = t.vert[k];\n\t\t}\n\t\treturn;\n\t}\n\n\tif(etexcoords.empty()) loopv(esmoothgroups) esmoothgroups[i].flags &= ~esmoothgroup::F_UVSMOOTH;\n\tif(esmoothedges.length()) while(esmoothedges.length() < etriangles.length()) esmoothedges.add(7);\n\n\tvector<Vec3> tarea, tnorms;\n\tloopv(etriangles)\n\t{\n\t\tetriangle &t = etriangles[i];\n\t\tVec3 v0(epositions[t.vert[0]]),\n\t\t\t v1(epositions[t.vert[1]]),\n\t\t\t v2(epositions[t.vert[2]]);\n\t\ttnorms.add(tarea.add((v2 - v0).cross(v1 - v0)).normalize());\n\t}\n\n\tint nextalloc = 0;\n\tvector<weldinfo *> allocs;\n\thashtable<Vec4, weldinfo *> welds(1<<12);\n\n\tloopv(etriangles)\n\t{\n\t\tetriangle &t = etriangles[i];\n\t\tloopk(3)\n\t\t{\n\t\t\tweldinfo **next = &welds.access(epositions[t.vert[k]], NULL);\n\t\t\tif(! (nextalloc % 1024)) allocs.add(new weldinfo[1024]);\n\t\t\tweldinfo &w = allocs[nextalloc/1024][nextalloc%1024];\n\t\t\tnextalloc++;\n\t\t\tw.tri = i;\n\t\t\tw.vert = k;\n\t\t\tw.next = *next;\n\t\t\t*next = &w;\n\t\t}\n\t}\n\n\tint numwelds = 0;\n\tunionfind<int> welder;\n\tenumerate(welds, Vec4, vpos, weldinfo *, vwelds, weldvert(tnorms, vpos, vwelds, numwelds, welder));\n\n\tloopv(allocs) delete[] allocs[i];\n\n\tloopi(numwelds) enormals.add(Vec3(0, 0, 0));\n\tloopv(etriangles)\n\t{\n\t\tetriangle &t = etriangles[i];\n\t\tloopk(3) enormals[t.weld[k]]+= areaweight ? tarea[i] : tnorms[i];\n\t}\n\tloopv(enormals) if(enormals[i] != Vec3(0, 0, 0)) enormals[i] = enormals[i].normalize();\n}\n\nstruct sharedvert\n{\n\tint index, weld;\n\n\tsharedvert() {}\n\tsharedvert(int index, int weld) : index(index), weld(weld) {}\n};\n\nstatic inline bool htcmp(const sharedvert &v, const sharedvert &s)\n{\n\tif(epositions[v.index] != epositions[s.index]) return false;\n\tif(etexcoords.length() && etexcoords[v.index] != etexcoords[s.index]) return false;\n\tif(enormals.length() && enormals[v.weld] != enormals[s.weld]) return false;\n\tif(eblends.length() && eblends[v.index] != eblends[s.index]) return false;\n\tif(ecolors.length() && ecolors[v.index] != ecolors[s.index]) return false;\n\tloopi(10) if(ecustom[i].length() && ecustom[i][v.index] != ecustom[i][s.index]) return false; \n\treturn true;\n}\n\nstatic inline uint hthash(const sharedvert &v)\n{\n\treturn hthash(epositions[v.index]);\n}\n\nconst struct vertexarraytype\n{\n\tconst char *name;\n\tint code;\n} vatypes[] =\n{\n\t{ \"position\", IQM_POSITION },\n\t{ \"texcoord\", IQM_TEXCOORD },\n\t{ \"normal\", IQM_NORMAL  },\n\t{ \"tangent\", IQM_TANGENT },\n\t{ \"blendindexes\", IQM_BLENDINDEXES },\n\t{ \"blendweights\", IQM_BLENDWEIGHTS },\n\t{ \"color\", IQM_COLOR },\n\t{ \"custom0\", IQM_CUSTOM + 0 },\n\t{ \"custom1\", IQM_CUSTOM + 1 },\n\t{ \"custom2\", IQM_CUSTOM + 2 },\n\t{ \"custom3\", IQM_CUSTOM + 3 },\n\t{ \"custom4\", IQM_CUSTOM + 4 },\n\t{ \"custom5\", IQM_CUSTOM + 5 },\n\t{ \"custom6\", IQM_CUSTOM + 6 },\n\t{ \"custom7\", IQM_CUSTOM + 7 },\n\t{ \"custom8\", IQM_CUSTOM + 8 },\n\t{ \"custom9\", IQM_CUSTOM + 9 }\n};\n\nint findvertexarraytype(const char *name)\n{\n\tloopi(sizeof(vatypes)/sizeof(vatypes[0]))\n\t{\n\t\tif(!strcasecmp(vatypes[i].name, name))\n\t\t\treturn vatypes[i].code;\n\t}\n\treturn -1;\n}\n\nconst struct vertexarrayformat\n{\n\tconst char *name;\n\tint code;\n\tint size;\n} vaformats[] = \n{\n\t{ \"byte\", IQM_BYTE, 1 },\n\t{ \"ubyte\", IQM_UBYTE, 1 },\n\t{ \"short\", IQM_SHORT, 2 },\n\t{ \"ushort\", IQM_USHORT, 2 },\n\t{ \"int\", IQM_INT, 4 },\n\t{ \"uint\", IQM_UINT, 4 },\n\t{ \"half\", IQM_HALF, 2 },\n\t{ \"float\", IQM_FLOAT, 4 },\n\t{ \"double\", IQM_DOUBLE, 8 }\n};\n\nint findvertexarrayformat(const char *name)\n{\n\tloopi(sizeof(vaformats)/sizeof(vaformats[0]))\n\t{\n\t\tif(!strcasecmp(vaformats[i].name, name))\n\t\t\treturn vaformats[i].code;\n\t}\n\treturn -1;\n}\n\nstruct vertexarray\n{\n\tuint type, flags, format, size, offset, count;\n\tvector<uchar> vdata;\n\n\tvertexarray(uint type, uint format, uint size) : type(type), flags(0), format(format), size(size), offset(0), count(0) {}\n\n\tint formatsize() const\n\t{\n\t\treturn vaformats[format].size;\n\t}\n\n\tint bytesize() const\n\t{\n\t\treturn size * vaformats[format].size;\n\t}\n};\n\nvector<sharedvert> vmap;\nvector<vertexarray> varrays;\nvector<uchar> vdata;\n\nstruct halfdata\n{\n\tushort val;\n\n\thalfdata(double d)\n\t{\n\t\tunion\n\t\t{\n\t\t\tullong i;\n\t\t\tdouble d;\n\t\t} conv;\n\t\tconv.d = d;\n\t\tushort signbit = ushort((conv.i>>63)&1);\n\t\tushort mantissa = ushort((conv.i>>(52-10))&0x3FF);\n\t\tint exponent = int((conv.i>>52)&0x7FF) - 1023 + 15;\n\t\tif(exponent <= 0)\n\t\t{\n\t\t\tmantissa |= 0x400;\n\t\t\tmantissa >>= min(1-exponent, 10+1);\n\t\t\texponent = 0;\n\t\t} \n\t\telse if(exponent >= 0x1F)\n\t\t{\n\t\t\tmantissa = 0;\n\t\t\texponent = 0x1F;\n\t\t}\n\t\tval = (signbit<<15) | (ushort(exponent)<<10) | mantissa;\n\t}\n};\n\ntemplate<> inline halfdata endianswap<halfdata>(halfdata n) { n.val = endianswap16(n.val); return n; }\n\ntemplate<int TYPE> static inline int remapindex(int i, const sharedvert &v) { return v.index; }\ntemplate<> inline int remapindex<IQM_NORMAL>(int i, const sharedvert &v) { return v.weld; }\ntemplate<> inline int remapindex<IQM_TANGENT>(int i, const sharedvert &v) { return i; }\n\ntemplate<class T, class U>\nstatic inline void putattrib(T &out, const U &val) { out = T(val); }\n\ntemplate<class T, class U>\nstatic inline void uroundattrib(T &out, const U &val, double scale) { out = T(clamp(0.5 + val*scale, 0.0, scale)); }\ntemplate<class T, class U>\nstatic inline void sroundattrib(T &out, const U &val, double scale, double low, double high) { double n = val*scale*0.5; out = T(clamp(n < 0 ? ceil(n - 1) : floor(n), low, high)); }\n\ntemplate<class T, class U>\nstatic inline void scaleattrib(T &out, const U &val) { putattrib(out, val); }\ntemplate<class U>\nstatic inline void scaleattrib(char &out, const U &val) { sroundattrib(out, val, 255.0, -128.0, 127.0); }\ntemplate<class U>\nstatic inline void scaleattrib(short &out, const U &val) { sroundattrib(out, val, 65535.0, -32768.0, 32767.0); }\ntemplate<class U>\nstatic inline void scaleattrib(int &out, const U &val) { sroundattrib(out, val, 4294967295.0, -2147483648.0, 2147483647.0); }\ntemplate<class U>\nstatic inline void scaleattrib(uchar &out, const U &val) { uroundattrib(out, val, 255.0); }\ntemplate<class U>\nstatic inline void scaleattrib(ushort &out, const U &val) { uroundattrib(out, val, 65535.0); }\ntemplate<class U>\nstatic inline void scaleattrib(uint &out, const U &val) { uroundattrib(out, val, 4294967295.0); }\n\ntemplate<int T>\nstatic inline bool normalizedattrib() { return true; }\n\ntemplate<int TYPE, int FMT, class T, class U>\nstatic inline void serializeattrib(const vertexarray &va, T *data, const U &attrib)\n{\n\tif(normalizedattrib<TYPE>()) switch(va.size)\n\t{\n\tcase 4: scaleattrib(data[3], attrib.w);\n\tcase 3: scaleattrib(data[2], attrib.z);\n\tcase 2: scaleattrib(data[1], attrib.y);\n\tcase 1: scaleattrib(data[0], attrib.x);\n\t}\n\telse switch(va.size)\n\t{\n\tcase 4: putattrib(data[3], attrib.w);\n\tcase 3: putattrib(data[2], attrib.z);\n\tcase 2: putattrib(data[1], attrib.y);\n\tcase 1: putattrib(data[0], attrib.x);\n\t}\n\tlilswap(data, va.size);\n}\n\ntemplate<int TYPE, int FMT, class T>\nstatic inline void serializeattrib(const vertexarray &va, T *data, const Vec3 &attrib)\n{\n\tif(normalizedattrib<TYPE>()) switch(va.size)\n\t{\n\tcase 3: scaleattrib(data[2], attrib.z);\n\tcase 2: scaleattrib(data[1], attrib.y);\n\tcase 1: scaleattrib(data[0], attrib.x);\n\t}\n\telse switch(va.size)\n\t{\n\tcase 3: putattrib(data[2], attrib.z);\n\tcase 2: putattrib(data[1], attrib.y);\n\tcase 1: putattrib(data[0], attrib.x);\n\t}\n\tlilswap(data, va.size);\n}\n\ntemplate<int TYPE, int FMT, class T>\nstatic inline void serializeattrib(const vertexarray &va, T *data, const blendcombo &blend)\n{\n\tif(TYPE == IQM_BLENDINDEXES)\n\t{\n\t\tswitch(va.size)\n\t\t{\n\t\tcase 4: putattrib(data[3], blend.bones[3]);\n\t\tcase 3: putattrib(data[2], blend.bones[2]);\n\t\tcase 2: putattrib(data[1], blend.bones[1]);\n\t\tcase 1: putattrib(data[0], blend.bones[0]);\n\t\t}\n\t}\n\telse if(FMT == IQM_UBYTE)\n\t{\n\t\tuchar weights[4];\n\t\tblend.serialize(weights);\n\t\tswitch(va.size)\n\t\t{\n\t\tcase 4: putattrib(data[3], weights[3]);\n\t\tcase 3: putattrib(data[2], weights[2]);\n\t\tcase 2: putattrib(data[1], weights[1]);\n\t\tcase 1: putattrib(data[0], weights[0]);\n\t\t}\n\t}\n\telse\n\t{\n\t\tswitch(va.size)\n\t\t{\n\t\tcase 4: scaleattrib(data[3], blend.weights[3]);\n\t\tcase 3: scaleattrib(data[2], blend.weights[2]);\n\t\tcase 2: scaleattrib(data[1], blend.weights[1]);\n\t\tcase 1: scaleattrib(data[0], blend.weights[0]);\n\t\t}\n\t}\n\tlilswap(data, va.size);\n}\n\ntemplate<int TYPE, class T>\nvoid setupvertexarray(const vector<T> &attribs, uint type, uint fmt, uint size, uint first)\n{\n\tconst char *name = \"\";\n\tloopv(evarrays) if(evarrays[i].type == type)\n\t{\n\t\tevarray &info = evarrays[i];\n\t\tfmt = info.format;\n\t\tsize = (uint)clamp((int)info.size, 1, 4);\n\t\tname = info.name;\n\t\tbreak;\n\t}\n\n\tif(type >= IQM_CUSTOM)\n\t{\n\t\tif(!name[0])\n\t\t{\n\t\t\tdefformatstring(customname, \"custom%d\", type-IQM_CUSTOM);\n\t\t\ttype = IQM_CUSTOM + sharestring(customname);\n\t\t}\n\t\telse type = IQM_CUSTOM + sharestring(name);\n\t}\n\n\tint k;\n\tfor (k = 0; k < varrays.length(); k++)\n\t{\n\t\tif (varrays[k].type == type && varrays[k].format == fmt && varrays[k].size == size)\n\t\t\tbreak;\n\t}\n\tif (k == varrays.length())\n\t\tvarrays.add(vertexarray(type, fmt, size)); \n\tvertexarray &va = varrays[k];\n\tif (va.count != first)\n\t\tfatal(\"count != first\");\t//gaps are a problem.\n\tva.count += vmap.length();\n\n\tint totalsize = va.bytesize() * vmap.length();\n\tuchar *data = va.vdata.reserve(totalsize);\n\tva.vdata.advance(totalsize);\n\tloopv(vmap)\n\t{\n\t\tconst T &attrib = attribs[remapindex<TYPE>(i, vmap[i])];\n\t\tswitch(va.format)\n\t\t{\n\t\tcase IQM_BYTE: serializeattrib<TYPE, IQM_BYTE>(va, (char *)data, attrib); break;\n\t\tcase IQM_UBYTE: serializeattrib<TYPE, IQM_UBYTE>(va, (uchar *)data, attrib); break;\n\t\tcase IQM_SHORT: serializeattrib<TYPE, IQM_SHORT>(va, (short *)data, attrib); break;\n\t\tcase IQM_USHORT: serializeattrib<TYPE, IQM_USHORT>(va, (ushort *)data, attrib); break;\n\t\tcase IQM_INT: serializeattrib<TYPE, IQM_INT>(va, (int *)data, attrib); break;\n\t\tcase IQM_UINT: serializeattrib<TYPE, IQM_UINT>(va, (uint *)data, attrib); break;\n\t\tcase IQM_HALF: serializeattrib<TYPE, IQM_HALF>(va, (halfdata *)data, attrib); break;\n\t\tcase IQM_FLOAT: serializeattrib<TYPE, IQM_FLOAT>(va, (float *)data, attrib); break;\n\t\tcase IQM_DOUBLE: serializeattrib<TYPE, IQM_DOUBLE>(va, (double *)data, attrib); break;\n\t\t}\n\t\tdata += va.bytesize();\n\t}\n}\n\n// linear speed vertex cache optimization from Tom Forsyth\n\n#define MAXVCACHE 32\n\nstruct triangleinfo \n{ \n\tbool used;\n\tfloat score;\n\tuint vert[3]; \n\n\ttriangleinfo() {} \n\ttriangleinfo(uint v0, uint v1, uint v2) \n\t{ \n\t\tvert[0] = v0; \n\t\tvert[1] = v1; \n\t\tvert[2] = v2; \n\t} \n};\n\nstruct vertexcache : listnode<vertexcache>\n{\n\tint index, rank;\n\tfloat score;\n\tint numuses;\n\ttriangleinfo **uses;\n\n\tvertexcache() : index(-1), rank(-1), score(-1.0f), numuses(0), uses(NULL) {}\n\n\tvoid calcscore()\n\t{\n\t\tif(numuses > 0)\n\t\t{\n\t\t\tscore = 2.0f * powf(numuses, -0.5f);\n\t\t\tif(rank >= 3) score += powf(1.0f - (rank - 3)/float(MAXVCACHE - 3), 1.5f);\n\t\t\telse if(rank >= 0) score += 0.75f;\n\t\t}\n\t\telse score = -1.0f;\n\t}\n\n\tvoid removeuse(triangleinfo *t)\n\t{\n\t\tloopi(numuses) if(uses[i] == t)\n\t\t{\n\t\t\tuses[i] = uses[--numuses];\n\t\t\treturn;\n\t\t}\n\t}\n};\n\nvoid maketriangles(vector<triangleinfo> &tris, const vector<sharedvert> &mmap)\n{\n\ttriangleinfo **uses = new triangleinfo *[3*tris.length()];\n\tvertexcache *verts = new vertexcache[mmap.length()];\n\tlist<vertexcache> vcache;\n\n\tloopv(tris)\n\t{\n\t\ttriangleinfo &t = tris[i];\n\t\tt.used = t.vert[0] == t.vert[1] || t.vert[1] == t.vert[2] || t.vert[2] == t.vert[0];\n\t\tif(t.used) continue;\n\t\tloopk(3) verts[t.vert[k]].numuses++;\n\t}\n\ttriangleinfo **curuse = uses;\n\tloopvrev(tris)\n\t{\n\t\ttriangleinfo &t = tris[i];\n\t\tif(t.used) continue;\n\t\tloopk(3)\n\t\t{\n\t\t\tvertexcache &v = verts[t.vert[k]];\n\t\t\tif(!v.uses) { curuse += v.numuses; v.uses = curuse; }\n\t\t\t*--v.uses = &t;\n\t\t}\n\t}\n\tloopv(mmap) verts[i].calcscore();\n\ttriangleinfo *besttri = NULL;\n\tfloat bestscore = -1e16f;\n\tloopv(tris)\n\t{\n\t\ttriangleinfo &t = tris[i];\n\t\tif(t.used) continue;\n\t\tt.score = verts[t.vert[0]].score + verts[t.vert[1]].score + verts[t.vert[2]].score;\n\t\tif(t.score > bestscore) { besttri = &t; bestscore = t.score; }\n\t}\n\n\t//int reloads = 0;\n\twhile(besttri)\n\t{\n\t\tbesttri->used = true;\n\t\ttriangle &t = triangles.add();\n\t\tloopk(3) \n\t\t{\n\t\t\tvertexcache &v = verts[besttri->vert[k]];\n\t\t\tif(v.index < 0) { v.index = vmap.length(); vmap.add(mmap[besttri->vert[k]]); }\n\t\t\tt.vert[k] = v.index;\n\t\t\tv.removeuse(besttri);\n\t\t\tif(v.rank >= 0) vcache.remove(&v)->rank = -1;\n//\t\t\telse reloads++;\n\t\t\tif(v.numuses <= 0) continue;\n\t\t\tvcache.insertfirst(&v);\n\t\t\tv.rank = 0;\n\t\t}\n\t\tint rank = 0;\n\t\tfor(vertexcache *v = vcache.first(); v != vcache.end(); v = v->next)\n\t\t{\n\t\t\tv->rank = rank++;\n\t\t\tv->calcscore();\n\t\t}\n\t\tbesttri = NULL;\n\t\tbestscore = -1e16f;\n\t\tfor(vertexcache *v = vcache.first(); v != vcache.end(); v = v->next)\n\t\t{\n\t\t\tloopi(v->numuses)\n\t\t\t{\n\t\t\t\ttriangleinfo &t = *v->uses[i];\n\t\t\t\tt.score = verts[t.vert[0]].score + verts[t.vert[1]].score + verts[t.vert[2]].score;\n\t\t\t\tif(t.score > bestscore) { besttri = &t; bestscore = t.score; }\n\t\t\t}\n\t\t}\n\t\twhile(vcache.size > MAXVCACHE) vcache.removelast()->rank = -1;\n\t\tif(!besttri) loopv(tris)\n\t\t{\n\t\t\ttriangleinfo &t = tris[i];\n\t\t\tif(!t.used && t.score > bestscore) { besttri = &t; bestscore = t.score; }\n\t\t}\n\t}\n//\tprintf(\"reloads: %d, worst: %d, best: %d\\n\", reloads, tris.length()*3, mmap.length());\n\n\tdelete[] uses;\n\tdelete[] verts;\n}\n\nvoid calctangents(uint priortris, bool areaweight = true)\n{\n\tuint numverts = vmap.length();\n\tVec3 *tangent = new Vec3[2*numverts], *bitangent = tangent+numverts;\n\tfor (uint i = 0; i < 2*numverts; i++)\n\t\ttangent[i] = Vec3(0,0,0);\n\tfor (int i = priortris; i < triangles.length(); i++)\n\t{\n\t\tconst triangle &t = triangles[i];\n\t\tsharedvert &i0 = vmap[t.vert[0]],\n\t\t\t\t   &i1 = vmap[t.vert[1]],\n\t\t\t\t   &i2 = vmap[t.vert[2]];\n\n\t\tVec3 v0(epositions[i0.index]), e1 = Vec3(epositions[i1.index]) - v0, e2 = Vec3(epositions[i2.index]) - v0;\n\n\t\tdouble u1 = etexcoords[i1.index].x - etexcoords[i0.index].x, v1 = etexcoords[i1.index].y - etexcoords[i0.index].y,\n\t\t\t   u2 = etexcoords[i2.index].x - etexcoords[i0.index].x, v2 = etexcoords[i2.index].y - etexcoords[i0.index].y;\n\t\tVec3 u = e2*v1 - e1*v2,\n\t\t\t v = e2*u1 - e1*u2;\n\n\t\tif(e2.cross(e1).dot(v.cross(u)) < 0) \n\t\t{ \n\t\t\tu = -u; \n\t\t\tv = -v; \n\t\t}\n\n\t\tif(!areaweight)\n\t\t{\n\t\t\tu = u.normalize();\n\t\t\tv = v.normalize();\n\t\t}\n\n\t\tloopj(3)\n\t\t{\n\t\t\ttangent[t.vert[j]] += u;\n\t\t\tbitangent[t.vert[j]] += v;\n\t\t}\n\t}\n\tloopv(vmap)\n\t{\n\t\tconst Vec3 &n = enormals[vmap[i].weld],\n\t\t\t\t   &t = tangent[i],\n\t\t\t\t   &bt = bitangent[i];\n\t\tetangents.add(Vec4((t - n*n.dot(t)).normalize(), n.cross(t).dot(bt) < 0 ? -1 : 1));\n\t}\n\tdelete[] tangent;\n}\n\nstruct neighborkey\n{\n\tuint e0, e1;\n\n\tneighborkey() {}\n\tneighborkey(uint i0, uint i1)\n\t{\n\t\tif(epositions[i0] < epositions[i1]) { e0 = i0; e1 = i1; } \n\t\telse { e0 = i1; e1 = i0; }\n\t}\n\n\tuint hash() const { return hthash(epositions[e0]) + hthash(epositions[e1]); }\n\tbool operator==(const neighborkey &n) const \n\t{ \n\t\treturn epositions[e0] == epositions[n.e0] && epositions[e1] == epositions[n.e1] &&\n\t\t\t   (eblends.empty() || (eblends[e0] == eblends[n.e0] && eblends[e1] == eblends[n.e1])); \n\t}\n};\n\nstatic inline uint hthash(const neighborkey &n) { return n.hash(); }\nstatic inline bool htcmp(const neighborkey &x, const neighborkey &y) { return x == y; }\n\nstruct neighborval\n{\n\tuint tris[2];\n\n\tneighborval() {}\n\tneighborval(uint i) { tris[0] = i; tris[1] = 0xFFFFFFFFU; }\n\n\tvoid add(uint i)\n\t{\n\t\tif(tris[1] != 0xFFFFFFFFU) tris[0] = tris[1] = 0xFFFFFFFFU;\n\t\telse if(tris[0] != 0xFFFFFFFFU) tris[1] = i;\n\t} \n\n\tint opposite(uint i) const\n\t{\n\t\treturn tris[0] == i ? tris[1] : tris[0];\n\t}\n};\n\nvoid makeneighbors(uint priortris)\n{\n\thashtable<neighborkey, neighborval> nhash;\n\n\tfor(int i = priortris; i<triangles.length(); i++)\n\t{\n\t\ttriangle &t = triangles[i];\n\t\tfor(int j = 0, p = 2; j < 3; p = j, j++)\n\t\t{\n\t\t\tneighborkey key(t.vert[p], t.vert[j]);\n\t\t\tneighborval *val = nhash.access(key);\n\t\t\tif(val) val->add(i);\n\t\t\telse nhash[key] = neighborval(i);\n\t\t}\n\t}\n\n\tfor(int i = priortris; i<triangles.length(); i++)\n\t{\n\t\ttriangle &t = triangles[i];\n\t\ttriangle &n = neighbors.add();\n\t\tfor(int j = 0, p = 2; j < 3; p = j, j++)\n\t\t\tn.vert[p] = nhash[neighborkey(t.vert[p], t.vert[j])].opposite(i);\n\t}\n}\n\nQuat erotate;\ndouble escale = 1;\nVec3 emeshtrans(0, 0, 0);\nVec3 ejointtrans(0, 0, 0);\ndouble gscale = 1;\nVec3 gmeshtrans(0,0,0);\n\nvoid printlastmesh(void)\n{\n\tif (quiet)\n\t\treturn;\n\tmesh &m = meshes[meshes.length()-1];\n\tmeshprop &fm = meshes_fte[meshes.length()-1];\n\tprintf(\"    %smesh %i:\\tname=\\\"%s\\\",\\tmat=\\\"%s\\\",\\ttri=%i, vert=%i, skins=%i\\n\", fm.contents?\"c\":\"r\", meshes.length()-1,\n\t\t&stringdata[m.name], &stringdata[m.material], m.numtris, m.numverts, m.numskins);\n\n\tif (verbose)\n\t{\n\t\tif (noext)\n\t\t\tprintf(\"        writing mesh properties is disabled\\n\");\n\t\telse\n\t\t\tprintf(\"        c=%#x sf=%#x b=%i gs=%i gi=%i nd=%g fd=%g\\n\", fm.contents, fm.surfaceflags, fm.body, fm.geomset, fm.geomid, fm.maxdist, fm.mindist);\n\t}\n}\n\nvoid makemeshes(const filespec &spec)\n{\n\tif (spec.nomesh)\n\t\treturn;\n\t/*\n\tif (meshes.length())\n\t\treturn;\n\tmeshes.setsize(0);\n\tmeshes_fte.setsize(0);\n\ttriangles.setsize(0);\n\tneighbors.setsize(0);\n\tvarrays.setsize(0);\n\tvdata.setsize(0);\n\t*/\n\tint priorverts = numfverts;\n\tint priortris = triangles.length();\n\n\thashtable<sharedvert, uint> mshare(1<<12);\n\tvector<sharedvert> mmap;\n\tvector<triangleinfo> tinfo;\n\n\tif (!noext)\n\t{\n\t\tloopv(emeshes)\n\t\t{\n\t\t\tif (!emeshes[i].hasexplicits)\n\t\t\t\temeshes[i].explicits = spec.meshprops;\n\n\t\t\tloopk(meshoverrides.length())\n\t\t\t{\n\t\t\t\tif (!strcmp(meshoverrides[k].name, emeshes[i].name))\n\t\t\t\t{\n\t\t\t\t\temeshes[i].explicits = meshoverrides[k].props;\n\t\t\t\t\tfor (; k < meshoverrides.length()-1; k++)\n\t\t\t\t\t\tmeshoverrides[k] = meshoverrides[k+1];\n\t\t\t\t\tmeshoverrides.drop();\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tif (spec.ignoresurfname)\n\t{\n\t\tloopv(emeshes)\n\t\t{\n\t\t\temeshes[i].name = getnamekey(\"\");\n\t\t}\n\t}\n\n\tloopv(emeshes)\n\t{\n\t\temesh &em1 = emeshes[i];\n\t\tif(em1.used) continue;\n\t\tfor(int j = i; j < emeshes.length(); j++) \n\t\t{\n\t\t\temesh &em = emeshes[j];\n\t\t\tif(em.used) continue;\n\t\t\tif(strcmp(em.name?em.name:\"\", em1.name?em1.name:\"\") || strcmp(em.material, em1.material) || memcmp(&em.explicits, &em1.explicits, sizeof(em.explicits))) continue;\n\t\t\tint lasttri = emeshes.inrange(j+1) ? emeshes[j+1].firsttri : etriangles.length();\n\t\t\tfor(int k = em.firsttri; k < lasttri; k++)\n\t\t\t{\n\t\t\t\tetriangle &et = etriangles[k];\n\t\t\t\ttriangleinfo &t = tinfo.add();\n\t\t\t\tloopl(3)\n\t\t\t\t{\n\t\t\t\t\tsharedvert v(et.vert[l], et.weld[l]);\n\t\t\t\t\tt.vert[l] = mshare.access(v, mmap.length());\n\t\t\t\t\tif(!mmap.inrange(t.vert[l])) mmap.add(v);\n\t\t\t\t}\n\t\t\t}\n\t\t\tem.used = true;\n\t\t}\n\t\tif(tinfo.empty()) continue;\n\n\t\tmesh &m = meshes.add();\n\t\tif (spec.materialprefix || spec.materialsuffix)\n\t\t{\n\t\t\tchar material[512];\n\t\t\tformatstring(material, \"%s%s%s\", spec.materialprefix?spec.materialprefix:\"\", em1.material, spec.materialsuffix?spec.materialsuffix:\"\");\n\t\t\tm.material = sharestring(material);\n\t\t}\n\t\telse\n\t\t\tm.material = sharestring(em1.material);\n\t\tif (!em1.name)\n\t\t\tm.name = sharestring(em1.material);\n\t\telse\n\t\t\tm.name = sharestring(em1.name);\n\t\tm.firsttri = triangles.length();\n\t\tm.firstvert = numfverts+vmap.length();\n\t\tmaketriangles(tinfo, mmap);\n\t\tm.numtris = triangles.length() - m.firsttri;\n\t\tm.numverts = numfverts+vmap.length() - m.firstvert;\n\t\tm.numskins = 0;\t//we have a default material, so no worries.\n\n\t\tif (spec.materialsuffix && !strncmp(spec.materialsuffix, \"_00_00\", 6))\n\t\t{\t//for qex... populate our skins info\n\t\t\tfor (int s = 0; ; s++)\n\t\t\t{\n\t\t\t\tchar matname[512];\n\t\t\t\tformatstring(matname, \"%s%s_%02d_%02d%s\", spec.materialprefix?spec.materialprefix:\"\", em1.material, s, 0, spec.materialsuffix+6);\n\t\t\t\tFILE *f = fopen(matname, \"rb\");\n\t\t\t\tif (f)\n\t\t\t\t{\n\t\t\t\t\tfclose(f);\t//don't really care.\n\t\t\t\t\tauto &skin = meshskins.add();\n\t\t\t\t\tm.numskins++;\n\t\t\t\t\tskin.firstframe = skinframes.length();\n\t\t\t\t\tskin.countframes = 1;\n\t\t\t\t\tskin.interval = 0.2;\n\n\t\t\t\t\tauto &skinframe = skinframes.add();\n\t\t\t\t\tskinframe.material_idx = sharestring(matname);\n\t\t\t\t\tskinframe.shadertext_idx = 0;\n\t\t\t\t\tfor (skin.countframes = 1; ; skin.countframes++)\n\t\t\t\t\t{\n\t\t\t\t\t\tformatstring(matname, \"%s%s_%02d_%02d%s\", spec.materialprefix?spec.materialprefix:\"\", em1.material, s, skin.countframes, spec.materialsuffix+6);\n\t\t\t\t\t\tf = fopen(matname, \"rb\");\n\t\t\t\t\t\tif (!f)\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tfclose(f);\n\t\t\t\t\t\tauto &skinframe = skinframes.add();\n\t\t\t\t\t\tskinframe.material_idx = sharestring(matname);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse break;\n\t\t\t}\n\t\t}\n\n\t\tmeshprop &mf = meshes_fte.add();\n\t\tmf = em1.explicits;\n\n\t\tprintlastmesh();\n\n\t\tmshare.clear();\n\t\tmmap.setsize(0);\n\t\ttinfo.setsize(0);\n\t}\n\tnumfverts+=vmap.length();\n\n\tif(triangles.length()) makeneighbors(priortris);\n\n\tif(escale != 1) loopv(epositions) epositions[i] *= escale;\n\tif(erotate != Quat(0, 0, 0, 1))\n\t{\n\t\tloopv(epositions) epositions[i].setxyz(erotate.transform(Vec3(epositions[i])));\n\t\tloopv(enormals) enormals[i] = erotate.transform(enormals[i]);\n\t\tloopv(etangents) etangents[i].setxyz(erotate.transform(Vec3(etangents[i])));\n\t\tloopv(ebitangents) ebitangents[i] = erotate.transform(ebitangents[i]);\n\t}\n\tif(emeshtrans != Vec3(0, 0, 0)) loopv(epositions) epositions[i] += emeshtrans;\n\tif(epositions.length()) setupvertexarray<IQM_POSITION>(epositions, IQM_POSITION, IQM_FLOAT, 3, priorverts);\n\tif(etexcoords.length()) setupvertexarray<IQM_TEXCOORD>(etexcoords, IQM_TEXCOORD, IQM_FLOAT, 2, priorverts);\n\tif(enormals.length()) setupvertexarray<IQM_NORMAL>(enormals, IQM_NORMAL, IQM_FLOAT, 3, priorverts);\n\tif(etangents.length())\n\t{\n\t\tif(ebitangents.length() && enormals.length())\n\t\t{\n\t\t\tloopv(etangents) if(ebitangents.inrange(i) && enormals.inrange(i))\n\t\t\t\tetangents[i].w = enormals[i].cross(Vec3(etangents[i])).dot(ebitangents[i]) < 0 ? -1 : 1;\n\t\t}\n\t\tsetupvertexarray<IQM_TANGENT>(etangents, IQM_TANGENT, IQM_FLOAT, 4, priorverts);\n\t}\n\telse if(enormals.length() && etexcoords.length())\n\t{\n\t\tcalctangents(priortris);\n\t\tsetupvertexarray<IQM_TANGENT>(etangents, IQM_TANGENT, IQM_FLOAT, 4, priorverts);\n\t}\n\tif(eblends.length() && !stripbones)\n\t{\n\t\tif (ejoints.length() > 65535)\n\t\t\tsetupvertexarray<IQM_BLENDINDEXES>(eblends, IQM_BLENDINDEXES, IQM_UINT, 4, priorverts);\n\t\telse if (ejoints.length() > 255)\n\t\t\tsetupvertexarray<IQM_BLENDINDEXES>(eblends, IQM_BLENDINDEXES, IQM_USHORT, 4, priorverts);\n\t\telse\n\t\t\tsetupvertexarray<IQM_BLENDINDEXES>(eblends, IQM_BLENDINDEXES, IQM_UBYTE, 4, priorverts);\n\t\tsetupvertexarray<IQM_BLENDWEIGHTS>(eblends, IQM_BLENDWEIGHTS, IQM_UBYTE, 4, priorverts);\n\t}\n\tif(ecolors.length()) setupvertexarray<IQM_COLOR>(ecolors, IQM_COLOR, IQM_UBYTE, 4, priorverts);\n\tloopi(10) if(ecustom[i].length()) setupvertexarray<IQM_CUSTOM>(ecustom[i], IQM_CUSTOM + i, IQM_FLOAT, 4, priorverts);\n\n\t//make sure we keep this data in a usable form so that we can calc framebounds.\n\tif(epositions.length())\n\t{\n\t\tVec4 *o = mpositions.reserve(epositions.length());\n\t\tmpositions.advance(epositions.length());\n\t\tloopv(epositions)\n\t\t\to[i] = epositions[i];\n\t}\n\tif(eblends.length())\n\t{\n\t\tblendcombo *o = mblends.reserve(eblends.length());\n\t\tmblends.advance(eblends.length());\n\t\tloopv(eblends)\n\t\t\to[i] = eblends[i];\n\t}\n\n\t//the generated triangles currently refer to the imported arrays.\n\t//make sure they refer to the final verts\n\tif (priorverts)\n\t\tfor (int i = priortris; i < triangles.length(); i++)\n\t\t{\n\t\t\ttriangles[i].vert[0] += priorverts;\n\t\t\ttriangles[i].vert[1] += priorverts;\n\t\t\ttriangles[i].vert[2] += priorverts;\n\t\t}\n}\n\nvoid makebounds(framebounds &bb, Matrix3x4 *invbase, frame &frame)\n{\n\tvector<Matrix3x4> buf;\n\tbuf.growbuf(joints.length());\n\tbuf.setsize(joints.length());\n\n\t//make sure all final bones have some value, even if its gibberish. should probably ignore verts that depend upon bones not defined in this animation.\n\t//remap<0 means the bone was dropped.\n\tloopv(buf) {buf[i] = Matrix3x4(Quat(0,0,0,1),Vec3(0,0,0));}\n\tloopv(frame.pose) if (frame.pose[i].remap>=0)\n\t{\n\t\tint bone = frame.pose[i].remap;\n\t\tint jparent = frame.pose[i].boneparent;\n\t\tif (jparent >= 0) jparent = frame.pose[jparent].remap;\n\n\t\tif(jparent >= 0) buf[bone] = buf[jparent] * Matrix3x4(frame.pose[i].tr.orient, frame.pose[i].tr.pos, frame.pose[i].tr.scale);\n\t\telse buf[bone] = Matrix3x4(frame.pose[i].tr.orient, frame.pose[i].tr.pos, frame.pose[i].tr.scale);\n\t}\n\n\tloopv(frame.pose) buf[i] *= invbase[i];\n\tloopv(mpositions)\n\t{\n\t\tconst blendcombo &c = mblends[i]; \n\t\tMatrix3x4 m(Vec4(0, 0, 0, 0), Vec4(0, 0, 0, 0), Vec4(0, 0, 0, 0));\n\t\tloopk(4) if(c.weights[k] > 0)\n\t\t\tm += buf[c.bones[k]] * c.weights[k];\n\t\tVec3 p = m.transform(Vec3(mpositions[i]));\n\n\t\tif(!i) bb.bbmin = bb.bbmax = p;\n\t\telse\n\t\t{\n\t\t\tbb.bbmin.x = min(bb.bbmin.x, p.x);\n\t\t\tbb.bbmin.y = min(bb.bbmin.y, p.y);\n\t\t\tbb.bbmin.z = min(bb.bbmin.z, p.z);\n\t\t\tbb.bbmax.x = max(bb.bbmax.x, p.x);\n\t\t\tbb.bbmax.y = max(bb.bbmax.y, p.y);\n\t\t\tbb.bbmax.z = max(bb.bbmax.z, p.z);\n\t\t}\n\t\tdouble xyradius = p.x*p.x + p.y*p.y;\n\t\tbb.xyradius = max(bb.xyradius, xyradius);\n\t\tbb.radius = max(bb.radius, xyradius + p.z*p.z);\n\t}\n\tif(bb.xyradius > 0) bb.xyradius = sqrt(bb.xyradius);\n\tif(bb.radius > 0) bb.radius = sqrt(bb.radius);\n}\n\nvoid makerelativebasepose()\n{\n\tint numbasejoints = min(ejoints.length(), eframes.length() ? eframes[0] : eposes.length());\n\tfor(int i = numbasejoints-1; i >= 0; i--)\n\t{\n\t\tejoint &ej = ejoints[i];\n\t\tif(ej.parent < 0) continue; \n\t\ttransform &parent = eposes[ej.parent], &child = eposes[i];\n\t\tchild.pos = (-parent.orient).transform(child.pos - parent.pos);   \n\t\tchild.orient = (-parent.orient)*child.orient;\n\t\tchild.scale = child.scale / parent.scale;\n\t\tif(child.orient.w > 0) child.orient.flip();\n\t}\n}\n\nbool forcejoints = false;\n\nvoid printlastanim(void)\n{\n\tif (quiet)\n\t\treturn;\n\n\tanim &a = anims[anims.length()-1];\n\tif (a.numframes == 1)\n\t\tprintf(\"    frame %i:\\tname=\\\"%s\\\"\\tfps=%g, %s\\n\", anims.length()-1,\n\t\t\t&stringdata[a.name], a.fps, (a.flags & IQM_LOOP)?\"looped\":\"clamped\");\n\telse\n\t\tprintf(\"    anim %i:\\tname=\\\"%s\\\",\\tframes=%i, fps=%g, %s\\n\", anims.length()-1,\n\t\t\t&stringdata[a.name], a.numframes, a.fps, (a.flags & IQM_LOOP)?\"looped\":\"clamped\");\n\n\tloopv(events_fte)\n\t{\n\t\tif (events_fte[i].anim == (uint)anims.length()-1)\n\t\t\tprintf(\"        pose %g: %x \\\"%s\\\"\\n\", events_fte[i].timestamp*a.fps, events_fte[i].evcode, events_fte[i].evdata_str);\n\t}\n}\n\nvoid printbones(int parent = -1, size_t ind = 1)\n{\n\tchar prefix[256];\n\tif (ind >= sizeof(prefix))\n\t\tind = sizeof(prefix)-1;\n\tmemset(prefix, ' ', ind);\n\tprefix[ind] = 0;\n\n\tloopv(joints)\n\t{\n\t\tif (joints[i].parent == parent)\n\t\t{\t//show as 1-based for consistency with quake.\n\t\t\tif (parent == -1)\n\t\t\t\tconoutf(\"%sbone %i:\\tname=\\\"%s\\\"\\tparent=NONE, group=%i (%f %f %f)\", prefix, i+1, &stringdata[joints[i].name], joints[i].group, joints[i].pos[0], joints[i].pos[1], joints[i].pos[2]);\n\t\t\telse\n\t\t\t\tconoutf(\"%sbone %i:\\tname=\\\"%s\\\"\\tparent=%i, group=%i (%f %f %f)\", prefix, i+1, &stringdata[joints[i].name], joints[i].parent+1, joints[i].group, joints[i].pos[0], joints[i].pos[1], joints[i].pos[2]);\n\t\t\tprintbones(i, ind+1);\n\t\t}\n\t}\n}\nvoid printbonelist()\n{\n\tloopv(joints)\n\t{\n\t\tconoutf(\"bone %i:\\tname=\\\"%s\\\"\\tparent=%i%s, group=%i\", i+1, &stringdata[joints[i].name], joints[i].parent+1, joints[i].parent >= i?\"(ERROR)\":\"\", joints[i].group);\n\t}\n}\n\nint findjoint(const char *name)\n{\n\tloopv(joints)\n\t{\n\t\tif (!strcmp(&stringdata[joints[i].name], name))\n\t\t\treturn i;\n\t}\n\treturn -1;\n}\n\nbool floatcmp(float f1, float f2)\n{\n\tif (f1 != f2)\n\t\treturn true;\n\treturn false;\n}\n\nvoid makeanims(const filespec &spec)\n{\n\tif(escale != 1) loopv(eposes) eposes[i].pos *= escale;\n\tif(erotate != Quat(0, 0, 0, 1)) loopv(ejoints)\n\t{\n\t\tejoint &ej = ejoints[i];\n\t\tif(ej.parent < 0) for(int j = i; j < eposes.length(); j += ejoints.length())\n\t\t{\n\t\t\ttransform &p = eposes[j];\n\t\t\tp.orient = erotate * p.orient;\n\t\t\tp.pos = erotate.transform(p.pos);\n\t\t}\n\t}\n\tint numbasejoints = eframes.length() ? eframes[0] : eposes.length();\n\tif(forcejoints || emeshes.length())\n\t{\n\t\tbool warned = false;\n\t\tint *jr = new int[ejoints.length()];\n\t\tloopv(ejoints)\n\t\t{\n\t\t\tejoint &ej = ejoints[i];\n\t\t\tjr[i] = findjoint(ej.name);\n\t\t\tif (jr[i] >= 0)\n\t\t\t{\n\t\t\t\tbool rigmismatch = false;\n\t\t\t\tif (warned || forcejoints)\n\t\t\t\t\tcontinue;\n\t\t\t\tjoint &j = joints[jr[i]];\n\t\t\t\tloopk(3) if (floatcmp(j.pos[k], eposes[i].pos[k] + (ej.parent>=0?0:ejointtrans[k]))) rigmismatch = true;\n\t\t\t\tloopk(4) if (floatcmp(j.orient[k], eposes[i].orient[k])) rigmismatch = true;\n\t\t\t\tloopk(3) if (floatcmp(j.scale[k], eposes[i].scale[k])) rigmismatch = true;\n\t\t\t\tif (rigmismatch)\n\t\t\t\t{\n\t\t\t\t\twarned = true;\n\t\t\t\t\tconoutf(\"warning: rig mismatch (bone %s)\", ej.name);\n\t\t\t\t}\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tjr[i] = joints.length();\n\t\t\tjoint &j = joints.add();\n\t\t\tMatrix3x4 &m = mjoints.add();\n\t\t\tconst char *name = ej.name;\n\t\t\tint group = -1;\n\t\t\tloopvk(boneoverrides)\n\t\t\t{\n\t\t\t\tif (!strcmp(boneoverrides[k].name, name))\n\t\t\t\t{\n\t\t\t\t\tboneoverrides[k].used = true;\n\t\t\t\t\tif (boneoverrides[k].props.rename)\n\t\t\t\t\t\tname = boneoverrides[k].props.rename;\n\t\t\t\t\tif (boneoverrides[k].props.group >= 0)\n\t\t\t\t\t\tgroup = boneoverrides[k].props.group;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tj.name = sharestring(name);\n\t\t\tif (ej.parent >= 0)\n\t\t\t\tj.parent = findjoint(ejoints[ej.parent].name);\n\t\t\telse\n\t\t\t\tj.parent = -1;\n\t\t\tif (group < 0 && j.parent >= 0)\n\t\t\t\tgroup = joints[j.parent].group;\n\t\t\tif (group < 0)\n\t\t\t\tgroup = 0;\n\t\t\tj.group = group;\n\t\t\tif(i < numbasejoints) \n\t\t\t{\n\t\t\t\tm.invert(Matrix3x4(eposes[i].orient, eposes[i].pos + (ej.parent>=0?Vec3(0,0,0):ejointtrans), eposes[i].scale));\n\t\t\t\tloopk(3) j.pos[k] = eposes[i].pos[k] + (ej.parent>=0?0:ejointtrans[k]);\n\t\t\t\tloopk(4) j.orient[k] = eposes[i].orient[k];\n\t\t\t\tloopk(3) j.scale[k] = eposes[i].scale[k];\n\t\t\t}\n\t\t\telse m.invert(Matrix3x4(Quat(0, 0, 0, 1), Vec3(0, 0, 0), Vec3(1, 1, 1)));\n\t\t\tif(j.parent >= 0) m *= mjoints[j.parent];\n\t\t}\n\t\tloopv(eblends)\n\t\t{\n\t\t\tloopk(eblends[i].sorted)\n\t\t\t{\n\t\t\t\tint b = eblends[i].bones[k];\n\t\t\t\tif (b >= ejoints.length())\n\t\t\t\t\tb = 0;\n\t\t\t\telse\n\t\t\t\t\tb = jr[b];\n\t\t\t\teblends[i].bones[k] = b;\n\t\t\t}\n\t\t}\n\t\tdelete[] jr;\n\t}\n//\tloopv(spec.events)\n//\t\tspec.events[i].evdata_idx = 0;\n\tloopv(eanims)\n\t{\n\t\teanim &ea = eanims[i];\n\t\tif (ea.flags & IQM_UNPACK)\n\t\t{\t//some quake mods suck, and are unable to deal with animations\n\t\t\tfor (int j = ea.startframe, end = eanims.inrange(i+1) ? eanims[i+1].startframe : eframes.length(); j < end && j < ea.endframe; j++)\n\t\t\t{\n\t\t\t\tanim &a = anims.add();\n\t\t\t\tchar nname[256];\n\t\t\t\tformatstring(nname, \"%s_%i\", ea.name, j+1-ea.startframe);\n\t\t\t\ta.name = sharestring(nname);\n\t\t\t\ta.firstframe = frames.length();\n\t\t\t\ta.numframes = 0;\n\t\t\t\ta.fps = ea.fps;\n\t\t\t\ta.flags = ea.flags&~IQM_ALLPRIVATE;\n\n\t\t\t\tint offset = eframes[j], range = (eframes.inrange(j+1) ? eframes[j+1] : eposes.length()) - offset;\n\t\t\t\tif(range <= 0) continue;\n\t\t\t\tframe &fr = frames.add();\n\t\t\t\tloopk(min(range, ejoints.length())) fr.pose.add(frame::framepose(ejoints[k], eposes[offset + k]));\n\t\t\t\tloopk(max(ejoints.length() - range, 0)) fr.pose.add(frame::framepose(ejoints[k], transform(Vec3(0, 0, 0), Quat(0, 0, 0, 1), Vec3(1, 1, 1))));\n\t\t\t\ta.numframes++;\n\n\t\t\t\tprintlastanim();\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tanim &a = anims.add();\n\t\t\ta.name = sharestring(ea.name);\n\t\t\ta.firstframe = frames.length();\n\t\t\ta.numframes = 0;\n\t\t\ta.fps = ea.fps;\n\t\t\ta.flags = ea.flags&~IQM_ALLPRIVATE;\n\n\t\t\tfor(int j = ea.startframe, end = eanims.inrange(i+1) ? eanims[i+1].startframe : eframes.length(); j < end && j <= ea.endframe; j++)\n\t\t\t{\n\t\t\t\tint offset = eframes[j], range = (eframes.inrange(j+1) ? eframes[j+1] : eposes.length()) - offset;\n\t\t\t\tif(range <= 0) continue;\n\t\t\t\tframe &fr = frames.add();\n\t\t\t\tloopk(min(range, ejoints.length()))\n\t\t\t\t{\n\t\t\t\t\tif (ejoints[k].parent < 0)\n\t\t\t\t\t\teposes[offset+k].pos += ejointtrans;\n\t\t\t\t\tfr.pose.add(frame::framepose(ejoints[k], eposes[offset + k]));\n\t\t\t\t}\n\t\t\t\tloopk(max(ejoints.length() - range, 0))\n\t\t\t\t{\n\t\t\t\t\tif (ejoints[k].parent < 0)\n\t\t\t\t\t\tfr.pose.add(frame::framepose(ejoints[k], transform(ejointtrans, Quat(0, 0, 0, 1), Vec3(1, 1, 1))));\n\t\t\t\t\telse\n\t\t\t\t\t\tfr.pose.add(frame::framepose(ejoints[k], transform(Vec3(0, 0, 0), Quat(0, 0, 0, 1), Vec3(1, 1, 1))));\n\t\t\t\t}\n\t\t\t\ta.numframes++;\n\t\t\t}\n\t\t\tloopvj(spec.events)\n\t\t\t{\n\t\t\t\tfloat p;\n\t\t\t\tif (spec.events[j].anim == ~0u)\n\t\t\t\t{\n\t\t\t\t\tif (spec.events[j].timestamp < ea.startframe || spec.events[j].timestamp >= ea.startframe + a.numframes)\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\tp = spec.events[j].timestamp - ea.startframe;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tif (spec.events[j].anim != (uint)i)\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\telse\n\t\t\t\t\t\tp = spec.events[j].timestamp;\n\t\t\t\t}\n\t\t\t\tevent_fte &ev = events_fte.add(spec.events[j]);\n\t\t\t\tev.anim = anims.length()-1;\n\t\t\t\tev.timestamp = p / a.fps;\n\t\t\t}\n\t\t\tprintlastanim();\n\t\t}\n\t}\n\n//\tloopv(spec.events) if (!spec.events[i].evdata_idx)\n//\t{\n//\t\tconoutf(\"event specifies invalid animation from %s\", spec.file);\n//\t}\n}\n\nbool resetimporter(const filespec &spec, bool reuse = false)\n{\n\tif(reuse)\n\t{\n\t\tejoints.setsize(0);\n\t\tevarrays.setsize(0);\n\n\t\treturn false;\n\t}\n\n\tvmap.setsize(0);\n\tepositions.setsize(0);\n\tetexcoords.setsize(0);\n\tenormals.setsize(0);\n\tetangents.setsize(0);\n\tebitangents.setsize(0);\n\tecolors.setsize(0);\n\tloopi(10) ecustom[i].setsize(0);\n\teblends.setsize(0);\n\tetriangles.setsize(0);\n\tesmoothindexes.setsize(0);\n\tesmoothedges.setsize(0);\n\tesmoothgroups.setsize(0);\n\tesmoothgroups.add();\n\tejoints.setsize(0);\n\teposes.setsize(0);\n\teframes.setsize(0);\n\teanims.setsize(0);\n\temeshes.setsize(0);\n\tevarrays.setsize(0);\n\n\temeshtrans = gmeshtrans+spec.translate;\n\tejointtrans = spec.translate;\n\terotate = spec.rotate;\n\tescale = gscale*spec.scale;\n\n\treturn true;\n}\n\nbool parseiqe(stream *f)\n{\n\tconst char *curmesh = getnamekey(\"\"), *curmaterial = getnamekey(\"\");\n\tbool needmesh = true;\n\tint fmoffset = 0;\n\tchar buf[512];\n\tif(!f->getline(buf, sizeof(buf))) return false;\n\tif(!strchr(buf, '#') || strstr(buf, \"# Inter-Quake Export\") != strchr(buf, '#')) return false;\n\twhile(f->getline(buf, sizeof(buf)))\n\t{\n\t\tchar *c = buf;\n\t\twhile(isspace(*c)) ++c;\n\t\tif(isalpha(c[0]) && isalnum(c[1]) && (!c[2] || isspace(c[2]))) switch(*c++)\n\t\t{\n\t\tcase 'v':\n\t\t\tswitch(*c++)\n\t\t\t{\n\t\t\tcase 'p': epositions.add(parseattribs4(c, Vec4(0, 0, 0, 1))); continue;\n\t\t\tcase 't': etexcoords.add(parseattribs4(c)); continue;\n\t\t\tcase 'n': enormals.add(parseattribs3(c)); continue;\n\t\t\tcase 'x':\n\t\t\t{\n\t\t\t\tVec4 tangent(parseattribs3(c), 0);\n\t\t\t\tVec3 bitangent(0, 0, 0);\n\t\t\t\tbitangent.x = parseattrib(c);\n\t\t\t\tif(maybeparseattrib(c, bitangent.y))\n\t\t\t\t{\n\t\t\t\t\tbitangent.z = parseattrib(c);\n\t\t\t\t\tebitangents.add(bitangent);\n\t\t\t\t}\n\t\t\t\telse tangent.w = bitangent.x;\n\t\t\t\tetangents.add(tangent);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tcase 'b': eblends.add(parseblends(c)); continue;\n\t\t\tcase 'c': ecolors.add(parseattribs4(c, Vec4(0, 0, 0, 1))); continue;\n\t\t\tcase '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':\n\t\t\t{\n\t\t\t\tint n = c[-1] - '0';\n\t\t\t\tecustom[n].add(parseattribs4(c));\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tcase 's':\n\t\t\t\tparseindex(c, esmoothindexes.add());\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 'p':\n\t\t{\n\t\t\ttransform t;\n\t\t\tswitch(*c++)\n\t\t\t{\n\t\t\tcase 'q':\n\t\t\t\t{\n\t\t\t\t\tt.pos = parseattribs3(c);\n\t\t\t\t\tloopk(3) t.orient[k] = parseattrib(c);\n\t\t\t\t\tt.orient.restorew();\n\t\t\t\t\tdouble w = parseattrib(c, t.orient.w);\n\t\t\t\t\tif(w != t.orient.w)\n\t\t\t\t\t{\n\t\t\t\t\t\tt.orient.w = w;\n\t\t\t\t\t\tt.orient.normalize();\n//        double x2 = f.orient.x*f.orient.x, y2 = f.orient.y*f.orient.y, z2 = f.orient.z*f.orient.z, w2 = f.orient.w*f.orient.w, s2 = x2 + y2 + z2 + w2;\n//        f.orient.x = keepsign(f.orient.x, sqrt(max(1.0 - (w2 + y2 + z2) / s2, 0.0)));\n//        f.orient.y = keepsign(f.orient.y, sqrt(max(1.0 - (w2 + x2 + z2) / s2, 0.0)));\n//        f.orient.z = keepsign(f.orient.z, sqrt(max(1.0 - (w2 + x2 + y2) / s2, 0.0)));\n//        f.orient.w = keepsign(f.orient.w, sqrt(max(1.0 - (x2 + y2 + z2) / s2, 0.0)));\n\t\t\t\t\t}\n\t\t\t\t\tif(t.orient.w > 0) t.orient.flip();\n\t\t\t\t\tt.scale = parseattribs3(c, Vec3(1, 1, 1));\n\t\t\t\t\teposes.add(t);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\tcase 'm':\n\t\t\t\t{\n\t\t\t\t\tt.pos = parseattribs3(c);\n\t\t\t\t\tMatrix3x3 m;\n\t\t\t\t\tm.a = parseattribs3(c);\n\t\t\t\t\tm.b = parseattribs3(c);\n\t\t\t\t\tm.c = parseattribs3(c);\n\t\t\t\t\tVec3 mscale(Vec3(m.a.x, m.b.x, m.c.x).magnitude(), Vec3(m.a.y, m.b.y, m.c.y).magnitude(), Vec3(m.a.z, m.b.z, m.c.z).magnitude());\n\t\t\t\t\t// check determinant for sign of scaling\n\t\t\t\t\tif(m.determinant() < 0) mscale = -mscale;\n\t\t\t\t\tm.a /= mscale;\n\t\t\t\t\tm.b /= mscale;\n\t\t\t\t\tm.c /= mscale;\n\t\t\t\t\tt.orient = Quat(m);\n\t\t\t\t\tif(t.orient.w > 0) t.orient.flip();\n\t\t\t\t\tt.scale = parseattribs3(c, Vec3(1, 1, 1)) * mscale;\n\t\t\t\t\teposes.add(t);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\tcase 'a':\n\t\t\t\t{\n\t\t\t\t\tt.pos = parseattribs3(c);\n\t\t\t\t\tVec3 rot = parseattribs3(c);\n\t\t\t\t\tt.orient = Quat::fromangles(rot);\n\t\t\t\t\tt.scale = parseattribs3(c, Vec3(1, 1, 1));\n\t\t\t\t\teposes.add(t);\n\t\t\t\t\tcontinue;\n\t\t\t\t} \n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t\tcase 'f':\n\t\t\tswitch(*c++)\n\t\t\t{\n\t\t\tcase 'a': \n\t\t\t\t{\n\t\t\t\t\tint i1 = 0, i2 = 0, i3 = 0;\n\t\t\t\t\tif(!parseindex(c, i1) || !parseindex(c, i2)) continue;\n\t\t\t\t\tif(needmesh)\n\t\t\t\t\t{\n\t\t\t\t\t\temeshes.add(emesh(curmesh, curmaterial, etriangles.length()));\n\t\t\t\t\t\tneedmesh = false;\n\t\t\t\t\t}\n\t\t\t\t\tif(i1 < 0) i1 = max(epositions.length() + i1, 0);\n\t\t\t\t\tif(i2 < 0) i2 = max(epositions.length() + i2, 0);\n\t\t\t\t\twhile(parseindex(c, i3))\n\t\t\t\t\t{\n\t\t\t\t\t\tif(i3 < 0) i3 = max(epositions.length() + i3, 0);\n\t\t\t\t\t\tesmoothgroups.last().flags |= esmoothgroup::F_USED;\n\t\t\t\t\t\tetriangles.add(etriangle(i1, i2, i3, esmoothgroups.length()-1));\n\t\t\t\t\t\ti2 = i3;\n\t\t\t\t\t}\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\tcase 'm': \n\t\t\t\t{\n\t\t\t\t\tint i1 = 0, i2 = 0, i3 = 0;\n\t\t\t\t\tif(!parseindex(c, i1) || !parseindex(c, i2)) continue;\n\t\t\t\t\tif(needmesh)\n\t\t\t\t\t{\n\t\t\t\t\t\temeshes.add(emesh(curmesh, curmaterial, etriangles.length()));\n\t\t\t\t\t\tneedmesh = false;\n\t\t\t\t\t}\n\t\t\t\t\ti1 = i1 < 0 ? max(epositions.length() + i1, 0) : (fmoffset + i1);\n\t\t\t\t\ti2 = i2 < 0 ? max(epositions.length() + i2, 0) : (fmoffset + i2);\n\t\t\t\t\twhile(parseindex(c, i3))\n\t\t\t\t\t{\n\t\t\t\t\t\ti3 = i3 < 0 ? max(epositions.length() + i3, 0) : (fmoffset + i3);\n\t\t\t\t\t\tesmoothgroups.last().flags |= esmoothgroup::F_USED;\n\t\t\t\t\t\tetriangles.add(etriangle(i1, i2, i3, esmoothgroups.length()-1));\n\t\t\t\t\t\ti2 = i3;\n\t\t\t\t\t}\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\tcase 's':\n\t\t\t\t{\n\t\t\t\t\tint i1 = 0, i2 = 0, i3 = 0;\n\t\t\t\t\tuchar flags = 0;\n\t\t\t\t\tif(!parseindex(c, i1) || !parseindex(c, i2) || !parseindex(c, i3)) continue;\n\t\t\t\t\tflags |= clamp(i1, 0, 1);\n\t\t\t\t\tflags |= clamp(i2, 0, 1)<<1;\n\t\t\t\t\tflags |= clamp(i3, 0, 1)<<2;\n\t\t\t\t\tesmoothgroups.last().flags |= esmoothgroup::F_USED;\n\t\t\t\t\twhile(parseindex(c, i3))\n\t\t\t\t\t{\n\t\t\t\t\t\tesmoothedges.add(flags | 4);\n\t\t\t\t\t\tflags = 1 | ((flags & 4) >> 1) | (clamp(i3, 0, 1)<<2);\n\t\t\t\t\t} \n\t\t\t\t\tesmoothedges.add(flags);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t\tchar *args = c;\n\t\twhile(*args && !isspace(*args)) args++;\n\t\tif(!strncmp(c, \"smoothgroup\", max(int(args-c), 11)))\n\t\t{\n\t\t\tif(esmoothgroups.last().flags & esmoothgroup::F_USED) esmoothgroups.dup();\n\t\t\tparseindex(args, esmoothgroups.last().key);\n\t\t}\n\t\telse if(!strncmp(c, \"smoothangle\", max(int(args-c), 11)))\n\t\t{\n\t\t\tif(esmoothgroups.last().flags & esmoothgroup::F_USED) esmoothgroups.dup();\n\t\t\tdouble angle = parseattrib(args, 0);\n\t\t\tesmoothgroups.last().angle = fabs(cos(clamp(angle, -180.0, 180.0) * M_PI/180));\n\t\t}\n\t\telse if(!strncmp(c, \"smoothuv\", max(int(args-c), 8)))\n\t\t{\n\t\t\tif(esmoothgroups.last().flags & esmoothgroup::F_USED) esmoothgroups.dup();\n\t\t\tint val = 1;\n\t\t\tif(parseindex(args, val) && val <= 0) esmoothgroups.last().flags &= ~esmoothgroup::F_UVSMOOTH;\n\t\t\telse esmoothgroups.last().flags |= esmoothgroup::F_UVSMOOTH;\n\t\t}\n\t\telse if(!strncmp(c, \"mesh\", max(int(args-c), 4)))\n\t\t{ \n\t\t\tcurmesh = getnamekey(trimname(args));\n\t\t\tif(emeshes.empty() || emeshes.last().name != curmesh) needmesh = true;\n\t\t\tfmoffset = epositions.length();\n\n#if 0\n\t\t\temesh &m = emeshes.add();\n\t\t\tm.firsttri = etriangles.length();\n\t\t\tfmoffset = epositions.length();\n\t\t\tparsename(args, m.name);\n#endif\n\t\t}\n\t\telse if(!strncmp(c, \"material\", max(int(args-c), 8)))\n\t\t{\n\t\t\tcurmaterial = getnamekey(trimname(args));\n\t\t\tif(emeshes.empty() || emeshes.last().material != curmaterial) needmesh = true;\n//\t\t\tif(emeshes.length()) parsename(c, emeshes.last().material);\n\t\t}\n\t\telse if(!strncmp(c, \"joint\", max(int(args-c), 5)))\n\t\t{\n\t\t\tejoint &j = ejoints.add();\n\t\t\tj.name = getnamekey(trimname(args));\n\t\t\tparseindex(args, j.parent);\n\t\t}\n\t\telse if(!strncmp(c, \"vertexarray\", max(int(args-c), 11)))\n\t\t{\n\t\t\tevarray &va = evarrays.add();\n\t\t\tva.type = findvertexarraytype(trimname(args));\n\t\t\tva.format = findvertexarrayformat(trimname(args));\n\t\t\tva.size = strtol(args, &args, 10);\n\t\t\tcopystring(va.name, trimname(args));\n\t\t}\n\t\telse if(!strncmp(c, \"animation\", max(int(args-c), 9)))\n\t\t{\n\t\t\teanim &a = eanims.add();\n\t\t\ta.name = getnamekey(trimname(args));\n\t\t\ta.startframe = eframes.length();\n\t\t\tif(!eframes.length() || eframes.last() != eposes.length()) eframes.add(eposes.length());\n\t\t}\n\t\telse if(!strncmp(c, \"frame\", max(int(args-c), 5)))\n\t\t{\n\t\t\tif(eanims.length() && eframes.length() && eframes.last() != eposes.length()) eframes.add(eposes.length()); \n\t\t}\n\t\telse if(!strncmp(c, \"framerate\", max(int(args-c), 9)))\n\t\t{\n\t\t\tif(eanims.length())\n\t\t\t{\n\t\t\t\tdouble fps = parseattrib(args);\n\t\t\t\teanims.last().fps = max(fps, 0.0);\n\t\t\t}\n\t\t}\n\t\telse if(!strncmp(c, \"loop\", max(int(args-c), 4)))\n\t\t{\n\t\t\tif(eanims.length()) eanims.last().flags |= IQM_LOOP;\n\t\t}\n\t\telse if(!strncmp(c, \"comment\", max(int(args-c), 7)))\n\t\t{\n\t\t\tif(commentdata.length()) break;\n\t\t\tfor(;;)\n\t\t\t{\n\t\t\t\tsize_t len = f->read(commentdata.reserve(1024), 1024);\n\t\t\t\tcommentdata.advance(len);\n\t\t\t\tif(len < 1024) { commentdata.add('\\0'); break; }\n\t\t\t}\n\t\t}\n\t}\n\n\treturn true;\n}\n\nbool loadiqe(const char *filename, const filespec &spec)\n{\n\tint numfiles = 0;\n\twhile(filename)\n\t{\n\t\tconst char *endfile = strchr(filename, ',');\n\t\tconst char *file = endfile ? newstring(filename, endfile-filename) : filename;\n\t\tstream *f = openfile(file, \"r\");\n\t\tif(f)\n\t\t{\n\t\t\tresetimporter(spec, numfiles > 0); \n\t\t\tif(parseiqe(f)) numfiles++;\n\t\t\tdelete f;\n\t\t}\n\n\t\tif(!endfile) break;\n\n\t\tdelete[] file;\n\t\tfilename = endfile+1; \n\t}\n\n\tif(!numfiles) return false;\n\n\tif(eanims.length() == 1)\n\t{\n\t\teanim &a = eanims.last();\n\t\tif(spec.name) a.name = spec.name;\n\t\tif(spec.fps > 0) a.fps = spec.fps;\n\t\ta.flags |= spec.flags;\n\t\tif(spec.endframe >= 0) a.endframe = a.startframe + spec.endframe;\n\t\telse if(spec.endframe < -1) a.endframe = a.startframe + max(eframes.length() - a.startframe + spec.endframe + 1, 0);\n\t\ta.startframe += spec.startframe;\n\t}\n\n\tmakeanims(spec);\n\tif(emeshes.length())\n\t{\n\t\tsmoothverts();\n\t\tmakemeshes(spec);\n\t}\n\n\treturn true;\n}\n\nstruct md5weight\n{\n\tint joint;\n\tdouble bias;\n\tVec3 pos;\n};\n\nstruct md5vert\n{\n\tdouble u, v;\n\tuint start, count;\n};\n\nstruct md5hierarchy\n{\n\tconst char *name;\n\tint parent, flags, start;\n};\n\nvector<md5weight> weightinfo;\nvector<md5vert> vertinfo;\n\nvoid buildmd5verts()\n{\n\tloopv(vertinfo)\n\t{\n\t\tmd5vert &v = vertinfo[i];\n\t\tVec3 pos(0, 0, 0);\n\t\tloopk(v.count)\n\t\t{\n\t\t\tmd5weight &w = weightinfo[v.start+k];\n\t\t\ttransform &j = eposes[w.joint];\n\t\t\tpos += (j.orient.transform(w.pos) + j.pos)*w.bias;\n\t\t}\n\t\tepositions.add(Vec4(pos, 1));\n\t\tetexcoords.add(Vec4(v.u, v.v, 0, 0));\n\n\t\tblendcombo &c = eblends.add();\n\t\tloopj(v.count)\n\t\t{\n\t\t\tmd5weight &w = weightinfo[v.start+j];\n\t\t\tc.addweight(w.bias, w.joint);\n\t\t}\n\t\tc.finalize();\n\t}\n}\n\nvoid parsemd5mesh(stream *f, char *buf, size_t bufsize)\n{\n\tmd5weight w;\n\tmd5vert v;\n\tetriangle t(0, 0, 0, 0);\n\tint index, firsttri = etriangles.length(), firstvert = vertinfo.length(), firstweight = weightinfo.length(), numtris = 0, numverts = 0, numweights = 0;\n\temesh m;\n\n\twhile(f->getline(buf, bufsize) && buf[0]!='}')\n\t{\n\t\tif(strstr(buf, \"// meshes:\"))\n\t\t{\n\t\t\tchar *start = strchr(buf, ':')+1;\n\t\t\tif(*start==' ') start++;\n\t\t\tchar *end = start + strlen(start)-1;\n\t\t\twhile(end >= start && isspace(*end)) end--;\n\t\t\tend[1] = '\\0';\n\t\t\tm.name = getnamekey(start);\n\t\t}\n\t\telse if(strstr(buf, \"shader\"))\n\t\t{\n\t\t\tchar *start = strchr(buf, '\"'), *end = start ? strchr(start+1, '\"') : NULL;\n\t\t\tif(start && end)\n\t\t\t{\n\t\t\t\t*end = '\\0';\n\t\t\t\tm.material = getnamekey(start+1);\n\t\t\t}\n\t\t}\n\t\telse if(sscanf(buf, \" numverts %d\", &numverts)==1)\n\t\t{\n\t\t\tnumverts = max(numverts, 0);\n\t\t\tif(numverts)\n\t\t\t{\n\t\t\t\tvertinfo.reserve(numverts);\n\t\t\t\tvertinfo.advance(numverts);\n\t\t\t}\n\t\t}\n\t\telse if(sscanf(buf, \" numtris %d\", &numtris)==1)\n\t\t{\n\t\t\tnumtris = max(numtris, 0);\n\t\t\tif(numtris)\n\t\t\t{\n\t\t\t\tetriangles.reserve(numtris);\n\t\t\t\tetriangles.advance(numtris);\n\t\t\t}\n\t\t\tm.firsttri = firsttri;\n\t\t}\n\t\telse if(sscanf(buf, \" numweights %d\", &numweights)==1)\n\t\t{\n\t\t\tnumweights = max(numweights, 0);\n\t\t\tif(numweights)\n\t\t\t{\n\t\t\t\tweightinfo.reserve(numweights);\n\t\t\t\tweightinfo.advance(numweights);\n\t\t\t}\n\t\t}\n\t\telse if(sscanf(buf, \" vert %d ( %lf %lf ) %u %u\", &index, &v.u, &v.v, &v.start, &v.count)==5)\n\t\t{\n\t\t\tif(index>=0 && index<numverts)\n\t\t\t{\n\t\t\t\tv.start += firstweight;\n\t\t\t\tvertinfo[firstvert + index] = v;\n\t\t\t}\n\t\t}\n\t\telse if(sscanf(buf, \" tri %d %u %u %u\", &index, &t.vert[0], &t.vert[1], &t.vert[2])==4)\n\t\t{\n\t\t\tif(index>=0 && index<numtris)\n\t\t\t{\n\t\t\t\tloopk(3) t.vert[k] += firstvert;\n\t\t\t\tetriangles[firsttri + index] = t;\n\t\t\t}\n\t\t}\n\t\telse if(sscanf(buf, \" weight %d %d %lf ( %lf %lf %lf ) \", &index, &w.joint, &w.bias, &w.pos.x, &w.pos.y, &w.pos.z)==6)\n\t\t{\n\t\t\tif(index>=0 && index<numweights) weightinfo[firstweight + index] = w;\n\t\t}\n\t}\n\n\tif(numtris && numverts) emeshes.add(m);\n}\n\nbool loadmd5mesh(const char *filename, const filespec &spec)\n{\n\tstream *f = openfile(filename, \"r\");\n\tif(!f) return false;\n\n\tresetimporter(spec);\n\tesmoothgroups[0].flags |= esmoothgroup::F_UVSMOOTH;\n\n\tchar buf[512];\n\twhile(f->getline(buf, sizeof(buf)))\n\t{\n\t\tint tmp;\n\t\tif(sscanf(buf, \" MD5Version %d\", &tmp)==1)\n\t\t{\n\t\t\tif(tmp!=10) { delete f; return false; }\n\t\t}\n\t\telse if(sscanf(buf, \" numJoints %d\", &tmp)==1)\n\t\t{\n\t\t\tif(tmp<1 || (joints.length() && tmp != joints.length())) { delete f; return false; }\n\t\t}\n\t\telse if(sscanf(buf, \" numMeshes %d\", &tmp)==1)\n\t\t{\n\t\t\tif(tmp<1) { delete f; return false; }\n\t\t}\n\t\telse if(strstr(buf, \"joints {\"))\n\t\t{\n\t\t\tejoint j;\n\t\t\ttransform p;\n\t\t\twhile(f->getline(buf, sizeof(buf)) && buf[0]!='}')\n\t\t\t{\n\t\t\t\tchar *c = buf;\n\t\t\t\tj.name = getnamekey(trimname(c));\n\t\t\t\tif(sscanf(c, \" %d ( %lf %lf %lf ) ( %lf %lf %lf )\",\n\t\t\t\t\t&j.parent, &p.pos.x, &p.pos.y, &p.pos.z,\n\t\t\t\t\t&p.orient.x, &p.orient.y, &p.orient.z)==7)\n\t\t\t\t{\n\t\t\t\t\tp.orient.restorew();\n\t\t\t\t\tp.scale = Vec3(1, 1, 1);\n\t\t\t\t\tejoints.add(j);\n\t\t\t\t\teposes.add(p);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse if(strstr(buf, \"mesh {\"))\n\t\t{\n\t\t\tparsemd5mesh(f, buf, sizeof(buf));\n\t\t}\n\t}\n\n\tdelete f;\n\n\tbuildmd5verts();\n\tmakerelativebasepose();\n\tmakeanims(spec);\n\tsmoothverts();\n\tmakemeshes(spec);\n\n\treturn true;\n}\n\nbool loadmd5anim(const char *filename, const filespec &spec)\n{\n\tstream *f = openfile(filename, \"r\");\n\tif(!f) return false;\n\n\tresetimporter(spec);\n\n\tvector<md5hierarchy> hierarchy;\n\tvector<transform> baseframe;\n\tint animdatalen = 0, animframes = 0, frameoffset = eposes.length(), firstframe = eframes.length();\n\tdouble framerate = 0;\n\tdouble *animdata = NULL;\n\tchar buf[512];\n\twhile(f->getline(buf, sizeof(buf)))\n\t{\n\t\tint tmp;\n\t\tif(sscanf(buf, \" MD5Version %d\", &tmp)==1)\n\t\t{\n\t\t\tif(tmp!=10) { delete f; return false; }\n\t\t}\n\t\telse if(sscanf(buf, \" numJoints %d\", &tmp)==1)\n\t\t{\n\t\t\tif(tmp<1) { delete f; return false; }\n\t\t}\n\t\telse if(sscanf(buf, \" numFrames %d\", &animframes)==1)\n\t\t{\n\t\t\tif(animframes<1) { delete f; return false; }\n\t\t}\n\t\telse if(sscanf(buf, \" frameRate %lf\", &framerate)==1);\n\t\telse if(sscanf(buf, \" numAnimatedComponents %d\", &animdatalen)==1)\n\t\t{\n\t\t\tif(animdatalen>0) animdata = new double[animdatalen];\n\t\t}\n\t\telse if(strstr(buf, \"bounds {\"))\n\t\t{\n\t\t\twhile(f->getline(buf, sizeof(buf)) && buf[0]!='}');\n\t\t}\n\t\telse if(strstr(buf, \"hierarchy {\"))\n\t\t{\n\t\t\twhile(f->getline(buf, sizeof(buf)) && buf[0]!='}')\n\t\t\t{\n\t\t\t\tchar *c = buf;\n\t\t\t\tmd5hierarchy h;\n\t\t\t\th.name = getnamekey(trimname(c));\n\t\t\t\tif(sscanf(c, \" %d %d %d\", &h.parent, &h.flags, &h.start)==3)\n\t\t\t\t\thierarchy.add(h);\n\t\t\t}\n\t\t\tif(hierarchy.empty()) { delete f; return false; }\n\t\t\tloopv(hierarchy)\n\t\t\t{\n\t\t\t\tmd5hierarchy &h = hierarchy[i];\n\t\t\t\tejoint &j = ejoints.add();\n\t\t\t\tj.name = h.name;\n\t\t\t\tj.parent = h.parent;\n\t\t\t}\n\t\t}\n\t\telse if(strstr(buf, \"baseframe {\"))\n\t\t{\n\t\t\twhile(f->getline(buf, sizeof(buf)) && buf[0]!='}')\n\t\t\t{\n\t\t\t\ttransform j;\n\t\t\t\tif(sscanf(buf, \" ( %lf %lf %lf ) ( %lf %lf %lf )\", &j.pos.x, &j.pos.y, &j.pos.z, &j.orient.x, &j.orient.y, &j.orient.z)==6)\n\t\t\t\t{\n\t\t\t\t\tj.orient.restorew();\n\t\t\t\t\tj.scale = Vec3(1, 1, 1);\n\t\t\t\t\tbaseframe.add(j);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif(baseframe.length()!=hierarchy.length()) { delete f; return false; }\n\t\t\teposes.reserve(animframes*baseframe.length());\n\t\t\teposes.advance(animframes*baseframe.length());\n\t\t}\n\t\telse if(sscanf(buf, \" frame %d\", &tmp)==1)\n\t\t{\n\t\t\tfor(int numdata = 0; f->getline(buf, sizeof(buf)) && buf[0]!='}';)\n\t\t\t{\n\t\t\t\tfor(char *src = buf, *next = src; numdata < animdatalen; numdata++, src = next)\n\t\t\t\t{\n\t\t\t\t\tanimdata[numdata] = strtod(src, &next);\n\t\t\t\t\tif(next <= src) break;\n\t\t\t\t}\n\t\t\t}\n\t\t\tint offset = frameoffset + tmp*baseframe.length();\n\t\t\teframes.add(offset);\n\t\t\tloopv(baseframe)\n\t\t\t{\n\t\t\t\tmd5hierarchy &h = hierarchy[i];\n\t\t\t\ttransform j = baseframe[i];\n\t\t\t\tif(h.start < animdatalen && h.flags)\n\t\t\t\t{\n\t\t\t\t\tdouble *jdata = &animdata[h.start];\n\t\t\t\t\tif(h.flags&1) j.pos.x = *jdata++;\n\t\t\t\t\tif(h.flags&2) j.pos.y = *jdata++;\n\t\t\t\t\tif(h.flags&4) j.pos.z = *jdata++;\n\t\t\t\t\tif(h.flags&8) j.orient.x = *jdata++;\n\t\t\t\t\tif(h.flags&16) j.orient.y = *jdata++;\n\t\t\t\t\tif(h.flags&32) j.orient.z = *jdata++;\n\t\t\t\t\tj.orient.restorew();\n\t\t\t\t}\n\t\t\t\teposes[offset + i] = j;\n\t\t\t}\n\t\t}\n\t}\n\n\tif(animdata) delete[] animdata;\n\tdelete f;\n\n\teanim &a = eanims.add();\n\tif(spec.name) a.name = getnamekey(spec.name);\n\telse\n\t{\n\t\tstring name;\n\t\tcopystring(name, filename);\n\t\tchar *end = strrchr(name, '.');\n\t\tif(end) *end = '\\0';\n\t\ta.name = getnamekey(name);\n\t}\n\ta.startframe = firstframe;\n\ta.fps = spec.fps > 0 ? spec.fps : framerate;\n\ta.flags = spec.flags;\n\tif(spec.endframe >= 0) a.endframe = a.startframe + spec.endframe;\n\telse if(spec.endframe < -1) a.endframe = a.startframe + max(eframes.length() - a.startframe + spec.endframe + 1, 0);\n\ta.startframe += spec.startframe;\n\n\tmakeanims(spec);\n\n\treturn true;\n}\n\nnamespace smd\n{\n\nbool skipcomment(char *&curbuf)\n{\n\twhile(*curbuf && isspace(*curbuf)) curbuf++;\n\tswitch(*curbuf)\n\t{\n\t\tcase '#':\n\t\tcase ';':\n\t\tcase '\\r':\n\t\tcase '\\n':\n\t\tcase '\\0':\n\t\t\treturn true;\n\t\tcase '/':\n\t\t\tif(curbuf[1] == '/') return true;\n\t\t\tbreak;\n\t}\n\treturn false;\n}\n\nvoid skipsection(stream *f, char *buf, size_t bufsize)\n{\n\twhile(f->getline(buf, bufsize))\n\t{\n\t\tchar *curbuf = buf;\n\t\tif(skipcomment(curbuf)) continue;\n\t\tif(!strncmp(curbuf, \"end\", 3)) break;\n\t}\n}\n\nvoid readname(char *&curbuf, char *name, size_t namesize)\n{\n\tchar *curname = name;\n\twhile(*curbuf && isspace(*curbuf)) curbuf++;\n\tbool allowspace = false;\n\tif(*curbuf == '\"') { curbuf++; allowspace = true; }\n\twhile(*curbuf)\n\t{\n\t\tchar c = *curbuf++;\n\t\tif(c == '\"') break;\n\t\tif(isspace(c) && !allowspace) break;\n\t\tif(curname < &name[namesize-1]) *curname++ = c;\n\t}\n\t*curname = '\\0';\n}\n\nvoid readnodes(stream *f, char *buf, size_t bufsize)\n{\n\twhile(f->getline(buf, bufsize))\n\t{\n\t\tchar *curbuf = buf;\n\t\tif(skipcomment(curbuf)) continue;\n\t\tif(!strncmp(curbuf, \"end\", 3)) break;\n\t\tint id = strtol(curbuf, &curbuf, 10);\n\t\tstring name;\n\t\treadname(curbuf, name, sizeof(name));\n\t\tint parent = strtol(curbuf, &curbuf, 10);\n\t\tif(id < 0 || id > 255 || parent > 255 || !name[0] || (ejoints.inrange(id) && ejoints[id].name)) continue;\n\t\tejoint j;\n\t\tj.name = getnamekey(name);\n\t\tj.parent = parent;\n\t\twhile(ejoints.length() <= id) ejoints.add();\n\t\tejoints[id] = j;\n\t}\n}\n\nvoid readmaterial(char *&curbuf, char *mat, char *name, size_t matsize)\n{\n\tchar *curmat = mat;\n\twhile(*curbuf && isspace(*curbuf)) curbuf++;\n\tchar *ext = NULL;\n\twhile(*curbuf)\n\t{\n\t\tchar c = *curbuf++;\n\t\tif(isspace(c)) break;\n\t\tif(c == '.' && !ext) ext = curmat;\n\t\tif(curmat < &mat[matsize-1]) *curmat++ = c;\n\t}\n\t*curmat = '\\0';\n\tif(!ext) ext = curmat;\n\tmemcpy(name, mat, ext - mat);\n\tname[ext - mat] = '\\0';\n}\n\nvoid readskeleton(stream *f, char *buf, size_t bufsize)\n{\n\tint frame = -1, firstpose = -1;\n\twhile(f->getline(buf, bufsize))\n\t{\n\t\tchar *curbuf = buf;\n\t\tif(skipcomment(curbuf)) continue;\n\t\tif(sscanf(curbuf, \" time %d\", &frame) == 1) continue;\n\t\telse if(!strncmp(curbuf, \"end\", 3)) break;\n\t\telse if(frame != 0) continue;\n\t\tint bone;\n\t\tVec3 pos, rot;\n\t\tif(sscanf(curbuf, \" %d %lf %lf %lf %lf %lf %lf\", &bone, &pos.x, &pos.y, &pos.z, &rot.x, &rot.y, &rot.z) != 7)\n\t\t\tcontinue;\n\t\tif(!ejoints.inrange(bone))\n\t\t\tcontinue;\n\t\tif(firstpose < 0)\n\t\t{\n\t\t\tfirstpose = eposes.length();\n\t\t\teposes.reserve(ejoints.length());\n\t\t\teposes.advance(ejoints.length());\n\t\t}\n\t\ttransform p(pos, Quat::fromangles(rot));\n\t\teposes[firstpose + bone] = p;\n\t}\n}\n\nvoid readtriangles(stream *f, char *buf, size_t bufsize)\n{\n\temesh m;\n\twhile(f->getline(buf, bufsize))\n\t{\n\t\tchar *curbuf = buf;\n\t\tif(skipcomment(curbuf)) continue;\n\t\tif(!strncmp(curbuf, \"end\", 3)) break;\n\t\tstring name, material;\n\t\treadmaterial(curbuf, material, name, sizeof(material));\n\t\tif(!m.name || strcmp(m.name, name))\n\t\t{\n\t\t\tif(m.name && etriangles.length() > m.firsttri) emeshes.add(m);\n\t\t\tm.name = getnamekey(name);\n\t\t\tm.material = getnamekey(material);\n\t\t\tm.firsttri = etriangles.length();\n\t\t}\n\t\tVec4 *pos = epositions.reserve(3) + 2, *tc = etexcoords.reserve(3) + 2;\n\t\tVec3 *norm = enormals.reserve(3) + 2;\n\t\tblendcombo *c = eblends.reserve(3) + 2;\n\t\tloopi(3)\n\t\t{\n\t\t\tchar *curbuf;\n\t\t\tdo\n\t\t\t{\n\t\t\t\tif(!f->getline(buf, bufsize)) goto endsection;\n\t\t\t\tcurbuf = buf;\n\t\t\t} while(skipcomment(curbuf));\n\t\t\tint parent = -1, numlinks = 0, len = 0;\n\t\t\tif(sscanf(curbuf, \" %d %lf %lf %lf %lf %lf %lf %lf %lf %d%n\", &parent, &pos->x, &pos->y, &pos->z, &norm->x, &norm->y, &norm->z, &tc->x, &tc->y, &numlinks, &len) < 9) goto endsection;\n\t\t\tcurbuf += len;\n\t\t\tpos->w = 1;\n\t\t\ttc->y = 1 - tc->y;\n\t\t\ttc->z = tc->w = 0;\n\t\t\tc->reset();\n\t\t\tdouble pweight = 0, tweight = 0;\n\t\t\tfor(; numlinks > 0; numlinks--)\n\t\t\t{\n\t\t\t\tint bone = -1, len = 0;\n\t\t\t\tdouble weight = 0;\n\t\t\t\tif(sscanf(curbuf, \" %d %lf%n\", &bone, &weight, &len) < 2) break;\n\t\t\t\tcurbuf += len;\n\t\t\t\ttweight += weight;\n\t\t\t\tif(bone == parent) pweight += weight;\n\t\t\t\telse c->addweight(weight, bone);\n\t\t\t}\n\t\t\tif(tweight < 1) pweight += 1 - tweight;\n\t\t\tif(pweight > 0) c->addweight(pweight, parent);\n\t\t\tc->finalize();\n\t\t\t--pos;\n\t\t\t--tc;\n\t\t\t--norm;\n\t\t\t--c;\n\t\t}\n\t\tetriangle &t = etriangles.add();\n\t\tloopi(3) t.vert[i] = epositions.length() + i;\n\t\tt.smoothgroup = 0;\n\t\tepositions.advance(3);\n\t\tenormals.advance(3);\n\t\tetexcoords.advance(3);\n\t\teblends.advance(3);\n\t}\nendsection:\n\tif(m.name && etriangles.length () > m.firsttri) emeshes.add(m);\n}\n\nint readframes(stream *f, char *buf, size_t bufsize)\n{\n\tint frame = -1, numframes = 0, lastbone = ejoints.length(), frameoffset = eposes.length();\n\twhile(f->getline(buf, bufsize))\n\t{\n\t\tchar *curbuf = buf;\n\t\tif(skipcomment(curbuf)) continue;\n\t\tint nextframe = -1;\n\t\tif(sscanf(curbuf, \" time %d\", &nextframe) == 1)\n\t\t{\n\t\t\tfor(; lastbone < ejoints.length(); lastbone++) eposes[frameoffset + frame*ejoints.length() + lastbone] = eposes[frameoffset + lastbone];\n\t\t\tif(nextframe >= numframes)\n\t\t\t{\n\t\t\t\teposes.reserve(ejoints.length() * (nextframe + 1 - numframes));\n\t\t\t\tloopi(nextframe - numframes) \n\t\t\t\t{\n\t\t\t\t\teframes.add(eposes.length());\n\t\t\t\t\teposes.put(&eposes[frameoffset], ejoints.length());\n\t\t\t\t}\n\t\t\t\teframes.add(eposes.length());\n\t\t\t\teposes.advance(ejoints.length());\n\t\t\t\tnumframes = nextframe + 1;\n\t\t\t}\n\t\t\tframe = nextframe;\n\t\t\tlastbone = 0;\n\t\t\tcontinue;\n\t\t}\n\t\telse if(!strncmp(curbuf, \"end\", 3)) break;\n\t\tint bone;\n\t\tVec3 pos, rot;\n\t\tif(sscanf(curbuf, \" %d %lf %lf %lf %lf %lf %lf\", &bone, &pos.x, &pos.y, &pos.z, &rot.x, &rot.y, &rot.z) != 7)\n\t\t\tcontinue;\n\t\tif(bone < 0 || bone >= ejoints.length())\n\t\t\tcontinue;\n\t\tfor(; lastbone < bone; lastbone++) eposes[frameoffset + frame*ejoints.length() + lastbone] = eposes[frameoffset + lastbone];\n\t\tlastbone++;\n\t\ttransform p(pos, Quat::fromangles(rot));\n\t\teposes[frameoffset + frame*ejoints.length() + bone] = p;\n\t}\n\tfor(; lastbone < ejoints.length(); lastbone++) eposes[frameoffset + frame*ejoints.length() + lastbone] = eposes[frameoffset + lastbone];\n\treturn numframes;\n}\n\n}\n\nbool loadsmd(const char *filename, const filespec &spec)\n{\n\tstream *f = openfile(filename, \"r\");\n\tif(!f) return false;\n\n\tresetimporter(spec);\n\n\tchar buf[512];\n\tint version = -1, firstframe = eframes.length();\n\tbool hastriangles = false;\n\twhile(f->getline(buf, sizeof(buf)))\n\t{\n\t\tchar *curbuf = buf;\n\t\tif(smd::skipcomment(curbuf)) continue;\n\t\tif(sscanf(curbuf, \" version %d\", &version) == 1)\n\t\t{\n\t\t\tif(version != 1) { delete f; return false; }\n\t\t}\n\t\telse if(!strncmp(curbuf, \"nodes\", 5))\n\t\t\tsmd::readnodes(f, buf, sizeof(buf));\n\t\telse if(!strncmp(curbuf, \"triangles\", 9))\n\t\t{\n\t\t\tsmd::readtriangles(f, buf, sizeof(buf));\n\t\t\thastriangles = true;\n\t\t}\n\t\telse if(!strncmp(curbuf, \"skeleton\", 8))\n\t\t\tsmd::readframes(f, buf, sizeof(buf));\n\t\telse if(!strncmp(curbuf, \"vertexanimation\", 15))\n\t\t\tsmd::skipsection(f, buf, sizeof(buf));\n\t}\n\n\tdelete f;\n\n\tif(hastriangles)\n\t{\n\t\teframes.setsize(firstframe);\n\t\tmakeanims(spec);\n\t\tsmoothverts();\n\t\tmakemeshes(spec);\n\t}\n\telse\n\t{\n\t\teanim &a = eanims.add();\n\t\tif(spec.name) a.name = getnamekey(spec.name);\n\t\telse\n\t\t{\n\t\t\tstring name;\n\t\t\tconst char *shortname = filename;\n\t\t\tshortname = strrchr(filename, '/');\n\t\t\tif (shortname)\n\t\t\t\tshortname++;\n\t\t\telse\n\t\t\t\tshortname = filename;\n\t\t\tcopystring(name, shortname);\n\t\t\tchar *end = strrchr(name, '.');\n\t\t\tif(end) *end = '\\0';\n\t\t\ta.name = getnamekey(name);\n\t\t}\n\t\ta.startframe = firstframe;\n\t\ta.fps = spec.fps;\n\t\ta.flags = spec.flags;\n\t\tif(spec.endframe >= 0) a.endframe = a.startframe + spec.endframe;\n\t\telse if(spec.endframe < -1) a.endframe = a.startframe + max(eframes.length() - a.startframe + spec.endframe + 1, 0);\n\t\ta.startframe += spec.startframe;\n\t\tmakeanims(spec);\n\t}\n\n\treturn true;\n}\n\nstruct objvert { int attrib[3]; objvert() { attrib[0] = attrib[1] = attrib[2] = -1; } };\nstatic inline uint hthash(const objvert &k) { return k.attrib[0] ^ k.attrib[1] ^ k.attrib[2]; };\nstatic inline bool htcmp(const objvert &x, const objvert &y) { return x.attrib[0] == y.attrib[0] && x.attrib[1] == y.attrib[1] && x.attrib[2] == y.attrib[2]; }\n\nvoid parseobjvert(char *s, vector<Vec3> &out)\n{\n\tVec3 &v = out.add(Vec3(0, 0, 0));\n\twhile(isalpha(*s)) s++;\n\tloopi(3)\n\t{\n\t\tv[i] = strtod(s, &s);\n\t\twhile(isspace(*s)) s++;\n\t\tif(!*s) break;\n\t}\n}\n\nstruct mtlinfo{\n\tconst char *name;\n\tconst char *tex;\t//often null\n\tVec4 rgba;\t\t\t//often 1,1,1,1\n\tmtlinfo(const char *matname) : name(getnamekey(matname)), tex(NULL),rgba(Vec4(1,1,1,1)) {}\n};\nstatic vector<mtlinfo> parsemtl(stream *f)\n{\n\tvector<mtlinfo> ret;\n\tchar buf[512];\n\tmtlinfo dummy(\"\"), *curmat = &dummy;\n\n\tif (f)\n\twhile(f->getline(buf, sizeof(buf)))\n\t{\n\t\tchar *c = buf;\n\t\twhile(isspace(*c)) c++;\n\t\tif (*c == '#')\n\t\t\tcontinue;\n\t\telse if(!strncmp(c, \"newmtl\", 6) && isspace(c[6]))\n\t\t{\n\t\t\twhile(isalpha(*c)) c++;\n\t\t\twhile(isspace(*c)) c++;\n\t\t\tchar *name = c;\n\t\t\tsize_t namelen = strlen(c);\n\t\t\twhile(namelen > 0 && isspace(name[namelen-1])) namelen--;\n\t\t\tname[namelen] = 0;\n\n\t\t\tcurmat = &ret.add(mtlinfo(name));\n\t\t}\n\t\telse if(!strncmp(c, \"Kd\", 2) && isspace(c[2]))\n\t\t{\t//diffuse colours...\n\t\t\twhile(isalpha(*c)) c++;\n\t\t\twhile(isspace(*c)) c++;\n\t\t\tloopi(3)\n\t\t\t{\n\t\t\t\tcurmat->rgba[i] = strtod(c, &c);\n\t\t\t\twhile(isspace(*c)) c++;\n\t\t\t\tif(!*c) break;\n\t\t\t}\n\t\t}\n\t\telse if(!strncmp(c, \"D\", 1) && isspace(c[1]))\n\t\t{\t//'disolve', so basically alpha\n\t\t\twhile(isalpha(*c)) c++;\n\t\t\twhile(isspace(*c)) c++;\n\t\t\tcurmat->rgba[3] = strtod(c, &c);\n\t\t}\n\t\telse if(!strncmp(c, \"map_Kd\", 6) && isspace(c[6]))\n\t\t{\n\t\t\twhile(isalpha(*c) || *c == '_') c++;\n\t\t\twhile(isspace(*c)) c++;\n\t\t\tchar *name = c;\n\t\t\tsize_t namelen = strlen(c);\n\t\t\twhile(namelen > 0 && isspace(name[namelen-1])) namelen--;\n\t\t\tname[namelen] = 0;\n\t\t\tcurmat->tex = getnamekey(name);\n\t\t}\n\t}\n\n\treturn ret;\n}\n\nbool parseobj(stream *f)\n{\n\tvector<Vec3> attrib[3];\t//coord, tangemt, normal.\n\tchar buf[512];\n\thashtable<objvert, int> verthash;\n\tstring meshname = \"\", matname = \"\", tmpname=\"\";\n\tint curmesh = -1, smooth = 0;\n\tVec4 col = {1,1,1,1};\n\n\tvector<mtlinfo> mtl;\n\n\twhile(f->getline(buf, sizeof(buf)))\n\t{\n\t\tchar *c = buf;\n\t\twhile(isspace(*c)) c++;\n\t\tswitch(*c)\n\t\t{\n\t\t\tcase '#': continue;\n\t\t\tcase 'v':\n\t\t\t\tif(isspace(c[1])) parseobjvert(c, attrib[0]);\n\t\t\t\telse if(c[1]=='t') parseobjvert(c, attrib[1]);\n\t\t\t\telse if(c[1]=='n') parseobjvert(c, attrib[2]);\n\t\t\t\tbreak;\n\t\t\tcase 'g':\n\t\t\t{\n\t\t\t\twhile(isalpha(*c)) c++;\n\t\t\t\twhile(isspace(*c)) c++;\n\t\t\t\tchar *name = c;\n\t\t\t\tsize_t namelen = strlen(name);\n\t\t\t\twhile(namelen > 0 && isspace(name[namelen-1])) namelen--;\n\t\t\t\tcopystring(meshname, name, min(namelen+1, sizeof(meshname)));\n\t\t\t\tcurmesh = -1;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase 'm':\n\t\t\t{\n\t\t\t\tif(strncmp(c, \"mtllib\", 6)) continue;\n\t\t\t\twhile(isalpha(*c)) c++;\n\t\t\t\twhile(isspace(*c)) c++;\n\t\t\t\tconst char *name = c;\n\t\t\t\tsize_t namelen = strlen(name);\n\t\t\t\twhile(namelen > 0 && isspace(name[namelen-1])) namelen--;\n\t\t\t\tcopystring(tmpname, name, min(namelen+1, sizeof(tmpname)));\n\t\t\t\tmtl = parsemtl(openfile(tmpname, \"r\"));\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase 'u':\n\t\t\t{\n\t\t\t\tif(strncmp(c, \"usemtl\", 6)) continue;\n\t\t\t\twhile(isalpha(*c)) c++;\n\t\t\t\twhile(isspace(*c)) c++;\n\t\t\t\tconst char *name = c;\n\t\t\t\tsize_t namelen = strlen(name);\n\t\t\t\twhile(namelen > 0 && isspace(name[namelen-1])) namelen--;\n\t\t\t\tcopystring(matname, name, min(namelen+1, sizeof(matname)));\n\t\t\t\tcol = Vec4(1,1,1,1);\n\n\t\t\t\tloopv(mtl)\n\t\t\t\t{\t//if its an obj material, swap out the vertex colour+mat name for whatever the mtl file specifies.\n\t\t\t\t\tif (!strcmp(matname, mtl[i].name))\n\t\t\t\t\t{\n\t\t\t\t\t\tcol = mtl[i].rgba;\n\t\t\t\t\t\tconst char *tex = mtl[i].tex;\n\t\t\t\t\t\tif (!tex)\n\t\t\t\t\t\t\ttex = (col[3] < 1)?\"whiteskin#VC#BLEND\":\"whiteskin#VC\";\t//not me being racist or anything...\n\t\t\t\t\t\tcopystring(matname, tex, sizeof(matname));\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tcurmesh = -1;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase 's':\n\t\t\t{\n\t\t\t\tif(!isspace(c[1])) continue;\n\t\t\t\twhile(isalpha(*c)) c++;\n\t\t\t\twhile(isspace(*c)) c++;\n\t\t\t\tint key = strtol(c, &c, 10);\n\t\t\t\tsmooth = -1;\n\t\t\t\tloopv(esmoothgroups) if(esmoothgroups[i].key == key) { smooth = i; break; }\n\t\t\t\tif(smooth < 0)\n\t\t\t\t{\n\t\t\t\t\tsmooth = esmoothgroups.length();\n\t\t\t\t\tesmoothgroups.add().key = key;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase 'f':\n\t\t\t{\n\t\t\t\tif(curmesh < 0)\n\t\t\t\t{\n\t\t\t\t\temesh m;\n\t\t\t\t\tm.name = getnamekey(meshname);\n\t\t\t\t\tm.material = getnamekey(matname);\n\t\t\t\t\tm.firsttri = etriangles.length();\n\t\t\t\t\tcurmesh = emeshes.length();\n\t\t\t\t\temeshes.add(m);\n\t\t\t\t\tverthash.clear();\n\t\t\t\t}\n\t\t\t\tint v0 = -1, v1 = -1;\n\t\t\t\twhile(isalpha(*c)) c++;\n\t\t\t\tfor(;;)\n\t\t\t\t{\n\t\t\t\t\twhile(isspace(*c)) c++;\n\t\t\t\t\tif(!*c) break;\n\t\t\t\t\tobjvert vkey;\n\t\t\t\t\tloopi(3)\n\t\t\t\t\t{\n\t\t\t\t\t\tvkey.attrib[i] = strtol(c, &c, 10);\n\t\t\t\t\t\tif(vkey.attrib[i] < 0) vkey.attrib[i] = attrib[i].length() + vkey.attrib[i];\n\t\t\t\t\t\telse vkey.attrib[i]--;\n\t\t\t\t\t\tif(!attrib[i].inrange(vkey.attrib[i])) vkey.attrib[i] = -1;\n\t\t\t\t\t\tif(*c!='/') break;\n\t\t\t\t\t\tc++;\n\t\t\t\t\t}\n\t\t\t\t\tint *index = verthash.access(vkey);\n\t\t\t\t\tif(!index)\n\t\t\t\t\t{\n\t\t\t\t\t\tindex = &verthash[vkey];\n\t\t\t\t\t\t*index = epositions.length();\n\t\t\t\t\t\tepositions.add(Vec4(vkey.attrib[0] < 0 ? Vec3(0, 0, 0) : attrib[0][vkey.attrib[0]].zxy(), 1));\n\t\t\t\t\t\tif(vkey.attrib[2] >= 0) enormals.add(attrib[2][vkey.attrib[2]].zxy());\n\t\t\t\t\t\tetexcoords.add(vkey.attrib[1] < 0 ? Vec4(0, 0, 0, 0) : Vec4(attrib[1][vkey.attrib[1]].x, 1-attrib[1][vkey.attrib[1]].y, 0, 0));\n\t\t\t\t\t\tecolors.add(col);\n\t\t\t\t\t}\n\t\t\t\t\tif(v0 < 0) v0 = *index;\n\t\t\t\t\telse if(v1 < 0) v1 = *index;\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tetriangles.add(etriangle(*index, v1, v0, smooth));\n\t\t\t\t\t\tv1 = *index;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn true;\n}\n\nbool loadobj(const char *filename, const filespec &spec)\n{\n\tstream *f = openfile(filename, \"r\");\n\tif(!f) return false;\n\n\tint numfiles = 0;\n\twhile(filename)\n\t{\n\t\tconst char *endfile = strchr(filename, ',');\n\t\tconst char *file = endfile ? newstring(filename, endfile-filename) : filename;\n\t\tstream *f = openfile(file, \"r\");\n\t\tif(f)\n\t\t{\n\t\t\tif(resetimporter(spec, numfiles > 0))\n\t\t\t{\n\t\t\t\tesmoothgroups[0].key = 0;\n\t\t\t}\n\t\t\tif(parseobj(f)) numfiles++;\n\t\t\tdelete f;\n\t\t}\n\n\t\tif(!endfile) break;\n\n\t\tdelete[] file;\n\t\tfilename = endfile+1;\n\t}\n\n\tif(!numfiles) return false;\n\n\tsmoothverts();\n\tmakemeshes(spec);\n\n\treturn true;\n}\n\nnamespace fbx\n{\n\tstruct token\n\t{\n\t\tenum { NONE, PROP, NUMBER, STRING, ARRAY, BEGIN, END, LINE };\n\t\tint type;\n\t\tunion\n\t\t{\n\t\t\tchar s[64];\n\t\t\tdouble f;\n\t\t\tint i;\n\t\t};\n\n\t\ttoken() : type(NONE) {} \n\t};\n\n\tstruct tokenizer\n\t{\n\t\tstream *f;\n\t\tchar *pos;\n\t\tchar buf[4096];\n\n\t\tvoid reset(stream *s) { f = s; pos = buf; buf[0] = '\\0'; }\n \n\t\tbool parse(token &t)\n\t\t{\n\t\t\tfor(;;)\n\t\t\t{\n\t\t\t\twhile(isspace(*pos)) pos++;\n\t\t\t\tif(!*pos)\n\t\t\t\t{\n\t\t\t\t\tbool more = f->getline(buf, sizeof(buf));\n\t\t\t\t\tpos = buf;\n\t\t\t\t\tif(!more) { buf[0] = '\\0'; return false; }\n\t\t\t\t\tt.type = token::LINE;\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t\tsize_t slen = 0;\n\t\t\t\tswitch(*pos)\n\t\t\t\t{\n\t\t\t\t\tcase ',':\n\t\t\t\t\t\tpos++;\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\tcase ';':\n\t\t\t\t\t\tpos++;\n\t\t\t\t\t\twhile(*pos) pos++;\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\tcase '{':\n\t\t\t\t\t\tpos++;\n\t\t\t\t\t\tt.type = token::BEGIN;\n\t\t\t\t\t\treturn true;\n\t\t\t\t\tcase '}':\n\t\t\t\t\t\tpos++;\n\t\t\t\t\t\tt.type = token::END;\n\t\t\t\t\t\treturn true;\n\t\t\t\t\tcase '\"':\n\t\t\t\t\t\tpos++;\n\t\t\t\t\t\tfor(; *pos && *pos != '\"'; pos++) if(slen < sizeof(t.s)-1) t.s[slen++] = *pos;\n\t\t\t\t\t\tt.s[slen] = '\\0';\n\t\t\t\t\t\tif(*pos == '\"') pos++;\n\t\t\t\t\t\tt.type = token::STRING;\n\t\t\t\t\t\treturn true;\n\t\t\t\t\tcase '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case '.': case '-': case '+':\n\t\t\t\t\t\tt.f = strtod(pos, &pos);\n\t\t\t\t\t\tt.type = token::NUMBER;\n\t\t\t\t\t\treturn true;\n\t\t\t\t\tcase '*':\n\t\t\t\t\t\tpos++;\n\t\t\t\t\t\tt.i = int(strtol(pos, &pos, 10));\n\t\t\t\t\t\tt.type = token::ARRAY;\n\t\t\t\t\t\treturn true;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tfor(; *pos && !isspace(*pos) && *pos != ':'; pos++) if(slen < sizeof(t.s)-1) t.s[slen++] = *pos;\n\t\t\t\t\t\tt.s[slen] = '\\0';\n\t\t\t\t\t\tif(*pos == ':') pos++;\n\t\t\t\t\t\tt.type = token::PROP;\n\t\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\n\t\tbool skipprop()\n\t\t{\n\t\t\ttoken t;\n\t\t\twhile(parse(t)) switch(t.type)\n\t\t\t{\n\t\t\t\tcase token::LINE:\n\t\t\t\t\treturn true;\n\n\t\t\t\tcase token::BEGIN:\n\t\t\t\t\twhile(parse(t)) switch(t.type)\n\t\t\t\t\t{\n\t\t\t\t\t\tcase token::PROP:\n\t\t\t\t\t\t\tskipprop();\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase token::END:\n\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t\treturn true;\n\n\t\t\t\tcase token::END:\n\t\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\t\tbool findbegin()\n\t\t{\n\t\t\ttoken t;\n\t\t\twhile(parse(t)) switch(t.type)\n\t\t\t{\n\t\t\t\tcase token::LINE: return false;\n\t\t\t\tcase token::BEGIN: return true;\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\t\ttemplate<class T>\n\t\tbool readarray(vector<T> &vals, int size = 0)\n\t\t{\n\t\t\tif(!findbegin()) return false;\n\n\t\t\tif(size > 0) vals.reserve(min(size, 1<<16));\n\t\t\ttoken t;\n\t\t\twhile(parse(t)) switch(t.type)\n\t\t\t{\n\t\t\t\tcase token::NUMBER: if(size <= 0 || vals.length() < size) vals.add(T(t.f)); break;\n\t\t\t\tcase token::END: return true;\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\t};\n\n\tstruct node\n\t{\n\t\tenum { GEOM = 0, MODEL, MATERIAL, LIMB, CLUSTER, SKIN, CURVE, XFORM, ANIMLAYER, ANIMSTACK };\n\t\tenum { TRANS = 0, ROT, SCALE };\n\n\t\tvirtual int type() = 0;\n\t\tvirtual ~node() {}\n\n\t\tvirtual void process() {}\n\t\tvirtual void finish() {}\n\t};\n\n\tstruct namednode : node\n\t{\n\t\tstring name;\n\n\t\tnamednode() { name[0] = 0; }\n\t};\n\n\tstruct geomnode;\n\tstruct modelnode;\n\tstruct materialnode;\n\tstruct limbnode;\n\tstruct clusternode;\n\tstruct skinnode;\n\tstruct curvenode;\n\tstruct xformnode;\n\tstruct animlayernode;\n\tstruct animstacknode;\n\n\tstruct geomnode : node\n\t{\n\t\tint mesh, firstvert, lastvert, numverts;\n\t\tmodelnode *model;\n\t\tvector<int> remap;\n\t\tvector<blendcombo> blends;\n\n\t\tgeomnode() : mesh(-1), firstvert(-1), lastvert(-1), numverts(0), model(NULL) {}\n\t\tint type() { return GEOM; } \n\n\t\tvoid process();\n\t\tvoid finish();\n\t};\n\n\tstruct modelnode : namednode\n\t{\n\t\tmaterialnode *material;\n\t\tVec3 geomtrans, prerot, lcltrans, lclrot, lclscale;\n\n\t\tmodelnode() : material(NULL), geomtrans(0, 0, 0), prerot(0, 0, 0), lcltrans(0, 0, 0), lclrot(0, 0, 0), lclscale(1, 1, 1) {}\n\n\t\tint type() { return MODEL; }\n\t};\n\n\tstruct materialnode : namednode\n\t{\n\t\tint type() { return MATERIAL; }\n\t};\n\n\tstruct limbnode : namednode\n\t{\n\t\tlimbnode *parent;\n\t\tint index;\n\t\tVec3 trans, rot, prerot, scale;\n\t\tclusternode *cluster;\n\n\t\tlimbnode() : parent(NULL), index(-1), trans(0, 0, 0), rot(0, 0, 0), prerot(0, 0, 0), scale(1, 1, 1), cluster(NULL) {}\n\n\t\tint type() { return LIMB; }\n\n\t\tvoid process()\n\t\t{ \n\t\t\tif(parent) ejoints[index].parent = parent->index; \n\t\t}\n\n\t\tvoid finish();\n\t};\n\n\tstruct clusternode : node\n\t{\n\t\tskinnode *skin;\n\t\tlimbnode *limb;\n\t\tvector<int> indexes;\n\t\tvector<double> weights, transform, transformlink;\n\n\t\tclusternode() : skin(NULL), limb(NULL) {}\n\n\t\tint type() { return CLUSTER; }\n\n\t\tvoid process();\n\t};\n\n\tstruct skinnode : node\n\t{\n\t\tgeomnode *geom;\n\n\t\tskinnode() : geom(NULL) {}\n\n\t\tint type() { return SKIN; }\n\t};\n\n\tstruct curvenode : node\n\t{\n\t\tvector<double> vals;\n\n\t\tint type() { return CURVE; }\n\n\t\tbool varies() const\n\t\t{\n\t\t\tloopv(vals) if(vals[i] != vals[0]) return true;\n\t\t\treturn false;\n\t\t}\n\t};\n\n\tstruct xformnode : node\n\t{\n\t\tlimbnode *limb;\n\t\tint xform;\n\t\tVec3 val;\n\t\tcurvenode *curves[3];\n\n\t\txformnode() : limb(NULL), xform(-1), val(0, 0, 0) { curves[0] = curves[1] = curves[2] = NULL; }\n\n\t\tvoid setcurve(int i, curvenode *c)\n\t\t{\n\t\t\tif(c->varies()) curves[i] = c;\n\t\t\telse if(c->vals.length()) val[i] = c->vals[0];\n\t\t}\n\n\t\tint numframes()\n\t\t{\n\t\t\tint n = 0;\n\t\t\tloopi(3) if(curves[i]) { if(!n) n = curves[i]->vals.length(); else if(n != curves[i]->vals.length()) n = -1; }\n\t\t\treturn n;\n\t\t}\n\n\t\tint type() { return XFORM; }\n\t};\n\n\tstruct animlayernode : namednode\n\t{\n\t\tvector<xformnode *> xforms;\n\n\t\tint numframes()\n\t\t{\n\t\t\tint n = 0;\n\t\t\tloopv(xforms)\n\t\t\t{\n\t\t\t\tint xn = xforms[i]->numframes();\n\t\t\t\tif(xn) { if(!n) n = xn; else if(n != xn) n = -1; }\n\t\t\t}\n\t\t\treturn n;\n\t\t}\n\n\t\tint type() { return ANIMLAYER; }\n\t};\n\n\tstruct animstacknode : namednode\n\t{\n\t\tvector<animlayernode *> layers;\n\t\tdouble secs;\n\n\t\tanimstacknode() : secs(0) {}\n\n\t\tint numframes()\n\t\t{\n\t\t\tint n;\n\t\t\tloopv(layers)\n\t\t\t{\n\t\t\t\tint ln = layers[i]->numframes();\n\t\t\t\tif(ln) { if(!n) n = ln; else if(n != ln) n = -1; }\n\t\t\t}\n\t\t\treturn n;\n\t\t}\n\n\t\tint type() { return ANIMSTACK; }\n\n\t\tvoid process();\n\t};\n\n\thashtable<double, node *> nodes;\n\ttokenizer p;\n\n\tvoid parsegeometry()\n\t{\n\t\ttoken t;\n\t\tif(!p.parse(t)) return;\n\t\tif(t.type != token::NUMBER) { p.skipprop(); return; }\n\t\tdouble id = t.f;\n\t\tif(!p.findbegin()) return;\n\n\t\tvector<double> verts, norms, uvs, colors;\n\t\tvector<int> polyidxs, uvidxs, coloridxs;\n\t\twhile(p.parse(t)) switch(t.type)\n\t\t{\n\t\t\tcase token::END:\n\t\t\t\tgoto endgeometry;\n\n\t\t\tcase token::PROP:\n\t\t\t\tif(!strcmp(t.s, \"Vertices\")) p.readarray(verts);\n\t\t\t\telse if(!strcmp(t.s, \"PolygonVertexIndex\")) p.readarray(polyidxs);\n\t\t\t\telse if(!strcmp(t.s, \"LayerElementNormal\"))\n\t\t\t\t{\n\t\t\t\t\tif(p.findbegin())\n\t\t\t\t\t{\n\t\t\t\t\t\twhile(p.parse(t)) switch(t.type)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase token::PROP:\n\t\t\t\t\t\t\t\tif(!strcmp(t.s, \"Normals\")) p.readarray(norms);\n\t\t\t\t\t\t\t\telse p.skipprop();\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tcase token::END:\n\t\t\t\t\t\t\t\tgoto endnormals;\n\t\t\t\t\t\t}\n\t\t\t\t\tendnormals:;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse if(!strcmp(t.s, \"LayerElementUV\"))\n\t\t\t\t{\n\t\t\t\t\tif(p.findbegin())\n\t\t\t\t\t{\n\t\t\t\t\t\twhile(p.parse(t)) switch(t.type)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase token::PROP:\n\t\t\t\t\t\t\t\tif(!strcmp(t.s, \"UV\")) p.readarray(uvs);\n\t\t\t\t\t\t\t\telse if(!strcmp(t.s, \"UVIndex\")) p.readarray(uvidxs);\n\t\t\t\t\t\t\t\telse p.skipprop();\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tcase token::END:\n\t\t\t\t\t\t\t\tgoto enduvs;\n\t\t\t\t\t\t}\n\t\t\t\t\tenduvs:;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse if(!strcmp(t.s, \"LayerElementColor\"))\n\t\t\t\t{\n\t\t\t\t\tif(p.findbegin())\n\t\t\t\t\t{\n\t\t\t\t\t\twhile(p.parse(t)) switch(t.type)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase token::PROP:\n\t\t\t\t\t\t\t\tif(!strcmp(t.s, \"Colors\")) p.readarray(colors);\n\t\t\t\t\t\t\t\telse if(!strcmp(t.s, \"ColorIndex\")) p.readarray(coloridxs);\n\t\t\t\t\t\t\t\telse p.skipprop();\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tcase token::END:\n\t\t\t\t\t\t\t\tgoto endcolors;\n\t\t\t\t\t\t}\n\t\t\t\t\tendcolors:;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse p.skipprop();\n\t\t\t\tbreak;\n\t\t}\n\tendgeometry:\n\t\tint poslen = epositions.length();\n\t\tgeomnode *n = new geomnode;\n\t\tnodes[id] = n;\n\t\tif(polyidxs.empty()) for(int i = 0; i + 2 < verts.length(); i += 3) epositions.add(Vec4(verts[i], verts[i+1], verts[i+2], 1));\n\t\telse \n\t\t{\n\t\t\tloopv(polyidxs)\n\t\t\t{\n\t\t\t\tint idx = polyidxs[i];\n\t\t\t\tif(idx < 0) idx = -(idx+1);\n\t\t\t\tn->remap.add(idx);\n\t\t\t\tidx *= 3;\n\t\t\t\tepositions.add(Vec4(verts[idx], verts[idx+1], verts[idx+2], 1));\n\t\t\t}\n\t\t}\n\t\tloopi(epositions.length() - poslen) eblends.add();\n\n\t\temesh m;\n\t\tm.name = getnamekey(\"\");\n\t\tm.material = getnamekey(\"\");\n\t\tm.firsttri = etriangles.length();\n\t\tfor(int i = poslen; i + 2 < epositions.length(); i += 3)\n\t\t\tetriangles.add(etriangle(i+1, i, i+2));\n\t\temeshes.add(m);\n\n\t\tn->mesh = emeshes.length()-1;\n\t\tn->firstvert = poslen;\n\t\tn->lastvert = epositions.length();\n\t\tn->numverts = verts.length()/3;\n\n\t\tif(uvidxs.empty())\n\t\t{\n\t\t\tif(polyidxs.length() && uvs.length()/2 == verts.length()/3) loopv(polyidxs)\n\t\t\t{\n\t\t\t\tint idx = polyidxs[i];\n\t\t\t\tif(idx < 0) idx = -(idx+1);\n\t\t\t\tidx *= 2;\n\t\t\t\tetexcoords.add(Vec4(uvs[idx], 1-uvs[idx+1], 0, 0));\n\t\t\t}\n\t\t\telse for(int i = 0; i + 1 < uvs.length(); i += 2) etexcoords.add(Vec4(uvs[i], 1-uvs[i+1], 0, 0));\n\t\t}\n\t\telse loopv(uvidxs)\n\t\t{\n\t\t\tint idx = 2*uvidxs[i];\n\t\t\tetexcoords.add(Vec4(uvs[idx], 1-uvs[idx+1], 0, 0));\n\t\t}\n\n\t\tif(polyidxs.length() && norms.length() == verts.length()) loopv(polyidxs) \n\t\t{\n\t\t\tint idx = polyidxs[i];\n\t\t\tif(idx < 0) idx = -(idx+1);\n\t\t\tidx *= 3;\n\t\t\tenormals.add(Vec3(norms[idx], norms[idx+1], norms[idx+2]));\n\t\t}\n\t\telse for(int i = 0; i + 2 < norms.length(); i += 3) enormals.add(Vec3(norms[i], norms[i+1], norms[i+2]));\n\n\t\tif(coloridxs.empty())\n\t\t{\n\t\t\tif(polyidxs.length() && colors.length()/4 == verts.length()/3) loopv(polyidxs)\n\t\t\t{\n\t\t\t\tint idx = polyidxs[i];\n\t\t\t\tif(idx < 0) idx = -(idx+1);\n\t\t\t\tidx *= 4;\n\t\t\t\tecolors.add(Vec4(colors[idx], colors[idx+1], colors[idx+2], colors[idx+3]));\n\t\t\t}\n\t\t\telse for(int i = 0; i + 3 < colors.length(); i += 4) ecolors.add(Vec4(colors[i], colors[i+1], colors[i+2], colors[i+3]));\n\t\t}\n\t\telse loopv(coloridxs)\n\t\t{\n\t\t\tint idx = 4*coloridxs[i];\n\t\t\tecolors.add(Vec4(colors[idx], colors[idx+1], colors[idx+2], colors[idx+3]));\n\t\t}\n\t}\n\n\tvoid parsemodel()\n\t{\n\t\ttoken id, name, type, t;\n\t\tif(!p.parse(id) || !p.parse(name) || !p.parse(type)) return;\n\n\t\tif(id.type != token::NUMBER || type.type != token::STRING || name.type != token::STRING) { p.skipprop(); return; }\n\n\t\tchar *str = name.s;\n\t\tif(strstr(str, \"Model::\") == str) str += strlen(\"Model::\");\n\t\tif(!strcmp(type.s, \"Mesh\"))\n\t\t{\n\t\t\tmodelnode *n = new modelnode;\n\t\t\tcopystring(n->name, str);\n\t\t\tnodes[id.f] = n;\n\n\t\t\tif(!p.findbegin()) return;\n\t\t\twhile(p.parse(t)) switch(t.type)\n\t\t\t{\n\t\t\tcase token::END: return;\n\t\t\tcase token::PROP:\n\t\t\t\tif(!strcmp(t.s, \"Properties70\"))\n\t\t\t\t{\n\t\t\t\t\tif(!p.findbegin()) return;\n\t\t\t\t\twhile(p.parse(t)) switch(t.type)\n\t\t\t\t\t{\n\t\t\t\t\tcase token::END: goto endmeshprops;\n\t\t\t\t\tcase token::PROP:\n\t\t\t\t\t\tif(!strcmp(t.s, \"P\"))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif(!p.parse(t)) return;\n\t\t\t\t\t\t\tif(t.type == token::STRING)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tif(!strcmp(t.s, \"PreRotation\"))\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tloopi(3) if(!p.parse(t)) return;\n\t\t\t\t\t\t\t\t\tloopi(3) { if(!p.parse(t)) return; if(t.type != token::NUMBER) break; n->prerot[i] = t.f; }\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\telse if(!strcmp(t.s, \"GeometricTranslation\"))\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tloopi(3) if(!p.parse(t)) return;\n\t\t\t\t\t\t\t\t\tloopi(3) { if(!p.parse(t)) return; if(t.type != token::NUMBER) break; n->geomtrans[i] = t.f; }\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\telse if(!strcmp(t.s, \"Lcl Translation\"))\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tloopi(3) if(!p.parse(t)) return;\n\t\t\t\t\t\t\t\t\tloopi(3) { if(!p.parse(t)) return; if(t.type != token::NUMBER) break; n->lcltrans[i] = t.f; }\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\telse if(!strcmp(t.s, \"Lcl Rotation\"))\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tloopi(3) if(!p.parse(t)) return;\n\t\t\t\t\t\t\t\t\tloopi(3) { if(!p.parse(t)) return; if(t.type != token::NUMBER) break; n->lclrot[i] = t.f; }\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\telse if(!strcmp(t.s, \"Lcl Scaling\"))\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tloopi(3) if(!p.parse(t)) return;\n\t\t\t\t\t\t\t\t\tloopi(3) { if(!p.parse(t)) return; if(t.type != token::NUMBER) break; n->lclscale[i] = t.f; }\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tp.skipprop();\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\tendmeshprops:;\n\t\t\t\t}\n\t\t\t\tp.skipprop();\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\telse if(!strcmp(type.s, \"LimbNode\"))\n\t\t{\n\t\t\tlimbnode *n = new limbnode;\n\t\t\tcopystring(n->name, str);\n\t\t\tn->index = ejoints.length();\n\t\t\tnodes[id.f] = n;\n\n\t\t\tejoint &j = ejoints.add();\n\t\t\tj.name = getnamekey(str);\n\t\t\tj.parent = -1;\n\t\t\teposes.add(transform(Vec3(0, 0, 0), Quat(0, 0, 0, 1)));\n\n\t\t\tif(!p.findbegin()) return;\n\t\t\twhile(p.parse(t)) switch(t.type)\n\t\t\t{ \n\t\t\tcase token::END: \n\t\t\t\t{\n\t\t\t\t\ttransform &x = eposes[n->index];\n\t\t\t\t\tx.pos = n->trans;\n\t\t\t\t\tx.orient = Quat::fromdegrees(n->rot);\n\t\t\t\t\tx.scale = n->scale;\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\tcase token::PROP:\n\t\t\t\tif(!strcmp(t.s, \"Properties70\"))\n\t\t\t\t{\n\t\t\t\t\tif(!p.findbegin()) return;\n\t\t\t\t\twhile(p.parse(t)) switch(t.type)\n\t\t\t\t\t{\n\t\t\t\t\tcase token::END: goto endlimbprops;\n\t\t\t\t\tcase token::PROP:\n\t\t\t\t\t\tif(!strcmp(t.s, \"P\"))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif(!p.parse(t)) return;\n\t\t\t\t\t\t\tif(t.type == token::STRING)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tif(!strcmp(t.s, \"PreRotation\"))\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tloopi(3) if(!p.parse(t)) return;\n\t\t\t\t\t\t\t\t\tloopi(3) { if(!p.parse(t)) return; if(t.type != token::NUMBER) break; n->prerot[i] = t.f; }\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\telse if(!strcmp(t.s, \"Lcl Translation\"))\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tloopi(3) if(!p.parse(t)) return;\n\t\t\t\t\t\t\t\t\tloopi(3) { if(!p.parse(t)) return; if(t.type != token::NUMBER) break; n->trans[i] = t.f; }\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\telse if(!strcmp(t.s, \"Lcl Rotation\"))\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tloopi(3) if(!p.parse(t)) return;\n\t\t\t\t\t\t\t\t\tloopi(3) { if(!p.parse(t)) return; if(t.type != token::NUMBER) break; n->rot[i] = t.f; }\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\telse if(!strcmp(t.s, \"Lcl Scaling\"))\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tloopi(3) if(!p.parse(t)) return;\n\t\t\t\t\t\t\t\t\tloopi(3) { if(!p.parse(t)) return; if(t.type != token::NUMBER) break; n->scale[i] = t.f; } \n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tp.skipprop();\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\tendlimbprops:;\n\t\t\t\t}\n\t\t\t\tp.skipprop();\n\t\t\t\tbreak;\n\t\t\t}\n\t\t} \n\n\t\tp.skipprop();\n\t}\n\n\tvoid parsematerial()\n\t{\n\t\ttoken id, name;\n\t\tif(!p.parse(id) || !p.parse(name)) return;\n\n\t\tif(id.type == token::NUMBER)\n\t\t{\n\t\t\tif(name.type == token::STRING)\n\t\t\t{\n\t\t\t\tchar *str = name.s;\n\t\t\t\tif(strstr(str, \"Material::\") == str) str += strlen(\"Material::\");\n\t\t\t\tmaterialnode *n = new materialnode;\n\t\t\t\tcopystring(n->name, str);\n\t\t\t\tnodes[id.f] = n;\n\t\t\t}\n\t\t}\n\n\t\tp.skipprop();\n\t}\n\n\tvoid parsedeformer()\n\t{\n\t\ttoken id, name, type, t;\n\t\tif(!p.parse(id) || !p.parse(name) || !p.parse(type)) return;\n\t\tif(id.type != token::NUMBER || type.type != token::STRING || name.type != token::STRING)\n\t\t{\n\t\t\tp.skipprop();\n\t\t\treturn;\n\t\t}\n\n\t\tif(!strcmp(type.s, \"Skin\"))\n\t\t{\n\t\t\tskinnode *n = new skinnode;\n\t\t\tnodes[id.f] = n;\n\t\t}\n\t\telse if(!strcmp(type.s, \"Cluster\"))\n\t\t{\n\t\t\tif(!p.findbegin()) return;\n\n\t\t\tclusternode *n = new clusternode;\n\t\t\tnodes[id.f] = n;\n\t\t\twhile(p.parse(t)) switch(t.type)\n\t\t\t{\n\t\t\t\tcase token::END: return;\n\t\t\t\tcase token::PROP:\n\t\t\t\t\tif(!strcmp(t.s, \"Indexes\")) p.readarray(n->indexes);\n\t\t\t\t\telse if(!strcmp(t.s, \"Weights\")) p.readarray(n->weights);\n\t\t\t\t\telse if(!strcmp(t.s, \"Transform\")) p.readarray(n->transform);\n\t\t\t\t\telse if(!strcmp(t.s, \"TransformLink\")) p.readarray(n->transformlink);\n\t\t\t\t\telse p.skipprop();\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tp.skipprop();\n\t}\n\n\tvoid parsecurve()\n\t{\n\t\ttoken id, t;\n\t\tif(!p.parse(id)) return;\n\t\tif(id.type != token::NUMBER) { p.skipprop(); return; }\n\t\tcurvenode *n = new curvenode;\n\t\tnodes[id.f] = n;\n\t\twhile(p.parse(t)) switch(t.type)\n\t\t{\n\t\t\tcase token::END: return;\n\t\t\tcase token::PROP:\n\t\t\t\tif(!strcmp(t.s, \"KeyValueFloat\")) p.readarray(n->vals);\n\t\t\t\telse p.skipprop();\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\tvoid parsexform()\n\t{\n\t\ttoken id, t;\n\t\tif(!p.parse(id)) return;\n\t\tif(id.type != token::NUMBER) { p.skipprop(); return; }\n\t\tif(!p.findbegin()) return;\n\t\txformnode *n = new xformnode;\n\t\tnodes[id.f] = n;\n\t\twhile(p.parse(t)) switch(t.type)\n\t\t{\n\t\t\tcase token::END: return;\n\t\t\tcase token::PROP:\n\t\t\t\tif(!strcmp(t.s, \"Properties70\"))\n\t\t\t\t{\n\t\t\t\t\tif(!p.findbegin()) return;\n\t\t\t\t\twhile(p.parse(t)) switch(t.type)\n\t\t\t\t\t{\n\t\t\t\t\tcase token::END: goto endprops;\n\t\t\t\t\tcase token::PROP:\n\t\t\t\t\t\tif(!strcmp(t.s, \"P\"))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttoken name, type, val;\n\t\t\t\t\t\t\tif(!p.parse(name) || !p.parse(type) || !p.parse(t) || !p.parse(t)) return;\n\t\t\t\t\t\t\tif(name.type == token::STRING)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tif(!strcmp(name.s, \"d|X\")) { if(p.parse(val) && val.type == token::NUMBER) n->val.x = val.f; }\n\t\t\t\t\t\t\t\telse if(!strcmp(name.s, \"d|Y\")) { if(p.parse(val) && val.type == token::NUMBER) n->val.y = val.f; }\n\t\t\t\t\t\t\t\telse if(!strcmp(name.s, \"d|Z\")) { if(p.parse(val) && val.type == token::NUMBER) n->val.z = val.f; }\n\t\t\t\t\t\t\t} \n\t\t\t\t\t\t}\n\t\t\t\t\t\tp.skipprop();\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\tendprops:;\n\t\t\t\t}\n\t\t\t\telse p.skipprop();\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\tvoid parseanimlayer()\n\t{\n\t\ttoken id, name;\n\t\tif(!p.parse(id) || !p.parse(name)) return;\n\t\tif(id.type != token::NUMBER || name.type != token::STRING) { p.skipprop(); return; }\n\n\t\tchar *str = name.s;\n\t\tif(strstr(str, \"AnimLayer::\") == str) str += strlen(\"AnimLayer::\");\n\t\tanimlayernode *n = new animlayernode;\n\t\tcopystring(n->name, str);\n\t\tnodes[id.f] = n;\n\n\t\tp.skipprop();\n\t}\n\n\t#define FBX_SEC 46186158000.0\n\n\tvoid parseanimstack()\n\t{\n\t\ttoken id, name, t;\n\t\tif(!p.parse(id) || !p.parse(name)) return;\n\t\tif(id.type != token::NUMBER || name.type != token::STRING) { p.skipprop(); return; }\n\n\t\tchar *str = name.s;\n\t\tif(strstr(str, \"AnimStack::\") == str) str += strlen(\"AnimStack::\");\n\t\tanimstacknode *n = new animstacknode;\n\t\tcopystring(n->name, str);\n\t\tnodes[id.f] = n;\n\n\t\tif(!p.findbegin()) return;\n\t\twhile(p.parse(t)) switch(t.type)\n\t\t{\n\t\t\tcase token::END: return;\n\t\t\tcase token::PROP:\n\t\t\t\tif(!strcmp(t.s, \"Properties70\"))\n\t\t\t\t{\n\t\t\t\t\tif(!p.findbegin()) return;\n\t\t\t\t\twhile(p.parse(t)) switch(t.type)\n\t\t\t\t\t{\n\t\t\t\t\tcase token::END: goto endprops;\n\t\t\t\t\tcase token::PROP:\n\t\t\t\t\t\tif(!strcmp(t.s, \"P\"))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttoken name, type, val;\n\t\t\t\t\t\t\tif(!p.parse(name) || !p.parse(type) || !p.parse(t) || !p.parse(t)) return;\n\t\t\t\t\t\t\tif(name.type == token::STRING)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tif(!strcmp(name.s, \"LocalStop\")) { if(p.parse(val) && val.type == token::NUMBER) n->secs = val.f / FBX_SEC; }\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tp.skipprop();\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\tendprops:;\n\t\t\t\t}\n\t\t\t\telse p.skipprop();\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\tvoid parseobjects()\n\t{\n\t\tif(!p.findbegin()) return;\n\n\t\ttoken t;\n\t\twhile(p.parse(t)) switch(t.type)\n\t\t{\n\t\t\tcase token::END:\n\t\t\t\treturn;\n\n\t\t\tcase token::PROP:\n\t\t\t\tif(!strcmp(t.s, \"Geometry\")) parsegeometry();\n\t\t\t\telse if(!strcmp(t.s, \"Model\")) parsemodel();\n\t\t\t\telse if(!strcmp(t.s, \"Material\")) parsematerial();\n\t\t\t\telse if(!strcmp(t.s, \"Deformer\")) parsedeformer();\n\t\t\t\telse if(!strcmp(t.s, \"AnimationCurve\")) parsecurve();\n\t\t\t\telse if(!strcmp(t.s, \"AnimationCurveNode\")) parsexform();\n\t\t\t\telse if(!strcmp(t.s, \"AnimationLayer\")) parseanimlayer();\n\t\t\t\telse if(!strcmp(t.s, \"AnimationStack\")) parseanimstack();\n\t\t\t\telse p.skipprop();\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\tvoid parseconnection()\n\t{\n\t\ttoken type, from, to, prop;\n\t\tif(!p.parse(type) || !p.parse(from) || !p.parse(to)) return;\n\t\tif(type.type == token::STRING && from.type == token::NUMBER && to.type == token::NUMBER)\n\t\t{\n\t\t\tnode *nf = nodes.find(from.f, NULL), *nt = nodes.find(to.f, NULL);\n\t\t\tif(!strcmp(type.s, \"OO\") && nf && nt)\n\t\t\t{\n\t\t\t\tif(nf->type() == node::GEOM && nt->type() == node::MODEL)\n\t\t\t\t\t((geomnode *)nf)->model = (modelnode *)nt;\n\t\t\t\telse if(nf->type() == node::MATERIAL && nt->type() == node::MODEL)\n\t\t\t\t\t((modelnode *)nt)->material = (materialnode *)nf;\n\t\t\t\telse if(nf->type() == node::LIMB && nt->type() == node::LIMB)\n\t\t\t\t\t((limbnode *)nf)->parent = (limbnode *)nt;\n\t\t\t\telse if(nf->type() == node::CLUSTER && nt->type() == node::SKIN)\n\t\t\t\t\t((clusternode *)nf)->skin = (skinnode *)nt;\n\t\t\t\telse if(nf->type() == node::SKIN && nt->type() == node::GEOM)\n\t\t\t\t\t((skinnode *)nf)->geom = (geomnode *)nt;\n\t\t\t\telse if(nf->type() == node::LIMB && nt->type() == node::CLUSTER)\n\t\t\t\t{\n\t\t\t\t\t((clusternode *)nt)->limb = (limbnode *)nf;\n\t\t\t\t\t((limbnode *)nf)->cluster = (clusternode *)nt;\n\t\t\t\t}\n\t\t\t\telse if(nf->type() == node::ANIMLAYER && nt->type() == node::ANIMSTACK)\n\t\t\t\t\t((animstacknode *)nt)->layers.add((animlayernode *)nf);\n\t\t\t\telse if(nf->type() == node::XFORM && nt->type() == node::ANIMLAYER)\n\t\t\t\t\t((animlayernode *)nt)->xforms.add((xformnode *)nf);\n\t\t\t}\n\t\t\telse if(!strcmp(type.s, \"OP\") && nf && nt && p.parse(prop) && prop.type == token::STRING)\n\t\t\t{\n\t\t\t\tif(nf->type() == node::CURVE && nt->type() == node::XFORM)\n\t\t\t\t{\n\t\t\t\t\tif(!strcmp(prop.s, \"d|X\")) ((xformnode *)nt)->setcurve(0, (curvenode *)nf);\n\t\t\t\t\telse if(!strcmp(prop.s, \"d|Y\")) ((xformnode *)nt)->setcurve(1, (curvenode *)nf);\n\t\t\t\t\telse if(!strcmp(prop.s, \"d|Z\")) ((xformnode *)nt)->setcurve(2, (curvenode *)nf);\n\t\t\t\t}\n\t\t\t\telse if(nf->type() == node::XFORM && nt->type() == node::LIMB)\n\t\t\t\t{\n\t\t\t\t\t((xformnode *)nf)->limb = (limbnode *)nt;\n\t\t\t\t\tif(!strcmp(prop.s, \"Lcl Translation\")) ((xformnode *)nf)->xform = xformnode::TRANS;\n\t\t\t\t\telse if(!strcmp(prop.s, \"Lcl Rotation\")) ((xformnode *)nf)->xform = xformnode::ROT;\n\t\t\t\t\telse if(!strcmp(prop.s, \"Lcl Scaling\")) ((xformnode *)nf)->xform = xformnode::SCALE; \n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tp.skipprop();\n\t}\n\n\tvoid parseconnections()\n\t{\n\t\tif(!p.findbegin()) return;\n\n\t\ttoken t;\n\t\twhile(p.parse(t)) switch(t.type)\n\t\t{\n\t\t\tcase token::END:\n\t\t\t\treturn;\n\n\t\t\tcase token::PROP:\n\t\t\t\tif(!strcmp(t.s, \"C\")) parseconnection();\n\t\t\t\telse p.skipprop();\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\tvoid geomnode::process()\n\t{\n\t\tif(model)\n\t\t{\n\t\t\temeshes[mesh].name = getnamekey(model->name);\n\t\t\tif(model->material) emeshes[mesh].material = getnamekey(model->material->name);\n\t\t\tif(model->geomtrans != Vec3(0, 0, 0)) for(int i = firstvert; i < lastvert; i++) epositions[i] += model->geomtrans;\n\t\t\tif(model->lclscale != Vec3(1, 1, 1))\n\t\t\t{\n\t\t\t\tfor(int i = firstvert; i < lastvert; i++)\n\t\t\t\t{\n\t\t\t\t\tepositions[i].setxyz(model->lclscale * Vec3(epositions[i]));\n\t\t\t\t}\n\t\t\t}\n\t\t\tif(model->lclrot != Vec3(0, 0, 0))\n\t\t\t{\n\t\t\t\tQuat lclquat = Quat::fromdegrees(model->lclrot);\n\t\t\t\tfor(int i = firstvert; i < lastvert; i++)\n\t\t\t\t{\n\t\t\t\t\tepositions[i].setxyz(lclquat.transform(Vec3(epositions[i])));\n\t\t\t\t\tenormals[i] = lclquat.transform(enormals[i]);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif(model->prerot != Vec3(0, 0, 0))\n\t\t\t{\n\t\t\t\tQuat prequat = Quat::fromdegrees(model->prerot);\n\t\t\t\tfor(int i = firstvert; i < lastvert; i++) \n\t\t\t\t{\n\t\t\t\t\tepositions[i].setxyz(prequat.transform(Vec3(epositions[i])));\n\t\t\t\t\tenormals[i] = prequat.transform(enormals[i]);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif(model->lcltrans != Vec3(0, 0, 0)) for(int i = firstvert; i < lastvert; i++) epositions[i] += model->lcltrans;\n\t\t}\n\t}\n\n\tvoid clusternode::process()\n\t{\n\t\tif(!limb || limb->index > 255 || !skin || !skin->geom || indexes.length() != weights.length()) return;\n\t\tgeomnode *g = skin->geom;\n\t\tif(g->blends.empty()) loopi(g->numverts) g->blends.add();\n\t\tloopv(indexes)\n\t\t{\n\t\t\tint idx = indexes[i];\n\t\t\tdouble weight = weights[i];\n\t\t\tg->blends[idx].addweight(weight, limb->index);\n\t\t}\n\t}\n\n\tvoid animstacknode::process()\n\t{\n\t\tif(layers.empty()) return;\n\t\tanimlayernode *l = layers[0];\n\t\tint numframes = l->numframes();\n\t\tif(numframes < 0) return;\n\n\t\teanim &a = eanims.add();\n\t\ta.name = getnamekey(name);\n\t\ta.startframe = eframes.length();\n\t\ta.fps = secs > 0 ? numframes/secs : 0;         \n\n\t\ttransform *poses = eposes.reserve(numframes*ejoints.length());\n\t\tloopj(numframes) \n\t\t{\n\t\t\teframes.add(eposes.length());\n\t\t\teposes.put(eposes.getbuf(), ejoints.length());\n\t\t}\n\t\tloopv(l->xforms)\n\t\t{\n\t\t\txformnode &x = *l->xforms[i];\n\t\t\tif(!x.limb) continue;\n\t\t\ttransform *dst = &poses[x.limb->index];\n\t\t\tloopj(numframes)\n\t\t\t{\n\t\t\t\tVec3 val = x.val;\n\t\t\t\tloopk(3) if(x.curves[k]) val[k] = x.curves[k]->vals[j];\n\t\t\t\tswitch(x.xform)\n\t\t\t\t{\n\t\t\t\tcase xformnode::TRANS: dst->pos = val; break;\n\t\t\t\tcase xformnode::ROT: dst->orient = Quat::fromdegrees(val); break;\n\t\t\t\tcase xformnode::SCALE: dst->scale = val; break;\n\t\t\t\t}\n\t\t\t\tdst += ejoints.length();\n\t\t\t}\n\t\t}\n#if 0\n\t\tloopv(eposes)\n\t\t{\n\t\t\ttransform &t = eposes[i];\n\t\t\tMatrix3x3 m(t.orient, t.scale);\n\t\t\tVec3 mscale(Vec3(m.a.x, m.b.x, m.c.x).magnitude(), Vec3(m.a.y, m.b.y, m.c.y).magnitude(), Vec3(m.a.z, m.b.z, m.c.z).magnitude());\n\t\t\tif(m.determinant() < 0) mscale = -mscale;\n\t\t\tm.a /= mscale;\n\t\t\tm.b /= mscale;\n\t\t\tm.c /= mscale;\n\t\t\tQuat morient(m); if(morient.w > 0) morient.flip();\n\t\t\tt.orient = morient;\n\t\t\tt.scale = mscale;\n\t\t}\n#endif\n\t}\n \n\tvoid geomnode::finish()\n\t{\n\t\tif(blends.empty()) return;\n\n\t\tloopv(blends) blends[i].finalize();\n\t\twhile(eblends.length() < lastvert) eblends.add();\n\t\tif(remap.length()) loopv(remap) eblends[firstvert + i] = blends[remap[i]];\n\t\telse loopv(blends) eblends[firstvert + i] = blends[i];\n\t}\n\n\tvoid limbnode::finish()\n\t{\n\t\tif(prerot == Vec3(0, 0, 0)) return;\n\t\tQuat prequat = Quat::fromdegrees(prerot);\n\t\tfor(int i = index; i < eposes.length(); i += ejoints.length())\n\t\t\teposes[i].orient = prequat * eposes[i].orient;\n\t}\n\n\tbool checkversion(stream *f)\n\t{\n\t\treturn f->getline(p.buf, sizeof(p.buf)) && strstr(p.buf, \"FBX 7\");\n\t}\n\n\tvoid parse(stream *f)\n\t{\n\t\tp.reset(f);\n\t\ttoken t;\n\t\twhile(p.parse(t)) switch(t.type)\n\t\t{\n\t\t\tcase token::PROP:\n\t\t\t\tif(!strcmp(t.s, \"Objects\")) parseobjects();\n\t\t\t\telse if(!strcmp(t.s, \"Connections\")) parseconnections();\n\t\t\t\telse p.skipprop();\n\t\t\t\tbreak;\n\t\t}\n\t\tenumerate(nodes, double, id, node *, n, { (void)id; n->process(); });\n\t\tenumerate(nodes, double, id, node *, n, { (void)id; n->finish(); });\n\t\tenumerate(nodes, double, id, node *, n, { (void)id; delete n; });\n\t\tnodes.clear();\n\t}\n}\n\nbool loadfbx(const char *filename, const filespec &spec)\n{\n\tint numfiles = 0;\n\twhile(filename)\n\t{\n\t\tconst char *endfile = strchr(filename, ',');\n\t\tconst char *file = endfile ? newstring(filename, endfile-filename) : filename;\n\t\tstream *f = openfile(file, \"r\");\n\t\tif(f)\n\t\t{\n\t\t\tif(fbx::checkversion(f)) \n\t\t\t{\n\t\t\t\tresetimporter(spec, numfiles > 0);\n\t\t\t\tnumfiles++;\n\t\t\t\tfbx::parse(f);\n\t\t\t}\n\t\t\tdelete f;\n\t\t}\n\n\t\tif(!endfile) break;\n\n\t\tdelete[] file;\n\t\tfilename = endfile+1; \n\t}\n\n\tif(!numfiles) return false;\n\n\tif(eanims.length() == 1)\n\t{\n\t\teanim &a = eanims.last();\n\t\tif(spec.name) a.name = spec.name;\n\t\tif(spec.fps > 0) a.fps = spec.fps;\n\t\ta.flags |= spec.flags;\n\t\tif(spec.endframe >= 0) a.endframe = a.startframe + spec.endframe;\n\t\telse if(spec.endframe < -1) a.endframe = a.startframe + max(eframes.length() - a.startframe + spec.endframe + 1, 0);\n\t\ta.startframe += spec.startframe;\n\t}\n\n\terotate *= Quat(M_PI/2, Vec3(1, 0, 0));\n\n\tmakeanims(spec);\n\tif(emeshes.length())\n\t{\n\t\tsmoothverts();\n\t\tmakemeshes(spec);\n\t}\n\n\treturn true;\n}\n\n#ifdef FTEPLUGIN\nnamespace fte\n{\n\tstatic vector<cvar_t*> cvars;\n\tstatic plugfsfuncs_t cppfsfuncs;\n\tstatic plugmodfuncs_t cppmodfuncs;\n\tstatic plugcorefuncs_t cppplugfuncs;\n\tstatic plugcvarfuncs_t cppcvarfuncs;\n\n\tstatic cvar_t *Cvar_Create(const char *name, const char *defaultval, unsigned int flags, const char *description, const char *groupname)\n\t{\t//could maybe fill with environment settings perhaps? yuck.\n\t\tcvar_t *v = NULL;\n\t\tfor (int i = 0; i < cvars.length(); i++)\n\t\t\tif (!strcmp(cvars[i]->name, name))\n\t\t\t\treturn cvars[i];\n\t\tif (!v)\n\t\t{\n\t\t\tv = cvars.add() = new cvar_t();\n\t\t\tv->name = strdup(name);\n\t\t\tv->string = strdup(defaultval);\n\t\t\tv->value = atof(v->string);\n\t\t\tv->ival = atoi(v->string);\n\t\t}\n\t\treturn v;\n\t};\n\n\tstatic void SetupFTEPluginFuncs(void)\n\t{\n\t\tcppfsfuncs.OpenVFS = [](const char *filename, const char *mode, enum fs_relative relativeto)\n\t\t{\n\t\t\tstream *f = openfile(filename, \"rb\");\n\t\t\tif (!f)\n\t\t\t\treturn (vfsfile_t*)nullptr;\n\t\t\tstruct cppfile_t : public vfsfile_t\n\t\t\t{\n\t\t\t\tstatic int ReadBytes (struct vfsfile_s *file, void *buffer, int bytestoread)\n\t\t\t\t{\n\t\t\t\t\tauto c = static_cast<cppfile_t*>(file);\n\t\t\t\t\treturn c->f->read(buffer, bytestoread);\n\t\t\t\t}\n\t\t\t\tstatic qboolean Seek (struct vfsfile_s *file, qofs_t pos)\n\t\t\t\t{\n\t\t\t\t\tauto c = static_cast<cppfile_t*>(file);\n\t\t\t\t\treturn c->f->seek(pos)?qtrue:qfalse;\n\t\t\t\t}\n\t\t\t\tstatic qofs_t Tell (struct vfsfile_s *file)\n\t\t\t\t{\n\t\t\t\t\tauto c = static_cast<cppfile_t*>(file);\n\t\t\t\t\treturn c->f->tell();\n\t\t\t\t}\n\t\t\t\tstatic qofs_t GetLen (struct vfsfile_s *file)\n\t\t\t\t{\n\t\t\t\t\tauto c = static_cast<cppfile_t*>(file);\n\t\t\t\t\treturn c->f->size();\n\t\t\t\t}\n\t\t\t\tstatic qboolean Close (struct vfsfile_s *file)\n\t\t\t\t{\n\t\t\t\t\tauto c = static_cast<cppfile_t*>(file);\n\t\t\t\t\tc->f->close();\n\t\t\t\t\tdelete c;\n\t\t\t\t\treturn qtrue;\n\t\t\t\t}\n\t\t\t\tcppfile_t(stream *sourcefile):f(sourcefile)\n\t\t\t\t{\n\t\t\t\t\tvfsfile_t::ReadBytes = ReadBytes;\n\t\t\t\t\tvfsfile_t::Seek = Seek;\n\t\t\t\t\tvfsfile_t::Tell = Tell;\n\t\t\t\t\tvfsfile_t::GetLen = GetLen;\n\t\t\t\t\tvfsfile_t::Close = Close;\n\t\t\t\t}\n\t\t\t\tstream *f;\n\t\t\t};\n\t\t\tcppfile_t *c = new cppfile_t(f);\n\t\t\treturn static_cast<vfsfile_t*>(c);\n\t\t};\n\n\t\tcppmodfuncs.version = MODPLUGFUNCS_VERSION;\n\t\tcppmodfuncs.RegisterModelFormatText = [](const char *formatname, char *magictext, qboolean (QDECL *load) (struct model_s *mod, void *buffer, size_t fsize))\n\t\t{\t//called explicitly because we're lame.\n\t\t\treturn 0;\n\t\t};\n\t\tcppmodfuncs.RegisterModelFormatMagic = [](const char *formatname, qbyte *magic, size_t magicsize, qboolean (QDECL *load) (struct model_s *mod, void *buffer, size_t fsize))\n\t\t{\t//called explicitly because we're lame.\n\t\t\treturn 0;\n\t\t};\n\t\tcppplugfuncs.GMalloc = [](zonegroup_t *ctx, size_t size)\n\t\t{\n\t\t\t/*leak the memory, because we're lazy*/\n\t\t\tvoid *ret = malloc(size);\n\t\t\tmemset(ret, 0, size);\n\t\t\treturn ret;\n\t\t};\n\n\t\tcppmodfuncs.ConcatTransforms = [](const float in1[3][4], const float in2[3][4], float out[3][4])\n\t\t{\n\t\t\tout[0][0] = in1[0][0] * in2[0][0] + in1[0][1] * in2[1][0] +\n\t\t\t\t\t\tin1[0][2] * in2[2][0];\n\t\t\tout[0][1] = in1[0][0] * in2[0][1] + in1[0][1] * in2[1][1] +\n\t\t\t\t\t\tin1[0][2] * in2[2][1];\n\t\t\tout[0][2] = in1[0][0] * in2[0][2] + in1[0][1] * in2[1][2] +\n\t\t\t\t\t\tin1[0][2] * in2[2][2];\n\t\t\tout[0][3] = in1[0][0] * in2[0][3] + in1[0][1] * in2[1][3] +\n\t\t\t\t\t\tin1[0][2] * in2[2][3] + in1[0][3];\n\t\t\tout[1][0] = in1[1][0] * in2[0][0] + in1[1][1] * in2[1][0] +\n\t\t\t\t\t\tin1[1][2] * in2[2][0];\n\t\t\tout[1][1] = in1[1][0] * in2[0][1] + in1[1][1] * in2[1][1] +\n\t\t\t\t\t\tin1[1][2] * in2[2][1];\n\t\t\tout[1][2] = in1[1][0] * in2[0][2] + in1[1][1] * in2[1][2] +\n\t\t\t\t\t\tin1[1][2] * in2[2][2];\n\t\t\tout[1][3] = in1[1][0] * in2[0][3] + in1[1][1] * in2[1][3] +\n\t\t\t\t\t\tin1[1][2] * in2[2][3] + in1[1][3];\n\t\t\tout[2][0] = in1[2][0] * in2[0][0] + in1[2][1] * in2[1][0] +\n\t\t\t\t\t\tin1[2][2] * in2[2][0];\n\t\t\tout[2][1] = in1[2][0] * in2[0][1] + in1[2][1] * in2[1][1] +\n\t\t\t\t\t\tin1[2][2] * in2[2][1];\n\t\t\tout[2][2] = in1[2][0] * in2[0][2] + in1[2][1] * in2[1][2] +\n\t\t\t\t\t\tin1[2][2] * in2[2][2];\n\t\t\tout[2][3] = in1[2][0] * in2[0][3] + in1[2][1] * in2[1][3] +\n\t\t\t\t\t\tin1[2][2] * in2[2][3] + in1[2][3];\n\t\t};\n\n\t\tcppmodfuncs.GenMatrixPosQuat4Scale = [](const vec3_t pos, const vec4_t quat, const vec3_t scale, float result[12])\n\t\t{\n\t\t\tMatrix3x4 m = Matrix3x4(Quat(quat), Vec3(pos), Vec3(scale));\n\t\t\tresult[0] = m.a.x;\n\t\t\tresult[1] = m.b.x;\n\t\t\tresult[2] = m.c.x;\n\t\t\tresult[3] = m.a.w;\n\n\t\t\tresult[4] = m.a.y;\n\t\t\tresult[5] = m.b.y;\n\t\t\tresult[6] = m.c.y;\n\t\t\tresult[7] = m.b.w;\n\n\t\t\tresult[8] = m.a.z;\n\t\t\tresult[9] = m.b.z;\n\t\t\tresult[10] = m.c.z;\n\t\t\tresult[11] = m.c.w;\n\t\t};\n\n\t\tcppmodfuncs.GetTexture = [](const char *identifier, const char *subpath, unsigned int flags, void *fallbackdata, void *fallbackpalette, int fallbackwidth, int fallbackheight, uploadfmt_t fallbackfmt)\n\t\t{\n\t\t\timage_t *img = (image_t*)cppplugfuncs.GMalloc(NULL, sizeof(*img)+strlen(identifier)+1);\n\t\t\timg->ident = (char*)(img+1);\n\t\t\tstrcpy(img->ident, identifier);\n\t\t\timg->flags = flags;\n\t\t\treturn img;\n\t\t};\n\n\t\tcppmodfuncs.AccumulateTextureVectors = [](vecV_t *const vc, vec2_t *const tc, vec3_t *nv, vec3_t *sv, vec3_t *tv, const index_t *idx, int numidx, qboolean calcnorms)\n\t\t{\t//once per surface that shares the set of verts\n\t\t};\n\t\tcppmodfuncs.NormaliseTextureVectors = [](vec3_t *n, vec3_t *s, vec3_t *t, int v, qboolean calcnorms)\n\t\t{\t//once per shared set of verts.\n\t\t};\n\n\t\tcppplugfuncs.GetEngineInterface = [](const char *interfacename, size_t structsize)\n\t\t{\n\t\t\tvoid *ret = nullptr;\n\t\t\tif (!strcmp(interfacename, plugfsfuncs_name))\n\t\t\t\tret = &cppfsfuncs;\n\t\t\tif (!strcmp(interfacename, plugmodfuncs_name))\n\t\t\t\tret = &cppmodfuncs;\n\t\t\treturn ret;\n\t\t};\n\n\t\tcppcvarfuncs.GetNVFDG = Cvar_Create;\n\t}\n\textern \"C\"\n\t{\t//our plugin-style stuff has a few external dependancies not provided via pointers...\n\t\tvoid ImgTool_SetupPalette(void);\n\t\tqboolean QDECL Mod_LoadGLTFModel (struct model_s *mod, void *buffer, size_t fsize);\n\t\tqboolean QDECL Mod_LoadGLBModel (struct model_s *mod, void *buffer, size_t fsize);\n\t\tqboolean Plug_GLTF_Init(void);\n\t\tplugcorefuncs_t *plugfuncs = &cppplugfuncs;\n\t\tplugcmdfuncs_t *cmdfuncs;\n\t\tplugcvarfuncs_t *cvarfuncs = &cppcvarfuncs;\n\t\tvoid Q_strlcpy(char *d, const char *s, int n)\n\t\t{\n\t\t\tint i;\n\t\t\tn--;\n\t\t\tif (n < 0)\n\t\t\t\treturn;\t//this could be an error\n\n\t\t\tfor (i=0; *s; i++)\n\t\t\t{\n\t\t\t\tif (i == n)\n\t\t\t\t\tbreak;\n\t\t\t\t*d++ = *s++;\n\t\t\t}\n\t\t\t*d='\\0';\n\t\t}\n\t\tvoid Q_strlcat(char *d, const char *s, int n)\n\t\t{\n\t\t\tif (n)\n\t\t\t{\n\t\t\t\tint dlen = strlen(d);\n\t\t\t\tint slen = strlen(s)+1;\n\t\t\t\tif (slen > (n-1)-dlen)\n\t\t\t\t\tslen = (n-1)-dlen;\n\t\t\t\tmemcpy(d+dlen, s, slen);\n\t\t\t\td[n - 1] = 0;\n\t\t\t}\n\t\t}\n\t}\n\n\ttransform ftetransform(float bm[12], bool invert)\n\t{\n\t\tMatrix3x3 m(Vec3(bm[0], bm[1], bm[2]), Vec3(bm[4], bm[5], bm[6]), Vec3(bm[8], bm[9], bm[10]));\n\t\tm.transpose();\n\t\tVec3 pos(bm[3], bm[7], bm[11]);\n\t\ttransform t;\n\n\t\tVec3 mscale(Vec3(m.a.x, m.b.x, m.c.x).magnitude(), Vec3(m.a.y, m.b.y, m.c.y).magnitude(), Vec3(m.a.z, m.b.z, m.c.z).magnitude());\n\t\t// check determinant for sign of scaling\n\t\tif(Matrix3x3(m).determinant() < 0) mscale = -mscale;\n\t\tm.a /= mscale;\n\t\tm.b /= mscale;\n\t\tm.c /= mscale;\n\t\tt.orient = Quat(m);\n\t\tif(t.orient.w > 0) t.orient.flip();\n\t\tt.scale = mscale;\n\n\t\tif (invert)\n\t\t{\n\t\t\t// invert the translate\n\t\t\tt.pos[0] = -(pos[0] * m.a[0] + pos[1] * m.a[1] + pos[2] * m.a[2]);\n\t\t\tt.pos[1] = -(pos[0] * m.b[0] + pos[1] * m.b[1] + pos[2] * m.b[2]);\n\t\t\tt.pos[2] = -(pos[0] * m.c[0] + pos[1] * m.c[1] + pos[2] * m.c[2]);\n\t\t}\n\t\telse\n\t\t\tt.pos = pos;\n\t\treturn t;\n\t}\n\n\tbool loadfte(model_t *mod, const filespec &spec)\n\t{\t//import from fte's structs and convert to iqmtool's c++isms\n\t\tif (mod->type != mod_alias)\n\t\t\treturn false;\t//err...\n\t\tgaliasinfo_t *root = (galiasinfo_t*)mod->meshinfo, *surf=root;\n\t\tif (surf)\n\t\t{\n\t\t\tresetimporter(spec);\n\n\t\t\tif (surf->baseframeofs)\n\t\t\t{\n\t\t\t\tint b2;\n\t\t\t\tfor (int b = 0; b < surf->numbones; b++)\n\t\t\t\t{\n\t\t\t\t\ttransform p(ftetransform(surf->ofsbones[b].inverse, true));\n\n\t\t\t\t\t//spit out the joint info\n\t\t\t\t\tejoint &j = ejoints.add();\n\t\t\t\t\tj.name = surf->ofsbones[b].name;\n\t\t\t\t\tj.parent = surf->ofsbones[b].parent;\n\n\t\t\t\t\tfor (b2 = 0; b2 < b; b2++)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (!strcmp(surf->ofsbones[b2].name, surf->ofsbones[b].name))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tdefformatstring(newname, \"b#%i %s\", b, j.name);\n\t\t\t\t\t\t\tconoutf(\"warning: Bones %i and %i are both called %s\", b2, b, j.name);\n\t\t\t\t\t\t\tj.name = getnamekey(newname);\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t//and the base pose\n\t\t\t\t\teposes.add(p);\n\t\t\t\t}\n\t\t\t\tmakerelativebasepose();\n\n#if 1\t\t\t//import the animations\n\t\t\t\tfor (int animidx = 0; animidx < surf->numanimations; animidx++)\n\t\t\t\t{\n\t\t\t\t\tauto &anim = surf->ofsanimations[animidx];\n\t\t\t\t\tint firstframe = eframes.length();\n\t\t\t\t\tvector<float[12]> bonebuf;\n\n\t\t\t\t\tif (!anim.numposes)\t//drop any animations that don't have any poses defined...\n\t\t\t\t\t\tcontinue;\n\n\t\t\t\t\tfor (int f = 0; f < anim.numposes; f++)\n\t\t\t\t\t{\n\t\t\t\t\t\tskeltype_t sk = anim.skeltype;\n\t\t\t\t\t\tfloat time = f/anim.rate;\n\t\t\t\t\t\tfloat *bonedata;\n\t\t\t\t\t\tif (anim.GetRawBones)\n\t\t\t\t\t\t\tbonedata = anim.GetRawBones(surf, &anim, time, bonebuf.reserve(surf->numbones)[0], NULL, surf->numbones);\n\t\t\t\t\t\telse if (anim.boneofs)\n\t\t\t\t\t\t\tbonedata = (float*)anim.boneofs;\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tbonedata = (float*)surf->baseframeofs, sk = SKEL_ABSOLUTE;\t//abs...\n\n\t\t\t\t\t\tif (sk == SKEL_RELATIVE)\n\t\t\t\t\t\t\t;\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tconoutf(\"warning: Unusable skeletal type for import - %i\", (int)sk);\n\n\t\t\t\t\t\teframes.add(eposes.length());\n\t\t\t\t\t\tfor (int b = 0; b < surf->numbones; b++, bonedata += 12)\n\t\t\t\t\t\t\teposes.add(ftetransform(bonedata, false));\n\t\t\t\t\t}\n\n\n\t\t\t\t\teanim &a = eanims.add();\n\t\t\t\t\tif(spec.name) a.name = getnamekey(spec.name);\n\t\t\t\t\telse if (*anim.name) a.name = getnamekey(anim.name);\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tstring name;\n\t\t\t\t\t\tcopystring(name, mod->name);\n\t\t\t\t\t\tchar *end = strrchr(name, '.');\n\t\t\t\t\t\tif(end) *end = '\\0';\n\t\t\t\t\t\ta.name = getnamekey(name);\n\t\t\t\t\t}\n\t\t\t\t\ta.startframe = firstframe;\n\t\t\t\t\ta.fps = anim.rate;\n\t\t\t\t\ta.flags = anim.loop?IQM_LOOP:0;\n\t\t\t\t\ta.flags |= spec.flags;\n\t\t\t\t\ta.endframe = eframes.length();\n\t\t\t\t}\n#endif\n\t\t\t}\n\n\t\t\tfor(; surf; surf = surf->nextsurf)\n\t\t\t{\n\t\t\t\tif (surf->shares_bones != root->shares_bones)\n\t\t\t\t{\n\t\t\t\t\tconoutf(\"warning: Ignoring surface %s as it has a different rig\", surf->surfacename);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tif (surf->numindexes)\n\t\t\t\t{\n\t\t\t\t\tunsigned int firstvert=epositions.length();\n\t\t\t\t\tfor (int v = 0; v < surf->numverts; v++)\n\t\t\t\t\t{\n\t\t\t\t\t\tVec3 pos(surf->ofs_skel_xyz[v][0], surf->ofs_skel_xyz[v][1], surf->ofs_skel_xyz[v][2]);\n\t\t\t\t\t\tVec3 norm;\n\t\t\t\t\t\tif (surf->ofs_skel_norm)\n\t\t\t\t\t\t\tnorm = Vec3(surf->ofs_skel_norm[v][0],  surf->ofs_skel_norm[v][1],  surf->ofs_skel_norm[v][2]);\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tnorm = Vec3(0,0,0);\n\n\t\t\t\t\t\tetexcoords.add(Vec4(surf->ofs_st_array[v][0], surf->ofs_st_array[v][1], 0, 0));\n\t\t\t\t\t\tif (surf->ofs_rgbaf)\n\t\t\t\t\t\t\tecolors.add(Vec4(surf->ofs_rgbaf[v][0], surf->ofs_rgbaf[v][1], surf->ofs_rgbaf[v][2], surf->ofs_rgbaf[v][3]));\n\t\t\t\t\t\telse if (surf->ofs_rgbaub)\n\t\t\t\t\t\t\tecolors.add(Vec4(surf->ofs_rgbaub[v][0]/255.0, surf->ofs_rgbaub[v][1]/255.0, surf->ofs_rgbaub[v][2]/255.0, surf->ofs_rgbaub[v][3]/255.0));\n\n//\t\t\t\t\t\tif (surf->ofs_skel_svect)\n//\t\t\t\t\t\t\tetangents.add  (Vec4(surf->ofs_skel_svect[v][0], surf->ofs_skel_svect[v][1], surf->ofs_skel_svect[v][2], 0));\n//\t\t\t\t\t\tif (surf->ofs_skel_tvect)\n//\t\t\t\t\t\t\tebitangents.add(Vec3(surf->ofs_skel_tvect[v][0], surf->ofs_skel_tvect[v][1], surf->ofs_skel_tvect[v][2]));\n\n\t\t\t\t\t\tif (surf->shares_bones == root->shares_bones && surf->ofs_skel_weight && surf->ofs_skel_idx)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tblendcombo b = {};\n\t\t\t\t\t\t\t//Vec3 newpos(0,0,0);\n\t\t\t\t\t\t\t//Vec3 newnorm(0,0,0);\n\t\t\t\t\t\t\tfor (size_t w = 0; w < 4; w++)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t//newpos += bonerepositions[surf->ofs_skel_idx[v][w]].transform(pos) * surf->ofs_skel_weight[v][w];\n\t\t\t\t\t\t\t\t//newnorm += bonerepositions[surf->ofs_skel_idx[v][w]].transform3(norm) * surf->ofs_skel_weight[v][w];\n\t\t\t\t\t\t\t\tif (surf->ofs_skel_weight[v][w] > 0)\n\t\t\t\t\t\t\t\t\tb.addweight(surf->ofs_skel_weight[v][w], surf->ofs_skel_idx[v][w]);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tb.finalize();\n\t\t\t\t\t\t\teblends.add(b);\n\t\t\t\t\t\t\t//pos = newpos;\n\t\t\t\t\t\t\t//norm = newnorm;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tepositions.add(Vec4(pos));\n\t\t\t\t\t\tenormals.add(norm);\n\t\t\t\t\t}\n\n\t\t\t\t\t//iqms don't support skins/skingroups themselves.\n\t\t\t\t\t//we have only surface name and texture(aka material) name.\n\t\t\t\t\t//so use the diffuse texture's name where we can\n\t\t\t\t\t//\ta) its already processed properly so no ''path/model.gltf/sectionthatdoesntevenexistondisk' locations.\n\t\t\t\t\t//\tb) its more likely to show something without needing to synthesize shaders/textures.\n\t\t\t\t\t//we should probably cvar this.\n\t\t\t\t\tconst char *materialname;\n\t\t\t\t\tif (surf->numskins && surf->ofsskins[0].numframes && surf->ofsskins[0].frame[0].texnums.base && *surf->ofsskins[0].frame[0].texnums.base->ident != '$')\n\t\t\t\t\t\tmaterialname = surf->ofsskins[0].frame[0].texnums.base->ident;\n\t\t\t\t\telse if (surf->numskins)\n\t\t\t\t\t\tmaterialname = surf->ofsskins[0].name;\n\t\t\t\t\telse\n\t\t\t\t\t\tmaterialname = surf->surfacename;\n\n\t\t\t\t\tif (surf->nummorphs)\n\t\t\t\t\t\tconoutf(\"warning: Morph targets on input surface \\\"%s\\\" \\\"%s\\\" cannot be supported\", surf->surfacename, materialname);\n\n\t\t\t\t\temesh mesh(surf->surfacename, materialname, etriangles.length());\n\n\t\t\t\t\t//add in some extra surface properties.\n\t\t\t\t\tmesh.hasexplicits = true;\n\t\t\t\t\tmesh.explicits.contents = surf->contents;\n\t\t\t\t\tmesh.explicits.surfaceflags = surf->csurface.flags;\n\t\t\t\t\tmesh.explicits.body = surf->surfaceid;\n\t\t\t\t\tmesh.explicits.geomset = surf->geomset;\n\t\t\t\t\tmesh.explicits.geomid = surf->geomid;\n\t\t\t\t\tmesh.explicits.mindist = surf->mindist;\n\t\t\t\t\tmesh.explicits.maxdist = surf->maxdist;\n\n\t\t\t\t\temeshes.add(mesh);\n\t\t\t\t\tfor (int idx = 0; idx+2 < surf->numindexes; idx+=3)\n\t\t\t\t\t\tetriangles.add(etriangle(surf->ofs_indexes[idx+0]+firstvert, surf->ofs_indexes[idx+1]+firstvert, surf->ofs_indexes[idx+2]+firstvert));\n\t\t\t\t}\n\t\t\t}\n\t\t\tmakeanims(spec);\n\t\t\tif (emeshes.length())\n\t\t\t{\n\t\t\t\tsmoothverts();\n\t\t\t\tmakemeshes(spec);\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\tbool loadglb(const char *filename, const filespec &spec)\n\t{\n\t\tbool ret = false;\n\t\tmodel_t mod={};\n\t\tstream *f = openfile(filename, \"rb\");\n\t\tQ_strlcpy(mod.name, filename, sizeof(mod.name));\n\t\tif (f)\n\t\t{\n\t\t\tsize_t sz = f->size();\n\t\t\tauto filebuf = new char[sz];\n\t\t\tif (sz == f->read(filebuf, sz))\n\t\t\t{\n\t\t\t\tSetupFTEPluginFuncs();\n\t\t\t\tif (Plug_GLTF_Init())\n\t\t\t\t\tif (Mod_LoadGLBModel(&mod, filebuf, sz))\n\t\t\t\t\t\tret = loadfte(&mod, spec);\n\t\t\t}\n\t\t\tdelete[] filebuf;\n\t\t\tdelete f;\n\t\t}\n\t\treturn ret;\n\t}\n\tbool loadgltf(const char *filename, const filespec &spec)\n\t{\n\t\tbool ret = false;\n\t\tmodel_t mod={};\n\t\tstream *f = openfile(filename, \"rb\");\n\t\tQ_strlcpy(mod.name, filename, sizeof(mod.name));\n\t\tif (f)\n\t\t{\n\t\t\tsize_t sz = f->size();\n\t\t\tauto filebuf = new char[sz];\n\t\t\tif (sz == f->read(filebuf, sz))\n\t\t\t{\n\t\t\t\tSetupFTEPluginFuncs();\n\t\t\t\tif (Plug_GLTF_Init())\n\t\t\t\t\tif (Mod_LoadGLTFModel(&mod, filebuf, sz))\n\t\t\t\t\t\tret = loadfte(&mod, spec);\n\t\t\t}\n\t\t\tdelete[] filebuf;\n\t\t\tdelete f;\n\t\t}\n\t\treturn ret;\n\t}\n}\n#endif\n\nvoid genhitboxes(vector<hitbox> &hitboxes)\n{\n\t//for half-life weenies that are too lazy to define their own hitmeshes\n\tif (!hitboxes.length())\n\t\treturn;\n\t\n\tfilespec inspec;\n\tinspec.reset();\n\tresetimporter(inspec);\n\tloopv(hitboxes)\n\t{\n\t\thitbox &hb = hitboxes[i];\n\t\tint bone = -1;\n\t\tfor (bone = 0; bone < joints.length(); bone++)\n\t\t\tif (!strcasecmp(hb.bone, &stringdata[joints[bone].name]))\n\t\t\t\tbreak;\n\t\tif (bone == joints.length())\n\t\t{\n\t\t\tfatal(\"error: hitbox attached to invalid bone %s\", hb.bone);\n\t\t\tcontinue;\t//this hitbox is invalid\n\t\t}\n\n\t\temesh &m = emeshes.add();\n\t\tint firstvert = epositions.length();\n\t\tm.firsttri = etriangles.length();\n\t\tm.material = \"textures/common/hitmesh\";\t//to be vaugely compatible with q3map2's default shader names\n\t\tstring tmp;\n\t\tformatstring(tmp, \"hitbox%i\", hitboxes[i].body);\n\t\tm.name = newstring(tmp);\n\t\tm.hasexplicits = true;\n\t\tm.explicits = {};\n\t\tm.explicits.contents = 0x02000000;\n\t\tm.explicits.surfaceflags = 0x80;\n\t\tm.explicits.body = hitboxes[i].body;\n\t\tm.explicits.geomset = ~0u;\n\n\t\t//spit out some verts\n\t\tMatrix3x4 bm(mjoints[bone]);\n\t\tbm.invert();\n\t\tfor (int j = 0; j < 8; j++)\n\t\t{\n\t\t\tVec3 p = Vec3((j&1)?hb.mins[0]:hb.maxs[0], (j&2)?hb.mins[1]:hb.maxs[1], (j&4)?hb.mins[2]:hb.maxs[2]);\n\t\t\tp = bm.transform(p);\n\t\t\tepositions.add(Vec4(p, 0));\n\t\t\tenormals.add(p);\n\t\t\tetexcoords.add(Vec4(0,0,0,0));\n\t\t\teblends.add(blendcombo()).addweight(1, bone);\n\t\t}\n\n\t\t//and some triangles for them\n\t\tetriangles.add(etriangle(firstvert+2, firstvert+1, firstvert+0));\n\t\tetriangles.add(etriangle(firstvert+2, firstvert+3, firstvert+1));\n\t\tetriangles.add(etriangle(firstvert+4, firstvert+5, firstvert+6));\n\t\tetriangles.add(etriangle(firstvert+5, firstvert+7, firstvert+6));\n\n\t\tetriangles.add(etriangle(firstvert+0, firstvert+1, firstvert+4));\n\t\tetriangles.add(etriangle(firstvert+1, firstvert+5, firstvert+4));\n\t\tetriangles.add(etriangle(firstvert+6, firstvert+3, firstvert+2));\n\t\tetriangles.add(etriangle(firstvert+6, firstvert+7, firstvert+3));\n\n\t\tetriangles.add(etriangle(firstvert+4, firstvert+2, firstvert+0));\n\t\tetriangles.add(etriangle(firstvert+4, firstvert+6, firstvert+2));\n\t\tetriangles.add(etriangle(firstvert+1, firstvert+3, firstvert+5));\n\t\tetriangles.add(etriangle(firstvert+3, firstvert+7, firstvert+5));\n\t}\n\tsmoothverts();\n\tmakemeshes(inspec);\n}\n\nint framesize = 0;\nvector<ushort> animdata;\n\n#define QUANTIZE(offset, base, scale) ushort(0.5f + (float(offset) - base) / scale)\n\nstatic int jsort(const void *va, const void *vb)\n{\n\tjoint &a = joints[*(int*)va];\n\tjoint &b = joints[*(int*)vb];\n\n\tif (a.group == b.group)\n\t{\n\t\tif (*(int*)va > *(int*)vb)\n\t\t\treturn 1;\n\t\telse\n\t\t\treturn -1;\n\t}\n\telse if (a.group < b.group)\n\t\treturn -1;\n\telse\n\t\treturn 1;\n}\nvoid calcanimdata()\n{\n\thashtable<const char *, bool> bonewarnings;\n\n\t//reorder the joints according to their groups, including a lookup so we can fix up other mappings\n\tint *jointremap = new int[joints.length()];\n\tint *jointremapinv = new int[joints.length()];\n\tloopv(joints) jointremap[i] = i;\n\tqsort(jointremap, joints.length(), sizeof(int), jsort);\n\tvector<joint> oj;\n\tjoints.swap(oj);\n\tbool dodgyorder = false;\n\tloopv(oj)\n\t\tjointremapinv[jointremap[i]] = i;\n\tloopv(oj)\n\t{\n\t\tjoint &j = joints.add(oj[jointremap[i]]);\n\t\tif (j.parent >= 0)\n\t\t{\n\t\t\tj.parent = jointremapinv[j.parent];\n\t\t\tif (j.parent >= i)\n\t\t\t\tdodgyorder = true;\n\t\t}\n\t}\n\tif (dodgyorder)\n\t{\n\t\tprintbonelist();\n\t\tfatal(\"Bone group reordering resulted in invalid order\");\n\t}\n\n\t//try and ensure that the animation bone order matches the mesh bones\n\tloopv(joints)\n\t{\n\t\tpose &j = poses.add();\n\t\tj.name = &stringdata[joints[i].name];\n\t\tj.parent = joints[i].parent;\n\t\tloopk(10) { j.offset[k] = 1e16f; j.scale[k] = -1e16f; }\n\t}\n\n\tloopv(frames)\n\t{\n\t\tframe &fr = frames[i];\n\t\tloopl(fr.pose.length())\n\t\t{\n\t\t\tframe::framepose &p = fr.pose[l];\n\t\t\tp.remap = -1;\n\t\t\tloopvk(poses)\n\t\t\t{\n//\t\t\t\tif (poses[k].parent == p.boneparent)\n\t\t\t\tif (!strcmp(poses[k].name, p.bonename))\n\t\t\t\t{\n\t\t\t\t\tif (poses[k].parent == -1 || p.boneparent == -1)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (poses[k].parent != -1 || p.boneparent != -1)\n\t\t\t\t\t\t\tfatal(\"Error: bone %s has inconsistent parents\\n\", p.bonename);\n\t\t\t\t\t}\n\t\t\t\t\telse if (strcmp(poses[poses[k].parent].name, fr.pose[p.boneparent].bonename))\n\t\t\t\t\t\tfatal(\"Error: bone %s has inconsistent parents (%s vs %s)\\n\", p.bonename, poses[poses[k].parent].name, fr.pose[p.boneparent].bonename);\n\t\t\t\t\tp.remap = k;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif(p.remap < 0)\n\t\t\t{\n\t\t\t\t//if we have a mesh, then any extra bones are surplus to requirements.\n\t\t\t\t//otherwise we play safe and keep all (which is kinda awkward, because there's no way to name them in the output iqm).\n\t\t\t\tif (!joints.empty())\n\t\t\t\t{\n\t\t\t\t\tif (!bonewarnings.find(p.bonename, false))\n\t\t\t\t\t{\n\t\t\t\t\t\tconst char *a = \"UNKNOWN\";\n\t\t\t\t\t\tloopvj(anims)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif ((uint)i >= anims[j].firstframe && (uint)i < anims[j].firstframe+anims[j].numframes)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\ta = &stringdata[anims[j].name];\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbonewarnings.access(p.bonename, true);\n\t\t\t\t\t\tif (p.boneparent >= 0)\n\t\t\t\t\t\t\tconoutf(\"warning: ignoring bone %s (parent %s) (surplus in %s)\", p.bonename, fr.pose[p.boneparent].bonename, a);\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tconoutf(\"warning: ignoring bone %s (root) (surplus in %s)\", p.bonename, a);\n\t\t\t\t\t}\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (p.boneparent >= 0)\n\t\t\t\t\tconoutf(\"bone %s (%s)\", p.bonename, poses[p.boneparent].name);\n\t\t\t\telse\n\t\t\t\t\tconoutf(\"bone %s\", p.bonename);\n\t\t\t\tp.remap = poses.length();\n\t\t\t\tpose &j = poses.add();\n\t\t\t\tj.name = p.bonename;\n\t\t\t\tj.parent = -1;\n\t\t\t\tif(p.boneparent >= 0) {\n\t\t\t\t\tloopk(p.remap)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (!strcmp(poses[k].name, fr.pose[p.boneparent].bonename))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tj.parent = k;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tloopk(10) { j.offset[k] = 1e16f; j.scale[k] = -1e16f; }\n\t\t\t}\n\n\t\t\tpose &j = poses[p.remap];\n\t\t\ttransform &f = p.tr;\n\t\t\tloopk(3)\n\t\t\t{\n\t\t\t\tj.offset[k] = min(j.offset[k], float(f.pos[k]));\n\t\t\t\tj.scale[k] = max(j.scale[k], float(f.pos[k]));\n\t\t\t}\n\t\t\tloopk(4)\n\t\t\t{\n\t\t\t\tj.offset[3+k] = min(j.offset[3+k], float(f.orient[k]));\n\t\t\t\tj.scale[3+k] = max(j.scale[3+k], float(f.orient[k]));\n\t\t\t}\n\t\t\tloopk(3)\n\t\t\t{\n\t\t\t\tj.offset[7+k] = min(j.offset[7+k], float(f.scale[k]));\n\t\t\t\tj.scale[7+k] = max(j.scale[7+k], float(f.scale[k]));\n\t\t\t}\n\t\t}\n\t}\n\tloopv(poses)\n\t{\n\t\tpose &j = poses[i];\n\t\tloopk(10) \n\t\t{\n\t\t\tj.scale[k] -= j.offset[k];\n\t\t\tif(j.scale[k] >= 1e-10f) { framesize++; j.scale[k] /= 0xFFFF; j.flags |= 1<<k; } \n\t\t\telse j.scale[k] = 0.0f;\n\t\t}\n\t}\n#if 0\n\tint runlength = 0, blocksize = 0, blocks = 0;\n\t#define FLUSHVAL(val) \\\n\t\tif(!blocksize || (animdata.last() == val ? runlength >= 0xFF : runlength || blocksize > 0xFF)) \\\n\t\t{ \\\n\t\t\tanimdata.add(0); \\\n\t\t\tanimdata.add(val); \\\n\t\t\tblocksize = 1; \\\n\t\t\trunlength = 0; \\\n\t\t\tblocks++; \\\n\t\t} \\\n\t\telse if(animdata.last() == val) \\\n\t\t{ \\\n\t\t\tanimdata[animdata.length()-blocksize-1] += 0x10; \\\n\t\t\trunlength++; \\\n\t\t} \\\n\t\telse \\\n\t\t{ \\\n\t\t\tanimdata[animdata.length()-blocksize-1]++; \\\n\t\t\tanimdata.add(val); \\\n\t\t\tblocksize++; \\\n\t\t}\n\tloopv(joints)\n\t{\n\t\tjoint &j = joints[i];\n\t\tloopk(3) if(j.flags & (0x01<<k))\n\t\t{\n\t\t\tfor(int l = i; l < frames.length(); l += poses.length())\n\t\t\t{\n\t\t\t\ttransform &f = frames[l];\n\t\t\t\tushort val = QUANTIZE(f.pos[k], j.offset[k], j.scale[k]);\n\t\t\t\tFLUSHVAL(val);\n\t\t\t}\n\t\t}\n\t\tloopk(4) if(j.flags & (0x08<<k))\n\t\t{\n\t\t\tfor(int l = i; l < frames.length(); l += poses.length())\n\t\t\t{\n\t\t\t\ttransform &f = frames[l];\n\t\t\t\tushort val = QUANTIZE(f.orient[k], j.offset[3+k], j.scale[3+k]);\n\t\t\t\tFLUSHVAL(val);\n\t\t\t}\n\t\t}\n\t\tloopk(3) if(j.flags & (0x80<<k))\n\t\t{\n\t\t\tfor(int l = i; l < frames.length(); l += poses.length())\n\t\t\t{\n\t\t\t\ttransform &f = frames[l];\n\t\t\t\tushort val = QUANTIZE(f.scale[k], j.offset[7+k], j.scale[7+k]);\n\t\t\t\tFLUSHVAL(val);\n\t\t\t}\n\t\t}\n\t}\n\tprintf(\"%d frames of size %d/%d compressed from %d/%d to %d in %d blocks\", frames.length()/poses.length(), framesize, poses.length()*9, framesize*frames.length()/poses.length(), poses.length()*9*frames.length()/poses.length(), animdata.length(), blocks);\n#else\n\ttransform *tr = new transform[poses.length()];\n\tchar *def = new char[poses.length()];\n\tloopvk(poses) {def[k]=0;}\n\tloopv(frames)\n\t{\n\t\tframe &fr = frames[i];\n\t\tloopvk(poses) {tr[k] = transform(Vec3(0,0,0),Quat(0,0,0,1)); def[k]|=1;}\n\t\tloopvk(fr.pose) if (fr.pose[k].remap>=0) {tr[fr.pose[k].remap] = fr.pose[k].tr; def[fr.pose[k].remap] &= ~1;}\n\t\tloopvk(poses)\n\t\t{\n\t\t\tif (def[k] == 1)\n\t\t\t{\t//if this bone didn't have any data and is still in an identity pose, warn about it.\n\t\t\t\tdef[k] |= 2;\n\t\t\t\tconst char *a = \"UNKNOWN\";\n\t\t\t\tloopvj(anims)\n\t\t\t\t{\n\t\t\t\t\tif ((uint)i >= anims[j].firstframe && (uint)i < anims[j].firstframe+anims[j].numframes)\n\t\t\t\t\t{\n\t\t\t\t\t\ta = &stringdata[anims[j].name];\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tconoutf(\"warning: bone %s defaulted (missing in %s)\", poses[k].name, a);\n\t\t\t}\n\t\t\tpose &j = poses[k];\n\t\t\ttransform &f = tr[k];\n\t\t\tloopk(3) if(j.flags & (0x01<<k)) animdata.add(QUANTIZE(f.pos[k], j.offset[k], j.scale[k]));\n\t\t\tloopk(4) if(j.flags & (0x08<<k)) animdata.add(QUANTIZE(f.orient[k], j.offset[3+k], j.scale[3+k]));\n\t\t\tloopk(3) if(j.flags & (0x80<<k)) animdata.add(QUANTIZE(f.scale[k], j.offset[7+k], j.scale[7+k]));\n\t\t}\n\t}\n\tdelete[] tr;\n#endif\n\n\t//combine the arrays into a single vertex data lump.\n\tloopv(varrays)\n\t{\n\t\tvertexarray &va = varrays[i];\n\t\tva.offset = vdata.length();\n\n\t\t//align it, if needed\n\t\tuint align = max(va.formatsize(), 8);\n\t\tif(va.offset%align) { uint pad = align - va.offset%align; va.offset += pad; loopi(pad) vdata.add(0); }\n\n\t\t//splurge the data out.\n\t\tuchar *final = vdata.reserve(va.bytesize() * va.count);\n\t\tuchar *src = &va.vdata[0];\n\t\tif (va.type == IQM_BLENDINDEXES && (va.format == IQM_UBYTE || va.format == IQM_BYTE))\n\t\t\tloopk(va.size*va.count) final[k] = jointremapinv[src[k]];\n\t\telse if (va.type == IQM_BLENDINDEXES && (va.format == IQM_USHORT || va.format == IQM_SHORT))\n\t\t\tloopk(va.size*va.count) ((ushort*)final)[k] = jointremapinv[((ushort*)src)[k]];\n\t\telse if (va.type == IQM_BLENDINDEXES && (va.format == IQM_UINT || va.format == IQM_INT))\n\t\t\tloopk(va.size*va.count) ((uint*)final)[k] = jointremapinv[((uint*)src)[k]];\n\t\telse\n\t\t\tmemcpy(final, &va.vdata[0], va.bytesize() * va.count);\n\t\tvdata.advance(va.bytesize() * va.count);\n\n\t\tva.vdata.setsize(0);\t//no longer needed.\n\t}\n\n\twhile(vdata.length()%4) vdata.add(0);\n\twhile(stringdata.length()%4) stringdata.add('\\0');\n\twhile(commentdata.length()%4) commentdata.add('\\0');\n\twhile(animdata.length()%2) animdata.add(0);\n\n\tdelete[] jointremap;\n\tdelete[] jointremapinv;\n\n\tif (joints.length()) loopv(frames) makebounds(bounds.add(), mjoints.getbuf(), frames[i]);\n}\n\nbool writeiqm(const char *filename)\n{\n\tvector<iqmextension> extensions;\n\n\tfilestream *f = (filestream *) openfile(filename, \"wb\");\n\tif(!f) return false;\n\n\tiqmheader hdr;\n\tmemset(&hdr, 0, sizeof(hdr));\n\tcopystring(hdr.magic, IQM_MAGIC, sizeof(hdr.magic)); \n\thdr.version = IQM_VERSION;\n\thdr.filesize = sizeof(hdr);\n\thdr.flags = modelflags;\n\n\tiqmextension *ext_meshes_fte = NULL;\n\tif (meshes_fte.length())\n\t{\n\t\text_meshes_fte = &extensions.add();\n\t\text_meshes_fte->name = sharestring(\"FTE_MESH\");\n\t}\n\tiqmextension *ext_events_fte = NULL;\n\tif (events_fte.length())\n\t{\n\t\text_events_fte = &extensions.add();\n\t\text_events_fte->name = sharestring(\"FTE_EVENT\");\n\n\t\tloopv(events_fte)\n\t\t{\n\t\t\tevent_fte &ev = events_fte[i];\n\t\t\tev.evdata_idx = sharestring(ev.evdata_str);\n\t\t}\n\t}\n\n\tiqmextension *ext_skins_fte = NULL;\n\tif (meshskins.length())\n\t{\n\t\text_skins_fte = &extensions.add();\n\t\text_skins_fte->name = sharestring(\"FTE_SKINS\");\n\t\text_skins_fte->num_data = sizeof(iqmext_fte_skin);\n\t\text_skins_fte->num_data += meshes.length()*sizeof(uint);\n\t\text_skins_fte->num_data += skinframes.length()*sizeof(iqmext_fte_skin_skinframe);\n\t\text_skins_fte->num_data += meshskins.length()*sizeof(iqmext_fte_skin_meshskin);\n\t}\n\tif(stringdata.length()) hdr.ofs_text = pad_field_ofs(hdr.filesize), hdr.num_text = stringdata.length(), hdr.filesize = hdr.ofs_text + hdr.num_text;\n\thdr.num_meshes = meshes.length(); if(meshes.length()) hdr.ofs_meshes = pad_field_ofs(hdr.filesize), hdr.filesize = hdr.ofs_meshes + meshes.length() * sizeof(iqmmesh);\n\thdr.num_vertexarrays = varrays.length(); if(varrays.length()) hdr.ofs_vertexarrays = pad_field_ofs(hdr.filesize), hdr.filesize = hdr.ofs_vertexarrays + varrays.length() * sizeof(iqmvertexarray);\n\tuint valign = (8 - (hdr.filesize%8))%8;\n\tuint voffset = hdr.filesize + valign;\n\thdr.filesize += valign + vdata.length();\n\thdr.num_vertexes = numfverts;\n\thdr.num_triangles = triangles.length(); if(triangles.length()) hdr.ofs_triangles = pad_field_ofs(hdr.filesize), hdr.filesize = hdr.ofs_triangles + triangles.length() * sizeof(iqmtriangle);\n\tif(neighbors.length()) hdr.ofs_adjacency = pad_field_ofs(hdr.filesize), hdr.filesize = hdr.ofs_adjacency + neighbors.length() * sizeof(iqmtriangle);\n\thdr.num_joints = joints.length(); if(joints.length()) hdr.ofs_joints = pad_field_ofs(hdr.filesize), hdr.filesize = hdr.ofs_joints + joints.length() * sizeof(iqmjoint);\n\thdr.num_poses = poses.length(); if(poses.length()) hdr.ofs_poses = pad_field_ofs(hdr.filesize), hdr.filesize = hdr.ofs_poses + poses.length() * sizeof(iqmpose);\n\thdr.num_anims = anims.length(); if(anims.length()) hdr.ofs_anims = pad_field_ofs(hdr.filesize), hdr.filesize = hdr.ofs_anims + anims.length() * sizeof(iqmanim);\n\thdr.num_frames = frames.length(); hdr.num_framechannels = framesize; \n\tif(animdata.length()) hdr.ofs_frames = pad_field_ofs(hdr.filesize), hdr.filesize = hdr.ofs_frames + animdata.length() * sizeof(ushort);\n\tif(bounds.length()) hdr.ofs_bounds = pad_field_ofs(hdr.filesize), hdr.filesize = hdr.ofs_bounds + bounds.length() * sizeof(float[8]);\n\tif(commentdata.length()) hdr.ofs_comment = pad_field_ofs(hdr.filesize), hdr.num_comment = commentdata.length(), hdr.filesize = hdr.ofs_comment + hdr.num_comment;\n\tif (extensions.length()) hdr.ofs_extensions = pad_field_ofs(hdr.filesize), hdr.num_extensions = extensions.length(), hdr.filesize = hdr.ofs_extensions + sizeof(iqmextension) * hdr.num_extensions;\n\tif (ext_meshes_fte) ext_meshes_fte->ofs_data = pad_field_ofs(hdr.filesize), ext_meshes_fte->num_data = meshes_fte.length()*sizeof(iqmext_fte_mesh), hdr.filesize = ext_meshes_fte->ofs_data + ext_meshes_fte->num_data;\n\tif (ext_events_fte) ext_events_fte->ofs_data = pad_field_ofs(hdr.filesize), ext_events_fte->num_data = events_fte.length()*sizeof(iqmext_fte_events), hdr.filesize = ext_events_fte->ofs_data + ext_events_fte->num_data;\n\tif (ext_skins_fte) ext_skins_fte->ofs_data = pad_field_ofs(hdr.filesize), hdr.filesize = ext_skins_fte->ofs_data + ext_skins_fte->num_data;\n\n\tlilswap(&hdr.version, (sizeof(hdr) - sizeof(hdr.magic))/sizeof(uint));\n\n\tf->write(&hdr, sizeof(hdr));\n\n\t// Move file write position to location specified by ofs_text\n\tfor(int i = f->tell(); i < hdr.ofs_text; i++) {\n\t\tf->putchar(0);\n\t}\n\tif(stringdata.length()) f->write(stringdata.getbuf(), stringdata.length());\n\n\t// Move file write position to location specified by ofs_meshes\n\tfor(int i = f->tell(); i < hdr.ofs_meshes; i++) {\n\t\tf->putchar(0);\n\t}\n\tloopv(meshes)\n\t{\n\t\tmesh &m = meshes[i];\n\t\tf->putlil(m.name);\n\t\tf->putlil(m.material);\n\t\tf->putlil(m.firstvert);\n\t\tf->putlil(m.numverts);\n\t\tf->putlil(m.firsttri);\n\t\tf->putlil(m.numtris);\n\t}\n\n\t// Move file write position to location specified by ofs_vertexarrays\n\tfor(int i = f->tell(); i < hdr.ofs_vertexarrays; i++) {\n\t\tf->putchar(0);\n\t}\n\tloopv(varrays)\n\t{\n\t\tvertexarray &v = varrays[i];\n\t\tf->putlil(v.type); \n\t\tf->putlil(v.flags);\n\t\tf->putlil(v.format);\n\t\tf->putlil(v.size);\n\t\tf->putlil(voffset + v.offset);\n\t}\n\n\tloopi(valign) f->putchar(0);\n\tf->write(vdata.getbuf(), vdata.length());\n\n\t// Move file write position to location specified by ofs_triangles\n\tfor(int i = f->tell(); i < hdr.ofs_triangles; i++) {\n\t\tf->putchar(0);\n\t}\n\tloopv(triangles)\n\t{\n\t\ttriangle &t = triangles[i];\n\t\tloopk(3) f->putlil(t.vert[k]);\n\t}\n\n\t// Move file write position to location specified by ofs_adjacency\n\tfor(int i = f->tell(); i < hdr.ofs_adjacency; i++) {\n\t\tf->putchar(0);\n\t}\n\tloopv(neighbors)\n\t{\n\t\ttriangle &t = neighbors[i];\n\t\tloopk(3) f->putlil(t.vert[k]);\n\t}\n\n\t// Move file write position to location specified by ofs_joints\n\tfor(int i = f->tell(); i < hdr.ofs_joints; i++) {\n\t\tf->putchar(0);\n\t}\n\tloopv(joints)\n\t{\n\t\tjoint &j = joints[i];\n\t\tf->putlil(j.name);\n\t\tf->putlil(j.parent);\n\t\tloopk(3) f->putlil(float(j.pos[k]));\n\t\tloopk(4) f->putlil(float(j.orient[k]));\n\t\tloopk(3) f->putlil(float(j.scale[k]));\n\t}\n\n\t// Move file write position to location specified by ofs_poses\n\tfor(int i = f->tell(); i < hdr.ofs_poses; i++) {\n\t\tf->putchar(0);\n\t}\n\tloopv(poses)\n\t{\n\t\tpose &p = poses[i];\n\t\tf->putlil(p.parent);\n\t\tf->putlil(p.flags);\n\t\tloopk(10) f->putlil(p.offset[k]);\n\t\tloopk(10) f->putlil(p.scale[k]);\n\t}\n\n\t// Move file write position to location specified by ofs_anims\n\tfor(int i = f->tell(); i < hdr.ofs_anims; i++) {\n\t\tf->putchar(0);\n\t}\n\tloopv(anims)\n\t{\n\t\tanim &a = anims[i];\n\t\tf->putlil(a.name);\n\t\tf->putlil(a.firstframe);\n\t\tf->putlil(a.numframes);\n\t\tf->putlil(a.fps);\n\t\tf->putlil(a.flags);\n\t}\n\n\t// Move file write position to location specified by ofs_frames\n\tfor(int i = f->tell(); i < hdr.ofs_frames; i++) {\n\t\tf->putchar(0);\n\t}\n\tloopv(animdata) f->putlil(animdata[i]);\n\n\t// Move file write position to location specified by ofs_bounds\n\tfor(int i = f->tell(); i < hdr.ofs_bounds; i++) {\n\t\tf->putchar(0);\n\t}\n\tloopv(bounds)\n\t{\n\t\tframebounds &b = bounds[i];\n\t\tloopk(3) f->putlil(float(b.bbmin[k]));\n\t\tloopk(3) f->putlil(float(b.bbmax[k]));\n\t\tf->putlil(float(b.xyradius));\n\t\tf->putlil(float(b.radius));\n\t}\n\n\tif(commentdata.length())\n\t{\n\t\t// Move file write position to location specified by ofs_comment\n\t\tfor(int i = f->tell(); i < hdr.ofs_comment; i++) {\n\t\t\tf->putchar(0);\n\t\t}\n\t\tf->write(commentdata.getbuf(), commentdata.length());\n\t} \n\n\t// Move file write position to location specified by ofs_extensions\n\tfor(int i = f->tell(); i < hdr.ofs_extensions; i++) {\n\t\tf->putchar(0);\n\t}\n\tloopv (extensions)\n\t{\n\t\tiqmextension &ext = extensions[i];\n\t\tf->putlil(ext.name);\n\t\tf->putlil(ext.num_data);\n\t\tf->putlil(ext.ofs_data);\n\t\tif (i == extensions.length()-1)\n\t\t\tf->putlil(0);\n\t\telse\n\t\t\tf->putlil((uint)(hdr.ofs_extensions + (i+1)*sizeof(ext)));\n\t}\n\n\tif (ext_meshes_fte) \n\t{\n\t\t// Move file write position to location specified by ext_meshes_fte->ofs_data\n\t\tfor(int i = f->tell(); i < ext_meshes_fte->ofs_data; i++) {\n\t\t\tf->putchar(0);\n\t\t}\n\t\tloopv(meshes_fte)\n\t\t{\n\t\t\tmeshprop &mf = meshes_fte[i];\n\t\t\tf->putlil(mf.contents);\n\t\t\tf->putlil(mf.surfaceflags);\n\t\t\tf->putlil(mf.body);\n\t\t\tf->putlil(mf.geomset);\n\t\t\tf->putlil(mf.geomid);\n\t\t\tf->putlil(mf.mindist);\n\t\t\tf->putlil(mf.maxdist);\n\t\t}\n\t}\n\n\tif (ext_events_fte) \n\t{\n\t\t// Move file write position to location specified by ext_events_fte->ofs_data\n\t\tfor(int i = f->tell(); i < ext_events_fte->ofs_data; i++) {\n\t\t\tf->putchar(0);\n\t\t}\n\t\tloopv(events_fte)\n\t\t{\n\t\t\tevent_fte &ev = events_fte[i];\n\t\t\tf->putlil(ev.anim);\n\t\t\tf->putlil(ev.timestamp);\n\t\t\tf->putlil(ev.evcode);\n\t\t\tf->putlil(ev.evdata_idx);\n\t\t}\n\t}\n\n\tif (ext_skins_fte)\n\t{\n\t\t// Move file write position to location specified by ext_skins_fte->ofs_data\n\t\tfor(int i = f->tell(); i < ext_skins_fte->ofs_data; i++) {\n\t\t\tf->putchar(0);\n\t\t}\n\t\tf->putlil(skinframes.length());\n\t\tf->putlil(meshskins.length());\n\t\tloopv(meshes) f->putlil(meshes[i].numskins);\n\t\tloopv(skinframes)\n\t\t{\n\t\t\tf->putlil(skinframes[i].material_idx);\n\t\t\tf->putlil(skinframes[i].shadertext_idx);\n\t\t}\n\t\tloopv(meshskins)\n\t\t{\n\t\t\tf->putlil(meshskins[i].firstframe);\n\t\t\tf->putlil(meshskins[i].countframes);\n\t\t\tf->putlil(meshskins[i].interval);\n\t\t}\n\t}\n\n\tdelete f;\n\treturn true;\n}\n\n#ifdef IQMTOOL_MDLEXPORT\nstatic uchar qmdl_bestnorm(Vec3 &v)\n{\n\t#define NUMVERTEXNORMALS\t162\n\tstatic\tfloat\tr_avertexnormals[NUMVERTEXNORMALS][3] = {\n\t#include \"anorms.h\"\n\t};\n\tuchar best = 0;\n\tfloat bestdot = -FLT_MAX, dot;\n\tfor (size_t i = 0; i < countof(r_avertexnormals); i++)\n\t{\n\t\tdot = DotProduct(v, r_avertexnormals[i]);\n\t\tif (dot > bestdot)\n\t\t{\n\t\t\tbestdot = dot;\n\t\t\tbest = i;\n\t\t}\n\t}\n\treturn best;\n}\nstruct qmdl_vertex_t\n{\n\tunsigned char\tv[3];\n\tunsigned char\tnormalIndex;\n};\nstatic bool writemdl(const char *filename, bool md16)\n{\n\tif (meshes.length() != 1)\n\t{\n\t\tconoutf(\"warning: mdl output requires exactly one mesh\");\n\t\tif (meshes.length() < 0)\n\t\t\treturn false;\t//must have ONE mesh only.\n\t\telse\n\t\t\tconoutf(\"using first...\");\n\t}\n\tauto mesh = meshes[0];\t//should probably favour the mesh with the most verts or something.\n\tvertexarray *texcoords = NULL;\n\tvertexarray *vertcoords = NULL;\n\tvertexarray *vertnorm = NULL;\n\tvertexarray *vertbones = NULL;\n\tvertexarray *vertweights = NULL;\n\tuint skinwidth = 0;\n\tuint skinheight = 0;\n\tVec3 offset={0,0,0};\n\tVec3 scale={1,1,1};\n\tuint numskins = 0;\n\tvector<uchar> skindata;\n\tunsigned char *paletteddata;\n\n\tloopv(varrays)\n\t{\n\t\tif(varrays[i].type == IQM_TEXCOORD && varrays[i].format == IQM_FLOAT && varrays[i].size == 2)\n\t\t\ttexcoords = &varrays[i];\n\t\tif(varrays[i].type == IQM_POSITION && varrays[i].format == IQM_FLOAT && varrays[i].size == 3)\n\t\t\tvertcoords = &varrays[i];\n\t\tif(varrays[i].type == IQM_NORMAL && varrays[i].format == IQM_FLOAT && varrays[i].size == 3)\n\t\t\tvertnorm = &varrays[i];\n\t\tif(varrays[i].type == IQM_BLENDINDEXES && varrays[i].format == IQM_UBYTE && varrays[i].size == 4)\n\t\t\tvertbones = &varrays[i];\n\t\tif(varrays[i].type == IQM_BLENDWEIGHTS && varrays[i].format == IQM_UBYTE && varrays[i].size == 4)\n\t\t\tvertweights = &varrays[i];\n\t}\n\tif (!texcoords)\n\t{\n\t\tconoutf(\"warning: mdl output requires a float texcoord array\");\n\t\treturn false;\t//must have some vertex coords...\n\t}\n\tif (!vertcoords)\n\t{\n\t\tconoutf(\"warning: mdl output requires a suitable vertex positions array...\");\n\t\treturn false;\t//must have some vertex coords...\n\t}\n\tif (!vertnorm)\n\t{\n\t\tconoutf(\"warning: mdl output requires a suitable vertex normals array...\");\n\t\treturn false;\t//must have some vertex coords...\n\t}\n\tfloat *tcdata = (float*)texcoords->vdata.getbuf();\n\n\t//the actual mdl limit is really annoying to calculate.\n\tif (mesh.numverts >= 1024)\n\t\tconoutf(\"Writing mdl %s with %u verts exceeds regular limit of %u\", filename, mesh.numverts, 1024);\n\n\t//read the skin...\n\tsize_t filesize=0;\n\tqbyte *filedata = NULL;\n\tauto s = openfile(&stringdata[mesh.material], \"rb\");\n\tif (s)\n\t{\n\t\tfilesize = s->size();\n\t\tfiledata = (qbyte*)malloc(filesize);\n\t\ts->read(filedata, filesize);\n\t\tdelete s;\n\t}\n\t//decode it...\n\tfte::ImgTool_SetupPalette();\n\tstruct pendingtextureinfo *tex = NULL;\n\tif (filedata)\n\t\ttex = Image_LoadMipsFromMemory(IF_NOMIPMAP, &stringdata[mesh.material], &stringdata[mesh.material], filedata, filesize);\n\telse\n\t\tconoutf(\"could not open file %s\", &stringdata[mesh.material]);\n\tif (tex)\n\t{\t//okay, we have a valid image!\n#if 1\n\t\t//downsize it to work around glquake's limitations. square textures will generally end up 256*256 instead of 512*512 due to that stupid 480 height limit\n\t\tint newwidth = tex->mip[0].width;\n\t\tint newheight = tex->mip[0].height;\n\t\tauto npotup = [](unsigned val)\n\t\t{\t//convert to npot, rounding up.\n\t\t\tunsigned scaled = 1;\n\t\t\twhile(scaled < val)\n\t\t\t\tscaled<<=1;\n\t\t\treturn scaled;\n\t\t};\n\t\twhile (newwidth*newheight > 640*480/*GL_Upload8 limit*/ || npotup(newwidth)*npotup(newheight) > 1024*512/*GL_Upload32 limit, may be higher thanks to gl_max_size or gl_picmip but really that sucks*/ || newheight > 480/*Mod_LoadAliasModel limit -- weird MAX_LBM_HEIGHT check*/)\n\t\t{\n\t\t\tnewwidth >>= 1;\n\t\t\tnewheight >>= 1;\n\t\t}\n\t\tauto resized = (tex->mip[0].width == newwidth&&tex->mip[0].height == newheight)?NULL:Image_ResampleTexture(tex->encoding, tex->mip[0].data, tex->mip[0].width, tex->mip[0].height, NULL, newwidth, newheight);\n\t\tif (resized)\n\t\t{\n\t\t\ttex->mip[0].data = resized;\n\t\t\ttex->mip[0].datasize = 0; /*o.O*/\n\t\t\ttex->mip[0].width = newwidth;\n\t\t\ttex->mip[0].height = newheight;\n\t\t}\n#endif\n\n\t\t//palettize it, to match the q1 palette.\n\t\tqboolean allowedformats[PTI_MAX] = {};\n\t\tallowedformats[PTI_P8]=qtrue;\n\t\t//FIXME: add hexen2's alpha stuff?\n\t\tconoutf(\"Palettizing \\\"%s\\\" (%u*%u)\", &stringdata[mesh.material], tex->mip[0].width, tex->mip[0].height);\n\t\tImage_ChangeFormat(tex, allowedformats, PTI_INVALID, \"foo\");\n\n\t\tskinwidth = tex->mip[0].width;\n\t\tskinheight = tex->mip[0].height;\n\t}\n\telse\n\t{\t//texture coords are ints. if we don't have a large enough texture then we don't have much texture coord precision either, so use something reasonable.\n\t\tskinwidth = 128;\n\t\tskinheight = 128;\n\t}\n\tpaletteddata = skindata.reserve(skinwidth*skinheight);\n\tif (tex)\n\t{\n\t\tmemcpy(paletteddata, tex->mip[0].data, skinwidth*skinheight);\n\t\t*paletteddata^=1;\t//try to work around glquake's flood fill...\n\t}\n\telse\n\t{\t//fill with some sort of grey.\n\t\tmemset(paletteddata, 8, skinwidth*skinheight);\n\t\tpaletteddata[0] = 7;\t//work around flood filling...\n\t}\n\tskindata.advance(skinwidth*skinheight);\n\tnumskins++;\n\n\t//we're going to need the transformed pose data, without any bone weights getting in the way.\n\tvector<Vec3> vpos, vnorm;\n\tVec3 min={FLT_MAX,FLT_MAX,FLT_MAX}, max={-FLT_MAX,-FLT_MAX,-FLT_MAX};\n\tif (!anims.length())\n\t{\n\t\tVec3 *outv = vpos.reserve(mesh.numverts);\n\t\tVec3 *outn = vnorm.reserve(mesh.numverts);\n\n\t\tauto invert = (float*)vertcoords->vdata.getbuf();\n\t\tauto innorm = (float*)vertnorm->vdata.getbuf();\n//\t\tauto inbones = (uchar*)vertbones->vdata.getbuf();\n//\t\tauto inweights = (uchar*)vertweights->vdata.getbuf();\n\n\t\t//FIXME: generate bone matricies from base pose? or just use the vertex data as-is...\n\t\tfor (uint i = mesh.firstvert; i < mesh.firstvert+mesh.numverts; i++, outv++, outn++)\n\t\t{\n\t\t\t//FIXME: generate vert's matrix\n\n\t\t\t//transform each vert\n\t\t\t*outv = Vec3(invert[i*3+0], invert[i*3+1], invert[i*3+2]);\n\n\t\t\t//bound it to find the model's extents\n\t\t\tfor (uint c = 0; c < 3; c++)\n\t\t\t{\n\t\t\t\tif (min.v[c] > outv->v[c])\n\t\t\t\t\tmin.v[c] = outv->v[c];\n\t\t\t\tif (max.v[c] < outv->v[c])\n\t\t\t\t\tmax.v[c] = outv->v[c];\n\t\t\t}\n\n\t\t\t*outn = Vec3(innorm[i*3+0], innorm[i*3+1], innorm[i*3+2]);\n\t\t}\n\t\tvpos.advance(mesh.numverts);\n\t\tvnorm.advance(mesh.numverts);\n\t}\n\telse loopv(anims)\n\t{\n\t\tanim &a = anims[i];\n\t\tVec3 *outv = vpos.reserve(mesh.numverts*a.numframes);\n\t\tVec3 *outn = vnorm.reserve(mesh.numverts*a.numframes);\n\n//\t\tMatrix3x4 bonepose[joints.length()];\n\t\tvector<Matrix3x4> bonepose;\n\t\tbonepose.reserve(joints.length());\n\n\t\tauto invert = (float*)vertcoords->vdata.getbuf();\n\t\tauto innorm = (float*)vertnorm->vdata.getbuf();\n\t\tauto inbones = vertbones?(uchar*)vertbones->vdata.getbuf():NULL;\n\t\tauto inweights = vertweights?(uchar*)vertweights->vdata.getbuf():NULL;\n\n\t\tif (!inbones || !inweights)\n\t\t\tprintf(\"no bone indexes\\n\");\n\n\t\tfor (uint j = 0; j < a.numframes; j++)\n\t\t{\n\t\t\t//build absolute poses.\n\t\t\tframe &fr = frames[a.firstframe+j];\n\t\t\tfor (int b = 0; b < fr.pose.length(); b++)\n\t\t\t{\n\t\t\t\tauto &frpose = fr.pose[b];\n\t\t\t\tbonepose[b] = Matrix3x4(Quat(frpose.tr.orient), Vec3(frpose.tr.pos), Vec3(frpose.tr.scale));\n\t\t\t\tif (frpose.boneparent >= 0)\n\t\t\t\t\tbonepose[b]\t= bonepose[frpose.boneparent] * bonepose[b];\n\t\t\t}\n\t\t\t//done with parents... now we want to invert them\n\t\t\tfor (int b = 0; b < fr.pose.length(); b++)\n\t\t\t{\n\t\t\t\tMatrix3x4 invbind = Matrix3x4(mjoints[b]);\n\t\t\t\t//invbind.invert();\n\t\t\t\tbonepose[b] = bonepose[b] * invbind;\n\t\t\t}\n\t\t\tfor (uint i = mesh.firstvert; i < mesh.numverts; i++, outv++, outn++)\n\t\t\t{\n\t\t\t\t//generate per-vert matrix...\n\t\t\t\tMatrix3x4 blend;\n\t\t\t\tblend *= 0;\n\t\t\t\tif (inweights && inbones)\n\t\t\t\t{\n\t\t\t\t\tif (inweights[i*4+0]) blend += bonepose[inbones[i*4+0]] * (inweights[i*4+0]/255.0);\n\t\t\t\t\tif (inweights[i*4+1]) blend += bonepose[inbones[i*4+1]] * (inweights[i*4+1]/255.0);\n\t\t\t\t\tif (inweights[i*4+2]) blend += bonepose[inbones[i*4+2]] * (inweights[i*4+2]/255.0);\n\t\t\t\t\tif (inweights[i*4+3]) blend += bonepose[inbones[i*4+3]] * (inweights[i*4+3]/255.0);\n\t\t\t\t}\n\n\t\t\t\t//transform each vert\n\t\t\t\t*outv = blend.transform(Vec3(invert[i*3+0], invert[i*3+1], invert[i*3+2]));\n\n\t\t\t\t//bound it to find the model's extents\n\t\t\t\tfor (uint c = 0; c < 3; c++)\n\t\t\t\t{\n\t\t\t\t\tif (min.v[c] > outv->v[c])\n\t\t\t\t\t\tmin.v[c] = outv->v[c];\n\t\t\t\t\tif (max.v[c] < outv->v[c])\n\t\t\t\t\t\tmax.v[c] = outv->v[c];\n\t\t\t\t}\n\n\t\t\t\t*outn = blend.transform3(Vec3(innorm[i*3+0], innorm[i*3+1], innorm[i*3+2]));\n\t\t\t}\n\t\t\tvpos.advance(mesh.numverts);\n\t\t\tvnorm.advance(mesh.numverts);\n\t\t}\n\t}\n\n\toffset = min;\n\tscale = (max-min)/255; //ignore low order info here\n\n\tstream *f = openfile(filename, \"wb\");\n\tif(!f) return false;\n\n\tif (md16)\n\t\tf->putlil((uint)(('M'<<0)|('D'<<8)|('1'<<16)|('6'<<24)));\n\telse\n\t\tf->putlil((uint)(('I'<<0)|('D'<<8)|('P'<<16)|('O'<<24)));\n\tf->putlil((uint)6);\t//version\n\tf->putlil((float)scale[0]);\n\tf->putlil((float)scale[1]);\n\tf->putlil((float)scale[2]);\n\tf->putlil((float)offset[0]);\n\tf->putlil((float)offset[1]);\n\tf->putlil((float)offset[2]);\n\tf->putlil(0.f);\t//radius\n\tf->putlil(0.f);\t//eyeposx, never used afaik\n\tf->putlil(0.f);\t//eyeposy\n\tf->putlil(0.f);\t//eyeposz\n\n\tf->putlil((uint)numskins);\n\tf->putlil((uint)skinwidth);\n\tf->putlil((uint)skinheight);\n\n\tf->putlil((uint)mesh.numverts);\n\tf->putlil((uint)mesh.numtris);\n\tif (!anims.length())\n\t\tf->putlil((uint)1);\t\t\t\t\t//numanims\n\telse\n\t\tf->putlil((uint)anims.length());\t//numanims\n\n\tf->putlil((uint)0);\t//synctype\n\tf->putlil((uint)modelflags);\t//flags\n\tf->putlil(0.f);\t//size\n\n\t//skins\n\tfor (uint i = 0; i < numskins; i++)\n\t{\n\t\tf->putlil((uint)0);\t//ALIAS_SKIN_SINGLE\n\t\tf->write(skindata.getbuf()+i*skinwidth*skinheight, skinwidth*skinheight);\n\t}\n\t//texcoords\n\tfor (uint i = mesh.firstvert; i < mesh.firstvert+mesh.numverts; i++)\n\t{\n\t\tfloat s = (tcdata[i*2+0])*skinwidth;\n\t\tfloat t = (tcdata[i*2+1])*skinheight;\n\t\ts -= 0.5;\t//glquake has some annoying half-texel offset thing.\n\t\tt -= 0.5;\n\t\ts = bound(0, s, skinwidth);\n\t\tt = bound(0, t, skinheight);\n\t\tf->putlil((uint)(0?32:0));\t//onseam. no verts are ever onseam for us, as we don't do that nonsense here.\n\t\tf->putlil((int)s);\t//mdl texcoords are ints, in texels. which sucks, but what can you do...\n\t\tf->putlil((int)t);\n\t}\n\t//tris\n\tfor (uint i = mesh.firsttri; i < mesh.firsttri+mesh.numtris; i++)\n\t{\n\t\tf->putlil((uint)1);\t//faces front. All are effectively front-facing for us. This avoids annoying tc additions.\n\t\tf->putlil((uint)triangles[i].vert[0]);\n\t\tf->putlil((uint)triangles[i].vert[1]);\n\t\tf->putlil((uint)triangles[i].vert[2]);\n\t}\n\t//animations\n\tvector<qmdl_vertex_t> high, low;\n\tsize_t voffset = 0;\n\n\tif (!anims.length())\n\t{\n\t\tqmdl_vertex_t *th=high.reserve(mesh.numverts),*tl=low.reserve(mesh.numverts);\n\t\tfor (uint i = mesh.firstvert; i < mesh.numverts; i++, th++, tl++)\n\t\t{\n\t\t\tint l;\n\t\t\tfor (uint c = 0; c < 3; c++)\n\t\t\t{\n\t\t\t\tl = (((vpos[voffset][c]-offset[c])*256) / scale[c]);\n\t\t\t\tif (l<0)\t\tl = 0;\n\t\t\t\tif (l > 0xff00)\tl = 0xff00;\t//0xffff would exceed the bounds values, so don't use it.\n\t\t\t\tth->v[c] = l>>8;\n\t\t\t\ttl->v[c] = l&0xff;\n\t\t\t}\n\t\t\ttl->normalIndex = th->normalIndex = qmdl_bestnorm(vnorm[voffset]);\n\n\t\t\tvoffset++;\n\t\t}\n\t\thigh.advance(mesh.numverts);\n\t\tlow.advance(mesh.numverts);\n\n\t\tvoffset = 0;\n\t\tf->putlil((uint)0);\t//single-pose type\n\n\t\tchar name[16]=\"base\";\n\t\tqmdl_vertex_t min={{255,255,255}}, max={{0,0,0}};\n\t\tfor (uint k = 0; k < mesh.numverts; k++)\n\t\t{\n\t\t\tfor (uint c = 0; c < 3; c++)\n\t\t\t{\n\t\t\t\tif (min.v[c] > high[voffset+k].v[c])\n\t\t\t\t\tmin.v[c] = high[voffset+k].v[c];\n\t\t\t\tif (max.v[c] < high[voffset+k].v[c])\n\t\t\t\t\tmax.v[c] = high[voffset+k].v[c];\n\t\t\t}\n\t\t}\n\t\tf->put(min);\n\t\tf->put(max);\n\n\t\tname[countof(name)-1] = 0;\n\t\tfor (uint k = 0; k < countof(name); k++)\n\t\t\tf->put(name[k]);\n\n\t\tf->write(&high[voffset], sizeof(qmdl_vertex_t)*mesh.numverts);\n\t\tif (md16)\n\t\t\tf->write(&low[voffset], sizeof(qmdl_vertex_t)*mesh.numverts);\n\t\tvoffset += mesh.numverts;\n\t}\n\telse\n\t{\n\t\tloopv(anims)\n\t\t{\n\t\t\tanim &a = anims[i];\n\t\t\tfor (uint j = 0; j < a.numframes; j++)\n\t\t\t{\n\t\t\t\tqmdl_vertex_t *th=high.reserve(mesh.numverts),*tl=low.reserve(mesh.numverts);\n\n\t\t\t\tfor (uint i = mesh.firstvert; i < mesh.numverts; i++, th++, tl++)\n\t\t\t\t{\n\t\t\t\t\tint l;\n\t\t\t\t\tfor (uint c = 0; c < 3; c++)\n\t\t\t\t\t{\n\t\t\t\t\t\tl = (((vpos[voffset][c]-offset[c])*256) / scale[c]);\n\t\t\t\t\t\tif (l<0)\t\tl = 0;\n\t\t\t\t\t\tif (l > 0xff00)\tl = 0xff00;\t//0xffff would exceed the bounds values, so don't use it.\n\t\t\t\t\t\tth->v[c] = l>>8;\n\t\t\t\t\t\ttl->v[c] = l&0xff;\n\t\t\t\t\t}\n\t\t\t\t\ttl->normalIndex = th->normalIndex = qmdl_bestnorm(vnorm[voffset]);\n\n\t\t\t\t\tvoffset++;\n\t\t\t\t}\n\t\t\t\thigh.advance(mesh.numverts);\n\t\t\t\tlow.advance(mesh.numverts);\n\t\t\t}\n\t\t}\n\t\tvoffset = 0;\n\t\tloopv(anims)\n\t\t{\n\t\t\tanim &a = anims[i];\n\t\t\tif (a.numframes == 1)\n\t\t\t\tf->putlil((uint)0);\t//single-pose type\n\t\t\telse\n\t\t\t{\n\t\t\t\tf->putlil((uint)1);\t//anim type\n\t\t\t\tf->putlil((uint)a.numframes);\n\n\t\t\t\tqmdl_vertex_t min={{255,255,255}}, max={{0,0,0}};\n\t\t\t\tfor (uint k = 0; k < mesh.numverts*a.numframes; k++)\n\t\t\t\t{\n\t\t\t\t\tfor (uint c = 0; c < 3; c++)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (min.v[c] > high[voffset+k].v[c])\n\t\t\t\t\t\t\tmin.v[c] = high[voffset+k].v[c];\n\t\t\t\t\t\tif (max.v[c] < high[voffset+k].v[c])\n\t\t\t\t\t\t\tmax.v[c] = high[voffset+k].v[c];\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tf->put(min);\n\t\t\t\tf->put(max);\n\t\t\t\tfor (uint j = 0; j < a.numframes; j++)\n\t\t\t\t\tf->putlil(1.0f/a.fps);\t//intervals. we use the same value for each\n\t\t\t}\n\n\t\t\tfor (uint j = 0; j < a.numframes; j++)\n\t\t\t{\n\t\t\t\tchar name[16]={0};\n\t\t\t\tqmdl_vertex_t min={{255,255,255}}, max={{0,0,0}};\n\t\t\t\tfor (uint k = 0; k < mesh.numverts; k++)\n\t\t\t\t{\n\t\t\t\t\tfor (uint c = 0; c < 3; c++)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (min.v[c] > high[voffset+k].v[c])\n\t\t\t\t\t\t\tmin.v[c] = high[voffset+k].v[c];\n\t\t\t\t\t\tif (max.v[c] < high[voffset+k].v[c])\n\t\t\t\t\t\t\tmax.v[c] = high[voffset+k].v[c];\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tf->put(min);\n\t\t\t\tf->put(max);\n\n\t\t\t\tstrncpy(name, &stringdata[a.name], sizeof(name));\n\t\t\t\tname[countof(name)-1] = 0;\n\t\t\t\tfor (uint k = 0; k < countof(name); k++)\n\t\t\t\t\tf->put(name[k]);\n\n\t\t\t\tf->write(&high[voffset], sizeof(qmdl_vertex_t)*mesh.numverts);\n\t\t\t\tif (md16)\n\t\t\t\t\tf->write(&low[voffset], sizeof(qmdl_vertex_t)*mesh.numverts);\n\t\t\t\tvoffset += mesh.numverts;\n\t\t\t}\n\t\t}\n\t}\n\n\tdelete f;\n\treturn true;\n}\nstatic bool writeqmdl(const char *filename)\n{\n\treturn writemdl(filename, false);\n}\nstatic bool writemd16(const char *filename)\n{\n\treturn writemdl(filename, true);\n}\n#else\nstatic bool writeqmdl(const char *filename)\n{\n\tfatal(\"(q1)mdl output disabled at compile time\");\n\treturn false;\n}\nstatic bool writemd16(const char *filename)\n{\n\tfatal(\"md16 output disabled at compile time\");\n\treturn false;\n}\n#endif\n\nstatic bool writemd3(const char *filename)\n{\n\tfprintf(stderr, \"writemd3 is not implemented yet\\n\");\n\treturn false;\n}\n\nstatic void help(bool exitstatus, bool fullhelp)\n{\n\tfprintf(exitstatus != EXIT_SUCCESS ? stderr : stdout,\n\"-- FTE's Fork of Lee Salzman's iqm exporter --\\n\"\n\"Usage:\\n\"\n\"\\n\"\n\"./iqmtool cmdfile.cmd\\n\"\n\"./iqmtool [options] output.iqm mesh.iqe anim1.iqe ... animN.iqe\\n\"\n\"./iqmtool [options] output.iqm mesh.md5mesh anim1.md5anim ... animN.md5anim\\n\"\n\"./iqmtool [options] output.iqm mesh.smd anim1.smd ... animN.smd\\n\"\n\"./iqmtool [options] output.iqm mesh.fbx anim1.fbx ... animN.fbx\\n\"\n\"./iqmtool [options] output.iqm mesh.obj\\n\"\n\"./iqmtool [options] output.iqm source.gltf\\n\"\n\"./iqmtool [options] output.iqm --qex sources.md5\\n\"\n\"\\n\"\n\"Basic commandline options:\\n\"\n\"   --help              Show full help.\\n\"\n\"   -v                  Verbose\\n\"\n\"   -q                  Quiet operation\\n\"\n\"   -n                  Disable output of fte's iqm extensions\\n\"\n\t);\n\n\tif (fullhelp)\n\t\tfprintf(exitstatus != EXIT_SUCCESS ? stderr : stdout,\n\"\\n\"\n\"For certain formats, IQE, OBJ, and FBX, it is possible to combine multiple mesh\\n\"\n\"files of the exact same vertex layout and skeleton by supplying them as\\n\"\n\"\\\"mesh1.iqe,mesh2.iqe,mesh3.iqe\\\", that is, a comma-separated list of the mesh\\n\"\n\"files (with no spaces) in place of the usual mesh filename.\\n\"\n\"\\n\"\n\"Legacy commandline options that affect mesh import for the next file:\\n\"\n\"\\n\"\n\"   -s N                Sets the output scale to N (float).\\n\"\n\"   --meshtrans Z\\n\"\n\"   --meshtrans X,Y,Z   Translates a mesh by X,Y,Z (floats).\\n\"\n\"                       This does not affect the skeleton.\\n\"\n\"   -j\\n\"\n\"   --forcejoints       Forces the exporting of joint information in animation\\n\"\n\"                       files without meshes.\\n\"\n\"   --qex               Applies a set of fixups to work around the quirks in\\n\"\n\"                       the quake rerelease's md5 files.\\n\"\n\"\\n\"\n\"Legacy commandline options that affect the following animation file:\\n\"\n\"\\n\"\n\"    --name A\t\t\tSets the name of the animation to A.\\n\"\n\"    --fps N            Sets the FPS of the animation to N (float).\\n\"\n\"    --loop             Sets the loop flag for the animation.\\n\"\n\"    --start N          Sets the first frame of the animation to N (integer).\\n\"\n\"    --end N            Sets the last frame of the animation to N (integer).\\n\"\n\"    --zup              Source model is in quake's orientation.\\n\"\n\"\\n\"\n\"You can supply either a mesh file, animation files, or both.\\n\"\n\"Note that if an input mesh file is supplied, it must come before the animation\\n\"\n\"files in the file list.\\n\"\n\"The output IQM file will contain the supplied mesh and any supplied animations.\\n\"\n\"If no mesh is provided,the IQM file will simply contain the supplied animations\\n\"\n\"\\n\"\n\"Command script commands:\\n\"\n\"   hitbox BODYNUM BONENAME x y z x y z\\n\"\n\"                       Attaches a hitbox surface around the specified bone\\n\"\n\"                       (in base pose). Gamecode knows which part of the model\\n\"\n\"                       was hit via the body value.\\n\"\n\"   exec FILENAME       Reads additional commands from the specified file.\\n\"\n\"                       Handy for shared animations.\\n\"\n\"   modelflags FLAGS    Specifies the model's flags, mostly for trail effects.\\n\"\n\"                       Known values are rocket, grenade, gib, rotate, tracer1,\\n\"\n\"                       zomgib, tracer2, tracer3, or 0xXXXXXXXX\\n\"\n\"   mesh NAME MESHPROPS Overrides properties for a specific source surface.\\n\"\n\"   bone NAME BONEPROPS Overrides properties for a specific bone.\\n\"\n\"   ANIMPROPS           Provides default values for following imported files.\\n\"\n\"   import FILENAME [PROPS]\\n\"\n\"                       Loads the specified file, with per-file properties.\\n\"\n\"   output FILENAME     Specifies the iqm filename to write.\\n\"\n\"   output_mdl FILENAME Specifies the (q1) mdl filename to write.\\n\"\n\"\\n\"\n\"Bone Properties:\\n\"\n\"   rename NEWNAME      Renames the bone accordingly.\\n\"\n\"   group GROUPID       Inherited from parents this allows resorting bones.\\n\"\n\"\\n\"\n\"Mesh Properties:\\n\"\n\"   contents a[,b,c]    Override surface content values for collisons\\n\"\n\"                       Accepted values are empty, solid, lava, slime, water,\\n\"\n\"                       fluid, fte_ladder, playerclip, monsterclip, body,\\n\"\n\"                       corpse, q2_ladder, fte_sky,\tq3_nodrop,\\n\"\n\"                       or 0xXXXXXXXX for custom values.\\n\"\n\"   surfaceflags a[,b]  Specifies explicit surface flags values.\\n\"\n\"                       Accepted values are nodraw, or custom 0xXXXXXXXX values.\\n\"\n\"   body BODYNUM        Specifies mod-specific body numbers.\\n\"\n\"   geomset GROUP IDX   Controls which geomset this surface is part of\\n\"\n\"   lodrange min max    Specifies a distance range within which to draw this lod\\n\"\n\"\\n\"\n\"Anim Properties:\\n\"\n\"   name NAME           Defines the output's name for this animation.\\n\"\n\"   fps RATE            Controls the playback rate.\\n\"\n\"   loop                Forces the animation(s) to loop.\\n\"\n\"   clamp               Forces the animation(s) to not loop.\\n\"\n\"   unpack              Extract each pose into its own single-pose 'animation'.\\n\"\n\"   pack                Undoes the effect of 'unpack'.\\n\"\n\"   nomesh 1            Skips importing of meshes, getting animations only.\\n\"\n\"   noanim 1            Skips importing of animations, for mesh import only.\\n\"\n\"   materialprefix PRE  Prefixes material names with the specified string.\\n\"\n\"   materialsuffix EXT     Forces the material's extension as specified.\\n\"\n\"   ignoresurfname 1    Ignores source surface names.\\n\"\n\"   start FRAME         The Imported animation starts on this frame...\\n\"\n\"   end FRAME           ... and ends on this one.\\n\"\n\"   rotate X Y Z        Rotates the input model by the specified angles.\\n\"\n\"   scale FACTOR        Scales the input model by some value\\n\"\n\"   origin X Y Z        Translates the input model.\\n\"\n\"   event reset         Discard previously specified events\\n\"\n\"   event [ANIMIDX:]TIME CODE \\\"VALUE\\\"\\n\"\n\"                       Inserts a model event into the relevant animation index.\\n\"\n\t\t);\n\texit(exitstatus);\n}\n\nstruct bitnames\n{\n\tconst char *std;\n\tconst char *name;\n\tunsigned int bits;\n};\n\n//chops up the input string, returning subsections of it, like strtok_r, except with more specific separators.\nchar *mystrtok(char **ctx)\n{\n\tchar *ret = NULL;\n\tchar *p = *ctx;\n\t//skip whitespace\n\twhile (*p == ' ' || *p == '\\t' || *p == '\\r' || *p == '\\n')\n\t\tp++;\n\tif (!*p)\n\t\treturn NULL;\t//eof\n\tif (*p == '\\\"')\n\t{\n\t\tret = ++p;\n\t\twhile (*p && *p != '\\\"')\n\t\t\tp++;\n\t\tif (*p)\n\t\t\t*p++ = '\\0';\n\t\t*ctx = p;\n\t}\n\telse\n\t{\n\t\tret = p;\n\t\t//we're screwed if we reach a quote without trailing whitespace, blame the user in that case.\n\t\twhile (*p && *p != ' ' && *p != '\\t' && *p != '\\r' && *p != '\\n')\n\t\t\tp++;\n\t\tif (*p)\n\t\t\t*p++ = '\\0';\n\t\t*ctx = p;\n\t}\n\treturn ret;\n}\n\nunsigned int parsebits(bitnames *names, char **line)\n{\n\tunsigned int bits = 0;\n\tchar *comma;\n\tfor (char *value = mystrtok(line); value; value = comma)\n\t{\n\t\tcomma = strchr(value, ',');\n\t\tif (comma)\n\t\t\t*comma++ = 0;\n\t\tchar *end;\n\t\tstrtoul(value, &end, 0);\n\t\tif (end && !*end)\n\t\t\tbits |= strtoul(value, NULL, 0);\n\t\telse\n\t\t{\n\t\t\tsize_t i;\n\t\t\tchar *std = value;\n\t\t\tvalue = strchr(value, '_');\n\t\t\tif (value)\n\t\t\t\t*value++ = 0;\n\t\t\telse\n\t\t\t{\n\t\t\t\tvalue = std;\n\t\t\t\tstd += strlen(std);\n\t\t\t}\n\t\t\tfor (i = 0; names[i].name; i++)\n\t\t\t{\n\t\t\t\tif (!strcasecmp(names[i].std, std))\n\t\t\t\t{\n\t\t\t\t\tif (!strcasecmp(names[i].name, value))\n\t\t\t\t\t{\n\t\t\t\t\t\tbits |= names[i].bits;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!names[i].name)\n\t\t\t{\t//stuff with no specific standard, mostly for consistency\n\t\t\t\tfor (i = 0; names[i].name; i++)\n\t\t\t\t{\n\t\t\t\t\tif (!strcasecmp(names[i].name, value))\n\t\t\t\t\t{\n\t\t\t\t\t\tbits |= names[i].bits;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (!names[i].name)\n\t\t\t\t\tfatal(\"Unknown bit name: %s\\n\", value);\n\t\t\t}\n\t\t}\n\t}\n\treturn bits;\n}\n\nbool parsebonefield(const char *tok, char **line, boneoverride::prop &spec, bool defaults)\n{\n\tif (!strcasecmp(tok, \"rename\"))\n\t\tspec.rename = newstring(mystrtok(line));\n\telse if (!strcasecmp(tok, \"group\"))\n\t\tspec.group = atoi(mystrtok(line));\n\telse\n\t\treturn false;\n\treturn true;\n}\n\nbool parsemeshfield(const char *tok, char **line, meshprop &spec, bool defaults)\n{\n\tif (!strcasecmp(tok, \"contents\"))\n\t{\n\t\t//these should be (mostly) compatible with q2+q3\n\t\tbitnames contentnames[] = {\n\t\t\t{\"\",\t\"empty\",\t\t0x00000000},\n\t\t\t{\"\",\t\"solid\",\t\t0x00000001},\n\t\t\t{\"\",\t\"lava\",\t\t\t0x00000008},\n\t\t\t{\"\",\t\"slime\",\t\t0x00000010},\n\t\t\t{\"\",\t\"water\",\t\t0x00000020},\n\t\t\t{\"\",\t\"fluid\",\t\t0x00000038},\n\t\t\t{\"fte\",\t\"ladder\",\t\t0x00004000},\n\t\t\t{\"\",\t\"playerclip\",\t0x00010000},\n\t\t\t{\"\",\t\"monsterclip\",\t0x00010000},\n\t\t\t{\"\",\t\"body\",\t\t\t0x02000000},\n\t\t\t{\"\",\t\"corpse\",\t\t0x04000000},\n\t\t\t\t\t\t\t\t\t\t\t\t{\"q2\",\t\"ladder\",\t\t0x20000000},\n\t\t\t{\"fte\",\t\"sky\",\t\t\t0x80000000},{\"q3\",\t\"nodrop\",\t\t0x80000000},\n\t\t\t{NULL}\n\t\t};\n\t\tspec.contents = parsebits(contentnames, line);\n\t}\n\telse if (!strcasecmp(tok, \"surfaceflags\"))\n\t{\n\t\tbitnames surfaceflagnames[] = {\n\t\t\t{\"fte\",\t\"nodraw\",\t\t0x00000080},{\"q3\",\t\"nodraw\",\t\t0x00000080},\n\t\t\t{NULL}\n\t\t};\n\t\tspec.surfaceflags = parsebits(surfaceflagnames, line);\n\t}\n\telse if (!strcasecmp(tok, \"body\"))\n\t\tspec.body = strtoul(mystrtok(line), NULL, 0);\n\telse if (!strcasecmp(tok, \"geomset\"))\n\t{\n\t\tspec.geomset = strtoul(mystrtok(line), NULL, 0);\n\t\tspec.geomid = strtoul(mystrtok(line), NULL, 0);\n\t}\n\telse if (!strcasecmp(tok, \"lodrange\"))\n\t{\n\t\tspec.mindist = atof(mystrtok(line));\n\t\tspec.maxdist = atof(mystrtok(line));\n\t}\n\telse\n\t\treturn false;\n\treturn true;\n}\n\nbool parseanimfield(const char *tok, char **line, filespec &spec, bool defaults)\n{\n\tif (!strcasecmp(tok, \"name\") && !defaults)\n\t\tspec.name = newstring(mystrtok(line));\n\telse if (!strcasecmp(tok, \"fps\"))\n\t\tspec.fps = atof(mystrtok(line));\n\telse if (!strcasecmp(tok, \"loop\"))\n\t\tspec.flags |= IQM_LOOP;\n\telse if (!strcasecmp(tok, \"clamp\"))\n\t\tspec.flags &= ~IQM_LOOP;\n\telse if (!strcasecmp(tok, \"unpack\"))\n\t\tspec.flags |= IQM_UNPACK;\n\telse if (!strcasecmp(tok, \"pack\"))\n\t\tspec.flags &= ~IQM_UNPACK;\n\telse if (!strcasecmp(tok, \"nomesh\"))\n\t\tspec.nomesh = !!strtoul(mystrtok(line), NULL, 0);\n\telse if (!strcasecmp(tok, \"noanim\"))\n\t\tspec.noanim = !!strtoul(mystrtok(line), NULL, 0);\n\telse if (!strcasecmp(tok, \"materialprefix\"))\n\t\tspec.materialprefix = newstring(mystrtok(line));\n\telse if (!strcasecmp(tok, \"materialsuffix\"))\n\t\tspec.materialsuffix = newstring(mystrtok(line));\n\telse if (!strcasecmp(tok, \"ignoresurfname\"))\n\t\tspec.ignoresurfname = atoi(mystrtok(line));\n\telse if(!strcasecmp(tok, \"start\"))\n\t\tspec.startframe = max(atoi(mystrtok(line)), 0);\n\telse if(!strcasecmp(tok, \"end\"))\n\t\tspec.endframe = atoi(mystrtok(line));\n\telse if (!strcasecmp(tok, \"rotate\"))\n\t{\n\t\tVec3 ang;\n\t\tang.x = atof(mystrtok(line))*-M_PI/180;\n\t\tang.z = atof(mystrtok(line))*-M_PI/180;\n\t\tang.y = atof(mystrtok(line))*-M_PI/180;\n\t\tspec.rotate = Quat::fromangles(ang);\n\t}\n\telse if (!strcasecmp(tok, \"scale\"))\n\t\tspec.scale = atof(mystrtok(line));\n\telse if (!strcasecmp(tok, \"origin\"))\n\t{\n\t\tspec.translate.x = atof(mystrtok(line));\n\t\tspec.translate.y = atof(mystrtok(line));\n\t\tspec.translate.z = atof(mystrtok(line));\n\t}\n\telse if (!strcasecmp(tok, \"event\"))\n\t{\n\t\tconst char *poseidx = mystrtok(line);\n\t\tchar *dot;\n\t\tif (!strcmp(poseidx, \"reset\"))\n\t\t{\n\t\t\tspec.events.setsize(0);\n\t\t\treturn true;\n\t\t}\n\t\tevent_fte &ev = spec.events.add();\n\t\tev.anim = strtod(poseidx, &dot);\n\t\tif (*dot == ':')\n\t\t\tev.timestamp = strtoul(dot+1, NULL, 0);\n\t\telse\n\t\t{\n\t\t\tev.timestamp = strtod(poseidx, &dot);\n\t\t\tev.anim = ~0u;\t//fix up according to poses...\n\t\t}\n\t\tev.evcode = atoi(mystrtok(line));\n\t\tev.evdata_str = newstring(mystrtok(line));\n\t}\n\telse if (parsemeshfield(tok, line, spec.meshprops, defaults))\n\t\t;\n\telse\n\t\treturn false;\n\treturn true;\n}\n\nstatic struct\n{\n\tconst char *extname;\n\tbool (*write)(const char *filename);\n\tconst char *cmdname;\n\tconst char *altcmdname;\n} outputtypes[] =\n{\n\t{\".vvm\",\twriteiqm,\t\t\"output_vvm\"},\n\t{\".iqm\",\twriteiqm,\t\t\"output_iqm\"},\n\t{\".mdl\",\twriteqmdl,\t\t\"output_qmdl\"},\n\t{\".md16\",\twritemd16,\t\t\"output_md16\"},\n\t{\".md3\",\twritemd3,\t\t\"output_md3\"},\n};\n\nstatic bitnames modelflagnames[] = {\n\t{\"q1\",\t\"rocket\",\t\t1u<<0},\n\t{\"q1\",\t\"grenade\",\t\t1u<<1},\n\t{\"q1\",\t\"gib\",\t\t\t1u<<2},\n\t{\"q1\",\t\"rotate\",\t\t1u<<3},\n\t{\"q1\",\t\"tracer1\",\t\t1u<<4},\n\t{\"q1\",\t\"zomgib\",\t\t1u<<5},\n\t{\"q1\",\t\"tracer2\",\t\t1u<<6},\n\t{\"q1\",\t\"tracer3\",\t\t1u<<7},\n\t{\"q1\",\t\"holey\",\t\t1u<<14},\t//common extension\n\n\t{\"h2\",\t\"spidergib\",\t1u<<0},\t\t//conflicts with q1.\n\t{\"h2\",\t\"grenade\",\t\t1u<<1},\n\t{\"h2\",\t\"gib\",\t\t\t1u<<2},\n\t{\"h2\",\t\"rotate\",\t\t1u<<3},\n\t{\"h2\",\t\"tracer1\",\t\t1u<<4},\n\t{\"h2\",\t\"zomgib\",\t\t1u<<5},\n\t{\"h2\",\t\"tracer2\",\t\t1u<<6},\n\t{\"h2\",\t\"tracer3\",\t\t1u<<7},\n\t{\"h2\",\t\"fireball\",\t\t1u<<8},\n\t{\"h2\",\t\"ice\",\t\t\t1u<<9},\n\t{\"h2\",\t\"mipmap\",\t\t1u<<10},\n\t{\"h2\",\t\"spit\",\t\t\t1u<<11},\n\t{\"h2\",\t\"transparent\",\t1u<<12},\n\t{\"h2\",\t\"spell\",\t\t1u<<13},\n\t{\"h2\",\t\"holey\",\t\t1u<<14},\n\t{\"h2\",\t\"specialtrans\",\t1u<<15},\n\t{\"h2\",\t\"faceview\",\t\t1u<<16},\n\t{\"h2\",\t\"vorpmissile\",\t1u<<17},\n\t{\"h2\",\t\"setstaff\",\t\t1u<<18},\n\t{\"h2\",\t\"magicmissle\",\t1u<<19},\n\t{\"h2\",\t\"boneshard\",\t1u<<20},\n\t{\"h2\",\t\"scarab\",\t\t1u<<21},\n\t{\"h2\",\t\"acidball\",\t\t1u<<22},\n\t{\"h2\",\t\"bloodshot\",\t1u<<23},\n\n\t{NULL}\n};\n\nvoid parsecommands(char *filename, const char *outfiles[countof(outputtypes)], vector<filespec> &infiles, vector<hitbox> &hitboxes)\n{\n\tfilespec defaultspec;\n\tdefaultspec.reset();\n\n\tif (!quiet)\n\t\tconoutf(\"execing %s\", filename);\n\n\tstream *f = openfile(filename, \"rt\");\n\tif(!f)\n\t{\n\t\tfatal(\"Couldn't open command-file \\\"%s\\\"\\n\", filename);\n\t\treturn;\n\t}\n\n\tchar buf[2048];\n\twhile(f->getline(buf, sizeof(buf)))\n\t{\n\t\tconst char *tok;\n\t\tchar *line = buf;\n\t\ttok = mystrtok(&line);\n\t\tif (tok && *tok == '$')\n\t\t\ttok++;\n\t\tif (!tok)\n\t\t\tcontinue;\n\t\telse if (*tok == '#' || !strncasecmp(tok, \"//\", 2))\n\t\t{\t//comments\n\t\t\twhile (mystrtok(&line))\n\t\t\t\t;\n\t\t}\n//\t\telse if (!strcasecmp(tok, \"outputdir\"))\n//\t\t\t(void)mystrtok(&line);\n\t\telse if (!strcasecmp(tok, \"hitbox\") || !strcasecmp(tok, \"hbox\"))\n\t\t{\n\t\t\thitbox &hb = hitboxes.add();\n\t\t\thb.body = strtoul(mystrtok(&line), NULL, 0);\n\t\t\thb.bone = newstring(mystrtok(&line));\n\t\t\tfor (int i = 0; i < 3; i++)\n\t\t\t\thb.mins[i] = atof(mystrtok(&line));\n\t\t\tfor (int i = 0; i < 3; i++)\n\t\t\t\thb.maxs[i] = atof(mystrtok(&line));\n\t\t}\n\t\telse if (!strcasecmp(tok, \"exec\"))\n\t\t\tparsecommands(mystrtok(&line), outfiles, infiles, hitboxes);\n\t\telse if (!strcasecmp(tok, \"modelflags\"))\n\t\t\tmodelflags = parsebits(modelflagnames, &line);\n\t\telse if (!strcasecmp(tok, \"static\"))\n\t\t\tstripbones = true;\n\n\t\telse if (parseanimfield(tok, &line, defaultspec, true))\n\t\t\t;\n\n\t\telse if (!strcasecmp(tok, \"mesh\"))\n\t\t{\n\t\t\tmeshoverride &mo = meshoverrides.add();\n\t\t\tmo.name = newstring(mystrtok(&line));\n\t\t\tmo.props = defaultspec.meshprops;\n\n\t\t\twhile((\ttok = mystrtok(&line)))\n\t\t\t{\n\t\t\t\t//fixme: should probably separate this out.\n\t\t\t\tif (parsemeshfield(tok, &line, mo.props, false))\n\t\t\t\t\t;\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tprintf(\"unknown mesh token \\\"%s\\\"\\n\", tok);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\telse if (!strcasecmp(tok, \"bone\"))\n\t\t{\n\t\t\tboneoverride &mo = boneoverrides.add();\n\t\t\tmo.name = newstring(mystrtok(&line));\n\t\t\tmo.props = boneoverride::prop();\n\n\t\t\twhile((\ttok = mystrtok(&line)))\n\t\t\t{\n\t\t\t\t//fixme: should probably separate this out.\n\t\t\t\tif (parsebonefield(tok, &line, mo.props, false))\n\t\t\t\t\t;\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tprintf(\"unknown mesh token \\\"%s\\\"\\n\", tok);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\telse if (!strcasecmp(tok, \"import\") || !strcasecmp(tok, \"model\") || !strcasecmp(tok, \"scene\") || !strcasecmp(tok, \"animation\"))\n\t\t{\n\t\t\tfilespec inspec = defaultspec;\n\t\t\t\n\t\t\t//first token is always the filename(s)\n\t\t\tinspec.file = newstring(mystrtok(&line));\n\n\t\t\twhile((\ttok = mystrtok(&line)))\n\t\t\t{\n\t\t\t\tif (parseanimfield(tok, &line, inspec, false))\n\t\t\t\t\t;\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tprintf(\"unknown scene token \\\"%s\\\"\\n\", tok);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tinfiles.add(inspec);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tsize_t n, j;\n\t\t\tif (!strcasecmp(tok, \"output\"))\n\t\t\t\ttok = \"output_iqm\";\n\t\t\tfor (n = 0; n < countof(outputtypes); n++)\n\t\t\t{\n\t\t\t\tif (!strcasecmp(tok, outputtypes[n].cmdname))\n\t\t\t\t{\n\t\t\t\t\toutfiles[n] = newstring(mystrtok(&line));\n\t\t\t\t\tfor (j = 0; j < countof(outputtypes); j++)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (n!=j && outfiles[j] && !strcasecmp(outfiles[n], outfiles[j]))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tprintf(\"cancelling %s\\n\", outputtypes[j].cmdname);\n\t\t\t\t\t\t\toutfiles[j] = NULL;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\ttok = \"\";\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (*tok)\n\t\t\t{\n\t\t\t\tprintf(\"unsupported command \\\"%s\\\"\\n\", tok);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\n\t\tif ((tok=mystrtok(&line)))\n\t\t\tif (*tok)\n\t\t\t\tprintf(\"unexpected junk at end-of-line \\\"%s\\\" \\\"%s\\\"\\n\", buf, tok);\n\t}\n\tdelete f;\n}\n\nint main(int argc, char **argv)\n{\n\tif(argc <= 1) help(EXIT_FAILURE, false);\n\n\tvector<filespec> infiles;\n\tvector<hitbox> hitboxes;\n\tfilespec inspec;\n\tconst char *outfiles[countof(outputtypes)] = {};\n\tfor(int i = 1; i < argc; i++)\n\t{\n\t\tif(argv[i][0] == '-') \n\t\t{\n\t\t\tif(argv[i][1] == '-')\n\t\t\t{\n\t\t\t\tif(!strcasecmp(&argv[i][2], \"set\"))\n\t\t\t\t{\n#ifdef FTEPLUGIN\n\t\t\t\t\tif(i + 2 < argc)\n\t\t\t\t\t\tfte::Cvar_Create(argv[i+1], argv[i+2], 0, NULL, \"cmdline\");\n#endif\n\t\t\t\t\ti+=2;\n\t\t\t\t}\n\t\t\t\telse if(!strcasecmp(&argv[i][2], \"cmd\")) { if(i + 1 < argc) parsecommands(argv[++i], outfiles, infiles, hitboxes); }\n\t\t\t\telse if(!strcasecmp(&argv[i][2], \"noext\")) noext = true;\n\t\t\t\telse if(!strcasecmp(&argv[i][2], \"fps\")) { if(i + 1 < argc) inspec.fps = atof(argv[++i]); }\n\t\t\t\telse if(!strcasecmp(&argv[i][2], \"name\")) { if(i + 1 < argc) inspec.name = argv[++i]; }\n\t\t\t\telse if(!strcasecmp(&argv[i][2], \"loop\")) { inspec.flags |= IQM_LOOP; }\n\t\t\t\telse if(!strcasecmp(&argv[i][2], \"unpack\")) { inspec.flags |= IQM_UNPACK; }\n\t\t\t\telse if(!strcasecmp(&argv[i][2], \"start\")) { if(i + 1 < argc) inspec.startframe = max(atoi(argv[++i]), 0); }\n\t\t\t\telse if(!strcasecmp(&argv[i][2], \"end\")) { if(i + 1 < argc) inspec.endframe = atoi(argv[++i]); }\n\t\t\t\telse if(!strcasecmp(&argv[i][2], \"scale\")) { if(i + 1 < argc) inspec.scale = clamp(atof(argv[++i]), 1e-8, 1e8); }\n\t\t\t\telse if(!strcasecmp(&argv[i][2], \"rotate\")) { if(i + 3 < argc) inspec.rotate = Quat::fromdegrees(-Vec3(atof(argv[i+1]),atof(argv[i+3]),atof(argv[i+2]))); i+=3;}\n\t\t\t\telse if(!strcasecmp(&argv[i][2], \"yup\"))\tinspec.rotate = Quat::fromangles(Vec3(0,-M_PI,0));\n\t\t\t\telse if(!strcasecmp(&argv[i][2], \"zup\"))\tinspec.rotate = Quat::fromdegrees(Vec3(0,-90,-90));\n\t\t\t\telse if(!strcasecmp(&argv[i][2], \"ignoresurfname\")) inspec.ignoresurfname = true;\n\t\t\t\telse if(!strcasecmp(&argv[i][2], \"help\")) help(EXIT_SUCCESS,true);\n\t\t\t\telse if(!strcasecmp(&argv[i][2], \"forcejoints\")) forcejoints = true;\n\t\t\t\telse if(!strcasecmp(&argv[i][2], \"materialprefix\")) { if(i + 1 < argc) inspec.materialprefix = argv[++i]; }\n\t\t\t\telse if(!strcasecmp(&argv[i][2], \"materialsuffix\")) { if(i + 1 < argc) inspec.materialsuffix = argv[++i]; }\n\t\t\t\telse if(!strcasecmp(&argv[i][2], \"qex\")) inspec.materialprefix = \"progs/\", inspec.materialsuffix = \"_00_00.lmp\", inspec.flags |= IQM_UNPACK;\n\t\t\t\telse if(!strcasecmp(&argv[i][2], \"modelflags\")) { if(i + 1 < argc) { modelflags |= parsebits(modelflagnames, &argv[++i]); }}\n\t\t\t\telse if(!strcasecmp(&argv[i][2], \"static\")) stripbones = true;\n\t\t\t\telse if(!strcasecmp(&argv[i][2], \"meshtrans\"))\n\t\t\t\t{\n\t\t\t\t\tif(i + 1 < argc) switch(sscanf(argv[++i], \"%lf , %lf , %lf\", &gmeshtrans.x, &gmeshtrans.y, &gmeshtrans.z))\n\t\t\t\t\t{\n\t\t\t\t\t\tcase 1: gmeshtrans = Vec3(0, 0, gmeshtrans.x); break;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\telse switch(argv[i][1])\n\t\t\t{\n\t\t\tcase 'h':\n\t\t\t\thelp(EXIT_SUCCESS,true);\n\t\t\t\tbreak;\n\t\t\tcase 's':\n\t\t\t\tif(i + 1 < argc) gscale = clamp(atof(argv[++i]), 1e-8, 1e8);\n\t\t\t\tbreak;\n\t\t\tcase 'j':\n\t\t\t\tforcejoints = true;\n\t\t\t\tbreak;\n\t\t\tcase 'v':\n\t\t\t\tverbose = true;\n\t\t\t\tbreak;\n\t\t\tcase 'q':\n\t\t\t\tquiet = true;\n\t\t\t\tbreak;\n\t\t\tcase 'n':\n\t\t\t\tnoext = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tconst char *type = strrchr(argv[i], '.');\n\t\t\tif (type && (!strcasecmp(type, \".cmd\")||!strcasecmp(type, \".cfg\")||!strcasecmp(type, \".txt\")||!strcasecmp(type, \".qc\")))\t//.qc to humour halflife fanboys\n\t\t\t\tparsecommands(argv[i], outfiles, infiles, hitboxes);\n\t\t\telse\n\t\t\t{\n\t\t\t\tsize_t j;\n\t\t\t\tfor (j = 0; j < countof(outfiles); j++)\n\t\t\t\t\tif (outfiles[j])\n\t\t\t\t\t\tbreak;\n\t\t\t\tif(j == countof(outfiles))\n\t\t\t\t{\n\t\t\t\t\tfor (j = countof(outfiles)-1; j > 0; j--)\n\t\t\t\t\t\tif (type && !strcasecmp(type, outputtypes[j].extname))\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\toutfiles[j] = argv[i];\t//first arg is the output name, if its not an export script thingie.\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tinfiles.add(inspec).file = argv[i];\n\t\t\t\t\tinspec.reset();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tsize_t n;\n\tfor (n = 0; n < countof(outputtypes) && !outfiles[n]; n++);\n\tif(n == countof(outfiles)) fatal(\"no output file specified\");\n\tif(infiles.empty())\n\t{\n\t\tif (outfiles[0])\n\t\t{\n\t\t\tinfiles.add(inspec).file = outfiles[0];\n\t\t\tinspec.reset();\n\t\t\toutfiles[0] = NULL;\n\t\t}\n\t\telse\n\t\t\tfatal(\"no input files specified\");\n\t}\n\n\tif(gscale != 1) printf(\"scale: %f\\n\", escale);\n\tif(gmeshtrans != Vec3(0, 0, 0)) printf(\"mesh translate: %f, %f, %f\\n\", gmeshtrans.x, gmeshtrans.y, gmeshtrans.z);\n\n\tloopv(infiles)\n\t{\n\t\tconst filespec &inspec = infiles[i];\n\t\tconst char *infile = inspec.file, *type = strrchr(infile, '.');\n\t\tif (verbose)\n\t\t\tconoutf(\"importing %s\", infile);\n\t\tif(!type) fatal(\"no file type: %s\", infile);\n\t\telse if(!strcasecmp(type, \".md5mesh\"))\n\t\t{\n\t\t\tif(!loadmd5mesh(infile, inspec)) fatal(\"failed reading: %s\", infile);\n\t\t}\n\t\telse if(!strcasecmp(type, \".md5anim\"))\n\t\t{\n\t\t\tif(!loadmd5anim(infile, inspec)) fatal(\"failed reading: %s\", infile);\n\t\t}\n\t\telse if(!strcasecmp(type, \".md5\"))\n\t\t{\n\t\t\tchar tmp[MAXSTRLEN];\n\t\t\tformatstring(tmp, \"%smesh\", infile);\n\t\t\tif(!loadmd5mesh(tmp, inspec)) fatal(\"failed reading: %s\", tmp);\n\t\t\tformatstring(tmp, \"%sanim\", infile);\n\t\t\tif(!loadmd5anim(tmp, inspec)) fatal(\"failed reading: %s\", tmp);\n\t\t}\n\t\telse if(!strcasecmp(type, \".iqe\"))\n\t\t{\n\t\t\tif(!loadiqe(infile, inspec)) fatal(\"failed reading: %s\", infile);\n\t\t}\n\t\telse if(!strcasecmp(type, \".smd\"))\n\t\t{\n\t\t\tif(!loadsmd(infile, inspec)) fatal(\"failed reading: %s\", infile);\n\t\t}\n\t\telse if(!strcasecmp(type, \".fbx\"))\n\t\t{\n\t\t\tif(!loadfbx(infile, inspec)) fatal(\"failed reading: %s\", infile);\n\t\t}\n\t\telse if(!strcasecmp(type, \".obj\"))\n\t\t{\n\t\t\tif(!loadobj(infile, inspec)) fatal(\"failed reading: %s\", infile);\n\t\t}\n\t\telse if(!strcasecmp(type, \".glb\"))\n\t\t{\n#ifdef FTEPLUGIN\n\t\t\tif(!fte::loadglb(infile, inspec)) fatal(\"failed reading: %s\", infile);\n#else\n\t\t\tfatal(\"GLTF/GLB support was disabled at compile time\");\n#endif\n\t\t}\n\t\telse if(!strcasecmp(type, \".gltf\"))\n\t\t{\n#ifdef FTEPLUGIN\n\t\t\tif(!fte::loadgltf(infile, inspec)) fatal(\"failed reading: %s\", infile);\n#else\n\t\t\tfatal(\"GLTF/GLB support was disabled at compile time\");\n#endif\n\t\t}\n\t\telse fatal(\"unknown file type: %s\", type);\t \n\t}\n\n\tgenhitboxes(hitboxes);\n\n\tloopv(boneoverrides) if (!boneoverrides[i].used)\n\t\tconoutf(\"warning: bone \\\"%s\\\" overriden, but not present\", boneoverrides[i].name);\n\n\tif (noext && meshoverrides.length())\n\t\tconoutf(\"warning: mesh overrides used, but iqm extensions disabled\");\n\telse loopv(meshoverrides)\n\t\tconoutf(\"warning: mesh \\\"%s\\\" overriden, but not present\", meshoverrides[i].name);\n\n\n\tif (stripbones)\n\t{\n\t\tif (!quiet)\n\t\t\tconoutf(\"static bones\");\n\t\tjoints.setsize(0);\n\t\tposes.setsize(0);\n\t\tframes.setsize(0);\n\t\tanims.setsize(0);\n\t\tbounds.setsize(0);\n\t}\n\n\tcalcanimdata();\n\n\tif (!quiet)\n\t{\n\t\tconoutf(\"bone list:\");\n\t\tprintbones();\n//\t\tprintbonelist();\n\t}\n\n\tif (!quiet)\n\t\tconoutf(\"\");\n\n\tfor (size_t n = 0; n < countof(outputtypes); n++)\n\t{\n\t\tif (outfiles[n] != NULL)\n\t\t{\n\t\t\tif(outputtypes[n].write(outfiles[n]))\n\t\t\t{\n\t\t\t\tif (!quiet)\n\t\t\t\t\tconoutf(\"exported: %s\", outfiles[n]);\n\t\t\t}\n\t\t\telse fatal(\"failed writing: %s\", outfiles[n]);\n\t\t}\n\t}\n\treturn EXIT_SUCCESS;\n}\n\n"
  },
  {
    "path": "iqm/iqm.h",
    "content": "#ifndef __IQM_H__\n#define __IQM_H__\n\n#define IQM_MAGIC \"INTERQUAKEMODEL\"\n#define IQM_VERSION 2\n\nstruct iqmheader\n{\n\tchar magic[16];\n\tunsigned int version;\n\tunsigned int filesize;\n\tunsigned int flags;\n\tunsigned int num_text, ofs_text;\n\tunsigned int num_meshes, ofs_meshes;\n\tunsigned int num_vertexarrays, num_vertexes, ofs_vertexarrays;\n\tunsigned int num_triangles, ofs_triangles, ofs_adjacency;\n\tunsigned int num_joints, ofs_joints;\n\tunsigned int num_poses, ofs_poses;\n\tunsigned int num_anims, ofs_anims;\n\tunsigned int num_frames, num_framechannels, ofs_frames, ofs_bounds;\n\tunsigned int num_comment, ofs_comment;\n\tunsigned int num_extensions, ofs_extensions;\n};\n\nstruct iqmmesh\n{\n\tunsigned int name;\n\tunsigned int material;\n\tunsigned int first_vertex, num_vertexes;\n\tunsigned int first_triangle, num_triangles;\n};\n\nenum\n{\n\tIQM_POSITION     = 0,\n\tIQM_TEXCOORD     = 1,\n\tIQM_NORMAL       = 2,\n\tIQM_TANGENT      = 3,\n\tIQM_BLENDINDEXES = 4,\n\tIQM_BLENDWEIGHTS = 5,\n\tIQM_COLOR        = 6,\n\tIQM_CUSTOM       = 0x10\n};\n\nenum\n{\n\tIQM_BYTE   = 0,\n\tIQM_UBYTE  = 1,\n\tIQM_SHORT  = 2,\n\tIQM_USHORT = 3,\n\tIQM_INT    = 4,\n\tIQM_UINT   = 5,\n\tIQM_HALF   = 6,\n\tIQM_FLOAT  = 7,\n\tIQM_DOUBLE = 8\n};\n\nstruct iqmtriangle\n{\n\tunsigned int vertex[3];\n};\n\nstruct iqmadjacency\n{\n\tunsigned int triangle[3];\n};\n\nstruct iqmjointv1\n{\n\tunsigned int name;\n\tint parent;\n\tfloat translate[3], rotate[3], scale[3];\n};\n\nstruct iqmjoint\n{\n\tunsigned int name;\n\tint parent;\n\tfloat translate[3], rotate[4], scale[3];\n};\n\nstruct iqmposev1\n{\n\tint parent;\n\tunsigned int mask;\n\tfloat channeloffset[9];\n\tfloat channelscale[9];\n};\n\nstruct iqmpose\n{\n\tint parent;\n\tunsigned int mask;\n\tfloat channeloffset[10];\n\tfloat channelscale[10];\n};\n\nstruct iqmanim\n{\n\tunsigned int name;\n\tunsigned int first_frame, num_frames;\n\tfloat framerate;\n\tunsigned int flags;\n};\n\nenum\n{\n\tIQM_LOOP = 1<<0\n};\n\nstruct iqmvertexarray\n{\n\tunsigned int type;\n\tunsigned int flags;\n\tunsigned int format;\n\tunsigned int size;\n\tunsigned int offset;\n};\n\nstruct iqmbounds\n{\n\tfloat bbmin[3], bbmax[3];\n\tfloat xyradius, radius;\n};\n\nstruct iqmextension\n{\n\tunsigned int name;\n\tunsigned int num_data, ofs_data;\n\tunsigned int ofs_extensions; // pointer to next extension\n};\n\nstruct iqmext_fte_mesh\n{\n\tunsigned int contents;\t\t//default CONTENTS_BODY\n\tunsigned int surfaceflags;\t//propagates to trace_surfaceflags\n\tunsigned int body;\t\t\t//the part of the body that this mesh is meant to be from\n\tunsigned int geomset;\n\tunsigned int geomid;\n\tfloat\tmindist;\n\tfloat\tmaxdist;\n};\nstruct iqmext_fte_events\n{\n\tunsigned int anim;\n\tfloat timestamp;\n\tunsigned int evcode;\n\tunsigned int evdata_str;\n};\n\n\n//skin lump is made of 3 parts\nstruct iqmext_fte_skin\n{\n\tunsigned int nummeshskins;\n\tunsigned int numskinframes;\n\t//unsigned int numskins[nummeshes];\n\t//iqmext_fte_skin_skinframe[numskinframes];\n\t//iqmext_fte_skin_meshskin mesh0[numskins[0]];\n\t//iqmext_fte_skin_meshskin mesh1[numskins[1]]; etc\n};\nstruct iqmext_fte_skin_skinframe\n{\t//as many as needed\n\tunsigned int material_idx;\n\tunsigned int shadertext_idx;\n};\nstruct iqmext_fte_skin_meshskin\n{\n\tunsigned int firstframe;\t//index into skinframes\n\tunsigned int countframes;\t//skinframes\n\tfloat interval;\n};\n#endif\n\n"
  },
  {
    "path": "iqm/util.h",
    "content": "#include <math.h>\n#include <string.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <ctype.h>\n#include <stdarg.h>\n#include <limits.h>\n#include <assert.h>\n#include <float.h>\n#include \"iqm.h\"\n\n#define ASSERT(c) if(c) {}\n\n#ifdef NULL\n#undef NULL\n#endif\n#define NULL 0\n\n#ifdef _WIN32\n#ifndef M_PI\n#define M_PI 3.1415926535897932384626433832795\n#endif\n#ifndef strcasecmp\n#define strcasecmp _stricmp\n#endif\n#ifndef strncasecmp\n#define strncasecmp _strnicmp\n#endif\n#endif\n\ntypedef unsigned char uchar;\ntypedef unsigned short ushort;\ntypedef unsigned int uint;\ntypedef signed long long int llong;\ntypedef unsigned long long int ullong;\n\n/*inline void *operator new(size_t size)\n{\n\tvoid *p = malloc(size);\n\tif(!p) abort();\n\treturn p;\n}\ninline void *operator new[](size_t size)\n{\n\tvoid *p = malloc(size);\n\tif(!p) abort();\n\treturn p;\n}\ninline void operator delete(void *p) { if(p) free(p); }\ninline void operator delete[](void *p) { if(p) free(p); }\ninline void operator delete(void *p, size_t sz) { if(p) free(p); }\ninline void operator delete[](void *p, size_t sz) { if(p) free(p); }\n*/\ninline void *operator new(size_t, void *p) { return p; }\ninline void *operator new[](size_t, void *p) { return p; }\ninline void operator delete(void *, void *) {}\ninline void operator delete[](void *, void *) {}\n\n#ifdef swap\n#undef swap\n#endif\ntemplate<class T>\nstatic inline void swap(T &a, T &b)\n{\n\tT t = a;\n\ta = b;\n\tb = t;\n}\n#ifdef max\n#undef max\n#endif\n#ifdef min\n#undef min\n#endif\ntemplate<class T>\nstatic inline T max(T a, T b)\n{\n\treturn a > b ? a : b;\n}\ntemplate<class T>\nstatic inline T min(T a, T b)\n{\n\treturn a < b ? a : b;\n}\n\n#ifndef countof\n#define countof(n) (sizeof(n)/sizeof(n[0]))\n#endif\n#define clamp(a,b,c) (max(b, min(a, c)))\n\n#define loop(v,m) for(int v = 0; v<int(m); v++)\n#define loopi(m) loop(i,m)\n#define loopj(m) loop(j,m)\n#define loopk(m) loop(k,m)\n#define loopl(m) loop(l,m)\n\n#ifdef WIN32\n#ifdef M_PI\n#undef M_PI\n#endif\n#define M_PI 3.14159265\n\n#ifndef __GNUC__\n#pragma warning (3: 4189)       // local variable is initialized but not referenced\n#pragma warning (disable: 4244) // conversion from 'int' to 'float', possible loss of data\n#pragma warning (disable: 4267) // conversion from 'size_t' to 'int', possible loss of data\n#pragma warning (disable: 4355) // 'this' : used in base member initializer list\n#pragma warning (disable: 4996) // 'strncpy' was declared deprecated\n#endif\n\n#define PATHDIV '\\\\'\n#else\n#define __cdecl\n#define _vsnprintf vsnprintf\n#define PATHDIV '/'\n#endif\n\n// easy safe strings\n\n#define MAXSTRLEN 260\ntypedef char string[MAXSTRLEN];\n\ninline void vformatstring(char *d, const char *fmt, va_list v, int len = MAXSTRLEN) { _vsnprintf(d, len, fmt, v); d[len-1] = 0; }\ninline char *copystring(char *d, const char *s, size_t len = MAXSTRLEN)\n{ \n\tsize_t slen = min(strlen(s)+1, len);\n\tmemcpy(d, s, slen);\n\td[slen-1] = 0;\n\treturn d;\n}\ninline char *concatstring(char *d, const char *s) { size_t len = strlen(d); return copystring(d+len, s, MAXSTRLEN-len); }\n\ntemplate<size_t N> inline void formatstring(char (&d)[N], const char *fmt, ...)\n{\n\tva_list v;\n\tva_start(v, fmt);\n\tvformatstring(d, fmt, v, int(N));\n\tva_end(v);\n}\n\n#define defformatstring(d,...) string d; formatstring(d, __VA_ARGS__)\n#define defvformatstring(d,last,fmt) string d; { va_list ap; va_start(ap, last); vformatstring(d, fmt, ap); va_end(ap); }\n\ninline char *newstring(size_t l)                { return new char[l+1]; }\ninline char *newstring(const char *s, size_t l) { return copystring(newstring(l), s, l+1); }\ninline char *newstring(const char *s)           { size_t l = strlen(s); char *d = newstring(l); memcpy(d, s, l+1); return d; }\n\n#define loopv(v)    for(int i = 0; i<(v).length(); i++)\n#define loopvj(v)   for(int j = 0; j<(v).length(); j++)\n#define loopvk(v)   for(int k = 0; k<(v).length(); k++)\n#define loopvrev(v) for(int i = (v).length()-1; i>=0; i--)\n\ntemplate <class T> struct vector\n{\n\tstatic const int MINSIZE = 8;\n\n\tT *buf;\n\tint alen, ulen;\n\n\tvector() : buf(NULL), alen(0), ulen(0)\n\t{\n\t}\n\n\tvector(const vector &v) : buf(NULL), alen(0), ulen(0)\n\t{\n\t\t*this = v;\n\t}\n\n\t~vector() { setsize(0); if(buf) delete[] (uchar *)buf; }\n\n\tvector<T> &operator=(const vector<T> &v)\n\t{\n\t\tsetsize(0);\n\t\tif(v.length() > alen) growbuf(v.length());\n\t\tloopv(v) add(v[i]);\n\t\treturn *this;\n\t}\n\n\tT &add(const T &x)\n\t{\n\t\tif(ulen==alen) growbuf(ulen+1);\n\t\tnew (&buf[ulen]) T(x);\n\t\treturn buf[ulen++];\n\t}\n\n\tT &add()\n\t{\n\t\tif(ulen==alen) growbuf(ulen+1);\n\t\tnew (&buf[ulen]) T;\n\t\treturn buf[ulen++];\n\t}\n\n\tT &dup()\n\t{\n\t\tif(ulen==alen) growbuf(ulen+1);\n\t\tnew (&buf[ulen]) T(buf[ulen-1]);\n\t\treturn buf[ulen++];\n\t}\n\n\tbool inrange(uint i) const { return i<uint(ulen); }\n\tbool inrange(int i) const { return i>=0 && i<ulen; }\n\n\tT &pop() { return buf[--ulen]; }\n\tT &last() { return buf[ulen-1]; }\n\tvoid drop() { ulen--; buf[ulen].~T(); }\n\n\tbool empty() const { return ulen==0; }\n\tint capacity() const { return alen; }\n\tint length() const { return ulen; }\n\tT &operator[](int i) { ASSERT(i>=0 && i<ulen); return buf[i]; }\n\tconst T &operator[](int i) const { ASSERT(i >= 0 && i<ulen); return buf[i]; }\n\n\tvoid setsize(int i) { ASSERT(i <= ulen); ulen = i; }\n\n\tvoid swap(vector<T> &v)\n\t{\n\t\t::swap(buf, v.buf);\n\t\t::swap(ulen, v.ulen);\n\t\t::swap(alen, v.alen);\n\t}\n\n\tT *getbuf() { return buf; }\n\tconst T *getbuf() const { return buf; }\n\tbool inbuf(const T *e) const { return e >= buf && e < &buf[ulen]; }\n\n\tvoid growbuf(int sz)\n\t{\n\t\tint olen = alen;\n\t\tif(!alen) alen = max(MINSIZE, sz);\n\t\telse while(alen < sz) alen *= 2;\n\t\tif(alen <= olen) return;\n\t\tuchar *newbuf = new uchar[alen*sizeof(T)];\n\t\tif(olen > 0)\n\t\t{\n\t\t\tmemcpy(newbuf, buf, olen*sizeof(T));\n\t\t\tdelete[] (uchar *)buf;\n\t\t}\n\t\tbuf = (T *)newbuf;\n\t}\n\n\tT *reserve(int sz)\n\t{\n\t\tif(ulen+sz > alen) growbuf(ulen+sz);\n\t\treturn &buf[ulen];\n\t}\n\n\tvoid advance(int sz)\n\t{\n\t\tulen += sz;\n\t}\n\n\tvoid put(const T *v, int n)\n\t{\n\t\tmemcpy(reserve(n), v, n*sizeof(T));\n\t\tadvance(n);\n\t}\n};\n\nstatic inline uint hthash(const char *key)\n{\n\tuint h = 5381;\n\tfor(int i = 0, k; (k = key[i]); i++) h = ((h<<5)+h)^k;    // bernstein k=33 xor\n\treturn h;\n}\n\nstatic inline bool htcmp(const char *x, const char *y)\n{\n\treturn !strcmp(x, y);\n}\n\nstatic inline uint hthash(int key)\n{\n\treturn key;\n}\n\nstatic inline bool htcmp(int x, int y)\n{\n\treturn x==y;\n}\n\nstatic inline bool htcmp(double x, double y)\n{\n\treturn x == y;\n}\n\nstatic inline uint hthash(double k)\n{\n\tunion { double f; uint h[sizeof(double)/sizeof(uint)]; } conv;\n\tconv.f = k;\n\tuint hash = conv.h[0];\n\tfor(size_t i = 1; i < sizeof(conv.h)/sizeof(uint); i++) hash ^= conv.h[i];\n\treturn hash;\n}\n\ntemplate <class K, class T> struct hashtable\n{\n\ttypedef K key;\n\ttypedef const K const_key;\n\ttypedef T value;\n\ttypedef const T const_value;\n\n\tenum { CHUNKSIZE = 64 };\n\n\tstruct chain      { T data; K key; chain *next; };\n\tstruct chainchunk { chain chains[CHUNKSIZE]; chainchunk *next; };\n\n\tint size;\n\tint numelems;\n\tchain **table;\n\n\tchainchunk *chunks;\n\tchain *unused;\n\n\thashtable(int size = 1<<10)\n\t  : size(size)\n\t{\n\t\tnumelems = 0;\n\t\tchunks = NULL;\n\t\tunused = NULL;\n\t\ttable = new chain *[size];\n\t\tloopi(size) table[i] = NULL;\n\t}\n\n\t~hashtable()\n\t{\n\t\tif(table) delete[] table;\n\t\tdeletechunks();\n\t}\n\n\tchain *insert(const K &key, uint h)\n\t{\n\t\tif(!unused)\n\t\t{\n\t\t\tchainchunk *chunk = new chainchunk;\n\t\t\tchunk->next = chunks;\n\t\t\tchunks = chunk;\n\t\t\tloopi(CHUNKSIZE-1) chunk->chains[i].next = &chunk->chains[i+1];\n\t\t\tchunk->chains[CHUNKSIZE-1].next = unused;\n\t\t\tunused = chunk->chains;\n\t\t}\n\t\tchain *c = unused;\n\t\tunused = unused->next;\n\t\tc->key = key;\n\t\tc->next = table[h]; \n\t\ttable[h] = c;\n\t\tnumelems++;\n\t\treturn c;\n\t}\n\n\t#define HTFIND(success, fail) \\\n\t\tuint h = hthash(key)&(size-1); \\\n\t\tfor(chain *c = table[h]; c; c = c->next) \\\n\t\t{ \\\n\t\t\tif(htcmp(key, c->key)) return (success); \\\n\t\t} \\\n\t\treturn (fail);\n\n\ttemplate<class L>\n\tT *access(const L &key)\n\t{\n\t\tHTFIND(&c->data, NULL);\n\t}\n\n\ttemplate<class L>\n\tT &access(const L &key, const T &data)\n\t{\n\t\tHTFIND(c->data, insert(key, h)->data = data);\n\t}\n\n\ttemplate<class L>\n\tconst T &find(const L &key, const T &notfound)\n\t{\n\t\tHTFIND(c->data, notfound);\n\t}\n\n\ttemplate<class L>\n\tT &operator[](const L &key)\n\t{\n\t\tHTFIND(c->data, insert(key, h)->data);\n\t}\n\n\t#undef HTFIND\n   \n\ttemplate<class L>\n\tbool remove(const L &key)\n\t{\n\t\tuint h = hthash(key)&(size-1); \n\t\tfor(chain **p = &table[h], *c = table[h]; c; p = &c->next, c = c->next)\n\t\t{\n\t\t\tif(htcmp(key, c->key))\n\t\t\t{\n\t\t\t\t*p = c->next;\n\t\t\t\tc->data.~T();\n\t\t\t\tc->key.~K();\n\t\t\t\tnew (&c->data) T;\n\t\t\t\tnew (&c->key) K;\n\t\t\t\tc->next = unused;\n\t\t\t\tunused = c;\n\t\t\t\tnumelems--;\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\tvoid deletechunks()\n\t{\n\t\tfor(chainchunk *nextchunk; chunks; chunks = nextchunk)\n\t\t{\n\t\t\tnextchunk = chunks->next;\n\t\t\tdelete chunks;\n\t\t}\n\t}\n\n\tvoid clear()\n\t{\n\t\tif(!numelems) return;\n\t\tloopi(size) table[i] = NULL;\n\t\tnumelems = 0;\n\t\tunused = NULL;\n\t\tdeletechunks();\n\t}\n};\n\n#define enumerate(ht,k,e,t,f,b) loopi((ht).size)  for(hashtable<k,t>::chain *enumc = (ht).table[i]; enumc;) { hashtable<k,t>::const_key &e = enumc->key; t &f = enumc->data; enumc = enumc->next; b; }\n\ntemplate<class T>\nstruct unionfind\n{\n\tstruct ufval\n\t{\n\t\tint rank, next;\n\t\tT val;\n\n\t\tufval(const T &val) : rank(0), next(-1), val(val) {}\n\t};\n\n\tvector<ufval> ufvals;\n\n\tvoid clear()\n\t{\n\t\tufvals.setsize(0);\n\t}\n\n\tconst T &find(int k, const T &noval, const T &initval)\n\t{\n\t\tif(k>=ufvals.length()) return initval;\n\t\twhile(ufvals[k].next>=0) k = ufvals[k].next;\n\t\tif(ufvals[k].val == noval) ufvals[k].val = initval;\n\t\treturn ufvals[k].val;\n\t}\n\n\tint compressfind(int k)\n\t{\n\t\tif(ufvals[k].next<0) return k;\n\t\treturn ufvals[k].next = compressfind(ufvals[k].next);\n\t}\n\n\tvoid unite (int x, int y, const T &noval)\n\t{\n\t\twhile(ufvals.length() <= max(x, y)) ufvals.add(ufval(noval));\n\t\tx = compressfind(x);\n\t\ty = compressfind(y);\n\t\tif(x==y) return;\n\t\tufval &xval = ufvals[x], &yval = ufvals[y];\n\t\tif(xval.rank < yval.rank) xval.next = y;\n\t\telse\n\t\t{\n\t\t\tyval.next = x;\n\t\t\tif(xval.rank==yval.rank) yval.rank++;\n\t\t}\n\t}\n};\n\ntemplate<class T>\nstruct listnode\n{\n\tT *prev, *next;\n};\n\ntemplate<class T>\nstruct list\n{\n\ttypedef listnode<T> node;\n\n\tint size;\n\tlistnode<T> nodes;\n\n\tlist() { clear(); }\n\n\tbool empty() const { return nodes.prev == nodes.next; }\n\tbool notempty() const { return nodes.prev != nodes.next; }\n\n\tT *first() const { return nodes.next; }\n\tT *last() const { return nodes.prev; }\n\tT *end() const { return (T *)&nodes; }\n\n\tvoid clear()\n\t{\n\t\tsize = 0;\n\t\tnodes.prev = nodes.next = (T *)&nodes;\n\t}\n\n\tT *remove(T *node)\n\t{\n\t\tsize--;\n\t\tnode->prev->next = node->next;\n\t\tnode->next->prev = node->prev;\n\t\treturn node;\n\t}\n\n\tT *insertafter(T *node, T *pos)\n\t{\n\t\tsize++;\n\t\tnode->next = pos->next;\n\t\tnode->next->prev = node;\n\t\tnode->prev = pos;\n\t\tpos->next = node;\n\t\treturn node;\n\t}\n\n\tT *insertbefore(T *node, T *pos)\n\t{\n\t\tsize++;\n\t\tnode->prev = pos->prev;\n\t\tnode->prev->next = node;\n\t\tnode->next = pos;\n\t\tpos->prev = node;\n\t\treturn node;\n\t} \n\n\tT *insertfirst(T *node) { return insertafter(node, end()); }\n\tT *insertlast(T *node) { return insertbefore(node, end()); }\n\n\tT *removefirst() { return remove(first()); }\n\tT *removelast() { return remove(last()); }\n};\n\nstatic inline bool islittleendian() { union { int i; uchar b[sizeof(int)]; } conv; conv.i = 1; return conv.b[0] != 0; }\ninline ushort endianswap16(ushort n) { return (n<<8) | (n>>8); }\ninline uint endianswap32(uint n) { return (n<<24) | (n>>24) | ((n>>8)&0xFF00) | ((n<<8)&0xFF0000); }\ninline ullong endianswap64(ullong n) { return endianswap32(uint(n >> 32)) | ((ullong)endianswap32(uint(n)) << 32); }\ntemplate<class T> inline T endianswap(T n) { union { T t; uint i; } conv; conv.t = n; conv.i = endianswap32(conv.i); return conv.t; }\ntemplate<> inline uchar endianswap<uchar>(uchar n) { return n; }\ntemplate<> inline char endianswap<char>(char n) { return n; }\ntemplate<> inline ushort endianswap<ushort>(ushort n) { return endianswap16(n); }\ntemplate<> inline short endianswap<short>(short n) { return endianswap16(n); }\ntemplate<> inline uint endianswap<uint>(uint n) { return endianswap32(n); }\ntemplate<> inline int endianswap<int>(int n) { return endianswap32(n); }\ntemplate<> inline ullong endianswap<ullong>(ullong n) { return endianswap64(n); }\ntemplate<> inline llong endianswap<llong>(llong n) { return endianswap64(n); }\ntemplate<> inline double endianswap<double>(double n) { union { double t; uint i; } conv; conv.t = n; conv.i = endianswap64(conv.i); return conv.t; }\ntemplate<class T> inline void endianswap(T *buf, int len) { for(T *end = &buf[len]; buf < end; buf++) *buf = endianswap(*buf); }\ntemplate<class T> inline T endiansame(T n) { return n; }\ntemplate<class T> inline void endiansame(T *buf, int len) {}\ntemplate<class T> inline T lilswap(T n) { return islittleendian() ? n : endianswap(n); }\ntemplate<class T> inline void lilswap(T *buf, int len) { if(!islittleendian()) endianswap(buf, len); }\ntemplate<class T> inline T bigswap(T n) { return islittleendian() ? endianswap(n) : n; }\ntemplate<class T> inline void bigswap(T *buf, int len) { if(islittleendian()) endianswap(buf, len); }\n\n/* workaround for some C platforms that have these two functions as macros - not used anywhere */\n#ifdef getchar\n#undef getchar\n#endif\n#ifdef putchar\n#undef putchar\n#endif\n\nstruct stream\n{\n\tvirtual ~stream() {}\n\tvirtual void close() = 0;\n\tvirtual bool end() = 0;\n\tvirtual long tell() { return -1; }\n\tvirtual bool seek(long offset, int whence = SEEK_SET) { return false; }\n\tvirtual long size();\n\tvirtual size_t read(void *buf, size_t len) { return 0; }\n\tvirtual size_t write(const void *buf, size_t len) { return 0; }\n\tvirtual int getchar() { uchar c; return read(&c, 1) == 1 ? c : -1; }\n\tvirtual bool putchar(int n) { uchar c = n; return write(&c, 1) == 1; }\n\tvirtual bool getline(char *str, size_t len);\n\tvirtual bool putstring(const char *str) { size_t len = strlen(str); return write(str, len) == len; }\n\tvirtual bool putline(const char *str) { return putstring(str) && putchar('\\n'); }\n\tvirtual int printf(const char *fmt, ...) { return -1; }\n\n\ttemplate<class T> bool put(T n) { return write(&n, sizeof(n)) == sizeof(n); }\n\ttemplate<class T> bool putlil(T n) { return put<T>(lilswap(n)); }\n\ttemplate<class T> bool putbig(T n) { return put<T>(bigswap(n)); }\n\n\ttemplate<class T> T get() { T n; return read(&n, sizeof(n)) == sizeof(n) ? n : 0; }\n\ttemplate<class T> T getlil() { return lilswap(get<T>()); }\n\ttemplate<class T> T getbig() { return bigswap(get<T>()); }\n};\n\nlong stream::size()\n{\n\tlong pos = tell(), endpos;\n\tif(pos < 0 || !seek(0, SEEK_END)) return -1;\n\tendpos = tell();\n\treturn pos == endpos || seek(pos, SEEK_SET) ? endpos : -1;\n}\n\nbool stream::getline(char *str, size_t len)\n{\n\tloopi(len-1)\n\t{\n\t\tif(read(&str[i], 1) != 1) { str[i] = '\\0'; return i > 0; }\n\t\telse if(str[i] == '\\n') { str[i+1] = '\\0'; return true; }\n\t}\n\tif(len > 0) str[len-1] = '\\0';\n\treturn true;\n}\n\nstruct filestream : stream\n{\n\tFILE *file;\n\n\tfilestream() : file(NULL) {}\n\t~filestream() { close(); }\n\n\tbool open(const char *name, const char *mode)\n\t{\n\t\tif(file) return false;\n\t\tfile = fopen(name, mode);\n\t\treturn file!=NULL;\n\t}\n\n\tvoid close()\n\t{\n\t\tif(file) { fclose(file); file = NULL; }\n\t}\n\n\tbool end() { return feof(file)!=0; }\n\tlong tell() { return ftell(file); }\n\tbool seek(long offset, int whence) { return fseek(file, offset, whence) >= 0; }\n\tsize_t read(void *buf, size_t len) { return fread(buf, 1, len, file); }\n\tsize_t write(const void *buf, size_t len) { return fwrite(buf, 1, len, file); }\n\tint getchar() { return fgetc(file); }\n\tbool putchar(int c) { return fputc(c, file)!=EOF; }\n\tbool getline(char *str, int len) { return fgets(str, len, file)!=NULL; }\n\tbool putstring(const char *str) { return fputs(str, file)!=EOF; }\n\n\tint printf(const char *fmt, ...)\n\t{\n\t\tva_list v;\n\t\tva_start(v, fmt);\n\t\tint result = vfprintf(file, fmt, v);\n\t\tva_end(v);\n\t\treturn result;\n\t}\n};\n\nchar *path(char *s)\n{\n\tfor(char *curpart = s;;)\n\t{\n\t\tchar *endpart = strchr(curpart, '&');\n\t\tif(endpart) *endpart = '\\0';\n\t\tif(curpart[0]=='<')\n\t\t{\n\t\t\tchar *file = strrchr(curpart, '>');\n\t\t\tif(!file) return s;\n\t\t\tcurpart = file+1;\n\t\t}\n\t\tfor(char *t = curpart; (t = strpbrk(t, \"/\\\\\")); *t++ = PATHDIV);\n\t\tfor(char *prevdir = NULL, *curdir = s;;)\n\t\t{\n\t\t\tprevdir = curdir[0]==PATHDIV ? curdir+1 : curdir;\n\t\t\tcurdir = strchr(prevdir, PATHDIV);\n\t\t\tif(!curdir) break;\n\t\t\tif(prevdir+1==curdir && prevdir[0]=='.')\n\t\t\t{\n\t\t\t\tmemmove(prevdir, curdir+1, strlen(curdir+1)+1);\n\t\t\t\tcurdir = prevdir;\n\t\t\t}\n\t\t\telse if(curdir[1]=='.' && curdir[2]=='.' && curdir[3]==PATHDIV)\n\t\t\t{\n\t\t\t\tif(prevdir+2==curdir && prevdir[0]=='.' && prevdir[1]=='.') continue;\n\t\t\t\tmemmove(prevdir, curdir+4, strlen(curdir+4)+1);\n\t\t\t\tcurdir = prevdir;\n\t\t\t}\n\t\t}\n\t\tif(endpart)\n\t\t{\n\t\t\t*endpart = '&';\n\t\t\tcurpart = endpart+1;\n\t\t}\n\t\telse break;\n\t}\n\treturn s;\n}\n\nchar *path(const char *s, bool copy)\n{\n\tstatic string tmp;\n\tcopystring(tmp, s);\n\tpath(tmp);\n\treturn tmp;\n}\n\nconst char *parentdir(const char *directory)\n{\n\tconst char *p = directory + strlen(directory);\n\twhile(p > directory && *p != '/' && *p != '\\\\') p--;\n\tstatic string parent;\n\tsize_t len = p-directory+1;\n\tcopystring(parent, directory, len);\n\treturn parent;\n}\n\nstream *openfile(const char *filename, const char *mode)\n{\n\tfilestream *file = new filestream;\n\tif(!file->open(path(filename, true), mode)) { delete file; return NULL; }\n\treturn file;\n}\n\nstruct Vec4;\n\nstruct Vec3\n{\n\tunion\n\t{\n\t\tstruct { double x, y, z; };\n\t\tdouble v[3];\n\t\tuint h[3*sizeof(double)/sizeof(uint)];\n\t};\n\n\tVec3() {}\n\tVec3(double x, double y, double z) : x(x), y(y), z(z) {}\n\texplicit Vec3(const double *v) : x(v[0]), y(v[1]), z(v[2]) {}\n\texplicit Vec3(const float *v) : x(v[0]), y(v[1]), z(v[2]) {}\n\texplicit Vec3(const Vec4 &v);\n\n\tdouble &operator[](int i) { return v[i]; }\n\tdouble operator[](int i) const { return v[i]; }\n\n\tbool operator==(const Vec3 &o) const { return x == o.x && y == o.y && z == o.z; }\n\tbool operator!=(const Vec3 &o) const { return x != o.x || y != o.y || z != o.z; }\n\tbool operator<(const Vec3 &o) const { return x < o.x || y < o.y || z < o.z; }\n\tbool operator>(const Vec3 &o) const { return x > o.x || y > o.y || z > o.z; }\n\n\tVec3 operator+(const Vec3 &o) const { return Vec3(x+o.x, y+o.y, z+o.z); }\n\tVec3 operator-(const Vec3 &o) const { return Vec3(x-o.x, y-o.y, z-o.z); }\n\tVec3 operator+(double k) const { return Vec3(x+k, y+k, z+k); }\n\tVec3 operator-(double k) const { return Vec3(x-k, y-k, z-k); }\n\tVec3 operator-() const { return Vec3(-x, -y, -z); }\n\tVec3 operator*(const Vec3 &o) const { return Vec3(x*o.x, y*o.y, z*o.z); }\n\tVec3 operator/(const Vec3 &o) const { return Vec3(x/o.x, y/o.y, z/o.z); }\n\tVec3 operator*(double k) const { return Vec3(x*k, y*k, z*k); }\n\tVec3 operator/(double k) const { return Vec3(x/k, y/k, z/k); }\n\n\tVec3 &operator+=(const Vec3 &o) { x += o.x; y += o.y; z += o.z; return *this; }\n\tVec3 &operator-=(const Vec3 &o) { x -= o.x; y -= o.y; z -= o.z; return *this; }\n\tVec3 &operator+=(double k) { x += k; y += k; z += k; return *this; }\n\tVec3 &operator-=(double k) { x -= k; y -= k; z -= k; return *this; }\n\tVec3 &operator*=(const Vec3 &o) { x *= o.x; y *= o.y; z *= o.z; return *this; }\n\tVec3 &operator/=(const Vec3 &o) { x /= o.x; y /= o.y; z /= o.z; return *this; }\n\tVec3 &operator*=(double k) { x *= k; y *= k; z *= k; return *this; }\n\tVec3 &operator/=(double k) { x /= k; y /= k; z /= k; return *this; }\n\n\tdouble dot(const Vec3 &o) const { return x*o.x + y*o.y + z*o.z; }\n\tdouble magnitude() const { return sqrt(dot(*this)); }\n\tdouble squaredlen() const { return dot(*this); }\n\tdouble dist(const Vec3 &o) const { return (*this - o).magnitude(); }\n\tVec3 normalize() const { return *this * (1.0 / magnitude()); }\n\tVec3 cross(const Vec3 &o) const { return Vec3(y*o.z-z*o.y, z*o.x-x*o.z, x*o.y-y*o.x); }\n\tVec3 reflect(const Vec3 &n) const { return *this - n*2.0*dot(n); }\n\tVec3 project(const Vec3 &n) const { return *this - n*dot(n); }\n\n\tVec3 zxy() const { return Vec3(z, x, y); }\n\tVec3 zyx() const { return Vec3(z, y, x); }\n\tVec3 yxz() const { return Vec3(y, x, z); }\n\tVec3 yzx() const { return Vec3(y, z, x); }\n\tVec3 xzy() const { return Vec3(x, z, y); }\n};\n\nstatic inline bool htcmp(const Vec3 &x, const Vec3 &y)\n{\n\treturn x == y;\n}\n\nstatic inline uint hthash(const Vec3 &k)\n{\n\tuint hash = k.h[0];\n\tfor(size_t i = 1; i < sizeof(k.h)/sizeof(uint); i++) hash ^= k.h[i];\n\treturn hash;\n}\n\nstruct Vec4\n{\n\tunion\n\t{\n\t\tstruct { double x, y, z, w; };\n\t\tdouble v[4];\n\t\tuint h[4*sizeof(double)/sizeof(uint)];\n\t};\n\n\tVec4() {}\n\tVec4(double x, double y, double z, double w) : x(x), y(y), z(z), w(w) {}\n\texplicit Vec4(const Vec3 &p, double w = 0) : x(p.x), y(p.y), z(p.z), w(w) {}\n\texplicit Vec4(const double *v) : x(v[0]), y(v[1]), z(v[2]), w(v[3]) {}\n\texplicit Vec4(const float *v) : x(v[0]), y(v[1]), z(v[2]), w(v[3]) {}\n\n\tdouble &operator[](int i)       { return v[i]; }\n\tdouble  operator[](int i) const { return v[i]; }\n\n\tbool operator==(const Vec4 &o) const { return x == o.x && y == o.y && z == o.z && w == o.w; }\n\tbool operator!=(const Vec4 &o) const { return x != o.x || y != o.y || z != o.z || w != o.w; }\n\tbool operator<(const Vec4 &o) const { return x < o.x || y < o.y || z < o.z || w < o.w; }\n\tbool operator>(const Vec4 &o) const { return x > o.x || y > o.y || z > o.z || w > o.w; }\n\n\tVec4 operator+(const Vec4 &o) const { return Vec4(x+o.x, y+o.y, z+o.z, w+o.w); }\n\tVec4 operator-(const Vec4 &o) const { return Vec4(x-o.x, y-o.y, z-o.z, w-o.w); }\n\tVec4 operator+(double k) const { return Vec4(x+k, y+k, z+k, w+k); }\n\tVec4 operator-(double k) const { return Vec4(x-k, y-k, z-k, w-k); }\n\tVec4 operator-() const { return Vec4(-x, -y, -z, -w); }\n\tVec4 operator*(double k) const { return Vec4(x*k, y*k, z*k, w*k); }\n\tVec4 operator/(double k) const { return Vec4(x/k, y/k, z/k, w/k); }\n\tVec4 addw(double f) const { return Vec4(x, y, z, w + f); }\n\n\tVec4 &operator+=(const Vec4 &o) { x += o.x; y += o.y; z += o.z; w += o.w; return *this; }\n\tVec4 &operator+=(const Vec3 &o) { x += o.x; y += o.y; z += o.z; return * this; }\n\tVec4 &operator-=(const Vec4 &o) { x -= o.x; y -= o.y; z -= o.z; w -= o.w; return *this; }\n\tVec4 &operator-=(const Vec3 &o) { x -= o.x; y -= o.y; z -= o.z; return * this; }\n\tVec4 &operator+=(double k) { x += k; y += k; z += k; w += k; return *this; }\n\tVec4 &operator-=(double k) { x -= k; y -= k; z -= k; w -= k; return *this; }\n\tVec4 &operator*=(double k) { x *= k; y *= k; z *= k; w *= k; return *this; }\n\tVec4 &operator/=(double k) { x /= k; y /= k; z /= k; w /= k; return *this; }\n\n\tdouble dot3(const Vec4 &o) const { return x*o.x + y*o.y + z*o.z; }\n\tdouble dot3(const Vec3 &o) const { return x*o.x + y*o.y + z*o.z; }\n\tdouble dot(const Vec4 &o) const { return dot3(o) + w*o.w; }\n\tdouble dot(const Vec3 &o) const  { return x*o.x + y*o.y + z*o.z + w; }\n\tdouble magnitude() const  { return sqrt(dot(*this)); }\n\tdouble magnitude3() const { return sqrt(dot3(*this)); }\n\tVec4 normalize() const { return *this * (1.0 / magnitude()); }\n\tVec3 cross3(const Vec4 &o) const { return Vec3(y*o.z-z*o.y, z*o.x-x*o.z, x*o.y-y*o.x); }\n\tVec3 cross3(const Vec3 &o) const { return Vec3(y*o.z-z*o.y, z*o.x-x*o.z, x*o.y-y*o.x); }\n\n\tvoid setxyz(const Vec3 &o) { x = o.x; y = o.y; z = o.z; }\n};\n\ninline Vec3::Vec3(const Vec4 &v) : x(v.x), y(v.y), z(v.z) {}\n\nstatic inline bool htcmp(const Vec4 &x, const Vec4 &y)\n{\n\treturn x == y;\n}\n\nstatic inline uint hthash(const Vec4 &k)\n{\n\tuint hash = k.h[0];\n\tfor(size_t i = 1; i < sizeof(k.h)/sizeof(uint); i++) hash ^= k.h[i];\n\treturn hash;\n}\n\nstruct Matrix3x3;\nstruct Matrix3x4;\n\nstruct Quat : Vec4\n{\n\tQuat() {}\n\tQuat(double x, double y, double z, double w) : Vec4(x, y, z, w) {}\n\tQuat(const float *ptr) : Vec4(ptr) {}\n\tQuat(double angle, const Vec3 &axis)\n\t{\n\t\tdouble s = sin(0.5*angle);\n\t\tx = s*axis.x;\n\t\ty = s*axis.y;\n\t\tz = s*axis.z;\n\t\tw = cos(0.5*angle);\n\t}\n\texplicit Quat(const Vec3 &v) : Vec4(v.x, v.y, v.z, -sqrt(max(1.0 - v.squaredlen(), 0.0))) {}\n\texplicit Quat(const Matrix3x3 &m) { convertmatrix(m); }\n\texplicit Quat(const Matrix3x4 &m) { convertmatrix(m); }\n\n\tvoid restorew()\n\t{\n\t\tw = -sqrt(max(1.0 - dot3(*this), 0.0));\n\t}\n\n\tQuat operator*(const Quat &o) const\n\t{\n\t\treturn Quat(w*o.x + x*o.w + y*o.z - z*o.y,\n\t\t\t\t\tw*o.y - x*o.z + y*o.w + z*o.x,\n\t\t\t\t\tw*o.z + x*o.y - y*o.x + z*o.w,\n\t\t\t\t\tw*o.w - x*o.x - y*o.y - z*o.z);\n\t}\n\tQuat &operator*=(const Quat &o) { return (*this = *this * o); }\n\n\tQuat operator+(const Vec4 &o) const { return Quat(x+o.x, y+o.y, z+o.z, w+o.w); }\n\tQuat &operator+=(const Vec4 &o) { return (*this = *this + o); }\n\tQuat operator-(const Vec4 &o) const { return Quat(x-o.x, y-o.y, z-o.z, w-o.w); }\n\tQuat &operator-=(const Vec4 &o) { return (*this = *this - o); }\n\n\tQuat operator-() const { return Quat(-x, -y, -z, w); }\n\n\tvoid flip() { x = -x; y = -y; z = -z; w = -w; }\n\n\tVec3 transform(const Vec3 &p) const\n\t{\n\t\treturn p + cross3(cross3(p) + p*w)*2.0;\n\t}\n\n\ttemplate<class M>\n\tvoid convertmatrix(const M &m)\n\t{\n\t\tdouble trace = m.a.x + m.b.y + m.c.z;\n\t\tif(trace>0)\n\t\t{\n\t\t\tdouble r = sqrt(1 + trace), inv = 0.5/r;\n\t\t\tw = 0.5*r;\n\t\t\tx = (m.c.y - m.b.z)*inv;\n\t\t\ty = (m.a.z - m.c.x)*inv;\n\t\t\tz = (m.b.x - m.a.y)*inv;\n\t\t}\n\t\telse if(m.a.x > m.b.y && m.a.x > m.c.z)\n\t\t{\n\t\t\tdouble r = sqrt(1 + m.a.x - m.b.y - m.c.z), inv = 0.5/r;\n\t\t\tx = 0.5*r;\n\t\t\ty = (m.b.x + m.a.y)*inv;\n\t\t\tz = (m.a.z + m.c.x)*inv;\n\t\t\tw = (m.c.y - m.b.z)*inv;\n\t\t}\n\t\telse if(m.b.y > m.c.z)\n\t\t{\n\t\t\tdouble r = sqrt(1 + m.b.y - m.a.x - m.c.z), inv = 0.5/r;\n\t\t\tx = (m.b.x + m.a.y)*inv;\n\t\t\ty = 0.5*r;\n\t\t\tz = (m.c.y + m.b.z)*inv;\n\t\t\tw = (m.a.z - m.c.x)*inv;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tdouble r = sqrt(1 + m.c.z - m.a.x - m.b.y), inv = 0.5/r;\n\t\t\tx = (m.a.z + m.c.x)*inv;\n\t\t\ty = (m.c.y + m.b.z)*inv;\n\t\t\tz = 0.5*r;\n\t\t\tw = (m.b.x - m.a.y)*inv;\n\t\t}\n\t}\n\n\tstatic Quat fromangles(const Vec3 &rot)\n\t{\n\t\tdouble cx = cos(rot.x/2), sx = sin(rot.x/2),\n\t\t\t   cy = cos(rot.y/2), sy = sin(rot.y/2),\n\t\t\t   cz = cos(rot.z/2), sz = sin(rot.z/2);\n\t\tQuat q(sx*cy*cz - cx*sy*sz,\n\t\t\t   cx*sy*cz + sx*cy*sz,\n\t\t\t   cx*cy*sz - sx*sy*cz,\n\t\t\t   cx*cy*cz + sx*sy*sz);\n\t\tif(q.w > 0) q.flip();\n\t\treturn q;\n\t}\n\n\tstatic Quat fromdegrees(const Vec3 &rot) { return fromangles(rot * (M_PI / 180)); }\n};\n\nstruct Matrix3x3\n{\n\tVec3 a, b, c;\n\n\tMatrix3x3() {}\n\tMatrix3x3(const Vec3 &a, const Vec3 &b, const Vec3 &c) : a(a), b(b), c(c) {}\n\texplicit Matrix3x3(const Quat &q) { convertquat(q); }\n\texplicit Matrix3x3(const Quat &q, const Vec3 &scale)\n\t{\n\t\tconvertquat(q);\n\t\ta *= scale;\n\t\tb *= scale;\n\t\tc *= scale;\n\t}\n\n\tvoid convertquat(const Quat &q)\n\t{\n\t\tdouble x = q.x, y = q.y, z = q.z, w = q.w,\n\t\t\t   tx = 2*x, ty = 2*y, tz = 2*z,\n\t\t\t   txx = tx*x, tyy = ty*y, tzz = tz*z,\n\t\t\t   txy = tx*y, txz = tx*z, tyz = ty*z,\n\t\t\t   twx = w*tx, twy = w*ty, twz = w*tz;\n\t\ta = Vec3(1 - (tyy + tzz), txy - twz, txz + twy);\n\t\tb = Vec3(txy + twz, 1 - (txx + tzz), tyz - twx);\n\t\tc = Vec3(txz - twy, tyz + twx, 1 - (txx + tyy));\n\t}\n\n\tMatrix3x3 operator*(const Matrix3x3 &o) const\n\t{\n\t\treturn Matrix3x3(\n\t\t\to.a*a.x + o.b*a.y + o.c*a.z,\n\t\t\to.a*b.x + o.b*b.y + o.c*b.z,\n\t\t\to.a*c.x + o.b*c.y + o.c*c.z);\n\t}\n\tMatrix3x3 &operator*=(const Matrix3x3 &o) { return (*this = *this * o); }\n\n\tvoid transpose(const Matrix3x3 &o)\n\t{\n\t\ta = Vec3(o.a.x, o.b.x, o.c.x);\n\t\tb = Vec3(o.a.y, o.b.y, o.c.y);\n\t\tc = Vec3(o.a.z, o.b.z, o.c.z);\n\t}\n\tvoid transpose() { transpose(Matrix3x3(*this)); }\n\n\tVec3 transform(const Vec3 &o) const { return Vec3(a.dot(o), b.dot(o), c.dot(o)); }\n\n\tfloat determinant()\n\t{\n\t\treturn\n\t\t\ta.x * b.y * c.z +\n\t\t\ta.y * b.z * c.x +\n\t\t\ta.z * b.x * c.y -\n\t\t\ta.z * b.y * c.x -\n\t\t\ta.y * b.x * c.z -\n\t\t\ta.x * b.z * c.y;\n\t}\n};\n\nstruct Matrix3x4\n{\n\tVec4 a, b, c;\n\n\tMatrix3x4() {}\n\tMatrix3x4(const Vec4 &a, const Vec4 &b, const Vec4 &c) : a(a), b(b), c(c) {}\n\texplicit Matrix3x4(const Matrix3x3 &rot, const Vec3 &trans)\n\t\t: a(Vec4(rot.a, trans.x)), b(Vec4(rot.b, trans.y)), c(Vec4(rot.c, trans.z))\n\t{\n\t}\n\texplicit Matrix3x4(const Quat &rot, const Vec3 &trans)\n\t{\n\t\t*this = Matrix3x4(Matrix3x3(rot), trans);\n\t}\n\texplicit Matrix3x4(const Quat &rot, const Vec3 &trans, const Vec3 &scale)\n\t{\n\t\t*this = Matrix3x4(Matrix3x3(rot, scale), trans);\n\t}\n\n\tMatrix3x4 operator*(float k) const { return Matrix3x4(*this) *= k; }\n\tMatrix3x4 &operator*=(float k)\n\t{\n\t\ta *= k;\n\t\tb *= k;\n\t\tc *= k;\n\t\treturn *this;\n\t}\n\n\tMatrix3x4 operator+(const Matrix3x4 &o) const { return Matrix3x4(*this) += o; }\n\tMatrix3x4 &operator+=(const Matrix3x4 &o)\n\t{\n\t\ta += o.a;\n\t\tb += o.b;\n\t\tc += o.c;\n\t\treturn *this;\n\t}\n\tMatrix3x4 operator+(const Vec3 &o) const { return Matrix3x4(*this) += o; }\n\tMatrix3x4 &operator+=(const Vec3 &o)\n\t{\n\t\ta[3] += o[0];\n\t\tb[3] += o[1];\n\t\tc[3] += o[2];\n\t\treturn *this;\n\t}\n\n\tvoid invert(const Matrix3x4 &o)\n\t{\n\t\tMatrix3x3 invrot(Vec3(o.a.x, o.b.x, o.c.x), Vec3(o.a.y, o.b.y, o.c.y), Vec3(o.a.z, o.b.z, o.c.z));\n\t\tinvrot.a /= invrot.a.squaredlen();\n\t\tinvrot.b /= invrot.b.squaredlen();\n\t\tinvrot.c /= invrot.c.squaredlen();\n\t\tVec3 trans(o.a.w, o.b.w, o.c.w);\n\t\ta = Vec4(invrot.a, -invrot.a.dot(trans));\n\t\tb = Vec4(invrot.b, -invrot.b.dot(trans));\n\t\tc = Vec4(invrot.c, -invrot.c.dot(trans));\n\t}\n\tvoid invert() { invert(Matrix3x4(*this)); }\n\n\tMatrix3x4 operator*(const Matrix3x4 &o) const\n\t{\n\t\treturn Matrix3x4(\n\t\t\t(o.a*a.x + o.b*a.y + o.c*a.z).addw(a.w),\n\t\t\t(o.a*b.x + o.b*b.y + o.c*b.z).addw(b.w),\n\t\t\t(o.a*c.x + o.b*c.y + o.c*c.z).addw(c.w));\n\t}\n\tMatrix3x4 &operator*=(const Matrix3x4 &o) { return (*this = *this * o); }\n\n\tVec3 transform(const Vec3 &o) const { return Vec3(a.dot(o), b.dot(o), c.dot(o)); }\n\tVec3 transform3(const Vec3 &o) const { return Vec3(a.dot3(o), b.dot3(o), c.dot3(o)); }\n};\n\nvoid conoutf(const char *s, ...)\n{\n\tdefvformatstring(msg,s,s);\n\tprintf(\"%s\\n\", msg);\n}\n\nvoid fatal(const char *s, ...)    // failure exit\n{\n\tdefvformatstring(msg,s,s);\n\tfprintf(stderr, \"%s\\n\", msg);\n\n\texit(EXIT_FAILURE);\n}\n\n//\n// According to IQM file spec, all field offsets must be 4-byte aligned.\n// Given a desired destination pointer to write data to, add pad bytes\n// to ensure 4-byte alignment. \n// \nunsigned int pad_field_ofs(unsigned int field_ofs) \n{\n\treturn (field_ofs - 1) + 4 - ((field_ofs - 1) % 4);\n}"
  },
  {
    "path": "plugins/Makefile",
    "content": "\n#windows is special as always, but we don't support itanium, and microsoft don't support anything else (not even arm with the nt win32 api)\nifeq ($(FTE_TARGET),win32)\n\tPLUG_NATIVE_EXT=_x86.dll\n\tPLUG_LDFLAGS= -static-libgcc\n\tPLUG_LDFLAGS_ZLIB=-L../engine/libs/mingw-libs -lz\n\tBITS=32\n\tPLUG_LDFLAGS_DL=\n\tCMAKERULES=$(OUT_DIR)/toolchain_$(FTE_TARGET).cmake\nendif\n\nifeq ($(FTE_TARGET),win64)\n\tPLUG_NATIVE_EXT=_x64.dll\n\tPLUG_LDFLAGS=-Wl,--support-old-code -static-libgcc\n\tPLUG_LDFLAGS_ZLIB=-L../engine/libs/mingw64-libs -lz\n\tBITS=64\n\tPLUG_LDFLAGS_DL=\n\tCMAKERULES=$(OUT_DIR)/toolchain_$(FTE_TARGET).cmake\nendif\n\nPLUG_PREFIX=$(OUT_DIR)/fteplug_\n\nifeq ($(FTE_TARGET),bsd)\n\tPLUG_LDFLAGS_DL?=-ldl -lc\nelse\n\tPLUG_LDFLAGS_DL?=-ldl -static-libgcc\nendif\n\nPLUG_LDFLAGS?=-L/usr/local/lib -Wl,-R/usr/local/lib -lm\nPLUG_LDFLAGS_ZLIB?=-lz\n\nifneq ($(PLUG_NATIVE_EXT),)\n\t#if we're on windows, we'll put our windows-specific hacks here.\n\tPLUG_DEFFILE=plugin.def\n\tPLUG_CFLAGS=\n\tPLUG_CXXFLAGS=\nendif\n\n#cygwin uses dll naming.\nifeq ($(FTE_TARGET),cygwin)\n\tifeq ($(BITS),64)\n\t\tPLUG_DEFFILE=plugin.def\n\t\tPLUG_NATIVE_EXT=_amd64.dll\n\tendif\n\tifneq ($(BITS),64)\n\t\tPLUG_DEFFILE=plugin.def\n\t\tPLUG_NATIVE_EXT=_x86.dll\n\tendif\nendif\n\n#if they're not on windows, we'll try asking the compiler directly\n#the check to see if its already set is to avoid asking msvc, which would probably break things.\nifeq ($(PLUG_NATIVE_EXT),)\n\tLIBRESOLV=-lresolv\n\tifneq ($(shell echo|$(CC) -E -dM -|grep __amd64__),)\n\t\t#either x32 or x64 ABIs\n\t\tifneq ($(shell echo|$(CC) -E -dM -|grep __ILP32__|grep 1),)\n\t\t\tPLUG_NATIVE_EXT=_x32.so\n\t\telse\n\t\t\tPLUG_NATIVE_EXT=_amd64.so\n\t\tendif\n\tendif\n\n\tifneq ($(shell echo|$(CC) -E -dM -|grep __i386__),)\n\t\tPLUG_NATIVE_EXT=_x86.so\n\tendif\n\n\tifneq ($(shell echo|$(CC) -E -dM -|grep __arm__),)\n\t\t#gnueabi[hf]\n\t\tifneq ($(shell echo|$(CC) -E -dM -|grep __SOFTFP__),)\n\t\t\tPLUG_NATIVE_EXT=_arm.so\n\t\telse\n\t\t\tPLUG_NATIVE_EXT=_armhf.so\n\t\tendif\n\tendif\n\n\tifneq ($(shell echo|$(CC) -E -dM -|grep __ppc__),)\n\t\tPLUG_NATIVE_EXT=_ppc.so\t\t#32bit big-endian\n\tendif\n\n\tifneq ($(shell echo|$(CC) -E -dM -|grep __ppc64__),)\n\t\tPLUG_NATIVE_EXT=_ppc64.so\t#64bit big-endian\n\tendif\n\n\tifneq ($(shell echo|$(CC) -E -dM -|grep __ppc64le__),)\n\t\tPLUG_NATIVE_EXT=_ppc64le.so\t#64bit little-endian.\n\tendif\nendif\n\nifeq ($(FTE_TARGET),droid)\n\t#plugins get written to the tmp build dir, to avoid conflicts\n\tPLUG_PREFIX=$(OUT_DIR)/m_droid-$(DROID_ARCH)/libplug_\n\t#don't bother with cpu arch postfixes. they'll be in separate directories anyway.\n\tPLUG_NATIVE_EXT=.so\n\t#libresolv has no public api on android...\n\tLIBRESOLV=\n\t#so we know our target.\n\tPLUG_CFLAGS=-DANDROID\n\tPLUG_CXXFLAGS=-DANDROID\nendif\nARCHLIBS=../engine/libs-$(ARCH)\n\n#fallback\nPLUG_NATIVE_EXT?=.so\n\nPLUG_DEFFILE?=\nPLUG_CFLAGS?=-fPIC -Wl,--no-undefined -Bsymbolic -fvisibility=hidden -static-libgcc\nPLUG_CXXFLAGS?=-fPIC -Wl,--no-undefined -Bsymbolic -fvisibility=hidden -static-libgcc\nPLUG_CMAKE?=-DCMAKE_POSITION_INDEPENDENT_CODE=ON -DCMAKE_C_COMPILER=$(CC) -DCMAKE_CXX_COMPILER=$(CXX)\nPLUG_LDFLAGS_ZLIB?=\nARCH:=$(shell $(CC) -dumpmachine)\nPLUG_LDFLAGS:=-L$(ARCHLIBS) $(PLUG_LDFLAGS)\nPLUG_CFLAGS:=-I$(ARCHLIBS) $(PLUG_CFLAGS)\nPLUG_CXXFLAGS:=-I$(ARCHLIBS) $(PLUG_CXXFLAGS)\n\n#$1 is the plugin name, $2 is its filename, $3 is title, $4 is description\ndefine EMBEDMETA\n\t@echo \"{\\\\n\tpackage fteplug_$1\\\\n\tver \\\"${SVN_VERSION}\\\"\\\\n\tcategory Plugins\\\\n\ttitle \\\"$3\\\"\\\\n\tgamedir \\\"\\\"\\\\n\tdesc \\\"$4\\\"\\\\n}\" | zip -q -9 -fz- $2.metazip -\n\t@cat $2.metazip >> $2\n\t@zip -q -A $2\n\t@rm $2.metazip\nendef\n\n#legacy build rule, now equivelent to all\nnative: all\n\nclean: ezscript-clean qi-clean hud-clean irc-clean\n\n.PHONY: all native distclean clean\n\nhelp:\n\t@-echo make a subdirectory\n\n######################################\n#small script to download+install avformat for windows cross compiles.\n#linux users are expected to have the library installed locally already. If your version is too old or missing, run the following command to install it (to /usr/local), then delete the gz and directory.\n#wget http://ffmpeg.org/releases/ffmpeg-1.2.tar.gz && cd tar xvfz ffmpeg-1.2.tar.gz && cd ffmpeg-1.2/ && ./configure --disable-yasm --enable-shared && make && sudo make install\n#we use ffmpeg's version for some reason, as opposed to libav. not sure what the differences are meant to be, but libav seemed to have non-depricated functions defined, docs that say to use them, and these functions missing.\n\nAV_VER=ffmpeg-4.0\n\nifeq ($(findstring win,$(FTE_TARGET)),win)\n\tAV_BASE=$(abspath $(OUT_DIR)/../fte_libav_$(AV_VER))/\nelse\n\tifeq ($(FTE_TARGET),bsd)\n\t\tAV_BASE=/usr/local/include/\n\telse\n\t\tAV_BASE=/usr/include/ffmpeg/\n\tendif\nendif\n\nifneq ($(AV_BASE),)\n\tAV_DEP=$(AV_BASE)libavformat/avformat.h\n\tAV_CFLAGS=-I$(AV_BASE)\n\tAV_LDFLAGS=-L$(AV_BASE)lib$(BITS) -lavcodec -lavformat -lavutil -lswscale\nelse\n\tAV_LDFLAGS=-lavcodec -lavformat -lavutil -lswscale\nendif\n\nifeq ($(FTE_TARGET),bsd)\n\tAV_LDFLAGS+=-lc\nendif\n\nAVPLUG_OBJS= avplug/avaudio.c avplug/avencode.c avplug/avdecode.c plugin.c\n\nifeq ($(findstring win,$(FTE_TARGET)),win)\n\tifeq (0,1)\n\t\tAV_ARCHIVEEXT=.z7\n\t\tAV_EXTRACT=7z e -y\n\telse\n\t\tAV_ARCHIVEEXT=.zip\n\t\tAV_EXTRACT=unzip -ju\n\tendif\n\n\tAV_W32_DEV=$(AV_VER)-win32-dev$(AV_ARCHIVEEXT)\n\tAV_W64_DEV=$(AV_VER)-win64-dev$(AV_ARCHIVEEXT)\n\tAV_W32_BIN=$(AV_VER)-win32-shared$(AV_ARCHIVEEXT)\n\tAV_W64_BIN=$(AV_VER)-win64-shared$(AV_ARCHIVEEXT)\n\tAV_URL32_DEV=https://archive.org/download/zeranoe/win32/dev/$(AV_W32_DEV)\n\tAV_URL64_DEV=https://archive.org/download/zeranoe/win64/dev/$(AV_W64_DEV)\n\tAV_URL32_BIN=https://archive.org/download/zeranoe/win32/shared/$(AV_W32_BIN)\n\tAV_URL64_BIN=https://archive.org/download/zeranoe/win64/shared/$(AV_W64_BIN)\n\tAV_PRE32_DEV=$(AV_VER)-win32-dev/\n\tAV_PRE64_DEV=$(AV_VER)-win64-dev/\n\tAV_PRE32_BIN=$(AV_VER)-win32-shared/\n\tAV_PRE64_BIN=$(AV_VER)-win64-shared/\n\n\tifeq ($(FTE_TARGET),win32)\n\t\tFFMPEG_ZIP=$(AV_BASE)/$(AV_VER)-x86.zip\n\t\t#NATIVE_PLUGINS+=ffmpeg\n\tendif\n\n\tifeq ($(FTE_TARGET),win64)\n\t\tFFMPEG_ZIP=$(AV_BASE)/$(AV_VER)-x64.zip\n\t\t#NATIVE_PLUGINS+=ffmpeg\n\tendif\n\n$(AV_BASE)$(AV_VER)-win32.zip:\n\tmkdir -p $(AV_BASE)\n\tcd $(AV_BASE) && wget -N $(AV_URL32_BIN)\n\tmkdir -p $(AV_BASE)bin32 && cd $(AV_BASE)bin32 && $(AV_EXTRACT) ../$(AV_W32_BIN) $(AV_PRE32_BIN)bin/avcodec-*.dll $(AV_PRE32_BIN)bin/avutil-*.dll $(AV_PRE32_BIN)bin/swresample-*.dll $(AV_PRE32_BIN)bin/avformat-*.dll $(AV_PRE32_BIN)bin/swscale-*.dll && cd -\n\tzip -j9 $@ $(AV_BASE)bin32/*.dll\n\n$(AV_BASE)$(AV_VER)-win64.zip:\n\tmkdir -p $(AV_BASE)\n\tcd $(AV_BASE) && wget -N $(AV_URL64_BIN)\n\tmkdir -p $(AV_BASE)bin64 && cd $(AV_BASE)bin64 && $(AV_EXTRACT) ../$(AV_W64_BIN) $(AV_PRE64_BIN)bin/avcodec-*.dll $(AV_PRE64_BIN)bin/avutil-*.dll $(AV_PRE64_BIN)bin/swresample-*.dll $(AV_PRE64_BIN)bin/avformat-*.dll $(AV_PRE64_BIN)bin/swscale-*.dll && cd -\n\tzip -j9 $@ $(AV_BASE)bin64/*.dll\n\n\tifneq ($(FFMPEG_ZIP),)\n\t$(FFMPEG_ZIP): $(AV_BASE)$(AV_VER)-$(FTE_TARGET).zip\n\t\tcp $(AV_BASE)$(AV_VER)-$(FTE_TARGET).zip $@\n\tendif\n\n$(AV_BASE)libavformat/avformat.h:\n\tmkdir -p $(AV_BASE)\n\tcd $(AV_BASE) && wget -N $(AV_URL32_DEV)\n\tmkdir -p $(AV_BASE)libavformat && cd $(AV_BASE)libavformat && $(AV_EXTRACT) ../$(AV_W32_DEV) $(AV_PRE32_DEV)include/libavformat/* && cd -\n\tmkdir -p $(AV_BASE)libavcodec  && cd $(AV_BASE)libavcodec  && $(AV_EXTRACT) ../$(AV_W32_DEV) $(AV_PRE32_DEV)include/libavcodec/*  && cd -\n\tmkdir -p $(AV_BASE)libavutil   && cd $(AV_BASE)libavutil   && $(AV_EXTRACT) ../$(AV_W32_DEV) $(AV_PRE32_DEV)include/libavutil/*   && cd -\n\tmkdir -p $(AV_BASE)libswscale  && cd $(AV_BASE)libswscale  && $(AV_EXTRACT) ../$(AV_W32_DEV) $(AV_PRE32_DEV)include/libswscale/*  && cd -\n\tmkdir -p $(AV_BASE)lib32 && cd $(AV_BASE)lib32 && $(AV_EXTRACT) ../$(AV_W32_DEV) $(AV_PRE32_DEV)lib/avformat.lib $(AV_PRE32_DEV)lib/avcodec.lib $(AV_PRE32_DEV)lib/avutil.lib $(AV_PRE32_DEV)lib/swscale.lib && cd -\n\tcd $(AV_BASE) && wget -N $(AV_URL64_DEV)\n\tmkdir -p $(AV_BASE)lib64 && cd $(AV_BASE)lib64 && $(AV_EXTRACT) ../$(AV_W64_DEV) $(AV_PRE64_DEV)lib/avformat.lib $(AV_PRE64_DEV)lib/avcodec.lib $(AV_PRE64_DEV)lib/avutil.lib $(AV_PRE64_DEV)lib/swscale.lib && cd -\n\ndistclean:\n\trm $(AV_BASE)libavformat/avformat.h\n\trm $(AV_BASE)$(AV_VER)-win32.zip\n\trm $(AV_BASE)$(AV_VER)-win64.zip\n\n$(PLUG_PREFIX)ffmpeg$(PLUG_NATIVE_EXT): $(AV_DEP) $(AVPLUG_OBJS)\n\t$(CC) $(BASE_CFLAGS) $(CFLAGS) -DFTEPLUGIN -s -o $@ -shared $(PLUG_CFLAGS) $(AV_CFLAGS) $(AVPLUG_OBJS) $(PLUG_DEFFILE) $(PLUG_LDFLAGS) $(AV_LDFLAGS)\n\t$(call EMBEDMETA,ffmpeg,$@,FFMPEG Video Decoding Plugin,Provides support for more audio formats as well as video playback and better capture support.)\n\n\nelse # NIX\n$(PLUG_PREFIX)ffmpeg$(PLUG_NATIVE_EXT):\n\t$(CC) $(BASE_CFLAGS) $(CFLAGS) -DFTEPLUGIN -s -o $@ -shared $(PLUG_CFLAGS) $(AV_CFLAGS) $(AVPLUG_OBJS) $(PLUG_DEFFILE) $(PLUG_LDFLAGS) $(AV_LDFLAGS)\n\t$(call EMBEDMETA,ffmpeg,$@,FFMPEG Video Decoding Plugin,Provides support for more audio formats as well as video playback and better capture support.)\nendif\nNATIVE_PLUGINS+=ffmpeg\n######################################\n\n######################################\n#small script for ode\n#FIXME: ode fails to compile under cygwin\n#FIXME: race condition if you try compiling debug+release at the same time, as this makefile is invoked twice by the engine's one\nODE_ARCH=$(FTE_TARGET)\nifeq ($(ODE_ARCH),)\n  ODE_ARCH=unknown\nendif\nODE_VER=0.16.5\nODE_URL=https://bitbucket.org/odedevs/ode/downloads/ode-$(ODE_VER).tar.gz\nODE_BASE=$(ARCHLIBS)/ode-$(ODE_VER)_$(ODE_ARCH)/\nODE_LIB=$(ODE_BASE)ode-$(ODE_VER)/ode/src/.libs/libode.a\n$(OUT_DIR)/../ode-$(ODE_VER).tar.gz:\n\tmkdir -p $(ODE_BASE)\n\tcd $(OUT_DIR)/.. && wget -N $(ODE_URL)\n$(ODE_LIB): $(OUT_DIR)/../ode-$(ODE_VER).tar.gz\n\tmkdir -p $(ODE_BASE) && cd $(ODE_BASE) && tar xvfz $<\n\tcd $(ODE_BASE)ode-$(ODE_VER)/ && ./bootstrap && ./configure --enable-double-precision --disable-demos --without-x --with-pic CC=\"$(CC) $(PLUG_CXXFLAGS)\" CXX=\"$(CC) $(PLUG_CXXFLAGS)\" --host=`$(CC) -dumpmachine` && $(MAKE)\n\nODE_FILES=../engine/common/com_phys_ode.c ../engine/common/mathlib.c plugin.c $(ODE_LIB)\n$(PLUG_PREFIX)ode$(PLUG_NATIVE_EXT): $(ODE_FILES)\n\t$(CC) -flto -s $(BASE_CFLAGS) $(CFLAGS) -Os -DFTEPLUGIN -DODE_STATIC -o $@ -shared $(PLUG_CFLAGS) -I$(ODE_BASE)ode-$(ODE_VER)/include $(ODE_FILES) $(PLUG_DEFFILE) $(PLUG_LDFLAGS) -static-libgcc `$(CC) -print-file-name=libstdc++.a` -lpthread\n\t$(call EMBEDMETA,ode,$@,ODE Physics,Provides Rigid Body Physics behaviours.)\n#NATIVE_PLUGINS+=ode\n######################################\n\n######################################\nifneq ($(CMAKERULES),)\nBULLET_CFLAGS+=-static-libstdc++\nPLUG_CMAKE+= -DCMAKE_TOOLCHAIN_FILE=\"$(CMAKERULES)\" \n$(CMAKERULES):\n\techo \"set(CMAKE_SYSTEM_NAME Windows)\" > $@\n\techo \"set(TOOLCHAIN_PREFIX `$(CC) -dumpmachine`)\" >> $@\n\t# cross compilers to use for C, C++ and Fortran\n\techo \"set(CMAKE_C_COMPILER $(CC))\" >> $@\n\techo \"set(CMAKE_CXX_COMPILER $(CXX))\" >> $@\n\techo \"set(CMAKE_RC_COMPILER $${TOOLCHAIN_PREFIX}-windres)\" >> $@\n\t# target environment on the build host system\n\techo \"set(CMAKE_FIND_ROOT_PATH /usr/$${TOOLCHAIN_PREFIX})\" >> $@\n\t# modify default behavior of FIND_XXX() commands\n\techo \"set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)\" >> $@\n\techo \"set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)\" >> $@\n\techo \"set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)\" >> $@\nendif\n\nBULLET_VER=2.87\nBULLET_URL=https://github.com/bulletphysics/bullet3/archive/$(BULLET_VER).tar.gz\nBULLET_BASE=$(OUT_DIR)/../bullet3-$(BULLET_VER)_$(FTE_TARGET)/\nBULLET_LIBS=\t\\\n\t$(BULLET_BASE)bullet3-$(BULLET_VER)/lib/libBulletDynamics.a\t\t\\\n\t$(BULLET_BASE)bullet3-$(BULLET_VER)/lib/libBulletCollision.a\t\\\n\t$(BULLET_BASE)bullet3-$(BULLET_VER)/lib/libLinearMath.a\nBULLET_CFLAGS+=-I$(BULLET_BASE)bullet3-$(BULLET_VER)/src\n$(OUT_DIR)/../bullet3-$(BULLET_VER).tar.gz:\n\tmkdir -p $(BULLET_BASE)\n\twget -N $(BULLET_URL) -O $@\n\nBULLET_LIB=$(BULLET_BASE)bullet3-$(BULLET_VER)/lib/libLinearMath.a\n$(BULLET_BASE)bullet3-$(BULLET_VER)/lib/libBulletDynamics.a $(BULLET_BASE)bullet3-$(BULLET_VER)/lib/libBulletCollision.a: $(BULLET_LIB)\n$(BULLET_LIB): $(OUT_DIR)/../bullet3-$(BULLET_VER).tar.gz $(CMAKERULES)\n\tmkdir -p $(BULLET_BASE) && cd $(BULLET_BASE) && tar xvfz $<\n\trm $(BULLET_BASE)bullet3-$(BULLET_VER)/build3/cmake/FindPythonLibs.cmake\t#cmake is a pile of shite and fails at cross compiling. oh well, we didn't want any python stuff anyway.\n\tcd $(BULLET_BASE)bullet3-$(BULLET_VER)/ && cmake $(PLUG_CMAKE) -DBUILD_PYBULLET:BOOL=OFF -DBUILD_DEMOS:BOOL=OFF -DBUILD_EXTRAS:BOOL=OFF -DLIBRARY_OUTPUT_PATH=$(BULLET_BASE)bullet3-$(BULLET_VER)/lib . && $(MAKE) LinearMath BulletDynamics BulletCollision\n#./configure --enable-double-precision --disable-demos --without-x CXX=\"$(CC)\" CFLAGS=\"$(PLUG_CFLAGS)\" CXXFLAGS=\"$(PLUG_CXXFLAGS)\" --host=`$(CC) -dumpmachine` && make\n\n\n$(PLUG_PREFIX)bullet$(PLUG_NATIVE_EXT): bullet/bulletplug.cpp plugin.c $(BULLET_LIBS)\n\t$(CXX) $(BASE_CXXFLAGS) $(CFLAGS) -DFTEPLUGIN -o $@ -shared $(PLUG_CFLAGS) $^ $(PLUG_DEFFILE) $(PLUG_LDFLAGS) $(BULLET_CFLAGS)\n\t$(call EMBEDMETA,bullet,$@,Bullet Physics Plugin,Provides Rigid Body Physics.)\n#NATIVE_PLUGINS+=bullet\n######################################\n\n######################################\n#Regarding CEF Versions: Cef 2526 is reportedly the most recent _WORKING_ version of libcef. Later versions have screwed webgl etc, and are just generally unstable.\n#However, that's now impossible to get hold of since the old cefbuilds server went down. New builds are hosted by spotify and they're all randomly broken, so we might as well just use whatever seems fairly recent.\n\n#WARNING: Changing CEF_VER requires updating downloadables.php etc\nifeq ($(FTE_TARGET),win32)\n    CEF_ARCH=windows32\nendif\nifeq ($(FTE_TARGET),win64)\n    CEF_ARCH=windows64\nendif\nifeq ($(FTE_TARGET),linux32)\n    CEF_ARCH=linux32\nendif\nifeq ($(FTE_TARGET),linux64)\n    CEF_ARCH=linux64\nendif\n#ifeq ($(FTE_TARGET),macosx64)\n    #CEF_ARCH=macosx64\n#endif\nCEF_VER=95.7.14+g9f72f35+chromium-95.0.4638.69\nCEF_NAME=cef_binary_$(CEF_VER)_$(CEF_ARCH)_minimal\nCEF_URL=https://cef-builds.spotifycdn.com/cef_binary_$(CEF_VER)_$(CEF_ARCH)_minimal.tar.bz2\n\nifneq ($(CEF_ARCH),)\ncef/$(CEF_NAME)/include/cef_version.h:\n\tcd cef && wget -N $(CEF_URL)\n\tcd cef && tar -xjf $(CEF_NAME).tar.bz2\ncef/$(CEF_NAME)/rel.zip: cef/$(CEF_NAME)/include/cef_version.h\n\tcd cef/$(CEF_NAME)/Release && zip -9 ../rel.zip *.dll *.bin\n\tcd cef/$(CEF_NAME)/Resources && zip -r9 ../rel.zip .\n$(OUT_DIR)/cef_$(CEF_VER).zip: cef/$(CEF_NAME)/rel.zip\n\tcp cef/$(CEF_NAME)/rel.zip $@\n\nCEF_SOURCES=cef/cef.c plugin.c\n$(PLUG_PREFIX)cef$(PLUG_NATIVE_EXT): $(CEF_SOURCES) $(OUT_DIR)/cef_$(CEF_VER).zip cef/$(CEF_NAME)/include/cef_version.h\n\t$(CC) $(BASE_CFLAGS) $(CFLAGS) -DFTEPLUGIN -o $(PLUG_PREFIX)cef$(PLUG_NATIVE_EXT) -shared $(PLUG_CFLAGS) -Icef/$(CEF_NAME) $(CEF_SOURCES) $(PLUG_DEFFILE) $(PLUG_LDFLAGS) -Wl,-rpath,. $(PLUG_LDFLAGS_DL)\n\t$(call EMBEDMETA,cef,$@,libcef Browser Plugin,This plugin provides support for an in-game web browser.)\n#NATIVE_PLUGINS+=cef\nelse\n.PHONEY: $(PLUG_PREFIX)cef$(PLUG_NATIVE_EXT)\n$(PLUG_PREFIX)cef$(PLUG_NATIVE_EXT):\n\t@echo cef plugin not supported on this arch - $(FTE_TARGET) - $(CEF_ARCH)\nendif\n\n######################################\n#quake3\n\nBOTLIBFILES=quake3/botlib/be_aas_bspq3.c\t\t\\\n\t\t\tquake3/botlib/be_aas_cluster.c\t\\\n\t\t\tquake3/botlib/be_aas_debug.c\t\t\\\n\t\t\tquake3/botlib/be_aas_entity.c\t\\\n\t\t\tquake3/botlib/be_aas_file.c\t\t\\\n\t\t\tquake3/botlib/be_aas_main.c\t\t\\\n\t\t\tquake3/botlib/be_aas_move.c\t\t\\\n\t\t\tquake3/botlib/be_aas_optimize.c\t\\\n\t\t\tquake3/botlib/be_aas_reach.c\t\t\\\n\t\t\tquake3/botlib/be_aas_routealt.c\t\\\n\t\t\tquake3/botlib/be_aas_route.c\t\t\\\n\t\t\tquake3/botlib/be_aas_sample.c\t\\\n\t\t\tquake3/botlib/be_ai_char.c\t\t\\\n\t\t\tquake3/botlib/be_ai_chat.c\t\t\\\n\t\t\tquake3/botlib/be_ai_gen.c\t\\\n\t\t\tquake3/botlib/be_ai_goal.c\t\\\n\t\t\tquake3/botlib/be_ai_move.c\t\\\n\t\t\tquake3/botlib/be_ai_weap.c\t\\\n\t\t\tquake3/botlib/be_ai_weight.c\t\\\n\t\t\tquake3/botlib/be_ea.c\t\t\\\n\t\t\tquake3/botlib/be_interface.c\t\\\n\t\t\tquake3/botlib/l_crc.c\t\t\\\n\t\t\tquake3/botlib/l_libvar.c\t\t\\\n\t\t\tquake3/botlib/l_log.c\t\t\\\n\t\t\tquake3/botlib/l_memory.c\t\t\\\n\t\t\tquake3/botlib/l_precomp.c\t\\\n\t\t\tquake3/botlib/l_script.c\t\t\\\n\t\t\tquake3/botlib/l_struct.c\t\t\\\n\t\t\tquake3/botlib/standalone.c\nQUAKE3FILES=$(BOTLIBFILES)\t\\\n\t\t\tplugin.c\t\t\\\n\t\t\tquake3/clq3_cg.c\t\t\\\n\t\t\tquake3/clq3_ui.c\t\t\\\n\t\t\tquake3/clq3_parse.c\t\\\n\t\t\tquake3/svq3_game.c\t\\\n\t\t\tquake3/q3common.c\n$(PLUG_PREFIX)quake3$(PLUG_NATIVE_EXT): ${QUAKE3FILES}\n\t$(CC) $(BASE_CFLAGS) $(CFLAGS) -DFTEPLUGIN -DMULTITHREAD -DBOTLIB -DBOTLIB_STATIC -o $@ -shared $(PLUG_CFLAGS) $^ $(PLUG_DEFFILE) $(PLUG_LDFLAGS_ZLIB) $(PLUG_LDFLAGS)\n\t$(call EMBEDMETA,quake3,$@,Quake3 Compat,Quake3 Gamecode Compatibility)\nNATIVE_PLUGINS+=quake3\n\n######################################\n\n#for custom/private plugins...\n-include Makefile.private\n\n#small plugins with simpler build rules...\n\n######################################\n#Mostly for a joke.\n$(PLUG_PREFIX)mpq$(PLUG_NATIVE_EXT): mpq/fs_mpq.c mpq/blast.c plugin.c\n\t$(CC) $(BASE_CFLAGS) $(CFLAGS) -DFTEPLUGIN -o $@ -shared $(PLUG_CFLAGS) -Impq $^ $(PLUG_DEFFILE) $(PLUG_LDFLAGS_ZLIB) $(PLUG_LDFLAGS)\n\t$(call EMBEDMETA,mpq,$@,MPQ Archives,Provides support for Blizzard's .mpq archive format used in eg Diablo 2)\n#NATIVE_PLUGINS+=mpq\n######################################\n\n######################################\n# XMPP aka Jabber chat-protocol support. requires manual account configuration.\n$(PLUG_PREFIX)xmpp$(PLUG_NATIVE_EXT): jabber/jabberclient.c jabber/jingle.c jabber/sift.c jabber/xml.c plugin.c ../engine/common/sha1.c ../engine/common/sha2.c emailnot/md5.c\n\t$(CC) $(BASE_CFLAGS) $(CFLAGS) -DFTEPLUGIN -o $@ -shared $(PLUG_CFLAGS) -Ijabber $^ $(PLUG_DEFFILE) $(PLUG_LDFLAGS) $(LIBRESOLV)\n\t$(call EMBEDMETA,xmpp,$@,XMPP Chat,A slightly more modern alternative to IRC. Requires manual account configuration.)\nNATIVE_PLUGINS+=xmpp\n######################################\n\n######################################\n# Quake Injector plugin.\n$(PLUG_PREFIX)qi$(PLUG_NATIVE_EXT): qi/qi.c jabber/xml.c plugin.c\n\t$(CC) $(BASE_CFLAGS) $(CFLAGS) -DFTEPLUGIN -o $@ -shared $(PLUG_CFLAGS) -Ijabber $^ $(PLUG_DEFFILE) $(PLUG_LDFLAGS)\n\t$(call EMBEDMETA,qi,$@,Quake-Injector Plugin,Provides easy access to the Quaddicted mod database.)\nNATIVE_PLUGINS+=qi\n######################################\n\n######################################\n# Internet Relay Chat (IRC) plugin.\n$(PLUG_PREFIX)irc$(PLUG_NATIVE_EXT): irc/ircclient.c plugin.c\n\t$(CC) $(BASE_CFLAGS) $(CFLAGS) -DFTEPLUGIN -o $@ -shared $(PLUG_CFLAGS) -Iirc $^ $(PLUG_DEFFILE) $(PLUG_LDFLAGS)\n\t$(call EMBEDMETA,irc,$@,IRC Chat,Internet Relay Chat plugin - requires extra server/nick/channel configuration)\nNATIVE_PLUGINS+=irc\n######################################\n\n######################################\n#OpenXR plugin, for use with VR headsets.\n#the tar.gz has a stupid name\nOPENXRVER=1.0.33\n$(ARCHLIBS)/openxr/openxr.h:\n\tcd $(ARCHLIBS)/.. && (test -f release-$(OPENXRVER).tar.gz || wget https://github.com/KhronosGroup/OpenXR-SDK/archive/refs/tags/release-$(OPENXRVER).tar.gz)\n\tcd $(ARCHLIBS) && tar -xvzf ../release-$(OPENXRVER).tar.gz --strip-components=2 OpenXR-SDK-release-$(OPENXRVER)/include/openxr/\n$(PLUG_PREFIX)openxr$(PLUG_NATIVE_EXT): openxr.c plugin.c $(ARCHLIBS)/openxr/openxr.h\n\t$(CC) $(BASE_CFLAGS) $(CFLAGS) -DFTEPLUGIN -o $@ -shared $(PLUG_CFLAGS) $^ $(PLUG_DEFFILE) $(PLUG_LDFLAGS) -DXR_NO_PROTOTYPES `$(PKGCONFIG) --cflags openxr` -DGLQUAKE -DVKQUAKE -DD3D11QUAKE\n\t$(call EMBEDMETA,openxr,$@,OpenXR Support,Enables the use of Virtual Reality headsets and inputs.)\nNATIVE_PLUGINS+=openxr\n######################################\n\n######################################\n#for compat with ezquake\n$(PLUG_PREFIX)ezhud$(PLUG_NATIVE_EXT): ezhud/ezquakeisms.c ezhud/hud.c ezhud/hud_common.c ezhud/hud_editor.c plugin.c\n\t$(CC) $(BASE_CFLAGS) $(CFLAGS) -DFTEPLUGIN -o $@ -shared $(PLUG_CFLAGS) -Iezhud $^ $(PLUG_DEFFILE) $(PLUG_LDFLAGS)\n\t$(call EMBEDMETA,ezhud,$@,EzHud Plugin,Provides compat with ezquake's hud scripts.)\nNATIVE_PLUGINS+=ezhud\n######################################\n\n######################################\n#not really relevant now that gltf was made an internal plugin\n$(PLUG_PREFIX)models$(PLUG_NATIVE_EXT): models/gltf.c models/exportiqm.c models/models.c plugin.c ../engine/common/json.c\n\t$(CC) $(BASE_CFLAGS) $(CFLAGS) -DFTEPLUGIN -o $@ -shared $(PLUG_CFLAGS) -Imodels $^ $(PLUG_DEFFILE) $(PLUG_LDFLAGS)\n\t$(call EMBEDMETA,models,$@,Models Plugin,Kinda redundant now that the engine has gltf2 loading)\n#NATIVE_PLUGINS+=models\n######################################\n\n######################################\n#Openssl crypto plugin, to replace microsoft's shoddy dtls implementation. could also be useful on the BSDs, yay system components?\n\nifeq ($(FTE_TARGET),win32)\n    OSSL_ARCH=mingw\nendif\nifeq ($(FTE_TARGET),win64)\n    OSSL_ARCH=mingw64\nendif\n#statically link openssl on the above systems (instead of depending on system libs)\nifneq ($(OSSL_ARCH),)\nOSSL_VERSION=3.0.1\n#../engine/libs-$(ARCH)/openssl-$(OSSL_VERSION).tar.gz:\n../engine/openssl-$(OSSL_VERSION).tar.gz:\n\twget -O $@ -N https://github.com/openssl/openssl/archive/refs/tags/openssl-$(OSSL_VERSION).tar.gz\n$(ARCHLIBS)/openssl-openssl-$(OSSL_VERSION)/libssl.a $(ARCHLIBS)/openssl-openssl-$(OSSL_VERSION)/libcrypto.a: ../engine/openssl-$(OSSL_VERSION).tar.gz\n\t(cd $(ARCHLIBS) && tar xvfz ../openssl-$(OSSL_VERSION).tar.gz)\n\t(cd $(ARCHLIBS)/openssl-openssl-$(OSSL_VERSION) && CFLAGS=-Os ./Configure --release no-filenames no-legacy no-shared no-stdio no-asm $(OSSL_ARCH) --cross-compile-prefix=$(ARCH)- && $(MAKE))\n$(PLUG_PREFIX)openssl$(PLUG_NATIVE_EXT): $(ARCHLIBS)/openssl-openssl-$(OSSL_VERSION)/libssl.a $(ARCHLIBS)/openssl-openssl-$(OSSL_VERSION)/libcrypto.a\n\n#we should be using openssl's no-sock option, but that also disables core dtls functionality (despite us using our own BIOs).\nOPENSSL_LDCFLAGS=-I$(ARCHLIBS)/openssl-openssl-$(OSSL_VERSION)/include -L$(ARCHLIBS)/openssl-openssl-$(OSSL_VERSION) -lssl -lcrypto -lws2_32\nOPENSSL_AVAILABLE=1\nendif\n\nOPENSSL_LDCFLAGS?=`$(PKGCONFIG) --libs --cflags openssl`\nOPENSSL_AVAILABLE?=$(shell $(OPENSSLPKGPATH) $(PKGCONFIG) --atleast-version=3.0.0 openssl && echo 1)\n$(PLUG_PREFIX)openssl$(PLUG_NATIVE_EXT): net_ssl_openssl.c plugin.c\n\t$(CC) -s $(BASE_CFLAGS) $(CFLAGS) -DFTEPLUGIN -o $@ -shared $(PLUG_CFLAGS) $^ $(PLUG_DEFFILE) $(PLUG_LDFLAGS) $(OPENSSL_LDCFLAGS)\n\t$(call EMBEDMETA,openssl,$@,OpenSSL,Provides OpenSSL support for dtls/tls/https support. The crypto library that is actually used is controlled via the tls_provider cvar.)\n#OpenSSL 3.0.0 is apache2 and thus GPL3-compatible, earlier versions are NOT GPL-compatible at all, so only enable it if its okay.\n#(you can still force it, but don't distribute)\nifeq ($(OPENSSL_AVAILABLE),1)\nNATIVE_PLUGINS+=openssl\nendif\n######################################\n\n######################################\n#for compat with half-life 2's file formats\n$(PLUG_PREFIX)hl2$(PLUG_NATIVE_EXT): hl2/fs_vpk.c hl2/fs_vpk_vtmb.c hl2/fs_gma.c hl2/img_tth.c hl2/img_vtf.c hl2/mod_hl2.c hl2/mat_vmt.c hl2/mod_vbsp.c hl2/hl2.c plugin.c\n\t$(CC) $(BASE_CFLAGS) $(CFLAGS) -DFTEPLUGIN -DMULTITHREAD -o $@ -shared $(PLUG_CFLAGS) $^ $(PLUG_DEFFILE) $(PLUG_LDFLAGS_ZLIB) $(PLUG_LDFLAGS)\n\t$(call EMBEDMETA,hl2,$@,HL2 Formats,Provides support for various formats used by Valve's Source engine.)\nNATIVE_PLUGINS+=hl2\n######################################\n\n######################################\n#for compat with cod's file formats\n$(PLUG_PREFIX)cod$(PLUG_NATIVE_EXT): cod/codmod.c cod/codbsp.c cod/codmat.c cod/codiwi.c plugin.c\n\t$(CC) $(BASE_CFLAGS) $(CFLAGS) -DFTEPLUGIN -DMULTITHREAD -o $@ -shared $(PLUG_CFLAGS) $^ $(PLUG_DEFFILE) $(PLUG_LDFLAGS)\n\t$(call EMBEDMETA,cod,$@,CoD Formats,Provides support for various formats used by Call of Duty.)\nNATIVE_PLUGINS+=cod\n######################################\n\n\n\nifeq ($(findstring win,$(FTE_TARGET)),win)\n######################################\n#winamp ipc\n$(PLUG_PREFIX)winamp$(PLUG_NATIVE_EXT): winamp/winamp.c plugin.c\n\t$(CC) $(BASE_CFLAGS) $(CFLAGS) -DFTEPLUGIN -o $@ -shared $(PLUG_CFLAGS) $^ $(PLUG_DEFFILE) $(PLUG_LDFLAGS)\n\t$(call EMBEDMETA,winamp,$@,Winamp,Provides support for controlling Winamp.)\nNATIVE_PLUGINS+=winamp\n######################################\nendif\n\nall: $(foreach FOO,$(NATIVE_PLUGINS), $(PLUG_PREFIX)$(FOO)$(PLUG_NATIVE_EXT))\n"
  },
  {
    "path": "plugins/avplug/avaudio.c",
    "content": "#include \"../plugin.h\"\r\n#include \"../engine.h\"\r\n\r\n#include \"libavcodec/avcodec.h\"\r\n#include \"libavformat/avformat.h\"\r\n\r\nstatic size_t activedecoders;\r\nstatic cvar_t *ffmpeg_audiodecoder, *pdeveloper;\r\n\r\n#define HAVE_DECOUPLED_API (LIBAVCODEC_VERSION_MAJOR>57 || (LIBAVCODEC_VERSION_MAJOR==57&&LIBAVCODEC_VERSION_MINOR>=36))\r\n\r\nstruct avaudioctx\r\n{\r\n\t//raw file\r\n\tuint8_t *filedata;\r\n\tsize_t fileofs;\r\n\tsize_t filesize;\r\n\r\n\t//avformat stuff\r\n\tAVFormatContext *pFormatCtx;\r\n\tint audioStream;\r\n\r\n\tAVCodecContext *pACodecCtx;\r\n\tAVFrame *pAFrame;\r\n\r\n\t//decoding\r\n\tint64_t lasttime;\r\n\r\n\t//output audio\r\n\t//we throw away data if the format changes. which is awkward, but gah.\r\n\tint64_t samples_framestart;\r\n\tint samples_channels;\r\n\tint samples_speed;\r\n\tqaudiofmt_t samples_format;\r\n\tqbyte *samples_buffer;\r\n\tsize_t samples_framecount;\r\n\tsize_t samples_maxbytes;\r\n};\r\n\r\nstatic void S_AV_Purge(sfx_t *s)\r\n{\r\n\tstruct avaudioctx *ctx = (struct avaudioctx*)s->decoder.buf;\r\n\r\n\ts->loadstate = SLS_NOTLOADED;\r\n\r\n\t// Free the audio decoder\r\n\tif (ctx->pACodecCtx)\r\n\t\tavcodec_free_context(&ctx->pACodecCtx);\r\n\tav_free(ctx->pAFrame);\r\n\r\n\t// Close the video file\r\n\tavformat_close_input(&ctx->pFormatCtx);\r\n\r\n\t//free the decoded buffer\r\n\tfree(ctx->samples_buffer);\r\n\r\n\t//file storage will be cleared here too\r\n\tfree(ctx);\r\n\r\n\tif (s->decoder.ended)\r\n\t\tactivedecoders--;\r\n\tmemset(&s->decoder, 0, sizeof(s->decoder));\r\n}\r\n#define QAF_U8 0x81\r\n#define QAF_S32 0x04\r\n#ifndef MIXER_F32\r\n#define QAF_F32 0x84\r\n#endif\r\n#define QAF_F64 0x88\r\nstatic void S_AV_ReadFrame(struct avaudioctx *ctx)\r\n{\t//reads an audioframe and spits its data into the output sound file for the game engine to use.\r\n\tqaudiofmt_t outformat = QAF_S16, informat=QAF_S16;\r\n\tint channels = ctx->pACodecCtx->ch_layout.nb_channels;\r\n\tint planes = 1, p;\r\n\tunsigned int auddatasize = av_samples_get_buffer_size(NULL, ctx->pACodecCtx->ch_layout.nb_channels, ctx->pAFrame->nb_samples, ctx->pACodecCtx->sample_fmt, 1);\r\n\tswitch(ctx->pACodecCtx->sample_fmt)\r\n\t{\t//we don't support planar audio. we just treat it as mono instead.\r\n\tdefault:\r\n\t\tauddatasize = 0;\r\n\t\tbreak;\r\n\tcase AV_SAMPLE_FMT_U8P:\r\n\t\tplanes = channels;\r\n\t\toutformat = QAF_S8;\r\n\t\tinformat = QAF_U8;\r\n\t\tbreak;\r\n\tcase AV_SAMPLE_FMT_U8:\r\n\t\tplanes = 1;\r\n\t\toutformat = QAF_S8;\r\n\t\tinformat = QAF_U8;\r\n\t\tbreak;\r\n\tcase AV_SAMPLE_FMT_S16P:\r\n\t\tplanes = channels;\r\n\t\toutformat = QAF_S16;\r\n\t\tinformat = QAF_S16;\r\n\t\tbreak;\r\n\tcase AV_SAMPLE_FMT_S16:\r\n\t\tplanes = 1;\r\n\t\toutformat = QAF_S16;\r\n\t\tinformat = QAF_S16;\r\n\t\tbreak;\r\n\r\n\tcase AV_SAMPLE_FMT_S32P:\r\n\t\tplanes = channels;\r\n\t\toutformat = QAF_S16;\r\n\t\tinformat = QAF_S32;\r\n\t\tbreak;\r\n\tcase AV_SAMPLE_FMT_S32:\r\n\t\tplanes = 1;\r\n\t\toutformat = QAF_S16;\r\n\t\tinformat = QAF_S32;\r\n\t\tbreak;\r\n\r\n#ifdef MIXER_F32\r\n\tcase AV_SAMPLE_FMT_FLTP:\r\n\t\tplanes = channels;\r\n\t\toutformat = QAF_F32;\r\n\t\tinformat = QAF_F32;\r\n\t\tbreak;\r\n\tcase AV_SAMPLE_FMT_FLT:\r\n\t\tplanes = 1;\r\n\t\toutformat = QAF_F32;\r\n\t\tinformat = QAF_F32;\r\n\t\tbreak;\r\n\r\n\tcase AV_SAMPLE_FMT_DBLP:\r\n\t\tplanes = channels;\r\n\t\toutformat = QAF_F32;\r\n\t\tinformat = QAF_F64;\r\n\t\tbreak;\r\n\tcase AV_SAMPLE_FMT_DBL:\r\n\t\tplanes = 1;\r\n\t\toutformat = QAF_F32;\r\n\t\tinformat = QAF_F64;\r\n\t\tbreak;\r\n#else\r\n\tcase AV_SAMPLE_FMT_FLTP:\r\n\t\tplanes = channels;\r\n\t\toutformat = QAF_S16;\r\n\t\tinformat = QAF_F32;\r\n\t\tbreak;\r\n\tcase AV_SAMPLE_FMT_FLT:\r\n\t\tplanes = 1;\r\n\t\toutformat = QAF_S16;\r\n\t\tinformat = QAF_F32;\r\n\t\tbreak;\r\n\r\n\tcase AV_SAMPLE_FMT_DBLP:\r\n\t\tplanes = channels;\r\n\t\toutformat = QAF_S16;\r\n\t\tinformat = QAF_F64;\r\n\t\tbreak;\r\n\tcase AV_SAMPLE_FMT_DBL:\r\n\t\tplanes = 1;\r\n\t\toutformat = QAF_S16;\r\n\t\tinformat = QAF_F64;\r\n\t\tbreak;\r\n#endif\r\n\t}\r\n\r\n\tif (ctx->samples_channels != channels || ctx->samples_speed != ctx->pACodecCtx->sample_rate || ctx->samples_format != outformat)\r\n\t{\t//something changed, update\r\n\t\tctx->samples_channels = channels;\r\n\t\tctx->samples_speed = ctx->pACodecCtx->sample_rate;\r\n\t\tctx->samples_format = outformat;\r\n\r\n\t\t//and discard any decoded audio. this might loose some.\r\n\t\tctx->samples_framestart += ctx->samples_framecount;\r\n\t\tctx->samples_framecount = 0;\r\n\t}\r\n\tif (ctx->samples_maxbytes < (ctx->samples_framecount*QAF_BYTES(ctx->samples_format)*ctx->samples_channels)+auddatasize)\r\n\t{\r\n\t\tctx->samples_maxbytes = (ctx->samples_framecount*QAF_BYTES(ctx->samples_format)*ctx->samples_channels)+auddatasize;\r\n\t\tctx->samples_maxbytes *= 2;\t//slop\r\n\t\tctx->samples_buffer = realloc(ctx->samples_buffer, ctx->samples_maxbytes);\r\n\t}\r\n\tif (planes==1 && outformat != QAF_S8 && informat==outformat)\r\n\t\tmemcpy(ctx->samples_buffer + ctx->samples_framecount*(QAF_BYTES(ctx->samples_format)*ctx->samples_channels), ctx->pAFrame->data[0], auddatasize);\r\n\telse\r\n\t{\r\n\t\tvoid *fte_restrict outv = (ctx->samples_buffer + ctx->samples_framecount*(QAF_BYTES(ctx->samples_format)*ctx->samples_channels));\r\n\t\tsize_t i, samples = auddatasize / (planes*QAF_BYTES(informat));\r\n\t\tif (outformat == QAF_S8 && informat == QAF_U8)\r\n\t\t{\r\n\t\t\tchar *out = outv;\r\n\t\t\tfor (p = 0; p < planes; p++, out++)\r\n\t\t\t{\r\n\t\t\t\tunsigned char *in = ctx->pAFrame->data[p];\r\n\t\t\t\tfor (i = 0; i < samples; i++)\r\n\t\t\t\t\tout[i*planes] = in[i]-128;\t//convert from u8 to s8.\r\n\t\t\t}\r\n\t\t}\r\n\t\telse if (outformat == QAF_S16 && informat == QAF_S16)\r\n\t\t{\r\n\t\t\tsigned short *out = outv;\r\n\t\t\tfor (p = 0; p < planes; p++, out++)\r\n\t\t\t{\r\n\t\t\t\tsigned short *in = (signed short *)ctx->pAFrame->data[p];\r\n\t\t\t\tfor (i = 0; i < samples; i++)\r\n\t\t\t\t\tout[i*planes] = in[i];\t//no conversion needed\r\n\t\t\t}\r\n\t\t}\r\n\t\telse if (outformat == QAF_S16 && informat == QAF_S32)\r\n\t\t{\r\n\t\t\tsigned short *out = outv;\r\n\t\t\tfor (p = 0; p < planes; p++, out++)\r\n\t\t\t{\r\n\t\t\t\tsigned int *in = (signed int *)ctx->pAFrame->data[p];\r\n\t\t\t\tfor (i = 0; i < samples; i++)\r\n\t\t\t\t\tout[i*planes] = in[i]>>16;\t//just use the MSBs, no clamping needed.\r\n\t\t\t}\r\n\t\t}\r\n#ifdef MIXER_F32\r\n\t\telse if (outformat == QAF_F32 && informat == QAF_F32)\r\n\t\t{\r\n\t\t\tfloat *out = outv;\r\n\t\t\tfor (p = 0; p < planes; p++, out++)\r\n\t\t\t{\r\n\t\t\t\tfloat *in = (float *)ctx->pAFrame->data[p];\r\n\t\t\t\tfor (i = 0; i < samples; i++)\r\n\t\t\t\t\tout[i*planes] = in[i];\t//no conversion needed.\r\n\t\t\t}\r\n\t\t}\r\n\t\telse if (outformat == QAF_F32 && informat == QAF_F64)\r\n\t\t{\r\n\t\t\tfloat *out = outv;\r\n\t\t\tfor (p = 0; p < planes; p++, out++)\r\n\t\t\t{\r\n\t\t\t\tdouble *in = (double *)ctx->pAFrame->data[p];\r\n\t\t\t\tfor (i = 0; i < samples; i++)\r\n\t\t\t\t\tout[i*planes] = in[i];\t//no clamping needed.\r\n\t\t\t}\r\n\t\t}\r\n#else\r\n\t\telse if (outformat == QAF_S16 && informat == QAF_F32)\r\n\t\t{\r\n\t\t\tsigned short *out = outv;\r\n\t\t\tfor (p = 0; p < planes; p++, out++)\r\n\t\t\t{\r\n\t\t\t\tfloat *in = (float *)ctx->pAFrame->data[p];\r\n\t\t\t\tfor (i = 0; i < samples; i++)\r\n\t\t\t\t{\r\n\t\t\t\t\tint v = in[i] * 32767;\r\n\t\t\t\t\tif (v < -32768)\r\n\t\t\t\t\t\tv = -32768;\r\n\t\t\t\t\tif (v > 32767)\r\n\t\t\t\t\t\tv = 32767;\r\n\t\t\t\t\tout[i*planes] = v;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t\telse if (outformat == QAF_S16 && informat == QAF_F64)\r\n\t\t{\r\n\t\t\tsigned short *out = outv;\r\n\t\t\tfor (p = 0; p < planes; p++, out++)\r\n\t\t\t{\r\n\t\t\t\tdouble *in = (double *)ctx->pAFrame->data[p];\r\n\t\t\t\tfor (i = 0; i < samples; i++)\r\n\t\t\t\t{\r\n\t\t\t\t\tint v = in[i] * 32767;\r\n\t\t\t\t\tif (v < -32768)\r\n\t\t\t\t\t\tv = -32768;\r\n\t\t\t\t\tif (v > 32767)\r\n\t\t\t\t\t\tv = 32767;\r\n\t\t\t\t\tout[i*planes] = v;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n#endif\r\n\t}\r\n\tctx->samples_framecount += auddatasize/(QAF_BYTES(informat)*ctx->samples_channels);\r\n}\r\nstatic sfxcache_t *S_AV_Locate(sfx_t *sfx, sfxcache_t *buf, ssamplepos_t start, int length)\r\n{\t//warning: can be called on a different thread.\r\n\tstruct avaudioctx *ctx = (struct avaudioctx*)sfx->decoder.buf;\r\n\tAVPacket\t\tpacket;\r\n\tint64_t\t\t\tcurtime;\r\n\r\n\tif (!buf)\r\n\t\treturn NULL;\r\n\r\n\tcurtime = start + length;\r\n\r\n\twhile (1)\r\n\t{\r\n\t\tif (start < ctx->samples_framestart)\r\n\t\t\tbreak;\t//o.O rewind!\r\n\r\n\t\tif (ctx->samples_framestart+ctx->samples_framecount > curtime)\r\n\t\t\tbreak;\t//no need yet.\r\n\r\n#ifdef HAVE_DECOUPLED_API\r\n\t\tif(0==avcodec_receive_frame(ctx->pACodecCtx, ctx->pAFrame))\r\n\t\t{\r\n\t\t\tS_AV_ReadFrame(ctx);\r\n\t\t\tcontinue;\r\n\t\t}\r\n#endif\r\n\r\n\t\t// We're ahead of the previous frame. try and read the next.\r\n\t\tif (av_read_frame(ctx->pFormatCtx, &packet) < 0)\r\n\t\t\tbreak;\r\n\r\n\t\t// Is this a packet from the video stream?\r\n\t\tif(packet.stream_index==ctx->audioStream)\r\n\t\t{\r\n#ifdef HAVE_DECOUPLED_API\r\n\t\t\tavcodec_send_packet(ctx->pACodecCtx, &packet);\r\n#else\r\n\t\t\tint okay;\r\n\t\t\tint len;\r\n\t\t\tvoid *odata = packet.data;\r\n\t\t\twhile (packet.size > 0)\r\n\t\t\t{\t//this old api only decodes part of the packet with each itteration, so keep reading until we decoded the entire thing.\r\n\t\t\t\tokay = false;\r\n\t\t\t\tlen = avcodec_decode_audio4(ctx->pACodecCtx, ctx->pAFrame, &okay, &packet);\r\n\t\t\t\tif (len < 0)\r\n\t\t\t\t\tbreak;\r\n\t\t\t\tpacket.size -= len;\r\n\t\t\t\tpacket.data += len;\r\n\t\t\t\tif (okay)\r\n\t\t\t\t\tS_AV_ReadFrame(ctx);\r\n\t\t\t}\r\n\t\t\tpacket.data = odata;\r\n#endif\r\n\t\t}\r\n\r\n\t\t// Free the packet that was allocated by av_read_frame\r\n\t\tav_packet_unref(&packet);\r\n\t}\r\n\r\n\tbuf->length = ctx->samples_framecount;\r\n\tbuf->speed = ctx->samples_speed;\r\n\tbuf->format = ctx->samples_format;\r\n\tbuf->numchannels = ctx->samples_channels;\r\n\tbuf->soundoffset = ctx->samples_framestart;\r\n\tbuf->data = ctx->samples_buffer;\r\n\r\n\t//if we couldn't return any new data, then we're at an eof, return NULL to signal that.\r\n\tif (start == buf->soundoffset + buf->length && length > 0)\r\n\t\treturn NULL;\r\n\r\n\treturn buf;\r\n}\r\nstatic float S_AV_Query(struct sfx_s *sfx, struct sfxcache_s *buf, char *title, size_t titlesize)\r\n{\r\n\tstruct avaudioctx *ctx = (struct avaudioctx*)sfx->decoder.buf;\r\n\tif (!ctx)\r\n\t\treturn -1;\r\n\tif (buf)\r\n\t{\r\n\t\tbuf->data = NULL;\r\n\t\tbuf->soundoffset = 0;\r\n\t\tbuf->length = 0;\r\n\t\tbuf->numchannels = ctx->samples_channels;\r\n\t\tbuf->speed = ctx->samples_speed;\r\n\t\tbuf->format = ctx->samples_format;\r\n\t}\r\n\treturn ctx->pFormatCtx->duration / (float)AV_TIME_BASE;\r\n}\r\n\r\nstatic int AVIO_Mem_Read(void *opaque, uint8_t *buf, int buf_size)\r\n{\r\n\tstruct avaudioctx *ctx = opaque;\r\n\tif (ctx->fileofs > ctx->filesize)\r\n\t\tbuf_size = 0;\r\n\tif (buf_size > ctx->filesize-ctx->fileofs)\r\n\t\tbuf_size = ctx->filesize-ctx->fileofs;\r\n\tif (buf_size > 0)\r\n\t{\r\n\t\tmemcpy(buf, ctx->filedata + ctx->fileofs, buf_size);\r\n\t\tctx->fileofs += buf_size;\r\n\t\treturn buf_size;\r\n\t}\r\n\treturn 0;\r\n}\r\nstatic int64_t AVIO_Mem_Seek(void *opaque, int64_t offset, int whence)\r\n{\r\n\tstruct avaudioctx *ctx = opaque;\r\n\twhence &= ~AVSEEK_FORCE;\r\n\tswitch(whence)\r\n\t{\r\n\tdefault:\r\n\t\treturn -1;\r\n\tcase SEEK_SET:\r\n\t\tctx->fileofs = offset;\r\n\t\tbreak;\r\n\tcase SEEK_CUR:\r\n\t\tctx->fileofs += offset;\r\n\t\tbreak;\r\n\tcase SEEK_END:\r\n\t\tctx->fileofs = ctx->filesize + offset;\r\n\t\tbreak;\r\n\tcase AVSEEK_SIZE:\r\n\t\treturn ctx->filesize;\r\n\t}\r\n\tif (ctx->fileofs < 0)\r\n\t\tctx->fileofs = 0;\r\n\treturn ctx->fileofs;\r\n}\r\n\r\n/*const char *COM_GetFileExtension (const char *in)\r\n{\r\n\tconst char *dot;\r\n\r\n\tfor (dot = in + strlen(in); dot >= in && *dot != '.'; dot--)\r\n\t\t;\r\n\tif (dot < in)\r\n\t\treturn \"\";\r\n\tin = dot+1;\r\n\treturn in;\r\n}*/\r\nstatic qboolean QDECL S_LoadAVSound (sfx_t *s, qbyte *data, size_t datalen, int sndspeed)\r\n{\r\n\tstruct avaudioctx *ctx;\r\n\tint i;\r\n\tconst AVCodec *pCodec;\r\n\tconst int iBufSize = 4 * 1024;\r\n\r\n\tif (!ffmpeg_audiodecoder)\r\n\t\treturn false;\r\n\tif (!ffmpeg_audiodecoder->ival /* && *ffmpeg_audiodecoder.string */)\r\n\t\treturn false;\r\n\r\n\r\n\tif (!data || !datalen)\r\n\t\treturn false;\r\n\r\n\t//ignore it if it looks like a wav file. that means we don't need to figure out how to calculate loopstart.\r\n\t//FIXME: this also blocks playing the audio from avi files too!\r\n\tif (datalen >= 4 && !strncmp(data, \"RIFF\", 4))\r\n\t\treturn false;\r\n\r\n//\tif (strcasecmp(COM_GetFileExtension(s->name), \"wav\"))\t//don't do .wav - I've no idea how to read the loopstart tag with ffmpeg.\r\n//\t\treturn false;\r\n\r\n\ts->decoder.buf = ctx = malloc(sizeof(*ctx) + datalen);\r\n\tif (!ctx)\r\n\t\treturn false;\t//o.O\r\n\tmemset(ctx, 0, sizeof(*ctx));\r\n\r\n\t// Create internal io buffer for FFmpeg\r\n\tctx->filedata = data;\t//defer that copy\r\n\tctx->filesize = datalen;\t//defer that copy\r\n\tctx->pFormatCtx = avformat_alloc_context();\r\n\tctx->pFormatCtx->pb = avio_alloc_context(av_malloc(iBufSize), iBufSize, 0, ctx, AVIO_Mem_Read, 0, AVIO_Mem_Seek);\r\n\r\n\t// Open file\r\n\tif(avformat_open_input(&ctx->pFormatCtx, s->name, NULL, NULL)==0)\r\n\t{\r\n\t\t// Retrieve stream information\r\n\t\tif(avformat_find_stream_info(ctx->pFormatCtx, NULL)>=0)\r\n\t\t{\r\n\t\t\tctx->audioStream=-1;\r\n\t\t\tfor(i=0; i<ctx->pFormatCtx->nb_streams; i++)\r\n#if LIBAVFORMAT_VERSION_MAJOR >= 57\r\n\t\t\t\tif(ctx->pFormatCtx->streams[i]->codecpar->codec_type==AVMEDIA_TYPE_AUDIO)\r\n#else\r\n\t\t\t\tif(ctx->pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_AUDIO)\r\n#endif\r\n\t\t\t\t{\r\n\t\t\t\t\tctx->audioStream=i;\r\n\t\t\t\t\tbreak;\r\n\t\t\t\t}\r\n\t\t\tif(ctx->audioStream!=-1)\r\n\t\t\t{\r\n#if LIBAVFORMAT_VERSION_MAJOR >= 57\r\n\t\t\t\tpCodec=avcodec_find_decoder(ctx->pFormatCtx->streams[ctx->audioStream]->codecpar->codec_id);\r\n\t\t\t\tctx->pACodecCtx = avcodec_alloc_context3(pCodec);\r\n\t\t\t\tif (avcodec_parameters_to_context(ctx->pACodecCtx, ctx->pFormatCtx->streams[ctx->audioStream]->codecpar) < 0)\r\n\t\t\t\t{\r\n\t\t\t\t\tavcodec_free_context(&ctx->pACodecCtx);\r\n\t\t\t\t\tpCodec = NULL;\r\n\t\t\t\t}\r\n#else\r\n\t\t\t\tctx->pACodecCtx=ctx->pFormatCtx->streams[ctx->audioStream]->codec;\r\n\t\t\t\tpCodec=avcodec_find_decoder(ctx->pACodecCtx->codec_id);\r\n#endif\r\n\t\t\t\tctx->pAFrame=av_frame_alloc();\r\n\t\t\t\tif(pCodec!=NULL && ctx->pAFrame && avcodec_open2(ctx->pACodecCtx, pCodec, NULL) >= 0)\r\n\t\t\t\t{\t//success\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t\tctx->audioStream = -1;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tif (ctx->audioStream != -1)\r\n\t\t{\r\n\t\t\t//sucky copy\r\n\t\t\tctx->filedata = (uint8_t*)(ctx+1);\r\n\t\t\tmemcpy(ctx->filedata, data, datalen);\r\n\r\n\t\t\ts->decoder.ended = S_AV_Purge;\r\n\t\t\ts->decoder.purge = S_AV_Purge;\r\n\t\t\ts->decoder.decodedata = S_AV_Locate;\r\n\t\t\ts->decoder.querydata = S_AV_Query;\r\n\t\t\tactivedecoders++;\r\n\t\t\treturn true;\r\n\t\t}\r\n\t}\r\n\tS_AV_Purge(s);\r\n\treturn false;\r\n}\r\nqboolean AVAudio_MayUnload(void)\r\n{\r\n\treturn activedecoders==0;\r\n}\r\nstatic qboolean AVAudio_Init(void)\r\n{\r\n\tif (!plugfuncs->ExportFunction(\"MayUnload\", AVAudio_MayUnload) ||\r\n\t\t!plugfuncs->ExportFunction(\"S_LoadSound\", S_LoadAVSound))\r\n\t{\r\n\t\tCon_Printf(\"ffmpeg: Engine doesn't support audio decoder plugins\\n\");\r\n\t\treturn false;\r\n\t}\r\n\tffmpeg_audiodecoder = cvarfuncs->GetNVFDG(\"ffmpeg_audiodecoder_wip\", \"1\", 0, \"Enables the use of ffmpeg's decoder for pure audio files.\", \"ffmpeg\");\r\n\tif (!ffmpeg_audiodecoder->ival)\r\n\t\tCon_Printf(\"ffmpeg: audio decoding disabled, use \\\"set %s 1\\\" to enable ffmpeg audio decoding\\n\", ffmpeg_audiodecoder->name);\r\n\treturn true;\r\n}\r\n\r\n\r\n//generic module stuff. this has to go somewhere.\r\nstatic void AVLogCallback(void *avcl, int level, const char *fmt, va_list vl)\r\n{\t//needs to be reenterant\r\n#ifdef _DEBUG\r\n\tchar\t\tstring[1024];\r\n\tif (level >= AV_LOG_INFO)\r\n\t\treturn;\t//don't care if its just going to be spam.\r\n\tQ_vsnprintf (string, sizeof(string), fmt, vl);\r\n\tif (level >= AV_LOG_WARNING)\r\n\t{\r\n\t\tif (pdeveloper && pdeveloper->ival)\r\n\t\t\tCon_Printf(\"ffmpeg: %s\", string);\r\n\t}\r\n\telse if (level >= AV_LOG_ERROR)\r\n\t\tCon_Printf(CON_WARNING\"ffmpeg: %s\", string);\r\n\telse\r\n\t\tCon_Printf(CON_ERROR\"ffmpeg: %s\", string);\r\n#endif\r\n}\r\n\r\n//get the encoder/decoders to register themselves with the engine, then make sure avformat/avcodec have registered all they have to give.\r\nqboolean AVEnc_Init(void);\r\nqboolean AVDec_Init(void);\r\nqboolean Plug_Init(void)\r\n{\r\n\tqboolean okay = false;\r\n\r\n\tokay |= AVAudio_Init();\r\n\tokay |= AVDec_Init();\r\n\tokay |= AVEnc_Init();\r\n\tif (okay)\r\n\t{\r\n#if ( LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(58,9,100) )\r\n\t\tav_register_all();\r\n\t\tavcodec_register_all();\r\n#endif\r\n\r\n\t\tpdeveloper = cvarfuncs->GetNVFDG(\"developer\", \"0\", 0, \"Developer spam.\", \"ffmpeg\");\r\n\t\tav_log_set_level(AV_LOG_WARNING);\r\n\t\tav_log_set_callback(AVLogCallback);\r\n\t}\r\n\treturn okay;\r\n}\r\n\r\n"
  },
  {
    "path": "plugins/avplug/avdecode.c",
    "content": "#include \"../plugin.h\"\n#include \"../engine.h\"\n\nstatic plugfsfuncs_t *filefuncs;\nstatic plugaudiofuncs_t *audiofuncs;\n\n#include \"libavcodec/avcodec.h\"\n#include \"libavformat/avformat.h\"\n#include \"libswscale/swscale.h\"\n#include \"libavutil/imgutils.h\"\n\n#define TARGET_FFMPEG (LIBAVFORMAT_VERSION_MICRO >= 100)\n#define HAVE_DECOUPLED_API (LIBAVCODEC_VERSION_MAJOR>57 || (LIBAVCODEC_VERSION_MAJOR==57&&LIBAVCODEC_VERSION_MINOR>=36))\n\n//between av 52.31 and 54.35, lots of constants etc got renamed to gain an extra AV_ prefix.\n/*\n#define AV_PIX_FMT_BGRA PIX_FMT_BGRA\n#define AVMEDIA_TYPE_AUDIO CODEC_TYPE_AUDIO\n#define AVMEDIA_TYPE_VIDEO CODEC_TYPE_VIDEO\n#define AV_PIX_FMT_BGRA PIX_FMT_BGRA\n#define AV_SAMPLE_FMT_U8 SAMPLE_FMT_U8\n#define AV_SAMPLE_FMT_S16 SAMPLE_FMT_S16\n#define AV_SAMPLE_FMT_FLT SAMPLE_FMT_FLT\n#define AVIOContext ByteIOContext\n#define avio_alloc_context av_alloc_put_byte\n*/\n\n#define DECODERNAME \"ffmpeg\"\n\n/*should probably try threading this, though I suppose it should be the engine doing that.*/\n/*timing is based upon the start time. this means overflow issues with rtsp etc*/\n\nstruct decctx\n{\n\tunsigned int width, height;\n\n\tvfsfile_t *file;\n\tint64_t fileofs;\n\tint64_t filelen;\n\tAVFormatContext *pFormatCtx;\n\n\tint audioStream;\n\tAVCodecContext  *pACodecCtx;\n\tAVFrame         *pAFrame;\n\n\tint videoStream;\n\tAVCodecContext  *pVCodecCtx;\n\tAVFrame         *pVFrame;\n\tint64_t num, denum;\n\tint64_t lasttime;\n\tint64_t timeoffset;\t//timestamp of first video frame\n\n\tuint8_t *rgb_data;\n\tint rgb_linesize;\n\tstruct SwsContext\t\t*pScaleCtx;\n};\n\nstatic qboolean AVDec_SetSize (void *vctx, int width, int height)\n{\n\tstruct decctx\t*ctx = (struct decctx*)vctx;\n\tuint8_t *rgb_data[4];\t//av_image_alloc requires at least 4 entries for certain pix formats (libav (but not ffmpeg) zero-fills, so this is important).\n\tint rgb_linesize[4];\n\n\t//colourspace conversions will be fastest if we\n//\tif (width > ctx->pCodecCtx->width)\n\t\twidth = ctx->pVCodecCtx->width;\n//\tif (height > ctx->pCodecCtx->height)\n\t\theight = ctx->pVCodecCtx->height;\n\n\t//is this a no-op?\n\tif (width == ctx->width && height == ctx->height && ctx->pScaleCtx)\n\t\treturn true;\n\n\tif (av_image_alloc(rgb_data, rgb_linesize, width, height, AV_PIX_FMT_BGRA, 1) >= 0)\n\t{\n\t\t//update the scale context as required\n\t\t//clear the old stuff out\n\t\tav_free(ctx->rgb_data);\n\n\t\tctx->width = width;\n\t\tctx->height = height;\n\t\tctx->rgb_data = rgb_data[0];\n\t\tctx->rgb_linesize = rgb_linesize[0];\n\t\treturn qtrue;\n\t}\n\treturn qfalse;\t//unsupported\n}\n\nstatic int AVIO_Read(void *opaque, uint8_t *buf, int buf_size)\n{\n\tstruct decctx *ctx = opaque;\n\tint ammount;\n\tammount = VFS_READ(ctx->file, buf, buf_size);\n\tif (ammount > 0)\n\t\tctx->fileofs += ammount;\n\treturn ammount;\n}\nstatic int64_t AVIO_Seek(void *opaque, int64_t offset, int whence)\n{\n\tstruct decctx *ctx = opaque;\n\twhence &= ~AVSEEK_FORCE;\n\tswitch(whence)\n\t{\n\tdefault:\n\t\treturn -1;\n\tcase SEEK_SET:\n\t\tctx->fileofs = offset;\n\t\tbreak;\n\tcase SEEK_CUR:\n\t\tctx->fileofs += offset;\n\t\tbreak;\n\tcase SEEK_END:\n\t\tctx->fileofs = ctx->filelen + offset;\n\t\tbreak;\n\tcase AVSEEK_SIZE:\n\t\treturn ctx->filelen;\n\t}\n\tVFS_SEEK(ctx->file, ctx->fileofs);\n\treturn ctx->fileofs;\n}\n\nstatic void AVDec_Destroy(void *vctx)\n{\n\tstruct decctx *ctx = (struct decctx*)vctx;\n\n\t// Free the video stuff\n\tav_free(ctx->rgb_data);\n\tif (ctx->pVCodecCtx)\n\t\tavcodec_free_context(&ctx->pVCodecCtx);\n\tav_free(ctx->pVFrame);\n\n\t// Free the audio decoder\n\tif (ctx->pACodecCtx)\n\t\tavcodec_free_context(&ctx->pACodecCtx);\n\tav_free(ctx->pAFrame);\n\n\t// Close the video file\n\tavformat_close_input(&ctx->pFormatCtx);\n\n\tif (ctx->file)\n\t\tVFS_CLOSE(ctx->file);\n\n\tfree(ctx);\n}\n\nstatic void *AVDec_Create(const char *medianame)\n{\n\tstruct decctx *ctx;\n\n\tunsigned int             i;\n\tconst AVCodec         *pCodec;\n\tqboolean useioctx = false;\n//\tconst char *extension = strrchr(medianame, '.');\n\n\t/*always respond to av: media prefixes*/\n\tif (!strncmp(medianame, \"av:\", 3) || !strncmp(medianame, \"ff:\", 3))\n\t{\n\t\tmedianame = medianame + 3;\n\t\tuseioctx = true;\n\t}\n\telse if (!strncmp(medianame, \"avs:\", 4) || !strncmp(medianame, \"ffs:\", 4))\n\t{\n\t\tmedianame = medianame + 4;\n\t\t//let avformat do its own avio context stuff\n\t}\n\telse if (strchr(medianame, ':'))\t//block other types of url/prefix.\n\t\treturn NULL;\n//\telse if (!strcasecmp(extension, \".roq\") || !strcasecmp(extension, \".roq\") || !strcasecmp(extension, \".cin\"))\n//\t\treturn NULL;\t//roq+cin should be played back via the engine instead...\n\telse\n\t\tuseioctx = true;\n\n\tctx = malloc(sizeof(*ctx));\n\tmemset(ctx, 0, sizeof(*ctx));\n\n\tctx->lasttime = -1;\n\tctx->file = NULL;\n\tif (useioctx)\n\t{\n\t\t// Create internal Buffer for FFmpeg:\n\t\tconst int iBufSize = 32 * 1024;\n\t\tchar *pBuffer = av_malloc(iBufSize);\n\t\tAVIOContext *ioctx;\n\n\t\tctx->file = filefuncs->OpenVFS(medianame, \"rb\", FS_GAME);\n\t\tif (!ctx->file)\n\t\t\tctx->file = filefuncs->OpenVFS(va(\"video/%s\", medianame), \"rb\", FS_GAME);\n\t\tif (!ctx->file)\n\t\t{\n\t\t\tCon_Printf(\"Unable to open %s\\n\", medianame);\n\t\t\tfree(ctx);\n\t\t\tav_free(pBuffer);\n\t\t\treturn NULL;\n\t\t}\n\t\tctx->filelen = VFS_GETLEN(ctx->file);\n\n\t\tioctx = avio_alloc_context(pBuffer, iBufSize, 0, ctx, AVIO_Read, 0, AVIO_Seek);\n\t\tctx->pFormatCtx = avformat_alloc_context();\n\n\t\tctx->pFormatCtx->pb = ioctx;\n\t}\n\t/*\nsmall how-to note for if I ever try to add support for voice-and-video rtp decoding.\nthis stuff is presumably needed to handle ICE+stun+ports etc.\nI prolly need to hack around with adding rtcp too. :s\n\nrtsp: Add support for depacketizing RTP data via custom IO\n\nTo use this, set sdpflags=custom_io to the sdp demuxer. During\nthe avformat_open_input call, the SDP is read from the AVFormatContext\nAVIOContext (ctx->pb) - after the avformat_open_input call,\nduring the av_read_frame() calls, the same ctx->pb is used for reading\npackets (and sending back RTCP RR packets).\n\nNormally, one would use this with a read-only AVIOContext for the\nSDP during the avformat_open_input call, then close that one and\nreplace it with a read-write one for the packets after the\navformat_open_input call has returned.\n\nThis allows using the RTP depacketizers as \"pure\" demuxers, without\nhaving them tied to the libavformat network IO.\n\t*/\n\n\n\t// Open video file\n\tif(avformat_open_input(&ctx->pFormatCtx, medianame, NULL, NULL)==0)\n\t{\n\t\t// Retrieve stream information\n\t\tif(avformat_find_stream_info(ctx->pFormatCtx, NULL)>=0)\n\t\t{\n\t\t\tctx->audioStream=-1;\n\t\t\tfor(i=0; i<ctx->pFormatCtx->nb_streams && ctx->audioStream==-1; i++)\n\t\t\t{\n#if LIBAVFORMAT_VERSION_MAJOR >= 57\n\t\t\t\tif(ctx->pFormatCtx->streams[i]->codecpar->codec_type==AVMEDIA_TYPE_AUDIO)\n#else\n\t\t\t\tif(ctx->pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_AUDIO)\n#endif\n\t\t\t\t\tctx->audioStream=i;\n\t\t\t}\n\t\t\tif(ctx->audioStream!=-1)\n\t\t\t{\n#if LIBAVFORMAT_VERSION_MAJOR >= 57\n\t\t\t\tpCodec=avcodec_find_decoder(ctx->pFormatCtx->streams[ctx->audioStream]->codecpar->codec_id);\n\t\t\t\tctx->pACodecCtx = avcodec_alloc_context3(pCodec);\n\t\t\t\tif (avcodec_parameters_to_context(ctx->pACodecCtx, ctx->pFormatCtx->streams[ctx->audioStream]->codecpar) < 0)\n\t\t\t\t{\n\t\t\t\t\tavcodec_free_context(&ctx->pACodecCtx);\n\t\t\t\t\tpCodec = NULL;\n\t\t\t\t}\n#else\n\t\t\t\tctx->pACodecCtx=ctx->pFormatCtx->streams[ctx->audioStream]->codec;\n\t\t\t\tpCodec=avcodec_find_decoder(ctx->pACodecCtx->codec_id);\n#endif\n\n\t\t\t\tctx->pAFrame=av_frame_alloc();\n\t\t\t\tif(pCodec!=NULL && ctx->pAFrame && avcodec_open2(ctx->pACodecCtx, pCodec, NULL) >= 0)\n\t\t\t\t{\n\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tctx->audioStream = -1;\n\t\t\t}\n\n\t\t\tctx->videoStream=-1;\n\t\t\tfor(i=0; i<ctx->pFormatCtx->nb_streams && ctx->videoStream==-1; i++)\n\t\t\t{\n#if LIBAVFORMAT_VERSION_MAJOR >= 57\n\t\t\t\tif(ctx->pFormatCtx->streams[i]->codecpar->codec_type==AVMEDIA_TYPE_VIDEO)\n#else\n\t\t\t\tif(ctx->pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO)\n#endif\n\t\t\t\t\tctx->videoStream=i;\n\t\t\t}\n\t\t\tif(ctx->videoStream!=-1)\n\t\t\t{\n#if LIBAVFORMAT_VERSION_MAJOR >= 57\n\t\t\t\tpCodec=avcodec_find_decoder(ctx->pFormatCtx->streams[ctx->videoStream]->codecpar->codec_id);\n\t\t\t\tctx->pVCodecCtx = avcodec_alloc_context3(pCodec);\n\t\t\t\tif (avcodec_parameters_to_context(ctx->pVCodecCtx, ctx->pFormatCtx->streams[ctx->videoStream]->codecpar) < 0)\n\t\t\t\t{\n\t\t\t\t\tavcodec_free_context(&ctx->pVCodecCtx);\n\t\t\t\t\tpCodec = NULL;\n\t\t\t\t}\n#else\n\t\t\t\tctx->pVCodecCtx=ctx->pFormatCtx->streams[ctx->videoStream]->codec;\n\t\t\t\tpCodec=avcodec_find_decoder(ctx->pVCodecCtx->codec_id);\n#endif\n\t\t\t\tctx->num = ctx->pFormatCtx->streams[ctx->videoStream]->time_base.num;\n\t\t\t\tctx->denum = ctx->pFormatCtx->streams[ctx->videoStream]->time_base.den;\n\n\t\t\t\tif (ctx->pFormatCtx->streams[ctx->videoStream]->start_time != AV_NOPTS_VALUE)\n\t\t\t\t\tctx->timeoffset = ctx->pFormatCtx->streams[ctx->videoStream]->start_time;\n\t\t\t\telse\n\t\t\t\t\tctx->timeoffset = 0;\t//should probably guess.\n\n\t\t\t\t// Open codec\n\t\t\t\tif(pCodec!=NULL && avcodec_open2(ctx->pVCodecCtx, pCodec, NULL) >= 0)\n\t\t\t\t{\n\t\t\t\t\t// Allocate video frame\n\t\t\t\t\tctx->pVFrame=av_frame_alloc();\n\t\t\t\t\tif(ctx->pVFrame!=NULL)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (AVDec_SetSize(ctx, ctx->pVCodecCtx->width, ctx->pVCodecCtx->height))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn ctx;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tAVDec_Destroy(ctx);\n\treturn NULL;\n}\n\nstatic qboolean VARGS AVDec_DisplayFrame(void *vctx, qboolean nosound, qboolean forcevideo, double mediatime, void (QDECL *uploadtexture)(void *ectx, uploadfmt_t fmt, int width, int height, void *data, void *palette), void *ectx)\n{\n\tstruct decctx *ctx = (struct decctx*)vctx;\n\tAVPacket        packet;\n#if HAVE_DECOUPLED_API\n#else\n\tint             frameFinished;\n#endif\n\tqboolean\t\trepainted = false;\n\tint64_t curtime;\n\n\tcurtime = (mediatime * ctx->denum) / ctx->num;\n\n\tnosound |= !audiofuncs;\n\t\n\twhile (1)\n\t{\n\t\tif (ctx->lasttime >= curtime)\n\t\t\tbreak;\n\n\t\t// We're ahead of the previous frame. try and read the next.\n\t\t//FIXME: when streaming, av_read_frame will _block_ and that sucks big hairy donkey balls.\n\t\tif (av_read_frame(ctx->pFormatCtx, &packet) < 0)\n\t\t{\n\t\t\tif (repainted)\n\t\t\t\tbreak;\n\t\t\treturn false;\n\t\t}\n\n#if HAVE_DECOUPLED_API\n\t\tif(packet.stream_index==ctx->videoStream)\n\t\t{\n\t\t\tavcodec_send_packet(ctx->pVCodecCtx, &packet);\n\t\t\t\n\t\t\twhile(0==avcodec_receive_frame(ctx->pVCodecCtx, ctx->pVFrame))\n\t\t\t{\n\t\t\t\t//rescale+convert it to what we're rendering (no more yuv)\n\t\t\t\tctx->pScaleCtx = sws_getCachedContext(ctx->pScaleCtx, ctx->pVCodecCtx->width, ctx->pVCodecCtx->height, ctx->pVCodecCtx->pix_fmt, ctx->width, ctx->height, AV_PIX_FMT_BGRA, SWS_POINT, 0, 0, 0);\n\t\t\t\tsws_scale(ctx->pScaleCtx, (void*)ctx->pVFrame->data, ctx->pVFrame->linesize, 0, ctx->pVCodecCtx->height, &ctx->rgb_data, &ctx->rgb_linesize);\n\n\t\t\t\tctx->lasttime = ctx->pVFrame->best_effort_timestamp-ctx->timeoffset;\n\n\t\t\t\tif (!ctx->file && curtime - ctx->lasttime > (1 * ctx->denum) / ctx->num)\n\t\t\t\t{\n\t\t\t\t\tctx->timeoffset = ctx->pVFrame->best_effort_timestamp-curtime;\n\t\t\t\t\tctx->lasttime = curtime;\n\t\t\t\t}\n\t\t\t\trepainted = true;\n\t\t\t}\n\t\t}\n\t\telse if(packet.stream_index==ctx->audioStream && !nosound)\n\t\t{\n\t\t\tavcodec_send_packet(ctx->pACodecCtx, &packet);\n\t\t\twhile(0==avcodec_receive_frame(ctx->pACodecCtx, ctx->pAFrame))\n\t\t\t{\n\t\t\t\tint width = 2;\n\t\t\t\tint channels = ctx->pACodecCtx->ch_layout.nb_channels;\n\t\t\t\tunsigned int auddatasize = av_samples_get_buffer_size(NULL, ctx->pACodecCtx->ch_layout.nb_channels, ctx->pAFrame->nb_samples, ctx->pACodecCtx->sample_fmt, 1);\n\t\t\t\tvoid *auddata = ctx->pAFrame->data[0];\n\t\t\t\tswitch(ctx->pACodecCtx->sample_fmt)\n\t\t\t\t{\n\t\t\t\tdefault:\n\t\t\t\t\tauddatasize = 0;\n\t\t\t\t\tbreak;\n\t\t\t\tcase AV_SAMPLE_FMT_U8P:\n\t\t\t\t\tauddatasize /= channels;\n\t\t\t\t\tchannels = 1;\n\t\t\t\tcase AV_SAMPLE_FMT_U8:\n\t\t\t\t\twidth = 1;\n\t\t\t\t\tbreak;\n\t\t\t\tcase AV_SAMPLE_FMT_S16P:\n\t\t\t\t\tauddatasize /= channels;\n\t\t\t\t\tchannels = 1;\n\t\t\t\tcase AV_SAMPLE_FMT_S16:\n\t\t\t\t\twidth = 2;\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase AV_SAMPLE_FMT_FLTP:\n\t\t\t\t\tif (channels == 2)\n\t\t\t\t\t{\n#ifdef MIXER_F32\n\t\t\t\t\t\tfloat *l = (float*)ctx->pAFrame->data[0], *r = (float*)ctx->pAFrame->data[1], *t;\n\t\t\t\t\t\tunsigned int i;\n\t\t\t\t\t\tunsigned int frames = ctx->pAFrame->nb_samples;\n\t\t\t\t\t\twidth = sizeof(*t);\n\t\t\t\t\t\tauddatasize = frames*width*channels;\n\t\t\t\t\t\tt = malloc(auddatasize);\n\t\t\t\t\t\tfor (i = 0; i < frames; i++)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tt[2*i+0] = l[i];\n\t\t\t\t\t\t\tt[2*i+1] = r[i];\n\t\t\t\t\t\t}\n\t\t\t\t\t\taudiofuncs->RawAudio(-1, t, ctx->pACodecCtx->sample_rate, auddatasize/(channels*width), channels, width, 1);\n\t\t\t\t\t\tfree(t);\n\t\t\t\t\t\tcontinue;\n#else\n\t\t\t\t\t\t//note that we can only reformat in place because we are NOT outputting floats.\n\t\t\t\t\t\tfloat *in[2] = {(float*)ctx->pAFrame->data[0],(float*)ctx->pAFrame->data[1]};\n\t\t\t\t\t\tsigned short *out = (void*)auddata;\n\t\t\t\t\t\tint v;\n\t\t\t\t\t\tunsigned int i, c;\n\t\t\t\t\t\tunsigned int frames = ctx->pAFrame->nb_samples;\n\t\t\t\t\t\tfor (i = 0; i < frames; i++)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tfor (c = 0; c < 2; c++)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tv = (short)(in[c][i]*32767);\n\t\t\t\t\t\t\t\tif (v < -32767)\n\t\t\t\t\t\t\t\t\tv = -32767;\n\t\t\t\t\t\t\t\telse if (v > 32767)\n\t\t\t\t\t\t\t\t\tv = 32767;\n\t\t\t\t\t\t\t\t*out++ = v;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\twidth = sizeof(*out);\n\t\t\t\t\t\tauddatasize = frames*width*channels;\n\t\t\t\t\t\tbreak;\n#endif\n\t\t\t\t\t}\n\n\t\t\t\t\tauddatasize /= channels;\n\t\t\t\t\tchannels = 1;\n\t\t\t\t\t//fallthrough, using just the first channel as mono\n\t\t\t\tcase AV_SAMPLE_FMT_FLT:\n#ifdef MIXER_F32\n\t\t\t\t\twidth = 4;\n#else\n\t\t\t\t\t{\n\t\t\t\t\t\tfloat *in = (void*)auddata;\n\t\t\t\t\t\tsigned short *out = (void*)auddata;\n\t\t\t\t\t\tint v;\n\t\t\t\t\t\tunsigned int i;\n\t\t\t\t\t\tfor (i = 0; i < auddatasize/sizeof(*in); i++)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tv = (short)(in[i]*32767);\n\t\t\t\t\t\t\tif (v < -32767)\n\t\t\t\t\t\t\t\tv = -32767;\n\t\t\t\t\t\t\telse if (v > 32767)\n\t\t\t\t\t\t\t\tv = 32767;\n\t\t\t\t\t\t\tout[i] = v;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tauddatasize/=2;\n\t\t\t\t\t\twidth = 2;\n\t\t\t\t\t}\n#endif\n\t\t\t\t\tbreak;\n\t\t\t\tcase AV_SAMPLE_FMT_DBLP:\n\t\t\t\t\tauddatasize /= channels;\n\t\t\t\t\tchannels = 1;\n\t\t\t\tcase AV_SAMPLE_FMT_DBL:\n\t\t\t\t\t{\n\t\t\t\t\t\tdouble *in = (double*)auddata;\n\t\t\t\t\t\tsigned short *out = (void*)auddata;\n\t\t\t\t\t\tint v;\n\t\t\t\t\t\tunsigned int i;\n\t\t\t\t\t\tfor (i = 0; i < auddatasize/sizeof(*in); i++)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tv = (short)(in[i]*32767);\n\t\t\t\t\t\t\tif (v < -32767)\n\t\t\t\t\t\t\t\tv = -32767;\n\t\t\t\t\t\t\telse if (v > 32767)\n\t\t\t\t\t\t\t\tv = 32767;\n\t\t\t\t\t\t\tout[i] = v;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tauddatasize/=4;\n\t\t\t\t\t\twidth = 2;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\taudiofuncs->RawAudio(-1, auddata, ctx->pACodecCtx->sample_rate, auddatasize/(channels*width), channels, width, 1);\n\t\t\t}\n\t\t}\n\n\t\tav_packet_unref(&packet);\n#else\n\n\t\t// Is this a packet from the video stream?\n\t\tif(packet.stream_index==ctx->videoStream)\n\t\t{\n\t\t\t// Decode video frame\n\t\t\tavcodec_decode_video2(ctx->pVCodecCtx, ctx->pVFrame, &frameFinished, &packet);\n\n\t\t\t// Did we get a video frame?\n\t\t\tif(frameFinished)\n\t\t\t{\n\t\t\t\tctx->pScaleCtx = sws_getCachedContext(ctx->pScaleCtx, ctx->pVCodecCtx->width, ctx->pVCodecCtx->height, ctx->pVCodecCtx->pix_fmt, ctx->width, ctx->height, AV_PIX_FMT_BGRA, SWS_POINT, 0, 0, 0);\n\n\t\t\t\t// Convert the image from its native format to RGB\n\t\t\t\tsws_scale(ctx->pScaleCtx, (void*)ctx->pVFrame->data, ctx->pVFrame->linesize, 0, ctx->pVCodecCtx->height, &ctx->rgb_data, &ctx->rgb_linesize);\n\n\t\t\t\trepainted = true;\n\t\t\t}\n#if TARGET_FFMPEG\n\t\t\tctx->lasttime = av_frame_get_best_effort_timestamp(ctx->pVFrame);\n#else\n\t\t\tif(frameFinished)\n\t\t\t{\n\t\t\t\tif (ctx->pVFrame->pkt_pts != AV_NOPTS_VALUE)\n\t\t\t\t\tctx->lasttime = ctx->pVFrame->pkt_pts;\n\t\t\t\telse\n\t\t\t\t\tctx->lasttime = ctx->pVFrame->pkt_dts;\n\t\t\t}\n#endif\n\t\t}\n\t\telse if(packet.stream_index==ctx->audioStream && !nosound)\n\t\t{\n\t\t\tint okay;\n\t\t\tint len;\n\t\t\tvoid *odata = packet.data;\n\t\t\twhile (packet.size > 0)\n\t\t\t{\n\t\t\t\tokay = false;\n\t\t\t\tlen = avcodec_decode_audio4(ctx->pACodecCtx, ctx->pAFrame, &okay, &packet);\n\t\t\t\tif (len < 0)\n\t\t\t\t\tbreak;\n\t\t\t\tpacket.size -= len;\n\t\t\t\tpacket.data += len;\n\t\t\t\tif (okay)\n\t\t\t\t{\n\t\t\t\t\tint width = 2;\n\t\t\t\t\tint channels = ctx->pACodecCtx->ch_layout.nb_channels;\n\t\t\t\t\tunsigned int auddatasize = av_samples_get_buffer_size(NULL, ctx->pACodecCtx->ch_layout.nb_channels, ctx->pAFrame->nb_samples, ctx->pACodecCtx->sample_fmt, 1);\n\t\t\t\t\tvoid *auddata = ctx->pAFrame->data[0];\n\t\t\t\t\tswitch(ctx->pACodecCtx->sample_fmt)\n\t\t\t\t\t{\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tauddatasize = 0;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase AV_SAMPLE_FMT_U8P:\n\t\t\t\t\t\tauddatasize /= channels;\n\t\t\t\t\t\tchannels = 1;\n\t\t\t\t\tcase AV_SAMPLE_FMT_U8:\n\t\t\t\t\t\twidth = 1;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase AV_SAMPLE_FMT_S16P:\n\t\t\t\t\t\tauddatasize /= channels;\n\t\t\t\t\t\tchannels = 1;\n\t\t\t\t\tcase AV_SAMPLE_FMT_S16:\n\t\t\t\t\t\twidth = 2;\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase AV_SAMPLE_FMT_FLTP:\n\t\t\t\t\t\tauddatasize /= channels;\n\t\t\t\t\t\tchannels = 1;\n\t\t\t\t\tcase AV_SAMPLE_FMT_FLT:\n\t\t\t\t\t\t//FIXME: support float audio internally.\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tfloat *in = (void*)auddata;\n\t\t\t\t\t\t\tsigned short *out = (void*)auddata;\n\t\t\t\t\t\t\tint v;\n\t\t\t\t\t\t\tunsigned int i;\n\t\t\t\t\t\t\tfor (i = 0; i < auddatasize/sizeof(*in); i++)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tv = (short)(in[i]*32767);\n\t\t\t\t\t\t\t\tif (v < -32767)\n\t\t\t\t\t\t\t\t\tv = -32767;\n\t\t\t\t\t\t\t\telse if (v > 32767)\n\t\t\t\t\t\t\t\t\tv = 32767;\n\t\t\t\t\t\t\t\tout[i] = v;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tauddatasize/=2;\n\t\t\t\t\t\t\twidth = 2;\n\t\t\t\t\t\t}\n\n\t\t\t\t\tcase AV_SAMPLE_FMT_DBLP:\n\t\t\t\t\t\tauddatasize /= channels;\n\t\t\t\t\t\tchannels = 1;\n\t\t\t\t\tcase AV_SAMPLE_FMT_DBL:\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tdouble *in = (double*)auddata;\n\t\t\t\t\t\t\tsigned short *out = (void*)auddata;\n\t\t\t\t\t\t\tint v;\n\t\t\t\t\t\t\tunsigned int i;\n\t\t\t\t\t\t\tfor (i = 0; i < auddatasize/sizeof(*in); i++)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tv = (short)(in[i]*32767);\n\t\t\t\t\t\t\t\tif (v < -32767)\n\t\t\t\t\t\t\t\t\tv = -32767;\n\t\t\t\t\t\t\t\telse if (v > 32767)\n\t\t\t\t\t\t\t\t\tv = 32767;\n\t\t\t\t\t\t\t\tout[i] = v;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tauddatasize/=4;\n\t\t\t\t\t\t\twidth = 2;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\taudiofuncs->RawAudio(-1, auddata, ctx->pACodecCtx->sample_rate, auddatasize/(channels*width), channels, width, 1);\n\t\t\t\t}\n\t\t\t}\n\t\t\tpacket.data = odata;\n\t\t}\n\n\t\t// Free the packet that was allocated by av_read_frame\n\t\tav_packet_unref(&packet);\n#endif\n\t}\n\n\tif (forcevideo || repainted)\n\t\tuploadtexture(ectx, TF_BGRA32, ctx->width, ctx->height, ctx->rgb_data, NULL);\n\treturn true;\n}\nstatic void AVDec_GetSize (void *vctx, int *width, int *height)\n{\n\tstruct decctx *ctx = (struct decctx*)vctx;\n\t*width = ctx->width;\n\t*height = ctx->height;\n}\n\n/*static void AVDec_CursorMove (void *vctx, float posx, float posy)\n{\n\t//its a video, dumbass\n}\nstatic void AVDec_Key (void *vctx, int code, int unicode, int isup)\n{\n\t//its a video, dumbass\n}\nstatic void AVDec_ChangeStream(void *vctx, char *newstream)\n{\n}\n*/\nstatic void AVDec_Rewind(void *vctx)\n{\n\tstruct decctx *ctx = (struct decctx*)vctx;\n\tif (ctx->lasttime != -1)\n\t{\n\t\tav_seek_frame(ctx->pFormatCtx, -1, 0, AVSEEK_FLAG_FRAME|AVSEEK_FLAG_BACKWARD);\n\t\tavcodec_flush_buffers(ctx->pVCodecCtx);\n\t}\n\tctx->lasttime = -1;\n}\n\n/*\n//avcodec has no way to shut down properly.\nstatic qboolean AVDec_Shutdown(void)\n{\n\treturn 0;\n}\n*/\n\nstatic media_decoder_funcs_t decoderfuncs =\n{\n\tsizeof(media_decoder_funcs_t),\n\tDECODERNAME,\n\tAVDec_Create,\n\tAVDec_DisplayFrame,\n\tAVDec_Destroy,\n\tAVDec_Rewind,\n\n\tNULL,//AVDec_CursorMove,\n\tNULL,//AVDec_Key,\n\tNULL,//AVDec_SetSize,\n\tAVDec_GetSize,\n\tNULL,//AVDec_ChangeStream\n};\n\nqboolean AVDec_Init(void)\n{\n\tfilefuncs = plugfuncs->GetEngineInterface(plugfsfuncs_name, sizeof(*filefuncs));\n\taudiofuncs = plugfuncs->GetEngineInterface(plugaudiofuncs_name, sizeof(*audiofuncs));\n\tif (!filefuncs ||\n\t\t!plugfuncs->ExportInterface(\"Media_VideoDecoder\", &decoderfuncs, sizeof(decoderfuncs)))\n\t{\n\t\tCon_Printf(DECODERNAME\": Engine doesn't support media decoder plugins\\n\");\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n"
  },
  {
    "path": "plugins/avplug/avencode.c",
    "content": "#include \"../plugin.h\"\r\n#include \"../engine.h\"\r\n\r\n#include \"libavformat/avformat.h\"\r\n//#include \"libavformat/avio.h\"\r\n#include \"libavcodec/avcodec.h\"\r\n#include \"libswscale/swscale.h\"\r\n#include \"libavutil/imgutils.h\"\r\n#include \"libavutil/opt.h\"\r\n#include \"libavutil/channel_layout.h\"\r\n\r\n#define TARGET_FFMPEG (LIBAVFORMAT_VERSION_MICRO >= 100)\r\n\r\n#if TARGET_FFMPEG\r\n#define ENCODERNAME \"ffmpeg\"\r\n#else\r\n#define ENCODERNAME \"libav\"\r\n#endif\r\n\r\n#define HAVE_DECOUPLED_API (LIBAVCODEC_VERSION_MAJOR>57 || (LIBAVCODEC_VERSION_MAJOR==57&&LIBAVCODEC_VERSION_MINOR>=36))\r\n\r\n//crappy compat crap\r\n#ifndef AV_CODEC_FLAG_GLOBAL_HEADER\r\n#define AV_CODEC_FLAG_GLOBAL_HEADER CODEC_FLAG_GLOBAL_HEADER\r\n#endif\r\n#ifndef AV_ERROR_MAX_STRING_SIZE\r\n#define AV_ERROR_MAX_STRING_SIZE 64\r\n#endif\r\n\r\nstatic plugfsfuncs_t *filefuncs;\r\n\r\n/*\r\nMost of the logic in here came from here:\r\nhttp://svn.gnumonks.org/tags/21c3-video/upstream/ffmpeg-0.4.9-pre1/output_example.c\r\n*/\r\n\r\nstatic cvar_t *ffmpeg_format_force;\r\nstatic cvar_t *ffmpeg_videocodec;\r\nstatic cvar_t *ffmpeg_videobitrate;\r\nstatic cvar_t *ffmpeg_videoforcewidth;\r\nstatic cvar_t *ffmpeg_videoforceheight;\r\nstatic cvar_t *ffmpeg_videopreset;\r\nstatic cvar_t *ffmpeg_video_crf;\r\nstatic cvar_t *ffmpeg_audiocodec;\r\nstatic cvar_t *ffmpeg_audiobitrate;\r\n\r\nstruct encctx\r\n{\r\n\tchar abspath[MAX_OSPATH];\r\n\tAVFormatContext *fc;\r\n\tqboolean doneheaders;\r\n\r\n\tAVCodecContext *video_codec;\r\n\tAVStream *video_st;\r\n\tstruct SwsContext *scale_ctx;\r\n\tAVFrame *picture;\r\n\tuint8_t *video_outbuf;\r\n\tint video_outbuf_size;\r\n\r\n\tAVCodecContext *audio_codec;\r\n\tAVStream *audio_st;\r\n\tAVFrame *audio;\r\n\tuint8_t *audio_outbuf;\r\n\tuint32_t audio_outcount;\r\n\tint64_t audio_pts;\r\n};\r\n\r\n#define VARIABLE_AUDIO_FRAME_MIN_SIZE 512\t//audio frames smaller than a certain size are just wasteful\r\n#define VARIABLE_AUDIO_FRAME_MAX_SIZE 1024\r\n\r\n#if !TARGET_FFMPEG\r\n#define av_make_error_string qav_make_error_string\r\nstatic inline char *av_make_error_string(char *errbuf, size_t errbuf_size, int errnum)\r\n{\r\n\tav_strerror(errnum, errbuf, errbuf_size);\r\n\treturn errbuf;\r\n}\r\n#endif\r\n\r\nstatic void AVEnc_End (void *ctx);\r\n\r\nstatic AVFrame *alloc_frame(enum AVPixelFormat pix_fmt, int width, int height)\r\n{\r\n\tAVFrame *picture;\r\n\tuint8_t *picture_buf;\r\n\tint size;\r\n\r\n\tpicture = av_frame_alloc();\r\n\tif(!picture)\r\n\t\treturn NULL;\r\n#if TARGET_FFMPEG\r\n\tsize = av_image_get_buffer_size(pix_fmt, width, height, 1);\r\n#else\r\n\tsize = avpicture_get_size(pix_fmt, width, height);\r\n#endif\r\n\tpicture_buf = (uint8_t*)(av_malloc(size));\r\n\tif (!picture_buf)\r\n\t{\r\n\t\tav_free(picture);\r\n\t\treturn NULL;\r\n\t}\r\n#if TARGET_FFMPEG\r\n\tav_image_fill_arrays(picture->data, picture->linesize, picture_buf, pix_fmt, width, height, 1/*fixme: align*/);\r\n#else\r\n\tavpicture_fill((AVPicture*)picture, picture_buf, pix_fmt, width, height);\r\n#endif\r\n\tpicture->width = width;\r\n\tpicture->height = height;\r\n\treturn picture;\r\n}\r\nstatic AVStream *add_video_stream(struct encctx *ctx, const AVCodec *codec, int fps, int width, int height)\r\n{\r\n\tAVCodecContext *c;\r\n\tAVStream *st;\r\n\tint bitrate = ffmpeg_videobitrate->value;\r\n\tint forcewidth = ffmpeg_videoforcewidth->value;\r\n\tint forceheight = ffmpeg_videoforceheight->value;\r\n\r\n#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 48, 101)\r\n\tst = avformat_new_stream(ctx->fc, NULL);\r\n\tif (!st)\r\n\t\treturn NULL;\r\n\tst->id = ctx->fc->nb_streams-1;\r\n\tc = avcodec_alloc_context3(codec);\r\n#else\r\n\tst = avformat_new_stream(ctx->fc, codec);\r\n\tif (!st)\r\n\t\treturn NULL;\r\n\r\n\tc = st->codec;\r\n\tst->id = ctx->fc->nb_streams-1;\r\n#endif\r\n\tctx->video_codec = c;\r\n\tc->codec_id = codec->id;\r\n\tc->codec_type = codec->type;\r\n\r\n\t/* put sample parameters */\r\n\tif (bitrate)\r\n\t\tc->bit_rate = bitrate;\r\n//\tc->rc_max_rate = bitrate;\r\n//\tc->rc_min_rate = bitrate;\r\n\t/* resolution must be a multiple of two */\r\n\tc->width = forcewidth?forcewidth:width;\r\n\tc->height = forceheight?forceheight:height;\r\n\t/* frames per second */\r\n\tc->time_base.num = 1;\r\n\tc->time_base.den = fps;\r\n\t//c->gop_size = 12; /* emit one intra frame every twelve frames at most */\r\n\tc->pix_fmt       = AV_PIX_FMT_YUV420P;\r\n\tif (c->codec_id == AV_CODEC_ID_MPEG2VIDEO)\r\n\t{\r\n\t\t/* just for testing, we also add B frames */\r\n\t\tc->max_b_frames = 2;\r\n\t}\r\n\tif (c->codec_id == AV_CODEC_ID_MPEG1VIDEO)\r\n\t{\r\n\t\t/* needed to avoid using macroblocks in which some coeffs overflow \r\n\t\t   this doesnt happen with normal video, it just happens here as the \r\n\t\t   motion of the chroma plane doesnt match the luma plane */\r\n//\t\tc->mb_decision=2;\r\n\t}\r\n\t// some formats want stream headers to be seperate\r\n\tif (ctx->fc->oformat->flags & AVFMT_GLOBALHEADER)\r\n\t\tc->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;\r\n\r\n\tif (*ffmpeg_videopreset->string)\r\n\t\tav_opt_set(c->priv_data, \"preset\", ffmpeg_videopreset->string, AV_OPT_SEARCH_CHILDREN);\r\n\tif (*ffmpeg_video_crf->string)\r\n\t\tav_opt_set(c->priv_data, \"crf\", ffmpeg_video_crf->string, AV_OPT_SEARCH_CHILDREN);\r\n\r\n\treturn st;\r\n}\r\nstatic void close_video(struct encctx *ctx)\r\n{\r\n\tif (!ctx->video_st)\r\n\t\treturn;\r\n\r\n\tavcodec_free_context(&ctx->video_codec);\r\n\tif (ctx->picture)\r\n\t{\r\n\t\tav_free(ctx->picture->data[0]);\r\n\t\tav_free(ctx->picture);\r\n\t}\r\n\tav_free(ctx->video_outbuf);\r\n}\r\n\r\n#if HAVE_DECOUPLED_API\r\n//frame can be null on eof.\r\nstatic void AVEnc_DoEncode(AVFormatContext *fc, AVStream *stream, AVCodecContext *codec, AVFrame *frame)\r\n{\r\n\tAVPacket *pkt = av_packet_alloc();\r\n\tint err = avcodec_send_frame(codec, frame);\r\n\tif (err)\r\n\t{\r\n\t\tchar buf[512];\r\n\t\tCon_Printf(\"avcodec_send_frame: error: %s\\n\", av_make_error_string(buf, sizeof(buf), err));\r\n\t}\r\n\r\n\twhile (!(err=avcodec_receive_packet(codec, pkt)))\r\n\t{\r\n\t\tav_packet_rescale_ts(pkt, codec->time_base, stream->time_base);\r\n\t\tpkt->stream_index = stream->index;\r\n\t\terr = av_interleaved_write_frame(fc, pkt);\r\n\t\tif (err)\r\n\t\t{\r\n\t\t\tchar buf[512];\r\n\t\t\tCon_Printf(\"av_interleaved_write_frame: error: %s\\n\", av_make_error_string(buf, sizeof(buf), err));\r\n\t\t}\r\n\t\tav_packet_unref(pkt);\r\n\t}\r\n\tif (err && err != AVERROR(EAGAIN) && err != AVERROR_EOF)\r\n\t{\r\n\t\tchar buf[512];\r\n\t\tCon_Printf(\"avcodec_receive_packet: error: %s\\n\", av_make_error_string(buf, sizeof(buf), err));\r\n\t}\r\n\tav_packet_free(&pkt);\r\n}\r\n#endif\r\n\r\nstatic void AVEnc_Video (void *vctx, int frameno, void *data, int bytestride, int width, int height, enum uploadfmt qpfmt)\r\n{\r\n\tstruct encctx *ctx = vctx;\r\n\tconst uint8_t *srcslices[4];\r\n\tint srcstride[4];\r\n\tint avpfmt;\r\n\r\n\tif (!ctx->video_st)\r\n\t\treturn;\r\n\r\n\tswitch(qpfmt)\r\n\t{\r\n\tcase TF_BGRA32: avpfmt = AV_PIX_FMT_BGRA; break;\r\n\tcase TF_RGBA32: avpfmt = AV_PIX_FMT_RGBA; break;\r\n#if TARGET_FFMPEG\r\n\tcase TF_BGRX32: avpfmt = AV_PIX_FMT_BGR0; break;\r\n\tcase TF_RGBX32: avpfmt = AV_PIX_FMT_RGB0; break;\r\n#endif\r\n\tcase TF_BGR24: avpfmt = AV_PIX_FMT_BGR24; break;\r\n\tcase TF_RGB24: avpfmt = AV_PIX_FMT_RGB24; break;\r\n\tdefault:\r\n\t\treturn;\r\n\t}\r\n\r\n\tif (bytestride < 0)\t//fix up the buffers so callers don't have to.\r\n\t\tdata = (char*)data - bytestride*(height-1);\r\n\r\n\t//weird maths to flip it.\r\n\tsrcslices[0] = (uint8_t*)data;\r\n\tsrcstride[0] = bytestride;\r\n\tsrcslices[1] = NULL;\r\n\tsrcstride[1] = 0;\r\n\tsrcslices[2] = NULL;\t//libav's version probably needs this excess\r\n\tsrcstride[2] = 0;\r\n\tsrcslices[3] = NULL;\r\n\tsrcstride[3] = 0;\r\n\r\n\t//fixme: it would be nice to avoid copies here if possible...\r\n\t//convert RGB to whatever the codec needs (ie: yuv...).\r\n\t//also rescales, but only if the user resizes the video while recording. which is a stupid thing to do.\r\n\tctx->scale_ctx = sws_getCachedContext(ctx->scale_ctx, width, height, avpfmt, ctx->picture->width, ctx->picture->height, ctx->video_codec->pix_fmt, SWS_POINT, 0, 0, 0);\r\n\tsws_scale(ctx->scale_ctx, srcslices, srcstride, 0, height, ctx->picture->data, ctx->picture->linesize);\r\n\r\n\tctx->picture->pts = frameno;\r\n\tctx->picture->format = ctx->video_codec->pix_fmt;\r\n#if HAVE_DECOUPLED_API\r\n\tAVEnc_DoEncode(ctx->fc, ctx->video_st, ctx->video_codec, ctx->picture);\r\n#else\r\n\t{\r\n\t\tint success;\r\n\t\tint err;\r\n\t\tAVPacket *pkt = av_packet_alloc();\r\n\r\n\t\tpkt->data = ctx->video_outbuf;\r\n\t\tpkt->size = ctx->video_outbuf_size;\r\n\t\tsuccess = 0;\r\n\t\terr = avcodec_encode_video2(ctx->video_codec, pkt, ctx->picture, &success);\r\n\t\tif (err)\r\n\t\t{\r\n\t\t\tchar buf[512];\r\n\t\t\tCon_Printf(\"avcodec_encode_video2: error: %s\\n\", av_make_error_string(buf, sizeof(buf), err));\r\n\t\t}\r\n\t\telse if (err == 0 && success)\r\n\t\t{\r\n\t\t\tav_packet_rescale_ts(pkt, ctx->video_codec->time_base, ctx->video_st->time_base);\r\n\t\t\tpkt->stream_index = ctx->video_st->index;\r\n\t\t\terr = av_interleaved_write_frame(ctx->fc, pkt);\r\n\t\t\r\n\t\t\tif (err)\r\n\t\t\t{\r\n\t\t\t\tchar buf[512];\r\n\t\t\t\tCon_Printf(\"av_interleaved_write_frame: error: %s\\n\", av_make_error_string(buf, sizeof(buf), err));\r\n\t\t\t}\r\n\t\t}\r\n\t\tav_packet_free(&pkt);\r\n\t}\r\n#endif\r\n}\r\n\r\nstatic AVStream *add_audio_stream(struct encctx *ctx, const AVCodec *codec, int *samplerate, int *bits, int channels)\r\n{\r\n\tAVCodecContext *c;\r\n\tAVStream *st;\r\n\tint bitrate = ffmpeg_audiobitrate->value;\r\n\tint num_sample_fmts = 0;\r\n\tenum AVSampleFormat *sample_fmts;\r\n\r\n#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(57, 48, 101)\r\n\tst = avformat_new_stream(ctx->fc, codec);\r\n\tif (!st)\r\n\t\treturn NULL;\r\n\r\n\tc = st->codec;\r\n#else\r\n\tst = avformat_new_stream(ctx->fc, NULL);\r\n\tif (!st)\r\n\t\treturn NULL;\r\n\r\n\tc = avcodec_alloc_context3(codec);\r\n\tif(avcodec_parameters_to_context(c, st->codecpar))\r\n\t\treturn NULL;\r\n#endif\r\n\tst->id = ctx->fc->nb_streams-1;\r\n\tctx->audio_codec = c;\r\n\tc->codec_id = codec->id;\r\n\tc->codec_type = codec->type;\r\n\r\n//\tif (c->codec_id == AV_CODEC_ID_OPUS)\t//opus is strictly 48khz. force that here.\r\n//\t\t*samplerate = 48000;\t\t\t\t//FIXME: the engine can't cope with this.\r\n\r\n\t/* put sample parameters */\r\n\tc->bit_rate = bitrate;\r\n\t/* frames per second */\r\n\tc->time_base.num = 1;\r\n\tc->time_base.den = *samplerate;\r\n\tc->sample_rate = *samplerate;\r\n\tc->ch_layout.nb_channels = channels;\r\n\tav_channel_layout_default(&c->ch_layout, channels);\r\n\r\n\tavcodec_get_supported_config(c, codec, AV_CODEC_CONFIG_SAMPLE_FORMAT, 0, (const void **)&sample_fmts, &num_sample_fmts);\r\n\r\n\tc->sample_fmt = sample_fmts[0];\r\n\r\n//\tif (c->sample_fmt == AV_SAMPLE_FMT_FLTP || c->sample_fmt == AV_SAMPLE_FMT_FLT)\r\n//\t\t*bits = 32;\t//get the engine to mix 32bit audio instead of whatever its currently set to.\r\n//\telse if (c->sample_fmt == AV_SAMPLE_FMT_U8P || c->sample_fmt == AV_SAMPLE_FMT_U8)\r\n//\t\t*bits = 8;\t//get the engine to mix 32bit audio instead of whatever its currently set to.\r\n//\telse if (c->sample_fmt == AV_SAMPLE_FMT_S16P || c->sample_fmt == AV_SAMPLE_FMT_S16)\r\n//\t\t*bits = 16;\r\n//\telse\r\n\t\t*bits = 32;\t//ask for float audio.\r\n\r\n\tc->strict_std_compliance = FF_COMPLIANCE_EXPERIMENTAL;\r\n\r\n\t// some formats want stream headers to be seperate\r\n\tif (ctx->fc->oformat->flags & AVFMT_GLOBALHEADER)\r\n\t\tc->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;\r\n\r\n//\tavcodec_parameters_from_context(st->codecpar, c);\r\n\r\n\treturn st;\r\n}\r\nstatic void close_audio(struct encctx *ctx)\r\n{\r\n\tif (!ctx->audio_st)\r\n\t\treturn;\r\n\r\n\tavcodec_free_context(&ctx->audio_codec);\r\n}\r\nstatic void AVEnc_Audio (void *vctx, void *data, int bytes)\r\n{\r\n\tstruct encctx *ctx = vctx;\r\n\r\n\tif (!ctx->audio_st)\r\n\t\treturn;\r\n\r\n\twhile (bytes)\r\n\t{\r\n\t\tint i, p, chans = ctx->audio_codec->ch_layout.nb_channels;\r\n\t\tint blocksize = sizeof(float)*chans;\r\n\t\tint count = bytes / blocksize;\r\n\t\tint planesize = ctx->audio_codec->frame_size;\r\n\t\tfloat *in;\r\n\t\tint offset;\r\n\r\n\t\tif (!planesize)\t//variable-sized frames. yay\r\n\t\t{\r\n\t\t\tplanesize = count;\r\n\t\t\tif (count > VARIABLE_AUDIO_FRAME_MAX_SIZE - ctx->audio_outcount)\r\n\t\t\t\tcount = VARIABLE_AUDIO_FRAME_MAX_SIZE - ctx->audio_outcount;\r\n\t\t}\r\n\t\telse if (count > ctx->audio_codec->frame_size - ctx->audio_outcount)\r\n\t\t\tcount = ctx->audio_codec->frame_size - ctx->audio_outcount;\r\n\r\n\t\tin = (float*)data;\r\n\t\toffset = ctx->audio_outcount;\r\n\t\tctx->audio_outcount += count;\r\n\t\tdata = (qbyte*)data + count * blocksize;\r\n\t\tbytes -= count * blocksize;\r\n\r\n\t\t//input is always float audio, because I'm lazy.\r\n\t\t//output is whatever the codec needs (may be packed or planar, gah).\r\n\t\t//the engine's mixer will do all rate scaling for us, as well as channel selection\r\n\t\tswitch(ctx->audio_codec->sample_fmt)\r\n\t\t{\r\n\t\tcase AV_SAMPLE_FMT_DBL:\r\n\t\t\toffset *= chans;\r\n\t\t\tcount *= chans;\r\n\t\t\tplanesize *= chans;\r\n\t\t\tchans = 1;\r\n\t\tcase AV_SAMPLE_FMT_DBLP:\r\n\t\t\tfor (p = 0; p < chans; p++)\r\n\t\t\t{\r\n\t\t\t\tdouble *f = (double*)ctx->audio_outbuf + p*planesize + offset;\r\n\t\t\t\tfor (i = 0; i < count*chans; i+=chans)\r\n\t\t\t\t\t*f++ = in[i];\r\n\t\t\t\tin++;\r\n\t\t\t}\r\n\t\t\tbreak;\r\n\t\tcase AV_SAMPLE_FMT_FLT:\r\n\t\t\toffset *= chans;\r\n\t\t\tcount *= chans;\r\n\t\t\tplanesize *= chans;\r\n\t\t\tchans = 1;\r\n\t\tcase AV_SAMPLE_FMT_FLTP:\r\n\t\t\tfor (p = 0; p < chans; p++)\r\n\t\t\t{\r\n\t\t\t\tfloat *f = (float *)ctx->audio_outbuf + p*planesize + offset;\r\n\t\t\t\tfor (i = 0; i < count*chans; i+=chans)\r\n\t\t\t\t\t*f++ = in[i];\r\n\t\t\t\tin++;\r\n\t\t\t}\r\n\t\t\tbreak;\r\n\t\tcase AV_SAMPLE_FMT_S32:\r\n\t\t\toffset *= chans;\r\n\t\t\tcount *= chans;\r\n\t\t\tplanesize *= chans;\r\n\t\t\tchans = 1;\r\n\t\tcase AV_SAMPLE_FMT_S32P:\r\n\t\t\tfor (p = 0; p < chans; p++)\r\n\t\t\t{\r\n\t\t\t\tint32_t *f = (int32_t *)ctx->audio_outbuf + p*planesize + offset;\r\n\t\t\t\tfor (i = 0; i < count*chans; i+=chans)\r\n\t\t\t\t\t*f++ = bound(0x80000000, (int)(in[i] * (float)0x7fffffff), 0x7fffffff);\r\n\t\t\t\tin++;\r\n\t\t\t}\r\n\t\t\tbreak;\r\n\t\tcase AV_SAMPLE_FMT_S16:\r\n\t\t\toffset *= chans;\r\n\t\t\tcount *= chans;\r\n\t\t\tplanesize *= chans;\r\n\t\t\tchans = 1;\r\n\t\tcase AV_SAMPLE_FMT_S16P:\r\n\t\t\tfor (p = 0; p < chans; p++)\r\n\t\t\t{\r\n\t\t\t\tint16_t *f = (int16_t *)ctx->audio_outbuf + p*planesize + offset;\r\n\t\t\t\tfor (i = 0; i < count*chans; i+=chans)\r\n\t\t\t\t\t*f++ = bound(-32768, (int)(in[i] * 32767), 32767);\r\n\r\n\t\t\t\t//sin((ctx->audio_pts+ctx->audio_outcount-count+i/chans)*0.1) * 32767;//\r\n\t\t\t\tin++;\r\n\t\t\t}\r\n\t\t\tbreak;\r\n\t\tcase AV_SAMPLE_FMT_U8:\r\n\t\t\toffset *= chans;\r\n\t\t\tcount *= chans;\r\n\t\t\tplanesize *= chans;\r\n\t\t\tchans = 1;\r\n\t\tcase AV_SAMPLE_FMT_U8P:\r\n\t\t\tfor (p = 0; p < chans; p++)\r\n\t\t\t{\r\n\t\t\t\tuint8_t *f = (uint8_t*)ctx->audio_outbuf + p*planesize + offset;\r\n\t\t\t\tfor (i = 0; i < count*chans; i+=chans)\r\n\t\t\t\t\t*f++ = bound(0, 128+(int)(in[i] * 127), 255);\r\n\t\t\t\tin++;\r\n\t\t\t}\r\n\t\t\tbreak;\r\n\t\tdefault:\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\tif (ctx->audio_codec->frame_size)\r\n\t\t{\r\n\t\t\tif (ctx->audio_outcount < ctx->audio_codec->frame_size)\r\n\t\t\t\tbreak;\t//not enough data yet.\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tif (ctx->audio_outcount < VARIABLE_AUDIO_FRAME_MIN_SIZE)\r\n\t\t\t\tbreak;\t//not enough data yet.\r\n\t\t}\r\n\r\n\t\tctx->audio->nb_samples = ctx->audio_outcount;\r\n\t\tavcodec_fill_audio_frame(ctx->audio, ctx->audio_codec->ch_layout.nb_channels, ctx->audio_codec->sample_fmt, ctx->audio_outbuf, av_get_bytes_per_sample(ctx->audio_codec->sample_fmt)*ctx->audio_outcount*ctx->audio_codec->ch_layout.nb_channels, 1);\r\n\t\tctx->audio->pts = ctx->audio_pts;\r\n\t\tctx->audio_pts += ctx->audio_outcount;\r\n\t\tctx->audio_outcount = 0;\r\n\r\n#if HAVE_DECOUPLED_API\r\n\t\tAVEnc_DoEncode(ctx->fc, ctx->audio_st, ctx->audio_codec, ctx->audio);\r\n#else\r\n\t\t{\r\n\t\t\tint success;\r\n\t\t\tint err;\r\n\t\t\tAVPacket *pkt = av_packet_alloc();\r\n\t\t\tpkt->data = NULL;\r\n\t\t\tpkt->size = 0;\r\n\t\t\tsuccess = 0;\r\n\t\t\terr = avcodec_encode_audio2(ctx->audio_codec, pkt, ctx->audio, &success);\r\n\r\n\t\t\tif (err)\r\n\t\t\t{\r\n\t\t\t\tchar buf[512];\r\n\t\t\t\tCon_Printf(\"avcodec_encode_audio2: error: %s\\n\", av_make_error_string(buf, sizeof(buf), err));\r\n\t\t\t}\r\n\t\t\telse if (success)\r\n\t\t\t{\r\n\t\t//\t\tpkt.pts = ctx->audio_codec->coded_frame->pts;\r\n\t\t//\t\tif(ctx->audio_codec->coded_frame->key_frame)\r\n\t\t//\t\t\tpkt.flags |= AV_PKT_FLAG_KEY;\r\n\r\n\t\t\t\tav_packet_rescale_ts(pkt, ctx->audio_codec->time_base, ctx->audio_st->time_base);\r\n\t\t\t\tpkt->stream_index = ctx->audio_st->index;\r\n\t\t\t\terr = av_interleaved_write_frame(ctx->fc, pkt);\r\n\t\t\t\tif (err)\r\n\t\t\t\t{\r\n\t\t\t\t\tchar buf[512];\r\n\t\t\t\t\tCon_Printf(\"av_interleaved_write_frame: error: %s\\n\", av_make_error_string(buf, sizeof(buf), err));\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\tav_packet_free(&pkt);\r\n\t\t}\r\n#endif\r\n\t}\r\n}\r\n\r\nstatic void *AVEnc_Begin (char *streamname, int videorate, int width, int height, int *sndkhz, int *sndchannels, int *sndbits)\r\n{\r\n\tstruct encctx *ctx;\r\n\tconst AVOutputFormat *fmt = NULL;\r\n\tconst AVCodec *videocodec = NULL;\r\n\tconst AVCodec *audiocodec = NULL;\r\n\tint err;\r\n\tchar errtxt[AV_ERROR_MAX_STRING_SIZE] = {0};\r\n\r\n\tif (*ffmpeg_format_force->string)\r\n\t{\r\n\t\tfmt = av_guess_format(ffmpeg_format_force->string, NULL, NULL);\r\n\t\tif (!fmt)\r\n\t\t{\r\n\t\t\tCon_Printf(\"Unknown format specified: %s.\\n\", ffmpeg_format_force->string);\r\n\t\t\treturn NULL;\r\n\t\t}\r\n\t}\r\n\tif (!fmt)\r\n\t\tfmt = av_guess_format(NULL, streamname, NULL);\r\n\tif (!fmt)\r\n\t{\r\n\t\tCon_DPrintf(\"Could not deduce output format from file extension: using MPEG.\\n\");\r\n\t\tfmt = av_guess_format(\"mpeg\", NULL, NULL);\r\n\t}\r\n\tif (!fmt)\r\n\t{\r\n\t\tCon_Printf(\"Format not known\\n\");\r\n\t\treturn NULL;\r\n\t}\r\n\r\n\tif (videorate)\r\n\t{\r\n\t\tif (strcmp(ffmpeg_videocodec->string, \"none\"))\r\n\t\t{\r\n\t\t\tif (ffmpeg_videocodec->string[0])\r\n\t\t\t{\r\n\t\t\t\tvideocodec = avcodec_find_encoder_by_name(ffmpeg_videocodec->string);\r\n\t\t\t\tif (!videocodec)\r\n\t\t\t\t{\r\n\t\t\t\t\tCon_Printf(\"Unsupported %s \\\"%s\\\"\\n\", ffmpeg_videocodec->name, ffmpeg_videocodec->string);\r\n\t\t\t\t\treturn NULL;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\tif (!videocodec && fmt->video_codec != AV_CODEC_ID_NONE)\r\n\t\t\t\tvideocodec = avcodec_find_encoder(fmt->video_codec);\r\n\t\t}\r\n\t}\r\n\tif (*sndkhz)\r\n\t{\r\n\t\tif (strcmp(ffmpeg_audiocodec->string, \"none\"))\r\n\t\t{\r\n\t\t\tif (ffmpeg_audiocodec->string[0])\r\n\t\t\t{\r\n\t\t\t\taudiocodec = avcodec_find_encoder_by_name(ffmpeg_audiocodec->string);\r\n\t\t\t\tif (!audiocodec)\r\n\t\t\t\t{\r\n\t\t\t\t\tCon_Printf(ENCODERNAME\": Unsupported %s \\\"%s\\\"\\n\", ffmpeg_audiocodec->name, ffmpeg_audiocodec->string);\r\n\t\t\t\t\treturn NULL;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\tif (!audiocodec && fmt->audio_codec != AV_CODEC_ID_NONE)\r\n\t\t\t\taudiocodec = avcodec_find_encoder(fmt->audio_codec);\r\n\t\t}\r\n\t}\r\n\r\n\tCon_DPrintf(ENCODERNAME\": Using format \\\"%s\\\"\\n\", fmt->name);\r\n\tif (videocodec)\r\n\t\tCon_DPrintf(ENCODERNAME\": Using Video Codec \\\"%s\\\"\\n\", videocodec->name);\r\n\telse\r\n\t\tCon_DPrintf(ENCODERNAME\": Not encoding video\\n\");\r\n\tif (audiocodec)\r\n\t\tCon_DPrintf(ENCODERNAME\": Using Audio Codec \\\"%s\\\"\\n\", audiocodec->name);\r\n\telse\r\n\t\tCon_DPrintf(ENCODERNAME\": Not encoding audio\\n\");\r\n\r\n\tif (!videocodec && !audiocodec)\r\n\t{\r\n\t\tCon_DPrintf(ENCODERNAME\": Nothing to encode!\\n\");\r\n\t\treturn NULL;\r\n\t}\r\n\r\n\tif (!audiocodec)\r\n\t\t*sndkhz = 0;\r\n\r\n\tctx = malloc(sizeof(*ctx));\r\n\tif (!ctx)\r\n\t\treturn NULL;\r\n\tmemset(ctx, 0, sizeof(*ctx));\r\n\r\n\tctx->fc = avformat_alloc_context();\r\n\tctx->fc->oformat = fmt;\r\n#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(58, 6, 100)\r\n\tQ_strncatz(ctx->fc->filename, streamname, sizeof(ctx->fc->filename));\r\n#else\r\n\tctx->fc->url = av_strdup(streamname);\r\n#endif\r\n\r\n\t//pick default codecs\r\n\tctx->video_st = NULL;\r\n\tctx->audio_st = NULL;\r\n\tif (videocodec)\r\n\t\tctx->video_st = add_video_stream(ctx, videocodec, videorate, width, height);\r\n\tif (audiocodec)\r\n\t\tctx->audio_st = add_audio_stream(ctx, audiocodec, sndkhz, sndbits, *sndchannels);\r\n\r\n\tif (ctx->video_st)\r\n\t{\r\n\t\tAVCodecContext *c = ctx->video_codec;\r\n\t\terr = avcodec_open2(c, videocodec, NULL);\r\n\t\tif (err < 0)\r\n\t\t{\r\n\t\t\tCon_Printf(ENCODERNAME\": Could not init codec instance \\\"%s\\\" - %s\\nMaybe try a different framerate/resolution/bitrate\\n\", videocodec->name, av_make_error_string(errtxt, sizeof(errtxt), err));\r\n\t\t\tAVEnc_End(ctx);\r\n\t\t\treturn NULL;\r\n\t\t}\r\n\r\n\t\tctx->picture = alloc_frame(c->pix_fmt, c->width, c->height);\r\n\r\n\t\tctx->video_outbuf_size = 200000;\r\n\t\tctx->video_outbuf = av_malloc(ctx->video_outbuf_size);\r\n\t\tif (!ctx->video_outbuf)\r\n\t\t\tctx->video_outbuf_size = 0;\r\n\r\n#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 48, 101)\r\n\t\t//copy the avcodec parameters over to avformat\r\n\t\terr = avcodec_parameters_from_context(ctx->video_st->codecpar, c);\r\n\t\tif(err < 0)\r\n\t\t{\r\n\t\t\tAVEnc_End(ctx);\r\n\t\t\treturn NULL;\r\n\t\t}\r\n#endif\r\n\t}\r\n\tif (ctx->audio_st)\r\n\t{\r\n\t\tint sz;\r\n\t\tAVCodecContext *c = ctx->audio_codec;\r\n\t\terr = avcodec_open2(c, audiocodec, NULL);\r\n\t\tif (err < 0)\r\n\t\t{\r\n\t\t\tCon_Printf(ENCODERNAME\": Could not init codec instance \\\"%s\\\" - %s\\n\", audiocodec->name, av_make_error_string(errtxt, sizeof(errtxt), err));\r\n\t\t\tAVEnc_End(ctx);\r\n\t\t\treturn NULL;\r\n\t\t}\r\n\r\n\t\tctx->audio = av_frame_alloc();\r\n\t\tsz = ctx->audio_codec->frame_size;\r\n\t\tif (!sz)\r\n\t\t\tsz = VARIABLE_AUDIO_FRAME_MAX_SIZE;\r\n\t\tsz *= av_get_bytes_per_sample(ctx->audio_codec->sample_fmt) * ctx->audio_codec->ch_layout.nb_channels;\r\n\t\tctx->audio_outbuf = av_malloc(sz);\r\n\r\n#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 48, 101)\r\n\t\t//copy the avcodec parameters over to avformat\r\n\t\terr = avcodec_parameters_from_context(ctx->audio_st->codecpar, c);\r\n\t\tif(err < 0)\r\n\t\t{\r\n\t\t\tAVEnc_End(ctx);\r\n\t\t\treturn NULL;\r\n\t\t}\r\n#endif\r\n\t}\r\n\r\n\tav_dump_format(ctx->fc, 0, streamname, 1);\r\n\r\n\tif (!(fmt->flags & AVFMT_NOFILE))\r\n\t{\r\n\t\t//okay, this is annoying, but I'm too lazy to figure out the issue I was having with avio stuff.\r\n\t\tif (!filefuncs->NativePath(streamname, FS_GAMEONLY, ctx->abspath, sizeof(ctx->abspath)))\r\n\t\t{\r\n\t\t\tCon_Printf(\"Couldn't find system path for '%s'\\n\", streamname);\r\n\t\t\tAVEnc_End(ctx);\r\n\t\t\treturn NULL;\r\n\t\t}\r\n\t\terr = avio_open(&ctx->fc->pb, ctx->abspath, AVIO_FLAG_WRITE);\r\n\t\tif (err < 0)\r\n\t\t{\r\n\t\t\tCon_Printf(\"Could not open '%s' - %s\\n\", ctx->abspath, av_make_error_string(errtxt, sizeof(errtxt), err));\r\n\t\t\tAVEnc_End(ctx);\r\n\t\t\treturn NULL;\r\n\t\t}\r\n\t}\r\n\telse\r\n\t{\r\n\t\tstrncpy(ctx->abspath, \"<STREAM>\", sizeof(ctx->abspath)-1);\r\n\t}\r\n\r\n\t//different formats have different metadata formats. there's no standards here.\r\n\t//av_dict_set(&ctx->fc-\t>metadata, \"TPFL\", \"testtest\", 0);\r\n\t//FIXME: use ffmpeg's sidedata stuff, which should handle it in a generic way\r\n\r\n\t//nearly complete, can make the file dirty now.\r\n\terr = avformat_write_header(ctx->fc, NULL);\r\n\tif (err < 0)\r\n\t{\r\n\t\tCon_Printf(\"avformat_write_header(%s): failed %s\\n\", ctx->abspath, av_make_error_string(errtxt, sizeof(errtxt), err));\r\n\t\tif (ctx->video_st)\r\n\t\t\tCon_Printf(\"  Video %s: %i * %i\\n\", ctx->video_codec->codec->name, width, height);\r\n\t\tif (ctx->audio_st)\r\n\t\t\tCon_Printf(\"  Audio %s: %i channels, %ibit @ %ikhz\\n\", ctx->audio_codec->codec->name, *sndchannels, *sndbits, *sndkhz);\r\n\t\tAVEnc_End(ctx);\r\n\t\treturn NULL;\r\n\t}\r\n\tctx->doneheaders = true;\r\n\treturn ctx;\r\n}\r\nstatic void AVEnc_End (void *vctx)\r\n{\r\n\tstruct encctx *ctx = vctx;\r\n\tunsigned int i;\r\n\r\n#if HAVE_DECOUPLED_API\r\n\tif (ctx->doneheaders)\r\n\t{\r\n\t\t//terminate the codecs properly, flushing all unwritten packets\r\n\t\tif (ctx->video_st)\r\n\t\t\tAVEnc_DoEncode(ctx->fc, ctx->video_st, ctx->video_codec, NULL);\r\n\t\tif (ctx->audio_st)\r\n\t\t\tAVEnc_DoEncode(ctx->fc, ctx->audio_st, ctx->audio_codec, NULL);\r\n\t}\r\n#endif\r\n\r\n\t//don't write trailers if this is an error case and we never even wrote the headers.\r\n\tif (ctx->doneheaders)\r\n\t{\r\n\t\tav_write_trailer(ctx->fc);\r\n\t\tif (*ctx->abspath)\r\n\t\t\tCon_Printf(\"Finished writing %s\\n\", ctx->abspath);\r\n\t}\r\n\r\n\tclose_video(ctx);\r\n\tclose_audio(ctx);\r\n\r\n\tfor(i = 0; i < ctx->fc->nb_streams; i++)\r\n\t\tav_freep(&ctx->fc->streams[i]);\r\n//\tif (!(fmt->flags & AVFMT_NOFILE))\r\n\t\tavio_close(ctx->fc->pb);\r\n\tav_free(ctx->audio_outbuf);\r\n\tavformat_free_context(ctx->fc);\r\n\tfree(ctx);\r\n}\r\nstatic media_encoder_funcs_t encoderfuncs =\r\n{\r\n\tsizeof(media_encoder_funcs_t),\r\n\t\"ffmpeg\",\r\n\t\"Use ffmpeg's various codecs. Various settings are configured with the \"ENCODERNAME\"_* cvars.\",\r\n\t\".mp4;.*\",\r\n\tAVEnc_Begin,\r\n\tAVEnc_Video,\r\n\tAVEnc_Audio,\r\n\tAVEnc_End\r\n};\r\n\r\n\r\nstatic void AVEnc_Preset_Nvidia_f(void)\r\n{\r\n\tcvarfuncs->SetString(\"capturedriver\", ENCODERNAME);\t//be sure to use our encoder\r\n\tcvarfuncs->SetString(ENCODERNAME\"_videocodec\", \"h264_nvenc\");\r\n\tcvarfuncs->SetString(\"capturerate\", \"60\");\t//we should be able to cope with it, and the default of 30 sucks\r\n\tcvarfuncs->SetString(\"capturedemowidth\", \"1920\");\t//force a specific size, some codecs need multiples of 16 or whatever.\r\n\tcvarfuncs->SetString(\"capturedemoheight\", \"1080\");\t//so this avoids issues with various video codecs.\r\n\r\n\tcvarfuncs->SetString(\"capturesound\", \"1\");\r\n\tcvarfuncs->SetString(\"capturesoundchannels\", \"2\");\r\n\tcvarfuncs->SetString(\"capturesoundbits\", \"16\");\r\n\r\n\tCon_Printf(ENCODERNAME\": now configured for nvidia's hardware encoder\\n\");\r\n\tCon_Printf(ENCODERNAME\": use ^[/capture foo.mp4^] or ^[/capturedemo foo.mvd foo.mkv^] commands to begin capturing\\n\");\r\n}\r\nvoid AVEnc_Preset_Defaults_f(void)\r\n{\t//most formats will end up using the x264 encoder or something\r\n\tcvarfuncs->SetString(ENCODERNAME\"_format_force\", \"\");\r\n\tcvarfuncs->SetString(ENCODERNAME\"_videocodec\", \"\");\r\n\tcvarfuncs->SetString(ENCODERNAME\"_videobitrate\", \"\");\r\n\tcvarfuncs->SetString(ENCODERNAME\"_videoforcewidth\", \"\");\r\n\tcvarfuncs->SetString(ENCODERNAME\"_videoforceheight\", \"\");\r\n\tcvarfuncs->SetString(ENCODERNAME\"_videopreset\", \"veryfast\");\r\n\tcvarfuncs->SetString(ENCODERNAME\"_video_crf\", \"\");\r\n\tcvarfuncs->SetString(ENCODERNAME\"_audiocodec\", \"\");\r\n\tcvarfuncs->SetString(ENCODERNAME\"_audiobitrate\", \"\");\r\n\r\n\tcvarfuncs->SetString(\"capturedriver\", ENCODERNAME);\r\n\tcvarfuncs->SetString(\"capturerate\", \"30\");\r\n\tcvarfuncs->SetString(\"capturedemowidth\", \"0\");\r\n\tcvarfuncs->SetString(\"capturedemoheight\", \"0\");\r\n\tcvarfuncs->SetString(\"capturesound\", \"1\");\r\n\tcvarfuncs->SetString(\"capturesoundchannels\", \"2\");\r\n\tcvarfuncs->SetString(\"capturesoundbits\", \"16\");\r\n\r\n\tCon_Printf(ENCODERNAME\": capture settings reset to \"ENCODERNAME\" defaults\\n\");\r\n\tCon_Printf(ENCODERNAME\": Note that some codecs may have restrictions on video sizes\\n\");\r\n}\r\n\r\n\r\nqboolean AVEnc_Init(void)\r\n{\r\n\tfilefuncs = plugfuncs->GetEngineInterface(plugfsfuncs_name, sizeof(*filefuncs));\r\n\tif (!cvarfuncs || !cmdfuncs || !filefuncs || !plugfuncs->ExportInterface(\"Media_VideoEncoder\", &encoderfuncs, sizeof(encoderfuncs)))\r\n\t{\r\n\t\tCon_Printf(ENCODERNAME\": Engine doesn't support media encoder plugins\\n\");\r\n\t\treturn false;\r\n\t}\r\n\r\n\tffmpeg_format_force\t\t= cvarfuncs->GetNVFDG(ENCODERNAME\"_format_force\",\t\t\"\",\t\t\t\t0, \"Forces the output container format. If blank, will guess based upon filename extension.\", ENCODERNAME);\r\n\tffmpeg_videocodec\t\t= cvarfuncs->GetNVFDG(ENCODERNAME\"_videocodec\",\t\t\t\"\",\t\t\t\t0, \"Forces which video encoder to use. If blank, guesses based upon container defaults.\\nCommon names are libx264 (software), x264_nvenc (hardware accelerated)\", ENCODERNAME);\r\n\tffmpeg_videobitrate\t\t= cvarfuncs->GetNVFDG(ENCODERNAME\"_videobitrate\",\t\t\"\",\t\t\t\t0, \"Specifies the target video bitrate\", ENCODERNAME);\r\n\tffmpeg_videoforcewidth\t= cvarfuncs->GetNVFDG(ENCODERNAME\"_videoforcewidth\",\t\"\",\t\t\t\t0, \"Rescales the input video width. Best to leave blank in order to record the video at the native resolution.\", ENCODERNAME);\r\n\tffmpeg_videoforceheight\t= cvarfuncs->GetNVFDG(ENCODERNAME\"_videoforceheight\",\t\"\",\t\t\t\t0, \"Rescales the input video height. Best to leave blank in order to record the video at the native resolution.\", ENCODERNAME);\r\n\tffmpeg_videopreset\t\t= cvarfuncs->GetNVFDG(ENCODERNAME\"_videopreset\",\t\t\"veryfast\",\t\t0, \"Specifies which codec preset to use, for codecs that support such presets.\", ENCODERNAME);\r\n\tffmpeg_video_crf\t\t= cvarfuncs->GetNVFDG(ENCODERNAME\"_video_crf\",\t\t\t\"\",\t\t\t\t0, \"Specifies the 'Constant Rate Factor' codec setting.\\nA value of 0 is 'lossless', a value of 51 is 'worst quality posible', a value of 23 is default.\", ENCODERNAME);\r\n\tffmpeg_audiocodec\t\t= cvarfuncs->GetNVFDG(ENCODERNAME\"_audiocodec\",\t\t\t\"\",\t\t\t\t0, \"Forces which audio encoder to use. If blank, guesses based upon container defaults.\", ENCODERNAME);\r\n\tffmpeg_audiobitrate\t\t= cvarfuncs->GetNVFDG(ENCODERNAME\"_audiobitrate\",\t\t\"\",\t\t\t\t0, \"Specifies the target audio bitrate\", ENCODERNAME);\r\n\r\n//\tcmdfuncs->AddCommand(ENCODERNAME\"_configure\", AVEnc_LoadPreset_f);\r\n\tcmdfuncs->AddCommand(ENCODERNAME\"_nvidia\", AVEnc_Preset_Nvidia_f, \"Attempts to reconfigure video capture to use nvidia's hardware encoder.\");\r\n\tcmdfuncs->AddCommand(ENCODERNAME\"_defaults\", AVEnc_Preset_Defaults_f, \"Reconfigures video capture to the \"ENCODERNAME\" plugin's default settings.\");\r\n\t//cmdfuncs->AddCommand(ENCODERNAME\"_twitch\", AVEnc_Preset_Twitch_f, \"Reconfigures video capture to stream to twitch.\");\r\n\r\n\treturn true;\r\n}\r\n\r\n"
  },
  {
    "path": "plugins/avplug/msvc_libc/inttypes.h",
    "content": "typedef unsigned char uint8_t;\r\ntypedef signed char int8_t;\r\n\r\ntypedef unsigned short uint16_t;\r\ntypedef signed short int16_t;\r\n\r\ntypedef unsigned int uint32_t;\r\ntypedef signed int int32_t;\r\n\r\ntypedef unsigned long long uint64_t;\r\ntypedef signed long long int64_t;\r\n\r\n#define inline _inline"
  },
  {
    "path": "plugins/avplug/msvc_libc/stdint.h",
    "content": "#define UINT64_C(n) n##ull"
  },
  {
    "path": "plugins/avplug/readme.txt",
    "content": "video encoder/decoder using the ffmpeg avformat/avcodec libraries.\r\n\r\nThe video decoder plugs into the media decoder functionality on media with an 'av:' or 'avs:' prefix, specifically:\r\nThe console command 'playfilm av:foo.mpg' will start playing back $BASEDIR/$GAMEDIR/foo.mpg fullscreen (or from inside paks/pk3s, but make sure seeking is fast, so avoid compression in pk3s...).\r\nThe console command 'playfilm avs:c:\\foo.mpg' will start playing back c:\\foo.mpg fullscreen.\r\nThe shader term 'videomap avs:c:\\foo.mpg' will play the video upon the shader. This can be used with csqc+drawpic, csqc+beginpolygon, or placed upon walls.\r\nIt theoretically supports any file that the avformat/avcodec libraries support, but has no ability to pass arguments, thus playback is likely limited only to files which require no explicit overrides.\r\n\r\nThe video encoder plugs into the existing capture command. Or something. I don't know. Its all basically junk.\r\nThe container type is guessed by the file name used.\r\navplug_format says which container format to use. If empty, will be guessed based upon file extension. See ffmpeg docs for more info.\r\navplug_videocodec says which video codec to use. If empty, will be guessed based upon the container type ('libx264' for h264 compression). See ffmpeg docs for a full list.\r\navplug_videobitrate says what bitrate to encode at. Default 400000.\r\nAt the time of writing, audio is not implemented.\r\n\r\nTo check if the plugin is loaded, use the plug_list command.\r\n\r\nFor streaming, you can try this:\r\navplug_format mpegts\r\navplug_videocodec mpeg4\r\ncapture udp://PLAYERIP:1234\r\nYou can then run VLC or some such app and tell it to listen on port 1234.\r\nYou might be able to find some other protocol/codec that works better for you. Consult the ffmpeg documentation for that, if you can find something that's actually readable.\r\n"
  },
  {
    "path": "plugins/berkelium/Makefile",
    "content": "all: linux32 linux64\nlinux32:\n\tgcc -fvisibility=hidden -shared -fPIC -m32 -lstdc++ -Llib32 -llibberkelium plugapi.cpp ../plugin.c ../qvm_api.c -oberkeliumx86.so -I.\n\nlinux64:\n\tgcc -fvisibility=hidden -shared -fPIC -m64 -lstdc++ -Llib64 -llibberkelium plugapi.cpp ../plugin.c ../qvm_api.c -oberkeliumamd.so -I.\n"
  },
  {
    "path": "plugins/berkelium/plugapi.cpp",
    "content": "#include \"../plugin.h\"\r\n#include \"../engine.h\"\r\n\r\n#include \"berkelium/Berkelium.hpp\"\r\n#include \"berkelium/Window.hpp\"\r\n#include \"berkelium/WindowDelegate.hpp\"\r\n#include \"berkelium/Context.hpp\"\r\n\r\n#include <string>\r\n\r\nqboolean inited;\r\n\r\nclass decctx\r\n{\r\npublic:\r\n\tBerkelium::Window *wnd;\r\n\tint width;\r\n\tint height;\r\n\tunsigned int *buffer;\r\n\tbool repainted;\r\n\r\n\tint paintedwidth;\r\n\tint paintedheight;\r\n};\r\n\r\nclass MyDelegate : public Berkelium::WindowDelegate\r\n{\r\nprivate:\r\n\tdecctx *ctx;\r\n\r\n\tvirtual void onCrashedWorker(Berkelium::Window *win)\r\n\t{\r\n\t\tint i;\r\n\t\tCon_Printf(\"Berkelium worker crashed\\n\");\r\n\r\n\t\t/*black it out*/\r\n\t\tfor (i = 0; i < ctx->width*ctx->height; i++)\r\n\t\t{\r\n\t\t\tctx->buffer[i] = 0xff000000;\r\n\t\t}\r\n\t\tctx->repainted = true;\r\n\t}\r\n\r\n\tvirtual void onCrashed(Berkelium::Window *win)\r\n\t{\r\n\t\tint i;\r\n\t\tCon_Printf(\"Berkelium window crashed\\n\");\r\n\r\n\t\t/*black it out*/\r\n\t\tfor (i = 0; i < ctx->width*ctx->height; i++)\r\n\t\t{\r\n\t\t\tctx->buffer[i] = 0xff000000;\r\n\t\t}\r\n\t\tctx->repainted = true;\r\n\t}\r\n\tvirtual void onUnresponsive(Berkelium::Window *win)\r\n\t{\r\n\t\tCon_Printf(\"Berkelium window unresponsive\\n\");\r\n\t}\r\n\tvirtual void onResponsive(Berkelium::Window *win)\r\n\t{\r\n\t\tCon_Printf(\"Berkelium window responsive again, yay\\n\");\r\n\t}\r\n\r\n\tvirtual void onPaint(Berkelium::Window *wini, const unsigned char *bitmap_in, const Berkelium::Rect &bitmap_rect, size_t num_copy_rects, const Berkelium::Rect *copy_rects, int dx, int dy, const Berkelium::Rect& scroll_rect)\r\n\t{\r\n\t\tint i;\r\n\t\t// handle paint events...\r\n\t\tif (dx || dy)\r\n\t\t{\r\n\t\t\tint y, m;\r\n\t\t\tint dt = scroll_rect.top();\r\n\t\t\tint dl = scroll_rect.left();\r\n\t\t\tint w = scroll_rect.width();\r\n\t\t\tint h = scroll_rect.height();\r\n\t\t\tint st = dt - dy;\r\n\t\t\tint sl = dl - dx;\r\n\r\n\t\t\t/*bound the output rect*/\r\n\t\t\tif (dt < 0)\r\n\t\t\t{\r\n\t\t\t\tst -= dt;\r\n\t\t\t\th += dt;\r\n\t\t\t\tdt = 0;\r\n\t\t\t}\r\n\t\t\tif (dl < 0)\r\n\t\t\t{\r\n\t\t\t\tsl -= dl;\r\n\t\t\t\tw += dl;\r\n\t\t\t\tdl = 0;\r\n\t\t\t}\r\n\t\t\t/*bound the source rect*/\r\n\t\t\tif (st < 0)\r\n\t\t\t{\r\n\t\t\t\tdt -= st;\r\n\t\t\t\th += st;\r\n\t\t\t\tst = 0;\r\n\t\t\t}\r\n\t\t\tif (sl < 0)\r\n\t\t\t{\r\n\t\t\t\tdl -= sl;\r\n\t\t\t\tw += sl;\r\n\t\t\t\tsl = 0;\r\n\t\t\t}\r\n\t\t\t/*bound the width*/\r\n\t\t\tm = (dl>sl)?dl:sl;\r\n\t\t\tif (m + w > ctx->width)\r\n\t\t\t\tw = ctx->width - m;\r\n\t\t\tm = (dt>st)?dt:st;\r\n\t\t\tif (m + h > ctx->height)\r\n\t\t\t\th = ctx->height - m;\r\n\r\n\t\t\tif (w > 0 && h > 0)\r\n\t\t\t{\r\n\t\t\t\tif (dy > 0)\r\n\t\t\t\t{\r\n\t\t\t\t\t//if we're moving downwards, we need to write the bottom before the top (so we don't overwrite the data before its copied)\r\n\t\t\t\t\tfor (y = h-1; y >= 0; y--)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tmemmove(ctx->buffer + (dl + (dt+y)*ctx->width), ctx->buffer + (sl + (st+y)*ctx->width), w*4);\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\t//moving upwards requires we write the top row first\r\n\t\t\t\t\tfor (y = 0; y < h; y++)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tmemmove(ctx->buffer + (dl + (dt+y)*ctx->width), ctx->buffer + (sl + (st+y)*ctx->width), w*4);\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t\tfor (i = 0; i < num_copy_rects; i++)\r\n\t\t{\r\n\t\t\tunsigned int *out = ctx->buffer;\r\n\t\t\tconst unsigned int *in = (const unsigned int*)bitmap_in;\r\n\t\t\tint x, y;\r\n\t\t\tint t = copy_rects[i].top();\r\n\t\t\tint l = copy_rects[i].left();\r\n\t\t\tint r = copy_rects[i].width() + l;\r\n\t\t\tint b = copy_rects[i].height() + t;\r\n\t\t\tint w, h;\r\n\r\n\t\t\t//Clip the rect to the display. This should generally happen anyway, but resizes can be lagged a bit with the whole multi-process/thread thing.\r\n\t\t\t//don't need to clip to the bitmap rect, that should be correct.\r\n\t\t\tif (l < 0)\r\n\t\t\t\tl = 0;\r\n\t\t\tif (t < 0)\r\n\t\t\t\tt = 0;\r\n\t\t\tif (r > ctx->width)\r\n\t\t\t\tr = ctx->width;\r\n\t\t\tif (b > ctx->height)\r\n\t\t\t\tb = ctx->height;\r\n\t\t\tw = r - l;\r\n\t\t\th = b - t;\r\n\r\n\t\t\tunsigned int instride = bitmap_rect.width() - (w);\r\n\t\t\tunsigned int outstride = ctx->width - (w);\r\n\r\n\t\t\tout += l;\r\n\t\t\tout += t * ctx->width;\r\n\r\n\t\t\tin += (l-bitmap_rect.left());\r\n\t\t\tin += (t-bitmap_rect.top()) * bitmap_rect.width();\r\n\r\n\t\t\tfor (y = 0; y < h; y++)\r\n\t\t\t{\r\n\t\t\t\tfor (x = 0; x < w; x++)\r\n\t\t\t\t{\r\n\t\t\t\t\t*out++ = *in++;\r\n\t\t\t\t}\r\n\t\t\t\tin += instride;\r\n\t\t\t\tout += outstride;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tctx->repainted = true;\r\n\t}\r\n\r\npublic:\r\n\tMyDelegate(decctx *_ctx) : ctx(_ctx) {};\r\n};\r\n\r\nstatic void *Dec_Create(const char *medianame)\r\n{\r\n\t/*only respond to berkelium: media prefixes*/\r\n\tif (!strncmp(medianame, \"berkelium:\", 10))\r\n\t\tmedianame = medianame + 10;\r\n\telse if (!strcmp(medianame, \"berkelium\"))\r\n\t\tmedianame = (char*)\"about:blank\";\r\n\telse if (!strncmp(medianame, \"http:\", 5) || !strncmp(medianame, \"https:\", 6))\r\n\t\tmedianame = medianame;\t//and direct http requests.\r\n\telse\r\n\t\treturn NULL;\r\n\r\n\tif (!inited)\r\n\t{\r\n\t\t//linux lags behind and apparently returns void, so don't bother checking return values on windows, cos I'm lazy.\r\n\t\tBerkelium::init(Berkelium::FileString::empty());\r\n\t\tinited = qtrue;\r\n\t}\r\n\r\n\tdecctx *ctx = new decctx();\r\n\r\n\tBerkelium::Context* context = Berkelium::Context::create();\r\n\tctx->paintedwidth = ctx->width = 1024;\r\n\tctx->paintedheight = ctx->height = 1024;\r\n\tctx->repainted = false;\r\n\tctx->buffer = (unsigned int*)malloc(ctx->width * ctx->height * 4);\r\n\tctx->wnd = Berkelium::Window::create(context);\r\n\tdelete context;\r\n\r\n\tctx->wnd->setDelegate(new MyDelegate(ctx));\r\n\r\n\r\n\tctx->wnd->resize(ctx->width, ctx->height);\r\n\tstd::string url = medianame;\r\n\tctx->wnd->navigateTo(Berkelium::URLString::point_to(url.data(), url.length()));\r\n\r\n\treturn ctx;\r\n}\r\n\r\nstatic qboolean VARGS Dec_DisplayFrame(void *vctx, qboolean nosound, qboolean forcevideo, double mediatime, void (QDECL *uploadtexture)(void *ectx, uploadfmt_t fmt, int width, int height, void *data, void *palette), void *ectx)\r\n{\r\n\tdecctx *ctx = (decctx*)vctx;\r\n\tif (forcevideo || ctx->repainted)\r\n\t{\r\n\t\tuploadtexture(ectx, TF_BGRA32, ctx->width, ctx->height, ctx->buffer, NULL);\r\n\t\tctx->paintedwidth = ctx->width;\r\n\t\tctx->paintedheight = ctx->height;\r\n\t\tctx->repainted = false;\r\n\t}\r\n\treturn qtrue;\r\n}\r\nstatic void Dec_Destroy(void *vctx)\r\n{\r\n\tdecctx *ctx = (decctx*)vctx;\r\n\tif (inited)\t//make sure things don't happen in the wrong order. we can still leak though\r\n\t\tctx->wnd->destroy();\r\n\tdelete ctx;\r\n}\r\nstatic void Dec_GetSize (void *vctx, int *width, int *height)\r\n{\r\n\tdecctx *ctx = (decctx*)vctx;\r\n\tif (ctx->repainted)\r\n\t{\r\n\t\t*width = ctx->width;\r\n\t\t*height = ctx->height;\r\n\t}\r\n\telse\r\n\t{\r\n\t\t*width = ctx->paintedwidth;\r\n\t\t*height = ctx->paintedheight;\r\n\t}\r\n}\r\nstatic qboolean Dec_SetSize (void *vctx, int width, int height)\r\n{\r\n\tdecctx *ctx = (decctx*)vctx;\r\n\tif (width < 4)\r\n\t\twidth = 4;\r\n\tif (height < 4)\r\n\t\theight = 4;\r\n\tif (ctx->width == width && ctx->height == height)\r\n\t\treturn qtrue;\t//no point\r\n\r\n\t//there's no resize notification. apparently javascript cannot resize windows. yay.\r\n\tunsigned int *newbuf = (unsigned int*)realloc(ctx->buffer, width * height * sizeof(*newbuf));\r\n\tif (!newbuf)\r\n\t\treturn qfalse;\t//failed?!?\r\n\tctx->width = width;\r\n\tctx->height = height;\r\n\tctx->buffer = newbuf;\r\n\tctx->repainted = false;\r\n\r\n\tctx->wnd->resize(ctx->width, ctx->height);\r\n\r\n\treturn qtrue;\r\n}\r\nstatic void Dec_CursorMove (void *vctx, float posx, float posy)\r\n{\r\n\tdecctx *ctx = (decctx*)vctx;\r\n\tctx->wnd->mouseMoved((int)(posx * ctx->width), (int)(posy * ctx->height));\r\n}\r\nstatic void Dec_Key (void *vctx, int code, int unicode, int isup)\r\n{\r\n\tdecctx *ctx = (decctx*)vctx;\r\n\twchar_t wchr = unicode;\r\n\r\n\tif (code >= 178 && code < 178+6)\r\n\t{\r\n\t\tcode = code - 178;\r\n\t\t//swap mouse2+3\r\n\t\tif (code == 1)\r\n\t\t\tcode = 2;\r\n\t\telse if (code == 2)\r\n\t\t\tcode = 1;\r\n\t\tctx->wnd->mouseButton(code, !isup);\r\n\t}\r\n\telse if (code == 188 || code == 189)\r\n\t{\r\n\t\tif (!isup)\r\n\t\t\tctx->wnd->mouseWheel(0, (code==189)?-30:30);\r\n\t}\r\n\telse\r\n\t{\r\n\t\tint mods = 0;\r\n\t\tif (code == 127)\r\n\t\t\tcode = 0x08;\r\n\t\telse if (code == 140)\t//del\r\n\t\t\tcode = 0x2e;\r\n\t\telse if (code == 143)\t//home\r\n\t\t\tcode = 0x24;\r\n\t\telse if (code == 144)\t//end\r\n\t\t\tcode = 0x23;\r\n\t\telse if (code == 141)\t//pgdn\r\n\t\t\tcode = 0x22;\r\n\t\telse if (code == 142)\t//pgup\r\n\t\t\tcode = 0x21;\r\n\t\telse if (code == 139)\t//ins\r\n\t\t\tcode = 0x2d;\r\n\t\telse if (code == 132)\t//up\r\n\t\t\tcode = 0x26;\r\n\t\telse if (code == 133)\t//down\r\n\t\t\tcode = 0x28;\r\n\t\telse if (code == 134)\t//left\r\n\t\t\tcode = 0x25;\r\n\t\telse if (code == 135)\t//right\r\n\t\t\tcode = 0x27;\r\n\t\tif (code)\r\n\t\t\tctx->wnd->keyEvent(!isup, mods, code, 0);\r\n\t\tif (unicode && !isup)\r\n\t\t{\r\n\t\t\twchar_t chars[2] = {unicode};\r\n\t\t\tif (unicode == 127 || unicode == 8 || unicode == 9 || unicode == 27)\r\n\t\t\t\treturn;\r\n\t\t\tctx->wnd->textEvent(chars, 1);\r\n\t\t}\r\n\t}\r\n}\r\n\r\nstatic void Dec_ChangeStream(void *vctx, const char *newstream)\r\n{\r\n\tdecctx *ctx = (decctx*)vctx;\r\n\r\n\tif (!strncmp(newstream, \"cmd:\", 4))\r\n\t{\r\n\t\tif (!strcmp(newstream+4, \"refresh\"))\r\n\t\t\tctx->wnd->refresh();\r\n\t\telse if (!strcmp(newstream+4, \"transparent\"))\r\n\t\t\tctx->wnd->setTransparent(true);\r\n\t\telse if (!strcmp(newstream+4, \"focus\"))\r\n\t\t\tctx->wnd->focus();\r\n\t\telse if (!strcmp(newstream+4, \"unfocus\"))\r\n\t\t\tctx->wnd->unfocus();\r\n\t\telse if (!strcmp(newstream+4, \"opaque\"))\r\n\t\t\tctx->wnd->setTransparent(false);\r\n\t\telse if (!strcmp(newstream+4, \"stop\"))\r\n\t\t\tctx->wnd->stop();\r\n\t\telse if (!strcmp(newstream+4, \"back\"))\r\n\t\t\tctx->wnd->goBack();\r\n\t\telse if (!strcmp(newstream+4, \"forward\"))\r\n\t\t\tctx->wnd->goForward();\r\n\t\telse if (!strcmp(newstream+4, \"cut\"))\r\n\t\t\tctx->wnd->cut();\r\n\t\telse if (!strcmp(newstream+4, \"copy\"))\r\n\t\t\tctx->wnd->copy();\r\n\t\telse if (!strcmp(newstream+4, \"paste\"))\r\n\t\t\tctx->wnd->paste();\r\n\t\telse if (!strcmp(newstream+4, \"del\"))\r\n\t\t\tctx->wnd->del();\r\n\t\telse if (!strcmp(newstream+4, \"selectall\"))\r\n\t\t\tctx->wnd->selectAll();\r\n\t}\r\n\telse if (!strncmp(newstream, \"javascript:\", 11))\r\n\t{\r\n\t\tnewstream+=11;\r\n\t\tint len = mblen(newstream, MB_CUR_MAX);\r\n\t\twchar_t *wchrs = (wchar_t *)malloc((len+1)*2);\r\n\t\tlen = mbstowcs(wchrs, newstream, len);\r\n\t\tctx->wnd->executeJavascript(Berkelium::WideString::point_to(wchrs, len));\r\n\t\tfree(wchrs);\r\n\t}\r\n\telse\r\n\t{\r\n\t\tstd::string url = newstream;\r\n\t\tctx->wnd->navigateTo(Berkelium::URLString::point_to(url.data(), url.length()));\r\n\t}\r\n}\r\n\r\nstatic bool Dec_Init(void)\r\n{\r\n\treturn true;\r\n}\r\n\r\nstatic qintptr_t Dec_Tick(qintptr_t *args)\r\n{\r\n\t//need to keep it ticking over, if any work is to be done.\r\n\tif (inited)\r\n\t\tBerkelium::update();\r\n\treturn 0;\r\n}\r\n\r\nstatic qintptr_t Dec_Shutdown(qintptr_t *args)\r\n{\r\n\t//force-kill all.\r\n\tif (inited)\r\n\t\tBerkelium::destroy();\r\n\tinited = qfalse;\r\n\treturn 0;\r\n}\r\n\r\nstatic media_decoder_funcs_t decoderfuncs =\r\n{\r\n\tsizeof(media_decoder_funcs_t),\r\n\r\n\t\"berkelium\",\r\n\tDec_Create,\r\n\tDec_DisplayFrame,\r\n\tDec_Destroy,\r\n\tNULL,//rewind\r\n\r\n\tDec_CursorMove,\r\n\tDec_Key,\r\n\tDec_SetSize,\r\n\tDec_GetSize,\r\n\tDec_ChangeStream\r\n};\r\n\r\nextern \"C\" qintptr_t Plug_Init(qintptr_t *args)\r\n{\r\n\tif (!Plug_Export(\"Tick\", Dec_Tick))\r\n\t{\r\n\t\tCon_Printf(\"Berkelium plugin failed: Engine doesn't support Tick feature\\n\");\r\n\t\treturn false;\r\n\t}\r\n\tif (!Plug_Export(\"Shutdown\", Dec_Shutdown))\r\n\t{\r\n\t\tCon_Printf(\"Berkelium plugin failed: Engine doesn't support Shutdown feature\\n\");\r\n\t\treturn false;\r\n\t}\r\n\tif (!pPlug_ExportNative(\"Media_VideoDecoder\", &decoderfuncs))\r\n\t{\r\n\t\tCon_Printf(\"Berkelium plugin failed: Engine doesn't support media decoder plugins\\n\");\r\n\t\treturn false;\r\n\t}\r\n\treturn Dec_Init();\r\n}\r\n\r\n"
  },
  {
    "path": "plugins/berkelium/readme.txt",
    "content": "simple media decoding plugin that decodes web pages instead of videos...\r\nSee installation instructions at the end of this file.\r\n\r\n\r\nThis means you can do: playfilm berkelium:http://google.com\r\nAnd you'll get google.com displayed as if it were a cinematic or so.\r\nThe engine will pass mouse+keyboard events to such cinematics so you can interact with it.\r\n\r\n\r\nYou can also use the 'videomap' term within a shader, if you want to put some youtube video on a wall or something. Here's an example of such a shader:\r\nmuh_bad\r\n{\r\n\t{\r\n\t\tmap $lightmap\r\n\t}\r\n\t{\r\n\t\tvideomap berkelium:http://google.com\r\n\t\tblendfunc filter\r\n\t}\r\n}\r\n\r\n\r\nCSQC is able to interact with videos by:\r\n1: find the name of the shader\r\n\tstring texname = getsurfacetexture(trace_ent, getsurfacenearpoint(trace_ent, trace_endpos));\r\n2: send a key event\r\n\tgecko_keyevent(texname, keycode, keydown);\r\n3: move the mouse cursor\r\n\tgecko_mousemove(texname, x, y);\r\n\tnote that the x and y values should be between 0 and 1. Surface resolution is not relevent here.\r\n4: resize the image\r\n\tgecko_resize(texname, 1024, 1024);\r\n\tif you're using it in 2d, make sure it matches the pixel width of the screen where it'll be displayed. it'll get fuzzy otherwise.\r\n\tSize matters. Beware of sites that do not resize images for different resolutions (ie: most of them).\r\n5: you can query the image with:\r\n\tvector v = gecko_get_texture_extent(texname);\r\n6: you can purge resources with:\r\n\tgecko_destroy(texname);\r\n\tthis will 'end' the video. when the shader is next displayed it'll reset from the original url.\r\n7: you can change the url with:\r\n\tgecko_navigate(texname, \"http://fteqw.com\");\r\n8: you can send it these navigation commands (in the place of a url). You'll likely want a focus command.\r\n\tcmd:refresh\r\n\tcmd:transparent\r\n\tcmd:focus\r\n\tcmd:unfocus\r\n\tcmd:opaque\r\n\tcmd:stop\r\n\tcmd:back\r\n\tcmd:forward\r\n\tcmd:cut\r\n\tcmd:copy\r\n\tcmd:paste\r\n\tcmd:del\r\n\tcmd:selectall\r\n\r\n\r\nCompiling the plugin for Windows:\r\nIn the berkelium 7z file that you should have already downloaded, there'll be an includes and a lib directory. Stick the contents of both of those in the empty doubled berkelium directory on the fte svn.\r\nThe msvc project file should now be usable. You will likely want to change the linker target path to get picked up by the engine automatically.\r\n\r\nWindows Installation instructions:\r\nDownload Berkelium from here: http://berkelium.org/ (http://github.com/sirikata/berkelium/downloads)\r\nYou should get a 7z file that contains a bin directory. Copy the contents of that bin directory to your quake directory.\r\nThe FTE-specific plugin 'berkeliumx86.dll' should be placed as 'quake/fte/plugins/berkeliumx86.dll'.\r\n"
  },
  {
    "path": "plugins/botlib/makebotlibdll.bat",
    "content": "REM Quite clearly this is a stupid way to do this.\r\nREM But it keeps it all in one file, so that's always fun.\r\nREM To use: Stick in the q3 quake3-1.32b/code/botlib directory.\r\nREM Install gcc as either cygwin or mingw\r\nREM Double click it your batch file.\r\nREM Copy the resultant botlib.dll to your quake directory.\r\nREM Run FTE in Q3 with bots.\r\n\r\nREM note that botlib doesn't work the same as other addons, and must be compiled to native.\r\nREM it could potentially be compiled in, but that results in a lot of conflicts and much bloat when the engine is primarily targeted for q1.\r\nREM botlib works as a dll, and FTE links dynamically. botlib calls fail without the dll.\r\nREM The bots_enabled q3 cvar is readonly and forced to 0 without the dll.\r\nREM The primary reason for the library as a dll is to stop the damn thing from crashing on cached memory references on map changes.\r\nREM FTE likes freeing memory, while q3 reuses it. Botlib has some really unhealthy reads. The only ways around that I could find were rewriting botlib or closing the dll between maps. DLLs help combat q1 bloat though, and requires less maintainence in botlib.\r\n\r\n\r\necho off\r\necho. >standalone.c\r\n\r\necho #include \"../game/q_shared.h\" >>standalone.c\r\n\r\necho void Com_Memset (void* dest, const int val, const size_t count) >>standalone.c\r\necho { >>standalone.c\r\necho \tmemset(dest, val, count); >>standalone.c\r\necho } >>standalone.c\r\necho void Com_Memcpy (void* dest, const void* src, const size_t count) >>standalone.c\r\necho { >>standalone.c\r\necho \tmemcpy(dest, src, count); >>standalone.c\r\necho } >>standalone.c\r\n\r\necho void\tQDECL Com_Error( int level, const char *error, ... ) >>standalone.c\r\necho { >>standalone.c\r\necho \texit(0); >>standalone.c\r\necho } >>standalone.c\r\n\r\ngcc -mno-cygwin *.c ../game/q_shared.c ../game/q_math.c -DBOTLIB -D__LCC__ -shared -o botlib.dll -DCom_Printf=printf"
  },
  {
    "path": "plugins/bullet/bulletplug.cpp",
    "content": "/*\r\nCopyright (C) 1996-1997 Id Software, Inc.\r\n\r\nThis program is free software; you can redistribute it and/or\r\nmodify it under the terms of the GNU General Public License\r\nas published by the Free Software Foundation; either version 2\r\nof the License, or (at your option) any later version.\r\n\r\nThis program is distributed in the hope that it will be useful,\r\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\r\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\r\n\r\nSee the GNU General Public License for more details.\r\n\r\nYou should have received a copy of the GNU General Public License\r\nalong with this program; if not, write to the Free Software\r\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\r\n\r\n*/\r\n\r\n//if we're not building as an fte-specific plugin, we must be being built as part of the fte engine itself.\r\n//(no, we don't want to act as a plugin for ezquake...)\r\n#ifndef FTEPLUGIN\r\n#define FTEENGINE\r\n#define FTEPLUGIN\r\n#define pCvar_Register Cvar_Get\r\n#define pCvar_GetNVFDG Cvar_Get2\r\n#define pCvar_GetFloat(x) Cvar_FindVar(x)->value\r\n#define pSys_Error Sys_Error\r\n#define Plug_Init Plug_Bullet_Init\r\n#pragma comment(lib,\"../../plugins/bullet/libs/bullet_dbg.lib\")\r\n#endif\r\n#include \"quakedef.h\"\r\n#include \"../plugin.h\"\r\n#include \"../engine.h\"\r\n\r\n#include \"pr_common.h\"\r\n#include \"com_mesh.h\"\r\n\r\n#ifndef FTEENGINE\r\n#define BZ_Malloc malloc\r\n#define BZ_Free free\r\n#define Z_Free BZ_Free\r\n//#define vec3_origin vec3_origin_\r\n//static vec3_t vec3_origin;\r\n#define VectorCompare VectorCompare_\r\nstatic int VectorCompare (const pvec3_t v1, const pvec3_t v2)\r\n{\r\n\tint\t\ti;\r\n\tfor (i=0 ; i<3 ; i++)\r\n\t\tif (v1[i] != v2[i])\r\n\t\t\treturn 0;\r\n\treturn 1;\r\n}\r\n\r\n#endif\r\nstatic rbeplugfuncs_t *rbefuncs;\r\n\r\n\r\n\r\n//============================================================================\r\n// physics engine support\r\n//============================================================================\r\n\r\n#define DEG2RAD(d) (d * M_PI * (1/180.0f))\r\n#define RAD2DEG(d) ((d*180) / M_PI)\r\n\r\n#include \"btBulletDynamicsCommon.h\"\r\n\r\n//not sure where these are going. seems to be an issue only on windows.\r\n#ifndef max\r\n#define max(a,b) ((a) > (b) ? (a) : (b))\r\n#endif\r\n#ifndef min\r\n#define min(a,b) ((a) < (b) ? (a) : (b))\r\n#endif\r\n\r\nstatic void World_Bullet_RunCmd(world_t *world, rbecommandqueue_t *cmd);\r\n\r\nstatic cvar_t *physics_bullet_maxiterationsperframe;\r\nstatic cvar_t *physics_bullet_framerate;\r\nstatic cvar_t *pr_meshpitch;\r\n\r\nvoid World_Bullet_Init(void)\r\n{\r\n\tphysics_bullet_maxiterationsperframe\t= cvarfuncs->GetNVFDG(\"physics_bullet_maxiterationsperframe\",\t\"10\",\t0, \"FIXME: should be 1 when CCD is working properly.\", \"Bullet\");\r\n\tphysics_bullet_framerate\t\t\t\t= cvarfuncs->GetNVFDG(\"physics_bullet_framerate\",\t\t\t\t\"60\",\t0, \"Bullet physics run at a fixed framerate in order to preserve numerical stability (interpolation is used to smooth out the result). Higher framerates are of course more demanding.\", \"Bullet\");\r\n\tpr_meshpitch\t\t\t\t\t\t\t= cvarfuncs->GetNVFDG(\"r_meshpitch\",\t\t\t\t\t\t\t\t\"-1\",\t0, \"\", \"Bullet\");\r\n}\r\n\r\ntypedef struct bulletcontext_s\r\n{\r\n\trigidbodyengine_t funcs;\r\n\r\n\tbool hasextraobjs;\r\n//\tvoid *ode_space;\r\n//\tvoid *ode_contactgroup;\r\n\t// number of constraint solver iterations to use (for dWorldStepFast)\r\n//\tint ode_iterations;\r\n\t// actual step (server frametime / ode_iterations)\r\n//\tvec_t ode_step;\r\n\t// max velocity for a 1-unit radius object at current step to prevent\r\n\t// missed collisions\r\n//\tvec_t ode_movelimit;\r\n\trbecommandqueue_t *cmdqueuehead;\r\n\trbecommandqueue_t *cmdqueuetail;\r\n\r\n\r\n\tworld_t *gworld;\r\n\r\n\r\n\tbtBroadphaseInterface *broadphase;\r\n\tbtDefaultCollisionConfiguration *collisionconfig;\r\n\tbtCollisionDispatcher *collisiondispatcher;\r\n\tbtSequentialImpulseConstraintSolver *solver;\r\n\tbtDiscreteDynamicsWorld *dworld;\r\n\tbtOverlapFilterCallback *ownerfilter;\r\n} bulletcontext_t;\r\n\r\nclass QCFilterCallback : public btOverlapFilterCallback\r\n{\r\n\t// return true when pairs need collision\r\n\tvirtual bool\tneedBroadphaseCollision(btBroadphaseProxy* proxy0,btBroadphaseProxy* proxy1) const\r\n\t{\r\n\t\t//dimensions don't collide\r\n\t\tbool collides = (proxy0->m_collisionFilterGroup & proxy1->m_collisionFilterMask) != 0;\r\n\t\tcollides = collides && (proxy1->m_collisionFilterGroup & proxy0->m_collisionFilterMask);\r\n\r\n\t\t//owners don't collide (unless one is world, obviouslyish)\r\n\t\tif (collides)\r\n\t\t{\r\n\t\t\tauto b1 = (btRigidBody*)proxy0->m_clientObject;\r\n\t\t\tauto b2 = (btRigidBody*)proxy1->m_clientObject;\r\n\t\t\t//don't let two qc-controlled entities collide in Bullet, that's the job of quake.\r\n\t\t\tif (b1->isStaticOrKinematicObject() && b2->isStaticOrKinematicObject())\r\n\t\t\t\treturn false;\r\n\t\t\tauto e1 = (wedict_t*)b1->getUserPointer();\r\n\t\t\tauto e2 = (wedict_t*)b2->getUserPointer();\r\n\t\t\tif (e1&&e2)\r\n\t\t\t{\r\n\t\t\t\tif ((e1->v->solid == SOLID_TRIGGER && e2->v->solid != SOLID_BSP) ||\r\n\t\t\t\t\t(e2->v->solid == SOLID_TRIGGER && e1->v->solid != SOLID_BSP))\r\n\t\t\t\t\treturn false;\t//triggers only collide with bsp objects.\r\n\t\t\t\tif (e1->entnum && e2->entnum)\r\n\t\t\t\t\tcollides = e1->v->owner != e2->entnum && e2->v->owner != e1->entnum;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\treturn collides;\r\n\t}\r\n};\r\n\r\nstatic void QDECL World_Bullet_End(world_t *world)\r\n{\r\n\tauto ctx = reinterpret_cast<bulletcontext_t*>(world->rbe);\r\n\tworld->rbe = nullptr;\r\n\tdelete ctx->dworld;\r\n\tdelete ctx->solver;\r\n\tdelete ctx->collisionconfig;\r\n\tdelete ctx->collisiondispatcher;\r\n\tdelete ctx->broadphase;\r\n\tdelete ctx->ownerfilter;\r\n\tZ_Free(ctx);\r\n}\r\n\r\nstatic void QDECL World_Bullet_RemoveJointFromEntity(world_t *world, wedict_t *ed)\r\n{\r\n\ted->rbe.joint_type = 0;\r\n//\tif(ed->rbe.joint)\r\n//\t\tdJointDestroy((dJointID)ed->rbe.joint);\r\n\ted->rbe.joint.joint = NULL;\r\n}\r\n\r\nstatic void QDECL World_Bullet_RemoveFromEntity(world_t *world, wedict_t *ed)\r\n{\r\n\tauto ctx = reinterpret_cast<bulletcontext_t*>(world->rbe);\r\n\tbtRigidBody *body;\r\n\tbtCollisionShape *geom;\r\n\tif (!ed->rbe.physics)\r\n\t\treturn;\r\n\r\n\t// entity is not physics controlled, free any physics data\r\n\ted->rbe.physics = qfalse;\r\n\r\n\tbody = (btRigidBody*)ed->rbe.body.body;\r\n\ted->rbe.body.body = NULL;\r\n\tif (body)\r\n\t\tctx->dworld->removeRigidBody (body);\r\n\r\n\tgeom = (btCollisionShape*)ed->rbe.body.geom;\r\n\ted->rbe.body.geom = NULL;\r\n\tif (ed->rbe.body.geom)\r\n\t\tdelete geom;\r\n\r\n\t//FIXME: joints\r\n\trbefuncs->ReleaseCollisionMesh(ed);\r\n\tif(ed->rbe.massbuf)\r\n\t\tBZ_Free(ed->rbe.massbuf);\r\n\ted->rbe.massbuf = NULL;\r\n}\r\n\r\nstatic bool NegativeMeshPitch(world_t *world, wedict_t *ent)\r\n{\r\n\tif (ent->v->modelindex)\r\n\t{\r\n\t\tmodel_t *model = world->Get_CModel(world, ent->v->modelindex);\r\n\t\tif (model && (model->type == mod_alias || model->type == mod_halflife))\r\n\t\t\treturn pr_meshpitch->value < 0;\r\n\t\treturn false;\r\n\t}\r\n\treturn false;\r\n}\r\n\r\nstatic btTransform transformFromQuake(world_t *world, wedict_t *ent)\r\n{\r\n\tvec3_t forward, left, up;\r\n\tif (NegativeMeshPitch(world, ent))\r\n\t{\r\n\t\tpvec3_t iangles = {-ent->v->angles[0], ent->v->angles[1], ent->v->angles[2]};\r\n\t\trbefuncs->AngleVectors(iangles, forward, left, up);\r\n\t}\r\n\telse\r\n\t\trbefuncs->AngleVectors(ent->v->angles, forward, left, up);\r\n\tVectorNegate(left, left);\r\n\r\n\treturn btTransform(btMatrix3x3(forward[0], forward[1], forward[2], left[0], left[1], left[2], up[0], up[1], up[2]), btVector3(ent->v->origin[0], ent->v->origin[1], ent->v->origin[2]));\r\n}\r\n\r\nstatic void World_Bullet_Frame_JointFromEntity(world_t *world, wedict_t *ed)\r\n{\r\n\tauto rbe = reinterpret_cast<bulletcontext_t*>(world->rbe);\r\n\tbtTypedConstraint *j = nullptr;\r\n\tbtRigidBody *b1 = nullptr;\r\n\tbtRigidBody *b2 = nullptr;\r\n\tint movetype = 0;\r\n\tint jointtype = 0;\r\n\tint enemy = 0, aiment = 0;\r\n\twedict_t *e1, *e2;\r\n//\tvec_t CFM, ERP, FMax;\r\n\tvec_t Stop;\r\n//\tvec_t Vel;\r\n\tvec3_t forward;\r\n\tmovetype = ed->v->movetype;\r\n\tjointtype = ed->xv->jointtype;\r\n\tenemy = ed->v->enemy;\r\n\taiment = ed->v->aiment;\r\n\tbtVector3 origin(ed->v->origin[0], ed->v->origin[1], ed->v->origin[2]);\r\n\tbtVector3 velocity(ed->v->velocity[0], ed->v->velocity[1], ed->v->velocity[2]);\r\n\tbtVector3 movedir(ed->v->movedir[0], ed->v->movedir[1], ed->v->movedir[2]);\r\n\tif(movetype == MOVETYPE_PHYSICS)\r\n\t\tjointtype = 0; // can't have both\r\n\r\n\te1 = (wedict_t*)PROG_TO_EDICT(world->progs, enemy);\r\n\tb1 = (btRigidBody*)e1->rbe.body.body;\r\n\tif(ED_ISFREE(e1) || !b1)\r\n\t\tenemy = 0;\r\n\te2 = (wedict_t*)PROG_TO_EDICT(world->progs, aiment);\r\n\tb2 = (btRigidBody*)e2->rbe.body.body;\r\n\tif(ED_ISFREE(e2) || !b2)\r\n\t\taiment = 0;\r\n\t// see http://www.ode.org/old_list_archives/2006-January/017614.html\r\n\t// we want to set ERP? make it fps independent and work like a spring constant\r\n\t// note: if movedir[2] is 0, it becomes ERP = 1, CFM = 1.0 / (H * K)\r\n\tif(movedir[0] > 0 && movedir[1] > 0)\r\n\t{\r\n//\t\tfloat K = movedir[0];\r\n//\t\tfloat D = movedir[1];\r\n//\t\tfloat R = 2.0 * D * sqrt(K); // we assume D is premultiplied by sqrt(sprungMass)\r\n//\t\tCFM = 1.0 / (rbe->ode_step * K + R); // always > 0\r\n//\t\tERP = rbe->ode_step * K * CFM;\r\n//\t\tVel = 0;\r\n//\t\tFMax = 0;\r\n\t\tStop = movedir[2];\r\n\t}\r\n\telse if(movedir[1] < 0)\r\n\t{\r\n//\t\tCFM = 0;\r\n//\t\tERP = 0;\r\n//\t\tVel = movedir[0];\r\n//\t\tFMax = -movedir[1]; // TODO do we need to multiply with world.physics.ode_step?\r\n\t\tStop = movedir[2] > 0 ? movedir[2] : BT_INFINITY;\r\n\t}\r\n\telse // movedir[0] > 0, movedir[1] == 0 or movedir[0] < 0, movedir[1] >= 0\r\n\t{\r\n//\t\tCFM = 0;\r\n//\t\tERP = 0;\r\n//\t\tVel = 0;\r\n//\t\tFMax = 0;\r\n\t\tStop = BT_INFINITY;\r\n\t}\r\n\tif(jointtype == ed->rbe.joint_type && VectorCompare(origin, ed->rbe.joint_origin) && VectorCompare(velocity, ed->rbe.joint_velocity) && VectorCompare(ed->v->angles, ed->rbe.joint_angles) && enemy == ed->rbe.joint_enemy && aiment == ed->rbe.joint_aiment && VectorCompare(movedir, ed->rbe.joint_movedir))\r\n\t\treturn; // nothing to do\r\n\r\n\tif(ed->rbe.joint.joint)\r\n\t{\r\n\t\tj = (btTypedConstraint*)ed->rbe.joint.joint;\r\n\t\trbe->dworld->removeConstraint(j);\r\n\t\ted->rbe.joint.joint = nullptr;\r\n\t\tdelete j;\r\n\t}\r\n\tif (!jointtype)\r\n\t\treturn;\r\n\r\n\tbtVector3 b1org(0,0,0), b2org(0,0,0);\r\n\tif(enemy)\r\n\t\tb1org.setValue(e1->v->origin[0], e1->v->origin[1], e1->v->origin[2]);\r\n\tif(aiment)\r\n\t\tb2org.setValue(e2->v->origin[0], e2->v->origin[1], e2->v->origin[2]);\r\n\r\n\ted->rbe.joint_type = jointtype;\r\n\ted->rbe.joint_enemy = enemy;\r\n\ted->rbe.joint_aiment = aiment;\r\n\tVectorCopy(origin, ed->rbe.joint_origin);\r\n\tVectorCopy(velocity, ed->rbe.joint_velocity);\r\n\tVectorCopy(ed->v->angles, ed->rbe.joint_angles);\r\n\tVectorCopy(movedir, ed->rbe.joint_movedir);\r\n\r\n\trbefuncs->AngleVectors(ed->v->angles, forward, nullptr, nullptr);\r\n\r\n\t//Con_Printf(\"making new joint %i\\n\", (int) (ed - prog->edicts));\r\n\tswitch(jointtype)\r\n\t{\r\n\tcase JOINTTYPE_POINT:\r\n\t\tj = new btPoint2PointConstraint(*b1, *b2, btVector3(b1org - origin), btVector3(b2org - origin));\r\n\t\tbreak;\r\n/*\tcase JOINTTYPE_HINGE:\r\n\t\tbtHingeConstraint *h = new btHingeConstraint(*b1, *b2, btVector3(b1org - origin), btVector3(b2org - origin), aa, ab, ref);\r\n\t\tj = h;\r\n\t\tif (h)\r\n\t\t{\r\n\t\t\th->setLimit(-Stop, Stop, softness, bias, relaxation);\r\n\t\t\th->setAxis(btVector3(forward[0], forward[1], forward[2]));\r\n//\t\t\th->dJointSetHingeParam(j, dParamFMax, FMax);\r\n//\t\t\th->dJointSetHingeParam(j, dParamHiStop, Stop);\t\r\n//\t\t\th->dJointSetHingeParam(j, dParamLoStop, -Stop);\r\n//\t\t\th->dJointSetHingeParam(j, dParamStopCFM, CFM);\r\n//\t\t\th->dJointSetHingeParam(j, dParamStopERP, ERP);\r\n\r\n//\t\t\th->setMotorTarget(vel);\r\n\t\t}\r\n\t\tbreak;*/\r\n\tcase JOINTTYPE_SLIDER:\r\n\t\t{\r\n\t\t\tbtTransform jointtransform = transformFromQuake(world, ed);\r\n\t\t\tbtTransform b1transform = transformFromQuake(world, e1).inverseTimes(jointtransform);\r\n\t\t\tbtTransform b2transform = transformFromQuake(world, e2).inverseTimes(jointtransform);\r\n\r\n\t\t\tbtSliderConstraint *s = new btSliderConstraint(*b1, *b2, b1transform, b2transform, false);\r\n\t\t\tj = s;\r\n\t\t\tif (s)\r\n\t\t\t{\r\n//\t\t\t\ts->dJointSetSliderAxis(j, forward[0], forward[1], forward[2]);\r\n//\t\t\t\ts->dJointSetSliderParam(j, dParamFMax, FMax);\r\n\t\t\t\ts->setLowerLinLimit(-Stop);\r\n\t\t\t\ts->setUpperLinLimit(Stop);\r\n\t\t\t\ts->setLowerAngLimit(0);\r\n\t\t\t\ts->setUpperAngLimit(0);\r\n//\t\t\t\ts->dJointSetSliderParam(j, dParamHiStop, Stop);\r\n//\t\t\t\ts->dJointSetSliderParam(j, dParamLoStop, -Stop);\r\n//\t\t\t\ts->dJointSetSliderParam(j, dParamStopCFM, CFM);\r\n//\t\t\t\ts->dJointSetSliderParam(j, dParamStopERP, ERP);\r\n//\t\t\t\ts->setTargetLinMotorVelocity(vel);\r\n//\t\t\t\ts->setPoweredLinMotor(true);\r\n\t\t\t}\r\n\t\t}\r\n\t\tbreak;\r\n/*\tcase JOINTTYPE_UNIVERSAL:\r\n\t\tbtGeneric6DofConstraint\r\n\t\tj = dJointCreateUniversal(rbe->ode_world, 0);\r\n\t\tif (j)\r\n\t\t{\r\n\t\t\tdJointSetUniversalAnchor(j, origin[0], origin[1], origin[2]);\r\n\t\t\tdJointSetUniversalAxis1(j, forward[0], forward[1], forward[2]);\r\n\t\t\tdJointSetUniversalAxis2(j, up[0], up[1], up[2]);\r\n\t\t\tdJointSetUniversalParam(j, dParamFMax, FMax);\r\n\t\t\tdJointSetUniversalParam(j, dParamHiStop, Stop);\r\n\t\t\tdJointSetUniversalParam(j, dParamLoStop, -Stop);\r\n\t\t\tdJointSetUniversalParam(j, dParamStopCFM, CFM);\r\n\t\t\tdJointSetUniversalParam(j, dParamStopERP, ERP);\r\n\t\t\tdJointSetUniversalParam(j, dParamVel, Vel);\r\n\t\t\tdJointSetUniversalParam(j, dParamFMax2, FMax);\r\n\t\t\tdJointSetUniversalParam(j, dParamHiStop2, Stop);\r\n\t\t\tdJointSetUniversalParam(j, dParamLoStop2, -Stop);\r\n\t\t\tdJointSetUniversalParam(j, dParamStopCFM2, CFM);\r\n\t\t\tdJointSetUniversalParam(j, dParamStopERP2, ERP);\r\n\t\t\tdJointSetUniversalParam(j, dParamVel2, Vel);\r\n\t\t}\r\n\t\tbreak;*/\r\n/*\tcase JOINTTYPE_HINGE2:\r\n\t\tj = dJointCreateHinge2(rbe->ode_world, 0);\r\n\t\tif (j)\r\n\t\t{\r\n\t\t\tdJointSetHinge2Anchor(j, origin[0], origin[1], origin[2]);\r\n\t\t\tdJointSetHinge2Axis1(j, forward[0], forward[1], forward[2]);\r\n\t\t\tdJointSetHinge2Axis2(j, velocity[0], velocity[1], velocity[2]);\r\n\t\t\tdJointSetHinge2Param(j, dParamFMax, FMax);\r\n\t\t\tdJointSetHinge2Param(j, dParamHiStop, Stop);\r\n\t\t\tdJointSetHinge2Param(j, dParamLoStop, -Stop);\r\n\t\t\tdJointSetHinge2Param(j, dParamStopCFM, CFM);\r\n\t\t\tdJointSetHinge2Param(j, dParamStopERP, ERP);\r\n\t\t\tdJointSetHinge2Param(j, dParamVel, Vel);\r\n\t\t\tdJointSetHinge2Param(j, dParamFMax2, FMax);\r\n\t\t\tdJointSetHinge2Param(j, dParamHiStop2, Stop);\r\n\t\t\tdJointSetHinge2Param(j, dParamLoStop2, -Stop);\r\n\t\t\tdJointSetHinge2Param(j, dParamStopCFM2, CFM);\r\n\t\t\tdJointSetHinge2Param(j, dParamStopERP2, ERP);\r\n\t\t\tdJointSetHinge2Param(j, dParamVel2, Vel);\r\n\t\t}\r\n\t\tbreak;*/\r\n\tcase JOINTTYPE_FIXED:\r\n\t\t{\r\n\t\t\tbtTransform jointtransform = transformFromQuake(world, ed);\r\n\t\t\tbtTransform b1transform = transformFromQuake(world, e1).inverseTimes(jointtransform);\r\n\t\t\tbtTransform b2transform = transformFromQuake(world, e2).inverseTimes(jointtransform);\r\n\r\n\t\t\tj = new btFixedConstraint(*b1, *b2, b1transform, b2transform);\r\n\t\t}\r\n\t\tbreak;\r\n\tcase 0:\r\n\tdefault:\r\n\t\tj = nullptr;\r\n\t\tbreak;\r\n\t}\r\n\r\n\ted->rbe.joint.joint = (void *) j;\r\n\tif (j)\r\n\t{\r\n\t\tj->setUserConstraintPtr((void *) ed);\r\n\t\trbe->dworld->addConstraint(j, false);\r\n\t}\r\n}\r\n\r\nstatic void MatToTransform(const float *mat, btTransform &tr)\r\n{\r\n\ttr.setBasis(btMatrix3x3(\r\n\t\tmat[0], mat[1], mat[2],\r\n\t\tmat[4], mat[5], mat[6],\r\n\t\tmat[8], mat[9], mat[10]));\r\n\ttr.setOrigin(btVector3(mat[3], mat[7], mat[11]));\r\n}\r\nstatic void MatFromTransform(float *mat, const btTransform &tr)\r\n{\r\n\tconst btMatrix3x3 &m = tr.getBasis();\r\n\tconst btVector3 &o = tr.getOrigin();\r\n\tconst btVector3 &r0 = m.getRow(0);\r\n\tconst btVector3 &r1 = m.getRow(1);\r\n\tconst btVector3 &r2 = m.getRow(2);\r\n\tmat[0] = r0[0];\r\n\tmat[1] = r0[1];\r\n\tmat[2] = r0[2];\r\n\tmat[3] =o[0];\r\n\tmat[4] = r1[0];\r\n\tmat[5] = r1[1];\r\n\tmat[6] = r1[2];\r\n\tmat[7] =o[1];\r\n\tmat[8] = r2[0];\r\n\tmat[9] = r2[1];\r\n\tmat[10]= r2[2];\r\n\tmat[11]=o[2];\r\n}\r\nstatic qboolean QDECL World_Bullet_RagMatrixToBody(rbebody_t *bodyptr, float *mat)\r\n{\t//mat is a 4*3 matrix\r\n\tbtTransform tr;\r\n\tauto body = reinterpret_cast<btRigidBody*>(bodyptr->body);\r\n\tMatToTransform(mat, tr);\r\n\tbody->setWorldTransform(tr);\r\n\treturn qtrue;\r\n}\r\nstatic qboolean QDECL World_Bullet_RagCreateBody(world_t *world, rbebody_t *bodyptr, rbebodyinfo_t *bodyinfo, float *mat, wedict_t *ent)\r\n{\r\n\tbtRigidBody *body = nullptr;\r\n\tbtCollisionShape *geom = nullptr;\r\n\tfloat radius;\r\n\tfloat threshold;\r\n//\tfloat length;\r\n\tauto ctx = reinterpret_cast<bulletcontext_t*>(world->rbe);\r\n//\tint axisindex;\r\n\tctx->hasextraobjs = true;\r\n\r\n\tswitch(bodyinfo->geomshape)\r\n\t{\r\n/*\r\n\tcase GEOMTYPE_TRIMESH:\r\n//\t\tfoo Matrix4x4_Identity(ed->rbe.offsetmatrix);\r\n\t\tgeom = NULL;\r\n\t\tif (!model)\r\n\t\t{\r\n\t\t\tCon_Printf(\"entity %i (classname %s) has no model\\n\", NUM_FOR_EDICT(world->progs, (edict_t*)ed), PR_GetString(world->progs, ed->v->classname));\r\n\t\t\tif (ed->rbe.physics)\r\n\t\t\t\tWorld_Bullet_RemoveFromEntity(world, ed);\r\n\t\t\treturn;\r\n\t\t}\r\n\t\tif (!rbefuncs->GenerateCollisionMesh(world, model, ed, geomcenter))\r\n\t\t{\r\n\t\t\tif (ed->rbe.physics)\r\n\t\t\t\tWorld_Bullet_RemoveFromEntity(world, ed);\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n//\t\tfoo Matrix4x4_RM_CreateTranslate(ed->rbe.offsetmatrix, geomcenter[0], geomcenter[1], geomcenter[2]);\r\n\r\n\t\t{\r\n\t\t\tbtTriangleIndexVertexArray *tiva = new btTriangleIndexVertexArray();\r\n\t\t\tbtIndexedMesh mesh;\r\n\t\t\tmesh.m_vertexType = PHY_FLOAT;\r\n\t\t\tmesh.m_indexType = PHY_INTEGER;\r\n\t\t\tmesh.m_numTriangles = ed->rbe.numtriangles;\r\n\t\t\tmesh.m_numVertices = ed->rbe.numvertices;\r\n\t\t\tmesh.m_triangleIndexBase = (const unsigned char*)ed->rbe.element3i;\r\n\t\t\tmesh.m_triangleIndexStride = sizeof(*ed->rbe.element3i)*3;\r\n\t\t\tmesh.m_vertexBase = (const unsigned char*)ed->rbe.vertex3f;\r\n\t\t\tmesh.m_vertexStride = sizeof(*ed->rbe.vertex3f)*3;\r\n\t\t\ttiva->addIndexedMesh(mesh);\r\n\t\t\tgeom = new btBvhTriangleMeshShape(tiva, true);\r\n\t\t}\r\n\t\tbreak;\r\n*/\r\n\tdefault:\r\n\t\tCon_DPrintf(\"World_Bullet_RagCreateBody: unsupported geomshape %i\\n\", bodyinfo->geomshape);\r\n\tcase GEOMTYPE_BOX:\r\n\t\tgeom = new btBoxShape(btVector3(bodyinfo->dimensions[0], bodyinfo->dimensions[1], bodyinfo->dimensions[2]) * 0.5);\r\n\t\tradius = sqrt(DotProduct(bodyinfo->dimensions,bodyinfo->dimensions));\r\n\t\tthreshold = min(bodyinfo->dimensions[0], min(bodyinfo->dimensions[1], bodyinfo->dimensions[2]));\r\n\t\tbreak;\r\n\r\n\tcase GEOMTYPE_SPHERE:\r\n\t\tthreshold = radius = bodyinfo->dimensions[0] * 0.5f;\r\n\t\tgeom = new btSphereShape(radius);\r\n\t\tbreak;\r\n\r\n\tcase GEOMTYPE_CAPSULE:\r\n//\tcase GEOMTYPE_CAPSULE_X:\r\n//\tcase GEOMTYPE_CAPSULE_Y:\r\n\tcase GEOMTYPE_CAPSULE_Z:\r\n\t\tradius = (bodyinfo->dimensions[0]+bodyinfo->dimensions[1]) * 0.5f;\r\n\t\tgeom = new btCapsuleShapeZ(radius, bodyinfo->dimensions[2]);\r\n\t\tthreshold = min(radius, bodyinfo->dimensions[2]*0.5);\r\n\t\tradius = max(radius, bodyinfo->dimensions[2]*0.5);\r\n\t\tbreak;\r\n\r\n\tcase GEOMTYPE_CYLINDER:\r\n//\tcase GEOMTYPE_CYLINDER_X:\r\n//\tcase GEOMTYPE_CYLINDER_Y:\r\n\tcase GEOMTYPE_CYLINDER_Z:\r\n\t\tradius = (bodyinfo->dimensions[0] + bodyinfo->dimensions[1]) * 0.5;\r\n\t\tgeom = new btCylinderShapeZ(btVector3(radius, radius, bodyinfo->dimensions[2])*0.5);\r\n\t\tthreshold = min(radius, bodyinfo->dimensions[2]*0.5);\r\n\t\tradius = max(radius, bodyinfo->dimensions[2]*0.5);\r\n\t\tbreak;\r\n\t}\r\n\tbodyptr->geom = geom;\r\n\r\n\t//now create the body too\r\n\r\n\tbtVector3 fallInertia(0, 0, 0);\r\n\tgeom->calculateLocalInertia(bodyinfo->mass, fallInertia);\r\n\tbtRigidBody::btRigidBodyConstructionInfo fallRigidBodyCI(bodyinfo->mass, nullptr, geom, fallInertia);\r\n\tMatToTransform(mat, fallRigidBodyCI.m_startWorldTransform);\r\n\tbody = new btRigidBody(fallRigidBodyCI);\r\n\tbody->setUserPointer(ent);\r\n\tbodyptr->body = reinterpret_cast<void*>(body);\r\n\r\n\t//motion threshhold should be speed/physicsframerate.\r\n\t//FIXME: recalculate...\r\n\tbody->setCcdMotionThreshold(threshold/64);\r\n\t//radius should be the body's radius\r\n\r\n\tbody->setCcdSweptSphereRadius(radius);\r\n\r\n\tctx->dworld->addRigidBody(body, ent?ent->xv->dimension_solid:~0, ent?ent->xv->dimension_hit:~0);\r\n\r\n\treturn qtrue;\r\n}\r\n\r\nstatic void QDECL World_Bullet_RagMatrixFromJoint(rbejoint_t *joint, rbejointinfo_t *info, float *mat)\r\n{\r\n/*\r\n\tdVector3 dr3;\r\n\tswitch(info->type)\r\n\t{\r\n\tcase JOINTTYPE_POINT:\r\n\t\tdJointGetBallAnchor(joint->ode_joint, dr3);\r\n\t\tmat[3] = dr3[0];\r\n\t\tmat[7] = dr3[1];\r\n\t\tmat[11] = dr3[2];\r\n\t\tVectorClear(mat+4);\r\n\t\tVectorClear(mat+8);\r\n\t\tbreak;\r\n\r\n\tcase JOINTTYPE_HINGE:\r\n\t\tdJointGetHingeAnchor(joint->ode_joint, dr3);\r\n\t\tmat[3] = dr3[0];\r\n\t\tmat[7] = dr3[1];\r\n\t\tmat[11] = dr3[2];\r\n\r\n\t\tdJointGetHingeAxis(joint->ode_joint, dr3);\r\n\t\tVectorCopy(dr3, mat+4);\r\n\t\tVectorClear(mat+8);\r\n\r\n\t\tCrossProduct(mat+4, mat+8, mat+0);\r\n\t\treturn;\r\n\t\tbreak;\r\n\tcase JOINTTYPE_HINGE2:\r\n\t\tdJointGetHinge2Anchor(joint->ode_joint, dr3);\r\n\t\tmat[3] = dr3[0];\r\n\t\tmat[7] = dr3[1];\r\n\t\tmat[11] = dr3[2];\r\n\r\n\t\tdJointGetHinge2Axis1(joint->ode_joint, dr3);\r\n\t\tVectorCopy(dr3, mat+4);\r\n\t\tdJointGetHinge2Axis2(joint->ode_joint, dr3);\r\n\t\tVectorCopy(dr3, mat+8);\r\n\t\tbreak;\r\n\r\n\tcase JOINTTYPE_SLIDER:\r\n\t\t//no anchor point...\r\n\t\t//get the two bodies and average their origin for a somewhat usable representation of an anchor.\r\n\t\t{\r\n\t\t\tconst dReal *p1, *p2;\r\n\t\t\tdReal n[3];\r\n\t\t\tdBodyID b1 = dJointGetBody(joint->ode_joint, 0), b2 = dJointGetBody(joint->ode_joint, 1);\r\n\t\t\tif (b1)\r\n\t\t\t\tp1 = dBodyGetPosition(b1);\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tp1 = n;\r\n\t\t\t\tVectorClear(n);\r\n\t\t\t}\r\n\t\t\tif (b2)\r\n\t\t\t\tp2 = dBodyGetPosition(b2);\r\n\t\t\telse\r\n\t\t\t\tp2 = p1;\r\n\t\t\tdJointGetSliderAxis(joint->ode_joint, dr3 + 0);\r\n\t\t\tVectorInterpolate(p1, 0.5, p2, dr3);\r\n\t\t\tmat[3] = dr3[0];\r\n\t\t\tmat[7] = dr3[1];\r\n\t\t\tmat[11] = dr3[2];\r\n\r\n\t\t\tVectorClear(mat+4);\r\n\t\t\tVectorClear(mat+8);\r\n\t\t}\r\n\t\tbreak;\r\n\r\n\tcase JOINTTYPE_UNIVERSAL:\r\n\t\tdJointGetUniversalAnchor(joint->ode_joint, dr3);\r\n\t\tmat[3] = dr3[0];\r\n\t\tmat[7] = dr3[1];\r\n\t\tmat[11] = dr3[2];\r\n\r\n\t\tdJointGetUniversalAxis1(joint->ode_joint, dr3);\r\n\t\tVectorCopy(dr3, mat+4);\r\n\t\tdJointGetUniversalAxis2(joint->ode_joint, dr3);\r\n\t\tVectorCopy(dr3, mat+8);\r\n\r\n\t\tCrossProduct(mat+4, mat+8, mat+0);\r\n\t\treturn;\r\n\t\tbreak;\r\n\t}\r\n\trbefuncs->AngleVectors(vec3_origin, mat+0, mat+4, mat+8);\r\n\tVectorNegate((mat+4), (mat+4));\r\n*/\r\n}\r\n\r\nstatic void QDECL World_Bullet_RagMatrixFromBody(world_t *world, rbebody_t *bodyptr, float *mat)\r\n{\r\n\t//auto ctx = reinterpret_cast<bulletcontext_t*>(world->rbe);\r\n\tauto body = reinterpret_cast<btRigidBody*>(bodyptr->body);\r\n\tMatFromTransform(mat, body->getCenterOfMassTransform());\r\n}\r\nstatic void QDECL World_Bullet_RagEnableJoint(rbejoint_t *joint, qboolean enabled)\r\n{\r\n\tauto j = reinterpret_cast<btTypedConstraint*>(joint->joint);\r\n\tj->setEnabled(enabled);\r\n}\r\nstatic void QDECL World_Bullet_RagCreateJoint(world_t *world, rbejoint_t *joint, rbejointinfo_t *info, rbebody_t *body1, rbebody_t *body2, vec3_t aaa2[3])\r\n{\r\n\tauto ctx = reinterpret_cast<bulletcontext_t*>(world->rbe);\r\n\tbtTypedConstraint *j;\r\n\tbtVector3 org(aaa2[0][0], aaa2[0][1], aaa2[0][2]);\r\n\tbtVector3 axis1(aaa2[1][0], aaa2[1][1], aaa2[1][2]);\r\n\tbtVector3 axis2(aaa2[2][0], aaa2[2][1], aaa2[2][2]);\r\n\tauto rb1 = reinterpret_cast<btRigidBody*>(body1->body);\r\n\tauto rb2 = reinterpret_cast<btRigidBody*>(body2->body);\r\n\r\n\tswitch(info->type)\r\n\t{\r\n\tcase JOINTTYPE_POINT:\r\n\t\t{\r\n\t\t\tauto point = new btPoint2PointConstraint(*rb1, *rb2, rb1->getWorldTransform().getOrigin()-org, rb2->getWorldTransform().getOrigin()-org);\r\n//\t\t\tpoint->setParam(BT_P2P_FLAGS_ERP, info->ERP);\r\n//\t\t\tpoint->setParam(BT_P2P_FLAGS_CFM, info->CFM);\r\n\t\t\tj = point;\r\n\t\t}\r\n\t\tbreak;\r\n\tcase JOINTTYPE_HINGE:\r\n\t\t{\r\n\t\t\tauto hinge = new btHingeConstraint(*rb1, *rb2, org, org, axis1, axis2);\r\n\t\t\thinge->setLimit(info->LoStop, info->HiStop /*other stuff!*/);\r\n//\t\t\thinge->setParam(BT_HINGE_FLAGS_CFM_NORM, info->CFM);\r\n//\t\t\thinge->setParam(BT_HINGE_FLAGS_CFM_STOP, info->CFM2);\r\n//\t\t\thinge->setParam(BT_HINGE_FLAGS_ERP_NORM, info->ERP);\r\n//\t\t\thinge->setParam(BT_HINGE_FLAGS_ERP_STOP, info->ERP2);\r\n\t\t\tj = hinge;\r\n\t\t}\r\n\t\tbreak;\r\n//\tcase JOINTTYPE_SLIDER:\r\n//\t\tj = btSliderConstraint(rb1, rb2, tr1, tr2, false);\r\n//\t\tbreak;\r\n\tcase JOINTTYPE_UNIVERSAL:\r\n\t\t{\r\n\t\t\tauto uni = new btUniversalConstraint(*rb1, *rb2, org, axis1, axis2);\r\n//\t\t\tuni->setParam(BT_6DOF_FLAGS_CFM_NORM, info->CFM);\r\n//\t\t\tuni->setParam(BT_6DOF_FLAGS_CFM_STOP, info->CFM2);\r\n//\t\t\tuni->setParam(BT_6DOF_FLAGS_ERP_STOP, info->ERP);\r\n\t\t\tuni->setUpperLimit(info->HiStop, info->HiStop2);\r\n\t\t\tuni->setLowerLimit(info->LoStop, info->LoStop2);\r\n\t\t\tj = uni;\r\n\t\t}\r\n\t\tbreak;\r\n\tcase JOINTTYPE_HINGE2:\r\n\t\t{\r\n\t\t\tauto hinge2 = new btHinge2Constraint(*rb1, *rb2, org, axis1, axis2);\r\n\t\t\thinge2->setUpperLimit(info->HiStop);\r\n\t\t\thinge2->setLowerLimit(info->LoStop);\r\n\t\t\tj = hinge2;\r\n\t\t}\r\n\t\tbreak;\r\n//\tcase JOINTTYPE_FIXED:\r\n//\t\tj = btFixedConstraint(rb1, rb2, tr1, tr2);\r\n//\t\tbreak;\r\n\tdefault:\r\n\t\tCon_Printf(\"Bullet: joint type %i not supported\\n\", info->type);\r\n\t\tj = nullptr;\r\n\t\tbreak;\r\n\t}\r\n/*\tif (joint->ode_joint)\r\n\t{\r\n\t\t//Con_Printf(\"made new joint %i\\n\", (int) (ed - prog->edicts));\r\n//\t\tdJointSetData(joint->ode_joint, NULL);\r\n\t\tdJointAttach(joint->ode_joint, body1?body1->ode_body:NULL, body2?body2->ode_body:NULL);\r\n\r\n\t\tswitch(info->type)\r\n\t\t{\r\n\t\t\tcase JOINTTYPE_POINT:\r\n\t\t\t\tdJointSetBallAnchor(joint->ode_joint, aaa2[0][0], aaa2[0][1], aaa2[0][2]);\r\n\t\t\t\tbreak;\r\n\t\t\tcase JOINTTYPE_HINGE:\r\n\t\t\t\tdJointSetHingeAnchor(joint->ode_joint, aaa2[0][0], aaa2[0][1], aaa2[0][2]);\r\n\t\t\t\tdJointSetHingeAxis(joint->ode_joint, aaa2[1][0], aaa2[1][1], aaa2[1][2]);\r\n\t\t\t\tdJointSetHingeParam(joint->ode_joint, dParamFMax, info->FMax);\r\n\t\t\t\tdJointSetHingeParam(joint->ode_joint, dParamHiStop, info->HiStop);\t\r\n\t\t\t\tdJointSetHingeParam(joint->ode_joint, dParamLoStop, info->LoStop);\r\n\t\t\t\tdJointSetHingeParam(joint->ode_joint, dParamStopCFM, info->CFM);\r\n\t\t\t\tdJointSetHingeParam(joint->ode_joint, dParamStopERP, info->ERP);\r\n\t\t\t\tdJointSetHingeParam(joint->ode_joint, dParamVel, info->Vel);\r\n\t\t\t\tbreak;\r\n\t\t\tcase JOINTTYPE_SLIDER:\r\n\t\t\t\tdJointSetSliderAxis(joint->ode_joint, aaa2[1][0], aaa2[1][1], aaa2[1][2]);\r\n\t\t\t\tdJointSetSliderParam(joint->ode_joint, dParamFMax, info->FMax);\r\n\t\t\t\tdJointSetSliderParam(joint->ode_joint, dParamHiStop, info->HiStop);\r\n\t\t\t\tdJointSetSliderParam(joint->ode_joint, dParamLoStop, info->LoStop);\r\n\t\t\t\tdJointSetSliderParam(joint->ode_joint, dParamStopCFM, info->CFM);\r\n\t\t\t\tdJointSetSliderParam(joint->ode_joint, dParamStopERP, info->ERP);\r\n\t\t\t\tdJointSetSliderParam(joint->ode_joint, dParamVel, info->Vel);\r\n\t\t\t\tbreak;\r\n\t\t\tcase JOINTTYPE_UNIVERSAL:\r\n\t\t\t\tdJointSetUniversalAnchor(joint->ode_joint, aaa2[0][0], aaa2[0][1], aaa2[0][2]);\r\n\t\t\t\tdJointSetUniversalAxis1(joint->ode_joint, aaa2[1][0], aaa2[1][1], aaa2[1][2]);\r\n\t\t\t\tdJointSetUniversalAxis2(joint->ode_joint, aaa2[2][0], aaa2[2][1], aaa2[2][2]);\r\n\t\t\t\tdJointSetUniversalParam(joint->ode_joint, dParamFMax, info->FMax);\r\n\t\t\t\tdJointSetUniversalParam(joint->ode_joint, dParamHiStop, info->HiStop);\r\n\t\t\t\tdJointSetUniversalParam(joint->ode_joint, dParamLoStop, info->LoStop);\r\n\t\t\t\tdJointSetUniversalParam(joint->ode_joint, dParamStopCFM, info->CFM);\r\n\t\t\t\tdJointSetUniversalParam(joint->ode_joint, dParamStopERP, info->ERP);\r\n\t\t\t\tdJointSetUniversalParam(joint->ode_joint, dParamVel, info->Vel);\r\n\t\t\t\tdJointSetUniversalParam(joint->ode_joint, dParamFMax2, info->FMax2);\r\n\t\t\t\tdJointSetUniversalParam(joint->ode_joint, dParamHiStop2, info->HiStop2);\r\n\t\t\t\tdJointSetUniversalParam(joint->ode_joint, dParamLoStop2, info->LoStop2);\r\n\t\t\t\tdJointSetUniversalParam(joint->ode_joint, dParamStopCFM2, info->CFM2);\r\n\t\t\t\tdJointSetUniversalParam(joint->ode_joint, dParamStopERP2, info->ERP2);\r\n\t\t\t\tdJointSetUniversalParam(joint->ode_joint, dParamVel2, info->Vel2);\r\n\t\t\t\tbreak;\r\n\t\t\tcase JOINTTYPE_HINGE2:\r\n\t\t\t\tdJointSetHinge2Anchor(joint->ode_joint, aaa2[0][0], aaa2[0][1], aaa2[0][2]);\r\n\t\t\t\tdJointSetHinge2Axis1(joint->ode_joint, aaa2[1][0], aaa2[1][1], aaa2[1][2]);\r\n\t\t\t\tdJointSetHinge2Axis2(joint->ode_joint, aaa2[2][0], aaa2[2][1], aaa2[2][2]);\r\n\t\t\t\tdJointSetHinge2Param(joint->ode_joint, dParamFMax, info->FMax);\r\n\t\t\t\tdJointSetHinge2Param(joint->ode_joint, dParamHiStop, info->HiStop);\r\n\t\t\t\tdJointSetHinge2Param(joint->ode_joint, dParamLoStop, info->LoStop);\r\n\t\t\t\tdJointSetHinge2Param(joint->ode_joint, dParamStopCFM, info->CFM);\r\n\t\t\t\tdJointSetHinge2Param(joint->ode_joint, dParamStopERP, info->ERP);\r\n\t\t\t\tdJointSetHinge2Param(joint->ode_joint, dParamVel, info->Vel);\r\n\t\t\t\tdJointSetHinge2Param(joint->ode_joint, dParamFMax2, info->FMax2);\r\n\t\t\t\tdJointSetHinge2Param(joint->ode_joint, dParamHiStop2, info->HiStop2);\r\n\t\t\t\tdJointSetHinge2Param(joint->ode_joint, dParamLoStop2, info->LoStop2);\r\n\t\t\t\tdJointSetHinge2Param(joint->ode_joint, dParamStopCFM2, info->CFM2);\r\n\t\t\t\tdJointSetHinge2Param(joint->ode_joint, dParamStopERP2, info->ERP2);\r\n\t\t\t\tdJointSetHinge2Param(joint->ode_joint, dParamVel2, info->Vel2);\r\n\t\t\t\tbreak;\r\n\t\t\tcase JOINTTYPE_FIXED:\r\n\t\t\t\tdJointSetFixed(joint->ode_joint);\r\n\t\t\t\tbreak;\r\n\t\t}\r\n\t}\r\n*/\r\n\tif (j)\r\n\t\tctx->dworld->addConstraint(j, true);\r\n\tjoint->joint = reinterpret_cast<void*>(j);\r\n}\r\n\r\nstatic void QDECL World_Bullet_RagDestroyBody(world_t *world, rbebody_t *bodyptr)\r\n{\r\n\tauto ctx = reinterpret_cast<bulletcontext_t*>(world->rbe);\r\n\tauto body = reinterpret_cast<btRigidBody*>(bodyptr->body);\r\n\tauto geom = reinterpret_cast<btCollisionShape*>(bodyptr->geom);\r\n\r\n\tbodyptr->body = nullptr;\r\n\tbodyptr->geom = nullptr;\r\n\r\n\tif (body)\r\n\t{\r\n\t\tctx->dworld->removeRigidBody(body);\r\n\t\tdelete body;\r\n\t}\r\n\tif (geom)\r\n\t\tdelete geom;\r\n}\r\n\r\nstatic void QDECL World_Bullet_RagDestroyJoint(world_t *world, rbejoint_t *joint)\r\n{\r\n\tauto ctx = reinterpret_cast<bulletcontext_t*>(world->rbe);\r\n\tauto j = reinterpret_cast<btTypedConstraint*>(joint->joint);\r\n\tif (j)\r\n\t{\r\n\t\tctx->dworld->removeConstraint(j);\r\n\t\tdelete j;\r\n\t}\r\n\tjoint->joint = nullptr;\r\n}\r\n\r\n//bullet gives us a handy way to get/set motion states. we can cheesily update entity fields this way.\r\nclass QCMotionState : public btMotionState\r\n{\r\n\twedict_t *edict;\r\n\tqboolean dirty;\r\n\tbtTransform trans;\r\n\tworld_t *world;\r\n\r\n\r\npublic:\r\n\tvoid ReloadMotionState(void)\r\n\t{\r\n\t\tvec3_t offset;\r\n\t\tvec3_t axis[3];\r\n\t\tbtVector3 org;\r\n\t\trbefuncs->AngleVectors(edict->v->angles, axis[0], axis[1], axis[2]);\r\n\t\tVectorNegate(axis[1], axis[1]);\r\n\t\tVectorAvg(edict->rbe.mins, edict->rbe.maxs, offset);\r\n\t\tVectorMA(edict->v->origin, offset[0]*1, axis[0], org);\r\n\t\torg[3] = 0;//for sse.\r\n\t\tVectorMA(org, offset[1]*1, axis[1], org);\r\n\t\tVectorMA(org, offset[2]*1, axis[2], org);\r\n\r\n\t\ttrans.setBasis(btMatrix3x3(axis[0][0], axis[1][0], axis[2][0],\r\n\t\t\t\t\t\t\t\taxis[0][1],\taxis[1][1],\taxis[2][1],\r\n\t\t\t\t\t\t\t\taxis[0][2],\taxis[1][2],\taxis[2][2]));\r\n\t\ttrans.setOrigin(org);\r\n\t}\r\n\tQCMotionState(wedict_t *ed, world_t *w)\r\n\t{\r\n\t\tdirty = qtrue;\r\n\t\tedict = ed;\r\n\t\tworld = w;\r\n\r\n\t\tReloadMotionState();\r\n\t}\r\n\tvirtual ~QCMotionState()\r\n\t{\r\n\t}\r\n\r\n\tvirtual void getWorldTransform(btTransform &worldTrans) const\r\n\t{\r\n\t\tworldTrans = trans;\r\n\t}\r\n\r\n\tvirtual void setWorldTransform(const btTransform &worldTrans)\r\n\t{\r\n\t\tvec3_t fwd, left, up, offset;\r\n\t\ttrans = worldTrans;\r\n\r\n\t\tbtVector3 pos = worldTrans.getOrigin();\r\n\t\tVectorCopy(worldTrans.getBasis().getColumn(0), fwd);\r\n\t\tVectorCopy(worldTrans.getBasis().getColumn(1), left);\r\n\t\tVectorCopy(worldTrans.getBasis().getColumn(2), up);\r\n\t\tVectorAvg(edict->rbe.mins, edict->rbe.maxs, offset);\r\n\t\tVectorMA(pos, offset[0]*-1, fwd, pos);\r\n\t\tVectorMA(pos, offset[1]*-1, left, pos);\r\n\t\tVectorMA(pos, offset[2]*-1, up, edict->v->origin);\r\n\r\n\t\trbefuncs->VectorAngles(fwd, up, edict->v->angles, (qboolean)NegativeMeshPitch(world, edict));\r\n\r\n\t\tconst btVector3 &vel = ((btRigidBody*)edict->rbe.body.body)->getLinearVelocity();\r\n\t\tVectorCopy(vel.m_floats, edict->v->velocity);\r\n\r\n\t\t//so it doesn't get rebuilt\r\n\t\tVectorCopy(edict->v->origin, edict->rbe.origin);\r\n\t\tVectorCopy(edict->v->angles, edict->rbe.angles);\r\n\t\tVectorCopy(edict->v->velocity, edict->rbe.velocity);\r\n\r\n\t\t//FIXME: relink the ent into the areagrid\r\n\t}\r\n};\r\n\r\nstatic void World_Bullet_Frame_BodyFromEntity(world_t *world, wedict_t *ed)\r\n{\r\n\tauto ctx = reinterpret_cast<bulletcontext_t*>(world->rbe);\r\n\tbtRigidBody *body = nullptr;\r\n//\tbtScalar mass;\r\n\tfloat test;\r\n//\tvoid *dataID;\r\n\tmodel_t *model;\r\n\tint axisindex;\r\n\tint modelindex = 0;\r\n\tint movetype = MOVETYPE_NONE;\r\n\tint solid = SOLID_NOT;\r\n\tint geomtype = GEOMTYPE_SOLID;\r\n\tqboolean modified = qfalse;\r\n\tvec3_t angles;\r\n\tvec3_t avelocity;\r\n\tvec3_t entmaxs;\r\n\tvec3_t entmins;\r\n\tvec3_t forward;\r\n\tvec3_t geomcenter;\r\n\tvec3_t geomsize;\r\n\tvec3_t left;\r\n\tvec3_t origin;\r\n\tvec3_t spinvelocity;\r\n\tvec3_t up;\r\n\tvec3_t velocity;\r\n//\tvec_t f;\r\n\tvec_t length;\r\n\tvec_t massval = 1.0f;\r\n//\tvec_t movelimit;\r\n\tvec_t radius;\r\n\tvec_t scale;\r\n//\tvec_t spinlimit;\r\n\tqboolean gravity;\r\n\r\n\tgeomtype = ed->xv->geomtype;\r\n\tsolid = ed->v->solid;\r\n\tmovetype = ed->v->movetype;\r\n\tscale = ed->xv->scale?ed->xv->scale:1;\r\n\tmodelindex = 0;\r\n\tmodel = nullptr;\r\n\r\n\tif (!geomtype)\r\n\t{\r\n\t\tswitch(solid)\r\n\t\t{\r\n\t\tcase SOLID_NOT:\t\t\t\tgeomtype = GEOMTYPE_NONE;\t\tbreak;\r\n\t\tcase SOLID_TRIGGER:\t\t\tgeomtype = GEOMTYPE_NONE;\t\tbreak;\r\n\t\tcase SOLID_BSP:\t\t\t\tgeomtype = GEOMTYPE_TRIMESH;\tbreak;\r\n\t\tcase SOLID_PHYSICS_TRIMESH:\tgeomtype = GEOMTYPE_TRIMESH;\tbreak;\r\n\t\tcase SOLID_PHYSICS_BOX:\t\tgeomtype = GEOMTYPE_BOX;\t\tbreak;\r\n\t\tcase SOLID_PHYSICS_SPHERE:\tgeomtype = GEOMTYPE_SPHERE;\t\tbreak;\r\n\t\tcase SOLID_PHYSICS_CAPSULE:\tgeomtype = GEOMTYPE_CAPSULE;\tbreak;\r\n\t\tcase SOLID_PHYSICS_CYLINDER:geomtype = GEOMTYPE_CYLINDER;\tbreak;\r\n\t\tdefault:\t\t\t\t\tgeomtype = GEOMTYPE_BOX;\t\tbreak;\r\n\t\t}\r\n\t}\r\n\r\n\tswitch(geomtype)\r\n\t{\r\n\tcase GEOMTYPE_TRIMESH:\r\n\t\tmodelindex = ed->v->modelindex;\r\n\t\tmodel = world->Get_CModel(world, modelindex);\r\n\t\tif (!model || model->loadstate != MLS_LOADED)\r\n\t\t{\r\n\t\t\tmodel = nullptr;\r\n\t\t\tmodelindex = 0;\r\n\t\t}\r\n\t\tif (model)\r\n\t\t{\r\n\t\t\tVectorScale(model->mins, scale, entmins);\r\n\t\t\tVectorScale(model->maxs, scale, entmaxs);\r\n\t\t\tif (ed->xv->mass)\r\n\t\t\t\tmassval = ed->xv->mass;\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tmodelindex = 0;\r\n\t\t\tmassval = 1.0f;\r\n\t\t}\r\n\t\tmassval = 0;\t//bullet does not support trisoup moving through the world.\r\n\t\tbreak;\r\n\tcase GEOMTYPE_BOX:\r\n\tcase GEOMTYPE_SPHERE:\r\n\tcase GEOMTYPE_CAPSULE:\r\n\tcase GEOMTYPE_CAPSULE_X:\r\n\tcase GEOMTYPE_CAPSULE_Y:\r\n\tcase GEOMTYPE_CAPSULE_Z:\r\n\tcase GEOMTYPE_CYLINDER:\r\n\tcase GEOMTYPE_CYLINDER_X:\r\n\tcase GEOMTYPE_CYLINDER_Y:\r\n\tcase GEOMTYPE_CYLINDER_Z:\r\n\t\tVectorCopy(ed->v->mins, entmins);\r\n\t\tVectorCopy(ed->v->maxs, entmaxs);\r\n\t\tif (ed->xv->mass)\r\n\t\t\tmassval = ed->xv->mass;\r\n\t\tbreak;\r\n\tdefault:\r\n//\tcase GEOMTYPE_NONE:\r\n\t\tif (ed->rbe.physics)\r\n\t\t\tWorld_Bullet_RemoveFromEntity(world, ed);\r\n\t\treturn;\r\n\t}\r\n\r\n\tVectorSubtract(entmaxs, entmins, geomsize);\r\n\tif (DotProduct(geomsize,geomsize) == 0)\r\n\t{\r\n\t\t// we don't allow point-size physics objects...\r\n\t\tif (ed->rbe.physics)\r\n\t\t\tWorld_Bullet_RemoveFromEntity(world, ed);\r\n\t\treturn;\r\n\t}\r\n\r\n\t// check if we need to create or replace the geom\r\n\tif (!ed->rbe.physics\r\n\t || !VectorCompare(ed->rbe.mins, entmins)\r\n\t || !VectorCompare(ed->rbe.maxs, entmaxs)\r\n\t || ed->rbe.modelindex != modelindex)\r\n\t{\r\n\t\tbtCollisionShape *geom;\r\n\r\n\t\tmodified = qtrue;\r\n\t\tWorld_Bullet_RemoveFromEntity(world, ed);\r\n\t\ted->rbe.physics = qtrue;\r\n\t\tVectorCopy(entmins, ed->rbe.mins);\r\n\t\tVectorCopy(entmaxs, ed->rbe.maxs);\r\n\t\ted->rbe.modelindex = modelindex;\r\n\t\tVectorAvg(entmins, entmaxs, geomcenter);\r\n\t\ted->rbe.movelimit = min(geomsize[0], min(geomsize[1], geomsize[2]));\r\n\r\n/*\t\tmemset(ed->rbe.offsetmatrix, 0, sizeof(ed->rbe.offsetmatrix));\r\n\t\ted->rbe.offsetmatrix[0] = 1;\r\n\t\ted->rbe.offsetmatrix[5] = 1;\r\n\t\ted->rbe.offsetmatrix[10] = 1;\r\n\t\ted->rbe.offsetmatrix[3] = -geomcenter[0];\r\n\t\ted->rbe.offsetmatrix[7] = -geomcenter[1];\r\n\t\ted->rbe.offsetmatrix[11] = -geomcenter[2];\r\n*/\r\n\t\ted->rbe.mass = massval;\r\n\r\n\t\tswitch(geomtype)\r\n\t\t{\r\n\t\tcase GEOMTYPE_TRIMESH:\r\n//\t\t\tfoo Matrix4x4_Identity(ed->rbe.offsetmatrix);\r\n\t\t\tgeom = nullptr;\r\n\t\t\tif (!model)\r\n\t\t\t{\r\n\t\t\t\tCon_Printf(\"entity %i (classname %s) has no model\\n\", NUM_FOR_EDICT(world->progs, (edict_t*)ed), PR_GetString(world->progs, ed->v->classname));\r\n\t\t\t\tif (ed->rbe.physics)\r\n\t\t\t\t\tWorld_Bullet_RemoveFromEntity(world, ed);\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\t\t\tif (!rbefuncs->GenerateCollisionMesh(world, model, ed, geomcenter))\r\n\t\t\t{\r\n\t\t\t\tif (ed->rbe.physics)\r\n\t\t\t\t\tWorld_Bullet_RemoveFromEntity(world, ed);\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n//\t\t\tfoo Matrix4x4_RM_CreateTranslate(ed->rbe.offsetmatrix, geomcenter[0], geomcenter[1], geomcenter[2]);\r\n\r\n\t\t\t{\r\n\t\t\t\tbtTriangleIndexVertexArray *tiva = new btTriangleIndexVertexArray();\r\n\t\t\t\tbtIndexedMesh mesh;\r\n\t\t\t\tmesh.m_vertexType = PHY_FLOAT;\r\n\t\t\t\tmesh.m_indexType = PHY_INTEGER;\r\n\t\t\t\tmesh.m_numTriangles = ed->rbe.numtriangles;\r\n\t\t\t\tmesh.m_numVertices = ed->rbe.numvertices;\r\n\t\t\t\tmesh.m_triangleIndexBase = (const unsigned char*)ed->rbe.element3i;\r\n\t\t\t\tmesh.m_triangleIndexStride = sizeof(*ed->rbe.element3i)*3;\r\n\t\t\t\tmesh.m_vertexBase = (const unsigned char*)ed->rbe.vertex3f;\r\n\t\t\t\tmesh.m_vertexStride = sizeof(*ed->rbe.vertex3f)*3;\r\n\t\t\t\ttiva->addIndexedMesh(mesh);\r\n\t\t\t\tgeom = new btBvhTriangleMeshShape(tiva, true);\r\n\t\t\t}\r\n\t\t\tbreak;\r\n\r\n\t\tcase GEOMTYPE_BOX:\r\n\t\t\tgeom = new btBoxShape(btVector3(geomsize[0], geomsize[1], geomsize[2]) * 0.5);\r\n\t\t\tbreak;\r\n\r\n\t\tcase GEOMTYPE_SPHERE:\r\n\t\t\tgeom = new btSphereShape(geomsize[0] * 0.5f);\r\n\t\t\tbreak;\r\n\r\n\t\tcase GEOMTYPE_CAPSULE:\r\n\t\tcase GEOMTYPE_CAPSULE_X:\r\n\t\tcase GEOMTYPE_CAPSULE_Y:\r\n\t\tcase GEOMTYPE_CAPSULE_Z:\r\n\t\t\tif (geomtype == GEOMTYPE_CAPSULE)\r\n\t\t\t{\r\n\t\t\t\t// the qc gives us 3 axis radius, the longest axis is the capsule axis\r\n\t\t\t\taxisindex = 0;\r\n\t\t\t\tif (geomsize[axisindex] < geomsize[1])\r\n\t\t\t\t\taxisindex = 1;\r\n\t\t\t\tif (geomsize[axisindex] < geomsize[2])\r\n\t\t\t\t\taxisindex = 2;\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t\taxisindex = geomtype-GEOMTYPE_CAPSULE_X;\r\n\t\t\tif (axisindex == 0)\r\n\t\t\t\tradius = min(geomsize[1], geomsize[2]) * 0.5f;\r\n\t\t\telse if (axisindex == 1)\r\n\t\t\t\tradius = min(geomsize[0], geomsize[2]) * 0.5f;\r\n\t\t\telse\r\n\t\t\t\tradius = min(geomsize[0], geomsize[1]) * 0.5f;\r\n\t\t\tlength = geomsize[axisindex] - radius*2;\r\n\t\t\tif (length <= 0)\r\n\t\t\t{\r\n\t\t\t\tradius -= (1 - length)*0.5;\r\n\t\t\t\tlength = 1;\r\n\t\t\t}\r\n\t\t\tif (axisindex == 0)\r\n\t\t\t\tgeom = new btCapsuleShapeX(radius, length);\r\n\t\t\telse if (axisindex == 1)\r\n\t\t\t\tgeom = new btCapsuleShape(radius, length);\r\n\t\t\telse\r\n\t\t\t\tgeom = new btCapsuleShapeZ(radius, length);\r\n\t\t\tbreak;\r\n\r\n\t\tcase GEOMTYPE_CYLINDER:\r\n\t\tcase GEOMTYPE_CYLINDER_X:\r\n\t\tcase GEOMTYPE_CYLINDER_Y:\r\n\t\tcase GEOMTYPE_CYLINDER_Z:\r\n\t\t\tif (geomtype == GEOMTYPE_CYLINDER)\r\n\t\t\t{\r\n\t\t\t\t// the qc gives us 3 axis radius, the longest axis is the capsule axis\r\n\t\t\t\taxisindex = 0;\r\n\t\t\t\tif (geomsize[axisindex] < geomsize[1])\r\n\t\t\t\t\taxisindex = 1;\r\n\t\t\t\tif (geomsize[axisindex] < geomsize[2])\r\n\t\t\t\t\taxisindex = 2;\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t\taxisindex = geomtype-GEOMTYPE_CYLINDER_X;\r\n\t\t\tif (axisindex == 0)\r\n\t\t\t\tgeom = new btCylinderShapeX(btVector3(geomsize[0], geomsize[1], geomsize[2])*0.5);\r\n\t\t\telse if (axisindex == 1)\r\n\t\t\t\tgeom = new btCylinderShape(btVector3(geomsize[0], geomsize[1], geomsize[2])*0.5);\r\n\t\t\telse\r\n\t\t\t\tgeom = new btCylinderShapeZ(btVector3(geomsize[0], geomsize[1], geomsize[2])*0.5);\r\n\t\t\tbreak;\r\n\r\n\t\tdefault:\r\n//\t\t\tCon_Printf(\"World_Bullet_BodyFromEntity: unrecognized solid value %i was accepted by filter\\n\", solid);\r\n\t\t\tif (ed->rbe.physics)\r\n\t\t\t\tWorld_Bullet_RemoveFromEntity(world, ed);\r\n\t\t\treturn;\r\n\t\t}\r\n//\t\tMatrix3x4_InvertTo4x4_Simple(ed->rbe.offsetmatrix, ed->rbe.offsetimatrix);\r\n//\t\ted->rbe.massbuf = BZ_Malloc(sizeof(dMass));\r\n//\t\tmemcpy(ed->rbe.massbuf, &mass, sizeof(dMass));\r\n\r\n\t\ted->rbe.body.geom = (void *)geom;\r\n\t}\r\n\r\n\t//non-moving objects need to be static objects (and thus need 0 mass)\r\n\tif (movetype != MOVETYPE_PHYSICS && movetype != MOVETYPE_WALK) //enabling kinematic objects for everything else destroys framerates (!movetype || movetype == MOVETYPE_PUSH)\r\n\t\tmassval = 0;\r\n\r\n\t//if the mass changes, we'll need to create a new body (but not the shape, so invalidate the current one)\r\n\tif (ed->rbe.mass != massval)\r\n\t{\r\n\t\ted->rbe.mass = massval;\r\n\t\tbody = (btRigidBody*)ed->rbe.body.body;\r\n\t\tif (body)\r\n\t\t\tctx->dworld->removeRigidBody(body);\r\n\t\ted->rbe.body.body = NULL;\r\n\t}\r\n\r\n//\tif(ed->rbe.body.geom)\r\n//\t\tdGeomSetData(ed->rbe.body.geom, (void*)ed);\r\n\tif (movetype == MOVETYPE_PHYSICS && ed->rbe.mass)\r\n\t{\r\n\t\tif (ed->rbe.body.body == NULL)\r\n\t\t{\r\n//\t\t\ted->rbe.body.body = (void *)(body = dBodyCreate(world->rbe.world));\r\n//\t\t\tdGeomSetBody(ed->rbe.body.geom, body);\r\n//\t\t\tdBodySetData(body, (void*)ed);\r\n//\t\t\tdBodySetMass(body, (dMass *) ed->rbe.massbuf);\r\n\r\n\t\t\tbtVector3 fallInertia(0, 0, 0);\r\n\t\t\t((btCollisionShape*)ed->rbe.body.geom)->calculateLocalInertia(ed->rbe.mass, fallInertia);\r\n\t\t\tbtRigidBody::btRigidBodyConstructionInfo fallRigidBodyCI(ed->rbe.mass, new QCMotionState(ed,world), (btCollisionShape*)ed->rbe.body.geom, fallInertia);\r\n\t\t\tbody = new btRigidBody(fallRigidBodyCI);\r\n\t\t\tbody->setUserPointer(ed);\r\n//\t\t\tbtTransform trans;\r\n//\t\t\ttrans.setFromOpenGLMatrix(ed->rbe.offsetmatrix);\r\n//\t\t\tbody->setCenterOfMassTransform(trans);\r\n\t\t\ted->rbe.body.body = (void*)body;\r\n\r\n\t\t\t//motion threshhold should be speed/physicsframerate.\r\n\t\t\t//Threshhold enables CCD when the object moves faster than X\r\n\t\t\t//FIXME: recalculate...\r\n\t\t\tbody->setCcdMotionThreshold((geomsize[0]+geomsize[1]+geomsize[2])*(4/3));\r\n\t\t\t//radius should be the body's radius, or smaller.\r\n\t\t\tbody->setCcdSweptSphereRadius((geomsize[0]+geomsize[1]+geomsize[2])*(0.5/3));\r\n\r\n\t\t\tctx->dworld->addRigidBody(body, ed->xv->dimension_solid, ed->xv->dimension_hit);\r\n\r\n\t\t\tmodified = qtrue;\r\n\t\t}\r\n\t}\r\n\telse\r\n\t{\r\n\t\tif (ed->rbe.body.body == nullptr)\r\n\t\t{\r\n\t\t\tbtRigidBody::btRigidBodyConstructionInfo rbci(ed->rbe.mass, new QCMotionState(ed,world), (btCollisionShape*)ed->rbe.body.geom, btVector3(0, 0, 0));\r\n\t\t\tbody = new btRigidBody(rbci);\r\n\t\t\tbody->setUserPointer(ed);\r\n//\t\t\tbtTransform trans;\r\n//\t\t\ttrans.setFromOpenGLMatrix(ed->rbe.offsetmatrix);\r\n//\t\t\tbody->setCenterOfMassTransform(trans);\r\n\t\t\ted->rbe.body.body = (void*)body;\r\n\t\t\tif (ed->rbe.mass)\r\n\t\t\t\tbody->setCollisionFlags(body->getCollisionFlags() | btCollisionObject::CF_KINEMATIC_OBJECT);\r\n\t\t\telse\r\n\t\t\t\tbody->setCollisionFlags(body->getCollisionFlags() | btCollisionObject::CF_STATIC_OBJECT);\r\n\t\t\tctx->dworld->addRigidBody(body, ed->xv->dimension_solid, ed->xv->dimension_hit);\r\n\r\n\t\t\tmodified = qtrue;\r\n\t\t}\r\n\t}\r\n\r\n\tbody = (btRigidBody*)ed->rbe.body.body;\r\n\r\n\t// get current data from entity\r\n\tgravity = qtrue;\r\n\tVectorCopy(ed->v->origin, origin);\r\n\tVectorCopy(ed->v->velocity, velocity);\r\n\tVectorCopy(ed->v->angles, angles);\r\n\tVectorCopy(ed->v->avelocity, avelocity);\r\n\tif (ed == world->edicts || (ed->xv->gravity && ed->xv->gravity <= 0.01))\r\n\t\tgravity = qfalse;\r\n\r\n\t// compatibility for legacy entities\r\n//\tif (!DotProduct(forward,forward) || solid == SOLID_BSP)\r\n\t{\r\n\t\tvec3_t qangles, qavelocity;\r\n\t\tVectorCopy(angles, qangles);\r\n\t\tVectorCopy(avelocity, qavelocity);\r\n\t\r\n\t\tif (ed->v->modelindex)\r\n\t\t{\r\n\t\t\tmodel = world->Get_CModel(world, ed->v->modelindex);\r\n\t\t\tif (!model || model->type == mod_alias)\r\n\t\t\t{\r\n\t\t\t\tqangles[PITCH] *= -1;\r\n\t\t\t\tqavelocity[PITCH] *= -1;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\trbefuncs->AngleVectors(qangles, forward, left, up);\r\n\t\tVectorNegate(left,left);\r\n\t\t// convert single-axis rotations in avelocity to spinvelocity\r\n\t\t// FIXME: untested math - check signs\r\n\t\tVectorSet(spinvelocity, DEG2RAD(qavelocity[PITCH]), DEG2RAD(qavelocity[ROLL]), DEG2RAD(qavelocity[YAW]));\r\n\t}\r\n\r\n\t// compatibility for legacy entities\r\n\tswitch (solid)\r\n\t{\r\n\tcase SOLID_BBOX:\r\n\tcase SOLID_SLIDEBOX:\r\n\tcase SOLID_CORPSE:\r\n\t\tVectorSet(forward, 1, 0, 0);\r\n\t\tVectorSet(left, 0, 1, 0);\r\n\t\tVectorSet(up, 0, 0, 1);\r\n\t\tVectorSet(spinvelocity, 0, 0, 0);\r\n\t\tbreak;\r\n\t}\r\n\r\n\r\n\t// we must prevent NANs...\r\n\ttest = DotProduct(origin,origin) + DotProduct(forward,forward) + DotProduct(left,left) + DotProduct(up,up) + DotProduct(velocity,velocity) + DotProduct(spinvelocity,spinvelocity);\r\n\tif (IS_NAN(test))\r\n\t{\r\n\t\tmodified = qtrue;\r\n\t\t//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_EDICTFIELDVALUE(ed, prog->fieldoffsets.classname)->string), 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]);\r\n\t\tCon_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\", NUM_FOR_EDICT(world->progs, (edict_t*)ed), PR_GetString(world->progs, ed->v->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]);\r\n\t\ttest = DotProduct(origin,origin);\r\n\t\tif (IS_NAN(test))\r\n\t\t\tVectorClear(origin);\r\n\t\ttest = DotProduct(forward,forward) * DotProduct(left,left) * DotProduct(up,up);\r\n\t\tif (IS_NAN(test))\r\n\t\t{\r\n\t\t\tVectorSet(angles, 0, 0, 0);\r\n\t\t\tVectorSet(forward, 1, 0, 0);\r\n\t\t\tVectorSet(left, 0, 1, 0);\r\n\t\t\tVectorSet(up, 0, 0, 1);\r\n\t\t}\r\n\t\ttest = DotProduct(velocity,velocity);\r\n\t\tif (IS_NAN(test))\r\n\t\t\tVectorClear(velocity);\r\n\t\ttest = DotProduct(spinvelocity,spinvelocity);\r\n\t\tif (IS_NAN(test))\r\n\t\t{\r\n\t\t\tVectorClear(avelocity);\r\n\t\t\tVectorClear(spinvelocity);\r\n\t\t}\r\n\t}\r\n\r\n\t// check if the qc edited any position data\r\n\tif (\r\n\t\t0//!VectorCompare(origin, ed->rbe.origin)\r\n\t || !VectorCompare(velocity, ed->rbe.velocity)\r\n\t //|| !VectorCompare(angles, ed->rbe.angles)\r\n\t || !VectorCompare(avelocity, ed->rbe.avelocity)\r\n\t || gravity != ed->rbe.gravity)\r\n\t\tmodified = qtrue;\r\n\r\n\t// store the qc values into the physics engine\r\n\tbody = (btRigidBody*)ed->rbe.body.body;\r\n\tif (modified && body)\r\n\t{\r\n//\t\tdVector3 r[3];\r\n//\t\tfloat entitymatrix[16];\r\n//\t\tfloat bodymatrix[16];\r\n\r\n#if 0\r\n\t\tCon_Printf(\"entity %i got changed by QC\\n\", (int) (ed - prog->edicts));\r\n\t\tif(!VectorCompare(origin, ed->rbe.origin))\r\n\t\t\tCon_Printf(\"  origin: %f %f %f -> %f %f %f\\n\", ed->rbe.origin[0], ed->rbe.origin[1], ed->rbe.origin[2], origin[0], origin[1], origin[2]);\r\n\t\tif(!VectorCompare(velocity, ed->rbe.velocity))\r\n\t\t\tCon_Printf(\"  velocity: %f %f %f -> %f %f %f\\n\", ed->rbe.velocity[0], ed->rbe.velocity[1], ed->rbe.velocity[2], velocity[0], velocity[1], velocity[2]);\r\n\t\tif(!VectorCompare(angles, ed->rbe.angles))\r\n\t\t\tCon_Printf(\"  angles: %f %f %f -> %f %f %f\\n\", ed->rbe.angles[0], ed->rbe.angles[1], ed->rbe.angles[2], angles[0], angles[1], angles[2]);\r\n\t\tif(!VectorCompare(avelocity, ed->rbe.avelocity))\r\n\t\t\tCon_Printf(\"  avelocity: %f %f %f -> %f %f %f\\n\", ed->rbe.avelocity[0], ed->rbe.avelocity[1], ed->rbe.avelocity[2], avelocity[0], avelocity[1], avelocity[2]);\r\n\t\tif(gravity != ed->rbe.gravity)\r\n\t\t\tCon_Printf(\"  gravity: %i -> %i\\n\", ed->ide.ode_gravity, gravity);\r\n#endif\r\n\r\n\t\t// values for BodyFromEntity to check if the qc modified anything later\r\n\t\tVectorCopy(origin, ed->rbe.origin);\r\n\t\tVectorCopy(velocity, ed->rbe.velocity);\r\n\t\tVectorCopy(angles, ed->rbe.angles);\r\n\t\tVectorCopy(avelocity, ed->rbe.avelocity);\r\n\t\ted->rbe.gravity = gravity;\r\n\r\n//\t\tfoo Matrix4x4_RM_FromVectors(entitymatrix, forward, left, up, origin);\r\n//\t\tfoo Matrix4_Multiply(ed->rbe.offsetmatrix, entitymatrix, bodymatrix);\r\n//\t\tfoo Matrix3x4_RM_ToVectors(bodymatrix, forward, left, up, origin);\r\n\r\n//\t\tr[0][0] = forward[0];\r\n//\t\tr[1][0] = forward[1];\r\n//\t\tr[2][0] = forward[2];\r\n//\t\tr[0][1] = left[0];\r\n//\t\tr[1][1] = left[1];\r\n//\t\tr[2][1] = left[2];\r\n//\t\tr[0][2] = up[0];\r\n//\t\tr[1][2] = up[1];\r\n//\t\tr[2][2] = up[2];\r\n\r\n\t\tauto ms = (QCMotionState*)body->getMotionState();\r\n\t\tms->ReloadMotionState();\r\n\t\tbody->setMotionState(ms);\r\n\t\tbody->setLinearVelocity(btVector3(velocity[0], velocity[1], velocity[2]));\r\n\t\tbody->setAngularVelocity(btVector3(spinvelocity[0], spinvelocity[1], spinvelocity[2]));\r\n//\t\tbody->setGravity(btVector3(ed->xv->gravitydir[0], ed->xv->gravitydir[1], ed->xv->gravitydir[2]) * ed->xv->gravity);\r\n\r\n\t\t//something changed. make sure it still falls over appropriately\r\n\t\tbody->setActivationState(1);\r\n\t}\r\n\r\n/* FIXME: check if we actually need this insanity with bullet (ode sucks).\r\n\tif(body)\r\n\t{\r\n\t\t// limit movement speed to prevent missed collisions at high speed\r\n\t\tbtVector3 ovelocity = body->getLinearVelocity();\r\n\t\tbtVector3 ospinvelocity = body->getAngularVelocity();\r\n\t\tmovelimit = ed->rbe.movelimit * world->rbe.movelimit;\r\n\t\ttest = DotProduct(ovelocity,ovelocity);\r\n\t\tif (test > movelimit*movelimit)\r\n\t\t{\r\n\t\t\t// scale down linear velocity to the movelimit\r\n\t\t\t// scale down angular velocity the same amount for consistency\r\n\t\t\tf = movelimit / sqrt(test);\r\n\t\t\tVectorScale(ovelocity, f, velocity);\r\n\t\t\tVectorScale(ospinvelocity, f, spinvelocity);\r\n\t\t\tbody->setLinearVelocity(btVector3(velocity[0], velocity[1], velocity[2]));\r\n\t\t\tbody->setAngularVelocity(btVector3(spinvelocity[0], spinvelocity[1], spinvelocity[2]));\r\n\t\t}\r\n\r\n\t\t// make sure the angular velocity is not exploding\r\n\t\tspinlimit = physics_bullet_spinlimit.value;\r\n\t\ttest = DotProduct(ospinvelocity,ospinvelocity);\r\n\t\tif (test > spinlimit)\r\n\t\t{\r\n\t\t\tbody->setAngularVelocity(btVector3(0, 0, 0));\r\n\t\t}\r\n\t}\r\n*/\r\n}\r\n\r\n/*\r\n#define MAX_CONTACTS 16\r\nstatic void VARGS nearCallback (void *data, dGeomID o1, dGeomID o2)\r\n{\r\n\tworld_t *world = (world_t *)data;\r\n\tdContact contact[MAX_CONTACTS]; // max contacts per collision pair\r\n\tdBodyID b1;\r\n\tdBodyID b2;\r\n\tdJointID c;\r\n\tint i;\r\n\tint numcontacts;\r\n\r\n\tfloat bouncefactor1 = 0.0f;\r\n\tfloat bouncestop1 = 60.0f / 800.0f;\r\n\tfloat bouncefactor2 = 0.0f;\r\n\tfloat bouncestop2 = 60.0f / 800.0f;\r\n\tfloat erp;\r\n\tdVector3 grav;\r\n\twedict_t *ed1, *ed2;\r\n\r\n\tif (dGeomIsSpace(o1) || dGeomIsSpace(o2))\r\n\t{\r\n\t\t// colliding a space with something\r\n\t\tdSpaceCollide2(o1, o2, data, &nearCallback);\r\n\t\t// Note we do not want to test intersections within a space,\r\n\t\t// only between spaces.\r\n\t\t//if (dGeomIsSpace(o1)) dSpaceCollide(o1, data, &nearCallback);\r\n\t\t//if (dGeomIsSpace(o2)) dSpaceCollide(o2, data, &nearCallback);\r\n\t\treturn;\r\n\t}\r\n\r\n\tb1 = dGeomGetBody(o1);\r\n\tb2 = dGeomGetBody(o2);\r\n\r\n\t// at least one object has to be using MOVETYPE_PHYSICS or we just don't care\r\n\tif (!b1 && !b2)\r\n\t\treturn;\r\n\r\n\t// exit without doing anything if the two bodies are connected by a joint\r\n\tif (b1 && b2 && dAreConnectedExcluding(b1, b2, dJointTypeContact))\r\n\t\treturn;\r\n\r\n\ted1 = (wedict_t *) dGeomGetData(o1);\r\n\ted2 = (wedict_t *) dGeomGetData(o2);\r\n\tif (ed1 == ed2 && ed1)\r\n\t{\r\n\t\t//ragdolls don't make contact with the bbox of the doll entity\r\n\t\t//the origional entity should probably not be solid anyway.\r\n\t\t//these bodies should probably not collide against bboxes of other entities with ragdolls either, but meh.\r\n\t\tif (ed1->rbe.body.body == b1 || ed2->rbe.body == b2)\r\n\t\t\treturn;\r\n\t}\r\n\tif(!ed1 || ed1->isfree)\r\n\t\ted1 = world->edicts;\r\n\tif(!ed2 || ed2->isfree)\r\n\t\ted2 = world->edicts;\r\n\r\n\t// generate contact points between the two non-space geoms\r\n\tnumcontacts = dCollide(o1, o2, MAX_CONTACTS, &(contact[0].geom), sizeof(contact[0]));\r\n\tif (numcontacts)\r\n\t{\r\n\t\tif(ed1 && ed1->v->touch)\r\n\t\t{\r\n\t\t\tworld->Event_Touch(world, ed1, ed2);\r\n\t\t}\r\n\t\tif(ed2 && ed2->v->touch)\r\n\t\t{\r\n\t\t\tworld->Event_Touch(world, ed2, ed1);\r\n\t\t}\r\n\r\n\t\t// if either ent killed itself, don't collide \r\n\t\tif ((ed1&&ed1->isfree) || (ed2&&ed2->isfree))\r\n\t\t\treturn;\r\n\t}\r\n\r\n\tif(ed1)\r\n\t{\r\n\t\tif (ed1->xv->bouncefactor)\r\n\t\t\tbouncefactor1 = ed1->xv->bouncefactor;\r\n\r\n\t\tif (ed1->xv->bouncestop)\r\n\t\t\tbouncestop1 = ed1->xv->bouncestop;\r\n\t}\r\n\r\n\tif(ed2)\r\n\t{\r\n\t\tif (ed2->xv->bouncefactor)\r\n\t\t\tbouncefactor2 = ed2->xv->bouncefactor;\r\n\r\n\t\tif (ed2->xv->bouncestop)\r\n\t\t\tbouncestop2 = ed2->xv->bouncestop;\r\n\t}\r\n\r\n\tif ((ed2->entnum&&ed1->v->owner == ed2->entnum) || (ed1->entnum&&ed2->v->owner == ed1->entnum))\r\n\t\treturn;\r\n\r\n\t// merge bounce factors and bounce stop\r\n\tif(bouncefactor2 > 0)\r\n\t{\r\n\t\tif(bouncefactor1 > 0)\r\n\t\t{\r\n\t\t\t// TODO possibly better logic to merge bounce factor data?\r\n\t\t\tif(bouncestop2 < bouncestop1)\r\n\t\t\t\tbouncestop1 = bouncestop2;\r\n\t\t\tif(bouncefactor2 > bouncefactor1)\r\n\t\t\t\tbouncefactor1 = bouncefactor2;\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tbouncestop1 = bouncestop2;\r\n\t\t\tbouncefactor1 = bouncefactor2;\r\n\t\t}\r\n\t}\r\n\tdWorldGetGravity(world->rbe.world, grav);\r\n\tbouncestop1 *= fabs(grav[2]);\r\n\r\n\terp = (DotProduct(ed1->v->velocity, ed1->v->velocity) > DotProduct(ed2->v->velocity, ed2->v->velocity)) ? ed1->xv->erp : ed2->xv->erp;\r\n\r\n\t// add these contact points to the simulation\r\n\tfor (i = 0;i < numcontacts;i++)\r\n\t{\r\n\t\tcontact[i].surface.mode =\t(physics_bullet_contact_mu.value != -1 ? dContactApprox1 : 0) |\r\n\t\t\t\t\t\t\t\t\t(physics_bullet_contact_erp.value != -1 ? dContactSoftERP : 0) |\r\n\t\t\t\t\t\t\t\t\t(physics_bullet_contact_cfm.value != -1 ? dContactSoftCFM : 0) |\r\n\t\t\t\t\t\t\t\t\t(bouncefactor1 > 0 ? dContactBounce : 0);\r\n\t\tcontact[i].surface.mu = physics_bullet_contact_mu.value;\r\n\t\tif (ed1->xv->friction)\r\n\t\t\tcontact[i].surface.mu *= ed1->xv->friction;\r\n\t\tif (ed2->xv->friction)\r\n\t\t\tcontact[i].surface.mu *= ed2->xv->friction;\r\n\t\tcontact[i].surface.mu2 = 0;\r\n\t\tcontact[i].surface.soft_erp = physics_bullet_contact_erp.value + erp;\r\n\t\tcontact[i].surface.soft_cfm = physics_bullet_contact_cfm.value;\r\n\t\tcontact[i].surface.bounce = bouncefactor1;\r\n\t\tcontact[i].surface.bounce_vel = bouncestop1;\r\n\t\tc = dJointCreateContact(world->rbe.world, world->rbe.contactgroup, contact + i);\r\n\t\tdJointAttach(c, b1, b2);\r\n\t}\r\n}\r\n*/\r\n\r\nstatic void QDECL World_Bullet_Frame(world_t *world, double frametime, double gravity)\r\n{\r\n\tauto ctx = reinterpret_cast<bulletcontext_t*>(world->rbe);\r\n\tif (world->rbe_hasphysicsents || ctx->hasextraobjs)\r\n\t{\r\n\t\tint iters;\r\n\t\tunsigned int i;\r\n\t\twedict_t *ed;\r\n\r\n//\t\tworld->rbe.iterations = bound(1, physics_bullet_iterationsperframe.ival, 1000);\r\n//\t\tworld->rbe.step = frametime / world->rbe.iterations;\r\n//\t\tworld->rbe.movelimit = physics_bullet_movelimit.value / world->rbe.step;\r\n\r\n\r\n\t\t// copy physics properties from entities to physics engine\r\n\t\tfor (i = 0;i < world->num_edicts;i++)\r\n\t\t{\r\n\t\t\ted = WEDICT_NUM_PB(world->progs, i);\r\n\t\t\tif (!ED_ISFREE(ed))\r\n\t\t\t\tWorld_Bullet_Frame_BodyFromEntity(world, ed);\r\n\t\t}\r\n\t\t// oh, and it must be called after all bodies were created\r\n\t\tfor (i = 0;i < world->num_edicts;i++)\r\n\t\t{\r\n\t\t\ted = WEDICT_NUM_PB(world->progs, i);\r\n\t\t\tif (!ED_ISFREE(ed))\r\n\t\t\t\tWorld_Bullet_Frame_JointFromEntity(world, ed);\r\n\t\t}\r\n\t\twhile(ctx->cmdqueuehead)\r\n\t\t{\r\n\t\t\tauto cmd = ctx->cmdqueuehead;\r\n\t\t\tctx->cmdqueuehead = cmd->next;\r\n\t\t\tif (!cmd->next)\r\n\t\t\t\tctx->cmdqueuetail = nullptr;\r\n\t\t\tWorld_Bullet_RunCmd(world, cmd);\r\n\t\t\tZ_Free(cmd);\r\n\t\t}\r\n\r\n\t\tctx->dworld->setGravity(btVector3(0, 0, -gravity));\r\n\r\n\t\titers=physics_bullet_maxiterationsperframe->value;\r\n\t\tif (iters < 0)\r\n\t\t\titers = 0;\r\n\t\tctx->dworld->stepSimulation(frametime, iters, 1/bound(1, physics_bullet_framerate->value, 500));\r\n\r\n\t\t// set the tolerance for closeness of objects\r\n//\t\tdWorldSetContactSurfaceLayer(world->rbe.world, max(0, physics_bullet_contactsurfacelayer.value));\r\n\r\n\t\t// run collisions for the current world state, creating JointGroup\r\n//\t\tdSpaceCollide(world->rbe.space, (void *)world, nearCallback);\r\n\r\n\t\t// run physics (move objects, calculate new velocities)\r\n//\t\tif (physics_bullet_worldquickstep.ival)\r\n//\t\t{\r\n//\t\t\tdWorldSetQuickStepNumIterations(world->rbe.world, bound(1, physics_bullet_worldquickstep_iterations.ival, 200));\r\n//\t\t\tdWorldQuickStep(world->rbe.world, world->rbe.step);\r\n//\t\t}\r\n//\t\telse\r\n//\t\t\tdWorldStep(world->rbe.world, world->rbe.step);\r\n\r\n\t\t// clear the JointGroup now that we're done with it\r\n//\t\tdJointGroupEmpty(world->rbe.contactgroup);\r\n\t}\r\n}\r\n\r\nstatic void World_Bullet_RunCmd(world_t *world, rbecommandqueue_t *cmd)\r\n{\r\n\tauto body = (btRigidBody*)(cmd->edict->rbe.body.body);\r\n\tswitch(cmd->command)\r\n\t{\r\n\tcase RBECMD_ENABLE:\r\n\t\tif (body)\r\n\t\t\tbody->setActivationState(1);\r\n\t\tbreak;\r\n\tcase RBECMD_DISABLE:\r\n\t\tif (body)\r\n\t\t\tbody->setActivationState(0);\r\n\t\tbreak;\r\n\tcase RBECMD_FORCE:\r\n\t\tif (body)\r\n\t\t{\r\n\t\t\tbtVector3 relativepos;\r\n\t\t\tconst btVector3 &center = body->getCenterOfMassPosition();\r\n\t\t\tVectorSubtract(cmd->v2, center, relativepos);\r\n\t\t\tbody->setActivationState(1);\r\n\t\t\tbody->applyImpulse(btVector3(cmd->v1[0], cmd->v1[1], cmd->v1[2]), relativepos);\r\n\t\t}\r\n\t\tbreak;\r\n\tcase RBECMD_TORQUE:\r\n\t\tif (cmd->edict->rbe.body.body)\r\n\t\t{\r\n\t\t\tbody->setActivationState(1);\r\n\t\t\tbody->applyTorque(btVector3(cmd->v1[0], cmd->v1[1], cmd->v1[2]));\r\n\t\t}\r\n\t\tbreak;\r\n\t}\r\n}\r\n\r\nstatic void QDECL World_Bullet_PushCommand(world_t *world, rbecommandqueue_t *val)\r\n{\r\n\tauto ctx = reinterpret_cast<bulletcontext_t*>(world->rbe);\r\n\tauto cmd = (rbecommandqueue_t*)BZ_Malloc(sizeof(rbecommandqueue_t));\r\n\tworld->rbe_hasphysicsents = qtrue;\t//just in case.\r\n\tmemcpy(cmd, val, sizeof(*cmd));\r\n\tcmd->next = nullptr;\r\n\t//add on the end of the queue, so that order is preserved.\r\n\tif (ctx->cmdqueuehead)\r\n\t{\r\n\t\tauto ot = ctx->cmdqueuetail;\r\n\t\tot->next = ctx->cmdqueuetail = cmd;\r\n\t}\r\n\telse\r\n\t\tctx->cmdqueuetail = ctx->cmdqueuehead = cmd;\r\n}\r\n\r\nstatic void QDECL World_Bullet_TraceEntity(world_t *world, wedict_t *ed, vec3_t start, vec3_t end, trace_t *trace)\r\n{\r\n\tauto ctx = reinterpret_cast<bulletcontext_t*>(world->rbe);\r\n\tauto shape = (btCollisionShape*)ed->rbe.body.geom;\r\n\r\n//btCollisionAlgorithm\r\n\tclass myConvexResultCallback : public btCollisionWorld::ConvexResultCallback\r\n\t{\r\n\tpublic:\r\n\t\tvoid *m_impactent;\r\n\t\tbtVector3 m_impactpos;\r\n\t\tbtVector3 m_impactnorm;\r\n\t\tvirtual\tbtScalar\taddSingleResult(btCollisionWorld::LocalConvexResult& convexResult,bool normalInWorldSpace)\r\n\t\t{\r\n\t\t\tif (m_closestHitFraction > convexResult.m_hitFraction)\r\n\t\t\t{\r\n\t\t\t\tm_closestHitFraction = convexResult.m_hitFraction;\r\n\t\t\t\tm_impactpos = convexResult.m_hitPointLocal;\r\n\t\t\t\tm_impactnorm = convexResult.m_hitNormalLocal;\r\n\t\t\t\tm_impactent = convexResult.m_hitCollisionObject->getUserPointer();\r\n\t\t\t}\r\n\t\t\treturn 0;\r\n\t\t}\r\n\t} result;\r\n\tresult.m_impactent = nullptr;\r\n\tresult.m_closestHitFraction = trace->fraction;\r\n\r\n\tbtTransform from(btMatrix3x3(1, 0, 0, 0, 1, 0, 0, 0, 1), btVector3(start[0], start[1], start[2]));\r\n\tbtTransform to(btMatrix3x3(1, 0, 0, 0, 1, 0, 0, 0, 1), btVector3(end[0], end[1], end[2]));\r\n\tctx->dworld->convexSweepTest((btConvexShape*)shape, from, to, result, 1);\r\n\r\n\tif (result.m_impactent)\r\n\t{\r\n\t\tmemset(trace, 0, sizeof(*trace));\r\n\t\ttrace->fraction = trace->truefraction = result.m_closestHitFraction;\r\n\t\tVectorInterpolate(start, result.m_closestHitFraction, end, trace->endpos);\r\n//\t\tVectorCopy(result.m_impactpos, trace->endpos);\r\n\t\tVectorCopy(result.m_impactnorm, trace->plane.normal);\r\n\t\ttrace->ent = result.m_impactent;\r\n\t\ttrace->startsolid = qfalse; //FIXME: we don't really know\r\n\t}\r\n}\r\n\r\nstatic void QDECL World_Bullet_Start(world_t *world)\r\n{\r\n\tbulletcontext_t *ctx;\r\n\tif (world->rbe)\r\n\t\treturn;\t//no thanks, we already have one. somehow.\r\n\r\n\tctx = reinterpret_cast<bulletcontext_t*>(BZ_Malloc(sizeof(*ctx)));\r\n\tmemset(ctx, 0, sizeof(*ctx));\r\n\tctx->gworld = world;\r\n\tctx->funcs.End\t\t\t\t\t\t= World_Bullet_End;\r\n\tctx->funcs.RemoveJointFromEntity\t= World_Bullet_RemoveJointFromEntity;\r\n\tctx->funcs.RemoveFromEntity\t\t\t= World_Bullet_RemoveFromEntity;\r\n\tctx->funcs.RagMatrixToBody\t\t\t= World_Bullet_RagMatrixToBody;\r\n\tctx->funcs.RagCreateBody\t\t\t= World_Bullet_RagCreateBody;\r\n\tctx->funcs.RagMatrixFromJoint\t\t= World_Bullet_RagMatrixFromJoint;\r\n\tctx->funcs.RagMatrixFromBody\t\t= World_Bullet_RagMatrixFromBody;\r\n\tctx->funcs.RagEnableJoint\t\t\t= World_Bullet_RagEnableJoint;\r\n\tctx->funcs.RagCreateJoint\t\t\t= World_Bullet_RagCreateJoint;\r\n\tctx->funcs.RagDestroyBody\t\t\t= World_Bullet_RagDestroyBody;\r\n\tctx->funcs.RagDestroyJoint\t\t\t= World_Bullet_RagDestroyJoint;\r\n\tctx->funcs.RunFrame\t\t\t\t\t= World_Bullet_Frame;\r\n\tctx->funcs.PushCommand\t\t\t\t= World_Bullet_PushCommand;\r\n\tctx->funcs.Trace\t\t\t\t\t= World_Bullet_TraceEntity;\r\n\tworld->rbe = &ctx->funcs;\r\n\r\n\r\n\tctx->broadphase = new btDbvtBroadphase();\r\n\tctx->collisionconfig = new btDefaultCollisionConfiguration();\r\n\tctx->collisiondispatcher = new btCollisionDispatcher(ctx->collisionconfig);\r\n\tctx->solver = new btSequentialImpulseConstraintSolver;\r\n\tctx->dworld = new btDiscreteDynamicsWorld(ctx->collisiondispatcher, ctx->broadphase, ctx->solver, ctx->collisionconfig);\r\n\r\n\tctx->ownerfilter = new QCFilterCallback();\r\n\tctx->dworld->getPairCache()->setOverlapFilterCallback(ctx->ownerfilter);\r\n\r\n\r\n\r\n\tctx->dworld->setGravity(btVector3(0, -10, 0));\r\n\r\n/*\r\n\tif(physics_bullet_world_erp.value >= 0)\r\n\t\tdWorldSetERP(world->rbe.world, physics_bullet_world_erp.value);\r\n\tif(physics_bullet_world_cfm.value >= 0)\r\n\t\tdWorldSetCFM(world->rbe.world, physics_bullet_world_cfm.value);\r\n\tif (physics_bullet_world_damping.ival)\r\n\t{\r\n\t\tdWorldSetLinearDamping(world->rbe.world, (physics_bullet_world_damping_linear.value >= 0) ? (physics_bullet_world_damping_linear.value * physics_bullet_world_damping.value) : 0);\r\n\t\tdWorldSetLinearDampingThreshold(world->rbe.world, (physics_bullet_world_damping_linear_threshold.value >= 0) ? (physics_bullet_world_damping_linear_threshold.value * physics_bullet_world_damping.value) : 0);\r\n\t\tdWorldSetAngularDamping(world->rbe.world, (physics_bullet_world_damping_angular.value >= 0) ? (physics_bullet_world_damping_angular.value * physics_bullet_world_damping.value) : 0);\r\n\t\tdWorldSetAngularDampingThreshold(world->rbe.world, (physics_bullet_world_damping_angular_threshold.value >= 0) ? (physics_bullet_world_damping_angular_threshold.value * physics_bullet_world_damping.value) : 0);\r\n\t}\r\n\telse\r\n\t{\r\n\t\tdWorldSetLinearDamping(world->rbe.world, 0);\r\n\t\tdWorldSetLinearDampingThreshold(world->rbe.world, 0);\r\n\t\tdWorldSetAngularDamping(world->rbe.world, 0);\r\n\t\tdWorldSetAngularDampingThreshold(world->rbe.world, 0);\r\n\t}\r\n\tif (physics_bullet_autodisable.ival)\r\n\t{\r\n\t\tdWorldSetAutoDisableSteps(world->rbe.world, bound(1, physics_bullet_autodisable_steps.ival, 100)); \r\n\t\tdWorldSetAutoDisableTime(world->rbe.world, physics_bullet_autodisable_time.value);\r\n\t\tdWorldSetAutoDisableAverageSamplesCount(world->rbe.world, bound(1, physics_bullet_autodisable_threshold_samples.ival, 100));\r\n\t\tdWorldSetAutoDisableLinearThreshold(world->rbe.world, physics_bullet_autodisable_threshold_linear.value); \r\n\t\tdWorldSetAutoDisableAngularThreshold(world->rbe.world, physics_bullet_autodisable_threshold_angular.value); \r\n\t\tdWorldSetAutoDisableFlag (world->rbe.world, true);\r\n\t}\r\n\telse\r\n\t\tdWorldSetAutoDisableFlag (world->rbe.world, false);\r\n\t*/\r\n}\r\n\r\nstatic void QDECL World_Bullet_Shutdown(void)\r\n{\r\n\tif (rbefuncs)\r\n\t\trbefuncs->UnregisterPhysicsEngine(\"Bullet\");\r\n}\r\n\r\nstatic bool World_Bullet_DoInit(void)\r\n{\r\n\tif (!rbefuncs || !rbefuncs->RegisterPhysicsEngine)\r\n\t{\r\n\t\trbefuncs = nullptr;\r\n\t\tCon_Printf(\"Bullet plugin failed: Engine is incompatible.\\n\");\r\n\t}\r\n\telse if (!rbefuncs->RegisterPhysicsEngine(\"Bullet\", World_Bullet_Start))\r\n\t\tCon_Printf(\"Bullet plugin failed: Engine already has a physics plugin active.\\n\");\r\n\telse\r\n\t{\r\n\t\tWorld_Bullet_Init();\r\n\t\treturn true;\r\n\t}\r\n\treturn false;\r\n}\r\n\r\nextern \"C\" qboolean Plug_Init(void)\r\n{\r\n\trbefuncs = (rbeplugfuncs_t*)plugfuncs->GetEngineInterface(\"RBE\", sizeof(*rbefuncs));\r\n\tif (rbefuncs && (\trbefuncs->version < RBEPLUGFUNCS_VERSION ||\r\n\t\t\t\t\t\trbefuncs->wedictsize != sizeof(wedict_t)))\r\n\t\trbefuncs = nullptr;\r\n\r\n\tplugfuncs->ExportFunction(\"Shutdown\", (funcptr_t)World_Bullet_Shutdown);\r\n\treturn World_Bullet_DoInit()?qtrue:qfalse;\r\n}\r\n\r\n\r\n"
  },
  {
    "path": "plugins/cef/cef.c",
    "content": "//fte defs\n#include \"../plugin.h\"\n#include \"../engine.h\"\n\nstatic plugfsfuncs_t *fsfuncs;\nstatic plugsubconsolefuncs_t *confuncs;\nstatic plugclientfuncs_t *clientfuncs;\n\n#pragma GCC diagnostic ignored \"-Wstrict-prototypes\"\t//not my bug.\n\n//libcef defs\n#include \"include/cef_version.h\"\n#include \"include/capi/cef_app_capi.h\"\n#include \"include/capi/cef_client_capi.h\"\n#include \"include/capi/cef_parser_capi.h\"\n#include \"include/capi/cef_request_context_handler_capi.h\"\n//#include \"include/capi/cef_url_capi.h\"\n#include \"assert.h\"\n#if defined(_DEBUG) && defined(_MSC_VER)\n\t#include <crtdbg.h>\n#endif\n#ifndef _WIN32\n#include <unistd.h>\n#endif\n#include <sys/types.h>\n#include <sys/stat.h>\n\n#define EXPECTED_COMMIT_NUMBER  2179\t//last version of libcef we tried building against...\n#if EXPECTED_COMMIT_NUMBER != EXPECTED_COMMIT_NUMBER\n\t#warning \"libcef version different from expected. expect problems with libcef's unstable API.\"\n#endif\n\n#define cef_addref(ptr)\t\t(ptr)->base.add_ref(&(ptr)->base)\n#define cef_release(ptr)\t(((ptr)->base.release)(&(ptr)->base))\n\n#if !defined(LIBCEF_STATIC) && !defined(LIBCEF_DYNAMIC)\n\t#define LIBCEF_DYNAMIC\n#endif\n#ifdef LIBCEF_DYNAMIC\n//avoid conflicts with cef headers\n#define cef_version_info\t\t\t\t\t\tpcef_version_info\n#define cef_initialize\t\t\t\t\t\t\tpcef_initialize\n#define cef_do_message_loop_work\t\t\t\tpcef_do_message_loop_work\n#define cef_shutdown\t\t\t\t\t\t\tpcef_shutdown\n#define cef_execute_process\t\t\t\t\t\tpcef_execute_process\n#define cef_browser_host_create_browser_sync\tpcef_browser_host_create_browser_sync\n#define cef_string_utf8_to_utf16\t\t\t\tpcef_string_utf8_to_utf16\n#define cef_string_utf16_to_utf8\t\t\t\tpcef_string_utf16_to_utf8\n#define cef_string_utf16_clear\t\t\t\t\tpcef_string_utf16_clear\n#define cef_string_utf16_set\t\t\t\t\tpcef_string_utf16_set\n#define cef_string_utf8_clear\t\t\t\t\tpcef_string_utf8_clear\n#define cef_string_utf8_set\t\t\t\t\t\tpcef_string_utf8_set\n#define cef_string_userfree_utf16_free\t\t\tpcef_string_userfree_utf16_free\n#define cef_register_scheme_handler_factory\t\tpcef_register_scheme_handler_factory\n#define cef_get_mime_type\t\t\t\t\t\tpcef_get_mime_type\n#define cef_v8value_create_function\t\t\t\tpcef_v8value_create_function\n#define cef_v8value_create_string\t\t\t\tpcef_v8value_create_string\n#define cef_process_message_create\t\t\t\tpcef_process_message_create\n#define cef_v8context_get_current_context\t\tpcef_v8context_get_current_context\n#define\tcef_post_task\t\t\t\t\t\t\tpcef_post_task\n#define cef_request_context_create_context\t\tpcef_request_context_create_context\n#define cef_string_multimap_alloc\t\t\t\tpcef_string_multimap_alloc\n#define cef_string_multimap_append\t\t\t\tpcef_string_multimap_append\n#define cef_string_multimap_size\t\t\t\tpcef_string_multimap_size\n#define cef_string_multimap_key\t\t\t\t\tpcef_string_multimap_key\n#define cef_string_multimap_value\t\t\t\tpcef_string_multimap_value\n#define cef_string_multimap_free\t\t\t\tpcef_string_multimap_free\n#define cef_string_list_size\t\t\t\t\tpcef_string_list_size\n#define cef_string_list_value\t\t\t\t\tpcef_string_list_value\n\nstatic int\t\t\t\t\t\t(*cef_version_info)(int entry);\nstatic int\t\t\t\t\t\t(*cef_initialize)(const struct _cef_main_args_t* args, const cef_settings_t* settings, cef_app_t* application, void* windows_sandbox_info);\nstatic void\t\t\t\t\t\t(*cef_do_message_loop_work)(void);\nstatic void\t\t\t\t\t\t(*cef_shutdown)(void);\nstatic int\t\t\t\t\t\t(*cef_execute_process)(const cef_main_args_t* args, cef_app_t* application, void* windows_sandbox_info);\nstatic cef_browser_t*\t\t\t(*cef_browser_host_create_browser_sync)(const cef_window_info_t* windowInfo, cef_client_t* client, const cef_string_t* url, const cef_browser_settings_t* settings, cef_dictionary_value_t* extra_info, cef_request_context_t* request_context);\nstatic int\t\t\t\t\t\t(*cef_string_utf8_to_utf16)(const char* src, size_t src_len, cef_string_utf16_t* output);\nstatic int\t\t\t\t\t\t(*cef_string_utf16_to_utf8)(const char16* src, size_t src_len, cef_string_utf8_t* output);\nstatic void\t\t\t\t\t\t(*cef_string_utf16_clear)(cef_string_utf16_t* str);\nstatic int\t\t\t\t\t\t(*cef_string_utf16_set)(const char16* src, size_t src_len, cef_string_utf16_t* output, int copy);\nstatic void\t\t\t\t\t\t(*cef_string_utf8_clear)(cef_string_utf8_t* str);\nstatic int\t\t\t\t\t\t(*cef_string_utf8_set)(const char* src, size_t src_len, cef_string_utf8_t* output, int copy);\nstatic void\t\t\t\t\t\t(*cef_string_userfree_utf16_free)(cef_string_userfree_utf16_t str);\nstatic int\t\t\t\t\t\t(*cef_register_scheme_handler_factory)(const cef_string_t* scheme_name, const cef_string_t* domain_name, cef_scheme_handler_factory_t* factory);\nstatic cef_string_userfree_t\t(*cef_get_mime_type)(const cef_string_t* extension);\nstatic cef_v8value_t*\t\t\t(*cef_v8value_create_function)(const cef_string_t* name, cef_v8handler_t* handler);\nstatic cef_v8value_t*\t\t\t(*cef_v8value_create_string)(const cef_string_t* value);\nstatic cef_process_message_t*\t(*cef_process_message_create)(const cef_string_t* name);\nstatic cef_v8context_t*\t\t\t(*cef_v8context_get_current_context)(void);\t//typical C++ programmers omitted the void.\nstatic int\t\t\t\t\t\t(*cef_post_task)(cef_thread_id_t threadId, cef_task_t* task);\nstatic cef_request_context_t*\t(*cef_request_context_create_context)(const cef_request_context_settings_t* settings, cef_request_context_handler_t* handler);\nstatic cef_string_multimap_t\t(*cef_string_multimap_alloc)(void);\nstatic int\t\t\t\t\t\t(*cef_string_multimap_append)(cef_string_multimap_t map, const cef_string_t* key, const cef_string_t* value);\nstatic size_t\t\t\t\t\t(*cef_string_multimap_size)(cef_string_multimap_t map);\nstatic int\t\t\t\t\t\t(*cef_string_multimap_key)(cef_string_multimap_t map, size_t index, cef_string_t* key);\nstatic int\t\t\t\t\t\t(*cef_string_multimap_value)(cef_string_multimap_t map, size_t index, cef_string_t* value);\nstatic void\t\t\t\t\t\t(*cef_string_multimap_free)(cef_string_multimap_t map);\nstatic size_t\t\t\t\t\t(*cef_string_list_size)(cef_string_list_t list);\nstatic int\t\t\t\t\t\t(*cef_string_list_value)(cef_string_list_t list, size_t index, cef_string_t* value);\n#endif\n\nstatic cvar_t\t*cef_incognito;\nstatic cvar_t\t*cef_allowplugins;\nstatic cvar_t\t*cef_allowcvars;\nstatic cvar_t\t*cef_devtools;\n\nstatic char plugname[MAX_OSPATH];\nstatic char *newconsole;\n\n/*static void setcefstring(char *str, cef_string_t *r)\n{\n\tcef_string_from_utf8(str, strlen(str), r);\n}*/\nstatic cef_string_t makecefstring(char *str)\n{\n\tcef_string_t r = {NULL};\n\tcef_string_from_utf8(str, strlen(str), &r);\n\treturn r;\n}\nstatic cef_string_t *makecefstringptr(char *str, cef_string_t *ptr)\n{\n\tcef_string_from_utf8(str, strlen(str), ptr);\n\treturn ptr;\n}\n\nstatic char *Info_JSONify (char *s, char *o, size_t outlen)\n{\n\toutlen--;\t//so we don't have to consider nulls\n\n\tif (*s == '\\\\')\n\t\ts++;\n\twhile (*s)\n\t{\n\t\t//min overhead\n\t\tif (outlen < 6)\n\t\t\tbreak;\n\t\toutlen -= 6;\n\n\t\t*o++ = ',';\n\t\t*o++ = '\\\"';\n\t\tfor (; *s && *s != '\\\\'; s++)\n\t\t{\n\t\t\tif (*s != '\\\"' && outlen)\n\t\t\t{\n\t\t\t\toutlen--;\n\t\t\t\t*o++ = *s;\n\t\t\t}\n\t\t}\n\t\t*o++ = '\\\"';\n\t\t*o++ = ':';\n\t\t*o++ = '\\\"';\n\n\t\tif (!*s++)\n\t\t{\n\t\t\t//should never happen.\n\t\t\t*o++ = '\\\"';\n\t\t\t*o = 0;\n\t\t\treturn o;\n\t\t}\n\n\t\tfor (; *s && *s != '\\\\'; s++)\n\t\t{\n\t\t\tif (*s != '\\\"' && outlen)\n\t\t\t{\n\t\t\t\toutlen--;\n\t\t\t\t*o++ = *s;\n\t\t\t}\n\t\t}\n\n\t\t*o++ = '\\\"';\n\n\t\tif (*s)\n\t\t\ts++;\n\t}\n\t*o = 0;\n\treturn o;\n}\n\n#ifdef _MSC_VER\n\t#define atomic_fetch_add(p,v) (InterlockedIncrement(p)-1)\n\t#define atomic_fetch_sub(p,v) (InterlockedDecrement(p)+1)\n\t#define atomic_uint32_t LONG\n#else\n\t#define atomic_fetch_add __sync_fetch_and_add\n\t#define atomic_fetch_sub __sync_fetch_and_sub\n\t#define atomic_uint32_t unsigned int\n#endif\n\ntypedef struct\n{\n\tatomic_uint32_t refcount;\t//this needs to be atomic to avoid multiple threads adding at the same time\n\t//cef interface objects\n\tcef_client_t client;\n\tcef_render_handler_t render_handler;\n\tcef_display_handler_t display_handler;\n\tcef_request_handler_t request_handler;\n\tcef_life_span_handler_t life_span_handler;\n\tcef_context_menu_handler_t context_menu_handler;\n\tcef_browser_t *thebrowser;\n\n\tvoid *videodata;\n\tint videowidth;\n\tint videoheight;\n\tqboolean updated;\n\tint desiredwidth;\n\tint desiredheight;\n\tchar *consolename;\t//for internal plugin use.\n\tqboolean fullscreen;\n\tcef_string_utf8_t currenturl;\n\tcef_string_utf8_t currenticon;\n\tcef_string_utf8_t currenttitle;\n\tcef_string_utf8_t currentstatus;\n\n\tcef_mouse_event_t mousepos;\n\tunsigned char keystate[K_MAX];\n} browser_t;\nunsigned int numbrowsers;\n\nstatic void browser_addref(browser_t *br)\n{\n\tatomic_fetch_add(&br->refcount, 1);\n}\nstatic int browser_release(browser_t *br)\n{\n\tif (atomic_fetch_sub(&br->refcount, 1) == 1)\n\t{\n\t\tif (br->consolename)\n\t\t\tfree(br->consolename);\n\t\tif (br->videodata)\n\t\t\tfree(br->videodata);\n\t\tcef_string_utf8_clear(&br->currenturl);\n\t\tcef_string_utf8_clear(&br->currenticon);\n\t\tcef_string_utf8_clear(&br->currenttitle);\n\t\tcef_string_utf8_clear(&br->currentstatus);\n\n\t\tnumbrowsers--;\n\n\t\tfree(br);\n\t\treturn true;\n\t}\n\treturn false;\n}\n\n#define browser_subs(sub) \\\n\tstatic void CEF_CALLBACK browser_##sub##_addref(cef_base_ref_counted_t* self) {browser_t *br = (browser_t*)((char*)self - offsetof(browser_t, sub.base)); browser_addref(br);};\t\\\n\tstatic int CEF_CALLBACK browser_##sub##_release(cef_base_ref_counted_t* self) {browser_t *br = (browser_t*)((char*)self - offsetof(browser_t, sub.base)); return browser_release(br);};\t\\\n\tstatic int CEF_CALLBACK browser_##sub##_hasoneref(cef_base_ref_counted_t* self) {browser_t *br = (browser_t*)((char*)self - offsetof(browser_t, sub.base)); return br->refcount == 1;};\nbrowser_subs(client);\nbrowser_subs(render_handler);\nbrowser_subs(display_handler);\nbrowser_subs(request_handler);\nbrowser_subs(life_span_handler);\nbrowser_subs(context_menu_handler);\n#undef browser_subs\n\n//client methods\nstatic cef_render_handler_t *CEF_CALLBACK browser_get_render_handler(cef_client_t *self)\n{\n\tbrowser_t *br = (browser_t*)((char*)self - offsetof(browser_t, client));\n\tcef_addref(&br->render_handler);\n\treturn &br->render_handler;\n}\nstatic cef_life_span_handler_t *CEF_CALLBACK browser_get_life_span_handler(cef_client_t *self)\n{\n\tbrowser_t *br = (browser_t*)((char*)self - offsetof(browser_t, client));\n\tcef_addref(&br->life_span_handler);\n\treturn &br->life_span_handler;\n}\nstatic cef_context_menu_handler_t *CEF_CALLBACK browser_get_context_menu_handler(cef_client_t *self)\n{\n\tbrowser_t *br = (browser_t*)((char*)self - offsetof(browser_t, client));\n\tcef_addref(&br->context_menu_handler);\n\treturn &br->context_menu_handler;\n}\nstatic cef_display_handler_t *CEF_CALLBACK browser_get_display_handler(cef_client_t *self)\n{\n\tbrowser_t *br = (browser_t*)((char*)self - offsetof(browser_t, client));\n\tcef_addref(&br->display_handler);\n\treturn &br->display_handler;\n}\nstatic cef_request_handler_t *CEF_CALLBACK browser_get_request_handler(cef_client_t *self)\n{\n\tbrowser_t *br = (browser_t*)((char*)self - offsetof(browser_t, client));\n\tcef_addref(&br->request_handler);\n\treturn &br->request_handler;\n}\n\nstatic qboolean browser_handle_query(const char *req, char *buffer, size_t buffersize)\n{\n\tif (!req)\n\t\treturn false;\n\telse if (!strncmp(req, \"getcvar_\", 8))\n\t{\n\t\tif (cef_allowcvars->value && cvarfuncs->GetString(req+8, buffer, buffersize))\n\t\t\treturn true;\n\t}\n\telse if (!strncmp(req, \"setcvar_\", 8))\n\t{\n\t\tconst char *eq = strchr(req+8, '=');\n\t\tif (eq)\n\t\t\t*(char*)eq++ = 0;\n\t\telse\n\t\t\teq = req+strlen(req);\n\n\t\tif (cef_allowcvars->value)\n\t\t{\n\t\t\tcvarfuncs->SetString(req+8, eq);\n\t\t\t*buffer = 0;\n\t\t\treturn true;\n\t\t}\n\t}\n\telse if (!strcmp(req, \"getstats\"))\n\t{\t//1 [, one sign, 10 chars, one ], one comma\n\t\t//FIXME: should be more than just a one-off.\n\t\tunsigned int stats[256], i, m;\n\t\tchar *e = buffer;\n\t\tm = clientfuncs->GetStats(0, stats, countof(stats));\n\t\tif (!m)\n\t\t{\n\t\t\tm = 0;\n\t\t\tstats[m++] = 0;\n\t\t}\n\n\t\t*e++ = '[';\n\t\tfor (i = 0; i < m; i++)\n\t\t{\n\t\t\tif (i)\n\t\t\t\t*e++ = ',';\n\t\t\t\n\t\t\tsprintf(e, \"%i\", (int)stats[i]);\n\t\t\te += strlen(e);\n\t\t}\n\t\t*e++ = ']';\n\t\t*e = 0;\n\t\tassert(e <= buffer + buffersize);\n\t\treturn true;\n\t}\n\telse if (!strcmp(req, \"getseats\"))\n\t{\n\t\tint i;\n\t\tchar *e = buffer;\n\t\tint players[MAX_SPLITS];\n\t\tint tracks[MAX_SPLITS];\n\t\tint seats = clientfuncs->GetLocalPlayerNumbers(0, MAX_SPLITS, players, tracks);\n\t\t*e++ = '[';\n\t\tfor (i = 0; i < seats; i++)\n\t\t{\n\t\t\tif (i)\n\t\t\t\t*e++ = ',';\n\t\t\tsprintf(e, \"{\\\"player\\\":%i,\\\"track\\\":%i}\", players[i], tracks[i]); e += strlen(e);\n\t\t}\n\t\t*e++ = ']';\n\t\t*e = 0;\n\t\tassert(e <= buffer + buffersize);\n\t\treturn true;\n\t}\n\telse if (!strcmp(req, \"getserverinfo\"))\n\t{\n\t\tchar serverinfo[4096];\n\t\tchar *e = buffer;\n\t\tclientfuncs->GetServerInfoRaw(serverinfo, sizeof(serverinfo));\n\t\te = Info_JSONify(serverinfo, e, buffer + buffersize - e-1);\n\t\tif (e == buffer) e++;\n\t\t*buffer = '{';\n\t\t*e++ = '}';\n\t\t*e = 0;\n\t\tassert(e <= buffer + buffersize);\n\t\treturn true;\n\t}\n\telse if (!strcmp(req, \"getplayers\"))\n\t{\n\t\tunsigned int i;\n\t\tchar *e = buffer;\n\t\tplugclientinfo_t info;\n\n\t\t*e++ = '[';\n\t\tfor (i = 0; ; i++)\n\t\t{\n\t\t\tclientfuncs->GetPlayerInfo(i, &info);\n\n\t\t\tif (buffer + buffersize - e-1 < 100)\n\t\t\t\tbreak;\n\n\t\t\tif (i)\n\t\t\t\t*e++ = ',';\n\t\t\t*e++ = '{';\n\t\t\t//splurge the specific info\n\t\t\tsprintf(e, \"\\\"frags\\\":%i,\\\"ping\\\":%i,\\\"pl\\\":%i,\\\"active\\\":%i,\\\"userid\\\":%i\", info.frags, info.ping, info.pl, info.activetime, info.userid);\n\t\t\te += strlen(e);\n\t\t\t//splurge the generic info (colours, name, team)\n\t\t\te = Info_JSONify(info.userinfo, e, buffer + buffersize - e-1);\n\t\t\t*e++ = '}';\n\t\t}\n\t\t*e++ = ']';\n\t\t*e = 0;\n\t\tassert(e <= buffer + buffersize);\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\nstatic int CEF_CALLBACK browser_on_process_message_received(cef_client_t* self, cef_browser_t* browser, cef_frame_t* frame, cef_process_id_t source_process, cef_process_message_t* message)\n{\n\tint handled = false;\n//\tbrowser_t *br = (browser_t*)((char*)self - offsetof(browser_t, request_handler));\n\tcef_string_userfree_t msgnameunusable = message->get_name(message);\n\tcef_string_utf8_t name = {NULL};\n\tcef_string_to_utf8(msgnameunusable->str, msgnameunusable->length, &name);\n\tif (!strcmp(name.str, \"fte_query\"))\n\t{\n\t\tchar buffer[8192];\n\t\tint id1, id2;\n\t\tcef_process_message_t *reply;\n\t\tcef_string_utf8_t queryname = {NULL};\n\t\tcef_string_t str = {NULL};\n\t\tcef_list_value_t *args = message->get_argument_list(message);\n\t\tcef_string_userfree_t cmdunusable = args->get_string(args, 0);\n\t\tcef_string_to_utf8(cmdunusable?cmdunusable->str:NULL, cmdunusable?cmdunusable->length:0, &queryname);\n\n\t\tid1 = args->get_int(args, 2);\n\t\tid2 = args->get_int(args, 3);\n\t\tcef_release(args);\n\n\t\treply = cef_process_message_create(msgnameunusable);\n\t\targs = reply->get_argument_list(reply);\n\t\targs->set_string(args, 0, cmdunusable);\n\t\targs->set_int(args, 2, id1);\n\t\targs->set_int(args, 3, id2);\n\n\t\tif (browser_handle_query(queryname.str, buffer, sizeof(buffer)))\n\t\t{\n\t\t\tstr = makecefstring(buffer);\n\t\t\targs->set_string(args, 1, &str);\n\t\t\tcef_string_clear(&str);\n\t\t}\n\t\telse\n\t\t\targs->set_null(args, 1);\n\t\tcef_release(args);\n\t\tcef_string_utf8_clear(&queryname);\n\t\tif (cmdunusable)\n\t\t\tcef_string_userfree_free(cmdunusable);\n\t\tframe->send_process_message(frame, source_process, reply);\n\t\thandled = true;\n\t}\n\tcef_release(message);\n\tcef_release(browser);\n\tcef_string_utf8_clear(&name);\n\tcef_string_userfree_free(msgnameunusable);\n//\tif (messagerouter->OnProcessMessageReceived(browser, source_process, message))\n//\t\treturn true;\n\treturn handled;\n}\n\n//render_handler methods\nstatic void CEF_CALLBACK browser_get_view_rect(cef_render_handler_t *self, cef_browser_t *browser, cef_rect_t *rect)\n{\n\tbrowser_t *br = (browser_t*)((char*)self - offsetof(browser_t, render_handler));\n\n\trect->x = 0;\n\trect->y = 0;\n\trect->width = br->desiredwidth;\n\trect->height = br->desiredheight;\n\tcef_release(browser);\n}\nstatic void CEF_CALLBACK browser_on_paint(cef_render_handler_t *self, cef_browser_t *browser, cef_paint_element_type_t type, size_t dirtyRectsCount, cef_rect_t const* dirtyRects, const void* buffer, int width, int height)\n{\n\tbrowser_t *br = (browser_t*)((char*)self - offsetof(browser_t, render_handler));\n\n\tcef_release(browser);\n\n\t//popups are gonna be so awkward...\n\tif (type != PET_VIEW)\n\t\treturn;\n\n\t/*\n\tif (pbocontext)\n\t{\n\t\tif (!PBO_Lock(pbocontext, width, height, &lost, rgba))\n\t\t\treturn;\n\t\tif (lost)\n\t\t\tPBO_Update(pbocontext, buffer, width, height, stride);\n\t\telse while (dirtyRectsCount --> 0)\n\t\t\tPBO_Update(pbocontext, (char*)buffer+(width*dirtyRects->y + dirtyRects->x)*4), dirtyRects->width, dirtyRects->height, width*4);\n\t\tPBO_Unlock(pbocontext);\n\t}\n\telse\n\t*/\n\n\tif (br->videowidth != width || br->videoheight != height)\n\t{\t//copy the entire thing.\n\t\tif (br->videodata)\n\t\t\tfree(br->videodata);\n\t\tbr->videowidth = width;\n\t\tbr->videoheight = height;\n\t\tbr->videodata = malloc(width * height * 4);\n\t\tmemcpy(br->videodata, buffer, width * height * 4);\n\t}\n\telse\n\t{\t//try to save cpu time by copying only the dirty parts\n\t\twhile (dirtyRectsCount --> 0)\n\t\t{\n\t\t\tif (width == dirtyRects->width && height == dirtyRects->height)\n\t\t\t\tmemcpy(br->videodata, buffer, width * height * 4);\n\t\t\telse\n\t\t\t{\n\t\t\t\tint y;\n\t\t\t\tconst unsigned int *src;\n\t\t\t\tunsigned int *dst;\n\t\t\t\tsrc = buffer;\n\t\t\t\tsrc += width * dirtyRects->y + dirtyRects->x;\n\t\t\t\tdst = br->videodata;\n\t\t\t\tdst += width * dirtyRects->y + dirtyRects->x;\n\n\t\t\t\tfor (y = 0; y < dirtyRects->height; y++)\n\t\t\t\t{\n\t\t\t\t\tmemcpy(dst, src, dirtyRects->width*4);\n\t\t\t\t\tsrc += width;\n\t\t\t\t\tdst += width;\n\t\t\t\t}\n\t\t\t}\n\t\t\tdirtyRects++;\n\t\t}\n\t}\n\tbr->updated = true;\n}\n\nstatic void CEF_CALLBACK browser_on_before_close(cef_life_span_handler_t* self, cef_browser_t* browser)\n{\n\tbrowser_t *br = (browser_t*)((char*)self - offsetof(browser_t, life_span_handler));\n\tif (br->thebrowser)\n\t{\t//we may have already released our reference to this, if something else was blocking for some reason.\n\t\tcef_release(br->thebrowser);\n\t\tbr->thebrowser = NULL;\n\t}\n\tcef_release(browser);\n}\n\n//context_menu_handler methods\nstatic void CEF_CALLBACK browser_on_before_context_menu(struct _cef_context_menu_handler_t* self, struct _cef_browser_t* browser, struct _cef_frame_t* frame, struct _cef_context_menu_params_t* params, struct _cef_menu_model_t* model)\n{\n//\tbrowser_t *br = (browser_t*)((char*)self - offsetof(browser_t, context_menu_handler));\n\n\t//wipe whatever elements libcef thinks it should add\n\tmodel->clear(model);\n\n\t//don't bother adding any new ones.\n\n\n\tcef_release(browser);\n\tcef_release(frame);\n\tcef_release(params);\n\tcef_release(model);\n}\n\n//display_handler methods\n//redirect console.log messages to quake's console, but only display them if we've got developer set.\nstatic int CEF_CALLBACK browser_on_console_message(cef_display_handler_t* self, cef_browser_t* browser, cef_log_severity_t level, const cef_string_t* message, const cef_string_t* source, int line)\n{\n\tcef_string_utf8_t u8_source = {NULL};\n\tcef_string_utf8_t u8_message = {NULL};\n\tif (source)\n\t\tcef_string_to_utf8(source->str, source->length, &u8_source);\n\tif (message)\n\t\tcef_string_to_utf8(message->str, message->length, &u8_message);\n\n\tCon_DPrintf(\"%s:%i: %s\\n\", u8_source.str, line, u8_message.str);\n\n\tcef_string_utf8_clear(&u8_source);\n\tcef_string_utf8_clear(&u8_message);\n\tcef_release(browser);\n\treturn true;\n}\nstatic void CEF_CALLBACK browser_on_title_change(cef_display_handler_t* self, cef_browser_t* browser, const cef_string_t* title)\n{\n\tbrowser_t *br = (browser_t*)((char*)self - offsetof(browser_t, display_handler));\n\tif (title)\n\t\tcef_string_to_utf8(title->str, title->length, &br->currenttitle);\n\telse\n\t\tcef_string_utf8_copy(br->currenturl.str, br->currenturl.length, &br->currenttitle);\n\n\tif (br->consolename)\n\t\tconfuncs->SetConsoleString(br->consolename, \"title\", br->currenttitle.str?br->currenttitle.str:\"\");\n\n\tcef_release(browser);\n}\nstatic void CEF_CALLBACK browser_on_favicon_urlchange(cef_display_handler_t* self, cef_browser_t* browser, cef_string_list_t favicon)\n{\n\tbrowser_t *br = (browser_t*)((char*)self - offsetof(browser_t, display_handler));\n\tcef_string_t str = {NULL};\n\n\tif (favicon)\n\t{\n\t\t//size_t opts = cef_string_list_size(favicon);\n\t\tcef_string_list_value(favicon, 0, &str);\n\t}\n\n\tcef_string_to_utf8(str.str, str.length, &br->currenticon);\n\tcef_string_clear(&str);\n\n\tif (br->consolename)\n\t\tconfuncs->SetConsoleString(br->consolename, \"icon\", br->currenticon.str?br->currenticon.str:\"\");\n\n\tcef_release(browser);\n}\nstatic void CEF_CALLBACK browser_on_fullscreenmode_change(cef_display_handler_t* self, cef_browser_t* browser, int fullscreen)\n{\n\tbrowser_t *br = (browser_t*)((char*)self - offsetof(browser_t, display_handler));\n\tbr->fullscreen = fullscreen;\n\n\tif (br->consolename)\n\t\tconfuncs->SetConsoleFloat(br->consolename, \"fullscreen\", br->fullscreen);\n\n\tcef_release(browser);\n}\nstatic void CEF_CALLBACK browser_on_address_change(cef_display_handler_t* self, cef_browser_t* browser, cef_frame_t* frame, const cef_string_t* url)\n{\n\tbrowser_t *br = (browser_t*)((char*)self - offsetof(browser_t, display_handler));\n\n\tcef_string_to_utf8(url->str, url->length, &br->currenturl);\n\n\tif (br->currenticon.length)\n\t{\n\t\tcef_string_utf8_clear(&br->currenticon);\n\t\tif (br->consolename)\n\t\t\tconfuncs->SetConsoleString(br->consolename, \"icon\", br->currenticon.str?br->currenticon.str:\"\");\n\t}\n\n\t//FIXME: should probably make sure its the root frame\n//\tCon_Printf(\"new url: %s\\n\", url.ToString().c_str());\n\tcef_release(browser);\n\tcef_release(frame);\n}\nstatic int CEF_CALLBACK browser_on_tooltip(cef_display_handler_t* self, cef_browser_t* browser, cef_string_t* text)\n{\n\tbrowser_t *br = (browser_t*)((char*)self - offsetof(browser_t, display_handler));\n\tif (br->consolename)\n\t{\n\t\tcef_string_utf8_t u8_text = {NULL};\n\t\tcef_string_to_utf8(text->str, text->length, &u8_text);\n\t\tconfuncs->SetConsoleString(br->consolename, \"tooltip\", u8_text.str?u8_text.str:\"\");\n\t\tcef_string_utf8_clear(&u8_text);\n\t}\n\tcef_release(browser);\n\treturn true;\t//cef won't draw tooltips when running like this\n}\nstatic void CEF_CALLBACK browser_on_status_message(cef_display_handler_t* self, cef_browser_t* browser, const cef_string_t* value)\n{\n\tbrowser_t *br = (browser_t*)((char*)self - offsetof(browser_t, display_handler));\n\tif (br->consolename)\n\t{\n\t\tcef_string_utf8_t u8_value = {NULL};\n\t\tif (value)\n\t\t\tcef_string_to_utf8(value->str, value->length, &u8_value);\n\t\tconfuncs->SetConsoleString(br->consolename, \"footer\", u8_value.str?u8_value.str:\"\");\n\t\tcef_string_utf8_clear(&u8_value);\n\t}\n\n\tcef_release(browser);\n}\n\n//request_handler methods\n\n\nstatic int CEF_CALLBACK browser_on_before_browse(cef_request_handler_t* self, cef_browser_t* browser, cef_frame_t* frame, cef_request_t* request, int user_gesture, int is_redirect)\n{\n//\tbrowser_t *br = (browser_t*)((char*)self - offsetof(browser_t, request_handler));\n\n\tcef_release(browser);\n\tcef_release(frame);\n\tcef_release(request);\n\n\treturn false;\t//false = allow navigation, true = block\n}\nstatic void CEF_CALLBACK browser_on_render_process_terminated(cef_request_handler_t* self, cef_browser_t* browser, cef_termination_status_t status)\n{\n\tbrowser_t *br = (browser_t*)((char*)self - offsetof(browser_t, request_handler));\n\tif (br->videodata)\n\t\tfree(br->videodata);\n\tbr->videowidth = 1;\n\tbr->videoheight = 1;\n\tbr->videodata = malloc(br->videowidth * br->videoheight * 4);\n\tmemset(br->videodata, 0, br->videowidth * br->videoheight * 4);\n\tbr->updated = true;\n\n\tcef_release(browser);\n}\n\n\nstatic browser_t *browser_create(void)\n{\n\tbrowser_t *nb = malloc(sizeof(*nb));\n\tmemset(nb, 0, sizeof(*nb));\n\tnb->refcount = 1;\n\n#define browser_subs(sub) \\\n\tnb->sub.base.add_ref = browser_##sub##_addref;\t\t\\\n\tnb->sub.base.release = browser_##sub##_release;\t\\\n\tnb->sub.base.has_one_ref = browser_##sub##_hasoneref;\t\\\n\tnb->sub.base.size = sizeof(nb->sub);\nbrowser_subs(client);\nbrowser_subs(render_handler);\nbrowser_subs(display_handler);\nbrowser_subs(request_handler);\nbrowser_subs(life_span_handler);\nbrowser_subs(context_menu_handler);\n#undef browser_subs\n\n\tnb->client.get_context_menu_handler = browser_get_context_menu_handler;\n\tnb->client.get_dialog_handler = NULL;\n\tnb->client.get_display_handler = browser_get_display_handler;\n\tnb->client.get_download_handler = NULL;\n\tnb->client.get_drag_handler = NULL;\n\tnb->client.get_find_handler = NULL;\n\tnb->client.get_focus_handler = NULL;\n\tnb->client.get_jsdialog_handler = NULL;\n\tnb->client.get_keyboard_handler = NULL;\n\tnb->client.get_life_span_handler = browser_get_life_span_handler;\n\tnb->client.get_load_handler = NULL;\n\tnb->client.get_render_handler = browser_get_render_handler;\n\tnb->client.get_request_handler = browser_get_request_handler;\n\tnb->client.on_process_message_received = browser_on_process_message_received;\n\n//\tnb->render_handler.get_accessibility_handler = NULL;\n\tnb->render_handler.get_root_screen_rect = NULL;\n\tnb->render_handler.get_view_rect = browser_get_view_rect;\n\tnb->render_handler.get_screen_point = NULL;\n\tnb->render_handler.get_screen_info = NULL;\n\tnb->render_handler.on_popup_show = NULL;\n\tnb->render_handler.on_popup_size = NULL;\n\tnb->render_handler.on_paint = browser_on_paint;\n//\tnb->render_handler.on_cursor_change = NULL;\n\tnb->render_handler.start_dragging = NULL;\n\tnb->render_handler.update_drag_cursor = NULL;\n\tnb->render_handler.on_scroll_offset_changed = NULL;\n//\tnb->render_handler.on_ime_composition_range_changed = NULL;\n\n\tnb->display_handler.on_address_change = browser_on_address_change;\n\tnb->display_handler.on_title_change = browser_on_title_change;\n\tnb->display_handler.on_favicon_urlchange = browser_on_favicon_urlchange;\n\tnb->display_handler.on_fullscreen_mode_change = browser_on_fullscreenmode_change;\n\tnb->display_handler.on_tooltip = browser_on_tooltip;\n\tnb->display_handler.on_status_message = browser_on_status_message;\n\tnb->display_handler.on_console_message = browser_on_console_message;\n\n\tnb->request_handler.on_before_browse = browser_on_before_browse;\n\tnb->request_handler.on_open_urlfrom_tab = NULL;\n//\tnb->request_handler.on_before_resource_load = NULL;\n//\tnb->request_handler.get_resource_handler = NULL;\n//\tnb->request_handler.on_resource_redirect = NULL;\n//\tnb->request_handler.on_resource_response = NULL;\n//\tnb->request_handler.get_resource_response_filter = NULL;\n//\tnb->request_handler.on_resource_load_complete = NULL;\n\tnb->request_handler.get_auth_credentials = NULL;\n\tnb->request_handler.on_quota_request = NULL;\n//\tnb->request_handler.on_protocol_execution = NULL; //FIXME: should implement.\n\tnb->request_handler.on_certificate_error = NULL;\n//\tnb->request_handler.on_select_client_certificate = NULL; //we have no such certs\n\tnb->request_handler.on_plugin_crashed = NULL;\n\tnb->request_handler.on_render_view_ready = NULL;\n\tnb->request_handler.on_render_process_terminated = browser_on_render_process_terminated;\n\n\tnb->life_span_handler.on_before_popup = NULL;\n\tnb->life_span_handler.on_after_created = NULL;\n\tnb->life_span_handler.do_close = NULL;\n\tnb->life_span_handler.on_before_close = browser_on_before_close;\n\n\tnb->context_menu_handler.on_before_context_menu = browser_on_before_context_menu;\n\tnb->context_menu_handler.run_context_menu = NULL;\t\t\t//fixme: implement a working context menu somehow\n\tnb->context_menu_handler.on_context_menu_command = NULL;\t//for custom context things, like opening in a new window...\n\tnb->context_menu_handler.on_context_menu_dismissed = NULL;\t//\n\n\tnb->desiredwidth = 640;\n\tnb->desiredheight = 480;\n\n\tif (newconsole)\n\t\tnb->consolename = strdup(newconsole);\n\telse\n\t\tnb->consolename = NULL;\n\n\t//make it white until there's actually something to draw\n\tnb->videowidth = 1;\n\tnb->videoheight = 1;\n\tnb->videodata = malloc(nb->videowidth * nb->videoheight * 4);\n\t*(int*)nb->videodata = 0x00ffffff;\n\tmemset(nb->videodata, 0xff, nb->videowidth * nb->videoheight * 4);\n\tnb->updated = true;\n\n\tnumbrowsers++;\n\treturn nb;\n}\n\n//request contexts are per-session things. eg, incognito tabs would have their own private instance\nstatic cef_request_context_t *request_context;\nstatic cef_request_context_handler_t request_context_handler;\n//request_context_handler methods\nstatic int CEF_CALLBACK request_context_handler_on_before_plugin_load(cef_request_context_handler_t* self, const cef_string_t* mime_type, const cef_string_t* plugin_url, int is_main_frame, const cef_string_t* top_origin_url, cef_web_plugin_info_t* plugin_info, cef_plugin_policy_t* plugin_policy)\n{\n//\tCon_DPrintf(\"%s (%s), \\\"%s\\\" \\\"%s\\\" \\\"%s\\\" \\\"%s\\\"\\n\", policy_url.ToString().c_str(), url.ToString().c_str(), \n//\t\tinfo->GetName().ToString().c_str(), info->GetPath().ToString().c_str(), info->GetVersion().ToString().c_str(), info->GetDescription().ToString().c_str());\n\n\t*plugin_policy = PLUGIN_POLICY_BLOCK;\t//block by default (user can manually override supposedly). most plugins are unlikely to cope well with our offscreen rendering stuff, and flash sucks.\n\n\tcef_release(plugin_info);\n\n\tif (!cef_allowplugins->value)\n\t{\n\t\t*plugin_policy = PLUGIN_POLICY_DISABLE;\n//\t\tCon_Printf(\"Blocking plugin: %s (%s)\\n\", info->GetName().ToString().c_str(), url.ToString().c_str());\n\t}\n\telse\n\t\treturn false;\t//false to use the 'recommended' policy\n\treturn true;\n}\n\n//there's only one of these, so I'm not going to bother making separate objects for all of the interfaces, nor ref counting\nstatic cef_app_t app;\nstatic cef_browser_process_handler_t browser_process_handler;\nstatic cef_render_process_handler_t render_process_handler;\nstatic cef_v8handler_t v8handler_query;\t//window.fte_query\nstatic cef_scheme_handler_factory_t scheme_handler_factory;\n\nstatic cef_browser_process_handler_t* CEF_CALLBACK app_get_browser_process_handler(cef_app_t* self)\n{\n\t//cef_addref(&browser_process_handler);\n\treturn &browser_process_handler;\n}\nstatic cef_render_process_handler_t* CEF_CALLBACK app_get_render_process_handler(cef_app_t* self)\n{\n\t//cef_addref(&render_process_handler);\n\treturn &render_process_handler;\n}\nstatic void CEF_CALLBACK app_on_register_custom_schemes(struct _cef_app_t* self, cef_scheme_registrar_t* registrar)\n{\n\tcef_string_t fte = makecefstring(\"fte\");\n\tregistrar->add_custom_scheme(registrar, &fte, CEF_SCHEME_OPTION_NONE\n\t\t|CEF_SCHEME_OPTION_STANDARD\n\t\t/*|CEF_SCHEME_OPTION_LOCAL*/\n\t\t|CEF_SCHEME_OPTION_DISPLAY_ISOLATED\n\t\t|CEF_SCHEME_OPTION_SECURE\n\t\t|CEF_SCHEME_OPTION_CORS_ENABLED\n\t\t/*|CEF_SCHEME_OPTION_CSP_BYPASSING*/\n\t\t/*|CEF_SCHEME_OPTION_FETCH_ENABLED*/\n\t\t);\n\tcef_string_clear(&fte);\n}\n\nstatic void CEF_CALLBACK browser_process_handler_on_context_initialized(cef_browser_process_handler_t* self)\n{\n\tcef_string_t fte = makecefstring(\"fte\");\n\tcef_register_scheme_handler_factory(&fte, NULL, &scheme_handler_factory);\n\tcef_string_clear(&fte);\n}\nstatic void CEF_CALLBACK browser_process_handler_on_before_child_process_launch(cef_browser_process_handler_t* self, cef_command_line_t* command_line)\n{\n\tchar *arg = \"--plugwrapper\";\n\tchar *funcname = \"CefSubprocessInit\";\n\tcef_string_t cefisannoying = {NULL};\n\n\tcef_string_from_utf8(arg, strlen(arg), &cefisannoying);\n\tcommand_line->append_argument(command_line, &cefisannoying);\n\tcef_string_from_utf8(plugname, strlen(plugname), &cefisannoying);\n\tcommand_line->append_argument(command_line, &cefisannoying);\n\tcef_string_from_utf8(funcname, strlen(funcname), &cefisannoying);\n\tcommand_line->append_argument(command_line, &cefisannoying);\n\n//\tMessageBoxW(NULL, command_line->GetCommandLineString().c_str(), L\"CEF\", 0); \n\tcef_string_clear(&cefisannoying);\n\tcef_release(command_line);\n}\n\nstatic void CEF_CALLBACK render_process_handler_on_context_created(cef_render_process_handler_t* self, cef_browser_t* browser, cef_frame_t* frame, cef_v8context_t* context)\n{\n\tcef_v8value_t *jswindow = context->get_global(context);\n\n//eg:\twindow.fte_query(\"getstats\", function(req,res){console.log(\"health: \"+JSON.parse(res)[0/*STAT_HEALTH*/]);});\n\tcef_string_t key = makecefstring(\"fte_query\");\n\tjswindow->set_value_bykey(jswindow, &key, cef_v8value_create_function(&key, &v8handler_query), V8_PROPERTY_ATTRIBUTE_READONLY | V8_PROPERTY_ATTRIBUTE_DONTENUM | V8_PROPERTY_ATTRIBUTE_DONTDELETE);\n\tcef_string_clear(&key);\n\n\tcef_release(browser);\n\tcef_release(frame);\n\tcef_release(context);\n}\n\n//only use these in the 'renderer' thread / javascript thread.\ntypedef struct activequery_s\n{\n\tcef_v8value_t *callbackfunc;\t//this is the js function to call when the result is available.\n\tcef_v8context_t *context;\n\tcef_frame_t *frame;\n\tint64 queryid;\n\tstruct activequery_s *next;\n} activequery_t;\nstatic activequery_t *queries;\nstatic int64 next_queryid;\n\ntypedef struct\n{\n\tcef_task_t task;\n\tcef_string_userfree_t request;\n\tcef_string_userfree_t result;\n\tint64 queryid;\n\tatomic_uint32_t refcount;\n} queryresponse_t;\nstatic void CEF_CALLBACK queryresponse_addref(cef_base_ref_counted_t* self)\n{\n\tqueryresponse_t *qr = (queryresponse_t*)((char*)self - offsetof(queryresponse_t, task.base)); \n\tatomic_fetch_add(&qr->refcount, 1);\n}\nstatic int CEF_CALLBACK queryresponse_release(cef_base_ref_counted_t* self)\n{\n\tqueryresponse_t *qr = (queryresponse_t*)((char*)self - offsetof(queryresponse_t, task.base)); \n\tif (atomic_fetch_sub(&qr->refcount, 1) == 1)\n\t{\n\t\tif (qr->request)\n\t\t\tcef_string_userfree_free(qr->request);\n\t\tif (qr->result)\n\t\t\tcef_string_userfree_free(qr->result);\n\t\tfree(qr);\n\t\treturn true;\n\t}\n\treturn false;\n}\nstatic void CEF_CALLBACK queryresponse_execute(struct _cef_task_t* self)\n{\t//lethal injection.\n\tqueryresponse_t *qr = (queryresponse_t*)((char*)self - offsetof(queryresponse_t, task.base)); \n\tactivequery_t **link, *q;\n\tfor (link = &queries; (q=*link); link = &(*link)->next)\n\t{\n\t\tif (q->queryid == qr->queryid)\n\t\t{\n\t\t\tif (q->callbackfunc)\n\t\t\t{\n\t\t\t\tcef_v8value_t *args[2] = {cef_v8value_create_string(qr->request), cef_v8value_create_string(qr->result)};\n\t\t\t\tcef_v8value_t *r;\n\t\t\t\tcef_addref(q->context);\n\t\t\t\tr = q->callbackfunc->execute_function_with_context(q->callbackfunc, q->context, NULL, 2, args);\n\t\t\t\tcef_release(r);\n\t\t\t}\n\n\t\t\t//and clear up the request context too.\n\t\t\t*link = q->next;\n\t\t\tif (q->callbackfunc)\n\t\t\t\tcef_release(q->callbackfunc);\n\t\t\tcef_release(q->frame);\n\t\t\tcef_release(q->context);\n\t\t\tfree(q);\n\t\t\treturn;\n\t\t}\n\t}\n}\n\nstatic void CEF_CALLBACK render_process_handler_on_context_released(cef_render_process_handler_t* self, cef_browser_t* browser, cef_frame_t* frame, cef_v8context_t* context)\n{\n\tactivequery_t **link, *q;\n\tfor (link = &queries; (q=*link); )\n\t{\n\t\tif (q->context == context)// && q->frame == frame)\n\t\t{\n\t\t\t*link = q->next;\n\t\t\tif (q->callbackfunc)\n\t\t\t\tcef_release(q->callbackfunc);\n\t\t\tcef_release(q->frame);\n\t\t\tcef_release(q->context);\n\t\t\tfree(q);\n\t\t\tcontinue;\n\t\t}\n\t\tlink = &(*link)->next;\n\t}\n\tcef_release(browser);\n\tcef_release(frame);\n\tcef_release(context);\n}\n\n\n/*javascript methods for the guest code to call*/\nstatic cef_v8value_t *makev8string(char *str)\n{\n\tcef_v8value_t *r;\n\tcef_string_t cs = makecefstring(str);\n\tr = cef_v8value_create_string(&cs);\n\tcef_string_clear(&cs);\n\treturn r;\n}\nstatic int CEF_CALLBACK fsfunc_execute(cef_v8handler_t* self, const cef_string_t* name, cef_v8value_t* object, size_t argumentsCount, cef_v8value_t* const* arguments, cef_v8value_t** retval, cef_string_t* exception)\n{\n\tcef_process_message_t *msg;\n\tcef_list_value_t *args;\n\n\tcef_v8context_t *v8ctx = cef_v8context_get_current_context();\n\tcef_browser_t *browser = v8ctx->get_browser(v8ctx);\n\tcef_frame_t *frame = v8ctx->get_frame(v8ctx);\n//\tint64 frame_id = frame->get_identifier(frame);\n\t\n//\tcef_string_t key = {L\"omgwtfitkindaworks\"};\n//\tkey.length = wcslen(key.str);\n\n//\t*exception = makecefstring(\"SOME KIND OF EXCEPTION!\");\n\t*retval = makev8string(\"\");\n\n\tif (argumentsCount)\n\t{\n\t\tcef_string_userfree_t setting = arguments[0]->get_string_value(arguments[0]);\n\n\t\tactivequery_t *q = malloc(sizeof(*q));\n\t\tmemset(q, 0, sizeof(*q));\n\t\tq->context = v8ctx;\t//hold on to these\n\t\tq->frame = frame;\n\t\tq->queryid = ++next_queryid;\n\t\tq->next = queries;\n\t\tq->callbackfunc = arguments[1];\n\t\tqueries = q;\n\n\t\tcef_addref(q->callbackfunc);\n\n\t\t\n\t\tmsg = cef_process_message_create(name);\n\t\targs = msg->get_argument_list(msg);\n\n\t\targs->set_string(args, 0, setting);\n\t\targs->set_null(args, 1);\n\t\targs->set_int(args, 2, q->queryid & 0xffffffff);\n\t\targs->set_int(args, 3, (q->queryid>>32) & 0xffffffff);\n\t\tcef_release(args);\n\n\t\tframe->send_process_message(frame, PID_BROWSER, msg);\n\n\t\tif (setting)\n\t\t\tcef_string_userfree_free(setting);\n\t}\n\telse\n\t{\n\t\tcef_release(frame);\n\t\tcef_release(v8ctx);\n\t}\n\tcef_release(browser);\n\n\n\treturn 1;\n} \n\nstatic int CEF_CALLBACK render_process_handler_on_process_message_received(cef_render_process_handler_t* self,cef_browser_t* browser, cef_frame_t* frame, cef_process_id_t source_process, cef_process_message_t* message)\n{\n\tint handled = false;\n//\tbrowser_t *br = (browser_t*)((char*)self - offsetof(browser_t, request_handler));\n\tcef_string_userfree_t msgnameunusable = message->get_name(message);\n\tcef_string_utf8_t name = {NULL};\n\tcef_string_to_utf8(msgnameunusable->str, msgnameunusable->length, &name);\n\tif (!strcmp(name.str, \"fte_query\"))\n\t{\n\t\tcef_list_value_t *args = message->get_argument_list(message);\n\t\tqueryresponse_t *task = malloc(sizeof(*task));\n\t\tmemset(task, 0, sizeof(*task));\n\t\ttask->refcount = 1;\n\t\ttask->task.base.size = sizeof(task->task);\n\t\ttask->task.base.add_ref = queryresponse_addref;\n\t\ttask->task.base.release = queryresponse_release;\n\t\ttask->task.execute = queryresponse_execute;\n\t\ttask->request = args->get_string(args, 0);\n\t\ttask->result = args->get_string(args, 1);\n\t\ttask->queryid = args->get_int(args, 2) | ((int64)args->get_int(args, 3)<<32u);\n\t\tcef_release(args);\n\t\tcef_post_task(TID_RENDERER, &task->task);\n\n\t\thandled = true;\n\t}\n\tcef_string_utf8_clear(&name);\n\tcef_string_userfree_free(msgnameunusable);\n\n\tcef_release(browser);\n\tcef_release(message);\n\treturn handled;\n}\n\n/* fte://file/path scheme handler */\ntypedef struct\n{\n\tcef_resource_handler_t rh;\n\tatomic_uint32_t refcount;\n\tvfsfile_t *fh;\n\tchar *data;\n\tsize_t offset;\n\tsize_t datasize;\n\tunsigned int resultcode;\n\tchar *responseheaders;\n} fteresource_t;\nstatic void CEF_CALLBACK resource_handler_addref(cef_base_ref_counted_t* self)\n{\n\tfteresource_t *rh = (fteresource_t*)((char*)self - offsetof(fteresource_t, rh.base)); \n\tatomic_fetch_add(&rh->refcount, 1);\n}\nstatic int CEF_CALLBACK resource_handler_release(cef_base_ref_counted_t* self)\n{\n\tfteresource_t *rh = (fteresource_t*)((char*)self - offsetof(fteresource_t, rh.base)); \n\tif (atomic_fetch_sub(&rh->refcount, 1) == 1)\n\t{\n\t\tif (rh->fh)\n\t\t\tVFS_CLOSE(rh->fh);\n\t\tif (rh->data)\n\t\t\tfree(rh->data);\n\t\tfree(rh->responseheaders);\n\t\tfree(rh);\n\t\treturn true;\n\t}\n\treturn false;\n}\nstatic void res_catfield_l(char **const orig, const char *key, int kl, const char *val, int vl)\n{\n\tsize_t ol = *orig?strlen(*orig):0;\n\tchar *n;\n\tif (ol)\n\t{\n\t\tn = malloc(ol+1+kl+1+vl+1);\n\t\tmemcpy(n, *orig, ol);\n\t\tn[ol++] = '\\n';\n\t}\n\telse\n\t\tn = malloc(kl+1+vl+1);\n\tmemcpy(n+ol, key, kl);\n\tol+=kl;\n\tn[ol++] = '\\n';\n\tmemcpy(n+ol, val, vl);\n\tol+=vl;\n\tn[ol++] = 0;\n\tfree(*orig);\n\t*orig = n;\n}\nstatic void res_catfield(char **const orig, const char *key, const char *val)\n{\n\tres_catfield_l(orig, key, strlen(key), val, strlen(val));\n}\nstatic void res_catfield_csuf(char **const orig, const char *key, cef_string_userfree_t cs)\n{\n\tcef_string_utf8_t u8 = {NULL};\n\tcef_string_to_utf8(cs->str, cs->length, &u8);\n\tres_catfield_l(orig, key, strlen(key), u8.str, u8.length);\n\tcef_string_utf8_clear(&u8);\n}\nstatic void res_catfield_cs(char **const orig, cef_string_t *key, cef_string_t *val)\n{\n\tcef_string_utf8_t keyu8 = {NULL};\n\tcef_string_utf8_t valu8 = {NULL};\n\tcef_string_to_utf8(key->str, key->length, &keyu8);\n\tcef_string_to_utf8(val->str, val->length, &valu8);\n\tres_catfield_l(orig, keyu8.str, keyu8.length, valu8.str, valu8.length);\n\tcef_string_utf8_clear(&keyu8);\n\tcef_string_utf8_clear(&valu8);\n}\n\nstatic int CEF_CALLBACK resource_handler_process_request(cef_resource_handler_t* self, cef_request_t* request, cef_callback_t* callback)\n{\n\tfteresource_t *rh = (fteresource_t*)((char*)self - offsetof(fteresource_t, rh));\n\tcef_string_userfree_t url = request->get_url(request), method;\n\tcef_post_data_t *postdata;\n\tsize_t numelements;\n\tcef_post_data_element_t *elements[1];\n\tsize_t postsize;\n\tchar *postbytes;\n\tchar *q;\n\tchar *e;\n\tcef_string_utf8_t u8_url = {NULL}, u8={NULL};\n\tcef_string_t ext = {NULL};\n\tcef_string_to_utf8(url->str, url->length, &u8_url);\n\trh->resultcode = 404;\n\n\t//hack at the url to hide the\n\tq = strchr(u8_url.str, '?');\n\tif (q)\n\t\t*q = 0;\n\tfor(e = q?q:u8_url.str+strlen(u8_url.str); e > u8_url.str; )\n\t{\n\t\te--;\n\t\tif (*e == '/')\n\t\t\tbreak;\t\t//no extension\n\t\tif (*e == '.')\n\t\t{\n\t\t\te++;\n\t\t\tcef_string_from_utf8(e, strlen(e), &ext);\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tres_catfield(&rh->responseheaders, \"Access-Control-Allow-Origin\", \"fte://data\");\n\tres_catfield(&rh->responseheaders, \"Access-Control-Allow-Origin\", \"fte://csqc\");\n\tres_catfield(&rh->responseheaders, \"Access-Control-Allow-Origin\", \"fte://ssqc\");\n\tres_catfield(&rh->responseheaders, \"Access-Control-Allow-Origin\", \"fte://menu\");\n\n\t//sandboxed to the same dir that qc can fopen/fwrite.\n\t//(also blocks any fancy http:// parsing that an engine might do)\n\tif (!strncmp(u8_url.str, \"fte://data/\", 11))\n\t{\n\t\trh->fh = fsfuncs->OpenVFS(u8_url.str+6, \"rb\", FS_GAME);\n\t\tif (rh->fh)\n\t\t{\n\t\t\tcef_string_userfree_t mt = cef_get_mime_type(&ext);\n\t\t\tif (mt)\n\t\t\t{\n\t\t\t\tres_catfield_csuf(&rh->responseheaders, \"Content-Type\", mt);\n\t\t\t\tcef_string_userfree_free(mt);\n\t\t\t}\n\t\t\trh->resultcode = 200;\n\t\t}\n\t\telse\n\t\t{\n\t\t\trh->resultcode = 404;\n\t\t\tres_catfield(&rh->responseheaders, \"Content-Type\", \"text/html\");\n\t\t\trh->data = strdup(\"<html><style type=\\\"text/css\\\">body {background-color: lightblue;}</style><title>not found</title>File not found within game filesystem.</html>\");\n\t\t\trh->datasize = strlen(rh->data);\n\t\t}\n\t}\n\telse if (!strncmp(u8_url.str, \"fte://ssqc/\", 11) || !strncmp(u8_url.str, \"fte://csqc/\", 11) || !strncmp(u8_url.str, \"fte://menu/\", 11))\n\t{\n\t\tstruct pubprogfuncs_s *progs;\n\t\tconst char *page;\n\t\tconst char *respheaders = NULL;\n\t\tconst char *reqheaders = NULL;\n\t\tif (ext.str)\n\t\t{\n\t\t\tcef_string_userfree_t mt = cef_get_mime_type(&ext);\n\t\t\tif (mt)\n\t\t\t{\n\t\t\t\tres_catfield_csuf((char**)&respheaders, \"Content-Type\", mt);\n\t\t\t\tcef_string_userfree_free(mt);\n\t\t\t}\n\t\t}\n\n\t\tif(q)\n\t\t\t*q = '?';\t//put it back so the qc can get the full url.\n\n\t\trh->resultcode = 404;\n\n\t\tif (!strncmp(u8_url.str, \"fte://ssqc/\", 11))\n\t\t\tprogs = plugfuncs->GetEngineInterface(\"SSQCVM\", sizeof(*progs));\t//WARNING: goes direct rather than via the server, so basically single-player only.\n\t\telse if (!strncmp(u8_url.str, \"fte://csqc/\", 11))\n\t\t\tprogs = plugfuncs->GetEngineInterface(\"CSQCVM\", sizeof(*progs));\n\t\telse if (!strncmp(u8_url.str, \"fte://menu/\", 11))\n\t\t\tprogs = plugfuncs->GetEngineInterface(\"MenuQCVM\", sizeof(*progs));\n\t\telse\n\t\t\tprogs = NULL;\n\t\t\n\t\tif (progs)\n\t\t{\n\t\t\tfunc_t func = progs->FindFunction(progs, \"Cef_GeneratePage\", PR_ANY);\n\t\t\tif (func)\n\t\t\t{\n\t\t\t\tvoid *pr_globals = PR_globals(progs, PR_CURRENT);\n\t\t\t\t((string_t *)pr_globals)[OFS_PARM0] = progs->TempString(progs, u8_url.str+11);\n\n\t\t\t\tmethod = request->get_method(request);\n\t\t\t\tcef_string_to_utf8(method->str, method->length, &u8);\n\t\t\t\t((string_t *)pr_globals)[OFS_PARM1] = progs->TempString(progs, u8.str);\n\t\t\t\tcef_string_userfree_free(method);\n\t\t\t\tcef_string_utf8_clear(&u8);\n\n\t\t\t\tpostdata = request->get_post_data(request);\n\t\t\t\tif (postdata)\n\t\t\t\t{\n\t\t\t\t\tnumelements = countof(elements);\n\t\t\t\t\tmemset(elements, 0, sizeof(elements));\n\t\t\t\t\tpostdata->get_elements(postdata, &numelements, elements);\n\t\t\t\t\tpostsize = elements[0]->get_bytes_count(elements[0]);\n\t\t\t\t\tpostbytes = malloc(postsize+1);\n\t\t\t\t\telements[0]->get_bytes(elements[0], postsize, postbytes);\n\t\t\t\t\tpostbytes[postsize] = 0;\n\t\t\t\t\t((string_t *)pr_globals)[OFS_PARM2] = progs->TempString(progs, postbytes);\n\t\t\t\t\tfree(postbytes);\n\t\t\t\t\tcef_release(elements[0]);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\t((string_t *)pr_globals)[OFS_PARM2] = 0;\n\n\t\t\t\t{\n\t\t\t\t\tsize_t i, elems;\n\t\t\t\t\tcef_string_t key = {NULL}, val = {NULL};\n\t\t\t\t\tcef_string_multimap_t hmap = cef_string_multimap_alloc();\n\t\t\t\t\trequest->get_header_map(request, hmap);\n\t\t\t\t\telems = cef_string_multimap_size(hmap);\n\t\t\t\t\tfor (i = 0; i < elems; i++)\n\t\t\t\t\t{\n\t\t\t\t\t\tcef_string_multimap_key(hmap, i, &key);\n\t\t\t\t\t\tcef_string_multimap_key(hmap, i, &val);\n\t\t\t\t\t\tres_catfield_cs(&rh->responseheaders, &key, &val);\n\t\t\t\t\t\tcef_string_clear(&key);\n\t\t\t\t\t\tcef_string_clear(&val);\n\t\t\t\t\t}\n\t\t\t\t\tcef_string_multimap_free(hmap);\n\t\t\t\t}\n\n\t\t\t\t((string_t *)pr_globals)[OFS_PARM3] = reqheaders?progs->TempString(progs, reqheaders):0;\t//request heders\n\t\t\t\t((string_t *)pr_globals)[OFS_PARM4] = rh->responseheaders?progs->TempString(progs, rh->responseheaders):0;\t//response headers\n\t\t\t\t((string_t *)pr_globals)[OFS_PARM5] = 0;\n\t\t\t\t((string_t *)pr_globals)[OFS_PARM6] = 0;\n\t\t\t\t((string_t *)pr_globals)[OFS_PARM7] = 0;\n\t\t\t\tprogs->ExecuteProgram(progs, func);\n\n\t\t\t\tif (((string_t *)pr_globals)[OFS_RETURN])\n\t\t\t\t{\n\t\t\t\t\tpage = progs->StringToNative(progs, ((string_t *)pr_globals)[OFS_RETURN]);\n\t\t\t\t\trespheaders = progs->StringToNative(progs, ((string_t *)pr_globals)[OFS_PARM4]);\n\t\t\t\t\trh->resultcode = 200;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tpage = \"<html><style type=\\\"text/css\\\">body {background-color: lightblue;}</style><title>not found</title>Cef_GeneratePage returned null</html>\";\n\t\t\t}\n\t\t\telse\n\t\t\t\tpage = \"<html><style type=\\\"text/css\\\">body {background-color: lightblue;}</style><title>not found</title>Cef_GeneratePage not implemented by mod</html>\";\n\t\t}\n\t\telse\n\t\t\tpage = \"<html><style type=\\\"text/css\\\">body {background-color: lightblue;}</style><title>not found</title>That QCVM is not running</html>\";\n\n\t\tif (*respheaders == '\\n')\n\t\t\trespheaders++;\n\t\trh->responseheaders = strdup(respheaders);\n\t\t//FIXME: only return any data if we were successful OR the mime is text/html\n\t\trh->data = strdup(page);\n\t\trh->datasize = strlen(rh->data);\n\t}\n\telse\n\t{\n\t\trh->resultcode = 403;\n\t\tres_catfield(&rh->responseheaders, \"Content-Type\", \"text/html\");\n\t\trh->data = strdup(\"<html><style type=\\\"text/css\\\">body {background-color: lightblue;}</style><title>forbidden</title><a href=\\\"fte://data/index.html\\\">Try here</a> <a href=\\\"fte://csqc/index.html\\\">Or try here</a></html>\");\n\t\trh->datasize = strlen(rh->data);\n\t}\n\n\tcef_string_userfree_free(url);\n\tcef_string_utf8_clear(&u8_url);\n\n\tcallback->cont(callback);\t//headers are now known... should be delayed.\n\tcef_release(callback);\n\tcef_release(request);\n\treturn 1;\t//failure is reported as an http error code rather than an exception\n}\nstatic char *strseps(char *str, char *chars)\n{\t//find the next separator\n\tchar *best = str+strlen(str);\n\tchar *c;\n\tif (*str)\n\t\twhile(*chars)\n\t\t{\n\t\t\tc = strchr(str, *chars++);\n\t\t\tif (c && c < best)\n\t\t\t\tbest = c;\n\t\t}\n\treturn best;\n}\nstatic void CEF_CALLBACK resource_handler_get_response_headers(cef_resource_handler_t* self, cef_response_t* response, int64* response_length, cef_string_t* redirectUrl)\n{\n\tfteresource_t *rh = (fteresource_t*)((char*)self - offsetof(fteresource_t, rh));\n\tcef_string_multimap_t *hmap;\n\tcef_string_t key = {NULL}, val={NULL};\n\n\tif (rh->fh)\n\t\t*response_length = VFS_GETLEN(rh->fh);\n\telse if (rh->data)\n\t\t*response_length = rh->datasize;\n\telse\n\t\t*response_length = -1;\n\n\thmap = cef_string_multimap_alloc();\n\tif (rh->responseheaders)\n\t{\n\t\tchar *start;\n\t\tchar *sep;\n\t\tchar *nl;\n\t\tfor (start = rh->responseheaders; *start; )\n\t\t{\n\t\t\tsep = strseps(start, \":\\n\");\n\t\t\tnl = strseps(sep+1, \"\\n\");\n\n\t\t\tcef_string_from_utf8(start, sep-start, &key);\n\t\t\tif (*sep)\n\t\t\t\tsep++;\n\t\t\tcef_string_from_utf8(sep, nl-sep, &val);\n\n\t\t\tcef_string_multimap_append(hmap, &key, &val);\n\t\t\tif (*nl)\n\t\t\t\tstart = nl+1;\n\t\t\telse\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\tcef_string_multimap_append(hmap, makecefstringptr(\"Access-Control-Allow-Origin\", &key), makecefstringptr(\"fte://data\", &val));\n\tresponse->set_header_map(response, hmap);\n\n//\tresponse->set_mime_type(response, &rh->mimetype);\n\tresponse->set_status(response, rh->resultcode);\n\n\tcef_string_clear(&key);\n\tcef_string_clear(&val);\n\tcef_release(response);\n}\nstatic int CEF_CALLBACK resource_handler_read_response(cef_resource_handler_t* self, void* data_out, int bytes_to_read, int* bytes_read, cef_callback_t* callback)\n{\n\tfteresource_t *rh = (fteresource_t*)((char*)self - offsetof(fteresource_t, rh));\n\n\tif (rh->fh)\n\t\t*bytes_read = VFS_READ(rh->fh, data_out, bytes_to_read);\n\telse if (rh->data)\n\t{\n\t\tif (bytes_to_read > rh->datasize - rh->offset)\n\t\t\tbytes_to_read = rh->datasize - rh->offset;\n\t\t*bytes_read = bytes_to_read;\n\t\tmemcpy(data_out, rh->data + rh->offset, bytes_to_read);\n\t\trh->offset += bytes_to_read;\n\t}\n\telse\n\t\t*bytes_read = 0;\n\n\t//callback->cont(callback);\t//headers are now known... should be delayed.\n\tcef_release(callback);\n\n\tif (*bytes_read <= 0)\n\t{\n\t\t*bytes_read = 0;\n\t\treturn 0;\n\t}\n\treturn true;\t//more to come\n}\nstatic void CEF_CALLBACK resource_handler_cancel(cef_resource_handler_t* self)\n{\n\tfteresource_t *rh = (fteresource_t*)((char*)self - offsetof(fteresource_t, rh));\n\n\tif (rh->fh)\n\t\tVFS_CLOSE(rh->fh);\n\trh->fh = NULL;\n\tif (rh->data)\n\t\tfree(rh->data);\n\trh->data = NULL;\n\trh->offset = 0;\n\trh->datasize = 0;\n}\n\nstatic cef_resource_handler_t* CEF_CALLBACK scheme_handler_factory_create(cef_scheme_handler_factory_t* self, cef_browser_t* browser, cef_frame_t* frame, const cef_string_t* scheme_name, cef_request_t* request)\n{\n\tfteresource_t *rh = malloc(sizeof(*rh));\n\tmemset(rh, 0, sizeof(*rh));\n\n\trh->rh.base.size = sizeof(*rh);\n\trh->rh.base.add_ref\t\t\t= resource_handler_addref;\n\trh->rh.base.release\t\t\t= resource_handler_release;\n\trh->rh.process_request\t\t= resource_handler_process_request;\n\trh->rh.get_response_headers\t= resource_handler_get_response_headers;\n\trh->rh.read_response\t\t= resource_handler_read_response;\n\trh->rh.cancel\t\t\t\t= resource_handler_cancel;\n\n\tcef_addref(&rh->rh);\n\n\tcef_release(browser);\n\tcef_release(frame);\n\tcef_release(request);\n\treturn &rh->rh;\n}\n\nstatic void app_initialize(void)\n{\n\tapp.base.size = sizeof(app);\n\tapp.get_browser_process_handler = app_get_browser_process_handler;\n\tapp.get_render_process_handler = app_get_render_process_handler;\n\tapp.on_register_custom_schemes = app_on_register_custom_schemes;\n\n\tbrowser_process_handler.base.size = sizeof(browser_process_handler);\n\tbrowser_process_handler.on_before_child_process_launch\t= browser_process_handler_on_before_child_process_launch;\n\tbrowser_process_handler.on_context_initialized\t\t\t= browser_process_handler_on_context_initialized;\n\n\trender_process_handler.base.size = sizeof(render_process_handler);\n\trender_process_handler.on_context_created\t\t\t\t= render_process_handler_on_context_created;\n\trender_process_handler.on_context_released\t\t\t\t= render_process_handler_on_context_released;\n\trender_process_handler.on_process_message_received\t\t= render_process_handler_on_process_message_received;\n\n\tv8handler_query.base.size = sizeof(v8handler_query);\n\tv8handler_query.execute\t\t\t\t\t\t\t\t\t= fsfunc_execute;\n\n\tscheme_handler_factory.base.size = sizeof(scheme_handler_factory);\n\tscheme_handler_factory.create\t\t\t\t\t\t\t= scheme_handler_factory_create;\n\n\trequest_context_handler.base.size = sizeof(request_context_handler);\n\trequest_context_handler.on_before_plugin_load\t\t\t= request_context_handler_on_before_plugin_load;\n}\nstatic int cefwasinitialised;\n\ncef_request_context_t *Cef_GetRequestContext(void)\n{\n\tchar utf8[MAX_OSPATH];\n\tcef_request_context_t *ret = NULL;\n\tqboolean incog;\n\t\n\tincog = cef_incognito->value;\n\n\tif (!incog)\n\t\tret = request_context;\n\n\tif (!ret)\n\t{\n\t\tcef_request_context_settings_t csettings = {sizeof(csettings)};\n\t\tcsettings.persist_user_preferences = !incog;\n\t\tif (!incog && fsfuncs->NativePath(\"cefcache\", FS_ROOT, utf8, sizeof(utf8)))\n\t\t\tcef_string_from_utf8(utf8, strlen(utf8), &csettings.cache_path);\t//should be empty for incognito.\n\t\tret = cef_request_context_create_context(&csettings, &request_context_handler);\n\t\tcef_string_clear(&csettings.cache_path);\n\t}\n\telse\n\t\tcef_addref(ret);\n\n\tif (!incog && !request_context)\n\t{\n\t\trequest_context = ret;\n\t\tcef_addref(request_context);\n\t}\n\treturn ret;\n}\n\nstatic qboolean Cef_Init(qboolean engineprocess);\n\nstruct mediacallbacks_s;\t//todo...\nstatic void *Cef_Create(const char *name, struct mediacallbacks_s *callbacks)\n{\n\tcef_window_info_t window_info = {0};\n\tcef_browser_settings_t browserSettings = {sizeof(browserSettings)};\n\tbrowser_t *newbrowser;\n\tcef_string_t url = {NULL};\n\n\tif (!strcmp(name, \"cef\"))\n\t\tname += 3;\n\telse if (!strncmp(name, \"cef:\", 4))\n\t\tname += 4;\n\telse if (!strcmp(name, \"http\"))\n\t\tname += 4;\n\telse if (!strncmp(name, \"http:\", 5))\n\t\t;\n\telse if (!strncmp(name, \"https:\", 6))\n\t\t;\n\telse if (!strncmp(name, \"ftp:\", 4))\n\t\t;\n\telse\n\t\treturn NULL;\n\n\n\tif (!cefwasinitialised)\n\t{\n\t\tchar utf8[MAX_OSPATH];\n\t\tcef_main_args_t mainargs = {0};\n\t\tcef_settings_t settings = {sizeof(settings)};\n\n\t\tif (!Cef_Init(true))\n\t\t\treturn NULL;\n\n\t\t//const char *ua = \"FTEBrowser\";\n\t\t//cef_string_from_utf8(ua, strlen(ua), &settings.user_agent);\n\n\t\tif (fsfuncs->NativePath(\"cefcache\", FS_ROOT, utf8, sizeof(utf8)))\n\t\t\tcef_string_from_utf8(utf8, strlen(utf8), &settings.cache_path);\n\t\tif (fsfuncs->NativePath(\"cef_debug.log\", FS_ROOT, utf8, sizeof(utf8)))\n\t\t\tcef_string_from_utf8(utf8, strlen(utf8), &settings.log_file);\n\n\t\tif (fsfuncs->NativePath(\"\", FS_BINARYPATH, utf8, sizeof(utf8)))\n\t\t\tcef_string_from_utf8(utf8, strlen(utf8), &settings.resources_dir_path);\n\n\t\tif (fsfuncs->NativePath(\"locales\", FS_BINARYPATH, utf8, sizeof(utf8)))\n\t\t{\n\t\t\tstruct stat statbuf;\n\t\t\tif (stat(utf8, &statbuf) < 0)\n\t\t\t{\n\t\t\t\tCon_Printf(\"%s not found\\n\", utf8);\n\t\t\t\treturn NULL;\n\t\t\t}\n\t\t\tcef_string_from_utf8(utf8, strlen(utf8), &settings.locales_dir_path);\n\t\t}\n\n#ifdef _WIN32\n\t\t{\n\t\t\twchar_t omgwtfamonkey[MAX_OSPATH];\n\t\t\tif (GetModuleFileNameW(NULL, omgwtfamonkey, countof(omgwtfamonkey)))\n\t\t\t\tcef_string_from_utf16(omgwtfamonkey, wcslen(omgwtfamonkey), &settings.browser_subprocess_path);\n\t\t\tmainargs.instance = GetModuleHandle(NULL);\n\t\t}\n#endif\n\n#ifdef _DEBUG\n\t\tsettings.log_severity = LOGSEVERITY_VERBOSE;\n#else\n\t\tsettings.log_severity = LOGSEVERITY_DISABLE;\n#endif\n\t\tsettings.background_color = 0x00ffffff;\n//\t\tsettings.single_process = true;\n#ifdef _WIN32\n//\t\tsettings.multi_threaded_message_loop = true;\t//fixme: use this.\n#endif\n\t\tsettings.windowless_rendering_enabled = true;\n//\t\tsettings.command_line_args_disabled = true;\n//\t\tsettings.persist_session_cookies = false;\n\n/*\t\t{\n\t\t\tchar *s;\n\t\t\tstrcpy(utf8, FULLENGINENAME \"/\" STRINGIFY(FTE_VER_MAJOR) \".\" STRINGIFY(FTE_VER_MINOR));\n\t\t\twhile((s = strchr(utf8, ' ')))\n\t\t\t\t*s = '_';\n\t\t\tcef_string_from_utf8(utf8, strlen(utf8), &settings.product_version);\n\t\t}\n*/\n\t\tcefwasinitialised = !!cef_initialize(&mainargs, &settings, &app, NULL);\n\t\tcef_string_clear(&settings.browser_subprocess_path);\n//\t\tcef_string_clear(&settings.product_version);\n\t\tcef_string_clear(&settings.cache_path);\n\t\tcef_string_clear(&settings.log_file);\n\t}\n\n\tif (!cefwasinitialised)\n\t\treturn NULL;\n\n\t//tbh, web browser's are so horribly insecure that it seems pointless to even try disabling stuff that might be useful\n\tbrowserSettings.windowless_frame_rate = 60;\n\tbrowserSettings.javascript_close_windows = STATE_DISABLED;\n\tbrowserSettings.javascript_access_clipboard = STATE_DISABLED;\n//\tbrowserSettings.universal_access_from_file_urls = STATE_DISABLED;\n//\tbrowserSettings.file_access_from_file_urls = STATE_DISABLED;\n\tbrowserSettings.remote_fonts = STATE_DISABLED;\n\tbrowserSettings.plugins = STATE_DISABLED;\n\tbrowserSettings.background_color = 0x00ffffff;\n\n\twindow_info.windowless_rendering_enabled = true;\n\tmemset(&window_info.parent_window, 0, sizeof(window_info.parent_window));\n\n\tnewbrowser = browser_create();\n\tif (!newbrowser)\n\t\treturn NULL;\n\n\tif (!*name || !strcmp(name, \"http:\") || !strcmp(name, \"https:\"))\n\t\tname = \"about:blank\";\n\tcef_string_from_utf8(name, strlen(name), &url);\n\n\tcef_addref(&newbrowser->client);\n\tnewbrowser->thebrowser = cef_browser_host_create_browser_sync(&window_info, &newbrowser->client, &url, &browserSettings, NULL, Cef_GetRequestContext());\n\tcef_string_to_utf8(url.str, url.length, &newbrowser->currenturl);\n\tcef_string_clear(&url);\n\tif (!newbrowser->thebrowser)\n\t{\n\t\tbrowser_release(newbrowser);\n\t\treturn NULL;\t//cef fucked up.\n\t}\n\n\tif (cef_devtools->value)\n\t{\n\t\tcef_browser_host_t *host = newbrowser->thebrowser->get_host(newbrowser->thebrowser);\n\t\tbrowser_t *devtools = browser_create();\n\t\t\n#ifdef _WIN32\n\t\twindow_info.style = WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_VISIBLE;\n\t\twindow_info.parent_window = NULL;\n\t\twindow_info.bounds.x = CW_USEDEFAULT;\n\t\twindow_info.bounds.y = CW_USEDEFAULT;\n\t\twindow_info.bounds.width = CW_USEDEFAULT;\n\t\twindow_info.bounds.height = CW_USEDEFAULT;\n\t\twindow_info.window_name = makecefstring(\"CEF Dev Tools\");\n#else\n\t\tmemset(&window_info.parent_window, 0, sizeof(window_info.parent_window));\n\t\twindow_info.x = 0;\n\t\twindow_info.y = 0;\n\t\twindow_info.width = 320;\n\t\twindow_info.height = 240;\n#endif\n\t\twindow_info.windowless_rendering_enabled = false;\n\n\t\tcef_addref(&devtools->client);\n\t\thost->show_dev_tools(host, &window_info, &devtools->client, &browserSettings, NULL);\n\t\tcef_release(host);\n\t\tbrowser_release(devtools);\t//cef should continue to hold a reference to it while its visible, but its otherwise out of engine now.\n\n#ifdef _WIN32\n\t\tcef_string_clear(&window_info.window_name);\n#endif\n\t}\n\n\treturn (void*)newbrowser;\n}\n\nstatic void *Cef_CreateOld(const char *name)\n{\n\treturn Cef_Create(name, NULL);\n}\n\nstatic qboolean VARGS Cef_DisplayFrame(void *ctx, qboolean nosound, qboolean forcevideo, double mediatime, void (QDECL *uploadtexture)(void *ectx, uploadfmt_t fmt, int width, int height, void *data, void *palette), void *ectx)\n{\n\tbrowser_t *browser = (browser_t*)ctx;\n\tif (browser->updated || forcevideo)\n\t{\n\t\tuploadtexture(ectx, TF_BGRA32, browser->videowidth, browser->videoheight, browser->videodata, NULL);\n\t\tbrowser->updated = false;\n\t}\n\treturn true;\n}\nstatic void Cef_Destroy(void *ctx)\n{\t//engine isn't allowed to talk about the browser any more. kill it.\n\tbrowser_t *br = (browser_t*)ctx;\n\tcef_browser_host_t *host = br->thebrowser->get_host(br->thebrowser);\n\thost->close_browser(host, true);\n\tcef_release(host);\n\tif (br->thebrowser)\n\t{\n\t\tcef_release(br->thebrowser);\n\t\tbr->thebrowser = NULL;\n\t}\n\n\t//now release our reference to it.\n\tbrowser_release(br);\t//hopefully this should be the last reference, but we might be waiting for something on the cef side. hopefully nothing blocking on an unload event...\n}\n\nstatic void VARGS Cef_CursorMove (void *ctx, float posx, float posy)\n{\n\tbrowser_t *br = (browser_t*)ctx;\n\tcef_browser_host_t *host = br->thebrowser->get_host(br->thebrowser);\n\tbr->mousepos.x = (int)(posx * br->desiredwidth);\n\tbr->mousepos.y = (int)(posy * br->desiredheight);\n\tbr->mousepos.modifiers = 0;\n\thost->send_mouse_move_event(host, &br->mousepos, false);\n\tcef_release(host);\n}\n\nstatic void VARGS Cef_Key (void *ctx, int code, int unicode, int event)\n{\n\tbrowser_t *browser = (browser_t*)ctx;\n\tcef_browser_host_t *host = browser->thebrowser->get_host(browser->thebrowser);\n\n\t//handle mouse buttons\n\tif (code >= K_MOUSE1 && code <= K_MOUSE3)\n\t{\n\t\tint buttons[] = {MBT_LEFT, MBT_RIGHT, MBT_MIDDLE};\n\t\tif (!event || browser->keystate[code])\n\t\t\thost->send_mouse_click_event(host, &browser->mousepos, buttons[code-K_MOUSE1], event?true:false, 1);\n\t\tif (event)\n\t\t\tbrowser->keystate[code] = 0;\n\t\telse\n\t\t\tbrowser->keystate[code] = 1;\n\t\tcef_release(host);\n\t\treturn;\n\t}\n\n\tif (code == K_TOUCH)\n\t{\t//FIXME\n\t\tcef_release(host);\n\t\treturn;\n\t}\n\tif (code == K_TOUCHSLIDE || code == K_TOUCHTAP || code == K_TOUCHLONG)\n\t{\n\t\tcef_release(host);\n\t\treturn;\t//has to do its own\n\t}\n\n\n\t//handle mouse wheels\n\tif (code == K_MWHEELUP || code == K_MWHEELDOWN)\n\t{\n\t\tif (!event)\n\t\t\thost->send_mouse_wheel_event(host, &browser->mousepos, 0, (code == K_MWHEELDOWN)?-32:32);\n\t\tcef_release(host);\n\t\treturn;\n\t}\n\n\t//handle keypress/release events\n\tif (code)\n\t{\n\t\tcef_key_event_t kev = {0};\n\t\tif (event && !browser->keystate[code])\n\t\t{\n\t\t\tcef_release(host);\n\t\t\treturn;\t//releasing a key that is already released is weird.\n\t\t}\n\n\t\tkev.type = event?KEYEVENT_KEYUP:KEYEVENT_RAWKEYDOWN;\n\t\tkev.modifiers = 0;\n\t\tswitch(code)\n\t\t{\n\t\tcase 0:\t\t\t\tkev.windows_key_code = 0;\t\t\t\t\tbreak;\n\t\tcase K_UPARROW:\t\tkev.windows_key_code = 0x26/*VK_UP*/;\t\tbreak;\n\t\tcase K_DOWNARROW:\tkev.windows_key_code = 0x28/*VK_DOWN*/;\t\tbreak;\n\t\tcase K_LEFTARROW:\tkev.windows_key_code = 0x25/*VK_LEFT*/;\t\tbreak;\n\t\tcase K_RIGHTARROW:\tkev.windows_key_code = 0x27/*VK_RIGHT*/;\tbreak;\n\t\tcase K_ESCAPE:\t\tkev.windows_key_code = 0x1b/*VK_ESCAPE*/;\tbreak;\n\t\tcase K_SPACE:\t\tkev.windows_key_code = 0x20/*VK_SPACE*/;\tbreak;\n\t\tcase K_RSHIFT:\t\tkev.windows_key_code = 0x10/*VK_SHIFT*/;\tbreak;\n\t\tcase K_LSHIFT:\t\tkev.windows_key_code = 0x10/*VK_SHIFT*/;\tbreak;\n\t\tcase K_RCTRL:\t\tkev.windows_key_code = 0x11/*VK_CONTROL*/;\tbreak;\n\t\tcase K_LCTRL:\t\tkev.windows_key_code = 0x11/*VK_CONTROL*/;\tbreak;\n\t\tcase K_RALT:\t\tkev.windows_key_code = 0x12/*VK_MENU*/;\t\tbreak;\n\t\tcase K_LALT:\t\tkev.windows_key_code = 0x12/*VK_MENU*/;\t\tbreak;\n\t\tcase K_TAB:\t\t\tkev.windows_key_code = 0x09/*VK_TAB*/;\t\tbreak;\n\t\tcase K_RWIN:\t\tkev.windows_key_code = 0x5c/*VK_RWIN*/;\t\tbreak;\n\t\tcase K_LWIN:\t\tkev.windows_key_code = 0x5b/*VK_LWIN*/;\t\tbreak;\n\t\tcase K_APP:\t\t\tkev.windows_key_code = 0x5d/*VK_APPS*/;\t\tbreak;\n\t\tcase K_F1:\t\t\tkev.windows_key_code = 0x70/*VK_F1*/;\t\tbreak;\n\t\tcase K_F2:\t\t\tkev.windows_key_code = 0x71/*VK_F2*/;\t\tbreak;\n\t\tcase K_F3:\t\t\tkev.windows_key_code = 0x72/*VK_F3*/;\t\tbreak;\n\t\tcase K_F4:\t\t\tkev.windows_key_code = 0x73/*VK_F4*/;\t\tbreak;\n\t\tcase K_F5:\t\t\tkev.windows_key_code = 0x74/*VK_F5*/;\t\tbreak;\n\t\tcase K_F6:\t\t\tkev.windows_key_code = 0x75/*VK_F6*/;\t\tbreak;\n\t\tcase K_F7:\t\t\tkev.windows_key_code = 0x76/*VK_F7*/;\t\tbreak;\n\t\tcase K_F8:\t\t\tkev.windows_key_code = 0x77/*VK_F8*/;\t\tbreak;\n\t\tcase K_F9:\t\t\tkev.windows_key_code = 0x78/*VK_F9*/;\t\tbreak;\n\t\tcase K_F10:\t\t\tkev.windows_key_code = 0x79/*VK_F10*/;\t\tbreak;\n\t\tcase K_F11:\t\t\tkev.windows_key_code = 0x81/*VK_F11*/;\t\tbreak;\n\t\tcase K_F12:\t\t\tkev.windows_key_code = 0x82/*VK_F12*/;\t\tbreak;\n\t\tcase K_BACKSPACE:\tkev.windows_key_code = 0x08/*VK_BACK*/;\t\tbreak;\n\t\tcase K_DEL:\t\t\tkev.windows_key_code = 0x2e/*VK_DELETE*/;\tbreak;\n\t\tcase K_HOME:\t\tkev.windows_key_code = 0x24/*VK_HOME*/;\t\tbreak;\n\t\tcase K_END:\t\t\tkev.windows_key_code = 0x23/*VK_END*/;\t\tbreak;\n\t\tcase K_INS:\t\t\tkev.windows_key_code = 0x2d/*VK_INSERT*/;\tbreak;\n\t\tcase K_PGUP:\t\tkev.windows_key_code = 0x21/*VK_PRIOR*/;\tbreak;\n\t\tcase K_PGDN:\t\tkev.windows_key_code = 0x22/*VK_NEXT*/;\t\tbreak;\n\n\t\tdefault:\n\t\t\tif ((code >= 0x30 && code <= 0x39) || (code >= 0x41 && code <= 0x5a))\n\t\t\t\tkev.windows_key_code = code;\n\t\t\telse if (code >= 'a' && code <= 'z')\n\t\t\t\tkev.windows_key_code = (code-'a') + 'A';\n\t\t\telse\n\t\t\t\tkev.windows_key_code = 0;\n\t\t\tbreak;\n\t\t}\n\t\tkev.native_key_code = unicode<<16;\n\n\t\tif (browser->keystate[code])\n\t\t\tkev.native_key_code |= 1<<30;\n\t\tif (event)\n\t\t\tkev.native_key_code |= 1u<<31;\n\n\t\tif (event)\n\t\t\tbrowser->keystate[code] = 0;\n\t\telse\n\t\t\tbrowser->keystate[code] = 1;\n\n\t\tkev.is_system_key = 0;\n\t\tkev.character = unicode;\n\t\tkev.unmodified_character = unicode;\n\t\tkev.focus_on_editable_field = true;\n\t\thost->send_key_event(host, &kev);\n\t}\n\n\t//handle text input events (down events only)\n\tif (unicode && !event)\n\t{\n\t\tcef_key_event_t kev;\n\n\t\tkev.type = KEYEVENT_CHAR;\n\t\tkev.modifiers = 0;\n\t\n\t\tkev.windows_key_code = unicode;\n\t\tkev.native_key_code = unicode<<16;\n\n\t\tif (browser->keystate[code])\n\t\t\tkev.native_key_code |= 1<<30;\n\t\tif (event)\n\t\t\tkev.native_key_code |= 1u<<31;\n\n\t\tkev.is_system_key = 0;\n\t\tkev.character = unicode;\n\t\tkev.unmodified_character = unicode;\n\t\tkev.focus_on_editable_field = true;\n\t\thost->send_key_event(host, &kev);\n\t}\n\tcef_release(host);\n}\nstatic qboolean VARGS Cef_SetSize (void *ctx, int width, int height)\n{\n\tbrowser_t *browser = (browser_t*)ctx;\n\tcef_browser_host_t *host = browser->thebrowser->get_host(browser->thebrowser);\n\tif (browser->desiredwidth != width || browser->desiredheight != height)\n\t{\n\t\tbrowser->desiredwidth = width;\n\t\tbrowser->desiredheight = height;\n\t\thost->was_resized(host);\n\t}\n\tcef_release(host);\n\treturn qtrue;\n}\nstatic void VARGS Cef_GetSize (void *ctx, int *width, int *height)\n{\n\t//this specifies the logical size/aspect of the browser object\n\tbrowser_t *browser = (browser_t*)ctx;\n\t*width = browser->desiredwidth;\n\t*height = browser->desiredheight;\n}\nstatic void VARGS Cef_ChangeStream (void *ctx, const char *streamname)\n{\n\tbrowser_t *browser = (browser_t*)ctx;\n\tcef_browser_host_t *host = browser->thebrowser->get_host(browser->thebrowser);\n\tcef_frame_t *frame = NULL;\n\tif (!strncmp(streamname, \"cmd:\", 4))\n\t{\n\t\tconst char *cmd = streamname+4;\n\t\tif (!strcmp(cmd, \"focus\"))\n\t\t\thost->set_focus(host, true);\n\t\telse if (!strcmp(cmd, \"unfocus\"))\n\t\t\thost->set_focus(host, false);\n\t\telse if (!strcmp(cmd, \"refresh\"))\n\t\t\tbrowser->thebrowser->reload(browser->thebrowser);\n\t\telse if (!strcmp(cmd, \"transparent\"))\n;\n\t\telse if (!strcmp(cmd, \"opaque\"))\n;\n\t\telse if (!strcmp(cmd, \"stop\"))\n\t\t\tbrowser->thebrowser->stop_load(browser->thebrowser);\n\t\telse if (!strcmp(cmd, \"back\"))\n\t\t\tbrowser->thebrowser->go_back(browser->thebrowser);\n\t\telse if (!strcmp(cmd, \"forward\"))\n\t\t\tbrowser->thebrowser->go_forward(browser->thebrowser);\n\t\telse if (!strcmp(cmd, \"home\"))\n\t\t\tCef_ChangeStream(ctx, \"http://fte.triptohell.info\");\n\t\telse\n\t\t{\n\t\t\tframe = browser->thebrowser->get_focused_frame(browser->thebrowser);\n\t\t\tif (!strcmp(cmd, \"undo\"))\n\t\t\t\tframe->undo(frame);\n\t\t\telse if (!strcmp(cmd, \"redo\"))\n\t\t\t\tframe->redo(frame);\n\t\t\telse if (!strcmp(cmd, \"cut\"))\n\t\t\t\tframe->cut(frame);\n\t\t\telse if (!strcmp(cmd, \"copy\"))\n\t\t\t\tframe->copy(frame);\n\t\t\telse if (!strcmp(cmd, \"paste\"))\n\t\t\t\t;//frame->paste(frame);\t//possible security hole, as this uses the system clipboard\n\t\t\telse if (!strcmp(cmd, \"del\"))\n\t\t\t\tframe->del(frame);\n\t\t\telse if (!strcmp(cmd, \"selectall\"))\n\t\t\t\tframe->select_all(frame);\n\t\t\telse\n\t\t\t\tCon_Printf(\"unrecognised cmd: %s\\n\", cmd);\n\t\t}\n\t}\n\telse if (!strncmp(streamname, \"javascript:\", 11))\n\t{\n\t\tcef_string_t thescript = {NULL};\n\t\tcef_string_t url = {NULL};\n\t\tcef_string_from_utf8(streamname+11, strlen(streamname+11), &thescript);\n\t\tcef_string_from_utf8(\"http://localhost/\", strlen(\"http://localhost/\"), &url);\n\t\tframe = browser->thebrowser->get_main_frame(browser->thebrowser);\n\t\tframe->execute_java_script(frame, &thescript, &url, 1);\n\t\tcef_string_clear(&thescript);\n\t\tcef_string_clear(&url);\n\t}\n\t/*else if (!strncmp(streamname, \"raw:\", 4))\n\t{\n\t\tcef_string_t thehtml = {NULL};\n\t\tcef_string_t url = {NULL};\n\t\tcef_string_from_utf8(streamname+4, strlen(streamname+4), &thehtml);\n\t\tcef_string_from_utf8(\"http://localhost/\", strlen(\"http://localhost/\"), &url);\n\t\tframe = browser->thebrowser->get_main_frame(browser->thebrowser);\n\t\tframe->load_string(frame, &thehtml, &url);\n\t\tcef_string_clear(&thehtml);\n\t\tcef_string_clear(&url);\n\t}*/\n\telse if (*streamname && strcmp(streamname, \"http:\") && strcmp(streamname, \"https:\"))\n\t{\n\t\tcef_string_t url = {NULL};\n\t\tcef_string_from_utf8(streamname, strlen(streamname), &url);\n\t\tframe = browser->thebrowser->get_main_frame(browser->thebrowser);\n\t\tframe->load_url(frame, &url);\n\t\tcef_string_clear(&url);\n\t}\n\tif (frame)\n\t\tcef_release(frame);\n\tcef_release(host);\n}\n\nqboolean VARGS Cef_GetProperty (void *ctx, const char *field, char *out, size_t *outsize)\n{\n\tbrowser_t *browser = (browser_t*)ctx;\n\tconst char *ret = NULL;\n\tif (!strcmp(field, \"url\"))\n\t\tret = browser->currenturl.str;\n\telse if (!strcmp(field, \"title\"))\n\t\tret = browser->currenttitle.str;\n\telse if (!strcmp(field, \"status\"))\n\t\tret = browser->currentstatus.str;\n\telse if (!strcmp(field, \"icon\"))\n\t\tret = browser->currenticon.str;\n\n\tif (ret)\n\t{\n\t\tsize_t retsize = strlen(ret);\n\t\tif (out)\n\t\t{\n\t\t\tif (*outsize < retsize)\n\t\t\t\treturn false;\t//caller fucked up\n\t\t\tmemcpy(out, ret, retsize);\n\t\t}\n\t\t*outsize = retsize;\n\t\treturn true;\n\t}\n\treturn false;\n}\n\nstatic media_decoder_funcs_t decoderfuncs;\n\nstatic qintptr_t Cef_Tick(qintptr_t *args)\n{\n\tif (cefwasinitialised)\n\t{\n\t\tcef_do_message_loop_work();\n\n\t\t/* libcef can't cope with this.\n\t\tif (!numbrowsers)\n\t\t{\n\t\t\tif (request_context)\n\t\t\t{\n\t\t\t\tcef_release(request_context);\n\t\t\t\trequest_context = NULL;\n\t\t\t}\n\t\t\tcef_shutdown();\n\t\t\tcefwasinitialised = false;\n\t\t}\n\t\t*/\n\t}\n\treturn 0;\n}\nstatic qintptr_t Cef_Shutdown(qintptr_t *args)\n{\n\tif (cefwasinitialised)\n\t{\n\t\tint tries = 1000*10;//60*5;\t//keep trying for a duration (in ms)... give up after then as it just isn't working.\n\t\twhile(numbrowsers && tries > 0)\n\t\t{\n\t\t\tcef_do_message_loop_work();\n\n\t\t\ttries -= 10;\n#ifdef _WIN32\n\t\t\tSleep(10);\n#else\n\t\t\tusleep(10*1000);\n#endif\n\t\t}\n#ifdef _WIN32\n\t\tif (numbrowsers)\n\t\t{\t//this really should NOT be happening.\n\t\t\tMessageBox(NULL, \"Browsers are still open\", \"CEF Fuckup\", 0);\n\t\t}\n#endif\n\t\tif (request_context)\n\t\t{\n\t\t\tcef_release(request_context);\n\t\t\trequest_context = NULL;\n\t\t}\n\t\tcef_shutdown();\n\t\tcefwasinitialised = false;\n\t\tnumbrowsers = 0;\n\t}\n\n#if defined(_DEBUG) && defined(_MSC_VER)\n//\t_CrtDumpMemoryLeaks();\n#endif\n\n\treturn 0;\n}\n\n#ifndef _WIN32\nstatic int argc=0;\nstatic char *argv[64];\nstatic char commandline[8192];\nstatic void SetupArgv(cef_main_args_t *a)\n{\n\tFILE *f;\n\tif (!argc)\n\t{\n\t\tf = fopen(\"/proc/self/cmdline\", \"r\");\n\t\tif (f)\n\t\t{\n\t\t\tchar *s = commandline;\n\t\t\tchar *e = commandline+fread(commandline, 1, sizeof(commandline), f);\n\t\t\tfclose(f);\n\t\t\twhile(s < e)\n\t\t\t{\n\t\t\t\targv[argc++] = s;\n\t\t\t\twhile(*s)\n\t\t\t\t\ts++;\n\t\t\t\ts++;\n\t\t\t}\n\t\t}\n\t}\n\ta->argc = argc;\n\ta->argv = argv;\n}\n#endif\n\n//if we're a subprocess and somehow failed to add the --plugwrapper arg to the engine, then make sure we're not starting endless processes.\nstatic qboolean Cef_Init(qboolean engineprocess)\n{\n\tstatic qboolean cefwasloaded = qfalse;\n\n#ifdef _WIN32\n\tcef_main_args_t args = {GetModuleHandle(NULL)};\n#else\n\tcef_main_args_t args;\n\tSetupArgv(&args);\n#endif\n\n\tif (cefwasloaded)\n\t\treturn qtrue;\n\n\t{\n\t\tint result;\n\n#ifdef LIBCEF_DYNAMIC\n\t\tdllfunction_t ceffuncs[] =\n\t\t{\n\t\t\t{(void **)&cef_version_info,\t\t\t\t\t\"cef_version_info\"},\n\t\t\t{(void **)&cef_initialize,\t\t\t\t\t\t\"cef_initialize\"},\n\t\t\t{(void **)&cef_do_message_loop_work,\t\t\t\"cef_do_message_loop_work\"},\n\t\t\t{(void **)&cef_shutdown,\t\t\t\t\t\t\"cef_shutdown\"},\n\t\t\t{(void **)&cef_execute_process,\t\t\t\t\t\"cef_execute_process\"},\n\t\t\t{(void **)&cef_browser_host_create_browser_sync,\"cef_browser_host_create_browser_sync\"},\n\t\t\t{(void **)&cef_string_utf8_to_utf16,\t\t\t\"cef_string_utf8_to_utf16\"},\n\t\t\t{(void **)&cef_string_utf16_to_utf8,\t\t\t\"cef_string_utf16_to_utf8\"},\n\t\t\t{(void **)&cef_string_utf16_clear,\t\t\t\t\"cef_string_utf16_clear\"},\n\t\t\t{(void **)&cef_string_utf16_set,\t\t\t\t\"cef_string_utf16_set\"},\n\t\t\t{(void **)&cef_string_utf8_clear,\t\t\t\t\"cef_string_utf8_clear\"},\n\t\t\t{(void **)&cef_string_utf8_set,\t\t\t\t\t\"cef_string_utf8_set\"},\n\t\t\t{(void **)&cef_string_userfree_utf16_free,\t\t\"cef_string_userfree_utf16_free\"},\n\t\t\t{(void **)&cef_register_scheme_handler_factory,\t\"cef_register_scheme_handler_factory\"},\n\t\t\t{(void **)&cef_v8value_create_function,\t\t\t\"cef_v8value_create_function\"},\n\t\t\t{(void **)&cef_v8value_create_string,\t\t\t\"cef_v8value_create_string\"},\n\t\t\t{(void **)&cef_process_message_create,\t\t\t\"cef_process_message_create\"},\n\t\t\t{(void **)&cef_v8context_get_current_context,\t\"cef_v8context_get_current_context\"},\n\t\t\t{(void **)&cef_post_task,\t\t\t\t\t\t\"cef_post_task\"},\n\t\t\t{(void **)&cef_request_context_create_context,\t\"cef_request_context_create_context\"},\n\t\t\t{(void **)&cef_string_multimap_alloc,\t\t\t\"cef_string_multimap_alloc\"},\n\t\t\t{(void **)&cef_string_multimap_append,\t\t\t\"cef_string_multimap_append\"},\n\t\t\t{(void **)&cef_string_multimap_size,\t\t\t\"cef_string_multimap_size\"},\n\t\t\t{(void **)&cef_string_multimap_key,\t\t\t\t\"cef_string_multimap_key\"},\n\t\t\t{(void **)&cef_string_multimap_value,\t\t\t\"cef_string_multimap_value\"},\n\t\t\t{(void **)&cef_string_multimap_free,\t\t\t\"cef_string_multimap_free\"},\n\t\t\t{(void **)&cef_string_list_size,\t\t\t\t\"cef_string_list_size\"},\n\t\t\t{(void **)&cef_string_list_value,\t\t\t\t\"cef_string_list_value\"},\n\t\t\t{NULL}\n\t\t};\n\n#ifdef _WIN32\n\t\tif (plugfuncs && !plugfuncs->LoadDLL(\"libcef\", ceffuncs))\n#else\n\t\tif (plugfuncs && !plugfuncs->LoadDLL(\"./libcef\", ceffuncs))\n#endif\n\t\t{\n\t\t\tif (engineprocess)\n\t\t\t\tCon_Printf(\"Unable to load libcef (version \"CEF_VERSION\")\\n\");\n\t\t\treturn false;\n\t\t}\n#endif\n\n\t\tif (engineprocess)\n\t\t{\n\t\t\tCon_DPrintf(\"libcef %i.%i.%i.%i (chrome %i.%i.%i.%i)\\n\", cef_version_info(0), cef_version_info(1), cef_version_info(2), cef_version_info(3), cef_version_info(4), cef_version_info(5), cef_version_info(6), cef_version_info(7));\n\n\t\t\tif (cef_version_info(0) != CEF_VERSION_MAJOR||\n\t\t\t\tcef_version_info(1) != CEF_VERSION_MINOR||\n\t\t\t\tcef_version_info(2) != CEF_VERSION_PATCH||\n\t\t\t\tcef_version_info(3) != CEF_COMMIT_NUMBER||\n\t\t\t\tcef_version_info(4) != CHROME_VERSION_MAJOR||\n\t\t\t\tcef_version_info(5) != CHROME_VERSION_MINOR||\n\t\t\t\tcef_version_info(6) != CHROME_VERSION_BUILD||\n\t\t\t\tcef_version_info(7) != CHROME_VERSION_PATCH)\n\t\t\t{\t//the libcef api hash can be used to see if there's an api change that might break stuff.\n\t\t\t\t//refuse to load it if the api changed.\n\t\t\t\tCon_Printf(\"libcef outdated. Please install libcef version \"CEF_VERSION\"\\n\");\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\tapp_initialize();\n\t\tif (!engineprocess)\n\t\t{\n\t\t\tresult = cef_execute_process(&args, &app, 0);\n\t\t\tif (result >= 0 || !engineprocess)\n\t\t\t{\t//result is meant to be the exit code that the child process is meant to exit with\n\t\t\t\t//either way, we really don't want to return to the engine because that would run a second instance of it.\n\t\t\t\texit(result);\n\t\t\t\treturn qfalse;\n\t\t\t}\n\t\t}\n\t}\n\treturn cefwasloaded=qtrue;\n}\n//works with the --plugwrapper engine argument\nint NATIVEEXPORT CefSubprocessInit(plugcorefuncs_t *corefuncs)\n{\n\tplugfuncs = corefuncs;\n\treturn Cef_Init(false);\n}\n\nvoid Cef_ExecuteCommand(void)\n{\n\tif (confuncs && Cef_Init(true))\n\t{\n\t\tstatic int sequence;\n\t\tchar f[128];\n\t\tchar videomap[8192];\n\t\tQ_snprintf(f, sizeof(f), \"libcef:%i\", ++sequence);\n\t\tnewconsole = f;\n\t\tstrcpy(videomap, \"cef:\");\n\t\tcmdfuncs->Argv(1, videomap+4, sizeof(videomap)-4);\n\t\tif (!videomap[4])\n\t\t\tstrcpy(videomap, \"cef:https://fte.triptohell.info\");\n\n\t\tconfuncs->SetConsoleString(f, \"title\", videomap+4);\n\t\tconfuncs->SetConsoleFloat(f, \"iswindow\", true);\n\t\tconfuncs->SetConsoleFloat(f, \"forceutf8\", true);\n\t\tconfuncs->SetConsoleFloat(f, \"wnd_w\", 640+16);\n\t\tconfuncs->SetConsoleFloat(f, \"wnd_h\", 480+16+8);\n\t\tconfuncs->SetConsoleString(f, \"backvideomap\", videomap);\n\t\tconfuncs->SetConsoleFloat(f, \"linebuffered\", 2);\n\t\tconfuncs->SetActive(f);\n\n\t\tnewconsole = NULL;\n\t}\n}\n\nstatic qboolean QDECL Cef_PluginMayUnload(void)\n{\n\tif (cefwasinitialised)\n\t\treturn false;\t//cef is a piece of shite. we have to leave it running or the threads it spawns will crash+burn...\n\treturn true;\n}\n\nqboolean Plug_Init(void)\n{\n\tfsfuncs = (plugfsfuncs_t*)plugfuncs->GetEngineInterface(plugfsfuncs_name, sizeof(*fsfuncs));\t\t\t\t\t//for fte://data/ scheme\n\tconfuncs = (plugsubconsolefuncs_t*)plugfuncs->GetEngineInterface(plugsubconsolefuncs_name, sizeof(*confuncs));\t//for cef command etc.\n\tclientfuncs = (plugclientfuncs_t*)plugfuncs->GetEngineInterface(plugclientfuncs_name, sizeof(*clientfuncs));\t//for weird people trying to use xml requests to query game status (for hud stuff)\n\tif (!fsfuncs || !confuncs || !clientfuncs\n\t\t|| !plugfuncs->GetPluginName(-1, plugname, sizeof(plugname))\n\t\t|| !plugfuncs->ExportFunction(\"MayUnload\", Cef_PluginMayUnload)\n\t\t|| !plugfuncs->ExportFunction(\"Tick\", Cef_Tick)\n\t\t|| !plugfuncs->ExportFunction(\"Shutdown\", Cef_Shutdown))\n\t{\n\t\tCon_Printf(\"CEF plugin failed: Required engine feature missing.\\n\");\n\t\treturn false;\n\t}\n\n\tdecoderfuncs.structsize = sizeof(media_decoder_funcs_t);\n\tdecoderfuncs.drivername = \"cef\";\n\tdecoderfuncs.createdecoder = Cef_CreateOld;\n//\tdecoderfuncs.createdecoderCB = Cef_Create;\n\tdecoderfuncs.decodeframe = Cef_DisplayFrame;\n\tdecoderfuncs.shutdown = Cef_Destroy;\n\tdecoderfuncs.cursormove = Cef_CursorMove;\n\tdecoderfuncs.key = Cef_Key;\n\tdecoderfuncs.setsize = Cef_SetSize;\n\tdecoderfuncs.getsize = Cef_GetSize;\n\tdecoderfuncs.changestream = Cef_ChangeStream;\n\tdecoderfuncs.getproperty = Cef_GetProperty;\n\n\tif (!plugfuncs->ExportInterface(\"Media_VideoDecoder\", &decoderfuncs, sizeof(decoderfuncs)))\n\t{\n\t\tCon_Printf(\"CEF plugin failed: Engine doesn't support media decoder plugins\\n\");\n\t\treturn false;\n\t}\n\n\tcmdfuncs->AddCommand(\"cef\", Cef_ExecuteCommand, \"Open a web page!\");\n\n\tcef_incognito = cvarfuncs->GetNVFDG(\"cef_incognito\", \"0\", 0, NULL, \"browser settings\");\n\tcef_allowplugins = cvarfuncs->GetNVFDG(\"cef_allowplugins\", \"0\", 0, NULL, \"browser settings\");\n\tcef_allowcvars = cvarfuncs->GetNVFDG(\"cef_allowcvars\", \"0\", 0, NULL, \"browser settings\");\n\tcef_devtools = cvarfuncs->GetNVFDG(\"cef_devtools\", \"0\", 0, NULL, \"browser settings\");\n\n\treturn true;\n}\n\n"
  },
  {
    "path": "plugins/cod/codbsp.c",
    "content": "// https://wiki.zeroy.com/index.php?title=Call_of_Duty_1:_d3dbsp\n// https://wiki.zeroy.com/index.php?title=Call_of_Duty_2:_d3dbsp\n//yes, shockingly badly documented. that's half the challenge though, right?\n//vaguely derived from quake3.\n\n#include \"../plugin.h\"\n#include \"../engine/common/com_mesh.h\"\n#include \"../engine/common/com_bih.h\"\nstatic plugfsfuncs_t *filefuncs;\nstatic plugmodfuncs_t *modfuncs;\nstatic plugthreadfuncs_t\t*threadfuncs;\n\ntypedef struct\n{\n\t//materials are used for rendering and collisions\n\tq2mapsurface_t\t*surfaces;\t//for collision properties, texturing info, not actual surfaces.\n\n\t//generally useful stuff\n\tunsigned int codbspver;\n\tmplane_t *planes;\n\tsize_t num_planes;\n\tstruct codnode_s\n\t{\n\t\tmplane_t *plane;\n\t\tint childnum[2];\n\t\tivec3_t mins, maxs;\n\t} *nodes;\n\tsize_t numnodes;\n\tstruct codleaf_s\n\t{\n\t\tint cluster;\n\t\tint area;\n\t\t//we don't care about what we don't understand.\n\t\t//unsigned int firstleafbrush;\n\t\t//unsigned int numleafbrushes;\n\t\t//unsigned int firstleafsurface;\n\t\t//unsigned int numleafsurfaces;\n\t\t//int cell;\n\t\tunsigned int firstlightindex;\n\t\tunsigned int numlightindexes;\n\t} *leaf;\n\tsize_t numleafs;\n\tqbyte *pvsdata;\n\n\t//rendering stuff. this is pretty simple as its all soup.\n\tmesh_t soupverts;\t//don't trust the indexes!\n\tstruct codsoup_s\n\t{\n\t\tunsigned int vertex_offset;\n\t\tunsigned int index_offset;\n\t\tunsigned int index_fixup;\n\t} *soups;\t//aka surfs\n\n\t//brush collision... this stuff all goes away once we've built our BIH.\n\tconst struct codbsp_brushside_s\n\t{\t//unswapped...\n\t\tunion{\n\t\t\tunsigned int plane;\n\t\t\tfloat dist;\n\t\t};\n\t\tunsigned int material_idx;\n\t} *brushsides;\n\tsize_t num_brushsides;\n\tq2cbrush_t *brushes;\n\tsize_t num_brushes;\n\t//patch/soup collision nightmare.\n\tconst struct codpatch_s\n\t{\t//unswapped...\n\t\tshort mat;\t//material?\n\t\tshort mode;\t//mode\n\t\tunion\n\t\t{\n\t\t\tstruct\n\t\t\t{\t//patch\n\t\t\t\tshort w;\t//width\n\t\t\t\tshort h;\t//height\n\t\t\t\tint unknown;\t//unknown. flatness?\n\t\t\t\tint firstvert;\t//first CODLUMP_COLLISIONVERTS\n\t\t\t} mode0;\n\t\t\tstruct\n\t\t\t{\t//soup\n\t\t\t\tshort numverts;\n\t\t\t\tshort numidx;\n\t\t\t\tint firstvert;\t//first CODLUMP_COLLISIONVERTS\n\t\t\t\tint firstidx;\t//first CODLUMP_COLLISIONINDEXES\n\t\t\t} mode1;\n\t\t};\n\t} *patches;\n\tsize_t numpatches;\n\tconst unsigned int *leafpatches;\t//loadtime only, unswapped...\n\tsize_t numleafpatches;\n\tvecV_t *patchvertexes;\n\tsize_t numpatchvertexes;\n\tindex_t *patchindexes;\n\tsize_t numpatchindexes;\n\n\tunsigned short *lightindexes;\n\tsize_t numlightindexes;\n\tstruct codlight_s\n\t{\n\t\tint type;\n\t\tvec3_t xyz;\n\t\tvec3_t rgb;\n\t\tvec3_t dir;\n\t\tfloat scale;\n\t\tfloat fov;\n\t} *lights;\n\tsize_t numlights;\n} codbspinfo_t;\n\n#define COD1BSP_VERSION 0x0000003b\n#define COD2BSP_VERSION 0x00000004\nenum\n{\n\tCOD1LUMP_MATERIALS=0,\t//names, surfaceflags, and contentbits for said materials (so dedicated servers don't need to parse anything extra).\n\tCOD1LUMP_LIGHTMAPS=1,\t//just 2d images. 512*512 RGB ones.\n\tCOD1LUMP_PLANES=2,\t\t//used for nodes and brushsides\n\tCOD1LUMP_BRUSHSIDES=3,\t//which planes+materials to use for each side of the brushes...\n\tCOD1LUMP_BRUSHES=4,\t\t//defines which sets of said sides to group, and their material(contents).\n//\tCOD1LUMP_UNKNOWN=5,\n\tCOD1LUMP_SOUPS=6,\t\t//q3 would call em surfaces. except they're ALL trisoup, none are planar/patches, they're prebatched (within their 'cells').\n\tCOD1LUMP_SOUPVERTS=7,\t//the vertex attributes for the soup\n\tCOD1LUMP_SOUPINDEXES=8,\t//the indexes, woo. how fancy. it ain't soup without this!\n\tCOD1LUMP_CULLGROUPS=9,\n\tCOD1LUMP_CULLGROUPINDEXES=10,\n\tCOD1LUMP_PORTALVERTS=11,\n\tCOD1LUMP_OCCLUDERS=12,\n\tCOD1LUMP_OCCLUDERPLANES=13,\n\tCOD1LUMP_OCCLUDEREDGES=14,\n\tCOD1LUMP_OCCLUDERINDEXES=15,\n\tCOD1LUMP_AABBTREES=16,\t\t//some sort of tree...\n\tCOD1LUMP_CELLS=17,\t\t\t//part of the portal rendering system (reduced number vs leafs so there's less to compute)\n\tCOD1LUMP_PORTALS=18,\t\t//for walking between cells?\n\tCOD1LUMP_LIGHTINDEXES=19,\t//indexes into LIGHTVALUES\n\tCOD1LUMP_NODES=20,\t\t\t//tree leading to leafs.\n\tCOD1LUMP_LEAFS=21,\t\t\t//describes a small convex area\n\tCOD1LUMP_LEAFBRUSHES=22,\t//list of brushes so leafs can share them, for collision+pointcontents.\n\tCOD1LUMP_LEAFPATCHES=23,\t//list of patches and embedded meshes per leaf, for collision\n\tCOD1LUMP_PATCHCOLLISION=24,\t//defines which verts/topology etc to use for each mesh/patch\n\tCOD1LUMP_COLLISIONVERTS=25,\t//simple verts used for patch/embedded-mesh collision\n\tCOD1LUMP_COLLISIONINDEXES=26,//indexes for the collision verts, only used for embedded meshes\n\tCOD1LUMP_MODELS=27,\t\t\t//sub (aka inline) models - the first entry is the worldmodel, the second is your \"*1\" model and upwards.\n\tCOD1LUMP_VISIBILITY=28,\t\t//numclusters, numrowbytes, then just packed rows per cluster (no compression like q3 unlike q1).\n\tCOD1LUMP_ENTITIES=29,\n\tCOD1LUMP_LIGHTS=30,\n//\tCOD1LUMP_UNKNOWN=31,\n//\tCOD1LUMP_UNKNOWN=32,\n\tCOD1LUMP_COUNT=33\n};\nenum\n{\n\tCOD2LUMP_MATERIALS=0,\n\tCOD2LUMP_LIGHTMAPS=1,\n\tCOD2LUMP_LIGHTGRIDHASH=2,\n\tCOD2LUMP_LIGHTGRIDVALUES=3,\n\tCOD2LUMP_PLANES=4,\n\tCOD2LUMP_BRUSHSIDES=5,\n\tCOD2LUMP_BRUSHES=6,\n\tCOD2LUMP_SOUPS=7,\n\tCOD2LUMP_SOUPVERTS=8,\n\tCOD2LUMP_SOUPINDEXES=9,\n\tCOD2LUMP_CULLGROUPS=10,\n\tCOD2LUMP_CULLGROUPINDEXES=11,\n//\tCOD2LUMP_UNKNOWN=12,\n//\tCOD2LUMP_UNKNOWN=13,\n//\tCOD2LUMP_UNKNOWN=14,\n//\tCOD2LUMP_UNKNOWN=15,\n//\tCOD2LUMP_UNKNOWN=16,\n\tCOD2LUMP_PORTALVERTS=17,\n\tCOD2LUMP_OCCLUDERS=18,\n\tCOD2LUMP_OCCLUDERPLANES=19,\n\tCOD2LUMP_OCCLUDEREDGES=20,\n\tCOD2LUMP_OCCLUDERINDEXES=21,\n\tCOD2LUMP_AABBTREES=22,\n\tCOD2LUMP_CELLS=23,\n\tCOD2LUMP_PORTALS=24,\n\tCOD2LUMP_NODES=25,\n\tCOD2LUMP_LEAFS=26,\n\tCOD2LUMP_LEAFBRUSHES=27,\n//\tCOD2LUMP_LEAFPATCHES=28,\t//o.O\n\tCOD2LUMP_COLLISIONVERTS=29,\n\tCOD2LUMP_COLLISIONEDGES=30,\n\tCOD2LUMP_COLLISIONINDEXES=31,\n\tCOD2LUMP_COLLISIONBORDERS=32,\n\tCOD2LUMP_COLLISIONPARTS=33,\n\tCOD2LUMP_COLLISIONAABBS=34,\n\tCOD2LUMP_MODELS=35,\n\tCOD2LUMP_VISIBILITY=36,\n\tCOD2LUMP_ENTITIES=37,\n\tCOD2LUMP_PATHS=38,\n\tCOD2LUMP_COUNT=39\n};\n\nstatic struct codleaf_s *CODBSP_LeafForPoint (model_t *mod, const vec3_t p, int num)\n{\n\tcodbspinfo_t *prv = (codbspinfo_t*)mod->meshinfo;\n\tfloat\t\td;\n\tstruct codnode_s\t*node;\n\tmplane_t\t*plane;\n\n\twhile (num >= 0)\n\t{\n\t\tnode = prv->nodes + num;\n\t\tplane = node->plane;\n\n\t\tif (plane->type < 3)\n\t\t\td = p[plane->type] - plane->dist;\n\t\telse\n\t\t\td = DotProduct (plane->normal, p) - plane->dist;\n\t\tif (d < 0)\n\t\t\tnum = node->childnum[1];\n\t\telse\n\t\t\tnum = node->childnum[0];\n\t}\n\n\treturn &prv->leaf[-1 - num];\n}\nstatic int\tCODBSP_ClusterForPoint\t(struct model_s *model, const vec3_t point, int *areaout)\n{\n\tstruct codleaf_s *leaf = CODBSP_LeafForPoint(model, point, 0);\n\tif (areaout)\n\t\t*areaout = leaf->area;\n\treturn leaf->cluster;\n}\nstatic qbyte *CODBSP_ClusterPVS\t\t(struct model_s *model, int cluster, pvsbuffer_t *pvsbuffer, pvsmerge_t merge)\n{\n\tcodbspinfo_t *prv = (codbspinfo_t*)model->meshinfo;\n\tsize_t i;\n\tif (cluster >= 0 && cluster < model->numclusters)\n\t{\n\t\tqbyte *pvs = prv->pvsdata + cluster*model->pvsbytes;\t//packed, without compresion.\n\t\tif (merge == PVM_FAST)\n\t\t\treturn pvs;\n\t\telse\n\t\t{\n\t\t\tif (pvsbuffer->buffersize < model->pvsbytes)\n\t\t\t\tpvsbuffer->buffer = plugfuncs->Realloc(pvsbuffer->buffer, pvsbuffer->buffersize = model->pvsbytes);\n\t\t\tif (merge==PVM_REPLACE)\n\t\t\t\tmemcpy(pvsbuffer->buffer, pvs, model->pvsbytes);\n\t\t\telse for (i = 0; i < model->pvsbytes; i++)\n\t\t\t\tpvsbuffer->buffer[i] |= pvs[i];\t//slooooow\n\t\t\treturn pvsbuffer->buffer;\n\t\t}\n\t}\n\n\tif (pvsbuffer)\n\t{\n\t\tif (pvsbuffer->buffersize < model->pvsbytes)\n\t\t\tpvsbuffer->buffer = plugfuncs->Realloc(pvsbuffer->buffer, pvsbuffer->buffersize = model->pvsbytes);\n\t\tif (merge!=PVM_MERGE)\n\t\t\tmemset(pvsbuffer->buffer, 0, model->pvsbytes);\n\t\treturn pvsbuffer->buffer;\n\t}\n\treturn NULL;\n}\n//static qbyte *CODBSP_ClusterPHS\t\t(struct model_s *model, int cluster, pvsbuffer_t *pvsbuffer){return \"\\xff\";}\n\n\n//static void CODBSP_PurgeModel(struct model_s *mod){}\n\n//static qbyte *CODBSP_ClustersInSphere(struct model_s *model, const vec3_t point, float radius, pvsbuffer_t *pvsbuffer, const qbyte *fte_restrict unionwith){}\n\n//static size_t CODBSP_WriteAreaBits(struct model_s *model, qbyte *buffer, size_t maxbytes, int area, qboolean merge){}\n//static qboolean CODBSP_AreasConnected(struct model_s *model, unsigned int area1, unsigned int area2){}\n//static void CODBSP_SetAreaPortalState(struct model_s *model, unsigned int portal, unsigned int area1, unsigned int area2, qboolean open){}\n//static size_t CODBSP_SaveAreaPortalBlob(struct model_s *model, void **ptr){}\n//static size_t CODBSP_LoadAreaPortalBlob(struct model_s *model, void *ptr, size_t size){}\n\n\n#ifdef HAVE_SERVER\nstatic int\t\tleaf_count, leaf_maxcount;\nstatic struct codleaf_s **leaf_list;\nstatic const float\t*leaf_mins, *leaf_maxs;\nstatic int\t\tleaf_topnode;\n#define BoxOnPlaneSide CodBoxOnPlaneSide\nstatic int BoxOnPlaneSide (const vec3_t emins, const vec3_t emaxs, const mplane_t *p)\n{\n\tfloat\tdist1, dist2;\n\tint\t\tsides;\n// general case\n\tswitch (p->signbits)\n\t{\n\tdefault:\n\tcase 0:\ndist1 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2];\ndist2 = p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2];\n\t\tbreak;\n\tcase 1:\ndist1 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2];\ndist2 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2];\n\t\tbreak;\n\tcase 2:\ndist1 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2];\ndist2 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2];\n\t\tbreak;\n\tcase 3:\ndist1 = p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2];\ndist2 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2];\n\t\tbreak;\n\tcase 4:\ndist1 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2];\ndist2 = p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2];\n\t\tbreak;\n\tcase 5:\ndist1 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2];\ndist2 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2];\n\t\tbreak;\n\tcase 6:\ndist1 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2];\ndist2 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2];\n\t\tbreak;\n\tcase 7:\ndist1 = p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2];\ndist2 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2];\n\t\tbreak;\n\t}\n\tsides = 0;\n\tif (dist1 >= p->dist)\n\t\tsides = 1;\n\tif (dist2 < p->dist)\n\t\tsides |= 2;\n\treturn sides;\n}\nstatic void CODBSP_BoxLeafs_r (codbspinfo_t *prv, int nodenum)\n{\n\tmplane_t\t*plane;\n\tstruct codnode_s\t\t*node;\n\tint\t\ts;\n\twhile (1)\n\t{\n\t\tif (nodenum < 0)\n\t\t{\n\t\t\tif (leaf_count >= leaf_maxcount)\n\t\t\t\treturn;\n\t\t\tleaf_list[leaf_count++] = &prv->leaf[-1 - nodenum];\n\t\t\treturn;\n\t\t}\n\t\tnode = &prv->nodes[nodenum];\n\t\tplane = node->plane;\n\t\ts = BOX_ON_PLANE_SIDE(leaf_mins, leaf_maxs, plane);\n\t\tif (s == 1)\n\t\t\tnodenum = node->childnum[0];\n\t\telse if (s == 2)\n\t\t\tnodenum = node->childnum[1];\n\t\telse\n\t\t{\t// go down both\n\t\t\tif (leaf_topnode == -1)\n\t\t\t\tleaf_topnode = nodenum;\n\t\t\tCODBSP_BoxLeafs_r (prv, node->childnum[0]);\n\t\t\tnodenum = node->childnum[1];\n\t\t}\n\t}\n}\nstatic int\tCODBSP_BoxLeafs (model_t *mod, const vec3_t mins, const vec3_t maxs, struct codleaf_s **list, int listsize, int *topnode)\n{\n\tleaf_list = list;\n\tleaf_count = 0;\n\tleaf_maxcount = listsize;\n\tleaf_mins = mins;\n\tleaf_maxs = maxs;\n\n\tleaf_topnode = -1;\n\n\tCODBSP_BoxLeafs_r (mod->meshinfo, 0);\n\n\tif (topnode)\n\t\t*topnode = leaf_topnode;\n\n\treturn leaf_count;\n}\nstatic unsigned int CODBSP_FatPVS\t\t(struct model_s *mod, const vec3_t org, pvsbuffer_t *result, qboolean merge)\n{\n\tstruct codleaf_s *leafs[64];\n\tint\t\ti, j, count;\n\tvec3_t\tmins, maxs;\n\n\tfor (i=0 ; i<3 ; i++)\n\t{\n\t\tmins[i] = org[i] - 8;\n\t\tmaxs[i] = org[i] + 8;\n\t}\n\n\tcount = CODBSP_BoxLeafs (mod, mins, maxs, leafs, countof(leafs), NULL);\n\tif (count < 1)\n\t\tSys_Errorf (\"CODBSP_FatPVS: count < 1\");\n\n\t//grow the buffer if needed\n\tif (result->buffersize < mod->pvsbytes)\n\t\tresult->buffer = plugfuncs->Realloc(result->buffer, result->buffersize=mod->pvsbytes);\n\n\tif (count == 1 && leafs[0]->cluster == -1)\n\t{\t//if the only leaf is the outside then broadcast it.\n\t\tmemset(result->buffer, 0xff, mod->pvsbytes);\n\t\ti = count;\n\t}\n\telse\n\t{\n\t\ti = 0;\n\t\tif (!merge)\n\t\t\tmod->funcs.ClusterPVS(mod, leafs[i++]->cluster, result, PVM_REPLACE);\n\t\t// or in all the other leaf bits\n\t\tfor ( ; i<count ; i++)\n\t\t{\n\t\t\tfor (j=0 ; j<i ; j++)\n\t\t\t\tif (leafs[i] == leafs[j])\n\t\t\t\t\tbreak;\n\t\t\tif (j != i)\n\t\t\t\tcontinue;\t\t// already have the cluster we want\n\t\t\tmod->funcs.ClusterPVS(mod, leafs[i]->cluster, result, PVM_MERGE);\n\t\t}\n\t}\n\treturn mod->pvsbytes;\n}\nstatic qboolean CODBSP_HeadnodeVisible (codbspinfo_t *prv, int nodenum, const qbyte *visbits)\n{\n\tint\t\tleafnum;\n\tint\t\tcluster;\n\tstruct codnode_s *node;\n\n\tif (nodenum < 0)\n\t{\n\t\tleafnum = -1-nodenum;\n\t\tcluster = prv->leaf[leafnum].cluster;\n\t\tif (cluster == -1)\n\t\t\treturn false;\n\t\tif (visbits[cluster>>3] & (1<<(cluster&7)))\n\t\t\treturn true;\n\t\treturn false;\n\t}\n\n\tnode = &prv->nodes[nodenum];\n\tif (CODBSP_HeadnodeVisible(prv, node->childnum[0], visbits))\n\t\treturn true;\n\treturn CODBSP_HeadnodeVisible(prv, node->childnum[1], visbits);\n}\nstatic qboolean CODBSP_EdictInFatPVS\t(struct model_s *mod, const struct pvscache_s *ent, const qbyte *pvs, const int *areas)\n{\n\tint i,l;\n\t/*int nullarea = -1;\n\tif (areas)\n\t{\t//areas[0] is the count of areas the camera is in, if valid. requires us to track portal states...\n\t\tfor (i = 1; ; i++)\n\t\t{\n\t\t\tif (i > areas[0])\n\t\t\t\treturn false;\t//none of the camera's areas could see the entity\n\t\t\tif (areas[i] == ent->areanum)\n\t\t\t{\n\t\t\t\tif (areas[i] != nullarea)\n\t\t\t\t\tbreak;\n\t\t\t\t//else entity is fully outside the world, invisible to all...\n\t\t\t}\n\t\t\telse if (CODBSP_AreasConnected (mod, areas[i], ent->areanum))\n\t\t\t\tbreak;\n\t\t\t// doors can legally straddle two areas, so\n\t\t\t// we may need to check another one\n\t\t\telse if (ent->areanum2 != nullarea && CODBSP_AreasConnected (mod, areas[i], ent->areanum2))\n\t\t\t\tbreak;\n\t\t}\n\t}*/\n\n\tif (ent->num_leafs == -1)\n\t{\t// too many leafs for individual check, go by headnode\n\t\tif (!CODBSP_HeadnodeVisible (mod->meshinfo, ent->headnode, pvs))\n\t\t\treturn false;\n\t}\n\telse\n\t{\t// check individual leafs\n\t\tfor (i=0 ; i < ent->num_leafs ; i++)\n\t\t{\n\t\t\tl = ent->leafnums[i];\n\t\t\tif (pvs[l >> 3] & (1 << (l&7) ))\n\t\t\t\tbreak;\n\t\t}\n\t\tif (i == ent->num_leafs)\n\t\t\treturn false;\t\t// not visible\n\t}\n\treturn true;\n}\nstatic void CODBSP_FindTouchedLeafs\t(struct model_s *model, struct pvscache_s *ent, const vec3_t mins, const vec3_t maxs)\n{\n#define MAX_TOTAL_ENT_LEAFS\t\tMAX_ENT_LEAFS+1\n\tstruct codleaf_s *leafs[MAX_TOTAL_ENT_LEAFS];\n\tint\t\t\tclusters[MAX_TOTAL_ENT_LEAFS];\n\tint num_leafs;\n\tint\t\t\ttopnode;\n\tint i, j;\n\tint\t\t\tarea;\n\tint nullarea = -1;\n\n\t//ent->num_leafs == q2's ent->num_clusters\n\tent->num_leafs = 0;\n\tent->areanum = nullarea;\n\tent->areanum2 = nullarea;\n\n\tif (!mins || !maxs)\n\t\treturn;\n\n\t//get all leafs, including solids\n\tnum_leafs = CODBSP_BoxLeafs (model, mins, maxs, leafs, MAX_TOTAL_ENT_LEAFS, &topnode);\n\n\t// set areas\n\tfor (i=0 ; i<num_leafs ; i++)\n\t{\n\t\tclusters[i] = leafs[i]->cluster;\t//could dedupe these.\n\t\tarea = leafs[i]->area;\n\t\tif (area != nullarea)\n\t\t{\t// doors may legally straggle two areas,\n\t\t\t// but nothing should ever need more than that\n\t\t\tif (ent->areanum != nullarea && ent->areanum != area)\n\t\t\t\tent->areanum2 = area;\n\t\t\telse\n\t\t\t\tent->areanum = area;\n\t\t}\n\t}\n\n\tif (num_leafs >= MAX_TOTAL_ENT_LEAFS)\n\t{\t// assume we missed some leafs, and mark by headnode\n\t\tent->num_leafs = -1;\n\t\tent->headnode = topnode;\n\t}\n\telse\n\t{\n\t\tent->num_leafs = 0;\n\t\tfor (i=0 ; i<num_leafs ; i++)\n\t\t{\n\t\t\tif (clusters[i] == -1)\n\t\t\t\tcontinue;\t\t// not a visible leaf\n\t\t\tfor (j=0 ; j<i ; j++)\n\t\t\t\tif (clusters[j] == clusters[i])\n\t\t\t\t\tbreak;\n\t\t\tif (j == i)\n\t\t\t{\n\t\t\t\tif (ent->num_leafs == MAX_ENT_LEAFS)\n\t\t\t\t{\t// assume we missed some leafs, and mark by headnode\n\t\t\t\t\tent->num_leafs = -1;\n\t\t\t\t\tent->headnode = topnode;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tent->leafnums[ent->num_leafs++] = clusters[i];\n\t\t\t}\n\t\t}\n\t}\n}\n#endif\n#ifdef HAVE_CLIENT\nstatic void CODBSP_LightPointValues\t(struct model_s *mod, const vec3_t point, vec3_t res_diffuse, vec3_t res_ambient, vec3_t res_dir)\n{\n\tcodbspinfo_t\t*prv = (codbspinfo_t*)mod->meshinfo;\n\tstruct codleaf_s *leaf = CODBSP_LeafForPoint(mod, point, 0);\n\tunsigned short *lightindexes = prv->lightindexes + leaf->firstlightindex;\n\tsize_t i;\n\tstruct codlight_s *light;\n//\tvec3_t move;\n//\tfloat d;\n\tfloat scale;\n\n\tif (!leaf->numlightindexes)\n\t{\n\t\tVectorSet(res_diffuse, 128,128,128);\n\t\tVectorSet(res_ambient, 128,128,128);\n\t\tVectorSet(res_dir, 1,0,0);\n\t\treturn;\n\t}\n\n\tVectorSet(res_diffuse, 0,0,0);\n\tVectorSet(res_ambient, 0,0,0);\n\tVectorSet(res_dir, 0,0,0);\n\n\tfor (i = 0; i < leaf->numlightindexes; i++, lightindexes++)\n\t{\n\t\tif (*lightindexes >= prv->numlights)\n\t\t{\t// :( don't know what this is meant to signify, happens with the first index more often than not.\n\t\t\tif (!prv->numlights)\n\t\t\t\tcontinue;\n\t\t\tlight = prv->lights;\n\t\t}\n\t\telse\n\t\t\tlight = prv->lights + *lightindexes;\n\n\t\tswitch (light->type)\n\t\t{\n\t\tcase 1:\t//sun...\n\t\t\tscale = 256;\n\t\t\tbreak;\n/*\t\tcase 4:\n\t\t\tVectorSubtract(point, light->xyz, move);\n\t\t\td = DotProduct(move,move);\n\t\t\tif (d > light->scale)\n\t\t\t\tcontinue;\n\t\t\tscale = (light->scale-d)/d;\n\t\t\tscale *= 256;\n\t\t\tbreak;*/\n/*\t\tcase 5:\n\t\t\tbreak;*/\n/*\t\tcase 7:\n\t\t\tbreak;*/\n\t\tdefault:\n\t\t\tcontinue;\n\t\t}\n\t\tVectorMA(res_diffuse,\tscale, light->rgb, res_diffuse);\n\t\tVectorMA(res_ambient,\tscale, light->rgb, res_ambient);\n\t\tVectorMA(res_dir,\t\tscale, light->dir, res_dir);\n\n\t\tbreak; //:(\n\t}\n\n\tscale = DotProduct(res_dir,res_dir);\n\tif (scale <= 0)\n\t\tVectorSet(res_dir, 1,0,0);\n\telse\n\t\tVectorScale(res_dir, 1/scale, res_dir);\n}\n//static void CODBSP_GenerateShadowMesh\t(struct model_s *model, dlight_t *dl, const qbyte *lvis, qbyte *truevis, void(*callback)(struct msurface_s*)){}\n//static void CODBSP_StainNode\t\t\t(struct model_s *model, float *parms){}\n//static void CODBSP_MarkLights\t\t\t(struct dlight_s *light, dlightbitmask_t bit, struct mnode_s *node){}\n\nstatic void CODBSP_BuildSurfMesh(model_t *mod, msurface_t *surf, builddata_t *bd)\n{\t//just builds the actual mesh data, now that it has per-batch storage allocated.\n\tcodbspinfo_t\t*prv = (codbspinfo_t*)mod->meshinfo;\n\tmesh_t *mesh = surf->mesh;\n\tstruct codsoup_s *soup = &prv->soups[surf-mod->surfaces];\n\tint i;\n\n\tmesh->istrifan = false;\n\n\tmemcpy(mesh->xyz_array, prv->soupverts.xyz_array+soup->vertex_offset, sizeof(*mesh->xyz_array)*mesh->numvertexes);\n\tmemcpy(mesh->normals_array, prv->soupverts.normals_array+soup->vertex_offset, sizeof(*mesh->normals_array)*mesh->numvertexes);\n\tfor (i = 0;  i < mesh->numvertexes; i++)\n\t\tVector4Scale(prv->soupverts.colors4b_array[soup->vertex_offset+i], 1.f/255, mesh->colors4f_array[0][i]);\n\t//memcpy(mesh->colors4b_array, prv->soupverts.colors4b_array+soup->vertex_offset, sizeof(*mesh->colors4b_array)*mesh->numvertexes);\n\tmemcpy(mesh->st_array, prv->soupverts.st_array+soup->vertex_offset, sizeof(*mesh->st_array)*mesh->numvertexes);\n\tmemcpy(mesh->lmst_array[0], prv->soupverts.lmst_array[0]+soup->vertex_offset, sizeof(*mesh->lmst_array[0])*mesh->numvertexes);\n\n\tif (soup->index_fixup)\n\t{\n\t\tfor (i = 0; i < mesh->numindexes; i++)\n\t\t\tmesh->indexes[i] = prv->soupverts.indexes[soup->index_offset+i] - soup->index_fixup;\n\t}\n\telse\n\t\tmemcpy(mesh->indexes, prv->soupverts.indexes+soup->index_offset, sizeof(*mesh->indexes)*mesh->numindexes);\n\n\tif (prv->soupverts.snormals_array)\n\t{\t//cod2 made them explicit. yay.\n\t\tmemcpy(mesh->snormals_array, prv->soupverts.snormals_array+soup->vertex_offset, sizeof(*mesh->snormals_array)*mesh->numvertexes);\n\t\tmemcpy(mesh->tnormals_array, prv->soupverts.tnormals_array+soup->vertex_offset, sizeof(*mesh->tnormals_array)*mesh->numvertexes);\n\t}\n\telse\n\t{\t//compute the tangents for rtlights.\n\t\tmodfuncs->AccumulateTextureVectors(mesh->xyz_array, mesh->st_array, mesh->normals_array, mesh->snormals_array, mesh->tnormals_array, mesh->indexes, mesh->numindexes, false);\n\t\tmodfuncs->NormaliseTextureVectors(mesh->normals_array, mesh->snormals_array, mesh->tnormals_array, mesh->numvertexes, false);\n\t}\n}\nstatic void CODBSP_GenerateMaterials(void *ctx, void *data, size_t a, size_t b)\n{\n\tmodel_t *mod = ctx;\n\tconst char *script;\n\n\tif (!a)\n\t{\t//submodels share textures, so only do this if 'a' is 0 (inline index, 0 = world).\n\t\tfor(a = 0; a < mod->numtextures; a++)\n\t\t{\n\t\t\tscript = NULL;\n\t\t\tif (!strncmp(mod->textures[a]->name, \"sky/\", 4))\n\t\t\t\tscript =\n\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\"sort sky\\n\"\n\t\t\t\t\t\t\"surfaceparm nodlight\\n\"\n\t\t\t\t\t\t\"skyparms - - -\\n\"\n\t\t\t\t\t\"}\\n\";\n\t\t\tmod->textures[a]->shader = modfuncs->RegisterBasicShader(mod, mod->textures[a]->name, SUF_LIGHTMAP, script, PTI_INVALID, 0, 0, NULL, NULL);\n\t\t}\n\t}\n\tmodfuncs->Batches_Build(mod, data);\n\tif (data)\n\t\tplugfuncs->Free(data);\n}\n\nstatic void CODBSP_PrepareFrame(struct model_s *mod, refdef_t *refdef, int area, int clusters[2], pvsbuffer_t *vis, qbyte **entvis_out, qbyte **surfvis_out)\n{\n\t*entvis_out = *surfvis_out = CODBSP_ClusterPVS(mod, clusters[0], vis, false);\n\tif (clusters[1] != -1)\n\t\tCODBSP_ClusterPVS(mod, clusters[1], vis, true);\n\n\t/*if (!refdef->areabitsknown)\n\t{\t//generate the info each frame, as the gamecode didn't tell us what to use.\n\t\tint leafnum = CODBSP_PointLeafnum (mod, refdef->vieworg);\n\t\tint clientarea = CODBSP_LeafArea (mod, leafnum);\n\t\tCODBSP_WriteAreaBits(mod, refdef->areabits, clientarea, false);\n\t\trefdef->areabitsknown = true;\n\t}*/\n\n\tif (0)\n\t{\n\t\tsize_t i;\n\t\tmsurface_t *surf;\n\t\tfor (i = mod->firstmodelsurface+mod->nummodelsurfaces; i --> mod->firstmodelsurface; )\n\t\t{\n\t\t\tsurf = &mod->surfaces[i];\n\t\t\tsurf->sbatch->mesh[surf->sbatch->meshes++] = surf->mesh;\n\t\t}\n\t}\n\telse\n\t{\n\t\tsize_t i;\n\t\tmsurface_t *surf;\n\t\tfor (i = mod->firstmodelsurface; i < mod->nummodelsurfaces; i++)\n\t\t{\n\t\t\tsurf = &mod->surfaces[i];\n\t\t\tsurf->sbatch->mesh[surf->sbatch->meshes++] = surf->mesh;\n\t\t}\n\t}\n\n\t//for static props...\n\t//ent = modfuncs->NewSceneEntity();\n}\nstatic void CODBSP_InfoForPoint(struct model_s *mod, vec3_t pos, int *area, int *cluster, unsigned int *contentbits)\n{\n\tstruct codleaf_s *leaf = CODBSP_LeafForPoint(mod, pos, 0);\n\t*area = leaf->area;\n\t*cluster = leaf->cluster;\n\t*contentbits = mod->funcs.PointContents(mod, NULL, pos);\t//needs a proper pointcontents.\n}\n#endif\n\nstatic qboolean CODBSP_LoadShaders (model_t *mod, qbyte *mod_base, lump_t *l)\n{\n\tcodbspinfo_t\t*prv = (codbspinfo_t*)mod->meshinfo;\n\tstruct\n\t{\n\t\tchar shadername[64];\n\t\tunsigned int surfflags;\n\t\tunsigned int contents;\n\t} *in = (void *)(mod_base + l->fileofs);\n\tq2mapsurface_t\t*out;\n\tint\t\t\t\ti, count;\n\ttexture_t *tex;\n\n\tif (l->filelen % sizeof(*in))\n\t{\n\t\tCon_Printf (CON_ERROR \"CODBSP_LoadShaders: funny lump size\\n\");\n\t\treturn false;\n\t}\n\tcount = l->filelen / sizeof(*in);\n\n\tif (count < 1)\n\t{\n\t\tCon_Printf (CON_ERROR \"CODBSP_LoadShaders: Map with no shaders\\n\");\n\t\treturn false;\n\t}\n\n\tmod->numtexinfo = count;\n\tout = prv->surfaces = plugfuncs->GMalloc(&mod->memgroup, count*sizeof(*out));\n\n\tmod->textures = plugfuncs->GMalloc(&mod->memgroup, (sizeof(texture_t*)+sizeof(mtexinfo_t)+sizeof(texture_t))*count);\t//+1 is 'noshader' for flares.\n\tmod->texinfo = (mtexinfo_t*)(mod->textures+count);\n\ttex = (texture_t*)(mod->texinfo+count);\n\tmod->numtextures = count;\n\n\tfor ( i=0 ; i<count ; i++, in++, out++ )\n\t{\n\t\tout->c.flags = LittleLong ( in->surfflags );\n\t\tout->c.value = LittleLong ( in->contents );\n\t\tQ_strlcpy(out->rname, in->shadername, sizeof(out->rname));\n\n\t\tmod->texinfo[i].texture = tex+i;\n\t\tmod->texinfo[i].flags = prv->surfaces[i].c.flags;\n\t\tQ_strlcpy(mod->texinfo[i].texture->name, in->shadername, sizeof(mod->texinfo[i].texture->name));\n\t\tmod->textures[i] = mod->texinfo[i].texture;\n\t}\n\n\treturn true;\n}\n\nstatic qboolean COD1BSP_LoadLightmap(model_t *mod, qbyte *mod_base, lump_t *l)\n{\n//\tcodbspinfo_t\t*prv = (codbspinfo_t*)mod->meshinfo;\n\tint overbright;\n\tint bytes;\n\tsize_t i, lev;\n\tqbyte *in = mod_base+l->fileofs;\n\tmod->lightmaps.width = 512;\n\tmod->lightmaps.height = 512;\n\tmod->lightmaps.prebaked = PTI_RGB8;\n\tmod->lightmaps.fmt = LM_RGB8;\n\tbytes = 3;\n\tmod->lightmaps.surfstyles = 1;\t//always style 0...\n\tmod->lightmaps.maxstyle = 0;\n\tmod->lightmaps.deluxemapping = false;\n\tmod->lightmaps.deluxemapping_modelspace = false;\n\tmod->lightmaps.first = 0;\n\tmod->lightmaps.count = l->filelen / (mod->lightmaps.width*mod->lightmaps.height*bytes);\n\tif (l->filelen != mod->lightmaps.count * (mod->lightmaps.width*mod->lightmaps.height*bytes))\n\t{\n\t\tCon_Printf (CON_ERROR \"CODBSP_LoadLighting: funny lump size\\n\");\n\t\treturn false;\t//err... rounded badly.\n\t}\n\n\tmod->lightdata = plugfuncs->GMalloc(&mod->memgroup, l->filelen);\n\tmod->lightdatasize = l->filelen;\n\n\toverbright = cvarfuncs->GetFloat(\"gl_overbright\");\n\tmod->engineflags = MDLF_NEEDOVERBRIGHT;\n\tif (overbright == 2)\n\t\tmemcpy(mod->lightdata, in, l->filelen);\n\telse\n\t{\n\t\tqbyte *out = mod->lightdata;\n\t\toverbright = (1<<(2-overbright));\n\t\tfor (i = 0; i < l->filelen; i++, in++)\n\t\t{\n\t\t\tlev = *in * overbright;\n\t\t\t*out++ = min(255, lev);\n\t\t}\n\t}\n\treturn true;\n}\nstatic qboolean COD2BSP_LoadLightmap(model_t *mod, qbyte *mod_base, lump_t *l)\n{\n\t//seems to be sets of 4 images (3 normalmaps and some extra discoloured one). more deluxemap than lightmap. this is not useful to us.\n\t//cod2 bundles some hlsl code, which may reveal clues.\n//\tcodbspinfo_t\t*prv = (codbspinfo_t*)mod->meshinfo;\n\n\tmod->lightmaps.width = 512;\n\tmod->lightmaps.height = 512;\n\tmod->lightmaps.prebaked = PTI_RGBA8;\t//needs glsl to use properly.\n\n\tmod->lightmaps.surfstyles = 1;\t//always style 0...\n\tmod->lightmaps.maxstyle = 0;\n\tmod->lightmaps.deluxemapping = false;\t//fixme: uses 4 lightmap textures at a time.\n\tmod->lightmaps.deluxemapping_modelspace = false;\n\tmod->lightmaps.first = 0;\n\tmod->lightmaps.count = 0;\n\n\tCon_Printf(CON_WARNING\"COD2 lightmaps are not supported\\n\");\n\treturn true;\n}\nstatic qboolean COD1BSP_LoadSoupVertices (model_t *mod, qbyte *mod_base, lump_t *l)\n{\n\tcodbspinfo_t\t*prv = (codbspinfo_t*)mod->meshinfo;\n\tstruct\n\t{\n\t\tvec3_t position;\n\t\tvec2_t tc;\n\t\tvec2_t lmtc;\n\t\tvec3_t normal;\n\t\tbyte_vec4_t rgba;\n\t} *in = (void*)((qbyte*)mod_base + l->fileofs);\n\tsize_t i, count = l->filelen / sizeof(*in);\n\tmesh_t *mesh = &prv->soupverts;\n\tif (l->filelen % sizeof(*in))\n\t{\n\t\tCon_Printf (CON_ERROR \"CODBSP_LoadSoupVertices: funny lump size\\n\");\n\t\treturn false;\n\t}\n\n\t//allocate lots of space. stoopid separate arrays.\n\tprv->soupverts.istrifan = false;\n\tprv->soupverts.xyz_array = plugfuncs->GMalloc(&mod->memgroup, count*(\n\t\t\tsizeof(*mesh->xyz_array)+\n\t\t\tsizeof(*mesh->normals_array)+\n\t\t\tsizeof(*mesh->colors4b_array)+\n\t\t\tsizeof(*mesh->st_array)+\n\t\t\tsizeof(*mesh->lmst_array[0])));\n\tmesh->normals_array\t\t= (void*)(mesh->xyz_array\t\t+ count);\n\tmesh->colors4b_array\t= (void*)(mesh->normals_array\t+ count);\n\tmesh->st_array\t\t\t= (void*)(mesh->colors4b_array\t+ count);\n\tmesh->lmst_array[0]\t\t= (void*)(mesh->st_array\t\t+ count);\n\n\t//copy it all over.\n\tfor (i = 0; i < count; i++, in++)\n\t{\n\t\tmesh->xyz_array[i][0] = LittleFloat(in->position[0]);\n\t\tmesh->xyz_array[i][1] = LittleFloat(in->position[1]);\n\t\tmesh->xyz_array[i][2] = LittleFloat(in->position[2]);\n\t\tmesh->st_array[i][0] = LittleFloat(in->tc[0]);\n\t\tmesh->st_array[i][1] = LittleFloat(in->tc[1]);\n\t\tmesh->lmst_array[0][i][0] = LittleFloat(in->lmtc[0]);\n\t\tmesh->lmst_array[0][i][1] = LittleFloat(in->lmtc[1]);\n\t\tmesh->normals_array[i][0] = LittleFloat(in->normal[0]);\n\t\tmesh->normals_array[i][1] = LittleFloat(in->normal[1]);\n\t\tmesh->normals_array[i][2] = LittleFloat(in->normal[2]);\n\t\tmesh->colors4b_array[i][0] = in->rgba[0];\n\t\tmesh->colors4b_array[i][1] = in->rgba[1];\n\t\tmesh->colors4b_array[i][2] = in->rgba[2];\n\t\tmesh->colors4b_array[i][3] = in->rgba[3];\n\t}\n\treturn true;\n}\nstatic qboolean COD2BSP_LoadSoupVertices (model_t *mod, qbyte *mod_base, lump_t *l)\n{\n\tcodbspinfo_t\t*prv = (codbspinfo_t*)mod->meshinfo;\n\tstruct\n\t{\t//slightly rearranged for some reason (plus addition of tangents at the end)\n\t\tvec3_t position;\n\t\tvec3_t normal;\n\t\tbyte_vec4_t rgba;\n\t\tvec2_t tc;\n\t\tvec2_t lmtc;\n\t\tvec3_t sdir;\n\t\tvec3_t tdir;\n\t} *in = (void*)((qbyte*)mod_base + l->fileofs);\n\tsize_t i, count = l->filelen / sizeof(*in);\n\tmesh_t *mesh = &prv->soupverts;\n\tif (l->filelen % sizeof(*in))\n\t{\n\t\tCon_Printf (CON_ERROR \"CODBSP_LoadSoupVertices: funny lump size\\n\");\n\t\treturn false;\n\t}\n\n\t//allocate lots of space. stoopid separate arrays.\n\tprv->soupverts.istrifan = false;\n\tprv->soupverts.xyz_array = plugfuncs->GMalloc(&mod->memgroup, count*(\n\t\t\tsizeof(*mesh->xyz_array)+\n\t\t\tsizeof(*mesh->normals_array)+\n\t\t\tsizeof(*mesh->snormals_array)+\n\t\t\tsizeof(*mesh->tnormals_array)+\n\t\t\tsizeof(*mesh->colors4b_array)+\n\t\t\tsizeof(*mesh->st_array)+\n\t\t\tsizeof(*mesh->lmst_array[0])));\n\tmesh->normals_array\t\t= (void*)(mesh->xyz_array\t\t+ count);\n\tmesh->snormals_array\t= (void*)(mesh->normals_array\t\t+ count);\n\tmesh->tnormals_array\t= (void*)(mesh->snormals_array\t\t+ count);\n\tmesh->colors4b_array\t= (void*)(mesh->tnormals_array\t+ count);\n\tmesh->st_array\t\t\t= (void*)(mesh->colors4b_array\t+ count);\n\tmesh->lmst_array[0]\t\t= (void*)(mesh->st_array\t\t+ count);\n\n\t//copy it all over.\n\tfor (i = 0; i < count; i++, in++)\n\t{\n\t\tmesh->xyz_array[i][0] = LittleFloat(in->position[0]);\n\t\tmesh->xyz_array[i][1] = LittleFloat(in->position[1]);\n\t\tmesh->xyz_array[i][2] = LittleFloat(in->position[2]);\n\t\tmesh->st_array[i][0] = LittleFloat(in->tc[0]);\n\t\tmesh->st_array[i][1] = LittleFloat(in->tc[1]);\n\t\tmesh->lmst_array[0][i][0] = LittleFloat(in->lmtc[0]);\n\t\tmesh->lmst_array[0][i][1] = LittleFloat(in->lmtc[1]);\n\t\tmesh->normals_array[i][0] = LittleFloat(in->normal[0]);\n\t\tmesh->normals_array[i][1] = LittleFloat(in->normal[1]);\n\t\tmesh->normals_array[i][2] = LittleFloat(in->normal[2]);\n\t\tmesh->snormals_array[i][0] = LittleFloat(in->sdir[0]);\n\t\tmesh->snormals_array[i][1] = LittleFloat(in->sdir[1]);\n\t\tmesh->snormals_array[i][2] = LittleFloat(in->sdir[2]);\n\t\tmesh->tnormals_array[i][0] = LittleFloat(in->tdir[0]);\n\t\tmesh->tnormals_array[i][1] = LittleFloat(in->tdir[1]);\n\t\tmesh->tnormals_array[i][2] = LittleFloat(in->tdir[2]);\n\t\tmesh->colors4b_array[i][0] = in->rgba[0];\n\t\tmesh->colors4b_array[i][1] = in->rgba[1];\n\t\tmesh->colors4b_array[i][2] = in->rgba[2];\n\t\tmesh->colors4b_array[i][3] = in->rgba[2];\n\t}\n\treturn true;\n}\nstatic qboolean CODBSP_LoadSoupIndexes (model_t *mod, qbyte *mod_base, lump_t *l)\n{\n\tcodbspinfo_t\t*prv = (codbspinfo_t*)mod->meshinfo;\n\tunsigned short *in = (void*)((qbyte*)mod_base + l->fileofs);\n\tsize_t i, count = l->filelen / sizeof(*in);\n\tif (l->filelen % sizeof(*in))\n\t{\n\t\tCon_Printf (CON_ERROR \"CODBSP_LoadSoupIndexes: funny lump size\\n\");\n\t\treturn false;\n\t}\n\tprv->soupverts.indexes = plugfuncs->GMalloc(&mod->memgroup, count*sizeof(*prv->soupverts.indexes));\n\tfor (i = 0; i < count; i++, in++)\n\t\tprv->soupverts.indexes[i] = LittleShort(*in);\n\n\treturn true;\n}\nstatic qboolean CODBSP_LoadSoups (model_t *mod, qbyte *mod_base, lump_t *l)\n{\n\tcodbspinfo_t\t*prv = (codbspinfo_t*)mod->meshinfo;\n\tstruct\n\t{\n\t\tunsigned short material_idx;\n\t\tunsigned short lightmap_idx;\n\t\tunsigned int vertex_offset;\n\t\tunsigned short vertex_count;\n\t\tunsigned short index_count;\n\t\tunsigned int index_offset;\n\t} *in = (void*)((qbyte*)mod_base + l->fileofs);\n\tstruct codsoup_s *out;\n\tmesh_t *mesh;\n\tsize_t j, i, count = l->filelen / sizeof(*in);\n\tunsigned int mn,mx, idx;\n\tif (l->filelen % sizeof(*in))\n\t{\n\t\tCon_Printf (CON_ERROR \"CODBSP_LoadSoups: funny lump size\\n\");\n\t\treturn false;\n\t}\n\tprv->soups = out = plugfuncs->GMalloc(&mod->memgroup, count*sizeof(*out));\n\tmod->numsurfaces = count;\n\tmod->nummodelsurfaces = count;\n\tmod->surfaces = plugfuncs->GMalloc(&mod->memgroup,\tcount*sizeof(*mod->surfaces) +\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tcount*sizeof(*mesh));\n\tmesh = (void*)(mod->surfaces+count);\n\tfor (i = 0; i < count; i++, in++, out++)\n\t{\n\t\tunsigned short tex\t= LittleShort(in->material_idx);\n\t\tunsigned short lmap\t= LittleShort(in->lightmap_idx);\n\t\tout->vertex_offset\t= LittleLong (in->vertex_offset);\n\t\tout->index_offset\t= LittleLong (in->index_offset);\n\n\t\tif (tex >= mod->numtexinfo)\n\t\t\treturn false;\n\n\t\tfor (j = 0; j < MAXCPULIGHTMAPS; j++)\n\t\t\tmod->surfaces[i].styles[j] = INVALID_LIGHTSTYLE;\n\t\tfor (j = 0; j < MAXRLIGHTMAPS; j++)\n\t\t{\n\t\t\tmod->surfaces[i].vlstyles[j] = INVALID_VLIGHTSTYLE;\n\t\t\tmod->surfaces[i].lightmaptexturenums[j] = -1;\n\t\t}\n\t\tmod->surfaces[i].styles[0] = 0;\n\t\tmod->surfaces[i].lightmaptexturenums[0] = lmap==(unsigned short)~0u?INVALID_LIGHTSTYLE:lmap;\n\n\t\tmod->surfaces[i].texinfo = &mod->texinfo[tex];\n\t\tmod->surfaces[i].mesh = &mesh[i];\n\t\tmesh[i].numindexes = LittleShort(in->index_count);\n\t\tmesh[i].numvertexes = LittleShort(in->vertex_count);\n\n\t\t//cod2 sucks and is way out and results in horrible memory use. calculate what it should have been.\n\t\tmn = ~0u;\n\t\tmx = 0;\n\t\tfor (j = 0; j < mesh[i].numindexes; j++)\n\t\t{\n\t\t\tidx = LittleLong(prv->soupverts.indexes[out->index_offset+j]);\n\t\t\tif (mx<= idx)\n\t\t\t\tmx = idx+1;\n\t\t\tif (mn > idx)\n\t\t\t\tmn = idx;\n\t\t}\n\t\tif (mx<mn)\n\t\t\tmx=mn=0;\t//erk?\n\t\tmesh[i].numvertexes = mx-mn;\n\t\tif (mesh[i].numvertexes > 65535)\n\t\t\treturn false;\n\t\tout->index_fixup = mn;\n\t\tout->vertex_offset += mn;\n\t}\n\n\treturn true;\n}\n\nstatic qboolean CODBSP_LoadLights (model_t *mod, qbyte *mod_base, lump_t *l)\n{\n\tcodbspinfo_t\t*prv = (codbspinfo_t*)mod->meshinfo;\n\tstruct\n\t{\t//18...\n\t\tint type;\t//1=sunlight?\n\t\t\t\t\t//4=omni?\n\t\t\t\t\t//5=oriented non-spot?\n\t\t\t\t\t//7=oriented spot light?\n\t\tvec3_t rgb;\t//these values are completely fucked in most modes. :(\n\t\tvec3_t xyz;\t//actually matches a lightsource\n\t\tvec3_t dir;\n\t\tfloat pointnineish;\t//some sort of exponent? falloff?\n\t\tfloat scale;\t\t//?big float. radius? sometimes denormalised?\n\t\tfloat fov;\t\t//very fovy\n\t\tint naught0;\t//no info. could be floats.\n\t\tint naught1;\t//no info. could be floats.\n\t\tint naught2;\t//no info. could be floats.\n\t\tint naught3;\t//no info. could be floats.\n\t\tint naught4;\t//no info. could be floats.\n\t} *in = (void*)((qbyte*)mod_base + l->fileofs);\n\tstruct codlight_s *out;\n\tsize_t i, count = l->filelen / sizeof(*in);\n\tif (l->filelen % sizeof(*in))\n\t{\n\t\tCon_Printf (CON_ERROR \"CODBSP_LoadLightValues: funny lump size\\n\");\n\t\treturn false;\n\t}\n\tprv->lights = out = plugfuncs->GMalloc(&mod->memgroup, count*sizeof(*out));\n\tprv->numlights = count;\n\tfor (i = 0; i < count; i++, in++, out++)\n\t{\n\t\tout->type\t= LittleLong(in->type);\n\t\tout->xyz[0] = LittleFloat(in->xyz[0]);\n\t\tout->xyz[1] = LittleFloat(in->xyz[1]);\n\t\tout->xyz[2] = LittleFloat(in->xyz[2]);\n\t\tout->rgb[0] = LittleFloat(in->rgb[0]);\n\t\tout->rgb[1] = LittleFloat(in->rgb[1]);\n\t\tout->rgb[2] = LittleFloat(in->rgb[2]);\n\t\tout->dir[0] = LittleFloat(in->dir[0]);\n\t\tout->dir[1] = LittleFloat(in->dir[1]);\n\t\tout->dir[2] = LittleFloat(in->dir[2]);\n\t\tout->scale\t= LittleFloat(in->scale);\n\t\tout->fov\t= LittleFloat(in->fov);\n\t}\n\n\treturn true;\n}\nstatic qboolean CODBSP_LoadLightIndexes (model_t *mod, qbyte *mod_base, lump_t *l)\n{\n\tcodbspinfo_t\t*prv = (codbspinfo_t*)mod->meshinfo;\n\tunsigned short *in = (void*)((qbyte*)mod_base + l->fileofs), *out, v;\n\tsize_t i, count = l->filelen / sizeof(*in);\n\tif (l->filelen % sizeof(*in))\n\t{\n\t\tCon_Printf (CON_ERROR \"CODBSP_LoadLightIndexes: funny lump size\\n\");\n\t\treturn false;\n\t}\n\tprv->lightindexes = out = plugfuncs->GMalloc(&mod->memgroup, count*sizeof(*out));\n\tprv->numlightindexes = count;\n\tfor (i = 0; i < count; i++)\n\t{\n\t\tv = LittleShort(*in++);\n\t\tif (v == 0xffff)\n\t\t\t; //o.O\n\t\telse if (v >= prv->numlights)\n\t\t{\n\t\t\tCon_Printf (CON_ERROR \"CODBSP_LoadLightIndexes: invalid index %i\\n\", v);\n\t\t\treturn false;\n\t\t}\n\t\t*out++ = v;\n\t}\n\treturn true;\n}\nstatic qboolean CODBSP_LoadEntities (model_t *mod, qbyte *mod_base, lump_t *l)\n{\t//just quake-style { \"field\" \"value\" \"field2\" \"value2\" } blocks.\n\treturn modfuncs->LoadEntities(mod, mod_base+l->fileofs, l->filelen);\n}\n\nstatic qboolean CODBSP_LoadPlanes (model_t *mod, qbyte *mod_base, lump_t *l)\n{\n\tcodbspinfo_t *prv = (codbspinfo_t*)mod->meshinfo;\n\tvec4_t *in = (void*)((qbyte*)mod_base + l->fileofs);\n\tmplane_t *out;\n\tsize_t j, i, count = l->filelen / sizeof(*in);\n\tif (l->filelen % sizeof(*in))\n\t{\n\t\tCon_Printf (CON_ERROR \"CODBSP_LoadPlanes: funny lump size\\n\");\n\t\treturn false;\n\t}\n\tprv->planes = out = plugfuncs->GMalloc(&mod->memgroup, count*sizeof(*out));\n\tprv->num_planes = count;\n\tfor (i = 0; i < count; i++, out++)\n\t{\n\t\tout->normal[0] = LittleFloat(in[i][0]);\n\t\tout->normal[1] = LittleFloat(in[i][1]);\n\t\tout->normal[2] = LittleFloat(in[i][2]);\n\t\tout->dist = LittleFloat(in[i][3]);\n\n\t\tout->type = PLANE_ANYZ;\n\t\tout->signbits = 0;\n\t\tfor (j=0 ; j<3 ; j++)\n\t\t{\n\t\t\tif (out->normal[j] < 0)\n\t\t\t\tout->signbits |= 1<<j;\n\t\t\telse if (out->normal[j] == 1)\n\t\t\t\tout->type = j;\n\t\t}\n\t}\n\treturn true;\n}\nstatic qboolean CODBSP_LoadBrushSides (model_t *mod, qbyte *mod_base, lump_t *l)\n{\n\tcodbspinfo_t\t*prv = (codbspinfo_t*)mod->meshinfo;\n\tstruct codbsp_brushside_s *in = (void*)((qbyte*)mod_base + l->fileofs);\n\tsize_t count = l->filelen / sizeof(*in);\n\tif (l->filelen % sizeof(*in))\n\t{\n\t\tCon_Printf (CON_ERROR \"CODBSP_LoadBrushSides: funny lump size\\n\");\n\t\treturn false;\n\t}\n\tprv->brushsides = in;\t//used elsewhere in the loader\n\tprv->num_brushsides = count;\n\treturn true;\n}\nstatic qboolean CODBSP_LoadBrushes (model_t *mod, qbyte *mod_base, lump_t *l)\n{\n\tcodbspinfo_t\t*prv = (codbspinfo_t*)mod->meshinfo;\n\tconst struct\n\t{\n\t\tunsigned short sides;\n\t\tunsigned short material;\n\t} *in = (void*)((qbyte*)mod_base + l->fileofs);\n\tconst struct codbsp_brushside_s *inside = prv->brushsides;\n\tq2cbrush_t *out;\n\tq2cbrushside_t *outside;\n\tmplane_t *aplane;\n\tsize_t j, i, count = l->filelen / sizeof(*in);\n\tif (l->filelen % sizeof(*in))\n\t{\n\t\tCon_Printf (CON_ERROR \"CODBSP_LoadBrushes: funny lump size\\n\");\n\t\treturn false;\n\t}\n\tprv->brushes = out = plugfuncs->GMalloc(&mod->memgroup, count*sizeof(*out));\n\taplane = plugfuncs->GMalloc(&mod->memgroup, count*6*sizeof(*aplane));\n\t//read the data\n\tfor (i = 0, j = 0; i < count; i++, in++)\n\t{\n\t\tunsigned int mat = LittleShort(in->material);\n\t\tout[i].numsides = (unsigned short)LittleShort(in->sides);\n\t\tout[i].contents = prv->surfaces[mat].c.value;\t//is this right? seems to kinda work? feels wrong though.\n\t\tj += out[i].numsides;\n\t}\n\t//fix up the planes...\n\toutside = plugfuncs->GMalloc(&mod->memgroup, j*sizeof(*outside));\n\tprv->num_brushes = count;\n\tfor (i = 0, j = 0; i < count; i++, out++)\n\t{\n\t\tout->brushside = outside;\n\t\tfor (j = 0; j < out->numsides; j++, inside++, outside++)\n\t\t{\n\t\t\tunsigned int mat = LittleLong(inside->material_idx);\n\t\t\tif (j < 6)\n\t\t\t{\n\t\t\t\taplane->dist = LittleFloat(inside->dist);\n\t\t\t\tif (j&1)\n\t\t\t\t{\t//stored nx px ny py nz pz\n\t\t\t\t\taplane->normal[j>>1] = 1;\n\t\t\t\t\tout->absmaxs[j>>1] = aplane->dist;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\taplane->normal[j>>1] = -1;\n\t\t\t\t\tout->absmins[j>>1] = aplane->dist;\n\t\t\t\t\taplane->dist *= -1;\n\t\t\t\t}\n\t\t\t\toutside->plane = aplane++;\n\t\t\t}\n\t\t\telse\n\t\t\t\toutside->plane = prv->planes + LittleLong(inside->plane);\n\t\t\toutside->surface = &prv->surfaces[mat];\n\t\t}\n\t}\n\treturn true;\n}\n/*static qboolean CODBSP_LoadLeafBrushes (model_t *mod, qbyte *mod_base, lump_t *l)\n{\t//we don't really care about this, as we're using our BIH stuff for collisions instead.\n//\tcodbspinfo_t *prv = (codbspinfo_t*)mod->meshinfo;\n\tstruct\n\t{\n\t\tint a;\n\t} *in = (void*)((qbyte*)mod_base + l->fileofs);\n\tsize_t i, count = l->filelen / sizeof(*in);\n\tint highest=0;\n\tint lowest=0;\n\tif (l->filelen % sizeof(*in))\n\t{\n\t\tCon_Printf (CON_ERROR \"CODBSP_LoadLeafBrushes: funny lump size\\n\");\n\t\treturn false;\n\t}\n\tfor (i = 0; i < count; i++, in++)\n\t{\n\t\tif (lowest > in->a)\n\t\t\tlowest = in->a;\n\t\tif (highest < in->a)\n\t\t\thighest = in->a;\n//\t\tCon_Printf(\"%i: %i\\n\", (int)i, in->a);\n\t}\n\tCon_Printf(\"leaf brushes: %i - %i\\n\", lowest, highest);\n\treturn true;\n}*/\n\nstatic qboolean CODBSP_LoadPatchVertexes (model_t *mod, qbyte *mod_base, lump_t *l)\n{\t//for collision\n\tcodbspinfo_t *prv = (codbspinfo_t*)mod->meshinfo;\n\tconst vec3_t *in = (void*)((qbyte*)mod_base + l->fileofs);\n\tvecV_t *out;\n\tsize_t i, count = l->filelen / sizeof(*in);\n\tif (l->filelen % sizeof(*in))\n\t{\n\t\tCon_Printf (CON_ERROR \"CODBSP_LoadPatchVertexes: funny lump size\\n\");\n\t\treturn false;\n\t}\n\tprv->patchvertexes = out = plugfuncs->GMalloc(&mod->memgroup, count*sizeof(*out));\n\tprv->numpatchvertexes = count;\n\tfor (i = 0; i < count; i++)\n\t{\n\t\tout[i][0] = LittleFloat(in[i][0]);\n\t\tout[i][1] = LittleFloat(in[i][1]);\n\t\tout[i][2] = LittleFloat(in[i][2]);\n\t}\n\treturn true;\n}\nstatic qboolean CODBSP_LoadPatchIndexes (model_t *mod, qbyte *mod_base, lump_t *l)\n{\t//for collision\n\tcodbspinfo_t *prv = (codbspinfo_t*)mod->meshinfo;\n\tunsigned short *in = (void*)((qbyte*)mod_base + l->fileofs);\n\tindex_t *out;\n\tsize_t i, count = l->filelen / sizeof(*in);\n\tif (l->filelen % sizeof(*in))\n\t{\n\t\tCon_Printf (CON_ERROR \"CODBSP_LoadPatchIndexes: funny lump size\\n\");\n\t\treturn false;\n\t}\n\tprv->patchindexes = out = plugfuncs->GMalloc(&mod->memgroup, count*sizeof(*out));\n\tprv->numpatchindexes = count;\n\tfor (i = 0; i < count; i++)\n\t{\n\t\t*out++ = (unsigned short)LittleShort(*in++);\n\t}\n\treturn true;\n}\nstatic qboolean CODBSP_LoadPatchCollision (model_t *mod, qbyte *mod_base, lump_t *l)\n{\n\tcodbspinfo_t *prv = (codbspinfo_t*)mod->meshinfo;\n\tstruct codpatch_s *in = (void*)((qbyte*)mod_base + l->fileofs);\n\tsize_t i, count = l->filelen / sizeof(*in);\n\tif (l->filelen % sizeof(*in))\n\t{\n\t\tCon_Printf (CON_ERROR \"CODBSP_LoadPatchesCollision: funny lump size\\n\");\n\t\treturn false;\n\t}\n\tprv->patches = in;\n\tprv->numpatches = count;\n\tfor (i = 0; i < count; i++, in++)\n\t{\n\t\tif (in->mode==0)\n\t\t\t;//Con_Printf(\"p%i: %s ?+%i*%-4i v%i+%i %i\\n\", (int)i, mod->textures[in->mat]->name, in->mode0.w, in->mode0.h, in->mode0.firstvert,in->mode0.w*in->mode0.h, in->mode0.unknown);\n\t\telse if (in->mode==1)\n\t\t\t;//Con_Printf(\"s%i: %s %4i+%-4i v%i+%i\\n\", (int)i, mod->textures[in->mat]->name, in->mode1.firstidx,in->mode1.numidx, in->mode1.firstvert,in->mode1.numverts);\n\t\telse\n\t\t{\n\t\t\tCon_Printf(\"?%i: %s %i ?!?!?!?!?\\n\", (int)i, mod->textures[in->mat]->name, in->mode);\n\t\t\treturn false;\t//nope.\n\t\t}\n\t}\n\n\treturn true;\n}\nstatic qboolean CODBSP_LoadLeafPatches (model_t *mod, qbyte *mod_base, lump_t *l)\n{\t//for collision\n\tcodbspinfo_t *prv = (codbspinfo_t*)mod->meshinfo;\n\tunsigned int *in = (void*)((qbyte*)mod_base + l->fileofs);\n\tsize_t count = l->filelen / sizeof(*in);\n\tif (l->filelen % sizeof(*in))\n\t{\n\t\tCon_Printf (CON_ERROR \"CODBSP_LoadLeafPatches: funny lump size\\n\");\n\t\treturn false;\n\t}\n\tprv->leafpatches = in;\n\tprv->numleafpatches = count;\n\treturn true;\n}\n\n/*\nstatic qboolean CODBSP_LoadAABBs (model_t *mod, qbyte *mod_base, lump_t *l)\n{\n//\tcodbspinfo_t *prv = (codbspinfo_t*)mod->meshinfo;\n\tstruct\n\t{\n\t\tint a;\t//surfaceindex (submodel0 only, so stops short of the full range implied by the lump's count)\n\t\tint b;\t//numsurfaces\n\t\tint c;\t//some sort of offset?\n\t} *in = (void*)((qbyte*)mod_base + l->fileofs);\n\tsize_t i, count = l->filelen / sizeof(*in);\n\tif (l->filelen % sizeof(*in))\n\t{\n\t\tCon_Printf (CON_ERROR \"CODBSP_LoadAABBs: funny lump size\\n\");\n\t\treturn false;\n\t}\n\tfor (i = 0; i < count; i++, in++)\n\t{\n//\t\tCon_Printf(\"%i: %i+%i %i\\n\", (int)i, in->a, in->b, in->c);\n\t}\n\treturn true;\n}\nstatic qboolean CODBSP_LoadCells (model_t *mod, qbyte *mod_base, lump_t *l)\n{\n//\tcodbspinfo_t *prv = (codbspinfo_t*)mod->meshinfo;\n\tstruct\n\t{\n\t\tvec3_t mins;\n\t\tvec3_t maxs;\n\n\t\tint aabtree; //lump 16ish? CODLUMP_AABBTREES\n\t\tint firstportal;\t//lump 18? CODLUMP_PORTALS\n\t\tint numportals;\n\t\tint firstcullgroupindex;\t//lump 10? CODLUMP_CULLGROUPINDEXES\n\t\tint numcullgroupindexes;\n\t\tint firstoccluderindex;\n\t\tint numoccluders;\n\t} *in = (void*)((qbyte*)mod_base + l->fileofs);\n\tsize_t i, count = l->filelen / sizeof(*in);\n\tif (l->filelen % sizeof(*in))\n\t{\n\t\tCon_Printf (CON_ERROR \"CODBSP_LoadCells: funny lump size\\n\");\n\t\treturn false;\n\t}\n\tfor (i = 0; i < count; i++, in++)\n\t{\n\t\tCon_Printf(\"%i: [%f %f %f] [%f %f %f] %i %i+%i %i+%i %i+%i\\n\", (int)i,\n\t\t\tin->mins[0], in->mins[1], in->mins[2],\n\t\t\tin->maxs[0], in->maxs[1], in->maxs[2],\n\t\t\tin->aabtree, in->firstportal, in->numportals, in->firstcullgroupindex,\n\t\t\tin->numcullgroupindexes, in->firstoccluderindex, in->numoccluders);\n\t}\n\treturn true;\n}*/\n\nstatic qboolean CODBSP_LoadLeafs (model_t *mod, qbyte *mod_base, lump_t *l)\n{\n\tcodbspinfo_t\t*prv = (codbspinfo_t*)mod->meshinfo;\n\tstruct codinlinemodel_s{\n\t\tint cluster;\t//-1 for invalid\n\t\tint area;\t\t//-1 for invalid\n\t\tunsigned int firstleafsurfs;\n\t\tunsigned int numsurfaces;\n\t\tunsigned int firstleafbrushes;\n\t\tunsigned int numbrushes;\n\n\t\tint cell;\t\t//-1 for invalid\n\t\tunsigned int firstlightindex;\n\t\tunsigned int numlightindexes;\n\t} *in = (void*)((qbyte*)mod_base + l->fileofs);\n\tsize_t count = l->filelen / sizeof(*in);\n\tsize_t i;\n\tif (l->filelen % sizeof(*in) || count < 1)\n\t{\n\t\tCon_Printf (CON_ERROR \"CODBSP_LoadLeafs: funny lump size\\n\");\n\t\treturn false;\n\t}\n\tprv->leaf = plugfuncs->GMalloc(&mod->memgroup, count*sizeof(*prv->leaf));\n\tprv->numleafs = count;\n\tfor (i = 0; i < count; i++, in++)\n\t{\n\t\tprv->leaf[i].cluster = LittleLong(in->cluster);\n\t\tprv->leaf[i].area = LittleLong(in->area);\n\n\t\tprv->leaf[i].firstlightindex = LittleLong(in->firstlightindex);\n\t\tprv->leaf[i].numlightindexes = LittleLong(in->numlightindexes);\n\t}\n\treturn true;\n}\nstatic qboolean CODBSP_LoadNodes (model_t *mod, qbyte *mod_base, lump_t *l)\n{\n\tcodbspinfo_t\t*prv = (codbspinfo_t*)mod->meshinfo;\n\tstruct codinlinemodel_s{\n\t\tunsigned int plane;\n\t\tint child[2]; //negative for leaf\n\t\tivec3_t mins;\n\t\tivec3_t maxs;\n\t} *in = (void*)((qbyte*)mod_base + l->fileofs);\n\tsize_t count = l->filelen / sizeof(*in);\n\tsize_t i;\n\tif (l->filelen % sizeof(*in) || count < 1)\n\t{\n\t\tCon_Printf (CON_ERROR \"CODBSP_LoadNodes: funny lump size\\n\");\n\t\treturn false;\n\t}\n\tprv->nodes = plugfuncs->GMalloc(&mod->memgroup, count*sizeof(*prv->nodes));\n\tprv->numnodes = count;\n\tfor (i = 0; i < count; i++, in++)\n\t{\n\t\tprv->nodes[i].plane = &prv->planes[LittleLong(in->plane)];\n\t\tprv->nodes[i].childnum[0] = LittleLong(in->child[0]);\n\t\tprv->nodes[i].childnum[1] = LittleLong(in->child[1]);\n\n\t\tprv->nodes[i].mins[0] = LittleLong(in->mins[0]);\n\t\tprv->nodes[i].mins[1] = LittleLong(in->mins[1]);\n\t\tprv->nodes[i].mins[2] = LittleLong(in->mins[2]);\n\t\tprv->nodes[i].maxs[0] = LittleLong(in->maxs[0]);\n\t\tprv->nodes[i].maxs[1] = LittleLong(in->maxs[1]);\n\t\tprv->nodes[i].maxs[2] = LittleLong(in->maxs[2]);\n\t}\n\treturn true;\n}\nstatic qboolean CODBSP_LoadVisibility (model_t *mod, qbyte *mod_base, lump_t *l)\n{\n\tcodbspinfo_t\t*prv = (codbspinfo_t*)mod->meshinfo;\n\tqbyte *in = (void*)((qbyte*)mod_base + l->fileofs);\n\tif (!l->filelen)\n\t{\t//unvised.\n\t\tmod->numclusters = 0;\n\t\tmod->pvsbytes = 0;\n\t\tprv->pvsdata = NULL;\n\t\treturn true;\n\t}\n\tif (l->filelen < 8)\n\t{\n\t\tCon_Printf (CON_ERROR \"CODBSP_LoadVisibility: funny lump size\\n\");\n\t\treturn false;\n\t}\n\tmod->numclusters = LittleLong(((int*)in)[0]);\n\tmod->pvsbytes = LittleLong(((int*)in)[1]);\n\tif (l->filelen != 8 + mod->numclusters*mod->pvsbytes)\n\t{\n\t\tCon_Printf (CON_ERROR \"CODBSP_LoadVisibility: funny lump size\\n\");\n\t\treturn false;\n\t}\n\n\tprv->pvsdata = plugfuncs->GMalloc(&mod->memgroup, l->filelen-8);\n\tmemcpy(prv->pvsdata, in+8, mod->numclusters*mod->pvsbytes);\n\treturn true;\n}\n\nvoid AddPointToBounds (const vec3_t v, vec3_t mins, vec3_t maxs)\n{\n\tint\t\ti;\n\tvec_t\tval;\n\n\tfor (i=0 ; i<3 ; i++)\n\t{\n\t\tval = v[i];\n\t\tif (val < mins[i])\n\t\t\tmins[i] = val;\n\t\tif (val > maxs[i])\n\t\t\tmaxs[i] = val;\n\t}\n}\nstatic void CODBSP_BuildBIH (model_t *mod, size_t firstbrush, size_t numbrushes, size_t firstleafpatch, size_t numleafpatches)\n{\n\tcodbspinfo_t\t*prv = (codbspinfo_t*)mod->meshinfo;\n\tsize_t numtriangles = 0;\n\tsize_t numquads = 0;\n\n\tindex_t *silly;\n\tstruct bihleaf_s *bihleaf, *l;\n\tsize_t i, j, lp;\n\tqbyte *patches = memset(alloca((prv->numpatches+7)>>3), 0, (prv->numpatches+7)>>3);\n\tfor (i = firstleafpatch; i < numleafpatches; i++)\n\t{\t//de-dupe them... *sigh*\n\t\tlp = LittleLong(prv->leafpatches[i]);\n\t\tif (lp < prv->numpatches)\n\t\t\tpatches[lp>>3] |= 1u<<(lp&7);\n\t}\n\tfor (i = 0; i < prv->numpatches; i++)\n\t{\n\t\tif (patches[i>>3] & (1u<<(i&7)))\n\t\t{\n\t\t\tif (prv->patches[i].mode)\n\t\t\t\tnumtriangles+=(unsigned short)LittleShort(prv->patches[i].mode1.numidx)/3;\n\t\t\telse\n\t\t\t\tnumquads+=(unsigned int)((unsigned short)LittleShort(prv->patches[i].mode0.w)-1) * ((unsigned short)LittleShort(prv->patches[i].mode0.h)-1);\n\t\t}\n\t}\n\n\tbihleaf = l = plugfuncs->Malloc(sizeof(*bihleaf)*(numbrushes+numtriangles+numquads*2));\n\n\tsilly = plugfuncs->GMalloc(&mod->memgroup, sizeof(*silly)*6*numquads);\n\n\t//now we have enough storage, spit them out providing bounds info.\n\tfor (i = 0; i < prv->numpatches; i++)\n\t{\n\t\tif (patches[i>>3] & (1u<<(i&7)))\n\t\t{\n\t\t\tif (prv->patches[i].mode)\n\t\t\t{\n\t\t\t\tunsigned int numidx = (unsigned short)LittleShort(prv->patches[i].mode1.numidx);\n\t\t\t\tfor (j = 0; j < numidx; j+=3)\n\t\t\t\t{\n\t\t\t\t\tvec_t *v1,*v2,*v3;\n\t\t\t\t\tl->type = BIH_TRIANGLE;\n\t\t\t\t\tl->data.contents = prv->surfaces[LittleLong(prv->patches[i].mat)].c.value;\n\t\t\t\t\tl->data.tri.xyz = prv->patchvertexes + (unsigned int)LittleLong(prv->patches[i].mode1.firstvert);\n\t\t\t\t\tl->data.tri.indexes = prv->patchindexes + (unsigned int)LittleLong(prv->patches[i].mode1.firstidx) + j;\n\n\t\t\t\t\tv1 = l->data.tri.xyz[l->data.tri.indexes[0]];\n\t\t\t\t\tv2 = l->data.tri.xyz[l->data.tri.indexes[1]];\n\t\t\t\t\tv3 = l->data.tri.xyz[l->data.tri.indexes[2]];\n\n\t\t\t\t\tVectorCopy(v1, l->mins);\n\t\t\t\t\tVectorCopy(v1, l->maxs);\n\t\t\t\t\tAddPointToBounds(v2, l->mins, l->maxs);\n\t\t\t\t\tAddPointToBounds(v3, l->mins, l->maxs);\n\t\t\t\t\tl++;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tunsigned int w = (unsigned short)LittleShort(prv->patches[i].mode0.w);\n\t\t\t\tunsigned int h = (unsigned short)LittleShort(prv->patches[i].mode0.h);\n\t\t\t\tunsigned int x, y;\n\t\t\t\tfor (y = 0; y < h-1; y++)\n\t\t\t\tfor (x = 0; x < w-1; x++)\n\t\t\t\t{\n\t\t\t\t\tconst vec_t *v1,*v2,*v3;\n\n\t\t\t\t\tsilly[0] = x+y*w;\n\t\t\t\t\tsilly[1] = silly[0]+1;\n\t\t\t\t\tsilly[2] = silly[0]+w;\n\t\t\t\t\tsilly[3] = silly[1];\n\t\t\t\t\tsilly[4] = silly[1]+w;\n\t\t\t\t\tsilly[5] = silly[2];\n\n\t\t\t\t\tl->type = BIH_TRIANGLE;\n\t\t\t\t\tl->data.contents = FTECONTENTS_SOLID; //prv->surfaces[LittleLong(prv->patches[i].mat)].c.value;\n\t\t\t\t\tl->data.tri.xyz = prv->patchvertexes + (unsigned int)LittleLong(prv->patches[i].mode0.firstvert);\n\t\t\t\t\tl->data.tri.indexes = silly;\n\n\t\t\t\t\tv1 = l->data.tri.xyz[l->data.tri.indexes[0]];\n\t\t\t\t\tv2 = l->data.tri.xyz[l->data.tri.indexes[1]];\n\t\t\t\t\tv3 = l->data.tri.xyz[l->data.tri.indexes[2]];\n\n\t\t\t\t\tVectorCopy(v1, l->mins);\n\t\t\t\t\tVectorCopy(v1, l->maxs);\n\t\t\t\t\tAddPointToBounds(v2, l->mins, l->maxs);\n\t\t\t\t\tAddPointToBounds(v3, l->mins, l->maxs);\n\t\t\t\t\tl++;\n\t\t\t\t\tsilly+=3;\n\n\n\t\t\t\t\tl->type = BIH_TRIANGLE;\n\t\t\t\t\tl->data.contents = FTECONTENTS_SOLID; //prv->surfaces[LittleLong(prv->patches[i].mat)].c.value;\n\t\t\t\t\tl->data.tri.xyz = prv->patchvertexes + (unsigned int)LittleLong(prv->patches[i].mode0.firstvert);\n\t\t\t\t\tl->data.tri.indexes = silly;\n\n\t\t\t\t\tv1 = l->data.tri.xyz[l->data.tri.indexes[0]];\n\t\t\t\t\tv2 = l->data.tri.xyz[l->data.tri.indexes[1]];\n\t\t\t\t\tv3 = l->data.tri.xyz[l->data.tri.indexes[2]];\n\n\t\t\t\t\tVectorCopy(v1, l->mins);\n\t\t\t\t\tVectorCopy(v1, l->maxs);\n\t\t\t\t\tAddPointToBounds(v2, l->mins, l->maxs);\n\t\t\t\t\tAddPointToBounds(v3, l->mins, l->maxs);\n\t\t\t\t\tl++;\n\t\t\t\t\tsilly+=3;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t//now we have enough storage, spit them out providing bounds info.\n\tfor (i = 0; i < numbrushes; i++)\n\t{\n\t\tq2cbrush_t *b = &prv->brushes[firstbrush+i];\n\t\tl->type = BIH_BRUSH;\n\t\tl->data.brush = b;\n\n\t\tl->data.contents = b->contents;\n\t\tVectorCopy(b->absmins, l->mins);\n\t\tVectorCopy(b->absmaxs, l->maxs);\n\t\tl++;\n\t}\n\tmodfuncs->BIH_Build(mod, bihleaf, l-bihleaf);\n\tplugfuncs->Free(bihleaf);\n}\n\nstatic qboolean CODBSP_LoadInlineModels (model_t *wmod, qbyte *mod_base, lump_t *l)\n{\n//\tcodbspinfo_t\t*prv = (codbspinfo_t*)wmod->meshinfo;\n\tstruct codinlinemodel_s{\n\t\tvec3_t mins;\n\t\tvec3_t maxs;\n\t\tunsigned int firstsurf;\n\t\tunsigned int numsurfs;\n\t\tunsigned int firstleafpatch;\t//seems to match lump 23,\n\t\tunsigned int numleafpatches;\n\t\tunsigned int firstbrush;\n\t\tunsigned int numbrushes;\n\t} *in = (void*)((qbyte*)mod_base + l->fileofs);\n\tsize_t count = l->filelen / sizeof(*in);\n\tsize_t i, j;\n\tif (l->filelen % sizeof(*in) || count < 1)\n\t{\n\t\tCon_Printf (CON_ERROR \"CODBSP_LoadInlineModels: funny lump size\\n\");\n\t\treturn false;\n\t}\n\n\tfor (i = 0; i < count; i++, in++)\n\t{\n\t\tchar name[MAX_QPATH];\n\t\tmodel_t *mod;\n\t\tif (i)\n\t\t{\t//submodels\n\t\t\tQ_snprintfz (name, sizeof(name), \"*%u:%s\", (unsigned int)i, wmod->publicname);\n\t\t\tmod = modfuncs->BeginSubmodelLoad(name);\n\t\t\t*mod = *wmod;\n\t\t\tmod->archive = NULL;\n\t\t\tmod->entities_raw = NULL;\n\t\t\tmod->submodelof = wmod;\n\t\t\tQ_strlcpy(mod->publicname, name, sizeof(mod->publicname));\n\t\t\tQ_snprintfz (mod->name, sizeof(mod->name), \"*%u:%s\", (unsigned int)i, wmod->name);\n\t\t\tmemset(&mod->memgroup, 0, sizeof(mod->memgroup));\n\t\t}\n\t\telse\t//handle the world model here too\n\t\t\tmod = wmod;\n\n\t\tmod->hulls[0].firstclipnode = i?-1:0;\n\t\tfor (j = 1; j < countof(mod->hulls); j++)\n\t\t\tmod->hulls[j].firstclipnode = -1;\t//no nodes,\n\t\tmod->nodes = mod->rootnode = NULL;\n\t\tmod->leafs = NULL;\n\n\t\tmod->nummodelsurfaces = LittleLong(in->numsurfs);\n\t\tmod->firstmodelsurface = LittleLong(in->firstsurf);\n\n\t\tVectorCopy(in->mins, mod->mins);\n\t\tVectorCopy(in->maxs, mod->maxs);\n\t\tCODBSP_BuildBIH(mod, LittleLong(in->firstbrush), LittleLong(in->numbrushes), LittleLong(in->firstleafpatch), LittleLong(in->numleafpatches));\n\n\t\tmemset(&mod->batches, 0, sizeof(mod->batches));\n\t\tmod->vbos = NULL;\n\n#ifdef HAVE_CLIENT\n//\t\tmod->radius = RadiusFromBounds (mod->mins, mod->maxs);\n\n//\t\tif (qrenderer != QR_NONE)\n\t\t{\n\t\t\tbuilddata_t *bd = plugfuncs->Malloc(sizeof(*bd));\n\t\t\tbd->buildfunc = CODBSP_BuildSurfMesh;\n\t\t\tbd->paintlightmaps = false;\t//q3like with prebaked lightmaps.\n\t\t\tthreadfuncs->AddWork(WG_MAIN, CODBSP_GenerateMaterials, mod, bd, 0, 0);\n\t\t}\n#endif\n\n\t\tif (mod != wmod)\n\t\t\tmodfuncs->EndSubmodelLoad(mod, MLS_LOADED);\n\t}\n\treturn true;\n}\n\nstatic qboolean QDECL Mod_LoadCodBSP(struct model_s *mod, void *buffer, size_t fsize)\n{\n\tcodbspinfo_t\t*prv;\n\tqboolean okay = true;\n\tint i;\n\tint ver = LittleLong(((int*)buffer)[1]);\n\tlump_t lumps[max((int)COD1LUMP_COUNT, (int)COD2LUMP_COUNT)];\n\n\tmod->fromgame = fg_new;\n#ifdef HAVE_SERVER\n\tmod->funcs.FatPVS\t\t\t\t= CODBSP_FatPVS;\n\tmod->funcs.EdictInFatPVS\t\t= CODBSP_EdictInFatPVS;\n\tmod->funcs.FindTouchedLeafs\t\t= CODBSP_FindTouchedLeafs;\n#endif\n#ifdef HAVE_CLIENT\n\tmod->funcs.LightPointValues\t\t= CODBSP_LightPointValues;\n//\tmod->funcs.StainNode\t\t\t= CODBSP_StainNode;\n//\tmod->funcs.MarkLights\t\t\t= CODBSP_MarkLights;\n//\tmod->funcs.GenerateShadowMesh\t= CODBSP_GenerateShadowMesh;\n#endif\n\tmod->funcs.ClusterPVS\t\t\t= CODBSP_ClusterPVS;\n//\tmod->funcs.ClusterPHS\t\t\t= CODBSP_ClusterPHS;\n\tmod->funcs.ClusterForPoint\t\t= CODBSP_ClusterForPoint;\n//\tmod->funcs.SetAreaPortalState\t= CODBSP_SetAreaPortalState;\n//\tmod->funcs.AreasConnected\t\t= CODBSP_AreasConnected;\n//\tmod->funcs.LoadAreaPortalBlob\t= CODBSP_LoadAreaPortalBlob;\n//\tmod->funcs.SaveAreaPortalBlob\t= CODBSP_SaveAreaPortalBlob;\n\tmod->funcs.PrepareFrame\t\t\t= CODBSP_PrepareFrame;\n\tmod->funcs.InfoForPoint\t\t\t= CODBSP_InfoForPoint;\n\n\tif (ver == COD1BSP_VERSION)\n\t{\n\t\tmemcpy(lumps, (char*)buffer+8, sizeof(*lumps)*COD1LUMP_COUNT);\n\t\tfor (i = 0; i < COD1LUMP_COUNT; i++)\n\t\t{\n\t\t\tint ffs = lumps[i].filelen;\t//ffs\n\t\t\tlumps[i].filelen = LittleLong(lumps[i].fileofs);\n\t\t\tlumps[i].fileofs = LittleLong(ffs);\n\t\t\tif (lumps[i].filelen && lumps[i].fileofs+(size_t)lumps[i].filelen > fsize)\n\t\t\t{\n\t\t\t\tCon_Printf(CON_ERROR\"Truncated BSP file\\n\");\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\tmod->meshinfo = prv = plugfuncs->GMalloc(&mod->memgroup, sizeof(codbspinfo_t));\n\t\tprv->codbspver = ver;\n\n\t\t//basic trisoup info\n\t\tokay = okay && CODBSP_LoadShaders(mod, buffer, &lumps[COD1LUMP_MATERIALS]);\n\t\tokay = okay && COD1BSP_LoadLightmap(mod, buffer, &lumps[COD1LUMP_LIGHTMAPS]);\n\t\tokay = okay && COD1BSP_LoadSoupVertices(mod, buffer, &lumps[COD1LUMP_SOUPVERTS]);\n\t\tokay = okay && CODBSP_LoadSoupIndexes(mod, buffer, &lumps[COD1LUMP_SOUPINDEXES]);\n\t\tokay = okay && CODBSP_LoadSoups(mod, buffer, &lumps[COD1LUMP_SOUPS]);\n\n\t\t//gamecode needs to know what's around\n\t\tokay = okay && CODBSP_LoadLights(mod, buffer, &lumps[COD1LUMP_LIGHTS]);\n\t\tokay = okay && CODBSP_LoadLightIndexes(mod, buffer, &lumps[COD1LUMP_LIGHTINDEXES]);\n\t\tokay = okay && CODBSP_LoadEntities(mod, buffer, &lumps[COD1LUMP_ENTITIES]);\n\n\t\t//basic collision\n\t\tokay = okay && CODBSP_LoadPlanes(mod, buffer, &lumps[COD1LUMP_PLANES]);\n\t\tokay = okay && CODBSP_LoadBrushSides(mod, buffer, &lumps[COD1LUMP_BRUSHSIDES]);\n\t\tokay = okay && CODBSP_LoadBrushes(mod, buffer, &lumps[COD1LUMP_BRUSHES]);\n\t\t//okay = okay && CODBSP_LoadLeafBrushes(mod, buffer, &lumps[COD1LUMP_LEAFBRUSHES]);\n\n\t\t//patch collision...\n\t\tokay = okay && CODBSP_LoadPatchVertexes(mod, buffer, &lumps[COD1LUMP_COLLISIONVERTS]);\n\t\tokay = okay && CODBSP_LoadPatchIndexes(mod, buffer, &lumps[COD1LUMP_COLLISIONINDEXES]);\n\t\tokay = okay && CODBSP_LoadPatchCollision(mod, buffer, &lumps[COD1LUMP_PATCHCOLLISION]);\n\t\tokay = okay && CODBSP_LoadLeafPatches(mod, buffer, &lumps[COD1LUMP_LEAFPATCHES]);\n\n\t\t//seems like cod is a portal engine?\n\t\t//okay = okay && CODBSP_LoadAABBs(mod, buffer, &lumps[COD1LUMP_AABBTREES]);\n\t\t//okay = okay && CODBSP_LoadCells(mod, buffer, &lumps[COD1LUMP_CELLS]);\n\n\t\t//but we still have pvs with its clusters+areas that are node based (also required to determine which 'cell' we're inside).\n\t\tokay = okay && CODBSP_LoadVisibility(mod, buffer, &lumps[COD1LUMP_VISIBILITY]);\n\t\tokay = okay && CODBSP_LoadLeafs(mod, buffer, &lumps[COD1LUMP_LEAFS]);\n\t\tokay = okay && CODBSP_LoadNodes(mod, buffer, &lumps[COD1LUMP_NODES]);\n\n\t\tokay = okay && CODBSP_LoadInlineModels(mod, buffer, &lumps[COD1LUMP_MODELS]);\n\t}\n\telse if (ver == COD2BSP_VERSION)\n\t{\n\t\tmemcpy(lumps, (char*)buffer+8, sizeof(*lumps)*COD2LUMP_COUNT);\n\t\tfor (i = 0; i < COD2LUMP_COUNT; i++)\n\t\t{\n\t\t\tint ffs = lumps[i].filelen;\t//ffs\n\t\t\tlumps[i].filelen = LittleLong(lumps[i].fileofs);\n\t\t\tlumps[i].fileofs = LittleLong(ffs);\n\t\t\tif (lumps[i].filelen && lumps[i].fileofs+(size_t)lumps[i].filelen > fsize)\n\t\t\t{\n\t\t\t\tCon_Printf(CON_ERROR\"Truncated BSP file\\n\");\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\tmod->meshinfo = prv = plugfuncs->GMalloc(&mod->memgroup, sizeof(codbspinfo_t));\n\t\tprv->codbspver = ver;\n\n\t\t//basic trisoup info\n\t\tokay = okay && CODBSP_LoadShaders(mod, buffer, &lumps[COD2LUMP_MATERIALS]);\n\t\tokay = okay && COD2BSP_LoadLightmap(mod, buffer, &lumps[COD2LUMP_LIGHTMAPS]);\n\t\tokay = okay && COD2BSP_LoadSoupVertices(mod, buffer, &lumps[COD2LUMP_SOUPVERTS]);\n\t\tokay = okay && CODBSP_LoadSoupIndexes(mod, buffer, &lumps[COD2LUMP_SOUPINDEXES]);\n\t\tokay = okay && CODBSP_LoadSoups(mod, buffer, &lumps[COD2LUMP_SOUPS]);\n\n\t\t//gamecode needs to know what's around\n\t\tokay = okay && CODBSP_LoadEntities(mod, buffer, &lumps[COD2LUMP_ENTITIES]);\n\n\t\t//basic collision\n\t\tokay = okay && CODBSP_LoadPlanes(mod, buffer, &lumps[COD2LUMP_PLANES]);\n\t\tokay = okay && CODBSP_LoadBrushSides(mod, buffer, &lumps[COD2LUMP_BRUSHSIDES]);\n\t\tokay = okay && CODBSP_LoadBrushes(mod, buffer, &lumps[COD2LUMP_BRUSHES]);\n\t\t//okay = okay && CODBSP_LoadLeafBrushes(mod, buffer, &lumps[COD2LUMP_LEAFBRUSHES]);\n\n\t\t//patch collision...\n//\t\tokay = okay && CODBSP_LoadPatchVertexes(mod, buffer, &lumps[COD1LUMP_COLLISIONVERTS]);\n//\t\tokay = okay && CODBSP_LoadPatchIndexes(mod, buffer, &lumps[COD1LUMP_COLLISIONINDEXES]);\n//\t\tokay = okay && CODBSP_LoadPatchCollision(mod, buffer, &lumps[COD2LUMP_PATCHCOLLISION]);\n//\t\tokay = okay && CODBSP_LoadLeafPatches(mod, buffer, &lumps[COD2LUMP_LEAFPATCHES]);\n\n\t\t//seems like cod is a portal engine?\n\t\t//okay = okay && CODBSP_LoadAABBs(mod, buffer, &lumps[COD2LUMP_AABBTREES]);\n\t\t//okay = okay && CODBSP_LoadCells(mod, buffer, &lumps[COD2LUMP_CELLS]);\n\n\t\t//but we still have pvs with its clusters+areas that are node based (also required to determine which 'cell' we're inside).\n\t\tokay = okay && CODBSP_LoadVisibility(mod, buffer, &lumps[COD2LUMP_VISIBILITY]);\n\t\tokay = okay && CODBSP_LoadLeafs(mod, buffer, &lumps[COD2LUMP_LEAFS]);\n\t\tokay = okay && CODBSP_LoadNodes(mod, buffer, &lumps[COD2LUMP_NODES]);\n\n\t\tokay = okay && CODBSP_LoadInlineModels(mod, buffer, &lumps[COD2LUMP_MODELS]);\n\t}\n\telse\n\t{\n\t\tCon_Printf(CON_ERROR\"Bad COD Version...\\n\");\t//should have already been checked, so this ain't possible.\n\t\tokay = false;\n\t}\n\treturn okay;\n}\n\nqboolean CODBSP_Init(void)\n{\n\tfilefuncs = plugfuncs->GetEngineInterface(plugfsfuncs_name, sizeof(*filefuncs));\n\tmodfuncs = plugfuncs->GetEngineInterface(plugmodfuncs_name, sizeof(*modfuncs));\n\tthreadfuncs = plugfuncs->GetEngineInterface(plugthreadfuncs_name, sizeof(*threadfuncs));\n\tif (modfuncs && modfuncs->version != MODPLUGFUNCS_VERSION)\n\t\tmodfuncs = NULL;\n\n\tif (modfuncs && filefuncs && threadfuncs)\n\t{\n\t\tmodfuncs->RegisterModelFormatMagic(\"CoD(1) Maps\", \"IBSP\\x3b\\0\\0\\0\",8, Mod_LoadCodBSP);\n\t\tmodfuncs->RegisterModelFormatMagic(\"CoD2 Maps\", \"IBSP\\x04\\0\\0\\0\",8, Mod_LoadCodBSP);\n\t\treturn true;\n\t}\n\treturn false;\n}\n"
  },
  {
    "path": "plugins/cod/codiwi.c",
    "content": "#include \"../plugin.h\"\n\nstatic plugimagefuncs_t *imagefuncs;\nstatic struct pendingtextureinfo *Image_ReadIWIFile(unsigned int imgflags, const char *fname, qbyte *filedata, size_t filesize)\n{\n\tif (filesize >= 12 && filedata[0]=='I'&&filedata[1]=='W'&&filedata[2]=='i' && (filedata[3]==5/*cod2*/||filedata[3]==6/*cod4*/))\n\t{\n\t\tstatic const enum uploadfmt fmts[] = {PTI_INVALID/*0*/, PTI_RGBA8/*1*/, PTI_RGB8/*2*/, PTI_L8A8/*3, VALIDATE!*/, PTI_INVALID/*4,PTI_A8*/, PTI_INVALID/*5*/,PTI_INVALID/*6*/,PTI_INVALID/*7*/,PTI_INVALID/*8*/,PTI_INVALID/*9*/,PTI_INVALID/*10*/,PTI_BC1_RGBA/*11*/, PTI_BC2_RGBA/*12*/, PTI_BC3_RGBA/*13*/};\n\t\tenum uploadfmt fmt = PTI_INVALID;\n\t\tunsigned int bb,bw,bh,bd;\n\t\tunsigned int iw,ih,id, l;\n\t\tstruct pendingtextureinfo *mips;\n\t\tunsigned int offsets[4];\n\t\tqbyte *end = filedata+filesize;\n\t\tenum {\n\t\t\tIWI_STANDARD=0,\n\t\t\tIWI_MIPLESS=3,\n\t\t\tIWI_CUBEMAP=6,\n//\t\t\tIWI_NORMALMAP=0x20,\n//\t\t\tIWI_UNKNOWN=0x40,\n//\t\t\tIWI_UNKNOWN=0x80,\n\t\t} usage = filedata[5];\n\t\tif (filedata[4] < countof(fmts))\n\t\t\tfmt = fmts[filedata[4]];\n\t\tif (fmt == PTI_INVALID)\n\t\t{\t//bail. with warning\n\t\t\tCon_Printf(CON_WARNING\"Image_ReadIWIFile(%s): unsupported iwi pixelformat %x\\n\", fname, filedata[4]);\n\t\t\treturn NULL;\n\t\t}\n\t\tiw = filedata[6] | (filedata[7]<<8);\n\t\tih = filedata[8] | (filedata[9]<<8);\n\t\tid = filedata[10] | (filedata[11]<<8);\n\n\t\timagefuncs->BlockSizeForEncoding(fmt, &bb, &bw, &bh, &bd);\n\t\tif (!bb)\n\t\t{\n\t\t\tCon_Printf(CON_WARNING\"Image_ReadIWIFile(%s): unsupported fte pixelformat %x(%i)\\n\", fname, filedata[4], fmt);\n\t\t\treturn NULL;\n\t\t}\n\n\t\tmips = plugfuncs->Malloc(sizeof(*mips));\n\t\tmips->encoding = fmt;\n\t\tif ((filedata[5]&0xf) == IWI_CUBEMAP && id==1)\n\t\t{\n\t\t\tmips->type = PTI_CUBE;\n\t\t\tid *= 6;\n\t\t}\n\t\telse\n\t\t\tmips->type = (id>1)?PTI_3D:PTI_2D;\n\t\tmips->extrafree = filedata;\n\t\tfiledata += 12;\n\t\tfor (l = 0; l < countof(offsets); l++, filedata+=4)\n\t\t\toffsets[l] = filedata[0] | (filedata[1]<<8) | (filedata[2]<<16) | (filedata[3]<<24);\t//dunno what this 4 values are for. looks like descending ends?\n\t\tif (mips->type != PTI_2D || (usage&0xf)==IWI_MIPLESS)\n\t\t\tmips->mipcount = l = 1;\n\t\telse for (l = 0; l < countof(mips->mip); l++)\n\t\t{\n\t\t\tif ((iw >> l) || (ih >> l) || (id >> l))\n\t\t\t\tmips->mipcount++;\n\t\t\telse\n\t\t\t\tbreak;\n\t\t}\n\t\t//these are smallest to biggest.\n\t\twhile (l --> 0)\n\t\t{\n\t\t\tunsigned int w = iw>>l;\n\t\t\tunsigned int h = ih>>l;\n\t\t\tunsigned int d = (mips->type==PTI_3D)?id>>l:id;\n\t\t\tsize_t datasize;\n\t\t\tif (!w && !h && ((mips->type==PTI_3D)?!d:true))\n\t\t\t\tbreak;\n\t\t\tif (!w)\n\t\t\t\tw = 1;\n\t\t\tif (!h)\n\t\t\t\th = 1;\n\t\t\tif (!d)\n\t\t\t\td = 1;\n\t\t\tdatasize = ((w+bw-1)/bw) * ((h+bh-1)/bh) * ((d+bd-1)/bd) * bb;\n\n\t\t\tif (filedata + datasize > end)\n\t\t\t{\n\t\t\t\tCon_Printf(CON_WARNING\"%s: truncated\\n\", fname);\n\t\t\t\tCon_Printf(\"%s: %#x\\n\", fname, usage);\n\t\t\t\tplugfuncs->Free(mips);\n\t\t\t\treturn NULL;\t//doesn't fit...\n\t\t\t}\n\t\t\tmips->mip[l].width = w;\n\t\t\tmips->mip[l].height = h;\n\t\t\tmips->mip[l].depth = d;\n\t\t\tmips->mip[l].data = filedata;\n\t\t\tmips->mip[l].datasize = datasize;\n\t\t\tmips->mip[l].needfree = false;\n\t\t\tfiledata += datasize;\n\t\t}\n\t\tif (filedata != end)\n\t\t{\n\t\t\tCon_Printf(CON_WARNING\"%s: trailing data\\n\", fname);\n\t\t\tCon_Printf(\"%s: %#x\\n\", fname, usage);\n\t\t\tplugfuncs->Free(mips);\n\t\t\treturn NULL;\t//doesn't fit...\n\t\t}\n\t\treturn mips;\n\t}\n\treturn NULL;\n}\nstatic plugimageloaderfuncs_t iwifuncs =\n{\n\t\"InfinityWard Image\",\n\tsizeof(struct pendingtextureinfo),\n\tfalse,\n\tImage_ReadIWIFile,\n};\nqboolean IWI_Init(void)\n{\n\timagefuncs = plugfuncs->GetEngineInterface(plugimagefuncs_name, sizeof(*imagefuncs));\n\tif (!imagefuncs)\n\t\treturn false;\n\treturn plugfuncs->ExportInterface(plugimageloaderfuncs_name, &iwifuncs, sizeof(iwifuncs));\n}\n"
  },
  {
    "path": "plugins/cod/codmat.c",
    "content": "#include \"../plugin.h\"\n#include \"shader.h\"\ntypedef struct shaderparsestate_s parsestate_t;\nstatic plugfsfuncs_t *fsfuncs;\n\nstatic qboolean COD2_DecodeMaterial(parsestate_t *ps, const char *filename, void (*LoadMaterialString)(parsestate_t *ps, const char *script))\n{\n\tsize_t sz;\n\tqbyte *base = fsfuncs->LoadFile(va(\"materials/%s\", filename), &sz);\n\tstruct\n\t{\n\t\tunsigned int ofs_materialname;\t//usually matches filename. dunno why this needs to be here. for aliases? does the qbsp read this instead?\n\t\tunsigned int ofs_texturename;\t//simplification for tools? dunno.\n\t\tunsigned int z1;\n\t\tqbyte unk10;\n\t\tqbyte sort;\t//sort:\n\t\t\t\t\t//0x00: distortion\n\t\t\t\t\t//0x01: opaquewater\n\t\t\t\t\t//0x02: boathull\n\t\t\t\t\t//0x03: opaque\n\t\t\t\t\t//0x04: sky\n\t\t\t\t\t//0x04: skybox_sunmoon\n\t\t\t\t\t//0x06: skybox_clouds\n\t\t\t\t\t//0x08: decal_bottom1\n\t\t\t\t\t//0x09: decal_bottom2\n\t\t\t\t\t//0x0a: decal_bottom3\n\t\t\t\t\t//0x0b: decal_world\n\t\t\t\t\t//0x0c: decal_middle1\n\t\t\t\t\t//0x0d: decal_middle2\n\t\t\t\t\t//0x0e: decal_middle3\n\t\t\t\t\t//0x0f: decal_gunimpact\n\t\t\t\t\t//0x10: decal_top1\n\t\t\t\t\t//0x11: decal_top2\n\t\t\t\t\t//0x12: decal_top3\n\t\t\t\t\t//0x12: decal_multiplicative\n\t\t\t\t\t//0x14: banner\n\t\t\t\t\t//0x15: hair\n\t\t\t\t\t//0x16: underwater\n\t\t\t\t\t//0x17: transparentwater\n\t\t\t\t\t//0x18: corona\n\t\t\t\t\t//0x19: windowinside\n\t\t\t\t\t//0x1a: windowoutside\n\t\t\t\t\t//0x1b: blend\n\t\t\t\t\t//0x1c: viewmodel\n\t\tqbyte unk12;\n\t\tqbyte unk13;\n\t\tunsigned int z2;\n\t\tunsigned short unk2[2];\n\t\tunsigned int unk3;\n\t\tunsigned short width, height;\n\t\tunsigned int z3;\n\t\tunsigned int surfaceflags;\t//lower bits seem like they might be wrong. upper bits are consistentish with cod1 surfaceflags though.\n\t\tunsigned int contentbits;\t//pure guess. probably wrong.\n\t\tunsigned int blendbits;\t\t//0x00ff00ff bits are src/dst color/alpha blendfuncs matching the D3DBLEND enum.\n\t\tunsigned int unk7;\n\t\tunsigned int unk8;\n\t\tunsigned int ofs_program;\n\t\tunsigned int ofs_table; //0x44\n\t\tunsigned int ofs_table_end;\n\n\t\t//regarding the various unknowns, we can expect editor locale+usage (which are useless to us), blendfunc+alphafunc++cullface+depthtest+depthwrite+polygonoffset, maybe some tessellation settings and envmapping stuff.\n\n\t\tstruct\n\t\t{\n\t\t\tunsigned int ofs_sampler;\n\t\t\tunsigned int flags; //maybe? 0x200 for rgb, 0x300 for normals, 0x400 for spec...? there's probably clamping options here.\n\t\t\t\t\t\t\t\t//&7==0: default\n\t\t\t\t\t\t\t\t//&7==1: nearest... or trilinear?!?\n\t\t\t\t\t\t\t\t//&7==2: linear\n\t\t\t\t\t\t\t\t//&7==3: ???\n\t\t\t\t\t\t\t\t//&7==4: ???\n\t\t\t\t\t\t\t\t//&7==5: ???\n\t\t\t\t\t\t\t\t//&7==6: bilinear\n\t\t\t\t\t\t\t\t//&7==7: anisotropic\n\t\t\tunsigned int ofs_texname;\n\t\t} maps[1];\n\t} *header = (void*)base;\n\tunsigned int m;\n\tif (!base)\n\t\treturn false;\t//nope, bad filename\n\tif (header->ofs_table == 0x44)\t//make sure we know what it is...\n\t{\n\t\tsize_t ofs = 0;\n\t\tchar shad[8192];\n\t\tconst char *srcfac, *dstfac;\n\n\t\t//no initial { cos we're weird.\n\t\tQ_snprintf(shad+ofs,sizeof(shad)-ofs, \"\\n\"),ofs+=strlen(shad+ofs);\t//not actually useful to us. maybe for the hud so it doesn't have to wait for the image data too?\n\t\tQ_snprintf(shad+ofs,sizeof(shad)-ofs, \"\\t//material='%s'\\n\\t//tooltex='%s'\\n\", base+header->ofs_materialname, base+header->ofs_texturename),ofs+=strlen(shad+ofs);\n\t\tQ_snprintf(shad+ofs,sizeof(shad)-ofs, \"\\t//z1=%#x z2=%#x z3=%#x\\n\\t//unk1=%#x,%#x,%#x,%#x\\n\\t//unk2=%#x,%#x\\n\\t//unk3=%#x\\n\\t//surfaceflags=%#x\\n\\t//contentbits=%#x\\n\\t//unk6=%#x\\n\\t//unk7=%#x\\n\\t//unk8=%#x\\n\", header->z1, header->z2, header->z3, header->unk10,header->sort,header->unk12,header->unk13, header->unk2[0],header->unk2[1], header->unk3, header->surfaceflags, header->contentbits, header->blendbits, header->unk7, header->unk8),ofs+=strlen(shad+ofs);\n\t\tQ_snprintf(shad+ofs,sizeof(shad)-ofs, \"\\timagesize %i %i\\n\", header->width, header->height),ofs+=strlen(shad+ofs);\t//not actually useful to us. maybe for the hud so it doesn't have to wait for the image data too?\n\n\t\tfor (m = 0; m < (header->ofs_table_end-header->ofs_table)/sizeof(header->maps[0]); m++)\n\t\t{\n\t\t\tconst char *sampler = base+header->maps[m].ofs_sampler;\n\t\t\tif (!strcmp(sampler, \"colorMap\"))\n\t\t\t\tQ_snprintf(shad+ofs,sizeof(shad)-ofs, \"\\tdiffusemap images/%s.iwi //%#x\\n\", base+header->maps[m].ofs_texname, header->maps[m].flags),ofs+=strlen(shad+ofs);\n\t\t\telse if (!strcmp(sampler, \"normalMap\"))\n\t\t\t\tQ_snprintf(shad+ofs,sizeof(shad)-ofs, \"\\tnormalmap images/%s.iwi //%#x\\n\", base+header->maps[m].ofs_texname, header->maps[m].flags),ofs+=strlen(shad+ofs);\n\t\t\telse if (!strcmp(sampler, \"detailMap\"))\n\t\t\t\tQ_snprintf(shad+ofs,sizeof(shad)-ofs, \"\\tdisplacementmap images/%s.iwi //%#x\\n\", base+header->maps[m].ofs_texname, header->maps[m].flags),ofs+=strlen(shad+ofs);\t//hack. might as well use this one as detail.\n\t\t\telse if (!strcmp(sampler, \"specularMap\"))\n\t\t\t\tQ_snprintf(shad+ofs,sizeof(shad)-ofs, \"\\tspecularmap images/%s.iwi //%#x\\n\", base+header->maps[m].ofs_texname, header->maps[m].flags),ofs+=strlen(shad+ofs);\n\t\t\telse\n\t\t\t\tCon_Printf(\"\\t%s:%#x<-%s\\n\", base+header->maps[m].ofs_sampler, header->maps[m].flags, base+header->maps[m].ofs_texname);\n\t\t}\n\n\t\t//spit out a pass...\n\t\tQ_snprintf(shad+ofs,sizeof(shad)-ofs, \"\\t{\\n\"),ofs+=strlen(shad+ofs);\n\t\tQ_snprintf(shad+ofs,sizeof(shad)-ofs, \"\\t\\tprogram %s\\n\", base+header->ofs_program),ofs+=strlen(shad+ofs);\n\t\tQ_snprintf(shad+ofs,sizeof(shad)-ofs, \"\\t\\tmap $diffuse\\n\"),ofs+=strlen(shad+ofs);\t//just in case.\n\n\t\tswitch((header->blendbits>>0)&0xf)\n\t\t{\t//source factors\n\t\tdefault:\n\t\tcase 1: srcfac=\"zero\";\tbreak;\n\t\tcase 2: srcfac=\"one\";\tbreak;\n\t\tcase 3: srcfac=\"src_color\";\tbreak;\n\t\tcase 4: srcfac=\"one_minus_src_color\";\tbreak;\n\t\tcase 5: srcfac=\"src_alpha\";\tbreak;\n\t\tcase 6: srcfac=\"one_minus_src_alpha\";\tbreak;\n\t\tcase 7: srcfac=\"dst_alpha\";\tbreak;\n\t\tcase 8: srcfac=\"one_minus_dst_alpha\";\tbreak;\n\t\tcase 9: srcfac=\"dst_color\";\tbreak;\n\t\tcase 10: srcfac=\"one_minus_dst_color\";\tbreak;\n\t\tcase 11: srcfac=\"src_alpha_sat\";\tbreak;\n\t\tcase 12: srcfac=\"both_src_alpha\";\tbreak;\n\t\tcase 13: srcfac=\"both_one_minus_src_alpha\";\tbreak;\n\t\tcase 14: srcfac=\"blend_factor\";\tbreak;\n\t\tcase 15: srcfac=\"one_minus_blend_factor\";\tbreak;\n\t\t}\n\t\tswitch((header->blendbits>>4)&0xf)\n\t\t{\t//dest factors\n\t\tdefault:\n\t\tcase 1:  dstfac=\"zero\";\tbreak;\n\t\tcase 2:  dstfac=\"one\";\tbreak;\n\t\tcase 3:  dstfac=\"src_color\";\tbreak;\n\t\tcase 4:  dstfac=\"one_minus_src_color\";\tbreak;\n\t\tcase 5:  dstfac=\"src_alpha\";\tbreak;\n\t\tcase 6:  dstfac=\"one_minus_src_alpha\";\tbreak;\n\t\tcase 7:  dstfac=\"dst_alpha\";\tbreak;\n\t\tcase 8:  dstfac=\"one_minus_dst_alpha\";\tbreak;\n\t\tcase 9:  dstfac=\"dst_color\";\tbreak;\n\t\tcase 10: dstfac=\"one_minus_dst_color\";\tbreak;\n\t\tcase 11: dstfac=\"src_alpha_sat\";\tbreak;\n\t\tcase 12: dstfac=\"both_src_alpha\";\tbreak;\n\t\tcase 13: dstfac=\"both_one_minus_src_alpha\";\tbreak;\n\t\tcase 14: dstfac=\"blend_factor\";\tbreak;\n\t\tcase 15: dstfac=\"one_minus_blend_factor\";\tbreak;\n\t\t}\n\n\t\tQ_snprintf(shad+ofs,sizeof(shad)-ofs, \"\\t\\tblendfunc %s %s\\n\", srcfac, dstfac),ofs+=strlen(shad+ofs);\t//just in case.\n\n\t\tQ_snprintf(shad+ofs,sizeof(shad)-ofs, \"\\t}\\n\"),ofs+=strlen(shad+ofs);\n\n\t\tQ_snprintf(shad+ofs,sizeof(shad)-ofs, \"}\\n\");\n\t\tplugfuncs->Free(base);\n\t\tLoadMaterialString(ps, shad);\n\t\treturn true;\n\t}\n\telse\n\t\tCon_Printf(CON_WARNING\"%s doesn't seem to be a material? table=%#x..%#x\\n\", filename, header->ofs_table, header->ofs_table_end);\n\tplugfuncs->Free(base);\n\treturn false;\n}\n\nstatic qboolean SType_LoadShader(parsestate_t *ps, const char *filename, void (*LoadMaterialString)(parsestate_t *ps, const char *script))\n{\n\tchar stypefname[MAX_QPATH];\n\tchar *path;\n\tconst char *sep;\n\tconst char *at;\n\tsize_t pre;\n\tchar *base, *in, *out;\n\n\tsep = strrchr(filename, '/');\n\tif (!sep++)\n\t\treturn COD2_DecodeMaterial(ps, filename, LoadMaterialString);\n\tat = strrchr(sep, '@');\n\tif (!at)\n\t\treturn false;\t//nope, no shadertype specified. depend on the engine/loader/etc defaults.\n\n\tif (!strncmp(filename, \"skins/\", 5))\n\t\tpath = \"shadertypes/model/\";\n\telse if (!strncmp(filename, \"textures/\", 9))\n\t\tpath = \"shadertypes/world/\";\n\telse if (!strncmp(filename, \"gfx/\", 4))\n\t\tpath = \"shadertypes/2d/\";\n\telse\n\t\treturn false;\t//nope, not gonna try.\n\n\tpre = strlen(path);\n\tif (pre+(at-sep)+7 > sizeof(stypefname))\n\t\treturn false;\t//nope, too long...\n\tmemcpy(stypefname, path, pre);\n\tmemcpy(stypefname+pre, sep, at-sep);\n\tmemcpy(stypefname+pre+(at-sep), \".stype\", 7);\n\n\tin = out = base = fsfuncs->LoadFile(stypefname, &pre);\n\tif (!base)\n\t\treturn false;\t//nope, bad filename\n\t//yay we got something, but we need to fix up the $texturename strings to $diffuse because $reasons\n\twhile (*in)\n\t{\n\t\tif (*in == '$' && !strncmp(in, \"$texturename\", 12))\n\t\t{\n\t\t\tmemcpy(out, \"$diffuse\", 8);\n\t\t\tout += 8;\n\t\t\tin += 12;\n\t\t}\n\t\telse\n\t\t\t*out++ = *in++;\n\t}\n\t*out = 0;\n\n\t//now hand over the fteised shader script.\n\tin = base;\n\twhile(*in == ' ' || *in == '\\t' || *in == '\\n' || *in == '\\r')\n\t\tin++;\n\tif (*in == '{')\n\t\tin++;\n\tLoadMaterialString(ps, in);\n\tplugfuncs->Free(base);\t//done with it now.\n\treturn true;\n}\n\n/*static struct sbuiltin_s codprograms[] =\n{\n\t{QR_NONE}\n};*/\nstatic plugmaterialloaderfuncs_t stypefuncs =\n{\n\t\"Cod Shader Types\",\n\tSType_LoadShader,\n\n//\tcodprograms,\n};\nqboolean STypes_Init(void)\n{\n\tfsfuncs = plugfuncs->GetEngineInterface(plugfsfuncs_name, sizeof(*fsfuncs));\n\tif (!fsfuncs)\n\t\treturn false;\n\treturn plugfuncs->ExportInterface(plugmaterialloaderfuncs_name, &stypefuncs, sizeof(stypefuncs));\n}\n"
  },
  {
    "path": "plugins/cod/codmod.c",
    "content": "#include \"../plugin.h\"\n#include \"../engine/common/com_mesh.h\"\nstatic plugfsfuncs_t *filefuncs;\nstatic plugmodfuncs_t *modfuncs;\n\n//Utility functions. silly plugins.\nfloat Length(const vec3_t v) {return sqrt(DotProduct(v,v));}\nfloat RadiusFromBounds (const vec3_t mins, const vec3_t maxs)\n{\n\tint\t\ti;\n\tvec3_t\tcorner;\n\tfor (i=0 ; i<3 ; i++)\n\t\tcorner[i] = fabs(mins[i]) > fabs(maxs[i]) ? fabs(mins[i]) : fabs(maxs[i]);\n\treturn Length(corner);\n}\n\n\n\nstruct fstream_s\n{\n\tvoid *start;\n\tsize_t len;\n\tsize_t ofs;\n\n\tsize_t numbones;\n\tgaliasbone_t *bones;\n\tfloat *basepose;\n};\nstruct lod_s\n{\n\tfloat coverage;\n\tconst char *name;\n\tunsigned short numtex;\n\tconst char *tex[64];\n};\n\nstatic qboolean ReadEOF(struct fstream_s *f)\n{\n\treturn (f->ofs >= f->len);\n}\nstatic qbyte ReadByte(struct fstream_s *f)\n{\n\tif (f->ofs+1 > f->len)\n\t{\n\t\tf->ofs++;\n\t\treturn 0;\n\t}\n\treturn ((qbyte*)f->start)[f->ofs++];\n}\nstatic const char *ReadBytes(struct fstream_s *f, size_t len)\n{\n\tf->ofs+=len;\n\tif (f->ofs > f->len)\n\t\treturn NULL;\n\treturn &((const char*)f->start)[f->ofs-len];\n}\nstatic const char *ReadString(struct fstream_s *f)\n{\n\tsize_t len;\n\tfor (len = 0; f->ofs+len < f->len && ((qbyte*)f->start)[f->ofs+len]; len++)\n\t\t;\n\tlen++; //for the null\n\tf->ofs += len;\n\treturn &((const qbyte*)f->start)[f->ofs-len];\n}\nstatic unsigned short ReadUInt16(struct fstream_s *f)\n{\n\tunsigned short r;\n\tr = ReadByte(f);\n\tr|= ReadByte(f) << 8;\n\treturn r;\n}\nstatic short ReadSInt16(struct fstream_s *f)\n{\n\treturn (signed short)ReadUInt16(f);\n}\nstatic unsigned int ReadUInt32(struct fstream_s *f)\n{\n\tunsigned int r;\n\tr = ReadByte(f);\n\tr|= ReadByte(f) << 8;\n\tr|= ReadByte(f) << 16;\n\tr|= ReadByte(f) << 24;\n\treturn r;\n}\nstatic float ReadFloat(struct fstream_s *f)\n{\n\tunion {\n\t\tunsigned int u;\n\t\tfloat f;\n\t} r;\n\tr.u = ReadByte(f);\n\tr.u|= ReadByte(f) << 8;\n\tr.u|= ReadByte(f) << 16;\n\tr.u|= ReadByte(f) << 24;\n\treturn r.f;\n}\nstatic qboolean Mod_XModel_LoadPart (struct model_s *mod, struct fstream_s *f)\n{\n\tunsigned short ver = ReadUInt16(f);\n\tunsigned short b, nboner = ReadUInt16(f);\n\tunsigned short nbonea = ReadUInt16(f);\n\tfloat rel[12];\n\tswitch(ver)\n\t{\n\tcase 0x0e:\n\t\tbreak;\n\tcase 0x14:\n\t\tbreak;\n\tdefault:\n\t\tCon_Printf(CON_ERROR\"%s: Unknown version %#x\\n\", mod->name, ver);\n\t\treturn false;\n\t}\n//\tCon_Printf(\"%s: version %x rb:%i ab:%i\\n\", mod->name, ver, nboner, nbonea);\n\tf->numbones = nbonea+nboner;\n\tf->basepose = plugfuncs->GMalloc(&mod->memgroup, sizeof(*f->basepose)*12 * f->numbones);\n\tf->bones = plugfuncs->GMalloc(&mod->memgroup, sizeof(*f->bones) * f->numbones);\n\n\tfor (b = 0; b < nbonea; b++)\n\t{\t//root bones, with identity position. for some reason.\n\t\tf->bones[b].parent = -1;\n\n\t\tVectorClear(f->bones[b].ref.org);\n\t\tVector4Set(f->bones[b].ref.quat, 0, 0, 0, 1);\n\t\tVectorSet(f->bones[b].ref.scale, 1, 1, 1);\n\t\tmodfuncs->GenMatrixPosQuat4Scale(f->bones[b].ref.org, f->bones[b].ref.quat, f->bones[b].ref.scale, f->basepose + b*12);\n\t}\n\tfor (; b < f->numbones; b++)\n\t{\n\t\tf->bones[b].parent = ReadByte(f);\n\n\t\tif (f->bones[b].parent >= b)\n\t\t\tCon_Printf(CON_ERROR\"b%i (%s) has parent %i\\n\", b, f->bones[b].name, f->bones[b].parent);\n\n\t\tf->bones[b].ref.org[0] = ReadFloat(f);\n\t\tf->bones[b].ref.org[1] = ReadFloat(f);\n\t\tf->bones[b].ref.org[2] = ReadFloat(f);\n\n\t\tf->bones[b].ref.quat[0] = ReadSInt16(f)/32767.0f;\n\t\tf->bones[b].ref.quat[1] = ReadSInt16(f)/32767.0f;\n\t\tf->bones[b].ref.quat[2] = ReadSInt16(f)/32767.0f;\n\t\tf->bones[b].ref.quat[3] = 1.0-DotProduct(f->bones[b].ref.quat,f->bones[b].ref.quat);\t//reconstruct the w part.\n\t\tif (f->bones[b].ref.quat[3]>0)\n\t\t\tf->bones[b].ref.quat[3] = sqrt(f->bones[b].ref.quat[3]);\n\t\telse\n\t\t\tf->bones[b].ref.quat[3] = 0;\n\n\t\tVectorSet(f->bones[b].ref.scale, 1, 1, 1);\n\n\t\tmodfuncs->GenMatrixPosQuat4Scale(f->bones[b].ref.org, f->bones[b].ref.quat, f->bones[b].ref.scale, rel);\n\t\tmodfuncs->ConcatTransforms((void*)(f->basepose + f->bones[b].parent*12), (void*)rel, (void*)(f->basepose + b*12));\n\n//\t\tCon_Printf(\"b%i: p:%i, [%f %f %f] [%f %f %f %f]\\n\", b, f->bones[b].parent, pos[0],pos[1],pos[2], quat[0],quat[1],quat[2],quat[3]);\n\t}\n\tfor (b = 0; b < f->numbones; b++)\n\t{\n\t\tvec3_t mins, maxs;\n\t\tconst char *n = ReadString(f);\n\t\tif (ver >= 0x14)\n\t\t{\t//omitted.\n\t\t\tVectorClear(mins);\n\t\t\tVectorClear(maxs);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tmins[0] = ReadFloat(f);\n\t\t\tmins[1] = ReadFloat(f);\n\t\t\tmins[2] = ReadFloat(f);\n\t\t\tmaxs[0] = ReadFloat(f);\n\t\t\tmaxs[1] = ReadFloat(f);\n\t\t\tmaxs[2] = ReadFloat(f);\n\t\t}\n\n\t\t//hack... I assume the (game)code does a skel_setbone so this doesn't actually matter?\n\t\tif (!strcmp(n, \"torso_stabilizer\"))\n\t\t\tVector4Set(f->bones[b].ref.quat, 0,0,0,1);\n\n\t\tQ_strlcpy(f->bones[b].name, n, sizeof(f->bones[b].name));\n\t\tmodfuncs->M3x4_Invert(f->basepose+12*b, f->bones[b].inverse);\n\n//\t\tif (ver >= 0x14)\n//\t\t\tCon_Printf(\"b%i: %-20s\\n\", b, n);\n//\t\telse\n//\t\t\tCon_Printf(\"b%i: %-20s [%f %f %f] [%f %f %f]\\n\", b, n, mins[0], mins[1], mins[2], maxs[0], maxs[1], maxs[2]);\n\t}\n\tfor (b = 0; b < f->numbones; b++)\n\t\tf->bones[b].group = ReadByte(f);\t//presumed.\n\treturn true;\n}\nstatic qboolean Mod_XModel_LoadSurfs (struct model_s *mod, struct fstream_s *f, struct lod_s *lod, float mincov)\n{\n\tgaliasinfo_t *surf;\n\tgaliasskin_t *skins;\n\tskinframe_t *skinframe;\n\tunsigned short ver = ReadUInt16(f);\n\tunsigned short n, nsurfs = ReadUInt16(f);\n\tswitch(ver)\n\t{\n\tcase 0x0e:\n\t\tbreak;\n\tcase 0x14:\n\t\tbreak;\n\tdefault:\n\t\tCon_Printf(\"%s: Unknown version %#x\\n\", mod->name, ver);\n\t\treturn false;\n\t}\n\n\tif (!nsurfs)\n\t\treturn true;\t//nothing to do...\n\n\tsurf = plugfuncs->GMalloc(&mod->memgroup,\tsizeof(*surf)*nsurfs +\n\t\t\t\t\t\t\t\t\t\t\t\tsizeof(*skins)*nsurfs +\n\t\t\t\t\t\t\t\t\t\t\t\tsizeof(*skinframe)*nsurfs);\n\tskins = (galiasskin_t*)(surf+nsurfs);\n\tskinframe = (skinframe_t*)(skins+nsurfs);\n\tfor (n = 0; n < nsurfs; n++)\n\t{\n\t\tsurf[n].numbones = f->numbones;\n\t\tsurf[n].ofsbones = f->bones;\n\t\tsurf[n].baseframeofs = f->basepose;\n\n\t\tsurf[n].mindist = mincov;\n\t\tsurf[n].maxdist = lod->coverage;\n\t\tsurf[n].shares_verts = n;\n\t\tsurf[n].shares_bones = 0;\n\t\tsurf[n].nextsurf = &surf[n+1];\n\t\tsurf[n].ofsskins = &skins[n];\n\t\tsurf[n].numskins = 1;\n\t\tskins[n].skinspeed = 0.1;\n\t\tskins[n].frame = &skinframe[n];\n\t\tskins[n].numframes = 1;\n\t\tQ_snprintf(surf[n].surfacename, sizeof(surf[n].surfacename), \"%s/%i\", lod->name, n);\n\t\tQ_strlcpy(skins[n].name, lod->name, sizeof(skins[n].name));\n\t\tif (n < lod->numtex)\n\t\t\tQ_snprintf(skinframe[n].shadername, sizeof(skinframe[n].shadername), (ver==0x0e)?\"skins/%s\":\"%s\", lod->tex[n]);\n\t}\n\tsurf[nsurfs-1].nextsurf = mod->meshinfo;\n\tmod->meshinfo = surf;\n\tfor(surf = surf[nsurfs-1].nextsurf; surf; surf = surf->nextsurf)\n\t\tsurf->shares_verts += nsurfs;\n\tsurf = mod->meshinfo;\n\n\tfor (n = 0; n < nsurfs; n++, surf++)\n\t{\n\t\tint flags = ReadByte(f);\n\t\tint v, nverts = ReadUInt16(f);\n\t\tint t, ntris = ReadUInt16(f);\n\t\tint r, runs = (ver==0xe)?ReadUInt16(f):0;\n\t\tunsigned short wcount = 0;\n\t\tqbyte run;\n\t\tunsigned int idx1, idx2, idx3;\n\n\t\tunsigned short boneidx = ReadUInt16(f);\n\t\tqboolean boney = boneidx == (unsigned short)~0u;\n\t\tint exweights = 0;\n\t\tunsigned short *vw = NULL;\n//\t\tunsigned short bunk2;\n\n\t\tvec3_t norm;\n\t\tvec4_t xyzw;\n\t\tconst float *matrix;\n\n\t\t(void)flags;\n\n\t\tif (boney)\n\t\t{\n\t\t\tif (ver == 0x0e)\n\t\t\t\texweights = ReadUInt16(f);\n\t\t\t/*bunk2 =*/ ReadUInt16(f); //seems to be vaguely related to vert/triangle counts.\n\t\t}\n\n\t\tsurf->numverts = nverts;\n\n\t\tif (ver == 0xe)\n\t\t{\t//cod1\n\t\t\tsurf->ofs_indexes\t\t= plugfuncs->GMalloc(&mod->memgroup, sizeof(*surf->ofs_indexes\t\t) * ntris*3);\n\t\t\tfor(t = 0, r = 0; r < runs; r++)\n\t\t\t{\n\t\t\t\trun = ReadByte(f);\n\t\t\t\tidx1 = ReadUInt16(f);\n\t\t\t\tidx2 = ReadUInt16(f);\n\t\t\t\trun-=2;\n\n\t\t\t\tfor (;;)\n\t\t\t\t{\t//strip\n\t\t\t\t\tif(!run--) break;\n\t\t\t\t\tidx3 = ReadUInt16(f);\n\t\t\t\t\tif (idx1 != idx2 && idx1 != idx3 && idx2 != idx3 && t < ntris)\n\t\t\t\t\t{\n\t\t\t\t\t\tsurf->ofs_indexes[t*3+0] = idx1;\n\t\t\t\t\t\tsurf->ofs_indexes[t*3+1] = idx2;\n\t\t\t\t\t\tsurf->ofs_indexes[t*3+2] = idx3;\n\t\t\t\t\t\tt++;\n\t\t\t\t\t}\n\t\t\t\t\tidx1 = idx2;\n\t\t\t\t\tidx2 = idx3;\n\n\t\t\t\t\t//alternating triangles flip the order\n\t\t\t\t\tif(!run--) break;\n\t\t\t\t\tidx3 = ReadUInt16(f);\n\t\t\t\t\tif (idx1 != idx2 && idx1 != idx3 && idx2 != idx3 && t < ntris)\n\t\t\t\t\t{\n\t\t\t\t\t\tsurf->ofs_indexes[t*3+2] = idx1;\n\t\t\t\t\t\tsurf->ofs_indexes[t*3+1] = idx2;\n\t\t\t\t\t\tsurf->ofs_indexes[t*3+0] = idx3;\n\t\t\t\t\t\tt++;\n\t\t\t\t\t}\n\t\t\t\t\tidx1 = idx2;\n\t\t\t\t\tidx2 = idx3;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (ntris != t)\n\t\t\t{\n\t\t\t\tCon_Printf(CON_ERROR\"Expected %i tris, got %i from %i runs\\n\", ntris, t, r);\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tsurf->numindexes = t*3;\n\n\t\t\t//lazy and a bit slower.\n\t\t\tsurf->ofs_st_array\t\t= plugfuncs->GMalloc(&mod->memgroup, sizeof(*surf->ofs_st_array\t\t) * surf->numverts);\n\t\t\tsurf->ofs_skel_xyz\t\t= plugfuncs->GMalloc(&mod->memgroup, sizeof(*surf->ofs_skel_xyz\t\t) * surf->numverts);\n\t\t\tsurf->ofs_skel_norm\t\t= plugfuncs->GMalloc(&mod->memgroup, sizeof(*surf->ofs_skel_norm\t) * surf->numverts);\n\t\t\tif (boney || f->numbones>0)\n\t\t\t{\n\t\t\t\tsurf->ofs_skel_idx\t\t= plugfuncs->GMalloc(&mod->memgroup, sizeof(*surf->ofs_skel_idx\t\t) * surf->numverts);\n\t\t\t\tsurf->ofs_skel_weight\t= plugfuncs->GMalloc(&mod->memgroup, sizeof(*surf->ofs_skel_weight\t) * surf->numverts);\n\t\t\t\tvw = memset(alloca(sizeof(*vw)*nverts), 0, sizeof(*vw)*nverts);\n\t\t\t}\n\t\t\tboneidx = min(boneidx,f->numbones-1);\n\n\t\t\tfor (v = 0; v < nverts; v++)\n\t\t\t{\n\t\t\t\tnorm[0]\t= ReadFloat(f);\n\t\t\t\tnorm[1]\t= ReadFloat(f);\n\t\t\t\tnorm[2]\t= ReadFloat(f);\n\t\t\t\tsurf->ofs_st_array[v][0]\t= ReadFloat(f);\n\t\t\t\tsurf->ofs_st_array[v][1]\t= ReadFloat(f);\n\t\t\t\tif (boney)\n\t\t\t\t{\n\t\t\t\t\twcount = ReadUInt16(f);\n\t\t\t\t\tboneidx = ReadUInt16(f);\n\t\t\t\t\tboneidx = min(boneidx,f->numbones-1);\n\t\t\t\t}\n\n\t\t\t\txyzw[0]\t= ReadFloat(f);\n\t\t\t\txyzw[1]\t= ReadFloat(f);\n\t\t\t\txyzw[2]\t= ReadFloat(f);\n\t\t\t\tif (wcount)\n\t\t\t\t{\n\t\t\t\t\txyzw[3] = ReadFloat(f);\n\t\t\t\t\tVectorScale(xyzw, xyzw[3], xyzw);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\txyzw[3]\t= 1;\n\n\t\t\t\tif (surf->ofs_skel_idx)\n\t\t\t\t{\n\t\t\t\t\tvw[v] = wcount;\t//urgh.\n\t\t\t\t\tsurf->ofs_skel_idx[v][0] = surf->ofs_skel_idx[v][1] = surf->ofs_skel_idx[v][2] = surf->ofs_skel_idx[v][3] = boneidx;\t//set all of them, cache might thank us.\n\t\t\t\t\tsurf->ofs_skel_weight[v][0] = xyzw[3];\n\t\t\t\t}\n\n\t\t\t\t//calculate the correct position in the base pose\n\t\t\t\tmatrix = f->basepose + boneidx*12;\n\t\t\t\tsurf->ofs_skel_xyz[ v][0] = xyzw[0] * matrix[0] + xyzw[1] * matrix[1] + xyzw[2] * matrix[ 2] + xyzw[3] * matrix[ 3];\n\t\t\t\tsurf->ofs_skel_xyz[ v][1] = xyzw[0] * matrix[4] + xyzw[1] * matrix[5] + xyzw[2] * matrix[ 6] + xyzw[3] * matrix[ 7];\n\t\t\t\tsurf->ofs_skel_xyz[ v][2] = xyzw[0] * matrix[8] + xyzw[1] * matrix[9] + xyzw[2] * matrix[10] + xyzw[3] * matrix[11];\n\t\t\t\tsurf->ofs_skel_norm[v][0] = norm[0] * matrix[0] + norm[1] * matrix[1] + norm[2] * matrix[ 2];\n\t\t\t\tsurf->ofs_skel_norm[v][1] = norm[0] * matrix[4] + norm[1] * matrix[5] + norm[2] * matrix[ 6];\n\t\t\t\tsurf->ofs_skel_norm[v][2] = norm[0] * matrix[8] + norm[1] * matrix[9] + norm[2] * matrix[10];\n\t\t\t}\n\t\t\tif (vw)\n\t\t\t{\n\t\t\t\tfloat lowestv;\n\t\t\t\tint lowesti, j;\n\t\t\t\tfor (v = 0; v < nverts; v++)\n\t\t\t\t{\n\t\t\t\t\tfor (; vw[v] > 0; vw[v]--)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (--exweights < 0)\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tboneidx = ReadUInt16(f);\n\t\t\t\t\t\tboneidx = min(boneidx,f->numbones-1);\n\t\t\t\t\t\txyzw[0]\t= ReadFloat(f);\n\t\t\t\t\t\txyzw[1]\t= ReadFloat(f);\n\t\t\t\t\t\txyzw[2]\t= ReadFloat(f);\n\t\t\t\t\t\txyzw[3] = ReadFloat(f);\n\t\t\t\t\t\tVectorScale(xyzw, xyzw[3], xyzw);\n\n\t\t\t\t\t\tmatrix = f->basepose + boneidx*12;\n\t\t\t\t\t\tsurf->ofs_skel_xyz[ v][0] += xyzw[0] * matrix[0] + xyzw[1] * matrix[1] + xyzw[2] * matrix[ 2] + xyzw[3] * matrix[ 3];\n\t\t\t\t\t\tsurf->ofs_skel_xyz[ v][1] += xyzw[0] * matrix[4] + xyzw[1] * matrix[5] + xyzw[2] * matrix[ 6] + xyzw[3] * matrix[ 7];\n\t\t\t\t\t\tsurf->ofs_skel_xyz[ v][2] += xyzw[0] * matrix[8] + xyzw[1] * matrix[9] + xyzw[2] * matrix[10] + xyzw[3] * matrix[11];\n\n\t\t\t\t\t\tlowesti = 0;\n\t\t\t\t\t\tlowestv = surf->ofs_skel_weight[v][0];\n\t\t\t\t\t\tfor (j = 1; j < countof(surf->ofs_skel_idx[v]); j++)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (surf->ofs_skel_weight[v][j] < lowestv)\n\t\t\t\t\t\t\t\tlowestv = surf->ofs_skel_weight[v][lowesti=j];\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (lowestv < xyzw[3])\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tsurf->ofs_skel_idx[v][lowesti] = boneidx;\n\t\t\t\t\t\t\tsurf->ofs_skel_weight[v][lowesti] = xyzw[3];\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t//compensate for any missing weights\n\t\t\t\t\txyzw[3] = surf->ofs_skel_weight[v][0]+surf->ofs_skel_weight[v][1]+surf->ofs_skel_weight[v][2]+surf->ofs_skel_weight[v][3];\n\t\t\t\t\tif (xyzw[3]>0)\n\t\t\t\t\t\tVector4Scale(surf->ofs_skel_weight[v], 1/xyzw[3], surf->ofs_skel_weight[v]);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (exweights)\n\t\t\t{\t//something was misread\n\t\t\t\tsurf->numverts = 0;\n\t\t\t\tsurf->numindexes = 0;\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\t//compute the tangents that are not stored in the file.\n\t\t\tsurf->ofs_skel_svect\t= plugfuncs->GMalloc(&mod->memgroup, sizeof(*surf->ofs_skel_svect\t) * surf->numverts);\n\t\t\tsurf->ofs_skel_tvect\t= plugfuncs->GMalloc(&mod->memgroup, sizeof(*surf->ofs_skel_tvect\t) * surf->numverts);\n\t\t\tmodfuncs->AccumulateTextureVectors(surf->ofs_skel_xyz, surf->ofs_st_array, surf->ofs_skel_norm, surf->ofs_skel_svect, surf->ofs_skel_tvect, surf->ofs_indexes, surf->numindexes, false);\n\t\t\tmodfuncs->NormaliseTextureVectors(surf->ofs_skel_norm, surf->ofs_skel_svect, surf->ofs_skel_tvect, surf->numverts, false);\n\t\t}\n\t\telse if (ver == 0x14)\n\t\t{\t//cod2\n\t\t\tsurf->ofs_st_array\t\t= plugfuncs->GMalloc(&mod->memgroup, sizeof(*surf->ofs_st_array\t\t) * surf->numverts);\n\t\t\tsurf->ofs_skel_xyz\t\t= plugfuncs->GMalloc(&mod->memgroup, sizeof(*surf->ofs_skel_xyz\t\t) * surf->numverts);\n\t\t\tsurf->ofs_skel_norm\t\t= plugfuncs->GMalloc(&mod->memgroup, sizeof(*surf->ofs_skel_norm\t) * surf->numverts);\n\t\t\tsurf->ofs_skel_svect\t= plugfuncs->GMalloc(&mod->memgroup, sizeof(*surf->ofs_skel_svect\t) * surf->numverts);\n\t\t\tsurf->ofs_skel_tvect\t= plugfuncs->GMalloc(&mod->memgroup, sizeof(*surf->ofs_skel_tvect\t) * surf->numverts);\n\t\t\tsurf->ofs_rgbaub\t\t= plugfuncs->GMalloc(&mod->memgroup, sizeof(*surf->ofs_rgbaub\t\t) * surf->numverts);\n\n\t\t\tif (boney || f->numbones)\n\t\t\t{\n\t\t\t\tsurf->ofs_skel_idx\t\t= plugfuncs->GMalloc(&mod->memgroup, sizeof(*surf->ofs_skel_idx\t\t) * surf->numverts);\n\t\t\t\tsurf->ofs_skel_weight\t= plugfuncs->GMalloc(&mod->memgroup, sizeof(*surf->ofs_skel_weight\t) * surf->numverts);\n\t\t\t}\n\t\t\tboneidx = min(boneidx,f->numbones-1);\n\n\t\t\tfor (v = 0; v < nverts; v++)\n\t\t\t{\n\t\t\t\tsurf->ofs_skel_norm[v][0]\t= ReadFloat(f);\n\t\t\t\tsurf->ofs_skel_norm[v][1]\t= ReadFloat(f);\n\t\t\t\tsurf->ofs_skel_norm[v][2]\t= ReadFloat(f);\n\t\t\t\tsurf->ofs_rgbaub[v][0]\t\t= ReadByte(f);\n\t\t\t\tsurf->ofs_rgbaub[v][1]\t\t= ReadByte(f);\n\t\t\t\tsurf->ofs_rgbaub[v][2]\t\t= ReadByte(f);\n\t\t\t\tsurf->ofs_rgbaub[v][3]\t\t= ReadByte(f);\n\t\t\t\tsurf->ofs_st_array[v][0]\t= ReadFloat(f);\n\t\t\t\tsurf->ofs_st_array[v][1]\t= ReadFloat(f);\n\t\t\t\tsurf->ofs_skel_svect[v][0]\t= ReadFloat(f);\n\t\t\t\tsurf->ofs_skel_svect[v][1]\t= ReadFloat(f);\n\t\t\t\tsurf->ofs_skel_svect[v][2]\t= ReadFloat(f);\n\t\t\t\tsurf->ofs_skel_tvect[v][0]\t= ReadFloat(f);\n\t\t\t\tsurf->ofs_skel_tvect[v][1]\t= ReadFloat(f);\n\t\t\t\tsurf->ofs_skel_tvect[v][2]\t= ReadFloat(f);\n\t\t\t\tif (boney)\n\t\t\t\t{\n\t\t\t\t\tint w;\n\t\t\t\t\twcount = ReadByte(f);\n\t\t\t\t\tboneidx = ReadUInt16(f);\n\t\t\t\t\tboneidx = min(boneidx,f->numbones-1);\n\t\t\t\t\txyzw[0]\t= ReadFloat(f);\n\t\t\t\t\txyzw[1]\t= ReadFloat(f);\n\t\t\t\t\txyzw[2]\t= ReadFloat(f);\n\t\t\t\t\txyzw[3] = wcount?ReadByte(f)/255.f:1;\n\t\t\t\t\tVectorScale(xyzw, xyzw[3], xyzw);\n\t\t\t\t\tmatrix = f->basepose + boneidx*12;\n\t\t\t\t\tsurf->ofs_skel_xyz[ v][0] = xyzw[0] * matrix[0] + xyzw[1] * matrix[1] + xyzw[2] * matrix[ 2] + xyzw[3] * matrix[ 3];\n\t\t\t\t\tsurf->ofs_skel_xyz[ v][1] = xyzw[0] * matrix[4] + xyzw[1] * matrix[5] + xyzw[2] * matrix[ 6] + xyzw[3] * matrix[ 7];\n\t\t\t\t\tsurf->ofs_skel_xyz[ v][2] = xyzw[0] * matrix[8] + xyzw[1] * matrix[9] + xyzw[2] * matrix[10] + xyzw[3] * matrix[11];\n\n\t\t\t\t\tsurf->ofs_skel_idx[v][0] = surf->ofs_skel_idx[v][1] = surf->ofs_skel_idx[v][2] = surf->ofs_skel_idx[v][3] = boneidx;\n\t\t\t\t\tsurf->ofs_skel_weight[v][0] = xyzw[3]; surf->ofs_skel_weight[v][1] = surf->ofs_skel_weight[v][2] = surf->ofs_skel_weight[v][3] = 0;\n\n\t\t\t\t\tfor (w = 1; w <= wcount; w++)\n\t\t\t\t\t{\n\t\t\t\t\t\tboneidx = ReadUInt16(f);\n\t\t\t\t\t\tboneidx = min(boneidx,f->numbones-1);\n\t\t\t\t\t\txyzw[0] = ReadFloat(f);\n\t\t\t\t\t\txyzw[1] = ReadFloat(f);\n\t\t\t\t\t\txyzw[2] = ReadFloat(f);\n\t\t\t\t\t\txyzw[3] = ReadUInt16(f)/65535.f;\n\t\t\t\t\t\tVectorScale(xyzw, xyzw[3], xyzw);\n\t\t\t\t\t\tmatrix = f->basepose + boneidx*12;\n\t\t\t\t\t\tsurf->ofs_skel_xyz[ v][0] += xyzw[0] * matrix[0] + xyzw[1] * matrix[1] + xyzw[2] * matrix[ 2] + xyzw[3] * matrix[ 3];\n\t\t\t\t\t\tsurf->ofs_skel_xyz[ v][1] += xyzw[0] * matrix[4] + xyzw[1] * matrix[5] + xyzw[2] * matrix[ 6] + xyzw[3] * matrix[ 7];\n\t\t\t\t\t\tsurf->ofs_skel_xyz[ v][2] += xyzw[0] * matrix[8] + xyzw[1] * matrix[9] + xyzw[2] * matrix[10] + xyzw[3] * matrix[11];\n\t\t\t\t\t\tif (w < 4)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tsurf->ofs_skel_idx[v][w] = boneidx;\n\t\t\t\t\t\t\tsurf->ofs_skel_weight[v][w] = xyzw[3];\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t//compensate for any missing weights\n\t\t\t\t\txyzw[3] = surf->ofs_skel_weight[v][0]+surf->ofs_skel_weight[v][1]+surf->ofs_skel_weight[v][2]+surf->ofs_skel_weight[v][3];\n\t\t\t\t\tif (xyzw[3]>0&&xyzw[3]!=1)\n\t\t\t\t\t\tVector4Scale(surf->ofs_skel_weight[v], 1/xyzw[3], surf->ofs_skel_weight[v]);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tsurf->ofs_skel_xyz[v][0]\t= ReadFloat(f);\n\t\t\t\t\tsurf->ofs_skel_xyz[v][1]\t= ReadFloat(f);\n\t\t\t\t\tsurf->ofs_skel_xyz[v][2]\t= ReadFloat(f);\n\n\t\t\t\t\tif (surf->ofs_skel_idx)\n\t\t\t\t\t{\n\t\t\t\t\t\tsurf->ofs_skel_idx[v][0] = surf->ofs_skel_idx[v][1] = surf->ofs_skel_idx[v][2] = surf->ofs_skel_idx[v][3] = boneidx;\n\t\t\t\t\t\tsurf->ofs_skel_weight[v][0] = 1; surf->ofs_skel_weight[v][1] = surf->ofs_skel_weight[v][2] = surf->ofs_skel_weight[v][3] = 0;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t//indexes moved to AFTER. also triangles instead of weird strips. much nicer.\n\t\t\tsurf->numindexes = ntris*3;\n\t\t\tsurf->ofs_indexes = plugfuncs->GMalloc(&mod->memgroup, sizeof(*surf->ofs_indexes\t\t) * surf->numindexes);\n\t\t\tfor (v = 0; v < ntris*3; v++)\n\t\t\t\tsurf->ofs_indexes[v] = ReadUInt16(f);\n\t\t}\n\t}\n\treturn true;\n}\n\nstruct codbone_s\n{\t//source data is the number of samples, the pose index for each sample and THEN the actual samples at the specified sparse poses.\n//\tconst char *name;\n\tunsigned int numquats;\n\tqboolean flip;\n\tstruct\n\t{\n\t\tunsigned short ts;\n\t\tsigned short v[3];\n\t} *quats;\n\tunsigned int numcoords;\n\tstruct\n\t{\n\t\tunsigned int ts;\n\t\tvec3_t v;\n\t} *coord;\n};\n\nstatic void Mod_XAnim_LoadQuats (struct model_s *mod, struct fstream_s *f, struct codbone_s *b, unsigned int maxts, qboolean flipquat, qboolean zonly)\n{\n\tunsigned int i, n = ReadUInt16(f);\n\tb->flip = flipquat;\n\tif (n)\n\t{\n\t\tb->numquats = n;\n\t\tb->quats = plugfuncs->GMalloc(&mod->memgroup, sizeof(*b->quats)*n);\n\t\tif (n == 1 || n == maxts)\n\t\t\tfor (i = 0; i < n; i++)\n\t\t\t\tb->quats[i].ts = i;\n\t\telse if (maxts > 0xff)\n\t\t\tfor (i = 0; i < n; i++)\n\t\t\t\tb->quats[i].ts = ReadUInt16(f);\n\t\telse\n\t\t\tfor (i = 0; i < n; i++)\n\t\t\t\tb->quats[i].ts = ReadByte(f);\n\n\t\tfor (i = 0; i < n; i++)\n\t\t{\n\t\t\tb->quats[i].v[0] = zonly?0:ReadSInt16(f);\n\t\t\tb->quats[i].v[1] = zonly?0:ReadSInt16(f);\n\t\t\tb->quats[i].v[2] =         ReadSInt16(f);\n\t\t}\n\t}\n}\nstatic void Mod_XAnim_LoadCoords (struct model_s *mod, struct fstream_s *f, struct codbone_s *b, unsigned int maxts)\n{\n\tunsigned int i, n = ReadUInt16(f);\n\tif (n)\n\t{\n\t\tb->numcoords = n;\n\t\tb->coord = plugfuncs->GMalloc(&mod->memgroup, sizeof(*b->coord)*n);\n\t\tif (n == 1 || n == maxts)\n\t\t\tfor (i = 0; i < n; i++)\n\t\t\t\tb->coord[i].ts = i;\n\t\telse if (maxts > 0xff)\n\t\t\tfor (i = 0; i < n; i++)\n\t\t\t\tb->coord[i].ts = ReadUInt16(f);\n\t\telse\n\t\t\tfor (i = 0; i < n; i++)\n\t\t\t\tb->coord[i].ts = ReadByte(f);\n\n\t\tfor (i = 0; i < n; i++)\n\t\t{\n\t\t\tb->coord[i].v[0] = ReadFloat(f);\n\t\t\tb->coord[i].v[1] = ReadFloat(f);\n\t\t\tb->coord[i].v[2] = ReadFloat(f);\n\t\t}\n\t}\n}\nstatic float *QDECL Mod_XAnim_GetRawBones(const struct galiasinfo_s *animmesh, const struct galiasanimation_s *a, float time, float *bonematrixstorage, const struct galiasbone_s *boneinf, int numbones)\n{\n\tconst struct codbone_s *bone = (const void*)(a+1);\n\tsize_t b, i, j;\n\tint ts;\n\tfloat frac;\n\tgaliasrefpose_t ref;\n\ttime *= a->rate;\n\tts = time;\n\tfrac = time - ts;\t//FIXME: negative time!\n\tif (a->loop)\n\t{\n\t\tts %= a->numposes;\n\t\tif (ts < 0)\n\t\t\tts += a->numposes;\n\t}\n\tts = bound(0, ts, a->numposes);\n\tif (!boneinf)\n\t{\t//our own bones?!? that'll be a horrible mess, but if you're really asking for that...\n\t\tboneinf = animmesh->ofsbones;\n\t\tnumbones = min(numbones,animmesh->numbones);\n\t}\n\n\tfor (b = 0; b < numbones; b++, boneinf++)\n\t{\n\t\tref = boneinf->ref;\n\t\tfor (j = 0; j < animmesh->numbones; j++)\n\t\t{\n\t\t\tif (!strcmp(boneinf->name, animmesh->ofsbones[j].name))\n\t\t\t{\t//okay this is the one we want. replace the reference pose with the animated data.\n\t\t\t\tbone = (const struct codbone_s*)(a+1) + j;\n\t\t\t\tfor (i = 0; i < bone->numquats; i++)\n\t\t\t\t\tif (ts < bone->quats[i].ts)\n\t\t\t\t\t\tbreak;\t//we flew too close to the sun!\n\t\t\t\tif (!i--)\n\t\t\t\t\t;\t//use the value from the model.\n\t\t\t\telse if (i < bone->numquats-1 && bone->quats[i+1].ts == ts+1)\n\t\t\t\t{\n\t\t\t\t\tvec4_t oq,nq;\n\t\t\t\t\tVectorScale(bone->quats[i].v, 1.f/32767, oq);\n\t\t\t\t\toq[3] = 1 - DotProduct(oq,oq);\n\t\t\t\t\tif (oq[3] > 0)\n\t\t\t\t\t\toq[3] = sqrt(oq[3]);\n\n\t\t\t\t\tVectorScale(bone->quats[i+1].v, 1.f/32767, nq);\n\t\t\t\t\tnq[3] = 1 - DotProduct(nq,nq);\n\t\t\t\t\tif (nq[3] > 0)\n\t\t\t\t\t\tnq[3] = sqrt(nq[3]);\n\n\t\t\t\t\tmodfuncs->QuaternionSlerp(oq, nq, frac, ref.quat);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tVectorScale(bone->quats[i].v, 1.f/32767, ref.quat);\n\t\t\t\t\tref.quat[3] = 1 - DotProduct(ref.quat,ref.quat);\n\t\t\t\t\tif (ref.quat[3] > 0)\n\t\t\t\t\t\tref.quat[3] = sqrt(ref.quat[3]);\n\t\t\t\t}\n\n\t\t\t\tfor (i = 0; i < bone->numcoords; i++)\n\t\t\t\t\tif (ts < bone->coord[i].ts)\n\t\t\t\t\t\tbreak;\n\t\t\t\tif (!i--)\n\t\t\t\t\t;\t//use the value from the model.\n\t\t\t\telse if (i < bone->numcoords-1 && bone->coord[i+1].ts == ts+1)\n\t\t\t\t\tVectorInterpolate(bone->coord[i].v, frac, bone->coord[i+1].v, ref.org);\n\t\t\t\telse\n\t\t\t\t\tVectorCopy(bone->coord[i].v, ref.org);\n\n\t\t\t\tbreak;\t//don't look for others.\n\t\t\t}\n\t\t}\n\t\tmodfuncs->GenMatrixPosQuat4Scale(ref.org, ref.quat, ref.scale, bonematrixstorage + b*12);\n\t}\n\treturn bonematrixstorage;\n}\nstatic int Mod_XAnim_CompareEvents (const void *av, const void *bv)\n{\n\tconst galiasevent_t *a=av;\n\tconst galiasevent_t *b=bv;\n\treturn b->timestamp-a->timestamp;\n}\nstatic qboolean Mod_XAnim_Load (struct model_s *mod, void *buffer, size_t fsize)\n{\n\tstruct fstream_s f = {buffer, fsize};\n\tunsigned short ver = ReadUInt16(&f);\n\tunsigned short numposes = ReadUInt16(&f);\n\tunsigned short numabones=0, numrbones = ReadUInt16(&f);\n\tunsigned char flags = ReadByte(&f);\n\tunsigned short framerate = ReadUInt16(&f);\n\tunsigned int i;\n\tconst qbyte *flip, *tiny;\n\tstruct codbone_s *bone;\n\tgaliasinfo_t *surf;\n\tgaliasanimation_t *anim;\n\tgaliasbone_t *gbones;\n\tint numev;\n\tswitch(ver)\n\t{\n\tcase 0x0e:\t//cod1\n\t\tbreak;\n\tcase 0x14:\n\t\tbreak;\n\tdefault:\n\t\tCon_Printf(CON_ERROR\"%s: unknown version %x\\n\", mod->name, ver);\n\t\treturn false;\n\t}\n//\tCon_Printf(CON_DEBUG\"Poses:%i bones:%i flags:%i rate:%i\\n\", numposes, numrbones, flags, framerate);\n\n\tif (flags & 2)\n\t\tnumabones = 1;\n\n\tmod->type = mod_alias;\n\tmod->fromgame = fg_new;\n\tmod->meshinfo = surf = plugfuncs->GMalloc(&mod->memgroup, sizeof(*surf) + sizeof(*anim) + sizeof(*bone)*(numabones+numrbones));\n\tsurf->numbones = numabones+numrbones;\n\tsurf->ofsbones = gbones = plugfuncs->GMalloc(&mod->memgroup, sizeof(*gbones)*(numabones+numrbones));\n\n\tsurf->numanimations = 1;\n\tsurf->ofsanimations = anim = (void*)(surf+1);\n\n\tanim->loop = !!(flags&1);\n\tanim->numposes = numposes;\n\tanim->skeltype = SKEL_RELATIVE;\t//no parents info stored here, so these are screwy unless you're importing them into a model that DOES provide the parents info for you.\n\tbone = (void*)(anim+1);\n\tanim->rate = framerate;\n\tQ_strlcpy(anim->name, mod->name, sizeof(anim->name));\n\tanim->GetRawBones = Mod_XAnim_GetRawBones;\n\n\tfor (i = 0; i < numabones; i++)\n\t{\n\t\tgbones[i].parent = -1;\n\t\tVectorClear(gbones[i].ref.org);\n\t\tVector4Set(gbones[i].ref.quat,0,0,0,1);\n\t\tVectorSet(gbones[i].ref.scale,1,1,1);\n\t\tQ_strlcpy(gbones[i].name, \"tag_origin\", sizeof(gbones[i].name));\n\n\t\tMod_XAnim_LoadQuats(mod, &f, &bone[i], numposes, false, true);\n\t\tMod_XAnim_LoadCoords(mod, &f, &bone[i], numposes);\n\t}\n\tflip = ReadBytes(&f, (numrbones+7)>>3);\n\ttiny = ReadBytes(&f, (numrbones+7)>>3);\n\tif (anim->loop)\n\t\tnumposes++;\t//is this the right place?\n\tfor (i = 0; i < numrbones; i++)\n\t{\n\t\tconst char *n = ReadString(&f);\n\t\tgbones[numabones+i].parent = -1;\t//no information. oh noes.\n\t\tVectorClear(gbones[i].ref.org);\n\t\tVector4Set(gbones[i].ref.quat,0,0,0,1);\n\t\tVectorSet(gbones[i].ref.scale,1,1,1);\n\t\tQ_strlcpy(gbones[numabones+i].name, n, sizeof(gbones[numabones+i].name));\n//\t\tCon_Printf(CON_DEBUG\"Part %i = %s (%i %i)\\n\", numabones+i, bone[numabones+i].name, !!(flip[i>>3]&(1u<<(i&7))), !!(tiny[i>>3]&(1u<<(i&7))));\n\t}\n\tfor (i = 0; i < numrbones; i++)\n\t{\n//\t\tCon_Printf(CON_WARNING\"%s:\\n\", gbones[numabones+i].name);\n\t\tMod_XAnim_LoadQuats(mod, &f, &bone[numabones+i], numposes, !!(flip[i>>3]&(1u<<(i&7))), !!(tiny[i>>3]&(1u<<(i&7))));\n\t\tMod_XAnim_LoadCoords(mod, &f, &bone[numabones+i], numposes);\n\t}\n\tif (anim->loop)\n\t\tnumposes--;\n\n\tnumev = ReadByte(&f);\n\tif (numev)\n\t{\n\t\tanim->events = plugfuncs->GMalloc(&mod->memgroup, sizeof(*anim->events)*(numev));\n\t\tfor (i = 0; i < numev; i++)\n\t\t{\n\t\t\tconst char *txt = ReadString(&f);\n\t\t\tanim->events[i].code = 0;\t//this format doesn't provide event ids, just pure strings.\n\t\t\tanim->events[i].data = strcpy(plugfuncs->GMalloc(&mod->memgroup, strlen(txt)+1), txt);\n\n\t\t\tanim->events[i].timestamp = ReadUInt16(&f) / (float)numposes;\n\t\t}\n\t\tqsort(anim->events, numev, sizeof(*anim->events), Mod_XAnim_CompareEvents);\t//make sure they're sorted by timestamps.\n\t\tfor (i = 0; i < numev-1; i++)\n\t\t\tanim->events[i].next = &anim->events[i+1];\n\t}\n\n\tif (f.ofs != f.len)\n\t\tCon_Printf(CON_WARNING\"Misread %s (%u bytes of %u)\\n\", mod->name, (unsigned)f.ofs, (unsigned)f.len);\n\treturn true;\n}\nqboolean QDECL Mod_XModel_Load (struct model_s *mod, void *buffer, size_t fsize)\n{\n\tstruct fstream_s f = {buffer, fsize};\n\tstruct fstream_s pf = {NULL,0};\n\tunsigned short ver;\n\tunsigned i;\n\tunsigned int clod, clodnsurf;\n\tunsigned short t;\n\tstruct lod_s lod[4];\n\tfloat mincov = 0;\n\tint nlod;\n\n\t//I fucking hate this.\n\tif (!strncmp(mod->publicname, \"xanim/\", 6))\t//anims are not linked in any way, we treat them as separate precachable models so that gamecode can selectively load them.\n\t\treturn Mod_XAnim_Load(mod, buffer, fsize);\n\tif (!strncmp(mod->publicname, \"xmodelparts/\", 12))\t//loaded as part of models. don't get confused.\n\t\treturn false;\n\tif (!strncmp(mod->publicname, \"xmodelsurfs/\", 12))\t//loaded as part of models. don't get confused.\n\t\treturn false;\n\t//\"xmodels/\" is okay though!\n\n\tver = ReadUInt16(&f);\n\tswitch(ver)\n\t{\n\tcase 0x0e:\t//cod1\n\t\tnlod = 3;\n\t\tbreak;\n\tcase 0x14:\t//cod2\n\t\t/*type =*/ ReadByte(&f);\n\t\tnlod = 4;\n\t\tbreak;\n\tdefault:\n\t\tCon_Printf(CON_ERROR\"%s: Unknown version %x\\n\", mod->name, ver);\n\t\treturn false;\n\t}\n\tmod->mins[0] = ReadFloat(&f);\n\tmod->mins[1] = ReadFloat(&f);\n\tmod->mins[2] = ReadFloat(&f);\n\tmod->maxs[0] = ReadFloat(&f);\n\tmod->maxs[1] = ReadFloat(&f);\n\tmod->maxs[2] = ReadFloat(&f);\n\tfor (i = 0; i < nlod; i++)\n\t{\n\t\tlod[i].coverage = ReadFloat(&f);\t//coverage\n\t\tlod[i].name = ReadString(&f);\n\t}\n\tclod = ReadUInt32(&f);\n\tclodnsurf = ReadUInt32(&f);\n\t(void)clod;\n\tfor (i = 0; i < clodnsurf && !ReadEOF(&f); i++)\n\t{\n\t\tunsigned int t, ntris = ReadUInt32(&f);\n\t\tfor (t = 0; t < ntris && !ReadEOF(&f); t++)\n\t\t{\n\t\t\tvec4_t p, sd, td;\n\t\t\t//plane\n\t\t\tp[0] = ReadFloat(&f);\n\t\t\tp[1] = ReadFloat(&f);\n\t\t\tp[2] = ReadFloat(&f);\n\t\t\tp[3] = ReadFloat(&f);\n\n\t\t\t//?splane?\n\t\t\tsd[0] = ReadFloat(&f);\n\t\t\tsd[1] = ReadFloat(&f);\n\t\t\tsd[2] = ReadFloat(&f);\n\t\t\tsd[3] = ReadFloat(&f);\n\n\t\t\t//?tplane?\n\t\t\ttd[0] = ReadFloat(&f);\n\t\t\ttd[1] = ReadFloat(&f);\n\t\t\ttd[2] = ReadFloat(&f);\n\t\t\ttd[3] = ReadFloat(&f);\n\n\t\t\t(void)p;\n\t\t\t(void)sd;\n\t\t\t(void)td;\n\n//\t\t\tCon_Printf(\"%i: %f %f %f %f : %f %f %f %f : %f %f %f %f\\n\", t, p[0],p[1],p[2],p[3], sd[0],sd[1],sd[2],sd[3], td[0],td[1],td[2],td[3]);\n\t\t\t//dunno how to use this for collision... a triangle has 3 side faces AND a front!\n\t\t}\n\t\t//bounds\n\t\tReadFloat(&f);\n\t\tReadFloat(&f);\n\t\tReadFloat(&f);\n\t\tReadFloat(&f);\n\t\tReadFloat(&f);\n\t\tReadFloat(&f);\n\t\t/*boneidx = */ ReadUInt32(&f);\n\t\t/*contents = */ ReadUInt32(&f);\n\t\t/*surfaceflags = */ ReadUInt32(&f);\n\t}\n\n\tpf.ofs = 0;\n\tpf.start = filefuncs->LoadFile(va(\"xmodelparts/%s\", lod[0].name), &pf.len);\n\tif (pf.start)\n\t{\n\t\tif (!ReadEOF(&pf) && Mod_XModel_LoadPart(mod, &pf))\n\t\t{\n\t\t\tif (pf.ofs != pf.len)\n\t\t\t\tCon_Printf(CON_WARNING\"Misread xmodelparts/%s (%u bytes of %u)\\n\", lod[0].name, (unsigned)f.ofs, (unsigned)f.len);\n\t\t}\n\t\telse\n\t\t\treturn false;\n\t\tplugfuncs->Free(pf.start);\n\t}\n\n\tfor (i = 0; i < nlod; i++)\n\t{\n\t\tif (!*lod[i].name)\n\t\t\tbreak;\n\t\tlod[i].numtex = ReadUInt16(&f);\n\t\tfor (t = 0; t < lod[i].numtex; t++)\n\t\t\tlod[i].tex[t] = ReadString(&f);\n\t\tpf.ofs = 0;\n\t\tpf.start = filefuncs->LoadFile(va(\"xmodelsurfs/%s\", lod[i].name), &pf.len);\n\t\tif (pf.start)\n\t\t{\n\t\t\tif (!ReadEOF(&pf) && Mod_XModel_LoadSurfs(mod, &pf, &lod[i], mincov))\n\t\t\t{\n\t\t\t\tif (pf.ofs != pf.len)\n\t\t\t\t\tCon_Printf(CON_WARNING\"Misread xmodelsurfs/%s (%u bytes of %u)\\n\", lod[i].name, (unsigned)f.ofs, (unsigned)f.len);\n\t\t\t}\n\t\t\telse\n\t\t\t\treturn false;\n\t\t\tmincov = lod[i].coverage;\n\t\t\tplugfuncs->Free(pf.start);\n\t\t}\n\t}\n\n\n\tmod->type = mod_alias;\n\tmod->fromgame = fg_new;\n\tmod->radius = RadiusFromBounds(mod->mins, mod->maxs);\n//\tif (mod->meshinfo)\n//\t\tmodfuncs->BIH_BuildAlias(mod, mod->meshinfo);\n\treturn !!mod->meshinfo;\n}\n\nstatic qboolean XMODEL_Init(void)\n{\n\tfilefuncs = plugfuncs->GetEngineInterface(plugfsfuncs_name, sizeof(*filefuncs));\n\tmodfuncs = plugfuncs->GetEngineInterface(plugmodfuncs_name, sizeof(*modfuncs));\n\tif (modfuncs && modfuncs->version != MODPLUGFUNCS_VERSION)\n\t\tmodfuncs = NULL;\n\n\tif (modfuncs && filefuncs)\n\t{\n\t\tmodfuncs->RegisterModelFormatMagic(\"XMODEL\", \"\\x0e\\0\",2, Mod_XModel_Load);\t//cod1\n\t\tmodfuncs->RegisterModelFormatMagic(\"XMODEL\", \"\\x14\\0\",2, Mod_XModel_Load);\t//cod2\n\t\treturn true;\n\t}\n\treturn false;\n}\n\n\nqboolean CODBSP_Init(void);\nqboolean STypes_Init(void);\nqboolean IWI_Init(void);\n//qboolean IWFF_Init(void);\n\nqboolean Plug_Init(void)\n{\n\tqboolean somethingisokay = false;\n\tchar plugname[128];\n\tstrcpy(plugname, \"cod\");\n\tplugfuncs->GetPluginName(0, plugname, sizeof(plugname));\n\n\tif (!STypes_Init())\tCon_Printf(CON_ERROR\"%s: Shader Types support unavailable\\n\",\tplugname);\telse\tsomethingisokay = true;\n\tif (!IWI_Init())\tCon_Printf(CON_ERROR\"%s: IWI support unavailable\\n\",\t\t\tplugname);\telse\tsomethingisokay = true;\n\tif (!XMODEL_Init())\tCon_Printf(CON_ERROR\"%s: XModel support unavailable\\n\",\t\t\tplugname);\telse\tsomethingisokay = true;\n\tif (!CODBSP_Init())\tCon_Printf(CON_ERROR\"%s: CODBSP support unavailable\\n\",\t\t\tplugname);\telse\tsomethingisokay = true;\n//\tif (!IWFF_Init())\tCon_Printf(CON_ERROR\"%s: FF support unavailable\\n\",\t\t\t\tplugname);\telse\tsomethingisokay = true;\n\treturn somethingisokay;\n}\n"
  },
  {
    "path": "plugins/emailnot/emailnot.q3asm",
    "content": "-o \"emailnot\"\nplugin\nqvm_api\nmemory\nimapnoti\npop3noti\nmd5"
  },
  {
    "path": "plugins/emailnot/imapnoti.c",
    "content": "/*\nCopyright (C) 2005 David Walton.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\n\n\nAs a special exception, you may incorpotate patents and libraries regarding only hashing and security, on the conditions that it is also open source.\nThis means md4/5, rsa, ssl and similar.\n*/\n\n#include \"../plugin.h\"\n\n//code to sit on an imap server and check for new emails every now and then.\n\n\n\n\n\nchar *STR_Parse(char *str, char *out, int outlen, char *punctuation)\n{\n\tchar *s = str;\n\tchar *f;\n\t\nskipwhite:\n\t//skip over the whitespace\n\twhile (*s <= ' ' && *s)\n\t\ts++;\n\n\tif (*s == '/')\n\t{\n\t\tif (s[1] == '/')\t//c++ style comment\n\t\t{\n\t\t\twhile(*s != '\\n' && *s)\n\t\t\t\ts++;\n\n\t\t\tgoto skipwhite;\n\t\t}\n\t\tif (s[1] == '*')\n\t\t{\n\t\t\ts+=2;\n\t\t\twhile(*s)\n\t\t\t{\n\t\t\t\tif (s[0] == '*' && s[1] == '/')\n\t\t\t\t{\n\t\t\t\t\ts+=2;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\ts++;\n\t\t\t}\n\t\t\tgoto skipwhite;\n\t\t}\n\t}\n\n\tif (*s == '\\\"')\n\t{\n\t\ts++;\n\t\twhile(*s && outlen>1)\n\t\t{\n\t\t\tif (*s == '\\\"')\n\t\t\t{\n\t\t\t\ts++;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\t*out++ = *s++;\n\t\t\toutlen--;\n\t\t}\n\t\t*out++ = '\\0';\n\t\treturn s;\n\t}\n\n\tif (strchr(punctuation, *s))\n\t{\t//starts with punctuation, so return only the first char\n\t\tif (outlen < 2)\n\t\t\treturn NULL;\t//aaaah!\n\t\t*out++ = *s;\n\t\t*out++ = '\\0';\n\t\ts++;\n\n\t\treturn s;\n\t}\n\t//skip over non-white\n\tfor (f = s; outlen>1 && *(unsigned char*)f > ' '; f++, outlen--)\n\t{\n\t\tif (strchr(punctuation, *f))\n\t\t{\t//found punctuation, so return up to here\n\t\t\tbreak;\n\t\t}\n\t\t*out++ = *f;\n\t}\n\t*out++ = '\\0';\n\n\treturn f;\n}\n\n\n\n\n\n\n//exported.\nvoid IMAP_CreateConnection(char *servername, char *username, char *password);\nint imap_checkfrequency=60*1000;\nvoid IMAP_Think (void);\n//end export list.\n\n\n\n\n#define IMAP_PORT 143\n\n\ntypedef struct imap_con_s {\n\tchar server[128];\n\tchar username[128];\n\tchar password[128];\n\n\tunsigned int lastnoop;\n\n\t//these are used so we can fail a send.\n\t//or recieve only part of an input.\n\t//FIXME:\tmake dynamically sizable, as it could drop if the send is too small (That's okay.\n\t//\t\t\tbut if the read is bigger than one command we suddenly fail entirly.\n\tint sendlen;\n\tint sendbuffersize;\n\tchar *sendbuffer;\n\tint readlen;\n\tint readbuffersize;\n\tchar *readbuffer;\n\n\tqboolean drop;\n\n\tqhandle_t socket;\n\n\tenum {\n\t\tIMAP_WAITINGFORINITIALRESPONCE,\n\t\tIMAP_AUTHING,\n\t\tIMAP_AUTHED,\n\t\tIMAP_INBOX\n\t} state;\n\n\tstruct imap_con_s *next;\n} imap_con_t;\n\nstatic imap_con_t *imapsv;\n\nvoid IMAP_CreateConnection(char *addy, char *username, char *password)\n{\n\tunsigned long _true = true;\n\timap_con_t *con;\n\n\tfor (con = imapsv; con; con = con->next)\n\t{\n\t\tif (!strcmp(con->server, addy))\n\t\t{\n\t\t\tCon_Printf(\"Already connected to that imap server\\n\");\n\t\t\treturn;\n\t\t}\n\t}\n\n\tcon = malloc(sizeof(imap_con_t));\n\n\tcon->socket = Net_TCPConnect(addy, IMAP_PORT);\n\n\tif (!con->socket)\n\t{\n\t\tCon_Printf (\"IMAP_CreateConnection: connect failed\\n\");\n\t\tfree(con);\n\t\treturn;\n\t}\n\n\tstrlcpy(con->server, addy, sizeof(con->server));\n\tstrlcpy(con->username, username, sizeof(con->username));\n\tstrlcpy(con->password, password, sizeof(con->password));\n\n\tcon->next = imapsv;\n\timapsv = con;\n\n\tCon_Printf (\"Connected to %s (%s)\\n\", addy, username);\n}\n\nstatic void IMAP_EmitCommand(imap_con_t *imap, char *text)\n{\n\tint newlen;\n\t\n\t//makes a few things easier though\n\n\tnewlen = imap->sendlen + 2 + strlen(text) + 2;\n\n\tif (newlen >= imap->sendbuffersize || !imap->sendbuffer)\t//pre-length check.\n\t{\n\t\tchar *newbuf;\n\t\timap->sendbuffersize = newlen*2;\n\t\tnewbuf = malloc(imap->sendbuffersize);\t//the null terminator comes from the >=\n\t\tif (!newbuf)\n\t\t{\n\t\t\tCon_Printf(\"Memory is low\\n\");\n\t\t\timap->drop = true;\t//failed.\n\t\t\treturn;\n\t\t}\n\t\tif (imap->sendbuffer)\n\t\t{\n\t\t\tmemcpy(newbuf, imap->sendbuffer, imap->sendlen);\n\t\t\tfree(imap->sendbuffer);\n\t\t}\n\t\timap->sendbuffer = newbuf;\n\t}\n\n\tsnprintf(imap->sendbuffer+imap->sendlen, newlen+1, \"* %s\\r\\n\", text);\n\timap->sendlen = newlen;\n}\n\nstatic char *IMAP_AddressStructure(char *msg, char *out, int outsize)\n{\n\tchar name[256];\n\tchar mailbox[64];\n\tchar hostname[128];\n\tchar route[128];\n\tint indents=0;\n\twhile(*msg == ' ')\n\t\tmsg++;\n\twhile(*msg == '(')\t//do it like this, we can get 2... I'm not sure if that's always true..\n\t{\n\t\tmsg++;\n\t\tindents++;\n\t}\n\n\tmsg = STR_Parse(msg, name, sizeof(name), \"\");\t//name\n\tmsg = STR_Parse(msg, route, sizeof(route), \"\");\t//smtp route (ignored normally)\n\tmsg = STR_Parse(msg, mailbox, sizeof(mailbox), \"\");\t//mailbox\n\tmsg = STR_Parse(msg, hostname, sizeof(hostname), \"\");\t//hostname\n\n\twhile(indents && *msg == ')')\n\t\tmsg++;\n\n\tif (out)\n\t{\n\t\tif (!strcmp(name, \"NIL\"))\n\t\t\tsnprintf(out, outsize, \"%s@%s\", mailbox, hostname);\n\t\telse\n\t\t\tsnprintf(out, outsize, \"%s <%s@%s>\", name, mailbox, hostname);\n\t}\n\n\treturn msg;\n}\n\nstatic qboolean IMAP_ThinkCon(imap_con_t *imap)\t//false means drop the connection.\n{\n\tchar *ending;\n\tint len;\n\n\t//get the buffer, stick it in our read holder\n\tif (imap->readlen+32 >= imap->readbuffersize || !imap->readbuffer)\n\t{\n\t\tlen = imap->readbuffersize;\n\t\tif (!imap->readbuffer)\n\t\t\timap->readbuffersize = 256;\n\t\telse\n\t\t\timap->readbuffersize*=2;\n\n\t\tending = malloc(imap->readbuffersize);\n\t\tif (!ending)\n\t\t{\n\t\t\tCon_Printf(\"Memory is low\\n\");\n\t\t\treturn false;\n\t\t}\n\t\tif (imap->readbuffer)\n\t\t{\n\t\t\tmemcpy(ending, imap->readbuffer, len);\n\t\t\tfree(imap->readbuffer);\n\t\t}\n\t\timap->readbuffer = ending;\n\t}\n\n\tlen = Net_Recv(imap->socket, imap->readbuffer+imap->readlen, imap->readbuffersize-imap->readlen-1);\n\tif (len>0)\n\t{\n\t\timap->readlen+=len;\n\t\timap->readbuffer[imap->readlen] = '\\0';\n\t}\n\n\tif (imap->readlen>0)\n\t{\n\t\tending = strstr(imap->readbuffer, \"\\r\\n\");\n\n\t\tif (ending)\t//pollable text.\n\t\t{\n\t\t\t*ending = '\\0';\n//\t\t\tCon_Printf(\"%s\\n\", imap->readbuffer);\n\n\t\t\tending+=2;\n\t\t\tif (imap->state == IMAP_WAITINGFORINITIALRESPONCE)\n\t\t\t{\n\t\t\t\t//can be one of two things.\n\t\t\t\tif (!strncmp(imap->readbuffer, \"* OK\", 4))\n\t\t\t\t{\n\t\t\t\t\tIMAP_EmitCommand(imap, va(\"LOGIN %s %s\", imap->username, imap->password));\n\t\t\t\t\timap->state = IMAP_AUTHING;\n\t\t\t\t}\n\t\t\t\telse if (!strncmp(imap->readbuffer, \"* PREAUTH\", 9))\n\t\t\t\t{\n\t\t\t\t\tCon_Printf(\"Logged on to %s\\n\", imap->server);\n\t\t\t\t\tIMAP_EmitCommand(imap, \"SELECT INBOX\");\n\t\t\t\t\timap->state = IMAP_AUTHED;\n\t\t\t\t\timap->lastnoop = Sys_Milliseconds();\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tCon_Printf(\"Unexpected response from IMAP server\\n\");\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (imap->state == IMAP_AUTHING)\n\t\t\t{\n\t\t\t\tif (!strncmp(imap->readbuffer, \"* OK\", 4))\n\t\t\t\t{\n\t\t\t\t\tCon_Printf(\"Logged on to %s\\n\", imap->server);\n\t\t\t\t\tIMAP_EmitCommand(imap, \"SELECT INBOX\");\n\t\t\t\t\timap->state = IMAP_AUTHED;\n\t\t\t\t\timap->lastnoop = Sys_Milliseconds();\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tCon_Printf(\"Unexpected response from IMAP server\\n\");\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (imap->state == IMAP_AUTHED)\n\t\t\t{\n\t\t\t\tchar *num;\n\t\t\t\tnum = imap->readbuffer;\n\t\t\t\tif (!strncmp(imap->readbuffer, \"* SEARCH \", 8))\t//we only ever search for recent messages. So we fetch them and get sender and subject.\n\t\t\t\t{\n\t\t\t\t\tchar *s;\n\t\t\t\t\ts = imap->readbuffer+8;\n\t\t\t\t\tnum = NULL;\n\t\t\t\t\twhile(*s)\n\t\t\t\t\t{\n\t\t\t\t\t\ts++;\n\t\t\t\t\t\tnum = s;\n\t\t\t\t\t\twhile (*s >= '0' && *s <= '9')\n\t\t\t\t\t\t\ts++;\n\n\t\t\t\t\t\tIMAP_EmitCommand(imap, va(\"FETCH %i ENVELOPE\", atoi(num)));\t//envelope so that it's all one line.\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse if (imap->readbuffer[0] == '*' && imap->readbuffer[1] == ' ')\n\t\t\t\t{\n\t\t\t\t\tnum = imap->readbuffer+2;\n\t\t\t\t\twhile(*num >= '0' && *num <= '9')\n\t\t\t\t\t{\n\t\t\t\t\t\tnum++;\n\t\t\t\t\t}\n\t\t\t\t\tif (!strcmp(num, \" RECENT\"))\n\t\t\t\t\t{\n\t\t\t\t\t\tif (atoi(imap->readbuffer+2) > 0)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tIMAP_EmitCommand(imap, \"SEARCH RECENT\");\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\telse if (!strncmp(num, \" FETCH (ENVELOPE (\", 18))\n\t\t\t\t\t{\n\t\t\t\t\t\tchar from[256];\n\t\t\t\t\t\tchar subject[256];\n\t\t\t\t\t\tchar date[256];\n\n\t\t\t\t\t\tnum += 18;\n\n\t\t\t\t\t\tnum = STR_Parse(num, date, sizeof(date), \"\");\n\t\t\t\t\t\tnum = STR_Parse(num, subject, sizeof(subject), \"\");\n//\t\t\t\t\t\tCon_Printf(\"Date/Time: %s\\n\", date);\n\n\t\t\t\t\t\tnum = IMAP_AddressStructure(num, from, sizeof(from));\n\n\n\t\t\t\t\t\tif ((rand() & 3) == 3)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (rand()&1)\n\t\t\t\t\t\t\t\tCon_Printf(\"\\n^2New spam has arrived\\n\");\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\tCon_Printf(\"\\n^2You have new spam\\n\");\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (rand()&1)\n\t\t\t\t\t\t\tCon_Printf(\"\\n^2New mail has arrived\\n\");\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tCon_Printf(\"\\n^2You have new mail\\n\");\n\n\t\t\t\t\t\tCon_Printf(\"Subject: %s\\n\", subject);\n\t\t\t\t\t\tCon_Printf(\"From: %s\\n\", from);\n\n\t\t\t\t\t\tSCR_CenterPrint(va(\"NEW MAIL HAS ARRIVED\\n\\nTo: %s@%s\\nFrom: %s\\nSubject: %s\", imap->username, imap->server, from, subject));\n\n\t\t\t\t\t\t//throw the rest away.\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tCon_Printf(\"Bad client state\\n\");\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\timap->readlen -= ending - imap->readbuffer;\n\t\t\tmemmove(imap->readbuffer, ending, strlen(ending)+1);\n\t\t}\n\t}\n\tif (imap->drop)\n\t\treturn false;\n\n\tif (imap->state == IMAP_AUTHED)\n\t{\n\t\tif (imap->lastnoop + imap_checkfrequency < Sys_Milliseconds())\n\t\t{\t//we need to keep the connection reasonably active\n\n\t\t\tIMAP_EmitCommand(imap, \"SELECT INBOX\");\t//this causes the recent flags to be reset. This is the only way I found.\n\t\t\timap->lastnoop = Sys_Milliseconds();\n\t\t}\n\t}\n\n\tif (imap->sendlen)\n\t{\n\t\tlen = Net_Send(imap->socket, imap->sendbuffer, imap->sendlen);\n\t\tif (len>0)\n\t\t{\n\t\t\timap->sendlen-=len;\n\t\t\tmemmove(imap->sendbuffer, imap->sendbuffer+len, imap->sendlen+1);\n\t\t}\n\t}\n\treturn true;\n}\n\nvoid IMAP_Think (void)\n{\n\timap_con_t *prev = NULL;\n\timap_con_t *imap;\n\n\tfor (imap = imapsv; imap; imap = imap->next)\n\t{\n\t\tif (imap->drop || !IMAP_ThinkCon(imap))\n\t\t{\n\t\t\tif (!prev)\n\t\t\t\timapsv = imap->next;\n\t\t\telse\n\t\t\t\tprev->next = imap->next;\n\t\t\tNet_Close(imap->socket);\n\t\t\tfree(imap);\n\t\t\tif (!prev)\n\t\t\t\tbreak;\n\t\t}\n\n\t\tprev = imap;\n\t}\n}\n"
  },
  {
    "path": "plugins/emailnot/md5.c",
    "content": "//used by pop3.\n#include \"../plugin.h\"\n\n\n/* MD5C.C - RSA Data Security, Inc., MD5 message-digest algorithm\n */\n\n/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All\nrights reserved.\n\nLicense to copy and use this software is granted provided that it\nis identified as the \"RSA Data Security, Inc. MD5 Message-Digest\nAlgorithm\" in all material mentioning or referencing this software\nor this function.\n\nLicense is also granted to make and use derivative works provided\nthat such works are identified as \"derived from the RSA Data\nSecurity, Inc. MD5 Message-Digest Algorithm\" in all material\nmentioning or referencing the derived work.\n\nRSA Data Security, Inc. makes no representations concerning either\nthe merchantability of this software or the suitability of this\nsoftware for any particular purpose. It is provided \"as is\"\nwithout express or implied warranty of any kind.\n\nThese notices must be retained in any copies of any part of this\ndocumentation and/or software.\n */\n\n\n/* GLOBAL.H - RSAREF types and constants\n */\n\n/* POINTER defines a generic pointer type */\ntypedef unsigned char *POINTER;\n\n/* UINT2 defines a two byte word */\ntypedef unsigned short int UINT2;\n\n/* UINT4 defines a four byte word */\ntypedef unsigned int UINT4;\n\n\n\n\n\n\n\n/* MD5.H - header file for MD5C.C\n */\n\n/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All\nrights reserved.\n\nLicense to copy and use this software is granted provided that it\nis identified as the \"RSA Data Security, Inc. MD5 Message-Digest\nAlgorithm\" in all material mentioning or referencing this software\nor this function.\n\nLicense is also granted to make and use derivative works provided\nthat such works are identified as \"derived from the RSA Data\nSecurity, Inc. MD5 Message-Digest Algorithm\" in all material\nmentioning or referencing the derived work.\n\nRSA Data Security, Inc. makes no representations concerning either\nthe merchantability of this software or the suitability of this\nsoftware for any particular purpose. It is provided \"as is\"\nwithout express or implied warranty of any kind.\n\nThese notices must be retained in any copies of any part of this\ndocumentation and/or software.\n */\n\n/* MD5 context. */\ntypedef struct {\n  UINT4 state[4];                                   /* state (ABCD) */\n  UINT4 count[2];        /* number of bits, modulo 2^64 (lsb first) */\n  unsigned char buffer[64];                         /* input buffer */\n} MD5_CTX;\n\nvoid MD5Init (MD5_CTX *ctx);\nvoid MD5Update (MD5_CTX *, unsigned char *, unsigned int);\nvoid MD5Final (unsigned char [16], MD5_CTX *);\n\n\n\n\n\n\n\n\n\n\n\n\n/* Constants for MD5Transform routine.\n */\n\n#define S11 7\n#define S12 12\n#define S13 17\n#define S14 22\n#define S21 5\n#define S22 9\n#define S23 14\n#define S24 20\n#define S31 4\n#define S32 11\n#define S33 16\n#define S34 23\n#define S41 6\n#define S42 10\n#define S43 15\n#define S44 21\n\nstatic void MD5Transform (UINT4 [4], unsigned char [64]);\nstatic void Encode (unsigned char *, UINT4 *, unsigned int);\nstatic void Decode (UINT4 *, unsigned char *, unsigned int);\n\nstatic unsigned char PADDING[64] = {\n  0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\n};\n\n/* F, G, H and I are basic MD5 functions.\n */\n#define F(x, y, z) (((x) & (y)) | ((~x) & (z)))\n#define G(x, y, z) (((x) & (z)) | ((y) & (~z)))\n#define H(x, y, z) ((x) ^ (y) ^ (z))\n#define I(x, y, z) ((y) ^ ((x) | (~z)))\n\n/* ROTATE_LEFT rotates x left n bits.\n */\n#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n))))\n\n/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4.\nRotation is separate from addition to prevent recomputation.\n */\n#define FF(a, b, c, d, x, s, ac) { \\\n (a) += F ((b), (c), (d)) + (x) + (UINT4)(ac); \\\n (a) = ROTATE_LEFT ((a), (s)); \\\n (a) += (b); \\\n  }\n#define GG(a, b, c, d, x, s, ac) { \\\n (a) += G ((b), (c), (d)) + (x) + (UINT4)(ac); \\\n (a) = ROTATE_LEFT ((a), (s)); \\\n (a) += (b); \\\n  }\n#define HH(a, b, c, d, x, s, ac) { \\\n (a) += H ((b), (c), (d)) + (x) + (UINT4)(ac); \\\n (a) = ROTATE_LEFT ((a), (s)); \\\n (a) += (b); \\\n  }\n#define II(a, b, c, d, x, s, ac) { \\\n (a) += I ((b), (c), (d)) + (x) + (UINT4)(ac); \\\n (a) = ROTATE_LEFT ((a), (s)); \\\n (a) += (b); \\\n  }\n\n/* MD5 initialization. Begins an MD5 operation, writing a new context.\n */\nvoid MD5Init (MD5_CTX *context)\n{\n  context->count[0] = context->count[1] = 0;\n  /* Load magic initialization constants.\n*/\n  context->state[0] = 0x67452301;\n  context->state[1] = 0xefcdab89;\n  context->state[2] = 0x98badcfe;\n  context->state[3] = 0x10325476;\n}\n\n/* MD5 block update operation. Continues an MD5 message-digest\n  operation, processing another message block, and updating the\n  context.\n */\nvoid MD5Update (MD5_CTX *context, unsigned char *input, unsigned int inputLen)\n{\n  unsigned int i, index, partLen;\n\n  /* Compute number of bytes mod 64 */\n  index = (unsigned int)((context->count[0] >> 3) & 0x3F);\n\n  /* Update number of bits */\n  if ((context->count[0] += ((UINT4)inputLen << 3))\n\n   < ((UINT4)inputLen << 3))\n context->count[1]++;\n  context->count[1] += ((UINT4)inputLen >> 29);\n\n  partLen = 64 - index;\n\n  /* Transform as many times as possible.\n*/\n  if (inputLen >= partLen) {\n memcpy\n   ((POINTER)&context->buffer[index], (POINTER)input, partLen);\n MD5Transform (context->state, context->buffer);\n\n for (i = partLen; i + 63 < inputLen; i += 64)\n   MD5Transform (context->state, &input[i]);\n\n index = 0;\n  }\n  else\n i = 0;\n\n  /* Buffer remaining input */\n  memcpy\n ((POINTER)&context->buffer[index], (POINTER)&input[i],\n  inputLen-i);\n}\n\n/* MD5 finalization. Ends an MD5 message-digest operation, writing the\n  the message digest and zeroizing the context.\n */\nvoid MD5Final (unsigned char digest[16], MD5_CTX *context)\n{\n  unsigned char bits[8];\n  unsigned int index, padLen;\n\n  /* Save number of bits */\n  Encode (bits, context->count, 8);\n\n  /* Pad out to 56 mod 64.\n*/\n  index = (unsigned int)((context->count[0] >> 3) & 0x3f);\n  padLen = (index < 56) ? (56 - index) : (120 - index);\n  MD5Update (context, PADDING, padLen);\n\n  /* Append length (before padding) */\n  MD5Update (context, bits, 8);\n\n  /* Store state in digest */\n  Encode (digest, context->state, 16);\n\n  /* Zeroize sensitive information.\n*/\n  memset ((POINTER)context, 0, sizeof (*context));\n}\n\n/* MD5 basic transformation. Transforms state based on block.\n */\nstatic void MD5Transform (UINT4 state[4], unsigned char block[64])\n{\n  UINT4 a = state[0], b = state[1], c = state[2], d = state[3], x[16];\n\n  Decode (x, block, 64);\n\n  /* Round 1 */\n  FF (a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */\n  FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */\n  FF (c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */\n  FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */\n  FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */\n  FF (d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */\n  FF (c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */\n  FF (b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */\n  FF (a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */\n  FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */\n  FF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */\n  FF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */\n  FF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */\n  FF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */\n  FF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */\n  FF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */\n\n /* Round 2 */\n  GG (a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */\n  GG (d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */\n  GG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */\n  GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */\n  GG (a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */\n  GG (d, a, b, c, x[10], S22,  0x2441453); /* 22 */\n  GG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */\n  GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */\n  GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */\n  GG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */\n  GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */\n\n  GG (b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */\n  GG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */\n  GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */\n  GG (c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */\n  GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */\n\n  /* Round 3 */\n  HH (a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */\n  HH (d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */\n  HH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */\n  HH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */\n  HH (a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */\n  HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */\n  HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */\n  HH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */\n  HH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */\n  HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */\n  HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */\n  HH (b, c, d, a, x[ 6], S34,  0x4881d05); /* 44 */\n  HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */\n  HH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */\n  HH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */\n  HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */\n\n  /* Round 4 */\n  II (a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */\n  II (d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */\n  II (c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */\n  II (b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */\n  II (a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */\n  II (d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */\n  II (c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */\n  II (b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */\n  II (a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */\n  II (d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */\n  II (c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */\n  II (b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */\n  II (a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */\n  II (d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */\n  II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */\n  II (b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */\n\n  state[0] += a;\n  state[1] += b;\n  state[2] += c;\n  state[3] += d;\n\n  /* Zeroize sensitive information.\n\n*/\n  memset ((POINTER)x, 0, sizeof (x));\n}\n\n/* Encodes input (UINT4) into output (unsigned char). Assumes len is\n  a multiple of 4.\n */\nstatic void Encode (unsigned char *output, UINT4 *input, unsigned int len)\n{\n  unsigned int i, j;\n\n  for (i = 0, j = 0; j < len; i++, j += 4) {\n output[j] = (unsigned char)(input[i] & 0xff);\n output[j+1] = (unsigned char)((input[i] >> 8) & 0xff);\n output[j+2] = (unsigned char)((input[i] >> 16) & 0xff);\n output[j+3] = (unsigned char)((input[i] >> 24) & 0xff);\n  }\n}\n\n/* Decodes input (unsigned char) into output (UINT4). Assumes len is\n  a multiple of 4.\n */\nstatic void Decode (UINT4 *output, unsigned char *input, unsigned int len)\n{\n  unsigned int i, j;\n\n  for (i = 0, j = 0; j < len; i++, j += 4)\n output[i] = ((UINT4)input[j]) | (((UINT4)input[j+1]) << 8) |\n   (((UINT4)input[j+2]) << 16) | (((UINT4)input[j+3]) << 24);\n}\n\n\n\n\nchar *MD5_ToHex(char *input, int inputlen, char *ret, int retlen)\n{\n\tint v, i;\n\tunsigned char\tdigest[16];\n\tMD5_CTX\t\tctx;\n\n\tif (retlen < 33)\n\t\treturn NULL;\n\n\tMD5Init (&ctx);\n\tMD5Update (&ctx, (unsigned char *)input, inputlen);\n\tMD5Final ( (unsigned char *)digest, &ctx);\n\n\tfor (i = 0; i < 16; i++)\n\t{\n\t\tv = digest[i]>>4;\n\t\tif (v >= 10)\n\t\t\tret[i*2+0] = (v-10) + 'a';\n\t\telse\n\t\t\tret[i*2+0] = v + '0';\n\t\tv = digest[i]&0xf;\n\t\tif (v >= 10)\n\t\t\tret[i*2+1] = (v-10) + 'a';\n\t\telse\n\t\t\tret[i*2+1] = v + '0';\n\t}\n\tret[i*2] = '\\0';\n\treturn ret;\n}\nchar *MD5_ToBinary(char *input, int inputlen, char *ret, int retlen)\n{\n\tMD5_CTX\t\tctx;\n\n\tif (retlen < 16)\n\t\treturn NULL;\n\n\tMD5Init (&ctx);\n\tMD5Update (&ctx, (unsigned char *)input, inputlen);\n\tMD5Final ( (unsigned char *)ret, &ctx);\n\n\treturn ret;\n}\n\nchar *MD5_GetPop3APOPString(char *timestamp, char *secrit)\n{\n\tint v, i;\n\tstatic char ret[33];\n\tunsigned char\tdigest[16];\n\tMD5_CTX\t\tctx;\n\n\tMD5Init (&ctx);\n\tMD5Update (&ctx, (unsigned char *)timestamp, strlen(timestamp));\n\tMD5Update (&ctx, (unsigned char *)secrit, strlen(secrit));\n\tMD5Final ( (unsigned char *)digest, &ctx);\n\n\tfor (i = 0; i < 16; i++)\n\t{\n\t\tv = digest[i]>>4;\n\t\tif (v >= 10)\n\t\t\tret[i*2+0] = (v-10) + 'a';\n\t\telse\n\t\t\tret[i*2+0] = v + '0';\n\t\tv = digest[i]&0xf;\n\t\tif (v >= 10)\n\t\t\tret[i*2+1] = (v-10) + 'a';\n\t\telse\n\t\t\tret[i*2+1] = v + '0';\n\t}\n\tret[i*2] = '\\0';\n\treturn ret;\n}\n"
  },
  {
    "path": "plugins/emailnot/pop3noti.c",
    "content": "/*\nCopyright (C) 2005 David Walton.\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\nAs a special exception, you may incorpotate patents and libraries regarding only hashing and security, on the conditions that it is also open source.\nThis means md4/5, rsa, ssl and similar.\n*/\n\n\n\n#include \"../plugin.h\"\n\n//the idea is to send a UIDL request, and compare against the previous list.\n//this list will be stored on disk on quit.\n\n//be aware that we cannot stay connected. POP3 mailboxes are not refreshable without disconnecting.\n//so we have a special state.\n\n\n\n\nchar *MD5_GetPop3APOPString(char *timestamp, char *secrit);\n\nvoid IMAP_CreateConnection(char *servername, char *username, char *password);\nvoid IMAP_Think (void);\n\n\n\n\n\n//exported.\nvoid POP3_CreateConnection(char *servername, char *username, char *password);\nint pop3_checkfrequency=60*1000;\nvoid POP3_Think (void);\nvoid POP3_WriteCache (void);\n//end export list.\n\ntypedef struct msglist_s {\n\tstruct msglist_s  *next;\n\tchar name[4];\n} msglist_t;\n\nmsglist_t *msglist;\nqboolean POP3_IsMessageUnique(char *name)\n{\n\tmsglist_t *msg;\n\n\tfor (msg = msglist; msg; msg = msg->next)\n\t{\n\t\tif (!strcmp(msg->name, name))\n\t\t\treturn false;\n\t}\n\n\tmsg = malloc(sizeof(msglist_t) + strlen(name)+1);\n\tif (!msg)\n\t\treturn false;\n\tstrcpy(msg->name, name);\n\tmsg->next = msglist;\n\tmsglist = msg;\n\t\n\treturn true;\n}\n\n#define POP3_PORT\t110\n\n\ntypedef struct pop3_con_s {\n\tchar server[128];\n\tchar username[128];\n\tchar password[128];\n\n\tunsigned int lastnoop;\n\n\t//these are used so we can fail a send.\n\t//or recieve only part of an input.\n\t//FIXME:\tmake dynamically sizable, as it could drop if the send is too small (That's okay.\n\t//\t\t\tbut if the read is bigger than one command we suddenly fail entirly.)\n\tint sendlen;\n\tint sendbuffersize;\n\tchar *sendbuffer;\n\tint readlen;\n\tint readbuffersize;\n\tchar *readbuffer;\n\n\tqboolean drop;\n\n\tqhandle_t socket;\n\n\t//we have a certain number of stages.\n\tenum {\n\t\tPOP3_NOTCONNECTED,\n\t\tPOP3_WAITINGFORINITIALRESPONCE,\t//waiting for an initial response.\n\t\tPOP3_AUTHING,\t//wating for a response from USER\n\t\tPOP3_AUTHING2,\t//Set PASS, waiting to see if we passed.\n\t\tPOP3_LISTING,\t//Sent UIDL, waiting to see\n\t\tPOP3_RETRIEVING,\t//sent TOP, waiting for message headers to print info.\n\t\tPOP3_HEADER,\n\t\tPOP3_BODY,\n\t\tPOP3_QUITTING\n\t} state;\n\n\tint retrlist[256];\t//unrecognised uidls are added to this list.\n\tint numtoretrieve;\n\n\tchar msgsubject[256];\n\tchar msgfrom[256];\n\n\tstruct pop3_con_s *next;\n} pop3_con_t;\n\nstatic pop3_con_t *pop3sv;\n\nvoid POP3_CreateConnection(char *addy, char *username, char *password)\n{\n\tpop3_con_t *con;\n\n\tfor (con = pop3sv; con; con = con->next)\n\t{\n\t\tif (!strcmp(con->server, addy) && !strcmp(con->username, username))\n\t\t{\n\t\t\tif (con->state == POP3_NOTCONNECTED && !con->socket)\n\t\t\t\tbreak;\n\t\t\tCon_Printf(\"Already connected to that pop3 server\\n\");\n\t\t\treturn;\n\t\t}\n\t}\n\n\tif (!con)\n\t{\n\t\tcon = malloc(sizeof(pop3_con_t));\n\t\tif (!con)\n\t\t{\n\t\t\tCon_Printf (\"POP3_CreateConnection: out of plugin memory\\n\");\n\t\t\treturn;\n\t\t}\n\t\tmemset(con, 0, sizeof(*con));\n\t}\n\telse\n\t\tcon->state = POP3_WAITINGFORINITIALRESPONCE;\n\n\tcon->socket = Net_TCPConnect(addy, POP3_PORT);\n\tif (!con->socket)\n\t{\n\t\tCon_Printf (\"POP3_CreateConnection: connect failed\\n\");\n\t\tfree(con);\n\t\treturn;\n\t}\n\n\tstrlcpy(con->server, addy, sizeof(con->server));\n\tstrlcpy(con->username, username, sizeof(con->username));\n\tstrlcpy(con->password, password, sizeof(con->password));\n\n\tif (!con->state)\n\t{\n\t\tcon->state = POP3_WAITINGFORINITIALRESPONCE;\n\n\t\tcon->next = pop3sv;\n\t\tpop3sv = con;\n\n\t\tCon_Printf(\"Connected to %s\\n\", con->server);\n\t}\n}\n\nstatic void POP3_EmitCommand(pop3_con_t *pop3, char *text)\n{\n\tint newlen;\n\n\tnewlen = pop3->sendlen + strlen(text) + 2;\n\n\tif (newlen >= pop3->sendbuffersize || !pop3->sendbuffer)\t//pre-length check.\n\t{\n\t\tchar *newbuf;\n\t\tpop3->sendbuffersize = newlen*2;\n\t\tnewbuf = malloc(pop3->sendbuffersize);\n\t\tif (!newbuf)\n\t\t{\n\t\t\tCon_Printf(\"Memory is low\\n\");\n\t\t\tpop3->drop = true;\t//failed.\n\t\t\treturn;\n\t\t}\n\t\tif (pop3->sendbuffer)\n\t\t{\n\t\t\tmemcpy(newbuf, pop3->sendbuffer, pop3->sendlen);\n\t\t\tfree(pop3->sendbuffer);\n\t\t}\n\t\tpop3->sendbuffer = newbuf;\n\t}\n\n\n\tsnprintf(pop3->sendbuffer+pop3->sendlen, newlen+1, \"%s\\r\\n\", text);\n\tpop3->sendlen = newlen;\n\n//\tCon_Printf(\"^3%s\\n\", text);\n}\n\nstatic qboolean POP3_ThinkCon(pop3_con_t *pop3)\t//false means drop the connection.\n{\n\tchar *ending;\n\tint len;\n\n\tif (pop3->state == POP3_NOTCONNECTED && !pop3->socket)\n\t{\n\t\tif (pop3->lastnoop + pop3_checkfrequency < Sys_Milliseconds())\n\t\t{\t//we need to recreate the connection now.\n\t\t\tpop3->lastnoop = Sys_Milliseconds();\n\t\t\tPOP3_CreateConnection(pop3->server, pop3->username, pop3->password);\n\t\t}\n\n\t\treturn true;\n\t}\n\n\t//get the buffer, stick it in our read holder\n\tif (pop3->readlen+32 >= pop3->readbuffersize || !pop3->readbuffer)\n\t{\n\t\tlen = pop3->readbuffersize;\n\t\tif (!pop3->readbuffer)\n\t\t\tpop3->readbuffersize = 256;\n\t\telse\n\t\t\tpop3->readbuffersize*=2;\n\n\t\tending = malloc(pop3->readbuffersize);\n\t\tif (!ending)\n\t\t{\n\t\t\tCon_Printf(\"Memory is low\\n\");\n\t\t\treturn false;\n\t\t}\n\t\tif (pop3->readbuffer)\n\t\t{\n\t\t\tmemcpy(ending, pop3->readbuffer, len);\n\t\t\tfree(pop3->readbuffer);\n\t\t}\n\t\tpop3->readbuffer = ending;\n\t}\n\n\tlen = Net_Recv(pop3->socket, pop3->readbuffer+pop3->readlen, pop3->readbuffersize-pop3->readlen-1);\n\tif (len>0)\n\t{\n\t\tpop3->readlen+=len;\n\t\tpop3->readbuffer[pop3->readlen] = '\\0';\n\t}\n\n\tif (pop3->readlen>0)\n\t{\n\t\tending = strstr(pop3->readbuffer, \"\\r\\n\");\n\n\t\tif (ending)\t//pollable text.\n\t\t{\n\t\t\t*ending = '\\0';\n//\t\t\tCon_Printf(\"^2%s\\n\", pop3->readbuffer);\n\n\t\t\tending+=2;\n\t\t\tif (pop3->state == POP3_WAITINGFORINITIALRESPONCE)\n\t\t\t{\n\t\t\t\tif (!strncmp(pop3->readbuffer, \"+OK\", 3))\n\t\t\t\t{\n\t\t\t\t\tchar *angle1;\n\t\t\t\t\tchar *angle2 = NULL;\n\t\t\t\t\tangle1 = strchr(pop3->readbuffer, '<');\n\t\t\t\t\tif (angle1)\n\t\t\t\t\t{\n\t\t\t\t\t\tangle2 = strchr(angle1+1, '>');\n\t\t\t\t\t}\n\t\t\t\t\tif (angle2)\n\t\t\t\t\t{\t//just in case\n\t\t\t\t\t\tangle2[1] = '\\0';\n\n\t\t\t\t\t\tPOP3_EmitCommand(pop3, va(\"APOP %s %s\", pop3->username, MD5_GetPop3APOPString(angle1, pop3->password)));\n\t\t\t\t\t\tpop3->state = POP3_AUTHING2;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tPOP3_EmitCommand(pop3, va(\"USER %s\", pop3->username));\n\t\t\t\t\t\tpop3->state = POP3_AUTHING;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tCon_Printf(\"Unexpected response from POP3 server\\n\");\n\t\t\t\t\treturn false;\t//some sort of error.\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (pop3->state == POP3_AUTHING)\n\t\t\t{\n\t\t\t\tif (!strncmp(pop3->readbuffer, \"+OK\", 3))\n\t\t\t\t{\n\t\t\t\t\tPOP3_EmitCommand(pop3, va(\"PASS %s\", pop3->password));\n\t\t\t\t\tpop3->state = POP3_AUTHING2;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tCon_Printf(\"Unexpected response from POP3 server.\\nCheck username/password\\n\");\n\t\t\t\t\treturn false;\t//some sort of error.\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (pop3->state == POP3_AUTHING2)\n\t\t\t{\n\t\t\t\tif (!strncmp(pop3->readbuffer, \"+OK\", 3))\n\t\t\t\t{\n\t\t\t\t\tPOP3_EmitCommand(pop3, \"UIDL\");\n\t\t\t\t\tpop3->state = POP3_LISTING;\n\t\t\t\t\tpop3->lastnoop = Sys_Milliseconds();\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tCon_Printf(\"Unexpected response from POP3 server.\\nCheck username/password\\n\");\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (pop3->state == POP3_LISTING)\n\t\t\t{\n\t\t\t\tif (!strncmp(pop3->readbuffer, \"-ERR\", 4))\n\t\t\t\t{\n\t\t\t\t\tCon_Printf(\"Unexpected response from POP3 server.\\nUIDL not supported?\\n\");\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\telse if (!strncmp(pop3->readbuffer, \"+OK\", 3))\n\t\t\t\t{\n\t\t\t\t}\n\t\t\t\telse if (!strncmp(pop3->readbuffer, \".\", 1))\t//we only ever search for recent messages. So we fetch them and get sender and subject.\n\t\t\t\t{\n\t\t\t\t\tif (!pop3->numtoretrieve)\n\t\t\t\t\t{\n\t\t\t\t\t\tpop3->state = POP3_QUITTING;\n\t\t\t\t\t\tPOP3_EmitCommand(pop3, \"QUIT\");\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tpop3->state = POP3_RETRIEVING;\n\t\t\t\t\t\tPOP3_EmitCommand(pop3, va(\"RETR %i\", pop3->retrlist[--pop3->numtoretrieve]));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tchar *s;\n\t\t\t\t\ts = pop3->readbuffer;\n\t\t\t\t\tif (*s)\n\t\t\t\t\t{\n\t\t\t\t\t\ts++;\n\t\t\t\t\t\twhile (*s >= '0' && *s <= '9')\n\t\t\t\t\t\t\ts++;\n\t\t\t\t\t\twhile (*s == ' ')\n\t\t\t\t\t\t\ts++;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (POP3_IsMessageUnique(s))\n\t\t\t\t\t\tif (pop3->numtoretrieve < sizeof(pop3->retrlist)/sizeof(pop3->retrlist[0]))\n\t\t\t\t\t\t\tpop3->retrlist[pop3->numtoretrieve++] = atoi(pop3->readbuffer);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (pop3->state == POP3_RETRIEVING)\n\t\t\t{\n\t\t\t\tif (!strncmp(pop3->readbuffer, \"+OK\", 3))\n\t\t\t\t{\n\t\t\t\t\tpop3->msgsubject[0] = '\\0';\n\t\t\t\t\tpop3->msgfrom[0] = '\\0';\n\n\t\t\t\t\tpop3->state = POP3_HEADER;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\t//erm... go for the next?\n\t\t\t\t\tif (!pop3->numtoretrieve)\n\t\t\t\t\t{\n\t\t\t\t\t\tpop3->state = POP3_QUITTING;\n\t\t\t\t\t\tPOP3_EmitCommand(pop3, \"QUIT\");\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\tPOP3_EmitCommand(pop3, va(\"RETR %i\", pop3->retrlist[--pop3->numtoretrieve]));\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (pop3->state == POP3_HEADER)\n\t\t\t{\n\t\t\t\tif (!strnicmp(pop3->readbuffer, \"From: \", 6))\n\t\t\t\t\tstrlcpy(pop3->msgfrom, pop3->readbuffer + 6, sizeof(pop3->msgfrom));\n\t\t\t\telse if (!strnicmp(pop3->readbuffer, \"Subject: \", 9))\n\t\t\t\t\tstrlcpy(pop3->msgsubject, pop3->readbuffer + 9, sizeof(pop3->msgsubject));\n\t\t\t\telse if (!strncmp(pop3->readbuffer, \".\", 1))\n\t\t\t\t{\n\t\t\t\t\tCon_Printf(\"New message:\\nFrom: %s\\nSubject: %s\\n\", pop3->msgfrom, pop3->msgsubject);\n\n\t\t\t\t\tif (BUILTINISVALID(SCR_CenterPrint))\n\t\t\t\t\t\tSCR_CenterPrint(va(\"NEW MAIL HAS ARRIVED\\n\\nTo: %s@%s\\nFrom: %s\\nSubject: %s\", pop3->username, pop3->server, pop3->msgfrom, pop3->msgsubject));\n\n\t\t\t\t\tif (!pop3->numtoretrieve)\n\t\t\t\t\t{\n\t\t\t\t\t\tpop3->state = POP3_QUITTING;\n\t\t\t\t\t\tPOP3_EmitCommand(pop3, \"QUIT\");\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tpop3->state = POP3_RETRIEVING;\n\t\t\t\t\t\tPOP3_EmitCommand(pop3, va(\"RETR %i\", pop3->retrlist[--pop3->numtoretrieve]));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse if (!*pop3->readbuffer)\n\t\t\t\t\tpop3->state = POP3_BODY;\n\t\t\t}\n\t\t\telse if (pop3->state == POP3_BODY)\n\t\t\t{\n\t\t\t\tif (!strncmp(pop3->readbuffer, \"..\", 2))\n\t\t\t\t{\n\t\t\t\t\t//line of text, skipping first '.'\n\t\t\t\t\tCon_Printf(\"%s\\n\", pop3->readbuffer+1);\n\t\t\t\t}\n\t\t\t\telse if (!strncmp(pop3->readbuffer, \".\", 1))\n\t\t\t\t{\n\t\t\t\t\tCon_Printf(\"New message:\\nFrom: %s\\nSubject: %s\\n\", pop3->msgfrom, pop3->msgsubject);\n\t\t\t\t\tif (BUILTINISVALID(SCR_CenterPrint))\n\t\t\t\t\t\tSCR_CenterPrint(va(\"NEW MAIL HAS ARRIVED\\n\\nTo: %s@%s\\nFrom: %s\\nSubject: %s\", pop3->username, pop3->server, pop3->msgfrom, pop3->msgsubject));\n\n\t\t\t\t\tif (!pop3->numtoretrieve)\n\t\t\t\t\t{\n\t\t\t\t\t\tpop3->state = POP3_QUITTING;\n\t\t\t\t\t\tPOP3_EmitCommand(pop3, \"QUIT\");\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tpop3->state = POP3_RETRIEVING;\n\t\t\t\t\t\tPOP3_EmitCommand(pop3, va(\"RETR %i\", pop3->retrlist[--pop3->numtoretrieve]));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t//normal line of text\n\t\t\t\t\tCon_Printf(\"%s\\n\", pop3->readbuffer);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (pop3->state == POP3_QUITTING)\n\t\t\t{\n\t\t\t\tpop3->state = POP3_NOTCONNECTED;\n\t\t\t\tNet_Close(pop3->socket);\n\t\t\t\tpop3->lastnoop = Sys_Milliseconds();\n\t\t\t\tpop3->socket = 0;\n\t\t\t\tpop3->readlen = 0;\n\t\t\t\tpop3->sendlen = 0;\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tCon_Printf(\"Bad client state\\n\");\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tpop3->readlen -= ending - pop3->readbuffer;\n\t\t\tmemmove(pop3->readbuffer, ending, strlen(ending)+1);\n\t\t}\n\t}\n\tif (pop3->drop)\n\t\treturn false;\n\n\tif (pop3->sendlen)\n\t{\n\t\tlen = Net_Send(pop3->socket, pop3->sendbuffer, pop3->sendlen);\n\t\tif (len>0)\n\t\t{\n\t\t\tpop3->sendlen-=len;\n\t\t\tmemmove(pop3->sendbuffer, pop3->sendbuffer+len, pop3->sendlen+1);\n\t\t}\n\t}\n\treturn true;\n}\n\nvoid POP3_Think (void)\n{\n\tpop3_con_t *prev = NULL;\n\tpop3_con_t *pop3;\n\n\tfor (pop3 = pop3sv; pop3; pop3 = pop3->next)\n\t{\n\t\tif (pop3->drop || !POP3_ThinkCon(pop3))\n\t\t{\n\t\t\tif (!prev)\n\t\t\t\tpop3sv = pop3->next;\n\t\t\telse\n\t\t\t\tprev->next = pop3->next;\n\t\t\tif (pop3->socket)\n\t\t\t\tNet_Close(pop3->socket);\n\t\t\tfree(pop3);\n\t\t\tif (!prev)\n\t\t\t\tbreak;\n\t\t}\n\n\t\tprev = pop3;\n\t}\n}\n\n\nint EmailNotification_Frame(int *args)\n{\n\tPOP3_Think();\n\tIMAP_Think();\n\n\treturn 0;\n}\n\nvoid IMAP_Account(void)\n{\n\tchar arg1[64];\n\tchar arg2[64];\n\tchar arg3[64];\n\n\tCmd_Argv(1, arg1, sizeof(arg1));\n\tCmd_Argv(2, arg2, sizeof(arg2));\n\tCmd_Argv(3, arg3, sizeof(arg3));\n\n\tif (!*arg1)\n\t{\n\t\tCon_Printf(\"imapaccount <servername> <username> <password>\\n\");\n\t}\n\telse\n\t\tIMAP_CreateConnection(arg1, arg2, arg3);\n}\nvoid POP3_Account(void)\n{\n\tchar arg1[64];\n\tchar arg2[64];\n\tchar arg3[64];\n\n\tCmd_Argv(1, arg1, sizeof(arg1));\n\tCmd_Argv(2, arg2, sizeof(arg2));\n\tCmd_Argv(3, arg3, sizeof(arg3));\n\n\tif (!*arg1)\n\t{\n\t\tCon_Printf(\"pop3account <servername> <username> <password>\\n\");\n\t\tCon_Printf(\"Say you had an acount called \\\"foo\\\" at yahoo's mail servers\\n\");\n\t\tCon_Printf(\"Yahoo's pop3 servers are named \\\"pop.mail.yahoo.co.uk\\\"\\n\");\n\t\tCon_Printf(\"Then if your password was bar, this is the command you would use\\n\");\n\t\tCon_Printf(\"pop3account pop.mail.yahoo.co.uk foo bar\\n\");\n\t\tCon_Printf(\"Of course, different pop3 servers have different naming conventions\\n\");\n\t\tCon_Printf(\"So read your provider's documentation\\n\");\n\t}\n\telse\n\t\tPOP3_CreateConnection(arg1, arg2, arg3);\n}\n\nint EmailNotification_ExecuteCommand(int *args)\n{\n\tchar cmd[64];\n\tCmd_Argv(0, cmd, sizeof(cmd));\n\tif (!strcmp(cmd, \"imapaccount\"))\n\t{\n\t\tIMAP_Account();\n\t\treturn true;\n\t}\n\tif (!strcmp(cmd, \"pop3account\"))\n\t{\n\t\tPOP3_Account();\n\t\treturn true;\n\t}\n\treturn false;\n}\n\nint Plug_Init(int *args)\n{\n\tif (!Plug_Export(\"Tick\", EmailNotification_Frame) || !Plug_Export(\"ExecuteCommand\", EmailNotification_ExecuteCommand))\n\t{\n\t\tCon_Print(\"email notification plugin failed\\n\");\n\t\treturn false;\n\t}\n\n\tCmd_AddCommand(\"imapaccount\");\n\tCmd_AddCommand(\"pop3account\");\n\n\tCon_Print(\"email notification plugin loaded\\n\");\n\n\treturn true;\n}\n"
  },
  {
    "path": "plugins/engine.h",
    "content": "#ifndef FTEPLUGIN\r\n#ifndef VARGS\r\n#define VARGS QDECL\r\n#endif\r\ntypedef enum uploadfmt_e\r\n{\r\n\tTF_INVALID,\r\n\tTF_RGBA32,\r\n\tTF_BGRA32,\r\n\tTF_RGBX32,\r\n\tTF_BGRX32,\r\n\tTF_RGB24,\r\n\tTF_BGR24\r\n} uploadfmt_t;\r\n\r\ntypedef struct\r\n{\r\n\tsize_t structsize;\r\n\tconst char *drivername;\r\n\tvoid *(VARGS *createdecoder)(const char *name);\r\n\tqboolean (VARGS *decodeframe)(void *ctx, qboolean nosound, qboolean forcevideo, double mediatime, void (QDECL *uploadtexture)(void *ectx, uploadfmt_t fmt, int width, int height, void *data, void *palette), void *ectx);\r\n\tvoid (VARGS *shutdown)(void *ctx);\r\n\tvoid (VARGS *rewind)(void *ctx);\r\n\r\n\t//these are any interactivity functions you might want...\r\n\tvoid (VARGS *cursormove) (void *ctx, float posx, float posy);\t//pos is 0-1\r\n\tvoid (VARGS *key) (void *ctx, int code, int unicode, int event);\r\n\tqboolean (VARGS *setsize) (void *ctx, int width, int height);\r\n\tvoid (VARGS *getsize) (void *ctx, int *width, int *height);\r\n\tvoid (VARGS *changestream) (void *ctx, const char *streamname);\r\n\r\n\tsize_t (VARGS *gettext) (void *ctx, const char *field, char *out, size_t outlen);\t//if out is null, returns required buffer size. returns 0 on failure / buffer too small\r\n} media_decoder_funcs_t;\r\ntypedef struct\r\n{\r\n\tsize_t structsize;\r\n\tconst char *drivername;\r\n\tconst char *description;\r\n\tconst char *defaultextension;\r\n\tvoid *(VARGS *capture_begin) (char *streamname, int videorate, int width, int height, int *sndkhz, int *sndchannels, int *sndbits);\r\n\tvoid (VARGS *capture_video) (void *ctx, void *data, int frame, int width, int height, enum uploadfmt fmt);\r\n\tvoid (VARGS *capture_audio) (void *ctx, void *data, int bytes);\r\n\tvoid (VARGS *capture_end) (void *ctx);\r\n} media_encoder_funcs_t;\r\n#endif\r\n\r\n"
  },
  {
    "path": "plugins/ezhud/builtin_huds.h",
    "content": "/*\r\nWARNING: THIS FILE IS GENERATED BY 'generatebuiltin.c'.\r\nYOU SHOULD NOT EDIT THIS FILE BY HAND\r\n*/\r\n\r\nconst char *builtin_hud_nquake =\r\n//fixme: sanitise markup\r\n\r\n\"hud_ammo1_align                      \\\"center\\\"\\n\"\r\n\"hud_ammo1_align_x                    \\\"center\\\"\\n\"\r\n\"hud_ammo1_align_y                    \\\"after\\\"\\n\"\r\n\"hud_ammo1_digits                     \\\"3\\\"\\n\"\r\n\"hud_ammo1_frame                      \\\"0\\\"\\n\"\r\n\"hud_ammo1_frame_color                \\\"0 0 0\\\"\\n\"\r\n\"hud_ammo1_item_opacity               \\\"0.5\\\"\\n\"\r\n\"hud_ammo1_order                      \\\"16\\\"\\n\"\r\n\"hud_ammo1_place                      \\\"gun2\\\"\\n\"\r\n\"hud_ammo1_pos_x                      \\\"0\\\"\\n\"\r\n\"hud_ammo1_pos_y                      \\\"2\\\"\\n\"\r\n\"hud_ammo1_scale                      \\\"0.4\\\"\\n\"\r\n\"hud_ammo1_show                       \\\"1\\\"\\n\"\r\n\"hud_ammo1_style                      \\\"0\\\"\\n\"\r\n\"hud_ammo2_align                      \\\"center\\\"\\n\"\r\n\"hud_ammo2_align_x                    \\\"center\\\"\\n\"\r\n\"hud_ammo2_align_y                    \\\"after\\\"\\n\"\r\n\"hud_ammo2_digits                     \\\"3\\\"\\n\"\r\n\"hud_ammo2_frame                      \\\"0\\\"\\n\"\r\n\"hud_ammo2_frame_color                \\\"0 0 0\\\"\\n\"\r\n\"hud_ammo2_item_opacity               \\\"0.5\\\"\\n\"\r\n\"hud_ammo2_order                      \\\"14\\\"\\n\"\r\n\"hud_ammo2_place                      \\\"gun4\\\"\\n\"\r\n\"hud_ammo2_pos_x                      \\\"0\\\"\\n\"\r\n\"hud_ammo2_pos_y                      \\\"2\\\"\\n\"\r\n\"hud_ammo2_scale                      \\\"0.4\\\"\\n\"\r\n\"hud_ammo2_show                       \\\"1\\\"\\n\"\r\n\"hud_ammo2_style                      \\\"0\\\"\\n\"\r\n\"hud_ammo3_align                      \\\"center\\\"\\n\"\r\n\"hud_ammo3_align_x                    \\\"center\\\"\\n\"\r\n\"hud_ammo3_align_y                    \\\"after\\\"\\n\"\r\n\"hud_ammo3_digits                     \\\"3\\\"\\n\"\r\n\"hud_ammo3_frame                      \\\"0\\\"\\n\"\r\n\"hud_ammo3_frame_color                \\\"0 0 0\\\"\\n\"\r\n\"hud_ammo3_item_opacity               \\\"0.5\\\"\\n\"\r\n\"hud_ammo3_order                      \\\"14\\\"\\n\"\r\n\"hud_ammo3_place                      \\\"gun6\\\"\\n\"\r\n\"hud_ammo3_pos_x                      \\\"0\\\"\\n\"\r\n\"hud_ammo3_pos_y                      \\\"2\\\"\\n\"\r\n\"hud_ammo3_scale                      \\\"0.4\\\"\\n\"\r\n\"hud_ammo3_show                       \\\"1\\\"\\n\"\r\n\"hud_ammo3_style                      \\\"0\\\"\\n\"\r\n\"hud_ammo4_align                      \\\"center\\\"\\n\"\r\n\"hud_ammo4_align_x                    \\\"center\\\"\\n\"\r\n\"hud_ammo4_align_y                    \\\"after\\\"\\n\"\r\n\"hud_ammo4_digits                     \\\"3\\\"\\n\"\r\n\"hud_ammo4_frame                      \\\"0\\\"\\n\"\r\n\"hud_ammo4_frame_color                \\\"0 0 0\\\"\\n\"\r\n\"hud_ammo4_item_opacity               \\\"0.5\\\"\\n\"\r\n\"hud_ammo4_order                      \\\"16\\\"\\n\"\r\n\"hud_ammo4_place                      \\\"gun8\\\"\\n\"\r\n\"hud_ammo4_pos_x                      \\\"0\\\"\\n\"\r\n\"hud_ammo4_pos_y                      \\\"2\\\"\\n\"\r\n\"hud_ammo4_scale                      \\\"0.4\\\"\\n\"\r\n\"hud_ammo4_show                       \\\"1\\\"\\n\"\r\n\"hud_ammo4_style                      \\\"0\\\"\\n\"\r\n\"hud_ammo_align                       \\\"right\\\"\\n\"\r\n\"hud_ammo_align_x                     \\\"after\\\"\\n\"\r\n\"hud_ammo_align_y                     \\\"bottom\\\"\\n\"\r\n\"hud_ammo_digits                      \\\"3\\\"\\n\"\r\n\"hud_ammo_frame                       \\\"0\\\"\\n\"\r\n\"hud_ammo_frame_color                 \\\"0 0 0\\\"\\n\"\r\n\"hud_ammo_item_opacity                \\\"0.99\\\"\\n\"\r\n\"hud_ammo_order                       \\\"30\\\"\\n\"\r\n\"hud_ammo_place                       \\\"health\\\"\\n\"\r\n\"hud_ammo_pos_x                       \\\"5\\\"\\n\"\r\n\"hud_ammo_pos_y                       \\\"0\\\"\\n\"\r\n\"hud_ammo_scale                       \\\"0.9\\\"\\n\"\r\n\"hud_ammo_show                        \\\"0\\\"\\n\"\r\n\"hud_ammo_style                       \\\"0\\\"\\n\"\r\n\"hud_armor_align                      \\\"right\\\"\\n\"\r\n\"hud_armor_align_x                    \\\"right\\\"\\n\"\r\n\"hud_armor_align_y                    \\\"center\\\"\\n\"\r\n\"hud_armor_digits                     \\\"3\\\"\\n\"\r\n\"hud_armor_frame                      \\\"0\\\"\\n\"\r\n\"hud_armor_frame_color                \\\"0 0 0\\\"\\n\"\r\n\"hud_armor_item_opacity               \\\"0.95\\\"\\n\"\r\n\"hud_armor_order                      \\\"5\\\"\\n\"\r\n\"hud_armor_pent_666                   \\\"1\\\"\\n\"\r\n\"hud_armor_place                      \\\"bar_armor\\\"\\n\"\r\n\"hud_armor_pos_x                      \\\"-33\\\"\\n\"\r\n\"hud_armor_pos_y                      \\\"2\\\"\\n\"\r\n\"hud_armor_scale                      \\\"1\\\"\\n\"\r\n\"hud_armor_show                       \\\"1\\\"\\n\"\r\n\"hud_armor_style                      \\\"0\\\"\\n\"\r\n/*\r\n\"hud_armordamage_align                \\\"right\\\"\\n\"\r\n\"hud_armordamage_align_x              \\\"left\\\"\\n\"\r\n\"hud_armordamage_align_y              \\\"before\\\"\\n\"\r\n\"hud_armordamage_digits               \\\"3\\\"\\n\"\r\n\"hud_armordamage_duration             \\\"0.8\\\"\\n\"\r\n\"hud_armordamage_frame                \\\"0\\\"\\n\"\r\n\"hud_armordamage_frame_color          \\\"0 0 0\\\"\\n\"\r\n\"hud_armordamage_item_opacity         \\\"0.99\\\"\\n\"\r\n\"hud_armordamage_order                \\\"6\\\"\\n\"\r\n\"hud_armordamage_place                \\\"armor\\\"\\n\"\r\n\"hud_armordamage_pos_x                \\\"0\\\"\\n\"\r\n\"hud_armordamage_pos_y                \\\"0\\\"\\n\"\r\n\"hud_armordamage_scale                \\\"1\\\"\\n\"\r\n\"hud_armordamage_show                 \\\"0\\\"\\n\"\r\n\"hud_armordamage_style                \\\"0\\\"\\n\"\r\n*/\r\n\"hud_bar_armor_align_x                \\\"before\\\"\\n\"\r\n\"hud_bar_armor_align_y                \\\"center\\\"\\n\"\r\n\"hud_bar_armor_color_ga               \\\"32 128 0 128\\\"\\n\"\r\n\"hud_bar_armor_color_noarmor          \\\"128 128 128 64\\\"\\n\"\r\n\"hud_bar_armor_color_ra               \\\"128 0 0 128\\\"\\n\"\r\n\"hud_bar_armor_color_unnatural        \\\"128 128 128\\\"\\n\"\r\n\"hud_bar_armor_color_ya               \\\"192 128 0 128\\\"\\n\"\r\n\"hud_bar_armor_direction              \\\"1\\\"\\n\"\r\n\"hud_bar_armor_frame                  \\\"0\\\"\\n\"\r\n\"hud_bar_armor_frame_color            \\\"0 0 0\\\"\\n\"\r\n\"hud_bar_armor_height                 \\\"16\\\"\\n\"\r\n\"hud_bar_armor_item_opacity           \\\"0.99\\\"\\n\"\r\n\"hud_bar_armor_order                  \\\"4\\\"\\n\"\r\n\"hud_bar_armor_place                  \\\"group1\\\"\\n\"\r\n\"hud_bar_armor_pos_x                  \\\"-10\\\"\\n\"\r\n\"hud_bar_armor_pos_y                  \\\"0\\\"\\n\"\r\n\"hud_bar_armor_show                   \\\"1\\\"\\n\"\r\n\"hud_bar_armor_width                  \\\"200\\\"\\n\"\r\n\"hud_bar_health_align_x               \\\"after\\\"\\n\"\r\n\"hud_bar_health_align_y               \\\"center\\\"\\n\"\r\n\"hud_bar_health_color_mega            \\\"64 96 128 128\\\"\\n\"\r\n\"hud_bar_health_color_nohealth        \\\"128 128 128 64\\\"\\n\"\r\n\"hud_bar_health_color_normal          \\\"32 64 128 128\\\"\\n\"\r\n\"hud_bar_health_color_twomega         \\\"128 128 255 128\\\"\\n\"\r\n\"hud_bar_health_color_unnatural       \\\"255 255 255 128\\\"\\n\"\r\n\"hud_bar_health_direction             \\\"0\\\"\\n\"\r\n\"hud_bar_health_frame                 \\\"0\\\"\\n\"\r\n\"hud_bar_health_frame_color           \\\"0 0 0\\\"\\n\"\r\n\"hud_bar_health_height                \\\"16\\\"\\n\"\r\n\"hud_bar_health_item_opacity          \\\"0.99\\\"\\n\"\r\n\"hud_bar_health_order                 \\\"2\\\"\\n\"\r\n\"hud_bar_health_place                 \\\"group1\\\"\\n\"\r\n\"hud_bar_health_pos_x                 \\\"10\\\"\\n\"\r\n\"hud_bar_health_pos_y                 \\\"0\\\"\\n\"\r\n\"hud_bar_health_show                  \\\"1\\\"\\n\"\r\n\"hud_bar_health_width                 \\\"200\\\"\\n\"\r\n\"hud_clock_align_x                    \\\"right\\\"\\n\"\r\n\"hud_clock_align_y                    \\\"console\\\"\\n\"\r\n\"hud_clock_big                        \\\"0\\\"\\n\"\r\n\"hud_clock_blink                      \\\"0\\\"\\n\"\r\n\"hud_clock_format                     \\\"3\\\"\\n\"\r\n\"hud_clock_frame                      \\\"0\\\"\\n\"\r\n\"hud_clock_frame_color                \\\"0 0 0\\\"\\n\"\r\n\"hud_clock_item_opacity               \\\"0.99\\\"\\n\"\r\n\"hud_clock_order                      \\\"8\\\"\\n\"\r\n\"hud_clock_place                      \\\"screen\\\"\\n\"\r\n\"hud_clock_pos_x                      \\\"1\\\"\\n\"\r\n\"hud_clock_pos_y                      \\\"0\\\"\\n\"\r\n\"hud_clock_scale                      \\\"1.000013\\\"\\n\"\r\n\"hud_clock_show                       \\\"1\\\"\\n\"\r\n\"hud_clock_style                      \\\"0\\\"\\n\"\r\n\"hud_democlock_align_x                \\\"right\\\"\\n\"\r\n\"hud_democlock_align_y                \\\"middle\\\"\\n\"\r\n\"hud_democlock_big                    \\\"0\\\"\\n\"\r\n\"hud_democlock_blink                  \\\"0\\\"\\n\"\r\n\"hud_democlock_frame                  \\\"0\\\"\\n\"\r\n\"hud_democlock_frame_color            \\\"0 0 0\\\"\\n\"\r\n\"hud_democlock_item_opacity           \\\"0.99\\\"\\n\"\r\n\"hud_democlock_order                  \\\"11\\\"\\n\"\r\n\"hud_democlock_place                  \\\"fps\\\"\\n\"\r\n\"hud_democlock_pos_x                  \\\"0\\\"\\n\"\r\n\"hud_democlock_pos_y                  \\\"8\\\"\\n\"\r\n\"hud_democlock_scale                  \\\"1\\\"\\n\"\r\n\"hud_democlock_show                   \\\"0\\\"\\n\"\r\n\"hud_democlock_style                  \\\"0\\\"\\n\"\r\n\"hud_digits_trim                      \\\"1\\\"\\n\"\r\n\"hud_editor_allowalign                \\\"1\\\"\\n\"\r\n\"hud_editor_allowmove                 \\\"1\\\"\\n\"\r\n\"hud_editor_allowplace                \\\"1\\\"\\n\"\r\n\"hud_editor_allowresize               \\\"1\\\"\\n\"\r\n\"hud_face_align_x                     \\\"before\\\"\\n\"\r\n\"hud_face_align_y                     \\\"bottom\\\"\\n\"\r\n\"hud_face_frame                       \\\"0\\\"\\n\"\r\n\"hud_face_frame_color                 \\\"0 0 0\\\"\\n\"\r\n\"hud_face_item_opacity                \\\"0.99\\\"\\n\"\r\n\"hud_face_order                       \\\"30\\\"\\n\"\r\n\"hud_face_place                       \\\"health\\\"\\n\"\r\n\"hud_face_pos_x                       \\\"0\\\"\\n\"\r\n\"hud_face_pos_y                       \\\"0\\\"\\n\"\r\n\"hud_face_scale                       \\\"0.84\\\"\\n\"\r\n\"hud_face_show                        \\\"0\\\"\\n\"\r\n\"hud_fps_align_x                      \\\"before\\\"\\n\"\r\n\"hud_fps_align_y                      \\\"center\\\"\\n\"\r\n/*\r\n\"hud_fps_decimals                     \\\"0\\\"\\n\"\r\n*/\r\n\"hud_fps_drop                         \\\"70\\\"\\n\"\r\n\"hud_fps_frame                        \\\"0\\\"\\n\"\r\n\"hud_fps_frame_color                  \\\"0 0 0\\\"\\n\"\r\n\"hud_fps_item_opacity                 \\\"0.99\\\"\\n\"\r\n\"hud_fps_order                        \\\"10\\\"\\n\"\r\n\"hud_fps_place                        \\\"ping\\\"\\n\"\r\n\"hud_fps_pos_x                        \\\"-10\\\"\\n\"\r\n\"hud_fps_pos_y                        \\\"0\\\"\\n\"\r\n\"hud_fps_show                         \\\"1\\\"\\n\"\r\n\"hud_fps_show_min                     \\\"0\\\"\\n\"\r\n\"hud_fps_style                        \\\"0\\\"\\n\"\r\n\"hud_fps_title                        \\\"1\\\"\\n\"\r\n\"hud_frags_align_x                    \\\"center\\\"\\n\"\r\n\"hud_frags_align_y                    \\\"bottom\\\"\\n\"\r\n\"hud_frags_bignum                     \\\"0\\\"\\n\"\r\n\"hud_frags_cell_height                \\\"8\\\"\\n\"\r\n\"hud_frags_cell_width                 \\\"28\\\"\\n\"\r\n\"hud_frags_colors_alpha               \\\"1.0\\\"\\n\"\r\n\"hud_frags_cols                       \\\"8\\\"\\n\"\r\n\"hud_frags_extra_spec_info            \\\"ALL\\\"\\n\"\r\n\"hud_frags_fliptext                   \\\"0\\\"\\n\"\r\n\"hud_frags_frame                      \\\"0\\\"\\n\"\r\n\"hud_frags_frame_color                \\\"0 0 0\\\"\\n\"\r\n\"hud_frags_item_opacity               \\\"0.99\\\"\\n\"\r\n\"hud_frags_maxname                    \\\"16\\\"\\n\"\r\n\"hud_frags_notintp                    \\\"0\\\"\\n\"\r\n\"hud_frags_order                      \\\"0\\\"\\n\"\r\n\"hud_frags_padtext                    \\\"1\\\"\\n\"\r\n\"hud_frags_place                      \\\"screen\\\"\\n\"\r\n\"hud_frags_pos_x                      \\\"0\\\"\\n\"\r\n\"hud_frags_pos_y                      \\\"-80\\\"\\n\"\r\n\"hud_frags_rows                       \\\"1\\\"\\n\"\r\n\"hud_frags_show                       \\\"0\\\"\\n\"\r\n\"hud_frags_shownames                  \\\"0\\\"\\n\"\r\n\"hud_frags_showself_always            \\\"1\\\"\\n\"\r\n\"hud_frags_showteams                  \\\"0\\\"\\n\"\r\n\"hud_frags_space_x                    \\\"1\\\"\\n\"\r\n\"hud_frags_space_y                    \\\"1\\\"\\n\"\r\n\"hud_frags_strip                      \\\"1\\\"\\n\"\r\n\"hud_frags_style                      \\\"2\\\"\\n\"\r\n\"hud_frags_teamsort                   \\\"1\\\"\\n\"\r\n\"hud_frags_vertical                   \\\"0\\\"\\n\"\r\n\"hud_gameclock_align_x                \\\"center\\\"\\n\"\r\n\"hud_gameclock_align_y                \\\"top\\\"\\n\"\r\n\"hud_gameclock_big                    \\\"1\\\"\\n\"\r\n\"hud_gameclock_blink                  \\\"0\\\"\\n\"\r\n\"hud_gameclock_countdown              \\\"1\\\"\\n\"\r\n\"hud_gameclock_frame                  \\\"0\\\"\\n\"\r\n\"hud_gameclock_frame_color            \\\"0 0 0\\\"\\n\"\r\n\"hud_gameclock_item_opacity           \\\"0.95\\\"\\n\"\r\n\"hud_gameclock_offset                 \\\"0\\\"\\n\"\r\n\"hud_gameclock_order                  \\\"8\\\"\\n\"\r\n\"hud_gameclock_place                  \\\"screen\\\"\\n\"\r\n\"hud_gameclock_pos_x                  \\\"0\\\"\\n\"\r\n\"hud_gameclock_pos_y                  \\\"10\\\"\\n\"\r\n\"hud_gameclock_scale                  \\\"1\\\"\\n\"\r\n\"hud_gameclock_show                   \\\"1\\\"\\n\"\r\n\"hud_gameclock_style                  \\\"0\\\"\\n\"\r\n\"hud_group1_align_x                   \\\"center\\\"\\n\"\r\n\"hud_group1_align_y                   \\\"bottom\\\"\\n\"\r\n\"hud_group1_frame                     \\\"0.1\\\"\\n\"\r\n\"hud_group1_frame_color               \\\"100 100 100\\\"\\n\"\r\n\"hud_group1_height                    \\\"26\\\"\\n\"\r\n\"hud_group1_item_opacity              \\\"0.99\\\"\\n\"\r\n\"hud_group1_name                      \\\"group1\\\"\\n\"\r\n\"hud_group1_order                     \\\"1\\\"\\n\"\r\n\"hud_group1_pic_alpha                 \\\"1.0\\\"\\n\"\r\n\"hud_group1_pic_scalemode             \\\"0\\\"\\n\"\r\n\"hud_group1_picture                   \\\"\\\"\\n\"\r\n\"hud_group1_place                     \\\"screen\\\"\\n\"\r\n\"hud_group1_pos_x                     \\\"0\\\"\\n\"\r\n\"hud_group1_pos_y                     \\\"-5\\\"\\n\"\r\n\"hud_group1_show                      \\\"1\\\"\\n\"\r\n\"hud_group1_width                     \\\"2\\\"\\n\"\r\n\"hud_group2_align_x                   \\\"center\\\"\\n\"\r\n\"hud_group2_align_y                   \\\"before\\\"\\n\"\r\n\"hud_group2_frame                     \\\"0.1\\\"\\n\"\r\n\"hud_group2_frame_color               \\\"100 100 100\\\"\\n\"\r\n\"hud_group2_height                    \\\"2\\\"\\n\"\r\n\"hud_group2_item_opacity              \\\"0.99\\\"\\n\"\r\n\"hud_group2_name                      \\\"group2\\\"\\n\"\r\n\"hud_group2_order                     \\\"2\\\"\\n\"\r\n\"hud_group2_pic_alpha                 \\\"1.0\\\"\\n\"\r\n\"hud_group2_pic_scalemode             \\\"0\\\"\\n\"\r\n\"hud_group2_picture                   \\\"\\\"\\n\"\r\n\"hud_group2_place                     \\\"group1\\\"\\n\"\r\n\"hud_group2_pos_x                     \\\"0\\\"\\n\"\r\n\"hud_group2_pos_y                     \\\"-6\\\"\\n\"\r\n\"hud_group2_show                      \\\"1\\\"\\n\"\r\n\"hud_group2_width                     \\\"325\\\"\\n\"\r\n\"hud_group3_align_x                   \\\"center\\\"\\n\"\r\n\"hud_group3_align_y                   \\\"before\\\"\\n\"\r\n\"hud_group3_frame                     \\\"0\\\"\\n\"\r\n\"hud_group3_frame_color               \\\"0 0 0\\\"\\n\"\r\n\"hud_group3_height                    \\\"32\\\"\\n\"\r\n\"hud_group3_item_opacity              \\\"0.99\\\"\\n\"\r\n\"hud_group3_name                      \\\"group2\\\"\\n\"\r\n\"hud_group3_order                     \\\"3\\\"\\n\"\r\n\"hud_group3_pic_alpha                 \\\"0.1\\\"\\n\"\r\n\"hud_group3_pic_scalemode             \\\"2\\\"\\n\"\r\n\"hud_group3_picture                   \\\"weaplist_full\\\"\\n\"\r\n\"hud_group3_place                     \\\"group2\\\"\\n\"\r\n\"hud_group3_pos_x                     \\\"0\\\"\\n\"\r\n\"hud_group3_pos_y                     \\\"0\\\"\\n\"\r\n\"hud_group3_show                      \\\"1\\\"\\n\"\r\n\"hud_group3_width                     \\\"336\\\"\\n\"\r\n\"hud_group4_align_x                   \\\"center\\\"\\n\"\r\n\"hud_group4_align_y                   \\\"center\\\"\\n\"\r\n\"hud_group4_frame                     \\\".5\\\"\\n\"\r\n\"hud_group4_frame_color               \\\"0 0 0\\\"\\n\"\r\n\"hud_group4_height                    \\\"10\\\"\\n\"\r\n\"hud_group4_item_opacity              \\\"0.99\\\"\\n\"\r\n\"hud_group4_name                      \\\"group2\\\"\\n\"\r\n\"hud_group4_order                     \\\"14\\\"\\n\"\r\n\"hud_group4_pic_alpha                 \\\"1.0\\\"\\n\"\r\n\"hud_group4_pic_scalemode             \\\"0\\\"\\n\"\r\n\"hud_group4_picture                   \\\"\\\"\\n\"\r\n\"hud_group4_place                     \\\"gun6\\\"\\n\"\r\n\"hud_group4_pos_x                     \\\"-8\\\"\\n\"\r\n\"hud_group4_pos_y                     \\\"-1\\\"\\n\"\r\n\"hud_group4_show                      \\\"0\\\"\\n\"\r\n\"hud_group4_width                     \\\"60\\\"\\n\"\r\n\"hud_group5_align_x                   \\\"center\\\"\\n\"\r\n\"hud_group5_align_y                   \\\"center\\\"\\n\"\r\n\"hud_group5_frame                     \\\".5\\\"\\n\"\r\n\"hud_group5_frame_color               \\\"0 0 0\\\"\\n\"\r\n\"hud_group5_height                    \\\"10\\\"\\n\"\r\n\"hud_group5_item_opacity              \\\"0.99\\\"\\n\"\r\n\"hud_group5_name                      \\\"group2\\\"\\n\"\r\n\"hud_group5_order                     \\\"16\\\"\\n\"\r\n\"hud_group5_pic_alpha                 \\\"1.0\\\"\\n\"\r\n\"hud_group5_pic_scalemode             \\\"0\\\"\\n\"\r\n\"hud_group5_picture                   \\\"\\\"\\n\"\r\n\"hud_group5_place                     \\\"gun8\\\"\\n\"\r\n\"hud_group5_pos_x                     \\\"-8\\\"\\n\"\r\n\"hud_group5_pos_y                     \\\"-1\\\"\\n\"\r\n\"hud_group5_show                      \\\"0\\\"\\n\"\r\n\"hud_group5_width                     \\\"60\\\"\\n\"\r\n\"hud_group6_align_x                   \\\"left\\\"\\n\"\r\n\"hud_group6_align_y                   \\\"top\\\"\\n\"\r\n\"hud_group6_frame                     \\\".5\\\"\\n\"\r\n\"hud_group6_frame_color               \\\"0 0 0\\\"\\n\"\r\n\"hud_group6_height                    \\\"64\\\"\\n\"\r\n\"hud_group6_item_opacity              \\\"0.99\\\"\\n\"\r\n\"hud_group6_name                      \\\"group6\\\"\\n\"\r\n\"hud_group6_order                     \\\"0\\\"\\n\"\r\n\"hud_group6_pic_alpha                 \\\"1.0\\\"\\n\"\r\n\"hud_group6_pic_scalemode             \\\"0\\\"\\n\"\r\n\"hud_group6_picture                   \\\"\\\"\\n\"\r\n\"hud_group6_place                     \\\"screen\\\"\\n\"\r\n\"hud_group6_pos_x                     \\\"0\\\"\\n\"\r\n\"hud_group6_pos_y                     \\\"0\\\"\\n\"\r\n\"hud_group6_show                      \\\"0\\\"\\n\"\r\n\"hud_group6_width                     \\\"64\\\"\\n\"\r\n\"hud_group7_align_x                   \\\"left\\\"\\n\"\r\n\"hud_group7_align_y                   \\\"top\\\"\\n\"\r\n\"hud_group7_frame                     \\\".5\\\"\\n\"\r\n\"hud_group7_frame_color               \\\"0 0 0\\\"\\n\"\r\n\"hud_group7_height                    \\\"64\\\"\\n\"\r\n\"hud_group7_item_opacity              \\\"0.99\\\"\\n\"\r\n\"hud_group7_name                      \\\"group7\\\"\\n\"\r\n\"hud_group7_order                     \\\"0\\\"\\n\"\r\n\"hud_group7_pic_alpha                 \\\"1.0\\\"\\n\"\r\n\"hud_group7_pic_scalemode             \\\"0\\\"\\n\"\r\n\"hud_group7_picture                   \\\"\\\"\\n\"\r\n\"hud_group7_place                     \\\"screen\\\"\\n\"\r\n\"hud_group7_pos_x                     \\\"0\\\"\\n\"\r\n\"hud_group7_pos_y                     \\\"0\\\"\\n\"\r\n\"hud_group7_show                      \\\"0\\\"\\n\"\r\n\"hud_group7_width                     \\\"64\\\"\\n\"\r\n\"hud_group8_align_x                   \\\"left\\\"\\n\"\r\n\"hud_group8_align_y                   \\\"top\\\"\\n\"\r\n\"hud_group8_frame                     \\\".5\\\"\\n\"\r\n\"hud_group8_frame_color               \\\"0 0 0\\\"\\n\"\r\n\"hud_group8_height                    \\\"64\\\"\\n\"\r\n\"hud_group8_item_opacity              \\\"0.99\\\"\\n\"\r\n\"hud_group8_name                      \\\"group8\\\"\\n\"\r\n\"hud_group8_order                     \\\"0\\\"\\n\"\r\n\"hud_group8_pic_alpha                 \\\"1.0\\\"\\n\"\r\n\"hud_group8_pic_scalemode             \\\"0\\\"\\n\"\r\n\"hud_group8_picture                   \\\"\\\"\\n\"\r\n\"hud_group8_place                     \\\"screen\\\"\\n\"\r\n\"hud_group8_pos_x                     \\\"0\\\"\\n\"\r\n\"hud_group8_pos_y                     \\\"0\\\"\\n\"\r\n\"hud_group8_show                      \\\"0\\\"\\n\"\r\n\"hud_group8_width                     \\\"64\\\"\\n\"\r\n\"hud_group9_align_x                   \\\"left\\\"\\n\"\r\n\"hud_group9_align_y                   \\\"top\\\"\\n\"\r\n\"hud_group9_frame                     \\\".5\\\"\\n\"\r\n\"hud_group9_frame_color               \\\"0 0 0\\\"\\n\"\r\n\"hud_group9_height                    \\\"64\\\"\\n\"\r\n\"hud_group9_item_opacity              \\\"0.99\\\"\\n\"\r\n\"hud_group9_name                      \\\"group9\\\"\\n\"\r\n\"hud_group9_order                     \\\"0\\\"\\n\"\r\n\"hud_group9_pic_alpha                 \\\"1.0\\\"\\n\"\r\n\"hud_group9_pic_scalemode             \\\"0\\\"\\n\"\r\n\"hud_group9_picture                   \\\"\\\"\\n\"\r\n\"hud_group9_place                     \\\"screen\\\"\\n\"\r\n\"hud_group9_pos_x                     \\\"0\\\"\\n\"\r\n\"hud_group9_pos_y                     \\\"0\\\"\\n\"\r\n\"hud_group9_show                      \\\"0\\\"\\n\"\r\n\"hud_group9_width                     \\\"64\\\"\\n\"\r\n\"hud_gun2_align_x                     \\\"before\\\"\\n\"\r\n\"hud_gun2_align_y                     \\\"center\\\"\\n\"\r\n\"hud_gun2_frame                       \\\"0\\\"\\n\"\r\n\"hud_gun2_frame_color                 \\\"0 0 0\\\"\\n\"\r\n\"hud_gun2_item_opacity                \\\"0.99\\\"\\n\"\r\n\"hud_gun2_order                       \\\"15\\\"\\n\"\r\n\"hud_gun2_place                       \\\"gun3\\\"\\n\"\r\n\"hud_gun2_pos_x                       \\\"0\\\"\\n\"\r\n\"hud_gun2_pos_y                       \\\"0\\\"\\n\"\r\n\"hud_gun2_scale                       \\\"2\\\"\\n\"\r\n\"hud_gun2_show                        \\\"1\\\"\\n\"\r\n\"hud_gun2_style                       \\\"0\\\"\\n\"\r\n\"hud_gun3_align_x                     \\\"before\\\"\\n\"\r\n\"hud_gun3_align_y                     \\\"center\\\"\\n\"\r\n\"hud_gun3_frame                       \\\"0\\\"\\n\"\r\n\"hud_gun3_frame_color                 \\\"0 0 0\\\"\\n\"\r\n\"hud_gun3_item_opacity                \\\"0.99\\\"\\n\"\r\n\"hud_gun3_order                       \\\"14\\\"\\n\"\r\n\"hud_gun3_place                       \\\"gun4\\\"\\n\"\r\n\"hud_gun3_pos_x                       \\\"0\\\"\\n\"\r\n\"hud_gun3_pos_y                       \\\"0\\\"\\n\"\r\n\"hud_gun3_scale                       \\\"2\\\"\\n\"\r\n\"hud_gun3_show                        \\\"1\\\"\\n\"\r\n\"hud_gun3_style                       \\\"0\\\"\\n\"\r\n\"hud_gun4_align_x                     \\\"before\\\"\\n\"\r\n\"hud_gun4_align_y                     \\\"center\\\"\\n\"\r\n\"hud_gun4_frame                       \\\"0\\\"\\n\"\r\n\"hud_gun4_frame_color                 \\\"0 0 0\\\"\\n\"\r\n\"hud_gun4_item_opacity                \\\"0.99\\\"\\n\"\r\n\"hud_gun4_order                       \\\"13\\\"\\n\"\r\n\"hud_gun4_place                       \\\"gun5\\\"\\n\"\r\n\"hud_gun4_pos_x                       \\\"0\\\"\\n\"\r\n\"hud_gun4_pos_y                       \\\"0\\\"\\n\"\r\n\"hud_gun4_scale                       \\\"2\\\"\\n\"\r\n\"hud_gun4_show                        \\\"1\\\"\\n\"\r\n\"hud_gun4_style                       \\\"0\\\"\\n\"\r\n\"hud_gun5_align_x                     \\\"center\\\"\\n\"\r\n\"hud_gun5_align_y                     \\\"center\\\"\\n\"\r\n\"hud_gun5_frame                       \\\"0\\\"\\n\"\r\n\"hud_gun5_frame_color                 \\\"0 0 0\\\"\\n\"\r\n\"hud_gun5_item_opacity                \\\"0.99\\\"\\n\"\r\n\"hud_gun5_order                       \\\"12\\\"\\n\"\r\n\"hud_gun5_place                       \\\"group3\\\"\\n\"\r\n\"hud_gun5_pos_x                       \\\"0\\\"\\n\"\r\n\"hud_gun5_pos_y                       \\\"0\\\"\\n\"\r\n\"hud_gun5_scale                       \\\"2\\\"\\n\"\r\n\"hud_gun5_show                        \\\"1\\\"\\n\"\r\n\"hud_gun5_style                       \\\"0\\\"\\n\"\r\n\"hud_gun6_align_x                     \\\"after\\\"\\n\"\r\n\"hud_gun6_align_y                     \\\"center\\\"\\n\"\r\n\"hud_gun6_frame                       \\\"0\\\"\\n\"\r\n\"hud_gun6_frame_color                 \\\"0 0 0\\\"\\n\"\r\n\"hud_gun6_item_opacity                \\\"0.99\\\"\\n\"\r\n\"hud_gun6_order                       \\\"13\\\"\\n\"\r\n\"hud_gun6_place                       \\\"gun5\\\"\\n\"\r\n\"hud_gun6_pos_x                       \\\"0\\\"\\n\"\r\n\"hud_gun6_pos_y                       \\\"0\\\"\\n\"\r\n\"hud_gun6_scale                       \\\"2\\\"\\n\"\r\n\"hud_gun6_show                        \\\"1\\\"\\n\"\r\n\"hud_gun6_style                       \\\"0\\\"\\n\"\r\n\"hud_gun7_align_x                     \\\"after\\\"\\n\"\r\n\"hud_gun7_align_y                     \\\"center\\\"\\n\"\r\n\"hud_gun7_frame                       \\\"0\\\"\\n\"\r\n\"hud_gun7_frame_color                 \\\"0 0 0\\\"\\n\"\r\n\"hud_gun7_item_opacity                \\\"0.99\\\"\\n\"\r\n\"hud_gun7_order                       \\\"14\\\"\\n\"\r\n\"hud_gun7_place                       \\\"gun6\\\"\\n\"\r\n\"hud_gun7_pos_x                       \\\"0\\\"\\n\"\r\n\"hud_gun7_pos_y                       \\\"0\\\"\\n\"\r\n\"hud_gun7_scale                       \\\"2\\\"\\n\"\r\n\"hud_gun7_show                        \\\"1\\\"\\n\"\r\n\"hud_gun7_style                       \\\"0\\\"\\n\"\r\n\"hud_gun8_align_x                     \\\"after\\\"\\n\"\r\n\"hud_gun8_align_y                     \\\"center\\\"\\n\"\r\n\"hud_gun8_frame                       \\\"0\\\"\\n\"\r\n\"hud_gun8_frame_color                 \\\"0 0 0\\\"\\n\"\r\n\"hud_gun8_item_opacity                \\\"0.99\\\"\\n\"\r\n\"hud_gun8_order                       \\\"15\\\"\\n\"\r\n\"hud_gun8_place                       \\\"gun7\\\"\\n\"\r\n\"hud_gun8_pos_x                       \\\"0\\\"\\n\"\r\n\"hud_gun8_pos_y                       \\\"0\\\"\\n\"\r\n\"hud_gun8_scale                       \\\"2\\\"\\n\"\r\n\"hud_gun8_show                        \\\"1\\\"\\n\"\r\n\"hud_gun8_style                       \\\"0\\\"\\n\"\r\n\"hud_gun8_wide                        \\\"0\\\"\\n\"\r\n\"hud_gun_align_x                      \\\"center\\\"\\n\"\r\n\"hud_gun_align_y                      \\\"bottom\\\"\\n\"\r\n\"hud_gun_frame                        \\\"0\\\"\\n\"\r\n\"hud_gun_frame_color                  \\\"0 0 0\\\"\\n\"\r\n\"hud_gun_item_opacity                 \\\"0.99\\\"\\n\"\r\n\"hud_gun_order                        \\\"0\\\"\\n\"\r\n\"hud_gun_place                        \\\"screen\\\"\\n\"\r\n\"hud_gun_pos_x                        \\\"center\\\"\\n\"\r\n\"hud_gun_pos_y                        \\\"-43\\\"\\n\"\r\n\"hud_gun_scale                        \\\"0.5\\\"\\n\"\r\n\"hud_gun_show                         \\\"0\\\"\\n\"\r\n\"hud_gun_style                        \\\"1\\\"\\n\"\r\n\"hud_gun_wide                         \\\"0\\\"\\n\"\r\n\"hud_health_align                     \\\"left\\\"\\n\"\r\n\"hud_health_align_x                   \\\"left\\\"\\n\"\r\n\"hud_health_align_y                   \\\"center\\\"\\n\"\r\n\"hud_health_digits                    \\\"3\\\"\\n\"\r\n\"hud_health_frame                     \\\"0\\\"\\n\"\r\n\"hud_health_frame_color               \\\"0 0 0\\\"\\n\"\r\n\"hud_health_item_opacity              \\\"0.95\\\"\\n\"\r\n\"hud_health_order                     \\\"29\\\"\\n\"\r\n\"hud_health_place                     \\\"bar_health\\\"\\n\"\r\n\"hud_health_pos_x                     \\\"20\\\"\\n\"\r\n\"hud_health_pos_y                     \\\"0\\\"\\n\"\r\n\"hud_health_scale                     \\\"1\\\"\\n\"\r\n\"hud_health_show                      \\\"1\\\"\\n\"\r\n\"hud_health_style                     \\\"0\\\"\\n\"\r\n/*\r\n\"hud_healthdamage_align               \\\"right\\\"\\n\"\r\n\"hud_healthdamage_align_x             \\\"left\\\"\\n\"\r\n\"hud_healthdamage_align_y             \\\"before\\\"\\n\"\r\n\"hud_healthdamage_digits              \\\"3\\\"\\n\"\r\n\"hud_healthdamage_duration            \\\"0.8\\\"\\n\"\r\n\"hud_healthdamage_frame               \\\"0\\\"\\n\"\r\n\"hud_healthdamage_frame_color         \\\"0 0 0\\\"\\n\"\r\n\"hud_healthdamage_item_opacity        \\\"0.99\\\"\\n\"\r\n\"hud_healthdamage_order               \\\"30\\\"\\n\"\r\n\"hud_healthdamage_place               \\\"health\\\"\\n\"\r\n\"hud_healthdamage_pos_x               \\\"0\\\"\\n\"\r\n\"hud_healthdamage_pos_y               \\\"0\\\"\\n\"\r\n\"hud_healthdamage_scale               \\\"1\\\"\\n\"\r\n\"hud_healthdamage_show                \\\"0\\\"\\n\"\r\n\"hud_healthdamage_style               \\\"0\\\"\\n\"\r\n*/\r\n\"hud_iammo1_align_x                   \\\"after\\\"\\n\"\r\n\"hud_iammo1_align_y                   \\\"top\\\"\\n\"\r\n\"hud_iammo1_frame                     \\\"0\\\"\\n\"\r\n\"hud_iammo1_frame_color               \\\"0 0 0\\\"\\n\"\r\n\"hud_iammo1_item_opacity              \\\"0.99\\\"\\n\"\r\n\"hud_iammo1_order                     \\\"17\\\"\\n\"\r\n\"hud_iammo1_place                     \\\"ammo1\\\"\\n\"\r\n\"hud_iammo1_pos_x                     \\\"1\\\"\\n\"\r\n\"hud_iammo1_pos_y                     \\\"1\\\"\\n\"\r\n\"hud_iammo1_scale                     \\\"0.25\\\"\\n\"\r\n\"hud_iammo1_show                      \\\"0\\\"\\n\"\r\n\"hud_iammo1_style                     \\\"0\\\"\\n\"\r\n\"hud_iammo2_align_x                   \\\"after\\\"\\n\"\r\n\"hud_iammo2_align_y                   \\\"top\\\"\\n\"\r\n\"hud_iammo2_frame                     \\\"0\\\"\\n\"\r\n\"hud_iammo2_frame_color               \\\"0 0 0\\\"\\n\"\r\n\"hud_iammo2_item_opacity              \\\"0.99\\\"\\n\"\r\n\"hud_iammo2_order                     \\\"18\\\"\\n\"\r\n\"hud_iammo2_place                     \\\"ammo2\\\"\\n\"\r\n\"hud_iammo2_pos_x                     \\\"1\\\"\\n\"\r\n\"hud_iammo2_pos_y                     \\\"1\\\"\\n\"\r\n\"hud_iammo2_scale                     \\\"0.25\\\"\\n\"\r\n\"hud_iammo2_show                      \\\"0\\\"\\n\"\r\n\"hud_iammo2_style                     \\\"0\\\"\\n\"\r\n\"hud_iammo3_align_x                   \\\"after\\\"\\n\"\r\n\"hud_iammo3_align_y                   \\\"top\\\"\\n\"\r\n\"hud_iammo3_frame                     \\\"0\\\"\\n\"\r\n\"hud_iammo3_frame_color               \\\"0 0 0\\\"\\n\"\r\n\"hud_iammo3_item_opacity              \\\"0.99\\\"\\n\"\r\n\"hud_iammo3_order                     \\\"19\\\"\\n\"\r\n\"hud_iammo3_place                     \\\"ammo3\\\"\\n\"\r\n\"hud_iammo3_pos_x                     \\\"1\\\"\\n\"\r\n\"hud_iammo3_pos_y                     \\\"1\\\"\\n\"\r\n\"hud_iammo3_scale                     \\\"0.25\\\"\\n\"\r\n\"hud_iammo3_show                      \\\"0\\\"\\n\"\r\n\"hud_iammo3_style                     \\\"0\\\"\\n\"\r\n\"hud_iammo4_align_x                   \\\"after\\\"\\n\"\r\n\"hud_iammo4_align_y                   \\\"top\\\"\\n\"\r\n\"hud_iammo4_frame                     \\\"0\\\"\\n\"\r\n\"hud_iammo4_frame_color               \\\"0 0 0\\\"\\n\"\r\n\"hud_iammo4_item_opacity              \\\"0.99\\\"\\n\"\r\n\"hud_iammo4_order                     \\\"20\\\"\\n\"\r\n\"hud_iammo4_place                     \\\"ammo4\\\"\\n\"\r\n\"hud_iammo4_pos_x                     \\\"1\\\"\\n\"\r\n\"hud_iammo4_pos_y                     \\\"1\\\"\\n\"\r\n\"hud_iammo4_scale                     \\\"0.25\\\"\\n\"\r\n\"hud_iammo4_show                      \\\"0\\\"\\n\"\r\n\"hud_iammo4_style                     \\\"0\\\"\\n\"\r\n\"hud_iammo_align_x                    \\\"after\\\"\\n\"\r\n\"hud_iammo_align_y                    \\\"bottom\\\"\\n\"\r\n\"hud_iammo_frame                      \\\"0\\\"\\n\"\r\n\"hud_iammo_frame_color                \\\"0 0 0\\\"\\n\"\r\n\"hud_iammo_item_opacity               \\\"0.99\\\"\\n\"\r\n\"hud_iammo_order                      \\\"31\\\"\\n\"\r\n\"hud_iammo_place                      \\\"ammo\\\"\\n\"\r\n\"hud_iammo_pos_x                      \\\"0\\\"\\n\"\r\n\"hud_iammo_pos_y                      \\\"0\\\"\\n\"\r\n\"hud_iammo_scale                      \\\"0.84\\\"\\n\"\r\n\"hud_iammo_show                       \\\"0\\\"\\n\"\r\n\"hud_iammo_style                      \\\"0\\\"\\n\"\r\n\"hud_iarmor_align_x                   \\\"before\\\"\\n\"\r\n\"hud_iarmor_align_y                   \\\"bottom\\\"\\n\"\r\n\"hud_iarmor_frame                     \\\"0\\\"\\n\"\r\n\"hud_iarmor_frame_color               \\\"0 0 0\\\"\\n\"\r\n\"hud_iarmor_item_opacity              \\\"0.99\\\"\\n\"\r\n\"hud_iarmor_order                     \\\"6\\\"\\n\"\r\n\"hud_iarmor_place                     \\\"armor\\\"\\n\"\r\n\"hud_iarmor_pos_x                     \\\"0\\\"\\n\"\r\n\"hud_iarmor_pos_y                     \\\"0\\\"\\n\"\r\n\"hud_iarmor_scale                     \\\"0.84\\\"\\n\"\r\n\"hud_iarmor_show                      \\\"0\\\"\\n\"\r\n\"hud_iarmor_style                     \\\"0\\\"\\n\"\r\n/*\r\n\"hud_itemsclock_align_x               \\\"right\\\"\\n\"\r\n\"hud_itemsclock_align_y               \\\"center\\\"\\n\"\r\n\"hud_itemsclock_frame                 \\\"0\\\"\\n\"\r\n\"hud_itemsclock_frame_color           \\\"0 0 0\\\"\\n\"\r\n\"hud_itemsclock_item_opacity          \\\"0.99\\\"\\n\"\r\n\"hud_itemsclock_order                 \\\"1\\\"\\n\"\r\n\"hud_itemsclock_place                 \\\"screen\\\"\\n\"\r\n\"hud_itemsclock_pos_x                 \\\"0\\\"\\n\"\r\n\"hud_itemsclock_pos_y                 \\\"0\\\"\\n\"\r\n\"hud_itemsclock_show                  \\\"0\\\"\\n\"\r\n\"hud_itemsclock_style                 \\\"0\\\"\\n\"\r\n\"hud_itemsclock_timelimit             \\\"5\\\"\\n\"\r\n*/\r\n\"hud_key1_align_x                     \\\"center\\\"\\n\"\r\n\"hud_key1_align_y                     \\\"bottom\\\"\\n\"\r\n\"hud_key1_frame                       \\\"0\\\"\\n\"\r\n\"hud_key1_frame_color                 \\\"0 0 0\\\"\\n\"\r\n\"hud_key1_item_opacity                \\\"0.99\\\"\\n\"\r\n\"hud_key1_order                       \\\"0\\\"\\n\"\r\n\"hud_key1_place                       \\\"top\\\"\\n\"\r\n\"hud_key1_pos_x                       \\\"155\\\"\\n\"\r\n\"hud_key1_pos_y                       \\\"-32\\\"\\n\"\r\n\"hud_key1_scale                       \\\"2\\\"\\n\"\r\n\"hud_key1_show                        \\\"1\\\"\\n\"\r\n\"hud_key1_style                       \\\"0\\\"\\n\"\r\n\"hud_key2_align_x                     \\\"center\\\"\\n\"\r\n\"hud_key2_align_y                     \\\"bottom\\\"\\n\"\r\n\"hud_key2_frame                       \\\"0\\\"\\n\"\r\n\"hud_key2_frame_color                 \\\"0 0 0\\\"\\n\"\r\n\"hud_key2_item_opacity                \\\"0.99\\\"\\n\"\r\n\"hud_key2_order                       \\\"1\\\"\\n\"\r\n\"hud_key2_place                       \\\"key1\\\"\\n\"\r\n\"hud_key2_pos_x                       \\\"-34\\\"\\n\"\r\n\"hud_key2_pos_y                       \\\"0\\\"\\n\"\r\n\"hud_key2_scale                       \\\"2\\\"\\n\"\r\n\"hud_key2_show                        \\\"1\\\"\\n\"\r\n\"hud_key2_style                       \\\"0\\\"\\n\"\r\n/*\r\n\"hud_keys_align_x                     \\\"right\\\"\\n\"\r\n\"hud_keys_align_y                     \\\"center\\\"\\n\"\r\n\"hud_keys_frame                       \\\"0.5\\\"\\n\"\r\n\"hud_keys_frame_color                 \\\"20 20 20\\\"\\n\"\r\n\"hud_keys_item_opacity                \\\"0.99\\\"\\n\"\r\n\"hud_keys_order                       \\\"1\\\"\\n\"\r\n\"hud_keys_place                       \\\"screen\\\"\\n\"\r\n\"hud_keys_pos_x                       \\\"0\\\"\\n\"\r\n\"hud_keys_pos_y                       \\\"0\\\"\\n\"\r\n\"hud_keys_scale                       \\\"2\\\"\\n\"\r\n\"hud_keys_show                        \\\"0\\\"\\n\"\r\n*/\r\n\"hud_mouserate_align_x                \\\"left\\\"\\n\"\r\n\"hud_mouserate_align_y                \\\"bottom\\\"\\n\"\r\n\"hud_mouserate_frame                  \\\"0\\\"\\n\"\r\n\"hud_mouserate_frame_color            \\\"0 0 0\\\"\\n\"\r\n\"hud_mouserate_interval               \\\"1\\\"\\n\"\r\n\"hud_mouserate_item_opacity           \\\"0.99\\\"\\n\"\r\n\"hud_mouserate_order                  \\\"9\\\"\\n\"\r\n\"hud_mouserate_place                  \\\"screen\\\"\\n\"\r\n\"hud_mouserate_pos_x                  \\\"0\\\"\\n\"\r\n\"hud_mouserate_pos_y                  \\\"0\\\"\\n\"\r\n\"hud_mouserate_show                   \\\"1\\\"\\n\"\r\n\"hud_mouserate_style                  \\\"0\\\"\\n\"\r\n\"hud_mouserate_title                  \\\"1\\\"\\n\"\r\n\"hud_mp3_time_align_x                 \\\"left\\\"\\n\"\r\n\"hud_mp3_time_align_y                 \\\"bottom\\\"\\n\"\r\n\"hud_mp3_time_frame                   \\\"0\\\"\\n\"\r\n\"hud_mp3_time_frame_color             \\\"0 0 0\\\"\\n\"\r\n\"hud_mp3_time_item_opacity            \\\"0.99\\\"\\n\"\r\n\"hud_mp3_time_on_scoreboard           \\\"0\\\"\\n\"\r\n\"hud_mp3_time_order                   \\\"0\\\"\\n\"\r\n\"hud_mp3_time_place                   \\\"top\\\"\\n\"\r\n\"hud_mp3_time_pos_x                   \\\"0\\\"\\n\"\r\n\"hud_mp3_time_pos_y                   \\\"0\\\"\\n\"\r\n\"hud_mp3_time_show                    \\\"0\\\"\\n\"\r\n\"hud_mp3_time_style                   \\\"0\\\"\\n\"\r\n\"hud_mp3_title_align_x                \\\"right\\\"\\n\"\r\n\"hud_mp3_title_align_y                \\\"bottom\\\"\\n\"\r\n\"hud_mp3_title_frame                  \\\"0\\\"\\n\"\r\n\"hud_mp3_title_frame_color            \\\"0 0 0\\\"\\n\"\r\n\"hud_mp3_title_height                 \\\"8\\\"\\n\"\r\n\"hud_mp3_title_item_opacity           \\\"0.99\\\"\\n\"\r\n\"hud_mp3_title_on_scoreboard          \\\"0\\\"\\n\"\r\n\"hud_mp3_title_order                  \\\"0\\\"\\n\"\r\n\"hud_mp3_title_place                  \\\"top\\\"\\n\"\r\n\"hud_mp3_title_pos_x                  \\\"0\\\"\\n\"\r\n\"hud_mp3_title_pos_y                  \\\"0\\\"\\n\"\r\n\"hud_mp3_title_scroll                 \\\"1\\\"\\n\"\r\n\"hud_mp3_title_scroll_delay           \\\"0.5\\\"\\n\"\r\n\"hud_mp3_title_show                   \\\"0\\\"\\n\"\r\n\"hud_mp3_title_style                  \\\"2\\\"\\n\"\r\n\"hud_mp3_title_width                  \\\"512\\\"\\n\"\r\n\"hud_mp3_title_wordwrap               \\\"0\\\"\\n\"\r\n\"hud_net_align_x                      \\\"left\\\"\\n\"\r\n\"hud_net_align_y                      \\\"center\\\"\\n\"\r\n\"hud_net_frame                        \\\"0.2\\\"\\n\"\r\n\"hud_net_frame_color                  \\\"0 0 0\\\"\\n\"\r\n\"hud_net_item_opacity                 \\\"0.99\\\"\\n\"\r\n\"hud_net_order                        \\\"7\\\"\\n\"\r\n\"hud_net_period                       \\\"1\\\"\\n\"\r\n\"hud_net_place                        \\\"top\\\"\\n\"\r\n\"hud_net_pos_x                        \\\"0\\\"\\n\"\r\n\"hud_net_pos_y                        \\\"0\\\"\\n\"\r\n\"hud_net_show                         \\\"1\\\"\\n\"\r\n/*\r\n\"hud_netgraph_align_x                 \\\"left\\\"\\n\"\r\n\"hud_netgraph_align_y                 \\\"bottom\\\"\\n\"\r\n\"hud_netgraph_alpha                   \\\"1\\\"\\n\"\r\n\"hud_netgraph_frame                   \\\"0\\\"\\n\"\r\n\"hud_netgraph_frame_color             \\\"0 0 0\\\"\\n\"\r\n\"hud_netgraph_full                    \\\"0\\\"\\n\"\r\n\"hud_netgraph_height                  \\\"32\\\"\\n\"\r\n\"hud_netgraph_inframes                \\\"0\\\"\\n\"\r\n\"hud_netgraph_item_opacity            \\\"0.99\\\"\\n\"\r\n\"hud_netgraph_lostscale               \\\"1\\\"\\n\"\r\n\"hud_netgraph_order                   \\\"0\\\"\\n\"\r\n\"hud_netgraph_place                   \\\"top\\\"\\n\"\r\n\"hud_netgraph_ploss                   \\\"1\\\"\\n\"\r\n\"hud_netgraph_pos_x                   \\\"0\\\"\\n\"\r\n\"hud_netgraph_pos_y                   \\\"0\\\"\\n\"\r\n\"hud_netgraph_scale                   \\\"256\\\"\\n\"\r\n\"hud_netgraph_show                    \\\"0\\\"\\n\"\r\n\"hud_netgraph_swap_x                  \\\"0\\\"\\n\"\r\n\"hud_netgraph_swap_y                  \\\"0\\\"\\n\"\r\n\"hud_netgraph_width                   \\\"256\\\"\\n\"\r\n*/\r\n\"hud_netproblem_align_x               \\\"left\\\"\\n\"\r\n\"hud_netproblem_align_y               \\\"top\\\"\\n\"\r\n\"hud_netproblem_frame                 \\\"0\\\"\\n\"\r\n\"hud_netproblem_frame_color           \\\"0 0 0\\\"\\n\"\r\n\"hud_netproblem_item_opacity          \\\"0.99\\\"\\n\"\r\n\"hud_netproblem_order                 \\\"0\\\"\\n\"\r\n\"hud_netproblem_place                 \\\"top\\\"\\n\"\r\n\"hud_netproblem_pos_x                 \\\"0\\\"\\n\"\r\n\"hud_netproblem_pos_y                 \\\"0\\\"\\n\"\r\n\"hud_netproblem_scale                 \\\"1\\\"\\n\"\r\n\"hud_netproblem_show                  \\\"0\\\"\\n\"\r\n/*\r\n\"hud_notify_align_x                   \\\"left\\\"\\n\"\r\n\"hud_notify_align_y                   \\\"top\\\"\\n\"\r\n\"hud_notify_cols                      \\\"30\\\"\\n\"\r\n\"hud_notify_frame                     \\\"0\\\"\\n\"\r\n\"hud_notify_frame_color               \\\"0 0 0\\\"\\n\"\r\n\"hud_notify_item_opacity              \\\"0.99\\\"\\n\"\r\n\"hud_notify_order                     \\\"8\\\"\\n\"\r\n\"hud_notify_place                     \\\"top\\\"\\n\"\r\n\"hud_notify_pos_x                     \\\"0\\\"\\n\"\r\n\"hud_notify_pos_y                     \\\"0\\\"\\n\"\r\n\"hud_notify_rows                      \\\"4\\\"\\n\"\r\n\"hud_notify_scale                     \\\"1\\\"\\n\"\r\n\"hud_notify_show                      \\\"0\\\"\\n\"\r\n\"hud_notify_time                      \\\"4\\\"\\n\"\r\n*/\r\n/*\r\n\"hud_ownfrags_align_x                 \\\"center\\\"\\n\"\r\n\"hud_ownfrags_align_y                 \\\"top\\\"\\n\"\r\n\"hud_ownfrags_frame                   \\\"0\\\"\\n\"\r\n\"hud_ownfrags_frame_color             \\\"0 0 100\\\"\\n\"\r\n\"hud_ownfrags_item_opacity            \\\"0.99\\\"\\n\"\r\n\"hud_ownfrags_order                   \\\"1\\\"\\n\"\r\n\"hud_ownfrags_place                   \\\"screen\\\"\\n\"\r\n\"hud_ownfrags_pos_x                   \\\"0\\\"\\n\"\r\n\"hud_ownfrags_pos_y                   \\\"75\\\"\\n\"\r\n\"hud_ownfrags_scale                   \\\"1\\\"\\n\"\r\n\"hud_ownfrags_show                    \\\"1\\\"\\n\"\r\n\"hud_ownfrags_timeout                 \\\"3\\\"\\n\"\r\n*/\r\n\"hud_pent_align_x                     \\\"center\\\"\\n\"\r\n\"hud_pent_align_y                     \\\"before\\\"\\n\"\r\n\"hud_pent_frame                       \\\"0\\\"\\n\"\r\n\"hud_pent_frame_color                 \\\"0 0 0\\\"\\n\"\r\n\"hud_pent_item_opacity                \\\"0.99\\\"\\n\"\r\n\"hud_pent_order                       \\\"12\\\"\\n\"\r\n\"hud_pent_place                       \\\"quad\\\"\\n\"\r\n\"hud_pent_pos_x                       \\\"0\\\"\\n\"\r\n\"hud_pent_pos_y                       \\\"0\\\"\\n\"\r\n\"hud_pent_scale                       \\\"2\\\"\\n\"\r\n\"hud_pent_show                        \\\"1\\\"\\n\"\r\n\"hud_pent_style                       \\\"0\\\"\\n\"\r\n\"hud_ping_align_x                     \\\"center\\\"\\n\"\r\n\"hud_ping_align_y                     \\\"center\\\"\\n\"\r\n\"hud_ping_blink                       \\\"0\\\"\\n\"\r\n\"hud_ping_frame                       \\\"0\\\"\\n\"\r\n\"hud_ping_frame_color                 \\\"0 0 0\\\"\\n\"\r\n\"hud_ping_item_opacity                \\\"0.99\\\"\\n\"\r\n\"hud_ping_order                       \\\"9\\\"\\n\"\r\n\"hud_ping_period                      \\\"1\\\"\\n\"\r\n\"hud_ping_place                       \\\"clock\\\"\\n\"\r\n\"hud_ping_pos_x                       \\\"1\\\"\\n\"\r\n\"hud_ping_pos_y                       \\\"9\\\"\\n\"\r\n\"hud_ping_show                        \\\"1\\\"\\n\"\r\n\"hud_ping_show_dev                    \\\"0\\\"\\n\"\r\n\"hud_ping_show_max                    \\\"0\\\"\\n\"\r\n\"hud_ping_show_min                    \\\"0\\\"\\n\"\r\n\"hud_ping_show_pl                     \\\"0\\\"\\n\"\r\n\"hud_ping_style                       \\\"0\\\"\\n\"\r\n\"hud_quad_align_x                     \\\"right\\\"\\n\"\r\n\"hud_quad_align_y                     \\\"center\\\"\\n\"\r\n\"hud_quad_frame                       \\\"0\\\"\\n\"\r\n\"hud_quad_frame_color                 \\\"0 0 0\\\"\\n\"\r\n\"hud_quad_item_opacity                \\\"0.99\\\"\\n\"\r\n\"hud_quad_order                       \\\"11\\\"\\n\"\r\n\"hud_quad_place                       \\\"screen\\\"\\n\"\r\n\"hud_quad_pos_x                       \\\"0\\\"\\n\"\r\n\"hud_quad_pos_y                       \\\"0\\\"\\n\"\r\n\"hud_quad_scale                       \\\"2\\\"\\n\"\r\n\"hud_quad_show                        \\\"1\\\"\\n\"\r\n\"hud_quad_style                       \\\"0\\\"\\n\"\r\n/*\r\n\"hud_radar_align_x                    \\\"left\\\"\\n\"\r\n\"hud_radar_align_y                    \\\"bottom\\\"\\n\"\r\n\"hud_radar_autosize                   \\\"0\\\"\\n\"\r\n\"hud_radar_fade_players               \\\"1\\\"\\n\"\r\n\"hud_radar_frame                      \\\"0\\\"\\n\"\r\n\"hud_radar_frame_color                \\\"0 0 0\\\"\\n\"\r\n\"hud_radar_height                     \\\"25%\\\"\\n\"\r\n\"hud_radar_highlight                  \\\"0\\\"\\n\"\r\n\"hud_radar_highlight_color            \\\"yellow\\\"\\n\"\r\n\"hud_radar_item_opacity               \\\"0.99\\\"\\n\"\r\n\"hud_radar_itemfilter                 \\\"backpack quad pent armor mega\\\"\\n\"\r\n\"hud_radar_onlytp                     \\\"0\\\"\\n\"\r\n\"hud_radar_opacity                    \\\"0.5\\\"\\n\"\r\n\"hud_radar_order                      \\\"0\\\"\\n\"\r\n\"hud_radar_otherfilter                \\\"projectiles gibs explosions shotgun\\\"\\n\"\r\n\"hud_radar_place                      \\\"top\\\"\\n\"\r\n\"hud_radar_player_size                \\\"10\\\"\\n\"\r\n\"hud_radar_pos_x                      \\\"0\\\"\\n\"\r\n\"hud_radar_pos_y                      \\\"0\\\"\\n\"\r\n\"hud_radar_show                       \\\"0\\\"\\n\"\r\n\"hud_radar_show_height                \\\"1\\\"\\n\"\r\n\"hud_radar_show_hold                  \\\"0\\\"\\n\"\r\n\"hud_radar_show_names                 \\\"0\\\"\\n\"\r\n\"hud_radar_show_powerups              \\\"1\\\"\\n\"\r\n\"hud_radar_show_stats                 \\\"1\\\"\\n\"\r\n\"hud_radar_weaponfilter               \\\"gl rl lg\\\"\\n\"\r\n\"hud_radar_width                      \\\"30%\\\"\\n\"\r\n*/\r\n\"hud_ring_align_x                     \\\"center\\\"\\n\"\r\n\"hud_ring_align_y                     \\\"after\\\"\\n\"\r\n\"hud_ring_frame                       \\\"0\\\"\\n\"\r\n\"hud_ring_frame_color                 \\\"0 0 0\\\"\\n\"\r\n\"hud_ring_item_opacity                \\\"0.99\\\"\\n\"\r\n\"hud_ring_order                       \\\"12\\\"\\n\"\r\n\"hud_ring_place                       \\\"quad\\\"\\n\"\r\n\"hud_ring_pos_x                       \\\"0\\\"\\n\"\r\n\"hud_ring_pos_y                       \\\"0\\\"\\n\"\r\n\"hud_ring_scale                       \\\"2\\\"\\n\"\r\n\"hud_ring_show                        \\\"1\\\"\\n\"\r\n\"hud_ring_style                       \\\"0\\\"\\n\"\r\n\"hud_score_bar_align_x                \\\"center\\\"\\n\"\r\n\"hud_score_bar_align_y                \\\"console\\\"\\n\"\r\n\"hud_score_bar_format_big             \\\"%t:%e:%Z\\\"\\n\"\r\n\"hud_score_bar_format_small           \\\"&c69f%T&r:%t &cf10%E&r:%e $[%D$]\\\"\\n\"\r\n\"hud_score_bar_frame                  \\\"0.5\\\"\\n\"\r\n\"hud_score_bar_frame_color            \\\"0 0 0\\\"\\n\"\r\n\"hud_score_bar_item_opacity           \\\"0.99\\\"\\n\"\r\n\"hud_score_bar_order                  \\\"0\\\"\\n\"\r\n\"hud_score_bar_place                  \\\"screen\\\"\\n\"\r\n\"hud_score_bar_pos_x                  \\\"0\\\"\\n\"\r\n\"hud_score_bar_pos_y                  \\\"0\\\"\\n\"\r\n\"hud_score_bar_scale                  \\\"1\\\"\\n\"\r\n\"hud_score_bar_show                   \\\"0\\\"\\n\"\r\n\"hud_score_bar_style                  \\\"0\\\"\\n\"\r\n\"hud_score_difference_align           \\\"right\\\"\\n\"\r\n\"hud_score_difference_align_x         \\\"after\\\"\\n\"\r\n\"hud_score_difference_align_y         \\\"bottom\\\"\\n\"\r\n\"hud_score_difference_colorize        \\\"1\\\"\\n\"\r\n\"hud_score_difference_digits          \\\"0\\\"\\n\"\r\n\"hud_score_difference_frame           \\\"0.5\\\"\\n\"\r\n\"hud_score_difference_frame_color     \\\"0 0 0\\\"\\n\"\r\n\"hud_score_difference_item_opacity    \\\"0.99\\\"\\n\"\r\n\"hud_score_difference_order           \\\"2\\\"\\n\"\r\n\"hud_score_difference_place           \\\"score_enemy\\\"\\n\"\r\n\"hud_score_difference_pos_x           \\\"0\\\"\\n\"\r\n\"hud_score_difference_pos_y           \\\"0\\\"\\n\"\r\n\"hud_score_difference_scale           \\\"1\\\"\\n\"\r\n\"hud_score_difference_show            \\\"0\\\"\\n\"\r\n\"hud_score_difference_style           \\\"0\\\"\\n\"\r\n\"hud_score_enemy_align                \\\"right\\\"\\n\"\r\n\"hud_score_enemy_align_x              \\\"center\\\"\\n\"\r\n\"hud_score_enemy_align_y              \\\"before\\\"\\n\"\r\n\"hud_score_enemy_colorize             \\\"0\\\"\\n\"\r\n\"hud_score_enemy_digits               \\\"3\\\"\\n\"\r\n\"hud_score_enemy_frame                \\\"0.25\\\"\\n\"\r\n\"hud_score_enemy_frame_color          \\\"200 75 75\\\"\\n\"\r\n\"hud_score_enemy_item_opacity         \\\"0.99\\\"\\n\"\r\n\"hud_score_enemy_order                \\\"1\\\"\\n\"\r\n\"hud_score_enemy_place                \\\"score_team\\\"\\n\"\r\n\"hud_score_enemy_pos_x                \\\"0\\\"\\n\"\r\n\"hud_score_enemy_pos_y                \\\"-1\\\"\\n\"\r\n\"hud_score_enemy_scale                \\\"0.5\\\"\\n\"\r\n\"hud_score_enemy_show                 \\\"1\\\"\\n\"\r\n\"hud_score_enemy_style                \\\"2\\\"\\n\"\r\n\"hud_score_position_align             \\\"right\\\"\\n\"\r\n\"hud_score_position_align_x           \\\"after\\\"\\n\"\r\n\"hud_score_position_align_y           \\\"bottom\\\"\\n\"\r\n\"hud_score_position_colorize          \\\"1\\\"\\n\"\r\n\"hud_score_position_digits            \\\"0\\\"\\n\"\r\n\"hud_score_position_frame             \\\"0.5\\\"\\n\"\r\n\"hud_score_position_frame_color       \\\"0 0 0\\\"\\n\"\r\n\"hud_score_position_item_opacity      \\\"0.99\\\"\\n\"\r\n\"hud_score_position_order             \\\"3\\\"\\n\"\r\n\"hud_score_position_place             \\\"score_difference\\\"\\n\"\r\n\"hud_score_position_pos_x             \\\"0\\\"\\n\"\r\n\"hud_score_position_pos_y             \\\"0\\\"\\n\"\r\n\"hud_score_position_scale             \\\"1\\\"\\n\"\r\n\"hud_score_position_show              \\\"0\\\"\\n\"\r\n\"hud_score_position_style             \\\"0\\\"\\n\"\r\n\"hud_score_team_align                 \\\"right\\\"\\n\"\r\n\"hud_score_team_align_x               \\\"right\\\"\\n\"\r\n\"hud_score_team_align_y               \\\"bottom\\\"\\n\"\r\n\"hud_score_team_colorize              \\\"0\\\"\\n\"\r\n\"hud_score_team_digits                \\\"3\\\"\\n\"\r\n\"hud_score_team_frame                 \\\"0.25\\\"\\n\"\r\n\"hud_score_team_frame_color           \\\"75 75 200\\\"\\n\"\r\n\"hud_score_team_item_opacity          \\\"0.99\\\"\\n\"\r\n\"hud_score_team_order                 \\\"0\\\"\\n\"\r\n\"hud_score_team_place                 \\\"screen\\\"\\n\"\r\n\"hud_score_team_pos_x                 \\\"-5\\\"\\n\"\r\n\"hud_score_team_pos_y                 \\\"-5\\\"\\n\"\r\n\"hud_score_team_scale                 \\\"0.5\\\"\\n\"\r\n\"hud_score_team_show                  \\\"1\\\"\\n\"\r\n\"hud_score_team_style                 \\\"2\\\"\\n\"\r\n\"hud_sigil1_align_x                   \\\"center\\\"\\n\"\r\n\"hud_sigil1_align_y                   \\\"bottom\\\"\\n\"\r\n\"hud_sigil1_frame                     \\\"0\\\"\\n\"\r\n\"hud_sigil1_frame_color               \\\"0 0 0\\\"\\n\"\r\n\"hud_sigil1_item_opacity              \\\"0.99\\\"\\n\"\r\n\"hud_sigil1_order                     \\\"0\\\"\\n\"\r\n\"hud_sigil1_place                     \\\"screen\\\"\\n\"\r\n\"hud_sigil1_pos_x                     \\\"-160\\\"\\n\"\r\n\"hud_sigil1_pos_y                     \\\"-80\\\"\\n\"\r\n\"hud_sigil1_scale                     \\\"2\\\"\\n\"\r\n\"hud_sigil1_show                      \\\"1\\\"\\n\"\r\n\"hud_sigil1_style                     \\\"0\\\"\\n\"\r\n\"hud_sigil2_align_x                   \\\"center\\\"\\n\"\r\n\"hud_sigil2_align_y                   \\\"bottom\\\"\\n\"\r\n\"hud_sigil2_frame                     \\\"0\\\"\\n\"\r\n\"hud_sigil2_frame_color               \\\"0 0 0\\\"\\n\"\r\n\"hud_sigil2_item_opacity              \\\"0.99\\\"\\n\"\r\n\"hud_sigil2_order                     \\\"1\\\"\\n\"\r\n\"hud_sigil2_place                     \\\"sigil1\\\"\\n\"\r\n\"hud_sigil2_pos_x                     \\\"17\\\"\\n\"\r\n\"hud_sigil2_pos_y                     \\\"0\\\"\\n\"\r\n\"hud_sigil2_scale                     \\\"2\\\"\\n\"\r\n\"hud_sigil2_show                      \\\"1\\\"\\n\"\r\n\"hud_sigil2_style                     \\\"0\\\"\\n\"\r\n\"hud_sigil3_align_x                   \\\"center\\\"\\n\"\r\n\"hud_sigil3_align_y                   \\\"bottom\\\"\\n\"\r\n\"hud_sigil3_frame                     \\\"0\\\"\\n\"\r\n\"hud_sigil3_frame_color               \\\"0 0 0\\\"\\n\"\r\n\"hud_sigil3_item_opacity              \\\"0.99\\\"\\n\"\r\n\"hud_sigil3_order                     \\\"2\\\"\\n\"\r\n\"hud_sigil3_place                     \\\"sigil2\\\"\\n\"\r\n\"hud_sigil3_pos_x                     \\\"17\\\"\\n\"\r\n\"hud_sigil3_pos_y                     \\\"0\\\"\\n\"\r\n\"hud_sigil3_scale                     \\\"2\\\"\\n\"\r\n\"hud_sigil3_show                      \\\"1\\\"\\n\"\r\n\"hud_sigil3_style                     \\\"0\\\"\\n\"\r\n\"hud_sigil4_align_x                   \\\"center\\\"\\n\"\r\n\"hud_sigil4_align_y                   \\\"bottom\\\"\\n\"\r\n\"hud_sigil4_frame                     \\\"0\\\"\\n\"\r\n\"hud_sigil4_frame_color               \\\"0 0 0\\\"\\n\"\r\n\"hud_sigil4_item_opacity              \\\"0.99\\\"\\n\"\r\n\"hud_sigil4_order                     \\\"3\\\"\\n\"\r\n\"hud_sigil4_place                     \\\"sigil3\\\"\\n\"\r\n\"hud_sigil4_pos_x                     \\\"17\\\"\\n\"\r\n\"hud_sigil4_pos_y                     \\\"0\\\"\\n\"\r\n\"hud_sigil4_scale                     \\\"2\\\"\\n\"\r\n\"hud_sigil4_show                      \\\"1\\\"\\n\"\r\n\"hud_sigil4_style                     \\\"0\\\"\\n\"\r\n\"hud_speed2_align_x                   \\\"center\\\"\\n\"\r\n\"hud_speed2_align_y                   \\\"bottom\\\"\\n\"\r\n\"hud_speed2_color_fast                \\\"72\\\"\\n\"\r\n\"hud_speed2_color_fastest             \\\"216\\\"\\n\"\r\n\"hud_speed2_color_insane              \\\"229\\\"\\n\"\r\n\"hud_speed2_color_normal              \\\"100\\\"\\n\"\r\n\"hud_speed2_color_stopped             \\\"52\\\"\\n\"\r\n\"hud_speed2_frame                     \\\"0\\\"\\n\"\r\n\"hud_speed2_frame_color               \\\"0 0 0\\\"\\n\"\r\n\"hud_speed2_item_opacity              \\\"0.99\\\"\\n\"\r\n\"hud_speed2_opacity                   \\\"1.0\\\"\\n\"\r\n\"hud_speed2_order                     \\\"7\\\"\\n\"\r\n\"hud_speed2_orientation               \\\"0\\\"\\n\"\r\n\"hud_speed2_place                     \\\"top\\\"\\n\"\r\n\"hud_speed2_pos_x                     \\\"0\\\"\\n\"\r\n\"hud_speed2_pos_y                     \\\"0\\\"\\n\"\r\n\"hud_speed2_radius                    \\\"50.0\\\"\\n\"\r\n\"hud_speed2_show                      \\\"0\\\"\\n\"\r\n\"hud_speed2_wrapspeed                 \\\"500\\\"\\n\"\r\n\"hud_speed2_xyz                       \\\"0\\\"\\n\"\r\n/*\r\n\"hud_speed_align_x                    \\\"center\\\"\\n\"\r\n\"hud_speed_align_y                    \\\"center\\\"\\n\"\r\n\"hud_speed_color_fast                 \\\"72\\\"\\n\"\r\n\"hud_speed_color_fastest              \\\"216\\\"\\n\"\r\n\"hud_speed_color_insane               \\\"229\\\"\\n\"\r\n\"hud_speed_color_normal               \\\"100\\\"\\n\"\r\n\"hud_speed_color_stopped              \\\"52\\\"\\n\"\r\n\"hud_speed_frame                      \\\"0\\\"\\n\"\r\n\"hud_speed_frame_color                \\\"0 0 0\\\"\\n\"\r\n\"hud_speed_height                     \\\"15\\\"\\n\"\r\n\"hud_speed_item_opacity               \\\"0\\\"\\n\"\r\n\"hud_speed_opacity                    \\\"1.0\\\"\\n\"\r\n\"hud_speed_order                      \\\"7\\\"\\n\"\r\n\"hud_speed_place                      \\\"screen\\\"\\n\"\r\n\"hud_speed_pos_x                      \\\"100\\\"\\n\"\r\n\"hud_speed_pos_y                      \\\"0\\\"\\n\"\r\n\"hud_speed_show                       \\\"0\\\"\\n\"\r\n\"hud_speed_style                      \\\"0\\\"\\n\"\r\n\"hud_speed_text_align                 \\\"3\\\"\\n\"\r\n\"hud_speed_tick_spacing               \\\"0.2\\\"\\n\"\r\n\"hud_speed_vertical                   \\\"0\\\"\\n\"\r\n\"hud_speed_vertical_text              \\\"1\\\"\\n\"\r\n\"hud_speed_width                      \\\"160\\\"\\n\"\r\n\"hud_speed_xyz                        \\\"0\\\"\\n\"\r\n*/\r\n\"hud_suit_align_x                     \\\"center\\\"\\n\"\r\n\"hud_suit_align_y                     \\\"after\\\"\\n\"\r\n\"hud_suit_frame                       \\\"0\\\"\\n\"\r\n\"hud_suit_frame_color                 \\\"0 0 0\\\"\\n\"\r\n\"hud_suit_item_opacity                \\\"0.99\\\"\\n\"\r\n\"hud_suit_order                       \\\"13\\\"\\n\"\r\n\"hud_suit_place                       \\\"ring\\\"\\n\"\r\n\"hud_suit_pos_x                       \\\"0\\\"\\n\"\r\n\"hud_suit_pos_y                       \\\"0\\\"\\n\"\r\n\"hud_suit_scale                       \\\"2\\\"\\n\"\r\n\"hud_suit_show                        \\\"1\\\"\\n\"\r\n\"hud_suit_style                       \\\"0\\\"\\n\"\r\n\"hud_teamfrags_align_x                \\\"center\\\"\\n\"\r\n\"hud_teamfrags_align_y                \\\"bottom\\\"\\n\"\r\n\"hud_teamfrags_bignum                 \\\"0\\\"\\n\"\r\n\"hud_teamfrags_cell_height            \\\"8\\\"\\n\"\r\n\"hud_teamfrags_cell_width             \\\"35\\\"\\n\"\r\n\"hud_teamfrags_colors_alpha           \\\"1.0\\\"\\n\"\r\n\"hud_teamfrags_cols                   \\\"2\\\"\\n\"\r\n\"hud_teamfrags_extra_spec_info        \\\"0\\\"\\n\"\r\n\"hud_teamfrags_fliptext               \\\"1\\\"\\n\"\r\n\"hud_teamfrags_frame                  \\\"0\\\"\\n\"\r\n\"hud_teamfrags_frame_color            \\\"0 0 0\\\"\\n\"\r\n\"hud_teamfrags_item_opacity           \\\"0.99\\\"\\n\"\r\n\"hud_teamfrags_maxname                \\\"16\\\"\\n\"\r\n\"hud_teamfrags_onlytp                 \\\"0\\\"\\n\"\r\n\"hud_teamfrags_order                  \\\"9\\\"\\n\"\r\n\"hud_teamfrags_padtext                \\\"1\\\"\\n\"\r\n\"hud_teamfrags_place                  \\\"gameclock\\\"\\n\"\r\n\"hud_teamfrags_pos_x                  \\\"0\\\"\\n\"\r\n\"hud_teamfrags_pos_y                  \\\"11\\\"\\n\"\r\n\"hud_teamfrags_rows                   \\\"1\\\"\\n\"\r\n\"hud_teamfrags_show                   \\\"0\\\"\\n\"\r\n\"hud_teamfrags_shownames              \\\"0\\\"\\n\"\r\n\"hud_teamfrags_space_x                \\\"0\\\"\\n\"\r\n\"hud_teamfrags_space_y                \\\"1\\\"\\n\"\r\n\"hud_teamfrags_strip                  \\\"1\\\"\\n\"\r\n\"hud_teamfrags_style                  \\\"3\\\"\\n\"\r\n\"hud_teamfrags_vertical               \\\"0\\\"\\n\"\r\n/*\r\n\"hud_teamholdbar_align_x              \\\"left\\\"\\n\"\r\n\"hud_teamholdbar_align_y              \\\"bottom\\\"\\n\"\r\n\"hud_teamholdbar_frame                \\\"0\\\"\\n\"\r\n\"hud_teamholdbar_frame_color          \\\"0 0 0\\\"\\n\"\r\n\"hud_teamholdbar_height               \\\"8\\\"\\n\"\r\n\"hud_teamholdbar_item_opacity         \\\"0.99\\\"\\n\"\r\n\"hud_teamholdbar_onlytp               \\\"0\\\"\\n\"\r\n\"hud_teamholdbar_opacity              \\\"0.8\\\"\\n\"\r\n\"hud_teamholdbar_order                \\\"0\\\"\\n\"\r\n\"hud_teamholdbar_place                \\\"top\\\"\\n\"\r\n\"hud_teamholdbar_pos_x                \\\"0\\\"\\n\"\r\n\"hud_teamholdbar_pos_y                \\\"0\\\"\\n\"\r\n\"hud_teamholdbar_show                 \\\"0\\\"\\n\"\r\n\"hud_teamholdbar_show_text            \\\"1\\\"\\n\"\r\n\"hud_teamholdbar_vertical             \\\"0\\\"\\n\"\r\n\"hud_teamholdbar_vertical_text        \\\"0\\\"\\n\"\r\n\"hud_teamholdbar_width                \\\"200\\\"\\n\"\r\n*/\r\n/*\r\n\"hud_teamholdinfo_align_x             \\\"left\\\"\\n\"\r\n\"hud_teamholdinfo_align_y             \\\"bottom\\\"\\n\"\r\n\"hud_teamholdinfo_frame               \\\"0\\\"\\n\"\r\n\"hud_teamholdinfo_frame_color         \\\"0 0 0\\\"\\n\"\r\n\"hud_teamholdinfo_height              \\\"8\\\"\\n\"\r\n\"hud_teamholdinfo_item_opacity        \\\"0.99\\\"\\n\"\r\n\"hud_teamholdinfo_itemfilter          \\\"quad ra ya ga mega pent rl quad\\\"\\n\"\r\n\"hud_teamholdinfo_onlytp              \\\"0\\\"\\n\"\r\n\"hud_teamholdinfo_opacity             \\\"0.8\\\"\\n\"\r\n\"hud_teamholdinfo_order               \\\"0\\\"\\n\"\r\n\"hud_teamholdinfo_place               \\\"top\\\"\\n\"\r\n\"hud_teamholdinfo_pos_x               \\\"0\\\"\\n\"\r\n\"hud_teamholdinfo_pos_y               \\\"0\\\"\\n\"\r\n\"hud_teamholdinfo_show                \\\"0\\\"\\n\"\r\n\"hud_teamholdinfo_style               \\\"1\\\"\\n\"\r\n\"hud_teamholdinfo_width               \\\"200\\\"\\n\"\r\n*/\r\n/*\r\n\"hud_teaminfo_align_right             \\\"0\\\"\\n\"\r\n\"hud_teaminfo_align_x                 \\\"right\\\"\\n\"\r\n\"hud_teaminfo_align_y                 \\\"center\\\"\\n\"\r\n\"hud_teaminfo_armor_style             \\\"3\\\"\\n\"\r\n\"hud_teaminfo_frame                   \\\"0.25\\\"\\n\"\r\n\"hud_teaminfo_frame_color             \\\"20 20 20\\\"\\n\"\r\n\"hud_teaminfo_item_opacity            \\\"1.0\\\"\\n\"\r\n\"hud_teaminfo_layout                  \\\"%p%n $x10%l$x11 %a/%H %w\\\"\\n\"\r\n\"hud_teaminfo_loc_width               \\\"5\\\"\\n\"\r\n\"hud_teaminfo_low_health              \\\"25\\\"\\n\"\r\n\"hud_teaminfo_name_width              \\\"6\\\"\\n\"\r\n\"hud_teaminfo_order                   \\\"0\\\"\\n\"\r\n\"hud_teaminfo_place                   \\\"\\\"\\n\"\r\n\"hud_teaminfo_pos_x                   \\\"0\\\"\\n\"\r\n\"hud_teaminfo_pos_y                   \\\"45\\\"\\n\"\r\n\"hud_teaminfo_powerup_style           \\\"1\\\"\\n\"\r\n\"hud_teaminfo_scale                   \\\"1\\\"\\n\"\r\n\"hud_teaminfo_show                    \\\"1\\\"\\n\"\r\n\"hud_teaminfo_show_enemies            \\\"0\\\"\\n\"\r\n\"hud_teaminfo_show_self               \\\"1\\\"\\n\"\r\n\"hud_teaminfo_weapon_style            \\\"0\\\"\\n\"\r\n*/\r\n\"hud_tp_need                          \\\"0\\\"\\n\"\r\n\"hud_tracking_align_x                 \\\"center\\\"\\n\"\r\n\"hud_tracking_align_y                 \\\"bottom\\\"\\n\"\r\n\"hud_tracking_format                  \\\"^mTracking:^m %t %n, ^mJUMP^m for next\\\"\\n\"\r\n\"hud_tracking_frame                   \\\"0\\\"\\n\"\r\n\"hud_tracking_frame_color             \\\"0 0 0\\\"\\n\"\r\n\"hud_tracking_item_opacity            \\\"0.99\\\"\\n\"\r\n\"hud_tracking_order                   \\\"31\\\"\\n\"\r\n\"hud_tracking_place                   \\\"top\\\"\\n\"\r\n\"hud_tracking_pos_x                   \\\"0\\\"\\n\"\r\n\"hud_tracking_pos_y                   \\\"-25\\\"\\n\"\r\n\"hud_tracking_scale                   \\\"1\\\"\\n\"\r\n\"hud_tracking_show                    \\\"1\\\"\\n\"\r\n\"hud_vidlag_align_x                   \\\"right\\\"\\n\"\r\n\"hud_vidlag_align_y                   \\\"top\\\"\\n\"\r\n\"hud_vidlag_frame                     \\\"0\\\"\\n\"\r\n\"hud_vidlag_frame_color               \\\"0 0 0\\\"\\n\"\r\n\"hud_vidlag_item_opacity              \\\"0.99\\\"\\n\"\r\n\"hud_vidlag_order                     \\\"9\\\"\\n\"\r\n\"hud_vidlag_place                     \\\"top\\\"\\n\"\r\n\"hud_vidlag_pos_x                     \\\"0\\\"\\n\"\r\n\"hud_vidlag_pos_y                     \\\"0\\\"\\n\"\r\n\"hud_vidlag_show                      \\\"0\\\"\\n\"\r\n\"hud_vidlag_style                     \\\"0\\\"\\n\"\r\n;\r\n"
  },
  {
    "path": "plugins/ezhud/ezquakeisms.c",
    "content": "#include \"ezquakeisms.h\"\r\n#include \"hud.h\"\r\n#include \"hud_editor.h\"\r\n\r\n#ifdef FTEENGINE\r\n#define Plug_Init Plug_EZHud_Init\r\n#endif\r\n\r\nplug2dfuncs_t *drawfuncs;\r\nplugclientfuncs_t *clientfuncs;\r\nplugfsfuncs_t *filefuncs;\r\npluginputfuncs_t *inputfuncs;\r\n\r\nstruct ezcl_s cl;\r\nstruct ezcls_s cls;\r\nstruct ezvid_s vid;\r\n\r\nint sb_lines;\r\nfloat scr_con_current;\r\nint sb_showteamscores;\r\nint sb_showscores;\r\nint host_screenupdatecount;\r\nfloat alphamul;\r\n\r\ncvar_t *scr_newHud;\r\n\r\nvoid HUD_InitSbarImages(void);\r\nstatic void QDECL EZHud_UpdateVideo(int width, int height, qboolean restarted)\r\n{\r\n\tvid.width = width;\r\n\tvid.height = height;\r\n\r\n\tif (restarted)\r\n\t\tHUD_InitSbarImages();\r\n}\r\n\r\nchar *Cmd_Argv(int arg)\r\n{\r\n\tstatic char buf[4][128];\r\n\tif (arg >= 4)\r\n\t\treturn \"\";\r\n\tcmdfuncs->Argv(arg, buf[arg], sizeof(buf[arg]));\r\n\treturn buf[arg];\r\n}\r\n\r\nfloat infofloat(char *info, char *findkey, float def);\r\n\r\nvoid Draw_SetOverallAlpha(float a)\r\n{\r\n\talphamul = a;\r\n}\r\nvoid Draw_AlphaFillRGB(float x, float y, float w, float h, qbyte r, qbyte g, qbyte b, qbyte a)\r\n{\r\n\tdrawfuncs->Colour4f(r/255.0, g/255.0, b/255.0, a/255.0 * alphamul);\r\n\tdrawfuncs->Fill(x, y, w, h);\r\n\tdrawfuncs->Colour4f(1, 1, 1, 1);\r\n}\r\nvoid Draw_Fill(float x, float y, float w, float h, qbyte pal)\r\n{\r\n\tdrawfuncs->Colourpa(pal, alphamul);\r\n\tdrawfuncs->Fill(x, y, w, h);\r\n\tdrawfuncs->Colour4f(1, 1, 1, 1);\r\n}\r\nconst char *ColorNameToRGBString (const char *newval)\r\n{\r\n\treturn newval;\r\n}\r\nbyte *StringToRGB(const char *str)\r\n{\r\n\tstatic byte rgba[4];\r\n\tint i;\r\n\tfor (i = 0; i < 4; i++)\r\n\t{\r\n\t\twhile(*str && *str <= ' ')\r\n\t\t\tstr++;\r\n\t\tif (!*str)\r\n\t\t\trgba[i] = 255;\r\n\t\telse\r\n\t\t\trgba[i] = strtoul(str, (char**)&str, 0);\r\n\t}\r\n\treturn rgba;\r\n}\r\nvoid Draw_TextBox (int x, int y, int width, int lines)\r\n{\r\n}\r\n\r\nchar *TP_LocationName (const vec3_t location)\r\n{\r\n\tstatic char locname[256];\r\n\tclientfuncs->GetLocationName(location, locname, sizeof(locname));\r\n\treturn locname;\r\n}\r\n\r\n\r\nvoid Draw_SPic(float x, float y, mpic_t *pic, float scale)\r\n{\r\n\tqhandle_t image = (intptr_t)pic;\r\n\tfloat w=64, h=64;\r\n\tdrawfuncs->ImageSize(image, &w, &h);\r\n\tdrawfuncs->Image(x, y, w*scale, h*scale, 0, 0, 1, 1, image);\r\n}\r\nvoid Draw_SSubPic(float x, float y, mpic_t *pic, float s1, float t1, float s2, float t2, float scale)\r\n{\r\n\tqhandle_t image = (intptr_t)pic;\r\n\tfloat w=64, h=64;\r\n\tdrawfuncs->ImageSize(image, &w, &h);\r\n\tdrawfuncs->Image(x, y, (s2-s1)*scale, (t2-t1)*scale, s1/w, t1/h, s2/w, t2/h, image);\r\n}\r\nvoid Draw_EZString(float x, float y, char *str, float scale, qboolean red)\r\n{\r\n\tunsigned int flags = 0;\r\n\tif (red)\r\n\t\tflags |= 1;\r\n\tdrawfuncs->StringH(x, y, scale, flags, str);\r\n}\r\n\r\n#define Draw_STransPic Draw_SPic\r\nvoid Draw_Character(float x, float y, unsigned int ch)\r\n{\r\n\tdrawfuncs->Character(x, y, 0xe000|ch);\r\n}\r\nvoid Draw_SCharacter(float x, float y, unsigned int ch, float scale)\r\n{\r\n\tdrawfuncs->CharacterH(x, y, 8*scale, 0, 0xe000|ch);\r\n}\r\n\r\nvoid SCR_DrawWadString(float x, float y, float scale, char *str)\r\n{\r\n\tdrawfuncs->String(x, y, str);\t//FIXME\r\n}\r\n\r\nvoid Draw_SAlphaSubPic2(float x, float y, mpic_t *pic, float s1, float t1, float s2, float t2, float sw, float sh, float alpha)\r\n{\r\n\tqhandle_t image = (intptr_t)pic;\r\n\tfloat w=64, h=64;\r\n\tdrawfuncs->ImageSize(image, &w, &h);\r\n\tdrawfuncs->Colour4f(1, 1, 1, alpha * alphamul);\r\n\tdrawfuncs->Image(x, y, (s2-s1)*sw, (t2-t1)*sh, s1/w, t1/h, s2/w, t2/h, image);\r\n\tdrawfuncs->Colour4f(1, 1, 1, 1);\r\n}\r\n\r\nvoid Draw_AlphaFill(float x, float y, float w, float h, unsigned int pal, float alpha)\r\n{\r\n\tif (pal >= 256)\r\n\t\tdrawfuncs->Colour4f(((pal>>16)&0xff)/255.0, ((pal>>8)&0xff)/255.0, ((pal>>0)&0xff)/255.0, alpha * alphamul);\r\n\telse\r\n\t\tdrawfuncs->Colourpa(pal, alpha * alphamul);\r\n\tdrawfuncs->Fill(x, y, w, h);\r\n\tdrawfuncs->Colour4f(1, 1, 1, 1);\r\n}\r\nvoid Draw_AlphaPic(float x, float y, mpic_t *pic, float alpha)\r\n{\r\n\tqhandle_t image = (intptr_t)pic;\r\n\tfloat w, h;\r\n\tdrawfuncs->ImageSize(image, &w, &h);\r\n\tdrawfuncs->Colour4f(1, 1, 1, alpha * alphamul);\r\n\tdrawfuncs->Image(x, y, w, h, 0, 0, 1, 1, image);\r\n\tdrawfuncs->Colour4f(1, 1, 1, 1);\r\n}\r\nvoid Draw_AlphaSubPic(float x, float y, mpic_t *pic, float s1, float t1, float s2, float t2, float alpha)\r\n{\r\n\tqhandle_t image = (intptr_t)pic;\r\n\tfloat w, h;\r\n\tdrawfuncs->ImageSize(image, &w, &h);\r\n\tdrawfuncs->Colour4f(1, 1, 1, alpha * alphamul);\r\n\tdrawfuncs->Image(x, y, s2-s1, t2-t1, s1/w, t1/h, s2/w, t2/h, image);\r\n\tdrawfuncs->Colour4f(1, 1, 1, 1);\r\n}\r\nvoid SCR_HUD_DrawBar(int direction, int value, float max_value, float *rgba, int x, int y, int width, int height)\r\n{\r\n\tint amount;\r\n\r\n\tif(direction >= 2)\r\n\t\t// top-down\r\n\t\tamount = Q_rint(fabs((height * value) / max_value));\r\n\telse// left-right\r\n\t\tamount = Q_rint(fabs((width * value) / max_value));\r\n\r\n\tdrawfuncs->Colour4f(rgba[0]/255.0, rgba[1]/255.0, rgba[2]/255.0, rgba[3]/255.0 * alphamul);\r\n\tif(direction == 0)\r\n\t\t// left->right\r\n\t\tdrawfuncs->Fill(x, y, amount, height);\r\n\telse if (direction == 1)\r\n\t\t// right->left\r\n\t\tdrawfuncs->Fill(x + width - amount, y, amount, height);\r\n\telse if (direction == 2)\r\n\t\t// down -> up\r\n\t\tdrawfuncs->Fill(x, y + height - amount, width, amount);\r\n\telse\r\n\t\t// up -> down\r\n\t\tdrawfuncs->Fill(x, y, width, amount);\r\n\tdrawfuncs->Colour4f(1, 1, 1, 1);\r\n}\r\n\r\nvoid Draw_Polygon(int x, int y, vec3_t *vertices, int num_vertices, qbool fill, byte r, byte g, byte b, byte a)\r\n{\r\n\tdrawfuncs->Colour4f(r/255.0, g/255.0, b/255.0, a/255.0 * alphamul);\r\n//\tdrawfuncs->Line(x1, y1, x2, y1);\r\n\tdrawfuncs->Colour4f(1, 1, 1, 1);\r\n}\r\nvoid Draw_ColoredString3(float x, float y, const char *str, clrinfo_t *clr, int huh, int wut)\r\n{\r\n\tdrawfuncs->Colour4f(clr->c[0]/255.0, clr->c[1]/255.0, clr->c[2]/255.0, clr->c[3]/255.0 * alphamul);\r\n\tdrawfuncs->String(x, y, str);\r\n\tdrawfuncs->Colour4f(1, 1, 1, 1);\r\n}\r\nvoid UI_PrintTextBlock(float x, float y, float w, float h, char *str, int flags)\r\n{\r\n}\r\nvoid Draw_AlphaRectangleRGB(int x, int y, int w, int h, int foo, int bar, byte r, byte g, byte b, byte a)\r\n{\r\n\tfloat x1 = x;\r\n\tfloat x2 = x+w;\r\n\tfloat y1 = y;\r\n\tfloat y2 = y+h;\r\n\tdrawfuncs->Colour4f(r/255.0, g/255.0, b/255.0, a/255.0 * alphamul);\r\n\tdrawfuncs->Line(x1, y1, x2, y1);\r\n\tdrawfuncs->Line(x2, y1, x2, y2);\r\n\tdrawfuncs->Line(x1, y2, x2, y2);\r\n\tdrawfuncs->Line(x1, y1, x1, y2);\r\n\tdrawfuncs->Colour4f(1, 1, 1, 1);\r\n}\r\nvoid Draw_AlphaLineRGB(float x1, float y1, float x2, float y2, float width, byte r, byte g, byte b, byte a)\r\n{\r\n\tdrawfuncs->Colour4f(r/255.0, g/255.0, b/255.0, a/255.0 * alphamul);\r\n\tdrawfuncs->Line(x1, y1, x2, y2);\r\n\tdrawfuncs->Colour4f(1, 1, 1, 1);\r\n}\r\n\r\nmpic_t *Draw_CachePicSafe(const char *name, qbool crash, qbool ignorewad)\r\n{\r\n\tif (!*name)\r\n\t\treturn NULL;\r\n\treturn (mpic_t*)(qintptr_t)drawfuncs->LoadImage(name);\r\n}\r\nmpic_t *Draw_CacheWadPic(const char *name)\r\n{\r\n\tchar ftename[MAX_QPATH];\r\n\tQ_snprintf(ftename, sizeof(ftename), \"gfx/%s\", name);\r\n\treturn (mpic_t*)(qintptr_t)drawfuncs->LoadImage(ftename);\r\n}\r\n\r\nmpic_t *SCR_LoadCursorImage(char *cursorimage)\r\n{\r\n\treturn Draw_CachePicSafe(cursorimage, false, true);\r\n}\r\n\r\nunsigned int\tSbar_ColorForMap (unsigned int m)\r\n{\r\n\tif (m >= 16)\r\n\t\treturn m;\r\n\tm = (m < 0) ? 0 : ((m > 13) ? 13 : m);\r\n\tm *= 16;\r\n\treturn m < 128 ? m + 8 : m + 8;\r\n}\r\nint Sbar_TopColor(player_info_t *pi)\r\n{\r\n\treturn Sbar_ColorForMap(pi->topcolour);\r\n}\r\nint Sbar_BottomColor(player_info_t *pi)\r\n{\r\n\treturn Sbar_ColorForMap(pi->bottomcolour);\r\n}\r\nint dehex(char nib)\r\n{\r\n\tif (nib >= '0' && nib <= '9')\r\n\t\treturn nib - '0';\r\n\tif (nib >= 'a' && nib <= 'f')\r\n\t\treturn nib - 'a' + 10;\r\n\tif (nib >= 'A' && nib <= 'F')\r\n\t\treturn nib - 'A' + 10;\r\n\treturn 0;\r\n}\r\nchar *TP_ParseFunChars(char *str)\r\n{\r\n\tstatic char resultbuf[1024];\r\n\tchar *out = resultbuf, *end = resultbuf+sizeof(resultbuf)-1;\r\n\r\n\twhile (out < end)\r\n\t{\r\n\t\tif (str[0] == '$' && str[1] == 'x' && str[2] && str[3])\r\n\t\t{\r\n\t\t\t*out++ = (dehex(str[2]) << 4) | dehex(str[3]);\r\n\t\t\tstr+=4;\r\n\t\t}\r\n\t\telse if (str[0] == '$')\r\n\t\t{\r\n\t\t\tint c = 0;\r\n\t\t\tswitch (str[1])\r\n\t\t\t{\r\n\t\t\t\tcase '\\\\': c = 0x0D; break;\r\n\t\t\t\tcase ':': c = 0x0A; break;\r\n\t\t\t\tcase '[': c = 0x10; break;\r\n\t\t\t\tcase ']': c = 0x11; break;\r\n\t\t\t\tcase 'G': c = 0x86; break;\r\n\t\t\t\tcase 'R': c = 0x87; break;\r\n\t\t\t\tcase 'Y': c = 0x88; break;\r\n\t\t\t\tcase 'B': c = 0x89; break;\r\n\t\t\t\tcase '(': c = 0x80; break;\r\n\t\t\t\tcase '=': c = 0x81; break;\r\n\t\t\t\tcase ')': c = 0x82; break;\r\n\t\t\t\tcase 'a': c = 0x83; break;\r\n\t\t\t\tcase '<': c = 0x1d; break;\r\n\t\t\t\tcase '-': c = 0x1e; break;\r\n\t\t\t\tcase '>': c = 0x1f; break;\r\n\t\t\t\tcase ',': c = 0x1c; break;\r\n\t\t\t\tcase '.': c = 0x9c; break;\r\n\t\t\t\tcase 'b': c = 0x8b; break;\r\n\t\t\t\tcase 'c':\r\n\t\t\t\tcase 'd': c = 0x8d; break;\r\n\t\t\t\tcase '$': c = '$'; break;\r\n\t\t\t\tcase '^': c = '^'; break;\r\n\t\t\t\tcase '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8':case '9': c = str[1] -'0' + 0x12;break;\r\n\t\t\t}\r\n\t\t\tif (c)\r\n\t\t\t{\r\n\t\t\t\t*out++ = c;\r\n\t\t\t\tstr++;\r\n\t\t\t}\r\n\t\t\tstr++;\r\n\t\t}\r\n\t\telse if (*str)\r\n\t\t\t*out++ = *str++;\r\n\t\telse\r\n\t\t\tbreak;\r\n\t}\r\n\t*out = 0;\r\n\treturn resultbuf;\r\n}\r\nchar *TP_ItemName(unsigned int itbit)\r\n{\r\n\tcvar_t *var = NULL;\r\n\tswitch (itbit)\r\n\t{\r\n#define ITEMNAME(it, nam)\tcase it: var = cvarfuncs->GetNVFDG(\"tp_name_\"#nam, #nam, 0, NULL, \"Item Names\"); break\r\n\tITEMNAME(IT_SHOTGUN,\t\t\tsg);\r\n\tITEMNAME(IT_SUPER_SHOTGUN,\t\tssg);\r\n\tITEMNAME(IT_NAILGUN,\t\t\tng);\r\n\tITEMNAME(IT_SUPER_NAILGUN,\t\tsng);\r\n\tITEMNAME(IT_GRENADE_LAUNCHER,\tgl);\r\n\tITEMNAME(IT_ROCKET_LAUNCHER,\trl);\r\n\tITEMNAME(IT_LIGHTNING,\t\t\tlg);\r\n//\tITEMNAME(IT_SUPER_LIGHTNING,\t???);\r\n\tITEMNAME(IT_SHELLS,\t\t\t\tshells);\r\n\tITEMNAME(IT_NAILS,\t\t\t\tnails);\r\n\tITEMNAME(IT_ROCKETS,\t\t\trockets);\r\n\tITEMNAME(IT_CELLS,\t\t\t\tcells);\r\n\tITEMNAME(IT_AXE,\t\t\t\taxe);\r\n\tITEMNAME(IT_ARMOR1,\t\t\t\tga);\r\n\tITEMNAME(IT_ARMOR2,\t\t\t\tya);\r\n\tITEMNAME(IT_ARMOR3,\t\t\t\tra);\r\n\tITEMNAME(IT_SUPERHEALTH,\t\tmh);\r\n//\tITEMNAME(IT_KEY1,\t\t\t\t);\r\n//\tITEMNAME(IT_KEY2,\t\t\t\t);\r\n\tITEMNAME(IT_INVISIBILITY,\t\tring);\r\n\tITEMNAME(IT_INVULNERABILITY,\tpent);\r\n\tITEMNAME(IT_SUIT,\t\t\t\tsuit);\r\n\tITEMNAME(IT_QUAD,\t\t\t\tquad);\r\n//\tITEMNAME(IT_SIGIL1,\t\t\t\t);\r\n//\tITEMNAME(IT_SIGIL2,\t\t\t\t);\r\n//\tITEMNAME(IT_SIGIL3,\t\t\t\t);\r\n//\tITEMNAME(IT_SIGIL4,\t\t\t\t);\r\n\t}\r\n\tif (var)\r\n\t\treturn var->string;\r\n\treturn va(\"it%#x\", itbit);\r\n}\r\n\r\nvoid Replace_In_String(char *src, size_t strsize, char leadchar, int patterns, ...)\r\n{\r\n\tchar orig[1024];\r\n\tchar *out, *outstop;\r\n\tva_list ap;\r\n\tint i;\r\n\r\n\tstrlcpy(orig, src, sizeof(orig));\r\n\tout = src;\r\n\toutstop = out + strsize-1;\r\n\tsrc = orig;\r\n\r\n\twhile(*src)\r\n\t{\r\n\t\tif (out == outstop)\r\n\t\t\tbreak;\r\n\t\tif (*src != leadchar)\r\n\t\t\t*out++ = *src++;\r\n\t\telse if (*++src == leadchar)\r\n\t\t\t*out++ = leadchar;\r\n\t\telse\r\n\t\t{\r\n\t\t\tva_start(ap, patterns);\r\n\t\t\tfor (i = 0; i < patterns; i++)\r\n\t\t\t{\r\n\t\t\t\tconst char *arg = va_arg(ap, const char *);\r\n\t\t\t\tconst char *val = va_arg(ap, const char *);\r\n\t\t\t\tsize_t alen = strlen(arg);\r\n\t\t\t\tif (!strncmp(src, arg, strlen(arg)))\r\n\t\t\t\t{\r\n\t\t\t\t\tstrlcpy(out, val, (outstop-out)+1);\r\n\t\t\t\t\tout += strlen(out);\r\n\t\t\t\t\tsrc += alen;\r\n\t\t\t\t\tbreak;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\tif (i == patterns)\r\n\t\t\t{\r\n\t\t\t\tstrlcpy(out, \"unknown\", (outstop-out)+1);\r\n\t\t\t\tout += strlen(out);\r\n\t\t\t}\r\n\t\t\tva_end(ap);\r\n\t\t}\r\n\t}\r\n\t*out = 0;\r\n}\r\n\r\nint SCR_GetClockStringWidth(const char *s, qbool big, float scale)\r\n{\r\n\tint w = 0;\r\n\tif (big)\r\n\t{\r\n\t\twhile(*s)\r\n\t\t\tw += ((*s++==':')?16:24);\r\n\t}\r\n\telse\r\n\t\tw = strlen(s) * 8;\r\n\treturn w * scale;\r\n}\r\nint SCR_GetClockStringHeight(qbool big, float scale)\r\n{\r\n\treturn (big?24:8)*scale;\r\n}\r\nchar *SecondsToMinutesString(int print_time, char *buffer, size_t buffersize) {\r\n\tint tens_minutes, minutes, tens_seconds, seconds;\r\n\r\n\ttens_minutes = fmod (print_time / 600, 6);\r\n\tminutes = fmod (print_time / 60, 10);\r\n\ttens_seconds = fmod (print_time / 10, 6);\r\n\tseconds = fmod (print_time, 10);\r\n\tsnprintf (buffer, buffersize, \"%i%i:%i%i\", tens_minutes, minutes, tens_seconds, seconds);\r\n\treturn buffer;\r\n}\r\nchar *SCR_GetGameTime(int t, char *buffer, size_t buffersize)\r\n{\r\n\tfloat timelimit;\r\n\r\n\ttimelimit = (t == TIMETYPE_GAMECLOCKINV) ? 60 * infofloat(cl.serverinfo, \"timelimit\", 0) + 1: 0;\r\n\r\n\tif (cl.countdown || cl.standby)\r\n\t\tSecondsToMinutesString(timelimit, buffer, buffersize);\r\n\telse\r\n\t\tSecondsToMinutesString((int) fabs(timelimit - (cl.time - cl.matchstart)), buffer, buffersize);\r\n\treturn buffer;\r\n}\r\n\t\r\nconst char* SCR_GetTimeString(int timetype, const char *format)\r\n{\r\n\tstatic char buffer[256];\r\n\tswitch(timetype)\r\n\t{\r\n\tcase TIMETYPE_CLOCK:\r\n\t\t{\r\n\t\t\ttime_t t;\r\n\t\t\tstruct tm *ptm;\r\n\t\t\ttime (&t);\r\n\t\t\tptm = localtime (&t);\r\n\t\t\tif (!ptm) return \"-:-\";\r\n\t\t\tif (!strftime(buffer, sizeof(buffer)-1, format, ptm)) return \"-:-\";\r\n\t\t\treturn buffer;\r\n\t\t}\r\n\tcase TIMETYPE_GAMECLOCK:\r\n\tcase TIMETYPE_GAMECLOCKINV:\r\n\t\treturn SCR_GetGameTime(timetype, buffer, sizeof(buffer));\r\n\r\n\tcase TIMETYPE_DEMOCLOCK:\r\n\t\treturn SecondsToMinutesString(infofloat(cl.serverinfo, \"demotime\", 0), buffer, sizeof(buffer));\r\n\r\n\tdefault:\r\n\t\treturn \"01234\";\r\n\t}\r\n}\r\nvoid SCR_DrawBigClock(int x, int y, int style, int blink, float scale, const char *t)\r\n{\r\n    extern  mpic_t  *sb_nums[2][11];\r\n    extern  mpic_t  *sb_colon/*, *sb_slash*/;\r\n\tqbool lblink = (int)(cls.realtime*2) & 1;\r\n\r\n    if (style > 1)  style = 1;\r\n    if (style < 0)  style = 0;\r\n\r\n\twhile (*t)\r\n    {\r\n        if (*t >= '0'  &&  *t <= '9')\r\n        {\r\n\t\t\tDraw_STransPic(x, y, sb_nums[style][*t-'0'], scale);\r\n            x += 24*scale;\r\n        }\r\n        else if (*t == ':')\r\n        {\r\n            if (lblink || !blink)\r\n\t\t\t\tDraw_STransPic (x, y, sb_colon, scale);\r\n\r\n\t\t\tx += 16*scale;\r\n        }\r\n        else\r\n\t\t{\r\n\t\t\tDraw_SCharacter(x, y, *t+(style?128:0), 3*scale);\r\n            x += 24*scale;\r\n\t\t}\r\n        t++;\r\n    }\r\n}\r\nvoid SCR_DrawSmallClock(int x, int y, int style, int blink, float scale, const char *t)\r\n{\r\n\tqbool lblink = (int)(cls.realtime*2) & 1;\r\n\tint c;\r\n\r\n    if (style > 3)  style = 3;\r\n    if (style < 0)  style = 0;\r\n\r\n    while (*t)\r\n    {\r\n\t\tc = (int) *t;\r\n        if (c >= '0'  &&  c <= '9')\r\n        {\r\n            if (style == 1)\r\n                c += 128;\r\n            else if (style == 2  ||  style == 3)\r\n                c -= 30;\r\n        }\r\n        else if (c == ':')\r\n        {\r\n            if (style == 1  ||  style == 3)\r\n                c += 128;\r\n            if (lblink ||  !blink)\r\n                ;\r\n            else\r\n                c = ' ';\r\n        }\r\n        Draw_SCharacter(x, y, c, scale);\r\n        x+= 8*scale;\r\n        t++;\r\n    }\r\n}\r\n\r\n#include \"builtin_huds.h\"\r\nvoid EZHud_UseNquake_f(void)\r\n{\r\n\tconst char *hudstr = builtin_hud_nquake;\r\n\tcmdfuncs->AddText(hudstr, true);\r\n}\r\n\r\nint IN_BestWeapon(void)\r\n{\r\n\treturn 0;\r\n}\r\n\r\nqbool VID_VSyncIsOn(void){return false;}\r\ndouble vid_vsync_lag;\r\n\r\n\r\nvrect_t scr_vrect;\r\n\r\nvoid EZHud_Tick(double realtime, double gametime)\r\n{\r\n\tcls.realtime = realtime;\r\n\tcl.time = gametime;\r\n}\r\nchar *findinfo(char *info, char *findkey)\r\n{\r\n\tint kl = strlen(findkey);\r\n\tchar *key, *value;\r\n\twhile(*info)\r\n\t{\r\n\t\tkey = strchr(info, '\\\\');\r\n\t\tif (!key)\r\n\t\t\tbreak;\r\n\t\tkey++;\r\n\t\tvalue = strchr(key, '\\\\');\r\n\t\tif (!value)\r\n\t\t\tbreak;\r\n\t\tinfo = value+1;\r\n\t\tif (!strncmp(key, findkey, kl) && key[kl] == '\\\\')\r\n\t\t\treturn info;\r\n\t}\r\n\treturn NULL;\r\n}\r\nchar *infostring(char *info, char *findkey, char *buffer, size_t bufsize)\r\n{\r\n\tchar *value = findinfo(info, findkey);\r\n\tchar *end;\r\n\tif (value)\r\n\t{\r\n\t\tend = strchr(value, '\\\\');\r\n\t\tif (!end)\r\n\t\t\tend = value + strlen(value);\r\n\t\tbufsize--;\r\n\t\tif (bufsize > (end - value))\r\n\t\t\tbufsize = (end - value);\r\n\t\tmemcpy(buffer, value, bufsize);\r\n\t\tbuffer[bufsize] = 0;\r\n\t\treturn buffer;\r\n\t}\r\n\t*buffer = 0;\r\n\treturn buffer;\r\n}\r\nfloat infofloat(char *info, char *findkey, float def)\r\n{\r\n\tchar *value = findinfo(info, findkey);\r\n\tif (value)\r\n\t\treturn atof(value);\r\n\treturn def;\r\n}\r\nint EZHud_Draw(int seat, float viewx, float viewy, float viewwidth, float viewheight, int showscores)\r\n{\r\n\tchar serverinfo[4096];\r\n\tchar val[64];\r\n\tint i;\r\n\r\n\tstatic float lasttime, lasttime_min = 99999;\r\n\tstatic int framecount;\r\n\tstatic float oldtime;\r\n\tif (cls.realtime - lasttime > 1)\r\n\t{\r\n\t\tcls.fps = framecount/(cls.realtime - lasttime);\r\n\t\tlasttime = cls.realtime;\r\n\t\tframecount = 0;\r\n\r\n\t\tif (cls.realtime - lasttime_min > 30)\r\n\t\t{\r\n\t\t\tcls.min_fps = cls.fps;\r\n\t\t\tlasttime_min = cls.realtime;\r\n\t\t}\r\n\t\telse if (cls.min_fps > cls.fps)\r\n\t\t\tcls.min_fps = cls.fps;\r\n\t}\r\n\tif (!seat)\r\n\t{\r\n\t\tcls.frametime = cls.realtime - oldtime;\r\n\t\tframecount++;\r\n\t\toldtime = cls.realtime;\r\n\t}\r\n\r\n\tcl.splitscreenview = seat;\r\n\tscr_vrect.x = viewx;\r\n\tscr_vrect.y = viewy;\r\n\tscr_vrect.width = viewwidth;\r\n\tscr_vrect.height = viewheight;\r\n\tsb_showscores = showscores & 1;\r\n\tsb_showteamscores = showscores & 2;\r\n\r\n\tclientfuncs->GetStats(0, cl.stats, sizeof(cl.stats)/sizeof(cl.stats[0]));\r\n\tfor (i = 0; i < 32; i++)\r\n\t\tclientfuncs->GetPlayerInfo(i, &cl.players[i]);\r\n\r\n\tclientfuncs->GetLocalPlayerNumbers(cl.splitscreenview, 1, &cl.playernum, &cl.tracknum);\r\n\tclientfuncs->GetServerInfoRaw(cl.serverinfo, sizeof(serverinfo));\r\n\tcl.deathmatch = infofloat(cl.serverinfo, \"deathmatch\", 0);\r\n\tcl.teamplay = infofloat(cl.serverinfo, \"teamplay\", 0);\r\n\tcl.intermission = infofloat(cl.serverinfo, \"intermission\", 0);\r\n\tcl.spectator = (cl.playernum>=32)||cl.players[cl.playernum].spectator;\r\n\tinfostring(cl.serverinfo, \"status\", val, sizeof(val));\r\n\tcl.standby = !strcmp(val, \"standby\");\r\n\tcl.countdown = !strcmp(val, \"countdown\");\r\n\tcl.matchstart = infofloat(cl.serverinfo, \"matchstart\", 0);\r\n\tcls.state = ca_active;\r\n\r\n\tinfostring(cl.serverinfo, \"demotype\", val, sizeof(val));\r\n\tcls.mvdplayback = !strcmp(val, \"mvd\");\r\n\tcls.demoplayback = strcmp(val, \"\");\r\n\r\n\t\r\n\t{\r\n\t\tstatic cvar_t *pscr_viewsize = NULL;\r\n\t\tint size;\r\n\t\tif (!pscr_viewsize)\r\n\t\t\tpscr_viewsize = cvarfuncs->GetNVFDG(\"viewsize\", \"100\", 0, NULL, NULL);\r\n\t\tsize = cl.intermission ? 120 : pscr_viewsize->value;\r\n\t\tif (size >= 120)\r\n\t\t\tsb_lines = 0;           // no status bar at all\r\n\t\telse if (size >= 110)\r\n\t\t\tsb_lines = 24;          // no inventory\r\n\t\telse\r\n\t\t\tsb_lines = 24 + 16 + 8;\r\n\t}\r\n\r\n\tclientfuncs->GetPredInfo(seat, cl.simvel);\r\n\r\n\r\n\t//cl.faceanimtime\r\n\t//cl.item_gettime\r\n\r\n\t//cls.state;\r\n\t//cls.min_fps;\r\n\t//cls.fps;\r\n\t////cls.realtime;\r\n\t////cls.trueframetime;\r\n\r\n\thost_screenupdatecount++;\r\n\tHUD_Draw();\r\n\treturn true;\r\n}\r\n\r\nunsigned int keydown[K_MAX];\r\nfloat cursor_x;\r\nfloat cursor_y;\r\nfloat mouse_x;\r\nfloat mouse_y;\r\nmpic_t\t*scr_cursor_icon\t= NULL;\r\nqboolean QDECL EZHud_MenuEvent(int eventtype, int keyparam, int unicodeparm, float mousecursor_x, float mousecursor_y, float vidwidth, float vidheight)\r\n{\r\n\tmouse_x += mousecursor_x - cursor_x;\t//FIXME: the hud editor should NOT need this sort of thing\r\n\tmouse_y += mousecursor_y - cursor_y;\r\n\tcursor_x = mousecursor_x;\r\n\tcursor_y = mousecursor_y;\r\n\r\n\tHUD_Editor_MouseEvent(cursor_x, cursor_y);\r\n\r\n\tswitch(eventtype)\r\n\t{\r\n\tcase 0:\t//draw\r\n\t\thost_screenupdatecount++;\r\n\t\tHUD_Draw();\r\n\t\tHUD_Editor_Draw();\r\n\r\n\t\tmouse_x = 0;\r\n\t\tmouse_y = 0;\r\n\t\tbreak;\r\n\tcase 1:\r\n\t\tif (keyparam < K_MAX)\r\n\t\t\tkeydown[keyparam] = true;\r\n\t\tHUD_Editor_Key(keyparam, 0, true);\r\n\t\tbreak;\r\n\tcase 2:\r\n\t\tif (keyparam < K_MAX)\r\n\t\t\tkeydown[keyparam] = false;\r\n\t\tHUD_Editor_Key(keyparam, 0, false);\r\n\t\tbreak;\r\n\t}\r\n\treturn 1;\r\n}\r\n\r\nqboolean Plug_Init(void)\r\n{\r\n\tdrawfuncs = plugfuncs->GetEngineInterface(plug2dfuncs_name, sizeof(*drawfuncs));\r\n\tclientfuncs = plugfuncs->GetEngineInterface(plugclientfuncs_name, sizeof(*clientfuncs));\r\n\tfilefuncs =  plugfuncs->GetEngineInterface(plugfsfuncs_name, sizeof(*filefuncs));\r\n\tinputfuncs = plugfuncs->GetEngineInterface(pluginputfuncs_name, sizeof(*inputfuncs));\r\n\r\n\tplugfuncs->ExportFunction(\"UpdateVideo\", EZHud_UpdateVideo);\r\n\r\n\tif (cvarfuncs && drawfuncs && clientfuncs && filefuncs && inputfuncs &&\r\n\t\tplugfuncs->ExportFunction(\"SbarBase\", EZHud_Draw) &&\r\n\t\tplugfuncs->ExportFunction(\"MenuEvent\", EZHud_MenuEvent) &&\r\n\t\tplugfuncs->ExportFunction(\"Tick\", EZHud_Tick))\r\n\t{\r\n\t\tCmd_AddCommand(\"ezhud_nquake\", EZHud_UseNquake_f);\r\n\t\tHUD_Init();\r\n\t\tHUD_Editor_Init();\r\n\t\treturn true;\r\n\t}\r\n\tCon_Printf(\"EZHud: Unable to initiailise\\n\");\r\n\treturn false;\r\n}\r\n"
  },
  {
    "path": "plugins/ezhud/ezquakeisms.h",
    "content": "//ezquake likes this\r\n#ifndef FTEPLUGIN\r\n#include \"quakedef.h\"\r\n#define FTEENGINE\t//we're getting statically linked. lucky us.\r\n#define FTEPLUGIN\r\n#endif\r\n#include \"../plugin.h\"\r\n#include <assert.h>\r\n#include <ctype.h>\r\n\r\n#ifdef FTEENGINE\r\n#define drawfuncs ezhud_drawfuncs\r\n#define filefuncs ezhud_filefuncs\r\n#define clientfuncs ezhud_clientfuncs\r\n#define inputfuncs ezhud_inputfuncs\r\n#endif\r\n\r\nextern plug2dfuncs_t *drawfuncs;\r\nextern plugfsfuncs_t *filefuncs;\r\nextern plugclientfuncs_t *clientfuncs;\r\nextern pluginputfuncs_t *inputfuncs;\r\n\r\n//ezquake sucks. I'd fix these, but that'd make diffs more messy.\r\n#ifdef __GNUC__\r\n\t#pragma GCC diagnostic ignored \"-Wold-style-definition\"\r\n\t#pragma GCC diagnostic ignored \"-Wstrict-prototypes\"\r\n\t#pragma GCC diagnostic ignored \"-Wmissing-prototypes\"\r\n#endif\r\n\r\n//ezquake types.\r\n#define byte qbyte\r\n#define qbool qboolean\r\n#define Com_Printf Con_Printf\r\n#define Com_DPrintf Con_DPrintf\r\n#define Cvar_Find(n) cvarfuncs->GetNVFDG(n,NULL,0,NULL,NULL)\r\n#define Cvar_SetValue(var,val) cvarfuncs->SetFloat(var->name,val)\r\n#define Cvar_Set(var,val) cvarfuncs->SetString(var->name,val)\r\n#define Cmd_AddCommand(nam,ptr) cmdfuncs->AddCommand(nam,ptr,NULL)\r\n#define Cmd_Argc cmdfuncs->Argc\r\n#define Cbuf_AddText(x) cmdfuncs->AddText(x,false)\r\n#define Sys_Error(x) plugfuncs->Error(x)\r\n#define Q_calloc calloc\r\n#define Q_malloc malloc\r\n#define Q_strdup strdup\r\n#define Q_free free\r\n#define Q_rint(x) ((int)(x+0.5))\r\n#define Q_atoi atoi\r\n#ifdef FTEENGINE\r\n\t#define strlcpy Q_strncpyz\r\n\t#define strlcat Q_strncatz\r\n#else\r\n\t#define strlcpy Q_strlcpy\r\n\t#define strlcat Q_strlcat\r\n#endif\r\n\r\n//ezhud has a number of common symbol conflicts, which matter when sttatically linking into the engine\r\n#define Cmd_Argv\t\t\tezCmd_Argv\r\n#define TP_LocationName\t\tezTP_LocationName\r\n#define TP_ParseFunChars\tezTP_ParseFunChars\r\n#define Sbar_ColorForMap\tezSbar_ColorForMap\r\n#define scr_vrect\t\t\tezscr_vrect\r\n#define sb_lines\t\t\tezsb_lines\r\n#define keydown\t\t\t\tezkeydown\r\n#define scr_con_current\t\tezscr_con_current\r\n\r\n\r\n#undef mpic_t\r\n#define mpic_t void\r\n\r\n\r\n#define MV_VIEWS 4\r\n\r\n\r\nextern float cursor_x;\r\nextern float cursor_y;\r\nextern int host_screenupdatecount;\r\nextern cvar_t *scr_newHud;\r\nextern cvar_t *cl_multiview;\r\n\r\n#define Cam_TrackNum() cl.tracknum\r\n#define spec_track cl.tracknum\r\n#define autocam ((spec_track==-1)?CAM_NONE:CAM_TRACK)\r\n#define CAM_TRACK true\r\n#define CAM_NONE false\r\n//#define HAXX\r\n\r\n#define vid plugvid\r\n#define cls plugcls\r\n#define cl plugcl\r\n#define player_info_t plugclientinfo_t\r\n\r\nextern struct ezcl_s{\r\n\tint intermission;\r\n\tint teamplay;\r\n\tint deathmatch;\r\n\tint stats[MAX_CL_STATS];\r\n\tint item_gettime[32];\r\n\tchar serverinfo[4096];\r\n\tplayer_info_t players[MAX_CLIENTS];\r\n\tint playernum;\r\n\tint tracknum;\r\n\tvec3_t simvel;\r\n\tfloat time;\r\n\tfloat matchstart;\r\n\tfloat faceanimtime;\r\n\tqboolean spectator;\r\n\tqboolean standby;\r\n\tqboolean countdown;\r\n\r\n\tint splitscreenview;\r\n} cl;\r\nextern struct ezcls_s{\r\n\tint state;\r\n\tfloat min_fps;\r\n\tfloat fps;\r\n\tfloat realtime;\r\n\tfloat frametime;\r\n\tqbool mvdplayback;\r\n\tint demoplayback;\r\n} cls;\r\nextern struct ezvid_s{\r\n\tint width;\r\n\tint height;\r\n//\tfloat displayFrequency;\r\n} vid;\r\n\r\n\r\n//reimplementations of ezquake functions\r\nvoid Draw_SetOverallAlpha(float a);\r\nvoid Draw_AlphaFillRGB(float x, float y, float w, float h, qbyte r, qbyte g, qbyte b, qbyte a);\r\nvoid Draw_Fill(float x, float y, float w, float h, qbyte pal);\r\nconst char *ColorNameToRGBString (const char *newval);\r\nbyte *StringToRGB(const char *str);\r\n\r\n#define Draw_String\t\t\t\t\tdrawfuncs->String\r\n\r\nvoid Draw_EZString(float x, float y, char *str, float scale, qboolean red);\r\n#define Draw_Alt_String(x,y,s)\t\t\tDraw_EZString(x,y,s,8,true)\r\n#define Draw_ColoredString(x,y,str,alt)\tDraw_EZString(x,y,str,8,alt)\r\n#define Draw_SString(x,y,str,sc)\t\tDraw_EZString(x,y,str,8*sc,false)\r\n#define Draw_SAlt_String(x,y,str,sc)\tDraw_EZString(x,y,str,8*sc,true)\r\n\r\nvoid Draw_SPic(float x, float y, mpic_t *pic, float scale);\r\nvoid Draw_SSubPic(float x, float y, mpic_t *pic, float s1, float t1, float s2, float t2, float scale);\r\n#define Draw_STransPic Draw_SPic\r\nvoid Draw_Character(float x, float y, unsigned int ch);\r\nvoid Draw_SCharacter(float x, float y, unsigned int ch, float scale);\r\n\r\nvoid SCR_DrawWadString(float x, float y, float scale, char *str);\r\n\r\nvoid Draw_SAlphaSubPic2(float x, float y, mpic_t *pic, float s1, float t1, float s2, float t2, float w, float h, float alpha);\r\n\r\nvoid Draw_AlphaFill(float x, float y, float w, float h, unsigned int pal, float alpha);\r\nvoid Draw_AlphaPic(float x, float y, mpic_t *pic, float alpha);\r\nvoid Draw_AlphaSubPic(float x, float y, mpic_t *pic, float s1, float t1, float s2, float t2, float alpha);\r\nvoid SCR_HUD_DrawBar(int direction, int value, float max_value, float *rgba, int x, int y, int width, int height);\r\n\r\nmpic_t *Draw_CachePicSafe(const char *name, qbool crash, qbool ignorewad);\r\nmpic_t *Draw_CacheWadPic(const char *name);\r\n\r\nint Sbar_TopColor(player_info_t *pi);\r\nint Sbar_BottomColor(player_info_t *pi);\r\nchar *TP_ParseFunChars(char*);\r\nchar *TP_ItemName(unsigned int itbit);\r\nchar*\t\tTP_LocationName (const vec3_t location);\r\n\r\nchar *Cmd_Argv(int arg);\r\nextern float           scr_con_current;\t//current console lines shown\r\n\r\n#define Util_SkipChars(src,strip,dst,dstlen) strlcpy(dst,src,dstlen)\r\n#define Util_SkipEZColors(src,dst,dstlen) strlcpy(dst,src,dstlen)\r\n\r\nvoid Replace_In_String(char *string, size_t strsize, char leadchar, int patterns, ...);\r\n//static qbool Utils_RegExpMatch(char *regexp, char *term) {return true;}\r\n#define Utils_RegExpMatch(regexp,term) (true)\r\n\r\n#define clamp(v,min,max) v=bound(min,v,max)\r\n#define strlen_color(line) (drawfuncs->StringWidth(8, 0, line)/8.0)\r\n\r\n#define TIMETYPE_CLOCK 0\r\n#define TIMETYPE_GAMECLOCK 1\r\n#define TIMETYPE_GAMECLOCKINV 2\r\n#define TIMETYPE_DEMOCLOCK 3\r\nint SCR_GetClockStringWidth(const char *s, qbool big, float scale);\r\nint SCR_GetClockStringHeight(qbool big, float scale);\r\nconst char* SCR_GetTimeString(int timetype, const char *format);\r\nvoid SCR_DrawBigClock(int x, int y, int style, int blink, float scale, const char *t);\r\nvoid SCR_DrawSmallClock(int x, int y, int style, int blink, float scale, const char *t);\r\n\r\ntypedef struct\r\n{\r\n\tqbyte c[4];\r\n} clrinfo_t;\r\nvoid Draw_ColoredString3(float x, float y, const char *str, clrinfo_t *clr, int huh, int wut);\r\nvoid UI_PrintTextBlock(float x, float y, float w, float h, char *str, int flags);\r\nvoid Draw_AlphaRectangleRGB(int x, int y, int w, int h, int foo, int bar, byte r, byte g, byte b, byte a);\r\nvoid Draw_AlphaLineRGB(float x1, float y1, float x2, float y2, float width, byte r, byte g, byte b, byte a);\r\nvoid Draw_Polygon(int x, int y, vec3_t *vertices, int num_vertices, qbool fill, byte r, byte g, byte b, byte a);\r\n\r\nextern int\t\t\tsb_lines;\t\t\t// scan lines to draw\r\n#ifndef SBAR_HEIGHT\r\n#define SBAR_HEIGHT 24\r\n#define STAT_HEALTH\t\t\t0\r\n#define STAT_WEAPONMODELI\t2\r\n#define STAT_AMMO\t\t\t3\r\n#define STAT_ARMOR\t\t\t4\r\n#define STAT_WEAPONFRAME\t5\r\n#define STAT_SHELLS\t\t\t6\r\n#define STAT_NAILS\t\t\t7\r\n#define STAT_ROCKETS\t\t8\r\n#define STAT_CELLS\t\t\t9\r\n#define STAT_ACTIVEWEAPON\t10\r\n#define STAT_TOTALSECRETS\t11\r\n#define STAT_TOTALMONSTERS\t12\r\n#define STAT_SECRETS\t\t13\t\t// bumped on client side by svc_foundsecret\r\n#define STAT_MONSTERS\t\t14\t\t// bumped by svc_killedmonster\r\n#define STAT_ITEMS\t\t\t15\r\n#define STAT_VIEWHEIGHT\t\t16\t//same as zquake\r\n#define STAT_TIME\t\t\t17\t//zquake\r\n#define STAT_MATCHSTARTTIME 18\r\n\r\n#define\tIT_SHOTGUN\t\t\t\t(1u<<0)\r\n#define\tIT_SUPER_SHOTGUN\t\t(1u<<1)\r\n#define\tIT_NAILGUN\t\t\t\t(1u<<2)\r\n#define\tIT_SUPER_NAILGUN\t\t(1u<<3)\r\n\r\n#define\tIT_GRENADE_LAUNCHER\t\t(1u<<4)\r\n#define\tIT_ROCKET_LAUNCHER\t\t(1u<<5)\r\n#define\tIT_LIGHTNING\t\t\t(1u<<6)\r\n#define\tIT_SUPER_LIGHTNING\t\t(1u<<7)\r\n\r\n#define\tIT_SHELLS\t\t\t\t(1u<<8)\r\n#define\tIT_NAILS\t\t\t\t(1u<<9)\r\n#define\tIT_ROCKETS\t\t\t\t(1u<<10)\r\n#define\tIT_CELLS\t\t\t\t(1u<<11)\r\n\r\n#define\tIT_AXE\t\t\t\t\t(1u<<12)\r\n\r\n#define\tIT_ARMOR1\t\t\t\t(1u<<13)\r\n#define\tIT_ARMOR2\t\t\t\t(1u<<14)\r\n#define\tIT_ARMOR3\t\t\t\t(1u<<15)\r\n\r\n#define\tIT_SUPERHEALTH\t\t\t(1u<<16)\r\n\r\n#define\tIT_KEY1\t\t\t\t\t(1u<<17)\r\n#define\tIT_KEY2\t\t\t\t\t(1u<<18)\r\n\r\n#define\tIT_INVISIBILITY\t\t\t(1u<<19)\r\n\r\n#define\tIT_INVULNERABILITY\t\t(1u<<20)\r\n#define\tIT_SUIT\t\t\t\t\t(1u<<21)\r\n#define\tIT_QUAD\t\t\t\t\t(1u<<22)\r\n\r\n#define\tIT_SIGIL1\t\t\t\t(1u<<28)\r\n\r\n#define\tIT_SIGIL2\t\t\t\t(1u<<29)\r\n#define\tIT_SIGIL3\t\t\t\t(1u<<30)\r\n#define\tIT_SIGIL4\t\t\t\t(1u<<31)\r\n#endif"
  },
  {
    "path": "plugins/ezhud/hud.c",
    "content": "/*\nCopyright (C) 2011 azazello and ezQuake team\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program.  If not, see <http://www.gnu.org/licenses/>.\n*/\n//\n// HUD commands\n//\n\n#include \"ezquakeisms.h\"\n//#include \"common_draw.h\"\n//#include \"keys.h\"\n#include \"hud.h\"\n#include \"hud_common.h\"\n#include \"hud_editor.h\"\n//#include \"utils.h\"\n//#include \"sbar.h\"\n\n#define sbar_last_width 320  // yeah yeah I know, *garbage* -> leave it be :>\n\nchar *align_strings_x[] = {\n    \"left\",\n    \"center\",\n    \"right\",\n    \"before\",\n    \"after\"\n};\n#define  num_align_strings_x  (sizeof(align_strings_x) / sizeof(align_strings_x[0]))\n\nchar *align_strings_y[] = {\n    \"top\",\n    \"center\",\n    \"bottom\",\n    \"before\",\n    \"after\",\n    \"console\"\n};\n#define  num_align_strings_y  (sizeof(align_strings_y) / sizeof(align_strings_y[0]))\n\nchar *snap_strings[] = {\n    \"screen\",\n    \"top\",\n    \"view\",\n    \"sbar\",\n    \"ibar\",\n    \"hbar\",\n    \"sfree\",\n    \"ifree\",\n    \"hfree\",\n};\n#define  num_snap_strings  (sizeof(snap_strings) / sizeof(snap_strings[0]))\n\n// Hud elements list.\nhud_t *hud_huds = NULL;\n\nqbool doreorder;\n\n//\n// Hud plus func - show element.\n//\nvoid HUD_Plus_f(void)\n{\n    char *t;\n    hud_t *hud;\n\n    if (Cmd_Argc() < 1)\n        return;\n\n    t = Cmd_Argv(0);\n    if (strncmp(t, \"+hud_\", 5))\n        return;\n\n    hud = HUD_Find(t + 5);\n    if (!hud)\n    {\n        // This should never happen...\n        return;\n    }\n\n    if (!hud->show)\n    {\n        // This should never happen...\n        return;\n    }\n\n    Cvar_Set(hud->show, \"1\");\n}\n\n//\n// Hud minus func - hide element.\n//\nvoid HUD_Minus_f(void)\n{\n    char *t;\n    hud_t *hud;\n\n    if (Cmd_Argc() < 1)\n        return;\n\n    t = Cmd_Argv(0);\n    if (strncmp(t, \"-hud_\", 5))\n        return;\n\n    hud = HUD_Find(t + 5);\n    if (!hud)\n    {\n        // this should never happen...\n        return;\n    }\n\n    if (!hud->show)\n    {\n        // this should never happen...\n        return;\n    }\n\n    Cvar_Set(hud->show, \"0\");\n}\n\n//\n// Hud element func - describe it\n// this also solves the TAB completion problem\n//\nvoid HUD_Func_f(void)\n{\n    int i;\n    hud_t *hud;\n\n    hud = HUD_Find(Cmd_Argv(0));\n\n    if (!hud)\n    {\n        // This should never happen...\n        Com_Printf(\"Hud element not found\\n\");\n        return;\n    }\n\n    if (Cmd_Argc() > 1)\n    {\n        char buf[512];\n\n        snprintf(buf, sizeof(buf), \"hud_%s_%s\", hud->name, Cmd_Argv(1));\n        if (Cvar_Find(buf) != NULL)\n        {\n            Cbuf_AddText(buf);\n            if (Cmd_Argc() > 2)\n            {\n                Cbuf_AddText(\" \");\n                Cbuf_AddText(Cmd_Argv(2));\n            }\n            Cbuf_AddText(\"\\n\");\n        }\n        else\n        {\n            Com_Printf(\"Trying \\\"%s\\\" - no such variable\\n\", buf);\n        }\n        return;\n    }\n\n    // Description.\n    Com_Printf(\"%s\\n\\n\", hud->description);\n\n    // Status.\n    if (hud->show != NULL)\n\t{\n        Com_Printf(\"Current status: %s\\n\", hud->show->value ? \"shown\" : \"hidden\");\n\t}\n\n\tif (hud->frame != NULL)\n\t{\n        Com_Printf(\"Frame:          %s\\n\\n\", hud->frame->string);\n\t}\n\n\tif (hud->frame_color != NULL)\n\t{\n        Com_Printf(\"Frame color:          %s\\n\\n\", hud->frame_color->string);\n\t}\n\n    // Placement.\n    Com_Printf(\"Placement:        %s\\n\", hud->place->string);\n\n    // Alignment.\n    Com_Printf(\"Alignment (x y):  %s %s\\n\", hud->align_x->string, hud->align_y->string);\n\n    // Position.\n    Com_Printf(\"Offset (x y):     %d %d\\n\", (int)(hud->pos_x->value), (int)(hud->pos_y->value));\n\n\t// Ordering.\n\tCom_Printf(\"Draw Order (z):   %d\\n\", (int)hud->order->value);\n\n    // Additional parameters.\n    if (hud->num_params > 0)\n    {\n        int prefix_l = strlen(va(\"hud_%s_\", hud->name));\n        Com_Printf(\"\\nParameters:\\n\");\n        for (i=0; i < hud->num_params; i++)\n        {\n            if (strlen(hud->params[i]->name) > prefix_l)\n                Com_Printf(\"  %-15s %s\\n\", hud->params[i]->name + prefix_l,\n                    hud->params[i]->string);\n        }\n    }\n}\n\n//\n// Find the elements with the max and min z-order.\n//\nvoid HUD_FindMaxMinOrder(int *max, int *min)\n{\n\thud_t *hud = hud_huds;\n\n\twhile(hud)\n\t{\n\t\t(*min) = ((int)hud->order->value < (*min)) ? (int)hud->order->value : (*min);\n\t\t(*max) = ((int)hud->order->value > (*max)) ? (int)hud->order->value : (*max);\n\n\t\thud = hud->next;\n\t}\n}\n\n//\n// Find hud placement by string\n// return 0 if error\n//\nint HUD_FindPlace(hud_t *hud)\n{\n    int i;\n    hud_t *par;\n    qbool out;\n    char *t;\n\n    // First try standard strings.\n    for (i=0; i < num_snap_strings; i++)\n\t{\n        if (!strcasecmp(hud->place->string, snap_strings[i]))\n\t\t{\n            break;\n\t\t}\n\t}\n\n    if (i < num_snap_strings)\n    {\n        // Found.\n        hud->place_num = i+1;\n        hud->place_hud = NULL;\n        return 1;\n    }\n\n    // then try another HUD element\n    out = true;\n    t = hud->place->string;\n    if (hud->place->string[0] == '@')\n    {\n        // place inside\n        out = false;\n        t++;\n    }\n\n    par = hud_huds;\n    while (par)\n    {\n        if (par != hud && !strcmp(t, par->name))\n        {\n            hud->place_outside = out;\n            hud->place_hud = par;\n            hud->place_num = HUD_PLACE_SCREEN;\n            return 1;\n        }\n        par = par->next;\n    }\n\n\t// No way.\n    hud->place_num = HUD_PLACE_SCREEN;\n    hud->place_hud = NULL;\n    return 0;\n}\n\n//\n// Find hud alignment by strings\n// return 0 if error\n//\nint HUD_FindAlignX(hud_t *hud)\n{\n    int i;\n\n    // First try standard strings.\n    for (i=0; i < num_align_strings_x; i++)\n\t{\n        if (!strcasecmp(hud->align_x->string, align_strings_x[i]))\n\t\t{\n            break;\n\t\t}\n\t}\n\n    if (i < num_align_strings_x)\n    {\n        // Found.\n        hud->align_x_num = i+1;\n        return 1;\n    }\n    else\n    {\n        // Error.\n\t\thud->align_x_num = HUD_ALIGN_LEFT; // left\n        return 0;\n    }\n}\n\n//\n// Find the alignment for a hud element.\n//\nint HUD_FindAlignY(hud_t *hud)\n{\n    int i;\n\n    // First try standard strings.\n    for (i=0; i < num_align_strings_y; i++)\n\t{\n        if (!strcasecmp(hud->align_y->string, align_strings_y[i]))\n\t\t{\n            break;\n\t\t}\n\t}\n\n    if (i < num_align_strings_y)\n    {\n        // Found.\n        hud->align_y_num = i + 1;\n        return 1;\n    }\n    else\n    {\n        // Error.\n        hud->align_y_num = HUD_ALIGN_TOP; // Left.\n        return 0;\n    }\n}\n\nint Hud_HudCompare (const void *p1, const void *p2)\n{\n\treturn strcmp((*((hud_t **) p1))->name, (*((hud_t **) p2))->name);\n}\n\n//\n// List hud elements\n//\nvoid HUD_List (void)\n{\n\tstatic hud_t *sorted_huds[256];\n\tint i, count;\n    hud_t *hud;\n\n#define MAX_SORTED_HUDS (sizeof (sorted_huds) / sizeof (sorted_huds[0]))\n\n\tfor (hud = hud_huds, count = 0; hud && count < MAX_SORTED_HUDS; hud = hud->next, count++)\n\t\tsorted_huds[count] = hud;\n\tqsort (sorted_huds, count, sizeof (hud_t *), Hud_HudCompare);\n\n\tif (count == MAX_SORTED_HUDS)\n\t\tassert(!\"count == MAX_SORTED_HUDS\");\n\n    Com_Printf(\"name            status\\n\");\n    Com_Printf(\"--------------- ------\\n\");\n\tfor (i = 0; i < count; i++) {\n\t\thud = sorted_huds[i];\n\n\t\tCom_Printf(\"%-15s %s\\n\", hud->name, hud->show->value ? \"shown\" : \"hidden\");\n\t}\n}\n\n//\n// Show the specified hud element.\n//\nvoid HUD_Show_f (void)\n{\n    hud_t *hud;\n\n    if (Cmd_Argc() != 2)\n    {\n        Com_Printf(\"Usage: show [<name> | all]\\n\");\n        Com_Printf(\"Show given HUD element.\\n\");\n        Com_Printf(\"use \\\"show all\\\" to show all elements.\\n\");\n        Com_Printf(\"Current elements status:\\n\\n\");\n        HUD_List();\n        return;\n    }\n\n    if (!strcasecmp(Cmd_Argv(1), \"all\"))\n    {\n        hud = hud_huds;\n        while (hud)\n        {\n            Cvar_SetValue(hud->show, 1);\n            hud = hud->next;\n        }\n    }\n    else\n    {\n        hud = HUD_Find(Cmd_Argv(1));\n\n        if (!hud)\n        {\n            Com_Printf(\"No such element: %s\\n\", Cmd_Argv(1));\n            return;\n        }\n\n        Cvar_SetValue(hud->show, 1);\n    }\n}\n\n\n//\n// Hide the specified hud element.\n//\nvoid HUD_Hide_f (void)\n{\n    hud_t *hud;\n\n    if (Cmd_Argc() != 2)\n    {\n        Com_Printf(\"Usage: hide [<name> | all]\\n\");\n        Com_Printf(\"Hide given HUD element\\n\");\n        Com_Printf(\"use \\\"hide all\\\" to hide all elements.\\n\");\n        Com_Printf(\"Current elements status:\\n\\n\");\n        HUD_List();\n        return;\n    }\n\n    if (!strcasecmp(Cmd_Argv(1), \"all\"))\n    {\n        hud = hud_huds;\n        while (hud)\n        {\n            Cvar_SetValue(hud->show, 0);\n            hud = hud->next;\n        }\n    }\n    else\n    {\n        hud = HUD_Find(Cmd_Argv(1));\n\n        if (!hud)\n        {\n            Com_Printf(\"No such element: %s\\n\", Cmd_Argv(1));\n            return;\n        }\n\n        Cvar_SetValue(hud->show, 0);\n    }\n}\n\n//\n// Toggles specified hud element.\n//\nvoid HUD_Toggle_f (void)\n{\n    hud_t *hud;\n\n    if (Cmd_Argc() != 2)\n    {\n        Com_Printf(\"Usage: togglehud <name> | <variable>\\n\");\n        Com_Printf(\"Show/hide given HUD element, or toggles variable value.\\n\");\n        return;\n    }\n\n    hud = HUD_Find(Cmd_Argv(1));\n\n    if (!hud)\n    {\n        // look for cvar\n        cvar_t *var = Cvar_Find(Cmd_Argv(1));\n        if (!var)\n        {\n            Com_Printf(\"No such element or variable: %s\\n\", Cmd_Argv(1));\n            return;\n        }\n\n\t\tCvar_Set (var, var->value ? \"0\" : \"1\");\n        return;\n    }\n\n    Cvar_Set (hud->show, hud->show->value ? \"0\" : \"1\");\n}\n\n//\n// Move the specified hud element relative to placement/alignment.\n//\nvoid HUD_Move_f (void)\n{\n    hud_t *hud;\n\n    if (Cmd_Argc() != 4 &&  Cmd_Argc() != 2)\n    {\n        Com_Printf(\"Usage: move <name> [<x> <y>]\\n\");\n        Com_Printf(\"Set offset for given HUD element\\n\");\n        return;\n    }\n\n    hud = HUD_Find(Cmd_Argv(1));\n\n    if (!hud)\n    {\n        Com_Printf(\"No such element: %s\\n\", Cmd_Argv(1));\n        return;\n    }\n\n    if (Cmd_Argc() == 2)\n    {\n        Com_Printf(\"Current %s offset is:\\n\", Cmd_Argv(1));\n        Com_Printf(\"  x:  %s\\n\", hud->pos_x->string);\n        Com_Printf(\"  y:  %s\\n\", hud->pos_y->string);\n        return;\n    }\n\n    Cvar_SetValue(hud->pos_x, atof(Cmd_Argv(2)));\n    Cvar_SetValue(hud->pos_y, atof(Cmd_Argv(3)));\n}\n\n//\n// Resets a hud item to the center of the screen.\n//\nvoid HUD_Reset_f (void)\n{\n\thud_t *hud = NULL;\n\tchar *hudname = NULL;\n\n    if (Cmd_Argc() != 2)\n    {\n        Com_Printf(\"Usage: reset <name>\\n\");\n        Com_Printf(\"Resets the position of the given HUD element to the center of the screen.\\n\");\n        return;\n    }\n\n\thudname = Cmd_Argv(1);\n\n    hud = HUD_Find(hudname);\n\n\tif (!hud)\n\t{\n\t\tCom_Printf(\"No such HUD element %s.\\n\", hudname);\n\t\treturn;\n\t}\n\t\n\tCbuf_AddText(va(\"place %s screen\\n\", hudname));\n\tCbuf_AddText(va(\"move %s 0 0\\n\", hudname));\n\tCbuf_AddText(va(\"align %s center center\\n\", hudname));\n}\n\n//\n// Reorders children so that they are place infront of their parent.\n//\nvoid HUD_ReorderChildren(void)\n{\n\thud_t *hud = hud_huds;\n\n\t// Give all children a higher Z-order.\n\twhile(hud)\n\t{\n\t\tif(hud->place_hud && hud->order->value <= hud->place_hud->order->value)\n\t\t{\n\t\t\tCvar_SetValue(hud->order, hud->place_hud->order->value + 1);\n\t\t}\n\n\t\thud = hud->next;\n\t}\n}\n\n//\n// Place the specified hud element.\n//\nvoid HUD_Place_f (void)\n{\n    hud_t *hud;\n    char temp[512];\n\n    if (Cmd_Argc() < 2 || Cmd_Argc() > 3)\n    {\n        Com_Printf(\"Usage: move <name> [<area>]\\n\");\n        Com_Printf(\"Place HUD element at given area.\\n\");\n        Com_Printf(\"\\nPossible areas are:\\n\");\n        Com_Printf(\"  screen - screen area\\n\");\n        Com_Printf(\"  top    - screen minus status bar\\n\");\n        Com_Printf(\"  view   - view\\n\");\n        Com_Printf(\"  sbar   - status bar\\n\");\n        Com_Printf(\"  ibar   - inventory bar\\n\");\n        Com_Printf(\"  hbar   - health bar\\n\");\n        Com_Printf(\"  sfree  - status bar free area\\n\");\n        Com_Printf(\"  ifree  - inventory bar free area\\n\");\n        Com_Printf(\"  hfree  - health bar free area\\n\");\n        Com_Printf(\"You can also use any other HUD element as a base alignment. In such case you should specify area as:\\n\");\n        Com_Printf(\"  @elem  - if you want to place\\n\");\n        Com_Printf(\"           it inside elem\\n\");\n        Com_Printf(\"   elem  - if you want to place\\n\");\n        Com_Printf(\"           it outside elem\\n\");\n        Com_Printf(\"Examples:\\n\");\n        Com_Printf(\"  place fps view\\n\");\n        Com_Printf(\"  place fps @ping\\n\");\n        return;\n    }\n\n    hud = HUD_Find(Cmd_Argv(1));\n\n    if (!hud)\n    {\n        Com_Printf(\"No such element: %s\\n\", Cmd_Argv(1));\n        return;\n    }\n\n    if (Cmd_Argc() == 2)\n    {\n        Com_Printf(\"Current %s placement: %s\\n\", hud->name, hud->place->string);\n        return;\n    }\n\n    // Place with helper.\n    strlcpy(temp, hud->place->string, sizeof(temp));\n    Cvar_Set(hud->place, Cmd_Argv(2));\n    if (!HUD_FindPlace(hud))\n    {\n        Com_Printf(\"place: invalid area argument: %s\\n\", Cmd_Argv(2));\n        Cvar_Set(hud->place, temp); // Restore old value.\n    }\n\telse\n\t{\n\t\tHUD_ReorderChildren();\n\t}\n}\n\n//\n// Sets the z-order of a HUD element.\n//\nvoid HUD_Order_f (void)\n{\n\tint max = 0;\n\tint min = 0;\n\tchar *option = NULL;\n\thud_t *hud = NULL;\n\n\tif (Cmd_Argc() < 2 || Cmd_Argc() > 3)\n\t{\n\t\tCom_Printf(\"Usage: order <name> [<option>]\\n\");\n\t\tCom_Printf(\"Set HUD element draw order\\n\");\n\t\tCom_Printf(\"\\nPossible values for option:\\n\");\n\t\tCom_Printf(\"  #         - An integer representing the order.\\n\");\n\t\tCom_Printf(\"  backward  - Send the element backwards in the order.\\n\");\n\t\tCom_Printf(\"  forward   - Send the element forward in the order.\\n\");\n\t\tCom_Printf(\"  front     - Bring the element to the front.\\n\");\n\t\tCom_Printf(\"  back      - Put the element at the far back.\\n\");\n\t\treturn;\n\t}\n\n\thud = HUD_Find (Cmd_Argv(1));\n\n\tif (!hud)\n    {\n        Com_Printf(\"No such element: %s\\n\", Cmd_Argv(1));\n        return;\n    }\n\n\tif (Cmd_Argc() == 2)\n    {\n        Com_Printf(\"Current order for %s is:\\n\", Cmd_Argv(1));\n\t\tCom_Printf(\"  order:  %d\\n\", (int)hud->order->value);\n        return;\n    }\n\n\toption = Cmd_Argv(2);\n\n\tHUD_FindMaxMinOrder (&max, &min);\n\n\tif (!strncasecmp (option, \"backward\", 8))\n\t{\n\t\t// Send backward one step.\n\t\tCvar_SetValue(hud->order, (int)hud->order->value - 1);\n\t}\n\telse if (!strncasecmp (option, \"forward\", 7))\n\t{\n\t\t// Move forward one step.\n\t\tCvar_SetValue(hud->order, (int)hud->order->value + 1);\n\t}\n\telse if (!strncasecmp (option, \"front\", 5))\n\t{\n\t\t// Bring to front.\n\t\tCvar_SetValue(hud->order, max + 1);\n\t}\n\telse if (!strncasecmp (option, \"back\", 8))\n\t{\n\t\t// Send to far back.\n\t\tCvar_SetValue(hud->order, min - 1);\n\t}\n\telse\n\t{\n\t\t// Order #\n\t\tCvar_SetValue (hud->order, atoi(Cmd_Argv(2)));\n\t}\n}\n\n//\n// Align the specified hud element\n//\nvoid HUD_Align_f (void)\n{\n    hud_t *hud;\n\n    if (Cmd_Argc() != 4  &&  Cmd_Argc() != 2)\n    {\n        Com_Printf(\"Usage: align <name> [<ax> <ay>]\\n\");\n        Com_Printf(\"Set HUD element alignment\\n\");\n        Com_Printf(\"\\nPossible values for ax are:\\n\");\n        Com_Printf(\"  left    - left area edge\\n\");\n        Com_Printf(\"  center  - area center\\n\");\n        Com_Printf(\"  right   - right area edge\\n\");\n        Com_Printf(\"  before  - before area (left)\\n\");\n        Com_Printf(\"  after   - after area (right)\\n\");\n        Com_Printf(\"\\nPossible values for ay are:\\n\");\n        Com_Printf(\"  top     - screen top\\n\");\n        Com_Printf(\"  center  - screen center\\n\");\n        Com_Printf(\"  bottom  - screen bottom\\n\");\n        Com_Printf(\"  before  - before area (top)\\n\");\n        Com_Printf(\"  after   - after area (bottom)\\n\");\n        Com_Printf(\"  console - below console\\n\");\n        return;\n    }\n\n    hud = HUD_Find(Cmd_Argv(1));\n\n    if (!hud)\n    {\n        Com_Printf(\"No such element: %s\\n\", Cmd_Argv(1));\n        return;\n    }\n\n    if (Cmd_Argc() == 2)\n    {\n        Com_Printf(\"Current alignment for %s is:\\n\", Cmd_Argv(1));\n        Com_Printf(\"  horizontal (x):  %s\\n\", hud->align_x->string);\n        Com_Printf(\"  vertical (y):    %s\\n\", hud->align_y->string);\n        return;\n    }\n\n    // validate and set\n    Cvar_Set(hud->align_x, Cmd_Argv(2));\n    if (!HUD_FindAlignX(hud))\n        Com_Printf(\"align: invalid X alignment: %s\\n\", Cmd_Argv(2));\n\n    Cvar_Set(hud->align_y, Cmd_Argv(3));\n    if (!HUD_FindAlignY(hud))\n        Com_Printf(\"align: invalid Y alignment: %s\\n\", Cmd_Argv(3));\n}\n\n//\n// Recalculate all elements\n// should be called if some HUD parameters (like place)\n// were changed directly by vars, not comands (like place)\n// - after execing cfg or sth\n//\nvoid HUD_Recalculate(void)\n{\n    hud_t *hud = hud_huds;\n\n    while (hud)\n    {\n        HUD_FindPlace(hud);\n        HUD_FindAlignX(hud);\n        HUD_FindAlignY(hud);\n\n        hud = hud->next;\n    }\n}\nvoid HUD_Recalculate_f(void)\n{\n    HUD_Recalculate();\n}\n\nvoid HUD_Export_f(void)\n{\n\tchar line[8192];\n\tqhandle_t handle;\n\thud_t *hud;\n\tcvar_t *var;\n\tint i;\n\n\tchar fname[64];\n\tchar fdesc[256];\n\n\tcmdfuncs->Argv(1, fname, sizeof(fname));\n\tcmdfuncs->Argv(2, fdesc, sizeof(fdesc));\n\n\tif (!*fdesc)\n\t\tsnprintf(fdesc, sizeof(fdesc), \"%s\", fname);\n\n\tsnprintf(line, sizeof(line), \"configs/hud_%s.cfg\", fname);\n\n\tif (filefuncs->Open(line, &handle, 2) < 0)\n\t\tCom_Printf(\"Couldn't open %s\\n\", line);\n\telse\n\t{\n\t\t//FIXME: should print the result of an flocate, but plugins are not really aware of that stuff.\n\t\tCom_Printf(\"Writing %s\\n\", line);\n\t\tsnprintf(line, sizeof(line), \"//desc:%s\\n\\n//hud cvar settings, for use with FTEQW's ezhud plugin.\\n\", fdesc);\n\t\tfilefuncs->Write(handle, line, strlen(line));\n\n\t\tfor (hud = hud_huds; hud; hud = hud->next)\n\t\t{\n\t\t\tfor (i = 0; i < hud->num_params; i++)\n\t\t\t{\n\t\t\t\tvar = hud->params[i];\n\t\t\t\t//fixme: deal with \" and \\n\n\t\t\t\tsnprintf(line, sizeof(line), \"set %s \\\"%s\\\"\\n\", var->name, var->string);\n\t\t\t\tfilefuncs->Write(handle, line, strlen(line));\n\t\t\t}\n\t\t}\n\n\t\tfilefuncs->Close(handle);\n\t}\n}\n\n//\n// Initialize HUD.\n//\nvoid HUD_Init(void)\n{\n\t// from hud.c, doesn't suit anywhere\n\tvoid HUD_Inputlag_hit_f(void);\n\n\t// Commands.\n\tCmd_AddCommand (\"show\", HUD_Show_f);\n\tCmd_AddCommand (\"hide\", HUD_Hide_f);\n\tCmd_AddCommand (\"move\", HUD_Move_f);\n\tCmd_AddCommand (\"place\", HUD_Place_f);\n\tCmd_AddCommand (\"reset\", HUD_Reset_f);\n\tCmd_AddCommand (\"order\", HUD_Order_f);\n\tCmd_AddCommand (\"togglehud\", HUD_Toggle_f);\n\tCmd_AddCommand (\"align\", HUD_Align_f);\n\tCmd_AddCommand (\"hud_recalculate\", HUD_Recalculate_f);\n\tCmd_AddCommand (\"hud_export\", HUD_Export_f);\n\n\t// Register the hud items.\n\tCommonDraw_Init();\n\n\t// Sort the elements.\n\tHUD_Sort();\n}\n\n//\n// Calculate frame extents.\n//\nvoid HUD_CalcFrameExtents(hud_t *hud, int width, int height,\t\t\t\t\t\t\t\t\t// In.\n                          int *frame_left, int *frame_right, int *frame_top, int *frame_bottom) // Out.\n{\n    if ((hud->flags & HUD_NO_GROW) && hud->frame->value != 2)\n    {\n        *frame_left = *frame_right = *frame_top = *frame_bottom = 0;\n        return;\n    }\n\n    if (hud->frame->value == 2) // Treat text box separately.\n    {\n        int ax\t\t\t= (width % 16);\n        int ay\t\t\t= (height % 8);\n        *frame_left\t\t= 8 + ax / 2;\n        *frame_top\t\t= 8 + ay / 2;\n        *frame_right\t= 8 + ax - ax / 2;\n        *frame_bottom\t= 8 + ay - ay / 2;\n    }\n    else if (hud->frame->value > 0  &&  hud->frame->value <= 1)\n    {\n        int frame_x, frame_y;\n        frame_x = 2;\n        frame_y = 2;\n\n        if (width > 8)\n\t\t{\n            frame_x <<= 1;\n\t\t}\n\n        if (height > 8)\n\t\t{\n            frame_y <<= 1;\n\t\t}\n\n        *frame_left\t\t= frame_x;\n        *frame_right\t= frame_x;\n        *frame_top\t\t= frame_y;\n        *frame_bottom\t= frame_y;\n    }\n    else\n    {\n        // No frame at all.\n        *frame_left = *frame_right = *frame_top = *frame_bottom = 0;\n    }\n}\n\nvoid HUD_OnChangeFrameColor(cvar_t *var, char *oldval)\n{\n\t// Converts \"red\" into \"255 0 0\", etc. or returns input as it was.\n\tconst char *new_color = ColorNameToRGBString (var->string);\n\tchar buf[256], buf2[128];\n\tsize_t hudname_len;\n\thud_t* hud_elem;\n\tbyte* b_colors;\n\n\thudname_len = min (sizeof (buf), strlen (var->name) - strlen (\"_frame_color\") - strlen (\"hud_\") + 1);\n\tstrlcpy (buf, var->name + 4, hudname_len);\n\thud_elem = HUD_Find (buf);\n\n\tstrlcpy(buf2,new_color,sizeof(buf2));\n\tb_colors = StringToRGB (buf2);\n\n\tmemcpy (hud_elem->frame_color_cache, b_colors, sizeof (byte) * 3);\n}\n\n//\n// Draw frame for HUD element.\n//\nvoid HUD_DrawFrame(hud_t *hud, int x, int y, int width, int height)\n{\n    if (!hud->frame->value)\n        return;\n\n    if (hud->frame->value > 0  &&  hud->frame->value <= 1)\n    {\n\t\thud->frame_color_cache[3] = (byte)(255 * hud->frame->value);\n\n\t\tDraw_AlphaFillRGB(x, y, width, height, hud->frame_color_cache[0], hud->frame_color_cache[1], hud->frame_color_cache[2], hud->frame_color_cache[3]);\n        return;\n    }\n    else\n    {\n        switch ((int)(hud->frame->value))\n        {\n        case 2:     // Text box.\n            Draw_TextBox(x, y, width/8-2, height/8-2);\n            break;\n        default:    // More will probably come.\n            break;\n        }\n    }\n}\n\n//\n// Calculate element placement.\n//\nqbool HUD_PrepareDrawByName(char *name, int width, int height,\t// In.\n\t\t\t\t\t\t\tint *ret_x, int *ret_y)\t\t\t\t// Out (Position).\n{\n    hud_t *hud = HUD_Find(name);\n    if (hud == NULL)\n\t{\n        return false; // error in C code\n\t}\n\n    return HUD_PrepareDraw(hud, width, height, ret_x, ret_y);\n}\n\n//\n// Calculate object extents and draws frame if needed.\n//\nqbool HUD_PrepareDraw(hud_t *hud, int width, int height, // In.\n\t\t\t\t\t  int *ret_x, int *ret_y)\t\t\t // Out (Position).\n{\n    extern vrect_t scr_vrect;\n    int x, y;\n    int frame_left, frame_right, frame_top, frame_bottom;\t// Frame left, right, top and bottom.\n    int area_x, area_y, area_width, area_height;\t\t\t// Area coordinates & sizes to align.\n    int bounds_x, bounds_y, bounds_width, bounds_height;\t// Bounds to draw within.\n\n\t// Don't show the hud element.\n\tif (cls.state < hud->min_state || !hud->show->value)\n\t{\n        return false;\n\t}\n\n    HUD_CalcFrameExtents(hud, width, height, &frame_left, &frame_right, &frame_top, &frame_bottom);\n\n    width  += frame_left + frame_right;\n    height += frame_top + frame_bottom;\n\n    //\n    // Placement.\n\t//\n    switch (hud->place_num)\n    {\n\t\tdefault:\n\t\tcase HUD_PLACE_SCREEN:\n\t\t\tbounds_x = bounds_y = 0;\n\t\t\tbounds_width = vid.width;\n\t\t\tbounds_height = vid.height;\n\t\t\tbreak;\n\t\tcase HUD_PLACE_TOP: // Top = screen - sbar\n\t\t\tbounds_x = bounds_y = 0;\n\t\t\tbounds_width = vid.width;\n\t\t\tbounds_height = vid.height - sb_lines;\n\t\t\tbreak;\n\t\tcase HUD_PLACE_VIEW:\n\t\t\tbounds_x = scr_vrect.x;\n\t\t\tbounds_y = scr_vrect.y;\n\t\t\tbounds_width = scr_vrect.width;\n\t\t\tbounds_height = scr_vrect.height;\n\t\t\tbreak;\n\t\tcase HUD_PLACE_SBAR:\n\t\t\tbounds_x = 0;\n\t\t\tbounds_y = vid.height - sb_lines;\n\t\t\tbounds_width = sbar_last_width;\n\t\t\tbounds_height = sb_lines;\n\t\t\tbreak;\n\t\tcase HUD_PLACE_IBAR:\n\t\t\tbounds_width = sbar_last_width;\n\t\t\tbounds_height = max(sb_lines - SBAR_HEIGHT, 0);\n\t\t\tbounds_x = 0;\n\t\t\tbounds_y = vid.height - sb_lines;\n\t\t\tbreak;\n\t\tcase HUD_PLACE_HBAR:\n\t\t\tbounds_width = sbar_last_width;\n\t\t\tbounds_height = min(SBAR_HEIGHT, sb_lines);\n\t\t\tbounds_x = 0;\n\t\t\tbounds_y = vid.height - bounds_height;\n\t\t\tbreak;\n\t\tcase HUD_PLACE_SFREE:\n\t\t\tbounds_x = sbar_last_width;\n\t\t\tbounds_y = vid.height - sb_lines;\n\t\t\tbounds_width = vid.width - sbar_last_width;\n\t\t\tbounds_height = sb_lines;\n\t\t\tbreak;\n\t\tcase HUD_PLACE_IFREE:\n\t\t\tbounds_width = vid.width - sbar_last_width;\n\t\t\tbounds_height = max(sb_lines - SBAR_HEIGHT, 0);\n\t\t\tbounds_x = sbar_last_width;\n\t\t\tbounds_y = vid.height - sb_lines;\n\t\t\tbreak;\n\t\tcase HUD_PLACE_HFREE:\n\t\t\tbounds_width = vid.width - sbar_last_width;\n\t\t\tbounds_height = min(SBAR_HEIGHT, sb_lines);\n\t\t\tbounds_x = sbar_last_width;\n\t\t\tbounds_y = vid.height - bounds_height;\n\t\t\tbreak;\n    }\n\n    if (hud->place_hud == NULL)\n    {\n        // Accepted boundaries are our area.\n        area_x\t\t= bounds_x;\n        area_y\t\t= bounds_y;\n        area_width\t= bounds_width;\n        area_height\t= bounds_height;\n    }\n    else\n    {\n        // Out area is our parent area.\n        area_x\t\t= hud->place_hud->lx;\n        area_y\t\t= hud->place_hud->ly;\n        area_width\t= hud->place_hud->lw;\n        area_height = hud->place_hud->lh;\n\n        if (hud->place_outside)\n        {\n            area_x\t\t-= hud->place_hud->al;\n            area_y\t\t-= hud->place_hud->at;\n            area_width\t+= hud->place_hud->al + hud->place_hud->ar;\n            area_height += hud->place_hud->at + hud->place_hud->ab;\n        }\n    }\n\n    //\n    // Horizontal pos.\n    //\n    switch (hud->align_x_num)\n    {\n\t\tdefault:\n\t\tcase HUD_ALIGN_LEFT:\n\t\t\tx = area_x;\n\t\t\tbreak;\n\t\tcase HUD_ALIGN_CENTER:\n\t\t\tx = area_x + (area_width - width) / 2;\n\t\t\tbreak;\n\t\tcase HUD_ALIGN_RIGHT:\n\t\t\tx = area_x + area_width - width;\n\t\t\tbreak;\n\t\tcase HUD_ALIGN_BEFORE:\n\t\t\tx = area_x - width;\n\t\t\tbreak;\n\t\tcase HUD_ALIGN_AFTER:\n\t\t\tx = area_x + area_width;\n\t\t\tbreak;\n    }\n\n    x += hud->pos_x->value;\n\n    //\n    // Vertical pos.\n    //\n    switch (hud->align_y_num)\n    {\n\t\tdefault:\n\t\tcase HUD_ALIGN_TOP:\n\t\t\ty = area_y;\n\t\t\tbreak;\n\t\tcase HUD_ALIGN_CENTER:\n\t\t\ty = area_y + (area_height - height) / 2;\n\t\t\tbreak;\n\t\tcase HUD_ALIGN_BOTTOM:\n\t\t\ty = area_y + area_height - height;\n\t\t\tbreak;\n\t\tcase HUD_ALIGN_BEFORE:\n\t\t\ty = area_y - height;\n\t\t\tbreak;\n\t\tcase HUD_ALIGN_AFTER:\n\t\t\ty = area_y + area_height;\n\t\t\tbreak;\n\t\tcase HUD_ALIGN_CONSOLE:\n\t\t\ty = max(area_y, scr_con_current);\n\t\t\tbreak;\n    }\n\n    y += hud->pos_y->value;\n\n\tif (ret_x)\n\t{\n\t\t// Draw frame.\n\t\tHUD_DrawFrame(hud, x, y, width, height);\n\n\t\t// Assign values.\n\t\t*ret_x = x + frame_left;\n\t\t*ret_y = y + frame_top;\n\t}\n\n    // Remember values for children.\n    hud->lx = x + frame_left;\n    hud->ly = y + frame_top;\n    hud->lw = width - frame_left - frame_right;\n    hud->lh = height - frame_top - frame_bottom;\n    hud->al = frame_left;\n    hud->ar = frame_right;\n    hud->at = frame_top;\n    hud->ab = frame_bottom;\n\n\t// Check if we're supposed to draw the entire item or just the outline/frame.\n\t// (If we're in hud editor align/place mode)\n\tif(!HUD_Editor_ConfirmDraw(hud))\n\t{\n\t\treturn false;\n\t}\n\n    // Remember drawing sequence.\n    hud->last_draw_sequence = host_screenupdatecount;\n    return true;\n}\n\n//\n// Creates a HUD variable based on a name.\n//\ncvar_t * HUD_CreateVar(char *hud_name, char *subvar, char *value)\n{\n    char buf[128];\n\n    snprintf (buf, sizeof (buf), \"hud_%s_%s\", hud_name, subvar);\n\n\treturn cvarfuncs->GetNVFDG(buf, value, 0, NULL, \"ezhud\");\n}\n\n//\n// Onchange for when z-order changes for a hud element. Resorts the elements.\n//\nvoid HUD_OnChangeOrder(cvar_t *var, char *val)\n{\n\tdoreorder = true;\n}\n\n//\n// Registers a new HUD element to the list of HUD elements.\n//\nhud_t * HUD_Register(char *name, char *var_alias, char *description,\n                     int flags, cactive_t min_state, int draw_order,\n                     hud_func_type draw_func,\n                     char *show, char *place, char *align_x, char *align_y,\n                     char *pos_x, char *pos_y, char *frame, char *frame_color,\n\t\t\t\t\t char *item_opacity,\n                     char *params, ...)\n{\n    int i;\n    va_list     argptr;\n    hud_t\t\t*hud;\n    char\t\t*subvar;\n\n\t// We want to include Frame, frame_color, item_opacity in the list of\n\t// available cvar's for the user also. If any additional cvars that\n\t// common for all hud elements are added this needs to be increased.\n\tint\t\t\tnum_params = 3;\n\n\t// Allocate room for the HUD.\n    hud = (hud_t *) Q_malloc(sizeof(hud_t));\n    memset(hud, 0, sizeof(hud_t));\n    hud->next = hud_huds;\n    hud_huds = hud;\n    hud->min_state = min_state;\n    hud->draw_func = draw_func;\n\n\t// Name.\n    hud->name = (char *) Q_malloc(strlen(name)+1);\n    strcpy(hud->name, name);\n\n\t// Description.\n    hud->description = (char *) Q_malloc(strlen(description)+1);\n    strcpy(hud->description, description);\n\n\t// Count the number of params.\n\tsubvar = params;\n    va_start (argptr, params);\n\twhile(subvar)\n\t{\n\t\tnum_params++;\n\t\tsubvar = va_arg(argptr, char *);\n\t}\n\tva_end (argptr);\n\n\t// Allocate the params array.\n\thud->params = Q_calloc(num_params, sizeof(cvar_t *));\n\n\t// Set flags.\n    hud->flags = flags;\n\n    Cmd_AddCommand(name, HUD_Func_f);\n\n\t//\n    // Create standard variables.\n\t//\n\n\t//\n\t// Ordering\n\t//\n\t{\n\t\tchar order[18];\n\t\tsnprintf (order, sizeof(order), \"%d\", draw_order);\n\t\thud->order = HUD_CreateVar(name, \"order\", order);\n\t\thud->order->callback = HUD_OnChangeOrder;\n\t}\n\n\t//\n    // Place.\n\t//\n    hud->place = HUD_CreateVar(name, \"place\", place);\n    i = HUD_FindPlace(hud);\n    if (i == 0)\n    {\n        // Probably parent should be registered earlier.\n\t\t// (This doesn't matter, since we'll re-place all elements after\n\t\t// all the elements have been registered)\n        hud->place_num = 0;\n        hud->place_hud = NULL;\n    }\n\n\t//\n    // Show.\n\t//\n    if (show)\n    {\n        hud->show = HUD_CreateVar(name, \"show\", show);\n\n        if (flags & HUD_PLUSMINUS)\n        {\n            // Add plus and minus commands.\n            Cmd_AddCommand(Q_strdup(va(\"+hud_%s\", name)), HUD_Plus_f);\n            Cmd_AddCommand(Q_strdup(va(\"-hud_%s\", name)), HUD_Minus_f);\n        }\n    }\n    else\n\t{\n        hud->flags |= HUD_NO_SHOW;\n\t}\n\n\t//\n    // X align & pos.\n\t//\n    if (pos_x  &&  align_x)\n    {\n        hud->pos_x = HUD_CreateVar(name, \"pos_x\", pos_x);\n        hud->align_x = HUD_CreateVar(name, \"align_x\", align_x);\n    }\n    else\n\t{\n        hud->flags |= HUD_NO_POS_X;\n\t}\n\n\t//\n    // Y align & pos.\n\t//\n    if (pos_y  &&  align_y)\n    {\n        hud->pos_y = HUD_CreateVar(name, \"pos_y\", pos_y);\n        hud->align_y = HUD_CreateVar(name, \"align_y\", align_y);\n    }\n    else\n\t{\n        hud->flags |= HUD_NO_POS_Y;\n\t}\n\n\t//\n    // Frame.\n\t//\n    if (frame)\n    {\n        hud->frame = HUD_CreateVar(name, \"frame\", frame);\n        hud->params[hud->num_params++] = hud->frame;\n\n\t\thud->frame_color = HUD_CreateVar(name, \"frame_color\", frame_color);\n\t\thud->frame_color->callback = HUD_OnChangeFrameColor;\n        hud->params[hud->num_params++] = hud->frame_color;\n    }\n    else\n\t{\n        hud->flags |= HUD_NO_FRAME;\n\t}\n\n\t//\n\t// Item Opacity.\n\t//\n\t{\n\t\thud->opacity = HUD_CreateVar(name, \"item_opacity\", (item_opacity) ? item_opacity : \"0.99\");\n\t\thud->flags |= HUD_OPACITY;\n\t\thud->params[hud->num_params++] = hud->opacity;\n\t}\n\n\t//\n    // Create parameters.\n\t//\n    subvar = params;\n    va_start (argptr, params);\n\n    while (subvar)\n    {\n        char *value = va_arg(argptr, char *);\n        if (value == NULL || hud->num_params >= HUD_MAX_PARAMS || hud->num_params >= num_params)\n\t\t{\n            Sys_Error(\"HUD_Register: HUD_MAX_PARAMS overflow\");\n\t\t}\n\n        hud->params[hud->num_params] = HUD_CreateVar(name, subvar, value);\n        hud->num_params ++;\n        subvar = va_arg(argptr, char *);\n    }\n\n    va_end (argptr);\n\n    return hud;\n}\n\nvoid HUD_ParamsCleanup(void)\n{\n//\tint i = 0;\n\thud_t *hud = hud_huds;\n\n    while (hud)\n    {\n/*\t\tfor (i=0; i < hud->num_params; i++)\n\t\t{\n\t\t\tCvar_Delete(hud->params[i]->name);\n\t\t}\n*/\n\t\tQ_free(hud->params);\n\n        hud = hud->next;\n    }\n}\n\n//\n// Find element in list.\n//\nhud_t * HUD_Find(char *name)\n{\n    hud_t *hud = hud_huds;\n\n    while (hud)\n    {\n        if (!strcasecmp(hud->name, name))\n\t\t{\n            return hud;\n\t\t}\n\n        hud = hud->next;\n    }\n    return NULL;\n}\n\n//\n// Retrieve hud cvar.\n//\ncvar_t *HUD_FindVar(hud_t *hud, char *subvar)\n{\n    int i;\n    char buf[128];\n\n    snprintf(buf, sizeof(buf), \"hud_%s_%s\", hud->name, subvar);\n\n    for (i=0; i < hud->num_params; i++)\n\t{\n        if (!strcmp(buf, hud->params[i]->name))\n\t\t{\n            return hud->params[i];\n\t\t}\n\t}\n\n    return NULL;\n}\n\n//\n// Draws single HUD element.\n//\nvoid HUD_DrawObject(hud_t *hud)\n{\n    extern qbool sb_showscores, sb_showteamscores;\n\n\t// Already tried to draw this frame.\n    if (hud->last_try_sequence == host_screenupdatecount)\n\t{\n        return;\n\t}\n\n    hud->last_try_sequence = host_screenupdatecount;\n\n    // Check if we should draw this.\n    if (!hud->show->value)\n\t{\n        return;\n\t}\n\n    if (cls.state < hud->min_state)\n\t{\n        return;\n\t}\n\n    if (cl.intermission == 1  &&  !(hud->flags & HUD_ON_INTERMISSION))\n\t{\n\t\treturn;\n\t}\n\n    if (cl.intermission == 2 && !(hud->flags & HUD_ON_FINALE))\n\t{\n        return;\n\t}\n\n    if ((sb_showscores || sb_showteamscores) && !(hud->flags & HUD_ON_SCORES))\n\t{\n        return;\n\t}\n\n    if (hud->place_hud)\n    {\n        // Parent should be drawn it should be first.\n        HUD_DrawObject(hud->place_hud);\n\n        // If parent was not drawn, we refuse to draw too\n        if (hud->place_hud->last_draw_sequence < host_screenupdatecount)\n\t\t{\n            return;\n\t\t}\n    }\n\n\t//\n\t// Let the HUD element draw itself - updates last_draw_sequence itself.\n\t//\n\tDraw_SetOverallAlpha(hud->opacity->value);\n\thud->draw_func(hud);\n\tDraw_SetOverallAlpha(1.0);\n\n\t// last_draw_sequence is update by HUD_PrepareDraw\n    // if object was succesfully drawn (wasn't outside area etc..)\n}\n\n//\n// Draw all active elements.\n//\nvoid HUD_Draw(void)\n{\n    hud_t *hud;\n\n\tif (doreorder)\n\t{\n\t\tdoreorder = false;\n\t\tHUD_ReorderChildren();\n\t\tHUD_Sort();\n\t}\n\n\t// Only draw the hud once in multiview.\n/*\tif (cl_multiview.integer && cls.mvdplayback)\n\t{\n\t\tif (CURRVIEW != 1)\n\t\t{\n\t\t\treturn;\n\t\t}\n\t}\n*/\n\tif (mvd_autohud->ival && !autohud_loaded)\n\t{\n\t\tHUD_AutoLoad_MVD(mvd_autohud->ival);\n\t\tCom_DPrintf(\"Loading AUTOHUD...\\n\");\n\t\tautohud_loaded = true;\n\t}\n\n/*    if (scr_newHud->value == 0)\n\t{\n\t\treturn;\n\t}\n*/\n    hud = hud_huds;\n\n\tHUD_BeforeDraw();\n\n    while (hud)\n    {\n        // Draw.\n        HUD_DrawObject(hud);\n\n        // Go to next.\n        hud = hud->next;\n    }\n\n\tHUD_AfterDraw();\n}\n\n//\n// Compares two hud elements.\n//\nint HUD_OrderFunc(const void * p_h1, const void * p_h2)\n{\n    const hud_t *h1 = *((hud_t **)p_h1);\n    const hud_t *h2 = *((hud_t **)p_h2);\n\n\treturn (int)h1->order->value - (int)h2->order->value;\n}\n\n//\n// Last phase of initialization.\n//\nvoid HUD_Sort(void)\n{\n    // Sort elements based on their draw order.\n    int i;\n    hud_t *huds[MAX_HUD_ELEMENTS];\n    int count = 0;\n    hud_t *hud;\n\n    // Copy to table.\n    hud = hud_huds;\n    while (hud)\n    {\n        huds[count++] = hud;\n        hud = hud->next;\n    }\n\n    if (count <= 0)\n        return;\n\n    // Sort table.\n    qsort(huds, count, sizeof(huds[0]), HUD_OrderFunc);\n\n    // Back to list.\n    hud_huds = huds[0];\n    hud = hud_huds;\n    hud->next = NULL;\n    for (i=1; i < count; i++)\n    {\n        hud->next = huds[i];\n        hud = hud->next;\n        hud->next = NULL;\n    }\n\n    // Recalculate elements so vars are parsed.\n    HUD_Recalculate();\n}\n"
  },
  {
    "path": "plugins/ezhud/hud.h",
    "content": "/*\nCopyright (C) 2011 azazello and ezQuake team\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program.  If not, see <http://www.gnu.org/licenses/>.\n*/\n//\n// HUD commands\n//\n\n#ifndef __hud_h__\n#define __hud_h__\n\n// flags\n#define HUD_NO_DRAW            (1 <<  0)  // don't draw this automatically\n#define HUD_NO_SHOW            (1 <<  1)  // doesn't support show/hide\n#define HUD_NO_POS_X           (1 <<  2)  // doesn't support x positioning\n#define HUD_NO_POS_Y           (1 <<  3)  // doesn't support y positioning\n#define HUD_NO_FRAME           (1 <<  4)  // don't add frame\n#define HUD_ON_DIALOG          (1 <<  5)  // draw on dialog too\n#define HUD_ON_INTERMISSION    (1 <<  6)  // draw on intermission too\n#define HUD_ON_FINALE          (1 <<  7)  // draw on finale too\n#define HUD_ON_SCORES          (1 <<  8)  // draw on +showscores too\n#define HUD_NO_GROW            (1 <<  9)  // no frame grow\n#define HUD_PLUSMINUS          (1 << 10)  // auto add +/- commands\n#define HUD_OPACITY\t\t\t\t(1 << 11)\n\n#define HUD_INVENTORY          (HUD_NO_GROW)   // aply for sbar elements\n\n#define HUD_MAX_PARAMS  24\n\n#define\tHUD_REGEXP_OFFSET_COUNT\t20\n\n// Placement\n#define HUD_PLACE_SCREEN\t\t1\n#define HUD_PLACE_TOP\t\t\t2\n#define HUD_PLACE_VIEW\t\t\t3\n#define HUD_PLACE_SBAR\t\t\t4\n#define HUD_PLACE_IBAR\t\t\t5\n#define HUD_PLACE_HBAR\t\t\t6\n#define HUD_PLACE_SFREE\t\t\t7\n#define HUD_PLACE_IFREE\t\t\t8\n#define HUD_PLACE_HFREE\t\t\t9\n\n// Alignment\n#define HUD_ALIGN_LEFT\t\t\t1\n#define HUD_ALIGN_TOP\t\t\t1\n#define HUD_ALIGN_CENTER\t\t2\n#define HUD_ALIGN_RIGHT\t\t\t3\n#define HUD_ALIGN_BOTTOM\t\t3\n#define HUD_ALIGN_BEFORE\t\t4\n#define HUD_ALIGN_AFTER\t\t\t5\n#define HUD_ALIGN_CONSOLE\t\t6\n\ntypedef struct hud_s\n{\n    char *name;\t\t\t\t\t\t\t// Element name.\n    char *description;\t\t\t\t\t// Little help.\n\n    void (*draw_func) (struct hud_s *);\t// Drawing function.\n\n\tcvar_t *order;\t\t\t\t\t\t// Higher it is, later this element will be drawn\n\t\t\t\t\t\t\t\t\t\t// and more probable that will be on top.\n\n    cvar_t *show;\t\t\t\t\t\t// Show cvar.\n    cvar_t *frame;\t\t\t\t\t\t// Frame cvar.\n\tcvar_t *frame_color;\t\t\t\t// Frame color cvar.\n\tbyte frame_color_cache[4];\t\t\t// Cache for parsed frame color.\n\n\tcvar_t *opacity;\t\t\t\t\t// The overall opacity of the entire HUD element.\n\n    // placement\n    cvar_t *place;\t\t\t\t\t\t// Place string, parsed to:\n    struct hud_s *place_hud;\t\t\t// if snapped to hud element\n    qbool place_outside;\t\t\t\t// if hud: inside ot outside\n    int place_num;\t\t\t\t\t\t// place number (our or parent if hud)\n\t\t\t\t\t\t\t\t\t\t// note: item is placed at another HUD element\n\t\t\t\t\t\t\t\t\t\t// if place_hud != NULL\n\n    cvar_t *align_x;\t\t\t\t\t// Alignment cvars (left, right, ...)\n    cvar_t *align_y;\n    int    align_x_num;\t\t\t\t\t// Parsed alignment.\n    int    align_y_num;\n\n    cvar_t *pos_x;\t\t\t\t\t\t// Position cvars.\n    cvar_t *pos_y;\n\n\tcvar_t **params;\t\t\t\t\t// Registered parameters for the HUD element.\n    int num_params;\n\n    cactive_t  min_state;\t\t\t\t// At least this state is required\n\t\t\t\t\t\t\t\t\t\t// to draw this element.\n\n    unsigned   flags;\n\n    // Last draw parameters (mostly used by children)\n    int lx, ly, lw, lh;\t\t\t\t\t// Last position.\n    int al, ar, at, ab;\t\t\t\t\t// Last frame params.\n\n    int last_try_sequence;\t\t\t\t// Sequence, at which object tried to draw itself.\n    int last_draw_sequence;\t\t\t\t// Sequence, at which it was last drawn successfully.\n\n    struct hud_s *next;\t\t\t\t\t// Next HUD in the list.\n} hud_t;\n\ntypedef  void (*hud_func_type) (struct hud_s *);\n\n#define MAX_HUD_ELEMENTS 256\n\n//\n// Initialize\n// \nvoid HUD_Init(void);\n\n//\n// Add element to list\n// parameter format: \"name1\", \"default1\", ..., \"nameX\", \"defaultX\", NULL\n//\nhud_t * HUD_Register(char *name, char *var_alias, char *description,\n                     int flags, cactive_t min_state, int draw_order,\n                     hud_func_type draw_func,\n                     char *show, char *place, char *align_x, char *align_y,\n                     char *pos_x, char *pos_y, char *frame, char *frame_color,\n                     char *item_opacity, char *params, ...);\n\n\n//\n// Draw all active elements.\n//\nvoid HUD_Draw(void);\n\n//\n// Retrieve hud cvar.\n//\ncvar_t *HUD_FindVar(hud_t *hud, char *subvar);\n\n//\n// Find element in list.\n//\nhud_t * HUD_Find(char *name);\n\n//\n// Calculate screen position of element.\n// return value:\n//    true  - draw it\n//    false - don't draw, it is off screen (mayby partially)\n//\nqbool HUD_PrepareDraw(\n\t\t\thud_t *hud, int width, int height,\t\t// In.\n\t\t\tint *ret_x, int *ret_y);\t\t\t\t// Out.\n\nqbool HUD_PrepareDrawByName(\n\t\t\tchar *element, int width, int height,\t// In.\n\t\t\tint *ret_x, int *ret_y);\t\t\t\t// Out.\n\n\n// Sort all HUD Elements.\nvoid HUD_Sort(void);\n\n// Recalculate the position of all hud elements.\nvoid HUD_Recalculate(void);\n\n// when show pre-selected weapon/ammo? 1) player uses this system 2) not dead 3) when playing\n#define ShowPreselectedWeap()  (cl_weaponpreselect->value && cl.stats[STAT_HEALTH] > 0 && !cls.demoplayback && !cl.spectator)\n\n#endif // __hud_h__\n"
  },
  {
    "path": "plugins/ezhud/hud_common.c",
    "content": "/*\r\nCopyright (C) 2011 azazello and ezQuake team\r\n\r\nThis program is free software; you can redistribute it and/or\r\nmodify it under the terms of the GNU General Public License\r\nas published by the Free Software Foundation; either version 2\r\nof the License, or (at your option) any later version.\r\n\r\nThis program is distributed in the hope that it will be useful,\r\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\r\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\r\n\r\nSee the GNU General Public License for more details.\r\n\r\nYou should have received a copy of the GNU General Public License\r\nalong with this program.  If not, see <http://www.gnu.org/licenses/>.\r\n*/\r\n//\r\n// common HUD elements\r\n// like clock etc..\r\n//\r\n\r\n#include \"ezquakeisms.h\"\r\n/*\r\n#include \"common_draw.h\"\r\n#include \"mp3_player.h\"\r\n#include <png.h>\r\n#include \"image.h\"\r\n#include \"stats_grid.h\"\r\n#include \"vx_stuff.h\"\r\n#include \"gl_model.h\"\r\n#include \"gl_local.h\"\r\n#include \"tr_types.h\"\r\n#include \"rulesets.h\"\r\n#include \"utils.h\"\r\n#include \"sbar.h\"\r\n#include \"Ctrl.h\"\r\n#include \"console.h\"\r\n#include \"teamplay.h\"\r\n#include \"mvd_utils.h\"\r\n*/\r\n#include \"hud.h\"\r\n\r\n//#define WITH_PNG\t//more WITH_RADAR than anything else.\r\n\r\n#define draw_disc draw_disc2\r\n\r\nstatic mpic_t\t*sb_ammo[4];\r\nstatic mpic_t\t*sb_faces[7][2];\r\nstatic mpic_t\t*sb_face_invis;\r\nstatic mpic_t\t*sb_face_quad;\r\nstatic mpic_t\t*sb_face_invuln;\r\nstatic mpic_t\t*sb_face_invis_invuln;\r\nstatic mpic_t\t*sb_weapons[7][8];\r\nstatic mpic_t\t*sb_items[6];\r\nstatic mpic_t\t*sb_sigil[4];\r\n mpic_t\t*sb_nums[2][11];\r\nstatic mpic_t\t*sb_ibar;\r\nstatic mpic_t\t*sb_armor[3];\r\n mpic_t\t*sb_colon;\r\nstatic mpic_t\t*sb_slash;\r\nstatic mpic_t\t*sb_disc;\r\nstatic mpic_t\t*sb_net;\r\n\r\nvoid HUD_InitSbarImages(void)\r\n{\r\n\tint i;\r\n\tsb_disc = Draw_CacheWadPic(\"disc\");\r\n\tsb_net = Draw_CacheWadPic(\"net\");\r\n\r\n\tfor (i = 0; i < 10; i++) {\r\n\t\tsb_nums[0][i] = Draw_CacheWadPic (va(\"num_%i\",i));\r\n\t\tsb_nums[1][i] = Draw_CacheWadPic (va(\"anum_%i\",i));\r\n\t}\r\n\r\n\tsb_nums[0][10] = Draw_CacheWadPic (\"num_minus\");\r\n\tsb_nums[1][10] = Draw_CacheWadPic (\"anum_minus\");\r\n\r\n\tsb_colon = Draw_CacheWadPic (\"num_colon\");\r\n\tsb_slash = Draw_CacheWadPic (\"num_slash\");\r\n\r\n\tsb_weapons[0][0] = Draw_CacheWadPic (\"inv_shotgun\");\r\n\tsb_weapons[0][1] = Draw_CacheWadPic (\"inv_sshotgun\");\r\n\tsb_weapons[0][2] = Draw_CacheWadPic (\"inv_nailgun\");\r\n\tsb_weapons[0][3] = Draw_CacheWadPic (\"inv_snailgun\");\r\n\tsb_weapons[0][4] = Draw_CacheWadPic (\"inv_rlaunch\");\r\n\tsb_weapons[0][5] = Draw_CacheWadPic (\"inv_srlaunch\");\r\n\tsb_weapons[0][6] = Draw_CacheWadPic (\"inv_lightng\");\r\n\r\n\tsb_weapons[1][0] = Draw_CacheWadPic (\"inv2_shotgun\");\r\n\tsb_weapons[1][1] = Draw_CacheWadPic (\"inv2_sshotgun\");\r\n\tsb_weapons[1][2] = Draw_CacheWadPic (\"inv2_nailgun\");\r\n\tsb_weapons[1][3] = Draw_CacheWadPic (\"inv2_snailgun\");\r\n\tsb_weapons[1][4] = Draw_CacheWadPic (\"inv2_rlaunch\");\r\n\tsb_weapons[1][5] = Draw_CacheWadPic (\"inv2_srlaunch\");\r\n\tsb_weapons[1][6] = Draw_CacheWadPic (\"inv2_lightng\");\r\n\r\n\tfor (i = 0; i < 5; i++) \r\n\t{\r\n\t\tsb_weapons[2 + i][0] = Draw_CacheWadPic (va(\"inva%i_shotgun\", i + 1));\r\n\t\tsb_weapons[2 + i][1] = Draw_CacheWadPic (va(\"inva%i_sshotgun\", i + 1));\r\n\t\tsb_weapons[2 + i][2] = Draw_CacheWadPic (va(\"inva%i_nailgun\", i + 1));\r\n\t\tsb_weapons[2 + i][3] = Draw_CacheWadPic (va(\"inva%i_snailgun\", i + 1));\r\n\t\tsb_weapons[2 + i][4] = Draw_CacheWadPic (va(\"inva%i_rlaunch\", i + 1));\r\n\t\tsb_weapons[2 + i][5] = Draw_CacheWadPic (va(\"inva%i_srlaunch\", i + 1));\r\n\t\tsb_weapons[2 + i][6] = Draw_CacheWadPic (va(\"inva%i_lightng\", i + 1));\r\n\t}\r\n\r\n\tsb_ammo[0] = Draw_CacheWadPic (\"sb_shells\");\r\n\tsb_ammo[1] = Draw_CacheWadPic (\"sb_nails\");\r\n\tsb_ammo[2] = Draw_CacheWadPic (\"sb_rocket\");\r\n\tsb_ammo[3] = Draw_CacheWadPic (\"sb_cells\");\r\n\r\n\tsb_armor[0] = Draw_CacheWadPic (\"sb_armor1\");\r\n\tsb_armor[1] = Draw_CacheWadPic (\"sb_armor2\");\r\n\tsb_armor[2] = Draw_CacheWadPic (\"sb_armor3\");\r\n\r\n\tsb_items[0] = Draw_CacheWadPic (\"sb_key1\");\r\n\tsb_items[1] = Draw_CacheWadPic (\"sb_key2\");\r\n\tsb_items[2] = Draw_CacheWadPic (\"sb_invis\");\r\n\tsb_items[3] = Draw_CacheWadPic (\"sb_invuln\");\r\n\tsb_items[4] = Draw_CacheWadPic (\"sb_suit\");\r\n\tsb_items[5] = Draw_CacheWadPic (\"sb_quad\");\r\n\r\n\tsb_sigil[0] = Draw_CacheWadPic (\"sb_sigil1\");\r\n\tsb_sigil[1] = Draw_CacheWadPic (\"sb_sigil2\");\r\n\tsb_sigil[2] = Draw_CacheWadPic (\"sb_sigil3\");\r\n\tsb_sigil[3] = Draw_CacheWadPic (\"sb_sigil4\");\r\n\r\n\tsb_faces[4][0] = Draw_CacheWadPic (\"face1\");\r\n\tsb_faces[4][1] = Draw_CacheWadPic (\"face_p1\");\r\n\tsb_faces[3][0] = Draw_CacheWadPic (\"face2\");\r\n\tsb_faces[3][1] = Draw_CacheWadPic (\"face_p2\");\r\n\tsb_faces[2][0] = Draw_CacheWadPic (\"face3\");\r\n\tsb_faces[2][1] = Draw_CacheWadPic (\"face_p3\");\r\n\tsb_faces[1][0] = Draw_CacheWadPic (\"face4\");\r\n\tsb_faces[1][1] = Draw_CacheWadPic (\"face_p4\");\r\n\tsb_faces[0][0] = Draw_CacheWadPic (\"face5\");\r\n\tsb_faces[0][1] = Draw_CacheWadPic (\"face_p5\");\r\n\r\n\tsb_face_invis = Draw_CacheWadPic (\"face_invis\");\r\n\tsb_face_invuln = Draw_CacheWadPic (\"face_invul2\");\r\n\tsb_face_invis_invuln = Draw_CacheWadPic (\"face_inv2\");\r\n\tsb_face_quad = Draw_CacheWadPic (\"face_quad\");\r\n\r\n\tsb_ibar = Draw_CacheWadPic(\"ibar\");\r\n}\r\n\r\nplugnetinfo_t *GetNetworkInfo(void)\r\n{\r\n\tstatic plugnetinfo_t ni;\r\n\tstatic int uc;\r\n\tif (uc != host_screenupdatecount)\r\n\t{\r\n\t\tuc = host_screenupdatecount;\r\n\t\tclientfuncs->GetNetworkInfo(&ni, sizeof(ni));\r\n\t}\r\n\treturn &ni;\r\n}\r\n\r\n\r\n#ifndef STAT_MINUS\r\n#define STAT_MINUS\t\t10\r\n#endif\r\n\r\nhud_t *hud_netgraph = NULL;\r\n\r\n// ----------------\r\n// HUD planning\r\n//\r\n\r\nstruct\r\n{\r\n\t// this is temporary storage place for some of user's settings\r\n\t// hud_* values will be dumped into config file\r\n\tint old_multiview;\r\n\tint old_fov;\r\n\tint old_newhud;\r\n\r\n\tqbool active;\r\n} autohud;\r\n\r\nvoid OnAutoHudChange(cvar_t *var, char *value, qbool *cancel);\r\nqbool autohud_loaded = false;\r\ncvar_t *hud_planmode;\r\ncvar_t *mvd_autohud;\r\ncvar_t *hud_digits_trim;\r\ncvar_t *cl_multiview;\r\n\r\nint hud_stats[MAX_CL_STATS];\r\n\r\ncvar_t *cl_weaponpreselect;\r\nextern int IN_BestWeapon(void);\r\nextern void DumpHUD(char *);\r\nextern char *Macro_MatchType(void);\r\n\r\nint HUD_Stats(int stat_num)\r\n{\r\n    if (hud_planmode->value)\r\n        return hud_stats[stat_num];\r\n    else\r\n        return cl.stats[stat_num];\r\n}\r\n\r\n// ----------------\r\n// HUD low levels\r\n//\r\n\r\ncvar_t *hud_tp_need;\r\n\r\n/* tp need levels\r\nint TP_IsHealthLow(void);\r\nint TP_IsArmorLow(void);\r\nint TP_IsAmmoLow(int weapon); */\r\ncvar_t *tp_need_health, *tp_need_ra, *tp_need_ya, *tp_need_ga,\r\n\t\t*tp_weapon_order, *tp_need_weapon, *tp_need_shells,\r\n\t\t*tp_need_nails, *tp_need_rockets, *tp_need_cells;\r\n\r\nint State_AmmoNumForWeapon(int weapon)\r\n{\t// returns ammo number (shells = 1, nails = 2, rox = 3, cells = 4) for given weapon\r\n\tswitch (weapon) {\r\n\t\tcase 2: case 3: return 1;\r\n\t\tcase 4: case 5: return 2;\r\n\t\tcase 6: case 7: return 3;\r\n\t\tcase 8: return 4;\r\n\t\tdefault: return 0;\r\n\t}\r\n}\r\n\r\nint State_AmmoForWeapon(int weapon)\r\n{\t// returns ammo amount for given weapon\r\n\tint ammon = State_AmmoNumForWeapon(weapon);\r\n\r\n\tif (ammon)\r\n\t\treturn cl.stats[STAT_SHELLS + ammon - 1];\r\n\telse\r\n\t\treturn 0;\r\n}\r\n\r\nint TP_IsHealthLow(void)\r\n{\r\n    return cl.stats[STAT_HEALTH] <= tp_need_health->value;\r\n}\r\n\r\nint TP_IsArmorLow(void)\r\n{\r\n    if ((cl.stats[STAT_ARMOR] > 0) && (cl.stats[STAT_ITEMS] & IT_ARMOR3))\r\n        return cl.stats[STAT_ARMOR] <= tp_need_ra->value;\r\n    if ((cl.stats[STAT_ARMOR] > 0) && (cl.stats[STAT_ITEMS] & IT_ARMOR2))\r\n        return cl.stats[STAT_ARMOR] <= tp_need_ya->value;\r\n    if ((cl.stats[STAT_ARMOR] > 0) && (cl.stats[STAT_ITEMS] & IT_ARMOR1))\r\n        return cl.stats[STAT_ARMOR] <= tp_need_ga->value;\r\n    return 1;\r\n}\r\n\r\nint TP_IsWeaponLow(void)\r\n{\r\n    char *s = tp_weapon_order->string;\r\n    while (*s  &&  *s != tp_need_weapon->string[0])\r\n    {\r\n        if (cl.stats[STAT_ITEMS] & (IT_SHOTGUN << (*s-'0'-2)))\r\n            return false;\r\n        s++;\r\n    }\r\n    return true;\r\n}\r\n\r\nint TP_IsAmmoLow(int weapon)\r\n{\r\n    int ammo = State_AmmoForWeapon(weapon);\r\n    switch (weapon)\r\n    {\r\n    case 2:\r\n    case 3:  return ammo <= tp_need_shells->value;\r\n    case 4:\r\n    case 5:  return ammo <= tp_need_nails->value;\r\n    case 6:\r\n    case 7:  return ammo <= tp_need_rockets->value;\r\n    case 8:  return ammo <= tp_need_cells->value;\r\n    default: return 0;\r\n    }\r\n}\r\n\r\nint TP_TeamFortressEngineerSpanner(void)\r\n{\r\n#ifdef HAXX\r\n\tchar *player_skin=Info_ValueForKey(cl.players[cl.playernum].userinfo,\"skin\");\r\n\tchar *model_name=cl.model_precache[cl.viewent.current.modelindex]->name;\r\n\tif (cl.teamfortress && player_skin\r\n\t\t\t&& (strcasecmp(player_skin, \"tf_eng\") == 0)\r\n\t\t\t&& model_name\r\n\t\t\t&& (strcasecmp(model_name, \"progs/v_span.mdl\") == 0))\r\n\t{\r\n\t\treturn 1;\r\n\t}\r\n\telse\r\n#endif\r\n\t{\r\n\t\treturn 0;\r\n\t}\r\n}\r\n\r\nqbool HUD_HealthLow(void)\r\n{\r\n    if (hud_tp_need->value)\r\n        return TP_IsHealthLow();\r\n    else\r\n        return HUD_Stats(STAT_HEALTH) <= 25;\r\n}\r\n\r\nqbool HUD_ArmorLow(void)\r\n{\r\n    if (hud_tp_need->value)\r\n        return (TP_IsArmorLow());\r\n    else\r\n        return (HUD_Stats(STAT_ARMOR) <= 25);\r\n}\r\n\r\nqbool HUD_AmmoLow(void)\r\n{\r\n    if (hud_tp_need->value)\r\n    {\r\n        if (HUD_Stats(STAT_ITEMS) & IT_SHELLS)\r\n            return TP_IsAmmoLow(2);\r\n        else if (HUD_Stats(STAT_ITEMS) & IT_NAILS)\r\n            return TP_IsAmmoLow(4);\r\n        else if (HUD_Stats(STAT_ITEMS) & IT_ROCKETS)\r\n            return TP_IsAmmoLow(6);\r\n        else if (HUD_Stats(STAT_ITEMS) & IT_CELLS)\r\n            return TP_IsAmmoLow(8);\r\n        return false;\r\n    }\r\n    else\r\n        return (HUD_Stats(STAT_AMMO) <= 10);\r\n}\r\n\r\nint HUD_AmmoLowByWeapon(int weapon)\r\n{\r\n    if (hud_tp_need->value)\r\n        return TP_IsAmmoLow(weapon);\r\n    else\r\n    {\r\n        int a;\r\n        switch (weapon)\r\n        {\r\n        case 2:\r\n        case 3:\r\n            a = STAT_SHELLS; break;\r\n        case 4:\r\n        case 5:\r\n            a = STAT_NAILS; break;\r\n        case 6:\r\n        case 7:\r\n            a = STAT_ROCKETS; break;\r\n        case 8:\r\n            a = STAT_CELLS; break;\r\n        default:\r\n            return false;\r\n        }\r\n        return (HUD_Stats(a) <= 10);\r\n    }\r\n}\r\n\r\n// ----------------\r\n// DrawFPS\r\nvoid SCR_HUD_DrawFPS(hud_t *hud)\r\n{\r\n    int x, y;\r\n    char st[128];\r\n\r\n    static cvar_t\r\n\t    *hud_fps_show_min = NULL,\r\n\t    *hud_fps_style,\r\n\t    *hud_fps_title,\r\n\t    *hud_fps_drop;\r\n\r\n    if (hud_fps_show_min == NULL)   // first time called\r\n    {\r\n\t    hud_fps_show_min = HUD_FindVar(hud, \"show_min\");\r\n\t    hud_fps_style    = HUD_FindVar(hud, \"style\");\r\n\t    hud_fps_title    = HUD_FindVar(hud, \"title\");\r\n\t    hud_fps_drop = HUD_FindVar(hud, \"drop\");\r\n    }\r\n\r\n    if (hud_fps_show_min->value)\r\n        snprintf (st, sizeof (st), \"%3d^Ue00f%3d\", (int)(cls.min_fps + 0.25), (int) (cls.fps + 0.25));\r\n    else\r\n        snprintf (st, sizeof (st), \"%3d\", (int)(cls.fps + 0.25));\r\n\r\n    if (hud_fps_title->value)\r\n        strlcat (st, \" fps\", sizeof (st));\r\n\r\n    if (HUD_PrepareDraw(hud, strlen(st)*8, 8, &x, &y))\r\n    {\r\n\t\tplugnetinfo_t *netinfo = GetNetworkInfo();\r\n\t\tif (netinfo->capturing == 2)\t//don't show fps if its locked to something anyway.\r\n\t\t\treturn;\r\n\r\n\t\tif ((hud_fps_style->value) == 1)\r\n\t\t\tDraw_Alt_String(x, y, st);\r\n\t\telse if ((hud_fps_style->value) == 2) {\r\n\t\t\tif ((hud_fps_drop->value) >= cls.fps) // if fps is less than a user-set value, then show it\r\n\t\t\t\tDraw_String(x, y, st);\r\n\t\t}\r\n\t\telse if ((hud_fps_style->value) == 3) {\r\n\t\t\tif ((hud_fps_drop->value) >= cls.fps) // if fps is less than a user-set value, then show it\r\n\t\t\t\tDraw_Alt_String(x, y, st);\r\n\t\t}\r\n\t\telse // hud_fps_style is anything other than 1,2,3\r\n\t\t\tDraw_String(x, y, st);\r\n    }\r\n}\r\n\r\nvoid SCR_HUD_DrawVidLag(hud_t *hud)\r\n{\r\n\tint x, y;\r\n\tchar st[128];\r\n\tstatic cvar_t *hud_vidlag_style = NULL;\r\n\r\n\tplugnetinfo_t *netinfo = GetNetworkInfo();\r\n\tstatic double old_lag;\r\n\r\n\tif (netinfo->vlatency)\r\n\t{\r\n\t\t// take the average of last two values, otherwise it\r\n\t\t// changes very fast and is hard to read\r\n\t\tdouble current, avg;\r\n\t\tcurrent = netinfo->vlatency;\r\n\t\tavg = (current + old_lag) * 0.5;\r\n\t\told_lag = current;\r\n\t\tsnprintf (st, sizeof (st), \"%2.1f\", avg * 1000);\r\n\t}\r\n\telse\r\n\t\tstrcpy(st, \"?\");\r\n\r\n\tif (hud_vidlag_style == NULL)  // first time called\r\n\t{\r\n\t\thud_vidlag_style = HUD_FindVar(hud, \"style\");\r\n\t}\r\n\r\n\tstrlcat (st, \" ms\", sizeof (st));\r\n\r\n\tif (HUD_PrepareDraw(hud, strlen(st)*8, 8, &x, &y))\r\n\t{\r\n\t\tif (hud_vidlag_style->value)\r\n\t\t{\r\n\t\t\tDraw_Alt_String(x, y, st);\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tDraw_String(x, y, st);\r\n\t\t}\r\n\t}\r\n}\r\n\r\nvoid SCR_HUD_DrawMouserate(hud_t *hud)\r\n{\r\n\tint x, y;\r\n\tstatic int lastresult = 0;\r\n\tint newresult;\r\n\tchar st[80];\t// string buffer\r\n\tdouble t;\t\t// current time\r\n\tstatic double lastframetime;\t// last refresh\r\n\tplugnetinfo_t *netinfo = GetNetworkInfo();\r\n\r\n    static cvar_t *hud_mouserate_title = NULL,\r\n\t\t*hud_mouserate_interval,\r\n\t\t*hud_mouserate_style;\r\n\r\n    if (hud_mouserate_title == NULL) // first time called\r\n    {\r\n\t\thud_mouserate_style    = HUD_FindVar(hud, \"style\");\r\n        hud_mouserate_title    = HUD_FindVar(hud, \"title\");\r\n\t\thud_mouserate_interval = HUD_FindVar(hud, \"interval\");\r\n    }\r\n\r\n\tt = cls.realtime;\r\n\tif ((t - lastframetime) >= hud_mouserate_interval->value) {\r\n\t\tnewresult = netinfo->mrate;\r\n\t\tlastframetime = t;\r\n\t} else\r\n\t\tnewresult = 0;\r\n\r\n\tif (newresult > 0) {\r\n\t\tsnprintf(st, sizeof(st), \"%4d\", newresult);\r\n\t\tlastresult = newresult;\r\n\t} else if (!newresult)\r\n\t\tsnprintf(st, sizeof(st), \"%4d\", lastresult);\r\n\telse\r\n\t\tsnprintf(st, sizeof(st), \"n/a\");\r\n\r\n    if (hud_mouserate_title->value)\r\n        strlcat(st, \" Hz\", sizeof (st));\r\n\r\n    if (HUD_PrepareDraw(hud, strlen(st)*8, 8, &x, &y))\r\n    {\r\n\t\tif (hud_mouserate_style->value)\r\n\t\t{\r\n\t\t\tDraw_Alt_String(x, y, st);\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tDraw_String(x, y, st);\r\n\t\t}\r\n    }\r\n}\r\n\r\n#define MAX_TRACKING_STRING\t\t512\r\n\r\nvoid SCR_HUD_DrawTracking(hud_t *hud)\r\n{\r\n#ifdef HAXX\r\n\tstatic char tracked_strings[MV_VIEWS][MAX_TRACKING_STRING];\r\n\tstatic int tracked[MV_VIEWS] = {-1, -1, -1, -1};\r\n\tint view = 0;\r\n#endif\r\n\tint views = 1;\r\n    int x = 0, y = 0, width = 0, height = 0;\r\n    char track_string[MAX_TRACKING_STRING];\r\n\r\n\tstatic cvar_t *hud_tracking_format = NULL,\r\n\t\t*hud_tracking_scale;\r\n\r\n\tif (!hud_tracking_format) {\r\n\t\thud_tracking_format = HUD_FindVar(hud, \"format\");\r\n\t\thud_tracking_scale = HUD_FindVar(hud, \"scale\");\r\n\t}\r\n\r\n\tstrlcpy(track_string, hud_tracking_format->string, sizeof(track_string));\r\n\r\n#ifdef HAXX\r\n\tif(cls.mvdplayback && cl_multiview->value && CURRVIEW > 0)\r\n\t{\r\n\t\t//\r\n\t\t// Multiview.\r\n\t\t//\r\n\r\n\t\tviews = cl_multiview->value;\r\n\r\n\t\t// Save the currently tracked player for the slot being drawn\r\n\t\t// (this will be done for all views and we'll get a complete\r\n\t\t// list over who we're tracking).\r\n\t\ttracked[CURRVIEW - 1] = spec_track;\r\n\r\n\t\tfor(view = 0; view < MV_VIEWS; view++)\r\n\t\t{\r\n\t\t\tint new_width = 0;\r\n\r\n\t\t\t// We haven't found who we're tracking in this view.\r\n\t\t\tif(tracked[view] < 0)\r\n\t\t\t{\r\n\t\t\t\tcontinue;\r\n\t\t\t}\r\n\r\n\t\t\tstrlcpy(tracked_strings[view], hud_tracking_format->string, sizeof(tracked_strings[view]));\r\n\r\n\t\t\tReplace_In_String(tracked_strings[view], sizeof(tracked_strings[view]), '%', 3,\r\n\t\t\t\t\"v\", cl_multiview->value ? va(\"%d\", view+1) : \"\",\t\t\t// Replace %v with the current view (in multiview)\r\n\t\t\t\t\"n\", cl.players[tracked[view]].name,\t\t\t\t\t\t// Replace %n with player name.\r\n\t\t\t\t\"t\", cl.teamplay ? cl.players[tracked[view]].team : \"\");\t// Replace %t with player team if teamplay is on.\r\n\r\n\t\t\t// Set the width.\r\n\t\t\tnew_width = 8 * strlen_color(tracked_strings[view]);\r\n\t\t\twidth = (new_width > width) ? new_width : width;\r\n\t\t}\r\n\t}\r\n\telse\r\n#endif\r\n\t{\r\n\t\t// Normal.\r\n\t\tReplace_In_String(track_string, sizeof(track_string), '%', 2,\r\n\t\t\t\"n\", cl.players[spec_track].name,\t\t\t\t\t\t// Replace %n with player name.\r\n\t\t\t\"t\", cl.teamplay ? cl.players[spec_track].team : \"\");\t// Replace %t with player team if teamplay is on.\r\n\t\twidth = 8 * strlen_color(track_string);\r\n\t}\r\n\r\n\theight = 8 * views;\r\n\theight *= hud_tracking_scale->value;\r\n\twidth *= hud_tracking_scale->value;\r\n\r\n\tif (!(cl.spectator && autocam == CAM_TRACK))\r\n\t\theight = 0;\r\n\r\n\tif(!HUD_PrepareDraw(hud, width, height, &x, &y))\r\n\t{\r\n\t\treturn;\r\n\t}\r\n\r\n\tif (height == 0)\r\n\t\treturn;\r\n\r\n#ifdef HAXX\r\n\tif (cls.mvdplayback && cl_multiview->value && autocam == CAM_TRACK)\r\n\t{\r\n\t\t// Multiview\r\n\t\tfor(view = 0; view < MV_VIEWS; view++)\r\n\t\t{\r\n\t\t\tif(tracked[view] < 0 || CURRVIEW <= 0)\r\n\t\t\t{\r\n\t\t\t\tcontinue;\r\n\t\t\t}\r\n\t\t\tDraw_SString(x, y + view*8, tracked_strings[view], hud_tracking_scale->value);\r\n\t\t}\r\n\t}\r\n\telse\r\n#endif\r\n\t\tif (cl.spectator && autocam == CAM_TRACK && !cl_multiview->value)\r\n\t{\r\n\t\t// Normal\r\n\t\tDraw_SString(x, y, track_string, hud_tracking_scale->value);\r\n\t}\r\n}\r\n\r\n#ifdef HAXX\r\nvoid R_MQW_NetGraph(int outgoing_sequence, int incoming_sequence, int *packet_latency,\r\n                int lost, int minping, int avgping, int maxping, int devping,\r\n                int posx, int posy, int width, int height, int revx, int revy);\r\n// ----------------\r\n// Netgraph\r\nstatic void SCR_HUD_Netgraph(hud_t *hud)\r\n{\r\n    static cvar_t\r\n        *par_width = NULL, *par_height,\r\n        *par_swap_x, *par_swap_y,\r\n        *par_ploss;\r\n\r\n    if (par_width == NULL)  // first time\r\n    {\r\n        par_width  = HUD_FindVar(hud, \"width\");\r\n        par_height = HUD_FindVar(hud, \"height\");\r\n        par_swap_x = HUD_FindVar(hud, \"swap_x\");\r\n        par_swap_y = HUD_FindVar(hud, \"swap_y\");\r\n        par_ploss  = HUD_FindVar(hud, \"ploss\");\r\n    }\r\n\r\n    R_MQW_NetGraph(cls.netchan.outgoing_sequence, cls.netchan.incoming_sequence,\r\n        packet_latency, par_ploss->value ? CL_CalcNet() : -1, -1, -1, -1, -1, -1,\r\n        -1, (int)par_width->value, (int)par_height->value,\r\n        (int)par_swap_x->value, (int)par_swap_y->value);\r\n}\r\n#endif\r\n\r\n//---------------------\r\n//\r\n// draw HUD ping\r\n//\r\nstatic void SCR_HUD_DrawPing(hud_t *hud)\r\n{\r\n    double t;\r\n    static double last_calculated;\r\n    static int ping_avg, pl, ping_min, ping_max;\r\n    static float ping_dev;\r\n\r\n    int width, height;\r\n    int x, y;\r\n    char buf[512];\r\n\tplugnetinfo_t *netinfo = GetNetworkInfo();\r\n\r\n    static cvar_t\r\n\t\t*hud_ping_period = NULL,\r\n        *hud_ping_show_pl,\r\n        *hud_ping_show_dev,\r\n        *hud_ping_show_min,\r\n        *hud_ping_show_max,\r\n\t\t*hud_ping_style,\r\n        *hud_ping_blink;\r\n\r\n    if (hud_ping_period == NULL)    // first time\r\n    {\r\n        hud_ping_period   = HUD_FindVar(hud, \"period\");\r\n        hud_ping_show_pl  = HUD_FindVar(hud, \"show_pl\");\r\n        hud_ping_show_dev = HUD_FindVar(hud, \"show_dev\");\r\n        hud_ping_show_min = HUD_FindVar(hud, \"show_min\");\r\n        hud_ping_show_max = HUD_FindVar(hud, \"show_max\");\r\n\t\thud_ping_style    = HUD_FindVar(hud, \"style\");\r\n        hud_ping_blink    = HUD_FindVar(hud, \"blink\");\r\n    }\r\n\r\n    t = cls.realtime;\r\n    if (t - last_calculated  >  hud_ping_period->value)\r\n    {\r\n//        float period;\r\n\r\n        last_calculated = t;\r\n\r\n//        period = max(hud_ping_period->value, 0);\r\n\r\n        ping_avg = (int)(netinfo->ping.s_avg*1000 + 0.5);\r\n        ping_min = (int)(netinfo->ping.s_mn*1000 + 0.5);\r\n        ping_max = (int)(netinfo->ping.s_mx*1000 + 0.5);\r\n        ping_dev = netinfo->ping.ms_stddev;\r\n\t\tpl = netinfo->loss.dropped*100;\r\n\r\n        clamp(ping_avg, 0, 999);\r\n        clamp(ping_min, 0, 999);\r\n        clamp(ping_max, 0, 999);\r\n        clamp(ping_dev, 0, 99.9);\r\n        clamp(pl, 0, 100);\r\n    }\r\n\r\n    buf[0] = 0;\r\n\r\n    // blink\r\n    if (hud_ping_blink->value)   // add dot\r\n        strlcat (buf, (last_calculated + hud_ping_period->value/2 > cls.realtime) ? \"^Ue08f\" : \" \", sizeof (buf));\r\n\r\n    // min ping\r\n    if (hud_ping_show_min->value)\r\n        strlcat (buf, va(\"%d^Ue00f\", ping_min), sizeof (buf));\r\n\r\n    // ping\r\n    strlcat (buf, va(\"%d\", ping_avg), sizeof (buf));\r\n\r\n    // max ping\r\n    if (hud_ping_show_max->value)\r\n        strlcat (buf, va(\"^Ue00f%d\", ping_max), sizeof (buf));\r\n\r\n    // unit\r\n    strlcat (buf, \" ms\", sizeof (buf));\r\n\r\n    // standard deviation\r\n    if (hud_ping_show_dev->value)\r\n        strlcat (buf, va(\" (%.1f)\", ping_dev), sizeof (buf));\r\n\r\n    // pl\r\n    if (hud_ping_show_pl->value)\r\n        strlcat (buf, va(\" ^Ue08f %d%%\", pl), sizeof (buf));\r\n\r\n    // display that on screen\r\n    width = strlen(buf) * 8;\r\n    height = 8;\r\n\r\n    if (HUD_PrepareDraw(hud, width, height, &x, &y))\r\n    {\r\n\t\tif (hud_ping_style->value)\r\n\t\t{\r\n\t\t\tDraw_Alt_String(x, y, buf);\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tDraw_String(x, y, buf);\r\n\t\t}\r\n    }\r\n}\r\n\r\nstatic const char *SCR_HUD_ClockFormat(int format)\r\n{\r\n\tswitch (format) {\r\n\t\tcase 1: return \"%I:%M %p\";\r\n\t\tcase 2: return \"%I:%M:%S %p\";\r\n\t\tcase 3: return \"%H:%M\";\r\n\t\tdefault: case 0: return \"%H:%M:%S\";\r\n\t}\r\n}\r\n\r\n//---------------------\r\n//\r\n// draw HUD clock\r\n//\r\nvoid SCR_HUD_DrawClock(hud_t *hud)\r\n{\r\n    int width, height;\r\n    int x, y;\r\n\tconst char *t;\r\n\r\n    static cvar_t\r\n        *hud_clock_big = NULL,\r\n        *hud_clock_style,\r\n        *hud_clock_blink,\r\n\t\t*hud_clock_scale,\r\n\t\t*hud_clock_format;\r\n\r\n    if (hud_clock_big == NULL)    // first time\r\n    {\r\n        hud_clock_big   = HUD_FindVar(hud, \"big\");\r\n        hud_clock_style = HUD_FindVar(hud, \"style\");\r\n        hud_clock_blink = HUD_FindVar(hud, \"blink\");\r\n\t\thud_clock_scale = HUD_FindVar(hud, \"scale\");\r\n\t\thud_clock_format= HUD_FindVar(hud, \"format\");\r\n    }\r\n\r\n\tt = SCR_GetTimeString(TIMETYPE_CLOCK, SCR_HUD_ClockFormat(hud_clock_format->ival));\r\n\twidth = SCR_GetClockStringWidth(t, hud_clock_big->ival, hud_clock_scale->value);\r\n\theight = SCR_GetClockStringHeight(hud_clock_big->ival, hud_clock_scale->value);\r\n\r\n    if (HUD_PrepareDraw(hud, width, height, &x, &y))\r\n    {\r\n        if (hud_clock_big->value)\r\n            SCR_DrawBigClock(x, y, hud_clock_style->value, hud_clock_blink->value, hud_clock_scale->value, t);\r\n        else\r\n            SCR_DrawSmallClock(x, y, hud_clock_style->value, hud_clock_blink->value, hud_clock_scale->value, t);\r\n    }\r\n}\r\n\r\n//---------------------\r\n//\r\n// draw HUD notify\r\n//\r\n\r\nstatic void SCR_HUD_DrawNotify(hud_t* hud)\r\n{\r\n\tstatic cvar_t* hud_notify_rows = NULL;\r\n\tstatic cvar_t* hud_notify_scale;\r\n\tstatic cvar_t* hud_notify_time;\r\n\tstatic cvar_t* hud_notify_cols;\r\n\r\n\tint x;\r\n\tint y;\r\n\tint width;\r\n\tint height;\r\n\r\n\tif (hud_notify_rows == NULL) // First time.\r\n\t{\r\n       hud_notify_rows  = HUD_FindVar(hud, \"rows\");\r\n       hud_notify_cols  = HUD_FindVar(hud, \"cols\");\r\n       hud_notify_scale = HUD_FindVar(hud, \"scale\");\r\n       hud_notify_time  = HUD_FindVar(hud, \"time\");\r\n\t}\r\n\r\n\theight = hud_notify_rows->ival * 8 * hud_notify_scale->value;\r\n\twidth  = 8 * hud_notify_cols->ival * hud_notify_scale->value;\r\n\r\n\tif (HUD_PrepareDraw(hud, width, height, &x, &y))\r\n\t{\r\n\t\tcvarfuncs->SetFloat(\"con_notify_x\",\t\t(float)x / vid.width);\r\n\t\tcvarfuncs->SetFloat(\"con_notify_y\",\t\t(float)y / vid.height);\r\n\t\tcvarfuncs->SetFloat(\"con_notify_w\",\t\t(float)width / vid.width);\r\n\t\tcvarfuncs->SetFloat(\"con_numnotifylines\",(int)(height/(8*hud_notify_scale->value) + 0.01));\r\n\t\tcvarfuncs->SetFloat(\"con_notifytime\",\t(float)hud_notify_time->ival);\r\n\t\tcvarfuncs->SetFloat(\"con_textsize\",\t\t8.0 * hud_notify_scale->value);\r\n//\t\tSCR_DrawNotify(x, y, hud_notify_scale->value, hud_notify_time->ival, hud_notify_rows->ival, hud_notify_cols->ival);\r\n\t}\r\n}\r\n\r\n//---------------------\r\n//\r\n// draw HUD gameclock\r\n//\r\nvoid SCR_HUD_DrawGameClock(hud_t *hud)\r\n{\r\n    int width, height;\r\n    int x, y;\r\n\tint timetype;\r\n\tconst char *t;\r\n\r\n    static cvar_t\r\n        *hud_gameclock_big = NULL,\r\n        *hud_gameclock_style,\r\n        *hud_gameclock_blink,\r\n\t\t*hud_gameclock_countdown,\r\n\t\t*hud_gameclock_scale\r\n//\t\t*hud_gameclock_offset\r\n\t\t;\r\n\r\n    if (hud_gameclock_big == NULL)    // first time\r\n    {\r\n        hud_gameclock_big   = HUD_FindVar(hud, \"big\");\r\n        hud_gameclock_style = HUD_FindVar(hud, \"style\");\r\n        hud_gameclock_blink = HUD_FindVar(hud, \"blink\");\r\n\t\thud_gameclock_countdown = HUD_FindVar(hud, \"countdown\");\r\n\t\thud_gameclock_scale = HUD_FindVar(hud, \"scale\");\r\n//\t\thud_gameclock_offset = HUD_FindVar(hud, \"offset\");\r\n//\t\tgameclockoffset = &hud_gameclock_offset->ival;\r\n    }\r\n\r\n\ttimetype = (hud_gameclock_countdown->value) ? TIMETYPE_GAMECLOCKINV : TIMETYPE_GAMECLOCK;\r\n\tt = SCR_GetTimeString(timetype, NULL);\r\n\twidth = SCR_GetClockStringWidth(t, hud_gameclock_big->ival, hud_gameclock_scale->value);\r\n\theight = SCR_GetClockStringHeight(hud_gameclock_big->ival, hud_gameclock_scale->value);\r\n\r\n    if (HUD_PrepareDraw(hud, width, height, &x, &y))\r\n    {\r\n        if (hud_gameclock_big->value)\r\n            SCR_DrawBigClock(x, y, hud_gameclock_style->value, hud_gameclock_blink->value, hud_gameclock_scale->value, t);\r\n        else\r\n            SCR_DrawSmallClock(x, y, hud_gameclock_style->value, hud_gameclock_blink->value, hud_gameclock_scale->value, t);\r\n    }\r\n}\r\n\r\n//---------------------\r\n//\r\n// draw HUD democlock\r\n//\r\nvoid SCR_HUD_DrawDemoClock(hud_t *hud)\r\n{\r\n    int width = 0;\r\n\tint height = 0;\r\n    int x = 0;\r\n\tint y = 0;\r\n\tconst char *t;\r\n    static cvar_t\r\n        *hud_democlock_big = NULL,\r\n        *hud_democlock_style,\r\n        *hud_democlock_blink,\r\n\t\t*hud_democlock_scale;\r\n\r\n\tif (!cls.demoplayback || cls.mvdplayback == 2)\r\n\t{\r\n\t\tHUD_PrepareDraw(hud, width, height, &x, &y);\r\n\t\treturn;\r\n\t}\r\n\r\n    if (hud_democlock_big == NULL)    // first time\r\n    {\r\n        hud_democlock_big   = HUD_FindVar(hud, \"big\");\r\n        hud_democlock_style = HUD_FindVar(hud, \"style\");\r\n        hud_democlock_blink = HUD_FindVar(hud, \"blink\");\r\n\t\thud_democlock_scale = HUD_FindVar(hud, \"scale\");\r\n    }\r\n\r\n\tt = SCR_GetTimeString(TIMETYPE_DEMOCLOCK, NULL);\r\n\twidth = SCR_GetClockStringWidth(t, hud_democlock_big->ival, hud_democlock_scale->value);\r\n\theight = SCR_GetClockStringHeight(hud_democlock_big->ival, hud_democlock_scale->value);\r\n\r\n    if (HUD_PrepareDraw(hud, width, height, &x, &y))\r\n    {\r\n        if (hud_democlock_big->value)\r\n            SCR_DrawBigClock(x, y, hud_democlock_style->value, hud_democlock_blink->value, hud_democlock_scale->value, t);\r\n        else\r\n            SCR_DrawSmallClock(x, y, hud_democlock_style->value, hud_democlock_blink->value, hud_democlock_scale->value, t);\r\n\t}\r\n}\r\n\r\n//---------------------\r\n//\r\n// network statistics\r\n//\r\nstatic void SCR_NetStats(int x, int y, float period, plugnetinfo_t *netinfo)\r\n{\r\n    char line[128];\r\n    double t;\r\n\r\n    // static data\r\n    static double last_calculated;\r\n    static int    ping_min, ping_max, ping_avg;\r\n    static float  ping_dev;\r\n    static float  f_min, f_max, f_avg;\r\n    static int    lost_lost, lost_delta, lost_rate, lost_total;\r\n    static int    size_all, size_in, size_out, pps_in, pps_out;\r\n    static int    bandwidth_all, bandwidth_in, bandwidth_out;\r\n    static int    with_delta;\r\n\r\n    if (cls.state != ca_active)\r\n        return;\r\n\r\n    if (period < 0)\r\n        period = 0;\r\n\r\n    t = cls.realtime;\r\n    if (t - last_calculated > period)\r\n    {\r\n        // recalculate\r\n\r\n        last_calculated = t;\r\n\r\n        ping_avg = (int)(netinfo->ping.s_avg*1000 + 0.5);\r\n        ping_min = (int)(netinfo->ping.s_mn*1000 + 0.5);\r\n        ping_max = (int)(netinfo->ping.s_mx*1000 + 0.5);\r\n        ping_dev = netinfo->ping.ms_stddev;\r\n\r\n        clamp(ping_avg, 0, 999);\r\n        clamp(ping_min, 0, 999);\r\n        clamp(ping_max, 0, 999);\r\n        clamp(ping_dev, 0, 99.9);\r\n\r\n        f_avg = (int)(netinfo->ping.fr_avg+0.5);\r\n        f_min = netinfo->ping.fr_mn;\r\n        f_max = netinfo->ping.fr_mx;\r\n\r\n        clamp(f_avg, 0, 99);\r\n        clamp(f_min, 0, 99);\r\n        clamp(f_max, 0, 99);\r\n\r\n        lost_lost     = (int)(netinfo->loss.dropped*100  + 0.5);\r\n        lost_rate     = (int)(netinfo->loss.choked*100  + 0.5);\r\n        lost_delta    = (int)(netinfo->loss.invalid*100  + 0.5);\r\n        lost_total    = (int)((netinfo->loss.dropped + netinfo->loss.choked + netinfo->loss.invalid)*100 + 0.5);\r\n\r\n        clamp(lost_lost,  0, 100);\r\n        clamp(lost_rate,  0, 100);\r\n        clamp(lost_delta, 0, 100);\r\n        clamp(lost_total, 0, 100);\r\n\r\n\t\tpps_in = netinfo->clrate.in_pps;\r\n\t\tpps_out = netinfo->clrate.out_pps;\r\n\r\n\t\t//per packet sizes\r\n        size_in  = (int)(netinfo->clrate.in_bps/netinfo->clrate.in_pps + 0.5);\r\n        size_out = (int)(netinfo->clrate.out_bps/netinfo->clrate.out_pps + 0.5);\r\n        size_all = (int)(netinfo->clrate.in_bps/netinfo->clrate.in_pps + netinfo->clrate.out_bps/netinfo->clrate.out_pps + 0.5);\r\n\r\n\t\t//overall rate\r\n        bandwidth_in  = (int)(netinfo->clrate.in_bps  + 0.5);\r\n        bandwidth_out = (int)(netinfo->clrate.out_bps + 0.5);\r\n        bandwidth_all = (int)(netinfo->clrate.in_bps + netinfo->clrate.out_bps + 0.5);\r\n\r\n        clamp(size_in,  0, 999);\r\n        clamp(size_out, 0, 999);\r\n        clamp(size_all, 0, 999);\r\n        clamp(bandwidth_in,  0, 99999);\r\n        clamp(bandwidth_out, 0, 99999);\r\n        clamp(bandwidth_all, 0, 99999);\r\n\r\n        with_delta = !cvarfuncs->GetFloat(\"cl_nodelta\");\r\n    }\r\n\r\n    Draw_Alt_String(x+36, y, \"latency\");\r\n    y+=12;\r\n\r\n    snprintf (line, sizeof (line), \"min  %4f %3d ms\", f_min, ping_min);\r\n    Draw_String(x, y, line);\r\n    y+=8;\r\n\r\n    snprintf(line, sizeof (line), \"avg  %4f %3d ms\", f_avg, ping_avg);\r\n    Draw_String(x, y, line);\r\n    y+=8;\r\n\r\n    snprintf(line, sizeof (line), \"max  %4f %3d ms\", f_max, ping_max);\r\n    Draw_String(x, y, line);\r\n    y+=8;\r\n\r\n    snprintf(line, sizeof (line), \"dev     %f ms\", ping_dev);\r\n    Draw_String(x, y, line);\r\n    y+=12;\r\n\r\n    Draw_Alt_String(x+20, y, \"packet loss\");\r\n    y+=12;\r\n\r\n    snprintf(line, sizeof (line), \"lost       %3d %%\", lost_lost);\r\n    Draw_String(x, y, line);\r\n    y+=8;\r\n\r\n    snprintf(line, sizeof (line), \"rate cut   %3d %%\", lost_rate);\r\n    Draw_String(x, y, line);\r\n    y+=8;\r\n\r\n    if (with_delta)\r\n        snprintf(line, sizeof (line), \"bad delta  %3d %%\", lost_delta);\r\n    else\r\n        strlcpy (line, \"no delta compr\", sizeof (line));\r\n    Draw_String(x, y, line);\r\n    y+=8;\r\n\r\n    snprintf(line, sizeof (line), \"total      %3d %%\", lost_total);\r\n    Draw_String(x, y, line);\r\n    y+=12;\r\n\r\n\r\n    Draw_Alt_String(x+4, y, \"packet size/BPS\");\r\n    y+=12;\r\n\r\n    snprintf(line, sizeof (line), \"out    %3d %5d %d\", size_out, bandwidth_out, pps_out);\r\n    Draw_String(x, y, line);\r\n    y+=8;\r\n\r\n    snprintf(line, sizeof (line), \"in     %3d %5d %3d\", size_in, bandwidth_in, pps_in);\r\n    Draw_String(x, y, line);\r\n    y+=8;\r\n\r\n    snprintf(line, sizeof (line), \"total  %3d %5d %3d\", size_all, bandwidth_all, pps_in+pps_out);\r\n    Draw_String(x, y, line);\r\n    y+=8;\r\n}\r\n\r\nstatic void SCR_HUD_DrawNetStats(hud_t *hud)\r\n{\r\n    int width, height;\r\n    int x, y;\r\n\r\n\tplugnetinfo_t *netinfo = GetNetworkInfo();\r\n\r\n    static cvar_t *hud_net_period = NULL;\r\n\r\n    if (hud_net_period == NULL)    // first time\r\n    {\r\n        hud_net_period = HUD_FindVar(hud, \"period\");\r\n    }\r\n\r\n    width = 16*8 ;\r\n    height = 12 + 8 + 8 + 8 + 8 + 16 + 8 + 8 + 8 + 8 + 16 + 8 + 8 + 8;\r\n\r\n\tif (!netinfo || netinfo->capturing==2)\r\n\t\tHUD_PrepareDraw(hud, 0, 0, &x, &y);\r\n\telse if (HUD_PrepareDraw(hud, width, height, &x, &y))\r\n\t{\r\n        SCR_NetStats(x, y, hud_net_period->value, netinfo);\r\n\t}\r\n}\r\n\r\n#define SPEED_GREEN\t\t\t\t\"52\"\r\n#define SPEED_BROWN_RED\t\t\t\"100\"\r\n#define SPEED_DARK_RED\t\t\t\"72\"\r\n#define SPEED_BLUE\t\t\t\t\"216\"\r\n#define SPEED_RED\t\t\t\t\"229\"\r\n\r\n#define\tSPEED_STOPPED\t\t\tSPEED_GREEN\r\n#define\tSPEED_NORMAL\t\t\tSPEED_BROWN_RED\r\n#define\tSPEED_FAST\t\t\t\tSPEED_DARK_RED\r\n#define\tSPEED_FASTEST\t\t\tSPEED_BLUE\r\n#define\tSPEED_INSANE\t\t\tSPEED_RED\r\n\r\n//---------------------\r\n//\r\n// speed-o-meter\r\n//\r\n\r\n#define SPEED_TAG_LENGTH\t\t2\r\n#define SPEED_OUTLINE_SPACING\tSPEED_TAG_LENGTH\r\n#define SPEED_FILL_SPACING\t\tSPEED_OUTLINE_SPACING + 1\r\n#define SPEED_WHITE\t\t\t\t10\r\n#define SPEED_TEXT_ONLY\t\t\t1\r\n\r\n#define\tSPEED_TEXT_ALIGN_NONE\t0\r\n#define SPEED_TEXT_ALIGN_CLOSE\t1\r\n#define SPEED_TEXT_ALIGN_CENTER\t2\r\n#define SPEED_TEXT_ALIGN_FAR\t3\r\n\r\n// FIXME: hud-only now, can/should be moved\r\nvoid SCR_DrawHUDSpeed (\r\n\tint x, int y, int width, int height,\r\n\tint type,\r\n\tfloat tick_spacing,\r\n\tfloat opacity,\r\n\tint vertical,\r\n\tint vertical_text,\r\n\tint text_align,\r\n\tbyte color_stopped,\r\n\tbyte color_normal,\r\n\tbyte color_fast,\r\n\tbyte color_fastest,\r\n\tbyte color_insane,\r\n\tint style,\r\n\tfloat scale\r\n)\r\n{\r\n\tbyte color_offset;\r\n\tbyte color1, color2;\r\n\tint player_speed;\r\n\tvec_t *velocity;\r\n\r\n\tif (scr_con_current == vid.height) {\r\n\t\treturn;     // console is full screen\r\n\t}\r\n\r\n\t// Get the velocity.\r\n//\tif (cl.players[cl.playernum].spectator && Cam_TrackNum() >= 0) {\r\n//\t\tvelocity = cl.frames[cls.netchan.incoming_sequence & UPDATE_MASK].playerstate[Cam_TrackNum()].velocity;\r\n//\t}\r\n//\telse {\r\n\t\tvelocity = cl.simvel;\r\n//\t}\r\n\r\n\t// Calculate the speed\r\n\tif (!type)\r\n\t{\r\n\t\t// Based on XY.\r\n\t\tplayer_speed = sqrt(velocity[0]*velocity[0]\r\n\t\t\t\t\t\t  + velocity[1]*velocity[1]);\r\n\t}\r\n\telse\r\n\t{\r\n\t\t// Based on XYZ.\r\n\t\tplayer_speed = sqrt(velocity[0]*velocity[0]\r\n\t\t\t\t\t\t  + velocity[1]*velocity[1]\r\n\t\t\t\t\t\t  + velocity[2]*velocity[2]);\r\n\t}\r\n\r\n\t// Calculate the color offset for the \"background color\".\r\n\tif (vertical) {\r\n\t\tcolor_offset = height * (player_speed % 500) / 500;\r\n\t}\r\n\telse {\r\n\t\tcolor_offset = width * (player_speed % 500) / 500;\r\n\t}\r\n\r\n\t// Set the color based on the speed.\r\n\tswitch (player_speed / 500)\r\n\t{\r\n\t\tcase 0:\r\n\t\t\tcolor1 = color_stopped;\r\n\t\t\tcolor2 = color_normal;\r\n\t\t\tbreak;\r\n\t\tcase 1:\r\n\t\t\tcolor1 = color_normal;\r\n\t\t\tcolor2 = color_fast;\r\n\t\t\tbreak;\r\n\t\tcase 2:\r\n\t\t\tcolor1 = color_fast;\r\n\t\t\tcolor2 = color_fastest;\r\n\t\t\tbreak;\r\n\t\tdefault:\r\n\t\t\tcolor1 = color_fastest;\r\n\t\t\tcolor2 = color_insane;\r\n\t\t\tbreak;\r\n\t}\r\n\r\n\t// Draw tag marks.\r\n\tif (tick_spacing > 0.0 && style != SPEED_TEXT_ONLY)\r\n\t{\r\n\t\tfloat f;\r\n\r\n\t\tfor(f = tick_spacing; f < 1.0; f += tick_spacing)\r\n\t\t{\r\n\t\t\tif(vertical)\r\n\t\t\t{\r\n\t\t\t\t// Left.\r\n\t\t\t\tDraw_AlphaFill(x,\t\t\t\t// x\r\n\t\t\t\t\ty + (int)(f * height),\t// y\r\n\t\t\t\t\tSPEED_TAG_LENGTH,\t\t// Width\r\n\t\t\t\t\t1,\t\t\t\t\t\t// Height\r\n\t\t\t\t\tSPEED_WHITE,\t\t\t// Color\r\n\t\t\t\t\topacity);\t\t\t\t// Opacity\r\n\r\n\t\t\t\t// Right.\r\n\t\t\t\tDraw_AlphaFill(x + width - SPEED_TAG_LENGTH + 1,\r\n\t\t\t\t\ty + (int)(f * height),\r\n\t\t\t\t\tSPEED_TAG_LENGTH,\r\n\t\t\t\t\t1,\r\n\t\t\t\t\tSPEED_WHITE,\r\n\t\t\t\t\topacity);\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\t// Above.\r\n\t\t\t\tDraw_AlphaFill(x + (int)(f * width),\r\n\t\t\t\t\ty,\r\n\t\t\t\t\t1,\r\n\t\t\t\t\tSPEED_TAG_LENGTH,\r\n\t\t\t\t\tSPEED_WHITE,\r\n\t\t\t\t\topacity);\r\n\r\n\t\t\t\t// Below.\r\n\t\t\t\tDraw_AlphaFill(x + (int)(f * width),\r\n\t\t\t\t\ty + height - SPEED_TAG_LENGTH + 1,\r\n\t\t\t\t\t1,\r\n\t\t\t\t\tSPEED_TAG_LENGTH,\r\n\t\t\t\t\tSPEED_WHITE,\r\n\t\t\t\t\topacity);\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\t//\r\n\t// Draw outline.\r\n\t//\r\n\tif (style != SPEED_TEXT_ONLY)\r\n\t{\r\n\t\tif(vertical)\r\n\t\t{\r\n\t\t\t// Left.\r\n\t\t\tDraw_AlphaFill(x + SPEED_OUTLINE_SPACING,\r\n\t\t\t\ty,\r\n\t\t\t\t1,\r\n\t\t\t\theight,\r\n\t\t\t\tSPEED_WHITE,\r\n\t\t\t\topacity);\r\n\r\n\t\t\t// Right.\r\n\t\t\tDraw_AlphaFill(x + width - SPEED_OUTLINE_SPACING,\r\n\t\t\t\ty,\r\n\t\t\t\t1,\r\n\t\t\t\theight,\r\n\t\t\t\tSPEED_WHITE,\r\n\t\t\t\topacity);\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\t// Above.\r\n\t\t\tDraw_AlphaFill(x,\r\n\t\t\t\ty + SPEED_OUTLINE_SPACING,\r\n\t\t\t\twidth,\r\n\t\t\t\t1,\r\n\t\t\t\tSPEED_WHITE,\r\n\t\t\t\topacity);\r\n\r\n\t\t\t// Below.\r\n\t\t\tDraw_AlphaFill(x,\r\n\t\t\t\ty + height - SPEED_OUTLINE_SPACING,\r\n\t\t\t\twidth,\r\n\t\t\t\t1,\r\n\t\t\t\tSPEED_WHITE,\r\n\t\t\t\topacity);\r\n\t\t}\r\n\t}\r\n\r\n\t//\r\n\t// Draw fill.\r\n\t//\r\n\tif (style != SPEED_TEXT_ONLY)\r\n\t{\r\n\t\tif(vertical)\r\n\t\t{\r\n\t\t\t// Draw the right color (slower).\r\n\t\t\tDraw_AlphaFill (x + SPEED_FILL_SPACING,\r\n\t\t\t\ty,\r\n\t\t\t\twidth - (2 * SPEED_FILL_SPACING),\r\n\t\t\t\theight - color_offset,\r\n\t\t\t\tcolor1,\r\n\t\t\t\topacity);\r\n\r\n\t\t\t// Draw the left color (faster).\r\n\t\t\tDraw_AlphaFill (x + SPEED_FILL_SPACING,\r\n\t\t\t\ty + height - color_offset,\r\n\t\t\t\twidth - (2 * SPEED_FILL_SPACING),\r\n\t\t\t\tcolor_offset,\r\n\t\t\t\tcolor2,\r\n\t\t\t\topacity);\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\t// Draw the right color (slower).\r\n\t\t\tDraw_AlphaFill (x + color_offset,\r\n\t\t\t\ty + SPEED_FILL_SPACING,\r\n\t\t\t\twidth - color_offset,\r\n\t\t\t\theight - (2 * SPEED_FILL_SPACING),\r\n\t\t\t\tcolor1,\r\n\t\t\t\topacity);\r\n\r\n\t\t\t// Draw the left color (faster).\r\n\t\t\tDraw_AlphaFill (x,\r\n\t\t\t\ty + SPEED_FILL_SPACING,\r\n\t\t\t\tcolor_offset,\r\n\t\t\t\theight - (2 * SPEED_FILL_SPACING),\r\n\t\t\t\tcolor2,\r\n\t\t\t\topacity);\r\n\t\t}\r\n\t}\r\n\r\n\t// Draw the speed text.\r\n\tif(vertical && vertical_text)\r\n\t{\r\n\t\tint i = 1;\r\n\t\tint len = 0;\r\n\r\n\t\t// Align the text accordingly.\r\n\t\tswitch(text_align)\r\n\t\t{\r\n\t\t\tcase SPEED_TEXT_ALIGN_NONE:\t\treturn;\r\n\t\t\tcase SPEED_TEXT_ALIGN_FAR:\t\ty = y + height - 4*8; break;\r\n\t\t\tcase SPEED_TEXT_ALIGN_CENTER:\ty = Q_rint(y + height/2.0 - 16); break;\r\n\t\t\tcase SPEED_TEXT_ALIGN_CLOSE:\r\n\t\t\tdefault: break;\r\n\t\t}\r\n\r\n\t\tlen = strlen(va(\"%d\", player_speed));\r\n\r\n\t\t// 10^len\r\n\t\twhile(len > 0)\r\n\t\t{\r\n\t\t\ti *= 10;\r\n\t\t\tlen--;\r\n\t\t}\r\n\r\n\t\t// Write one number per row.\r\n\t\tfor(; i > 1; i /= 10)\r\n\t\t{\r\n\t\t\tint next;\r\n\t\t\tnext = (i/10);\r\n\r\n\t\t\t// Really make sure we don't try division by zero :)\r\n\t\t\tif(next <= 0)\r\n\t\t\t{\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\r\n\t\t\tDraw_SString(Q_rint(x + width/2.0 - 4 * scale), y, va(\"%1d\", (player_speed % i) / next), scale);\r\n\t\t\ty += 8;\r\n\t\t}\r\n\t}\r\n\telse\r\n\t{\r\n\t\t// Align the text accordingly.\r\n\t\tswitch(text_align)\r\n\t\t{\r\n\t\t\tcase SPEED_TEXT_ALIGN_FAR:\r\n\t\t\t\tx = x + width - 4 * 8 * scale;\r\n\t\t\t\tbreak;\r\n\t\t\tcase SPEED_TEXT_ALIGN_CENTER:\r\n\t\t\t\tx = Q_rint(x + width / 2.0 - 2 * 8 * scale);\r\n\t\t\t\tbreak;\r\n\t\t\tcase SPEED_TEXT_ALIGN_CLOSE:\r\n\t\t\tcase SPEED_TEXT_ALIGN_NONE:\r\n\t\t\tdefault:\r\n\t\t\t\tbreak;\r\n\t\t}\r\n\r\n\t\tDraw_SString(x, Q_rint(y + height/2.0 - 4 * scale), va(\"%4d\", player_speed), scale);\r\n\t}\r\n}\r\n\r\nstatic void SCR_HUD_DrawSpeed(hud_t *hud)\r\n{\r\n    int width, height;\r\n    int x, y;\r\n\r\n    static cvar_t *hud_speed_xyz = NULL,\r\n\t\t*hud_speed_width,\r\n        *hud_speed_height,\r\n\t\t*hud_speed_tick_spacing,\r\n\t\t*hud_speed_opacity,\r\n\t\t*hud_speed_color_stopped,\r\n\t\t*hud_speed_color_normal,\r\n\t\t*hud_speed_color_fast,\r\n\t\t*hud_speed_color_fastest,\r\n\t\t*hud_speed_color_insane,\r\n\t\t*hud_speed_vertical,\r\n\t\t*hud_speed_vertical_text,\r\n\t\t*hud_speed_text_align,\r\n\t\t*hud_speed_style,\r\n\t\t*hud_speed_scale;\r\n\r\n    if (hud_speed_xyz == NULL)    // first time\r\n    {\r\n        hud_speed_xyz\t\t\t= HUD_FindVar(hud, \"xyz\");\r\n\t\thud_speed_width\t\t\t= HUD_FindVar(hud, \"width\");\r\n\t\thud_speed_height\t\t= HUD_FindVar(hud, \"height\");\r\n\t\thud_speed_tick_spacing\t= HUD_FindVar(hud, \"tick_spacing\");\r\n\t\thud_speed_opacity\t\t= HUD_FindVar(hud, \"opacity\");\r\n\t\thud_speed_color_stopped\t= HUD_FindVar(hud, \"color_stopped\");\r\n\t\thud_speed_color_normal\t= HUD_FindVar(hud, \"color_normal\");\r\n\t\thud_speed_color_fast\t= HUD_FindVar(hud, \"color_fast\");\r\n\t\thud_speed_color_fastest\t= HUD_FindVar(hud, \"color_fastest\");\r\n\t\thud_speed_color_insane\t= HUD_FindVar(hud, \"color_insane\");\r\n\t\thud_speed_vertical\t\t= HUD_FindVar(hud, \"vertical\");\r\n\t\thud_speed_vertical_text\t= HUD_FindVar(hud, \"vertical_text\");\r\n\t\thud_speed_text_align\t= HUD_FindVar(hud, \"text_align\");\r\n\t\thud_speed_style\t\t\t= HUD_FindVar(hud, \"style\");\r\n\t\thud_speed_scale\t\t\t= HUD_FindVar(hud, \"scale\");\r\n    }\r\n\r\n\twidth = max(0, hud_speed_width->value);\r\n\theight = max(0, hud_speed_height->value);\r\n\r\n    if (HUD_PrepareDraw(hud, width, height, &x, &y))\r\n\t{\r\n\t\tSCR_DrawHUDSpeed(x, y, width, height,\r\n\t\t\thud_speed_xyz->value,\r\n\t\t\thud_speed_tick_spacing->value,\r\n\t\t\thud_speed_opacity->value,\r\n\t\t\thud_speed_vertical->value,\r\n\t\t\thud_speed_vertical_text->value,\r\n\t\t\thud_speed_text_align->value,\r\n\t\t\thud_speed_color_stopped->value,\r\n\t\t\thud_speed_color_normal->value,\r\n\t\t\thud_speed_color_fast->value,\r\n\t\t\thud_speed_color_fastest->value,\r\n\t\t\thud_speed_color_insane->value,\r\n\t\t\thud_speed_style->ival,\r\n\t\t\thud_speed_scale->value);\r\n\t}\r\n}\r\n\r\n#define\tHUD_SPEED2_ORIENTATION_UP\t\t0\r\n#define\tHUD_SPEED2_ORIENTATION_DOWN\t\t1\r\n#define\tHUD_SPEED2_ORIENTATION_RIGHT\t2\r\n#define\tHUD_SPEED2_ORIENTATION_LEFT\t\t3\r\n\r\nvoid SCR_HUD_DrawSpeed2(hud_t *hud)\r\n{\r\n\tint width, height;\r\n    int x, y;\r\n\r\n    static cvar_t *hud_speed2_xyz = NULL,\r\n//\t\t*hud_speed2_opacity,\r\n\t\t*hud_speed2_color_stopped,\r\n\t\t*hud_speed2_color_normal,\r\n\t\t*hud_speed2_color_fast,\r\n\t\t*hud_speed2_color_fastest,\r\n\t\t*hud_speed2_color_insane,\r\n\t\t*hud_speed2_radius,\r\n\t\t*hud_speed2_wrapspeed,\r\n\t\t*hud_speed2_orientation;\r\n\r\n    if (hud_speed2_xyz == NULL)    // first time\r\n    {\r\n        hud_speed2_xyz\t\t\t\t= HUD_FindVar(hud, \"xyz\");\r\n//\t\thud_speed2_opacity\t\t\t= HUD_FindVar(hud, \"opacity\");\r\n\t\thud_speed2_color_stopped\t= HUD_FindVar(hud, \"color_stopped\");\r\n\t\thud_speed2_color_normal\t\t= HUD_FindVar(hud, \"color_normal\");\r\n\t\thud_speed2_color_fast\t\t= HUD_FindVar(hud, \"color_fast\");\r\n\t\thud_speed2_color_fastest\t= HUD_FindVar(hud, \"color_fastest\");\r\n\t\thud_speed2_color_insane\t\t= HUD_FindVar(hud, \"color_insane\");\r\n\t\thud_speed2_radius\t\t\t= HUD_FindVar(hud, \"radius\");\r\n\t\thud_speed2_wrapspeed\t\t= HUD_FindVar(hud, \"wrapspeed\");\r\n\t\thud_speed2_orientation\t\t= HUD_FindVar(hud, \"orientation\");\r\n    }\r\n\r\n\t// Calculate the height and width based on the radius.\r\n\tswitch((int)hud_speed2_orientation->value)\r\n\t{\r\n\t\tcase HUD_SPEED2_ORIENTATION_LEFT :\r\n\t\tcase HUD_SPEED2_ORIENTATION_RIGHT :\r\n\t\t\theight = max(0, 2*hud_speed2_radius->value);\r\n\t\t\twidth = max(0, (hud_speed2_radius->value));\r\n\t\t\tbreak;\r\n\t\tcase HUD_SPEED2_ORIENTATION_DOWN :\r\n\t\tcase HUD_SPEED2_ORIENTATION_UP :\r\n\t\tdefault :\r\n\t\t\t// Include the height of the speed text in the height.\r\n\t\t\theight = max(0, (hud_speed2_radius->value));\r\n\t\t\twidth = max(0, 2*hud_speed2_radius->value);\r\n\t\t\tbreak;\r\n\t}\r\n\r\n    if (HUD_PrepareDraw(hud, width, height, &x, &y))\r\n\t{\r\n\t\tint player_speed;\r\n\t\tint arc_length;\r\n\t\tint color1, color2;\r\n\t\tint text_x = x;\r\n\t\tint text_y = y;\r\n\t\tvec_t *velocity;\r\n\r\n\t\t// Start and end points for the needle\r\n\t\tint needle_start_x = 0;\r\n\t\tint needle_start_y = 0;\r\n\t\tint needle_end_x = 0;\r\n\t\tint needle_end_y = 0;\r\n\r\n\t\t// The length of the arc between the zero point\r\n\t\t// and where the needle is pointing at.\r\n\t\tint needle_offset = 0;\r\n\r\n\t\t// The angle between the zero point and the position\r\n\t\t// that the needle is drawn on.\r\n\t\tfloat needle_angle = 0.0;\r\n\r\n\t\t// The angle where to start drawing the half circle and where to end.\r\n\t\t// This depends on the orientation of the circle (left, right, up, down).\r\n\t\tfloat circle_startangle = 0.0;\r\n\t\tfloat circle_endangle = 0.0;\r\n\r\n\t\t// Avoid divison by zero.\r\n\t\tif(hud_speed2_radius->value <= 0)\r\n\t\t{\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\t// Get the velocity.\r\n#ifdef HAXX\r\n\t\tif (cl.players[cl.playernum].spectator && Cam_TrackNum() >= 0)\r\n\t\t{\r\n\t\t\tvelocity = cl.frames[cls.netchan.incoming_sequence & UPDATE_MASK].playerstate[Cam_TrackNum()].velocity;\r\n\t\t}\r\n\t\telse\r\n#endif\r\n\t\t{\r\n\t\t\tvelocity = cl.simvel;\r\n\t\t}\r\n\r\n\t\t// Calculate the speed\r\n\t\tif (!hud_speed2_xyz->value)\r\n\t\t{\r\n\t\t\t// Based on XY.\r\n\t\t\tplayer_speed = sqrt(velocity[0]*velocity[0]\r\n\t\t\t\t\t\t\t  + velocity[1]*velocity[1]);\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\t// Based on XYZ.\r\n\t\t\tplayer_speed = sqrt(velocity[0]*velocity[0]\r\n\t\t\t\t\t\t\t  + velocity[1]*velocity[1]\r\n\t\t\t\t\t\t\t  + velocity[2]*velocity[2]);\r\n\t\t}\r\n\r\n\t\t// Set the color based on the wrap speed.\r\n\t\tswitch ((int)(player_speed / hud_speed2_wrapspeed->value))\r\n\t\t{\r\n\t\t\tcase 0:\r\n\t\t\t\tcolor1 = hud_speed2_color_stopped->ival;\r\n\t\t\t\tcolor2 = hud_speed2_color_normal->ival;\r\n\t\t\t\tbreak;\r\n\t\t\tcase 1:\r\n\t\t\t\tcolor1 = hud_speed2_color_normal->ival;\r\n\t\t\t\tcolor2 = hud_speed2_color_fast->ival;\r\n\t\t\t\tbreak;\r\n\t\t\tcase 2:\r\n\t\t\t\tcolor1 = hud_speed2_color_fast->ival;\r\n\t\t\t\tcolor2 = hud_speed2_color_fastest->ival;\r\n\t\t\t\tbreak;\r\n\t\t\tdefault:\r\n\t\t\t\tcolor1 = hud_speed2_color_fastest->ival;\r\n\t\t\t\tcolor2 = hud_speed2_color_insane->ival;\r\n\t\t\t\tbreak;\r\n\t\t}\r\n\r\n\t\t// Set some properties how to draw the half circle, needle and text\r\n\t\t// based on the orientation of the hud item.\r\n\t\tswitch((int)hud_speed2_orientation->value)\r\n\t\t{\r\n\t\t\tcase HUD_SPEED2_ORIENTATION_LEFT :\r\n\t\t\t{\r\n\t\t\t\tx += width;\r\n\t\t\t\ty += height / 2;\r\n\t\t\t\tcircle_startangle = M_PI / 2.0;\r\n\t\t\t\tcircle_endangle\t= (3*M_PI) / 2.0;\r\n\r\n\t\t\t\ttext_x = x - 32;\r\n\t\t\t\ttext_y = y - 4;\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\t\t\tcase HUD_SPEED2_ORIENTATION_RIGHT :\r\n\t\t\t{\r\n\t\t\t\ty += height / 2;\r\n\t\t\t\tcircle_startangle = (3*M_PI) / 2.0;\r\n\t\t\t\tcircle_endangle = (5*M_PI) / 2.0;\r\n\t\t\t\tneedle_end_y = y + hud_speed2_radius->value * sin (needle_angle);\r\n\r\n\t\t\t\ttext_x = x;\r\n\t\t\t\ttext_y = y - 4;\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\t\t\tcase HUD_SPEED2_ORIENTATION_DOWN :\r\n\t\t\t{\r\n\t\t\t\tx += width / 2;\r\n\t\t\t\tcircle_startangle = M_PI;\r\n\t\t\t\tcircle_endangle = 2*M_PI;\r\n\t\t\t\tneedle_end_y = y + hud_speed2_radius->value * sin (needle_angle);\r\n\r\n\t\t\t\ttext_x = x - 16;\r\n\t\t\t\ttext_y = y;\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\t\t\tcase HUD_SPEED2_ORIENTATION_UP :\r\n\t\t\tdefault :\r\n\t\t\t{\r\n\t\t\t\tx += width / 2;\r\n\t\t\t\ty += height;\r\n\t\t\t\tcircle_startangle = 0;\r\n\t\t\t\tcircle_endangle = M_PI;\r\n\t\t\t\tneedle_end_y = y - hud_speed2_radius->value * sin (needle_angle);\r\n\r\n\t\t\t\ttext_x = x - 16;\r\n\t\t\t\ttext_y = y - 8;\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t//\r\n\t\t// Calculate the offsets and angles.\r\n\t\t//\r\n\t\t{\r\n\t\t\t// Calculate the arc length of the half circle background.\r\n\t\t\tarc_length = fabs((circle_endangle - circle_startangle) * hud_speed2_radius->value);\r\n\r\n\t\t\t// Calculate the angle where the speed needle should point.\r\n\t\t\tneedle_offset = arc_length * (player_speed % Q_rint(hud_speed2_wrapspeed->value)) / Q_rint(hud_speed2_wrapspeed->value);\r\n\t\t\tneedle_angle = needle_offset / hud_speed2_radius->value;\r\n\r\n\t\t\t// Draw from the center of the half circle.\r\n\t\t\tneedle_start_x = x;\r\n\t\t\tneedle_start_y = y;\r\n\t\t}\r\n\r\n\t\t// Set the needle end point depending on the orientation of the hud item.\r\n\r\n\t\tswitch((int)hud_speed2_orientation->value)\r\n\t\t{\r\n\t\t\tcase HUD_SPEED2_ORIENTATION_LEFT :\r\n\t\t\t{\r\n\t\t\t\tneedle_end_x = x - hud_speed2_radius->value * sin (needle_angle);\r\n\t\t\t\tneedle_end_y = y + hud_speed2_radius->value * cos (needle_angle);\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\t\t\tcase HUD_SPEED2_ORIENTATION_RIGHT :\r\n\t\t\t{\r\n\t\t\t\tneedle_end_x = x + hud_speed2_radius->value * sin (needle_angle);\r\n\t\t\t\tneedle_end_y = y - hud_speed2_radius->value * cos (needle_angle);\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\t\t\tcase HUD_SPEED2_ORIENTATION_DOWN :\r\n\t\t\t{\r\n\t\t\t\tneedle_end_x = x + hud_speed2_radius->value * cos (needle_angle);\r\n\t\t\t\tneedle_end_y = y + hud_speed2_radius->value * sin (needle_angle);\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\t\t\tcase HUD_SPEED2_ORIENTATION_UP :\r\n\t\t\tdefault :\r\n\t\t\t{\r\n\t\t\t\tneedle_end_x = x - hud_speed2_radius->value * cos (needle_angle);\r\n\t\t\t\tneedle_end_y = y - hud_speed2_radius->value * sin (needle_angle);\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\t\t}\r\n\r\n#ifdef HAXX\r\n\t\t// Draw the speed-o-meter background.\r\n\t\tDraw_AlphaPieSlice (x, y,\t\t\t\t// Position\r\n\t\t\thud_speed2_radius->value,\t\t\t// Radius\r\n\t\t\tcircle_startangle,\t\t\t\t\t// Start angle\r\n\t\t\tcircle_endangle - needle_angle,\t\t// End angle\r\n\t\t\t1,\t\t\t\t\t\t\t\t\t// Thickness\r\n\t\t\ttrue,\t\t\t\t\t\t\t\t// Fill\r\n\t\t\tcolor1,\t\t\t\t\t\t\t\t// Color\r\n\t\t\thud_speed2_opacity->value);\t\t\t// Opacity\r\n\r\n\t\t// Draw a pie slice that shows the \"color\" of the speed.\r\n\t\tDraw_AlphaPieSlice (x, y,\t\t\t\t// Position\r\n\t\t\thud_speed2_radius->value,\t\t\t// Radius\r\n\t\t\tcircle_endangle - needle_angle,\t\t// Start angle\r\n\t\t\tcircle_endangle,\t\t\t\t\t// End angle\r\n\t\t\t1,\t\t\t\t\t\t\t\t\t// Thickness\r\n\t\t\ttrue,\t\t\t\t\t\t\t\t// Fill\r\n\t\t\tcolor2,\t\t\t\t\t\t\t\t// Color\r\n\t\t\thud_speed2_opacity->value);\t\t\t// Opacity\r\n\r\n\t\t// Draw the \"needle attachment\" circle.\r\n\t\tDraw_AlphaCircle (x, y, 2.0, 1, true, 15, hud_speed2_opacity->value);\r\n\r\n\t\t// Draw the speed needle.\r\n\t\tDraw_AlphaLineRGB (needle_start_x, needle_start_y, needle_end_x, needle_end_y, 1, RGBA_TO_COLOR(250, 250, 250, 255 * hud_speed2_opacity->value));\r\n#else\r\n\t\t(void)color1;\r\n\t\t(void)color2;\r\n\t\t(void)needle_start_x;\r\n\t\t(void)needle_start_y;\r\n\t\t(void)needle_end_x;\r\n\t\t(void)needle_end_y;\r\n#endif\r\n\r\n\t\t// Draw the speed.\r\n\t\tDraw_String (text_x, text_y, va(\"%d\", player_speed));\r\n\t}\r\n}\r\n\r\n// =======================================================\r\n//\r\n//  s t a t u s   b a r   e l e m e n t s\r\n//\r\n//\r\n\r\n\r\n// -----------\r\n// gunz\r\n//\r\nvoid SCR_HUD_DrawGunByNum (hud_t *hud, int num, float scale, int style, int wide)\r\n{\r\n    int i = num - 2;\r\n    int width, height;\r\n    int x, y;\r\n    char *tmp;\r\n\r\n    scale = max(scale, 0.01);\r\n\r\n    switch (style)\r\n    {\r\n\tcase 3: // opposite colors of case 1\r\n    case 1:     // text, gold inactive, white active\r\n        width = 16 * scale;\r\n        height = 8 * scale;\r\n        if (!HUD_PrepareDraw(hud, width, height, &x, &y))\r\n            return;\r\n        if ( HUD_Stats(STAT_ITEMS) & (IT_SHOTGUN<<i) )\r\n        {\r\n            switch (num)\r\n            {\r\n            case 2: tmp = \"sg\"; break;\r\n            case 3: tmp = \"bs\"; break;\r\n            case 4: tmp = \"ng\"; break;\r\n            case 5: tmp = \"sn\"; break;\r\n            case 6: tmp = \"gl\"; break;\r\n            case 7: tmp = \"rl\"; break;\r\n            case 8: tmp = \"lg\"; break;\r\n            default: tmp = \"\";\r\n            }\r\n\r\n            if ( ((HUD_Stats(STAT_ACTIVEWEAPON) == (IT_SHOTGUN<<i)) && (style==1)) ||\r\n\t\t\t\t ((HUD_Stats(STAT_ACTIVEWEAPON) != (IT_SHOTGUN<<i)) && (style==3))\r\n\t\t\t   )\r\n                Draw_SString(x, y, tmp, scale);\r\n            else\r\n                Draw_SAlt_String(x, y, tmp, scale);\r\n        }\r\n        break;\r\n\tcase 4: // opposite colors of case 2\r\n    case 2:     // numbers, gold inactive, white active\r\n        width = 8 * scale;\r\n        height = 8 * scale;\r\n        if (!HUD_PrepareDraw(hud, width, height, &x, &y))\r\n            return;\r\n        if ( HUD_Stats(STAT_ITEMS) & (IT_SHOTGUN<<i) )\r\n        {\r\n            if ( HUD_Stats(STAT_ACTIVEWEAPON) == (IT_SHOTGUN<<i) )\r\n\t\t\t\tnum += '0' + (style == 4 ? 128 : 0);\r\n            else\r\n\t\t\t\tnum += '0' + (style == 4 ? 0 : 128);\r\n            Draw_SCharacter(x, y, num, scale);\r\n        }\r\n        break;\r\n\tcase 5: // COLOR active, gold inactive\r\n\tcase 7: // COLOR active, white inactive\r\n\tcase 6: // white active, COLOR inactive\r\n\tcase 8: // gold active, COLOR inactive\r\n        width = 16 * scale;\r\n        height = 8 * scale;\r\n       \r\n\t\tif (!HUD_PrepareDraw(hud, width, height, &x, &y))\r\n            return;\r\n\r\n        if ( HUD_Stats(STAT_ITEMS) & (IT_SHOTGUN<<i) ) {\r\n\t\t\tif ( HUD_Stats(STAT_ACTIVEWEAPON) == (IT_SHOTGUN<<i) ) {\r\n\t\t\t\tif ((style==5) || (style==7)) { // strip {}\r\n\t\t\t\t\tchar *weap_str = TP_ItemName((IT_SHOTGUN<<i));\r\n\t\t\t\t\tchar weap_white_stripped[32];\r\n\t\t\t\t\tUtil_SkipChars(weap_str, \"{}\", weap_white_stripped, 32);\r\n\t\t\t\t\tDraw_SString(x, y, weap_white_stripped, scale);\r\n\t\t\t\t}\r\n\t\t\t\telse { //Strip both &cRGB and {}\r\n\t\t\t\t\tchar inactive_weapon_buf[16];\r\n\t\t\t\t\tchar inactive_weapon_buf_nowhite[16];\r\n\t\t\t\t\tUtil_SkipEZColors(inactive_weapon_buf, TP_ItemName(IT_SHOTGUN<<i), sizeof(inactive_weapon_buf));\r\n\t\t\t\t\tUtil_SkipChars(inactive_weapon_buf, \"{}\", inactive_weapon_buf_nowhite, sizeof(inactive_weapon_buf_nowhite));\r\n\r\n\t\t\t\t\tif (style==8) // gold active\r\n\t\t\t\t\t\tDraw_SAlt_String(x, y, inactive_weapon_buf_nowhite, scale);\r\n\t\t\t\t\telse if (style==6) // white active\r\n\t\t\t\t\t\tDraw_SString(x, y, inactive_weapon_buf_nowhite, scale);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\telse {\r\n\t\t\t\tif ((style==5) || (style==7)) { //Strip both &cRGB and {}\r\n\t\t\t\t\tchar inactive_weapon_buf[16];\r\n\t\t\t\t\tchar inactive_weapon_buf_nowhite[16];\r\n\t\t\t\t\tUtil_SkipEZColors(inactive_weapon_buf, TP_ItemName(IT_SHOTGUN<<i), sizeof(inactive_weapon_buf));\r\n\t\t\t\t\tUtil_SkipChars(inactive_weapon_buf, \"{}\", inactive_weapon_buf_nowhite, sizeof(inactive_weapon_buf_nowhite));\r\n\t\t\t\t\r\n\t\t\t\t\tif (style==5) // gold inactive\r\n\t\t\t\t\t\tDraw_SAlt_String(x, y, inactive_weapon_buf_nowhite, scale);\r\n\t\t\t\t\telse if (style==7) // white inactive\r\n\t\t\t\t\t\tDraw_SString(x, y, inactive_weapon_buf_nowhite, scale);\r\n\t\t\t\t}\r\n\t\t\t\telse if ((style==6) || (style==8)) { // strip only {}\r\n\t\t\t\t\tchar *weap_str = TP_ItemName((IT_SHOTGUN<<i));\r\n\t\t\t\t\tchar weap_white_stripped[32];\r\n\t\t\t\t\tUtil_SkipChars(weap_str, \"{}\", weap_white_stripped, 32);\r\n\t\t\t\t\tDraw_SString(x, y, weap_white_stripped, scale);\r\n\t\t\t\t}\r\n\r\n\t\t\t}\r\n        }\r\n        break;\r\n    default:    // classic - pictures\r\n        width  = scale * (wide ? 48 : 24);\r\n        height = scale * 16;\r\n\r\n        if (!HUD_PrepareDraw(hud, width, height, &x, &y))\r\n            return;\r\n\r\n        if ( HUD_Stats(STAT_ITEMS) & (IT_SHOTGUN<<i) )\r\n        {\r\n            float   time;\r\n            int     flashon;\r\n\r\n            time = cl.item_gettime[i];\r\n            flashon = (int)((cl.time - time)*10);\r\n            if (flashon < 0)\r\n                flashon = 0;\r\n            if (flashon >= 10)\r\n            {\r\n                if ( HUD_Stats(STAT_ACTIVEWEAPON) == (IT_SHOTGUN<<i) )\r\n                    flashon = 1;\r\n                else\r\n                    flashon = 0;\r\n            }\r\n            else\r\n                flashon = (flashon%5) + 2;\r\n\r\n            if (wide  ||  num != 8)\r\n                Draw_SPic (x, y, sb_weapons[flashon][i], scale);\r\n            else\r\n                Draw_SSubPic (x, y, sb_weapons[flashon][i], 0, 0, 24, 16, scale);\r\n        }\r\n        break;\r\n    }\r\n}\r\n\r\nvoid SCR_HUD_DrawGun2 (hud_t *hud)\r\n{\r\n    static cvar_t *scale = NULL, *style;\r\n    if (scale == NULL)  // first time callse\r\n    {\r\n        scale = HUD_FindVar(hud, \"scale\");\r\n        style = HUD_FindVar(hud, \"style\");\r\n    }\r\n    SCR_HUD_DrawGunByNum (hud, 2, scale->value, style->value, 0);\r\n}\r\nvoid SCR_HUD_DrawGun3 (hud_t *hud)\r\n{\r\n    static cvar_t *scale = NULL, *style;\r\n    if (scale == NULL)  // first time called\r\n    {\r\n        scale = HUD_FindVar(hud, \"scale\");\r\n        style = HUD_FindVar(hud, \"style\");\r\n    }\r\n    SCR_HUD_DrawGunByNum (hud, 3, scale->value, style->value, 0);\r\n}\r\nvoid SCR_HUD_DrawGun4 (hud_t *hud)\r\n{\r\n    static cvar_t *scale = NULL, *style;\r\n    if (scale == NULL)  // first time called\r\n    {\r\n        scale = HUD_FindVar(hud, \"scale\");\r\n        style = HUD_FindVar(hud, \"style\");\r\n    }\r\n    SCR_HUD_DrawGunByNum (hud, 4, scale->value, style->value, 0);\r\n}\r\nvoid SCR_HUD_DrawGun5 (hud_t *hud)\r\n{\r\n    static cvar_t *scale = NULL, *style;\r\n    if (scale == NULL)  // first time called\r\n    {\r\n        scale = HUD_FindVar(hud, \"scale\");\r\n        style = HUD_FindVar(hud, \"style\");\r\n    }\r\n    SCR_HUD_DrawGunByNum (hud, 5, scale->value, style->value, 0);\r\n}\r\nvoid SCR_HUD_DrawGun6 (hud_t *hud)\r\n{\r\n    static cvar_t *scale = NULL, *style;\r\n    if (scale == NULL)  // first time called\r\n    {\r\n        scale = HUD_FindVar(hud, \"scale\");\r\n        style = HUD_FindVar(hud, \"style\");\r\n    }\r\n    SCR_HUD_DrawGunByNum (hud, 6, scale->value, style->value, 0);\r\n}\r\nvoid SCR_HUD_DrawGun7 (hud_t *hud)\r\n{\r\n    static cvar_t *scale = NULL, *style;\r\n    if (scale == NULL)  // first time called\r\n    {\r\n        scale = HUD_FindVar(hud, \"scale\");\r\n        style = HUD_FindVar(hud, \"style\");\r\n    }\r\n    SCR_HUD_DrawGunByNum (hud, 7, scale->value, style->value, 0);\r\n}\r\nvoid SCR_HUD_DrawGun8 (hud_t *hud)\r\n{\r\n    static cvar_t *scale = NULL, *style, *wide;\r\n    if (scale == NULL)  // first time called\r\n    {\r\n        scale = HUD_FindVar(hud, \"scale\");\r\n        style = HUD_FindVar(hud, \"style\");\r\n        wide  = HUD_FindVar(hud, \"wide\");\r\n    }\r\n    SCR_HUD_DrawGunByNum (hud, 8, scale->value, style->value, wide->value);\r\n}\r\nvoid SCR_HUD_DrawGunCurrent (hud_t *hud)\r\n{\r\n    int gun;\r\n    static cvar_t *scale = NULL, *style, *wide;\r\n\r\n    if (scale == NULL)  // first time called\r\n    {\r\n        scale = HUD_FindVar(hud, \"scale\");\r\n        style = HUD_FindVar(hud, \"style\");\r\n        wide  = HUD_FindVar(hud, \"wide\");\r\n    }\r\n\r\n\tif (ShowPreselectedWeap()) {\r\n\t// using weapon pre-selection so show info for current best pre-selected weapon\r\n\t\tgun = IN_BestWeapon();\r\n\t\tif (gun < 2) {\r\n\t\t\treturn;\r\n\t\t}\r\n\t} else {\r\n\t// not using weapon pre-selection or player is dead so show current selected weapon\r\n\t\tswitch (HUD_Stats(STAT_ACTIVEWEAPON))\r\n\t\t{\r\n\t\t\tcase IT_SHOTGUN << 0:   gun = 2; break;\r\n\t\t\tcase IT_SHOTGUN << 1:   gun = 3; break;\r\n\t\t\tcase IT_SHOTGUN << 2:   gun = 4; break;\r\n\t\t\tcase IT_SHOTGUN << 3:   gun = 5; break;\r\n\t\t\tcase IT_SHOTGUN << 4:   gun = 6; break;\r\n\t\t\tcase IT_SHOTGUN << 5:   gun = 7; break;\r\n\t\t\tcase IT_SHOTGUN << 6:   gun = 8; break;\r\n\t\t\tdefault: return;\r\n\t\t}\r\n\t}\r\n\r\n    SCR_HUD_DrawGunByNum (hud, gun, scale->value, style->value, wide->value);\r\n}\r\n\r\n// ----------------\r\n// powerzz\r\n//\r\nvoid SCR_HUD_DrawPowerup(hud_t *hud, int num, float scale, int style)\r\n{\r\n    int    x, y, width, height;\r\n    int    c;\r\n\r\n    scale = max(scale, 0.01);\r\n\r\n    switch (style)\r\n    {\r\n    case 1:     // letter\r\n        width = height = 8 * scale;\r\n        if (!HUD_PrepareDraw(hud, width, height, &x, &y))\r\n            return;\r\n        if (HUD_Stats(STAT_ITEMS) & (1<<(17+num)))\r\n        {\r\n            switch (num)\r\n            {\r\n            case 0: c = '1'; break;\r\n            case 1: c = '2'; break;\r\n            case 2: c = 'r'; break;\r\n            case 3: c = 'p'; break;\r\n            case 4: c = 's'; break;\r\n            case 5: c = 'q'; break;\r\n            default: c = '?';\r\n            }\r\n            Draw_SCharacter(x, y, c, scale);\r\n        }\r\n        break;\r\n    default:    // classic - pics\r\n        width = height = scale * 16;\r\n        if (!HUD_PrepareDraw(hud, width, height, &x, &y))\r\n            return;\r\n        if (HUD_Stats(STAT_ITEMS) & (1<<(17+num)))\r\n            Draw_SPic (x, y, sb_items[num], scale);\r\n        break;\r\n    }\r\n}\r\n\r\nvoid SCR_HUD_DrawKey1(hud_t *hud)\r\n{\r\n    static cvar_t *scale = NULL, *style;\r\n    if (scale == NULL)  // first time called\r\n    {\r\n        scale = HUD_FindVar(hud, \"scale\");\r\n        style = HUD_FindVar(hud, \"style\");\r\n    }\r\n    SCR_HUD_DrawPowerup(hud, 0, scale->value, style->value);\r\n}\r\nvoid SCR_HUD_DrawKey2(hud_t *hud)\r\n{\r\n    static cvar_t *scale = NULL, *style;\r\n    if (scale == NULL)  // first time called\r\n    {\r\n        scale = HUD_FindVar(hud, \"scale\");\r\n        style = HUD_FindVar(hud, \"style\");\r\n    }\r\n    SCR_HUD_DrawPowerup(hud, 1, scale->value, style->value);\r\n}\r\nvoid SCR_HUD_DrawRing(hud_t *hud)\r\n{\r\n    static cvar_t *scale = NULL, *style;\r\n    if (scale == NULL)  // first time called\r\n    {\r\n        scale = HUD_FindVar(hud, \"scale\");\r\n        style = HUD_FindVar(hud, \"style\");\r\n    }\r\n    SCR_HUD_DrawPowerup(hud, 2, scale->value, style->value);\r\n}\r\nvoid SCR_HUD_DrawPent(hud_t *hud)\r\n{\r\n    static cvar_t *scale = NULL, *style;\r\n    if (scale == NULL)  // first time called\r\n    {\r\n        scale = HUD_FindVar(hud, \"scale\");\r\n        style = HUD_FindVar(hud, \"style\");\r\n    }\r\n    SCR_HUD_DrawPowerup(hud, 3, scale->value, style->value);\r\n}\r\nvoid SCR_HUD_DrawSuit(hud_t *hud)\r\n{\r\n    static cvar_t *scale = NULL, *style;\r\n    if (scale == NULL)  // first time called\r\n    {\r\n        scale = HUD_FindVar(hud, \"scale\");\r\n        style = HUD_FindVar(hud, \"style\");\r\n    }\r\n    SCR_HUD_DrawPowerup(hud, 4, scale->value, style->value);\r\n}\r\nvoid SCR_HUD_DrawQuad(hud_t *hud)\r\n{\r\n    static cvar_t *scale = NULL, *style;\r\n    if (scale == NULL)  // first time called\r\n    {\r\n        scale = HUD_FindVar(hud, \"scale\");\r\n        style = HUD_FindVar(hud, \"style\");\r\n    }\r\n    SCR_HUD_DrawPowerup(hud, 5, scale->value, style->value);\r\n}\r\n\r\n// -----------\r\n// sigils\r\n//\r\nvoid SCR_HUD_DrawSigil(hud_t *hud, int num, float scale, int style)\r\n{\r\n    int     x, y;\r\n\r\n    scale = max(scale, 0.01);\r\n\r\n    switch (style)\r\n    {\r\n    case 1:     // sigil number\r\n        if (!HUD_PrepareDraw(hud, 8*scale, 8*scale, &x, &y))\r\n            return;\r\n        if (HUD_Stats(STAT_ITEMS) & (1<<(28+num)))\r\n            Draw_SCharacter(x, y, num + '0', scale);\r\n        break;\r\n    default:    // classic - picture\r\n        if (!HUD_PrepareDraw(hud, 8*scale, 16*scale, &x, &y))\r\n            return;\r\n        if (HUD_Stats(STAT_ITEMS) & (1<<(28+num)))\r\n            Draw_SPic(x, y, sb_sigil[num], scale);\r\n        break;\r\n    }\r\n}\r\n\r\nvoid SCR_HUD_DrawSigil1(hud_t *hud)\r\n{\r\n    static cvar_t *scale = NULL, *style;\r\n    if (scale == NULL)  // first time called\r\n    {\r\n        scale = HUD_FindVar(hud, \"scale\");\r\n        style = HUD_FindVar(hud, \"style\");\r\n    }\r\n    SCR_HUD_DrawSigil(hud, 0, scale->value, style->value);\r\n}\r\nvoid SCR_HUD_DrawSigil2(hud_t *hud)\r\n{\r\n    static cvar_t *scale = NULL, *style;\r\n    if (scale == NULL)  // first time called\r\n    {\r\n        scale = HUD_FindVar(hud, \"scale\");\r\n        style = HUD_FindVar(hud, \"style\");\r\n    }\r\n    SCR_HUD_DrawSigil(hud, 1, scale->value, style->value);\r\n}\r\nvoid SCR_HUD_DrawSigil3(hud_t *hud)\r\n{\r\n    static cvar_t *scale = NULL, *style;\r\n    if (scale == NULL)  // first time called\r\n    {\r\n        scale = HUD_FindVar(hud, \"scale\");\r\n        style = HUD_FindVar(hud, \"style\");\r\n    }\r\n    SCR_HUD_DrawSigil(hud, 2, scale->value, style->value);\r\n}\r\nvoid SCR_HUD_DrawSigil4(hud_t *hud)\r\n{\r\n    static cvar_t *scale = NULL, *style;\r\n    if (scale == NULL)  // first time called\r\n    {\r\n        scale = HUD_FindVar(hud, \"scale\");\r\n        style = HUD_FindVar(hud, \"style\");\r\n    }\r\n    SCR_HUD_DrawSigil(hud, 3, scale->value, style->value);\r\n}\r\n\r\n// icons - active ammo, armor, face etc..\r\nvoid SCR_HUD_DrawAmmoIcon(hud_t *hud, int num, float scale, int style)\r\n{\r\n    int   x, y, width, height;\r\n\r\n    scale = max(scale, 0.01);\r\n\r\n    width = height = (style ? 8 : 24) * scale;\r\n\r\n    if (!HUD_PrepareDraw(hud, width, height, &x, &y))\r\n        return;\r\n\r\n    if (style)\r\n    {\r\n        switch (num)\r\n        {\r\n        case 1: Draw_SAlt_String(x, y, \"s\", scale); break;\r\n        case 2: Draw_SAlt_String(x, y, \"n\", scale); break;\r\n        case 3: Draw_SAlt_String(x, y, \"r\", scale); break;\r\n        case 4: Draw_SAlt_String(x, y, \"c\", scale); break;\r\n        }\r\n    }\r\n    else\r\n    {\r\n        Draw_SPic (x, y, sb_ammo[num-1], scale);\r\n    }\r\n}\r\nvoid SCR_HUD_DrawAmmoIconCurrent (hud_t *hud)\r\n{\r\n    int num;\r\n    static cvar_t *scale = NULL, *style;\r\n\r\n    if (scale == NULL)  // first time called\r\n    {\r\n        scale = HUD_FindVar(hud, \"scale\");\r\n        style = HUD_FindVar(hud, \"style\");\r\n    }\r\n\r\n\tif (ShowPreselectedWeap()) {\r\n\t// using weapon pre-selection so show info for current best pre-selected weapon ammo\r\n\t\tif (!(num = State_AmmoNumForWeapon(IN_BestWeapon())))\r\n\t\t\treturn;\r\n\t} else {\r\n\t// not using weapon pre-selection or player is dead so show current selected ammo\r\n\t\tif (HUD_Stats(STAT_ITEMS) & IT_SHELLS)\r\n\t\t\tnum = 1;\r\n\t\telse if (HUD_Stats(STAT_ITEMS) & IT_NAILS)\r\n\t\t\tnum = 2;\r\n\t\telse if (HUD_Stats(STAT_ITEMS) & IT_ROCKETS)\r\n\t\t\tnum = 3;\r\n\t\telse if (HUD_Stats(STAT_ITEMS) & IT_CELLS)\r\n\t\t\tnum = 4;\r\n\t\telse if (TP_TeamFortressEngineerSpanner())\r\n\t\t\tnum = 4;\r\n\t\telse\r\n\t\t\treturn;\r\n\t}\r\n\r\n    SCR_HUD_DrawAmmoIcon(hud, num, scale->value, style->value);\r\n}\r\nvoid SCR_HUD_DrawAmmoIcon1 (hud_t *hud)\r\n{\r\n    static cvar_t *scale = NULL, *style;\r\n    if (scale == NULL)  // first time called\r\n    {\r\n        scale = HUD_FindVar(hud, \"scale\");\r\n        style = HUD_FindVar(hud, \"style\");\r\n    }\r\n    SCR_HUD_DrawAmmoIcon(hud, 1, scale->value, style->value);\r\n}\r\nvoid SCR_HUD_DrawAmmoIcon2 (hud_t *hud)\r\n{\r\n    static cvar_t *scale = NULL, *style;\r\n    if (scale == NULL)  // first time called\r\n    {\r\n        scale = HUD_FindVar(hud, \"scale\");\r\n        style = HUD_FindVar(hud, \"style\");\r\n    }\r\n    SCR_HUD_DrawAmmoIcon(hud, 2, scale->value, style->value);\r\n}\r\nvoid SCR_HUD_DrawAmmoIcon3 (hud_t *hud)\r\n{\r\n    static cvar_t *scale = NULL, *style;\r\n    if (scale == NULL)  // first time called\r\n    {\r\n        scale = HUD_FindVar(hud, \"scale\");\r\n        style = HUD_FindVar(hud, \"style\");\r\n    }\r\n    SCR_HUD_DrawAmmoIcon(hud, 3, scale->value, style->value);\r\n}\r\nvoid SCR_HUD_DrawAmmoIcon4 (hud_t *hud)\r\n{\r\n    static cvar_t *scale = NULL, *style;\r\n    if (scale == NULL)  // first time called\r\n    {\r\n        scale = HUD_FindVar(hud, \"scale\");\r\n        style = HUD_FindVar(hud, \"style\");\r\n    }\r\n    SCR_HUD_DrawAmmoIcon(hud, 4, scale->value, style->value);\r\n}\r\n\r\nvoid SCR_HUD_DrawArmorIcon(hud_t *hud)\r\n{\r\n    int   x, y, width, height;\r\n\r\n    int style;\r\n    float scale;\r\n\r\n    static cvar_t *v_scale = NULL, *v_style;\r\n    if (v_scale == NULL)  // first time called\r\n    {\r\n        v_scale = HUD_FindVar(hud, \"scale\");\r\n        v_style = HUD_FindVar(hud, \"style\");\r\n    }\r\n\r\n    scale = max(v_scale->value, 0.01);\r\n    style = (int)(v_style->value);\r\n\r\n    width = height = (style ? 8 : 24) * scale;\r\n\r\n    if (!HUD_PrepareDraw(hud, width, height, &x, &y))\r\n        return;\r\n\r\n    if (style)\r\n    {\r\n        int c;\r\n\r\n        if (HUD_Stats(STAT_ITEMS) & IT_INVULNERABILITY)\r\n            c = '@';\r\n        else  if (HUD_Stats(STAT_ITEMS) & IT_ARMOR3)\r\n            c = 'r';\r\n        else if (HUD_Stats(STAT_ITEMS) & IT_ARMOR2)\r\n            c = 'y';\r\n        else if (HUD_Stats(STAT_ITEMS) & IT_ARMOR1)\r\n            c = 'g';\r\n        else return;\r\n\r\n        c += 128;\r\n\r\n        Draw_SCharacter(x, y, c, scale);\r\n    }\r\n    else\r\n    {\r\n        mpic_t  *pic;\r\n\r\n        if (HUD_Stats(STAT_ITEMS) & IT_INVULNERABILITY)\r\n            pic = sb_disc;\r\n        else  if (HUD_Stats(STAT_ITEMS) & IT_ARMOR3)\r\n            pic = sb_armor[2];\r\n        else if (HUD_Stats(STAT_ITEMS) & IT_ARMOR2)\r\n            pic = sb_armor[1];\r\n        else if (HUD_Stats(STAT_ITEMS) & IT_ARMOR1)\r\n            pic = sb_armor[0];\r\n        else return;\r\n\r\n        Draw_SPic (x, y, pic, scale);\r\n    }\r\n}\r\n\r\n// face\r\nvoid SCR_HUD_DrawFace(hud_t *hud)\r\n{\r\n    int     f, anim;\r\n    int     x, y;\r\n    float   scale;\r\n\r\n    static cvar_t *v_scale = NULL;\r\n    if (v_scale == NULL)  // first time called\r\n    {\r\n        v_scale = HUD_FindVar(hud, \"scale\");\r\n    }\r\n\r\n    scale = max(v_scale->value, 0.01);\r\n\r\n    if (!HUD_PrepareDraw(hud, 24*scale, 24*scale, &x, &y))\r\n        return;\r\n\r\n    if ( (HUD_Stats(STAT_ITEMS) & (IT_INVISIBILITY | IT_INVULNERABILITY) )\r\n\t\t== (IT_INVISIBILITY | IT_INVULNERABILITY) )\r\n    {\r\n        Draw_SPic (x, y, sb_face_invis_invuln, scale);\r\n        return;\r\n    }\r\n    if (HUD_Stats(STAT_ITEMS) & IT_QUAD)\r\n    {\r\n        Draw_SPic (x, y, sb_face_quad, scale);\r\n        return;\r\n    }\r\n    if (HUD_Stats(STAT_ITEMS) & IT_INVISIBILITY)\r\n    {\r\n        Draw_SPic (x, y, sb_face_invis, scale);\r\n        return;\r\n    }\r\n    if (HUD_Stats(STAT_ITEMS) & IT_INVULNERABILITY)\r\n    {\r\n        Draw_SPic (x, y, sb_face_invuln, scale);\r\n        return;\r\n    }\r\n\r\n    if (HUD_Stats(STAT_HEALTH) >= 100)\r\n        f = 4;\r\n    else\r\n        f = max(0, HUD_Stats(STAT_HEALTH)) / 20;\r\n\r\n    if (cl.time <= cl.faceanimtime)\r\n        anim = 1;\r\n    else\r\n        anim = 0;\r\n    Draw_SPic (x, y, sb_faces[f][anim], scale);\r\n}\r\n\r\n\r\n// status numbers\r\nvoid SCR_HUD_DrawNum(hud_t *hud, int num, qbool low,\r\n                     float scale, int style, int digits, char *s_align)\r\n{\r\n    int  i;\r\n    char buf[sizeof(int) * 3]; // each byte need <= 3 chars\r\n    int  len;\r\n\r\n    int width, height, x, y;\r\n    int size;\r\n    int align;\r\n\r\n    clamp(num, -99999, 999999);\r\n\r\n    scale = max(scale, 0.01);\r\n\r\n    if (digits > 0)\r\n\t\tclamp(digits, 1, 6);\r\n\telse\r\n\t\tdigits = 0; // auto-resize\r\n\r\n    align = 2;\r\n    switch (tolower(s_align[0]))\r\n    {\r\n    default:\r\n    case 'l':   // 'l'eft\r\n        align = 0; break;\r\n    case 'c':   // 'c'enter\r\n        align = 1; break;\r\n    case 'r':   // 'r'ight\r\n        align = 2; break;\r\n    }\r\n\r\n\tsnprintf(buf, sizeof (buf), \"%d\", (style == 2 || style == 3) ? num : abs(num));\r\n\r\n\tif(digits)\r\n\t{\r\n\t\tswitch (hud_digits_trim->ival)\r\n\t\t{\r\n\t\t\tcase 0: // 10030 -> 999\r\n\t\t\t\tlen = strlen(buf);\r\n\t\t\t\tif (len > digits)\r\n\t\t\t\t{\r\n\t\t\t\t\tchar *p = buf;\r\n\t\t\t\t\tif(num < 0)\r\n\t\t\t\t\t\t*p++ = '-';\r\n\t\t\t\t\tfor (i = (num < 0) ? 1 : 0 ; i < digits; i++)\r\n\t\t\t\t\t\t*p++ = '9';\r\n\t\t\t\t\t*p = 0;\r\n\t\t\t\t\tlen = digits;\r\n\t\t\t\t}\r\n\t\t\t\tbreak;\r\n\t\t\tdefault:\r\n\t\t\tcase 1: // 10030 -> 030\r\n\t\t\t\tlen = strlen(buf);\r\n\t\t\t\tif(len > digits)\r\n\t\t\t\t{\r\n\t\t\t\t\tchar *p = buf;\r\n\t\t\t\t\tmemmove(p, p + (len - digits), digits);\r\n\t\t\t\t\tbuf[digits] = '\\0';\r\n\t\t\t\t\tlen = strlen(buf);\r\n\t\t\t\t}\r\n\t\t\t\tbreak;\r\n\t\t\tcase 2: // 10030 -> 100\r\n\t\t\t\tbuf[digits] = '\\0';\r\n    \t\t\tlen = strlen(buf);\r\n\t\t\t\tbreak;\r\n\t\t}\r\n\t}\r\n\telse\r\n\t{\r\n\t\tlen = strlen(buf);\r\n\t}\r\n\r\n    switch (style)\r\n    {\r\n\t\tcase 1:\r\n\t\tcase 3:\r\n\t\t\tsize = 8;\r\n\t\t\tbreak;\r\n\t\tcase 0:\r\n\t\tcase 2:\r\n\t\tdefault:\r\n\t\t\tsize = 24;\r\n\t\t\tbreak;\r\n    }\r\n\r\n    if(digits)\r\n\t\twidth = digits * size;\r\n\telse\r\n\t\twidth = size * len;\r\n\r\n    height = size;\r\n\r\n    switch (style)\r\n    {\r\n\t\tcase 1:\r\n\t\tcase 3:\r\n\t\t\tif (!HUD_PrepareDraw(hud, scale*width, scale*height, &x, &y))\r\n\t\t\t\treturn;\r\n\t\t\tswitch (align)\r\n\t\t\t{\r\n\t\t\t\tcase 0: break;\r\n\t\t\t\tcase 1: x += scale * (width - size * len) / 2; break;\r\n\t\t\t\tcase 2: x += scale * (width - size * len); break;\r\n\t\t\t}\r\n\t\t\tif (low)\r\n\t\t\t\tDraw_SAlt_String(x, y, buf, scale);\r\n\t\t\telse\r\n\t\t\t\tDraw_SString(x, y, buf, scale);\r\n\t\t\tbreak;\r\n\r\n\t\tcase 0:\r\n\t\tcase 2:\r\n\t\tdefault:\r\n\t\t\tif (!HUD_PrepareDraw(hud, scale*width, scale*height, &x, &y))\r\n\t\t\t\treturn;\r\n\t\t\tswitch (align)\r\n\t\t\t{\r\n\t\t\t\tcase 0: break;\r\n\t\t\t\tcase 1: x += scale * (width - size * len) / 2; break;\r\n\t\t\t\tcase 2: x += scale * (width - size * len); break;\r\n\t\t\t}\r\n\t\t\tfor (i = 0; i < len; i++)\r\n\t\t\t{\r\n\t\t\t\tif(buf[i] == '-' && style == 2)\r\n\t\t\t\t{\r\n\t\t\t\t\tDraw_STransPic (x, y, sb_nums[low ? 1 : 0][STAT_MINUS], scale);\r\n\t\t\t\t\tx += 24 * scale;\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\tDraw_STransPic (x, y, sb_nums[low ? 1 : 0][buf[i] - '0'], scale);\r\n\t\t\t\t\tx += 24 * scale;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\tbreak;\r\n    }\r\n}\r\n\r\nvoid SCR_HUD_DrawHealth(hud_t *hud)\r\n{\r\n    static cvar_t *scale = NULL, *style, *digits, *align;\r\n\tstatic int value;\r\n    if (scale == NULL)  // first time called\r\n    {\r\n        scale  = HUD_FindVar(hud, \"scale\");\r\n        style  = HUD_FindVar(hud, \"style\");\r\n        digits = HUD_FindVar(hud, \"digits\");\r\n        align  = HUD_FindVar(hud, \"align\");\r\n    }\r\n\tvalue = HUD_Stats(STAT_HEALTH);\r\n    SCR_HUD_DrawNum(hud, (value < 0 ? 0 : value), HUD_HealthLow(),\r\n        scale->value, style->value, digits->value, align->string);\r\n}\r\n\r\nvoid SCR_HUD_DrawArmor(hud_t *hud)\r\n{\r\n    int level;\r\n    qbool low;\r\n    static cvar_t *scale = NULL, *style, *digits, *align, *pent_666;\r\n    if (scale == NULL)  // first time called\r\n    {\r\n        scale  = HUD_FindVar(hud, \"scale\");\r\n        style  = HUD_FindVar(hud, \"style\");\r\n        digits = HUD_FindVar(hud, \"digits\");\r\n        align  = HUD_FindVar(hud, \"align\");\r\n\t\tpent_666 = HUD_FindVar(hud, \"pent_666\"); // Show 666 or armor value when carrying pentagram\r\n    }\r\n\r\n    if (HUD_Stats(STAT_HEALTH) > 0)\r\n    {\r\n\t\tif ((HUD_Stats(STAT_ITEMS) & IT_INVULNERABILITY) && pent_666->ival)\r\n        {\r\n            level = 666;\r\n            low = true;\r\n        }\r\n        else\r\n        {\r\n            level = HUD_Stats(STAT_ARMOR);\r\n            low = HUD_ArmorLow();\r\n        }\r\n    }\r\n    else\r\n    {\r\n        level = 0;\r\n        low = true;\r\n    }\r\n\r\n    SCR_HUD_DrawNum(hud, level, low,\r\n        scale->value, style->value, digits->value, align->string);\r\n}\r\n\r\n//void Draw_AMFStatLoss (int stat, hud_t* hud);\r\nstatic int vxdamagecount, vxdamagecount_time, vxdamagecount_oldhealth;\r\nstatic int vxdamagecountarmour, vxdamagecountarmour_time, vxdamagecountarmour_oldhealth;\r\nvoid Amf_Reset_DamageStats(void)\r\n{\r\n\tvxdamagecount = vxdamagecount_time = vxdamagecount_oldhealth = 0;\r\n\tvxdamagecountarmour = vxdamagecountarmour_time = vxdamagecountarmour_oldhealth = 0;\r\n}\r\nvoid Draw_AMFStatLoss (int stat, hud_t* hud) {\r\n\t//fixme: should reset these on pov change\r\n    int * vxdmgcnt, * vxdmgcnt_t, * vxdmgcnt_o;\r\n    float alpha;\r\n\tint elem;\r\n\r\n\tif (stat == STAT_HEALTH) {\r\n        vxdmgcnt = &vxdamagecount;\r\n        vxdmgcnt_t = &vxdamagecount_time;\r\n        vxdmgcnt_o = &vxdamagecount_oldhealth;\r\n\t\telem = 0;\r\n    } else {\r\n        vxdmgcnt = &vxdamagecountarmour;\r\n        vxdmgcnt_t = &vxdamagecountarmour_time;\r\n        vxdmgcnt_o = &vxdamagecountarmour_oldhealth;\r\n\t\telem = 1;\r\n    }\r\n\r\n    //VULT STAT LOSS\r\n\t//Pretty self explanitory, I just thought it would be a nice feature to go with my \"what the hell is going on?\" theme\r\n\t//and obscure even more of the screen\r\n\tif (cl.stats[stat] < (*vxdmgcnt_o - 1))\r\n    {\r\n      \tif (*vxdmgcnt_t > cl.time) //add to damage\r\n        \t\t*vxdmgcnt = *vxdmgcnt + (*vxdmgcnt_o - cl.stats[stat]);\r\n      \telse\r\n        \t\t*vxdmgcnt = *vxdmgcnt_o - cl.stats[stat];\r\n      \t*vxdmgcnt_t = cl.time + 2 * (HUD_FindVar(hud, \"duration\")->value);\r\n    }\r\n    *vxdmgcnt_o = cl.stats[stat];\r\n\r\n    if (*vxdmgcnt_t > cl.time)\r\n      \talpha = min(1, (*vxdmgcnt_t - cl.time));\r\n\telse\r\n\t\talpha = 0;\r\n\r\n\tdrawfuncs->Colour4f(1,1,1,alpha);\r\n\t{\r\n\t\tstatic cvar_t *scale[2] = {NULL}, *style[2], *digits[2], *align[2];\r\n\t\tif (scale[elem] == NULL)  // first time called\r\n\t\t{\r\n\t\t\tscale[elem]  = HUD_FindVar(hud, \"scale\");\r\n\t\t\tstyle[elem]  = HUD_FindVar(hud, \"style\");\r\n\t\t\tdigits[elem] = HUD_FindVar(hud, \"digits\");\r\n\t\t\talign[elem]  = HUD_FindVar(hud, \"align\");\r\n\t\t}\r\n\t\tSCR_HUD_DrawNum (hud, abs(*vxdmgcnt), 1,\r\n            scale[elem]->value, style[elem]->value, digits[elem]->ival, align[elem]->string);\r\n\t}\r\n\tdrawfuncs->Colour4f(1,1,1,1);\r\n}\r\n\r\nstatic void SCR_HUD_DrawHealthDamage(hud_t *hud)\r\n{\r\n\tDraw_AMFStatLoss (STAT_HEALTH, hud);\r\n\tif (HUD_Stats(STAT_HEALTH) <= 0)\r\n\t{\r\n\t\tAmf_Reset_DamageStats();\r\n\t}\r\n}\r\n\r\nstatic void SCR_HUD_DrawArmorDamage(hud_t *hud)\r\n{\r\n\tDraw_AMFStatLoss (STAT_ARMOR, hud);\r\n}\r\n\r\nvoid SCR_HUD_DrawAmmo(hud_t *hud, int num,\r\n                      float scale, int style, int digits, char *s_align)\r\n{\r\n    int value, num_old;\r\n    qbool low;\r\n\r\n\tnum_old = num;\r\n    if (num < 1 || num > 4)\r\n    {\t// draw 'current' ammo, which one is it?\r\n\r\n\t\tif (ShowPreselectedWeap()) {\r\n\t\t// using weapon pre-selection so show info for current best pre-selected weapon ammo\r\n\t\t\tif (!(num = State_AmmoNumForWeapon(IN_BestWeapon())))\r\n\t\t\t\treturn;\r\n\t\t} else {\r\n\t\t// not using weapon pre-selection or player is dead so show current selected ammo\r\n\t\t\tif (HUD_Stats(STAT_ITEMS) & IT_SHELLS)\r\n\t\t\t\tnum = 1;\r\n\t\t\telse if (HUD_Stats(STAT_ITEMS) & IT_NAILS)\r\n\t\t\t\tnum = 2;\r\n\t\t\telse if (HUD_Stats(STAT_ITEMS) & IT_ROCKETS)\r\n\t\t\t\tnum = 3;\r\n\t\t\telse if (HUD_Stats(STAT_ITEMS) & IT_CELLS)\r\n\t\t\t\tnum = 4;\r\n\t\t\telse if (TP_TeamFortressEngineerSpanner())\r\n\t\t\t\tnum = 4;\r\n\t\t\telse\r\n\t\t\t\treturn;\r\n\t\t}\r\n    }\r\n\r\n    low = HUD_AmmoLowByWeapon(num * 2);\r\n\tif (num_old == 0 && (!ShowPreselectedWeap() || cl.standby)) {\r\n\t\t// this check is here to display a feature from KTPRO/KTX where you can see received damage in prewar\r\n\t\t// also we make sure this applies only to 'ammo' element\r\n\t\t// weapon preselection must always use HUD_Stats()\r\n\t\tvalue = cl.stats[STAT_AMMO];\r\n\t} else {\r\n\t\tvalue = HUD_Stats(STAT_SHELLS + num - 1);\r\n\t}\r\n\r\n    if (style < 2)\r\n    {\r\n        // simply draw number\r\n        SCR_HUD_DrawNum(hud, value, low, scale, style, digits, s_align);\r\n    }\r\n    else\r\n    {\r\n        // else - draw classic ammo-count box with background\r\n        char buf[8];\r\n        int  x, y;\r\n\r\n        scale = max(scale, 0.01);\r\n\r\n        if (!HUD_PrepareDraw(hud, 42*scale, 11*scale, &x, &y))\r\n            return;\r\n\r\n        snprintf (buf, sizeof (buf), \"%3i\", value);\r\n        Draw_SSubPic(x, y, sb_ibar, 3+((num-1)*48), 0, 42, 11, scale);\r\n        if (buf[0] != ' ')  Draw_SCharacter (x +  7*scale, y, 18+buf[0]-'0', scale);\r\n        if (buf[1] != ' ')  Draw_SCharacter (x + 15*scale, y, 18+buf[1]-'0', scale);\r\n        if (buf[2] != ' ')  Draw_SCharacter (x + 23*scale, y, 18+buf[2]-'0', scale);\r\n    }\r\n}\r\n\r\nvoid SCR_HUD_DrawAmmoCurrent(hud_t *hud)\r\n{\r\n    static cvar_t *scale = NULL, *style, *digits, *align;\r\n    if (scale == NULL)  // first time called\r\n    {\r\n        scale  = HUD_FindVar(hud, \"scale\");\r\n        style  = HUD_FindVar(hud, \"style\");\r\n        digits = HUD_FindVar(hud, \"digits\");\r\n        align  = HUD_FindVar(hud, \"align\");\r\n    }\r\n    SCR_HUD_DrawAmmo(hud, 0, scale->value, style->value, digits->value, align->string);\r\n}\r\nvoid SCR_HUD_DrawAmmo1(hud_t *hud)\r\n{\r\n    static cvar_t *scale = NULL, *style, *digits, *align;\r\n    if (scale == NULL)  // first time called\r\n    {\r\n        scale  = HUD_FindVar(hud, \"scale\");\r\n        style  = HUD_FindVar(hud, \"style\");\r\n        digits = HUD_FindVar(hud, \"digits\");\r\n        align  = HUD_FindVar(hud, \"align\");\r\n    }\r\n    SCR_HUD_DrawAmmo(hud, 1, scale->value, style->value, digits->value, align->string);\r\n}\r\nvoid SCR_HUD_DrawAmmo2(hud_t *hud)\r\n{\r\n    static cvar_t *scale = NULL, *style, *digits, *align;\r\n    if (scale == NULL)  // first time called\r\n    {\r\n        scale  = HUD_FindVar(hud, \"scale\");\r\n        style  = HUD_FindVar(hud, \"style\");\r\n        digits = HUD_FindVar(hud, \"digits\");\r\n        align  = HUD_FindVar(hud, \"align\");\r\n    }\r\n    SCR_HUD_DrawAmmo(hud, 2, scale->value, style->value, digits->value, align->string);\r\n}\r\nvoid SCR_HUD_DrawAmmo3(hud_t *hud)\r\n{\r\n    static cvar_t *scale = NULL, *style, *digits, *align;\r\n    if (scale == NULL)  // first time called\r\n    {\r\n        scale  = HUD_FindVar(hud, \"scale\");\r\n        style  = HUD_FindVar(hud, \"style\");\r\n        digits = HUD_FindVar(hud, \"digits\");\r\n        align  = HUD_FindVar(hud, \"align\");\r\n    }\r\n    SCR_HUD_DrawAmmo(hud, 3, scale->value, style->value, digits->value, align->string);\r\n}\r\nvoid SCR_HUD_DrawAmmo4(hud_t *hud)\r\n{\r\n    static cvar_t *scale = NULL, *style, *digits, *align;\r\n    if (scale == NULL)  // first time called\r\n    {\r\n        scale  = HUD_FindVar(hud, \"scale\");\r\n        style  = HUD_FindVar(hud, \"style\");\r\n        digits = HUD_FindVar(hud, \"digits\");\r\n        align  = HUD_FindVar(hud, \"align\");\r\n    }\r\n    SCR_HUD_DrawAmmo(hud, 4, scale->value, style->value, digits->value, align->string);\r\n}\r\n\r\n// Problem icon, Net\r\n\r\nstatic void SCR_HUD_NetProblem (hud_t *hud) {\r\n\tstatic cvar_t *scale = NULL;\r\n\tint x, y;\r\n\textern qbool hud_editor;\r\n\tplugnetinfo_t *netinfo = GetNetworkInfo();\r\n\r\n\tfloat picwidth = 64;\r\n\tfloat picheight = 64;\r\n\tdrawfuncs->ImageSize((intptr_t)sb_net, &picwidth, &picheight);\r\n\r\n\tif(scale == NULL)\r\n\t\tscale = HUD_FindVar(hud, \"scale\");\r\n\r\n\tif (netinfo->loss.dropped < 1)\r\n\t{\r\n\t\tif (hud_editor)\r\n\t\t\tHUD_PrepareDraw(hud, picwidth, picheight, &x, &y);\r\n\t\treturn;\r\n\t}\r\n\r\n\tif (!HUD_PrepareDraw(hud, picwidth, picheight, &x, &y))\r\n\t\treturn;\r\n\r\n\tDraw_SPic (x, y, sb_net, scale->value);\r\n}\r\n\r\n// ============================================================================0\r\n// Groups\r\n// ============================================================================0\r\n\r\nmpic_t *hud_pic_group1;\r\nmpic_t *hud_pic_group2;\r\nmpic_t *hud_pic_group3;\r\nmpic_t *hud_pic_group4;\r\nmpic_t *hud_pic_group5;\r\nmpic_t *hud_pic_group6;\r\nmpic_t *hud_pic_group7;\r\nmpic_t *hud_pic_group8;\r\nmpic_t *hud_pic_group9;\r\n\r\nvoid SCR_HUD_DrawGroup(hud_t *hud, int width, int height, mpic_t *pic, int pic_scalemode, float pic_alpha)\r\n{\r\n\t#define HUD_GROUP_SCALEMODE_TILE\t\t1\r\n\t#define HUD_GROUP_SCALEMODE_STRETCH\t\t2\r\n\t#define HUD_GROUP_SCALEMODE_GROW\t\t3\r\n\t#define HUD_GROUP_SCALEMODE_CENTER\t\t4\r\n\r\n\tint x, y;\r\n\r\n\tfloat picwidth = 64;\r\n\tfloat picheight = 64;\r\n\r\n\tif (pic && drawfuncs->ImageSize((intptr_t)pic, &picwidth, &picheight) <= 0)\r\n\t{\r\n\t\tpic = NULL;\r\n\t\tpicwidth = 64;\r\n\t\tpicheight = 64;\r\n\t}\r\n\r\n\tclamp(width, 1, 99999);\r\n    clamp(height, 1, 99999);\r\n\r\n\t// Set it to this, because 1.0 will make the colors\r\n\t// completly saturated, and no semi-transparency will show.\r\n\tpic_alpha = (pic_alpha) >= 1.0 ? 0.99 : pic_alpha;\r\n\r\n\t// Grow the group if necessary.\r\n\tif (pic_scalemode == HUD_GROUP_SCALEMODE_GROW\r\n\t\t&& pic != NULL && picheight > 0 && picwidth > 0)\r\n\t{\r\n\t\twidth = max(picwidth, width);\r\n\t\theight = max(picheight, height);\r\n\t}\r\n\r\n\tif (!HUD_PrepareDraw(hud, width, height, &x, &y))\r\n\t{\r\n        return;\r\n\t}\r\n\r\n    // Draw the picture if it's set.\r\n\tif (pic != NULL && picheight > 0)\r\n    {\r\n        int pw, ph;\r\n\r\n\t\tif (pic_scalemode == HUD_GROUP_SCALEMODE_TILE)\r\n        {\r\n            // Tile.\r\n            int cx = 0, cy = 0;\r\n            while (cy < height)\r\n            {\r\n                while (cx < width)\r\n                {\r\n                    pw = min(picwidth, width - cx);\r\n                    ph = min(picheight, height - cy);\r\n\r\n                    if (pw >= picwidth  &&  ph >= picheight)\r\n\t\t\t\t\t{\r\n                        Draw_AlphaPic (x + cx , y + cy, pic, pic_alpha);\r\n\t\t\t\t\t}\r\n                    else\r\n\t\t\t\t\t{\r\n                        Draw_AlphaSubPic (x + cx, y + cy, pic, 0, 0, pw, ph, pic_alpha);\r\n\t\t\t\t\t}\r\n\r\n                    cx += picwidth;\r\n                }\r\n\r\n                cx = 0;\r\n                cy += picheight;\r\n            }\r\n        }\r\n\t\telse if (pic_scalemode == HUD_GROUP_SCALEMODE_STRETCH)\r\n\t\t{\r\n\t\t\t// Stretch or shrink the picture to fit.\r\n\t\t\tfloat scale_x = (float)width / picwidth;\r\n\t\t\tfloat scale_y = (float)height / picheight;\r\n\r\n\t\t\tDraw_SAlphaSubPic2 (x, y, pic, 0, 0, picwidth, picheight, scale_x, scale_y, pic_alpha);\r\n\t\t}\r\n\t\telse if (pic_scalemode == HUD_GROUP_SCALEMODE_CENTER)\r\n\t\t{\r\n\t\t\t// Center the picture in the group.\r\n\t\t\tint pic_x = x + (width - picwidth) / 2;\r\n\t\t\tint pic_y = y + (height - picheight) / 2;\r\n\r\n\t\t\tint src_x = 0;\r\n\t\t\tint src_y = 0;\r\n\r\n\t\t\tif(x > pic_x)\r\n\t\t\t{\r\n\t\t\t\tsrc_x = x - pic_x;\r\n\t\t\t\tpic_x = x;\r\n\t\t\t}\r\n\r\n\t\t\tif(y > pic_y)\r\n\t\t\t{\r\n\t\t\t\tsrc_y = y - pic_y;\r\n\t\t\t\tpic_y = y;\r\n\t\t\t}\r\n\r\n\t\t\tDraw_AlphaSubPic (pic_x, pic_y,\tpic, src_x, src_y, min(width, picwidth), min(height, picheight), pic_alpha);\r\n\t\t}\r\n\t\telse\r\n        {\r\n\t\t\t// Normal. Draw in the top left corner.\r\n\t\t\tDraw_AlphaSubPic (x, y, pic, 0, 0, min(width, picwidth), min(height, picheight), pic_alpha);\r\n        }\r\n    }\r\n}\r\n\r\nvoid SCR_HUD_LoadGroupPic(cvar_t *var, mpic_t **hud_pic, char *oldval)\r\n{\r\n\tchar *newpic = var->string;\r\n\t#define HUD_GROUP_PIC_BASEPATH\t\"gfx/%s\"\r\n\r\n\tmpic_t *temp_pic = NULL;\r\n\tchar pic_path[MAX_QPATH];\r\n\r\n\tif (!hud_pic)\r\n\t{\r\n\t\tCom_Printf (\"Couldn't load picture %s for hud group. HUD PIC is null\\n\", newpic);\r\n\t\treturn;\r\n\t}\r\n\r\n\t// If we have no pic name.\r\n\tif(!newpic || !strcmp (newpic, \"\"))\r\n\t{\r\n\t\t*hud_pic = NULL;\r\n\t\treturn;\r\n\t}\r\n\r\n\t// Get the path for the pic.\r\n\tsnprintf (pic_path, sizeof(pic_path), HUD_GROUP_PIC_BASEPATH, newpic);\r\n\r\n\t// Try loading the pic.\r\n\tif (!(temp_pic = Draw_CachePicSafe(pic_path, false, true)))\r\n\t{\r\n\t\tCom_Printf(\"Couldn't load picture %s for hud group.\\n\", newpic);\r\n\t\tcvarfuncs->SetString(var->name, \"\");\r\n\t\treturn;\r\n\t}\r\n\r\n\t// Save the pic.\r\n\tif (hud_pic)\r\n\t\t*hud_pic = temp_pic;\r\n\r\n\treturn;\r\n}\r\n\r\nvoid SCR_HUD_OnChangePic_Group1(cvar_t *var, char *oldval)\r\n{\r\n\tSCR_HUD_LoadGroupPic(var, &hud_pic_group1, oldval);\r\n}\r\n\r\nvoid SCR_HUD_OnChangePic_Group2(cvar_t *var, char *oldval)\r\n{\r\n\tSCR_HUD_LoadGroupPic(var, &hud_pic_group2, oldval);\r\n}\r\n\r\nvoid SCR_HUD_OnChangePic_Group3(cvar_t *var, char *oldval)\r\n{\r\n\tSCR_HUD_LoadGroupPic(var, &hud_pic_group3, oldval);\r\n}\r\n\r\nvoid SCR_HUD_OnChangePic_Group4(cvar_t *var, char *oldval)\r\n{\r\n\tSCR_HUD_LoadGroupPic(var, &hud_pic_group4, oldval);\r\n}\r\n\r\nvoid SCR_HUD_OnChangePic_Group5(cvar_t *var, char *oldval)\r\n{\r\n\tSCR_HUD_LoadGroupPic(var, &hud_pic_group5, oldval);\r\n}\r\n\r\nvoid SCR_HUD_OnChangePic_Group6(cvar_t *var, char *oldval)\r\n{\r\n\tSCR_HUD_LoadGroupPic(var, &hud_pic_group6, oldval);\r\n}\r\n\r\nvoid SCR_HUD_OnChangePic_Group7(cvar_t *var, char *oldval)\r\n{\r\n\tSCR_HUD_LoadGroupPic(var, &hud_pic_group7, oldval);\r\n}\r\n\r\nvoid SCR_HUD_OnChangePic_Group8(cvar_t *var, char *oldval)\r\n{\r\n\tSCR_HUD_LoadGroupPic(var, &hud_pic_group8, oldval);\r\n}\r\n\r\nvoid SCR_HUD_OnChangePic_Group9(cvar_t *var, char *oldval)\r\n{\r\n\tSCR_HUD_LoadGroupPic(var, &hud_pic_group9, oldval);\r\n}\r\n\r\nvoid SCR_HUD_Group1(hud_t *hud)\r\n{\r\n    static cvar_t *width = NULL,\r\n\t\t*height,\r\n\t\t*picture,\r\n\t\t*pic_alpha,\r\n\t\t*pic_scalemode;\r\n\r\n    if (width == NULL)  // first time called\r\n    {\r\n        width\t\t\t\t= HUD_FindVar(hud, \"width\");\r\n        height\t\t\t\t= HUD_FindVar(hud, \"height\");\r\n        picture\t\t\t\t= HUD_FindVar(hud, \"picture\");\r\n\t\tpic_alpha\t\t\t= HUD_FindVar(hud, \"pic_alpha\");\r\n        pic_scalemode\t\t= HUD_FindVar(hud, \"pic_scalemode\");\r\n\r\n\t\tpicture->callback\t= SCR_HUD_OnChangePic_Group1;\r\n\t\tSCR_HUD_LoadGroupPic(picture, &hud_pic_group1, picture->string);\r\n    }\r\n\r\n\tSCR_HUD_DrawGroup(hud,\r\n\t\twidth->value,\r\n\t\theight->value,\r\n\t\thud_pic_group1,\r\n\t\tpic_scalemode->value,\r\n\t\tpic_alpha->value);\r\n}\r\n\r\nvoid SCR_HUD_Group2(hud_t *hud)\r\n{\r\n\textern void DrawNewText(int x, int y, char *text);\r\n    static cvar_t *width = NULL,\r\n\t\t*height,\r\n\t\t*picture,\r\n\t\t*pic_alpha,\r\n\t\t*pic_scalemode;\r\n\r\n    if (width == NULL)  // first time called\r\n    {\r\n        width\t\t\t= HUD_FindVar(hud, \"width\");\r\n        height\t\t\t= HUD_FindVar(hud, \"height\");\r\n        picture\t\t\t= HUD_FindVar(hud, \"picture\");\r\n\t\tpic_alpha\t\t= HUD_FindVar(hud, \"pic_alpha\");\r\n        pic_scalemode\t= HUD_FindVar(hud, \"pic_scalemode\");\r\n\r\n\t\tpicture->callback\t= SCR_HUD_OnChangePic_Group2;\r\n\t\tSCR_HUD_LoadGroupPic(picture, &hud_pic_group2, picture->string);\r\n    }\r\n\r\n\tSCR_HUD_DrawGroup(hud,\r\n\t\twidth->value,\r\n\t\theight->value,\r\n\t\thud_pic_group2,\r\n\t\tpic_scalemode->value,\r\n\t\tpic_alpha->value);\r\n}\r\n\r\nvoid SCR_HUD_Group3(hud_t *hud)\r\n{\r\n    static cvar_t *width = NULL,\r\n\t\t*height,\r\n\t\t*picture,\r\n\t\t*pic_alpha,\r\n\t\t*pic_scalemode;\r\n\r\n    if (width == NULL)  // first time called\r\n    {\r\n        width\t\t\t= HUD_FindVar(hud, \"width\");\r\n        height\t\t\t= HUD_FindVar(hud, \"height\");\r\n        picture\t\t\t= HUD_FindVar(hud, \"picture\");\r\n\t\tpic_alpha\t\t= HUD_FindVar(hud, \"pic_alpha\");\r\n        pic_scalemode\t= HUD_FindVar(hud, \"pic_scalemode\");\r\n\r\n\t\tpicture->callback\t= SCR_HUD_OnChangePic_Group3;\r\n\t\tSCR_HUD_LoadGroupPic(picture, &hud_pic_group3, picture->string);\r\n    }\r\n\r\n\tSCR_HUD_DrawGroup(hud,\r\n\t\twidth->value,\r\n\t\theight->value,\r\n\t\thud_pic_group3,\r\n\t\tpic_scalemode->value,\r\n\t\tpic_alpha->value);\r\n}\r\n\r\nvoid SCR_HUD_Group4(hud_t *hud)\r\n{\r\n    static cvar_t *width = NULL,\r\n\t\t*height,\r\n\t\t*picture,\r\n\t\t*pic_alpha,\r\n\t\t*pic_scalemode;\r\n\r\n    if (width == NULL)  // first time called\r\n    {\r\n        width\t\t\t= HUD_FindVar(hud, \"width\");\r\n        height\t\t\t= HUD_FindVar(hud, \"height\");\r\n        picture\t\t\t= HUD_FindVar(hud, \"picture\");\r\n\t\tpic_alpha\t\t= HUD_FindVar(hud, \"pic_alpha\");\r\n        pic_scalemode\t= HUD_FindVar(hud, \"pic_scalemode\");\r\n\r\n\t\tpicture->callback\t= SCR_HUD_OnChangePic_Group4;\r\n\t\tSCR_HUD_LoadGroupPic(picture, &hud_pic_group4, picture->string);\r\n    }\r\n\r\n\tSCR_HUD_DrawGroup(hud,\r\n\t\twidth->value,\r\n\t\theight->value,\r\n\t\thud_pic_group4,\r\n\t\tpic_scalemode->value,\r\n\t\tpic_alpha->value);\r\n}\r\n\r\nvoid SCR_HUD_Group5(hud_t *hud)\r\n{\r\n    static cvar_t *width = NULL,\r\n\t\t*height,\r\n\t\t*picture,\r\n\t\t*pic_alpha,\r\n\t\t*pic_scalemode;\r\n\r\n    if (width == NULL)  // first time called\r\n    {\r\n        width\t\t\t= HUD_FindVar(hud, \"width\");\r\n        height\t\t\t= HUD_FindVar(hud, \"height\");\r\n        picture\t\t\t= HUD_FindVar(hud, \"picture\");\r\n\t\tpic_alpha\t\t= HUD_FindVar(hud, \"pic_alpha\");\r\n        pic_scalemode\t= HUD_FindVar(hud, \"pic_scalemode\");\r\n\r\n\t\tpicture->callback\t= SCR_HUD_OnChangePic_Group5;\r\n\t\tSCR_HUD_LoadGroupPic(picture, &hud_pic_group5, picture->string);\r\n    }\r\n\r\n\tSCR_HUD_DrawGroup(hud,\r\n\t\twidth->value,\r\n\t\theight->value,\r\n\t\thud_pic_group5,\r\n\t\tpic_scalemode->value,\r\n\t\tpic_alpha->value);\r\n}\r\n\r\nvoid SCR_HUD_Group6(hud_t *hud)\r\n{\r\n    static cvar_t *width = NULL,\r\n\t\t*height,\r\n\t\t*picture,\r\n\t\t*pic_alpha,\r\n\t\t*pic_scalemode;\r\n\r\n    if (width == NULL)  // first time called\r\n    {\r\n        width\t\t\t= HUD_FindVar(hud, \"width\");\r\n        height\t\t\t= HUD_FindVar(hud, \"height\");\r\n        picture\t\t\t= HUD_FindVar(hud, \"picture\");\r\n\t\tpic_alpha\t\t= HUD_FindVar(hud, \"pic_alpha\");\r\n        pic_scalemode\t= HUD_FindVar(hud, \"pic_scalemode\");\r\n\r\n\t\tpicture->callback\t= SCR_HUD_OnChangePic_Group6;\r\n\t\tSCR_HUD_LoadGroupPic(picture, &hud_pic_group6, picture->string);\r\n    }\r\n\r\n\tSCR_HUD_DrawGroup(hud,\r\n\t\twidth->value,\r\n\t\theight->value,\r\n\t\thud_pic_group6,\r\n\t\tpic_scalemode->value,\r\n\t\tpic_alpha->value);\r\n}\r\n\r\nvoid SCR_HUD_Group7(hud_t *hud)\r\n{\r\n    static cvar_t *width = NULL,\r\n\t\t*height,\r\n\t\t*picture,\r\n\t\t*pic_alpha,\r\n\t\t*pic_scalemode;\r\n\r\n    if (width == NULL)  // first time called\r\n    {\r\n        width\t\t\t= HUD_FindVar(hud, \"width\");\r\n        height\t\t\t= HUD_FindVar(hud, \"height\");\r\n        picture\t\t\t= HUD_FindVar(hud, \"picture\");\r\n\t\tpic_alpha\t\t= HUD_FindVar(hud, \"pic_alpha\");\r\n        pic_scalemode\t= HUD_FindVar(hud, \"pic_scalemode\");\r\n\r\n\t\tpicture->callback\t= SCR_HUD_OnChangePic_Group7;\r\n\t\tSCR_HUD_LoadGroupPic(picture, &hud_pic_group7, picture->string);\r\n    }\r\n\r\n\tSCR_HUD_DrawGroup(hud,\r\n\t\twidth->value,\r\n\t\theight->value,\r\n\t\thud_pic_group7,\r\n\t\tpic_scalemode->value,\r\n\t\tpic_alpha->value);\r\n}\r\n\r\nvoid SCR_HUD_Group8(hud_t *hud)\r\n{\r\n    static cvar_t *width = NULL,\r\n\t\t*height,\r\n\t\t*picture,\r\n\t\t*pic_alpha,\r\n\t\t*pic_scalemode;\r\n\r\n    if (width == NULL)  // first time called\r\n    {\r\n        width\t\t\t= HUD_FindVar(hud, \"width\");\r\n        height\t\t\t= HUD_FindVar(hud, \"height\");\r\n        picture\t\t\t= HUD_FindVar(hud, \"picture\");\r\n\t\tpic_alpha\t\t= HUD_FindVar(hud, \"pic_alpha\");\r\n        pic_scalemode\t= HUD_FindVar(hud, \"pic_scalemode\");\r\n\r\n\t\tpicture->callback\t= SCR_HUD_OnChangePic_Group8;\r\n\t\tSCR_HUD_LoadGroupPic(picture, &hud_pic_group8, picture->string);\r\n    }\r\n\r\n\tSCR_HUD_DrawGroup(hud,\r\n\t\twidth->value,\r\n\t\theight->value,\r\n\t\thud_pic_group8,\r\n\t\tpic_scalemode->value,\r\n\t\tpic_alpha->value);\r\n}\r\n\r\nvoid SCR_HUD_Group9(hud_t *hud)\r\n{\r\n    static cvar_t *width = NULL,\r\n\t\t*height,\r\n\t\t*picture,\r\n\t\t*pic_alpha,\r\n\t\t*pic_scalemode;\r\n\r\n    if (width == NULL)  // first time called\r\n    {\r\n        width\t\t\t= HUD_FindVar(hud, \"width\");\r\n        height\t\t\t= HUD_FindVar(hud, \"height\");\r\n        picture\t\t\t= HUD_FindVar(hud, \"picture\");\r\n\t\tpic_alpha\t\t= HUD_FindVar(hud, \"pic_alpha\");\r\n        pic_scalemode\t= HUD_FindVar(hud, \"pic_scalemode\");\r\n\r\n\t\tpicture->callback\t= SCR_HUD_OnChangePic_Group9;\r\n\t\tSCR_HUD_LoadGroupPic(picture, &hud_pic_group9, picture->string);\r\n    }\r\n\r\n\tSCR_HUD_DrawGroup(hud,\r\n\t\twidth->value,\r\n\t\theight->value,\r\n\t\thud_pic_group9,\r\n\t\tpic_scalemode->value,\r\n\t\tpic_alpha->value);\r\n}\r\n\r\n// player sorting\r\n// for frags and players\r\ntypedef struct sort_teams_info_s\r\n{\r\n\tchar *name;\r\n\tint  frags;\r\n\tint  min_ping;\r\n\tint  avg_ping;\r\n\tint  max_ping;\r\n\tint  nplayers;\r\n\tint  top, bottom;   // leader colours\r\n\tint  rlcount;\t\t// Number of RL's present in the team. (Cokeman 2006-05-27)\r\n}\r\nsort_teams_info_t;\r\n\r\ntypedef struct sort_players_info_s\r\n{\r\n    int playernum;\r\n    sort_teams_info_t *team;\r\n}\r\nsort_players_info_t;\r\n\r\nstatic sort_players_info_t\t\tsorted_players[MAX_CLIENTS];\r\nstatic sort_teams_info_t\t\tsorted_teams[MAX_CLIENTS];\r\nstatic int\t\t\t\t\t\tn_teams;\r\nstatic int\t\t\t\t\t\tn_players;\r\nstatic int\t\t\t\t\t\tn_spectators;\r\nstatic int\t\t\t\t\t\tsort_teamsort = 0;\r\n\r\nstatic int HUD_ComparePlayers(const void *vp1, const void *vp2)\r\n{\r\n\tconst sort_players_info_t *p1 = vp1;\r\n\tconst sort_players_info_t *p2 = vp2;\r\n\r\n    int r = 0;\r\n    player_info_t *i1 = &cl.players[p1->playernum];\r\n    player_info_t *i2 = &cl.players[p2->playernum];\r\n\r\n    if (i1->spectator && !i2->spectator)\r\n\t{\r\n        r = -1;\r\n\t}\r\n    else if (!i1->spectator && i2->spectator)\r\n\t{\r\n        r = 1;\r\n\t}\r\n    else if (i1->spectator && i2->spectator)\r\n    {\r\n        r = strcmp(i1->name, i2->name);\r\n    }\r\n    else\r\n    {\r\n\t\t//\r\n\t\t// Both are players.\r\n\t\t//\r\n\t\tif(sort_teamsort && cl.teamplay && p1->team && p2->team)\r\n\t\t{\r\n\t\t\t// Leading team on top, sort players inside of the teams.\r\n\r\n\t\t\t// Teamsort 1, first sort on team frags.\r\n\t\t\tif (sort_teamsort == 1)\r\n\t\t\t{\r\n\t\t\t\tr = p1->team->frags - p2->team->frags;\r\n\t\t\t}\r\n\r\n\t\t\t// Teamsort == 2, sort on team name only.\r\n\t\t\tr = (r == 0) ? -strcmp(p1->team->name, p2->team->name) : r;\r\n\t\t}\r\n\r\n\t\tr = (r == 0) ? i1->frags - i2->frags : r;\r\n\t\tr = (r == 0) ? strcmp(i1->name, i2->name) : r;\r\n    }\r\n\r\n\tr = (r == 0) ? (p1->playernum - p2->playernum) : r;\r\n\r\n\t// qsort() sorts ascending by default, we want descending.\r\n\t// So negate the result.\r\n\treturn -r;\r\n}\r\n\r\nstatic int HUD_CompareTeams(const void *vt1, const void *vt2)\r\n{\r\n\tint r = 0;\r\n\tconst sort_teams_info_t *t1 = vt1;\r\n\tconst sort_teams_info_t *t2 = vt2;\r\n\r\n\tr = (t1->frags - t2->frags);\r\n\tr = !r ? strcmp(t1->name, t2->name) : r;\r\n\r\n\t// qsort() sorts ascending by default, we want descending.\r\n\t// So negate the result.\r\n\treturn -r;\r\n}\r\n\r\n#define HUD_SCOREBOARD_ALL\t\t\t0xffffffff\r\n#define HUD_SCOREBOARD_SORT_TEAMS\t(1 << 0)\r\n#define HUD_SCOREBOARD_SORT_PLAYERS\t(1 << 1)\r\n#define HUD_SCOREBOARD_UPDATE\t\t(1 << 2)\r\n#define HUD_SCOREBOARD_AVG_PING\t\t(1 << 3)\r\n\r\nstatic void HUD_Sort_Scoreboard(int flags)\r\n{\r\n    int i;\r\n    int team;\r\n\r\n    n_teams = 0;\r\n    n_players = 0;\r\n    n_spectators = 0;\r\n\r\n    // Set team properties.\r\n\tif(flags & HUD_SCOREBOARD_UPDATE)\r\n\t{\r\n\t\tmemset(sorted_teams, 0, sizeof(sorted_teams));\r\n\r\n\t\tfor (i=0; i < MAX_CLIENTS; i++)\r\n\t\t{\r\n\t\t\tif (cl.players[i].name[0] && !cl.players[i].spectator)\r\n\t\t\t{\r\n\t\t\t\t// Find players team\r\n\t\t\t\tfor (team = 0; team < n_teams; team++)\r\n\t\t\t\t{\r\n\t\t\t\t\tif (!strcmp(cl.players[i].team, sorted_teams[team].name)\r\n\t\t\t\t\t\t&& sorted_teams[team].name[0])\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\r\n\t\t\t\t// The team wasn't found in the list of existing teams\r\n\t\t\t\t// so add a new team.\r\n\t\t\t\tif (team == n_teams)\r\n\t\t\t\t{\r\n\t\t\t\t\tteam = n_teams++;\r\n\t\t\t\t\tsorted_teams[team].avg_ping = 0;\r\n\t\t\t\t\tsorted_teams[team].max_ping = 0;\r\n\t\t\t\t\tsorted_teams[team].min_ping = 999;\r\n\t\t\t\t\tsorted_teams[team].nplayers = 0;\r\n\t\t\t\t\tsorted_teams[team].frags = 0;\r\n\t\t\t\t\tsorted_teams[team].top = Sbar_TopColor(&cl.players[i]);\r\n\t\t\t\t\tsorted_teams[team].bottom = Sbar_BottomColor(&cl.players[i]);\r\n\t\t\t\t\tsorted_teams[team].name = cl.players[i].team;\r\n\t\t\t\t\tsorted_teams[team].rlcount = 0;\r\n\t\t\t\t}\r\n\r\n\t\t\t\tsorted_teams[team].nplayers++;\r\n\t\t\t\tsorted_teams[team].frags += cl.players[i].frags;\r\n\t\t\t\tsorted_teams[team].avg_ping += cl.players[i].ping;\r\n\t\t\t\tsorted_teams[team].min_ping = min(sorted_teams[team].min_ping, cl.players[i].ping);\r\n\t\t\t\tsorted_teams[team].max_ping = max(sorted_teams[team].max_ping, cl.players[i].ping);\r\n\r\n#ifdef HAXX\r\n\t\t\t\t// The total RL count for the players team.\r\n\t\t\t\tif(cl.players[i].stats[STAT_ITEMS] & IT_ROCKET_LAUNCHER)\r\n\t\t\t\t{\r\n\t\t\t\t\tsorted_teams[team].rlcount++;\r\n\t\t\t\t}\r\n#endif\r\n\r\n\t\t\t\t// Set player data.\r\n\t\t\t\tsorted_players[n_players + n_spectators].playernum = i;\r\n\t\t\t\t//sorted_players[n_players + n_spectators].team = &sorted_teams[team];\r\n\r\n\t\t\t\t// Increase the count.\r\n\t\t\t\tif (cl.players[i].spectator)\r\n\t\t\t\t{\r\n\t\t\t\t\tn_spectators++;\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\tn_players++;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n    // Calc avg ping.\r\n\tif(flags & HUD_SCOREBOARD_AVG_PING)\r\n\t{\r\n\t\tfor (team = 0; team < n_teams; team++)\r\n\t\t{\r\n\t\t\tsorted_teams[team].avg_ping /= sorted_teams[team].nplayers;\r\n\t\t}\r\n\t}\r\n\r\n\t// Sort teams.\r\n\tif(flags & HUD_SCOREBOARD_SORT_TEAMS)\r\n\t{\r\n\t\tqsort(sorted_teams, n_teams, sizeof(sort_teams_info_t), HUD_CompareTeams);\r\n\r\n\t\t// BUGFIX, this needs to happen AFTER the team array has been sorted, otherwise the\r\n\t\t// players might be pointing to the incorrect team adress.\r\n\t\tfor (i = 0; i < MAX_CLIENTS; i++)\r\n\t\t{\r\n\t\t\tplayer_info_t *player = &cl.players[sorted_players[i].playernum];\r\n\t\t\tsorted_players[i].team = NULL;\r\n\r\n\t\t\t// Find players team.\r\n\t\t\tfor (team = 0; team < n_teams; team++)\r\n\t\t\t{\r\n\t\t\t\tif (!strcmp(player->team, sorted_teams[team].name)\r\n\t\t\t\t\t&& sorted_teams[team].name[0])\r\n\t\t\t\t{\r\n\t\t\t\t\tsorted_players[i].team = &sorted_teams[team];\r\n\t\t\t\t\tbreak;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\t// Sort players.\r\n\tif(flags & HUD_SCOREBOARD_SORT_PLAYERS)\r\n\t{\r\n\t\tqsort(sorted_players, n_players + n_spectators, sizeof(sort_players_info_t), HUD_ComparePlayers);\r\n\t}\r\n}\r\n\r\nvoid Frags_DrawColors(int x, int y, int width, int height,\r\n\t\t\t\t\t  int top_color, int bottom_color, float color_alpha,\r\n\t\t\t\t\t  int frags, int drawBrackets, int style,\r\n\t\t\t\t\t  float bignum)\r\n{\r\n\tchar buf[32];\r\n\tint posy = 0;\r\n\tint char_size = (bignum > 0) ? Q_rint(24 * bignum) : 8;\r\n\r\n\tDraw_AlphaFill(x, y, width, height / 2, top_color, color_alpha);\r\n\tDraw_AlphaFill(x, y + height / 2, width, height - height / 2, bottom_color, color_alpha);\r\n\r\n\tposy = y + (height - char_size) / 2;\r\n\r\n\tif (bignum > 0)\r\n\t{\r\n\t\t//\r\n\t\t// Scaled big numbers for frags.\r\n\t\t//\r\n\t\tchar *t = buf;\r\n\t\tint char_x;\r\n\t\tint char_y;\r\n\t\tsnprintf(buf, sizeof (buf), \"%d\", frags);\r\n\r\n\t\tchar_x = max(x, x + (width  - (int)strlen(buf) * char_size) / 2);\r\n\t\tchar_y = max(y, posy);\r\n\r\n\t\twhile (*t)\r\n\t\t{\r\n\t\t\tif (*t >= '0' && *t <= '9')\r\n\t\t\t{\r\n\t\t\t\tDraw_STransPic(char_x, char_y, sb_nums[0][*t - '0'], bignum);\r\n\t\t\t\tchar_x += char_size;\r\n\t\t\t}\r\n\t\t\telse if (*t == '-')\r\n\t\t\t{\r\n\t\t\t\tDraw_STransPic(char_x, char_y, sb_nums[0][STAT_MINUS], bignum);\r\n\t\t\t\tchar_x += char_size;\r\n\t\t\t}\r\n\r\n\t\t\tt++;\r\n\t\t}\r\n\t}\r\n\telse\r\n\t{\r\n\t\t// Normal text size.\r\n\t\tsnprintf(buf, sizeof (buf), \"%3d\", frags);\r\n\t\tDraw_String(x - 2 + (width - char_size * strlen(buf) - 2) / 2, posy, buf);\r\n\t}\r\n\r\n\tif(drawBrackets)\r\n    {\r\n\t\t// Brackets [] are not available scaled, so use normal size even\r\n\t\t// if we're drawing big frag nums.\r\n\t\tint brack_posy = y + (height - 8) / 2;\r\n        int d = (width >= 32) ? 0 : 1;\r\n\r\n\t\tswitch(style)\r\n\t\t{\r\n\t\t\tcase 1 :\r\n\t\t\t\tDraw_Character(x - 8, posy, 13);\r\n\t\t\t\tbreak;\r\n\t\t\tcase 2 :\r\n\t\t\t\t// Red outline.\r\n\t\t\t\tDraw_Fill(x, y - 1, width, 1, 0x4f);\r\n\t\t\t\tDraw_Fill(x, y - 1, 1, height + 2, 0x4f);\r\n\t\t\t\tDraw_Fill(x + width - 1, y - 1, 1, height + 2, 0x4f);\r\n\t\t\t\tDraw_Fill(x, y + height, width, 1, 0x4f);\r\n\t\t\t\tbreak;\r\n\t\t\tcase 0 :\r\n\t\t\tdefault :\r\n\t\t\t\tDraw_Character(x - 2 - 2 * d, brack_posy, 16); // [\r\n\t\t\t\tDraw_Character(x + width - 8 + 1 + d, brack_posy, 17); // ]\r\n\t\t\t\tbreak;\r\n\t\t}\r\n    }\r\n}\r\n\r\n#define\tFRAGS_HEALTHBAR_WIDTH\t\t\t5\r\n\r\n#define FRAGS_HEALTHBAR_NORMAL_COLOR\t75\r\n#define FRAGS_HEALTHBAR_MEGA_COLOR\t\t251\r\n#define\tFRAGS_HEALTHBAR_TWO_MEGA_COLOR\t238\r\n#define\tFRAGS_HEALTHBAR_UNNATURAL_COLOR\t144\r\n\r\nvoid Frags_DrawHealthBar(int original_health, int x, int y, int height, int width)\r\n{\r\n\tfloat health_height = 0.0;\r\n\tint health;\r\n\r\n\t// Get the health.\r\n\thealth = original_health;\r\n\thealth = min(100, health);\r\n\r\n\t// Draw a health bar.\r\n\thealth_height = Q_rint((height / 100.0) * health);\r\n\thealth_height = (health_height > 0.0 && health_height < 1.0) ? 1 : health_height;\r\n\thealth_height = (health_height < 0.0) ? 0.0 : health_height;\r\n\tDraw_Fill(x, y + height - (int)health_height, 3, (int)health_height, FRAGS_HEALTHBAR_NORMAL_COLOR);\r\n\r\n\t// Get the health again to check if health is more than 100.\r\n\thealth = original_health;\r\n\tif(health > 100 && health <= 200)\r\n\t{\r\n\t\thealth_height = (int)Q_rint((height / 100.0) * (health - 100));\r\n\t\tDraw_Fill(x, y + height - health_height, width, health_height, FRAGS_HEALTHBAR_MEGA_COLOR);\r\n\t}\r\n\telse if(health > 200 && health <= 250)\r\n\t{\r\n\t\thealth_height = (int)Q_rint((height / 100.0) * (health - 200));\r\n\t\tDraw_Fill(x, y, width, height, FRAGS_HEALTHBAR_MEGA_COLOR);\r\n\t\tDraw_Fill(x, y + height - health_height, width, health_height, FRAGS_HEALTHBAR_TWO_MEGA_COLOR);\r\n\t}\r\n\telse if(health > 250)\r\n\t{\r\n\t\t// This will never happen during a normal game.\r\n\t\tDraw_Fill(x, y, width, health_height, FRAGS_HEALTHBAR_UNNATURAL_COLOR);\r\n\t}\r\n}\r\n\r\n#define\tTEAMFRAGS_EXTRA_SPEC_NONE\t0\r\n#define TEAMFRAGS_EXTRA_SPEC_BEFORE\t1\r\n#define\tTEAMFRAGS_EXTRA_SPEC_ONTOP\t2\r\n#define TEAMFRAGS_EXTRA_SPEC_NOICON 3\r\n#define TEAMFRAGS_EXTRA_SPEC_RLTEXT 4\r\n\r\nint TeamFrags_DrawExtraSpecInfo(int num, int px, int py, int width, int height, int style)\r\n{\r\n\tfloat rl_width, rl_height;\r\n\tmpic_t *pic = sb_weapons[0][5];\r\n\tdrawfuncs->ImageSize((intptr_t)pic, &rl_width, &rl_height);\r\n\r\n\t// Only allow this for spectators.\r\n\tif (!(cls.demoplayback || cl.spectator)\r\n\t\t|| style > TEAMFRAGS_EXTRA_SPEC_RLTEXT\r\n\t\t|| style <= TEAMFRAGS_EXTRA_SPEC_NONE\r\n\t\t|| !style)\r\n\t{\r\n\t\treturn px;\r\n\t}\r\n\r\n\t// Check if the team has any RL's.\r\n\tif(sorted_teams[num].rlcount > 0)\r\n\t{\r\n\t\tint y_pos = py;\r\n\r\n\t\t//\r\n\t\t// Draw the RL + count depending on style.\r\n\t\t//\r\n\r\n\t\tif((style == TEAMFRAGS_EXTRA_SPEC_BEFORE || style == TEAMFRAGS_EXTRA_SPEC_NOICON)\r\n\t\t\t&& style != TEAMFRAGS_EXTRA_SPEC_RLTEXT)\r\n\t\t{\r\n\t\t\ty_pos = Q_rint(py + (height / 2.0) - 4);\r\n\t\t\tDraw_ColoredString(px, y_pos, va(\"%d\", sorted_teams[num].rlcount), 0);\r\n\t\t\tpx += 8 + 1;\r\n\t\t}\r\n\r\n\t\tif(style != TEAMFRAGS_EXTRA_SPEC_NOICON && style != TEAMFRAGS_EXTRA_SPEC_RLTEXT)\r\n\t\t{\r\n\t\t\ty_pos = Q_rint(py + (height / 2.0) - (rl_height / 2.0));\r\n\t\t\tDraw_SSubPic (px, y_pos, pic, 0, 0, rl_width, rl_height, 1);\r\n\t\t\tpx += rl_width + 1;\r\n\t\t}\r\n\r\n\t\tif(style == TEAMFRAGS_EXTRA_SPEC_ONTOP && style != TEAMFRAGS_EXTRA_SPEC_RLTEXT)\r\n\t\t{\r\n\t\t\ty_pos = Q_rint(py + (height / 2.0) - 4);\r\n\t\t\tDraw_ColoredString(px - 14, y_pos, va(\"%d\", sorted_teams[num].rlcount), 0);\r\n\t\t}\r\n\r\n\t\tif(style == TEAMFRAGS_EXTRA_SPEC_RLTEXT)\r\n\t\t{\r\n\t\t\ty_pos = Q_rint(py + (height / 2.0) - 4);\r\n\t\t\tDraw_ColoredString(px, y_pos, va(\"&ce00RL&cfff%d\", sorted_teams[num].rlcount), 0);\r\n\t\t\tpx += 8*3 + 1;\r\n\t\t}\r\n\t}\r\n\telse\r\n\t{\r\n\t\t// If the team has no RL's just pad with nothing.\r\n\t\tif(style == TEAMFRAGS_EXTRA_SPEC_BEFORE)\r\n\t\t{\r\n\t\t\t// Draw the rl count before the rl icon.\r\n\t\t\tpx += rl_width + 8 + 1 + 1;\r\n\t\t}\r\n\t\telse if(style == TEAMFRAGS_EXTRA_SPEC_ONTOP)\r\n\t\t{\r\n\t\t\t// Draw the rl count on top of the RL instead of infront.\r\n\t\t\tpx += rl_width + 1;\r\n\t\t}\r\n\t\telse if(style == TEAMFRAGS_EXTRA_SPEC_NOICON)\r\n\t\t{\r\n\t\t\t// Only draw the rl count.\r\n\t\t\tpx += 8 + 1;\r\n\t\t}\r\n\t\telse if(style == TEAMFRAGS_EXTRA_SPEC_RLTEXT)\r\n\t\t{\r\n\t\t\tpx += 8*3 + 1;\r\n\t\t}\r\n\t}\r\n\r\n\treturn px;\r\n}\r\n\r\nstatic qbool hud_frags_extra_spec_info\t= true;\r\nstatic qbool hud_frags_show_rl\t\t\t= true;\r\nstatic qbool hud_frags_show_armor\t\t= true;\r\nstatic qbool hud_frags_show_health\t\t= true;\r\nstatic qbool hud_frags_show_powerup\t\t= true;\r\nstatic qbool hud_frags_textonly\t\t\t= false;\r\n\r\nstatic void QDECL Frags_OnChangeExtraSpecInfo(cvar_t *var, char *oldvalue)\r\n{\r\n\t// Parse the extra spec info.\r\n\thud_frags_show_rl\t\t= Utils_RegExpMatch(\"RL|ALL\",\t\tvar->string);\r\n\thud_frags_show_armor\t= Utils_RegExpMatch(\"ARMOR|ALL\",\tvar->string);\r\n\thud_frags_show_health\t= Utils_RegExpMatch(\"HEALTH|ALL\",\tvar->string);\r\n\thud_frags_show_powerup\t= Utils_RegExpMatch(\"POWERUP|ALL\",\tvar->string);\r\n\thud_frags_textonly\t\t= Utils_RegExpMatch(\"TEXT\",\t\t\tvar->string);\r\n\r\n\thud_frags_extra_spec_info = (hud_frags_show_rl || hud_frags_show_armor || hud_frags_show_health || hud_frags_show_powerup);\r\n}\r\n\r\nstatic int Frags_DrawExtraSpecInfo(player_info_t *info,\r\n\t\t\t\t\t\t\t int px, int py,\r\n\t\t\t\t\t\t\t int cell_width, int cell_height,\r\n\t\t\t\t\t\t\t int space_x, int space_y, int flip)\r\n{\r\n#ifdef HAXX\r\n\tmpic_t *rl_picture = sb_weapons[0][5];\t// Picture of RL.\r\n\tfloat rl_width, rl_height;\r\n\r\n\tfloat\tarmor_height = 0.0;\r\n\tint\t\tarmor = 0;\r\n\tint\t\tarmor_bg_color = 0;\r\n\tfloat\tarmor_bg_power = 0;\r\n\tint\t\thealth_spacing = 1;\r\n\tint\t\tweapon_width = 24;\r\n\r\n\tdrawfuncs->ImageSize((intptr_t)rl_picture, &rl_width, &rl_height);\r\n\r\n\t// Only allow this for spectators.\r\n\tif (!(cls.demoplayback || cl.spectator))\r\n\t{\r\n\t\treturn px;\r\n\t}\r\n\r\n\t// Set width based on text or picture.\r\n\tweapon_width = hud_frags_textonly ? rl_width : 24;\r\n\r\n\t// Draw health bar. (flipped)\r\n\tif(flip && hud_frags_show_health)\r\n\t{\r\n\t\tFrags_DrawHealthBar(info->stats[STAT_HEALTH], px, py, cell_height, 3);\r\n\t\tpx += 3 + health_spacing;\r\n\t}\r\n\r\n\tarmor = info->stats[STAT_ARMOR];\r\n\r\n\t// If the player has any armor, draw it in the appropriate color.\r\n\tif(info->stats[STAT_ITEMS] & IT_ARMOR1)\r\n\t{\r\n\t\tarmor_bg_power = 100;\r\n\t\tarmor_bg_color = 178; // Green armor.\r\n\t}\r\n\telse if(info->stats[STAT_ITEMS] & IT_ARMOR2)\r\n\t{\r\n\t\tarmor_bg_power = 150;\r\n\t\tarmor_bg_color = 111; // Yellow armor.\r\n\t}\r\n\telse if(info->stats[STAT_ITEMS] & IT_ARMOR3)\r\n\t{\r\n\t\tarmor_bg_power = 200;\r\n\t\tarmor_bg_color = 79; // Red armor.\r\n\t}\r\n\r\n\t// Only draw the armor if the current player has one and if the style allows it.\r\n\tif(armor_bg_power && hud_frags_show_armor)\r\n\t{\r\n\t\tarmor_height = Q_rint((cell_height / armor_bg_power) * armor);\r\n\r\n\t\tDraw_AlphaFill(px,\t\t\t\t\t\t\t\t\t\t\t\t// x\r\n\t\t\t\t\t\tpy + cell_height - (int)armor_height,\t\t\t// y (draw from bottom up)\r\n\t\t\t\t\t\tweapon_width,\t\t\t\t\t\t\t\t\t// width\r\n\t\t\t\t\t\t(int)armor_height,\t\t\t\t\t\t\t\t// height\r\n\t\t\t\t\t\tarmor_bg_color,\t\t\t\t\t\t\t\t\t// color\r\n\t\t\t\t\t\t0.3);\t\t\t\t\t\t\t\t\t\t\t// alpha\r\n\t}\r\n\r\n\t// Draw the rl if the current player has it and the style allows it.\r\n\tif(info->stats[STAT_ITEMS] & IT_ROCKET_LAUNCHER && hud_frags_show_rl)\r\n\t{\r\n\t\tif(!hud_frags_textonly)\r\n\t\t{\r\n\t\t\t// Draw the rl-pic.\r\n\t\t\tDraw_SSubPic (px,\r\n\t\t\t\tpy + Q_rint((cell_height/2.0)) - (rl_height/2.0),\r\n\t\t\t\trl_picture, 0, 0,\r\n\t\t\t\trl_width,\r\n\t\t\t\trl_height, 1);\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\t// Just print \"RL\" instead.\r\n\t\t\tDraw_String(px + 12 - 8, py + Q_rint((cell_height/2.0)) - 4, \"RL\");\r\n\t\t}\r\n\t}\r\n\r\n\t// Only draw powerups is the current player has it and the style allows it.\r\n\tif(hud_frags_show_powerup)\r\n\t{\r\n\r\n\t\t//float powerups_x = px + (spec_extra_weapon_w / 2.0);\r\n\t\tfloat powerups_x = px + (weapon_width / 2.0);\r\n\r\n\t\tif(info->stats[STAT_ITEMS] & IT_INVULNERABILITY\r\n\t\t\t&& info->stats[STAT_ITEMS] & IT_INVISIBILITY\r\n\t\t\t&& info->stats[STAT_ITEMS] & IT_QUAD)\r\n\t\t{\r\n\t\t\tDraw_ColoredString(Q_rint(powerups_x - 10), py, \"&c0ffQ&cf00P&cff0R\", 0);\r\n\t\t}\r\n\t\telse if(info->stats[STAT_ITEMS] & IT_QUAD\r\n\t\t\t&& info->stats[STAT_ITEMS] & IT_INVULNERABILITY)\r\n\t\t{\r\n\t\t\tDraw_ColoredString(Q_rint(powerups_x - 8), py, \"&c0ffQ&cf00P\", 0);\r\n\t\t}\r\n\t\telse if(info->stats[STAT_ITEMS] & IT_QUAD\r\n\t\t\t&& info->stats[STAT_ITEMS] & IT_INVISIBILITY)\r\n\t\t{\r\n\t\t\tDraw_ColoredString(Q_rint(powerups_x - 8), py, \"&c0ffQ&cff0R\", 0);\r\n\t\t}\r\n\t\telse if(info->stats[STAT_ITEMS] & IT_INVULNERABILITY\r\n\t\t\t&& info->stats[STAT_ITEMS] & IT_INVISIBILITY)\r\n\t\t{\r\n\t\t\tDraw_ColoredString(Q_rint(powerups_x - 8), py, \"&cf00P&cff0R\", 0);\r\n\t\t}\r\n\t\telse if(info->stats[STAT_ITEMS] & IT_QUAD)\r\n\t\t{\r\n\t\t\tDraw_ColoredString(Q_rint(powerups_x - 4), py, \"&c0ffQ\", 0);\r\n\t\t}\r\n\t\telse if(info->stats[STAT_ITEMS] & IT_INVULNERABILITY)\r\n\t\t{\r\n\t\t\tDraw_ColoredString(Q_rint(powerups_x - 4), py, \"&cf00P\", 0);\r\n\t\t}\r\n\t\telse if(info->stats[STAT_ITEMS] & IT_INVISIBILITY)\r\n\t\t{\r\n\t\t\tDraw_ColoredString(Q_rint(powerups_x - 4), py, \"&cff0R\", 0);\r\n\t\t}\r\n\t}\r\n\r\n\tpx += weapon_width + health_spacing;\r\n\r\n\t// Draw health bar. (not flipped)\r\n\tif(!flip && hud_frags_show_health)\r\n\t{\r\n\t\tFrags_DrawHealthBar(info->stats[STAT_HEALTH], px, py, cell_height, 3);\r\n\t\tpx += 3 + health_spacing;\r\n\t}\r\n#endif\r\n\treturn px;\r\n}\r\n\r\nvoid Frags_DrawBackground(int px, int py, int cell_width, int cell_height,\r\n\t\t\t\t\t\t  int space_x, int space_y, int max_name_length, int max_team_length,\r\n\t\t\t\t\t\t  int bg_color, int shownames, int showteams, int drawBrackets, int style)\r\n{\r\n\tint bg_width = cell_width + space_x;\r\n\t//int bg_color = Sbar_BottomColor(info);\r\n\tfloat bg_alpha = 0.3;\r\n\r\n\tif(style == 4\r\n\t\t|| style == 6\r\n\t\t|| style == 8)\r\n\t\tbg_alpha = 0;\r\n\r\n\tif(shownames)\r\n\t\tbg_width += max_name_length*8 + space_x;\r\n\r\n\tif(showteams)\r\n\t\tbg_width += max_team_length * 8 + space_x;\r\n\r\n\tif(drawBrackets)\r\n\t\tbg_alpha = 0.7;\r\n\r\n\tif(style == 7 || style == 8)\r\n\t\tbg_color = 0x4f;\r\n\r\n\tDraw_AlphaFill(px - 1, py - space_y / 2, bg_width, cell_height + space_y, bg_color, bg_alpha);\r\n\r\n\tif(drawBrackets && (style == 5 || style == 6))\r\n\t{\r\n\t\tDraw_Fill(px - 1, py - 1 - space_y / 2, bg_width, 1, 0x4f);\r\n\r\n\t\tDraw_Fill(px - 1, py - space_y / 2, 1, cell_height + space_y, 0x4f);\r\n\t\tDraw_Fill(px + bg_width - 1, py - 1 - space_y / 2, 1, cell_height + 1 + space_y, 0x4f);\r\n\r\n\t\tDraw_Fill(px - 1, py + cell_height + space_y / 2, bg_width + 1, 1, 0x4f);\r\n\t}\r\n}\r\n\r\nint Frags_DrawText(int px, int py,\r\n\t\t\t\t\tint cell_width, int cell_height,\r\n\t\t\t\t\tint space_x, int space_y,\r\n\t\t\t\t\tint max_name_length, int max_team_length,\r\n\t\t\t\t\tint flip, int pad,\r\n\t\t\t\t\tint shownames, int showteams,\r\n\t\t\t\t\tchar* name, char* team)\r\n{\r\n\tchar _name[MAX_SCOREBOARDNAME + 1];\r\n\tchar _team[MAX_SCOREBOARDNAME + 1];\r\n\tint team_length = 0;\r\n\tint name_length = 0;\r\n\tint char_size = 8;\r\n\tint y_pos;\r\n\r\n\ty_pos = Q_rint(py + (cell_height / 2.0) - 4);\r\n\r\n\t// Draw team\r\n\tif(showteams && cl.teamplay)\r\n\t{\r\n\t\tstrlcpy(_team, team, clamp(max_team_length, 0, sizeof(_team)));\r\n\t\tteam_length = strlen(_team);\r\n\r\n\t\tif(!flip)\r\n\t\t\tpx += space_x;\r\n\r\n\t\tif(pad && flip)\r\n\t\t{\r\n\t\t\tpx += (max_team_length - team_length) * char_size;\r\n\t\t\tDraw_String(px, y_pos, _team);\r\n\t\t\tpx += team_length * char_size;\r\n\t\t}\r\n\t\telse if(pad)\r\n\t\t{\r\n\t\t\tDraw_String(px, y_pos, _team);\r\n\t\t\tpx += max_team_length * char_size;\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tDraw_String(px, y_pos, _team);\r\n\t\t\tpx += team_length * char_size;\r\n\t\t}\r\n\r\n\t\tif(flip)\r\n\t\t\tpx += space_x;\r\n\t}\r\n\r\n\tif(shownames)\r\n\t{\r\n\t\t// Draw name\r\n\t\tstrlcpy(_name, name, clamp(max_name_length, 0, sizeof(_name)));\r\n\t\tname_length = strlen(_name);\r\n\r\n\t\tif(flip && pad)\r\n\t\t{\r\n\t\t\tpx += (max_name_length - name_length) * char_size;\r\n\t\t\tDraw_String(px, y_pos, _name);\r\n\t\t\tpx += name_length * char_size;\r\n\t\t}\r\n\t\telse if(pad)\r\n\t\t{\r\n\t\t\tDraw_String(px, y_pos, _name);\r\n\t\t\tpx += max_name_length * char_size;\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tDraw_String(px, y_pos, _name);\r\n\t\t\tpx += name_length * char_size;\r\n\t\t}\r\n\r\n\t\tpx += space_x;\r\n\t}\r\n\r\n\treturn px;\r\n}\r\n\r\nvoid SCR_HUD_DrawFrags(hud_t *hud)\r\n{\r\n    int width = 0, height = 0;\r\n    int x, y;\r\n\tint max_team_length = 0;\r\n\tint max_name_length = 0;\r\n\r\n    int rows, cols, cell_width, cell_height, space_x, space_y;\r\n    int a_rows, a_cols; // actual\r\n\r\n    static cvar_t\r\n        *hud_frags_cell_width = NULL,\r\n        *hud_frags_cell_height,\r\n        *hud_frags_rows,\r\n        *hud_frags_cols,\r\n        *hud_frags_space_x,\r\n        *hud_frags_space_y,\r\n        *hud_frags_vertical,\r\n        *hud_frags_strip,\r\n        *hud_frags_teamsort,\r\n\t\t*hud_frags_shownames,\r\n\t\t*hud_frags_teams,\r\n\t\t*hud_frags_padtext,\r\n\t\t*hud_frags_showself,\r\n\t\t*hud_frags_extra_spec,\r\n\t\t*hud_frags_fliptext,\r\n\t\t*hud_frags_style,\r\n\t\t*hud_frags_bignum,\r\n\t\t*hud_frags_colors_alpha,\r\n\t\t*hud_frags_maxname,\r\n\t\t*hud_frags_notintp;\r\n\r\n\tmpic_t *rl_picture = sb_weapons[0][5];\r\n\tfloat rl_width, rl_height;\r\n\tdrawfuncs->ImageSize((intptr_t)rl_picture, &rl_width, &rl_height);\r\n\r\n    if (hud_frags_cell_width == NULL)    // first time\r\n    {\r\n\t\tchar specval[256];\r\n\r\n        hud_frags_cell_width    = HUD_FindVar(hud, \"cell_width\");\r\n        hud_frags_cell_height   = HUD_FindVar(hud, \"cell_height\");\r\n        hud_frags_rows          = HUD_FindVar(hud, \"rows\");\r\n        hud_frags_cols          = HUD_FindVar(hud, \"cols\");\r\n        hud_frags_space_x       = HUD_FindVar(hud, \"space_x\");\r\n        hud_frags_space_y       = HUD_FindVar(hud, \"space_y\");\r\n        hud_frags_teamsort      = HUD_FindVar(hud, \"teamsort\");\r\n        hud_frags_strip         = HUD_FindVar(hud, \"strip\");\r\n        hud_frags_vertical      = HUD_FindVar(hud, \"vertical\");\r\n\t\thud_frags_shownames\t\t= HUD_FindVar(hud, \"shownames\");\r\n\t\thud_frags_teams\t\t\t= HUD_FindVar(hud, \"showteams\");\r\n\t\thud_frags_padtext\t\t= HUD_FindVar(hud, \"padtext\");\r\n\t\thud_frags_showself\t\t= HUD_FindVar(hud, \"showself_always\");\r\n\t\thud_frags_extra_spec\t= HUD_FindVar(hud, \"extra_spec_info\");\r\n\t\thud_frags_fliptext\t\t= HUD_FindVar(hud, \"fliptext\");\r\n\t\thud_frags_style\t\t\t= HUD_FindVar(hud, \"style\");\r\n\t\thud_frags_bignum\t\t= HUD_FindVar(hud, \"bignum\");\r\n\t\thud_frags_colors_alpha\t= HUD_FindVar(hud, \"colors_alpha\");\r\n\t\thud_frags_maxname\t\t= HUD_FindVar(hud, \"maxname\");\r\n\t\thud_frags_notintp\t\t= HUD_FindVar(hud, \"notintp\");\r\n\r\n\t\t// Set the OnChange function for extra spec info.\r\n\t\thud_frags_extra_spec->callback = Frags_OnChangeExtraSpecInfo;\r\n\t\tstrlcpy(specval, hud_frags_extra_spec->string, sizeof(specval));\r\n\t\tCvar_Set(hud_frags_extra_spec, specval);\r\n    }\r\n\r\n\t// Don't draw the frags if we're in teamplay.\r\n\tif(hud_frags_notintp->value && cl.teamplay)\r\n\t{\r\n\t\tHUD_PrepareDraw(hud, width, height, &x, &y);\r\n\t\treturn;\r\n\t}\r\n\r\n\t//\r\n\t// Clamp values to be \"sane\".\r\n\t//\r\n\t{\r\n\t\trows = hud_frags_rows->value;\r\n\t\tclamp(rows, 1, MAX_CLIENTS);\r\n\r\n\t\tcols = hud_frags_cols->value;\r\n\t\tclamp(cols, 1, MAX_CLIENTS);\r\n\r\n\t\t// Some users doesn't want to show the actual frags, just\r\n\t\t// extra_spec_info stuff + names.\r\n\t\tcell_width = hud_frags_cell_width->value;\r\n\t\tclamp(cell_width, 0, 128);\r\n\r\n\t\tcell_height = hud_frags_cell_height->value;\r\n\t\tclamp(cell_height, 7, 32);\r\n\r\n\t\tspace_x = hud_frags_space_x->value;\r\n\t\tclamp(space_x, 0, 128);\r\n\r\n\t\tspace_y = hud_frags_space_y->value;\r\n\t\tclamp(space_y, 0, 128);\r\n\t}\r\n\r\n\tsort_teamsort = hud_frags_teamsort->ival;\r\n\r\n    if (hud_frags_strip->ival)\r\n    {\r\n\t\t// Auto set the number of rows / cols based on the number of players.\r\n\t\t// (This is kinda fucked up, but I won't mess with it for the sake of backwards compability).\r\n\r\n        if (hud_frags_vertical->value)\r\n        {\r\n            a_cols = min((n_players + rows - 1) / rows, cols);\r\n            a_rows = min(rows, n_players);\r\n        }\r\n        else\r\n        {\r\n            a_rows = min((n_players + cols - 1) / cols, rows);\r\n            a_cols = min(cols, n_players);\r\n        }\r\n    }\r\n    else\r\n    {\r\n        a_rows = rows;\r\n        a_cols = cols;\r\n    }\r\n\r\n    width  = (a_cols * cell_width)  + ((a_cols + 1) * space_x);\r\n    height = (a_rows * cell_height) + ((a_rows + 1) * space_y);\r\n\r\n\t// Get the longest name/team name for padding.\r\n\tif(hud_frags_shownames->value || hud_frags_teams->value)\r\n\t{\r\n\t\tint cur_length = 0;\r\n\t\tint n;\r\n\r\n\t\tfor(n = 0; n < n_players; n++)\r\n\t\t{\r\n\t\t\tplayer_info_t *info = &cl.players[sorted_players[n].playernum];\r\n\t\t\tcur_length = strlen(info->name);\r\n\r\n\t\t\t// Name.\r\n\t\t\tif(cur_length >= max_name_length)\r\n\t\t\t{\r\n\t\t\t\tmax_name_length = cur_length + 1;\r\n\t\t\t}\r\n\r\n\t\t\tcur_length = strlen(info->team);\r\n\r\n\t\t\t// Team name.\r\n\t\t\tif(cur_length >= max_team_length)\r\n\t\t\t{\r\n\t\t\t\tmax_team_length = cur_length + 1;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t// If the user has set a limit on how many chars that\r\n\t\t// are allowed to be shown for a name/teamname.\r\n\t\tmax_name_length = min(max(0, (int)hud_frags_maxname->value), max_name_length) + 1;\r\n\t\tmax_team_length = min(max(0, (int)hud_frags_maxname->value), max_team_length) + 1;\r\n\r\n\t\t// We need a wider box to draw in if we show the names.\r\n\t\tif(hud_frags_shownames->value)\r\n\t\t{\r\n\t\t\twidth += (a_cols * (max_name_length + 3) * 8) + ((a_cols + 1) * space_x);\r\n\t\t}\r\n\r\n\t\tif(cl.teamplay && hud_frags_teams->value)\r\n\t\t{\r\n\t\t\twidth += (a_cols * max_team_length * 8) + ((a_cols + 1) * space_x);\r\n\t\t}\r\n\t}\r\n\r\n\t// Make room for the extra spectator stuff.\r\n\tif(hud_frags_extra_spec_info && (cls.demoplayback || cl.spectator) )\r\n\t{\r\n\t\twidth += a_cols * (rl_width + FRAGS_HEALTHBAR_WIDTH);\r\n\t}\r\n\r\n    if (HUD_PrepareDraw(hud, width, height, &x, &y))\r\n    {\r\n        int i = 0;\r\n        int player_x = 0;\r\n        int player_y = 0;\r\n        int num = 0;\r\n\t\tint drawBrackets = 0;\r\n\r\n\t\t// The number of players that are to be visible.\r\n\t\tint limit = min(n_players, a_rows * a_cols);\r\n\r\n\t\t// Always show my current frags (don't just show the leaders).\r\n\t\t// TODO: When all players aren't being shown in the frags, draw\r\n\t\t// a small arrow that indicates that there are more frags to be seen.\r\n\t\tif(hud_frags_showself->value && !cl_multiview->value)\r\n\t\t{\r\n\t\t\tint player_pos = 0;\r\n\r\n\t\t\t// Find my position in the scoreboard.\r\n\t\t\tfor(player_pos = 0; i < n_players; player_pos++)\r\n\t\t\t{\r\n\t\t\t\tif (cls.demoplayback || cl.spectator)\r\n\t\t\t\t{\r\n\t\t\t\t\tif (spec_track == sorted_players[player_pos].playernum)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t\telse if(sorted_players[player_pos].playernum == cl.playernum)\r\n\t\t\t\t{\r\n\t\t\t\t\tbreak;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\tif(player_pos + 1 <= (a_rows * a_cols))\r\n\t\t\t{\r\n\t\t\t\t// If I'm not \"outside\" the shown frags, start drawing from the top.\r\n\t\t\t\tnum = 0;\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\t// Always include me in the shown frags.\r\n\t\t\t\tnum = abs((a_rows * a_cols) - (player_pos + 1));\r\n\t\t\t}\r\n\r\n\t\t\t// Make sure we're not trying to go outside the player array.\r\n\t\t\tnum = (num < 0 || num > n_players) ? 0 : num;\r\n\t\t}\r\n\r\n\t\t//num = 0;  // FIXME! johnnycz; (see fixme below)\r\n\r\n\t\t//\r\n\t\t// Loop through all the positions that should be drawn (columns * rows or number of players).\r\n\t\t//\r\n\t\t// Start drawing player \"num\", usually the first player in the array, but if\r\n\t\t// showself_always is set this might be someone else (since we need to make sure the current\r\n\t\t// player is always shown).\r\n\t\t//\r\n        for (i = 0; i < limit; i++)\r\n        {\r\n            player_info_t *info = &cl.players[sorted_players[num].playernum]; // FIXME! johnnycz; causes crashed on some demos\r\n\r\n\t\t\t//\r\n\t\t\t// Set the coordinates where to draw the next element.\r\n\t\t\t//\r\n            if (hud_frags_vertical->value)\r\n            {\r\n                if (i % a_rows == 0)\r\n                {\r\n\t\t\t\t\t// We're drawing a new column.\r\n\r\n\t\t\t\t\tint element_width = cell_width + space_x;\r\n\r\n\t\t\t\t\t// Get the width of all the stuff that is shown, the name, frag cell and so on.\r\n\r\n\t\t\t\t\tif(hud_frags_shownames->value)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\telement_width += (max_name_length) * 8;\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\tif(hud_frags_teams->value)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\telement_width += (max_team_length) * 8;\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\tif(hud_frags_extra_spec_info && (cls.demoplayback || cl.spectator) )\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\telement_width += rl_width;\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\tplayer_x = x + space_x + ((i / a_rows) * element_width);\r\n\r\n\t\t\t\t\t// New column.\r\n                    player_y = y + space_y;\r\n                }\r\n            }\r\n            else\r\n            {\r\n                if (i % a_cols == 0)\r\n                {\r\n\t\t\t\t\t// Drawing new row.\r\n                    player_x = x + space_x;\r\n                    player_y = y + space_y + (i / a_cols) * (cell_height + space_y);\r\n                }\r\n            }\r\n\r\n\t\t\tdrawBrackets = 0;\r\n\r\n\t\t\t// Bug fix. Before the wrong player would be higlighted\r\n\t\t\t// during qwd-playback, since you ARE the player that you're\r\n\t\t\t// being spectated (you're not a spectator).\r\n\t\t\tif(cls.demoplayback && !cl.spectator && !cls.mvdplayback)\r\n\t\t\t{\r\n\t\t\t\tdrawBrackets = (sorted_players[num].playernum == cl.playernum);\r\n\t\t\t}\r\n\t\t\telse if (cls.demoplayback || cl.spectator)\r\n\t\t\t{\r\n\t\t\t\tdrawBrackets = (spec_track == sorted_players[num].playernum && Cam_TrackNum() >= 0);\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tdrawBrackets = (sorted_players[num].playernum == cl.playernum);\r\n\t\t\t}\r\n\r\n\t\t\t// Don't draw any brackets in multiview since we're\r\n\t\t\t// tracking several players.\r\n\t\t\tif (cl_multiview->value > 1 && cls.mvdplayback)\r\n\t\t\t{\r\n\t\t\t\t// TODO: Highlight all players being tracked (See tracking hud-element)\r\n\t\t\t\tdrawBrackets = 0;\r\n\t\t\t}\r\n\r\n\t\t\tif(hud_frags_shownames->value || hud_frags_teams->value || hud_frags_extra_spec_info)\r\n\t\t\t{\r\n\t\t\t\t// Relative x coordinate where we draw the subitems.\r\n\t\t\t\tint rel_player_x = player_x;\r\n\r\n\t\t\t\tif(hud_frags_style->value >= 4 && hud_frags_style->value <= 8)\r\n\t\t\t\t{\r\n\t\t\t\t\t// Draw background based on the style.\r\n\r\n\t\t\t\t\tFrags_DrawBackground(player_x, player_y, cell_width, cell_height, space_x, space_y,\r\n\t\t\t\t\t\tmax_name_length, max_team_length, Sbar_BottomColor(info),\r\n\t\t\t\t\t\thud_frags_shownames->value, hud_frags_teams->value, drawBrackets,\r\n\t\t\t\t\t\thud_frags_style->value);\r\n\t\t\t\t}\r\n\r\n\t\t\t\tif(hud_frags_fliptext->value)\r\n\t\t\t\t{\r\n\t\t\t\t\t//\r\n\t\t\t\t\t// Flip the text\r\n\t\t\t\t\t// NAME | TEAM | FRAGS | EXTRA_SPEC_INFO\r\n\t\t\t\t\t//\r\n\r\n\t\t\t\t\t// Draw name.\r\n\t\t\t\t\trel_player_x = Frags_DrawText(rel_player_x, player_y, cell_width, cell_height,\r\n\t\t\t\t\t\tspace_x, space_y, max_name_length, max_team_length,\r\n\t\t\t\t\t\thud_frags_fliptext->value, hud_frags_padtext->value,\r\n\t\t\t\t\t\thud_frags_shownames->value, 0,\r\n\t\t\t\t\t\tinfo->name, info->team);\r\n\r\n\t\t\t\t\t// Draw team.\r\n\t\t\t\t\trel_player_x = Frags_DrawText(rel_player_x, player_y, cell_width, cell_height,\r\n\t\t\t\t\t\tspace_x, space_y, max_name_length, max_team_length,\r\n\t\t\t\t\t\thud_frags_fliptext->value, hud_frags_padtext->value,\r\n\t\t\t\t\t\t0, hud_frags_teams->value,\r\n\t\t\t\t\t\tinfo->name, info->team);\r\n\r\n\t\t\t\t\tFrags_DrawColors(rel_player_x, player_y, cell_width, cell_height,\r\n\t\t\t\t\t\tSbar_TopColor(info), Sbar_BottomColor(info), hud_frags_colors_alpha->value,\r\n\t\t\t\t\t\tinfo->frags,\r\n\t\t\t\t\t\tdrawBrackets,\r\n\t\t\t\t\t\thud_frags_style->value,\r\n\t\t\t\t\t\thud_frags_bignum->value);\r\n\r\n\t\t\t\t\trel_player_x += cell_width + space_x;\r\n\r\n\t\t\t\t\t// Show extra information about all the players if spectating:\r\n\t\t\t\t\t// - What armor they have.\r\n\t\t\t\t\t// - How much health.\r\n\t\t\t\t\t// - If they have RL or not.\r\n\t\t\t\t\trel_player_x = Frags_DrawExtraSpecInfo(info, rel_player_x, player_y, cell_width, cell_height,\r\n\t\t\t\t\t\t\t space_x, space_y,\r\n\t\t\t\t\t\t\t hud_frags_fliptext->value);\r\n\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\t//\r\n\t\t\t\t\t// Don't flip the text\r\n\t\t\t\t\t// EXTRA_SPEC_INFO | FRAGS | TEAM | NAME\r\n\t\t\t\t\t//\r\n\r\n\t\t\t\t\trel_player_x = Frags_DrawExtraSpecInfo(info, rel_player_x, player_y, cell_width, cell_height,\r\n\t\t\t\t\t\t\t space_x, space_y,\r\n\t\t\t\t\t\t\t hud_frags_fliptext->value);\r\n\r\n\t\t\t\t\tFrags_DrawColors(rel_player_x, player_y, cell_width, cell_height,\r\n\t\t\t\t\t\tSbar_TopColor(info), Sbar_BottomColor(info), hud_frags_colors_alpha->value,\r\n\t\t\t\t\t\tinfo->frags,\r\n\t\t\t\t\t\tdrawBrackets,\r\n\t\t\t\t\t\thud_frags_style->value,\r\n\t\t\t\t\t\thud_frags_bignum->value);\r\n\r\n\t\t\t\t\trel_player_x += cell_width + space_x;\r\n\r\n\t\t\t\t\t// Draw team.\r\n\t\t\t\t\trel_player_x = Frags_DrawText(rel_player_x, player_y, cell_width, cell_height,\r\n\t\t\t\t\t\tspace_x, space_y, max_name_length, max_team_length,\r\n\t\t\t\t\t\thud_frags_fliptext->value, hud_frags_padtext->value,\r\n\t\t\t\t\t\t0, hud_frags_teams->value,\r\n\t\t\t\t\t\tinfo->name, info->team);\r\n\r\n\t\t\t\t\t// Draw name.\r\n\t\t\t\t\trel_player_x = Frags_DrawText(rel_player_x, player_y, cell_width, cell_height,\r\n\t\t\t\t\t\tspace_x, space_y, max_name_length, max_team_length,\r\n\t\t\t\t\t\thud_frags_fliptext->value, hud_frags_padtext->value,\r\n\t\t\t\t\t\thud_frags_shownames->value, 0,\r\n\t\t\t\t\t\tinfo->name, info->team);\r\n\t\t\t\t}\r\n\r\n\t\t\t\tif(hud_frags_vertical->value)\r\n\t\t\t\t{\r\n\t\t\t\t\t// Next row.\r\n\t\t\t\t\tplayer_y += cell_height + space_y;\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\t// Next column.\r\n\t\t\t\t\tplayer_x = rel_player_x + space_x;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\t// Only showing the frags, no names or extra spec info.\r\n\r\n\t\t\t\tFrags_DrawColors(player_x, player_y, cell_width, cell_height,\r\n\t\t\t\t\tSbar_TopColor(info), Sbar_BottomColor(info), hud_frags_colors_alpha->value,\r\n\t\t\t\t\tinfo->frags,\r\n\t\t\t\t\tdrawBrackets,\r\n\t\t\t\t\thud_frags_style->value,\r\n\t\t\t\t\thud_frags_bignum->value);\r\n\r\n\t\t\t\tif (hud_frags_vertical->value)\r\n\t\t\t\t{\r\n\t\t\t\t\t// Next row.\r\n\t\t\t\t\tplayer_y += cell_height + space_y;\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\t// Next column.\r\n\t\t\t\t\tplayer_x += cell_width + space_x;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\t// Next player.\r\n            num++;\r\n        }\r\n    }\r\n}\r\n\r\nvoid SCR_HUD_DrawTeamFrags(hud_t *hud)\r\n{\r\n    int width = 0, height = 0;\r\n    int x, y;\r\n\tint max_team_length = 0, num = 0;\r\n    int rows, cols, cell_width, cell_height, space_x, space_y;\r\n    int a_rows, a_cols; // actual\r\n\r\n    static cvar_t\r\n        *hud_teamfrags_cell_width,\r\n        *hud_teamfrags_cell_height,\r\n        *hud_teamfrags_rows,\r\n        *hud_teamfrags_cols,\r\n        *hud_teamfrags_space_x,\r\n        *hud_teamfrags_space_y,\r\n        *hud_teamfrags_vertical,\r\n        *hud_teamfrags_strip,\r\n\t\t*hud_teamfrags_shownames,\r\n\t\t*hud_teamfrags_fliptext,\r\n\t\t*hud_teamfrags_padtext,\r\n\t\t*hud_teamfrags_style,\r\n\t\t*hud_teamfrags_extra_spec,\r\n\t\t*hud_teamfrags_onlytp,\r\n\t\t*hud_teamfrags_bignum,\r\n\t\t*hud_teamfrags_colors_alpha;\r\n\r\n\tmpic_t *rl_picture = sb_weapons[0][5];\r\n\tfloat rl_width, rl_height;\r\n\tdrawfuncs->ImageSize((intptr_t)rl_picture, &rl_width, &rl_height);\r\n\r\n    if (hud_teamfrags_cell_width == 0)    // first time\r\n    {\r\n        hud_teamfrags_cell_width    = HUD_FindVar(hud, \"cell_width\");\r\n        hud_teamfrags_cell_height   = HUD_FindVar(hud, \"cell_height\");\r\n        hud_teamfrags_rows          = HUD_FindVar(hud, \"rows\");\r\n        hud_teamfrags_cols          = HUD_FindVar(hud, \"cols\");\r\n        hud_teamfrags_space_x       = HUD_FindVar(hud, \"space_x\");\r\n        hud_teamfrags_space_y       = HUD_FindVar(hud, \"space_y\");\r\n        hud_teamfrags_strip         = HUD_FindVar(hud, \"strip\");\r\n        hud_teamfrags_vertical      = HUD_FindVar(hud, \"vertical\");\r\n\t\thud_teamfrags_shownames\t\t= HUD_FindVar(hud, \"shownames\");\r\n\t\thud_teamfrags_fliptext\t\t= HUD_FindVar(hud, \"fliptext\");\r\n\t\thud_teamfrags_padtext\t\t= HUD_FindVar(hud, \"padtext\");\r\n\t\thud_teamfrags_style\t\t\t= HUD_FindVar(hud, \"style\");\r\n\t\thud_teamfrags_extra_spec\t= HUD_FindVar(hud, \"extra_spec_info\");\r\n\t\thud_teamfrags_onlytp\t\t= HUD_FindVar(hud, \"onlytp\");\r\n\t\thud_teamfrags_bignum\t\t= HUD_FindVar(hud, \"bignum\");\r\n\t\thud_teamfrags_colors_alpha\t= HUD_FindVar(hud, \"colors_alpha\");\r\n    }\r\n\r\n\t// Don't draw the frags if we're not in teamplay.\r\n\tif(hud_teamfrags_onlytp->value && !cl.teamplay)\r\n\t{\r\n\t\tHUD_PrepareDraw(hud, width, height, &x, &y);\r\n\t\treturn;\r\n\t}\r\n\r\n    rows = hud_teamfrags_rows->value;\r\n    clamp(rows, 1, MAX_CLIENTS);\r\n    cols = hud_teamfrags_cols->value;\r\n    clamp(cols, 1, MAX_CLIENTS);\r\n    cell_width = hud_teamfrags_cell_width->value;\r\n    clamp(cell_width, 28, 128);\r\n    cell_height = hud_teamfrags_cell_height->value;\r\n    clamp(cell_height, 7, 32);\r\n    space_x = hud_teamfrags_space_x->value;\r\n    clamp(space_x, 0, 128);\r\n    space_y = hud_teamfrags_space_y->value;\r\n    clamp(space_y, 0, 128);\r\n\r\n\tif (hud_teamfrags_strip->value)\r\n    {\r\n        if (hud_teamfrags_vertical->value)\r\n        {\r\n            a_cols = min((n_teams+rows-1) / rows, cols);\r\n            a_rows = min(rows, n_teams);\r\n        }\r\n        else\r\n        {\r\n            a_rows = min((n_teams+cols-1) / cols, rows);\r\n            a_cols = min(cols, n_teams);\r\n        }\r\n    }\r\n    else\r\n    {\r\n        a_rows = rows;\r\n        a_cols = cols;\r\n    }\r\n\r\n    width  = a_cols*cell_width  + (a_cols+1)*space_x;\r\n    height = a_rows*cell_height + (a_rows+1)*space_y;\r\n\r\n\t// Get the longest team name for padding.\r\n\tif(hud_teamfrags_shownames->value || hud_teamfrags_extra_spec->value)\r\n\t{\r\n\t\tint rlcount_width = 0;\r\n\r\n\t\tint cur_length = 0;\r\n\t\tint n;\r\n\r\n\t\tfor(n=0; n < n_teams; n++)\r\n\t\t{\r\n\t\t\tif(hud_teamfrags_shownames->value)\r\n\t\t\t{\r\n\t\t\t\tcur_length = strlen(sorted_teams[n].name);\r\n\r\n\t\t\t\t// Team name\r\n\t\t\t\tif(cur_length >= max_team_length)\r\n\t\t\t\t{\r\n\t\t\t\t\tmax_team_length = cur_length + 1;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t// Calculate the length of the extra spec info.\r\n\t\tif(hud_teamfrags_extra_spec->value && (cls.demoplayback || cl.spectator))\r\n\t\t{\r\n\t\t\tif(hud_teamfrags_extra_spec->value == TEAMFRAGS_EXTRA_SPEC_BEFORE)\r\n\t\t\t{\r\n\t\t\t\t// Draw the rl count before the rl icon.\r\n\t\t\t\trlcount_width = rl_width + 8 + 1 + 1;\r\n\t\t\t}\r\n\t\t\telse if(hud_teamfrags_extra_spec->value == TEAMFRAGS_EXTRA_SPEC_ONTOP)\r\n\t\t\t{\r\n\t\t\t\t// Draw the rl count on top of the RL instead of infront.\r\n\t\t\t\trlcount_width = rl_width + 1;\r\n\t\t\t}\r\n\t\t\telse if(hud_teamfrags_extra_spec->value == TEAMFRAGS_EXTRA_SPEC_NOICON)\r\n\t\t\t{\r\n\t\t\t\t// Only draw the rl count.\r\n\t\t\t\trlcount_width = 8 + 1;\r\n\t\t\t}\r\n\t\t\telse if(hud_teamfrags_extra_spec->value == TEAMFRAGS_EXTRA_SPEC_RLTEXT)\r\n\t\t\t{\r\n\t\t\t\trlcount_width = 8*3 + 1;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\twidth += a_cols*max_team_length*8 + (a_cols+1)*space_x + a_cols*rlcount_width;\r\n\t}\r\n\r\n    if (HUD_PrepareDraw(hud, width, height, &x, &y))\r\n    {\r\n        int i;\r\n        int px = 0;\r\n        int py = 0;\r\n\t\tint drawBrackets;\r\n\t\tint limit = min(n_teams, a_rows*a_cols);\r\n\r\n        for (i=0; i < limit; i++)\r\n        {\r\n            if (hud_teamfrags_vertical->value)\r\n            {\r\n                if (i % a_rows == 0)\r\n                {\r\n                    px = x + space_x + (i/a_rows) * (cell_width+space_x);\r\n                    py = y + space_y;\r\n                }\r\n            }\r\n            else\r\n            {\r\n                if (i % a_cols == 0)\r\n                {\r\n                    px = x + space_x;\r\n                    py = y + space_y + (i/a_cols) * (cell_height+space_y);\r\n                }\r\n            }\r\n\r\n\t\t\tdrawBrackets = 0;\r\n\r\n\t\t\t// Bug fix. Before the wrong player would be higlighted\r\n\t\t\t// during qwd-playback, since you ARE the player that you're\r\n\t\t\t// being spectated.\r\n\t\t\tif(cls.demoplayback && !cl.spectator && !cls.mvdplayback)\r\n\t\t\t{\r\n\t\t\t\t// QWD Playback.\r\n\t\t\t\tif (!strcmp(sorted_teams[num].name, cl.players[cl.playernum].team))\r\n\t\t\t\t{\r\n\t\t\t\t\tdrawBrackets = 1;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\telse if (cls.demoplayback || cl.spectator)\r\n\t\t\t{\r\n\t\t\t\t// MVD playback / spectating.\r\n\t\t\t\tif (!strcmp(cl.players[spec_track].team, sorted_teams[num].name) && Cam_TrackNum() >= 0)\r\n\t\t\t\t{\r\n\t\t\t\t\tdrawBrackets = 1;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\t// Normal player.\r\n\t\t\t\tif (!strcmp(sorted_teams[num].name, cl.players[cl.playernum].team))\r\n\t\t\t\t{\r\n\t\t\t\t\tdrawBrackets = 1;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\tif (cl_multiview->value && cl.splitscreenview != 0 )  // Only draw bracket for first view, might make todo below unnecessary\r\n\t\t\t{\r\n\t\t\t\t// TODO: Check if \"track team\" is set, if it is then draw brackets around that team.\r\n\t\t\t\t//cl.players[nPlayernum]\r\n\r\n\t\t\t\tdrawBrackets = 0;\r\n\t\t\t}\r\n\r\n\t\t\tif(hud_teamfrags_shownames->value || hud_teamfrags_extra_spec->value)\r\n\t\t\t{\r\n\t\t\t\tint _px = px;\r\n\r\n\t\t\t\t// Draw a background if the style tells us to.\r\n\t\t\t\tif(hud_teamfrags_style->value >= 4 && hud_teamfrags_style->value <= 8)\r\n\t\t\t\t{\r\n\t\t\t\t\tFrags_DrawBackground(px, py, cell_width, cell_height, space_x, space_y,\r\n\t\t\t\t\t\t0, max_team_length, sorted_teams[num].bottom,\r\n\t\t\t\t\t\t0, hud_teamfrags_shownames->value, drawBrackets,\r\n\t\t\t\t\t\thud_teamfrags_style->value);\r\n\t\t\t\t}\r\n\r\n\t\t\t\t// Draw the text on the left or right side of the score?\r\n\t\t\t\tif(hud_teamfrags_fliptext->value)\r\n\t\t\t\t{\r\n\t\t\t\t\t// Draw team.\r\n\t\t\t\t\t_px = Frags_DrawText(_px, py, cell_width, cell_height,\r\n\t\t\t\t\t\tspace_x, space_y, 0, max_team_length,\r\n\t\t\t\t\t\thud_teamfrags_fliptext->value, hud_teamfrags_padtext->value,\r\n\t\t\t\t\t\t0, hud_teamfrags_shownames->value,\r\n\t\t\t\t\t\t\"\", sorted_teams[num].name);\r\n\r\n\t\t\t\t\tFrags_DrawColors(_px, py, cell_width, cell_height,\r\n\t\t\t\t\t\tsorted_teams[num].top,\r\n\t\t\t\t\t\tsorted_teams[num].bottom,\r\n\t\t\t\t\t\thud_teamfrags_colors_alpha->value,\r\n\t\t\t\t\t\tsorted_teams[num].frags,\r\n\t\t\t\t\t\tdrawBrackets,\r\n\t\t\t\t\t\thud_teamfrags_style->value,\r\n\t\t\t\t\t\thud_teamfrags_bignum->value);\r\n\r\n\t\t\t\t\t_px += cell_width + space_x;\r\n\r\n\t\t\t\t\t// Draw the rl if the current player has it and the style allows it.\r\n\t\t\t\t\t_px = TeamFrags_DrawExtraSpecInfo(num, _px, py, cell_width, cell_height, hud_teamfrags_extra_spec->value);\r\n\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\t// Draw the rl if the current player has it and the style allows it.\r\n\t\t\t\t\t_px = TeamFrags_DrawExtraSpecInfo(num, _px, py, cell_width, cell_height, hud_teamfrags_extra_spec->value);\r\n\r\n\t\t\t\t\tFrags_DrawColors(_px, py, cell_width, cell_height,\r\n\t\t\t\t\t\tsorted_teams[num].top,\r\n\t\t\t\t\t\tsorted_teams[num].bottom,\r\n\t\t\t\t\t\thud_teamfrags_colors_alpha->value,\r\n\t\t\t\t\t\tsorted_teams[num].frags,\r\n\t\t\t\t\t\tdrawBrackets,\r\n\t\t\t\t\t\thud_teamfrags_style->value,\r\n\t\t\t\t\t\thud_teamfrags_bignum->value);\r\n\r\n\t\t\t\t\t_px += cell_width + space_x;\r\n\r\n\t\t\t\t\t// Draw team.\r\n\t\t\t\t\t_px = Frags_DrawText(_px, py, cell_width, cell_height,\r\n\t\t\t\t\t\tspace_x, space_y, 0, max_team_length,\r\n\t\t\t\t\t\thud_teamfrags_fliptext->value, hud_teamfrags_padtext->value,\r\n\t\t\t\t\t\t0, hud_teamfrags_shownames->value,\r\n\t\t\t\t\t\t\"\", sorted_teams[num].name);\r\n\t\t\t\t}\r\n\r\n\t\t\t\tif(hud_teamfrags_vertical->value)\r\n\t\t\t\t{\r\n\t\t\t\t\tpy += cell_height + space_y;\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\tpx = _px + space_x;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tFrags_DrawColors(px, py, cell_width, cell_height,\r\n\t\t\t\t\tsorted_teams[num].top,\r\n\t\t\t\t\tsorted_teams[num].bottom,\r\n\t\t\t\t\thud_teamfrags_colors_alpha->value,\r\n\t\t\t\t\tsorted_teams[num].frags,\r\n\t\t\t\t\tdrawBrackets,\r\n\t\t\t\t\thud_teamfrags_style->value,\r\n\t\t\t\t\thud_teamfrags_bignum->value);\r\n\r\n\t\t\t\tif (hud_teamfrags_vertical->value)\r\n\t\t\t\t{\r\n\t\t\t\t\tpy += cell_height + space_y;\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\tpx += cell_width + space_x;\r\n\t\t\t\t}\r\n\t\t\t}\r\n            num ++;\r\n        }\r\n    }\r\n}\r\n\r\nchar *Get_MP3_HUD_style(float style, char *st)\r\n{\r\n\tstatic char HUD_style[32];\r\n\tif(style == 1.0)\r\n\t{\r\n\t\tstrlcpy(HUD_style, va(\"%s:\", st), sizeof(HUD_style));\r\n\t}\r\n\telse if(style == 2.0)\r\n\t{\r\n\t\tstrlcpy(HUD_style, va(\"^Ue010%s^Ue011\", st), sizeof(HUD_style));\r\n\t}\r\n\telse\r\n\t{\r\n\t\tstrlcpy(HUD_style, \"\", sizeof(HUD_style));\r\n\t}\r\n\treturn HUD_style;\r\n}\r\n\r\n// Draws MP3 Title.\r\nvoid SCR_HUD_DrawMP3_Title(hud_t *hud)\r\n{\r\n\tint x=0, y=0/*, n=1*/;\r\n    int width = 64;\r\n\tint height = 8;\r\n\r\n#ifdef WITH_MP3_PLAYER\r\n\t//int width_as_text = 0;\r\n\tstatic int title_length = 0;\r\n\t//int row_break = 0;\r\n\t//int i=0;\r\n\tint status = 0;\r\n\tstatic char title[MP3_MAXSONGTITLE];\r\n\tdouble t;\t\t// current time\r\n\tstatic double lastframetime;\t// last refresh\r\n\r\n\tstatic cvar_t *style = NULL, *width_var, *height_var, *scroll, *scroll_delay, *on_scoreboard, *wordwrap;\r\n\r\n\tif (style == NULL)  // first time called\r\n    {\r\n        style =\t\t\t\tHUD_FindVar(hud, \"style\");\r\n\t\twidth_var =\t\t\tHUD_FindVar(hud, \"width\");\r\n\t\theight_var =\t\tHUD_FindVar(hud, \"height\");\r\n\t\tscroll =\t\t\tHUD_FindVar(hud, \"scroll\");\r\n\t\tscroll_delay =\t\tHUD_FindVar(hud, \"scroll_delay\");\r\n\t\ton_scoreboard =\t\tHUD_FindVar(hud, \"on_scoreboard\");\r\n\t\twordwrap =\t\t\tHUD_FindVar(hud, \"wordwrap\");\r\n    }\r\n\r\n\tif(on_scoreboard->value)\r\n\t{\r\n\t\thud->flags |= HUD_ON_SCORES;\r\n\t}\r\n\telse if((int)on_scoreboard->value & HUD_ON_SCORES)\r\n\t{\r\n\t\thud->flags -= HUD_ON_SCORES;\r\n\t}\r\n\r\n\twidth = (int)width_var->value;\r\n\theight = (int)height_var->value;\r\n\r\n\tif(width < 0) width = 0;\r\n\tif(width > vid.width) width = vid.width;\r\n\tif(height < 0) height = 0;\r\n\tif(height > vid.width) height = vid.height;\r\n\r\n\tt = Sys_DoubleTime();\r\n\r\n\tif ((t - lastframetime) >= 2) { // 2 sec refresh rate\r\n\t\tlastframetime = t;\r\n\t\tstatus = MP3_GetStatus();\r\n\r\n\t\tswitch(status)\r\n\t\t{\r\n\t\t\tcase MP3_PLAYING :\r\n\t\t\t\ttitle_length = snprintf(title, sizeof(title)-1, \"%s %s\", Get_MP3_HUD_style(style->value, \"Playing\"), MP3_Macro_MP3Info());\r\n\t\t\t\tbreak;\r\n\t\t\tcase MP3_PAUSED :\r\n\t\t\t\ttitle_length = snprintf(title, sizeof(title)-1, \"%s %s\", Get_MP3_HUD_style(style->value, \"Paused\"), MP3_Macro_MP3Info());\r\n\t\t\t\tbreak;\r\n\t\t\tcase MP3_STOPPED :\r\n\t\t\t\ttitle_length = snprintf(title, sizeof(title)-1, \"%s %s\", Get_MP3_HUD_style(style->value, \"Stopped\"), MP3_Macro_MP3Info());\r\n\t\t\t\tbreak;\r\n\t\t\tcase MP3_NOTRUNNING\t:\r\n\t\t\tdefault :\r\n\t\t\t\tstatus = MP3_NOTRUNNING;\r\n\t\t\t\ttitle_length = snprintf (title, sizeof (title), \"%s is not running.\", mp3_player->PlayerName_AllCaps);\r\n\t\t\t\tbreak;\r\n\t\t}\r\n\r\n\t\tif(title_length < 0)\r\n\t\t{\r\n\t\t\tsnprintf(title, sizeof (title), \"Error retrieving current song.\");\r\n\t\t}\r\n\t}\r\n\r\n\tif (HUD_PrepareDraw(hud, width , height, &x, &y))\r\n\t{\r\n\t\tSCR_DrawWordWrapString(x, y, 8, width, height, (int)wordwrap->value, (int)scroll->value, (float)scroll_delay->value, title);\r\n\t}\r\n#else\r\n\tHUD_PrepareDraw(hud, width , height, &x, &y);\r\n#endif\r\n}\r\n\r\n// Draws MP3 Time as a HUD-element.\r\nvoid SCR_HUD_DrawMP3_Time(hud_t *hud)\r\n{\r\n\tint x = 0, y = 0, width = 0, height = 0;\r\n#ifdef WITH_MP3_PLAYER\r\n\tint elapsed = 0;\r\n\tint remain = 0;\r\n\tint total = 0;\r\n\tstatic char time_string[MP3_MAXSONGTITLE];\r\n\tstatic char elapsed_string[MP3_MAXSONGTITLE];\r\n\tdouble t; // current time\r\n\tstatic double lastframetime; // last refresh\r\n\r\n\tstatic cvar_t *style = NULL, *on_scoreboard;\r\n\r\n\tif(style == NULL)\r\n\t{\r\n\t\tstyle\t\t\t= HUD_FindVar(hud, \"style\");\r\n\t\ton_scoreboard\t= HUD_FindVar(hud, \"on_scoreboard\");\r\n\t}\r\n\r\n\tif(on_scoreboard->value)\r\n\t{\r\n\t\thud->flags |= HUD_ON_SCORES;\r\n\t}\r\n\telse if((int)on_scoreboard->value & HUD_ON_SCORES)\r\n\t{\r\n\t\thud->flags -= HUD_ON_SCORES;\r\n\t}\r\n\r\n\tt = Sys_DoubleTime();\r\n\tif ((t - lastframetime) >= 2) { // 2 sec refresh rate\r\n\t\tlastframetime = t;\r\n\r\n\t\tif(!MP3_GetOutputtime(&elapsed, &total) || elapsed < 0 || total < 0)\r\n\t\t{\r\n\t\t\tsnprintf (time_string, sizeof (time_string), \"^Ue010-:-^Ue011\");\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tswitch((int)style->value)\r\n\t\t\t{\r\n\t\t\t\tcase 1 :\r\n\t\t\t\t\tremain = total - elapsed;\r\n\t\t\t\t\tstrlcpy (elapsed_string, SecondsToMinutesString (remain), sizeof (elapsed_string));\r\n\t\t\t\t\tsnprintf (time_string, sizeof (time_string), \"^Ue010-%s/%s^Ue011\", elapsed_string, SecondsToMinutesString (total));\r\n\t\t\t\t\tbreak;\r\n\t\t\t\tcase 2 :\r\n\t\t\t\t\tremain = total - elapsed;\r\n\t\t\t\t\tsnprintf (time_string, sizeof (time_string), \"^Ue010-%s^Ue011\", SecondsToMinutesString (remain));\r\n\t\t\t\t\tbreak;\r\n\t\t\t\tcase 3 :\r\n\t\t\t\t\tsnprintf (time_string, sizeof (time_string), \"^Ue010%s^Ue011\", SecondsToMinutesString (elapsed));\r\n\t\t\t\t\tbreak;\r\n\t\t\t\tcase 4 :\r\n\t\t\t\t\tremain = total - elapsed;\r\n\t\t\t\t\tstrlcpy (elapsed_string, SecondsToMinutesString (remain), sizeof (elapsed_string));\r\n\t\t\t\t\tsnprintf (time_string, sizeof (time_string), \"%s/%s\", elapsed_string, SecondsToMinutesString (total));\r\n\t\t\t\t\tbreak;\r\n\t\t\t\tcase 5 :\r\n\t\t\t\t\tstrlcpy (elapsed_string, SecondsToMinutesString (elapsed), sizeof (elapsed_string));\r\n\t\t\t\t\tsnprintf (time_string, sizeof (time_string), \"-%s/%s\", elapsed_string, SecondsToMinutesString (total));\r\n\t\t\t\t\tbreak;\r\n\t\t\t\tcase 6 :\r\n\t\t\t\t\tremain = total - elapsed;\r\n\t\t\t\t\tsnprintf (time_string, sizeof (time_string), \"-%s\", SecondsToMinutesString (remain));\r\n\t\t\t\t\tbreak;\r\n\t\t\t\tcase 7 :\r\n\t\t\t\t\tsnprintf (time_string, sizeof (time_string), \"%s\", SecondsToMinutesString (elapsed));\r\n\t\t\t\t\tbreak;\r\n\t\t\t\tcase 0 :\r\n\t\t\t\tdefault :\r\n\t\t\t\t\tstrlcpy (elapsed_string, SecondsToMinutesString (elapsed), sizeof (elapsed_string));\r\n\t\t\t\t\tsnprintf (time_string, sizeof (time_string), \"^Ue010%s/%s^Ue011\", elapsed_string, SecondsToMinutesString (total));\r\n\t\t\t\t\tbreak;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t}\r\n\r\n\t// Don't allow showing the timer if ruleset disallows it\r\n\t// It could be used for timing powerups\r\n\t// Use same check that is used for any external communication\r\n\tif(Rulesets_RestrictPacket())\r\n\t\tsnprintf (time_string, sizeof (time_string), \"^Ue010%s^Ue011\", \"Not allowed\");\r\n\r\n\twidth = strlen (time_string) * 8;\r\n\theight = 8;\r\n\r\n\tif (HUD_PrepareDraw(hud, width , height, &x, &y))\r\n\t\tDraw_String(x, y, time_string);\r\n#else\r\n\tHUD_PrepareDraw(hud, width , height, &x, &y);\r\n#endif\r\n}\r\n\r\n#ifdef WITH_PNG\r\n\r\n// Map picture to draw for the mapoverview hud control.\r\nmpic_t *radar_pic;\r\nstatic qbool radar_pic_found = false;\r\n\r\n// The conversion formula used for converting from quake coordinates to pixel coordinates\r\n// when drawing on the map overview.\r\nstatic float map_x_slope;\r\nstatic float map_x_intercept;\r\nstatic float map_y_slope;\r\nstatic float map_y_intercept;\r\nstatic qbool conversion_formula_found = false;\r\n\r\n// Used for drawing the height of the player.\r\nstatic float map_height_diff = 0.0;\r\n\r\n#define RADAR_BASE_PATH_FORMAT\t\"radars/%s.png\"\r\n\r\n//\r\n// Is run when a new map is loaded.\r\n//\r\nvoid HUD_NewRadarMap()\r\n{\r\n\tint i = 0;\r\n\tint len = 0;\r\n\tint n_textcount = 0;\r\n\tmpic_t *radar_pic_p = NULL;\r\n\tpng_textp txt = NULL;\r\n\tchar *radar_filename = NULL;\r\n\r\n\tif (!cl.worldmodel)\r\n\t\treturn; // seems we are not ready to do that\r\n\r\n\t// Reset the radar pic status.\r\n\tradar_pic = NULL;\r\n\tradar_pic_found = false;\r\n\tconversion_formula_found = false;\r\n\r\n\t// Allocate a string for the path to the radar image.\r\n\tlen = strlen (RADAR_BASE_PATH_FORMAT) +  strlen (host_mapname.string);\r\n\tradar_filename = Q_calloc (len, sizeof(char));\r\n\tsnprintf (radar_filename, len, RADAR_BASE_PATH_FORMAT, host_mapname.string);\r\n\r\n\t// Load the map picture.\r\n\tif ((radar_pic_p = GL_LoadPicImage (radar_filename, host_mapname.string, 0, 0, TEX_ALPHA)) != NULL)\r\n\t{\r\n\t\tradar_pic = *radar_pic_p;\r\n\t\tradar_pic_found = true;\r\n\r\n\t\t// Calculate the height of the map.\r\n\t\tmap_height_diff = abs(cl.worldmodel->maxs[2] - cl.worldmodel->mins[2]);\r\n\r\n\t\t// Get the comments from the PNG.\r\n\t\ttxt = Image_LoadPNG_Comments(radar_filename, &n_textcount);\r\n\r\n\t\t// Check if we found any comments.\r\n\t\tif(txt != NULL)\r\n\t\t{\r\n\t\t\tint found_count = 0;\r\n\r\n\t\t\t// Find the conversion formula in the comments found in the PNG.\r\n\t\t\tfor(i = 0; i < n_textcount; i++)\r\n\t\t\t{\r\n\t\t\t\tif(!strcmp(txt[i].key, \"QWLMConversionSlopeX\"))\r\n\t\t\t\t{\r\n\t\t\t\t\tmap_x_slope = atof(txt[i].text);\r\n\t\t\t\t\tfound_count++;\r\n\t\t\t\t}\r\n\t\t\t\telse if(!strcmp(txt[i].key, \"QWLMConversionInterceptX\"))\r\n\t\t\t\t{\r\n\t\t\t\t\tmap_x_intercept = atof(txt[i].text);\r\n\t\t\t\t\tfound_count++;\r\n\t\t\t\t}\r\n\t\t\t\telse if(!strcmp(txt[i].key, \"QWLMConversionSlopeY\"))\r\n\t\t\t\t{\r\n\t\t\t\t\tmap_y_slope = atof(txt[i].text);\r\n\t\t\t\t\tfound_count++;\r\n\t\t\t\t}\r\n\t\t\t\telse if(!strcmp(txt[i].key, \"QWLMConversionInterceptY\"))\r\n\t\t\t\t{\r\n\t\t\t\t\tmap_y_intercept = atof(txt[i].text);\r\n\t\t\t\t\tfound_count++;\r\n\t\t\t\t}\r\n\r\n\t\t\t\tconversion_formula_found = (found_count == 4);\r\n\t\t\t}\r\n\r\n\t\t\t// Free the text chunks.\r\n\t\t\tQ_free(txt);\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tconversion_formula_found = false;\r\n\t\t}\r\n\t}\r\n\telse\r\n\t{\r\n\t\t// No radar pic found.\r\n\t\tmemset (&radar_pic, 0, sizeof(radar_pic));\r\n\t\tradar_pic_found = false;\r\n\t\tconversion_formula_found = false;\r\n\t}\r\n\r\n\t// Free the path string to the radar png.\r\n\tQ_free (radar_filename);\r\n}\r\n#endif // WITH_PNG\r\n\r\n#define TEMPHUD_NAME \"_temphud\"\r\n#define TEMPHUD_FULLPATH \"configs/\"TEMPHUD_NAME\".cfg\"\r\n\r\n// will check if user wants to un/load external MVD HUD automatically\r\nvoid HUD_AutoLoad_MVD(int autoload) {\r\n#ifdef HAXX\r\n\tchar *cfg_suffix = \"custom\";\r\n\textern cvar_t *scr_fov;\r\n\textern cvar_t *scr_newHud;\r\n\textern void Cmd_Exec_f (void);\r\n\textern void DumpConfig(char *name);\r\n\r\n\tif (autoload && cls.mvdplayback) {\r\n\t\t// Turn autohud ON here\r\n\r\n\t\tCom_DPrintf(\"Loading MVD Hud\\n\");\r\n\t\t// Store current settings.\r\n\t\tif (!autohud.active)\r\n\t\t{\r\n\t\t\t// Save old cfg_save values so that we don't screw the users\r\n\t\t\t// settings when saving the temp config.\r\n\t\t\tint old_cmdline = cvarfuncs->GetFloat(\"cfg_save_cmdline\");\r\n\t\t\tint old_cvars\t= cvarfuncs->GetFloat(\"cfg_save_cvars\");\r\n\t\t\tint old_cmds\t= cvarfuncs->GetFloat(\"cfg_save_cmds\");\r\n\t\t\tint old_aliases = cvarfuncs->GetFloat(\"cfg_save_aliases\");\r\n\t\t\tint old_binds\t= cvarfuncs->GetFloat(\"cfg_save_binds\");\r\n\r\n\t\t\tautohud.old_fov = (int) scr_fov->value;\r\n\t\t\tautohud.old_multiview = (int) cl_multiview->value;\r\n\t\t\tautohud.old_newhud = (int) scr_newHud->value;\r\n\r\n\t\t\t// Make sure everything current settings are saved.\r\n\t\t\tcvarfuncs->SetFloat(\"cfg_save_cmdline\",\t1);\r\n\t\t\tcvarfuncs->SetFloat(\"cfg_save_cvars\",\t1);\r\n\t\t\tcvarfuncs->SetFloat(\"cfg_save_cmds\",\t\t1);\r\n\t\t\tcvarfuncs->SetFloat(\"cfg_save_aliases\",\t1);\r\n\t\t\tcvarfuncs->SetFloat(\"cfg_save_binds\",\t1);\r\n\r\n\t\t\t// Save a temporary config.\r\n\t\t\tDumpConfig(TEMPHUD_NAME\".cfg\");\r\n\r\n\t\t\tcvarfuncs->SetFloat(\"cfg_save_cmdline\",\told_cmdline);\r\n\t\t\tcvarfuncs->SetFloat(\"cfg_save_cvars\",\told_cvars);\r\n\t\t\tcvarfuncs->SetFloat(\"cfg_save_cmds\",\t\told_cmds);\r\n\t\t\tcvarfuncs->SetFloat(\"cfg_save_aliases\",\told_aliases);\r\n\t\t\tcvarfuncs->SetFloat(\"cfg_save_binds\",\told_binds);\r\n\t\t}\r\n\r\n\t\t// load MVD HUD config\r\n\t\tswitch ((int) autoload) {\r\n\t\t\tcase 1: // load 1on1 or 4on4 or custom according to $matchtype\r\n\t\t\t\tif (!strncmp(Macro_MatchType(), \"duel\", 4)) {\r\n\t\t\t\t\tcfg_suffix = \"1on1\";\r\n\t\t\t\t} else if (!strncmp(Macro_MatchType(), \"4on4\", 4)) {\r\n\t\t\t\t\tcfg_suffix = \"4on4\";\r\n\t\t\t\t} else {\r\n\t\t\t\t\tcfg_suffix = \"custom\";\r\n\t\t\t\t}\r\n\t\t\t\tbreak;\r\n\t\t\tdefault:\r\n\t\t\tcase 2:\r\n\t\t\t\tcfg_suffix = \"custom\";\r\n\t\t\t\tbreak;\r\n\t\t}\r\n\r\n\t\tCbuf_AddText(va(\"exec cfg/mvdhud_%s.cfg\\n\", cfg_suffix));\r\n\r\n\t\tautohud.active = true;\r\n\t\treturn;\r\n\t}\r\n\r\n\tif ((!cls.mvdplayback || !autoload) && autohud.active) {\r\n\t\t// either user decided to turn mvd autohud off or mvd playback is over\r\n\t\t// -> Turn autohud OFF here\r\n\t\tFILE *tempfile;\r\n\t\tchar *fullname = va(\"%s/ezquake/\"TEMPHUD_FULLPATH, com_basedir);\r\n\r\n\t\tCom_DPrintf(\"Unloading MVD Hud\\n\");\r\n\t\t// load stored settings\r\n\t\tcvarfuncs->SetFloat(scr_fov->name, autohud.old_fov);\r\n\t\tcvarfuncs->SetFloat(cl_multiview->name, autohud.old_multiview);\r\n\t\tcvarfuncs->SetFloat(scr_newHud->name, autohud.old_newhud);\r\n\t\t//Cmd_TokenizeString(\"exec \"TEMPHUD_FULLPATH);\r\n\t\tCmd_TokenizeString(\"cfg_load \"TEMPHUD_FULLPATH);\r\n\t\tCmd_Exec_f();\r\n\r\n\t\t// delete temp config with hud_* settings\r\n\t\tif ((tempfile = fopen(fullname, \"rb\")) && (fclose(tempfile) != EOF))\r\n\t\t\tunlink(fullname);\r\n\r\n\t\tautohud.active = false;\r\n\t\treturn;\r\n\t}\r\n#endif\r\n}\r\n\r\nvoid OnAutoHudChange(cvar_t *var, char *value, qbool *cancel) {\r\n\tHUD_AutoLoad_MVD(Q_atoi(value));\r\n}\r\n\r\n// Is run when a new map is loaded.\r\nvoid HUD_NewMap(void) {\r\n#if defined(WITH_PNG)\r\n\tHUD_NewRadarMap();\r\n#endif // WITH_PNG\r\n\r\n\tautohud_loaded = false;\r\n}\r\n\r\n#define HUD_SHOW_ONLY_IN_TEAMPLAY\t\t1\r\n#define HUD_SHOW_ONLY_IN_DEMOPLAYBACK\t2\r\n\r\nqbool HUD_ShowInDemoplayback(int val)\r\n{\r\n\tif(!cl.teamplay && val == HUD_SHOW_ONLY_IN_TEAMPLAY)\r\n\t{\r\n\t\treturn false;\r\n\t}\r\n\telse if(!cls.demoplayback && val == HUD_SHOW_ONLY_IN_DEMOPLAYBACK)\r\n\t{\r\n\t\treturn false;\r\n\t}\r\n\telse if(!cl.teamplay && !cls.demoplayback\r\n\t\t&& val == HUD_SHOW_ONLY_IN_TEAMPLAY + HUD_SHOW_ONLY_IN_DEMOPLAYBACK)\r\n\t{\r\n\t\treturn false;\r\n\t}\r\n\r\n\treturn true;\r\n}\r\n\r\n// Team hold filters.\r\nstatic qbool teamhold_show_pent\t\t= false;\r\nstatic qbool teamhold_show_quad\t\t= false;\r\nstatic qbool teamhold_show_ring\t\t= false;\r\nstatic qbool teamhold_show_suit\t\t= false;\r\nstatic qbool teamhold_show_rl\t\t= false;\r\nstatic qbool teamhold_show_lg\t\t= false;\r\nstatic qbool teamhold_show_gl\t\t= false;\r\nstatic qbool teamhold_show_sng\t\t= false;\r\nstatic qbool teamhold_show_mh\t\t= false;\r\nstatic qbool teamhold_show_ra\t\t= false;\r\nstatic qbool teamhold_show_ya\t\t= false;\r\nstatic qbool teamhold_show_ga\t\t= false;\r\n\r\nvoid TeamHold_DrawBars(int x, int y, int width, int height,\r\n\t\t\t\t\t\tfloat team1_percent, float team2_percent,\r\n\t\t\t\t\t\tint team1_color, int team2_color,\r\n\t\t\t\t\t\tfloat opacity)\r\n{\r\n\tint team1_width = 0;\r\n\tint team2_width = 0;\r\n\tint bar_height = 0;\r\n\r\n\tbar_height = Q_rint (height/2.0);\r\n\tteam1_width = (int) (width * team1_percent);\r\n\tteam2_width = (int) (width * team2_percent);\r\n\r\n\tclamp(team1_width, 0, width);\r\n\tclamp(team2_width, 0, width);\r\n\r\n\tDraw_AlphaFill(x, y, team1_width, bar_height, team1_color, opacity);\r\n\r\n\ty += bar_height;\r\n\r\n\tDraw_AlphaFill(x, y, team2_width, bar_height, team2_color, opacity);\r\n}\r\n\r\nvoid TeamHold_DrawPercentageBar(int x, int y, int width, int height,\r\n\t\t\t\t\t\t\t\tfloat team1_percent, float team2_percent,\r\n\t\t\t\t\t\t\t\tint team1_color, int team2_color,\r\n\t\t\t\t\t\t\t\tint show_text, int vertical,\r\n\t\t\t\t\t\t\t\tint vertical_text, float opacity)\r\n{\r\n\tint _x, _y;\r\n\tint _width, _height;\r\n\r\n\tif(vertical)\r\n\t{\r\n\t\t//\r\n\t\t// Draw vertical.\r\n\t\t//\r\n\r\n\t\t// Team 1.\r\n\t\t_x = x;\r\n\t\t_y = y;\r\n\t\t_width = max(0, width);\r\n\t\t_height = Q_rint(height * team1_percent);\r\n\t\t_height = max(0, height);\r\n\r\n\t\tDraw_AlphaFill(_x, _y, _width, _height, team1_color, opacity);\r\n\r\n\t\t// Team 2.\r\n\t\t_x = x;\r\n\t\t_y = Q_rint(y + (height * team1_percent));\r\n\t\t_width = max(0, width);\r\n\t\t_height = Q_rint(height * team2_percent);\r\n\t\t_height = max(0, _height);\r\n\r\n\t\tDraw_AlphaFill(_x, _y, _width, _height, team2_color, opacity);\r\n\r\n\t\t// Show the percentages in numbers also.\r\n\t\tif(show_text)\r\n\t\t{\r\n\t\t\t// TODO: Move this to a separate function (since it's prett much copy and paste for both teams).\r\n\t\t\t// Team 1.\r\n\t\t\tif(team1_percent > 0.05)\r\n\t\t\t{\r\n\t\t\t\tif(vertical_text)\r\n\t\t\t\t{\r\n\t\t\t\t\tint percent = 0;\r\n\t\t\t\t\tint percent10 = 0;\r\n\t\t\t\t\tint percent100 = 0;\r\n\r\n\t\t\t\t\t_x = x + (width / 2) - 4;\r\n\t\t\t\t\t_y = Q_rint(y + (height * team1_percent)/2 - 12);\r\n\r\n\t\t\t\t\tpercent = Q_rint(100 * team1_percent);\r\n\r\n\t\t\t\t\tif((percent100 = percent / 100))\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tDraw_String(_x, _y, va(\"%d\", percent100));\r\n\t\t\t\t\t\t_y += 8;\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\tif((percent10 = percent / 10))\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tDraw_String(_x, _y, va(\"%d\", percent10));\r\n\t\t\t\t\t\t_y += 8;\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\tDraw_String(_x, _y, va(\"%d\", percent % 10));\r\n\t\t\t\t\t_y += 8;\r\n\r\n\t\t\t\t\tDraw_String(_x, _y, \"%\");\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\t_x = x + (width / 2) - 12;\r\n\t\t\t\t\t_y = Q_rint(y + (height * team1_percent)/2 - 4);\r\n\t\t\t\t\tDraw_String(_x, _y, va(\"%2.0f%%\", 100 * team1_percent));\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\t// Team 2.\r\n\t\t\tif(team2_percent > 0.05)\r\n\t\t\t{\r\n\t\t\t\tif(vertical_text)\r\n\t\t\t\t{\r\n\t\t\t\t\tint percent = 0;\r\n\t\t\t\t\tint percent10 = 0;\r\n\t\t\t\t\tint percent100 = 0;\r\n\r\n\t\t\t\t\t_x = x + (width / 2) - 4;\r\n\t\t\t\t\t_y = Q_rint(y + (height * team1_percent) + (height * team2_percent)/2 - 12);\r\n\r\n\t\t\t\t\tpercent = Q_rint(100 * team2_percent);\r\n\r\n\t\t\t\t\tif((percent100 = percent / 100))\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tDraw_String(_x, _y, va(\"%d\", percent100));\r\n\t\t\t\t\t\t_y += 8;\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\tif((percent10 = percent / 10))\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tDraw_String(_x, _y, va(\"%d\", percent10));\r\n\t\t\t\t\t\t_y += 8;\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\tDraw_String(_x, _y, va(\"%d\", percent % 10));\r\n\t\t\t\t\t_y += 8;\r\n\r\n\t\t\t\t\tDraw_String(_x, _y, \"%\");\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\t_x = x + (width / 2) - 12;\r\n\t\t\t\t\t_y = Q_rint(y + (height * team1_percent) + (height * team2_percent)/2 - 4);\r\n\t\t\t\t\tDraw_String(_x, _y, va(\"%2.0f%%\", 100 * team2_percent));\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\telse\r\n\t{\r\n\t\t//\r\n\t\t// Draw horizontal.\r\n\t\t//\r\n\r\n\t\t// Team 1.\r\n\t\t_x = x;\r\n\t\t_y = y;\r\n\t\t_width = Q_rint(width * team1_percent);\r\n\t\t_width = max(0, _width);\r\n\t\t_height = max(0, height);\r\n\r\n\t\tDraw_AlphaFill(_x, _y, _width, _height, team1_color, opacity);\r\n\r\n\t\t// Team 2.\r\n\t\t_x = Q_rint(x + (width * team1_percent));\r\n\t\t_y = y;\r\n\t\t_width = Q_rint(width * team2_percent);\r\n\t\t_width = max(0, _width);\r\n\t\t_height = max(0, height);\r\n\r\n\t\tDraw_AlphaFill(_x, _y, _width, _height, team2_color, opacity);\r\n\r\n\t\t// Show the percentages in numbers also.\r\n\t\tif(show_text)\r\n\t\t{\r\n\t\t\t// Team 1.\r\n\t\t\tif(team1_percent > 0.05)\r\n\t\t\t{\r\n\t\t\t\t_x = Q_rint(x + (width * team1_percent)/2 - 8);\r\n\t\t\t\t_y = y + (height / 2) - 4;\r\n\t\t\t\tDraw_String(_x, _y, va(\"%2.0f%%\", 100 * team1_percent));\r\n\t\t\t}\r\n\r\n\t\t\t// Team 2.\r\n\t\t\tif(team2_percent > 0.05)\r\n\t\t\t{\r\n\t\t\t\t_x = Q_rint(x + (width * team1_percent) + (width * team2_percent)/2 - 8);\r\n\t\t\t\t_y = y + (height / 2) - 4;\r\n\t\t\t\tDraw_String(_x, _y, va(\"%2.0f%%\", 100 * team2_percent));\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n}\r\n\r\n#ifdef HAXX\r\nstatic void SCR_HUD_DrawTeamHoldBar(hud_t *hud)\r\n{\r\n\tint x, y;\r\n\tint height = 8;\r\n\tint width = 0;\r\n\tfloat team1_percent = 0;\r\n\tfloat team2_percent = 0;\r\n\r\n\tstatic cvar_t\r\n        *hud_teamholdbar_style = NULL,\r\n\t\t*hud_teamholdbar_opacity,\r\n\t\t*hud_teamholdbar_width,\r\n\t\t*hud_teamholdbar_height,\r\n\t\t*hud_teamholdbar_vertical,\r\n\t\t*hud_teamholdbar_show_text,\r\n\t\t*hud_teamholdbar_onlytp,\r\n\t\t*hud_teamholdbar_vertical_text;\r\n\r\n    if (hud_teamholdbar_style == NULL)    // first time\r\n    {\r\n\t\thud_teamholdbar_style\t\t\t\t= HUD_FindVar(hud, \"style\");\r\n\t\thud_teamholdbar_opacity\t\t\t\t= HUD_FindVar(hud, \"opacity\");\r\n\t\thud_teamholdbar_width\t\t\t\t= HUD_FindVar(hud, \"width\");\r\n\t\thud_teamholdbar_height\t\t\t\t= HUD_FindVar(hud, \"height\");\r\n\t\thud_teamholdbar_vertical\t\t\t= HUD_FindVar(hud, \"vertical\");\r\n\t\thud_teamholdbar_show_text\t\t\t= HUD_FindVar(hud, \"show_text\");\r\n\t\thud_teamholdbar_onlytp\t\t\t\t= HUD_FindVar(hud, \"onlytp\");\r\n\t\thud_teamholdbar_vertical_text\t\t= HUD_FindVar(hud, \"vertical_text\");\r\n    }\r\n\r\n\theight = max(1, hud_teamholdbar_height->value);\r\n\twidth = max(0, hud_teamholdbar_width->value);\r\n\r\n\t// Don't show when not in teamplay/demoplayback.\r\n\tif(!HUD_ShowInDemoplayback(hud_teamholdbar_onlytp->value))\r\n\t{\r\n\t\tHUD_PrepareDraw(hud, width , height, &x, &y);\r\n\t\treturn;\r\n\t}\r\n\r\n\tif (HUD_PrepareDraw(hud, width , height, &x, &y))\r\n\t{\r\n\t\t// We need something to work with.\r\n\t\tif(stats_grid != NULL)\r\n\t\t{\r\n\t\t\t// Check if we have any hold values to calculate from.\r\n\t\t\tif(stats_grid->teams[STATS_TEAM1].hold_count + stats_grid->teams[STATS_TEAM2].hold_count > 0)\r\n\t\t\t{\r\n\t\t\t\t// Calculate the percentage for the two teams for the \"team strength bar\".\r\n\t\t\t\tteam1_percent = ((float)stats_grid->teams[STATS_TEAM1].hold_count) / (stats_grid->teams[STATS_TEAM1].hold_count + stats_grid->teams[STATS_TEAM2].hold_count);\r\n\t\t\t\tteam2_percent = ((float)stats_grid->teams[STATS_TEAM2].hold_count) / (stats_grid->teams[STATS_TEAM1].hold_count + stats_grid->teams[STATS_TEAM2].hold_count);\r\n\r\n\t\t\t\tteam1_percent = fabs(max(0, team1_percent));\r\n\t\t\t\tteam2_percent = fabs(max(0, team2_percent));\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tDraw_AlphaFill(x, y, hud_teamholdbar_width->value, height, 0, hud_teamholdbar_opacity->value*0.5);\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\t// Draw the percentage bar.\r\n\t\t\tTeamHold_DrawPercentageBar(x, y, width, height,\r\n\t\t\t\tteam1_percent, team2_percent,\r\n\t\t\t\tstats_grid->teams[STATS_TEAM1].color,\r\n\t\t\t\tstats_grid->teams[STATS_TEAM2].color,\r\n\t\t\t\thud_teamholdbar_show_text->value,\r\n\t\t\t\thud_teamholdbar_vertical->value,\r\n\t\t\t\thud_teamholdbar_vertical_text->value,\r\n\t\t\t\thud_teamholdbar_opacity->value);\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\t// If there's no stats grid available we don't know what to show, so just show a black frame.\r\n\t\t\tDraw_AlphaFill(x, y, hud_teamholdbar_width->value, height, 0, hud_teamholdbar_opacity->value * 0.5);\r\n\t\t}\r\n\t}\r\n}\r\n#endif\r\n\r\nvoid TeamHold_OnChangeItemFilterInfo(cvar_t *var, char *oldvalue)\r\n{\r\n//\tchar *start = var->string;\r\n//\tchar *end = start;\r\n//\tint order = 0;\r\n\r\n\t// Parse the item filter.\r\n\tteamhold_show_rl\t\t= Utils_RegExpMatch(\"RL\",\tvar->string);\r\n\tteamhold_show_quad\t\t= Utils_RegExpMatch(\"QUAD\",\tvar->string);\r\n\tteamhold_show_ring\t\t= Utils_RegExpMatch(\"RING\",\tvar->string);\r\n\tteamhold_show_pent\t\t= Utils_RegExpMatch(\"PENT\",\tvar->string);\r\n\tteamhold_show_suit\t\t= Utils_RegExpMatch(\"SUIT\",\tvar->string);\r\n\tteamhold_show_lg\t\t= Utils_RegExpMatch(\"LG\",\tvar->string);\r\n\tteamhold_show_gl\t\t= Utils_RegExpMatch(\"GL\",\tvar->string);\r\n\tteamhold_show_sng\t\t= Utils_RegExpMatch(\"SNG\",\tvar->string);\r\n\tteamhold_show_mh\t\t= Utils_RegExpMatch(\"MH\",\tvar->string);\r\n\tteamhold_show_ra\t\t= Utils_RegExpMatch(\"RA\",\tvar->string);\r\n\tteamhold_show_ya\t\t= Utils_RegExpMatch(\"YA\",\tvar->string);\r\n\tteamhold_show_ga\t\t= Utils_RegExpMatch(\"GA\",\tvar->string);\r\n#ifdef HAXX\r\n\t// Reset the ordering of the items.\r\n\tStatsGrid_ResetHoldItemsOrder();\r\n\r\n\t// Trim spaces from the start of the word.\r\n\twhile (*start && *start == ' ')\r\n\t{\r\n\t\tstart++;\r\n\t}\r\n\r\n\tend = start;\r\n\r\n\t// Go through the string word for word and set a\r\n\t// rising order for each hold item based on their\r\n\t// order in the string.\r\n\twhile (*end)\r\n\t{\r\n\t\tif (*end != ' ')\r\n\t\t{\r\n\t\t\t// Not at the end of the word yet.\r\n\t\t\tend++;\r\n\t\t\tcontinue;\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\t// We've found a word end.\r\n\t\t\tchar temp[256];\r\n\r\n\t\t\t// Try matching the current word with a hold item\r\n\t\t\t// and set it's ordering according to it's placement\r\n\t\t\t// in the string.\r\n\t\t\tstrlcpy (temp, start, min(end - start, sizeof(temp)));\r\n\t\t\tStatsGrid_SetHoldItemOrder(temp, order);\r\n\t\t\torder++;\r\n\r\n\t\t\t// Get rid of any additional spaces.\r\n\t\t\twhile (*end && *end == ' ')\r\n\t\t\t{\r\n\t\t\t\tend++;\r\n\t\t\t}\r\n\r\n\t\t\t// Start trying to find a new word.\r\n\t\t\tstart = end;\r\n\t\t}\r\n\t}\r\n\r\n\t// Order the hold items.\r\n\tStatsGrid_SortHoldItems();\r\n#endif\r\n}\r\n\r\n#define HUD_TEAMHOLDINFO_STYLE_TEAM_NAMES\t\t0\r\n#define HUD_TEAMHOLDINFO_STYLE_PERCENT_BARS\t\t1\r\n#define HUD_TEAMHOLDINFO_STYLE_PERCENT_BARS2\t2\r\n\r\n#ifdef HAXX\r\nstatic void SCR_HUD_DrawTeamHoldInfo(hud_t *hud)\r\n{\r\n\tint i;\r\n\tint x, y;\r\n\tint width, height;\r\n\r\n\tstatic cvar_t\r\n        *hud_teamholdinfo_style = NULL,\r\n\t\t*hud_teamholdinfo_opacity,\r\n\t\t*hud_teamholdinfo_width,\r\n\t\t*hud_teamholdinfo_height,\r\n\t\t*hud_teamholdinfo_onlytp,\r\n\t\t*hud_teamholdinfo_itemfilter;\r\n\r\n    if (hud_teamholdinfo_style == NULL)    // first time\r\n    {\r\n\t\tchar val[256];\r\n\r\n\t\thud_teamholdinfo_style\t\t\t\t= HUD_FindVar(hud, \"style\");\r\n\t\thud_teamholdinfo_opacity\t\t\t= HUD_FindVar(hud, \"opacity\");\r\n\t\thud_teamholdinfo_width\t\t\t\t= HUD_FindVar(hud, \"width\");\r\n\t\thud_teamholdinfo_height\t\t\t\t= HUD_FindVar(hud, \"height\");\r\n\t\thud_teamholdinfo_onlytp\t\t\t\t= HUD_FindVar(hud, \"onlytp\");\r\n\t\thud_teamholdinfo_itemfilter\t\t\t= HUD_FindVar(hud, \"itemfilter\");\r\n\r\n\t\t// Unecessary to parse the item filter string on each frame.\r\n\t\thud_teamholdinfo_itemfilter->OnChange = TeamHold_OnChangeItemFilterInfo;\r\n\r\n\t\t// Parse the item filter the first time (trigger the OnChange function above).\r\n\t\tstrlcpy (val, hud_teamholdinfo_itemfilter->string, sizeof(val));\r\n\t\tCvar_Set (hud_teamholdinfo_itemfilter, val);\r\n    }\r\n\r\n\t// Get the height based on how many items we have, or what the user has set it to.\r\n\theight = max(0, hud_teamholdinfo_height->value);\r\n\twidth = max(0, hud_teamholdinfo_width->value);\r\n\r\n\t// Don't show when not in teamplay/demoplayback.\r\n\tif(!HUD_ShowInDemoplayback(hud_teamholdinfo_onlytp->value))\r\n\t{\r\n\t\tHUD_PrepareDraw(hud, width , height, &x, &y);\r\n\t\treturn;\r\n\t}\r\n\r\n\t// We don't have anything to show.\r\n\tif(stats_important_ents == NULL || stats_grid == NULL)\r\n\t{\r\n\t\tHUD_PrepareDraw(hud, width , height, &x, &y);\r\n\t\treturn;\r\n\t}\r\n\r\n\tif (HUD_PrepareDraw(hud, width , height, &x, &y))\r\n\t{\r\n\t\tint _y = 0;\r\n\r\n\t\t_y = y;\r\n\r\n\t\t// Go through all the items and print the stats for them.\r\n\t\tfor(i = 0; i < stats_important_ents->count; i++)\r\n\t\t{\r\n\t\t\tfloat team1_percent;\r\n\t\t\tfloat team2_percent;\r\n\t\t\tint team1_hold_count = 0;\r\n\t\t\tint team2_hold_count = 0;\r\n\t\t\tint names_width = 0;\r\n\r\n\t\t\t// Don't draw outside the specified height.\r\n\t\t\tif((_y - y) + 8 > height)\r\n\t\t\t{\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\r\n\t\t\t// If the item isn't of the specified type, then skip it.\r\n\t\t\tif(!(\t(teamhold_show_rl\t&& !strncmp(stats_important_ents->list[i].name, \"RL\",\t2))\r\n\t\t\t\t||\t(teamhold_show_quad\t&& !strncmp(stats_important_ents->list[i].name, \"QUAD\", 4))\r\n\t\t\t\t||\t(teamhold_show_ring\t&& !strncmp(stats_important_ents->list[i].name, \"RING\", 4))\r\n\t\t\t\t||\t(teamhold_show_pent\t&& !strncmp(stats_important_ents->list[i].name, \"PENT\", 4))\r\n\t\t\t\t||\t(teamhold_show_suit\t&& !strncmp(stats_important_ents->list[i].name, \"SUIT\", 4))\r\n\t\t\t\t||\t(teamhold_show_lg\t&& !strncmp(stats_important_ents->list[i].name, \"LG\",\t2))\r\n\t\t\t\t||\t(teamhold_show_gl\t&& !strncmp(stats_important_ents->list[i].name, \"GL\",\t2))\r\n\t\t\t\t||\t(teamhold_show_sng\t&& !strncmp(stats_important_ents->list[i].name, \"SNG\",\t3))\r\n\t\t\t\t||\t(teamhold_show_mh\t&& !strncmp(stats_important_ents->list[i].name, \"MH\",\t2))\r\n\t\t\t\t||\t(teamhold_show_ra\t&& !strncmp(stats_important_ents->list[i].name, \"RA\",\t2))\r\n\t\t\t\t||\t(teamhold_show_ya\t&& !strncmp(stats_important_ents->list[i].name, \"YA\",\t2))\r\n\t\t\t\t||\t(teamhold_show_ga\t&& !strncmp(stats_important_ents->list[i].name, \"GA\",\t2))\r\n\t\t\t\t))\r\n\t\t\t{\r\n\t\t\t\tcontinue;\r\n\t\t\t}\r\n\r\n\t\t\t// Calculate the width of the longest item name so we can use it for padding.\r\n\t\t\tnames_width = 8 * (stats_important_ents->longest_name + 1);\r\n\r\n\t\t\t// Calculate the percentages of this item that the two teams holds.\r\n\t\t\tteam1_hold_count = stats_important_ents->list[i].teams_hold_count[STATS_TEAM1];\r\n\t\t\tteam2_hold_count = stats_important_ents->list[i].teams_hold_count[STATS_TEAM2];\r\n\r\n\t\t\tteam1_percent = ((float)team1_hold_count) / (team1_hold_count + team2_hold_count);\r\n\t\t\tteam2_percent = ((float)team2_hold_count) / (team1_hold_count + team2_hold_count);\r\n\r\n\t\t\tteam1_percent = fabs(max(0, team1_percent));\r\n\t\t\tteam2_percent = fabs(max(0, team2_percent));\r\n\r\n\t\t\t// Write the name of the item.\r\n\t\t\tDraw_ColoredString(x, _y, va(\"&cff0%s:\", stats_important_ents->list[i].name), 0);\r\n\r\n\t\t\tif(hud_teamholdinfo_style->value == HUD_TEAMHOLDINFO_STYLE_TEAM_NAMES)\r\n\t\t\t{\r\n\t\t\t\t//\r\n\t\t\t\t// Prints the team name that holds the item.\r\n\t\t\t\t//\r\n\t\t\t\tif(team1_percent > team2_percent)\r\n\t\t\t\t{\r\n\t\t\t\t\tDraw_ColoredString(x + names_width, _y, stats_important_ents->teams[STATS_TEAM1].name, 0);\r\n\t\t\t\t}\r\n\t\t\t\telse if(team1_percent < team2_percent)\r\n\t\t\t\t{\r\n\t\t\t\t\tDraw_ColoredString(x + names_width, _y, stats_important_ents->teams[STATS_TEAM2].name, 0);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\telse if(hud_teamholdinfo_style->value == HUD_TEAMHOLDINFO_STYLE_PERCENT_BARS)\r\n\t\t\t{\r\n\t\t\t\t//\r\n\t\t\t\t// Show a percenteage bar for the item.\r\n\t\t\t\t//\r\n\t\t\t\tTeamHold_DrawPercentageBar(x + names_width, _y,\r\n\t\t\t\t\tQ_rint(hud_teamholdinfo_width->value - names_width), 8,\r\n\t\t\t\t\tteam1_percent, team2_percent,\r\n\t\t\t\t\tstats_important_ents->teams[STATS_TEAM1].color,\r\n\t\t\t\t\tstats_important_ents->teams[STATS_TEAM2].color,\r\n\t\t\t\t\t0, // Don't show percentage values, get's too cluttered.\r\n\t\t\t\t\tfalse,\r\n\t\t\t\t\tfalse,\r\n\t\t\t\t\thud_teamholdinfo_opacity->value);\r\n\t\t\t}\r\n\t\t\telse if(hud_teamholdinfo_style->value == HUD_TEAMHOLDINFO_STYLE_PERCENT_BARS2)\r\n\t\t\t{\r\n\t\t\t\tTeamHold_DrawBars(x + names_width, _y,\r\n\t\t\t\t\tQ_rint(hud_teamholdinfo_width->value - names_width), 8,\r\n\t\t\t\t\tteam1_percent, team2_percent,\r\n\t\t\t\t\tstats_important_ents->teams[STATS_TEAM1].color,\r\n\t\t\t\t\tstats_important_ents->teams[STATS_TEAM2].color,\r\n\t\t\t\t\thud_teamholdinfo_opacity->value);\r\n\t\t\t}\r\n\r\n\t\t\t// Next line.\r\n\t\t\t_y += 8;\r\n\t\t}\r\n\t}\r\n}\r\n#endif\r\n\r\nstatic int SCR_HudDrawTeamInfoPlayer(teamplayerinfo_t *ti_cl, int x, int y, int maxname, int maxloc, qbool width_only, hud_t *hud);\r\n\r\n#define FONTWIDTH 8\r\nstatic void SCR_HUD_DrawTeamInfo(hud_t *hud)\r\n{\r\n\tint x, y, _y, width, height;\r\n\tint i, j, k, slots_num, maxname, maxloc;\r\n\tchar tmp[1024], *nick;\r\n\tteamplayerinfo_t ti_clients[MAX_CLIENTS];\r\n\r\n\textern qbool hud_editor;\r\n\r\n\tstatic cvar_t\r\n\t\t*hud_teaminfo_weapon_style = NULL,\r\n                *hud_teaminfo_align_right,\r\n\t\t*hud_teaminfo_loc_width,\r\n\t\t*hud_teaminfo_name_width,\r\n\t\t*hud_teaminfo_show_enemies,\r\n\t\t*hud_teaminfo_show_self,\r\n\t\t*hud_teaminfo_scale;\r\n\r\n\tif (hud_teaminfo_weapon_style == NULL)    // first time\r\n\t{\r\n\t\thud_teaminfo_weapon_style\t\t\t= HUD_FindVar(hud, \"weapon_style\");\r\n\t\thud_teaminfo_align_right\t\t\t= HUD_FindVar(hud, \"align_right\");\r\n\t\thud_teaminfo_loc_width\t\t\t\t= HUD_FindVar(hud, \"loc_width\");\r\n\t\thud_teaminfo_name_width\t\t\t\t= HUD_FindVar(hud, \"name_width\");\r\n\t\thud_teaminfo_show_enemies\t\t\t= HUD_FindVar(hud, \"show_enemies\");\r\n\t\thud_teaminfo_show_self\t\t\t\t= HUD_FindVar(hud, \"show_self\");\r\n\t\thud_teaminfo_scale\t\t\t\t\t= HUD_FindVar(hud, \"scale\");\r\n\t}\r\n\r\n\t// Don't update hud item unless first view is beeing displayed\r\n//\tif ( CURRVIEW != 1 && CURRVIEW != 0)\r\n//\t\treturn;\r\n\r\n\tslots_num = clientfuncs->GetTeamInfo?clientfuncs->GetTeamInfo(ti_clients, countof(ti_clients), hud_teaminfo_show_enemies->ival, hud_teaminfo_show_self->ival?-1:0):0;\r\n\r\n\t// fill data we require to draw teaminfo\r\n\tfor ( maxloc = maxname = i = 0; i < slots_num; i++ ) {\r\n\t\t// dynamically guess max length of name/location\r\n\t\tnick = (ti_clients[i].nick[0] ? ti_clients[i].nick : cl.players[i].name); // we use nick or name\r\n\t\tmaxname = max(maxname, strlen(TP_ParseFunChars(nick)));\r\n\r\n\t\tstrlcpy(tmp, TP_LocationName(ti_clients[i].org), sizeof(tmp));\r\n\t\tmaxloc  = max(maxloc,  strlen(TP_ParseFunChars(tmp)));\r\n\t}\r\n\r\n\t// well, better use fixed loc length\r\n\tmaxloc  = bound(0, hud_teaminfo_loc_width->ival, 100);\r\n\t// limit name length\r\n\tmaxname = bound(0, maxname, hud_teaminfo_name_width->ival);\r\n\r\n\t// this does't draw anything, just calculate width\r\n\twidth = FONTWIDTH * hud_teaminfo_scale->value * SCR_HudDrawTeamInfoPlayer(&ti_clients[0], 0, 0, maxname, maxloc, true, hud);\r\n\theight = FONTWIDTH * hud_teaminfo_scale->value * (hud_teaminfo_show_enemies->ival?slots_num+n_teams:slots_num);\r\n\r\n\tif (hud_editor)\r\n\t\tHUD_PrepareDraw(hud, width , FONTWIDTH, &x, &y);\r\n\r\n\tif ( !slots_num )\r\n\t\treturn;\r\n\r\n\tif (!cl.teamplay)  // non teamplay mode\r\n\t\treturn;\r\n\r\n\tif (!HUD_PrepareDraw(hud, width , height, &x, &y))\r\n\t\treturn;\r\n\r\n\t_y = y ;\r\n\tx = (hud_teaminfo_align_right->value ? x - (width * (FONTWIDTH * hud_teaminfo_scale->value)) : x);\r\n\r\n\t// If multiple teams are displayed then sort the display and print team header on overlay\r\n\tk=0;\r\n\tif (hud_teaminfo_show_enemies->ival)\r\n\t{\r\n\t\twhile (sorted_teams[k].name)\r\n\t\t{\r\n\t\t\tDraw_SString (x, _y, sorted_teams[k].name, hud_teaminfo_scale->value);\r\n\t\t\tsprintf(tmp,\"%s %i\",TP_ParseFunChars(\"$.\"), sorted_teams[k].frags);\r\n\t\t\tDraw_SString (x+(strlen(sorted_teams[k].name)+1)*FONTWIDTH, _y, tmp, hud_teaminfo_scale->value);\r\n\t\t\t_y += FONTWIDTH * hud_teaminfo_scale->value;\r\n\t\t\tfor ( j = 0; j < slots_num; j++ ) \r\n\t\t\t{\r\n\t\t\t\ti = ti_clients[j].client;\r\n\t\t\t\tif (!strcmp(cl.players[i].team,sorted_teams[k].name))\r\n\t\t\t\t{\r\n\t\t\t\t\tSCR_HudDrawTeamInfoPlayer(&ti_clients[j], x, _y, maxname, maxloc, false, hud);\r\n\t\t\t\t\t_y += FONTWIDTH * hud_teaminfo_scale->value;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\tk++;\r\n\t\t}\r\n\t}\r\n\telse \r\n\t{\r\n\t\tfor ( j = 0; j < slots_num; j++ ) {\r\n\t\t\tSCR_HudDrawTeamInfoPlayer(&ti_clients[j], x, _y, maxname, maxloc, false, hud);\r\n\t\t\t_y += FONTWIDTH * hud_teaminfo_scale->value;\r\n\t\t}\r\n\t}\r\n}\r\n\r\nqbool Has_Both_RL_and_LG (int flags) { return (flags & IT_ROCKET_LAUNCHER) && (flags & IT_LIGHTNING); }\r\n#define FONTWIDTH 8\r\nvoid str_align_right (char *target, size_t size, const char *source, size_t length)\r\n{\r\n\tif (length > size - 1)\r\n\t\tlength = size - 1;\r\n\r\n\tif (strlen(source) >= length) {\r\n\t\tstrlcpy(target, source, size);\r\n\t\ttarget[length] = 0;\r\n\t} else {\r\n\t\tint i;\r\n\r\n\t\tfor (i = 0; i < length - strlen(source); i++) {\r\n\t\t\ttarget[i] = ' ';\r\n\t\t}\r\n\r\n\t\tstrlcpy(target + i, source, size - i);\r\n\t}\r\n}\r\nint Player_GetTrackId(int uid)\r\n{\r\n\treturn uid;\r\n}\r\nunsigned int BestWeaponFromStatItems(unsigned int items)\r\n{\r\n\tint i;\r\n\tfor (i = 1<<7; i; i>>=1)\r\n\t{\r\n\t\tif (items & i)\r\n\t\t\treturn i;\r\n\t}\r\n\treturn 0;\r\n}\r\nmpic_t * SCR_GetWeaponIconByFlag (int flag)\r\n{\r\n\tint i, j;\r\n\tfor (i = 0, j = 1; i < 7; i++, j*=2)\r\n\t{\r\n\t\tif (flag == j)\r\n\t\t\treturn sb_weapons[0][i];\r\n\t}\r\n\treturn NULL;\r\n}\r\nstatic int SCR_HudDrawTeamInfoPlayer(teamplayerinfo_t *ti_cl, int x, int y, int maxname, int maxloc, qbool width_only, hud_t *hud)\r\n{\r\n\textern mpic_t * SCR_GetWeaponIconByFlag (int flag);\r\n\r\n\tchar *s, *loc, tmp[1024], tmp2[1024], *aclr;\r\n\tint x_in = x; // save x\r\n\tint i;\r\n\tmpic_t *pic;\r\n\tfloat scale = HUD_FindVar(hud, \"scale\")->value;\r\n\r\n\tif (!ti_cl)\r\n\t\treturn 0;\r\n\r\n\ti = ti_cl->client;\r\n\r\n\tif (i < 0 || i >= MAX_CLIENTS)\r\n\t{\r\n\t\tCom_DPrintf(\"SCR_Draw_TeamInfoPlayer: wrong client %d\\n\", i);\r\n\t\treturn 0;\r\n\t}\r\n\r\n\t// this limit len of string because TP_ParseFunChars() do not check overflow\r\n\tstrlcpy(tmp2, HUD_FindVar(hud, \"layout\")->string , sizeof(tmp2));\r\n\tstrlcpy(tmp2, TP_ParseFunChars(tmp2), sizeof(tmp2));\r\n\ts = tmp2;\r\n\r\n\t//\r\n\t// parse/draw string like this \"%n %h:%a %l %p %w\"\r\n\t//\r\n\r\n\tfor ( ; *s; s++) {\r\n\t\tswitch( (int) s[0] ) {\r\n\t\tcase '%':\r\n\r\n\t\t\ts++; // advance\r\n\r\n\t\t\tswitch( (int) s[0] ) {\r\n\t\t\tcase 'n': // draw name\r\n\r\n\t\t\t\tif(!width_only) {\r\n\t\t\t\t\tchar *nick = TP_ParseFunChars(ti_cl->nick[0] ? ti_cl->nick : cl.players[i].name);\r\n\t\t\t\t\tstr_align_right(tmp, sizeof(tmp), nick, maxname);\r\n\t\t\t\t\tDraw_SString (x, y, tmp, scale);\r\n\t\t\t\t}\r\n\t\t\t\tx += maxname * FONTWIDTH * scale;\r\n\r\n\t\t\t\tbreak;\r\n\t\t\tcase 'w': // draw \"best\" weapon icon/name\r\n\r\n\t\t\t\tswitch (HUD_FindVar(hud, \"weapon_style\")->ival) {\r\n\t\t\t\tcase 1:\r\n\t\t\t\t\tif(!width_only) {\r\n\t\t\t\t\t\tif (Has_Both_RL_and_LG(ti_cl->items)) {\r\n\t\t\t\t\t\t\tchar *weap_str = cvarfuncs->GetNVFDG(\"tp_name_rlg\", \"rlg\", 0, NULL, NULL)->string;\r\n\t\t\t\t\t\t\tchar weap_white_stripped[32];\r\n\t\t\t\t\t\t\tUtil_SkipChars(weap_str, \"{}\", weap_white_stripped, 32);\r\n\t\t\t\t\t\t\tDraw_ColoredString (x, y, weap_white_stripped, false);\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\telse {\r\n\t\t\t\t\t\t\tchar *weap_str = TP_ItemName(BestWeaponFromStatItems( ti_cl->items ));\r\n\t\t\t\t\t\t\tchar weap_white_stripped[32];\r\n\t\t\t\t\t\t\tUtil_SkipChars(weap_str, \"{}\", weap_white_stripped, 32);\r\n\t\t\t\t\t\t\tDraw_ColoredString (x, y, weap_white_stripped, false);\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t}\r\n\t\t\t\t\tx += 3 * FONTWIDTH * scale;\r\n\r\n\t\t\t\t\tbreak;\r\n\t\t\t\tdefault: // draw image by default\r\n\t\t\t\t\tif(!width_only)\r\n\t\t\t\t\t\tif ( (pic = SCR_GetWeaponIconByFlag(BestWeaponFromStatItems( ti_cl->items ))) )\r\n\t\t\t\t\t\t\tDraw_SPic (x, y, pic, 0.5 * scale);\r\n\t\t\t\t\tx += 2 * FONTWIDTH * scale;\r\n\r\n\t\t\t\t\tbreak;\r\n\t\t\t\t}\r\n\r\n\t\t\t\tbreak;\r\n\t\t\tcase 'h': // draw health, padding with space on left side\r\n\t\t\tcase 'H': // draw health, padding with space on right side\r\n\r\n\t\t\t\tif(!width_only) {\r\n\t\t\t\t\tsnprintf(tmp, sizeof(tmp), (s[0] == 'h' ? \"%s%3d\" : \"%s%-3d\"), (ti_cl->health < HUD_FindVar(hud, \"low_health\")->ival ? \"&cf00\" : \"\"), (int)ti_cl->health);\r\n\t\t\t\t\tDraw_SString (x, y, tmp, scale);\r\n\t\t\t\t}\r\n\t\t\t\tx += 3 * FONTWIDTH * scale;\r\n\r\n\t\t\t\tbreak;\r\n\t\t\tcase 'a': // draw armor, padded with space on left side\r\n\t\t\tcase 'A': // draw armor, padded with space on right side\r\n\r\n\t\t\t\taclr = \"\";\r\n\r\n\t\t\t\t//\r\n\t\t\t\t// different styles of armor\r\n\t\t\t\t//\r\n\t\t\t\tswitch (HUD_FindVar(hud,\"armor_style\")->ival) {\r\n\t\t\t\tcase 1: // image prefixed armor value\r\n\t\t\t\t\tif(!width_only) {\r\n\t\t\t\t\t\tif (ti_cl->items & IT_ARMOR3)\r\n\t\t\t\t\t\t\tDraw_SPic (x, y, sb_armor[2], 1.0/3 * scale);\r\n\t\t\t\t\t\telse if (ti_cl->items & IT_ARMOR2)\r\n\t\t\t\t\t\t\tDraw_SPic (x, y, sb_armor[1], 1.0/3 * scale);\r\n\t\t\t\t\t\telse if (ti_cl->items & IT_ARMOR1)\r\n\t\t\t\t\t\t\tDraw_SPic (x, y, sb_armor[0], 1.0/3 * scale);\r\n\t\t\t\t\t}\r\n\t\t\t\t\tx += FONTWIDTH * scale;\r\n\r\n\t\t\t\t\tbreak;\r\n\t\t\t\tcase 2: // colored background of armor value\r\n                                        /*\r\n\t\t\t\t\tif(!width_only) {\r\n\t\t\t\t\t\tbyte col[4] = {255, 255, 255, 0};\r\n\r\n\t\t\t\t\t\tif (ti_cl->items & IT_ARMOR3) {\r\n\t\t\t\t\t\t\tcol[0] = 255; col[1] =   0; col[2] =   0; col[3] = 255;\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\telse if (ti_cl->items & IT_ARMOR2) {\r\n\t\t\t\t\t\t\tcol[0] = 255; col[1] = 255; col[2] =   0; col[3] = 255;\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\telse if (ti_cl->items & IT_ARMOR1) {\r\n\t\t\t\t\t\t\tcol[0] =   0; col[1] = 255; col[2] =   0; col[3] = 255;\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t}\r\n                                        */\r\n\r\n\t\t\t\t\tbreak;\r\n\t\t\t\tcase 3: // colored armor value\r\n\t\t\t\t\tif(!width_only) {\r\n\t\t\t\t\t\tif (ti_cl->items & IT_ARMOR3)\r\n\t\t\t\t\t\t\taclr = \"&cf00\";\r\n\t\t\t\t\t\telse if (ti_cl->items & IT_ARMOR2)\r\n\t\t\t\t\t\t\taclr = \"&cff0\";\r\n\t\t\t\t\t\telse if (ti_cl->items & IT_ARMOR1)\r\n\t\t\t\t\t\t\taclr = \"&c0f0\";\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\tbreak;\r\n\t\t\t\tcase 4: // armor value prefixed with letter\r\n\t\t\t\t\tif(!width_only) {\r\n\t\t\t\t\t\tif (ti_cl->items & IT_ARMOR3)\r\n\t\t\t\t\t\t\tDraw_SString (x, y, \"r\", scale);\r\n\t\t\t\t\t\telse if (ti_cl->items & IT_ARMOR2)\r\n\t\t\t\t\t\t\tDraw_SString (x, y, \"y\", scale);\r\n\t\t\t\t\t\telse if (ti_cl->items & IT_ARMOR1)\r\n\t\t\t\t\t\t\tDraw_SString (x, y, \"g\", scale);\r\n\t\t\t\t\t}\r\n\t\t\t\t\tx += FONTWIDTH * scale;\r\n\r\n\t\t\t\t\tbreak;\r\n\t\t\t\t}\r\n\r\n\t\t\t\tif(!width_only) { // value drawed no matter which style\r\n\t\t\t\t\tsnprintf(tmp, sizeof(tmp), (s[0] == 'a' ? \"%s%3d\" : \"%s%-3d\"), aclr, (int)ti_cl->armor);\r\n\t\t\t\t\tDraw_SString (x, y, tmp, scale);\r\n\t\t\t\t}\r\n\t\t\t\tx += 3 * FONTWIDTH * scale;\r\n\r\n\t\t\t\tbreak;\r\n\t\t\tcase 'l': // draw location\r\n\r\n\t\t\t\tif(!width_only) {\r\n\t\t\t\t\tloc = TP_LocationName(ti_cl->org);\r\n\t\t\t\t\tif (!loc[0])\r\n\t\t\t\t\t\tloc = \"unknown\";\r\n\r\n\t\t\t\t\tstr_align_right(tmp, sizeof(tmp), TP_ParseFunChars(loc), maxloc);\r\n\t\t\t\t\tDraw_SString (x, y, tmp, scale);\r\n\t\t\t\t}\r\n\t\t\t\tx += maxloc * FONTWIDTH * scale;\r\n\r\n\t\t\t\tbreak;\r\n\t\t\tcase 'p': // draw powerups\r\n\t\t\tswitch (HUD_FindVar(hud, \"powerup_style\")->ival) {\r\n\t\t\t\tcase 1: // quad/pent/ring image\r\n\t\t\t\t\tif(!width_only) {\r\n\t\t\t\t\t\tif (ti_cl->items & IT_QUAD)\r\n\t\t\t\t\t\t\tDraw_SPic (x, y, sb_items[5], 1.0/2);\r\n\t\t\t\t\t\tx += FONTWIDTH;\r\n\t\t\t\t\t\tif (ti_cl->items & IT_INVULNERABILITY)\r\n\t\t\t\t\t\t\tDraw_SPic (x, y, sb_items[3], 1.0/2);\r\n\t\t\t\t\t\tx += FONTWIDTH;\r\n\t\t\t\t\t\tif (ti_cl->items & IT_INVISIBILITY)\r\n\t\t\t\t\t\t\tDraw_SPic (x, y, sb_items[2], 1.0/2);\r\n\t\t\t\t\t\tx += FONTWIDTH;\r\n\t\t\t\t\t}\r\n\t\t\t\t\telse { x += 3* FONTWIDTH; }\r\n\t\t\t\t\tbreak;\r\n\r\n\t\t\t\tcase 2: // player powerup face\r\n\t\t\t\t\tif(!width_only) {\r\n\t\t\t\t\t\tif ( sb_face_quad && (ti_cl->items & IT_QUAD))\r\n\t\t\t\t\t\t\tDraw_SPic (x, y, sb_face_quad, 1.0/3);\r\n\t\t\t\t\t\tx += FONTWIDTH;\r\n\t\t\t\t\t\tif ( sb_face_invuln && (ti_cl->items & IT_INVULNERABILITY))\r\n\t\t\t\t\t\t\tDraw_SPic (x, y, sb_face_invuln, 1.0/3);\r\n\t\t\t\t\t\tx += FONTWIDTH;\r\n\t\t\t\t\t\tif ( sb_face_invis && (ti_cl->items & IT_INVISIBILITY))\r\n\t\t\t\t\t\t\tDraw_SPic (x, y, sb_face_invis, 1.0/3);\r\n\t\t\t\t\t\tx += FONTWIDTH;\r\n\t\t\t\t\t}\r\n\t\t\t\t\telse { x += 3* FONTWIDTH; }\r\n\t\t\t\t\tbreak;\r\n\r\n\t\t\t\tcase 3: // colored font (QPR)\r\n\t\t\t\t\tif(!width_only) {\r\n\t\t\t\t\t\tif (ti_cl->items & IT_QUAD)\r\n\t\t\t\t\t\t\tDraw_ColoredString (x, y, \"&c03fQ\", false);\r\n\t\t\t\t\t\tx += FONTWIDTH;\r\n\t\t\t\t\t\tif (ti_cl->items & IT_INVULNERABILITY)\r\n\t\t\t\t\t\t\tDraw_ColoredString (x, y, \"&cf00P\", false);\r\n\t\t\t\t\t\tx += FONTWIDTH;\r\n\t\t\t\t\t\tif (ti_cl->items & IT_INVISIBILITY)\r\n\t\t\t\t\t\t\tDraw_ColoredString (x, y, \"&cff0R\", false);\r\n\t\t\t\t\t\tx += FONTWIDTH;\r\n\t\t\t\t\t}\r\n\t\t\t\t\telse { x += 3* FONTWIDTH; }\r\n\t\t\t\t\tbreak;\r\n\t\t\t}\r\n\t\t\tbreak;\r\n\r\n\t\t\tcase 't':\r\n\t\t\t\tif(!width_only)\r\n\t\t\t\t{\r\n\t\t\t\t\tsprintf(tmp, \"%i\", Player_GetTrackId(cl.players[ti_cl->client].userid));\r\n\t\t\t\t\tDraw_SString (x, y, tmp, scale);\r\n\t\t\t\t}\r\n\t\t\t\tx += FONTWIDTH * scale; // will break if tracknumber is double digits\r\n\t\t\t\tbreak;\r\n\r\n\t\t\tcase '%': // wow, %% result in one %, how smart\r\n\r\n\t\t\t\tif(!width_only)\r\n\t\t\t\t\tDraw_SString (x, y, \"%\", scale);\r\n\t\t\t\tx += FONTWIDTH * scale;\r\n\r\n\t\t\t\tbreak;\r\n\r\n\t\t\tdefault: // print %x - that mean sequence unknown\r\n\r\n\t\t\t\tif(!width_only) {\r\n\t\t\t\t\tsnprintf(tmp, sizeof(tmp), \"%%%c\", s[0]);\r\n\t\t\t\t\tDraw_SString (x, y, tmp, scale);\r\n\t\t\t\t}\r\n\t\t\t\tx += (s[0] ? 2 : 1) * FONTWIDTH * scale;\r\n\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\r\n\t\t\tbreak;\r\n\r\n\t\tdefault: // print x\r\n\t\t\tif(!width_only) {\r\n\t\t\t\tsnprintf(tmp, sizeof(tmp), \"%c\", s[0]);\r\n\t\t\t\tif (s[0] != ' ') // inhuman smart optimization, do not print space!\r\n\t\t\t\t\tDraw_SString (x, y, tmp, scale);\r\n\t\t\t}\r\n\t\t\tx += FONTWIDTH * scale;\r\n\r\n\t\t\tbreak;\r\n\t\t}\r\n\t}\r\n\r\n\treturn (x - x_in) / (FONTWIDTH * scale); // return width\r\n}\r\n\r\n#ifdef HAXX\r\nvoid SCR_HUD_DrawItemsClock(hud_t *hud)\r\n{\r\n\textern qbool hud_editor;\r\n\tint width, height;\r\n\tint x, y;\r\n\tstatic cvar_t *hud_itemsclock_timelimit = NULL, *hud_itemsclock_style;\r\n\r\n\tif (hud_itemsclock_timelimit == NULL) {\r\n\t\thud_itemsclock_timelimit = HUD_FindVar(hud, \"timelimit\");\r\n\t\thud_itemsclock_style = HUD_FindVar(hud, \"style\");\r\n\t}\r\n\r\n\tMVD_ClockList_TopItems_DimensionsGet(hud_itemsclock_timelimit->value, hud_itemsclock_style->ival, &width, &height);\r\n\t\r\n\tif (hud_editor)\r\n\t\tHUD_PrepareDraw(hud, width, LETTERHEIGHT, &x, &y);\r\n\r\n\tif (!height)\r\n\t\treturn;\r\n\r\n    if (!HUD_PrepareDraw(hud, width, height, &x, &y))\r\n        return;\r\n\t\r\n\tMVD_ClockList_TopItems_Draw(hud_itemsclock_timelimit->value, hud_itemsclock_style->ival, x, y);\r\n}\r\n#endif\r\n\r\n//\r\n// TODO: decide what to do in freefly mode (and how to catch it?!), now all score_* hud elements just draws \"0\"\r\n//\r\nvoid SCR_HUD_DrawScoresTeam(hud_t *hud)\r\n{\r\n    static cvar_t *scale = NULL, *style, *digits, *align, *colorize;\r\n\tint value = 0;\r\n\tint i;\r\n\r\n    if (scale == NULL)  // first time called\r\n    {\r\n        scale\t\t= HUD_FindVar(hud, \"scale\");\r\n        style\t\t= HUD_FindVar(hud, \"style\");\r\n        digits\t\t= HUD_FindVar(hud, \"digits\");\r\n        align\t\t= HUD_FindVar(hud, \"align\");\r\n\t\tcolorize\t= HUD_FindVar(hud, \"colorize\");\r\n    }\r\n\t\r\n\t//\r\n\t// AAS: someone please tell me how to do it in a proper way!\r\n\t//\r\n\tif(cl.teamplay)\r\n\t{\r\n\t\tfor(i = 0; i < n_teams; i++)\r\n\t\t{\r\n\t\t\t// playing qwd demo || mvd spec/demo || playing \r\n\t\t\tif( (cls.demoplayback && !cl.spectator && !cls.mvdplayback && strcmp(sorted_teams[i].name, cl.players[cl.playernum].team) == 0) ||\r\n\t\t\t\t((cls.demoplayback || cl.spectator) && ((strcmp(cl.players[spec_track].team, sorted_teams[i].name) == 0) && (Cam_TrackNum() >= 0))) ||\r\n\t\t\t\t(strcmp(sorted_teams[i].name, cl.players[cl.playernum].team) == 0) )\r\n\t\t\t{\r\n\t\t\t\tvalue = sorted_teams[i].frags;\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\telse if(cl.deathmatch)\r\n\t{\r\n\t\tfor(i = 0; i < n_players; i++)\r\n\t\t{\r\n\t\t\tif( (cls.demoplayback && !cl.spectator && !cls.mvdplayback && strcmp(cl.players[sorted_players[i].playernum].name, cl.players[cl.playernum].name) == 0) ||\r\n\t\t\t\t((cls.demoplayback || cl.spectator) && ((strcmp(cl.players[spec_track].name, cl.players[sorted_players[i].playernum].name) == 0) && (Cam_TrackNum() >= 0))) ||\r\n\t\t\t\t(strcmp(cl.players[sorted_players[i].playernum].name, cl.players[cl.playernum].name) == 0) )\r\n\t\t\t{\r\n\t\t\t\tvalue = cl.players[sorted_players[i].playernum].frags;\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\tSCR_HUD_DrawNum(hud, value, (colorize->ival) ? (value < 0 || colorize->ival > 1) : false, scale->value, style->value, digits->value, align->string);\r\n}\r\n\r\nvoid SCR_HUD_DrawScoresEnemy(hud_t *hud)\r\n{\r\n\tstatic cvar_t *scale = NULL, *style, *digits, *align, *colorize;\r\n\tint value = 0;\r\n\tint i;\r\n\r\n\tif (scale == NULL)  // first time called\r\n\t{\r\n\t\tscale\t\t= HUD_FindVar(hud, \"scale\");\r\n\t\tstyle\t\t= HUD_FindVar(hud, \"style\");\r\n\t\tdigits\t\t= HUD_FindVar(hud, \"digits\");\r\n\t\talign\t\t= HUD_FindVar(hud, \"align\");\r\n\t\tcolorize\t= HUD_FindVar(hud, \"colorize\");\r\n\t}\r\n\t\r\n\t//\r\n\t// AAS: voodoo, again\r\n\t//\r\n\tif(cl.teamplay)\r\n\t{\r\n\t\tfor(i = 0; i < n_teams; i++)\r\n\t\t{\r\n\t\t\t\r\n\t\t\tif(\t(cls.demoplayback && !cl.spectator && !cls.mvdplayback && strcmp(sorted_teams[i].name, cl.players[cl.playernum].team) == 0) ||\r\n\t\t\t\t((cls.demoplayback || cl.spectator) && ((strcmp(cl.players[spec_track].team, sorted_teams[i].name) == 0) && (Cam_TrackNum() >= 0))) ||\r\n\t\t\t\t(strcmp(sorted_teams[i].name, cl.players[cl.playernum].team) == 0) )\r\n\t\t\t{\r\n\t\t\t\tif(n_teams > 1)\r\n\t\t\t\t\tvalue = sorted_teams[i == 0 ? 1 : 0].frags;\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\telse if(cl.deathmatch)\r\n\t{\r\n\t\tfor(i = 0; i < n_players; i++)\r\n\t\t{\r\n\t\t\tif(\t(cls.demoplayback && !cl.spectator && !cls.mvdplayback && strcmp(cl.players[sorted_players[i].playernum].name, cl.players[cl.playernum].name) == 0) ||\r\n\t\t\t\t((cls.demoplayback || cl.spectator) && ((strcmp(cl.players[spec_track].name, cl.players[sorted_players[i].playernum].name) == 0) && (Cam_TrackNum() >= 0))) ||\r\n\t\t\t\t(strcmp(cl.players[sorted_players[i].playernum].name, cl.players[cl.playernum].name) == 0) )\r\n\t\t\t{\r\n\t\t\t\tif(n_players > 1)\r\n\t\t\t\t\tvalue = cl.players[sorted_players[i == 0 ? 1 : 0].playernum].frags;\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\tSCR_HUD_DrawNum(hud, value, (colorize->ival) ? (value < 0 || colorize->ival > 1) : false, scale->value, style->value, digits->value, align->string);\r\n}\r\n\r\nvoid SCR_HUD_DrawScoresDifference(hud_t *hud)\r\n{\r\n\tstatic cvar_t *scale = NULL, *style, *digits, *align, *colorize;\r\n\tint value = 0;\r\n\tint i;\r\n\r\n\tif (scale == NULL)  // first time called\r\n\t{\r\n\t\tscale\t\t= HUD_FindVar(hud, \"scale\");\r\n\t\tstyle\t\t= HUD_FindVar(hud, \"style\");\r\n\t\tdigits\t\t= HUD_FindVar(hud, \"digits\");\r\n\t\talign\t\t= HUD_FindVar(hud, \"align\");\r\n\t\tcolorize\t= HUD_FindVar(hud, \"colorize\");\r\n\t}\r\n\r\n\t//\r\n\t// AAS: more voodoo\r\n\t//\r\n\tif(cl.teamplay)\r\n\t{\r\n\t\tfor(i = 0; i < n_teams; i++)\r\n\t\t{\r\n\t\t\tif(\t(cls.demoplayback && !cl.spectator && !cls.mvdplayback && strcmp(sorted_teams[i].name, cl.players[cl.playernum].team) == 0) ||\r\n\t\t\t\t((cls.demoplayback || cl.spectator) && ((strcmp(cl.players[spec_track].team, sorted_teams[i].name) == 0) && (Cam_TrackNum() >= 0))) ||\r\n\t\t\t\t(strcmp(sorted_teams[i].name, cl.players[cl.playernum].team) == 0) )\r\n\t\t\t{\r\n\t\t\t\tif(i == 0)\r\n\t\t\t\t{\r\n\t\t\t\t\tif(n_teams > 1)\r\n\t\t\t\t\t\tvalue = sorted_teams[0].frags - sorted_teams[1].frags;\r\n\t\t\t\t\telse\r\n\t\t\t\t\t\tvalue = sorted_teams[0].frags;\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\tif(n_teams > 1)\r\n\t\t\t\t\t\tvalue = sorted_teams[i].frags - sorted_teams[0].frags;\r\n\t\t\t\t}\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\telse if(cl.deathmatch)\r\n\t{\r\n\t\tfor(i = 0; i < n_players; i++)\r\n\t\t{\r\n\t\t\tif(\t(cls.demoplayback && !cl.spectator && !cls.mvdplayback && strcmp(cl.players[sorted_players[i].playernum].name, cl.players[cl.playernum].name) == 0) ||\r\n\t\t\t\t((cls.demoplayback || cl.spectator) && ((strcmp(cl.players[spec_track].name, cl.players[sorted_players[i].playernum].name) == 0) && (Cam_TrackNum() >= 0))) ||\r\n\t\t\t\t(strcmp(cl.players[sorted_players[i].playernum].name, cl.players[cl.playernum].name) == 0) )\r\n\t\t\t{\r\n\t\t\t\tif(i == 0)\r\n\t\t\t\t{\r\n\t\t\t\t\tif(n_players > 1)\r\n\t\t\t\t\t\tvalue = cl.players[sorted_players[0].playernum].frags - cl.players[sorted_players[1].playernum].frags;\r\n\t\t\t\t\telse\r\n\t\t\t\t\t\tvalue = cl.players[sorted_players[0].playernum].frags;\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\tif(n_players > 1)\r\n\t\t\t\t\t\tvalue = cl.players[sorted_players[i].playernum].frags - cl.players[sorted_players[0].playernum].frags;\r\n\t\t\t\t}\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\tSCR_HUD_DrawNum(hud, value, (colorize->ival) ? (value < 0 || colorize->ival > 1) : false, scale->value, style->value, digits->value, align->string);\r\n}\r\n\r\nvoid SCR_HUD_DrawScoresPosition(hud_t *hud)\r\n{\r\n\tstatic cvar_t *scale = NULL, *style, *digits, *align, *colorize;\r\n\tint value = 0;\r\n\tint i;\r\n\r\n\tif (scale == NULL)  // first time called\r\n\t{\r\n\t\tscale\t\t= HUD_FindVar(hud, \"scale\");\r\n\t\tstyle\t\t= HUD_FindVar(hud, \"style\");\r\n\t\tdigits\t\t= HUD_FindVar(hud, \"digits\");\r\n\t\talign\t\t= HUD_FindVar(hud, \"align\");\r\n\t\tcolorize\t= HUD_FindVar(hud, \"colorize\");\r\n\t}\r\n\r\n\t//\r\n\t// AAS: someone, please stop me\r\n\t//\r\n\tif(cl.teamplay)\r\n\t{\r\n\t\tfor(i = 0; i < n_teams; i++)\r\n\t\t{\r\n\t\t\tif( (cls.demoplayback && !cl.spectator && !cls.mvdplayback && strcmp(sorted_teams[i].name, cl.players[cl.playernum].team) == 0) ||\r\n\t\t\t\t((cls.demoplayback || cl.spectator) && ((strcmp(cl.players[spec_track].team, sorted_teams[i].name) == 0) && (Cam_TrackNum() >= 0))) ||\r\n\t\t\t\t(strcmp(sorted_teams[i].name, cl.players[cl.playernum].team) == 0) )\r\n\t\t\t{\r\n\t\t\t\tvalue = i;\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\telse if(cl.deathmatch)\r\n\t{\r\n\t\tfor(i = 0; i < n_players; i++)\r\n\t\t{\r\n\t\t\tif( (cls.demoplayback && !cl.spectator && !cls.mvdplayback && strcmp(cl.players[sorted_players[i].playernum].name, cl.players[cl.playernum].name) == 0) ||\r\n\t\t\t\t((cls.demoplayback || cl.spectator) && ((strcmp(cl.players[spec_track].name, cl.players[sorted_players[i].playernum].name) == 0) && (Cam_TrackNum() >= 0))) ||\r\n\t\t\t\t(strcmp(cl.players[sorted_players[i].playernum].name, cl.players[cl.playernum].name) == 0) )\r\n\t\t\t{\r\n\t\t\t\tvalue = i;\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\tSCR_HUD_DrawNum(hud, value, (colorize->ival) ? (value < 0 || colorize->ival > 1) : false, scale->value, style->value, digits->value, align->string);\r\n}\r\n\r\n/*\r\n\tezQuake's analogue of +scores of KTX\r\n\t( t:x e:x [x] )\r\n*/\r\nvoid SCR_HUD_DrawScoresBar(hud_t *hud)\r\n{\r\n\tstatic\tcvar_t *scale = NULL, *style, *format_big, *format_small;\r\n\tint\t\twidth = 0, height = 0, x, y;\r\n\tint\t\ti = 0;\r\n\r\n\tint\t\ts_team = 0, s_enemy = 0, s_difference = 0;\r\n\tchar\t*n_team = \"T\", *n_enemy = \"E\";\r\n\r\n\tchar\tbuf[256];\r\n\tchar\tc, *out, *temp,\t*in;\r\n\r\n\tif (scale == NULL)  // first time called\r\n\t{\r\n\t\tscale\t\t= HUD_FindVar(hud, \"scale\");\r\n\t\tstyle\t\t= HUD_FindVar(hud, \"style\");\r\n\t\tformat_big\t= HUD_FindVar(hud, \"format_big\");\r\n\t\tformat_small= HUD_FindVar(hud, \"format_small\");\r\n\t}\r\n\r\n\t//\r\n\t// AAS: nightmare comes back\r\n\t//\r\n\tif(cl.teamplay)\r\n\t{\r\n\t\tfor(i = 0; i < n_teams; i++)\r\n\t\t{\r\n\t\t\tif(\t(cls.demoplayback && !cl.spectator && !cls.mvdplayback && strcmp(sorted_teams[i].name, cl.players[cl.playernum].team) == 0) ||\r\n\t\t\t\t((cls.demoplayback || cl.spectator) && ((strcmp(cl.players[spec_track].team, sorted_teams[i].name) == 0) && (Cam_TrackNum() >= 0))) ||\r\n\t\t\t\t(strcmp(sorted_teams[i].name, cl.players[cl.playernum].team) == 0) )\r\n\t\t\t{\r\n\t\t\t\ts_team = sorted_teams[i].frags;\r\n\t\t\t\tn_team = sorted_teams[i].name;\r\n\t\t\t\tif(n_teams > 1)\r\n\t\t\t\t{\r\n\t\t\t\t\ts_enemy = sorted_teams[i == 0 ? 1 : 0].frags;\r\n\t\t\t\t\tn_enemy = sorted_teams[i == 0 ? 1 : 0].name;\r\n\t\t\t\t}\r\n\t\t\t\ts_difference = s_team - s_enemy;\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\telse if(cl.deathmatch)\r\n\t{\r\n\t\tfor(i = 0; i < n_players; i++)\r\n\t\t{\r\n\t\t\tif(\t(cls.demoplayback && !cl.spectator && !cls.mvdplayback && strcmp(cl.players[sorted_players[i].playernum].name, cl.players[cl.playernum].name) == 0) ||\r\n\t\t\t\t((cls.demoplayback || cl.spectator) && ((strcmp(cl.players[spec_track].name, cl.players[sorted_players[i].playernum].name) == 0) && (Cam_TrackNum() >= 0))) ||\r\n\t\t\t\t(strcmp(cl.players[sorted_players[i].playernum].name, cl.players[cl.playernum].name) == 0) )\r\n\t\t\t{\r\n\t\t\t\ts_team = cl.players[sorted_players[i].playernum].frags;\r\n\t\t\t\tif(n_players > 1)\r\n\t\t\t\t{\r\n\t\t\t\t\ts_enemy = cl.players[sorted_players[i == 0 ? 1 : 0].playernum].frags;\r\n\t\t\t\t}\r\n\t\t\t\ts_difference = s_team - s_enemy;\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\r\n\t// two pots of delicious customized copypasta from math_tools.c\r\n\tswitch(style->ival)\r\n\t{\r\n\t\t// Big\r\n\t\tcase 1:\r\n\t\t\tin = TP_ParseFunChars(format_big->string);\r\n\t\t\tbuf[0] = 0;\r\n\t\t\tout = buf;\r\n\r\n\t\t\twhile((c = *in++) && (out - buf < sizeof(buf) - 1))\r\n\t\t\t{\r\n\t\t\t\tif((c == '%') && *in)\r\n\t\t\t\t{\r\n\t\t\t\t\tswitch((c = *in++))\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\t// c = colorize, r = reset\r\n\t\t\t\t\t\tcase 'd':\r\n\t\t\t\t\t\t\ttemp = va(\"%d\", s_difference);\r\n\t\t\t\t\t\t\twidth += (s_difference >= 0) ? strlen(temp) * 24 : ((strlen(temp) - 1) * 24) + 16;\r\n\t\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t\tcase 'D':\r\n\t\t\t\t\t\t\ttemp = va(\"c%dr\", s_difference);\r\n\t\t\t\t\t\t\twidth += (s_difference >= 0) ? (strlen(temp) - 2) * 24 : ((strlen(temp) - 3) * 24) + 16;\r\n\t\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t\tcase 'e':\r\n\t\t\t\t\t\t\ttemp = va(\"%d\", s_enemy);\r\n\t\t\t\t\t\t\twidth += (s_enemy >= 0) ? strlen(temp) * 24 : ((strlen(temp) - 1) * 24) + 16;\r\n\t\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t\tcase 'E':\r\n\t\t\t\t\t\t\ttemp = va(\"c%dr\", s_enemy);\r\n\t\t\t\t\t\t\twidth += (s_enemy >= 0) ? (strlen(temp) - 2) * 24 : ((strlen(temp) - 3) * 24) + 16;\r\n\t\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t\tcase 'p':\r\n\t\t\t\t\t\t\ttemp = va(\"%d\", i + 1);\r\n\t\t\t\t\t\t\twidth += 24;\r\n\t\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t\tcase 't':\r\n\t\t\t\t\t\t\ttemp = va(\"%d\", s_team);\r\n\t\t\t\t\t\t\twidth += (s_team >= 0) ? strlen(temp) * 24 : ((strlen(temp) - 1) * 24) + 16;\r\n\t\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t\tcase 'T':\r\n\t\t\t\t\t\t\ttemp = va(\"c%dr\", s_team);\r\n\t\t\t\t\t\t\twidth += (s_team >= 0) ? (strlen(temp) - 2) * 24 : ((strlen(temp) - 3) * 24) + 16;\r\n\t\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t\tcase 'z':\r\n\t\t\t\t\t\t\tif(s_difference >= 0)\r\n\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\ttemp = va(\"%d\", s_difference);\r\n\t\t\t\t\t\t\t\twidth += strlen(temp) * 24;\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\telse\r\n\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\ttemp = va(\"c%dr\", -(s_difference));\r\n\t\t\t\t\t\t\t\twidth += (strlen(temp) - 2) * 24;\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t\tcase 'Z':\r\n\t\t\t\t\t\t\tif(s_difference >= 0)\r\n\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\ttemp = va(\"c%dr\", s_difference);\r\n\t\t\t\t\t\t\t\twidth += (strlen(temp) - 2) * 24;\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\telse\r\n\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\ttemp = va(\"%d\", -(s_difference));\r\n\t\t\t\t\t\t\t\twidth += strlen(temp) * 24;\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t\tdefault:\r\n\t\t\t\t\t\t\ttemp = NULL;\r\n\t\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t}\r\n\t\t\t\t\t\r\n\t\t\t\t\tif(temp != NULL)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tstrlcpy(out, temp, sizeof(buf) - (out - buf));\r\n\t\t\t\t\t\tout += strlen(temp);\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t\telse if (c == ':' || c == '/' || c == '-' || c == ' ')\r\n\t\t\t\t{\r\n\t\t\t\t\twidth += 16;\r\n\t\t\t\t\t*out++ = c;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\t*out = 0;\r\n\t\t\tbreak;\r\n\r\n\t\t// Small\r\n\t\tcase 0:\t\r\n\t\tdefault:\r\n\t\t\tin = TP_ParseFunChars(format_small->string);\r\n\t\t\tbuf[0] = 0;\r\n\t\t\tout = buf;\r\n\r\n\t\t\twhile((c = *in++) && (out - buf < sizeof(buf) - 1))\r\n\t\t\t{\r\n\t\t\t\tif((c == '%') && *in)\r\n\t\t\t\t{\r\n\t\t\t\t\tswitch((c = *in++))\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tcase '%':\r\n\t\t\t\t\t\t\ttemp = \"%\";\r\n\t\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t\tcase 't':\r\n\t\t\t\t\t\t\ttemp = va(\"%d\", s_team);\r\n\t\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t\tcase 'e':\r\n\t\t\t\t\t\t\ttemp = va(\"%d\", s_enemy);\r\n\t\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t\tcase 'd':\r\n\t\t\t\t\t\t\ttemp = va(\"%d\", s_difference);\r\n\t\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t\tcase 'p':\r\n\t\t\t\t\t\t\ttemp = va(\"%d\", i + 1);\r\n\t\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t\tcase 'T':\r\n\t\t\t\t\t\t\ttemp = n_team;\r\n\t\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t\tcase 'E':\r\n\t\t\t\t\t\t\ttemp = n_enemy;\r\n\t\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t\tcase 'D':\r\n\t\t\t\t\t\t\ttemp = va(\"%+d\", s_difference);\r\n\t\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t\tdefault:\r\n\t\t\t\t\t\t\ttemp = va(\"%%%c\", c);\r\n\t\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t}\r\n\t\t\t\t\tstrlcpy(out, temp, sizeof(buf) - (out - buf));\r\n\t\t\t\t\tout += strlen(temp);\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\t*out++ = c;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\t*out = 0;\r\n\t\t\tbreak;\r\n\t}\r\n\r\n\tswitch(style->ival)\r\n\t{\r\n\t\t// Big\r\n\t\tcase 1:\r\n\t\t\twidth *= scale->value;\r\n\t\t\theight = 24 * scale->value;\r\n\r\n\t\t\tif(HUD_PrepareDraw(hud, width, height, &x, &y))\r\n\t\t\t{\r\n\t\t\t\tSCR_DrawWadString(x, y, scale->value, buf);\r\n\t\t\t}\r\n\t\t\tbreak;\r\n\r\n\t\t// Small\r\n\t\tcase 0:\r\n\t\tdefault:\r\n\t\t\twidth = 8 * strlen_color(buf) * scale->value;\r\n\t\t\theight = 8 * scale->value;\r\n\r\n\t\t\tif(HUD_PrepareDraw(hud, width, height, &x, &y))\r\n\t\t\t{\r\n\t\t\t\tDraw_SString(x, y, buf, scale->value);\r\n\t\t\t}\r\n\t\t\tbreak;\r\n\t}\r\n}\r\n\r\nvoid SCR_HUD_DrawBarArmor(hud_t *hud)\r\n{\r\n\tstatic\tcvar_t *width = NULL, *height, *direction, *color_noarmor, *color_ga, *color_ya, *color_ra, *color_unnatural;\r\n\tint\t\tx, y;\r\n\tint\t\tarmor = HUD_Stats(STAT_ARMOR);\r\n\tqbool\talive = cl.stats[STAT_HEALTH] > 0;\r\n\t\r\n\tif (width == NULL)  // first time called\r\n\t{\r\n\t\twidth\t\t\t= HUD_FindVar(hud, \"width\");\r\n\t\theight\t\t\t= HUD_FindVar(hud, \"height\");\r\n\t\tdirection\t\t= HUD_FindVar(hud, \"direction\");\r\n\t\tcolor_noarmor\t= HUD_FindVar(hud, \"color_noarmor\");\r\n\t\tcolor_ga\t\t= HUD_FindVar(hud, \"color_ga\");\r\n\t\tcolor_ya\t\t= HUD_FindVar(hud, \"color_ya\");\r\n\t\tcolor_ra\t\t= HUD_FindVar(hud, \"color_ra\");\r\n\t\tcolor_unnatural\t= HUD_FindVar(hud, \"color_unnatural\");\r\n\t}\r\n\t\r\n\tif(HUD_PrepareDraw(hud, width->ival, height->ival, &x, &y))\r\n\t{\r\n\t\tif(!width->ival || !height->ival)\r\n\t\t\treturn;\r\n\r\n\t\tif(HUD_Stats(STAT_ITEMS) & IT_INVULNERABILITY && alive)\r\n\t\t{\r\n\t\t\tSCR_HUD_DrawBar(direction->ival, 100, 100.0, color_unnatural->vec4, x, y, width->ival, height->ival);\r\n\t\t}\r\n\t\telse  if (HUD_Stats(STAT_ITEMS) & IT_ARMOR3 && alive)\r\n\t\t{\r\n\t\t\tSCR_HUD_DrawBar(direction->ival, 100, 100.0, color_noarmor->vec4, x, y, width->ival, height->ival);\r\n\t\t\tSCR_HUD_DrawBar(direction->ival, armor, 200.0, color_ra->vec4, x, y, width->ival, height->ival);\r\n\t\t}\r\n\t\telse if (HUD_Stats(STAT_ITEMS) & IT_ARMOR2 && alive)\r\n\t\t{\r\n\t\t\tSCR_HUD_DrawBar(direction->ival, 100, 100.0, color_noarmor->vec4, x, y, width->ival, height->ival);\r\n\t\t\tSCR_HUD_DrawBar(direction->ival, armor, 150.0, color_ya->vec4, x, y, width->ival, height->ival);\r\n\t\t}\r\n\t\telse if (HUD_Stats(STAT_ITEMS) & IT_ARMOR1 && alive)\r\n\t\t{\r\n\t\t\tSCR_HUD_DrawBar(direction->ival, 100, 100.0, color_noarmor->vec4, x, y, width->ival, height->ival);\r\n\t\t\tSCR_HUD_DrawBar(direction->ival, armor, 100.0, color_ga->vec4, x, y, width->ival, height->ival);\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tSCR_HUD_DrawBar(direction->ival, 100, 100.0, color_noarmor->vec4, x, y, width->ival, height->ival);\r\n\t\t}\r\n\t}\r\n}\r\n\r\nvoid SCR_HUD_DrawBarHealth(hud_t *hud)\r\n{\r\n\tstatic\tcvar_t *width = NULL, *height, *direction, *color_nohealth, *color_normal, *color_mega, *color_twomega, *color_unnatural;\r\n\tint\t\tx, y;\r\n\tint\t\thealth = cl.stats[STAT_HEALTH];\r\n\r\n\tif (width == NULL)  // first time called\r\n\t{\r\n\t\twidth\t\t\t= HUD_FindVar(hud, \"width\");\r\n\t\theight\t\t\t= HUD_FindVar(hud, \"height\");\r\n\t\tdirection\t\t= HUD_FindVar(hud, \"direction\");\r\n\t\tcolor_nohealth\t= HUD_FindVar(hud, \"color_nohealth\");\r\n\t\tcolor_normal\t= HUD_FindVar(hud, \"color_normal\");\r\n\t\tcolor_mega\t\t= HUD_FindVar(hud, \"color_mega\");\r\n\t\tcolor_twomega\t= HUD_FindVar(hud, \"color_twomega\");\r\n\t\tcolor_unnatural\t= HUD_FindVar(hud, \"color_unnatural\");\r\n\t}\r\n\r\n\tif(HUD_PrepareDraw(hud, width->ival, height->ival, &x, &y))\r\n\t{\r\n\t\tif(!width->ival || !height->ival)\r\n\t\t\treturn;\r\n\r\n\t\tif(health > 250)\r\n\t\t{\r\n\t\t\tSCR_HUD_DrawBar(direction->ival, 100, 100.0, color_unnatural->vec4, x, y, width->ival, height->ival);\r\n\t\t}\r\n\t\telse if(health > 200)\r\n\t\t{\r\n\t\t\tSCR_HUD_DrawBar(direction->ival, 100, 100.0, color_normal->vec4, x, y, width->ival, height->ival);\r\n\t\t\tSCR_HUD_DrawBar(direction->ival, 100, 100.0, color_mega->vec4, x, y, width->ival, height->ival);\r\n\t\t\tSCR_HUD_DrawBar(direction->ival, health - 200, 100.0, color_twomega->vec4, x, y, width->ival, height->ival);\r\n\t\t}\r\n\t\telse if(health > 100)\r\n\t\t{\r\n\t\t\tSCR_HUD_DrawBar(direction->ival, 100, 100.0, color_normal->vec4, x, y, width->ival, height->ival);\r\n\t\t\tSCR_HUD_DrawBar(direction->ival, health - 100, 100.0, color_mega->vec4, x, y, width->ival, height->ival);\r\n\t\t}\r\n\t\telse if(health > 0)\r\n\t\t{\r\n\t\t\tSCR_HUD_DrawBar(direction->ival, 100, 100.0, color_nohealth->vec4, x, y, width->ival, height->ival);\r\n\t\t\tSCR_HUD_DrawBar(direction->ival, health, 100.0, color_normal->vec4, x, y, width->ival, height->ival);\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tSCR_HUD_DrawBar(direction->ival, 100, 100.0, color_nohealth->vec4, x, y, width->ival, height->ival);\r\n\t\t}\r\n\t}\r\n}\r\n\r\nvoid SCR_HUD_DrawOwnFrags(hud_t *hud)\r\n{\r\n\t// not implemented yet: scale, color\r\n\t// fixme: add appropriate opengl functions that will add alpha, scale and color\r\n\tchar ownfragtext[256];\r\n\tfloat age;\r\n\tint width;\r\n\tint height = 8;\r\n\tint x, y;\r\n\tdouble alpha;\r\n\tstatic cvar_t\r\n\t\t*hud_ownfrags_timeout = NULL,\r\n\t\t*hud_ownfrags_scale = NULL;\r\n//\t\t*hud_ownfrags_color = NULL;\r\n\textern qbool hud_editor;\r\n\r\n\tif (hud_ownfrags_timeout == NULL)    // first time\r\n\t{\r\n\t\thud_ownfrags_scale\t\t\t\t= HUD_FindVar(hud, \"scale\");\r\n\t\t// hud_ownfrags_color\t\t\t= HUD_FindVar(hud, \"color\");\r\n\t\thud_ownfrags_timeout\t\t\t= HUD_FindVar(hud, \"timeout\");\r\n\t}\r\n\r\n\tif (hud_editor)\r\n\t{\r\n\t\tstrcpy(ownfragtext, \"Own Frags\");\r\n\t\tage = 0;\r\n\t}\r\n\telse if (clientfuncs->GetTrackerOwnFrags)\r\n\t\tage = clientfuncs->GetTrackerOwnFrags(0, ownfragtext, sizeof(ownfragtext));\r\n\telse\r\n\t\tage = 999999;\r\n\twidth = strlen(ownfragtext)*8;\r\n\r\n\twidth *= hud_ownfrags_scale->value;\r\n\theight *= hud_ownfrags_scale->value;\r\n\r\n\tif (age >= hud_ownfrags_timeout->value)\r\n\t\twidth = 0;\r\n\r\n\talpha = 2 - age / hud_ownfrags_timeout->value * 2;\r\n\talpha = bound(0, alpha, 1);\r\n\r\n\tif (!width)\r\n\t{\r\n\t\tHUD_PrepareDraw(hud, width, height, NULL, NULL);\r\n\t\treturn;\r\n\t}\r\n\tif (!HUD_PrepareDraw(hud, width, height, &x, &y))\r\n\t\treturn;\r\n\r\n\tdrawfuncs->Colour4f(1, 1, 1, alpha);\r\n\tDraw_SString(x, y, ownfragtext, hud_ownfrags_scale->value);\r\n\tdrawfuncs->Colour4f(1, 1, 1, 1);\r\n}\r\n\r\n#ifdef QUAKEHUD\r\nstatic struct wstats_s *findweapon(struct wstats_s *w, size_t wc, char *wn)\r\n{\r\n\tfor (; wc>0; wc--, w++)\r\n\t{\r\n\t\tif (!strcmp(wn, w->wname))\r\n\t\t\treturn w;\r\n\t}\r\n\treturn NULL;\r\n}\r\nstatic void SCR_HUD_DrawWeaponStats(hud_t *hud)\r\n{\r\n\tchar line[1024], *o, *i;\r\n\tint width;\r\n\tint height = 8;\r\n\tint x, y;\r\n\tstatic cvar_t *hud_weaponstats_scale = NULL;\r\n\tstatic cvar_t *hud_weaponstats_fmt = NULL;\r\n\textern qbool hud_editor;\r\n\r\n\tint ws;\r\n\tstruct wstats_s wstats[16];\r\n\tws = clientfuncs->GetWeaponStats?clientfuncs->GetWeaponStats(-1, wstats, countof(wstats)):0;\r\n\r\n\tif (hud_editor)\r\n\t{\r\n\t\tws = 0;\r\n\t\tstrcpy(wstats[ws].wname, \"axe\");\r\n\t\twstats[ws].hit = 20;\r\n\t\twstats[ws].total = 100;\r\n\t\tws++;\r\n\t\tstrcpy(wstats[ws].wname, \"rl\");\r\n\t\twstats[ws].hit = 60;\r\n\t\twstats[ws].total = 120;\r\n\t\tws++;\r\n\t\tstrcpy(wstats[ws].wname, \"lg\");\r\n\t\twstats[ws].hit = 20;\r\n\t\twstats[ws].total = 100;\r\n\t\tws++;\r\n\t}\r\n\r\n\tif (hud_weaponstats_scale == NULL)    // first time\r\n\t{\r\n\t\thud_weaponstats_scale\t\t\t\t= HUD_FindVar(hud, \"scale\");\r\n\t\thud_weaponstats_fmt\t\t\t\t\t= HUD_FindVar(hud, \"fmt\");\r\n//\t\t\"&c990sg&r:[%sg] &c099ssg&r:[%ssg] &c900rl&r:[#rl] &c009lg&r:[%lg]\"\r\n\t}\r\n\r\n\theight = 8;\r\n\tfor (o = line, i = hud_weaponstats_fmt->string; ws && *i && o < line+countof(line)-1; )\r\n\t{\r\n\t\tif (i[0] == '[' && (i[1] == '%' || i[1] == '#'))\r\n\t\t{\r\n\t\t\tstruct wstats_s *w;\r\n\t\t\tchar wname[16];\r\n\t\t\tint pct = i[1]=='%', j;\r\n\t\t\ti+=2;\r\n\t\t\tfor (j = 0; *i && j < countof(wname)-1; j++)\r\n\t\t\t{\r\n\t\t\t\tif (*i == ']')\r\n\t\t\t\t{\r\n\t\t\t\t\ti++;\r\n\t\t\t\t\tbreak;\r\n\t\t\t\t}\r\n\t\t\t\twname[j] = *i++;\r\n\t\t\t}\r\n\t\t\twname[j] = 0;\r\n\t\t\tw = findweapon(wstats, ws, wname);\r\n\t\t\tif (pct && w && w->total)\r\n\t\t\t\tsnprintf(wname, sizeof(wname), \"%.1f\", (100.0 * w->hit) / w->total);\r\n\t\t\telse if (pct)\r\n\t\t\t\tsnprintf(wname, sizeof(wname), \"%.1f\", 0.0);\r\n\t\t\telse if (w)\r\n\t\t\t\tsnprintf(wname, sizeof(wname), \"%u\", w->hit);\r\n\t\t\telse\r\n\t\t\t\tsnprintf(wname, sizeof(wname), \"%u\", 0);\r\n\r\n\t\t\tfor (j = 0; wname[j] && o < line+countof(line)-1; j++)\r\n\t\t\t\t*o++ = wname[j];\r\n\t\t}\r\n\t\telse if (*i == '\\n')\r\n\t\t{\r\n\t\t\theight += 8;\r\n\t\t\t*o++ = *i++;\r\n\t\t}\r\n\t\telse\r\n\t\t\t*o++ = *i++;\r\n\t}\r\n\t*o++ = 0;\r\n\r\n\twidth = 8*strlen_color(line);\r\n\r\n\twidth *= hud_weaponstats_scale->value;\r\n\theight *= hud_weaponstats_scale->value;\r\n\r\n\tif (!HUD_PrepareDraw(hud, width, height, &x, &y))\r\n\t\treturn;\r\n\r\n\tDraw_SString(x, y, line, hud_weaponstats_scale->value);\r\n}\r\n#endif\r\n\r\nvoid SCR_HUD_DrawKeys(hud_t *hud)\r\n{\r\n\tchar line1[32], line2[32];\r\n\tint width, height, x, y;\r\n\tusercmd_t b;\r\n\tstatic cvar_t* vscale = NULL;\r\n\tfloat scale;\r\n\r\n\tmemset(&b, 0, sizeof(b));\r\n\tclientfuncs->GetLastInputFrame(0, &b);\r\n\r\n\tif (!vscale) {\r\n\t\tvscale = HUD_FindVar(hud, \"scale\");\r\n\t}\r\n\r\n\tscale = vscale->value;\r\n\tscale = max(0, scale);\r\n\r\n\tsnprintf(line1, sizeof(line1), \"^{%x}^{%x}^{%x}\", \r\n\t\t0xe000 + 'x' + ((b.buttons & 1)?0x80:0),\r\n\t\t0xe000 + '^' + ((b.forwardmove > 0)?0x80:0),\r\n\t\t0xe000 + 'J' + ((b.buttons & 2)?0x80:0));\r\n\tsnprintf(line2, sizeof(line2), \"^{%x}^{%x}^{%x}\", \r\n\t\t0xe000 + '<' + ((b.sidemove < 0)?0x80:0),\r\n\t\t0xe000 + '_' + ((b.forwardmove < 0)?0x80:0),\r\n\t\t0xe000 + '>' + ((b.sidemove > 0)?0x80:0));\r\n\r\n\twidth = 8 * 3 * scale;\r\n\theight = 8 * 2 * scale;\r\n\r\n\tif (!HUD_PrepareDraw(hud, width, height, &x, &y))\r\n\t\treturn;\r\n\r\n\tDraw_SString(x, y, line1, scale);\r\n\tDraw_SString(x, y + 8*scale, line2, scale);\r\n}\r\n\r\n#ifdef WITH_PNG\r\n// What stats to draw.\r\n#define HUD_RADAR_STATS_NONE\t\t\t\t0\r\n#define HUD_RADAR_STATS_BOTH_TEAMS_HOLD\t\t1\r\n#define HUD_RADAR_STATS_TEAM1_HOLD\t\t\t2\r\n#define HUD_RADAR_STATS_TEAM2_HOLD\t\t\t3\r\n#define HUD_RADAR_STATS_BOTH_TEAMS_DEATHS\t4\r\n#define HUD_RADAR_STATS_TEAM1_DEATHS\t\t5\r\n#define HUD_RADAR_STATS_TEAM2_DEATHS\t\t6\r\n\r\nvoid Radar_DrawGrid(stats_weight_grid_t *grid, int x, int y, float scale, int pic_width, int pic_height, int style)\r\n{\r\n\tint row, col;\r\n\r\n\t// Don't try to draw anything if we got no data.\r\n\tif(grid == NULL || grid->cells == NULL || style == HUD_RADAR_STATS_NONE)\r\n\t{\r\n\t\treturn;\r\n\t}\r\n\r\n\t// Go through all the cells and draw them based on their weight.\r\n\tfor(row = 0; row < grid->row_count; row++)\r\n\t{\r\n\t\t// Just to be safe if something went wrong with the allocation.\r\n\t\tif(grid->cells[row] == NULL)\r\n\t\t{\r\n\t\t\tcontinue;\r\n\t\t}\r\n\r\n\t\tfor(col = 0; col < grid->col_count; col++)\r\n\t\t{\r\n\t\t\tfloat weight = 0.0;\r\n\t\t\tint color = 0;\r\n\r\n\t\t\tfloat tl_x, tl_y;\t\t\t// The pixel coordinate of the top left corner of a grid cell.\r\n\t\t\tfloat p_cell_length_x;\t\t// The pixel length of a cell.\r\n\t\t\tfloat p_cell_length_y;\t\t// The pixel \"length\" on the Y-axis. We calculate this\r\n\t\t\t\t\t\t\t\t\t\t// seperatly because we'll get errors when converting from\r\n\t\t\t\t\t\t\t\t\t\t// quake coordinates -> pixel coordinates.\r\n\r\n\t\t\t// Calculate the pixel coordinates of the top left corner of the current cell.\r\n\t\t\t// (This is times 8 because the conversion formula was calculated from a .loc-file)\r\n\t\t\ttl_x = (map_x_slope * (8.0 * grid->cells[row][col].tl_x) + map_x_intercept) * scale;\r\n\t\t\ttl_y = (map_y_slope * (8.0 * grid->cells[row][col].tl_y) + map_y_intercept) * scale;\r\n\r\n\t\t\t// Calculate the cell length in pixel length.\r\n\t\t\tp_cell_length_x = map_x_slope*(8.0 * grid->cell_length) * scale;\r\n\t\t\tp_cell_length_y = map_y_slope*(8.0 * grid->cell_length) * scale;\r\n\r\n\t\t\t// Add rounding errors (so that we don't get weird gaps in the grid).\r\n\t\t\tp_cell_length_x += tl_x - Q_rint(tl_x);\r\n\t\t\tp_cell_length_y += tl_y - Q_rint(tl_y);\r\n\r\n\t\t\t// Don't draw the stats stuff outside the picture.\r\n\t\t\tif(tl_x + p_cell_length_x > pic_width || tl_y + p_cell_length_y > pic_height || x + tl_x < x || y + tl_y < y)\r\n\t\t\t{\r\n\t\t\t\tcontinue;\r\n\t\t\t}\r\n\r\n\t\t\t//\r\n\t\t\t// Death stats.\r\n\t\t\t//\r\n\t\t\tif(grid->cells[row][col].teams[STATS_TEAM1].death_weight + grid->cells[row][col].teams[STATS_TEAM2].death_weight > 0)\r\n\t\t\t{\r\n\t\t\t\tweight = 0;\r\n\r\n\t\t\t\tif(style == HUD_RADAR_STATS_BOTH_TEAMS_DEATHS || style == HUD_RADAR_STATS_TEAM1_DEATHS)\r\n\t\t\t\t{\r\n\t\t\t\t\tweight = grid->cells[row][col].teams[STATS_TEAM1].death_weight;\r\n\t\t\t\t}\r\n\r\n\t\t\t\tif(style == HUD_RADAR_STATS_BOTH_TEAMS_DEATHS || style == HUD_RADAR_STATS_TEAM2_DEATHS)\r\n\t\t\t\t{\r\n\t\t\t\t\tweight += grid->cells[row][col].teams[STATS_TEAM2].death_weight;\r\n\t\t\t\t}\r\n\r\n\t\t\t\tcolor = 79;\r\n\t\t\t}\r\n\r\n\t\t\t//\r\n\t\t\t// Team stats.\r\n\t\t\t//\r\n\t\t\t{\r\n\t\t\t\t// No point in drawing if we have no weight.\r\n\t\t\t\tif(grid->cells[row][col].teams[STATS_TEAM1].weight + grid->cells[row][col].teams[STATS_TEAM2].weight <= 0\r\n\t\t\t\t\t&& (style == HUD_RADAR_STATS_BOTH_TEAMS_HOLD\r\n\t\t\t\t\t||\tstyle == HUD_RADAR_STATS_TEAM1_HOLD\r\n\t\t\t\t\t||\tstyle == HUD_RADAR_STATS_TEAM2_HOLD))\r\n\t\t\t\t{\r\n\t\t\t\t\tcontinue;\r\n\t\t\t\t}\r\n\r\n\t\t\t\t// Get the team with the highest weight for this cell.\r\n\t\t\t\tif(grid->cells[row][col].teams[STATS_TEAM1].weight > grid->cells[row][col].teams[STATS_TEAM2].weight\r\n\t\t\t\t\t&& (style == HUD_RADAR_STATS_BOTH_TEAMS_HOLD\r\n\t\t\t\t\t||\tstyle == HUD_RADAR_STATS_TEAM1_HOLD))\r\n\t\t\t\t{\r\n\t\t\t\t\tweight = grid->cells[row][col].teams[STATS_TEAM1].weight;\r\n\t\t\t\t\tcolor = stats_grid->teams[STATS_TEAM1].color;\r\n\t\t\t\t}\r\n\t\t\t\telse if(style == HUD_RADAR_STATS_BOTH_TEAMS_HOLD ||\tstyle == HUD_RADAR_STATS_TEAM2_HOLD)\r\n\t\t\t\t{\r\n\t\t\t\t\tweight = grid->cells[row][col].teams[STATS_TEAM2].weight;\r\n\t\t\t\t\tcolor = stats_grid->teams[STATS_TEAM2].color;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\t// Draw the cell in the color of the team with the\r\n\t\t\t// biggest weight for this cell. Or draw deaths.\r\n\t\t\tDraw_AlphaFill(\r\n\t\t\t\tx + Q_rint(tl_x),\t\t\t// X.\r\n\t\t\t\ty + Q_rint(tl_y),\t\t\t// Y.\r\n\t\t\t\tQ_rint(p_cell_length_x),\t\t// Width.\r\n\t\t\t\tQ_rint(p_cell_length_y),\t\t// Height.\r\n\t\t\t\tcolor,\t\t\t\t\t\t// Color.\r\n\t\t\t\tweight);\t\t\t\t\t// Alpha.\r\n\t\t}\r\n\t}\r\n}\r\n\r\n// The skinnum property in the entity_s structure is used\r\n// for determening what type of armor to draw on the radar.\r\n#define HUD_RADAR_GA\t\t\t\t\t0\r\n#define HUD_RADAR_YA\t\t\t\t\t1\r\n#define HUD_RADAR_RA\t\t\t\t\t2\r\n\r\n// Radar filters.\r\n#define RADAR_SHOW_WEAPONS (radar_show_ssg || radar_show_ng || radar_show_sng || radar_show_gl || radar_show_rl || radar_show_lg)\r\nstatic qbool radar_show_ssg\t\t\t= false;\r\nstatic qbool radar_show_ng\t\t\t= false;\r\nstatic qbool radar_show_sng\t\t\t= false;\r\nstatic qbool radar_show_gl\t\t\t= false;\r\nstatic qbool radar_show_rl\t\t\t= false;\r\nstatic qbool radar_show_lg\t\t\t= false;\r\n\r\n#define RADAR_SHOW_ITEMS (radar_show_backpacks || radar_show_health || radar_show_ra || radar_show_ya || radar_show_ga || radar_show_rockets || radar_show_nails || radar_show_cells || radar_show_shells || radar_show_quad || radar_show_pent || radar_show_ring || radar_show_suit)\r\nstatic qbool radar_show_backpacks\t= false;\r\nstatic qbool radar_show_health\t\t= false;\r\nstatic qbool radar_show_ra\t\t\t= false;\r\nstatic qbool radar_show_ya\t\t\t= false;\r\nstatic qbool radar_show_ga\t\t\t= false;\r\nstatic qbool radar_show_rockets\t\t= false;\r\nstatic qbool radar_show_nails\t\t= false;\r\nstatic qbool radar_show_cells\t\t= false;\r\nstatic qbool radar_show_shells\t\t= false;\r\nstatic qbool radar_show_quad\t\t= false;\r\nstatic qbool radar_show_pent\t\t= false;\r\nstatic qbool radar_show_ring\t\t= false;\r\nstatic qbool radar_show_suit\t\t= false;\r\nstatic qbool radar_show_mega\t\t= false;\r\n\r\n#define RADAR_SHOW_OTHER (radar_show_gibs || radar_show_explosions || radar_show_nails_p || radar_show_rockets_p || radar_show_shaft_p || radar_show_teleport || radar_show_shotgun)\r\nstatic qbool radar_show_nails_p\t\t= false;\r\nstatic qbool radar_show_rockets_p\t= false;\r\nstatic qbool radar_show_shaft_p\t\t= false;\r\nstatic qbool radar_show_gibs\t\t= false;\r\nstatic qbool radar_show_explosions\t= false;\r\nstatic qbool radar_show_teleport\t= false;\r\nstatic qbool radar_show_shotgun\t\t= false;\r\n\r\nvoid Radar_OnChangeWeaponFilter(cvar_t *var, char *oldval)\r\n{\r\n\t// Parse the weapon filter.\r\n\tradar_show_ssg\t\t= Utils_RegExpMatch(\"SSG|SUPERSHOTGUN|ALL\",\t\t\tvar->string);\r\n\tradar_show_ng\t\t= Utils_RegExpMatch(\"([^S]|^)NG|NAILGUN|ALL\",\t\tvar->string); // Yes very ugly, but we don't want to match SNG.\r\n\tradar_show_sng\t\t= Utils_RegExpMatch(\"SNG|SUPERNAILGUN|ALL\",\t\t\tvar->string);\r\n\tradar_show_rl\t\t= Utils_RegExpMatch(\"RL|ROCKETLAUNCHER|ALL\",\t\tvar->string);\r\n\tradar_show_gl\t\t= Utils_RegExpMatch(\"GL|GRENADELAUNCHER|ALL\",\t\tvar->string);\r\n\tradar_show_lg\t\t= Utils_RegExpMatch(\"LG|SHAFT|LIGHTNING|ALL\",\t\tvar->string);\r\n}\r\n\r\nvoid Radar_OnChangeItemFilter(cvar_t *var, char *oldval)\r\n{\r\n\t// Parse the item filter.\r\n\tradar_show_backpacks\t\t= Utils_RegExpMatch(\"BP|BACKPACK|ALL\",\t\t\t\t\tvar->string);\r\n\tradar_show_health\t\t\t= Utils_RegExpMatch(\"HP|HEALTH|ALL\",\t\t\t\t\tvar->string);\r\n\tradar_show_ra\t\t\t\t= Utils_RegExpMatch(\"RA|REDARMOR|ARMOR|ALL\",\t\t\tvar->string);\r\n\tradar_show_ya\t\t\t\t= Utils_RegExpMatch(\"YA|YELLOWARMOR|ARMOR|ALL\",\t\t\tvar->string);\r\n\tradar_show_ga\t\t\t\t= Utils_RegExpMatch(\"GA|GREENARMOR|ARMOR|ALL\",\t\t\tvar->string);\r\n\tradar_show_rockets\t\t\t= Utils_RegExpMatch(\"ROCKETS|ROCKS|AMMO|ALL\",\t\t\tvar->string);\r\n\tradar_show_nails\t\t\t= Utils_RegExpMatch(\"NAILS|SPIKES|AMMO|ALL\",\t\t\tvar->string);\r\n\tradar_show_cells\t\t\t= Utils_RegExpMatch(\"CELLS|BATTERY|AMMO|ALL\",\t\t\tvar->string);\r\n\tradar_show_shells\t\t\t= Utils_RegExpMatch(\"SHELLS|AMMO|ALL\",\t\t\t\t\tvar->string);\r\n\tradar_show_quad\t\t\t\t= Utils_RegExpMatch(\"QUAD|POWERUPS|ALL\",\t\t\t\tvar->string);\r\n\tradar_show_pent\t\t\t\t= Utils_RegExpMatch(\"PENT|PENTAGRAM|666|POWERUPS|ALL\",\tvar->string);\r\n\tradar_show_ring\t\t\t\t= Utils_RegExpMatch(\"RING|INVISIBLE|EYES|POWERUPS|ALL\",\tvar->string);\r\n\tradar_show_suit\t\t\t\t= Utils_RegExpMatch(\"SUIT|POWERUPS|ALL\",\t\t\t\tvar->string);\r\n\tradar_show_mega\t\t\t\t= Utils_RegExpMatch(\"MH|MEGA|MEGAHEALTH|100\\\\+|ALL\",\tvar->string);\r\n}\r\n\r\nvoid Radar_OnChangeOtherFilter(cvar_t *var, char *oldval)\r\n{\r\n\t// Parse the \"other\" filter.\r\n\tradar_show_nails_p\t\t\t= Utils_RegExpMatch(\"NAILS|PROJECTILES|ALL\",\tvar->string);\r\n\tradar_show_rockets_p\t\t= Utils_RegExpMatch(\"ROCKETS|PROJECTILES|ALL\",\tvar->string);\r\n\tradar_show_shaft_p\t\t\t= Utils_RegExpMatch(\"SHAFT|PROJECTILES|ALL\",\tvar->string);\r\n\tradar_show_gibs\t\t\t\t= Utils_RegExpMatch(\"GIBS|ALL\",\t\t\t\t\tvar->string);\r\n\tradar_show_explosions\t\t= Utils_RegExpMatch(\"EXPLOSIONS|ALL\",\t\t\tvar->string);\r\n\tradar_show_teleport\t\t\t= Utils_RegExpMatch(\"TELE|ALL\",\t\t\t\t\tvar->string);\r\n\tradar_show_shotgun\t\t\t= Utils_RegExpMatch(\"SHOTGUN|SG|BUCK|ALL\",\t\tvar->string);\r\n}\r\n\r\n\r\n#define HUD_COLOR_DEFAULT_TRANSPARENCY\t75\r\n\r\nbyte hud_radar_highlight_color[4] = {255, 255, 0, HUD_COLOR_DEFAULT_TRANSPARENCY};\r\n\r\nvoid Radar_OnChangeHighlightColor(cvar_t *var, char *newval, qbool *cancel)\r\n{\r\n\tchar *new_color;\r\n\tchar buf[MAX_COM_TOKEN];\r\n\r\n\t// Translate a colors name to RGB values.\r\n\tnew_color = ColorNameToRGBString(newval);\r\n\r\n\t// Parse the colors.\r\n\t//color = StringToRGB(new_color);\r\n\tstrlcpy(buf,new_color,sizeof(buf));\r\n\tmemcpy(hud_radar_highlight_color, StringToRGB(buf), sizeof(byte) * 4);\r\n\r\n\t// Set the cvar to contain the new color string\r\n\t// (if the user entered \"red\" it will be \"255 0 0\").\r\n\tCvar_Set(var, new_color);\r\n}\r\n\r\nvoid Radar_DrawEntities(int x, int y, float scale, float player_size, int show_hold_areas)\r\n{\r\n\tint i;\r\n\r\n\t// Entities (weapons and such). cl_main.c\r\n\textern visentlist_t cl_visents;\r\n\r\n\t// Go through all the entities and draw the ones we're supposed to.\r\n\tfor (i = 0; i < cl_visents.count; i++)\r\n\t{\r\n\t\tint entity_q_x = 0;\r\n\t\tint entity_q_y = 0;\r\n\t\tint entity_p_x = 0;\r\n\t\tint entity_p_y = 0;\r\n\r\n\t\t// Get quake coordinates (times 8 to get them in the same format as .locs).\r\n\t\tentity_q_x = cl_visents.list[i].origin[0]*8;\r\n\t\tentity_q_y = cl_visents.list[i].origin[1]*8;\r\n\r\n\t\t// Convert from quake coordiantes -> pixel coordinates.\r\n\t\tentity_p_x = x + Q_rint((map_x_slope*entity_q_x + map_x_intercept) * scale);\r\n\t\tentity_p_y = y + Q_rint((map_y_slope*entity_q_y + map_y_intercept) * scale);\r\n\r\n\t\t// TODO: Replace all model name comparison below with MOD_HINT's instead for less comparisons (create new ones in Mod_LoadAliasModel() in r_model.c and gl_model.c/.h for the ones that don't have one already).\r\n\r\n\t\t//\r\n\t\t// Powerups.\r\n\t\t//\r\n\r\n\t\tif(radar_show_pent && !strcmp(cl_visents.list[i].model->name, \"progs/invulner.mdl\"))\r\n\t\t{\r\n\t\t\t// Pentagram.\r\n\t\t\tDraw_ColoredString(entity_p_x, entity_p_y, \"&cf00P\", 0);\r\n\t\t}\r\n\t\telse if(radar_show_quad && !strcmp(cl_visents.list[i].model->name, \"progs/quaddama.mdl\"))\r\n\t\t{\r\n\t\t\t// Quad.\r\n\t\t\tDraw_ColoredString(entity_p_x, entity_p_y, \"&c0ffQ\", 0);\r\n\t\t}\r\n\t\telse if(radar_show_ring && !strcmp(cl_visents.list[i].model->name, \"progs/invisibl.mdl\"))\r\n\t\t{\r\n\t\t\t// Ring.\r\n\t\t\tDraw_ColoredString(entity_p_x, entity_p_y, \"&cff0R\", 0);\r\n\t\t}\r\n\t\telse if(radar_show_suit && !strcmp(cl_visents.list[i].model->name, \"progs/suit.mdl\"))\r\n\t\t{\r\n\t\t\t// Suit.\r\n\t\t\tDraw_ColoredString(entity_p_x, entity_p_y, \"&c0f0S\", 0);\r\n\t\t}\r\n\r\n\t\t//\r\n\t\t// Show RL, LG and backpacks.\r\n\t\t//\r\n\t\tif(radar_show_rl && !strcmp(cl_visents.list[i].model->name, \"progs/g_rock2.mdl\"))\r\n\t\t{\r\n\t\t\t// RL.\r\n\t\t\tDraw_String(entity_p_x - (2*8)/2, entity_p_y - 4, \"RL\");\r\n\t\t}\r\n\t\telse if(radar_show_lg && !strcmp(cl_visents.list[i].model->name, \"progs/g_light.mdl\"))\r\n\t\t{\r\n\t\t\t// LG.\r\n\t\t\tDraw_String(entity_p_x - (2*8)/2, entity_p_y - 4, \"LG\");\r\n\t\t}\r\n\t\telse if(radar_show_backpacks && cl_visents.list[i].model->modhint == MOD_BACKPACK)\r\n\t\t{\r\n\t\t\t// Back packs.\r\n\t\t\tfloat back_pack_size = 0;\r\n\r\n\t\t\tback_pack_size = max(player_size * 0.5, 0.05);\r\n\r\n\t\t\tDraw_AlphaCircleFill (entity_p_x, entity_p_y, back_pack_size, 114, 1);\r\n\t\t\tDraw_AlphaCircleOutline (entity_p_x, entity_p_y, back_pack_size, 1.0, 0, 1);\r\n\t\t}\r\n\r\n\t\tif(!strcmp(cl_visents.list[i].model->name, \"progs/armor.mdl\"))\r\n\t\t{\r\n\t\t\t//\r\n\t\t\t// Show armors.\r\n\t\t\t//\r\n\r\n\t\t\tif(radar_show_ga && cl_visents.list[i].skinnum == HUD_RADAR_GA)\r\n\t\t\t{\r\n\t\t\t\t// GA.\r\n\t\t\t\tDraw_AlphaCircleFill (entity_p_x, entity_p_y, 3.0, 178, 1.0);\r\n\t\t\t}\r\n\t\t\telse if(radar_show_ya && cl_visents.list[i].skinnum == HUD_RADAR_YA)\r\n\t\t\t{\r\n\t\t\t\t// YA.\r\n\t\t\t\tDraw_AlphaCircleFill (entity_p_x, entity_p_y, 3.0, 192, 1.0);\r\n\t\t\t}\r\n\t\t\telse if(radar_show_ra && cl_visents.list[i].skinnum == HUD_RADAR_RA)\r\n\t\t\t{\r\n\t\t\t\t// RA.\r\n\t\t\t\tDraw_AlphaCircleFill (entity_p_x, entity_p_y, 3.0, 251, 1.0);\r\n\t\t\t}\r\n\r\n\t\t\tDraw_AlphaCircleOutline (entity_p_x, entity_p_y, 3.0, 1.0, 0, 1.0);\r\n\t\t}\r\n\r\n\t\tif(radar_show_mega && !strcmp(cl_visents.list[i].model->name, \"maps/b_bh100.bsp\"))\r\n\t\t{\r\n\t\t\t//\r\n\t\t\t// Show megahealth.\r\n\t\t\t//\r\n\r\n\t\t\t// Draw a red border around the cross.\r\n\t\t\tDraw_AlphaRectangleRGB(entity_p_x - 3, entity_p_y - 3, 8, 8, 1, false, RGBA_TO_COLOR(200, 0, 0, 200));\r\n\r\n\t\t\t// Draw a black outline cross.\r\n\t\t\tDraw_AlphaFill(entity_p_x - 3, entity_p_y - 1, 8, 4, 0, 1);\r\n\t\t\tDraw_AlphaFill(entity_p_x - 1, entity_p_y - 3, 4, 8, 0, 1);\r\n\r\n\t\t\t// Draw a 2 pixel cross.\r\n\t\t\tDraw_AlphaFill(entity_p_x - 2, entity_p_y, 6, 2, 79, 1);\r\n\t\t\tDraw_AlphaFill(entity_p_x, entity_p_y - 2, 2, 6, 79, 1);\r\n\t\t}\r\n\r\n\t\tif(radar_show_ssg && !strcmp(cl_visents.list[i].model->name, \"progs/g_shot.mdl\"))\r\n\t\t{\r\n\t\t\t// SSG.\r\n\t\t\tDraw_String(entity_p_x - (3*8)/2, entity_p_y - 4, \"SSG\");\r\n\t\t}\r\n\t\telse if(radar_show_ng && !strcmp(cl_visents.list[i].model->name, \"progs/g_nail.mdl\"))\r\n\t\t{\r\n\t\t\t// NG.\r\n\t\t\tDraw_String(entity_p_x - (2*8)/2, entity_p_y - 4, \"NG\");\r\n\t\t}\r\n\t\telse if(radar_show_sng && !strcmp(cl_visents.list[i].model->name, \"progs/g_nail2.mdl\"))\r\n\t\t{\r\n\t\t\t// SNG.\r\n\t\t\tDraw_String(entity_p_x - (3*8)/2, entity_p_y - 4, \"SNG\");\r\n\t\t}\r\n\t\telse if(radar_show_gl && !strcmp(cl_visents.list[i].model->name, \"progs/g_rock.mdl\"))\r\n\t\t{\r\n\t\t\t// GL.\r\n\t\t\tDraw_String(entity_p_x - (2*8)/2, entity_p_y - 4, \"GL\");\r\n\t\t}\r\n\r\n\t\tif(radar_show_gibs\r\n\t\t\t&&(!strcmp(cl_visents.list[i].model->name, \"progs/gib1.mdl\")\r\n\t\t\t|| !strcmp(cl_visents.list[i].model->name, \"progs/gib2.mdl\")\r\n\t\t\t|| !strcmp(cl_visents.list[i].model->name, \"progs/gib3.mdl\")))\r\n\t\t{\r\n\t\t\t//\r\n\t\t\t// Gibs.\r\n\t\t\t//\r\n\r\n\t\t\tDraw_AlphaCircleFill(entity_p_x, entity_p_y, 2.0, 251, 1);\r\n\t\t}\r\n\r\n\t\tif(radar_show_health\r\n\t\t\t&&(!strcmp(cl_visents.list[i].model->name, \"maps/b_bh25.bsp\")\r\n\t\t\t|| !strcmp(cl_visents.list[i].model->name, \"maps/b_bh10.bsp\")))\r\n\t\t{\r\n\t\t\t//\r\n\t\t\t// Health.\r\n\t\t\t//\r\n\r\n\t\t\t// Draw a black outline cross.\r\n\t\t\tDraw_AlphaFill (entity_p_x - 3, entity_p_y - 1, 7, 3, 0, 1);\r\n\t\t\tDraw_AlphaFill (entity_p_x - 1, entity_p_y - 3, 3, 7, 0, 1);\r\n\r\n\t\t\t// Draw a cross.\r\n\t\t\tDraw_AlphaFill (entity_p_x - 2, entity_p_y, 5, 1, 79, 1);\r\n\t\t\tDraw_AlphaFill (entity_p_x, entity_p_y - 2, 1, 5, 79, 1);\r\n\t\t}\r\n\r\n\t\t//\r\n\t\t// Ammo.\r\n\t\t//\r\n\t\tif(radar_show_rockets\r\n\t\t\t&&(!strcmp(cl_visents.list[i].model->name, \"maps/b_rock0.bsp\")\r\n\t\t\t|| !strcmp(cl_visents.list[i].model->name, \"maps/b_rock1.bsp\")))\r\n\t\t{\r\n\t\t\t//\r\n\t\t\t// Rockets.\r\n\t\t\t//\r\n\r\n\t\t\t// Draw a black outline.\r\n\t\t\tDraw_AlphaFill (entity_p_x - 1, entity_p_y - 6, 3, 5, 0, 1);\r\n\t\t\tDraw_AlphaFill (entity_p_x - 2, entity_p_y - 1, 5, 5, 0, 1);\r\n\r\n\t\t\t// The brown rocket.\r\n\t\t\tDraw_AlphaFill (entity_p_x, entity_p_y - 5, 1, 5, 120, 1);\r\n\t\t\tDraw_AlphaFill (entity_p_x - 1, entity_p_y, 1, 3, 120, 1);\r\n\t\t\tDraw_AlphaFill (entity_p_x + 1, entity_p_y, 1, 3, 120, 1);\r\n\t\t}\r\n\r\n\t\tif(radar_show_cells\r\n\t\t\t&&(!strcmp(cl_visents.list[i].model->name, \"maps/b_batt0.bsp\")\r\n\t\t\t|| !strcmp(cl_visents.list[i].model->name, \"maps/b_batt1.bsp\")))\r\n\t\t{\r\n\t\t\t//\r\n\t\t\t// Cells.\r\n\t\t\t//\r\n\r\n\t\t\t// Draw a black outline.\r\n\t\t\tDraw_AlphaLine(entity_p_x - 3, entity_p_y, entity_p_x + 4, entity_p_y - 5, 3, 0, 1);\r\n\t\t\tDraw_AlphaLine(entity_p_x - 3, entity_p_y, entity_p_x + 3 , entity_p_y, 3, 0, 1);\r\n\t\t\tDraw_AlphaLine(entity_p_x + 3, entity_p_y, entity_p_x - 3, entity_p_y + 4, 3, 0, 1);\r\n\r\n\t\t\t// Draw a yellow lightning!\r\n\t\t\tDraw_AlphaLine(entity_p_x - 2, entity_p_y, entity_p_x + 3, entity_p_y - 4, 1, 111, 1);\r\n\t\t\tDraw_AlphaLine(entity_p_x - 2, entity_p_y, entity_p_x + 2 , entity_p_y, 1, 111, 1);\r\n\t\t\tDraw_AlphaLine(entity_p_x + 2, entity_p_y, entity_p_x - 2, entity_p_y + 3, 1, 111, 1);\r\n\t\t}\r\n\r\n\t\tif(radar_show_nails\r\n\t\t\t&&(!strcmp(cl_visents.list[i].model->name, \"maps/b_nail0.bsp\")\r\n\t\t\t|| !strcmp(cl_visents.list[i].model->name, \"maps/b_nail1.bsp\")))\r\n\t\t{\r\n\t\t\t//\r\n\t\t\t// Nails.\r\n\t\t\t//\r\n\r\n\t\t\t// Draw a black outline.\r\n\t\t\tDraw_AlphaFill (entity_p_x - 3, entity_p_y - 3, 7, 3, 0, 1);\r\n\t\t\tDraw_AlphaFill (entity_p_x - 2, entity_p_y - 2, 5, 3, 0, 0.5);\r\n\t\t\tDraw_AlphaFill (entity_p_x - 1, entity_p_y, 3, 3, 0, 1);\r\n\t\t\tDraw_AlphaFill (entity_p_x - 1, entity_p_y + 3, 1, 1, 0, 0.5);\r\n\t\t\tDraw_AlphaFill (entity_p_x + 1, entity_p_y + 3, 1, 1, 0, 0.5);\r\n\t\t\tDraw_AlphaFill (entity_p_x, entity_p_y + 4, 1, 1, 0, 1);\r\n\r\n\t\t\tDraw_AlphaFill (entity_p_x - 2, entity_p_y - 2, 5, 1, 6, 1);\r\n\t\t\tDraw_AlphaFill (entity_p_x - 1, entity_p_y - 1, 3, 1, 6, 0.5);\r\n\t\t\tDraw_AlphaFill (entity_p_x, entity_p_y, 1, 4, 6, 1);\r\n\t\t}\r\n\r\n\t\tif(radar_show_shells\r\n\t\t\t&&(!strcmp(cl_visents.list[i].model->name, \"maps/b_shell0.bsp\")\r\n\t\t\t|| !strcmp(cl_visents.list[i].model->name, \"maps/b_shell1.bsp\")))\r\n\t\t{\r\n\t\t\t//\r\n\t\t\t// Shells.\r\n\t\t\t//\r\n\r\n\t\t\t// Draw a black outline.\r\n\t\t\tDraw_AlphaFill (entity_p_x - 2, entity_p_y - 3, 5, 9, 0, 1);\r\n\r\n\t\t\t// Draw 2 shotgun shells.\r\n\t\t\tDraw_AlphaFill (entity_p_x - 1, entity_p_y - 2, 1, 4, 73, 1);\r\n\t\t\tDraw_AlphaFill (entity_p_x - 1, entity_p_y - 2 + 5, 1, 2, 104, 1);\r\n\r\n\t\t\tDraw_AlphaFill (entity_p_x + 1, entity_p_y - 2, 1, 4, 73, 1);\r\n\t\t\tDraw_AlphaFill (entity_p_x + 1, entity_p_y - 2 + 5, 1, 2, 104, 1);\r\n\t\t}\r\n\r\n\t\t//\r\n\t\t// Show projectiles (rockets, grenades, nails, shaft).\r\n\t\t//\r\n\r\n\t\tif(radar_show_nails_p\r\n\t\t\t&& (!strcmp(cl_visents.list[i].model->name, \"progs/s_spike.mdl\")\r\n\t\t\t|| !strcmp(cl_visents.list[i].model->name, \"progs/spike.mdl\")))\r\n\t\t{\r\n\t\t\t//\r\n\t\t\t// Spikes from SNG and NG.\r\n\t\t\t//\r\n\r\n\t\t\tDraw_AlphaFill(entity_p_x, entity_p_y, 1, 1, 254, 1);\r\n\t\t}\r\n\t\telse if(radar_show_rockets_p\r\n\t\t\t&& (!strcmp(cl_visents.list[i].model->name, \"progs/missile.mdl\")\r\n\t\t\t|| !strcmp(cl_visents.list[i].model->name, \"progs/grenade.mdl\")))\r\n\t\t{\r\n\t\t\t//\r\n\t\t\t// Rockets and grenades.\r\n\t\t\t//\r\n\r\n\t\t\tfloat entity_angle = 0;\r\n\t\t\tint x_line_end = 0;\r\n\t\t\tint y_line_end = 0;\r\n\r\n\t\t\t// Get the entity angle in radians.\r\n\t\t\tentity_angle = DEG2RAD(cl_visents.list[i].angles[1]);\r\n\r\n\t\t\tx_line_end = entity_p_x + 5 * cos(entity_angle) * scale;\r\n\t\t\ty_line_end = entity_p_y - 5 * sin(entity_angle) * scale;\r\n\r\n\t\t\t// Draw the rocket/grenade showing it's angle also.\r\n\t\t\tDraw_AlphaLine (entity_p_x, entity_p_y, x_line_end, y_line_end, 1.0, 254, 1);\r\n\t\t}\r\n\t\telse if(radar_show_shaft_p\r\n\t\t\t&& (!strcmp(cl_visents.list[i].model->name, \"progs/bolt.mdl\")\r\n\t\t\t|| !strcmp(cl_visents.list[i].model->name, \"progs/bolt2.mdl\")\r\n\t\t\t|| !strcmp(cl_visents.list[i].model->name, \"progs/bolt3.mdl\")))\r\n\t\t{\r\n\t\t\t//\r\n\t\t\t// Shaft beam.\r\n\t\t\t//\r\n\r\n\t\t\tfloat entity_angle = 0;\r\n\t\t\tfloat shaft_length = 0;\r\n\t\t\tfloat x_line_end = 0;\r\n\t\t\tfloat y_line_end = 0;\r\n\r\n\t\t\t// Get the length and angle of the shaft.\r\n\t\t\tshaft_length = cl_visents.list[i].model->maxs[1];\r\n\t\t\tentity_angle = (cl_visents.list[i].angles[1]*M_PI)/180;\r\n\r\n\t\t\t// Calculate where the shaft beam's ending point.\r\n\t\t\tx_line_end = entity_p_x + shaft_length * cos(entity_angle);\r\n\t\t\ty_line_end = entity_p_y - shaft_length * sin(entity_angle);\r\n\r\n\t\t\t// Draw the shaft beam.\r\n\t\t\tDraw_AlphaLine (entity_p_x, entity_p_y, x_line_end, y_line_end, 1.0, 254, 1);\r\n\t\t}\r\n\t}\r\n\r\n\t// Draw a circle around \"hold areas\", the grid cells within this circle\r\n\t// are the ones that are counted for that particular hold area. The team\r\n\t// that has the most percentage of these cells is considered to hold that area.\r\n\tif(show_hold_areas && stats_important_ents != NULL && stats_important_ents->list != NULL)\r\n\t{\r\n\t\tint entity_p_x = 0;\r\n\t\tint entity_p_y = 0;\r\n\r\n\t\tfor(i = 0; i < stats_important_ents->count; i++)\r\n\t\t{\r\n\t\t\tentity_p_x = x + Q_rint((map_x_slope*8*stats_important_ents->list[i].origin[0] + map_x_intercept) * scale);\r\n\t\t\tentity_p_y = y + Q_rint((map_y_slope*8*stats_important_ents->list[i].origin[1] + map_y_intercept) * scale);\r\n\r\n\t\t\tDraw_ColoredString(entity_p_x  - (8 * strlen(stats_important_ents->list[i].name)) / 2.0, entity_p_y - 4,\r\n\t\t\t\tva(\"&c55f%s\", stats_important_ents->list[i].name), 0);\r\n\r\n\t\t\tDraw_AlphaCircleOutline(entity_p_x , entity_p_y, map_x_slope * 8 * stats_important_ents->hold_radius * scale, 1.0, 15, 0.2);\r\n\t\t}\r\n\t}\r\n\r\n\t//\r\n\t// Draw temp entities (explosions, blood, teleport effects).\r\n\t//\r\n\tfor(i = 0; i < MAX_TEMP_ENTITIES; i++)\r\n\t{\r\n\t\tfloat time_diff = 0.0;\r\n\r\n\t\tint entity_q_x = 0;\r\n\t\tint entity_q_y = 0;\r\n\t\tint entity_p_x = 0;\r\n\t\tint entity_p_y = 0;\r\n\r\n\t\t// Get the time since the entity spawned.\r\n\t\tif(cls.demoplayback)\r\n\t\t{\r\n\t\t\ttime_diff = cls.demotime - temp_entities.list[i].time;\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\ttime_diff = cls.realtime - temp_entities.list[i].time;\r\n\t\t}\r\n\r\n\t\t// Don't show temp entities for long.\r\n\t\tif(time_diff < 0.25)\r\n\t\t{\r\n\t\t\tfloat radius = 0.0;\r\n\t\t\tradius = (time_diff < 0.125) ? (time_diff * 32.0) : (time_diff * 32.0) - time_diff;\r\n\t\t\tradius *= scale;\r\n\t\t\tradius = min(max(radius, 0), 200);\r\n\r\n\t\t\t// Get quake coordinates (times 8 to get them in the same format as .locs).\r\n\t\t\tentity_q_x = temp_entities.list[i].pos[0]*8;\r\n\t\t\tentity_q_y = temp_entities.list[i].pos[1]*8;\r\n\r\n\t\t\tentity_p_x = x + Q_rint((map_x_slope*entity_q_x + map_x_intercept) * scale);\r\n\t\t\tentity_p_y = y + Q_rint((map_y_slope*entity_q_y + map_y_intercept) * scale);\r\n\r\n\t\t\tif(radar_show_explosions\r\n\t\t\t\t&& (temp_entities.list[i].type == TE_EXPLOSION\r\n\t\t\t\t|| temp_entities.list[i].type == TE_TAREXPLOSION))\r\n\t\t\t{\r\n\t\t\t\t//\r\n\t\t\t\t// Explosions.\r\n\t\t\t\t//\r\n\r\n\t\t\t\tDraw_AlphaCircleFill (entity_p_x, entity_p_y, radius, 235, 0.8);\r\n\t\t\t}\r\n\t\t\telse if(radar_show_teleport && temp_entities.list[i].type == TE_TELEPORT)\r\n\t\t\t{\r\n\t\t\t\t//\r\n\t\t\t\t// Teleport effect.\r\n\t\t\t\t//\r\n\r\n\t\t\t\tradius *= 1.5;\r\n\t\t\t\tDraw_AlphaCircleFill (entity_p_x, entity_p_y, radius, 244, 0.8);\r\n\t\t\t}\r\n\t\t\telse if(radar_show_shotgun && temp_entities.list[i].type == TE_GUNSHOT)\r\n\t\t\t{\r\n\t\t\t\t//\r\n\t\t\t\t// Shotgun fire.\r\n\t\t\t\t//\r\n\r\n\t\t\t\t#define SHOTGUN_SPREAD 10\r\n\t\t\t\tint spread_x = 0;\r\n\t\t\t\tint spread_y = 0;\r\n\t\t\t\tint n = 0;\r\n\r\n\t\t\t\tfor(n = 0; n < 10; n++)\r\n\t\t\t\t{\r\n\t\t\t\t\tspread_x = (int)(rand() / (((double)RAND_MAX + 1) / SHOTGUN_SPREAD));\r\n\t\t\t\t\tspread_y = (int)(rand() / (((double)RAND_MAX + 1) / SHOTGUN_SPREAD));\r\n\r\n\t\t\t\t\tDraw_AlphaFill (entity_p_x + spread_x - (SHOTGUN_SPREAD/2), entity_p_y + spread_y - (SHOTGUN_SPREAD/2), 1, 1, 8, 0.9);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n}\r\n\r\nvoid Radar_DrawPlayers(int x, int y, int width, int height, float scale,\r\n\t\t\t\t\t   float show_height, float show_powerups,\r\n\t\t\t\t\t   float player_size, float show_names,\r\n\t\t\t\t\t   float fade_players, float highlight,\r\n\t\t\t\t\t   char *highlight_color)\r\n{\r\n\tint i;\r\n\tplayer_state_t *state;\r\n\tplayer_info_t *info;\r\n\r\n\t// Get player state so we can know where he is (or on rare occassions, she).\r\n\tstate = cl.frames[cl.oldparsecount & UPDATE_MASK].playerstate;\r\n\r\n\t// Get the info for the player.\r\n\tinfo = cl.players;\r\n\r\n\t//\r\n\t// Draw the players.\r\n\t//\r\n\tfor (i = 0; i < MAX_CLIENTS; i++, info++, state++)\r\n\t{\r\n\t\t// Players quake coordinates\r\n\t\t// (these are multiplied by 8, since the conversion formula was\r\n\t\t// calculated using the coordinates in a .loc-file, which are in\r\n\t\t// the format quake-coordainte*8).\r\n\t\tint player_q_x = 0;\r\n\t\tint player_q_y = 0;\r\n\r\n\t\t// The height of the player.\r\n\t\tfloat player_z = 1.0;\r\n\t\tfloat player_z_relative = 1.0;\r\n\r\n\t\t// Players pixel coordinates.\r\n\t\tint player_p_x = 0;\r\n\t\tint player_p_y = 0;\r\n\r\n\t\t// Used for drawing the the direction the\r\n\t\t// player is looking at.\r\n\t\tfloat player_angle = 0;\r\n\t\tint x_line_start = 0;\r\n\t\tint y_line_start = 0;\r\n\t\tint x_line_end = 0;\r\n\t\tint y_line_end = 0;\r\n\r\n\t\t// Color and opacity of the player.\r\n\t\tint player_color = 0;\r\n\t\tfloat player_alpha = 1.0;\r\n\r\n\t\t// Make sure we're not drawing any ghosts.\r\n\t\tif(!info->name[0])\r\n\t\t{\r\n\t\t\tcontinue;\r\n\t\t}\r\n\r\n\t\tif (state->messagenum == cl.oldparsecount)\r\n\t\t{\r\n\t\t\t// TODO: Implement lerping to get smoother drawing.\r\n\r\n\t\t\t// Get the quake coordinates. Multiply by 8 since\r\n\t\t\t// the conversion formula has been calculated using\r\n\t\t\t// a .loc-file which is in that format.\r\n\t\t\tplayer_q_x = state->origin[0]*8;\r\n\t\t\tplayer_q_y = state->origin[1]*8;\r\n\r\n\t\t\t// Get the players view angle.\r\n\t\t\tplayer_angle = cls.demoplayback ? state->viewangles[1] : cl.simangles[1];\r\n\r\n\t\t\t// Convert from quake coordiantes -> pixel coordinates.\r\n\t\t\tplayer_p_x = Q_rint((map_x_slope*player_q_x + map_x_intercept) * scale);\r\n\t\t\tplayer_p_y = Q_rint((map_y_slope*player_q_y + map_y_intercept) * scale);\r\n\r\n\t\t\tplayer_color = Sbar_BottomColor(info);\r\n\r\n\t\t\t// Calculate the height of the player.\r\n\t\t\tif(show_height)\r\n\t\t\t{\r\n\t\t\t\tplayer_z = state->origin[2];\r\n\t\t\t\tplayer_z += (player_z >= 0) ? fabs(cl.worldmodel->mins[2]) : fabs(cl.worldmodel->maxs[2]);\r\n\t\t\t\tplayer_z_relative = min(fabs(player_z / map_height_diff), 1.0);\r\n\t\t\t\tplayer_z_relative = max(player_z_relative, 0.2);\r\n\t\t\t}\r\n\r\n\t\t\t// Make the players fade out as they get less armor/health.\r\n\t\t\tif(fade_players)\r\n\t\t\t{\r\n\t\t\t\tint armor_strength = 0;\r\n\t\t\t\tarmor_strength = (info->stats[STAT_ITEMS] & IT_ARMOR1) ? 100 :\r\n\t\t\t\t\t((info->stats[STAT_ITEMS] & IT_ARMOR2) ? 150 :\r\n\t\t\t\t\t((info->stats[STAT_ITEMS] & IT_ARMOR3) ? 200 : 0));\r\n\r\n\t\t\t\t// Don't let the players get completly transparent so add 0.2 to the final value.\r\n\t\t\t\tplayer_alpha = ((info->stats[STAT_HEALTH] + (info->stats[STAT_ARMOR] * armor_strength)) / 100.0) + 0.2;\r\n\t\t\t}\r\n\r\n\t\t\t// Turn dead people red.\r\n\t\t\tif(info->stats[STAT_HEALTH] <= 0)\r\n\t\t\t{\r\n\t\t\t\tplayer_alpha = 1.0;\r\n\t\t\t\tplayer_color = 79;\r\n\t\t\t}\r\n\r\n\t\t\t// Draw a ring around players with powerups if it's enabled.\r\n\t\t\tif(show_powerups)\r\n\t\t\t{\r\n\t\t\t\tif(info->stats[STAT_ITEMS] & IT_INVISIBILITY)\r\n\t\t\t\t{\r\n\t\t\t\t\tDraw_AlphaCircleFill (x + player_p_x, y + player_p_y, player_size*2*player_z_relative, 161, 0.2);\r\n\t\t\t\t}\r\n\r\n\t\t\t\tif(info->stats[STAT_ITEMS] & IT_INVULNERABILITY)\r\n\t\t\t\t{\r\n\t\t\t\t\tDraw_AlphaCircleFill (x + player_p_x, y + player_p_y, player_size*2*player_z_relative, 79, 0.5);\r\n\t\t\t\t}\r\n\r\n\t\t\t\tif(info->stats[STAT_ITEMS] & IT_QUAD)\r\n\t\t\t\t{\r\n\t\t\t\t\tDraw_AlphaCircleFill (x + player_p_x, y + player_p_y, player_size*2*player_z_relative, 244, 0.2);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\t#define HUD_RADAR_HIGHLIGHT_NONE\t\t\t0\r\n\t\t\t#define HUD_RADAR_HIGHLIGHT_TEXT_ONLY\t\t1\r\n\t\t\t#define HUD_RADAR_HIGHLIGHT_OUTLINE\t\t\t2\r\n\t\t\t#define HUD_RADAR_HIGHLIGHT_FIXED_OUTLINE\t3\r\n\t\t\t#define HUD_RADAR_HIGHLIGHT_CIRCLE\t\t\t4\r\n\t\t\t#define HUD_RADAR_HIGHLIGHT_FIXED_CIRCLE\t5\r\n\t\t\t#define HUD_RADAR_HIGHLIGHT_ARROW_BOTTOM\t6\r\n\t\t\t#define HUD_RADAR_HIGHLIGHT_ARROW_CENTER\t7\r\n\t\t\t#define HUD_RADAR_HIGHLIGHT_ARROW_TOP\t\t8\r\n\t\t\t#define HUD_RADAR_HIGHLIGHT_CROSS_CORNERS\t9\r\n\r\n\t\t\t// Draw a circle around the tracked player.\r\n\t\t\tif (highlight != HUD_RADAR_HIGHLIGHT_NONE && Cam_TrackNum() >= 0 && info->userid == cl.players[Cam_TrackNum()].userid)\r\n\t\t\t{\r\n\t\t\t\tcolor_t higlight_color = RGBAVECT_TO_COLOR(hud_radar_highlight_color);\r\n\r\n\t\t\t\t// Draw the highlight.\r\n\t\t\t\tswitch ((int)highlight)\r\n\t\t\t\t{\r\n\t\t\t\t\tcase HUD_RADAR_HIGHLIGHT_CROSS_CORNERS :\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\t// Top left\r\n\t\t\t\t\t\tDraw_AlphaLineRGB (x, y, x + player_p_x, y + player_p_y, 2, higlight_color);\r\n\r\n\t\t\t\t\t\t// Top right.\r\n\t\t\t\t\t\tDraw_AlphaLineRGB (x + width, y, x + player_p_x, y + player_p_y, 2, higlight_color);\r\n\r\n\t\t\t\t\t\t// Bottom left.\r\n\t\t\t\t\t\tDraw_AlphaLineRGB (x, y + height, x + player_p_x, y + player_p_y, 2, higlight_color);\r\n\r\n\t\t\t\t\t\t// Bottom right.\r\n\t\t\t\t\t\tDraw_AlphaLineRGB (x + width, y + height, x + player_p_x, y + player_p_y, 2, higlight_color);\r\n\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t}\r\n\t\t\t\t\tcase HUD_RADAR_HIGHLIGHT_ARROW_TOP :\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\t// Top center.\r\n\t\t\t\t\t\tDraw_AlphaLineRGB (x + width / 2, y, x + player_p_x, y + player_p_y, 2, higlight_color);\r\n\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t}\r\n\t\t\t\t\tcase HUD_RADAR_HIGHLIGHT_ARROW_CENTER :\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\t// Center.\r\n\t\t\t\t\t\tDraw_AlphaLineRGB (x + width / 2, y + height / 2, x + player_p_x, y + player_p_y, 2, higlight_color);\r\n\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t}\r\n\t\t\t\t\tcase HUD_RADAR_HIGHLIGHT_ARROW_BOTTOM :\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\t// Bottom center.\r\n\t\t\t\t\t\tDraw_AlphaLineRGB (x + width / 2, y + height, x + player_p_x, y + player_p_y, 2, higlight_color);\r\n\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t}\r\n\t\t\t\t\tcase HUD_RADAR_HIGHLIGHT_FIXED_CIRCLE :\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tDraw_AlphaCircleRGB (x + player_p_x, y + player_p_y, player_size * 1.5, 1.0, true, higlight_color);\r\n\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t}\r\n\t\t\t\t\tcase HUD_RADAR_HIGHLIGHT_CIRCLE :\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tDraw_AlphaCircleRGB (x + player_p_x, y + player_p_y, player_size * player_z_relative * 2.0, 1.0, true, higlight_color);\r\n\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t}\r\n\t\t\t\t\tcase HUD_RADAR_HIGHLIGHT_FIXED_OUTLINE :\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tDraw_AlphaCircleOutlineRGB (x + player_p_x, y + player_p_y, player_size * 1.5, 1.0, higlight_color);\r\n\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t}\r\n\t\t\t\t\tcase HUD_RADAR_HIGHLIGHT_OUTLINE :\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tDraw_AlphaCircleOutlineRGB (x + player_p_x, y + player_p_y, player_size * player_z_relative * 2.0, 1.0, higlight_color);\r\n\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t}\r\n\t\t\t\t\tcase HUD_RADAR_HIGHLIGHT_TEXT_ONLY :\r\n\t\t\t\t\tdefault :\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\t// Draw the actual player and a line showing what direction the player is looking in.\r\n\t\t\t{\r\n\t\t\t\tfloat relative_x = 0;\r\n\t\t\t\tfloat relative_y = 0;\r\n\r\n\t\t\t\tx_line_start = x + player_p_x;\r\n\t\t\t\ty_line_start = y + player_p_y;\r\n\r\n\t\t\t\t// Translate the angle into radians.\r\n\t\t\t\tplayer_angle = DEG2RAD(player_angle);\r\n\r\n\t\t\t\trelative_x = cos(player_angle);\r\n\t\t\t\trelative_y = sin(player_angle);\r\n\r\n\t\t\t\t// Draw a slightly larger line behind the colored one\r\n\t\t\t\t// so that it get's an outline.\r\n\t\t\t\tx_line_end = x_line_start + (player_size * 2 * player_z_relative + 1) * relative_x;\r\n\t\t\t\ty_line_end = y_line_start - (player_size * 2 * player_z_relative + 1) * relative_y;\r\n\t\t\t\tDraw_AlphaLine (x_line_start, y_line_start, x_line_end, y_line_end, 4.0, 0, 1.0);\r\n\r\n\t\t\t\t// Draw the colored line.\r\n\t\t\t\tx_line_end = x_line_start + (player_size * 2 * player_z_relative) * relative_x;\r\n\t\t\t\ty_line_end = y_line_start - (player_size * 2 * player_z_relative) * relative_y;\r\n\t\t\t\tDraw_AlphaLine (x_line_start, y_line_start, x_line_end, y_line_end, 2.0, player_color, player_alpha);\r\n\r\n\t\t\t\t// Draw the player on the map.\r\n\t\t\t\tDraw_AlphaCircleFill (x + player_p_x, y + player_p_y, player_size * player_z_relative, player_color, player_alpha);\r\n\t\t\t\tDraw_AlphaCircleOutline (x + player_p_x, y + player_p_y, player_size * player_z_relative, 1.0, 0, 1.0);\r\n\t\t\t}\r\n\r\n\t\t\t// Draw the players name.\r\n\t\t\tif(show_names)\r\n\t\t\t{\r\n\t\t\t\tint name_x = 0;\r\n\t\t\t\tint name_y = 0;\r\n\r\n\t\t\t\tname_x = x + player_p_x;\r\n\t\t\t\tname_y = y + player_p_y;\r\n\r\n\t\t\t\t// Make sure we're not too far right.\r\n\t\t\t\twhile(name_x + 8 * strlen(info->name) > x + width)\r\n\t\t\t\t{\r\n\t\t\t\t\tname_x--;\r\n\t\t\t\t}\r\n\r\n\t\t\t\t// Make sure we're not outside the radar to the left.\r\n\t\t\t\tname_x = max(name_x, x);\r\n\r\n\t\t\t\t// Draw the name.\r\n\t\t\t\tif (highlight >= HUD_RADAR_HIGHLIGHT_TEXT_ONLY\r\n\t\t\t\t\t&& info->userid == cl.players[Cam_TrackNum()].userid)\r\n\t\t\t\t{\r\n\t\t\t\t\t// Draw the tracked players name in the user specified color.\r\n\t\t\t\t\tDraw_ColoredString (name_x, name_y,\r\n\t\t\t\t\t\tva(\"&c%x%x%x%s\",\r\n\t\t\t\t\t\t(unsigned int)(hud_radar_highlight_color[0] * 15),\r\n\t\t\t\t\t\t(unsigned int)(hud_radar_highlight_color[1] * 15),\r\n\t\t\t\t\t\t(unsigned int)(hud_radar_highlight_color[2] * 15), info->name), 0);\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\t// Draw other players in normal character color.\r\n\t\t\t\t\tDraw_String (name_x, name_y, info->name);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\t// Show if a person lost an RL-pack.\r\n\t\t\tif(info->stats[STAT_HEALTH] <= 0 && info->stats[STAT_ACTIVEWEAPON] == IT_ROCKET_LAUNCHER)\r\n\t\t\t{\r\n\t\t\t\tDraw_AlphaCircleOutline (x + player_p_x, y + player_p_y, player_size*player_z_relative*2, 1.0, 254, player_alpha);\r\n\t\t\t\tDraw_ColoredString (x + player_p_x, y + player_p_y, va(\"&cf00PACK!\"), 1);\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n}\r\n\r\n//\r\n// Draws a map of the current level and plots player movements on it.\r\n//\r\nvoid SCR_HUD_DrawRadar(hud_t *hud)\r\n{\r\n\tint width, height, x, y;\r\n\tfloat width_limit, height_limit;\r\n\tfloat scale;\r\n\tfloat x_scale;\r\n\tfloat y_scale;\r\n\r\n    static cvar_t\r\n\t\t*hud_radar_opacity = NULL,\r\n\t\t*hud_radar_width,\r\n\t\t*hud_radar_height,\r\n\t\t*hud_radar_autosize,\r\n\t\t*hud_radar_fade_players,\r\n\t\t*hud_radar_show_powerups,\r\n\t\t*hud_radar_show_names,\r\n\t\t*hud_radar_highlight,\r\n\t\t*hud_radar_highlight_color,\r\n\t\t*hud_radar_player_size,\r\n\t\t*hud_radar_show_height,\r\n\t\t*hud_radar_show_stats,\r\n\t\t*hud_radar_show_hold,\r\n\t\t*hud_radar_weaponfilter,\r\n\t\t*hud_radar_itemfilter,\r\n\t\t*hud_radar_onlytp,\r\n\t\t*hud_radar_otherfilter;\r\n\r\n    if (hud_radar_opacity == NULL)    // first time\r\n    {\r\n\t\tchar checkval[256];\r\n\r\n\t\thud_radar_opacity\t\t\t= HUD_FindVar(hud, \"opacity\");\r\n\t\thud_radar_width\t\t\t\t= HUD_FindVar(hud, \"width\");\r\n\t\thud_radar_height\t\t\t= HUD_FindVar(hud, \"height\");\r\n\t\thud_radar_autosize\t\t\t= HUD_FindVar(hud, \"autosize\");\r\n\t\thud_radar_fade_players\t\t= HUD_FindVar(hud, \"fade_players\");\r\n\t\thud_radar_show_powerups\t\t= HUD_FindVar(hud, \"show_powerups\");\r\n\t\thud_radar_show_names\t\t= HUD_FindVar(hud, \"show_names\");\r\n\t\thud_radar_player_size\t\t= HUD_FindVar(hud, \"player_size\");\r\n\t\thud_radar_show_height\t\t= HUD_FindVar(hud, \"show_height\");\r\n\t\thud_radar_show_stats\t\t= HUD_FindVar(hud, \"show_stats\");\r\n\t\thud_radar_show_hold\t\t\t= HUD_FindVar(hud, \"show_hold\");\r\n\t\thud_radar_weaponfilter\t\t= HUD_FindVar(hud, \"weaponfilter\");\r\n\t\thud_radar_itemfilter\t\t= HUD_FindVar(hud, \"itemfilter\");\r\n\t\thud_radar_otherfilter\t\t= HUD_FindVar(hud, \"otherfilter\");\r\n\t\thud_radar_onlytp\t\t\t= HUD_FindVar(hud, \"onlytp\");\r\n\t\thud_radar_highlight\t\t\t= HUD_FindVar(hud, \"highlight\");\r\n\t\thud_radar_highlight_color\t= HUD_FindVar(hud, \"highlight_color\");\r\n\r\n\t\t//\r\n\t\t// Only parse the the filters when they change, not on each frame.\r\n\t\t//\r\n\r\n\t\t// Weapon filter.\r\n\t\thud_radar_weaponfilter->OnChange = Radar_OnChangeWeaponFilter;\r\n\t\tstrlcpy(checkval, hud_radar_weaponfilter->string, sizeof(checkval));\r\n\t\tCvar_Set(hud_radar_weaponfilter, checkval);\r\n\r\n\t\t// Item filter.\r\n\t\thud_radar_itemfilter->OnChange = Radar_OnChangeItemFilter;\r\n\t\tstrlcpy(checkval, hud_radar_itemfilter->string, sizeof(checkval));\r\n\t\tCvar_Set(hud_radar_itemfilter, checkval);\r\n\r\n\t\t// Other filter.\r\n\t\thud_radar_otherfilter->OnChange = Radar_OnChangeOtherFilter;\r\n\t\tstrlcpy(checkval, hud_radar_otherfilter->string, sizeof(checkval));\r\n\t\tCvar_Set(hud_radar_otherfilter, checkval);\r\n\r\n\t\t// Highlight color.\r\n\t\thud_radar_highlight_color->OnChange = Radar_OnChangeHighlightColor;\r\n\t\tstrlcpy(checkval, hud_radar_highlight_color->string, sizeof(checkval));\r\n\t\tCvar_Set(hud_radar_highlight_color, checkval);\r\n    }\r\n\r\n\t// Don't show anything if it's a normal player.\r\n\tif(!(cls.demoplayback || cl.spectator))\r\n\t{\r\n\t\tHUD_PrepareDraw(hud, hud_radar_width->value, hud_radar_height->value, &x, &y);\r\n\t\treturn;\r\n\t}\r\n\r\n\t// Don't show when not in teamplay/demoplayback.\r\n\tif(!HUD_ShowInDemoplayback(hud_radar_onlytp->value))\r\n\t{\r\n\t\tHUD_PrepareDraw(hud, hud_radar_width->value, hud_radar_height->value, &x, &y);\r\n\t\treturn;\r\n\t}\r\n\r\n\t// Save the width and height of the HUD. We're using these because\r\n\t// if autosize is on these will be altered and we don't want to change\r\n\t// the settings that the user set, if we try, and the user turns off\r\n\t// autosize again the size of the HUD will remain \"autosized\" until the user\r\n\t// resets it by hand again.\r\n    width_limit = hud_radar_width->value;\r\n\theight_limit = hud_radar_height->value;\r\n\r\n    // we support also sizes specified as a percentage of total screen width/height\r\n    if (strchr(hud_radar_width->string, '%'))\r\n        width_limit = width_limit * vid.conwidth / 100.0;\r\n    if (strchr(hud_radar_height->string, '%'))\r\n\t    height_limit = hud_radar_height->value * vid.conheight / 100.0;\r\n\r\n\t// This map doesn't have a map pic.\r\n\tif(!radar_pic_found)\r\n\t{\r\n\t\tif(HUD_PrepareDraw(hud, Q_rint(width_limit), Q_rint(height_limit), &x, &y))\r\n\t\t{\r\n\t\t\tDraw_String(x, y, \"No radar picture found!\");\r\n\t\t}\r\n\t\treturn;\r\n\t}\r\n\r\n\t// Make sure we can translate the coordinates.\r\n\tif(!conversion_formula_found)\r\n\t{\r\n\t\tif(HUD_PrepareDraw(hud, Q_rint(width_limit), Q_rint(height_limit), &x, &y))\r\n\t\t{\r\n\t\t\tDraw_String(x, y, \"No conversion formula found!\");\r\n\t\t}\r\n\t\treturn;\r\n\t}\r\n\r\n\tx = 0;\r\n\ty = 0;\r\n\r\n\tscale = 1;\r\n\r\n\tif(hud_radar_autosize->value)\r\n\t{\r\n\t\t//\r\n\t\t// Autosize the hud element based on the size of the radar picture.\r\n\t\t//\r\n\r\n\t\twidth = width_limit = radar_pic.width;\r\n\t\theight = height_limit = radar_pic.height;\r\n\t}\r\n\telse\r\n\t{\r\n\t\t//\r\n\t\t// Size the picture so that it fits inside the hud element.\r\n\t\t//\r\n\r\n\t\t// Set the scaling based on the picture dimensions.\r\n\t\tx_scale = (width_limit / radar_pic.width);\r\n\t\ty_scale = (height_limit / radar_pic.height);\r\n\r\n\t\tscale = (x_scale < y_scale) ? x_scale : y_scale;\r\n\r\n\t\twidth = radar_pic.width * scale;\r\n\t\theight = radar_pic.height * scale;\r\n\t}\r\n\r\n\tif (HUD_PrepareDraw(hud, Q_rint(width_limit), Q_rint(height_limit), &x, &y))\r\n\t{\r\n\t\tfloat player_size = 1.0;\r\n\t\tstatic int lastframecount = -1;\r\n\r\n\t\t// Place the map picture in the center of the HUD element.\r\n\t\tx += Q_rint((width_limit / 2.0) - (width / 2.0));\r\n\t\tx = max(0, x);\r\n\t\tx = min(x + width, x);\r\n\r\n\t\ty += Q_rint((height_limit / 2.0) - (height / 2.0));\r\n\t\ty = max(0, y);\r\n\t\ty = min(y + height, y);\r\n\r\n\t\t// Draw the radar background.\r\n\t\tDraw_SAlphaPic (x, y, &radar_pic, hud_radar_opacity->value, scale);\r\n\r\n\t\t// Only draw once per frame.\r\n\t\tif (cls.framecount == lastframecount)\r\n\t\t{\r\n\t\t\treturn;\r\n\t\t}\r\n\t\tlastframecount = cls.framecount;\r\n\r\n\t\tif (!cl.oldparsecount || !cl.parsecount || cls.state < ca_active)\r\n\t\t{\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\t// Scale the player size after the size of the radar.\r\n\t\tplayer_size = hud_radar_player_size->value * scale;\r\n\r\n\t\t// Draw team stats.\r\n\t\tif(hud_radar_show_stats->value)\r\n\t\t{\r\n\t\t\tRadar_DrawGrid(stats_grid, x, y, scale, width, height, hud_radar_show_stats->value);\r\n\t\t}\r\n\r\n\t\t// Draw entities such as powerups, weapons and backpacks.\r\n\t\tif(RADAR_SHOW_WEAPONS || RADAR_SHOW_ITEMS || RADAR_SHOW_OTHER)\r\n\t\t{\r\n\t\t\tRadar_DrawEntities(x, y, scale,\r\n\t\t\t\tplayer_size,\r\n\t\t\t\thud_radar_show_hold->value);\r\n\t\t}\r\n\r\n\t\t// Draw the players.\r\n\t\tRadar_DrawPlayers(x, y, width, height, scale,\r\n\t\t\thud_radar_show_height->value,\r\n\t\t\thud_radar_show_powerups->value,\r\n\t\t\tplayer_size,\r\n\t\t\thud_radar_show_names->value,\r\n\t\t\thud_radar_fade_players->value,\r\n\t\t\thud_radar_highlight->value,\r\n\t\t\thud_radar_highlight_color->string);\r\n\t}\r\n}\r\n\r\n#endif // WITH_PNG\r\n\r\n//\r\n// Run before HUD elements are drawn.\r\n// Place stuff that is common for HUD elements here.\r\n//\r\nvoid HUD_BeforeDraw()\r\n{\r\n\t// Only sort once per draw.\r\n\tHUD_Sort_Scoreboard (HUD_SCOREBOARD_ALL);\r\n}\r\n\r\n//\r\n// Run after HUD elements are drawn.\r\n// Place stuff that is common for HUD elements here.\r\n//\r\nvoid HUD_AfterDraw()\r\n{\r\n}\r\n\r\n\r\n#ifndef HAXX\r\nstatic void SCR_HUD_DrawNotImplemented(hud_t *hud)\r\n{\r\n\tchar line1[64];\r\n\tint width, height, x, y;\r\n\r\n\tsnprintf(line1, sizeof(line1), \"%s not implemented\", hud->name);\r\n\r\n\twidth = 8 * strlen(line1);\r\n\theight = 8;\r\n\r\n\tif (!HUD_PrepareDraw(hud, width, height, &x, &y))\r\n\t\treturn;\r\n\r\n\tDraw_SString(x, y, line1, 1);\r\n}\r\n#define SCR_HUD_DrawTeamHoldBar SCR_HUD_DrawNotImplemented\r\n#define SCR_HUD_DrawTeamHoldInfo SCR_HUD_DrawNotImplemented\r\n#define SCR_HUD_DrawItemsClock SCR_HUD_DrawNotImplemented\r\n#endif\r\n\r\n\r\n// ----------------\r\n// Init\r\n// and add some common elements to hud (clock etc)\r\n//\r\n\r\nvoid CommonDraw_Init(void)\r\n{\r\n    int i;\r\n\r\n\tHUD_InitSbarImages();\r\n\r\n\t// variables\r\n\thud_planmode\t\t= cvarfuncs->GetNVFDG(\"hud_planmode\", \"0\", 0, NULL, \"ezhud\");\r\n\thud_tp_need\t\t\t= cvarfuncs->GetNVFDG(\"hud_tp_need\", \"0\", 0, NULL, \"ezhud\");\r\n\thud_digits_trim\t\t= cvarfuncs->GetNVFDG(\"hud_digits_trim\", \"1\", 0, NULL, \"ezhud\");\r\n\tmvd_autohud\t\t\t= cvarfuncs->GetNVFDG(\"mvd_autohud\", \"0\", 0, NULL, \"ezhud\");\r\n\tcl_weaponpreselect\t= cvarfuncs->GetNVFDG(\"cl_weaponpreselect\", \"0\", 0, NULL, \"ezhud\");\r\n\tcl_multiview\t\t= cvarfuncs->GetNVFDG(\"cl_multiview\", \"0\", 0, NULL, \"ezhud\");\r\n\r\n\r\n\ttp_need_health\t\t= cvarfuncs->GetNVFDG(\"tp_need_health\",\t\"50\",\t\t0, NULL, \"ezhud\");\r\n\ttp_need_ra\t\t\t= cvarfuncs->GetNVFDG(\"tp_need_ra\",\t\t\"50\",\t\t0, NULL, \"ezhud\");\r\n\ttp_need_ya\t\t\t= cvarfuncs->GetNVFDG(\"tp_need_ya\",\t\t\"50\",\t\t0, NULL, \"ezhud\");\r\n\ttp_need_ga\t\t\t= cvarfuncs->GetNVFDG(\"tp_need_ga\",\t\t\"50\",\t\t0, NULL, \"ezhud\");\r\n\ttp_weapon_order\t\t= cvarfuncs->GetNVFDG(\"tp_weapon_order\",\t\"78654321\",\t0, NULL, \"ezhud\");\r\n\ttp_need_weapon\t\t= cvarfuncs->GetNVFDG(\"tp_need_weapon\",\t\"35687\",\t0, NULL, \"ezhud\");\r\n\ttp_need_shells\t\t= cvarfuncs->GetNVFDG(\"tp_need_shells\",\t\"10\",\t\t0, NULL, \"ezhud\");\r\n\ttp_need_nails\t\t= cvarfuncs->GetNVFDG(\"tp_need_nails\",\t\"40\",\t\t0, NULL, \"ezhud\");\r\n\ttp_need_rockets\t\t= cvarfuncs->GetNVFDG(\"tp_need_rockets\",\t\"5\",\t\t0, NULL, \"ezhud\");\r\n\ttp_need_cells\t\t= cvarfuncs->GetNVFDG(\"tp_need_cells\",\t\"20\",\t\t0, NULL, \"ezhud\");\r\n\r\n    // init HUD STAT table\r\n    for (i=0; i < MAX_CL_STATS; i++)\r\n        hud_stats[i] = 0;\r\n    hud_stats[STAT_HEALTH]  = 200;\r\n    hud_stats[STAT_AMMO]    = 100;\r\n    hud_stats[STAT_ARMOR]   = 200;\r\n    hud_stats[STAT_SHELLS]  = 100;\r\n    hud_stats[STAT_NAILS]   = 200;\r\n    hud_stats[STAT_ROCKETS] = 100;\r\n    hud_stats[STAT_CELLS]   = 100;\r\n    hud_stats[STAT_ACTIVEWEAPON] = 32;\r\n    hud_stats[STAT_ITEMS] = 0xffffffff - IT_ARMOR2 - IT_ARMOR1;\r\n\r\n\tautohud.active = 0;\r\n\r\n    // init gameclock\r\n\tHUD_Register(\"gameclock\", NULL, \"Shows current game time (hh:mm:ss).\",\r\n        HUD_PLUSMINUS, ca_disconnected, 8, SCR_HUD_DrawGameClock,\r\n        \"1\", \"top\", \"right\", \"console\", \"0\", \"0\", \"0\", \"0 0 0\", NULL,\r\n        \"big\",      \"1\",\r\n        \"style\",    \"0\",\r\n\t\t\"scale\",    \"1\",\r\n        \"blink\",    \"1\",\r\n\t\t\"countdown\",\"0\",\r\n\t\t\"offset\",\"0\",\r\n        NULL);\r\n\r\n\tHUD_Register(\"notify\", NULL, \"Shows last console lines\",\r\n\t\tHUD_PLUSMINUS, ca_disconnected, 8, SCR_HUD_DrawNotify,\r\n\t\t\"0\", \"top\", \"left\", \"top\", \"0\", \"0\", \"0\", \"0 0 0\", NULL,\r\n\t\t\"rows\", \"4\",\r\n\t\t\"cols\", \"30\",\r\n\t\t\"scale\", \"1\",\r\n\t\t\"time\", \"4\",\r\n\t\tNULL);\r\n\r\n\t// fps\r\n\tHUD_Register(\"fps\", NULL,\r\n        \"Shows your current framerate in frames per second (fps).\"\r\n        \"This can also show the minimum framerate that occured in the last measured period.\",\r\n        HUD_PLUSMINUS, ca_active, 9, SCR_HUD_DrawFPS,\r\n        \"1\", \"gameclock\", \"center\", \"after\", \"0\", \"0\", \"0\", \"0 0 0\", NULL,\r\n        \"show_min\", \"0\",\r\n\t\t\"style\",\t\"0\",\r\n        \"title\",    \"1\",\r\n\t\t\"drop\", \"70\",\r\n        NULL);\r\n\r\n\tHUD_Register(\"vidlag\", NULL,\r\n        \"Shows the delay between the time a frame is rendered and the time it's displayed.\",\r\n        HUD_PLUSMINUS, ca_active, 9, SCR_HUD_DrawVidLag,\r\n        \"0\", \"top\", \"right\", \"top\", \"0\", \"0\", \"0\", \"0 0 0\", NULL,\r\n\t\t\"style\",\t\"0\",\r\n        NULL);\r\n\r\n\tHUD_Register(\"mouserate\", NULL, \"Show your current mouse input rate\", HUD_PLUSMINUS, ca_active, 9,\r\n\t\tSCR_HUD_DrawMouserate,\r\n\t\t\"0\", \"screen\", \"left\", \"bottom\", \"0\", \"0\", \"0\", \"0 0 0\", NULL,\r\n\t\t\"title\", \"1\",\r\n\t\t\"interval\", \"1\",\r\n\t\t\"style\",\t\"0\",\r\n\t\tNULL);\r\n\r\n    // init clock\r\n\tHUD_Register(\"clock\", NULL, \"Shows current local time (hh:mm:ss).\",\r\n        HUD_PLUSMINUS, ca_disconnected, 8, SCR_HUD_DrawClock,\r\n        \"0\", \"top\", \"right\", \"console\", \"0\", \"0\", \"0\", \"0 0 0\", NULL,\r\n        \"big\",      \"1\",\r\n        \"style\",    \"0\",\r\n\t\t\"scale\",    \"1\",\r\n        \"blink\",    \"1\",\r\n\t\t\"format\",   \"0\",\r\n        NULL);\r\n\r\n    // init democlock\r\n\tHUD_Register(\"democlock\", NULL, \"Shows current demo time (hh:mm:ss).\",\r\n        HUD_PLUSMINUS, ca_disconnected, 7, SCR_HUD_DrawDemoClock,\r\n        \"1\", \"top\", \"right\", \"console\", \"0\", \"8\", \"0\", \"0 0 0\", NULL,\r\n        \"big\",      \"0\",\r\n        \"style\",    \"0\",\r\n\t\t\"scale\",    \"1\",\r\n        \"blink\",    \"0\",\r\n        NULL);\r\n\r\n    // init ping\r\n    HUD_Register(\"ping\", NULL, \"Shows most important net conditions, like ping and pl. Shown only when you are connected to a server.\",\r\n        HUD_PLUSMINUS, ca_active, 9, SCR_HUD_DrawPing,\r\n        \"0\", \"screen\", \"left\", \"bottom\", \"0\", \"0\", \"0\", \"0 0 0\", NULL,\r\n        \"period\",       \"1\",\r\n        \"show_pl\",      \"1\",\r\n        \"show_min\",     \"0\",\r\n        \"show_max\",     \"0\",\r\n        \"show_dev\",     \"0\",\r\n\t\t\"style\",\t\t\"0\",\r\n        \"blink\",        \"1\",\r\n        NULL);\r\n\r\n\t// init net\r\n    HUD_Register(\"net\", NULL, \"Shows network statistics, like latency, packet loss, average packet sizes and bandwidth. Shown only when you are connected to a server.\",\r\n        HUD_PLUSMINUS, ca_active, 7, SCR_HUD_DrawNetStats,\r\n        \"0\", \"top\", \"left\", \"center\", \"0\", \"0\", \"0.2\", \"0 0 0\", NULL,\r\n        \"period\",  \"1\",\r\n        NULL);\r\n\r\n    // init speed\r\n    HUD_Register(\"speed\", NULL, \"Shows your current running speed. It is measured over XY or XYZ axis depending on \\'xyz\\' property.\",\r\n        HUD_PLUSMINUS, ca_active, 7, SCR_HUD_DrawSpeed,\r\n        \"0\", \"top\", \"center\", \"bottom\", \"0\", \"-5\", \"0\", \"0 0 0\", NULL,\r\n        \"xyz\",  \"0\",\r\n\t\t\"width\", \"160\",\r\n\t\t\"height\", \"15\",\r\n\t\t\"opacity\", \"1.0\",\r\n\t\t\"tick_spacing\", \"0.2\",\r\n\t\t\"color_stopped\", SPEED_STOPPED,\r\n\t\t\"color_normal\", SPEED_NORMAL,\r\n\t\t\"color_fast\", SPEED_FAST,\r\n\t\t\"color_fastest\", SPEED_FASTEST,\r\n\t\t\"color_insane\", SPEED_INSANE,\r\n\t\t\"vertical\", \"0\",\r\n\t\t\"vertical_text\", \"1\",\r\n\t\t\"text_align\", \"1\",\r\n\t\t\"style\", \"0\",\r\n\t\t\"scale\", \"1\",\r\n\t\tNULL);\r\n\r\n    // Init speed2 (half circle thingie).\r\n    HUD_Register(\"speed2\", NULL, \"Shows your current running speed. It is measured over XY or XYZ axis depending on \\'xyz\\' property.\",\r\n        HUD_PLUSMINUS, ca_active, 7, SCR_HUD_DrawSpeed2,\r\n        \"0\", \"top\", \"center\", \"bottom\", \"0\", \"0\", \"0\", \"0 0 0\", NULL,\r\n        \"xyz\",  \"0\",\r\n\t\t\"opacity\", \"1.0\",\r\n\t\t\"color_stopped\", SPEED_STOPPED,\r\n\t\t\"color_normal\", SPEED_NORMAL,\r\n\t\t\"color_fast\", SPEED_FAST,\r\n\t\t\"color_fastest\", SPEED_FASTEST,\r\n\t\t\"color_insane\", SPEED_INSANE,\r\n\t\t\"radius\", \"50.0\",\r\n\t\t\"wrapspeed\", \"500\",\r\n\t\t\"orientation\", \"0\",\r\n\t\tNULL);\r\n\r\n    // init guns\r\n    HUD_Register(\"gun\", NULL, \"Part of your inventory - current weapon.\",\r\n        HUD_INVENTORY, ca_active, 0, SCR_HUD_DrawGunCurrent,\r\n        \"0\", \"ibar\", \"center\", \"bottom\", \"0\", \"0\", \"0\", \"0 0 0\", NULL,\r\n        \"wide\",  \"0\",\r\n        \"style\", \"0\",\r\n        \"scale\", \"1\",\r\n        NULL);\r\n    HUD_Register(\"gun2\", NULL, \"Part of your inventory - shotgun.\",\r\n        HUD_INVENTORY, ca_active, 0, SCR_HUD_DrawGun2,\r\n        \"1\", \"ibar\", \"left\", \"bottom\", \"0\", \"0\", \"0\", \"0 0 0\", NULL,\r\n        \"style\", \"0\",\r\n        \"scale\", \"1\",\r\n        NULL);\r\n    HUD_Register(\"gun3\", NULL, \"Part of your inventory - super shotgun.\",\r\n        HUD_INVENTORY, ca_active, 0, SCR_HUD_DrawGun3,\r\n        \"1\", \"gun2\", \"after\", \"center\", \"0\", \"0\", \"0\", \"0 0 0\", NULL,\r\n        \"style\", \"0\",\r\n        \"scale\", \"1\",\r\n        NULL);\r\n    HUD_Register(\"gun4\", NULL, \"Part of your inventory - nailgun.\",\r\n        HUD_INVENTORY, ca_active, 0, SCR_HUD_DrawGun4,\r\n        \"1\", \"gun3\", \"after\", \"center\", \"0\", \"0\", \"0\", \"0 0 0\", NULL,\r\n        \"style\", \"0\",\r\n        \"scale\", \"1\",\r\n        NULL);\r\n    HUD_Register(\"gun5\", NULL, \"Part of your inventory - super nailgun.\",\r\n        HUD_INVENTORY, ca_active, 0, SCR_HUD_DrawGun5,\r\n        \"1\", \"gun4\", \"after\", \"center\", \"0\", \"0\", \"0\", \"0 0 0\", NULL,\r\n        \"style\", \"0\",\r\n        \"scale\", \"1\",\r\n        NULL);\r\n    HUD_Register(\"gun6\", NULL, \"Part of your inventory - grenade launcher.\",\r\n        HUD_INVENTORY, ca_active, 0, SCR_HUD_DrawGun6,\r\n        \"1\", \"gun5\", \"after\", \"center\", \"0\", \"0\", \"0\", \"0 0 0\", NULL,\r\n        \"style\", \"0\",\r\n        \"scale\", \"1\",\r\n        NULL);\r\n    HUD_Register(\"gun7\", NULL, \"Part of your inventory - rocket launcher.\",\r\n        HUD_INVENTORY, ca_active, 0, SCR_HUD_DrawGun7,\r\n        \"1\", \"gun6\", \"after\", \"center\", \"0\", \"0\", \"0\", \"0 0 0\", NULL,\r\n        \"style\", \"0\",\r\n        \"scale\", \"1\",\r\n        NULL);\r\n    HUD_Register(\"gun8\", NULL, \"Part of your inventory - thunderbolt.\",\r\n        HUD_INVENTORY, ca_active, 0, SCR_HUD_DrawGun8,\r\n        \"1\", \"gun7\", \"after\", \"center\", \"0\", \"0\", \"0\", \"0 0 0\", NULL,\r\n        \"wide\",  \"0\",\r\n        \"style\", \"0\",\r\n        \"scale\", \"1\",\r\n        NULL);\r\n\r\n    // init powerzz\r\n    HUD_Register(\"key1\", NULL, \"Part of your inventory - silver key.\",\r\n        HUD_INVENTORY, ca_active, 0, SCR_HUD_DrawKey1,\r\n        \"1\", \"ibar\", \"top\", \"left\", \"0\", \"64\", \"0\", \"0 0 0\", NULL,\r\n        \"style\", \"0\",\r\n        \"scale\", \"1\",\r\n        NULL);\r\n    HUD_Register(\"key2\", NULL, \"Part of your inventory - gold key.\",\r\n        HUD_INVENTORY, ca_active, 0, SCR_HUD_DrawKey2,\r\n        \"1\", \"key1\", \"left\", \"after\", \"0\", \"0\", \"0\", \"0 0 0\", NULL,\r\n        \"style\", \"0\",\r\n        \"scale\", \"1\",\r\n        NULL);\r\n    HUD_Register(\"ring\", NULL, \"Part of your inventory - invisibility.\",\r\n        HUD_INVENTORY, ca_active, 0, SCR_HUD_DrawRing,\r\n        \"1\", \"key2\", \"left\", \"after\", \"0\", \"0\", \"0\", \"0 0 0\", NULL,\r\n        \"style\", \"0\",\r\n        \"scale\", \"1\",\r\n        NULL);\r\n    HUD_Register(\"pent\", NULL, \"Part of your inventory - invulnerability.\",\r\n        HUD_INVENTORY, ca_active, 0, SCR_HUD_DrawPent,\r\n        \"1\", \"ring\", \"left\", \"after\", \"0\", \"0\", \"0\", \"0 0 0\", NULL,\r\n        \"style\", \"0\",\r\n        \"scale\", \"1\",\r\n        NULL);\r\n    HUD_Register(\"suit\", NULL, \"Part of your inventory - biosuit.\",\r\n        HUD_INVENTORY, ca_active, 0, SCR_HUD_DrawSuit,\r\n        \"1\", \"pent\", \"left\", \"after\", \"0\", \"0\", \"0\", \"0 0 0\", NULL,\r\n        \"style\", \"0\",\r\n        \"scale\", \"1\",\r\n        NULL);\r\n    HUD_Register(\"quad\", NULL, \"Part of your inventory - quad damage.\",\r\n        HUD_INVENTORY, ca_active, 0, SCR_HUD_DrawQuad,\r\n        \"1\", \"suit\", \"left\", \"after\", \"0\", \"0\", \"0\", \"0 0 0\", NULL,\r\n        \"style\", \"0\",\r\n        \"scale\", \"1\",\r\n        NULL);\r\n\r\n\t// netproblem icon\r\n    HUD_Register(\"netproblem\", NULL, \"Shows an icon if you are experiencing network problems\",\r\n        HUD_NO_FRAME, ca_active, 0, SCR_HUD_NetProblem,\r\n        \"1\", \"top\", \"left\", \"top\", \"0\", \"0\", \"0\", \"0 0 0\", NULL,\r\n        \"scale\", \"1\",\r\n        NULL);\r\n\r\n    // sigilzz\r\n    HUD_Register(\"sigil1\", NULL, \"Part of your inventory - sigil 1.\",\r\n        HUD_INVENTORY, ca_active, 0, SCR_HUD_DrawSigil1,\r\n        \"0\", \"ibar\", \"left\", \"top\", \"0\", \"0\", \"0\", \"0 0 0\", NULL,\r\n        \"style\", \"0\",\r\n        \"scale\", \"1\",\r\n        NULL);\r\n    HUD_Register(\"sigil2\", NULL, \"Part of your inventory - sigil 2.\",\r\n        HUD_INVENTORY, ca_active, 0, SCR_HUD_DrawSigil2,\r\n        \"0\", \"sigil1\", \"after\", \"top\", \"0\", \"0\", \"0\", \"0 0 0\", NULL,\r\n        \"style\", \"0\",\r\n        \"scale\", \"1\",\r\n        NULL);\r\n    HUD_Register(\"sigil3\", NULL, \"Part of your inventory - sigil 3.\",\r\n        HUD_INVENTORY, ca_active, 0, SCR_HUD_DrawSigil3,\r\n        \"0\", \"sigil2\", \"after\", \"top\", \"0\", \"0\", \"0\", \"0 0 0\", NULL,\r\n        \"style\", \"0\",\r\n        \"scale\", \"1\",\r\n        NULL);\r\n    HUD_Register(\"sigil4\", NULL, \"Part of your inventory - sigil 4.\",\r\n        HUD_INVENTORY, ca_active, 0, SCR_HUD_DrawSigil4,\r\n        \"0\", \"sigil3\", \"after\", \"top\", \"0\", \"0\", \"0\", \"0 0 0\", NULL,\r\n        \"style\", \"0\",\r\n        \"scale\", \"1\",\r\n        NULL);\r\n\r\n\t// player face (health indicator)\r\n    HUD_Register(\"face\", NULL, \"Your bloody face.\",\r\n        HUD_INVENTORY, ca_active, 0, SCR_HUD_DrawFace,\r\n        \"1\", \"screen\", \"center\", \"bottom\", \"0\", \"0\", \"0\", \"0 0 0\", NULL,\r\n        \"scale\", \"1\",\r\n        NULL);\r\n\r\n\t// health\r\n    HUD_Register(\"health\", NULL, \"Part of your status - health level.\",\r\n        HUD_INVENTORY, ca_active, 0, SCR_HUD_DrawHealth,\r\n        \"1\", \"face\", \"after\", \"center\", \"0\", \"0\", \"0\", \"0 0 0\", NULL,\r\n        \"style\",  \"0\",\r\n        \"scale\",  \"1\",\r\n        \"align\",  \"right\",\r\n        \"digits\", \"3\",\r\n        NULL);\r\n\r\n    // ammo/s\r\n    HUD_Register(\"ammo\", NULL, \"Part of your inventory - ammo for active weapon.\",\r\n        HUD_INVENTORY, ca_active, 0, SCR_HUD_DrawAmmoCurrent,\r\n        \"1\", \"health\", \"after\", \"center\", \"32\", \"0\", \"0\", \"0 0 0\", NULL,\r\n        \"style\", \"0\",\r\n        \"scale\", \"1\",\r\n        \"align\", \"right\",\r\n        \"digits\", \"3\",\r\n        NULL);\r\n    HUD_Register(\"ammo1\", NULL, \"Part of your inventory - ammo - shells.\",\r\n        HUD_INVENTORY, ca_active, 0, SCR_HUD_DrawAmmo1,\r\n        \"0\", \"ibar\", \"left\", \"top\", \"0\", \"0\", \"0\", \"0 0 0\", NULL,\r\n        \"style\", \"0\",\r\n        \"scale\", \"1\",\r\n        \"align\", \"right\",\r\n        \"digits\", \"3\",\r\n        NULL);\r\n    HUD_Register(\"ammo2\", NULL, \"Part of your inventory - ammo - nails.\",\r\n        HUD_INVENTORY, ca_active, 0, SCR_HUD_DrawAmmo2,\r\n        \"0\", \"ammo1\", \"after\", \"top\", \"0\", \"0\", \"0\", \"0 0 0\", NULL,\r\n        \"style\", \"0\",\r\n        \"scale\", \"1\",\r\n        \"align\", \"right\",\r\n        \"digits\", \"3\",\r\n        NULL);\r\n    HUD_Register(\"ammo3\", NULL, \"Part of your inventory - ammo - rockets.\",\r\n        HUD_INVENTORY, ca_active, 0, SCR_HUD_DrawAmmo3,\r\n        \"0\", \"ammo2\", \"after\", \"top\", \"0\", \"0\", \"0\", \"0 0 0\", NULL,\r\n        \"style\", \"0\",\r\n        \"scale\", \"1\",\r\n        \"align\", \"right\",\r\n        \"digits\", \"3\",\r\n        NULL);\r\n    HUD_Register(\"ammo4\", NULL, \"Part of your inventory - ammo - cells.\",\r\n        HUD_INVENTORY, ca_active, 0, SCR_HUD_DrawAmmo4,\r\n        \"0\", \"ammo3\", \"after\", \"top\", \"0\", \"0\", \"0\", \"0 0 0\", NULL,\r\n        \"style\", \"0\",\r\n        \"scale\", \"1\",\r\n        \"align\", \"right\",\r\n        \"digits\", \"3\",\r\n        NULL);\r\n\r\n    // ammo icon/s\r\n    HUD_Register(\"iammo\", NULL, \"Part of your inventory - ammo icon.\",\r\n        HUD_INVENTORY, ca_active, 0, SCR_HUD_DrawAmmoIconCurrent,\r\n        \"1\", \"ammo\", \"before\", \"center\", \"0\", \"0\", \"0\", \"0 0 0\", NULL,\r\n        \"style\", \"0\",\r\n        \"scale\", \"1\",\r\n        NULL);\r\n    HUD_Register(\"iammo1\", NULL, \"Part of your inventory - ammo icon.\",\r\n        HUD_INVENTORY, ca_active, 0, SCR_HUD_DrawAmmoIcon1,\r\n        \"0\", \"ibar\", \"left\", \"top\", \"0\", \"0\", \"0\", \"0 0 0\", NULL,\r\n        \"style\", \"2\",\r\n        \"scale\", \"1\",\r\n        NULL);\r\n    HUD_Register(\"iammo2\", NULL, \"Part of your inventory - ammo icon.\",\r\n        HUD_INVENTORY, ca_active, 0, SCR_HUD_DrawAmmoIcon2,\r\n        \"0\", \"iammo1\", \"after\", \"top\", \"0\", \"0\", \"0\", \"0 0 0\", NULL,\r\n        \"style\", \"2\",\r\n        \"scale\", \"1\",\r\n        NULL);\r\n    HUD_Register(\"iammo3\", NULL, \"Part of your inventory - ammo icon.\",\r\n        HUD_INVENTORY, ca_active, 0, SCR_HUD_DrawAmmoIcon3,\r\n        \"0\", \"iammo2\", \"after\", \"top\", \"0\", \"0\", \"0\", \"0 0 0\", NULL,\r\n        \"style\", \"2\",\r\n        \"scale\", \"1\",\r\n        NULL);\r\n    HUD_Register(\"iammo4\", NULL, \"Part of your inventory - ammo icon.\",\r\n        HUD_INVENTORY, ca_active, 0, SCR_HUD_DrawAmmoIcon4,\r\n        \"0\", \"iammo3\", \"after\", \"top\", \"0\", \"0\", \"0\", \"0 0 0\", NULL,\r\n        \"style\", \"2\",\r\n        \"scale\", \"1\",\r\n        NULL);\r\n\r\n    // armor count\r\n    HUD_Register(\"armor\", NULL, \"Part of your inventory - armor level.\",\r\n        HUD_INVENTORY, ca_active, 0, SCR_HUD_DrawArmor,\r\n        \"1\", \"face\", \"before\", \"center\", \"-32\", \"0\", \"0\", \"0 0 0\", NULL,\r\n        \"style\",  \"0\",\r\n        \"scale\",  \"1\",\r\n        \"align\",  \"right\",\r\n        \"digits\", \"3\",\r\n\t\t\"pent_666\", \"1\",  // Show 666 instead of armor value\r\n        NULL);\r\n\r\n\t// armor icon\r\n    HUD_Register(\"iarmor\", NULL, \"Part of your inventory - armor icon.\",\r\n        HUD_INVENTORY, ca_active, 0, SCR_HUD_DrawArmorIcon,\r\n        \"1\", \"armor\", \"before\", \"center\", \"0\", \"0\", \"0\", \"0 0 0\", NULL,\r\n        \"style\", \"0\",\r\n        \"scale\", \"1\",\r\n        NULL);\r\n\r\n\t// Tracking JohnNy_cz (Contains name of the player who's player we're watching at the moment)\r\n\tHUD_Register(\"tracking\", NULL, \"Shows the name of tracked player.\",\r\n\t\tHUD_PLUSMINUS, ca_active, 9, SCR_HUD_DrawTracking,\r\n\t\t\"1\", \"face\", \"center\", \"before\", \"0\", \"0\", \"0\", \"0 0 0\", NULL,\r\n\t\t\"format\", \"^mTracking:^m %t %n\"/*, ^mJUMP^m for next\"*/, //\"Tracking: team name, JUMP for next\", \"Tracking:\" and \"JUMP\" are brown. default: \"Tracking %t %n, [JUMP] for next\"\r\n\t\t\"scale\", \"1\",\r\n\t\tNULL);\r\n\r\n    // groups\r\n    HUD_Register(\"group1\", NULL, \"Group element.\",\r\n        HUD_NO_GROW, ca_disconnected, 0, SCR_HUD_Group1,\r\n        \"0\", \"screen\", \"left\", \"top\", \"0\", \"0\", \".5\", \"0 0 0\", NULL,\r\n        \"name\", \"group1\",\r\n        \"width\", \"64\",\r\n        \"height\", \"64\",\r\n        \"picture\", \"\",\r\n        \"pic_alpha\", \"1.0\",\r\n        \"pic_scalemode\", \"0\",\r\n        NULL);\r\n    HUD_Register(\"group2\", NULL, \"Group element.\",\r\n        HUD_NO_GROW, ca_disconnected, 0, SCR_HUD_Group2,\r\n        \"0\", \"screen\", \"center\", \"top\", \"0\", \"0\", \".5\", \"0 0 0\", NULL,\r\n        \"name\", \"group2\",\r\n        \"width\", \"64\",\r\n        \"height\", \"64\",\r\n        \"picture\", \"\",\r\n        \"pic_alpha\", \"1.0\",\r\n        \"pic_scalemode\", \"0\",\r\n        NULL);\r\n    HUD_Register(\"group3\", NULL, \"Group element.\",\r\n        HUD_NO_GROW, ca_disconnected, 0, SCR_HUD_Group3,\r\n        \"0\", \"screen\", \"right\", \"top\", \"0\", \"0\", \".5\", \"0 0 0\", NULL,\r\n        \"name\", \"group3\",\r\n        \"width\", \"64\",\r\n        \"height\", \"64\",\r\n        \"picture\", \"\",\r\n        \"pic_alpha\", \"1.0\",\r\n        \"pic_scalemode\", \"0\",\r\n        NULL);\r\n    HUD_Register(\"group4\", NULL, \"Group element.\",\r\n        HUD_NO_GROW, ca_disconnected, 0, SCR_HUD_Group4,\r\n        \"0\", \"screen\", \"left\", \"center\", \"0\", \"0\", \".5\", \"0 0 0\", NULL,\r\n        \"name\", \"group4\",\r\n        \"width\", \"64\",\r\n        \"height\", \"64\",\r\n        \"picture\", \"\",\r\n        \"pic_alpha\", \"1.0\",\r\n        \"pic_scalemode\", \"0\",\r\n        NULL);\r\n    HUD_Register(\"group5\", NULL, \"Group element.\",\r\n        HUD_NO_GROW, ca_disconnected, 0, SCR_HUD_Group5,\r\n        \"0\", \"screen\", \"center\", \"center\", \"0\", \"0\", \".5\", \"0 0 0\", NULL,\r\n        \"name\", \"group5\",\r\n        \"width\", \"64\",\r\n        \"height\", \"64\",\r\n        \"picture\", \"\",\r\n\t\t\"pic_alpha\", \"1.0\",\r\n        \"pic_scalemode\", \"0\",\r\n        NULL);\r\n    HUD_Register(\"group6\", NULL, \"Group element.\",\r\n        HUD_NO_GROW, ca_disconnected, 0, SCR_HUD_Group6,\r\n        \"0\", \"screen\", \"right\", \"center\", \"0\", \"0\", \".5\", \"0 0 0\", NULL,\r\n        \"name\", \"group6\",\r\n        \"width\", \"64\",\r\n        \"height\", \"64\",\r\n        \"picture\", \"\",\r\n\t\t\"pic_alpha\", \"1.0\",\r\n        \"pic_scalemode\", \"0\",\r\n        NULL);\r\n    HUD_Register(\"group7\", NULL, \"Group element.\",\r\n        HUD_NO_GROW, ca_disconnected, 0, SCR_HUD_Group7,\r\n        \"0\", \"screen\", \"left\", \"bottom\", \"0\", \"0\", \".5\", \"0 0 0\", NULL,\r\n        \"name\", \"group7\",\r\n        \"width\", \"64\",\r\n        \"height\", \"64\",\r\n        \"picture\", \"\",\r\n\t\t\"pic_alpha\", \"1.0\",\r\n        \"pic_scalemode\", \"0\",\r\n        NULL);\r\n    HUD_Register(\"group8\", NULL, \"Group element.\",\r\n        HUD_NO_GROW, ca_disconnected, 0, SCR_HUD_Group8,\r\n        \"0\", \"screen\", \"center\", \"bottom\", \"0\", \"0\", \".5\", \"0 0 0\", NULL,\r\n        \"name\", \"group8\",\r\n        \"width\", \"64\",\r\n        \"height\", \"64\",\r\n        \"picture\", \"\",\r\n\t\t\"pic_alpha\", \"1.0\",\r\n        \"pic_scalemode\", \"0\",\r\n        NULL);\r\n    HUD_Register(\"group9\", NULL, \"Group element.\",\r\n        HUD_NO_GROW, ca_disconnected, 0, SCR_HUD_Group9,\r\n        \"0\", \"screen\", \"right\", \"bottom\", \"0\", \"0\", \".5\", \"0 0 0\", NULL,\r\n        \"name\", \"group9\",\r\n        \"width\", \"64\",\r\n        \"height\", \"64\",\r\n        \"picture\", \"\",\r\n\t\t\"pic_alpha\", \"1.0\",\r\n        \"pic_scalemode\", \"0\",\r\n        NULL);\r\n\r\n    // healthdamage\r\n    HUD_Register(\"healthdamage\", NULL, \"Shows amount of damage done to your health.\",\r\n        HUD_INVENTORY, ca_active, 0, SCR_HUD_DrawHealthDamage,\r\n        \"0\", \"health\", \"left\", \"before\", \"0\", \"0\", \"0\", \"0 0 0\", NULL,\r\n        \"style\",  \"0\",\r\n        \"scale\",  \"1\",\r\n        \"align\",  \"right\",\r\n        \"digits\", \"3\",\r\n\t\t\"duration\", \"0.8\",\r\n        NULL);\r\n\r\n    // armordamage\r\n    HUD_Register(\"armordamage\", NULL, \"Shows amount of damage done to your armour.\",\r\n        HUD_INVENTORY, ca_active, 0, SCR_HUD_DrawArmorDamage,\r\n        \"0\", \"armor\", \"left\", \"before\", \"0\", \"0\", \"0\", \"0 0 0\", NULL,\r\n        \"style\",  \"0\",\r\n        \"scale\",  \"1\",\r\n        \"align\",  \"right\",\r\n        \"digits\", \"3\",\r\n\t\t\"duration\", \"0.8\",\r\n        NULL);\r\n\r\n    HUD_Register(\"frags\", NULL, \"Show list of player frags in short form.\",\r\n        0, ca_active, 0, SCR_HUD_DrawFrags,\r\n        \"0\", \"top\", \"right\", \"bottom\", \"0\", \"0\", \"0\", \"0 0 0\", NULL,\r\n        \"cell_width\", \"32\",\r\n        \"cell_height\", \"8\",\r\n        \"rows\", \"1\",\r\n        \"cols\", \"4\",\r\n        \"space_x\", \"1\",\r\n        \"space_y\", \"1\",\r\n        \"teamsort\", \"0\",\r\n        \"strip\", \"1\",\r\n        \"vertical\", \"0\",\r\n\t\t\"shownames\", \"0\",\r\n\t\t\"showteams\", \"0\",\r\n\t\t\"padtext\", \"1\",\r\n\t\t\"showself_always\", \"1\",\r\n\t\t\"extra_spec_info\", \"ALL\",\r\n\t\t\"fliptext\", \"0\",\r\n\t\t\"style\", \"0\",\r\n\t\t\"bignum\", \"0\",\r\n\t\t\"colors_alpha\", \"1.0\",\r\n\t\t\"maxname\", \"16\",\r\n\t\t\"notintp\", \"0\",\r\n        NULL);\r\n\r\n    HUD_Register(\"teamfrags\", NULL, \"Show list of team frags in short form.\",\r\n        0, ca_active, 0, SCR_HUD_DrawTeamFrags,\r\n        \"1\", \"ibar\", \"center\", \"before\", \"0\", \"0\", \"0\", \"0 0 0\", NULL,\r\n        \"cell_width\", \"32\",\r\n        \"cell_height\", \"8\",\r\n        \"rows\", \"1\",\r\n        \"cols\", \"2\",\r\n        \"space_x\", \"1\",\r\n        \"space_y\", \"1\",\r\n        \"strip\", \"1\",\r\n        \"vertical\", \"0\",\r\n\t\t\"shownames\", \"0\",\r\n\t\t\"padtext\", \"1\",\r\n\t\t\"fliptext\", \"1\",\r\n\t\t\"style\", \"0\",\r\n\t\t\"extra_spec_info\", \"1\",\r\n\t\t\"onlytp\", \"0\",\r\n\t\t\"bignum\", \"0\",\r\n\t\t\"colors_alpha\", \"1.0\",\r\n\t\t\"maxname\", \"16\",\r\n\t\tNULL);\r\n\r\n    HUD_Register(\"teaminfo\", NULL, \"Show information about your team in short form.\",\r\n        0, ca_active, 0, SCR_HUD_DrawTeamInfo,\r\n        \"0\", \"\", \"right\", \"center\", \"0\", \"0\", \"0.2\", \"20 20 20\", NULL,\r\n\t\t\"layout\", \"%p%n $x10%l$x11 %a/%H %w\",\r\n\t\t\"align_right\",\"0\",\r\n\t\t\"loc_width\",\"5\",\r\n\t\t\"name_width\",\"6\",\r\n\t\t\"low_health\",\"25\",\r\n\t\t\"armor_style\",\"3\",\r\n\t\t\"weapon_style\",\"0\",\r\n\t\t\"show_enemies\",\"0\",\r\n\t\t\"show_self\",\"1\",\r\n\t\t\"scale\",\"1\",\r\n\t\t\"powerup_style\",\"1\",\r\n\t\tNULL);\r\n\r\n\tHUD_Register(\"mp3_title\", NULL, \"Shows current mp3 playing.\",\r\n        HUD_PLUSMINUS, ca_disconnected, 0, SCR_HUD_DrawMP3_Title,\r\n        \"0\", \"top\", \"right\", \"bottom\", \"0\", \"0\", \"0\", \"0 0 0\", NULL,\r\n\t\t\"style\",\t\"2\",\r\n\t\t\"width\",\t\"512\",\r\n\t\t\"height\",\t\"8\",\r\n\t\t\"scroll\",\t\"1\",\r\n\t\t\"scroll_delay\", \"0.5\",\r\n\t\t\"on_scoreboard\", \"0\",\r\n\t\t\"wordwrap\", \"0\",\r\n        NULL);\r\n\r\n\tHUD_Register(\"mp3_time\", NULL, \"Shows the time of the current mp3 playing.\",\r\n        HUD_PLUSMINUS, ca_disconnected, 0, SCR_HUD_DrawMP3_Time,\r\n        \"0\", \"top\", \"left\", \"bottom\", \"0\", \"0\", \"0\", \"0 0 0\", NULL,\r\n\t\t\"style\",\t\"0\",\r\n\t\t\"on_scoreboard\", \"0\",\r\n        NULL);\r\n\r\n#ifdef WITH_PNG\r\n\tHUD_Register(\"radar\", NULL, \"Plots the players on a picture of the map. (Only when watching MVD's or QTV).\",\r\n        HUD_PLUSMINUS, ca_active, 0, SCR_HUD_DrawRadar,\r\n        \"0\", \"top\", \"left\", \"bottom\", \"0\", \"0\", \"0\", \"0 0 0\", NULL,\r\n\t\t\"opacity\", \"0.5\",\r\n\t\t\"width\", \"30%\",\r\n\t\t\"height\", \"25%\",\r\n\t\t\"autosize\", \"0\",\r\n\t\t\"show_powerups\", \"1\",\r\n\t\t\"show_names\", \"0\",\r\n\t\t\"highlight_color\", \"yellow\",\r\n\t\t\"highlight\", \"0\",\r\n\t\t\"player_size\", \"10\",\r\n\t\t\"show_height\", \"1\",\r\n\t\t\"show_stats\", \"1\",\r\n\t\t\"fade_players\", \"1\",\r\n\t\t\"show_hold\", \"0\",\r\n\t\t\"weaponfilter\", \"gl rl lg\",\r\n\t\t\"itemfilter\", \"backpack quad pent armor mega\",\r\n\t\t\"otherfilter\", \"projectiles gibs explosions shotgun\",\r\n\t\t\"onlytp\", \"0\",\r\n        NULL);\r\n#endif // WITH_PNG\r\n\r\n\tHUD_Register(\"teamholdbar\", NULL, \"Shows how much of the level (in percent) that is currently being held by either team.\",\r\n        HUD_PLUSMINUS, ca_active, 0, SCR_HUD_DrawTeamHoldBar,\r\n        \"0\", \"top\", \"left\", \"bottom\", \"0\", \"0\", \"0\", \"0 0 0\", NULL,\r\n\t\t\"opacity\", \"0.8\",\r\n\t\t\"width\", \"200\",\r\n\t\t\"height\", \"8\",\r\n\t\t\"vertical\", \"0\",\r\n\t\t\"vertical_text\", \"0\",\r\n\t\t\"show_text\", \"1\",\r\n\t\t\"onlytp\", \"0\",\r\n        NULL);\r\n\r\n\tHUD_Register(\"teamholdinfo\", NULL, \"Shows which important items in the level that are being held by the teams.\",\r\n        HUD_PLUSMINUS, ca_active, 0, SCR_HUD_DrawTeamHoldInfo,\r\n        \"0\", \"top\", \"left\", \"bottom\", \"0\", \"0\", \"0\", \"0 0 0\", NULL,\r\n\t\t\"opacity\", \"0.8\",\r\n\t\t\"width\", \"200\",\r\n\t\t\"height\", \"8\",\r\n\t\t\"onlytp\", \"0\",\r\n\t\t\"style\", \"1\",\r\n\t\t\"itemfilter\", \"quad ra ya ga mega pent rl quad\",\r\n        NULL);\r\n\r\n    HUD_Register(\"ownfrags\" /* jeez someone give me a better name please */, NULL, \"Highlights your own frags\",\r\n        0, ca_active, 1, SCR_HUD_DrawOwnFrags,\r\n        \"1\", \"screen\", \"center\", \"top\", \"0\", \"50\", \"0.2\", \"0 0 100\", NULL,\r\n        /*\r\n        \"color\", \"255 255 255\",\r\n        */\r\n        \"timeout\", \"3\",\r\n\t\t\"scale\", \"1.5\",\r\n        NULL\r\n        );\r\n\r\n\tHUD_Register(\"keys\", NULL, \"Shows which keys user does press at the moment\",\r\n\t\t0, ca_active, 1, SCR_HUD_DrawKeys,\r\n\t\t\"0\", \"screen\", \"right\", \"center\", \"0\", \"0\", \"0.5\", \"20 20 20\", NULL,\r\n\t\t\"scale\", \"2\",\r\n\t\tNULL\r\n\t\t);\r\n\r\n\tHUD_Register(\"itemsclock\", NULL, \"Displays upcoming item respawns\",\r\n\t\t0, ca_active, 1, SCR_HUD_DrawItemsClock,\r\n\t\t\"0\", \"screen\", \"right\", \"center\", \"0\", \"0\", \"0\", \"0 0 0\", NULL,\r\n\t\t\"timelimit\", \"5\",\r\n\t\t\"style\", \"0\",\r\n\t\tNULL\r\n\t\t);\r\n\r\n    HUD_Register(\"score_team\", NULL, \"Own scores or team scores.\",\r\n        0, ca_active, 0, SCR_HUD_DrawScoresTeam,\r\n        \"0\", \"screen\", \"left\", \"bottom\", \"0\", \"0\", \"0.5\", \"4 8 32\", NULL,\r\n        \"style\", \"0\",\r\n        \"scale\", \"1\",\r\n        \"align\", \"right\",\r\n        \"digits\", \"0\",\r\n\t\t\"colorize\", \"0\",\r\n        NULL\r\n\t\t);\r\n\r\n\tHUD_Register(\"score_enemy\", NULL, \"Scores of enemy or enemy team.\",\r\n        0, ca_active, 0, SCR_HUD_DrawScoresEnemy,\r\n        \"0\", \"score_team\", \"after\", \"bottom\", \"0\", \"0\", \"0.5\", \"32 4 0\", NULL,\r\n        \"style\", \"0\",\r\n        \"scale\", \"1\",\r\n        \"align\", \"right\",\r\n        \"digits\", \"0\",\r\n\t\t\"colorize\", \"0\",\r\n        NULL\r\n\t\t);\r\n\r\n\tHUD_Register(\"score_difference\", NULL, \"Difference between teamscores and enemyscores.\",\r\n        0, ca_active, 0, SCR_HUD_DrawScoresDifference,\r\n        \"0\", \"score_enemy\", \"after\", \"bottom\", \"0\", \"0\", \"0.5\", \"0 0 0\", NULL,\r\n        \"style\", \"0\",\r\n        \"scale\", \"1\",\r\n        \"align\", \"right\",\r\n        \"digits\", \"0\",\r\n\t\t\"colorize\", \"1\",\r\n        NULL\r\n\t\t);\r\n\r\n\tHUD_Register(\"score_position\", NULL, \"Position on scoreboard.\",\r\n        0, ca_active, 0, SCR_HUD_DrawScoresPosition,\r\n        \"0\", \"score_difference\", \"after\", \"bottom\", \"0\", \"0\", \"0.5\", \"0 0 0\", NULL,\r\n        \"style\", \"0\",\r\n        \"scale\", \"1\",\r\n        \"align\", \"right\",\r\n        \"digits\", \"0\",\r\n\t\t\"colorize\", \"1\",\r\n        NULL\r\n\t\t);\r\n\r\n\tHUD_Register(\"score_bar\", NULL, \"Team, enemy, and difference scores together.\",\r\n        HUD_PLUSMINUS, ca_active, 0, SCR_HUD_DrawScoresBar,\r\n        \"0\", \"screen\", \"center\", \"console\", \"0\", \"0\", \"0.5\", \"0 0 0\", NULL,\r\n        \"style\", \"0\",\r\n        \"scale\", \"1\",\r\n\t\t\"format_small\", \"&c69f%T&r:%t &cf10%E&r:%e $[%D$]\",\r\n\t\t\"format_big\", \"%t:%e:%Z\",\r\n\r\n        NULL\r\n\t\t);\r\n\r\n\tHUD_Register(\"bar_armor\", NULL, \"Armor bar.\",\r\n        HUD_PLUSMINUS, ca_active, 0, SCR_HUD_DrawBarArmor,\r\n        \"0\", \"armor\", \"left\", \"center\", \"0\", \"0\", \"0\", \"0 0 0\", NULL,\r\n        \"height\", \"16\",\r\n        \"width\", \"64\",\r\n\t\t\"direction\", \"1\",\r\n\t\t\"color_noarmor\", \"128 128 128 64\",\r\n\t\t\"color_ga\", \"32 128 0 128\",\r\n\t\t\"color_ya\", \"192 128 0 128\",\r\n\t\t\"color_ra\", \"128 0 0 128\",\r\n\t\t\"color_unnatural\", \"255 255 255 128\",\r\n        NULL\r\n\t\t);\r\n\r\n\tHUD_Register(\"bar_health\", NULL, \"Health bar.\",\r\n        HUD_PLUSMINUS, ca_active, 0, SCR_HUD_DrawBarHealth,\r\n        \"0\", \"health\", \"right\", \"center\", \"0\", \"0\", \"0\", \"0 0 0\", NULL,\r\n        \"height\", \"16\",\r\n        \"width\", \"64\",\r\n\t\t\"direction\", \"0\",\r\n\t\t\"color_nohealth\", \"128 128 128 64\",\r\n\t\t\"color_normal\", \"32 64 128 128\",\r\n\t\t\"color_mega\", \"64 96 128 128\",\r\n\t\t\"color_twomega\", \"128 128 255 128\",\r\n\t\t\"color_unnatural\", \"255 255 255 128\",\r\n        NULL\r\n\t\t);\r\n\r\n#ifdef QUAKEHUD\r\n\tHUD_Register(\"weaponstats\", NULL, \"Weapon Stats\",\r\n        HUD_PLUSMINUS, ca_active, 0, SCR_HUD_DrawWeaponStats,\r\n        \"0\", \"screen\", \"right\", \"center\", \"0\", \"0\", \"0\", \"0 0 0\", NULL,\r\n\t\t\"scale\", \"1\",\r\n\t\t\"fmt\", \"&c990sg&r:[%sg] &c099ssg&r:[%ssg] &c900rl&r:[#rl] &c009lg&r:[%lg]\",\r\n        NULL\r\n\t\t);\r\n#endif\r\n\r\n/* hexum -> FIXME? this is used only for debug purposes, I wont bother to port it (it shouldnt be too difficult if anyone cares)\r\n#ifdef _DEBUG\r\n    HUD_Register(\"framegraph\", NULL, \"Shows different frame times for debug/profiling purposes.\",\r\n        HUD_PLUSMINUS | HUD_ON_SCORES, ca_disconnected, 0, SCR_HUD_DrawFrameGraph,\r\n        \"0\", \"top\", \"left\", \"bottom\", \"0\", \"0\", \"2\",\r\n        \"swap_x\",       \"0\",\r\n        \"swap_y\",       \"0\",\r\n        \"scale\",        \"14\",\r\n        \"width\",        \"256\",\r\n        \"height\",       \"64\",\r\n        \"alpha\",        \"1\",\r\n        NULL);\r\n#endif\r\n*/\r\n\r\n}\r\n\r\n\r\n"
  },
  {
    "path": "plugins/ezhud/hud_common.h",
    "content": "/*\nCopyright (C) 2011 azazello and ezQuake team\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program.  If not, see <http://www.gnu.org/licenses/>.\n*/\n//\n// common HUD elements\n// like clock etc..\n//\n\n#ifndef __HUD_COMMON__H__\n#define __HUD_COMMON__H__\n\nextern hud_t *hud_netgraph;\n\nvoid SCR_HUD_Netgraph(hud_t *hud);\nvoid SCR_HUD_DrawFPS(hud_t *hud);\nvoid SCR_HUD_DrawNetStats(hud_t *hud);\n\nvoid SCR_HUD_DrawGun2 (hud_t *hud);\nvoid SCR_HUD_DrawGun3 (hud_t *hud);\nvoid SCR_HUD_DrawGun4 (hud_t *hud);\nvoid SCR_HUD_DrawGun5 (hud_t *hud);\nvoid SCR_HUD_DrawGun6 (hud_t *hud);\nvoid SCR_HUD_DrawGun7 (hud_t *hud);\nvoid SCR_HUD_DrawGun8 (hud_t *hud);\nvoid SCR_HUD_DrawGunCurrent (hud_t *hud);\n\nvoid HUD_NewMap();\nvoid HUD_NewRadarMap();\nvoid SCR_HUD_DrawRadar(hud_t *hud);\n\nvoid HudCommon_Init(void);\nvoid SCR_HUD_DrawNum(hud_t *hud, int num, qbool low, float scale, int style, int digits, char *s_align);\n\nextern qbool autohud_loaded;\nextern cvar_t *mvd_autohud;\nvoid HUD_AutoLoad_MVD(int autoload);\n\nvoid HUD_BeforeDraw();\nvoid HUD_AfterDraw();\nvoid CommonDraw_Init(void);\n\n#endif // __HUD_COMMON__H__\n"
  },
  {
    "path": "plugins/ezhud/hud_editor.c",
    "content": "/*\nCopyright (C) 2011 ezQuake team\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program.  If not, see <http://www.gnu.org/licenses/>.\n*/\n/**\n\tHUD Editor module\n\n\tInitial concept code jogihoogi, rewritten by Cokeman, Feb 2007\n*/\n\n#include \"ezquakeisms.h\"\n\n//#include \"qsound.h\"\n//#include \"utils.h\"\n//#include \"menu.h\"\n//#include \"keys.h\"\n//#include \"Ctrl.h\"\n//#include \"ez_controls.h\"\n//#include \"ez_button.h\"\n//#include \"ez_label.h\"\n//#include \"ez_scrollbar.h\"\n//#include \"ez_scrollpane.h\"\n//#include \"ez_slider.h\"\n//#include \"ez_listview.h\"\n//#include \"ez_window.h\"\n#include \"hud.h\"\n#include \"hud_editor.h\"\n\nextern unsigned int keydown[];\n#define isShiftDown() (keydown[K_LSHIFT]||keydown[K_RSHIFT])\n#define isCtrlDown() (keydown[K_LCTRL]||keydown[K_RCTRL])\n#define isAltDown() (keydown[K_LALT]||keydown[K_RALT])\n\n#ifdef HAXX\nez_tree_t help_control_tree;\n#endif\n\nextern mpic_t\t\t*scr_cursor_icon;\t// cl_screen.c\nextern cvar_t\t\t*hud_planmode;\t\t// hud_common.c\n\nmpic_t\t\t\t\t*hud_editor_move_icon\t= NULL;\nmpic_t\t\t\t\t*hud_editor_resize_icon\t= NULL;\nmpic_t\t\t\t\t*hud_editor_align_icon\t= NULL;\nmpic_t\t\t\t\t*hud_editor_place_icon\t= NULL;\n\ncvar_t\t\t\t\t*hud_editor_allowresize;//\t= {\"hud_editor_allowresize\", \"1\"};\t// Show resize handles / allow resizing.\ncvar_t\t\t\t\t*hud_editor_allowmove;//\t= {\"hud_editor_allowmove\", \"1\"};\t// Allow moving...\ncvar_t\t\t\t\t*hud_editor_allowplace;//\t= {\"hud_editor_allowplace\", \"1\"};\t// Allow placing HUDs.\ncvar_t\t\t\t\t*hud_editor_allowalign;//\t= {\"hud_editor_allowalign\", \"1\"};\t// Allow aligning HUDs.\n\nextern hud_t\t\t*hud_huds;\t\t\t\t\t\t\t\t\t\t// The list of HUDs..\nhud_t\t\t\t\t*selected_hud\t\t\t= NULL;\t\t\t\t\t// The currently selected HUD.\n\nqbool\t\t\t\thud_editor\t\t\t\t= false;\t\t\t\t// If we're in HUD editor mode or not.\nhud_editor_mode_t\thud_editor_mode\t\t\t= hud_editmode_off;\t\t// The current mode the HUD editor is in.\nhud_editor_mode_t\thud_editor_prevmode\t\t= hud_editmode_off;\t\t// The previous mode the HUD editor was in.\n\nqbool\t\t\t\thud_editor_showhelp\t\t= false;\t\t\t\t// Show the help plaque or not?\nqbool\t\t\t\thud_editor_showoutlines\t= true;\t\t\t\t\t// Show guidelines around the HUD elements.\n\nfloat\t\t\t\thud_mouse_x;\t\t\t\t\t\t\t\t\t// The screen coordinates of the mouse cursor.\nfloat\t\t\t\thud_mouse_y;\n\n// Macros for what mouse buttons are clicked.\n#define MOUSEDOWN\t\t\t( keydown[K_MOUSE1] ||  keydown[K_MOUSE2]|| keydown[K_MOUSE3])\n#define MOUSEDOWN_1_2\t\t( keydown[K_MOUSE1] &&  keydown[K_MOUSE2])\n#define MOUSEDOWN_1_3\t\t( keydown[K_MOUSE1] &&  keydown[K_MOUSE3])\n#define MOUSEDOWN_1_2_3\t\t( keydown[K_MOUSE1] &&  keydown[K_MOUSE2] &&  keydown[K_MOUSE3])\n#define MOUSEDOWN_1_ONLY\t( keydown[K_MOUSE1] && !keydown[K_MOUSE2] && !keydown[K_MOUSE3])\n#define MOUSEDOWN_2_ONLY\t(!keydown[K_MOUSE1] &&  keydown[K_MOUSE2] && !keydown[K_MOUSE3])\n#define MOUSEDOWN_3_ONLY\t(!keydown[K_MOUSE1] && !keydown[K_MOUSE2] &&  keydown[K_MOUSE3])\n#define MOUSEDOWN_1_2_ONLY\t( keydown[K_MOUSE1] &&  keydown[K_MOUSE2] && !keydown[K_MOUSE3])\n#define MOUSEDOWN_1_3_ONLY\t( keydown[K_MOUSE1] && !keydown[K_MOUSE2] &&  keydown[K_MOUSE3])\n#define MOUSEDOWN_2_3_ONLY\t(!keydown[K_MOUSE1] &&  keydown[K_MOUSE2] &&  keydown[K_MOUSE3])\n#define MOUSEDOWN_NONE\t\t(!keydown[K_MOUSE1] && !keydown[K_MOUSE2] && !keydown[K_MOUSE3])\n\n#define HUD_CENTER_X(h)\t\t((h)->lx + (h)->lw / 2)\t// Gets the coordinates for the center point of the specified hud.\n#define HUD_CENTER_Y(h)\t\t((h)->ly + (h)->lh / 2)\n\n//\n// HUD align polygons.\n//\n#define HUD_ALIGN_POLYCOUNT_CORNER\t5\n#define HUD_ALIGN_POLYCOUNT_EDGE\t4\n#define HUD_ALIGN_POLYCOUNT_CENTER\t8\n#define HUD_ALIGN_POLYCOUNT_CONSOLE\t4\n\nvec3_t *hud_align_current_poly = NULL;\t// The current alignment polygon when in alignment mode.\nint hud_align_current_polycount = 0;\t// Number of vertices in teh polygon.\n\nvec3_t hud_align_topright_poly[HUD_ALIGN_POLYCOUNT_CORNER];\nvec3_t hud_align_top_poly[HUD_ALIGN_POLYCOUNT_EDGE];\nvec3_t hud_align_topleft_poly[HUD_ALIGN_POLYCOUNT_CORNER];\nvec3_t hud_align_left_poly[HUD_ALIGN_POLYCOUNT_EDGE];\nvec3_t hud_align_bottomleft_poly[HUD_ALIGN_POLYCOUNT_CORNER];\nvec3_t hud_align_bottom_poly[HUD_ALIGN_POLYCOUNT_EDGE];\nvec3_t hud_align_bottomright_poly[HUD_ALIGN_POLYCOUNT_CORNER];\nvec3_t hud_align_right_poly[HUD_ALIGN_POLYCOUNT_EDGE];\nvec3_t hud_align_center_poly[HUD_ALIGN_POLYCOUNT_CENTER];\n\nvec3_t hud_align_consoleleft_poly[HUD_ALIGN_POLYCOUNT_CONSOLE];\nvec3_t hud_align_console_poly[HUD_ALIGN_POLYCOUNT_CONSOLE];\nvec3_t hud_align_consoleright_poly[HUD_ALIGN_POLYCOUNT_CONSOLE];\n\ntypedef enum hud_alignmode_s\n{\n\thud_align_center,\n\thud_align_top,\n\thud_align_topleft,\n\thud_align_left,\n\thud_align_bottomleft,\n\thud_align_bottom,\n\thud_align_bottomright,\n\thud_align_right,\n\thud_align_topright,\n\thud_align_consoleleft,\n\thud_align_consoleright,\n\thud_align_console\n\t// Not including before/after for now.\n} hud_alignmode_t;\n\nhud_alignmode_t\thud_alignmode = hud_align_center;\n\n// Possible positions for a grep handle.\ntypedef enum hud_greppos_s\n{\n\tpos_visible,\t// On screen.\n\tpos_top,\n\tpos_left,\n\tpos_bottom,\n\tpos_right\n} hud_greppos_t;\n\n// A Grep handle for when a HUD goes offscreen.\ntypedef struct hud_grephandle_s\n{\n\thud_t\t\t\t\t\t*hud;\t\t\t// HUD associated with this grep handle.\n\tint\t\t\t\t\t\tx;\t\t\t\t// The position in screen coordinates for the grephandle.\n\tint\t\t\t\t\t\ty;\n\tint\t\t\t\t\t\twidth;\n\tint\t\t\t\t\t\theight;\n\tqbool\t\t\t\t\thighlighted;\t// Should this grephandle be drawn highlighted?\n\thud_greppos_t\t\t\tpos;\t\t\t// The offscreen position of the HUD associated with this handle.\n\tstruct hud_grephandle_s\t*next;\n\tstruct hud_grephandle_s\t*previous;\n} hud_grephandle_t;\n\nhud_grephandle_t\t*hud_greps = NULL;\t\t\t\t\t// The list of \"grep handles\" that are shown if a HUD element is moved offscreen.\n\nhud_grephandle_t\t*hud_hoverlist = NULL;\t\t\t\t// The list of HUDs the mouse is hovering.\nint\t\t\t\t\thud_hoverlist_count = 0;\t\t\t// The number of hovered HUDs...\nqbool\t\t\t\thud_hoverlist_pos_is_set = false;\t// Has the coordinates been set for the hoverlist yet?\nfloat\t\t\t\thud_hoverlist_x;\t\t\t\t\t// The x position of the hover list.\nfloat\t\t\t\thud_hoverlist_y;\t\t\t\t\t// The y position of the hover list.\nhud_grephandle_t\thud_containers[MAX_HUD_ELEMENTS];\t// List of HUDs which have been hovered.\n\n//\n// Adds a new HUD to the list of HUDs that are being hovered.\n//\nstatic void HUD_Editor_AddHoverHud(hud_grephandle_t *hud_container)\n{\n\t// Nothing to add.\n\tif(!hud_container)\n\t{\n\t\treturn;\n\t}\n\n\thud_container->next = hud_hoverlist;\n\tif(hud_hoverlist)\n\t{\n\t\thud_hoverlist->previous = hud_container;\n\t}\n\n\thud_hoverlist = hud_container;\n\n\thud_hoverlist_count++;\n}\n\n//\n// Associates (\"Creates\") a HUD with a HUD container.\n//\nstatic hud_grephandle_t *HUD_Editor_CreateHoverHud(hud_t *hud)\n{\n\tstatic int i = 0;\n\tint j = 0;\n\n\tif(i >= MAX_HUD_ELEMENTS - 1)\n\t{\n\t\treturn NULL;\n\t}\n\n\t// Check if the HUD already exists and use that one if that's the case...\n\tfor(j = 0; j < MAX_HUD_ELEMENTS && hud_containers[j].hud; j++)\n\t{\n\t\tif(hud_containers[j].hud == hud)\n\t\t{\n\t\t\thud_containers[j].next = NULL;\n\t\t\thud_containers[j].previous = NULL;\n\t\t\treturn &hud_containers[j];\n\t\t}\n\t}\n\n\t// The HUD wasn't found so add it to the end of the list.\n\thud_containers[i].hud = hud;\n\thud_containers[i].next = NULL;\n\thud_containers[i].previous = NULL;\n\ti++;\n\n\treturn &hud_containers[i - 1];\n}\n\n//\n// Find the HUD that is being selected in the hover list.\n//\nstatic hud_t *HUD_Editor_FindHoverListSelection(hud_grephandle_t *list)\n{\n\thud_t *match = NULL;\n\thud_grephandle_t *hud_iter = list;\n\n\twhile(hud_iter)\n\t{\n\t\tif(hud_iter->hud\n\t\t\t&& hud_mouse_x > hud_iter->x\n\t\t\t&& hud_mouse_x < (hud_iter->x + hud_iter->width)\n\t\t\t&& hud_mouse_y > hud_iter->y\n\t\t\t&& hud_mouse_y < (hud_iter->y + hud_iter->height))\n\t\t{\n\t\t\thud_iter->highlighted = true;\n\t\t\tmatch = hud_iter->hud;\n\t\t}\n\t\telse\n\t\t{\n\t\t\thud_iter->highlighted = false;\n\t\t}\n\n\t\thud_iter = hud_iter->next;\n\t}\n\n\treturn match;\n}\n\n//\n// Draw the hover list...\n//\nstatic qbool HUD_Editor_DrawHoverList(int x, int y, hud_grephandle_t *list)\n{\n\t#define PADDING 5\n\t#define LINEGAP\t1\n\tint width = 0;\n\tint height = 0;\n\tint i = 0;\n\tclrinfo_t color, highlight;\n\thud_grephandle_t *hud_iter = list;\n\n\t// No point in showing a list if there's only one hud under the cursor\n\t// or if the user hasn't right-clicked.\n\tif (hud_hoverlist_count <= 1 || hud_editor_mode != hud_editmode_hoverlist)\n\t{\n\t\treturn false;\n\t}\n\n\t// Get the width of to draw with.\n\twhile (hud_iter)\n\t{\n\t\twidth = max(width, (PADDING + 2 + strlen(hud_iter->hud->name)) * 8);\n\t\thud_iter = hud_iter->next;\n\t}\n\n\t// Draw the background fill.\n\theight = ((hud_hoverlist_count - 1) * (8 + LINEGAP)) + (2 * PADDING);\n\tDraw_AlphaFillRGB(x, y - height, width, height, 0, 0, 0, 255);\n\n\thud_iter = list;\n\n\t// Highlight color (yellow)...\n\tVector4Set(highlight.c, 0, 255, 0, 255);\n\n\t// Orange.\n\tVector4Set(color.c, 255, 150, 0, 255);\n\n\t// Draw the list items.\n\twhile(hud_iter)\n\t{\n\t\t// Calculate the size and position of the HUD in the list.\n\t\thud_iter->x = x;\n\t\thud_iter->y = y - height + (i * 8) + LINEGAP;\n\t\thud_iter->width = width;\n\t\thud_iter->height = 8;\n\n\t\t// Draw the z-order + name of the HUD.\n\t\tDraw_ColoredString3(hud_iter->x, hud_iter->y,\n\t\t\tva(\"%2d - %s\", hud_iter->hud->order->ival, hud_iter->hud->name),\n\t\t\t(hud_iter->highlighted ? &highlight : &color), 1, 0);\n\n\t\thud_iter = hud_iter->next;\n\t\ti++;\n\t}\n\n\treturn true;\n}\n\n//\n// Sets a new HUD Editor mode (and saves the previous one).\n//\nstatic void HUD_Editor_SetMode(hud_editor_mode_t newmode)\n{\n\thud_editor_prevmode = hud_editor_mode;\n\thud_editor_mode = newmode;\n}\n\n//\n// Draws a tool tip with a background next to the cursor.\n//\nstatic void HUD_Editor_DrawTooltip(int x, int y, char *string, float r, float g, float b, float a)\n{\n\tint len = strlen(string) * 8;\n\n\ty -= 9;\n\n\t// Make sure we're drawing within the screen.\n\tif (x + len > vid.width)\n\t{\n\t\tx -= len;\n\t}\n\n\tif (y + 9 > vid.height)\n\t{\n\t\ty -= 9;\n\t}\n\n\tDraw_AlphaFillRGB(x, y, len, 8, r, g, b, a);\n\tDraw_String(x, y, string);\n}\n\n//\n// Gets an alignment string for a specified alignmode enum.\n//\nstatic char *HUD_Editor_GetAlignmentString(hud_alignmode_t align)\n{\n\tswitch(hud_alignmode)\n\t{\n\t\tcase hud_align_center :\t\treturn \"center center\";\n\t\tcase hud_align_top :\t\treturn \"center top\";\n\t\tcase hud_align_topleft :\treturn \"left top\";\n\t\tcase hud_align_left :\t\treturn \"left center\";\n\t\tcase hud_align_bottomleft :\treturn \"left bottom\";\n\t\tcase hud_align_bottom :\t\treturn \"center bottom\";\n\t\tcase hud_align_bottomright :return \"right bottom\";\n\t\tcase hud_align_right :\t\treturn \"right center\";\n\t\tcase hud_align_topright :\treturn \"right top\";\n\t\tcase hud_align_consoleleft :return \"left console\";\n\t\tcase hud_align_console :\treturn \"center console\";\n\t\tcase hud_align_consoleright:return \"right console\";\n\t\tdefault :\t\t\t\t\treturn \"\";\n\t}\n}\n\n//\n// Returns an alignment enum based on a specified alignment string.\n//\nstatic hud_alignmode_t HUD_Editor_GetAlignmentFromString(char *alignstr)\n{\n\tif(!strcmp(alignstr, \"center center\"))\n\t{\n\t\treturn hud_align_center;\n\t}\n\telse if(!strcmp(alignstr, \"center top\"))\n\t{\n\t\treturn hud_align_top;\n\t}\n\telse if(!strcmp(alignstr, \"left top\"))\n\t{\n\t\treturn hud_align_topleft;\n\t}\n\telse if(!strcmp(alignstr, \"left center\"))\n\t{\n\t\treturn hud_align_left;\n\t}\n\telse if(!strcmp(alignstr, \"left bottom\"))\n\t{\n\t\treturn hud_align_bottomleft;\n\t}\n\telse if(!strcmp(alignstr, \"center bottom\"))\n\t{\n\t\treturn hud_align_bottom;\n\t}\n\telse if(!strcmp(alignstr, \"right bottom\"))\n\t{\n\t\treturn hud_align_bottomright;\n\t}\n\telse if(!strcmp(alignstr, \"right center\"))\n\t{\n\t\treturn hud_align_right;\n\t}\n\telse if(!strcmp(alignstr, \"right top\"))\n\t{\n\t\treturn hud_align_topright;\n\t}\n\telse if(!strcmp(alignstr, \"left console\"))\n\t{\n\t\treturn hud_align_consoleleft;\n\t}\n\telse if(!strcmp(alignstr, \"center console\"))\n\t{\n\t\treturn hud_align_console;\n\t}\n\telse if(!strcmp(alignstr, \"right console\"))\n\t{\n\t\treturn hud_align_consoleright;\n\t}\n\n\treturn hud_align_center;\n}\n\nqbool IsPointInPolygon(int points, vec3_t planes[], float x, float y)\n{\n\treturn false;\n}\n//\n// Returns the alignment we are trying to align the selected HUD to at it's placement\n// when in alignment mode.\n//\nstatic hud_alignmode_t HUD_Editor_GetAlignment(int x, int y, hud_t *hud_element)\n{\n\t// For less clutter.\n\tfloat\tmid_x\t\t= 0.0;\n\tfloat\tmid_y\t\t= 0.0;\n\tint\t\toffset_x\t= 0;\n\tint\t\toffset_y\t= 0;\n\n\tif(hud_element)\n\t{\n\t\t// Aligns for HUD elements.\n\t\toffset_x = hud_element->lx;\n\t\toffset_y = hud_element->ly;\n\t\tmid_x = hud_element->lw / 2.0;\n\t\tmid_y = hud_element->lh / 2.0;\n\t}\n\telse\n\t{\n\t\t// Aligns for Screen.\n\t\toffset_x = 0;\n\t\toffset_y = (int)(vid.height * 0.05);\n\t\tmid_x = vid.width / 2.0;\n\t\tmid_y = (vid.height + offset_y) / 2.0;\n\t}\n\n\tif(!hud_element)\n\t{\n\t\t// Console left.\n\t\t{\n\t\t\thud_align_consoleleft_poly[0][0] = 0;\n\t\t\thud_align_consoleleft_poly[0][1] = 0;\n\t\t\thud_align_consoleleft_poly[0][2] = 0;\n\n\t\t\thud_align_consoleleft_poly[1][0] = 0;\n\t\t\thud_align_consoleleft_poly[1][1] = offset_y;\n\t\t\thud_align_consoleleft_poly[1][2] = 0;\n\n\t\t\thud_align_consoleleft_poly[2][0] = mid_x / 2.0;\n\t\t\thud_align_consoleleft_poly[2][1] = offset_y;\n\t\t\thud_align_consoleleft_poly[2][2] = 0;\n\n\t\t\thud_align_consoleleft_poly[3][0] = mid_x / 2.0;\n\t\t\thud_align_consoleleft_poly[3][1] = 0;\n\t\t\thud_align_consoleleft_poly[3][2] = 0;\n\n\t\t\tif (IsPointInPolygon(HUD_ALIGN_POLYCOUNT_CONSOLE, hud_align_consoleleft_poly, x, y))\n\t\t\t{\n\t\t\t\thud_align_current_poly = hud_align_consoleleft_poly;\n\t\t\t\thud_align_current_polycount = HUD_ALIGN_POLYCOUNT_CONSOLE;\n\t\t\t\treturn hud_align_consoleleft;\n\t\t\t}\n\t\t}\n\n\t\t// Console.\n\t\t{\n\t\t\thud_align_console_poly[0][0] = mid_x / 2.0;\n\t\t\thud_align_console_poly[0][1] = 0;\n\t\t\thud_align_console_poly[0][2] = 0;\n\n\t\t\thud_align_console_poly[1][0] = mid_x / 2.0;\n\t\t\thud_align_console_poly[1][1] = offset_y;\n\t\t\thud_align_console_poly[1][2] = 0;\n\n\t\t\thud_align_console_poly[2][0] = mid_x + (mid_x / 2.0);\n\t\t\thud_align_console_poly[2][1] = offset_y;\n\t\t\thud_align_console_poly[2][2] = 0;\n\n\t\t\thud_align_console_poly[3][0] = mid_x + (mid_x / 2.0);\n\t\t\thud_align_console_poly[3][1] = 0;\n\t\t\thud_align_console_poly[3][2] = 0;\n\n\t\t\tif (IsPointInPolygon(HUD_ALIGN_POLYCOUNT_CONSOLE, hud_align_console_poly, x, y))\n\t\t\t{\n\t\t\t\thud_align_current_poly = hud_align_console_poly;\n\t\t\t\thud_align_current_polycount = HUD_ALIGN_POLYCOUNT_CONSOLE;\n\t\t\t\treturn hud_align_console;\n\t\t\t}\n\t\t}\n\n\t\t// Console right.\n\t\t{\n\t\t\thud_align_consoleright_poly[0][0] = mid_x + (mid_x / 2.0);\n\t\t\thud_align_consoleright_poly[0][1] = 0;\n\t\t\thud_align_consoleright_poly[0][2] = 0;\n\n\t\t\thud_align_consoleright_poly[1][0] = mid_x + (mid_x / 2.0);\n\t\t\thud_align_consoleright_poly[1][1] = offset_y;\n\t\t\thud_align_consoleright_poly[1][2] = 0;\n\n\t\t\thud_align_consoleright_poly[2][0] = 2 * mid_x;\n\t\t\thud_align_consoleright_poly[2][1] = offset_y;\n\t\t\thud_align_consoleright_poly[2][2] = 0;\n\n\t\t\thud_align_consoleright_poly[3][0] = 2 * mid_x;\n\t\t\thud_align_consoleright_poly[3][1] = 0;\n\t\t\thud_align_consoleright_poly[3][2] = 0;\n\n\t\t\tif (IsPointInPolygon(HUD_ALIGN_POLYCOUNT_CONSOLE, hud_align_consoleright_poly, x, y))\n\t\t\t{\n\t\t\t\thud_align_current_poly = hud_align_consoleright_poly;\n\t\t\t\thud_align_current_polycount = HUD_ALIGN_POLYCOUNT_CONSOLE;\n\t\t\t\treturn hud_align_consoleright;\n\t\t\t}\n\t\t}\n\t}\n\n\t// Top right.\n\t{\n\t\thud_align_topright_poly[0][0] = mid_x + (mid_x / 2.0);\n\t\thud_align_topright_poly[0][1] = 0;\n\t\thud_align_topright_poly[0][2] = 0;\n\n\t\thud_align_topright_poly[1][0] = mid_x + (mid_x / 4.0);\n\t\thud_align_topright_poly[1][1] = mid_y - (mid_y / 2.0);\n\t\thud_align_topright_poly[1][2] = 0;\n\n\t\thud_align_topright_poly[2][0] = mid_x + (mid_x / 2.0);\n\t\thud_align_topright_poly[2][1] = mid_y - (mid_y / 4.0);\n\t\thud_align_topright_poly[2][2] = 0;\n\n\t\thud_align_topright_poly[3][0] = 2 * mid_x;\n\t\thud_align_topright_poly[3][1] = mid_y - (mid_y / 2.0);\n\t\thud_align_topright_poly[3][2] = 0;\n\n\t\thud_align_topright_poly[4][0] = 2 * mid_x;\n\t\thud_align_topright_poly[4][1] = 0;\n\t\thud_align_topright_poly[4][2] = 0;\n\n\t\tif (IsPointInPolygon(HUD_ALIGN_POLYCOUNT_CORNER, hud_align_topright_poly, x - offset_x, y - offset_y))\n\t\t{\n\t\t\thud_align_current_poly = hud_align_topright_poly;\n\t\t\thud_align_current_polycount = HUD_ALIGN_POLYCOUNT_CORNER;\n\t\t\treturn hud_align_topright;\n\t\t}\n\t}\n\n\t// Top.\n\t{\n\t\thud_align_top_poly[0][0] = mid_x / 2.0;\n\t\thud_align_top_poly[0][1] = 0;\n\t\thud_align_top_poly[0][2] = 0;\n\n\t\thud_align_top_poly[1][0] = mid_x - (mid_x / 4.0);\n\t\thud_align_top_poly[1][1] = mid_y - (mid_y / 2.0);\n\t\thud_align_top_poly[1][2] = 0;\n\n\t\thud_align_top_poly[2][0] = mid_x + (mid_x / 4.0);\n\t\thud_align_top_poly[2][1] = mid_y - (mid_y / 2.0);\n\t\thud_align_top_poly[2][2] = 0;\n\n\t\thud_align_top_poly[3][0] = mid_x + (mid_x / 2.0);\n\t\thud_align_top_poly[3][1] = 0;\n\t\thud_align_top_poly[3][2] = 0;\n\n\t\tif (IsPointInPolygon(HUD_ALIGN_POLYCOUNT_EDGE, hud_align_top_poly, x - offset_x, y - offset_y))\n\t\t{\n\t\t\thud_align_current_poly = hud_align_top_poly;\n\t\t\thud_align_current_polycount = HUD_ALIGN_POLYCOUNT_EDGE;\n\t\t\treturn hud_align_top;\n\t\t}\n\t}\n\n\t// Top Left.\n\t{\n\t\thud_align_topleft_poly[0][0] = 0;\n\t\thud_align_topleft_poly[0][1] = 0;\n\t\thud_align_topleft_poly[0][2] = 0;\n\n\t\thud_align_topleft_poly[1][0] = 0;\n\t\thud_align_topleft_poly[1][1] = mid_y - (mid_y / 2.0);\n\t\thud_align_topleft_poly[1][2] = 0;\n\n\t\thud_align_topleft_poly[2][0] = mid_x - (mid_x / 2.0);\n\t\thud_align_topleft_poly[2][1] = mid_y - (mid_y / 4.0);\n\t\thud_align_topleft_poly[2][2] = 0;\n\n\t\thud_align_topleft_poly[3][0] = mid_x - (mid_x / 4.0);\n\t\thud_align_topleft_poly[3][1] = mid_y - (mid_y / 2.0);\n\t\thud_align_topleft_poly[3][2] = 0;\n\n\t\thud_align_topleft_poly[4][0] = mid_x - (mid_x / 2.0);\n\t\thud_align_topleft_poly[4][1] = 0;\n\t\thud_align_topleft_poly[4][2] = 0;\n\n\t\tif (IsPointInPolygon(HUD_ALIGN_POLYCOUNT_CORNER, hud_align_topleft_poly, x - offset_x, y - offset_y))\n\t\t{\n\t\t\thud_align_current_poly = hud_align_topleft_poly;\n\t\t\thud_align_current_polycount = HUD_ALIGN_POLYCOUNT_CORNER;\n\t\t\treturn hud_align_topleft;\n\t\t}\n\t}\n\n\t// Left.\n\t{\n\t\thud_align_left_poly[0][0] = 0;\n\t\thud_align_left_poly[0][1] = mid_y / 2.0;\n\t\thud_align_left_poly[0][2] = 0;\n\n\t\thud_align_left_poly[1][0] = 0;\n\t\thud_align_left_poly[1][1] = mid_y + (mid_y / 2.0);\n\t\thud_align_left_poly[1][2] = 0;\n\n\t\thud_align_left_poly[2][0] = mid_x / 2.0;\n\t\thud_align_left_poly[2][1] = mid_y + (mid_y / 4.0);\n\t\thud_align_left_poly[2][2] = 0;\n\n\t\thud_align_left_poly[3][0] = mid_x / 2.0;\n\t\thud_align_left_poly[3][1] = mid_y - (mid_y / 4.0);\n\t\thud_align_left_poly[3][2] = 0;\n\n\t\tif (IsPointInPolygon(HUD_ALIGN_POLYCOUNT_EDGE, hud_align_left_poly, x - offset_x, y - offset_y))\n\t\t{\n\t\t\thud_align_current_poly = hud_align_left_poly;\n\t\t\thud_align_current_polycount = HUD_ALIGN_POLYCOUNT_EDGE;\n\t\t\treturn hud_align_left;\n\t\t}\n\t}\n\n\t// Bottom Left.\n\t{\n\t\thud_align_bottomleft_poly[0][0] = 0;\n\t\thud_align_bottomleft_poly[0][1] = mid_y + (mid_y / 2.0);\n\t\thud_align_bottomleft_poly[0][2] = 0;\n\n\t\thud_align_bottomleft_poly[1][0] = 0;\n\t\thud_align_bottomleft_poly[1][1] = 2 * mid_y;\n\t\thud_align_bottomleft_poly[1][2] = 0;\n\n\t\thud_align_bottomleft_poly[2][0] = mid_x / 2.0;\n\t\thud_align_bottomleft_poly[2][1] = 2 * mid_y;\n\t\thud_align_bottomleft_poly[2][2] = 0;\n\n\t\thud_align_bottomleft_poly[3][0] = mid_x - (mid_x / 4.0);\n\t\thud_align_bottomleft_poly[3][1] = mid_y + (mid_y / 2.0);\n\t\thud_align_bottomleft_poly[3][2] = 0;\n\n\t\thud_align_bottomleft_poly[4][0] = mid_x / 2.0;\n\t\thud_align_bottomleft_poly[4][1] = mid_y + (mid_y / 4.0);\n\t\thud_align_bottomleft_poly[4][2] = 0;\n\n\t\tif (IsPointInPolygon(HUD_ALIGN_POLYCOUNT_CORNER, hud_align_bottomleft_poly, x - offset_x, y - offset_y))\n\t\t{\n\t\t\thud_align_current_poly = hud_align_bottomleft_poly;\n\t\t\thud_align_current_polycount = HUD_ALIGN_POLYCOUNT_CORNER;\n\t\t\treturn hud_align_bottomleft;\n\t\t}\n\t}\n\n\t// Bottom.\n\t{\n\t\thud_align_bottom_poly[0][0] = mid_x - (mid_x / 2.0);\n\t\thud_align_bottom_poly[0][1] = 2 * mid_y;\n\t\thud_align_bottom_poly[0][2] = 0;\n\n\t\thud_align_bottom_poly[1][0] = mid_x + (mid_x / 2.0);\n\t\thud_align_bottom_poly[1][1] = 2 * mid_y;\n\t\thud_align_bottom_poly[1][2] = 0;\n\n\t\thud_align_bottom_poly[2][0] = mid_x + (mid_x / 4.0);\n\t\thud_align_bottom_poly[2][1] = mid_y + (mid_y / 2.0);\n\t\thud_align_bottom_poly[2][2] = 0;\n\n\t\thud_align_bottom_poly[3][0] = mid_x - (mid_x / 4.0);\n\t\thud_align_bottom_poly[3][1] = mid_y + (mid_y / 2.0);\n\t\thud_align_bottom_poly[3][2] = 0;\n\n\t\tif (IsPointInPolygon(HUD_ALIGN_POLYCOUNT_EDGE, hud_align_bottom_poly, x - offset_x, y - offset_y))\n\t\t{\n\t\t\thud_align_current_poly = hud_align_bottom_poly;\n\t\t\thud_align_current_polycount = HUD_ALIGN_POLYCOUNT_EDGE;\n\t\t\treturn hud_align_bottom;\n\t\t}\n\t}\n\n\t// Bottom Right.\n\t{\n\t\thud_align_bottomright_poly[0][0] = mid_x + (mid_x / 2.0);\n\t\thud_align_bottomright_poly[0][1] = 2 * mid_y;\n\t\thud_align_bottomright_poly[0][2] = 0;\n\n\t\thud_align_bottomright_poly[1][0] = 2 * mid_x;\n\t\thud_align_bottomright_poly[1][1] = 2 * mid_y;\n\t\thud_align_bottomright_poly[1][2] = 0;\n\n\t\thud_align_bottomright_poly[2][0] = 2 * mid_x;\n\t\thud_align_bottomright_poly[2][1] = mid_y + (mid_y / 2.0);\n\t\thud_align_bottomright_poly[2][2] = 0;\n\n\t\thud_align_bottomright_poly[3][0] = mid_x + (mid_x / 2.0);\n\t\thud_align_bottomright_poly[3][1] = mid_y + (mid_y / 4.0);\n\t\thud_align_bottomright_poly[3][2] = 0;\n\n\t\thud_align_bottomright_poly[4][0] = mid_x + (mid_x / 4.0);\n\t\thud_align_bottomright_poly[4][1] = mid_y + (mid_y / 2.0);\n\t\thud_align_bottomright_poly[4][2] = 0;\n\n\t\tif (IsPointInPolygon(HUD_ALIGN_POLYCOUNT_CORNER, hud_align_bottomright_poly, x - offset_x, y - offset_y))\n\t\t{\n\t\t\thud_align_current_poly = hud_align_bottomright_poly;\n\t\t\thud_align_current_polycount = HUD_ALIGN_POLYCOUNT_CORNER;\n\t\t\treturn hud_align_bottomright;\n\t\t}\n\t}\n\n\t// Right.\n\t{\n\t\thud_align_right_poly[0][0] = 2 * mid_x;\n\t\thud_align_right_poly[0][1] = mid_y + (mid_y / 2.0);\n\t\thud_align_right_poly[0][2] = 0;\n\n\t\thud_align_right_poly[1][0] = 2 * mid_x;\n\t\thud_align_right_poly[1][1] = mid_y / 2.0;\n\t\thud_align_right_poly[1][2] = 0;\n\n\t\thud_align_right_poly[2][0] = mid_x + (mid_x / 2.0);\n\t\thud_align_right_poly[2][1] = mid_y - (mid_y / 4.0);\n\t\thud_align_right_poly[2][2] = 0;\n\n\t\thud_align_right_poly[3][0] = mid_x + (mid_x / 2.0);\n\t\thud_align_right_poly[3][1] = mid_y + (mid_y / 4.0);\n\t\thud_align_right_poly[3][2] = 0;\n\n\t\tif (IsPointInPolygon(HUD_ALIGN_POLYCOUNT_EDGE, hud_align_right_poly, x - offset_x, y - offset_y))\n\t\t{\n\t\t\thud_align_current_poly = hud_align_right_poly;\n\t\t\thud_align_current_polycount = HUD_ALIGN_POLYCOUNT_EDGE;\n\t\t\treturn hud_align_right;\n\t\t}\n\t}\n\n\t// Center.\n\t{\n\t\thud_align_center_poly[0][0] = mid_x - (mid_x / 4.0);\n\t\thud_align_center_poly[0][1] = mid_y / 2.0;\n\t\thud_align_center_poly[0][2] = 0;\n\n\t\thud_align_center_poly[1][0] = mid_x / 2.0;\n\t\thud_align_center_poly[1][1] = mid_y - (mid_y / 4.0);\n\t\thud_align_center_poly[1][2] = 0;\n\n\t\thud_align_center_poly[2][0] = mid_x / 2.0;\n\t\thud_align_center_poly[2][1] = mid_y + (mid_y / 4.0);\n\t\thud_align_center_poly[2][2] = 0;\n\n\t\thud_align_center_poly[3][0] = mid_x - (mid_x / 4.0);\n\t\thud_align_center_poly[3][1] = mid_y + (mid_y / 2.0);\n\t\thud_align_center_poly[3][2] = 0;\n\n\t\thud_align_center_poly[4][0] = mid_x + (mid_x / 4.0);\n\t\thud_align_center_poly[4][1] = mid_y + (mid_y / 2.0);\n\t\thud_align_center_poly[4][2] = 0;\n\n\t\thud_align_center_poly[5][0] = mid_x + (mid_x / 2.0);\n\t\thud_align_center_poly[5][1] = mid_y + (mid_y / 4.0);\n\t\thud_align_center_poly[5][2] = 0;\n\n\t\thud_align_center_poly[6][0] = mid_x + (mid_x / 2.0);\n\t\thud_align_center_poly[6][1] = mid_y - (mid_y / 4.0);\n\t\thud_align_center_poly[6][2] = 0;\n\n\t\thud_align_center_poly[7][0] = mid_x + (mid_x / 4.0);\n\t\thud_align_center_poly[7][1] = mid_y / 2.0;\n\t\thud_align_center_poly[7][2] = 0;\n\n\t\tif (IsPointInPolygon(HUD_ALIGN_POLYCOUNT_CENTER, hud_align_center_poly, x - offset_x, y - offset_y))\n\t\t{\n\t\t\thud_align_current_poly = hud_align_center_poly;\n\t\t\thud_align_current_polycount = HUD_ALIGN_POLYCOUNT_CENTER;\n\t\t\treturn hud_align_center;\n\t\t}\n\t}\n\n\t// Default to center.\n\thud_align_current_poly = hud_align_center_poly;\n\thud_align_current_polycount = HUD_ALIGN_POLYCOUNT_CENTER;\n\treturn hud_align_center;\n}\n\n//\n// Finds the next child of the specified HUD element.\n//\nstatic hud_t *HUD_Editor_FindNextChild(hud_t *hud_element)\n{\n\tstatic hud_t\t*hud_it = NULL;\n\tstatic hud_t\t*parent = NULL;\n\thud_t\t\t\t*hud_result = NULL;\n\n\t// Reset everything if we're given a NULL argument.\n\tif(!hud_element)\n\t{\n\t\thud_it = NULL;\n\t\tparent = NULL;\n\t\treturn NULL;\n\t}\n\n\t// There's a new parent so start searching from the beginning.\n\tif(!parent || strcmp(parent->name, hud_element->name))\n\t{\n\t\tparent = hud_element;\n\t\thud_it = hud_huds;\n\t}\n\n\twhile(hud_it)\n\t{\n\t\t// Check if this HUD is placed at the parent, if so we've found a child.\n\t\tif(hud_it->place_hud && !strcmp(hud_it->place_hud->name, parent->name))\n\t\t{\n\t\t\thud_result = hud_it;\n\t\t\thud_it = hud_it->next;\n\t\t\treturn hud_result;\n\t\t}\n\n\t\thud_it = hud_it->next;\n\t}\n\n\tparent = NULL;\n\n\t// No children found.\n\treturn NULL;\n}\n\n//\n// Moves a HUD element.\n//\nstatic void HUD_Editor_Move(float dx, float dy, hud_t *hud_element)\n{\n\tif(!hud_element)\n\t{\n\t\treturn;\n\t}\n\n\tCvar_SetValue(hud_element->pos_x, hud_element->pos_x->value + dx);\n\tCvar_SetValue(hud_element->pos_y, hud_element->pos_y->value + dy);\n}\n\n//\n// Draw the current alignment.\n//\nstatic void HUD_Editor_DrawAlignment(hud_t *hud_parent)\n{\n\tif(hud_parent)\n\t{\n\t\t//Draw_Polygon(hud_parent->lx, hud_parent->ly, hud_align_current_poly, hud_align_current_polycount, true, RGBA_TO_COLOR(255, 255, 0, 50));\n\t\tDraw_Polygon(hud_parent->lx, hud_parent->ly, hud_align_current_poly, hud_align_current_polycount, true, 255, 255, 0, 50);\n\t}\n\telse\n\t{\n\t\t//Draw_Polygon(0, 0, hud_align_current_poly, hud_align_current_polycount, true, RGBA_TO_COLOR(255, 255, 0, 50));\n\t\tDraw_Polygon(0, 0, hud_align_current_poly, hud_align_current_polycount, true, 255, 255, 0, 50);\n\t}\n}\n\ntypedef struct hud_resize_handle_s\n{\n\tint x;\n\tint y;\n\tint width;\n\tint height;\n} hud_resize_handle_t;\n\n#define HUD_RESIZEHANDLE_THICKNESS\t\t5\n#define HUD_RESIZEHANDLE_SIZEFACTOR\t\t0.3\n#define HUD_RESIZEHANDLE_COUNT\t\t\t8\n#define HUD_RESIZE_MAGICSCALE\t\t\t200 // HACK : This seems to work nice though...\n\n#define HUD_RESIZEHANDLE_NONE\t\t\t-1\n#define HUD_RESIZEHANDLE_TOPLEFT\t\t0\n#define HUD_RESIZEHANDLE_TOP\t\t\t1\n#define HUD_RESIZEHANDLE_TOPRIGHT\t\t2\n#define HUD_RESIZEHANDLE_RIGHT\t\t\t3\n#define HUD_RESIZEHANDLE_BOTTOMRIGHT\t4\n#define HUD_RESIZEHANDLE_BOTTOM\t\t\t5\n#define HUD_RESIZEHANDLE_BOTTOMLEFT\t\t6\n#define HUD_RESIZEHANDLE_LEFT\t\t\t7\n\n//\n// Resizes the height/width of a HUD element by a delta size and alignment.\n//\nstatic void HUD_Editor_ResizeDelta(cvar_t *size, float delta_size, hud_alignmode_t alignment)\n{\n\tif(!size)\n\t{\n\t\treturn;\n\t}\n\n\tswitch(alignment)\n\t{\n\t\tcase hud_align_right :\n\t\tcase hud_align_bottom :\n\t\t\tCvar_SetValue(size, size->value + delta_size);\n\t\t\tbreak;\n\t\tcase hud_align_left :\n\t\tcase hud_align_top :\n\t\t\tCvar_SetValue(size, size->value - delta_size);\n\t\t\tbreak;\n\t\t// unhandled\n\t\tcase hud_align_center:\n\t\tcase hud_align_topleft:\n\t\tcase hud_align_bottomleft:\n\t\tcase hud_align_bottomright:\n\t\tcase hud_align_topright:\n\t\tcase hud_align_consoleleft:\n\t\tcase hud_align_consoleright:\n\t\tcase hud_align_console:\n\t\t\tbreak;\n\t}\n}\n\n//\n// Scales a HUD element.\n//\nstatic void HUD_EditorScaleDelta(cvar_t *scale, float delta_scale, hud_alignmode_t alignment)\n{\n\tif(!scale)\n\t{\n\t\treturn;\n\t}\n\n\tswitch(alignment)\n\t{\n\t\tcase hud_align_topleft :\n\t\tcase hud_align_bottomleft :\n\t\t\tCvar_SetValue(scale, scale->value - delta_scale);\n\t\t\tbreak;\n\t\tcase hud_align_topright :\n\t\tcase hud_align_bottomright :\n\t\t\tCvar_SetValue(scale, scale->value + delta_scale);\n\t\t\tbreak;\n\t\t// unhandled\n\t\tcase hud_align_center:\n\t\tcase hud_align_top:\n\t\tcase hud_align_left:\n\t\tcase hud_align_bottom:\n\t\tcase hud_align_right:\n\t\tcase hud_align_consoleleft:\n\t\tcase hud_align_consoleright:\n\t\tcase hud_align_console:\n\t\t\tbreak;\n\t}\n\n\tif(scale->value < 0)\n\t{\n\t\tCvar_SetValue(scale, 0);\n\t}\n}\n\n//\n// Checks if the HUD element we're hovering has any dimension variables\n// and handles resizing for it if it does by drawing resize handles\n// that the user can click.\n//\nstatic qbool HUD_Editor_Resizing(hud_t *hud_hover)\n{\n\textern float mouse_x, mouse_y;\t\t// in_win.c, delta mouse.\n\tint i\t\t\t\t\t= 0;\n\tqbool found_handle\t\t= false;\t// Did we find any handle under the cursor?\n\tstatic cvar_t *width\t= NULL;\n\tstatic cvar_t *height\t= NULL;\n\tstatic cvar_t *scale\t= NULL;\n\thud_resize_handle_t *resize_handles[HUD_RESIZEHANDLE_COUNT];\n\tstatic int last_resize_handle = HUD_RESIZEHANDLE_NONE;\n\n\t// Is this mode turned on?\n\tif(!hud_editor_allowresize->value)\n\t{\n\t\treturn false;\n\t}\n\n\t// Select a HUD if it hasn't already been done.\n\tif(hud_hover && hud_editor_mode == hud_editmode_move_resize\n\t\t&& !selected_hud && hud_hover)\n\t{\n\t\tselected_hud = hud_hover;\n\t\treturn true;\n\t}\n\n\tmemset(resize_handles, 0, sizeof(resize_handles));\n\n\t// Try getting any available dimension variables\n\t// that are available for this HUD element, these\n\t// aren't mandatory for all HUD elements, so only\n\t// some will have them.\n\tif(hud_hover)\n\t{\n\t\twidth\t= HUD_FindVar(hud_hover, \"width\");\n\t\theight\t= HUD_FindVar(hud_hover, \"height\");\n\t\tscale\t= HUD_FindVar(hud_hover, \"scale\");\n\n\t\tif(width)\n\t\t{\n\t\t\t// Right & left.\n\t\t\tstatic hud_resize_handle_t right;\n\t\t\tstatic hud_resize_handle_t left;\n\n\t\t\tright.width\t\t= HUD_RESIZEHANDLE_THICKNESS;\n\t\t\tright.height\t= hud_hover->lh * HUD_RESIZEHANDLE_SIZEFACTOR;\n\t\t\tright.x\t\t\t= hud_hover->lw - right.width;\n\t\t\tright.y\t\t\t= (hud_hover->lh - right.height) / 2;\n\t\t\tresize_handles[HUD_RESIZEHANDLE_RIGHT] = &right;\n\n\t\t\tleft.width\t\t= HUD_RESIZEHANDLE_THICKNESS;\n\t\t\tleft.height\t\t= hud_hover->lh * HUD_RESIZEHANDLE_SIZEFACTOR;\n\t\t\tleft.x\t\t\t= 0;\n\t\t\tleft.y\t\t\t= (hud_hover->lh - left.height) / 2;\n\t\t\tresize_handles[HUD_RESIZEHANDLE_LEFT] = &left;\n\t\t}\n\n\t\tif(height)\n\t\t{\n\t\t\t// Top & bottom\n\t\t\tstatic hud_resize_handle_t top;\n\t\t\tstatic hud_resize_handle_t bottom;\n\n\t\t\ttop.width\t\t= hud_hover->lw * HUD_RESIZEHANDLE_SIZEFACTOR;\n\t\t\ttop.height\t\t= HUD_RESIZEHANDLE_THICKNESS;\n\t\t\ttop.x\t\t\t= (hud_hover->lw - top.width) / 2;\n\t\t\ttop.y\t\t\t= 0;\n\t\t\tresize_handles[HUD_RESIZEHANDLE_TOP] = &top;\n\n\t\t\tbottom.width\t\t= hud_hover->lw * HUD_RESIZEHANDLE_SIZEFACTOR;\n\t\t\tbottom.height\t\t= HUD_RESIZEHANDLE_THICKNESS;\n\t\t\tbottom.x\t\t\t= (hud_hover->lw - bottom.width) / 2;\n\t\t\tbottom.y\t\t\t= hud_hover->lh - bottom.height;\n\t\t\tresize_handles[HUD_RESIZEHANDLE_BOTTOM] = &bottom;\n\t\t}\n\n\t\tif(scale)\n\t\t{\n\t\t\t// Top left, top right, bottom left & bottom right.\n\t\t\tstatic hud_resize_handle_t topleft;\n\t\t\tstatic hud_resize_handle_t bottomleft;\n\t\t\tstatic hud_resize_handle_t topright;\n\t\t\tstatic hud_resize_handle_t bottomright;\n\n\t\t\ttopleft.width\t\t= HUD_RESIZEHANDLE_THICKNESS;\n\t\t\ttopleft.height\t\t= HUD_RESIZEHANDLE_THICKNESS;\n\t\t\ttopleft.x\t\t\t= 0;\n\t\t\ttopleft.y\t\t\t= 0;\n\t\t\tresize_handles[HUD_RESIZEHANDLE_TOPLEFT] = &topleft;\n\n\t\t\tbottomleft.width\t= HUD_RESIZEHANDLE_THICKNESS;\n\t\t\tbottomleft.height\t= HUD_RESIZEHANDLE_THICKNESS;\n\t\t\tbottomleft.x\t\t= 0;\n\t\t\tbottomleft.y\t\t= hud_hover->lh - bottomleft.height;\n\t\t\tresize_handles[HUD_RESIZEHANDLE_BOTTOMLEFT] = &bottomleft;\n\n\t\t\ttopright.width\t\t= HUD_RESIZEHANDLE_THICKNESS;\n\t\t\ttopright.height\t\t= HUD_RESIZEHANDLE_THICKNESS;\n\t\t\ttopright.x\t\t\t= hud_hover->lw - topright.width;\n\t\t\ttopright.y\t\t\t= 0;\n\t\t\tresize_handles[HUD_RESIZEHANDLE_TOPRIGHT] = &topright;\n\n\t\t\tbottomright.width\t= HUD_RESIZEHANDLE_THICKNESS;\n\t\t\tbottomright.height\t= HUD_RESIZEHANDLE_THICKNESS;\n\t\t\tbottomright.x\t\t= hud_hover->lw - bottomright.width;\n\t\t\tbottomright.y\t\t= hud_hover->lh - bottomright.height;\n\t\t\tresize_handles[HUD_RESIZEHANDLE_BOTTOMRIGHT] = &bottomright;\n\t\t}\n\t}\n\n\tfor(i = 0; i < HUD_RESIZEHANDLE_COUNT; i++)\n\t{\n\t\t// Non existant for this HUD element.\n\t\tif(!resize_handles[i])\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tif(hud_editor_mode == hud_editmode_move_resize)\n\t\t{\n\t\t\t// We're in resize mode, so check if we're clicking any of the\n\t\t\t// resize handles.\n\n\t\t\tif((selected_hud && (last_resize_handle == i)) ||\n\t\t\t\t  (hud_mouse_x >= (selected_hud->lx + resize_handles[i]->x)\n\t\t\t\t&& (hud_mouse_x <= (selected_hud->lx + resize_handles[i]->x + resize_handles[i]->width))\n\t\t\t\t&& (hud_mouse_y >= (selected_hud->ly + resize_handles[i]->y))\n\t\t\t\t&& (hud_mouse_y <= (selected_hud->ly + resize_handles[i]->y + resize_handles[i]->height))))\n\t\t\t{\n\t\t\t\t// Keep track of which resize handle we're grabbing\n\t\t\t\t// so that it doesn't matter if the mouse \"slips\" outside\n\t\t\t\t// it's bounding box as long as the mouse is still pressed.\n\t\t\t\tif(last_resize_handle < 0 || last_resize_handle != i)\n\t\t\t\t{\n\t\t\t\t\tlast_resize_handle = i;\n\t\t\t\t}\n\n\t\t\t\tfound_handle = true;\n\n\t\t\t\t// Draw the resize handle highlighted.\n\t\t\t\tDraw_AlphaFillRGB(\n\t\t\t\t\tselected_hud->lx + resize_handles[last_resize_handle]->x,\n\t\t\t\t\tselected_hud->ly + resize_handles[last_resize_handle]->y,\n\t\t\t\t\tresize_handles[last_resize_handle]->width,\n\t\t\t\t\tresize_handles[last_resize_handle]->height,\n\t\t\t\t\t255, 255, 0, 125);\n\n\t\t\t\t// Check which resize handle that has been selected\n\t\t\t\t// and resize the HUD element accordingly.\n\t\t\t\tswitch(i)\n\t\t\t\t{\n\t\t\t\t\tcase HUD_RESIZEHANDLE_RIGHT :\n\t\t\t\t\t\tHUD_Editor_ResizeDelta(width, mouse_x, hud_align_right);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase HUD_RESIZEHANDLE_LEFT :\n\t\t\t\t\t\tHUD_Editor_ResizeDelta(width, mouse_x, hud_align_left);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase HUD_RESIZEHANDLE_TOP :\n\t\t\t\t\t\tHUD_Editor_ResizeDelta(height, mouse_y, hud_align_top);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase HUD_RESIZEHANDLE_BOTTOM :\n\t\t\t\t\t\tHUD_Editor_ResizeDelta(height, mouse_y, hud_align_bottom);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase HUD_RESIZEHANDLE_TOPRIGHT :\n\t\t\t\t\t\t// HACK : Dividing by 200 seems to work fine, but this could probably be done a lot nicer.\n\t\t\t\t\t\tHUD_EditorScaleDelta(scale, (mouse_x - mouse_y) / HUD_RESIZE_MAGICSCALE, hud_align_topright);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase HUD_RESIZEHANDLE_BOTTOMRIGHT :\n\t\t\t\t\t\tHUD_EditorScaleDelta(scale, (mouse_x + mouse_y) / HUD_RESIZE_MAGICSCALE, hud_align_bottomright);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase HUD_RESIZEHANDLE_TOPLEFT :\n\t\t\t\t\t\tHUD_EditorScaleDelta(scale, (mouse_x + mouse_y) / HUD_RESIZE_MAGICSCALE, hud_align_topleft);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase HUD_RESIZEHANDLE_BOTTOMLEFT :\n\t\t\t\t\t\tHUD_EditorScaleDelta(scale, (mouse_x - mouse_y) / HUD_RESIZE_MAGICSCALE, hud_align_bottomleft);\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\t// Recalculate all HUD elements.\n\t\t\t\tHUD_Recalculate();\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\telse if(hud_editor_prevmode == hud_editmode_move_resize)\n\t\t{\n\t\t\t// We left resize mode.\n\t\t\tselected_hud = NULL;\n\t\t\tlast_resize_handle = HUD_RESIZEHANDLE_NONE;\n\t\t}\n\t\telse if(hud_hover)\n\t\t{\n\t\t\t// If we're hovering a HUD, always draw all it's resize handles.\n\t\t\tif((hud_mouse_x >= (hud_hover->lx + resize_handles[i]->x)\n\t\t\t\t&& hud_mouse_x <= (hud_hover->lx + resize_handles[i]->x + resize_handles[i]->width)\n\t\t\t\t&& hud_mouse_y >= (hud_hover->ly + resize_handles[i]->y)\n\t\t\t\t&& hud_mouse_y <= (hud_hover->ly + resize_handles[i]->y + resize_handles[i]->height)))\n\t\t\t{\n\t\t\t\t// Highlight the resize handle under the cursor.\n\t\t\t\tDraw_AlphaFillRGB(hud_hover->lx + resize_handles[i]->x, hud_hover->ly + resize_handles[i]->y,\n\t\t\t\t\tresize_handles[i]->width, resize_handles[i]->height,\n\t\t\t\t\t255, 255, 0, 125);\n\n\t\t\t\t// Set the resize cursor icon since we're hovering a resize handle.\n\t\t\t\tscr_cursor_icon = hud_editor_resize_icon;\n\t\t\t\tfound_handle = true;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t// Normal resize handle.\n\t\t\t\tDraw_AlphaFillRGB(hud_hover->lx + resize_handles[i]->x, hud_hover->ly + resize_handles[i]->y,\n\t\t\t\tresize_handles[i]->width, resize_handles[i]->height,\n\t\t\t\t255, 255, 0, 50);\n\t\t\t}\n\t\t}\n\t}\n\n\t// We didn't hover any resize handle so show no icon.\n\tif(!found_handle)\n\t{\n\t\tscr_cursor_icon = NULL;\n\t}\n\n\t// We didn't perform any action, so let others try.\n\treturn false;\n}\n\nstatic qbool hud_editor_locked_axis_is_x = true;\t// Are we locking X- or Y-axis movement?\nstatic qbool hud_editor_lockaxis_found= false;\t\t// Have we found what axist to lock on to?\nstatic qbool hud_editor_finding_lockaxis = false;\t// Are we in the process of finding the lock axis?\n\n//\n// Check if we're supposed to be moving anything.\n//\nstatic qbool HUD_Editor_Moving(hud_t *hud_hover)\n{\n\t// Mouse delta (in_win.c)\n\textern float mouse_x, mouse_y;\n\n\t// Is this mode allowed?\n\tif(!hud_editor_allowmove->value)\n\t{\n\t\treturn false;\n\t}\n\n\t// Set the move cursor icon if we're hovering a HUD element\n\t// unless it's not already set (resize may have set it if\n\t// a resize handle is being hovered).\n\tif(hud_hover && !scr_cursor_icon)\n\t{\n\t\tscr_cursor_icon = hud_editor_move_icon;\n\t}\n\telse if(!hud_hover)\n\t{\n\t\t// Not hovering anything so show no cursor.\n\t\tscr_cursor_icon = NULL;\n\t}\n\n\t// Left mousebutton down = lets move it !\n\tif (hud_editor_mode == hud_editmode_move_resize || hud_editor_mode == hud_editmode_move_lockedaxis)\n\t{\n\t\t// If we just entered movement mode, nothing is selected\n\t\t// so select the hud we're hovering to start with.\n\t\tif(!selected_hud && hud_hover)\n\t\t{\n\t\t\tselected_hud = hud_hover;\n\t\t\treturn true;\n\t\t}\n\n\t\tif(selected_hud)\n\t\t{\n\t\t\t// Move using the mouse delta instead of the absolute\n\t\t\t// mouse cursor coordinates on the screen.\n\n\t\t\t// Lock the movement to the axis that the user starts\n\t\t\t// dragging the HUD element along if we're in locked-axis mode.\n\t\t\tif(hud_editor_mode == hud_editmode_move_lockedaxis)\n\t\t\t{\n\t\t\t\t// We haven't found the axis.\n\t\t\t\tif(!hud_editor_lockaxis_found)\n\t\t\t\t{\n\t\t\t\t\tstatic float last_mouse_x = 0.0;\n\t\t\t\t\tstatic float last_mouse_y = 0.0;\n\n\t\t\t\t\tif(!hud_editor_finding_lockaxis)\n\t\t\t\t\t{\n\t\t\t\t\t\t// Start trying to find the lock axis.\n\t\t\t\t\t\thud_editor_finding_lockaxis = true;\n\t\t\t\t\t\tlast_mouse_x = 0;\n\t\t\t\t\t\tlast_mouse_y = 0;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\t// Get the delta of the mouse movement from when the\n\t\t\t\t\t\t// user clicked the mouse button and started dragging.\n\t\t\t\t\t\tfloat mouse_delta_x = fabs(last_mouse_x - mouse_x);\n\t\t\t\t\t\tfloat mouse_delta_y = fabs(last_mouse_y - mouse_y);\n\n\t\t\t\t\t\t// Increment the mouse movement since last frame.\n\t\t\t\t\t\tlast_mouse_x += mouse_x;\n\t\t\t\t\t\tlast_mouse_y += mouse_y;\n\n\t\t\t\t\t\t// If the mouse has moved more than 5 pixels in any\n\t\t\t\t\t\t// direction we decide to lock onto that axis.\n\t\t\t\t\t\tif(mouse_delta_x > 5 || mouse_delta_y > 5)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\thud_editor_locked_axis_is_x = (mouse_delta_x > mouse_delta_y);\n\t\t\t\t\t\t\thud_editor_lockaxis_found = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Move while locked to the axis.\n\t\t\t\tif(hud_editor_locked_axis_is_x)\n\t\t\t\t{\n\t\t\t\t\t// Move only along X-axis.\n\t\t\t\t\tHUD_Editor_Move(mouse_x, 0, hud_hover);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t// Move only along Y-axis.\n\t\t\t\t\tHUD_Editor_Move(0, mouse_y, hud_hover);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t// Ordinary move.\n\t\t\t\tHUD_Editor_Move(mouse_x, mouse_y, hud_hover);\n\t\t\t}\n\n\t\t\tHUD_Recalculate();\n\t\t\treturn true;\n\t\t}\n\t}\n\telse if((hud_editor_prevmode == hud_editmode_move_resize && hud_editor_mode != hud_editmode_move_lockedaxis)\n\t\t || (hud_editor_prevmode == hud_editmode_move_lockedaxis && hud_editor_mode != hud_editmode_move_resize))\n\t{\n\t\t// Only deselect if we're going from a move mode to a non-move mode (Such as align/place).\n\n\t\t// We've left move mode, so deselect.\n\t\tselected_hud = NULL;\n\t\thud_editor_lockaxis_found = false;\n\t\thud_editor_finding_lockaxis = false;\n\t}\n\n\t// We haven't handled the input.\n\treturn false;\n}\n\n//\n// Handles input from mouse if in alignment mode.\n//\nstatic qbool HUD_Editor_Aligning(hud_t *hud_hover)\n{\n\t// Is this mode allowed?\n\tif(!hud_editor_allowalign->value)\n\t{\n\t\treturn false;\n\t}\n\n\tif(hud_editor_mode == hud_editmode_align)\n\t{\n\t\t// If we just entered alignment mode, nothing is selected\n\t\t// so select the hud we're hovering to start with.\n\t\tif(!selected_hud && hud_hover)\n\t\t{\n\t\t\tselected_hud = hud_hover;\n\t\t\treturn true;\n\t\t}\n\n\t\t// Set the align icon for the cursor.\n\t\tscr_cursor_icon = hud_editor_align_icon;\n\n\t\t// We have something selected so show some visual\n\t\t// feedback for when aligning to the user.\n\t\tif(selected_hud)\n\t\t{\n\t\t\tif(selected_hud->place_hud)\n\t\t\t{\n\t\t\t\t// Placed at another HUD, so align onto that.\n\t\t\t\thud_alignmode = HUD_Editor_GetAlignment(hud_mouse_x, hud_mouse_y, selected_hud->place_hud);\n\t\t\t\tHUD_Editor_DrawAlignment(selected_hud->place_hud);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t// Placed on the screen.\n\t\t\t\thud_alignmode = HUD_Editor_GetAlignment(hud_mouse_x, hud_mouse_y, NULL);\n\t\t\t\tHUD_Editor_DrawAlignment(NULL);\n\t\t\t}\n\t\t}\n\t}\n\telse if(hud_editor_prevmode == hud_editmode_align && isAltDown())\n\t{\n\t\t// The user just released the mousebutton but is still holding\n\t\t// down ALT. If the user releases ALT before the mouse button\n\t\t// the operation will be cancelled. So commit the users actions.\n\n\t\t// We must have something to align.\n\t\tif(selected_hud)\n\t\t{\n\t\t\t// Reset position.\n\t\t\tCvar_Set(selected_hud->pos_x, \"0\");\n\t\t\tCvar_Set(selected_hud->pos_y, \"0\");\n\n\t\t\t// Align to the area the mouse is placed over (previously set in hud_alignmode).\n\t\t\tCbuf_AddText(va(\"align %s %s\\n\", selected_hud->name, HUD_Editor_GetAlignmentString(hud_alignmode)));\n\t\t\tHUD_Recalculate();\n\n\t\t\t// Free selection.\n\t\t\tselected_hud = NULL;\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n\n//\n// Handles feedback/commiting of actions when in placement mode.\n//\nstatic qbool HUD_Editor_Placing(hud_t *hud_hover)\n{\n\t// Is this mode allowed?\n\tif(!hud_editor_allowplace->value)\n\t{\n\t\treturn false;\n\t}\n\n\t// Show the place icon at the cursor if ctrl is pressed\n\t// while hovering a HUD element.\n\tif(hud_hover && isCtrlDown())\n\t{\n\t\t// Set the place cursor icon.\n\t\tscr_cursor_icon = hud_editor_place_icon;\n\t}\n\telse if(!hud_hover)\n\t{\n\t\tscr_cursor_icon = NULL;\n\t}\n\n\tif(hud_editor_mode == hud_editmode_place)\n\t{\n\t\t// If we just entered placement mode, nothing is selected\n\t\t// so select the hud we're hovering to start with.\n\t\tif(!selected_hud && hud_hover)\n\t\t{\n\t\t\tselected_hud = hud_hover;\n\t\t\treturn true;\n\t\t}\n\n\t\t// Set the place cursor icon.\n\t\tscr_cursor_icon = hud_editor_place_icon;\n\n\t\tif(selected_hud)\n\t\t{\n\t\t\t// Find the center of the selected HUD so we know where\n\t\t\t// to draw the line from.\n\n\t\t\tif(hud_hover)\n\t\t\t{\n\t\t\t\t// We're trying to place the HUD on itself or on the HUD it's already placed at.\n\t\t\t\tif(hud_hover == selected_hud || (selected_hud->place_hud && selected_hud->place_hud == hud_hover))\n\t\t\t\t{\n\t\t\t\t\t// Red \"not allowed\".\n\t\t\t\t\tDraw_AlphaRectangleRGB(hud_hover->lx, hud_hover->ly, hud_hover->lw, hud_hover->lh, 1, true, 255, 0, 0, 25);\n\t\t\t\t\tDraw_AlphaRectangleRGB(hud_hover->lx, hud_hover->ly, hud_hover->lw, hud_hover->lh, 1, false, 255, 0, 0, 255);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t// Green \"allowed\" placement.\n\t\t\t\t\tDraw_AlphaRectangleRGB(hud_hover->lx, hud_hover->ly, hud_hover->lw, hud_hover->lh, 1, true, 0, 255, 0, 25);\n\t\t\t\t}\n\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\t// Placement at the screen (We don't care about stuff like IFREE, HBAR, SBAR and so on).\n\t\t\tif(!strcmp(selected_hud->place->string, \"screen\"))\n\t\t\t{\n\t\t\t\t// Not allowed, already placed there.\n\t\t\t\tDraw_AlphaRectangleRGB(0, 0, vid.width, vid.height, 1, true, 255, 0, 0, 25);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t// Allowed.\n\t\t\t\tDraw_AlphaRectangleRGB(0, 0, vid.width, vid.height, 1, true, 0, 255, 0, 25);\n\t\t\t}\n\t\t}\n\t}\n\telse if(hud_editor_prevmode == hud_editmode_place && isCtrlDown())\n\t{\n\t\t// We've just exited placement mode, but control is still pressed,\n\t\t// that means we should place the selected_hud.\n\t\t// (If you release ctrl before you release Mouse 1, you cancel the place operation).\n\n\t\tif(selected_hud)\n\t\t{\n\t\t\t// If we're hovering a HUD place it there.\n\t\t\tif(hud_hover)\n\t\t\t{\n\t\t\t\t// We're trying to place the HUD on itself or on the HUD it's already placed at so do nothing.\n\t\t\t\tif(hud_hover == selected_hud || (selected_hud->place_hud && selected_hud->place_hud == hud_hover))\n\t\t\t\t{\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\n\t\t\t\t// Place at other HUD.\n\t\t\t\tCvar_Set(selected_hud->align_x, \"center\");\n\t\t\t\tCvar_Set(selected_hud->align_y, \"center\");\n\t\t\t\tCvar_Set(selected_hud->pos_x, \"0\");\n\t\t\t\tCvar_Set(selected_hud->pos_y, \"0\");\n\t\t\t\tCvar_Set(selected_hud->place, hud_hover->name);\n\n\t\t\t\t// Make sure the child has a higher order.\n\t\t\t\tif((int)selected_hud->order->value <= (int)hud_hover->order->value)\n\t\t\t\t{\n\t\t\t\t\tCvar_SetValue(selected_hud->order, hud_hover->order->value + 1);\n\t\t\t\t}\n\n\t\t\t\tHUD_Recalculate();\n\n\t\t\t\t// Free selection.\n\t\t\t\tselected_hud = NULL;\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t// Mouse button was released on on the \"screen\".\n\n\t\t\t\tif(!strcmp(selected_hud->place->string, \"screen\"))\n\t\t\t\t{\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\n\t\t\t\t// Place at other HUD.\n\t\t\t\tCvar_Set(selected_hud->align_x, \"center\");\n\t\t\t\tCvar_Set(selected_hud->align_y, \"center\");\n\t\t\t\tCvar_Set(selected_hud->pos_x, \"0\");\n\t\t\t\tCvar_Set(selected_hud->pos_y, \"0\");\n\t\t\t\tCvar_Set(selected_hud->place, \"screen\");\n\t\t\t\tHUD_Recalculate();\n\n\t\t\t\t// Free selection.\n\t\t\t\tselected_hud = NULL;\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t}\n\n\t// We weren't placing something so check other states also.\n\treturn false;\n}\n\n// =============================================================================\n//\t\t\t\t\t\t\tHUD Editor Grep Handles.\n// =============================================================================\n// These show up when a HUD element is moved completly offscreen (by accident\n// most likely), so that the user can still grab it and move it back onto the\n// screen again.\n// =============================================================================\n\n//\n// Finds a HUD grephandle (if the hud element has gone offscreen).\n//\nstatic hud_grephandle_t *HUD_Editor_FindGrep(hud_t *hud_element)\n{\n\thud_grephandle_t *greps_it = NULL;\n\tgreps_it = hud_greps;\n\n\twhile(greps_it)\n\t{\n\t\tif(!strcmp(greps_it->hud->name, hud_element->name))\n\t\t{\n\t\t\treturn greps_it;\n\t\t}\n\n\t\tgreps_it = greps_it->next;\n\t}\n\n\treturn NULL;\n}\n\n//\n// Returns an \"offscreen\" arrow in the correct direction based on where\n// offscreen the HUD element is located.\n//\nstatic char *HUD_Editor_GetGrepArrow(hud_grephandle_t *grep)\n{\n\tswitch(grep->pos)\n\t{\n\t\tcase pos_top :\t\treturn \"/\\\\\";\n\t\tcase pos_bottom :\treturn \"\\\\/\";\n\t\tcase pos_left :\t\treturn \"<<<\";\n\t\tcase pos_right :\treturn \">>>\";\n\t\tdefault :\t\t\treturn \"\";\n\t}\n}\n\n//\n// Draws the grephandles.\n//\nstatic void HUD_Editor_DrawGreps()\n{\n\tclrinfo_t color, highlight;\n\thud_grephandle_t *greps_it = NULL;\n\tgreps_it = hud_greps;\n\n\t// Highlight color (yellow).\n\tVector4Set(highlight.c, 0, 255, 0, 255);\n\n\t// Orange.\n\tVector4Set(color.c, 255, 150, 0, 255);\n\n\twhile(greps_it)\n\t{\n\t\tDraw_ColoredString3(greps_it->x, greps_it->y,\n\t\t\tva(\"%s %s\", HUD_Editor_GetGrepArrow(greps_it), greps_it->hud->name),\n\t\t\t(greps_it->highlighted ? &highlight : &color), 1, 0);\n\n\t\tgreps_it = greps_it->next;\n\t}\n}\n\n//\n// Get's the position offscreen for a HUD element\n// left/right/top/bottom or visible if it's not offscreen.\n//\nstatic hud_greppos_t HUD_Editor_GetHudGrepPosition(hud_t *hud)\n{\n\tif(hud->lx + hud->lw <= 0)\n\t{\n\t\treturn pos_left;\n\t}\n\telse if(hud->lx >= (signed)vid.width)\n\t{\n\t\treturn pos_right;\n\t}\n\telse if(hud->ly + hud->lh <= 0)\n\t{\n\t\treturn pos_top;\n\t}\n\telse if(hud->ly >= (signed)vid.height)\n\t{\n\t\treturn pos_bottom;\n\t}\n\n\treturn pos_visible;\n}\n\n//\n// Positions a grephandle based on it's position and where the HUD element\n// it's associated with is located.\n//\nstatic void HUD_Editor_PositionGrep(hud_t *hud_element, hud_grephandle_t *grep)\n{\n\t// Get the position of the grephandle.\n\tgrep->pos = HUD_Editor_GetHudGrepPosition(hud_element);\n\n\t// Position the grephandle on screen.\n\tswitch(grep->pos)\n\t{\n\t\tcase pos_top :\n\t\t\tgrep->x\t= hud_element->lx;\n\t\t\tgrep->y\t= 5;\n\t\t\tbreak;\n\t\tcase pos_bottom :\n\t\t\tgrep->x\t= hud_element->lx;\n\t\t\tgrep->y\t= vid.height - grep->height - 5;\n\t\t\tbreak;\n\t\tcase pos_left :\n\t\t\tgrep->x = 5;\n\t\t\tgrep->y = hud_element->ly;\n\t\t\tbreak;\n\t\tcase pos_right :\n\t\t\tgrep->x = vid.width - grep->width - 5;\n\t\t\tgrep->y = hud_element->ly;\n\t\t\tbreak;\n\t\tdefault :\n\t\t\tgrep->x = hud_element->lx;\n\t\t\tgrep->y = hud_element->ly;\n\t\t\tbreak;\n\t}\n}\n\n//\n// Creates a new grephandle and associates it with a HUD element that is offscreen.\n//\nstatic hud_grephandle_t *HUD_Editor_CreateGrep(hud_t *hud_element)\n{\n\thud_grephandle_t *grep = NULL;\n\n\tgrep\t\t\t= Q_malloc(sizeof(hud_grephandle_t));\n\tmemset(grep, 0, sizeof(*grep));\n\n\tgrep->width\t\t= 8 * (4 + strlen(hud_element->name));\n\tgrep->height\t= 8;\n\tgrep->hud\t\t= hud_element;\n\tgrep->previous\t= NULL;\n\tgrep->next\t\t= hud_greps;\n\tif(hud_greps)\n\t{\n\t\thud_greps->previous = grep;\n\t}\n\n\thud_greps = grep;\n\n\tHUD_Editor_PositionGrep(hud_element, grep);\n\n\treturn grep;\n}\n\n//\n// Destroys a grephandle (called if it's no longer offscreen).\n//\nstatic void HUD_Editor_DestroyGrep(hud_grephandle_t *grep)\n{\n\t// Already destroyed.\n\tif(!grep)\n\t{\n\t\treturn;\n\t}\n\n\t// Relink any neighbours in the list.\n\tif(grep->next && grep->previous)\n\t{\n\t\tgrep->previous->next = grep->next;\n\t\tgrep->next->previous = grep->previous;\n\t}\n\telse if(grep->next)\n\t{\n\t\tgrep->next->previous = NULL;\n\t\thud_greps = grep->next;\n\t}\n\telse if(grep->previous)\n\t{\n\t\tgrep->previous->next = NULL;\n\t}\n\telse\n\t{\n\t\thud_greps = NULL;\n\t}\n\n\tmemset(grep, 0, sizeof(*grep));\n\n\tQ_free(grep);\n}\n\n//\n// Finds a HUD element associated with the grephandle under the mouse cursor.\n//\nstatic hud_t *HUD_Editor_FindHudByGrep()\n{\n\thud_grephandle_t *greps_it = NULL;\n\tgreps_it = hud_greps;\n\n\twhile(greps_it)\n\t{\n\t\tif (greps_it->hud\n\t\t\t&& hud_mouse_x >= greps_it->x\n\t\t\t&& hud_mouse_x <= (greps_it->x + greps_it->width)\n\t\t\t&& hud_mouse_y >= greps_it->y\n\t\t\t&& hud_mouse_y <= (greps_it->y + greps_it->height))\n\t\t{\n\t\t\tgreps_it->highlighted = true;\n\t\t\treturn greps_it->hud;\n\t\t}\n\n\t\tgreps_it->highlighted = false;\n\n\t\tgreps_it = greps_it->next;\n\t}\n\n\treturn NULL;\n}\n\n//\n// Draws the outline of the visible HUD elements.\n//\nstatic void HUD_Editor_DrawOutlines(void)\n{\n\thud_t *temp_hud\t= hud_huds;\n\n\tif(!temp_hud || !hud_editor_showoutlines)\n\t{\n\t\treturn;\n\t}\n\n\twhile(temp_hud->next)\n\t{\n\t\t// Check if the item is visible.\n\t\tif (!temp_hud->show->value || (temp_hud->place_hud && !temp_hud->place_hud->show->value))\n\t\t{\n\t\t\ttemp_hud = temp_hud->next;\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Draw an outline for all hud elements (faint).\n\t\tDraw_AlphaRectangleRGB(temp_hud->lx, temp_hud->ly, temp_hud->lw, temp_hud->lh, 1, false, 0, 255, 0, 25);\n\n\t\ttemp_hud = temp_hud->next;\n\t}\n}\n\n//\n// Finds if there's any HUD under the cursor and draws outlines for all HUD elements.\n//\nstatic qbool HUD_Editor_FindHudUnderCursor(hud_t **hud)\n{\n\thud_grephandle_t\t*grep\t\t= NULL;\n\thud_greppos_t\t\tpos\t\t\t= pos_visible;\n\thud_t\t\t\t\t*temp_hud\t= hud_huds;\n\tqbool\t\t\t\tfound\t\t= false;\n\n\tif(!temp_hud)\n\t{\n\t\treturn false;\n\t}\n\n\t// Check if we already had something selected since last time and was moving.\n\tif(selected_hud && (hud_editor_mode == hud_editmode_move_resize || hud_editor_mode == hud_editmode_move_lockedaxis))\n\t{\n\t\tfound = true;\n\t\t(*hud) = selected_hud;\n\t}\n\n\t// If the hover list is being showed, only look for HUDs in that\n\t// not the HUD's that are below the cursor.\n\tif(hud_editor_mode == hud_editmode_hoverlist && hud_hoverlist_count > 0)\n\t{\n\t\t(*hud) = HUD_Editor_FindHoverListSelection(hud_hoverlist);\n\t\treturn (hud != NULL);\n\t}\n\n\t// Reset the hoverlist since we might be hovering something new.\n\thud_hoverlist_count = 0;\n\thud_hoverlist = NULL;\n\n\twhile(temp_hud)\n\t{\n\t\t// Not visible.\n\t\tif (!temp_hud->show->value || (temp_hud->place_hud && !temp_hud->place_hud->show->value))\n\t\t{\n\t\t\ttemp_hud = temp_hud->next;\n\t\t\tcontinue;\n\t\t}\n\n\t\t// We found one.\n\t\tif (hud_mouse_x >= temp_hud->lx\n\t\t\t&& hud_mouse_x <= (temp_hud->lx + temp_hud->lw)\n\t\t\t&& hud_mouse_y >= temp_hud->ly\n\t\t\t&& hud_mouse_y <= (temp_hud->ly + temp_hud->lh))\n\t\t{\n\t\t\t// If we're moving/resizing something only continue checking for\n\t\t\t// more HUD elements we have the mouse over if we haven't already\n\t\t\t// found one. If we don't do this when you drag a HUD element\n\t\t\t// over another HUD element that has a greater Z-order the selection\n\t\t\t// will jump to that HUD element, and you'll start moving that instead.\n\t\t\t//\n\t\t\t// Vice versa if we would skip any HUD item after we've found one\n\t\t\t// when not already moving an item, it would mean that we could only\n\t\t\t// select HUD elements that are topmost in the Z-order, so an item\n\t\t\t// placed within another item would not be selectable.\n\t\t\tif(((hud_editor_mode == hud_editmode_move_resize || hud_editor_mode == hud_editmode_move_lockedaxis) && !found)\n\t\t\t\t|| (hud_editor_mode != hud_editmode_move_resize && hud_editor_mode != hud_editmode_move_lockedaxis))\n\t\t\t{\n\t\t\t\tfound = true;\n\t\t\t\t(*hud) = temp_hud;\n\n\t\t\t\t// Add this HUD to the list of HUDs under the cursor.\n\t\t\t\tHUD_Editor_AddHoverHud(HUD_Editor_CreateHoverHud(temp_hud));\n\t\t\t}\n\t\t}\n\n\t\t// Check if the hud element is offscreen and\n\t\t// if there's any grep handle for this hud element\n\t\t{\n\t\t\tpos = HUD_Editor_GetHudGrepPosition(temp_hud);\n\t\t\tgrep = HUD_Editor_FindGrep(temp_hud);\n\n\t\t\tif(pos != pos_visible)\n\t\t\t{\n\t\t\t\t// We didn't find any grep handle so create one.\n\t\t\t\tif(!grep)\n\t\t\t\t{\n\t\t\t\t\tgrep = HUD_Editor_CreateGrep(temp_hud);\n\t\t\t\t}\n\n\t\t\t\t// Position the grep if we got one.\n\t\t\t\tif(grep)\n\t\t\t\t{\n\t\t\t\t\tHUD_Editor_PositionGrep(temp_hud, grep);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t// The HUD element is visbile, so no need for a grep handle for it.\n\t\t\t\tHUD_Editor_DestroyGrep(grep);\n\t\t\t}\n\t\t}\n\n\t\ttemp_hud = temp_hud->next;\n\t}\n\n\t// We didn't find any HUD's under the cursor, but\n\t// what about \"grep handles\" (for offscreen HUDs).\n\tif(!found)\n\t{\n\t\ttemp_hud = HUD_Editor_FindHudByGrep();\n\n\t\tif(temp_hud)\n\t\t{\n\t\t\tfound = true;\n\t\t\t(*hud) = temp_hud;\n\t\t}\n\t}\n\n\t// Nothing found, make sure result is NULL.\n\tif (!found)\n\t{\n\t\t(*hud) = NULL;\n\t}\n\n\treturn found;\n}\n\n//\n// Returns the point a HUD is aligned to on it's parent in screen coordinates.\n//\nstatic void HUD_Editor_GetAlignmentPoint(hud_t *hud, int *x, int *y)\n{\n\textern float scr_con_current; // Console height. console.c\n\tint parent_x = 0;\n\tint parent_y = 0;\n\tint parent_w = 0;\n\tint parent_h = 0;\n\thud_alignmode_t alignmode = HUD_Editor_GetAlignmentFromString(va(\"%s %s\", hud->align_x->string, hud->align_y->string));\n\n\tif(hud->place_hud)\n\t{\n\t\t// Placed at another HUD.\n\t\tparent_x = hud->place_hud->lx;\n\t\tparent_y = hud->place_hud->ly;\n\t\tparent_w = hud->place_hud->lw;\n\t\tparent_h = hud->place_hud->lh;\n\n\t\t(*x) = HUD_CENTER_X(hud->place_hud);\n\t\t(*y) = HUD_CENTER_Y(hud->place_hud);\n\t}\n\telse\n\t{\n\t\t// Placed at \"screen\".\n\t\tparent_x = 0;\n\t\tparent_y = 0;\n\t\tparent_w = vid.width;\n\t\tparent_h = vid.height;\n\n\t\t(*x) = vid.width / 2;\n\t\t(*y) = vid.height / 2;\n\t}\n\n\tswitch(alignmode)\n\t{\n\t\tdefault:\n\t\tcase hud_align_center:\n\t\t\t// Already set.\n\t\t\tbreak;\n\t\tcase hud_align_right:\n\t\t\t(*x) = parent_x + parent_w;\n\t\t\t(*y) = parent_y + (parent_h / 2);\n\t\t\tbreak;\n\t\tcase hud_align_topright:\n\t\t\t(*x) = parent_x + parent_w;\n\t\t\t(*y) = parent_y;\n\t\t\tbreak;\n\t\tcase hud_align_top:\n\t\t\t(*x) = parent_x + (parent_w / 2);\n\t\t\t(*y) = parent_y;\n\t\t\tbreak;\n\t\tcase hud_align_topleft:\n\t\t\t(*x) = parent_x;\n\t\t\t(*y) = parent_y;\n\t\t\tbreak;\n\t\tcase hud_align_left:\n\t\t\t(*x) = parent_x;\n\t\t\t(*y) = parent_y + (parent_h / 2);\n\t\t\tbreak;\n\t\tcase hud_align_bottomleft:\n\t\t\t(*x) = parent_x;\n\t\t\t(*y) = parent_y + parent_h;\n\t\t\tbreak;\n\t\tcase hud_align_bottom:\n\t\t\t(*x) = parent_x + (parent_w / 2);\n\t\t\t(*y) = parent_y + parent_h;\n\t\t\tbreak;\n\t\tcase hud_align_bottomright:\n\t\t\t(*x) = parent_x + parent_w;\n\t\t\t(*y) = parent_y + parent_h;\n\t\t\tbreak;\n\t\tcase hud_align_consoleleft:\n\t\t\t(*x) = parent_x;\n\t\t\t(*y) = scr_con_current;\n\t\t\tbreak;\n\t\tcase hud_align_console:\n\t\t\t(*x) = parent_x + (parent_w / 2);\n\t\t\t(*y) = scr_con_current;\n\t\t\tbreak;\n\t\tcase hud_align_consoleright:\n\t\t\t(*x) = parent_x + parent_w;\n\t\t\t(*y) = scr_con_current;\n\t\t\tbreak;\n\t}\n}\n\n//\n// Draws a green line to each corner of a HUD element from a specified point.\n//\n/*\nstatic void HUD_Editor_DrawLinesToEachCorner(hud_t *hud, int x, int y)\n{\n\tDraw_AlphaLineRGB(hud->lx, hud->ly,\t\t\t\t\t\tx, y, 1, RGBA_TO_COLOR(0, 255, 0, 25));\n\tDraw_AlphaLineRGB(hud->lx, hud->ly + hud->lh,\t\t\tx, y, 1, RGBA_TO_COLOR(0, 255, 0, 25));\n\tDraw_AlphaLineRGB(hud->lx + hud->lw, hud->ly,\t\t\tx, y, 1, RGBA_TO_COLOR(0, 255, 0, 25));\n\tDraw_AlphaLineRGB(hud->lx + hud->lw, hud->ly + hud->lh, x, y, 1, RGBA_TO_COLOR(0, 255, 0, 25));\n}\n*/\n\n//\n// Draws connections to/from a HUD element.\n//\nstatic void HUD_Editor_DrawConnections(hud_t *hud_hover)\n{\n\thud_t *child = NULL;\n\tint align_x = 0.0;\n\tint align_y = 0.0;\n\n\tif (!hud_hover || !hud_editor_showoutlines)\n\t{\n\t\treturn;\n\t}\n\n\t// Get the alignment point at the parent in screen coordinates.\n\tHUD_Editor_GetAlignmentPoint(hud_hover, &align_x, &align_y);\n\n\t// Draw a line to the parent of the HUD we're hovering.\n\tDraw_AlphaLineRGB(HUD_CENTER_X(hud_hover), HUD_CENTER_Y(hud_hover), align_x, align_y, 1, 0, 255, 0, 25);\n\n\t// Draw a line to all children of the HUD we're hovering.\n\twhile((child = HUD_Editor_FindNextChild(hud_hover)))\n\t{\n\t\t// Don't bother with hidden children.\n\t\tif(!child->show->value)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Draw a line from the alignment point on the parent to the child.\n\t\tHUD_Editor_GetAlignmentPoint(child, &align_x, &align_y);\n\t\tDraw_AlphaLineRGB(align_x, align_y, HUD_CENTER_X(child), HUD_CENTER_Y(child), 1, 0, 255, 0, 25);\n\t}\n}\n\n//\n// Evaluates the current mouse/keyboard state and sets the appropriate mode.\n//\nstatic void HUD_Editor_EvaluateState(hud_t *hud_hover)\n{\n\t// Mouse 1\t\t\t= Move + Resize\n\t// Mouse 2\t\t\t= Toggle menu\n\t// Ctrl  + Mouse 1\t= Place\n\t// Alt\t + Mouse 1\t= Align\n\t// Shift + Mouse 1\t= Lock moving to one axis (If you start dragging along x-axis, it will stick to that)\n\n\tif (MOUSEDOWN)\n\t{\n\t\t// Turn of help on mouse click.\n\t\thud_editor_showhelp = false;\n\t}\n\n\tif (hud_editor_mode == hud_editmode_hoverlist)\n\t{\n\t\tif (!MOUSEDOWN)\n\t\t{\n\t\t\t// Stay in hoverlist mode until the user clicks something.\n\t\t\treturn;\n\t\t}\n\t}\n\n\tif (hud_hover && MOUSEDOWN_1_ONLY && isShiftDown())\n\t{\n\t\t// Move (Locked to an axis).\n\t\tHUD_Editor_SetMode(hud_editmode_move_lockedaxis);\n\t}\n\telse if ((hud_hover || hud_editor_prevmode == hud_editmode_place) && MOUSEDOWN_1_ONLY && isCtrlDown())\n\t{\n\t\t// Place.\n\t\tHUD_Editor_SetMode(hud_editmode_place);\n\t}\n\telse if ((hud_hover || hud_editor_prevmode == hud_editmode_align) && MOUSEDOWN_1_ONLY && isAltDown())\n\t{\n\t\t// Align.\n\t\tHUD_Editor_SetMode(hud_editmode_align);\n\t}\n\telse if ((hud_hover || selected_hud) && MOUSEDOWN_1_ONLY)\n\t{\n\t\t// Move + resize.\n\t\tHUD_Editor_SetMode(hud_editmode_move_resize);\n\t}\n\telse if (hud_hoverlist_count > 1 && MOUSEDOWN_2_ONLY)\n\t{\n\t\t// Hover list for when hovering more than one HUD.\n\t\tHUD_Editor_SetMode(hud_editmode_hoverlist);\n\t}\n\telse if (hud_hover && MOUSEDOWN_2_ONLY)\n\t{\n\t\t// HUD element menu for the HUD element we have the mouse over.\n\t\tHUD_Editor_SetMode(hud_editmode_hudmenu);\n\t}\n\telse if (MOUSEDOWN_2_ONLY)\n\t{\n\t\t// Main menu for adding HUDs if we right click non-occupied space.\n\t\tHUD_Editor_SetMode(hud_editmode_menu);\n\t}\n\telse\n\t{\n\t\t// Nothing special happening.\n\t\tHUD_Editor_SetMode(hud_editmode_normal);\n\t}\n}\n\n//\n// Draws the tooltips for a HUD element based on the state we're in.\n//\nstatic void HUD_Editor_DrawTooltips(hud_t *hud_hover)\n{\n\tchar *message = NULL;\n\tbyte color[4] = {0, 0, 0, 0};\n\n\tif(!hud_hover)\n\t{\n\t\treturn;\n\t}\n\n\tif (selected_hud)\n\t{\n\t\tswitch(hud_editor_mode)\n\t\t{\n\t\t\tcase hud_editmode_move_lockedaxis :\n\t\t\tcase hud_editmode_move_resize :\n\t\t\t{\n\t\t\t\tmessage = va(\"(%d, %d) moving %s\", (int)selected_hud->pos_x->value, (int)selected_hud->pos_y->value, selected_hud->name);\n\t\t\t\tcolor[0] = 255;\n\t\t\t\tcolor[3] = 125;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase hud_editmode_align :\n\t\t\t{\n\t\t\t\tchar *align = NULL;\n\n\t\t\t\talign = HUD_Editor_GetAlignmentString(hud_alignmode);\n\n\t\t\t\tmessage = va(\"align %s to %s\", selected_hud->name, align);\n\t\t\t\tcolor[1] = 255;\n\t\t\t\tcolor[2] = 255;\n\t\t\t\tcolor[3] = 125;\n\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase hud_editmode_place :\n\t\t\t{\n\t\t\t\tmessage = va(\"placing %s\", selected_hud->name);\n\t\t\t\tcolor[0] = 255;\n\t\t\t\tcolor[3] = 125;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase hud_editmode_normal :\n\t\t\t{\n\t\t\t\tmessage = hud_hover->name;\n\t\t\t\tcolor[2] = 255;\n\t\t\t\tcolor[3] = 125;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\t// unhandled\n\t\t\tcase hud_editmode_off:\n\t\t\tcase hud_editmode_resize:\n\t\t\tcase hud_editmode_hudmenu:\n\t\t\tcase hud_editmode_menu:\n\t\t\tcase hud_editmode_hoverlist:\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\tif(!message)\n\t{\n\t\tmessage = hud_hover->name;\n\t\tcolor[2] = 255;\n\t\tcolor[3] = 125;\n\t}\n\n\tHUD_Editor_DrawTooltip(hud_mouse_x, hud_mouse_y, message, color[0], color[1], color[2], color[3]);\n}\n\n//\n// Draws a help window.\n//\nstatic void HUD_Editor_DrawHelp()\n{\n\t#define HUD_EDITOR_HELP_BORDER\t32\n\t#define HUD_EDITOR_HELP_WIDTH\tmin(vid.width - (2 * HUD_EDITOR_HELP_BORDER), 500)\n\t#define HUD_EDITOR_HELP_HEIGHT\t(vid.height - (2 * HUD_EDITOR_HELP_BORDER))\n\t#define HUD_EDITOR_HELP_X\t\t((vid.width - HUD_EDITOR_HELP_WIDTH) / 2)\n\t#define HUD_EDITOR_HELP_Y\t\tHUD_EDITOR_HELP_BORDER\n\t#define HUD_EDITOR_HELP_TITLE\t\"&cfd0HUD EDITOR HELP\"\n\n\tDraw_TextBox(HUD_EDITOR_HELP_X, HUD_EDITOR_HELP_Y, HUD_EDITOR_HELP_WIDTH / 8, HUD_EDITOR_HELP_HEIGHT / 8);\n\n\tDraw_ColoredString(\n\t\tHUD_EDITOR_HELP_X + ((HUD_EDITOR_HELP_WIDTH - strlen(HUD_EDITOR_HELP_TITLE) * 8) / 2),\n\t\tHUD_EDITOR_HELP_Y + 10,\n\t\tHUD_EDITOR_HELP_TITLE, 1);\n\n\tUI_PrintTextBlock(\n\t\tHUD_EDITOR_HELP_X + 10,\n\t\tHUD_EDITOR_HELP_Y + 30,\n\t\tHUD_EDITOR_HELP_WIDTH,\n\t\tHUD_EDITOR_HELP_HEIGHT - 30,\n\t\t\"The HUD Editor helps you to customize your Heads Up Display. \"\n\t\t\"When you move the cursor over a HUD element it will be \"\n\t\t\"highlighted and it's name will be shown. When hovering a HUD \"\n\t\t\"you can perform the following actions:\\n\"\n\t\t\"\\n\"\n\t\t\"&cfd0MOVE&r relative to the HUD elements parent/alignment by \"\n\t\t\"holding down &c0dfMOUSE 1&r and &c0dfdragging&r.\\n\"\n\t\t\"(Lock movement to a specific axis by holding down &c0dfSHIFT&r).\\n\"\n\t\t\"\\n\"\n\t\t\"&cfd0RESIZE&r the HUD element by clicking on one of the \"\n\t\t\"&c0dfresize handles&r that appears when hovering and item and \"\n\t\t\"&c0dfdragging&r. (Not all HUD elements are resizeable/scaleable).\\n\"\n\t\t\"\\n\"\n\t\t\"&cfd0PLACE&r the HUD element at another HUD element or a location \"\n\t\t\"such as the screen/console by holding down &c0dfCTRL&r when \"\n\t\t\"dragging. The target that the element will be placed in \"\n\t\t\"will turn green if you can place it there, red otherwise.\\n\"\n\t\t\"\\n\"\n\t\t\"&cfd0ALIGN&r the HUD element in different ways to it's parent by \"\n\t\t\"holding down &c0dfALT&r when dragging. Doing this will show \"\n\t\t\"yellow highlights at the position you're about to align to.\\n\"\n\t\t\"\\n\"\n\t\t\"  &cfd0Keyboard shortcuts:\\n\"\n\t\t\"  &c0dfP&r      Toggle HUD planmode on/off (default on).\\n\"\n\t\t\"  &c0dfH&r      Toggle this help.\\n\"\n\t\t\"  &c0dfF1&r     Toggle if moving should be allowed.\\n\"\n\t\t\"  &c0dfF2&r     Toggle resizing.\\n\"\n\t\t\"  &c0dfF3&r     Toggle aligning.\\n\"\n\t\t\"  &c0dfF4&r     Toggle placing.\\n\"\n\t\t\"  &c0dfSPACE&r  Toggle outlines/guidelines.\\n\",\n\t\t0);\n}\n\n#ifdef HAXX\nint Test_OnGotFocus(ez_control_t *self, void *payload, void *ext_event_info)\n{\n\tEZ_control_SetBackgroundColor(self, self->background_color[0], self->background_color[1], self->background_color[2], 200);\n\treturn 0;\n}\n\nint Test_OnLostFocus(ez_control_t *self, void *payload, void *ext_event_info)\n{\n\tEZ_control_SetBackgroundColor(self, self->background_color[0], self->background_color[1], self->background_color[2], 100);\n\treturn 0;\n}\n\nez_control_t *root = NULL;\nez_control_t *child1 = NULL;\nez_control_t *child2 = NULL;\nez_button_t *button = NULL;\nez_label_t *label = NULL;\nez_label_t *label2 = NULL;\nez_slider_t *slider = NULL;\nez_scrollbar_t *scrollbar = NULL;\nez_scrollpane_t *scrollpane = NULL;\nez_listview_t *listview = NULL;\nez_window_t *window = NULL;\n\nint Test_OnButtonDraw(ez_control_t *self, void *payload, void *ext_event_info)\n{\n\tint x, y;\n\tEZ_control_GetDrawingPosition(self, &x, &y);\n\n\treturn 0;\n}\n\nint Test_OnSliderPositionChanged(ez_control_t *self, void *payload, void *ext_event_info)\n{\n\tez_slider_t *slider = (ez_slider_t *)self;\n\n\tint slider_pos = EZ_slider_GetPosition(slider);\n\n\tEZ_label_SetText(label2, va(\"%i\", slider_pos));\n\n\treturn 0;\n}\n\nint Test_OnControlDraw(ez_control_t *self, void *payload, void *ext_event_info)\n{\n\tint x, y; //, i;\n\tEZ_control_GetDrawingPosition(self, &x, &y);\n\n\t/*for (i = 0; i < 30; i++)\n\t{\n\t\tDraw_String(x, y + i*8, va(\"%d%d%d%d\", i, i, i, i));\n\t}*/\n\n\t/*\n\t{\n\t\tchar str[] = \"Hello this is a sentence that's supposed to fit within a box of stuff, I hope this works...\";\n\t\tchar line[1024];\n\t\tint last_index = 0;\n\t\tint i = 0;\n\n\t\twhile (Util_GetNextWordwrapString(str, line, last_index, &last_index, sizeof(str) / sizeof(char), self->width, 8))\n\t\t{\n\t\t\tDraw_String(x, y + i*8, line);\n\t\t\ti++;\n\t\t}\n\t}\n\t*/\n\n\t/*{\n\t\tclrinfo_t color;\n\t\tcolor.i = 0;\n\t\tcolor.c = RGBA_TO_COLOR(0, 255, 0, 255);\n\t\tDraw_BigString(x, y, \"Hej\", &color, 1, 1, 1, 0);\n\t}*/\n\n\treturn 0;\n}\n#endif\n\nstatic hud_t *hud_hover = NULL;\n\n//\n// Main HUD Editor function.\n//\nstatic void HUD_Editor(void)\n{\n\tqbool found = false;\n\n\t// If we just entered hoverlist mode we want to keep the mouse coordinates\n\t// so we know where to draw the list until the user has picked a HUD.\n\tif (!hud_hoverlist_pos_is_set\n\t\t&& hud_editor_mode == hud_editmode_hoverlist\n\t\t&& hud_editor_prevmode != hud_editmode_hoverlist)\n\t{\n\t\thud_hoverlist_x = cursor_x;\n\t\thud_hoverlist_y = cursor_y;\n\t\thud_hoverlist_pos_is_set = true;\n\t}\n\telse if (hud_editor_mode != hud_editmode_hoverlist)\n\t{\n\t\thud_hoverlist_pos_is_set = false;\n\t}\n\n\t// Find the HUD we're moving or have the cursor over.\n\tfound = HUD_Editor_FindHudUnderCursor(&hud_hover);\n\n\t// Draw faint outlines for all visible hud elements.\n\tHUD_Editor_DrawOutlines();\n\n\t// Draw the \"grep handles\" for offscreen HUDs.\n\tHUD_Editor_DrawGreps();\n\n\t// Draw a rectangle around the currently active HUD element.\n\tif(found && hud_hover)\n\t{\n\t\tDraw_AlphaRectangleRGB(hud_hover->lx, hud_hover->ly, hud_hover->lw, hud_hover->lh, 1, false, 0, 255, 0, 255);\n\t}\n\n\t// If we are realigning draw a green outline for the selected hud element.\n\tif (selected_hud)\n\t{\n\t\tDraw_AlphaRectangleRGB(selected_hud->lx, selected_hud->ly, selected_hud->lw, selected_hud->lh, 2, false, 0, 255, 0, 255);\n\t}\n\n\t// Check the mouse/keyboard states and if we're hovering above a hud or not.\n\tHUD_Editor_EvaluateState(hud_hover);\n\n\t// Draw the child/parent connections the hud we're hovering has.\n\tHUD_Editor_DrawConnections(hud_hover);\n\n\t// Draw a red line from selected hud to cursor.\n\tif (selected_hud)\n\t{\n\t\tDraw_AlphaLineRGB(hud_mouse_x, hud_mouse_y, HUD_CENTER_X(selected_hud), HUD_CENTER_Y(selected_hud), 1, 255, 0, 0, 255);\n\t}\n\n\t// Check if we're performing any action.\n\t// (Only perform one at any given time).\n\t(void)\n\t(HUD_Editor_DrawHoverList(hud_hoverlist_x, hud_hoverlist_y, hud_hoverlist)\n\t\t || HUD_Editor_Resizing(hud_hover)\n\t\t || HUD_Editor_Moving(hud_hover)\n\t\t || HUD_Editor_Placing(hud_hover)\n\t\t || HUD_Editor_Aligning(hud_hover));\n\n\t// Draw tooltips for the HUD.\n\tHUD_Editor_DrawTooltips(hud_hover);\n\n\t// Show the help window?\n\tif(hud_editor_showhelp)\n\t{\n\t\tHUD_Editor_DrawHelp();\n\t}\n\n#ifdef HAXX\n\tEZ_tree_EventLoop(&help_control_tree);\n#endif\n}\n\n//\n// Toggles the HUD Editor on or off.\n//\nvoid HUD_Editor_Toggle_f(void)\n{\n\t// static keydest_t key_dest_prev = key_game;\n\tstatic int old_hud_planmode = 0;\n\n\tif (cls.state != ca_active)\n\t{\n\t\t// We can't turn on the hud editor when disconnected.\n\t\tif(!hud_editor)\n\t\t{\n\t\t\tCom_Printf(\"You need to be in game to use the HUD editor.\\n\");\n\t\t}\n\n\t\t// If the hud editor managed to still be on while disconnected.\n\t\thud_editor = false;\n\t}\n#ifdef HAXX\n\telse if (!scr_newHud->value)\n\t{\n\t\tCom_Printf(\"You have to have scr_newHud turned on to use the HUD editor.\\n\");\n\t\thud_editor = false;\n\t}\n#endif\n\telse\n\t{\n\t\t// Toggle.\n\t\thud_editor = !hud_editor;\n//\t\tS_LocalSound(\"misc/basekey.wav\");\n\t}\n\n\tif (hud_editor)\n\t{\n\t\t// Start HUD Editor.\n\n\t\tinputfuncs->SetMenuFocus(true, \"\", 0, 0, 0);\n\t\tHUD_Editor_SetMode(hud_editmode_normal);\n\n\t\t// Set planmode by default.\n\t\told_hud_planmode = hud_planmode->value;\n\t\tCvar_SetValue(hud_planmode, 1.0);\n\n\t\t// Start showing the help plaque so the user learns the controls.\n\t\thud_editor_showhelp = true;\n\t}\n\telse\n\t{\n\t\t// Exit the HUD Editor.\n\n\t\tinputfuncs->SetMenuFocus(false, \"\", 0, 0, 0);\n\t\tHUD_Editor_SetMode(hud_editmode_off);\n\t\tscr_cursor_icon = NULL;\n\n\t\t// Reset to the old value for HUD planmode.\n\t\tCvar_SetValue(hud_planmode, old_hud_planmode);\n\t}\n}\n\n//\n// Handles mouse events sent to the HUD editor.\n//\nqbool HUD_Editor_MouseEvent(float x, float y)\n{\n\t// Updating cursor location.\n\tif(hud_editor_mode == hud_editmode_move_lockedaxis)\n\t{\n\t\t// Don't update the HUD Editor cursor if the axis is locked\n\t\t// so that we avoid explicit checks for that.\n\t\t// The cursor will still move around the screen properly.\n\t\tif(hud_editor_locked_axis_is_x)\n\t\t{\n\t\t\thud_mouse_x = x;\n\n\t\t\t// Draw a line that indicates that the movement is locked to the X-axis.\n\t\t\tif (selected_hud)\n\t\t\t{\n\t\t\t\tDraw_AlphaLineRGB(0, HUD_CENTER_Y(selected_hud), vid.width, HUD_CENTER_Y(selected_hud), 1, 255, 0, 0, 75);\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\thud_mouse_y = y;\n\n\t\t\tif (selected_hud)\n\t\t\t{\n\t\t\t\tDraw_AlphaLineRGB(HUD_CENTER_X(selected_hud), 0, HUD_CENTER_X(selected_hud), vid.height, 1, 255, 0, 0, 75);\n\t\t\t}\n\t\t}\n\t}\n\telse\n\t{\n\t\t// Normal operation, always update cursor.\n\t\thud_mouse_x = x;\n\t\thud_mouse_y = y;\n\t}\n\n#ifdef HAXX\n\treturn EZ_tree_MouseEvent(&help_control_tree, ms);\n#else\n\treturn true;\n#endif\n}\n\n//\n// Handles key events sent to the HUD editor.\n//\nvoid HUD_Editor_Key(int key, int unichar, qbool down)\n{\n\tstatic int planmode = 1;\n#ifdef HAXX\n\tint togglekeys[2];\n\n\tEZ_tree_KeyEvent(&help_control_tree, key, unichar, down);\n\n\tM_FindKeysForCommand(\"toggleconsole\", togglekeys);\n\tif ((key == togglekeys[0]) || (key == togglekeys[1]))\n\t{\n\t\tCon_ToggleConsole_f();\n\t\treturn;\n\t}\n#endif\n\n\tif (down)\n\t{\n\t\tswitch (key)\n\t\t{\n\t\t\tcase K_ESCAPE:\n\t\t\tcase K_GP_BACK:\n\t\t\t\tHUD_Editor_Toggle_f();\n\t\t\t\tbreak;\n\t\t\tcase 'p' :\n\t\t\t\t// Toggle HUD plan mode.\n\t\t\t\tplanmode = !planmode;\n\t\t\t\tCvar_SetValue(hud_planmode, planmode);\n\t\t\t\tbreak;\n\t\t\tcase 'h' :\n\t\t\t\t// Toggle the help window.\n\t\t\t\thud_editor_showhelp = !hud_editor_showhelp;\n\t\t\t\tbreak;\n\t\t\tcase K_SPACE :\n\t\t\t\t// Toggle hud element outlines.\n\t\t\t\thud_editor_showoutlines = !hud_editor_showoutlines;\n\t\t\t\tbreak;\n\t\t\tcase K_F1 :\n\t\t\t\t// Toggle moving.\n\t\t\t\tCvar_SetValue(hud_editor_allowmove, !hud_editor_allowmove->value);\n\t\t\t\tbreak;\n\t\t\tcase K_F2 :\n\t\t\t\t// Toggle resizing.\n\t\t\t\tCvar_SetValue(hud_editor_allowresize, !hud_editor_allowresize->value);\n\t\t\t\tbreak;\n\t\t\tcase K_F3 :\n\t\t\t\t// Toggle aligning.\n\t\t\t\tCvar_SetValue(hud_editor_allowalign, !hud_editor_allowalign->value);\n\t\t\t\tbreak;\n\t\t\tcase K_F4 :\n\t\t\t\t// Toggle placing.\n\t\t\t\tCvar_SetValue(hud_editor_allowplace, !hud_editor_allowplace->value);\n\t\t\t\tbreak;\n\t\t\tcase K_UPARROW :\n\t\t\tcase K_KP_UPARROW:\n\t\t\tcase K_GP_DPAD_UP:\n\t\t\t\t// TODO : Add \"nudging\" in hud editor.\n\t\t\t\tbreak;\n\t\t\tcase K_DOWNARROW :\n\t\t\tcase K_KP_DOWNARROW:\n\t\t\tcase K_GP_DPAD_DOWN:\n\t\t\t\t// TODO : Add \"nudging\" in hud editor.\n\t\t\t\tbreak;\n\t\t\tcase K_LEFTARROW :\n\t\t\tcase K_KP_LEFTARROW:\n\t\t\tcase K_GP_DPAD_LEFT:\n\t\t\t\t// TODO : Add \"nudging\" in hud editor.\n\t\t\t\tbreak;\n\t\t\tcase K_RIGHTARROW :\n\t\t\tcase K_KP_RIGHTARROW:\n\t\t\tcase K_GP_DPAD_RIGHT:\n\t\t\t\t// TODO : Add \"nudging\" in hud editor.\n\t\t\t\tbreak;\n\t\t}\n\t}\n}\n\n//\n// Inits HUD Editor.\n//\nvoid HUD_Editor_Init(void)\n{\n\textern mpic_t *SCR_LoadCursorImage(char *cursorimage);\n\n#if 0\n\tclrinfo_t color;\n\n\tcolor.c = RGBA_TO_COLOR(255, 255, 255, 255);\n\tcolor.i = 0;\n\n\t// Root\n\t{\n\t\troot = EZ_control_Create(&help_control_tree, NULL, \"Test window\", \"Test\", 50, 50, 400, 400, control_focusable | control_movable | control_resize_h | control_resize_v);\n\t\tEZ_control_SetBackgroundColor(root, 0, 0, 0, 100);\n\t\tEZ_control_SetSize(root, 400, 400);\n\t}\n\n\t// Child 1\n\t{\n\t\tchild1 = EZ_control_Create(&help_control_tree, root, \"Child 1\", \"Test\", 10, 10, 50, 50, control_focusable | control_movable | control_contained | control_scrollable | control_resizeable);\n\n\t\tEZ_control_AddOnGotFocus(child1, Test_OnGotFocus, NULL);\n\t\tEZ_control_AddOnLostFocus(child1, Test_OnLostFocus, NULL);\n\t\tEZ_control_AddOnDraw(child1, Test_OnControlDraw, NULL);\n\n\t\tEZ_control_SetMinVirtualSize(child1, child1->width * 3, child1->height * 3);\n\t\t//EZ_control_SetVirtualSize(child1, child1->width * 4, child1->height * 2);\n\n\t\tEZ_control_SetBackgroundColor(child1, 150, 150, 0, 100);\n\t}\n\n\t// Child 2\n\t{\n\t\tchild2 = EZ_control_Create(&help_control_tree, root, \"Child 2\", \"Test\", 30, 50, 50, 20, control_focusable | control_contained);\n\n\t\tEZ_control_AddOnGotFocus(child2, Test_OnGotFocus, NULL);\n\t\tEZ_control_AddOnLostFocus(child2, Test_OnLostFocus, NULL);\n\n\t\tEZ_control_SetBackgroundColor(child2, 150, 150, 200, 100);\n\t}\n\n\t// Button.\n\t{\n\t\tbutton = EZ_button_Create(&help_control_tree, child1, \"button\", \"A crazy button!\", 15, -15, 80, 60, control_contained | control_resizeable);\n\t\tEZ_control_AddOnDraw((ez_control_t *)button, Test_OnButtonDraw, NULL);\n\n\t\tEZ_button_SetFocusedColor(button, 255, 0, 0, 255);\n\t\tEZ_button_SetNormalColor(button, 255, 255, 0, 100);\n\t\tEZ_button_SetPressedColor(button, 255, 255, 0, 255);\n\t\tEZ_button_SetHoverColor(button, 255, 0, 0, 150);\n\n\t\tEZ_button_SetToggleable(button, true);\n\n\t\tEZ_button_SetText(button, \"Button\");\n\t\tEZ_button_SetTextAlignment(button, middle_center);\n\n\t\tEZ_control_SetAnchor((ez_control_t *)button, (anchor_left | anchor_right | anchor_bottom));\n\t}\n\n\t// Label.\n\t{\n\t\tlabel = EZ_label_Create(&help_control_tree, root,\n\t\t\t\"label\", \"A crazy label!\", 200, 200, 250, 80,\n\t\t\tcontrol_focusable | control_contained | control_resizeable | control_scrollable /*| control_movable */ | control_resize_h | control_resize_v,\n\t\t\tlabel_wraptext | label_autosize,\n\t\t\t\"Hello\\nthis is a test are you fine because I am bla bla bla this is a very long string and it's plenty of fun haha!\");\n\n\t\tEZ_label_SetTextScale(label, 2.0);\n\t\t//EZ_label_SetTextFlags(label, LABEL_READONLY);\n\n\t\tEZ_control_SetBackgroundColor((ez_control_t *)label, 150, 150, 0, 50);\n\t\t//EZ_control_SetAnchor((ez_control_t *)label, anchor_top | anchor_right | anchor_bottom);\n\t}\n\n\t// Label 2.\n\t{\n\t\tlabel2 = EZ_label_Create(&help_control_tree, root,\n\t\t\t\"label2\", \"A crazy label!\", 100, 50, 32, 16,\n\t\t\tcontrol_focusable | control_contained | control_resizeable,\n\t\t\t0, \"\");\n\t}\n\n\t// Slider.\n\t{\n\t\tslider = EZ_slider_Create(&help_control_tree, root,\n\t\t\t\"slider\", \"Slider omg\", 50, 100, 150, 8, control_focusable | control_contained | control_resizeable);\n\n\t\tEZ_control_SetAnchor((ez_control_t *)slider, anchor_left | anchor_right);\n\n\t\tEZ_slider_SetMax(slider, 100);\n\t\tEZ_slider_SetMin(slider, 50);\n\t\tEZ_slider_SetPosition(slider, 5);\n\t\tEZ_slider_SetScale(slider, 1.0);\n\n\t\tEZ_slider_AddOnSliderPositionChanged(slider, Test_OnSliderPositionChanged, NULL);\n\t}\n\n\t/*\n\t// Scrollbar.\n\t{\n\t\tez_control_t *label_ctrl = (ez_control_t *)label;\n\n\t\tscrollbar = EZ_scrollbar_Create(&help_control_tree, root, \"Scrollbar\", \"\",\n\t\t\t30, 150, 10, 150, control_anchor_viewport);\n\n\t\tEZ_scrollbar_SetTargetParent(scrollbar, false);\n\t\t//EZ_control_SetContained((ez_control_t *)scrollbar, false);\n\t\tEZ_control_SetAnchor((ez_control_t *)scrollbar, anchor_right | anchor_top | anchor_bottom);\n\t\tEZ_control_SetMovable((ez_control_t *)scrollbar, false);\n\t}\n\t*/\n\n\t// Listview\n\t{\n\t\tlistview = EZ_listview_Create(&help_control_tree, root, \"Listview\", \"\", 50, 50, 200, 200,\n\t\t\tcontrol_resize_h | control_resize_v | control_resizeable);\n\n\t\tEZ_listview_SetHeaderText(listview, 0, \"Hej\");\n\t\tEZ_listview_SetHeaderText(listview, 1, \"Hej 2\");\n\n\t\tEZ_listview_SetColumnWidth(listview, 0, 80);\n\t\tEZ_listview_SetColumnWidth(listview, 1, 50);\n\t}\n\n\t// Scrollpane\n\t{\n\t\tscrollpane = EZ_scrollpane_Create(&help_control_tree, root, \"Scrollpane\", \"\", -10, -20, 150, 150,\n\t\t\tcontrol_resize_h | control_resize_v | control_resizeable);\n\n\t\tEZ_control_SetBackgroundColor((ez_control_t *)scrollpane, 255, 0, 0, 100);\n\n\t\t//EZ_scrollpane_SetTarget(scrollpane, child1);\n\t\tEZ_scrollpane_SetTarget(scrollpane, (ez_control_t *)listview);\n\t}\n\n\t// Window.\n\t{\n\t\twindow = EZ_window_Create(&help_control_tree, root, \"Window\", NULL, 20, 20, 150, 150,\n\t\t\tcontrol_movable | control_focusable | control_resize_h | control_resize_v | control_contained);\n\n\t\tEZ_control_SetBackgroundColor((ez_control_t *)window, 0, 100, 0, 100);\n\n\t\tEZ_window_SetWindowAreaMinVirtualSize(window, 200, 200);\n\n\t\t//EZ_window_AddChild(window, (ez_control_t *)scrollpane);\n\t}\n\n\t/*\n\t// Test.\n\t{\n\t\tez_control_t *c = EZ_control_Create(&help_control_tree, root, \"C test 1\", \"Test\", 10, 10, 150, 150,\n\t\t\tcontrol_resize_h | control_focusable | control_movable | control_contained | control_scrollable | control_resizeable);\n\n\t\tez_control_t *c2 = EZ_control_Create(&help_control_tree, c, \"C test 1\", \"Test\", 0, 10, 30, 10,\n\t\t\tcontrol_focusable | control_movable | control_contained | control_scrollable | control_resizeable);\n\n\t\tEZ_control_SetAnchor(c2, anchor_top | anchor_right);\n\t\tEZ_control_SetBackgroundColor(c2, 150, 0, 20, 100);\n\n\t\tEZ_control_SetBackgroundColor(c, 50, 40, 50, 100);\n\t}\n\t*/\n\n\tEZ_tree_Refresh(&help_control_tree);\n\n#endif\n\n\t// Register commands.\n\tCmd_AddCommand(\"hud_editor\", HUD_Editor_Toggle_f);\n\n\t// Register variables.\n\thud_editor_allowresize\t= cvarfuncs->GetNVFDG(\"hud_editor_allowresize\",\t\"1\", 0, NULL, \"hud\");\n\thud_editor_allowmove\t= cvarfuncs->GetNVFDG(\"hud_editor_allowmove\",\t\"1\", 0, NULL, \"hud\");\n\thud_editor_allowplace\t= cvarfuncs->GetNVFDG(\"hud_editor_allowplace\",\t\"1\", 0, NULL, \"hud\");\n\thud_editor_allowalign\t= cvarfuncs->GetNVFDG(\"hud_editor_allowalign\",\t\"1\", 0, NULL, \"hud\");\n\n\t// Load HUD editor cursor icons.\n\thud_editor_move_icon = SCR_LoadCursorImage(\"gfx/hud_move_icon\");\n\thud_editor_resize_icon = SCR_LoadCursorImage(\"gfx/hud_resize_icon\");\n\thud_editor_align_icon = SCR_LoadCursorImage(\"gfx/hud_align_icon\");\n\thud_editor_place_icon = SCR_LoadCursorImage(\"gfx/hud_place_icon\");\n\n\thud_editor = false;\n\tHUD_Editor_SetMode(hud_editmode_off);\n}\n\n//\n// Draws the HUD Editor if it's on.\n//\nvoid HUD_Editor_Draw(void)\n{\n\tif (!hud_editor)\n\t\treturn;\n\n\tHUD_Editor();\n}\n\n//\n// Should this HUD element be fully drawn or not when in align mode\n// when using the HUD editor?\n//\nqbool HUD_Editor_ConfirmDraw(hud_t *hud)\n{\n\tif(hud_editor_mode == hud_editmode_align || hud_editor_mode == hud_editmode_place)\n\t{\n\t\t// If this is the selected hud, or the parent of the selected hud then draw it.\n\t\tif((selected_hud && !strcmp(selected_hud->name, hud->name))\n\t\t\t|| (selected_hud && hud->place_hud && !strcmp(selected_hud->name, hud->place_hud->name)))\n\t\t{\n\t\t\treturn true;\n\t\t}\n\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\n"
  },
  {
    "path": "plugins/ezhud/hud_editor.h",
    "content": "/*\nHUD Editor module\n\nCopyright (C) 2007 Cokeman\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n#ifndef __HUD_EDITOR_H__\n#define __HUD_EDITOR_H__\n\n// hud editor drawing function\nvoid HUD_Editor_Draw(void);\n\n// hud editor initialization\nvoid HUD_Editor_Init(void);\n\n// Mouse processing.\nqbool HUD_Editor_MouseEvent (float x, float y);\n\n// Key press processing function.\nvoid HUD_Editor_Key(int key, int unichar, qbool down);\n\n//\n// Should this HUD element be fully drawn or not when in align mode\n// when using the HUD editor.\n//\nqbool HUD_Editor_ConfirmDraw(hud_t *hud);\n\ntypedef enum\n{\n\thud_editmode_off,\n\thud_editmode_align,\n\thud_editmode_place,\n\thud_editmode_move_resize,\n\thud_editmode_resize,\n\thud_editmode_move_lockedaxis,\n\thud_editmode_hudmenu,\n\thud_editmode_menu,\n\thud_editmode_hoverlist,\n\thud_editmode_normal\n} hud_editor_mode_t;\n\nextern hud_editor_mode_t\thud_editor_mode;\nextern hud_t\t\t\t\t*selected_hud;\n\n#endif // __HUD_EDITOR_H__\n"
  },
  {
    "path": "plugins/ezscript/ezscript.c",
    "content": "#include \"../plugin.h\"\n#define RELEASE \"__DATE__\"\n\n#define ezscriptcvars \"ezScript Console Variables\"\nvmcvar_t\tezscript_silentmode = {\"ezscript_silentmode\", \"1\", ezscriptcvars, 0};\n#undef ezscriptcvars\n\nvmcvar_t\t*cvarlist[] ={\n\t&ezscript_silentmode\n};\n\nvoid ezScript_InitCvars(void)\n{\n\tvmcvar_t *v;\n\tint i;\n\n\tfor (v = cvarlist[0],i=0; i < sizeof(cvarlist)/sizeof(cvarlist[0]); v++, i++)\n\t\tv->handle = Cvar_Register(v->name, v->string, v->flags, v->group);\n}\n\nint ezScript_CvarUpdate(void)\n{\n\tvmcvar_t *v;\n\tint i;\n\tfor (v = cvarlist[0],i=0; i < sizeof(cvarlist)/sizeof(cvarlist[0]); v++, i++)\n\t\tv->modificationcount = Cvar_Update(v->handle, &v->modificationcount, v->string, &v->value);\n\treturn 0;\n}\n\nint Plug_ExecuteCommand(int *args)\n{\n\tchar cmd[256];\n\tchar param[256];\n\tchar *cvar;\n\n\tCmd_Argv(0, cmd, sizeof(cmd));\n\n\t     if (!strcmp(\"loadsky\",\t\t\t\tcmd))\tcvar = \"r_skybox\";\n\telse if (!strcmp(\"r_skyname\",\t\t\tcmd))\tcvar = \"r_skybox\";\n\telse if (!strcmp(\"r_skycolor\",\t\t\tcmd))\tcvar = \"r_fastskycolour\"; // note the england spelling, spike is a englander\n\telse if (!strcmp(\"cl_physfps\",\t\t\tcmd))\tcvar = \"cl_netfps\";\n\telse if (!strcmp(\"fps_skycolor\",\t\tcmd))\tcvar = \"r_fastskycolour\"; // note the england spelling, spike is a englander\n\telse if (!strcmp(\"fps_sky\",\t\t\t\tcmd))\tcvar = \"r_fastsky\";\n\telse if (!strcmp(\"gl_consolefont\",\t\tcmd))\tcvar = \"gl_font\";\n\telse if (!strcmp(\"gl_bounceparticles\",\tcmd))\tcvar = \"r_bouncysparks\";\n\telse if (!strcmp(\"gl_loadlitfiles\",\t\tcmd))\tcvar = \"r_loadlit\";\n\telse if (!strcmp(\"gl_weather_rain\",\t\tcmd))\tcvar = \"r_part_rain\";\n\telse if (!strcmp(\"cl_bonusflash\",\t\tcmd))\tcvar = \"v_bonusflash\";\n\telse if (!strcmp(\"sw_gamma\",\t\t\tcmd))\tcvar = \"gamma\";\n\telse if (!strcmp(\"sw_contrast\",\t\t\tcmd))\tcvar = \"contrast\";\n\telse if (!strcmp(\"s_nosound\",\t\t\tcmd))\tcvar = \"nosound\";\n\telse if (!strcmp(\"scr_conback\",\t\t\tcmd))\tcvar = \"gl_conback\";\n\telse if (!strcmp(\"sv_maxpitch\",\t\t\tcmd))\tcvar = \"serverinfo maxpitch\";\n\telse if (!strcmp(\"sv_minpitch\",\t\t\tcmd))\tcvar = \"serverinfo minpitch\";\n\telse if (!strcmp(\"sv_zombietime\",\t\tcmd))\tcvar = \"zombietime\";\n\telse if (!strcmp(\"tp_triggers\",\t\t\tcmd))\tcvar = \"cl_triggers\";\n\telse if (!strcmp(\"teamskin\",\t\t\tcmd))\tcvar = \"cl_teamskin\";\n\telse if (!strcmp(\"enemyskin\",\t\t\tcmd))\tcvar = \"cl_enemyskin\";\n\telse if (!strcmp(\"cl_predictPlayers\",\tcmd))\tcvar = \"cl_predict_players\"; //lets not forget there is a cl_predict_players2\n\telse if (!strcmp(\"sshot_format\",\t\tcmd))\tcvar = \"scr_sshot_type\";\n\telse if (!strcmp(\"cl_solidPlayers\",\t\tcmd))\tcvar = \"cl_solid_players\";\n\telse if (!strcmp(\"fps_muzzleflash\",\t\tcmd))\tcvar = \"cl_muzzleflash\";\n\telse if (!strcmp(\"in_m_mwhook\",\t\t\tcmd))\tcvar = \"in_mwhook\";\n\telse if (!strcmp(\"r_floorcolor\",\t\tcmd))\tcvar = \"r_floorcolour\";\n\telse if (!strcmp(\"r_wallcolor\",\t\t\tcmd))\tcvar = \"r_wallcolour\";\n\telse if (!strcmp(\"r_farclip\",\t\t\tcmd))\tcvar = \"gl_maxdist\";\n\telse if (!strcmp(\"vid_colorbits\",\t\tcmd))\tcvar = \"vid_bpp\";\n\telse if (!strcmp(\"vid_customheight\",\tcmd))\tcvar = \"vid_height\";\n\telse if (!strcmp(\"vid_customwidth\",\t\tcmd))\tcvar = \"vid_width\";\n\telse if (!strcmp(\"vid_hwgammacontrol\",\tcmd))\tcvar = \"vid_hardwaregamma\";\n\telse if (!strcmp(\"vid_vsync\",\t\t\tcmd))\tcvar = \"_vid_wait_override\";\n\telse if (!strcmp(\"gl_lighting_vertex\",\tcmd))\tcvar = \"r_vertexlight\";\n\telse if (!strcmp(\"bgmvolume\",\t\t\tcmd))\tcvar = \"musicvolume\";\n\telse if (!strcmp(\"scr_menualpha\",\t\tcmd))\tcvar = \"scr_conalpha\";\n\telse if (!strcmp(\"cl_fakeshaft\",\t\tcmd))\tcvar = \"cl_truelightning\";\n\telse cvar = NULL;\n\n\tif (cvar)\n\t{\n\t\tif (Cmd_Argc() == 1)\t//a query\n\t\t{\n\t\t\tif (!Cvar_GetString(cvar, param, sizeof(param)))\n\t\t\t\tCon_Printf(\"ezScript: %s(%s) is BAD\\n\", cmd, cvar);\n\t\t\telse\n\t\t\t\tCon_Printf(\"ezScript: %s(%s) is \\\"%s\\\"\\n\", cmd, cvar, param);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tCmd_Argv(1, param, sizeof(param));\n\n\t\t\tCvar_SetString(cvar,param);\n\n\t\t\tezScript_CvarUpdate();\n\n\t\t\tif (ezscript_silentmode.value == 0) { Con_Printf(\"-------------------------------------\\n^7ezScript: ^1%s^7 is a ^3Fuh/ez/Z/More quakeworld ^7cvar, sending '^6%s^7' to ^2%s^7\\n-------------------------------------\\n\",cmd,param,cvar); }\n\n\t\t}\n\t\treturn 1;\n\t}\n\n\treturn 0;\n}\n\nvoid ezScript_InitCommands(void) // not really needed actually\n{\n\t//skyboxes\n\tCmd_AddCommand(\"loadsky\");\n\tCmd_AddCommand(\"r_skyname\");\n\tCmd_AddCommand(\"r_skycolor\");\n\tCmd_AddCommand(\"fps_sky\");\n\tCmd_AddCommand(\"fps_skycolor\");\n\t//gl stuff\n\tCmd_AddCommand(\"gl_consolefont\");\n\tCmd_AddCommand(\"gl_bounceparticles\");\n\tCmd_AddCommand(\"gl_loadlitfiles\");\n\tCmd_AddCommand(\"gl_weather_rain\");\n\tCmd_AddCommand(\"r_farclip\");\n\tCmd_AddCommand(\"vid_vsync\");\n\tCmd_AddCommand(\"gl_lighting_vertex\");\n\tCmd_AddCommand(\"scr_conback\");\n\t//misc\n\tCmd_AddCommand(\"cl_bonusflash\");\n\tCmd_AddCommand(\"cl_fakeshaft\");\n\tCmd_AddCommand(\"r_floorcolor\");\n\tCmd_AddCommand(\"r_wallcolor\");\n\t//gamma\n\tCmd_AddCommand(\"sw_gamma\");\n\tCmd_AddCommand(\"sw_contrast\");\n\t//sound\n\tCmd_AddCommand(\"s_nosound\");\n\t//teamplay\n\tCmd_AddCommand(\"tp_triggers\");\n\tCmd_AddCommand(\"teamskin\");\n\tCmd_AddCommand(\"enemyskin\");\n\t//console\n\tCmd_AddCommand(\"scr_menualpha\");\n\t//misc\n\tCmd_AddCommand(\"cl_predictPlayers\");\n\tCmd_AddCommand(\"sshot_format\");\n\tCmd_AddCommand(\"cl_solidPlayers\");\n\tCmd_AddCommand(\"fps_muzzleflash\");\n\tCmd_AddCommand(\"scr_menualpha\");\n\tCmd_AddCommand(\"in_m_mwhook\");\n\t//sound\n\tCmd_AddCommand(\"bgmvolume\");\n\t//fps\n\tCmd_AddCommand(\"cl_physfps\");\n\t//video\n\tCmd_AddCommand(\"vid_colorbits\");\n\tCmd_AddCommand(\"vid_customheight\");\n\tCmd_AddCommand(\"vid_cumstomwidth\");\n\tCmd_AddCommand(\"vid_hwgammacontrol\");\n\t//server\n\tCmd_AddCommand(\"sv_maxpitch\");\n\tCmd_AddCommand(\"sv_minpitch\");\n\tCmd_AddCommand(\"sv_zombietime\");\n}\n\nint Plug_Init(int *args)\n{\n\tif (!Plug_Export(\"ExecuteCommand\", Plug_ExecuteCommand))\n\t{\n\t\tCon_Printf(\"ezScript Plugin failed\\n\");\n\t\treturn false;\n\t}\n\n\tCon_Printf(va(\"ezScript Plugin %s Loaded\\n\",RELEASE));\n\tezScript_InitCvars();\n\tezScript_InitCommands();\n\treturn true;\n}\n"
  },
  {
    "path": "plugins/ezscript/ezscript.q3asm",
    "content": "-o \"ezscript\"\nplugin\nqvm_api\nezscript\n"
  },
  {
    "path": "plugins/hl2/Makefile",
    "content": "all: mat_vmt_progs.h\n\nVMTPROGSBASE=lightmapped refract transition unlit vertexlit water rt\nVMTPROGS:=$(foreach p,$(VMTPROGSBASE),vmt/$p)\nVMTPROGSFILES:=$(foreach p,$(VMTPROGS),glsl/$p.glsl)\nmat_vmt_progs.h: $(VMTPROGSFILES)\n\t../../engine/shaders/generatebuiltinsl $@ $(VMTPROGS)\n\n"
  },
  {
    "path": "plugins/hl2/fs_gma.c",
    "content": "#include \"../plugin.h\"\n#include \"../../engine/common/fs.h\"\n\nstatic plugfsfuncs_t *filefuncs = NULL;\nstatic plugthreadfuncs_t *threadfuncs = NULL;\n#define Sys_CreateMutex() (threadfuncs?threadfuncs->CreateMutex():NULL)\n#define Sys_LockMutex(m) (threadfuncs?threadfuncs->LockMutex(m):true)\n#define Sys_UnlockMutex if(threadfuncs)threadfuncs->UnlockMutex\n#define Sys_DestroyMutex if(threadfuncs)threadfuncs->DestroyMutex\n\ntypedef struct gmafile {\n\tfsbucket_t bucket;\n\tchar name[MAX_QPATH];\n\tsize_t ofs;\n\tsize_t len;\n} gmafile_t;\n\ntypedef struct gma {\n\tsearchpathfuncs_t pub;\n\tchar desc[MAX_OSPATH];\n\tvfsfile_t *handle;\n\tsize_t num_files;\n\tgmafile_t *files;\n\tvoid *mutex;\n\tint references;\n} gma_t;\n\nstatic qboolean VFS_READZ(vfsfile_t *file, char *out, size_t outlen)\n{\n\tsize_t i;\n\tfor (i = 0; i < outlen; i++)\n\t{\n\t\tif (VFS_READ(file, &out[i], sizeof(char)) != sizeof(char))\n\t\t\tbreak;\n\t\tif (out[i] == 0)\n\t\t\treturn true;\n\t}\n\n\t// too long\n\tout[i] = 0;\n\treturn false;\n}\n\nstatic void QDECL FSGMA_GetPathDetails(searchpathfuncs_t *handle, char *out, size_t outlen)\n{\n\tgma_t *pak = (gma_t *)handle;\n\t*out = 0;\n\tif (pak->references != 1)\n\t\tQ_snprintfz(out, outlen, \"(%i)\", pak->references - 1);\n}\n\nstatic void QDECL FSGMA_ClosePath(searchpathfuncs_t *handle)\n{\n\tqboolean stillopen;\n\tgma_t *pak = (gma_t *)handle;\n\n\tif (!Sys_LockMutex(pak->mutex))\n\t\treturn; //ohnoes\n\tstillopen = --pak->references > 0;\n\tSys_UnlockMutex(pak->mutex);\n\tif (stillopen)\n\t\treturn; //not free yet\n\n\tVFS_CLOSE(pak->handle);\n\n\tSys_DestroyMutex(pak->mutex);\n\n\tplugfuncs->Free(pak->files);\n\tplugfuncs->Free(pak);\n}\n\nstatic void QDECL FSGMA_BuildHash(searchpathfuncs_t *handle, int depth, void (QDECL *AddFileHash)(int depth, const char *fname, fsbucket_t *filehandle, void *pathhandle))\n{\n\tgma_t *pak = (gma_t *)handle;\n\tint i;\n\tfor (i = 0; i < pak->num_files; i++)\n\t\tAddFileHash(depth, pak->files[i].name, &pak->files[i].bucket, &pak->files[i]);\n}\n\nstatic unsigned int QDECL FSGMA_FindFile(searchpathfuncs_t *handle, flocation_t *loc, const char *filename, void *hashedresult)\n{\n\tgmafile_t *file = (gmafile_t *)hashedresult;\n\tgma_t *pak = (gma_t *)handle;\n\tint i;\n\n\tif (file)\n\t{\n\t\t//is this a pointer to a file in this pak?\n\t\tif (file < pak->files || file > pak->files + pak->num_files)\n\t\t\treturn FF_NOTFOUND;\n\t}\n\telse\n\t{\n\t\tfor (i = 0; i < pak->num_files; i++)\n\t\t{\n\t\t\tif (strcasecmp(filename, pak->files[i].name) == 0)\n\t\t\t{\n\t\t\t\tfile = &pak->files[i];\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\tif (file)\n\t{\n\t\tif (loc)\n\t\t{\n\t\t\tloc->fhandle = file;\n\t\t\t*loc->rawname = 0;\n\t\t\tloc->offset = (qofs_t)-1;\n\t\t\tloc->len = file->len;\n\t\t}\n\n\t\treturn FF_FOUND;\n\t}\n\n\treturn FF_NOTFOUND;\n}\n\ntypedef struct vfsgma {\n\tvfsfile_t funcs;\n\tgma_t *parent;\n\tqofs_t startpos;\n\tqofs_t length;\n\tqofs_t currentpos;\n} vfsgma_t;\n\nstatic int QDECL VFSGMA_ReadBytes(struct vfsfile_s *vfs, void *buffer, int bytestoread)\n{\n\tvfsgma_t *vfsp = (vfsgma_t *)vfs;\n\tint read = 0;\n\n\tif (bytestoread + vfsp->currentpos > vfsp->length)\n\t\tbytestoread = vfsp->length - vfsp->currentpos;\n\n\tif (Sys_LockMutex(vfsp->parent->mutex))\n\t{\n\t\tVFS_SEEK(vfsp->parent->handle, vfsp->startpos + vfsp->currentpos);\n\t\tread = VFS_READ(vfsp->parent->handle, buffer, bytestoread);\n\t\tvfsp->currentpos += read;\n\t\tSys_UnlockMutex(vfsp->parent->mutex);\n\t}\n\n\treturn read;\n}\n\nstatic int QDECL VFSGMA_WriteBytes(struct vfsfile_s *vfs, const void *buffer, int bytestoread)\n{\n\tplugfuncs->Error(\"Cannot write to gma files\\n\");\n\treturn 0;\n}\n\nstatic qofs_t QDECL VFSGMA_Tell(struct vfsfile_s *vfs)\n{\n\tvfsgma_t *vfsp = (vfsgma_t *)vfs;\n\treturn vfsp->currentpos;\n}\n\nstatic qofs_t QDECL VFSGMA_GetLen(struct vfsfile_s *vfs)\n{\n\tvfsgma_t *vfsp = (vfsgma_t *)vfs;\n\treturn vfsp->length;\n}\n\nstatic qboolean QDECL VFSGMA_Close(vfsfile_t *vfs)\n{\n\tvfsgma_t *vfsp = (vfsgma_t *)vfs;\n\tFSGMA_ClosePath(&vfsp->parent->pub); //tell the parent that we don't need it open any more (reference counts)\n\tplugfuncs->Free(vfsp); //free ourselves.\n\treturn true;\n}\n\nstatic qboolean QDECL VFSGMA_Seek(struct vfsfile_s *vfs, qofs_t pos)\n{\n\tvfsgma_t *vfsp = (vfsgma_t *)vfs;\n\tif (pos > vfsp->length)\n\t\treturn false;\n\tvfsp->currentpos = pos;\n\treturn true;\n}\n\nstatic vfsfile_t *QDECL FSGMA_OpenVFS(searchpathfuncs_t *handle, flocation_t *loc, const char *mode)\n{\n\tgma_t *pak = (gma_t *)handle;\n\tvfsgma_t *vfs;\n\tgmafile_t *f = (gmafile_t *)loc->fhandle;\n\n\tif (strcmp(mode, \"rb\") != 0)\n\t\treturn NULL;\n\n\tvfs = plugfuncs->Malloc(sizeof(vfsgma_t));\n\n\tvfs->parent = pak;\n\tif (!Sys_LockMutex(vfs->parent->mutex))\n\t{\n\t\tplugfuncs->Free(vfs);\n\t\treturn NULL;\n\t}\n\n\tvfs->parent->references++;\n\tSys_UnlockMutex(vfs->parent->mutex);\n\n\tvfs->startpos = f->ofs;\n\tvfs->length = loc->len;\n\tvfs->currentpos = 0;\n\n#ifdef _DEBUG\n\tQ_strlcpy(vfs->funcs.dbgname, f->name, sizeof(vfs->funcs.dbgname));\n#endif\n\tvfs->funcs.Close = VFSGMA_Close;\n\tvfs->funcs.GetLen = VFSGMA_GetLen;\n\tvfs->funcs.ReadBytes = VFSGMA_ReadBytes;\n\tvfs->funcs.Seek = VFSGMA_Seek;\n\tvfs->funcs.Tell = VFSGMA_Tell;\n\tvfs->funcs.WriteBytes = VFSGMA_WriteBytes; //not supported\n\n\treturn (vfsfile_t *)vfs;\n}\n\nstatic void QDECL FSGMA_ReadFile(searchpathfuncs_t *handle, flocation_t *loc, char *buffer)\n{\n\tvfsfile_t *f;\n\tf = FSGMA_OpenVFS(handle, loc, \"rb\");\n\tif (!f)\n\t\treturn;\n\tVFS_READ(f, buffer, loc->len);\n\tVFS_CLOSE(f);\n}\n\nstatic int QDECL FSGMA_EnumerateFiles(searchpathfuncs_t *handle, const char *match, int (QDECL *func)(const char *, qofs_t, time_t mtime, void *, searchpathfuncs_t *spath), void *parm)\n{\n\tgma_t *pak = (gma_t *)handle;\n\tint i;\n\n\tfor (i = 0; i < pak->num_files; i++)\n\t{\n\t\tif (filefuncs->WildCmp(match, pak->files[i].name))\n\t\t{\n\t\t\tif (!func(pak->files[i].name, pak->files[i].len, 0, parm, handle))\n\t\t\t\treturn false;\n\t\t}\n\t}\n\n\treturn true;\n}\n\nstatic int QDECL FSGMA_GeneratePureCRC(searchpathfuncs_t *handle, const int *seed)\n{\n\tgma_t *pak = (gma_t *)handle;\n\treturn filefuncs->BlockChecksum(pak->files, sizeof(*pak->files) * pak->num_files);\n}\n\nstatic searchpathfuncs_t *QDECL FSGMA_LoadArchive(vfsfile_t *file, searchpathfuncs_t *parent, const char *filename, const char *desc, const char *prefix)\n{\n\tchar magic[4];\n\tuint8_t version;\n\tuint64_t steamid;\n\tuint64_t timestamp;\n\tuint8_t padding;\n\tchar addon_name[MAX_QPATH];\n\tchar addon_description[MAX_QPATH];\n\tchar addon_author[MAX_QPATH];\n\tuint32_t addon_version;\n\tgma_t *pak;\n\tint i;\n\tuint64_t ofs;\n\n\t// sanity checks\n\tif (!file) return NULL;\n\tif (prefix && *prefix) return NULL;\n\n\t// validate magic\n\tif (VFS_READ(file, magic, sizeof(magic)) < sizeof(magic))\n\t\treturn NULL;\n\tif (memcmp(magic, \"GMAD\", 4) != 0)\n\t\treturn NULL;\n\n\t// validate version\n\tif (VFS_READ(file, &version, sizeof(version)) < sizeof(version))\n\t\treturn NULL;\n\tif (version != 3) // i don't know what's different in the other versions\n\t\treturn NULL;\n\n\t// read other stuff\n\tif (VFS_READ(file, &steamid, sizeof(steamid)) < sizeof(steamid))\n\t\treturn NULL;\n\tif (VFS_READ(file, &timestamp, sizeof(timestamp)) < sizeof(timestamp))\n\t\treturn NULL;\n\tif (VFS_READ(file, &padding, sizeof(padding)) < sizeof(padding))\n\t\treturn NULL;\n\tif (!VFS_READZ(file, addon_name, sizeof(addon_name)))\n\t\treturn NULL;\n\tif (!VFS_READZ(file, addon_description, sizeof(addon_description)))\n\t\treturn NULL;\n\tif (!VFS_READZ(file, addon_author, sizeof(addon_author)))\n\t\treturn NULL;\n\tif (VFS_READ(file, &addon_version, sizeof(addon_version)) < sizeof(addon_version))\n\t\treturn NULL;\n\n\t// alloc\n\tpak = plugfuncs->Malloc(sizeof(*pak));\n\n\t// read file table\n\tpak->num_files = 0;\n\tofs = 0;\n\twhile (1)\n\t{\n\t\tuint32_t id;\n\t\tuint64_t len;\n\t\tuint32_t crc;\n\n\t\t// read id (0 means end of file list)\n\t\tVFS_READ(file, &id, sizeof(id));\n\t\tif (id == 0)\n\t\t\tbreak;\n\n\t\tpak->files = plugfuncs->Realloc(pak->files, sizeof(*pak->files) * (pak->num_files + 1));\n\n\t\t// read path, len, crc\n\t\tVFS_READZ(file, pak->files[pak->num_files].name, sizeof(pak->files[pak->num_files].name));\n\t\tVFS_READ(file, &len, sizeof(len));\n\t\tVFS_READ(file, &crc, sizeof(crc));\n\n\t\tpak->files[pak->num_files].len = LittleI64(len);\n\t\tpak->files[pak->num_files].ofs = ofs;\n\t\tofs += len;\n\n\t\tpak->num_files++;\n\t}\n\n\t// fix up file data offsets\n\tofs = VFS_TELL(file);\n\tfor (i = 0; i < pak->num_files; i++)\n\t\tpak->files[i].ofs += ofs;\n\n\t// setup info\n\tpak->references++;\n\tpak->handle = file;\n\tstrcpy(pak->desc, desc);\n\tVFS_SEEK(pak->handle, 0);\n\tpak->mutex = Sys_CreateMutex();\n\n\t// setup funcs\n\tpak->pub.fsver = FSVER;\n\tpak->pub.GetPathDetails = FSGMA_GetPathDetails;\n\tpak->pub.ClosePath = FSGMA_ClosePath;\n\tpak->pub.BuildHash = FSGMA_BuildHash;\n\tpak->pub.FindFile = FSGMA_FindFile;\n\tpak->pub.ReadFile = FSGMA_ReadFile;\n\tpak->pub.EnumerateFiles = FSGMA_EnumerateFiles;\n\tpak->pub.GeneratePureCRC = FSGMA_GeneratePureCRC;\n\tpak->pub.OpenVFS = FSGMA_OpenVFS;\n\n\treturn (searchpathfuncs_t *)pak;\n}\n\nqboolean GMA_Init(void)\n{\n\t// get funcs\n\tthreadfuncs = plugfuncs->GetEngineInterface(plugthreadfuncs_name, sizeof(*threadfuncs));\n\tfilefuncs = plugfuncs->GetEngineInterface(plugfsfuncs_name, sizeof(*filefuncs));\n\tif (!filefuncs) // threadfuncs optional\n\t\treturn false;\n\n\t// export our fs function\n\tif (!plugfuncs->ExportFunction(\"FS_RegisterArchiveType_gma\", FSGMA_LoadArchive))\n\t\treturn false;\n\n\treturn true;\n}\n"
  },
  {
    "path": "plugins/hl2/fs_vpk.c",
    "content": "#include \"../plugin.h\"\n#include \"../../engine/common/fs.h\"\n\nplugfsfuncs_t *filefuncs;\n\nstatic plugthreadfuncs_t *threading;\n#define Sys_CreateMutex() (threading?threading->CreateMutex():NULL)\n#define Sys_LockMutex(m) (threading?threading->LockMutex(m):true)\n#define Sys_UnlockMutex if(threading)threading->UnlockMutex\n#define Sys_DestroyMutex if(threading)threading->DestroyMutex\n\n\n\n//\n// in memory\n//\n\ntypedef struct\n{\n\tfsbucket_t bucket;\n\n\tchar\tname[MAX_QPATH];\n\tconst struct dvpkfile_s *file;\n} mvpkfile_t;\n\ntypedef struct vpk_s\n{\n\tsearchpathfuncs_t pub;\n\tchar\tdescname[MAX_OSPATH];\n\n\tqbyte *treedata;\t//raw file list\n\tsize_t treesize;\n\n\tstruct vpk_s\t**fragments;\n\tsize_t numfragments;\n\n\tvoid\t\t*mutex;\n\tvfsfile_t\t*handle;\n\tunsigned int filepos;\t//the pos the subfiles left it at (to optimize calls to vfs_seek)\n\tint references;\t//seeing as all vfiles from a pak file use the parent's vfsfile, we need to keep the parent open until all subfiles are closed.\n\n\tsize_t\t\tnumfiles;\n\tmvpkfile_t\tfiles[1];\t//processed file list\n} vpk_t;\n\n//\n// on disk\n//\ntypedef struct dvpkfile_s\n{\t//chars because these are misaligned.\n\tunsigned char crc[4];\n\tunsigned char preloadsize[2];\n\tunsigned char archiveindex[2];\n\tunsigned char archiveoffset[4];\n\tunsigned char archivesize[4];\n\tunsigned char sentinel[2];//=0xffff;\n} dvpkfile_t;\n\ntypedef struct\n{\n\t//v1\n\tunsigned int magic;\n\tunsigned int version;\n\tunsigned int tablesize;\n\n\t//v2\n\tunsigned int filedatasize;\n\tunsigned int archivemd5size;\n\tunsigned int globalmd5size;\n\tunsigned int signaturesize;\n} dvpkheader_t;\n\nstatic void QDECL FSVPK_GetPathDetails(searchpathfuncs_t *handle, char *out, size_t outlen)\n{\n\tvpk_t *pak = (void*)handle;\n\n\t*out = 0;\n\tif (pak->references != 1)\n\t\tQ_snprintfz(out, outlen, \"(%i)\", pak->references-1);\n}\nstatic void QDECL FSVPK_ClosePath(searchpathfuncs_t *handle)\n{\n\tqboolean stillopen;\n\tsize_t i;\n\tvpk_t *pak = (void*)handle;\n\n\tif (!Sys_LockMutex(pak->mutex))\n\t\treturn;\t//ohnoes\n\tstillopen = --pak->references > 0;\n\tSys_UnlockMutex(pak->mutex);\n\tif (stillopen)\n\t\treturn;\t//not free yet\n\n\n\tVFS_CLOSE (pak->handle);\n\n\tSys_DestroyMutex(pak->mutex);\n\tfor (i = 0; i < pak->numfragments; i++)\n\t{\n\t\tif (pak->fragments[i])\n\t\t\tpak->fragments[i]->pub.ClosePath(&pak->fragments[i]->pub);\n\t\tpak->fragments[i] = NULL;\n\t}\n\tplugfuncs->Free(pak->fragments);\n\tplugfuncs->Free(pak->treedata);\n\tplugfuncs->Free(pak);\n}\nstatic void QDECL FSVPK_BuildHash(searchpathfuncs_t *handle, int depth, void (QDECL *AddFileHash)(int depth, const char *fname, fsbucket_t *filehandle, void *pathhandle))\n{\n\tvpk_t *pak = (void*)handle;\n\tint i;\n\n\tfor (i = 0; i < pak->numfiles; i++)\n\t{\n\t\tAddFileHash(depth, pak->files[i].name, &pak->files[i].bucket, &pak->files[i]);\n\t}\n}\nstatic unsigned int QDECL FSVPK_FLocate(searchpathfuncs_t *handle, flocation_t *loc, const char *filename, void *hashedresult)\n{\n\tmvpkfile_t *pf = hashedresult;\n\tint i;\n\tvpk_t\t\t*pak = (void*)handle;\n\n// look through all the pak file elements\n\n\tif (pf)\n\t{\t//is this a pointer to a file in this pak?\n\t\tif (pf < pak->files || pf > pak->files + pak->numfiles)\n\t\t\treturn FF_NOTFOUND;\t//was found in a different path\n\t}\n\telse\n\t{\n\t\tchar fname[MAX_QPATH];\n\t\tfor (i = 0; i < MAX_QPATH-1 && filename[i]; i++)\n\t\t{\n\t\t\tif (filename[i] >= 'A' && filename[i] <= 'Z')\n\t\t\t\tfname[i] = filename[i] - 'A' + 'a';\n\t\t\telse\n\t\t\t\tfname[i] = filename[i];\n\t\t}\n\t\tfname[i] = 0;\n\t\tfor (i=0 ; i<pak->numfiles ; i++)\t//look for the file\n\t\t{\n\t\t\tif (!strcmp (pak->files[i].name, fname))\n\t\t\t{\n\t\t\t\tpf = &pak->files[i];\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\tif (pf)\n\t{\n\t\tif (loc)\n\t\t{\n\t\t\tloc->fhandle = pf;\n\t\t\t*loc->rawname = 0;\n\t\t\tloc->offset = (qofs_t)-1;\n\t\t\tloc->len =\t ((pf->file->preloadsize[0]<<0)|(pf->file->preloadsize[1]<<8))\n\t\t\t\t\t\t+((pf->file->archivesize[0]<<0)|(pf->file->archivesize[1]<<8)|(pf->file->archivesize[2]<<16)|(pf->file->archivesize[3]<<24));\n\t\t}\n\t\treturn FF_FOUND;\n\t}\n\treturn FF_NOTFOUND;\n}\nstatic int QDECL FSVPK_EnumerateFiles (searchpathfuncs_t *handle, const char *match, int (QDECL *func)(const char *, qofs_t, time_t mtime, void *, searchpathfuncs_t *spath), void *parm)\n{\n\tvpk_t\t*pak = (void*)handle;\n\tint\t\tnum;\n\tmvpkfile_t *file;\n\tqofs_t\tsize;\n\n\tfor (num = 0; num<(int)pak->numfiles; num++)\n\t{\n\t\tif (filefuncs->WildCmp(match, pak->files[num].name))\n\t\t{\n\t\t\tfile = &pak->files[num];\n\t\t\t//FIXME: time 0? maybe use the pak's mtime?\n\t\t\tsize = ((file->file->preloadsize[0]<<0)|(file->file->preloadsize[1]<<8))\n\t\t\t\t  +((file->file->archivesize[0]<<0)|(file->file->archivesize[1]<<8)|(file->file->archivesize[2]<<16)|(file->file->archivesize[3]<<24));\n\t\t\tif (!func(file->name, size, 0, parm, handle))\n\t\t\t\treturn false;\n\t\t}\n\t}\n\n\treturn true;\n}\n\nstatic int QDECL FSVPK_GeneratePureCRC(searchpathfuncs_t *handle, const int *seed)\n{\n\tvpk_t *pak = (void*)handle;\n\n\treturn filefuncs->BlockChecksum(pak->treedata, pak->treesize);\n}\n\ntypedef struct {\n\tvfsfile_t funcs;\n\tvpk_t *parentpak;\n\tconst qbyte *preloaddata;\n\tsize_t preloadsize;\n\tqofs_t startpos;\n\tqofs_t length;\n\tqofs_t currentpos;\n} vfsvpk_t;\nstatic int QDECL VFSVPK_ReadBytes (struct vfsfile_s *vfs, void *buffer, int bytestoread)\n{\n\tvfsvpk_t *vfsp = (void*)vfs;\n\tint read, preread;\n\n\tif (bytestoread <= 0)\n\t\treturn 0;\n\n\tif (vfsp->currentpos < vfsp->preloadsize)\n\t{\n\t\tpreread = bytestoread;\n\t\tif (preread > vfsp->preloadsize-vfsp->currentpos)\n\t\t\tpreread = vfsp->preloadsize-vfsp->currentpos;\n\t\tif (preread < 0)\n\t\t\treturn -1;\t//erk...\n\t\tmemcpy(buffer, vfsp->preloaddata+vfsp->currentpos, preread);\n\t\tvfsp->currentpos += preread;\n\t\tif (preread == bytestoread)\n\t\t\treturn preread;\t//we're done, no need to seek etc\n\t\tbytestoread -= preread;\n\t\tbuffer = (char*)buffer+preread;\n\t}\n\telse\n\t\tpreread = 0;\n\n\tif (vfsp->currentpos + bytestoread > vfsp->length)\n\t\tbytestoread = vfsp->length - vfsp->currentpos;\n\tif (bytestoread <= 0)\n\t{\n\t\tif (preread)\n\t\t\treturn preread;\n\t\treturn -1;\n\t}\n\n\tif (Sys_LockMutex(vfsp->parentpak->mutex))\n\t{\n\t\tif (vfsp->parentpak->filepos != vfsp->startpos+vfsp->currentpos-vfsp->preloadsize)\n\t\t\tVFS_SEEK(vfsp->parentpak->handle, vfsp->startpos+vfsp->currentpos-vfsp->preloadsize);\n\t\tread = VFS_READ(vfsp->parentpak->handle, buffer, bytestoread);\n\t\tif (read > 0)\n\t\t{\n\t\t\tvfsp->currentpos += read;\n\t\t\tread += preread;\n\t\t}\n\t\telse if (preread)\n\t\t\tread = preread;\n\t\tvfsp->parentpak->filepos = vfsp->startpos+vfsp->currentpos-vfsp->preloadsize;\n\t\tSys_UnlockMutex(vfsp->parentpak->mutex);\n\t}\n\telse\n\t\tread = preread;\n\n\treturn read;\n}\nstatic int QDECL VFSVPK_WriteBytes (struct vfsfile_s *vfs, const void *buffer, int bytestoread)\n{\t//not supported.\n\tplugfuncs->Error(\"Cannot write to vpk files\\n\");\n\treturn 0;\n}\nstatic qboolean QDECL VFSVPK_Seek (struct vfsfile_s *vfs, qofs_t pos)\n{\n\tvfsvpk_t *vfsp = (void*)vfs;\n\tif (pos > vfsp->length)\n\t\treturn false;\n\tvfsp->currentpos = pos;// + vfsp->startpos;\n\n\treturn true;\n}\nstatic qofs_t QDECL VFSVPK_Tell (struct vfsfile_s *vfs)\n{\n\tvfsvpk_t *vfsp = (void*)vfs;\n\treturn vfsp->currentpos;// - vfsp->startpos;\n}\nstatic qofs_t QDECL VFSVPK_GetLen (struct vfsfile_s *vfs)\n{\n\tvfsvpk_t *vfsp = (void*)vfs;\n\treturn vfsp->length;\n}\nstatic qboolean QDECL VFSVPK_Close(vfsfile_t *vfs)\n{\n\tvfsvpk_t *vfsp = (void*)vfs;\n\tFSVPK_ClosePath(&vfsp->parentpak->pub);\t//tell the parent that we don't need it open any more (reference counts)\n\tplugfuncs->Free(vfsp);\t//free ourselves.\n\treturn true;\n}\nstatic vfsfile_t *QDECL FSVPK_OpenVFS(searchpathfuncs_t *handle, flocation_t *loc, const char *mode)\n{\n\tvpk_t *pack = (void*)handle;\n\tvfsvpk_t *vfs;\n\tmvpkfile_t *f = loc->fhandle;\n\tunsigned int frag;\n\n\tif (strcmp(mode, \"rb\"))\n\t\treturn NULL; //urm, unable to write/append\n\n\tfrag = (f->file->archiveindex[0]<<0)|(f->file->archiveindex[1]<<8);\n\tif (frag >= pack->numfragments || !pack->fragments[frag])\n\t\treturn NULL;\n\tpack = pack->fragments[frag];\n\n\tvfs = plugfuncs->Malloc(sizeof(vfsvpk_t));\n\n\tvfs->parentpak = pack;\n\tif (!Sys_LockMutex(pack->mutex))\n\t{\n\t\tplugfuncs->Free(vfs);\n\t\treturn NULL;\n\t}\n\tvfs->parentpak->references++;\n\tSys_UnlockMutex(pack->mutex);\n\n\tvfs->preloaddata = (const qbyte*)f->file + sizeof(*f->file);\n\tvfs->preloadsize = (f->file->preloadsize[0]<<0) | (f->file->preloadsize[1]<<8);\n\n\tvfs->startpos = (f->file->archiveoffset[0]<<0)|(f->file->archiveoffset[1]<<8)|(f->file->archiveoffset[2]<<16)|(f->file->archiveoffset[3]<<24);\n\tvfs->length = loc->len;\n\tvfs->currentpos = 0;\n\n#ifdef _DEBUG\n\t{\n\t\tmvpkfile_t *pf = loc->fhandle;\n\t\tQ_strlcpy(vfs->funcs.dbgname, pf->name, sizeof(vfs->funcs.dbgname));\n\t}\n#endif\n\tvfs->funcs.Close = VFSVPK_Close;\n\tvfs->funcs.GetLen = VFSVPK_GetLen;\n\tvfs->funcs.ReadBytes = VFSVPK_ReadBytes;\n\tvfs->funcs.Seek = VFSVPK_Seek;\n\tvfs->funcs.Tell = VFSVPK_Tell;\n\tvfs->funcs.WriteBytes = VFSVPK_WriteBytes;\t//not supported\n\n\treturn (vfsfile_t *)vfs;\n}\n\nstatic void QDECL FSVPK_ReadFile(searchpathfuncs_t *handle, flocation_t *loc, char *buffer)\n{\n\tvfsfile_t *f;\n\tf = FSVPK_OpenVFS(handle, loc, \"rb\");\n\tif (!f)\t//err...\n\t\treturn;\n\tVFS_READ(f, buffer, loc->len);\n\tVFS_CLOSE(f);\n}\n\nstatic unsigned int FSVPK_WalkTree(vpk_t *vpk, const char *start, const char *end)\n{\t//the weird arrangement of these files is presumably an indicator of how source handles its file types.\n\tconst char *ext, *path, *name;\n\tconst dvpkfile_t *file;\n\tsize_t preloadsize;\n\tunsigned int files = 0;\n\twhile(start < end)\n\t{\t//extensions\n\t\text = start;\n\t\tif (!*ext)\n\t\t{\n\t\t\tstart++;\n\t\t\tbreak;\n\t\t}\n\t\tif (ext[0] == ' ' && !ext[1])\n\t\t\text = \"\";\n\t\tstart += strlen(start)+1;\n\t\twhile(start < end)\n\t\t{\t//paths\n\t\t\tpath = start;\n\t\t\tif (!*path)\n\t\t\t{\n\t\t\t\tstart++;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (path[0] == ' ' && !path[1])\n\t\t\t\tpath = \"\";\n\t\t\tstart += strlen(start)+1;\n\t\t\twhile(start < end)\n\t\t\t{\t//names\n\t\t\t\tname = start;\n\t\t\t\tif (!*name)\n\t\t\t\t{\n\t\t\t\t\tstart++;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tif (name[0] == ' ' && !name[1])\n\t\t\t\t\tname = \"\";\n\t\t\t\tstart += strlen(start)+1;\n\n\t\t\t\tfile = (const dvpkfile_t*)start;\n\t\t\t\tpreloadsize = (file->preloadsize[0]<<0) | (file->preloadsize[1]<<8);\n\t\t\t\tstart += sizeof(*file)+preloadsize;\n\t\t\t\tif (start > end)\n\t\t\t\t\treturn 0;\t//truncated...\n\t\t\t\tif (file->sentinel[0] != 0xff || file->sentinel[1] != 0xff)\n\t\t\t\t\treturn 0;\t//sentinel failure\n//\t\t\t\tCon_Printf(\"Found file %s%s%s%s%s\\n\", path, *path?\"/\":\"\", name, *ext?\".\":\"\", ext);\n\t\t\t\tif (!vpk)\n\t\t\t\t\tfiles++;\n\t\t\t\telse if (files < vpk->numfiles)\n\t\t\t\t{\n\t\t\t\t\tunsigned int frag = (file->archiveindex[0]<<0)|(file->archiveindex[1]<<8);\n\t\t\t\t\tQ_snprintfz(vpk->files[files].name, sizeof(vpk->files[files].name), \"%s%s%s%s%s\", path, *path?\"/\":\"\", name, *ext?\".\":\"\", ext);\n\t\t\t\t\tfilefuncs->CleanUpPath(vpk->files[files].name);\t//just in case...\n\t\t\t\t\tvpk->files[files].file = file;\n\n\t\t\t\t\tif (vpk->numfragments < frag+1)\n\t\t\t\t\t\tvpk->numfragments = frag+1;\n\t\t\t\t\tfiles++;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn files;\n}\n\nsearchpathfuncs_t *QDECL FSVVPK_LoadArchive(vfsfile_t *file, searchpathfuncs_t *parent, const char *filename, const char *desc, const char *prefix);\n\n/*\n=================\nCOM_LoadPackFile\n\nTakes an explicit (not game tree related) path to a pak file.\n\nLoads the header and directory, adding the files at the beginning\nof the list so they override previous pack files.\n=================\n*/\nstatic searchpathfuncs_t *QDECL FSVPK_LoadArchive (vfsfile_t *file, searchpathfuncs_t *parent, const char *filename, const char *desc, const char *prefix)\n{\n\tdvpkheader_t\theader;\n\tint\t\t\t\ti;\n\tint\t\t\t\tnumpackfiles;\n\tvpk_t\t\t\t*vpk, *f;\n\tvfsfile_t\t\t*packhandle;\n\tint\t\t\t\tread;\n\tqbyte\t\t\t*tree;\n\tunsigned int\tfrag;\n\tunsigned int tablesize;\n\n\tpackhandle = file;\n\tif (packhandle == NULL)\n\t\treturn NULL;\n\n\tif (prefix && *prefix)\n\t\treturn NULL;\t//not supported at this time\n\n\tread = VFS_READ(packhandle, &header, sizeof(header));\n\theader.magic = LittleLong(header.magic);\n\theader.version = LittleLong(header.version);\n\theader.tablesize = LittleLong(header.tablesize);\n\n\theader.filedatasize = LittleLong(header.filedatasize);\n\theader.archivemd5size = LittleLong(header.archivemd5size);\n\theader.globalmd5size = LittleLong(header.globalmd5size);\n\theader.signaturesize = LittleLong(header.signaturesize);\n\n\tif (read < 12 || header.magic != 0x55aa1234 || header.tablesize <= 0)\n\t{\t//this will include the non-dir files too.\n//\t\tCon_Printf(\"%s is not a vpk\\n\", desc);\n\n\t\t/* HACK: pre 2009 Left4Dead */\n\t\tif (header.magic == 7630198) {\n\t\t\tVFS_SEEK(packhandle, 0);\n\t\t\ttablesize = 1313113;\n\t\t} else {\n\t\t\t// try to load as vtmb vpk\n\t\t\treturn FSVVPK_LoadArchive(file, parent, filename, desc, prefix);\n\t\t}\n\t} else {\n\t\ti = LittleLong(header.version);\n\t\tif (i == 2)\n\t\t\t;//VFS_SEEK(packhandle, 7*sizeof(int));\n\t\telse if (i == 1)\n\t\t\tVFS_SEEK(packhandle, 3*sizeof(int));\n\t\telse\n\t\t{\n\t\t\tCon_Printf(\"vpk %s is version %x (unspported)\\n\", desc, i);\n\t\t\treturn NULL;\n\t\t}\n\t\ttablesize = header.tablesize;\n\t}\n\t\n\ttree = plugfuncs->Malloc(tablesize);\n\tread = VFS_READ(packhandle, tree, tablesize);\n\n\tnumpackfiles = FSVPK_WalkTree(NULL, tree, tree+read);\n\n\tvpk = (vpk_t*)plugfuncs->Malloc (sizeof (*vpk) + sizeof(*vpk->files)*(numpackfiles-1));\n\tvpk->treedata = tree;\n\tvpk->treesize = read;\n\tvpk->numfiles = numpackfiles;\n\tvpk->numfragments = 0;\n\tvpk->numfiles = FSVPK_WalkTree(vpk, tree, tree+read);\n\n\tstrcpy (vpk->descname, desc);\n\tvpk->handle = packhandle;\n\tvpk->filepos = 0;\n\tVFS_SEEK(packhandle, vpk->filepos);\n\n\tvpk->references++;\n\n\tvpk->mutex = Sys_CreateMutex();\n\n\tCon_Printf (\"Added vpkfile %s (%i files)\\n\", desc, numpackfiles);\n\n\tvpk->pub.fsver\t\t\t= FSVER;\n\tvpk->pub.GetPathDetails = FSVPK_GetPathDetails;\n\tvpk->pub.ClosePath = FSVPK_ClosePath;\n\tvpk->pub.BuildHash = FSVPK_BuildHash;\n\tvpk->pub.FindFile = FSVPK_FLocate;\n\tvpk->pub.ReadFile = FSVPK_ReadFile;\n\tvpk->pub.EnumerateFiles = FSVPK_EnumerateFiles;\n\tvpk->pub.GeneratePureCRC = FSVPK_GeneratePureCRC;\n\tvpk->pub.OpenVFS = FSVPK_OpenVFS;\n\n\tvpk->fragments = plugfuncs->Malloc(vpk->numfragments*sizeof(*vpk->fragments));\n\tfor(frag = 0; frag < vpk->numfragments; frag++)\n\t{\n\t\tflocation_t loc;\n\t\tchar fragname[MAX_OSPATH], *ext;\n\t\tQ_strlcpy(fragname, filename, sizeof(fragname));\n\t\text = strrchr(fragname, '.');\n\t\tif (!ext)\n\t\t\text = fragname + strlen(fragname);\n\t\tif (ext-fragname>4 && !strncmp(ext-4, \"_dir\", 4))\n\t\t\text-=4;\n\t\tQ_snprintfz(ext, sizeof(fragname)-(ext-fragname), \"_%03u.vpk\", frag);\n\t\tif (parent->FindFile(parent, &loc, fragname, NULL) != FF_FOUND)\n\t\t\tcontinue;\n\t\tpackhandle = parent->OpenVFS(parent, &loc, \"rb\");\n\t\tif (!packhandle)\n\t\t\tcontinue;\n\n\t\tvpk->fragments[frag] = f = (vpk_t*)plugfuncs->Malloc(sizeof(*f));\n//\t\tQ_strncpyz(f->descname, splitname, sizeof(f->descname));\n\t\tf->handle = packhandle;\n//\t\tf->rawsize = VFS_GETLEN(f->raw);\n\t\tf->references = 1;\n\t\tf->mutex = Sys_CreateMutex();\n\t\tf->pub.ClosePath\t\t\t= FSVPK_ClosePath;\n\t}\n\treturn &vpk->pub;\n}\n\nqboolean VVPK_Init(void);\n\nqboolean VPK_Init(void)\n{\n\tthreading = plugfuncs->GetEngineInterface(plugthreadfuncs_name, sizeof(*threading));\n\tif (!threading)\n\t\treturn false;\n\tfilefuncs = plugfuncs->GetEngineInterface(plugfsfuncs_name, sizeof(*filefuncs));\n\tif (!filefuncs)\n\t\treturn false;\n\n\t// vtmb\n\tif (!VVPK_Init())\n\t\treturn false;\n\n\t//we can't cope with being closed randomly. files cannot be orphaned safely.\n\t//so ask the engine to ensure we don't get closed before everything else is.\n\tplugfuncs->ExportFunction(\"MayShutdown\", NULL);\n\n\tif (!plugfuncs->ExportFunction(\"FS_RegisterArchiveType_vpk\", FSVPK_LoadArchive))\n\t{\n\t\tCon_Printf(\"hl2: Engine doesn't support filesystem plugins\\n\");\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\n"
  },
  {
    "path": "plugins/hl2/fs_vpk_vtmb.c",
    "content": "#include \"../plugin.h\"\n#include \"../../engine/common/fs.h\"\n\nstatic plugfsfuncs_t *filefuncs = NULL;\nstatic plugthreadfuncs_t *threadfuncs = NULL;\n#define Sys_CreateMutex() (threadfuncs?threadfuncs->CreateMutex():NULL)\n#define Sys_LockMutex(m) (threadfuncs?threadfuncs->LockMutex(m):true)\n#define Sys_UnlockMutex if(threadfuncs)threadfuncs->UnlockMutex\n#define Sys_DestroyMutex if(threadfuncs)threadfuncs->DestroyMutex\n\ntypedef struct vvpkfile {\n\tfsbucket_t bucket;\n\tchar name[MAX_QPATH];\n\tsize_t ofs;\n\tsize_t len;\n} vvpkfile_t;\n\ntypedef struct vvpk {\n\tsearchpathfuncs_t pub;\n\tchar desc[MAX_OSPATH];\n\tvfsfile_t *handle;\n\tsize_t num_files;\n\tvvpkfile_t *files;\n\tvoid *mutex;\n\tint references;\n} vvpk_t;\n\nstatic void QDECL FSVVPK_GetPathDetails(searchpathfuncs_t *handle, char *out, size_t outlen)\n{\n\tvvpk_t *pak = (vvpk_t *)handle;\n\t*out = 0;\n\tif (pak->references != 1)\n\t\tQ_snprintfz(out, outlen, \"(%i)\", pak->references - 1);\n}\n\nstatic void QDECL FSVVPK_ClosePath(searchpathfuncs_t *handle)\n{\n\tqboolean stillopen;\n\tvvpk_t *pak = (vvpk_t *)handle;\n\n\tif (!Sys_LockMutex(pak->mutex))\n\t\treturn; //ohnoes\n\tstillopen = --pak->references > 0;\n\tSys_UnlockMutex(pak->mutex);\n\tif (stillopen)\n\t\treturn; //not free yet\n\n\tVFS_CLOSE(pak->handle);\n\n\tSys_DestroyMutex(pak->mutex);\n\n\tplugfuncs->Free(pak->files);\n\tplugfuncs->Free(pak);\n}\n\nstatic void QDECL FSVVPK_BuildHash(searchpathfuncs_t *handle, int depth, void (QDECL *AddFileHash)(int depth, const char *fname, fsbucket_t *filehandle, void *pathhandle))\n{\n\tvvpk_t *pak = (vvpk_t *)handle;\n\tint i;\n\tfor (i = 0; i < pak->num_files; i++)\n\t\tAddFileHash(depth, pak->files[i].name, &pak->files[i].bucket, &pak->files[i]);\n}\n\nstatic unsigned int QDECL FSVVPK_FindFile(searchpathfuncs_t *handle, flocation_t *loc, const char *filename, void *hashedresult)\n{\n\tvvpkfile_t *file = (vvpkfile_t *)hashedresult;\n\tvvpk_t *pak = (vvpk_t *)handle;\n\tint i;\n\n\tif (file)\n\t{\n\t\t//is this a pointer to a file in this pak?\n\t\tif (file < pak->files || file > pak->files + pak->num_files)\n\t\t\treturn FF_NOTFOUND;\n\t}\n\telse\n\t{\n\t\tfor (i = 0; i < pak->num_files; i++)\n\t\t{\n\t\t\tif (strcasecmp(filename, pak->files[i].name) == 0)\n\t\t\t{\n\t\t\t\tfile = &pak->files[i];\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\tif (file)\n\t{\n\t\tif (loc)\n\t\t{\n\t\t\tloc->fhandle = file;\n\t\t\t*loc->rawname = 0;\n\t\t\tloc->offset = (qofs_t)-1;\n\t\t\tloc->len = file->len;\n\t\t}\n\n\t\treturn FF_FOUND;\n\t}\n\n\treturn FF_NOTFOUND;\n}\n\ntypedef struct vfsvvpk {\n\tvfsfile_t funcs;\n\tvvpk_t *parent;\n\tqofs_t startpos;\n\tqofs_t length;\n\tqofs_t currentpos;\n} vfsvvpk_t;\n\nstatic int QDECL VFSVVPK_ReadBytes(struct vfsfile_s *vfs, void *buffer, int bytestoread)\n{\n\tvfsvvpk_t *vfsp = (vfsvvpk_t *)vfs;\n\tint read = 0;\n\n\tif (bytestoread + vfsp->currentpos > vfsp->length)\n\t\tbytestoread = vfsp->length - vfsp->currentpos;\n\n\tif (Sys_LockMutex(vfsp->parent->mutex))\n\t{\n\t\tVFS_SEEK(vfsp->parent->handle, vfsp->startpos + vfsp->currentpos);\n\t\tread = VFS_READ(vfsp->parent->handle, buffer, bytestoread);\n\t\tvfsp->currentpos += read;\n\t\tSys_UnlockMutex(vfsp->parent->mutex);\n\t}\n\n\treturn read;\n}\n\nstatic int QDECL VFSVVPK_WriteBytes(struct vfsfile_s *vfs, const void *buffer, int bytestoread)\n{\n\tplugfuncs->Error(\"Cannot write to vpk files\\n\");\n\treturn 0;\n}\n\nstatic qofs_t QDECL VFSVVPK_Tell(struct vfsfile_s *vfs)\n{\n\tvfsvvpk_t *vfsp = (vfsvvpk_t *)vfs;\n\treturn vfsp->currentpos;\n}\n\nstatic qofs_t QDECL VFSVVPK_GetLen(struct vfsfile_s *vfs)\n{\n\tvfsvvpk_t *vfsp = (vfsvvpk_t *)vfs;\n\treturn vfsp->length;\n}\n\nstatic qboolean QDECL VFSVVPK_Close(vfsfile_t *vfs)\n{\n\tvfsvvpk_t *vfsp = (vfsvvpk_t *)vfs;\n\tFSVVPK_ClosePath(&vfsp->parent->pub); //tell the parent that we don't need it open any more (reference counts)\n\tplugfuncs->Free(vfsp); //free ourselves.\n\treturn true;\n}\n\nstatic qboolean QDECL VFSVVPK_Seek(struct vfsfile_s *vfs, qofs_t pos)\n{\n\tvfsvvpk_t *vfsp = (vfsvvpk_t *)vfs;\n\tif (pos > vfsp->length)\n\t\treturn false;\n\tvfsp->currentpos = pos;\n\treturn true;\n}\n\nstatic vfsfile_t *QDECL FSVVPK_OpenVFS(searchpathfuncs_t *handle, flocation_t *loc, const char *mode)\n{\n\tvvpk_t *pak = (vvpk_t *)handle;\n\tvfsvvpk_t *vfs;\n\tvvpkfile_t *f = (vvpkfile_t *)loc->fhandle;\n\n\tif (strcmp(mode, \"rb\") != 0)\n\t\treturn NULL;\n\n\tvfs = plugfuncs->Malloc(sizeof(vfsvvpk_t));\n\n\tvfs->parent = pak;\n\tif (!Sys_LockMutex(vfs->parent->mutex))\n\t{\n\t\tplugfuncs->Free(vfs);\n\t\treturn NULL;\n\t}\n\n\tvfs->parent->references++;\n\tSys_UnlockMutex(vfs->parent->mutex);\n\n\tvfs->startpos = f->ofs;\n\tvfs->length = loc->len;\n\tvfs->currentpos = 0;\n\n#ifdef _DEBUG\n\tQ_strlcpy(vfs->funcs.dbgname, f->name, sizeof(vfs->funcs.dbgname));\n#endif\n\tvfs->funcs.Close = VFSVVPK_Close;\n\tvfs->funcs.GetLen = VFSVVPK_GetLen;\n\tvfs->funcs.ReadBytes = VFSVVPK_ReadBytes;\n\tvfs->funcs.Seek = VFSVVPK_Seek;\n\tvfs->funcs.Tell = VFSVVPK_Tell;\n\tvfs->funcs.WriteBytes = VFSVVPK_WriteBytes; //not supported\n\n\treturn (vfsfile_t *)vfs;\n}\n\nstatic void QDECL FSVVPK_ReadFile(searchpathfuncs_t *handle, flocation_t *loc, char *buffer)\n{\n\tvfsfile_t *f;\n\tf = FSVVPK_OpenVFS(handle, loc, \"rb\");\n\tif (!f)\n\t\treturn;\n\tVFS_READ(f, buffer, loc->len);\n\tVFS_CLOSE(f);\n}\n\nstatic int QDECL FSVVPK_EnumerateFiles(searchpathfuncs_t *handle, const char *match, int (QDECL *func)(const char *, qofs_t, time_t mtime, void *, searchpathfuncs_t *spath), void *parm)\n{\n\tvvpk_t *pak = (vvpk_t *)handle;\n\tint i;\n\n\tfor (i = 0; i < pak->num_files; i++)\n\t{\n\t\tif (filefuncs->WildCmp(match, pak->files[i].name))\n\t\t{\n\t\t\tif (!func(pak->files[i].name, pak->files[i].len, 0, parm, handle))\n\t\t\t\treturn false;\n\t\t}\n\t}\n\n\treturn true;\n}\n\nstatic int QDECL FSVVPK_GeneratePureCRC(searchpathfuncs_t *handle, const int *seed)\n{\n\tvvpk_t *pak = (vvpk_t *)handle;\n\treturn filefuncs->BlockChecksum(pak->files, sizeof(*pak->files) * pak->num_files);\n}\n\nsearchpathfuncs_t *QDECL FSVVPK_LoadArchive(vfsfile_t *file, searchpathfuncs_t *parent, const char *filename, const char *desc, const char *prefix)\n{\n\tvvpk_t *pak;\n\tint i;\n\tint fsize;\n\tuint32_t num_files, ofs_files;\n\tuint8_t sentinel;\n\n\t// sanity checks\n\tif (!file) return NULL;\n\tif (prefix && *prefix) return NULL;\n\n\t// last ditch check to make sure this is a vtmb vpk\n\tif (strncasecmp(filename, \"pack\", 4) != 0)\n\t\treturn NULL;\n\n\t// validate footer\n\tfsize = VFS_GETLEN(file);\n\tif (fsize < 9)\n\t\treturn NULL;\n\tVFS_SEEK(file, fsize - ((sizeof(uint32_t) * 2) + sizeof(uint8_t)));\n\tVFS_READ(file, &num_files, sizeof(num_files));\n\tVFS_READ(file, &ofs_files, sizeof(ofs_files));\n\tVFS_READ(file, &sentinel, sizeof(sentinel));\n\tif (sentinel != 0)\n\t\treturn NULL;\n\n\tnum_files = LittleLong(num_files);\n\tofs_files = LittleLong(ofs_files);\n\n\t// alloc\n\tpak = plugfuncs->Malloc(sizeof(*pak));\n\tpak->files = plugfuncs->Malloc(sizeof(*pak->files) * num_files);\n\n\t// read tree\n\tVFS_SEEK(file, ofs_files);\n\tfor (i = 0; i < num_files; i++)\n\t{\n\t\tuint32_t len_name;\n\t\tuint32_t ofs_data;\n\t\tuint32_t len_data;\n\t\tVFS_READ(file, &len_name, sizeof(len_name));\n\t\tlen_name = LittleLong(len_name);\n\t\tif (len_name > MAX_QPATH - 1)\n\t\t{\n\t\t\tCon_Printf(\"%s: filename in vpk is too long\\n\", filename);\n\t\t\treturn NULL;\n\t\t}\n\n\t\tVFS_READ(file, pak->files[i].name, len_name);\n\t\tVFS_READ(file, &ofs_data, sizeof(ofs_data));\n\t\tVFS_READ(file, &len_data, sizeof(len_data));\n\n\t\tpak->files[i].ofs = LittleLong(ofs_data);\n\t\tpak->files[i].len = LittleLong(len_data);\n\t}\n\n\t// setup info\n\tpak->references++;\n\tpak->num_files = num_files;\n\tpak->handle = file;\n\tstrcpy(pak->desc, desc);\n\tVFS_SEEK(pak->handle, 0);\n\tpak->mutex = Sys_CreateMutex();\n\n\t// setup funcs\n\tpak->pub.fsver = FSVER;\n\tpak->pub.GetPathDetails = FSVVPK_GetPathDetails;\n\tpak->pub.ClosePath = FSVVPK_ClosePath;\n\tpak->pub.BuildHash = FSVVPK_BuildHash;\n\tpak->pub.FindFile = FSVVPK_FindFile;\n\tpak->pub.ReadFile = FSVVPK_ReadFile;\n\tpak->pub.EnumerateFiles = FSVVPK_EnumerateFiles;\n\tpak->pub.GeneratePureCRC = FSVVPK_GeneratePureCRC;\n\tpak->pub.OpenVFS = FSVVPK_OpenVFS;\n\n\treturn (searchpathfuncs_t *)pak;\n}\n\nqboolean VVPK_Init(void)\n{\n\t// get funcs\n\tthreadfuncs = plugfuncs->GetEngineInterface(plugthreadfuncs_name, sizeof(*threadfuncs));\n\tfilefuncs = plugfuncs->GetEngineInterface(plugfsfuncs_name, sizeof(*filefuncs));\n\tif (!filefuncs) // threadfuncs optional\n\t\treturn false;\n\n\treturn true;\n}\n"
  },
  {
    "path": "plugins/hl2/glsl/vmt/lightmapped.glsl",
    "content": "!!ver 110\n!!permu FOG\n!!permu BUMP\n!!permu LIGHTSTYLED\n!!permu FULLBRIGHT\n!!permu REFLECTCUBEMASK\n!!permu NOFOG\n!!samps diffuse\n\n!!samps lightmap\n!!samps =LIGHTSTYLED lightmap1 lightmap2 lightmap3\n\n!!samps =BUMP normalmap\n!!samps =FULLBRIGHT fullbright\n\n// envmaps only\n!!samps =REFLECTCUBEMASK reflectmask reflectcube\n\n!!permu FAKESHADOWS\n!!cvardf r_glsl_pcf\n!!samps =FAKESHADOWS shadowmap\n\n#ifndef ENVTINT\n#define ENVTINT 1.0,1.0,1.0\n#endif\n\n#ifndef ENVSAT\n#define ENVSAT 1.0,1.0,1.0\n#endif\n\n#include \"sys/defs.h\"\n\nvarying vec2 tex_c;\n\nvarying vec2 lm0;\n\n#ifdef LIGHTSTYLED\nvarying vec2 lm1, lm2, lm3;\n#endif\n\n#ifdef FAKESHADOWS\n\tvarying vec4 vtexprojcoord;\n#endif\n\n/* CUBEMAPS ONLY */\n#ifdef REFLECTCUBEMASK\n\tvarying vec3 eyevector;\n\tvarying mat3 invsurface;\n#endif\n\n#ifdef VERTEX_SHADER\n\tvoid lightmapped_init(void)\n\t{\n\t\tlm0 = v_lmcoord;\n\t#ifdef LIGHTSTYLED\n\t\tlm1 = v_lmcoord2;\n\t\tlm2 = v_lmcoord3;\n\t\tlm3 = v_lmcoord4;\n\t#endif\n\t}\n\n\tvoid main ()\n\t{\n\t\tlightmapped_init();\n\t\ttex_c = v_texcoord;\n\t\tgl_Position = ftetransform();\n\n\t/* CUBEMAPS ONLY */\n\t#ifdef REFLECTCUBEMASK\n\t\tinvsurface = mat3(v_svector, v_tvector, v_normal);\n\n\t\tvec3 eyeminusvertex = e_eyepos - v_position.xyz;\n\t\teyevector.x = dot(eyeminusvertex, v_svector.xyz);\n\t\teyevector.y = dot(eyeminusvertex, v_tvector.xyz);\n\t\teyevector.z = dot(eyeminusvertex, v_normal.xyz);\n\t#endif\n\n\t#ifdef FAKESHADOWS\n\t\tvtexprojcoord = (l_cubematrix*vec4(v_position.xyz, 1.0));\n\t#endif\n\t}\n#endif\n\n#ifdef FRAGMENT_SHADER\n\t#include \"sys/fog.h\"\n\t\n#ifdef FAKESHADOWS\n\t#include \"sys/pcf.h\"\n#endif\n\n\t#ifdef LIGHTSTYLED\n\t\t#define LIGHTMAP0 texture2D(s_lightmap0, lm0).rgb\n\t\t#define LIGHTMAP1 texture2D(s_lightmap1, lm1).rgb\n\t\t#define LIGHTMAP2 texture2D(s_lightmap2, lm2).rgb\n\t\t#define LIGHTMAP3 texture2D(s_lightmap3, lm3).rgb\n\t#else\n\t\t#define LIGHTMAP texture2D(s_lightmap, lm0).rgb \n\t#endif\n\n\tvec3 lightmap_fragment()\n\t{\n\t\tvec3 lightmaps;\n\n#ifdef LIGHTSTYLED\n\t\tlightmaps  = LIGHTMAP0 * e_lmscale[0].rgb;\n\t\tlightmaps += LIGHTMAP1 * e_lmscale[1].rgb;\n\t\tlightmaps += LIGHTMAP2 * e_lmscale[2].rgb;\n\t\tlightmaps += LIGHTMAP3 * e_lmscale[3].rgb;\n#else\n\t\tlightmaps  = LIGHTMAP * e_lmscale.rgb;\n#endif\n\t\treturn lightmaps;\n\t}\n\n\tvec3 env_saturation(vec3 rgb, float adjustment) {\n\t    vec3 intensity = vec3(dot(rgb, vec3(0.2126,0.7152,0.0722)));\n\t    return mix(intensity, rgb, adjustment);\n\t}\n\n\tvoid main (void)\n\t{\n\t\tvec4 diffuse_f;\n\n\t\tdiffuse_f = texture2D(s_diffuse, tex_c);\n\t\tdiffuse_f.rgb *= e_colourident.rgb;\n\n#ifdef MASKLT\n\t\tif (diffuse_f.a < float(MASK))\n\t\t\tdiscard;\n#endif\n\n#ifdef FAKESHADOWS\n\t\tdiffuse_f.rgb *= ShadowmapFilter(s_shadowmap, vtexprojcoord);\n#endif\n\n\t\t/* deluxemapping isn't working on Source BSP yet */\n\t\tdiffuse_f.rgb *= lightmap_fragment();\n\n/* CUBEMAPS ONLY */\n#ifdef REFLECTCUBEMASK\n\t/* We currently only use the normal/bumpmap for cubemap warping. move this block out once we do proper radiosity normalmapping */\n\t#ifdef BUMP\n\t\t/* Source's normalmaps are in the DX format where the green channel is flipped */\n\t\tvec4 normal_f = texture2D(s_normalmap, tex_c);\n\t\tnormal_f.g = 1.0 - normal_f.g;\n\t\tnormal_f.rgb = normalize(normal_f.rgb - 0.5);\n\t#else\n\t\tvec4 normal_f = vec4(0.0,0.0,1.0,0.0);\n\t#endif\n\n\n\t#if defined(ENVFROMMASK)\n\t\t/* We have a dedicated reflectmask */\n\t\t#define refl texture2D(s_reflectmask, tex_c).r\n\t#else\n\t\t/* when ENVFROMBASE is set or a normal isn't present, we're getting the reflectivity info from the diffusemap's alpha channel */\n\t\t#if defined(ENVFROMBASE) || !defined(BUMP)\n\t\t\t#define refl 1.0 - diffuse_f.a\n\t\t#else\n\t\t\t/* when ENVFROMNORM is set, we don't invert the refl */\n\t\t\t#if defined(ENVFROMNORM)\n\t\t\t\t#define refl texture2D(s_normalmap, tex_c).a\n\t\t\t#else\n\t\t\t\t#define refl 1.0 - texture2D(s_normalmap, tex_c).a\n\t\t\t#endif\n\t\t#endif\n\t#endif\n\t\n\n\t\tvec3 cube_c = reflect(-eyevector, normal_f.rgb);\n\t\tvec3 cube_tint = vec3(ENVTINT);\n\t\tvec3 cube_sat = vec3(ENVSAT);\n\t\tcube_c = cube_c.x * invsurface[0] + cube_c.y * invsurface[1] + cube_c.z * invsurface[2];\n\t\tcube_c = (m_model * vec4(cube_c.xyz, 0.0)).xyz;\n\t\tvec3 cube_t = env_saturation(textureCube(s_reflectcube, cube_c).rgb, cube_sat.r);\n\t\tcube_t.r *= cube_tint.r;\n\t\tcube_t.g *= cube_tint.g;\n\t\tcube_t.b *= cube_tint.b;\n\t\tdiffuse_f.rgb += (cube_t * vec3(refl,refl,refl));\n#endif\n\n\t#ifdef FULLBRIGHT\n\t\tdiffuse_f.rgb += texture2D(s_fullbright, tex_c).rgb * texture2D(s_fullbright, tex_c).a;\n\t#endif\n\n\t#ifdef NOFOG\n\t\tgl_FragColor = diffuse_f;\n\t#else\n\t\tgl_FragColor = fog4(diffuse_f);\n\t#endif\n\t}\n#endif\n"
  },
  {
    "path": "plugins/hl2/glsl/vmt/refract.glsl",
    "content": "!!ver 110\n!!permu BUMP\n!!samps diffuse\n!!samps =BUMP normalmap\n!!samps refraction=0 dudvmap=1\n\n#include \"sys/defs.h\"\n\nvarying vec2 tex_c;\nvarying mat3 invsurface;\nvarying vec4 tf_c;\nvarying vec3 eyeminusvertex;\n\n#ifdef VERTEX_SHADER\n\tvoid main ()\n\t{\n\t\tinvsurface[0] = v_svector;\n\t\tinvsurface[1] = v_tvector;\n\t\tinvsurface[2] = v_normal;\n\t\ttf_c = ftetransform();\n\t\ttex_c = v_texcoord;\n\t\tgl_Position = tf_c;\n\t}\n#endif\n\n#ifdef FRAGMENT_SHADER\n\t#include \"sys/fog.h\"\n\tvoid main ( void )\n\t{\n\t\tvec2 refl_c;\n\t\tvec3 refr_f;\n\t\tvec3 dudv_f;\n\t\tvec4 out_f = vec4( 1.0, 1.0, 1.0, 1.0 );\n\n\t\tdudv_f = ( texture2D( s_dudvmap, tex_c).xyz);\n\t\tdudv_f += ( texture2D( s_dudvmap, tex_c).xyz);\n\t\tdudv_f -= 1.0 - ( 4.0 / 256.0 );\n\t\tdudv_f = normalize( dudv_f );\n\n\t\t// Reflection/View coordinates\n\t\trefl_c = ( 1.0 + ( tf_c.xy / tf_c.w ) ) * 0.5;\n\n\t\trefr_f = texture2D( s_refraction, refl_c + ( dudv_f.st) ).rgb;\n\t\tout_f.rgb = refr_f;\n#ifdef TINTTEXTURE\n\t\tout_f.rgb *= texture2D( s_diffuse, tex_c ).rgb;\n#endif\n\n\t\tgl_FragColor = out_f;\n\t}\n#endif\n"
  },
  {
    "path": "plugins/hl2/glsl/vmt/rt.glsl",
    "content": "!!ver 110\n!!permu FOG\n!!samps diffuse=0\n\n#include \"sys/defs.h\"\n#include \"sys/fog.h\"\n\nvarying vec2 tex_c;\n\n#ifdef VERTEX_SHADER\nvoid main ()\n{\n\ttex_c = v_texcoord;\n\tgl_Position = ftetransform();\n}\n#endif\n\n#ifdef FRAGMENT_SHADER\nvoid main ()\n{\n\tvec4 diffuse_f = texture2D( s_diffuse, fract(tex_c) );\n\tgl_FragColor = fog4( diffuse_f );\n}\n#endif\n"
  },
  {
    "path": "plugins/hl2/glsl/vmt/transition.glsl",
    "content": "!!ver 110\n!!permu FOG\n!!permu BUMP\n!!permu LIGHTSTYLED\n!!permu REFLECTCUBEMASK\n!!permu UPPERLOWER\n!!samps diffuse upper\n\n!!samps lightmap\n!!samps =LIGHTSTYLED lightmap1 lightmap2 lightmap3\n\n!!samps =BUMP normalmap\n\n// envmaps only\n!!samps =REFLECTCUBEMASK reflectmask reflectcube\n\n!!permu FAKESHADOWS\n!!cvardf r_glsl_pcf\n!!samps =FAKESHADOWS shadowmap\n\n#include \"sys/defs.h\"\n\nvarying vec2 tex_c;\nvarying vec4 vex_color;\n\nvarying vec2 lm0;\n\n#ifdef LIGHTSTYLED\nvarying vec2 lm1, lm2, lm3;\n#endif\n\n#ifdef FAKESHADOWS\n\tvarying vec4 vtexprojcoord;\n#endif\n\n/* CUBEMAPS ONLY */\n#ifdef REFLECTCUBEMASK\n\tvarying vec3 eyevector;\n\tvarying mat3 invsurface;\n#endif\n\n#ifdef VERTEX_SHADER\n\tvoid lightmapped_init(void)\n\t{\n\t\tlm0 = v_lmcoord;\n\t#ifdef LIGHTSTYLED\n\t\tlm1 = v_lmcoord2;\n\t\tlm2 = v_lmcoord3;\n\t\tlm3 = v_lmcoord4;\n\t#endif\n\t}\n\n\tvoid main ()\n\t{\n\t\tlightmapped_init();\n\t\ttex_c = v_texcoord;\n\t\tgl_Position = ftetransform();\n\t\tvex_color = v_colour;\n\n\t/* CUBEMAPS ONLY */\n\t#ifdef REFLECTCUBEMASK\n\t\tinvsurface = mat3(v_svector, v_tvector, v_normal);\n\n\t\tvec3 eyeminusvertex = e_eyepos - v_position.xyz;\n\t\teyevector.x = dot(eyeminusvertex, v_svector.xyz);\n\t\teyevector.y = dot(eyeminusvertex, v_tvector.xyz);\n\t\teyevector.z = dot(eyeminusvertex, v_normal.xyz);\n\t#endif\n\n\t#ifdef FAKESHADOWS\n\t\tvtexprojcoord = (l_cubematrix*vec4(v_position.xyz, 1.0));\n\t#endif\n\t}\n#endif\n\n#ifdef FRAGMENT_SHADER\n\t#include \"sys/fog.h\"\n\t\n#ifdef FAKESHADOWS\n\t#include \"sys/pcf.h\"\n#endif\n\n\t#ifdef LIGHTSTYLED\n\t\t#define LIGHTMAP0 texture2D(s_lightmap0, lm0).rgb\n\t\t#define LIGHTMAP1 texture2D(s_lightmap1, lm1).rgb\n\t\t#define LIGHTMAP2 texture2D(s_lightmap2, lm2).rgb\n\t\t#define LIGHTMAP3 texture2D(s_lightmap3, lm3).rgb\n\t#else\n\t\t#define LIGHTMAP texture2D(s_lightmap, lm0).rgb \n\t#endif\n\n\tvec3 lightmap_fragment()\n\t{\n\t\tvec3 lightmaps;\n\n#ifdef LIGHTSTYLED\n\t\tlightmaps  = LIGHTMAP0 * e_lmscale[0].rgb;\n\t\tlightmaps += LIGHTMAP1 * e_lmscale[1].rgb;\n\t\tlightmaps += LIGHTMAP2 * e_lmscale[2].rgb;\n\t\tlightmaps += LIGHTMAP3 * e_lmscale[3].rgb;\n#else\n\t\tlightmaps  = LIGHTMAP * e_lmscale.rgb;\n#endif\n\n\t\t/* the light we're getting is always too bright */\n\t\tlightmaps *= 0.75;\n\n\t\t/* clamp at 1.5 */\n\t\tif (lightmaps.r > 1.5)\n\t\t\tlightmaps.r = 1.5;\n\t\tif (lightmaps.g > 1.5)\n\t\t\tlightmaps.g = 1.5;\n\t\tif (lightmaps.b > 1.5)\n\t\t\tlightmaps.b = 1.5;\n\n\t\treturn lightmaps;\n\t}\n\n\tvoid main (void)\n\t{\n\t\tvec4 diffuse_f;\n\t\tdiffuse_f.rgb = mix(texture2D(s_diffuse, tex_c).rgb,  texture2D(s_upper, tex_c).rgb, vex_color.a);\n\t\tdiffuse_f.a = 1.0;\n\n\t\t/* deluxemapping isn't working on Source BSP yet, FIXME */\n\t\tdiffuse_f.rgb *= lightmap_fragment();\n\n#ifdef FAKESHADOWS\n\t\tdiffuse_f.rgb *= ShadowmapFilter(s_shadowmap, vtexprojcoord);\n#endif\n\n/* CUBEMAPS ONLY */\n#ifdef REFLECTCUBEMASK\n\t/* We currently only use the normal/bumpmap for cubemap warping. move this block out once we do proper radiosity normalmapping */\n\t#ifdef BUMP\n\t\t/* Source's normalmaps are in the DX format where the green channel is flipped */\n\t\tvec4 normal_f = texture2D(s_normalmap, tex_c);\n\t\tnormal_f.g = 1.0 - normal_f.g;\n\t\tnormal_f.rgb = normalize(normal_f.rgb - 0.5);\n\t#else\n\t\tvec4 normal_f = vec4(0.0,0.0,1.0,0.0);\n\t#endif\n\n\t#if defined(ENVFROMMASK)\n\t\t/* We have a dedicated reflectmask */\n\t\t#define refl texture2D(s_reflectmask, tex_c).r\n\t#else\n\t\t/* when ENVFROMBASE is set or a normal isn't present, we're getting the reflectivity info from the diffusemap's alpha channel */\n\t\t#if defined(ENVFROMBASE) || !defined(BUMP)\n\t\t\t#define refl 1.0 - diffuse_f.a\n\t\t#else\n\t\t\t/* when ENVFROMNORM is set, we don't invert the refl */\n\t\t\t#if defined(ENVFROMNORM)\n\t\t\t\t#define refl texture2D(s_normalmap, tex_c).a\n\t\t\t#else\n\t\t\t\t#define refl 1.0 - texture2D(s_normalmap, tex_c).a\n\t\t\t#endif\n\t\t#endif\n\t#endif\n\n\t\tvec3 cube_c = reflect(normalize(-eyevector), normal_f.rgb);\n\t\tcube_c = cube_c.x * invsurface[0] + cube_c.y * invsurface[1] + cube_c.z * invsurface[2];\n\t\tcube_c = (m_model * vec4(cube_c.xyz, 0.0)).xyz;\n\t\tdiffuse_f.rgb += (textureCube(s_reflectcube, cube_c).rgb * vec3(refl,refl,refl));\n#endif\n\n\t\tgl_FragColor = fog4(diffuse_f);\n\t}\n#endif\n"
  },
  {
    "path": "plugins/hl2/glsl/vmt/unlit.glsl",
    "content": "!!ver 110\n!!permu FOG\n!!samps diffuse\n\n#include \"sys/defs.h\"\n#include \"sys/fog.h\"\n\nvarying vec2 tex_c;\n\n#ifdef VERTEX_SHADER\nvoid main ()\n{\n\ttex_c = v_texcoord;\n\tgl_Position = ftetransform();\n}\n#endif\n\n#ifdef FRAGMENT_SHADER\nvoid main ()\n{\n\tvec4 diffuse_f = texture2D( s_diffuse, tex_c );\n\n#ifdef MASKLT\n\t\tif (diffuse_f.a < float(MASK))\n\t\t\tdiscard;\n#endif\n\n\tgl_FragColor = fog4( diffuse_f );\n}\n#endif\n"
  },
  {
    "path": "plugins/hl2/glsl/vmt/vertexlit.glsl",
    "content": "!!ver 100 150\n!!permu FRAMEBLEND\n!!permu BUMP\n!!permu FOG\n!!permu NOFOG\n!!permu SKELETAL\n!!permu FULLBRIGHT\n!!permu AMBIENTCUBE\n!!permu REFLECTCUBEMASK\n!!samps diffuse\n!!samps =BUMP normalmap\n!!samps =FULLBRIGHT fullbright\n!!samps rtenvsphere:2D=0\n!!permu FAKESHADOWS\n!!cvardf r_glsl_pcf\n!!cvardf r_glsl_rtenvsphere\n!!samps =FAKESHADOWS shadowmap\n\n// envmaps only\n!!samps =REFLECTCUBEMASK reflectmask reflectcube\n!!cvardf r_skipDiffuse\n\n#include \"sys/defs.h\"\n\nvarying vec2 tex_c;\nvarying vec3 norm;\nvarying vec4 light;\n\n/* CUBEMAPS ONLY */\n#ifdef REFLECTCUBEMASK\n\tvarying vec3 eyevector;\n\tvarying mat3 invsurface;\n\n#endif\n\n#ifndef ENVTINT\n#define ENVTINT 1.0,1.0,1.0\n#endif\n\n#ifndef ENVSAT\n#define ENVSAT 1.0\n#endif\n\n\n#ifdef FAKESHADOWS\n\tvarying vec4 vtexprojcoord;\n#endif\n\n#ifdef VERTEX_SHADER\n\t#include \"sys/skeletal.h\"\n\n\tfloat lambert(vec3 normal, vec3 dir)\n\t{\n\t\treturn dot(normal, dir);\n\t}\n\n\tfloat halflambert(vec3 normal, vec3 dir)\n\t{\n\t\treturn (dot(normal, dir) * 0.5) + 0.5;\n\t}\n\n\tvoid main (void)\n\t{\n\t\tvec3 n, s, t, w;\n\t\ttex_c = v_texcoord;\n\t\tgl_Position = skeletaltransform_wnst(w,n,s,t);\n\t\tnorm = n = normalize(n);\n\t\ts = normalize(s);\n\t\tt = normalize(t);\n\t\tlight.rgba = vec4(e_light_ambient, 1.0);\n\n\t#ifdef AMBIENTCUBE\n\t\t//no specular effect here. use rtlights for that.\n\t\tvec3 nn = norm*norm; //FIXME: should be worldspace normal.\n\t\tlight.rgb = nn.x * e_light_ambientcube[(norm.x<0.0)?1:0] +\n\t\t\t\tnn.y * e_light_ambientcube[(norm.y<0.0)?3:2] +\n\t\t\t\tnn.z * e_light_ambientcube[(norm.z<0.0)?5:4];\n\t#else\n\t\t#ifdef HALFLAMBERT\n\t\t\tlight.rgb += max(0.0,halflambert(n,e_light_dir)) * e_light_mul;\n\t\t#else\n\t\t\tlight.rgb += max(0.0,dot(n,e_light_dir)) * e_light_mul;\n\t\t#endif\n\t#endif\n\n/* CUBEMAPS ONLY */\n#ifdef REFLECTCUBEMASK\n\t\tinvsurface = mat3(s, t, n);\n\n\t\tvec3 eyeminusvertex = e_eyepos - w.xyz;\n\t\teyevector.x = dot(eyeminusvertex, s.xyz);\n\t\teyevector.y = dot(eyeminusvertex, t.xyz);\n\t\teyevector.z = dot(eyeminusvertex, n.xyz);\n#endif\n\t\t\n\t\t#ifdef FAKESHADOWS\n\t\tvtexprojcoord = (l_cubematrix*vec4(w.xyz, 1.0));\n\t\t#endif\n\t}\n#endif\n\n\n#ifdef FRAGMENT_SHADER\n\t#include \"sys/fog.h\"\n\t#include \"sys/pcf.h\"\n\n\tvec3 env_saturation(vec3 rgb, float adjustment) {\n\t    vec3 intensity = vec3(dot(rgb, vec3(0.2126,0.7152,0.0722)));\n\t    return mix(intensity, rgb, adjustment);\n\t}\n\n\tvoid main (void)\n\t{\n\t\tvec4 diffuse_f = texture2D(s_diffuse, tex_c);\n\n#ifdef MASKLT\n\t\tif (diffuse_f.a < float(MASK))\n\t\t\tdiscard;\n#endif\n\n/* Normal/Bumpmap Shenanigans */\n#ifdef BUMP\n\t\t/* Source's normalmaps are in the DX format where the green channel is flipped */\n\t\tvec3 normal_f = texture2D(s_normalmap, tex_c).rgb;\n\t\tnormal_f.g = 1.0 - normal_f.g;\n\t\tnormal_f = normalize(normal_f.rgb - 0.5);\n#else\n\t\tvec3 normal_f = vec3(0.0,0.0,1.0);\n#endif\n\n/* CUBEMAPS ONLY */\n#ifdef REFLECTCUBEMASK\n\n\t#if defined(ENVFROMMASK)\n\t\t/* We have a dedicated reflectmask */\n\t\t#define refl texture2D(s_reflectmask, tex_c).r\n\t#else\n\t\t/* when ENVFROMBASE is set or a normal isn't present, we're getting the reflectivity info from the diffusemap's alpha channel */\n\t\t#if defined(ENVFROMBASE) || !defined(BUMP)\n\t\t\t#define refl 1.0 - diffuse_f.a\n\t\t#else\n\t\t\t/* when ENVFROMNORM is set, we don't invert the refl */\n\t\t\t#if defined(ENVFROMNORM)\n\t\t\t\t#define refl texture2D(s_normalmap, tex_c).a\n\t\t\t#else\n\t\t\t\t#define refl 1.0 - texture2D(s_normalmap, tex_c).a\n\t\t\t#endif\n\t\t#endif\n\t#endif\n\n\t\tvec3 cube_tint = vec3(ENVTINT);\n\t\tvec3 cube_sat = vec3(ENVSAT);\n\n\t#if r_glsl_rtenvsphere == 1\n\t\tvec3 r = reflect(normalize(-eyevector), normal_f.rgb);\n\t\tvec2 sphereCoord = 0.5 + r.xy * 0.5;\n\t\tvec3 cube_t = env_saturation(texture2D(s_rtenvsphere, sphereCoord).rgb * 0.5, cube_sat.r);\n\t#else\n\t\tvec3 cube_c = reflect(-eyevector, normal_f.rgb);\n\t\tcube_c = cube_c.x * invsurface[0] + cube_c.y * invsurface[1] + cube_c.z * invsurface[2];\n\t\tcube_c = (m_model * vec4(cube_c.xyz, 0.0)).xyz;\n\t\tvec3 cube_t = env_saturation(textureCube(s_reflectcube, cube_c).rgb, cube_sat.r);\n\t#endif\n\n\t\tcube_t.r *= cube_tint.r;\n\t\tcube_t.g *= cube_tint.g;\n\t\tcube_t.b *= cube_tint.b;\n\t\t\n\t\tdiffuse_f.rgb += (cube_t * vec3(refl,refl,refl));\n#endif\n\n\t\tdiffuse_f.rgb *= light.rgb * e_colourident.rgb;\n\n\t#ifdef FAKESHADOWS\n\t\tdiffuse_f.rgb *= ShadowmapFilter(s_shadowmap, vtexprojcoord);\n\t#endif\n\n\t#ifdef FULLBRIGHT\n\t\tdiffuse_f.rgb += texture2D(s_fullbright, tex_c).rgb * texture2D(s_fullbright, tex_c).a;\n\t#endif\n\n\n\t#if 1\n\t\tgl_FragColor = diffuse_f;\n\t#else\n\t\tgl_FragColor = fog4(diffuse_f);\n\t#endif\n\t}\n#endif\n"
  },
  {
    "path": "plugins/hl2/glsl/vmt/water.glsl",
    "content": "!!cvardf r_glsl_turbscale_reflect=1\t//simpler scaler\n!!cvardf r_glsl_turbscale_refract=1\t//simpler scaler\n!!permu REFLECTCUBEMASK\n!!samps diffuse normalmap\n!!samps\trefract=0\t//always present\n!!samps reflect=1\n!!samps =REFLECTCUBEMASK reflectcube\n!!permu FOG\n\n#include \"sys/defs.h\"\n\n//modifier: REFLECT\t\t(s_t2 is a reflection instead of diffusemap)\n//modifier: STRENGTH_REFL\t(distortion strength - 0.1 = fairly gentle, 0.2 = big waves)\n//modifier: STRENGTH_REFL\t(distortion strength - 0.1 = fairly gentle, 0.2 = big waves)\n//modifier: FRESNEL_EXP\t(5=water)\n//modifier: TXSCALE\t\t(wave size - 0.2)\n//modifier: RIPPLEMAP\t\t(s_t3 contains a ripplemap\n//modifier: TINT_REFR\t\t(some colour value)\n//modifier: TINT_REFL\t\t(some colour value)\n//modifier: ALPHA\t\t(mix in the normal water texture over the top)\n//modifier: USEMODS\t\t(use single-texture scrolling via tcmods - note, also forces the engine to actually use tcmod etc)\n\n//a few notes on DP compat:\n//'dpwater' makes numerous assumptions about DP internals\n//by default there is a single pass that uses the pass's normal tcmods\n//the fresnel has a user-supplied min+max rather than an exponent\n//both parts are tinted individually\n//if alpha is enabled, the regular water texture is blended over the top, again using the same crappy tcmods...\n\n//legacy crap\n#ifndef FRESNEL\n#define FRESNEL 5.0\n#endif\n#ifndef TINT\n#define TINT 0.7,0.8,0.7\n#endif\n#ifndef STRENGTH\n#define STRENGTH 0.25\n#endif\n#ifndef TXSCALE\n#define TXSCALE 1\n#endif\n\n//current values (referring to legacy defaults where needed)\n#ifndef FRESNEL_EXP\n#define FRESNEL_EXP 5.0\n#endif\n#ifndef FRESNEL_MIN\n#define FRESNEL_MIN 0.0\n#endif\n#ifndef FRESNEL_RANGE\n#define FRESNEL_RANGE 1.0\n#endif\n#ifndef STRENGTH_REFL\n#define STRENGTH_REFL STRENGTH\n#endif\n#ifndef STRENGTH_REFR\n#define STRENGTH_REFR STRENGTH\n#endif\n#ifndef TXSCALE1\n#define TXSCALE1 TXSCALE\n#endif\n#ifndef TXSCALE2\n#define TXSCALE2 TXSCALE\n#endif\n#ifndef TINT_REFR\n#define TINT_REFR TINT\n#endif\n#ifndef TINT_REFL\n#define TINT_REFL 1.0,1.0,1.0\n#endif\n#ifndef FOGTINT\n#define FOGTINT 0.2,0.3,0.2\n#endif\n\nvarying vec2 tc;\nvarying vec4 tf;\nvarying vec3 norm;\nvarying vec3 eye;\n\n#ifdef VERTEX_SHADER\nvoid main (void)\n{\n\ttc = v_texcoord.st;\n\ttf = ftetransform();\n\tnorm = v_normal;\n\teye = e_eyepos - v_position.xyz;\n\tgl_Position = ftetransform();\n}\n#endif\n\n#ifdef FRAGMENT_SHADER\n#include \"sys/fog.h\"\n\n\nvoid main (void)\n{\n\tvec2 stc;\t//screen tex coords\n\tvec2 ntc;\t//normalmap/diffuse tex coords\n\tvec3 n, refr, refl;\n\tfloat fres;\n\tfloat depth;\n\tstc = (1.0 + (tf.xy / tf.w)) * 0.5;\n\t//hack the texture coords slightly so that there are less obvious gaps\n\tstc.t -= 1.5*norm.z/1080.0;\n\n#if 0//def USEMODS\n\tntc = tc;\n\tn = texture2D(s_normalmap, ntc).xyz - 0.5;\n#else\n\t//apply q1-style warp, just for kicks\n\tntc.s = tc.s + sin(tc.t+e_time)*0.125;\n\tntc.t = tc.t + sin(tc.s+e_time)*0.125;\n\n\t//generate the two wave patterns from the normalmap\n\tn = (texture2D(s_normalmap, vec2(TXSCALE1)*tc + vec2(e_time*0.1, 0.0)).xyz);\n\tn += (texture2D(s_normalmap, vec2(TXSCALE2)*tc - vec2(0, e_time*0.097)).xyz);\n\tn -= 1.0 - 4.0/256.0;\n#endif\n\n#ifdef RIPPLEMAP\n\tn += texture2D(s_ripplemap, stc).rgb*3.0;\n#endif\n\tn = normalize(n);\n\n\t//the fresnel term decides how transparent the water should be\n\tfres = pow(1.0-abs(dot(n, normalize(eye))), float(FRESNEL_EXP)) * float(FRESNEL_RANGE) + float(FRESNEL_MIN);\n\n#ifdef DEPTH\n\tfloat far = #include \"cvar/gl_maxdist\";\n\tfloat near = #include \"cvar/gl_mindist\";\n\t//get depth value at the surface\n\tfloat sdepth = gl_FragCoord.z;\n\tsdepth = (2.0*near) / (far + near - sdepth * (far - near));\n\tsdepth = mix(near, far, sdepth);\n\n\t//get depth value at the ground beyond the surface.\n\tfloat gdepth = texture2D(s_refractdepth, stc).x;\n\tgdepth = (2.0*near) / (far + near - gdepth * (far - near));\n\tif (gdepth >= 0.5)\n\t{\n\t\tgdepth = sdepth;\n\t\tdepth = 0.0;\n\t}\n\telse\n\t{\n\t\tgdepth = mix(near, far, gdepth);\n\t\tdepth = gdepth - sdepth;\n\t}\n\n\t//reduce the normals in shallow water (near walls, reduces the pain of linear sampling)\n\tif (depth < 100.0)\n\t\tn *= depth/100.0;\n#else\n\tdepth = 1.0;\n#endif \n\n\n\t//refraction image (and water fog, if possible)\n\trefr = texture2D(s_refract, stc + n.st*float(STRENGTH_REFR)*float(r_glsl_turbscale_refract)).rgb * vec3(TINT_REFR);\n#ifdef DEPTH\n\trefr = mix(refr, vec3(FOGTINT), min(depth/4096.0, 1.0));\n#endif\n\n#ifdef LQWATER\n\trefl = textureCube(s_reflectcube, n).rgb;// * vec3(TINT_REFL);\n#else\n\trefl = texture2D(s_reflect, stc - n.st*float(STRENGTH_REFL)*float(r_glsl_turbscale_reflect)).rgb * vec3(TINT_REFL);\n#endif\n\n\t//interplate by fresnel\n\trefr = mix(refr, refl, fres);\n\n#ifdef ALPHA\n\tvec4 ts = texture2D(s_diffuse, ntc);\n\tvec4 surf = fog4blend(vec4(ts.rgb, float(ALPHA)*ts.a));\n\trefr = mix(refr, surf.rgb, surf.a);\n#else\n\trefr = fog3(refr);\t\n#endif\n\n\t//done\n\tgl_FragColor = vec4(refr, 1.0);\n}\n#endif\n"
  },
  {
    "path": "plugins/hl2/hl2.c",
    "content": "#include \"../plugin.h\"\nqboolean GMA_Init(void);\nqboolean VPK_Init(void);\nqboolean TTH_Init(void);\nqboolean VTF_Init(void);\nqboolean MDL_Init(void);\nqboolean VMT_Init(void);\nqboolean VBSP_Init(void);\n\nqboolean Plug_Init(void)\n{\n\tqboolean somethingisokay = false;\n\tchar plugname[128];\n\tstrcpy(plugname, \"hl2\");\n\tplugfuncs->GetPluginName(0, plugname, sizeof(plugname));\n\n\tif (!GMA_Init())\tCon_Printf(CON_ERROR\"%s: GMA support unavailable\\n\", plugname);\telse\tsomethingisokay = true;\n\tif (!VPK_Init())\tCon_Printf(CON_ERROR\"%s: VPK support unavailable\\n\", plugname);\telse\tsomethingisokay = true;\n\tif (!TTH_Init())\tCon_Printf(CON_ERROR\"%s: TTH support unavailable\\n\", plugname);\telse\tsomethingisokay = true;\n\tif (!VTF_Init())\tCon_Printf(CON_ERROR\"%s: VTF support unavailable\\n\", plugname);\telse\tsomethingisokay = true;\n\tif (!VMT_Init())\tCon_Printf(CON_ERROR\"%s: VMT support unavailable\\n\", plugname);\telse\tsomethingisokay = true;\n\tif (!MDL_Init())\tCon_Printf(CON_ERROR\"%s: MDL support unavailable\\n\", plugname);\telse\tsomethingisokay = true;\n\tif (!VBSP_Init())\tCon_Printf(CON_ERROR\"%s: BSP support unavailable\\n\", plugname);\telse\tsomethingisokay = true;\n\treturn somethingisokay;\n}\n\n"
  },
  {
    "path": "plugins/hl2/img_tth.c",
    "content": "#include <zlib.h>\n#include \"../plugin.h\"\n\nstatic plugimagefuncs_t *imagefuncs = NULL;\nstatic plugfsfuncs_t *filefuncs = NULL;\n\nstruct pendingtextureinfo *Image_ReadVTFFile(unsigned int flags, const char *fname, qbyte *filedata, size_t filesize);\n\n#ifndef FTEENGINE\nqboolean COM_RequireExtension(char *path, const char *extension, int maxlen)\n{\n\tqboolean okay = true;\n\tint plen = strlen(path);\n\tint elen = strlen(extension);\n\n\t//check if its aready suffixed\n\tif (plen >= elen)\n\t{\n\t\tif (!Q_strcasecmp(path+plen-elen, extension))\n\t\t\treturn okay;\n\t}\n\n\t//truncate if required\n\tif (plen+1+elen > maxlen)\n\t{\n\t\tif (elen+1 > maxlen)\n\t\t\tSys_Errorf(\"extension longer than path buffer\");\n\t\tokay = false;\n\t\tplen = maxlen - 1+elen;\n\t}\n\n\t//do the copy\n\twhile(*extension)\n\t\tpath[plen++] = *extension++;\n\tpath[plen] = 0;\n\treturn okay;\n}\n#endif\n\nstatic struct pendingtextureinfo *Image_ReadTTHFile(unsigned int flags, const char *fname, qbyte *filedata, size_t filesize)\n{\n\tuint8_t num_mipmaps; // number of mipmaps in this texture\n\tuint32_t len_vtf_chunk; // size of uncompressed vtf chunk in header\n\tuint32_t len_vtf_file; // total size of uncompressed vtf file\n\tuint32_t len_ttz_tail; // compressed size of accompanying ttz file\n\tqbyte *vtf;\n\tqbyte *tail = NULL;\n\tsize_t tailsize = 0;\n\n\t// check magic\n\tif (memcmp(filedata, \"TTH\\0\", 4) != 0)\n\t\treturn NULL;\n\n\t// check version\n\tif (filedata[4] != 1 || filedata[5] != 0)\n\t\treturn NULL;\n\n\t// grab other data\n\tnum_mipmaps = filedata[6];\n\t// aspect_flag skipped\n\tlen_vtf_chunk = LittleLong(*(uint32_t *)&filedata[8]);\n\n\t// skip past mipmap flags to grab other other data\n\tlen_vtf_file = LittleLong(*(uint32_t *)(filedata + 12 + (num_mipmaps * sizeof(uint64_t))));\n\tlen_ttz_tail = LittleLong(*(uint32_t *)(filedata + 12 + (num_mipmaps * sizeof(uint64_t)) + 4));\n\n\t// load tail if it exists\n\tif (len_ttz_tail > 0)\n\t{\n\t\t// change extension\n\t\tchar tailfilename[MAX_QPATH];\n\t\tsize_t tailfilenamelen;\n\t\tQ_strlcpy(tailfilename, fname, sizeof(tailfilename));\n\n\t\ttailfilenamelen = strlen(tailfilename);\n\t\tif (tailfilename[tailfilenamelen - 1] == 'h')\n\t\t\ttailfilename[tailfilenamelen - 1] = 'z';\n\t\telse if (tailfilename[tailfilenamelen - 1] == 'H')\n\t\t\ttailfilename[tailfilenamelen - 1] = 'Z';\n\t\telse\n\t\t\tCOM_RequireExtension(tailfilename, \".ttz\", sizeof(tailfilename));\n\n\t\t// load tail\n\t\ttail = filefuncs->LoadFile(tailfilename, &tailsize);\n\t\tif (!tail)\n\t\t{\n\t\t\tCon_Printf(\"hl2: ERROR: couldn't load %s\\n\", tailfilename);\n\t\t\treturn NULL;\n\t\t}\n\n\t\t// shouldn't happen\n\t\tif (tailsize != len_ttz_tail)\n\t\t\tCon_Printf(\"hl2: WARNING: %s size mismatch\\n\", tailfilename);\n\t}\n\n\t// allocate return vtf file\n\tvtf = plugfuncs->Malloc(len_vtf_file);\n\tif (!vtf)\n\t\treturn NULL;\n\n\t// copy in vtf chunk\n\tmemcpy(vtf, filedata + 12 + (num_mipmaps * sizeof(uint64_t)) + 8, len_vtf_chunk);\n\n\t// zlib decompress the tail onto the main vtf\n\tif (tail)\n\t{\n\t\tz_stream stream;\n\n\t\tmemset(&stream, 0, sizeof(stream));\n\t\tstream.next_in = tail;\n\t\tstream.avail_in = len_ttz_tail;\n\t\tstream.next_out = vtf + len_vtf_chunk;\n\t\tstream.avail_out = len_vtf_file - len_vtf_chunk;\n\n\t\tinflateInit(&stream);\n\t\tinflate(&stream, Z_FINISH);\n\t\tinflateEnd(&stream);\n\n\t\tplugfuncs->Free(tail);\n\t}\n\n\t// now send it to the VTF loader\n\treturn Image_ReadVTFFile(flags, fname, vtf, len_vtf_file);\n}\n\nstatic plugimageloaderfuncs_t tthfuncs =\n{\n\t\"Troika Texture File\",\n\tsizeof(struct pendingtextureinfo),\n\ttrue,\n\tImage_ReadTTHFile,\n};\n\nqboolean TTH_Init(void)\n{\n\timagefuncs = plugfuncs->GetEngineInterface(plugimagefuncs_name, sizeof(*imagefuncs));\n\tfilefuncs = plugfuncs->GetEngineInterface(plugfsfuncs_name, sizeof(*filefuncs));\n\tif (!imagefuncs || !filefuncs)\n\t\treturn false;\n\treturn plugfuncs->ExportInterface(plugimageloaderfuncs_name, &tthfuncs, sizeof(tthfuncs));\n}\n"
  },
  {
    "path": "plugins/hl2/img_vtf.c",
    "content": "#include \"../plugin.h\"\n\nstatic plugimagefuncs_t *imagefuncs;\n\n//many of these look like dupes, not really sure how they're meant to work. probably legacy.\ntypedef enum {\n\tVMF_INVALID=-1,\n\tVMF_RGBA8=0,\n//\tVMF_ABGR8=1,\n\tVMF_RGB8=2,\n\tVMF_BGR8=3,\n//\tVMF_RGB565=4,\n\tVMF_I8=5,\n\tVMF_IA8=6,\n//\tVMF_P8=7,\n//\tVMF_A8=8,\n//\tVMF_RGB8_BS=9,\n//\tVMF_BGR8_BS=10,\n//\tVMF_ARGB_BS=11,\n\tVMF_BGRA8=12,\n\tVMF_BC1=13,\n\tVMF_BC2=14,\n\tVMF_BC3=15,\n\tVMF_BGRX8=16,\n//\tVMF_BGR565=17,\n//\tVMF_BGRX5551=18,\n//\tVMF_BGRA4444=19,\n\tVMF_BC1A=20,\n//\tVMF_BGRA5551=21,\n\tVMF_UV88=22,\n//\tVMF_UVWQ8=23,\n\tVMF_RGBA16F=24,\n//\tVMF_RGBA16N=25,\n//\tVMF_UVLX8=26,\n\tVMF_MAX\n} fmtfmt_t;\nstatic uploadfmt_t ImageVTF_VtfToFTE(fmtfmt_t f)\n{\n\tswitch(f)\n\t{\n\tcase VMF_BC1:\n\t\treturn PTI_BC1_RGB;\n\tcase VMF_BC1A:\n\t\treturn PTI_BC1_RGBA;\n\tcase VMF_BC2:\n\t\treturn PTI_BC2_RGBA;\n\tcase VMF_BC3:\n\t\treturn PTI_BC3_RGBA;\n\tcase VMF_RGB8:\n\t\treturn PTI_RGB8;\n\tcase VMF_RGBA8:\n\t\treturn PTI_RGBA8;\n\tcase VMF_BGR8:\n\t\treturn PTI_BGR8;\n\tcase VMF_BGRA8:\n\t\treturn PTI_BGRA8;\n\tcase VMF_BGRX8:\n\t\treturn PTI_BGRX8;\n\tcase VMF_RGBA16F:\n\t\treturn PTI_RGBA16F;\n\tcase VMF_UV88:\n\t\treturn PTI_RG8;\n\tcase VMF_I8:\n\t\treturn PTI_L8;\n\tcase VMF_IA8:\n\t\treturn PTI_L8A8;\n\tcase VMF_INVALID:\n\t\treturn PTI_INVALID;\n\n\tdefault:\n\t\treturn PTI_INVALID;\n\t}\n}\nstruct pendingtextureinfo *Image_ReadVTFFile(unsigned int flags, const char *fname, qbyte *filedata, size_t filesize)\n{\n\tstruct vtf_s\n\t{\n\t\tchar magic[4];\n\t\tunsigned int major,minor;\n\t\tunsigned int headersize;\n\n\t\tunsigned short width, height;\n\t\tunsigned int flags;\n\t\tunsigned short numframes, firstframe;\n\t\tunsigned int pad1;\n\n\t\tvec3_t reflectivity;\n\t\tfloat pad2;\n\n\t\tfloat bumpmapscale;\n\t\tunsigned int imgformat;\n\t\tunsigned char mipmapcount;\n\t\tunsigned char lowresfmt_misaligned[4];\n\t\tunsigned char lowreswidth;\n\t\tunsigned char lowresheight;\n\n\t\t//7.2\n\t\tunsigned char depth_misaligned[2];\n\t\t//7.3\n\t\tunsigned char pad3[3];\n\t\tunsigned int numresources;\n\t} *vtf;\n\tfmtfmt_t vmffmt, lrfmt;\n\tunsigned int bw, bh, bd, bb;\n\tqbyte *end = filedata + filesize;\n\tunsigned int faces, frame, frames, miplevel, miplevels, img;\n\tunsigned int w, h, d = 1;\n\tsize_t\tdatasize;\n\tunsigned int version;\n\n\tstruct pendingtextureinfo *mips;\n\n\tvtf = (void*)filedata;\n\n\tif (memcmp(vtf->magic, \"VTF\\0\", 4))\n\t\treturn NULL;\n\n\t// erysdren: do endian swapping\n\tvtf->major = LittleLong(vtf->major);\n\tvtf->minor = LittleLong(vtf->minor);\n\tvtf->headersize = LittleLong(vtf->headersize);\n\tvtf->width = LittleShort(vtf->width);\n\tvtf->height = LittleShort(vtf->height);\n\tvtf->flags = LittleLong(vtf->flags);\n\tvtf->numframes = LittleShort(vtf->numframes);\n\tvtf->firstframe = LittleShort(vtf->firstframe);\n\tvtf->pad1 = LittleLong(vtf->pad1);\n\tvtf->reflectivity[0] = LittleFloat(vtf->reflectivity[0]);\n\tvtf->reflectivity[1] = LittleFloat(vtf->reflectivity[1]);\n\tvtf->reflectivity[2] = LittleFloat(vtf->reflectivity[2]);\n\tvtf->pad2 = LittleFloat(vtf->pad2);\n\tvtf->bumpmapscale = LittleFloat(vtf->bumpmapscale);\n\n\tversion = (vtf->major<<16)|vtf->minor;\n\tif (version > 0x00070005)\n\t{\n\t\tCon_Printf(\"%s: VTF version %i.%i is not supported\\n\", fname, vtf->major, vtf->minor);\n\t\treturn NULL;\n\t}\n\n\tlrfmt = LittleLong((vtf->lowresfmt_misaligned[0]<<0)|(vtf->lowresfmt_misaligned[1]<<16)|(vtf->lowresfmt_misaligned[2]<<16)|(vtf->lowresfmt_misaligned[3]<<24));\n\tvmffmt = LittleLong(vtf->imgformat);\n\n\tmips = NULL;\n\tif (version >= 0x00070003)\n\t{\n\t\tint i;\n\t\tstruct\n\t\t{\n\t\t\tunsigned int rtype;\n\t\t\tunsigned int rdata; //usually an offset.\n\t\t} *restable = (void*)(filedata+sizeof(*vtf));\n\t\tfor (i = 0; i < LittleLong(vtf->numresources); i++, restable++)\n\t\t{\n\t\t\tif ((LittleLong(restable->rtype) & 0x00ffffff) == 0x30)\n\t\t\t{\n\t\t\t\tmips = plugfuncs->Malloc(sizeof(*mips));\n\t\t\t\tmips->extrafree = filedata;\n\t\t\t\tfiledata += LittleLong(restable->rdata);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\t//other unknown resource types.\n\t\t}\n\t}\n\tif (!mips)\n\t{\n\t\tmips = plugfuncs->Malloc(sizeof(*mips));\n\t\tmips->extrafree = filedata;\n\n\t\t//skip the header\n\t\tfiledata += vtf->headersize;\n\t\t//and skip the low-res image too.\n\t\tif (vtf->lowreswidth && vtf->lowresheight)\n\t\t\timagefuncs->BlockSizeForEncoding(ImageVTF_VtfToFTE(lrfmt), &bb, &bw, &bh, &bd);\n\t\telse\n\t\t\tbb=bw=bh=bd=1;\n\t\tdatasize = ((vtf->lowreswidth+bw-1)/bw) * ((vtf->lowresheight+bh-1)/bh) * ((1/*vtf->lowresdepth*/+bd-1)/bd) * bb;\n\t\tfiledata += datasize;\n\t}\n\n\t//now handle the high-res image\n\tif (mips)\n\t{\n\t\tmips->type = (vtf->flags & 0x4000)?PTI_CUBE:PTI_2D;\n\n\t\tmips->encoding = ImageVTF_VtfToFTE(vmffmt);\n\t\timagefuncs->BlockSizeForEncoding(mips->encoding, &bb, &bw, &bh, &bd);\n\n\t\tmiplevels = vtf->mipmapcount;\n\t\tframes = 1;//vtf->numframes;\n\t\tfaces = ((mips->type==PTI_CUBE)?6:1);\t//no cubemaps yet.\n\n\t\tmips->mipcount = miplevels * frames;\n\t\twhile (mips->mipcount > countof(mips->mip))\n\t\t{\n\t\t\tif (miplevels > 1)\n\t\t\t\tmiplevels--;\n\t\t\telse\n\t\t\t\tframes--;\n\t\t\tmips->mipcount = miplevels * frames;\n\t\t}\n\t\tif (!mips->mipcount)\n\t\t{\n\t\t\tplugfuncs->Free(mips);\n\t\t\treturn NULL;\n\t\t}\n\t\tfor (miplevel = vtf->mipmapcount; miplevel-- > 0;)\n\t\t{\t//smallest to largest, which is awkward.\n\t\t\tw = vtf->width>>miplevel;\n\t\t\th = vtf->height>>miplevel;\n\t\t\tif (!w)\n\t\t\t\tw = 1;\n\t\t\tif (!h)\n\t\t\t\th = 1;\n\t\t\tdatasize = ((w+bw-1)/bw) * ((h+bh-1)/bh) * ((d+bd-1)/bd) * bb;\n\t\t\tfor (frame = 0; frame < vtf->numframes; frame++)\n\t\t\t{\n\t\t\t\tif (miplevel < miplevels)\n\t\t\t\t{\n\t\t\t\t\timg = miplevel + frame*miplevels;\n\t\t\t\t\tif (img >= countof(mips->mip))\n\t\t\t\t\t\tbreak;\t//erk?\n\t\t\t\t\tif (filedata + datasize > end)\n\t\t\t\t\t\tbreak;\t//no more data here...\n\t\t\t\t\tmips->mip[img].width = w;\n\t\t\t\t\tmips->mip[img].height = h;\n\t\t\t\t\tmips->mip[img].depth = faces;\n\t\t\t\t\tmips->mip[img].data = filedata;\n\t\t\t\t\tmips->mip[img].datasize = datasize*faces;\n\t\t\t\t}\n\t\t\t\tfiledata += datasize*faces;\n\t\t\t}\n\t\t}\n\t}\n\treturn mips;\n}\n\nstatic plugimageloaderfuncs_t vtffuncs =\n{\n\t\"Valve Texture File\",\n\tsizeof(struct pendingtextureinfo),\n\ttrue,\n\tImage_ReadVTFFile,\n};\n\nqboolean VTF_Init(void)\n{\n\timagefuncs = plugfuncs->GetEngineInterface(plugimagefuncs_name, sizeof(*imagefuncs));\n\tif (!imagefuncs)\n\t\treturn false;\n\treturn plugfuncs->ExportInterface(plugimageloaderfuncs_name, &vtffuncs, sizeof(vtffuncs));\n}\n\n"
  },
  {
    "path": "plugins/hl2/mat_vmt.c",
    "content": "#include \"../plugin.h\"\n#include \"shader.h\"\nstatic plugfsfuncs_t *fsfuncs;\n\ntypedef struct shaderparsestate_s parsestate_t;\n\ntypedef struct\n{\n\tchar **savefile;\n\tchar *sourcefile;\n\tchar type[MAX_QPATH];\n\tchar normalmap[MAX_QPATH];\n\tstruct\n\t{\n\t\tchar name[MAX_QPATH];\n\t} tex[5];\n\tchar fullbrightmap[MAX_QPATH];\n\tchar envmap[MAX_QPATH];\n\tchar envmapmask[MAX_QPATH];\n\tchar dudvmap[MAX_QPATH];\n\tchar refracttinttexture[MAX_QPATH];\n\tchar color[MAX_QPATH];\n\tchar envfrombase;\n\tchar envfromnorm;\n\tchar halflambert;\n\n\tfloat envmaptint_r;\n\tfloat envmaptint_g;\n\tfloat envmaptint_b;\n\tfloat envmapsat_r;\n\tfloat envmapsat_g;\n\tfloat envmapsat_b;\n\tfloat alphatestref;\n\tchar *blendfunc;\n\tqboolean alphatest;\n\tqboolean culldisable;\n\tqboolean ignorez;\n\tchar *replaceblock;\n\n\tchar vertexcolor;\n\tchar vertexalpha;\n\tchar nodraw;\n\tchar additive;\n\tchar translucent;\n\tchar selfillum;\n\tchar decal;\n\tchar nofog;\n\tchar mod2x; /* modulate only */\n\tchar water_cheap; /* water only */\n\tchar water_expensive; /* water only */\n} vmtstate_t;\n\n\nstatic void VARGS Q_strlcatfz (char *dest, size_t *offset, size_t size, const char *fmt, ...) LIKEPRINTF(4);\nstatic void VARGS Q_strlcatfz (char *dest, size_t *offset, size_t size, const char *fmt, ...)\n{\n\tva_list\t\targptr;\n\n\tdest += *offset;\n\tsize -= *offset;\n\n\tva_start (argptr, fmt);\n\tQ_vsnprintfz(dest, size, fmt, argptr);\n\tva_end (argptr);\n\t*offset += strlen(dest);\n}\nstatic void Q_StrCat(char **ptr, const char *append)\n{\n\tsize_t oldlen = *ptr?strlen(*ptr):0;\n\tsize_t newlen = strlen(append);\n\tchar *newptr = plugfuncs->Malloc(oldlen+newlen+1);\n\tmemcpy(newptr, *ptr, oldlen);\n\tmemcpy(newptr+oldlen, append, newlen);\n\tnewptr[oldlen+newlen] = 0;\n\tplugfuncs->Free(*ptr);\n\t*ptr = newptr;\n}\n\nstatic qboolean VMT_ReadVMT(const char *materialname, vmtstate_t *st);\t//this is made more complicated on account of includes allowing recursion\nstatic char *VMT_ParseBlock(const char *fname, vmtstate_t *st, char *line)\n{\t//assumes the open { was already parsed, but will parse the close.\n\tchar *replace = NULL;\n\tcom_tokentype_t ttype;\n\tchar key[MAX_OSPATH];\n\tchar value[MAX_OSPATH];\n\tchar *qmark;\n\tqboolean cond;\n\tfor(;line;)\n\t{\n\t\tline = cmdfuncs->ParseToken(line, key, sizeof(key), &ttype);\n\t\tif (ttype == TTP_RAWTOKEN && !strcmp(key, \"}\"))\n\t\t\tbreak;\t//end-of-block\n\t\tline = cmdfuncs->ParseToken(line, value, sizeof(value), &ttype);\n\t\tif (ttype == TTP_RAWTOKEN && !strcmp(value, \"{\"))\n\t\t{\t//sub block. we don't go into details here.\n\t\t\t//insert and replace blocks do the same thing in practice \n\t\t\tif (!Q_strcasecmp(key, \"replace\") || !Q_strcasecmp(key, \"insert\"))\n\t\t\t\treplace = line;\n\t\t\telse\n\t\t\t\tCon_DPrintf(\"%s: Unknown block \\\"%s\\\"\\n\", fname, key);\n\t\t\tline = VMT_ParseBlock(fname, NULL, line);\n\t\t\tcontinue;\n\t\t}\n\n\t\twhile ((qmark = strchr(key, '?')))\n\t\t{\n\t\t\t*qmark++ = 0;\n\t\t\tif (!Q_strcasecmp(key, \"srgb\"))\n\t\t\t\tcond = false;//!!(vid.flags & VID_SRGBAWARE);\n\t\t\telse\n\t\t\t{\n\t\t\t\tCon_DPrintf(\"%s: Unknown vmt conditional \\\"%s\\\"\\n\", fname, key);\n\t\t\t\tcond = false;\n\t\t\t}\n\t\t\tif (!cond)\n\t\t\t{\n\t\t\t\t*key = 0;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\telse\n\t\t\t\tmemmove(key, qmark, strlen(qmark)+1);\n\t\t}\n\n\t\tif (!*key || !st)\n\t\t\t;\n\t\telse if (!Q_strcasecmp(key, \"include\"))\n\t\t{\n\t\t\tif (!VMT_ReadVMT(value, st))\n\t\t\t\treturn NULL;\n\t\t}\n\t\telse if (!Q_strcasecmp(key, \"$basetexture\") || !Q_strcasecmp(key, \"$hdrbasetexture\"))\t//fixme: hdr version should probably override the other. order matters.\n\t\t\tQ_strlcpy(st->tex[0].name, value, sizeof(st->tex[0].name));\n\t\telse if (!Q_strcasecmp(key, \"$hdrcompressedtexture\"))\t//named texture is R8G8B8E8 and needs to be decompressed manually... should probably just use e5bgr9 but we don't have a way to transcode it here.\n\t\t\t;\n\t\telse if (!Q_strcasecmp(key, \"$basetexturetransform\"))\n\t\t\t;\n\t\telse if (!Q_strcasecmp(key, \"$bumpmap\")) // same as normalmap ~eukara\n\t\t\tQ_strlcpy(st->normalmap, value, sizeof(st->normalmap));\n\t\telse if (!Q_strcasecmp(key, \"$dudvmap\")) // refractions only\n\t\t{\n\t\t\tQ_strlcpy(st->dudvmap, value, sizeof(st->dudvmap));\n\t\t\tCon_DPrintf(\"%s: %s \\\"%s\\\"\\n\", fname, key, value);\n\t\t}\n\t\telse if (!Q_strcasecmp(key, \"$ssbump\"))\n\t\t\t;\n\t\telse if (!Q_strcasecmp(key, \"$ssbumpmathfix\"))\n\t\t\t;\n\t\telse if (!Q_strcasecmp(key, \"$basetexture2\") || !strcmp(key, \"$texture2\"))\n\t\t\tQ_strlcpy(st->tex[1].name, value, sizeof(st->tex[1].name));\n\t\telse if (!Q_strcasecmp(key, \"$basetexturetransform2\"))\n\t\t\t;\n\t\telse if (!Q_strcasecmp(key, \"$surfaceprop\"))\n\t\t\t;\n\n\t\telse if (!Q_strcasecmp(key, \"$ignorez\"))\n\t\t\tst->ignorez = !!atoi(value);\n\t\telse if (!Q_strcasecmp(key, \"$nocull\") && (!strcmp(value, \"1\")||!strcmp(value, \"0\")))\n\t\t\tst->culldisable = atoi(value);\n\t\telse if (!Q_strcasecmp(key, \"$alphatest\") && (!strcmp(value, \"1\")||!strcmp(value, \"0\")))\n\t\t\tst->alphatest = atoi(value);\n\t\telse if (!Q_strcasecmp(key, \"$alphatestreference\"))\n\t\t\tst->alphatestref = atof(value);\n\t\telse if (!Q_strcasecmp(key, \"$alphafunc\"))\n\t\t{\n\t\t\tCon_DPrintf(\"%s: %s \\\"%s\\\"\\n\", fname, key, value);\n\t\t}\n\t\telse if (!Q_strcasecmp(key, \"$alpha\"))\n\t\t{\n\t\t\tCon_DPrintf(\"%s: %s \\\"%s\\\"\\n\", fname, key, value);\n\t\t}\n\t\telse if (!Q_strcasecmp(key, \"$translucent\"))\n\t\t{\n\t\t\tst->translucent = 1;\n\t\t}\n\t\telse if (!Q_strcasecmp(key, \"$additive\"))\n\t\t{\n\t\t\tif (atoi(value))\n\t\t\t\tst->additive = 1;\n\t\t\tCon_DPrintf(\"%s: %s \\\"%s\\\"\\n\", fname, key, value);\n\t\t}\n\t\telse if (!Q_strcasecmp(key, \"$halflambert\"))\n\t\t\tst->halflambert = 1;\n\t\telse if (!Q_strcasecmp(key, \"%compiletrigger\"))\n\t\t\tst->nodraw = 1;\n\t\telse if (!Q_strcasecmp(key, \"lampbeam\"))\n\t\t\tst->additive = 1;\n\t\telse if (!Q_strcasecmp(key, \"$color\"))\n\t\t\tQ_strlcpy(st->color, value, sizeof(st->color));\n\t\telse if (!Q_strcasecmp(key, \"$vertexcolor\"))\n\t\t\tst->vertexcolor = 1;\n\t\telse if (!Q_strcasecmp(key, \"$vertexalpha\"))\n\t\t\tst->vertexalpha = 1;\n\t\telse if (!Q_strcasecmp(key, \"$decal\"))\n\t\t\tst->decal = atoi(value);\n\t\telse if (!Q_strcasecmp(key, \"$decalscale\"))\n\t\t\tCon_DPrintf(\"%s: %s \\\"%s\\\"\\n\", fname, key, value);\n\t\telse if (!Q_strcasecmp(key, \"$decalsize\"))\n\t\t\tCon_DPrintf(\"%s: %s \\\"%s\\\"\\n\", fname, key, value);\n\t\telse if (!Q_strcasecmp(key, \"$envmap\"))\n\t\t\tQ_strlcpy(st->envmap, value, sizeof(st->envmap));\n\t\telse if (!Q_strcasecmp(key, \"$envmapmask\"))\n\t\t\tQ_strlcpy(st->envmapmask, value, sizeof(st->envmapmask));\n\t\telse if (!Q_strcasecmp(key, \"$envmapcontrast\"))\n\t\t\tCon_DPrintf(\"%s: %s \\\"%s\\\"\\n\", fname, key, value);\n\t\telse if (!Q_strcasecmp(key, \"$envmaptint\"))\n\t\t{\n\t\t\tchar tok[64];\n\t\t\tchar *tintline;\n\t\t\ttintline = cmdfuncs->ParsePunctuation(value, \"[\", tok, sizeof(tok), 0);\n\n\t\t\tif (!strcmp(tok, \"[\")) {\n\t\t\t\ttintline = cmdfuncs->ParseToken(tintline, tok, sizeof(tok), 0);\n\t\t\t\tst->envmaptint_r = strtof(tok, NULL);\n\t\t\t\ttintline = cmdfuncs->ParseToken(tintline, tok, sizeof(tok), 0);\n\t\t\t\tst->envmaptint_g = strtof(tok, NULL);\n\t\t\t\ttintline = cmdfuncs->ParsePunctuation(tintline, \"]\", tok, sizeof(tok), 0);\n\t\t\t\tst->envmaptint_b = strtof(tok, NULL);\n\t\t\t} else {\n\t\t\t\tst->envmaptint_r = st->envmaptint_g = st->envmaptint_b = atof(value);\n\t\t\t}\n\t\t}\n\t\telse if (!Q_strcasecmp(key, \"$envmapsaturation\"))\n\t\t{\n\t\t\tchar tok[64];\n\t\t\tchar *tintline;\n\t\t\ttintline = cmdfuncs->ParsePunctuation(value, \"[\", tok, sizeof(tok), 0);\n\n\t\t\tif (!strcmp(tok, \"[\")) {\n\t\t\t\ttintline = cmdfuncs->ParseToken(tintline, tok, sizeof(tok), 0);\n\t\t\t\tst->envmapsat_r = strtof(tok, NULL);\n\t\t\t\ttintline = cmdfuncs->ParseToken(tintline, tok, sizeof(tok), 0);\n\t\t\t\tst->envmapsat_g = strtof(tok, NULL);\n\t\t\t\ttintline = cmdfuncs->ParsePunctuation(tintline, \"]\", tok, sizeof(tok), 0);\n\t\t\t\tst->envmapsat_b = strtof(tok, NULL);\n\t\t\t} else {\n\t\t\t\tst->envmapsat_r = st->envmapsat_g = st->envmapsat_b = atof(value);\n\t\t\t}\n\t\t}\n\t\telse if (!Q_strcasecmp(key, \"$basealphaenvmapmask\"))\n\t\t\tst->envfrombase=1;\n\t\telse if (!Q_strcasecmp(key, \"$normalmapalphaenvmapmask\"))\n\t\t\tst->envfromnorm=1;\n\t\telse if (!Q_strcasecmp(key, \"$crackmaterial\"))\n\t\t\tCon_DPrintf(\"%s: %s \\\"%s\\\"\\n\", fname, key, value);\n\t\telse if (!Q_strcasecmp(key, \"$selfillum\"))\n\t\t\tst->selfillum = 1;\n\t\telse if (!Q_strcasecmp(key, \"$selfillummask\"))\n\t\t\tQ_strlcpy(st->fullbrightmap, value, sizeof(st->fullbrightmap));\n\t\telse if (!Q_strcasecmp(key, \"$selfillumtint\"))\n\t\t\tCon_DPrintf(\"%s: %s \\\"%s\\\"\\n\", fname, key, value);\n\t\telse if (!Q_strcasecmp(key, \"$nofog\"))\n\t\t\tst->nofog = 1;\n\t\telse if (!Q_strcasecmp(key, \"$nomip\"))\n\t\t\tCon_DPrintf(\"%s: %s \\\"%s\\\"\\n\", fname, key, value);\n\t\telse if (!Q_strcasecmp(key, \"$nodecal\"))\n\t\t\tCon_DPrintf(\"%s: %s \\\"%s\\\"\\n\", fname, key, value);\n\t\telse if (!Q_strcasecmp(key, \"$detail\"))\n\t\t\tCon_DPrintf(\"%s: %s \\\"%s\\\"\\n\", fname, key, value);\n\t\telse if (!Q_strcasecmp(key, \"$detailscale\"))\n\t\t\tCon_DPrintf(\"%s: %s \\\"%s\\\"\\n\", fname, key, value);\n\t\telse if (!Q_strcasecmp(key, \"$detailtint\"))\n\t\t\tCon_DPrintf(\"%s: %s \\\"%s\\\"\\n\", fname, key, value);\n\t\telse if (!Q_strcasecmp(key, \"$detailblendfactor\"))\n\t\t\tCon_DPrintf(\"%s: %s \\\"%s\\\"\\n\", fname, key, value);\n\t\telse if (!Q_strcasecmp(key, \"$detailblendmode\"))\n\t\t\tCon_DPrintf(\"%s: %s \\\"%s\\\"\\n\", fname, key, value);\n\n\t\telse if (!Q_strcasecmp(key, \"$surfaceprop2\"))\n\t\t\tCon_DPrintf(\"%s: %s \\\"%s\\\"\\n\", fname, key, value);\n\t\telse if (!Q_strcasecmp(key, \"$AllowAlphaToCoverage\"))\n\t\t\tCon_DPrintf(\"%s: %s \\\"%s\\\"\\n\", fname, key, value);\n\t\telse if (!Q_strcasecmp(key, \"$blendmodulatetexture\"))\n\t\t\tCon_DPrintf(\"%s: %s \\\"%s\\\"\\n\", fname, key, value);\n\n\t\t//water/reflection stuff\n\t\telse if (!Q_strcasecmp(key, \"$REFRACTTINTTEXTURE\"))\n\t\t\tQ_strlcpy(st->refracttinttexture, value, sizeof(st->refracttinttexture));\n\t\telse if (!Q_strcasecmp(key, \"$refracttexture\"))\n\t\t\tCon_DPrintf(\"%s: %s \\\"%s\\\"\\n\", fname, key, value);\n\t\telse if (!Q_strcasecmp(key, \"$refractamount\"))\n\t\t\tCon_DPrintf(\"%s: %s \\\"%s\\\"\\n\", fname, key, value);\n\t\telse if (!Q_strcasecmp(key, \"$refracttint\"))\n\t\t\tCon_DPrintf(\"%s: %s \\\"%s\\\"\\n\", fname, key, value);\n\t\telse if (!Q_strcasecmp(key, \"$reflecttexture\"))\n\t\t\tCon_DPrintf(\"%s: %s \\\"%s\\\"\\n\", fname, key, value);\n\t\telse if (!Q_strcasecmp(key, \"$reflectamount\"))\n\t\t\tCon_DPrintf(\"%s: %s \\\"%s\\\"\\n\", fname, key, value);\n\t\telse if (!Q_strcasecmp(key, \"$reflecttint\"))\n\t\t\tCon_DPrintf(\"%s: %s \\\"%s\\\"\\n\", fname, key, value);\n\t\telse if (!Q_strcasecmp(key, \"$fresnelpower\"))\n\t\t\tCon_DPrintf(\"%s: %s \\\"%s\\\"\\n\", fname, key, value);\n\t\telse if (!Q_strcasecmp(key, \"$minreflectivity\"))\n\t\t\tCon_DPrintf(\"%s: %s \\\"%s\\\"\\n\", fname, key, value);\n\t\telse if (!Q_strcasecmp(key, \"$maxreflectivity\"))\n\t\t\tCon_DPrintf(\"%s: %s \\\"%s\\\"\\n\", fname, key, value);\n\t\telse if (!Q_strcasecmp(key, \"$mod2x\"))\n\t\t\tst->mod2x = 1;\n\t\telse if (!Q_strcasecmp(key, \"$forcecheap\"))\n\t\t\tst->water_cheap = 1;\n\t\telse if (!Q_strcasecmp(key, \"$forceexpensive\"))\n\t\t\tst->water_expensive = 1;\n\t\telse if (!Q_strcasecmp(key, \"$normalmap\"))\n\t\t{\n\t\t\tQ_strlcpy(st->normalmap, value, sizeof(st->normalmap));\n\t\t\tCon_DPrintf(\"%s: %s \\\"%s\\\"\\n\", fname, key, value);\n\t\t}\n\t\telse if (!Q_strcasecmp(key, \"$bumpframe\"))\n\t\t\tCon_DPrintf(\"%s: %s \\\"%s\\\"\\n\", fname, key, value);\n\t\telse if (!Q_strcasecmp(key, \"$fogenable\"))\n\t\t\tCon_DPrintf(\"%s: %s \\\"%s\\\"\\n\", fname, key, value);\n\t\telse if (!Q_strcasecmp(key, \"$fogcolor\"))\n\t\t\tCon_DPrintf(\"%s: %s \\\"%s\\\"\\n\", fname, key, value);\n\t\telse if (!Q_strcasecmp(key, \"$fogstart\"))\n\t\t\tCon_DPrintf(\"%s: %s \\\"%s\\\"\\n\", fname, key, value);\n\t\telse if (!Q_strcasecmp(key, \"$fogend\"))\n\t\t\tCon_DPrintf(\"%s: %s \\\"%s\\\"\\n\", fname, key, value);\n\t\telse if (!Q_strcasecmp(key, \"$abovewater\"))\n\t\t\tCon_DPrintf(\"%s: %s \\\"%s\\\"\\n\", fname, key, value);\n\t\telse if (!Q_strcasecmp(key, \"$underwateroverlay\"))\n\t\t\tCon_DPrintf(\"%s: %s \\\"%s\\\"\\n\", fname, key, value);\n\t\telse if (!Q_strcasecmp(key, \"$reflectentities\"))\n\t\t\tCon_DPrintf(\"%s: %s \\\"%s\\\"\\n\", fname, key, value);\n\t\telse if (!Q_strcasecmp(key, \"$scale\"))\n\t\t\tCon_DPrintf(\"%s: %s \\\"%s\\\"\\n\", fname, key, value);\n\t\telse if (!Q_strcasecmp(key, \"$bottommaterial\"))\n\t\t\tCon_DPrintf(\"%s: %s \\\"%s\\\"\\n\", fname, key, value);\n\t\telse if (!Q_strcasecmp(key, \"$scroll1\"))\n\t\t\tCon_DPrintf(\"%s: %s \\\"%s\\\"\\n\", fname, key, value);\n\t\telse if (!Q_strcasecmp(key, \"$scroll2\"))\n\t\t\tCon_DPrintf(\"%s: %s \\\"%s\\\"\\n\", fname, key, value);\n\n\t\telse if (*key == '%')\n\t\t\t;\t//editor lines\n\t\telse\n\t\t\tCon_DPrintf(\"%s: Unknown field \\\"%s\\\"\\n\", fname, key);\n\t}\n\tif (replace)\n\t\tVMT_ParseBlock(fname, st, replace);\n\treturn line;\n}\nstatic void Shader_GenerateFromVMT(parsestate_t *ps, vmtstate_t *st, const char *shortname, void (*LoadMaterialString)(parsestate_t *ps, const char *script))\n{\n\tsize_t offset = 0;\n\tchar script[8192];\n\tchar envmaptint[128];\n\tchar envmapsat[128];\n\tchar *progargs = \"\";\n\n\tif (!*st->tex[0].name)\t//fill in a default...\n\t\tQ_strlcpy(st->tex[0].name, shortname, sizeof(st->tex[0].name));\n\n\tif (st->alphatest)\n\t\tprogargs = \"#MASK=0.5#MASKLT\";\t//alphamask has to be handled by glsl (when glsl is used)\n\n\tif (st->nofog)\n\t\tprogargs = \"#NOFOG\";\n\n\t/* FIXME: check proper */\n\tif (st->envmaptint_b > 0.0f) {\n\t\tQ_snprintfz(envmaptint, sizeof(envmaptint), \"#ENVTINT=%f,%f,%f\", st->envmaptint_r, st->envmaptint_g, st->envmaptint_b);\n\t} else {\n\t\tQ_snprintfz(envmaptint, sizeof(envmaptint), \"\");\n\t}\n\n\t/* FIXME: check proper */\n\tif (st->envmapsat_r > 0.0f) {\n\t\tQ_snprintfz(envmapsat, sizeof(envmapsat), \"#ENVSAT=%f,%f,%f\", st->envmapsat_r, st->envmapsat_g, st->envmapsat_b);\n\t} else {\n\t\tQ_snprintfz(envmapsat, sizeof(envmapsat), \"\");\n\t}\n\n\tQ_strlcatfz(script, &offset, sizeof(script), \"\\n\");\n\n\tif (st->nodraw)\n\t{\n\t\tQ_strlcatfz(script, &offset, sizeof(script), \"\\tsurfaceparm nodraw\\n\");\n\t}\n\telse if (!Q_strcasecmp(st->type, \"UnlitGeneric\"))\n\t{\n\t\tQ_strlcatfz(script, &offset, sizeof(script), \"{\\n\");\n\t\tQ_strlcatfz(script, &offset, sizeof(script),\t\"\\tmap \\\"%s%s.vtf\\\"\\n\", strcmp(st->tex[0].name, \"materials/\")?\"materials/\":\"\", st->tex[0].name);\n\n\t\tif (st->vertexcolor)\n\t\t\tQ_strlcatfz(script, &offset, sizeof(script),\t\"\\rrgbGen vertex\\n\");\n\t\tif (st->vertexalpha)\n\t\t\tQ_strlcatfz(script, &offset, sizeof(script),\t\"\\ralphaGen vertex\\n\");\n\n\t\tif (st->additive)\n\t\t\tQ_strlcatfz(script, &offset, sizeof(script),\t\"\\tblendFunc add\\n\");\n\t\telse if (st->translucent)\n\t\t\tQ_strlcatfz(script, &offset, sizeof(script),\t\"\\tblendFunc blend\\n\");\n\t\telse if (st->alphatest)\n\t\t\tQ_strlcatfz(script, &offset, sizeof(script),\t\"\\talphaFunc GE128\\n\");\n\n\n\t\tQ_strlcatfz(script, &offset, sizeof(script), \"}\\n\");\n\t}\n\telse if (!Q_strcasecmp(st->type, \"WorldVertexTransition\"))\n\t{\n\t\tif (*st->envmap && st->envfrombase)\n\t\t\tQ_strlcpy(st->type, \"vmt/transition#ENVFROMBASE\", sizeof(st->type));\n\t\telse if (*st->envmap && st->envfromnorm)\n\t\t\tQ_strlcpy(st->type, \"vmt/transition#ENVFROMNORM\", sizeof(st->type));\n\t\telse if (*st->envmap && *st->envmapmask) /* dedicated reflectmask */\n\t\t\tQ_strlcpy(st->type, \"vmt/transition#ENVFROMMASK\", sizeof(st->type));\n\t\telse /* take from normalmap */\n\t\t\tQ_strlcpy(st->type, \"vmt/transition\", sizeof(st->type));\n\n\t\tQ_strlcatfz(script, &offset, sizeof(script),\t\"\\tprogram \\\"%s%s\\\"\\n\", st->type, progargs);\n\t\tQ_strlcatfz(script, &offset, sizeof(script),\t\"\\tdiffusemap \\\"%s%s.vtf\\\"\\n\", strcmp(st->tex[0].name, \"materials/\")?\"materials/\":\"\", st->tex[0].name);\n\t\tQ_strlcatfz(script, &offset, sizeof(script),\t\"\\tuppermap \\\"%s%s.vtf\\\"\\n\", strcmp(st->tex[1].name, \"materials/\")?\"materials/\":\"\", st->tex[1].name);\n\n\t\t/* there's also bumpmap2, but rtlight glsl doesn't respect it anyway. */\n\t\tif (*st->normalmap)\n\t\t\tQ_strlcatfz(script, &offset, sizeof(script),\t\"\\tnormalmap \\\"%s%s.vtf\\\"\\n\", strcmp(st->normalmap, \"materials/\")?\"materials/\":\"\", st->normalmap);\n\t}\n\telse if (!Q_strcasecmp(st->type, \"UnlitTwoTexture\"))\n\t{\n\n\t\tQ_strlcatfz(script, &offset, sizeof(script), \"{\\n\");\n\t\tQ_strlcatfz(script, &offset, sizeof(script),\t\"\\tmap \\\"%s%s.vtf\\\"\\n\", strcmp(st->tex[0].name, \"materials/\")?\"materials/\":\"\", st->tex[0].name);\n\n\t\tif (st->mod2x) {\n\t\t\tQ_strlcatfz(script, &offset, sizeof(script),\"\\t\\tblendFunc gl_dst_color gl_src_color\\n\");\n\t\t} else if (st->additive) {\n\t\t\tQ_strlcatfz(script, &offset, sizeof(script),\"\\t\\tblendFunc add\\n\");\n\t\t}\n\n\t\tQ_strlcatfz(script, &offset, sizeof(script), \"}\\n\");\n\t}\n\telse if (!Q_strcasecmp(st->type, \"Sprite\"))\n\t{\n\t\tQ_strlcatfz(script, &offset, sizeof(script), \"{\\n\");\n\t\tQ_strlcatfz(script, &offset, sizeof(script),\t\"\\tmap \\\"%s%s.vtf\\\"\\n\", strcmp(st->tex[0].name, \"materials/\")?\"materials/\":\"\", st->tex[0].name);\n\t\tQ_strlcatfz(script, &offset, sizeof(script),\t\"\\rrgbGen vertex\\n\");\n\t\tQ_strlcatfz(script, &offset, sizeof(script),\t\"\\tblendFunc add\\n\");\n\t\tQ_strlcatfz(script, &offset, sizeof(script), \"}\\n\");\n\t}\n\telse if (!Q_strcasecmp(st->type, \"Decal\"))\n\t{\n\t\tQ_strlcpy(st->type, \"vmt/vertexlit\", sizeof(st->type));\n\t\tQ_strlcatfz(script, &offset, sizeof(script),\t\"\\tprogram \\\"%s%s\\\"\\n\", st->type, progargs);\n\t\tQ_strlcatfz(script, &offset, sizeof(script),\t\"\\tdiffusemap \\\"%s%s.vtf\\\"\\n\", strcmp(st->tex[0].name, \"materials/\")?\"materials/\":\"\", st->tex[0].name);\n\t\tQ_strlcatfz(script, &offset, sizeof(script),\t\"\\tpolygonOffset 1\\n\");\n\t\tst->decal = 0;\n\t}\n\telse if (!Q_strcasecmp(st->type, \"DecalModulate\"))\n\t{\n\t\tQ_strlcatfz(script, &offset, sizeof(script),\t\"\\tpolygonOffset 1\\n\");\n\t\tQ_strlcatfz(script, &offset, sizeof(script),\t\"\\t{\\n\");\n\t\tQ_strlcatfz(script, &offset, sizeof(script),\t\"\\t\\tmap \\\"%s%s.vtf\\\"\\n\", strcmp(st->tex[0].name, \"materials/\")?\"materials/\":\"\", st->tex[0].name);\n\n\t\tif (!st->mod2x) {\n\t\t\tQ_strlcatfz(script, &offset, sizeof(script),\"\\t\\tblendFunc gl_dst_color gl_src_color\\n\");\n\t\t} else {\n\t\t\tQ_strlcatfz(script, &offset, sizeof(script),\"\\t\\tblendFunc gl_dst_color gl_one_minus_src_alpha\\n\");\n\t\t}\n\n\t\tQ_strlcatfz(script, &offset, sizeof(script),\t\"\\t}\\n\");\n\t\tst->decal = 0;\n\t\tst->translucent = 0;\n\t}\n\telse if (!Q_strcasecmp(st->type, \"Modulate\"))\n\t{\n\n\t\tQ_strlcatfz(script, &offset, sizeof(script),\t\"\\t{\\n\");\n\t\tQ_strlcatfz(script, &offset, sizeof(script),\t\"\\t\\tmap \\\"%s%s.vtf\\\"\\n\", strcmp(st->tex[0].name, \"materials/\")?\"materials/\":\"\", st->tex[0].name);\n\n\t\tif (!st->mod2x) {\n\t\t\tQ_strlcatfz(script, &offset, sizeof(script),\"\\t\\tblendFunc gl_dst_color gl_src_color\\n\");\n\t\t} else {\n\t\t\tQ_strlcatfz(script, &offset, sizeof(script),\"\\t\\tblendFunc gl_dst_color gl_one_minus_src_alpha\\n\");\n\t\t}\n\t\tQ_strlcatfz(script, &offset, sizeof(script),\t\"\\trgbGen vertex\\n\");\n\n\t\tQ_strlcatfz(script, &offset, sizeof(script),\t\"\\t}\\n\");\n\t\tst->translucent = 0;\n\t}\n\telse if (!Q_strcasecmp(st->type, \"Water\"))\n\t{\n\t\tif (st->water_cheap)\n\t\t\tprogargs = \"#LQWATER\";\n\t\tif (st->water_expensive)\n\t\t\tprogargs = \"#HQWATER\";\n\n\t\tQ_strlcatfz(script, &offset, sizeof(script),\n\t\t\t\"\\t{\\n\"\n\t\t\t\t\"\\t\\tprogram \\\"vmt/water%s\\\"\\n\"\n\t\t\t\t\"\\t\\tmap $refraction\\n\"\n\t\t\t\t\"\\t\\tmap $reflection\\n\"\n\t\t\t\"\\t}\\n\", progargs);\n\t\tQ_strlcatfz(script, &offset, sizeof(script),\t\"\\tdiffusemap \\\"%s%s.vtf\\\"\\n\", strcmp(st->tex[0].name, \"materials/\")?\"materials/\":\"\", st->tex[0].name);\n\t\tQ_strlcatfz(script, &offset, sizeof(script),\t\"\\tnormalmap \\\"%s%s.vtf\\\"\\n\", strcmp(st->normalmap, \"materials/\")?\"materials/\":\"\", st->normalmap);\n\t\tQ_strlcatfz(script, &offset, sizeof(script),\t\"\\tsurfaceparm nodlight\\n\");\n\t\tQ_strlcatfz(script, &offset, sizeof(script),\t\"\\tsurfaceparm trans\\n\");\n\t\tQ_strlcatfz(script, &offset, sizeof(script),\t\"\\tsurfaceparm alphashadow\\n\");\n\t}\n\telse if (!Q_strcasecmp(st->type, \"Refract\"))\n\t{\n\t\tif (*st->refracttinttexture)\n\t\t\tprogargs = \"#TINTTEXTURE\";\n\n\t\tQ_strlcatfz(script, &offset, sizeof(script),\n\t\t\t\"\\t{\\n\"\n\t\t\t\t\"\\t\\tprogram \\\"vmt/refract%s\\\"\\n\"\n\t\t\t\t\"\\t\\tmap $currentrender\\n\"\n\t\t\t\t\"\\t\\tmap \\\"%s%s.vtf\\\"\\n\"\n\t\t\t\"\\t}\\n\", progargs, strcmp(st->normalmap, \"materials/\")?\"materials/\":\"\", st->normalmap);\n\n\t\tif (*st->refracttinttexture)\n\t\t\tQ_strlcatfz(script, &offset, sizeof(script),\t\"\\tdiffusemap \\\"%s%s.vtf\\\"\\n\", strcmp(st->refracttinttexture, \"materials/\")?\"materials/\":\"\", st->refracttinttexture);\n\n\t\tQ_strlcatfz(script, &offset, sizeof(script),\t\"\\tnormalmap \\\"%s%s.vtf\\\"\\n\", strcmp(st->normalmap, \"materials/\")?\"materials/\":\"\", st->normalmap);\n\t}\n\telse if (!Q_strcasecmp(st->type, \"VertexlitGeneric\") || st->decal)\n\t{\n\n\t\t\tif (*st->envmap && st->envfrombase)\n\t\t\t{\n\t\t\t\tif (st->halflambert)\n\t\t\t\t\tQ_strlcpy(st->type, \"vmt/vertexlit#ENVFROMBASE#HALFLAMBERT\", sizeof(st->type));\n\t\t\t\telse\n\t\t\t\t\tQ_strlcpy(st->type, \"vmt/vertexlit#ENVFROMBASE\", sizeof(st->type));\n\t\t\t}\n\t\t\telse if (*st->envmap && st->envfromnorm)\n\t\t\t{\n\t\t\t\tif (st->halflambert)\n\t\t\t\t\tQ_strlcpy(st->type, \"vmt/vertexlit#ENVFROMNORM#HALFLAMBERT\", sizeof(st->type));\n\t\t\t\telse\n\t\t\t\t\tQ_strlcpy(st->type, \"vmt/vertexlit#ENVFROMNORM\", sizeof(st->type));\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (st->halflambert)\n\t\t\t\t\tQ_strlcpy(st->type, \"vmt/vertexlit#HALFLAMBERT\", sizeof(st->type));\n\t\t\t\telse\n\t\t\t\t\tQ_strlcpy(st->type, \"vmt/vertexlit\", sizeof(st->type));\n\t\t\t}\n\n\n\n\t\tQ_strlcatfz(script, &offset, sizeof(script),\t\"\\tdiffusemap \\\"%s%s.vtf\\\"\\n\", strcmp(st->tex[0].name, \"materials/\")?\"materials/\":\"\", st->tex[0].name);\n\n\t\tif (*st->normalmap) {\n\t\t\tQ_strlcatfz(script, &offset, sizeof(script),\t\"\\tnormalmap \\\"%s%s.vtf\\\"\\n\", strcmp(st->normalmap, \"materials/\")?\"materials/\":\"\", st->normalmap);\n\t\t}\n#if 0\n\t\tif (st->additive)\n\t\t\tst->blendfunc = \"src_one dst_one\";\n\t\telse if (st->translucent)\n\t\t\tst->blendfunc = \"src_alpha one_minus_src_alpha\";\n#endif\n\n\t\tQ_strlcatfz(script, &offset, sizeof(script),\t\"\\treflectcube $cube:materials/skybox/sky_day03_06\\n\");\n\t\tQ_strlcatfz(script, &offset, sizeof(script),\t\"\\t{\\n\\t\\tprogram \\\"%s%s%s%s\\\"\\n\\t\\tmap $rt:$linear:rtenvsphere\\n\\t}\\n\", st->type, progargs, envmaptint, envmapsat);\n\n\t}\n\telse if (!Q_strcasecmp(st->type, \"LightmappedGeneric\"))\n\t{\n\t\t/* reflectmask from diffuse map alpha */\n\n\t\t\tif (*st->envmap && st->envfrombase)\n\t\t\t\tQ_strlcpy(st->type, \"vmt/lightmapped#ENVFROMBASE\", sizeof(st->type));\n\t\t\telse if (*st->envmap && st->envfromnorm)\n\t\t\t\tQ_strlcpy(st->type, \"vmt/lightmapped#ENVFROMNORM\", sizeof(st->type));\n\t\t\telse if (*st->envmap && *st->envmapmask) /* dedicated reflectmask */\n\t\t\t\tQ_strlcpy(st->type, \"vmt/lightmapped#ENVFROMMASK\", sizeof(st->type));\n\t\t\telse /* take from normalmap */\n\t\t\t\tQ_strlcpy(st->type, \"vmt/lightmapped\", sizeof(st->type));\n\n\t\t\tQ_strlcatfz(script, &offset, sizeof(script),\t\"\\tprogram \\\"%s%s%s%s\\\"\\n\", st->type, progargs, envmaptint, envmapsat);\n\n\n\t\tQ_strlcatfz(script, &offset, sizeof(script),\t\"\\tdiffusemap \\\"%s%s.vtf\\\"\\n\", strcmp(st->tex[0].name, \"materials/\")?\"materials/\":\"\", st->tex[0].name);\n\n\t\tif (*st->normalmap)\n\t\t\tQ_strlcatfz(script, &offset, sizeof(script),\t\"\\tnormalmap \\\"%s%s.vtf\\\"\\n\", strcmp(st->normalmap, \"materials/\")?\"materials/\":\"\", st->normalmap);\n\t}\n\telse\n\t{\n\t\t/* render-target camera/monitor - eukara*/\n\t\tif (!Q_strcasecmp(st->tex[0].name, \"_rt_Camera\"))\n\t\t\tQ_strlcatfz(script, &offset, sizeof(script),\n\t\t\t\"\\t{\\n\"\n\t\t\t\t\"\\t\\tprogram vmt/rt\\n\"\n\t\t\t\t\"\\t\\tmap $rt:base\\n\"\n\t\t\t\"\\t}\\n\"/*, progargs*/);\n\t\telse\n\t\t{\n\t\t\t/* the default should just be unlit, let's not make any assumptions - eukara*/\n\t\t\tQ_strlcpy(st->type, \"vmt/unlit\", sizeof(st->type));\n\t\t\tQ_strlcatfz(script, &offset, sizeof(script),\t\"\\tprogram \\\"%s%s\\\"\\n\", st->type, progargs);\n\t\t\tQ_strlcatfz(script, &offset, sizeof(script),\t\"\\tdiffusemap \\\"%s%s.vtf\\\"\\n\", strcmp(st->tex[0].name, \"materials/\")?\"materials/\":\"\", st->tex[0].name);\n\t\t}\n\t}\n\n\tif (st->translucent) {\n\t\tst->blendfunc = \"src_alpha one_minus_src_alpha\";\n\t}\n\n\tif (st->decal) {\n\t\tQ_strlcatfz(script, &offset, sizeof(script),\t\"\\tpolygonOffset 1\\n\");\n\t\tQ_strlcatfz(script, &offset, sizeof(script),\t\"\\trgbGen vertex\\n\");\n\t}\n\n\tif (*st->fullbrightmap)\n\t\tQ_strlcatfz(script, &offset, sizeof(script), \"\\tfullbrightmap \\\"%s%s.vtf\\\"\\n\", strcmp(st->fullbrightmap, \"materials/\")?\"materials/\":\"\", st->fullbrightmap);\n\telse if (st->selfillum == 1)\n\t\tQ_strlcatfz(script, &offset, sizeof(script), \"\\tfullbrightmap \\\"%s%s.vtf\\\"\\n\", strcmp(st->tex[0].name, \"materials/\")?\"materials/\":\"\", st->tex[0].name);\n\n\tif (*st->envmapmask)\n\t\tQ_strlcatfz(script, &offset, sizeof(script),\t\"\\treflectmask \\\"%s%s.vtf\\\"\\n\", strcmp(st->envmapmask, \"materials/\")?\"materials/\":\"\", st->envmapmask);\n\tif (*st->envmap && strcmp(st->envmap, \"env_cubemap\"))\n\t\tQ_strlcatfz(script, &offset, sizeof(script),\t\"\\treflectcube \\\"%s%s.vtf\\\"\\n\", strcmp(st->envmap, \"materials/\")?\"materials/\":\"\", st->envmap);\n\tif (st->alphatest)\n\t\tQ_strlcatfz(script, &offset, sizeof(script), \"\\talphatest ge128\\n\");\n\tif (st->culldisable)\n\t\tQ_strlcatfz(script, &offset, sizeof(script), \"\\tcull disable\\n\");\n\tif (st->ignorez)\n\t\tQ_strlcatfz(script, &offset, sizeof(script), \"\\tnodepth\\n\");\n\tif (st->blendfunc)\n\t\tQ_strlcatfz(script, &offset, sizeof(script), \"\\tprogblendfunc %s\\n\", st->blendfunc);\n\tif (*st->color)\n\t\tQ_strlcatfz(script, &offset, sizeof(script), \"\\trgbGen const %s\\n\", st->color);\n\n\tQ_strlcatfz(script, &offset, sizeof(script), \"}\\n\");\n\n\tLoadMaterialString(ps, script);\n\n\tif (st->sourcefile)\n\t{\t//cat the original file on there...\n\t\tif (st->savefile)\n\t\t{\n\t\t\tchar *winsucks;\t//strip any '\\r' chars in there that like to show as ugly glyphs.\n\t\t\tfor (winsucks = st->sourcefile; *winsucks; winsucks++)\n\t\t\t\tif (*winsucks=='\\r')\n\t\t\t\t\t*winsucks = ' ';\n\n\t\t\tQ_StrCat(st->savefile, \"\\n/*\\n\");\n\t\t\tQ_StrCat(st->savefile, st->sourcefile);\n\t\t\tQ_StrCat(st->savefile, \"*/\");\n\t\t}\n\t\tplugfuncs->Free(st->sourcefile);\n\t}\n}\nstatic qboolean VMT_ReadVMT(const char *fname, vmtstate_t *st)\n{\n\tchar *line, *file = NULL;\n\tcom_tokentype_t ttype;\n\tchar token[MAX_QPATH*2];\n\tchar *prefix=\"\", *postfix=\"\";\n\n\tif (strstr(fname, \"://\"))\n\t\treturn false;\t//don't try to handle urls.\n\n\t//don't dupe the mandatory materials/ prefix\n\tif (strncmp(fname, \"materials/\", 10))\n\t\tprefix = \"materials/\";\n\tif (strcmp(fsfuncs->GetExtension(fname, NULL), \".vmt\"))\n\t\tpostfix = \".vmt\";\n\tQ_snprintfz(token, sizeof(token), \"%s%s%s\", prefix, fname, postfix);\n\n\tfile = fsfuncs->LoadFile(token, NULL);\n\tif (file)\n\t{\n\t\tif (st->savefile)\n\t\t{\n\t\t\tif (st->sourcefile)\n\t\t\t{\n\t\t\t\tQ_StrCat(&st->sourcefile, fname);\n\t\t\t\tQ_StrCat(&st->sourcefile, \":\\n\");\n\t\t\t\tQ_StrCat(&st->sourcefile, file);\n\t\t\t}\n\t\t\telse\n\t\t\t\tQ_StrCat(&st->sourcefile, file);\n\t\t}\n\n\t\tline = file;\n\t\tline = cmdfuncs->ParseToken(line, st->type, sizeof(st->type), &ttype);\n\t\tline = cmdfuncs->ParseToken(line, token, sizeof(token), &ttype);\n\t\tif (!strcmp(token, \"{\"))\n\t\t{\n\t\t\tline = VMT_ParseBlock(fname, st, line);\n\t\t}\n\n\t\tplugfuncs->Free(file);\n\t\treturn !!line;\n\t}\n\treturn false;\n}\nstatic qboolean Shader_LoadVMT(parsestate_t *ps, const char *filename, void (*LoadMaterialString)(parsestate_t *ps, const char *script))\n{\n\tvmtstate_t st;\n\tmemset(&st, 0, sizeof(st));\n\tst.savefile = NULL;//ps->saveshaderbody;\n\tif (!VMT_ReadVMT(filename, &st))\n\t{\n\t\tif (st.sourcefile)\n\t\t\tplugfuncs->Free(st.sourcefile);\n\t\treturn false;\n\t}\n\n\tShader_GenerateFromVMT(ps, &st, filename, LoadMaterialString);\n\treturn true;\n}\n\nstatic struct sbuiltin_s vmtprograms[] =\n{\n//we don't know what renderer the engine will need...\n#ifdef FTEPLUGIN\n\t#ifndef GLQUAKE\n\t\t#define GLQUAKE\n\t#endif\n\t#ifndef VKQUAKE\n\t\t#define VKQUAKE\n\t#endif\n\t#ifndef D3DQUAKE\n\t\t#define D3DQUAKE\n\t#endif\n#endif\n#include \"mat_vmt_progs.h\"\n\t{QR_NONE}\n};\nstatic plugmaterialloaderfuncs_t vmtfuncs =\n{\n\t\"HL2 VMT\",\n\tShader_LoadVMT,\n\n\tvmtprograms,\n};\n\nqboolean VMT_Init(void)\n{\n\tfsfuncs = plugfuncs->GetEngineInterface(plugfsfuncs_name, sizeof(*fsfuncs));\n\tif (!fsfuncs)\n\t\treturn false;\n\treturn plugfuncs->ExportInterface(plugmaterialloaderfuncs_name, &vmtfuncs, sizeof(vmtfuncs));\n}\n"
  },
  {
    "path": "plugins/hl2/mat_vmt_progs.h",
    "content": "/*\nWARNING: THIS FILE IS GENERATED BY 'generatebuiltinsl.c'.\nYOU SHOULD NOT EDIT THIS FILE BY HAND\n*/\n\n#ifdef GLQUAKE\n{QR_OPENGL, 110, \"vmt/lightmapped\",\n\"!!ver 110\\n\"\n\"!!permu FOG\\n\"\n\"!!permu BUMP\\n\"\n\"!!permu LIGHTSTYLED\\n\"\n\"!!permu FULLBRIGHT\\n\"\n\"!!permu REFLECTCUBEMASK\\n\"\n\"!!permu NOFOG\\n\"\n\"!!samps diffuse\\n\"\n\n\"!!samps lightmap\\n\"\n\"!!samps =LIGHTSTYLED lightmap1 lightmap2 lightmap3\\n\"\n\n\"!!samps =BUMP normalmap\\n\"\n\"!!samps =FULLBRIGHT fullbright\\n\"\n\n// envmaps only\n\"!!samps =REFLECTCUBEMASK reflectmask reflectcube\\n\"\n\n\"!!permu FAKESHADOWS\\n\"\n\"!!cvardf r_glsl_pcf\\n\"\n\"!!samps =FAKESHADOWS shadowmap\\n\"\n\n\"#ifndef ENVTINT\\n\"\n\"#define ENVTINT 1.0,1.0,1.0\\n\"\n\"#endif\\n\"\n\n\"#ifndef ENVSAT\\n\"\n\"#define ENVSAT 1.0,1.0,1.0\\n\"\n\"#endif\\n\"\n\n\"#include \\\"sys/defs.h\\\"\\n\"\n\n\"varying vec2 tex_c;\\n\"\n\n\"varying vec2 lm0;\\n\"\n\n\"#ifdef LIGHTSTYLED\\n\"\n\"varying vec2 lm1, lm2, lm3;\\n\"\n\"#endif\\n\"\n\n\"#ifdef FAKESHADOWS\\n\"\n\"varying vec4 vtexprojcoord;\\n\"\n\"#endif\\n\"\n\n/* CUBEMAPS ONLY */\n\"#ifdef REFLECTCUBEMASK\\n\"\n\"varying vec3 eyevector;\\n\"\n\"varying mat3 invsurface;\\n\"\n\"#endif\\n\"\n\n\"#ifdef VERTEX_SHADER\\n\"\n\"void lightmapped_init(void)\\n\"\n\"{\\n\"\n\"lm0 = v_lmcoord;\\n\"\n\"#ifdef LIGHTSTYLED\\n\"\n\"lm1 = v_lmcoord2;\\n\"\n\"lm2 = v_lmcoord3;\\n\"\n\"lm3 = v_lmcoord4;\\n\"\n\"#endif\\n\"\n\"}\\n\"\n\n\"void main ()\\n\"\n\"{\\n\"\n\"lightmapped_init();\\n\"\n\"tex_c = v_texcoord;\\n\"\n\"gl_Position = ftetransform();\\n\"\n\n/* CUBEMAPS ONLY */\n\"#ifdef REFLECTCUBEMASK\\n\"\n\"invsurface = mat3(v_svector, v_tvector, v_normal);\\n\"\n\n\"vec3 eyeminusvertex = e_eyepos - v_position.xyz;\\n\"\n\"eyevector.x = dot(eyeminusvertex, v_svector.xyz);\\n\"\n\"eyevector.y = dot(eyeminusvertex, v_tvector.xyz);\\n\"\n\"eyevector.z = dot(eyeminusvertex, v_normal.xyz);\\n\"\n\"#endif\\n\"\n\n\"#ifdef FAKESHADOWS\\n\"\n\"vtexprojcoord = (l_cubematrix*vec4(v_position.xyz, 1.0));\\n\"\n\"#endif\\n\"\n\"}\\n\"\n\"#endif\\n\"\n\n\"#ifdef FRAGMENT_SHADER\\n\"\n\"#include \\\"sys/fog.h\\\"\\n\"\n\n\"#ifdef FAKESHADOWS\\n\"\n\"#include \\\"sys/pcf.h\\\"\\n\"\n\"#endif\\n\"\n\n\"#ifdef LIGHTSTYLED\\n\"\n\"#define LIGHTMAP0 texture2D(s_lightmap0, lm0).rgb\\n\"\n\"#define LIGHTMAP1 texture2D(s_lightmap1, lm1).rgb\\n\"\n\"#define LIGHTMAP2 texture2D(s_lightmap2, lm2).rgb\\n\"\n\"#define LIGHTMAP3 texture2D(s_lightmap3, lm3).rgb\\n\"\n\"#else\\n\"\n\"#define LIGHTMAP texture2D(s_lightmap, lm0).rgb \\n\"\n\"#endif\\n\"\n\n\"vec3 lightmap_fragment()\\n\"\n\"{\\n\"\n\"vec3 lightmaps;\\n\"\n\n\"#ifdef LIGHTSTYLED\\n\"\n\"lightmaps  = LIGHTMAP0 * e_lmscale[0].rgb;\\n\"\n\"lightmaps += LIGHTMAP1 * e_lmscale[1].rgb;\\n\"\n\"lightmaps += LIGHTMAP2 * e_lmscale[2].rgb;\\n\"\n\"lightmaps += LIGHTMAP3 * e_lmscale[3].rgb;\\n\"\n\"#else\\n\"\n\"lightmaps  = LIGHTMAP * e_lmscale.rgb;\\n\"\n\"#endif\\n\"\n\"return lightmaps;\\n\"\n\"}\\n\"\n\n\"vec3 env_saturation(vec3 rgb, float adjustment) {\\n\"\n\"vec3 intensity = vec3(dot(rgb, vec3(0.2126,0.7152,0.0722)));\\n\"\n\"return mix(intensity, rgb, adjustment);\\n\"\n\"}\\n\"\n\n\"void main (void)\\n\"\n\"{\\n\"\n\"vec4 diffuse_f;\\n\"\n\n\"diffuse_f = texture2D(s_diffuse, tex_c);\\n\"\n\"diffuse_f.rgb *= e_colourident.rgb;\\n\"\n\n\"#ifdef MASKLT\\n\"\n\"if (diffuse_f.a < float(MASK))\\n\"\n\"discard;\\n\"\n\"#endif\\n\"\n\n\"#ifdef FAKESHADOWS\\n\"\n\"diffuse_f.rgb *= ShadowmapFilter(s_shadowmap, vtexprojcoord);\\n\"\n\"#endif\\n\"\n\n/* deluxemapping isn't working on Source BSP yet */\n\"diffuse_f.rgb *= lightmap_fragment();\\n\"\n\n/* CUBEMAPS ONLY */\n\"#ifdef REFLECTCUBEMASK\\n\"\n/* We currently only use the normal/bumpmap for cubemap warping. move this block out once we do proper radiosity normalmapping */\n\"#ifdef BUMP\\n\"\n/* Source's normalmaps are in the DX format where the green channel is flipped */\n\"vec4 normal_f = texture2D(s_normalmap, tex_c);\\n\"\n\"normal_f.g = 1.0 - normal_f.g;\\n\"\n\"normal_f.rgb = normalize(normal_f.rgb - 0.5);\\n\"\n\"#else\\n\"\n\"vec4 normal_f = vec4(0.0,0.0,1.0,0.0);\\n\"\n\"#endif\\n\"\n\n\n\"#if defined(ENVFROMMASK)\\n\"\n/* We have a dedicated reflectmask */\n\"#define refl texture2D(s_reflectmask, tex_c).r\\n\"\n\"#else\\n\"\n/* when ENVFROMBASE is set or a normal isn't present, we're getting the reflectivity info from the diffusemap's alpha channel */\n\"#if defined(ENVFROMBASE) || !defined(BUMP)\\n\"\n\"#define refl 1.0 - diffuse_f.a\\n\"\n\"#else\\n\"\n/* when ENVFROMNORM is set, we don't invert the refl */\n\"#if defined(ENVFROMNORM)\\n\"\n\"#define refl texture2D(s_normalmap, tex_c).a\\n\"\n\"#else\\n\"\n\"#define refl 1.0 - texture2D(s_normalmap, tex_c).a\\n\"\n\"#endif\\n\"\n\"#endif\\n\"\n\"#endif\\n\"\n\n\n\"vec3 cube_c = reflect(-eyevector, normal_f.rgb);\\n\"\n\"vec3 cube_tint = vec3(ENVTINT);\\n\"\n\"vec3 cube_sat = vec3(ENVSAT);\\n\"\n\"cube_c = cube_c.x * invsurface[0] + cube_c.y * invsurface[1] + cube_c.z * invsurface[2];\\n\"\n\"cube_c = (m_model * vec4(cube_c.xyz, 0.0)).xyz;\\n\"\n\"vec3 cube_t = env_saturation(textureCube(s_reflectcube, cube_c).rgb, cube_sat.r);\\n\"\n\"cube_t.r *= cube_tint.r;\\n\"\n\"cube_t.g *= cube_tint.g;\\n\"\n\"cube_t.b *= cube_tint.b;\\n\"\n\"diffuse_f.rgb += (cube_t * vec3(refl,refl,refl));\\n\"\n\"#endif\\n\"\n\n\"#ifdef FULLBRIGHT\\n\"\n\"diffuse_f.rgb += texture2D(s_fullbright, tex_c).rgb * texture2D(s_fullbright, tex_c).a;\\n\"\n\"#endif\\n\"\n\n\"#ifdef NOFOG\\n\"\n\"gl_FragColor = diffuse_f;\\n\"\n\"#else\\n\"\n\"gl_FragColor = fog4(diffuse_f);\\n\"\n\"#endif\\n\"\n\"}\\n\"\n\"#endif\\n\"\n},\n#endif\n#ifdef GLQUAKE\n{QR_OPENGL, 110, \"vmt/refract\",\n\"!!ver 110\\n\"\n\"!!permu BUMP\\n\"\n\"!!samps diffuse\\n\"\n\"!!samps =BUMP normalmap\\n\"\n\"!!samps refraction=0 dudvmap=1\\n\"\n\n\"#include \\\"sys/defs.h\\\"\\n\"\n\n\"varying vec2 tex_c;\\n\"\n\"varying mat3 invsurface;\\n\"\n\"varying vec4 tf_c;\\n\"\n\"varying vec3 eyeminusvertex;\\n\"\n\n\"#ifdef VERTEX_SHADER\\n\"\n\"void main ()\\n\"\n\"{\\n\"\n\"invsurface[0] = v_svector;\\n\"\n\"invsurface[1] = v_tvector;\\n\"\n\"invsurface[2] = v_normal;\\n\"\n\"tf_c = ftetransform();\\n\"\n\"tex_c = v_texcoord;\\n\"\n\"gl_Position = tf_c;\\n\"\n\"}\\n\"\n\"#endif\\n\"\n\n\"#ifdef FRAGMENT_SHADER\\n\"\n\"#include \\\"sys/fog.h\\\"\\n\"\n\"void main ( void )\\n\"\n\"{\\n\"\n\"vec2 refl_c;\\n\"\n\"vec3 refr_f;\\n\"\n\"vec3 dudv_f;\\n\"\n\"vec4 out_f = vec4( 1.0, 1.0, 1.0, 1.0 );\\n\"\n\n\"dudv_f = ( texture2D( s_dudvmap, tex_c).xyz);\\n\"\n\"dudv_f += ( texture2D( s_dudvmap, tex_c).xyz);\\n\"\n\"dudv_f -= 1.0 - ( 4.0 / 256.0 );\\n\"\n\"dudv_f = normalize( dudv_f );\\n\"\n\n// Reflection/View coordinates\n\"refl_c = ( 1.0 + ( tf_c.xy / tf_c.w ) ) * 0.5;\\n\"\n\n\"refr_f = texture2D( s_refraction, refl_c + ( dudv_f.st) ).rgb;\\n\"\n\"out_f.rgb = refr_f;\\n\"\n\"#ifdef TINTTEXTURE\\n\"\n\"out_f.rgb *= texture2D( s_diffuse, tex_c ).rgb;\\n\"\n\"#endif\\n\"\n\n\"gl_FragColor = out_f;\\n\"\n\"}\\n\"\n\"#endif\\n\"\n},\n#endif\n#ifdef GLQUAKE\n{QR_OPENGL, 110, \"vmt/transition\",\n\"!!ver 110\\n\"\n\"!!permu FOG\\n\"\n\"!!permu BUMP\\n\"\n\"!!permu LIGHTSTYLED\\n\"\n\"!!permu REFLECTCUBEMASK\\n\"\n\"!!permu UPPERLOWER\\n\"\n\"!!samps diffuse upper\\n\"\n\n\"!!samps lightmap\\n\"\n\"!!samps =LIGHTSTYLED lightmap1 lightmap2 lightmap3\\n\"\n\n\"!!samps =BUMP normalmap\\n\"\n\n// envmaps only\n\"!!samps =REFLECTCUBEMASK reflectmask reflectcube\\n\"\n\n\"!!permu FAKESHADOWS\\n\"\n\"!!cvardf r_glsl_pcf\\n\"\n\"!!samps =FAKESHADOWS shadowmap\\n\"\n\n\"#include \\\"sys/defs.h\\\"\\n\"\n\n\"varying vec2 tex_c;\\n\"\n\"varying vec4 vex_color;\\n\"\n\n\"varying vec2 lm0;\\n\"\n\n\"#ifdef LIGHTSTYLED\\n\"\n\"varying vec2 lm1, lm2, lm3;\\n\"\n\"#endif\\n\"\n\n\"#ifdef FAKESHADOWS\\n\"\n\"varying vec4 vtexprojcoord;\\n\"\n\"#endif\\n\"\n\n/* CUBEMAPS ONLY */\n\"#ifdef REFLECTCUBEMASK\\n\"\n\"varying vec3 eyevector;\\n\"\n\"varying mat3 invsurface;\\n\"\n\"#endif\\n\"\n\n\"#ifdef VERTEX_SHADER\\n\"\n\"void lightmapped_init(void)\\n\"\n\"{\\n\"\n\"lm0 = v_lmcoord;\\n\"\n\"#ifdef LIGHTSTYLED\\n\"\n\"lm1 = v_lmcoord2;\\n\"\n\"lm2 = v_lmcoord3;\\n\"\n\"lm3 = v_lmcoord4;\\n\"\n\"#endif\\n\"\n\"}\\n\"\n\n\"void main ()\\n\"\n\"{\\n\"\n\"lightmapped_init();\\n\"\n\"tex_c = v_texcoord;\\n\"\n\"gl_Position = ftetransform();\\n\"\n\"vex_color = v_colour;\\n\"\n\n/* CUBEMAPS ONLY */\n\"#ifdef REFLECTCUBEMASK\\n\"\n\"invsurface = mat3(v_svector, v_tvector, v_normal);\\n\"\n\n\"vec3 eyeminusvertex = e_eyepos - v_position.xyz;\\n\"\n\"eyevector.x = dot(eyeminusvertex, v_svector.xyz);\\n\"\n\"eyevector.y = dot(eyeminusvertex, v_tvector.xyz);\\n\"\n\"eyevector.z = dot(eyeminusvertex, v_normal.xyz);\\n\"\n\"#endif\\n\"\n\n\"#ifdef FAKESHADOWS\\n\"\n\"vtexprojcoord = (l_cubematrix*vec4(v_position.xyz, 1.0));\\n\"\n\"#endif\\n\"\n\"}\\n\"\n\"#endif\\n\"\n\n\"#ifdef FRAGMENT_SHADER\\n\"\n\"#include \\\"sys/fog.h\\\"\\n\"\n\n\"#ifdef FAKESHADOWS\\n\"\n\"#include \\\"sys/pcf.h\\\"\\n\"\n\"#endif\\n\"\n\n\"#ifdef LIGHTSTYLED\\n\"\n\"#define LIGHTMAP0 texture2D(s_lightmap0, lm0).rgb\\n\"\n\"#define LIGHTMAP1 texture2D(s_lightmap1, lm1).rgb\\n\"\n\"#define LIGHTMAP2 texture2D(s_lightmap2, lm2).rgb\\n\"\n\"#define LIGHTMAP3 texture2D(s_lightmap3, lm3).rgb\\n\"\n\"#else\\n\"\n\"#define LIGHTMAP texture2D(s_lightmap, lm0).rgb \\n\"\n\"#endif\\n\"\n\n\"vec3 lightmap_fragment()\\n\"\n\"{\\n\"\n\"vec3 lightmaps;\\n\"\n\n\"#ifdef LIGHTSTYLED\\n\"\n\"lightmaps  = LIGHTMAP0 * e_lmscale[0].rgb;\\n\"\n\"lightmaps += LIGHTMAP1 * e_lmscale[1].rgb;\\n\"\n\"lightmaps += LIGHTMAP2 * e_lmscale[2].rgb;\\n\"\n\"lightmaps += LIGHTMAP3 * e_lmscale[3].rgb;\\n\"\n\"#else\\n\"\n\"lightmaps  = LIGHTMAP * e_lmscale.rgb;\\n\"\n\"#endif\\n\"\n\n/* the light we're getting is always too bright */\n\"lightmaps *= 0.75;\\n\"\n\n/* clamp at 1.5 */\n\"if (lightmaps.r > 1.5)\\n\"\n\"lightmaps.r = 1.5;\\n\"\n\"if (lightmaps.g > 1.5)\\n\"\n\"lightmaps.g = 1.5;\\n\"\n\"if (lightmaps.b > 1.5)\\n\"\n\"lightmaps.b = 1.5;\\n\"\n\n\"return lightmaps;\\n\"\n\"}\\n\"\n\n\"void main (void)\\n\"\n\"{\\n\"\n\"vec4 diffuse_f;\\n\"\n\"diffuse_f.rgb = mix(texture2D(s_diffuse, tex_c).rgb,  texture2D(s_upper, tex_c).rgb, vex_color.a);\\n\"\n\"diffuse_f.a = 1.0;\\n\"\n\n/* deluxemapping isn't working on Source BSP yet, FIXME */\n\"diffuse_f.rgb *= lightmap_fragment();\\n\"\n\n\"#ifdef FAKESHADOWS\\n\"\n\"diffuse_f.rgb *= ShadowmapFilter(s_shadowmap, vtexprojcoord);\\n\"\n\"#endif\\n\"\n\n/* CUBEMAPS ONLY */\n\"#ifdef REFLECTCUBEMASK\\n\"\n/* We currently only use the normal/bumpmap for cubemap warping. move this block out once we do proper radiosity normalmapping */\n\"#ifdef BUMP\\n\"\n/* Source's normalmaps are in the DX format where the green channel is flipped */\n\"vec4 normal_f = texture2D(s_normalmap, tex_c);\\n\"\n\"normal_f.g = 1.0 - normal_f.g;\\n\"\n\"normal_f.rgb = normalize(normal_f.rgb - 0.5);\\n\"\n\"#else\\n\"\n\"vec4 normal_f = vec4(0.0,0.0,1.0,0.0);\\n\"\n\"#endif\\n\"\n\n\"#if defined(ENVFROMMASK)\\n\"\n/* We have a dedicated reflectmask */\n\"#define refl texture2D(s_reflectmask, tex_c).r\\n\"\n\"#else\\n\"\n/* when ENVFROMBASE is set or a normal isn't present, we're getting the reflectivity info from the diffusemap's alpha channel */\n\"#if defined(ENVFROMBASE) || !defined(BUMP)\\n\"\n\"#define refl 1.0 - diffuse_f.a\\n\"\n\"#else\\n\"\n/* when ENVFROMNORM is set, we don't invert the refl */\n\"#if defined(ENVFROMNORM)\\n\"\n\"#define refl texture2D(s_normalmap, tex_c).a\\n\"\n\"#else\\n\"\n\"#define refl 1.0 - texture2D(s_normalmap, tex_c).a\\n\"\n\"#endif\\n\"\n\"#endif\\n\"\n\"#endif\\n\"\n\n\"vec3 cube_c = reflect(normalize(-eyevector), normal_f.rgb);\\n\"\n\"cube_c = cube_c.x * invsurface[0] + cube_c.y * invsurface[1] + cube_c.z * invsurface[2];\\n\"\n\"cube_c = (m_model * vec4(cube_c.xyz, 0.0)).xyz;\\n\"\n\"diffuse_f.rgb += (textureCube(s_reflectcube, cube_c).rgb * vec3(refl,refl,refl));\\n\"\n\"#endif\\n\"\n\n\"gl_FragColor = fog4(diffuse_f);\\n\"\n\"}\\n\"\n\"#endif\\n\"\n},\n#endif\n#ifdef GLQUAKE\n{QR_OPENGL, 110, \"vmt/unlit\",\n\"!!ver 110\\n\"\n\"!!permu FOG\\n\"\n\"!!samps diffuse\\n\"\n\n\"#include \\\"sys/defs.h\\\"\\n\"\n\"#include \\\"sys/fog.h\\\"\\n\"\n\n\"varying vec2 tex_c;\\n\"\n\n\"#ifdef VERTEX_SHADER\\n\"\n\"void main ()\\n\"\n\"{\\n\"\n\"tex_c = v_texcoord;\\n\"\n\"gl_Position = ftetransform();\\n\"\n\"}\\n\"\n\"#endif\\n\"\n\n\"#ifdef FRAGMENT_SHADER\\n\"\n\"void main ()\\n\"\n\"{\\n\"\n\"vec4 diffuse_f = texture2D( s_diffuse, tex_c );\\n\"\n\n\"#ifdef MASKLT\\n\"\n\"if (diffuse_f.a < float(MASK))\\n\"\n\"discard;\\n\"\n\"#endif\\n\"\n\n\"gl_FragColor = fog4( diffuse_f );\\n\"\n\"}\\n\"\n\"#endif\\n\"\n},\n#endif\n#ifdef GLQUAKE\n{QR_OPENGL, 110, \"vmt/vertexlit\",\n\"!!ver 100 150\\n\"\n\"!!permu FRAMEBLEND\\n\"\n\"!!permu BUMP\\n\"\n\"!!permu FOG\\n\"\n\"!!permu NOFOG\\n\"\n\"!!permu SKELETAL\\n\"\n\"!!permu FULLBRIGHT\\n\"\n\"!!permu AMBIENTCUBE\\n\"\n\"!!permu REFLECTCUBEMASK\\n\"\n\"!!samps diffuse\\n\"\n\"!!samps =BUMP normalmap\\n\"\n\"!!samps =FULLBRIGHT fullbright\\n\"\n\"!!samps rtenvsphere:2D=0\\n\"\n\"!!permu FAKESHADOWS\\n\"\n\"!!cvardf r_glsl_pcf\\n\"\n\"!!cvardf r_glsl_rtenvsphere\\n\"\n\"!!samps =FAKESHADOWS shadowmap\\n\"\n\n// envmaps only\n\"!!samps =REFLECTCUBEMASK reflectmask reflectcube\\n\"\n\"!!cvardf r_skipDiffuse\\n\"\n\n\"#include \\\"sys/defs.h\\\"\\n\"\n\n\"varying vec2 tex_c;\\n\"\n\"varying vec3 norm;\\n\"\n\"varying vec4 light;\\n\"\n\n/* CUBEMAPS ONLY */\n\"#ifdef REFLECTCUBEMASK\\n\"\n\"varying vec3 eyevector;\\n\"\n\"varying mat3 invsurface;\\n\"\n\n\"#endif\\n\"\n\n\"#ifndef ENVTINT\\n\"\n\"#define ENVTINT 1.0,1.0,1.0\\n\"\n\"#endif\\n\"\n\n\"#ifndef ENVSAT\\n\"\n\"#define ENVSAT 1.0\\n\"\n\"#endif\\n\"\n\n\n\"#ifdef FAKESHADOWS\\n\"\n\"varying vec4 vtexprojcoord;\\n\"\n\"#endif\\n\"\n\n\"#ifdef VERTEX_SHADER\\n\"\n\"#include \\\"sys/skeletal.h\\\"\\n\"\n\n\"float lambert(vec3 normal, vec3 dir)\\n\"\n\"{\\n\"\n\"return dot(normal, dir);\\n\"\n\"}\\n\"\n\n\"float halflambert(vec3 normal, vec3 dir)\\n\"\n\"{\\n\"\n\"return (dot(normal, dir) * 0.5) + 0.5;\\n\"\n\"}\\n\"\n\n\"void main (void)\\n\"\n\"{\\n\"\n\"vec3 n, s, t, w;\\n\"\n\"tex_c = v_texcoord;\\n\"\n\"gl_Position = skeletaltransform_wnst(w,n,s,t);\\n\"\n\"norm = n = normalize(n);\\n\"\n\"s = normalize(s);\\n\"\n\"t = normalize(t);\\n\"\n\"light.rgba = vec4(e_light_ambient, 1.0);\\n\"\n\n\"#ifdef AMBIENTCUBE\\n\"\n//no specular effect here. use rtlights for that.\n\"vec3 nn = norm*norm; //FIXME: should be worldspace normal.\\n\"\n\"light.rgb = nn.x * e_light_ambientcube[(norm.x<0.0)?1:0] +\\n\"\n\"nn.y * e_light_ambientcube[(norm.y<0.0)?3:2] +\\n\"\n\"nn.z * e_light_ambientcube[(norm.z<0.0)?5:4];\\n\"\n\"#else\\n\"\n\"#ifdef HALFLAMBERT\\n\"\n\"light.rgb += max(0.0,halflambert(n,e_light_dir)) * e_light_mul;\\n\"\n\"#else\\n\"\n\"light.rgb += max(0.0,dot(n,e_light_dir)) * e_light_mul;\\n\"\n\"#endif\\n\"\n\"#endif\\n\"\n\n/* CUBEMAPS ONLY */\n\"#ifdef REFLECTCUBEMASK\\n\"\n\"invsurface = mat3(s, t, n);\\n\"\n\n\"vec3 eyeminusvertex = e_eyepos - w.xyz;\\n\"\n\"eyevector.x = dot(eyeminusvertex, s.xyz);\\n\"\n\"eyevector.y = dot(eyeminusvertex, t.xyz);\\n\"\n\"eyevector.z = dot(eyeminusvertex, n.xyz);\\n\"\n\"#endif\\n\"\n\n\"#ifdef FAKESHADOWS\\n\"\n\"vtexprojcoord = (l_cubematrix*vec4(w.xyz, 1.0));\\n\"\n\"#endif\\n\"\n\"}\\n\"\n\"#endif\\n\"\n\n\n\"#ifdef FRAGMENT_SHADER\\n\"\n\"#include \\\"sys/fog.h\\\"\\n\"\n\"#include \\\"sys/pcf.h\\\"\\n\"\n\n\"vec3 env_saturation(vec3 rgb, float adjustment) {\\n\"\n\"vec3 intensity = vec3(dot(rgb, vec3(0.2126,0.7152,0.0722)));\\n\"\n\"return mix(intensity, rgb, adjustment);\\n\"\n\"}\\n\"\n\n\"void main (void)\\n\"\n\"{\\n\"\n\"vec4 diffuse_f = texture2D(s_diffuse, tex_c);\\n\"\n\n\"#ifdef MASKLT\\n\"\n\"if (diffuse_f.a < float(MASK))\\n\"\n\"discard;\\n\"\n\"#endif\\n\"\n\n/* Normal/Bumpmap Shenanigans */\n\"#ifdef BUMP\\n\"\n/* Source's normalmaps are in the DX format where the green channel is flipped */\n\"vec3 normal_f = texture2D(s_normalmap, tex_c).rgb;\\n\"\n\"normal_f.g = 1.0 - normal_f.g;\\n\"\n\"normal_f = normalize(normal_f.rgb - 0.5);\\n\"\n\"#else\\n\"\n\"vec3 normal_f = vec3(0.0,0.0,1.0);\\n\"\n\"#endif\\n\"\n\n/* CUBEMAPS ONLY */\n\"#ifdef REFLECTCUBEMASK\\n\"\n\n\"#if defined(ENVFROMMASK)\\n\"\n/* We have a dedicated reflectmask */\n\"#define refl texture2D(s_reflectmask, tex_c).r\\n\"\n\"#else\\n\"\n/* when ENVFROMBASE is set or a normal isn't present, we're getting the reflectivity info from the diffusemap's alpha channel */\n\"#if defined(ENVFROMBASE) || !defined(BUMP)\\n\"\n\"#define refl 1.0 - diffuse_f.a\\n\"\n\"#else\\n\"\n/* when ENVFROMNORM is set, we don't invert the refl */\n\"#if defined(ENVFROMNORM)\\n\"\n\"#define refl texture2D(s_normalmap, tex_c).a\\n\"\n\"#else\\n\"\n\"#define refl 1.0 - texture2D(s_normalmap, tex_c).a\\n\"\n\"#endif\\n\"\n\"#endif\\n\"\n\"#endif\\n\"\n\n\"vec3 cube_tint = vec3(ENVTINT);\\n\"\n\"vec3 cube_sat = vec3(ENVSAT);\\n\"\n\n\"#if r_glsl_rtenvsphere == 1\\n\"\n\"vec3 r = reflect(normalize(-eyevector), normal_f.rgb);\\n\"\n\"vec2 sphereCoord = 0.5 + r.xy * 0.5;\\n\"\n\"vec3 cube_t = env_saturation(texture2D(s_rtenvsphere, sphereCoord).rgb * 0.5, cube_sat.r);\\n\"\n\"#else\\n\"\n\"vec3 cube_c = reflect(-eyevector, normal_f.rgb);\\n\"\n\"cube_c = cube_c.x * invsurface[0] + cube_c.y * invsurface[1] + cube_c.z * invsurface[2];\\n\"\n\"cube_c = (m_model * vec4(cube_c.xyz, 0.0)).xyz;\\n\"\n\"vec3 cube_t = env_saturation(textureCube(s_reflectcube, cube_c).rgb, cube_sat.r);\\n\"\n\"#endif\\n\"\n\n\"cube_t.r *= cube_tint.r;\\n\"\n\"cube_t.g *= cube_tint.g;\\n\"\n\"cube_t.b *= cube_tint.b;\\n\"\n\n\"diffuse_f.rgb += (cube_t * vec3(refl,refl,refl));\\n\"\n\"#endif\\n\"\n\n\"diffuse_f.rgb *= light.rgb * e_colourident.rgb;\\n\"\n\n\"#ifdef FAKESHADOWS\\n\"\n\"diffuse_f.rgb *= ShadowmapFilter(s_shadowmap, vtexprojcoord);\\n\"\n\"#endif\\n\"\n\n\"#ifdef FULLBRIGHT\\n\"\n\"diffuse_f.rgb += texture2D(s_fullbright, tex_c).rgb * texture2D(s_fullbright, tex_c).a;\\n\"\n\"#endif\\n\"\n\n\n\"#if 1\\n\"\n\"gl_FragColor = diffuse_f;\\n\"\n\"#else\\n\"\n\"gl_FragColor = fog4(diffuse_f);\\n\"\n\"#endif\\n\"\n\"}\\n\"\n\"#endif\\n\"\n},\n#endif\n#ifdef GLQUAKE\n{QR_OPENGL, 110, \"vmt/water\",\n\"!!cvardf r_glsl_turbscale_reflect=1 //simpler scaler\\n\"\n\"!!cvardf r_glsl_turbscale_refract=1 //simpler scaler\\n\"\n\"!!permu REFLECTCUBEMASK\\n\"\n\"!!samps diffuse normalmap\\n\"\n\"!!samps refract=0 //always present\\n\"\n\"!!samps reflect=1\\n\"\n\"!!samps =REFLECTCUBEMASK reflectcube\\n\"\n\"!!permu FOG\\n\"\n\n\"#include \\\"sys/defs.h\\\"\\n\"\n\n//modifier: REFLECT\t\t(s_t2 is a reflection instead of diffusemap)\n//modifier: STRENGTH_REFL\t(distortion strength - 0.1 = fairly gentle, 0.2 = big waves)\n//modifier: STRENGTH_REFL\t(distortion strength - 0.1 = fairly gentle, 0.2 = big waves)\n//modifier: FRESNEL_EXP\t(5=water)\n//modifier: TXSCALE\t\t(wave size - 0.2)\n//modifier: RIPPLEMAP\t\t(s_t3 contains a ripplemap\n//modifier: TINT_REFR\t\t(some colour value)\n//modifier: TINT_REFL\t\t(some colour value)\n//modifier: ALPHA\t\t(mix in the normal water texture over the top)\n//modifier: USEMODS\t\t(use single-texture scrolling via tcmods - note, also forces the engine to actually use tcmod etc)\n\n//a few notes on DP compat:\n//'dpwater' makes numerous assumptions about DP internals\n//by default there is a single pass that uses the pass's normal tcmods\n//the fresnel has a user-supplied min+max rather than an exponent\n//both parts are tinted individually\n//if alpha is enabled, the regular water texture is blended over the top, again using the same crappy tcmods...\n\n//legacy crap\n\"#ifndef FRESNEL\\n\"\n\"#define FRESNEL 5.0\\n\"\n\"#endif\\n\"\n\"#ifndef TINT\\n\"\n\"#define TINT 0.7,0.8,0.7\\n\"\n\"#endif\\n\"\n\"#ifndef STRENGTH\\n\"\n\"#define STRENGTH 0.25\\n\"\n\"#endif\\n\"\n\"#ifndef TXSCALE\\n\"\n\"#define TXSCALE 1\\n\"\n\"#endif\\n\"\n\n//current values (referring to legacy defaults where needed)\n\"#ifndef FRESNEL_EXP\\n\"\n\"#define FRESNEL_EXP 5.0\\n\"\n\"#endif\\n\"\n\"#ifndef FRESNEL_MIN\\n\"\n\"#define FRESNEL_MIN 0.0\\n\"\n\"#endif\\n\"\n\"#ifndef FRESNEL_RANGE\\n\"\n\"#define FRESNEL_RANGE 1.0\\n\"\n\"#endif\\n\"\n\"#ifndef STRENGTH_REFL\\n\"\n\"#define STRENGTH_REFL STRENGTH\\n\"\n\"#endif\\n\"\n\"#ifndef STRENGTH_REFR\\n\"\n\"#define STRENGTH_REFR STRENGTH\\n\"\n\"#endif\\n\"\n\"#ifndef TXSCALE1\\n\"\n\"#define TXSCALE1 TXSCALE\\n\"\n\"#endif\\n\"\n\"#ifndef TXSCALE2\\n\"\n\"#define TXSCALE2 TXSCALE\\n\"\n\"#endif\\n\"\n\"#ifndef TINT_REFR\\n\"\n\"#define TINT_REFR TINT\\n\"\n\"#endif\\n\"\n\"#ifndef TINT_REFL\\n\"\n\"#define TINT_REFL 1.0,1.0,1.0\\n\"\n\"#endif\\n\"\n\"#ifndef FOGTINT\\n\"\n\"#define FOGTINT 0.2,0.3,0.2\\n\"\n\"#endif\\n\"\n\n\"varying vec2 tc;\\n\"\n\"varying vec4 tf;\\n\"\n\"varying vec3 norm;\\n\"\n\"varying vec3 eye;\\n\"\n\n\"#ifdef VERTEX_SHADER\\n\"\n\"void main (void)\\n\"\n\"{\\n\"\n\"tc = v_texcoord.st;\\n\"\n\"tf = ftetransform();\\n\"\n\"norm = v_normal;\\n\"\n\"eye = e_eyepos - v_position.xyz;\\n\"\n\"gl_Position = ftetransform();\\n\"\n\"}\\n\"\n\"#endif\\n\"\n\n\"#ifdef FRAGMENT_SHADER\\n\"\n\"#include \\\"sys/fog.h\\\"\\n\"\n\n\n\"void main (void)\\n\"\n\"{\\n\"\n\"vec2 stc; //screen tex coords\\n\"\n\"vec2 ntc; //normalmap/diffuse tex coords\\n\"\n\"vec3 n, refr, refl;\\n\"\n\"float fres;\\n\"\n\"float depth;\\n\"\n\"stc = (1.0 + (tf.xy / tf.w)) * 0.5;\\n\"\n//hack the texture coords slightly so that there are less obvious gaps\n\"stc.t -= 1.5*norm.z/1080.0;\\n\"\n\n\"#if 0//def USEMODS\\n\"\n\"ntc = tc;\\n\"\n\"n = texture2D(s_normalmap, ntc).xyz - 0.5;\\n\"\n\"#else\\n\"\n//apply q1-style warp, just for kicks\n\"ntc.s = tc.s + sin(tc.t+e_time)*0.125;\\n\"\n\"ntc.t = tc.t + sin(tc.s+e_time)*0.125;\\n\"\n\n//generate the two wave patterns from the normalmap\n\"n = (texture2D(s_normalmap, vec2(TXSCALE1)*tc + vec2(e_time*0.1, 0.0)).xyz);\\n\"\n\"n += (texture2D(s_normalmap, vec2(TXSCALE2)*tc - vec2(0, e_time*0.097)).xyz);\\n\"\n\"n -= 1.0 - 4.0/256.0;\\n\"\n\"#endif\\n\"\n\n\"#ifdef RIPPLEMAP\\n\"\n\"n += texture2D(s_ripplemap, stc).rgb*3.0;\\n\"\n\"#endif\\n\"\n\"n = normalize(n);\\n\"\n\n//the fresnel term decides how transparent the water should be\n\"fres = pow(1.0-abs(dot(n, normalize(eye))), float(FRESNEL_EXP)) * float(FRESNEL_RANGE) + float(FRESNEL_MIN);\\n\"\n\n\"#ifdef DEPTH\\n\"\n\"float far = #include \\\"cvar/gl_maxdist\\\";\\n\"\n\"float near = #include \\\"cvar/gl_mindist\\\";\\n\"\n//get depth value at the surface\n\"float sdepth = gl_FragCoord.z;\\n\"\n\"sdepth = (2.0*near) / (far + near - sdepth * (far - near));\\n\"\n\"sdepth = mix(near, far, sdepth);\\n\"\n\n//get depth value at the ground beyond the surface.\n\"float gdepth = texture2D(s_refractdepth, stc).x;\\n\"\n\"gdepth = (2.0*near) / (far + near - gdepth * (far - near));\\n\"\n\"if (gdepth >= 0.5)\\n\"\n\"{\\n\"\n\"gdepth = sdepth;\\n\"\n\"depth = 0.0;\\n\"\n\"}\\n\"\n\"else\\n\"\n\"{\\n\"\n\"gdepth = mix(near, far, gdepth);\\n\"\n\"depth = gdepth - sdepth;\\n\"\n\"}\\n\"\n\n//reduce the normals in shallow water (near walls, reduces the pain of linear sampling)\n\"if (depth < 100.0)\\n\"\n\"n *= depth/100.0;\\n\"\n\"#else\\n\"\n\"depth = 1.0;\\n\"\n\"#endif \\n\"\n\n\n//refraction image (and water fog, if possible)\n\"refr = texture2D(s_refract, stc + n.st*float(STRENGTH_REFR)*float(r_glsl_turbscale_refract)).rgb * vec3(TINT_REFR);\\n\"\n\"#ifdef DEPTH\\n\"\n\"refr = mix(refr, vec3(FOGTINT), min(depth/4096.0, 1.0));\\n\"\n\"#endif\\n\"\n\n\"#ifdef LQWATER\\n\"\n\"refl = textureCube(s_reflectcube, n).rgb;// * vec3(TINT_REFL);\\n\"\n\"#else\\n\"\n\"refl = texture2D(s_reflect, stc - n.st*float(STRENGTH_REFL)*float(r_glsl_turbscale_reflect)).rgb * vec3(TINT_REFL);\\n\"\n\"#endif\\n\"\n\n//interplate by fresnel\n\"refr = mix(refr, refl, fres);\\n\"\n\n\"#ifdef ALPHA\\n\"\n\"vec4 ts = texture2D(s_diffuse, ntc);\\n\"\n\"vec4 surf = fog4blend(vec4(ts.rgb, float(ALPHA)*ts.a));\\n\"\n\"refr = mix(refr, surf.rgb, surf.a);\\n\"\n\"#else\\n\"\n\"refr = fog3(refr); \\n\"\n\"#endif\\n\"\n\n//done\n\"gl_FragColor = vec4(refr, 1.0);\\n\"\n\"}\\n\"\n\"#endif\\n\"\n},\n#endif\n#ifdef GLQUAKE\n{QR_OPENGL, 110, \"vmt/rt\",\n\"!!ver 110\\n\"\n\"!!permu FOG\\n\"\n\"!!samps diffuse=0\\n\"\n\n\"#include \\\"sys/defs.h\\\"\\n\"\n\"#include \\\"sys/fog.h\\\"\\n\"\n\n\"varying vec2 tex_c;\\n\"\n\n\"#ifdef VERTEX_SHADER\\n\"\n\"void main ()\\n\"\n\"{\\n\"\n\"tex_c = v_texcoord;\\n\"\n\"gl_Position = ftetransform();\\n\"\n\"}\\n\"\n\"#endif\\n\"\n\n\"#ifdef FRAGMENT_SHADER\\n\"\n\"void main ()\\n\"\n\"{\\n\"\n\"vec4 diffuse_f = texture2D( s_diffuse, fract(tex_c) );\\n\"\n\"gl_FragColor = fog4( diffuse_f );\\n\"\n\"}\\n\"\n\"#endif\\n\"\n},\n#endif\n"
  },
  {
    "path": "plugins/hl2/mod_hl2.c",
    "content": "#include \"../plugin.h\"\n#include \"../engine/common/com_mesh.h\"\n\n\n/*\n   Half-Life 2 / Source models store much of their data in other files.\n   I'm only going to try loading simple static models, so we don't need much data from the .mdl itself.\n\n   FIXME: multiple meshes are still buggy.\n   FIXME: materials are not loaded properly.\n   FIXME: no lod stuff.\n*/\n\nstatic plugfsfuncs_t *filefuncs;\nstatic plugmodfuncs_t *modfuncs;\nstatic plugfsfuncs_t *fsfuncs;\n\n//Utility functions. silly plugins.\nfloat Length(const vec3_t v) {return sqrt(DotProduct(v,v));}\nfloat RadiusFromBounds (const vec3_t mins, const vec3_t maxs)\n{\n\tint\t\ti;\n\tvec3_t\tcorner;\n\n\tfor (i=0 ; i<3 ; i++)\n\t{\n\t\tcorner[i] = fabs(mins[i]) > fabs(maxs[i]) ? fabs(mins[i]) : fabs(maxs[i]);\n\t}\n\n\treturn Length (corner);\n}\n\n//blargh\ntypedef struct\n{\n\tunsigned int magic;\n\tunsigned int version;\n\tunsigned int revisionid;\n\tchar name[64];\n\tunsigned int filesize;\n\n\tvec3_t _80;\n\tvec3_t _92;\n\tvec3_t mins;\n\tvec3_t maxs;\n\tvec3_t _128;\n\tvec3_t _140;\n\tunsigned int _152;\n\tunsigned int num_bones;\n\tunsigned int ofs_bones;\t\t//hl2mdlbone_t\n\tunsigned int _164;\n\tunsigned int _168;\n\tunsigned int _172;\n\tunsigned int _176;\n\tunsigned int num_anims;\n\tunsigned int ofs_anims;\t\t//hl2mdlanim_t\n\tunsigned int _188;\n\tunsigned int _192;\n\tunsigned int _196;\n\tunsigned int _200;\n\tunsigned int tex_count;\n\tunsigned int tex_ofs;\t\t//hl2mdltexture_t\n\tunsigned int texpath_count;\n\tunsigned int texpath_ofs;\t//hl2mdltexturepath_t\n\tunsigned int texbind_count;\t//N slots per skin\n\tunsigned int skin_count;\n\tunsigned int texbind_offset;//provides skin|slot->texture mappings\n\tunsigned int body_count;\n\tunsigned int body_ofs;\t\t//hl2mdlbody_t\n\tunsigned int _240;\n\tunsigned int _244;\n\tunsigned int _248;\n\tunsigned int _252;\n\tunsigned int _256;\n\tunsigned int _260;\n\tunsigned int _264;\n\tunsigned int _268;\n\tunsigned int _272;\n\tunsigned int _276;\n\tunsigned int _280;\n\tunsigned int _284;\n\tunsigned int _288;\n\tunsigned int _292;\n\tunsigned int _296;\n\tunsigned int _300;\n\tunsigned int _304;\n\tunsigned int _308;\n\tunsigned int _312;\n\tunsigned int _316;\n\tunsigned int _320;\n\tunsigned int _324;\n\tunsigned int _328;\n\tunsigned int _332;\n\tunsigned int _336;\n\tunsigned int _340;\n\tunsigned int _344;\n\tunsigned int ofs_aniname;\t//348\n\tunsigned int num_aniblocks;\n\tunsigned int ofs_aniblocks;\n\t//other stuff?\n} hl2mdlheader_t;\ntypedef struct\n{\n\tunsigned int name_ofs;\n\tint parent;\n\tint junk[6];\n\tvec3_t pos;\n\tfloat quat[4];\n\tvec3_t rot;\n\tvec3_t posscale;\n\tvec3_t rotscale;\n\tfloat inverse[12];\n\tint junk3[18];\n} hl2mdlbone_t;\ntypedef struct\n{\n\tqbyte bone;\t\t//skip ones in their base pose.\n\tqbyte flags;\t//says what the data is\n\tshort next;\t\t//to walk the list when its variable sized...\n\tunsigned short data[1];\n} hl2mdlpose_t;\ntypedef struct\n{\n\tunsigned int _0;\n\tunsigned int name_ofs;\n\tfloat fps;\n\tunsigned int loop;\n\tunsigned int numframes;\n\tunsigned int _20;\n\tunsigned int _24;\n\tunsigned int _28;\n\tunsigned int _32;\n\tunsigned int _36;\n\tunsigned int _40;\n\tunsigned int _44;\n\tunsigned int _48;\n\tstruct poseofs_s{\n\t\tunsigned int externalblock;\n\t\tunsigned int ofs_pose;\t//hl2mdlpose_t\n\t} defpose;\n\tunsigned int _60;\n\tunsigned int _64;\n\tunsigned int _68;\n\tunsigned int _72;\n\tunsigned int _76;\n\tunsigned int ofs_posesections;\t//{block, ofs}\n\tunsigned int posespersection;\t//0 if its small enough to avoid the extra indirection.\n\tunsigned int _88;\n\tunsigned int _92;\n\tunsigned int _96;\n} hl2mdlanim_t;\ntypedef struct\n{\n\tunsigned int name_ofs;\n\tunsigned int surf_count;\n\tunsigned int base;\n\tunsigned int surf_ofs;\n} hl2mdlbody_t;\ntypedef struct\n{\n\tchar name[64];\n\tunsigned int type;\n\tunsigned int radius;\n\tunsigned int mesh_count;\n\tunsigned int mesh_ofs;\n\tunsigned int vertex_count;\n\tunsigned int _84;\n\tunsigned int _88;\n\tunsigned int _92;\n\tunsigned int _96;\n\tunsigned int _100;\n\tunsigned int _104;\n\tunsigned int _108;\n\tunsigned int _112;\n\tunsigned int _116;\n\tunsigned int _120;\n\tunsigned int _124;\n\tunsigned int _128;\n\tunsigned int _132;\n\tunsigned int _136;\n\tunsigned int _140;\n\tunsigned int _144;\n} hl2mdlsurf_t;\ntypedef struct\n{\n\tunsigned int mat_idx;\n\tunsigned int model_ofs;\n\tunsigned int vert_count;\n\tunsigned int vert_first;\n\tunsigned int _16;\n\tunsigned int _20;\n\tunsigned int _24;\n\tunsigned int _28;\n\tunsigned int _32;\n\tunsigned int _36;\n\tunsigned int _40;\n\tunsigned int _44;\n\tunsigned int _48;\n\tunsigned int _52;\n\tunsigned int _56;\n\tunsigned int _60;\n\tunsigned int _64;\n\tunsigned int _68;\n\tunsigned int _72;\n\tunsigned int _76;\n\tunsigned int _80;\n\tunsigned int _84;\n\tunsigned int _88;\n\tunsigned int _92;\n\tunsigned int _96;\n\tunsigned int _100;\n\tunsigned int _104;\n\tunsigned int _108;\n\tunsigned int _112;\n} hl2mdlmesh_t;\ntypedef struct\n{\n\tunsigned int nameofs;\n\tunsigned int _4;\n\tunsigned int _8;\n\tunsigned int _12;\n\tunsigned int _16;\n\tunsigned int _20;\n\tunsigned int _24;\n\tunsigned int _28;\n\tunsigned int _32;\n\tunsigned int _36;\n\tunsigned int _40;\n\tunsigned int _44;\n\tunsigned int _48;\n\tunsigned int _52;\n\tunsigned int _56;\n\tunsigned int _60;\n} hl2mdltexture_t;\ntypedef struct\n{\n\tunsigned int nameofs;\n} hl2mdltexturepath_t;\n#pragma pack(push,1)\t//urgh wtf is this bullshit\ntypedef struct\n{\n\tunsigned int numskins;\n\tunsigned int offsetskin;\n} hl2vtxskins_t;\ntypedef struct\n{\n\tunsigned short foo;\n\t//no padding\n\tunsigned int offsetskinname;\n} hl2vtxskin_t;\ntypedef struct\n{\n\tunsigned int version;\n\tunsigned int vertcachesize;\n\tunsigned short bonesperstrip;\n\tunsigned short bonespertri;\n\tunsigned int bonespervert;\n\tunsigned int revisionid;\n\tunsigned int lod_count;\n\tunsigned int texreplacements_offset;\n\tunsigned int body_count;\n\tunsigned int body_ofs;\n} hl2vtxheader_t;\ntypedef struct\n{\n\tunsigned int surf_count;\n\tunsigned int surf_ofs;\n} hl2vtxbody_t;\ntypedef struct\n{\n\tunsigned int lod_count;\n\tunsigned int lod_ofs;\n} hl2vtxsurf_t;\ntypedef struct\n{\n\tunsigned int mesh_count;\n\tunsigned int mesh_ofs;\n\tfloat dist;\n} hl2vtxlod_t;\ntypedef struct\n{\n\tunsigned int stripg_count;\n\tunsigned int stripg_ofs;\n\tunsigned char flags;\n\t//no padding (3 bytes)\n} hl2vtxmesh_t;\ntypedef struct\n{\n\tunsigned int vert_count;\n\tunsigned int vert_ofs;\n\tunsigned int idx_count;\n\tunsigned int idx_ofs;\n\tunsigned int strip_count;\n\tunsigned int strip_ofs;\n\tunsigned char flags;\n\t//no padding (3 bytes)\n} hl2vtxstripg_t;\ntypedef struct\n{\n\tunsigned int idx_num;\n\tunsigned int idx_ofs;\n\tunsigned int vert_count;\n\tunsigned int vert_ofs;\n\tunsigned short bone_count;\n\tunsigned char flags;\n\t//no padding (1 byte)\n\tunsigned int bonestate_count;\n\tunsigned int bonestate_ofs;\n} hl2vtxstrip_t;\ntypedef struct\n{\n\tqbyte bone[3];\n\tqbyte bone_count;\n\tunsigned short vert;\n\tqbyte boneID[3];\n\t//no padding\n} hl2vtxvert_t;\ntypedef struct\n{\n\tunsigned int magic;\n\tunsigned int version;\n\tunsigned int revisionid;\n\tunsigned int lod_count;\n\tunsigned int lodverts_count[8];\n\tunsigned int fixups_count;\n\tunsigned int fixups_offset;\n\tunsigned int verts_offset;\n\tunsigned int tangents_offset;\n} hl2vvdheader_t;\ntypedef struct\n{\n\tunsigned int lod;\n\tunsigned int sourcevert;\n\tunsigned int numverts;\n} hl2vvdfixup_t;\ntypedef struct\n{\n\tfloat weight[3];\n\tqbyte bone[3];\n\tqbyte numbones;\n\tvec3_t xyz;\n\tvec3_t norm;\n\tvec2_t st;\n} hl2vvdvert_t;\n#pragma pack(pop)\n/*seriously, how many structs do you need?*/\ntypedef struct\n{\n\tmodel_t *mod;\n\thl2mdlheader_t *header;\n\tvoid *ani;\n\n\tfloat *basepose;\n\tfloat *baserelpose;\n\tgaliasbone_t *bones;\n\n\tunsigned int num_animations;\n\tgaliasanimation_t *ofs_animations;\n\n\tunsigned numverts;\n\tvec2_t *ofs_st_array;\n\tvecV_t *ofs_skel_xyz;\n\tvec3_t *ofs_skel_norm;\n\tvec3_t *ofs_skel_svect;\n\tvec3_t *ofs_skel_tvect;\n\tbyte_vec4_t *ofs_skel_idx;\n\tvec4_t *ofs_skel_weight;\n\tstruct\n\t{\n\t\tunsigned int numfixups;\n\t\tindex_t *fixup;\n\t} lod[1];\t//must remain at 1 (instead of 8) until fixups are handled.\n} hl2parsecontext_t;\n\nstatic index_t *Mod_HL2_LoadIndexes(hl2parsecontext_t *ctx, unsigned int *idxcount, const hl2vtxmesh_t *vmesh, unsigned int lod, index_t firstindex, index_t bias)\n{\n\tsize_t numidx = 0, g;\n\tconst hl2vtxstripg_t *vg;\n\tindex_t *idx, *ret = NULL;\n\n\tvg = (const void*)((const qbyte*)vmesh+vmesh->stripg_ofs);\n\tfor (g = 0; g < vmesh->stripg_count; g++, vg++)\n\t{\n\t\tif (vg->idx_count%3)\n\t\t{\n\t\t\t*idxcount = 0;\n\t\t\treturn NULL;\n\t\t}\n\t\tnumidx += vg->idx_count;\n\t}\n\n\tret = idx = plugfuncs->GMalloc(&ctx->mod->memgroup, sizeof(*idx)*numidx);\n\n\tvg = (const void*)((const qbyte*)vmesh+vmesh->stripg_ofs);\n\tfor (g = 0; g < vmesh->stripg_count; g++, vg++)\n\t{\n\t\tconst unsigned short *in = (const void*)((const qbyte*)vg+vg->idx_ofs);\n\t\tconst unsigned short *e = in+vg->idx_count;\n\t\tconst hl2vtxvert_t *v = (const void*)((const qbyte*)vg+vg->vert_ofs);\n\t\tif (ctx->lod[lod].numfixups)\n\t\t{\n\t\t\tindex_t *fixup = ctx->lod[lod].fixup;\n\t\t\tfor(;;)\n\t\t\t{\n\t\t\t\tif (in == e)\n\t\t\t\t\tbreak;\n\t\t\t\t*idx++ = fixup[v[*in++].vert+firstindex] - bias;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tfor(;;)\n\t\t\t{\n\t\t\t\tif (in == e)\n\t\t\t\t\tbreak;\n\t\t\t\t*idx++ = v[*in++].vert+firstindex - bias;\n\t\t\t}\n\t\t}\n\t}\n\t*idxcount = idx-ret;\n\treturn ret;\n}\nstatic qboolean Mod_HL2_LoadVTX(hl2parsecontext_t *ctx, const void *buffer, size_t fsize)\n{\t//horribly overcomplicated way to express this stuff.\n\tconst hl2mdlheader_t *mdl = ctx->header;\n\tsize_t totalsurfs = 0, b, s, l, m, t, z;\n\tconst hl2vtxheader_t *header = buffer;\n\tconst hl2vtxbody_t *vbody;\n\tconst hl2vtxsurf_t *vsurf;\n\tconst hl2vtxlod_t *vlod;\n\tconst hl2vtxmesh_t *vmesh;\n//\tconst hl2vtxskins_t *vskins;\n//\tconst hl2vtxskin_t *vskin;\n\n\tconst hl2mdlbody_t *mbody = (const hl2mdlbody_t*)((const qbyte*)mdl + mdl->body_ofs);\n\tconst hl2mdltexture_t *mtex = (const hl2mdltexture_t*)((const qbyte*)mdl + mdl->tex_ofs);\n\tconst unsigned short *skinbind;\n\n\tgaliasinfo_t *surf=NULL;\n\tgaliasskin_t *skin;\n\tskinframe_t *skinframe;\n\tsize_t firstvert = 0;\n\n\tif (fsize < sizeof(*header) || header->version != 7 || header->revisionid != ctx->header->revisionid || header->body_count == 0)\n\t\treturn false;\n\n\tvbody = (const void*)((const qbyte*)header + header->body_ofs);\n\tfor (b = 0; b < header->body_count; b++, vbody++)\n\t{\n\t\tvsurf = (const void*)((const qbyte*)vbody + vbody->surf_ofs);\n\t\tfor (s = 0; s < vbody->surf_count; s++, vsurf++)\n\t\t{\n\t\t\tvlod = (const void*)((const qbyte*)vsurf + vsurf->lod_ofs);\n\t\t\tfor (l = 0; l < min(vsurf->lod_count, countof(ctx->lod)); l++, vlod++)\n\t\t\t\ttotalsurfs += vlod->mesh_count;\n\t\t}\n\t}\n\n\tif (!totalsurfs)\n\t\treturn false;\n\n\tctx->mod->meshinfo = surf = plugfuncs->GMalloc(&ctx->mod->memgroup, sizeof(*surf)*totalsurfs);\n\n\tt = mdl->skin_count*mdl->texbind_count;\n\tskinbind = (const unsigned short*)((const qbyte*)mdl+mdl->texbind_offset);\n\tskin = plugfuncs->GMalloc(&ctx->mod->memgroup, sizeof(*skin)*t + sizeof(*skinframe)*t);\n\tskinframe = (skinframe_t*)(skin+t);\n\tfor (s = 0; s < mdl->skin_count; s++)\n\tfor (t = 0; t < mdl->texbind_count; t++)\n\t{\n\t\tgaliasskin_t *ns = &skin[s + t*mdl->skin_count];\n\t\tQ_snprintfz(ns->name, sizeof(ns->name), \"skin%u %u\", (unsigned)s, (unsigned)t);\n\n\t\tm = *skinbind++;\n\t\tif (mdl->texpath_count)\n\t\t{\n\t\t\tfor (z = 0; z < mdl->texpath_count; z++) {\n\t\t\t\tchar fsTest[MAX_QPATH];\n\t\t\t\tconst hl2mdltexturepath_t *mpath = (const hl2mdltexturepath_t*)((const qbyte*)mdl + mdl->texpath_ofs + sizeof(hl2mdltexturepath_t) * z);\n\t\t\t\tQ_strlcpy(skinframe->shadername, (const char*)mdl+mpath->nameofs, sizeof(skinframe->shadername));\n\t\t\t\tQ_strlcat(skinframe->shadername, (const char*)&mtex[m]+mtex[m].nameofs, sizeof(skinframe->shadername));\n\t\t\t\tQ_strlcat(skinframe->shadername, \".vmt\", sizeof(skinframe->shadername));\n\t\t\t\tQ_snprintfz(fsTest, sizeof(fsTest), \"materials\\\\%s\", skinframe->shadername);\n\n\t\t\t\tif (fsfuncs->LocateFile(fsTest, FSLF_IFFOUND, NULL)) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tmodfuncs->StripExtension((const char*)ctx->mod->name, skinframe->shadername, sizeof(skinframe->shadername));\n\t\t\tQ_strlcat(skinframe->shadername, \"/\", sizeof(skinframe->shadername));\n\t\t\tQ_strlcat(skinframe->shadername, (const char*)&mtex[m]+mtex[m].nameofs, sizeof(skinframe->shadername));\n\t\t\tQ_strlcat(skinframe->shadername, \".vmt\", sizeof(skinframe->shadername));\n\t\t}\n\n\t\tns->numframes = 1;\t//no skingroups... not that kind anyway.\n\t\tns->skinspeed = 10;\n\t\tns->frame = skinframe;\n\t\tskinframe++;\n\t}\n\n\tvbody = (const void*)((const qbyte*)header + header->body_ofs);\n\tfor (b = 0; b < header->body_count; b++, vbody++, mbody++)\n\t{\n\t\tconst hl2mdlsurf_t *msurf = (const hl2mdlsurf_t*)((const qbyte*)mbody + mbody->surf_ofs);\n\t\tvsurf = (const void*)((const qbyte*)vbody + vbody->surf_ofs);\n\t\tfor (s = 0; s < vbody->surf_count; s++, vsurf++, msurf++)\n\t\t{\n\t\t\tvlod = (const void*)((const qbyte*)vsurf + vsurf->lod_ofs);\n//\t\t\tvskins = (const hl2vtxskins_t*)((const qbyte*)header + header->texreplacements_offset);\n\t\t\tfor (l = 0, t = 0; l < min(vsurf->lod_count, countof(ctx->lod)); l++, vlod++/*, vskins++*/)\n\t\t\t{\n\t\t\t\tconst hl2mdlmesh_t *mmesh = (const hl2mdlmesh_t*)((const qbyte*)msurf + msurf->mesh_ofs);\n\t\t\t\tvmesh = (const void*)((const qbyte*)vlod + vlod->mesh_ofs);\n\t\t\t\tfor (m = 0; m < vlod->mesh_count; m++, vmesh++, mmesh++)\n\t\t\t\t{\n\t\t\t\t\tQ_snprintfz(surf->surfacename, sizeof(surf->surfacename), \"%s:%s:l%u:m%u\", (const char*)mbody+mbody->name_ofs, msurf->name, (unsigned)l, (unsigned)m);\n\n\t\t\t\t\t/*animation info*/\n\t\t\t\t\tsurf->numanimations = ctx->num_animations;\n\t\t\t\t\tsurf->ofsanimations = ctx->ofs_animations;\t//we have no animation data\n\n\t\t\t\t\tsurf->baseframeofs = ctx->basepose;\n\t\t\t\t\tsurf->ofsbones = ctx->bones;\n\t\t\t\t\tsurf->numbones = mdl->num_bones;\n\t\t\t\t\tsurf->shares_bones = 0;\t//all the same id\n\n\t\t\t\t\t#ifndef SERVERONLY\n\t\t\t\t\t/*skin data*/\n\t\t\t\t\tsurf->numskins = mdl->skin_count;\n\t\t\t\t\tsurf->ofsskins = skin+mmesh->mat_idx*mdl->skin_count;\n\n\t\t\t\t\t/*vertdata*/\n\t\t\t\t\tsurf->ofs_rgbaf = NULL;\n\t\t\t\t\tsurf->ofs_rgbaub = NULL;\n\t\t\t\t\tsurf->ofs_st_array = ctx->ofs_st_array;\n\t\t\t\t\t#endif\n\t\t\t\t\tsurf->shares_verts = 0;\n\t\t\t\t\tsurf->numverts = ctx->numverts;\n\t\t\t\t\tsurf->ofs_skel_xyz = ctx->ofs_skel_xyz;\n\t\t\t\t\tsurf->ofs_skel_norm = ctx->ofs_skel_norm;\n\t\t\t\t\tsurf->ofs_skel_svect = ctx->ofs_skel_svect;\n\t\t\t\t\tsurf->ofs_skel_tvect = ctx->ofs_skel_tvect;\n\t\t\t\t\tsurf->ofs_skel_idx = ctx->ofs_skel_idx;\n\t\t\t\t\tsurf->ofs_skel_weight = ctx->ofs_skel_weight;\n\n\t\t\t\t\t/*index data*/\n\t\t\t\t\tif (mmesh->vert_first+mmesh->vert_count > surf->numverts)\n\t\t\t\t\t\tsurf->ofs_indexes = NULL, surf->numindexes = 0;\t//erk?\n\t\t\t\t\telse if (!ctx->lod[l].numfixups /*&& mdl->num_bones > sh_config.max_gpu_bones*/)\n\t\t\t\t\t{\t//FIXME: fixups make this too screwy, so we can't use this path when they're in use, which means we will probably exceed out gpu bones limit more than we otherwise would (just a perf issue)\n\t\t\t\t\t\tunsigned int bias = firstvert+mmesh->vert_first;\n\t\t\t\t\t\tsurf->numverts = mmesh->vert_count;\n\t\t\t\t\t\tsurf->ofs_st_array += bias;\n\t\t\t\t\t\tsurf->ofs_skel_xyz += bias;\n\t\t\t\t\t\tsurf->ofs_skel_norm += bias;\n\t\t\t\t\t\tsurf->ofs_skel_svect += bias;\n\t\t\t\t\t\tsurf->ofs_skel_tvect += bias;\n\t\t\t\t\t\tsurf->ofs_skel_idx += bias;\n\t\t\t\t\t\tsurf->ofs_skel_weight += bias;\n\t\t\t\t\t\tsurf->shares_verts = surf-(galiasinfo_t*)ctx->mod->meshinfo;\n\n\t\t\t\t\t\tsurf->ofs_indexes = Mod_HL2_LoadIndexes(ctx, &surf->numindexes, vmesh, l, bias, bias);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\tsurf->ofs_indexes = Mod_HL2_LoadIndexes(ctx, &surf->numindexes, vmesh, l, firstvert+mmesh->vert_first, 0);\n\n\t\t\t\t\t/*misc data*/\n\t\t\t\t\tsurf->geomset = 0;\n\t\t\t\t\tsurf->geomid = 0;\n\t\t\t\t\tsurf->contents = FTECONTENTS_BODY;\n\t\t\t\t\tsurf->csurface.flags = 0;\n\t\t\t\t\tsurf->surfaceid = b;\n\t\t\t\t\tsurf->mindist = 0;\t/*fixme: lods*/\n\t\t\t\t\tsurf->maxdist = 0;\n\n\t\t\t\t\tif (surf != ctx->mod->meshinfo)\n\t\t\t\t\t\tsurf[-1].nextsurf = surf;\n\t\t\t\t\tsurf->nextsurf = NULL;\n\t\t\t\t\tsurf++;\n\t\t\t\t}\n\t\t\t}\n\t\t\tfirstvert += msurf->vertex_count;\n\t\t}\n\t}\n\n\tif (surf == ctx->mod->meshinfo)\n\t\treturn false;\n\n\treturn true;\n}\nvoid CrossProduct (const vec3_t v1, const vec3_t v2, vec3_t cross)\n{\n\tcross[0] = v1[1]*v2[2] - v1[2]*v2[1];\n\tcross[1] = v1[2]*v2[0] - v1[0]*v2[2];\n\tcross[2] = v1[0]*v2[1] - v1[1]*v2[0];\n}\nstatic qboolean Mod_HL2_LoadVVD(hl2parsecontext_t *ctx, const void *buffer, size_t fsize)\n{\n\tconst hl2vvdheader_t *header = buffer;\n\tsize_t lod;\n\tconst hl2vvdvert_t *in;\n\tconst vec4_t *it;\n\tif (fsize < sizeof(*header) || header->magic != (('I'<<0)|('D'<<8)|('S'<<16)|('V'<<24)) || header->version != 4 || header->revisionid != ctx->header->revisionid || header->lodverts_count[0] == 0)\n\t\treturn false;\n\n\t{\n\t\tsize_t v, numverts = ctx->numverts = header->lodverts_count[0];\n\t\tvec2_t *st = ctx->ofs_st_array\t\t= plugfuncs->GMalloc(&ctx->mod->memgroup, sizeof(*st)*numverts);\n\t\tvecV_t *xyz = ctx->ofs_skel_xyz\t\t= plugfuncs->GMalloc(&ctx->mod->memgroup, sizeof(*xyz)*numverts);\n\t\tvec3_t *norm = ctx->ofs_skel_norm\t= plugfuncs->GMalloc(&ctx->mod->memgroup, sizeof(*norm)*numverts);\n\t\tvec3_t *sdir = ctx->ofs_skel_svect\t= plugfuncs->GMalloc(&ctx->mod->memgroup, sizeof(*sdir)*numverts);\n\t\tvec3_t *tdir = ctx->ofs_skel_tvect\t= plugfuncs->GMalloc(&ctx->mod->memgroup, sizeof(*tdir)*numverts);\n\t\tbyte_vec4_t *bone = ctx->ofs_skel_idx = plugfuncs->GMalloc(&ctx->mod->memgroup, sizeof(*bone)*numverts);\n\t\tvec4_t *weight = ctx->ofs_skel_weight = plugfuncs->GMalloc(&ctx->mod->memgroup, sizeof(*weight)*numverts);\n\n\t\tin = (const void*)((const char*)buffer+header->verts_offset);\n\t\tit = (const void*)((const char*)buffer+header->tangents_offset);\n\t\tfor(v = 0; v < numverts; v++, in++, it++)\n\t\t{\n\t\t\tVector2Copy(in->st, st[v]);\n\t\t\tVectorCopy(in->xyz, xyz[v]);\n\t\t\tVectorCopy(in->norm, norm[v]);\n\n\t\t\tVectorCopy(in->bone, bone[v]);\n\t\t\tbone[v][3] = bone[v][2];\t//make sure its valid, and in cache.\n\t\t\tVectorCopy(in->weight, weight[v]);\n\t\t\tweight[v][3] = 0;\t\t\t//missing influences cannot influence.\n\n\t\t\t//tangents are compacted, and for some reason in a different part of the file.\n\t\t\tVectorCopy((*it), sdir[v]);\n\t\t\tCrossProduct(in->norm, (*it), tdir[v]);\n\t\t\tVectorScale(tdir[v], (*it)[3], tdir[v]);\n\t\t}\n\t}\n\n\tif (header->fixups_count)\n\t{\n\t\tsize_t fixups = header->fixups_count, f, v;\n\t\tconst hl2vvdfixup_t *fixup = (const hl2vvdfixup_t*)((const qbyte*)header+header->fixups_offset);\n\t\tfor (lod = 0; lod < countof(ctx->lod) && lod < header->lod_count; lod++)\n\t\t{\n\t\t\tsize_t numverts;\n\t\t\tfor (numverts=0, f = 0; f < fixups; f++)\n\t\t\t{\n\t\t\t\tif (fixup[f].lod >= lod)\n\t\t\t\t\tnumverts += fixup[f].numverts;\n\t\t\t}\n\t\t\tif (numverts != header->lodverts_count[lod])\n\t\t\t\tcontinue;\n\t\t\tctx->lod[lod].numfixups = numverts;\n\t\t\tctx->lod[lod].fixup = plugfuncs->GMalloc(&ctx->mod->memgroup, sizeof(index_t)*numverts);\n\t\t\tfor (numverts=0, f = 0; f < fixups; f++)\n\t\t\t{\n\t\t\t\tif (fixup[f].lod >= lod)\n\t\t\t\t\tfor (v = 0; v < fixup[f].numverts; v++)\n\t\t\t\t\t\tctx->lod[lod].fixup[numverts++] = fixup[f].sourcevert+v;\n\t\t\t}\n\t\t}\n\t}\n\treturn true;\n}\n\nstatic void Angle2Quaternion(const vec3_t angles, vec4_t quaternion)\n{\n\tfloat\tyaw = angles[2] * 0.5;\n\tfloat\tpitch = angles[1] * 0.5;\n\tfloat\troll = angles[0] * 0.5;\n\tfloat\tsiny = sin(yaw);\n\tfloat\tcosy = cos(yaw);\n\tfloat\tsinp = sin(pitch);\n\tfloat\tcosp = cos(pitch);\n\tfloat\tsinr = sin(roll);\n\tfloat\tcosr = cos(roll);\n\n\tquaternion[0] = sinr * cosp * cosy - cosr * sinp * siny;\n\tquaternion[1] = cosr * sinp * cosy + sinr * cosp * siny;\n\tquaternion[2] = cosr * cosp * siny - sinr * sinp * cosy;\n\tquaternion[3] = cosr * cosp * cosy + sinr * sinp * siny;\n}\nstatic signed short Mod_HL2_ReadAnimValue(const void *baseptr, signed short offset, int posenum)\n{\n\tconst hlmdl_animvalue_t\t*animvalue = (const hlmdl_animvalue_t *) ((const qbyte *) baseptr + offset);\n\tif (!offset)\n\t\treturn 0;\t//nope, axis not present.\n\t/* find values including the required frame */\n\twhile(animvalue->num.total <= posenum)\n\t{\n\t\tposenum -= animvalue->num.total;\n\t\tanimvalue += animvalue->num.valid + 1;\n\t}\n\tif (posenum >= animvalue->num.valid)\n\t\tposenum = animvalue->num.valid;\n\telse\n\t\tposenum += 1;\n\treturn animvalue[posenum].value;\n}\nstatic const void *Mod_HL2_GetExternalBlock(hl2parsecontext_t *ctx, const void *base, int blockidx, int offset)\n{\n\tif (!blockidx)\n\t{\t//data is relative to the parent structure...\n\t\treturn (const qbyte*)base + offset;\n\t}\n\telse\n\t{\t//data is elsewhere...\n\t\tconst struct {\n\t\t\tint start;\n\t\t\tint end;\n\t\t} *block = (const void*)((const qbyte*)ctx->header + ctx->header->ofs_aniblocks);\n\t\tblock += blockidx;\n\n\t\tif (!ctx->ani)\n\t\t{\n\t\t\tvfsfile_t *f = filefuncs->OpenVFS((const char *)ctx->header + ctx->header->ofs_aniname, \"rb\", FS_GAME);\n\t\t\tif (f)\n\t\t\t{\n\t\t\t\tsize_t sz = f->GetLen(f);\n\t\t\t\tctx->ani = plugfuncs->GMalloc(&ctx->mod->memgroup, sz);\n\t\t\t\tf->ReadBytes(f, ctx->ani, sz);\n\t\t\t\tf->Close(f);\n\t\t\t}\n\t\t}\n\t\tif (ctx->ani)\n\t\t\treturn (const qbyte*)ctx->ani + block->start + offset;\n\t}\n\treturn NULL;\n}\n\nstatic float HalfToFloat(unsigned short val)\n{\t//hl2 supported shitty low-ram consoles. yay for data compression?\n\tunion\n\t{\n\t\tfloat f;\n\t\tunsigned int u;\n\t} u;\n\tif (val&0x7c00)\n\t\tu.u = (((val&0x7c00)>>10)-15+127)<<23;\t//read exponent, rebias it, and reshift.\n\telse\n\t\tu.u = 0;\t//denormal (or 0).\n\tu.u |= ((val & 0x3ff)<<13);//shift up the mantissa, but don't fold\n\tu.u |= (val&0x8000)<<16;\t//retain the sign bit.\n\treturn u.f;\n}\nstatic void Mod_HL2_ReadPose(hl2parsecontext_t *ctx, const hl2mdlanim_t *anim, int posenum, matrix3x4 *out, unsigned int numbones, const hl2mdlbone_t *boneinfo)\n{\t//hl2 models seem to have both animations and sequences\n\tvec3_t org;\n\tvec4_t quat;\n\tstatic vec3_t scale={1,1,1};\n\tconst unsigned short *data;\n\tconst hl2mdlpose_t *pose;\n\tstruct poseofs_s poseofs = anim->defpose;\n\tint sect = 0;\n\tif (anim->posespersection)\n\t{\t//can be stored in groups, so we don't have to walk as many rle entries.\n\t\tsect = posenum/anim->posespersection;\n\t\tposeofs = ((const struct poseofs_s*)((const qbyte*)anim + anim->ofs_posesections))[sect];\n\t\tposenum -= sect * anim->posespersection;\n\t}\n\n\tmemcpy(out, ctx->baserelpose, sizeof(*out)*numbones);\n\tpose = Mod_HL2_GetExternalBlock(ctx, anim, poseofs.externalblock, poseofs.ofs_pose);\n\tif (!pose || pose->bone==255)\n\t\treturn;\n\tfor (;;)\n\t{\n\t\tdata = pose->data;\n\n\t\tif (pose->flags & 0x2)\n\t\t{\t//static data shared between all frames in this cluster\n\t\t\tquat[0] = ((int)(data[0]       )-0x8000) / (float)0x8000;\n\t\t\tquat[1] = ((int)(data[1]       )-0x8000) / (float)0x8000;\n\t\t\tquat[2] = ((int)(data[2]&0x7fff)-0x4000) / (float)0x4000;\n\t\t\tquat[3] = 1 - DotProduct(quat, quat);\n\t\t\tquat[3] = sqrt(quat[3]);\n\t\t\tif (data[2]&0x8000)\n\t\t\t\tquat[3] *= -1;\n\t\t\tdata+=3;\n\t\t}\n\t\telse if (pose->flags & 0x20)\n\t\t{\t//apparently these are 21+21+21+1 bits each\n\t\t\tquint64_t q64 = *(const quint64_t*)data;\n\t\t\tquat[0] = (((int)(q64>> 0)&((1<<21)-1))-(1<<20)) / (float)(1<<20);\n\t\t\tquat[1] = (((int)(q64>>21)&((1<<21)-1))-(1<<20)) / (float)(1<<20);\n\t\t\tquat[2] = (((int)(q64>>42)&((1<<21)-1))-(1<<20)) / (float)(1<<20);\n\t\t\tquat[3] = 1 - DotProduct(quat, quat);\n\t\t\tquat[3] = sqrt(quat[3]);\n\t\t\tif (q64&(((quint64_t)1)<<63))\n\t\t\t\tquat[3] *= -1;\n\t\t\tdata+=sizeof(q64)/sizeof(*data);\n\t\t}\n\t\telse if (pose->flags & 0x8)\n\t\t{\t//animated version (using some sort of RLE)\n\t\t\tvec3_t ang;\n\t\t\tif (pose->flags & 0x10)\n\t\t\t\tVectorClear(ang);\n\t\t\telse\n\t\t\t\tVectorCopy(boneinfo[pose->bone].rot, ang);\n\t\t\tang[0] += Mod_HL2_ReadAnimValue(data, data[0], posenum) * boneinfo[pose->bone].rotscale[0];\n\t\t\tang[1] += Mod_HL2_ReadAnimValue(data, data[1], posenum) * boneinfo[pose->bone].rotscale[1];\n\t\t\tang[2] += Mod_HL2_ReadAnimValue(data, data[2], posenum) * boneinfo[pose->bone].rotscale[2];\n\t\t\tdata+=3;\n\t\t\tAngle2Quaternion(ang, quat);\n\t\t}\n\t\telse if (pose->flags & 0x10)\n\t\t\tVector4Set(quat, 0, 0, 0, 1);\n\t\telse\n\t\t\tVectorCopy(boneinfo[pose->bone].quat, quat);\n\n\t\tif (pose->flags & 1)\n\t\t{\t//static data\n\t\t\torg[0] = HalfToFloat(data[0]);\n\t\t\torg[1] = HalfToFloat(data[1]);\n\t\t\torg[2] = HalfToFloat(data[2]);\n\t\t\tdata+=3;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (pose->flags & 0x10)\n\t\t\t\tVectorClear(org);\n\t\t\telse\n\t\t\t\tVectorCopy(boneinfo[pose->bone].pos, org);\n\t\t\tif (pose->flags & 0x4)\n\t\t\t{\t//animated version (using some sort of RLE)\n\t\t\t\torg[0] += Mod_HL2_ReadAnimValue(data, data[0], posenum) * boneinfo[pose->bone].posscale[0];\n\t\t\t\torg[1] += Mod_HL2_ReadAnimValue(data, data[1], posenum) * boneinfo[pose->bone].posscale[1];\n\t\t\t\torg[2] += Mod_HL2_ReadAnimValue(data, data[2], posenum) * boneinfo[pose->bone].posscale[2];\n\t\t\t\tdata+=3;\n\t\t\t}\n\t\t}\n\n\t\t//FIXME: bone controllers\n\n\t\tmodfuncs->GenMatrixPosQuat4Scale(org, quat, scale, (float*)(out + pose->bone));\n\t\tif (!pose->next)\n\t\t\tbreak;\n\t\tpose = (const hl2mdlpose_t*)((const qbyte*)pose + pose->next);\n\t}\n}\n\n\n// matches engine Mod_InsertEvent from parses a foo.mdl.events file and inserts the events into the relevant animations\nstatic void Mod_InsertEvent(zonegroup_t *mem, galiasanimation_t *anims, unsigned int numanimations, unsigned int eventanimation, float eventpose, int eventcode, const char *eventdata)\n{\n\tgaliasevent_t *ev, **link;\n\tif (eventanimation >= numanimations)\n\t{\n\t\tCon_Printf(\"Mod_InsertEvent: invalid frame index\\n\");\n\t\treturn;\n\t}\n\tev = plugfuncs->GMalloc(mem, sizeof(*ev) + strlen(eventdata)+1);\n\tev->data = (char*)(ev+1);\n\n\tev->timestamp = eventpose;\n\tev->timestamp /= anims[eventanimation].rate;\n\tev->code = eventcode;\n\tstrcpy(ev->data, eventdata);\n\tlink = &anims[eventanimation].events;\n\twhile (*link && (*link)->timestamp <= ev->timestamp)\n\t\tlink = &(*link)->next;\n\tev->next = *link;\n\t*link = ev;\n}\n\n// matches engine Mod_ParseModelEvents\nstatic qboolean Mod_ParseModelEvents(model_t *mod, galiasanimation_t *anims, unsigned int numanimations)\n{\n\tunsigned int anim;\n\tfloat pose;\n\tint eventcode;\n\n\tconst char *modelname = mod->name;\n\tzonegroup_t *mem = &mod->memgroup;\n\tchar fname[MAX_QPATH], tok[2048];\n\tsize_t fsize;\n\tchar *line, *file, *eol;\n\tQ_snprintfz(fname, sizeof(fname), \"%s.events\", modelname);\n\tline = file = filefuncs->LoadFile(fname, &fsize);\n\tif (!file)\n\t\treturn false;\n\twhile(line && *line)\n\t{\n\t\teol = strchr(line, '\\n');\n\t\tif (eol)\n\t\t\t*eol = 0;\n\n\t\tline = cmdfuncs->ParseToken(line, tok, sizeof(tok), 0);\n\t\tanim = strtoul(tok, NULL, 0);\n\t\tline = cmdfuncs->ParseToken(line, tok, sizeof(tok), 0);\n\t\tpose = strtod(tok, NULL);\n\t\tline = cmdfuncs->ParseToken(line, tok, sizeof(tok), 0);\n\t\teventcode = (long)strtol(tok, NULL, 0);\n\t\tline = cmdfuncs->ParseToken(line, tok, sizeof(tok), 0);\n\t\tMod_InsertEvent(mem, anims, numanimations, anim, pose, eventcode, tok);\n\n\t\tif (eol)\n\t\t\tline = eol+1;\n\t\telse\n\t\t\tbreak;\n\t}\n\tplugfuncs->Free(file);\n\treturn true;\n}\n\n\nqboolean QDECL Mod_LoadHL2Model (model_t *mod, void *buffer, size_t fsize)\n{\n\thl2parsecontext_t ctx = {mod, buffer};\n\tvoid *vtx = NULL, *vvd = NULL;\n\tsize_t vtxsize = 0, vvdsize = 0;\n\tchar base[MAX_QPATH];\n\tqboolean result;\n\tsize_t i, p;\n\tconst char *vtxpostfixes[] = {\n\t\t\".dx90.vtx\",\n\t\t\".dx80.vtx\",\n\t\t\".vtx\",\n\t\t\".sw.vtx\"\n\t};\n\tconst char *vvdpostfixes[] = {\n\t\t\".vvd\"\n\t};\n\tgaliasinfo_t *galias;\n\n\tfor (i = 0; !vtx && i < countof(vtxpostfixes); i++)\n\t{\n\t\tmodfuncs->StripExtension(mod->name, base, sizeof(base));\n\t\tQ_strncatz(base, vtxpostfixes[i], sizeof(base));\n\t\tvtx = filefuncs->LoadFile(base, &vtxsize);\n\t}\n\tfor (i = 0; !vvd && i < countof(vvdpostfixes); i++)\n\t{\n\t\tmodfuncs->StripExtension(mod->name, base, sizeof(base));\n\t\tQ_strncatz(base, vvdpostfixes[i], sizeof(base));\n\t\tvvd = filefuncs->LoadFile(base, &vvdsize);\n\t}\n\n\tif (ctx.header->num_bones)\n\t{\n\t\tconst hl2mdlbone_t *in = (const hl2mdlbone_t*)((const qbyte*)ctx.header + ctx.header->ofs_bones);\n\t\tvec3_t scale = {1,1,1};\n\t\tctx.basepose = plugfuncs->GMalloc(&mod->memgroup, sizeof(float)*12*ctx.header->num_bones);\n\t\tctx.baserelpose = alloca(sizeof(float)*12*ctx.header->num_bones);\n\t\tctx.bones = plugfuncs->GMalloc(&mod->memgroup, sizeof(*ctx.bones)*ctx.header->num_bones);\n\n\t\tfor (i = 0; i < ctx.header->num_bones; i++)\n\t\t{\n\t\t\tfloat *pose = ctx.baserelpose + 12*i;\n\t\t\tQ_strlcpy(ctx.bones[i].name, (const char*)&in[i]+in[i].name_ofs, sizeof(ctx.bones[i].name));\n\t\t\tctx.bones[i].parent = in[i].parent;\n\t\t\tmodfuncs->GenMatrixPosQuat4Scale(in[i].pos, in[i].quat, scale, pose);\n\t\t\tif (in[i].parent>=0)\n\t\t\t\tmodfuncs->ConcatTransforms((const void*)(ctx.basepose+in[i].parent*12), (const void*)pose, (void*)(ctx.basepose+i*12));\n\t\t\telse\n\t\t\t\tmemcpy(ctx.basepose+12*i, pose, sizeof(float)*12);\n\t\t\tmemcpy(ctx.bones[i].inverse, in[i].inverse, sizeof(float)*12);\t//use the provided value, but should match modfuncs->M3x4_Invert(ctx.basepose+12*i, ctx.bones[i].inverse);\n\t\t}\n\n\t\tif (ctx.header->num_anims)\n\t\t{\n\t\t\tgaliasanimation_t *a;\n\t\t\tconst hl2mdlanim_t *in = (const hl2mdlanim_t*)((const qbyte*)ctx.header + ctx.header->ofs_anims);\n\t\t\ta = ctx.ofs_animations = plugfuncs->GMalloc(&mod->memgroup, sizeof(*ctx.ofs_animations)*ctx.header->num_anims);\n\t\t\t//note that hl2 models have anims and sequences... I'm guessing I ought to be exposing sequences instead of anims.\n\t\t\tfor (i = 0; i < ctx.header->num_anims; i++, in++)\n\t\t\t{\n\t\t\t\ta->skeltype = SKEL_RELATIVE;\n\t\t\t\ta->numposes = in->numframes;\n\n\t\t\t\ta->GetRawBones = NULL;\t//FIXME: for delay loading the proper way...\n\t\t\t\t\t\t\t\t\t\t//FIXME: replace Alias_FindRawSkelData instead, to handle bone controllers etc.\n\t\t\t\ta->boneofs = plugfuncs->GMalloc(&mod->memgroup, sizeof(float)*a->numposes*12*ctx.header->num_bones);\n\t\t\t\ta->loop = in->loop;\n\t\t\t\ta->rate = in->fps;\n\t\t\t\ta->action = -1;\n\t\t\t\ta->actionweight = 0;\n\t\t\t\ta->events = NULL;\n\t\t\t\tQ_strlcpy(a->name, (const char*)in + in->name_ofs, sizeof(a->name));\n\n\t\t\t\tfor (p = 0; p < a->numposes; p++)\n\t\t\t\t\tMod_HL2_ReadPose(&ctx, in, p, (matrix3x4*)a->boneofs+p*ctx.header->num_bones, ctx.header->num_bones, (const hl2mdlbone_t*)((const qbyte*)ctx.header + ctx.header->ofs_bones));\n\t\t\t\ta++;\n\t\t\t}\n\t\t\tctx.num_animations = a-ctx.ofs_animations;\n\t\t}\n\t}\n\n\n\tresult  = Mod_HL2_LoadVVD(&ctx, vvd, vvdsize);\n\tresult &= Mod_HL2_LoadVTX(&ctx, vtx, vtxsize);\n\tplugfuncs->Free(vvd);\n\tplugfuncs->Free(vtx);\n\n\tif (ctx.header->num_bones && !mod->meshinfo)\n\t{\t//it has a rig... make sure we spit out a surface so we can read the bones.\n\t\tgaliasinfo_t *surf = mod->meshinfo = plugfuncs->GMalloc(&mod->memgroup, sizeof(galiasinfo_t));\n\t\tsurf->numbones = ctx.header->num_bones;\n\t\tsurf->baseframeofs = ctx.basepose;\n\t\tsurf->ofsbones = ctx.bones;\n\n\t\tsurf->numanimations = ctx.num_animations;\n\t\tsurf->ofsanimations = ctx.ofs_animations;\n\t\tresult = true;\n\t}\n\n\tVectorCopy(ctx.header->mins, mod->mins);\n\tVectorCopy(ctx.header->maxs, mod->maxs);\n\tgalias = (galiasinfo_t*)mod->meshinfo;\n\tMod_ParseModelEvents(mod, galias->ofsanimations, galias->numanimations);\n\n\tmod->type = mod_alias;\n\tmod->radius = RadiusFromBounds(mod->mins, mod->maxs);\n\tmodfuncs->BIH_BuildAlias(mod, mod->meshinfo);\n\treturn result;\n}\n\nqboolean MDL_Init(void)\n{\n\tfilefuncs = plugfuncs->GetEngineInterface(plugfsfuncs_name, sizeof(*filefuncs));\n\tmodfuncs = plugfuncs->GetEngineInterface(plugmodfuncs_name, sizeof(*modfuncs));\n\tcmdfuncs = plugfuncs->GetEngineInterface(plugcmdfuncs_name, sizeof(*cmdfuncs));\n\tfsfuncs = plugfuncs->GetEngineInterface(plugfsfuncs_name, sizeof(*fsfuncs));\n\n\tif (modfuncs && modfuncs->version != MODPLUGFUNCS_VERSION)\n\t\tmodfuncs = NULL;\n\n\tif (modfuncs && filefuncs)\n\t{\n\t\tmodfuncs->RegisterModelFormatMagic(\"Source model (v44)\", \"IDST\\x2c\\0\\0\\0\",8, Mod_LoadHL2Model);\n\t\tmodfuncs->RegisterModelFormatMagic(\"Source model (v45)\", \"IDST\\x2d\\0\\0\\0\",8, Mod_LoadHL2Model);\n\t\tmodfuncs->RegisterModelFormatMagic(\"Source model (v46)\", \"IDST\\x2e\\0\\0\\0\",8, Mod_LoadHL2Model);\n\t\tmodfuncs->RegisterModelFormatMagic(\"Source model (v47)\", \"IDST\\x2f\\0\\0\\0\",8, Mod_LoadHL2Model);\n\t\tmodfuncs->RegisterModelFormatMagic(\"Source model (v48)\", \"IDST\\x30\\0\\0\\0\",8, Mod_LoadHL2Model);\n\t\tmodfuncs->RegisterModelFormatMagic(\"Source model (v49)\", \"IDST\\x31\\0\\0\\0\",8, Mod_LoadHL2Model);\n\t\treturn true;\n\t}\n\treturn false;\n}\n"
  },
  {
    "path": "plugins/hl2/mod_vbsp.c",
    "content": "/*\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n*/\n/*\n\tTODO:\n\t\tLightmaps - skip ssbump stuff properly. currently extra lightstyles are screwed.\n\t\tEnt Lighting - leafs have some list of light 'cubes' that define lighting, allowing for ents to get backlit etc properly.\n\t\tAreaportals - the server 'needs' a way to specify areaportals on a per-player basis. for now the gamecode will have to explicitly force-open most of the portals in the game (map_noareas can be used as a workaround).\n\t\tStatic Props - these are solid, but use the visible mesh for collisions instead of the special/simpler collision mesh. This makes it a bit easier to climb up them, which may be a gameplay issue.\n\t\tDetail Props - we don't attempt to handle these. They need manual batching or something (eg clutter-shader stuff). Their loss should not affect gameplay much as they can be disabled in HL2 too.\n\t\tDynamic Lighting - r_dynamic not enabled.\n\t\tRealtime Lighting - screwed. Doesn't light all world surfaces for some reason.\n\t\tRBE(Bullet) - probably screwed.\n\t\tSkyboxes - doesn't handle the whole per-face skies nor weird hdr encoding.\n\t\tFog - no fog here... this results in issues where the pvs provides distance culling.\n\t\tLoad Times - materials are loaded on a single thread. this gets slow.\n\t\tMaterials - all kinds of screwed.\n\t\tPortal2 Gels - we need to repurpose stainmap code or something.\n\t\tRefraction - this stuff is horribly expensive.\n*/\n\n#include \"../plugin.h\"\n#include \"quakedef.h\"\n#ifdef HAVE_CLIENT\n#include \"glquake.h\"\n#endif\n#include \"com_mesh.h\"\n#include \"com_bih.h\"\n\nstatic plugfsfuncs_t\t\t*filefuncs;\nstatic plugmodfuncs_t\t\t*modfuncs;\nstatic plugthreadfuncs_t\t*threadfuncs;\n\n#define Q_strncpyz Q_strlcpy\nfloat VectorNormalize2(const vec3_t in,vec3_t out) {float l = sqrt(DotProduct(in,in)); if (l) l = 1.0/l; VectorScale(in,l,out); return l;}\n#define VectorNormalize(v) VectorNormalize2(v,v)\nfte_inlinebody float M_LinearToSRGB(float x, float mag);\nvec3_t vec3_origin;\nstatic refdef_t *refdef;\n\nstatic vec3_t modelorg;\nstatic qbyte *frustumvis;\nstatic int vbsp_nodesequence; //to track which nodes need walking\nstatic int vbsp_surfsequence; //so we don't draw the same surface if its found multiple ways\n\nr_qrenderer_t qrenderer = QR_OPENGL;\n\n#define\tMAX_VBSP_AREAS\t\tMAX_Q2MAP_AREAS\n#define SURF_OFFNODE\t\tSURF_DRAWBACKGROUND\t//might as well just reuse that.\n\nstatic cvar_t *hl2_novis;\nstatic cvar_t *hl2_displacement_scale;\nstatic cvar_t *hl2_favour_ldr;\nstatic cvar_t *map_noareas;\nstatic cvar_t *map_autoopenportals;\nstatic cvar_t *hl2_contents_remap;\n\nchar *Q_strlwr(char *s)\n{\n\tchar *ret=s;\n\twhile(*s)\n\t{\n\t\tif (*s >= 'A' && *s <= 'Z')\n\t\t\t*s=*s-'A'+'a';\n\t\ts++;\n\t}\n\n\treturn ret;\n}\n\nvoid AddPointToBounds (const vec3_t v, vec3_t mins, vec3_t maxs)\n{\n\tint\t\ti;\n\tvec_t\tval;\n\n\tfor (i=0 ; i<3 ; i++)\n\t{\n\t\tval = v[i];\n\t\tif (val < mins[i])\n\t\t\tmins[i] = val;\n\t\tif (val > maxs[i])\n\t\t\tmaxs[i] = val;\n\t}\n}\n\nvoid ClearBounds (vec3_t mins, vec3_t maxs)\n{\n\tmins[0] = mins[1] = mins[2] = FLT_MAX;\n\tmaxs[0] = maxs[1] = maxs[2] = -FLT_MAX;\n}\n\n\n/*\n==================\nBoxOnPlaneSide\n\nReturns 1, 2, or 1 + 2\n==================\n*/\nint VARGS BoxOnPlaneSide (const vec3_t emins, const vec3_t emaxs, const mplane_t *p)\n{\n\tfloat\tdist1, dist2;\n\tint\t\tsides;\n\n// general case\n\tswitch (p->signbits)\n\t{\n\tdefault:\n\tcase 0:\ndist1 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2];\ndist2 = p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2];\n\t\tbreak;\n\tcase 1:\ndist1 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2];\ndist2 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2];\n\t\tbreak;\n\tcase 2:\ndist1 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2];\ndist2 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2];\n\t\tbreak;\n\tcase 3:\ndist1 = p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2];\ndist2 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2];\n\t\tbreak;\n\tcase 4:\ndist1 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2];\ndist2 = p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2];\n\t\tbreak;\n\tcase 5:\ndist1 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2];\ndist2 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2];\n\t\tbreak;\n\tcase 6:\ndist1 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2];\ndist2 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2];\n\t\tbreak;\n\tcase 7:\ndist1 = p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2];\ndist2 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2];\n\t\tbreak;\n\t}\n\n\tsides = 0;\n\tif (dist1 >= p->dist)\n\t\tsides = 1;\n\tif (dist2 < p->dist)\n\t\tsides |= 2;\n\n\treturn sides;\n}\n\n\n#define Host_Error Sys_Errorf\n\nenum hllumps_e\n{\t//note how this order matches q1 so well...\n\t//and yet the format is disturbingly similar to q2... huh... :P\n\tVLUMP_ENTITIES\t\t= LUMP_ENTITIES,\n\tVLUMP_PLANES\t\t= LUMP_PLANES,\n\tVLUMP_TEXTURES\t\t= LUMP_TEXTURES,\n\tVLUMP_VERTEXES\t\t= LUMP_VERTEXES,\n\tVLUMP_VISIBILITY\t= LUMP_VISIBILITY,\n\tVLUMP_NODES\t\t\t= LUMP_NODES,\n\tVLUMP_TEXINFO\t\t= LUMP_TEXINFO,\n\tVLUMP_FACES_LDR\t\t= LUMP_FACES,\n\tVLUMP_LIGHTING_LDR\t= LUMP_LIGHTING,\n//\tVLUMP_FOO\t\t\t= 9,//LUMP_CLIPNODES\n\tVLUMP_LEAFS\t\t\t= LUMP_LEAFS,\n//\tVLUMP_FOO\t\t\t= 11,//LUMP_MARKSURFACES\n\tVLUMP_EDGES\t\t\t= LUMP_EDGES,\n\tVLUMP_SURFEDGES\t\t= LUMP_SURFEDGES,\n\tVLUMP_MODELS\t\t= LUMP_MODELS,\n//\tVLUMP_FOO\t\t\t= 15,\n\tVLUMP_LEAFFACES\t\t= 16,\n\tVLUMP_LEAFBRUSHES\t= 17,\n\tVLUMP_BRUSHES\t\t= 18,\n\tVLUMP_BRUSHSIDES\t= 19,\n\tVLUMP_AREAS\t\t\t= 20,\n\tVLUMP_AREAPORTALS\t= 21,\n//\tVLUMP_FOO\t\t\t= 22,\n//\tVLUMP_FOO\t\t\t= 23,\n//\tVLUMP_FOO\t\t\t= 24,\n//\tVLUMP_FOO\t\t\t= 25,\n\tVLUMP_DISP_INFO\t\t= 26,\n//\tVLUMP_FOO\t\t\t= 27,\n//\tVLUMP_FOO\t\t\t= 28,\n//\tVLUMP_FOO\t\t\t= 29,\n//\tVLUMP_FOO\t\t\t= 30,\n//\tVLUMP_FOO\t\t\t= 31,\n\tVLUMP_DISP_LMALPHA\t= 32,\n\tVLUMP_DISP_VERTS\t= 33,\n\tVLUMP_DISP_LMCOORDS\t= 34,\n\tVLUMP_GAMELUMP\t\t= 35,\n//\tVLUMP_FOO\t\t\t= 36,\n//\tVLUMP_FOO\t\t\t= 37,\n//\tVLUMP_FOO\t\t\t= 38,\n//\tVLUMP_FOO\t\t\t= 39,\n\n\tVLUMP_ZIPFILE\t\t= 40,\n\tVLUMP_AREAPORTALVERTS = 41,\n//\tVLUMP_FOO\t\t\t= 42,\n\tVLUMP_STRINGDATA\t= 43,\n\tVLUMP_STRINGOFFSETS\t= 44,\n//\tVLUMP_FOO\t\t\t= 45,\n//\tVLUMP_FOO\t\t\t= 46,\n//\tVLUMP_FOO\t\t\t= 47,\n\tVLUMP_DISP_TRIFLAGS\t= 48,\n//\tVLUMP_FOO\t\t\t= 49,\n//\tVLUMP_FOO\t\t\t= 50,\n\tVLUMP_LEAFLIGHTI_HDR= 51,\t//indexes into VLUMP_LEAFLIGHTV_HDR, two shorts per leaf.\n\tVLUMP_LEAFLIGHTI_LDR= 52,\n\tVLUMP_LIGHTING_HDR\t= 53,\n//\tVLUMP_FOO\t\t\t= 54,\n\tVLUMP_LEAFLIGHTV_HDR= 55,\n\tVLUMP_LEAFLIGHTV_LDR= 56,\n//\tVLUMP_FOO\t\t\t= 57,\n\tVLUMP_FACES_HDR\t\t= 58,\n//\tVLUMP_FOO\t\t\t= 59,\n//\tVLUMP_FOO\t\t\t= 60,\n//\tVLUMP_FOO\t\t\t= 61,\n//\tVLUMP_FOO\t\t\t= 62,\n//\tVLUMP_FOO\t\t\t= 63,\n\tHL2_MAXLUMPS \t\t= 64\n};\ntypedef struct {\n\tunsigned int fileofs;\n\tunsigned int filelen;\n\tunsigned int version;\n\tunsigned int fourcc;\n} vlump_t;\ntypedef struct {\n\tunsigned int magic;\n\tunsigned int version;\n\tvlump_t lumps[HL2_MAXLUMPS];\n} dvbspheader_t;\n\ntypedef struct\n{\n\tint\t\tnumareaportals;\n\tint\t\tfirstareaportal;\n} carea_t;\ntypedef struct\n{\n\tint\t\tfloodnum;\t\t\t// if two areas have equal floodnums, they are connected\n\tint\t\tfloodvalid;\t\t\t// flags the area as having been visited (sequence numbers matching prv->floodvalid)\n} careaflood_t;\ntypedef struct cmodel_s\n{\n\tvec3_t\t\tmins, maxs;\n\tvec3_t\t\torigin;\t\t// for sounds or lights\n\tmnode_t\t\t*headnode;\n\tmleaf_t\t\t*headleaf;\n\tint\t\tnumsurfaces;\n\tint\t\tfirstsurface;\n\n\tint firstbrush;\n\tint num_brushes;\n} cmodel_t;\ntypedef struct dispinfo_s\n{\n    struct msurface_s *surf;\n    vec3_t aamin;\n    vec3_t aamax;\n    pvscache_t pvs; //which pvs clusters this displacement is visible in...\n    unsigned int contents;\n    unsigned int width;\n    unsigned int height;\n    vecV_t *xyz;    //(width+1)*(height+1)\n    index_t *idx;   //width*height*6;\n    size_t numindexes;\n    struct dispvert_s\n    {\n        vec3_t norm;\n        vec2_t st;\n        float alpha;\n    } *verts;\n    //unsigned short *flags;\n} dispinfo_t;\n\ntypedef struct vbspinfo_s\n{\n\tstruct\n\t{\n\t\tqbyte *vis;\n\t\tpvsbuffer_t visbuf;\n\t\tint viewcluster[2];\n\t} vcache;\n\n\tint\t\t\t\tnumbrushsides;\n\tq2cbrushside_t *brushsides;\n\n\tq2mapsurface_t\t*surfaces;\n\tdispinfo_t\t\t**surfdisp;\n\n\tint\t\t\t\tnumleafbrushes;\n\tq2cbrush_t\t\t**leafbrushes;\n\n\tint\t\t\t\tnumcmodels;\n\tcmodel_t\t\t*cmodels;\n\n\tint\t\t\t\tnumbrushes;\n\tq2cbrush_t\t\t*brushes;\n\n\tint\t\t\t\tnumvisibility;\n\tq2dvis_t\t\t*vis;\n\tqbyte\t\t\t*phscalced;\n\n\tstruct vbsptexinfo_s\n\t{\n\t\tvec4_t lmvecs[2];\n\t} *texinfo;\n\n\tint\t\t\t\tnumareas;\n\tint\t\t\t\tfloodvalid;\n\tcareaflood_t\tareaflood[MAX_VBSP_AREAS];\n\t//areas have a list of portals that open into other areas.\n\tcarea_t\t\t\t*areas;\t//indexes into q2areaportals for flooding\n\tsize_t\t\t\tnumareaportals;\n\tq2dareaportal_t\t*areaportals;\n\n\t//and this is the state that is actually changed. booleans.\n\tqbyte\t\t\tportalopen[MAX_Q2MAP_AREAPORTALS];\t//memset will work if it's a qbyte, really it should be a qboolean\n\tmplane_t\t\t**portalplane;\n\tqbyte\t\t\tportalquerying[MAX_Q2MAP_AREAPORTALS];\n\tmesh_t\t\t\t*portalpoly;\t\t//[numq2areaportals]\n\tint\t\t\t\t*occlusionqueries; //[numq2areaportals]\n\n\tstruct mleaflight_s\n\t{\n\t\tstruct leaflightpoint_s\n\t\t{\n\t\t\tvec3_t rgb[6];\n\t\t\tqbyte x, y, z;\n\t\t} *point;\n\t\tint count;\n\t} *leaflight;\n\n\tsize_t numdisplacements;\n\tdispinfo_t *displacements;\n\n\tsize_t numstaticprops;\n\tstruct staticprop_s\n\t{\n\t\tfloat fademindist;\n\t\tfloat fademaxdist;\n\t\tqboolean solid;\n\t\tstruct bihtransform_s transform;\n\t\tvec3_t\tlightorg;\n\t\tentity_t ent;\n\t} *staticprops;\n\n\tunsigned int contentsremap[32];\n} vbspinfo_t;\n\nstatic q2mapsurface_t\tnullsurface;\n\nstatic int\t\tVBSP_NumInlineModels (model_t *model);\nstatic cmodel_t\t*VBSP_InlineModel (model_t *model, char *name);\nstatic void VBSP_FinalizeBrush(q2cbrush_t *brush);\nstatic void\tFloodAreaConnections (vbspinfo_t\t*prv);\n\n/*\n===============================================================================\n\n\t\t\t\t\tMAP LOADING\n\n===============================================================================\n*/\n\nstatic unsigned int VBSP_TranslateContentBits(vbspinfo_t *prv, unsigned int source)\n{\n\tunsigned int ret = 0;\n\tif (source & 0x0000ffff)\n\t{\n\t\tif (source & 0x0000000f)\n\t\t{\n\t\t\tif (source & 0x00000001)\tret |= prv->contentsremap[ 0];\n\t\t\tif (source & 0x00000002)\tret |= prv->contentsremap[ 1];\n\t\t\tif (source & 0x00000004)\tret |= prv->contentsremap[ 2];\n\t\t\tif (source & 0x00000008)\tret |= prv->contentsremap[ 3];\n\t\t}\n\t\tif (source & 0x000000f0)\n\t\t{\n\t\t\tif (source & 0x00000010)\tret |= prv->contentsremap[ 4];\n\t\t\tif (source & 0x00000020)\tret |= prv->contentsremap[ 5];\n\t\t\tif (source & 0x00000040)\tret |= prv->contentsremap[ 6];\n\t\t\tif (source & 0x00000080)\tret |= prv->contentsremap[ 7];\n\t\t}\n\t\tif (source & 0x000000f00)\n\t\t{\n\t\t\tif (source & 0x00000100)\tret |= prv->contentsremap[ 8];\n\t\t\tif (source & 0x00000200)\tret |= prv->contentsremap[ 9];\n\t\t\tif (source & 0x00000400)\tret |= prv->contentsremap[10];\n\t\t\tif (source & 0x00000800)\tret |= prv->contentsremap[11];\n\t\t}\n\t\tif (source & 0x000000f00)\n\t\t{\n\t\t\tif (source & 0x000001000)\tret |= prv->contentsremap[12];\n\t\t\tif (source & 0x000002000)\tret |= prv->contentsremap[13];\n\t\t\tif (source & 0x000004000)\tret |= prv->contentsremap[14];\n\t\t\tif (source & 0x000008000)\tret |= prv->contentsremap[15];\n\t\t}\n\t}\n\tif (source & 0xffff0000)\n\t{\n\t\tif (source & 0x000f0000)\n\t\t{\n\t\t\tif (source & 0x00010000)\tret |= prv->contentsremap[16];\n\t\t\tif (source & 0x00020000)\tret |= prv->contentsremap[17];\n\t\t\tif (source & 0x00040000)\tret |= prv->contentsremap[18];\n\t\t\tif (source & 0x00080000)\tret |= prv->contentsremap[19];\n\t\t}\n\t\tif (source & 0x00f00000)\n\t\t{\n\t\t\tif (source & 0x00100000)\tret |= prv->contentsremap[20];\n\t\t\tif (source & 0x00200000)\tret |= prv->contentsremap[21];\n\t\t\tif (source & 0x00400000)\tret |= prv->contentsremap[22];\n\t\t\tif (source & 0x00800000)\tret |= prv->contentsremap[23];\n\t\t}\n\t\tif (source & 0x0f000000)\n\t\t{\n\t\t\tif (source & 0x01000000)\tret |= prv->contentsremap[24];\n\t\t\tif (source & 0x02000000)\tret |= prv->contentsremap[25];\n\t\t\tif (source & 0x04000000)\tret |= prv->contentsremap[26];\n\t\t\tif (source & 0x08000000)\tret |= prv->contentsremap[27];\n\t\t}\n\t\tif (source & 0xf0000000)\n\t\t{\n\t\t\tif (source & 0x10000000)\tret |= prv->contentsremap[28];\n\t\t\tif (source & 0x20000000)\tret |= prv->contentsremap[29];\n\t\t\tif (source & 0x40000000)\tret |= prv->contentsremap[30];\n\t\t\tif (source & 0x80000000)\tret |= prv->contentsremap[31];\n\t\t}\n\t}\n\treturn ret;\n}\nstatic void VBSP_TranslateContentBits_Setup(vbspinfo_t *prv)\n{\n\tsize_t i, j;\n\tchar contname[64];\n\tconst char *contremap = hl2_contents_remap->string;\n\tstatic const struct {\n\t\tconst char *name;\n\t\tunsigned int contents;\n\t} knowncontents[] =\n\t{\n\t\t{\"empty\", FTECONTENTS_EMPTY},\n\t\t{\"solid\", FTECONTENTS_SOLID},\n\t\t{\"window\", FTECONTENTS_WINDOW},\n\t\t{\"lava\", FTECONTENTS_LAVA},\n\t\t{\"slime\", FTECONTENTS_SLIME},\n\t\t{\"water\", FTECONTENTS_WATER},\n\t\t{\"ladder\", FTECONTENTS_LADDER},\n\t\t{\"playerclip\", FTECONTENTS_PLAYERCLIP},\n\t\t{\"monsterclip\", FTECONTENTS_MONSTERCLIP},\n\t\t{\"clip\", FTECONTENTS_PLAYERCLIP|FTECONTENTS_MONSTERCLIP},\n\t\t{\"body\", FTECONTENTS_BODY},\n\t\t{\"corpse\", FTECONTENTS_CORPSE},\n\t\t{\"detail\", FTECONTENTS_DETAIL},\n\t\t{\"sky\", FTECONTENTS_SKY},\n\t\t{\"Q2SOLID\", Q2CONTENTS_SOLID},\n\t\t{\"Q2WINDOW\", Q2CONTENTS_WINDOW},\n\t\t{\"Q2AUX\", Q2CONTENTS_AUX},\n\t\t{\"Q2LAVA\", Q2CONTENTS_LAVA},\n\t\t{\"Q2SLIME\", Q2CONTENTS_SLIME},\n\t\t{\"Q2WATER\", Q2CONTENTS_WATER},\n\t\t{\"Q2MIST\", Q2CONTENTS_MIST},\n\t\t{\"Q2AREAPORTAL\", Q2CONTENTS_AREAPORTAL},\n\t\t{\"Q2PLAYERCLIP\", Q2CONTENTS_PLAYERCLIP},\n\t\t{\"Q2MONSTERCLIP\", Q2CONTENTS_MONSTERCLIP},\n\t\t{\"Q2CURRENT_0\", Q2CONTENTS_CURRENT_0},\n\t\t{\"Q2CURRENT_90\", Q2CONTENTS_CURRENT_90},\n\t\t{\"Q2CURRENT_180\", Q2CONTENTS_CURRENT_180},\n\t\t{\"Q2CURRENT_270\", Q2CONTENTS_CURRENT_270},\n\t\t{\"Q2CURRENT_UP\", Q2CONTENTS_CURRENT_UP},\n\t\t{\"Q2CURRENT_DOWN\", Q2CONTENTS_CURRENT_DOWN},\n\t\t{\"Q2ORIGIN\", Q2CONTENTS_ORIGIN},\n\t\t{\"Q2MONSTER\", Q2CONTENTS_MONSTER},\n\t\t{\"Q2DEADMONSTER\", Q2CONTENTS_DEADMONSTER},\n\t\t{\"Q2DETAIL\", Q2CONTENTS_DETAIL},\n\t\t{\"Q2TRANSLUCENT\", Q2CONTENTS_TRANSLUCENT},\n\t\t{\"Q2LADDER\", Q2CONTENTS_LADDER},\n\t\t{\"Q3SOLID\", Q3CONTENTS_SOLID},\n\t\t{\"Q3LAVA\", Q3CONTENTS_LAVA},\n\t\t{\"Q3SLIME\", Q3CONTENTS_SLIME},\n\t\t{\"Q3WATER\", Q3CONTENTS_WATER},\n\t\t{\"Q3NOTTEAM1\", Q3CONTENTS_NOTTEAM1},\n\t\t{\"Q3NOTTEAM2\", Q3CONTENTS_NOTTEAM2},\n\t\t{\"Q3NOBOTCLIP\", Q3CONTENTS_NOBOTCLIP},\n\t\t{\"Q3AREAPORTAL\", Q3CONTENTS_AREAPORTAL},\n\t\t{\"Q3PLAYERCLIP\", Q3CONTENTS_PLAYERCLIP},\n\t\t{\"Q3MONSTERCLIP\", Q3CONTENTS_MONSTERCLIP},\n\t\t{\"Q3TELEPORTER\", Q3CONTENTS_TELEPORTER},\n\t\t{\"Q3JUMPPAD\", Q3CONTENTS_JUMPPAD},\n\t\t{\"Q3CLUSTERPORTAL\", Q3CONTENTS_CLUSTERPORTAL},\n\t\t{\"Q3DONOTENTER\", Q3CONTENTS_DONOTENTER},\n\t\t{\"Q3BOTCLIP\", Q3CONTENTS_BOTCLIP},\n\t\t{\"Q3MOVER\", Q3CONTENTS_MOVER},\n\t\t{\"Q3ORIGIN\", Q3CONTENTS_ORIGIN},\n\t\t{\"Q3BODY\", Q3CONTENTS_BODY},\n\t\t{\"Q3CORSE\", Q3CONTENTS_CORPSE},\n\t\t{\"Q3DETAIL\", Q3CONTENTS_DETAIL},\n\t\t{\"Q3STRUCTURAL\", Q3CONTENTS_STRUCTURAL},\n\t\t{\"Q3TRANSLUCENT\", Q3CONTENTS_TRANSLUCENT},\n\t\t{\"Q3TRIGGER\", Q3CONTENTS_TRIGGER},\n\t\t{\"Q3NODROP\", Q3CONTENTS_NODROP},\n\t};\n\tif (!*contremap)\n\t\tfor (i = 0; i < 32; i++)\n\t\t\tprv->contentsremap[i] = 1<<i;\n\telse for (i = 0; i < 32; i++)\n\t{\n\t\tcontremap = cmdfuncs->ParseToken(contremap, contname, sizeof(contname), NULL);\n\t\tif (!contremap || !*contname)\n\t\t\tprv->contentsremap[i] = 0;\n\t\telse\n\t\t{\n\t\t\tchar *tmp;\n\t\t\tint bit = strtol(contname, &tmp, 10);\n\t\t\tif (!*tmp)\n\t\t\t{\n\t\t\t\tif (bit >= 0)\n\t\t\t\t\tprv->contentsremap[i] = 1<<bit;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tfor (j = 0; j < countof(knowncontents); j++)\n\t\t\t\t{\n\t\t\t\t\tif (!Q_strcasecmp(contname, knowncontents[j].name))\n\t\t\t\t\t{\n\t\t\t\t\t\tprv->contentsremap[i] = knowncontents[j].contents;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (j == countof(knowncontents))\n\t\t\t\t\tCon_Printf(CON_WARNING\"%s: Unknown bit name %s\\n\", hl2_contents_remap->name, contname);\n\t\t\t}\n\t\t}\n\t}\n}\n\nstatic void VBSP_SetParent (mnode_t *node, mnode_t *parent)\n{\n\tnode->parent = parent;\n\tif (node->contents != -1)\n\t\treturn;\n\tVBSP_SetParent (node->children[0], node);\n\tVBSP_SetParent (node->children[1], node);\n}\n\nstatic void VBSP_FindBrushRange (vbspinfo_t\t*prv, mnode_t *node, size_t *firstbrush, size_t *lastbrush)\n{\n\tsize_t u, b;\n\tmleaf_t *leaf;\n\twhile (node->contents == -1)\n\t{\t//walk every node to find every leaf...\n\t\tVBSP_FindBrushRange(prv, node->children[0], firstbrush, lastbrush);\n\t\tnode = node->children[1];\n\t}\n\n\tleaf = (mleaf_t*)node;\n\tfor (u = 0; u < leaf->numleafbrushes; u++)\n\t{\n\t\tb = prv->leafbrushes[leaf->firstleafbrush+u]-prv->brushes;\n\t\tif (*firstbrush > b)\n\t\t\t*firstbrush = b;\n\t\tif (*lastbrush < b+1)\n\t\t\t*lastbrush = b+1;\n\t}\n}\n\nstatic qboolean VBSP_LoadVertexes (model_t *loadmodel, qbyte *mod_base, vlump_t *l)\n{\n\tdvertex_t\t*in;\n\tmvertex_t\t*out;\n\tsize_t\t\t\ti, count;\n\n\tin = (void *)(mod_base + l->fileofs);\n\tcount = l->filelen / sizeof(*in);\n\tif (l->filelen % sizeof(*in) || count > SANITY_LIMIT(*out))\n\t{\n\t\tCon_Printf (CON_ERROR \"VBSP_LoadVertexes: funny lump size in %s\\n\", loadmodel->name);\n\t\treturn false;\n\t}\n\tout = plugfuncs->GMalloc(&loadmodel->memgroup, count*sizeof(*out));\n\n\tloadmodel->vertexes = out;\n\tloadmodel->numvertexes = count;\n\n\tfor ( i=0 ; i<count ; i++, in++, out++)\n\t{\n\t\tout->position[0] = LittleFloat (in->point[0]);\n\t\tout->position[1] = LittleFloat (in->point[1]);\n\t\tout->position[2] = LittleFloat (in->point[2]);\n\t}\n\n\treturn true;\n}\nstatic qboolean VBSP_LoadEdges (model_t *loadmodel, qbyte *mod_base, vlump_t *l)\n{\n\tmedge_t *out;\n\tsize_t \ti, count;\n\n\tdsedge_t *in = (void *)(mod_base + l->fileofs);\n\tcount = l->filelen / sizeof(*in);\n\tif (l->filelen % sizeof(*in) || count > SANITY_LIMIT(*out))\n\t{\n\t\tCon_Printf (\"VBSP_LoadEdges: funny lump size in %s\\n\", loadmodel->name);\n\t\treturn false;\n\t}\n\tout = plugfuncs->GMalloc(&loadmodel->memgroup, (count + 1) * sizeof(*out));\n\n\tloadmodel->edges = out;\n\tloadmodel->numedges = count;\n\n\tfor ( i=0 ; i<count ; i++, in++, out++)\n\t{\n\t\tout->v[0] = (unsigned short)LittleShort(in->v[0]);\n\t\tout->v[1] = (unsigned short)LittleShort(in->v[1]);\n\t}\n\n\treturn true;\n}\nstatic qboolean VBSP_LoadSurfedges (model_t *loadmodel, qbyte *mod_base, vlump_t *l)\n{\n\tsize_t\t\ti, count;\n\tunsigned int\t\t*in, *out;\n\n\tin = (void *)(mod_base + l->fileofs);\n\tcount = l->filelen / sizeof(*in);\n\tif (l->filelen % sizeof(*in) || count > SANITY_LIMIT(*out))\n\t{\n\t\tCon_Printf (CON_ERROR \"VBSP_LoadSurfedges: funny lump size in %s\\n\",loadmodel->name);\n\t\treturn false;\n\t}\n\tout = plugfuncs->GMalloc(&loadmodel->memgroup, count*sizeof(*out));\n\n\tloadmodel->surfedges = out;\n\tloadmodel->numsurfedges = count;\n\n\tfor ( i=0 ; i<count ; i++)\n\t\tout[i] = LittleLong (in[i]);\n\n\treturn true;\n}\nstatic qboolean VBSP_LoadMarksurfaces (model_t *loadmodel, qbyte *mod_base, vlump_t *l)\n{\n\tsize_t\t\ti, j, count;\n\tmsurface_t **out;\n\n\tunsigned short\t\t*ins;\n\tins = (void *)(mod_base + l->fileofs);\n\tcount = l->filelen / sizeof(*ins);\n\tif (l->filelen % sizeof(*ins) || count > SANITY_LIMIT(*out))\n\t{\n\t\tCon_Printf (CON_ERROR \"VBSP_LoadMarksurfaces: funny lump size in %s\\n\",loadmodel->name);\n\t\treturn false;\n\t}\n\tout = plugfuncs->GMalloc(&loadmodel->memgroup, count*sizeof(*out));\n\n\tloadmodel->marksurfaces = out;\n\tloadmodel->nummarksurfaces = count;\n\n\tfor ( i=0 ; i<count ; i++)\n\t{\n\t\tj = (unsigned short)LittleShort(ins[i]);\n\t\tif (j >= loadmodel->numsurfaces)\n\t\t{\n\t\t\tCon_Printf (CON_ERROR \"VBSP_LoadMarksurfaces: bad surface number\\n\");\n\t\t\treturn false;\n\t\t}\n\t\tout[i] = loadmodel->surfaces + j;\n\t}\n\n\treturn true;\n}\nstatic qboolean VBSP_LoadEntities (model_t *loadmodel, qbyte *mod_base, vlump_t *l)\n{\n\treturn modfuncs->LoadEntities(loadmodel, mod_base+l->fileofs, l->filelen);\n}\n\n/*\n=================\nCMod_LoadSubmodels\n=================\n*/\nstatic qboolean VBSP_LoadSubmodels (model_t *loadmodel, qbyte *mod_base, vlump_t *l)\n{\n\tvbspinfo_t\t*prv = (vbspinfo_t*)loadmodel->meshinfo;\n\tq2dmodel_t\t*in;\n\tcmodel_t\t*out;\n\tint\t\t\ti, j, count;\n\tsize_t\t\tfirstbrush, lastbrush;\n\n\tin = (void *)(mod_base + l->fileofs);\n\tif (l->filelen % sizeof(*in))\n\t{\n\t\tCon_Printf (CON_ERROR \"VBSP_LoadSubmodels: funny lump size\\n\");\n\t\treturn false;\n\t}\n\tcount = l->filelen / sizeof(*in);\n\n\tif (count < 1)\n\t{\n\t\tCon_Printf (CON_ERROR \"Map with no models\\n\");\n\t\treturn false;\n\t}\n\tif (count > SANITY_MAX_Q2MAP_MODELS)\n\t{\n\t\tCon_Printf (CON_ERROR \"Map has too many models\\n\");\n\t\treturn false;\n\t}\n\n\tout = prv->cmodels = plugfuncs->GMalloc(&loadmodel->memgroup, count * sizeof(*prv->cmodels));\n\tprv->numcmodels = count;\n\n\tfor (i=0 ; i<count ; i++, in++, out++)\n\t{\n\t\tfor (j=0 ; j<3 ; j++)\n\t\t{\t// spread the mins / maxs by a pixel\n\t\t\tout->mins[j] = LittleFloat (in->mins[j]) - 1;\n\t\t\tout->maxs[j] = LittleFloat (in->maxs[j]) + 1;\n\t\t\tout->origin[j] = LittleFloat (in->origin[j]);\n\t\t}\n\t\tout->headnode = loadmodel->nodes + LittleLong (in->headnode);\n\t\tout->firstsurface = LittleLong (in->firstface);\n\t\tout->numsurfaces = LittleLong (in->numfaces);\n\n\n\t\tfirstbrush = ~0u;\n\t\tlastbrush = 0u;\n\t\tVBSP_FindBrushRange(prv, out->headnode, &firstbrush, &lastbrush);\n\t\tif (lastbrush > firstbrush)\n\t\t{\n\t\t\tout->firstbrush = firstbrush;\n\t\t\tout->num_brushes = lastbrush-firstbrush;\n\t\t}\n\t}\n\n\tAddPointToBounds(prv->cmodels[0].mins, loadmodel->mins, loadmodel->maxs);\n\tAddPointToBounds(prv->cmodels[0].maxs, loadmodel->mins, loadmodel->maxs);\n\n\treturn true;\n}\n\n/*\n=================\nCMod_LoadBrushes\n\n=================\n*/\nstatic qboolean VBSP_LoadBrushes (model_t *mod, qbyte *mod_base, vlump_t *l)\n{\n\tvbspinfo_t\t*prv = (vbspinfo_t*)mod->meshinfo;\n\tq2dbrush_t\t*in;\n\tq2cbrush_t\t*out;\n\tint\t\t\ti, count;\n\n\tin = (void *)(mod_base + l->fileofs);\n\tif (l->filelen % sizeof(*in))\n\t{\n\t\tCon_Printf (CON_ERROR \"VBSP_LoadBrushes: funny lump size\\n\");\n\t\treturn false;\n\t}\n\tcount = l->filelen / sizeof(*in);\n\n\tif (count > SANITY_LIMIT(*out))\n\t{\n\t\tCon_Printf (CON_ERROR \"Map has too many brushes\");\n\t\treturn false;\n\t}\n\n\tprv->brushes = plugfuncs->GMalloc(&mod->memgroup, sizeof(*out) * (count+1));\n\n\tout = prv->brushes;\n\n\tprv->numbrushes = count;\n\n\tfor (i=0 ; i<count ; i++, out++, in++)\n\t{\n\t\t//FIXME: missing bounds checks\n\t\tout->brushside = &prv->brushsides[LittleLong(in->firstside)];\n\t\tout->numsides = LittleLong(in->numsides);\n\t\tout->contents = VBSP_TranslateContentBits(prv, LittleLong(in->contents));\n\t\tVBSP_FinalizeBrush(out);\n\t}\n\n\treturn true;\n}\n\n/*\n=================\nCMod_LoadPlanes\n=================\n*/\nstatic qboolean VBSP_LoadPlanes (model_t *mod, qbyte *mod_base, vlump_t *l)\n{\n\tint\t\t\ti, j;\n\tmplane_t\t*out;\n\tdplane_t \t*in;\n\tint\t\t\tcount;\n\tint\t\t\tbits;\n\n\tin = (void *)(mod_base + l->fileofs);\n\tif (l->filelen % sizeof(*in))\n\t{\n\t\tCon_Printf (CON_ERROR \"VBSP_LoadPlanes: funny lump size\\n\");\n\t\treturn false;\n\t}\n\tcount = l->filelen / sizeof(*in);\n\n\tif (count < 1)\n\t{\n\t\tCon_Printf (CON_ERROR \"Map with no planes\\n\");\n\t\treturn false;\n\t}\n\t// need to save space for box planes\n\tif (count > SANITY_LIMIT(*out))\n\t{\n\t\tCon_Printf (CON_ERROR \"Map has too many planes (%i)\\n\", count);\n\t\treturn false;\n\t}\n\n\tmod->planes = out = plugfuncs->GMalloc(&mod->memgroup, sizeof(*out) * count);\n\tmod->numplanes = count;\n\n\tfor ( i=0 ; i<count ; i++, in++, out++)\n\t{\n\t\tbits = 0;\n\t\tfor (j=0 ; j<3 ; j++)\n\t\t{\n\t\t\tout->normal[j] = LittleFloat (in->normal[j]);\n\t\t\tif (out->normal[j] < 0)\n\t\t\t\tbits |= 1<<j;\n\t\t}\n\n\t\tout->dist = LittleFloat (in->dist);\n\t\tout->type = LittleLong (in->type);\n\t\tout->signbits = bits;\n\t}\n\n\treturn true;\n}\n\n/*\n=================\nCMod_LoadLeafBrushes\n=================\n*/\nstatic qboolean VBSP_LoadLeafBrushes (model_t *mod, qbyte *mod_base, vlump_t *l)\n{\n\tvbspinfo_t\t*prv = (vbspinfo_t*)mod->meshinfo;\n\tint\t\t\ti;\n\tq2cbrush_t\t**out;\n\tunsigned short \t*in;\n\tint\t\t\tcount;\n\n\tin = (void *)(mod_base + l->fileofs);\n\tif (l->filelen % sizeof(*in))\n\t{\n\t\tCon_Printf (CON_ERROR \"VBSP_LoadLeafBrushes: funny lump size\\n\");\n\t\treturn false;\n\t}\n\tcount = l->filelen / sizeof(*in);\n\n\tif (count < 1)\n\t{\n\t\tCon_Printf (CON_ERROR \"Map with no planes\\n\");\n\t\treturn false;\n\t}\n\t// need to save space for box planes\n\tif (count > SANITY_LIMIT(**out))\n\t{\n\t\tCon_Printf (CON_ERROR \"Map has too many leafbrushes\\n\");\n\t\treturn false;\n\t}\n\n\t//prv->numbrushes is because of submodels being weird.\n\tout = prv->leafbrushes = plugfuncs->GMalloc(&mod->memgroup, sizeof(*out) * (count+prv->numbrushes));\n\tprv->numleafbrushes = count;\n\n\tfor ( i=0 ; i<count ; i++, in++, out++)\n\t\t*out = prv->brushes + (unsigned short)(short)LittleShort (*in);\n\n\treturn true;\n}\n\n/*\n=================\nCMod_LoadAreas\n=================\n*/\nstatic qboolean VBSP_LoadAreas (model_t *mod, qbyte *mod_base, vlump_t *l)\n{\n\tvbspinfo_t\t*prv = (vbspinfo_t*)mod->meshinfo;\n\tint\t\t\ti;\n\tcarea_t\t*out;\n\tq2darea_t \t*in;\n\tint\t\t\tcount;\n\n\tin = (void *)(mod_base + l->fileofs);\n\tif (l->filelen % sizeof(*in))\n\t{\n\t\tCon_Printf (CON_ERROR \"VBSP_LoadAreas: funny lump size\\n\");\n\t\treturn false;\n\t}\n\tcount = l->filelen / sizeof(*in);\n\n\tif (count > MAX_Q2MAP_AREAS)\n\t{\n\t\tCon_Printf (CON_ERROR \"Map has too many areas\\n\");\n\t\treturn false;\n\t}\n\n\tout = prv->areas = plugfuncs->GMalloc(&mod->memgroup, sizeof(*out) * count);\n\tprv->numareas = count;\n\n\tfor ( i=0 ; i<count ; i++, in++, out++)\n\t{\n\t\tout->numareaportals = LittleLong (in->numareaportals);\n\t\tout->firstareaportal = LittleLong (in->firstareaportal);\n\t}\n\n\treturn true;\n}\n\n/*\n=================\nCMod_LoadVisibility\n=================\n*/\nstatic qboolean VBSP_LoadVisibility (model_t *mod, qbyte *mod_base, vlump_t *l)\n{\n\tvbspinfo_t\t*prv = (vbspinfo_t*)mod->meshinfo;\n\tint\t\ti;\n\n\tprv->numvisibility = l->filelen;\n//\tif (l->filelen > MAX_Q2MAP_VISIBILITY)\n//\t{\n//\t\tCon_Printf (CON_ERROR \"Map has too large visibility lump\\n\");\n//\t\treturn false;\n//\t}\n\n\tprv->vis = plugfuncs->GMalloc(&mod->memgroup, l->filelen);\n\tmemcpy (prv->vis, mod_base + l->fileofs, l->filelen);\n\n\tmod->vis = prv->vis;\n\n\tprv->vis->numclusters = LittleLong (prv->vis->numclusters);\n\tfor (i=0 ; i<prv->vis->numclusters ; i++)\n\t{\n\t\tprv->vis->bitofs[i][0] = LittleLong (prv->vis->bitofs[i][0]);\n\t\tprv->vis->bitofs[i][1] = LittleLong (prv->vis->bitofs[i][1]);\n\t}\n\tmod->numclusters = prv->vis->numclusters;\n\tmod->pvsbytes = ((mod->numclusters + 31)>>3)&~3;\n\n\treturn true;\n}\n\n/*\nstatic qbyte *CM_LeafnumPVS (model_t *model, int leafnum, qbyte *buffer, unsigned int buffersize)\n{\n\treturn CM_ClusterPVS(model, CM_LeafCluster(model, leafnum), buffer, buffersize);\n}\n*/\n\n#ifdef HAVE_CLIENT\n\n/*extern int\tr_dlightframecount;\nstatic void VBSP_MarkLights (dlight_t *light, dlightbitmask_t bit, mnode_t *node)\n{\n\tmplane_t\t*splitplane;\n\tfloat\t\tdist;\n\tmsurface_t\t*surf;\n\tint\t\t\ti;\n\n\tif (node->contents != -1)\n\t{\n\t\tmleaf_t *leaf = (mleaf_t *)node;\n\t\tmsurface_t **mark;\n\n\t\ti = leaf->nummarksurfaces;\n\t\tmark = leaf->firstmarksurface;\n\t\twhile(i--!=0)\n\t\t{\n\t\t\tsurf = *mark++;\n\t\t\tif (surf->dlightframe != r_dlightframecount)\n\t\t\t{\n\t\t\t\tsurf->dlightbits = 0;\n\t\t\t\tsurf->dlightframe = r_dlightframecount;\n\t\t\t}\n\t\t\tsurf->dlightbits |= bit;\n\t\t}\n\t\treturn;\n\t}\n\n\tsplitplane = node->plane;\n\tdist = DotProduct (light->origin, splitplane->normal) - splitplane->dist;\n\n\tif (dist > light->radius)\n\t{\n\t\tVBSP_MarkLights (light, bit, node->children[0]);\n\t\treturn;\n\t}\n\tif (dist < -light->radius)\n\t{\n\t\tVBSP_MarkLights (light, bit, node->children[1]);\n\t\treturn;\n\t}\n\n// mark the polygons\n\tsurf = cl.worldmodel->surfaces + node->firstsurface;\n\tfor (i=0 ; i<node->numsurfaces ; i++, surf++)\n\t{\n\t\tif (surf->dlightframe != r_dlightframecount)\n\t\t{\n\t\t\tsurf->dlightbits = 0u;\n\t\t\tsurf->dlightframe = r_dlightframecount;\n\t\t}\n\t\tsurf->dlightbits |= bit;\n\t}\n\n\tVBSP_MarkLights (light, bit, node->children[0]);\n\tVBSP_MarkLights (light, bit, node->children[1]);\n}\n\nstatic void VBSP_StainNode (mnode_t *node, float *parms)\n{\n\tmplane_t\t*splitplane;\n\tfloat\t\tdist;\n\tmsurface_t\t*surf;\n\tint\t\t\ti;\n\n\tif (node->contents != -1)\n\t\treturn;\n\n\tsplitplane = node->plane;\n\tdist = DotProduct ((parms+1), splitplane->normal) - splitplane->dist;\n\n\tif (dist > (*parms))\n\t{\n\t\tVBSP_StainNode (node->children[0], parms);\n\t\treturn;\n\t}\n\tif (dist < (-*parms))\n\t{\n\t\tVBSP_StainNode (node->children[1], parms);\n\t\treturn;\n\t}\n\n// mark the polygons\n\tsurf = cl.worldmodel->surfaces + node->firstsurface;\n\tfor (i=0 ; i<node->numsurfaces ; i++, surf++)\n\t{\n\t\tif (surf->flags&~(SURF_DONTWARP|SURF_PLANEBACK))\n\t\t\tcontinue;\n\t\tSurf_StainSurf(surf, parms);\n\t}\n\n\tVBSP_StainNode (node->children[0], parms);\n\tVBSP_StainNode (node->children[1], parms);\n}\n*/\n\n#endif\n\ntypedef struct\n{\n\tfloat\t\tvecs[2][4];\t\t// [s/t][xyz offset]\n\tfloat\t\tlmvecs[2][4];\t//well that's awkward\n\tint\t\t\tflags;\t\t\t// miptex flags + overrides\n\tint\t\t\ttextureindex;\n} hltexinfo_t;\nstatic qboolean VBSP_LoadSurfaces (model_t *mod, qbyte *mod_base, vlump_t *l)\n{\n\tvbspinfo_t\t*prv = (vbspinfo_t*)mod->meshinfo;\n\thltexinfo_t\t*in;\n\tq2mapsurface_t\t*out;\n\tint\t\t\ti, count;\n\n\tin = (void *)(mod_base + l->fileofs);\n\tif (l->filelen % sizeof(*in))\n\t{\n\t\tCon_Printf (CON_ERROR \"VBSP_LoadSurfaces: funny lump size\\n\");\n\t\treturn false;\n\t}\n\tcount = l->filelen / sizeof(*in);\n\tif (count < 1)\n\t{\n\t\tCon_Printf (CON_ERROR \"Map with no surfaces\\n\");\n\t\treturn false;\n\t}\n//\tif (count > MAX_Q2MAP_TEXINFO)\n//\t\tHost_Error (\"Map has too many surfaces\");\n\n\tmod->numtexinfo = count;\n\tout = prv->surfaces = plugfuncs->GMalloc(&mod->memgroup, count * sizeof(*prv->surfaces));\n\n\tfor ( i=0 ; i<count ; i++, in++, out++)\n\t{\n\t\tQ_strncpyz (out->c.name, \"FIXME\", sizeof(out->c.name));\n\t\tQ_strncpyz (out->rname, \"FIXME\", sizeof(out->rname));\n\t\tout->c.flags = LittleLong (in->flags);\n\t\tout->c.value = 0;\n\t}\n\n\treturn true;\n}\n\ntypedef struct\n{\n\tvec3_t reflectivity;\t\t//not very useful to us.\n\tunsigned int stringindex;\n\tunsigned int width;\n\tunsigned int height;\n\tunsigned int width2;\t//no idea why there's two of these.\n\tunsigned int height2;\n} hltexture_t;\n#define TIHL2_LIGHT\t\t\tTI_LIGHT\n#define TIHL2_SKYBOX\t\t0x2\n#define TIHL2_SKYROOM\t\t0x4\n#define TIHL2_WARP\t\t\tTI_WARP\n\n#define\tTIHL2_TRANS\t\t\tTI_TRANS33\n#define TIHL2_NOPORTAL\t\t0x20\n#define TIHL2_TRIGGER\t\t0x40\n#define TIHL2_NODRAW\t\tTI_NODRAW\n#define TIHL2_HINT\t\t0x100\n#define TIHL2_SKIP\t\t0x200\n#define TIHL2_NOLIGHT\t\t0x400\n//#define TIHL2_BUMPLIGHT\t0x800\n//#define TIHL2_NOSHADOWS\t0x1000\n//#define TIHL2_NODECALS\t0x2000\n//#define TIHL2_NOCHOP\t\t0x4000\n//#define TIHL2_HITBOX\t\t0x8000\n\nstatic qboolean VBSP_LoadTexInfo (model_t *mod, qbyte *mod_base, vlump_t *lumps, char *mapname)\n{\t//texinfo->textures->stringoffsets->strings. gah, so many lumps just to find the texture name to use!\n\tvbspinfo_t\t*prv = (vbspinfo_t*)mod->meshinfo;\n\thltexinfo_t *in;\n\tmtexinfo_t *out;\n\tint \ti, j, count;\n\tchar\tsname[256];\n\tint texcount;\n\thltexture_t *textures = (void*)(mod_base + lumps[VLUMP_TEXTURES].fileofs);\n\tunsigned int *stringoffsets = (void*)(mod_base + lumps[VLUMP_STRINGOFFSETS].fileofs);\n\tchar *strings = mod_base + lumps[VLUMP_STRINGDATA].fileofs;\n\tunsigned int flags;\n\n\tin = (void *)(mod_base + lumps[VLUMP_TEXINFO].fileofs);\n\tif (lumps[VLUMP_TEXINFO].filelen % sizeof(*in))\n\t{\n\t\tCon_Printf (\"VBSP_LoadTexInfo: funny lump size in %s\\n\", mod->name);\n\t\treturn false;\n\t}\n\tcount = lumps[VLUMP_TEXINFO].filelen / sizeof(*in);\n\tout = plugfuncs->GMalloc(&mod->memgroup, count*sizeof(*out));\n\tprv->texinfo = plugfuncs->GMalloc(&mod->memgroup, count*sizeof(*prv->texinfo));\n\n\tmod->textures = plugfuncs->GMalloc(&mod->memgroup, sizeof(texture_t *)*count);\n\ttexcount = 0;\n\n\tmod->texinfo = out;\n\tmod->numtexinfo = count;\n\n\tfor ( i=0 ; i<count ; i++, in++, out++)\n\t{\n\t\thltexture_t *texture = ((in->textureindex>=0)?textures + in->textureindex:NULL);\n\t\tchar *texturename = (texture?strings + stringoffsets[texture->stringindex]:\"INVALID\");\n\t\tflags = LittleLong (in->flags);\n\n\t\tfor (j=0 ; j<4 ; j++)\n\t\t\tout->vecs[0][j] = LittleFloat (in->vecs[0][j]);\n\t\tfor (j=0 ; j<4 ; j++)\n\t\t\tout->vecs[1][j] = LittleFloat (in->vecs[1][j]);\n\t\tout->vecscale[0] = 1.0/Length (out->vecs[0]);\n\t\tout->vecscale[1] = 1.0/Length (out->vecs[1]);\n\n\t\tfor (j=0 ; j<4 ; j++)\n\t\t\tprv->texinfo[i].lmvecs[0][j] = LittleFloat (in->lmvecs[0][j]);\n\t\tfor (j=0 ; j<4 ; j++)\n\t\t\tprv->texinfo[i].lmvecs[1][j] = LittleFloat (in->lmvecs[1][j]);\n\n\t\tif (flags & (TIHL2_SKYBOX|TIHL2_SKYROOM))\n\t\t\tQ_snprintfz(sname, sizeof(sname), \"sky/%s\", texturename);\n\t\telse\n\t\t\tQ_snprintfz(sname, sizeof(sname), \"%s\", texturename);\n\t\tif (flags & (TIHL2_WARP))\n\t\t\tQ_strncatz(sname, \"#WARP\", sizeof(sname));\n//\t\tif (out->flags & TIHL2_FLOWING)\n//\t\t\tQ_strncatz(sname, \"#FLOW\", sizeof(sname));\n//\t\tif (out->flags & TIHL2_TRANS66)\n//\t\t\tQ_strncatz(sname, \"#ALPHA=0.66\", sizeof(sname));\n\t\telse if (out->flags & TIHL2_TRANS)\n\t\t\tQ_strncatz(sname, \"#ALPHA=1\", sizeof(sname));\n//\t\telse if (out->flags & (TIHL2_WARP))\n//\t\t\tQ_strncatz(sname, \"#ALPHA=1\", sizeof(sname));\n\n\t\tif (flags & (TIHL2_SKYBOX|TIHL2_SKYROOM))\n\t\t\tout->flags |= TI_SKY;\n\n\t\tif (flags & (TIHL2_WARP))\n\t\t\tout->flags |= TI_WARP;\n\n\t\tif (flags & TIHL2_NOLIGHT)\n\t\t\tout->flags |= TEX_SPECIAL;\n\n\t\tif (flags & TIHL2_NODRAW)\n\t\t\tout->flags |= TI_NODRAW;\n\n// \t\tif (flags & TIHL2_HINT)\n// \t\t\tout->flags |= TI_HINT;\n\n// \t\tif (flags & TIHL2_SKIP)\n// \t\t\tout->flags |= TI_SKIP;\n\n\t\t//compact the textures.\n\t\tfor (j=0; j < texcount; j++)\n\t\t{\n\t\t\tif (!Q_strcasecmp(sname, mod->textures[j]->name))\n\t\t\t{\n\t\t\t\tout->texture = mod->textures[j];\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (j == texcount)\t//load a new one\n\t\t{\n\t\t\tout->texture = plugfuncs->GMalloc(&mod->memgroup, sizeof(texture_t));\n\t\t\tQ_strncpyz(out->texture->name, sname, sizeof(out->texture->name));\n\t\t\tQ_strlwr(out->texture->name);\n\t\t\tif (texture)\n\t\t\t{\n\t\t\t\tout->texture->vwidth = texture->width;\n\t\t\t\tout->texture->vheight = texture->height;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tout->texture->vwidth = out->texture->vheight = 128;\n\t\t\t}\n\n\t\t\tmod->textures[texcount++] = out->texture;\n\t\t}\n\t}\n\n\tmod->numtextures = texcount;\n\n\treturn true;\n}\n\ntypedef struct\n{\n\t//shared with q2dnode_t\n\tint\t\t\tplanenum;\n\tint\t\t\tchildren[2];\t// negative numbers are -(leafs+1), not nodes\n\tshort\t\tmins[3];\t\t// for frustom culling\n\tshort\t\tmaxs[3];\n\tunsigned short\tfirstface;\n\tunsigned short\tnumfaces;\t// counting both sides\n\n\t//new for hl2\n\tunsigned short\tarea;\n\tunsigned short pad;\n} hl2dnode_t;\nstatic qboolean VBSP_LoadNodes (model_t *mod, qbyte *mod_base, vlump_t *l)\n{\n\thl2dnode_t *in;\n\tint\t\t\tchild;\n\tmnode_t\t\t*out;\n\tint\t\t\ti, j, count;\n\n\tin = (void *)(mod_base + l->fileofs);\n\tif (l->filelen % sizeof(*in))\n\t{\n\t\tCon_Printf (CON_ERROR \"VBSP_LoadNodes: funny lump size\\n\");\n\t\treturn false;\n\t}\n\tcount = l->filelen / sizeof(*in);\n\n\tif (count < 1)\n\t{\n\t\tCon_Printf (CON_ERROR \"Map has no nodes\\n\");\n\t\treturn false;\n\t}\n\tif (count > SANITY_LIMIT(*out))\n\t{\n\t\tCon_Printf (CON_ERROR \"Map has too many nodes\\n\");\n\t\treturn false;\n\t}\n\n\tout = plugfuncs->GMalloc(&mod->memgroup, sizeof(mnode_t)*count);\n\n\tmod->nodes = out;\n\tmod->numnodes = count;\n\n\tfor (i=0 ; i<count ; i++, out++, in++)\n\t{\n\t\tmemset(out, 0, sizeof(*out));\n\n\t\tfor (j=0 ; j<3 ; j++)\n\t\t{\n\t\t\tout->minmaxs[j] = LittleShort (in->mins[j]);\n\t\t\tout->minmaxs[3+j] = LittleShort (in->maxs[j]);\n\t\t}\n\n\t\tout->plane = mod->planes + LittleLong(in->planenum);\n\n\t\tout->firstsurface = (unsigned short)LittleShort (in->firstface);\n\t\tout->numsurfaces = (unsigned short)LittleShort (in->numfaces);\n\t\tout->contents = -1;\t// differentiate from leafs\n\n\t\tfor (j=0 ; j<2 ; j++)\n\t\t{\n\t\t\tchild = LittleLong (in->children[j]);\n\t\t\tout->childnum[j] = child;\n\t\t\tif (child < 0)\n\t\t\t\tout->children[j] = (mnode_t *)(mod->leafs + -1-child);\n\t\t\telse\n\t\t\t\tout->children[j] = mod->nodes + child;\n\t\t}\n\t}\n\n\tVBSP_SetParent (mod->nodes, NULL);\t// sets nodes and leafs\n\n\treturn true;\n}\ntypedef struct\n{\n\t//copied from q2dleaf_t\n\tint\t\t\t\tcontents;\t\t\t// OR of all brushes (NOTE: hl2 doesn't seem to have these all set properly, at least for detail brushes)\n\n\tshort\t\t\tcluster;\n\tshort\t\t\tarea;\n\n\tshort\t\t\tmins[3];\t\t\t// for frustum culling\n\tshort\t\t\tmaxs[3];\n\n\tunsigned short\tfirstleafface;\n\tunsigned short\tnumleaffaces;\n\n\tunsigned short\tfirstleafbrush;\n\tunsigned short\tnumleafbrushes;\n\n\t//new for hl2\n\tshort leafwaterid;\n\tstruct\t//present in v19. gone in v20.\n\t{\n\t\tqbyte rgb[3];\n\t\tsigned char e;\n\t} light[6];\n\tshort pad;\n} hl2dleaf_t;\n\nstatic qboolean VBSP_LoadLeafs (model_t *mod, qbyte *mod_base, vlump_t *l, int ver)\n{\n\tvbspinfo_t\t*prv = (vbspinfo_t*)mod->meshinfo;\n\tint\t\t\ti, j;\n\tmleaf_t\t\t*out;\n\thl2dleaf_t\t*in;\n\tint\t\t\tcount;\n\tsize_t\t\tinsize = sizeof(*in);\n\tstruct leaflightpoint_s *lightpoint = NULL;\n\n\tif (ver == 19)\n\t\tinsize = 56;\t//older maps have some lighting info here.\n\telse\n\t\tinsize = 32;\n\n\tin = (void *)(mod_base + l->fileofs);\n\tif (l->filelen % insize)\n\t{\n\t\tCon_Printf (CON_ERROR \"VBSP_LoadLeafs: funny lump size\\n\");\n\t\treturn false;\n\t}\n\tcount = l->filelen / insize;\n\n\tif (count < 1)\n\t{\n\t\tCon_Printf (CON_ERROR \"Map with no leafs\\n\");\n\t\treturn false;\n\t}\n\t// need to save space for box planes\n\tif (count > SANITY_LIMIT(*out))\n\t{\n\t\tCon_Printf (CON_ERROR \"Map has too many leafs\\n\");\n\t\treturn false;\n\t}\n\n\tout = plugfuncs->GMalloc(&mod->memgroup, sizeof(*out) * (count+1));\n\tmod->numclusters = 0;\n\n\tmod->leafs = out;\n\tmod->numleafs = count;\n\n\tif (ver == 19)\n\t{\n\t\tprv->leaflight = plugfuncs->GMalloc(&mod->memgroup, sizeof(*out) * count);\n\t\tlightpoint = plugfuncs->GMalloc(&mod->memgroup, sizeof(*lightpoint) * count);\n\t}\n\n\tfor ( i=0 ; i<count ; i++, in = (hl2dleaf_t*)((qbyte*)in+insize), out++)\n\t{\n\t\tmemset(out, 0, sizeof(*out));\n\n\t\tfor (j=0 ; j<3 ; j++)\n\t\t{\n\t\t\tout->minmaxs[j] = LittleShort (in->mins[j]);\n\t\t\tout->minmaxs[3+j] = LittleShort (in->maxs[j]);\n\t\t}\n\n\t\tout->contents = VBSP_TranslateContentBits(prv,LittleLong (in->contents));\n\t\tout->cluster = (unsigned short)LittleShort (in->cluster);\n\t\tif (out->cluster == 0xffff)\n\t\t\tout->cluster = -1;\n\n\t\tout->area = (unsigned short)LittleShort (in->area);\n\t\tout->area &= 0x1ff;\t\t//upper part is flags.\n\t\tout->firstleafbrush = (unsigned short)LittleShort (in->firstleafbrush);\n\t\tout->numleafbrushes = (unsigned short)LittleShort (in->numleafbrushes);\n\n\t\tout->firstmarksurface = mod->marksurfaces +\n\t\t\t(unsigned short)LittleShort(in->firstleafface);\n\t\tout->nummarksurfaces = (unsigned short)LittleShort(in->numleaffaces);\n\n\t\tif (out->cluster >= mod->numclusters)\n\t\t\tmod->numclusters = out->cluster + 1;\n\n\t\tif (lightpoint)\n\t\t{\n\t\t\tfor (j = 0; j < 6; j++)\n\t\t\t{\n\t\t\t\tfloat e = pow(2, in->light[j].e);\n\t\t\t\tlightpoint->rgb[j][0] = e * in->light[j].rgb[0];\n\t\t\t\tlightpoint->rgb[j][1] = e * in->light[j].rgb[1];\n\t\t\t\tlightpoint->rgb[j][2] = e * in->light[j].rgb[2];\n\t\t\t}\n\t\t\tprv->leaflight[i].count = 1;\n\t\t\tprv->leaflight[i].point = lightpoint++;\n\t\t}\n\t}\n\tmod->pvsbytes = ((mod->numclusters + 31)>>3)&~3;\n\n\treturn true;\n}\n\ntypedef struct\n{\n\tunsigned short portalnum;\n\tunsigned short otherarea;\n\n\tunsigned short firstvert;\n\tunsigned short numverts;\n\tunsigned int planenum;\n} hl2dareaportal_t;\nstatic qboolean VBSP_LoadAreaPortals (model_t *mod, qbyte *mod_base, vlump_t *l, vlump_t *lump_verts)\n{\n\tvbspinfo_t\t*prv = (vbspinfo_t*)mod->meshinfo;\n\tint\t\t\ti;\n\tq2dareaportal_t\t\t*out;\n\thl2dareaportal_t \t*in;\n\tint\t\t\tcount, vcount;\n\tvec3_t\t\t*inverts;\n\tmesh_t\t\tmesh;\n\n\tin = (void *)(mod_base + l->fileofs);\n\tif (l->filelen % sizeof(*in))\n\t{\n\t\tCon_Printf (CON_ERROR \"VBSP_LoadAreaPortals: funny lump size\\n\");\n\t\treturn false;\n\t}\n\tcount = l->filelen / sizeof(*in);\n\n\tinverts = (void *)(mod_base + lump_verts->fileofs);\n\tif (lump_verts->filelen % sizeof(*inverts))\n\t{\n\t\tCon_Printf (CON_ERROR \"VBSP_LoadAreaPortals: funny lump size\\n\");\n\t\treturn false;\n\t}\n\tvcount = lump_verts->filelen / sizeof(*inverts);\n\n\tif (count > MAX_Q2MAP_AREAS)\n\t{\n\t\tCon_Printf (CON_ERROR \"Map has too many areas\\n\");\n\t\treturn false;\n\t}\n\n\tout = prv->areaportals = plugfuncs->GMalloc(&mod->memgroup, sizeof(*out) * count);\n\tprv->portalpoly = plugfuncs->GMalloc(&mod->memgroup, sizeof(*prv->portalpoly)*count);\n\tprv->portalplane = plugfuncs->GMalloc(&mod->memgroup, sizeof(*prv->portalplane)*count);\n\tprv->numareaportals = count;\n\n\tmesh.xyz_array = plugfuncs->GMalloc(&mod->memgroup, sizeof(*mesh.xyz_array)*vcount);\n\tfor (i=0 ; i<vcount ; i++)\n\t{\n\t\tmesh.xyz_array[i][0] = LittleFloat(inverts[i][0]);\n\t\tmesh.xyz_array[i][1] = LittleFloat(inverts[i][1]);\n\t\tmesh.xyz_array[i][2] = LittleFloat(inverts[i][2]);\n\t}\n\tmesh.indexes = plugfuncs->GMalloc(&mod->memgroup, sizeof(*mesh.indexes)*64*3);\n\tmesh.st_array = plugfuncs->GMalloc(&mod->memgroup, sizeof(*mesh.st_array)*64);\n\tfor (i=0 ; i<64 ; i++)\n\t{\n\t\tVector2Set(mesh.st_array[i], 0,0);\n\t\tmesh.indexes[i*3+0] = 0;\n\t\tmesh.indexes[i*3+1] = i+1;\n\t\tmesh.indexes[i*3+2] = i+2;\n\t}\n\n\tfor (i=0 ; i<count ; i++, in++, out++)\n\t{\n\t\tout->portalnum = LittleShort (in->portalnum);\n\t\tout->otherarea = LittleShort (in->otherarea);\n\n\t\tprv->portalplane[i] = mod->planes + LittleLong (in->planenum);\n\n\t\tprv->portalpoly[i].xyz_array = mesh.xyz_array+LittleLong(in->firstvert);\n\t\tprv->portalpoly[i].st_array = mesh.st_array;\n\t\tprv->portalpoly[i].numvertexes = LittleLong(in->numverts);\n\n\t\tif (prv->portalpoly[i].numvertexes>2)\n\t\t{\n\t\t\tprv->portalpoly[i].istrifan = true;\n\t\t\tprv->portalpoly[i].indexes = mesh.indexes;\n\t\t\tprv->portalpoly[i].numindexes = (prv->portalpoly[i].numvertexes-2)*3;\n\t\t}\n\t}\n\n\treturn true;\n}\n\ntypedef struct\n{\t//the main displacement lump\n\tvec3_t position;\t//not really sure how to use this.\n\tint firstvert;\t\t//((1<<power)+1) squared verts\n\tint firsttriflags;\t//ignored (two shorts per quad)\n\tint power;\n\tint minpower;\t//ignored... FIXME: add lod...\n\tfloat smoothangle;\n\tunsigned int contents;\n\tunsigned short faceidx; //mod->surfaces index\n\tunsigned int lightmapalphaoffset;\t//erk, rgba? that's going to restrict lightmap formats.\n\tunsigned int lightofs;\t//extents? cabbage? oh noes we've gone mad again! or is this some special blend weights in addition to the surface's lighting?\n\tstruct\n\t{\t//erk\n\t\tunsigned short peer;\n\t\tunsigned char orientation;\n\t\tunsigned char span;\n\t\tunsigned char peerspan;\n\t} edgepeers[8];\t//two per edge\n\tstruct\n\t{\t//no idea how this works\n\t\tunsigned short peer[4];\n\t\tunsigned char numpeers;\n\t} cornerpeers[4];\n\tunsigned int allowedverts[10];\n} hl2ddisplacement_t;\ntypedef struct\n{\n\tvec3_t norm;\n\tfloat dist;\n\tfloat alpha;\t//vertex alpha.\n} hl2displacementvert_t;\nstatic qboolean VBSP_LoadDisplacements (model_t *mod, qbyte *mod_base, vlump_t *lumps)\n{\n\tvbspinfo_t\t\t\t*prv = (vbspinfo_t*)mod->meshinfo;\n\tvlump_t \t\t\t*l = &lumps[VLUMP_DISP_INFO];\n\tvlump_t \t\t\t*vl = &lumps[VLUMP_DISP_VERTS];\n\thl2ddisplacement_t\t*in;\n\tdispinfo_t\t\t\t*out;\n\tint\t\t\t\t\ti, count, x, y;\n\thl2displacementvert_t *inv;\n\tstruct dispvert_s\t*verts;\n\tvecV_t\t\t\t\t*xyz;\n\tindex_t \t\t\t*indexes;\n\tsize_t\t\t\t\tmaxverts;\n\tmsurface_t \t\t\t*surf;\n\tfloat *sverts[4];\n\tsigned int e, idx;\n\tfloat fx,fy;\n\tvec3_t p, base;\n\tsize_t stride;\n\n\tint primary;\n\tfloat pdist,dist;\n\n\tin = (void *)(mod_base + l->fileofs);\n\tif (l->filelen % sizeof(*in))\n\t{\n\t\tCon_Printf (\"VBSP_LoadDisplacements: funny lump size in %s\\n\",mod->name);\n\t\treturn false;\n\t}\n\tcount = l->filelen / sizeof(*in);\n\tif (!count)\n\t\treturn true;\t//nothing to worry about.\n\tout = plugfuncs->GMalloc(&mod->memgroup, count*sizeof(*out));\n\n\tfor (maxverts = 0, i = 0; i < count; i++)\n\t\tmaxverts += ((1<<in[i].power)) * ((1<<in[i].power));\n\tindexes = plugfuncs->GMalloc(&mod->memgroup, maxverts*sizeof(*indexes)*6);\n\n\tfor (maxverts = 0, i = 0; i < count; i++)\n\t\tmaxverts += ((1<<in[i].power)+1) * ((1<<in[i].power)+1);\n\tverts = plugfuncs->GMalloc(&mod->memgroup, maxverts*sizeof(*verts));\n\txyz = plugfuncs->GMalloc(&mod->memgroup, maxverts*sizeof(*xyz));\n\n\tprv->displacements = out;\n\tprv->numdisplacements = count;\n\tfor (i = 0; i < count; i++, out++, in++)\n\t{\n\t\tsurf = mod->surfaces + in->faceidx;\n\t\tif (surf->numedges != 4)\n\t\t{\n\t\t\tCon_Printf (\"VBSP_LoadDisplacements: displacement surface doesn't have 4 edges in %s\\n\",mod->name);\n\t\t\treturn false;\n\t\t}\n\n\t\t//find the 4 verts... messy.\n\t\tfor (x = 0; x < 4; x++)\n\t\t{\n\t\t\te = mod->surfedges[surf->firstedge+x];\n\t\t\tidx = e < 0;\n\t\t\tif (idx)\n\t\t\t\te = -e;\n\t\t\tif (e < 0 || e >= mod->numedges)\n\t\t\t\tsverts[x] = mod->vertexes[0].position;\n\t\t\telse\n\t\t\t\tsverts[x] = mod->vertexes[mod->edges[e].v[idx]].position;\n\t\t}\n\n\t\t//this is just stupid and pointless.\n\t\t//the in->position point tells us which point is the primary one, instead of just rotating the edges.\n\t\tprimary = 0;\n\t\tpdist = FLT_MAX;\n\t\tfor (x = 0; x < 4; x++)\n\t\t{\n\t\t\tVectorSubtract(sverts[x], in->position, p);\n\t\t\tdist = DotProduct(p,p);\n\t\t\tif (dist < pdist)\n\t\t\t{\n\t\t\t\tpdist = dist;\n\t\t\t\tprimary = x;\n\t\t\t}\n\t\t}\n\n\t\tout->surf = surf;\n\t\tprv->surfdisp[in->faceidx] = out;\t//the surface needs to be able to get its proper info when building vbos\n\t\tClearBounds(out->aamin, out->aamax);\n\t\tout->contents = VBSP_TranslateContentBits(prv,in->contents);\n\t\tout->width = (1<<in->power);\n\t\tout->height = (1<<in->power);\n\n\t\tout->idx = indexes;\n\t\tstride = out->width+1;\n\t\tfor (y=0 ; y<out->height ; y++)\n\t\tfor (x=0 ; x<out->width ; x++)\n\t\t{\n\t\t\tif ((x+y)&1)\n\t\t\t{\t//a diamond pattern - flipping alternately\n\t\t\t\t*indexes++ = (x+0)+(y+1)*stride;\n\t\t\t\t*indexes++ = (x+1)+(y+1)*stride;\n\t\t\t\t*indexes++ = (x+1)+(y+0)*stride;\n\t\t\t\t*indexes++ = (x+0)+(y+1)*stride;\n\t\t\t\t*indexes++ = (x+1)+(y+0)*stride;\n\t\t\t\t*indexes++ = (x+0)+(y+0)*stride;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t*indexes++ = (x+0)+(y+0)*stride;\n\t\t\t\t*indexes++ = (x+0)+(y+1)*stride;\n\t\t\t\t*indexes++ = (x+1)+(y+1)*stride;\n\t\t\t\t*indexes++ = (x+0)+(y+0)*stride;\n\t\t\t\t*indexes++ = (x+1)+(y+1)*stride;\n\t\t\t\t*indexes++ = (x+1)+(y+0)*stride;\n\t\t\t}\n\t\t}\n\t\tout->numindexes = indexes - out->idx;\n\n\t\tinv = (void *)(mod_base + vl->fileofs);\n\t\tinv += in->firstvert;\n\t\tout->verts = verts;\n\t\tout->xyz = xyz;\n\t\tfor (y = 0; y <= out->height; y++)\n\t\t\tfor (x = 0; x <= out->width; x++)\n\t\t\t{\n\t\t\t\t//I have no idea if this is right. probably not. oh well.\n\t\t\t\tfx = (float)x/out->width;\n\t\t\t\tfy = (float)y/out->height;\n\t\t\t\tVectorClear(base);\n\t\t\t\tVectorMA(base, (1-fx)*(1-fy), sverts[(primary+0)&3], base);\n\t\t\t\tVectorMA(base, (1-fx)*(  fy), sverts[(primary+1)&3], base);\n\t\t\t\tVectorMA(base, (  fx)*(  fy), sverts[(primary+2)&3], base);\n\t\t\t\tVectorMA(base, (  fx)*(1-fy), sverts[(primary+3)&3], base);\n\t\t\t\tverts->st[0] = DotProduct(base, surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3];\n\t\t\t\tverts->st[1] = DotProduct(base, surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3];\n\n\t\t\t\tVectorScale(inv->norm, inv->dist*hl2_displacement_scale->value, p);\n\t\t\t\tVectorNormalize2(p, verts->norm);\n\t\t\t\tVectorAdd(base, p, (*xyz));\n\t\t\t\tverts->alpha = inv->alpha;\n\t\t\t\tAddPointToBounds((*xyz), out->aamin, out->aamax);\n\n\t\t\t\tinv++;\n\t\t\t\tverts++;\n\t\t\t\txyz++;\n\t\t\t}\n\n\t\tsurf->mesh->numindexes = out->numindexes;\n\t\tsurf->mesh->numvertexes = verts-out->verts;\n\t}\n\treturn true;\n}\n\ntypedef struct\n{\n\tshort\t\tplanenum;\n\tqbyte\t\tside;\n\tqbyte\t\tonnode;\t//o.O\n\n\tint\t\t\tfirstedge;\t\t// we must support > 64k edges\n\tunsigned short\t\tnumedges;\n\tunsigned short\t\ttexinfo;\n\n\tunsigned short\t\tdispinfo;\n\tshort\t\tfogvolume;\n\n// lighting info\n\tqbyte\t\tstyles[4];\n\tint\t\t\tlightofs;\t\t// start of [numstyles*surfsize] samples\n\tfloat\t\tsurfacearea;\n\tint\t\t\textents_min[2];\n\tint\t\t\textents_size[2];\n\n\tint\t\t\torigface;\n\tunsigned short numprims;\n\tunsigned short firstprim;\n\tunsigned int smoothinggroup;\n} hl2dface_t;\n\n\ntypedef struct\n{\n\tint\t\t\tpadding[8];\n\tshort\t\tplanenum;\n\tqbyte\t\tside;\n\tqbyte\t\tonnode;\t//o.O\n\n\tint\t\t\tfirstedge;\t\t// we must support > 64k edges\n\tunsigned short\t\tnumedges;\n\tunsigned short\t\ttexinfo;\n\n\tunsigned short\t\tdispinfo;\n\tshort\t\tfogvolume;\n\n// lighting info\n\tqbyte\t\tstyles[8];\n\tqbyte\t\tday[8];\n\tqbyte\t\tnight[8];\n\tint\t\t\tlightofs;\t\t// start of [numstyles*surfsize] samples\n\tfloat\t\tsurfacearea;\n\tint\t\t\textents_min[2];\n\tint\t\t\textents_size[2];\n\n\tint\t\t\torigface;\n\tunsigned int smoothinggroup;\n} vampiredface_t;\n\nstatic qboolean VBSP_LoadFaces_Vampire (model_t *mod, qbyte *mod_base, vlump_t *lumps, int version)\n{\n\tvbspinfo_t\t*prv = (vbspinfo_t*)mod->meshinfo;\n\tvlump_t *l = &lumps[VLUMP_FACES_LDR];\n\tvampiredface_t\t*in;\n\tmsurface_t \t*out;\n\tint\t\t\ti, count, surfnum;\n\tint\t\t\tplanenum;\n\tint\t\t\tti, st;\n\tint\t\t\tlumpsize = sizeof(*in);\n\n\tmesh_t\t\t*meshes;\n\n\tin = (void *)(mod_base + l->fileofs);\n\tif (l->filelen % lumpsize)\n\t{\n\t\tCon_Printf (\"VBSP_LoadFaces_Vampire: funny lump size in %s\\n\",mod->name);\n\t\treturn false;\n\t}\n\tcount = l->filelen / lumpsize;\n\tout = plugfuncs->GMalloc(&mod->memgroup, count*sizeof(*out));\n\tprv->surfdisp = plugfuncs->GMalloc(&mod->memgroup, count * sizeof(*prv->surfdisp));\n\n\tmeshes = plugfuncs->GMalloc(&mod->memgroup, count*sizeof(*meshes));\n\n\tmod->surfaces = out;\n\tmod->numsurfaces = count;\n\n\tmod->lightmaps.surfstyles = 1;\n\n\tfor ( surfnum=0 ; surfnum<count ; surfnum++, in = (void*)((qbyte*)in+lumpsize), out++)\n\t{\n\t\tout->firstedge = LittleLong(in->firstedge);\n\t\tout->numedges = (unsigned short)LittleShort(in->numedges);\n\t\tout->flags = 0;\n\t\tout->mesh = meshes+surfnum;\n\t\tout->mesh->numvertexes = out->numedges;\n\t\tout->mesh->numindexes = (out->mesh->numvertexes-2)*3;\n\n\t\tplanenum = (unsigned short)LittleShort(in->planenum);\n\t\tif (in->side)\n\t\t\tout->flags |= SURF_PLANEBACK;\n\t\tif (!in->onnode)\n\t\t\tout->flags |= SURF_OFFNODE;\n\n\t\tout->plane = mod->planes + planenum;\n\n\t\tti = (unsigned short)LittleShort (in->texinfo);\n\t\tif (ti < 0 || ti >= mod->numtexinfo)\n\t\t{\n\t\t\tCon_Printf (CON_ERROR \"VBSP_LoadFaces: bad texinfo number\\n\");\n\t\t\treturn false;\n\t\t}\n\t\tout->texinfo = mod->texinfo + ti;\n\n\t\tif (out->texinfo->flags & TI_SKY)\n\t\t{\n\t\t\tout->flags |= SURF_DRAWSKY|SURF_DRAWTILED;\n\t\t}\n\t\tif (out->texinfo->flags & TI_WARP)\n\t\t{\n\t\t\tout->flags |= SURF_DRAWTURB|SURF_DRAWTILED;\n\t\t}\n\n\t\tout->lmshift = 0;\n\t\tout->texturemins[0] = in->extents_min[0];\n\t\tout->texturemins[1] = in->extents_min[1];\n\t\tout->extents[0] = in->extents_size[0];\n\t\tout->extents[1] = in->extents_size[1];\n\n\t// lighting info\n\n\t\tfor (i=0 ; i<Q1Q2BSP_STYLESPERSURF ; i++)\n\t\t{\n\t\t\tst = in->styles[i];\n\t\t\tif (st == 255)\n\t\t\t\tst = INVALID_LIGHTSTYLE;\n\t\t\telse if (mod->lightmaps.maxstyle < st)\n\t\t\t\tmod->lightmaps.maxstyle = st;\n\t\t\tout->styles[i] = st;\n\t\t}\n\t\tfor (; i<MAXCPULIGHTMAPS ; i++)\n\t\t\tout->styles[i] = INVALID_LIGHTSTYLE;\n\t\tfor (i = 0; i<MAXRLIGHTMAPS ; i++)\n\t\t\tout->vlstyles[i] = INVALID_VLIGHTSTYLE;\n\t\ti = LittleLong(in->lightofs);\n\t\tif (i == -1 || !mod->lightdata)\n\t\t\tout->samples = NULL;\n\t\telse\n\t\t\tout->samples = mod->lightdata + i;\n\n\t// set the drawing flags\n\n\t\tif (out->texinfo->flags & TI_WARP)\n\t\t\tout->flags |= SURF_DRAWTURB;\n\t}\n\n\treturn true;\n}\n\nstatic qboolean VBSP_LoadFaces (model_t *mod, qbyte *mod_base, vlump_t *lumps, int version)\n{\n\tvbspinfo_t\t*prv = (vbspinfo_t*)mod->meshinfo;\n\tvlump_t *l = &lumps[VLUMP_FACES_LDR];\n\tvlump_t *l2 = &lumps[VLUMP_FACES_HDR];\n\thl2dface_t\t*in;\n\tmsurface_t \t*out;\n\tint\t\t\ti, count, surfnum;\n\tint\t\t\tplanenum;\n\tint\t\t\tti, st;\n\tint\t\t\tlumpsize = sizeof(*in);\n\n\tmesh_t\t\t*meshes;\n\n\tif (version == 17)\n\t{\n\t\treturn VBSP_LoadFaces_Vampire(mod, mod_base, lumps, version);\n\t}\n\n\tif (l2->filelen && !(hl2_favour_ldr->ival && lumps[VLUMP_LIGHTING_LDR].filelen))\n\t\tl = l2;\n\n\tif (version == 18)\n\t{\t//this version seems to have rgbx*4 prefixed\n\t\tin = (void *)(mod_base + l->fileofs + 4*4);\n\t\tlumpsize += 4*4;\n\t}\n\telse\n\t\tin = (void *)(mod_base + l->fileofs);\n\tif (l->filelen % lumpsize)\n\t{\n\t\tCon_Printf (\"VBSP_LoadFaces: funny lump size in %s\\n\",mod->name);\n\t\treturn false;\n\t}\n\tcount = l->filelen / lumpsize;\n\tout = plugfuncs->GMalloc(&mod->memgroup, count*sizeof(*out));\n\tprv->surfdisp = plugfuncs->GMalloc(&mod->memgroup, count * sizeof(*prv->surfdisp));\n\n\tmeshes = plugfuncs->GMalloc(&mod->memgroup, count*sizeof(*meshes));\n\n\tmod->surfaces = out;\n\tmod->numsurfaces = count;\n\n\tmod->lightmaps.surfstyles = 1;\n\n\tfor ( surfnum=0 ; surfnum<count ; surfnum++, in = (void*)((qbyte*)in+lumpsize), out++)\n\t{\n\t\tout->firstedge = LittleLong(in->firstedge);\n\t\tout->numedges = (unsigned short)LittleShort(in->numedges);\n\t\tout->flags = 0;\n\t\tout->mesh = meshes+surfnum;\n\t\tout->mesh->numvertexes = out->numedges;\n\t\tout->mesh->numindexes = (out->mesh->numvertexes-2)*3;\n\n\t\tplanenum = (unsigned short)LittleShort(in->planenum);\n\t\tif (in->side)\n\t\t\tout->flags |= SURF_PLANEBACK;\n\t\tif (!in->onnode)\n\t\t\tout->flags |= SURF_OFFNODE;\n\n\t\tout->plane = mod->planes + planenum;\n\n\t\tti = (unsigned short)LittleShort (in->texinfo);\n\t\tif (ti < 0 || ti >= mod->numtexinfo)\n\t\t{\n\t\t\tCon_Printf (CON_ERROR \"VBSP_LoadFaces: bad texinfo number\\n\");\n\t\t\treturn false;\n\t\t}\n\t\tout->texinfo = mod->texinfo + ti;\n\n\t\tif (out->texinfo->flags & TI_SKY)\n\t\t{\n\t\t\tout->flags |= SURF_DRAWSKY|SURF_DRAWTILED;\n\t\t}\n\t\tif (out->texinfo->flags & TI_WARP)\n\t\t{\n\t\t\tout->flags |= SURF_DRAWTURB|SURF_DRAWTILED;\n\t\t}\n\n\t\tout->lmshift = 0;\n\t\tout->texturemins[0] = in->extents_min[0];\n\t\tout->texturemins[1] = in->extents_min[1];\n\t\tout->extents[0] = in->extents_size[0];\n\t\tout->extents[1] = in->extents_size[1];\n\n\t// lighting info\n\n\t\tfor (i=0 ; i<Q1Q2BSP_STYLESPERSURF ; i++)\n\t\t{\n\t\t\tst = in->styles[i];\n\t\t\tif (st == 255)\n\t\t\t\tst = INVALID_LIGHTSTYLE;\n\t\t\telse if (mod->lightmaps.maxstyle < st)\n\t\t\t\tmod->lightmaps.maxstyle = st;\n\t\t\tout->styles[i] = st;\n\t\t}\n\t\tfor (; i<MAXCPULIGHTMAPS ; i++)\n\t\t\tout->styles[i] = INVALID_LIGHTSTYLE;\n\t\tfor (i = 0; i<MAXRLIGHTMAPS ; i++)\n\t\t\tout->vlstyles[i] = INVALID_VLIGHTSTYLE;\n\t\ti = LittleLong(in->lightofs);\n\t\tif (i == -1 || !mod->lightdata)\n\t\t\tout->samples = NULL;\n\t\telse\n\t\t\tout->samples = mod->lightdata + i;\n\n\t// set the drawing flags\n\n\t\tif (out->texinfo->flags & TI_WARP)\n\t\t\tout->flags |= SURF_DRAWTURB;\n\t}\n\n\treturn true;\n}\n#ifdef HAVE_CLIENT\nstatic void VBSP_BuildSurfMesh(model_t *mod, msurface_t *surf, builddata_t *bd)\n{\n\tvbspinfo_t\t*prv = (vbspinfo_t*)mod->meshinfo;\n\n\tunsigned int vertidx;\n\tint i, lindex, edgevert;\n\tmesh_t *mesh = surf->mesh;\n\tfloat *vec;\n\tfloat s, t, miss;\n\tint sty;\n\tstruct vbsptexinfo_s *vtexinfo;\n\n\t//displacement surfaces...\n\tdispinfo_t *d = prv->surfdisp[surf-mod->surfaces];\n\tif (d)\n\t{\n\t\tstruct dispvert_s *dv = d->verts;\n\n\t\tmesh->istrifan = false;\n\n\t\tmemcpy(mesh->indexes, d->idx, sizeof(index_t)*d->numindexes);\n\n\t\t//output the renderable verticies\n\t\tfor (i=0 ; i<mesh->numvertexes ; i++, dv++)\n\t\t{\n\t\t\t//xyz\n\t\t\tVectorCopy (d->xyz[i], mesh->xyz_array[i]);\n\n\t\t\t//st\n\t\t\tmesh->st_array[i][0] = dv->st[0];\n\t\t\tmesh->st_array[i][1] = dv->st[1];\n\t\t\tif (surf->texinfo->texture->vwidth)\n\t\t\t\tmesh->st_array[i][0] /= surf->texinfo->texture->vwidth;\n\t\t\tif (surf->texinfo->texture->vheight)\n\t\t\t\tmesh->st_array[i][1] /= surf->texinfo->texture->vheight;\n\n\t\t\t//lmst\n\t\t\ts = (float)(i%(d->width+1))/d->width;\n\t\t\tt = (float)(i/(d->width+1))/d->height;\n\t\t\tfor (sty = 0; sty < 1; sty++)\n\t\t\t{\n\t\t\t\tmesh->lmst_array[sty][i][0] = (s*surf->extents[0] + surf->light_s[sty] + 0.5) / (mod->lightmaps.width);\n\t\t\t\tmesh->lmst_array[sty][i][1] = (t*surf->extents[1] + surf->light_t[sty] + 0.5) / (mod->lightmaps.height);\n\t\t\t}\n\n\t\t\t//normals\n\t\t\tVectorCopy(surf->plane->normal, mesh->normals_array[i]);\n\t\t\tVectorCopy(surf->texinfo->vecs[0], mesh->snormals_array[i]);\n\t\t\tVectorNegate(surf->texinfo->vecs[1], mesh->tnormals_array[i]);\n\n\t\t\t//rgba\n\t\t\tfor (sty = 0; sty < 1; sty++)\n\t\t\t{\n\t\t\t\tmesh->colors4f_array[sty][i][0] = 1;\n\t\t\t\tmesh->colors4f_array[sty][i][1] = 1;\n\t\t\t\tmesh->colors4f_array[sty][i][2] = 1;\n\t\t\t\tmesh->colors4f_array[sty][i][3] = ((int)dv->alpha&255)/255.0;\n\t\t\t}\n\t\t}\n\t\treturn;\n\t}\n\n\t//regular surfaces...\n\tmesh->istrifan = true;\n\n\t//output the mesh's indicies\n\tfor (i=0 ; i<mesh->numvertexes-2 ; i++)\n\t{\n\t\tmesh->indexes[i*3] = 0;\n\t\tmesh->indexes[i*3+1] = i+1;\n\t\tmesh->indexes[i*3+2] = i+2;\n\t}\n\t//output the renderable verticies\n\tfor (i=0 ; i<mesh->numvertexes ; i++)\n\t{\n\t\tlindex = mod->surfedges[surf->firstedge + i];\n\t\tedgevert = lindex <= 0;\n\t\tif (edgevert)\n\t\t\tlindex = -lindex;\n\t\tif (lindex < 0 || lindex >= mod->numedges)\n\t\t\tvertidx = 0;\n\t\telse\n\t\t\tvertidx = mod->edges[lindex].v[edgevert];\n\t\tvec = mod->vertexes[vertidx].position;\n\n\t\ts = DotProduct (vec, surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3];\n\t\tt = DotProduct (vec, surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3];\n\n\t\tVectorCopy (vec, mesh->xyz_array[i]);\n\n\t\tmesh->st_array[i][0] = s;\n\t\tmesh->st_array[i][1] = t;\n\t\tif (surf->texinfo->texture->vwidth)\n\t\t\tmesh->st_array[i][0] /= surf->texinfo->texture->vwidth;\n\t\tif (surf->texinfo->texture->vheight)\n\t\t\tmesh->st_array[i][1] /= surf->texinfo->texture->vheight;\n\n\t\tvtexinfo = &prv->texinfo[surf->texinfo-mod->texinfo];\n\t\ts = DotProduct (vec, vtexinfo->lmvecs[0]) + vtexinfo->lmvecs[0][3];\n\t\tt = DotProduct (vec, vtexinfo->lmvecs[1]) + vtexinfo->lmvecs[1][3];\n\t\tfor (sty = 0; sty < 1; sty++)\n\t\t{\n\t\t\tmesh->lmst_array[sty][i][0] = (s - surf->texturemins[0] + (surf->light_s[sty]<<surf->lmshift) + (1<<surf->lmshift)*0.5) / (mod->lightmaps.width<<surf->lmshift);\n\t\t\tmesh->lmst_array[sty][i][1] = (t - surf->texturemins[1] + (surf->light_t[sty]<<surf->lmshift) + (1<<surf->lmshift)*0.5) / (mod->lightmaps.height<<surf->lmshift);\n\t\t}\n\n\t\t//figure out the texture directions, for bumpmapping and stuff\n// \t\tif (surf->flags & SURF_PLANEBACK)\n// \t\t\tVectorNegate(surf->plane->normal, mesh->normals_array[i]);\n// \t\telse\n\t\tVectorCopy(surf->plane->normal, mesh->normals_array[i]);\n\t\tVectorCopy(surf->texinfo->vecs[0], mesh->snormals_array[i]);\n\t\tVectorNegate(surf->texinfo->vecs[1], mesh->tnormals_array[i]);\n\t\t//the s+t vectors are axis-aligned, so fiddle them so they're normal aligned instead\n\t\tmiss = -DotProduct(mesh->normals_array[i], mesh->snormals_array[i]);\n\t\tVectorMA(mesh->snormals_array[i], miss, mesh->normals_array[i], mesh->snormals_array[i]);\n\t\tmiss = -DotProduct(mesh->normals_array[i], mesh->tnormals_array[i]);\n\t\tVectorMA(mesh->tnormals_array[i], miss, mesh->normals_array[i], mesh->tnormals_array[i]);\n\t\tVectorNormalize(mesh->snormals_array[i]);\n\t\tVectorNormalize(mesh->tnormals_array[i]);\n\n\t\t//q1bsp has no colour information (fixme: sample from the lightmap?)\n\t\tfor (sty = 0; sty < 1; sty++)\n\t\t{\n\t\t\tmesh->colors4f_array[sty][i][0] = 1;\n\t\t\tmesh->colors4f_array[sty][i][1] = 1;\n\t\t\tmesh->colors4f_array[sty][i][2] = 1;\n\t\t\tmesh->colors4f_array[sty][i][3] = 1;\n\t\t}\n\t}\n}\n\n\nstatic void VBSP_LoadLighting (model_t *mod, qbyte *mod_base, vlump_t *ldr, vlump_t *hdr)\n{\n\tqbyte *src;\n\tunsigned int *out;\n\tsize_t count;\n\tif (hdr->filelen && !(hl2_favour_ldr->ival && ldr->filelen))\n\t{\n\t\tmod->lightdatasize = hdr->filelen;\n\t\tsrc = (mod_base + hdr->fileofs);\n\t}\n\telse if (ldr->filelen)\n\t{\n\t\tmod->lightdatasize = ldr->filelen;\n\t\tsrc = (mod_base + ldr->fileofs);\n\t}\n\telse\n\t\treturn;\n\n\tmod->lightmaps.fmt = LM_E5BGR9;\n\tmod->lightdata = (qbyte*)(out = plugfuncs->GMalloc(&mod->memgroup, mod->lightdatasize));\n\n\t//convert from linear e8bgr8 to srgb e5bgr9\n\tfor (count = mod->lightdatasize/4; count --> 0; src+=4)\n\t{\n\t\tint e = 0;\n\t\tfloat m;\n\t\tfloat scale;\n\t\tunsigned int hdr;\n\t\tvec3_t rgb;\n\n\t\t//decode input\n\t\tm = pow(2, (signed char)src[3])/255.0;\n\t\trgb[0] = m * src[0];\n\t\trgb[1] = m * src[1];\n\t\trgb[2] = m * src[2];\n\n\t\t//rescale its gamma ramp to something we can actually use properly\n\t\trgb[0] = M_LinearToSRGB(rgb[0], 1.0);\n\t\trgb[1] = M_LinearToSRGB(rgb[1], 1.0);\n\t\trgb[2] = M_LinearToSRGB(rgb[2], 1.0);\n\n\t\t//encode output\n\t\tm = max(max(rgb[0], rgb[1]), rgb[2]);\n\t\tif (m < 0)\n\t\t\tm = 0;\n\n\t\tif (m >= 0.5)\n\t\t{\t//positive exponent\n\t\t\twhile (m >= (1<<(e)) && e < 30-15)\t//don't do nans.\n\t\t\t\te++;\n\t\t}\n\t\telse\n\t\t{\t//negative exponent...\n\t\t\twhile (m < 1/(1<<-e) && e > -14)\t//don't do denormals.\n\t\t\t\te--;\n\t\t}\n\n\t\tscale = pow(2, e-9);\n\t\thdr = ((e+15)<<27);\n\t\thdr |= bound(0, (int)(rgb[0]/scale + 0.5), 0x1ff)<<0;\n\t\thdr |= bound(0, (int)(rgb[1]/scale + 0.5), 0x1ff)<<9;\n\t\thdr |= bound(0, (int)(rgb[2]/scale + 0.5), 0x1ff)<<18;\n\t\t*out++ = hdr;\n\t}\n}\n#endif\n\ntypedef struct\n{\n\tunsigned short planenum;\n\tshort texinfo;\n\tunsigned short dispinfo;\n\tshort bevel;\n} hl2dbrushside_t;\nstatic qboolean VBSP_LoadBrushSides (model_t *mod, qbyte *mod_base, vlump_t *l)\n{\n\tvbspinfo_t\t*prv = (vbspinfo_t*)mod->meshinfo;\n\tunsigned int\t\t\ti, j;\n\tq2cbrushside_t\t*out;\n\thl2dbrushside_t *in;\n\tint\t\t\tcount;\n\tint\t\t\tnum;\n\n\tin = (void *)(mod_base + l->fileofs);\n\tif (l->filelen % sizeof(*in))\n\t{\n\t\tCon_Printf (CON_ERROR \"VBSP_LoadBrushSides: funny lump size\\n\");\n\t\treturn false;\n\t}\n\tcount = l->filelen / sizeof(*in);\n\n\t// need to save space for box planes\n\tif (count > SANITY_MAX_MAP_BRUSHSIDES)\n\t{\n\t\tCon_Printf (CON_ERROR \"Map has too many brushsides (%i)\\n\", count);\n\t\treturn false;\n\t}\n\n\tout = prv->brushsides = plugfuncs->GMalloc(&mod->memgroup, sizeof(*out) * count);\n\tprv->numbrushsides = count;\n\n\tfor ( i=0 ; i<count ; i++, in++, out++)\n\t{\n\t\tnum = (unsigned short)LittleShort (in->planenum);\n\t\tout->plane = &mod->planes[num];\n\t\tj = (unsigned short)LittleShort (in->texinfo);\n\t\tif (j >= mod->numtexinfo)\n\t\t\tout->surface = &nullsurface;\n\t\telse\n\t\t\tout->surface = &prv->surfaces[j];\n\t}\n\n\treturn true;\n}\n\ntypedef struct {\n\tunsigned int count;\n\tstruct {\n\t\tunsigned int id;\n\t\tunsigned short flags;\n\t\tunsigned short version;\n\t\tunsigned int ofs;\n\t\tunsigned int len;\n\t} sl[1];\n} hlgamelumpheader_t;\nstatic qboolean VBSP_LoadStaticProps(model_t *mod, qbyte *offset, size_t size, int version)\t//present on server, because they're potentially solid.\n{\n\tvbspinfo_t *prv = mod->meshinfo;\n\tstruct {\n\t\tconst char name[128];\n\t} *modelref;\n\tsize_t nummodels, numleafrefs, numprops, i;\n\tunsigned short *leafref, *eleafref;\n\tstruct staticprop_s *sent;\n\tentity_t *ent;\n\n\tsize_t modelindex;\n\n\tqboolean skip = false;\n\tint dxlevel = 95, cpulevel=0, gpulevel=0;\n\tunsigned int leafcount, l;\n\n\tqbyte *prop;\n\tsize_t propsize;\n\tswitch(version)\n\t{\n\tcase 4:\n\t\tpropsize = 14*4;\n\t\tbreak;\n\tcase 5:\t//+scale\n\t\tpropsize = 15*4;\n\t\tbreak;\n\tcase 6://+dxlevels\n\t\tpropsize = 16*4;\n\t\tbreak;\n\tcase 7:\t//+rgba\n\tcase 8: //-dxlevels+[cg]pulevels\n\t\tpropsize = 17*4;\n\t\tbreak;\n\tcase 9:\t//+360\n\tcase 10://-360+flags\n\t\tpropsize = 18*4;\n\t\tbreak;\n\tcase 11://+scale\n\t\tpropsize = 19*4;\n\t\tbreak;\n\tdefault:\n\t\treturn true;\t//version not supported, just ignore it entirely. sorry.\n\t}\n\n\n\tnummodels = LittleLong(*(int*)offset);\n\toffset\t+= 4;\n\tsize\t-= 4;\n\tmodelref = (void*)offset;\n\toffset\t+= nummodels*sizeof(*modelref);\n\tsize\t-= nummodels*sizeof(*modelref);\n\n\tnumleafrefs = LittleLong(*(int*)offset);\n\toffset\t+= 4;\n\tsize\t-= 4;\n\tleafref = (void*)offset;\n\toffset\t+= numleafrefs*sizeof(*leafref);\n\tsize\t-= numleafrefs*sizeof(*leafref);\n\n\tnumprops = LittleLong(*(int*)offset);\n\toffset\t+= 4;\n\tsize\t-= 4;\n\tprop = (void*)offset;\n\toffset\t+= numprops*propsize;\n\tsize\t-= numprops*propsize;\n\n\tif (size)\n\t\treturn true;\t//funny lump size...\n\n\tprv->staticprops = plugfuncs->GMalloc(&mod->memgroup, sizeof(*prv->staticprops)*numprops);\n\tfor (i = 0, sent = prv->staticprops; i < numprops; i++)\n\t{\n\t\tent = &sent->ent;\n\t\tskip = false;\n\t\tent->playerindex = -1;\n\t\tent->topcolour = TOP_DEFAULT;\n\t\tent->bottomcolour = BOTTOM_DEFAULT;\n\t\tent->scale = 1;\n\t\tent->flags = RF_NOSHADOW;\n\t\tent->shaderRGBAf[0] = 1;\n\t\tent->shaderRGBAf[1] = 1;\n\t\tent->shaderRGBAf[2] = 1;\n\t\tent->shaderRGBAf[3] = 1;\n\t\tent->framestate.g[FS_REG].frame[0] = 0;\n\t\tent->framestate.g[FS_REG].lerpweight[0] = 1;\n\n\n\t\tent->origin[0] = LittleFloat(*(float*)prop);\t\t\t\t\tprop += sizeof(float);\n\t\tent->origin[1] = LittleFloat(*(float*)prop);\t\t\t\t\tprop += sizeof(float);\n\t\tent->origin[2] = LittleFloat(*(float*)prop);\t\t\t\t\tprop += sizeof(float);\n\t\tent->angles[0] = LittleFloat(*(float*)prop);\t\t\t\t\tprop += sizeof(float);\n\t\tent->angles[1] = LittleFloat(*(float*)prop);\t\t\t\t\tprop += sizeof(float);\n\t\tent->angles[2] = LittleFloat(*(float*)prop);\t\t\t\t\tprop += sizeof(float);\n\t\tmodelindex = (unsigned short)LittleShort(*(short*)prop);\t\tprop += sizeof(unsigned short);\n\t\teleafref = leafref+(unsigned short)LittleShort(*(unsigned short*)prop);\t\t\tprop += sizeof(unsigned short);\n\t\tleafcount = LittleShort(*(unsigned short*)prop);\t\t\t\tprop += sizeof(unsigned short);\n\t\tsent->solid = *prop;\t\t\t\t\t\t\t\t\t\t\tprop += sizeof(qbyte);\n\t\t/*ent->flags = *prop*/;\t\t\t\t\t\t\t\t\t\t\tprop += sizeof(qbyte);\n\t\tent->skinnum = LittleLong(*(unsigned int*)prop);\t\t\t\tprop += sizeof(unsigned int);\n\t\tsent->fademindist = LittleFloat(*(float*)prop);\t\t\t\t\tprop += sizeof(float);\n\t\tsent->fademaxdist = LittleFloat(*(float*)prop);\t\t\t\t\tprop += sizeof(float);\n\t\tsent->lightorg[0] = LittleFloat(*(float*)prop);\t\t\t\t\tprop += sizeof(float);\n\t\tsent->lightorg[1] = LittleFloat(*(float*)prop);\t\t\t\t\tprop += sizeof(float);\n\t\tsent->lightorg[2] = LittleFloat(*(float*)prop);\t\t\t\t\tprop += sizeof(float);\n\t\tif (version >= 5)\n\t\t{\n\t\t\t/*ent->fadescale = LittleFloat(*(float*)prop);*/\t\t\tprop += sizeof(float);\n\t\t}\n\t\tif (version >= 8)\n\t\t{\n\t\t\tskip |= (prop[0] > cpulevel || cpulevel > prop[1]);\t\t\tprop += sizeof(qbyte)*2;\n\t\t\tskip |= (prop[0] > gpulevel || gpulevel > prop[1]);\t\t\tprop += sizeof(qbyte)*2;\n\t\t}\n\t\telse if (version >= 6)\n\t\t{\n\t\t\tunsigned short minlev, maxlev;\n\t\t\tminlev = LittleShort(*(unsigned short*)prop);\t\t\t\tprop += sizeof(unsigned short);\n\t\t\tmaxlev = LittleShort(*(unsigned short*)prop);\t\t\t\tprop += sizeof(unsigned short);\n\t\t\tskip |=  (minlev > 0) && ((minlev > dxlevel) || (dxlevel > maxlev));\n\t\t}\n\t\tif (version >= 7)\n\t\t{\n\t\t\tVectorScale(prop, 1/255.0, ent->shaderRGBAf);\t\t\t\tprop += sizeof(qbyte)*4;\n\t\t}\n\n\t\tif (version == 9)\n\t\t{\n\t\t\t/*disablex360 = LittleLong(*(int*)prop);*/\t\t\t\t\tprop += sizeof(int);\n\t\t}\n\t\tif (version >= 10)\n\t\t{\n\t\t\tent->flags = LittleLong(*(int*)prop);\t\t\t\t\t\tprop += sizeof(int);\n\t\t}\n\t\tif (version >= 11)\n\t\t{\n\t\t\tent->scale = LittleFloat(*(float*)prop);\t\t\t\t\tprop += sizeof(float);\n\t\t}\n\n\t\t//okay, we parsed the prop data now...\n\t\tskip |= modelindex >= nummodels;\n\t\tif (skip)\n\t\t\tcontinue;\t//we're ignoring it for some reason\n\t\tent->model = modfuncs->BeginSubmodelLoad(modelref[modelindex].name);\n\t\tmodfuncs->AngleVectors(ent->angles, ent->axis[0], ent->axis[1], ent->axis[2]);\n\t\tVectorNegate(ent->axis[1],ent->axis[1]);\n\n\t\t//transforms, in case we need to recursively walk its bih\n\t\tVectorCopy(ent->axis[0], prv->staticprops[i].transform.axis[0]);\n\t\tVectorCopy(ent->axis[1], prv->staticprops[i].transform.axis[1]);\n\t\tVectorCopy(ent->axis[2], prv->staticprops[i].transform.axis[2]);\n\t\tVectorCopy(ent->origin, prv->staticprops[i].transform.origin);\n\n\t\t//Hack: special value to flag it for linking once we've loaded its model. this needs to go when we make them solid.\n\t\tif (leafcount > countof(ent->pvscache.leafnums))\n\t\t\tent->pvscache.num_leafs = -2;\t//overflow. calculate it later once its loaded.\n\t\telse\n\t\t{\n\t\t\tent->pvscache.areanum = -1;\t//FIXME\n\t\t\tent->pvscache.areanum2 = -1;\n\t\t\tent->pvscache.num_leafs = leafcount;\t//overflow. calculate it later once its loaded.\n\t\t\tfor (l = 0; l < leafcount; l++)\n\t\t\t{\n\t\t\t\tmleaf_t *lf = mod->leafs + *eleafref++;\n\t\t\t\tent->pvscache.leafnums[l]/*actually clusters*/ = lf->cluster;\n\t\t\t\t//and try to track the areas too. not quite so reliable...\n\t\t\t\tif (ent->pvscache.areanum == -1)\n\t\t\t\t\tent->pvscache.areanum = lf->area;\n\t\t\t\telse\n\t\t\t\t\tent->pvscache.areanum2 = lf->area;\n\t\t\t}\n\t\t}\n\t\t//Hack: lighting is wrong.\n//\t\tent->light_type = ELT_UNKNOWN;\n#if 0\n\t\tVectorSet(ent->light_dir, 0, 0.707, 0.707);\n\t\tVectorSet(ent->light_avg, 0.75, 0.75, 0.75);\n\t\tVectorSet(ent->light_range, 0.5, 0.5, 0.5);\n#endif\n\n\t\t//not all props will be emitted, according to d3d levels...\n\t\tprv->numstaticprops++;\n\t\tsent++;\n\t}\n\treturn true;\n}\nstatic qboolean VBSP_LoadGameLump(model_t *mod, qbyte *mod_base, vlump_t *l)\n{\n\tsize_t i;\n\thlgamelumpheader_t *blob = (void*)(mod_base + l->fileofs);\n\tif (!l->filelen)\n\t\treturn true; //missing\n\tif (l->filelen < sizeof(*blob) + sizeof(*blob->sl)*(blob->count-1))\n\t\treturn false;\t//not even enough space for the header...\n\tfor (i = 0; i < blob->count; i++)\n\t{\n#define LUMPTYPE(a,b,c,d, minver, maxver) (blob->sl[i].id == (((qbyte)a<<24)|((qbyte)b<<16)|((qbyte)c<<8)|((qbyte)d<<0)) && blob->sl[i].version >= minver && blob->sl[i].version <= maxver)\n\t\tif (LUMPTYPE('s','p','r','p', 4,10) && !blob->sl[i].flags)\t//static props (placed by mapper)\n\t\t\tVBSP_LoadStaticProps(mod, mod_base+blob->sl[i].ofs/*sigh*/, blob->sl[i].len, blob->sl[i].version);\n\t\telse if (LUMPTYPE('d','p','r','p', 4,4) && !blob->sl[i].flags)\t//dynamic props (generated by textures)\n\t\t\t;\n\t\telse if (LUMPTYPE('d','p','l','t', 0,0) && !blob->sl[i].flags)\t//detail prop ldr lighting\n\t\t\t;\n\t\telse if (LUMPTYPE('d','p','l','h', 0,0) && !blob->sl[i].flags)\t//detail prop hdr lighting\n\t\t\t;\n\t\telse\n\t\t\tCon_Printf(\"Unsupported gamelump id/version %c%c%c%c %i\\n\", (blob->sl[i].id>>24),(blob->sl[i].id>>16),(blob->sl[i].id>>8),(blob->sl[i].id>>0),blob->sl[i].version);\n#undef LUMPTYPE\n\t}\n\treturn true;\n}\n\n#ifdef HAVE_CLIENT\nstatic void VBSP_GenerateMaterials(void *ctx, void *data, size_t a, size_t b)\n{\n\tmodel_t *mod = ctx;\n\tconst char *script;\n\n\tif (!a)\n\t{\t//submodels share textures, so only do this if 'a' is 0 (inline index, 0 = world).\n\t\tfor(a = 0; a < mod->numtextures; a++)\n\t\t{\n\t\t\tscript = NULL;\n\t\t\tif (!strncmp(mod->textures[a]->name, \"sky/\", 4))\n\t\t\t\tscript =\n\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\"sort sky\\n\"\n\t\t\t\t\t\t\"surfaceparm nodlight\\n\"\n\t\t\t\t\t\t\"skyparms - - -\\n\"\n\t\t\t\t\t\"}\\n\";\n\t\t\tmod->textures[a]->shader = modfuncs->RegisterBasicShader(mod, mod->textures[a]->name, SUF_LIGHTMAP, script, PTI_INVALID, 0, 0, NULL, NULL);\n\t\t}\n\t}\n\tmodfuncs->Batches_Build(mod, data);\n\tif (data)\n\t\tplugfuncs->Free(data);\n}\n#endif\n\n/*\n==================\nCM_InlineModel\n==================\n*/\nstatic cmodel_t\t*VBSP_InlineModel (model_t *model, char *name)\n{\n\tvbspinfo_t\t*prv = (vbspinfo_t*)model->meshinfo;\n\tint\t\tnum;\n\n\tif (!name)\n\t\tHost_Error(\"Bad model\\n\");\n\telse if (name[0] != '*')\n\t\tHost_Error(\"Bad model\\n\");\n\n\tnum = atoi (name+1);\n\n\tif (num < 1 || num >= prv->numcmodels)\n\t\tHost_Error (\"CM_InlineModel: bad number\");\n\n\treturn &prv->cmodels[num];\n}\n\nstatic int VBSP_NumInlineModels (model_t *model)\n{\n\tvbspinfo_t\t*prv = (vbspinfo_t*)model->meshinfo;\n\treturn prv->numcmodels;\n}\n\nstatic int VBSP_LeafContents (model_t *model, int leafnum)\n{\n\tif (leafnum < 0 || leafnum >= model->numleafs)\n\t\tHost_Error (\"CM_LeafContents: bad number\");\n\treturn model->leafs[leafnum].contents;\n}\n\nstatic int VBSP_LeafCluster (model_t *model, int leafnum)\n{\n\tif (leafnum < 0 || leafnum >= model->numleafs)\n\t\tHost_Error (\"CM_LeafCluster: bad number\");\n\treturn model->leafs[leafnum].cluster;\n}\n\nstatic int VBSP_LeafArea (model_t *model, int leafnum)\n{\n\tif (leafnum < 0 || leafnum >= model->numleafs)\n\t\tHost_Error (\"CM_LeafArea: bad number\");\n\treturn model->leafs[leafnum].area;\n}\n\n//=======================================================================\n\n/*\n==================\nCM_PointLeafnum_r\n\n==================\n*/\nstatic int VBSP_PointLeafnum_r (model_t *mod, const vec3_t p, int num)\n{\n\tfloat\t\td;\n\tmnode_t\t\t*node;\n\tmplane_t\t*plane;\n\n\twhile (num >= 0)\n\t{\n\t\tnode = mod->nodes + num;\n\t\tplane = node->plane;\n\n\t\tif (plane->type < 3)\n\t\t\td = p[plane->type] - plane->dist;\n\t\telse\n\t\t\td = DotProduct (plane->normal, p) - plane->dist;\n\t\tif (d < 0)\n\t\t\tnum = node->childnum[1];\n\t\telse\n\t\t\tnum = node->childnum[0];\n\t}\n\n\treturn -1 - num;\n}\n\nstatic int VBSP_PointLeafnum (model_t *mod, const vec3_t p)\n{\n\tif (mod->loadstate != MLS_LOADED)\n\t\treturn 0;\t\t// sound may call this without map loaded\n\treturn VBSP_PointLeafnum_r (mod, p, 0);\n}\n\nstatic int VBSP_PointCluster (model_t *mod, const vec3_t p, int *area)\n{\n\tint leaf;\n\tif (mod->loadstate != MLS_LOADED)\n\t\treturn 0;\t\t// sound may call this without map loaded\n\n\tleaf = VBSP_PointLeafnum_r (mod, p, 0);\n\tif (area)\n\t\t*area = VBSP_LeafArea(mod, leaf);\n\treturn VBSP_LeafCluster(mod, leaf);\n}\n\nstatic unsigned int VBSP_PointContents (model_t *mod, const vec3_t axis[3], const vec3_t p)\n{\n\tvec3_t np;\n\tif (mod->loadstate != MLS_LOADED)\n\t\treturn 0;\n\n\tif (axis)\n\t{\n\t\tnp[0] = DotProduct(p, axis[0]);\n\t\tnp[1] = DotProduct(p, axis[1]);\n\t\tnp[2] = DotProduct(p, axis[2]);\n\t\tp = np;\n\t}\n\n\treturn VBSP_LeafContents(mod, VBSP_PointLeafnum_r (mod, p, 0));\n}\n\n/*\n=============\nCM_BoxLeafnums\n\nFills in a list of all the leafs touched\n=============\n*/\nstatic int\t\tleaf_count, leaf_maxcount;\nstatic int\t\t*leaf_list;\nstatic const float\t*leaf_mins, *leaf_maxs;\nstatic int\t\tleaf_topnode;\n\nstatic void VBSP_BoxLeafnums_r (model_t *mod, int nodenum)\n{\n\tmplane_t\t*plane;\n\tmnode_t\t\t*node;\n\tint\t\ts;\n\n\twhile (1)\n\t{\n\t\tif (nodenum < 0)\n\t\t{\n\t\t\tif (leaf_count >= leaf_maxcount)\n\t\t\t\treturn;\n\t\t\tleaf_list[leaf_count++] = -1 - nodenum;\n\t\t\treturn;\n\t\t}\n\n\t\tnode = &mod->nodes[nodenum];\n\t\tplane = node->plane;\n//\t\ts = BoxOnPlaneSide (leaf_mins, leaf_maxs, plane);\n\t\ts = BOX_ON_PLANE_SIDE(leaf_mins, leaf_maxs, plane);\n\t\tif (s == 1)\n\t\t\tnodenum = node->childnum[0];\n\t\telse if (s == 2)\n\t\t\tnodenum = node->childnum[1];\n\t\telse\n\t\t{\t// go down both\n\t\t\tif (leaf_topnode == -1)\n\t\t\t\tleaf_topnode = nodenum;\n\t\t\tVBSP_BoxLeafnums_r (mod, node->childnum[0]);\n\t\t\tnodenum = node->childnum[1];\n\t\t}\n\n\t}\n}\n\nstatic int\tVBSP_BoxLeafnums_headnode (model_t *mod, const vec3_t mins, const vec3_t maxs, int *list, int listsize, int headnode, int *topnode)\n{\n\tleaf_list = list;\n\tleaf_count = 0;\n\tleaf_maxcount = listsize;\n\tleaf_mins = mins;\n\tleaf_maxs = maxs;\n\n\tleaf_topnode = -1;\n\n\tVBSP_BoxLeafnums_r (mod, headnode);\n\n\tif (topnode)\n\t\t*topnode = leaf_topnode;\n\n\treturn leaf_count;\n}\n\nstatic int\tVBSP_BoxLeafnums (model_t *mod, const vec3_t mins, const vec3_t maxs, int *list, int listsize, int *topnode)\n{\n\treturn VBSP_BoxLeafnums_headnode (mod, mins, maxs, list,\n\t\tlistsize, mod->hulls[0].firstclipnode, topnode);\n}\n\nstatic void VBSP_FindTouchedLeafs(model_t *model, struct pvscache_s *ent, const float *mins, const float *maxs)\n{\n#define MAX_TOTAL_ENT_LEAFS\t\t128\n\tint\t\t\tleafs[MAX_TOTAL_ENT_LEAFS];\n\tint\t\t\tclusters[MAX_TOTAL_ENT_LEAFS];\n\tint num_leafs;\n\tint\t\t\ttopnode;\n\tint i, j;\n\tint\t\t\tarea;\n\tint nullarea = -1;\n\n\t//ent->num_leafs == q2's ent->num_clusters\n\tent->num_leafs = 0;\n\tent->areanum = nullarea;\n\tent->areanum2 = nullarea;\n\n\tif (!mins || !maxs)\n\t\treturn;\n\n\t//get all leafs, including solids\n\tnum_leafs = VBSP_BoxLeafnums (model, mins, maxs,\n\t\tleafs, MAX_TOTAL_ENT_LEAFS, &topnode);\n\n\t// set areas\n\tfor (i=0 ; i<num_leafs ; i++)\n\t{\n\t\tclusters[i] = VBSP_LeafCluster (model, leafs[i]);\n\t\tarea = VBSP_LeafArea (model, leafs[i]);\n\t\tif (area != nullarea)\n\t\t{\t// doors may legally straggle two areas,\n\t\t\t// but nothing should ever need more than that\n\t\t\tif (ent->areanum != nullarea && ent->areanum != area)\n\t\t\t\tent->areanum2 = area;\n\t\t\telse\n\t\t\t\tent->areanum = area;\n\t\t}\n\t}\n\n\tif (num_leafs >= MAX_TOTAL_ENT_LEAFS)\n\t{\t// assume we missed some leafs, and mark by headnode\n\t\tent->num_leafs = -1;\n\t\tent->headnode = topnode;\n\t}\n\telse\n\t{\n\t\tent->num_leafs = 0;\n\t\tfor (i=0 ; i<num_leafs ; i++)\n\t\t{\n\t\t\tif (clusters[i] == -1)\n\t\t\t\tcontinue;\t\t// not a visible leaf\n\t\t\tfor (j=0 ; j<i ; j++)\n\t\t\t\tif (clusters[j] == clusters[i])\n\t\t\t\t\tbreak;\n\t\t\tif (j == i)\n\t\t\t{\n\t\t\t\tif (ent->num_leafs == MAX_ENT_LEAFS)\n\t\t\t\t{\t// assume we missed some leafs, and mark by headnode\n\t\t\t\t\tent->num_leafs = -1;\n\t\t\t\t\tent->headnode = topnode;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tent->leafnums[ent->num_leafs++] = clusters[i];\n\t\t\t}\n\t\t}\n\t}\n}\n\n/*\n===============================================================================\n\nBOX TRACING\n\n===============================================================================\n*/\n\nstatic void VBSP_FinalizeBrush(q2cbrush_t *brush)\n{\n\tvecV_t verts[256];\n\tvec4_t planes[256];\n\tint i, j;\n\tClearBounds(brush->absmins, brush->absmaxs);\n\tfor (i = 0; i < brush->numsides; i++)\n\t{\n\t\tVectorCopy(brush->brushside[i].plane->normal, planes[i]);\n\t\tplanes[i][3] = brush->brushside[i].plane->dist;\n\t}\n\tfor (i = 0; i < brush->numsides; i++)\n\t{\n\t\t//most brushes are axial, which can save some a little loadtime\n\t\tif (planes[i][0] == 1)\n\t\t\tbrush->absmaxs[0] = planes[i][3];\n\t\telse if (planes[i][1] == 1)\n\t\t\tbrush->absmaxs[1] = planes[i][3];\n\t\telse if (planes[i][2] == 1)\n\t\t\tbrush->absmaxs[2] = planes[i][3];\n\t\telse if (planes[i][0] == -1)\n\t\t\tbrush->absmins[0] = -planes[i][3];\n\t\telse if (planes[i][1] == -1)\n\t\t\tbrush->absmins[1] = -planes[i][3];\n\t\telse if (planes[i][2] == -1)\n\t\t\tbrush->absmins[2] = -planes[i][3];\n\t\telse\n\t\t{\n\t\t\tj = modfuncs->ClipPlaneToBrush(verts, countof(verts), planes, sizeof(planes[0]), brush->numsides, planes[i]);\n\t\t\twhile (j-- > 0)\n\t\t\t\tAddPointToBounds(verts[j], brush->absmins, brush->absmaxs);\n\t\t}\n\t}\n}\n\n/*\n===============================================================================\n\nAREAPORTALS\n\n===============================================================================\n*/\n\nstatic void FloodArea_r (vbspinfo_t\t*prv, size_t areaidx, int floodnum)\n{\n\tsize_t\t\ti;\n\n\tcareaflood_t *flood = &prv->areaflood[areaidx];\n\tif (flood->floodvalid == prv->floodvalid)\n\t{\n\t\tif (flood->floodnum == floodnum)\n\t\t\treturn;\n\t\tCon_Printf (\"FloodArea_r: reflooded\\n\");\n\t\treturn;\n\t}\n\n\tflood->floodnum = floodnum;\n\tflood->floodvalid = prv->floodvalid;\n\n\t{\n\t\tcarea_t *area = &prv->areas[areaidx];\n\t\tq2dareaportal_t\t*p = &prv->areaportals[area->firstareaportal];\n\t\tfor (i=0 ; i<area->numareaportals ; i++, p++)\n\t\t{\n\t\t\tif (prv->portalopen[p->portalnum])\n\t\t\t\tFloodArea_r (prv, p->otherarea, floodnum);\n\t\t}\n\t}\n}\n\n/*\n====================\nFloodAreaConnections\n\n\n====================\n*/\nstatic void\tFloodAreaConnections (vbspinfo_t\t*prv)\n{\n\tsize_t\t\ti;\n\tint\t\tfloodnum;\n\n\t// all current floods are now invalid\n\tprv->floodvalid++;\n\tfloodnum = 0;\n\n\t// area 0 is not used\n\tfor (i=0 ; i<prv->numareas ; i++)\n\t{\n\t\tif (prv->areaflood[i].floodvalid == prv->floodvalid)\n\t\t\tcontinue;\t\t// already flooded into\n\t\tfloodnum++;\n\t\tFloodArea_r (prv, i, floodnum);\n\t}\n}\n\nstatic void\tVBSP_SetAreaPortalState (model_t *mod, unsigned int portalnum, unsigned int area1, unsigned int area2, qboolean open)\n{\n\tvbspinfo_t\t*prv;\n\tprv = (vbspinfo_t*)mod->meshinfo;\n\tif (portalnum > prv->numareaportals)\n\t\treturn;\n\tif (prv->portalopen[portalnum] == open)\n\t\treturn;\n\tprv->portalopen[portalnum] = open;\n\tFloodAreaConnections (prv);\n}\n\nstatic qboolean\tVBSP_AreasConnected (model_t *mod, unsigned int area1, unsigned int area2)\n{\n\tvbspinfo_t\t*prv = (vbspinfo_t*)mod->meshinfo;\n\n\tif (map_noareas->value)\n\t\treturn true;\n\n\tif (area1 == ~0 || area2 == ~0)\n\t\treturn area1 == area2;\n\tif (area1 > prv->numareas || area2 > prv->numareas)\n\t\tHost_Error (\"area > numareas\");\n\n\tif (prv->areaflood[area1].floodnum == prv->areaflood[area2].floodnum)\n\t\treturn true;\n\treturn false;\n}\n\n\n/*\n=================\nCM_WriteAreaBits\n\nWrites a length qbyte followed by a bit vector of all the areas\nthat area in the same flood as the area parameter\n\nThis is used by the client refreshes to cull visibility\n=================\n*/\nstatic int VBSP_WriteAreaBits (model_t *mod, qbyte *buffer, int area, qboolean merge)\n{\n\tvbspinfo_t\t*prv = (vbspinfo_t*)mod->meshinfo;\n\tint\t\ti;\n\tint\t\tfloodnum;\n\tint\t\tbytes;\n\tint\t\tnullarea = 0;\n\n\tbytes = (prv->numareas+7)>>3;\n\n\tif (map_noareas->value || (area == nullarea && !merge))\n\t{\t// for debugging, send everything\n\t\tif (!merge)\n\t\t\tmemset (buffer, 255, bytes);\n\t}\n\telse\n\t{\n\t\tif (!merge)\n\t\t\tmemset (buffer, 0, bytes);\n\n\t\tfloodnum = prv->areaflood[area].floodnum;\n\t\tfor (i=0 ; i<prv->numareas ; i++)\n\t\t{\n\t\t\tif (prv->areaflood[i].floodnum == floodnum)\n\t\t\t\tbuffer[i>>3] |= 1<<(i&7);\n\t\t}\n\t}\n\n\treturn bytes;\n}\n\n/*\n===================\nCM_WritePortalState\n\nReturns a size+pointer to the data that needs to be written into a saved game. \n===================\n*/\nstatic size_t VBSP_SaveAreaPortalBlob (model_t *mod, void **data)\n{\n\tvbspinfo_t\t*prv = (vbspinfo_t*)mod->meshinfo;\n\n\t*data = prv->portalopen;\n\treturn sizeof(prv->portalopen);\n}\n\n/*\n===================\nCM_ReadPortalState\n\nReads the portal state from a savegame file\nand recalculates the area connections\n===================\n*/\nstatic size_t VBSP_LoadAreaPortalBlob (model_t *mod, void *ptr, size_t ptrsize)\n{\n\tvbspinfo_t\t*prv = (vbspinfo_t*)mod->meshinfo;\n\n\tmemcpy(prv->portalopen, ptr, min(ptrsize,sizeof(prv->portalopen)));\n\n\tFloodAreaConnections (prv);\n\treturn sizeof(prv->portalopen);\n}\n\n\n/*\n===============================================================================\n\nPVS / PHS\n\n===============================================================================\n*/\n\n/*\n===================\nCM_DecompressVis\n===================\n*/\nstatic void VBSP_DecompressVis (model_t *mod, qbyte *in, qbyte *out, qboolean merge)\n{\n\tvbspinfo_t\t*prv = (vbspinfo_t*)mod->meshinfo;\n\tint\t\tc;\n\tqbyte\t*out_p;\n\tint\t\trow;\n\n\trow = (mod->numclusters+7)>>3;\n\tout_p = out;\n\n\tif (!in || !prv->numvisibility)\n\t{\t// no vis info, so make all visible\n\t\twhile (row)\n\t\t{\n\t\t\t*out_p++ = 0xff;\n\t\t\trow--;\n\t\t}\n\t\treturn;\n\t}\n\n\tif (merge)\n\t{\n\t\tdo\n\t\t{\n\t\t\tif (*in)\n\t\t\t{\n\t\t\t\t*out_p++ |= *in++;\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tout_p += in[1];\n\t\t\tin += 2;\n\t\t} while (out_p - out < row);\n\t}\n\telse\n\t{\n\t\tdo\n\t\t{\n\t\t\tif (*in)\n\t\t\t{\n\t\t\t\t*out_p++ = *in++;\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tc = in[1];\n\t\t\tin += 2;\n\t\t\tif ((out_p - out) + c > row)\n\t\t\t{\n\t\t\t\tc = row - (out_p - out);\n\t\t\t\tCon_DPrintf (\"warning: Vis decompression overrun\\n\");\n\t\t\t}\n\t\t\twhile (c)\n\t\t\t{\n\t\t\t\t*out_p++ = 0;\n\t\t\t\tc--;\n\t\t\t}\n\t\t} while (out_p - out < row);\n\t}\n}\n\nstatic pvsbuffer_t\tpvsrow;\nstatic pvsbuffer_t\tphsrow;\n\n\n\nstatic qbyte\t*VBSP_ClusterPVS (model_t *mod, int cluster, pvsbuffer_t *buffer, pvsmerge_t merge)\n{\n\tvbspinfo_t\t*prv = (vbspinfo_t*)mod->meshinfo;\n\tif (!buffer)\n\t\tbuffer = &pvsrow;\n\tif (buffer->buffersize < mod->pvsbytes)\n\t\tbuffer->buffer = plugfuncs->Realloc(buffer->buffer, buffer->buffersize=mod->pvsbytes);\n\n\tif (cluster == -1)\n\t\tmemset (buffer->buffer, 0, (mod->numclusters+7)>>3);\n\telse\n\t\tVBSP_DecompressVis (mod, ((qbyte*)prv->vis) + prv->vis->bitofs[cluster][DVIS_PVS], buffer->buffer, merge==PVM_MERGE);\n\treturn buffer->buffer;\n}\n\nstatic qbyte\t*VBSP_ClusterPHS (model_t *mod, int cluster, pvsbuffer_t *buffer)\n{\n\tvbspinfo_t\t*prv = (vbspinfo_t*)mod->meshinfo;\n\n\tif (!buffer)\n\t\tbuffer = &phsrow;\n\tif (buffer->buffersize < mod->pvsbytes)\n\t\tbuffer->buffer = plugfuncs->Realloc(buffer->buffer, buffer->buffersize=mod->pvsbytes);\n\n\tif (cluster == -1)\n\t\tmemset (buffer->buffer, 0, (mod->numclusters+7)>>3);\n\telse\n\t\tVBSP_DecompressVis (mod, ((qbyte*)prv->vis) + prv->vis->bitofs[cluster][DVIS_PHS], buffer->buffer, false);\n\treturn buffer->buffer;\n}\n\nstatic unsigned int  VBSP_FatPVS (model_t *mod, const vec3_t org, pvsbuffer_t *result, qboolean merge)\n{\n\tint\tleafs[64];\n\tint\t\ti, j, count;\n\tvec3_t\tmins, maxs;\n\n\tfor (i=0 ; i<3 ; i++)\n\t{\n\t\tmins[i] = org[i] - 8;\n\t\tmaxs[i] = org[i] + 8;\n\t}\n\n\tcount = VBSP_BoxLeafnums (mod, mins, maxs, leafs, countof(leafs), NULL);\n\tif (count < 1)\n\t\tSys_Errorf (\"SV_Q2FatPVS: count < 1\");\n\n\t// convert leafs to clusters\n\tfor (i=0 ; i<count ; i++)\n\t\tleafs[i] = VBSP_LeafCluster(mod, leafs[i]);\n\n\t//grow the buffer if needed\n\tif (result->buffersize < mod->pvsbytes)\n\t\tresult->buffer = plugfuncs->Realloc(result->buffer, result->buffersize=mod->pvsbytes);\n\n\tif (count == 1 && leafs[0] == -1)\n\t{\t//if the only leaf is the outside then broadcast it.\n\t\tmemset(result->buffer, 0xff, mod->pvsbytes);\n\t\ti = count;\n\t}\n\telse\n\t{\n\t\ti = 0;\n\t\tif (!merge)\n\t\t\tmod->funcs.ClusterPVS(mod, leafs[i++], result, PVM_REPLACE);\n\t\t// or in all the other leaf bits\n\t\tfor ( ; i<count ; i++)\n\t\t{\n\t\t\tfor (j=0 ; j<i ; j++)\n\t\t\t\tif (leafs[i] == leafs[j])\n\t\t\t\t\tbreak;\n\t\t\tif (j != i)\n\t\t\t\tcontinue;\t\t// already have the cluster we want\n\t\t\tmod->funcs.ClusterPVS(mod, leafs[i], result, PVM_MERGE);\n\t\t}\n\t}\n\treturn mod->pvsbytes;\n}\n\n/*\n=============\nCM_HeadnodeVisible\n\nReturns true if any leaf under headnode has a cluster that\nis potentially visible\n=============\n*/\nstatic qboolean VBSP_HeadnodeVisible (model_t *mod, int nodenum, const qbyte *visbits)\n{\n\tint\t\tleafnum;\n\tint\t\tcluster;\n\tmnode_t\t*node;\n\n\tif (nodenum < 0)\n\t{\n\t\tleafnum = -1-nodenum;\n\t\tcluster = mod->leafs[leafnum].cluster;\n\t\tif (cluster == -1)\n\t\t\treturn false;\n\t\tif (visbits[cluster>>3] & (1<<(cluster&7)))\n\t\t\treturn true;\n\t\treturn false;\n\t}\n\n\tnode = &mod->nodes[nodenum];\n\tif (VBSP_HeadnodeVisible(mod, node->childnum[0], visbits))\n\t\treturn true;\n\treturn VBSP_HeadnodeVisible(mod, node->childnum[1], visbits);\n}\n\nstatic qboolean VBSP_EdictInFatPVS(model_t *mod, const pvscache_t *ent, const qbyte *pvs, const int *areas)\n{\n\tint i,l;\n\tint nullarea = 0;\n\tif (areas)\n\t{\n\t\tfor (i = 1; ; i++)\n\t\t{\n\t\t\tif (i > areas[0])\n\t\t\t\treturn false;\t//none of the camera's areas could see the entity\n\t\t\tif (areas[i] == ent->areanum)\n\t\t\t{\n\t\t\t\tif (areas[i] != nullarea)\n\t\t\t\t\tbreak;\n\t\t\t\t//else entity is fully outside the world, invisible to all...\n\t\t\t}\n\t\t\telse if (VBSP_AreasConnected (mod, areas[i], ent->areanum))\n\t\t\t\tbreak;\n\t\t\t// doors can legally straddle two areas, so\n\t\t\t// we may need to check another one\n\t\t\telse if (ent->areanum2 != nullarea && VBSP_AreasConnected (mod, areas[i], ent->areanum2))\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\tif (ent->num_leafs == -1)\n\t{\t// too many leafs for individual check, go by headnode\n\t\tif (!VBSP_HeadnodeVisible (mod, ent->headnode, pvs))\n\t\t\treturn false;\n\t}\n\telse\n\t{\t// check individual leafs\n\t\tfor (i=0 ; i < ent->num_leafs ; i++)\n\t\t{\n\t\t\tl = ent->leafnums[i];\n\t\t\tif (pvs[l >> 3] & (1 << (l&7) ))\n\t\t\t\tbreak;\n\t\t}\n\t\tif (i == ent->num_leafs)\n\t\t\treturn false;\t\t// not visible\n\t}\n\treturn true;\n}\n\n\n/*\n===============================================================================\n\nCollision Stuff.\n\n===============================================================================\n*/\n\nstatic void VBSP_BuildBIHSubmodel(model_t *mod, int submodel)\n{\n\tvbspinfo_t\t*prv = (vbspinfo_t*)mod->meshinfo;\n\tcmodel_t\t*sub = &prv->cmodels[submodel];\n\n\tstruct bihleaf_s *bihleaf, *l;\n\tsize_t i;\n\n\tbihleaf = l = plugfuncs->Malloc(sizeof(*bihleaf)*sub->num_brushes);\n\tfor (i = 0; i < sub->num_brushes; i++)\n\t{\n\t\tq2cbrush_t *b = &prv->brushes[sub->firstbrush+i];\n\t\tl->type = BIH_BRUSH;\n\t\tl->data.brush = b;\n\n\t\tl->data.contents = b->contents;\n\t\tVectorCopy(b->absmins, l->mins);\n\t\tVectorCopy(b->absmaxs, l->maxs);\n\t\tl++;\n\t}\n\n\tmodfuncs->BIH_Build(mod, bihleaf, l-bihleaf);\n\tplugfuncs->Free(bihleaf);\n}\nstatic void VBSP_BuildBIHMain(void *ctx, void *unusedp, size_t unuseda, size_t unusedb)\n{\t//NOTE: must be on main thread because we're waiting for submodels for their size.\n\tmodel_t\t\t*mod = ctx;\n\tvbspinfo_t\t*prv = (vbspinfo_t*)mod->meshinfo;\n\tcmodel_t\t*sub = &prv->cmodels[0];\n\n\tstruct bihleaf_s *bihleaf, *l;\n\tsize_t bihleafs, i;\n\n\tbihleafs = sub->num_brushes;\n\tfor (i = 0; i < prv->numdisplacements; i++)\n\t\tbihleafs += prv->displacements[i].numindexes/3;\n\tfor (i = 0; i < prv->numstaticprops; i++)\n\t{\n\t\tif (prv->staticprops[i].solid)\n\t\t{\n\t\t\tif (prv->staticprops[i].ent.model && prv->staticprops[i].ent.model->loadstate == MLS_NOTLOADED)\n\t\t\t\tmodfuncs->GetModel(prv->staticprops[i].ent.model->publicname, MLV_WARN);\t//we use threads, so these'll load in time.\n\t\t\tbihleafs++;\n\t\t}\n\t}\n\n\tbihleaf = l = plugfuncs->Malloc(sizeof(*bihleaf)*bihleafs);\n\n\t//now we have enough storage, spit them out providing bounds info.\n\tfor (i = 0; i < sub->num_brushes; i++)\n\t{\n\t\tq2cbrush_t *b = &prv->brushes[sub->firstbrush+i];\n\t\tl->type = BIH_BRUSH;\n\t\tl->data.brush = b;\n\n\t\tl->data.contents = b->contents;\n\t\tVectorCopy(b->absmins, l->mins);\n\t\tVectorCopy(b->absmaxs, l->maxs);\n\t\tl++;\n\t}\n\tfor (i = 0; i < prv->numdisplacements; i++)\n\t{\n\t\tdispinfo_t *d = &prv->displacements[i];\n\n\t\tsize_t j;\n\t\tfor (j = 0; j < d->numindexes; j+=3)\n\t\t{\n\t\t\tindex_t *v = d->idx+j;\n\t\t\tvec_t *v1 = d->xyz[v[0]], *v2 = d->xyz[v[1]], *v3 = d->xyz[v[2]];\n\n\t\t\tl->type = BIH_TRIANGLE;\n\t\t\tl->data.tri.xyz = d->xyz;\n\t\t\tl->data.tri.indexes = v;\n\n\t\t\tl->data.contents = d->contents;\n\t\t\tVectorCopy(v1, l->mins);\n\t\t\tVectorCopy(v1, l->maxs);\n\t\t\tAddPointToBounds(v2, l->mins, l->maxs);\n\t\t\tAddPointToBounds(v3, l->mins, l->maxs);\n\t\t\tl++;\n\t\t}\n\t}\n\tfor (i = 0; i < prv->numstaticprops; i++)\n\t{\n\t\tif (!prv->staticprops[i].solid)\n\t\t\tcontinue;\n\n\t\tl->type = BIH_MODEL;\n\t\tl->data.mesh.model = prv->staticprops[i].ent.model;\n\t\tl->data.mesh.tr = &prv->staticprops[i].transform;\n\n\t\t//*cry*\n\t\twhile (l->data.mesh.model->loadstate==MLS_LOADING)\n\t\t\tthreadfuncs->WaitForCompletion(l->data.mesh.model, &l->data.mesh.model->loadstate, MLS_LOADING);\n\n\t\tif (!l->data.mesh.model || !l->data.mesh.model->funcs.NativeTrace || !l->data.mesh.model->funcs.NativeContents)\n\t\t{\n\t\t\tCon_Printf(\"%s has no collision info and must not be used as a solid static prop\\n\", l->data.mesh.model->name);\n\t\t\tcontinue;\n\t\t}\n\n\t\tl->data.contents = ~0u; //yuck!\n\n\t\t//a clever person could probably do a better job.\n\t\tl->mins[0] = prv->staticprops[i].ent.origin[0] - l->data.mesh.model->radius;\n\t\tl->mins[1] = prv->staticprops[i].ent.origin[1] - l->data.mesh.model->radius;\n\t\tl->mins[2] = prv->staticprops[i].ent.origin[2] - l->data.mesh.model->radius;\n\t\tl->maxs[0] = prv->staticprops[i].ent.origin[0] + l->data.mesh.model->radius;\n\t\tl->maxs[1] = prv->staticprops[i].ent.origin[1] + l->data.mesh.model->radius;\n\t\tl->maxs[2] = prv->staticprops[i].ent.origin[2] + l->data.mesh.model->radius;\n\t\tl++;\n\t}\n\n\tmodfuncs->BIH_Build(mod, bihleaf, l-bihleaf);\n\tplugfuncs->Free(bihleaf);\n\n\tmod->funcs.PointContents\t\t= VBSP_PointContents;\n}\n\n/*\n===============================================================================\n\nRendering stuff\n\n===============================================================================\n*/\n\n#ifdef HAVE_CLIENT\nstatic qboolean VBSP_CullBox (vec3_t mins, vec3_t maxs)\n{\n\t//this isn't very precise.\n\t//checking each plane individually can be problematic\n\t//if you have a large object behind the view, it can cross multiple planes, and be infront of each one at some point, yet should still be outside the view.\n\t//this is quite noticable with terrain where the potential height of a section is essentually infinite.\n\t//note that this is not a concern for spheres, just boxes.\n\tint\t\ti;\n\n\tfor (i = 0; i < refdef->frustum_numplanes; i++)\n\t\tif (BOX_ON_PLANE_SIDE (mins, maxs, &refdef->frustum[i]) == 2)\n\t\t\treturn true;\n\treturn false;\n}\nstatic void VBSP_RecursiveWorldNode (model_t *model, mnode_t *node)\n{\n\tint\t\t\tc, side;\n\tmplane_t\t*plane;\n\tmsurface_t\t*surf, **mark;\n\tmleaf_t\t\t*pleaf;\n\tdouble\t\tdot;\n\n\tint sidebit;\n\n\tif (node->contents == FTECONTENTS_SOLID)\n\t\treturn;\t\t// solid\n\n\tif (node->visframe != vbsp_nodesequence)\n\t\treturn;\n\tif (VBSP_CullBox (node->minmaxs, node->minmaxs+3))\n\t\treturn;\n\n// if a leaf node, draw stuff\n\tif (node->contents != -1)\n\t{\n\t\tpleaf = (mleaf_t *)node;\n\n\t\t// check for door connected areas\n\t\tif (! (refdef->areabits[pleaf->area>>3] & (1<<(pleaf->area&7)) ) )\n\t\t\treturn;\t\t// not visible\n\n\t\tc = pleaf->cluster;\n\t\tif (c >= 0)\n\t\t\tfrustumvis[c>>3] |= 1<<(c&7);\n\n\t\tmark = pleaf->firstmarksurface;\n\t\tc = pleaf->nummarksurfaces;\n\n\t\tif (c)\n\t\t{\n\t\t\tdo\n\t\t\t{\n\t\t\t\tsurf = *mark++;\n\t\t\t\tif (surf->flags & SURF_OFFNODE)\n\t\t\t\t{\n\t\t\t\t\tif (surf->visframe != vbsp_surfsequence)\n\t\t\t\t\t{\t//only add once, it might be in multiple leafs.\n\t\t\t\t\t\tsurf->visframe = vbsp_surfsequence;\n\t\t\t\t\t\tmodfuncs->RenderDynamicLightmaps (surf);\n\t\t\t\t\t\tsurf->sbatch->mesh[surf->sbatch->meshes++] = surf->mesh;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tsurf->visframe = vbsp_surfsequence;\n\t\t\t} while (--c);\n\t\t}\n\t\treturn;\n\t}\n\n// node is just a decision point, so go down the apropriate sides\n\n// find which side of the node we are on\n\tplane = node->plane;\n\n\tswitch (plane->type)\n\t{\n\tcase PLANE_X:\n\t\tdot = modelorg[0] - plane->dist;\n\t\tbreak;\n\tcase PLANE_Y:\n\t\tdot = modelorg[1] - plane->dist;\n\t\tbreak;\n\tcase PLANE_Z:\n\t\tdot = modelorg[2] - plane->dist;\n\t\tbreak;\n\tdefault:\n\t\tdot = DotProduct (modelorg, plane->normal) - plane->dist;\n\t\tbreak;\n\t}\n\n\tif (dot >= 0)\n\t{\n\t\tside = 0;\n\t\tsidebit = 0;\n\t}\n\telse\n\t{\n\t\tside = 1;\n\t\tsidebit = SURF_PLANEBACK;\n\t}\n\n// recurse down the children, front side first\n\tVBSP_RecursiveWorldNode (model, node->children[side]);\n\n\t// draw stuff\n\tfor ( c = node->numsurfaces, surf = model->surfaces + node->firstsurface; c ; c--, surf++)\n\t{\n\t\tif (surf->visframe != vbsp_surfsequence)\n\t\t\tcontinue;\n\n\t\tif ( (surf->flags & SURF_PLANEBACK) != sidebit )\n\t\t\tcontinue;\t\t// wrong side\n\n\t\tsurf->visframe = 0;//vbsp_surfsequence;//-1;\n\n\t\tmodfuncs->RenderDynamicLightmaps (surf);\n\n\t\tsurf->sbatch->mesh[surf->sbatch->meshes++] = surf->mesh;\n\t}\n\n\n// recurse down the back side\n\tVBSP_RecursiveWorldNode (model, node->children[!side]);\n}\nstatic qbyte *VBSP_MarkLeaves (model_t *model, int clusters[2])\n{\n\tvbspinfo_t\t*prv = (vbspinfo_t*)model->meshinfo;\n\tmnode_t\t*node;\n\tint\t\ti;\n\n\tint cluster;\n\tmleaf_t\t*leaf;\n\tqbyte *vis;\n\n\tint portal = refdef->recurse;\n\n\tif (refdef->forcevis)\n\t{\n\t\tvis = refdef->forcedvis;\n\t\tprv->vcache.vis = NULL;\n\t}\n\telse if (portal || hl2_novis->ival || clusters[0] == -1 || !model->vis)\n\t\treturn NULL;\t//use some blind whole-model thing\n\telse\n\t{\n\t\tvis = prv->vcache.vis;\n\t\tif (prv->vcache.viewcluster[0] == clusters[0] && prv->vcache.viewcluster[1] == clusters[1] && vis)\n\t\t\treturn vis;\n\n\t\tif (clusters[1] != clusters[0])\t// may have to combine two clusters because of solid water boundaries\n\t\t{\n\t\t\tvis = VBSP_ClusterPVS (model, clusters[0], &prv->vcache.visbuf, PVM_REPLACE);\n\t\t\tvis = VBSP_ClusterPVS (model, clusters[1], &prv->vcache.visbuf, PVM_MERGE);\n\t\t}\n\t\telse\n\t\t\tvis = VBSP_ClusterPVS (model, clusters[0], &prv->vcache.visbuf, PVM_FAST);\n\t\tprv->vcache.vis = vis;\n\t\tprv->vcache.viewcluster[0] = clusters[0];\n\t\tprv->vcache.viewcluster[1] = clusters[1];\n\t}\n\n\tvbsp_nodesequence++;\n\n\tfor (i=0,leaf=model->leafs ; i<model->numleafs ; i++, leaf++)\n\t{\n\t\tcluster = leaf->cluster;\n\t\tif (cluster == -1)\n\t\t\tcontinue;\n\t\tif (vis[cluster>>3] & (1<<(cluster&7)))\n\t\t{\n\t\t\tnode = (mnode_t *)leaf;\n\t\t\tdo\n\t\t\t{\n\t\t\t\tif (node->visframe == vbsp_nodesequence)\n\t\t\t\t\tbreak;\n\t\t\t\tnode->visframe = vbsp_nodesequence;\n\t\t\t\tnode = node->parent;\n\t\t\t} while (node);\n\t\t}\n\t}\n\treturn vis;\n}\n\nqboolean HL2_CalcModelLighting(entity_t *e, model_t *clmodel, refdef_t *r_refdef, model_t *mod)\n{\n\tvec3_t lightdir;\n\tvec3_t shadelight, ambientlight;\n\n\te->light_dir[0] = 0; e->light_dir[1] = 1; e->light_dir[2] = 0;\n\tmod->funcs.LightPointValues(mod, e->origin, shadelight, ambientlight, lightdir);\n\n\te->light_dir[0] = DotProduct(lightdir, e->axis[0]);\n\te->light_dir[1] = DotProduct(lightdir, e->axis[1]);\n\te->light_dir[2] = DotProduct(lightdir, e->axis[2]);\n\n\tVectorNormalize(e->light_dir);\n\n\tshadelight[0] *= 1/255.0f;\n\tshadelight[1] *= 1/255.0f;\n\tshadelight[2] *= 1/255.0f;\n\tambientlight[0] *= 1/255.0f;\n\tambientlight[1] *= 1/255.0f;\n\tambientlight[2] *= 1/255.0f;\n\n\t//calculate average and range, to allow for negative lighting dotproducts\n\tVectorCopy(shadelight, e->light_avg);\n\tVectorCopy(ambientlight, e->light_range);\n\n\te->light_known = 1;\n\treturn e->light_known-1;\n}\n\nstatic void VBSP_PrepareFrame(model_t *mod, refdef_t *r_refdef, int area, int clusters[2], pvsbuffer_t *vis, qbyte **entvis_out, qbyte **surfvis_out)\n{\n\tvbspinfo_t\t*prv = (vbspinfo_t*)mod->meshinfo;\n\tqbyte *surfvis, *entvis;\n\n\trefdef = r_refdef;\n\n\tif (vis->buffersize < mod->pvsbytes)\n\t\tvis->buffer = plugfuncs->Realloc(vis->buffer, vis->buffersize=mod->pvsbytes);\n\tfrustumvis = vis->buffer;\n\tmemset(frustumvis, 0, mod->pvsbytes);\n\n\tif (!r_refdef->areabitsknown)\n\t{\t//generate the info each frame, as the gamecode didn't tell us what to use.\n\t\tint leafnum = VBSP_PointLeafnum (mod, r_refdef->vieworg);\n\t\tint clientarea = VBSP_LeafArea (mod, leafnum);\n\t\tVBSP_WriteAreaBits(mod, r_refdef->areabits, clientarea, false);\n\t\tr_refdef->areabitsknown = true;\n\t}\n\n\n\tentvis = surfvis = VBSP_MarkLeaves(mod, clusters);\n\tVectorCopy (r_refdef->vieworg, modelorg);\n\tif (!surfvis)\n\t{\n\t\tsize_t i;\n\t\tmsurface_t *surf;\n\t\tfor (i = 0; i < mod->nummodelsurfaces; i++)\n\t\t{\n\t\t\tsurf = &mod->surfaces[i];\n\t\t\tmodfuncs->RenderDynamicLightmaps (surf);\n\t\t\tsurf->sbatch->mesh[surf->sbatch->meshes++] = surf->mesh;\n\t\t}\n\t}\n\telse\n\t{\n\t\tsize_t i;\n\t\tdispinfo_t *disp;\n\t\tmsurface_t *surf;\n\t\tint areas[2];\n\t\tareas[0] = 1;\n\t\tareas[1] = area;\n\t\tvbsp_surfsequence++;\n\t\tVBSP_RecursiveWorldNode (mod, mod->nodes);\n\t\tfor (i = 0; i < prv->numdisplacements; i++)\n\t\t{\n\t\t\tdisp = &prv->displacements[i];\n\t\t\tif (VBSP_EdictInFatPVS(mod, &disp->pvs, surfvis, areas))\n\t\t\t{\n\t\t\t\tsurf = disp->surf;\n\t\t\t\tmodfuncs->RenderDynamicLightmaps (surf);\n\t\t\t\tsurf->sbatch->mesh[surf->sbatch->meshes++] = surf->mesh;\n\t\t\t}\n\t\t}\n\t}\n\n\t*surfvis_out = frustumvis;\n\t*entvis_out = entvis;\n\n\n\n\tif (prv->numstaticprops)\n\t{\n\t\tstruct staticprop_s *sent;\n\t\tentity_t *src, *ent;\n\t\tfloat d;\n\t\tsize_t i;\n\t\tvec3_t disp;\n\t\tfor (i = 0; i < prv->numstaticprops; i++)\n\t\t{\n\t\t\tsent = &prv->staticprops[i];\n\t\t\tsrc = &sent->ent;\n\n\t\t\tif (sent->fademaxdist)\n\t\t\t{\n\t\t\t\tVectorSubtract(refdef->vieworg, src->origin, disp);\n\t\t\t\td = VectorLength(disp);\n\t\t\t\tif (d > sent->fademaxdist)\n\t\t\t\t\tcontinue;\t//skip it.\n\t\t\t\td -= sent->fademindist;\n\t\t\t\td /= sent->fademaxdist-sent->fademindist;\n\t\t\t\tif (d < 0)\n\t\t\t\t\td = 0;\n\t\t\t}\n\t\t\telse\n\t\t\t\td = 0;\n\n\t\t\tif (!src->model || src->model->loadstate != MLS_LOADED)\n\t\t\t{\n\t\t\t\tif (src->model && src->model->loadstate == MLS_NOTLOADED)\n\t\t\t\t\tmodfuncs->GetModel(src->model->publicname, MLV_WARN);\t//we use threads, so these'll load in time.\n\t\t\t\tcontinue;\n\t\t\t}\n#if 1\n\t\t\tif (!src->light_known)\n\t\t\t{\n\t\t\t\tvec3_t tmp;\n\t\t\t\tVectorCopy(src->origin, tmp);\n\t\t\t\tVectorCopy(sent->lightorg, src->origin);\n\t\t\t\tHL2_CalcModelLighting(src, src->model, r_refdef, mod);\n\t\t\t\tVectorCopy(tmp, src->origin);\n\n\t\t\t\t/* HACK: If it's dark, might as well sample the model pos directly. */\n\t\t\t\tif (VectorLength(src->light_range) < 0.25)\n\t\t\t\t{\n\t\t\t\t\tHL2_CalcModelLighting(src, src->model, r_refdef, mod);\n\t\t\t\t}\n\t\t\t}\n#endif\n\t\t\tif (src->pvscache.num_leafs==-2)\n\t\t\t{\n\t\t\t\tvec3_t absmin, absmax;\n\t\t\t\tfloat r = src->model->radius;\n\t\t\t\tVectorSet(absmin, -r,-r,-r);\n\t\t\t\tVectorSet(absmax, r,r,r);\n\t\t\t\tVectorAdd(absmin, src->origin, absmin);\n\t\t\t\tVectorAdd(absmax, src->origin, absmax);\n\t\t\t\tVBSP_FindTouchedLeafs(mod, &src->pvscache, absmin, absmax);\n\t\t\t}\n\n\t\t\tent = modfuncs->NewSceneEntity();\n\t\t\tif (!ent)\n\t\t\t\treturn;\n\t\t\t*ent = *src;\n\t\t\tent->framestate.g[FS_REG].frametime[0] = refdef->time;\n\t\t\tent->framestate.g[FS_REG].frametime[1] = refdef->time;\n\t\t\tif (d)\n\t\t\t{\n\t\t\t\tent->shaderRGBAf[3] *= 1-d;\n\t\t\t\tent->flags |= RF_TRANSLUCENT;\n\t\t\t}\n\t\t}\n\t}\n}\nstatic void VBSP_InfoForPoint (struct model_s *mod, vec3_t pos, int *area, int *cluster, unsigned int *contentbits)\n{\n\tint leaf = VBSP_PointLeafnum_r (mod, pos, 0);\n\t*area = VBSP_LeafArea(mod, leaf);\n\t*cluster = VBSP_LeafCluster(mod, leaf);\n\t*contentbits = VBSP_LeafContents(mod, leaf);\n}\n\n\n#ifdef RTLIGHTS\nstatic int vbsp_shadowsequence;\nstatic qbyte *shadowedpvs;\nstatic model_t *shadowmodel;\nstatic void VBSP_WalkShadows (dlight_t *dl, void (*callback)(msurface_t *surf), mnode_t *node)\n{\n\tint\t\t\tc, side;\n\tmplane_t\t*plane;\n\tmsurface_t\t*surf, **mark;\n\tmleaf_t\t\t*pleaf;\n\tdouble\t\tdot;\n\n\tfloat\t\tl, maxdist;\n\tint\t\t\tj, s, t;\n\tvec3_t\t\timpact;\n\tstruct vbsptexinfo_s *vtexinfo;\n\tvbspinfo_t *prv;\n\n\tif (node->shadowframe != vbsp_shadowsequence)\n\t\treturn;\n\n\t//if light areabox is outside node, ignore node + children\n\tfor (c = 0; c < 3; c++)\n\t{\n\t\tif (dl->origin[c] + dl->radius < node->minmaxs[c])\n\t\t\treturn;\n\t\tif (dl->origin[c] - dl->radius > node->minmaxs[3+c])\n\t\t\treturn;\n\t}\n\n// if a leaf node, draw stuff\n\tif (node->contents != -1)\n\t{\n\t\tpleaf = (mleaf_t *)node;\n\n\t\tif (pleaf->cluster >= 0)\n\t\t\tshadowedpvs[pleaf->cluster>>3] |= 1<<(pleaf->cluster&7);\n\n\t\tmark = pleaf->firstmarksurface;\n\t\tc = pleaf->nummarksurfaces;\n\n\t\tif (c)\n\t\t{\n\t\t\tdo\n\t\t\t{\n\t\t\t\tsurf = *mark++;\n\t\t\t\tif (surf->flags & SURF_OFFNODE)\n\t\t\t\t{\n\t\t\t\t\tif (surf->shadowframe != vbsp_shadowsequence)\n\t\t\t\t\t{\t//if its not on a node then its probably not a nice flat surface, so don't bother trying to cull it in fancy ways that depend on its plane.\n\t\t\t\t\t\tsurf->shadowframe = vbsp_shadowsequence;\n\t\t\t\t\t\tcallback(surf);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tsurf->shadowframe = vbsp_shadowsequence;\n\t\t\t} while (--c);\n\t\t}\n\t\treturn;\n\t}\n\n// node is just a decision point, so go down the apropriate sides\n\n// find which side of the node we are on\n\tplane = node->plane;\n\n\tswitch (plane->type)\n\t{\n\tcase PLANE_X:\n\t\tdot = dl->origin[0] - plane->dist;\n\t\tbreak;\n\tcase PLANE_Y:\n\t\tdot = dl->origin[1] - plane->dist;\n\t\tbreak;\n\tcase PLANE_Z:\n\t\tdot = dl->origin[2] - plane->dist;\n\t\tbreak;\n\tdefault:\n\t\tdot = DotProduct (dl->origin, plane->normal) - plane->dist;\n\t\tbreak;\n\t}\n\n\tif (dot >= 0)\n\t\tside = 0;\n\telse\n\t\tside = 1;\n\n// recurse down the children, front side first\n\tVBSP_WalkShadows (dl, callback, node->children[side]);\n\n// draw stuff\n  \tc = node->numsurfaces;\n\tif (c)\n\t{\n\t\tprv = shadowmodel->meshinfo;\n\t\tsurf = shadowmodel->surfaces + node->firstsurface;\n\t\tmaxdist = dl->radius*dl->radius;\n\t\tfor ( ; c ; c--, surf++)\n\t\t{\n\t\t\tif (surf->shadowframe != vbsp_shadowsequence)\n\t\t\t\tcontinue;\n\n\t\t\tif ((dot < 0) ^ !!(surf->flags & SURF_PLANEBACK))\n\t\t\t\tcontinue;\t\t// wrong side\n\n\t\t\t/*if (surf->flags & (SURF_DRAWALPHA | SURF_DRAWTILED))\n\t\t\t{\t// no shadows\n\t\t\t\tcontinue;\n\t\t\t}*/\n\n\t\t\t//is the light on the right side?\n\t\t\tif (surf->flags & SURF_PLANEBACK)\n\t\t\t{//inverted normal.\n\t\t\t\tif (-DotProduct(surf->plane->normal, dl->origin)+surf->plane->dist >= dl->radius)\n\t\t\t\t\tcontinue;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (DotProduct(surf->plane->normal, dl->origin)-surf->plane->dist >= dl->radius)\n\t\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t//Yeah, you can blame LordHavoc for this alternate code here.\n\t\t\tfor (j=0 ; j<3 ; j++)\n\t\t\t\timpact[j] = dl->origin[j] - surf->plane->normal[j]*dot;\n\n\t\t\tvtexinfo = &prv->texinfo[surf->texinfo-shadowmodel->texinfo];\n\t\t\t// clamp center of light to corner and check brightness\n\t\t\tl = DotProduct (impact, vtexinfo->lmvecs[0]) + vtexinfo->lmvecs[0][3] - surf->texturemins[0];\n\t\t\ts = l;if (s < 0) s = 0;else if (s > surf->extents[0]) s = surf->extents[0];\n\t\t\ts = (l - s)*surf->texinfo->vecscale[0];\n\t\t\tl = DotProduct (impact, vtexinfo->lmvecs[1]) + vtexinfo->lmvecs[1][3] - surf->texturemins[1];\n\t\t\tt = l;if (t < 0) t = 0;else if (t > surf->extents[1]) t = surf->extents[1];\n\t\t\tt = (l - t)*surf->texinfo->vecscale[1];\n\t\t\t// compare to minimum light\n\t\t\tif ((s*s+t*t+dot*dot) < maxdist)\n\t\t\t\tcallback(surf);\n\t\t}\n\t}\n\n// recurse down the back side\n\tVBSP_WalkShadows (dl, callback, node->children[!side]);\n}\n\nstatic void VBSP_MarkShadows(model_t *model, dlight_t *dl, const qbyte *lvis)\n{\n\tmnode_t *node;\n\tint i;\n\tmleaf_t *leaf;\n\tint cluster;\n\n//\tif (!dl->die)\n\t{\n\t\t//static\n\t\t//variation on mark leaves\n\t\tfor (i=0,leaf=model->leafs ; i<model->numleafs ; i++, leaf++)\n\t\t{\n\t\t\tcluster = leaf->cluster;\n\t\t\tif (cluster == -1)\n\t\t\t\tcontinue;\n\t\t\tif (lvis[cluster>>3] & (1<<(cluster&7)))\n\t\t\t{\n\t\t\t\tnode = (mnode_t *)leaf;\n\t\t\t\tdo\n\t\t\t\t{\n\t\t\t\t\tif (node->shadowframe == vbsp_shadowsequence)\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tnode->shadowframe = vbsp_shadowsequence;\n\t\t\t\t\tnode = node->parent;\n\t\t\t\t} while (node);\n\t\t\t}\n\t\t}\n\t}\n/*\telse\n\t{\n\t\t//dynamic lights will be discarded after this frame anyway, so only include leafs that are visible\n\t\t//variation on mark leaves\n\t\tfor (i=0,leaf=model->leafs ; i<model->numleafs ; i++, leaf++)\n\t\t{\n\t\t\tcluster = leaf->cluster;\n\t\t\tif (cluster == -1)\n\t\t\t\tcontinue;\n\t\t\tif (lvis[cluster>>3] & (1<<(cluster&7)))\n\t\t\t{\n\t\t\t\tnode = (mnode_t *)leaf;\n\t\t\t\tdo\n\t\t\t\t{\n\t\t\t\t\tif (node->shadowframe == vbsp_shadowsequence)\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tnode->shadowframe = vbsp_shadowsequence;\n\t\t\t\t\tnode = node->parent;\n\t\t\t\t} while (node);\n\t\t\t}\n\t\t}\n\t}*/\n}\nstatic void VBSP_GenerateShadowMesh(model_t *model, dlight_t *dl, const qbyte *lightvis, qbyte *litvis, void (*callback)(msurface_t *surf))\n{\n\tvbspinfo_t *prv = model->meshinfo;\n\tdispinfo_t *disp;\n\tint i;\n\t//globals are evil\n\tshadowmodel = model;\n\tshadowedpvs = litvis;\t//this is an output\n\tvbsp_shadowsequence++;\n\n\tVBSP_MarkShadows(model, dl, lightvis);\n\n\tVBSP_WalkShadows(dl, callback, model->nodes);\n\n\tfor (i = 0; i < prv->numdisplacements; i++)\n\t{\n\t\tdisp = &prv->displacements[i];\n\t\tif (VBSP_EdictInFatPVS(model, &disp->pvs, litvis, NULL))\n\t\t{\n\t\t\tcallback(disp->surf);\n\t\t}\n\t}\n}\n#endif\n\n\n\n\nstatic void VBSP_LoadLeafLight (model_t *mod, qbyte *mod_base, vlump_t *hdridx, vlump_t *ldridx, vlump_t *hdrvals, vlump_t *ldrvals, int version)\n{\n\tvbspinfo_t\t*prv = (vbspinfo_t*)mod->meshinfo;\n\tvlump_t *lump_idx, *lump_vals;\n\tstruct leaflightpoint_s *point;\n\tsize_t i, j;\n\tunsigned short *in;\n\tqbyte *inpoint;\n\n\tif (version == 17 || version == 19)\n\t\treturn; //nope. this info is in the leafs.\n\n\tif (hdridx && hdrvals)\n\t\tlump_idx = hdridx, lump_vals = hdrvals;\n\telse if (ldridx && ldrvals)\n\t\tlump_idx = ldridx, lump_vals = ldrvals;\n\telse\n\t\treturn;\t//unsupported.\n\n\tif (lump_vals->filelen%(7*4))\n\t\treturn;\n\n\tif (lump_idx->filelen != mod->numleafs*sizeof(short)*2)\n\t\treturn;\t//erk?\n\n\t//easy enough to load some of the data...\n\tpoint = plugfuncs->GMalloc(&mod->memgroup, sizeof(*point)*(lump_vals->filelen/(7*4)));\n\tinpoint = mod_base + lump_vals->fileofs;\n\tfor (i = 0; i < lump_vals->filelen/(7*4); i++)\n\t{\n\t\tfor (j = 0; j < 6; j++, inpoint+=4)\n\t\t{\n\t\t\tfloat e = pow(2, (signed char)inpoint[3]);\n\t\t\tpoint[i].rgb[j][0] = e * inpoint[0];\n\t\t\tpoint[i].rgb[j][1] = e * inpoint[1];\n\t\t\tpoint[i].rgb[j][2] = e * inpoint[2];\n\t\t}\n\t\tpoint[i].x = *inpoint++;\n\t\tpoint[i].y = *inpoint++;\n\t\tpoint[i].z = *inpoint++;\n\t\tinpoint++;\n\t}\n\n\tprv->leaflight = plugfuncs->GMalloc(&mod->memgroup, sizeof(*prv->leaflight)*mod->numleafs);\n\tin = (unsigned short*)(mod_base + lump_idx->fileofs);\n\tfor (i = 0; i < mod->numleafs; i++)\n\t{\n\t\tprv->leaflight[i].count = LittleShort(*in++);\n\t\tprv->leaflight[i].point = point + (unsigned short)LittleShort(*in++);\n\t}\n}\nstatic void VBSP_LightPointValues\t(struct model_s *model, const vec3_t point, vec3_t res_diffuse, vec3_t res_ambient, vec3_t res_dir)\n{\n\tvbspinfo_t\t*prv = (vbspinfo_t*)model->meshinfo;\n\tint leafnum = VBSP_PointLeafnum(model,point);\n\tmleaf_t *leaf = model->leafs+leafnum;\n\tstruct mleaflight_s *leaflight = prv->leaflight+leafnum;\n\tstruct leaflightpoint_s *best, *lp;\n\tsize_t i, j, d, bd=~0;\n\tint xyz[3];\n\tvec3_t diff[6];\n\tfloat sig[6];\n\n\tstatic cvar_t *srgbmag, *scale, *forceface;\n\tif (!srgbmag)\tsrgbmag\t\t= cvarfuncs->GetNVFDG(\"hl2_lt_srgb_mag\",\"1\",\t0, \"TEST\", \"TEST\");\n\tif (!scale)\t\tscale\t\t= cvarfuncs->GetNVFDG(\"hl2_lt_scale\",\t\"256\",\t0, \"TEST\", \"TEST\");\n\tif (!forceface)\tforceface\t= cvarfuncs->GetNVFDG(\"hl2_lt_face\",\t\"-1\",\t0, \"TEST\", \"TEST\");\n\n\tif (prv->leaflight && leaflight->count)\n\t{\n\t\tfor (i = 0; i < 3; i++)\n\t\t\txyz[i] = 255*(point[i] - leaf->minmaxs[i]) / (leaf->minmaxs[3+i]-leaf->minmaxs[i]);\n\t\tfor (i = 0, best=lp = leaflight->point; i < leaflight->count; i++, lp++)\n\t\t{\n\t\t\tint m[3];\n\t\t\tm[0] = xyz[0] - lp->x;\n\t\t\tm[1] = xyz[1] - lp->y;\n\t\t\tm[2] = xyz[2] - lp->z;\n\t\t\td = DotProduct(m,m);\n\t\t\tif (bd > d)\n\t\t\t{\n\t\t\t\tbd = d;\n\t\t\t\tbest = lp;\n\t\t\t}\n\t\t}\n\n\n\t\tVectorClear(res_ambient);\n\t\tfor (j = 0; j < 6; j++)\n\t\t\tVectorAdd(res_ambient, best->rgb[j], res_ambient);\n\t\tVectorScale(res_ambient, 1.0/6, res_ambient);\n\n\t\t//try and figure out an average dir for the brightest direction\n\t\tfor (j = 0; j < 6; j++)\n\t\t{\n\t\t\tVectorSubtract(best->rgb[j], res_ambient, diff[j]);\n\t\t\tsig[j] = VectorLength(diff[j]);\n\t\t}\n\t\tfor (j = 0; j < 3; j++)\n\t\t\tres_dir[j] = sig[j*2+1] - sig[j*2];\n\t\tVectorNormalize(res_dir);\n\n\t\t//figure out how much light there should be in that direction.\n\t\tVectorCopy(res_ambient, res_diffuse);\n\t\tfor (j = 0; j < 3; j++)\n\t\t{\n\t\t\tif (res_dir[0]>=0)\n\t\t\t\tVectorMA(res_diffuse, res_dir[0], diff[j*2+1], res_diffuse);\n\t\t\telse\n\t\t\t\tVectorMA(res_diffuse, -res_dir[0], diff[j*2+0], res_diffuse);\n\t\t}\n\n\t\tif (forceface->ival >= 0)\n\t\t{\n\t\t\tVectorCopy(best->rgb[forceface->ival], res_diffuse);\n\t\t\tVectorCopy(best->rgb[forceface->ival], res_ambient);\n\t\t\tVectorClear(res_dir);\n\t\t\tres_dir[forceface->ival/3] = (forceface->ival&1)?-1:1;\n\t\t}\n\n\t\tif (srgbmag->value)\n\t\t{\n\t\t\tfor (j = 0; j < 3; j++)\n\t\t\t{\n\t\t\t\tres_diffuse[j] = M_LinearToSRGB(res_diffuse[j], srgbmag->value)*scale->value;\n\t\t\t\tres_ambient[j] = M_LinearToSRGB(res_ambient[j], srgbmag->value)*scale->value;\n\t\t\t}\n\n\t\t\t/*for (j = 0; j < 6; j++)\n\t\t\t{\n\t\t\t\tres_cube[j][0] = M_LinearToSRGB(best->rgb[j][0], srgbmag->value)*scale->value;\n\t\t\t\tres_cube[j][1] = M_LinearToSRGB(best->rgb[j][1], srgbmag->value)*scale->value;\n\t\t\t\tres_cube[j][2] = M_LinearToSRGB(best->rgb[j][2], srgbmag->value)*scale->value;\n\t\t\t}*/\n\t\t}\n\t\telse\n\t\t{\n\t\t\tVectorScale(res_diffuse, scale->value, res_diffuse);\n\t\t\tVectorScale(res_ambient, scale->value, res_ambient);\n\n\t\t\t/*for (j = 0; j < 6; j++)\n\t\t\t\tVectorScale(best->rgb[j], scale->value, res_cube[j]);*/\n\t\t}\n\n\t\treturn;\n\t}\n\tVectorSet(res_dir, 0,0.707,0.707);\n\tVectorSet(res_diffuse, 64,64,64);\n\tVectorSet(res_ambient, 192,192,192);\n}\n#else\nstatic void VBSP_LightPointValues\t(struct model_s *model, const vec3_t point, vec3_t res_diffuse, vec3_t res_ambient, vec3_t res_dir)\n{\n\tVectorSet(res_diffuse, 64,64,64);\n\tVectorSet(res_ambient, 192,192,192);\n\tVectorSet(res_dir, 0,0.707,0.707);\n}\nstatic void VBSP_LoadLeafLight (model_t *mod, qbyte *mod_base, vlump_t *hdridx, vlump_t *ldridx, vlump_t *hdrvals, vlump_t *ldrvals, int version)\n{\n}\n#endif\n\n\n\n\n\n\n\nstatic void VBSP_ComputeChecksum(model_t *mod, void *data, size_t length)\n{\n\tunsigned int checksum = LittleLong (filefuncs->BlockChecksum(data, length));\n\tmod->checksum = mod->checksum2 = checksum;\n}\n\nstatic qboolean VBSP_LoadModel(model_t *mod, qbyte *mod_base, size_t filelen, char *loadname)\n{\n\tdvbspheader_t *srcheader = (void*)mod_base;\n\tdvbspheader_t header;\n\tvbspinfo_t *prv = mod->meshinfo;\n\tsize_t i;\n\tqboolean noerrors = true;\n#ifdef HAVE_CLIENT\n\tqboolean haverenderer = qrenderer != QR_NONE;\n#endif\n\n\tVBSP_TranslateContentBits_Setup(prv);\n\n\tmod->lightmaps.width = LMBLOCK_SIZE_MAX;\n\tmod->lightmaps.height = LMBLOCK_SIZE_MAX;\n\n\tmod->fromgame = fg_new;\n\tmod->engineflags |= MDLF_NEEDOVERBRIGHT;\n\theader.version = LittleLong(srcheader->version);\n\tfor (i=0 ; i<HL2_MAXLUMPS ; i++)\n\t{\n\t\theader.lumps[i].filelen = LittleLong (srcheader->lumps[i].filelen);\n\t\theader.lumps[i].fileofs = LittleLong (srcheader->lumps[i].fileofs);\n\t\t//fixme: truncate lumps if they go off the end\n\t}\n\n\tif (header.lumps[VLUMP_ZIPFILE].filelen)\n\t\tmodfuncs->LoadMapArchive(mod, mod_base+header.lumps[VLUMP_ZIPFILE].fileofs, header.lumps[VLUMP_ZIPFILE].filelen);\n\n\t// load into heap\n\tnoerrors = noerrors && VBSP_LoadVertexes\t\t(mod, mod_base, &header.lumps[VLUMP_VERTEXES]);\n\tnoerrors = noerrors && VBSP_LoadEdges\t\t\t(mod, mod_base, &header.lumps[VLUMP_EDGES]);\n\tnoerrors = noerrors && VBSP_LoadSurfedges\t\t(mod, mod_base, &header.lumps[VLUMP_SURFEDGES]);\n#ifdef HAVE_CLIENT\n\tif (noerrors && haverenderer)\n\t\tVBSP_LoadLighting\t\t\t\t\t\t\t(mod, mod_base, &header.lumps[VLUMP_LIGHTING_LDR], &header.lumps[VLUMP_LIGHTING_HDR]);\n#endif\n\tnoerrors = noerrors && VBSP_LoadSurfaces\t\t(mod, mod_base, &header.lumps[VLUMP_TEXINFO]);\n\tnoerrors = noerrors && VBSP_LoadPlanes\t\t\t(mod, mod_base, &header.lumps[VLUMP_PLANES]);\n\tnoerrors = noerrors && VBSP_LoadTexInfo\t\t\t(mod, mod_base, header.lumps, loadname);\n\tif (noerrors)\n\t\tVBSP_LoadEntities\t\t\t\t\t\t\t(mod, mod_base, &header.lumps[VLUMP_ENTITIES]);\n\tnoerrors = noerrors && VBSP_LoadFaces\t\t\t(mod, mod_base, header.lumps, header.version);\n\tnoerrors = noerrors && VBSP_LoadDisplacements\t(mod, mod_base, header.lumps);\n\tnoerrors = noerrors && VBSP_LoadMarksurfaces\t(mod, mod_base, &header.lumps[VLUMP_LEAFFACES]);\n\tnoerrors = noerrors && VBSP_LoadVisibility\t\t(mod, mod_base, &header.lumps[VLUMP_VISIBILITY]);\n\tnoerrors = noerrors && VBSP_LoadBrushSides\t\t(mod, mod_base, &header.lumps[VLUMP_BRUSHSIDES]);\n\tnoerrors = noerrors && VBSP_LoadBrushes\t\t\t(mod, mod_base, &header.lumps[VLUMP_BRUSHES]);\n\tnoerrors = noerrors && VBSP_LoadLeafBrushes\t\t(mod, mod_base, &header.lumps[VLUMP_LEAFBRUSHES]);\n\tnoerrors = noerrors && VBSP_LoadLeafs\t\t\t(mod, mod_base, &header.lumps[VLUMP_LEAFS], header.version);\n\tnoerrors = noerrors && VBSP_LoadNodes\t\t\t(mod, mod_base, &header.lumps[VLUMP_NODES]);\n\tnoerrors = noerrors && VBSP_LoadSubmodels\t\t(mod, mod_base, &header.lumps[VLUMP_MODELS]);\n\tnoerrors = noerrors && VBSP_LoadAreas\t\t\t(mod, mod_base, &header.lumps[VLUMP_AREAS]);\n\tnoerrors = noerrors && VBSP_LoadAreaPortals\t\t(mod, mod_base, &header.lumps[VLUMP_AREAPORTALS], &header.lumps[VLUMP_AREAPORTALVERTS]);\n#ifdef HAVE_CLIENT\n\tif (noerrors && haverenderer)\n\t\tVBSP_LoadLeafLight\t\t\t\t\t\t\t(mod, mod_base, &header.lumps[VLUMP_LEAFLIGHTI_HDR], &header.lumps[VLUMP_LEAFLIGHTI_LDR],\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t&header.lumps[VLUMP_LEAFLIGHTV_HDR], &header.lumps[VLUMP_LEAFLIGHTV_LDR], header.version);\n#endif\n\tnoerrors = noerrors && VBSP_LoadGameLump\t\t(mod, mod_base, &header.lumps[VLUMP_GAMELUMP]);\n\n\tif (!noerrors)\n\t\treturn false;\n#ifdef HAVE_SERVER\n\tmod->funcs.FatPVS\t\t\t\t= VBSP_FatPVS;\n\tmod->funcs.EdictInFatPVS\t\t= VBSP_EdictInFatPVS;\n\tmod->funcs.FindTouchedLeafs\t\t= VBSP_FindTouchedLeafs;\n#endif\n#ifdef HAVE_CLIENT\n\tmod->funcs.LightPointValues\t\t= VBSP_LightPointValues;\n//\tmod->funcs.StainNode\t\t\t= VBSP_StainNode;\n//\tmod->funcs.MarkLights\t\t\t= VBSP_MarkLights;\n\tmod->funcs.GenerateShadowMesh\t= VBSP_GenerateShadowMesh;\n#endif\n\tmod->funcs.ClusterPVS\t\t\t= VBSP_ClusterPVS;\n\tmod->funcs.ClusterPHS\t\t\t= VBSP_ClusterPHS;\n\tmod->funcs.ClusterForPoint\t\t= VBSP_PointCluster;\n\n\tmod->funcs.SetAreaPortalState\t= VBSP_SetAreaPortalState;\n\tmod->funcs.AreasConnected\t\t= VBSP_AreasConnected;\n\tmod->funcs.LoadAreaPortalBlob\t= VBSP_LoadAreaPortalBlob;\n\tmod->funcs.SaveAreaPortalBlob\t= VBSP_SaveAreaPortalBlob;\n\n\tmod->funcs.PrepareFrame\t\t\t= VBSP_PrepareFrame;\n\tmod->funcs.InfoForPoint\t\t\t= VBSP_InfoForPoint;\n\n\t//displacements suck\n\tfor (i = 0; i < prv->numdisplacements; i++)\n\t\tVBSP_FindTouchedLeafs(mod, &prv->displacements[i].pvs, prv->displacements[i].aamin, prv->displacements[i].aamax);\n//\tif (noerrors)\n//\t\tCM_CreatePatchesForLeafs (mod, prv);\n\n\treturn true;\n}\n\n/*\n==================\nCM_LoadMap\n\nLoads in the map and all submodels\n==================\n*/\nstatic qboolean VBSP_LoadMap (model_t *mod, void *filein, size_t filelen)\n{\n\tunsigned\t\t*buf;\n\tint\t\t\t\ti;\n\tdvbspheader_t\theader;\n\tmodel_t\t\t\t*wmod = mod;\n\tchar\t\t\tloadname[32];\n\n#ifdef HAVE_CLIENT\n\tqbyte *facedata = NULL;\n\tunsigned int facesize = 0;\n#endif\n\tvbspinfo_t\t*prv;\n\n\tfilefuncs->FileBase (mod->name, loadname, sizeof(loadname));\n\n\t// free old stuff, just in case.\n\tmod->meshinfo = prv = plugfuncs->GMalloc(&mod->memgroup, sizeof(*prv));\n\n\tmod->type = mod_brush;\n\n\t//\n\t// load the file\n\t//\n\tbuf = (unsigned\t*)filein;\n\tif (!buf)\n\t{\n\t\tCon_Printf (CON_ERROR \"Couldn't load %s\\n\", mod->name);\n\t\treturn false;\n\t}\n\n\theader = *(dvbspheader_t *)(buf);\n\theader.magic = LittleLong(header.magic);\n\theader.version = LittleLong(header.version);\n\n\tClearBounds(mod->mins, mod->maxs);\n\n\tswitch(header.version)\n\t{\n\tcase 17:\t//vampire\n\tcase 18:\t//beta\n\tcase 19:\t//hl2,cs:s,hl2dm\n\tcase 20:\t//portal, l4d, hl2ep2\n\tcase 21:\t//cs:go, portal 2, l4d2\n\t//case 22:\t//dota 2\n\t//case 23:\t//dota 2\n\t//case 27:\t//'contagion'\n\t//case 29:\t//'titanfall'\n\t\tif (!VBSP_LoadModel(mod, filein, filelen, loadname))\n\t\t\treturn false;\n\t\tbreak;\n\tdefault:\n\t\tCon_Printf (CON_ERROR \"VBSP with unknown version (%s: %i should be 18, 19, 20, or 21)\\n\"\n\t\t\t, mod->name, header.version);\n\t\treturn false;\n\t}\n\n\tif (map_autoopenportals->value)\n\t\tmemset (prv->portalopen, 1, sizeof(prv->portalopen));\t//open them all. Used for progs that havn't got a clue.\n\telse\n\t\tmemset (prv->portalopen, 0, sizeof(prv->portalopen));\t//make them start closed.\n\tFloodAreaConnections (prv);\n\n\tmod->nummodelsurfaces = mod->numsurfaces;\n\tmemset(&mod->batches, 0, sizeof(mod->batches));\n\tmod->vbos = NULL;\n\n\tmod->numsubmodels = VBSP_NumInlineModels(mod);\n\n\t//in case anyone wants to know the typical player size...\n\tVectorSet(mod->hulls[1].clip_mins, -16,-16,-36);\n\tVectorSet(mod->hulls[1].clip_maxs, 16,16,36);\n\tmod->hulls[0].firstclipnode = prv->cmodels[0].headnode-mod->nodes;\n\tmod->rootnode = prv->cmodels[0].headnode;\n\tmod->nummodelsurfaces = prv->cmodels[0].numsurfaces;\n\n#ifdef HAVE_CLIENT\n\tif (qrenderer != QR_NONE)\n\t{\n\t\tbuilddata_t *bd = plugfuncs->Malloc(sizeof(*bd) + facesize*mod->nummodelsurfaces);\n\t\tbd->buildfunc = VBSP_BuildSurfMesh;\n\t\tbd->paintlightmaps = true;\n\t\tmemcpy(bd+1, facedata + mod->firstmodelsurface*facesize, facesize*mod->nummodelsurfaces);\n\t\tthreadfuncs->AddWork(WG_MAIN, VBSP_GenerateMaterials, mod, bd, 0, 0);\n\t}\n#endif\n\n\tfor (i=1 ; i< mod->numsubmodels ; i++)\n\t{\n\t\tcmodel_t\t*bm;\n\n\t\tchar\tname[MAX_QPATH];\n\n\t\tQ_snprintfz (name, sizeof(name), \"*%i:%s\", i, wmod->publicname);\n\t\tmod = modfuncs->BeginSubmodelLoad(name);\n\t\t*mod = *wmod;\n\t\tmod->archive = NULL;\n\t\tmod->entities_raw = NULL;\n\t\tmod->submodelof = wmod;\n\t\tQ_strncpyz(mod->publicname, name, sizeof(mod->publicname));\n\t\tQ_snprintfz (mod->name, sizeof(mod->name), \"*%i:%s\", i, wmod->name);\n\t\tmemset(&mod->memgroup, 0, sizeof(mod->memgroup));\n\n\t\tbm = VBSP_InlineModel (wmod, name);\n\n\t\tmod->hulls[0].firstclipnode = -1;\t//no nodes,\n\t\tif (bm->headleaf)\n\t\t{\n\t\t\tmod->leafs = bm->headleaf;\n\t\t\tmod->nodes = NULL;\n\t\t\tmod->hulls[0].firstclipnode = -1;\t//make it refer directly to the first leaf, for things that still use numbers.\n\t\t\tmod->rootnode = (mnode_t*)bm->headleaf;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tmod->leafs = wmod->leafs;\n\t\t\tmod->nodes = wmod->nodes;\n\t\t\tmod->hulls[0].firstclipnode = bm->headnode - mod->nodes;\t//determine the correct node index\n\t\t\tmod->rootnode = bm->headnode;\n\t\t}\n\t\tmod->nummodelsurfaces = bm->numsurfaces;\n\t\tmod->firstmodelsurface = bm->firstsurface;\n\n\t\tVBSP_BuildBIHSubmodel(mod, i);\n\n\t\tmemset(&mod->batches, 0, sizeof(mod->batches));\n\t\tmod->vbos = NULL;\n\n\t\tVectorCopy (bm->maxs, mod->maxs);\n\t\tVectorCopy (bm->mins, mod->mins);\n#ifdef HAVE_CLIENT\n\t\tmod->radius = RadiusFromBounds (mod->mins, mod->maxs);\n\n\t\tif (qrenderer != QR_NONE)\n\t\t{\n\t\t\tbuilddata_t *bd = plugfuncs->Malloc(sizeof(*bd) + facesize*mod->nummodelsurfaces);\n\t\t\tbd->buildfunc = VBSP_BuildSurfMesh;\n\t\t\tbd->paintlightmaps = true;\n\t\t\tmemcpy(bd+1, facedata + mod->firstmodelsurface*facesize, facesize*mod->nummodelsurfaces);\n\t\t\tthreadfuncs->AddWork(WG_MAIN, VBSP_GenerateMaterials, mod, bd, i, 0);\n\t\t}\n#endif\n\t\tmodfuncs->EndSubmodelLoad(mod, MLS_LOADED);\n\t}\n\n\t//urgh, we need to wait for models to load in order to get their sizes. that requires being on the main thread and the caller will think we're loaded on completion so we can't safely pingpong it back before generating the bih tree\n\tthreadfuncs->AddWork(WG_MAIN, VBSP_BuildBIHMain, wmod, NULL, 0, 0);\n\n\t//main thread should have a load of work to do now. worker thread should now be free to compute the hash before its finally marked as loaded and the temp file memory goes away.\n\tVBSP_ComputeChecksum(mod, filein, filelen);\n\treturn true;\n}\n\n\nqboolean VBSP_Init(void)\n{\n\tfilefuncs = plugfuncs->GetEngineInterface(plugfsfuncs_name, sizeof(*filefuncs));\n\tmodfuncs = plugfuncs->GetEngineInterface(plugmodfuncs_name, sizeof(*modfuncs));\n\tif (modfuncs && modfuncs->version != MODPLUGFUNCS_VERSION)\n\t\tmodfuncs = NULL;\n\tthreadfuncs = plugfuncs->GetEngineInterface(plugthreadfuncs_name, sizeof(*threadfuncs));\n\n\tif (modfuncs && filefuncs && threadfuncs)\n\t{\n\t\tmodfuncs->RegisterModelFormatMagic(\"Source (V)BSP\", \"VBSP\",4, VBSP_LoadMap);\n\n#define MAPOPTIONS \"Map Cvar Options\"\n\t\thl2_novis = cvarfuncs->GetNVFDG(\"r_novis\", \"0\", 0, \"Multiplier for how far displacements can move.\", MAPOPTIONS);\n\t\thl2_displacement_scale = cvarfuncs->GetNVFDG(\"hl2_displacement_scale\", \"1\", CVAR_RENDERERLATCH|CVAR_CHEAT, \"Multiplier for how far displacements can move.\", MAPOPTIONS);\n\t\thl2_favour_ldr = cvarfuncs->GetNVFDG(\"hl2_favour_ldr\", \"0\", CVAR_RENDERERLATCH|CVAR_CHEAT, \"Favour LDR data instead of HDR (when both are present).\", MAPOPTIONS);\n\t\tmap_noareas = cvarfuncs->GetNVFDG(\"map_noareas\", \"0\", 0, \"Ignore areaportals.\", MAPOPTIONS);\n\t\tmap_autoopenportals = cvarfuncs->GetNVFDG(\"map_autoopenportals\", \"0\", CVAR_RENDERERLATCH, \"When set to 1, force-opens all area portals. Normally these start closed and are opened by doors when they move, but this requires the gamecode to signal this.\", MAPOPTIONS);\n\t\thl2_contents_remap = cvarfuncs->GetNVFDG(\"hl2_contents_remap\",\n\t\t\t\"/*solid*/SOLID \"\n\t\t\t\"/*window*/WINDOW \"\n\t\t\t\"/*aux*/Q2AUX \"\n\t\t\t\"/*grate*/CLIP \"\t\t//would otherwise be LAVA\n\t\t\t\"/*slime*/SLIME \"\n\t\t\t\"/*water*/WATER \"\n\t\t\t\"/*mist*/Q2MIST \"\n\t\t\t\"/*opaque*/7 \"\n\t\t\t\"/*testfogvolume*/8 \"\n\t\t\t\"/*??*/9 \"\n\t\t\t\"/*??*/10 \"\n\t\t\t\"/*team1*/11 \"\n\t\t\t\"/*team2*/12 \"\n\t\t\t\"/*ignorenodrawopaque*/13 \"\n\t\t\t\"/*movable*/-1 \"\t//would otherwise be LADDER\n\t\t\t\"/*areaportal*/Q2AREAPORTAL \"\n\t\t\t\"/*playerclip*/PLAYERCLIP \"\n\t\t\t\"/*monsterclip*/MONSTERCLIP \"\n\t\t\t\"/*current_0*/Q2CURRENT_0 \"\n\t\t\t\"/*current_90*/Q2CURRENT_90 \"\n\t\t\t\"/*current_180*/Q2CURRENT_180 \"\n\t\t\t\"/*current_270*/Q2CURRENT_270 \"\n\t\t\t\"/*current_up*/Q2CURRENT_UP \"\n\t\t\t\"/*current_down*/Q2CURRENT_DOWN \"\n\t\t\t\"/*origin*/Q2ORIGIN \"\n\t\t\t\"/*monster*/BODY \"\n\t\t\t\"/*deadmonster*/CORPSE \"\n\t\t\t\"/*detail*/DETAIL \"\n\t\t\t\"/*translucent*/Q2TRANSLUCENT \"\n\t\t\t\"/*ladder*/LADDER \"\t//would otherwise be Q2LADDER\n\t\t\t\"/*hitbox*/30 \"\n\t\t\t\"/*??*/SKY\"\n\t\t\t,CVAR_RENDERERLATCH, \"Specifies a table for hl2->internal contentbits (one entry for each source bit).\", MAPOPTIONS);\n\n\t\treturn true;\n\t}\n\treturn false;\n}\n"
  },
  {
    "path": "plugins/hud/qwui.q3asm",
    "content": "-o \"hud\"\nplugin\nui_sbar\nqvm_api"
  },
  {
    "path": "plugins/hud/ui_sbar.c",
    "content": "#include \"../plugin.h\"\n\n#ifdef _MSC_VER\n#pragma warning(4: 4244)\n#pragma warning(4: 4305)\n#endif\n\n#define DEFAULTHUDNAME \"ftehud.hud\"\n\n#define MAX_ELEMENTS 128\n\nint K_UPARROW;\nint K_DOWNARROW;\nint K_LEFTARROW;\nint K_RIGHTARROW;\nint K_ESCAPE;\nint K_MOUSE1;\nint K_MOUSE2;\nint K_HOME;\nint K_SHIFT;\nint K_MWHEELDOWN;\nint K_MWHEELUP;\nint K_PAGEUP;\nint K_PAGEDOWN;\n\n\n#define\tMAX_CL_STATS\t\t32\n#define\tSTAT_HEALTH\t\t\t0\n#define\tSTAT_WEAPON\t\t\t2\n#define\tSTAT_AMMO\t\t\t3\n#define\tSTAT_ARMOR\t\t\t4\n#define\tSTAT_WEAPONFRAME\t5\n#define\tSTAT_SHELLS\t\t\t6\n#define\tSTAT_NAILS\t\t\t7\n#define\tSTAT_ROCKETS\t\t8\n#define\tSTAT_CELLS\t\t\t9\n#define\tSTAT_ACTIVEWEAPON\t10\n#define\tSTAT_TOTALSECRETS\t11\n#define\tSTAT_TOTALMONSTERS\t12\n#define\tSTAT_SECRETS\t\t13\t\t// bumped on client side by svc_foundsecret\n#define\tSTAT_MONSTERS\t\t14\t\t// bumped by svc_killedmonster\n#define\tSTAT_ITEMS\t\t\t15\n\n// context menus\n\n#define CONTEXT_NONE\t\t\t0\n#define CONTEXT_MAIN\t\t\t1\n#define CONTEXT_NEW_ITEM\t\t2\n#define CONTEXT_NEW_ITEM_SUB\t3\n\n//some engines can use more.\n//any mod specific ones should be 31 and downwards rather than upwards.\n\n\n#define\tIT_GUN1\t\t\t(1<<0)\n#define\tIT_GUN2\t\t\t(1<<1)\t\t//the code assumes these are linear.\n#define\tIT_GUN3\t\t\t(1<<2)\t\t//be careful with strange mods.\n#define\tIT_GUN4\t\t\t(1<<3)\n#define\tIT_GUN5\t\t\t(1<<4)\n#define\tIT_GUN6\t\t\t(1<<5)\n#define\tIT_GUN7\t\t\t(1<<6)\n#define\tIT_GUN8\t\t\t(1<<7)\t//quake doesn't normally use this.\n\n#define\tIT_AMMO1\t\t(1<<8)\n#define\tIT_AMMO2\t\t(1<<9)\n#define\tIT_AMMO3\t\t(1<<10)\n#define\tIT_AMMO4\t\t(1<<11)\n\n#define\tIT_GUN0\t\t\t(1<<12)\n\n#define\tIT_ARMOR1\t\t(1<<13)\n#define\tIT_ARMOR2\t\t(1<<14)\n#define\tIT_ARMOR3\t\t(1<<15)\n#define\tIT_SUPERHEALTH\t(1<<16)\n\n#define\tIT_PUP1\t\t\t(1<<17)\n#define\tIT_PUP2\t\t\t(1<<18)\n#define\tIT_PUP3\t\t\t(1<<19)\n#define\tIT_PUP4\t\t\t(1<<20)\n#define\tIT_PUP5\t\t\t(1<<21)\n#define\tIT_PUP6\t\t\t(1<<22)\n\n#define\tIT_RUNE1\t\t(1<<23)\n#define\tIT_RUNE2\t\t(1<<24)\n#define\tIT_RUNE3\t\t(1<<25)\n#define\tIT_RUNE4\t\t(1<<26)\n\n//these are linear and treated the same\n#define\tnumpups\t\t\t6\n\n//the names of the cvars, as they will appear on the console\n#define UI_NOSBAR \"ui_defaultsbar\"\n#define UI_NOIBAR \"ui_noibar\"\n#define UI_NOFLASH \"ui_nosbarflash\"\n\nstatic char *weaponabbreviation[] = {\t//the postfix for the weapon anims\n\t\"shotgun\",  // shotgun\n\t\"sshotgun\", // super shotgun\n\t\"nailgun\",  // nailgun\n\t\"snailgun\", // super nailgun\n\t\"rlaunch\",  // grenade launcher\n\t\"srlaunch\", // rocket launcher\n\t\"lightng\"   // thunderbolt\n};\n#define numweaps (sizeof(weaponabbreviation) / sizeof(char *))\n\nstatic char *pupabbr[] = {\t//the postfix for the powerup anims\n\t\"key1\",\n\t\"key2\",\n\t\"invis\",\n\t\"invul\",\n\t\"suit\",\n\t\"quad\"\n};\nstatic char *pupabbr2[] = {\t//the postfix for the powerup anims\n\t\"key1\",\n\t\"key2\",\n\t\"invis\",\n\t\"invuln\",\n\t\"suit\",\n\t\"quad\"\n};\n\n//0 = owned, 1 selected, 2-7 flashing\nstatic qhandle_t pic_cursor;\nstatic qhandle_t con_chars;\nstatic qhandle_t pic_weapon[8][numweaps];\nstatic qhandle_t sbarback, ibarback;\n\n//0 = owned, 1-6 flashing\nstatic qhandle_t pic_pup[7][numpups];\nstatic qhandle_t pic_armour[3];\nstatic qhandle_t pic_ammo[4];\nstatic qhandle_t pic_rune[4];\nstatic qhandle_t pic_num[13];\nstatic qhandle_t pic_anum[11];\n\n//faces\nstatic qhandle_t pic_face[5];\nstatic qhandle_t pic_facep[5];\nstatic qhandle_t pic_facequad;\nstatic qhandle_t pic_faceinvis;\nstatic qhandle_t pic_faceinvisinvuln;\nstatic qhandle_t pic_faceinvuln;\n//static qhandle_t pic_faceinvulnquad;\n\nstatic int currenttime;\nstatic int gotweapontime[numweaps];\nstatic int gotpuptime[numpups];\n\nfloat\tsbarminx;\nfloat\tsbarminy;\nfloat\tsbarscalex;\nfloat\tsbarscaley;\nfloat\tsbaralpha;\nint\t\tsbartype;\nint sbarindex;\n\nstatic int hudedit;\nstatic int typetoinsert;\n\nenum {\n\tDZ_BOTTOMLEFT,\n\tDZ_BOTTOMRIGHT\n};\n\ntypedef void drawelementfnc_t(void);\ntypedef struct {\n\tfloat defaultx;\t//used if couldn't load a config\n\tfloat defaulty;\n\tint defaultzone;\n\tfloat defaultalpha;\n\tdrawelementfnc_t *DrawElement;\n\tint subtype;\n} huddefaultelement_t;\n\n\n\n\n\ndrawelementfnc_t Hud_SBar;\ndrawelementfnc_t Hud_StatSmall;\ndrawelementfnc_t Hud_StatBig;\ndrawelementfnc_t Hud_ArmourPic;\ndrawelementfnc_t Hud_HealthPic;\ndrawelementfnc_t Hud_CurrentAmmoPic;\ndrawelementfnc_t Hud_IBar;\ndrawelementfnc_t Hud_Weapon;\ndrawelementfnc_t Hud_W_Lightning;\ndrawelementfnc_t Hud_Powerup;\ndrawelementfnc_t Hud_Rune;\ndrawelementfnc_t Hud_Ammo;\ndrawelementfnc_t Hud_ScoreCard;\ndrawelementfnc_t Hud_ScoreName;\ndrawelementfnc_t Hud_Blackness;\ndrawelementfnc_t Hud_TeamScore;\ndrawelementfnc_t Hud_TeamName;\ndrawelementfnc_t Hud_Tracking;\ndrawelementfnc_t Hud_TeamOverlay;\n// TODO: more elements\n// - generalized graphic elements\n// - cvar controlled small and big numbers\n// - alias controlled graphic elements (both +/-showscores like and alias calling?)\n// - Q2-style current weapon icon\n\nint statsremap[] =\n{\n\tSTAT_HEALTH,\n\tSTAT_ARMOR,\n\tSTAT_AMMO,\n\tSTAT_SHELLS,\n\tSTAT_NAILS,\n\tSTAT_ROCKETS,\n\tSTAT_CELLS\n};\n\nstruct subtypenames {\n\tchar *name;\n};\ntypedef struct {\n\tdrawelementfnc_t *draw;\n\tchar *name;\n\tint width, height;\n\tint maxsubtype;\n\tstruct subtypenames subtypename[20];\n} drawelement_t;\ndrawelement_t drawelement[] =\n{\n\t{Hud_SBar,\t\t\"Status bar\",\t320,\t24,\t0},\n\t{Hud_StatSmall,\t\t\"Stat (small)\",\t8*3,\t8,\t6, {\"Health\", \"Armour\", \"Ammo\", \"Shells\", \"Nails\", \"Rockets\", \"Cells\"}}, // equal to sizeof(statsremap)/sizeof(statsremap[0])-1\n\t{Hud_StatBig,\t\t\"Stat (big)\",\t24*3,\t24,\t6, {\"Health\", \"Armour\", \"Ammo\", \"Shells\", \"Nails\", \"Rockets\", \"Cells\"}}, // equal to sizeof(statsremap)/sizeof(statsremap[0])-1\n\t{Hud_ArmourPic,\t\t\"Armor pic\",\t24,\t24,\t0},\n\t{Hud_HealthPic,\t\t\"Health pic\",\t24,\t24,\t0},\n\t{Hud_CurrentAmmoPic,\t\"Ammo pic\",\t24,\t24,\t0},\n\t{Hud_IBar,\t\t\"Info bar\",\t320,\t24,\t0},\n\t{Hud_Weapon,\t\t\"Weapon pic\",\t24,\t16,\t6, {\"Shotgun\", \"Super Shotgun\", \"Nailgun\", \"Super Nailgun\", \"Grenade Launcher\", \"Rocket Launcher\", \"Thunderbolt\"}},\n\t{Hud_W_Lightning,\t\"Shaft pic\",\t24,\t16,\t0},\n\t{Hud_Powerup,\t\t\"Powerup pic\",\t16,\t16,\t5, {\"Key 1\", \"Key 2\", \"Ring of Invis\", \"Pentagram\", \"Biosuit\", \"Quad\"}},\n\t{Hud_Rune,\t\t\"Rune pic\",\t8,\t16,\t3, {\"Rune 1\", \"Rune 2\", \"Rune 3\", \"Rune 4\"}},\n\t{Hud_Ammo,\t\t\"Ammo display\",\t42,\t11,\t3, {\"Shells\", \"Spikes\", \"Rockets\", \"Cells\"}},\n\t{Hud_Blackness,\t\t\"Blackness\",\t16,\t16,\t9, {\"10%\", \"20%\", \"30%\", \"40%\", \"50%\", \"60%\", \"70%\", \"80%\", \"90%\", \"100%\"}},\n\t{Hud_ScoreCard,\t\t\"Scorecard\",\t32,\t8,\t15, {\"Player 0\", \"Player 1\", \"Player 2\", \"Player 3\", \"Player 4\", \"Player 5\", \"Player 6\", \"Player 7\", \"Player 8\", \"Player 9\", \"Player 10\", \"Player 11\", \"Player 12\", \"Player 13\", \"Player 14\", \"Player 15\"}},\n\t{Hud_ScoreName,\t\t\"Scorename\",\t128,\t8,\t7, {\"Player 0\", \"Player 1\", \"Player 2\", \"Player 3\", \"Player 4\", \"Player 5\", \"Player 6\", \"Player 7\"}},\n\t{Hud_TeamScore,\t\t\"TeamScore\",\t32,\t8,\t7, {\"Team 0\", \"Team 1\", \"Team 2\", \"Team 3\", \"Team 4\", \"Team 5\", \"Team 6\", \"Team 7\"}},\n\t{Hud_TeamName,\t\t\"TeamName\",\t128,\t8,\t7, {\"Team 0\", \"Team 1\", \"Team 2\", \"Team 3\", \"Team 4\", \"Team 5\", \"Team 6\", \"Team 7\"}},\n\t{Hud_Tracking,\t\t\"Tracking\",\t128,\t8,\t0},\n\t{Hud_TeamOverlay,\t\"Team overlay\",\t256,\t64,\t0}\n};\n\nhuddefaultelement_t hedefaulttype[] = {\n\t{\n\t\t0, -24, DZ_BOTTOMLEFT,\n\t\t0.3f,\n\t\tHud_SBar\n\t},\n\n\t{\n\t\t0, -24, DZ_BOTTOMLEFT,\n\t\t1,\n\t\tHud_ArmourPic\n\t},\n\t{\n\t\t24, -24, DZ_BOTTOMLEFT,\n\t\t1,\n\t\tHud_StatBig,\n\t\t1\n\t},\n\n\t{\n\t\t112, -24, DZ_BOTTOMLEFT,\n\t\t1,\n\t\tHud_HealthPic\n\t},\n\t{\n\t\t24*6, -24, DZ_BOTTOMLEFT,\n\t\t1,\n\t\tHud_StatBig,\n\t\t0\n\t},\n\n\t{\n\t\t224, -24, DZ_BOTTOMLEFT,\n\t\t1,\n\t\tHud_CurrentAmmoPic\n\t},\n\t{\n\t\t248, -24, DZ_BOTTOMLEFT,\n\t\t1,\n\t\tHud_StatBig,\n\t\t2\n\t},\n\n\t{\n\t\t0, -48, DZ_BOTTOMLEFT,\n\t\t0.3f,\n\t\tHud_IBar\n\t},\n\n\t{\n\t\t0, -40, DZ_BOTTOMLEFT,\n\t\t1,\n\t\tHud_Weapon,\n\t\t0\n\t},\n\t{\n\t\t24, -40, DZ_BOTTOMLEFT,\n\t\t1,\n\t\tHud_Weapon,\n\t\t1\n\t},\n\t{\n\t\t48, -40, DZ_BOTTOMLEFT,\n\t\t1,\n\t\tHud_Weapon,\n\t\t2\n\t},\n\t{\n\t\t72, -40, DZ_BOTTOMLEFT,\n\t\t1,\n\t\tHud_Weapon,\n\t\t3\n\t},\n\t{\n\t\t96, -40, DZ_BOTTOMLEFT,\n\t\t1,\n\t\tHud_Weapon,\n\t\t4\n\t},\n\t{\n\t\t120, -40, DZ_BOTTOMLEFT,\n\t\t1,\n\t\tHud_Weapon,\n\t\t5\n\t},\n\t{\n\t\t146, -40, DZ_BOTTOMLEFT,\n\t\t1,\n\t\tHud_W_Lightning\n\t},\n\t{\n\t\t194, -40, DZ_BOTTOMLEFT,\n\t\t0.3f,\n\t\tHud_Powerup,\n\t\t0\n\t},\n\t{\n\t\t208, -40, DZ_BOTTOMLEFT,\n\t\t0.3f,\n\t\tHud_Powerup,\n\t\t1\n\t},\n\t{\n\t\t224, -40, DZ_BOTTOMLEFT,\n\t\t1,\n\t\tHud_Powerup,\n\t\t2\n\t},\n\t{\n\t\t240, -40, DZ_BOTTOMLEFT,\n\t\t1,\n\t\tHud_Powerup,\n\t\t3\n\t},\n\t{\n\t\t256, -40, DZ_BOTTOMLEFT,\n\t\t1,\n\t\tHud_Powerup,\n\t\t4\n\t},\n\t{\n\t\t272, -40, DZ_BOTTOMLEFT,\n\t\t1,\n\t\tHud_Powerup,\n\t\t5\n\t},\n\t{\n\t\t288, -40, DZ_BOTTOMLEFT,\n\t\t0.3f,\n\t\tHud_Rune,\n\t\t0\n\t},\n\t{\n\t\t296, -40, DZ_BOTTOMLEFT,\n\t\t0.3f,\n\t\tHud_Rune,\n\t\t1\n\t},\n\t{\n\t\t304, -40, DZ_BOTTOMLEFT,\n\t\t0.3f,\n\t\tHud_Rune,\n\t\t2\n\t},\n\t{\n\t\t312, -40, DZ_BOTTOMLEFT,\n\t\t0.3f,\n\t\tHud_Rune,\n\t\t3\n\t},\n\n\t{\n\t\t48*0+3, -48, DZ_BOTTOMLEFT,\n\t\t1,\n\t\tHud_Ammo,\n\t\t0\n\t},\n\t{\n\t\t48*1+3, -48, DZ_BOTTOMLEFT,\n\t\t1,\n\t\tHud_Ammo,\n\t\t1\n\t},\n\t{\n\t\t48*2+3, -48, DZ_BOTTOMLEFT,\n\t\t1,\n\t\tHud_Ammo,\n\t\t2\n\t},\n\t{\n\t\t48*3+3, -48, DZ_BOTTOMLEFT,\n\t\t1,\n\t\tHud_Ammo,\n\t\t3\n\t},\n\n\t{\n\t\t42*3, -48, DZ_BOTTOMLEFT,\n\t\t1,\n\t\tHud_ScoreCard\n\t}\n};\ntypedef struct {\n\tint type;\n\tint subtype;\n\n\tfloat x, y;\n\tfloat scalex;\n\tfloat scaley;\n\tfloat alpha;\n} hudelement_t;\nhudelement_t element[MAX_ELEMENTS];\t//look - Spike used a constant - that's a turn up for the books!\nint numelements;\n\nint currentitem;\nint hoveritem;\nqboolean mousedown, shiftdown;\nfloat mouseofsx, mouseofsy;\nqboolean context;\n\nvec3_t player_location[32];\nint player_armor[32];\nint player_health[32];\nunsigned int player_items[32];\nchar *player_nick[32] =\n{\n\t\"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\",\n\t\"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\",\n\t\"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\",\n\t\"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\"\n};\nunsigned int player_nicklength;\n\nvoid Hud_TeamOverlayUpdate(void);\n\n/*\n==================\nCOM_DefaultExtension\n==================\n*/\nvoid COM_DefaultExtension (char *path, char *extension, int maxlen)\n{\n\tchar    *src;\n//\n// if path doesn't have a .EXT, append extension\n// (extension should include the .)\n//\n\tsrc = path + strlen(path) - 1;\n\n\twhile (*src != '/' && src != path)\n\t{\n\t\tif (*src == '.')\n\t\t\treturn;                 // it has an extension\n\t\tsrc--;\n\t}\n\n\tstrlcpy (path+strlen(path), extension, maxlen);\n}\n\nvoid UI_DrawPic(qhandle_t pic, int x, int y, int width, int height)\n{\n\tDraw_Image((float)x*sbarscalex+sbarminx, (float)y*sbarscaley+sbarminy, (float)width*sbarscalex, (float)height*sbarscaley, 0, 0, 1, 1, pic);\n}\nvoid UI_DrawChar(unsigned int c, int x, int y)\n{\nstatic float size = 1.0f/16.0f;\n\tfloat s1 = size * (c&15);\n\tfloat t1 = size * ((c>>4)&15);\n\tDraw_Image((float)x*sbarscalex+sbarminx, (float)y*sbarscaley+sbarminy, 8*sbarscalex, 8*sbarscaley, s1, t1, s1+size, t1+size, con_chars);\n}\nvoid UI_DrawString(char *s, int x, int y)\n{\n\twhile(*s)\n\t{\n\t\tUI_DrawChar((unsigned int)*s++, x, y);\n\t\tx+=8;\n\t}\n}\nvoid UI_DrawAltString(char *s, int x, int y, qboolean shouldmask)\n{\n\tint mask = shouldmask?128:0;\n\twhile(*s)\n\t{\n\t\tUI_DrawChar((unsigned int)*s++ | mask, x, y);\n\t\tx+=8;\n\t}\n}\n\n\nvoid UI_DrawBigNumber(int num, int x, int y, qboolean red)\n{\n\tchar *s;\n\tint len;\n\ts = va(\"%i\", num);\n\n\n\tlen = strlen(s);\n\tif (len < 3)\n\t\tx += 24*(3-len);\n\telse\n\t\ts += len-3;\n\n\tif (red)\n\t{\n\t\twhile(*s)\n\t\t{\n\t\t\tif (*s == '-')\n\t\t\t\tUI_DrawPic (pic_anum[10], x, y, 24, 24);\n\t\t\telse\n\t\t\t\tUI_DrawPic (pic_anum[*s-'0'], x, y, 24, 24);\n\t\t\ts++;\n\t\t\tx+=24;\n\t\t}\n\t}\n\telse\n\t{\n\t\twhile(*s)\n\t\t{\n\t\t\tif (*s == '-')\n\t\t\t\tUI_DrawPic (pic_num[10], x, y, 24, 24);\n\t\t\telse\n\t\t\t\tUI_DrawPic (pic_num[*s-'0'], x, y, 24, 24);\n\t\t\ts++;\n\t\t\tx+=24;\n\t\t}\n\t}\n}\n\nvoid SBar_FlushAll(void)\n{\n\tnumelements = 0;\n}\n\nstatic int idxforfunc(drawelementfnc_t *fnc)\n{\n\tint i;\n\tfor (i = 0; i < sizeof(drawelement)/sizeof(drawelement[0]); i++)\n\t{\n\t\tif (drawelement[i].draw == fnc)\n\t\t\treturn i;\n\t}\n\treturn -10000;\t//try and crash\n}\n\nvoid SBar_ReloadDefaults(void)\n{\n\tint i;\n\tfor (i = 0; i < sizeof(hedefaulttype)/sizeof(hedefaulttype[0]); i++)\n\t{\n\t\tif (hedefaulttype[i].defaultalpha)\n\t\t{\n\t\t\tif (numelements >= MAX_ELEMENTS)\n\t\t\t\tbreak;\n\t\t\telement[numelements].type = idxforfunc(hedefaulttype[i].DrawElement);\n\t\t\telement[numelements].alpha = hedefaulttype[i].defaultalpha;\n\t\t\telement[numelements].scalex = 1;\n\t\t\telement[numelements].scaley = 1;\n\t\t\telement[numelements].subtype = hedefaulttype[i].subtype;\n\t\t\tswitch(hedefaulttype[i].defaultzone)\n\t\t\t{\n\t\t\tcase DZ_BOTTOMLEFT:\n\t\t\t\telement[numelements].x = hedefaulttype[i].defaultx;\n\t\t\t\telement[numelements].y = 480+hedefaulttype[i].defaulty;\n\t\t\t\tbreak;\n\t\t\tcase DZ_BOTTOMRIGHT:\n\t\t\t\telement[numelements].x = 640+hedefaulttype[i].defaultx;\n\t\t\t\telement[numelements].y = 480+hedefaulttype[i].defaulty;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tnumelements++;\n\t\t}\n\t}\n}\n\nvoid UI_SbarInit(void)\n{\n\tint i;\n\tint j;\n\n//main bar (add cvars later)\n\tibarback = Draw_LoadImage(\"ibar\", true);\n\tsbarback = Draw_LoadImage(\"sbar\", true);\n\n\tcon_chars = Draw_LoadImage(\"conchars\", true);\n\n//load images.\n\tfor (i = 0; i < 10; i++)\n\t{\n\t\tpic_num[i] = Draw_LoadImage(va(\"num_%i\", i), true);\n\t\tpic_anum[i] = Draw_LoadImage(va(\"anum_%i\", i), true);\n\t}\n\tpic_num[10] = Draw_LoadImage(\"num_minus\", true);\n\tpic_anum[10] = Draw_LoadImage(\"anum_minus\", true);\n\tpic_num[11] = Draw_LoadImage(\"num_colon\", true);\n\tpic_num[12] = Draw_LoadImage(\"num_slash\", true);\n\n\tfor (i = 0; i < numweaps; i++)\n\t{\n\t\tgotweapontime[i] = 0;\n\t\tpic_weapon[0][i] = Draw_LoadImage(va(\"inv_%s\", weaponabbreviation[i]), true);\n\t\tpic_weapon[1][i] = Draw_LoadImage(va(\"inv2_%s\", weaponabbreviation[i]), true);\n\t\tfor (j = 0; j < 5; j++)\n\t\t{\n\t\t\tpic_weapon[2+j][i] = Draw_LoadImage(va(\"inva%i_%s\", j+1, weaponabbreviation[i]), true);\n\t\t}\n\t}\n\tfor (i = 0; i < numpups; i++)\n\t{\n\t\tgotpuptime[i] = 0;\n\t\tpic_pup[0][i] = Draw_LoadImage(va(\"sb_%s\", pupabbr2[i]), true);\n\t\tfor (j = 0; j < 5; j++)\n\t\t{\n\t\t\tpic_pup[1+j][i] = Draw_LoadImage(va(\"sba%i_%s\", j+1, pupabbr[i]), true);\n\t\t}\n\t}\n\tpic_cursor = Draw_LoadImage(\"gfx/cursor\", false);\n\n\tpic_armour[0] = Draw_LoadImage(\"sb_armor1\", true);\n\tpic_armour[1] = Draw_LoadImage(\"sb_armor2\", true);\n\tpic_armour[2] = Draw_LoadImage(\"sb_armor3\", true);\n\n\tpic_ammo[0] = Draw_LoadImage(\"sb_shells\", true);\n\tpic_ammo[1] = Draw_LoadImage(\"sb_nails\", true);\n\tpic_ammo[2] = Draw_LoadImage(\"sb_rocket\", true);\n\tpic_ammo[3] = Draw_LoadImage(\"sb_cells\", true);\n\n\tpic_rune[0] = Draw_LoadImage(\"sb_sigil1\", true);\n\tpic_rune[1] = Draw_LoadImage(\"sb_sigil2\", true);\n\tpic_rune[2] = Draw_LoadImage(\"sb_sigil3\", true);\n\tpic_rune[3] = Draw_LoadImage(\"sb_sigil4\", true);\n\n\tpic_face[0] = Draw_LoadImage(\"face1\", true);\n\tpic_face[1] = Draw_LoadImage(\"face2\", true);\n\tpic_face[2] = Draw_LoadImage(\"face3\", true);\n\tpic_face[3] = Draw_LoadImage(\"face4\", true);\n\tpic_face[4] = Draw_LoadImage(\"face5\", true);\n\n\tpic_facep[0] = Draw_LoadImage(\"face_p1\", true);\n\tpic_facep[1] = Draw_LoadImage(\"face_p2\", true);\n\tpic_facep[2] = Draw_LoadImage(\"face_p3\", true);\n\tpic_facep[3] = Draw_LoadImage(\"face_p4\", true);\n\tpic_facep[4] = Draw_LoadImage(\"face_p5\", true);\n\n\tpic_facequad = Draw_LoadImage(\"face_quad\", true);\n\tpic_faceinvis = Draw_LoadImage(\"face_invis\", true);\n\tpic_faceinvisinvuln = Draw_LoadImage(\"face_inv2\", true);\n\tpic_faceinvuln = Draw_LoadImage(\"face_invul2\", true);\n//\tpic_faceinvulnquad = Draw_LoadImage(\"face_invul1\", true);\n\n\tSBar_FlushAll();\n\tSBar_ReloadDefaults();\n}\n\nunsigned int stats[MAX_CL_STATS];\n\nvoid Hud_SBar(void)\n{\n\tUI_DrawPic(sbarback, 0, 0, 320, 24);\n}\n\nvoid Hud_ArmourPic(void)\n{\n\tif (stats[STAT_ITEMS] & IT_ARMOR3)\n\t\tUI_DrawPic(pic_armour[2], 0, 0, 24, 24);\n\telse if (stats[STAT_ITEMS] & IT_ARMOR2)\n\t\tUI_DrawPic(pic_armour[1], 0, 0, 24, 24);\n\telse if (stats[STAT_ITEMS] & IT_ARMOR1 || hudedit)\n\t\tUI_DrawPic(pic_armour[0], 0, 0, 24, 24);\n}\n\nvoid Hud_HealthPic(void)\n{\n\tint hl;\n\n\tif (stats[STAT_ITEMS] & IT_PUP3)\n\t{\t//invisability\n\t\tif (stats[STAT_ITEMS] & IT_PUP4)\n\t\t\tUI_DrawPic(pic_faceinvisinvuln, 0, 0, 24, 24);\n\t\telse\n\t\t\tUI_DrawPic(pic_faceinvis, 0, 0, 24, 24);\n\t\treturn;\n\t}\n\n\tif (stats[STAT_ITEMS] & IT_PUP4)\n\t{\t//invuln\n//\t\tif (stats[STAT_ITEMS] & IT_PUP6)\n//\t\t\tUI_DrawPic(pic_faceinvulnquad, 0, 0, 24, 24);\n//\t\telse\n\t\t\tUI_DrawPic(pic_faceinvuln, 0, 0, 24, 24);\n\t\treturn;\n\t}\n\tif (stats[STAT_ITEMS] & IT_PUP6)\n\t{\n\t\tUI_DrawPic(pic_facequad, 0, 0, 24, 24);\n\t\treturn;\n\t}\n\n\thl = stats[STAT_HEALTH]/20;\n\tif (hl > 4)\n\t\thl = 4;\n\tif (hl < 0)\n\t\thl = 0;\n\n\t//FIXME\n//\tif (innpain)\n//\t\tUI_DrawPic(pic_facep[4-hl], 0, 0, 24, 24);\n//\telse\n\t\tUI_DrawPic(pic_face[4-hl], 0, 0, 24, 24);\n}\n\nvoid Hud_StatBig(void)\n{\n\tint i = stats[statsremap[sbartype]];\n\n\tUI_DrawBigNumber(i, 0, 0, i < 25);\n}\n\nvoid Hud_StatSmall(void)\n{\n\tint i = stats[statsremap[sbartype]];\n\n\t// TODO: need some sort of options thing to change between brown/white/gold text\n\tUI_DrawChar(i%10+18, 19, 0);\n\ti/=10;\n\tif (i)\n\t\tUI_DrawChar(i%10+18, 11, 0);\n\ti/=10;\n\tif (i)\n\t\tUI_DrawChar(i%10+18, 3, 0);\n}\n\nvoid Hud_CurrentAmmoPic(void)\n{\n\t\t if ((stats[STAT_ITEMS] & IT_AMMO1))\n\t\tUI_DrawPic(pic_ammo[0], 0, 0, 24, 24);\n\telse if (stats[STAT_ITEMS] & IT_AMMO2)\n\t\tUI_DrawPic(pic_ammo[1], 0, 0, 24, 24);\n\telse if (stats[STAT_ITEMS] & IT_AMMO3)\n\t\tUI_DrawPic(pic_ammo[2], 0, 0, 24, 24);\n\telse if (stats[STAT_ITEMS] & IT_AMMO4 || hudedit)\n\t\tUI_DrawPic(pic_ammo[3], 0, 0, 24, 24);\n}\n\nvoid Hud_IBar(void)\n{\n\tUI_DrawPic(ibarback, 0, 0, 320, 24);\n}\n\nvoid Hud_Weapon(void)\n{\n\tint flash;\n\tif (!(stats[STAT_ITEMS] & (IT_GUN1 << sbartype)) && !hudedit)\n\t{\n\t\tgotweapontime[sbartype] = 0;\n\t\treturn;\n\t}\n\n\tif (!gotweapontime[sbartype])\n\t\tgotweapontime[sbartype] = currenttime;\n\tflash = (currenttime - gotweapontime[sbartype])/100;\n\tif (flash < 0)\t//errr... whoops...\n\t\tflash = 0;\n\n\tif (flash > 10)\n\t{\n\t\tif (stats[STAT_ACTIVEWEAPON] & (IT_GUN1 << sbartype))\n\t\t\tflash = 1;\t//selected.\n\t\telse\n\t\t\tflash = 0;\n\t}\n\telse\n\t\tflash = (flash%5) + 2;\n\n\tUI_DrawPic(pic_weapon[flash][sbartype], 0, 0, 24, 16);\n}\n\nvoid Hud_W_HalfLightning(void)\t//left half only (needed due to LG icon being twice as wide)\n{\n\tint flash;\n\tint wnum = 6;\n\n\tif (!(stats[STAT_ITEMS] & (IT_GUN1 << wnum)) && !hudedit)\n\t{\n\t\tgotweapontime[wnum] = 0;\n\t\treturn;\n\t}\n\n\tif (!gotweapontime[wnum])\n\t\tgotweapontime[wnum] = currenttime;\n\tflash = (currenttime - gotweapontime[wnum])/100;\n\tif (flash < 0)\t//errr... whoops...\n\t\tflash = 0;\n\n\tif (flash > 10)\n\t{\n\t\tif (stats[STAT_ACTIVEWEAPON] & (IT_GUN1 << wnum))\n\t\t\tflash = 1;\t//selected.\n\t\telse\n\t\t\tflash = 0;\n\t}\n\telse\n\t\tflash = (flash%5) + 2;\n\n\tDraw_Image(sbarminx, sbarminy, (float)24*sbarscalex, (float)16*sbarscaley, 0, 0, 0.5, 1, pic_weapon[flash][wnum]);\n}\nvoid Hud_W_Lightning(void)\n{\n\tint flash;\n\tint wnum = 6;\n\n\tif (!(stats[STAT_ITEMS] & (IT_GUN1 << wnum)) && !hudedit)\n\t{\n\t\tgotweapontime[wnum] = 0;\n\t\treturn;\n\t}\n\n\tif (!gotweapontime[wnum])\n\t\tgotweapontime[wnum] = currenttime;\n\tflash = (currenttime - gotweapontime[wnum])/100;\n\tif (flash < 0)\t//errr... whoops...\n\t\tflash = 0;\n\n\tif (flash > 10)\n\t{\n\t\tif (stats[STAT_ACTIVEWEAPON] & (IT_GUN1 << wnum))\n\t\t\tflash = 1;\t//selected.\n\t\telse\n\t\t\tflash = 0;\n\t}\n\telse\n\t\tflash = (flash%5) + 2;\n\n\tUI_DrawPic(pic_weapon[flash][wnum], 0, 0, 48, 16);\n}\n\nvoid Hud_Powerup(void)\n{\n\tint flash;\n\tif (!(stats[STAT_ITEMS] & (IT_PUP1 << sbartype)) && !hudedit)\n\t\treturn;\n\n\tif (!gotpuptime[sbartype])\n\t\tgotpuptime[sbartype] = currenttime;\n\tflash = (currenttime - gotpuptime[sbartype])/100;\n\tif (flash < 0)\t//errr... whoops...\n\t\tflash = 0;\n\n\tif (flash > 10)\n\t{\n\t\tflash = 0;\n\t}\n\telse\n\t\tflash = (flash%5) + 2;\n\n\tUI_DrawPic(pic_pup[flash][sbartype], 0, 0, 16, 16);\n}\n\nvoid Hud_Rune(void)\n{\n\tif (!(stats[STAT_ITEMS] & (IT_RUNE1 << sbartype)) && !hudedit)\n\t\treturn;\n\tUI_DrawPic(pic_rune[sbartype], 0, 0, 8, 16);\n}\n\nvoid Hud_Ammo(void)\n{\n\tint num;\n\tDraw_Image(sbarminx, sbarminy, (float)42*sbarscalex, (float)11*sbarscaley, (3+(sbartype*48))/320.0f, 0, (3+(sbartype*48)+42)/320.0f, 11/24.0f, ibarback);\n\n\tnum = stats[STAT_SHELLS+sbartype];\n\tif (hudedit)\n\t\tnum = 999;\n\n\tUI_DrawChar(num%10+18, 19, 0);\n\tnum/=10;\n\tif (num)\n\t\tUI_DrawChar(num%10+18, 11, 0);\n\tnum/=10;\n\tif (num)\n\t\tUI_DrawChar(num%10+18, 3, 0);\n}\n\nfloat pc[16][3] =\n{\n/*\n\t{235, 235, 235},\n\t{143, 111, 035},\n\t{139, 139, 203},\n\t{107, 107, 015},\n\t{127, 000, 000},\n\t{175, 103, 035},\n\t{255, 243, 027},\n\t{227, 179, 151},\n\t{171, 139, 163},\n\t{187, 115, 159},\n\t{219, 195, 187},\n\t{111, 131, 123},\n\t{255, 243, 027},\n\t{000, 000, 255},\n\t{247, 211, 139}\n*/\n\t{0.922, 0.922, 0.922},\n\t{0.560, 0.436, 0.137},\n\t{0.545, 0.545, 0.796},\n\t{0.420, 0.420, 0.059},\n\t{0.498, 0.000, 0.000},\n\t{0.686, 0.404, 0.137},\n\t{1.000, 0.953, 0.106},\n\t{0.890, 0.702, 0.592},\n\t{0.671, 0.545, 0.639},\n\t{0.733, 0.451, 0.624},\n\t{0.859, 0.765, 0.733},\n\t{0.436, 0.514, 0.482},\n\t{1.000, 0.953, 0.106},\n\t{0.000, 0.000, 1.000},\n\t{0.969, 0.827, 0.545}\n};\n\nint numsortedplayers;\nint trackedplayer;\nint sortedplayers[32];\nplugclientinfo_t players[32];\n\nvoid SortPlayers(void)\n{\n\tint i, j;\n\tint temp;\n\n\tnumsortedplayers = 0;\n\ttrackedplayer = -1;\n\tfor (i = 0; i < 32; i++)\n\t{\n\t\tif (GetPlayerInfo(i, &players[i])>0)\n\t\t\ttrackedplayer = i;\n\t\tif (players[i].spectator)\n\t\t\tcontinue;\n\t\tif (*players[i].name != 0)\n\t\t\tsortedplayers[numsortedplayers++] = i;\n\t}\n\n\tfor (i = 0; i < numsortedplayers; i++)\n\t{\n\t\tfor (j = i+1; j < numsortedplayers; j++)\n\t\t{\n\t\t\tif (players[sortedplayers[i]].frags < players[sortedplayers[j]].frags)\n\t\t\t{\n\t\t\t\ttemp = sortedplayers[j];\n\t\t\t\tsortedplayers[j] = sortedplayers[i];\n\t\t\t\tsortedplayers[i] = temp;\n\t\t\t}\n\t\t}\n\t}\n}\n\ntypedef struct {\n\tchar name[8];\n\tint frags;\n\tint tc;\n\tint bc;\n} teams_t;\nteams_t team[32];\nint numsortedteams;\n\nvoid SortTeams(void)\n{\n\tteams_t temp;\n\tint i, j;\n\n\tnumsortedplayers = 0;\n\ttrackedplayer = -1;\n\tfor (i = 0; i < 32; i++)\n\t{\n\t\tif (GetPlayerInfo(i, &players[i])>0)\n\t\t\ttrackedplayer = i;\n\t\tif (players[i].spectator)\n\t\t\tcontinue;\n\t\tif (*players[i].name != 0)\n\t\t\tsortedplayers[numsortedplayers++] = i;\n\n\t\tfor (j = 0; j < numsortedteams; j++)\n\t\t{\n\t\t\tif (!strcmp(team[j].name, players[i].name))\n\t\t\t{\n\t\t\t\tteam[j].frags += players[i].frags;\n\t\t\t\twhile(j > 0)\n\t\t\t\t{\n\t\t\t\t\tif (team[j-1].frags < team[j].frags)\n\t\t\t\t\t{\n\t\t\t\t\t\tmemcpy(&temp, &team[j], sizeof(teams_t));\n\t\t\t\t\t\tmemcpy(&team[j], &team[j-1], sizeof(teams_t));\n\t\t\t\t\t\tmemcpy(&team[j-1], &temp, sizeof(teams_t));\n\t\t\t\t\t\tj--;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (j == numsortedteams)\n\t\t{\n\t\t\tstrlcpy(team[j].name, players[i].name, sizeof(team[j].name));\n\t\t\tteam[j].frags = players[i].frags;\n\t\t\tteam[j].tc = players[i].topcolour;\n\t\t\tteam[j].bc = players[i].bottomcolour;\n\t\t\tnumsortedteams++;\n\t\t}\n\t}\n}\n\nvoid Hud_ScoreCard(void)\n{\n\tint frags, tc, bc, p;\n\tint brackets;\n\tchar number[6];\n\n\tif (hudedit)\n\t{\n\t\tfrags = sbartype;\n\t\ttc = 0;\n\t\tbc = 0;\n\t\tbrackets = 1;\n\t}\n\telse\n\t{\n\t\tSortPlayers();\n\t\tif (sbartype>=numsortedplayers)\n\t\t\treturn;\n\t\tp = sortedplayers[sbartype];\n\t\tbc = players[p].bottomcolour;\n\t\ttc = players[p].topcolour;\n\t\tfrags = players[p].frags;\n\t\tbrackets = p==trackedplayer;\n\t}\n\n\tDraw_Colour4f(pc[tc][0], pc[tc][1], pc[tc][2], sbaralpha);\n\tDraw_Fill(sbarminx, sbarminy, (float)32*sbarscalex, (float)4*sbarscaley);\n\tDraw_Colour4f(pc[bc][0], pc[bc][1], pc[bc][2], \tsbaralpha);\n\tDraw_Fill(sbarminx, sbarminy+4*sbarscaley, (float)32*sbarscalex, (float)4*sbarscaley);\n\n\tDraw_Colour4f(1, 1, 1, sbaralpha);\n\tif (brackets)\n\t{\n\t\tUI_DrawChar(16, 0, 0);\n\t\tUI_DrawChar(17, 24, 0);\n\t}\n\n\tsnprintf(number, sizeof(number), \"%-3i\", frags);\n\tUI_DrawChar(number[0], 4, 0);\n\tUI_DrawChar(number[1], 12, 0);\n\tUI_DrawChar(number[2], 20, 0);\n\n\tDraw_Colour4f(1,1,1,1);\n}\nvoid Hud_ScoreName(void)\n{\n\tint p;\n\tchar *name;\n\tif (hudedit)\n\t{\n\t\tname = va(\"Player %i\", sbartype);\n\t}\n\telse\n\t{\n\t\tSortPlayers();\n\t\tif (sbartype>=numsortedplayers)\n\t\t\treturn;\n\t\tp = sortedplayers[sbartype];\n\t\tname = players[p].name;\n\t}\n\tUI_DrawString(name, 0, 0);\n}\n\nvoid Hud_TeamScore(void)\n{\n\tint frags, tc, bc, p;\n\tint brackets;\n\tchar number[6];\n\n\tif (hudedit)\n\t{\n\t\tfrags = sbartype;\n\t\ttc = 0;\n\t\tbc = 0;\n\t\tbrackets = 1;\n\t}\n\telse\n\t{\n\t\tSortPlayers();\n\t\tif (sbartype>=numsortedteams)\n\t\t\treturn;\n\t\tp = sbartype;\n\t\tbc = team[p].bc;\n\t\ttc = team[p].tc;\n\t\tfrags = team[p].frags;\n\t\tbrackets = p==trackedplayer;\n\t}\n\n\tDraw_Colour4f(pc[tc][0], pc[tc][1], pc[tc][2], sbaralpha);\n\tDraw_Fill(sbarminx, sbarminy, (float)32*sbarscalex, (float)4*sbarscaley);\n\tDraw_Colour4f(pc[bc][0], pc[bc][1], pc[bc][2], \tsbaralpha);\n\tDraw_Fill(sbarminx, sbarminy+4*sbarscaley, (float)32*sbarscalex, (float)4*sbarscaley);\n\n\tDraw_Colour4f(1, 1, 1, sbaralpha);\n\tif (brackets)\n\t{\n\t\tUI_DrawChar(16, 0, 0);\n\t\tUI_DrawChar(17, 24, 0);\n\t}\n\n\tsnprintf(number, sizeof(number), \"%-3i\", frags);\n\tUI_DrawChar(number[0], 4, 0);\n\tUI_DrawChar(number[1], 12, 0);\n\tUI_DrawChar(number[2], 20, 0);\n\n\n\n\tDraw_Colour4f(1,1,1,1);\n}\n\nvoid Hud_TeamName(void)\n{\n\tint p;\n\tchar *tname;\n\n\tif (hudedit)\n\t{\n\t\ttname = va(\"T%-3i\", sbartype);\n\t}\n\telse\n\t{\n\t\tSortTeams();\n\t\tif (sbartype>=numsortedteams)\n\t\t\treturn;\n\t\tp = sbartype;\n\t\ttname = team[p].name;\n\t}\n\n\tDraw_Colour4f(1, 1, 1, sbaralpha);\n\n\tif (tname[0])\n\t{\n\t\tUI_DrawChar(tname[0], 0, 0);\n\t\tif (tname[1])\n\t\t{\n\t\t\tUI_DrawChar(tname[1], 8, 0);\n\t\t\tif (tname[2])\n\t\t\t{\n\t\t\t\tUI_DrawChar(tname[2], 8, 0);\n\t\t\t\tif (tname[3])\n\t\t\t\t{\n\t\t\t\t\tUI_DrawChar(tname[3], 8, 0);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tDraw_Colour4f(1,1,1,1);\n}\n\nvoid Hud_TeamOverlay(void)\n{\n\tstatic int current_player = -1;\n\tint offset = 0;\n\n\tif (hudedit)\n\t{\n\t\tUI_DrawString(\"UnnamedPlayer1: r999/999 rlg ra-mega\", 0, 0);\n\t\tUI_DrawString(\"UnnamedPlayer2: r999/999 rlg ra-mega\", 0, 16);\n\t\tUI_DrawString(\"UnnamedPlayer3: r999/999 rlg ra-mega\", 0, 32);\n\t}\n\telse\n\t{\n\t\tunsigned int i;\n\n\t\t// If the tracking has changed, flush the old teaminfo\n\t\tif (current_player != players[trackedplayer].userid)\n\t\t{\n\t\t\tint j;\n\n\t\t\tfor (j = 0; j < 32; j++)\n\t\t\t\tplayer_nick[j] = \"\";\n\t\t\tplayer_nicklength = 0;\n\n\t\t\tcurrent_player = players[trackedplayer].userid;\n\n\t\t\treturn;\n\t\t}\n\n\t\tfor (i = 0; i < 32; i++)\n\t\t{\n\t\t\t// Empty nicknames are defined as not being printed\n\t\t\tif (player_nick[i] != \"\")\n\t\t\t{\n\t\t\t\tchar armortype = ' ';\n\t\t\t\tchar* bestweap = \"    \";\n\t\t\t\tchar loc[256], str[256], spacing[64], spacing_a[64], spacing_h[64];\n\t\t\t\tunsigned int j;\n\n\t\t\t\t// More info about armortype\n\t\t\t\tif (player_items[i] & IT_ARMOR3)\n\t\t\t\t\tarmortype = 'r';\n\t\t\t\telse if (player_items[i] & IT_ARMOR2)\n\t\t\t\t\tarmortype = 'y';\n\t\t\t\telse if (player_items[i] & IT_ARMOR1)\n\t\t\t\t\tarmortype = 'g';\n\n\t\t\t\t// Only care about reporting weapons that have some meaning\n\t\t\t\tif ((player_items[i] & (IT_GUN6 | IT_GUN7)) == (IT_GUN6 | IT_GUN7))\n\t\t\t\t\tbestweap = \"rlg \";\n\t\t\t\telse if ((player_items[i] & IT_GUN7) == IT_GUN7)\n\t\t\t\t\tbestweap = \"lg  \";\n\t\t\t\telse if ((player_items[i] & IT_GUN6) == IT_GUN6)\n\t\t\t\t\tbestweap = \"rl  \";\n\t\t\t\telse if ((player_items[i] & IT_GUN5) == IT_GUN5)\n\t\t\t\t\tbestweap = \"gl  \";\n\t\t\t\telse if ((player_items[i] & IT_GUN4) == IT_GUN4)\n\t\t\t\t\tbestweap = \"sng \";\n\t\t\t\telse if ((player_items[i] & IT_GUN2) == IT_GUN2)\n\t\t\t\t\tbestweap = \"ssg \";\n\n\t\t\t\tGetLocationName(player_location[i], loc, sizeof(loc));\n\n\t\t\t\t// Format spacing\n\n\t\t\t\t// Nicknames\n\t\t\t\tfor (j = 0; j < (player_nicklength - strlen(player_nick[i]) + 1) && j < sizeof(spacing); j++)\n\t\t\t\t\tspacing[j] = ' ';\n\t\t\t\tspacing[j] = '\\0';\n\n\t\t\t\t// Armor\n\t\t\t\tif (player_armor[i] % 10 == player_armor[i])\n\t\t\t\t\tstrlcpy(spacing_a, \"  \", sizeof(spacing_a));\t// 0 - 9\n\t\t\t\telse if (player_armor[i] % 100 == player_armor[i])\n\t\t\t\t\tstrlcpy(spacing_a, \" \", sizeof(spacing_a));\t// 10 - 99\n\t\t\t\telse\n\t\t\t\t\tstrlcpy(spacing_a, \"\", sizeof(spacing_a));\n\n\t\t\t\t// Health\n\t\t\t\tif (player_health[i] % 10 == player_health[i])\n\t\t\t\t\tstrlcpy(spacing_h, \"  \", sizeof(spacing_h));\t// 0 - 9\n\t\t\t\telse if (player_health[i] % 100 == player_health[i])\n\t\t\t\t\tstrlcpy(spacing_h, \" \", sizeof(spacing_h));\t// 10 - 99\n\t\t\t\telse\n\t\t\t\t\tstrlcpy(spacing_h, \"\", sizeof(spacing_h));\n\n\t\t\t\t// TODO: Translate $5 and similar macros in loc names.\n\t\t\t\tsnprintf(str, sizeof(str), \"%s%c%s%s%c%d%c%d %s%s%c%s%c\",\n\t\t\t\t\tplayer_nick[i],\t\t// player netname\n\t\t\t\t\t':'+128,\t\t\t// colored colon\n\t\t\t\t\tspacing,\t\t\t// spacing\n\t\t\t\t\tspacing_a,\t\t\t// armor spacing\n\t\t\t\t\tarmortype,\t\t\t// armor type: r, y, g or none\n\t\t\t\t\tplayer_armor[i],\t// current armor\n\t\t\t\t\t'/'+128,\t\t\t// colored slash\n\t\t\t\t\tplayer_health[i],\t// current health\n\t\t\t\t\tspacing_h,\t\t\t// health spacing\n\t\t\t\t\tbestweap,\t\t\t// best weapon\n\t\t\t\t\t'\\x10',\t\t\t\t// left bracket\n\t\t\t\t\tloc,\t\t\t\t// player location\n\t\t\t\t\t'\\x11'\t\t\t\t// right bracket\n\t\t\t\t\t);\n\n\t\t\t\tUI_DrawString(str, 0, offset);\n\t\t\t\toffset += 16;\n\t\t\t}\n\t\t}\n\t}\n}\n\nvoid Hud_TeamOverlayUpdate(void)\n{\n\tint user;\n\tchar str[256];\n\n\t// Number in the players[] array\n\tCmd_Argv(1, str, sizeof(str));\n\tuser = atoi(str);\n\n\t// Position in the x-axis\n\tCmd_Argv(2, str, sizeof(str));\n\tplayer_location[user][0] = atoi(str);\n\n\t// Position in the y-axis\n\tCmd_Argv(3, str, sizeof(str));\n\tplayer_location[user][1] = atoi(str);\n\n\t// Position in the z-axis\n\tCmd_Argv(4, str, sizeof(str));\n\tplayer_location[user][2] = atoi(str);\n\n\t// Player health\n\tCmd_Argv(5, str, sizeof(str));\n\tplayer_health[user] = atoi(str);\n\n\t// Player armor\n\tCmd_Argv(6, str, sizeof(str));\n\tplayer_armor[user] = atoi(str);\n\n\t// Player item bitmask\n\tCmd_Argv(7, str, sizeof(str));\n\tplayer_items[user] = atoi(str);\n\n\t// Setting this nick will make this info print\n\tplayer_nick[user] = players[user].name;\n\n\t// Check who has the longest nick for a better overlay format\n\tif (strlen(player_nick[user]) > player_nicklength)\n\t\tplayer_nicklength = strlen(player_nick[user]);\n\n\treturn;\n}\n\n//fixme: draw dark blobs\nvoid Hud_Blackness(void)\n{\n\tDraw_Colour4f(0, 0, 0, (sbartype+1)/10.0f);\n\n\tif (hudedit)\n\t{\n\t\tif (sbarindex == currentitem)\n\t\t{\n\t\t\tfloat j = ((currenttime % 1000) - 500) / 500.0f;\n\t\t\tif (j < 0)\n\t\t\t\tj = -j;\n\t\t\tj/=3;\n\n\t\t\tDraw_Colour4f(j, 0, 0, (sbartype+1)/10.0f);\n\t\t}\n\t\telse if (sbarindex == hoveritem)\n\t\t{\n\t\t\tDraw_Colour4f(0.0, 0.2, 0.0, (sbartype+1)/10.0f);\n\t\t}\n\t}\n\tDraw_Fill(sbarminx, sbarminy, (float)16*sbarscalex, (float)16*sbarscaley);\n\tDraw_Colour4f(1,1,1,1);\n}\n\nvoid Hud_Tracking(void)\n{\n\tqboolean flag = false;\n\tchar str[256];\n\n\tif (hudedit)\n\t{\n\t\tUI_DrawString(\"Tracking ...\", 0, 0);\n\t\treturn;\n\t}\n\n\t// FIXME: Need a check here to return if we are not spectating\n\n\t// Print it\n\tsnprintf(str, sizeof(str), \"Tracking %s\", players[trackedplayer].name);\n\tUI_DrawString(str, 0, 0);\n}\n\nvoid UI_DrawHandles(int *arg, int i)\n{\n\tint mt;\n\tfloat vsx, vsy;\n\tvsx = arg[3]/640.0f;\n\tvsy = arg[4]/480.0f;\n\n\tsbarminx = arg[1] + element[i].x*vsx;\n\tsbarminy = arg[2] + element[i].y*vsy;\n\tsbarscalex  = element[i].scalex*vsx;\n\tsbarscaley  = element[i].scaley*vsy;\n\tmt = element[i].type;\n\tsbartype = element[i].subtype;\n\tsbaralpha = element[i].alpha;\n\n\tDraw_Colour4f(1, 0, 0, 1);\n\tDraw_Fill(sbarminx+drawelement[mt].width*sbarscalex-((sbarscalex<0)?0:(vsx*4)), sbarminy-((sbarscaley>=0)?0:(vsy*4)), (float)4*vsx, (float)4*vsy);\n\n\tDraw_Colour4f(0, 1, 0, 1);\n\tDraw_Fill(sbarminx+drawelement[mt].width*sbarscalex-((sbarscalex<0)?0:(vsx*4)), sbarminy+drawelement[mt].height*sbarscaley-((sbarscaley<0)?0:(vsy*4)), (float)4*vsx, (float)4*vsy);\n}\n\n//draw body of sbar\n//arg[0] is playernum\n//arg[1]/arg[2] is x/y start of subwindow\n//arg[3]/arg[4] is width/height of subwindow\nint UI_StatusBar(int *arg)\n{\n\tint i;\n\n\tfloat vsx, vsy;\n\n\tif (hudedit) // don't redraw twice\n\t\treturn 1;\n\n\tif (arg[5])\n\t\treturn 1;\n\n\tCL_GetStats(arg[0], stats, sizeof(stats)/sizeof(int));\n\n\tif (stats[STAT_HEALTH] <= 0)\n\t\treturn 1;\n\n\tvsx = arg[3]/640.0f;\n\tvsy = arg[4]/480.0f;\n\tfor (i = 0; i < numelements; i++)\n\t{\n\t\tsbarminx = arg[1] + element[i].x*vsx;\n\t\tsbarminy = arg[2] + element[i].y*vsy;\n\t\tsbarscalex  = element[i].scalex*vsx;\n\t\tsbarscaley  = element[i].scaley*vsy;\n\t\tsbartype = element[i].subtype;\n\t\tsbaralpha = element[i].alpha;\n\t\tdrawelement[element[i].type].draw();\n\t}\n\n\treturn 1;\n}\n\nint UI_StatusBarEdit(int *arg) // seperated so further improvements to editor view can be done\n{\n\tint i;\n\n\tfloat vsx, vsy;\n\tqboolean clrset = false;\n\n\tCL_GetStats(arg[0], stats, sizeof(stats)/sizeof(int));\n\n\tvsx = arg[3]/640.0f;\n\tvsy = arg[4]/480.0f;\n\tfor (i = 0; i < numelements; i++)\n\t{\n\t\tif (i == currentitem)\n\t\t{\n\t\t\tfloat j = ((currenttime % 1000) - 500) / 500.0f;\n\t\t\tif (j < 0)\n\t\t\t\tj = -j;\n\n\t\t\tDraw_Colour3f(1.0, j, j);\n\t\t\tclrset = true;\n\t\t}\n\t\telse if (i == hoveritem)\n\t\t{\n\t\t\tDraw_Colour3f(0.0, 1.0, 0.0);\n\t\t\tclrset = true;\n\t\t}\n\n\t\tsbarminx = arg[1] + element[i].x*vsx;\n\t\tsbarminy = arg[2] + element[i].y*vsy;\n\t\tsbarscalex  = element[i].scalex*vsx;\n\t\tsbarscaley  = element[i].scaley*vsy;\n\t\tsbartype = element[i].subtype;\n\t\tsbaralpha = element[i].alpha;\n\t\tsbarindex = i;\n\t\tdrawelement[element[i].type].draw();\n\n\t\tif (clrset)\n\t\t{\n\t\t\tDraw_Colour3f(1.0, 1.0, 1.0);\n\t\t\tclrset = false;\n\t\t}\n\t}\n\n\treturn true;\n}\n\n/*\nint UI_ScoreBoard(int *arg)\n{\n\tint i;\n\n\tif (!arg[5])\n\t\treturn false;\n\n\tsbarminx = 320;\n\tsbarminy = 48;\n\tsbarscalex = 1;\n\tsbarscaley = 1;\n\tsbaralpha = 1;\n\n\tSortPlayers();\n\tfor (i = 0; i < numsortedplayers; i++)\n\t{\n\t\tsbartype = i;\n\t\tHud_ScoreCard();\n\t\tUI_DrawString(players[sortedplayers[i]].name, 40, 0);\n\n\t\tsbarminy += 16;\n\t}\n\n\treturn true;\n}\n*/\n\n#define HUD_VERSION 52345\nvoid PutFloat(float f, char sep, qhandle_t handle)\n{\n\tchar *buffer;\n\tbuffer = va(\"%f%c\", f, sep);\n\tFS_Write(handle, buffer, strlen(buffer));\n}\nvoid PutInteger(int i, char sep, qhandle_t handle)\n{\n\tchar *buffer;\n\tbuffer = va(\"%i%c\", i, sep);\n\tFS_Write(handle, buffer, strlen(buffer));\n}\n\nvoid Hud_Save(char *fname)\n{\n\tchar name[256];\n\tint i;\n\tqhandle_t handle;\n\tif (!fname || !*fname)\n\t\tfname = DEFAULTHUDNAME;\n\tsnprintf(name, sizeof(name)-5, \"huds/%s\", fname);\n\tCOM_DefaultExtension(name, \".hud\", sizeof(name));\n\tif (FS_Open(name, &handle, 2)<0)\n\t{\n\t\tCon_Printf(\"Couldn't open %s\\n\", name);\n\t\treturn;\n\t}\n\n\tPutInteger(HUD_VERSION, '\\n', handle);\n\tPutInteger(numelements, '\\n', handle);\n\tfor (i = 0; i < numelements; i++)\n\t{\n\t\tPutFloat(element[i].x, ' ', handle);\n\t\tPutFloat(element[i].y, ' ', handle);\n\t\tPutFloat(element[i].scalex, ' ', handle);\n\t\tPutFloat(element[i].scaley, ' ', handle);\n\t\tPutInteger(element[i].type, ' ', handle);\n\t\tPutInteger(element[i].subtype, ' ', handle);\n\t\tPutFloat(element[i].alpha, '\\n', handle);\n\t}\n\n\tFS_Close(handle);\n}\nfloat GetFloat(char **f, qhandle_t handle)\n{\n\tchar *ts;\n\twhile(**f <= ' ' && **f != 0)\n\t\t(*f)++;\n\twhile(*f[0] == '/' && *f[1] == '/')\n\t{\n\t\twhile(**f != '\\n' && **f != 0)\n\t\t\t(*f)++;\n\t\twhile(**f <= ' ' && **f != 0)\n\t\t\t(*f)++;\n\t}\n\tts = *f;\n\twhile (**f>' ')\n\t\t(*f)++;\n\n\treturn (float)atof(ts);\n}\nint GetInteger(char **f, qhandle_t handle)\n{\n\tchar *ts;\n\twhile(**f <= ' ' && **f != 0)\n\t\t(*f)++;\n\twhile(*f[0] == '/' && *f[1] == '/')\n\t{\n\t\twhile(**f != '\\n' && **f != 0)\n\t\t\t(*f)++;\n\t\twhile(**f <= ' ' && **f != 0)\n\t\t\t(*f)++;\n\t}\n\tts = *f;\n\twhile (**f>' ')\n\t\t(*f)++;\n\n\treturn atoi(ts);\n}\nvoid Hud_Load(char *fname)\n{\n\tchar file[16384];\n\tchar name[256];\n\tchar *p;\n\tint len;\n\tint i;\n\tqhandle_t handle;\n\tint ver;\n\n\tfloat x, y, sx, sy, a;\n\tint type, subtype;\n\n\tif (!fname || !*fname)\n\t\tfname = DEFAULTHUDNAME;\n\tsnprintf(name, sizeof(name)-5, \"huds/%s\", fname);\n\tCOM_DefaultExtension(name, \".hud\", sizeof(name));\n\tlen = FS_Open(name, &handle, 1);\n\tif (len < 0)\n\t{\n\t\tCon_Printf(\"Couldn't load file\\n\");\n\t\treturn;\n\t}\n\tif (len > 16383)\n\t\tlen = 16383;\n\tFS_Read(handle, file, len);\n\tfile[len] = 0;\n\tFS_Close(handle);\n\n\tp = file;\n\n\tver = GetInteger(&p, handle);\n\tif (ver != HUD_VERSION)\n\t{\n\t\tCon_Printf(\"Hud version doesn't match (%i != %i)\\n\", ver, HUD_VERSION);\n\t\treturn;\n\t}\n\tnumelements = GetInteger(&p, handle);\n\tif (numelements > MAX_ELEMENTS)\n\t{\n\t\tnumelements = 0;\n\t\tCon_Printf(\"Hud has too many elements\\n\");\n\t\treturn;\n\t}\n\tfor (i = 0; i < numelements; i++)\n\t{\n\t\tx = GetFloat(&p, handle);\n\t\ty = GetFloat(&p, handle);\n\t\tsx = GetFloat(&p, handle);\n\t\tsy = GetFloat(&p, handle);\n\t\ttype = GetInteger(&p, handle);\n\t\tsubtype = GetInteger(&p, handle);\n\t\ta = GetFloat(&p, handle);\n\n\t\tif (type<0 || type>=sizeof(drawelement)/sizeof(drawelement[0]))\n\t\t{\n\t\t\tnumelements--;\n\t\t\ti--;\n\t\t\tcontinue;\n\t\t}\n\n\t\telement[i].x = x;\n\t\telement[i].y = y;\n\t\telement[i].scalex = sx;\n\t\telement[i].scaley = sy;\n\t\telement[i].alpha = a;\n\t\telement[i].type = type;\n\t\telement[i].subtype = subtype;\n\t}\n\n\tcurrentitem = -1;\n}\n\n// FindItemUnderMouse: given mouse coordinates, finds element number under mouse\n// returns -1 if no element found\nint FindItemUnderMouse(int mx, int my)\n{\n\tint i;\n\tint rv;\n\n\trv = -1;\n\n\tfor (i = 0; i < numelements; i++)\n\t{\n\t\tif (element[i].scalex < 0)\n\t\t{\n\t\t\tif (element[i].x < mx)\n\t\t\t\tcontinue;\n\t\t\tif (element[i].x + element[i].scalex*drawelement[element[i].type].width > mx)\n\t\t\t\tcontinue;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (element[i].x > mx)\n\t\t\t\tcontinue;\n\t\t\tif (element[i].x + element[i].scalex*drawelement[element[i].type].width < mx)\n\t\t\t\tcontinue;\n\t\t}\n\t\tif (element[i].scaley < 0)\n\t\t{\n\t\t\tif (element[i].y < my)\n\t\t\t\tcontinue;\n\t\t\tif (element[i].y + element[i].scaley*drawelement[element[i].type].height > my)\n\t\t\t\tcontinue;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (element[i].y > my)\n\t\t\t\tcontinue;\n\t\t\tif (element[i].y + element[i].scaley*drawelement[element[i].type].height < my)\n\t\t\t\tcontinue;\n\t\t}\n\n\t\trv = i;\n\t}\n\n\treturn rv; // no element found\n}\n\nvoid DrawContextMenu(int mx, int my)\n{\n\tint y;\n\tDraw_Colour4f(0, 0, 0, 0.4);\n\tDraw_Fill((mouseofsx-8)*sbarscalex, (mouseofsy-8)*sbarscaley, (float)112*sbarscalex, (float)(9*8)*sbarscaley);\n\tDraw_Colour4f(1,1,1,1);\n\n\tsbarminx = mouseofsx*sbarscalex;\n\tsbarminy = mouseofsy*sbarscaley;\n\n\tmy -= mouseofsy;\n\tmy/=8;\n\tmy--;\n\n\tmx -= mouseofsx;\n\n\tif (mx < 0)\n\t\tmy = -10;\n\tif (mx > 12*8)\n\t\tmy = -10;\n\n\ty = 0;\n\tUI_DrawAltString(\"CONTEXT MENU\", 0, y, 1);\n\ty+=8;\n\tUI_DrawAltString(\"------------\", 0, y, 0);\n\ty+=8;\n\tUI_DrawAltString(\"New\", 0, y, (my--)==1);\n\ty+=8;\n\tUI_DrawAltString(\"Snap To Grid\", 0, y, (my--)==1);\n\tif (shiftdown)\n\t\tUI_DrawAltString(\"X\", -8, y, (my)==1);\n\ty+=8;\n\tUI_DrawAltString(\"Save\", 0, y, (my--)==1);\n\ty+=8;\n\tUI_DrawAltString(\"Reload\", 0, y, (my--)==1);\n\ty+=8;\n\tUI_DrawAltString(\"Defaults\", 0, y, (my--)==1);\n\ty+=8;\n}\n\nvoid DrawPrimaryCreationMenu(int mx, int my)\n{\n\tint i;\n\tint y;\n\tint numopts;\n\tnumopts = sizeof(drawelement)/sizeof(drawelement[0]);\n\tnumopts += 2;\n\tDraw_Colour4f(0, 0, 0, 0.4);\n\tDraw_Fill((mouseofsx-8)*sbarscalex, (mouseofsy-8)*sbarscaley, (float)(17*8)*sbarscalex, (float)((numopts+2)*8)*sbarscaley);\n\tDraw_Colour4f(1,1,1,1);\n\n\tsbarminx = mouseofsx*sbarscalex;\n\tsbarminy = mouseofsy*sbarscaley;\n\n\tmy -= mouseofsy;\n\tmy/=8;\n\tmy--;\n\n\tmx -= mouseofsx;\n\n\tif (mx < 0)\n\t\tmy = -10;\n\tif (mx > 12*8)\n\t\tmy = -10;\n\n\ty = 0;\n\tUI_DrawAltString(\"CREATE NEW ITEM\", 0, y, 1);\n\ty+=8;\n\tUI_DrawAltString(\"------------\", 0, y, 0);\n\ty+=8;\n\n\n\tfor (i = 0; i < sizeof(drawelement)/sizeof(drawelement[0]); i++)\n\t{\n\t\tUI_DrawAltString(drawelement[i].name, 0, y, (my--)==1);\n\t\ty+=8;\n\t}\n}\n\nvoid DrawSecondaryCreationMenu(int mx, int my)\n{\n\tint i;\n\tint y;\n\tint numopts;\n\tnumopts = drawelement[typetoinsert].maxsubtype+1;\n\tnumopts += 2;\n\tDraw_Colour4f(0, 0, 0, 0.4);\n\tDraw_Fill((mouseofsx-8)*sbarscalex, (mouseofsy-8)*sbarscaley, (float)(17*8)*sbarscalex, (float)((numopts+2)*8)*sbarscaley);\n\tDraw_Colour4f(1,1,1,1);\n\n\tsbarminx = mouseofsx*sbarscalex;\n\tsbarminy = mouseofsy*sbarscaley;\n\n\tmy -= mouseofsy;\n\tmy/=8;\n\tmy--;\n\n\tmx -= mouseofsx;\n\n\tif (mx < 0)\n\t\tmy = -10;\n\tif (mx > 12*8)\n\t\tmy = -10;\n\n\ty = 0;\n\tUI_DrawAltString(\"CREATE NEW ITEM\", 0, y, 1);\n\ty+=8;\n\tUI_DrawAltString(\"------------\", 0, y, 0);\n\ty+=8;\n\n\n\tfor (i = 0; i <= drawelement[typetoinsert].maxsubtype; i++)\n\t{\n\t\tUI_DrawAltString(drawelement[typetoinsert].subtypename[i].name, 0, y, (my--)==1);\n\t\ty+=8;\n\t}\n}\n\nvoid UI_KeyPress(int key, int mx, int my)\n{\n\tint i;\n\tif (key == K_ESCAPE)\n\t{\n\t\tMenu_Control(0);\n\t\treturn;\n\t}\n\n\tif (context)\n\t{\n\t\tif (key != K_MOUSE1)\n\t\t{\n\t\t\tcontext = false;\n\t\t\treturn;\n\t\t}\n\n\t\tmx -= mouseofsx;\n\t\tif (mx < 0)\n\t\t\tmy = -10;\n\t\tif (mx > 12*8)\n\t\t\tmy = -10;\n\n\t\ti = my - mouseofsy;\n\t\ti /= 8;\n\n\t\tif (context == 3)\n\t\t{\n\t\t\tcontext = false;\n\t\t\tif ((unsigned)(i-2) > (unsigned)drawelement[typetoinsert].maxsubtype)\n\t\t\t\treturn;\n\t\t\tcurrentitem = numelements;\n\t\t\tnumelements++;\n\n\t\t\tif (typetoinsert == 12)\n\t\t\t{\n\t\t\t\tint j;\n\n\t\t\t\t// Blackness should be sent to the back\n\t\t\t\tfor (j = currentitem; j > 0; j--)\n\t\t\t\t{\n\t\t\t\t\telement[j] = element[j-1];\n\t\t\t\t}\n\n\t\t\t\tcurrentitem = 0;\n\t\t\t}\n\t\t\telement[currentitem].type = typetoinsert;\n\t\t\telement[currentitem].alpha = 1;\n\t\t\telement[currentitem].scalex = 1;\n\t\t\telement[currentitem].scaley = 1;\n\n\t\t\telement[currentitem].x = 320;\n\t\t\telement[currentitem].y = 240;\n\t\t\telement[currentitem].subtype = i-2;\n\t\t}\n\t\telse if (context == 2)\n\t\t{\n\t\t\ttypetoinsert = i-2;\n\t\t\tif ((unsigned)typetoinsert >= sizeof(drawelement)/sizeof(drawelement[0]))\n\t\t\t{\n\t\t\t\tcontext = false;\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (drawelement[typetoinsert].maxsubtype != 0)\n\t\t\t\tcontext = 3;\n\t\t\telse\n\t\t\t{\n\t\t\t\tcontext = false;\n\t\t\t\tcurrentitem = numelements;\n\t\t\t\tnumelements++;\n\n\t\t\t\telement[currentitem].type = i-2;\n\t\t\t\telement[currentitem].alpha = 1;\n\t\t\t\telement[currentitem].scalex = 1;\n\t\t\t\telement[currentitem].scaley = 1;\n\n\t\t\t\telement[currentitem].x = 320;\n\t\t\t\telement[currentitem].y = 240;\n\t\t\t\telement[currentitem].subtype = 0;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tcontext = false;\n\t\t\tswitch(i)\n\t\t\t{\n\t\t\tcase 2:\t//clone\n\t\t\t\tif (numelements==MAX_ELEMENTS)\n\t\t\t\t\treturn;\t//too many\n\t\t\t\t/*\n\t\t\t\tmemcpy(element+numelements, element+currentitem, sizeof(hudelement_t));\n\t\t\t\tcurrentitem = numelements;\n\t\t\t\tnumelements++;\n\t\t\t\t*/\n\t\t\t\tcontext = 2;\n\t\t\t\tbreak;\n\t\t\tcase 3: //snap\n\t\t\t\tshiftdown ^= 1;\n\t\t\t\tbreak;\n\t\t\tcase 4: //save\n\t\t\t\tHud_Save(NULL);\n\t\t\t\tbreak;\n\t\t\tcase 5: //reload\n\t\t\t\tHud_Load(NULL);\n\t\t\t\tbreak;\n\t\t\tcase 6: //defaults\n\t\t\t\tSBar_FlushAll();\n\t\t\t\tSBar_ReloadDefaults();\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\treturn;\n\t}\n\n\tif (key == K_MOUSE1)\n\t{\t//figure out which one our cursor is over...\n\t\tmousedown = false;\n\n\t\ti = FindItemUnderMouse(mx, my);\n\t\tif (i != -1)\n\t\t{\n\t\t\tint oldcurrent;\n\t\t\tfloat big;\n\t\t\toldcurrent = currentitem;\n\t\t\tcurrentitem = i;\n\n\t\t\tmouseofsx = mx - element[i].x;\n\t\t\tmouseofsy = my - element[i].y;\n\t\t\tmousedown |= 1;\n\n\t\t\tif (element[i].scalex < 0)\n\t\t\t{\n\t\t\t\tif (mx > element[i].x+4)\n\t\t\t\t\treturn;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tbig = element[i].scalex*drawelement[element[i].type].width;\n\t\t\t\tif (mx-element[i].x+4 < big)\n\t\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (my < element[i].y+4)\n\t\t\t{\n\t\t\t\tif (currentitem == oldcurrent)\n\t\t\t\t\tUI_KeyPress('d', 0, 0);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (element[i].scaley < 0)\n\t\t\t{\n\t\t\t\tif (my > element[i].y+4)\n\t\t\t\t\treturn;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tbig = element[i].scaley*drawelement[element[i].type].height;\n\t\t\t\tif (my-element[i].y+4 < big)\n\t\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tmouseofsx = mx - element[i].scalex*drawelement[element[i].type].width;\n\t\t\tmouseofsy = my - element[i].scaley*drawelement[element[i].type].height;\n\t\t\tmousedown |= 2;\n\t\t}\n\t\telse\n\t\t\tcurrentitem = -1;\n\t}\n\n\t// TODO: extra buttons\n\t// - toggle clip to edges and clip to other controls\n\t// - maybe toggle snap to grid instead of holding shift with mouse?\n\n\telse if (key == K_MOUSE2)\n\t{\n\t\tmousedown = false;\t//perhaps not logically true, but it's safest this way.\n\t\tcontext = true;\n\n\t\tmouseofsx = mx;\n\t\tmouseofsy = my;\n\t}\n\telse if (key == 'n')\n\t{\n\t\tcurrentitem++;\n\t\tif (currentitem >= numelements)\n\t\t\tcurrentitem = 0;\n\t}\n\telse if (key == 'm')\n\t{\n\t\tcurrentitem--;\n\t\tif (currentitem < 0)\n\t\t\tcurrentitem = numelements ? numelements - 1 : 0;\n\t}\n\telse if (key == 'i')\n\t{\n\t\tif (numelements==MAX_ELEMENTS)\n\t\t\treturn;\t//too many\n\n\t\telement[numelements].scalex = 1;\n\t\telement[numelements].scaley = 1;\n\t\telement[numelements].alpha = 1;\n\t\tnumelements++;\n\t}\n\telse if (key == K_SHIFT)\n\t\tshiftdown = true;\n\telse if (currentitem < numelements && currentitem != -1)\n\t{\n\t\tif (key == 'd')\n\t\t{\n\t\t\tmousedown = false;\n\t\t\tmemcpy(element+currentitem, element+currentitem+1, sizeof(element[0]) * (numelements - currentitem-1));\n\t\t\tnumelements--;\n\t\t\tcurrentitem = -1;\n\t\t}\n\t\telse if (key == 'c' || key == 'C')\n\t\t{\n\t\t\tif (numelements==MAX_ELEMENTS)\n\t\t\t\treturn;\t//too many\n\t\t\tmemcpy(element+numelements, element+currentitem, sizeof(hudelement_t));\n\t\t\tcurrentitem = numelements;\n\t\t\tnumelements++;\n\t\t}\n\t\telse if (key == K_PAGEUP)\n\t\t{\t//send to back\n\t\t\thudelement_t temp;\n\n\t\t\tmemcpy(&temp, element+currentitem, sizeof(temp));\n\t\t\tmemmove(element+1, element, sizeof(hudelement_t) * (currentitem));\n\t\t\tmemcpy(element, &temp, sizeof(hudelement_t));\n\t\t\tcurrentitem = 0;\n\t\t}\n\t\telse if (key == K_PAGEDOWN)\n\t\t{\t//bring to front\n\t\t\thudelement_t temp;\n\n\t\t\tmemcpy(&temp, element+currentitem, sizeof(temp));\n\t\t\tmemcpy(element+currentitem, element+currentitem+1, sizeof(element[0]) * (numelements - currentitem-1));\n\t\t\tcurrentitem = numelements - 1;\n\t\t\tmemcpy(element+currentitem , &temp, sizeof(hudelement_t));\n\t\t}\n\t\telse if (key == 'q')\n\t\t{\n\t\t\telement[currentitem].type--;\n\t\t\tif (element[currentitem].type < 0)\n\t\t\t\telement[currentitem].type = sizeof(drawelement)/sizeof(drawelement[0])-1;\n\t\t}\n\t\telse if (key == 'w')\n\t\t{\n\t\t\telement[currentitem].type++;\n\t\t\tif (element[currentitem].type >= sizeof(drawelement)/sizeof(drawelement[0]))\n\t\t\t\telement[currentitem].type = 0;\n\t\t}\n\t\telse if (key == ',' || key == K_MWHEELUP)\n\t\t{\n\t\t\telement[currentitem].subtype--;\n\t\t\tif (element[currentitem].subtype < 0)\n\t\t\t\telement[currentitem].subtype = drawelement[element[currentitem].type].maxsubtype;\n\t\t}\n\t\telse if (key == '.' || key == K_MWHEELDOWN)\n\t\t{\n\t\t\telement[currentitem].subtype++;\n\t\t\tif (element[currentitem].subtype > drawelement[element[currentitem].type].maxsubtype)\n\t\t\t\telement[currentitem].subtype = 0;\n\t\t}\n\t\telse if (key == K_UPARROW)\n\t\t{\n\t\t\telement[currentitem].y-=shiftdown?8:1;\n\t\t}\n\t\telse if (key == K_DOWNARROW)\n\t\t{\n\t\t\telement[currentitem].y+=shiftdown?8:1;\n\t\t}\n\t\telse if (key == K_LEFTARROW)\n\t\t{\n\t\t\telement[currentitem].x-=shiftdown?8:1;\n\t\t}\n\t\telse if (key == K_RIGHTARROW)\n\t\t{\n\t\t\telement[currentitem].x+=shiftdown?8:1;\n\t\t}\n\t\telse if (key == K_HOME)\n\t\t{\n\t\t\telement[currentitem].scalex=1.0f;\n\t\t\telement[currentitem].scaley=1.0f;\n\t\t\telement[currentitem].alpha=1.0f;\n\t\t}\n\t\telse if (key == '+')\n\t\t{\n\t\t\telement[currentitem].scalex*=1.1f;\n\t\t\telement[currentitem].scaley*=1.1f;\n\t\t}\n\t\telse if (key == '-')\n\t\t{\n\t\t\telement[currentitem].scalex/=1.1f;\n\t\t\telement[currentitem].scaley/=1.1f;\n\t\t}\n\t}\n}\n\nint Plug_MenuEvent(int *args)\n{\n\tint altargs[5];\n\tfloat cursorbias;\n\tfloat cursorsize;\n\n\targs[2]=(int)(args[2]*640.0f/vid.width);\n\targs[3]=(int)(args[3]*480.0f/vid.height);\n\n\tswitch(args[0])\n\t{\n\tcase 0:\t//draw\n\n\t\t// TODO: some sort of element property display\n\t\tif (context)\n\t\t{\n\t\t}\n\t\telse if (mousedown)\n\t\t{\n\t\t\tif (mousedown & 2)\t//2 is 'on the scaler'\n\t\t\t{\n\t\t\t\tfloat w = args[2] - mouseofsx;\n\t\t\t\tfloat h = args[3] - mouseofsy;\n\t\t\t\tif (shiftdown || (mousedown & 4))\t//4 is mouse2\n\t\t\t\t{\n\t\t\t\t\tw -= (int)w & 7;\n\t\t\t\t\th -= (int)h & 7;\n\t\t\t\t}\n\t\t\t\tif (w < 8 && w > -8)\n\t\t\t\t\tw = 8;\n\t\t\t\tif (h < 8 && h > -8)\n\t\t\t\t\th = 8;\n\t\t\t\telement[currentitem].scalex = w/drawelement[element[currentitem].type].width;\n\t\t\t\telement[currentitem].scaley = h/drawelement[element[currentitem].type].height;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\telement[currentitem].x = args[2] - mouseofsx;\n\t\t\t\telement[currentitem].y = args[3] - mouseofsy;\n\t\t\t\tif (shiftdown || (mousedown & 4))\t//4 is mouse2\n\t\t\t\t{\n\t\t\t\t\telement[currentitem].x -= (int)element[currentitem].x & 7;\n\t\t\t\t\telement[currentitem].y -= (int)element[currentitem].y & 7;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse\n\t\t\thoveritem = FindItemUnderMouse(args[2], args[3]); // this could possibly slow some things down...\n\n\t\taltargs[0] = 0;\n\t\taltargs[1] = 0;\n\t\taltargs[2] = 0;\n\t\taltargs[3] = vid.width;\n\t\taltargs[4] = vid.height;\n\t\tif (hudedit)\n\t\t\tUI_StatusBarEdit(altargs);\n//\t\telse\n//\t\t\tUI_StatusBar(altargs);\t//draw it using the same function (we're lazy)\n\n\t\tif (currentitem >= 0)\n\t\t\tUI_DrawHandles(altargs, currentitem);\n\n\t\tsbarscalex = vid.width/640.0f;\n\t\tsbarscaley = vid.height/480.0f;\n\t\tswitch (context)\n\t\t{\n\t\tcase 1:\n\t\t\tDrawContextMenu(args[2], args[3]);\n\t\t\tbreak;\n\t\tcase 2:\n\t\t\tDrawPrimaryCreationMenu(args[2], args[3]);\n\t\t\tbreak;\n\t\tcase 3:\n\t\t\tDrawSecondaryCreationMenu(args[2], args[3]);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tbreak;\n\t\t}\n\t\tsbarminx = args[2];\n\t\tsbarminy = args[3];\n\n\t\tcursorbias = Cvar_GetFloat(\"cl_cursorbias\");\n\t\tcursorsize = Cvar_GetFloat(\"cl_cursorsize\");\n\n\t\tDraw_Colour4f(1,1,1,1);\n\t\tDraw_Image(((float)args[2]*sbarscalex)-cursorbias, ((float)args[3]*sbarscaley)-cursorbias, (float)cursorsize*sbarscalex, (float)cursorsize*sbarscaley, 0, 0, 1, 1, pic_cursor);\n\t\tbreak;\n\tcase 1:\t//keydown\n\t\tUI_KeyPress(args[1], args[2], args[3]);\n\t\tbreak;\n\tcase 2:\t//keyup\n\t\tif (args[1] == K_MOUSE1)\n\t\t\tmousedown = false;\n\t\telse if (args[1] == K_SHIFT)\n\t\t\tshiftdown = false;\n\t\tbreak;\n\tcase 3:\t//menu closed (this is called even if we change it).\n\t\thudedit = false;\n\t\tbreak;\n\tcase 4:\t//mousemove\n\t\tbreak;\n\t}\n\n\treturn 0;\n}\n\nint Plug_Tick(int *args)\n{\n\tcurrenttime = args[0];\n\treturn true;\n}\n\nint Plug_ExecuteCommand(int *args)\n{\n\tchar cmd[256];\n\tCmd_Argv(0, cmd, sizeof(cmd));\n\tif (!strcmp(\"sbar_edit\", cmd) || !strcmp(\"hud_edit\", cmd))\n\t{\n\t\tMenu_Control(1);\n\t\tmousedown=false;\n\t\thudedit=true;\n\t\treturn 1;\n\t}\n\tif (!strcmp(\"sbar_save\", cmd) || !strcmp(\"hud_save\", cmd))\n\t{\n\t\tCmd_Argv(1, cmd, sizeof(cmd));\n\t\tHud_Save(cmd);\n\t\tmousedown=false;\n\t\treturn 1;\n\t}\n\tif (!strcmp(\"sbar_load\", cmd) || !strcmp(\"hud_load\", cmd))\n\t{\n\t\tCmd_Argv(1, cmd, sizeof(cmd));\n\t\tHud_Load(cmd);\n\t\tmousedown=false;\n\t\treturn 1;\n\t}\n\tif (!strcmp(\"sbar_defaults\", cmd) || !strcmp(\"hud_defaults\", cmd))\n\t{\n\t\tCmd_Argv(1, cmd, sizeof(cmd));\n\t\tSBar_FlushAll();\n\t\tSBar_ReloadDefaults();\n\t\tmousedown=false;\n\t\treturn 1;\n\t}\n\t// Modify a HUD element\n\tif (!strcmp(\"sbar\", cmd) || !strcmp(\"hud\", cmd))\n\t{\n\t\t// FIXME: add this command\n\t\treturn 1;\n\t}\n\t// Support for KTX team overlay\n\tif (!strcmp(\"tinfo\", cmd))\n\t{\n\t\tHud_TeamOverlayUpdate();\n\t\treturn 1;\n\t}\n\treturn 0;\n}\n\nint Plug_Init(int *args)\n{\n\tif (Plug_Export(\"Tick\", Plug_Tick) &&\n\t\tPlug_Export(\"SbarBase\", UI_StatusBar) &&\n//\t\tPlug_Export(\"SbarOverlay\", UI_ScoreBoard) &&\n\t\tPlug_Export(\"ExecuteCommand\", Plug_ExecuteCommand) &&\n\t\tPlug_Export(\"MenuEvent\", Plug_MenuEvent))\n\t{\n\n\t\tK_UPARROW\t\t= Key_GetKeyCode(\"uparrow\");\n\t\tK_DOWNARROW\t\t= Key_GetKeyCode(\"downarrow\");\n\t\tK_LEFTARROW\t\t= Key_GetKeyCode(\"leftarrow\");\n\t\tK_RIGHTARROW\t= Key_GetKeyCode(\"rightarrow\");\n\t\tK_ESCAPE\t\t= Key_GetKeyCode(\"escape\");\n\t\tK_HOME\t\t\t= Key_GetKeyCode(\"home\");\n\t\tK_MOUSE1\t\t= Key_GetKeyCode(\"mouse1\");\n\t\tK_MOUSE2\t\t= Key_GetKeyCode(\"mouse2\");\n\t\tK_MWHEELDOWN\t= Key_GetKeyCode(\"mwheeldown\");\n\t\tK_MWHEELUP\t\t= Key_GetKeyCode(\"mwheelup\");\n\t\tK_SHIFT\t\t\t= Key_GetKeyCode(\"shift\");\n\t\tK_PAGEUP\t\t= Key_GetKeyCode(\"pgup\");\n\t\tK_PAGEDOWN\t\t= Key_GetKeyCode(\"pgdn\");\n\n\t\tCmd_AddCommand(\"hud_edit\");\n\t\tCmd_AddCommand(\"sbar_edit\");\n\t\tif (BUILTINISVALID(FS_Write))\n\t\t{\n\t\t\tCmd_AddCommand(\"hud_save\");\n\t\t\tCmd_AddCommand(\"sbar_save\");\n\t\t}\n\t\tif (BUILTINISVALID(FS_Read))\n\t\t{\n\t\t\tCmd_AddCommand(\"hud_load\");\n\t\t\tCmd_AddCommand(\"sbar_load\");\n\t\t}\n\t\tCmd_AddCommand(\"hud_defaults\");\n\t\tCmd_AddCommand(\"sbar_defaults\");\n\t\t\n\t\t// For modifying hud elements\n\t\tCmd_AddCommand(\"hud\");\n\t\tCmd_AddCommand(\"sbar\");\n\n\t\t// Teamoverlay support\n\t\tCmd_AddCommand(\"tinfo\");\n\t\tCmd_AddText(\"newalias ktx_infoset \\\"cmd info ti 1\\\"\\n\", false);\n\n\t\tUI_SbarInit();\n\n\t\tif (BUILTINISVALID(FS_Read))\n\t\t\tHud_Load(\"\");\n\n\t\treturn 1;\n\t}\n\treturn 0;\n}\n"
  },
  {
    "path": "plugins/irc/ircclient.c",
    "content": "//Released under the terms of the gpl as this file uses a bit of quake derived code. All sections of the like are marked as such\n// changes name to while in channel\n// mode command\n// Spike can you implement nick tab completion. ~moodles\n// need option for whois on receiving PM\n// bug: setting channel to private, crashes fte when trying to join it.\n// http://www.mirc.net/raws/\n// http://www.ircle.com/reference/commands.shtml\n\n\n#include \"../plugin.h\"\nstatic plugsubconsolefuncs_t *confuncs;\nstatic plugfsfuncs_t *filefuncs;\nstatic plugnetfuncs_t *netfuncs;\nstatic plug2dfuncs_t *drawfuncs;\n#include <time.h>\n#include <ctype.h>\n#include \"../../engine/common/netinc.h\"\n\n#define handleisvalid(h) ((h)>=0)\n#define invalid_handle -1\n\nenum tlsmode_e\n{\n\tTLS_OFF,\n\tTLS_INITIAL,\t//tls only\n\tTLS_START,\t\t//tls upgrade\n\n\tTLS_STARTING\t//don't send any nick/user/pass info while this is set\n};\n\nstatic cvar_t\t*irc_debug;// = {\"irc_debug\", \"0\", irccvars, 0};\nstatic cvar_t\t*irc_motd;// = {\"irc_motd\", \"0\", irccvars, 0};\nstatic cvar_t\t*irc_nick;// = {\"irc_nick\", \"\", irccvars, 0};\nstatic cvar_t\t*irc_altnick;// = {\"irc_altnick\", \"\", irccvars, 0};\nstatic cvar_t\t*irc_realname;// = {\"irc_realname\", \"FTE IRC-Plugin\", irccvars, 0};\nstatic cvar_t\t*irc_hostname;// = {\"irc_hostname\", \"localhost\", irccvars, 0};\nstatic cvar_t\t*irc_username;// = {\"irc_username\", \"FTE\", irccvars, 0};\nstatic cvar_t\t*irc_timestamp;// = {\"irc_timestamp\", \"0\", irccvars, 0};\nstatic cvar_t\t*irc_quitmessage;// = {\"irc_quitmessage\", \"\", irccvars, 0};\nstatic cvar_t\t*irc_config;// = {\"irc_config\", \"1\", irccvars, 0};\n\nstatic icefuncs_t *piceapi;\nstatic int next_window_x;\nstatic int next_window_y;\nstatic qboolean reloadconfig;\n//static char commandname[64]; // belongs to magic tokenizer\nstatic char subvar[9][1000]; // etghack\nstatic char casevar[9][1000]; //numbered_command\n//static char servername[64]; // store server name\n#define CURRENTCONSOLE \"\" // need to make this the current console\n#define DEFAULTCONSOLE \"\"\n#define COMMANDNAME \"irc\"\n\n#if defined(SVNREVISION)\n\t#define RELEASE STRINGIFY(SVNREVISION)\n#else\n\t#define RELEASE __DATE__\n#endif\n\nstatic struct\n{\n\tint width;\n\tint height;\n} pvid;\nstatic void QDECL IRC_UpdateVideo(int width, int height, qboolean restarted)\n{\n\tpvid.width = width;\n\tpvid.height = height;\n}\n\nstatic qboolean (*Con_TrySubPrint)(const char *subname, const char *text);\nstatic qboolean Con_FakeSubPrint(const char *subname, const char *text)\n{\n\tplugfuncs->Print(text);\n\treturn true;\n}\n\n//porting zone:\n\n\n\t#define COLOURGREEN\t\"^2\"\n\t#define COLORWHITE \"^7\"\n\t#define COLOURWHITE \"^7\" // word\n\t#define COLOURRED \"^1\"\n\t#define COLOURYELLOW \"^3\"\n\t#define COLOURPURPLE \"^5\"\n\t#define COLOURBLUE \"^4\"\n\t#define COLOURINDIGO \"^6\"\n\n\n\t#define IRC_Malloc\tmalloc\n\t#define IRC_Free\t\tfree\n#undef COM_Parse\n\tstatic char *COM_Parse (char *data, char *token_out, int token_maxlen)\t//this is taken out of quake\n\t{\n\t\tint\t\tc;\n\t\tint\t\tlen;\n\n\t\tlen = 0;\n\t\ttoken_out[0] = 0;\n\n\t\tif (!data)\n\t\t\treturn NULL;\n\n\t// skip whitespace\n\tskipwhite:\n\t\twhile ( (c = *data) <= ' ')\n\t\t{\n\t\t\tif (c == 0)\n\t\t\t\treturn NULL;\t\t\t// end of file;\n\t\t\tdata++;\n\t\t}\n\n\t// skip // comments\n\t\tif (c=='/')\n\t\t{\n\t\t\tif (data[1] == '/')\n\t\t\t{\n\t\t\t\twhile (*data && *data != '\\n')\n\t\t\t\t\tdata++;\n\t\t\t\tgoto skipwhite;\n\t\t\t}\n\t\t}\n\n\n\t// handle quoted strings specially\n\t\tif (c == '\\\"')\n\t\t{\n\t\t\tdata++;\n\t\t\twhile (1)\n\t\t\t{\n\t\t\t\tif (len >= token_maxlen-1)\n\t\t\t\t\treturn data;\n\n\t\t\t\tc = *data++;\n\t\t\t\tif (c=='\\\"' || !c)\n\t\t\t\t{\n\t\t\t\t\ttoken_out[len] = 0;\n\t\t\t\t\treturn data;\n\t\t\t\t}\n\t\t\t\ttoken_out[len] = c;\n\t\t\t\tlen++;\n\t\t\t}\n\t\t}\n\n\t// parse a regular word\n\t\tdo\n\t\t{\n\t\t\tif (len >= token_maxlen-1)\n\t\t\t\treturn data;\n\n\t\t\ttoken_out[len] = c;\n\t\t\tdata++;\n\t\t\tlen++;\n\t\t\tc = *data;\n\t\t} while (c>32);\n\n\t\ttoken_out[len] = 0;\n\t\treturn data;\n\t}\n\n\n\n\n\n\n\n\n\n\n//\\r\\n is used to end a line.\n//meaning \\0s are valid.\n//but never used cos it breaks strings\n\n\n\n#define IRC_MAXNICKLEN 32\t//9 and a null term\n#define IRC_MAXMSGLEN 512\n#define IRC_MAXOUTBUFFER (IRC_MAXMSGLEN*16)\n\n\ntypedef struct ircclient_s {\n\tstruct ircclient_s *next;\n\n\tchar id[64];\t//used for console prints, so we can match up consoles and clients.\n\n\tchar server[64];\n\tint port;\n\n\tqhandle_t socket;\n\n\tenum tlsmode_e tlsmode;\n\tqboolean quitting;\n\tqboolean connecting;\n\tchar nick[IRC_MAXNICKLEN];\t//nick that we're actually using\n\tsize_t nicktries;\t\t\t//so we can cycle nicks till we get one that works.\n\n\tqboolean persist;\t\t\t//server connection is persistent across restarts\n\tchar primarynick[IRC_MAXNICKLEN];\t//primary nick the connection was configured with\n\tchar pwd[128];\t\t\t\t//server password\n\tchar realname[128];\t\t\t//this is your descriptive OS user account... supposedly.\n\tchar username[128];\t\t\t//this is your unique OS user name... supposedly.\n\tchar hostname[128];\t\t\t//this is your OS hostname... supposedly.\n\tchar autochannels[256];\t\t//\"#chan,pwd #foo,bar #fred #splodge\" for four channnels, two with a password\n\n\tchar defaultdest[IRC_MAXNICKLEN];//channel or nick\n\n\tchar bufferedinmessage[IRC_MAXMSGLEN+1];\t//there is a max size for protocol. (conveinient eh?) (and it's text format)\n\tint bufferedinammount;\n\n\n\tchar bufferedoutmessage[IRC_MAXOUTBUFFER+1];\t//there is a max size for protocol. (conveinient eh?) (and it's text format)\n\tint bufferedoutammount;\n\n\tstruct ircice_s\n\t{\n\t\tstruct ircice_s *next;\n\n\t\tenum iceproto_e type;\n\t\tchar peer[IRC_MAXNICKLEN];\n\t\tqboolean host;\t\t//the host is the person that initiated the call/etc. they're the ones responsible for resolving deadlocks.\n\t\tqboolean allowed;\t//user allowed it. woot.\n\t\tqboolean accepted;\t//peer accepted it. connection is active.\n\n\t\tstruct icestate_s *ice;\n\t} *ice;\n} ircclient_t;\nstatic ircclient_t *ircclients;\n\n\nstatic void IRC_SetFooter(ircclient_t *irc, const char *subname, const char *format, ...)\n{\n\tva_list\t\targptr;\n\tstatic char\t\tstring[1024];\n\tchar lwr[128];\n\tint i;\n\tconst char *channame = subname;\n\n\tva_start (argptr, format);\n\tQ_vsnprintf (string, sizeof(string), format,argptr);\n\tva_end (argptr);\n\n\tif (irc)\n\t{\n\t\tQ_strlcpy(lwr, irc->id, sizeof(lwr));\n\t\tfor (i = strlen(lwr); *subname && i < sizeof(lwr)-2; i++, subname++)\n\t\t{\n\t\t\tif (*subname >= 'A' && *subname <= 'Z')\n\t\t\t\tlwr[i] = *subname - 'A' + 'a';\n\t\t\telse\n\t\t\t\tlwr[i] = *subname;\n\t\t}\n\t\n\t\tlwr[i] = '\\0';\n\n\t\tif (confuncs && confuncs->GetConsoleFloat(lwr, \"iswindow\") < true)\n\t\t{\n\t\t\tconfuncs->SetConsoleString(lwr, \"title\", *channame?channame:irc->server);\n\t\t\tconfuncs->SetConsoleString(lwr, \"prompt\", va(\"[^1%s^7]: \", irc->nick));\n\t\t\tconfuncs->SetConsoleFloat(lwr, \"iswindow\", 2);\n\t\t\tconfuncs->SetConsoleFloat(lwr, \"forceutf8\", true);\n\t\t\tconfuncs->SetConsoleFloat(lwr, \"wnd_w\", 256);\n\t\t\tconfuncs->SetConsoleFloat(lwr, \"wnd_h\", 320);\n\n\t\t\t//lame, but whatever.\n\t\t\tif (next_window_x + 256 > pvid.width)\n\t\t\t{\n\t\t\t\tnext_window_x = 0;\n\t\t\t\tnext_window_y += 320;\n\t\t\t\tif (next_window_y + 320 > pvid.height)\n\t\t\t\t\tnext_window_y = 0;\n\t\t\t}\n\t\t\tconfuncs->SetConsoleFloat(lwr, \"wnd_x\", next_window_x);\n\t\t\tconfuncs->SetConsoleFloat(lwr, \"wnd_y\", next_window_y);\n\t\t\tnext_window_x += 256;\n\t\t}\n\n\t\tif (confuncs)\n\t\t\tconfuncs->SetConsoleString(lwr, \"footer\", string);\n\t}\n}\nstatic qboolean IRC_WindowShown(ircclient_t *irc, const char *subname)\n{\n\tchar lwr[128];\n\tint i;\n\tif (irc)\n\t{\n\t\tQ_strlcpy(lwr, irc->id, sizeof(lwr));\n\t\tfor (i = strlen(lwr); *subname && i < sizeof(lwr)-2; i++, subname++)\n\t\t{\n\t\t\tif (*subname >= 'A' && *subname <= 'Z')\n\t\t\t\tlwr[i] = *subname - 'A' + 'a';\n\t\t\telse\n\t\t\t\tlwr[i] = *subname;\n\t\t}\n\t\n\t\tlwr[i] = '\\0';\n\n\t\tif (confuncs && confuncs->GetConsoleFloat(lwr, \"iswindow\") < true)\n\t\t\treturn false;\n\t}\n\treturn true;\n}\nstatic void IRC_Printf(ircclient_t *irc, const char *subname, const char *format, ...) LIKEPRINTF(3);\nstatic void IRC_Printf(ircclient_t *irc, const char *subname, const char *format, ...)\n{\n\tva_list\t\targptr;\n\tstatic char\t\tstring[1024];\n\tchar lwr[128];\n\tint i;\n\tconst char *channame = subname;\n\n\tva_start (argptr, format);\n\tQ_vsnprintf (string, sizeof(string), format,argptr);\n\tva_end (argptr);\n\n\tif (!irc)\n\t\tplugfuncs->Print(string);\n\telse\n\t{\n\t\tQ_strlcpy(lwr, irc->id, sizeof(lwr));\n\t\tfor (i = strlen(lwr); *subname && i < sizeof(lwr)-2; i++, subname++)\n\t\t{\n\t\t\tif (*subname >= 'A' && *subname <= 'Z')\n\t\t\t\tlwr[i] = *subname - 'A' + 'a';\n\t\t\telse\n\t\t\t\tlwr[i] = *subname;\n\t\t}\n\t\n\t\tlwr[i] = '\\0';\n\n\t\tif (confuncs && confuncs->GetConsoleFloat(lwr, \"iswindow\") < true)\n\t\t{\n\t\t\tconfuncs->SetConsoleString(lwr, \"title\", *channame?channame:irc->server);\n\t\t\tconfuncs->SetConsoleString(lwr, \"prompt\", va(\"[^1%s^7]: \", irc->nick));\n\t\t\tconfuncs->SetConsoleFloat(lwr, \"iswindow\", 2);\n\t\t\tconfuncs->SetConsoleFloat(lwr, \"forceutf8\", true);\n\t\t\tconfuncs->SetConsoleFloat(lwr, \"wnd_w\", 256);\n\t\t\tconfuncs->SetConsoleFloat(lwr, \"wnd_h\", 320);\n\n\t\t\t//lame, but whatever.\n\t\t\tif (next_window_x + 256 > pvid.width)\n\t\t\t{\n\t\t\t\tnext_window_x = 0;\n\t\t\t\tnext_window_y += 320;\n\t\t\t\tif (next_window_y + 320 > pvid.height)\n\t\t\t\t\tnext_window_y = 0;\n\t\t\t}\n\t\t\tconfuncs->SetConsoleFloat(lwr, \"wnd_x\", next_window_x);\n\t\t\tconfuncs->SetConsoleFloat(lwr, \"wnd_y\", next_window_y);\n\t\t\tnext_window_x += 256;\n\t\t}\n\t\tif (!*string)\n\t\t\tconfuncs->SetActive(lwr);\n\n\t\tCon_TrySubPrint(lwr, string);\n\t}\n}\n\n\n\n\nstatic void IRC_InitCvars(void)\n{\n\tconst char *cvargroup = \"IRC Console Variables\";\n\tirc_debug\t\t= cvarfuncs->GetNVFDG(\"irc_debug\", \"0\", 0, NULL, cvargroup);\n\tirc_motd\t\t= cvarfuncs->GetNVFDG(\"irc_motd\", \"0\", 0, NULL, cvargroup);\n\tirc_nick\t\t= cvarfuncs->GetNVFDG(\"irc_nick\", \"\", 0, NULL, cvargroup);\n\tirc_altnick\t\t= cvarfuncs->GetNVFDG(\"irc_altnick\", \"\", 0, NULL, cvargroup);\n\tirc_realname\t= cvarfuncs->GetNVFDG(\"irc_realname\", \"FTE IRC-Plugin\", 0, NULL, cvargroup);\n\tirc_hostname\t= cvarfuncs->GetNVFDG(\"irc_hostname\", \"localhost\", 0, NULL, cvargroup);\n\tirc_username\t= cvarfuncs->GetNVFDG(\"irc_username\", \"FTE\", 0, NULL, cvargroup);\n\tirc_timestamp\t= cvarfuncs->GetNVFDG(\"irc_timestamp\", \"0\", 0, NULL, cvargroup);\n\tirc_quitmessage\t= cvarfuncs->GetNVFDG(\"irc_quitmessage\", \"\", 0, NULL, cvargroup);\n\tirc_config\t\t= cvarfuncs->GetNVFDG(\"irc_config\", \"1\", 0, NULL, cvargroup);\n}\n\nvoid IRC_Command(ircclient_t *ircclient, char *dest, char *args);\nvoid IRC_ExecuteCommand_f(void);\nint IRC_ConExecuteCommand(qboolean isinsecure);\nvoid IRC_Frame(double realtime, double gametime);\nqboolean IRC_ConsoleLink(void);\n\nqboolean Plug_Init(void)\n{\n\tconfuncs = plugfuncs->GetEngineInterface(plugsubconsolefuncs_name, sizeof(*confuncs));\n\tfilefuncs = plugfuncs->GetEngineInterface(plugfsfuncs_name, sizeof(*filefuncs));\n\tnetfuncs = plugfuncs->GetEngineInterface(plugnetfuncs_name, sizeof(*netfuncs));\n\tdrawfuncs = plugfuncs->GetEngineInterface(plug2dfuncs_name, sizeof(*drawfuncs));\n\tpiceapi = plugfuncs->GetEngineInterface(ICE_API_CURRENT, sizeof(*piceapi));\n\tplugfuncs->ExportFunction(\"UpdateVideo\", IRC_UpdateVideo);\n\n\tif (netfuncs &&\n\t\tfilefuncs &&\n\t\tplugfuncs->ExportFunction(\"Tick\", IRC_Frame))\n\t{\n\t\tcmdfuncs->AddCommand(COMMANDNAME, IRC_ExecuteCommand_f, \"Internet Relay Chat client, use ^[/\"COMMANDNAME\" /help^] for help\");\n\n\t\tplugfuncs->ExportFunction(\"ConsoleLink\", IRC_ConsoleLink);\n\t\tif (!confuncs || !plugfuncs->ExportFunction(\"ConExecuteCommand\", IRC_ConExecuteCommand))\n\t\t\tCon_TrySubPrint = Con_FakeSubPrint;\n\t\telse\n\t\t\tCon_TrySubPrint = confuncs->SubPrint;\n\n\t\treloadconfig = true;\n\t\tIRC_InitCvars();\n\n\t\treturn true;\n\t}\n\telse\n\t{\n\t\tplugfuncs->Print(\"IRC Client Plugin failed\\n\");\n\t}\n\treturn false;\n}\n\n\n\nvoid IRC_ExecuteCommand_f(void)\n{\n\tircclient_t *ircclient = ircclients;\n\tchar imsg[8192];\n\tcmdfuncs->Args(imsg, sizeof(imsg));\n\t//FIXME: select an irc network more inteligently\n\tIRC_Command(ircclient, ircclient?ircclient->defaultdest:\"\", imsg);\n}\nint IRC_ConExecuteCommand(qboolean isinsecure)\n{\n\tchar buffer[256];\n\tchar imsg[8192];\n\tircclient_t *ircclient;\n\t//FIXME: select the right network\n\n\tcmdfuncs->Argv(0, buffer, sizeof(buffer));\n\tcmdfuncs->Args(imsg, sizeof(imsg));\n\n\t//buffer is something like: irc53:#foo\n\tfor (ircclient = ircclients; ircclient; ircclient = ircclient->next)\n\t{\n\t\tif (!strncmp(ircclient->id, buffer, strlen(ircclient->id)))\n\t\t\tbreak;\n\t}\n\n\tif (!ircclient)\n\t{\n\t\tif (*buffer == '/')\n\t\t\tIRC_Command(NULL, \"\", imsg);\n\t\telse\n\t\t\tCon_TrySubPrint(buffer, \"You were disconnected\\n\");\n\t\treturn true;\n\t}\n\n\tIRC_Command(ircclient, buffer+strlen(ircclient->id), imsg);\n\treturn true;\n}\n\nstatic void IRC_AddClientMessage(ircclient_t *irc, char *msg)\n{\n\tchar output[4096];\n\tint len;\n\n\tQ_strlcpy(output, msg, sizeof(output));\n\tQ_strlcat(output, \"\\n\", sizeof(output));\n\tlen = strlen(output);\n\n\tif (irc->bufferedoutammount + len > sizeof(irc->bufferedoutmessage))\n\t\treturn;\n\tmemcpy(irc->bufferedoutmessage + irc->bufferedoutammount, output, len);\n\tirc->bufferedoutammount += len;\n\n\tif (irc_debug->value == 1) { IRC_Printf(irc, DEFAULTCONSOLE,COLOURYELLOW \"<< %s \\n\",msg); }\n}\n\nstatic ircclient_t *IRC_FindAccount(const char *server)\n{\n\tircclient_t *irc;\n\tfor (irc = ircclients; irc; irc = irc->next)\n\t{\n\t\tif (!strcmp(irc->server, server))\n\t\t\treturn irc;\n\t}\n\treturn NULL;\t//no match\n}\n\nstatic ircclient_t *IRC_Create(const char *server, const char *nick, const char *realname, const char *hostname, const char *username, const char *password, const char *channels)\n{\n\tircclient_t *irc;\n\n\t//FIXME: accept server:+port for starttls\n\t//FIXME: accept server:*port for initial tls\n\n\tirc = IRC_Malloc(sizeof(ircclient_t));\n\tif (!irc)\n\t\treturn NULL;\n\n\tmemset(irc, 0, sizeof(ircclient_t));\n\tQ_snprintf(irc->id, sizeof(irc->id), \"IRC%x%x:\", rand(),rand());\n\tirc->connecting = true;\n\tirc->tlsmode = TLS_OFF;\n\tirc->quitting = false;\n\tirc->socket = invalid_handle;\n\n\tQ_strlcpy(irc->server, server, sizeof(irc->server));\n\n\tQ_strlcpy(irc->primarynick,\tnick,\t\tsizeof(irc->primarynick));\n\tQ_strlcpy(irc->nick,\t\tnick,\t\tsizeof(irc->nick));\n\tQ_strlcpy(irc->realname,\trealname,\tsizeof(irc->realname));\n\tQ_strlcpy(irc->hostname,\thostname,\tsizeof(irc->hostname));\n\tQ_strlcpy(irc->username,\tusername,\tsizeof(irc->username));\n\tQ_strlcpy(irc->pwd,\t\t\tpassword,\tsizeof(irc->pwd));\n\n\tQ_strlcpy(irc->autochannels,channels,\tsizeof(irc->autochannels));\n\n//\tgethostname(irc->hostname, sizeof(irc->hostname));\n//\tirc->hostname[sizeof(irc->hostname)-1] = 0;\n\n\tirc->next = ircclients;\n\tircclients = irc;\n\n\treturn irc;\n}\n\nstatic void IRC_SetPass(ircclient_t *irc, char *pass)\n{\n\tif (irc->pwd != pass)\n\t\tQ_strlcpy(irc->pwd, pass, sizeof(irc->pwd));\n\tif (*pass && irc->tlsmode != TLS_STARTING)\n\t\tIRC_AddClientMessage(irc, va(\"PASS %s\", pass));\n}\nstatic void IRC_SetNick(ircclient_t *irc, char *nick)\n{\n\tif (irc->nick != nick)\n\t\tQ_strlcpy(irc->nick, nick, sizeof(irc->nick));\n\tif (irc->tlsmode != TLS_STARTING)\n\t\tIRC_AddClientMessage(irc, va(\"NICK %s\", irc->nick));\n}\nstatic void IRC_SetUser(ircclient_t *irc, char *user)\n{\n\tif (irc->tlsmode != TLS_STARTING)\n\t{\n\t\tconst char *username = irc->username;\n\t\tconst char *realname = irc->realname;\n\t\tif (!*username)\n\t\t\tusername = getenv(\"USER\");\n\t\tif (!username)\n\t\t\tusername = \"FTE\";\t//we need something.\n\n\t\tif (!*realname)\n\t\t\trealname = username;\n\t\t//servers will usually ignore the server arg, as they usually know their own dns name already...\n\t\t//servers SHOULD ignore the hostname arg too (using a reverse dns). or they'll just replace it with an ip address (note: could use this instead of a STUN server).\n\t\t//the username+realname are used, and need to be persistent for auto-op type mechanisms.\n\t\tIRC_AddClientMessage(irc, va(\"USER %s %s %s :%s\", username, irc->hostname, irc->server, realname));\n\t}\n}\n\nstatic qboolean IRC_Establish(ircclient_t *irc)\n{\n\tif (!irc)\n\t\treturn false;\n\n\tif (handleisvalid(irc->socket))\t//don't need to do anything.\n\t\treturn true;\n\n\t//clear up any stale state\n\tirc->bufferedoutammount = 0;\n\tirc->bufferedinammount = 0;\n\tirc->quitting = false;\n\n\tirc->socket = netfuncs->TCPConnect(irc->server, 6667);\t//port is only used if the url doesn't contain one. It's a default.\n\n\t//not yet blocking. So no frequent attempts please...\n\t//non blocking prevents connect from returning worthwhile sensible value.\n\tif (!handleisvalid(irc->socket))\n\t{\n\t\tCon_Printf(\"IRC_OpenSocket: couldn't connect\\n\");\n\t\treturn false;\n\t}\n\n\tif (irc->tlsmode == TLS_INITIAL)\n\t{\n\t\tif (netfuncs->SetTLSClient(irc->socket, irc->server) < 0)\n\t\t{\n\t\t\tnetfuncs->Close(irc->socket);\n\t\t\tirc->socket = invalid_handle;\n\t\t\treturn false;\n\t\t}\n\t}\n\telse if (irc->tlsmode != TLS_OFF)\n\t{\n\t\tIRC_AddClientMessage(irc, \"STARTTLS\");\n\t\tirc->tlsmode = TLS_STARTING;\n\t}\n\telse\n\t{\n\t\tirc->nicktries = 0;\n\t\tIRC_SetPass(irc, irc->pwd);\n\t\tIRC_SetNick(irc, irc->nick);\n\t\tIRC_SetUser(irc, irc_username->string);\n\t}\n\n\treturn true;\n}\n\nstatic void IRC_ParseConfig(void)\n{\n\tqhandle_t config;\n\tint len = filefuncs->Open(\"**plugconfig\", &config, 1);\n\tif (len >= 0)\n\t{\n\t\tchar *buf = malloc(len+1);\n\t\tchar *msg = buf;\n\t\tbuf[len] = 0;\n\t\tfilefuncs->Read(config, buf, len);\n\t\tfilefuncs->Close(config);\n\n\t\twhile (msg && *msg)\n\t\t{\n\t\t\tircclient_t *irc;\n\t\t\tchar server[256];\n\t\t\tchar channels[1024];\n\t\t\tchar nick[256];\n\t\t\tchar password[256];\n\t\t\tchar realname[256];\n\t\t\tchar hostname[256];\n\t\t\tchar username[256];\n\n\t\t\tmsg = COM_Parse(msg, server, sizeof(server));\n\t\t\tmsg = COM_Parse(msg, channels, sizeof(channels));\n\t\t\tmsg = COM_Parse(msg, nick, sizeof(nick));\n\t\t\tmsg = COM_Parse(msg, password, sizeof(password));\n\t\t\tmsg = COM_Parse(msg, realname, sizeof(realname));\n\t\t\tmsg = COM_Parse(msg, hostname, sizeof(hostname));\n\t\t\tmsg = COM_Parse(msg, username, sizeof(username));\n\t\t\tif (*server)\n\t\t\t{\n\t\t\t\tirc = IRC_Create(server, nick, realname, hostname, username, password, channels);\n\t\t\t\tif (irc)\n\t\t\t\t{\n\t\t\t\t\tirc->persist = true;\n\t\t\t\t\tif (IRC_Establish(irc))\n\t\t\t\t\t{\n\t\t\t\t\t\tif (!*irc->autochannels)\n\t\t\t\t\t\t\tIRC_Printf(irc, DEFAULTCONSOLE, \"Trying to connect to %s\\n\", irc->server);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\tIRC_Printf(irc, DEFAULTCONSOLE, \"Unable to connect to %s\\n\", irc->server);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tfree(buf);\n\t}\n}\nstatic void IRC_WriteConfig(void)\n{\n\tqhandle_t config;\n\n\tif (irc_config->value == 0)\n\t\treturn;\n\n\tfilefuncs->Open(\"**plugconfig\", &config, 2);\n\tif (config >= 0)\n\t{\n\t\tircclient_t *irc;\n\t\tfor(irc = ircclients; irc; irc = irc->next)\n\t\t{\n\t\t\tchar *s = va(\"\\\"%s\\\" \\\"%s\\\" \\\"%s\\\" \\\"%s\\\" \\\"%s\\\" \\\"%s\\\" \\\"%s\\\"\\n\", irc->server, irc->autochannels, irc->primarynick, irc->pwd, irc->realname, irc->hostname, irc->username);\n\t\t\tif (irc->quitting || !irc->persist)\n\t\t\t\tcontinue;\n\t\t\tfilefuncs->Write(config, s, strlen(s));\n\t\t}\n\n\t\tfilefuncs->Close(config);\n\t}\n}\nstatic void IRC_MakeDefault(ircclient_t *irc)\n{\t//unlinks the client, then links it at the head, so that its the first found (thus the default)\n\tircclient_t **link;\n\n\tfor (link = &ircclients; *link; link = &(*link)->next)\n\t{\n\t\tif (*link == irc)\n\t\t{\n\t\t\t*link = irc->next;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tirc->next = ircclients;\n\tircclients = irc;\n}\n\nstatic void IRC_PartChannelInternal(ircclient_t *irc, char *channelname)\n{\n\tchar ac[countof(irc->autochannels)];\n\tchar *chan;\n\tstrcpy(ac, irc->autochannels);\n\tchan = strtok(ac, \" \");\n\t*irc->autochannels = 0;\n\twhile(chan)\n\t{\n\t\tif (*chan)\n\t\t{\n\t\t\tchar *pwd = strchr(chan, ',');\n\t\t\tif (pwd)\n\t\t\t\t*pwd++ = 0;\n\n\t\t\tif (strcmp(chan, channelname))\n\t\t\t{\n\t\t\t\tif (*irc->autochannels)\n\t\t\t\t\tQ_strncatz(irc->autochannels, \" \", sizeof(irc->autochannels));\n\t\t\t\tif (pwd)\n\t\t\t\t\tQ_strncatz(irc->autochannels, va(\"%s,%s\", chan, pwd), sizeof(irc->autochannels));\n\t\t\t\telse\n\t\t\t\t\tQ_strncatz(irc->autochannels, va(\"%s\", chan), sizeof(irc->autochannels));\n\t\t\t}\n\t\t}\n\t\tchan = strtok(NULL, \" \");\n\t}\n}\n\nstatic void IRC_PartChannel(ircclient_t *irc, char *channelname)\n{\n\tIRC_PartChannelInternal(irc, channelname);\n\tIRC_AddClientMessage(irc, va(\"PART %s\", channelname));\n}\n\nstatic void IRC_JoinChannel(ircclient_t *irc, char *channel, char *key) // i screwed up, its actually: <channel>{,<channel>} [<key>{,<key>}]\n{\n\tIRC_PartChannelInternal(irc, channel);\n\n\tif (*irc->autochannels)\n\t\tQ_strncatz(irc->autochannels, \" \", sizeof(irc->autochannels));\n\tQ_strncatz(irc->autochannels, va(\"%s,%s\", channel, key), sizeof(irc->autochannels));\n\n\tif (key)\n\t{\n\t\t/*if (*channel != '#')\n\t\t\tIRC_AddClientMessage(irc, va(\"JOIN #%s %s\", channel,key));\n\t\telse*/\n\t\t\tIRC_AddClientMessage(irc, va(\"JOIN %s %s\", channel,key));\n\t}\n\telse\n\t{\n\t\t/*if (*channel != '#')\n\t\t\tIRC_AddClientMessage(irc, va(\"JOIN #%s\", channel));\n\t\telse*/\n\t\tIRC_AddClientMessage(irc, va(\"JOIN %s\", channel));\n\t}\n}\n\nstatic void IRC_JoinChannels(ircclient_t *irc, char *channelstring)\n{\n\tchar *chan = strtok(channelstring, \" \");\n\twhile(chan)\n\t{\n\t\tif (*chan)\n\t\t{\n\t\t\tchar *line = va(\"JOIN %s\", chan);\n\t\t\tchar *comma = strchr(line, ',');\n\t\t\tif (comma)\n\t\t\t\t*comma = ' ';\n\t\t\tIRC_AddClientMessage(irc, line);\n\t\t}\n\t\tchan = strtok(NULL, \" \");\n\t}\n}\n\n\n/*\n\nATTN: Spike\n\n# (just for reference) == Ctrl+K in mirc to put the color code symbol in\n\nnow to have a background color, you must specify a forground color first (#0,15)\n\n, denotes end of forground color, and start of background color\n\nirc colors work in many strange ways:\n\n#0-#15 for forground color // the code currently converts to this one, which is not the \"proper\" irc way, read the next one to understand. Still need to support it, just not output as it.\n\n#00-#15 for forground color (note #010 to #015 is not valid) --- this is the \"proper\" irc way, because I could say \"#11+1=2\" (which means I want 1+1=2 to appear black (1), but instead it will come out as indigo (11) and look like this: +1=2)\n\nbackground examples: (note\n\n#0,15 (white forground, light gray background)\n\n#00,15 (white forground, light gray background) // proper way\n\n#15,0 (white forground, light gray background)\n\n#15,00 (white forground, light gray background) // proper way\n\nI hope this makes sense to you, to be able to edit the IRC_FilterMircColours function ~ Moodles\n\n*/\nstatic void IRC_FilterMircColours(char *msg)\n{\n\tint i;\n\tint chars;\n\twhile(*msg)\n\t{\n\t\tif (*msg == 3)\n\t\t{\n\t\t\tchars = 2;\n\t\t\tif (msg[1] >= '0' && msg[1] <= '9')\n\t\t\t{\n\t\t\t\ti = msg[1]- '0';\n\t\t\t\tif (msg[2] >= '0' && msg[2] <= '9')\n\t\t\t\t{\n\t\t\t\t\ti = i*10 + (msg[2]-'0');\n\t\t\t\t\tchars = 3;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t\ti = msg[1];\n\t\t\tswitch(i)\n\t\t\t{\n\t\t\tcase 0:\n\t\t\t\tmsg[1] = '7';\t//white\n\t\t\t\tbreak;\n\t\t\tcase 1:\n\t\t\t\tmsg[1] = '0';\t//black\n\t\t\t\tbreak;\n\t\t\tcase 2:\n\t\t\t\tmsg[1] = '4';\t//darkblue\n\t\t\t\tbreak;\n\t\t\tcase 3:\n\t\t\t\tmsg[1] = '2';\t//darkgreen\n\t\t\t\tbreak;\n\t\t\tcase 4:\n\t\t\t\tmsg[1] = '1';\t//red\n\t\t\t\tbreak;\n\t\t\tcase 5:\n\t\t\t\tmsg[1] = '1';\t//brown\n\t\t\t\tbreak;\n\t\t\tcase 6:\n\t\t\t\tmsg[1] = '5';\t//purple\n\t\t\t\tbreak;\n\t\t\tcase 7:\n\t\t\t\tmsg[1] = '3';\t//orange\n\t\t\t\tbreak;\n\t\t\tcase 8:\n\t\t\t\tmsg[1] = '3';\t//yellow\n\t\t\t\tbreak;\n\t\t\tcase 9:\n\t\t\t\tmsg[1] = '2';\t//lightgreen\n\t\t\t\tbreak;\n\t\t\tcase 10:\n\t\t\t\tmsg[1] = '6';\t//darkcyan\n\t\t\t\tbreak;\n\t\t\tcase 11:\n\t\t\t\tmsg[1] = '6';\t//lightcyan\n\t\t\t\tbreak;\n\t\t\tcase 12:\n\t\t\t\tmsg[1] = '4';\t//lightblue\n\t\t\t\tbreak;\n\t\t\tcase 13:\n\t\t\t\tmsg[1] = '5';\t//pink\n\t\t\t\tbreak;\n\t\t\tcase 14:\n\t\t\t\tmsg[1] = '7';\t//grey\n\t\t\t\tbreak;\n\t\t\tcase 15:\n\t\t\t\tmsg[1] = '7';\t//lightgrey\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tmsg++;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t*msg = '^';\n\t\t\tmsg+=2;\n\t\t\tif (chars==3)\n\t\t\t\tmemmove(msg, msg+1, strlen(msg));\n\t\t\tcontinue;\n\t\t}\n\t\tmsg++;\n\t}\n}\n\n#define IRC_DONE 0\n#define IRC_CONTINUE 1\n#define IRC_KILL 2\n\nstatic void magic_tokenizer(int word,char *thestring)\n{\n\tchar *temp;\n\tint i = 1;\n\n\tstrcpy(casevar[1],thestring);\n\n\ttemp = strchr(casevar[1], ' ');\n\n\twhile (i < 8)\n\t{\n\t\ti++;\n\n\t\tif (temp != NULL)\n\t\t{\n\t\t\tstrcpy(casevar[i],temp+1);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tstrcpy(casevar[i], \"\");\n\t\t}\n\n\t\ttemp=strchr(casevar[i], ' ');\n\n\t}\n\n}\n\nstatic void magic_etghack(char *thestring)\n{\n\tchar *temp;\n\tint i = 1;\n\n\tstrcpy(subvar[1],thestring);\n\n\ttemp = strchr(subvar[1], ' ');\n\n\twhile (i < 8)\n\t{\n\t\ti++;\n\n\t\tif (temp != NULL)\n\t\t{\n\t\t\tstrcpy(subvar[i],temp+1);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tstrcpy(subvar[i], \"\");\n\t\t}\n\n\t\ttemp=strchr(subvar[i], ' ');\n\n\t}\n\n}\n\nstatic void IRC_TryNewNick(ircclient_t *irc, char *nickname)\n{\n\tchar *seedednick;\n\n\tif (irc->tlsmode == TLS_STARTING)\n\t{\n\t\t//don't submit any of this info here.\n\t\treturn;\n\t}\n\tif (irc->connecting)\n\t{\n\t\tif (irc->nicktries == 0)\n\t\t{\n\t\t\tirc->nicktries++;\n\t\t\tif (*irc->primarynick && strcmp(nickname, irc->primarynick))\n\t\t\t{\n\t\t\t\tIRC_SetNick(irc, irc->primarynick);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tif (irc->nicktries == 1)\n\t\t{\n\t\t\tirc->nicktries++;\n\t\t\tif (*irc_nick->string && strcmp(nickname, irc_nick->string))\n\t\t\t{\n\t\t\t\tIRC_SetNick(irc, irc_nick->string);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tif (irc->nicktries == 2)\n\t\t{\n\t\t\tirc->nicktries++;\n\t\t\tif (*irc_altnick->string && strcmp(nickname, irc_altnick->string))\n\t\t\t{\n\t\t\t\tIRC_SetNick(irc, irc_altnick->string);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\tif (++irc->nicktries == 10)\n\t\t{\n\t\t\tIRC_Printf(irc, DEFAULTCONSOLE, COLOURRED \"ERROR: Unable to obtain usable nickname\\n\");\n\t\t\treturn;\n\t\t}\n\n\t\t//panic and pick something at random\n\t\t//IRC_Printf(irc, DEFAULTCONSOLE, COLOURRED \"ERROR: primary nickname in use. Attempting random nickname.\\n\");\n\t\tif (*irc->primarynick && irc->nicktries < 7)\n\t\t\tseedednick = va(\"%.6s%i\", irc->primarynick, rand());\n\t\telse if (*irc_nick->string && irc->nicktries < 8)\n\t\t\tseedednick = va(\"%.6s%i\", irc_nick->string, rand());\n\t\telse if (*irc_altnick->string && irc->nicktries < 9)\n\t\t\tseedednick = va(\"%.6s%i\", irc_altnick->string, rand());\n\t\telse\n\t\t\tseedednick = va(\"%.6s%i\", \"FTE\", rand());\n\t\tseedednick[9] = 0; //'Each client is distinguished from other clients by a unique nickname having a maximum length of nine (9) characters'\n\n\t\tIRC_SetNick(irc, seedednick);\n\t}\n}\n\n//==================================================\n\nstatic void numbered_command(int comm, char *msg, ircclient_t *irc) // move vars up 1 more than debug says\n{\n\tmagic_tokenizer(0,msg);\n\n\tswitch (comm)\n\t{\n\tcase   1:\t/* RPL_WELCOME */\n\tcase   2:\t/* RPL_YOURHOST */\n\tcase   3:\t/* RPL_CREATED */\n\tcase   4:\t/* RPL_MYINFO */\n\tcase   5:\t/* RPL_ISUPPORT */\n\t{\n\t\tif (irc->tlsmode != TLS_STARTING)\n\t\t\tirc->connecting = 0; // ok we are connected\n\n\t\tif (irc_motd->value)\n\t\t\tIRC_Printf(irc, DEFAULTCONSOLE, COLOURYELLOW \"SERVER STATS: %s\\n\",casevar[3]);\n\t\treturn;\n\t}\n//\tcase   5:\t/* RPL_BOUNCE */\n//\t{\n//\t\tIRC_Printf(irc, DEFAULTCONSOLE, COLOURRED \"RPL_BOUNCE: %s\\n\",casevar[3]);\n//\t\treturn;\n//\t}\n\tcase 20:\t/* RPL_HELLO */\n\t{\n\t\tif (irc_motd->value)\n\t\t\tIRC_Printf(irc, DEFAULTCONSOLE, COLOURYELLOW \"%s\\n\",casevar[3]);\n\t\treturn;\n\t}\n\tcase 42:\t/*RPL_YOURID */\n\t{\n\t\tif (irc_motd->value)\n\t\t\tIRC_Printf(irc, DEFAULTCONSOLE, COLOURYELLOW \"%s\\n\",casevar[3]);\n\t\treturn;\n\t}\n\tcase 250:\n\tcase 251:\t/* RPL_LUSERCLIENT */\n\tcase 252:\t/* RPL_LUSEROP */\n\tcase 253:\t/* RPL_LUSERUNKNOWN */\n\tcase 254:\t/* RPL_LUSERCHANNELS */\n\tcase 255:\t/* RPL_LUSERME */\n\tcase 256:\t/* RPL_ADMINME */\n\tcase 257:\t/* RPL_ADMINLOC1 */\n\tcase 258:\t/* RPL_ADMINLOC2 */\n\tcase 259:\t/* RPL_ADMINEMAIL */\n\tcase 265:\n\tcase 266:\n\t{\n\t\tif (irc_motd->value)\n\t\t\tIRC_Printf(irc, DEFAULTCONSOLE, COLOURYELLOW \"SERVER STATS: %s\\n\",casevar[3]);\n\t\treturn;\n\t}\n\tcase 301: /* #define RPL_AWAY             301 */\n\t{\n\t\tchar *username = strtok(casevar[3], \" \");\n\t\tchar *awaymessage = casevar[4]+1;\n\n\t\tIRC_Printf(irc, DEFAULTCONSOLE,\"WHOIS: <%s> (Away Message: %s)\\n\",username,awaymessage);\n\t\treturn;\n\t}\n\tcase 305: /* RPL_UNAWAY */\n\tcase 306: /* RPL_NOWAWAY */\n\t{\n\t\tchar *away = casevar[3]+1;\n\n\t\tIRC_Printf(irc, CURRENTCONSOLE,\"%s\\n\",away);\n\t\treturn;\n\t}\n\tcase 311: /* #define RPL_WHOISUSER        311 */\n\t{\n\t\tchar *username = strtok(casevar[3], \" \");\n\t\tchar *ident = strtok(casevar[4], \" \");\n\t\tchar *address = strtok(casevar[5], \" \");\n\t\tchar *realname = casevar[7]+1;\n\n\t\tIRC_Printf(irc, DEFAULTCONSOLE,\"WHOIS: <%s> (Ident: %s) (Address: %s) (Realname: %s) \\n\", username, ident, address, realname);\n\t\treturn;\n\t}\n\tcase 312: /* #define RPL_WHOISSERVER      312 */ //seems to be /whowas also\n\t{\n\t\tchar *username = strtok(casevar[3], \" \");\n\t\tchar *serverhostname = strtok(casevar[4], \" \");\n\t\tchar *servername = casevar[5]+1;\n\n\t\tIRC_Printf(irc, DEFAULTCONSOLE,\"WHOIS: <%s> (Server: %s) (Server Name: %s) \\n\", username, serverhostname, servername);\n\t\treturn;\n\t}\n\tcase 313: /* RPL_WHOISOPERATOR */\n\t{\n\t\tchar *username = strtok(casevar[3], \" \");\n\t\tchar *isoperator = casevar[4]+1;\n\n\t\tIRC_Printf(irc, DEFAULTCONSOLE,\"WHOIS: <%s> (%s)\\n\", username,isoperator);\n\n\t\treturn;\n\t}\n\tcase 317: /* #define RPL_WHOISIDLE        317 */\n\t{\n\t\tchar *username = strtok(casevar[3], \" \");\n\t\tchar *secondsidle = strtok(casevar[4], \" \");\n\t\tchar *signontime = strtok(casevar[5], \" \");\n\t\ttime_t t;\n\t\tconst struct tm *tm;\n\t\tchar buffer[100];\n\n\t\tt=strtoul(signontime, 0, 0);\n\t\ttm=localtime(&t);\n\n\t\tstrftime (buffer, 100, \"%a %b %d %H:%M:%S\", tm);\n\n\t\tIRC_Printf(irc, DEFAULTCONSOLE,\"WHOIS: <%s> (Idle Time: %s seconds) (Signon Time: %s) \\n\", username, secondsidle, buffer);\n\t\treturn;\n\t}\n\tcase 318: /* #define RPL_ENDOFWHOIS       318 */\n\t{\n\t\tchar *endofwhois = casevar[4]+1;\n\n\t\tIRC_Printf(irc, DEFAULTCONSOLE,\"WHOIS: %s\\n\", endofwhois);\n\n\t\treturn;\n\t}\n\tcase 319: /* #define RPL_WHOISCHANNELS    319 */\n\t{\n\t\tchar *username = strtok(casevar[3], \" \");\n\t\tchar *channels = casevar[4]+1;\n\n\t\tIRC_Printf(irc, DEFAULTCONSOLE,\"WHOIS: <%s> (Channels: %s)\\n\",username,channels); // need to remove the space from the end of channels\n\t\treturn;\n\t}\n\tcase 321:\n\t{\n//\t\tIRC_Printf(irc, \"list\", \"Start /LIST\\n\");\n\n\t\treturn;\n\t}\n\tcase 322: /* #define RPL_LIST             322 */\n\t{\n\t\tchar *channel = strtok(casevar[3], \" \");\n\t\tchar *users = strtok(casevar[4], \" \");\n\t\tchar *topic = casevar[5]+1;\n\n\t\tIRC_Printf(irc, \"list\", \"^1Channel:^7 %s ^1Users:^7 %s ^1Topic:^7 %s\\n\\n\", channel,users,topic);\n\t\treturn;\n\t}\n\tcase 323:\t/* RPL_LISTEND*/\n\t{\n\t\t//char *endoflist = casevar[3]+1;\n\n//\t\tIRC_Printf(irc, \"list\", \"%s\\n\",endoflist);\n\n\t\treturn;\n\t}\n\tcase 333:\t/* RPL_TOPICWHOTIME channel user timestamp*/\n\t\treturn;\n\tcase 366:\t/* RPL_ENDOFNAMES */\n\t{\n\t\tchar *channel = strtok(casevar[3], \" \");\n\t\tchar *endofnameslist = casevar[4]+1;\n\n\t\tIRC_Printf(irc, channel,\"%s\\n\",endofnameslist);\n\t\treturn;\n\t}\n\tcase 372:\t/* RPL_MOTD */\n\tcase 375:\t/* RPL_MOTDSTART */\n\tcase 376:\t/* RPL_ENDOFMOTD */\n\t{\n\t\tchar *motdmessage = casevar[3]+1;\n\n\t\tif (irc_motd->value == 2)\n\t\t\tIRC_Printf(irc, DEFAULTCONSOLE, \"MOTD: %s\\n\", motdmessage);\n\t\telse if (irc_motd->value)\n\t\t\tIRC_Printf(irc, DEFAULTCONSOLE, \"%s\\n\", motdmessage);\n\n\t\tif (*irc->autochannels)\n\t\t\tIRC_JoinChannels(irc, irc->autochannels);\n\n\t\treturn;\n\t}\n\tcase 378:\n\t{\n\t\tIRC_Printf(irc, DEFAULTCONSOLE, \"%s\\n\", msg);\n\t\treturn;\n\t}\n\tcase 401:\t/* ERR_NOSUCHNICK */\n\tcase 403:\t/* ERR_NOSUCHCHANNEL */\n\tcase 404:\t/* ERR_CANNOTSENDTOCHAN */\n\tcase 405:\t/* ERR_TOOMANYCHANNELS */\n\tcase 442:\t/* ERR_NOTONCHANNEL */\n\t{\n\t\tchar *username = strtok(casevar[3], \" \");\n\t\tchar *error = casevar[4]+1;\n\n\t\tIRC_Printf(irc, username, COLOURRED \"ERROR <%s>: %s\\n\",username,error);\n\t\treturn;\n\t}\n\tcase 432: /* #define ERR_ERRONEUSNICKNAME 432 */\n\t{\n\t\tIRC_Printf(irc, DEFAULTCONSOLE, \"Erroneous/invalid nickname given\\n\");\n\t\tIRC_TryNewNick(irc, \"FTEUser\");\n\t\treturn;\n\t}\n\tcase 433: /* #define ERR_NICKNAMEINUSE    433 */\n\tcase 438:\n\tcase 453:\n\t{\n\t\tchar *nickname = strtok(casevar[4], \" \");\n\t\tchar *badnickname = \":Nickname\";\n\n\t\tif ( !strcasecmp(nickname,badnickname) ) // bug with ircd, the nickname actually shifts position.\n\t\t{\n\t\t\tnickname = strtok(casevar[3], \" \");\n\t\t}\n\n//\t\tIRC_Printf(irc, DEFAULTCONSOLE, COLOURRED \"ERROR: <%s> is already in use.\\n\",nickname);\n\n\t\tIRC_TryNewNick(irc, nickname);\n\t\treturn;\n\t}\n\tcase 471: /* ERR_CHANNELISFULL */\n\t{\n\t\tchar *channel = strtok(casevar[3], \" \");\n\t\tchar *error = casevar[4]+1;\n\n\t\tIRC_Printf(irc, DEFAULTCONSOLE, COLOURRED \"ERROR: <%s>: %s (Channel is full and has reached user limit)\\n\",channel,error);\n\t\treturn;\n\t}\n\tcase 472: /* ERR_UNKNOWNMODE */\n\t{\n\t\tchar *mode = strtok(casevar[3], \" \");\n\t\tchar *error = casevar[4]+1;\n\n\t\tIRC_Printf(irc, DEFAULTCONSOLE, COLOURRED \"ERROR: <%s>: %s (Unknown mode)\\n\",mode,error);\n\t\treturn;\n\t}\n\tcase 473: /* ERR_INVITEONLYCHAN */\n\t{\n\t\tchar *channel = strtok(casevar[3], \" \");\n\t\tchar *error = casevar[4]+1;\n\n\t\tIRC_Printf(irc, DEFAULTCONSOLE, COLOURRED \"ERROR: <%s>: %s (Invite only)\\n\",channel,error);\n\t\treturn;\n\t}\n\tcase 474: /* ERR_BANNEDFROMCHAN */\n\t{\n\t\tchar *channel = strtok(casevar[3], \" \");\n\t\tchar *error = casevar[4]+1;\n\n\t\tIRC_Printf(irc, DEFAULTCONSOLE, COLOURRED \"ERROR: <%s>: %s (You are banned)\\n\",channel,error);\n\t\treturn;\n\t}\n\tcase 475: /* ERR_BADCHANNELKEY */\n\t{\n\t\tchar *channel = strtok(casevar[3], \" \");\n\t\tchar *error = casevar[4]+1;\n\n\t\tIRC_Printf(irc, DEFAULTCONSOLE, COLOURRED \"ERROR: <%s>: %s (Need the correct channel key. Example: /join %s bananas)\\n\",channel,error,channel);\n\t\treturn;\n\t}\n\tcase 482: /* ERR_CHANOPRIVSNEEDED */\n\t{\n\t\tchar *channel = strtok(casevar[3], \" \");\n\t\tchar *error = casevar[4]+1;\n\n\t\tIRC_Printf(irc, DEFAULTCONSOLE, COLOURRED \"ERROR: <%s>: %s (Need +o or @ status)\\n\",channel,error);\n\t\treturn;\n\t}\n\tcase 670: /* RPL_STARTTLS */\n\t{\n\t\tnetfuncs->SetTLSClient(irc->socket, irc->server);\n\t\tirc->tlsmode = TLS_START;\n\t\tirc->nicktries = 0;\n\t\tIRC_SetPass(irc, irc->pwd);\n\t\tIRC_SetNick(irc, irc->nick);\n\t\tIRC_SetUser(irc, irc_username->string);\n\t\treturn;\n\t}\n\tcase 691: /* ERR_STARTTLS */\n\t{\n\t\tIRC_Printf(irc, DEFAULTCONSOLE, COLOURYELLOW \"STARTTLS Failed: %s\\n\", casevar[3]);\n\t\tnetfuncs->Close(irc->socket);\n\t\tirc->socket = invalid_handle;\n\t\treturn;\n\t}\n\t}\n\n\tIRC_Printf(irc, DEFAULTCONSOLE, \"%s\\n\", msg); // if no raw number exists, print the thing\n}\n\nstatic struct ircice_s *IRC_ICE_Find(ircclient_t *irc, const char *sender, enum iceproto_e type)\n{\n\tstruct ircice_s *ice;\n\tfor (ice = irc->ice; ice; ice = ice->next)\n\t{\n\t\tif (ice->type == type && !strcmp(ice->peer, sender))\n\t\t\treturn ice;\n\t}\n\treturn NULL;\n}\nstatic struct ircice_s *IRC_ICE_Create(ircclient_t *irc, const char *sender, enum iceproto_e type, qboolean creator)\n{\n\tstruct icestate_s *ice;\n\tstruct ircice_s *ircice;\n\tchar *s, token[MAX_OSPATH];\n\tif (!piceapi)\n\t\treturn NULL;\n\n\tif (!creator && type == ICEP_QWSERVER)\n\t\tice = piceapi->Create(NULL, NULL, sender, ICEM_ICE, ICEP_QWCLIENT, creator);\n\telse if (!creator && type == ICEP_QWCLIENT)\n\t\tice = piceapi->Create(NULL, NULL, sender, ICEM_ICE, ICEP_QWSERVER, creator);\n\telse\n\t\tice = piceapi->Create(NULL, NULL, sender, ICEM_ICE, type, creator);\n\n\tif (!ice)\n\t\treturn NULL;\n\n\tpiceapi->Set(ice, \"controller\", creator?\"1\":\"0\");\n\tif (creator && type == ICEP_VOICE)\n\t{\n\t\t//note: the engine will ignore codecs it does not support.\n\t\tpiceapi->Set(ice, \"codec96\", \"opus@48000\");\n\t\tpiceapi->Set(ice, \"codec97\", \"speex@16000\");\t//wide\n\t\tpiceapi->Set(ice, \"codec98\", \"speex@8000\");\t\t//narrow\n\t\tpiceapi->Set(ice, \"codec99\", \"speex@32000\");\t//ultrawide\n\t\tpiceapi->Set(ice, \"codec8\", \"pcma@8000\");\n\t\tpiceapi->Set(ice, \"codec0\", \"pcmu@8000\");\n\t}\n\n\t//query dns to see if there's a stunserver hosted by the same domain\n\t//nslookup -querytype=SRV _stun._udp.example.com\n//\tQ_snprintf(stunhost, sizeof(stunhost), \"_stun._udp.%s\", ice->server);\n//\tif (NET_DNSLookup_SRV(stunhost, stunhost, sizeof(stunhost)))\n//\t\tpiceapi->Set(ice, \"server\", va(\"stun:%s\" + stunhost));\n//\telse\n\t{\n\t\tchar *stun = cvarfuncs->GetNVFDG(\"net_ice_broker\", \"\", 0, NULL, NULL)->string;\n\t\ts = strstr(stun, \"://\");\n\t\tif (s) stun = s+3;\n\t\tpiceapi->Set(ice, \"server\", va(\"stun:%s\", stun));\n\t}\n\n\t//sadly we need to add the other ice servers ourselves despite there being a cvar to list them.\n\ts = cvarfuncs->GetNVFDG(\"net_ice_servers\", \"\", 0, NULL, NULL)->string;\n\twhile((s=cmdfuncs->ParseToken(s, token, sizeof(token), NULL)))\n\t\tpiceapi->Set(ice, \"server\", token);\n\n\tircice = malloc(sizeof(*ircice));\n\tmemset(ircice, 0, sizeof(*ircice));\n\tircice->next = irc->ice;\n\tirc->ice = ircice;\n\n\tircice->type = type;\n\tQ_strlcpy(ircice->peer, sender, sizeof(ircice->peer));\n\tircice->host = creator;\n\tircice->accepted = false;\n\tircice->allowed = creator;\n\tircice->ice = ice;\n\n\treturn ircice;\n}\nstatic void IRC_ICE_Update(ircclient_t *irc, struct ircice_s *ice, char updatetype)\n{\n\t//'+'\tpropose  ('hey, can I call you please?')\n\t//'='\toffer    ('these are my details')\n\t//'*'\tfinalise ('this is what I'm going to use')\n\t//'-'\treject   ('get lost, I don't want to talk to you any more')\n\t//'%'\tcandiate ('try this address')\n\t//I was originally using colons to separate terms, but switched to slashes to avoid smilies for irc clients that print unknown CTCP messages.\n\tchar message[1024];\n\tstruct icecandinfo_s *c;\n\tchar *icetype;\n\n\tif (!ice->allowed && updatetype != '-')\n\t\treturn;\n\n\t*message = 0;\n\n\tswitch(ice->type)\n\t{\n\tdefault:\n\tcase ICEP_VOICE:\n\t\ticetype = \"voice\";\n\t\tbreak;\n\tcase ICEP_QWSERVER: \n\t\ticetype = \"qwserver\";\n\t\tbreak;\n\tcase ICEP_QWCLIENT:\n\t\ticetype = \"qwclient\";\n\t\tbreak;\n\t}\n\n\tif (updatetype == '=' || updatetype == '*')\n\t{\n\t\tchar ufrag[256];\n\t\tchar pwd[256];\n\n\t\tpiceapi->Get(ice->ice, \"lufrag\",  ufrag, sizeof(ufrag));\n\t\tpiceapi->Get(ice->ice, \"lpwd\",\t pwd, sizeof(pwd));\n\n\t\tQ_snprintf(message, sizeof(message), \" ufrag/%s pwd/%s\", ufrag, pwd);\n\t}\n\n\tif (updatetype == '+' || updatetype == '=')\n\t{\n\t\tunsigned int i;\n\t\tfor (i = 0; i <= 127; i++)\n\t\t{\n\t\t\tchar codec[256];\n\t\t\tchar codecname[64];\n\t\t\tchar argn[64];\n\t\t\tQ_snprintf(argn, sizeof(argn), \"codec%i\", i);\n\t\t\tif (!piceapi->Get(ice->ice, argn,  codecname, sizeof(codecname)))\n\t\t\t\tcontinue;\n\n\t\t\tif (!strcmp(codecname, \"speex@8000\"))\t\t//speex narrowband\n\t\t\t\tQ_snprintf(codec, sizeof(codec), \"codec/%i/speex/8000\", i);\n\t\t\telse if (!strcmp(codecname, \"speex@16000\"))\t//speex wideband\n\t\t\t\tQ_snprintf(codec, sizeof(codec), \"codec/%i/speex/16000\", i);\n\t\t\telse if (!strcmp(codecname, \"speex@32000\"))\t//speex ultrawideband\n\t\t\t\tQ_snprintf(codec, sizeof(codec), \"codec/%i/speex/32000\", i);\n\t\t\telse if (!strcmp(codecname, \"pcma@8000\"))\t//speex wideband\n\t\t\t\tQ_snprintf(codec, sizeof(codec), \"codec/%i/pcma/8000\", i);\n\t\t\telse if (!strcmp(codecname, \"pcmu@8000\"))\t//speex ultrawideband\n\t\t\t\tQ_snprintf(codec, sizeof(codec), \"codec/%i/pcmu/8000\", i);\n\t\t\telse if (!strcmp(codecname, \"opus@48000\"))\t\t//opus codec.\n\t\t\t\tQ_snprintf(codec, sizeof(codec), \"codec/%i/opus/48000\", i);\n\t\t\telse\n\t\t\t\tcontinue;\n\n\t\t\tif (strlen(message) + strlen(codec) + 2 > 256)\n\t\t\t{\n\t\t\t\tIRC_AddClientMessage(irc, va(\"NOTICE %s :\\001FTEICE %c%s%s\\001\", ice->peer, updatetype, icetype, message));\n\t\t\t\tupdatetype = '%';\n\t\t\t\t*message = 0;\n\t\t\t}\n\n\t\t\tQ_strlcat(message, \" \", sizeof(message));\n\t\t\tQ_strlcat(message, codec, sizeof(message));\n\t\t}\n\t}\n\n/*\tif (*message)\n\t{\n\t\tIRC_AddClientMessage(irc, va(\"NOTICE %s :\\001FTEICE %c%s%s\\001\", ice->peer, updatetype, icetype, message));\n\t\t*message = 0;\n\t}\n*/\n\tif (updatetype != '+' && updatetype != '-')\n\t{\n\t\twhile ((c = piceapi->GetLCandidateInfo(ice->ice)))\n\t\t{\n\t\t\tchar type[] = \"hspr\";\n\t\t\tchar cand[256];\n\t\t\tQ_snprintf(cand, sizeof(cand), \"cand/\"\n\t\t\t\t\"%c%c/%i/%i/\"\n\t\t\t\t\"%i/%i/%i/\"\n\t\t\t\t\"%i/%s/%s\", \n\t\t\t\ttype[c->type], 'u', c->priority, c->port, \n\t\t\t\tc->network, c->generation, c->foundation,\n\t\t\t\tc->component, c->candidateid, c->addr);\n\n\t\t\tif (strlen(message) + strlen(cand) + 2 > 256)\n\t\t\t{\n\t\t\t\tIRC_AddClientMessage(irc, va(\"NOTICE %s :\\001FTEICE %c%s%s\\001\", ice->peer, updatetype, icetype, message));\n\t\t\t\tupdatetype = '%';\n\t\t\t\t*message = 0;\n\t\t\t}\n\n\t\t\tQ_strlcat(message, \" \", sizeof(message));\n\t\t\tQ_strlcat(message, cand, sizeof(message));\n\t\t}\n\t}\n\tif (*message || updatetype != '%')\n\t\tIRC_AddClientMessage(irc, va(\"NOTICE %s :\\001FTEICE %c%s%s\\001\", ice->peer, updatetype, icetype, message));\n}\n\nstatic void IRC_ICE_ParseCandidate(struct icestate_s *ice, char *cand)\n{\n\tchar *addr;\n\tstruct icecandinfo_s info;\n\tif (strlen(cand) < 12)\n\t\treturn;\n\tswitch(cand[5])\n\t{\n\tcase 'h':\tinfo.type = ICE_HOST;\tbreak;\n\tcase 's':\tinfo.type = ICE_SRFLX;\tbreak;\n\tcase 'p':\tinfo.type = ICE_PRFLX;\tbreak;\n\tdefault:\n\tcase 'r':\tinfo.type = ICE_RELAY;\tbreak;\n\t}\n\tinfo.transport\t= (cand[6] == 't')?1:0;\n\tinfo.priority\t= strtol(cand+8, &cand, 0); if (*cand != '/')return;\n\tinfo.port\t\t= strtol(cand+1, &cand, 0); if (*cand != '/')return;\n\tinfo.network\t= strtol(cand+1, &cand, 0); if (*cand != '/')return;\n\tinfo.generation\t= strtol(cand+1, &cand, 0); if (*cand != '/')return;\n\tinfo.foundation\t= strtol(cand+1, &cand, 0); if (*cand != '/')return;\n\tinfo.component\t= strtol(cand+1, &cand, 0); if (*cand != '/')return;\n\taddr = strchr(cand+1, '/');\n\tif (!addr)\n\t\treturn;\n\t*addr++ = 0;\n\tQ_strlcpy(info.candidateid, cand+1, sizeof(info.candidateid));\n\tQ_strlcpy(info.addr, addr, sizeof(info.candidateid));\n\t\n\tpiceapi->AddRCandidateInfo(ice, &info);\n}\n\nstatic void IRC_ICE_ParseCodec(struct icestate_s *ice, char *codec)\n{\n\tchar *start;\n\tunsigned int num;\n\tchar name[64];\n\tunsigned int rate;\n\tnum\t\t= strtoul(codec+6, &codec, 0); if (*codec != '/')return;\n\tstart = codec+1; codec = strchr(codec, '/'); if (!codec)return;*codec = 0; Q_strlcpy(name, start, sizeof(name));\n\trate = strtoul(codec+1, &codec, 0); \n\n\tQ_strlcat(name, va(\"@%u\", rate), sizeof(name));\n\n\tpiceapi->Set(ice, va(\"codec%i\", num), name);\n}\n\nstatic void IRC_ICE_Parse(ircclient_t *irc, const char *sender, char *message)\n{\n\tstruct ircice_s *ice;\n\tchar token[256];\n\tenum iceproto_e type = ICEP_INVALID;\n\tmessage = COM_Parse(message, token, sizeof(token));\n\tif (*token == '+' || *token == '=' || *token == '*' || *token == '%')\n\t{\t//+ is offer or accept for a new content type\n\t\t//= is an ack from the receiver\n\t\t//* is the final handshake that includes offerer's full details\n\t\t//% is extra updates.\n\t\tchar icetype = *token;\n\t\tif (!strcmp(token+1, \"voice\"))\n\t\t\ttype = ICEP_VOICE;\n\t\telse if (!strcmp(token+1, \"qwserver\"))\n\t\t\ttype = ICEP_QWSERVER;\n\t\telse if (!strcmp(token+1, \"qwclient\"))\n\t\t\ttype = ICEP_QWCLIENT;\n\t\telse\n\t\t{\n\t\t\tIRC_Printf(irc, sender, \"ICE session type %s is not recognised\\n\", token);\n\t\t\treturn;\n\t\t}\n\n\t\tice = IRC_ICE_Find(irc, sender, type);\n\t\tif (!ice && (icetype == '+' || icetype == '='))\n\t\t\tice = IRC_ICE_Create(irc, sender, type, false);//icetype=='=');\n\n\t\tif (ice)\n\t\t{\n\t\t\twhile(message)\n\t\t\t{\n\t\t\t\tmessage = COM_Parse(message, token, sizeof(token));\n\t\t\t\tif (!strncmp(token, \"cand/\", 5))\n\t\t\t\t\tIRC_ICE_ParseCandidate(ice->ice, token);\n\t\t\t\telse if (!strncmp(token, \"codec/\", 6))\n\t\t\t\t\tIRC_ICE_ParseCodec(ice->ice, token);\n\t\t\t\telse if (!strncmp(token, \"ufrag/\", 6))\n\t\t\t\t\tpiceapi->Set(ice->ice, \"rufrag\", token+6);\n\t\t\t\telse if (!strncmp(token, \"pwd/\", 4))\n\t\t\t\t\tpiceapi->Set(ice->ice, \"rpwd\", token+4);\n\t\t\t\telse if (*token)\n\t\t\t\t\tIRC_Printf(irc, sender, \"unknown ice token %s\\n\", token);\n\t\t\t}\n\n\t\t\tif ((icetype == '=' || icetype == '*') && !ice->accepted && ice->allowed)\n\t\t\t{\n\t\t\t\tpiceapi->Set(ice->ice, \"state\", STRINGIFY(ICE_CONNECTING));\n\t\t\t\tice->accepted = true;\n\t\t\t}\n\n\t\t\tswitch(icetype)\n\t\t\t{\n\t\t\tcase '+':\n\t\t\t\t//needs user \n\t\t\t\tif (!ice->allowed)\n\t\t\t\t{\n\t\t\t\t\tswitch(type)\n\t\t\t\t\t{\n\t\t\t\t\tcase ICEP_VOICE:\tIRC_Printf(irc, sender, \"%s is trying to call you. ^[[Click to Converse]\\\\act\\\\iceaccept_v\\\\who\\\\%s^] ^[[Click to Decline]\\\\act\\\\icedecline_v\\\\who\\\\%s^] \\n\", sender, sender, sender); break;\n\t\t\t\t\tcase ICEP_QWSERVER: IRC_Printf(irc, sender, \"%s wants you to join their game. ^[[Click to Join]\\\\act\\\\iceaccept_s\\\\who\\\\%s^] ^[[Click to Decline]\\\\act\\\\icedecline_s\\\\who\\\\%s^] \\n\", sender, sender, sender); break;\n\t\t\t\t\tcase ICEP_QWCLIENT: IRC_Printf(irc, sender, \"%s is trying to gatecrash your game. ^[[Click to Allow]\\\\act\\\\iceaccept_c\\\\who\\\\%s^] ^[[Click to Decline]\\\\act\\\\icedecline_c\\\\who\\\\%s^] \\n\", sender, sender, sender); break;\n\t\t\t\t\tcase ICEP_INVALID:\tbreak;\n\t\t\t\t\tcase ICEP_VIDEO:\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tswitch(type)\n\t\t\t\t\t{\n\t\t\t\t\tcase ICEP_VOICE:\tIRC_Printf(irc, sender, \"Accepting voice call\\n\"); break;\n\t\t\t\t\tcase ICEP_QWSERVER: IRC_Printf(irc, sender, \"Accepting game invite\\n\"); break;\n\t\t\t\t\tcase ICEP_QWCLIENT: IRC_Printf(irc, sender, \"Accepting gatecrash\\n\"); break;\n\t\t\t\t\tcase ICEP_INVALID:\tbreak;\n\t\t\t\t\tcase ICEP_VIDEO:\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tIRC_ICE_Update(irc, ice, '=');\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase '=':\n\t\t\t\tswitch(type)\n\t\t\t\t{\n\t\t\t\tcase ICEP_VOICE:\tIRC_Printf(irc, sender, \"Establishing voice call\\n\"); break;\n\t\t\t\tcase ICEP_QWSERVER: IRC_Printf(irc, sender, \"Establishing game invite\\n\"); break;\n\t\t\t\tcase ICEP_QWCLIENT: IRC_Printf(irc, sender, \"Establishing gatecrash\\n\"); break;\n\t\t\t\tcase ICEP_INVALID:\tbreak;\n\t\t\t\tcase ICEP_VIDEO:\tbreak;\n\t\t\t\t}\n\t\t\t\tIRC_ICE_Update(irc, ice, '*');\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tswitch(type)\n\t\t\t\t{\n\t\t\t\tcase ICEP_VOICE:\tIRC_Printf(irc, sender, \"Updating voice call\\n\"); break;\n\t\t\t\tcase ICEP_QWSERVER: IRC_Printf(irc, sender, \"Updating game invite\\n\"); break;\n\t\t\t\tcase ICEP_QWCLIENT: IRC_Printf(irc, sender, \"Updating gatecrash\\n\"); break;\n\t\t\t\tcase ICEP_INVALID:\tbreak;\n\t\t\t\tcase ICEP_VIDEO:\tbreak;\n\t\t\t\t}\n\t\t\t\tIRC_ICE_Update(irc, ice, '%');\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\telse if (*token == '-')\n\t{\n\t\tIRC_Printf(irc, sender, \"dropping connections is not supported yet: %s\\n\", token);\n\t}\n\telse\n\t\tIRC_Printf(irc, sender, \"ICE command type not supported: %s\\n\", token);\n}\n\nstatic void IRC_ICE_Frame(ircclient_t *irc)\n{\n\tchar bah[8];\n\tstruct ircice_s *ice;\n\tfor (ice = irc->ice; ice; ice = ice->next)\n\t{\n\t\tif (!ice->accepted || !ice->allowed)\n\t\t\tcontinue;\n\t\t//ice needs some maintainence. if things change then we need to be prepared to send updated candidate info\n\t\tpiceapi->Get(ice->ice, \"newlc\", bah, sizeof(bah));\n\t\tif (atoi(bah))\n\t\t{\n\t\t\tIRC_Printf(irc, ice->peer, \"Sending updated peer info\\n\");\n\t\t\tIRC_ICE_Update(irc, ice, '%');\n\t\t}\n\n\t\t//FIXME: detect when the ice connection goes idle.\n\t}\n}\n\nstatic void IRC_ICE_Authorise(ircclient_t *irc, const char *with, enum iceproto_e type, qboolean authorize, char *announce)\n{\n\tstruct ircice_s *ice, **link;\n\tfor (link = &irc->ice; *link; link = &(*link)->next)\n\t{\n\t\tice = *link;\n\t\tif (ice->type == type)\n\t\t\tif (!strcmp(ice->peer, with))\n\t\t\t{\n\t\t\t\tif (authorize)\n\t\t\t\t{\n\t\t\t\t\tif (ice->allowed)\n\t\t\t\t\t{\n\t\t\t\t\t\tIRC_Printf(irc, announce, \"Connection is already authorised\\n\");\n\t\t\t\t\t\treturn;\t//nothing to do\n\t\t\t\t\t}\n\n\t\t\t\t\t//yay! its good to go!\n\t\t\t\t\tice->allowed = true;\n\t\t\t\t\tswitch(type)\n\t\t\t\t\t{\n\t\t\t\t\tcase ICEP_VOICE:\tIRC_Printf(irc, announce, \"Accepting voice call\\n\"); break;\n\t\t\t\t\tcase ICEP_QWSERVER: IRC_Printf(irc, announce, \"Accepting game invite\\n\"); break;\n\t\t\t\t\tcase ICEP_QWCLIENT: IRC_Printf(irc, announce, \"Accepting gatecrash\\n\"); break;\n\t\t\t\t\tcase ICEP_VIDEO:\tbreak;\n\t\t\t\t\tcase ICEP_INVALID:\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tIRC_ICE_Update(irc, ice, '=');\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tIRC_ICE_Update(irc, ice, '-');\n\t\t\t\t\t*link = ice->next;\n\t\t\t\t\tif (ice->ice)\n\t\t\t\t\t\tpiceapi->Close(ice->ice, true);\n\t\t\t\t\tIRC_Free(ice);\n\n\t\t\t\t\tIRC_Printf(irc, announce, \"Connection terminated\\n\");\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\t}\n\n\tIRC_Printf(irc, announce, \"Connection is already terminated\\n\");\n}\n\nqboolean IRC_ConsoleLink(void)\n{\n\tircclient_t *irc;\n\tchar link[256];\n\tchar *who = NULL;\n\tchar *channel = NULL;\n\tchar what[256];\n\tchar whobuf[256];\n\tchar which[512];\n\tenum iceproto_e type;\n//\tcmdfuncs->Argv(0, text, sizeof(text));\n\tcmdfuncs->Argv(1, link, sizeof(link));\n\tcmdfuncs->Argv(2, which, sizeof(which));\n\n\tPlug_Info_ValueForKey(link, \"act\", what, sizeof(what));\n\twho = Plug_Info_ValueForKey(link, \"who\", whobuf, sizeof(whobuf));\n\n\tfor (irc = ircclients; irc; irc = irc->next)\n\t{\n\t\tif (!strncmp(irc->id, which, strlen(irc->id)))\n\t\t{\n\t\t\tchannel = which + strlen(irc->id);\n\t\t\tif (!*who)\n\t\t\t\twho = channel;\n\t\t\tbreak;\n\t\t}\n\t}\n\tif (!irc || !who || !*what)\n\t\treturn false;\n\n\tif (!strcmp(what, \"reconnect\"))\n\t{\n\t\tif (handleisvalid(irc->socket))\n\t\t\tIRC_Printf(irc, channel, \"Already %s.\\n\", irc->connecting?\"reconnecting\":\"connected\");\n\t\telse if (IRC_Establish(irc))\n\t\t\tIRC_Printf(irc, channel, \"Reconnecting...\\n\");\n\t\telse\n\t\t\tIRC_Printf(irc, channel, \"Unable to connect\\n\");\n\t\treturn true;\n\t}\n\tif (!*who)\n\t\treturn false;\t//that seems wrong. probably nothing to do with irc.\n\n\tif (irc->tlsmode == TLS_STARTING || irc->connecting)\n\t{\n\t\tIRC_SetFooter(irc, channel, \"Still connecting. Please wait.\\n\");\n\t\treturn true;\n\t}\n\n\tif (!strcmp(what, \"iceaccept_v\") || !strcmp(what, \"iceaccept_s\") || !strcmp(what, \"iceaccept_c\"))\n\t{\n\t\tswitch(what[10])\n\t\t{default:\n\t\tcase 'v': type = ICEP_VOICE; break;\n\t\tcase 's': type = ICEP_QWSERVER; break;\n\t\tcase 'c': type = ICEP_QWCLIENT; break;\n\t\t}\n\t\tIRC_Printf(irc, channel, \"Accepting foo from %s\\n\", who);\n\t\tIRC_ICE_Authorise(irc, who, type, true, channel);\n\t\treturn true;\n\t}\n\telse if (!strcmp(what, \"icedecline_v\") || !strcmp(what, \"icedecline_s\") || !strcmp(what, \"icedecline_c\"))\n\t{\n\t\tswitch(what[11])\n\t\t{default:\n\t\tcase 'v': type = ICEP_VOICE; break;\n\t\tcase 's': type = ICEP_QWSERVER; break;\n\t\tcase 'c': type = ICEP_QWCLIENT; break;\n\t\t}\n\t\tIRC_ICE_Authorise(irc, who, type, false, channel);\n\t\treturn true;\n\t}\n\telse if (!strcmp(what, \"user\"))\n\t{\n\t\tchar links[2048];\n\t\tchar link[512];\n\t\tQ_snprintf(links, sizeof(links), \"%s:\", who);\n\t\tif (1)\n\t\t{\n\t\t\tQ_snprintf(link, sizeof(link), \" ^[[Message]\\\\act\\\\msg\\\\who\\\\%s^]\", who);\n\t\t\tQ_strlcat(links, link, sizeof(links));\n\t\t}\n\t\tif (IRC_ICE_Find(irc, who, ICEP_VOICE))\n\t\t{\n\t\t\tQ_snprintf(link, sizeof(link), \" ^[[Hang up]\\\\act\\\\icedecline_v\\\\who\\\\%s^]\", who);\n\t\t\tQ_strlcat(links, link, sizeof(links));\n\t\t}\n\t\telse\n\t\t{\n\t\t\tQ_snprintf(link, sizeof(link), \" ^[[Call]\\\\act\\\\icestart_v\\\\who\\\\%s^]\", who);\n\t\t\tQ_strlcat(links, link, sizeof(links));\n\t\t}\n\t\tif (IRC_ICE_Find(irc, who, ICEP_QWSERVER))\n\t\t{\n\t\t\tQ_snprintf(link, sizeof(link), \" ^[[Disconnect]\\\\act\\\\icedecline_s\\\\who\\\\%s^]\", who);\n\t\t\tQ_strlcat(links, link, sizeof(links));\n\t\t}\n\t\telse\n\t\t{\n\t\t\tQ_snprintf(link, sizeof(link), \" ^[[Invite]\\\\act\\\\icestart_s\\\\who\\\\%s^]\", who);\n\t\t\tQ_strlcat(links, link, sizeof(links));\n\t\t}\n\t\tif (IRC_ICE_Find(irc, who, ICEP_QWCLIENT))\n\t\t{\n\t\t\tQ_snprintf(link, sizeof(link), \" ^[[Disconnect]\\\\act\\\\icedecline_c\\\\who\\\\%s^]\", who);\n\t\t\tQ_strlcat(links, link, sizeof(links));\n\t\t}\n\t\telse\n\t\t{\n\t\t\tQ_snprintf(link, sizeof(link), \" ^[[Gatecrash]\\\\act\\\\icestart_c\\\\who\\\\%s^]\", who);\n\t\t\tQ_strlcat(links, link, sizeof(links));\n\t\t}\n\t\tIRC_SetFooter(irc, channel, links);\n\t\treturn true;\n\t}\n\telse if (!strcmp(what, \"msg\"))\n\t{\n\t\tIRC_Printf(irc, who, \"\");\n\t\treturn true;\n\t}\n\telse if (!strcmp(what, \"icestart_v\") || !strcmp(what, \"icestart_s\") || !strcmp(what, \"icestart_c\"))\n\t{\n\t\tstruct ircice_s *ice;\n\t\tchar *text;\n\t\tchar link[512];\n\t\tswitch(what[9])\n\t\t{default:\n\t\tcase 'v': type = ICEP_VOICE;\ttext = \"Calling\";\t\tbreak;\n\t\tcase 's': type = ICEP_QWSERVER; text = \"Inviting\";\t\tbreak;\n\t\tcase 'c': type = ICEP_QWCLIENT; text = \"Gatecrashing\";\tbreak;\n\t\t}\n\t\tice = IRC_ICE_Create(irc, who, type, true);\n\t\tif (ice)\n\t\t{\n\t\t\tQ_snprintf(link, sizeof(link), \"^[\"COLOURGREEN\"%s\\\\act\\\\user^]\", ice->peer);\n\t\t\tIRC_ICE_Update(irc, ice, '+');\n\t\t\tIRC_Printf(irc, ice->peer, \"<%s %s ^[[Abort]\\\\act\\\\icedecline_%c\\\\who\\\\%s^]>\\n\", text, link, what[9], ice->peer);\n\t\t}\n\n\t\treturn true;\n\t}\n\treturn false;\n}\n\n//==================================================\n\nstatic int IRC_ClientFrame(ircclient_t *irc)\n{\n\tchar prefix[256];\n\tint ret;\n\tchar *nextmsg, *msg;\n\tchar *temp;\n\tchar token[1024];\n\tchar var[9][1000];\n\n\tint i = 1;\n\n\tret = netfuncs->Recv(irc->socket, irc->bufferedinmessage+irc->bufferedinammount, sizeof(irc->bufferedinmessage)-1 - irc->bufferedinammount);\n\tif (ret == 0)\n\t{\n\t\tif (!irc->bufferedinammount)\t//if we are half way through a message, read any possible conjunctions.\n\t\t\treturn IRC_DONE;\t//remove\n\t}\n\tif (ret < 0)\n\t\treturn IRC_KILL;\n\n\tif (ret>0)\n\t\tirc->bufferedinammount+=ret;\n\tirc->bufferedinmessage[irc->bufferedinammount] = '\\0';\n\tnextmsg = strstr(irc->bufferedinmessage, \"\\r\\n\");\n\tif (!nextmsg)\n\t\treturn IRC_DONE;\n\n\t*nextmsg = '\\0';\n\tnextmsg+=2;\n\n\tmsg = irc->bufferedinmessage;\n\n\tstrcpy(var[1],msg);\n\n\ttemp = strchr(var[1], ' ');\n\n\twhile (i < 8)\n\t{\n\t\ti++;\n\n\t\tif (temp != NULL)\n\t\t{\n\t\t\tstrcpy(var[i],temp+1);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tstrcpy(var[i], \"\");\n\t\t}\n\n\t\ttemp=strchr(var[i], ' ');\n\n\t}\n\n//\tif (irc_debug->value == 1) { IRC_Printf(irc, DEFAULTCONSOLE,COLOURRED \"!!!!! ^11: %s ^22: %s ^33: %s ^44: %s ^55: %s ^66: %s ^77: %s ^88: %s\\n\",var[1],var[2],var[3],var[4],var[5],var[6],var[7],var[8]); }\n\tif (irc_debug->value == 1) { IRC_Printf(irc, DEFAULTCONSOLE,COLOURRED \"%s\\n\",var[1]); }\n\n\tif (*msg == ':')\t//we need to strip off the prefix\n\t{\n\t\tchar *sp = strchr(msg, ' ');\n\t\tif (!sp)\n\t\t{\n\t\t\tIRC_Printf(irc, DEFAULTCONSOLE, \"Ignoring bad message\\n%s\\n\", msg);\n\t\t\tmemmove(irc->bufferedinmessage, nextmsg, irc->bufferedinammount - (msg-irc->bufferedinmessage));\n\t\t\tirc->bufferedinammount-=nextmsg-irc->bufferedinmessage;\n\t\t\treturn IRC_CONTINUE;\n\t\t}\n\n\t\tif (sp-msg >= sizeof(prefix))\n\t\t\tQ_strlcpy(prefix, msg+1, sizeof(prefix));\n\t\telse\n\t\t\tQ_strlcpy(prefix, msg+1, sp-msg);\n\n\t\tmsg = sp;\n\t\twhile(*msg == ' ')\n\t\t\tmsg++;\n\t}\n\telse\n\t\tstrcpy(prefix, irc->server);\n\n\tif (!strncmp(var[1], \"NOTICE AUTH \", 12))\n\t{\n\t\tIRC_Printf(irc, DEFAULTCONSOLE, COLOURGREEN \"SERVER NOTICE: %s\\n\", var[3]+1);\n\t}\n\telse if (!strncmp(var[1], \"PING \", 5))\n\t{\n\t\tIRC_AddClientMessage(irc, va(\"PONG %s\", var[2]));\n\t}\n\telse if (!strncmp(var[2], \"NOTICE \", 6))\n\t{\n\t\tchar *exc = strchr(prefix, '!');\n\t\tchar *col = strchr(msg+6, ':');\n\t\tchar *end;\n\t\tchar *to = msg + 7;\n\t\tchar *etghack;\n\n\t\tif (!strncmp(var[4]+1, \"\\1\", 1))\n\t\t{\n\t\t\tchar delimiters[] = \"!\";\n\t\t\tchar *username = strtok(var[1]+1, delimiters);\n\t\t\tchar *ctcpreplytype = strtok(var[4]+2, \" \");\n\t\t\tchar *ctcpreply = var[5];\n\n\t\t\tif (!strcmp(ctcpreplytype, \"FTEICE\"))\n\t\t\t{\n\t\t\t\tIRC_Printf(irc, username, \"ICE from %s\\n\", username);\t//from client\n\t\t\t\tIRC_ICE_Parse(irc, username, ctcpreply);\n\t\t\t}\n\t\t\telse\n\t\t\t\tIRC_Printf(irc, DEFAULTCONSOLE,\"<CTCP Reply> %s FROM %s: %s\\n\",ctcpreplytype,username,ctcpreply); // need to remove the last char on the end of ctcpreply\n\t\t}\n\t\telse if (exc && col)\n\t\t{\n\t\t\t*col = '\\0';\n\t\t\tcol++;\n\n\t\t\twhile(*to <= ' ' && *to)\n\t\t\t\tto++;\n\t\t\tfor (end = to + strlen(to)-1; end >= to && *end <= ' '; end--)\n\t\t\t\t*end = '\\0';\n\t\t\tif (!strcmp(to, irc_nick->string))\n\t\t\t\tto = prefix;\t//This was directed straight at us.\n\t\t\t\t\t\t\t\t//So change the 'to', to the 'from'.\n\n\t\t\tfor (end = to; *end; end++)\n\t\t\t{\n\t\t\t\tif (*end >= 'A' && *end <= 'Z')\n\t\t\t\t\t*end = *end + 'a' - 'A';\n\t\t\t}\n\n\t\t\t*exc = '\\0';\n\t\t\tif (!strncmp(col, \"\\001\", 1))\n\t\t\t{\n\t\t\t\tend = strchr(col+1, '\\001');\n\t\t\t\tif (end)\n\t\t\t\t\t*end = '\\0';\n\t\t\t\tif (!strncmp(col+1, \"ACTION \", 7))\n\t\t\t\t{\n\t\t\t\t\tIRC_FilterMircColours(col+8);\n\t\t\t\t\tIRC_Printf(irc, to, COLOURGREEN \"***%s \"COLORWHITE\"%s\\n\", prefix, col+8);\t//from client\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tIRC_FilterMircColours(col);\n\t\t\t\tIRC_Printf(irc, DEFAULTCONSOLE, COLOURGREEN \"NOTICE: -%s- %s\\n\", prefix, col);\t//from client\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tetghack = strtok(var[1],\"\\n\");\n\n\t\t\tif (!irc->connecting || IRC_WindowShown(irc, DEFAULTCONSOLE))\n\t\t\t\tIRC_Printf(irc, DEFAULTCONSOLE, COLOURGREEN \"SERVER NOTICE: <%s> %s\\n\", prefix, etghack);\n\n//\t\t\tstrcpy(servername,prefix);\n\n\t\t\twhile (1)\n\t\t\t{\n\t\t\t\tetghack = strtok(NULL, \"\\n\");\n\n\t\t\t\tif (etghack == NULL)\n\t\t\t\t\tbreak;\n\n\t\t\t\tmagic_etghack(etghack);\n\n\t\t\t\tif (atoi(subvar[2]) != 0)\n\t\t\t\t\tnumbered_command(atoi(subvar[2]), etghack, irc);\n\t\t\t\telse\n\t\t\t\t\tIRC_Printf(irc, DEFAULTCONSOLE, COLOURGREEN \"SERVER NOTICE: <%s> %s\\n\", prefix, subvar[4]);\n\n\t\t\t}\n\t\t}\n\n\t}\n\telse if (!strncmp(var[2], \"PRIVMSG \", 7))\t//no autoresponses to notice please, and any autoresponses should be in the form of a notice\n\t{\n\t\tchar *exc = strchr(prefix, '!');\n\t\tchar *col = strchr(msg+6, ':');\n\t\tchar *end;\n\t\tchar *to = msg + 7;\n\n\t\t//message takes the form :FROM PRIVMSG TO :MESSAGE\n\n\t\tif (drawfuncs)\n\t\t\tdrawfuncs->LocalSound (\"misc/talk.wav\", 256, 1);\n\n\t\tif ((!strcasecmp(var[4]+1, \"\\1VERSION\\1\")) && (!strncmp(var[2], \"PRIVMSG \", 7)))\n\t\t{\n\t\t\tchar *username;\n\t\t\tchar delimiters[] = \"!\";\n\n\t\t\tusername = strtok(var[1]+1, delimiters);\n\n\t\t\tIRC_AddClientMessage(irc, va(\"NOTICE %s :\\1VERSION FTEQW-IRC-Plugin Release: %s\", username, RELEASE));\n\t\t}\n\t\telse if ((!strcasecmp(var[4]+1, \"\\1TIME\\1\")) && (!strncmp(var[2], \"PRIVMSG \", 7)))\n\t\t{\n\t\t\tchar delimiters[] = \"!\";\n\t\t\tchar *username = strtok(var[1], delimiters);\n\t\t\ttime_t t;\n\t\t\tconst struct tm *tm;\n\t\t\tchar buffer[100];\n\n\t\t\ttime(&t);\n\t\t\ttm=localtime(&t);\n\n\t\t\tstrftime (buffer, 100, \"%a %b %d %H:%M:%S\", tm);\n\n\t\t\tIRC_AddClientMessage(irc, va(\"NOTICE %s :\\1TIME %s\\1\", username, buffer));\n\t\t}\n\t\telse if (exc && col)\n\t\t{\n\t\t\tchar link[256];\n\t\t\t*col = '\\0';\n\t\t\tcol++;\n\n\t\t\twhile(*to <= ' ' && *to)\n\t\t\t\tto++;\n\t\t\tfor (end = to + strlen(to)-1; end >= to && *end <= ' '; end--)\n\t\t\t\t*end = '\\0';\n\t\t\tif (!strcmp(to, irc->nick))\n\t\t\t\tto = prefix;\t//This was directed straight at us.\n\t\t\t\t\t\t\t\t//So change the 'to', to the 'from'.\n\n\t\t\tfor (end = to; *end; end++)\n\t\t\t{\n\t\t\t\tif (*end >= 'A' && *end <= 'Z')\n\t\t\t\t\t*end = *end + 'a' - 'A';\n\t\t\t}\n\n\t\t\t*exc = '\\0';\n\n\t\t\t//a link to interact with the sender\n\t\t\tif (Q_snprintfz(link, sizeof(link), \"^[\"COLOURGREEN\"%s\\\\act\\\\user\\\\who\\\\%s^]\", prefix, prefix))\n\t\t\t\tQ_snprintf(link, sizeof(link), \"%s\", prefix);\n\n\t\t\tif (!strncmp(col, \"\\001\", 1))\n\t\t\t{\n\t\t\t\tend = strchr(col+1, '\\001');\n\t\t\t\tif (end)\n\t\t\t\t\t*end = '\\0';\n\t\t\t\tif (!strncmp(col+1, \"ACTION \", 7))\n\t\t\t\t{\n\t\t\t\t\tIRC_FilterMircColours(col+8);\n\t\t\t\t\tIRC_Printf(irc, to, \"***%s %s\\n\", link, col+8);\t//from client\n\t\t\t\t}\n\t\t\t\telse if (!strncmp(col+1, \"PING \", 5))\n\t\t\t\t{\n\t\t\t\t\ttime_t currentseconds;\n\n\t\t\t\t\tcurrentseconds = time (NULL);\n\n\t\t\t\t\tIRC_Printf(irc, to, \"CTCP Ping from %s\\n\", link);\t//from client\n\t\t\t\t\tIRC_AddClientMessage(irc, va(\"NOTICE %s :\\001PING %u\\001\\r\\n\", prefix, (unsigned int)currentseconds));\n\t\t\t\t}\n\t\t\t\telse if (!strncmp(col+1, \"VERSION \", 8))\n\t\t\t\t{\n\t\t\t\t\tIRC_Printf(irc, to, \"CTCP Version from %s\\n\", link);\t//from client\n\t\t\t\t\tIRC_AddClientMessage(irc, va(\"NOTICE %s :\\001VERSION \"FULLENGINENAME\" \"RELEASE\" \\001\\r\\n\", prefix));\n\t\t\t\t}\n\t\t\t\telse if (!strncmp(col+1, \"FTEICE \", 7))\n\t\t\t\t{\n\t\t\t\t\tIRC_Printf(irc, to, \"ICE from %s\\n\", link);\t//from client\n\t\t\t\t\tIRC_ICE_Parse(irc, to, col+8);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tif (end)//put it back on. might as well.\n\t\t\t\t\t\t*end = '\\001';\n\t\t\t\t\tIRC_Printf(irc, to, \"%s: %s\\n\", link, col);\t//from client\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tIRC_FilterMircColours(col);\n\t\t\t\tIRC_Printf(irc, to, \"%s: %s\\n\", link, col);\t//from client\n\t\t\t}\n\t\t}\n\t\telse IRC_Printf(irc, DEFAULTCONSOLE, COLOURGREEN \"SERVER: <%s> %s\\n\", prefix, msg);\t//direct server message\n\t}\n\telse if (!strncmp(var[2], \"MODE \", 5))\n\t{\n\t\tchar *username = strtok(var[1]+1, \"! \");\n\t\tchar *mode = strtok(var[4], \" \");\n\t\tchar *target = strtok(var[5], \" \");\n\t\tchar channel[100];\n\n\t\tif (!strncmp(var[3], \"#\", 1))\n\t\t{\n\t\t\tstrcpy(channel,strtok(var[3], \" \"));\n\t\t}\n\t\telse\n\t\t{\n\t\t\tstrcpy(channel,DEFAULTCONSOLE);\n\t\t}\n\n\t\tif ((!strncmp(mode+1,\"o\", 1)) || (!strncmp(mode+1,\"v\",1))) // ops or voice\n\t\t{\n\t\t\tIRC_Printf(irc, channel,COLOURGREEN \"%s sets mode %s on %s\\n\",username,mode,target);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (IRC_WindowShown(irc, channel))\n\t\t\t\tIRC_Printf(irc, channel, COLOURGREEN \"%s sets mode %s\\n\",username,mode);\n\t\t}\n\n\t}\n\telse if (!strncmp(var[2], \"KICK \", 5))\n\t{\n\t\tchar *username = strtok(var[1]+1, \"!\");\n\t\tchar *channel = strtok(var[3], \" \");\n\t\tchar *target = strtok(var[4], \" \");\n\t\tchar *reason = var[5]+1;\n\n\t\tIRC_Printf(irc, channel,COLOURGREEN \"%s was kicked from %s Reason: '%s' by %s\\n\",target,channel,reason,username);\n\t}\n\telse if (!strncmp(msg, \"NICK \", 5))\n\t{\n\t\tchar *exc = strchr(prefix, '!');\n\t\tchar *col = strchr(msg+5, ':');\n\t\tif (exc && col)\n\t\t{\n\t\t\t*exc = '\\0';\n\t\t\t//fixme: print this in all channels as appropriate.\n\t\t\tIRC_Printf(irc, DEFAULTCONSOLE, COLOURGREEN \"%s changes name to %s\\n\", prefix, col+1);\n\t\t\tif (confuncs)\n\t\t\t{\n\t\t\t\tchar oldname[256];\n\t\t\t\tchar newname[256];\n\t\t\t\tQ_snprintf(oldname, sizeof(oldname), irc->id, prefix);\n\t\t\t\tQ_snprintf(newname, sizeof(newname), irc->id, col+1);\n\t\t\t\tconfuncs->RenameSub(oldname, newname);\t//if we were pming to them, rename accordingly.\n\t\t\t}\n\t\t}\n\t\telse IRC_Printf(irc, DEFAULTCONSOLE, COLOURGREEN \":%s%s\\n\", prefix, msg+6);\n\t}\n\telse if (!strncmp(msg, \"QUIT \", 5))\n\t{\n\t\t/*char *exc = strchr(prefix, '!');\n\t\tchar *col = strchr(msg+5, ':');\n\t\tif (exc && col)\n\t\t{\n\t\t\t*exc = '\\0';\n\t\t\tIRC_Printf(irc, col+1, COLOURGREEN \"%s joins channel %s\\n\", prefix, col+1);\n\t\t}\n\t\telse IRC_Printf(irc, DEFAULTCONSOLE, COLOURGREEN \":%s QUIT %s\\n\", prefix, msg+5);*/\n\t}\n\telse if (!strncmp(msg, \"PART \", 5))\n\t{\n\t\tchar *exc = strchr(prefix, '!');\n\t\tCOM_Parse(msg+5, token, sizeof(token));\n\t\tif (exc)\n\t\t{\n\t\t\t*exc = '\\0';\n\t\t\tIRC_Printf(irc, token, \"%s leaves channel %s\\n\", prefix, token);\n\t\t}\n\t\telse IRC_Printf(irc, DEFAULTCONSOLE, COLOURGREEN \":%sPART %s\\n\", prefix, msg+5);\n\t}\n\telse if (!strncmp(msg, \"JOIN \", 5))\n\t{\n\t\tchar *exc = strchr(prefix, '!');\n\t\tchar *col = strchr(msg+5, ':');\n\t\tif (exc && col)\n\t\t{\n\t\t\t*exc = '\\0';\n\t\t\tIRC_Printf(irc, col+1, COLOURGREEN \"%s joins channel %s\\n\", prefix, col+1);\n\t\t}\n\t\telse IRC_Printf(irc, DEFAULTCONSOLE, COLOURGREEN \":%s JOIN %s\\n\", prefix, msg+5);\n\t}\n\telse if (!strncmp(msg, \"372 \", 4))\n\t{\n\t\tchar *text = strstr(msg, \":-\");\n\t\tif (!*irc->autochannels || irc_motd->value)\n\t\t{\n\t\t\tif (text)\n\t\t\t\tIRC_Printf(irc, DEFAULTCONSOLE, \"%s\\n\", text+2);\n\t\t\telse\n\t\t\t\tIRC_Printf(irc, DEFAULTCONSOLE, \"%s\\n\", msg);\n\t\t}\n\t}\n\telse if (!strncmp(msg, \"TOPIC \", 5))\n\t{\n\t\tchar *topic = COM_Parse(msg+5, token, sizeof(token));\n\t\twhile (*topic == ' ')\n\t\t\ttopic++;\n\t\tif (*topic++ == ':')\n\t\t{\n\t\t\tchar *exc = strchr(prefix, '!');\n\t\t\tif (exc)\n\t\t\t\t*exc = 0;\n\t\t\tIRC_Printf(irc, token, COLOURGREEN \"%s changes topic to %s\\n\", prefix, topic);\n\t\t}\n\t\telse IRC_Printf(irc, DEFAULTCONSOLE, COLOURGREEN \":%s TOPIC %s\\n\", prefix, msg+5);\n\t}\n\telse if (!strncmp(msg, \"331 \", 4) ||//no topic\n\t\t\t !strncmp(msg, \"332 \", 4))\t//the topic\n\t{\n\t\tchar *topic;\n\t\tchar *chan;\n\t\ttopic = COM_Parse(msg, token, sizeof(token));\n\t\ttopic = COM_Parse(topic, token, sizeof(token));\n\t\ttopic = COM_Parse(topic, token, sizeof(token));\n\t\twhile(*topic == ' ')\n\t\t\ttopic++;\n\t\tif (*topic == ':')\n\t\t{\n\t\t\ttopic++;\n\t\t\tchan = token;\n\t\t}\n\t\telse\n\t\t{\n\t\t\ttopic = \"No topic\";\n\t\t\tchan = DEFAULTCONSOLE;\n\t\t}\n\n\t\tIRC_Printf(irc, chan, \"Topic on channel %s is: \"COLOURGREEN\"%s\\n\", chan, topic);\n\t}\n\telse if (!strncmp(msg, \"353 \", 4))\t//the names of people on a channel\n\t{\n\t\tchar *eq = strstr(msg, \"=\"); // BAD SPIKE!! = is normal channel :(\n\t\tchar *eq2 = strstr(msg, \"@\"); // @ means the channel is +s (secret)\n\t\tchar *eq3 = strstr(msg, \"*\"); // * means the channel is +p (private) rather redundant...\n\t\tchar *channeltype = strtok(var[4], \" \");\n\t\tchar *channel = strtok(var[5], \" \");\n\t\tchar *str;\n\n\n\t\tint secret = 0;\n\t\tint privatechan = 0;\n\t\tif ( !strcmp(channeltype,\"=\") )\n\t\t{\n\t\t\tchar *end;\n\t\t\teq++;\n\t\t\tstr = strstr(eq, \":\");\n\t\t\twhile(*eq == ' ')\n\t\t\t\teq++;\n\t\t\tfor (end = eq; *end>' '&&*end !=':'; end++)\n\t\t\t\t;\n\t\t\t*end = '\\0';\n\t\t\tstr++;\n\t\t}\n\t\t//else if (eq2)\n\t\telse if ( !strcmp(channeltype,\"@\") )\n\t\t{\n\t\t\tchar *end;\n\n\t\t\tsecret = 1;\n\n\t\t\teq2++;\n\t\t\tstr = strstr(eq2, \":\");\n\t\t\twhile(*eq2 == ' ')\n\t\t\t\teq2++;\n\t\t\tfor (end = eq2; *end>' '&&*end !=':'; end++)\n\t\t\t\t;\n\t\t\t*end = '\\0';\n\t\t\tstr++;\n\t\t}\n\t\telse if ( !strcmp(channeltype,\"*\") )\n\t\t{\n\t\t\tchar *end;\n\n\t\t\tprivatechan = 1;\n\n\t\t\teq3++;\n\t\t\tstr = strstr(eq3, \":\");\n\t\t\twhile(*eq3 == ' ')\n\t\t\t\teq3++;\n\t\t\tfor (end = eq3; *end>' '&&*end !=':'; end++)\n\t\t\t\t;\n\t\t\t*end = '\\0';\n\t\t\tstr++;\n\t\t}\n\t\telse\n\t\t{\n\t\t\teq = \"Corrupted_Message\";\n\t\t\tstr = NULL;\n\t\t}\n\t\tIRC_Printf(irc, channel, \"Users on channel %s:\\n\", channel);\n\t\twhile (str)\n\t\t{\n\t\t\tstr = COM_Parse(str, token, sizeof(token));\n\t\t\tif (*token == '@')\t//they're an operator\n\t\t\t\tIRC_Printf(irc, channel, \"^[@\"COLOURGREEN\"%s\\\\act\\\\user\\\\who\\\\%s\\\\tip\\\\Channel Operator^]\\n\", token+1, token+1);\n\t\t\telse if (*token == '%')\t//they've got half-op\n\t\t\t\tIRC_Printf(irc, channel, \"^[%%\"COLOURGREEN\"%s\\\\act\\\\user\\\\who\\\\%s\\\\tip\\\\Channel Half-Operator^]\\n\", token+1, token+1);\n\t\t\telse if (*token == '+')\t//they've got voice\n\t\t\t\tIRC_Printf(irc, channel, \"^[+\"COLOURGREEN\"%s\\\\act\\\\user\\\\who\\\\%s\\\\tip\\\\Voice^]\\n\", token+1, token+1);\n\t\t\telse\n\t\t\t\tIRC_Printf(irc, channel, \" ^[\"COLOURGREEN\"%s\\\\act\\\\user\\\\who\\\\%s^]\\n\", token, token);\n\t\t}\n\t\tif (secret == 1)\n\t\t{\n\t\t\tIRC_Printf(irc, channel, \"%s is secret (+s)\\n\",channel);\n\t\t}\n\t\telse if (privatechan == 1)\n\t\t{\n\t\t\tIRC_Printf(irc, channel, \"%s is private (+p)\\n\",channel);\n\t\t}\n\n\t}\n\t// would be great to convert the above to work better\n\telse if (atoi(var[2]) != 0)\n\t{\n//\t\tchar *rawparameter = strtok(var[4], \" \");\n//\t\tchar *rawmessage = var[5];\n//\t\tchar *wholerawmessage = var[4];\n\n\t\tnumbered_command(atoi(var[2]), msg, irc);\n\n\t\tif (irc_debug->value == 1) { IRC_Printf(irc, DEFAULTCONSOLE, \"%s\\n\", msg); }\n\t}\n\telse\n\t\tIRC_Printf(irc, DEFAULTCONSOLE, \"%s\\n\", msg);\n\n\tmemmove(irc->bufferedinmessage, nextmsg, irc->bufferedinammount - (msg-irc->bufferedinmessage));\n\tirc->bufferedinammount-=nextmsg-irc->bufferedinmessage;\n\treturn IRC_CONTINUE;\n}\n\n//functions above this line allow connections to multiple servers.\n//it is just the control functions that only allow one server.\n\nvoid IRC_Frame(double realtime, double gametime)\n{\n\tircclient_t *ircclient;\n\tif (reloadconfig)\n\t{\n\t\treloadconfig = false;\n\t\tIRC_ParseConfig();\n\t}\n\tfor (ircclient = ircclients; ircclient; ircclient = ircclient->next)\n\t{\n\t\tint stat = IRC_CONTINUE;\n\t\tif (!handleisvalid(ircclient->socket))\n\t\t\tcontinue;\t//this connection isn't enabled.\n\t\twhile(stat == IRC_CONTINUE)\n\t\t{\n\t\t\tstat = IRC_ClientFrame(ircclient);\n\t\t\tif (ircclient->bufferedoutammount)\n\t\t\t{\n\t\t\t\tint flushed = netfuncs->Send(ircclient->socket, ircclient->bufferedoutmessage, ircclient->bufferedoutammount);\t//FIXME: This needs rewriting to cope with errors+throttle.\n\t\t\t\tif (flushed > 0)\n\t\t\t\t{\n\t\t\t\t\tmemmove(ircclient->bufferedoutmessage, ircclient->bufferedoutmessage+flushed, ircclient->bufferedoutammount - flushed);\n\t\t\t\t\tircclient->bufferedoutammount -= flushed;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (ircclient->quitting && !ircclient->bufferedoutammount)\n\t\t\tstat = IRC_KILL;\n\t\tif (stat == IRC_KILL)\n\t\t{\n\t\t\tnetfuncs->Close(ircclient->socket);\n\t\t\tircclient->socket = invalid_handle;\n\t\t\tIRC_Printf(ircclient, DEFAULTCONSOLE, \"Disconnected from irc\\n^[[Reconnect]\\\\act\\\\reconnect^]\\n\");\n\t\t\tbreak;\t//lazy\n\t\t}\n\t\telse\n\t\t\tIRC_ICE_Frame(ircclient);\n\t}\n}\n\nvoid IRC_Command(ircclient_t *ircclient, char *dest, char *args)\n{\n\tchar token[1024];\n\tchar *msg;\n\n\tmsg = COM_Parse(args, token, sizeof(token));\n\n\tif (*token == '/')\n\t{\n\t\tif (!strcmp(token+1, \"server\"))\n\t\t{\t//selects the default server without connecting anywhere, for main console to use.\n\t\t\tmsg = COM_Parse(msg, token, sizeof(token));\n\n\t\t\tircclient = IRC_FindAccount(token);\n\t\t\tif (!ircclient)\n\t\t\t\tIRC_Printf(ircclient, dest, \"No such connection\\n\");\n\t\t\telse if (ircclients == ircclient)\n\t\t\t\tIRC_Printf(ircclient, dest, \"Connection is already the default.\\n\");\n\t\t\telse\n\t\t\t{\n\t\t\t\tIRC_MakeDefault(ircclient);\n\t\t\t\tIRC_Printf(ircclient, dest, \"Connection is now default.\\n\");\n\t\t\t}\n\t\t}\n\t\telse if (!strcmp(token+1, \"open\") || !strcmp(token+1, \"connect\"))\n\t\t{\n\t\t\tchar server[256];\n\t\t\tchar channels[1024];\n\t\t\tchar nick[256];\n\t\t\tchar password[256];\n\n\t\t\tmsg = COM_Parse(msg, server, sizeof(server));\n\t\t\tmsg = COM_Parse(msg, channels, sizeof(channels));\n\t\t\tmsg = COM_Parse(msg, nick, sizeof(nick));\n\t\t\tmsg = COM_Parse(msg, password, sizeof(password));\n\n\t\t\t//set up some defaults\n\t\t\tif (!*nick)\n\t\t\t\tQ_strlcpy(nick, irc_nick->string, sizeof(nick));\n\t\t\tif (!*nick)\n\t\t\t\tcvarfuncs->GetString(\"name\", nick, sizeof(nick));\n\n\t\t\tircclient = IRC_FindAccount(server);\n\t\t\tif (ircclient)\n\t\t\t{\n\t\t\t\tif (handleisvalid(ircclient->socket))\t//don't need to do anything.\n\t\t\t\t{\n\t\t\t\t\tIRC_Printf(ircclient, dest, \"IRC connection to %s already registered\\n\", server);\n\t\t\t\t\treturn;\t//silently ignore it if the account already exists\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t\tircclient = IRC_Create(server, nick, irc_realname->string, irc_hostname->string, irc_username->string, password, channels);\n\t\t\tif (ircclient)\n\t\t\t{\n\t\t\t\tIRC_MakeDefault(ircclient);\n\t\t\t\tircclient->persist |= !strcmp(token+1, \"connect\");\n\t\t\t\tif (IRC_Establish(ircclient))\n\t\t\t\t\tIRC_Printf(ircclient, dest, \"Trying to connect\\n\");\n\t\t\t\telse\n\t\t\t\t\tIRC_Printf(ircclient, dest, \"Unable to connect\\n\");\n\t\t\t}\n\t\t\telse\n\t\t\t\tIRC_Printf(ircclient, dest, \"Unable to open account\\n\");\n\n\t\t\tIRC_WriteConfig();\n\t\t}\n\t\telse if (!strcmp(token+1, \"help\"))\n\t\t{\n\t\t\tIRC_Printf(ircclient, dest, \"to connect to a server: /connect SERVER \\\"#chan #chan2,chan2password\\\" NICK SERVERPASSWORD\\n\");\n\t\t\tIRC_Printf(ircclient, dest, \"to disconnect from a server: /quit\\n\");\n\t\t\tIRC_Printf(ircclient, dest, \"to join a channel: /join\\n\");\n\t\t\tIRC_Printf(ircclient, dest, \"to leave a channel: /part\\n\");\n\t\t\tIRC_Printf(ircclient, dest, \"note that servers and channels will be remembered\\n\");\n\t\t}\n\t\telse if (!strcmp(token+1, \"nick\"))\n\t\t{\n\t\t\tmsg = COM_Parse(msg, token, sizeof(token));\n\t\t\tif (!ircclient)\t//not yet connected.\n\t\t\t\tcvarfuncs->SetString(irc_nick->name, token);\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (!handleisvalid(ircclient->socket))\n\t\t\t\t\tQ_strlcpy(ircclient->primarynick, token, sizeof(ircclient->primarynick));\n\t\t\t\tircclient->nicktries = 0;\n\t\t\t\tIRC_SetNick(ircclient, token);\n\t\t\t}\n\n\t\t\tIRC_WriteConfig();\n\t\t}\n\t\telse if (!strcmp(token+1, \"user\"))\n\t\t{\n\t\t\tmsg = COM_Parse(msg, token, sizeof(token));\n\t\t\tcvarfuncs->SetString(irc_username->name, token);\n\t\t\tif (ircclient)\n\t\t\t\tIRC_SetUser(ircclient, token);\n\n\t\t\tIRC_WriteConfig();\n\t\t}\n\t\telse if (!strcmp(token+1, \"info\") || !strcmp(token+1, \"status\"))\n\t\t{\n\t\t\tircclient_t *e;\n\t\t\tstruct ircice_s *ice;\n\t\t\tfor (e = ircclients; e; e = e->next)\n\t\t\t{\n\t\t\t\tIRC_Printf(ircclient, dest, \"SERVER: ^[%s\\\\type\\\\irc /server \\\"%s\\\"^]\\n\", e->server, e->server);\n\t\t\t\tif (e->connecting && handleisvalid(e->socket))\n\t\t\t\t\tIRC_Printf(ircclient, dest, \"<CONNECTING>\\n\");\n\t\t\t\telse if (handleisvalid(e->socket))\n\t\t\t\t\tIRC_Printf(ircclient, dest, \"<CONNECTED>\\n\");\n\t\t\t\telse\n\t\t\t\t\tIRC_Printf(ircclient, dest, \"<DISCONNECTED>\\n\");\n\t\t\t\tif (e->quitting)\n\t\t\t\t\tIRC_Printf(ircclient, dest, \"<QUITTING>\\n\");\n\t\t\t\tswitch(e->tlsmode)\n\t\t\t\t{\n\t\t\t\tdefault:\n\t\t\t\tcase TLS_OFF:\n\t\t\t\t\tIRC_Printf(ircclient, dest, \"TLS: insecure\\n\");\n\t\t\t\t\tbreak;\n\t\t\t\tcase TLS_INITIAL:\n\t\t\t\t\tIRC_Printf(ircclient, dest, \"TLS: initial\\n\");\n\t\t\t\t\tbreak;\n\t\t\t\tcase TLS_START:\n\t\t\t\tcase TLS_STARTING:\n\t\t\t\t\tIRC_Printf(ircclient, dest, \"TLS: upgrade\\n\");\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tIRC_Printf(ircclient, dest, \"nick: %s\\n\", e->nick);\n\t\t\t\tIRC_Printf(ircclient, dest, \"realname: %s\\n\", e->realname);\n\t\t\t\tIRC_Printf(ircclient, dest, \"hostname: %s\\n\", e->hostname);\n\t\t\t\tIRC_Printf(ircclient, dest, \"rejoin: %s\\n\", *e->autochannels?e->autochannels:\"<no channels>\");\n\t\t\t\tIRC_Printf(ircclient, dest, \"default dest: %s\\n\", e->defaultdest);\n\t\t\t\tfor (ice = e->ice; ice; ice = ice->next)\n\t\t\t\t{\n\t\t\t\t\tchar *allowed=ice->allowed?\" allowed\":\" not-allowed\";\n\t\t\t\t\tchar *accepted=ice->accepted?\" accepted\":\" not-accepted\";\n\t\t\t\t\tswitch(ice->type)\n\t\t\t\t\t{\n\t\t\t\t\tdefault:\n\t\t\t\t\tcase ICEP_INVALID:\n\t\t\t\t\t\tIRC_Printf(ircclient, dest, \" <INVALID ICE>\\n\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase ICEP_QWSERVER:\n\t\t\t\t\t\tIRC_Printf(ircclient, dest, \" server: \\\"%s\\\"%s%s\\n\", ice->peer, allowed, accepted);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase ICEP_QWCLIENT:\n\t\t\t\t\t\tIRC_Printf(ircclient, dest, \" client: \\\"%s\\\"%s%s\\n\", ice->peer, allowed, accepted);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase ICEP_VOICE:\n\t\t\t\t\t\tIRC_Printf(ircclient, dest, \" voice: \\\"%s\\\"%s%s\\n\", ice->peer, allowed, accepted);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase ICEP_VIDEO:\n\t\t\t\t\t\tIRC_Printf(ircclient, dest, \" voice: \\\"%s\\\"%s%s\\n\", ice->peer, allowed, accepted);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse if (!ircclient)\n\t\t{\n\t\t\tIRC_Printf(ircclient, dest, \"Not connected, please connect to an irc server first.\\n\");\n\t\t}\n\n\t\t//ALL other commands require you to be connected.\n\t\telse if (!strcmp(token+1, \"list\"))\n\t\t{\n\t\t\tIRC_AddClientMessage(ircclient, \"LIST\");\n\t\t}\n\t\telse if ( !strcmp(token+1, \"join\") || !strcmp(token+1, \"j\") )\n\t\t{\n\t\t\tchar chan[256];\n\t\t\tchar pwd[256];\n\n\t\t\tif (ircclient->tlsmode == TLS_STARTING || ircclient->connecting)\n\t\t\t{\n\t\t\t\tIRC_Printf(ircclient, dest, \"Still connecting. Please wait.\\n\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tmsg = COM_Parse(msg, chan, sizeof(chan));\n\t\t\tmsg = COM_Parse(msg, pwd, sizeof(pwd));\n\n\t\t\tIRC_JoinChannel(ircclient,chan,pwd);\n\t\t\tIRC_WriteConfig();\n\t\t}\n\t\telse if (!strcmp(token+1, \"part\") || !strcmp(token+1, \"leave\")) // need to implement leave reason\n\t\t{\n\t\t\tmsg = COM_Parse(msg, token, sizeof(token));\n\t\t\tIRC_PartChannel(ircclient, *token?token:dest);\n\t\t\tIRC_WriteConfig();\n\t\t}\n\t\telse if (!strcmp(token+1, \"call\"))\n\t\t{\n\t\t\tmsg = COM_Parse(msg, token, sizeof(token));\n\t\t\tif (ircclient->tlsmode == TLS_STARTING || ircclient->connecting)\n\t\t\t{\n\t\t\t\tIRC_Printf(ircclient, dest, \"Still connecting. Please wait.\\n\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t{\n\t\t\t\tstruct ircice_s *ice = IRC_ICE_Create(ircclient, *token?token:dest, ICEP_VOICE, true);\n\t\t\t\tif (ice)\n\t\t\t\t{\n\t\t\t\t\tIRC_ICE_Update(ircclient, ice, '+');\n\t\t\t\t\tIRC_Printf(ircclient, ice->peer, \"<Calling %s>\\n\", ice->peer);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse if (!strcmp(token+1, \"ginvite\"))\n\t\t{\n\t\t\tmsg = COM_Parse(msg, token, sizeof(token));\n\t\t\tif (ircclient->tlsmode == TLS_STARTING || ircclient->connecting)\n\t\t\t{\n\t\t\t\tIRC_Printf(ircclient, dest, \"Still connecting. Please wait.\\n\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t{\n\t\t\t\tstruct ircice_s *ice = IRC_ICE_Create(ircclient, *token?token:dest, ICEP_QWSERVER, true);\n\t\t\t\tif (ice)\n\t\t\t\t{\n\t\t\t\t\tIRC_ICE_Update(ircclient, ice, '+');\n\t\t\t\t\tIRC_Printf(ircclient, ice->peer, \"<inviting %s>\\n\", ice->peer);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse if (!strcmp(token+1, \"gatecrash\"))\n\t\t{\n\t\t\tmsg = COM_Parse(msg, token, sizeof(token));\n\t\t\tif (ircclient->tlsmode == TLS_STARTING || ircclient->connecting)\n\t\t\t{\n\t\t\t\tIRC_Printf(ircclient, dest, \"Still connecting. Please wait.\\n\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t{\n\t\t\t\tstruct ircice_s *ice = IRC_ICE_Create(ircclient, *token?token:dest, ICEP_QWCLIENT, true);\n\t\t\t\tif (ice)\n\t\t\t\t{\n\t\t\t\t\tIRC_ICE_Update(ircclient, ice, '+');\n\t\t\t\t\tIRC_Printf(ircclient, ice->peer, \"<gatecrashing %s>\\n\", ice->peer);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse if (!strcmp(token+1, \"msg\"))\n\t\t{\n\t\t\tmsg = COM_Parse(msg, token, sizeof(token));\n\t\t\tif (!msg)\n\t\t\t\treturn;\n\t\t\tif (ircclient->tlsmode == TLS_STARTING || ircclient->connecting)\n\t\t\t{\n\t\t\t\tIRC_Printf(ircclient, dest, \"Still connecting. Please wait.\\n\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tIRC_AddClientMessage(ircclient, va(\"PRIVMSG %s :%s\", token, msg+1));\n\t\t\tIRC_Printf(ircclient, token, \"%s: %s\\n\", ircclient->nick, msg);\n\t\t}\n\t\telse if (!strcmp(token+1, \"quote\") || !strcmp(token+1, \"raw\"))\n\t\t{\n\t\t\tIRC_AddClientMessage(ircclient, va(\"%s\", msg));\n\t\t}\n\t\telse if (!strcmp(token+1, \"reconnect\"))\n\t\t{\n\t\t\tif (IRC_Establish(ircclient))\n\t\t\t\tIRC_Printf(ircclient, dest, \"Trying to connect\\n\");\n\t\t\telse\n\t\t\t\tIRC_Printf(ircclient, dest, \"Unable to connect\\n\");\n\t\t}\n\t\telse if (!strcmp(token+1, \"quit\") || !strcmp(token+1, \"disconnect\"))\n\t\t{\n\t\t\tmsg = COM_Parse(msg, token, sizeof(token));\n\t\t\tif (*token)\n\t\t\t\tIRC_AddClientMessage(ircclient, va(\"QUIT :%s\", token));\n\t\t\telse\n\t\t\t\tIRC_AddClientMessage(ircclient, va(\"QUIT :%s\", irc_quitmessage->string));\n\t\t\tircclient->quitting = true;\n\n\t\t\tIRC_WriteConfig();\n\t\t}\n\t\telse if (!strcmp(token+1, \"whois\"))\n\t\t{\n\t\t\tmsg = COM_Parse(msg, token, sizeof(token));\n\t\t\tIRC_AddClientMessage(ircclient, va(\"WHOIS :%s\",token));\n\t\t}\n\t\telse if (!strcmp(token+1, \"away\"))\n\t\t{\n\t\t\tif ( strlen(msg) > 1 )\n\t\t\t\tIRC_AddClientMessage(ircclient, va(\"AWAY :%s\",msg+1));\n\t\t\telse\n\t\t\t\tIRC_AddClientMessage(ircclient, va(\"AWAY :\"));\n\t\t}\n\t\telse if (!strcmp(token+1, \"motd\"))\n\t\t{\n\t\t\tIRC_AddClientMessage(ircclient, \"MOTD\");\n\t\t}\n\t\telse if (!strcmp(token+1, \"ctcp\"))\n\t\t{\n\t\t\tmsg = COM_Parse(msg, token, sizeof(token));\n\t\t\tIRC_AddClientMessage(ircclient, va(\"PRIVMSG %s :\\1%s\\1\",token,msg+1));\n\t\t}\n\t\telse if (!strcmp(token+1, \"dest\"))\n\t\t{\n\t\t\tmsg = COM_Parse(msg, token, sizeof(token));\n\t\t\tQ_strlcpy(ircclient->defaultdest, token, sizeof(ircclient->defaultdest));\n\t\t}\n\t\telse if (!strcmp(token+1, \"ping\"))\n\t\t{\n\t\t\tif (!*dest)\n\t\t\t\tIRC_Printf(ircclient, DEFAULTCONSOLE, \"No channel joined. Try /join #<channel>\\n\");\n\t\t\telse\n\t\t\t\tIRC_AddClientMessage(ircclient, va(\"PRIVMSG %s :\\001PING%s\\001\", dest, msg));\n\t\t}\n\t\telse if (!strcmp(token+1, \"notice\"))\n\t\t{\n\t\t\tmsg = COM_Parse(msg, token, sizeof(token));\n\t\t\tIRC_AddClientMessage(ircclient, va(\"NOTICE %s :%s\",token, msg+1));\n\t\t}\n\t\telse if (!strcmp(token+1, \"me\"))\n\t\t{\n\t\t\tif (!*dest)\n\t\t\t\tIRC_Printf(ircclient, DEFAULTCONSOLE, \"No channel joined. Try /join #<channel>\\n\");\n\t\t\telse\n\t\t\t{\n\t\t\t\tif(*msg <= ' ' && *msg)\n\t\t\t\t\tmsg++;\n\t\t\t\tIRC_AddClientMessage(ircclient, va(\"PRIVMSG %s :\\001ACTION %s\\001\", dest, msg));\n\t\t\t\tIRC_Printf(ircclient, dest, \"***^3%s^7 %s\\n\", ircclient->nick, msg);\n\t\t\t}\n\t\t}\n\t\telse if (!strcmp(token+1, \"topic\"))\n\t\t{\n\t\t\tif (!*dest)\n\t\t\t\tIRC_Printf(ircclient, DEFAULTCONSOLE, \"No channel joined. Try /join #<channel>\\n\");\n\t\t\telse\n\t\t\t{\n\t\t\t\tif(*msg <= ' ' && *msg)\n\t\t\t\t\tmsg++;\n\t\t\t\tIRC_AddClientMessage(ircclient, va(\"TOPIC %s :%s\", dest, msg));\n\t\t\t}\n\t\t}\n\t\telse\n\t\t\tIRC_Printf(ircclient, dest, \"Command not recognised\\n\");\n\t}\n\telse\n\t{\n\t\tif (ircclient)\n\t\t{\n\t\t\tif (!handleisvalid(ircclient->socket))\n\t\t\t\tIRC_Printf(ircclient, dest, \"Connection was closed. use /reconnect\\n\");\n\t\t\telse if (ircclient->tlsmode == TLS_STARTING || ircclient->connecting)\n\t\t\t\tIRC_Printf(ircclient, dest, \"Still connecting. Please wait.\\n\");\n\t\t\telse if (!*dest)\n\t\t\t{\n\t\t\t\tIRC_Printf(ircclient, dest, \"No channel joined. Try /join #<channel>\\n\");\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tmsg = args;\n\t\t\t\twhile (*msg == ' ')\n\t\t\t\t\tmsg++;\n\t\t\t\tif (!*msg)\n\t\t\t\t\treturn;\t//this is apparently an error. certainly wasteful.\n\t\t\t\tIRC_AddClientMessage(ircclient, va(\"PRIVMSG %s :%s\", dest, msg));\n\t\t\t\tIRC_Printf(ircclient, dest, \"^3%s^7: %s\\n\", ircclient->nick, msg);\n\t\t\t}\n\t\t}\n\t\telse\n\t\t\tIRC_Printf(ircclient, \"Not connected\\ntype \\\"%s /open IRCSERVER [#channel1[,#channel2[,...]]] [nick]\\\" to connect\\n\", COMMANDNAME);\n\t}\n}\n"
  },
  {
    "path": "plugins/irc/ircclient.q3asm",
    "content": "-o \"ircclient\"\nplugin\nircclient\nqvm_api"
  },
  {
    "path": "plugins/irc/raw codes.txt",
    "content": "\t/*\n\telse if (!strncmp(msg, \"401 \", 4))\n\t{\n\t\tdprintf(\"Nickname/channel does not exist\\n\", msg);\n\t}\n\n  \telse if (!strncmp(msg, \"402 \", 4))\n\t{\n\t\tdprintf(\"No such server\\n\", msg);\n\t}\n\telse if (!strncmp(msg, \"403 \", 4))\n\t{\n\t\tdprintf(\"No such channel\\n\", msg);\n\t}\n\telse if (!strncmp(msg, \"404 \", 4))\n\t{\n\t\tdprintf(\"Cannot send to that channel\\n\", msg);\n\t}\n\telse if (!strncmp(msg, \"405 \", 4))\n\t{\n\t\tdprintf(\"You may not join annother channel\\n\", msg);\n\t}\n\telse if (!strncmp(msg, \"406 \", 4))\n\t{\n\t\tdprintf(\"Nickname does not exist\\n\", msg);\n\t}\n\telse if (!strncmp(msg, \"407 \", 4))\n\t{\n\t\tdprintf(\"Too many targets. Try to specify a specific nickname.\\n\", msg);\n\t}\n\n\telse if (!strncmp(msg, \"409 \", 4))\n\t{\n\t\tdprintf(\"No origin specified\\n\", msg);\n\t}\n\telse if (!strncmp(msg, \"411 \", 4))\n\t{\n\t\tdprintf(\"No recipient given.\\n\", msg);\n\t}\n\telse if (!strncmp(msg, \"412 \", 4))\n\t{\n\t\tdprintf(\"No text given\\n\", msg);\n\t}\n\telse if (!strncmp(msg, \"413 \", 4))\n\t{\n\t\tdprintf(\"No top level domain specified\\n\", msg);\n\t}\n\telse if (!strncmp(msg, \"414 \", 4))\n\t{\n\t\tdprintf(\"Wildcard in toplevel domain\\n\", msg);\n\t}\n\telse if (!strncmp(msg, \"421 \", 4))\n\t{\n\t\tdprintf(\"Unknown command.\\n\", msg);\n\t}\n\telse if (!strncmp(msg, \"422 \", 4))\n\t{\n\t\tdprintf(\"MOTD file is missing (awww)\\n\", msg);\n\t}\n\telse if (!strncmp(msg, \"423 \", 4))\n\t{\n\t\tdprintf(\"No administrative info is available\\n\", msg);\n\t}\n\telse if (!strncmp(msg, \"424 \", 4))\n\t{\n\t\tdprintf(\"Generic file error\\n\", msg);\n\t}\n  \telse if (!strncmp(msg, \"441 \", 4))\n\t{\n\t\tdprintf(\"User isn't in that channel\\n\", msg);\n\t}\n\telse if (!strncmp(msg, \"442 \", 4))\n\t{\n\t\tdprintf(\"You are not on that channel\\n\", msg);\n\t}\n\telse if (!strncmp(msg, \"443 \", 4))\n\t{\n\t\tdprintf(\"The user is already on that channel\\n\", msg);\n\t}\n\telse if (!strncmp(msg, \"444 \", 4))\n\t{\n\t\tdprintf(\"User not logged in\\n\", msg);\n\t}\n\telse if (!strncmp(msg, \"445 \", 4))\n\t{\n\t\tdprintf(\"SUMMON has been disabled\\n\", msg);\n\t}\n\telse if (!strncmp(msg, \"446 \", 4))\n\t{\n\t\tdprintf(\"USERS has been disabled\\n\", msg);\n\t}\n\telse if (!strncmp(msg, \"451 \", 4))\n\t{\n\t\tdprintf(\"You have not registered\\n\", msg);\n\t}\n\n\n        461     ERR_NEEDMOREPARAMS\n                        \"<command> :Not enough parameters\"\n\n                - Returned by the server by numerous commands to\n                  indicate to the client that it didn't supply enough\n                  parameters.\n\n        462     ERR_ALREADYREGISTRED\n                        \":You may not reregister\"\n\n                - Returned by the server to any link which tries to\n                  change part of the registered details (such as\n                  password or user details from second USER message).\n\n        463     ERR_NOPERMFORHOST\n                        \":Your host isn't among the privileged\"\n\n                - Returned to a client which attempts to register with\n                  a server which does not been setup to allow\n                  connections from the host the attempted connection\n                  is tried.\n\n\n\n--------------------------------------------------------------------------------\nPage 47\n\n        464     ERR_PASSWDMISMATCH\n                        \":Password incorrect\"\n\n                - Returned to indicate a failed attempt at registering\n                  a connection for which a password was required and\n                  was either not given or incorrect.\n\n        465     ERR_YOUREBANNEDCREEP\n                        \":You are banned from this server\"\n\n                - Returned after an attempt to connect and register\n                  yourself with a server which has been setup to\n                  explicitly deny connections to you.\n\n        467     ERR_KEYSET\n                        \"<channel> :Channel key already set\"\n        471     ERR_CHANNELISFULL\n                        \"<channel> :Cannot join channel (+l)\"\n        472     ERR_UNKNOWNMODE\n                        \"<char> :is unknown mode char to me\"\n        473     ERR_INVITEONLYCHAN\n                        \"<channel> :Cannot join channel (+i)\"\n        474     ERR_BANNEDFROMCHAN\n                        \"<channel> :Cannot join channel (+b)\"\n        475     ERR_BADCHANNELKEY\n                        \"<channel> :Cannot join channel (+k)\"\n        481     ERR_NOPRIVILEGES\n                        \":Permission Denied- You're not an IRC operator\"\n\n                - Any command requiring operator privileges to operate\n                  must return this error to indicate the attempt was\n                  unsuccessful.\n\n        482     ERR_CHANOPRIVSNEEDED\n                        \"<channel> :You're not channel operator\"\n\n                - Any command requiring 'chanop' privileges (such as\n                  MODE messages) must return this error if the client\n                  making the attempt is not a chanop on the specified\n                  channel.\n\n        483     ERR_CANTKILLSERVER\n                        \":You cant kill a server!\"\n\n                - Any attempts to use the KILL command on a server\n                  are to be refused and this error returned directly\n                  to the client.\n\n\n\n--------------------------------------------------------------------------------\nPage 48\n\n        491     ERR_NOOPERHOST\n                        \":No O-lines for your host\"\n\n                - If a client sends an OPER message and the server has\n                  not been configured to allow connections from the\n                  client's host as an operator, this error must be\n                  returned.\n\n        501     ERR_UMODEUNKNOWNFLAG\n                        \":Unknown MODE flag\"\n\n                - Returned by the server to indicate that a MODE\n                  message was sent with a nickname parameter and that\n                  the a mode flag sent was not recognized.\n\n        502     ERR_USERSDONTMATCH\n                        \":Cant change mode for other users\"\n\n                - Error sent to any user trying to view or change the\n                  user mode for a user other than themselves.\n\n\n6.2 Command responses.\n\n        300     RPL_NONE\n                        Dummy reply number. Not used.\n\n        302     RPL_USERHOST\n                        \":[<reply>{<space><reply>}]\"\n\n                - Reply format used by USERHOST to list replies to\n                  the query list.  The reply string is composed as\n                  follows:\n\n                  <reply> ::= <nick>['*'] '=' <'+'|'-'><hostname>\n\nThe '*' indicates whether the client has registered as an Operator. The '-' or '+' characters represent whether the client has set an AWAY message or not respectively.\n\n\n        303     RPL_ISON\n                        \":[<nick> {<space><nick>}]\"\n\n                - Reply format used by ISON to list replies to the\n                  query list.\n\n        301     RPL_AWAY\n                        \"<nick> :<away message>\"\n\n\n\n--------------------------------------------------------------------------------\nPage 49\n\n        305     RPL_UNAWAY\n                        \":You are no longer marked as being away\"\n        306     RPL_NOWAWAY\n                        \":You have been marked as being away\"\n\n                - These replies are used with the AWAY command (if\n                  allowed).  RPL_AWAY is sent to any client sending a\n                  PRIVMSG to a client which is away.  RPL_AWAY is only\n                  sent by the server to which the client is connected.\n                  Replies RPL_UNAWAY and RPL_NOWAWAY are sent when the\n                  client removes and sets an AWAY message.\n\n        311     RPL_WHOISUSER\n                        \"<nick> <user> <host> * :<real name>\"\n        312     RPL_WHOISSERVER\n                        \"<nick> <server> :<server info>\"\n        313     RPL_WHOISOPERATOR\n                        \"<nick> :is an IRC operator\"\n        317     RPL_WHOISIDLE\n                        \"<nick> <integer> :seconds idle\"\n        318     RPL_ENDOFWHOIS\n                        \"<nick> :End of /WHOIS list\"\n        319     RPL_WHOISCHANNELS\n                        \"<nick> :{[@|+]<channel><space>}\"\n\n                - Replies 311 - 313, 317 - 319 are all replies\n                  generated in response to a WHOIS message.  Given that\n                  there are enough parameters present, the answering\n                  server must either formulate a reply out of the above\n                  numerics (if the query nick is found) or return an\n                  error reply.  The '*' in RPL_WHOISUSER is there as\n                  the literal character and not as a wild card.  For\n                  each reply set, only RPL_WHOISCHANNELS may appear\n                  more than once (for long lists of channel names).\n                  The '@' and '+' characters next to the channel name\n                  indicate whether a client is a channel operator or\n                  has been granted permission to speak on a moderated\n                  channel.  The RPL_ENDOFWHOIS reply is used to mark\n                  the end of processing a WHOIS message.\n\n        314     RPL_WHOWASUSER\n                        \"<nick> <user> <host> * :<real name>\"\n        369     RPL_ENDOFWHOWAS\n                        \"<nick> :End of WHOWAS\"\n\n                - When replying to a WHOWAS message, a server must use\n                  the replies RPL_WHOWASUSER, RPL_WHOISSERVER or\n                  ERR_WASNOSUCHNICK for each nickname in the presented\n\n\n\n--------------------------------------------------------------------------------\nPage 50\nlist. At the end of all reply batches, there must be RPL_ENDOFWHOWAS (even if there was only one reply and it was an error).\n\n\n        321     RPL_LISTSTART\n                        \"Channel :Users  Name\"\n        322     RPL_LIST\n                        \"<channel> <# visible> :<topic>\"\n        323     RPL_LISTEND\n                        \":End of /LIST\"\n\n                - Replies RPL_LISTSTART, RPL_LIST, RPL_LISTEND mark\n                  the start, actual replies with data and end of the\n                  server's response to a LIST command.  If there are\n                  no channels available to return, only the start\n                  and end reply must be sent.\n\n        324     RPL_CHANNELMODEIS\n                        \"<channel> <mode> <mode params>\"\n\n        331     RPL_NOTOPIC\n                        \"<channel> :No topic is set\"\n        332     RPL_TOPIC\n                        \"<channel> :<topic>\"\n\n                - When sending a TOPIC message to determine the\n                  channel topic, one of two replies is sent.  If\n                  the topic is set, RPL_TOPIC is sent back else\n                  RPL_NOTOPIC.\n\n        341     RPL_INVITING\n                        \"<channel> <nick>\"\n\n                - Returned by the server to indicate that the\n                  attempted INVITE message was successful and is\n                  being passed onto the end client.\n\n        342     RPL_SUMMONING\n                        \"<user> :Summoning user to IRC\"\n\n                - Returned by a server answering a SUMMON message to\n                  indicate that it is summoning that user.\n\n        351     RPL_VERSION\n                        \"<version>.<debuglevel> <server> :<comments>\"\n\n                - Reply by the server showing its version details.\n                  The <version> is the version of the software being\n\n\n\n--------------------------------------------------------------------------------\nPage 51\nused (including any patchlevel revisions) and the\n\n                  <debuglevel> is used to indicate if the server is\n                  running in \"debug mode\".\n\nThe \"comments\" field may contain any comments about the version or further version details.\n\n\n        352     RPL_WHOREPLY\n                        \"<channel> <user> <host> <server> <nick> \\\n                         <H|G>[*][@|+] :<hopcount> <real name>\"\n        315     RPL_ENDOFWHO\n                        \"<name> :End of /WHO list\"\n\n                - The RPL_WHOREPLY and RPL_ENDOFWHO pair are used\n                  to answer a WHO message.  The RPL_WHOREPLY is only\n                  sent if there is an appropriate match to the WHO\n                  query.  If there is a list of parameters supplied\n                  with a WHO message, a RPL_ENDOFWHO must be sent\n                  after processing each list item with <name> being\n                  the item.\n\n        353     RPL_NAMREPLY\n                        \"<channel> :[[@|+]<nick> [[@|+]<nick> [...]]]\"\n        366     RPL_ENDOFNAMES\n                        \"<channel> :End of /NAMES list\"\n\n                - To reply to a NAMES message, a reply pair consisting\n                  of RPL_NAMREPLY and RPL_ENDOFNAMES is sent by the\n                  server back to the client.  If there is no channel\n                  found as in the query, then only RPL_ENDOFNAMES is\n                  returned.  The exception to this is when a NAMES\n                  message is sent with no parameters and all visible\n                  channels and contents are sent back in a series of\n                  RPL_NAMEREPLY messages with a RPL_ENDOFNAMES to mark\n                  the end.\n\n        364     RPL_LINKS\n                        \"<mask> <server> :<hopcount> <server info>\"\n        365     RPL_ENDOFLINKS\n                        \"<mask> :End of /LINKS list\"\n\n                - In replying to the LINKS message, a server must send\n                  replies back using the RPL_LINKS numeric and mark the\n                  end of the list using an RPL_ENDOFLINKS reply.\n\n        367     RPL_BANLIST\n                        \"<channel> <banid>\"\n        368     RPL_ENDOFBANLIST\n\n\n\n--------------------------------------------------------------------------------\nPage 52\n\"<channel> :End of channel ban list\"\n\n\n                - When listing the active 'bans' for a given channel,\n                  a server is required to send the list back using the\n                  RPL_BANLIST and RPL_ENDOFBANLIST messages.  A separate\n                  RPL_BANLIST is sent for each active banid.  After the\n                  banids have been listed (or if none present) a\n                  RPL_ENDOFBANLIST must be sent.\n\n        371     RPL_INFO\n                        \":<string>\"\n        374     RPL_ENDOFINFO\n                        \":End of /INFO list\"\n\n                - A server responding to an INFO message is required to\n                  send all its 'info' in a series of RPL_INFO messages\n                  with a RPL_ENDOFINFO reply to indicate the end of the\n                  replies.\n\n        375     RPL_MOTDSTART\n                        \":- <server> Message of the day - \"\n        376     RPL_ENDOFMOTD\n                        \":End of /MOTD command\"\n\n                - When responding to the MOTD message and the MOTD file\n                  is found, the file is displayed line by line, with\n                  each line no longer than 80 characters, using\n                  RPL_MOTD format replies.  These should be surrounded\n                  by a RPL_MOTDSTART (before the RPL_MOTDs) and an\n                  RPL_ENDOFMOTD (after).\n\n        381     RPL_YOUREOPER\n                        \":You are now an IRC operator\"\n\n                - RPL_YOUREOPER is sent back to a client which has\n                  just successfully issued an OPER message and gained\n                  operator status.\n\n        382     RPL_REHASHING\n                        \"<config file> :Rehashing\"\n\n                - If the REHASH option is used and an operator sends\n                  a REHASH message, an RPL_REHASHING is sent back to\n                  the operator.\n\n        391     RPL_TIME\n\n\n\n--------------------------------------------------------------------------------\nPage 53\n\"<server> :<string showing server's local time>\"\n\n\n                - When replying to the TIME message, a server must send\n                  the reply using the RPL_TIME format above.  The string\n                  showing the time need only contain the correct day and\n                  time there.  There is no further requirement for the\n                  time string.\n\n        392     RPL_USERSSTART\n                        \":UserID   Terminal  Host\"\n        393     RPL_USERS\n                        \":%-8s %-9s %-8s\"\n        394     RPL_ENDOFUSERS\n                        \":End of users\"\n        395     RPL_NOUSERS\n                        \":Nobody logged in\"\n\n                - If the USERS message is handled by a server, the\n                  replies RPL_USERSTART, RPL_USERS, RPL_ENDOFUSERS and\n                  RPL_NOUSERS are used.  RPL_USERSSTART must be sent\n                  first, following by either a sequence of RPL_USERS\n                  or a single RPL_NOUSER.  Following this is\n                  RPL_ENDOFUSERS.\n\n        200     RPL_TRACELINK\n                        \"Link <version & debug level> <destination> \\\n                         <next server>\"\n        201     RPL_TRACECONNECTING\n                        \"Try. <class> <server>\"\n        202     RPL_TRACEHANDSHAKE\n                        \"H.S. <class> <server>\"\n        203     RPL_TRACEUNKNOWN\n                        \"???? <class> [<client IP address in dot form>]\"\n        204     RPL_TRACEOPERATOR\n                        \"Oper <class> <nick>\"\n        205     RPL_TRACEUSER\n                        \"User <class> <nick>\"\n        206     RPL_TRACESERVER\n                        \"Serv <class> <int>S <int>C <server> \\\n                         <nick!user|*!*>@<host|server>\"\n        208     RPL_TRACENEWTYPE\n                        \"<newtype> 0 <client name>\"\n        261     RPL_TRACELOG\n                        \"File <logfile> <debug level>\"\n\n                - The RPL_TRACE* are all returned by the server in\n                  response to the TRACE message.  How many are\n                  returned is dependent on the the TRACE message and\n\n\n\n--------------------------------------------------------------------------------\nPage 54\nwhether it was sent by an operator or not. There is no predefined order for which occurs first. Replies RPL_TRACEUNKNOWN, RPL_TRACECONNECTING and RPL_TRACEHANDSHAKE are all used for connections which have not been fully established and are either unknown, still attempting to connect or in the process of completing the 'server handshake'. RPL_TRACELINK is sent by any server which handles a TRACE message and has to pass it on to another server. The list of RPL_TRACELINKs sent in response to a TRACE command traversing the IRC network should reflect the actual connectivity of the servers themselves along that path.\nRPL_TRACENEWTYPE is to be used for any connection which does not fit in the other categories but is being displayed anyway.\n\n\n        211     RPL_STATSLINKINFO\n                        \"<linkname> <sendq> <sent messages> \\\n                         <sent bytes> <received messages> \\\n                         <received bytes> <time open>\"\n        212     RPL_STATSCOMMANDS\n                        \"<command> <count>\"\n        213     RPL_STATSCLINE\n                        \"C <host> * <name> <port> <class>\"\n        214     RPL_STATSNLINE\n                        \"N <host> * <name> <port> <class>\"\n        215     RPL_STATSILINE\n                        \"I <host> * <host> <port> <class>\"\n        216     RPL_STATSKLINE\n                        \"K <host> * <username> <port> <class>\"\n        218     RPL_STATSYLINE\n                        \"Y <class> <ping frequency> <connect \\\n                         frequency> <max sendq>\"\n        219     RPL_ENDOFSTATS\n                        \"<stats letter> :End of /STATS report\"\n        241     RPL_STATSLLINE\n                        \"L <hostmask> * <servername> <maxdepth>\"\n        242     RPL_STATSUPTIME\n                        \":Server Up %d days %d:%02d:%02d\"\n        243     RPL_STATSOLINE\n                        \"O <hostmask> * <name>\"\n        244     RPL_STATSHLINE\n                        \"H <hostmask> * <servername>\"\n\n        221     RPL_UMODEIS\n                        \"<user mode string>\"\n\n\n\n--------------------------------------------------------------------------------\nPage 55\n\n                        - To answer a query about a client's own mode,\n                          RPL_UMODEIS is sent back.\n\n        251     RPL_LUSERCLIENT\n                        \":There are <integer> users and <integer> \\\n                         invisible on <integer> servers\"\n        252     RPL_LUSEROP\n                        \"<integer> :operator(s) online\"\n        253     RPL_LUSERUNKNOWN\n                        \"<integer> :unknown connection(s)\"\n        254     RPL_LUSERCHANNELS\n                        \"<integer> :channels formed\"\n        255     RPL_LUSERME\n                        \":I have <integer> clients and <integer> \\\n                          servers\"\n\n                        - In processing an LUSERS message, the server\n                          sends a set of replies from RPL_LUSERCLIENT,\n                          RPL_LUSEROP, RPL_USERUNKNOWN,\n                          RPL_LUSERCHANNELS and RPL_LUSERME.  When\n                          replying, a server must send back\n                          RPL_LUSERCLIENT and RPL_LUSERME.  The other\n                          replies are only sent back if a non-zero count\n                          is found for them.\n\n        256     RPL_ADMINME\n                        \"<server> :Administrative info\"\n        257     RPL_ADMINLOC1\n                        \":<admin info>\"\n        258     RPL_ADMINLOC2\n                        \":<admin info>\"\n        259     RPL_ADMINEMAIL\n                        \":<admin info>\"\n\n                        - When replying to an ADMIN message, a server\n                          is expected to use replies RLP_ADMINME\n                          through to RPL_ADMINEMAIL and provide a text\n                          message with each.  For RPL_ADMINLOC1 a\n                          description of what city, state and country\n                          the server is in is expected, followed by\n                          details of the university and department\n                          (RPL_ADMINLOC2) and finally the administrative\n                          contact for the server (an email address here\n                          is required) in RPL_ADMINEMAIL.\n\n\n\n--------------------------------------------------------------------------------\nPage 56\n\n6.3 Reserved numerics.\nThese numerics are not described above since they fall into one of the following categories:\n\n\n1 no longer in use;\n\n2 reserved for future planned use;\n\n3 in current use but are part of a non-generic 'feature' of\nthe current IRC server.\n\n        209     RPL_TRACECLASS          217     RPL_STATSQLINE\n        231     RPL_SERVICEINFO         232     RPL_ENDOFSERVICES\n        233     RPL_SERVICE             234     RPL_SERVLIST\n        235     RPL_SERVLISTEND\n        316     RPL_WHOISCHANOP         361     RPL_KILLDONE\n        362     RPL_CLOSING             363     RPL_CLOSEEND\n        373     RPL_INFOSTART           384     RPL_MYPORTIS\n        466     ERR_YOUWILLBEBANNED     476     ERR_BADCHANMASK\n        492     ERR_NOSERVICEHOST\n\n\n*/"
  },
  {
    "path": "plugins/jabber/jabbercl.q3asm",
    "content": "-o \"jabbercl\"\nplugin\njabberclient\nqvm_api\nmemory"
  },
  {
    "path": "plugins/jabber/jabberclient.c",
    "content": "//Released under the terms of the gpl as this file uses a bit of quake derived code. All sections of the like are marked as such\n\n/*\nNetwork limitations:\n\tgoogletalk:\n\t\tusername: same as gmail (foobar@gmail.com).\n\t\tFIXME: need to test foobar@googlemail.com\n\t\tauth mechanism: oauth2(tls+nontls) or plain(tls-only). no digests supported, so mitm can easily grab your password if they use certificate authority hackery, so DO NOT log in from work.\n\t\toauth2: I've registered a clientid for use with googletalk's network, but the whole web-browser-is-required crap makes it near unusable. We'll try it if they omit a password.\n\t\totherwise a complete implementation.\n\t\tother users appear unresponsive and permanently away. this is a wtf on google's part and not something I can trivially work around. these people are really offline but have previously used 'google hangouts', and google insist on the UI nightmare infecting other clients too.\n\t\tappears to hack avatar vcards into all presence messages, which is just an interesting thing to note as it seems to keep fucking up resulting in extra queries for avatar images.\n\n\tfacebook:\n\t\tusername: foobar@chat.facebook.com\n\t\tauth mechanism: digest-md5, x-facebook-platform.\n\t\tgateway implementation: no arbitary iq support (no invite/join/voice).\n\t\tno roster control\n\t\tcompletely untested. I've no interest in signing up to be tracked constantly (but somehow google is okay... go figure... I guess I'm just trying to avoid a double-whammy)\n\t\toauth2: no idea where to register a clientid, or what the correct addresses are. a google search implies they don't do refresh tokens properly. sticking with digest-md5 should work.\n\t\t*should* work for chat.\n\n\tmsn:\n\t\tusername: foobar@messenger.live.com (NOT foobar@live.com - this will timeout)\n\t\tauth mechansim: x-messenger-oath ONLY\n\t\tnon-standard unusable crap.\n\t\tuses incorrect certificates. any client that doesn't warn about that is buggy as fuck.\n\t\tprobably doesn't have iq support, no idea, can't log in to test that\n\t\trequires annoying see-other-host redirection.\n\t\tno roster control\n\t\tstun servers are listed in srv records for live.com and messanger.live.com but not messenger.live.com. retards.\n\t\toauth2: too lazy to register a clientid. stupid crap. I hate having to register everywhere.\n\n\tejabberd:\n\t\tauth mechanism: digest-md5, scram-sha1, plain.\n\t\tcomplete implementation. no issues.\n\t\tmay be lacking srv entries, depends on installation.\n\t\tmay have self-signed certificate issues, depends on installation.\n\nclient compat:\n\thangouts:\n\t\tUI nightmare infects the entire network and thus other clients also.\n\t\tvoip not supported. does not advertise any extensions and thus no voip.\n\t\tno file transfer support.\n\n\tgoogletalk:\n\t\timpossible to download from google any more. completely unsupported.\n\t\timplements old version of jingle. voice calls appear to not work.\n\t\tdoes not support SI file transfer.\n\t\tnot tested by me.\n\n\tpidgin:\n\t\t(linux) has issues with jingle+ice, and can easily be made to crash. voip uses speex. pidgin's ice seems vulnerable to dropped packets.\n\t\t(windows) doesn't support voice calls\n\t\tfile transfer works.\n\t\totherwise works.\n*/\n\n#include \"xmpp.h\"\n#include <time.h>\n\nstatic plugsubconsolefuncs_t *confuncs;\nstatic plug2dfuncs_t *drawfuncs;\t//needed for avatars.\nplugnetfuncs_t *netfuncs;\nplugfsfuncs_t *filefuncs;\nstatic plugclientfuncs_t *clientfuncs;\t//for more accurate presence info.\nstatic struct\n{\n\tint width;\n\tint height;\n} pvid;\n\n//#define USE_GOOGLE_MAIL_NOTIFY\n\n#ifdef DEFAULTDOMAIN\n\t#define EXAMPLEDOMAIN DEFAULTDOMAIN\t//used in examples / default text field (but not otherwise assumed when omitted)\n#else\n\t#define EXAMPLEDOMAIN \"example.com\"\t//used in examples\n#endif\n\n\n#ifdef JINGLE\nicefuncs_t *piceapi;\n#endif\nstatic qboolean jclient_needreadconfig;\nstatic qboolean jclient_configdirty;\nstatic qboolean jclient_updatebuddylist;\nstatic jclient_t *jclient_action_cl;\nstatic buddy_t *jclient_action_buddy;\nstatic enum\n{\n\tACT_NONE,\n\tACT_OAUTH,\n\tACT_NEWACCOUNT,\n\tACT_SETAUSERNAME,\n\tACT_SETADOMAIN,\n\tACT_SETASERVER,\n\tACT_SETARESOURCE,\n\tACT_SETAPASSWORD,\n\tACT_ADDFRIEND,\n\tACT_SETBALIAS,\n} jclient_action;\n\n#define BUDDYLISTTITLE \"Buddy List\"\n\n#define COL_NAME_THEM\t\"^1\" //red\n#define COL_NAME_US\t\t\"^5\" //cyan\n#define COL_TEXT_THEM\t\"^7\" //white\n#define COL_TEXT_US\t\t\"^3\" //yellow\n#define IMG_FB_THEM \"gfx/menudot1.lmp\"\n#define IMG_FB_US \"gfx/menuplyr.lmp\"\n\n#define Q_strncpyz(o, i, l) do {strncpy(o, i, l-1);o[l-1]='\\0';}while(0)\n\nstatic qboolean (*Con_TrySubPrint)(const char *conname, const char *message);\nqboolean Fallback_ConPrint(const char *conname, const char *message)\n{\n\tplugfuncs->Print(message);\n\treturn true;\n}\n\nvoid Con_SubPrintf(const char *subname, const char *format, ...)\n{\n\tva_list\t\targptr;\n\tstatic char\t\tstring[1024];\n\n\tva_start (argptr, format);\n\tQ_vsnprintf (string, sizeof(string), format,argptr);\n\tva_end (argptr);\n\n\tCon_TrySubPrint(subname, string);\n}\n\n\n//porting zone:\n\t#define COLOURWHITE \"^7\" // word\n\t#define COMMANDPREFIX \"xmpp\"\n\t#define COMMANDPREFIX2 \"jab\"\n\t#define COMMANDPREFIX3 \"jabbercl\"\n\n\tstatic char *JCL_ParseOut (char *data, char *buf, int bufsize)\t//GPL: this is taken out of quake\n\t{\n\t\tint\t\tc;\n\t\tint\t\tlen;\n\n\t\tlen = 0;\n\t\tbuf[0] = 0;\n\n\t\tif (!data)\n\t\t\treturn NULL;\n\n\t// skip whitespace\n\t\twhile ( (c = *data) <= ' ')\n\t\t{\n\t\t\tif (c == 0)\n\t\t\t\treturn NULL;\t\t\t// end of file;\n\t\t\tdata++;\n\t\t}\n\n\t// handle quoted strings specially\n\t\tif (c == '\\\"')\n\t\t{\n\t\t\tdata++;\n\t\t\twhile (1)\n\t\t\t{\n\t\t\t\tif (len >= bufsize-1)\n\t\t\t\t\treturn data;\n\n\t\t\t\tc = *data++;\n\t\t\t\tif (c=='\\\"' || !c)\n\t\t\t\t{\n\t\t\t\t\tbuf[len] = 0;\n\t\t\t\t\treturn data;\n\t\t\t\t}\n\t\t\t\tbuf[len] = c;\n\t\t\t\tlen++;\n\t\t\t}\n\t\t}\n\n\t// parse a regular word\n\t\tdo\n\t\t{\n\t\t\tif (len >= bufsize-1)\n\t\t\t\treturn data;\n\n\t\t\tbuf[len] = c;\n\t\t\tdata++;\n\t\t\tlen++;\n\t\t\tc = *data;\n\t\t} while (c>32);\n\n\t\tbuf[len] = 0;\n\t\treturn data;\n\t}\n\n\nchar *JCL_Info_ValueForKey (char *s, const char *key, char *valuebuf, int valuelen)\t//GPL: ripped from quake\n{\n\tchar\tpkey[1024];\n\tchar\t*o;\n\n\tif (*s == '\\\\')\n\t\ts++;\n\twhile (1)\n\t{\n\t\to = pkey;\n\t\twhile (*s != '\\\\')\n\t\t{\n\t\t\tif (!*s)\n\t\t\t{\n\t\t\t\t*valuebuf='\\0';\n\t\t\t\treturn valuebuf;\n\t\t\t}\n\t\t\t*o++ = *s++;\n\t\t\tif (o+2 >= pkey+sizeof(pkey))\t//hrm. hackers at work..\n\t\t\t{\n\t\t\t\t*valuebuf='\\0';\n\t\t\t\treturn valuebuf;\n\t\t\t}\n\t\t}\n\t\t*o = 0;\n\t\ts++;\n\n\t\to = valuebuf;\n\n\t\twhile (*s != '\\\\' && *s)\n\t\t{\n\t\t\tif (!*s)\n\t\t\t{\n\t\t\t\t*valuebuf='\\0';\n\t\t\t\treturn valuebuf;\n\t\t\t}\n\t\t\t*o++ = *s++;\n\n\t\t\tif (o+2 >= valuebuf+valuelen)\t//hrm. hackers at work..\n\t\t\t{\n\t\t\t\t*valuebuf='\\0';\n\t\t\t\treturn valuebuf;\n\t\t\t}\n\t\t}\n\t\t*o = 0;\n\n\t\tif (!strcmp (key, pkey) )\n\t\t\treturn valuebuf;\n\n\t\tif (!*s)\n\t\t{\n\t\t\t*valuebuf='\\0';\n\t\t\treturn valuebuf;\n\t\t}\n\t\ts++;\n\t}\n}\n\n\n\n\n/*\nthis is a fairly basic implementation.\ndon't expect it to do much.\nYou can probably get a better version from somewhere.\nThis has been tweaked to do xml markup in %s. Use %+s if you want to include xml-formatted text as-is.\n*/\nint Q_vsnprintxf(char *buffer, size_t maxlen, const char *format, va_list vargs)\n{\n\tint tokens=0;\n\tchar *string;\n\tchar tempbuffer[64];\n\tchar sign;\n\tunsigned int _uint;\n\tint _int;\n\tfloat _float;\n\tint i;\n\tint use0s;\n\tint width, useprepad, plus;\n\tint precision;\n\n\tif (!maxlen)\n\t\treturn 0;\nmaxlen--;\n\n\twhile(*format)\n\t{\n\t\tswitch(*format)\n\t\t{\n\t\tcase '%':\n\t\t\tplus = 0;\n\t\t\twidth= 0;\n\t\t\tprecision=-1;\n\t\t\tuseprepad=0;\n\t\t\tuse0s= 0;\nretry:\n\t\t\tswitch(*(++format))\n\t\t\t{\n\t\t\tcase '-':\n\t\t\t\tuseprepad=true;\n\t\t\t\tgoto retry;\n\t\t\tcase '+':\n\t\t\t\tplus = true;\n\t\t\t\tgoto retry;\n\t\t\tcase '.':\n\t\t\t\tprecision = 0;\n\t\t\t\twhile (format[1] >= '0' && format[1] <= '9')\n\t\t\t\t\tprecision = precision*10+*++format-'0';\n\t\t\t\tgoto retry;\n\t\t\tcase '0':\n\t\t\t\tif (!width)\n\t\t\t\t{\n\t\t\t\t\tuse0s=true;\n\t\t\t\t\tgoto retry;\n\t\t\t\t}\n\t\t\tcase '1':\n\t\t\tcase '2':\n\t\t\tcase '3':\n\t\t\tcase '4':\n\t\t\tcase '5':\n\t\t\tcase '6':\n\t\t\tcase '7':\n\t\t\tcase '8':\n\t\t\tcase '9':\n\t\t\t\twidth=width*10+*format-'0';\n\t\t\t\tgoto retry;\n\t\t\tcase '%':\t/*emit a %*/\n\t\t\t\tif (maxlen-- == 0) \n\t\t\t\t\t{*buffer++='\\0';return tokens;}\n\t\t\t\t*buffer++ = *format;\n\t\t\t\tbreak;\n\t\t\tcase 's':\n\t\t\t\tstring = va_arg(vargs, char *);\n\t\t\t\tif (!string)\n\t\t\t\t\tstring = \"(null)\";\n\t\t\t\tif (!plus)\n\t\t\t\t{\n\t\t\t\t\twhile (*string)\n\t\t\t\t\t{\n\t\t\t\t\t\tchar *rep;\n\t\t\t\t\t\tsize_t replen;\n\t\t\t\t\t\tchar c = *string++;\n\t\t\t\t\t\tswitch(c)\n\t\t\t\t\t\t{\n\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\tif (maxlen-- == 0) \n\t\t\t\t\t\t\t\t{*buffer++='\\0';return tokens;}\n\t\t\t\t\t\t\t*buffer++ = c;\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\tcase '<': rep = \"&lt;\"; break;\n\t\t\t\t\t\tcase '>': rep = \"&gt;\"; break;\n\t\t\t\t\t\tcase '&': rep = \"&amp;\"; break;\n\t\t\t\t\t\tcase '\\'': rep = \"&apos;\"; break;\n\t\t\t\t\t\tcase '\\\"': rep = \"&quot;\"; break;\n\t\t\t\t\t\t}\n\t\t\t\t\t\treplen = strlen(rep);\n\t\t\t\t\t\tif (maxlen < replen)\n\t\t\t\t\t\t\t{*buffer++='\\0';return tokens;}\n\t\t\t\t\t\tmaxlen -= replen;\n\t\t\t\t\t\tmemcpy(buffer, rep, replen);\n\t\t\t\t\t\tbuffer += replen;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse if (width)\n\t\t\t\t{\n\t\t\t\t\twhile (*string && width--)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (maxlen-- == 0) \n\t\t\t\t\t\t\t{*buffer++='\\0';return tokens;}\n\t\t\t\t\t\t*buffer++ = *string++;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\twhile (*string)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (maxlen-- == 0) \n\t\t\t\t\t\t\t{*buffer++='\\0';return tokens;}\n\t\t\t\t\t\t*buffer++ = *string++;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\ttokens++;\n\t\t\t\tbreak;\n\t\t\t/*case 'c':\n\t\t\t\t_int = va_arg(vargs, int);\n\t\t\t\tif (maxlen-- == 0) \n\t\t\t\t\t{*buffer++='\\0';return tokens;}\n\t\t\t\t*buffer++ = _int;\n\t\t\t\ttokens++;\n\t\t\t\tbreak;*/\n\t\t\tcase 'p':\n\t\t\t\tif (1)\n\t\t\t\t_uint = (size_t)va_arg(vargs, void*);\n\t\t\t\telse\n\t\t\tcase 'x':\n\t\t\t\t_uint = va_arg(vargs, unsigned int);\n\t\t\t\ti = sizeof(tempbuffer)-2;\n\t\t\t\ttempbuffer[i+1] = '\\0';\n\t\t\t\twhile(_uint)\n\t\t\t\t{\n\t\t\t\t\ttempbuffer[i] = (_uint&0xf) + '0';\n\t\t\t\t\tif (tempbuffer[i] > '9')\n\t\t\t\t\t\ttempbuffer[i] = tempbuffer[i] - ':' + 'a';\n\t\t\t\t\t_uint/=16;\n\t\t\t\t\ti--;\n\t\t\t\t}\n\t\t\t\tstring = tempbuffer+i+1;\n\n\t\t\t\tif (!*string)\n\t\t\t\t{\n\t\t\t\t\ti=61;\n\t\t\t\t\tstring = tempbuffer+i+1;\n\t\t\t\t\tstring[0] = '0';\n\t\t\t\t\tstring[1] = '\\0';\n\t\t\t\t}\n\n\t\t\t\twidth -= 62-i;\n\t\t\t\twhile (width>0)\n\t\t\t\t{\n\t\t\t\t\tstring--;\n\t\t\t\t\tif (use0s)\n\t\t\t\t\t\t*string = '0';\n\t\t\t\t\telse\n\t\t\t\t\t\t*string = ' ';\n\t\t\t\t\twidth--;\n\t\t\t\t}\n\n\t\t\t\twhile (*string)\n\t\t\t\t{\n\t\t\t\t\tif (maxlen-- == 0) \n\t\t\t\t\t\t{*buffer++='\\0';return tokens;}\n\t\t\t\t\t*buffer++ = *string++;\n\t\t\t\t}\n\t\t\t\ttokens++;\n\t\t\t\tbreak;\n\t\t\tcase 'd':\n\t\t\tcase 'u':\n\t\t\tcase 'i':\n\t\t\t\t_int = va_arg(vargs, int);\n\t\t\t\tif (useprepad)\n\t\t\t\t{\n\n\t\t\t\t}\n\t\t\t\tif (_int < 0)\n\t\t\t\t{\n\t\t\t\t\tsign = '-';\n\t\t\t\t\t_int *= -1;\n\t\t\t\t}\n\t\t\t\telse if (plus)\n\t\t\t\t\tsign = '+';\n\t\t\t\telse\n\t\t\t\t\tsign = 0;\n\t\t\t\ti = sizeof(tempbuffer)-2;\n\t\t\t\ttempbuffer[sizeof(tempbuffer)-1] = '\\0';\n\t\t\t\twhile(_int)\n\t\t\t\t{\n\t\t\t\t\ttempbuffer[i--] = _int%10 + '0';\n\t\t\t\t\t_int/=10;\n\t\t\t\t}\n\t\t\t\tif (sign)\n\t\t\t\t\ttempbuffer[i--] = sign;\n\t\t\t\tstring = tempbuffer+i+1;\n\n\t\t\t\tif (!*string)\n\t\t\t\t{\n\t\t\t\t\ti=61;\n\t\t\t\t\tstring = tempbuffer+i+1;\n\t\t\t\t\tstring[0] = '0';\n\t\t\t\t\tstring[1] = '\\0';\n\t\t\t\t}\n\n\t\t\t\twidth -= 62-i;\n/*\t\t\t\twhile (width>0)\n\t\t\t\t{\n\t\t\t\t\tstring--;\n\t\t\t\t\t*string = ' ';\n\t\t\t\t\twidth--;\n\t\t\t\t}\n*/\n\t\t\t\twhile(width>0)\n\t\t\t\t{\n\t\t\t\t\tif (maxlen-- == 0) \n\t\t\t\t\t\t{*buffer++='\\0';return tokens;}\n\t\t\t\t\tif (use0s)\n\t\t\t\t\t\t*buffer++ = '0';\n\t\t\t\t\telse\n\t\t\t\t\t\t*buffer++ = ' ';\n\t\t\t\t\twidth--;\n\t\t\t\t}\n\n\t\t\t\twhile (*string)\n\t\t\t\t{\n\t\t\t\t\tif (maxlen-- == 0) \n\t\t\t\t\t\t{*buffer++='\\0';return tokens;}\n\t\t\t\t\t*buffer++ = *string++;\n\t\t\t\t}\n\t\t\t\ttokens++;\n\t\t\t\tbreak;\n\t\t\tcase 'f':\n\t\t\t\t_float = (float)va_arg(vargs, double);\n\n//integer part.\n\t\t\t\t_int = (int)_float;\n\t\t\t\tif (_int < 0)\n\t\t\t\t{\n\t\t\t\t\tif (maxlen-- == 0) \n\t\t\t\t\t\t{*buffer++='\\0';return tokens;}\n\t\t\t\t\t*buffer++ = '-';\n\t\t\t\t\t_int *= -1;\n\t\t\t\t}\n\t\t\t\ti = sizeof(tempbuffer)-2;\n\t\t\t\ttempbuffer[sizeof(tempbuffer)-1] = '\\0';\n\t\t\t\tif (!_int)\n\t\t\t\t{\n\t\t\t\t\ttempbuffer[i--] = '0';\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\twhile(_int)\n\t\t\t\t\t{\n\t\t\t\t\t\ttempbuffer[i--] = _int%10 + '0';\n\t\t\t\t\t\t_int/=10;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tstring = tempbuffer+i+1;\n\t\t\t\twhile (*string)\n\t\t\t\t{\n\t\t\t\t\tif (maxlen-- == 0) \n\t\t\t\t\t\t{*buffer++='\\0';return tokens;}\n\t\t\t\t\t*buffer++ = *string++;\n\t\t\t\t}\n\n\t\t\t\t_int = sizeof(tempbuffer)-2-i;\n\n//floating point part.\n\t\t\t\t_float -= (int)_float;\n\t\t\t\ti = 0;\n\t\t\t\ttempbuffer[i++] = '.';\n\t\t\t\tif (precision < 0)\n\t\t\t\t\tprecision = 7;\n\t\t\t\twhile(_float - (int)_float)\n\t\t\t\t{\n\t\t\t\t\tif (i > precision)\t//remove the excess presision.\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t_float*=10;\n\t\t\t\t\ttempbuffer[i++] = (int)_float%10 + '0';\n\t\t\t\t}\n\t\t\t\tif (i == 1)\t//no actual fractional part\n\t\t\t\t{\n\t\t\t\t\ttokens++;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\t//concatinate to our string\n\t\t\t\ttempbuffer[i] = '\\0';\n\t\t\t\tstring = tempbuffer;\n\t\t\t\twhile (*string)\n\t\t\t\t{\n\t\t\t\t\tif (maxlen-- == 0) \n\t\t\t\t\t\t{*buffer++='\\0';return tokens;}\n\t\t\t\t\t*buffer++ = *string++;\n\t\t\t\t}\n\n\t\t\t\ttokens++;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tstring = \"ERROR IN FORMAT\";\n\t\t\t\twhile (*string)\n\t\t\t\t{\n\t\t\t\t\tif (maxlen-- == 0) \n\t\t\t\t\t\t{*buffer++='\\0';return tokens;}\n\t\t\t\t\t*buffer++ = *string++;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tif (maxlen-- == 0) \n\t\t\t\t{*buffer++='\\0';return tokens;}\n\t\t\t*buffer++ = *format;\n\t\t\tbreak;\n\t\t}\n\t\tformat++;\n\t}\n\t{*buffer++='\\0';return tokens;}\n}\n\n\n\n\n#if defined(_WIN32) && defined(HAVE_PACKET)\n#include <windns.h>\nstatic DNS_STATUS (WINAPI *pDnsQuery_UTF8) (PCSTR pszName, WORD wType, DWORD Options, PIP4_ARRAY aipServers, PDNS_RECORD *ppQueryResults, PVOID *pReserved);\nstatic VOID (WINAPI *pDnsRecordListFree)(PDNS_RECORD pRecordList, DNS_FREE_TYPE FreeType);\nstatic HMODULE dnsapi_lib;\nqboolean NET_DNSLookup_SRV(const char *host, char *out, int outlen)\n{\n\tDNS_RECORD *result = NULL;\n\tif (!dnsapi_lib)\n\t{\n\t\tdnsapi_lib = LoadLibrary(\"dnsapi.dll\");\n\t\tpDnsQuery_UTF8 = (void*)GetProcAddress(dnsapi_lib, \"DnsQuery_UTF8\");\n\t\tpDnsRecordListFree = (void*)GetProcAddress(dnsapi_lib, \"DnsRecordListFree\");\n\t}\n\t//win98?\n\tif (!pDnsQuery_UTF8 || !pDnsRecordListFree)\n\t\treturn false;\n\t//do lookup\n\tpDnsQuery_UTF8(host, DNS_TYPE_SRV, DNS_QUERY_STANDARD, NULL, &result, NULL);\n\tif (result)\n\t{\n\t\tQ_snprintf(out, outlen, \"[%s]:%i\", result->Data.SRV.pNameTarget, result->Data.SRV.wPort);\n\t\tpDnsRecordListFree(result, DnsFreeRecordList);\n\t\treturn true;\n\t}\n\treturn false;\n}\n#elif (defined(__unix__) || defined(__MACH__) || defined(__linux__)) && !defined(ANDROID)\n#include <resolv.h>\n#include <arpa/nameser.h>\nqboolean NET_DNSLookup_SRV(const char *host, char *out, int outlen)\n{\n\tint questions;\n\tint answers;\n\tqbyte answer[512];\n\tqbyte dname[512];\n\tint len, i;\n\tqbyte *msg, *eom;\n\n\tlen = res_query(host, C_IN, T_SRV, answer, sizeof(answer));\n\tif (len < 12)\n\t{\n\t\tCon_Printf(\"srv lookup failed for %s\\n\", host);\n\t\treturn false;\n\t}\n\n\teom = answer+len;\n\n\tquestions = (answer[4]<<8) | answer[5];\n\tanswers = (answer[6]<<8) | answer[7];\n//\tid @ 0\n//\tbits @ 2\n//\tquestioncount@4\n///\tanswer count@6\n//\tnameserver record count @8\n//\tadditional record count @10\n\n//\tquestions@12\n//\tanswers@12+sizeof(questions)\n\n\tif (answers < 1)\n\t\treturn false;\n\n\tmsg = answer+12;\n\n\twhile(questions --> 0)\n\t{\n\t\tdn_expand(answer, eom, msg, dname, sizeof(dname));\n//\t\tCon_Printf(\"Skip question %s\\n\", dname);\n\t\ti = dn_skipname(msg, eom);\n\t\tif (i <= 0)\n\t\t\treturn false;\n\t\tmsg += i;\n\t\tmsg += 2;//query type\n\t\tmsg += 2;//query class\n\t}\n\n\twhile(answers --> 0)\n\t{\n\t\ti = dn_expand(answer, eom, msg, dname, sizeof(dname));\n//\t\ti = dn_skipname(msg, eom);\n\t\tmsg += i;\n\t\tmsg += 2;//query type\n\t\tmsg += 2;//query class\n\t\tmsg += 4;//ttl\n\t\ti = (msg[0]<<8) | msg[1];\n\t\tmsg+=2;\n\t\t//noone tried to send the wrong type then, woo.\n\t\tif (!strcmp(dname, host))\n\t\t{\n\t\t\tint port;\n\t\t\t//we're not serving to other dns servers, and it seems they're already getting randomized, so just grab the first without rerandomizing.\n\t\t\tmsg += 2;//priority\n\t\t\tmsg += 2;//weight\n\t\t\tport = (msg[0]<<8) | msg[1];\n\t\t\tmsg += 2;//port\n\t\t\tdn_expand(answer, eom, msg, dname, sizeof(dname));\n\t\t\tQ_snprintf(out, outlen, \"[%s]:%i\", dname, port);\n//\t\t\tCon_Printf(\"Resolved to %s\\n\", out);\n\t\t\treturn true;\n\t\t}\n\t\tdn_expand(answer, eom, msg, out, outlen);\n//\t\tCon_Printf(\"Ignoring resolution to %s\\n\", out);\n\t\tmsg += i;\n\t}\n\n//type (2 octets)\n//class (2 octets)\n//TTL (4 octets)\n//resource data length (2 octets)\n//resource data (variable length)\n\n\tif (i < 0)\n\t\treturn false;\n\treturn true;\n}\n#else\nqboolean NET_DNSLookup_SRV(const char *host, char *out, int outlen)\n{\n\treturn false;\n}\n#endif\n\n\nstatic char base64[((4096+3)*4/3)+1];\nstatic unsigned int base64_len;\t//current output length\nstatic unsigned int base64_cur;\t//current pending value\nstatic unsigned int base64_bits;//current pending bits\nchar Base64_From64(int byt)\n{\n\tif (byt >= 0 && byt < 26)\n\t\treturn 'A' + byt - 0;\n\tif (byt >= 26 && byt < 52)\n\t\treturn 'a' + byt - 26;\n\tif (byt >= 52 && byt < 62)\n\t\treturn '0' + byt - 52;\n\tif (byt == 62)\n\t\treturn '+';\n\tif (byt == 63)\n\t\treturn '/';\n\treturn '!';\n}\nvoid Base64_Byte(unsigned int byt)\n{\n\tif (base64_len+4>=sizeof(base64)-1)\n\t\treturn;\n\tbase64_cur |= byt<<(16-\tbase64_bits);//first byte fills highest bits\n\tbase64_bits += 8;\n\tif (base64_bits == 24)\n\t{\n\t\tbase64[base64_len++] = Base64_From64((base64_cur>>18)&63);\n\t\tbase64[base64_len++] = Base64_From64((base64_cur>>12)&63);\n\t\tbase64[base64_len++] = Base64_From64((base64_cur>>6)&63);\n\t\tbase64[base64_len++] = Base64_From64((base64_cur>>0)&63);\n\t\tbase64[base64_len] = '\\0';\n//\t\tCon_Printf(\"base64: %s\\n\", base64+base64_len-4);\n\t\tbase64_bits = 0;\n\t\tbase64_cur = 0;\n\t}\n}\n\nvoid Base64_Add(const char *s, int len)\n{\n\tconst unsigned char *us = (const unsigned char *)s;\n\twhile(len-->0)\n\t\tBase64_Byte(*us++);\n}\n\nchar *Base64_Finish(void)\n{\n\t//output is always a multiple of four\n\n\t//0(0)->0(0)\n\t//1(8)->2(12)\n\t//2(16)->3(18)\n\t//3(24)->4(24)\n\n\tif (base64_bits != 0)\n\t{\n\t\tbase64[base64_len++]=Base64_From64((base64_cur>>18)&63);\n\t\tbase64[base64_len++]=Base64_From64((base64_cur>>12)&63);\n\t\tif (base64_bits == 8)\n\t\t{\n\t\t\tbase64[base64_len++]= '=';\n\t\t\tbase64[base64_len++]= '=';\n\t\t}\n\t\telse\n\t\t{\n\t\t\tbase64[base64_len++]=Base64_From64((base64_cur>>6)&63);\n\t\t\tif (base64_bits == 16)\n\t\t\t\tbase64[base64_len++]= '=';\n\t\t\telse\n\t\t\t\tbase64[base64_len++]=Base64_From64((base64_cur>>0)&63);\n\t\t}\n\t}\n\tbase64[base64_len++] = '\\0';\n\n\tbase64_len = 0; //for next time (use strlen)\n\tbase64_bits = 0;\n\tbase64_cur = 0;\n\n\treturn base64;\n}\n\n//decode a base64 byte to a 0-63 value. Cannot cope with =.\nstatic int Base64_DecodeByte(char byt)\n{\n    if (byt >= 'A' && byt <= 'Z')\n        return (byt-'A') + 0;\n    if (byt >= 'a' && byt <= 'z')\n        return (byt-'a') + 26;\n    if (byt >= '0' && byt <= '9')\n        return (byt-'0') + 52;\n    if (byt == '+')\n        return 62;\n    if (byt == '/')\n        return 63;\n    return -1;\n}\n//FIXME: we should be able to skip whitespace.\nint Base64_Decode(char *out, int outlen, const char *src, int srclen)\n{\n\tint len = 0;\n\tint result;\n\n\t//4 input chars give 3 output chars\n\twhile(srclen >= 4)\n\t{\n\t\tif (len+3 > outlen)\n\t\t\tbreak;\n\t\tresult = Base64_DecodeByte(src[0])<<18;\n\t\tresult |= Base64_DecodeByte(src[1])<<12;\n\t\tout[len++] = (result>>16)&0xff;\n\t\tif (src[2] != '=')\n\t\t{\n\t\t\tresult |= Base64_DecodeByte(src[2])<<6;\n\t\t\tout[len++] = (result>>8)&0xff;\n\t\t\tif (src[3] != '=')\n\t\t\t{\n\t\t\t\tresult |= Base64_DecodeByte(src[3])<<0;\n\t\t\t\tout[len++] = (result>>0)&0xff;\n\t\t\t}\n\t\t}\n\t\tif (result & 0xff000000)\n\t\t\treturn 0;\t//some kind of invalid char\n\n\t\tsrc += 4;\n\t\tsrclen -= 4;\n\t}\n\n\t//some kind of error\n\tif (srclen)\n\t\treturn 0;\n\t\n\treturn len;\n}\n\n\n\nvoid XMPP_Menu_Password(jclient_t *acc);\nvoid RenameConsole(char *totrim);\nvoid JCL_Command(int accid, char *consolename);\nvoid JCL_LoadConfig(void);\nvoid JCL_WriteConfig(void);\n\nstatic struct {\n\tchar *names;\n\tunsigned int cap;\n} capnames[] =\n{\n\t{\"avatars\", CAP_AVATARS},\n\t{\"jingle_voice\", CAP_VOICE},\n\t{\"jingle_video\", CAP_VIDEO},\n\t{\"google_voice\", CAP_GOOGLE_VOICE},\n\t{\"quake_invite\", CAP_GAMEINVITE},\n\t{\"poke\", CAP_POKE},\n#ifdef FILETRANSFERS\n\t{\"si_filetransfer\", CAP_SIFT},\n#endif\n\t{NULL}\n};\n\nstatic void JCL_ExecuteCommand_f(void)\n{\n\tqboolean isinsecure = cmdfuncs->IsInsecure();\n\tchar cmd[256];\n\tcmdfuncs->Argv(0, cmd, sizeof(cmd));\n\tif (!strcmp(cmd, COMMANDPREFIX) || !strcmp(cmd, COMMANDPREFIX2) || !strcmp(cmd, COMMANDPREFIX3))\n\t{\n\t\tif (!isinsecure || cmdfuncs->Argc() == 1)\n\t\t\tJCL_Command(0, \"\");\n\t}\n\telse if (!strncmp(cmd, COMMANDPREFIX, strlen(COMMANDPREFIX)))\n\t{\n\t\tif (!isinsecure || cmdfuncs->Argc() == 1)\n\t\t\tJCL_Command(atoi(cmd+strlen(COMMANDPREFIX)), \"\");\n\t}\n}\n\nqboolean JCL_ConsoleLink(void);\nqboolean JCL_ConsoleLinkMouseOver(float x, float y);\nint JCL_ConExecuteCommand(qboolean isinsecure);\n\nvoid JCL_Frame(double realtime, double gametime);\nvoid JCL_Shutdown(void);\n\nstatic void QDECL JCL_UpdateVideo(int width, int height, qboolean restarted)\n{\n\tpvid.width = width;\n\tpvid.height = height;\n\n\t//FIXME: clear/reload images.\n}\n\nqboolean Plug_Init(void)\n{\n\tconst char *cmddesc = \"XMPP client - ^[/\"COMMANDPREFIX\" /help^] for help.\";\n\tjclient_needreadconfig = true;\n\n\tconfuncs = (plugsubconsolefuncs_t*)plugfuncs->GetEngineInterface(plugsubconsolefuncs_name, sizeof(*confuncs));\n\tdrawfuncs = (plug2dfuncs_t*)plugfuncs->GetEngineInterface(plug2dfuncs_name, sizeof(*drawfuncs));\n\tnetfuncs = (plugnetfuncs_t*)plugfuncs->GetEngineInterface(plugnetfuncs_name, sizeof(*netfuncs));\n\tfilefuncs = (plugfsfuncs_t*)plugfuncs->GetEngineInterface(plugfsfuncs_name, sizeof(*filefuncs));\n\tclientfuncs = (plugclientfuncs_t*)plugfuncs->GetEngineInterface(plugclientfuncs_name, sizeof(*clientfuncs));\n\n\tif (netfuncs && filefuncs &&\n\t\tplugfuncs->ExportFunction(\"Tick\", JCL_Frame) &&\n\t\tplugfuncs->ExportFunction(\"Shutdown\", JCL_Shutdown))\n\t{\n\t\tCon_Printf(\"XMPP Plugin Loaded. For help, use: ^[/\"COMMANDPREFIX\" /help^]\\n\");\n\n\t\tplugfuncs->ExportFunction(\"UpdateVideo\", JCL_UpdateVideo);\n\t\tplugfuncs->ExportFunction(\"ConsoleLink\", JCL_ConsoleLink);\n\t\tif (drawfuncs)\n\t\t\tplugfuncs->ExportFunction(\"ConsoleLinkMouseOver\", JCL_ConsoleLinkMouseOver);\n\n\t\tif (!confuncs || !plugfuncs->ExportFunction(\"ConExecuteCommand\", JCL_ConExecuteCommand))\n\t\t{\n\t\t\tCon_Printf(\"XMPP plugin in single-console mode\\n\");\n\t\t\tCon_TrySubPrint = Fallback_ConPrint;\n\t\t}\n\t\telse\n\t\t\tCon_TrySubPrint = confuncs->SubPrint;\n\n\t\tcmdfuncs->AddCommand(COMMANDPREFIX, JCL_ExecuteCommand_f, cmddesc);\n\t\tcmdfuncs->AddCommand(COMMANDPREFIX2, JCL_ExecuteCommand_f, cmddesc);\n\t\tcmdfuncs->AddCommand(COMMANDPREFIX3, JCL_ExecuteCommand_f, cmddesc);\n\n\t\tcmdfuncs->AddCommand(COMMANDPREFIX\"0\", JCL_ExecuteCommand_f, cmddesc);\n\t\tcmdfuncs->AddCommand(COMMANDPREFIX\"1\", JCL_ExecuteCommand_f, cmddesc);\n\t\tcmdfuncs->AddCommand(COMMANDPREFIX\"2\", JCL_ExecuteCommand_f, cmddesc);\n\t\tcmdfuncs->AddCommand(COMMANDPREFIX\"3\", JCL_ExecuteCommand_f, cmddesc);\n\t\tcmdfuncs->AddCommand(COMMANDPREFIX\"4\", JCL_ExecuteCommand_f, cmddesc);\n\t\tcmdfuncs->AddCommand(COMMANDPREFIX\"5\", JCL_ExecuteCommand_f, cmddesc);\n\t\tcmdfuncs->AddCommand(COMMANDPREFIX\"6\", JCL_ExecuteCommand_f, cmddesc);\n\t\tcmdfuncs->AddCommand(COMMANDPREFIX\"7\", JCL_ExecuteCommand_f, cmddesc);\n\n\t\t//flags&1 == archive\n\t\tcvarfuncs->GetNVFDG(\"xmpp_nostatus\",\t\t\t\"0\", 0, NULL, \"xmpp\");\n\t\tcvarfuncs->GetNVFDG(\"xmpp_showstatusupdates\",\t\"0\", 0, NULL, \"xmpp\");\n\t\tcvarfuncs->GetNVFDG(\"xmpp_autoacceptjoins\",\t\t\"0\", 0, NULL, \"xmpp\");\n\t\tcvarfuncs->GetNVFDG(\"xmpp_autoacceptinvites\",\t\"0\", 0, NULL, \"xmpp\");\n\t\tcvarfuncs->GetNVFDG(\"xmpp_autoacceptvoice\",\t\t\"0\", 0, NULL, \"xmpp\");\n\t\tcvarfuncs->GetNVFDG(\"xmpp_debug\",\t\t\t\t\"0\", 0, NULL, \"xmpp\");\n\n#ifdef JINGLE\n\t\tpiceapi = plugfuncs->GetEngineInterface(ICE_API_CURRENT, sizeof(*piceapi));\n#endif\n\n\t\treturn 1;\n\t}\n\telse\n\t\tCon_Printf(\"JCL Client Plugin failed\\n\");\n\treturn 0;\n}\n\n\n\n\n\n\n\n\n\n\n//\\r\\n is used to end a line.\n//meaning \\0s are valid.\n//but never used cos it breaks strings\n\nstatic jclient_t *jclients[8];\nstatic int jclient_curtime;\nstatic int jclient_poketime;\n\ntypedef struct saslmethod_s\n{\n\tchar *method;\n\tint (*sasl_initial)(struct sasl_ctx_s *ctx, char *buf, int bufsize);\n\tint (*sasl_challenge)(struct sasl_ctx_s *ctx, char *inbuf, int insize, char *outbuf, int outsize);\n\tint (*sasl_success)(struct sasl_ctx_s *ctx, char *inbuf, int insize);\n} saslmethod_t;\n\n//#define OAUTH_CLIENT_ID_MSN \"0\"\n#ifdef OAUTH_CLIENT_ID_MSN\nstatic int sasl_plain_initial(struct sasl_ctx_s *ctx, char *buf, int bufsize)\n{\n\t//\"https://oauth.live.com/authorize?client_id=\" OAUTH_CLIENT_ID_MSN \"&scope=wl.messenger,wl.basic,wl.offline_access,wl.contacts_create,wl.share&response_type=token&redirect_uri=http://localhost/\";\n}\n#endif\n\n//\\0username\\0password\nstatic int sasl_plain_initial(struct sasl_ctx_s *ctx, char *buf, int bufsize)\n{\n\tint len = 0;\n\n\tif (ctx->issecure?ctx->allowauth_plaintls:ctx->allowauth_plainnontls)\n\t{\n\t\tif (!*ctx->password_plain)\n\t\t\treturn -2;\n\n\t\t//realm isn't specified\n\t\tbuf[len++] = 0;\n\t\tmemcpy(buf+len, ctx->username, strlen(ctx->username));\n\t\tlen += strlen(ctx->username);\n\t\tbuf[len++] = 0;\n\t\tmemcpy(buf+len, ctx->password_plain, strlen(ctx->password_plain));\n\t\tlen += strlen(ctx->password_plain);\n\n\t\treturn len;\n\t}\n\treturn -1;\n}\n\n\nstatic int saslattr(char *out, int outlen, char *srcbuf, int srclen, char *arg)\n{\n\tchar *vn;\n\tchar *s = srcbuf;\n\tchar *e = s + srclen;\n\tchar *vs;\n\twhile(s < e)\n\t{\n\t\twhile (s < e && *s == ',')\n\t\t\ts++;\n\t\tvn = s;\n\t\ts++;\n\t\twhile (s < e && *s >= 'a' && *s <= 'z')\n\t\t\ts++;\n\t\tif (*s == '=')\n\t\t{\n\t\t\tvs = ++s;\n\t\t\tif (*s == '\\\"')\n\t\t\t{\n\t\t\t\tvs = ++s;\n\t\t\t\twhile (s < e && *s != '\\\"')\n\t\t\t\t\ts++;\n\t\t\t\toutlen = s - vs;\n\t\t\t\ts++;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\twhile (s < e && *s != ',')\n\t\t\t\t\ts++;\n\t\t\t\toutlen = s - vs;\n\t\t\t}\n\n\t\t\tif (!strncmp(vn, arg, strlen(arg)) && vn[strlen(arg)] == '=')\n\t\t\t{\n\t\t\t\tmemcpy(out, vs, outlen);\n\t\t\t\tout[outlen] = 0;\n\t\t\t\treturn outlen;\n\t\t\t}\n\t\t}\n\t}\n\tout[0] = 0;\n\treturn 0;\n}\n\nstatic int sasl_digestmd5_initial(struct sasl_ctx_s *ctx, char *buf, int bufsize)\n{\n\tif (ctx->allowauth_digestmd5)\n\t{\n\t\tif (!*ctx->password_plain && ctx->password_hash_size != 16)\n\t\t\treturn -2;\n\n\t\t//FIXME: randomize the cnonce and check the auth key\n\t\t//although really I'm not entirely sure what the point is.\n\t\t//if we just authenticated with a mitm attacker relay, we're screwed either way.\n\t\tstrcpy(ctx->digest.authnonce, \"abcdefghijklmnopqrstuvwxyz\");\n\t\t//nothing. server does the initial data.\n\t\treturn 0;\n\t}\n\treturn -1;\n}\nchar *MD5_ToHex(char *input, int inputlen, char *ret, int retlen);\nchar *MD5_ToBinary(char *input, int inputlen, char *ret, int retlen);\nstatic int sasl_digestmd5_challenge(struct sasl_ctx_s *ctx, char *in, int inlen, char *out, int outlen)\n{\n\tchar *username = ctx->username;\n\tchar *cnonce = ctx->digest.authnonce;\n\tchar rspauth[512];\n\tchar realm[512];\n\tchar nonce[512];\n\tchar qop[512];\n\tchar charset[512];\n\tchar algorithm[512];\n\tchar X[512];\n\tchar Y[33];\n\tchar A1[512];\n\tchar A2[512];\n\tchar HA1[33];\n\tchar HA2[33];\n\tchar KD[512];\n\tchar Z[33];\n\tchar *nc = \"00000001\";\n\tchar digesturi[512];\n\tchar *authzid = \"\";\n\n\tsaslattr(rspauth, sizeof(rspauth), in, inlen, \"rspauth\");\n\tif (*rspauth)\n\t{\n\t\treturn 0;\t//we don't actually send any data back, just an xml 'response' tag to tell the server that we accept it.\n\t}\n\n\tsaslattr(realm, sizeof(realm), in, inlen, \"realm\");\n\tsaslattr(nonce, sizeof(nonce), in, inlen, \"nonce\");\n\tsaslattr(qop, sizeof(qop), in, inlen, \"qop\");\n\tsaslattr(charset, sizeof(charset), in, inlen, \"charset\");\n\tsaslattr(algorithm, sizeof(algorithm), in, inlen, \"algorithm\");\n\n\tif (!*realm)\n\t\tQ_strlcpy(realm, ctx->domain, sizeof(realm));\n\tif (Q_snprintfz(digesturi, sizeof(digesturi), \"xmpp/%s\", realm))\n\t\treturn -1;\n\n\n\tif (Q_snprintfz(X, sizeof(X), \"%s:%s:\", username, realm))\n\t\treturn -1;\n\tif (ctx->password_hash_size == 16 && !strcmp(X, ctx->password_validity))\n\t\tmemcpy(Y, ctx->password_hash, 16);\t//use the hashed password, instead of the (missing) plain one\n\telse if (*ctx->password_plain)\n\t{\n\t\tQ_strlcpy(ctx->password_validity, X, sizeof(ctx->password_validity));\n\n\t\tif (Q_snprintfz(X, sizeof(X), \"%s:%s:%s\", username, realm, ctx->password_plain))\n\t\t\treturn -1;\n\t\tMD5_ToBinary(X, strlen(X), Y, sizeof(Y));\n\n\t\tctx->password_hash_size = 16;\n\t\tmemcpy(ctx->password_hash, Y, 16);\t//save that hash for later.\n\t}\n\telse\n\t\treturn -1;\t//err, we didn't have a password...\n\n\tmemcpy(A1, Y, 16);\n\tif (*authzid)\n\t{\n\t\tif (Q_snprintfz(A1+16, sizeof(A1)-16, \":%s:%s:%s\", nonce, cnonce, authzid))\n\t\t\treturn -1;\n\t}\n\telse\n\t{\n\t\tif (Q_snprintfz(A1+16, sizeof(A1)-16, \":%s:%s\", nonce, cnonce))\n\t\t\treturn -1;\n\t}\n\tif (Q_snprintfz(A2, sizeof(A2), \"%s:%s\", \"AUTHENTICATE\", digesturi))\n\t\treturn -1;\n\tMD5_ToHex(A1, strlen(A1+16)+16, HA1, sizeof(HA1));\n\tMD5_ToHex(A2, strlen(A2), HA2, sizeof(HA2));\n\tif (Q_snprintfz(KD, sizeof(KD), \"%s:%s:%s:%s:%s:%s\", HA1, nonce, nc, cnonce, qop, HA2))\n\t\treturn -1;\n\tMD5_ToHex(KD, strlen(KD), Z, sizeof(Z));\n\n\tif (*authzid)\n\t\tQ_snprintf(out, outlen, \"username=\\\"%s\\\",realm=\\\"%s\\\",nonce=\\\"%s\\\",cnonce=\\\"%s\\\",nc=\\\"%s\\\",qop=\\\"%s\\\",digest-uri=\\\"%s\\\",response=\\\"%s\\\",charset=\\\"%s\\\",authzid=\\\"%s\\\"\",\n\t\t\t\t\tusername, realm, nonce, cnonce, nc, qop, digesturi, Z, charset, authzid);\n\telse\n\t\tQ_snprintf(out, outlen, \"username=\\\"%s\\\",realm=\\\"%s\\\",nonce=\\\"%s\\\",cnonce=\\\"%s\\\",nc=\\\"%s\\\",qop=\\\"%s\\\",digest-uri=\\\"%s\\\",response=\\\"%s\\\",charset=\\\"%s\\\"\",\n\t\t\t\t\tusername, realm, nonce, cnonce, nc, qop, digesturi, Z, charset);\n\n\treturn strlen(out);\n}\n\ntypedef struct\n{\n\tint len;\n\tchar buf[512];\n} buf_t;\nstatic int sasl_scram_initial(struct sasl_ctx_s *ctx, char *buf, int bufsize, hashfunc_t *hashfunc, qboolean plus)\n{\n\tif (ctx->allowauth_scramsha1)\n\t{\n\t\tunsigned int t;\n\t\tbuf_t bindingdata;\n\t\tint bs;\n\t\tif (!*ctx->password_plain && ctx->password_hash_size != hashfunc->digestsize)\n\t\t\treturn -2;\n\n#if 1//FIXME: test that we're compliant when we're verifying channel bindings.\n\t\t//the channel binding thing helps to avoid MITM attacks by tying the auth to the tls keys.\n\t\t//a MITM attacker will have different binding ids each side, resulting in screwed hashes (they would need to use the public keys (which would require knowing private keys too), or something).\n\t\tif (ctx->issecure)\n\t\t{\n\t\t\tbindingdata.len = sizeof(bindingdata.buf);\n\t\t\tif (netfuncs)\n\t\t\t\tbs = netfuncs->GetTLSBinding(ctx->socket, bindingdata.buf, &bindingdata.len);\n\t\t\telse\n\t\t\t\tbs = -1;\n\t\t}\n\t\telse\n#endif\n\t\t{\n\t\t\tbindingdata.len = 0;\n\t\t\tbs = -1;\n\t\t}\n\t\t//couldn't use plus for some reason\n\t\tif (bs < 0)\n\t\t{\t//not implemented by our tls implementation.\n\t\t\tif (plus)\n\t\t\t\treturn -1;\t//can't auth with this mechanism\n\t\t\tQ_strlcpy(ctx->scram.authcbindtype, \"n\", sizeof(ctx->scram.authcbindtype));\n\t\t}\n\t\telse if (plus && bs > 0)\n\t\t{\t//both sides should support it.\n\t\t\tQ_strlcpy(ctx->scram.authcbindtype, \"p=tls-unique\", sizeof(ctx->scram.authcbindtype));\n\t\t\tBase64_Add(bindingdata.buf, bindingdata.len);\n\t\t\tQ_snprintf(ctx->scram.authcbinding, sizeof(ctx->scram.authcbinding), \"%s\", Base64_Finish());\n\t\t}\n\t\telse\n\t\t{\t//we support it, but the server does not appear to (we failed the plus method, or it just wasn't listed).\n\t\t\t//server will fail the auth if it can do channel bindings (assuming someone stripped the plus method).\n\t\t\tQ_strlcpy(ctx->scram.authcbindtype, \"y\", sizeof(ctx->scram.authcbindtype));\n\t\t}\n\n\t\t//FIXME: this should be more random, to validate that the server actually knows our password too\n\t\t//we can't really do anything until we've already signed in, so why bother?\n\t\tt = plugfuncs->GetMilliseconds();\n\t\tBase64_Add((void*)&t, sizeof(t));\n\t\tBase64_Add(\"0123456789abcdef\", 16);\n\t\tBase64_Add((void*)&jclient_curtime, sizeof(jclient_curtime));\n\t\tstrcpy(ctx->scram.authnonce, Base64_Finish());\n\t\tctx->scram.hashfunc = hashfunc;\n\n\t\tQ_snprintf(buf, bufsize, \"%s,,n=%s,r=%s\", ctx->scram.authcbindtype, ctx->username, ctx->scram.authnonce);\n\t\treturn strlen(buf);\n\t}\n\treturn -1;\n}\nstatic int sasl_scramsha1minus_initial(struct sasl_ctx_s *ctx, char *buf, int bufsize)\n{\n\treturn sasl_scram_initial(ctx, buf, bufsize, &hash_sha1, false);\n}\nstatic int sasl_scramsha256minus_initial(struct sasl_ctx_s *ctx, char *buf, int bufsize)\n{\n\treturn sasl_scram_initial(ctx, buf, bufsize, &hash_sha2_256, false);\n}\nstatic int sasl_scramsha512minus_initial(struct sasl_ctx_s *ctx, char *buf, int bufsize)\n{\n\treturn sasl_scram_initial(ctx, buf, bufsize, &hash_sha2_512, false);\n}\nstatic int sasl_scramsha1plus_initial(struct sasl_ctx_s *ctx, char *buf, int bufsize)\n{\n\treturn sasl_scram_initial(ctx, buf, bufsize, &hash_sha1, true);\n}\nstatic int sasl_scramsha256plus_initial(struct sasl_ctx_s *ctx, char *buf, int bufsize)\n{\n\treturn sasl_scram_initial(ctx, buf, bufsize, &hash_sha2_256, true);\n}\nstatic int sasl_scramsha512plus_initial(struct sasl_ctx_s *ctx, char *buf, int bufsize)\n{\n\treturn sasl_scram_initial(ctx, buf, bufsize, &hash_sha2_512, true);\n}\n\nstatic void buf_cat(buf_t *buf, char *data, int len)\n{\n\tmemcpy(buf->buf + buf->len, data, len);\n\tbuf->len += len;\n\tbuf->buf[buf->len] = 0;\n}\nstatic void buf_cats(buf_t *buf, char *data)\n{\n\tbuf_cat(buf, data, strlen(data));\n}\nstatic size_t HMAC_Hi(hashfunc_t *hashfunc, char *out, char *password, int passwordlen, buf_t *salt, int times)\n{\n\tsize_t digestsize;\n\tchar prev[64];\n\tint i, j;\n\n\t//first iteration is special\n\tbuf_cat(salt, \"\\0\\0\\0\\1\", 4);\n\tdigestsize = CalcHMAC(hashfunc, prev, sizeof(prev), salt->buf, salt->len, password, passwordlen);\n\tmemcpy(out, prev, digestsize);\n\t\n\t//later iterations just use the previous iteration\n\tfor (i = 1; i < times; i++)\n\t{\n\t\tCalcHMAC(hashfunc, prev, digestsize, prev, digestsize, password, passwordlen);\n\n\t\tfor (j = 0; j < digestsize; j++)\n\t\t\tout[j] ^= prev[j];\n\t}\n\treturn digestsize;\n}\nstatic int sasl_scram_challenge(struct sasl_ctx_s *ctx, char *in, int inlen, char *out, int outlen)\n{\n\thashfunc_t *func = ctx->scram.hashfunc;\n\tsize_t digestsize = func->digestsize;\n\t//sasl SCRAM-SHA-1 challenge\n\t//send back the same 'r' attribute\n\tbuf_t saslchal;\n\tint i;\n\tbuf_t salt;\n\tbuf_t csn;\n\tbuf_t itr;\n\tbuf_t final;\n\tbuf_t sigkey;\n\tunsigned char salted_password[DIGEST_MAXSIZE];\n\tunsigned char proof[DIGEST_MAXSIZE];\n\tunsigned char clientkey[DIGEST_MAXSIZE];\n\tunsigned char serverkey[DIGEST_MAXSIZE];\n\tunsigned char storedkey[DIGEST_MAXSIZE];\n\tunsigned char clientsignature[DIGEST_MAXSIZE];\n\tchar *username = ctx->username;\n\tchar validationstr[256];\n\tconst unsigned char *tmp;\n\n\tif (digestsize > DIGEST_MAXSIZE)\n\t\treturn -1;\n\n\tsaslchal.len = 0;\n\tbuf_cat(&saslchal, in, inlen);\n\t\n\t//be warned, these CAN contain nulls.\n\tcsn.len = saslattr(csn.buf, sizeof(csn.buf), saslchal.buf, saslchal.len, \"r\");\n\tsalt.len = saslattr(salt.buf, sizeof(salt.buf), saslchal.buf, saslchal.len, \"s\");\n\titr.len = saslattr(itr.buf, sizeof(itr.buf), saslchal.buf, saslchal.len, \"i\");\n\n\tsalt.len = Base64_Decode(salt.buf, sizeof(salt.buf), salt.buf, salt.len);\n\n\t//csn MUST be prefixed with out authnone, to avoid mess.\n\tif (strncmp(csn.buf, ctx->scram.authnonce, strlen(ctx->scram.authnonce)))\n\t\treturn -1;\n\n\t//this is the first part of the message we're about to send, with no proof.\n\t//c(channel) is mandatory but nulled and forms part of the hash\n\tfinal.len = 0;\n\tbuf_cats(&final, \"c=\");\n\tBase64_Add(ctx->scram.authcbindtype, strlen(ctx->scram.authcbindtype));\n\tBase64_Add(\",,\", 2);\n\tBase64_Add(ctx->scram.authcbinding, strlen(ctx->scram.authcbinding));\n\tbuf_cats(&final, Base64_Finish());\n\tbuf_cats(&final, \",r=\");\n\tbuf_cat(&final, csn.buf, csn.len);\n\n\t//our original message + ',' + challenge + ',' + the message we're about to send.\n\tsigkey.len = 0;\n\tbuf_cats(&sigkey, \"n=\");\n\tbuf_cats(&sigkey, username);\n\tbuf_cats(&sigkey, \",r=\");\n\tbuf_cats(&sigkey, ctx->scram.authnonce);\n\tbuf_cats(&sigkey, \",\");\n\tbuf_cat(&sigkey, saslchal.buf, saslchal.len);\n\tbuf_cats(&sigkey, \",\");\n\tbuf_cat(&sigkey, final.buf, final.len);\n\n\tBase64_Add(salt.buf, salt.len);\n\tQ_snprintf(validationstr, sizeof(validationstr), \"%i:%s\", atoi(itr.buf), Base64_Finish());\n\tif (ctx->password_hash_size == digestsize && !strcmp(validationstr, ctx->password_validity))\n\t{\n\t\t//restore the hash\n\t\tmemcpy(salted_password, ctx->password_hash, digestsize);\n\t}\n\telse if (*ctx->password_plain)\n\t{\n\t\tctx->password_hash_size = HMAC_Hi(func, salted_password, ctx->password_plain, strlen(ctx->password_plain), &salt, atoi(itr.buf));\n\n\t\t//save that hash for later.\n\t\tQ_strlcpy(ctx->password_validity, validationstr, sizeof(ctx->password_validity));\n\t\tmemcpy(ctx->password_hash, salted_password, ctx->password_hash_size);\n\t}\n\telse\n\t\treturn -2;\t//panic. password not known any more. the server should not be changing salt/itr.\n\n\tCalcHMAC(func, clientkey, digestsize, \"Client Key\", strlen(\"Client Key\"), salted_password, digestsize);\n//Note: if we wanted to be fancy, we could store both clientkey and serverkey instead of salted_password, but I'm not sure there's all that much point.\n\ttmp = clientkey;\n\tCalcHash(func, storedkey, digestsize, tmp, digestsize);\n\tCalcHMAC(func, clientsignature, digestsize, sigkey.buf, sigkey.len, storedkey, digestsize);\n\n\tfor (i = 0; i < digestsize; i++)\n\t\tproof[i] = clientkey[i] ^ clientsignature[i];\n\n\tBase64_Add(proof, digestsize);\n\tBase64_Finish();\n\n\t//to validate the server...\n\tCalcHMAC(func, serverkey, digestsize, \"Server Key\", strlen(\"Server Key\"), salted_password, digestsize);\n\tCalcHMAC(func, ctx->scram.authvhash, digestsize, sigkey.buf, sigkey.len, serverkey, digestsize); //aka:serversignature\n\n\t//\"c=biws,r=fyko+d2lbbFgONRv9qkxdawL3rfcNHYJY1ZVvWVs7j,p=v0X8v3Bz2T0CJGbJQyF0X+HI4Ts=\"\n\tQ_snprintf(out, outlen, \"%s,p=%s\", final.buf, base64);\n\treturn strlen(out);\n}\n\nstatic int sasl_scram_final(struct sasl_ctx_s *ctx, char *in, int inlen)\n{\n\tbuf_t valid;\n\n\tvalid.len = saslattr(valid.buf, sizeof(valid.buf), in, inlen, \"v\");\n\tvalid.len = Base64_Decode(valid.buf, sizeof(valid.buf), valid.buf, valid.len);\n\tif (valid.len != ctx->scram.hashfunc->digestsize || memcmp(ctx->scram.authvhash, valid.buf, valid.len))\n\t\treturn -1;\t//server didn't give us the right answer. this is NOT the server we're looking for. give up now.\n\treturn true;\n}\n\nvoid URL_Split(char *url, char *proto, int protosize, char *host, int hostsize, char *res, int ressize)\n{\n\tchar *s;\n\t*proto = 0;\n\t*host = 0;\n\t*res = 0;\n\ts = strchr(url, ':');\n\tif (!s)\n\t\treturn;\n\tprotosize = min(protosize-1, s-url);\n\tmemcpy(proto, url, protosize);\n\tproto[protosize] = 0;\n\ts++;\n\tif (s[0] == '/' && s[1] == '/')\n\t\ts+=2;\n\turl = s;\n\ts = strchr(url, '/');\n\tif (!s)\n\t\ts = url+strlen(url);\n\thostsize = min(hostsize-1, s-url);\n\tmemcpy(host, url, hostsize);\n\thost[hostsize] = 0;\n\turl = s;\n\ts = url+strlen(url);\n\tressize = min(ressize-1, s-url);\n\tmemcpy(res, url, ressize);\n\tres[ressize] = 0;\n}\nvoid Q_strlcat_urlencode(char *d, const char *s, int n)\n{\n\tchar hex[16] = \"0123456789ABCDEF\";\n\tint clen = strlen(d);\n\td += clen;\n\tn -= clen;\n\tn--;\n\tif (s)\n\twhile (*s)\n\t{\n\t\tif ((*s >= '0' && *s <= '9') ||\n\t\t\t(*s >= 'a' && *s <= 'z') ||\n\t\t\t(*s >= 'A' && *s <= 'Z') ||\n\t\t\t*s == '-' || *s == '_' || *s == '.')\n\t\t{\n\t\t\tif (!n)\n\t\t\t\tbreak;\n\t\t\tn--;\n\t\t\t*d++ = *s++;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (n < 3)\n\t\t\t\tbreak;\n\t\t\tn -= 3;\n\t\t\t*d++ = '%';\n\t\t\t*d++ = hex[*s>>4];\n\t\t\t*d++ = hex[*s&15];\n\t\t\ts++;\n\t\t}\n\t}\n\t*d = 0;\n}\nstatic int sasl_oauth2_initial(struct sasl_ctx_s *ctx, char *buf, int bufsize)\n{\n\tchar proto[256];\n\tchar host[256];\n\tchar resource[256];\n\tint sock, l, rl=0;\n\tchar result[8192];\n\n\txmltree_t *x;\n\n\tif (*ctx->password_plain)\n\t\treturn -1;\n\n\tif (0)//*jcl->password_plain)\n\t{\n\t\tchar body[4096];\n\t\tchar header[4096];\n\n\t\tURL_Split(ctx->oauth2.refreshurl, proto, sizeof(proto), host, sizeof(host), resource, sizeof(resource));\n\n\t\t*body = 0;\n\t\tQ_strlcat(body, \"client_id=\", sizeof(body));\n\t\tQ_strlcat_urlencode(body, ctx->oauth2.clientid, sizeof(body));\n\t\tQ_strlcat(body, \"&client_secret=\", sizeof(body));\n\t\tQ_strlcat_urlencode(body, ctx->oauth2.clientsecret, sizeof(body));\n\t\tQ_strlcat(body, \"&\", sizeof(body));\n\n\t\tQ_strlcat(body, \"grant_type=password&username=\", sizeof(body));\n\t\tQ_strlcat_urlencode(body, ctx->oauth2.useraccount, sizeof(body));\n\t\tQ_strlcat(body, \"&password=\", sizeof(body));\n\t\tQ_strlcat_urlencode(body, ctx->password_plain, sizeof(body));\n\n\t\tQ_strlcat(body, \"&response_type=code\", sizeof(body));\n\n\t\tQ_strlcat(body, \"&redirect_uri=\", sizeof(body));\n\t\tQ_strlcat_urlencode(body, \"urn:ietf:wg:oauth:2.0:oob\", sizeof(body));\n\n\t\tQ_strlcat(body, \"&scope=\", sizeof(body));\n\t\tQ_strlcat_urlencode(body, ctx->oauth2.scope, sizeof(body));\n\n\t\tQ_snprintf(header, sizeof(header),\n\t\t\t\"POST %s HTTP/1.1\\r\\n\"\n\t\t\t\"Host: %s\\r\\n\"\n\t\t\t//\"Authorization: Basic %s\\r\\n\"\n\t\t\t\"Content-length: %u\\r\\n\"\n\t\t\t\"Content-Type: application/x-www-form-urlencoded\\r\\n\"\n\t\t\t\"Connection: close\\r\\n\"\n\t\t\t\"\\r\\n\",\n\t\t\t\tresource,\n\t\t\t\thost,\n\t\t\t\t(unsigned)strlen(body));\n\n\t\tsock = netfuncs->TCPConnect(host, 443);\n\t\tif (netfuncs->SetTLSClient(sock, host) >= 0)\n\t\t{\n\t\t\tnetfuncs->Send(sock, header, strlen(header));\n\t\t\tnetfuncs->Send(sock, body, strlen(body));\n\t\t\twhile(1)\n\t\t\t{\n\t\t\t\t//FIXME: infinite loop!\n\t\t\t\tl = netfuncs->Recv(sock, result+rl, sizeof(result)-rl);\n\t\t\t\tif (l < 0)\n\t\t\t\t\tbreak;\n\t\t\t\telse\n\t\t\t\t\trl += l;\n\t\t\t}\n\t\t}\n\t\tnetfuncs->Close(sock);\n\t\tresult[rl] = 0;\n\n\t\tCon_Printf(\"Got %s\\n\", result);\n\t}\n\t\n\t//if we have nothing, load up a browser to ask for the first token\n\tif (!*ctx->oauth2.refreshtoken && !*ctx->oauth2.authtoken)\n\t{\n\t\tchar url[4096];\n\t\t*url = 0;\n\t\tQ_strlcat(url, ctx->oauth2.obtainurl, sizeof(url));\n\t\tQ_strlcat(url, \"?redirect_uri=\", sizeof(url));\n\t\tQ_strlcat_urlencode(url, \"urn:ietf:wg:oauth:2.0:oob\", sizeof(url));\n\t\tQ_strlcat(url, \"&%72esponse_type=code&client_id=\", sizeof(url));\t//%72 = r. fucking ezquake colour codes. works with firefox anyway. no idea if that's the server changing it to an r or not. :s\n\t\tQ_strlcat_urlencode(url, ctx->oauth2.clientid, sizeof(url));\n\t\tQ_strlcat(url, \"&scope=\", sizeof(url));\n\t\tQ_strlcat_urlencode(url, ctx->oauth2.scope, sizeof(url));\n\t\tQ_strlcat(url, \"&access_type=offline\", sizeof(url));\n\t\tQ_strlcat(url, \"&login_hint=\", sizeof(url));\n\t\tQ_strlcat_urlencode(url, ctx->oauth2.useraccount, sizeof(url));\n\n//\t\tCon_Printf(\"Please visit ^[^4%s\\\\url\\\\%s^] and then enter:\\n^[/\"COMMANDPREFIX\"%i /oa2token <TOKEN>^]\\nNote: you can right-click the link to copy it to your browser, and you can use ctrl+v to paste the resulting auth token as part of the given command.\\n\", url, url, jcl->accountnum);\n\n\t\t//wait for user to act.\n\t\treturn -2;\n\t}\n\n\t//refresh token is not known, try and get one\n\tif (!*ctx->oauth2.refreshtoken && *ctx->oauth2.authtoken)\n\t{\n\t\txmltree_t *x;\n\t\tchar body[4096];\n\t\tchar header[4096];\n\n\t\t//send a refresh request\n\n\t\t*body = 0;\n\t\tQ_strlcat(body, \"code=\", sizeof(body));\n\t\tQ_strlcat_urlencode(body, ctx->oauth2.authtoken, sizeof(body));\n\t\tQ_strlcat(body, \"&client_id=\", sizeof(body));\n\t\tQ_strlcat_urlencode(body, ctx->oauth2.clientid, sizeof(body));\n\t\tQ_strlcat(body, \"&client_secret=\", sizeof(body));\n\t\tQ_strlcat_urlencode(body, ctx->oauth2.clientsecret, sizeof(body));\n\t\tQ_strlcat(body, \"&redirect_uri=\", sizeof(body));\n\t\tQ_strlcat_urlencode(body, \"urn:ietf:wg:oauth:2.0:oob\", sizeof(body));\n\t\tQ_strlcat(body, \"&grant_type=\", sizeof(body));\n\t\tQ_strlcat_urlencode(body, \"authorization_code\", sizeof(body));\n\t\tURL_Split(ctx->oauth2.refreshurl, proto, sizeof(proto), host, sizeof(host), resource, sizeof(resource));\n\n\t\tQ_snprintf(header, sizeof(header),\n\t\t\t\"POST %s HTTP/1.1\\r\\n\"\n\t\t\t\"Host: %s\\r\\n\"\n\t\t\t//\"Authorization: Basic %s\\r\\n\"\n\t\t\t\"Content-length: %u\\r\\n\"\n\t\t\t\"Content-Type: application/x-www-form-urlencoded\\r\\n\"\n\t\t\t\"user-agent: fteqw-plugin-xmpp\\r\\n\"\n\t\t\t\"Connection: close\\r\\n\"\n\t\t\t\"\\r\\n\",\n\t\t\tresource, host, (unsigned)strlen(body));\n\n\t\tCon_Printf(\"XMPP: Requesting access token\\n\");\n\t\tsock = netfuncs->TCPConnect(host, 443);\n\t\tif (netfuncs->SetTLSClient(sock, host) >= 0)\n\t\t{\n\t\t\tnetfuncs->Send(sock, header, strlen(header));\n\t\t\tnetfuncs->Send(sock, body, strlen(body));\n\t\t\twhile(1)\n\t\t\t{\n\t\t\t\t//FIXME: infinite loop!\n\t\t\t\tl = netfuncs->Recv(sock, result+rl, sizeof(result)-rl);\n\t\t\t\tif (l < 0)\n\t\t\t\t\tbreak;\n\t\t\t\telse\n\t\t\t\t\trl += l;\n\t\t\t}\n\t\t}\n\t\tresult[rl] = 0;\n\t\tnetfuncs->Close(sock);\n\n\t\t//should contain something like:\n\t\t//{\n\t\t//\"access_token\" : \"ya29.AHES6ZR-_Sx0UpexZdgqQwR8LFqTx-GFi-Zrq4nKrcLLA98N7g\",\n\t\t//\"token_type\" : \"Bearer\",\n\t\t//\"expires_in\" : 3600\n\t\t//}\n\n\t\tl = strstr(result, \"\\r\\n\\r\\n\")-result;\n\t\tl+= 4;\n\t\tif (l < 0 || l > rl)\n\t\t\tl = rl;\n\t\tx = XML_FromJSON(NULL, \"oauth2\", result, &l, rl);\n\t\tXML_ConPrintTree(x, \"\", 1);\n\t\tfree(ctx->oauth2.accesstoken);\n\t\tfree(ctx->oauth2.refreshtoken);\n\t\tctx->oauth2.accesstoken = strdup(XML_GetChildBody(x, \"access_token\", \"\"));\n//\t\tctx->oauth2.token_type = strdup(XML_GetChildBody(x, \"token_type\", \"\"));\n//\t\tctx->oauth2.expires_in = strdup(XML_GetChildBody(x, \"expires_in\", \"\"));\n\t\tctx->oauth2.refreshtoken = strdup(XML_GetChildBody(x, \"refresh_token\", \"\"));\n\n\t\t//in theory, the auth token is no longer valid/needed\n\t\tfree(ctx->oauth2.authtoken);\n\t\tctx->oauth2.authtoken = strdup(\"\");\n\t}\n\n\t//refresh our refresh token, obtaining a usable sign-in token at the same time.\n\telse if (!*ctx->oauth2.accesstoken)\n\t{\n\t\tchar body[4096];\n\t\tchar header[4096];\n\t\tconst char *newrefresh;\n\n\t\t//send a refresh request\n\n\t\t*body = 0;\n\t\tQ_strlcat(body, \"client_id=\", sizeof(body));\n\t\tQ_strlcat_urlencode(body, ctx->oauth2.clientid, sizeof(body));\n\t\tQ_strlcat(body, \"&client_secret=\", sizeof(body));\n\t\tQ_strlcat_urlencode(body, ctx->oauth2.clientsecret, sizeof(body));\n\t\tQ_strlcat(body, \"&grant_type=\", sizeof(body));\n\t\tQ_strlcat_urlencode(body, \"refresh_token\", sizeof(body));\n\t\tQ_strlcat(body, \"&refresh_token=\", sizeof(body));\n\t\tQ_strlcat_urlencode(body, ctx->oauth2.refreshtoken, sizeof(body));\n\t\tURL_Split(ctx->oauth2.refreshurl, proto, sizeof(proto), host, sizeof(host), resource, sizeof(resource));\n\n\t\tQ_snprintf(header, sizeof(header),\n\t\t\t\"POST %s HTTP/1.1\\r\\n\"\n\t\t\t\"Host: %s\\r\\n\"\n\t\t\t//\"Authorization: Basic %s\\r\\n\"\n\t\t\t\"Content-length: %u\\r\\n\"\n\t\t\t\"Content-Type: application/x-www-form-urlencoded\\r\\n\"\n\t\t\t\"user-agent: fteqw-plugin-xmpp\\r\\n\"\n\t\t\t\"Connection: close\\r\\n\"\n\t\t\t\"\\r\\n\",\n\t\t\tresource, host, (unsigned)strlen(body));\n\n\t\tCon_Printf(\"XMPP: Refreshing access token\\n\");\n\t\tsock = netfuncs->TCPConnect(host, 443);\n\t\tif (netfuncs->SetTLSClient(sock, host) >= 0)\n\t\t{\n\t\t\tnetfuncs->Send(sock, header, strlen(header));\n\t\t\tnetfuncs->Send(sock, body, strlen(body));\n\t\t\twhile(1)\n\t\t\t{\n\t\t\t\t//FIXME: infinite loop!\n\t\t\t\tl = netfuncs->Recv(sock, result+rl, sizeof(result)-rl);\n\t\t\t\tif (l < 0)\n\t\t\t\t\tbreak;\n\t\t\t\telse\n\t\t\t\t\trl += l;\n\t\t\t}\n\t\t}\n\t\tnetfuncs->Close(sock);\n\t\tresult[rl] = 0;\n\n\t\tl = strstr(result, \"\\r\\n\\r\\n\")-result;\n\t\tl+= 4;\n\t\tif (l < 0 || l > rl)\n\t\t\tl = rl;\n\t\tx = XML_FromJSON(NULL, \"oauth2\", result, &l, rl);\n//\t\tXML_ConPrintTree(x, \"\", 1);\n\n\t\tnewrefresh = XML_GetChildBody(x, \"refresh_token\", NULL);\n\t\tfree(ctx->oauth2.accesstoken);\n\t\tctx->oauth2.accesstoken = strdup(XML_GetChildBody(x, \"access_token\", \"\"));\n\t\tif (newrefresh || !*ctx->oauth2.accesstoken)\n\t\t{\n\t\t\tfree(ctx->oauth2.refreshtoken);\n\t\t\tctx->oauth2.refreshtoken = strdup(XML_GetChildBody(x, \"refresh_token\", \"\"));\n\t\t}\n//\t\tctx->oauth2.token_type = strdup(XML_GetChildBody(x, \"token_type\", \"\"));\n//\t\tctx->oauth2.expires_in = strdup(XML_GetChildBody(x, \"expires_in\", \"\"));\n\n\t\t//refresh token may mutate. follow the mutation.\n\t}\n\telse if (*ctx->oauth2.accesstoken)\n\t\tCon_Printf(\"XMPP: Using explicit access token\\n\");\n\n\tif (*ctx->oauth2.accesstoken)\n\t{\n\t\tint len = 0;\n\t\tif (*ctx->oauth2.useraccount)\n\t\t{\n\t\t\t//realm isn't specified\n\t\t\tbuf[len++] = 0;\n\t\t\tmemcpy(buf+len, ctx->oauth2.useraccount, strlen(ctx->oauth2.useraccount));\n\t\t\tlen += strlen(ctx->oauth2.useraccount);\n\t\t\tbuf[len++] = 0;\n\t\t}\n\t\tmemcpy(buf+len, ctx->oauth2.accesstoken, strlen(ctx->oauth2.accesstoken));\n\t\tlen += strlen(ctx->oauth2.accesstoken);\n\n\t\tCon_Printf(\"XMPP: Signing in\\n\");\n\t\tfree(ctx->oauth2.accesstoken);\n\t\tctx->oauth2.accesstoken = strdup(\"\");\n\t\treturn len;\n\t}\n\n\t//if the reply has a refresh token in it, clear out any password info\n\treturn -2;\n}\n\n//in descending priority order\nstatic saslmethod_t saslmethods[] =\n{\n\t{\"SCRAM-SHA-512-PLUS\",\tsasl_scramsha512plus_initial,\tsasl_scram_challenge,\t\tsasl_scram_final},\t//lots of unreadable hashing, with added channel bindings\n\t{\"SCRAM-SHA-256-PLUS\",\tsasl_scramsha256plus_initial,\tsasl_scram_challenge,\t\tsasl_scram_final},\t//lots of unreadable hashing, with added channel bindings\n\t{\"SCRAM-SHA-1-PLUS\",\tsasl_scramsha1plus_initial,\t\tsasl_scram_challenge,\t\tsasl_scram_final},\t//lots of unreadable hashing, with added channel bindings\n\t{\"SCRAM-SHA-512\",\t\tsasl_scramsha512minus_initial,\tsasl_scram_challenge,\t\tsasl_scram_final},\t//lots of unreadable hashing\n\t{\"SCRAM-SHA-256\",\t\tsasl_scramsha256minus_initial,\tsasl_scram_challenge,\t\tsasl_scram_final},\t//lots of unreadable hashing\n\t{\"SCRAM-SHA-1\",\t\t\tsasl_scramsha1minus_initial,\tsasl_scram_challenge,\t\tsasl_scram_final},\t//lots of unreadable hashing\n\t{\"DIGEST-MD5\",\t\t\tsasl_digestmd5_initial,\t\t\tsasl_digestmd5_challenge,\tNULL},\t\t\t\t//kinda silly but still better than plaintext.\n\t{\"PLAIN\",\t\t\t\tsasl_plain_initial,\t\t\t\tNULL,\t\t\t\t\t\tNULL},\t\t\t\t//realm\\0username\\0password\n\t{NULL,\t\t\t\t\tsasl_oauth2_initial,\t\t\tNULL,\t\t\t\t\t\tNULL}\t\t\t\t//potentially avoids having to ask+store their password. a browser is required to obtain auth token for us, so basically like pulling teeth.\n};\n\n/*\npidgin's msn request\nhttps://oauth.live.com/authorize?client_id=000000004C07035A&scope=wl.messenger,wl.basic,wl.offline_access,wl.contacts_create,wl.share&response_type=token&redirect_uri=http://pidgin.im/\n*/\n\n\nstruct subtree_s;\n\n//void JCL_AddClientMessagef(jclient_t *jcl, char *fmt, ...);\n//qboolean JCL_FindBuddy(jclient_t *jcl, const char *jid, buddy_t **buddy, bresource_t **bres, qboolean create);\nvoid JCL_GeneratePresence(jclient_t *jcl, qboolean force);\nstruct iq_s *JCL_SendIQf(jclient_t *jcl, qboolean (*callback) (jclient_t *jcl, struct subtree_s *tree, struct iq_s *iq), const char *iqtype, const char *target, const char *fmt, ...);\n//struct iq_s *JCL_SendIQNode(jclient_t *jcl, qboolean (*callback) (jclient_t *jcl, xmltree_t *tree, struct iq_s *iq), const char *iqtype, const char *target, xmltree_t *node, qboolean destroynode);\nvoid JCL_CloseConnection(jclient_t *jcl, const char *reason, qboolean reconnect);\nvoid JCL_JoinMUCChat(jclient_t *jcl, const char *room, const char *server, const char *myhandle, const char *password);\nstatic qboolean JCL_BuddyVCardReply(jclient_t *jcl, xmltree_t *tree, struct iq_s *iq);\n\nvoid JCL_GenLink(jclient_t *jcl, char *out, int outlen, const char *action, const char *context, const char *contextres, const char *sid, const char *txtfmt, ...)\n{\n\tva_list\t\targptr;\n\tqboolean textonly = false;\n\t*out = 0;\n\tif (!strchr(txtfmt, '%'))\n\t{\t//protect against potential bugs and exploits.\n\t\tQ_strlcpy(out, \"bad link text\", outlen);\n\t\treturn;\n\t}\n\t//FIXME: validate that there is no \\\\ markup within any section that would break the link.\n\t//FIXME: validate that ^[ and ^] are not used, as that would also mess things up. add markup for every ^?\n\tif (textonly)\n\t{\n\t\tva_start(argptr, txtfmt);\n\t\tQ_strlcat(out, \"[\", outlen);\n\t\tQ_vsnprintf(out, outlen, txtfmt, argptr);\n\t\tQ_strlcat(out, \"]\", outlen);\n\t\tva_end(argptr);\n\t\treturn;\n\t}\n\tQ_strlcat(out, \"^[[\", outlen);\n\tva_start(argptr, txtfmt);\n\tQ_vsnprintf(out+3, outlen-3, txtfmt, argptr);\n\tva_end(argptr);\n\tQ_strlcat(out, \"]\", outlen);\n\n\tif (jcl && jcl->accountnum)\n\t{\n\t\tchar acc[32];\n\t\tQ_snprintf(acc, sizeof(acc), \"%i\", jcl->accountnum);\n\t\tQ_strlcat(out, \"\\\\xmppacc\\\\\", outlen);\n\t\tQ_strlcat(out, acc, outlen);\n\t}\n\n\tif (action)\n\t{\n\t\tQ_strlcat(out, \"\\\\xmppact\\\\\", outlen);\n\t\tQ_strlcat(out, action, outlen);\n\t}\n\tif (context)\n\t{\n\t\tQ_strlcat(out, \"\\\\xmpp\\\\\", outlen);\n\t\tQ_strlcat(out, context, outlen);\n\t\tif (contextres)\n\t\t{\n\t\t\tQ_strlcat(out, \"/\", outlen);\n\t\t\tQ_strlcat(out, contextres, outlen);\n\t\t}\n\t}\n\tif (sid)\n\t{\n\t\tQ_strlcat(out, \"\\\\xmppsid\\\\\", outlen);\n\t\tQ_strlcat(out, sid, outlen);\n\t}\n\tQ_strlcat(out, \"^]\", outlen);\n}\n\nchar *TrimResourceFromJid(char *jid)\n{\n\tchar *slash;\n\tslash = strchr(jid, '/');\n\tif (slash)\n\t{\n\t\t*slash = '\\0';\n\t\treturn slash+1;\n\t}\n\treturn NULL;\n}\n\nqboolean JCL_ConsoleLinkMouseOver(float x, float y)\n{\n\tjclient_t *jcl;\n//\tchar text[256];\n\tchar link[256];\n\tchar who[256];\n\tchar what[256];\n\tchar which[256];\n\tchar *actiontext;\n\tint i;\n\tbuddy_t *b, *me = NULL;\n\tbresource_t *br;\n\n//\tcmdfuncs->Argv(0, text, sizeof(text));\n\tcmdfuncs->Argv(1, link, sizeof(link));\n\n\tJCL_Info_ValueForKey(link, \"xmpp\", who, sizeof(who));\n\tJCL_Info_ValueForKey(link, \"xmppact\", what, sizeof(what));\n\tJCL_Info_ValueForKey(link, \"xmppacc\", which, sizeof(which));\n\n\tif (!*who)\n\t\treturn false;\n\n\ti = atoi(which);\n\ti = bound(0, i, sizeof(jclients)/sizeof(jclients[0]));\n\tjcl = jclients[i];\n\n\tx += 16;\n\n\tif (!jcl)\n\t\treturn false;\n\n\tif (jcl->status != JCL_ACTIVE)\n\t{\n\t\tdrawfuncs->String(x, y, \"^&C0You are currently offline\");\n\t\treturn true;\n\t}\n\n\tif (!strcmp(what, \"pauth\"))\n\t\tactiontext = \"Befriend\";\n\telse if (!strcmp(what, \"pdeny\"))\n\t\tactiontext = \"Decline\";\n#ifdef FILETRANSFERS\n\telse if (!strcmp(what, \"fauth\") && (jcl->enabledcapabilities & CAP_SIFT))\n\t\tactiontext = \"Receive\";\n\telse if (!strcmp(what, \"fdeny\") && (jcl->enabledcapabilities & CAP_SIFT))\n\t\tactiontext = \"Decline\";\n#endif\n#ifdef JINGLE\n\telse if (!strcmp(what, \"jauth\") && (jcl->enabledcapabilities & (CAP_GAMEINVITE|CAP_VOICE|CAP_VIDEO|CAP_GOOGLE_VOICE)))\n\t\tactiontext = \"Answer\";\n\telse if (!strcmp(what, \"jdeny\") && (jcl->enabledcapabilities & (CAP_GAMEINVITE|CAP_VOICE|CAP_VIDEO|CAP_GOOGLE_VOICE)))\n\t\tactiontext = \"Hang Up\";\n\telse if (!strcmp(what, \"join\") && (jcl->enabledcapabilities & CAP_GAMEINVITE))\n\t\tactiontext = \"Join Game\";\n\telse if (!strcmp(what, \"invite\") && (jcl->enabledcapabilities & CAP_GAMEINVITE))\n\t\tactiontext = \"Invite To Game\";\n\telse if (!strcmp(what, \"call\") && (jcl->enabledcapabilities & (CAP_VOICE|CAP_GOOGLE_VOICE)))\n\t\tactiontext = \"Call\";\n\telse if (!strcmp(what, \"vidcall\") && (jcl->enabledcapabilities & CAP_VIDEO))\n\t\tactiontext = \"Video Call\";\n#endif\n\telse if (!strcmp(what, \"mucjoin\"))\n\t\tactiontext = \"Join Chat:\";\n\telse if ((*who && !*what) || !strcmp(what, \"msg\"))\n\t\tactiontext = \"Chat With\";\n\telse\n\t\treturn false;\n\n\tJCL_FindBuddy(jcl, who, &b, &br, false);\n\tif (!b)\n\t\treturn false;\n\tJCL_FindBuddy(jcl, jcl->fulljid, &me, NULL, true);\n\n\tif ((jcl->enabledcapabilities & CAP_AVATARS) && drawfuncs)\n\t{\n\t\tif (b->vcardphotochanged && b->friended && !jcl->avatarupdate)\n\t\t{\n\t\t\tb->vcardphotochanged = false;\n\t\t\tCon_Printf(\"Querying %s's photo\\n\", b->accountdomain);\n\t\t\tjcl->avatarupdate = JCL_SendIQf(jcl, JCL_BuddyVCardReply, \"get\", b->accountdomain, \"<vCard xmlns='vcard-temp'/>\");\n\t\t}\n\t\tif (b->image)\n\t\t{\n\t\t\t//xep-0153: The image height and width SHOULD be between thirty-two (32) and ninety-six (96) pixels; the recommended size is sixty-four (64) pixels high and sixty-four (64) pixels wide.\n\t\t\t//96 just feels far too large for a game that was origionally running at a resolution of 320*200.\n\t\t\t//FIXME: we should proably respect the image's aspect ratio...\n#define IMGSIZE 96/2\n\t\t\tdrawfuncs->Image (x, y, IMGSIZE, IMGSIZE, 0, 0, 1, 1, b->image);\n\t\t\tx += IMGSIZE+8;\n\t\t}\n\t}\n\n\tdrawfuncs->String(x, y, va(\"^&F0%s ^2%s\", actiontext, b->name));\n\ty+=8;\n\tdrawfuncs->String(x, y, va(\"^&F0%s\", b->accountdomain));\n\ty+=8;\n\tif (br)\n\t{\n\t\tdrawfuncs->String(x, y, va(\"^&F0  %s\", br->resource));\n\t\ty+=8;\n\t}\n\tif (b == me)\n\t\tdrawfuncs->String(x, y, \"^&90\" \"You\");\n\telse if (!b->friended)\n\t\tdrawfuncs->String(x, y, \"^&C0\" \"Unknown\");\n\ty+=8;\n\n\treturn true;\n}\nqboolean JCL_ConsoleLink(void)\n{\n\tjclient_t *jcl;\n\tchar link[256];\n\tchar who[256];\n\tchar what[256];\n\tchar which[256];\n\tint i;\n\tbuddy_t *b = NULL;\n//\tcmdfuncs->Argv(0, text, sizeof(text));\n\tcmdfuncs->Argv(1, link, sizeof(link));\n\n\tJCL_Info_ValueForKey(link, \"xmpp\", who, sizeof(who));\n\tJCL_Info_ValueForKey(link, \"xmppact\", what, sizeof(what));\n\tJCL_Info_ValueForKey(link, \"xmppacc\", which, sizeof(which));\n\n\tif (!*who && !*what)\n\t\treturn false;\n\n\ti = atoi(which);\n\ti = bound(0, i, sizeof(jclients)/sizeof(jclients[0]));\n\tjcl = jclients[i];\n\n\tjclient_updatebuddylist = true;\n\n\tif (!strcmp(what, \"pauth\"))\n\t{\n\t\t//we should friend them too.\n\t\tif (jcl && jcl->status == JCL_ACTIVE)\n\t\t\tJCL_AddClientMessagef(jcl, \"<presence to='%s' type='subscribed'/>\", who);\n\t\treturn true;\n\t}\n\telse if (!strcmp(what, \"pdeny\"))\n\t{\n\t\tif (jcl && jcl->status == JCL_ACTIVE)\n\t\t\tJCL_AddClientMessagef(jcl, \"<presence to='%s' type='unsubscribed'/>\", who);\n\t\treturn true;\n\t}\n#ifdef FILETRANSFERS\n\telse if (!strcmp(what, \"fauth\") && (jcl->enabledcapabilities & CAP_SIFT))\n\t{\n\t\tJCL_Info_ValueForKey(link, \"xmppsid\", what, sizeof(what));\n\t\tif (jcl && jcl->status == JCL_ACTIVE)\n\t\t\tXMPP_FT_AcceptFile(jcl, atoi(what), true);\n\t\treturn true;\n\t}\n\telse if (!strcmp(what, \"fdeny\") && (jcl->enabledcapabilities & CAP_SIFT))\n\t{\n\t\tJCL_Info_ValueForKey(link, \"xmppsid\", what, sizeof(what));\n\t\tif (jcl && jcl->status == JCL_ACTIVE)\n\t\t\tXMPP_FT_AcceptFile(jcl, atoi(what), false);\n\t\treturn true;\n\t}\n#endif\n#ifdef JINGLE\n\t//jauth/jdeny are used to accept/cancel all jingle/gingle content types.\n\telse if (!strcmp(what, \"jauth\") && (jcl->enabledcapabilities & (CAP_VOICE|CAP_VIDEO|CAP_GAMEINVITE|CAP_GOOGLE_VOICE)))\n\t{\n\t\tJCL_Info_ValueForKey(link, \"xmppsid\", what, sizeof(what));\n\t\tif (jcl && jcl->status == JCL_ACTIVE)\n\t\t\tJCL_Join(jcl, who, what, true, ICEP_INVALID);\n\t\tjclient_updatebuddylist = true;\n\t\treturn true;\n\t}\n\telse if (!strcmp(what, \"jdeny\") && (jcl->enabledcapabilities & (CAP_VOICE|CAP_VIDEO|CAP_GAMEINVITE|CAP_GOOGLE_VOICE)))\n\t{\n\t\tJCL_Info_ValueForKey(link, \"xmppsid\", what, sizeof(what));\n\t\tif (jcl && jcl->status == JCL_ACTIVE)\n\t\t\tJCL_Join(jcl, who, what, false, ICEP_INVALID);\n\t\tjclient_updatebuddylist = true;\n\t\treturn true;\n\t}\n\telse if (!strcmp(what, \"join\") && (jcl->enabledcapabilities & CAP_GAMEINVITE))\n\t{\n\t\tif (jcl && jcl->status == JCL_ACTIVE)\n\t\t\tJCL_Join(jcl, who, NULL, true, ICEP_QWCLIENT);\n\t\tjclient_updatebuddylist = true;\n\t\treturn true;\n\t}\n\telse if (!strcmp(what, \"invite\") && (jcl->enabledcapabilities & CAP_GAMEINVITE))\n\t{\n\t\tif (jcl && jcl->status == JCL_ACTIVE)\n\t\t\tJCL_Join(jcl, who, NULL, true, ICEP_QWSERVER);\n\t\tjclient_updatebuddylist = true;\n\t\treturn true;\n\t}\n\telse if (!strcmp(what, \"call\") && (jcl->enabledcapabilities & (CAP_VOICE|CAP_GOOGLE_VOICE)))\n\t{\n\t\tif (jcl && jcl->status == JCL_ACTIVE)\n\t\t\tJCL_Join(jcl, who, NULL, true, ICEP_VOICE);\n\t\tjclient_updatebuddylist = true;\n\t\treturn true;\n\t}\n\telse if (!strcmp(what, \"vidcall\") && (jcl->enabledcapabilities & (CAP_VIDEO)))\n\t{\n\t\tif (jcl && jcl->status == JCL_ACTIVE)\n\t\t\tJCL_Join(jcl, who, NULL, true, ICEP_VIDEO);\n\t\tjclient_updatebuddylist = true;\n\t\treturn true;\n\t}\n#endif\n\telse if (!strcmp(what, \"mucjoin\"))\n\t{\t//conference/chat join\n\t\tif (jcl)\n\t\t{\n\t\t\tJCL_Info_ValueForKey(link, \"xmppsid\", what, sizeof(what));\n\t\t\tJCL_JoinMUCChat(jcl, who, NULL, NULL, what);\n\t\t}\n\t}\n\telse if (!strcmp(what, \"addfriend\"))\n\t{\n\t\tif (jcl)\n\t\t{\n\t\t\tconfuncs->SetConsoleFloat(BUDDYLISTTITLE, \"linebuffered\", true);\n\t\t\tconfuncs->SetConsoleString(BUDDYLISTTITLE, \"footer\", \"Please enter your friend's account name\");\n\t\t\tjclient_action_cl = jcl;\n\t\t\tjclient_action_buddy = NULL;\n\t\t\tjclient_action = ACT_ADDFRIEND;\n\t\t}\n\t}\n\telse if (!strcmp(what, \"connect\"))\n\t{\n\t\tif (jcl && (jcl->status == JCL_INACTIVE || jcl->status == JCL_DEAD))\n\t\t{\n\t\t\tjcl->status = JCL_DEAD;\t//flag it as still trying to connect.\n\t\t\tjcl->timeout = jclient_curtime;\t//do it now\n\t\t}\n\t}\n\telse if (!strcmp(what, \"disconnect\"))\n\t{\n\t\tif (jcl)\n\t\t{\n\t\t\tif (jcl->status == JCL_INACTIVE)\n\t\t\t\tJCL_CloseConnection(jcl, \"\", false);\n\t\t\telse\n\t\t\t{\n\t\t\t\tJCL_CloseConnection(jcl, \"\", true);\n\t\t\t\tjcl->status = JCL_INACTIVE;\n\t\t\t}\n\t\t}\n\t}\n\telse if (!strcmp(what, \"forgetacc\"))\n\t{\n\t\tif (jcl)\n\t\t{\n\t\t\tJCL_CloseConnection(jcl, \"\", false);\n\t\t}\n\t}\n\telse if (!strcmp(what, \"newaccount\"))\n\t{\n\t\tconfuncs->SetConsoleFloat(BUDDYLISTTITLE, \"linebuffered\", true);\n\t\tconfuncs->SetConsoleString(BUDDYLISTTITLE, \"footer\", \"Please enter your XMPP account name\\neg: example@\"EXAMPLEDOMAIN);\n\t\tjclient_action_cl = jcl;\n\t\tjclient_action_buddy = NULL;\n\t\tjclient_action = ACT_NEWACCOUNT;\n\t}\n\telse if (!strcmp(what, \"setausername\"))\n\t{\n\t\tconfuncs->SetConsoleFloat(BUDDYLISTTITLE, \"linebuffered\", true);\n\t\tconfuncs->SetConsoleString(BUDDYLISTTITLE, \"footer\", \"Please enter your user name\");\n\t\tjclient_action_cl = jcl;\n\t\tjclient_action_buddy = NULL;\n\t\tjclient_action = ACT_SETAUSERNAME;\n\t}\n\telse if (!strcmp(what, \"setadomain\"))\n\t{\n\t\tconfuncs->SetConsoleFloat(BUDDYLISTTITLE, \"linebuffered\", true);\n\t\tconfuncs->SetConsoleString(BUDDYLISTTITLE, \"footer\", \"Please enter the domain for which your account is valid\");\n\t\tjclient_action_cl = jcl;\n\t\tjclient_action_buddy = NULL;\n\t\tjclient_action = ACT_SETADOMAIN;\n\t}\n\telse if (!strcmp(what, \"setaserver\"))\n\t{\n\t\tconfuncs->SetConsoleFloat(BUDDYLISTTITLE, \"linebuffered\", true);\n\t\tconfuncs->SetConsoleString(BUDDYLISTTITLE, \"footer\", \"Please enter the server to connect to.\");\n\t\tjclient_action_cl = jcl;\n\t\tjclient_action_buddy = NULL;\n\t\tjclient_action = ACT_SETASERVER;\n\t}\n\telse if (!strcmp(what, \"setaresource\"))\n\t{\n\t\tconfuncs->SetConsoleFloat(BUDDYLISTTITLE, \"linebuffered\", true);\n\t\tconfuncs->SetConsoleString(BUDDYLISTTITLE, \"footer\", \"Please enter some resource name.\");\n\t\tjclient_action_cl = jcl;\n\t\tjclient_action_buddy = NULL;\n\t\tjclient_action = ACT_SETARESOURCE;\n\t}\n\telse if (!strcmp(what, \"setapassword\"))\n\t{\n\t\tconfuncs->SetConsoleFloat(BUDDYLISTTITLE, \"linebuffered\", true);\n\t\tconfuncs->SetConsoleString(BUDDYLISTTITLE, \"footer\", \"Please enter your XMPP account name\\neg: example@\"EXAMPLEDOMAIN);\n\t\tjclient_action_cl = jcl;\n\t\tjclient_action_buddy = NULL;\n\t\tjclient_action = ACT_SETAPASSWORD;\n\t}\n\telse if (!strcmp(what, \"setasavepassword\"))\n\t\tjcl->savepassword = !jcl->savepassword;\n\telse if (!strcmp(what, \"accopts\"))\n\t{\n\t\tif (jcl)\n\t\t{\n\t\t\tchar footer[2048];\n\t\t\tchar link[512];\n\t\t\tif (jcl->status == JCL_INACTIVE)\n\t\t\t\tJCL_GenLink(jcl, link, sizeof(link), \"forgetacc\", NULL, NULL, NULL, \"%s\", \"Forget Account\");\n\t\t\telse if (jcl->status == JCL_DEAD)\n\t\t\t\tJCL_GenLink(jcl, link, sizeof(link), \"disconnect\", NULL, NULL, NULL, \"%s\", \"Disable\");\n\t\t\telse\n\t\t\t\tJCL_GenLink(jcl, link, sizeof(link), \"disconnect\", NULL, NULL, NULL, \"%s\", \"Disconnect\");\n\t\t\tQ_strlcpy(footer, \"\\n\", sizeof(footer));\n\t\t\tQ_strlcat(footer, link, sizeof(footer));\n\n\t\t\tif (jcl->status == JCL_INACTIVE)\n\t\t\t\tJCL_GenLink(jcl, link, sizeof(link), \"connect\", NULL, NULL, NULL, \"%s\", \"Connect\");\n\t\t\telse if (jcl->status == JCL_DEAD)\n\t\t\t\tJCL_GenLink(jcl, link, sizeof(link), \"connect\", NULL, NULL, NULL, \"%s\", \"Reconnect\");\n\t\t\telse if (jcl->status == JCL_ACTIVE)\n\t\t\t\tJCL_GenLink(jcl, link, sizeof(link), \"addfriend\", NULL, NULL, NULL, \"%s\", \"Add Friend\");\n\t\t\telse\n\t\t\t\t*link = 0;\n\n\t\t\tif (*link)\n\t\t\t{\n\t\t\t\tQ_strlcat(footer, \"\\n\", sizeof(footer));\n\t\t\t\tQ_strlcat(footer, link, sizeof(footer));\n\t\t\t}\n\n\t\t\tif (jcl->status == JCL_INACTIVE)\n\t\t\t{\n\t\t\t\tJCL_GenLink(jcl, link, sizeof(link), \"setausername\", NULL, NULL, NULL, \"Username: %s\", jcl->username);\n\t\t\t\tQ_strlcat(footer, \"\\n\", sizeof(footer));\n\t\t\t\tQ_strlcat(footer, link, sizeof(footer));\n\n\t\t\t\tJCL_GenLink(jcl, link, sizeof(link), \"setadomain\", NULL, NULL, NULL, \"Domain: %s\", jcl->domain);\n\t\t\t\tQ_strlcat(footer, \"\\n\", sizeof(footer));\n\t\t\t\tQ_strlcat(footer, link, sizeof(footer));\n\n\t\t\t\tJCL_GenLink(jcl, link, sizeof(link), \"setaserver\", NULL, NULL, NULL, \"Server: %s\", *jcl->serveraddr?jcl->serveraddr:\"<AUTO>\");\n\t\t\t\tQ_strlcat(footer, \"\\n\", sizeof(footer));\n\t\t\t\tQ_strlcat(footer, link, sizeof(footer));\n\n\t\t\t\tJCL_GenLink(jcl, link, sizeof(link), \"setaresource\", NULL, NULL, NULL, \"Resource: %s\", jcl->resource);\n\t\t\t\tQ_strlcat(footer, \"\\n\", sizeof(footer));\n\t\t\t\tQ_strlcat(footer, link, sizeof(footer));\n\t\t\t}\n//\t\t\tif (jcl->status == JCL_INACTIVE)\n\t\t\t{\n\t\t\t\tJCL_GenLink(jcl, link, sizeof(link), \"setapassword\", NULL, NULL, NULL, \"Password: %s\", jcl->sasl.password_plain);\n\t\t\t\tQ_strlcat(footer, \"\\n\", sizeof(footer));\n\t\t\t\tQ_strlcat(footer, link, sizeof(footer));\n\n\t\t\t\tJCL_GenLink(jcl, link, sizeof(link), \"setasavepassword\", NULL, NULL, NULL, \"Save Password: %s\", jcl->savepassword?\"true\":\"false\");\n\t\t\t\tQ_strlcat(footer, \"\\n\", sizeof(footer));\n\t\t\t\tQ_strlcat(footer, link, sizeof(footer));\n\t\t\t}\n\n\t\t\tconfuncs->SetConsoleString(BUDDYLISTTITLE, \"footer\", footer);\n\t\t}\n\t}\n\telse if (!strcmp(what, \"buddyopts\"))\n\t{\n\t\tchar footer[2048];\n\t\tchar chatlink[512];\n\t\tchar unfriend[512];\n\t\tchar realias[512];\n\t\tJCL_FindBuddy(jcl, *who?who:jcl->defaultdest, &b, NULL, false);\n\t\tif (b)\n\t\t{\n\t\t\tif (b->btype == BT_CHATROOM)\n\t\t\t{\n\t\t\t\tJCL_GenLink(jcl, chatlink, sizeof(chatlink), NULL, b->accountdomain, NULL, NULL, \"Chat in %s\", b->name);\n\t\t\t\tJCL_GenLink(jcl, unfriend, sizeof(unfriend), \"unfriend\", b->accountdomain, NULL, NULL, \"Forget %s\", b->name);\n\t\t\t\tJCL_GenLink(jcl, realias, sizeof(realias), \"setbalias\", b->accountdomain, NULL, NULL, \"Set alias for %s\", b->name);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tJCL_GenLink(jcl, chatlink, sizeof(chatlink), NULL, b->accountdomain, NULL, NULL, \"Chat with %s\", b->name);\n\t\t\t\tJCL_GenLink(jcl, unfriend, sizeof(unfriend), \"unfriend\", b->accountdomain, NULL, NULL, \"Unfriend %s\", b->name);\n\t\t\t\tJCL_GenLink(jcl, realias, sizeof(realias), \"setbalias\", b->accountdomain, NULL, NULL, \"Set alias for %s\", b->name);\n\t\t\t}\n\t\t\tQ_snprintf(footer, sizeof(footer), \"\\n%s\\n%s\\n%s\", chatlink, unfriend, realias);\n\n\t\t\tconfuncs->SetConsoleString(BUDDYLISTTITLE, \"footer\", footer);\n\t\t}\n\t}\n\telse if (!strcmp(what, \"unfriend\"))\n\t{\n\t\tJCL_FindBuddy(jcl, *who?who:jcl->defaultdest, &b, NULL, false);\n\t\tif (b)\n\t\t{\n\t\t\tif (b->btype == BT_CHATROOM)\n\t\t\t{\n\t\t\t\tJCL_AddClientMessagef(jcl, \"<presence to='%s/%s' type='unavailable'/>\", b->accountdomain, b->ourselves?b->ourselves->resource:\"\");\n\t\t\t\tJCL_ForgetBuddy(jcl, b, NULL);\n\t\t\t}\n\t\t\telse if (b->btype == BT_ROSTER)\n\t\t\t{\n\t\t\t\t//hide from em\n\t\t\t\tJCL_AddClientMessagef(jcl, \"<presence to='%s' type='unsubscribed'/>\", b->accountdomain);\n\n\t\t\t\t//stop looking for em\n\t\t\t\tJCL_AddClientMessagef(jcl, \"<presence to='%s' type='unsubscribe'/>\", b->accountdomain);\n\n\t\t\t\t//stop listing em\n\t\t\t\tJCL_SendIQf(jcl, NULL, \"set\", NULL, \"<query xmlns='jabber:iq:roster'><item jid='%s' subscription='remove' /></query>\", b->accountdomain);\n//\t\t\t\tb->btype = BT_UNKNOWN;\n\t\t\t\tJCL_ForgetBuddy(jcl, b, NULL);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tJCL_AddClientMessagef(jcl, \"<presence to='%s' type='unavailable'/>\", b->accountdomain);\n\t\t\t\tJCL_ForgetBuddy(jcl, b, NULL);\n\t\t\t}\n\t\t}\n\t}\n\telse if (!strcmp(what, \"setbalias\"))\n\t{\n\t\tJCL_FindBuddy(jcl, *who?who:jcl->defaultdest, &b, NULL, false);\n\t\tif (b)\n\t\t{\n\t\t\tconfuncs->SetConsoleFloat(BUDDYLISTTITLE, \"linebuffered\", true);\n\t\t\tconfuncs->SetConsoleString(BUDDYLISTTITLE, \"footer\", \"Please enter what you want to see them as\");\n\t\t\tjclient_action_cl = jcl;\n\t\t\tjclient_action_buddy = b;\n\t\t\tjclient_action = ACT_SETBALIAS;\n\t\t}\n\t}\n\telse if ((*who && !*what) || !strcmp(what, \"msg\"))\n\t{\n\t\tif (jcl)\n\t\t{\n\t\t\tchar *f;\n\t\t\tbuddy_t *b = NULL;\n\t\t\tbresource_t *br = NULL;\n\t\t\tJCL_FindBuddy(jcl, *who?who:jcl->defaultdest, &b, &br, true);\n\t\t\tif (b)\n\t\t\t{\n\t\t\t\tf = b->accountdomain;\n\t\t\t\tif (b->btype != BT_CHATROOM)\n\t\t\t\t\tb->defaultresource = br;\n\n\t\t\t\tif (confuncs)\n\t\t\t\t{\n\t\t\t\t\tconfuncs->SetConsoleString(f, \"title\", b->name);\n\t\t\t\t\tconfuncs->SetConsoleFloat(f, \"iswindow\", true);\n\t\t\t\t\tconfuncs->SetConsoleFloat(f, \"forceutf8\", true);\n\t\t\t\t\tconfuncs->SetConsoleFloat(f, \"wnd_w\", 256);\n\t\t\t\t\tconfuncs->SetConsoleFloat(f, \"wnd_h\", 320);\n\t\t\t\t}\n\t\t\t\tif (confuncs)\n\t\t\t\t\tconfuncs->SetActive(f);\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\telse\n\t{\n\t\tCon_Printf(\"Unsupported xmpp action (%s) in link\\n\", what);\n\t}\n\n\treturn false;\n}\n\nvoid JCL_ToJID(jclient_t *jcl, const char *in, char *out, int outsize, qboolean assumeresource)\n{\n\t//decompose links first\n\tif (in[0] == '^' && in[1] == '[')\n\t{\n\t\tconst char *sl;\n\t\tconst char *le;\n\t\tsl = in+2;\n\t\tsl = strchr(in, '\\\\');\n\t\tif (sl)\n\t\t{\n\t\t\tle = strstr(sl, \"^]\");\n\t\t\tif (le)\n\t\t\t{\n\t\t\t\tchar info[512];\n\t\t\t\tif (le-sl < 512)\n\t\t\t\t{\n\t\t\t\t\tmemcpy(info, sl, le-sl);\n\t\t\t\t\tinfo[le-sl] = 0;\n\t\t\t\t\tJCL_Info_ValueForKey(info, \"xmpp\", out, outsize);\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n\n\tif (!strchr(in, '@') && jcl)\n\t{\n\t\t//no @? probably its an alias, but could also be a server/domain perhaps. not sure we care. you'll just have to rename your friend.\n\t\t//check to see if we can find a friend going by that name\n\t\t//fixme: allow resources to make it through here\n\t\tbuddy_t *b;\n\t\tfor (b = jcl->buddies; b; b = b->next)\n\t\t{\n\t\t\tif (!strcasecmp(b->name, in))\n\t\t\t{\n\t\t\t\tif (b->defaultresource && assumeresource)\n\t\t\t\t\tQ_snprintf(out, outsize, \"%s/%s\", b->accountdomain, b->defaultresource->resource);\n\t\t\t\telse\n\t\t\t\t\tQ_strlcpy(out, b->accountdomain, outsize);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n\t\n\tif (assumeresource)\n\t{\n\t\tbuddy_t *b;\n\t\tfor (b = jcl->buddies; b; b = b->next)\n\t\t{\n\t\t\tif (!strcasecmp(b->accountdomain, in))\n\t\t\t{\n\t\t\t\tif (b->defaultresource && assumeresource)\n\t\t\t\t\tQ_snprintf(out, outsize, \"%s/%s\", b->accountdomain, b->defaultresource->resource);\n\t\t\t\telse\n\t\t\t\t\tQ_strlcpy(out, b->accountdomain, outsize);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n\n\t//a regular jabber account name\n\tQ_strlcpy(out, in, outsize);\n}\n\nvoid XMPP_AddFriend(jclient_t *jcl, const char *account, const char *nick)\n{\n\tchar jid[256];\n\t//FIXME: validate the name. deal with xml markup.\n\t//try and make sense of the name given\n\tJCL_ToJID(jcl, account, jid, sizeof(jid), false);\n\tif (!strchr(jid, '@'))\n\t\tCon_Printf(\"Missing @ character. Trying anyway, but this will be assumed to be a server rather than a user.\\n\");\n\n\t//can also rename. We should probably read back the groups for the update.\n\tJCL_SendIQf(jcl, NULL, \"set\", NULL, \"<query xmlns='jabber:iq:roster'><item jid='%s' name='%s'></item></query>\", jid, nick);\n\n\t//start looking for em\n\tJCL_AddClientMessagef(jcl, \"<presence to='%s' type='subscribe'/>\", jid);\n\n\t//let em see us\n\tif (jcl->preapproval)\n\t\tJCL_AddClientMessagef(jcl, \"<presence to='%s' type='subscribed'/>\", jid);\n}\njclient_t *JCL_Connect(int accnum, char *server, int forcetls, char *account, char *password);\nint JCL_ConExecuteCommand(qboolean isinsecure)\n{\n\tbuddy_t *b;\n\tchar consolename[256];\n\tjclient_t *jcl;\n\tint i;\n\n\tcmdfuncs->Argv(0, consolename, sizeof(consolename));\n\n\tif (!strcmp(consolename, BUDDYLISTTITLE))\n\t{\n\t\tchar args[512];\n\t\tcmdfuncs->Args(args, sizeof(args));\n\t\tconfuncs->SetConsoleFloat(BUDDYLISTTITLE, \"linebuffered\", false);\n\t\tconfuncs->SetConsoleString(BUDDYLISTTITLE, \"footer\", \"\");\n\t\tjclient_updatebuddylist = true;\n\n\t\t//FIXME: validate that the client is still active.\n\t\tswitch(jclient_action)\n\t\t{\n\t\tcase ACT_NONE:\n\t\t\tbreak;\n\t\tcase ACT_OAUTH:\n\t\t\tjcl = jclient_action_cl;\n\t\t\tif (jcl)\n\t\t\t{\n\t\t\t\tfree(jcl->sasl.oauth2.authtoken);\n\t\t\t\tjcl->sasl.oauth2.authtoken = strdup(args);\n\t\t\t\tif (jcl->status == JCL_INACTIVE)\n\t\t\t\t\tjcl->status = JCL_DEAD;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase ACT_NEWACCOUNT:\n\t\t\tif (!*args)\n\t\t\t\tbreak;\t//they didn't enter anything! oh well.\n\t\t\tfor (i = 0; i < sizeof(jclients)/sizeof(jclients[0]); i++)\n\t\t\t{\n\t\t\t\tif (jclients[i])\n\t\t\t\t\tcontinue;\n\t\t\t\tjclients[i] = JCL_Connect(i, \"\", 1, args, \"\");\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (i == sizeof(jclients)/sizeof(jclients[0]))\n\t\t\t\tconfuncs->SetConsoleString(BUDDYLISTTITLE, \"footer\", \"Too many accounts open\");\n\t\t\telse if (!jclients[i])\n\t\t\t\tconfuncs->SetConsoleString(BUDDYLISTTITLE, \"footer\", \"Unable to create account\");\n\t\t\telse\n\t\t\t{\t//now ask for a password instantly. because oauth2 is basically unusable.\n\t\t\t\tjclients[i]->status = JCL_INACTIVE;\n\t\t\t\tconfuncs->SetConsoleFloat(BUDDYLISTTITLE, \"linebuffered\", true);\n\t\t\t\tconfuncs->SetConsoleString(BUDDYLISTTITLE, \"footer\", \"Please enter password\");\n\t\t\t\tjclient_action_cl = jclients[i];\n\t\t\t\tjclient_action_buddy = NULL;\n\t\t\t\tjclient_action = ACT_SETAPASSWORD;\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase ACT_SETAUSERNAME:\n\t\t\tQ_strncpyz(jclient_action_cl->username, args, sizeof(jclient_action_cl->username));\n\t\t\tbreak;\n\t\tcase ACT_SETADOMAIN:\n\t\t\tQ_strncpyz(jclient_action_cl->domain, args, sizeof(jclient_action_cl->domain));\n\t\t\tbreak;\n\t\tcase ACT_SETASERVER:\n\t\t\tQ_strncpyz(jclient_action_cl->serveraddr, args, sizeof(jclient_action_cl->serveraddr));\n\t\t\tbreak;\n\t\tcase ACT_SETARESOURCE:\n\t\t\tQ_strncpyz(jclient_action_cl->resource, args, sizeof(jclient_action_cl->resource));\n\t\t\tbreak;\n\t\tcase ACT_SETAPASSWORD:\n\t\t\tif (*args)\n\t\t\t{\n\t\t\t\tQ_strncpyz(jclient_action_cl->sasl.password_plain, args, sizeof(jclient_action_cl->sasl.password_plain));\n\t\t\t\tjclient_action_cl->sasl.password_hash_size = 0; //invalidate it\n\t\t\t}\n\t\t\tif (jclient_action_cl->status == JCL_INACTIVE)\n\t\t\t\tjclient_action_cl->status = JCL_DEAD;\n\t\t\tjclient_action = ACT_NONE;\n\t\t\tjclient_configdirty = true;\n\t\t\treturn 2;\t//ask to not store in history.\n\t\tcase ACT_ADDFRIEND:\n\t\t\tif (*args)\n\t\t\t\tXMPP_AddFriend(jclient_action_cl, args, \"\");\n\t\t\tbreak;\n\t\tcase ACT_SETBALIAS:\n\t\t\tjclient_configdirty = true;\n\t\t\tQ_strncpyz(jclient_action_buddy->name, args, sizeof(jclient_action_buddy->name));\n\t\t\tif (jclient_action_buddy->btype == BT_ROSTER)\n\t\t\t\tJCL_SendIQf(jclient_action_cl, NULL, \"set\", NULL, \"<query xmlns='jabber:iq:roster'><item jid='%s' name='%s'></item></query>\", jclient_action_buddy->accountdomain, jclient_action_buddy->name);\n\t\t\tif (confuncs)\n\t\t\t\tconfuncs->SetConsoleString(jclient_action_buddy->accountdomain, \"title\", jclient_action_buddy->name);\n\t\t\tbreak;\n\t\t}\n\t\tjclient_action = ACT_NONE;\n\t\treturn true;\n\t}\n\n\tfor (i = 0; i < sizeof(jclients) / sizeof(jclients[0]); i++)\n\t{\n\t\tjcl = jclients[i];\n\t\tif (!jcl)\n\t\t\tcontinue;\n\t\tfor (b = jcl->buddies; b; b = b->next)\n\t\t{\n\t\t\tif (!strcmp(b->accountdomain, consolename))\n\t\t\t{\n\t\t\t\tif (b->defaultresource)\n\t\t\t\t\tQ_snprintf(jcl->defaultdest, sizeof(jcl->defaultdest), \"%s/%s\", b->accountdomain, b->defaultresource->resource);\n\t\t\t\telse\n\t\t\t\t\tQ_snprintf(jcl->defaultdest, sizeof(jcl->defaultdest), \"%s\", b->accountdomain);\n\t\t\t\tJCL_Command(i, consolename);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t}\n\tfor (i = 0; i < sizeof(jclients) / sizeof(jclients[0]); i++)\n\t{\n\t\tjcl = jclients[i];\n\t\tif (!jcl)\n\t\t\tcontinue;\n\t\tJCL_Command(i, consolename);\n\t\treturn true;\n\t}\n\n\tCon_SubPrintf(consolename, \"You were disconnected\\n\");\n\treturn true;\n}\n\nvoid JCL_FlushOutgoing(jclient_t *jcl)\n{\n\tint sent;\n\tif (!jcl || !jcl->outbuflen || jcl->socket == -1)\n\t\treturn;\n\n\tsent = netfuncs->Send(jcl->socket, jcl->outbuf + jcl->outbufpos, jcl->outbuflen);\t//FIXME: This needs rewriting to cope with errors.\n\tif (sent > 0)\n\t{\n\t\t//and print it on some subconsole if we're debugging\n\t\tif (jcl->streamdebug)\n\t\t{\n\t\t\tchar t = jcl->outbuf[jcl->outbufpos+sent];\n\t\t\tjcl->outbuf[jcl->outbufpos+sent] = 0;\n\t\t\tXMPP_ConversationPrintf(\"xmppout\", \"xmppout\", false, \"^3%s\\n\", jcl->outbuf + jcl->outbufpos);\n\t\t\tjcl->outbuf[jcl->outbufpos+sent] = t;\n\t\t}\n\n\t\tjcl->outbufpos += sent;\n\t\tjcl->outbuflen -= sent;\n\t}\n\telse if (sent < 0)\n\t\tCon_Printf(\"XMPP: Error sending\\n\");\n//\telse\n//\t\tCon_Printf(\"Unable to send anything\\n\");\n}\nvoid JCL_AddClientMessage(jclient_t *jcl, const char *msg, int datalen)\n{\n\t//handle overflows\n\tif (jcl->outbufpos+jcl->outbuflen+datalen > jcl->outbufmax)\n\t{\n\t\tif (jcl->outbuflen+datalen <= jcl->outbufmax)\n\t\t{\n\t\t\t//can get away with just moving the data\n\t\t\tmemmove(jcl->outbuf, jcl->outbuf + jcl->outbufpos, jcl->outbuflen);\n\t\t\tjcl->outbufpos = 0;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t//need to expand the buffer.\n\t\t\tint newmax = (jcl->outbuflen+datalen)*2;\n\t\t\tchar *newbuf;\n\n\t\t\tif (newmax < jcl->outbuflen)\n\t\t\t\tnewbuf = NULL;\t//eep... some special kind of evil overflow.\n\t\t\telse\n\t\t\t\tnewbuf = malloc(newmax+1);\n\n\t\t\tif (newbuf)\n\t\t\t{\n\t\t\t\tmemcpy(newbuf, jcl->outbuf + jcl->outbufpos, jcl->outbuflen);\n\t\t\t\tjcl->outbufmax = newmax;\n\t\t\t\tjcl->outbufpos = 0;\n\t\t\t\tjcl->outbuf = newbuf;\n\t\t\t}\n\t\t\telse\n\t\t\t\tdatalen = 0;\t//eep!\n\t\t}\n\t}\n\t//and write our data to it\n\tmemcpy(jcl->outbuf + jcl->outbufpos + jcl->outbuflen, msg, datalen);\n\tjcl->outbuflen += datalen;\n\n\t//try and flush it now\n\tJCL_FlushOutgoing(jcl);\n}\nvoid JCL_AddClientMessageString(jclient_t *jcl, const char *msg)\n{\n\tJCL_AddClientMessage(jcl, msg, strlen(msg));\n}\nvoid JCL_AddClientMessagef(jclient_t *jcl, const char *fmt, ...)\n{\n\tva_list\t\targptr;\n\tchar body[4096];\n\n\tva_start (argptr, fmt);\n\tQ_vsnprintxf (body, sizeof(body), fmt, argptr);\n\tva_end (argptr);\n\n\tJCL_AddClientMessageString(jcl, body);\n}\nqboolean JCL_Reconnect(jclient_t *jcl)\n{\n\tchar *serveraddr;\n\t//destroy any data that never got sent\n\tfree(jcl->outbuf);\n\tjcl->outbuf = NULL;\n\tjcl->outbuflen = 0;\n\tjcl->outbufpos = 0;\n\tjcl->outbufmax = 0;\n\tjcl->instreampos = 0;\n\tjcl->bufferedinammount = 0;\n\tQ_strlcpy(jcl->localalias, \">>\", sizeof(jcl->localalias));\n\tjcl->sasl.authmethod = NULL;\n\n\tif (*jcl->redirserveraddr)\n\t\tserveraddr = jcl->redirserveraddr;\n\telse\n\t\tserveraddr = jcl->serveraddr;\n\n\tif (!*serveraddr)\n\t{\n\t\t//jcl->tlsconnect requires an explicit hostname, so should not be able to take this path.\n\t\tchar srv[256];\n\t\tchar srvserver[256];\n\t\tif (!Q_snprintfz(srv, sizeof(srv), \"_xmpp-client._tcp.%s\", jcl->domain) && NET_DNSLookup_SRV(srv, srvserver, sizeof(srvserver)))\n\t\t{\n\t\t\tCon_DPrintf(\"XMPP: Trying to connect to %s (%s)\\n\", jcl->domain, srvserver);\n\t\t\tjcl->socket = netfuncs->TCPConnect(srvserver, jcl->serverport);\t//port is should already be part of the srvserver name\n\t\t}\n\t\telse\n\t\t{\n\t\t\t//SRV lookup failed. attempt to just use the domain directly.\n\t\t\tCon_DPrintf(\"XMPP: Trying to connect to %s\\n\", jcl->domain);\n\t\t\tjcl->socket = netfuncs->TCPConnect(jcl->domain, jcl->serverport);\t//port is only used if the url doesn't contain one. It's a default.\n\t\t}\n\t}\n\telse\n\t{\n\t\tCon_DPrintf(\"XMPP: Trying to connect to %s\\n\", jcl->domain);\n\t\tjcl->socket = netfuncs->TCPConnect(serveraddr, jcl->serverport);\t//port is only used if the url doesn't contain one. It's a default.\n\t}\n\n\t//not yet blocking. So no frequent attempts please...\n\t//non blocking prevents connect from returning worthwhile sensible value.\n\tif ((int)jcl->socket < 0)\n\t{\n\t\tQ_strncpyz(jcl->errormsg, \"Unable to connect\", sizeof(jcl->errormsg));\n\t\treturn false;\n\t}\n\n\tjcl->issecure = false;\n\tif (jcl->forcetls==2)\n\t\tif (netfuncs->SetTLSClient(jcl->socket, jcl->certificatedomain)>=0)\n\t\t\tjcl->issecure = true;\n\n\tjcl->status = JCL_AUTHING;\n\tjcl->connecting = true;\n\n\tJCL_AddClientMessageString(jcl,\n\t\t\"<?xml version='1.0' ?>\"\n\t\t\"<stream:stream to='\");\n\tJCL_AddClientMessageString(jcl, jcl->domain);\n\tJCL_AddClientMessageString(jcl, \"' xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' version='1.0'>\");\n\n\tjclient_updatebuddylist = true;\n\n\treturn true;\n}\n\njclient_t *JCL_ConnectXML(xmltree_t *acc)\n{\n\t//this is a list of xmpp networks that use oauth2 for which we have a clientid registered. this allows us to get the right oauth defaults.\n\tstruct \n\t{\n\t\tchar *domain;\n\t\tchar *saslmethod;\n\t\tchar *obtainurl;\n\t\tchar *refreshurl;\n\t\tchar *revokeurl;\n\t\tchar *scope;\n\t\tchar *clientid;\n\t\tchar *clientsecret;\n\t\t//char *signouturl;\n\t\t//char *clientregistrationurl;\n\t} *oa, dfltoauth[] = {\n\t\t{\n\t\t\t\"gmail.com\",\n\t\t\t\"X-OAUTH2\",\n\t\t\t\"https://accounts.google.com/o/oauth2/auth\",\t//authorize\n\t\t\t\"https://accounts.google.com/o/oauth2/token\",\t//refresh\n\t\t\t\"https://accounts.google.com/o/oauth2/revoke\",\t//revoke\n\t\t\t\"https://www.googleapis.com/auth/googletalk\",\t//scope\n\t\t\t\"1060926168015.apps.googleusercontent.com\",\t\t//clientid\n\t\t\t\"mptCRRTE5I626npsnoZ_RqoG\"\t\t\t\t\t\t//client secrit. there really is no securing this. I'll just have to avoid any pay-for google apis. *shrug*\n\t\t\t//\"https://accounts.google.com/IssuedAuthSubTokens\"\n\t\t\t//\"https://code.google.com/apis/console\"\n\t\t},\n/*\n\t\t{\n\t\t\t\"messenger.live.com\",\n\t\t\t\"X-MESSENGER-OAUTH2\",\n\t\t\t\"https://oauth.live.com/authorize\",\n\t\t\t\"https://oauth.live.com/token\",\n\t\t\t\"\",\t//FIXME fill in revoke url\n\t\t\t\"wl.messenger,wl.basic,wl.offline_access,wl.contacts_create,wl.share\",\t//no idea what's actually needed.\n\t\t\t\"\",\t//client-id - none registered. go register it yourself.\n\t\t\t\"\"\t//client-secret - client not registered, go do it yourself.\n\t\t\t//\"\"\n\t\t\t//\"https://manage.dev.live.com/\"\n\t\t},\n*/\n/*\n\t\t{\n\t\t\t\"messenger.live.com\",\n\t\t\t\"X-FACEBOOK-PLATFORM\",\n\t\t\t\"\", //FIXME fill in\tobtain url\n\t\t\t\"\",\t//FIXME fill in\trefresh url\n\t\t\t\"\",\t//FIXME fill in revoke url\n\t\t\t\"\",\t//FIXME: fill in scope\n\t\t\t\"\",\t//client-id - none registered. go register it yourself.\n\t\t\t\"\"\t//client-secret - client not registered, go do it yourself.\n\t\t\t//\"\"\n\t\t\t//\"\"\n\t\t},\n*/\n\t\t{\n\t\t\tNULL,\n\t\t\t\"\",\n\t\t\t\"\",\n\t\t\t\"\",\n\t\t\t\"\",\n\t\t\t\"\",\n\t\t\t\"\",\n\t\t\t\"\"\n\t\t}\n\t};\n\tjclient_t *jcl;\n\txmltree_t *oauth2;\n\txmltree_t *features;\n\tchar oauthname[512];\n\tstruct buddyinfo_s *bi;\n\tint bn;\n\n\tjcl = malloc(sizeof(jclient_t));\n\tif (!jcl)\n\t\treturn NULL;\n\n\tmemset(jcl, 0, sizeof(jclient_t));\n\tjcl->socket = -1;\n\tjcl->rcon_pipe = -1;\n\n\tjcl->enabledcapabilities = CAP_DEFAULTENABLEDCAPS;\n\n\tjcl->accountnum = atoi(XML_GetParameter(acc, \"id\", \"1\"));\n\n\t//make sure dependant properties are listed beneath their dependancies...\n\tjcl->forcetls = atoi(XML_GetChildBody(acc, \"forcetls\", \"1\"));\n\tjcl->streamdebug = atoi(XML_GetChildBody(acc, \"streamdebug\", \"0\"));\n\tjcl->streamdebug = bound(0, jcl->streamdebug, 2);\n\tQ_strlcpy(jcl->serveraddr, XML_GetChildBody(acc, \"serveraddr\", \"\"), sizeof(jcl->serveraddr));\n\tjcl->serverport = atoi(XML_GetChildBody(acc, \"serverport\", (jcl->forcetls==2)?\"5223\":\"5222\"));\n\tQ_strlcpy(jcl->username, XML_GetChildBody(acc, \"username\", \"user\"), sizeof(jcl->username));\n\tQ_strlcpy(jcl->domain, XML_GetChildBody(acc, \"domain\", \"localhost\"), sizeof(jcl->domain));\n\tQ_strlcpy(jcl->resource, XML_GetChildBody(acc, \"resource\", \"\"), sizeof(jcl->resource));\n\n\t//half of these networks seem to have weird domains. especially microsoft.\n\tif (strchr(jcl->username, '@'))\n\t\tQ_strlcpy(oauthname, jcl->username, sizeof(oauthname));\n\telse\n\t\tQ_snprintf(oauthname, sizeof(oauthname), \"%s@%s\", jcl->username, jcl->domain);\n\tfor (oa = dfltoauth; oa->domain; oa++)\n\t{\n\t\tif (!strcmp(oa->domain, jcl->domain))\n\t\t\tbreak;\n\t}\n\toauth2 = XML_ChildOfTree(acc, \"oauth2\", 0);\n\tQ_strlcpy(jcl->sasl.oauth2.saslmethod, XML_GetParameter(oauth2, \"method\", oa->saslmethod), sizeof(jcl->sasl.oauth2.saslmethod));\n\tQ_strlcpy(jcl->sasl.oauth2.obtainurl, XML_GetChildBody(oauth2, \"obtain-url\", oa->obtainurl), sizeof(jcl->sasl.oauth2.obtainurl));\n\tQ_strlcpy(jcl->sasl.oauth2.refreshurl, XML_GetChildBody(oauth2, \"refresh-url\", oa->refreshurl), sizeof(jcl->sasl.oauth2.refreshurl));\n\tQ_strlcpy(jcl->sasl.oauth2.clientid, XML_GetChildBody(oauth2, \"client-id\", oa->clientid), sizeof(jcl->sasl.oauth2.clientid));\n\tQ_strlcpy(jcl->sasl.oauth2.clientsecret, XML_GetChildBody(oauth2, \"client-secret\", oa->clientsecret), sizeof(jcl->sasl.oauth2.clientsecret));\n\tjcl->sasl.oauth2.scope = strdup(XML_GetChildBody(oauth2, \"scope\", oa->scope));\n\tjcl->sasl.oauth2.useraccount = strdup(XML_GetChildBody(oauth2, \"authname\", oauthname));\n\tjcl->sasl.oauth2.authtoken = strdup(XML_GetChildBody(oauth2, \"auth-token\", \"\"));\n\tjcl->sasl.oauth2.refreshtoken = strdup(XML_GetChildBody(oauth2, \"refresh-token\", \"\"));\n\tjcl->sasl.oauth2.accesstoken = strdup(XML_GetChildBody(oauth2, \"access-token\", \"\"));\n\n\tQ_strlcpy(jcl->sasl.password_plain, XML_GetChildBody(acc, \"password\", \"\"), sizeof(jcl->sasl.password_plain));\n\n\tif (!*jcl->resource)\n\t{\t//the default resource matches the game that they're trying to play.\n\t\tchar gamename[64], *res, *o;\n\t\tif (cvarfuncs->GetString(\"fs_gamename\", gamename, sizeof(gamename)))\n\t\t{\n\t\t\t//strip out any weird chars (namely whitespace)\n\t\t\tfor (o = gamename, res = gamename; *res; )\n\t\t\t{\n\t\t\t\tif (*res == ' ' || *res == '\\t')\n\t\t\t\t\tres++;\n\t\t\t\telse\n\t\t\t\t\t*o++ = *res++;\n\t\t\t}\n\t\t\tif (!*gamename)\n\t\t\t\tQ_strlcpy(jcl->resource, \"FTE\", sizeof(jcl->resource));\n\t\t\telse\n\t\t\t\tQ_strlcpy(jcl->resource, gamename, sizeof(jcl->resource));\n\t\t}\n\t}\n\tQ_strlcpy(jcl->certificatedomain, XML_GetChildBody(acc, \"certificatedomain\", jcl->domain), sizeof(jcl->certificatedomain));\n\tjcl->status = atoi(XML_GetChildBody(acc, \"inactive\", \"0\"))?JCL_INACTIVE:JCL_DEAD;\n\tjcl->sasl.allowauth_plainnontls\t= atoi(XML_GetChildBody(acc, \"allowauth_plain_nontls\",\t\"0\"));\n\tjcl->sasl.allowauth_plaintls\t\t= atoi(XML_GetChildBody(acc, \"allowauth_plain_tls\",\t\t\"1\"));\t//required 1 for googletalk, otherwise I'd set it to 0.\n\tjcl->sasl.allowauth_digestmd5\t= atoi(XML_GetChildBody(acc, \"allowauth_digest_md5\",\t\"1\"));\n\tjcl->sasl.allowauth_scramsha1\t= atoi(XML_GetChildBody(acc, \"allowauth_scram_sha_1\",\t\"1\"));\n\tjcl->sasl.allowauth_oauth2\t\t= atoi(XML_GetChildBody(acc, \"allowauth_oauth2\",\t\t*jcl->sasl.oauth2.saslmethod?\"1\":\"0\"));\n\n\tjcl->savepassword\t= atoi(XML_GetChildBody(acc, \"savepassword\",\t\"0\"));\n\n\tfeatures = XML_ChildOfTree(acc, \"features\", 0);\n\tif (features && XML_GetParameter(features, \"ver\", JCL_BUILD))\n\t{\n\t\tconst char *val;\n\t\tint j;\n\t\tfor (j = 0; capnames[j].names; j++)\n\t\t{\n\t\t\tval = XML_GetChildBody(features, capnames[j].names, NULL);\n\t\t\tif (val)\n\t\t\t{\n\t\t\t\tif (atoi(val))\n\t\t\t\t\tjcl->enabledcapabilities |= capnames[j].cap;\n\t\t\t\telse\n\t\t\t\t\tjcl->enabledcapabilities &= ~capnames[j].cap;\n\t\t\t}\n\t\t}\n\t}\n\n\n\t//parse chatrooms\n\tfeatures = XML_ChildOfTree(acc, \"chats\", 0);\n\tif (features)\n\t{\n\t\tfor (bn=0; ; bn++)\n\t\t{\n\t\t\txmltree_t *b = XML_ChildOfTree(features, \"room\", bn);\n\t\t\tif (b)\n\t\t\t{\n\t\t\t\tbuddy_t *room;\n\t\t\t\tconst char *jid = XML_GetParameter(b, \"name\", \"\");\n\t\t\t\tconst char *alias = XML_GetParameter(b, \"alias\", jid);\n\t\t\t\tconst char *topic = XML_GetParameter(b, \"topic\", NULL);\n\t\t\t\tconst char *nick = XML_GetParameter(b, \"nick\", NULL);\n\t\t\t\tconst char *password = XML_GetParameter(b, \"password\", NULL);\n\t\t\t\tqboolean autojoin = atoi(XML_GetParameter(b, \"autojoin\", \"1\"));\n\n\t\t\t\tif (JCL_FindBuddy(jcl, jid, &room, NULL, true) && room)\n\t\t\t\t{\n\t\t\t\t\troom->btype = BT_CHATROOM;\n\t\t\t\t\tQ_strlcpy(room->name, alias, sizeof(room->name));\n\t\t\t\t\troom->room_topic = topic?strdup(topic):NULL;\n\t\t\t\t\troom->room_nick = nick?strdup(nick):NULL;\n\t\t\t\t\troom->room_password = password?strdup(password):NULL;\n\t\t\t\t\troom->room_autojoin = autojoin;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t\tbreak;\n\t\t}\n\t}\n\telse\n\t{\n\t\t/*buddy_t *room;\n\t\tconst char *jid = \"quake@somedomain\";\n\t\tconst char *alias = \"FTE Users\";\n\t\tconst char *topic = NULL;\n\t\tconst char *nick = NULL;\n\t\tconst char *password = NULL;\n\n\t\tif (JCL_FindBuddy(jcl, jid, &room, NULL, true) && room)\n\t\t{\n\t\t\troom->btype = BT_CHATROOM;\n\t\t\tQ_strlcpy(room->name, alias, sizeof(room->name));\n\t\t\troom->room_topic = topic?strdup(topic):NULL;\n\t\t\troom->room_nick = nick?strdup(nick):NULL;\n\t\t\troom->room_password = password?strdup(password):NULL;\n\t\t}*/\n\t}\n\n\n\tfeatures = XML_ChildOfTree(acc, \"buddyinfo\", 0);\n\tfor (bn=0; ; bn++)\n\t{\n\t\txmltree_t *b = XML_ChildOfTree(features, \"buddy\", bn);\n\t\tif (b)\n\t\t{\n\t\t\tconst char *buddyid = XML_GetParameter(b, \"name\", JCL_BUILD);\n\t\t\tconst char *buddyimage = XML_GetChildBody(b, \"image\", NULL);\n\t\t\tconst char *buddyimagemime = XML_GetChildBody(b, \"imagemime\", NULL);\n\t\t\tconst char *buddyimagehash = XML_GetChildBody(b, \"imagehash\", NULL);\n\n\t\t\tbi = malloc(sizeof(*bi) + strlen(buddyid));\n\t\t\tstrcpy(bi->accountdomain, buddyid);\n\t\t\tbi->image = buddyimage?strdup(buddyimage):NULL;\n\t\t\tbi->imagemime = buddyimagemime?strdup(buddyimagemime):NULL;\n\t\t\tbi->imagehash = buddyimagehash?strdup(buddyimagehash):NULL;\n\t\t\tbi->next = jcl->buddyinfo;\n\t\t\tjcl->buddyinfo = bi;\n\t\t}\n\t\telse\n\t\t\tbreak;\n\t}\n\n\treturn jcl;\n}\n\njclient_t *JCL_Connect(int accnum, char *server, int forcetls, char *account, char *password)\n{\n\tchar gamename[64];\n\tjclient_t *jcl;\n\tchar *domain;\n\tchar *res;\n\txmltree_t *x;\n\n\tres = TrimResourceFromJid(account);\n\tif (!res || !*res)\n\t{\n\t\t//the default resource matches the game that they're trying to play.\n\t\tif (cvarfuncs->GetString(\"fs_gamename\", gamename, sizeof(gamename)))\n\t\t{\n\t\t\t//strip out any weird chars (namely whitespace)\n\t\t\tchar *o;\n\t\t\tfor (o = gamename, res = gamename; *res; )\n\t\t\t{\n\t\t\t\tif (*res == ' ' || *res == '\\t')\n\t\t\t\t\tres++;\n\t\t\t\telse\n\t\t\t\t\t*o++ = *res++;\n\t\t\t}\n\t\t\tres = gamename;\n\t\t}\n\t}\n\n/*\n\tif (forcetls>0)\n\t{\n\t\tif (!BUILTINISVALID(Net_SetTLSClient))\n\t\t{\n\t\t\tCon_Printf(\"XMPP: TLS is not supported\\n\");\n\t\t\treturn NULL;\n\t\t}\n\t}\n*/\n\n\tdomain = strchr(account, '@');\n\tif (domain)\n\t{\n\t\t*domain = '\\0';\n\t\tdomain++;\n\t}\n\telse\n\t{\n#ifdef DEFAULTDOMAIN\n\t\tdomain = DEFAULTDOMAIN;\n\t\tCon_Printf(\"XMPP: domain not specified, assuming %s\\n\", domain);\n#else\n\t\tCon_Printf(\"XMPP: domain not specified\\n\");\n\t\treturn NULL;\n#endif\n\t}\n\n\tx = XML_CreateNode(NULL, \"account\", \"\", \"\");\n\tXML_AddParameteri(x, \"id\", accnum);\n\tXML_CreateNode(x, \"serveraddr\", \"\", server);\n\tXML_CreateNode(x, \"username\", \"\", account);\n\tXML_CreateNode(x, \"domain\", \"\", domain);\n\tXML_CreateNode(x, \"resource\", \"\", res);\n\tXML_CreateNode(x, \"password\", \"\", password);\n\tjcl = JCL_ConnectXML(x);\n\tXML_Destroy(x);\n\treturn jcl;\n}\n\nvoid JCL_ForgetBuddyResource(jclient_t *jcl, buddy_t *buddy, bresource_t *bres)\n{\n\tbresource_t **link;\n\tbresource_t *r;\n\tfor (link = &buddy->resources; *link; )\n\t{\n\t\tr = *link;\n\t\tif (!bres || bres == r)\n\t\t{\n\t\t\t*link = r->next;\n\t\t\tfree(r);\n\t\t\tif (bres)\n\t\t\t\tbreak;\n\t\t}\n\t\telse\n\t\t\tlink = &r->next;\n\t}\n}\nvoid JCL_ForgetBuddy(jclient_t *jcl, buddy_t *buddy, bresource_t *bres)\n{\n\tbuddy_t **link;\n\tbuddy_t *b;\n\tfor (link = &jcl->buddies; *link; )\n\t{\n\t\tb = *link;\n\t\tif (!buddy || buddy == b)\n\t\t{\n\t\t\t*link = b->next;\n\t\t\tJCL_ForgetBuddyResource(jcl, b, bres);\n\t\t\tif (jclient_action_buddy == b)\n\t\t\t{\n\t\t\t\tconfuncs->SetConsoleFloat(BUDDYLISTTITLE, \"linebuffered\", false);\n\t\t\t\tjclient_action = ACT_NONE;\n\t\t\t\tjclient_action_buddy = NULL;\n\t\t\t\tjclient_action_cl = NULL;\n\t\t\t}\n\t\t\tfree(b);\n\t\t\tif (buddy)\n\t\t\t\tbreak;\n\t\t}\n\t\telse\n\t\t\tlink = &b->next;\n\t}\n\n\tjclient_updatebuddylist = true;\n}\n\nstruct stringprep_range\n{\n\tunsigned int cp_min;\n\tunsigned int cp_max;\n\tunsigned int remap[2];\n};\nstatic struct stringprep_range stringprep_A1[] =\n{\n\t{0x0234,0x024F},\n\t{0x02AE,0x02AF},\n\t{0x02EF,0x02FF},\n\t{0x0350,0x035F},\n\t{0x0370,0x0373},\n\t{0x0376,0x0379},\n\t{0x037B,0x037D},\n\t{0x037F,0x0383},\n\t{0x038B},\n\t{0x038D},\n\t{0x03A2},\n\t{0x03CF},\n\t{0x03F7,0x03FF},\n\t{0x0487},\n\t{0x04CF},\n\t{0x04F6,0x04F7},\n\t{0x04FA,0x04FF},\n\t{0x0510,0x0530},\n\t{0x0557,0x0558},\n\t{0x0560},\n\t{0x0588},\n\t{0x058B,0x0590},\n\t{0x05A2},\n\t{0x05BA},\n\t{0x05C5,0x05CF},\n\t{0x05EB,0x05EF},\n\t{0x05F5,0x060B},\n\t{0x0600,~0},\t//FIXME rest of A.1 (utf)\n};\nstatic struct stringprep_range stringprep_B1[] =\n{\n\t{0x00AD},\n\t{0x034F},\n\t{0x1806},\n\t{0x180B},\n\t{0x180C},\n\t{0x180D},\n\t{0x200B},\n\t{0x200C},\n\t{0x200D},\n\t{0x2060},\n\t{0xFE00},\n\t{0xFE01},\n\t{0xFE02},\n\t{0xFE03},\n\t{0xFE04},\n\t{0xFE05},\n\t{0xFE06},\n\t{0xFE07},\n\t{0xFE08},\n\t{0xFE09},\n\t{0xFE0A},\n\t{0xFE0B},\n\t{0xFE0C},\n\t{0xFE0D},\n\t{0xFE0E},\n\t{0xFE0F},\n\t{0xFEFF},\n};\nstatic struct stringprep_range stringprep_B2[] =\n{\n\t{0x0041, 0x005A,\t{0x0061}},\n\t{0x00B5, 0x00B5,\t{0x03BC}},\n\t{0x00C0, 0x00DE,\t{0x00E0}},\n\t{0x00DF, 0,\t\t\t{0x0073, 0x0073}},\n};\nstatic struct stringprep_range stringprep_C1[] =\n{\n\t//C1.1\n\t{0x0020},\n\t//C2.2\n\t{0x0020},\n\t{0x00A0},\n\t{0x1680},\n\t//FIXME... utf\n};\nstatic struct stringprep_range stringprep_C2[] =\n{\n\t//C.2.1\n\t{0x0000, 0x001F},\n\t{0x007F, 0x007F},\n\t//C.2.1\n\t{0x0080, 0x009F},\t\t\t//C.2.2\n\t//FIXME... utf\n\t{0x06DD, ~0},\n};\nstatic struct stringprep_range stringprep_C3[] =\n{\n\t{0xE000, 0xF8FF},\n\t{0xF0000, 0xFFFFD},\n\t{0x100000, 0x10FFFD},\n};\nstatic struct stringprep_range stringprep_C4[] =\n{\n\t{0xFDD0, 0xFDEF},\n\t{0xFFFE, 0xFFFF},\n\t{0x1FFFE, 0x1FFFF},\n\t{0x2FFFE, 0x2FFFF},\n\t{0x3FFFE, 0x3FFFF},\n\t{0x4FFFE, 0x4FFFF},\n\t{0x5FFFE, 0x5FFFF},\n\t{0x6FFFE, 0x6FFFF},\n\t{0x7FFFE, 0x7FFFF},\n\t{0x8FFFE, 0x8FFFF},\n\t{0x9FFFE, 0x9FFFF},\n\t{0xAFFFE, 0xAFFFF},\n\t{0xBFFFE, 0xBFFFF},\n\t{0xCFFFE, 0xCFFFF},\n\t{0xDFFFE, 0xDFFFF},\n\t{0xEFFFE, 0xEFFFF},\n\t{0xFFFFE, 0xFFFFF},\n\t{0x10FFFE, 0x10FFFF},\n};\nstatic struct stringprep_range stringprep_C5[] =\n{\n\t{0xD800, 0xDFFF},\n};\nstatic struct stringprep_range stringprep_C6[] =\n{\n\t{0xFFF9},\n\t{0xFFFA},\n\t{0xFFFB},\n\t{0xFFFC},\n\t{0xFFFD},\n};\nstatic struct stringprep_range stringprep_C7[] =\n{\n\t{0x2FF0, 0x2FFB},\n};\nstatic struct stringprep_range stringprep_C8[] =\n{\n\t{0x0340},\n\t{0x0341},\n\t{0x200E},\n\t{0x200F},\n\t{0x202A},\n\t{0x202B},\n\t{0x202C},\n\t{0x202D},\n\t{0x202E},\n\t{0x206A},\n\t{0x206B},\n\t{0x206C},\n\t{0x206D},\n\t{0x206E},\n\t{0x206F},\n};\nstatic struct stringprep_range stringprep_C9[] =\n{\n\t{0xE0001},\n\t{0xE0001},\n\t{0xE0020, 0xE007F},\n};\n\nstatic struct stringprep_range *StringPrep_InRange_(unsigned int codepoint, struct stringprep_range *range, size_t slots)\n{\n\tint i;\n\tfor (i = 0; i < slots; i++)\n\t{\n\t\tif (codepoint < range[i].cp_min)\n\t\t\treturn NULL;\t//early out\n\t\tif (range[i].cp_max)\n\t\t{\n\t\t\tif (codepoint < range[i].cp_max)\n\t\t\t\treturn &range[i];\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (codepoint == range[i].cp_min)\n\t\t\t\treturn &range[i];\n\t\t}\n\t}\n\treturn NULL;\n}\n#define StringPrep_InRange(cp,r) StringPrep_InRange_(cp,r,sizeof(r)/sizeof(r[0]))\n\nstatic qboolean JCL_NamePrep(const char *in, size_t insize, char *out, size_t outsize)\n{\n\tint j;\n\tunsigned int offset;\n\tunsigned int codepoint;\n\tstruct stringprep_range *mapping;\n\tstruct stringprep_range nomap;\n\toutsize--;\n\twhile (insize --> 0)\n\t{\n\t\tcodepoint = *in++;\n\t\tif (codepoint >= 0x80)\n\t\t\treturn false;\t//FIXME: utf-8 decode.\n\n\t\tif (StringPrep_InRange(codepoint, stringprep_A1)) return false;\t//no unassigned codepoints\n\n\t\tmapping = StringPrep_InRange(codepoint, stringprep_B1);\n\t\tif (!mapping)\n\t\t\tmapping = StringPrep_InRange(codepoint, stringprep_B2);\n\t\tif (!mapping)\n\t\t{\t//no mapping, its probably fine.\n\t\t\tnomap.cp_min = codepoint;\n\t\t\tnomap.cp_max = codepoint;\n\t\t\tnomap.remap[0] = codepoint;\n\t\t\tnomap.remap[1] = 0;\n\t\t\tmapping = &nomap;\n\t\t}\n\t\toffset = codepoint - mapping->cp_min;\n\n\t\tfor (j = 0; j < sizeof(mapping->remap)/sizeof(mapping->remap[0]) && mapping->remap[j]; j++)\n\t\t{\n\t\t\tcodepoint = mapping->remap[j] + offset;\n\t\t\tif (StringPrep_InRange(codepoint, stringprep_C1)) return false;\n\t\t\tif (StringPrep_InRange(codepoint, stringprep_C2)) return false;\n\t\t\tif (StringPrep_InRange(codepoint, stringprep_C3)) return false;\n\t\t\tif (StringPrep_InRange(codepoint, stringprep_C4)) return false;\n\t\t\tif (StringPrep_InRange(codepoint, stringprep_C5)) return false;\n\t\t\tif (StringPrep_InRange(codepoint, stringprep_C6)) return false;\n\t\t\tif (StringPrep_InRange(codepoint, stringprep_C7)) return false;\n\t\t\tif (StringPrep_InRange(codepoint, stringprep_C8)) return false;\n\t\t\tif (StringPrep_InRange(codepoint, stringprep_C9)) return false;\n\n\t\t\t//FIXME: utf-8 encode\n\t\t\tif (outsize < 1)\n\t\t\t\treturn false;\n\t\t\toutsize--;\n\t\t\t*out++ = codepoint;\n\t\t}\n\t}\n\t*out = 0;\n\treturn true;\n}\n\nstatic qboolean JCL_NameResourcePrep(const char *in, char *nout, size_t noutsize, char **res)\n{\n\tchar *resstart = strchr(in, '/');\n\tif (resstart)\n\t{\n\t\t*res = resstart+1;\n\t\t//FIXME: resprep the resource.\n\t\tif (!JCL_NamePrep(in, resstart-in, nout, noutsize))\n\t\t\treturn false;\n\t}\n\telse\n\t{\n\t\t*res = NULL;\n\t\tif (!JCL_NamePrep(in, strlen(in), nout, noutsize))\n\t\t\treturn false;\n\t}\n\treturn true;\n}\n\n//FIXME: add flags to avoid creation\nqboolean JCL_FindBuddy(jclient_t *jcl, const char *jid, buddy_t **buddy, bresource_t **bres, qboolean create)\n{\n\tchar name[256];\n\tchar *res;\n\tbuddy_t *b;\n\tbresource_t *r = NULL;\n\tif (!jid || !*jid || !JCL_NameResourcePrep(jid, name, sizeof(name), &res))\n\t{\t//no name / nameprep failed.\n\t\tif (buddy)\n\t\t\t*buddy = NULL;\n\t\tif (bres)\n\t\t\t*bres = NULL;\n\t\treturn false;\n\t}\n\n\tfor (b = jcl->buddies; b; b = b->next)\n\t{\n\t\tif (!strcmp(b->accountdomain, name))\n\t\t\tbreak;\n\t}\n\tif (!b && create)\n\t{\n\t\tb = malloc(sizeof(*b) + strlen(name));\n\t\tmemset(b, 0, sizeof(*b));\n\t\tb->next = jcl->buddies;\n\t\tjcl->buddies = b;\n//\t\tb->vcardphotochanged = true;\t//don't know what it is, query their photo as needed. google sucks, and things stop working.\n\t\tstrcpy(b->accountdomain, name);\n\t\tQ_strlcpy(b->name, name, sizeof(b->name));\t//default\n\t}\n\t*buddy = b;\n\tif (res && bres && b)\n\t{\n\t\tfor (r = b->resources; r; r = r->next)\n\t\t{\n\t\t\tif (!strcmp(r->resource, res))\n\t\t\t\tbreak;\n\t\t}\n\t\tif (!r && create)\n\t\t{\n\t\t\tr = malloc(sizeof(*r) + strlen(res));\n\t\t\tmemset(r, 0, sizeof(*r));\n\t\t\tr->next = b->resources;\n\t\t\tb->resources = r;\n\t\t\tstrcpy(r->resource, res);\n\t\t}\n\t\t*bres = r;\n\t}\n\telse if (bres)\n\t\t*bres = NULL;\n\n\tif (bres)\n\t\treturn *bres != NULL;\n\treturn *buddy != NULL;\n}\n\nvoid JCL_IQTimeouts(jclient_t *jcl)\n{\n\tstruct iq_s *iq, **link;\n\tfor (link = &jcl->pendingiqs; *link; )\n\t{\n\t\tiq = *link;\n\t\tif (iq->timeout < jclient_curtime)\n\t\t{\n\t\t\tiq = *link;\n\t\t\t*link = iq->next;\n\t\t\tif (iq->callback)\n\t\t\t{\n\t\t\t\tCon_DPrintf(\"IQ timeout with %s\\n\", iq->to);\n\t\t\t\tiq->callback(jcl, NULL, iq);\n\t\t\t}\n\t\t\tfree(iq);\n\t\t}\n\t\telse\n\t\t\tlink = &(*link)->next;\n\t}\n}\nstruct iq_s *JCL_SendIQ(jclient_t *jcl, qboolean (*callback) (jclient_t *jcl, xmltree_t *tree, struct iq_s *iq), const char *iqtype, const char *target, const char *body)\n{\n\tstruct iq_s *iq;\n\n\tif (!target)\n\t\ttarget = \"\";\n\n\tiq = malloc(sizeof(*iq) + strlen(target));\n\tiq->next = jcl->pendingiqs;\n\tjcl->pendingiqs = iq;\n\tQ_snprintf(iq->id, sizeof(iq->id), \"%i\", rand());\n\tiq->callback = callback;\n\tiq->timeout = jclient_curtime + 30*1000;\n\tstrcpy(iq->to, target);\n\n\tif (*target)\n\t{\n\t\tif (*jcl->fulljid)\n\t\t\tJCL_AddClientMessagef(jcl, \"<iq type='%s' id='%s' from='%s' to='%s'>\", iqtype, iq->id, jcl->fulljid, target);\n\t\telse\n\t\t\tJCL_AddClientMessagef(jcl, \"<iq type='%s' id='%s' to='%s'>\", iqtype, iq->id, target);\n\t}\n\telse\n\t{\n\t\tif (*jcl->fulljid)\n\t\t\tJCL_AddClientMessagef(jcl, \"<iq type='%s' id='%s' from='%s'>\", iqtype, iq->id, jcl->fulljid);\n\t\telse\n\t\t\tJCL_AddClientMessagef(jcl, \"<iq type='%s' id='%s'>\", iqtype, iq->id);\n\t}\n\tJCL_AddClientMessageString(jcl, body);\n\tJCL_AddClientMessageString(jcl, \"</iq>\");\n\treturn iq;\n}\nstruct iq_s *JCL_SendIQf(jclient_t *jcl, qboolean (*callback) (jclient_t *jcl, xmltree_t *tree, struct iq_s *iq), const char *iqtype, const char *target, const char *fmt, ...)\n{\n\tva_list\t\targptr;\n\tchar body[2048];\n\n\tva_start (argptr, fmt);\n\tQ_vsnprintxf (body, sizeof(body), fmt, argptr);\n\tva_end (argptr);\n\n\treturn JCL_SendIQ(jcl, callback, iqtype, target, body);\n}\nstruct iq_s *JCL_SendIQNode(jclient_t *jcl, qboolean (*callback) (jclient_t *jcl, xmltree_t *tree, struct iq_s *iq), const char *iqtype, const char *target, xmltree_t *node, qboolean destroynode)\n{\n\tstruct iq_s *n;\n\tchar *s = XML_GenerateString(node, false);\n\tn = JCL_SendIQ(jcl, callback, iqtype, target, s);\n\tfree(s);\n\tif (destroynode)\n\t\tXML_Destroy(node);\n\treturn n;\n}\n\n#ifdef USE_GOOGLE_MAIL_NOTIFY\nqboolean XMPP_NewGoogleMailsReply(jclient_t *jcl, xmltree_t *tree, struct iq_s *iq)\n{\n\tint i, j;\n\txmltree_t *mailbox = XML_ChildOfTreeNS(tree, \"google:mail:notify\", \"mailbox\", 0);\n//\tchar *url = XML_GetParameter(mailbox, \"url\", \"\");\n//\tint totalmatched = atoi(XML_GetParameter(mailbox, \"total-matched\", \"0\"));\n//\tchar *resulttime = XML_GetParameter(mailbox, \"result-time\", \"\");\n\txmltree_t *mailthread;\n\txmltree_t *senders, *sender;\n\tchar *subject;\n\tchar *sendername;\n\tchar *cleanup;\n\tchar *mailurl;\n\n\tfor (i = 0; ; i++)\n\t{\n\t\tmailthread = XML_ChildOfTree(mailbox, \"mail-thread-info\", i);\n\t\tif (!mailthread)\n\t\t\tbreak;\n\n//\t\ttid = XML_GetParameter(mailthread, \"tid\", \"\");\n\t\tmailurl = XML_GetParameter(mailthread, \"url\", \"\");\n//\t\tparticipation = XML_GetParameter(mailthread, \"participation\", \"\");\n//\t\tmessages = XML_GetParameter(mailthread, \"messages\", \"\");\n//\t\tdate = XML_GetParameter(mailthread, \"date\", \"\");\n//\t\tlabels = XML_GetChildBody(mailthread, \"labels\", \"\");\n\t\tsubject = XML_GetChildBody(mailthread, \"subject\", \"\");\n\t\tif (!*subject)\n\t\t\tsubject = XML_GetChildBody(mailthread, \"snippet\", \"<NO SUBJECT>\");\n\n\t\tsenders = XML_ChildOfTree(mailthread, \"senders\", 0);\n\t\tfor (j = 0; ; j++)\n\t\t{\n\t\t\tsender = XML_ChildOfTree(senders, \"sender\", j);\n\t\t\tif (!sender)\n\t\t\t\tbreak;\n//\t\t\taddress = XML_GetParameter(sender, \"address\", \"\");\n\t\t\tsendername = XML_GetParameter(sender, \"name\", \"\");\n\t\t\tif (!*sendername)\n\t\t\t\tsendername = XML_GetParameter(sender, \"address\", \"\");\n//\t\t\toriginator = XML_GetParameter(sender, \"originator\", \"\");\n\t\t\tif (atoi(XML_GetParameter(sender, \"unread\", \"1\")))\n\t\t\t{\n\t\t\t\t//we trust the server to not feed us gibberish like \\r or \\n chars.\n\t\t\t\t//however, other chars may be problematic and could break/hack the link markup.\n\t\t\t\tfor (cleanup = sendername; (cleanup = strchr(cleanup, '^')) != NULL; )\n\t\t\t\t\t*cleanup = ' ';\n\t\t\t\tfor (cleanup = sendername; (cleanup = strchr(cleanup, '\\\\')) != NULL; )\n\t\t\t\t\t*cleanup = '/';\n\t\t\t\tfor (cleanup = subject; (cleanup = strchr(cleanup, '^')) != NULL; )\n\t\t\t\t\t*cleanup = ' ';\n\t\t\t\tfor (cleanup = subject; (cleanup = strchr(cleanup, '\\\\')) != NULL; )\n\t\t\t\t\t*cleanup = '/';\n\t\t\t\tfor (cleanup = mailurl; (cleanup = strstr(cleanup, \"^]\")) != NULL; )\n\t\t\t\t\t*cleanup = '_';\t//FIXME: %5E\n\t\t\t\tfor (cleanup = mailurl; (cleanup = strchr(cleanup, '\\\\')) != NULL; )\n\t\t\t\t\t*cleanup = '/';\t//FIXME: %5C\n\t\t\t\tCon_Printf(\"^[^4New spam from %s: %s\\\\url\\\\%s^]\\n\", sendername, subject, mailurl);\n\t\t\t}\n\t\t}\n\t}\n\treturn true;\n}\n#endif\n\nqboolean XMPP_CarbonsEnabledReply(jclient_t *jcl, xmltree_t *tree, struct iq_s *iq)\n{\n\t//don't care. if we don't get carbons then no real loss.\n\t//xmltree_t *etype, *error = XML_ChildOfTreeNS(tree, NULL, \"error\", 0);\n\t//etype = XML_ChildOfTreeNS(error, NULL, \"forbidden\", 0);\n\t//etype = XML_ChildOfTreeNS(error, NULL, \"not-allowed\", 0);\n\treturn true;\n}\n\nstatic void JCL_RosterUpdate(jclient_t *jcl, xmltree_t *listp, const char *from)\n{\n\txmltree_t *i;\n\tbuddy_t *buddy;\n\tint cnum = 0;\n\tconst char *at = strrchr(from, '@');\n\tif (at)\n\t{\n\t\tif (strlen(jcl->username) != at-from || strncasecmp(from, jcl->username, at-from))\n\t\t\treturn;\n\t\tfrom = at+1;\n\t}\n\tif (strcmp(from, jcl->domain))\n\t\treturn;\t//ignore if from somewhere invalid\n\n\twhile ((i = XML_ChildOfTree(listp, \"item\", cnum++)))\n\t{\n\t\tconst char *name = XML_GetParameter(i, \"name\", \"\");\n\t\tconst char *jid = XML_GetParameter(i, \"jid\", \"\");\n\t\tconst char *sub = XML_GetParameter(i, \"subscription\", \"both\");\n\t\tconst char *ask = XML_GetParameter(i, \"ask\", \"\");\n\t\tJCL_FindBuddy(jcl, jid, &buddy, NULL, true);\n\t\tbuddy->btype = BT_ROSTER;\n\n\t\tif (*name)\n\t\t\tQ_strlcpy(buddy->name, name, sizeof(buddy->name));\n\t\telse\n\t\t\tbuddy->vcardphotochanged = true;\t//try to query their actual name\n\t\tif (strcasecmp(sub, \"none\"))\n\t\t\tbuddy->friended = true;\n\t\tif (*ask)\n\t\t\tbuddy->askfriend = true;\n\t}\n\tjclient_updatebuddylist = true;\n}\nstatic qboolean JCL_RosterReply(jclient_t *jcl, xmltree_t *tree, struct iq_s *iq)\n{\n\txmltree_t *c;\n\t//we're probably connected once we've had this reply.\n\tjcl->status = JCL_ACTIVE;\n\tJCL_GeneratePresence(jcl, true);\n\tc = XML_ChildOfTree(tree, \"query\", 0);\n\tif (c)\n\t{\n\t\tJCL_RosterUpdate(jcl, c, jcl->domain);\n\t\treturn true;\n\t}\n\treturn false;\n}\n\nstatic qboolean JCL_BindReply(jclient_t *jcl, xmltree_t *tree, struct iq_s *iq)\n{\n\txmltree_t *c;\n\tc = XML_ChildOfTree(tree, \"bind\", 0);\n\tif (c)\n\t{\n\t\tc = XML_ChildOfTree(c, \"jid\", 0);\n\t\tif (c)\n\t\t{\n\t\t\tchar myjid[512];\n\t\t\tQ_strlcpy(jcl->fulljid, c->body, sizeof(jcl->fulljid));\n\t\t\tJCL_GenLink(jcl, myjid, sizeof(myjid), NULL, jcl->fulljid, NULL, NULL, \"%s\", jcl->fulljid);\n\t\t\tCon_DPrintf(\"Bound to jid %s\\n\", jcl->fulljid);\n\t\t\treturn true;\n\t\t}\n\t}\n\treturn false;\n}\nstatic qboolean JCL_BuddyVCardReply(jclient_t *jcl, xmltree_t *tree, struct iq_s *iq)\n{\n\tchar myself[512];\n\tchar photodata[65536];\n\txmltree_t *vc, *photo, *photobinval;\n\tconst char *nickname;\n\tconst char *photomime;\n\n\tbuddy_t *b = NULL;\n\tchar *from = iq->to;\n\n\tif (!*from)\n\t{\n\t\tQ_snprintf(myself, sizeof(myself), \"%s@%s\", jcl->username, jcl->domain);\n\t\tfrom = myself;\n\t}\n\n\tif (jcl->avatarupdate == iq)\n\t{\n\t\tif (!tree)\n\t\t\tCon_DPrintf(\"timeout for %s's photo\\n\", iq->to);\n\t\telse\n\t\t\tCon_DPrintf(\"Response for %s's photo\\n\", iq->to);\n\t\tjcl->avatarupdate = NULL;\n\t}\n\n\tJCL_FindBuddy(jcl, from, &b, NULL, true);\n\tif (!b)\n\t{\n\t\tCon_DPrintf(\"unknown vcard from %s\\n\", from);\n\t\treturn false;\n\t}\n\n\tjclient_updatebuddylist = true;\t//make sure any new info is displayed properly.\n\n\tvc = XML_ChildOfTree(tree, \"vCard\", 0);\n\tif (!vc)\n\t\tCon_DPrintf(\"invalid vcard from %s\\n\", from);\n\telse\n\t{\n\t\tif (!strcmp(b->name, b->accountdomain))\n\t\t{\n\t\t\tnickname = XML_GetChildBody(vc, \"NICKNAME\", NULL);\n\t\t\tif (!nickname)\n\t\t\t\tnickname = XML_GetChildBody(vc, \"FN\", NULL);\n\t\t\tif (nickname)\n\t\t\t\tQ_strlcpy(b->name, nickname, sizeof(b->name));\n\t\t}\n\n\t\tphoto = XML_ChildOfTree(vc, \"PHOTO\", 0);\n\t\tphotobinval = XML_ChildOfTree(photo, \"BINVAL\", 0);\n\n\t\tif ((jcl->enabledcapabilities & CAP_AVATARS) && drawfuncs)\n\t\t{\n\t\t\tif (photobinval)\n\t\t\t{\n\t\t\t\tstruct buddyinfo_s *bi;\n\t\t\t\tunsigned int photosize = Base64_Decode(photodata, sizeof(photodata), photobinval->body, strlen(photobinval->body));\n\t\t\t\tphotomime = XML_GetChildBody(photo, \"TYPE\", \"\");\n\t\t\t\t//xep-0153: If the <TYPE/> is something other than image/gif, image/jpeg, or image/png, it SHOULD be ignored.\n\t\t\t\tif (strcmp(photomime, \"image/png\") && strcmp(photomime, \"image/jpeg\") && strcmp(photomime, \"image/gif\"))\n\t\t\t\t\tphotomime = \"\";\n\t\t\t\tb->image = drawfuncs->LoadImageData(va(\"xmpp/%s.png\", b->accountdomain), photomime, photodata, photosize);\n\t\t\t\tCon_DPrintf(\"vcard photo updated from %s\\n\", from);\n\n\t\t\t\tfor (bi = jcl->buddyinfo; bi; bi = bi->next)\n\t\t\t\t{\n\t\t\t\t\tif (!strcmp(bi->accountdomain, b->accountdomain))\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tif (!bi)\n\t\t\t\t{\n\t\t\t\t\tbi = malloc(sizeof(*bi)+strlen(b->accountdomain));\n\t\t\t\t\tmemset(bi, 0, sizeof(*bi));\n\t\t\t\t\tstrcpy(bi->accountdomain, b->accountdomain);\n\t\t\t\t\tbi->next = jcl->buddyinfo;\n\t\t\t\t\tjcl->buddyinfo = bi;\n\t\t\t\t}\n\n\t\t\t\tif (bi)\n\t\t\t\t{\n\t\t\t\t\tchar *hex = \"0123456789abcdef\";\n\t\t\t\t\tchar hash[20];\n\t\t\t\t\tchar hasha[41];\n\t\t\t\t\tint i, o;\n\t\t\t\t\tfree(bi->image);\n\t\t\t\t\tfree(bi->imagehash);\n\t\t\t\t\tfree(bi->imagemime);\n\t\t\t\t\tCalcHash(&hash_sha1, hash, sizeof(hash), photodata, photosize);\n\t\t\t\t\tfor (i = 0, o = 0; i < sizeof(hash); i++)\n\t\t\t\t\t{\n\t\t\t\t\t\thasha[o++] = hex[(hash[i]>>4) & 0xf];\n\t\t\t\t\t\thasha[o++] = hex[(hash[i]>>0) & 0xf];\n\t\t\t\t\t}\n\t\t\t\t\thasha[o] = 0;\n\t\t\t\t\tbi->imagehash = strdup(hasha);\n\t\t\t\t\tbi->image = strdup(photobinval->body);\n\t\t\t\t\tbi->imagemime = strdup(photomime);\n\n\t\t\t\t\tjclient_configdirty = true;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tb->image = drawfuncs->LoadImageData(va(\"xmpp/%s.png\", b->accountdomain), \"\", NULL, 0);\n\t\t\t\tCon_DPrintf(\"vcard photo invalidated from %s\\n\", from);\n\t\t\t}\n\t\t}\n\t\telse\n\t\t\tCon_DPrintf(\"vcard photo ignored from %s\\n\", from);\n\t}\n\n\treturn true;\n}\nstatic qboolean JCL_MyVCardReply(jclient_t *jcl, xmltree_t *tree, struct iq_s *iq)\n{\n\tchar photodata[65536];\n\tchar digest[20];\n\txmltree_t *vc, *fn, *nickname, *photo, *photobinval;\n\n\t//hack the from parametmer so it looks legit\n\tQ_snprintf(photodata, sizeof(photodata), \"%s@%s\", jcl->username, jcl->domain);\n\n\t//make sure our image is loaded etc\n\tJCL_BuddyVCardReply(jcl, tree, iq);\n\n\tvc = XML_ChildOfTree(tree, \"vCard\", 0);\n\tfn = XML_ChildOfTree(vc, \"FN\", 0);\n\tnickname = XML_ChildOfTree(vc, \"NICKNAME\", 0);\n\n\tphoto = XML_ChildOfTree(vc, \"PHOTO\", 0);\n\tphotobinval = XML_ChildOfTree(photo, \"BINVAL\", 0);\n\tif (!tree || !photobinval)\n\t{\n\t\t//server doesn't support vcards?\n\t\tif (jcl->vcardphotohashstatus != VCP_NONE)\n\t\t{\n\t\t\tjcl->vcardphotohashstatus = VCP_NONE;\n\t\t\tjcl->vcardphotohashchanged = true;\n\t\t\t*jcl->vcardphotohash = 0;\n\t\t}\n\t}\n\telse\n\t{\n\t\tint photosize = Base64_Decode(photodata, sizeof(photodata), photobinval->body, strlen(photobinval->body));\n\t\tCalcHash(&hash_sha1, digest, sizeof(digest), photodata, photosize);\n\t\tif (jcl->vcardphotohashstatus != VCP_KNOWN || memcmp(jcl->vcardphotohash, digest, sizeof(jcl->vcardphotohash)))\n\t\t{\n\t\t\tmemcpy(jcl->vcardphotohash, digest, sizeof(jcl->vcardphotohash));\n\t\t\tjcl->vcardphotohashchanged = true;\n\t\t\tjcl->vcardphotohashstatus = VCP_KNOWN;\n\t\t}\n\t}\n\n\tif (nickname && *nickname->body)\n\t\tQ_strlcpy(jcl->localalias, nickname->body, sizeof(jcl->localalias));\n\telse if (fn && *fn->body)\n\t\tQ_strlcpy(jcl->localalias, fn->body, sizeof(jcl->localalias));\n\telse\n\t\tQ_strlcpy(jcl->localalias, jcl->barejid, sizeof(jcl->localalias));\t//barejid or just username?\n\treturn true;\n}\nstatic qboolean JCL_ServerFeatureReply(jclient_t *jcl, xmltree_t *tree, struct iq_s *iq)\n{\n\txmltree_t *query = XML_ChildOfTreeNS(tree, \"http://jabber.org/protocol/disco#info\", \"query\", 0);\n\txmltree_t *feature;\n\tconst char *featurename;\n\tint f;\n#ifdef USE_GOOGLE_MAIL_NOTIFY\n\tqboolean gmail = false;\n#endif\n\tqboolean carbons = false;\n\n\tif (!query)\n\t\treturn false;\n\n\tfor (f = 0; ; f++)\n\t{\n\t\tfeature = XML_ChildOfTree(query, \"feature\", f);\n\t\tif (!feature)\n\t\t\tbreak;\n\t\tfeaturename = XML_GetParameter(feature, \"var\", \"\");\n#ifdef USE_GOOGLE_MAIL_NOTIFY\n\t\tif (!strcmp(featurename, \"google:mail:notify\"))\n\t\t\tgmail = true;\n\t\telse\n#endif\n\t\t\tif (!strcmp(featurename, \"urn:xmpp:carbons:2\"))\n\t\t\tcarbons = true;\n\t\telse\n\t\t{\n\t\t\tCon_DPrintf(\"Server supports feature %s\\n\", featurename);\n\t\t}\n\t}\n\n#ifdef USE_GOOGLE_MAIL_NOTIFY\n\tif (gmail)\n\t\tJCL_SendIQf(jcl, XMPP_NewGoogleMailsReply, \"get\", NULL, \"<query xmlns='google:mail:notify'/>\");\n#endif\n\tif (carbons)\n\t\tJCL_SendIQf(jcl, XMPP_CarbonsEnabledReply, \"set\", NULL, \"<enable xmlns='urn:xmpp:carbons:2'/>\");\n\t\n\treturn true;\n}\n/*static qboolean JCL_ServerPeerReply(jclient_t *jcl, xmltree_t *tree, struct iq_s *iq)\n{\n\txmltree_t *query = XML_ChildOfTreeNS(tree, \"http://jabber.org/protocol/disco#info\", \"query\", 0);\n\txmltree_t *feature;\n\txmltree_t *identity;\n\tconst char *from = iq->to;\n\tconst char *name;\n\tconst char *type;\n\tconst char *category;\n\tint f;\n\n\tif (!query)\n\t\treturn false;\n\n\tfor (f = 0; ; f++)\n\t{\n\t\tidentity = XML_ChildOfTree(query, \"identity\", f);\n\t\tif (!identity)\n\t\t\tbreak;\n\t\tname = XML_GetParameter(identity, \"name\", \"\");\n\t\ttype = XML_GetParameter(identity, \"type\", \"\");\n\t\tcategory = XML_GetParameter(identity, \"category\", \"\");\n\t\tCon_Printf(\"Server %s supports identity type=%s category=%s name=%s\\n\", from, type, category, name);\n\t}\n\n\tfor (f = 0; ; f++)\n\t{\n\t\tfeature = XML_ChildOfTree(query, \"feature\", f);\n\t\tif (!feature)\n\t\t\tbreak;\n\t\tname = XML_GetParameter(feature, \"var\", \"\");\n\t\tCon_Printf(\"Server %s supports feature %s\\n\", from, name);\n\t}\n\n\treturn true;\n}\nstatic qboolean JCL_ServerItemsReply(jclient_t *jcl, xmltree_t *tree, struct iq_s *iq)\n{\n\txmltree_t *query = XML_ChildOfTreeNS(tree, \"http://jabber.org/protocol/disco#items\", \"query\", 0);\n\txmltree_t *item;\n\tconst char *itemname;\n\tint f;\n\n\tif (!query)\n\t\treturn false;\n\n\tfor (f = 0; ; f++)\n\t{\n\t\titem = XML_ChildOfTree(query, \"item\", f);\n\t\tif (!item)\n\t\t\tbreak;\n\t\titemname = XML_GetParameter(item, \"jid\", \"\");\n\t\tif (*itemname)\n\t\t{\n\t\t\tCon_DPrintf(\"Server reports peer %s\\n\", itemname);\n\t\t\tJCL_SendIQf(jcl, JCL_ServerPeerReply, \"get\", itemname, \"<query xmlns='http://jabber.org/protocol/disco#info'/>\");\n\t\t}\n\t}\n\treturn true;\n}*/\nstatic qboolean JCL_SessionReply(jclient_t *jcl, xmltree_t *tree, struct iq_s *iq)\n{\n\tbuddy_t *b;\n\tJCL_SendIQf(jcl, JCL_RosterReply, \"get\", NULL, \"<query xmlns='jabber:iq:roster'/>\");\n\tJCL_SendIQf(jcl, JCL_MyVCardReply, \"get\", jcl->barejid, \"<vCard xmlns='vcard-temp'/>\");\n\tJCL_SendIQf(jcl, JCL_ServerFeatureReply, \"get\", jcl->domain, \"<query xmlns='http://jabber.org/protocol/disco#info'/>\");\n//\tJCL_SendIQf(jcl, JCL_ServerItemsReply, \"get\", jcl->domain, \"<query xmlns='http://jabber.org/protocol/disco#items'/>\");\n\n\tfor (b = jcl->buddies; b; b = b->next)\n\t{\n\t\tif (b->btype == BT_CHATROOM)\n\t\t\tif (b->room_autojoin)\n\t\t\t\tJCL_JoinMUCChat(jcl, b->accountdomain, NULL, b->room_nick, b->room_password);\n\t}\n\treturn true;\n}\n\nstatic struct\n{\n\tchar *name;\n\tunsigned int withcap;\n} caps[] =\n{\n#if 1\n\t{\"http://jabber.org/protocol/caps\"},\n\t{\"http://jabber.org/protocol/disco#info\"},\n\t{\"http://jabber.org/protocol/disco#items\"},\n\n\t{\"jabber:iq:version\"},\n\t#ifdef JINGLE\n\t\t{\"urn:xmpp:jingle:1\", CAP_GAMEINVITE|CAP_VOICE|CAP_VIDEO},\n\t\t{QUAKEMEDIAXMLNS, CAP_GAMEINVITE},\n\t\t#ifdef VOIP\n\t\t\t#ifdef VOIP_LEGACY\n\t\t\t\t{\"http://www.google.com/xmpp/protocol/session\", CAP_GOOGLE_VOICE},\t//so google's non-standard clients can chat with us\n\t\t\t\t{\"http://www.google.com/xmpp/protocol/voice/v1\", CAP_GOOGLE_VOICE}, //so google's non-standard clients can chat with us\n//\t\t\t\t{\"http://www.google.com/xmpp/protocol/camera/v1\", CAP_GOOGLE_VOICE},\t//can send video\n//\t\t\t\t{\"http://www.google.com/xmpp/protocol/video/v1\", CAP_GOOGLE_VOICE},\t//can receive video\n\t\t\t#endif\n\t\t\t#ifndef VOIP_LEGACY_ONLY\n\t\t\t\t{\"urn:xmpp:jingle:apps:rtp:1\", CAP_VOICE|CAP_VIDEO},\n\t\t\t\t{\"urn:xmpp:jingle:apps:rtp:audio\", CAP_VOICE},\n\t\t\t\t{\"urn:xmpp:jingle:apps:rtp:video\", CAP_VIDEO},\n\t\t\t#endif\n//\t\t\t{\"urn:xmpp:jingle:apps:rtp:video\", CAP_VIDEO},\t//we don't support rtp video chat\n\t\t#endif\n\t\t{\"urn:xmpp:jingle:transports:raw-udp:1\", CAP_GAMEINVITE|CAP_VOICE|CAP_VIDEO},\n\t\t#ifndef NOICE\n\t\t\t{\"urn:xmpp:jingle:transports:ice-udp:1\", CAP_GAMEINVITE|CAP_VOICE|CAP_VIDEO},\n\t\t#endif\n\t#endif\n\t#ifndef Q3_VM\n\t\t{\"urn:xmpp:time\"},\n\t#endif\n\t{\"urn:xmpp:ping\"},\t//FIXME: I'm not keen on this. I only added support to stop errors from pidgin when trying to debug.\n\t{\"urn:xmpp:attention:0\"},\t//poke.\n\n\t//file transfer\n\t#ifdef FILETRANSFERS\n\t\t{\"http://jabber.org/protocol/si\", CAP_SIFT},\n\t\t{\"http://jabber.org/protocol/si/profile/file-transfer\", CAP_SIFT},\n\t\t{\"http://jabber.org/protocol/ibb\", CAP_SIFT},\n\t\t{\"http://jabber.org/protocol/bytestreams\", CAP_SIFT},\n\t#endif\n\n\t{\"urn:xmpp:avatar:data\"},\n\t{\"urn:xmpp:avatar:metadata\"},\n\t{\"urn:xmpp:avatar:metadata+notify\"},\n//\t{\"http://jabber.org/protocol/mood\"},\n//\t{\"http://jabber.org/protocol/mood+notify\"},\n///\t{\"http://jabber.org/protocol/tune\"},\n//\t{\"http://jabber.org/protocol/tune+notify\"},\n\t{\"http://jabber.org/protocol/nick\"},\n\t{\"http://jabber.org/protocol/nick+notify\"},\n\n\t{\"http://jabber.org/protocol/commands\"},\n#else\n\t//for testing, this is the list of features pidgin supports (which is the other client I'm testing against).\n\n\t\"jabber:iq:last\",\n\t\"jabber:iq:oob\",\n\t\"urn:xmpp:time\",\n\t\"jabber:iq:version\",\n\t\"jabber:x:conference\",\n\t\"http://jabber.org/protocol/bytestreams\",\n\t\"http://jabber.org/protocol/caps\",\n\t\"http://jabber.org/protocol/chatstates\",\n\t\"http://jabber.org/protocol/disco#info\",\n\t\"http://jabber.org/protocol/disco#items\",\n\t\"http://jabber.org/protocol/muc\",\n\t\"http://jabber.org/protocol/muc#user\",\n\t\"http://jabber.org/protocol/si\",\n\t\"http://jabber.org/protocol/si/profile/file-transfer\",\n\t\"http://jabber.org/protocol/xhtml-im\",\n\t\"urn:xmpp:ping\",\n\t\"urn:xmpp:attention:0\",\n\t\"urn:xmpp:bob\",\n\t\"urn:xmpp:jingle:1\",\n\t\"http://www.google.com/xmpp/protocol/session\",\n\t\"http://www.google.com/xmpp/protocol/voice/v1\",\n\t\"http://www.google.com/xmpp/protocol/video/v1\",\n\t\"http://www.google.com/xmpp/protocol/camera/v1\",\n\t\"urn:xmpp:jingle:apps:rtp:1\",\n\t\"urn:xmpp:jingle:apps:rtp:audio\",\n\t\"urn:xmpp:jingle:apps:rtp:video\",\n\t\"urn:xmpp:jingle:transports:raw-udp:1\",\n\t\"urn:xmpp:jingle:transports:ice-udp:1\",\n\t\"urn:xmpp:avatar:metadata\",\n\t\"urn:xmpp:avatar:data\",\n\t\"urn:xmpp:avatar:metadata+notify\",\n\t\"http://jabber.org/protocol/mood\",\n\t\"http://jabber.org/protocol/mood+notify\",\n\t\"http://jabber.org/protocol/tune\",\n\t\"http://jabber.org/protocol/tune+notify\",\n\t\"http://jabber.org/protocol/nick\",\n\t\"http://jabber.org/protocol/nick+notify\",\n\t\"http://jabber.org/protocol/ibb\",\n#endif\n\t{NULL}\n};\nstatic void buildcaps(jclient_t *jcl, char *out, int outlen)\n{\n\tint i;\n\tQ_strncpyz(out, \"<identity category='client' type='pc' name='FTEQW'/>\", outlen);\n\n\tfor (i = 0; caps[i].name; i++)\n\t{\n\t\tif (caps[i].withcap && !(caps[i].withcap & jcl->enabledcapabilities))\n\t\t\tcontinue;\n\t\tQ_strlcat(out, \"<feature var='\", outlen);\n\t\tQ_strlcat(out, caps[i].name, outlen);\n\t\tQ_strlcat(out, \"'/>\", outlen);\n\t}\n}\nstatic int qsortcaps(const void *va, const void *vb)\n{\n\tchar *const a = *(char*const *)va;\n\tchar *const b = *(char*const *)vb;\n\treturn strcmp(a, b);\n}\nchar *buildcapshash(jclient_t *jcl)\n{\n\tint i, l;\n\tchar out[8192];\n\tint outlen = sizeof(out);\n\tunsigned char digest[64];\n\tQ_strlcpy(out, \"client/pc//FTEQW<\", outlen);\n\tqsort(caps, sizeof(caps)/sizeof(caps[0]) - 1, sizeof(caps[0]), qsortcaps); \n\tfor (i = 0; caps[i].name; i++)\n\t{\n\t\tif (caps[i].withcap && !(caps[i].withcap & jcl->enabledcapabilities))\n\t\t\tcontinue;\n\t\tQ_strlcat(out, caps[i].name, outlen);\n\t\tQ_strlcat(out, \"<\", outlen);\n\t}\n\tl = CalcHash(&hash_sha1, digest, sizeof(digest), out, strlen(out));\n\tfor (i = 0; i < l; i++)\n\t\tBase64_Byte(digest[i]);\n\tBase64_Finish();\n\treturn base64;\n}\n\n//xep-0115 1.4+\n//xep-0153\nchar *buildcapsvcardpresence(jclient_t *jcl, char *caps, size_t sizeofcaps)\n{\n\tchar *vcard;\n\tchar *voiceext = \"\";\t//xep-0115 1.0\n#ifdef VOIP_LEGACY\n\tif (jcl->enabledcapabilities & CAP_GOOGLE_VOICE)\n\t{\n\t\tif (jcl->enabledcapabilities & CAP_VIDEO)\n\t\t\tvoiceext = \" ext='voice-v1 camera-v1 video-v1'\";\n\t\telse\n\t\t\tvoiceext = \" ext='voice-v1'\";\n\t}\n#endif\n\n\tQ_snprintf(caps, sizeofcaps,\n\t\t\"<c xmlns='http://jabber.org/protocol/caps'\"\n\t\t\" hash='sha-1'\"\n\t\t\" node='\"DISCONODE\"'\"\n\t\t\" ver='%s'\"\n\t\t\"%s/>\"\n\t\t, buildcapshash(jcl), voiceext);\n\n\t//xep-0153\n\tvcard = caps+strlen(caps);\n\tsizeofcaps -= strlen(caps);\n\tif (jcl->vcardphotohashstatus == VCP_NONE)\n\t{\n\t\t//let other people know that we don't have one. yay. pointless. whatever.\n\t\tQ_snprintf(vcard, sizeofcaps,\n\t\t\t\t\"<x xmlns='vcard-temp:x:update'><photo/></x>\");\n\t}\n\telse if (jcl->vcardphotohashstatus == VCP_KNOWN)\n\t{\n\t\tunsigned char *hex = \"0123456789abcdef\";\n\t\tchar inhex[41];\n\t\tint i, o;\n\t\tfor (i = 0, o = 0; i < sizeof(jcl->vcardphotohash); i++)\n\t\t{\n\t\t\tinhex[o++] = hex[(jcl->vcardphotohash[i]>>4) & 0xf];\n\t\t\tinhex[o++] = hex[(jcl->vcardphotohash[i]>>0) & 0xf];\n\t\t}\n\t\tinhex[o] = 0;\n\n\t\t//if we know the vcard hash, we must tell other people what it is or if its changed, etc.\n\t\tQ_snprintf(vcard, sizeofcaps,\n\t\t\t\"<x xmlns='vcard-temp:x:update'><photo>%s</photo></x>\", inhex);\n\t}\n\telse\n\t{\n\t\t//always include a vcard update tag.\n\t\t//this says that we won't corrupt other resource's vcard.\n\t\t//note that googletalk seems to hack the current vcard hash anyway. don't test this feature on that network.\n\t\tQ_snprintf(vcard, sizeofcaps,\n\t\t\t\"<x xmlns='vcard-temp:x:update'/>\");\n\t}\n\treturn caps;\n}\n\nvoid JCL_ParseIQ(jclient_t *jcl, xmltree_t *tree)\n{\n\tqboolean unparsable = true;\n\tconst char *from;\n//\tconst char *to;\n\tconst char *id;\n\tconst char *f;\n\txmltree_t *ot;\n\n\t//FIXME: block from people who we don't know.\n\n\tid = XML_GetParameter(tree, \"id\", \"\");\n\tfrom = XML_GetParameter(tree, \"from\", jcl->domain);\n//\tto = XML_GetParameter(tree, \"to\", \"\");\n\n\tf = XML_GetParameter(tree, \"type\", \"\");\n\tif (!strcmp(f, \"get\"))\n\t{\n\t\tot = XML_ChildOfTree(tree, \"query\", 0);\n\t\tif (ot)\n\t\t{\n\t\t\tif (from && !strcmp(ot->xmlns, \"http://jabber.org/protocol/disco#info\"))\n\t\t\t{\t//http://xmpp.org/extensions/xep-0030.html\n\t\t\t\tchar msg[2048];\n\t\t\t\tchar hashednode[1024];\n\t\t\t\tconst char *node = XML_GetParameter(ot, \"node\", NULL);\n\n\t\t\t\tbuildcaps(jcl, msg, sizeof(msg));\n\t\t\t\tQ_snprintf(hashednode, sizeof(hashednode),DISCONODE\"#%s\", buildcapshash(jcl));\n\n\t\t\t\tif (!node || !strcmp(node, hashednode) || !strcmp(node, DISCONODE\"#\") )\n\t\t\t\t{\n\t\t\t\t\tunparsable = false;\n\t\t\t\t\tJCL_AddClientMessagef(jcl,\n\t\t\t\t\t\t\t\"<iq type='result' to='%s' id='%s'>\"\n\t\t\t\t\t\t\t\t\"<query xmlns='http://jabber.org/protocol/disco#info' node='%s'>\"\n\t\t\t\t\t\t\t\t\t\"%+s\"\n\t\t\t\t\t\t\t\t\"</query>\"\n\t\t\t\t\t\t\t\"</iq>\", from, id, node?node:hashednode, msg);\n\t\t\t\t}\n#ifdef VOIP_LEGACY\n\t\t\t\telse if (!strcmp(node, DISCONODE\"#voice-v1\") && (jcl->enabledcapabilities & CAP_GOOGLE_VOICE))\n\t\t\t\t{\n\t\t\t\t\tunparsable = false;\n\t\t\t\t\tJCL_AddClientMessagef(jcl,\n\t\t\t\t\t\t\t\"<iq type='result' to='%s' id='%s'>\"\n\t\t\t\t\t\t\t\t\"<query xmlns='http://jabber.org/protocol/disco#info' node='%s'>\"\n\t\t\t\t\t\t\t\t\t\"<feature var='http://www.google.com/xmpp/protocol/voice/v1'/>\"\n\t\t\t\t\t\t\t\t\"</query>\"\n\t\t\t\t\t\t\t\"</iq>\", from, id, node, msg);\n\t\t\t\t}\n\t\t\t\telse if (!strcmp(node, DISCONODE\"#camera-v1\") && (jcl->enabledcapabilities & CAP_GOOGLE_VOICE))\n\t\t\t\t{\n\t\t\t\t\tunparsable = false;\n\t\t\t\t\tJCL_AddClientMessagef(jcl,\n\t\t\t\t\t\t\t\"<iq type='result' to='%s' id='%s'>\"\n\t\t\t\t\t\t\t\t\"<query xmlns='http://jabber.org/protocol/disco#info' node='%s'>\"\n\t\t\t\t\t\t\t\t\t\"<feature var='http://www.google.com/xmpp/protocol/camera/v1'/>\"\n\t\t\t\t\t\t\t\t\"</query>\"\n\t\t\t\t\t\t\t\"</iq>\", from, id, node, msg);\n\t\t\t\t}\n\t\t\t\telse if (!strcmp(node, DISCONODE\"#video-v1\") && (jcl->enabledcapabilities & CAP_GOOGLE_VOICE))\n\t\t\t\t{\n\t\t\t\t\tunparsable = false;\n\t\t\t\t\tJCL_AddClientMessagef(jcl,\n\t\t\t\t\t\t\t\"<iq type='result' to='%s' id='%s'>\"\n\t\t\t\t\t\t\t\t\"<query xmlns='http://jabber.org/protocol/disco#info' node='%s'>\"\n\t\t\t\t\t\t\t\t\t\"<feature var='http://www.google.com/xmpp/protocol/video/v1'/>\"\n\t\t\t\t\t\t\t\t\"</query>\"\n\t\t\t\t\t\t\t\"</iq>\", from, id, node, msg);\n\t\t\t\t}\n#endif\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tJCL_AddClientMessagef(jcl,\n\t\t\t\t\t\t\t\"<iq type='error' to='%s' id='%s'>\"\n\t\t\t\t\t\t\t\t\"<error code='404' type='cancel'>\"\n\t\t\t\t\t\t\t\t\t\"<item-not-found/>\"\n\t\t\t\t\t\t\t\t\"</error>\"\n\t\t\t\t\t\t\t\"</iq>\", from, id);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (from && !strcmp(ot->xmlns, \"http://jabber.org/protocol/disco#items\"))\n\t\t\t{\t//http://xmpp.org/extensions/xep-0030.html\n\t\t\t\tconst char *node = XML_GetParameter(ot, \"node\", NULL);\n\t\t\t\tif (!strcmp(node, \"http://jabber.org/protocol/commands\"))\n\t\t\t\t{\n\t\t\t\t\tconst char *to = XML_GetParameter(tree, \"to\", \"\");\n\t\t\t\t\tunparsable = false;\n\t\t\t\t\tJCL_AddClientMessagef(jcl,\n\t\t\t\t\t\t\t\"<iq type='result' to='%s' id='%s'>\"\n\t\t\t\t\t\t\t\t\"<query xmlns='http://jabber.org/protocol/disco#items' node='%s'>\"\n\t\t\t\t\t\t\t\t\t\"<item jid='%s' node='rcon' name='Toggle Remote Control'/>\"\n\t\t\t\t\t\t\t\t\"</query>\"\n\t\t\t\t\t\t\t\"</iq>\", from, id, node, to);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\t//send back an empty list if its something we don't recognise\n\t\t\t\t\tJCL_AddClientMessagef(jcl,\n\t\t\t\t\t\t\t\"<iq type='result' to='%s' id='%s'>\"\n\t\t\t\t\t\t\t\t\"<query xmlns='http://jabber.org/protocol/disco#items' node='%s' />\"\n\t\t\t\t\t\t\t\"</iq>\", from, id, node);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (from && !strcmp(ot->xmlns, \"jabber:iq:version\"))\n\t\t\t{\t//client->client version request\n\t\t\t\tchar msg[2048];\n\t\t\t\tunparsable = false;\n\n\t\t\t\tQ_snprintf(msg, sizeof(msg),\n\t\t\t\t\t\t\"<iq type='result' to='%s' id='%s'>\"\n\t\t\t\t\t\t\t\"<query xmlns='jabber:iq:version'>\"\n\t\t\t\t\t\t\t\t\"<name>FTEQW XMPP</name>\"\n\t\t\t\t\t\t\t\t\"<version>V\"JCL_BUILD\"</version>\"\n#ifdef Q3_VM\n\t\t\t\t\t\t\t\t\"<os>QVM plugin</os>\"\n#else\n\t\t\t\t\t\t\t\t//don't specify the os otherwise, as it gives away required base addresses etc for exploits\n#endif\n\t\t\t\t\t\t\t\"</query>\"\n\t\t\t\t\t\t\"</iq>\", from, id);\n\n\t\t\t\tJCL_AddClientMessageString(jcl, msg);\n\t\t\t}\n\t\t\telse if (from && !strcmp(ot->xmlns, \"jabber:iq:last\"))\n\t\t\t{\n\t\t\t\tunparsable = false;\n\t\t\t\tJCL_AddClientMessagef(jcl,\n\t\t\t\t\t\t\"<iq type='error' to='%s' id='%s'>\"\n\t\t\t\t\t\t\t\"<error type='cancel'>\"\n\t\t\t\t\t\t\t\t\"<service-unavailable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>\"\n\t\t\t\t\t\t\t\"</error>\"\n\t\t\t\t\t\t\"</iq>\", from, id);\n\t\t\t}\n/*\t\t\telse if (from && !strcmp(ot->xmlns, \"jabber:iq:last\"))\n\t\t\t{\t//http://xmpp.org/extensions/xep-0012.html\n\t\t\t\tchar msg[2048];\n\t\t\t\tint idletime = 0;\n\t\t\t\tunparsable = false;\n\n\t\t\t\t//last activity\n\t\t\t\tQ_snprintf(msg, sizeof(msg),\n\t\t\t\t\t\t\"<iq type='result' to='%s' id='%s'>\"\n\t\t\t\t\t\t\t\"<query xmlns='jabber:iq:last' seconds='%i'/>\"\n\t\t\t\t\t\t\"</iq>\", from, id, idletime);\n\t\t\t\t\n\t\t\t\tJCL_AddClientMessageString(jcl, msg);\n\t\t\t}\n*/\n\t\t}\n#ifndef Q3_VM\n\t\tot = XML_ChildOfTree(tree, \"time\", 0);\n\t\tif (ot && !strcmp(ot->xmlns, \"urn:xmpp:time\"))\n\t\t{\t//http://xmpp.org/extensions/xep-0202.html\n\t\t\tchar msg[2048];\n\t\t\tchar tz[256];\n\t\t\tchar timestamp[256];\n\t\t\tstruct tm * timeinfo;\n\t\t\tint tzh, tzm;\n\t\t\ttime_t rawtime;\n\t\t\ttime (&rawtime);\n\t\t\ttimeinfo = localtime(&rawtime);\n\t\t\ttzh = timeinfo->tm_hour;\n\t\t\ttzm = timeinfo->tm_min;\n\t\t\ttimeinfo = gmtime (&rawtime);\n\t\t\ttzh -= timeinfo->tm_hour;\n\t\t\ttzm -= timeinfo->tm_min;\n\t\t\tQ_snprintf(tz, sizeof(tz), \"%+i:%i\", tzh, tzm);\n\t\t\tstrftime(timestamp, sizeof(timestamp), \"%Y-%m-%dT%H:%M:%SZ\", timeinfo);\n\t\t\tunparsable = false;\n\t\t\t//strftime\n\t\t\tQ_snprintf(msg, sizeof(msg),\n\t\t\t\t\t\"<iq type='result' to='%s' id='%s'>\"\n\t\t\t\t\t\t\"<time xmlns='urn:xmpp:time'>\"\n\t\t\t\t\t\t\t\"<tzo>%s</tzo>\"\n\t\t\t\t\t\t\t\"<utc>%s</utc>\"\n\t\t\t\t\t\t\"</time>\"\n\t\t\t\t\t\"</iq>\", from, id, tz, timestamp);\n\t\t\tJCL_AddClientMessageString(jcl, msg);\n\t\t}\n#endif\n\n\t\tot = XML_ChildOfTree(tree, \"ping\", 0);\n\t\tif (ot && !strcmp(ot->xmlns, \"urn:xmpp:ping\"))\n\t\t{\n\t\t\tJCL_AddClientMessagef(jcl, \"<iq type='result' to='%s' id='%s' />\", from, id);\n\t\t}\n\t\t\t\n\t\tif (unparsable)\n\t\t{\t//unsupported stuff\n\t\t\tchar msg[2048];\n\t\t\tunparsable = false;\n\n\t\t\tCon_Printf(\"Unsupported iq get\\n\");\n\t\t\tXML_ConPrintTree(tree, \"\", 0);\n\n\t\t\t//tell them OH NOES, instead of requiring some timeout.\n\t\t\tQ_snprintf(msg, sizeof(msg),\n\t\t\t\t\t\"<iq type='error' to='%s' id='%s'>\"\n\t\t\t\t\t\t\"<error type='cancel'>\"\n\t\t\t\t\t\t\t\"<service-unavailable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>\"\n\t\t\t\t\t\t\"</error>\"\n\t\t\t\t\t\"</iq>\", from, id);\n\t\t\tJCL_AddClientMessageString(jcl, msg);\n\t\t}\n\t}\n\telse if (!strcmp(f, \"set\"))\n\t{\n\t\txmltree_t *c;\n\n#ifdef FILETRANSFERS\n\t\tif (XMPP_FT_ParseIQSet(jcl, from, id, tree))\n\t\t\treturn;\n#endif\n\n\t\tc = XML_ChildOfTree(tree, \"query\", 0);\n\t\tif (c && !strcmp(c->xmlns, \"jabber:iq:roster\"))\n\t\t{\n\t\t\tunparsable = false;\n\t\t\tif (strcmp(from, jcl->domain))\n\t\t\t\tCon_Printf(\"Roster push from unknown entity: \\\"%s\\\"\\n\", from);\n\t\t\telse\n\t\t\t\tJCL_RosterUpdate(jcl, c, from);\n\t\t}\n\n\t\tc = XML_ChildOfTreeNS(tree, \"http://jabber.org/protocol/commands\", \"command\", 0);\n\t\tif (c)\n\t\t{\n\t\t\tchar *s;\n\t\t\tconst char *key, *val;\n\t\t\txmltree_t *rep, *cmd, *x, *fld;\n\t\t\tconst char *node = XML_GetParameter(c, \"node\", \"\");\n\t\t\tchar *completed = NULL;\n\t\t\tchar *canceled = NULL;\n\t\t\tchar infostring[65536];\n\t\t\tint i;\n\t\t\tunparsable = false;\n\n\t\t\t*infostring = 0;\n\t\t\tx = XML_ChildOfTreeNS(c, \"jabber:x:data\", \"x\", 0);\n\t\t\tfor (i = 0; ; i++)\n\t\t\t{\n\t\t\t\tfld = XML_ChildOfTree(x, \"field\", i);\n\t\t\t\tif (!fld)\n\t\t\t\t\tbreak;\n\t\t\t\tkey = XML_GetParameter(fld, \"var\", \"\");\n\t\t\t\tval = XML_GetChildBody(fld, \"value\", \"\");\n\t\t\t\tif (*key && *val)\n\t\t\t\t{\n\t\t\t\t\tQ_strlcat(infostring, \"\\\\\", sizeof(infostring));\n\t\t\t\t\tQ_strlcat(infostring, key, sizeof(infostring));\n\t\t\t\t\tQ_strlcat(infostring, \"\\\\\", sizeof(infostring));\n\t\t\t\t\tQ_strlcat(infostring, val, sizeof(infostring));\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (!strcmp(XML_GetParameter(c, \"action\", \"submit\"), \"cancel\"))\n\t\t\t\tcanceled = \"\";\n\t\t\telse if (!strcmp(node, \"rcon\"))\n\t\t\t{\n\t\t\t\tchar pwd[64];\n\t\t\t\tJCL_Info_ValueForKey(infostring, \"password\", pwd, sizeof(pwd));\n\t\t\t\tif (*pwd)\n\t\t\t\t{\n\t\t\t\t\tchar rcon_password[64];\n\t\t\t\t\tcvarfuncs->GetString(\"rcon_password\", rcon_password, sizeof(rcon_password));\n\t\t\t\t\tif (*rcon_password && !strcmp(JCL_Info_ValueForKey(infostring, \"password\", pwd, sizeof(pwd)), rcon_password))\n\t\t\t\t\t{\n\t\t\t\t\t\tQ_strlcpy(jcl->rcon_peer, from, sizeof(jcl->rcon_peer));\n\t\t\t\t\t\tif (jcl->rcon_pipe >= 0)\n\t\t\t\t\t\t\tfilefuncs->Close(jcl->rcon_pipe);\n\t\t\t\t\t\tif (confuncs)\n\t\t\t\t\t\t\tjcl->rcon_pipe = confuncs->POpen(NULL);\n\n\t\t\t\t\t\tif (jcl->rcon_pipe < 0)\n\t\t\t\t\t\t\tcanceled = \"Unable to read console\";\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tcompleted = \"Rcon Enabled\";\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\tcompleted = \"Invalid Password\";\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t\tcanceled = \"Unrecognised request\";\n\n\t\t\trep = XML_CreateNode(NULL, \"iq\", NULL, \"\");\n\t\t\tXML_AddParameter(rep, \"type\", \"result\");\n\t\t\tXML_AddParameter(rep, \"id\", id);\n\t\t\tXML_AddParameter(rep, \"to\", from);\n\t\t\tcmd = XML_CreateNode(rep, \"command\", \"http://jabber.org/protocol/commands\", \"\");\n\t\t\tXML_AddParameter(cmd, \"sessionid\", XML_GetParameter(c, \"sessionid\", id));\n\t\t\tXML_AddParameter(cmd, \"node\", node);\n\n\t\t\tif (canceled)\n\t\t\t{\n\t\t\t\tif (*canceled)\n\t\t\t\t{\n\t\t\t\t\tXML_AddParameter(cmd, \"status\", \"completed\");\n\t\t\t\t\tXML_AddParameter(XML_CreateNode(cmd, \"note\", NULL, canceled), \"type\", \"error\");\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tXML_AddParameter(cmd, \"status\", \"canceled\");\n\t\t\t}\n\t\t\telse if (completed)\n\t\t\t{\n\t\t\t\tXML_AddParameter(cmd, \"status\", \"completed\");\n\t\t\t\tif (*completed)\n\t\t\t\t\tXML_AddParameter(XML_CreateNode(cmd, \"note\", NULL, completed), \"type\", \"info\");\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tXML_AddParameter(cmd, \"status\", \"executing\");\n\n//\t\t\t\tx = XML_CreateNode(cmd, \"actions\", NULL, \"\");\n//\t\t\t\tXML_AddParameter(x, \"execute\", \"complete\");\n//\t\t\t\tXML_CreateNode(x, \"complete\", NULL, NULL);\n\n//\t\t\t\tx = XML_CreateNode(cmd, \"actions\", NULL, \"\");\n//\t\t\t\tXML_AddParameter(x, \"execute\", \"next\");\n//\t\t\t\tXML_CreateNode(x, \"next\", NULL, NULL);\n\n\t\t\t\tx = XML_CreateNode(cmd, \"x\", \"jabber:x:data\", \"\");\n\t\t\t\tXML_AddParameter(x, \"type\", \"result\");\n\t\t\t\tXML_CreateNode(x, \"title\", NULL, \"Enable RCon\");\n\t\t\t\tXML_CreateNode(x, \"instructions\", NULL, \"RCon password required...\\nWARNING: This is NOT end-to-end encrypted.\");\n\t\t\t\tfld = XML_CreateNode(x, \"field\", NULL, NULL);\n\t\t\t\tXML_AddParameter(fld, \"var\", \"password\");\n\t\t\t\tXML_AddParameter(fld, \"type\", \"text-private\");\n\t\t\t\tXML_AddParameter(fld, \"label\", \"Rcon Password\");\n\t\t\t\tXML_CreateNode(fld, \"value\", NULL, \"\");\n\t\t\t}\n\t\t\ts = XML_GenerateString(rep, false);\n\t\t\tXML_Destroy(rep);\n\t\t\tJCL_AddClientMessagef(jcl, s, from, id);\n\t\t\tfree(s);\n\t\t}\n\n#ifdef USE_GOOGLE_MAIL_NOTIFY\n\t\t//google-specific - new mail notifications.\n\t\tc = XML_ChildOfTree(tree, \"new-mail\", 0);\n\t\tif (c && !strcmp(c->xmlns, \"google:mail:notify\") && !strcmp(from, jcl->domain))\n\t\t{\n\t\t\tJCL_AddClientMessagef(jcl, \"<iq type='result' to='%s' id='%s' />\", from, id);\n\t\t\tJCL_SendIQf(jcl, XMPP_NewGoogleMailsReply, \"get\", \"\", \"<query xmlns='google:mail:notify'/>\");\n\t\t\treturn;\n\t\t}\n#endif\n\n#ifdef JINGLE\n\t\tc = XML_ChildOfTreeNS(tree, \"urn:xmpp:jingle:1\", \"jingle\", 0);\n\t\tif (c && (jcl->enabledcapabilities & (CAP_GAMEINVITE|CAP_VOICE|CAP_VIDEO)))\n\t\t{\n\t\t\tunparsable = !JCL_ParseJingle(jcl, c, from, id);\n\t\t\tjclient_updatebuddylist = true;\n\t\t}\n#ifdef VOIP_LEGACY\n\t\tc = XML_ChildOfTreeNS(tree, \"http://www.google.com/session\", \"session\", 0);\n\t\tif (c && (jcl->enabledcapabilities & (CAP_GOOGLE_VOICE)))\n\t\t{\n\t\t\tunparsable = !JCL_HandleGoogleSession(jcl, c, from, id);\n\t\t\tjclient_updatebuddylist = true;\n\t\t}\n#endif\n#endif\n\n\n\n\t\tif (unparsable)\n\t\t{\n\t\t\tchar msg[2048];\n\t\t\t//tell them OH NOES, instead of requiring some timeout.\n\t\t\tQ_snprintf(msg, sizeof(msg),\n\t\t\t\t\t\"<iq type='error' to='%s' id='%s'>\"\n\t\t\t\t\t\t\"<error type='cancel'>\"\n\t\t\t\t\t\t\t\"<service-unavailable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>\"\n\t\t\t\t\t\t\"</error>\"\n\t\t\t\t\t\"</iq>\", from, id);\n\t\t\tJCL_AddClientMessageString(jcl, msg);\n\t\t\tunparsable = false;\n\t\t}\n\t}\n\telse if (!strcmp(f, \"result\") || !strcmp(f, \"error\"))\n\t{\n\t\tconst char *id = XML_GetParameter(tree, \"id\", \"\");\n\t\tstruct iq_s **link, *iq;\n\t\tconst char *from = XML_GetParameter(tree, \"from\", jcl->domain);\n\t\tunparsable = false;\n\t\tfor (link = &jcl->pendingiqs; *link; link = &(*link)->next)\n\t\t{\n\t\t\tiq = *link;\n\t\t\tif (!strcmp(iq->id, id) && (!strcmp(iq->to, from) || !*iq->to))\n\t\t\t\tbreak;\n\t\t}\n\t\tif (*link)\n\t\t{\n\t\t\tiq = *link;\n\t\t\t*link = iq->next;\n\n\t\t\tif (iq->callback)\n\t\t\t{\n\t\t\t\tif (!iq->callback(jcl, !strcmp(f, \"error\")?NULL:tree, iq))\n\t\t\t\t{\n\t\t\t\t\tCon_Printf(\"Invalid iq result\\n\");\n\t\t\t\t\tXML_ConPrintTree(tree, \"\", 0);\n\t\t\t\t}\n\t\t\t}\n\t\t\tfree(iq);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tCon_Printf(\"Unrecognised iq result from %s\\n\", from);\n//\t\t\tXML_ConPrintTree(tree, \"\", 0);\n\t\t}\n\t}\n\t\n\tif (unparsable)\n\t{\n\t\tunparsable = false;\n\t\tCon_Printf(\"Unrecognised iq type\\n\");\n\t\tXML_ConPrintTree(tree, \"\", 0);\n\t}\n}\nvoid XMPP_ConversationPrintf(const char *context, const char *title, qboolean takefocus, char *format, ...)\n{\n\tva_list\t\targptr;\n\tstatic char\t\tstring[1024];\n\n\tva_start (argptr, format);\n\tQ_vsnprintf (string, sizeof(string), format,argptr);\n\tva_end (argptr);\n\n\tif (*context && confuncs && confuncs->GetConsoleFloat(context, \"iswindow\") < true)\n\t{\n\t\tconfuncs->SetConsoleFloat(context, \"iswindow\", true);\n\t\tconfuncs->SetConsoleFloat(context, \"forceutf8\", true);\n\t\tconfuncs->SetConsoleFloat(context, \"wnd_w\", 256);\n\t\tconfuncs->SetConsoleFloat(context, \"wnd_h\", 320);\n\t\tconfuncs->SetConsoleString(context, \"title\", title);\n\t}\n\tCon_TrySubPrint(context, string);\n}\nvoid JCL_ParseMessage(jclient_t *jcl, xmltree_t *tree)\n{\n\txmltree_t *ot;\n\tqboolean unparsable = true;\n\tconst char *f = XML_GetParameter(tree, \"from\", jcl->domain);\n\tconst char *type = XML_GetParameter(tree, \"type\", \"normal\");\n\tconst char *ctx = f;\n\n\txmltree_t *received = XML_ChildOfTree(tree, \"received\", 0);\n\txmltree_t *sent = XML_ChildOfTree(tree, \"sent\", 0);\n\tconst char *showicon = NULL;\t//their icon\n\n\tif (received && sent)\n\t\treturn;\t//no, just no.\n\tif (received)\n\t{\n\t\txmltree_t *forwarded = XML_ChildOfTree(received, \"forwarded\", 0);\n\t\tif (!forwarded || strcmp(f, jcl->barejid))\n\t\t\treturn;\t//no bugs, no spoofing\n\t\ttree = XML_ChildOfTree(forwarded, \"message\", 0);\n\t\tf = XML_GetParameter(tree, \"from\", jcl->domain);\n\t}\n\tif (sent)\n\t{\n\t\txmltree_t *forwarded = XML_ChildOfTree(sent, \"forwarded\", 0);\n\t\tif (!forwarded || strcmp(f, jcl->barejid))\n\t\t\treturn;\t//no bugs, no spoofing\n\t\ttree = XML_ChildOfTree(forwarded, \"message\", 0);\n\t\tf = XML_GetParameter(tree, \"to\", jcl->domain);\n\t}\n\n\tif (!strcmp(f, jcl->fulljid))\n\t\tunparsable = false;\n\telse\n\t{\n\t\tif (f)\n\t\t{\n\t\t\tbuddy_t *b = NULL;\n\t\t\tbresource_t *br = NULL;\n\t\t\tQ_strlcpy(jcl->defaultdest, f, sizeof(jcl->defaultdest));\n\t\t\tJCL_FindBuddy(jcl, f, &b, &br, true);\n\t\t\tif (b && b->btype == BT_CHATROOM && strchr(jcl->defaultdest, '/'))\n\t\t\t\t*strchr(jcl->defaultdest, '/') = 0;\n\t\t\tif (b)\n\t\t\t{\n\t\t\t\tctx = b->accountdomain;\n\t\t\t\tif (!strcmp(type, \"groupchat\"))\n\t\t\t\t{\t//clients can act as a MUC too, which is awkward.\n\t\t\t\t\tif (br)\n\t\t\t\t\t\tf = br->resource;\n\t\t\t\t}\n\t\t\t\telse if (b->btype == BT_CHATROOM)\n\t\t\t\t{\n\t\t\t\t\tctx = f;\t//one server with multiple rooms requires that we retain resource info.\n\t\t\t\t\tif (br)\n\t\t\t\t\t\tf = br->resource;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tif (confuncs)\n\t\t\t\t\t\tshowicon = b->accountdomain;\n\t\t\t\t\tf = b->name;\n\t\t\t\t\tif (br)\n\t\t\t\t\t\tb->defaultresource = br;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (!strcmp(type, \"error\"))\n\t\t{\n\t\t\tconst char *reason = NULL;\n\t\t\tunparsable = false;\n\t\t\tif (sent)\n\t\t\t\treturn;\n\t\t\tot = XML_ChildOfTree(tree, \"error\", 0);\n\t\t\tif (ot->child)\n\t\t\t\treason = ot->child->name;\n\t\t\tif (XML_ChildOfTree(ot, \"remote-server-not-found\", 0))\t\treason = \"Remote Server Not Found\";\n\t\t\tif (XML_ChildOfTree(ot, \"bad-request\", 0))\t\t\t\t\treason = \"Bad Request\";\n\t\t\tif (XML_ChildOfTree(ot, \"conflict\", 0))\t\t\t\t\t\treason = \"Conflict Error\";\n\t\t\tif (XML_ChildOfTree(ot, \"feature-not-implemented\", 0))\t\treason = \"feature-not-implemented\";\n\t\t\tif (XML_ChildOfTree(ot, \"forbidden\", 0))\t\t\t\t\treason = \"forbidden\";\n\t\t\tif (XML_ChildOfTree(ot, \"gone\", 0))\t\t\t\t\t\t\treason = \"'gone' Error\";\n\t\t\tif (XML_ChildOfTree(ot, \"internal-server-error\", 0))\t\treason = \"internal-server-error\";\n\t\t\tif (XML_ChildOfTree(ot, \"item-not-found\", 0))\t\t\t\treason = \"item-not-found\";\n\t\t\tif (XML_ChildOfTree(ot, \"jid-malformed\", 0))\t\t\t\treason = \"jid-malformed\";\n\t\t\tif (XML_ChildOfTree(ot, \"not-acceptable\", 0))\t\t\t\treason = \"not-acceptable\";\n\t\t\tif (XML_ChildOfTree(ot, \"not-allowed\", 0))\t\t\t\t\treason = \"not-allowed\";\n\t\t\tif (XML_ChildOfTree(ot, \"not-authorized\", 0))\t\t\t\treason = \"not-authorized\";\n\t\t\tif (XML_ChildOfTree(ot, \"policy-violation\", 0))\t\t\t\treason = \"policy-violation\";\n\t\t\tif (XML_ChildOfTree(ot, \"recipient-unavailable\", 0))\t\treason = \"recipient-unavailable\";\n\t\t\tif (XML_ChildOfTree(ot, \"redirect\", 0))\t\t\t\t\t\treason = \"'redirect' Error\";\n\t\t\tif (XML_ChildOfTree(ot, \"registration-required\", 0))\t\treason = \"registration-required\";\n\t\t\tif (XML_ChildOfTree(ot, \"remote-server-not-found\", 0))\t\treason = \"remote-server-not-found\";\n\t\t\tif (XML_ChildOfTree(ot, \"remote-server-timeout\", 0))\t\treason = \"remote-server-timeout\";\n\t\t\tif (XML_ChildOfTree(ot, \"resource-constraint\", 0))\t\t\treason = \"resource-constraint\";\n\t\t\tif (XML_ChildOfTree(ot, \"service-unavailable\", 0))\t\t\treason = \"service-unavailable\";\n\t\t\tif (XML_ChildOfTree(ot, \"subscription-required\", 0))\t\treason = \"subscription-required\";\n\t\t\tif (XML_ChildOfTree(ot, \"undefined-condition\", 0))\t\t\treason = \"undefined-condition\";\n\t\t\tif (XML_ChildOfTree(ot, \"unexpected-request\", 0))\t\t\treason = \"unexpected-request\";\n\t\t\treason = XML_GetChildBody(ot, \"text\", reason);\n\n\t\t\tot = XML_ChildOfTree(tree, \"body\", 0);\n\t\t\tif (ot)\n\t\t\t{\n\t\t\t\tif (reason)\n\t\t\t\t\tXMPP_ConversationPrintf(ctx, f, false, \"^1Error: %s (%s): \", reason, f);\n\t\t\t\telse\n\t\t\t\t\tXMPP_ConversationPrintf(ctx, f, false, \"^1error sending message to %s: \", f);\n\t\t\t\tif (f)\n\t\t\t\t{\n\t\t\t\t\tif (!strncmp(ot->body, \"/me \", 4))\n\t\t\t\t\t\tXMPP_ConversationPrintf(ctx, f, false, \"* ^2%s^7%s\\r\", ((!strcmp(jcl->localalias, \">>\"))?\"me\":jcl->localalias), ot->body+3);\n\t\t\t\t\telse\n\t\t\t\t\t\tXMPP_ConversationPrintf(ctx, f, false, \"%s\\r\", ot->body);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (reason)\n\t\t\t\t\tXMPP_ConversationPrintf(ctx, f, false, \"^1Error: %s (%s)\\r\", reason, f);\n\t\t\t\telse\n\t\t\t\t\tXMPP_ConversationPrintf(ctx, f, false, \"error sending message to %s\\r\", f);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tif (f && !sent)\n\t\t{\n\t\t\tot = XML_ChildOfTree(tree, \"composing\", 0);\n\t\t\tif (ot && !strcmp(ot->xmlns, \"http://jabber.org/protocol/chatstates\"))\n\t\t\t{\n\t\t\t\tunparsable = false;\n\t\t\t\tXMPP_ConversationPrintf(ctx, f, false, \"%s is typing\\r\", f);\n\t\t\t}\n\t\t\tot = XML_ChildOfTree(tree, \"paused\", 0);\n\t\t\tif (ot && !strcmp(ot->xmlns, \"http://jabber.org/protocol/chatstates\"))\n\t\t\t{\n\t\t\t\tunparsable = false;\n\t\t\t\tXMPP_ConversationPrintf(ctx, f, false, \"%s has stopped typing\\r\", f);\n\t\t\t}\n\t\t\tot = XML_ChildOfTree(tree, \"inactive\", 0);\n\t\t\tif (ot && !strcmp(ot->xmlns, \"http://jabber.org/protocol/chatstates\"))\n\t\t\t{\n\t\t\t\tunparsable = false;\n\t\t\t\tXMPP_ConversationPrintf(ctx, f, false, \"\\r\", f);\n\t\t\t}\n\t\t\tot = XML_ChildOfTree(tree, \"active\", 0);\n\t\t\tif (ot && !strcmp(ot->xmlns, \"http://jabber.org/protocol/chatstates\"))\n\t\t\t{\n\t\t\t\tunparsable = false;\n\t\t\t\tXMPP_ConversationPrintf(ctx, f, false, \"\\r\", f);\n\t\t\t}\n\t\t\tot = XML_ChildOfTree(tree, \"gone\", 0);\n\t\t\tif (ot && !strcmp(ot->xmlns, \"http://jabber.org/protocol/chatstates\"))\n\t\t\t{\n\t\t\t\tunparsable = false;\n\t\t\t\tXMPP_ConversationPrintf(ctx, f, false, \"%s has gone away\\r\", f);\n\t\t\t}\n\t\t}\n\n\t\tot = XML_ChildOfTreeNS(tree, \"urn:xmpp:attention:0\", \"attention\", 0);\n\t\tif (ot && !sent)\n\t\t{\n\t\t\tunparsable = false;\n\t\t\tif (jclient_poketime < jclient_curtime)\t//throttle these.\n\t\t\t{\n\t\t\t\tjclient_poketime = jclient_curtime + 10*1000;\n\t\t\t\tXMPP_ConversationPrintf(ctx, f, true, \"%s is an attention whore.\\n\", f);\n\t\t\t\tif (drawfuncs)\n\t\t\t\t\tdrawfuncs->LocalSound(\"misc/talk.wav\", 256, 1);\n\t\t\t}\n\t\t}\n\t\t\n\t\tot = XML_ChildOfTree(tree, \"subject\", 0);\n\t\tif (ot && !strcmp(type, \"groupchat\"))\n\t\t{\n\t\t\tbuddy_t *b;\n\t\t\tJCL_FindBuddy(jcl, ctx, &b, NULL, true);\n\t\t\tif (b && b->btype == BT_CHATROOM)\n\t\t\t{\n\t\t\t\tfree(b->room_topic);\n\t\t\t\tb->room_topic = strdup(ot->body);\n\t\t\t\tjclient_updatebuddylist = true;\n\t\t\t}\n\n\t\t\tunparsable = false;\n\t\t\tif (sent)\n\t\t\t\tXMPP_ConversationPrintf(ctx, f, false, COL_NAME_US\"You\"COL_TEXT_US\" have set the topic to: %s\\n\", ot->body);\n\t\t\telse\n\t\t\t\tXMPP_ConversationPrintf(ctx, f, false, COL_NAME_THEM\"%s\"COL_TEXT_THEM\" has set the topic to: %s\\n\", f, ot->body);\n\t\t}\n\n\t\tot = XML_ChildOfTreeNS(tree, \"http://jabber.org/protocol/pubsub#event\", \"event\", 0);\n\t\tif (ot)\n\t\t{\n\t\t\tbuddy_t *b;\n\t\t\tunparsable = false;\n\t\t\tJCL_FindBuddy(jcl, f, &b, NULL, false);\n\t\t\tif (!sent)\n\t\t\t{\n\t\t\t\txmltree_t *items = XML_ChildOfTree(ot, \"items\", 0);\n\t\t\t\tconst char *node = XML_GetParameter(items, \"node\", \"\");\n\t\t\t\tif (!strcmp(node, \"http://jabber.org/protocol/nick\"))\n\t\t\t\t{\n\t\t\t\t\txmltree_t *item = XML_ChildOfTree(items, \"item\", 0);\n\t\t\t\t\tconst char *nick = XML_GetChildBody(item, \"nick\", NULL);\n\t\t\t\t\tif (nick && b)\n\t\t\t\t\t\tQ_strlcpy(b->name, nick, sizeof(b->name));\n\t\t\t\t}\n\t\t\t\telse if (!strcmp(node, \"http://jabber.org/protocol/tune\"))\n\t\t\t\t{\n//\t\t\t\t\txmltree_t *item = XML_ChildOfTree(items, \"item\", 0);\n//\t\t\t\t\txmltree_t *tune = XML_ChildOfTreeNS(item, \"http://jabber.org/protocol/tune\", \"tune\", 0);\n\t\t\t\t}\n\t\t\t\telse if (!strcmp(node, \"http://jabber.org/protocol/mood\"))\n\t\t\t\t{\n//\t\t\t\t\txmltree_t *item = XML_ChildOfTree(items, \"item\", 0);\n//\t\t\t\t\txmltree_t *mood = XML_ChildOfTreeNS(item, \"http://jabber.org/protocol/mood\", \"mood\", 0);\n//\t\t\t\t\tconst char *text = XML_GetChildBody(mood, \"text\", NULL);\n\t\t\t\t\t//there's a whole army of options here. crazy stuff that makes it unusable.\n\t\t\t\t}\n\t\t\t\telse if (!strcmp(node, \"urn:xmpp:avatar:metadata\"))\n\t\t\t\t{\n//\t\t\t\t\txmltree_t *item = XML_ChildOfTree(items, \"item\", 0);\n//\t\t\t\t\txmltree_t *tune = XML_ChildOfTreeNS(item, \"urn:xmpp:avatar:metadata\", \"metadata\", 0);\n\t\t\t\t\t//this can get messy.\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tCon_DPrintf(\"Unknown pubsub/pep node \\\"%s\\\"\\n\", node);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tot = XML_ChildOfTreeNS(tree, \"http://jabber.org/protocol/muc#user\", \"x\", 0);\n\t\tif (ot && f && !strchr(f, '/'))\n\t\t{\n\t\t\t//this is an appaling extension protocol. we really have no way to know if someone's just making this shit up just to see our presence.\n\t\t\t//this message came from the groupchat server.\n\t\t\txmltree_t *inv = XML_ChildOfTree(ot, \"invite\", 0);\n\t\t\tif (inv)\n\t\t\t{\n\t\t\t\tconst char *who = XML_GetParameter(inv, \"from\", jcl->domain);\n\t\t\t\tconst char *reason = XML_GetChildBody(inv, \"reason\", NULL);\n\t\t\t\tconst char *password = XML_GetChildBody(ot, \"password\", 0);\n\t\t\t\tchar link[512];\n\t\t\t\tbuddy_t *b;\n\t\t\t\tif (!sent && JCL_FindBuddy(jcl, f, &b, NULL, true))\n\t\t\t\t{\n\t\t\t\t\tif (b->btype == BT_CHATROOM)\n\t\t\t\t\t\treturn;\t//we already know about it. don't spam.\n\t\t\t\t\tJCL_GenLink(jcl, link, sizeof(link), \"mucjoin\", f, NULL, password, \"%s\", f);\n//\t\t\t\t\tctx = who;\n\t\t\t\t\tif (reason)\n\t\t\t\t\t\tXMPP_ConversationPrintf(ctx, f, true, \"* ^2%s^7 has invited you to join %s: %s.\\n\", who, link, reason);\n\t\t\t\t\telse\n\t\t\t\t\t\tXMPP_ConversationPrintf(ctx, f, true, \"* ^2%s^7 has invited you to join %s.\\n\", who, link);\n\t\t\t\t}\n\t\t\t\treturn; //ignore any body/jabber:x:conference\n\t\t\t}\n\t\t}\n\n\t\tot = XML_ChildOfTreeNS(tree, \"jabber:x:conference\", \"x\", 0);\n\t\tif (ot)\n\t\t{\n\t\t\tchar link[512];\n\t\t\tconst char *chatjit = XML_GetParameter(ot, \"jid\", \"\");\n\t\t\tconst char *reason = XML_GetParameter(ot, \"reason\", NULL);\n\t\t\tconst char *password = XML_GetParameter(ot, \"password\", NULL);\n\t\t\tunparsable = false;\n\n\t\t\tJCL_GenLink(jcl, link, sizeof(link), \"mucjoin\", chatjit, NULL, password, \"%s\", chatjit);\n\t\t\tif (sent)\n\t\t\t{\n\t\t\t\tif (reason)\n\t\t\t\t\tXMPP_ConversationPrintf(ctx, f, true, \"* You have invited ^2%s^7 to join %s: %s.\\n\", f, link, reason);\n\t\t\t\telse\n\t\t\t\t\tXMPP_ConversationPrintf(ctx, f, true, \"* You have invited ^2%s^7 to join %s.\\n\", f, link);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (reason)\n\t\t\t\t\tXMPP_ConversationPrintf(ctx, f, true, \"* ^2%s^7 has invited you to join %s: %s.\\n\", f, link, reason);\n\t\t\t\telse\n\t\t\t\t\tXMPP_ConversationPrintf(ctx, f, true, \"* ^2%s^7 has invited you to join %s.\\n\", f, link);\n\t\t\t}\n\t\t\treturn;\t//ignore any body\n\t\t}\n\n\t\tot = XML_ChildOfTree(tree, \"body\", 0);\n\t\tif (ot)\n\t\t{\n\t\t\tunparsable = false;\n\t\t\tif (f)\n\t\t\t{\n\t\t\t\tif (sent)\n\t\t\t\t{\n\t\t\t\t\tif (!strncmp(ot->body, \"/me \", 4))\n\t\t\t\t\t\tXMPP_ConversationPrintf(ctx, f, false, \"* \"COL_NAME_US\"%s\"COL_TEXT_US\"%s\\n\", jcl->localalias, ot->body+3);\n\t\t\t\t\telse if (showicon)\n\t\t\t\t\t\tXMPP_ConversationPrintf(ctx, f, false, \"^[\\\\img\\\\xmpp/%s.png\\\\fbimg\\\\\"IMG_FB_US\"\\\\w\\\\32\\\\h\\\\32^]\"COL_NAME_US\"%s\"COL_TEXT_US\":\\v%s\\n\", jcl->barejid, jcl->localalias, ot->body);\n\t\t\t\t\telse\n\t\t\t\t\t\tXMPP_ConversationPrintf(ctx, f, false, COL_NAME_US\"%s\"COL_TEXT_US\": %s\\n\", jcl->localalias, ot->body);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tif (!strncmp(ot->body, \"/me \", 4))\n\t\t\t\t\t\tXMPP_ConversationPrintf(ctx, f, false, \"* \"COL_NAME_THEM\"%s\"COL_TEXT_THEM\"%s\\n\", f, ot->body+3);\n\t\t\t\t\telse if (showicon)\n\t\t\t\t\t\tXMPP_ConversationPrintf(ctx, f, false, \"^[\\\\img\\\\xmpp/%s.png\\\\fbimg\\\\\"IMG_FB_THEM\"\\\\w\\\\32\\\\h\\\\32^]\"COL_NAME_THEM\"%s\"COL_TEXT_THEM\":\\v%s\\n\", showicon, f, ot->body);\n\t\t\t\t\telse\n\t\t\t\t\t\tXMPP_ConversationPrintf(ctx, f, false, COL_NAME_THEM\"%s\"COL_TEXT_THEM\": %s\\n\", f, ot->body);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\t\n\t\t\t\tif (!sent)\n\t\t\t\t\tXMPP_ConversationPrintf(ctx, f, false, \"NOTICE: %s\\n\", ot->body);\n\t\t\t}\n\n\t\t\tif (drawfuncs)\n\t\t\t\tdrawfuncs->LocalSound(\"misc/talk.wav\", 256, 1);\n\t\t}\n\n\t\tif (unparsable)\n\t\t{\n\t\t\tunparsable = false;\n\t\t\tif (jcl->streamdebug)\n\t\t\t{\n\t\t\t\tXMPP_ConversationPrintf(ctx, f, false, \"Received a message without a body\\n\");\n\t\t\t\tXML_ConPrintTree(tree, \"\", 0);\n\t\t\t}\n\t\t}\n\t}\n}\n\n#if 0\nstatic qboolean JCL_ValidateCaps(xmltree_t *query, const char *node, const char *ver, const char *hash)\n{\n\tconst char *respnode = XML_GetParameter(query, \"node\", \"\");\n\tint l;\n\tchar out[8192];\n\tint outlen = sizeof(out);\n\tunsigned char digest[64];\n\tconst char *features[1024];\n\tsize_t i, numfeatures = 0;\n\tsize_t nlen = strlen(node);\n\tif (strcmp(hash, \"sha-1\"))\n\t\treturn false;\t//unable to validate.\n\tif (strncmp(respnode, node, nlen))\n\t\treturn false; //the client's name changed...\n\trespnode+=nlen;\n\tif (*respnode++ != '#')\n\t\treturn false; //o.O\n\tif (strcmp(respnode, ver))\n\t\treturn false; //the client's name changed...\n\tfor(i = 0; i < 64; i++)\n\t{\n\t\txmltree_t *ident = XML_ChildOfTree(query, \"identity\", i);\n\t\tif (ident)\n\t\t{\n\t\t\tconst char *category = XML_GetParameter(ident, \"category\", \"\");\n\t\t\tconst char *name = XML_GetParameter(ident, \"name\", \"\");\n\t\t\tconst char *type = XML_GetParameter(ident, \"type\", \"\");\n\t\t\tconst char *lang = XML_GetParameter(ident, \"xml:lang\", \"\");\n\t\t\tQ_snprintf(out, outlen, \"%s/%s/%s/%s<\", category, type, lang, name);\n\t\t}\n\t\telse\n\t\t\tbreak;\n\t}\n\tfor(i = 0; i < countof(features); i++)\n\t{\n\t\txmltree_t *feature = XML_ChildOfTree(query, \"feature\", i);\n\t\tif (feature)\n\t\t{\n\t\t\tconst char *var = XML_GetParameter(feature, \"var\", \"\");\n\t\t\tfeatures[numfeatures++] = var;\n\t\t}\n\t\telse\n\t\t\tbreak;\n\t}\n//\tqsort(caps, sizeof(caps)/sizeof(caps[0]) - 1, sizeof(caps[0]), qsortcaps); \n\tfor (i = 0; i < numfeatures; i++)\n\t{\n\t\tQ_strlcat(out, features[i], outlen);\n\t\tQ_strlcat(out, \"<\", outlen);\n\t}\n\t//fixme: add any form crap\n\tl = SHA1(digest, sizeof(digest), out, strlen(out));\n\tfor (i = 0; i < l; i++)\n\t\tBase64_Byte(digest[i]);\n\treturn !strcmp(respnode, Base64_Finish());\t//make sure its mostly valid.\n}\n#endif\nunsigned int JCL_ParseCaps(jclient_t *jcl, char *account, char *resource, xmltree_t *query)\n{\n\txmltree_t *feature;\n\tunsigned int caps = 0;\n\tqboolean rtp = false;\n\tqboolean rtpaudio = false;\n\tqboolean rtpvideo = false;\n\tqboolean quake = false;\n\tqboolean ice = false;\n\tqboolean raw = false;\n\tqboolean jingle = false;\n\tqboolean si = false;\n\tqboolean sift = false;\n\tqboolean ibb = false;\n\tint i = 0;\n\tconst char *var;\n//\tXML_ConPrintTree(query, 0);\n\twhile((feature = XML_ChildOfTree(query, \"feature\", i++)))\n\t{\n\t\tvar = XML_GetParameter(feature, \"var\", \"\");\n\t\t//check ones we recognise.\n//\t\tCon_Printf(\"%s/%s: %s\\n\", account, resource, var);\n\t\tif (!strcmp(var, QUAKEMEDIAXMLNS))\n\t\t\tquake = true;\n#ifndef VOIP_LEGACY_ONLY\n\t\tif (!strcmp(var, \"urn:xmpp:jingle:apps:rtp:audio\"))\n\t\t\trtpaudio = true;\n\t\tif (!strcmp(var, \"urn:xmpp:jingle:apps:rtp:video\"))\n\t\t\trtpvideo = true;\n#endif\n\t\tif (!strcmp(var, \"urn:xmpp:jingle:apps:rtp:1\"))\n\t\t\trtp = true;\t\t//kinda implied, but ensures version is okay\n\t\tif (!strcmp(var, \"urn:xmpp:jingle:transports:ice-udp:1\"))\n\t\t\tice = true;\n\t\tif (!strcmp(var, \"urn:xmpp:jingle:transports:raw-udp:1\"))\n\t\t\traw = true;\n\t\tif (!strcmp(var, \"urn:xmpp:jingle:1\"))\n\t\t\tjingle = true;\t//kinda implied, but ensures version is okay\n\n#ifdef VOIP_LEGACY\n\t\tif (!strcmp(var, \"http://www.google.com/xmpp/protocol/voice/v1\"))\n\t\t\tcaps |= CAP_GOOGLE_VOICE;\t//legacy crap, so we can call google's official clients, which do not follow current xmpp standards.\n#endif\n\n\t\tif (!strcmp(var, \"http://jabber.org/protocol/si\"))\n\t\t\tsi = true;\n\t\tif (!strcmp(var, \"http://jabber.org/protocol/si/profile/file-transfer\"))\n\t\t\tsift = true;\n\t\tif (!strcmp(var, \"http://jabber.org/protocol/ibb\"))\n\t\t\tibb = true;\n\t}\n\n\tif ((ice||raw) && jingle)\n\t{\n\t\tif (rtpaudio && rtp)\n\t\t\tcaps |= CAP_VOICE;\n\t\tif (rtpvideo && rtp)\n\t\t\tcaps |= CAP_VIDEO;\n\t\tif (quake)\n\t\t\tcaps |= CAP_GAMEINVITE;\n\t}\n\tif (si && sift && ibb)\n\t\tcaps |= CAP_SIFT;\n\n\tcaps &= jcl->enabledcapabilities;\n\n\treturn caps;\n}\n\nvoid JCL_CheckClientCaps(jclient_t *jcl, buddy_t *buddy, bresource_t *bres);\nqboolean JCL_ClientDiscoInfo(jclient_t *jcl, xmltree_t *tree, struct iq_s *iq)\n{\n\txmltree_t *query = XML_ChildOfTree(tree, \"query\", 0);\n\tunsigned int caps = 0;\n\tbuddy_t *b, *ob;\n\tbresource_t *r, *or;\n\tif (!JCL_FindBuddy(jcl, iq->to, &b, &r, true))\n\t\treturn false;\n\n\tif (!query)\n\t{\n\t\tcaps &= ~(CAP_QUERYING|CAP_QUERIED);\n\t\tcaps |= CAP_QUERYFAILED;\n\t}\n\telse\n\t{\n//\t\tXML_ConPrintTree(tree, 0);\n//\t\tif (!JCL_ValidateCaps(query, r->client_node, r->client_ver, r->client_hash))\n//\t\t\tcaps = 0;\n//\t\telse\n\t\t\tcaps = JCL_ParseCaps(jcl, b->accountdomain, r->resource, query);\n\t}\n\n\tif (b && r)\n\t{\n\t\tif (!(caps & CAP_QUERYFAILED))\n\t\t{\n//\t\t\tCon_Printf(\"%s/%s caps = %x\\n\", b->accountdomain, r->resource, caps);\n\t\t\tif (!(r->caps & CAP_QUERIED))\n\t\t\t\tr->caps = CAP_QUERIED;\t//reset it\n\t\t\tr->caps |= caps;\n\n\t\t\t//as we got a valid response, make sure other resources running the same software get the same caps\n\t\t\tfor (ob = jcl->buddies; ob; ob = ob->next)\n\t\t\t{\n\t\t\t\tfor (or = ob->resources; or; or = or->next)\n\t\t\t\t{\n\t\t\t\t\t//ignore evil clients.\n\t\t\t\t\tif (r == or || (or->caps & (CAP_QUERYFAILED|CAP_QUERIED|CAP_QUERYING)) || !or->client_node)\n\t\t\t\t\t\tcontinue;\n\n\t\t\t\t\t//all resources with the same details then get the same caps flags.\n\t\t\t\t\tif (!strcmp(r->client_node, or->client_node) && !strcmp(r->client_ver, or->client_ver) && !strcmp(r->client_hash, or->client_hash) && !strcmp(r->client_ext, or->client_ext))\n\t\t\t\t\t{\n//\t\t\t\t\t\tCon_Printf(\"%s/%s matches (updated) %s/%s (%s#%s)\\n\", ob->accountdomain, or->resource, b->accountdomain, r->resource, r->client_node, r->client_ver);\n\t\t\t\t\t\tor->caps = r->caps;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (!(r->caps & (CAP_QUERIED|CAP_QUERYFAILED)))\n\t\t\t{\n\t\t\t\tr->caps = CAP_QUERYFAILED;\n\t\t\t\t//as the response is invalid, we need to ensure that other resources that claim to use the same software are still queried anyway.\n\t\t\t\t//(this is needed in case the one that we asked was spoofing)\n\t\t\t\tfor (ob = jcl->buddies; ob; ob = ob->next)\n\t\t\t\t{\n\t\t\t\t\tfor (or = ob->resources; or; or = or->next)\n\t\t\t\t\t{\n\t\t\t\t\t\t//ignore evil clients.\n\t\t\t\t\t\tif (r == or || (or->caps & (CAP_QUERYFAILED|CAP_QUERIED|CAP_QUERYING)) || !or->client_node)\n\t\t\t\t\t\t\tcontinue;\n\n\t\t\t\t\t\t//all resources with the same details then get the same caps flags.\n\t\t\t\t\t\tif (r->client_node && !strcmp(r->client_node, or->client_node) && !strcmp(r->client_ver, or->client_ver) && !strcmp(r->client_hash, or->client_hash) && !strcmp(r->client_ext, or->client_ext))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tJCL_CheckClientCaps(jcl, ob, or);\n\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn true;\n}\nvoid JCL_CheckClientCaps(jclient_t *jcl, buddy_t *buddy, bresource_t *bres)\n{\n\tbuddy_t *b;\n\tbresource_t *r;\n\tchar extname[64], *ext;\n\n\t//ignore it if we're already asking them...\n\tif (bres->caps & (CAP_QUERYING|CAP_QUERIED|CAP_QUERYFAILED))\n\t\treturn;\n\n\tbres->buggycaps = 0;\n\n#ifdef VOIP_LEGACY\n\tif (!bres->client_node || !bres->client_hash || !*bres->client_hash)\n\t{\n\t\t//one of google's nodes. ONLY google get this fucked up evil hack because they're the only ones that are arrogant enough to not bother to query what that 'ext' actually means - and then to not even bother to tell other clients.\n\t\t//every other client is expected to have its act together and not fuck up like this.\n\t\tif (bres->client_node && (!!strstr(bres->client_node, \"google.com\") || !!strstr(bres->client_node, \"android.com\")))\n\t\t{\n\t\t\tfor (ext = bres->client_ext; ext; )\n\t\t\t{\n\t\t\t\text = JCL_ParseOut(ext, extname, sizeof(extname));\n\t\t\t\tif (googlefuckedup)\n\t\t\t\t{\n\t\t\t\t\t//work around repeated bugs in google's various clients.\n\t\t\t\t\tif (!strcmp(extname, \"voice-v1\"))\n\t\t\t\t\t\tbres->buggycaps |= CAP_GOOGLE_VOICE;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n#endif\n\n\t//look for another resource that we might already know about that is the same client+ver and thus has the same caps.\n\tif (bres->client_node)\n\t\tfor (b = jcl->buddies; b; b = b->next)\n\t\t{\n\t\t\tfor (r = b->resources; r; r = r->next)\n\t\t\t{\n\t\t\t\tif (r == bres)\n\t\t\t\t\tcontinue;\n\t\t\t\t//ignore evil clients.\n\t\t\t\tif ((r->caps & CAP_QUERYFAILED) || !r->client_node)\n\t\t\t\t\tcontinue;\n\n\t\t\t\tif (!strcmp(r->client_node, bres->client_node) && !strcmp(r->client_ver, bres->client_ver) && !strcmp(r->client_hash, bres->client_hash) && !strcmp(r->client_ext, bres->client_ext))\n\t\t\t\t{\n\t\t\t\t\tif (r->caps & CAP_QUERIED)\n\t\t\t\t\t{\n\t\t\t\t\t\tbres->caps = r->caps;\n//\t\t\t\t\t\tCon_Printf(\"%s/%s matches (known) %s/%s (%s#%s)\\n\", buddy->accountdomain, bres->resource, b->accountdomain, r->resource, r->client_node, r->client_ver);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tif (r->caps & CAP_QUERYING)\n\t\t\t\t\t{\n\t\t\t\t\t\t//we're already asking one client with the same software. don't ask for dupes.\n\t\t\t\t\t\tbres->caps = 0;\n//\t\t\t\t\t\tCon_Printf(\"%s/%s matches (pending) %s/%s (%s#%s)\\n\", buddy->accountdomain, bres->resource, b->accountdomain, r->resource, r->client_node, r->client_ver);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n//\tCon_Printf(\"%s/%s querying (%s#%s)\\n\", buddy->accountdomain, bres->resource, bres->client_node, bres->client_ver);\n\t//okay, this is the first time we've seen that software version. ask it what it supports, and hope we get a valid response...\n\tif (!bres->client_node || !bres->client_hash || !*bres->client_hash)\n\t{\n\t\t//if we cannot cache the result, don't bother asking.\n\t\t//this saves googletalk bugging out on us.\n\t\tbres->caps = CAP_QUERIED;\n\t}\n\telse\n\t{\n\t\tbres->caps = CAP_QUERYING;\n\n\t\t//ask for info about each extension too. which should only be used if the specified version isn't a hash.\n\t\tif (bres->client_hash && !*bres->client_hash)\n\t\t{\n\t\t\tfor (ext = bres->client_ext; ext; )\n\t\t\t{\n\t\t\t\text = JCL_ParseOut(ext, extname, sizeof(extname));\n\t\t\t\tif (*extname)\n\t\t\t\t\tJCL_SendIQf(jcl, JCL_ClientDiscoInfo, \"get\", va(\"%s/%s\", buddy->accountdomain, bres->resource), \"<query xmlns='http://jabber.org/protocol/disco#info' node='%s#%s'/>\", bres->client_node, extname);\n\t\t\t}\n\t\t}\n\t\tJCL_SendIQf(jcl, JCL_ClientDiscoInfo, \"get\", va(\"%s/%s\", buddy->accountdomain, bres->resource), \"<query xmlns='http://jabber.org/protocol/disco#info' node='%s#%s'/>\", bres->client_node, bres->client_ver);\n\t}\n}\nvoid JCL_ParsePresence(jclient_t *jcl, xmltree_t *tree)\n{\n\tbuddy_t *buddy;\n\tbresource_t *bres;\n\n\tconst char *from = XML_GetParameter(tree, \"from\", jcl->domain);\n\txmltree_t *show = XML_ChildOfTree(tree, \"show\", 0);\n\txmltree_t *status = XML_ChildOfTree(tree, \"status\", 0);\n\txmltree_t *quake = XML_ChildOfTree(tree, \"quake\", 0);\n\t//xmltree_t *mucmain = XML_ChildOfTreeNS(tree, \"http://jabber.org/protocol/muc\", \"x\", 0);\n\txmltree_t *mucuser = XML_ChildOfTreeNS(tree, \"http://jabber.org/protocol/muc#user\", \"x\", 0);\n\txmltree_t *caps = XML_ChildOfTreeNS(tree, \"http://jabber.org/protocol/caps\", \"c\", 0);\n\tconst char *type = XML_GetParameter(tree, \"type\", \"\");\n\tconst char *serverip = NULL;\n\tconst char *servermap = NULL;\n\tchar startconvo[512];\n\tchar oldbstatus[128];\n\tchar oldfstatus[128];\n\n\t//should really be using pep for this.\n\tif (quake && !strcmp(quake->xmlns, \"fteqw.com:game\") && (jcl->enabledcapabilities & CAP_GAMEINVITE))\n\t{\n\t\tserverip = XML_GetParameter(quake, \"serverip\", NULL);\n\t\tservermap = XML_GetParameter(quake, \"servermap\", NULL);\n\t}\n\n\tjclient_updatebuddylist = true;\n\n\tif (type && !strcmp(type, \"error\"))\n\t{\n\t\tJCL_FindBuddy(jcl, from, &buddy, &bres, false);\n\t\tif (buddy && bres)\n\t\t{\n\t\t\txmltree_t *error = XML_ChildOfTree(tree, \"error\", 0);\n\t\t\tif (error->child)\n\t\t\t{\n\t\t\t\tQ_strlcpy(bres->fstatus, error->child->name, sizeof(bres->fstatus));\n\t\t\t}\n\t\t}\n\t}\n\telse if (type && !strcmp(type, \"subscribe\"))\n\t{\n\t\t//they want us to let them see us.\n\t\tchar pauth[512], pdeny[512];\n\t\tJCL_GenLink(jcl, startconvo, sizeof(startconvo), NULL, from, NULL, NULL, \"%s\", from);\n\t\tJCL_GenLink(jcl, pauth, sizeof(pauth), \"pauth\", from, NULL, NULL, \"%s\", \"Authorize\");\n\t\tJCL_GenLink(jcl, pdeny, sizeof(pdeny), \"pdeny\", from, NULL, NULL, \"%s\", \"Deny\");\n\t\tCon_Printf(\"%s wants to be your friend! %s %s\\n\", startconvo, pauth, pdeny);\n\t}\n\telse if (type && !strcmp(type, \"subscribed\"))\n\t{\n\t\t//they allowed us to add them.\n\t\tJCL_GenLink(jcl, startconvo, sizeof(startconvo), NULL, from, NULL, NULL, \"%s\", from);\n\t\tCon_Printf(\"%s is now your friend!\\n\", startconvo);\n\t}\n\telse if (type && !strcmp(type, \"unsubscribe\"))\n\t{\n\t\t//they removed us.\n\t\tJCL_GenLink(jcl, startconvo, sizeof(startconvo), NULL, from, NULL, NULL, \"%s\", from);\n\t\tCon_Printf(\"%s has unfriended you\\n\", startconvo);\n\t}\n\telse if (type && !strcmp(type, \"unsubscribed\"))\n\t{\n\t\t//we removed them.\n\t\tJCL_GenLink(jcl, startconvo, sizeof(startconvo), NULL, from, NULL, NULL, \"%s\", from);\n\t\tCon_Printf(\"%s is no longer your friend\\n\", startconvo);\n\t}\n\telse\n\t{\n\t\tJCL_FindBuddy(jcl, from, &buddy, &bres, true);\n\t\tif (!bres)\n\t\t{\n\t\t\tJCL_FindBuddy(jcl, va(\"%s/\", from), &buddy, &bres, true);\n\t\t}\n\t\tJCL_GenLink(jcl, startconvo, sizeof(startconvo), NULL, from, NULL, NULL, \"%s\", buddy->name);\n\n\t\tif (bres)\n\t\t{\n\t\t\tif (servermap)\n\t\t\t{\n\t\t\t\tbres->servertype = 2;\n\t\t\t\tQ_strlcpy(bres->server, servermap, sizeof(bres->server));\n\t\t\t}\n\t\t\telse if (serverip)\n\t\t\t{\n\t\t\t\tbres->servertype = 1;\n\t\t\t\tQ_strlcpy(bres->server, serverip, sizeof(bres->server));\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tbres->servertype = 0;\n\t\t\t\tQ_strlcpy(bres->server, \"\", sizeof(bres->server));\n\t\t\t}\n\n\t\t\tQ_strlcpy(oldbstatus, bres->bstatus, sizeof(oldbstatus));\n\t\t\tQ_strlcpy(oldfstatus, bres->fstatus, sizeof(oldfstatus));\n\n\t\t\tQ_strlcpy(bres->fstatus, (status && *status->body)?status->body:\"\", sizeof(bres->fstatus));\n\t\t\tif (!tree->child)\n\t\t\t{\n\t\t\t\tQ_strlcpy(bres->bstatus, \"offline\", sizeof(bres->bstatus));\n\t\t\t\tbres->caps = 0;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\txmltree_t *vcu;\n\t\t\t\tconst char *photohash;\n\t\t\t\tbuddy_t *me;\n\t\t\t\tvcu = XML_ChildOfTreeNS(tree, \"vcard-temp:x:update\", \"x\", 0);\n\t\t\t\tphotohash = XML_GetChildBody(vcu, \"photo\", buddy->vcardphotohash);\n\t\t\t\tif (strcmp(buddy->vcardphotohash, photohash))\n\t\t\t\t{\n\t\t\t\t\tCon_DPrintf(\"%s changed their photo from \\\"%s\\\" to \\\"%s\\\"\\n\", from, buddy->vcardphotohash, photohash);\n\n\t\t\t\t\tQ_strlcpy(buddy->vcardphotohash, photohash, sizeof(buddy->vcardphotohash));\n\t\t\t\t\tbuddy->vcardphotochanged = true;\n\t\t\t\t}\n\n\t\t\t\tJCL_FindBuddy(jcl, jcl->fulljid, &me, NULL, true);\n\t\t\t\tif (buddy == me)\n\t\t\t\t{\n\t\t\t\t\tif (strcmp(buddy->vcardphotohash, jcl->vcardphotohash))\n\t\t\t\t\t{\t//if one of your other resources changed its image, we need to retrieve it so we can tell other clients about it if the other resource goes away\n\t\t\t\t\t\tjcl->vcardphotohashstatus = VCP_UNKNOWN;\n\t\t\t\t\t\tJCL_SendIQf(jcl, JCL_MyVCardReply, \"get\", NULL, \"<vCard xmlns='vcard-temp'/>\");\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tQ_strlcpy(bres->bstatus, (show && *show->body)?show->body:\"present\", sizeof(bres->bstatus));\n\n\t\t\t\tif (caps)\n\t\t\t\t{\n\t\t\t\t\tconst char *ext = XML_GetParameter(caps, \"ext\", \"\");\t//deprecated\n\t\t\t\t\tconst char *ver = XML_GetParameter(caps, \"ver\", \"\");\n\t\t\t\t\tconst char *node = XML_GetParameter(caps, \"node\", \"\");\n\t\t\t\t\tconst char *hash = XML_GetParameter(caps, \"hash\", \"\");\n\n\t\t\t\t\tif (!bres->client_hash || strcmp(ext, bres->client_ext) || strcmp(hash, bres->client_hash) || strcmp(node, bres->client_node) || strcmp(ver, bres->client_ver))\n\t\t\t\t\t{\n\t\t\t\t\t\tbres->caps &= ~(CAP_QUERIED|CAP_QUERYING|CAP_QUERYFAILED);\t//no idea what the new caps are. \n\t\t\t\t\t\tfree(bres->client_ext);\n\t\t\t\t\t\tfree(bres->client_hash);\n\t\t\t\t\t\tfree(bres->client_node);\n\t\t\t\t\t\tfree(bres->client_ver);\n\t\t\t\t\t\tbres->client_ext = strdup(ext);\n\t\t\t\t\t\tbres->client_hash = strdup(hash);\n\t\t\t\t\t\tbres->client_node = strdup(node);\n\t\t\t\t\t\tbres->client_ver = strdup(ver);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tJCL_CheckClientCaps(jcl, buddy, bres);\n\t\t\t}\n\n\t\t\tif (mucuser)\n\t\t\t{\n\t\t\t\t//<item affiliation='' jid='' role=''/>\n\t\t\t\t//<status code='xxx'/>\n\t\t\t\t//100=public jids\n\t\t\t\t//101=affiliation changed\n\t\t\t\t//102=shows unavailable members\n\t\t\t\t//103=no longer shows unavailable\n\t\t\t\t//104=room config changed\n\t\t\t\t//110=this is you\n\t\t\t\t//170=room logging enabled\n\t\t\t\t//171=room logging disabled\n\t\t\t\t//172=now annon\n\t\t\t\t//173=now semi-anon\n\t\t\t\t//201=new room created\n\t\t\t\t//210=roomnick changed\n\t\t\t\t//301=you're banned\n\t\t\t\t//303=\n\t\t\t\t//307=you're kicked\n\t\t\t\t//321=removed by affiliation change\n\t\t\t\t//322=not a member\n\t\t\t\t//332=shutdown\n\t\t\t\tJCL_GenLink(jcl, startconvo, sizeof(startconvo), NULL, from, NULL, NULL, \"%s\", bres->resource);\n\t\t\t\tif (type && !strcmp(type, \"unavailable\"))\n\t\t\t\t\tXMPP_ConversationPrintf(buddy->name, buddy->name, false, \"%s has left the conversation\\n\", bres->resource);\n\t\t\t\telse if (strcmp(oldbstatus, bres->bstatus))\n\t\t\t\t\tXMPP_ConversationPrintf(buddy->name, buddy->name, false, \"%s is now %s\\n\", startconvo, bres->bstatus);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tchar *conv = buddy->accountdomain;\n\t\t\t\tchar *title = buddy->name;\n\n\t\t\t\t//if we're not currently talking with them, put the status update into the main console instead (which will probably then get dropped).\n\t\t\t\tif (!confuncs || confuncs->GetConsoleFloat(conv, \"iswindow\") != true)\n\t\t\t\t\tconv = \"\";\n\n\t\t\t\tif (bres->servertype == 2)\n\t\t\t\t{\n\t\t\t\t\tchar joinlink[512];\n\t\t\t\t\tJCL_GenLink(jcl, joinlink, sizeof(joinlink), \"join\", from, NULL, NULL, \"Playing Quake - %s\", bres->server);\n\t\t\t\t\tXMPP_ConversationPrintf(conv, title, false, \"%s is now %s\\n\", startconvo, joinlink);\n\t\t\t\t}\n\t\t\t\telse if (bres->servertype == 1)\n\t\t\t\t\tXMPP_ConversationPrintf(conv, title, false, \"%s is now ^[[Playing Quake - %s]\\\\observe\\\\%s^]\\n\", startconvo, bres->server, bres->server);\n\t\t\t\telse if ((cvarfuncs->GetFloat(\"xmpp_showstatusupdates\")||*conv) && (strcmp(oldbstatus, bres->bstatus) || strcmp(oldfstatus, bres->fstatus)))\n\t\t\t\t{\n\t\t\t\t\tif (*bres->fstatus)\n\t\t\t\t\t\tXMPP_ConversationPrintf(conv, title, false, \"%s is now %s: %s\\n\", startconvo, bres->bstatus, bres->fstatus);\n\t\t\t\t\telse\n\t\t\t\t\t\tXMPP_ConversationPrintf(conv, title, false, \"%s is now %s\\n\", startconvo, bres->bstatus);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (type && !strcmp(type, \"unavailable\"))\n\t\t\t{\n\t\t\t\t//remove this buddy resource\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tCon_Printf(\"Weird presence:\\n\");\n\t\t\tXML_ConPrintTree(tree, \"\", 0);\n\t\t}\n\t}\n}\n\n#define JCL_DONE 0\t\t\t//no more data available for now.\n#define JCL_CONTINUE 1\t\t//more data needs parsing.\n#define JCL_KILL 2\t\t\t//some error, needs reconnecting.\n#define JCL_NUKEFROMORBIT 3\t//permanent error (or logged on from elsewhere)\nint JCL_ClientFrame(jclient_t *jcl, char **error)\n{\n\tint pos;\n\txmltree_t *tree, *ot;\n\tint ret;\n\tqboolean unparsable;\n\n\tret = netfuncs->Recv(jcl->socket, jcl->bufferedinmessage+jcl->bufferedinammount, sizeof(jcl->bufferedinmessage)-1 - jcl->bufferedinammount);\n\tif (ret == 0)\n\t{\n\t\tif (!jcl->bufferedinammount)\t//if we are half way through a message, read any possible conjunctions.\n\t\t\treturn JCL_DONE;\t//nothing more this frame\n\t}\n\tif (ret < 0)\n\t{\n\t\t*error = \"Socket Error\";\n\t\tif (jcl->socket != -1)\n\t\t\tnetfuncs->Close(jcl->socket);\n\t\tjcl->socket = -1;\n\t\treturn JCL_KILL;\n\t}\n\n\tif (ret>0)\n\t{\n\t\tjcl->bufferedinammount+=ret;\n\t\tjcl->bufferedinmessage[jcl->bufferedinammount] = 0;\n\t}\n\n\t//we never end parsing in the middle of a < >\n\t//this means we can filter out the <? ?>, <!-- --> and < /> stuff properly\n\tfor (pos = jcl->instreampos; pos < jcl->bufferedinammount; pos++)\n\t{\n\t\tif (jcl->bufferedinmessage[pos] == '<')\n\t\t{\n\t\t\tjcl->instreampos = pos;\n\t\t}\n\t\telse if (jcl->bufferedinmessage[pos] == '>')\n\t\t{\n\t\t\tif (pos < 1)\n\t\t\t\tbreak;\t//erm...\n\n/*\t\t\tif (jcl->bufferedinmessage[pos-1] != '/')\t//<blah/> is a tag without a body\n\t\t\t{\n\t\t\t\tif (jcl->bufferedinmessage[jcl->instreampos+1] != '?')\t//<? blah ?> is a tag without a body\n\t\t\t\t{\n\t\t\t\t\tif (jcl->bufferedinmessage[pos-1] != '?')\n\t\t\t\t\t{\n\t\t\t\t\t\tif (jcl->bufferedinmessage[jcl->instreampos+1] == '/')\t//</blah> is the end of a tag with a body\n\t\t\t\t\t\t\tjcl->tagdepth--;\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tjcl->tagdepth++;\t\t\t//<blah> is the start of a tag with a body\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n*/\n\t\t\tjcl->instreampos=pos+1;\n\t\t}\n\t}\n\n\tpos = 0;\n\twhile (jcl->connecting)\n\t{\t//first bit of info\n\t\ttree = XML_Parse(jcl->bufferedinmessage, &pos, jcl->instreampos, true, \"\");\n\t\tif (tree && !strcmp(tree->name, \"?xml\"))\n\t\t{\n\t\t\tXML_Destroy(tree);\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (jcl->streamdebug == 2)\n\t\t{\n\t\t\tchar t = jcl->bufferedinmessage[pos];\n\t\t\tjcl->bufferedinmessage[pos] = 0;\n\t\t\tXMPP_ConversationPrintf(\"xmppin\", \"xmppin\", false, \"%s\", jcl->bufferedinmessage);\n\t\t\tif (tree)\n\t\t\t\tXMPP_ConversationPrintf(\"xmppin\", \"xmppin\", false, \"\\n\");\n\t\t\tjcl->bufferedinmessage[pos] = t;\n\t\t}\n\n\t\tif (!tree)\n\t\t{\n\t\t\t*error = \"Not an xml stream\";\n\t\t\treturn JCL_KILL;\n\t\t}\n\t\tif (strcmp(tree->name, \"stream\") || strcmp(tree->xmlns, \"http://etherx.jabber.org/streams\"))\n\t\t{\n\t\t\t*error = \"Not an xmpp stream\";\n\t\t\treturn JCL_KILL;\n\t\t}\n\t\tQ_strlcpy(jcl->defaultnamespace, tree->xmlns_dflt, sizeof(jcl->defaultnamespace));\n\n\t\tot = tree;\n\t\ttree = tree->child;\n\t\tot->child = NULL;\n\n//\t\tCon_Printf(\"Discard\\n\");\n//\t\tXML_ConPrintTree(ot, 0);\n\t\tXML_Destroy(ot);\n\n\t\tjcl->connecting = false;\n\t}\n\n/*\tif (jcl->tagdepth != 1)\n\t{\n\t\tif (jcl->tagdepth < 1 && jcl->bufferedinammount==jcl->instreampos)\n\t\t{\n\t\t\t*error = \"End of XML stream\";\n\t\t\treturn JCL_KILL;\n\t\t}\n\t\treturn JCL_DONE;\n\t}\n*/\n\ttree = XML_Parse(jcl->bufferedinmessage, &pos, jcl->instreampos, false, jcl->defaultnamespace);\n\n\tif (jcl->streamdebug == 2 && tree)\n\t{\n\t\tchar t = jcl->bufferedinmessage[pos];\n\t\tjcl->bufferedinmessage[pos] = 0;\n\t\tXMPP_ConversationPrintf(\"xmppin\", \"xmppin\", false, \"%s\", jcl->bufferedinmessage);\n\t\tXMPP_ConversationPrintf(\"xmppin\", \"xmppin\", false, \"\\n\");\n\t\tjcl->bufferedinmessage[pos] = t;\n\t}\n\n\tif (!tree)\n\t{\n\t\t//make sure any prior crap is flushed.\n\t\tmemmove(jcl->bufferedinmessage, jcl->bufferedinmessage+pos, jcl->bufferedinammount-pos);\n\t\tjcl->bufferedinammount -= pos;\n\t\tjcl->instreampos -= pos;\n\t\tpos = 0;\n\n//\t\t\tCon_Printf(\"No input tree: %s\", jcl->bufferedinmessage);\n\t\treturn JCL_DONE;\n\t}\n\n//\tCon_Printf(\"read\\n\");\n//\tXML_ConPrintTree(tree, 0);\n\n\tif (jcl->streamdebug == 1)\n\t{\n\t\tXMPP_ConversationPrintf(\"xmppin\", \"xmppin\", false, \"\");\n\t\tXML_ConPrintTree(tree, \"xmppin\", 0);\n\t}\n\n\tjcl->timeout = jclient_curtime + 60*1000;\n\n\tunparsable = true;\n\tif (!strcmp(tree->name, \"features\"))\n\t{\n\t\tif (Q_snprintfz(jcl->barejid, sizeof(jcl->barejid), \"%s@%s\", jcl->username, jcl->domain) ||\n\t\t\tQ_snprintfz(jcl->fulljid, sizeof(jcl->fulljid), \"%s@%s/%s\", jcl->username, jcl->domain, jcl->resource))\n\t\t{\n\t\t\tXML_Destroy(tree);\n\t\t\treturn JCL_KILL;\n\t\t}\n\t\tif ((ot=XML_ChildOfTree(tree, \"bind\", 0)))\n\t\t{\n\t\t\tunparsable = false;\n\t\t\tJCL_SendIQf(jcl, JCL_BindReply, \"set\", NULL, \"<bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'><resource>%s</resource></bind>\", jcl->resource);\n\t\t}\n\t\tif ((ot=XML_ChildOfTree(tree, \"session\", 0)))\n\t\t{\n\t\t\tunparsable = false;\n\t\t\tJCL_SendIQf(jcl, JCL_SessionReply, \"set\", NULL, \"<session xmlns='urn:ietf:params:xml:ns:xmpp-session'/>\");\n\t\t\tjcl->connected = true;\n\n\t\t\tJCL_WriteConfig();\n\t\t}\n\n\n\t\tif (unparsable)\n\t\t{\n\t\t\tif ((!jcl->issecure) && XML_ChildOfTree(tree, \"starttls\", 0) != NULL && jcl->forcetls >= 0)\n\t\t\t{\n\t\t\t\tCon_DPrintf(\"XMPP: Attempting to switch to TLS\\n\");\n\t\t\t\tJCL_AddClientMessageString(jcl, \"<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls' />\");\n\t\t\t\tunparsable = false;\n\t\t\t}\n\t\t\telse if ((ot=XML_ChildOfTree(tree, \"mechanisms\", 0)))\n\t\t\t{\n\t\t\t\txmltree_t *m;\n\t\t\t\tint sm;\n\t\t\t\tchar out[512];\n\t\t\t\tchar *method = NULL;\n\t\t\t\tint outlen = -1;\n\t\t\t\tqboolean needpass = false;\n\t\t\t\tif (jcl->forcetls > 0 && !jcl->issecure)\n\t\t\t\t{\n\t\t\t\t\t*error = \"Unable to switch to TLS. You are probably being man-in-the-middle attacked.\";\n\t\t\t\t\tXML_ConPrintTree(tree, \"\", 0);\n\t\t\t\t\tXML_Destroy(tree);\n\t\t\t\t\treturn JCL_KILL;\n\t\t\t\t}\n\n\t\t\t\t//init some sasl fields from the jcl state.\n\t\t\t\tjcl->sasl.username = jcl->username;\t//auth user name\n\t\t\t\tjcl->sasl.domain = jcl->domain;\t\t//auth domain\n\t\t\t\tjcl->sasl.issecure = jcl->issecure;\t//says that its connected over tls, and that sock is the tls connection.\n\t\t\t\tjcl->sasl.socket = jcl->socket;\t\t\t//just for channel bindings\n\t\t\t\tjcl->sasl.authmethod = NULL;\t\t//unknown at this point\n\n\t\t\t\t//attempt to use a method based on the ones that we prefer.\n\t\t\t\tfor (sm = 0; sm < sizeof(saslmethods)/sizeof(saslmethods[0]); sm++)\n\t\t\t\t{\n\t\t\t\t\tmethod = saslmethods[sm].method?saslmethods[sm].method:jcl->sasl.oauth2.saslmethod;\n\t\t\t\t\tif (!*method)\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\tfor (m = ot->child; m; m = m->sibling)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (!strcmp(m->body, method))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\toutlen = saslmethods[sm].sasl_initial(&jcl->sasl, out, sizeof(out));\n\t\t\t\t\t\t\tif (outlen >= 0)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tjcl->sasl.authmethod = &saslmethods[sm];\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (outlen == -2)\n\t\t\t\t\t\t\t\tneedpass = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif (outlen >= 0)\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tif (outlen < 0 || !jcl->sasl.authmethod)\n\t\t\t\t{\n\t\t\t\t\tXML_Destroy(tree);\n\t\t\t\t\t//can't authenticate for some reason\n\t\t\t\t\tif (needpass)\n\t\t\t\t\t\tXMPP_Menu_Password(jcl);\n\t\t\t\t\t*error = \"Password needed\";\n\t\t\t\t\treturn JCL_NUKEFROMORBIT;\n\t\t\t\t}\n\n\t\t\t\tif (outlen >= 0)\n\t\t\t\t{\n\t\t\t\t\tBase64_Add(out, outlen);\n\t\t\t\t\tBase64_Finish();\n\n\t\t\t\t\tCon_DPrintf(\"XMPP: Authing with %s%s.\\n\", method, jcl->issecure?\" over tls\":\" without encription\");\n\t\t\t\t\tJCL_AddClientMessagef(jcl, \"<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='%s'\"\n\t\t\t\t\t\t\t\" auth:service='oauth2'\"\n\t\t\t\t\t\t\t\" xmlns:auth='http://www.google.com/talk/protocol/auth'\"\n\t\t\t\t\t\t\t\">%s</auth>\", method, base64);\n\t\t\t\t\tunparsable = false;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t*error = \"No suitable auth methods\";\n\t\t\t\t\tXML_ConPrintTree(tree, \"\", 0);\n\t\t\t\t\tXML_Destroy(tree);\n\t\t\t\t\treturn JCL_KILL;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\t//we cannot auth, no suitable method.\n\t\t\t{\n\t\t\t\t*error = \"Neither SASL or TLS are usable\";\n\t\t\t\tXML_Destroy(tree);\n\t\t\t\treturn JCL_KILL;\n\t\t\t}\n\t\t}\n\t}\n\telse if (!strcmp(tree->name, \"challenge\") && !strcmp(tree->xmlns, \"urn:ietf:params:xml:ns:xmpp-sasl\") && jcl->sasl.authmethod)\n\t{\n\t\tchar in[512];\n\t\tint inlen;\n\t\tchar out[512];\n\t\tint outlen;\n\t\tinlen = Base64_Decode(in, sizeof(in), tree->body, strlen(tree->body));\n\t\toutlen = jcl->sasl.authmethod->sasl_challenge(&jcl->sasl, in, inlen, out, sizeof(out));\n\t\tif (outlen < 0)\n\t\t{\n\t\t\t*error = \"Unable to auth with server\";\n\t\t\tXML_Destroy(tree);\n\t\t\treturn JCL_KILL;\n\t\t}\n\t\tBase64_Add(out, outlen);\n\t\tBase64_Finish();\n\t\tJCL_AddClientMessagef(jcl, \"<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>%s</response>\", base64);\n\t\tunparsable = false;\n\t}\n\telse if (!strcmp(tree->name, \"proceed\"))\n\t{\n\t\t//switch to TLS, if we can\n\n\t\t//Restart everything, basically.\n\t\tjcl->bufferedinammount = 0;\n\t\tjcl->instreampos = 0;\n\n\t\t//when using srv records, the certificate must match the user's domain, rather than merely the hostname of the server.\n\t\t//if you want to match the hostname of the server, use (oldstyle) tlsconnect directly instead.\n\t\tif (netfuncs->SetTLSClient(jcl->socket, jcl->certificatedomain)<0)\n\t\t{\n\t\t\t*error = \"failed to switch to TLS\";\n\t\t\tXML_Destroy(tree);\n\t\t\treturn JCL_KILL;\n\t\t}\n\t\tif (!*jcl->certificatedomain)\n\t\t\tCon_Printf(\"XMPP: WARNING: Connecting via TLS without validating certificate\\n\");\n\t\tjcl->issecure = true;\n\n\t\tjcl->connecting = true;\n\t\tJCL_AddClientMessageString(jcl,\n\t\t\t\"<?xml version='1.0' ?>\"\n\t\t\t\"<stream:stream to='\");\n\t\tJCL_AddClientMessageString(jcl, jcl->domain);\n\t\tJCL_AddClientMessageString(jcl, \"' xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' version='1.0'>\");\n\n\t\tXML_Destroy(tree);\n\t\treturn JCL_DONE;\n\t}\n\telse if (!strcmp(tree->name, \"failure\"))\n\t{\n\t\tif (tree->child)\n\t\t\t*error = va(\"Failure: %s\\n\", tree->child->name);\n\t\telse\n\t\t\t*error = \"Unknown failure\";\n\t\tXML_Destroy(tree);\n\t\treturn JCL_KILL;\n\t}\n\telse if (!strcmp(tree->name, \"error\"))\n\t{\n\t\txmltree_t *condition;\n\t\tcondition = XML_ChildOfTree(tree, \"success\", 0);\n\t\tif (!condition)\n\t\t\tcondition = XML_ChildOfTree(tree, \"see-other-host\", 0);\n\t\tif (!condition)\n\t\t\tcondition = XML_ChildOfTree(tree, \"invalid-xml\", 0);\n\n\t\tif (condition && !strcmp(condition->name, \"see-other-host\"))\n\t\t{\n\t\t\t//msn needs this, apparently\n\t\t\tQ_strlcpy(jcl->redirserveraddr, condition->body, sizeof(jcl->redirserveraddr));\n\t\t\tJCL_CloseConnection(jcl, \"Redirecting\", true);\n\t\t\tif (!JCL_Reconnect(jcl))\n\t\t\t{\n\t\t\t\t*error = \"Unable to redirect\";\n\t\t\t\treturn JCL_KILL;\n\t\t\t}\n\t\t\treturn JCL_CONTINUE;\n\t\t}\n\t\telse if (condition && !strcmp(condition->name, \"success\"))\n\t\t{\n\t\t\t*error = \"error: success\";\n\t\t\tunparsable = false;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tot = XML_ChildOfTree(tree, \"text\", 0);\n\t\t\tif (ot)\n\t\t\t\t*error = va(\"error: %s\", ot->body);\n\t\t\telse if (condition)\n\t\t\t\t*error = va(\"error: %s\", condition->name);\n\t\t\telse\n\t\t\t\t*error = \"Unknown error\";\n\n\t\t\tot = XML_ChildOfTree(tree, \"conflict\", 0);\n\t\t\tXML_Destroy(tree);\n\n\t\t\tif (ot)\n\t\t\t\treturn JCL_NUKEFROMORBIT;\n\t\t\telse\n\t\t\t\treturn JCL_KILL;\n\t\t}\n\t}\n\telse if (!strcmp(tree->name, \"success\") && !strcmp(tree->xmlns, \"urn:ietf:params:xml:ns:xmpp-sasl\"))\n\t{\n\t\tif (!jcl->sasl.authmethod || jcl->sasl.authmethod->sasl_success)\n\t\t{\n\t\t\tchar in[512];\n\t\t\tint inlen;\n\t\t\tinlen = Base64_Decode(in, sizeof(in), tree->body, strlen(tree->body));\n\t\t\tif (!jcl->sasl.authmethod || jcl->sasl.authmethod->sasl_success(&jcl->sasl, in, inlen) < 0)\n\t\t\t{\n\t\t\t\t*error = \"Server validation failed\";\n\t\t\t\tXML_Destroy(tree);\n\t\t\t\treturn JCL_KILL;\n\t\t\t}\n\t\t}\n\n\t\t//Restart everything, basically, AGAIN! (third time lucky?)\n\t\tjcl->bufferedinammount = 0;\n\t\tjcl->instreampos = 0;\n\n\t\tjcl->connecting = true;\n\t\tJCL_AddClientMessageString(jcl,\n\t\t\t\"<?xml version='1.0' ?>\"\n\t\t\t\"<stream:stream to='\");\n\t\tJCL_AddClientMessageString(jcl, jcl->domain);\n\t\tJCL_AddClientMessageString(jcl, \"' xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' version='1.0'>\");\n\n\t\treturn JCL_DONE;\n\t}\n\telse if (!strcmp(tree->name, \"iq\"))\n\t{\n\t\tJCL_ParseIQ(jcl, tree);\n\t\tunparsable = false;\n\t}\n\telse if (!strcmp(tree->name, \"message\"))\n\t{\n\t\tJCL_ParseMessage(jcl, tree);\n\t\tunparsable = false;\n\t}\n\telse if (!strcmp(tree->name, \"presence\"))\n\t{\n\t\tJCL_ParsePresence(jcl, tree);\n\t\t//we should keep a list of the people that we know of.\n\t\tunparsable = false;\n\t}\n\telse\n\t{\n\t\tCon_Printf(\"JCL unrecognised stanza: %s\\n\", tree->name);\n\t\tXML_ConPrintTree(tree, \"\", 0);\n\t}\n\n\tmemmove(jcl->bufferedinmessage, jcl->bufferedinmessage+pos, jcl->bufferedinammount-pos);\n\tjcl->bufferedinammount -= pos;\n\tjcl->instreampos -= pos;\n\n\tif (unparsable)\n\t{\n\t\t*error = \"Input corrupt, urecognised, or unusable.\";\n\t\tXML_ConPrintTree(tree, \"\", 0);\n\t\tXML_Destroy(tree);\n\t\treturn JCL_KILL;\n\t}\n\tXML_Destroy(tree);\n\treturn JCL_CONTINUE;\n}\n\nvoid JCL_CloseConnection(jclient_t *jcl, const char *reason, qboolean reconnect)\n{\n\t//send our signoff to the server, if we're still alive.\n\tif (jcl->status != JCL_DEAD && jcl->status != JCL_INACTIVE)\n\t\tCon_Printf(\"XMPP: Disconnected from %s@%s\\n\", jcl->username, jcl->domain);\n\n\tif (jcl->status == JCL_ACTIVE)\n\t\tJCL_AddClientMessageString(jcl, \"<presence type='unavailable'/>\");\n\tif (jcl->status != JCL_DEAD && jcl->status != JCL_INACTIVE)\n\t\tJCL_AddClientMessageString(jcl, \"</stream:stream>\");\n\tJCL_FlushOutgoing(jcl);\n\n\t//forget all our friends.\n\tJCL_ForgetBuddy(jcl, NULL, NULL);\n\n\t//destroy any data that never got sent\n\tfree(jcl->outbuf);\n\tjcl->outbuf = NULL;\n\tjcl->outbuflen = 0;\n\tjcl->outbufpos = 0;\n\tjcl->outbufmax = 0;\n\n\tif (jcl->socket != -1)\n\t\tnetfuncs->Close(jcl->socket);\n\tjcl->socket = -1;\n\tjcl->status = JCL_DEAD;\n\tQ_strncpyz(jcl->errormsg, reason, sizeof(jcl->errormsg));\n\n\tjcl->timeout = jclient_curtime + 30*1000;\t//wait 30 secs before reconnecting, to avoid flood-prot-protection issues.\n\n\tif (!reconnect)\n\t{\n\t\tint i;\n\t\tfree(jcl);\n\t\tfor (i = 0; i < sizeof(jclients)/sizeof(jclients[0]); i++)\n\t\t{\n\t\t\tif (jclients[i] == jcl)\n\t\t\t{\n\t\t\t\tjclients[i] = NULL;\n\t\t\t\tjclient_configdirty = true;\n\t\t\t}\n\t\t}\n\t}\n\n\tjclient_updatebuddylist = true;\n}\n\n//can be polled for server address updates\nvoid JCL_GeneratePresence(jclient_t *jcl, qboolean force)\n{\n\tint dummystat;\n\tchar serveraddr[1024*16];\n\tchar servermap[1024*16];\n\t//get the last server address\n\n\tserveraddr[0] = 0;\n\tservermap[0] = 0;\n\n\tif (!cvarfuncs->GetFloat(\"xmpp_nostatus\"))\n\t{\n\t\tif (cvarfuncs->GetFloat(\"sv.state\"))\n\t\t{\n\t\t\tcvarfuncs->GetString(\"sv.mapname\", servermap, sizeof(servermap));\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (!cvarfuncs->GetString(\"cl_serveraddress\", serveraddr, sizeof(serveraddr)))\n\t\t\t\tserveraddr[0] = 0;\n\t\t\tif (clientfuncs)\n\t\t\t{\n\t\t\t\t//if we can't get any stats, its because we're not actually on the server.\n\t\t\t\tif (!clientfuncs->GetStats(0, &dummystat, 1))\n\t\t\t\t\tserveraddr[0] = 0;\n\t\t\t}\n\t\t}\n\t}\n\n\tif (force || jcl->vcardphotohashchanged || strcmp(jcl->curquakeserver, *servermap?servermap:serveraddr))\n\t{\n\t\tchar *prior = \"<priority>24</priority>\";\n\t\tchar caps[512];\n\t\tjcl->vcardphotohashchanged = false;\n\t\tQ_strlcpy(jcl->curquakeserver, *servermap?servermap:serveraddr, sizeof(jcl->curquakeserver));\n\t\tCon_DPrintf(\"Sending presence %s\\n\", jcl->curquakeserver);\n\n\t\tbuildcapsvcardpresence(jcl, caps, sizeof(caps));\n\n\t\tif (!*jcl->curquakeserver)\n\t\t\tJCL_AddClientMessagef(jcl,\n\t\t\t\t\t\"<presence from='%s'>\"\n\t\t\t\t\t\t\"%+s\"\n\t\t\t\t\t\t\"%+s\"\n\t\t\t\t\t\"</presence>\", jcl->fulljid, prior, caps);\n\t\telse if (*servermap)\t//if we're running a server, say so\n\t\t\tJCL_AddClientMessagef(jcl, \n\t\t\t\t\t\t\"<presence from='%s'>\"\n\t\t\t\t\t\t\t\"%+s\"\n\t\t\t\t\t\t\t\"<quake xmlns='fteqw.com:game' servermap='%s'/>\"\n\t\t\t\t\t\t\t\"%+s\"\n\t\t\t\t\t\t\"</presence>\"\n\t\t\t\t\t\t, jcl->fulljid, prior, servermap, caps);\n\t\telse\t//if we're connected to a server, say so\n\t\t\tJCL_AddClientMessagef(jcl, \n\t\t\t\t\t\t\"<presence from='%s'>\"\n\t\t\t\t\t\t\t\"%+s\"\n\t\t\t\t\t\t\t\"<quake xmlns='fteqw.com:game' serverip='%s' />\"\n\t\t\t\t\t\t\t\"%+s\"\n\t\t\t\t\t\t\"</presence>\"\n\t\t\t\t, jcl->fulljid, prior, jcl->curquakeserver, caps);\n\t}\n}\n\nstatic void JCL_PrintBuddyStatus(char *console, jclient_t *jcl, buddy_t *b, bresource_t *r)\n{\n\tif (r->servertype == 2)\n\t{\n\t\tchar joinlink[512];\n\t\tJCL_GenLink(jcl, joinlink, sizeof(joinlink), \"join\", b->accountdomain, r->resource, NULL, \"Playing Quake - %s\", r->server);\n\t\tCon_SubPrintf(console, \"%s\", joinlink);\n\t}\n\telse if (r->servertype)\n\t\tCon_SubPrintf(console, \"^[[Playing Quake - %s]\\\\observe\\\\%s^]\", r->server, r->server);\n\telse if (*r->fstatus)\n\t\tCon_SubPrintf(console, \"%s - %s\", r->bstatus, r->fstatus);\n\telse\n\t\tCon_SubPrintf(console, \"%s\", r->bstatus);\n\n\tif ((r->caps & CAP_GAMEINVITE) && !r->servertype)\n\t{\n\t\tchar invitelink[512];\n\t\tJCL_GenLink(jcl, invitelink, sizeof(invitelink), \"invite\", b->accountdomain, r->resource, NULL, \"%s\", \"Invite\");\n\t\tCon_SubPrintf(console, \" %s\", invitelink);\n\t}\n\tif (r->caps & CAP_VOICE)\n\t{\n\t\tchar calllink[512];\n\t\tJCL_GenLink(jcl, calllink, sizeof(calllink), \"call\", b->accountdomain, r->resource, NULL, \"%s\", \"Call\");\n\t\tCon_SubPrintf(console, \" %s\", calllink);\n\t}\n\telse if ((r->caps|r->buggycaps) & CAP_GOOGLE_VOICE)\n\t{\n\t\tchar calllink[512];\n\t\tJCL_GenLink(jcl, calllink, sizeof(calllink), \"call\", b->accountdomain, r->resource, NULL, \"%s\", \"Call\");\n\t\tCon_SubPrintf(console, \" %s\", calllink);\n\t}\n}\nstatic void JCL_RegenerateBuddyList(qboolean force)\n{\n\tconst char *console = BUDDYLISTTITLE;\n\tjclient_t *jcl;\n\tbuddy_t *b, *me;\n\tbresource_t *r;\n\tint i, j;\n\tchar convolink[512];\n\tstruct c2c_s *c2c;\n\n\tbuddy_t *sortlist[256];\n\tint buds;\n\n\tjclient_updatebuddylist = false;\n\n\tif (!confuncs)\n\t\treturn;\n\n\t//only redraw the window if it actually exists. if they closed it, then don't mess things up.\n\tif (!force && confuncs->GetConsoleFloat(console, \"iswindow\") <= 0)\n\t\treturn;\n\n\tif (confuncs->GetConsoleFloat(console, \"iswindow\") != true)\n\t{\n\t\tconfuncs->SetConsoleFloat(console, \"iswindow\", true);\n\t\tconfuncs->SetConsoleFloat(console, \"forceutf8\", true);\n\t\tconfuncs->SetConsoleFloat(console, \"linebuffered\", false);\n\t\tconfuncs->SetConsoleFloat(console, \"wnd_x\", pvid.width - 256);\n\t\tconfuncs->SetConsoleFloat(console, \"wnd_y\", true);\n\t\tconfuncs->SetConsoleFloat(console, \"wnd_w\", 256);\n\t\tconfuncs->SetConsoleFloat(console, \"wnd_h\", pvid.height);\n\t}\n\tconfuncs->SetConsoleFloat(console, \"linecount\", 0);\t//clear it\n\tif (force)\n\t\tconfuncs->SetActive(console);\n\n\tfor (i = 0; i < sizeof(jclients)/sizeof(jclients[0]); i++)\n\t{\n\t\tjcl = jclients[i];\n\t\tif (!jcl)\n\t\t\tcontinue;\n\t\tif (*jcl->localalias && *jcl->localalias != '>')\n\t\t\tCon_SubPrintf(console, \"\\n\"COL_NAME_US\"%s\\n\", jcl->localalias);\n\t\telse\n\t\t\tCon_SubPrintf(console, \"\\n\"COL_NAME_US\"%s@%s\\n\", jcl->username, jcl->domain);\n\t\tif (jcl->status == JCL_INACTIVE)\n\t\t\tCon_SubPrintf(console, \"Not connected.\\n\", jcl->accountnum);\n\t\telse if (jcl->status == JCL_DEAD)\n\t\t\tCon_SubPrintf(console, \"%s.\\n\", *jcl->errormsg?jcl->errormsg:\"Connect failed\", jcl->accountnum);\n\t\telse if (jcl->status == JCL_AUTHING)\n\t\t\tCon_SubPrintf(console, \"Connecting... Please wait.\\n\");\n\n\t\tJCL_GenLink(jcl, convolink, sizeof(convolink), \"accopts\", NULL, NULL, NULL, \"%s\", \"Options\");\n\t\tCon_SubPrintf(console, \"%s\\n\", convolink);\n/*\n\t\tif (jcl->status == JCL_INACTIVE)\n\t\t\tJCL_GenLink(jcl, convolink, sizeof(convolink), \"forgetacc\", NULL, NULL, NULL, \"%s\", \"Forget Account\");\n\t\telse if (jcl->status == JCL_DEAD)\n\t\t\tJCL_GenLink(jcl, convolink, sizeof(convolink), \"disconnect\", NULL, NULL, NULL, \"%s\", \"Disable\");\n\t\telse\n\t\t\tJCL_GenLink(jcl, convolink, sizeof(convolink), \"disconnect\", NULL, NULL, NULL, \"%s\", \"Disconnect\");\n\t\tCon_SubPrintf(console, \"%s\", convolink);\n\t\tif (jcl->status == JCL_INACTIVE)\n\t\t{\n\t\t\tJCL_GenLink(jcl, convolink, sizeof(convolink), \"connect\", NULL, NULL, NULL, \"%s\", \"Connect\");\n\t\t\tCon_SubPrintf(console, \" %s\", convolink);\n\t\t}\n\t\telse if (jcl->status == JCL_DEAD)\n\t\t{\n\t\t\tJCL_GenLink(jcl, convolink, sizeof(convolink), \"connect\", NULL, NULL, NULL, \"%s\", \"Reconnect\");\n\t\t\tCon_SubPrintf(console, \" %s\", convolink);\n\t\t}\n*/\n\n\t\tfor (c2c = jcl->c2c; c2c; c2c = c2c->next)\n\t\t\tc2c->displayed = false;\n\n\t\tif (jcl->status != JCL_ACTIVE)\n\t\t\tCon_SubPrintf(console, \"\\n\");\n\t\telse\n\t\t{\n\t\t\tqboolean youarealoner = true;\n\n//\t\t\tJCL_GenLink(jcl, convolink, sizeof(convolink), \"addfriend\", NULL, NULL, NULL, \"%s\", \"Add Friend\");\n//\t\t\tCon_SubPrintf(console, \" %s\\n\", convolink);\n\n\t\t\tJCL_FindBuddy(jcl, jcl->fulljid, &me, NULL, true);\n\n\t\t\tfor (b = jcl->buddies, buds = 0; b && buds < sizeof(sortlist)/sizeof(sortlist[0]); b = b->next)\n\t\t\t{\n\t\t\t\tif (b == me)\n\t\t\t\t\tcontinue;\n\t\t\t\tif (!b->resources)\t//can't be online.\n\t\t\t\t\tcontinue;\n\t\t\t\tif (b->btype == BT_UNKNOWN)\n\t\t\t\t\tcontinue;\t\t//don't list people we don't actually know\n\n\t\t\t\tfor (j = buds; j > 0; j--)\n\t\t\t\t{\n\t\t\t\t\tif (strcasecmp(sortlist[j-1]->name, b->name) >= 0)\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tsortlist[j] = sortlist[j-1];\n\t\t\t\t}\n\t\t\t\tbuds++;\n\t\t\t\tsortlist[j] = b;\n\t\t\t}\n\t\t\tCon_SubPrintf(console, \"\\n\");\n\n\t\t\twhile(buds --> 0)\n\t\t\t{\n\t\t\t\tbresource_t *gameres = NULL;\n\t\t\t\tbresource_t *voiceres = NULL;\n\t\t\t\tbresource_t *chatres = NULL;\n\n\t\t\t\tb = sortlist[buds];\n\t\t\t\tif (b->btype == BT_ROSTER)\n\t\t\t\t{\t//gather capabilities from all of their resources\n\t\t\t\t\tfor (r = b->resources; r; r = r->next)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (!strcmp(r->bstatus, \"offline\"))\n\t\t\t\t\t\t\tcontinue;\n\n\t\t\t\t\t\tif ((r->caps & CAP_VOICE) && (!voiceres || r->priority > voiceres->priority))\n\t\t\t\t\t\t\tvoiceres = r;\n\t\t\t\t\t\tif ((r->caps & CAP_GAMEINVITE) && (!gameres || r->priority > gameres->priority))\n\t\t\t\t\t\t\tgameres = r;\n\t\t\t\t\t\tif (!chatres || r->priority > chatres->priority)\n\t\t\t\t\t\t\tchatres = r;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (b->defaultresource)\n\t\t\t\t{\n\t\t\t\t\tr = b->defaultresource;\n\t\t\t\t\tif (r->caps & CAP_VOICE)\n\t\t\t\t\t\tvoiceres = r;\n\t\t\t\t\tif (r->caps & CAP_GAMEINVITE)\n\t\t\t\t\t\tgameres = r;\n\t\t\t\t\tchatres = r;\n\t\t\t\t}\n\t\t\t\tif (chatres || b->btype == BT_CHATROOM)\n\t\t\t\t{\n\t\t\t\t\tyouarealoner = false;\n\n\t\t\t\t\tif (b->vcardphotochanged && b->friended)\n\t\t\t\t\t{\n\t\t\t\t\t\tstruct buddyinfo_s *bi;\n\t\t\t\t\t\tfor (bi = jcl->buddyinfo; bi; bi = bi->next)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (!strcmp(bi->accountdomain, b->accountdomain))\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (bi && !strcmp(b->vcardphotohash, bi->imagehash))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tchar photodata[65536];\n\t\t\t\t\t\t\tunsigned int photosize = bi->image?Base64_Decode(photodata, sizeof(photodata), bi->image, strlen(bi->image)):0;\n\t\t\t\t\t\t\tb->image = drawfuncs->LoadImageData(va(\"xmpp/%s.png\", b->accountdomain), bi->imagemime, photodata, photosize);\n\t\t\t\t\t\t\tb->vcardphotochanged = false;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (!*b->vcardphotohash)\n\t\t\t\t\t\t{\t//this buddy has no photo, so don't bother querying it\n\t\t\t\t\t\t\tb->vcardphotochanged = false;\n\t\t\t\t\t\t\tb->image = 0;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (jcl->avatarupdate == NULL)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tb->vcardphotochanged = false;\n\t\t\t\t\t\t\tCon_DPrintf(\"Querying %s's photo\\n\", b->accountdomain);\n\t\t\t\t\t\t\tjcl->avatarupdate = JCL_SendIQf(jcl, JCL_BuddyVCardReply, \"get\", b->accountdomain, \"<vCard xmlns='vcard-temp'/>\");\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tQ_snprintf(convolink, sizeof(convolink), \"^[%s\\\\xmppacc\\\\%i\\\\xmpp\\\\%s^]\", b->name, jcl->accountnum, b->accountdomain);\n\n\t\t\t\t\tCon_SubPrintf(console, \"^[\\\\img\\\\xmpp/%s.png\\\\fbimg\\\\\"IMG_FB_THEM\"\\\\w\\\\32\\\\h\\\\32^] \", b->accountdomain);\n\t\t\t\t\tCon_SubPrintf(console, \"%s\", convolink);\n\n\t\t\t\t\tif (chatres && *chatres->fstatus)\n\t\t\t\t\t\tCon_SubPrintf(console, \"\\v  %s\", chatres->fstatus);\n\t\t\t\t\telse if (b->btype == BT_CHATROOM && b->room_topic)\n\t\t\t\t\t\tCon_SubPrintf(console, \"\\v  %s\", b->room_topic);\n\n\t\t\t\t\tJCL_GenLink(jcl, convolink, sizeof(convolink), \"buddyopts\", b->accountdomain, NULL, NULL, \"^h%s^h\", \"Options\");\n\t\t\t\t\tif (chatres)\n\t\t\t\t\t\tCon_SubPrintf(console, \"\\v  %s    %s\", chatres->bstatus, convolink);\n\t\t\t\t\telse if (b->btype == BT_CHATROOM)\n\t\t\t\t\t\tCon_SubPrintf(console, \"\\v  %s    %s\", \"chatroom\", convolink);\n\t\t\t\t\telse\n\t\t\t\t\t\tCon_SubPrintf(console, \"\\v  %s    %s\", \"unknown\", convolink);\n\n\t\t\t\t\tfor (c2c = jcl->c2c; c2c; c2c = c2c->next)\n\t\t\t\t\t{\n\t\t\t\t\t\tbuddy_t *peer = NULL;\n\t\t\t\t\t\tqboolean voice = false, video = false, server = false;\n\t\t\t\t\t\tint c;\n\t\t\t\t\t\tif (JCL_FindBuddy(jcl, c2c->with, &peer, NULL, false) && peer == b)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tfor (c = 0; c < c2c->contents; c++)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tswitch(c2c->content[c].mediatype)\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tcase ICEP_INVALID:\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\tcase ICEP_VOICE:\tvoice = true; \tbreak;\n\t\t\t\t\t\t\t\tcase ICEP_VIDEO:\tvideo = true; \tbreak;\n\t\t\t\t\t\t\t\tcase ICEP_QWSERVER: server = true; \tbreak;\n\t\t\t\t\t\t\t\tcase ICEP_QWCLIENT: /*client = true;*/ \tbreak;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tc2c->displayed = true;\n\t\t\t\t\t\t\tif (server)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tJCL_GenLink(jcl, convolink, sizeof(convolink), \"jdeny\", c2c->with, NULL, c2c->sid, \"%s\", \"Kick\");\n\t\t\t\t\t\t\t\tgameres = NULL;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse if (video || voice)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tJCL_GenLink(jcl, convolink, sizeof(convolink), \"jdeny\", c2c->with, NULL, c2c->sid, \"%s\", \"Hang Up\");\n\t\t\t\t\t\t\t\tvoiceres = NULL;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse /*if (client)*/\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tJCL_GenLink(jcl, convolink, sizeof(convolink), \"jdeny\", c2c->with, NULL, c2c->sid, \"%s\", \"Disconnect\");\n\t\t\t\t\t\t\t\tgameres = NULL;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tCon_SubPrintf(console, \" %s\", convolink);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif (gameres)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (*gameres->server)\n\t\t\t\t\t\t\tJCL_GenLink(jcl, convolink, sizeof(convolink), \"join\", b->accountdomain, gameres->resource, NULL, \"Playing Quake - %s\", gameres->server);\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tJCL_GenLink(jcl, convolink, sizeof(convolink), \"invite\", b->accountdomain, gameres->resource, NULL, \"%s\", \"Invite\");\n\t\t\t\t\t\tCon_SubPrintf(console, \" %s\", convolink);\n\t\t\t\t\t}\n\t\t\t\t\tif (voiceres)\n\t\t\t\t\t{\n\t\t\t\t\t\tJCL_GenLink(jcl, convolink, sizeof(convolink), \"call\", b->accountdomain, voiceres->resource, NULL, \"%s\", \"Call\");\n\t\t\t\t\t\tCon_SubPrintf(console, \" %s\", convolink);\n\t\t\t\t\t}\n\t\t\t\t\tCon_SubPrintf(console, \"\\n\");\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (youarealoner)\n\t\t\t\tCon_SubPrintf(console, \"    You have no friends\\n\");\n\t\t}\n\n\t\t//and show any lingering c2cs with anyone not on our list.\n\t\tfor (c2c = jcl->c2c; c2c; c2c = c2c->next)\n\t\t{\n\t\t\tif (!c2c->displayed)\n\t\t\t{\n\t\t\t\tqboolean voice = false, video = false, server = false;\n\t\t\t\tint c;\n\n\t\t\t\tfor (c = 0; c < c2c->contents; c++)\n\t\t\t\t{\n\t\t\t\t\tswitch(c2c->content[c].mediatype)\n\t\t\t\t\t{\n\t\t\t\t\tcase ICEP_INVALID:\t\t\t\t\tbreak;\n\t\t\t\t\tcase ICEP_VOICE:\tvoice = true; \tbreak;\n\t\t\t\t\tcase ICEP_VIDEO:\tvideo = true; \tbreak;\n\t\t\t\t\tcase ICEP_QWSERVER: server = true; \tbreak;\n\t\t\t\t\tcase ICEP_QWCLIENT: /*client = true;*/ \tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tc2c->displayed = true;\n\t\t\t\tif (server)\n\t\t\t\t\tJCL_GenLink(jcl, convolink, sizeof(convolink), \"jdeny\", c2c->with, NULL, c2c->sid, \"%s\", \"Kick\");\n\t\t\t\telse if (video || voice)\n\t\t\t\t\tJCL_GenLink(jcl, convolink, sizeof(convolink), \"jdeny\", c2c->with, NULL, c2c->sid, \"%s\", \"Hang Up\");\n\t\t\t\telse /*if (client)*/\n\t\t\t\t\tJCL_GenLink(jcl, convolink, sizeof(convolink), \"jdeny\", c2c->with, NULL, c2c->sid, \"%s\", \"Disconnect\");\n\t\t\t\tCon_SubPrintf(console, \"%s: %s\\n\", c2c->with, convolink);\n\t\t\t}\n\t\t}\n\t}\n\n\tfor (i = 0; i < sizeof(jclients)/sizeof(jclients[0]); i++)\n\t\tif (!jclients[i])\n\t\t{\n\t\t\tJCL_GenLink(NULL, convolink, sizeof(convolink), \"newaccount\", NULL, NULL, NULL, \"%s\", \"Open New Account\");\n\t\t\tCon_SubPrintf(console, \"\\n%s\\n\", convolink);\n\t\t\tbreak;\n\t\t}\n}\n\nstatic void JCL_PrintBuddyList(char *console, jclient_t *jcl, qboolean all)\n{\n\tbuddy_t *b;\n\tbresource_t *r;\n\tstruct c2c_s *c2c;\n\tstruct ft_s *ft;\n\tchar convolink[512], actlink[512];\n\tint c;\n\n\tif (!jcl->buddies)\n\t\tCon_SubPrintf(console, \"You have no friends\\n\");\n\tfor (b = jcl->buddies; b; b = b->next)\n\t{\t\n\t\t//if we don't actually know them, don't list them.\n\t\tif (b->btype == BT_UNKNOWN)\n\t\t\tcontinue;\n\n\t\tif (b->btype == BT_CHATROOM)\n\t\t{\n\t\t\tr = b->resources;\n\t\t\tJCL_GenLink(jcl, convolink, sizeof(convolink), NULL, b->accountdomain, r->resource, NULL, \"%s\", b->name);\n\t\t\tCon_SubPrintf(console, \"%s: \", convolink);\n\t\t\tJCL_PrintBuddyStatus(console, jcl, b, r);\n\t\t\tCon_SubPrintf(console, \"\\n\");\n\t\t}\n\t\telse if (!b->resources)\t//offline\n\t\t{\n\t\t\tif (all)\n\t\t\t{\n\t\t\t\tJCL_GenLink(jcl, convolink, sizeof(convolink), NULL, b->accountdomain, NULL, NULL, \"%s\", b->name);\n\t\t\t\tCon_SubPrintf(console, \"%s: offline\\n\", convolink);\n\t\t\t}\n\t\t}\n\t\telse if (b->resources->next)\n\t\t{\t//multiple potential resources\n\t\t\tJCL_GenLink(jcl, convolink, sizeof(convolink), NULL, b->accountdomain, NULL, NULL, \"%s\", b->name);\n\t\t\tCon_SubPrintf(console, \"%s:\\n\", convolink);\n\t\t\tfor (r = b->resources; r; r = r->next)\n\t\t\t{\n\t\t\t\tJCL_GenLink(jcl, convolink, sizeof(convolink), NULL, b->accountdomain, r->resource, NULL, \"%s\", r->resource);\n\t\t\t\tCon_SubPrintf(console, \"    %s: \", convolink);\n\t\t\t\tJCL_PrintBuddyStatus(console, jcl, b, r);\n\t\t\t\tCon_SubPrintf(console, \"\\n\");\n\t\t\t}\n\t\t}\n\t\telse\t//only one resource\n\t\t{\n\t\t\tr = b->resources;\n\t\t\tJCL_GenLink(jcl, convolink, sizeof(convolink), NULL, b->accountdomain, r->resource, NULL, \"%s\", b->name);\n\t\t\tCon_SubPrintf(console, \"%s: \", convolink);\n\t\t\tJCL_PrintBuddyStatus(console, jcl, b, r);\n\t\t\tCon_SubPrintf(console, \"\\n\");\n\t\t}\n\t}\n\n#ifdef JINGLE\n\tif (jcl->c2c)\n\t\tCon_SubPrintf(console, \"Active sessions:\\n\");\n\tfor (c2c = jcl->c2c; c2c; c2c = c2c->next)\n\t{\n\t\tqboolean voice = false, video = false, server = false, client = false;\n\t\tJCL_FindBuddy(jcl, c2c->with, &b, &r, true);\n\t\tfor (c = 0; c < c2c->contents; c++)\n\t\t{\n\t\t\tswitch(c2c->content[c].mediatype)\n\t\t\t{\n\t\t\tcase ICEP_INVALID:\t\t\t\t\tbreak;\n\t\t\tcase ICEP_VOICE:\tvoice = true; \tbreak;\n\t\t\tcase ICEP_VIDEO:\tvideo = true; \tbreak;\n\t\t\tcase ICEP_QWSERVER: server = true; \tbreak;\n\t\t\tcase ICEP_QWCLIENT: client = true; \tbreak;\n\t\t\t}\n\t\t}\n\t\tJCL_GenLink(jcl, convolink, sizeof(convolink), NULL, b->accountdomain, r->resource, NULL, \"%s\", b->name);\n\t\tCon_SubPrintf(console, \"    %s: \", convolink);\n\t\tif (video)\n\t\t\tCon_SubPrintf(console, \"video \");\n\t\tif (voice)\n\t\t\tCon_SubPrintf(console, \"voice \");\n\t\tif (server)\n\t\t\tCon_SubPrintf(console, \"server \");\n\t\tif (client)\n\t\t\tCon_SubPrintf(console, \"client \");\n\t\tif (server)\n\t\t\tJCL_GenLink(jcl, actlink, sizeof(actlink), \"jdeny\", c2c->with, NULL, c2c->sid, \"%s\", \"Kick\");\n\t\telse if (video || voice)\n\t\t\tJCL_GenLink(jcl, actlink, sizeof(actlink), \"jdeny\", c2c->with, NULL, c2c->sid, \"%s\", \"Hang Up\");\n\t\telse\n\t\t\tJCL_GenLink(jcl, actlink, sizeof(actlink), \"jdeny\", c2c->with, NULL, c2c->sid, \"%s\", \"Disconnect\");\n\t\tCon_SubPrintf(console, \"%s\\n\", actlink);\n\t}\n#endif\n\n#ifdef FILETRANSFERS\n\tif (jcl->ft)\n\t\tCon_SubPrintf(console, \"Active file transfers:\\n\");\n\tfor (ft = jcl->ft; ft; ft = ft->next)\n\t{\n\t\tJCL_FindBuddy(jcl, ft->with, &b, &r, true);\n\t\tJCL_GenLink(jcl, convolink, sizeof(convolink), NULL, b->accountdomain, r->resource, NULL, \"%s\", b->name);\n\t\tJCL_GenLink(jcl, actlink, sizeof(actlink), \"fdeny\", ft->with, NULL, ft->sid, \"%s\", \"Cancel\");\n\t\tCon_SubPrintf(console, \"    %s: %s\\n\", convolink, ft->fname);\n\t}\n#endif\n}\n\n//functions above this line allow connections to multiple servers.\n//it is just the control functions that only allow one server.\n\nvoid JCL_Frame(double realtime, double gametime)\n{\n\tint i;\n\tjclient_curtime = realtime;\n\tif (jclient_needreadconfig)\n\t{\n\t\tJCL_LoadConfig();\n\t\tJCL_RegenerateBuddyList(false);\n\t}\n\tif (jclient_updatebuddylist)\n\t\tJCL_RegenerateBuddyList(false);\n\n\tfor (i = 0; i < sizeof(jclients)/sizeof(jclients[0]); i++)\n\t{\n\t\tjclient_t *jcl = jclients[i];\n\t\tif (jcl && jcl->status != JCL_INACTIVE)\n\t\t{\n\t\t\tint stat = JCL_CONTINUE;\n\t\t\tif (jcl->status == JCL_DEAD)\n\t\t\t{\n\t\t\t\tif (jclient_curtime > jcl->timeout)\n\t\t\t\t{\n\t\t\t\t\tJCL_Reconnect(jcl);\n\t\t\t\t\tjcl->timeout = jclient_curtime + 60*1000;\n\t\t\t\t\tjclient_updatebuddylist = true;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tchar *error = \"\";\n\t\t\t\tif (jcl->connected)\n\t\t\t\t\tJCL_GeneratePresence(jcl, false);\n\t\t\t\twhile(stat == JCL_CONTINUE)\n\t\t\t\t\tstat = JCL_ClientFrame(jcl, &error);\n\t\t\t\tif (stat == JCL_NUKEFROMORBIT)\n\t\t\t\t{\n\t\t\t\t\tJCL_CloseConnection(jcl, error, true);\n\t\t\t\t\tjcl->status = JCL_INACTIVE;\n\t\t\t\t}\n\t\t\t\telse if (stat == JCL_KILL)\n\t\t\t\t\tJCL_CloseConnection(jcl, error, true);\n\t\t\t\telse\n\t\t\t\t\tJCL_FlushOutgoing(jcl);\n\n\t\t\t\tif (stat == JCL_DONE)\n\t\t\t\t{\n\t\t\t\t\tXMPP_FT_Frame(jcl);\n\n\t\t\t\t\tif (jclient_curtime > jcl->timeout)\n\t\t\t\t\t{\n\t\t\t\t\t\t//client needs to send something valid as a keep-alive so routers don't silently forget mappings.\n\t\t\t\t\t\tJCL_SendIQf(jcl, NULL, \"get\", NULL, \"<ping xmlns='urn:xmpp:ping'/>\");\n\t\t\t\t\t\tjcl->timeout = jclient_curtime + 60*1000;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (jcl->status == JCL_ACTIVE)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (jcl->rcon_pipe >= 0)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tchar rcondata[64000];\n\t\t\t\t\t\t\tint rconsize = netfuncs->Recv(jcl->rcon_pipe, rcondata, sizeof(rcondata)-1);\n\t\t\t\t\t\t\tif (rconsize > 0)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tchar *ls, *le;\n\t\t\t\t\t\t\t\trcondata[rconsize] = 0;\n\t\t\t\t\t\t\t\tls = rcondata;\n\t\t\t\t\t\t\t\twhile (ls)\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tle = strchr(ls, '\\n');\n\t\t\t\t\t\t\t\t\tif (le)\n\t\t\t\t\t\t\t\t\t\t*le++ = 0;\n\t\t\t\t\t\t\t\t\tJCL_AddClientMessagef(jcl, \"<message to='%s'><body>%s</body></message>\", jcl->rcon_peer, ls);\n\t\t\t\t\t\t\t\t\tls = le;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse if (rconsize < 0)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tJCL_AddClientMessagef(jcl, \"<message to='%s'><body>%s</body></message>\", jcl->rcon_peer, \"RCON ERROR\");\n\t\t\t\t\t\t\t\tfilefuncs->Close(jcl->rcon_pipe);\n\t\t\t\t\t\t\t\tjcl->rcon_pipe = -1;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n#ifdef JINGLE\n\t\t\tJCL_JingleTimeouts(jcl, false);\n#endif\n\t\t\tJCL_IQTimeouts(jcl);\n\t\t}\n\t}\n}\n\nvoid JCL_WriteConfig(void)\n{\n\txmltree_t *m, *n, *oauth2, *features;\n\tint i, j;\n\tqhandle_t config;\n\tstruct buddyinfo_s *bi;\n\tbuddy_t *bud;\n\n\tif (!jclient_configdirty)\n\t\treturn; //no point yet.\n\tjclient_configdirty = false;\n\n\t//don't write the config if we're meant to be reading it. avoid wiping it if we're killed fast.\n\tif (jclient_needreadconfig)\n\t\treturn;\n\n\n\tm = XML_CreateNode(NULL, \"xmppaccounts\", \"\", \"\");\n\tfor (i = 0; i < sizeof(jclients) / sizeof(jclients[0]);  i++)\n\t{\n\t\tjclient_t *jcl = jclients[i];\n\t\tif (jcl)\n\t\t{\n\t\t\tchar foo[64];\n\t\t\tn = XML_CreateNode(m, \"account\", \"\", \"\");\n\t\t\tXML_AddParameteri(n, \"id\", i);\n\n\t\t\tQ_snprintf(foo, sizeof(foo), \"%i\", jcl->streamdebug);\n\t\t\tXML_CreateNode(n, \"streamdebug\", \"\", foo);\n\t\t\tQ_snprintf(foo, sizeof(foo), \"%i\", jcl->forcetls);\n\t\t\tXML_CreateNode(n, \"forcetls\", \"\", foo);\n\t\t\tXML_CreateNode(n, \"savepassword\", \"\", jcl->savepassword?\"1\":\"0\");\n\t\t\tXML_CreateNode(n, \"allowauth_plain_nontls\", \"\", jcl->sasl.allowauth_plainnontls?\"1\":\"0\");\n\t\t\tXML_CreateNode(n, \"allowauth_plain_tls\", \"\", jcl->sasl.allowauth_plaintls?\"1\":\"0\");\n\t\t\tXML_CreateNode(n, \"allowauth_digest_md5\", \"\", jcl->sasl.allowauth_digestmd5?\"1\":\"0\");\n\t\t\tXML_CreateNode(n, \"allowauth_scram_sha_1\", \"\", jcl->sasl.allowauth_scramsha1?\"1\":\"0\");\n\n\t\t\tif (*jcl->sasl.oauth2.saslmethod)\n\t\t\t{\n\t\t\t\tXML_CreateNode(n, \"allowauth_oauth2\", \"\", jcl->sasl.allowauth_oauth2?\"1\":\"0\");\n\t\t\t\toauth2 = XML_CreateNode(n, \"oauth2\", \"\", \"\");\n\t\t\t\tXML_AddParameter(oauth2, \"method\", jcl->sasl.oauth2.saslmethod);\n\t\t\t\tXML_CreateNode(oauth2, \"obtain-url\", \"\", jcl->sasl.oauth2.obtainurl);\n\t\t\t\tXML_CreateNode(oauth2, \"refresh-url\", \"\", jcl->sasl.oauth2.refreshurl);\n\t\t\t\tXML_CreateNode(oauth2, \"client-id\", \"\", jcl->sasl.oauth2.clientid);\n\t\t\t\tXML_CreateNode(oauth2, \"client-secret\", \"\", jcl->sasl.oauth2.clientsecret);\n\t\t\t\tXML_CreateNode(oauth2, \"scope\", \"\", jcl->sasl.oauth2.scope);\n\t\t\t\tXML_CreateNode(oauth2, \"auth-token\", \"\", jcl->sasl.oauth2.authtoken);\n\t\t\t\tXML_CreateNode(oauth2, \"refresh-token\", \"\", jcl->sasl.oauth2.refreshtoken);\n\t\t\t\tXML_CreateNode(oauth2, \"access-token\", \"\", jcl->sasl.oauth2.accesstoken);\n\t\t\t}\n\n\t\t\tXML_CreateNode(n, \"username\", \"\", jcl->username);\n\t\t\tXML_CreateNode(n, \"domain\", \"\", jcl->domain);\n\t\t\tXML_CreateNode(n, \"resource\", \"\", jcl->resource);\n\t\t\tif (jcl->savepassword)\n\t\t\t\tXML_CreateNode(n, \"password\", \"\", jcl->sasl.password_plain);\t//FIXME: should we base64 this just to obscure it? probably not. trivial obscurity does few favours.\n\t\t\tXML_CreateNode(n, \"serveraddr\", \"\", jcl->serveraddr);\n\t\t\tQ_snprintf(foo, sizeof(foo), \"%i\", jcl->serverport);\n\t\t\tXML_CreateNode(n, \"serverport\", \"\", foo);\n\t\t\tXML_CreateNode(n, \"certificatedomain\", \"\", jcl->certificatedomain);\n\t\t\tXML_CreateNode(n, \"inactive\", \"\", jcl->status == JCL_INACTIVE?\"1\":\"0\");\n\n\t\t\t//write out what optional/risky features should be enabled (like file transfers, jingle, etc)\n\t\t\tfeatures = XML_CreateNode(n, \"features\", \"\", \"\");\n\t\t\tXML_AddParameter(features, \"ver\", JCL_BUILD);\n\t\t\tfor (j = 0; capnames[j].names; j++)\n\t\t\t{\n\t\t\t\tXML_CreateNode(features, capnames[j].names, \"\", (jcl->enabledcapabilities & capnames[j].cap)?\"1\":\"0\");\n\t\t\t}\n\n\t\t\t//write a list of chatrooms that can't be saved in rosters\n\t\t\tfeatures = XML_CreateNode(n, \"chats\", \"\", \"\");\n\t\t\tfor (bud = jcl->buddies; bud; bud = bud->next)\n\t\t\t{\n\t\t\t\tif (bud->btype == BT_CHATROOM)\n\t\t\t\t{\n\t\t\t\t\txmltree_t *b = XML_CreateNode(features, \"room\", \"\", \"\");\n\t\t\t\t\tXML_AddParameter(b, \"name\", bud->accountdomain);\n\t\t\t\t\tXML_AddParameter(b, \"alias\", bud->name);\n\t\t\t\t\tXML_AddParameter(b, \"nick\", bud->room_nick);\n\t\t\t\t\tXML_AddParameter(b, \"password\", bud->room_password);\n\t\t\t\t\tXML_AddParameteri(b, \"autojoin\", bud->room_autojoin);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t//write an avatar cache\n\t\t\t//FIXME: some sort of timeout? only people on our roster?\n\t\t\tfeatures = XML_CreateNode(n, \"buddyinfo\", \"\", \"\");\n\t\t\tfor (bi = jcl->buddyinfo; bi; bi = bi->next)\n\t\t\t{\n\t\t\t\txmltree_t *b = XML_CreateNode(features, \"buddy\", \"\", \"\");\n\t\t\t\tXML_AddParameter(b, \"name\", bi->accountdomain);\n\n\t\t\t\tif (bi->image)\n\t\t\t\t\tXML_CreateNode(b, \"image\", \"\", bi->image);\n\t\t\t\tif (bi->imagemime)\n\t\t\t\t\tXML_CreateNode(b, \"imagemime\", \"\", bi->imagemime);\n\t\t\t\tif (bi->imagehash)\n\t\t\t\t\tXML_CreateNode(b, \"imagehash\", \"\", bi->imagehash);\n\t\t\t}\n\n\t\t\t//FIXME: client disco info\n\t\t}\n\t}\n\n\tfilefuncs->Open(\"**plugconfig\", &config, 2);\n\tif (config >= 0)\n\t{\n\t\tchar *s = XML_GenerateString(m, true);\n\t\tfilefuncs->Write(config, s, strlen(s));\n\t\tfree(s);\n\n\t\tfilefuncs->Close(config);\n\t}\n\tXML_Destroy(m);\n}\nvoid JCL_LoadConfig(void)\n{\n\tjclient_needreadconfig = false;\n\tif (!jclients[0])\n\t{\n\t\tint len;\n\t\tqhandle_t config;\n\t\tchar *buf;\n\t\tqboolean oldtls;\n\t\tjclient_configdirty = false;\n\t\tlen = filefuncs->Open(\"**plugconfig\", &config, 1);\n\t\tif (len >= 0)\n\t\t{\n\t\t\tbuf = malloc(len+1);\n\t\t\tbuf[len] = 0;\n\t\t\tfilefuncs->Read(config, buf, len);\n\t\t\tfilefuncs->Close(config);\n\n\t\t\tif (len && *buf != '<')\n\t\t\t{//legacy code, to be removed\n\t\t\t\tchar *line = buf;\n\t\t\t\tchar tls[256];\n\t\t\t\tchar server[256];\n\t\t\t\tchar account[256];\n\t\t\t\tchar password[256];\n\t\t\t\tline = JCL_ParseOut(line, tls, sizeof(tls));\n\t\t\t\tline = JCL_ParseOut(line, server, sizeof(server));\n\t\t\t\tline = JCL_ParseOut(line, account, sizeof(account));\n\t\t\t\tline = JCL_ParseOut(line, password, sizeof(password));\n\n\t\t\t\toldtls = atoi(tls);\n\n\t\t\t\tCon_Printf(\"Legacy config: %s (%i)\\n\", buf, len);\n\t\t\t\tjclients[0] = JCL_Connect(0, server, oldtls, account, password);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\txmltree_t *accs;\n\t\t\t\tint start = 0;\n\t\t\t\taccs = XML_Parse(buf, &start, len, false, \"\");\n\t\t\t\tif (accs)\n\t\t\t\t{\n\t\t\t\t\tint i;\n\t\t\t\t\txmltree_t *acc;\n\t\t\t\t\tfor (i = 0; (acc = XML_ChildOfTree(accs, \"account\", i)); i++)\n\t\t\t\t\t{\n\t\t\t\t\t\tint id = atoi(XML_GetParameter(acc, \"id\", \"0\"));\n\t\t\t\t\t\tif (id < 0 || id >= sizeof(jclients) / sizeof(jclients[0]) || jclients[id])\n\t\t\t\t\t\t\tcontinue;\n\n\t\t\t\t\t\tjclients[id] = JCL_ConnectXML(acc);\n\t\t\t\t\t}\n\t\t\t\t\tXML_Destroy(accs);\n\t\t\t\t}\n\t\t\t}\n\t\t\tfree(buf);\n\t\t}\n\t}\n}\n\n//on shutdown, write config and close connections.\nvoid JCL_Shutdown(void)\n{\n\tjclient_t *jcl;\n\tint i;\n\tJCL_WriteConfig();\n\tfor (i = 0; i < sizeof(jclients)/sizeof(jclients[0]); i++)\n\t{\n\t\tjcl = jclients[i];\n\t\tif (jcl)\n\t\t\tJCL_CloseConnection(jcl, \"\", false);\n\t}\n\n//\tif (_CrtDumpMemoryLeaks())\n//\t\tOutputDebugStringA(\"Leaks detected\\n\");\n}\n\nvoid JCL_SendMessage(jclient_t *jcl, char *to, char *msg)\n{\n\tchar markup[1024];\n\tchar *con, *title;\n\tbuddy_t *b;\n\tbresource_t *br;\n\tJCL_FindBuddy(jcl, to, &b, &br, true);\n\tif (!b)\n\t{\n\t\tCon_Printf(\"Can't find buddy \\\"%s\\\"\\n\", to);\n\t\treturn;\n\t}\n\tif (b->btype == BT_CHATROOM)\n\t{\n\t\tif (br)\n\t\t{\t//unicast...\n\t\t\tJCL_AddClientMessagef(jcl, \"<message to='%s/%s' type='chat'><body>\", b->accountdomain, br->resource);\n\t\t\tcon = to;\n\t\t}\n\t\telse\n\t\t{\t//send to all recipients\n\t\t\tJCL_AddClientMessagef(jcl, \"<message to='%s' type='groupchat'><body>\", b->accountdomain);\n\t\t\tcon = b->name;\n\t\t}\n\t\ttitle = con;\n\t}\n\telse\n\t{\n\t\ttitle = b->name;\n\t\tcon = b->accountdomain;\n\t\tif (!br)\n\t\t\tbr = b->defaultresource;\n\t\tif (br)\n\t\t\tJCL_AddClientMessagef(jcl, \"<message to='%s/%s'><body>\", b->accountdomain, br->resource);\n\t\telse\n\t\t\tJCL_AddClientMessagef(jcl, \"<message to='%s'><body>\", b->accountdomain);\n\t}\n\tJCL_AddClientMessage(jcl, markup, XML_Markup(msg, markup, sizeof(markup)) - markup);\n\tJCL_AddClientMessageString(jcl, \"</body></message>\");\n\tif (b->btype == BT_CHATROOM && !br)\n\t\treturn;\n\tif (!strncmp(msg, \"/me \", 4))\n\t\tXMPP_ConversationPrintf(con, title, false, \"* \"COL_NAME_US\"%s^7\"COL_TEXT_US\"%s\\n\", ((!strcmp(jcl->localalias, \">>\"))?\"me\":jcl->localalias), msg+3);\n\telse if (b->btype == BT_ROSTER && confuncs)\n\t\tXMPP_ConversationPrintf(con, title, false, \"^[\\\\img\\\\xmpp/%s.png\\\\fbimg\\\\\"IMG_FB_US\"\\\\w\\\\32\\\\h\\\\32^]\"COL_NAME_US\"%s^7:\\v\"COL_TEXT_US\"%s\\n\", jcl->barejid, jcl->localalias, msg);\n\telse\n\t\tXMPP_ConversationPrintf(con, title, false, COL_NAME_US\"%s^7: \"COL_TEXT_US\"%s\\n\", jcl->localalias, msg);\n}\nvoid JCL_AttentionMessage(jclient_t *jcl, char *to, char *msg)\n{\n\tchar fullto[256];\n\tbuddy_t *b = NULL;\n\tbresource_t *br = NULL;\n\txmltree_t *m;\n\tchar *s;\n\n\tJCL_FindBuddy(jcl, to, &b, &br, true);\n\tif (!b)\n\t\treturn;\n\tif (!br)\n\t\tbr = b->defaultresource;\n\tif (!br)\n\t\tbr = b->resources;\n\tif (!br)\n\t{\n\t\tCon_SubPrintf(b->accountdomain, \"User is not online\\n\");\n\t\treturn;\n\t}\n\tQ_snprintf(fullto, sizeof(fullto), \"%s/%s\", b->accountdomain, br->resource);\n\n\tm = XML_CreateNode(NULL, \"message\", \"\", \"\");\n\tXML_AddParameter(m, \"to\", fullto);\n//\tXML_AddParameter(m, \"type\", \"headline\");\n\n\tXML_CreateNode(m, \"attention\", \"urn:xmpp:attention:0\", \"\");\n\tif (msg)\n\t\tXML_CreateNode(m, \"body\", \"\", msg);\n\n\ts = XML_GenerateString(m, false);\n\tJCL_AddClientMessageString(jcl, s);\n\tfree(s);\n\tXML_Destroy(m);\n\n\tif (msg)\n\t{\n\t\tif (!strncmp(msg, \"/me \", 4))\n\t\t\tCon_SubPrintf(b->accountdomain, \"*\"COL_NAME_US\"%s^7\"COL_TEXT_US\"%s\\n\", ((!strcmp(jcl->localalias, \">>\"))?\"me\":jcl->localalias), msg+3);\n\t\telse\n\t\t\tCon_SubPrintf(b->accountdomain, COL_NAME_US\"%s^7: \"COL_TEXT_US\"%s\\n\", jcl->localalias, msg);\n\t}\n}\n\nstatic qboolean JCL_IsChatroom(jclient_t *jcl, char *room)\n{\n\tbuddy_t *b;\n\n\tif (!jcl)\n\t\treturn false;\n\n\tif (!JCL_FindBuddy(jcl, room, &b, NULL, true))\n\t\treturn false;\n\n\treturn b->btype == BT_CHATROOM;\n}\n\n//server may be null, in which case its expected to be folded into room.\nvoid JCL_JoinMUCChat(jclient_t *jcl, const char *room, const char *server, const char *myhandle, const char *password)\n{\n\tchar caps[512];\n\tchar roomserverhandle[512];\n\tbuddy_t *b;\n//\tbresource_t *r;\n\tif (server)\n\t\tQ_snprintf(roomserverhandle, sizeof(roomserverhandle), \"%s@%s\", room, server);\n\telse\n\t\tQ_snprintf(roomserverhandle, sizeof(roomserverhandle), \"%s\", room);\n\tif (!JCL_FindBuddy(jcl, roomserverhandle, &b, NULL, true))\n\t\treturn;\n\tif (!myhandle)\n\t{\n\t\tif (b->room_nick)\n\t\t\tmyhandle = b->room_nick;\n\t\telse\n\t\t\tmyhandle = jcl->username;\n\t}\n\tb->btype = BT_CHATROOM;\n\tbuildcapsvcardpresence(jcl, caps, sizeof(caps));\n\t//FIXME: check for errors\n\tJCL_AddClientMessagef(jcl, \n\t\t\t\t\"<presence to='%s/%s'>\"\n\t\t\t\t\t\"<x xmlns='http://jabber.org/protocol/muc'>\"\n\t\t\t\t\t\t\"<password>%s</password>\"\n\t\t\t\t\t\t//\"<history maxstanzas= maxchars= seconds= since=/>\"\n\t\t\t\t\t\"</x>\"\n\t\t\t\t\t\"%+s\"\n\t\t\t\t\"</presence>\"\n\t\t\t\t, roomserverhandle, myhandle, password?password:\"\", caps);\n\n\tif (b->room_nick != myhandle)\n\t{\n\t\tfree(b->room_nick);\n\t\tb->room_nick = myhandle?strdup(myhandle):NULL;\n\t}\n\tif (b->room_password != password)\n\t{\n\t\tfree(b->room_password);\n\t\tb->room_password = myhandle?strdup(password):NULL;\n\t}\n}\n\nvoid XMPP_Menu_Password(jclient_t *acc)\n{\n//\tint y;\n\n\tif (!jclient_action)\n\t{\n\t\tJCL_RegenerateBuddyList(true);\n\t\tconfuncs->SetConsoleFloat(BUDDYLISTTITLE, \"linebuffered\", true);\n\t\tjclient_action_cl = acc;\n\t\tjclient_action_buddy = NULL;\n\t\tjclient_action = ACT_SETAPASSWORD;\n\t}\n\n\t/*\n\tcmdfuncs->AddText(\"conmenu\\n\"\n\t\t\t\t\"{\\n\"\n\t\t\t\t\t\"menuclear\\n\"\n\t\t\t\t\t\"if (option == \\\"SignIn\\\")\\n\"\n\t\t\t\t\t\"{\\n\"\n\t\t\t\t\tCOMMANDPREFIX\" /set savepassword $_t1\\n\"\n\t\t\t\t\tCOMMANDPREFIX\" /password ${0}\\n\"\n\t\t\t\t\t\"}\\n\"\n\t\t\t\t\"}\\n\", false);\n\n\ty = 36;\n\tcmdfuncs->AddText(va(\"menutext 48 %i \\\"^sXMPP Sign In\\\"\\n\", y), false); y+=16;\n\tcmdfuncs->AddText(va(\"menutext 48 %i \\\"^sPlease provide your password for\\\"\\n\", y), false); y+=16;\n\tcmdfuncs->AddText(va(\"menueditpriv 48 %i \\\"%s@%s\\\" \\\"example\\\"\\n\", y, acc->username, acc->domain), false);y+=16;\n\tcmdfuncs->AddText(va(\"set _t1 0\\nmenucheck 48 %i \\\"Save Password\\\" _t1 1\\n\", y), false); y+=16;\n\tcmdfuncs->AddText(va(\"menutext 48 %i \\\"Sign In\\\" SignIn\\n\", y), false);\n\tcmdfuncs->AddText(va(\"menutext 256 %i \\\"Cancel\\\" cancel\\n\", y), false);\n\t*/\n}\nvoid XMPP_Menu_Connect(void)\n{\n\tint y;\n\tcmdfuncs->AddText(\"conmenu\\n\"\n\t\t\t\t\"{\\n\"\n\t\t\t\t\t\"menuclear\\n\"\n\t\t\t\t\t\"if (option == \\\"SignIn\\\")\\n\"\n\t\t\t\t\t\"{\\n\"COMMANDPREFIX\" /connect ${0}@${1}/${2}\\n}\\n\"\n\t\t\t\t\"}\\n\", false);\n\n\ty = 36;\n\tcmdfuncs->AddText(va(\"menutext 48 %i \\\"^sXMPP Sign In\\\"\\n\", y), false); y+=16;\n\tcmdfuncs->AddText(va(\"menueditpriv 48 %i \\\"Username\\\" \\\"example\\\"\\n\", y), false);y+=16;\n\tcmdfuncs->AddText(va(\"menueditpriv 48 %i \\\"Domain\\\" \\\"\"EXAMPLEDOMAIN\"\\\"\\n\", y), false);y+=16;\n\tcmdfuncs->AddText(va(\"menueditpriv 48 %i \\\"Resource\\\" \\\"\\\"\\n\", y), false);y+=32;\n\tcmdfuncs->AddText(va(\"menutext 48 %i \\\"Sign In\\\" SignIn\\n\", y), false);\n\tcmdfuncs->AddText(va(\"menutext 256 %i \\\"Cancel\\\" cancel\\n\", y), false);\n}\n\nvoid JCL_Command(int accid, char *console)\n{\n\tchar imsg[8192];\n\tchar arg[6][1024], *argstart[6];\n\tchar *msg;\n\tint i;\n\tchar nname[256];\n\tjclient_t *jcl;\n\n\tif (accid < 0 || accid >= sizeof(jclients)/sizeof(jclients[0]))\n\t\treturn;\n\tjcl = jclients[accid];\n\n\tcmdfuncs->Args(imsg, sizeof(imsg));\n\n\tmsg = imsg;\n\tfor (i = 0; i < 6; i++)\n\t{\n\t\tif (!msg)\n\t\t{\n\t\t\targstart[i] = \"\";\n\t\t\t*arg[i] = 0;\n\t\t}\n\t\telse\n\t\t{\n\t\t\twhile (*msg == ' ')\n\t\t\t\tmsg++;\n\t\t\targstart[i] = msg;\n\t\t\tmsg = JCL_ParseOut(msg, arg[i], sizeof(arg[i]));\n\t\t}\n\t}\n\n\tif (arg[0][0] == '/' && arg[0][1] != '/' && strcmp(arg[0]+1, \"me\"))\n\t{\n\t\tif (*console && !strcmp(arg[0]+1, \"ban\") && JCL_IsChatroom(jcl, console))\n\t\t{\n\t\t\tchar *roomnick = arg[1];\n\t\t\tchar *comment = arg[2];\n\t\t\tchar *jid;\n\t\t\tif (strchr(roomnick, '@'))\n\t\t\t\tjid = roomnick;\n\t\t\telse\n\t\t\t{\n\t\t\t\tCon_TrySubPrint(console, \"Unable to translate roomnicks. Please use their bare jid.\\n\");\n\t\t\t\treturn;\t//FIXME: translate from roomnick to real barejid\n\t\t\t}\n\n\t\t\tJCL_SendIQf(jcl, NULL, \"set\", console,\n\t\t\t\t\t\"<query xmlns='http://jabber.org/protocol/muc#admin'>\"\n\t\t\t\t\t\t\"<item affiliation='outcast' jid='%s'>\"\n\t\t\t\t\t\t\t\"<reason>%s</reason>\"\n\t\t\t\t\t\t\"</item>\"\n\t\t\t\t\t\"</query>\"\n\t\t\t\t\t, jid, comment\n\t\t\t\t);\n\t\t}\n\t\telse if (*console && !strcmp(arg[0]+1, \"invite\") && JCL_IsChatroom(jcl, console))\n\t\t{\n\t\t\tchar *jid = arg[1];\n\t\t\tchar *reason = arg[2];\n\t\t\tJCL_AddClientMessagef(jcl, \n\t\t\t\t\"<message to='%s'>\"\n\t\t\t\t\t\"<x xmlns='http://jabber.org/protocol/muc#user'>\"\n\t\t\t\t\t\t\"<invite to='%s'>\"\n\t\t\t\t\t\t\t\"<reason>%s</reason>\"\n\t\t\t\t\t\t\"</invite>\"\n\t\t\t\t\t\"</x>\"\n\t\t\t\t\"</message>\"\n\t\t\t\t, console, jid, reason\n\t\t\t\t);\n\t\t}\n\t\telse if (*console && !strcmp(arg[0]+1, \"join\") && JCL_IsChatroom(jcl, console))\n\t\t{\n\t\t\tchar *room = arg[1];\n\t\t\tchar *nick = \"\";\n\t\t\tchar *pass = arg[2];\n\t\t\tJCL_AddClientMessagef(jcl,\n\t\t\t\t\"<presence to='%s/%s'>\"\n\t\t\t\t\t\"<x xmlns='http://jabber.org/protocol/muc#user'>\"\n\t\t\t\t\t\t\"<password>%s</password>\"\n\t\t\t\t\t\"</x>\"\n\t\t\t\t\"</presence>\"\n\t\t\t\t, room, nick, pass\n\t\t\t\t);\n\t\t}\n\t\telse if (*console && !strcmp(arg[0]+1, \"kick\") && JCL_IsChatroom(jcl, console))\n\t\t{\n\t\t\tchar *roomnick = arg[1];\n\t\t\tchar *comment = arg[2];\n\t\t\tJCL_SendIQf(jcl, NULL, \"set\", console,\n\t\t\t\t\t\"<query xmlns='http://jabber.org/protocol/muc#admin'>\"\n\t\t\t\t\t\t\"<item nick='%s' role='none'>\"\n\t\t\t\t\t\t\t\"<reason>%s</reason>\"\n\t\t\t\t\t\t\"</item>\"\n\t\t\t\t\t\"</query>\"\n\t\t\t\t\t,roomnick, comment\n\t\t\t\t);\n\t\t}\n\t\telse if (*console && !strcmp(arg[0]+1, \"msg\") && JCL_IsChatroom(jcl, console))\n\t\t{\n\t\t\tchar *whom = arg[1];\n\t\t\tchar *msgtext = argstart[2];\n\t\t\tJCL_AddClientMessagef(jcl,\n\t\t\t\t\"<message to='%s/%s' type='chat'>\"\n\t\t\t\t\t\"<body>%s</body>\"\n\t\t\t\t\"</message>\"\n\t\t\t\t, console, whom, msgtext\n\t\t\t\t);\n\t\t}\n\t\telse if (*console && !strcmp(arg[0]+1, \"nick\") && JCL_IsChatroom(jcl, console))\n\t\t{\t//FIXME: needs escape\n\t\t\tchar *mynewnick = arg[1];\n\t\t\tJCL_AddClientMessagef(jcl,\n\t\t\t\t\"<presence to='%s/%s'/>\"\n\t\t\t\t, console, mynewnick\n\t\t\t\t);\n\t\t}\n\t\telse if (*console && !strcmp(arg[0]+1, \"part\") && JCL_IsChatroom(jcl, console))\n\t\t{\t//FIXME: escapes\n\t\t\tbuddy_t *b;\n\t\t\tchar *myroomnick = \"\";\n\t\t\tchar *comment = argstart[1];\n\n\t\t\tif (JCL_FindBuddy(jcl, console, &b, NULL, false))\n\t\t\t\tif (b && b->ourselves)\n\t\t\t\t\tmyroomnick = b->ourselves->resource;\n\n\t\t\tJCL_AddClientMessagef(jcl,\n\t\t\t\t\"<presence to='%s/%s' type='unavailable'>\"\n\t\t\t\t\t\"<status>%s</status>\"\n\t\t\t\t\"</presence>\"\n\t\t\t\t, console, myroomnick, comment\n\t\t\t\t);\n\n\t\t\tif (b && b->btype == BT_CHATROOM)\n\t\t\t\tJCL_ForgetBuddy(jcl, b, NULL);\n\t\t}\n\t\telse if (*console && !strcmp(arg[0]+1, \"topic\") && JCL_IsChatroom(jcl, console))\n\t\t{\t//FIXME: escapes\n\t\t\tchar *newtopic = argstart[1];\n\t\t\tJCL_AddClientMessagef(jcl,\n\t\t\t\t\"<message to='%s' type='groupchat'>\"\n\t\t\t\t\t\"<subject>%s</subject>\"\n\t\t\t\t\"</message>\"\n\t\t\t\t, console, newtopic\n\t\t\t\t);\n\t\t}\n\t\telse if (*console && !strcmp(arg[0]+1, \"userlist\") && JCL_IsChatroom(jcl, console))\n\t\t{\n\t\t\tbuddy_t *b;\n\t\t\tbresource_t *r;\n\t\t\tif (JCL_FindBuddy(jcl, console, &b, NULL, false))\n\t\t\t{\n\t\t\t\tCon_SubPrintf(console, \"Users on channel %s\\n\", console);\n\t\t\t\tfor (r = b->resources; r; r = r->next)\n\t\t\t\t{\n\t\t\t\t\tCon_SubPrintf(console, \" %s:\", r->resource);\n\t\t\t\t\tJCL_PrintBuddyStatus(console, jcl, b, r);\n\t\t\t\t\tCon_SubPrintf(console, \"\\n\");\n\t\t\t\t}\n\t\t\t\tCon_SubPrintf(console, \"<END>\\n\");\n\t\t\t}\n\t\t}\n\t\telse if (!strcmp(arg[0]+1, \"open\") || !strcmp(arg[0]+1, \"connect\") || !strcmp(arg[0]+1, \"autoopen\") || !strcmp(arg[0]+1, \"autoconnect\") || !strcmp(arg[0]+1, \"plainopen\") || !strcmp(arg[0]+1, \"plainconnect\") || !strcmp(arg[0]+1, \"tlsopen\") || !strcmp(arg[0]+1, \"tlsconnect\"))\n\t\t{\t//tlsconnect is 'old'.\n\t\t\tint tls;\n\t\t\tif (!*arg[1])\n\t\t\t{\n\t\t\t\tXMPP_Menu_Connect();\n\t\t\t\tCon_SubPrintf(console, \"%s <account@domain/resource> <password> <server>\\n\", arg[0]+1);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (jcl)\n\t\t\t{\n\t\t\t\tCon_TrySubPrint(console, \"You are already connected\\nPlease /quit first\\n\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (!strncmp(arg[0]+1, \"tls\", 3))\n\t\t\t\ttls = 2;\t//old initial-tls connect\n\t\t\telse if (!strncmp(arg[0]+1, \"plain\", 5))\n\t\t\t\ttls = -1;\t//don't bother with tls. at all.\n\t\t\telse if (!strncmp(arg[0]+1, \"auto\", 4))\n\t\t\t\ttls = 0;\t//use tls if its available, but don't really care otherwise.\n\t\t\telse\n\t\t\t\ttls = 1;\t//require tls\n\t\t\tjcl = jclients[accid] = JCL_Connect(accid, arg[3], tls, arg[1], arg[2]);\n\t\t\tif (!jclients[accid])\n\t\t\t{\n\t\t\t\tCon_TrySubPrint(console, \"Connect failed\\n\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tjclient_configdirty = true;\n\t\t}\n\t\telse if (!strcmp(arg[0]+1, \"help\")) \n\t\t{\n\t\t\tCon_TrySubPrint(console, \"^[/\" COMMANDPREFIX \" /connect USERNAME@DOMAIN/RESOURCE [PASSWORD] [XMPPSERVER]^]\\n\");\n\t\t\tCon_TrySubPrint(console, \"eg for gmail: ^[/\" COMMANDPREFIX \" /connect myusername@gmail.com^] (using oauth2)\\n\");\n\t\t\tCon_TrySubPrint(console, \"eg for gmail: ^[/\" COMMANDPREFIX \" /connect myusername@gmail.com mypassword^] (warning: password will be saved locally in plain text)\\n\");\n//\t\t\tCon_TrySubPrint(console, \"eg for facebook: ^[/\" COMMANDPREFIX \" /connect myusername@chat.facebook.com mypassword chat.facebook.com^]\\n\");\n//\t\t\tCon_TrySubPrint(console, \"eg for msn: ^[/\" COMMANDPREFIX \" /connect myusername@messanger.live.com mypassword^]\\n\");\n\t\t\tCon_TrySubPrint(console, \"Note that this info will be used the next time you start quake.\\n\");\n\n\t\t\t//small note:\n\t\t\t//for the account 'me@example.com' the server to connect to can be displayed with:\n\t\t\t//nslookup -querytype=SRV _xmpp-client._tcp.example.com\n\t\t\t//srv resolving seems to be non-standard on each system, I don't like having to special case things.\n\t\t\tCon_TrySubPrint(console, \t\"^[/\" COMMANDPREFIX \" /help^]\\n\"\n\t\t\t\t\t\t\t\t\t\t\"This text...\\n\");\n\t\t\tCon_TrySubPrint(console, \t\"^[/\" COMMANDPREFIX \" /raw <XML STANZAS/>^]\\n\"\n\t\t\t\t\t\t\t\t\t\t\"For debug hackery.\\n\");\n\t\t\tCon_TrySubPrint(console, \t\"^[/\" COMMANDPREFIX \" /friend accountname@domain friendlyname^]\\n\"\n\t\t\t\t\t\t\t\t\t\t\"Befriends accountname, and shows them in your various lists using the friendly name. Can also be used to rename friends.\\n\");\n\t\t\tCon_TrySubPrint(console,\t\"^[/\" COMMANDPREFIX \" /unfriend accountname@domain^]\\n\"\n\t\t\t\t\t\t\t\t\t\t\"Ostracise your new best enemy. You will no longer see them and they won't be able to contact you.\\n\");\n\t\t\tCon_TrySubPrint(console, \t\"^[/\" COMMANDPREFIX \" /blist^]\\n\"\n\t\t\t\t\t\t\t\t\t\t\"Show all your friends! Names are clickable and will begin conversations.\\n\");\n\t\t\tCon_TrySubPrint(console, \t\"^[/\" COMMANDPREFIX \" /quit^]\\n\"\n\t\t\t\t\t\t\t\t\t\t\"Disconnect from the XMPP server, noone will be able to hear you scream.\\n\");\n\t\t\tCon_TrySubPrint(console, \t\"^[/\" COMMANDPREFIX \" /join accountname@domain^]\\n\"\n\t\t\t\t\t\t\t\t\t\t\"Joins your friends game (they will be prompted).\\n\");\n\t\t\tCon_TrySubPrint(console, \t\"^[/\" COMMANDPREFIX \" /invite accountname@domain^]\\n\"\n\t\t\t\t\t\t\t\t\t\t\"Invite someone to join your game (they will be prompted).\\n\");\n\t\t\tCon_TrySubPrint(console, \t\"^[/\" COMMANDPREFIX \" /voice accountname@domain^]\\n\"\n\t\t\t\t\t\t\t\t\t\t\"Begin a bi-directional peer-to-peer voice conversation with someone (they will be prompted).\\n\");\n\t\t\tCon_TrySubPrint(console, \t\"^[/\" COMMANDPREFIX \" /msg ACCOUNTNAME@domain \\\"your message goes here\\\"^]\\n\"\n\t\t\t\t\t\t\t\t\t\t\"Sends a message to the named person. If given a resource postfix, your message will be sent only to that resource.\\n\");\n\t\t\tCon_TrySubPrint(console, \t\"If no arguments, will print out your friends list. If no /command is used, the arguments will be sent as a message to the person you last sent a message to.\\n\");\n\t\t}\n\t\telse if (!strcmp(arg[0]+1, \"clear\"))\n\t\t{\n\t\t\t//just clears the current console.\n\t\t\tif (*console)\n\t\t\t{\n\t\t\t\tconfuncs->Destroy(console);\n\t\t\t\tCon_SubPrintf(console, \"\");\n\t\t\t\tif (confuncs)\n\t\t\t\t\tconfuncs->SetActive(console);\n\t\t\t}\n\t\t\telse\n\t\t\t\tcmdfuncs->AddText(\"\\nclear\\n\", true);\n\t\t}\n\t\telse if (!jcl)\n\t\t{\n\t\t\tCon_SubPrintf(console, \"No account specified. Cannot %s\\n\", arg[0]);\n\t\t}\n\t\telse if (!strcmp(arg[0]+1, \"oa2token\"))\n\t\t{\n\t\t\tjclient_configdirty = true;\n\t\t\tfree(jcl->sasl.oauth2.authtoken);\n\t\t\tjcl->sasl.oauth2.authtoken = strdup(arg[1]);\n\t\t\tif (jcl->status == JCL_INACTIVE)\n\t\t\t\tjcl->status = JCL_DEAD;\n\t\t}\n\t\telse if (!strcmp(arg[0]+1, \"set\"))\n\t\t{\n\t\t\tjclient_configdirty = true;\n\t\t\tif (!strcmp(arg[1], \"savepassword\"))\n\t\t\t\tjcl->savepassword = atoi(arg[2]);\n\t\t\telse if (!strcmp(arg[1], \"avatars\"))\n\t\t\t{\n\t\t\t\tjcl->enabledcapabilities = (jcl->enabledcapabilities & ~CAP_AVATARS) | (atoi(arg[2])?CAP_AVATARS:0);\n\t\t\t\tJCL_GeneratePresence(jcl, true);\n\t\t\t}\n\t\t\telse if (!strcmp(arg[1], \"ft\"))\n\t\t\t{\n\t\t\t\tjcl->enabledcapabilities = (jcl->enabledcapabilities & ~CAP_SIFT) | (atoi(arg[2])?CAP_SIFT:0);\n\t\t\t\tJCL_GeneratePresence(jcl, false);\n\t\t\t}\n\t\t\telse if (!strcmp(arg[1], \"debug\"))\n\t\t\t\tjcl->streamdebug = atoi(arg[2]);\n\t\t\telse if (!strcmp(arg[1], \"resource\"))\n\t\t\t\tQ_strlcpy(jcl->resource, arg[2], sizeof(jcl->resource));\n\t\t\telse\n\t\t\t\tCon_SubPrintf(console, \"Sorry, setting not recognised.\\n\", arg[0]);\n\t\t}\n\t\telse if (!strcmp(arg[0]+1, \"password\"))\n\t\t{\n\t\t\tjclient_configdirty = true;\n\t\t\tQ_strncpyz(jcl->sasl.password_plain, arg[1], sizeof(jcl->sasl.password_plain));\n\t\t\tjcl->sasl.password_hash_size = 0;\n\t\t\tif (jcl->status == JCL_INACTIVE)\n\t\t\t\tjcl->status = JCL_DEAD;\n\t\t}\n\t\telse if (!strcmp(arg[0]+1, \"quit\"))\n\t\t{\n\t\t\t//disconnect from the xmpp server.\n\t\t\tJCL_CloseConnection(jcl, \"/quit\", false);\n\t\t}\n\t\telse if (jcl->status != JCL_ACTIVE)\n\t\t{\n\t\t\tCon_SubPrintf(console, \"You are not authed. Please wait.\\n\", arg[0]);\n\t\t}\n\t\telse if (!strcmp(arg[0]+1, \"blist\"))\n\t\t{\n\t\t\t//print out a full list of everyone, even those offline.\n\t\t\tJCL_PrintBuddyList(console, jcl, true);\n\t\t}\n\t\telse if (!strcmp(arg[0]+1, \"msg\"))\n\t\t{\n\t\t\t//FIXME: validate the dest. deal with xml markup in dest.\n\t\t\tQ_strlcpy(jcl->defaultdest, arg[1], sizeof(jcl->defaultdest));\n\t\t\tmsg = arg[2];\n\n\t\t\t//reparse the commands, so we get all trailing text\n\t\t\tmsg = imsg;\n\t\t\tmsg = JCL_ParseOut(msg, arg[0], sizeof(arg[0]));\n\t\t\tmsg = JCL_ParseOut(msg, arg[1], sizeof(arg[1]));\n\t\t\twhile(*msg == ' ')\n\t\t\t\tmsg++;\n\n\t\t\tJCL_SendMessage(jcl, jcl->defaultdest, msg);\n\t\t}\n\t\telse if (!strcmp(arg[0]+1, \"friend\")) \n\t\t{\n\t\t\tXMPP_AddFriend(jcl, arg[1], arg[2]);\n\t\t}\n\t\telse if (!strcmp(arg[0]+1, \"unfriend\")) \n\t\t{\n\t\t\tbuddy_t *b;\n\t\t\tif (JCL_FindBuddy(jcl, arg[1], &b, NULL, false))\n\t\t\t{\n\t\t\t\tif (b->btype == BT_CHATROOM)\n\t\t\t\t\tJCL_ForgetBuddy(jcl, b, NULL);\n\t\t\t\telse if (b->btype == BT_ROSTER)\n\t\t\t\t{\n\t\t\t\t\t//hide from em\n\t\t\t\t\tJCL_AddClientMessagef(jcl, \"<presence to='%s' type='unsubscribed'/>\", arg[1]);\n\n\t\t\t\t\t//stop looking for em\n\t\t\t\t\tJCL_AddClientMessagef(jcl, \"<presence to='%s' type='unsubscribe'/>\", arg[1]);\n\n\t\t\t\t\t//stop listing em\n\t\t\t\t\tJCL_SendIQf(jcl, NULL, \"set\", NULL, \"<query xmlns='jabber:iq:roster'><item jid='%s' subscription='remove' /></query>\", arg[1]);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tJCL_AddClientMessagef(jcl, \"<presence to='%s' type='unavailable'/>\", arg[1]);\n\t\t\t}\n\t\t}\n#ifdef JINGLE\n\t\telse if (!strcmp(arg[0]+1, \"join\")) \n\t\t{\n\t\t\tJCL_ToJID(jcl, *arg[1]?arg[1]:console, nname, sizeof(nname), true);\n\t\t\tJCL_Join(jcl, nname, NULL, true, ICEP_QWCLIENT);\n\t\t}\n\t\telse if (!strcmp(arg[0]+1, \"invite\")) \n\t\t{\n\t\t\tJCL_ToJID(jcl, *arg[1]?arg[1]:console, nname, sizeof(nname), true);\n\t\t\tJCL_Join(jcl, nname, NULL, true, ICEP_QWSERVER);\n\t\t}\n\t\telse if (!strcmp(arg[0]+1, \"voice\") || !strcmp(arg[0]+1, \"call\")) \n\t\t{\n\t\t\tJCL_ToJID(jcl, *arg[1]?arg[1]:console, nname, sizeof(nname), true);\n\t\t\tJCL_Join(jcl, nname, NULL, true, ICEP_VOICE);\n\t\t}\n\t\telse if (!strcmp(arg[0]+1, \"kick\")) \n\t\t{\n\t\t\tJCL_ToJID(jcl, *arg[1]?arg[1]:console, nname, sizeof(nname), true);\n\t\t\tJCL_Join(jcl, nname, NULL, false, ICEP_INVALID);\n\t\t}\n#endif\n#ifdef FILETRANSFERS\n\t\telse if (!strcmp(arg[0]+1, \"sendfile\"))\n\t\t{\n\t\t\tchar *fname = arg[1];\n\t\t\n\t\t\tif (!(jcl->enabledcapabilities & CAP_SIFT))\n\t\t\t{\n\t\t\t\tCon_SubPrintf(console, \"XMPP: file transfers are not enabled for this account. Edit your config.\\n\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (!*fname || strchr(fname, '*'))\n\t\t\t{\n\t\t\t\tCon_SubPrintf(console, \"XMPP: /sendfile FILENAME [to]\\n\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tJCL_ToJID(jcl, *arg[2]?arg[2]:console, nname, sizeof(nname), true);\n\n\t\t\t\tXMPP_FT_SendFile(jcl, console, nname, fname);\n\t\t\t}\n\t\t}\n#endif\n\t\telse if (!strcmp(arg[0]+1, \"joinchatroom\") || !strcmp(arg[0]+1, \"muc\") || !strcmp(arg[0]+1, \"joinmuc\")) \n\t\t{\n\t\t\tif (!*arg[1])\n\t\t\t\tCon_SubPrintf(console, \"xmpp %s room@server roomnick password\\n\");\n\t\t\telse\n\t\t\t\tJCL_JoinMUCChat(jcl, arg[1], NULL, *arg[2]?arg[2]:NULL, arg[3]);\n\t\t}\n\t\telse if (!strcmp(arg[0]+1, \"leavechatroom\") || !strcmp(arg[0]+1, \"leavemuc\")) \n\t\t{\n\t\t\tchar roomserverhandle[512];\n\t\t\tbuddy_t *b;\n\t\t\tbresource_t *r;\n\t\t\tif (!Q_snprintfz(roomserverhandle, sizeof(roomserverhandle), \"%s@%s/%s\", arg[1], arg[2], arg[3]) && JCL_FindBuddy(jcl, roomserverhandle, &b, &r, false))\n\t\t\t{\n\t\t\t\tJCL_AddClientMessagef(jcl, \"<presence to='%s' type='unavailable'/>\", roomserverhandle);\n\t\t\t\tJCL_ForgetBuddy(jcl, b, NULL);\n\t\t\t}\n\t\t}\n\t\telse if (!strcmp(arg[0]+1, \"slap\")) \n\t\t{\n\t\t\tchar *msgtab[] =\n\t\t\t{\n\t\t\t\t\"/me slaps you around a bit with a large trout\",\n\t\t\t\t\"/me slaps you around a bit with a large tunafish\",\n\t\t\t\t\"/me slaps you around a bit with a slimy hagfish\",\n\t\t\t\t\"/me slaps a large trout around a bit with your face\",\n\t\t\t\t\"/me gets eaten by a rabid shark while trying to slap you with it\",\n\t\t\t\t\"/me gets crushed under the weight of a large whale\",\n\t\t\t\t\"/me searches for a fresh fish, but gets crabs instead\",\n\t\t\t\t\"/me searches for a fish, but there are no more fish in the sea\",\n\t\t\t\t\"/me tickles you around a bit with a large fish finger\",\n\t\t\t\t\"/me goes to order cod and chips. brb\",\n\t\t\t\t\"/me goes to watch some monty python\"\n\t\t\t};\n\t\t\tJCL_ToJID(jcl, *arg[1]?arg[1]:console, nname, sizeof(nname), true);\n\t\t\tJCL_AttentionMessage(jcl, nname, msgtab[rand()%(sizeof(msgtab)/sizeof(msgtab[0]))]);\n\t\t}\n\t\telse if (!strcmp(arg[0]+1, \"poke\")) \n\t\t{\n\t\t\tJCL_ToJID(jcl, *arg[1]?arg[1]:console, nname, sizeof(nname), true);\n\t\t\tJCL_AttentionMessage(jcl, nname, NULL);\n\t\t}\n\t\telse if (!strcmp(arg[0]+1, \"raw\")) \n\t\t{\n\t\t\t//parse it first, so noone ever generates invalid xml and gets kicked... too obviously.\n\t\t\tint pos = 0;\n\t\t\tint maxpos = strlen(arg[1]);\n\t\t\txmltree_t *t;\n\t\t\twhile (pos != maxpos)\n\t\t\t{\n\t\t\t\tt = XML_Parse(arg[1], &pos, maxpos, false, \"\");\n\t\t\t\tif (t)\n\t\t\t\t\tXML_Destroy(t);\n\t\t\t\telse\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (pos == maxpos)\n\t\t\t{\n\t\t\t\tjcl->streamdebug = true;\n\t\t\t\tJCL_AddClientMessageString(jcl, arg[1]);\n\t\t\t}\n\t\t\telse\n\t\t\t\tCon_Printf(\"XML not well formed\\n\");\n\t\t}\n\t\telse\n\t\t\tCon_SubPrintf(console, \"Unrecognised command: %s\\n\", arg[0]);\n\t}\n\telse\n\t{\n\t\tmsg = imsg;\n\t\tif (jcl && jcl->status == JCL_ACTIVE)\n\t\t{\n\t\t\tif (!*msg)\n\t\t\t{\n\t\t\t\tif (!*console)\n\t\t\t\t{\n\t\t\t\t\tif (confuncs)\n\t\t\t\t\t\tJCL_RegenerateBuddyList(true);\n\t\t\t\t\telse\n\t\t\t\t\t\tJCL_PrintBuddyList(console, jcl, false);\n\t\t\t\t\t//Con_TrySubPrint(console, \"For help, type \\\"^[/\" COMMANDPREFIX \" /help^]\\\"\\n\");\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tJCL_ToJID(jcl, *console?console:jcl->defaultdest, nname, sizeof(nname), true);\n\t\t\t\tJCL_SendMessage(jcl, nname, msg);\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (!*msg && confuncs)\n\t\t\t\tJCL_RegenerateBuddyList(true);\n\t\t\telse\n\t\t\t\tCon_TrySubPrint(console, \"Not connected. For help, type \\\"^[/\" COMMANDPREFIX \" /help^]\\\"\\n\");\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "plugins/jabber/jingle.c",
    "content": "#include \"xmpp.h\"\n#ifdef JINGLE\nstatic struct c2c_s *JCL_JingleAddContentToSession(jclient_t *jcl, struct c2c_s *c2c, const char *with, bresource_t *bres, qboolean creator, const char *sid, const char *cname, int method, int mediatype)\n{\n\tstruct icestate_s *ice = NULL;\n\tchar generatedname[64];\n\tchar stunhost[256];\n\tint c;\n\tchar *s;\n\tchar token[MAX_OSPATH];\n\n\tif (!bres)\n\t\treturn NULL;\n\n\t//make sure we can add more contents to this session\n\t//block dupe content names.\n\tif (c2c)\n\t{\n\t\tif (c2c->contents == sizeof(c2c->content) / sizeof(c2c->content[0]))\n\t\t\treturn NULL;\n\t\tfor (c = 0; c < c2c->contents; c++)\n\t\t\tif (!strcmp(c2c->content[c].name, cname))\n\t\t\t\treturn NULL;\n\t}\n\t\n\tif (piceapi)\n\t\tice = piceapi->Create(NULL, sid, with, method, mediatype, creator);\n\tif (ice)\n\t{\n\t\tpiceapi->Get(ice, \"sid\", generatedname, sizeof(generatedname));\n\t\tsid = generatedname;\n\n\t\t//the controlling role MUST be assumed by the initiator and the controlled role MUST be assumed by the responder\n\t\tpiceapi->Set(ice, \"controller\", creator?\"1\":\"0\");\n\n\t\tif (creator && mediatype == ICEP_VOICE)\n\t\t{\n\t\t\t//note: the engine will ignore codecs it does not support.\n\t\t\tpiceapi->Set(ice, \"codec96\", \"opus@48000\");\n\t\t\tpiceapi->Set(ice, \"codec97\", \"speex@16000\");\t//wide\n\t\t\tpiceapi->Set(ice, \"codec98\", \"speex@8000\");\t\t//narrow\n\t\t\tpiceapi->Set(ice, \"codec99\", \"speex@32000\");\t//ultrawide\n\t\t\tpiceapi->Set(ice, \"codec0\", \"pcmu@8000\");\n\t\t\tpiceapi->Set(ice, \"codec8\", \"pcma@8000\");\n\t\t}\n\t}\n\telse\n\t\treturn NULL;\t//no way to get the local ip otherwise, which means things won't work proper\n\n\tif (!c2c)\n\t{\n\t\tc2c = malloc(sizeof(*c2c) + strlen(sid));\n\t\tmemset(c2c, 0, sizeof(*c2c));\n\t\tc2c->next = jcl->c2c;\n\t\tjcl->c2c = c2c;\n\t\tstrcpy(c2c->sid, sid);\t//safe due to trailing space.\n\n\t\tc2c->with = strdup(with);\n\t\tc2c->peercaps = bres->caps;\n\t\tc2c->creator = creator;\n\t}\n\n\tQ_strlcpy(c2c->content[c2c->contents].name, cname, sizeof(c2c->content[c2c->contents].name));\n\tc2c->content[c2c->contents].method = method;\n\tc2c->content[c2c->contents].mediatype = mediatype;\n\n\t//copy out the interesting parameters\n\tc2c->content[c2c->contents].ice = ice;\n\tc2c->contents++;\n\n\n\t//query dns to see if there's a stunserver hosted by the same domain\n\t//nslookup -querytype=SRV _stun._udp.example.com\n\t//for msn, live.com has one, messanger.live.com has one, but messenger.live.com does NOT. seriously, the typo has more services.  wtf microsoft?\n\t//google doesn't provide a stun srv entry\n\t//facebook doesn't provide a stun srv entry\n\tif (!Q_snprintfz(stunhost, sizeof(stunhost), \"_stun._udp.%s\", jcl->domain) && NET_DNSLookup_SRV(stunhost, stunhost, sizeof(stunhost)))\n\t\tpiceapi->Set(ice, \"stunip\", stunhost);\n\telse\n\t{\n\t\t//there is no real way to query stun servers from the xmpp server.\n\t\t//while there is some extdisco extension (aka: the 'standard' way), it has some huge big fat do-not-implement message (and googletalk does not implement it).\n\t\t//google provide their own jingleinfo extension. Which also has some huge fat 'do-not-implement' message. hardcoding the address should provide equivelent longevity. :(\n\t\t//google also don't provide stun srv records.\n\t\t//so we're basically screwed if we want to work with the googletalk xmpp service long term.\n\t\t//more methods are best, I suppose, but I'm lazy.\n\n\t\t//try to use our default rtcbroker setting as a stun server, too.\n\t\tchar *stun = cvarfuncs->GetNVFDG(\"net_ice_broker\", \"\", 0, NULL, NULL)->string;\n\t\ts = strstr(stun, \"://\");\n\t\tif (s) stun = s+3;\n\t\tpiceapi->Set(ice, \"server\", va(\"stun:%s\", stun));\n\t}\n\n\t//if the user has manually set up some other stun servers, use them.\n\ts = cvarfuncs->GetNVFDG(\"net_ice_servers\", \"\", 0, NULL, NULL)->string;\n\twhile((s=cmdfuncs->ParseToken(s, token, sizeof(token), NULL)))\n\t\tpiceapi->Set(ice, \"server\", token);\n\treturn c2c;\n}\nstatic qboolean JCL_JingleAcceptAck(jclient_t *jcl, xmltree_t *tree, struct iq_s *iq)\n{\n\tint i;\n\tstruct c2c_s *c2c;\n\tif (tree)\n\t{\n\t\tfor (c2c = jcl->c2c; c2c; c2c = c2c->next)\n\t\t{\n\t\t\tif (c2c == iq->usrptr)\n\t\t\t{\n\t\t\t\tfor (i = 0; i < c2c->contents; i++)\n\t\t\t\t{\n\t\t\t\t\tif (c2c->content[i].ice)\n\t\t\t\t\t\tpiceapi->Set(c2c->content[i].ice, \"state\", STRINGIFY(ICE_CONNECTING));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn true;\n}\n\n\nstatic void JCL_PopulateAudioDescription(xmltree_t *description, struct icestate_s *ice)\n{\n\txmltree_t *payload;\n\tint i;\n\tint pcma = -1, pcmu = -1;\n\tfor (i = 0; i <= 127; i++)\n\t{\n\t\tchar codecname[64];\n\t\tchar argn[64];\n\t\tQ_snprintf(argn, sizeof(argn), \"codec%i\", i);\n\t\tif (piceapi->Get(ice, argn,  codecname, sizeof(codecname)))\n\t\t{\n\t\t\tif (!strcasecmp(codecname, \"speex@8000\") || !strcasecmp(codecname, \"speex@16000\") || !strcasecmp(codecname, \"speex@32000\"))\n\t\t\t{\t//speex narrowband\n\t\t\t\tpayload = XML_CreateNode(description, \"payload-type\", \"\", \"\");\n\t\t\t\tXML_AddParameter(payload, \"channels\", \"1\");\n\t\t\t\tXML_AddParameter(payload, \"clockrate\", codecname+6);\n\t\t\t\tXML_AddParameter(payload, \"id\", argn+5);\n\t\t\t\tXML_AddParameter(payload, \"name\", \"speex\");\n\t\t\t}\n\t\t\telse if (!strcasecmp(codecname, \"opus\") || !strcasecmp(codecname, \"opus@48000\"))\n\t\t\t{\t//opus codec. implicitly at 48khz\n\t\t\t\tpayload = XML_CreateNode(description, \"payload-type\", \"\", \"\");\n\t\t\t\tXML_AddParameter(payload, \"channels\", \"1\");\n\t\t\t\tXML_AddParameter(payload, \"id\", argn+5);\n\t\t\t\tXML_AddParameter(payload, \"name\", \"opus\");\n\t\t\t}\n\t\t\telse if (!strcasecmp(codecname, \"pcma@8000\") || !strcasecmp(codecname, \"pcmu@8000\"))\n\t\t\t{\t//pcma/pcmu.\n\t\t\t\t//these get flagged to ensure they appear last, because they're not very good, esp compared to opus,\n\t\t\t\t// however they are simple and more widely distributed on traditional voice services,\n\t\t\t\t// so they're an important fallback\n\t\t\t\tif (!strcasecmp(codecname, \"pcma@8000\") && pcma < 0)\n\t\t\t\t\tpcma = i;\n\t\t\t\telse if (!strcasecmp(codecname, \"pcmu@8000\") && pcmu < 0)\n\t\t\t\t\tpcmu = i;\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tpayload = XML_CreateNode(description, \"payload-type\", \"\", \"\");\n\t\t\t\t\tXML_AddParameter(payload, \"channels\", \"1\");\n\t\t\t\t\tXML_AddParameter(payload, \"clockrate\", codecname+5);\n\t\t\t\t\tXML_AddParameter(payload, \"id\", argn+5);\n\t\t\t\t\tcodecname[4] = 0;\n\t\t\t\t\tXML_AddParameter(payload, \"name\", codecname);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tif (pcma>=0)\n\t{\n\t\tpayload = XML_CreateNode(description, \"payload-type\", \"\", \"\");\n\t\tXML_AddParameter(payload, \"channels\", \"1\");\n\t\tXML_AddParameter(payload, \"clockrate\", \"8000\");\n\t\tXML_AddParameteri(payload, \"id\", pcma);\n\t\tXML_AddParameter(payload, \"name\", \"pcma\");\n\t}\n\tif (pcmu>=0)\n\t{\n\t\tpayload = XML_CreateNode(description, \"payload-type\", \"\", \"\");\n\t\tXML_AddParameter(payload, \"channels\", \"1\");\n\t\tXML_AddParameter(payload, \"clockrate\", \"8000\");\n\t\tXML_AddParameteri(payload, \"id\", pcmu);\n\t\tXML_AddParameter(payload, \"name\", \"pcmu\");\n\t}\n}\n\nenum\n{\n\tJE_ACKNOWLEDGE,\n\tJE_UNSUPPORTED,\n\tJE_OUTOFORDER,\n\tJE_TIEBREAK,\n\tJE_UNKNOWNSESSION,\n\tJE_UNSUPPORTEDINFO\n};\nstatic void JCL_JingleError(jclient_t *jcl, xmltree_t *tree, const char *from, const char *id, int type)\n{\n\tswitch(type)\n\t{\n\tcase JE_ACKNOWLEDGE:\n\t\tJCL_AddClientMessagef(jcl,\n\t\t\t\"<iq type='result' to='%s' id='%s' />\", from, id);\n\t\tbreak;\n\tcase JE_UNSUPPORTED:\n\t\tJCL_AddClientMessagef(jcl,\n\t\t\t\t\"<iq id='%s' to='%s' type='error'>\"\n\t\t\t\t  \"<error type='cancel'>\"\n\t\t\t\t\t\"<bad-request xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>\"\n\t\t\t\t  \"</error>\"\n\t\t\t\t\"</iq>\", id, from);\n\t\tbreak;\n\tcase JE_UNKNOWNSESSION:\n\t\tJCL_AddClientMessagef(jcl,\n\t\t\t\t\"<iq id='%s' to='%s' type='error'>\"\n\t\t\t\t  \"<error type='modify'>\"\n\t\t\t\t\t\"<item-not-found xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>\"\n\t\t\t\t\t\"<unknown-session xmlns='urn:xmpp:jingle:errors:1'/>\"\n\t\t\t\t  \"</error>\"\n\t\t\t\t\"</iq>\", id, from);\n\t\tbreak;\n\tcase JE_UNSUPPORTEDINFO:\n\t\tJCL_AddClientMessagef(jcl,\n\t\t\t\t\"<iq id='%s' to='%s' type='error'>\"\n\t\t\t\t  \"<error type='modify'>\"\n\t\t\t\t\t\"<feature-not-implemented xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>\"\n\t\t\t\t\t\"<unsupported-info xmlns='urn:xmpp:jingle:errors:1'/>\"\n\t\t\t\t  \"</error>\"\n\t\t\t\t\"</iq>\", id, from);\n\t\tbreak;\n\t}\n}\n\n/*\nsends a jingle message to the peer.\naction should be one of multiple things:\nsession-terminate\t- totally not acceptable. this also closes the c2c\nsession-accept\t\t- details are okay. this also begins ice polling (on iq ack, once we're sure the peer got our message)\n\n(internally generated) transport-replace\t- details are okay, except we want a different transport method.\n*/\nstatic qboolean JCL_JingleSend(jclient_t *jcl, struct c2c_s *c2c, char *action)\n{\n\tqboolean result;\n\txmltree_t *jingle;\n\tint c;\n//\tstruct icestate_s *ice = c2c->ice;\n\tqboolean wasaccept = false;\n\tint transportmode = ICEM_ICE;\n#ifdef VOIP_LEGACY\n\tint cap = c2c->peercaps;\n#endif\n\n\tif (!c2c->contents)\n\t\taction = \"session-terminate\";\n\n#ifdef VOIP_LEGACY\n\tif ((cap & CAP_GOOGLE_VOICE) && !(cap & CAP_VOICE))\n\t{\n\t\t//legacy crap for google compatibility.\n\t\tif (!strcmp(action, \"session-initiate\"))\n\t\t{\n\t\t\txmltree_t *session = XML_CreateNode(NULL, \"session\", \"http://www.google.com/session\", \"\");\n\t\t\txmltree_t *description = XML_CreateNode(session, \"description\", \"http://www.google.com/session/phone\", \"\");\n\n\t\t\tXML_AddParameter(session, \"id\", c2c->sid);\n\t\t\tXML_AddParameter(session, \"initiator\", jcl->jid);\n\t\t\tXML_AddParameter(session, \"type\", \"initiate\");\n\t\t\tfor (c = 0; c < c2c->contents; c++)\n\t\t\t\tJCL_PopulateAudioDescription(description, c2c->content[c].ice);\n\n\t\t\tJCL_SendIQNode(jcl, NULL, \"set\", c2c->with, session, true);\n\t\t}\n\t\telse if (!strcmp(action, \"session-accept\"))\n\t\t{\n\t\t\txmltree_t *session = XML_CreateNode(NULL, \"session\", \"http://www.google.com/session\", \"\");\n\t\t\txmltree_t *description = XML_CreateNode(session, \"description\", \"http://www.google.com/session/phone\", \"\");\n\n\t\t\tXML_AddParameter(session, \"id\", c2c->sid);\n\t\t\tXML_AddParameter(session, \"initiator\", c2c->with);\n\t\t\tXML_AddParameter(session, \"type\", \"accept\");\n\t\t\tfor (c = 0; c < c2c->contents; c++)\n\t\t\t\tJCL_PopulateAudioDescription(description, c2c->content[c].ice);\n\n\t\t\tJCL_SendIQNode(jcl, JCL_JingleAcceptAck, \"set\", c2c->with, session, true)->usrptr = c2c;\n\t\t\tc2c->accepted = true;\n\t\t}\n\t\telse if (!strcmp(action, \"transport-info\"))\n\t\t{\n/*FIXME\n\t\t\tstruct icecandinfo_s *ca;\n\t\t\twhile ((ca = piceapi->ICE_GetLCandidateInfo(ice)))\n\t\t\t{\n\t\t\t\txmltree_t *session = XML_CreateNode(NULL, \"session\", \"http://www.google.com/session\", \"\");\n\n\t\t\t\tXML_AddParameter(session, \"id\", c2c->sid);\n\t\t\t\tXML_AddParameter(session, \"initiator\", c2c->creator?jcl->jid:c2c->with);\n\t\t\t\tXML_AddParameter(session, \"type\", \"candidates\");\n\n\t\t\t\t//one per message, apparently\n\t\t\t\tif (ca)\n\t\t\t\t{\n\t\t\t\t\tchar *ctypename[]={\"local\", \"stun\", \"stun\", \"relay\"};\n\t\t\t\t\tchar *pref[]={\"1\", \"0.9\", \"0.5\", \"0.2\"};\n\t\t\t\t\txmltree_t *candidate = XML_CreateNode(session, \"candidate\", \"\", \"\");\n\t\t\t\t\tchar pwd[64];\n\t\t\t\t\tchar uname[64];\n\n\t\t\t\t\tpiceapi->ICE_Get(ice, \"lufrag\",  uname, sizeof(uname));\n\t\t\t\t\tpiceapi->ICE_Get(ice, \"lpwd\",  pwd, sizeof(pwd));\n\n\t\t\t\t\tXML_AddParameter(candidate, \"address\", ca->addr);\n\t\t\t\t\tXML_AddParameteri(candidate, \"port\", ca->port);\n\t\t\t\t\tXML_AddParameter(candidate, \"name\", (ca->component==1)?\"rtp\":\"rtcp\");\n\t\t\t\t\tXML_AddParameter(candidate, \"username\", uname);\n\t\t\t\t\tXML_AddParameter(candidate, \"password\", pwd);\n\t\t\t\t\tXML_AddParameter(candidate, \"preference\", pref[ca->type]);\t//FIXME: c->priority\n\t\t\t\t\tXML_AddParameter(candidate, \"protocol\", \"udp\");\n\t\t\t\t\tXML_AddParameter(candidate, \"type\", ctypename[ca->type]);\n\t\t\t\t\tXML_AddParameteri(candidate, \"generation\", ca->generation);\n\t\t\t\t\tXML_AddParameteri(candidate, \"network\", ca->network);\n\t\t\t\t}\n\n\t\t\t\tJCL_SendIQNode(jcl, NULL, \"set\", c2c->with, session, true);\n\t\t\t}\n*/Con_Printf(\"No idea how to write candidates for gingle\\n\");\n\t\t}\n\t\telse if (!strcmp(action, \"session-terminate\"))\n\t\t{\n\t\t\tstruct c2c_s **link;\n\t\t\txmltree_t *session = XML_CreateNode(NULL, \"session\", \"http://www.google.com/session\", \"\");\n\n\t\t\tXML_AddParameter(session, \"id\", c2c->sid);\n\t\t\tXML_AddParameter(session, \"initiator\", c2c->creator?jcl->jid:c2c->with);\n\t\t\tXML_AddParameter(session, \"type\", \"terminate\");\n\t\t\tJCL_SendIQNode(jcl, NULL, \"set\", c2c->with, session, true);\n\n\t\t\tfor (link = &jcl->c2c; *link; link = &(*link)->next)\n\t\t\t{\n\t\t\t\tif (*link == c2c)\n\t\t\t\t{\n\t\t\t\t\t*link = c2c->next;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tfor (c = 0; c < c2c->contents; c++)\n\t\t\t{\n\t\t\t\tif (c2c->content[c].ice)\n\t\t\t\t\tpiceapi->ICE_Close(c2c->content[c].ice);\n\t\t\t\tc2c->content[c].ice = NULL;\n\t\t\t}\n\n\t\t\treturn false;\n\t\t}\n\t\telse\n\t\t{\n\t\t\treturn false;\n\t\t}\n\t\treturn true;\n\t}\n#endif\n\n\tjingle = XML_CreateNode(NULL, \"jingle\", \"urn:xmpp:jingle:1\", \"\");\n\tXML_AddParameter(jingle, \"sid\", c2c->sid);\n\n\tif (!strcmp(action, \"session-initiate\"))\n\t{\t//these attributes are meant to only be present in initiate. for call forwarding etc. which we don't properly support.\n\t\tXML_AddParameter(jingle, \"initiator\", jcl->fulljid);\n\t}\n\n\tif (!strcmp(action, \"session-terminate\"))\n\t{\n\t\tstruct c2c_s **link;\n\t\tfor (link = &jcl->c2c; *link; link = &(*link)->next)\n\t\t{\n\t\t\tif (*link == c2c)\n\t\t\t{\n\t\t\t\t*link = c2c->next;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tfor (c = 0; c < c2c->contents; c++)\n\t\t{\n\t\t\tif (c2c->content[c].ice)\n\t\t\t\tpiceapi->Close(c2c->content[c].ice, true);\n\t\t\tc2c->content[c].ice = NULL;\n\t\t}\n\n\t\tresult = false;\n\t}\n\telse\n\t{\n\t\tresult = true;\n\t\tfor (c = 0; c < c2c->contents; c++)\n\t\t{\n\t\t\txmltree_t *content = XML_CreateNode(jingle, \"content\", \"\", \"\");\n\t\t\tstruct icestate_s *ice = c2c->content[c].ice;\n\t\t\tXML_AddParameter(content, \"name\", c2c->content[c].name);\n\n\t\t\tif (!strcmp(action, \"session-accept\"))\n\t\t\t{\n\t\t\t\tif (c2c->content[c].method == transportmode)\n\t\t\t\t\tXML_AddParameter(jingle, \"responder\", jcl->fulljid);\n\t\t\t\telse\n\t\t\t\t\taction = \"transport-replace\";\n\t\t\t}\n\n\t\t\t{\n\t\t\t\txmltree_t *description;\n\t\t\t\txmltree_t *transport;\n\t\t\t\tif (transportmode == ICEM_RAW)\n\t\t\t\t{\n\t\t\t\t\ttransport = XML_CreateNode(content, \"transport\", \"urn:xmpp:jingle:transports:raw-udp:1\", \"\");\n\t\t\t\t\t{\n\t\t\t\t\t\txmltree_t *candidate;\n\t\t\t\t\t\tstruct icecandinfo_s *b = NULL;\n\t\t\t\t\t\tstruct icecandinfo_s *c;\n\t\t\t\t\t\twhile ((c = piceapi->GetLCandidateInfo(ice)))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (!b || b->priority < c->priority)\n\t\t\t\t\t\t\t\tb = c;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (b)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcandidate = XML_CreateNode(transport, \"candidate\", \"\", \"\");\n\t\t\t\t\t\t\tXML_AddParameter(candidate, \"ip\", b->addr);\n\t\t\t\t\t\t\tXML_AddParameteri(candidate, \"port\", b->port);\n\t\t\t\t\t\t\tXML_AddParameter(candidate, \"id\", b->candidateid);\n\t\t\t\t\t\t\tXML_AddParameteri(candidate, \"generation\", b->generation);\n\t\t\t\t\t\t\tXML_AddParameteri(candidate, \"component\", b->component);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse if (transportmode == ICEM_ICE)\n\t\t\t\t{\n\t\t\t\t\tchar val[64];\n\t\t\t\t\ttransport = XML_CreateNode(content, \"transport\", \"urn:xmpp:jingle:transports:ice-udp:1\", \"\");\n\t\t\t\t\tpiceapi->Get(ice, \"lufrag\",  val, sizeof(val));\n\t\t\t\t\tXML_AddParameter(transport, \"ufrag\", val);\n\t\t\t\t\tpiceapi->Get(ice, \"lpwd\",  val, sizeof(val));\n\t\t\t\t\tXML_AddParameter(transport, \"pwd\", val);\n\t\t\t\t\t{\n\t\t\t\t\t\tstruct icecandinfo_s *c;\n\t\t\t\t\t\twhile ((c = piceapi->GetLCandidateInfo(ice)))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tchar *ctypename[]={\"host\", \"srflx\", \"prflx\", \"relay\"};\n\t\t\t\t\t\t\txmltree_t *candidate = XML_CreateNode(transport, \"candidate\", \"\", \"\");\n\t\t\t\t\t\t\tXML_AddParameter(candidate, \"type\", ctypename[c->type]);\n\t\t\t\t\t\t\tXML_AddParameter(candidate, \"protocol\", \"udp\");\t//is this not just a little bit redundant? ice-udp? seriously?\n\t\t\t\t\t\t\tXML_AddParameteri(candidate, \"priority\", c->priority);\n\t\t\t\t\t\t\tXML_AddParameteri(candidate, \"port\", c->port);\n\t\t\t\t\t\t\tXML_AddParameteri(candidate, \"network\", c->network);\n\t\t\t\t\t\t\tXML_AddParameter(candidate, \"ip\", c->addr);\n\t\t\t\t\t\t\tXML_AddParameter(candidate, \"id\", c->candidateid);\n\t\t\t\t\t\t\tXML_AddParameteri(candidate, \"generation\", c->generation);\n\t\t\t\t\t\t\tXML_AddParameteri(candidate, \"foundation\", c->foundation);\n\t\t\t\t\t\t\tXML_AddParameteri(candidate, \"component\", c->component);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (strcmp(action, \"transport-info\"))\n\t\t\t\t{\n#ifdef VOIP\n\t\t\t\t\tif (c2c->content[c].mediatype == ICEP_VOICE)\n\t\t\t\t\t{\n\t\t\t\t\t\tXML_AddParameter(content, \"senders\", \"both\");\n\t\t\t\t\t\tXML_AddParameter(content, \"creator\", \"initiator\");\n\n\t\t\t\t\t\tdescription = XML_CreateNode(content, \"description\", \"urn:xmpp:jingle:apps:rtp:1\", \"\");\n\t\t\t\t\t\tXML_AddParameter(description, \"media\", MEDIATYPE_AUDIO);\n\n\t\t\t\t\t\tJCL_PopulateAudioDescription(description, ice);\n\t\t\t\t\t}\n\t\t\t\t\telse if (c2c->content[c].mediatype == ICEP_VIDEO)\n\t\t\t\t\t{\n\t\t\t\t\t\tXML_AddParameter(content, \"senders\", \"both\");\n\t\t\t\t\t\tXML_AddParameter(content, \"creator\", \"initiator\");\n\n\t\t\t\t\t\tdescription = XML_CreateNode(content, \"description\", \"urn:xmpp:jingle:apps:rtp:1\", \"\");\n\t\t\t\t\t\tXML_AddParameter(description, \"media\", MEDIATYPE_VIDEO);\n\n\t\t\t\t\t\t//JCL_PopulateAudioDescription(description, ice);\n\t\t\t\t\t}\n#endif\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tdescription = XML_CreateNode(content, \"description\", QUAKEMEDIAXMLNS, \"\");\n\t\t\t\t\t\tXML_AddParameter(description, \"media\", MEDIATYPE_QUAKE);\n\t\t\t\t\t\tif (c2c->content[c].mediatype == ICEP_QWSERVER)\n\t\t\t\t\t\t\tXML_AddParameter(description, \"host\", \"me\");\n\t\t\t\t\t\telse if (c2c->content[c].mediatype == ICEP_QWCLIENT)\n\t\t\t\t\t\t\tXML_AddParameter(description, \"host\", \"you\");\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (!strcmp(action, \"session-accept\"))\n\t\t\tc2c->accepted = wasaccept = true;\n\t}\n\n\tXML_AddParameter(jingle, \"action\", action);\n\n//\tCon_Printf(\"Sending Jingle:\\n\");\n//\tXML_ConPrintTree(jingle, 1);\n\tJCL_SendIQNode(jcl, wasaccept?JCL_JingleAcceptAck:NULL, \"set\", c2c->with, jingle, true)->usrptr = c2c;\n\n\treturn result;\n}\n\nvoid JCL_JingleTimeouts(jclient_t *jcl, qboolean killall)\n{\n\tint c;\n\tstruct c2c_s *c2c;\n\tfor (c2c = jcl->c2c; c2c; c2c = c2c->next)\n\t{\n\t\tfor (c = 0; c < c2c->contents; c++)\n\t\t{\n\t\t\tif (c2c->content[c].method == ICEM_ICE)\n\t\t\t{\n\t\t\t\tchar bah[2];\n\t\t\t\tpiceapi->Get(c2c->content[c].ice, \"newlc\", bah, sizeof(bah));\n\t\t\t\tif (atoi(bah))\n\t\t\t\t{\n\t\t\t\t\tCon_DPrintf(\"Sending updated local addresses\\n\");\n\t\t\t\t\tJCL_JingleSend(jcl, c2c, \"transport-info\");\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nvoid JCL_Join(jclient_t *jcl, const char *target, const char *sid, qboolean allow, int protocol)\n{\n\tstruct c2c_s *c2c = NULL, **link;\n\tchar autotarget[256];\n\tbuddy_t *b;\n\tbresource_t *br;\n\tint c;\n\tif (!jcl)\n\t\treturn;\n\n\tif (!JCL_FindBuddy(jcl, target, &b, &br, true))\n\t{\n\t\tCon_Printf(\"user/resource not known\\n\");\n\t\treturn;\n\t}\n\tif (!br)\n\t\tbr = b->defaultresource;\n\tif (!br)\n\t\tbr = b->resources;\n\n\tif (!strchr(target, '/'))\n\t{\n\t\tif (!br)\n\t\t{\n\t\t\tCon_Printf(\"User name not valid\\n\");\n\t\t\treturn;\n\t\t}\n\t\tQ_snprintf(autotarget, sizeof(autotarget), \"%s/%s\", b->accountdomain, br->resource);\n\t\ttarget = autotarget;\n\t}\n\n\tfor (link = &jcl->c2c; *link && !c2c; link = &(*link)->next)\n\t{\n\t\tif (!strcmp((*link)->with, target) && (!sid || !strcmp((*link)->sid, sid)))\n\t\t{\n\t\t\tif (protocol == ICEP_INVALID)\n\t\t\t\tc2c = *link;\n\t\t\telse\n\t\t\t{\n\t\t\t\tfor (c = 0; c < (*link)->contents; c++)\n\t\t\t\t\tif ((*link)->content[c].mediatype == protocol)\n\t\t\t\t\t\tc2c = *link;\n\t\t\t}\n\t\t}\n\t}\n\tif (allow)\n\t{\n\t\tif (!c2c)\n\t\t{\n\t\t\tif (!sid)\n\t\t\t{\n\t\t\t\tchar convolink[512], hanguplink[512];\n\t\t\t\tif (protocol == ICEP_INVALID)\n\t\t\t\t\tprotocol = ICEP_QWCLIENT;\n\t\t\t\tc2c = JCL_JingleAddContentToSession(jcl, NULL, target, br, true, sid, \"foobar2000\", DEFAULTICEMODE, protocol);\n\t\t\t\tJCL_JingleSend(jcl, c2c, \"session-initiate\");\n\n\t\t\t\tJCL_GenLink(jcl, convolink, sizeof(convolink), NULL, target, NULL, NULL, \"%s\", target);\n\t\t\t\tJCL_GenLink(jcl, hanguplink, sizeof(hanguplink), \"jdeny\", target, NULL, c2c->sid, \"%s\", \"Hang Up\");\n\t\t\t\tXMPP_ConversationPrintf(b->accountdomain, b->name, false, \"%s %s %s.\\n\",  protocol==ICEP_VOICE?\"Calling\":\"Requesting session with\", convolink, hanguplink);\n\t\t\t}\n\t\t\telse\n\t\t\t\tXMPP_ConversationPrintf(b->accountdomain, b->name, false, \"That session has expired.\\n\");\n\t\t}\n\t\telse if (c2c->creator)\n\t\t{\n\t\t\tchar convolink[512];\n\t\t\t//resend initiate if they've not acked it... I dunno...\n\t\t\tJCL_JingleSend(jcl, c2c, \"session-initiate\");\n\t\t\tJCL_GenLink(jcl, convolink, sizeof(convolink), NULL, target, NULL, NULL, \"%s\", target);\n\t\t\tXMPP_ConversationPrintf(b->accountdomain, b->name, false, \"Restarting session with %s.\\n\", convolink);\n\t\t}\n\t\telse if (c2c->accepted)\n\t\t\tXMPP_ConversationPrintf(b->accountdomain, b->name, false, \"That session was already accepted.\\n\");\n\t\telse\n\t\t{\n\t\t\tchar convolink[512];\n\t\t\tJCL_JingleSend(jcl, c2c, \"session-accept\");\n\t\t\tJCL_GenLink(jcl, convolink, sizeof(convolink), NULL, target, NULL, NULL, \"%s\", target);\n\t\t\tXMPP_ConversationPrintf(b->accountdomain, b->name, false, \"Accepting session from %s.\\n\", convolink);\n\t\t}\n\t}\n\telse\n\t{\n\t\tif (c2c)\n\t\t{\n\t\t\tchar convolink[512];\n\t\t\tJCL_JingleSend(jcl, c2c, \"session-terminate\");\n\t\t\tJCL_GenLink(jcl, convolink, sizeof(convolink), NULL, target, NULL, NULL, \"%s\", target);\n\t\t\tXMPP_ConversationPrintf(b->accountdomain, b->name, false, \"Terminating session with %s.\\n\", convolink);\n\t\t}\n\t\telse\n\t\t\tXMPP_ConversationPrintf(b->accountdomain, b->name, false, \"That session has already expired.\\n\");\n\t}\n}\n\nstatic void JCL_JingleParsePeerPorts(jclient_t *jcl, struct c2c_s *c2c, xmltree_t *inj, const char *from, const char *sid)\n{\n\txmltree_t *incontent;\n\txmltree_t *intransport;\n\txmltree_t *incandidate;\n\tstruct icecandinfo_s rem;\n\tstruct icestate_s *ice;\n\tint i, contid;\n\tconst char *cname;\n\n\tif (!*c2c->sid)\n\t\treturn;\n\n\tif (strcmp(c2c->with, from) || strcmp(c2c->sid, sid))\n\t{\n\t\tCon_Printf(\"%s is trying to mess with our connections...\\n\", from);\n\t\treturn;\n\t}\n\n\t//a message can contain multiple contents\n\tfor (contid = 0; ; contid++)\n\t{\n\t\tincontent = XML_ChildOfTree(inj, \"content\", contid);\n\t\tif (!incontent)\n\t\t\tbreak;\n\t\tcname = XML_GetParameter(incontent, \"name\", \"\");\n\n\t\t//find which content this node refers to.\n\t\tice = NULL;\n\t\tfor (i = 0; i < c2c->contents; i++)\n\t\t\tif (!strcmp(c2c->content[i].name, cname))\n\t\t\t\tice = c2c->content[i].ice;\n\t\tif (!ice)\n\t\t{\n\t\t\t//err... this content doesn't exist?\n\t\t\tcontinue;\n\t\t}\n\n\t\tintransport = XML_ChildOfTree(incontent, \"transport\", 0);\n\t\tif (!intransport)\n\t\t\tcontinue;\t//err, I guess it wasn't a transport update then (or related).\n\n\t\tpiceapi->Set(ice, \"rufrag\", XML_GetParameter(intransport, \"ufrag\", \"\"));\n\t\tpiceapi->Set(ice, \"rpwd\", XML_GetParameter(intransport, \"pwd\", \"\"));\n\n\t\tfor (i = 0; (incandidate = XML_ChildOfTree(intransport, \"candidate\", i)); i++)\n\t\t{\n\t\t\tconst char *s;\n\t\t\tmemset(&rem, 0, sizeof(rem));\n\t\t\tQ_strlcpy(rem.addr, XML_GetParameter(incandidate, \"ip\", \"\"), sizeof(rem.addr));\n\t\t\tQ_strlcpy(rem.candidateid, XML_GetParameter(incandidate, \"id\", \"\"), sizeof(rem.candidateid));\n\n\t\t\ts = XML_GetParameter(incandidate, \"type\", \"\");\n\t\t\tif (s && !strcmp(s, \"srflx\"))\n\t\t\t\trem.type = ICE_SRFLX;\n\t\t\telse if (s && !strcmp(s, \"prflx\"))\n\t\t\t\trem.type = ICE_PRFLX;\n\t\t\telse if (s && !strcmp(s, \"relay\"))\n\t\t\t\trem.type = ICE_RELAY;\n\t\t\telse\n\t\t\t\trem.type = ICE_HOST;\n\t\t\trem.port = atoi(XML_GetParameter(incandidate, \"port\", \"0\"));\n\t\t\trem.priority = atoi(XML_GetParameter(incandidate, \"priority\", \"0\"));\n\t\t\trem.network = atoi(XML_GetParameter(incandidate, \"network\", \"0\"));\n\t\t\trem.generation = atoi(XML_GetParameter(incandidate, \"generation\", \"0\"));\n\t\t\trem.foundation = atoi(XML_GetParameter(incandidate, \"foundation\", \"0\"));\n\t\t\trem.component = atoi(XML_GetParameter(incandidate, \"component\", \"0\"));\n\t\t\ts = XML_GetParameter(incandidate, \"protocol\", \"udp\");\n\t\t\tif (s && !strcmp(s, \"udp\"))\n\t\t\t\trem.transport = 0;\n\t\t\telse\n\t\t\t\trem.transport = 0;\n\t\t\tpiceapi->AddRCandidateInfo(ice, &rem);\n\t\t}\n\t}\n}\n#ifdef VOIP_LEGACY\nstatic void JCL_JingleParsePeerPorts_GoogleSession(jclient_t *jcl, struct c2c_s *c2c, xmltree_t *inj, char *from, char *sid)\n{\n\txmltree_t *incandidate;\n\tstruct icecandinfo_s rem;\n\tint i;\n\tint c;\n\n\tif (strcmp(c2c->with, from) || strcmp(c2c->sid, sid))\n\t{\n\t\tCon_Printf(\"%s is trying to mess with our connections...\\n\", from);\n\t\treturn;\n\t}\n\n\tif (!c2c->sid)\n\t\treturn;\n\n\t//with google's session api, every session uses a single set of ports.\n\tfor (i = 0; (incandidate = XML_ChildOfTree(inj, \"candidate\", i)); i++)\n\t{\n\t\tchar *s;\n\t\tmemset(&rem, 0, sizeof(rem));\n\t\tQ_strlcpy(rem.addr, XML_GetParameter(incandidate, \"address\", \"\"), sizeof(rem.addr));\n//\t\tQ_strlcpy(rem.candidateid, XML_GetParameter(incandidate, \"id\", \"\"), sizeof(rem.candidateid));\n\n\t\ts = XML_GetParameter(incandidate, \"type\", \"\");\n\t\tif (s && !strcmp(s, \"stun\"))\n\t\t\trem.type = ICE_SRFLX;\n\t\telse if (s && !strcmp(s, \"stun\"))\n\t\t\trem.type = ICE_PRFLX;\n\t\telse if (s && !strcmp(s, \"relay\"))\n\t\t\trem.type = ICE_RELAY;\n\t\telse\n\t\t\trem.type = ICE_HOST;\n\t\trem.port = atoi(XML_GetParameter(incandidate, \"port\", \"0\"));\n\t\trem.priority = atoi(XML_GetParameter(incandidate, \"priority\", \"0\"));\n\t\trem.network = atoi(XML_GetParameter(incandidate, \"network\", \"0\"));\n\t\trem.generation = atoi(XML_GetParameter(incandidate, \"generation\", \"0\"));\n\t\trem.foundation = atoi(XML_GetParameter(incandidate, \"foundation\", \"0\"));\n\t\ts = XML_GetParameter(incandidate, \"name\", \"rtp\");\n\t\tif (!strcmp(s, \"rtp\"))\n\t\t\trem.component = 1;\n\t\telse if (!strcmp(s, \"rtcp\"))\n\t\t\trem.component = 2;\n\t\telse\n\t\t\trem.component = 0;\n\t\ts = XML_GetParameter(incandidate, \"protocol\", \"udp\");\n\t\tif (s && !strcmp(s, \"udp\"))\n\t\t\trem.transport = 0;\n\t\telse\n\t\t\trem.transport = 0;\n\n\t\tfor (c = 0; c < c2c->contents; c++)\n\t\t\tif (c2c->content[c].ice)\n\t\t\t\tpiceapi->ICE_AddRCandidateInfo(c2c->content[c].ice, &rem);\n\t}\n}\nstatic qboolean JCL_JingleHandleInitiate_GoogleSession(jclient_t *jcl, xmltree_t *inj, char *from)\n{\n\txmltree_t *indescription = XML_ChildOfTree(inj, \"description\", 0);\n\tchar *descriptionxmlns = indescription?indescription->xmlns:\"\";\n\tchar *sid = XML_GetParameter(inj, \"id\", \"\");\n\n\tqboolean accepted = false;\n\tchar *response = \"terminate\";\n\tchar *offer = \"pwn you\";\n\tchar *autocvar = \"xmpp_autoaccepthax\";\n\tchar *initiator;\n\n\tstruct c2c_s *c2c = NULL;\n\tint mt = ICEP_INVALID;\n\tbuddy_t *b;\n\tbresource_t *br;\n\n\t//FIXME: add support for session forwarding so that we might forward the connection to the real server. for now we just reject it.\n\tinitiator = XML_GetParameter(inj, \"initiator\", \"\");\n\tif (strcmp(initiator, from))\n\t\treturn false;\n\n\tif (indescription && !strcmp(descriptionxmlns, \"http://www.google.com/session/phone\"))\n\t{\n\t\tmt = ICEP_VOICE;\n\t\toffer = \"is trying to call you\";\n\t\tautocvar = \"xmpp_autoacceptvoice\";\n\t}\n\tif (mt == ICEP_INVALID)\n\t\treturn false;\n\n\tif (!JCL_FindBuddy(jcl, from, &b, &br, true))\n\t\treturn false;\n\n\t//FIXME: if both people try to establish a connection to the other simultaneously, the higher session id is meant to be canceled, and the lower accepted automagically.\n\n\tc2c = JCL_JingleAddContentToSession(jcl, NULL, from, br, false,\n\t\tsid, \"the mystical magical content name\",\n\t\tICEM_ICE,\n\t\tmt\n\t\t);\n\tif (c2c)\n\t{\n\t\tint c = c2c->contents-1;\n\t\tc2c->peercaps = CAP_GOOGLE_VOICE;\n\n\t\tif (mt == ICEP_VOICE)\n\t\t{\n\t\t\tqboolean okay = false;\n\t\t\tint i = 0;\n\t\t\txmltree_t *payload;\n\t\t\t//chuck it at the engine and see what sticks. at least one must...\n\t\t\twhile((payload = XML_ChildOfTree(indescription, \"payload-type\", i++)))\n\t\t\t{\n\t\t\t\tchar *name = XML_GetParameter(payload, \"name\", \"\");\n\t\t\t\tchar *clock = XML_GetParameter(payload, \"clockrate\", \"\");\n\t\t\t\tchar *id = XML_GetParameter(payload, \"id\", \"\");\n\t\t\t\tchar parm[64];\n\t\t\t\tchar val[64];\n\t\t\t\t//note: the engine will ignore codecs it does not support, returning false.\n\t\t\t\tif (!strcasecmp(name, \"speex\"))\n\t\t\t\t{\n\t\t\t\t\tQ_snprintf(parm, sizeof(parm), \"codec%i\", atoi(id));\n\t\t\t\t\tQ_snprintf(val, sizeof(val), \"speex@%i\", atoi(clock));\n\t\t\t\t\tokay |= piceapi->ICE_Set(c2c->content[c].ice, parm, val);\n\t\t\t\t}\n\t\t\t\telse if (!strcasecmp(name, \"pcma\") || !strcasecmp(name, \"pcmu\"))\n\t\t\t\t{\n\t\t\t\t\tQ_snprintf(parm, sizeof(parm), \"codec%i\", atoi(id));\n\t\t\t\t\tQ_snprintf(val, sizeof(val), \"%s@%i\", name, atoi(clock));\n\t\t\t\t\tokay |= piceapi->ICE_Set(c2c->content[c].ice, parm, val);\n\t\t\t\t}\n\t\t\t\telse if (!strcasecmp(name, \"opus\"))\n\t\t\t\t{\n\t\t\t\t\tQ_snprintf(parm, sizeof(parm), \"codec%i\", atoi(id));\n\t\t\t\t\tokay |= piceapi->ICE_Set(c2c->content[c].ice, parm, \"opus@48000\");\n\t\t\t\t}\n\t\t\t}\n\t\t\t//don't do it if we couldn't successfully set any codecs, because the engine doesn't support the ones that were listed, or something.\n\t\t\t//we really ought to give a reason, but we're rude.\n\t\t\tif (!okay)\n\t\t\t{\n\t\t\t\tchar convolink[512];\n\t\t\t\tJCL_JingleSend(jcl, c2c, \"terminate\");\n\t\t\t\tJCL_GenLink(jcl, convolink, sizeof(convolink), NULL, from, NULL, NULL, \"%s\", b->name);\n\t\t\t\tXMPP_ConversationPrintf(b->accountdomain, b->name, \"%s does not support any compatible audio codecs, and is unable to call you.\\n\", convolink);\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\tif (mt != ICEP_INVALID)\n\t\t{\n\t\t\tchar convolink[512];\n\t\t\tJCL_GenLink(jcl, convolink, sizeof(convolink), NULL, from, NULL, NULL, \"%s\", b->name);\n\t\t\tif (!pCvar_GetFloat(autocvar))\n\t\t\t{\n\t\t\t\tchar authlink[512];\n\t\t\t\tchar denylink[512];\n\t\t\t\tJCL_GenLink(jcl, authlink, sizeof(authlink), \"jauth\", from, NULL, sid, \"%s\", \"Accept\");\n\t\t\t\tJCL_GenLink(jcl, denylink, sizeof(denylink), \"jdeny\", from, NULL, sid, \"%s\", \"Reject\");\n\n\t\t\t\t//show a prompt for it, send the reply when the user decides.\n\t\t\t\tXMPP_ConversationPrintf(b->accountdomain, b->name, true,\n\t\t\t\t\t\t\"%s %s. %s %s\\n\", convolink, offer, authlink, denylink);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tXMPP_ConversationPrintf(b->accountdomain, b->name, \"Auto-accepting session from %s\\n\", convolink);\n\t\t\t\tresponse = \"accept\";\n\t\t\t}\n\t\t}\n\t}\n\tJCL_JingleSend(jcl, c2c, response);\n\treturn true;\n}\n#endif\nstatic struct c2c_s *JCL_JingleHandleInitiate(jclient_t *jcl, xmltree_t *inj, const char *from)\n{\n\tconst char *sid = XML_GetParameter(inj, \"sid\", \"\");\n\n\tqboolean okay;\n\tconst char *initiator;\n\n\tstruct c2c_s *c2c = NULL;\n\tint mt = ICEP_INVALID;\n\tint i, c;\n\tbuddy_t *b;\n\tbresource_t *br;\n\n\t//FIXME: add support for session forwarding so that we might forward the connection to the real server. for now we just reject it.\n\tinitiator = XML_GetParameter(inj, \"initiator\", \"\");\n\tif (strcmp(initiator, from))\n\t\treturn NULL;\n\n\t//reject it if we don't know them.\n\tif (!JCL_FindBuddy(jcl, from, &b, &br, false))\n\t\treturn NULL;\n\n\tfor (i = 0; ; i++)\n\t{\n\t\txmltree_t *incontent = XML_ChildOfTree(inj, \"content\", i);\n\t\tconst char *cname = XML_GetParameter(incontent, \"name\", \"\");\n\t\txmltree_t *intransport = XML_ChildOfTree(incontent, \"transport\", 0);\n\t\txmltree_t *indescription = XML_ChildOfTree(incontent, \"description\", 0);\n\t\tconst char *transportxmlns = intransport?intransport->xmlns:\"\";\n\t\tconst char *descriptionxmlns = indescription?indescription->xmlns:\"\";\n\t\tconst char *descriptionmedia = XML_GetParameter(indescription, \"media\", \"\");\n\t\tif (!incontent)\n\t\t\tbreak;\n\n\t\tmt = ICEP_INVALID;\n\n\t\tif (incontent && !strcmp(descriptionmedia, MEDIATYPE_QUAKE) && !strcmp(descriptionxmlns, QUAKEMEDIAXMLNS))\n\t\t{\n\t\t\tconst char *host = XML_GetParameter(indescription, \"host\", \"you\");\n\t\t\tif (!strcmp(host, \"you\"))\n\t\t\t\tmt = ICEP_QWSERVER;\n\t\t\telse if (!strcmp(host, \"me\"))\n\t\t\t\tmt = ICEP_QWCLIENT;\n\t\t}\n\t\tif (incontent && !strcmp(descriptionmedia, MEDIATYPE_AUDIO) && !strcmp(descriptionxmlns, \"urn:xmpp:jingle:apps:rtp:1\"))\n\t\t\tmt = ICEP_VOICE;\n\t\tif (incontent && !strcmp(descriptionmedia, MEDIATYPE_VIDEO) && !strcmp(descriptionxmlns, \"urn:xmpp:jingle:apps:rtp:1\"))\n\t\t\tmt = ICEP_VIDEO;\n\n\t\tif (mt == ICEP_INVALID)\n\t\t\tcontinue;\n\n\n\t\tc2c = JCL_JingleAddContentToSession(jcl, NULL, from, br, false, sid, cname,\n\t\t\tstrcmp(transportxmlns, \"urn:xmpp:jingle:transports:raw-udp:1\")?ICEM_ICE:ICEM_RAW,\n\t\t\tmt\n\t\t\t);\n\t\tif (!c2c)\n\t\t\tcontinue;\n\t\tc = c2c->contents-1;\n\n\t\tokay = false;\n\t\tif (mt == ICEP_VOICE)\n\t\t{\n\t\t\tint i = 0;\n\t\t\txmltree_t *payload;\n\t\t\t//chuck it at the engine and see what sticks. at least one must...\n\t\t\twhile((payload = XML_ChildOfTree(indescription, \"payload-type\", i++)))\n\t\t\t{\n\t\t\t\tconst char *name = XML_GetParameter(payload, \"name\", \"\");\n\t\t\t\tconst char *clock = XML_GetParameter(payload, \"clockrate\", \"\");\n\t\t\t\tconst char *id = XML_GetParameter(payload, \"id\", \"\");\n\t\t\t\tchar parm[64];\n\t\t\t\tchar val[64];\n\t\t\t\t//note: the engine will ignore codecs it does not support, returning false.\n\t\t\t\tif (!strcasecmp(name, \"speex\") || !strcasecmp(name, \"pcma\") || !strcasecmp(name, \"pcmu\"))\n\t\t\t\t{\n\t\t\t\t\tQ_snprintf(parm, sizeof(parm), \"codec%i\", atoi(id));\n\t\t\t\t\tQ_snprintf(val, sizeof(val), \"%s@%i\", name, atoi(clock));\n\t\t\t\t\tokay |= piceapi->Set(c2c->content[c].ice, parm, val);\n\t\t\t\t}\n\t\t\t\telse if (!strcasecmp(name, \"opus\"))\n\t\t\t\t{\n\t\t\t\t\tQ_snprintf(parm, sizeof(parm), \"codec%i\", atoi(id));\n\t\t\t\t\tokay |= piceapi->Set(c2c->content[c].ice, parm, \"opus@48000\");\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse\n\t\t\tokay = true;\n\t\t//don't do it if we couldn't successfully set any codecs, because the engine doesn't support the ones that were listed, or something.\n\t\t//we really ought to give a reason, but we're rude.\n\t\tif (!okay)\n\t\t{\n\t\t\tchar convolink[512];\n\t\t\tJCL_GenLink(jcl, convolink, sizeof(convolink), NULL, from, NULL, NULL, \"%s\", b->name);\n\t\t\tXMPP_ConversationPrintf(b->accountdomain, b->name, false, \"%s does not support any compatible codecs, and is unable to call you.\\n\", convolink);\n\n\t\t\tif (c2c->content[c].ice)\n\t\t\t\tpiceapi->Close(c2c->content[c].ice, true);\n\t\t\tc2c->content[c].ice = NULL;\n\t\t\tc2c->contents--;\n\t\t}\n\t}\n\tif (!c2c)\n\t\treturn NULL;\n\n\tif (!c2c->contents)\n\t{\n\t\tif (jcl->c2c == c2c)\n\t\t{\n\t\t\tjcl->c2c = c2c->next;\n\t\t\tfree(c2c);\n\t\t}\n\t\telse\n\t\t\tCon_Printf(\"^1error in \"__FILE__\" %i\\n\", __LINE__);\n\t\treturn NULL;\n\t}\n\n\t//if they speak this, we never want to speak gingle at them!\n\tc2c->peercaps &= ~(CAP_GOOGLE_VOICE);\n\n\tJCL_JingleParsePeerPorts(jcl, c2c, inj, from, sid);\n\treturn c2c;\n}\n\nstatic qboolean JCL_JingleHandleSessionTerminate(jclient_t *jcl, xmltree_t *tree, struct c2c_s *c2c, struct c2c_s **link, buddy_t *b)\n{\n\txmltree_t *reason = XML_ChildOfTree(tree, \"reason\", 0);\n\tint c;\n\tif (!c2c)\n\t{\n\t\tXMPP_ConversationPrintf(b->accountdomain, b->name, false, \"Received session-terminate without an active session\\n\");\n\t\treturn false;\n\t}\n\n\tif (reason && reason->child)\n\t\tXMPP_ConversationPrintf(b->accountdomain, b->name, false, \"Session ended: %s\\n\", reason->child->name);\n\telse\n\t\tXMPP_ConversationPrintf(b->accountdomain, b->name, false, \"Session ended\\n\");\n\n\t//unlink it\n\tfor (link = &jcl->c2c; *link; link = &(*link)->next)\n\t{\n\t\tif (*link == c2c)\n\t\t{\n\t\t\t*link = c2c->next;\n\t\t\tbreak;\n\t\t}\n\t}\n\n//\tXML_ConPrintTree(tree, 0);\n\n\tfor (c = 0; c < c2c->contents; c++)\n\t\tif (c2c->content[c].ice)\n\t\t\tpiceapi->Close(c2c->content[c].ice, true);\n\tfree(c2c);\n\treturn true;\n}\nstatic qboolean JCL_JingleHandleSessionAccept(jclient_t *jcl, xmltree_t *tree, const char *from, struct c2c_s *c2c, buddy_t *b)\n{\n\t//peer accepted our session\n\t//make sure it actually was ours, and not theirs. sneaky sneaky.\n\t//will generally contain some port info.\n\tif (!c2c)\n\t{\n\t\tCon_DPrintf(\"Unknown session acceptance\\n\");\n\t\treturn false;\n\t}\n\telse if (!c2c->creator)\n\t{\n\t\tCon_DPrintf(\"Peer tried to accept a session that *they* created!\\n\");\n\t\treturn false;\n\t}\n\telse if (c2c->accepted)\n\t{\n\t\t//pidgin is buggy and can dupe-accept sessions multiple times.\n\t\tCon_DPrintf(\"Duplicate session-accept from peer.\\n\");\n\n\t\t//XML_ConPrintTree(tree, 0);\n\t\treturn false;\n\t}\n\telse\n\t{\n\t\tconst char *responder = XML_GetParameter(tree, \"responder\", from);\n\t\tint c;\n\t\tif (strcmp(responder, from))\n\t\t{\n\t\t\treturn false;\n\t\t}\n\t\tXMPP_ConversationPrintf(b->accountdomain, b->name, false, \"Session Accepted!\\n\");\n//\t\t\tXML_ConPrintTree(tree, 0);\n\n\t\tJCL_JingleParsePeerPorts(jcl, c2c, tree, from, XML_GetParameter(tree, \"sid\", \"\"));\n\t\tc2c->accepted = true;\n\n\t\t//if we didn't error out, the ICE stuff is meant to start sending handshakes/media as soon as the connection is accepted\n\t\tfor (c = 0; c < c2c->contents; c++)\n\t\t\tif (c2c->content[c].ice)\n\t\t\t\tpiceapi->Set(c2c->content[c].ice, \"state\", STRINGIFY(ICE_CONNECTING));\n\t}\n\treturn true;\n}\n#ifdef VOIP_LEGACY\nqboolean JCL_HandleGoogleSession(jclient_t *jcl, xmltree_t *tree, char *from, char *iqid)\n{\n\tchar *sid = XML_GetParameter(tree, \"id\", \"\");\n\tchar *type = XML_GetParameter(tree, \"type\", \"\");\n\n\tstruct c2c_s *c2c = NULL, **link;\n\tbuddy_t *b;\n\n\t//validate+find sender\n\tfor (link = &jcl->c2c; *link; link = &(*link)->next)\n\t{\n\t\tif (!strcmp((*link)->sid, sid))\n\t\t{\n\t\t\tc2c = *link;\n\t\t\tif (!c2c->accepted)\n\t\t\t\tbreak;\n\t\t}\n\t}\n\tif (!JCL_FindBuddy(jcl, from, &b, NULL, true))\n\t\treturn false;\n\tif (c2c && strcmp(c2c->with, from))\n\t{\n\t\tCon_Printf(\"%s is trying to mess with our connections...\\n\", from);\n\t\treturn false;\n\t}\n\n\tCon_Printf(\"google session with %s:\\n\", from);\n\tXML_ConPrintTree(tree, \"\", 0);\n\n\tif (!strcmp(type, \"accept\"))\n\t{\n\t\tif (!JCL_JingleHandleSessionAccept(jcl, tree, from, c2c, b))\n\t\t\treturn false;\n\t}\n\telse if (!strcmp(type, \"initiate\"))\n\t{\n\t\tif (!JCL_JingleHandleInitiate_GoogleSession(jcl, tree, from))\n\t\t\treturn false;\n\t}\n\telse if (!strcmp(type, \"terminate\"))\n\t{\n\t\tJCL_JingleHandleSessionTerminate(jcl, tree, c2c, link, b);\n\t}\n\telse if (!strcmp(type, \"candidates\"))\n\t{\n\t\tif (!c2c)\n\t\t\treturn false;\n\t\tJCL_JingleParsePeerPorts_GoogleSession(jcl, c2c, tree, from, sid);\n\t}\n\telse\n\t{\n\t\tCon_Printf(\"Unknown google session action: %s\\n\", type);\n//\t\tXML_ConPrintTree(tree, 0);\n\t\treturn false;\n\t}\n\n\tJCL_AddClientMessagef(jcl,\n\t\t\"<iq type='result' to='%s' id='%s' />\", from, iqid);\n\treturn true;\n}\n#endif\nqboolean JCL_ParseJingle(jclient_t *jcl, xmltree_t *tree, const char *from, const char *id)\n{\n\tconst char *action = XML_GetParameter(tree, \"action\", \"\");\n\tconst char *sid = XML_GetParameter(tree, \"sid\", \"\");\n\n\tstruct c2c_s *c2c = NULL, **link;\n\tbuddy_t *b;\n\n\tfor (link = &jcl->c2c; *link; link = &(*link)->next)\n\t{\n\t\tif (!strcmp((*link)->sid, sid))\n\t\t{\n\t\t\tc2c = *link;\n\t\t\tif (!c2c->accepted)\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\tif (!JCL_FindBuddy(jcl, from, &b, NULL, true))\n\t\treturn false;\n\n\t//validate sender\n\tif (c2c && strcmp(c2c->with, from))\n\t{\n\t\tCon_Printf(\"%s is trying to mess with our connections...\\n\", from);\n\t\treturn false;\n\t}\n\n\t//FIXME: transport-info, transport-replace\n\tif (!strcmp(action, \"session-terminate\"))\n\t{\n\t\tif (c2c)\n\t\t{\n\t\t\tJCL_JingleError(jcl, tree, from, id, JE_ACKNOWLEDGE);\n\t\t\tJCL_JingleHandleSessionTerminate(jcl, tree, c2c, link, b);\n\t\t}\n\t\telse\n\t\t\tJCL_JingleError(jcl, tree, from, id, JE_UNKNOWNSESSION);\n\t}\n\telse if (!strcmp(action, \"content-accept\"))\n\t{\n\t\t//response from content-add\n\t\tif (c2c)\n\t\t\tJCL_JingleError(jcl, tree, from, id, JE_UNSUPPORTED);\n\t\telse\n\t\t\tJCL_JingleError(jcl, tree, from, id, JE_UNKNOWNSESSION);\n\t}\n\telse if (!strcmp(action, \"content-add\"))\n\t{\n\t\t//FIXME: must send content-reject\n\t\tif (c2c)\n\t\t\tJCL_JingleError(jcl, tree, from, id, JE_UNSUPPORTED);\n\t\telse\n\t\t\tJCL_JingleError(jcl, tree, from, id, JE_UNKNOWNSESSION);\n\t}\n\telse if (!strcmp(action, \"content-modify\"))\n\t{\n\t\t//send an error to reject it\n\t\tJCL_JingleError(jcl, tree, from, id, JE_UNSUPPORTED);\n\t}\n\telse if (!strcmp(action, \"content-reject\"))\n\t{\n\t\t//response from content-add. an error until we actually generate content-adds...\n\t\tif (c2c)\n\t\t\tJCL_JingleError(jcl, tree, from, id, JE_UNSUPPORTED);\n\t\telse\n\t\t\tJCL_JingleError(jcl, tree, from, id, JE_UNKNOWNSESSION);\n\t}\n\telse if (!strcmp(action, \"content-remove\"))\n\t{\n\t\tif (c2c)\n\t\t\tJCL_JingleError(jcl, tree, from, id, JE_UNSUPPORTED);\n\t\telse\n\t\t\tJCL_JingleError(jcl, tree, from, id, JE_UNKNOWNSESSION);\n\t}\n\telse if (!strcmp(action, \"description-info\"))\n\t{\n\t\t//The description-info action is used to send informational hints about parameters related to the application type, such as the suggested height and width of a video display area or suggested configuration for an audio stream.\n\t\t//just ack and ignore it\n\t\tif (c2c)\n\t\t\tJCL_JingleError(jcl, tree, from, id, JE_UNSUPPORTED);\n\t\telse\n\t\t\tJCL_JingleError(jcl, tree, from, id, JE_UNKNOWNSESSION);\n\t}\n\telse if (!strcmp(action, \"security-info\"))\n\t{\n\t\t//The security-info action is used to send information related to establishment or maintenance of security preconditions.\n\t\t//no security mechanisms supported...\n\t\tif (c2c)\n\t\t\tJCL_JingleError(jcl, tree, from, id, JE_UNSUPPORTED);\n\t\telse\n\t\t\tJCL_JingleError(jcl, tree, from, id, JE_UNKNOWNSESSION);\n\t}\n\telse if (!strcmp(action, \"session-info\"))\n\t{\n\t\tif (tree->child)\n\t\t\tJCL_JingleError(jcl, tree, from, id, JE_UNSUPPORTEDINFO);\n\t\telse\n\t\t\tJCL_JingleError(jcl, tree, from, id, JE_ACKNOWLEDGE);\t//serves as a ping.\n\t}\n\n\telse if (!strcmp(action, \"transport-info\"))\n\t{\t//peer wants to add ports.\n\t\tif (c2c)\n\t\t{\n\t\t\tJCL_JingleError(jcl, tree, from, id, JE_ACKNOWLEDGE);\n\t\t\tJCL_JingleParsePeerPorts(jcl, c2c, tree, from, sid);\n\t\t}\n\t\telse\n\t\t\tJCL_JingleError(jcl, tree, from, id, JE_UNKNOWNSESSION);\n\t}\n//FIXME: we need to add support for this to downgrade to raw if someone tries calling through a SIP gateway\n\telse if (!strcmp(action, \"transport-replace\"))\n\t{\n\t\tif (c2c)\n\t\t{\n\t\t\tif (1)\n\t\t\t{\n\t\t\t\tJCL_JingleError(jcl, tree, from, id, JE_ACKNOWLEDGE);\n\t\t\t\tJCL_JingleSend(jcl, c2c, \"transport-reject\");\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tJCL_JingleError(jcl, tree, from, id, JE_ACKNOWLEDGE);\n\t\t\t\tJCL_JingleParsePeerPorts(jcl, c2c, tree, from, sid);\n\t\t\t\tJCL_JingleSend(jcl, c2c, \"transport-accept\");\n\t\t\t}\n\t\t}\n\t\telse\n\t\t\tJCL_JingleError(jcl, tree, from, id, JE_UNKNOWNSESSION);\n\t}\n\telse if (!strcmp(action, \"transport-reject\"))\n\t{\n\t\tJCL_JingleError(jcl, tree, from, id, JE_ACKNOWLEDGE);\n\t\tJCL_JingleSend(jcl, c2c, \"session-terminate\");\n\t}\n\telse if (!strcmp(action, \"session-accept\"))\n\t{\n\t\tif (c2c)\n\t\t{\n\t\t\t//response from a message we sent.\n\t\t\tif (JCL_JingleHandleSessionAccept(jcl, tree, from, c2c, b))\n\t\t\t\tJCL_JingleError(jcl, tree, from, id, JE_ACKNOWLEDGE);\n\t\t\telse\n\t\t\t\tJCL_JingleError(jcl, tree, from, id, JE_OUTOFORDER);\n\t\t}\n\t\telse\n\t\t\tJCL_JingleError(jcl, tree, from, id, JE_UNKNOWNSESSION);\n\t}\n\telse if (!strcmp(action, \"session-initiate\"))\n\t{\n//\t\tCon_Printf(\"Peer initiating connection!\\n\");\n//\t\tXML_ConPrintTree(tree, 0);\n\n\t\t\n\n\t\tc2c = JCL_JingleHandleInitiate(jcl, tree, from);\n\n\t\tif (c2c)\n\t\t{\n\t\t\tqboolean voice = false, video = false, server = false, client = false;\n\t\t\tchar *offer;\n\t\t\tchar convolink[512];\n\t\t\tint c;\n\t\t\tqboolean doprompt = false;\n\t\t\tJCL_JingleError(jcl, tree, from, id, JE_ACKNOWLEDGE);\n\n\t\t\tfor (c = 0; c < c2c->contents; c++)\n\t\t\t{\n\t\t\t\tswitch(c2c->content[c].mediatype)\n\t\t\t\t{\n\t\t\t\tcase ICEP_INVALID:\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\tcase ICEP_VOICE:\tvoice = true; \tdoprompt |= !cvarfuncs->GetFloat(\"xmpp_autoacceptvoice\");\tbreak;\n\t\t\t\tcase ICEP_VIDEO:\tvideo = true; \tdoprompt |= !cvarfuncs->GetFloat(\"xmpp_autoacceptvoice\");\tbreak;\n\t\t\t\tcase ICEP_QWSERVER: server = true; \tdoprompt |= !cvarfuncs->GetFloat(\"xmpp_autoacceptjoins\");\tbreak;\n\t\t\t\tcase ICEP_QWCLIENT: client = true; \tdoprompt |= !cvarfuncs->GetFloat(\"xmpp_autoacceptinvites\");\tbreak;\n\t\t\t\tdefault:\t\t\t\t\t\t\tdoprompt |= true;\t\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (video)\n\t\t\t{\n\t\t\t\tif (server)\n\t\t\t\t\toffer = \"wants to join your game (with ^1video^7)\";\n\t\t\t\telse if (client)\n\t\t\t\t\toffer = \"wants to invite you to thier game (with ^1video^7)\";\n\t\t\t\telse\n\t\t\t\t\toffer = \"is trying to ^1video^7 call you\";\n\t\t\t}\n\t\t\telse if (voice)\n\t\t\t{\n\t\t\t\tif (server)\n\t\t\t\t\toffer = \"wants to join your game (with voice)\";\n\t\t\t\telse if (client)\n\t\t\t\t\toffer = \"wants to invite you to thier game (with voice)\";\n\t\t\t\telse\n\t\t\t\t\toffer = \"is trying to call you\";\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (server)\n\t\t\t\t\toffer = \"wants to join your game\";\n\t\t\t\telse if (client)\n\t\t\t\t\toffer = \"wants to invite you to thier game\";\n\t\t\t\telse\n\t\t\t\t\toffer = \"is trying to waste your time\";\n\t\t\t}\n\n\t\t\tJCL_GenLink(jcl, convolink, sizeof(convolink), NULL, from, NULL, NULL, \"%s\", b->name);\n\t\t\tif (doprompt)\n\t\t\t{\n\t\t\t\tchar authlink[512];\n\t\t\t\tchar denylink[512];\n\t\t\t\tJCL_GenLink(jcl, authlink, sizeof(authlink), \"jauth\", from, NULL, sid, \"%s\", \"Accept\");\n\t\t\t\tJCL_GenLink(jcl, denylink, sizeof(denylink), \"jdeny\", from, NULL, sid, \"%s\", \"Reject\");\n\n\t\t\t\t//show a prompt for it, send the reply when the user decides.\n\t\t\t\tXMPP_ConversationPrintf(b->accountdomain, b->name, true,\n\t\t\t\t\t\t\"%s %s. %s %s\\n\", convolink, offer, authlink, denylink);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tXMPP_ConversationPrintf(b->accountdomain, b->name, false, \"Auto-accepting session from %s\\n\", convolink);\n\t\t\t\tJCL_Join(jcl, from, sid, true, ICEP_INVALID);\n\t\t\t}\n\t\t}\n\t\telse\n\t\t\tJCL_JingleError(jcl, tree, from, id, JE_UNSUPPORTED);\n\t}\n\telse\n\t{\n\t\tJCL_JingleError(jcl, tree, from, id, JE_UNSUPPORTED);\n\t}\n\treturn true;\n}\n#endif"
  },
  {
    "path": "plugins/jabber/sift.c",
    "content": "#include \"xmpp.h\"\n\n#ifdef FILETRANSFERS\n\nvoid XMPP_FT_Frame(jclient_t *jcl)\n{\n\tstatic char *hex = \"0123456789abcdef\";\n\tchar digest[20];\n\tchar domain[sizeof(digest)*2+1];\n\tint j;\n\tchar *req;\n\tstruct ft_s *ft;\n\tfor (ft = jcl->ft; ft; ft = ft->next)\n\t{\n\t\tif (ft->streamstatus == STRM_IDLE)\n\t\t\tcontinue;\n\n\t\tif (ft->stream < 0)\n\t\t{\n\t\t\tif (ft->nexthost > 0)\n\t\t\t{\n\t\t\t\tft->nexthost--;\n\t\t\t\tft->stream = netfuncs->TCPConnect(ft->streamhosts[ft->nexthost].host, ft->streamhosts[ft->nexthost].port);\n\t\t\t\tif (ft->stream == -1)\n\t\t\t\t\tcontinue;\n\n\t\t\t\tft->streamstatus = STRM_AUTH;\n\n\t\t\t\t//'authenticate' with socks5 proxy. tell it that we only support 'authless'.\n\t\t\t\tnetfuncs->Send(ft->stream, \"\\x05\\x01\\x00\", 3);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tJCL_AddClientMessagef(jcl,\n\t\t\t\t\t\"<iq id='%s' to='%s' type='error'>\"\n\t\t\t\t\t\t\"<error type='cancel'>\"\n\t\t\t\t\t\t\t\"<item-not-found xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>\"\n\t\t\t\t\t\t\"</error>\"\n\t\t\t\t\t\"</iq>\", ft->iqid, ft->with);\n\t\t\t\tft->streamstatus = STRM_IDLE;\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tchar data[8192];\n\t\t\tint len;\n\t\t\tif (ft->streamstatus == STRM_ACTIVE)\n\t\t\t{\n\t\t\t\tlen = netfuncs->Recv(ft->stream, data, sizeof(data)-1);\n\t\t\t\tif (len > 0)\n\t\t\t\t\tfilefuncs->Write(ft->file, data, len);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tlen = netfuncs->Recv(ft->stream, data, sizeof(data)-1);\n\t\t\t\tif (len > 0)\n\t\t\t\t{\n\t\t\t\t\tif (ft->streamstatus == STRM_AUTH)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (len == 2 && data[0] == 0x05 && data[1] == 0x00)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t//server has accepted us, woo.\n\t\t\t\t\t\t\t//sid+requester(them)+target(us)\n\t\t\t\t\t\t\treq = va(\"%s%s%s\", ft->sid, ft->with, jcl->fulljid);\n\t\t\t\t\t\t\tCalcHash(&hash_sha1, digest, sizeof(digest), req, strlen(req));\n\t\t\t\t\t\t\t//in hex\n\t\t\t\t\t\t\tfor (req = domain, j=0; j < 20; j++)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t*req++ = hex[(digest[j]>>4) & 0xf];\n\t\t\t\t\t\t\t\t*req++ = hex[(digest[j]>>0) & 0xf];\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t*req = 0;\n\n\t\t\t\t\t\t\t//connect with hostname(3).\n\t\t\t\t\t\t\treq = va(\"\\x05\\x01%c\\x03\"\"%c%s%c%c\", 0, (int)strlen(domain), domain, 0, 0);\n\t\t\t\t\t\t\tnetfuncs->Send(ft->stream, req, strlen(domain)+7);\n\t\t\t\t\t\t\tft->streamstatus = STRM_AUTHED;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tlen = -1;\n\t\t\t\t\t}\n\t\t\t\t\telse if (ft->streamstatus == STRM_AUTHED)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (data[0] == 0x05 && data[1] == 0x00)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (filefuncs->Open(ft->fname, &ft->file, 2) < 0)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tlen = -1;\n\t\t\t\t\t\t\t\tJCL_AddClientMessagef(jcl, \"<iq id='%s' to='%s' type='error'/>\", ft->iqid, ft->with);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tft->streamstatus = STRM_ACTIVE;\n\t\t\t\t\t\t\t\tJCL_AddClientMessagef(jcl,\n\t\t\t\t\t\t\t\t\t\t\"<iq id='%s' to='%s' type='result'>\"\n\t\t\t\t\t\t\t\t\t\t\t\"<query xmlns='http://jabber.org/protocol/bytestreams' sid='%s'>\"\n\t\t\t\t\t\t\t\t\t\t\t\t\"<streamhost-used jid='%s'/>\"\n\t\t\t\t\t\t\t\t\t\t\t\"</query>\"\n\t\t\t\t\t\t\t\t\t\t\"</iq>\", ft->iqid, ft->with, ft->sid, ft->streamhosts[ft->nexthost].jid);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tlen = -1;\t//err, this is someone else...\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (len == -1)\n\t\t\t{\n\t\t\t\tnetfuncs->Close(ft->stream);\n\t\t\t\tft->stream = -1;\n\t\t\t\tif (ft->streamstatus == STRM_ACTIVE)\n\t\t\t\t{\n\t\t\t\t\tint size;\n\t\t\t\t\tif (ft->file != -1)\n\t\t\t\t\t\tfilefuncs->Close(ft->file);\n\t\t\t\t\tft->file = -1;\n\n\t\t\t\t\tsize = filefuncs->Open(ft->fname, &ft->file, 1);\n\t\t\t\t\tif (ft->file != -1)\n\t\t\t\t\t\tfilefuncs->Close(ft->file);\n\t\t\t\t\tif (size == ft->size)\n\t\t\t\t\t{\n\t\t\t\t\t\tCon_Printf(\"File Transfer Completed\\n\");\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tCon_Printf(\"File Transfer Aborted by peer\\n\");\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tCon_Printf(\"File Transfer connection to %s:%i failed\\n\", ft->streamhosts[ft->nexthost].host, ft->streamhosts[ft->nexthost].port);\n\t\t\t}\n\t\t}\n\t}\n}\n\nvoid XMPP_FT_AcceptFile(jclient_t *jcl, int fileid, qboolean accept)\n{\n\tstruct ft_s *ft, **link;\n\tchar *s;\n\txmltree_t *repiq, *repsi, *c;\n\n\tfor (link = &jcl->ft; (ft=*link); link = &(*link)->next)\n\t{\n\t\tif (ft->privateid == fileid)\n\t\t\tbreak;\n\t}\n\tif (!ft)\n\t{\n\t\tCon_Printf(\"File not known\\n\");\n\t\treturn;\n\t}\n\n\tif (!accept)\n\t{\n\t\tCon_Printf(\"Declining file \\\"%s\\\" from \\\"%s\\\" (%i bytes)\\n\", ft->fname, ft->with, ft->size);\n\n\t\tif (!ft->allowed)\n\t\t{\n\t\t\tJCL_AddClientMessagef(jcl,\n\t\t\t\t\t\t\"<iq type='error' to='%s' id='%s'>\"\n\t\t\t\t\t\t\t\"<error type='cancel'>\"\n\t\t\t\t\t\t\t\"<forbidden xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>\"\n\t\t\t\t\t\t\t\t\"<text>Offer Declined</text>\"\n\t\t\t\t\t\t\t\"</error>\"\n\t\t\t\t\t\t\"</iq>\", ft->with, ft->iqid);\n\t\t}\n\t\telse\n\t\t{\n\t\t\t//FIXME: send a proper cancel\n\t\t}\n\n\t\tif (ft->file != -1)\n\t\t\tfilefuncs->Close(ft->file);\n\t\t*link = ft->next;\n\t\tfree(ft);\n\t}\n\telse\n\t{\n\t\tCon_Printf(\"Receiving file \\\"%s\\\" from \\\"%s\\\" (%i bytes)\\n\", ft->fname, ft->with, ft->size);\n\t\tft->allowed = true;\n\n\t\t//generate a response.\n\t\t//FIXME: we ought to delay response until after we prompt.\n\t\trepiq = XML_CreateNode(NULL, \"iq\", \"\", \"\");\n\t\tXML_AddParameter(repiq, \"type\", \"result\");\n\t\tXML_AddParameter(repiq, \"to\", ft->with);\n\t\tXML_AddParameter(repiq, \"id\", ft->iqid);\n\t\trepsi = XML_CreateNode(repiq, \"si\", \"http://jabber.org/protocol/si\", \"\");\n\t\tXML_CreateNode(repsi, \"file\", \"http://jabber.org/protocol/si/profile/file-transfer\", \"\");\t//I don't really get the point of this.\n\t\tc = XML_CreateNode(repsi, \"feature\", \"http://jabber.org/protocol/feature-neg\", \"\");\n\t\tc = XML_CreateNode(c, \"x\", \"jabber:x:data\", \"\");\n\t\tXML_AddParameter(c, \"type\", \"submit\");\n\t\tc = XML_CreateNode(c, \"field\", \"\", \"\");\n\t\tXML_AddParameter(c, \"var\", \"stream-method\");\n\t\tif (ft->method == FT_IBB)\n\t\t\tc = XML_CreateNode(c, \"value\", \"\", \"http://jabber.org/protocol/ibb\");\n\t\telse if (ft->method == FT_BYTESTREAM)\n\t\t\tc = XML_CreateNode(c, \"value\", \"\", \"http://jabber.org/protocol/bytestreams\");\n\n\t\ts = XML_GenerateString(repiq, false);\n\t\tJCL_AddClientMessageString(jcl, s);\n\t\tfree(s);\n\t\tXML_Destroy(repiq);\n\t}\n}\n\nstatic qboolean XMPP_FT_IBBChunked(jclient_t *jcl, xmltree_t *x, struct iq_s *iq)\n{\n\tconst char *from = XML_GetParameter(x, \"from\", jcl->domain);\n\tstruct ft_s *ft = iq->usrptr, **link, *v;\n\tfor (link = &jcl->ft; (v=*link); link = &(*link)->next)\n\t{\n\t\tif (v == ft && !strcmp(ft->with, from))\n\t\t{\n\t\t\t//its still valid\n\t\t\tif (x)\n\t\t\t{\n\t\t\t\tchar *base64;\n\t\t\t\tchar rawbuf[4096];\n\t\t\t\tint sz;\n\t\t\t\tsz = filefuncs->Read(ft->file, rawbuf, ft->blocksize);\n\t\t\t\tBase64_Add(rawbuf, sz);\n\t\t\t\tbase64 = Base64_Finish();\n\n\t\t\t\tif (sz > 0)\n\t\t\t\t{\n\t\t\t\t\tft->sizedone += sz;\n\t\t\t\t\tif (ft->sizedone == ft->size)\n\t\t\t\t\t\tft->eof = true;\n\t\t\t\t}\n\n\t\t\t\tif (sz && strlen(base64))\n\t\t\t\t{\n\t\t\t\t\tx = XML_CreateNode(NULL, \"data\", \"http://jabber.org/protocol/ibb\", base64);\n\t\t\t\t\tXML_AddParameteri(x, \"seq\", ft->seq++);\n\t\t\t\t\tXML_AddParameter(x, \"sid\", ft->sid);\n\t\t\t\t\tJCL_SendIQNode(jcl, XMPP_FT_IBBChunked, \"set\", from, x, true)->usrptr = ft;\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t\t//else eof\n\t\t\t}\n\n\t\t\t//errored or ended\n\n\t\t\tif (x)\n\t\t\t\tCon_Printf(\"Transfer of %s to %s completed\\n\", ft->fname, ft->with);\n\t\t\telse\n\t\t\t\tCon_Printf(\"%s aborted %s\\n\", ft->with, ft->fname);\n\t\t\tx = XML_CreateNode(NULL, \"close\", \"http://jabber.org/protocol/ibb\", \"\");\n\t\t\tXML_AddParameter(x, \"sid\", ft->sid);\n\t\t\tJCL_SendIQNode(jcl, NULL, \"set\", from, x, true)->usrptr = ft;\n\n\t\t\t//errored\n\t\t\tif (ft->file != -1)\n\t\t\t\tfilefuncs->Close(ft->file);\n\t\t\t*link = ft->next;\n\t\t\tfree(ft);\n\t\t\treturn true;\n\t\t}\n\t}\n\treturn true;\t//the ack can come after the bytestream has already finished sending. don't warn about that.\n}\nstatic qboolean XMPP_FT_IBBBegun(jclient_t *jcl, xmltree_t *x, struct iq_s *iq)\n{\n\tstruct ft_s *ft = iq->usrptr, **link, *v;\n\tconst char *from = XML_GetParameter(x, \"from\", jcl->domain);\n\tfor (link = &jcl->ft; (v=*link); link = &(*link)->next)\n\t{\n\t\tif (v == ft && !strcmp(ft->with, from))\n\t\t{\n\t\t\t//its still valid\n\t\t\tif (!x)\n\t\t\t{\n\t\t\t\tCon_Printf(\"%s aborted %s\\n\", ft->with, ft->fname);\n\t\t\t\t//errored\n\t\t\t\tif (ft->file != -1)\n\t\t\t\t\tfilefuncs->Close(ft->file);\n\t\t\t\t*link = ft->next;\n\t\t\t\tfree(ft);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tft->begun = true;\n\t\t\t\tft->method = FT_IBB;\n\t\t\t\tXMPP_FT_IBBChunked(jcl, x, iq);\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\t}\n\treturn false;\n}\nqboolean XMPP_FT_OfferAcked(jclient_t *jcl, xmltree_t *x, struct iq_s *iq)\n{\n\tstruct ft_s *ft = iq->usrptr, **link, *v;\n\tconst char *from = XML_GetParameter(x, \"from\", jcl->domain);\n\tfor (link = &jcl->ft; (v=*link); link = &(*link)->next)\n\t{\n\t\tif (v == ft && !strcmp(ft->with, from))\n\t\t{\n\t\t\t//its still valid\n\t\t\tif (!x)\n\t\t\t{\n\t\t\t\tCon_Printf(\"%s doesn't want %s\\n\", ft->with, ft->fname);\n\t\t\t\t//errored\n\t\t\t\tif (ft->file != -1)\n\t\t\t\t\tfilefuncs->Close(ft->file);\n\t\t\t\t*link = ft->next;\n\t\t\t\tfree(ft);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\txmltree_t *xo;\n\t\t\t\tCon_Printf(\"%s accepted %s\\n\", ft->with, ft->fname);\n\t\t\t\txo = XML_CreateNode(NULL, \"open\", \"http://jabber.org/protocol/ibb\", \"\");\n\t\t\t\tXML_AddParameter(xo, \"sid\", ft->sid);\n\t\t\t\tXML_AddParameteri(xo, \"block-size\", ft->blocksize);\n\t\t\t\t//XML_AddParameter(xo, \"stanza\", \"iq\");\n\n\t\t\t\tJCL_SendIQNode(jcl, XMPP_FT_IBBBegun, \"set\", XML_GetParameter(x, \"from\", jcl->domain), xo, true)->usrptr = ft;\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\t}\n\treturn false;\n}\n\nvoid XMPP_FT_SendFile(jclient_t *jcl, const char *console, const char *to, const char *fname)\n{\n\txmltree_t *xsi, *xfile, *c;\n\tstruct ft_s *ft;\n\n\tCon_SubPrintf(console, \"Offering %s to %s.\\n\", fname, to);\n\n\tft = malloc(sizeof(*ft));\n\tmemset(ft, 0, sizeof(*ft));\n\tft->stream = -1;\n\tft->allowed = true;\n\tft->transmitting = true;\n\tft->blocksize = 4096;\n\tQ_strlcpy(ft->fname, fname, sizeof(ft->fname));\n\tif (Q_snprintfz(ft->sid, sizeof(ft->sid), \"%x%s\", rand(), ft->fname))\n\t\t/*doesn't matter so long as its unique*/;\n\tQ_strlcpy(ft->md5hash, \"\", sizeof(ft->md5hash));\n\tft->size = filefuncs->Open(ft->fname, &ft->file, 1);\n\tft->with = strdup(to);\n\tft->method = FT_IBB;\n\tft->begun = false;\n\n\tft->next = jcl->ft;\n\tjcl->ft = ft;\n\n\t//generate an offer.\n\txsi = XML_CreateNode(NULL, \"si\", \"http://jabber.org/protocol/si\", \"\");\n\t\tXML_AddParameter(xsi, \"profile\", \"http://jabber.org/protocol/si/profile/file-transfer\");\n\t\tXML_AddParameter(xsi, \"id\", ft->sid);\n\t//XML_AddParameter(xsi, \"mime-type\", \"text/plain\");\n\txfile = XML_CreateNode(xsi, \"file\", \"http://jabber.org/protocol/si/profile/file-transfer\", \"\");\t//I don't really get the point of this.\n\t\tXML_AddParameter(xfile, \"name\", ft->fname);\n\t\tXML_AddParameteri(xfile, \"size\", ft->size);\n\tc = XML_CreateNode(xsi, \"feature\", \"http://jabber.org/protocol/feature-neg\", \"\");\n\t\tc = XML_CreateNode(c, \"x\", \"jabber:x:data\", \"\");\n\t\t\tXML_AddParameter(c, \"type\", \"form\");\n\t\t\tc = XML_CreateNode(c, \"field\", \"\", \"\");\n\t\t\t\tXML_AddParameter(c, \"var\", \"stream-method\");\n\t\t\t\tXML_AddParameter(c, \"type\", \"listsingle\");\n\t\t\t\t\tXML_CreateNode(XML_CreateNode(c, \"option\", \"\", \"\"), \"value\", \"\", \"http://jabber.org/protocol/ibb\");\n//\t\t\t\t\tXML_CreateNode(XML_CreateNode(c, \"option\", \"\", \"\"), \"value\", \"\", \"http://jabber.org/protocol/bytestreams\");\n\n\tJCL_SendIQNode(jcl, XMPP_FT_OfferAcked, \"set\", to, xsi, true)->usrptr = ft;\n}\n\nqboolean XMPP_FT_ParseIQSet(jclient_t *jcl, const char *iqfrom, const char *iqid, xmltree_t *tree)\n{\n\txmltree_t *ot;\n\n\t//if file transfers are not enabled in this build, reject all iqs\n\tif (!(jcl->enabledcapabilities & CAP_SIFT))\n\t\treturn false;\n\n\tot = XML_ChildOfTreeNS(tree, \"http://jabber.org/protocol/bytestreams\", \"query\", 0);\n\tif (ot)\n\t{\n\t\txmltree_t *c;\n\t\tstruct ft_s *ft;\n\t\tconst char *sid = XML_GetParameter(ot, \"sid\", \"\");\n\t\tfor (ft = jcl->ft; ft; ft = ft->next)\n\t\t{\n\t\t\tif (!strcmp(ft->sid, sid) && !strcmp(ft->with, iqfrom))\n\t\t\t{\n\t\t\t\tif (ft->allowed && !ft->begun && ft->transmitting == false)\n\t\t\t\t{\n\t\t\t\t\tconst char *jid;\n\t\t\t\t\tconst char *host;\n\t\t\t\t\tint port;\n\t\t\t\t\tconst char *mode = XML_GetParameter(ot, \"mode\", \"tcp\");\n\t\t\t\t\tint i;\n\t\t\t\t\tif (strcmp(mode, \"tcp\"))\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tfor (i = 0; ; i++)\n\t\t\t\t\t{\n\t\t\t\t\t\tc = XML_ChildOfTree(ot, \"streamhost\", i);\n\t\t\t\t\t\tif (!c)\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tjid = XML_GetParameter(c, \"jid\", \"\");\n\t\t\t\t\t\thost = XML_GetParameter(c, \"host\", \"\");\n\t\t\t\t\t\tport = atoi(XML_GetParameter(c, \"port\", \"0\"));\n\t\t\t\t\t\tif (port <= 0 || port > 65535)\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\tif (ft->nexthost < sizeof(ft->streamhosts) / sizeof(ft->streamhosts[ft->nexthost]))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tQ_strlcpy(ft->streamhosts[ft->nexthost].jid, jid, sizeof(ft->streamhosts[ft->nexthost].jid));\n\t\t\t\t\t\t\tQ_strlcpy(ft->streamhosts[ft->nexthost].host, host, sizeof(ft->streamhosts[ft->nexthost].host));\n\t\t\t\t\t\t\tft->streamhosts[ft->nexthost].port = port;\n\t\t\t\t\t\t\tft->nexthost++;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tft->streamstatus = STRM_AUTH;\n\t\t\t\t\t//iq gets acked when we finished connecting to the proxy. *then* the data can flow\n\t\t\t\t\tQ_strlcpy(ft->iqid, iqid, sizeof(ft->iqid));\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tJCL_AddClientMessagef(jcl, \"<iq id='%s' to='%s' type='error'/>\", iqid, iqfrom);\n\t\treturn true;\n\t}\n\n\tot = XML_ChildOfTreeNS(tree, \"http://jabber.org/protocol/ibb\", \"open\", 0);\n\tif (ot)\n\t{\n\t\tstruct ft_s *ft;\n\t\tconst char *sid = XML_GetParameter(ot, \"sid\", \"\");\n\t\tint blocksize = atoi(XML_GetParameter(ot, \"block-size\", \"4096\"));\t//technically this is required.\n\t\tconst char *stanza = XML_GetParameter(ot, \"stanza\", \"iq\");\n\t\tfor (ft = jcl->ft; ft; ft = ft->next)\n\t\t{\n\t\t\tif (!strcmp(ft->sid, sid) && !strcmp(ft->with, iqfrom))\n\t\t\t{\n\t\t\t\tif (ft->allowed && !ft->begun && ft->transmitting == false)\n\t\t\t\t{\n\t\t\t\t\tif (blocksize > 65536 || strcmp(stanza, \"iq\"))\n\t\t\t\t\t{\t//blocksize: MUST NOT be greater than 65535\n\t\t\t\t\t\tJCL_AddClientMessagef(jcl, \n\t\t\t\t\t\t\t\t\"<iq id='%s' to='%s' type='error'>\"\n\t\t\t\t\t\t\t\t\t\"<error type='modify'>\"\n\t\t\t\t\t\t\t\t\t\t\"<not-acceptable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>\"\n\t\t\t\t\t\t\t\t\t\"</error>\"\n\t\t\t\t\t\t\t\t\"</iq>\"\n\t\t\t\t\t\t\t\t, iqid, iqfrom);\n\t\t\t\t\t}\n\t\t\t\t\telse if (blocksize > 4096)\n\t\t\t\t\t{\t//ask for smaller chunks\n\t\t\t\t\t\tJCL_AddClientMessagef(jcl, \n\t\t\t\t\t\t\t\"<iq id='%s' to='%s' type='error'>\"\n\t\t\t\t\t\t\t\t\"<error type='modify'>\"\n\t\t\t\t\t\t\t\t\t\"<resource-constraint xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>\"\n\t\t\t\t\t\t\t\t\"</error>\"\n\t\t\t\t\t\t\t\"</iq>\"\n\t\t\t\t\t\t\t, iqid, iqfrom);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\t//it looks okay\n\t\t\t\t\t\tfilefuncs->Open(ft->fname, &ft->file, 2);\n\t\t\t\t\t\tft->method = FT_IBB;\n\t\t\t\t\t\tft->blocksize = blocksize;\n\t\t\t\t\t\tft->begun = true;\n\t\t\t\t\t\t//if its okay...\n\t\t\t\t\t\tJCL_AddClientMessagef(jcl, \"<iq id='%s' to='%s' type='result'/>\", iqid, iqfrom);\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\tot = XML_ChildOfTreeNS(tree, \"http://jabber.org/protocol/ibb\", \"close\", 0);\n\tif (ot)\n\t{\n\t\tstruct ft_s **link, *ft;\n\t\tconst char *sid = XML_GetParameter(ot, \"sid\", \"\");\n\t\tfor (link = &jcl->ft; *link; link = &(*link)->next)\n\t\t{\n\t\t\tft = *link;\n\t\t\tif (!strcmp(ft->sid, sid) && !strcmp(ft->with, iqfrom))\n\t\t\t{\n\t\t\t\tif (ft->begun && ft->method == FT_IBB)\n\t\t\t\t{\n\t\t\t\t\tint size;\n\t\t\t\t\tif (ft->file != -1)\n\t\t\t\t\t\tfilefuncs->Close(ft->file);\n\t\t\t\t\tif (ft->transmitting)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (ft->eof)\n\t\t\t\t\t\t\tCon_Printf(\"Sent \\\"%s\\\" to \\\"%s\\\"\\n\", ft->fname, ft->with);\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tCon_Printf(\"%s aborted transfer of \\\"%s\\\"\\n\", iqfrom, ft->fname);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tsize = filefuncs->Open(ft->fname, &ft->file, 1);\n\t\t\t\t\t\tif (ft->file != -1)\n\t\t\t\t\t\t\tfilefuncs->Close(ft->file);\n\t\t\t\t\t\tif (size == ft->size)\n\t\t\t\t\t\t\tCon_Printf(\"Received file \\\"%s\\\" successfully\\n\", ft->fname);\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tCon_Printf(\"%s aborted transfer of \\\"%s\\\"\\n\", iqfrom, ft->fname);\n\t\t\t\t\t}\n\t\t\t\t\t*link = ft->next;\n\t\t\t\t\tfree(ft);\n\t\t\t\t\t//if its okay...\n\t\t\t\t\tJCL_AddClientMessagef(jcl, \"<iq id='%s' to='%s' type='result'/>\", iqid, iqfrom);\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn false;\t//some kind of error\n\t}\n\tot = XML_ChildOfTreeNS(tree, \"http://jabber.org/protocol/ibb\", \"data\", 0);\n\tif (ot)\n\t{\n\t\tchar block[65536];\n\t\tconst char *sid = XML_GetParameter(ot, \"sid\", \"\");\n//\t\tunsigned short seq = atoi(XML_GetParameter(ot, \"seq\", \"0\"));\n\t\tint blocksize;\n\t\tstruct ft_s *ft;\n\t\t//FIXME: validate the sequence!\n\t\tfor (ft = jcl->ft; ft; ft = ft->next)\n\t\t{\n\t\t\tif (!strcmp(ft->sid, sid) && !ft->transmitting)\n\t\t\t{\n\t\t\t\tblocksize = Base64_Decode(block, sizeof(block), ot->body, strlen(ot->body));\n\t\t\t\tif (blocksize && blocksize <= ft->blocksize)\n\t\t\t\t{\n\t\t\t\t\tfilefuncs->Write(ft->file, block, blocksize);\n\t\t\t\t\tJCL_AddClientMessagef(jcl, \"<iq id='%s' to='%s' type='result'/>\", iqid, iqfrom);\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tCon_Printf(\"XMPP: Invalid blocksize in file transfer from %s\\n\", iqfrom);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\tot = XML_ChildOfTreeNS(tree, \"http://jabber.org/protocol/si\", \"si\", 0);\n\tif (ot)\n\t{\n\t\tconst char *profile = XML_GetParameter(ot, \"profile\", \"\");\n\n\t\tif (!strcmp(profile, \"http://jabber.org/protocol/si/profile/file-transfer\"))\n\t\t{\n//\t\t\tchar *mimetype = XML_GetParameter(ot, \"mime-type\", \"text/plain\");\n\t\t\tconst char *sid = XML_GetParameter(ot, \"id\", \"\");\n\t\t\txmltree_t *file = XML_ChildOfTreeNS(ot, \"http://jabber.org/protocol/si/profile/file-transfer\", \"file\", 0);\n\t\t\tconst char *fname = XML_GetParameter(file, \"name\", \"file.txt\");\n//\t\t\tchar *date = XML_GetParameter(file, \"date\", \"\");\n\t\t\tconst char *md5hash = XML_GetParameter(file, \"hash\", \"\");\n\t\t\tint fsize = strtoul(XML_GetParameter(file, \"size\", \"0\"), NULL, 0);\n//\t\t\tchar *desc = XML_GetChildBody(file, \"desc\", \"\");\n\t\t\tchar authlink[512];\n\t\t\tchar denylink[512];\n\t\t\t\n\t\t\t//file transfer offer\n\t\t\tstruct ft_s *ft = malloc(sizeof(*ft) + strlen(iqfrom)+1);\n\t\t\tmemset(ft, 0, sizeof(*ft));\n\t\t\tft->stream = -1;\n\t\t\tft->next = jcl->ft;\n\t\t\tjcl->ft = ft;\n\t\t\tft->privateid = ++jcl->privateidseq;\n\n\t\t\tft->transmitting = false;\n\t\t\tQ_strlcpy(ft->iqid, iqid, sizeof(ft->iqid));\n\t\t\tQ_strlcpy(ft->sid, sid, sizeof(ft->sid));\n\t\t\tQ_strlcpy(ft->fname, fname, sizeof(ft->sid));\n\t\t\tBase64_Decode(ft->md5hash, sizeof(ft->md5hash), md5hash, strlen(md5hash));\n\t\t\tft->size = fsize;\n\t\t\tft->file = -1;\n\t\t\tft->with = (char*)(ft+1);\n\t\t\tstrcpy(ft->with, iqfrom);\n\t\t\tft->method = (fsize > 1024*128)?FT_BYTESTREAM:FT_IBB;\t//favour bytestreams for large files. for small files, just use ibb as it saves sorting out proxies.\n\t\t\tft->begun = false;\n\n\t\t\tJCL_GenLink(jcl, authlink, sizeof(authlink), \"fauth\", iqfrom, NULL, va(\"%i\", ft->privateid), \"%s\", \"Accept\");\n\t\t\tJCL_GenLink(jcl, denylink, sizeof(denylink), \"fdeny\", iqfrom, NULL, va(\"%i\", ft->privateid), \"%s\", \"Deny\");\n\n\t\t\tCon_Printf(\"%s has offered to send you \\\"%s\\\" (%i bytes). %s %s\\n\", iqfrom, fname, fsize, authlink, denylink);\n\t\t}\n\t\telse\n\t\t{\n\t\t\t//profile not understood\n\t\t\tJCL_AddClientMessagef(jcl,\n\t\t\t\t\t\"<iq type='error' to='%s' id='%s'>\"\n\t\t\t\t\t\t\"<error code='400' type='cancel'>\"\n\t\t\t\t\t\t\t\"<bad-request xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>\"\n\t\t\t\t\t\t\t\"<bad-profile xmlns='http://jabber.org/protocol/si'/>\"\n\t\t\t\t\t\t\"</error>\"\n\t\t\t\t\t\"</iq>\", iqfrom, iqid);\n\t\t}\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n#endif\n"
  },
  {
    "path": "plugins/jabber/xml.c",
    "content": "#include \"../plugin.h\"\n\n#include \"xml.h\"\n\n//fixme\nvoid (*Con_TrySubPrint)(const char *conname, const char *message);\n\nvoid XML_Destroy(xmltree_t *t);\n\nconst char *XML_GetParameter(xmltree_t *t, const char *paramname, const char *def)\n{\n\txmlparams_t *p;\n\tif (t)\n\t{\n\t\tfor (p = t->params; p; p = p->next)\n\t\t\tif (!strcmp(p->name, paramname))\n\t\t\t\treturn p->val;\n\t}\n\treturn def;\n}\nvoid XML_AddParameter(xmltree_t *t, const char *paramname, const char *value)\n{\n\txmlparams_t *p = malloc(sizeof(xmlparams_t));\n\tQ_strlcpy(p->name, paramname, sizeof(p->name));\n\tQ_strlcpy(p->val, value?value:\"\", sizeof(p->val));\n\n\tif (t->params)\t//reverse insert\n\t{\n\t\txmlparams_t *prev;\n\t\tfor(prev = t->params; prev->next; prev = prev->next)\n\t\t\t;\n\t\tprev->next = p;\n\t\tp->next = NULL;\n\t}\n\telse\n\t{\n\t\tp->next = t->params;\n\t\tt->params = p;\n\t}\n}\nvoid XML_AddParameteri(xmltree_t *t, const char *paramname, int value)\n{\n\tchar svalue[64];\n\tQ_snprintf(svalue, sizeof(svalue), \"%i\", value);\n\tXML_AddParameter(t, paramname, svalue);\n}\nxmltree_t *XML_CreateNode(xmltree_t *parent, const char *name, const char *xmlns, const char *body)\n{\n\tint bodylen;\n\tstruct subtree_s *node = malloc(sizeof(*node));\n\n\tif (!body)\n\t\tbody = \"\";\n\tif (!xmlns)\n\t\txmlns = \"\";\n\n\tbodylen = strlen(body);\n\n\t//clear out links\n\tnode->params = NULL;\n\tnode->child = NULL;\n\tnode->sibling = NULL;\n\t//link into parent if we actually have a parent.\n\tif (parent)\n\t{\n\t\tif (parent->child)\n\t\t{\t//add at tail\n\t\t\txmltree_t *prev;\n\t\t\tfor(prev = parent->child; prev->sibling; prev = prev->sibling)\n\t\t\t\t;\n\t\t\tprev->sibling = node;\n\t\t\tnode->sibling = NULL;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tnode->sibling = parent->child;\n\t\t\tparent->child = node;\n\t\t}\n\t}\n\n\tQ_strlcpy(node->name, name, sizeof(node->name));\n\tQ_strlcpy(node->xmlns, xmlns, sizeof(node->xmlns));\n\tQ_strlcpy(node->xmlns_dflt, xmlns, sizeof(node->xmlns_dflt));\n\tnode->body = malloc(bodylen+1);\n\tmemcpy(node->body, body, bodylen+1);\n\n\tif (*xmlns)\n\t\tXML_AddParameter(node, \"xmlns\", xmlns);\n\n\treturn node;\n}\n\nconst struct\n{\n\tint codelen;\n\tchar *code;\n\tint namelen;\n\tchar *name;\n} xmlchars[] =\n{\n\t{1, \"<\",\t\t2, \"lt\"},\n\t{1, \">\",\t\t2, \"gt\"},\n\t{1, \"&\",\t\t3, \"amp\"},\n\t{1, \"\\'\",\t\t4, \"apos\"},\n\t{1, \"\\\"\",\t\t4, \"quot\"},\n\t{2, \"\\xC3\\x96\",\t4, \"ouml\"},\n\t{0, NULL,\t\t0, NULL}\n};\n//converts < to &lt; etc.\n//returns the end of d.\nchar *XML_Markup(const char *s, char *d, int dlen)\n{\n\tint i;\n\tdlen--;\n\twhile(*s)\n\t{\n\t\tfor(i = 0; xmlchars[i].code; i++)\n\t\t{\n\t\t\tif (!strncmp(s, xmlchars[i].code, xmlchars[i].codelen))\n\t\t\t\tbreak;\n\t\t}\n\t\tif (xmlchars[i].code)\n\t\t{\n\t\t\tif (dlen < xmlchars[i].namelen+2)\n\t\t\t\tbreak;\n\t\t\t*d++ = '&';\n\t\t\tmemcpy(d, xmlchars[i].name, xmlchars[i].namelen);\n\t\t\td+=xmlchars[i].namelen;\n\t\t\t*d++ = ';';\n\t\t\ts+=xmlchars[i].codelen;\n\t\t\tdlen -= xmlchars[i].namelen+2;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (!dlen)\n\t\t\t\tbreak;\n\t\t\tdlen--;\n\t\t\t*d++ = *s++;\n\t\t}\n\t}\n\t*d = 0;\n\treturn d;\n}\n//inplace. result will always be same length or shorter.\n//converts &lt; etc to their original chars\nvoid XML_Unmark(char *s)\n{\n\tchar *d;\n\tint i;\n\n\tfor (d = s; *s; )\n\t{\n\t\tif (*s == '&')\n\t\t{\n\t\t\ts++;\n\t\t\tfor (i = 0; xmlchars[i].name; i++)\n\t\t\t{\n\t\t\t\tif (!strncmp(s, xmlchars[i].name, xmlchars[i].namelen) && s[xmlchars[i].namelen] == ';')\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (xmlchars[i].name)\n\t\t\t{\n\t\t\t\ts += xmlchars[i].namelen+1;\n\n\t\t\t\tmemcpy(d, xmlchars[i].code, xmlchars[i].codelen);\n\t\t\t\td+=xmlchars[i].codelen;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t*d++ = '&';\n\t\t\t}\n\t\t}\n\t\telse\n\t\t\t*d++ = *s++;\n\t}\n\t*d = 0;\n}\n\nstruct buf_ctx\n{\n\tchar *buf;\n\tint len;\n\tint maxlen;\n};\nstatic void buf_cat(struct buf_ctx *buf, char *data, int datalen)\n{\n\tint newlen = buf->len + datalen+1;\n\tif (newlen > buf->maxlen)\n\t{\n\t\tchar *newd;\n\t\tnewlen *= 2;\n\t\tnewd = malloc(newlen);\n\t\tmemcpy(newd, buf->buf, buf->len);\n\t\tfree(buf->buf);\n\t\tbuf->buf = newd;\n\t\tbuf->maxlen = newlen;\n\t}\n\n\tmemcpy(buf->buf + buf->len, data, datalen);\n\tbuf->len += datalen;\n}\nstatic void XML_DumpToBuf(struct buf_ctx *buf, xmltree_t *t, int indent)\n{\n\txmltree_t *c;\n\txmlparams_t *p;\n\tint i;\n\tfor (i = 0; i < indent; i++)\n\t\tbuf_cat(buf, \" \", 1);\n\n\tbuf_cat(buf, \"<\", 1);\n\tbuf_cat(buf, t->name, strlen(t->name));\n\n\tfor (p = t->params; p; p = p->next)\n\t{\n\t\tbuf_cat(buf, \" \", 1);\n\t\tbuf_cat(buf, p->name, strlen(p->name));\n\t\tbuf_cat(buf, \"=\\'\", 2);\n\t\tbuf_cat(buf, p->val, strlen(p->val));\n\t\tbuf_cat(buf, \"\\'\", 1);\n\t}\n\n\tif (t->child)\n\t{\n\t\tbuf_cat(buf, \">\", 1);\n\t\tif (indent>=0)\n\t\t\tbuf_cat(buf, \"\\n\", 1);\n\t\tfor (c = t->child; c; c = c->sibling)\n\t\t\tXML_DumpToBuf(buf, c, ((indent<0)?indent:(indent+2)));\n\t\tfor (i = 0; i < indent; i++)\n\t\t\tbuf_cat(buf, \" \", 1);\n\t\tbuf_cat(buf, \"</\", 2);\n\t\tbuf_cat(buf, t->name, strlen(t->name));\n\t\tbuf_cat(buf, \">\", 1);\n\t}\n\telse if (*t->body)\n\t{\n\t\tbuf_cat(buf, \">\", 1);\n\t\tbuf_cat(buf, t->body, strlen(t->body));\n\t\tbuf_cat(buf, \"</\", 2);\n\t\tbuf_cat(buf, t->name, strlen(t->name));\n\t\tbuf_cat(buf, \">\", 1);\n\t}\n\telse\n\t{\n\t\tbuf_cat(buf, \"/>\", 2);\n\t}\n\tif (indent>=0)\n\t\tbuf_cat(buf, \"\\n\", 1);\n}\n\nchar *XML_GenerateString(xmltree_t *root, qboolean readable)\n{\n\tstruct buf_ctx buf = {NULL, 0, 0};\n\tXML_DumpToBuf(&buf, root, readable?0:-1);\n\tbuf_cat(&buf, \"\", 1);\n\treturn buf.buf;\n}\nxmltree_t *XML_Parse(const char *buffer, int *startpos, int maxpos, qboolean headeronly, const char *defaultnamespace)\n{\n\txmlparams_t *p;\n\txmltree_t *child;\n\txmltree_t **childlink;\t//to add at end, retaining order\n\txmltree_t *ret;\n\tint bodypos;\n\tint bodymax = 0;\n\tint pos, i;\n\tchar *tagend;\n\tconst char *tagstart;\n\tconst char *ns;\n\tchar token[1024];\n\tpos = *startpos;\n\twhile (buffer[pos] >= '\\0' && buffer[pos] <= ' ')\n\t{\n\t\tif (pos >= maxpos)\n\t\t\tbreak;\n\t\tpos++;\n\t}\n\n\tif (pos == maxpos)\n\t{\n\t\t*startpos = pos;\n\t\treturn NULL;\t//nothing anyway.\n\t}\n\nskippedcomment:\n\n\t//expect a <\n\n\tif (buffer[pos] != '<')\n\t{\n\t\tCon_Printf(\"Missing open bracket\\n\");\n\t\treturn NULL;\t//should never happen\n\t}\n\n\tif (buffer[pos+1] == '/')\n\t{\n\t\tCon_Printf(\"Unexpected close tag.\\n\");\n\t\treturn NULL;\t//err, terminating a parent tag\n\t}\n\n\tif (pos+4 < maxpos && buffer[pos+1] == '!' && buffer[pos+2] == '-' && buffer[pos+3] == '-')\n\t{\n\t\t//this looks like a comment. scan forwards until we find the -->\n\t\tpos += 4;\n\t\twhile (pos+3 < maxpos)\n\t\t{\n\t\t\tif (buffer[pos+0] == '-' && buffer[pos+1] == '-' && buffer[pos+2] == '>')\n\t\t\t{\n\t\t\t\tpos+=3;\n\t\t\t\tgoto skippedcomment;\n\t\t\t}\n\t\t\tpos++;\n\t\t}\n\t\tCon_Printf(\"Missing comment end\\n\");\n\t\treturn NULL;\t//should never happen\n\t}\n\n\ttagend = strchr(buffer+pos, '>');\n\tif (!tagend)\n\t{\n\t\tCon_Printf(\"Missing close bracket\\n\");\n\t\treturn NULL;\t//should never happen\n\t}\n\t*tagend = '\\0';\n\ttagend++;\n\n\t//assume no nulls in the tag header.\n\ttagstart = buffer+pos+1;\n\twhile (*tagstart == ' ' || *tagstart == '\\n' || *tagstart == '\\r' || *tagstart == '\\t')\n\t\ttagstart++;\n\tfor (i = 0; i < sizeof(token)-1 && *tagstart; )\n\t{\n\t\tif (*tagstart == ' ' || (i&&*tagstart == '/') || (i&&*tagstart == '?') || *tagstart == '\\n' || *tagstart == '\\r' || *tagstart == '\\t')\n\t\t\tbreak;\n\t\ttoken[i++] = *tagstart++;\n\t}\n\ttoken[i] = 0;\n\n\tpos = tagend - buffer;\n\n\tret = malloc(sizeof(xmltree_t));\n\tmemset(ret, 0, sizeof(*ret));\n\n\tns = strchr(token, ':');\n\tif (ns)\n\t{\n\t\tmemcpy(ret->xmlns, \"xmlns:\", 6);\n\t\tQ_strlncpy(ret->xmlns+6, token, sizeof(ret->xmlns)-6, ns-token);\n\t\tns++;\n\t\tQ_strlcpy(ret->name, ns, sizeof(ret->name));\n\t}\n\telse\n\t{\n\t\tQ_strlcpy(ret->xmlns, \"xmlns\", sizeof(ret->xmlns));\n\t\tQ_strlcpy(ret->name, token, sizeof(ret->name));\n\t}\n\n\twhile(*tagstart)\n\t{\n\t\tint nlen;\n\n\n\t\twhile(*(unsigned char*)tagstart <= ' ' && *tagstart)\n\t\t\ttagstart++;\t//skip whitespace (note that we know there is a null terminator before the end of the buffer)\n\n\t\tif (!*tagstart)\n\t\t\tbreak;\n\n\t\tp = malloc(sizeof(xmlparams_t));\n\t\tnlen = 0;\n\t\twhile (nlen < sizeof(p->name)-2)\n\t\t{\n\t\t\tif (*(unsigned char*)tagstart <= ' ' || *tagstart == '/' || *tagstart == '?')\n\t\t\t\tbreak;\n\n\t\t\tif (*tagstart == '=')\n\t\t\t\tbreak;\n\t\t\tp->name[nlen++] = *tagstart++;\n\t\t}\n\t\tp->name[nlen++] = '\\0';\n\n\t\twhile(*(unsigned char*)tagstart <= ' ' && *tagstart)\n\t\t\ttagstart++;\t//skip whitespace (note that we know there is a null terminator before the end of the buffer)\n\n\t\tif (*tagstart != '=')\n\t\t\tbreak;\n\t\ttagstart++;\n\n\t\twhile(*(unsigned char*)tagstart <= ' ' && *tagstart)\n\t\t\ttagstart++;\t//skip whitespace (note that we know there is a null terminator before the end of the buffer)\n\n\t\tnlen = 0;\n\t\tif (*tagstart == '\\'')\n\t\t{\n\t\t\ttagstart++;\n\t\t\twhile (*tagstart && nlen < sizeof(p->name)-2)\n\t\t\t{\n\t\t\t\tif(*tagstart == '\\'')\n\t\t\t\t\tbreak;\n\n\t\t\t\tp->val[nlen++] = *tagstart++;\n\t\t\t}\n\t\t\ttagstart++;\n\t\t\tp->val[nlen++] = '\\0';\n\t\t}\n\t\telse if (*tagstart == '\\\"')\n\t\t{\n\t\t\ttagstart++;\n\t\t\twhile (*tagstart && nlen < sizeof(p->name)-2)\n\t\t\t{\n\t\t\t\tif(*tagstart == '\\\"')\n\t\t\t\t\tbreak;\n\n\t\t\t\tp->val[nlen++] = *tagstart++;\n\t\t\t}\n\t\t\ttagstart++;\n\t\t\tp->val[nlen++] = '\\0';\n\t\t}\n\t\telse\n\t\t{\n\t\t\twhile (*tagstart && nlen < sizeof(p->name)-2)\n\t\t\t{\n\t\t\t\tif(*tagstart <= ' ')\n\t\t\t\t\tbreak;\n\n\t\t\t\tp->val[nlen++] = *tagstart++;\n\t\t\t}\n\t\t\tp->val[nlen++] = '\\0';\n\t\t}\n\t\tXML_Unmark(p->val);\n\t\tp->next = ret->params;\n\t\tret->params = p;\n\t}\n\n\tns = XML_GetParameter(ret, ret->xmlns, \"\");\n\tQ_strlcpy(ret->xmlns, ns, sizeof(ret->xmlns));\n\n\tns = XML_GetParameter(ret, \"xmlns\", NULL);\n\tQ_strlcpy(ret->xmlns_dflt, ns?ns:defaultnamespace, sizeof(ret->xmlns_dflt));\n\n\ttagend[-1] = '>';\n\n\tif (tagend[-2] == '/')\n\t{\t//no body\n\t\tret->body = malloc(1);\n\t\t*ret->body = 0;\n\t\t*startpos = pos;\n\t\treturn ret;\n\t}\n\tif (ret->name[0] == '?')\n\t{\n\t\t//no body either\n\t\tif (tagend[-2] == '?')\n\t\t{\n\t\t\tret->body = malloc(1);\n\t\t\t*ret->body = 0;\n\t\t\t*startpos = pos;\n\t\t\treturn ret;\n\t\t}\n\t}\n\n\tif (headeronly)\n\t{\n\t\t*startpos = pos;\n\t\treturn ret;\n\t}\n\n\t//does it have a body, or is it child tags?\n\n\tchildlink = &ret->child;\n\tbodypos = 0;\n\twhile(1)\n\t{\n\t\tif (pos == maxpos)\n\t\t{\t//malformed\n\t\t\tCon_Printf(\"tree is malfored\\n\");\n\t\t\tXML_Destroy(ret);\n\t\t\treturn NULL;\n\t\t}\n\n\t\tif (buffer[pos] == '<')\n\t\t{\n\t\t\tif (buffer[pos+1] == '/')\n\t\t\t{\t//the end of this block\n\t\t\t\t//FIXME: check name\n\n\t\t\t\ttagend = strchr(buffer+pos, '>');\n\t\t\t\tif (!tagend)\n\t\t\t\t{\n\t\t\t\t\tCon_Printf(\"No close tag\\n\");\n\t\t\t\t\tXML_Destroy(ret);\n\t\t\t\t\treturn NULL;\t//should never happen\n\t\t\t\t}\n\t\t\t\ttagend++;\n\t\t\t\tpos = tagend - buffer;\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tif (!strncmp(&buffer[pos], \"<![CDATA[\", 9))\n\t\t\t{\n\t\t\t\tpos += 9;\n\t\t\t\twhile(1)\n\t\t\t\t{\n\t\t\t\t\tchar c;\n\t\t\t\t\tif (pos == maxpos)\n\t\t\t\t\t{\t//malformed\n\t\t\t\t\t\tCon_Printf(\"CDATA is malfored (inside %s)\\n\", ret->name);\n\t\t\t\t\t\tXML_Destroy(ret);\n\t\t\t\t\t\treturn NULL;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (buffer[pos+0] == ']' && buffer[pos+1] == ']' && buffer[pos+2] == '>')\n\t\t\t\t\t{\n\t\t\t\t\t\tpos += 3;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\tc = buffer[pos++];\n\t\t\t\t\tif (bodypos == bodymax)\n\t\t\t\t\t{\n\t\t\t\t\t\tint nlen = bodypos*2 + 64;\n\t\t\t\t\t\tchar *nb = malloc(nlen);\n\t\t\t\t\t\tmemcpy(nb, ret->body, bodypos);\n\t\t\t\t\t\tfree(ret->body);\n\t\t\t\t\t\tret->body = nb;\n\t\t\t\t\t\tbodymax = nlen;\n\t\t\t\t\t}\n\t\t\t\t\tret->body[bodypos++] = c;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tchild = XML_Parse(buffer, &pos, maxpos, false, ret->xmlns_dflt);\n\t\t\t\tif (!child)\n\t\t\t\t{\n\t\t\t\t\tCon_Printf(\"Child block is unparsable (within %s)\\n\", ret->name);\n\t\t\t\t\tXML_Destroy(ret);\n\t\t\t\t\treturn NULL;\n\t\t\t\t}\n\n\t\t\t\tchild->sibling = *childlink;\n\t\t\t\t*childlink = child;\n\t\t\t\tchildlink = &child->sibling;\n\t\t\t}\n\t\t}\n\t\telse \n\t\t{\n\t\t\tchar c = buffer[pos++];\n\t\t\tif (bodypos == bodymax)\n\t\t\t{\n\t\t\t\tint nlen = bodypos*2 + 64;\n\t\t\t\tchar *nb = malloc(nlen);\n\t\t\t\tmemcpy(nb, ret->body, bodypos);\n\t\t\t\tfree(ret->body);\n\t\t\t\tret->body = nb;\n\t\t\t\tbodymax = nlen;\n\t\t\t}\n\t\t\tret->body[bodypos++] = c;\n\t\t}\n\t}\n\tif (bodypos == bodymax)\n\t{\n\t\tint nlen = bodypos+1;\n\t\tchar *nb = malloc(nlen);\n\t\tmemcpy(nb, ret->body, bodypos);\n\t\tfree(ret->body);\n\t\tret->body = nb;\n\t\tbodymax = nlen;\n\t}\n\tret->body[bodypos++] = '\\0';\n\n\tXML_Unmark(ret->body);\n\n\t*startpos = pos;\n\n\treturn ret;\n}\n\nvoid XML_Destroy(xmltree_t *t)\n{\n\txmlparams_t *p, *np;\n\n\tif (t->child)\n\t\tXML_Destroy(t->child);\n\tif (t->sibling)\n\t\tXML_Destroy(t->sibling);\n\n\tfor (p = t->params; p; p = np)\n\t{\n\t\tnp = p->next;\n\t\tfree(p);\n\t}\n\tfree(t->body);\n\tfree(t);\n}\n\nxmltree_t *XML_ChildOfTree(xmltree_t *t, const char *name, int childnum)\n{\n\tif (t)\n\t{\n\t\tfor (t = t->child; t; t = t->sibling)\n\t\t{\n\t\t\tif (!strcmp(t->name, name))\n\t\t\t{\n\t\t\t\tif (childnum-- == 0)\n\t\t\t\t\treturn t;\n\t\t\t}\n\t\t}\n\t}\n\treturn NULL;\n}\nxmltree_t *XML_ChildOfTreeNS(xmltree_t *t, const char *xmlns, const char *name, int childnum)\n{\n\tif (t)\n\t{\n\t\tfor (t = t->child; t; t = t->sibling)\n\t\t{\n\t\t\tif (!strcmp(t->xmlns, xmlns) && !strcmp(t->name, name))\n\t\t\t{\n\t\t\t\tif (childnum-- == 0)\n\t\t\t\t\treturn t;\n\t\t\t}\n\t\t}\n\t}\n\treturn NULL;\n}\nconst char *XML_GetChildBody(xmltree_t *t, const char *paramname, const char *def)\n{\n\txmltree_t *c = XML_ChildOfTree(t, paramname, 0);\n\tif (c)\n\t\treturn c->body;\n\treturn def;\n}\n\nvoid XML_ConPrintTree(xmltree_t *t, const char *subconsole, int indent)\n{\n\tint start, c, chunk;\n\tstruct buf_ctx buf = {NULL, 0, 0};\n\tif (!t)\n\t\treturn;\n\tXML_DumpToBuf(&buf, t, indent);\n\tbuf_cat(&buf, \"\", 1);\n\n\tfor (start = 0; start < buf.len; )\n\t{\n\t\tchunk = buf.len - start;\n\t\tif (chunk > 128)\n\t\t\tchunk = 128;\n\t\tc = buf.buf[start+chunk];\n\t\tbuf.buf[start+chunk] = 0;\n\t\tCon_TrySubPrint(subconsole, buf.buf+start);\n\t\tbuf.buf[start+chunk] = c;\n\n\t\tstart += chunk;\n\t}\n\n\tfree(buf.buf);\n}\n\n\nstatic void XML_SkipWhite(const char *msg, int *pos, int max)\n{\n\twhile (*pos < max && (\n\t\tmsg[*pos] == ' ' ||\n\t\tmsg[*pos] == '\\t' ||\n\t\tmsg[*pos] == '\\r' ||\n\t\tmsg[*pos] == '\\n'\n\t\t))\n\t\t*pos+=1;\n}\nstatic qboolean XML_ParseString(const char *msg, int *pos, int max, char *out, int outlen)\n{\n\t*out = 0;\n\tif (*pos < max && msg[*pos] == '\\\"')\n\t{\n\t\t*pos+=1;\n\n\t\toutlen--;\n\t\twhile (*pos < max && msg[*pos] != '\\\"')\n\t\t{\n\t\t\tif (!outlen)\n\t\t\t\treturn false;\n\t\t\t*out = msg[*pos];\n\t\t\tout++;\n\t\t\toutlen--;\n\t\t\t*pos+=1;\n\t\t}\n\t\tif (*pos < max && msg[*pos] == '\\\"')\n\t\t{\n\t\t\t*out = 0;\n\t\t\t*pos+=1;\n\t\t\treturn true;\n\t\t}\n\t}\n\telse\n\t{\n\t\toutlen--;\n\t\twhile (*pos < max\n\t\t\t&& msg[*pos] != ' '\n\t\t\t&& msg[*pos] != '\\t'\n\t\t\t&& msg[*pos] != '\\r'\n\t\t\t&& msg[*pos] != '\\n'\n\t\t\t&& msg[*pos] != ':'\n\t\t\t&& msg[*pos] != ','\n\t\t\t&& msg[*pos] != '}'\n\t\t\t&& msg[*pos] != '{')\n\t\t{\n\t\t\tif (!outlen)\n\t\t\t\treturn false;\n\t\t\t*out = msg[*pos];\n\t\t\tout++;\n\t\t\toutlen--;\n\t\t\t*pos+=1;\n\t\t}\n\t\t*out = 0;\n\t\treturn true;\n\t}\n\treturn false;\n}\nxmltree_t *XML_FromJSON(xmltree_t *t, const char *name, const char *json, int *jsonpos, int jsonlen)\n{\n\tchar child[4096];\n\tXML_SkipWhite(json, jsonpos, jsonlen);\n\n\tif (*jsonpos < jsonlen && json[*jsonpos] == '{')\n\t{\n\t\t*jsonpos+=1;\n\t\tXML_SkipWhite(json, jsonpos, jsonlen);\n\n\t\tt = XML_CreateNode(t, name, \"\", \"\");\n\n\t\twhile (*jsonpos < jsonlen && json[*jsonpos] == '\\\"')\n\t\t{\n\t\t\tif (!XML_ParseString(json, jsonpos, jsonlen, child, sizeof(child)))\n\t\t\t\tbreak;\n\t\t\tXML_SkipWhite(json, jsonpos, jsonlen);\n\t\t\tif (*jsonpos < jsonlen && json[*jsonpos] == ':')\n\t\t\t{\n\t\t\t\t*jsonpos+=1;\n\t\t\t\tif (!XML_FromJSON(t, child, json, jsonpos, jsonlen))\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tXML_SkipWhite(json, jsonpos, jsonlen);\n\n\t\t\tif (*jsonpos < jsonlen && json[*jsonpos] == ',')\n\t\t\t{\n\t\t\t\t*jsonpos+=1;\n\t\t\t\tXML_SkipWhite(json, jsonpos, jsonlen);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\n\t\tif (*jsonpos < jsonlen && json[*jsonpos] == '}')\n\t\t{\n\t\t\t*jsonpos+=1;\n\t\t\treturn t;\n\t\t}\n\t\tXML_Destroy(t);\n\t}\n\telse if (*jsonpos < jsonlen)\n\t{\n\t\tif (XML_ParseString(json, jsonpos, jsonlen, child, sizeof(child)))\n\t\t\treturn XML_CreateNode(t, name, \"\", child);\n\t}\n\treturn NULL;\n}\n"
  },
  {
    "path": "plugins/jabber/xml.h",
    "content": "typedef struct xmlparams_s\r\n{\r\n\tchar val[256];\t//FIXME: make pointer\r\n\tstruct xmlparams_s *next;\r\n\tchar name[128];\t//FIXME: make variable sized\r\n} xmlparams_t;\r\n\r\ntypedef struct subtree_s\r\n{\r\n\tchar name[64];\t\t\t\t\t\t\t\t\t\t\t\t//FIXME: make pointer to tail of structure\r\n\tchar xmlns[64];\t\t\t//namespace of the element\t\t\t//FIXME: make pointer to tail of structure\r\n\tchar xmlns_dflt[64];\t//default namespace of children\t\t//FIXME: make pointer to tail of structure\r\n\tchar *body;//[2048];\t\t\t\t\t\t\t\t\t\t\t//FIXME: make pointer+variablesized\r\n\r\n\txmlparams_t *params;\r\n\r\n\tstruct subtree_s *child;\r\n\tstruct subtree_s *sibling;\r\n} xmltree_t;\r\n\r\n\r\n\r\nconst char *XML_GetParameter(xmltree_t *t, const char *paramname, const char *def);\r\nvoid XML_AddParameter(xmltree_t *t, const char *paramname, const char *value);\r\nvoid XML_AddParameteri(xmltree_t *t, const char *paramname, int value);\r\nxmltree_t *XML_CreateNode(xmltree_t *parent, const char *name, const char *xmlns, const char *body);\r\nchar *XML_Markup(const char *s, char *d, int dlen);\r\nvoid XML_Unmark(char *s);\r\nchar *XML_GenerateString(xmltree_t *root, qboolean readable);\r\nxmltree_t *XML_Parse(const char *buffer, int *startpos, int maxpos, qboolean headeronly, const char *defaultnamespace);\r\nvoid XML_Destroy(xmltree_t *t);\r\nxmltree_t *XML_ChildOfTree(xmltree_t *t, const char *name, int childnum);\r\nxmltree_t *XML_ChildOfTreeNS(xmltree_t *t, const char *xmlns, const char *name, int childnum);\r\nconst char *XML_GetChildBody(xmltree_t *t, const char *paramname, const char *def);\r\nvoid XML_ConPrintTree(xmltree_t *t, const char *subconsole, int indent);\r\n\r\nxmltree_t *XML_FromJSON(xmltree_t *t, const char *name, const char *json, int *jsonpos, int jsonlen);\r\n"
  },
  {
    "path": "plugins/jabber/xmpp.h",
    "content": "#include \"../plugin.h\"\r\nextern plugnetfuncs_t *netfuncs;\r\nextern plugfsfuncs_t *filefuncs;\r\n\r\n#include \"xml.h\"\r\n\r\n\r\n//configuration\r\n\r\n//#define NOICE\t//if defined, we only do simple raw udp connections.\r\n#define FILETRANSFERS\t\t//IBB only, speeds suck. autoaccept is forced on. no protection from mods stuffcmding sendfile commands. needs more extensive testing\r\n#define QUAKECONNECT\t\t//including quake ICE connections (depends upon jingle)\r\n#define VOIP\t\t\t\t//enables voice chat (depends upon jingle)\r\n//#define VOIP_LEGACY\t\t\t//enables google-only voice chat compat. google have not kept up with the standardisation of jingle (aka: gingle).\r\n//#define VOIP_LEGACY_ONLY\t//disables jingle feature (advert+detection only)\r\n#define JINGLE\t\t\t\t//enables jingle signalling\r\n\r\n#ifdef JINGLE\r\n\t#include \"../../engine/common/netinc.h\"\r\n#else\r\n\t#undef VOIP\r\n\t#undef VOIP_LEGACY\r\n#endif\r\n\r\n\r\n#define JCL_BUILD \"5\"\r\n//#define DEFAULTDOMAIN \"triptohell.info\"\r\n#define DEFAULTRESOURCE \"Quake\"\r\n#define QUAKEMEDIAXMLNS \"http://fteqw.com/protocol/quake\"\r\n#define DISCONODE\t\t\"http://fteqw.com/ftexmpp\"\t//some sort of client identifier\r\n#define DEFAULTICEMODE ICEM_ICE\r\n\r\n#define MEDIATYPE_QUAKE \"quake\"\r\n#define MEDIATYPE_VIDEO \"video\"\r\n#define MEDIATYPE_AUDIO \"audio\"\r\n\r\n\r\n\r\n#define JCL_MAXMSGLEN 0x10000\r\n\r\n//values are not on the wire or anything\r\n#define CAP_VOICE\t\t\t(1u<<0)\t\t//supports voice\r\n#define CAP_GOOGLE_VOICE\t(1u<<1)\t\t//google supports some old non-standard version of jingle.\r\n#define CAP_VIDEO\t\t\t(1u<<2)\t\t//supports video calls\r\n\r\n#define CAP_GAMEINVITE\t\t(1u<<3)\t\t//supports game invites. custom/private protocol\r\n#define CAP_POKE\t\t\t(1u<<4)\t\t//can be slapped.\r\n#define CAP_SIFT\t\t\t(1u<<5)\t\t//non-jingle file transfers\r\n#define\tCAP_AVATARS\t\t\t(1u<<6)\t\t//can enable querying for user avatars, but cannot disable advertising our own.\r\n\r\n//not actually capabilities, but just down to how we handle querying them.\r\n#define CAP_QUERYING\t\t(1u<<29)\t//we've sent a query and are waiting for the response.\r\n#define CAP_QUERIED\t\t\t(1u<<30)\t//feature capabilities are actually know.\r\n#define CAP_QUERYFAILED\t\t(1u<<31)\t//caps request failed due to bad hash or some such.\r\n\r\n//features that default to disabled.\r\n#define CAP_DEFAULTENABLEDCAPS (CAP_VOICE/*|CAP_VIDEO*/|CAP_GAMEINVITE|CAP_POKE|CAP_AVATARS/*|CAP_SIFT*/|CAP_GOOGLE_VOICE)\r\n\r\ntypedef struct bresource_s\r\n{\r\n\tchar bstatus[128];\t//basic status\r\n\tchar fstatus[128];\t//full status\r\n\tchar server[256];\r\n\tint servertype;\t//0=none, 1=already a client, 2=joinable\r\n\tint priority;\r\n\r\n\tunsigned int buggycaps;\r\n\tunsigned int caps;\r\n\tchar *client_node;\t//vendor name\r\n\tchar *client_ver;\t//cap hash value\r\n\tchar *client_hash;\t//cap hash method\r\n\tchar *client_ext;\t//deprecated. additionally queried\r\n\r\n\tstruct bresource_s *next;\r\n\r\n\tchar resource[1];\r\n} bresource_t;\r\ntypedef struct buddy_s\r\n{\r\n\tbresource_t *resources;\r\n\tbresource_t *defaultresource;\t//this is the one that last replied (must be null for chatrooms, so we only talk to the room in general)\r\n\tbresource_t *ourselves;\t\t\t//this is set back to ourselves when in a chatroom\r\n\tint defaulttimestamp;\r\n\tqboolean askfriend;\r\n\tqboolean friended;\r\n\tqboolean vcardphotochanged;\r\n\tenum {\r\n\t\tBT_UNKNOWN, //this buddy isn't known...\r\n\t\tBT_ROSTER,\t//this is a friend! or at least on our list of potential friends anyway.\r\n\t\tBT_CHATROOM //we're treating this 'guy' as a MUC, each of their resources is a different person. which is weird.\r\n\t} btype;\r\n\r\n\tqboolean room_autojoin;\r\n\tchar *room_nick;\r\n\tchar *room_password;\r\n\tchar *room_topic;\r\n\r\n\tchar name[256];\r\n\tchar vcardphotohash[41];\r\n\tqhandle_t image;\r\n\r\n\tstruct buddy_s *next;\r\n\tchar accountdomain[1];\t//no resource on there\r\n} buddy_t;\r\ntypedef struct jclient_s\r\n{\r\n\tint accountnum;\t//a private id to track which client links are associated with\r\n\r\n\tchar redirserveraddr[64];\t//if empty, do an srv lookup.\r\n\r\n\tenum\r\n\t{\r\n\t\tJCL_INACTIVE,\t//not trying to connect.\r\n\t\tJCL_DEAD,\t\t//not connected. connection died or something.\r\n\t\tJCL_AUTHING,\t//connected, but not able to send any info on it other than to auth\r\n\t\tJCL_ACTIVE\t\t//we're connected, we got a buddy list and everything\r\n\t} status;\r\n\tunsigned int timeout;\t\t//reconnect/ping timer\r\n\tchar errormsg[256];\r\n\r\n\tunsigned int enabledcapabilities;\r\n\r\n\tqhandle_t socket;\r\n\r\n\tqhandle_t rcon_pipe;\t//contains console prints\r\n\tchar rcon_peer[256];\t//the name of the guy currently receiving console prints\r\n\r\n\t//we buffer output for times when the outgoing socket is full.\r\n\t//mostly this only happens at the start of the connection when the socket isn't actually open yet.\r\n\tchar *outbuf;\r\n\tint outbufpos;\r\n\tint outbuflen;\r\n\tint outbufmax;\r\n\r\n\tchar bufferedinmessage[JCL_MAXMSGLEN+1];\t//servers are required to be able to handle messages no shorter than a specific size.\r\n\t\t\t\t\t\t\t\t\t\t\t\t//which means we need to be able to handle messages when they get to us.\r\n\t\t\t\t\t\t\t\t\t\t\t\t//servers can still handle larger messages if they choose, so this might not be enough.\r\n\tint bufferedinammount;\r\n\r\n\tchar defaultdest[256];\r\n\r\n\t//config info\r\n\tchar serveraddr[64];\t//if empty, do an srv lookup.\r\n\tint serverport;\r\n\tchar domain[256];\r\n\tchar username[256];\r\n\tchar resource[256];\r\n\tchar certificatedomain[256];\r\n\tint forcetls;\t//-1=off, 0=ifpossible, 1=fail if can't upgrade, 2=old-style tls\r\n\tqboolean savepassword;\r\n\r\n\tchar fulljid[256];\t//this is our full username@domain/resource string\r\n\tchar barejid[256];\t//this is our bare username@domain string\r\n\tchar localalias[256];//this is what's shown infront of outgoing messages. >> by default until we can get our name.\r\n\tchar vcardphotohash[20];\t//20-byte sha1 hash.\r\n\tenum\r\n\t{\r\n\t\tVCP_UNKNOWN,\r\n\t\tVCP_NONE,\r\n\t\tVCP_KNOWN\r\n\t} vcardphotohashstatus;\r\n\tqboolean vcardphotohashchanged;\t//forces a presence send.\r\n\r\n\tint instreampos;\r\n\r\n\tqboolean connecting; //still waiting for intial stream tag\r\n\tqboolean connected;\t//fully on server and authed and everything.\r\n\tqboolean issecure;\t//tls enabled (either upgraded or initially)\r\n\tint streamdebug;\t//echo the stream to subconsoles\r\n\r\n\tqboolean preapproval;\t//server supports presence preapproval\r\n\r\n\tchar curquakeserver[2048];\r\n\tchar defaultnamespace[2048];\t//should be 'jabber:client' or blank (and spammy with all the extra xmlns attribs)\r\n\r\n\tstruct sasl_ctx_s\r\n\t{\r\n\t\tchar *username;\t//might be different from the account name, but probably isn't.\r\n\t\tchar *domain;\t//might be different from the account domain, but probably isn't.\r\n\t\tqboolean issecure;\t//tls enabled (either upgraded or initially)\r\n\t\tint socket;\r\n\r\n\t\t//this stuff should be saved\r\n\t\tchar password_plain[256];\t\t//plain password. scrubbed if we auth using a hashed auth.\r\n\t\tqbyte password_hash[256];\t\t//safer password, not encrypted, but stored hashed.\r\n\t\tsize_t password_hash_size;\r\n\t\tchar password_validity[256];\t//internal string used to check that the salt was unchanged\r\n\r\n\t\tstruct saslmethod_s *authmethod;\t//null name = oauth2->saslmethod\r\n\t\tqboolean allowauth_plainnontls;\t//allow plain plain\r\n\t\tqboolean allowauth_plaintls;\t//allow tls plain\r\n\t\tqboolean allowauth_digestmd5;\t//allow digest-md5 auth\r\n\t\tqboolean allowauth_scramsha1;\t//allow scram-sha-1 auth\r\n\t\tqboolean allowauth_oauth2;\t\t//use oauth2 where possible\r\n\r\n\t\tstruct\r\n\t\t{\r\n\t\t\tchar authnonce[256];\r\n\t\t} digest;\r\n\r\n\t\tstruct\r\n\t\t{\r\n\t\t\tqboolean plus;\r\n\t\t\tchar authnonce[256];\r\n\t\t\tchar authvhash[DIGEST_MAXSIZE];\r\n\t\t\tchar authcbindtype[20];\r\n\t\t\tchar authcbinding[256];\r\n\t\t\thashfunc_t *hashfunc;\r\n\t\t} scram;\r\n\r\n\t\tstruct\r\n\t\t{\r\n\t\t\tchar saslmethod[64];\r\n\t\t\tchar obtainurl[256];\r\n\t\t\tchar refreshurl[256];\r\n\t\t\tchar clientid[256];\r\n\t\t\tchar clientsecret[256];\r\n\t\t\tchar *useraccount;\r\n\t\t\tchar *scope;\r\n\t\t\tchar *accesstoken;\t//one-shot access token\r\n\t\t\tchar *refreshtoken;\t//long-term token that we can use to get new access tokens\r\n\t\t\tchar *authtoken;\t//short-term authorisation token, usable to get an access token (and a refresh token if we're lucky)\r\n\t\t} oauth2;\r\n\t} sasl;\r\n\r\n\tstruct iq_s\r\n\t{\r\n\t\tstruct iq_s *next;\r\n\t\tchar id[64];\r\n\t\tint timeout;\r\n\t\tqboolean (*callback) (struct jclient_s *jcl, struct subtree_s *tree, struct iq_s *iq);\r\n\t\tvoid *usrptr;\r\n\t\tchar to[1];\r\n\t} *pendingiqs;\r\n\r\n#ifdef JINGLE\r\n\tstruct c2c_s\r\n\t{\r\n\t\tstruct c2c_s *next;\r\n\t\tqboolean accepted;\t//connection is going\r\n\t\tqboolean creator;\t//true if we're the creator.\r\n\t\tunsigned int peercaps;\r\n\t\tqboolean displayed;\t//temp flag for displaying jingle sessions with people that are not on our buddy list for whatever reasons\r\n\r\n\t\tstruct\r\n\t\t{\r\n\t\t\tchar name[64];\t//uniquely identifies the content within the session.\r\n\t\t\tenum iceproto_e mediatype;\r\n\t\t\tenum icemode_e method;\t//ICE_RAW or ICE_ICE. this is what the peer asked for. updated if we degrade it.\r\n\t\t\tstruct icestate_s *ice;\r\n\t\t\tchar *peeraddr;\r\n\t\t\tint peerport;\r\n\t\t} content[3];\r\n\t\tint contents;\r\n\r\n\t\tchar *with;\r\n\t\tchar sid[1];\r\n\t} *c2c;\r\n#endif\r\n\r\n#ifdef FILETRANSFERS\r\n\tstruct ft_s\r\n\t{\r\n\t\tstruct ft_s *next;\r\n\t\tchar fname[MAX_QPATH];\r\n\t\tint size;\r\n\t\tint sizedone;\r\n\t\tchar *with;\r\n\t\tchar md5hash[16];\r\n\t\tint privateid;\r\n\t\tchar iqid[64];\r\n\t\tchar sid[64];\r\n\t\tint blocksize;\r\n\t\tunsigned short seq;\r\n\t\tqhandle_t file;\r\n\t\tqhandle_t stream;\r\n\t\tqboolean begun;\t//handshake\r\n\t\tqboolean eof;\r\n\t\tqboolean transmitting;\t//we're offering\r\n\t\tqboolean allowed;\t//if false, don't handshake the transfer\r\n\r\n\t\tstruct\r\n\t\t{\r\n\t\t\tchar jid[128];\r\n\t\t\tchar host[40];\r\n\t\t\tint port;\r\n\t\t} streamhosts[16];\r\n\t\tint nexthost;\r\n\t\tenum\r\n\t\t{\r\n\t\t\tSTRM_IDLE,\r\n\t\t\tSTRM_AUTH,\r\n\t\t\tSTRM_AUTHED,\r\n\t\t\tSTRM_ACTIVE\r\n\t\t} streamstatus;\r\n\t\tchar indata[64];\t//only for handshake data\r\n\t\tint inlen;\r\n\r\n\t\tenum\r\n\t\t{\r\n\t\t\tFT_NOTSTARTED,\r\n\t\t\tFT_IBB,\t\t\t//in-band bytestreams\r\n\t\t\tFT_BYTESTREAM\t//aka: relay\r\n\t\t} method;\r\n\t} *ft;\r\n\tint privateidseq;\r\n#endif\r\n\r\n\r\n\t//persistant achived info about buddies, to avoid spamming the server every time we connect\r\n\t//such things might result in lag mid game!\r\n\tstruct buddyinfo_s\r\n\t{\r\n\t\tstruct buddyinfo_s *next;\r\n\t\tchar *image;\r\n\t\tchar *imagehash;\r\n\t\tchar *imagemime;\r\n\t\tchar accountdomain[1];\r\n\t} *buddyinfo;\r\n\r\n\tbuddy_t *buddies;\r\n\tstruct iq_s *avatarupdate;\t//we only grab one buddy's photo at a time, this is to avoid too much spam.\r\n} jclient_t;\r\n\r\n\r\n#ifdef JINGLE\r\nextern icefuncs_t *piceapi;\r\n#endif\r\n\r\n\r\nqboolean NET_DNSLookup_SRV(const char *host, char *out, int outlen);\r\n\r\n//xmpp functionality\r\nstruct iq_s *JCL_SendIQNode(jclient_t *jcl, qboolean (*callback) (jclient_t *jcl, xmltree_t *tree, struct iq_s *iq), const char *iqtype, const char *target, xmltree_t *node, qboolean destroynode);\r\nvoid JCL_AddClientMessagef(jclient_t *jcl, const char *fmt, ...);\r\nvoid JCL_AddClientMessageString(jclient_t *jcl, const char *msg);\r\nqboolean JCL_FindBuddy(jclient_t *jcl, const char *jid, buddy_t **buddy, bresource_t **bres, qboolean create);\r\nvoid JCL_ForgetBuddy(jclient_t *jcl, buddy_t *buddy, bresource_t *bres);\r\n\r\n//quake functionality\r\nvoid JCL_GenLink(jclient_t *jcl, char *out, int outlen, const char *action, const char *context, const char *contextres, const char *sid, const char *txtfmt, ...);\r\nvoid Con_SubPrintf(const char *subname, const char *format, ...);\r\nvoid XMPP_ConversationPrintf(const char *context, const char *title, qboolean takefocus, char *format, ...);\r\n\r\n//jingle functions\r\nvoid JCL_Join(jclient_t *jcl, const char *target, const char *sid, qboolean allow, int protocol);\r\nvoid JCL_JingleTimeouts(jclient_t *jcl, qboolean killall);\r\n//jingle iq message handlers\r\nqboolean JCL_HandleGoogleSession(jclient_t *jcl, xmltree_t *tree, const char *from, const char *id);\r\nqboolean JCL_ParseJingle(jclient_t *jcl, xmltree_t *tree, const char *from, const char *id);\r\n\r\nvoid XMPP_FT_AcceptFile(jclient_t *jcl, int fileid, qboolean accept);\r\nqboolean XMPP_FT_OfferAcked(jclient_t *jcl, xmltree_t *x, struct iq_s *iq);\r\nqboolean XMPP_FT_ParseIQSet(jclient_t *jcl, const char *iqfrom, const char *iqid, xmltree_t *tree);\r\nvoid XMPP_FT_SendFile(jclient_t *jcl, const char *console, const char *to, const char *fname);\r\nvoid XMPP_FT_Frame(jclient_t *jcl);\r\n\r\nvoid Base64_Add(const char *s, int len);\r\nchar *Base64_Finish(void);\r\nint Base64_Decode(char *out, int outlen, const char *src, int srclen);\r\n"
  },
  {
    "path": "plugins/models/draco.cpp",
    "content": "#ifdef __cplusplus\nextern \"C\"\n{\n#endif\n#include <stddef.h>\n\n\t//I fucking hate c++\n\ttypedef struct ftedracofuncs_s\n\t{\n\t\tvoid (*Release)(struct ftedracofuncs_s *ctx);\t//frees all memory\n\n\t\tstruct ftedracoattr_s\n\t\t{\n\t\t\tvoid *ptr;\n\t\t\tint isnormalised;\n\t\t\tunsigned int type;\t//gl constants, because why not\n\t\t\tunsigned int components; //1-4\n\t\t\tunsigned int bytestride;\n\t\t\tunsigned int usage;\t//typical usage type.\n\t\t\tunsigned int uniqueid;\t//annoying extra lookup.\n\t\t} *attrib;\n\t\tunsigned int num_attribs;\n\n\t\t//mesh data\n\t\tchar triangleloop;\n\t\tunsigned int num_indexes;\n\t\tunsigned int *ptr_indexes, num_vertexes;\n\t} ftedracofuncs_t;\n\n\tftedracofuncs_t *Draco_Decode(void *ptr, size_t size);\n\n#ifdef __cplusplus\n};\n#endif\n\n#if !defined(DRACO_API_ONLY) && defined(HAVE_DRACO)\n#include <draco/compression/decode.h>\n\nstruct dracofuncs_t : public ftedracofuncs_s\n{\n\tdraco::Decoder decoder;\n\tdraco::DecoderBuffer buffer;\n\tdraco::Mesh mesh;\n\n\tstatic void DoRelease(ftedracofuncs_s *ctx)\n\t{\n\t\tauto d = reinterpret_cast<dracofuncs_t*>(ctx);\n\t\tdelete[] d->ptr_indexes;\n\t\tfor (auto i = d->num_attribs; i --> 0; )\n\t\t\tdelete[] (char*)d->attrib[i].ptr;\n\t\tdelete[] d->attrib;\n\t\tdelete d;\t//do all the third-party destructor stuff too.\n\t}\n\tdracofuncs_t()\n\t{\n\t\tRelease = DoRelease;\n\t}\n};\ntemplate <class attrtype, int gltype>\nstatic void CopyAttribute(unsigned int numpoints, draco::PointAttribute *attr, ftedracofuncs_s::ftedracoattr_s *oattr)\n{\n\tauto nc = oattr->components;\n\tauto *out = new attrtype[numpoints*nc];\n\tfor (size_t i = 0; i < numpoints; i++)\n\t\tattr->GetMappedValue(draco::PointIndex(i), &out[i*nc]);\n\n\toattr->bytestride = sizeof(attrtype)*nc; //the output data is tightly packed.\n\toattr->ptr = out;\n\toattr->type = gltype;\n};\nftedracofuncs_t *Draco_Decode(void *ptr, size_t size)\n{\n\tsize_t tris;\n\tdracofuncs_t *d = new dracofuncs_t();\n\n\td->buffer.Init((const char *)ptr, size);\n\tif (!d->decoder.DecodeBufferToGeometry(&d->buffer, &d->mesh).ok())\n\t{\t//check for corruption...\n\t\tdelete d;\n\t\treturn NULL;\n\t}\n\n\ttris = d->mesh.num_faces();\n\td->num_indexes = tris*3;\n\td->ptr_indexes = new unsigned int[d->num_indexes];\n\twhile (tris --> 0)\n\t{\n\t\tauto &tri = d->mesh.face(draco::FaceIndex(tris));\n\t\td->ptr_indexes[tris*3+0] = tri[0].value();\n\t\td->ptr_indexes[tris*3+1] = tri[1].value();\n\t\td->ptr_indexes[tris*3+2] = tri[2].value();\n\t}\n\n\td->num_vertexes = d->mesh.num_points();\n\td->num_attribs = d->mesh.num_attributes();\n\td->attrib = new struct ftedracofuncs_s::ftedracoattr_s[d->num_attribs];\n\tfor (unsigned int i = 0; i < d->num_attribs; i++)\n\t{\n\t\tdraco::PointAttribute *attr = d->mesh.attribute(i);\n\t\tauto oattr = &d->attrib[i];\n\t\toattr->isnormalised = attr->normalized();\n\t\toattr->bytestride = attr->byte_stride();\n\t\toattr->components = attr->num_components();\n\t\toattr->usage = attr->attribute_type();\n\t\toattr->uniqueid = attr->unique_id();\n#define GL_BYTE\t\t\t\t\t0x1400\n#define GL_UNSIGNED_BYTE\t\t0x1401\n#define GL_SHORT\t\t\t\t0x1402\n#define GL_UNSIGNED_SHORT\t\t0x1403\n#define GL_INT\t\t\t\t\t0x1404\n#define GL_UNSIGNED_INT\t\t\t0x1405\n#define GL_FLOAT\t\t\t\t0x1406\n#define GL_DOUBLE\t\t\t\t0x140A\n#define GL_INT64_ARB            0x140E\n#define GL_UNSIGNED_INT64_ARB   0x140F\n\n\t\tswitch(attr->data_type())\n\t\t{\n\t\tdefault:\n\t\t\tmemset(oattr, 0, sizeof(*oattr));\n\t\t\tbreak;\n\t\tcase draco::DataType::DT_INT8:\t\tCopyAttribute< int8_t,\tGL_BYTE\t\t\t\t\t>(d->num_vertexes, attr, oattr);\tbreak;\n\t\tcase draco::DataType::DT_UINT8:\t\tCopyAttribute<uint8_t,\tGL_UNSIGNED_BYTE\t\t>(d->num_vertexes, attr, oattr);\tbreak;\n\t\tcase draco::DataType::DT_INT16:\t\tCopyAttribute< int16_t,\tGL_SHORT\t\t\t\t>(d->num_vertexes, attr, oattr);\tbreak;\n\t\tcase draco::DataType::DT_UINT16:\tCopyAttribute<uint16_t,\tGL_UNSIGNED_SHORT\t\t>(d->num_vertexes, attr, oattr);\tbreak;\n\t\tcase draco::DataType::DT_INT32:\t\tCopyAttribute< int32_t,\tGL_INT\t\t\t\t\t>(d->num_vertexes, attr, oattr);\tbreak;\n\t\tcase draco::DataType::DT_UINT32:\tCopyAttribute<uint32_t,\tGL_UNSIGNED_INT\t\t\t>(d->num_vertexes, attr, oattr);\tbreak;\n\t\tcase draco::DataType::DT_INT64:\t\tCopyAttribute< int64_t,\tGL_INT64_ARB\t\t\t>(d->num_vertexes, attr, oattr);\tbreak;\n\t\tcase draco::DataType::DT_UINT64:\tCopyAttribute<uint64_t,\tGL_UNSIGNED_INT64_ARB\t>(d->num_vertexes, attr, oattr);\tbreak;\n\t\tcase draco::DataType::DT_FLOAT32:\tCopyAttribute<float,\tGL_FLOAT\t\t\t\t>(d->num_vertexes, attr, oattr);\tbreak;\n\t\tcase draco::DataType::DT_FLOAT64:\tCopyAttribute<double,\tGL_DOUBLE\t\t\t\t>(d->num_vertexes, attr, oattr);\tbreak;\n\t\t}\n\t}\n\treturn d;\n}\n\n#endif"
  },
  {
    "path": "plugins/models/exportiqm.c",
    "content": "#ifndef GLQUAKE\n#define GLQUAKE\t//this is shit.\n#endif\n#include \"quakedef.h\"\n#include \"../plugin.h\"\n#include \"com_mesh.h\"\n\n#ifdef SKELETALMODELS\n\nextern plugmodfuncs_t *modfuncs;\nextern plugfsfuncs_t *filefuncs;\n\n#define IQM_MAGIC \"INTERQUAKEMODEL\"\n#define IQM_VERSION2 2\n\nstruct iqmheader\n{\n\tchar magic[16];\n\tunsigned int version;\n\tunsigned int filesize;\n\tunsigned int flags;\n\tunsigned int num_text, ofs_text;\n\tunsigned int num_meshes, ofs_meshes;\n\tunsigned int num_vertexarrays, num_vertexes, ofs_vertexarrays;\n\tunsigned int num_triangles, ofs_triangles, ofs_adjacency;\n\tunsigned int num_joints, ofs_joints;\n\tunsigned int num_poses, ofs_poses;\n\tunsigned int num_anims, ofs_anims;\n\tunsigned int num_frames, num_framechannels, ofs_frames, ofs_bounds;\n\tunsigned int num_comment, ofs_comment;\n\tunsigned int num_extensions, ofs_extensions;\n};\nstruct iqmmesh\n{\n\tunsigned int name;\n\tunsigned int material;\n\tunsigned int first_vertex, num_vertexes;\n\tunsigned int first_triangle, num_triangles;\n};\n\nenum\n{\n\tIQM_POSITION     = 0,\n\tIQM_TEXCOORD     = 1,\n\tIQM_NORMAL       = 2,\n\tIQM_TANGENT      = 3,\n\tIQM_BLENDINDEXES = 4,\n\tIQM_BLENDWEIGHTS = 5,\n\tIQM_COLOR        = 6,\n\tIQM_CUSTOM       = 0x10\n};\n\nenum\n{\n\tIQM_BYTE   = 0,\n\tIQM_UBYTE  = 1,\n\tIQM_SHORT  = 2,\n\tIQM_USHORT = 3,\n\tIQM_INT    = 4,\n\tIQM_UINT   = 5,\n\tIQM_HALF   = 6,\n\tIQM_FLOAT  = 7,\n\tIQM_DOUBLE = 8,\n};\n\nstruct iqmtriangle\n{\n\tunsigned int vertex[3];\n};\n\nstruct iqmjoint2\n{\n\tunsigned int name;\n\tint parent;\n\tfloat translate[3], rotate[4], scale[3];\n};\n\nstruct iqmpose2\n{\n\tint parent;\n\tunsigned int mask;\n\tfloat channeloffset[10];\n\tfloat channelscale[10];\n};\n\nstruct iqmanim\n{\n\tunsigned int name;\n\tunsigned int first_frame, num_frames;\n\tfloat framerate;\n\tunsigned int flags;\n};\n\nenum\n{\n\tIQM_LOOP = 1<<0\n};\n\nstruct iqmvertexarray\n{\n\tunsigned int type;\n\tunsigned int flags;\n\tunsigned int format;\n\tunsigned int size;\n\tunsigned int offset;\n};\n\nstruct iqmbounds\n{\n\tfloat bbmin[3], bbmax[3];\n\tfloat xyradius, radius;\n};\n\nstruct iqmextension\n{\n\tunsigned int name;\n\tunsigned int num_data, ofs_data;\n\tunsigned int ofs_extensions; // pointer to next extension. wtf is up with this? how is this not redundant due to ofs_data?\n};\n\n\nstruct iqmext_fte_mesh\n{\n\tunsigned int contents;\t\t//default CONTENTS_BODY\n\tunsigned int surfaceflags;\t//propagates to trace_surfaceflags\n\tunsigned int surfaceid;\t\t//the body reported to qc via trace_surface\n\tunsigned int geomset;\n\tunsigned int geomid;\n\tfloat\tmindist;\n\tfloat\tmaxdist;\n};\nstruct iqmext_fte_event\n{\n\tunsigned int anim;\n\tfloat timestamp;\n\tunsigned int evcode;\n\tunsigned int evdata_str;\t//stringtable\n};\nstatic void CrossProduct_ (const vec3_t v1, const vec3_t v2, vec3_t cross)\n{\n\tcross[0] = v1[1]*v2[2] - v1[2]*v2[1];\n\tcross[1] = v1[2]*v2[0] - v1[0]*v2[2];\n\tcross[2] = v1[0]*v2[1] - v1[1]*v2[0];\n}\nstatic void Bone_To_PosQuat4(const float *matrix, float *pos, float *quat4, float *scale)\n{\t//I originally ripped this function out of DP. tweaked slightly.\n\t//http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm\n\tfloat origininvscale = 1;\n\tfloat origin[3];\n\tfloat quat[4];\n\tfloat quatscale;\n\n\tfloat trace = matrix[0*4+0] + matrix[1*4+1] + matrix[2*4+2];\n\n\torigin[0] = matrix[0*4+0];\n\torigin[1] = matrix[1*4+0];\n\torigin[2] = matrix[2*4+0];\n\tscale [0] = sqrt(DotProduct(origin,origin));\n\torigin[0] = matrix[0*4+1];\n\torigin[1] = matrix[1*4+1];\n\torigin[2] = matrix[2*4+1];\n\tscale [1] = sqrt(DotProduct(origin,origin));\n\torigin[1] = matrix[0*4+2];\n\torigin[1] = matrix[1*4+2];\n\torigin[2] = matrix[2*4+2];\n\tscale [2] = sqrt(DotProduct(origin,origin));\n\n\torigin[0] = matrix[0*4+3];\n\torigin[1] = matrix[1*4+3];\n\torigin[2] = matrix[2*4+3];\n\tif(trace > 0)\n\t{\n\t\tfloat r = sqrt(1.0f + trace), inv = 0.5f / r;\n\t\tquat[0] = (matrix[2*4+1] - matrix[1*4+2]) * inv;\n\t\tquat[1] = (matrix[0*4+2] - matrix[2*4+0]) * inv;\n\t\tquat[2] = (matrix[1*4+0] - matrix[0*4+1]) * inv;\n\t\tquat[3] = 0.5f * r;\n\t}\n\telse if(matrix[0*4+0] > matrix[1*4+1] && matrix[0*4+0] > matrix[2*4+2])\n\t{\n\t\tfloat r = sqrt(1.0f + matrix[0*4+0] - matrix[1*4+1] - matrix[2*4+2]), inv = 0.5f / r;\n\t\tquat[0] = 0.5f * r;\n\t\tquat[1] = (matrix[1*4+0] + matrix[0*4+1]) * inv;\n\t\tquat[2] = (matrix[0*4+2] + matrix[2*4+0]) * inv;\n\t\tquat[3] = (matrix[2*4+1] - matrix[1*4+2]) * inv;\n\t}\n\telse if(matrix[1*4+1] > matrix[2*4+2])\n\t{\n\t\tfloat r = sqrt(1.0f + matrix[1*4+1] - matrix[0*4+0] - matrix[2*4+2]), inv = 0.5f / r;\n\t\tquat[0] = (matrix[1*4+0] + matrix[0*4+1]) * inv;\n\t\tquat[1] = 0.5f * r;\n\t\tquat[2] = (matrix[2*4+1] + matrix[1*4+2]) * inv;\n\t\tquat[3] = (matrix[0*4+2] - matrix[2*4+0]) * inv;\n\t}\n\telse\n\t{\n\t\tfloat r = sqrt(1.0f + matrix[2*4+2] - matrix[0*4+0] - matrix[1*4+1]), inv = 0.5f / r;\n\t\tquat[0] = (matrix[0*4+2] + matrix[2*4+0]) * inv;\n\t\tquat[1] = (matrix[2*4+1] + matrix[1*4+2]) * inv;\n\t\tquat[2] = 0.5f * r;\n\t\tquat[3] = (matrix[1*4+0] - matrix[0*4+1]) * inv;\n\t}\n\t// normalize quaternion so that it is unit length\n\tquatscale = quat[0]*quat[0]+quat[1]*quat[1]+quat[2]*quat[2]+quat[3]*quat[3];\n\tif (quatscale)\n\t\tquatscale = (quat[3] >= 0 ? -1.0f : 1.0f) / sqrt(quatscale);\n\n\t// use a negative scale on the quat because the above function produces a\n\t// positive quat[3] and canonical quaternions have negative quat[3]\n\tVectorScale(origin, origininvscale, pos);\n\tVector4Scale(quat, quatscale, quat4);\n}\nvoid Mod_ExportIQM(char *fname, int flags, galiasinfo_t *mesh)\n{\n\tint i, j, k;\n\tvfsfile_t *f;\n\tgaliasinfo_t *m;\n\tqbyte *data = NULL;\n\tchar *otext;\n\tstruct iqmvertexarray *ovarr;\n\tstruct iqmtriangle *otri;\n\tstruct iqmmesh *omesh;\n\tstruct iqmjoint2 *ojoint = NULL;\n\tstruct iqmanim *oanim = NULL;\n\tstruct iqmpose2 *opose = NULL;\n\tstruct\n\t{\n\t\tfloat min[10], max[10], scale[10];\n\t\tint flags;\n\t} *poseinfo = NULL, *pi;\t//per bone\n\tstruct\n\t{\t//pos3, quat4, scale3\n\t\tfloat posquatscale[10];\t//raw values, used to calibrate ranges\n\t} *posedata = NULL, *pd;\t//per bone*joint\n\tvecV_t *ivert;\n\tvec2_t *ist;\n\tvec3_t *overt;\n\tvec3_t *onorm = NULL;\n\tvec4_t *otang = NULL;\n\tvec2_t *ost;\n\tbone_vec4_t *oboneidx = NULL;\n\tbyte_vec4_t *oboneweight = NULL;\n\tunsigned short *oposedata = NULL;\n\tstruct iqmheader hdr = {IQM_MAGIC, IQM_VERSION2}, *oh;\n\thdr.flags = flags;\n\thdr.num_vertexarrays = 4;\n\thdr.num_triangles = 0;\n\thdr.ofs_adjacency = 0;\t//noone actually uses this...\n\thdr.num_poses = 0;\n//\thdr.ofs_poses = 0;\n\thdr.num_anims = 0;\n//\thdr.ofs_anims = 0;\n\thdr.num_frames = 0;\n\thdr.num_framechannels = 0;\n//\thdr.ofs_frames = 0;\n//\thdr.ofs_bounds = 0;\n\thdr.num_comment = 0;\n//\thdr.ofs_comment = 0;\n\thdr.num_extensions = 0;\n//\thdr.ofs_extensions = 0;\n\n\n\thdr.num_joints = mesh->numbones;\n\tif (hdr.num_joints)\n\t{\n\t\tfloat *matrix;\n\t\thdr.num_vertexarrays+= 2;\n\t\thdr.num_anims = mesh->numanimations;\n\t\tfor (i = 0; i < hdr.num_anims; i++)\n\t\t{\n\t\t\thdr.num_text += strlen(mesh->ofsanimations[i].name)+1;\n\t\t\thdr.num_frames += mesh->ofsanimations[i].numposes;\n\t\t}\n\t\tif (hdr.num_frames)\n\t\t{\n\t\t\tposeinfo = malloc(sizeof(*poseinfo)*hdr.num_joints);\n\t\t\thdr.num_poses = hdr.num_joints;\n\t\t\tposedata = malloc(sizeof(*posedata)*hdr.num_joints*hdr.num_poses);\n\t\t\t//pull out the raw data and convert to the quats that we need\n\t\t\tfor (i = 0, pd = posedata; i < hdr.num_anims; i++)\n\t\t\t\tfor (j = 0, matrix = mesh->ofsanimations[i].boneofs; j < mesh->ofsanimations[i].numposes; j++)\n\t\t\t\t\tfor (k = 0; k < hdr.num_joints; k++)\n\t\t\t\t\t{\n\t\t\t\t\t\tBone_To_PosQuat4(matrix, &pd->posquatscale[0], &pd->posquatscale[3], &pd->posquatscale[7]);\n\t\t\t\t\t\tpd++;\n\t\t\t\t\t\tmatrix+=12;\n\t\t\t\t\t}\n\t\t\t//now figure out each poseinfo's min+max\n\t\t\tfor (i = 0, pi = poseinfo; i < hdr.num_joints; i++, pi++)\n\t\t\t\tfor (j = 0, pd = posedata+i; j < hdr.num_poses; j++, pd+=hdr.num_joints)\n\t\t\t\t\tfor (k = 0; k < 10; k++)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (!i || pd->posquatscale[k] < pi->min[k])\n\t\t\t\t\t\t\tpi->min[k] = pd->posquatscale[k];\n\t\t\t\t\t\tif (!i || pi[i].max[k] < pd->posquatscale[k])\n\t\t\t\t\t\t\tpi->max[k] = pd->posquatscale[k];\n\t\t\t\t\t}\n\t\t\t//figure out the offset+range+flags\n\t\t\tfor (i = 0, pi = poseinfo; i < hdr.num_joints; i++, pi++)\n\t\t\t\tfor (k = 0; k < 10; k++)\n\t\t\t\t{\n\t\t\t\t\tpi->scale[k] = pi->max[k]-pi->min[k];\n\t\t\t\t\tif (pi->scale[k] < 1e-10f)\n\t\t\t\t\t\t;\t//total range is tiny and won't make any real difference, drop this channel for a small saving.\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tpi->scale[k] /= 0xffffu;\t//compensate for the datatype's max\n\t\t\t\t\t\tpi->flags |= 1u<<k;\n\t\t\t\t\t\thdr.num_framechannels++;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\thdr.num_framechannels *= hdr.num_frames; //there'll be one for each pose*channel*frame\n\t\t}\n\t}\n\thdr.num_text += hdr.num_joints*32; //gah\n\n\t//count needed data\n\tfor (m = mesh; m; m = m->nextsurf)\n\t{\n\t\t//can't handle the surface if its verts are weird.\n\t\tif (m->shares_verts && m->shares_verts != hdr.num_meshes)\n\t\t\tcontinue;\n\t\t//can only handle one set of bones.\n\t\tif (m->shares_bones != 0)\n\t\t\tcontinue;\n\t\t//and must have the same number of bones.\n\t\tif (hdr.num_joints != m->numbones)\n\t\t\tcontinue;\n\n\t\thdr.num_text += strlen(m->surfacename)+1;\n\t\tif (m->ofsskins && m->ofsskins->frame)\n\t\t\thdr.num_text += strlen(m->ofsskins->frame->shadername)+1;\n\t\thdr.num_triangles += m->numindexes/3;\n\t\thdr.num_vertexes += m->numverts;\n\t\thdr.num_meshes++;\n\t}\n\n\t//allocate our output buffer\n#define ALLOCSPACE\thdr.filesize = 0; \\\n\t\t\t\t\t\tALLOC(oh, sizeof(*oh));\t\\\n\t\t\t\t\t\tALLOC(otext, sizeof(*otext)*hdr.num_text);\t\\\n\t\t\t\t\t\tALLOC(ovarr, sizeof(*ovarr)*hdr.num_vertexarrays); \\\n\t\t\t\t\t\tALLOC(otri, sizeof(*otri)*hdr.num_triangles); \\\n\t\t\t\t\t\tALLOC(overt, sizeof(*overt)*hdr.num_vertexes); \\\n\t\t\t\t\t\tALLOC(ost, sizeof(*ost)*hdr.num_vertexes);\t\\\n\t\t\t\t\t\tif (mesh->ofs_skel_norm) {ALLOC(onorm, sizeof(*onorm)*hdr.num_vertexes);} \\\n\t\t\t\t\t\tif (mesh->ofs_skel_svect && mesh->ofs_skel_tvect) {ALLOC(otang, sizeof(*otang)*hdr.num_vertexes);} \\\n\t\t\t\t\t\tif (hdr.num_joints) {ALLOC(oboneweight, sizeof(*oboneweight)*hdr.num_vertexes);} \\\n\t\t\t\t\t\tif (hdr.num_joints) {ALLOC(oboneidx, sizeof(*oboneidx)*hdr.num_vertexes);} \\\n\t\t\t\t\t\tif (hdr.num_joints) {ALLOC(ojoint, sizeof(*ojoint)*hdr.num_joints);} \\\n\t\t\t\t\t\tif (hdr.num_anims) {ALLOC(oanim, sizeof(*oanim)*hdr.num_anims);} \\\n\t\t\t\t\t\tif (hdr.num_poses) {ALLOC(opose, sizeof(*opose)*hdr.num_poses);} \\\n\t\t\t\t\t\tif (hdr.num_framechannels) {ALLOC(oposedata, sizeof(*oposedata)*hdr.num_framechannels);} \\\n\t\t\t\t\t\tALLOC(omesh, sizeof(*omesh)*hdr.num_meshes);\n#define ALLOC(p,s) p=(void*)(data+hdr.filesize);hdr.filesize+=s;\n\tALLOCSPACE;\t//figure out how much space we need\n\tdata = malloc(hdr.filesize);\n\tmemset(data, 0xFE, hdr.filesize);\n\tALLOCSPACE;\t//and assign everything to the right offsets.\n#undef ALLOC\n#undef ALLOCSPACE\n\n\t//copy over the preliminary header\n\t*oh = hdr;\n\n#define hdr hdr\n\n\tif (omesh) oh->ofs_meshes = (qbyte*)omesh-data;\n\tif (otext) oh->ofs_text = (qbyte*)otext-data;\n\tif (ovarr) oh->ofs_vertexarrays = (qbyte*)ovarr-data;\n\tif (otri) oh->ofs_triangles = (qbyte*)otri-data;\n\tif (ojoint) oh->ofs_joints = (qbyte*)ojoint-data;\n\tif (opose) oh->ofs_poses = (qbyte*)opose-data;\n\tif (oposedata) oh->ofs_frames = (qbyte*)oposedata-data;\n\tif (oanim) oh->ofs_anims = (qbyte*)oanim-data;\n\n\t//set up vertex array data. we might add some padding here, in case the extra data isn't availble.\n\tmemset(ovarr, 0, sizeof(*ovarr)*oh->num_vertexarrays);\n\toh->num_vertexarrays=0;\n\tovarr->type = IQM_POSITION;\n\tovarr->flags = 0;\n\tovarr->format = IQM_FLOAT;\n\tovarr->size = 3;\n\tovarr->offset = (qbyte*)overt - data;\n\tovarr++;\n\toh->num_vertexarrays++;\n\n\tovarr->type = IQM_TEXCOORD;\n\tovarr->flags = 0;\n\tovarr->format = IQM_FLOAT;\n\tovarr->size = 2;\n\tovarr->offset = (qbyte*)ost - data;\n\tovarr++;\n\toh->num_vertexarrays++;\n\n\tif (onorm)\n\t{\n\t\tovarr->type = IQM_NORMAL;\n\t\tovarr->flags = 0;\n\t\tovarr->format = IQM_FLOAT;\n\t\tovarr->size = 3;\n\t\tovarr->offset = (qbyte*)onorm - data;\n\t\tovarr++;\n\t\toh->num_vertexarrays++;\n\t}\n\tif (otang)\n\t{\n\t\tovarr->type = IQM_TANGENT;\n\t\tovarr->flags = 0;\n\t\tovarr->format = IQM_FLOAT;\n\t\tovarr->size = 4;\n\t\tovarr->offset = (qbyte*)otang - data;\n\t\tovarr++;\n\t\toh->num_vertexarrays++;\n\t}\n\tif (oboneidx)\n\t{\n\t\tovarr->type = IQM_BLENDINDEXES;\n\t\tovarr->flags = 0;\n\t\tovarr->format = (MAX_BONES>65536)?IQM_UINT:((MAX_BONES>256)?IQM_USHORT:IQM_UBYTE);\n\t\tovarr->size = 4;\n\t\tovarr->offset = (qbyte*)oboneidx - data;\n\t\tovarr++;\n\t\toh->num_vertexarrays++;\n\t}\n\tif (oboneweight)\n\t{\n\t\tovarr->type = IQM_BLENDWEIGHTS;\n\t\tovarr->flags = 0;\n\t\tovarr->format = IQM_BYTE;\n\t\tovarr->size = 4;\n\t\tovarr->offset = (qbyte*)oboneweight - data;\n\t\tovarr++;\n\t\toh->num_vertexarrays++;\n\t}\n\t/*if (orgba)\n\t{\n\t\tovarr->type = IQM_COLOR;\n\t\tovarr->flags = 0;\n\t\tovarr->format = IQM_FLOAT;\n\t\tovarr->size = 4;\n\t\tovarr->offset = (qbyte*)orgba - data;\n\t\tovarr++;\n\t\toh->num_vertexarrays++;\n\t}*/\n\n\tif (ojoint)\n\t{\n\t\tfor (i = 0; i < hdr.num_joints; i++)\n\t\t{\n\t\t\tojoint[i].parent = mesh->ofsbones[i].parent;\n\t\t\tojoint[i].name = (qbyte*)otext-(data+oh->ofs_text);\n\t\t\tstrcpy(otext, mesh->ofsbones[i].name);\n\t\t\totext += strlen(otext)+1;\n\n\t\t\tBone_To_PosQuat4(mesh->ofsbones[i].inverse, ojoint[i].translate, ojoint[i].rotate, ojoint[i].scale);\n\t\t}\n\t}\n\tif (opose)\n\t{\n\t\tint c;\n\t\tfor (i = 0, pi=poseinfo; i < hdr.num_joints; i++, pi++)\n\t\t{\n\t\t\topose[i].parent = mesh->ofsbones[i].parent;\n\t\t\topose[i].mask = pi->flags;\n\t\t\tfor (k = 0; k < 10; k++)\n\t\t\t{\n\t\t\t\topose[i].channeloffset[k] = pi->min[k];\n\t\t\t\topose[i].channelscale[k] = pi->scale[k];\n\t\t\t}\n\n\t\t\tfor (j = 0, pd = posedata+i; j < hdr.num_frames; j++, pd+=hdr.num_joints)\n\t\t\t{\n\t\t\t\tfor (k = 0; k < 10; k++)\n\t\t\t\t{\n\t\t\t\t\tif (opose[i].mask & (1<<k))\n\t\t\t\t\t{\n\t\t\t\t\t\tc = (pd->posquatscale[k]-pi->min[k])/pi->scale[k];\n\t\t\t\t\t\tc = bound(0, c, 0xffff);\t//clamp it just in case (floats can be annoying)\n\t\t\t\t\t\t*oposedata++ = c;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tif (oposedata)\n\t{\n\t\tfor (i = 0; i < hdr.num_joints; i++)\n\t\t{\n\t\t\topose[i].parent = mesh->ofsbones[i].parent;\n\t\t\topose[i].mask = poseinfo[i].flags;\n\t\t\tfor (k = 0; k < 10; k++)\n\t\t\t{\n\t\t\t\topose[i].channeloffset[k] = poseinfo[i].min[k];\n\t\t\t\topose[i].channelscale[k] = poseinfo[i].scale[k];\n\t\t\t}\n\t\t}\n\t}\n\n\thdr.num_frames = 0;\n\tfor (i = 0; i < hdr.num_anims; i++, oanim++)\n\t{\n\t\toanim->first_frame = hdr.num_frames;\n\t\toanim->num_frames = mesh->ofsanimations[i].numposes;\n\t\toanim->framerate = mesh->ofsanimations[i].rate;\n\t\toanim->flags = mesh->ofsanimations[i].loop?IQM_LOOP:0;\n\t\toanim->name = (qbyte*)otext-(data+oh->ofs_text);\n\t\tstrcpy(otext, mesh->ofsanimations[i].name);\n\t\totext += strlen(otext)+1;\n\t\thdr.num_frames += mesh->ofsanimations[i].numposes;\n\t}\n\toh->num_anims = i;\n\toh->num_frames = hdr.num_frames;\n\n\t//count needed data\n\thdr.num_triangles = 0;\n\thdr.num_vertexes = 0;\n\tfor (m = mesh; m; m = m->nextsurf)\n\t{\n\t\t//can't handle the surface if its verts are weird.\n\t\tif (m->shares_verts && m->shares_verts != hdr.num_meshes)\n\t\t\tcontinue;\n\t\t//can only handle one set of bones.\n\t\tif (m->shares_bones != 0)\n\t\t\tcontinue;\n\t\tif (hdr.num_joints != m->numbones)\n\t\t\tcontinue;\n\n\t\tomesh->name = (qbyte*)otext-(data+oh->ofs_text);\n\t\tstrcpy(otext, m->surfacename);\n\t\totext += strlen(otext)+1;\n\n\t\tomesh->material = (qbyte*)otext-(data+oh->ofs_text);\n\t\tif (m->ofsskins && m->ofsskins->frame)\n\t\t\tstrcpy(otext, m->ofsskins->frame->shadername);\n\t\telse\n\t\t\tstrcpy(otext, \"\");\n\t\totext += strlen(otext)+1;\n\n\t\tomesh->first_vertex = hdr.num_vertexes;\n\t\tomesh->num_vertexes = m->numverts;\n\t\tomesh->first_triangle = hdr.num_triangles;\n\t\tomesh->num_triangles = m->numindexes/3;\n\n\t\tif (m->ofs_skel_xyz)\n\t\t\tivert = m->ofs_skel_xyz;\n#ifdef NONSKELETALMODELS\n\t\telse if (m->numanimations && m->ofsanimations->numposes)\n\t\t\tivert = m->ofsanimations->poseofs->ofsverts;\n#endif\n\t\telse\n\t\t\tivert = NULL;\n\t\tif (ivert)\n\t\t{\n\t\t\tfor (i = 0; i < omesh->num_vertexes; i++)\n\t\t\t\tVectorCopy (ivert[i], overt[i]);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tfor (i = 0; i < omesh->num_vertexes; i++)\n\t\t\t\tVectorClear (overt[i]);\n\n\t\t}\n\t\tovert += i;\n\n\t\tif (oboneidx)\n\t\t{\n\t\t\tbone_vec4_t *iidx = m->ofs_skel_idx;\n\t\t\tvec4_t *iweight = m->ofs_skel_weight;\n\t\t\tfor (i = 0; i < omesh->num_vertexes; i++)\n\t\t\t{\n\t\t\t\tVector4Copy(iidx[i], oboneidx[i]);\n\t\t\t\tVector4Scale(iweight[i], 255, oboneweight[i]);\n\t\t\t}\n\t\t\toboneidx += i;\n\t\t\toboneweight += i;\n\t\t}\n\n\t\tif (ost)\n\t\t{\n\t\t\tist = m->ofs_st_array;\n\t\t\tfor (i = 0; i < omesh->num_vertexes; i++)\n\t\t\t\tVector2Copy(ist[i], ost[i]);\n\t\t\tost += i;\n\t\t}\n\n\t\tif (onorm)\n\t\t{\n\t\t\tvec3_t *inorm, *isdir, *itdir, t;\n\t\t\tif (m->ofs_skel_norm)\n\t\t\t{\n\t\t\t\tinorm = m->ofs_skel_norm;\n\t\t\t\tisdir = m->ofs_skel_svect;\n\t\t\t\titdir = m->ofs_skel_tvect;\n\t\t\t}\n#ifdef NONSKELETALMODELS\n\t\t\telse if (m->numanimations && m->ofsanimations->numposes)\n\t\t\t{\n\t\t\t\tinorm = m->ofsanimations->poseofs->ofsnormals;\n\t\t\t\tisdir = m->ofsanimations->poseofs->ofssvector;\n\t\t\t\titdir = m->ofsanimations->poseofs->ofstvector;\n\t\t\t}\n#endif\n\t\t\telse\n\t\t\t{\n\t\t\t\tinorm = NULL;\n\t\t\t\tisdir = NULL;\n\t\t\t\titdir = NULL;\n\t\t\t}\n\t\t\tif (otang)\n\t\t\t{\n\t\t\t\tif (inorm && isdir && itdir)\n\t\t\t\t{\n\t\t\t\t\tfor (i = 0; i < omesh->num_vertexes; i++)\n\t\t\t\t\t{\n\t\t\t\t\t\tVectorCopy (isdir[i], otang[i]);\n\t\t\t\t\t\tCrossProduct_(isdir[i], inorm[i], t);\n\t\t\t\t\t\totang[i][3] = DotProduct(itdir[i], t)<0;\t//fourth part is simply a flag that says which direction the bitangent is in, should otherwise be a nice crossproduct result.\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tfor (i = 0; i < omesh->num_vertexes; i++)\n\t\t\t\t\t{\n\t\t\t\t\t\tVectorClear (otang[i]);\n\t\t\t\t\t\totang[i][3] = 0;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\totang += i;\n\t\t\t}\n\t\t\tif (inorm)\n\t\t\t{\n\t\t\t\tfor (i = 0; i < omesh->num_vertexes; i++)\n\t\t\t\t\tVectorCopy (ivert[i], onorm[i]);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tfor (i = 0; i < omesh->num_vertexes; i++)\n\t\t\t\t\tVectorClear (onorm[i]);\n\t\t\t}\n\t\t\totang += i;\n\t\t\tonorm += i;\n\t\t}\n\n\t\tfor (i = 0; i < omesh->num_triangles; i++)\n\t\t{\n\t\t\totri[i].vertex[0] = m->ofs_indexes[i*3+0]+hdr.num_vertexes;\n\t\t\totri[i].vertex[1] = m->ofs_indexes[i*3+1]+hdr.num_vertexes;\n\t\t\totri[i].vertex[2] = m->ofs_indexes[i*3+2]+hdr.num_vertexes;\n\t\t}\n\t\totri += i;\n\n\t\thdr.num_vertexes += omesh->num_vertexes;\n\t\thdr.num_triangles += omesh->num_triangles;\n\t}\n\n\t//and write it out\n\tf = filefuncs->OpenVFS(fname, \"wb\", FS_GAMEONLY);\n\tif (f)\n\t{\n\t\tVFS_WRITE(f, oh, oh->filesize);\n\t\tVFS_CLOSE(f);\n\t}\n\tfree(data);\n\tfree(poseinfo);\n#undef hdr\n}\n#endif\n"
  },
  {
    "path": "plugins/models/gltf.c",
    "content": "#if !defined(GLQUAKE) && !defined(FTEENGINE)\r\n#define GLQUAKE\t//this is shit, but ensures index sizes come out the right size\r\n#endif\r\n#ifdef IQMTOOL\r\n#define FTEPLUGIN\r\n#endif\r\n#include \"../plugin.h\"\r\n#include \"com_mesh.h\"\r\n\r\n#ifdef IQMTOOL\r\n#define Con_Printf printf\r\n#define Con_DPrintf printf\r\n\r\n#undef CON_WARNING\r\n#define CON_WARNING\r\n#endif\r\n\r\n#ifdef SKELETALMODELS\r\n#define GLTFMODELS\r\n#endif\r\n\r\n/*Limitations:\r\n\tmaterials:\r\n\t\tmaterial names (when present) are assumed to be either globally unique, or the material attributes must match those of all other materials with the same name.\r\n\t\ttexture modes (like clamp) must match on both axis (either both clamp or both wrap, no mixing)\r\n\t\tmirrored-repeat not supported\r\n\t\tmip-mag-mip filters must match (all linear, or all nearest)\r\n\t\tgltf1: techniques are disabled by default.\r\n\t\tgltf1: symbol names are linked via defines. this results in potential conflicts.\r\n\t\tgltf1: static uniforms are not set.\r\n\tanimations:\r\n\t\tinput framerates are not well-defined. this can result in issues when converting to other formats (especially with stepping anims). this does not necessarily affect directly-loaded animations.\r\n\t\ttotal nodes(+joints) must be < MAX_BONES, and ideally <MAX_GPU_BONES too but it is sufficient for that to be per-mesh.\r\n\tmeshes:\r\n\t\tmultiple texture coord sets are not supported.\r\n\t\tadditional colours/weights attributes are not supported.\r\n\t\tmultiple meshes with the same material will not be merged.\r\n\tscene:\r\n\t\tcameras are parsed, but are not necessarily useful as they are not exposed to the gamecode.\r\n\textensions:\r\n\t\tno KHR_draco_mesh_compression, so many sample models will fail.\r\n\t\tunknown extensions will result in warning spam for each occurence. :(\r\n*/\r\n\r\n\r\n//'The units for all linear distances are meters.'\r\n//'feh: 1 metre is approx. 26.24671916 qu.'\r\n//if the player is 1.6m tall, and the player's model is around 48qu, then 1m=30qu, which is a slightly nicer number to work with, and 1qu is a really poorly defined unit.\r\n\r\n#ifdef GLTFMODELS\r\nstatic plugmodfuncs_t *modfuncs;\r\nstatic plugfsfuncs_t *filefuncs;\r\n\r\nstatic cvar_t *mod_gltf_loop;\r\nstatic cvar_t *mod_gltf_scale;\r\nstatic cvar_t *mod_gltf_fixbuggyanims;\r\nstatic cvar_t *mod_gltf_privatematerials;\r\nstatic cvar_t *mod_gltf_ignoretechniques;\r\nstatic cvar_t *mod_gltf_standardorientation;\r\n\r\n#include <stdarg.h>\r\nvoid VARGS Q_snprintfcat (char *dest, size_t size, const char *fmt, ...)\r\n{\r\n\tva_list\t\targptr;\r\n\r\n\tsize_t skip = strlen(dest);\r\n\tdest += skip;\r\n\tsize -= skip;\r\n\r\n\tva_start (argptr, fmt);\r\n\tvsnprintf(dest, size, fmt, argptr);\r\n\tva_end (argptr);\r\n}\r\n\r\n\r\n\r\nstatic void JSON_GetPath(json_t *t, qboolean ignoreroot, char *buffer, size_t buffersize)\r\n{\r\n\tif (t->parent && (t->parent->parent || !ignoreroot))\r\n\t{\r\n\t\tJSON_GetPath(t->parent, ignoreroot, buffer, buffersize);\r\n\t\tQ_strlcat(buffer, \".\", buffersize);\r\n\t}\r\n\tQ_strlcat(buffer, t->name, buffersize);\r\n}\r\nstatic void JSON_WarnUnused(json_t *t, int *warnlimit)\r\n{\r\n\tif (!t)\r\n\t\treturn;\r\n\tif (t->used)\r\n\t{\r\n\t\tfor (t = t->child; t; t = t->sibling)\r\n\t\t\tJSON_WarnUnused(t, warnlimit);\r\n\t}\r\n\telse\r\n\t{\r\n\t\tchar path[8192];\r\n\t\t*path = 0;\r\n\t\tJSON_GetPath(t, false, path, sizeof(path));\r\n\t\tif ((*warnlimit) --> 0)\r\n\t\t\tCon_DPrintf(CON_WARNING\"GLTF property %s was not used\\n\", path);\r\n\t}\r\n}\r\nstatic void JSON_FlagAsUsed(json_t *t, const char *child)\r\n{\r\n\tif (child)\r\n\t{\r\n\t\tt = JSON_FindChild(t, child);\r\n\t\tif (!t)\r\n\t\t\treturn;\r\n\t}\r\n\tt->used = true;\r\n\tfor (t = t->child; t; t = t->sibling)\r\n\t\tJSON_FlagAsUsed(t, NULL);\r\n}\r\nstatic void JSON_WarnIfChild(json_t *t, const char *child, int *warnlimit)\r\n{\r\n\tt = JSON_FindChild(t, child);\r\n\tif (t)\r\n\t{\r\n\t\tchar path[8192];\r\n\t\t*path = 0;\r\n\t\tJSON_GetPath(t, false, path, sizeof(path));\r\n\t\tif ((*warnlimit) --> 0)\r\n\t\t\tCon_Printf(CON_WARNING\"Standard feature %s is not supported\\n\", path);\r\n\t\tJSON_FlagAsUsed(t, NULL);\r\n\t}\r\n}\r\n\r\nstatic unsigned int FromBase64(char c)\r\n{\r\n\tif (c >= 'A' && c <= 'Z')\r\n\t\treturn 0+(c-'A');\r\n\tif (c >= 'a' && c <= 'z')\r\n\t\treturn 26+(c-'a');\r\n\tif (c >= '0' && c <= '9')\r\n\t\treturn 52+(c-'0');\r\n\tif (c == '+')\r\n\t\treturn 62;\r\n\tif (c == '/')\r\n\t\treturn 63;\r\n\treturn 64;\t//'=' for no-more/padding.\r\n}\r\n//fancy parsing of content. NOTE: doesn't bother to handle escape codes, which shouldn't be present (\\u for ascii chars is horribly wasteful).\r\nstatic void *JSON_MallocDataURI(json_t *t, size_t *outlen)\r\n{\r\n\tsize_t bl = t->bodyend-t->bodystart;\r\n\tif (bl >= 5 && !strncmp(t->bodystart, \"data:\", 5))\r\n\t{\r\n\t\tconst char *mimestart = t->bodystart+5;\r\n\t\tconst char *mimeend;\r\n\t\tconst char *encstart;\r\n\t\tconst char *encend;\r\n\t\tconst char *in;\r\n\t\tchar *out, *r;\r\n\r\n\t\tfor (mimeend = mimestart; *mimeend && mimeend < t->bodyend; mimeend++)\r\n\t\t{\r\n\t\t\tif (*mimeend == ';')\t//start of encoding\r\n\t\t\t\tbreak;\r\n\t\t\tif (*mimeend == ',')\t//start of data\r\n\t\t\t\tbreak;\r\n\t\t}\r\n\t\tif (*mimeend == ';')\r\n\t\t{\r\n\t\t\tfor (encend = encstart = mimeend+1; *encend && encend < t->bodyend; encend++)\r\n\t\t\t{\r\n\t\t\t\tif (*encend == ',')\t//start of data\r\n\t\t\t\t\tbreak;\r\n\t\t\t}\r\n\t\t}\r\n\t\telse\r\n\t\t\tencstart = encend = mimeend;\r\n\r\n\t\tif (*encend == ',' && encend < t->bodyend)\r\n\t\t{\r\n\t\t\tin = encend+1;\r\n\t\t\tif (encend-encstart == 6 && !strncmp(encstart, \"base64\", 6))\r\n\t\t\t{\r\n\t\t\t\t//base64\r\n\t\t\t\tr = out = malloc(((t->bodyend-in)*3)/4 + 1);\r\n\t\t\t\twhile (in+3 < t->bodyend)\r\n\t\t\t\t{\r\n\t\t\t\t\tunsigned int c1, c2, c3, c4;\r\n\t\t\t\t\tc1 = FromBase64(*in++);\r\n\t\t\t\t\tc2 = FromBase64(*in++);\r\n\t\t\t\t\tif (c1 >= 64 || c2 >= 64)\r\n\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t*out++ = (c1<<2) | (c2>>4);\r\n\t\t\t\t\tc3 = FromBase64(*in++);\r\n\t\t\t\t\tif (c3 >= 64)\r\n\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t*out++ = (c2<<4) | (c3>>2);\r\n\t\t\t\t\tc4 = FromBase64(*in++);\r\n\t\t\t\t\tif (c4 >= 64)\r\n\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t*out++ = (c3<<6) | (c4>>0);\r\n\t\t\t\t}\r\n\t\t\t\t*outlen = out-r;\r\n\t\t\t\t*out = 0;\r\n\t\t\t\treturn r;\r\n\t\t\t}\r\n\t\t\telse if (encend == encstart)\r\n\t\t\t{\t//url encoding. yuck, sod off.\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\treturn NULL;\r\n}\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n//glTF 1.0 and 2.0 differ in that 1 uses names and 2 uses indexes. There's also some significant differences with materials.\r\n//we only support 2.0\r\n\r\n//buffers are raw blobs that can come from multiple different sources\r\nstruct gltf_buffer\r\n{\r\n\tqboolean loaded;\r\n\tqboolean malloced;\r\n\tvoid *data;\r\n\tsize_t length;\r\n};\r\nstruct galiasbone_gltf_s\r\n{\t//stored in galiasinfo_t->ctx\r\n\tdouble rmatrix[16];\t\t\t//gah\r\n\tdouble quat[4], scale[3], trans[3];\t//annoying smeg\r\n};\r\ntypedef struct gltf_s\r\n{\r\n\tstruct model_s *mod;\r\n\tunsigned int numsurfaces;\r\n\tjson_t *r;\r\n\tint ver;\r\n\r\n\tunsigned int variations;\r\n\r\n\tint *bonemap;//[MAX_BONES];\t//remap skinned bones. I hate that we have to do this.\r\n\tstruct gltfbone_s\r\n\t{\r\n\t\tchar name[64];\r\n\t\tchar jointname[64];\t//gltf1 only\r\n\t\tint parent;\r\n\t\tint camera;\r\n\t\tdouble amatrix[16];\r\n\t\tdouble inverse[16];\r\n\t\tstruct galiasbone_gltf_s rel;\r\n\r\n\t\tstruct {\r\n\t\t\tstruct gltf_accessor *input;\r\n\t\t\tstruct gltf_accessor *output;\r\n\t\t} *rot, *scale, *translation;\r\n\t} *bones;//[MAX_BONES];\r\n\tunsigned int numbones;\r\n\r\n\tint warnlimit;\t//don't spam warnings. this is a loader, not a spammer\r\n\r\n\tstruct gltf_buffer buffers[64];\r\n} gltf_t;\r\n\r\nstatic void GLTF_FlagExtras(json_t *node)\r\n{\r\n\tJSON_FindChild(node, \"extensions\");\t//warn about child extensions, but not the extensions table itself.\r\n\tJSON_FlagAsUsed(node, \"extras\");\t//don't warn about application-specific extras\r\n}\r\n\r\nstatic json_t *GLTF_FindJSONIDParent(struct gltf_s *gltf, json_t *parent, json_t *id, quintptr_t *idx)\r\n{\r\n\tif (gltf->ver == 1)\r\n\t{\t//gltf1 uses string-based names\r\n\t\tchar name[64];\r\n\t\tJSON_ReadBody(id, name, sizeof(name));\r\n\t\tid = parent;\r\n\t\tif (idx)\r\n\t\t\t*idx = 0;\r\n\t\tif (id)\r\n\t\tfor (id = id->child; id; id = id->sibling)\r\n\t\t{\r\n\t\t\tif (!strcmp(id->name, name))\r\n\t\t\t{\r\n\t\t\t\tid->used = true;\r\n\t\t\t\treturn id;\r\n\t\t\t}\r\n\t\t\tif (idx)\r\n\t\t\t\t*idx += 1;\r\n\t\t}\r\n\t\treturn NULL;\r\n\t}\r\n\telse\r\n\t{\t//gltf2 uses array indexes.\r\n\t\tquintptr_t num;\r\n\t\tnum = id?JSON_GetInteger(id, NULL, -1):-1;\r\n\t\tif (idx)\r\n\t\t\t*idx = num;\r\n\t\treturn JSON_FindIndexedChild(parent, NULL, num);\r\n\t}\r\n}\r\nstatic json_t *GLTF_FindJSONID(struct gltf_s *gltf, const char *restype, json_t *id, quintptr_t *idx)\r\n{\t//if there's no scene info, treat it as \"-1\"\r\n\treturn GLTF_FindJSONIDParent(gltf, JSON_FindChild(gltf->r, restype), id, idx);\r\n}\r\nstatic json_t *GLTF_FindJSONID_First(struct gltf_s *gltf, const char *restype, json_t *id, quintptr_t *idx)\r\n{\t//if there's no scene info, treat it as \"0\"\r\n\r\n\tif (!id && gltf->ver > 1)\r\n\t\treturn JSON_FindIndexedChild(gltf->r, restype, 0);\r\n\treturn GLTF_FindJSONIDParent(gltf, JSON_FindChild(gltf->r, restype), id, idx);\r\n}\r\n\r\nstatic int dehex(int i)\r\n{\r\n\tif      (i >= '0' && i <= '9')\r\n\t\treturn (i-'0');\r\n\telse if (i >= 'A' && i <= 'F')\r\n\t\treturn (i-'A'+10);\r\n\telse if (i >= 'a' && i <= 'f')\r\n\t\treturn (i-'a'+10);\r\n\telse\r\n\t\treturn -1;\r\n}\r\n\r\nstatic void GLTF_RelativePath(const char *base, const char *relative, char *out, size_t outsize)\r\n{\r\n\tchar *out_start = out;\r\n\tsize_t t;\r\n\tconst char *sep;\r\n\tconst char *end = base;\r\n\tif (*relative == '/')\r\n\t{\r\n\t\trelative++;\r\n\t}\r\n\telse\r\n\t{\r\n\t\tfor (sep = end; *sep; sep++)\r\n\t\t{\r\n\t\t\tif (*sep == '/' || *sep == '\\\\')\r\n\t\t\t\tend = sep+1;\r\n\t\t}\r\n\t}\r\n\twhile (!strncmp(relative, \"../\", 3))\r\n\t{\r\n\t\tif (end > base)\r\n\t\t{\r\n\t\t\tend--;\r\n\t\t\twhile (end > base)\r\n\t\t\t{\r\n\t\t\t\tend--;\r\n\t\t\t\tif (*end == '/' || *end == '\\\\')\r\n\t\t\t\t{\r\n\t\t\t\t\trelative += 3;\r\n\t\t\t\t\tend++;\r\n\t\t\t\t\tbreak;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t\telse\r\n\t\t\tbreak;\r\n\t}\r\n\toutsize--;\t//for the null\r\n\r\n\tt = end-base;\r\n\tif (t > outsize)\r\n\t\tt = outsize;\r\n\tmemcpy(out, base, t);\r\n\tout += t;\r\n\toutsize -= t;\r\n\r\n\tfor (; *relative && outsize; outsize--)\r\n\t{\r\n\t\tif (*relative == '%' && ((out-out_start>=7 && !strncmp(out_start, \"http://\", 7)) || (out-out_start>=8 && !strncmp(out_start, \"https://\", 8))))\r\n\t\t{\r\n\t\t\tint high, low;\r\n\t\t\tchar b = *relative++;\r\n\t\t\thigh = dehex(relative[0]);\r\n\t\t\tif (high >= 0)\r\n\t\t\t{\r\n\t\t\t\tlow = dehex(relative[1]);\r\n\t\t\t\tif (low >= 0 && (high || low))\r\n\t\t\t\t{\r\n\t\t\t\t\trelative += 2;\r\n\t\t\t\t\tb = (high<<4) | low;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\t*out++ = b;\r\n\t\t}\r\n\t\telse\r\n\t\t\t*out++ = *relative++;\r\n\t}\r\n\r\n\t*out = 0;\r\n}\r\n\r\nstatic struct gltf_buffer *GLTF_GetBufferData(gltf_t *gltf, json_t *bufferid)\r\n{\r\n\tquintptr_t bufferidx;\r\n\tjson_t *b = GLTF_FindJSONID(gltf, \"buffers\", bufferid, &bufferidx);\r\n\tjson_t *uri = JSON_FindChild(b, \"uri\");\r\n\tsize_t length = JSON_GetUInteger(b, \"byteLength\", 0);\r\n\tstruct gltf_buffer *out;\r\n\r\n\tif (gltf->ver <= 1)\r\n\t{\r\n\t\tchar body[64];\r\n\t\tJSON_ReadBody(bufferid, body, sizeof(body));\r\n\t\tif (!strcmp(body, \"binary_glTF\") && !gltf->buffers[0].malloced && gltf->buffers[0].data)\r\n\t\t\treturn &gltf->buffers[0];\r\n\t\telse\r\n\t\t\tbufferidx++;\r\n\r\n\t\tJSON_FlagAsUsed(b, \"type\");\t//default: \"arraybuffer\"... yeah, not relevant to anything.\r\n\t}\r\n\tJSON_FlagAsUsed(b, \"name\");\r\n\tGLTF_FlagExtras(b);\r\n\r\n\r\n\tif (bufferidx >= countof(gltf->buffers))\r\n\t\treturn NULL;\r\n\tout = &gltf->buffers[bufferidx];\r\n\r\n\t//we may have been through here before...\r\n\tif (out->loaded)\r\n\t\treturn out->data?out:NULL;\r\n\tout->loaded = true;\r\n\r\n\tif (uri)\r\n\t{\r\n\t\tout->malloced = true;\r\n\t\tout->data = JSON_MallocDataURI(uri, &out->length);\r\n\t\tif (!out->data)\r\n\t\t{\r\n\t\t\t//read a file from disk.\r\n\t\t\tvfsfile_t *f;\r\n\t\t\tchar uritext[MAX_QPATH];\r\n\t\t\tchar filename[MAX_QPATH];\r\n\t\t\tJSON_ReadBody(uri, uritext, sizeof(uritext));\r\n\t\t\tGLTF_RelativePath(gltf->mod->name, uritext, filename, sizeof(filename));\r\n\t\t\tf = filefuncs->OpenVFS(filename, \"rb\", FS_GAME);\r\n\t\t\tif (f)\r\n\t\t\t{\r\n\t\t\t\tout->length = VFS_GETLEN(f);\r\n\t\t\t\tout->length = min(out->length, length);\r\n\t\t\t\tout->data = malloc(length);\r\n\t\t\t\tVFS_READ(f, out->data, length);\r\n\t\t\t\tVFS_CLOSE(f);\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t\tCon_Printf(CON_WARNING\"%s: Unable to read buffer file %s\\n\", gltf->mod->name, filename);\r\n\t\t}\r\n\t}\r\n\treturn out->data?out:NULL;\r\n}\r\n//buffer views are aka VBOs. each has its own VBO data type (vbo/ebo), and can be uploaded as-is.\r\nstruct gltf_bufferview\r\n{\r\n\tvoid *data;\r\n\tsize_t length;\r\n\tint bytestride;\r\n};\r\nstatic qboolean GLTF_GetBufferViewData(gltf_t *gltf, json_t *bufferviewid, struct gltf_bufferview *view)\r\n{\r\n\tstruct gltf_buffer *buf;\r\n\tjson_t *bv = GLTF_FindJSONID(gltf, \"bufferViews\", bufferviewid, NULL);\r\n\tsize_t offset;\r\n\tif (!bv)\r\n\t\treturn false;\r\n\r\n\tbuf = GLTF_GetBufferData(gltf, JSON_FindChild(bv, \"buffer\"));\r\n\tif (!buf)\r\n\t\treturn false;\r\n\toffset = JSON_GetUInteger(bv, \"byteOffset\", 0);\r\n\tview->data = (char*)buf->data + offset;\r\n\tview->length = JSON_GetUInteger(bv, \"byteLength\", 0);\t//required\r\n\tview->bytestride = (gltf->ver<=1)?0:JSON_GetInteger(bv, \"byteStride\", 0);\r\n\tif (offset + view->length > buf->length)\r\n\t\treturn false;\r\n\r\n\tJSON_FlagAsUsed(bv, \"target\");\t//required, but not useful for us.\r\n\tJSON_FlagAsUsed(bv, \"name\");\r\n\tGLTF_FlagExtras(bv);\r\n\treturn true;\r\n}\r\n\r\nstatic void GLTF_ExpandSparse(gltf_t *gltf, json_t *sparse, qbyte *data, unsigned int databytes, unsigned int datastride)\r\n{\r\n\tqbyte *out;\r\n\tsize_t count = JSON_GetUInteger(sparse, \"count\", 0);\r\n\tjson_t *indices = JSON_FindChild(sparse, \"indices\");\r\n\tstruct gltf_bufferview idxv;\r\n\tsize_t idxofs = JSON_GetUInteger(indices, \"byteOffset\", 0);\r\n\tint idxctype = JSON_GetUInteger(indices, \"componentType\", 0);\r\n\tjson_t *values = JSON_FindChild(sparse, \"values\");\r\n\tstruct gltf_bufferview valv;\r\n\tsize_t valofs = JSON_GetUInteger(values, \"byteOffset\", 0);\r\n\tsize_t idxstride;\r\n\r\n\tif (!GLTF_GetBufferViewData(gltf, JSON_FindChild(indices, \"bufferView\"), &idxv))\r\n\t\treturn;\r\n\tif (!GLTF_GetBufferViewData(gltf, JSON_FindChild(values, \"bufferView\"), &valv))\r\n\t\treturn;\r\n\r\n\tswitch(idxctype)\r\n\t{\r\n\tdefault:\r\n\t\tif (gltf->warnlimit --> 0)\r\n\t\t\tCon_Printf(CON_WARNING\"GLTF_ExpandSparse: %s: glTF2 unsupported index componentType (%i)\\n\", gltf->mod->name, idxctype);\r\n\t\treturn;\r\n\tcase 5120:\t//BYTE. signed values are bugs, but allow anyway.\r\n\tcase 5121:\t//UNSIGNED_BYTE\r\n\t\tidxstride = 1;\r\n\t\tbreak;\r\n\tcase 5122: //SHORT. signed values are bugs, but allow anyway.\r\n\tcase 5123: //UNSIGNED_SHORT\r\n\t\tidxstride = 2;\r\n\t\tbreak;\r\n\tcase 5124: //INT. signed values are bugs, but allow anyway.\r\n\tcase 5125: //UNSIGNED_INT\r\n\t\tidxstride = 4;\r\n\t\tbreak;\r\n//\tcase 5126: //FLOAT. doesn't make sense as an index.\r\n\t}\r\n\r\n\tif (idxstride*count > idxv.length - idxofs || idxofs > idxv.length)\r\n\t{\r\n\t\tif (gltf->warnlimit --> 0)\r\n\t\t\tCon_Printf(CON_WARNING\"GLTF_ExpandSparse: %s: sparse indexes violates bufferView\\n\", gltf->mod->name);\r\n\t\treturn;\r\n\t}\r\n\tif (datastride*count > valv.length - valofs || valofs > valv.length)\r\n\t{\r\n\t\tif (gltf->warnlimit --> 0)\r\n\t\t\tCon_Printf(CON_WARNING\"GLTF_ExpandSparse: %s: sparse values violates bufferView\\n\", gltf->mod->name);\r\n\t\treturn;\r\n\t}\r\n\tidxv.data = (qbyte*)idxv.data + idxofs;\r\n\tvalv.data = (qbyte*)valv.data + valofs;\r\n\r\n\twhile(count --> 0)\r\n\t{\r\n\t\tswitch(idxstride)\r\n\t\t{\r\n\t\tdefault: out = data; break;\r\n\t\tcase 1:\tout = data + datastride * *(unsigned char *)idxv.data; break;\r\n\t\tcase 2:\tout = data + datastride * *(unsigned short*)idxv.data; break;\r\n\t\tcase 4: out = data + datastride * *(unsigned int  *)idxv.data; break;\r\n\t\t}\r\n\t\tidxv.data = (char*)idxv.data + idxstride;\r\n\r\n\t\t//out is meant to be higher each time, but we don't bother verifying that specifically.\r\n\t\tif (out < data || out+datastride > data+databytes)\r\n\t\t\t;\t//don't write out of bounds.\r\n\t\telse\r\n\t\t\tmemcpy(out, valv.data, datastride);\r\n\t\tvalv.data = (char*)valv.data + datastride;\r\n\t}\r\n}\r\n\r\n//accessors are basically VAs blocks that refer inside a bufferview/VBO.\r\nstruct gltf_accessor\r\n{\r\n\tvoid *data;\r\n\tsize_t length;\r\n\tsize_t bytestride;\r\n\r\n\tint componentType;\t\t//5120 BYTE, 5121 UNSIGNED_BYTE, 5122 SHORT, 5123 UNSIGNED_SHORT, 5125 UNSIGNED_INT, 5126 FLOAT\r\n\tqboolean normalized;\r\n\tsize_t count;\r\n\tint type;\t//1,2,3,4 says component count, 256|(4,9,16) for square matricies...\r\n\r\n\tdouble mins[16];\r\n\tdouble maxs[16];\r\n};\r\nstatic qboolean GLTF_GetAccessor(gltf_t *gltf, json_t *accessorid, struct gltf_accessor *out)\r\n{\r\n\tstruct gltf_bufferview bv;\r\n\tjson_t *a, *mins, *maxs, *sparse;\r\n\tsize_t offset;\r\n\tint j;\r\n\tmemset(out, 0, sizeof(*out));\r\n\r\n\ta = GLTF_FindJSONID(gltf, \"accessors\", accessorid, NULL);\r\n\tif (!a)\r\n\t\treturn false;\r\n\r\n\tJSON_FlagAsUsed(a, \"name\");\r\n\r\n\tif (!GLTF_GetBufferViewData(gltf, JSON_FindChild(a, \"bufferView\"), &bv))\r\n\t{\t//when this is omitted, the data is 0-filled, with sparse stuff overwriting it.\r\n\t\tmemset(&bv, 0, sizeof(bv));\r\n\t\toffset = 0;\r\n\t}\r\n\telse\r\n\t{\r\n\t\toffset = JSON_GetUInteger(a, \"byteOffset\", 0);\r\n\t\tif (offset > bv.length)\r\n\t\t{\r\n\t\t\tif (gltf->warnlimit --> 0)\r\n\t\t\t\tCon_Printf(CON_WARNING\"%s: byteOffset+bufferView.length beyond buffer size\\n\", gltf->mod->name);\r\n\t\t\treturn false;\r\n\t\t}\r\n\t}\r\n\r\n\tif (gltf->ver <= 1)\r\n\t\tout->bytestride = JSON_GetInteger(a, \"byteStride\", 0);\r\n\telse\r\n\t\tout->bytestride = bv.bytestride;\r\n\tout->componentType = JSON_GetInteger(a, \"componentType\", 0);\r\n\tout->normalized = JSON_GetInteger(a, \"normalized\", false);\r\n\tout->count = JSON_GetInteger(a, \"count\", 0);\r\n\tif (JSON_Equals(a, \"type\", \"SCALAR\"))\r\n\t\tout->type = (1<<8) | 1;\r\n\telse if (JSON_Equals(a, \"type\", \"VEC2\"))\r\n\t\tout->type = (1<<8) | 2;\r\n\telse if (JSON_Equals(a, \"type\", \"VEC3\"))\r\n\t\tout->type = (1<<8) | 3;\r\n\telse if (JSON_Equals(a, \"type\", \"VEC4\"))\r\n\t\tout->type = (1<<8) | 4;\r\n\telse if (JSON_Equals(a, \"type\", \"MAT2\"))\r\n\t\tout->type = (2<<8) | 2;\r\n\telse if (JSON_Equals(a, \"type\", \"MAT3\"))\r\n\t\tout->type = (3<<8) | 3;\r\n\telse if (JSON_Equals(a, \"type\", \"MAT4\"))\r\n\t\tout->type = (4<<8) | 4;\r\n\telse\r\n\t{\r\n\t\tif (gltf->warnlimit --> 0)\r\n\t\t\tCon_Printf(CON_WARNING\"%s: glTF2 unsupported type\\n\", gltf->mod->name);\r\n\t\tout->type = 1;\r\n\t}\r\n\r\n\tif (!out->bytestride)\r\n\t{\r\n\t\tout->bytestride = (out->type & 0xff) * (out->type>>8);\r\n\t\tswitch(out->componentType)\r\n\t\t{\r\n\t\tdefault:\r\n\t\t\tif (gltf->warnlimit --> 0)\r\n\t\t\t\tCon_Printf(CON_WARNING\"GLTF_GetAccessor: %s: glTF2 unsupported componentType (%i)\\n\", gltf->mod->name, out->componentType);\r\n\t\tcase 5120:\t//BYTE\r\n\t\tcase 5121:\t//UNSIGNED_BYTE\r\n\t\t\tbreak;\r\n\t\tcase 5122: //SHORT\r\n\t\tcase 5123: //UNSIGNED_SHORT\r\n\t\t\tout->bytestride *= 2;\r\n\t\t\tbreak;\r\n\t\tcase 5125: //UNSIGNED_INT\r\n\t\tcase 5126: //FLOAT\r\n\t\t\tout->bytestride *= 4;\r\n\t\t\tbreak;\r\n\t\t}\r\n\t}\r\n\r\n\tif (bv.data)\r\n\t{\t//we had a proper bufferView.\r\n\t\tout->length = bv.length - offset;\r\n\t\tout->data = (char*)bv.data + offset;\r\n\t}\r\n\telse\r\n\t{\t//0-filled.\r\n\t\tout->length = out->bytestride * out->count;\r\n\t\tout->data = NULL;\r\n\t}\r\n\r\n\tsparse = JSON_FindChild(a, \"sparse\");\r\n\tif (sparse)\r\n\t{\t//0-initialised, with separate index+values tables for ones that need an actual value.\r\n\r\n\t\tif (out->data)\r\n\t\t{\t//we actually had some existing data... we're stomping over only parts of it, but make sure we don't corrupt anything else using the same bit of buffer.\r\n\t\t\tvoid *old = out->data;\r\n\t\t\tout->data = plugfuncs->GMalloc(&gltf->mod->memgroup, out->length);\r\n\t\t\tmemcpy(out->data, old, out->length);\r\n\t\t}\r\n\t\telse\t//okay, was 0 filled. just malloc some mem to use.\r\n\t\t\tout->data = plugfuncs->GMalloc(&gltf->mod->memgroup, out->length);\r\n\r\n\t\tGLTF_ExpandSparse(gltf, sparse, out->data, out->length, out->bytestride);\r\n\t}\r\n\telse\r\n\t{\t//just nothing...\r\n\t\tif (!out->data)\r\n\t\t\tout->data = plugfuncs->GMalloc(&gltf->mod->memgroup, out->length);\r\n\t}\r\n\r\n\tmins = JSON_FindChild(a, \"min\");\r\n\tmaxs = JSON_FindChild(a, \"max\");\r\n\tfor (j = 0; j < (out->type>>8)*(out->type&0xff); j++)\r\n\t{\t//'must' be set in various situations.\r\n\t\tout->mins[j] = JSON_GetIndexedFloat(mins, j, 0);\r\n\t\tout->maxs[j] = JSON_GetIndexedFloat(maxs, j, 0);\r\n\t}\r\n\r\n//\tJSON_WarnIfChild(a, \"name\");\r\n\tGLTF_FlagExtras(a);\r\n\r\n\treturn true;\r\n}\r\n\r\nstatic void GLTF_AccessorToTangents(gltf_t *gltf, const vec3_t *norm, size_t outverts, const struct gltf_accessor *a, vec3_t *sdir, vec3_t *tdir)\r\n{\t//input MUST be a single float4\r\n\t//output is two vec3s. wasteful perhaps.\r\n\tvec3_t *os = sdir;\r\n\tvec3_t *ot = tdir;\r\n\tchar *in = a->data;\r\n\r\n\tsize_t v, c;\r\n\tfloat side;\r\n\tif ((a->type&0xff) != 4)\r\n\t\treturn;\r\n\tswitch(a->componentType)\r\n\t{\r\n\tdefault:\r\n\t\tif (gltf->warnlimit --> 0)\r\n\t\t\tCon_Printf(CON_WARNING\"GLTF_AccessorToTangents: %s: glTF2 unsupported componentType (%i)\\n\", gltf->mod->name, a->componentType);\r\n\tcase 0:\r\n\t\tmemset(os, 0, sizeof(*os) * outverts);\r\n\t\tmemset(ot, 0, sizeof(*ot) * outverts);\r\n\t\tbreak;\r\n\tcase 5120: //BYTE\tKHR_mesh_quantization (always normalized)\r\n\t\tfor (v = 0; v < outverts; v++)\r\n\t\t{\r\n\t\t\tfor (c = 0; c < 3; c++)\r\n\t\t\t\tos[v][c] = max(-1.0, ((signed char*)in)[c] / 127.0);\t//negative values are larger, but we want to allow 1.0\r\n\t\t\tside = max(-1.0, ((signed char*)in)[3] / 127.0);\r\n\r\n\t\t\t//bitangent = cross(normal, tangent.xyz) * tangent.w\r\n\t\t\tot[v][0] = (norm[v][1]*os[v][2] - norm[v][2]*os[v][1]) * side;\r\n\t\t\tot[v][1] = (norm[v][2]*os[v][0] - norm[v][0]*os[v][2]) * side;\r\n\t\t\tot[v][2] = (norm[v][0]*os[v][1] - norm[v][1]*os[v][0]) * side;\r\n\r\n\t\t\tin += a->bytestride;\r\n\t\t}\r\n\t\tbreak;\r\n//\tcase 5121: //UNSIGNED_BYTE\r\n\tcase 5122: //SHORT\tKHR_mesh_quantization (always normalized)\r\n\t\tfor (v = 0; v < outverts; v++)\r\n\t\t{\r\n\t\t\tfor (c = 0; c < 3; c++)\r\n\t\t\t\tos[v][c] = max(-1.0, ((signed short*)in)[c] / 32767.0);\r\n\t\t\tside = max(-1.0, ((signed short*)in)[3] / 32767.0);\r\n\r\n\t\t\t//bitangent = cross(normal, tangent.xyz) * tangent.w\r\n\t\t\tot[v][0] = (norm[v][1]*os[v][2] - norm[v][2]*os[v][1]) * side;\r\n\t\t\tot[v][1] = (norm[v][2]*os[v][0] - norm[v][0]*os[v][2]) * side;\r\n\t\t\tot[v][2] = (norm[v][0]*os[v][1] - norm[v][1]*os[v][0]) * side;\r\n\r\n\t\t\tin += a->bytestride;\r\n\t\t}\r\n\t\tbreak;\r\n//\tcase 5123: //UNSIGNED_SHORT\r\n//\tcase 5125: //UNSIGNED_INT\r\n\tcase 5126: //FLOAT\r\n\t\tfor (v = 0; v < outverts; v++)\r\n\t\t{\r\n\t\t\tfor (c = 0; c < 3; c++)\r\n\t\t\t\tos[v][c] = ((float*)in)[c];\r\n\t\t\tside = ((float*)in)[3];\r\n\r\n\t\t\t//bitangent = cross(normal, tangent.xyz) * tangent.w\r\n\t\t\tot[v][0] = (norm[v][1]*os[v][2] - norm[v][2]*os[v][1]) * side;\r\n\t\t\tot[v][1] = (norm[v][2]*os[v][0] - norm[v][0]*os[v][2]) * side;\r\n\t\t\tot[v][2] = (norm[v][0]*os[v][1] - norm[v][1]*os[v][0]) * side;\r\n\r\n\t\t\tin += a->bytestride;\r\n\t\t}\r\n\t\tbreak;\r\n\t}\r\n}\r\n\r\nstatic void *GLTF_AccessorToDataF(gltf_t *gltf, size_t outverts, unsigned int outcomponents, const struct gltf_accessor *a, void *out)\r\n{\r\n\tfloat *ret = out, *o;\r\n\tchar *in = a->data;\r\n\r\n\tsize_t c, ic = a->type&0xff;\r\n\tif (ic > outcomponents)\r\n\t\tic = outcomponents;\r\n\tif (!ret)\r\n\t\tret = plugfuncs->GMalloc(&gltf->mod->memgroup, sizeof(*ret) * outcomponents * outverts);\r\n\to = ret;\r\n\tswitch(a->componentType)\r\n\t{\r\n\tdefault:\r\n\t\tif (gltf->warnlimit --> 0)\r\n\t\t\tCon_Printf(CON_WARNING\"GLTF_AccessorToDataF: %s: glTF2 unsupported componentType (%i)\\n\", gltf->mod->name, a->componentType);\r\n\tcase 0:\r\n\t\tmemset(ret, 0, sizeof(*ret) * outcomponents * outverts);\r\n\t\tbreak;\r\n\tcase 5120:\t//BYTE\r\n\t\tif (!a->normalized)\r\n\t\t{\t//KHR_mesh_quantization\r\n\t\t\twhile(outverts --> 0)\r\n\t\t\t{\r\n\t\t\t\tfor (c = 0; c < ic; c++)\r\n\t\t\t\t\to[c] = ((signed char*)in)[c];\r\n\t\t\t\tfor (; c < outcomponents; c++)\r\n\t\t\t\t\to[c] = 0;\r\n\t\t\t\to += outcomponents;\r\n\t\t\t\tin += a->bytestride;\r\n\t\t\t}\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\twhile(outverts --> 0)\r\n\t\t\t{\r\n\t\t\t\tfor (c = 0; c < ic; c++)\r\n\t\t\t\t\to[c] = max(-1.0, ((signed char*)in)[c] / 127.0);\t//negative values are larger, but we want to allow 1.0\r\n\t\t\t\tfor (; c < outcomponents; c++)\r\n\t\t\t\t\to[c] = 0;\r\n\t\t\t\to += outcomponents;\r\n\t\t\t\tin += a->bytestride;\r\n\t\t\t}\r\n\t\t}\r\n\t\tbreak;\r\n\tcase 5121:\t//UNSIGNED_BYTE\r\n\t\tif (!a->normalized)\r\n\t\t{\t//KHR_mesh_quantization\r\n\t\t\twhile(outverts --> 0)\r\n\t\t\t{\r\n\t\t\t\tfor (c = 0; c < ic; c++)\r\n\t\t\t\t\to[c] = ((unsigned char*)in)[c];\r\n\t\t\t\tfor (; c < outcomponents; c++)\r\n\t\t\t\t\to[c] = 0;\r\n\t\t\t\to += outcomponents;\r\n\t\t\t\tin += a->bytestride;\r\n\t\t\t}\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\twhile(outverts --> 0)\r\n\t\t\t{\r\n\t\t\t\tfor (c = 0; c < ic; c++)\r\n\t\t\t\t\to[c] = ((unsigned char*)in)[c] / 255.0;\r\n\t\t\t\tfor (; c < outcomponents; c++)\r\n\t\t\t\t\to[c] = 0;\r\n\t\t\t\to += outcomponents;\r\n\t\t\t\tin += a->bytestride;\r\n\t\t\t}\r\n\t\t}\r\n\t\tbreak;\r\n\tcase 5122: //SHORT\r\n\t\tif (!a->normalized)\r\n\t\t{\t//KHR_mesh_quantization\r\n\t\t\twhile(outverts --> 0)\r\n\t\t\t{\r\n\t\t\t\tfor (c = 0; c < ic; c++)\r\n\t\t\t\t\to[c] = ((signed short*)in)[c];\r\n\t\t\t\tfor (; c < outcomponents; c++)\r\n\t\t\t\t\to[c] = 0;\r\n\t\t\t\to += outcomponents;\r\n\t\t\t\tin += a->bytestride;\r\n\t\t\t}\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\twhile(outverts --> 0)\r\n\t\t\t{\r\n\t\t\t\tfor (c = 0; c < ic; c++)\r\n\t\t\t\t\to[c] = max(-1.0, ((signed short*)in)[c] / 32767.0);\t//negative values are larger, but we want to allow 1.0\r\n\t\t\t\tfor (; c < outcomponents; c++)\r\n\t\t\t\t\to[c] = 0;\r\n\t\t\t\to += outcomponents;\r\n\t\t\t\tin += a->bytestride;\r\n\t\t\t}\r\n\t\t}\r\n\t\tbreak;\r\n\tcase 5123: //UNSIGNED_SHORT\r\n\t\tif (!a->normalized)\r\n\t\t{\t//KHR_mesh_quantization\r\n\t\t\twhile(outverts --> 0)\r\n\t\t\t{\r\n\t\t\t\tfor (c = 0; c < ic; c++)\r\n\t\t\t\t\to[c] = ((unsigned short*)in)[c];\r\n\t\t\t\tfor (; c < outcomponents; c++)\r\n\t\t\t\t\to[c] = 0;\r\n\t\t\t\to += outcomponents;\r\n\t\t\t\tin += a->bytestride;\r\n\t\t\t}\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\twhile(outverts --> 0)\r\n\t\t\t{\r\n\t\t\t\tfor (c = 0; c < ic; c++)\r\n\t\t\t\t\to[c] = ((unsigned short*)in)[c] / 65535.0;\r\n\t\t\t\tfor (; c < outcomponents; c++)\r\n\t\t\t\t\to[c] = 0;\r\n\t\t\t\to += outcomponents;\r\n\t\t\t\tin += a->bytestride;\r\n\t\t\t}\r\n\t\t}\r\n\t\tbreak;\r\n\tcase 5125: //UNSIGNED_INT\r\n\t\tif (!a->normalized)\r\n\t\t{\t//?!?!?!?!\r\n\t\t\twhile(outverts --> 0)\r\n\t\t\t{\r\n\t\t\t\tfor (c = 0; c < ic; c++)\r\n\t\t\t\t\to[c] = ((unsigned int*)in)[c];\r\n\t\t\t\tfor (; c < outcomponents; c++)\r\n\t\t\t\t\to[c] = 0;\r\n\t\t\t\to += outcomponents;\r\n\t\t\t\tin += a->bytestride;\r\n\t\t\t}\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\twhile(outverts --> 0)\r\n\t\t\t{\r\n\t\t\t\tfor (c = 0; c < ic; c++)\r\n\t\t\t\t\to[c] = ((unsigned int*)in)[c] / (double)~0u;\t//stupid format to use. will be lossy.\r\n\t\t\t\tfor (; c < outcomponents; c++)\r\n\t\t\t\t\to[c] = 0;\r\n\t\t\t\to += outcomponents;\r\n\t\t\t\tin += a->bytestride;\r\n\t\t\t}\r\n\t\t}\r\n\t\tbreak;\r\n\tcase 5126: //FLOAT\r\n\t\twhile(outverts --> 0)\r\n\t\t{\r\n\t\t\tfor (c = 0; c < ic; c++)\r\n\t\t\t\to[c] = ((float*)in)[c];\r\n\t\t\tfor (; c < outcomponents; c++)\r\n\t\t\t\to[c] = 0;\r\n\t\t\to += outcomponents;\r\n\t\t\tin += a->bytestride;\r\n\t\t}\r\n\t\tbreak;\r\n\t}\r\n\treturn ret;\r\n}\r\nstatic void *GLTF_AccessorToDataUB(gltf_t *gltf, size_t outverts, unsigned int outcomponents, struct gltf_accessor *a)\r\n{\t//only used for colour, with fallback to float, so only UNSIGNED_BYTE needs to work.\r\n\tunsigned char *ret = plugfuncs->GMalloc(&gltf->mod->memgroup, sizeof(*ret) * outcomponents * outverts), *o;\r\n\tchar *in = a->data;\r\n\r\n\tsize_t c, ic = a->type&0xff;\r\n\tif (ic > outcomponents)\r\n\t\tic = outcomponents;\r\n\to = ret;\r\n\tswitch(a->componentType)\r\n\t{\r\n\tdefault:\r\n\t\tif (gltf->warnlimit --> 0)\r\n\t\t\tCon_Printf(CON_WARNING\"GLTF_AccessorToDataUB: %s: glTF2 unsupported componentType (%i)\\n\", gltf->mod->name, a->componentType);\r\n\tcase 0:\r\n\t\tmemset(ret, 0, sizeof(*ret) * outcomponents * outverts);\r\n\t\tbreak;\r\n//\tcase 5120:\t//BYTE\r\n\tcase 5121:\t//UNSIGNED_BYTE\r\n\t\twhile(outverts --> 0)\r\n\t\t{\r\n\t\t\tfor (c = 0; c < ic; c++)\r\n\t\t\t\to[c] = ((unsigned char*)in)[c];\r\n\t\t\tfor (; c < outcomponents; c++)\r\n\t\t\t\to[c] = 0;\r\n\t\t\to += outcomponents;\r\n\t\t\tin += a->bytestride;\r\n\t\t}\r\n\t\tbreak;\r\n//\tcase 5122: //SHORT\r\n//\tcase 5123: //UNSIGNED_SHORT\r\n//\tcase 5125: //UNSIGNED_INT\r\n/*\tcase 5126: //FLOAT\r\n\t\twhile(outverts --> 0)\r\n\t\t{\r\n\t\t\tfor (c = 0; c < ic; c++)\r\n\t\t\t\to[c] = ((float*)in)[c];\r\n\t\t\tfor (; c < outcomponents; c++)\r\n\t\t\t\to[c] = 0;\r\n\t\t\to += outcomponents;\r\n\t\t\tin += a->bytestride;\r\n\t\t}\r\n\t\tbreak;*/\r\n\t}\r\n\treturn ret;\r\n}\r\nstatic void *GLTF_AccessorToDataBone(gltf_t *gltf, size_t outverts, struct gltf_accessor *a)\r\n{\t//input should only be ubytes||ushorts.\r\n\tconst unsigned int outcomponents = 4;\r\n\tboneidx_t *ret = plugfuncs->GMalloc(&gltf->mod->memgroup, sizeof(*ret) * outcomponents * outverts), *o;\r\n\tchar *in = a->data;\r\n\r\n\r\n\tsize_t c, ic = a->type&0xff;\r\n\tif (ic > outcomponents)\r\n\t\tic = outcomponents;\r\n\to = ret;\r\n\tif (a->normalized)\r\n\t\tif (gltf->warnlimit --> 0)\r\n\t\t\tCon_Printf(CON_WARNING\"GLTF_AccessorToDataBone: %s: normalised input\\n\", gltf->mod->name);\r\n\tswitch(a->componentType)\r\n\t{\r\n\tdefault:\r\n\t\tif (gltf->warnlimit --> 0)\r\n\t\t\tCon_Printf(CON_WARNING\"GLTF_AccessorToDataBone: %s: glTF2 unsupported componentType (%i)\\n\", gltf->mod->name, a->componentType);\r\n\tcase 0:\r\n\t\tmemset(ret, 0, sizeof(*ret) * outcomponents * outverts);\r\n\t\tbreak;\r\n\tcase 5120:\t//BYTE - should not be negative, so ignore sign bit\r\n\tcase 5121:\t//UNSIGNED_BYTE\r\n\t\twhile(outverts --> 0)\r\n\t\t{\r\n\t\t\tunsigned char v;\r\n\t\t\tfor (c = 0; c < ic; c++)\r\n\t\t\t{\r\n\t\t\t\tv = ((unsigned char*)in)[c];\r\n\t\t\t\tif ((unsigned int)v >= MAX_BONES)\r\n\t\t\t\t\tv = 0;\r\n\t\t\t\to[c] = gltf->bonemap[v];\r\n\t\t\t}\r\n\t\t\tfor (; c < outcomponents; c++)\r\n\t\t\t\to[c] = gltf->bonemap[0];\r\n\t\t\to += outcomponents;\r\n\t\t\tin += a->bytestride;\r\n\t\t}\r\n\t\tbreak;\r\n\tcase 5122: //SHORT - should not be negative, so ignore sign bit\r\n\tcase 5123: //UNSIGNED_SHORT\r\n\t\twhile(outverts --> 0)\r\n\t\t{\r\n\t\t\tunsigned short v;\r\n\t\t\tfor (c = 0; c < ic; c++)\r\n\t\t\t{\r\n\t\t\t\tv = ((unsigned short*)in)[c];\r\n\t\t\t\tif (v >= MAX_BONES)\r\n\t\t\t\t\tv = 0;\r\n\t\t\t\to[c] = gltf->bonemap[v];\r\n\t\t\t}\r\n\t\t\tfor (; c < outcomponents; c++)\r\n\t\t\t\to[c] = gltf->bonemap[0];\r\n\t\t\to += outcomponents;\r\n\t\t\tin += a->bytestride;\r\n\t\t}\r\n\t\tbreak;\r\n\t\t//the spec doesn't require these.\r\n\tcase 5125: //UNSIGNED_INT\r\n\t\twhile(outverts --> 0)\r\n\t\t{\r\n\t\t\tunsigned int v;\r\n\t\t\tfor (c = 0; c < ic; c++)\r\n\t\t\t{\r\n\t\t\t\tv = ((unsigned short*)in)[c];\r\n\t\t\t\tif (v >= MAX_BONES)\r\n\t\t\t\t\tv = 0;\r\n\t\t\t\to[c] = gltf->bonemap[v];\r\n\t\t\t}\r\n\t\t\tfor (; c < outcomponents; c++)\r\n\t\t\t\to[c] = gltf->bonemap[0];\r\n\t\t\to += outcomponents;\r\n\t\t\tin += a->bytestride;\r\n\t\t}\r\n\t\tbreak;\r\n\tcase 5126: //FLOAT. for bone indexes. wtf?\r\n\t\twhile(outverts --> 0)\r\n\t\t{\r\n\t\t\tunsigned int v;\r\n\t\t\tfor (c = 0; c < ic; c++)\r\n\t\t\t{\r\n\t\t\t\tv = ((float*)in)[c];\r\n\t\t\t\tif (v >= MAX_BONES)\r\n\t\t\t\t\tv = 0;\r\n\t\t\t\to[c] = gltf->bonemap[v];\r\n\t\t\t}\r\n\t\t\tfor (; c < outcomponents; c++)\r\n\t\t\t\to[c] = gltf->bonemap[0];\r\n\t\t\to += outcomponents;\r\n\t\t\tin += a->bytestride;\r\n\t\t}\r\n\t\tbreak;\r\n\t}\r\n\treturn ret;\r\n}\r\nstatic void TransformPosArray(vecV_t *data, size_t vcount, double matrix[])\r\n{\r\n\twhile (vcount --> 0)\r\n\t{\r\n\t\tvec3_t t;\r\n\t\tVectorCopy((*data), t);\r\n\r\n\t\t(*data)[0] = matrix[0]*t[0] + matrix[4]*t[1] + matrix[8]*t[2] + matrix[12];\r\n\t\t(*data)[1] = matrix[1]*t[0] + matrix[5]*t[1] + matrix[9]*t[2] + matrix[13];\r\n\t\t(*data)[2] = matrix[2]*t[0] + matrix[6]*t[1] + matrix[10]*t[2] + matrix[14];\r\n\t\t//1        = matrix[3]*t[0] + matrix[7]*t[1] + matrix[11]*t[2] + matrix[14];\t//hopefully...\r\n\t\tdata++;\r\n\t}\r\n}\r\nstatic void TransformDirArray(vec3_t *data, size_t vcount, double matrix[])\r\n{\r\n\tvec3_t t;\r\n\tfloat mag;\r\n\twhile (vcount --> 0)\r\n\t{\r\n\t\tt[0] = matrix[0]*(*data)[0] + matrix[4]*(*data)[1] + matrix[8]*(*data)[2];\r\n\t\tt[1] = matrix[1]*(*data)[0] + matrix[5]*(*data)[1] + matrix[9]*(*data)[2];\r\n\t\tt[2] = matrix[2]*(*data)[0] + matrix[6]*(*data)[1] + matrix[10]*(*data)[2];\r\n\r\n\t\t//scaling is bad for axis.\r\n\t\tmag = DotProduct(t,t);\r\n\t\tif (mag)\r\n\t\t{\r\n\t\t\tmag = 1/sqrt(mag);\r\n\t\t\tVectorScale(t, mag, t);\r\n\t\t}\r\n\r\n\t\tVectorCopy(t, (*data));\r\n\t\tdata++;\r\n\t}\r\n}\r\n#ifndef SERVERONLY\r\nstatic texid_t GLTF_LoadImage(gltf_t *gltf, json_t *imageid, unsigned int flags)\r\n{\r\n\tsize_t size;\r\n\ttexid_t ret = r_nulltex;\r\n\tjson_t *image\t\t\t= GLTF_FindJSONID(gltf, \"images\", imageid, NULL);\r\n\tjson_t *uri\t\t\t\t= JSON_FindChild(image, \"uri\");\r\n\tjson_t *mimeType\t\t= JSON_FindChild(image, \"mimeType\");\r\n\tjson_t *bufferViewid\t= JSON_FindChild(image, \"bufferView\");\r\n\tchar uritext[MAX_QPATH];\r\n\tchar filename[MAX_QPATH];\r\n\tvoid *mem;\r\n\tstruct gltf_bufferview view;\r\n\r\n\tJSON_FlagAsUsed(image, \"name\");\r\n\r\n\tif (gltf->ver <= 1)\r\n\t{\r\n\t\tjson_t *binary_glTF\t= JSON_FindChild(image, \"extensions.KHR_binary_glTF\");\r\n\t\tif (binary_glTF)\r\n\t\t{\r\n\t\t\tbufferViewid = JSON_FindChild(binary_glTF, \"bufferView\");\r\n\t\t\tmimeType = JSON_FindChild(binary_glTF, \"mimeType\");\r\n\t\t\tJSON_FlagAsUsed(binary_glTF, \"width\");\r\n\t\t\tJSON_FlagAsUsed(binary_glTF, \"height\");\r\n\t\t\turi = NULL;\r\n\t\t}\r\n\t}\r\n\r\n\t//potentially valid mime types:\r\n\t//image/png\r\n\t//image/vnd-ms.dds (MSFT_texture_dds)\r\n\t(void)mimeType;\r\n\r\n\t*uritext = 0;\r\n\tif (uri)\r\n\t{\r\n\t\tmem = JSON_MallocDataURI(uri, &size);\r\n\t\tif (mem)\r\n\t\t{\r\n\t\t\tJSON_GetPath(image, false, uritext, sizeof(uritext));\r\n\t\t\tret = modfuncs->GetTexture(uritext, NULL, flags, mem, NULL, size, 0, TF_INVALID);\r\n\t\t\tfree(mem);\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tJSON_ReadBody(uri, uritext, sizeof(uritext));\r\n\t\t\tGLTF_RelativePath(gltf->mod->name, uritext, filename, sizeof(filename));\r\n\t\t\tret = modfuncs->GetTexture(filename, NULL, flags, NULL, NULL, 0, 0, TF_INVALID);\r\n\t\t}\r\n\t}\r\n\telse if (bufferViewid)\r\n\t{\r\n\t\tif (GLTF_GetBufferViewData(gltf, bufferViewid, &view))\r\n\t\t{\r\n\t\t\tJSON_GetPath(image, false, uritext, sizeof(uritext));\r\n\t\t\tret = modfuncs->GetTexture(uritext, NULL, flags, view.data, NULL, view.length, 0, TF_INVALID);\r\n\t\t}\r\n\t}\r\n\r\n\treturn ret;\r\n}\r\nstatic texid_t GLTF_LoadTexture(gltf_t *gltf, json_t *textureid, unsigned int flags)\r\n{\r\n\tjson_t *tex = GLTF_FindJSONID(gltf, \"textures\", textureid, NULL);\r\n\tjson_t *sampler = GLTF_FindJSONID(gltf, \"samplers\", JSON_FindChild(tex, \"sampler\"), NULL);\r\n\r\n\tint magFilter = JSON_GetInteger(sampler, \"magFilter\", 0);\r\n\tint minFilter = JSON_GetInteger(sampler, \"minFilter\", 0);\r\n\tint wrapS = JSON_GetInteger(sampler, \"wrapS\", 10497);\r\n\tint wrapT = JSON_GetInteger(sampler, \"wrapT\", 10497);\r\n\tjson_t *sourceid;\r\n\r\n\tJSON_FlagAsUsed(sampler, \"name\");\r\n\tJSON_FlagAsUsed(sampler, \"extensions\");\r\n\r\n\tswitch(magFilter)\r\n\t{\r\n\tdefault:\r\n\t\tbreak;\r\n\tcase 9728: //NEAREST\r\n\t\tflags |= IF_NOMIPMAP|IF_NEAREST;\r\n\t\tif (minFilter != 9728)\r\n\t\t\tif (gltf->warnlimit --> 0)\r\n\t\t\t\tCon_Printf(CON_WARNING\"%s: mixed min/mag filters\\n\", gltf->mod->name);\r\n\t\tbreak;\r\n\tcase 9986: // NEAREST_MIPMAP_LINEAR\r\n\t\tif (gltf->warnlimit --> 0)\r\n\t\t\tCon_Printf(CON_WARNING\"%s: mixed mag/mip filters\\n\", gltf->mod->name);\r\n\t\t//fallthrough\r\n\tcase 9984: // NEAREST_MIPMAP_NEAREST\r\n\t\tflags |= IF_NEAREST;\r\n\t\tif (minFilter != 9728)\r\n\t\t\tif (gltf->warnlimit --> 0)\r\n\t\t\t\tCon_Printf(CON_WARNING\"%s: mixed min/mag filters\\n\", gltf->mod->name);\r\n\t\tbreak;\r\n\r\n\tcase 9729: //LINEAR\r\n\t\tflags |= IF_NOMIPMAP|IF_LINEAR;\r\n\t\tif (minFilter != 9729)\r\n\t\t\tif (gltf->warnlimit --> 0)\r\n\t\t\t\tCon_Printf(CON_WARNING\"%s: mixed min/mag filters\\n\", gltf->mod->name);\r\n\t\tbreak;\r\n\tcase 9985: // LINEAR_MIPMAP_NEAREST\r\n\t\tif (gltf->warnlimit --> 0)\r\n\t\t\tCon_Printf(CON_WARNING\"%s: mixed mag/mip filters\\n\", gltf->mod->name);\r\n\t\t//fallthrough\r\n\tcase 9987: // LINEAR_MIPMAP_LINEAR\r\n\t\tflags |= IF_LINEAR;\r\n\t\tif (minFilter != 9729)\r\n\t\t\tif (gltf->warnlimit --> 0)\r\n\t\t\t\tCon_Printf(CON_WARNING\"%s: mixed min/mag filters\\n\", gltf->mod->name);\r\n\t\tbreak;\r\n\t}\r\n\tif (wrapS == 10497 && wrapT == 10497)\t//REPEAT\r\n\t\t;\r\n\telse if (wrapS == 33071 && wrapT == 33071)\t//CLAMP_TO_EDGE\r\n\t\tflags |= IF_CLAMP;\r\n\telse if (wrapS == 33648 && wrapT == 33648)\t//MIRRORED_REPEAT\r\n\t{\r\n\t\tif (gltf->warnlimit --> 0)\r\n\t\t\tCon_Printf(CON_WARNING\"%s: MIRRORED_REPEAT wrap mode not supported\\n\", gltf->mod->name);\r\n\t}\r\n\telse\r\n\t{\r\n\t\tif (gltf->warnlimit --> 0)\r\n\t\t\tCon_Printf(CON_WARNING\"%s: unsupported/mixed texture wrap modes %i,%i\\n\", gltf->mod->name, wrapS, wrapT);\r\n\r\n\t\tif (wrapS == 33071 || wrapT == 33071)\r\n\t\t\tflags |= IF_CLAMP;\r\n\t}\r\n\r\n\tflags |= IF_NOREPLACE;\r\n\r\n\tsourceid = JSON_FindChild(tex, \"extensions.MSFT_texture_dds.source\");\t//load a dds instead, if one is available.\r\n\tif (!sourceid)\r\n\t\tsourceid = JSON_FindChild(tex, \"source\");\t//fall back on the normal source\r\n\treturn GLTF_LoadImage(gltf, sourceid, flags);\r\n}\r\nstatic char *GLTF1_LoadShader(gltf_t *gltf, json_t *shaderid)\r\n{\t//reads a vertex or fragment shader blob\r\n\tjson_t *shader = GLTF_FindJSONID(gltf, \"shaders\", shaderid, NULL);\r\n\tjson_t *uri = JSON_FindChild(shader, \"uri\");\r\n\tchar *out = NULL;\r\n\tjson_t *bufferviewid = JSON_FindChild(shader, \"extensions.KHR_binary_glTF.bufferView\");\r\n\tstruct gltf_bufferview view;\r\n\tif (bufferviewid && GLTF_GetBufferViewData(gltf, bufferviewid, &view) && view.data && view.length)\r\n\t{\r\n\t\tout = malloc(view.length+1);\r\n\t\tmemcpy(out, view.data, view.length);\r\n\t\tout[view.length] = 0;\r\n\t}\r\n\telse\r\n\t{\r\n\t\tJSON_FlagAsUsed(shader, \"type\");\t//don't care\r\n\r\n\t\tif (uri)\r\n\t\t{\r\n\t\t\tsize_t length;\r\n\t\t\tout = JSON_MallocDataURI(uri, &length);\t//try and decode data schemes...\r\n\t\t\tif (!out)\r\n\t\t\t{\r\n\t\t\t\t//read a file from disk.\r\n\t\t\t\tvfsfile_t *f;\r\n\t\t\t\tchar uritext[MAX_QPATH];\r\n\t\t\t\tchar filename[MAX_QPATH];\r\n\t\t\t\tJSON_ReadBody(uri, uritext, sizeof(uritext));\r\n\t\t\t\tGLTF_RelativePath(gltf->mod->name, uritext, filename, sizeof(filename));\r\n\t\t\t\tf = filefuncs->OpenVFS(filename, \"rb\", FS_GAME);\r\n\t\t\t\tif (f)\r\n\t\t\t\t{\r\n\t\t\t\t\tlength = VFS_GETLEN(f);\r\n\t\t\t\t\tlength = min(length, length);\r\n\t\t\t\t\tout = malloc(length+1);\r\n\t\t\t\t\tout[length] = 0;\r\n\t\t\t\t\tVFS_READ(f, out, length);\r\n\t\t\t\t\tVFS_CLOSE(f);\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t\tCon_Printf(CON_WARNING\"%s: Unable to read buffer file %s\\n\", gltf->mod->name, filename);\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\t//if it starts with a precision modifier then just strip that out... gl doesn't like gles's precision modifiers and we tend to try to provide our own too, which doesn't help things.\r\n\tif (out && !strncmp(out, \"precision \", 10))\r\n\t{\r\n\t\tchar *le = strchr(out, '\\n');\r\n\t\tif (le++)\r\n\t\t\tmemmove(out, le, strlen(le)+1);\r\n\t}\r\n\treturn out;\r\n}\r\nstatic qboolean GLTF1_LoadMaterial(gltf_t *gltf, json_t *mat, texnums_t *texnums, char *shadertext, size_t shadertextsize)\r\n{\r\n\tjson_t *technique = GLTF_FindJSONID(gltf, \"techniques\", JSON_FindChild(mat, \"technique\"), NULL);\r\n\tjson_t *parameters = JSON_FindChild(technique, \"parameters\");\r\n\tjson_t *values = JSON_FindChild(mat, \"values\");\r\n\tjson_t *common = JSON_FindChild(mat, \"extensions.KHR_materials_common\");\r\n\tjson_t *v;\r\n\tjson_t *p;\r\n\tchar header[8192];\r\n\tchar samplers[8192];\r\n\tchar attributes[8192];\r\n\tchar uniforms[8192];\r\n\tchar *vertshader = NULL;\r\n\tchar *fragshader = NULL;\r\n\tint type;\r\n\tchar semantic[64];\r\n\tint sampidx = 0;\r\n\ttexid_t tex;\r\n\r\n\tif (common)\r\n\t{\r\n\t\tjson_t *values = JSON_FindChild(common, \"values\");\r\n//\t\tchar techniquebuf[64];\r\n//\t\tconst char *technique = JSON_GetString(common, \"technique\", techniquebuf, sizeof(techniquebuf), \"\");\r\n\t\tqboolean doubleSided = JSON_GetInteger(common, \"doubleSided\", false);\r\n//\t\tqboolean transparent = JSON_GetInteger(common, \"transparent\", false);\r\n//\t\tvec4_t ambient;\r\n\t\tjson_t *diffusename = JSON_FindChild(values, \"diffuse\");\r\n\t\tvec4_t diffusetint = {1,1,1,1};\r\n\t\tjson_t *emissionname = JSON_FindChild(values, \"emission\");\r\n\t\tvec4_t emissiontint = {1,1,1,1};\r\n\t\tjson_t *specularname = JSON_FindChild(values, \"emission\");\r\n\t\tvec4_t speculartint = {1,1,1,1};\r\n\t\tfloat shininess = JSON_GetFloat(values, \"shininess\", 0);\r\n\r\n\t\tif (emissionname)\r\n\t\t{\r\n\t\t\tif (!emissionname->child)\t//a string, so a texture id\r\n\t\t\t\ttexnums->fullbright = GLTF_LoadTexture(gltf, emissionname, IF_NOALPHA);\r\n\t\t\telse\r\n\t\t\t{\t//child nodes means its a vec4.\r\n\t\t\t\temissiontint[0] = JSON_GetIndexedFloat(emissionname, 0, 0);\r\n\t\t\t\temissiontint[1] = JSON_GetIndexedFloat(emissionname, 1, 0);\r\n\t\t\t\temissiontint[2] = JSON_GetIndexedFloat(emissionname, 2, 0);\r\n\t\t\t\temissiontint[3] = JSON_GetIndexedFloat(emissionname, 3, 1);\r\n\r\n\t\t\t\tif (emissiontint[0] || emissiontint[1] || emissiontint[2])\r\n\t\t\t\t\ttexnums->fullbright = modfuncs->GetTexture(\"$whiteimage\", NULL, IF_NOMIPMAP|IF_NOPICMIP|IF_NEAREST|IF_NOGAMMA, NULL, NULL, 0, 0, TF_INVALID);\r\n\t\t\t}\r\n\t\t}\r\n\t\tif (diffusename)\r\n\t\t{\r\n\t\t\tif (!diffusename->child)\t//a string, so a texture id\r\n\t\t\t\ttexnums->base = GLTF_LoadTexture(gltf, diffusename, 0);\r\n\t\t\telse\r\n\t\t\t{\t//child nodes means its a vec4.\r\n\t\t\t\ttexnums->base = modfuncs->GetTexture(\"$whiteimage\", NULL, IF_NOMIPMAP|IF_NOPICMIP|IF_NEAREST|IF_NOGAMMA, NULL, NULL, 0, 0, TF_INVALID);\r\n\t\t\t\tdiffusetint[0] = JSON_GetIndexedFloat(diffusename, 0, 0);\r\n\t\t\t\tdiffusetint[1] = JSON_GetIndexedFloat(diffusename, 1, 0);\r\n\t\t\t\tdiffusetint[2] = JSON_GetIndexedFloat(diffusename, 2, 0);\r\n\t\t\t\tdiffusetint[3] = JSON_GetIndexedFloat(diffusename, 3, 1);\r\n\t\t\t}\r\n\t\t}\r\n\t\tif (specularname)\r\n\t\t{\r\n\t\t\tif (!specularname->child)\t//a string, so a texture id\r\n\t\t\t\ttexnums->specular = GLTF_LoadTexture(gltf, specularname, IF_NOALPHA);\r\n\t\t\telse\r\n\t\t\t{\t//child nodes means its a vec4.\r\n\t\t\t\ttexnums->specular = modfuncs->GetTexture(\"$whiteimage\", NULL, IF_NOMIPMAP|IF_NOPICMIP|IF_NEAREST|IF_NOGAMMA, NULL, NULL, 0, 0, TF_INVALID);\r\n\t\t\t\tspeculartint[0] = JSON_GetIndexedFloat(specularname, 0, 0);\r\n\t\t\t\tspeculartint[1] = JSON_GetIndexedFloat(specularname, 1, 0);\r\n\t\t\t\tspeculartint[2] = JSON_GetIndexedFloat(specularname, 2, 0);\r\n\t\t\t\tspeculartint[3] = JSON_GetIndexedFloat(specularname, 3, 1);\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tQ_snprintf(shadertext, shadertextsize,\r\n\t\t\t\"{\\n\"\r\n\t\t\t\t\"%s\"//cull\r\n\t\t\t\t\"program defaultskin#VC%s#FTE_SPECULAR_EXPONENT=%f\\n\"\r\n\t\t\t\t\"{\\n\"\r\n\t\t\t\t\t\"map $diffuse\\n\"\r\n\t\t\t\t\t\"%s\"\t//blend\r\n\t\t\t\t\t\"%s\"\t//rgbgen\r\n\t\t\t\t\"}\\n\"\r\n\t\t\t\t\"fte_basefactor %f %f %f %f\\n\"\r\n\t\t\t\t\"fte_specularfactor %f %f %f %f\\n\"\r\n\t\t\t\t\"fte_fullbrightfactor %f %f %f %f\\n\"\r\n\t\t\t\"}\\n\",\r\n\t\t\tdoubleSided?\"cull disable\\n\":\"\",\r\n\t\t\t\"\",//alphaCutoffmodifier,\r\n\t\t\tshininess,\r\n\t\t\t\"\",//(alphaMode==1)?\"\":(alphaMode==2)?\"blendfunc blend\\n\":\"\",\r\n\t\t\t\"\",//vertexcolours?\"rgbgen vertex\\nalphagen vertex\\n\":\"\",\r\n\t\t\tdiffusetint[0],diffusetint[1],diffusetint[2],diffusetint[3],\r\n\t\t\tspeculartint[0],speculartint[1],speculartint[2],speculartint[3],\r\n\t\t\temissiontint[0],emissiontint[1],emissiontint[2],emissiontint[3]);\r\n\t\treturn true;\r\n\t}\r\n\r\n\t//mat->values.diffuse tends to be quite common. make an executive descision...\r\n\ttexnums->base = GLTF_LoadTexture(gltf, JSON_FindChild(values, \"diffuse\"), 0);\r\n\tif (mod_gltf_ignoretechniques->ival)\r\n\t{\t//mat->values.diffuse tends to be quite common\r\n\t\treturn false;\r\n\t}\r\n\tif (!technique)\r\n\t{\r\n\t\t//missing technique is supposed to result in a greyscale model\r\n\t\tQ_snprintf(shadertext, shadertextsize,\r\n\t\t\t\"{\\n\"\r\n\t\t\t\t\"program defaultskin\\n\"\r\n\t\t\t\t\"diffusemap $whiteimage\\n\"\r\n\t\t\t\t\"{\\n\"\r\n\t\t\t\t\t\"map $diffuse\\n\"\r\n\t\t\t\t\t\"rgbgen const 0.5 0.5 0.5\\n\"\r\n\t\t\t\t\t\"alphagen const 1.0\\n\"\r\n\t\t\t\t\"}\\n\"\r\n\t\t\t\"}\\n\"\r\n\t\t\t);\r\n\t\treturn true;\r\n\t}\r\n\r\n\t*header = 0;\r\n\t*samplers = 0;\r\n\t*attributes = 0;\r\n\t*uniforms = 0;\r\n\r\n\t//this is supposed to be glessl 100. lets try to do our best to get something compatible.\r\n\tQ_snprintfcat(header, sizeof(header), \"!!ver 100 120\\n\");\r\n\t//and reduce conflicts with fte's normal symbols.\r\n\tQ_snprintfcat(header, sizeof(header), \"!!explicit\\n\");\r\n\r\n\tv = JSON_FindChild(technique, \"uniforms\");\r\n\tif (v)\r\n\t\tfor (v = v->child; v; v = v->sibling)\r\n\t\t{\r\n\t\t\tenum {\r\n\t\t\t\tGLTF_BYTE = 5120,\r\n\t\t\t\tGLTF_UNSIGNED_BYTE = 5121,\r\n\t\t\t\tGLTF_SHORT = 5122,\r\n\t\t\t\tGLTF_UNSIGNED_SHORT = 5123,\r\n\t\t\t\tGLTF_INT = 5124,\r\n\t\t\t\tGLTF_UNSIGNED_INT = 5125,\r\n\t\t\t\tGLTF_FLOAT = 5126,\r\n\t\t\t\tGLTF_FLOAT_VEC2 = 35664,\r\n\t\t\t\tGLTF_FLOAT_VEC3 = 35665,\r\n\t\t\t\tGLTF_FLOAT_VEC4 = 35666,\r\n\t\t\t\tGLTF_INT_VEC2 = 35667,\r\n\t\t\t\tGLTF_INT_VEC3 = 35668,\r\n\t\t\t\tGLTF_INT_VEC4 = 35669,\r\n\t\t\t\tGLTF_BOOL = 35670,\r\n\t\t\t\tGLTF_BOOL_VEC2 = 35671,\r\n\t\t\t\tGLTF_BOOL_VEC3 = 35672,\r\n\t\t\t\tGLTF_BOOL_VEC4 = 35673,\r\n\t\t\t\tGLTF_FLOAT_MAT2 = 35674,\r\n\t\t\t\tGLTF_FLOAT_MAT3 = 35675,\r\n\t\t\t\tGLTF_FLOAT_MAT4 = 35676,\r\n\t\t\t\tGLTF_SAMPLER_2D = 35678,\r\n\t\t\t};\r\n\t\t\tv->used = true;\r\n\t\t\tp = GLTF_FindJSONIDParent(gltf, parameters, v, NULL);\r\n\r\n\t\t\tif (1 != JSON_GetInteger(p, \"count\", 1))\r\n\t\t\t{\r\n\t\t\t\tif (gltf->warnlimit --> 0)\r\n\t\t\t\t\tCon_Printf(CON_WARNING\"%s: Unsupported parameter->count for uniform %s\\n\", gltf->mod->name, v->name);\r\n\t\t\t\treturn false;\r\n\t\t\t}\r\n\t\t\tif (JSON_GetString(p, \"node\", semantic, sizeof(semantic), NULL))\r\n\t\t\t{\r\n\t\t\t\tif (gltf->warnlimit --> 0)\r\n\t\t\t\t\tCon_Printf(CON_WARNING\"%s: Unsupported parameter->node for uniform %s\\n\", gltf->mod->name, v->name);\r\n\t\t\t\treturn false;\r\n\t\t\t}\r\n\r\n\t\t\tif (!JSON_GetString(p, \"semantic\", semantic, sizeof(semantic), NULL))\r\n\t\t\t\t*semantic = 0;\r\n\t\t\ttype = JSON_GetInteger(p, \"type\", 0);\r\n\r\n\t\t\tif (!strcasecmp(semantic, \"MODEL\") && type == GLTF_FLOAT_MAT4)\r\n\t\t\t\tQ_snprintfcat(header, sizeof(header), \"!!semantic %s m_model\\n\", v->name);\r\n\t\t\telse if (!strcasecmp(semantic, \"VIEW\") && type == GLTF_FLOAT_MAT4)\r\n\t\t\t\tQ_snprintfcat(header, sizeof(header), \"!!semantic %s m_view\\n\", v->name);\r\n\t\t\telse if (!strcasecmp(semantic, \"PROJECTION\") && type == GLTF_FLOAT_MAT4)\r\n\t\t\t\tQ_snprintfcat(header, sizeof(header), \"!!semantic %s m_projection\\n\", v->name);\r\n\t\t\telse if (!strcasecmp(semantic, \"MODELVIEW\") && type == GLTF_FLOAT_MAT4)\r\n\t\t\t\tQ_snprintfcat(header, sizeof(header), \"!!semantic %s m_modelview\\n\", v->name);\r\n\t\t\telse if (!strcasecmp(semantic, \"MODELVIEWPROJECTION\") && type == GLTF_FLOAT_MAT4)\r\n\t\t\t\tQ_snprintfcat(header, sizeof(header), \"!!semantic %s m_modelviewprojection\\n\", v->name);\r\n\t\t\telse if (!strcasecmp(semantic, \"MODELINVERSE\") && type == GLTF_FLOAT_MAT4)\r\n\t\t\t\tQ_snprintfcat(header, sizeof(header), \"!!semantic %s m_invmodel\\n\", v->name);\r\n\t\t\telse if (!strcasecmp(semantic, \"VIEWINVERSE\") && type == GLTF_FLOAT_MAT4)\r\n\t\t\t\tQ_snprintfcat(header, sizeof(header), \"!!semantic %s m_invviewprojection\\n\", v->name);\r\n\t\t\telse if (!strcasecmp(semantic, \"PROJECTIONINVERSE\") && type == GLTF_FLOAT_MAT4)\r\n\t\t\t\tQ_snprintfcat(header, sizeof(header), \"!!semantic %s m_invprojection\\n\", v->name);\r\n\t\t\telse if (!strcasecmp(semantic, \"MODELVIEWINVERSE\") && type == GLTF_FLOAT_MAT4)\r\n\t\t\t\tQ_snprintfcat(header, sizeof(header), \"!!semantic %s m_invmodelview\\n\", v->name);\r\n\t\t\telse if (!strcasecmp(semantic, \"MODELVIEWPROJECTIONINVERSE\") && type == GLTF_FLOAT_MAT4)\r\n\t\t\t\tQ_snprintfcat(header, sizeof(header), \"!!semantic %s m_invmodelviewprojection\\n\", v->name);\r\n\t\t\telse if (!strcasecmp(semantic, \"MODELINVERSETRANSPOSE\") && type == GLTF_FLOAT_MAT3)\r\n\t\t\t\tQ_snprintfcat(header, sizeof(header), \"!!semantic %s m_invmodeltranspose\\n\", v->name);\r\n\t\t\telse if (!strcasecmp(semantic, \"MODELVIEWINVERSETRANSPOSE\") && type == GLTF_FLOAT_MAT3)\r\n\t\t\t\tQ_snprintfcat(header, sizeof(header), \"!!semantic %s m_invmodelviewtranspose\\n\", v->name);\r\n//\t\t\telse if (!strcasecmp(semantic, \"VIEWPORT\") && type == GLTF_FLOAT_VEC4)\r\n//\t\t\t\tQ_snprintfcat(header, sizeof(header), \"!!semantic %s UNSUPPORTED\\n\", v->name);\r\n\t\t\telse if (!strcasecmp(semantic, \"JOINTMATRIX\") && type == GLTF_FLOAT_MAT4)\r\n\t\t\t\tQ_snprintfcat(header, sizeof(header), \"!!semantic %s m_bones_mat4\\n\", v->name);\r\n//\t\t\telse if (!strcasecmp(semantic, \"LOCAL\") && type == GLTF_FLOAT_MAT4)\r\n//\t\t\t\tQ_snprintfcat(header, sizeof(header), \"!!semantic %s UNSUPPORTED\\n\", v->name);\r\n\t\t\telse if (!strcasecmp(semantic, \"\"))\r\n\t\t\t{\r\n\t\t\t\tjson_t *val = GLTF_FindJSONIDParent(gltf, values, v, NULL);\r\n\t\t\t\tswitch(type)\r\n\t\t\t\t{\r\n\t\t\t\tcase GLTF_SAMPLER_2D:\r\n\t\t\t\t\tQ_snprintfcat(header, sizeof(header), \"!!constt %s %i\\n\", v->name, sampidx++);\r\n\t\t\t\t\ttex = GLTF_LoadTexture(gltf, val, 0);\r\n\t\t\t\t\tQ_snprintfcat(samplers, sizeof(samplers), \"{\\nmap %s\\n}\\n\", tex?tex->ident:\"$whiteimage\");\r\n\t\t\t\t\tbreak;\r\n\r\n\t\t\t\tcase GLTF_FLOAT:\t\tQ_snprintfcat(header, sizeof(header), \"!!const1f %s %f\\n\", v->name, JSON_GetFloat(val, NULL, 0));\tbreak;\r\n\t\t\t\tcase GLTF_FLOAT_VEC2:\tQ_snprintfcat(header, sizeof(header), \"!!const2f %s %f %f\\n\", v->name, JSON_GetIndexedFloat(val, 0, 0), JSON_GetIndexedFloat(val, 1, 0));\tbreak;\r\n\t\t\t\tcase GLTF_FLOAT_VEC3:\tQ_snprintfcat(header, sizeof(header), \"!!const3f %s %f %f %f\\n\", v->name, JSON_GetIndexedFloat(val, 0, 0), JSON_GetIndexedFloat(val, 1, 0), JSON_GetIndexedFloat(val, 2, 0));\tbreak;\r\n\t\t\t\tcase GLTF_FLOAT_VEC4:\tQ_snprintfcat(header, sizeof(header), \"!!const4f %s %f %f %f %f\\n\", v->name, JSON_GetIndexedFloat(val, 0, 0), JSON_GetIndexedFloat(val, 1, 0), JSON_GetIndexedFloat(val, 2, 0), JSON_GetIndexedFloat(val, 3, 0));\tbreak;\r\n\r\n\t\t\t\tcase GLTF_BOOL:\t\t\t//glsl's bool/bvecN types can be ininitialised as floats or ints. lets just use ints here.\r\n\t\t\t\tcase GLTF_BYTE:\t\t\t//FIXME: it is not specified whether these are meant to be normalized or not. are they always float/vecN or int/ivecN? the spec doesn't say.\r\n\t\t\t\tcase GLTF_SHORT:\r\n\t\t\t\tcase GLTF_INT:\t\t\tQ_snprintfcat(header, sizeof(header), \"!!const1i %s %i\\n\", v->name, JSON_GetInteger(val, NULL, 0));\tbreak;\r\n\t\t\t\tcase GLTF_BOOL_VEC2:\r\n\t\t\t\tcase GLTF_INT_VEC2:\t\tQ_snprintfcat(header, sizeof(header), \"!!const2i %s %i %i\\n\", v->name, JSON_GetIndexedInteger(val, 0, 0), JSON_GetIndexedInteger(val, 1, 0));\tbreak;\r\n\t\t\t\tcase GLTF_BOOL_VEC3:\r\n\t\t\t\tcase GLTF_INT_VEC3:\t\tQ_snprintfcat(header, sizeof(header), \"!!const3i %s %i %i %i\\n\", v->name, JSON_GetIndexedInteger(val, 0, 0), JSON_GetIndexedInteger(val, 1, 0), JSON_GetIndexedInteger(val, 2, 0));\tbreak;\r\n\t\t\t\tcase GLTF_BOOL_VEC4:\r\n\t\t\t\tcase GLTF_INT_VEC4:\t\tQ_snprintfcat(header, sizeof(header), \"!!const4i %s %i %i %i %i\\n\", v->name, JSON_GetIndexedInteger(val, 0, 0), JSON_GetIndexedInteger(val, 1, 0), JSON_GetIndexedInteger(val, 2, 0), JSON_GetIndexedInteger(val, 3, 0));\tbreak;\r\n\r\n\t\t\t\tcase GLTF_UNSIGNED_BYTE://FIXME: it is not specified whether these are meant to be normalized or not. are they always float/vecN or int/ivecN? the spec doesn't say.\r\n\t\t\t\tcase GLTF_UNSIGNED_SHORT:\r\n\t\t\t\tcase GLTF_UNSIGNED_INT:\tQ_snprintfcat(header, sizeof(header), \"!!const1u %s %f\\n\", v->name, JSON_GetFloat(val, NULL, 0));\tbreak;\r\n\t\t\t\t//curiously no uvecs listed by the spec\r\n\r\n\t\t\t\tcase GLTF_FLOAT_MAT2:\tQ_snprintfcat(header, sizeof(header), \"!!const2m %s %f %f %f %f\\n\", v->name, JSON_GetIndexedFloat(val, 0, 0), JSON_GetIndexedFloat(val, 1, 0), JSON_GetIndexedFloat(val, 2, 0), JSON_GetIndexedFloat(val, 3, 0));\tbreak;\r\n\t\t\t\tcase GLTF_FLOAT_MAT3:\tQ_snprintfcat(header, sizeof(header), \"!!const3m %s %f %f %f %f %f %f %f %f %f\\n\", v->name, JSON_GetIndexedFloat(val, 0, 0), JSON_GetIndexedFloat(val, 1, 0), JSON_GetIndexedFloat(val, 2, 0), JSON_GetIndexedFloat(val, 3, 0), JSON_GetIndexedFloat(val, 4, 0), JSON_GetIndexedFloat(val, 5, 0), JSON_GetIndexedFloat(val, 6, 0), JSON_GetIndexedFloat(val, 7, 0), JSON_GetIndexedFloat(val, 8, 0));\tbreak;\r\n\t\t\t\tcase GLTF_FLOAT_MAT4:\tQ_snprintfcat(header, sizeof(header), \"!!const4m %s %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f\\n\", v->name, JSON_GetIndexedFloat(val, 0, 0), JSON_GetIndexedFloat(val, 1, 0), JSON_GetIndexedFloat(val, 2, 0), JSON_GetIndexedFloat(val, 3, 0), JSON_GetIndexedFloat(val, 4, 0), JSON_GetIndexedFloat(val, 5, 0), JSON_GetIndexedFloat(val, 6, 0), JSON_GetIndexedFloat(val, 7, 0), JSON_GetIndexedFloat(val, 8, 0), JSON_GetIndexedFloat(val, 9, 0), JSON_GetIndexedFloat(val, 10, 0), JSON_GetIndexedFloat(val, 11, 0), JSON_GetIndexedFloat(val, 12, 0), JSON_GetIndexedFloat(val, 13, 0), JSON_GetIndexedFloat(val, 14, 0), JSON_GetIndexedFloat(val, 15, 0));\tbreak;\r\n\r\n\t\t\t\tdefault:\r\n\t\t\t\t\tif (gltf->warnlimit --> 0)\r\n\t\t\t\t\t\tCon_Printf(CON_WARNING\"%s: Unsupported constant uniform type %i for uniform %s\\n\", gltf->mod->name, type, v->name);\r\n\t\t\t\t\treturn false;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tif (gltf->warnlimit --> 0)\r\n\t\t\t\t\tCon_Printf(CON_WARNING\"%s: Unknown/Unsupported semantic %s for uniform %s\\n\", gltf->mod->name, semantic, v->name);\r\n\t\t\t\treturn false;\r\n\t\t\t}\r\n\t\t}\r\n\tv = JSON_FindChild(technique, \"attributes\");\r\n\tif (v)\r\n\t\tfor (v = v->child; v; v = v->sibling)\r\n\t\t{\r\n\t\t\tv->used = true;\r\n\t\t\tp = GLTF_FindJSONIDParent(gltf, parameters, v, NULL);\r\n\t\t\tif (!JSON_GetString(p, \"semantic\", semantic, sizeof(semantic), NULL))\r\n\t\t\t\t*semantic = 0;\r\n\t\t\ttype = JSON_GetInteger(p, \"type\", 0);\r\n\r\n\t\t\tif (!strcasecmp(semantic, \"POSITION\"))\r\n\t\t\t\tQ_snprintfcat(attributes, sizeof(attributes), \"#define %s fte_v_position\\n\", v->name);\r\n\t\t\telse if (!strcasecmp(semantic, \"NORMAL\"))\r\n\t\t\t\tQ_snprintfcat(attributes, sizeof(attributes), \"#define %s fte_v_normal\\n\", v->name);\r\n\t\t\telse if (!strcasecmp(semantic, \"TEXCOORD_0\"))\r\n\t\t\t\tQ_snprintfcat(attributes, sizeof(attributes), \"#define %s fte_v_texcoord\\n\", v->name);\r\n\t\t\telse if (!strcasecmp(semantic, \"TEXCOORD_1\"))\r\n\t\t\t\tQ_snprintfcat(attributes, sizeof(attributes), \"#define %s fte_v_lmcoord\\n\", v->name);\r\n\t\t\telse if (!strcasecmp(semantic, \"JOINT\"))\r\n\t\t\t\tQ_snprintfcat(attributes, sizeof(attributes), \"#define %s fte_v_bone\\n\", v->name);\r\n\t\t\telse if (!strcasecmp(semantic, \"WEIGHT\"))\r\n\t\t\t\tQ_snprintfcat(attributes, sizeof(attributes), \"#define %s fte_v_weight\\n\", v->name);\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tif (gltf->warnlimit --> 0)\r\n\t\t\t\t\tCon_Printf(CON_WARNING\"%s: Unknown semantic %s for attribute %s\\n\", gltf->mod->name, semantic, v->name);\r\n\t\t\t\treturn false;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\tp = GLTF_FindJSONID(gltf, \"programs\", JSON_FindChild(technique, \"program\"), NULL);\r\n\tvertshader = GLTF1_LoadShader(gltf, JSON_FindChild(p, \"vertexShader\"));\r\n\tfragshader = GLTF1_LoadShader(gltf, JSON_FindChild(p, \"fragmentShader\"));\r\n\r\n\tQ_snprintf(shadertext, shadertextsize,\r\n\t\t\"{\\n\"\r\n\t\t\t\"surfaceparm nodlight\\n\"\t//o.O\r\n\t\t\t\"surfaceparm noshadows\\n\"\t//no surprises please.\r\n\t\t\t\"glslprogram\\n\"\r\n\t\t\t\"{\\n\"\r\n\t\t\t\t\"%s\"\t//header\r\n\t\t\t\t\"%s\"\t//uniformmaps\r\n\t\t\t\t\"#ifdef VERTEX_SHADER\\n\"\r\n\t\t\t\t\t\"%s\"\t//attributemaps\r\n\t\t\t\t\t\"%s\\n\"\t//vertexshader\r\n\t\t\t\t\"#endif\\n\"\r\n\t\t\t\t\"#ifdef FRAGMENT_SHADER\\n\"\r\n\t\t\t\t\t\"%s\\n\"\t//fragmentshader\r\n\t\t\t\t\"#endif\\n\"\r\n\t\t\t\"}\\n\"\r\n\t\t\t\"%s\"\t//{map foo} {map}...\r\n\t\t\"}\\n\",\r\n\t\theader,\r\n\t\tuniforms,\r\n\t\tattributes,\r\n\t\tvertshader,\r\n\t\tfragshader,\r\n\t\tsamplers\r\n\t\t);\r\n\tfree(vertshader);\r\n\tfree(fragshader);\r\n\treturn true;\r\n}\r\n\r\nstatic void GLTF_LoadMaterial(gltf_t *gltf, json_t *materialid, galiasskin_t *ret, qboolean vertexcolours)\r\n{\r\n\tqboolean doubleSided;\r\n\tint alphaMode;\r\n\tdouble alphaCutoff;\r\n\tchar shader[65536];\r\n\tchar alphaCutoffmodifier[128];\r\n\tquintptr_t materialidx;\r\n\tjson_t *mat = GLTF_FindJSONID(gltf, \"materials\", materialid, &materialidx);\r\n\tchar tmp[64];\r\n\tconst char *t;\r\n\r\n\tjson_t *nam, *unlit, *pbrsg, *pbrmr, *blinn;\r\n\tjson_t *transmission, *volume;\r\n\r\n\tnam = JSON_FindChild(mat, \"name\");\r\n\tunlit = JSON_FindChild(mat, \"extensions.KHR_materials_unlit\");\r\n\tpbrsg = JSON_FindChild(mat, \"extensions.KHR_materials_pbrSpecularGlossiness\");\r\n\tpbrmr = JSON_FindChild(mat, \"pbrMetallicRoughness\");\r\n\tblinn = JSON_FindChild(mat, \"extensions.KHR_materials_cmnBlinnPhong\");\r\n\r\n\ttransmission = JSON_FindChild(mat, \"extensions.KHR_materials_transmission\");\r\n\tvolume = JSON_FindChild(mat, \"extensions.KHR_materials_volume\");\r\n\r\n\tif (volume && !transmission)\r\n\t{\r\n\t\tif (gltf->warnlimit --> 0)\r\n\t\t\tCon_Printf(CON_WARNING\"%s: KHR_materials_volume without KHR_materials_transmission\\n\", gltf->mod->name);\r\n\t\tvolume = NULL;\r\n\t}\r\n\tif (transmission && (unlit || pbrsg || blinn) && !pbrmr)\r\n\t{\r\n\t\tif (gltf->warnlimit --> 0)\r\n\t\t\tCon_Printf(CON_WARNING\"%s: KHR_materials_transmission without pbrMetallicRoughness\\n\", gltf->mod->name);\r\n\t\ttransmission = volume = NULL;\r\n\t}\r\n\r\n\tdoubleSided = JSON_GetInteger(mat, \"doubleSided\", false);\r\n\talphaCutoff = JSON_GetFloat(mat, \"alphaCutoff\", 0.5);\r\n\tt = JSON_GetString(mat, \"alphaMode\", tmp, sizeof(tmp), \"OPAQUE\");\r\n\tif (!strcmp(t, \"MASK\"))\r\n\t\talphaMode = 1;\r\n\telse if (!strcmp(t, \"BLEND\"))\r\n\t\talphaMode = 2;\r\n\telse if (!strcmp(t, \"OPAQUE\"))\r\n\t\talphaMode = 0;\r\n\telse\r\n\t{\r\n\t\talphaMode = 0;\r\n\t\tif (gltf->warnlimit --> 0)\r\n\t\t\tCon_Printf(CON_WARNING\"%s: unsupported alphaMode: %s\\n\", gltf->mod->name, t);\r\n\t}\r\n\r\n\tret->numframes = 1;\r\n\tret->skinspeed = 0.1;\r\n\tret->frame = plugfuncs->GMalloc(&gltf->mod->memgroup, sizeof(*ret->frame));\r\n\r\n\t{\r\n\t\tint skip;\r\n\t\tif (nam)\r\n\t\t\tJSON_ReadBody(nam, shader, sizeof(shader));\r\n\t\telse if (mat && *mat->name)\r\n\t\t\tQ_snprintf(shader, sizeof(shader), \"%s\", mat->name);\r\n\t\telse if (!mat)\t//explicit invalid material\r\n\t\t\tQ_snprintf(shader, sizeof(shader), \"\");\r\n\t\telse\r\n\t\t\tQ_snprintf(shader, sizeof(shader), \"%i\", (int)materialidx);\r\n\t\tskip = sizeof(ret->frame->shadername)-32 - strlen(shader);\r\n\t\tif (skip > 0)\r\n\t\t\tskip = 0;\r\n\t\tif (mod_gltf_privatematerials->ival && !strchr(shader, '/'))\r\n\t\t{\r\n\t\t\tQ_snprintf(ret->frame->shadername, sizeof(ret->frame->shadername), \"%s/%u\", gltf->mod->name-skip, (unsigned)materialidx);\r\n\t\t\tQ_strncatz(ret->frame->shadername, \"/\", sizeof(ret->frame->shadername));\r\n\t\t}\r\n\t\telse\r\n\t\t\t*ret->frame->shadername = 0;\r\n\r\n\t\tQ_strncatz(ret->frame->shadername, shader, sizeof(ret->frame->shadername));\r\n\t}\r\n\r\n\tif (alphaMode == 1)\r\n\t\tQ_snprintf(alphaCutoffmodifier, sizeof(alphaCutoffmodifier), \"#ALPHATEST=>%f\", alphaCutoff);\r\n\telse\r\n\t\t*alphaCutoffmodifier = 0;\r\n\r\n\tif (gltf->ver <= 1)\r\n\t{\t//fixme: break\r\n\t\tif (!GLTF1_LoadMaterial(gltf, mat, &ret->frame->texnums, shader, sizeof(shader)))\r\n\t\t{\t//some lame placeholder/fallback.\r\n\t\t\tQ_snprintf(shader, sizeof(shader),\r\n\t\t\t\t\t\"{\\n\"\r\n\t\t\t\t\t\t\"program defaultskin\\n\"\r\n\t\t\t\t\t\"}\\n\"\r\n\t\t\t\t);\r\n\t\t}\r\n\t}\r\n\telse if (unlit)\r\n\t{\t//if this extension was present, then we don't get ANY lighting info.\r\n\t\tjson_t *albedo = JSON_FindChild(pbrmr, \"baseColorTexture.index\");\t//.rgba\r\n\t\tret->frame->texnums.base     = GLTF_LoadTexture(gltf, albedo, 0);\r\n\r\n\t\tQ_snprintf(shader, sizeof(shader),\r\n\t\t\t\"{\\n\"\r\n\t\t\t\t\"surfaceparm nodlight\\n\"\r\n\t\t\t\t\"%s\"//cull\r\n\t\t\t\t\"program default2d%s\\n\"\t//fixme: there's no gpu skeletal stuff with this prog\r\n\t\t\t\t\"{\\n\"\r\n\t\t\t\t\t\"map $diffuse\\n\"\r\n\t\t\t\t\t\"%s\"\t//blend\r\n\t\t\t\t\t\"%s\"\t//rgbgen\r\n\t\t\t\t\"}\\n\"\r\n\t\t\t\t\"fte_basefactor %f %f %f %f\\n\"\r\n\t\t\t\"}\\n\",\r\n\t\t\tdoubleSided?\"cull disable\\n\":\"\",\r\n\t\t\talphaCutoffmodifier,\r\n\t\t\t(alphaMode==1)?\"\":(alphaMode==2)?\"blendfunc blend\\n\":\"\",\r\n\t\t\tvertexcolours?\"rgbgen vertex\\nalphagen vertex\\n\":\"\",\r\n\t\t\tJSON_GetFloat(pbrmr, \"baseColorFactor.0\", 1),\r\n\t\t\t\tJSON_GetFloat(pbrmr, \"baseColorFactor.1\", 1),\r\n\t\t\t\tJSON_GetFloat(pbrmr, \"baseColorFactor.2\", 1),\r\n\t\t\t\tJSON_GetFloat(pbrmr, \"baseColorFactor.3\", 1)\r\n\t\t\t);\r\n\t}\r\n\telse if (blinn)\r\n\t{\r\n\t\tCon_DPrintf(CON_WARNING\"%s: KHR_materials_cmnBlinnPhong implemented according to draft spec\\n\", gltf->mod->name);\r\n\r\n\t\tret->frame->texnums.base     = GLTF_LoadTexture(gltf, JSON_FindChild(pbrsg, \"diffuseTexture.index\"), 0);\r\n\t\tret->frame->texnums.specular = GLTF_LoadTexture(gltf, JSON_FindChild(pbrsg, \"specularGlossinessTexture.index\"), 0);\r\n\r\n\t\t//you wouldn't normally want this, but we have separate factors so lack of a texture is technically valid.\r\n\t\tif (!ret->frame->texnums.base)\r\n\t\t\tret->frame->texnums.base = modfuncs->GetTexture(\"$whiteimage\", NULL, IF_NOMIPMAP|IF_NOPICMIP|IF_NEAREST|IF_NOGAMMA, NULL, NULL, 0, 0, TF_INVALID);\r\n\t\tif (!ret->frame->texnums.specular)\r\n\t\t\tret->frame->texnums.specular = modfuncs->GetTexture(\"$whiteimage\", NULL, IF_NOMIPMAP|IF_NOPICMIP|IF_NEAREST|IF_NOGAMMA, NULL, NULL, 0, 0, TF_INVALID);\r\n\r\n\t\tQ_snprintf(shader, sizeof(shader),\r\n\t\t\t\"{\\n\"\r\n\t\t\t\t\"%s\"//cull\r\n\t\t\t\t\"program defaultskin#VC%s\\n\"\r\n\t\t\t\t\"{\\n\"\r\n\t\t\t\t\t\"map $diffuse\\n\"\r\n\t\t\t\t\t\"%s\"\t//blend\r\n\t\t\t\t\t\"%s\"\t//rgbgen\r\n\t\t\t\t\"}\\n\"\r\n\t\t\t\t\"fte_basefactor %f %f %f %f\\n\"\r\n\t\t\t\t\"fte_specularfactor %f %f %f %f\\n\"\r\n\t\t\t\t\"fte_fullbrightfactor %f %f %f 1.0\\n\"\r\n\t\t\t\"}\\n\",\r\n\t\t\tdoubleSided?\"cull disable\\n\":\"\",\r\n\t\t\talphaCutoffmodifier,\r\n\t\t\t(alphaMode==1)?\"\":(alphaMode==2)?\"blendfunc blend\\n\":\"\",\r\n\t\t\tvertexcolours?\"rgbgen vertex\\nalphagen vertex\\n\":\"\",\r\n\t\t\tJSON_GetFloat(pbrsg, \"diffuseFactor.0\", 1),\r\n\t\t\t\tJSON_GetFloat(pbrsg, \"diffuseFactor.1\", 1),\r\n\t\t\t\tJSON_GetFloat(pbrsg, \"diffuseFactor.2\", 1),\r\n\t\t\t\tJSON_GetFloat(pbrsg, \"diffuseFactor.3\", 1),\r\n\t\t\tJSON_GetFloat(pbrsg, \"specularFactor.0\", 1),\t//FIXME: divide by gl_specular\r\n\t\t\t\tJSON_GetFloat(pbrsg, \"specularFactor.1\", 1),\r\n\t\t\t\tJSON_GetFloat(pbrsg, \"specularFactor.2\", 1),\r\n\t\t\t\tJSON_GetFloat(pbrsg, \"shininessFactor\", 1),\t//FIXME: divide by gl_specular_power\r\n\t\t\tJSON_GetFloat(mat, \"emissiveFactor.0\", 0),\r\n\t\t\t\tJSON_GetFloat(mat, \"emissiveFactor.1\", 0),\r\n\t\t\t\tJSON_GetFloat(mat, \"emissiveFactor.2\", 0)\r\n\t\t\t);\r\n\t}\r\n\telse if (pbrsg)\r\n\t{\t//if this extension was used, then we can use rgb gloss instead of metalness stuff.\r\n\t\tjson_t *occ = JSON_FindChild(mat, \"occlusionTexture.index\");\t//.r\r\n\t\tfloat ior = JSON_GetFloat(mat, \"extensions.KHR_materials_ior.ior\", 1.5);\t//supposedly still relevant here\r\n\t\tret->frame->texnums.base     = GLTF_LoadTexture(gltf, JSON_FindChild(pbrsg, \"diffuseTexture.index\"), 0);\r\n\t\tret->frame->texnums.specular = GLTF_LoadTexture(gltf, JSON_FindChild(pbrsg, \"specularGlossinessTexture.index\"), 0);\r\n\t\tif (occ)\r\n\t\t\tret->frame->texnums.occlusion = GLTF_LoadTexture(gltf, occ, IF_NOSRGB);\r\n\r\n\t\tQ_snprintf(shader, sizeof(shader),\r\n\t\t\t\"{\\n\"\r\n\t\t\t\t\"%s\"//cull\r\n\t\t\t\t\"program defaultskin\" \"#SG\" \"#VC\" \"#IOR=%.02f\" \"%s\"/*occlude*/ \"%s\"/*alphacutoff*/ \"\\n\"\r\n\t\t\t\t\"{\\n\"\r\n\t\t\t\t\t\"map $diffuse\\n\"\r\n\t\t\t\t\t\"%s\"\t//blend\r\n\t\t\t\t\t\"%s\"\t//rgbgen\r\n\t\t\t\t\"}\\n\"\r\n\t\t\t\t\"fte_basefactor %f %f %f %f\\n\"\r\n\t\t\t\t\"fte_specularfactor %f %f %f %f\\n\"\r\n\t\t\t\t\"fte_fullbrightfactor %f %f %f 1.0\\n\"\r\n\t\t\t\t\"bemode rtlight rtlight_sg\\n\"\r\n\t\t\t\"}\\n\",\r\n\t\t\tdoubleSided?\"cull disable\\n\":\"\",\r\n\t\t\tior,\r\n\t\t\t(occ)?\"#OCCLUDE\":\"\",\r\n\t\t\talphaCutoffmodifier,\r\n\t\t\t(alphaMode==1)?\"\":(alphaMode==2)?\"blendfunc blend\\n\":\"\",\r\n\t\t\tvertexcolours?\"rgbgen vertex\\nalphagen vertex\\n\":\"\",\r\n\t\t\tJSON_GetFloat(pbrsg, \"diffuseFactor.0\", 1),\r\n\t\t\t\tJSON_GetFloat(pbrsg, \"diffuseFactor.1\", 1),\r\n\t\t\t\tJSON_GetFloat(pbrsg, \"diffuseFactor.2\", 1),\r\n\t\t\t\tJSON_GetFloat(pbrsg, \"diffuseFactor.3\", 1),\r\n\t\t\tJSON_GetFloat(pbrsg, \"specularFactor.0\", 1),\r\n\t\t\t\tJSON_GetFloat(pbrsg, \"specularFactor.1\", 1),\r\n\t\t\t\tJSON_GetFloat(pbrsg, \"specularFactor.2\", 1),\r\n\t\t\tJSON_GetFloat(pbrsg, \"glossinessFactor\", 1),\r\n\t\t\tJSON_GetFloat(mat, \"emissiveFactor.0\", 0),\r\n\t\t\t\tJSON_GetFloat(mat, \"emissiveFactor.1\", 0),\r\n\t\t\t\tJSON_GetFloat(mat, \"emissiveFactor.2\", 0)\r\n\t\t\t);\r\n\t}\r\n\telse// if (pbrmr)\r\n\t{\t//this is the standard lighting model for gltf2\r\n\t\t//'When not specified, all the default values of pbrMetallicRoughness apply'\r\n\t\tjson_t *albedo = JSON_FindChild(pbrmr, \"baseColorTexture.index\");\t//.rgba\r\n\t\tjson_t *mrt = JSON_FindChild(pbrmr, \"metallicRoughnessTexture.index\");\t//.r = unused, .g = roughness, .b = metalic, .a = unused\r\n\t\tjson_t *occ = JSON_FindChild(mat, \"occlusionTexture.index\");\t//.r\r\n\t\tjson_t *n;\r\n\t\tchar occname[MAX_QPATH];\r\n\t\tchar mrtname[MAX_QPATH];\r\n\t\tfloat ior = JSON_GetFloat(mat, \"extensions.KHR_materials_ior.ior\", 1.5);\r\n\r\n\t\tif (JSON_GetInteger(pbrmr, \"baseColorTexture.texCoord\", 0) != 0)\r\n\t\t\tif (gltf->warnlimit --> 0)\r\n\t\t\t\tCon_Printf(\"%s: Unsupported baseColorTexture texCoord value\\n\", gltf->mod->name);\r\n\t\tif (JSON_GetInteger(pbrmr, \"metallicRoughnessTexture.texCoord\", 0) != 0)\r\n\t\t\tif (gltf->warnlimit --> 0)\r\n\t\t\t\tCon_Printf(\"%s: Unsupported metallicRoughnessTexture texCoord value\\n\", gltf->mod->name);\r\n\t\tif (JSON_GetInteger(mat, \"occlusionTexture.texCoord\", 0) != 0)\r\n\t\t\tif (gltf->warnlimit --> 0)\r\n\t\t\t\tCon_Printf(\"%s: Unsupported occlusionTexture texCoord value\\n\", gltf->mod->name);\r\n\r\n\t\t//now work around potential lame exporters (yay dds?).\r\n\t\tn = JSON_FindChild(mat, \"extensions.MSFT_packing_occlusionRoughnessMetallic.occlusionRoughnessMetallicTexture.index\");\r\n\t\tif (n)\r\n\t\t\tocc = n;\r\n\t\tn = JSON_FindChild(mat, \"extensions.MSFT_packing_occlusionRoughnessMetallic.occlusionRoughnessMetallicTexture.index\");\r\n\t\tif (n)\r\n\t\t\tmrt = n;\r\n\r\n\t\t//ideally we use the ORM.r for the occlusion map, but some people just love being annoying.\r\n\t\tJSON_ReadBody(occ, occname, sizeof(occname));\r\n\t\tJSON_ReadBody(mrt, mrtname, sizeof(mrtname));\r\n\t\tif (strcmp(occname,mrtname) && occ)\r\n\t\t\tret->frame->texnums.occlusion = GLTF_LoadTexture(gltf, occ, IF_NOSRGB);\r\n\r\n\t\t//note: extensions.MSFT_packing_normalRoughnessMetallic.normalRoughnessMetallicTexture.index gives rg=normalxy, b=roughness, .a=metalic\r\n\t\t//(would still need an ao map, and probably wouldn't work well as bc3 either)\r\n\r\n\t\tif (albedo)\r\n\t\t\tret->frame->texnums.base     = GLTF_LoadTexture(gltf, albedo, 0);\r\n\t\tif (mrt)\r\n\t\t\tret->frame->texnums.specular = GLTF_LoadTexture(gltf, mrt, IF_NOSRGB);\r\n\t\telse\t//else depend upon specularfactor\r\n\t\t\tret->frame->texnums.specular = modfuncs->GetTexture(\"$whiteimage\", NULL, IF_NOMIPMAP|IF_NOPICMIP|IF_NEAREST|IF_NOGAMMA, NULL, NULL, 0, 0, TF_INVALID);\r\n\r\n\t\tif (transmission)\r\n\t\t{\r\n\t\t\tn = JSON_FindChild(transmission, \"transmissionTexture.index\");\t//.r = factor\r\n\t\t\tif (n)\r\n\t\t\t\tret->frame->texnums.transmission = GLTF_LoadTexture(gltf, n, IF_NOSRGB);\r\n\t\t\telse\r\n\t\t\t\tret->frame->texnums.transmission = modfuncs->GetTexture(\"$whiteimage\", NULL, IF_NOMIPMAP|IF_NOPICMIP|IF_NEAREST|IF_NOGAMMA, NULL, NULL, 0, 0, TF_INVALID);\r\n\r\n\t\t\tif (volume)\r\n\t\t\t{\r\n\t\t\t\tn = JSON_FindChild(volume, \"thicknessTexture.index\");\t//.g = thicknessFactor\r\n\t\t\t\tif (n)\r\n\t\t\t\t\tret->frame->texnums.transmission = GLTF_LoadTexture(gltf, n, IF_NOSRGB);\r\n\t\t\t\telse\r\n\t\t\t\t\tret->frame->texnums.transmission = modfuncs->GetTexture(\"$whiteimage\", NULL, IF_NOMIPMAP|IF_NOPICMIP|IF_NEAREST|IF_NOGAMMA, NULL, NULL, 0, 0, TF_INVALID);\r\n\t\t\t}\r\n\t\t}\r\n#ifndef INFINITY\t//C99.\r\n#define INFINITY (1.0/0.0)\r\n#endif\r\n\t\tQ_snprintf(shader, sizeof(shader),\r\n\t\t\t\"{\\n\"\r\n\t\t\t\t\"%s\"//cull\r\n\t\t\t\t\"program defaultskin\" \"#ORM\" \"#VC\" \"#IOR=%.02f\" \"%s\"/*occlude*/ \"%s\"/*transmission*/ \"%s\"/*volume*/ \"%s\"/*alphatest*/ \"\\n\"\r\n\t\t\t\t\"{\\n\"\r\n\t\t\t\t\t\"map $diffuse\\n\"\r\n\t\t\t\t\t\"%s\"\t//blend\r\n\t\t\t\t\t\"%s\"\t//rgbgen\r\n\t\t\t\t\"}\\n\"\r\n\t\t\t\t\"fte_basefactor %f %f %f %f\\n\"\r\n\t\t\t\t\"fte_specularfactor %f %f %f 1.0\\n\"\r\n\t\t\t\t\"fte_fullbrightfactor %f %f %f 1.0\\n\"\r\n\t\t\t\t\"fte_transmissionfactor %f\\n\"\r\n\t\t\t\t\"fte_volumefactor %f %f %f %f %f\\n\"\r\n\r\n\t\t\t\t\"bemode rtlight rtlight_orm\\n\"\r\n\t\t\t\"}\\n\",\r\n\t\t\tdoubleSided?\"cull disable\\n\":\"\",\r\n\t\t\tior,\r\n\t\t\t(!occ)?\"#NOOCCLUDE\":(strcmp(occname,mrtname)?\"#OCCLUDE\":\"\"),\r\n\t\t\t(transmission?\"#USE_TRANSMISSION\":\"\"),\r\n\t\t\t(volume?\"#USE_VOLUME\":\"\"),\r\n\t\t\talphaCutoffmodifier,\r\n\t\t\t(alphaMode==1)?\"\":(alphaMode==2)?\"blendfunc blend\\n\":\"\",\r\n\t\t\tvertexcolours?\"rgbgen vertex\\nalphagen vertex\\n\":\"\",\r\n\t\t\tJSON_GetFloat(pbrmr, \"baseColorFactor.0\", 1),\r\n\t\t\t\tJSON_GetFloat(pbrmr, \"baseColorFactor.1\", 1),\r\n\t\t\t\tJSON_GetFloat(pbrmr, \"baseColorFactor.2\", 1),\r\n\t\t\t\tJSON_GetFloat(pbrmr, \"baseColorFactor.3\", 1),\r\n\t\t\tJSON_GetFloat(mat, \"occlusionTexture.strength\", 1),\r\n\t\t\t\tJSON_GetFloat(pbrmr, \"metallicFactor\", 1),\r\n\t\t\t\tJSON_GetFloat(pbrmr, \"roughnessFactor\", 1),\r\n\t\t\tJSON_GetFloat(mat, \"emissiveFactor.0\", 0),\r\n\t\t\t\tJSON_GetFloat(mat, \"emissiveFactor.1\", 0),\r\n\t\t\t\tJSON_GetFloat(mat, \"emissiveFactor.2\", 0),\r\n\t\t\tJSON_GetFloat(transmission, \"transmissionFactor\", 0),\r\n\t\t\tJSON_GetFloat(volume, \"attenuationColor.0\", 1),\r\n\t\t\t\tJSON_GetFloat(volume, \"attenuationColor.1\", 1),\r\n\t\t\t\tJSON_GetFloat(volume, \"attenuationColor.2\", 1),\r\n\t\t\t\tJSON_GetFloat(volume, \"thicknessFactor\", 0),\r\n\t\t\t\tJSON_GetFloat(volume, \"attenuationDistance\", INFINITY)\r\n\t\t\t);\r\n\t}\r\n\tif (!ret->frame->texnums.bump)\r\n\t\tret->frame->texnums.bump = GLTF_LoadTexture(gltf, JSON_FindChild(mat, \"normalTexture.index\"), IF_NOSRGB|IF_TRYBUMP);\r\n\tif (!ret->frame->texnums.fullbright)\r\n\t\tret->frame->texnums.fullbright = GLTF_LoadTexture(gltf, JSON_FindChild(mat, \"emissiveTexture.index\"), 0);\r\n\r\n\tif (!ret->frame->texnums.base)\r\n\t\tret->frame->texnums.base = modfuncs->GetTexture(\"$whiteimage\", NULL, IF_NOMIPMAP|IF_NOPICMIP|IF_NEAREST|IF_NOGAMMA, NULL, NULL, 0, 0, TF_INVALID);\r\n\r\n\tret->frame->defaultshader = memcpy(plugfuncs->GMalloc(&gltf->mod->memgroup, strlen(shader)+1), shader, strlen(shader)+1);\r\n\r\n\tQ_strlcpy(ret->name, ret->frame->shadername, sizeof(ret->name));\r\n}\r\n#endif\r\n\r\n#ifdef HAVE_DRACO\r\n\t#define DRACO_API_ONLY\r\n\t#include \"draco.cpp\"\r\n#endif\r\ntypedef struct {\r\n\tgltf_t *gltf;\r\n\tjson_t *prim;\r\n\tjson_t *primattrs;\r\n#ifdef HAVE_DRACO\r\n\tjson_t *dracoattrs;\r\n\tstruct ftedracofuncs_s *draco;\r\n#endif\r\n} gltf_prim_t;\r\nstatic qboolean GLTF_GetAttributeAccessor(gltf_prim_t *state, char *attributename, struct gltf_accessor *out)\r\n{\r\n\tjson_t *primaccessor = JSON_FindChild(state->primattrs, attributename);\r\n#ifdef HAVE_DRACO\r\n\tif (state->draco && primaccessor)\r\n\t{\r\n\t\tjson_t *da = JSON_FindChild(state->dracoattrs, attributename);\r\n\t\tif (da)\r\n\t\t{\t//comes from compressed data instead.\r\n\t\t\t//we're still meant to require some attributes match the original accessors.\r\n\t\t\tstruct ftedracoattr_s *dattr;\r\n\t\t\tjson_t *a, *mins, *maxs;\r\n\t\t\tunsigned int j, attridx;\r\n\t\t\tmemset(out, 0, sizeof(*out));\r\n\r\n\t\t\tif (!strcmp(attributename, \"NORMAL\") || !strcmp(attributename, \"TANGENT\"))\r\n\t\t\t\treturn false; //these come out shite. don't use.\r\n\r\n\t\t\ta = GLTF_FindJSONID(state->gltf, \"accessors\", primaccessor, NULL);\r\n\t\t\tif (!a)\r\n\t\t\t\treturn false;\r\n\t\t\tj = JSON_GetInteger(da, NULL, -1);\r\n\t\t\tfor (attridx = 0; attridx < state->draco->num_attribs; attridx++)\r\n\t\t\t\tif (state->draco->attrib[attridx].uniqueid == j)\r\n\t\t\t\t\tbreak;\r\n\t\t\tif (attridx >= state->draco->num_attribs)\r\n\t\t\t{\r\n\t\t\t\tif (state->gltf->warnlimit --> 0)\r\n\t\t\t\t\tCon_Printf(CON_WARNING\"%s: draco lacks specified uniqueid %i\\n\", state->gltf->mod->name, j);\r\n\t\t\t\treturn false;\r\n\t\t\t}\r\n\r\n\t\t\tJSON_FlagAsUsed(a, \"name\");\r\n\r\n\t\t\tout->bytestride = 0; //\r\n\t\t\tout->componentType = JSON_GetInteger(a, \"componentType\", 0);\r\n\t\t\tout->normalized = JSON_GetInteger(a, \"normalized\", false);\r\n\t\t\tout->count = JSON_GetInteger(a, \"count\", 0);\r\n\t\t\tif (JSON_Equals(a, \"type\", \"SCALAR\"))\r\n\t\t\t\tout->type = (1<<8) | 1;\r\n\t\t\telse if (JSON_Equals(a, \"type\", \"VEC2\"))\r\n\t\t\t\tout->type = (1<<8) | 2;\r\n\t\t\telse if (JSON_Equals(a, \"type\", \"VEC3\"))\r\n\t\t\t\tout->type = (1<<8) | 3;\r\n\t\t\telse if (JSON_Equals(a, \"type\", \"VEC4\"))\r\n\t\t\t\tout->type = (1<<8) | 4;\r\n\t\t\telse if (JSON_Equals(a, \"type\", \"MAT2\"))\r\n\t\t\t\tout->type = (2<<8) | 2;\r\n\t\t\telse if (JSON_Equals(a, \"type\", \"MAT3\"))\r\n\t\t\t\tout->type = (3<<8) | 3;\r\n\t\t\telse if (JSON_Equals(a, \"type\", \"MAT4\"))\r\n\t\t\t\tout->type = (4<<8) | 4;\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tif (state->gltf->warnlimit --> 0)\r\n\t\t\t\t\tCon_Printf(CON_WARNING\"%s: glTF2 unsupported type\\n\", state->gltf->mod->name);\r\n\t\t\t\tout->type = 1;\r\n\t\t\t}\r\n\r\n\t\t\tif (!out->bytestride)\r\n\t\t\t{\r\n\t\t\t\tout->bytestride = (out->type & 0xff) * (out->type>>8);\r\n\t\t\t\tswitch(out->componentType)\r\n\t\t\t\t{\r\n\t\t\t\tdefault:\r\n\t\t\t\t\tif (state->gltf->warnlimit --> 0)\r\n\t\t\t\t\t\tCon_Printf(CON_WARNING\"GLTF_GetAccessor: %s: glTF2 unsupported componentType (%i)\\n\", state->gltf->mod->name, out->componentType);\r\n\t\t\t\tcase 5120:\t//BYTE\r\n\t\t\t\tcase 5121:\t//UNSIGNED_BYTE\r\n\t\t\t\t\tbreak;\r\n\t\t\t\tcase 5122: //SHORT\r\n\t\t\t\tcase 5123: //UNSIGNED_SHORT\r\n\t\t\t\t\tout->bytestride *= 2;\r\n\t\t\t\t\tbreak;\r\n\t\t\t\tcase 5125: //UNSIGNED_INT\r\n\t\t\t\tcase 5126: //FLOAT\r\n\t\t\t\t\tout->bytestride *= 4;\r\n\t\t\t\t\tbreak;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\r\n\t\t\tmins = JSON_FindChild(a, \"min\");\r\n\t\t\tmaxs = JSON_FindChild(a, \"max\");\r\n\t\t\tfor (j = 0; j < (out->type>>8)*(out->type&0xff); j++)\r\n\t\t\t{\t//'must' be set in various situations.\r\n\t\t\t\tout->mins[j] = JSON_GetIndexedFloat(mins, j, 0);\r\n\t\t\t\tout->maxs[j] = JSON_GetIndexedFloat(maxs, j, 0);\r\n\t\t\t}\r\n\r\n\t\t\tGLTF_FlagExtras(a);\r\n\r\n\t\t\tdattr = &state->draco->attrib[attridx];\r\n\r\n\t\t\tif (out->count < state->draco->num_vertexes)\r\n\t\t\t\tout->count = state->draco->num_vertexes;\t//hack. seems to be needed for one of our test models.\r\n\t\t\tif (out->count != state->draco->num_vertexes ||\r\n\t\t\t\t!out->normalized != !dattr->isnormalised ||\r\n\t\t\t\tout->componentType != dattr->type ||\r\n\t\t\t\tout->type != ((1<<8)|dattr->components) ||\r\n\t\t\t\tout->bytestride != dattr->bytestride)\r\n\t\t\t{\r\n\t\t\t\tif (state->gltf->warnlimit --> 0)\r\n\t\t\t\t\tCon_Printf(CON_WARNING\"%s: %s (%i) draco/accessor mismatch\\n\", state->gltf->mod->name, attributename, dattr->usage);\r\n\t\t\t\tmemset(out, 0, sizeof(*out));\t//abort! abort!\r\n\t\t\t\treturn false;\r\n\t\t\t}\r\n\r\n\t\t\tout->data = dattr->ptr;\r\n\t\t\tout->length = out->bytestride*out->count;\r\n\t\t\treturn true;\r\n\t\t}\r\n\t}\r\n#endif\r\n\treturn GLTF_GetAccessor(state->gltf, primaccessor, out);\r\n}\r\nstatic void GLTF_GetIndiciesAccessor(gltf_prim_t *state, struct gltf_accessor *out)\r\n{\r\n#ifdef HAVE_DRACO\r\n\tif (state->draco)\r\n\t{\r\n\t\tmemset(out->mins, 0, sizeof(out->mins));\r\n\t\tmemset(out->maxs, 0, sizeof(out->maxs));\r\n\t\tout->componentType = 5125;\t//unsigned int\r\n\t\tout->normalized = false;\r\n\t\tout->type = (1<<8) | 1; //'scaler'\r\n\t\tout->bytestride = sizeof(*state->draco->ptr_indexes);\r\n\r\n\t\tout->count = state->draco->num_indexes;\r\n\t\tout->data = state->draco->ptr_indexes;\r\n\t\tout->length = out->bytestride*out->count;\r\n\t\treturn;\r\n\t}\r\n#endif\r\n\tGLTF_GetAccessor(state->gltf, JSON_FindChild(state->prim, \"indices\"), out);\r\n}\r\n\r\nstatic const float *QDECL GLTF_AnimateMorphs(const galiasinfo_t *surf, const framestate_t *framestate, float *morphs);\r\nstatic qboolean GLTF_ProcessMesh(gltf_t *gltf, json_t *meshid, int basebone, double skinmatrix[])\r\n{\r\n\tmodel_t *mod = gltf->mod;\r\n\tquintptr_t meshidx;\r\n\tjson_t *mesh = GLTF_FindJSONID(gltf, \"meshes\", meshid, &meshidx);\r\n\tjson_t *primnode;\r\n\tjson_t *meshname = JSON_FindChild(mesh, \"name\");\r\n\tjson_t *weights = NULL;\r\n\tsize_t morphtargets = 0;\r\n\r\n\tweights = JSON_FindChild(mesh, \"weights\");\r\n\tGLTF_FlagExtras(mesh);\r\n\r\n\tfor(primnode = JSON_FindIndexedChild(mesh, \"primitives\", 0); primnode; primnode = primnode->sibling)\r\n\t{\r\n\t\tint mode  = JSON_GetInteger(primnode, \"mode\", 4);\r\n\t\tjson_t *attr = JSON_FindChild(primnode, \"attributes\");\r\n\t\tjson_t *targets = JSON_FindChild(primnode, \"targets\");\r\n\t\tstruct gltf_accessor tc_0, tc_1, norm, tang, vpos, col0, idx, sidx, swgt;\r\n\t\tstruct\r\n\t\t{\r\n\t\t\tstruct gltf_accessor vpos, norm, tang;\r\n\t\t} *morph = NULL;\r\n\t\tgaliasinfo_t *surf;\r\n\t\tsize_t i, j;\r\n\t\tindex_t maxvert;\r\n\r\n\t\tgltf_prim_t prim = {gltf, primnode, attr};\r\n\r\n#ifdef HAVE_DRACO\r\n #define PRIMCLEANUP() do{if (prim.draco) prim.draco->Release(prim.draco);  free(morph);}while(0)\t\t//frees memory allocations from inside this loop\r\n\t\tjson_t *draconode = JSON_FindChild(primnode, \"extensions.KHR_draco_mesh_compression\");\r\n\t\tif (draconode)\r\n\t\t{\t//decompress the ext.bufferview and replace matching primative.attributes[n] with any listed ext.attributes[n] entries, and the indicies\r\n\t\t\t//accessor's componentType, type, count should match that from draco.\r\n\t\t\tstruct gltf_bufferview bv;\r\n\t\t\tif (!GLTF_GetBufferViewData(gltf, JSON_FindChild(draconode, \"bufferView\"), &bv))\r\n\t\t\t{\r\n\t\t\t\tif (gltf->warnlimit --> 0)\r\n\t\t\t\t\tCon_Printf(CON_WARNING \"%s: KHR_draco_mesh_compression without bufferview\\n\", gltf->mod->name);\r\n\t\t\t\tcontinue;\r\n\t\t\t}\r\n\t\t\tprim.draco = Draco_Decode(bv.data, bv.length);\r\n\t\t\tif (!prim.draco)\r\n\t\t\t{\r\n\t\t\t\tif (gltf->warnlimit --> 0)\r\n\t\t\t\t\tCon_Printf(CON_WARNING \"%s: KHR_draco_mesh_compression decompression failure\\n\", gltf->mod->name);\t//in case a model tries supplying more. we ought to renormalise the weights in this case.\r\n\t\t\t\tcontinue;\r\n\t\t\t}\r\n\t\t\tprim.dracoattrs = JSON_FindChild(draconode, \"attributes\");\r\n\t\t}\r\n#else\r\n #define PRIMCLEANUP() do{free(morph);}while(0)\r\n#endif\r\n\r\n\t\tprimnode->used = true;\r\n\r\n\t\tswitch(mode)\r\n\t\t{\r\n\t\tcase 4:\r\n\t\t\tbreak;\r\n\t\tcase 0: //points\r\n\t\tcase 1: //lines\r\n\t\tcase 2: //line loop\r\n\t\tcase 3: //line strip\r\n\t\tcase 5: //triangle strip -- FIXME: probably relevant (with degenerates)\r\n\t\tcase 6: //triangle fan\r\n\t\tdefault:\r\n\t\t\tif (gltf->warnlimit --> 0)\r\n\t\t\t\tCon_Printf(\"Primitive mode %i not supported\\n\", mode);\r\n\t\t\tPRIMCLEANUP();\r\n\t\t\tcontinue;\r\n\t\t}\r\n\r\n\t\ti = JSON_GetCount(targets);\r\n\t\tif (i != morphtargets)\r\n\t\t{\r\n\t\t\tif (morphtargets == 0)\r\n\t\t\t\tmorphtargets = i;\r\n\t\t\telse if (gltf->warnlimit --> 0)\r\n\t\t\t\tCon_Printf(CON_WARNING\"morphtargets count changed between primitives\\n\");\r\n\t\t}\r\n\t\tif (morphtargets)\r\n\t\t{\r\n\t\t\tmorph = malloc(sizeof(*morph) * morphtargets);\r\n\t\t\tfor (i = 0; i < morphtargets; i++)\r\n\t\t\t{\r\n\t\t\t\tjson_t *target = JSON_GetIndexed(targets, i);\r\n\t\t\t\tGLTF_GetAccessor(gltf, JSON_FindChild(target, \"POSITION\"), &morph[i].vpos);\r\n\t\t\t\tGLTF_GetAccessor(gltf, JSON_FindChild(target, \"NORMAL\"), &morph[i].norm);\r\n\t\t\t\tGLTF_GetAccessor(gltf, JSON_FindChild(target, \"TANGENT\"), &morph[i].tang);\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tGLTF_FlagExtras(primnode);\r\n\r\n\t\tGLTF_GetAttributeAccessor(&prim, \"POSITION\",\t\t&vpos);\t//float\r\n\t\tif (!vpos.count)\r\n\t\t{\r\n\t\t\tPRIMCLEANUP();\r\n\t\t\tcontinue;\r\n\t\t}\r\n\t\tGLTF_GetAttributeAccessor(&prim, \"TEXCOORD_0\",\t\t&tc_0);\t//float, ubyte, ushort\r\n\t\tGLTF_GetAttributeAccessor(&prim, \"TEXCOORD_1\",\t\t&tc_1);\t//float, ubyte, ushort\r\n\t\tGLTF_GetAttributeAccessor(&prim, \"NORMAL\",\t\t\t&norm);\t//float\r\n\t\tGLTF_GetAttributeAccessor(&prim, \"TANGENT\",\t\t&tang);\t//float\r\n\t\tGLTF_GetAttributeAccessor(&prim, \"COLOR_0\",\t\t&col0);\t//float, ubyte, ushort\r\n\t\tGLTF_GetIndiciesAccessor(&prim, &idx);\r\n\t\tif (gltf->ver <= 1)\r\n\t\t{\r\n\t\t\tGLTF_GetAttributeAccessor(&prim, \"JOINT\",\t&sidx);\t//ubyte, ushort\r\n\t\t\tGLTF_GetAttributeAccessor(&prim, \"WEIGHT\",\t&swgt);\t//float, ubyte, ushort\r\n\t\t}\r\n\t\telse\r\n\t\t{\t//potentially multiple, each a vec4.\r\n\t\t\tGLTF_GetAttributeAccessor(&prim, \"JOINTS_0\",\t&sidx);\t//ubyte, ushort\r\n\t\t\tGLTF_GetAttributeAccessor(&prim, \"WEIGHTS_0\",\t&swgt);\t//float, ubyte, ushort\r\n\t\t}\r\n\r\n\t\tif (JSON_GetInteger(attr, \"JOINTS_1\",\t-1) != -1 || JSON_GetInteger(attr, \"WEIGHTS_1\",\t-1) != -1)\r\n\t\t\tif (gltf->warnlimit --> 0)\r\n\t\t\t\tCon_Printf(CON_WARNING \"%s: only 4 bones supported per vert\\n\", gltf->mod->name);\t//in case a model tries supplying more. we ought to renormalise the weights in this case.\r\n\r\n\t\tsurf = plugfuncs->GMalloc(&mod->memgroup, sizeof(*surf) + (morphtargets*sizeof(float)));\r\n\r\n\t\tsurf->surfaceid = JSON_GetInteger(primnode, \"extras.fte.surfaceid\", meshidx);\r\n\t\tsurf->contents = JSON_GetInteger(primnode, \"extras.fte.contents\", FTECONTENTS_BODY);\r\n\t\tsurf->csurface.flags = JSON_GetInteger(primnode, \"extras.fte.surfaceflags\", 0);\r\n\t\tsurf->geomset = JSON_GetInteger(primnode, \"extras.fte.geomset\", ~0u);\r\n\t\tsurf->geomid = JSON_GetInteger(primnode, \"extras.fte.geomid\", 0);\r\n\t\tsurf->mindist = JSON_GetInteger(primnode, \"extras.fte.mindist\", 0);\r\n\t\tsurf->maxdist = JSON_GetInteger(primnode, \"extras.fte.maxdist\", 0);\r\n\r\n\t\tsurf->shares_bones = gltf->numsurfaces;\r\n\t\tsurf->shares_verts = gltf->numsurfaces;\r\n\t\tJSON_ReadBody(meshname, surf->surfacename, sizeof(surf->surfacename));\r\n\r\n\t\tsurf->numverts = vpos.count;\r\n\t\tif (idx.data)\r\n\t\t{\r\n\t\t\tsurf->numindexes = idx.count;\r\n\t\t\tsurf->ofs_indexes = plugfuncs->GMalloc(&mod->memgroup, sizeof(*surf->ofs_indexes) * idx.count);\r\n\t\t\tif (idx.componentType == 5123)\r\n\t\t\t{\t//unsigned shorts\r\n\t\t\t\tfor (i = 0; i < idx.count; i++)\r\n\t\t\t\t\tsurf->ofs_indexes[i] = *(unsigned short *)((char*)idx.data + i*idx.bytestride);\r\n\t\t\t}\r\n\t\t\telse if (idx.componentType == 5121)\r\n\t\t\t{\t//unsigned bytes\r\n\t\t\t\tfor (i = 0; i < idx.count; i++)\r\n\t\t\t\t\tsurf->ofs_indexes[i] = *(unsigned char *)((char*)idx.data + i*idx.bytestride);\r\n\t\t\t}\r\n\t\t\telse if (idx.componentType == 5125)\r\n\t\t\t{\t//unsigned ints. -- FIXME: catch overflows.\r\n\t\t\t\tfor (i = 0; i < idx.count; i++)\r\n\t\t\t\t\tsurf->ofs_indexes[i] = *(unsigned int *)((char*)idx.data + i*idx.bytestride);\t//FIXME: bounds check.\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tPRIMCLEANUP();\r\n\t\t\t\tcontinue;\r\n\t\t\t}\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tsurf->numindexes = surf->numverts;\r\n\t\t\tsurf->ofs_indexes = plugfuncs->GMalloc(&mod->memgroup, sizeof(*surf->ofs_indexes) * surf->numverts);\r\n\t\t\tfor (i = 0; i < surf->numverts; i++)\r\n\t\t\t\tsurf->ofs_indexes[i] = i;\r\n\t\t}\r\n\r\n\t\t//swap winding order. we cull wrongly.\r\n\t\tfor (i = 0; i < idx.count; i+=3)\r\n\t\t{\r\n\t\t\tindex_t t = surf->ofs_indexes[i+0];\r\n\t\t\tsurf->ofs_indexes[i+0] = surf->ofs_indexes[i+2];\r\n\t\t\tsurf->ofs_indexes[i+2] = t;\r\n\t\t}\r\n\r\n\t\tfor (maxvert = 0, i = 0; i < idx.count; i++)\r\n\t\t\tif (maxvert < surf->ofs_indexes[i])\r\n\t\t\t\tmaxvert = surf->ofs_indexes[i];\r\n\t\tif (maxvert >= surf->numverts)\r\n\t\t{\r\n\t\t\tCon_Printf(CON_WARNING \"%s: %s Index list exceeds vertex count range\\n\", gltf->mod->name, surf->surfacename);\t//in case a model tries supplying more. we ought to renormalise the weights in this case.\r\n\t\t\tPRIMCLEANUP();\r\n\t\t\tcontinue;\r\n\t\t}\r\n\r\n\t\tsurf->AnimateMorphs = GLTF_AnimateMorphs;\r\n\t\tfor (i = 0; i < morphtargets; i++)\r\n\t\t\t((float*)(surf+1))[i] = JSON_GetIndexedFloat(weights, i, 0);\r\n\t\tsurf->nummorphs = morphtargets;\r\n\t\tsurf->ofs_skel_xyz = plugfuncs->GMalloc(&mod->memgroup, (sizeof(*surf->ofs_skel_xyz)+sizeof(*surf->ofs_skel_norm)+sizeof(*surf->ofs_skel_svect)+sizeof(*surf->ofs_skel_tvect)) * surf->numverts * (1+morphtargets));\r\n\t\tsurf->ofs_skel_norm = (vec3_t*)(surf->ofs_skel_xyz+surf->numverts*(1+morphtargets));\r\n\t\tsurf->ofs_skel_svect = (vec3_t*)(surf->ofs_skel_norm+surf->numverts*(1+morphtargets));\r\n\t\tsurf->ofs_skel_tvect = (vec3_t*)(surf->ofs_skel_svect+surf->numverts*(1+morphtargets));\r\n\r\n\t\tsurf->ofs_skel_xyz\t\t= GLTF_AccessorToDataF(gltf, surf->numverts, countof(surf->ofs_skel_xyz[0]),\t\t&vpos, surf->ofs_skel_xyz);\r\n\t\tsurf->ofs_skel_norm\t\t= GLTF_AccessorToDataF(gltf, surf->numverts, countof(surf->ofs_skel_norm[0]),\t&norm, surf->ofs_skel_norm);\t\t\t//if no normals, normals should be flat (fragment shader or unwelding the verts...)\r\n\t\tGLTF_AccessorToTangents(gltf, surf->ofs_skel_norm, surf->numverts, &tang, surf->ofs_skel_svect, surf->ofs_skel_tvect);\r\n\r\n\t\tfor (i = 0; i < morphtargets; i++)\r\n\t\t{\r\n\t\t\tsize_t offset = (i+1) * surf->numverts;\r\n\t\t\t/*json_t *tname = JSON_FindIndexedChild(mesh, \"extras.targetNames\", i);\r\n\t\t\tif (tname)\r\n\t\t\t{\r\n\t\t\t\tsize_t nsize = JSON_ReadBody(tname, NULL, 0)+1;\r\n\t\t\t\tsurf->morphname[i] = plugfuncs->GMalloc(&mod->memgroup, nsize);\r\n\t\t\t\tJSON_ReadBody(tname, surf->morphname[i], nsize);\r\n\t\t\t}*/\r\n\t\t\tGLTF_AccessorToDataF(gltf, surf->numverts, countof(surf->ofs_skel_xyz[0]),\t&morph[i].vpos, surf->ofs_skel_xyz+offset);\r\n\t\t\tGLTF_AccessorToDataF(gltf, surf->numverts, countof(surf->ofs_skel_norm[0]),\t&morph[i].norm, surf->ofs_skel_norm+offset);\t\t\t//if no normals, normals should be flat (fragment shader or unwelding the verts...)\r\n\t\t\tGLTF_AccessorToTangents(gltf, surf->ofs_skel_norm+offset, surf->numverts,   &morph[i].tang, surf->ofs_skel_svect+offset, surf->ofs_skel_tvect+offset);\r\n\t\t}\r\n\t\tsurf->meshrootbone = basebone;\t//needed for morph anims\r\n\r\n\t\tsurf->ofs_st_array\t\t= GLTF_AccessorToDataF(gltf, surf->numverts, countof(surf->ofs_st_array[0]),\t\t&tc_0, NULL);\r\n\t\tif (tc_1.data)\r\n\t\t\tsurf->ofs_lmst_array\t= GLTF_AccessorToDataF(gltf, surf->numverts, countof(surf->ofs_lmst_array[0]),\t&tc_1, NULL);\r\n\t\tif (col0.data && col0.componentType == 5121)\t//UNSIGNED_BYTE\r\n\t\t\tsurf->ofs_rgbaub\t= GLTF_AccessorToDataUB(gltf, surf->numverts, countof(surf->ofs_rgbaub[0]),\t\t&col0);\r\n\t\telse if (col0.data)\r\n\t\t\tsurf->ofs_rgbaf\t\t= GLTF_AccessorToDataF(gltf, surf->numverts, countof(surf->ofs_rgbaf[0]),\t\t&col0, NULL);\r\n\t\t/*else\r\n\t\t{\r\n\t\t\tsurf->ofs_rgbaub = plugfuncs->GMalloc(&gltf->mod->memgroup, sizeof(*surf->ofs_rgbaub) * surf->numverts);\r\n\t\t\tmemset(surf->ofs_rgbaub, 0xff, sizeof(*surf->ofs_rgbaub) * surf->numverts);\r\n\t\t}*/\r\n\t\tif (sidx.data && swgt.data)\r\n\t\t{\r\n\t\t\tsurf->ofs_skel_idx\t\t= GLTF_AccessorToDataBone(gltf,surf->numverts, &sidx);\r\n\t\t\tsurf->ofs_skel_weight\t= GLTF_AccessorToDataF(gltf, surf->numverts, countof(surf->ofs_skel_weight[0]),\t&swgt, NULL);\r\n\r\n\t\t\tfor (i = 0; i < surf->numverts; i++)\r\n\t\t\t{\r\n\t\t\t\tfloat len = surf->ofs_skel_weight[i][0]+surf->ofs_skel_weight[i][1]+surf->ofs_skel_weight[i][2]+surf->ofs_skel_weight[i][3];\r\n\t\t\t\tif (len)\r\n\t\t\t\t\tVector4Scale(surf->ofs_skel_weight[i], 1/len, surf->ofs_skel_weight[i]);\r\n\t\t\t\telse\r\n\t\t\t\t\tVector4Set(surf->ofs_skel_weight[i], 0.5, 0.5, 0.5, 0.5);\r\n\t\t\t}\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tsurf->ofs_skel_idx = plugfuncs->GMalloc(&gltf->mod->memgroup, sizeof(surf->ofs_skel_idx[0]) * surf->numverts);\r\n\t\t\tsurf->ofs_skel_weight = plugfuncs->GMalloc(&gltf->mod->memgroup, sizeof(surf->ofs_skel_weight[0]) * surf->numverts);\r\n\t\t\tfor (i = 0; i < surf->numverts; i++)\r\n\t\t\t{\r\n\t\t\t\tVector4Set(surf->ofs_skel_idx[i], basebone, 0, 0, 0);\r\n\t\t\t\tVector4Set(surf->ofs_skel_weight[i], 1, 0, 0, 0);\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tif (skinmatrix)\r\n\t\t{\r\n\t\t\tTransformPosArray(surf->ofs_skel_xyz, surf->numverts, skinmatrix);\r\n\t\t\tif (norm.data)\r\n\t\t\t\tTransformDirArray(surf->ofs_skel_norm, surf->numverts, skinmatrix);\r\n\t\t\tif (tang.data)\r\n\t\t\t{\r\n\t\t\t\tTransformDirArray(surf->ofs_skel_svect, surf->numverts, skinmatrix);\r\n\t\t\t\tTransformDirArray(surf->ofs_skel_tvect, surf->numverts, skinmatrix);\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tfor (i = 0; i < surf->numverts; i++)\r\n\t\t{\r\n//\t\t\tVectorScale(surf->ofs_skel_xyz[i], 32, surf->ofs_skel_xyz[i]);\r\n\t\t\tfor (j = 0; j < 3; j++)\r\n\t\t\t{\r\n\t\t\t\tif (mod->maxs[j] < surf->ofs_skel_xyz[i][j])\r\n\t\t\t\t\tmod->maxs[j] = surf->ofs_skel_xyz[i][j];\r\n\t\t\t\tif (mod->mins[j] > surf->ofs_skel_xyz[i][j])\r\n\t\t\t\t\tmod->mins[j] = surf->ofs_skel_xyz[i][j];\r\n\t\t\t}\r\n\t\t}\r\n\r\n#ifndef SERVERONLY\r\n\t\t{\r\n\t\t\tjson_t *mapping, *var;\r\n\t\t\tsurf->numskins = 1+gltf->variations;\r\n\t\t\tsurf->ofsskins = plugfuncs->GMalloc(&gltf->mod->memgroup, sizeof(*surf->ofsskins)*surf->numskins);\r\n\t\t\tGLTF_LoadMaterial(gltf, JSON_FindChild(primnode, \"material\"), surf->ofsskins, surf->ofs_rgbaub||surf->ofs_rgbaf);\r\n\t\t\tfor (i = 0; i < gltf->variations; i++)\r\n\t\t\t\tsurf->ofsskins[1+i] = surf->ofsskins[0];\t//unspecified matches defaults...\r\n\r\n\t\t\tfor (mapping=JSON_FindIndexedChild(primnode, \"extensions.KHR_materials_variants.mappings\", 0); mapping; mapping = mapping->sibling)\r\n\t\t\t{\r\n\t\t\t\ti = 0;\r\n\t\t\t\tfor(var = JSON_FindIndexedChild(mapping, \"variants\", 0); var; var = var->sibling)\r\n\t\t\t\t{\r\n\t\t\t\t\tj = 1+JSON_GetUInteger(var, NULL, ~0u);\r\n\t\t\t\t\tif (j < 1 || j >= surf->numskins)\r\n\t\t\t\t\t\tcontinue;\t//not valid.\r\n\t\t\t\t\tif (!i)\r\n\t\t\t\t\t\tGLTF_LoadMaterial(gltf, JSON_FindChild(mapping, \"material\"), &surf->ofsskins[j], surf->ofs_rgbaub||surf->ofs_rgbaf);\r\n\t\t\t\t\telse\r\n\t\t\t\t\t\tsurf->ofsskins[j] = surf->ofsskins[i];\r\n\t\t\t\t\ti = j;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n#endif\r\n\r\n\t\tif (!tang.data)\r\n\t\t{\r\n\t\t\tmodfuncs->AccumulateTextureVectors(surf->ofs_skel_xyz, surf->ofs_st_array, surf->ofs_skel_norm, surf->ofs_skel_svect, surf->ofs_skel_tvect, surf->ofs_indexes, surf->numindexes, !norm.data);\r\n\t\t\tmodfuncs->NormaliseTextureVectors(surf->ofs_skel_norm, surf->ofs_skel_svect, surf->ofs_skel_tvect, surf->numverts, !norm.data);\r\n\t\t}\r\n\r\n\t\tgltf->numsurfaces++;\r\n\t\tsurf->nextsurf = mod->meshinfo;\r\n\t\tmod->meshinfo = surf;\r\n\r\n\t\tPRIMCLEANUP();\r\n\t}\r\n\treturn true;\r\n}\r\n\r\nstatic void Matrix4D_Multiply(const double *a, const double *b, double *out)\r\n{\r\n\tout[0]  = a[0] * b[0] + a[4] * b[1] + a[8] * b[2] + a[12] * b[3];\r\n\tout[1]  = a[1] * b[0] + a[5] * b[1] + a[9] * b[2] + a[13] * b[3];\r\n\tout[2]  = a[2] * b[0] + a[6] * b[1] + a[10] * b[2] + a[14] * b[3];\r\n\tout[3]  = a[3] * b[0] + a[7] * b[1] + a[11] * b[2] + a[15] * b[3];\r\n\r\n\tout[4]  = a[0] * b[4] + a[4] * b[5] + a[8] * b[6] + a[12] * b[7];\r\n\tout[5]  = a[1] * b[4] + a[5] * b[5] + a[9] * b[6] + a[13] * b[7];\r\n\tout[6]  = a[2] * b[4] + a[6] * b[5] + a[10] * b[6] + a[14] * b[7];\r\n\tout[7]  = a[3] * b[4] + a[7] * b[5] + a[11] * b[6] + a[15] * b[7];\r\n\r\n\tout[8]  = a[0] * b[8] + a[4] * b[9] + a[8] * b[10] + a[12] * b[11];\r\n\tout[9]  = a[1] * b[8] + a[5] * b[9] + a[9] * b[10] + a[13] * b[11];\r\n\tout[10] = a[2] * b[8] + a[6] * b[9] + a[10] * b[10] + a[14] * b[11];\r\n\tout[11] = a[3] * b[8] + a[7] * b[9] + a[11] * b[10] + a[15] * b[11];\r\n\r\n\tout[12] = a[0] * b[12] + a[4] * b[13] + a[8] * b[14] + a[12] * b[15];\r\n\tout[13] = a[1] * b[12] + a[5] * b[13] + a[9] * b[14] + a[13] * b[15];\r\n\tout[14] = a[2] * b[12] + a[6] * b[13] + a[10] * b[14] + a[14] * b[15];\r\n\tout[15] = a[3] * b[12] + a[7] * b[13] + a[11] * b[14] + a[15] * b[15];\r\n}\r\n\r\nstatic void GenMatrixPosQuat4ScaleDouble(const double pos[3], const double quat[4], const double scale[3], double result[16])\r\n{\r\n\tfloat xx, xy, xz, xw, yy, yz, yw, zz, zw;\r\n\tfloat x2, y2, z2;\r\n\tfloat s;\r\n\tx2 = quat[0] + quat[0];\r\n\ty2 = quat[1] + quat[1];\r\n\tz2 = quat[2] + quat[2];\r\n\r\n\txx = quat[0] * x2;   xy = quat[0] * y2;   xz = quat[0] * z2;\r\n\tyy = quat[1] * y2;   yz = quat[1] * z2;   zz = quat[2] * z2;\r\n\txw = quat[3] * x2;   yw = quat[3] * y2;   zw = quat[3] * z2;\r\n\r\n\ts = scale[0];\r\n\tresult[0*4+0] = s*(1.0f - (yy + zz));\r\n\tresult[1*4+0] = s*(xy + zw);\r\n\tresult[2*4+0] = s*(xz - yw);\r\n\tresult[3*4+0] = 0;\r\n\r\n\ts = scale[1];\r\n\tresult[0*4+1] = s*(xy - zw);\r\n\tresult[1*4+1] = s*(1.0f - (xx + zz));\r\n\tresult[2*4+1] = s*(yz + xw);\r\n\tresult[3*4+1] = 0;\r\n\r\n\ts = scale[2];\r\n\tresult[0*4+2] = s*(xz + yw);\r\n\tresult[1*4+2] = s*(yz - xw);\r\n\tresult[2*4+2] = s*(1.0f - (xx + yy));\r\n\tresult[3*4+2] = 0;\r\n\r\n\tresult[0*4+3] = pos[0];\r\n\tresult[1*4+3] = pos[1];\r\n\tresult[2*4+3] = pos[2];\r\n\tresult[3*4+3] = 1;\r\n}\r\n\r\nstatic qboolean GLTF_ProcessNode(gltf_t *gltf, json_t *nodeid, double pmatrix[16], int parentidx, qboolean isjoint)\r\n{\r\n\tdouble skinmatrix[16], *skinmatrixptr=NULL;\r\n\tjson_t *c;\r\n\tjson_t *node;\r\n\tjson_t *t;\r\n\tjson_t *skin;\r\n\tjson_t *meshid;\r\n\tquintptr_t nodeidx;\r\n\tstruct gltfbone_s *b;\r\n\tnode = GLTF_FindJSONID(gltf, \"nodes\", nodeid, &nodeidx);\r\n\tif (nodeidx >= gltf->numbones)\r\n\t{\r\n\t\tif (nodeidx < MAX_BONES)\t//don't spam if its detected elsewhere.\r\n\t\t\tCon_Printf(CON_WARNING\"%s: Invalid node index %i\\n\", gltf->mod->name, (int)nodeidx);\r\n\t\treturn false;\r\n\t}\r\n\tif (!node)\r\n\t{\r\n\t\tCon_Printf(CON_WARNING\"%s: Invalid node index %i\\n\", gltf->mod->name, (int)nodeidx);\r\n\t\treturn false;\r\n\t}\r\n\r\n\tb = &gltf->bones[nodeidx];\r\n\tb->parent = parentidx;\r\n\r\n\tt = JSON_FindChild(node, \"matrix\");\r\n\tif (t)\r\n\t{\r\n\t\tb->rel.rmatrix[0*4+0] = JSON_GetIndexedFloat(t, 0, 1.0);\r\n\t\tb->rel.rmatrix[1*4+0] = JSON_GetIndexedFloat(t, 1, 0.0);\r\n\t\tb->rel.rmatrix[2*4+0] = JSON_GetIndexedFloat(t, 2, 0.0);\r\n\t\tb->rel.rmatrix[3*4+0] = JSON_GetIndexedFloat(t, 3, 0.0);\r\n\t\tb->rel.rmatrix[0*4+1] = JSON_GetIndexedFloat(t, 4, 0.0);\r\n\t\tb->rel.rmatrix[1*4+1] = JSON_GetIndexedFloat(t, 5, 1.0);\r\n\t\tb->rel.rmatrix[2*4+1] = JSON_GetIndexedFloat(t, 6, 0.0);\r\n\t\tb->rel.rmatrix[3*4+1] = JSON_GetIndexedFloat(t, 7, 0.0);\r\n\t\tb->rel.rmatrix[0*4+2] = JSON_GetIndexedFloat(t, 8, 0.0);\r\n\t\tb->rel.rmatrix[1*4+2] = JSON_GetIndexedFloat(t, 9, 0.0);\r\n\t\tb->rel.rmatrix[2*4+2] = JSON_GetIndexedFloat(t, 10,1.0);\r\n\t\tb->rel.rmatrix[3*4+2] = JSON_GetIndexedFloat(t, 11,0.0);\r\n\t\tb->rel.rmatrix[0*4+3] = JSON_GetIndexedFloat(t, 12,0.0);\r\n\t\tb->rel.rmatrix[1*4+3] = JSON_GetIndexedFloat(t, 13,0.0);\r\n\t\tb->rel.rmatrix[2*4+3] = JSON_GetIndexedFloat(t, 14,0.0);\r\n\t\tb->rel.rmatrix[3*4+3] = JSON_GetIndexedFloat(t, 15,1.0);\r\n\r\n\t\tVector4Set(b->rel.quat, 0,0,0,1);\r\n\t\tVectorSet(b->rel.scale,1,1,1);\r\n\t\tVectorSet(b->rel.trans,0,0,0);\r\n\t}\r\n\telse\r\n\t{\r\n\t\tdouble rot[4];\r\n\t\tdouble scale[3];\r\n\t\tdouble trans[3];\r\n\t\tt = JSON_FindChild(node, \"rotation\");\r\n\t\trot[0] = JSON_GetIndexedFloat(t, 0, 0.0);\r\n\t\trot[1] = JSON_GetIndexedFloat(t, 1, 0.0);\r\n\t\trot[2] = JSON_GetIndexedFloat(t, 2, 0.0);\r\n\t\trot[3] = JSON_GetIndexedFloat(t, 3, 1.0);\r\n\t\tt = JSON_FindChild(node, \"scale\");\r\n\t\tscale[0] = JSON_GetIndexedFloat(t, 0, 1.0);\r\n\t\tscale[1] = JSON_GetIndexedFloat(t, 1, 1.0);\r\n\t\tscale[2] = JSON_GetIndexedFloat(t, 2, 1.0);\r\n\t\tt = JSON_FindChild(node, \"translation\");\r\n\t\ttrans[0] = JSON_GetIndexedFloat(t, 0, 0.0);\r\n\t\ttrans[1] = JSON_GetIndexedFloat(t, 1, 0.0);\r\n\t\ttrans[2] = JSON_GetIndexedFloat(t, 2, 0.0);\r\n\r\n\t\tVector4Copy(rot, b->rel.quat);\r\n\t\tVectorCopy(scale, b->rel.scale);\r\n\t\tVectorCopy(trans, b->rel.trans);\r\n\r\n\t\t//T * R * S\r\n\t\tGenMatrixPosQuat4ScaleDouble(trans, rot, scale, b->rel.rmatrix);\r\n\t}\r\n\tMatrix4D_Multiply(b->rel.rmatrix, pmatrix, b->amatrix);\r\n\r\n\tskin = GLTF_FindJSONID(gltf, \"skins\", JSON_FindChild(node, \"skin\"), NULL);\r\n\tif (skin)\r\n\t{\r\n\t\tquintptr_t j;\r\n\t\tjson_t *joints;\r\n\t\tstruct gltf_accessor inverse;\r\n\t\tfloat *inversef;\r\n\r\n\t\tif (gltf->ver <= 1)\r\n\t\t\tjoints = JSON_FindChild(skin, \"jointNames\");\r\n\t\telse\r\n\t\t\tjoints = JSON_FindChild(skin, \"joints\");\r\n\t\tif (joints)\r\n\t\t\tjoints = joints->child;\r\n\t\tGLTF_GetAccessor(gltf, JSON_FindChild(skin, \"inverseBindMatrices\"), &inverse);\r\n\t\tinversef = inverse.data;\r\n\t\tif (inverse.componentType != 5126/*FLOAT*/ || inverse.type != ((4<<8) | 4)/*mat4x4*/)\r\n\t\t\tinverse.count = 0;\r\n\t\tmemset(gltf->bonemap, 0, sizeof(*gltf->bonemap)*MAX_BONES);\t//avoid unexpected surprises...\r\n\t\tfor (j = 0; j < MAX_BONES && joints; j++, inversef+=inverse.bytestride/sizeof(float), joints=joints->sibling)\r\n\t\t{\r\n\t\t\tquintptr_t b;\r\n\t\t\tjoints->used = true;\r\n\t\t\tif (gltf->ver <= 1)\r\n\t\t\t{\t//urgh\r\n\t\t\t\tchar jointname[sizeof(gltf->bones[b].jointname)];\r\n\t\t\t\tJSON_ReadBody(joints, jointname, sizeof(jointname));\t//this is matched to nodes[b].jointName rather than (textual) b, so we can't use our helpers.\r\n\t\t\t\tfor (b = 0; b < gltf->numbones; b++)\r\n\t\t\t\t{\r\n\t\t\t\t\tif (!strcmp(gltf->bones[b].jointname, jointname))\r\n\t\t\t\t\t\tbreak;\r\n\t\t\t\t}\r\n\t\t\t\tif (b == gltf->numbones)\r\n\t\t\t\t\tbreak;\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tb = JSON_GetUInteger(joints, NULL, ~0);\r\n\t\t\t\tif (b >= gltf->numbones)\r\n\t\t\t\t\tbreak;\r\n\t\t\t}\r\n\t\t\tgltf->bonemap[j] = b;\r\n\t\t\tif (j < inverse.count)\r\n\t\t\t{\r\n\t\t\t\tgltf->bones[b].inverse[0] = inversef[0*4+0];\r\n\t\t\t\tgltf->bones[b].inverse[1] = inversef[1*4+0];\r\n\t\t\t\tgltf->bones[b].inverse[2] = inversef[2*4+0];\r\n\t\t\t\tgltf->bones[b].inverse[3] = inversef[3*4+0];\r\n\r\n\t\t\t\tgltf->bones[b].inverse[4] = inversef[0*4+1];\r\n\t\t\t\tgltf->bones[b].inverse[5] = inversef[1*4+1];\r\n\t\t\t\tgltf->bones[b].inverse[6] = inversef[2*4+1];\r\n\t\t\t\tgltf->bones[b].inverse[7] = inversef[3*4+1];\r\n\r\n\t\t\t\tgltf->bones[b].inverse[8] = inversef[0*4+2];\r\n\t\t\t\tgltf->bones[b].inverse[9] = inversef[1*4+2];\r\n\t\t\t\tgltf->bones[b].inverse[10]= inversef[2*4+2];\r\n\t\t\t\tgltf->bones[b].inverse[11]= inversef[3*4+2];\r\n\r\n\t\t\t\tgltf->bones[b].inverse[12]= inversef[0*4+3];\r\n\t\t\t\tgltf->bones[b].inverse[13]= inversef[1*4+3];\r\n\t\t\t\tgltf->bones[b].inverse[14]= inversef[2*4+3];\r\n\t\t\t\tgltf->bones[b].inverse[15]= inversef[3*4+3];\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tgltf->bones[b].inverse[0] = 1;\r\n\t\t\t\tgltf->bones[b].inverse[1] = 0;\r\n\t\t\t\tgltf->bones[b].inverse[2] = 0;\r\n\t\t\t\tgltf->bones[b].inverse[3] = 0;\r\n\r\n\t\t\t\tgltf->bones[b].inverse[4] = 0;\r\n\t\t\t\tgltf->bones[b].inverse[5] = 1;\r\n\t\t\t\tgltf->bones[b].inverse[6] = 0;\r\n\t\t\t\tgltf->bones[b].inverse[7] = 0;\r\n\r\n\t\t\t\tgltf->bones[b].inverse[8] = 0;\r\n\t\t\t\tgltf->bones[b].inverse[9] = 0;\r\n\t\t\t\tgltf->bones[b].inverse[10]= 1;\r\n\t\t\t\tgltf->bones[b].inverse[11]= 0;\r\n\r\n\t\t\t\tgltf->bones[b].inverse[12]= 0;\r\n\t\t\t\tgltf->bones[b].inverse[13]= 0;\r\n\t\t\t\tgltf->bones[b].inverse[14]= 0;\r\n\t\t\t\tgltf->bones[b].inverse[15]= 1;\r\n\t\t\t}\r\n\t\t}\r\n\r\n//\t\tGLTF_ProcessNode(gltf, JSON_FindChild(skin, \"skeleton\"), identity, nodeidx, true);\r\n\r\n\t\tif (gltf->ver <= 1)\r\n\t\t{\r\n\t\t\tint i;\r\n\t\t\tjson_t *bdsm = JSON_FindChild(skin, \"bindShapeMatrix\");\r\n\t\t\tif (bdsm)\r\n\t\t\t{\r\n\t\t\t\tskinmatrixptr = skinmatrix;\r\n\t\t\t\tfor (i = 0; i < 16; i++)\r\n\t\t\t\t\tskinmatrix[i] = JSON_GetIndexedFloat(bdsm, i, ((i%5)==0)?1.0:0.0);\r\n\t\t\t}\r\n\t\t}\r\n\t\tJSON_FlagAsUsed(skin, \"name\");\r\n\t}\r\n\r\n\tif (gltf->ver <= 1)\r\n\t{\t//multiple in gltf1\r\n\t\tmeshid = JSON_FindChild(node, \"meshes\");\r\n\t\tif (meshid)\r\n\t\t\tfor (meshid = meshid->child; meshid; meshid = meshid->sibling)\r\n\t\t\t\tGLTF_ProcessMesh(gltf, meshid, nodeidx, skinmatrixptr);\r\n\t}\r\n\telse\r\n\t{\t//gltf2 moved to only one.\r\n\t\tmeshid = JSON_FindChild(node, \"mesh\");\r\n\t\tif (meshid)\r\n\t\t\tGLTF_ProcessMesh(gltf, meshid, nodeidx, skinmatrixptr);\r\n\t}\r\n\r\n\tfor(c = JSON_FindIndexedChild(node, \"children\", 0); c; c = c->sibling)\r\n\t{\r\n\t\tc->used = true;\r\n\t\tGLTF_ProcessNode(gltf, c, b->amatrix, nodeidx, isjoint);\r\n\t}\r\n\r\n\tb->camera = JSON_GetInteger(node, \"camera\", -1);\r\n\r\n\tJSON_WarnIfChild(node, \"weights\", &gltf->warnlimit);\t//default value for morph weight animations\r\n\tGLTF_FlagExtras(node);\r\n\r\n\treturn true;\r\n}\r\n\r\nstruct gltf_animsampler\r\n{\r\n\tenum {\r\n\t\tAINTERP_LINEAR,\t//(s)lerp\r\n\t\tAINTERP_STEP,\t//round down\r\n\t\tAINTERP_CUBICSPLINE, //3 outputs per input, requires at least two inputs. messy.\r\n\t} interptype;\r\n\tstruct gltf_accessor input;\t//timestamps\r\n\tstruct gltf_accessor output;\t//values\r\n\tint outputs;\r\n};\r\nstatic void GLTF_Animation_Persist(gltf_t *gltf, struct gltf_accessor *accessor)\r\n{\r\n\tmodel_t *mod = gltf->mod;\r\n\tqbyte *newdata = plugfuncs->GMalloc(&mod->memgroup, accessor->length);\r\n\tmemcpy(newdata, accessor->data, accessor->length);\r\n\taccessor->data = newdata;\r\n}\r\nstatic struct gltf_animsampler GLTF_AnimationSampler(gltf_t *gltf, json_t *samplers, json_t *params, json_t *samplerid, int elems)\r\n{\r\n\tint outsperinput=1;\r\n\tstruct gltf_animsampler r;\r\n\tjson_t *sampler = GLTF_FindJSONIDParent(gltf, samplers, samplerid, NULL);\r\n\r\n\tchar t[32];\r\n\tconst char *lerptype = JSON_GetString(sampler, \"interpolation\", t, sizeof(t), \"LINEAR\");\r\n\tif (!strcmp(lerptype, \"LINEAR\"))\r\n\t\tr.interptype = AINTERP_LINEAR;\r\n\telse if (!strcmp(lerptype, \"STEP\"))\r\n\t\tr.interptype = AINTERP_STEP;\r\n\telse if (!strcmp(lerptype, \"CUBICSPLINE\"))\r\n\t{\r\n\t\toutsperinput = 3;\r\n\t\tr.interptype = AINTERP_CUBICSPLINE;\r\n\t}\r\n\telse\r\n\t{\r\n\t\tCon_Printf(\"Unknown interpolation type %s\\n\", lerptype);\r\n\t\tr.interptype = AINTERP_LINEAR;\r\n\t}\r\n\r\n\tif (gltf->ver <= 1)\r\n\t{\r\n\t\tGLTF_GetAccessor(gltf, GLTF_FindJSONIDParent(gltf, params, JSON_FindChild(sampler, \"input\"), NULL), &r.input);\r\n\t\tGLTF_GetAccessor(gltf, GLTF_FindJSONIDParent(gltf, params, JSON_FindChild(sampler, \"output\"), NULL), &r.output);\r\n\t}\r\n\telse\r\n\t{\r\n\t\tGLTF_GetAccessor(gltf, JSON_FindChild(sampler, \"input\"), &r.input);\r\n\t\tGLTF_GetAccessor(gltf, JSON_FindChild(sampler, \"output\"), &r.output);\r\n\t}\r\n\r\n\tif (!r.input.count)\r\n\t\tr.input.count = 1;\r\n\telse\r\n\t\tr.outputs = r.output.count / (r.input.count*outsperinput);\r\n\tif (!r.input.data || !r.output.data || r.input.count*outsperinput*r.outputs != r.output.count)\r\n\t\tmemset(&r, 0, sizeof(r));\r\n\telse\r\n\t{\r\n\t\tGLTF_Animation_Persist(gltf, &r.input);\r\n\t\tGLTF_Animation_Persist(gltf, &r.output);\r\n\t}\r\n\treturn r;\r\n}\r\n\r\nstatic float Anim_GetTime(const struct gltf_accessor *in, int index)\r\n{\r\n\t//read the input sampler (to get timestamps)\r\n\tswitch(in->componentType)\r\n\t{\r\n\tcase 5120:\t//BYTE\r\n\t\treturn max(-1, (*(signed char*)((qbyte*)in->data + in->bytestride*index)) / 127.0);\r\n\tcase 5121:\t//UNSIGNED_BYTE\r\n\t\treturn (*(unsigned char*)((qbyte*)in->data + in->bytestride*index)) / 255.0;\r\n\tcase 5122: //SHORT\r\n\t\treturn max(-1, (*(signed short*)((qbyte*)in->data + in->bytestride*index)) / 32767.0);\r\n\tcase 5123: //UNSIGNED_SHORT\r\n\t\treturn (*(unsigned short*)((qbyte*)in->data + in->bytestride*index)) / 65535.0;\r\n\tcase 5125: //UNSIGNED_INT\r\n\t\treturn (*(unsigned int*)((qbyte*)in->data + in->bytestride*index)) / (double)~0u;\r\n\tcase 5126: //FLOAT\r\n\t\treturn *(float*)((qbyte*)in->data + in->bytestride*index);\r\n\tdefault:\r\n\t\tCon_Printf(\"Unsupported input component type %i\\n\", in->componentType);\r\n\t\treturn 0;\r\n\t}\r\n}\r\nstatic void Anim_GetVal(const struct gltf_accessor *in, int index, float *result, int elems)\r\n{\r\n\t//read the input sampler (to get timestamps)\r\n\tswitch(in->componentType)\r\n\t{\r\n\tcase 5120:\t//BYTE\r\n\t\twhile (elems --> 0)\r\n\t\t\tresult[elems] = max(-1, ((signed char*)((qbyte*)in->data + in->bytestride*index))[elems] / 127.0);\r\n\t\tbreak;\r\n\tcase 5121:\t//UNSIGNED_BYTE\r\n\t\twhile (elems --> 0)\r\n\t\t\tresult[elems] = ((unsigned char*)((qbyte*)in->data + in->bytestride*index))[elems] / 255.0;\r\n\t\tbreak;\r\n\tcase 5122: //SHORT\r\n\t\twhile (elems --> 0)\r\n\t\t\tresult[elems] = max(-1, ((signed short*)((qbyte*)in->data + in->bytestride*index))[elems] / 32767.0);\r\n\t\tbreak;\r\n\tcase 5123: //UNSIGNED_SHORT\r\n\t\twhile (elems --> 0)\r\n\t\t\tresult[elems] = ((unsigned short*)((qbyte*)in->data + in->bytestride*index))[elems] / 65535.0;\r\n\t\tbreak;\r\n\tcase 5125: //UNSIGNED_INT\r\n\t\twhile (elems --> 0)\r\n\t\t\tresult[elems] = ((unsigned int*)((qbyte*)in->data + in->bytestride*index))[elems] / (double)~0u;\r\n\t\tbreak;\r\n\tcase 5126: //FLOAT\r\n\t\twhile (elems --> 0)\r\n\t\t\tresult[elems] = ((float*)((qbyte*)in->data + in->bytestride*index))[elems];\r\n\t\tbreak;\r\n\tdefault:\r\n\t\tCon_Printf(\"Unsupported output component type %i\\n\", in->componentType);\r\n\t\tbreak;\r\n\t}\r\n}\r\nstatic void QuaternionSlerp_(const vec4_t p, const vec4_t q, float t, vec4_t qt)\r\n{\r\n\tint i;\r\n\tfloat omega, cosom, sinom, sclp, sclq;\r\n\tvec4_t flipped;\r\n\r\n\t// decide if one of the quaternions is backwards\r\n\tfloat a = 0;\r\n\tfloat b = 0;\r\n\tfor (i = 0; i < 4; i++) {\r\n\t\ta += (p[i]-q[i])*(p[i]-q[i]);\r\n\t\tb += (p[i]+q[i])*(p[i]+q[i]);\r\n\t}\r\n\tif (a > b) {\r\n\t\tfor (i = 0; i < 4; i++) {\r\n\t\t\tflipped[i] = -q[i];\r\n\t\t}\r\n\t\tq = flipped;\r\n\t}\r\n\r\n\tcosom = p[0]*q[0] + p[1]*q[1] + p[2]*q[2] + p[3]*q[3];\r\n\r\n\tif ((1.0 + cosom) > 0.00000001) {\r\n\t\tif ((1.0 - cosom) > 0.00000001) {\r\n\t\t\tomega = acos( cosom );\r\n\t\t\tsinom = sin( omega );\r\n\t\t\tsclp = sin( (1.0 - t)*omega) / sinom;\r\n\t\t\tsclq = sin( t*omega ) / sinom;\r\n\t\t}\r\n\t\telse {\r\n\t\t\tsclp = 1.0 - t;\r\n\t\t\tsclq = t;\r\n\t\t}\r\n\t\tfor (i = 0; i < 4; i++) {\r\n\t\t\tqt[i] = sclp * p[i] + sclq * q[i];\r\n\t\t}\r\n\t}\r\n\telse {\r\n\t\tqt[0] = -p[1];\r\n\t\tqt[1] = p[0];\r\n\t\tqt[2] = -p[3];\r\n\t\tqt[3] = p[2];\r\n\t\tsclp = sin( (1.0 - t) * 0.5 * M_PI);\r\n\t\tsclq = sin( t * 0.5 * M_PI);\r\n\t\tfor (i = 0; i < 4; i++) {\r\n\t\t\tqt[i] = sclp * p[i] + sclq * qt[i];\r\n\t\t}\r\n\t}\r\n}\r\nstatic void LerpAnimData(const struct gltf_animsampler *samp, float time, float *result, int elems, qboolean slerp)\r\n{\r\n\tfloat t0, t1;\r\n\tfloat w0, w1;\r\n//\tfloat v0[elems], v1[elems];\r\n\tfloat *v0 = alloca(sizeof(*v0)*elems);\r\n\tfloat *v1 = alloca(sizeof(*v1)*elems);\r\n\tint f0, f1, c;\r\n\r\n\tconst struct gltf_accessor *in = &samp->input;\r\n\tconst struct gltf_accessor *out = &samp->output;\r\n\r\n\tt0 = t1 = Anim_GetTime(in, f1=f0=0);\r\n\twhile (time > t1 && f1 < in->count-1)\r\n\t{\r\n\t\tt0 = t1;\r\n\t\tf0 = f1;\r\n\t\tf1++;\r\n\t\tt1 = Anim_GetTime(in, f1);\r\n\t}\r\n\r\n\tf0 *= samp->outputs;\r\n\tf1 *= samp->outputs;\r\n\r\n\tif (samp->interptype == AINTERP_CUBICSPLINE)\r\n\t{\r\n\t\tfloat step=t1-t0;\r\n\t\tfloat t=bound(0, (time-t0)/step, 1);\r\n\t\tfloat tt=t*t, ttt=tt*t;\r\n\t\t//Hermite spline factors\r\n\t\tfloat m0 = (2*ttt - 3*tt + 1);\r\n\t\tfloat mb = (ttt - 2*tt + t)*step;\r\n\t\tfloat m1 = (-2*ttt + 3*tt);\r\n\t\tfloat ma = (ttt - tt)*step;\r\n\t\tfloat a[4], b[4];\r\n\r\n\t\t//get the relevant tangents+sample values\r\n\t\t//<quote>When used with CUBICSPLINE interpolation, tangents (ak, bk) and values (vk) are grouped within keyframes:\r\n\t\t//a1,a2,...an,v1,v2,...vn,b1,b2,...bn</quote>\r\n\t\t//so ignore that and use avb,avb,avb groups...\r\n\t\tAnim_GetVal(out, f1*3+0, a, elems);\r\n\t\tAnim_GetVal(out, f0*3+1, v0, elems);\r\n\t\tAnim_GetVal(out, f1*3+1, v1, elems);\r\n\t\tAnim_GetVal(out, f0*3+2, b, elems);\r\n\r\n\t\t//and compute the spline.\r\n\t\tfor (c = 0; c < elems; c++)\r\n\t\t\tresult[c] = m0*v0[c] + mb*b[c] + m1*v1[c] + ma*a[c];\r\n\r\n\t\t//quats must be normalized.\r\n\t\tif (slerp)\r\n\t\t{\r\n\t\t\tfloat len = sqrt(DotProduct4(result,result));\r\n\t\t\tVector4Scale(result, 1/len, result);\r\n\t\t}\r\n\t\treturn;\r\n\t}\r\n\telse if (time <= t0)\t\t\t//if before the first time, clamp it.\r\n\t\tw1 = 0;\r\n\telse if (time >= t1)\t//if after tha last frame we could find, clamp it to the last.\r\n\t\tw1 = 1;\r\n\telse if (samp->interptype == AINTERP_LINEAR)\r\n\t\tw1 = (time-t0)/(t1-t0);\r\n\telse //if (samp->interptype == AINTERP_STEP)\r\n\t\tw1 = 0;\r\n\r\n\tif (w1 <= 0)\r\n\t\tAnim_GetVal(out, f0, result, elems);\r\n\telse if (w1 >= 1)\r\n\t\tAnim_GetVal(out, f1, result, elems);\r\n\telse\r\n\t{\r\n\t\tAnim_GetVal(out, f0, v0, elems);\r\n\t\tAnim_GetVal(out, f1, v1, elems);\r\n\t\tif (slerp)\r\n\t\t\tQuaternionSlerp_(v0, v1, w1, result);\r\n\t\telse\r\n\t\t{\r\n\t\t\tw0 = 1-w1;\r\n\t\t\tfor (c = 0; c < elems; c++)\r\n\t\t\t\tresult[c] = v0[c]*w0 + w1*v1[c];\r\n\t\t}\r\n\t}\r\n}\r\n\r\nstatic void GLTF_RemapBone(gltf_t *gltf, size_t *nextidx, size_t b)\r\n{\t//potentially needs to walk to the root before the child. recursion sucks.\r\n\tif (b == -1 || gltf->bonemap[b] >= 0)\r\n\t\treturn;\t//already got remapped\r\n\tGLTF_RemapBone(gltf, nextidx, gltf->bones[b].parent);\r\n\tgltf->bonemap[b] = (*nextidx)++;\r\n}\r\nstatic void GLTF_RewriteBoneTree(gltf_t *gltf)\r\n{\r\n\tgaliasinfo_t *surf;\r\n\tsize_t j, n;\r\n\tstruct gltfbone_s *tmpbones;\r\n\r\n\tfor (j = 0; j < gltf->numbones; j++)\r\n\t{\r\n\t\tif (gltf->bones[j].parent >= j)\r\n\t\t\tbreak;\r\n\t}\r\n\tif (j == gltf->numbones)\r\n\t{\r\n\t\tfor (j = 0; j < gltf->numbones; j++)\r\n\t\t\tgltf->bonemap[j] = j;\r\n\t\treturn;\t//all are ordered okay\r\n\t}\r\n\r\n\tfor (j = 0; j < gltf->numbones; j++)\r\n\t\tgltf->bonemap[j] = -1;\r\n\tfor (     ; j < MAX_BONES; j++)\r\n\t\tgltf->bonemap[j] = 0;\r\n\tn = 0;\r\n\tfor (j = 0; j < gltf->numbones; j++)\r\n\t\tGLTF_RemapBone(gltf, &n, j);\r\n\r\n\ttmpbones = malloc(sizeof(*tmpbones)*gltf->numbones);\r\n\tmemcpy(tmpbones, gltf->bones, sizeof(*tmpbones)*gltf->numbones);\r\n\tfor (j = 0; j < gltf->numbones; j++)\r\n\t\tgltf->bones[gltf->bonemap[j]] = tmpbones[j];\r\n\tfor (j = 0; j < gltf->numbones; j++)\r\n\t\tif (gltf->bones[j].parent >= 0)\r\n\t\t\tgltf->bones[j].parent = gltf->bonemap[gltf->bones[j].parent];\r\n\r\n\tfor(surf = gltf->mod->meshinfo; surf; surf = surf->nextsurf)\r\n\t{\r\n\t\tfor (j = 0; j < surf->numverts; j++)\r\n\t\t\tfor (n = 0; n < countof(surf->ofs_skel_idx[j]); n++)\r\n\t\t\t\tsurf->ofs_skel_idx[j][n] = gltf->bonemap[surf->ofs_skel_idx[j][n]];\r\n\t\tsurf->meshrootbone = gltf->bonemap[surf->meshrootbone];\r\n\t}\r\n}\r\n\r\nstruct galiasanimation_gltf_s\r\n{\t//stored in galiasanimation_t->boneofs\r\n\tfloat duration;\r\n\tstruct\r\n\t{\r\n\t\tstruct gltf_animsampler rot,scale,trans,morph;\r\n\t} bone[1];\r\n};\r\nstatic const float *QDECL GLTF_AnimateMorphs(const galiasinfo_t *surf, const framestate_t *framestate, float *morphs)\r\n{\r\n\tfloat *imorphs = alloca(sizeof(*imorphs)*surf->nummorphs), *src;\r\n\tsize_t influence, m;\r\n\tint bone = surf->meshrootbone;\r\n\tconst struct galiasanimation_gltf_s *a;\r\n\tconst struct framestateregion_s *fg = &framestate->g[FS_REG];\r\n\tmemset(morphs, 0, sizeof(morphs[0])*surf->nummorphs);\r\n\tfor (influence = 0; influence < countof(framestate->g[FS_REG].frame); influence++)\r\n\t{\r\n\t\tm = 0;\r\n\t\tif (!fg->lerpweight[influence])\r\n\t\t\tcontinue;\t//mneh, don't care.\r\n\t\tif (surf->ofsanimations && (unsigned int)fg->frame[influence] < (unsigned int)surf->numanimations)\r\n\t\t{\r\n\t\t\ta = surf->ofsanimations[fg->frame[influence]].boneofs;\r\n\t\t\tif (a && a->bone[bone].morph.input.count)\r\n\t\t\t{\r\n\t\t\t\tint asz = min(a->bone[bone].morph.outputs, surf->nummorphs);\r\n\t\t\t\tdouble time = fg->frametime[influence];\r\n\t\t\t\tif (surf->ofsanimations[fg->frame[influence]].loop && time >= a->duration)\r\n\t\t\t\t\ttime = time - a->duration*floor(time/a->duration);\r\n\t\t\t\tLerpAnimData(&a->bone[bone].morph, time, imorphs, asz, false);\r\n\t\t\t\tfor (; m < surf->nummorphs && m < asz; m++)\r\n\t\t\t\t\tmorphs[m] += fg->lerpweight[influence] * imorphs[m];\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tsrc = (float*)(surf+1);\t//our static morphs are stuck on the end of our surf struct.;\r\n\t\tfor (; m < surf->nummorphs; m++)\r\n\t\t\tmorphs[m] += fg->lerpweight[influence]*src[m];\r\n\t}\r\n\treturn morphs;\r\n}\r\nstatic float *QDECL GLTF_AnimateBones(const galiasinfo_t *surf, const galiasanimation_t *anim, float time, float *bonematrix, const struct galiasbone_s *boneinf, int numbones)\r\n{\r\n\tconst struct galiasbone_gltf_s *defbone = surf->ctx;\r\n\tint j = 0, l;\r\n\tconst struct galiasanimation_gltf_s *a = anim->boneofs;\r\n\r\n\tnumbones = min(numbones, surf->numbones);\t//if you're asking for more bones, then expect bugs.\r\n\r\n\tif (anim->loop && time >= a->duration)\r\n\t\ttime = time - a->duration*floor(time/a->duration);\r\n\r\n\tfor (j = 0; j < numbones; j++, bonematrix+=12)\r\n\t{\r\n\t\tfloat scale[3];\r\n\t\tfloat rot[4];\r\n\t\tfloat trans[3];\r\n\t\t//eww, weird inheritance crap.\r\n\t\tif (a->bone[j].rot.input.data || a->bone[j].scale.input.data || a->bone[j].trans.input.data)\r\n\t\t{\r\n\t\t\tVectorCopy(defbone[j].scale, scale);\r\n\t\t\tVector4Copy(defbone[j].quat, rot);\r\n\t\t\tVectorCopy(defbone[j].trans, trans);\r\n\r\n\t\t\tif (a->bone[j].rot.input.data)\r\n\t\t\t\tLerpAnimData(&a->bone[j].rot, time, rot, 4, true);\r\n\t\t\tif (a->bone[j].scale.input.data)\r\n\t\t\t\tLerpAnimData(&a->bone[j].scale, time, scale, 3, false);\r\n\t\t\tif (a->bone[j].trans.input.data)\r\n\t\t\t\tLerpAnimData(&a->bone[j].trans, time, trans, 3, false);\r\n\t\t\t//figure out the bone matrix...\r\n\t\t\tmodfuncs->GenMatrixPosQuat4Scale(trans, rot, scale, bonematrix);\r\n\t\t}\r\n\t\telse\r\n\t\t{\t//nothing animated, use what we calculated earlier.\r\n\t\t\tfor (l = 0; l < 12; l++)\r\n\t\t\t\tbonematrix[l] = defbone[j].rmatrix[l];\r\n\t\t}\r\n\t\tif (surf->ofsbones[j].parent < 0)\r\n\t\t{\t//rotate any root bones from gltf to quake's orientation.\r\n\t\t\tfloat fnar[12];\r\n\t\t\tfloat toquake[12]={0};\r\n\t\t\tif (mod_gltf_standardorientation->ival)\r\n\t\t\t\ttoquake[2] = toquake[4] = toquake[9] = mod_gltf_scale->value;\r\n\t\t\telse\r\n\t\t\t\ttoquake[0] = toquake[5] = toquake[10] = mod_gltf_scale->value;\r\n\t\t\tmemcpy(fnar, bonematrix, sizeof(fnar));\r\n\t\t\tmodfuncs->ConcatTransforms((void*)toquake, (void*)fnar, (void*)bonematrix);\r\n\t\t}\r\n\t}\r\n\treturn bonematrix - j*12;\r\n}\r\n\r\n//okay, so gltf is some weird scene thing.\r\n//mostly there should be some default scene, so we'll just use that.\r\n//we do NOT supported nested nodes right now...\r\nstatic qboolean GLTF_LoadModel(struct model_s *mod, char *json, size_t jsonsize, void *buffer, size_t buffersize)\r\n{\r\n\tstatic struct knowngltfextensions_s\r\n\t{\r\n\t\tconst char *name;\r\n\t\tqboolean supported;\t//unsupported extensions don't really need to be listed, but they do prevent warnings from unknown-but-used extensions\r\n\t\tqboolean draft;\t\t//true when our implementation is probably buggy on account of the spec maybe changing.\r\n\t} extensions_v1[] =\r\n\t{\r\n\t\t{\"KHR_binary_glTF\",\t\t\t\t\t\t\ttrue,\tfalse},\r\n\t\t{\"KHR_materials_common\",\t\t\t\t\ttrue,   false},\r\n\t\t{NULL}\r\n\t}, extensions_v2[] =\r\n\t{\r\n\t\t{\"KHR_materials_pbrSpecularGlossiness\",\t\ttrue,   false},\r\n//\t\t{\"KHR_materials_cmnBlinnPhong\",\t\t\t\ttrue,   true},\r\n\t\t{\"KHR_materials_unlit\",\t\t\t\t\t\ttrue,\tfalse},\r\n\t\t{\"KHR_mesh_quantization\",\t\t\t\t\ttrue,\tfalse},\r\n\t\t{\"MSFT_texture_dds\",\t\t\t\t\t\ttrue,\tfalse},\r\n\t\t{\"MSFT_packing_occlusionRoughnessMetallic\", true,\tfalse},\r\n\t\t{\"KHR_materials_variants\",\t\t\t\t\ttrue,\tfalse},\r\n\t\t{\"KHR_materials_ior\",\t\t\t\t\t\ttrue,\tfalse},\r\n\r\n\t\t{\"KHR_materials_transmission\",\t\t\t\ttrue,\ttrue},\t//FIXME: requires glsl tweaks.\r\n\t\t{\"KHR_materials_volume\",\t\t\t\t\ttrue,\ttrue},\t//FOXME: requires glsl tweaks.\r\n\r\n#ifdef HAVE_DRACO\r\n\t\t{\"KHR_draco_mesh_compression\",\t\t\t\ttrue,\tfalse},\t//probably fatal\r\n#else\r\n\t\t{\"KHR_draco_mesh_compression\",\t\t\t\tfalse,\tfalse},\t//probably fatal\r\n#endif\r\n\t\t{\"KHR_texture_transform\",\t\t\t\t\tfalse,\ttrue},\t//requires glsl tweaks, per texmap. can't use tcmod if its only on the bumpmap etc.\r\n\t\t{\"KHR_materials_sheen\",\t\t\t\t\t\tfalse,\ttrue},\t//requires glsl tweaks, extra brdf layer in the middle for velvet.\r\n\t\t{\"KHR_materials_clearcoat\",\t\t\t\t\tfalse,\ttrue},\t//requires glsl tweaks, extra brdf layer over the top for varnish etc.\r\n\r\n\t\t{NULL}\r\n\t}, *extensions;\r\n\tgltf_t gltf;\r\n\tquintptr_t j,k;\r\n\tjson_t *scene, *n, *anim, *var;\r\n\tdouble rootmatrix[16];\r\n\tdouble gltfver;\r\n\tgaliasinfo_t *surf;\r\n\tgaliasbone_t *bone;\r\n\tgaliasanimation_t *framegroups = NULL;\r\n\tunsigned int numframegroups = 0;\r\n\tfloat *baseframe;\r\n\tstruct galiasbone_gltf_s *gltfbone;\r\n\tstruct jsonparsectx_s jsparsectx = {json, jsonsize, 0};\r\n\tmemset(&gltf, 0, sizeof(gltf));\r\n\tgltf.bonemap = malloc(sizeof(*gltf.bonemap)*MAX_BONES);\r\n\tgltf.bones = malloc(sizeof(*gltf.bones)*MAX_BONES);\r\n\tmemset(gltf.bones, 0, sizeof(*gltf.bones)*MAX_BONES);\r\n\tgltf.r = JSON_ParseNode(NULL, mod->name, NULL, &jsparsectx);\r\n\tgltf.mod = mod;\r\n\tgltf.buffers[0].data = buffer;\r\n\tgltf.buffers[0].length = buffersize;\r\n\tgltf.warnlimit = 5;\r\n\r\n\t//asset.version must exist, supposedly. so default to something b0rked\r\n\tgltfver = JSON_GetFloat(gltf.r, \"asset.minVersion\", 0.0);\r\n\tif (gltfver != 2.0)\r\n\t\tgltfver = JSON_GetFloat(gltf.r, \"asset.version\", 0.0);\r\n\tif (gltfver == 2.0)\r\n\t\tgltf.ver = 2;\r\n\tif (gltfver == 1.0)\t//we load gltf1 models. we don't get the materials right but gltf1 sucks for that anyway.\r\n\t\tgltf.ver = 1;\r\n\tif (gltf.ver)\r\n\t{\r\n\t\tif (gltf.ver <= 1)\r\n\t\t{\r\n\t\t\tJSON_FlagAsUsed(gltf.r, \"asset.profile\");\r\n\t\t\tJSON_FlagAsUsed(gltf.r, \"asset.premultipliedAlpha\");\r\n\t\t\textensions = extensions_v1;\r\n\t\t}\r\n\t\telse\r\n\t\t\textensions = extensions_v2;\r\n\t\tJSON_FlagAsUsed(gltf.r, \"asset.copyright\");\r\n\t\tJSON_FlagAsUsed(gltf.r, \"asset.generator\");\r\n\t\tJSON_WarnIfChild(gltf.r, \"asset.minVersion\", &gltf.warnlimit);\r\n\t\tJSON_WarnIfChild(gltf.r, \"asset.extensions\", &gltf.warnlimit);\r\n\r\n\t\tfor(n = JSON_FindIndexedChild(gltf.r, \"extensionsRequired\", 0); n; n = n->sibling)\r\n\t\t{\r\n\t\t\tchar extname[256];\r\n\t\t\tJSON_ReadBody(n, extname, sizeof(extname));\r\n\t\t\tfor (j = 0; extensions[j].name; j++)\r\n\t\t\t{\r\n\t\t\t\tif (!strcmp(extname, extensions[j].name))\r\n\t\t\t\t\tbreak;\r\n\t\t\t}\r\n\t\t\tif (!extensions[j].supported)\r\n\t\t\t{\r\n\t\t\t\tCon_Printf(CON_ERROR \"%s: Required gltf%i extension \\\"%s\\\" not supported\\n\", mod->name, gltf.ver, extname);\r\n\t\t\t\tgoto abort;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tmod->flags = JSON_GetInteger(gltf.r, \"asset.extras.fte.modelflags\", 0.0);\r\n\r\n\t\tfor(n = JSON_FindIndexedChild(gltf.r, \"extensionsUsed\", 0); n; n = n->sibling)\r\n\t\t{\t//must be a superset of the above.\r\n\t\t\tchar extname[256];\r\n\t\t\tJSON_ReadBody(n, extname, sizeof(extname));\r\n\t\t\tfor (j = 0; extensions[j].name; j++)\r\n\t\t\t{\r\n\t\t\t\tif (!strcmp(extname, extensions[j].name))\r\n\t\t\t\t\tbreak;\r\n\t\t\t}\r\n\t\t\tif (!extensions[j].supported)\r\n\t\t\t\tCon_Printf(CON_WARNING \"%s: gltf%i extension \\\"%s\\\" not known\\n\", mod->name, gltf.ver, extname);\r\n\t\t\telse if (extensions[j].draft)\r\n\t\t\t\tCon_Printf(CON_WARNING \"%s: gltf%i extension \\\"%s\\\" follows draft implementation, and may be non-standard/buggy\\n\", mod->name, gltf.ver, extname);\r\n\t\t}\r\n\r\n\t\tVectorClear(mod->maxs);\r\n\t\tVectorClear(mod->mins);\r\n\r\n\t\t//we don't really care about cameras.\r\n\t\tJSON_FlagAsUsed(gltf.r, \"cameras\");\r\n\r\n\t\tscene = GLTF_FindJSONID_First(&gltf, \"scenes\", JSON_FindChild(gltf.r, \"scene\"), NULL);\r\n\r\n\t\tmemset(&rootmatrix, 0, sizeof(rootmatrix));\r\n\t\tif (mod_gltf_standardorientation->ival)\t//transform from gltf to quake. mostly only needed for the base pose.\r\n\t\t{\r\n\t\t\trootmatrix[2] = rootmatrix[4] = rootmatrix[9] = mod_gltf_scale->value;\r\n\t\t\trootmatrix[15] = 1;\r\n\t\t}\r\n\t\telse\t//identity orientation that violates gltf standards.\r\n\t\t{\r\n\t\t\trootmatrix[0] = rootmatrix[5] = rootmatrix[10] = mod_gltf_scale->value;\r\n\t\t\trootmatrix[15] = 1;\r\n\t\t}\r\n\r\n\t\tn = JSON_FindChild(gltf.r, \"nodes\");\r\n\t\tfor (j = 0; ; j++)\r\n\t\t{\r\n\t\t\tif (gltf.ver <= 1)\r\n\t\t\t\tn = j?n->sibling:n->child;\r\n\t\t\telse\r\n\t\t\t\tn = JSON_FindIndexedChild(gltf.r, \"nodes\", j);\r\n\t\t\tif (!n)\r\n\t\t\t\tbreak;\r\n\t\t\tif (j == MAX_BONES)\r\n\t\t\t{\r\n\t\t\t\tCon_Printf(CON_WARNING\"%s: too many nodes (max %i)\\n\", mod->name, MAX_BONES);\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\t\t\tJSON_ReadBody(JSON_FindChild(n, \"jointName\"), gltf.bones[j].jointname, sizeof(gltf.bones[j].jointname));\r\n\t\t\tif (!JSON_ReadBody(JSON_FindChild(n, \"name\"), gltf.bones[j].name, sizeof(gltf.bones[j].name)))\r\n\t\t\t{\r\n\t\t\t\tif (n)\r\n\t\t\t\t\tJSON_GetPath(n, true, gltf.bones[j].name, sizeof(gltf.bones[j].name));\r\n\t\t\t\telse\r\n\t\t\t\t\tQ_snprintf(gltf.bones[j].name, sizeof(gltf.bones[j].name), \"bone%u\", (unsigned)j);\r\n\t\t\t}\r\n\t\t\tgltf.bones[j].camera = -1;\r\n\t\t\tgltf.bones[j].parent = -1;\r\n\t\t\tgltf.bones[j].amatrix[0] = gltf.bones[j].amatrix[5] = gltf.bones[j].amatrix[10] = gltf.bones[j].amatrix[15] = 1;\r\n\t\t\tgltf.bones[j].inverse[0] = gltf.bones[j].inverse[5] = gltf.bones[j].inverse[10] = gltf.bones[j].inverse[15] = 1;\r\n\t\t\tgltf.bones[j].rel.rmatrix[0] = gltf.bones[j].rel.rmatrix[5] = gltf.bones[j].rel.rmatrix[10] = gltf.bones[j].rel.rmatrix[15] = 1;\r\n\t\t}\r\n\t\tgltf.numbones = j;\r\n\r\n\r\n\t\tgltf.variations = 0;\r\n\t\tfor (var = JSON_FindIndexedChild(gltf.r, \"extensions.KHR_materials_variants.variants\", 0); var; var = var->sibling)\r\n\t\t\tgltf.variations++;\r\n\r\n\t\tJSON_FlagAsUsed(scene, \"name\");\r\n\t\tGLTF_FlagExtras(scene);\r\n\t\tfor (j = 0; ; j++)\r\n\t\t{\r\n\t\t\tn = JSON_FindIndexedChild(scene, \"nodes\", j);\r\n\t\t\tif (!n)\r\n\t\t\t\tbreak;\r\n\t\t\tn->used = true;\r\n//\t\t\tif (!\r\n\t\t\tGLTF_ProcessNode(&gltf, n, rootmatrix, -1, false);\r\n//\t\t\t\tbreak;\r\n\t\t}\r\n\r\n\t\tGLTF_RewriteBoneTree(&gltf);\r\n\r\n\t\tgltfbone = plugfuncs->GMalloc(&mod->memgroup, sizeof(*gltfbone)*gltf.numbones);\r\n\t\tbone = plugfuncs->GMalloc(&mod->memgroup, sizeof(*bone)*gltf.numbones);\r\n\t\tbaseframe = plugfuncs->GMalloc(&mod->memgroup, sizeof(float)*12*gltf.numbones);\r\n\t\tfor (j = 0; j < gltf.numbones; j++)\r\n\t\t{\r\n\t\t\tQ_strlcpy(bone[j].name, gltf.bones[j].name, sizeof(bone[j].name));\r\n\t\t\tbone[j].parent = gltf.bones[j].parent;\r\n\r\n\t\t\tif (gltf.bones[j].camera >= 0 && !mod->camerabone)\r\n\t\t\t\tmod->camerabone = j+1;\r\n\r\n\t\t\tfor(k = 0; k < 12; k++)\r\n\t\t\t{\r\n\t\t\t\tbaseframe[j*12+k] = gltf.bones[j].amatrix[k];\r\n\t\t\t\tbone[j].inverse[k] = gltf.bones[j].inverse[k];\r\n\t\t\t}\r\n\t\t\tgltfbone[j] = gltf.bones[j].rel;\r\n\t\t}\r\n\r\n\t\tanim = JSON_FindChild(gltf.r, \"animations\");\r\n\t\tif (anim)\r\n\t\t\tfor(anim = anim->child; anim; anim = anim->sibling)\r\n\t\t\t\tnumframegroups++;\r\n\t\tif (numframegroups)\r\n\t\t{\r\n\t\t\tstruct galiasanimation_gltf_s **mergeanims = NULL;\r\n\t\t\tif (mod_gltf_fixbuggyanims->ival)\r\n\t\t\t{\r\n\t\t\t\tmergeanims = alloca(sizeof(*mergeanims)*gltf.numbones);\r\n\t\t\t\tmemset(mergeanims, 0, sizeof(mergeanims)*gltf.numbones);\r\n\t\t\t}\r\n\t\t\tframegroups = plugfuncs->GMalloc(&mod->memgroup, sizeof(*framegroups)*numframegroups);\r\n\t\t\tanim = JSON_FindChild(gltf.r, \"animations\")->child;\r\n\t\t\tfor (k = 0; k < numframegroups; k++, anim = anim->sibling)\r\n\t\t\t{\r\n\t\t\t\tgaliasanimation_t *fg = &framegroups[k];\r\n\t\t\t\tjson_t *chan;\r\n\t\t\t\tjson_t *samps = JSON_FindChild(anim, \"samplers\");\r\n\t\t\t\tjson_t *params = gltf.ver<=1?JSON_FindChild(anim, \"parameters\"):0;\t//gltf1\r\n\t\t\t\tfloat maxtime = 0;\r\n\t\t\t\tunsigned maxposes = 0;\r\n\t\t\t\tstruct galiasanimation_gltf_s *a = plugfuncs->GMalloc(&mod->memgroup, sizeof(*a)+sizeof(a->bone[0])*(gltf.numbones-1));\r\n\t\t\t\tanim->used = true;\r\n\r\n\t\t\t\tif (!JSON_ReadBody(JSON_FindChild(anim, \"name\"), fg->name, sizeof(fg->name)))\r\n\t\t\t\t{\r\n\t\t\t\t\tif (gltf.ver <= 1)\r\n\t\t\t\t\t\tQ_snprintf(fg->name, sizeof(fg->name), \"%s\", anim->name);\r\n\t\t\t\t\telse if (anim)\r\n\t\t\t\t\t\tJSON_GetPath(anim, true, fg->name, sizeof(fg->name));\r\n\t\t\t\t\telse\r\n\t\t\t\t\t\tQ_snprintf(fg->name, sizeof(fg->name), \"anim%u\", (unsigned int)k);\r\n\t\t\t\t}\r\n\t\t\t\tfg->loop = !!mod_gltf_loop->ival;\r\n\t\t\t\tfg->skeltype = SKEL_RELATIVE;\r\n\t\t\t\tfg->action = -1;\r\n\t\t\t\tfg->actionweight = 0;\r\n\t\t\t\tfor(chan = JSON_FindIndexedChild(anim, \"channels\", 0); chan; chan = chan->sibling)\r\n\t\t\t\t{\r\n\t\t\t\t\tstruct gltf_animsampler s;\r\n\t\t\t\t\tjson_t *targ = JSON_FindChild(chan, \"target\");\r\n\t\t\t\t\tjson_t *samplerid = JSON_FindChild(chan, \"sampler\");\r\n\t\t\t\t\tqintptr_t bone;\r\n\t\t\t\t\tjson_t *path = JSON_FindChild(targ, \"path\");\r\n\t\t\t\t\tchan->used = true;\r\n\r\n\t\t\t\t\tif (gltf.ver <= 1)\r\n\t\t\t\t\t\tGLTF_FindJSONID(&gltf, \"nodes\", JSON_FindChild(targ, \"id\"), (quintptr_t*)&bone);\r\n\t\t\t\t\telse\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tbone = JSON_GetInteger(targ, \"node\", -2);\r\n\t\t\t\t\t\tif (bone == -2)\r\n\t\t\t\t\t\t\tcontinue;\t//'When node isn't defined, channel should be ignored'\r\n\t\t\t\t\t}\r\n\t\t\t\t\tif (bone < 0 || bone >= gltf.numbones)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tif (gltf.warnlimit --> 0)\r\n\t\t\t\t\t\t\tCon_Printf(\"%s: invalid node index %i\\n\", mod->name, (int)bone);\r\n\t\t\t\t\t\tcontinue;\t//error...\r\n\t\t\t\t\t}\r\n\t\t\t\t\tbone = gltf.bonemap[bone];\r\n\t\t\t\t\tif (mergeanims)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tif (mergeanims[bone] && mergeanims[bone] != a)\r\n\t\t\t\t\t\t\tmergeanims = NULL;\t//was some other animation... which means two animations are fighting over the same joint. which means we don't need to use this hack! yay!\r\n\t\t\t\t\t\telse\r\n\t\t\t\t\t\t\tmergeanims[bone] = a;\r\n\t\t\t\t\t}\r\n\t\t\t\t\ts = GLTF_AnimationSampler(&gltf, samps, params, samplerid, 4);\r\n\t\t\t\t\tif (!s.input.maxs[0] && s.input.count)\r\n\t\t\t\t\t\ts.input.maxs[0] = Anim_GetTime(&s.input, s.input.count-1);\r\n\t\t\t\t\tmaxtime = max(maxtime, s.input.maxs[0]);\r\n\t\t\t\t\tmaxposes = max(maxposes, s.input.count);\r\n\t\t\t\t\tif (JSON_Equals(path, NULL, \"rotation\") && s.outputs==1)\r\n\t\t\t\t\t\ta->bone[bone].rot = s;\r\n\t\t\t\t\telse if (JSON_Equals(path, NULL, \"scale\") && s.outputs==1)\r\n\t\t\t\t\t\ta->bone[bone].scale = s;\r\n\t\t\t\t\telse if (JSON_Equals(path, NULL, \"translation\") && s.outputs==1)\r\n\t\t\t\t\t\ta->bone[bone].trans = s;\r\n\t\t\t\t\telse if (JSON_Equals(path, NULL, \"weights\") && s.outputs>=1)\r\n\t\t\t\t\t\ta->bone[bone].morph = s;\r\n\t\t\t\t\telse\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tif (gltf.warnlimit --> 0)\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tchar buf[64];\r\n\t\t\t\t\t\t\tJSON_ReadBody(path, buf, sizeof(buf));\r\n\t\t\t\t\t\t\tCon_Printf(\"%s: unknown animation data - %s\\n\", mod->name, buf);\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\r\n\t\t\t\tif (!maxtime)\r\n\t\t\t\t\tmaxtime = 1.0/30;\t//some stuff doesn't like 0-length animations. divisions by 0 are not nice.\r\n\t\t\t\ta->duration = maxtime;\r\n\t\t\t\t//calc average framerate\r\n\t\t\t\tfg->numposes = maxposes;\r\n\t\t\t\tif (maxtime&&fg->numposes>1)\r\n\t\t\t\t\tfg->rate = (fg->numposes-1)/maxtime;\t//fix up the rate so we hit the exact end of the animation (so it doesn't have to be quite so exact).\r\n\t\t\t\telse\r\n\t\t\t\t\tfg->rate = 60;\r\n\r\n\t\t\t\tfg->skeltype = SKEL_RELATIVE;\r\n\t\t\t\tfg->GetRawBones = GLTF_AnimateBones;\r\n\t\t\t\tfg->boneofs = a;\r\n\t\t\t}\r\n\r\n\t\t\tif (mergeanims)\r\n\t\t\t{\t//if we got this far then no two animations referenced the same bone.\r\n\t\t\t\t//this is a common issue with various sample models, I'm going to call it an exporter bug, and work around it by merging them into a single animation.\r\n\t\t\t\tfor (k = 1; k < numframegroups && framegroups[k].rate == framegroups[0].rate && framegroups[k].numposes == framegroups[0].numposes; k++)\r\n\t\t\t\t\t;\r\n\t\t\t\tif (k == numframegroups)\r\n\t\t\t\t{\t//yup, all have the same duration+posecount. lets call it a valid fix.\r\n\t\t\t\t\tstruct galiasanimation_gltf_s *merged = framegroups[0].boneofs;\r\n\t\t\t\t\tQ_snprintf(framegroups->name, sizeof(framegroups->name), \"allbones\");\r\n\t\t\t\t\tnumframegroups = 1;\r\n\t\t\t\t\tfor (k = 0; k < gltf.numbones; k++)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tif (mergeanims[k])\r\n\t\t\t\t\t\t\tmerged->bone[k] = mergeanims[k]->bone[k];\t//copy over that bone.\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tfor(surf = mod->meshinfo; surf; surf = surf->nextsurf)\r\n\t\t{\r\n\t\t\tsurf->shares_bones = 0;\r\n\t\t\tsurf->numbones = gltf.numbones;\r\n\t\t\tsurf->ofsbones = bone;\r\n\t\t\tsurf->ctx = gltfbone;\r\n\t\t\tsurf->baseframeofs = baseframe;\r\n\t\t\tsurf->ofsanimations = framegroups;\r\n\t\t\tsurf->numanimations = numframegroups;\r\n\t\t\tsurf->contents = FTECONTENTS_BODY;\r\n\t\t\tsurf->csurface.flags = 0;\r\n\t\t\tsurf->geomset = ~0;\t//invalid set = always visible. FIXME: set this according to scene numbers?\r\n\t\t\tsurf->geomid = 0;\r\n\r\n#ifndef SERVERONLY\r\n\t\t\tif (surf->numskins>1)\r\n\t\t\t{\r\n\t\t\t\tint i;\r\n\t\t\t\tQ_snprintf(surf->ofsskins[0].name, sizeof(surf->ofsskins[0].name), \"Default\");\r\n\t\t\t\tfor (i = 1; i < surf->numskins; i++)\r\n\t\t\t\t\tJSON_GetString(JSON_FindIndexedChild(gltf.r, \"extensions.KHR_materials_variants.variants\", i-1), \"name\", surf->ofsskins[i].name, sizeof(surf->ofsskins[i].name), surf->ofsskins[i].name);\r\n\t\t\t}\r\n#endif\r\n\t\t}\r\n\r\n\t\tVectorScale(mod->mins, mod_gltf_scale->value, mod->mins);\r\n\t\tVectorScale(mod->maxs, mod_gltf_scale->value, mod->maxs);\r\n\r\n\t\tif (!mod->meshinfo && numframegroups>0)\r\n\t\t\tCon_Printf(\"%s: Doesn't contain any meshes...\\n\", mod->name);\r\n\t\telse if (!mod_gltf_loop->ival)\r\n\t\t{\t//gltf doesn't specify if things loop or not.\r\n\t\t\t//our lerping is between previous+next frames, we don't actually handle wrapping here, and our 'numframes' is entirely arbitrary.\r\n\t\t\t//if the last frame and first frame are identical then its safe to say that it MAY loop, otherwise there'll be a judder when trying to do so.\r\n\t\t\t//and if it does seem to loop okay, don't hold that last frame for an extra 1/fps when the repeats starts, just go snap straight to the first frame.\r\n\t\t\tfloat *first = malloc(sizeof(*first)*12*gltf.numbones*2);\r\n\t\t\tfloat *last = first+12*gltf.numbones;\r\n\t\t\tgaliasanimation_t *fg;\r\n\t\t\tstruct galiasanimation_gltf_s *a;\r\n\t\t\tsurf = mod->meshinfo;\r\n\t\t\tfor (k = 0; k < numframegroups; k++)\r\n\t\t\t{\r\n\t\t\t\tfg = &framegroups[k];\r\n\t\t\t\ta = fg->boneofs;\r\n\r\n\t\t\t\tfg->GetRawBones(surf, fg, 0, first, NULL, gltf.numbones);\r\n\t\t\t\tfg->GetRawBones(surf, fg, a->duration, last, NULL, gltf.numbones);\t//should fall on an exact frame.\r\n\t\t\t\tfor (j = 0; j < 12*gltf.numbones; j++)\r\n\t\t\t\t\tif (first[j] != last[j])\r\n\t\t\t\t\t\tbreak;\r\n\t\t\t\tif (j == 12*gltf.numbones)\r\n\t\t\t\t{\r\n\t\t\t\t\tfg->loop = true;\r\n\t\t\t\t\tfg->numposes--;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\tfree(first);\r\n\t\t}\r\n\t\tJSON_WarnUnused(gltf.r, &gltf.warnlimit);\r\n\t}\r\n\telse\r\n\t\tCon_Printf(\"%s: unsupported gltf version (%.2f)\\n\", mod->name, gltfver);\r\nabort:\r\n\tJSON_Destroy(gltf.r);\r\n\tfree(gltf.bones);\r\n\tfree(gltf.bonemap);\r\n\r\n\tmod->type = mod_alias;\r\n\treturn !!mod->meshinfo;\r\n}\r\nqboolean QDECL Mod_LoadGLTFModel (struct model_s *mod, void *buffer, size_t fsize)\r\n{\r\n\t//just straight json.\r\n\treturn GLTF_LoadModel(mod, buffer, fsize, NULL, 0);\r\n}\r\n//glb files are some binary header, a lump with json data, and optionally a lump with binary data\r\nqboolean QDECL Mod_LoadGLBModel (struct model_s *mod, void *buffer, size_t fsize)\r\n{\r\n\tunsigned char *header = buffer;\r\n\tunsigned int magic = header[0]|(header[1]<<8)|(header[2]<<16)|(header[3]<<24);\t\t//gltf\r\n\tunsigned int version = header[4]|(header[5]<<8)|(header[6]<<16)|(header[7]<<24);\t//2\r\n\tunsigned int length = header[8]|(header[9]<<8)|(header[10]<<16)|(header[11]<<24);\t//fsize\r\n\r\n\tunsigned int jsonlen = header[12]|(header[13]<<8)|(header[14]<<16)|(header[15]<<24);\r\n\tunsigned int jsontype = header[16]|(header[17]<<8)|(header[18]<<16)|(header[19]<<24);\r\n\tchar *json = (char*)(header+20);\r\n\r\n\tif (fsize < 28)\r\n\t\treturn false;\r\n\tif (magic != (('F'<<24)+('T'<<16)+('l'<<8)+'g'))\r\n\t\treturn false;\r\n\tif (fsize < length)\r\n\t\treturn false;\t//allow padding on the end, but not truncation\r\n\tif (version == 1)\r\n\t{\r\n\t\tunsigned int binlen = (length-20) - jsonlen;\r\n\t\tunsigned char *bin = header+20+jsonlen;\r\n\r\n\t\tif (jsonlen&3)\r\n\t\t\treturn false;\t//exporter is expected to pad with spaces.\r\n\t\tif (jsontype != 0)\t//'JSON'\r\n\t\t\treturn false;\r\n\t\tif (length != 20+jsonlen+binlen)\r\n\t\t\treturn false;\r\n\r\n\t\treturn GLTF_LoadModel(mod, json, jsonlen, bin, binlen);\r\n\t}\r\n\telse if (version == 2)\r\n\t{\r\n\t\tunsigned int binlen = header[20+jsonlen]|(header[21+jsonlen]<<8)|(header[22+jsonlen]<<16)|(header[23+jsonlen]<<24);\r\n\t\tunsigned int bintype = header[24+jsonlen]|(header[25+jsonlen]<<8)|(header[26+jsonlen]<<16)|(header[27+jsonlen]<<24);\r\n\t\tunsigned char *bin = header+28+jsonlen;\r\n\r\n\t\tif (jsontype != 0x4E4F534A)\t//'JSON'\r\n\t\t\treturn false;\r\n\t\tif (length != 28+jsonlen+binlen)\r\n\t\t\treturn false;\r\n\t\tif (bintype != 0x004E4942)\t//'BIN\\0'\r\n\t\t\treturn false;\r\n\r\n\t\treturn GLTF_LoadModel(mod, json, jsonlen, bin, binlen);\r\n\t}\r\n\treturn false;\r\n}\r\n\r\nqboolean Plug_GLTF_Init(void)\r\n{\r\n\tfilefuncs = plugfuncs->GetEngineInterface(plugfsfuncs_name, sizeof(*filefuncs));\r\n\tmodfuncs = plugfuncs->GetEngineInterface(plugmodfuncs_name, sizeof(*modfuncs));\r\n\tif (modfuncs && modfuncs->version != MODPLUGFUNCS_VERSION)\r\n\t\tmodfuncs = NULL;\r\n\tmod_gltf_scale = cvarfuncs->GetNVFDG(\"mod_gltf_scale\", \"30\", CVAR_RENDERERLATCH, \"This defines the number of units per metre, in order to correctly load standard-scale gltf models.\", \"GLTF Models\");\r\n\tmod_gltf_fixbuggyanims = cvarfuncs->GetNVFDG(\"mod_gltf_fixbuggyanims\", \"1\", CVAR_RENDERERLATCH, \"Work around buggy exporters by merging animations that affect only a single bone.\", \"GLTF Models\");\r\n#ifdef IQMTOOL\r\n\tmod_gltf_loop = cvarfuncs->GetNVFDG(\"mod_gltf_loop\", \"0\", CVAR_RENDERERLATCH, \"This forces all gltf models to loop, instead of only when the last frame matches the first.\", \"GLTF Models\");\r\n\tmod_gltf_privatematerials = cvarfuncs->GetNVFDG(\"mod_gltf_privatematerials\", \"0\", CVAR_RENDERERLATCH, \"Add the model path to material names, to isolate them between different models.\", \"GLTF Models\");\r\n#else\r\n\tmod_gltf_loop = cvarfuncs->GetNVFDG(\"mod_gltf_loop\", \"1\", CVAR_RENDERERLATCH, \"This forces all gltf models to loop, instead of only when the last frame matches the first.\", \"GLTF Models\");\r\n\tmod_gltf_privatematerials = cvarfuncs->GetNVFDG(\"mod_gltf_privatematerials\", \"1\", CVAR_RENDERERLATCH, \"Add the model path to material names, to isolate them between different models.\", \"GLTF Models\");\r\n#endif\r\n\tmod_gltf_ignoretechniques = cvarfuncs->GetNVFDG(\"mod_gltf_ignoretechniques\", \"1\", CVAR_RENDERERLATCH, \"Ignore the GLTF-1 model-specific glsl. This is enabled by default because it just doesn't work very well.\", \"GLTF Models\");\r\n\tmod_gltf_standardorientation = cvarfuncs->GetNVFDG(\"mod_gltf_standardorientation\", \"1\", CVAR_RENDERERLATCH, \"Transform GLTF files from standard y-up to Quake's z-up orientation. Set to 0 to violate the GLTF specification.\", \"GLTF Models\");\r\n\r\n\tif (modfuncs && filefuncs)\r\n\t{\r\n\t\tmodfuncs->RegisterModelFormatText(\"glTF models (glTF)\", \".gltf\", Mod_LoadGLTFModel);\r\n\t\tmodfuncs->RegisterModelFormatMagic(\"glTF models (glb)\", \"glTF\",4, Mod_LoadGLBModel);\r\n\t\treturn true;\r\n\t}\r\n\treturn false;\r\n}\r\n#endif\r\n\r\n"
  },
  {
    "path": "plugins/models/models.c",
    "content": "#ifndef GLQUAKE\r\n#define GLQUAKE\t//this is shit.\r\n#endif\r\n#include \"quakedef.h\"\r\n#include \"../plugin.h\"\r\n#include \"com_mesh.h\"\r\nplugmodfuncs_t *modfuncs;\r\nplugfsfuncs_t *filefuncs;\r\n\r\n//#define ASEMODELS\t//FIXME: TEST TEST TEST. shold be working iiuc\r\n//#define LWOMODELS\t//not working\r\n#ifdef SKELETALMODELS\r\n#define GLTFMODELS\t//FIXME: not yet working properly.\r\nextern qboolean Plug_GLTF_Init(void);\r\n#endif\r\n\r\n#ifdef ASEMODELS\r\nstruct aseimport_s\r\n{\r\n\tchar *file;\r\n\r\n\tstruct asematerial_s\r\n\t{\r\n\t\tchar name[MAX_QPATH];\r\n\r\n\t\tstruct asemesh_s\r\n\t\t{\r\n\t\t\tstruct asemesh_s *next;\r\n\r\n\t\t\tstruct asevert_s\r\n\t\t\t{\r\n\t\t\t\tvec3_t xyz;\r\n\t\t\t\tvec3_t norm;\r\n\t\t\t\tvec2_t st;\r\n\t\t\t} *vert;\r\n\t\t\tunsigned int numverts;\r\n\t\t\tunsigned int maxverts;\r\n\r\n\t\t\tindex_t *index;\r\n\t\t\tunsigned int numindexes;\r\n\t\t\tunsigned int maxindexes;\r\n\r\n\t\t} *meshes;\r\n\t} *materials;\r\n\tunsigned int nummaterials;\r\n};\r\n\r\nstatic char *ASE_GetToken(struct aseimport_s *ase, char *token, size_t tokensize)\r\n{\r\n\tchar *ret = token;\r\n\ttokensize--;\r\n\r\n\twhile(*ase->file == ' ' || *ase->file == '\\t')\r\n\t\tase->file++;\r\n\tif (*ase->file == '\\n')\r\n\t{\r\n\t\t*ret = 0;\r\n\t\treturn ret;\r\n\t}\r\n\r\n\tif (*ase->file == '\\\"')\r\n\t{\r\n\t\tase->file += 1;\r\n\t\twhile(tokensize-- > 0)\r\n\t\t{\r\n\t\t\tchar i = *ase->file;\r\n\t\t\tif (i == '\\n')\r\n\t\t\t\tbreak;\r\n\t\t\tase->file+=1;\r\n\t\t\tif (i == '\\\"')\r\n\t\t\t\tbreak;\r\n\r\n\t\t\t*token++ = i;\r\n\t\t}\r\n\t}\r\n\telse\r\n\t{\r\n\t\twhile(tokensize-- > 0)\r\n\t\t{\r\n\t\t\tchar i = *ase->file;\r\n\t\t\tif (i == ' ' || i == '\\t' || i == '\\r' || i == '\\n')\r\n\t\t\t\tbreak;\r\n\t\t\tase->file+=1;\r\n\r\n\t\t\t*token++ = i;\r\n\t\t}\r\n\t}\r\n\t*token = 0;\r\n\treturn ret;\r\n}\r\n\r\nstatic void ASE_SkipLine(struct aseimport_s *ase)\r\n{\r\n\twhile (*ase->file)\r\n\t{\r\n\t\tif (*ase->file == '\\n')\r\n\t\t{\r\n\t\t\tase->file += 1;\r\n\t\t\tbreak;\r\n\t\t}\r\n\t\tase->file += 1;\r\n\t}\r\n}\r\n\r\nstatic void ASE_SkipBlock(struct aseimport_s *ase)\r\n{\r\n\twhile (*ase->file)\r\n\t{\r\n\t\tif (*ase->file == '\\n' || *ase->file == '{')\r\n\t\t\tbreak;\r\n\t\tase->file += 1;\r\n\t}\r\n\tif (*ase->file == '{')\r\n\t{\r\n\t\tchar token[1024];\r\n\t\tASE_SkipLine(ase);\r\n\r\n\t\twhile (*ase->file)\r\n\t\t{\r\n\t\t\tASE_GetToken(ase, token, sizeof(token));\r\n\t\t\tif (!strcmp(token, \"}\"))\r\n\t\t\t\tbreak;\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tASE_SkipBlock(ase);\r\n\t\t\t}\r\n\t\t\tASE_SkipLine(ase);\r\n\t\t}\r\n\t}\r\n}\r\n\r\nstatic void ASE_Material(struct aseimport_s *ase)\r\n{\r\n\tint idx;\r\n\tchar token[1024];\r\n\tASE_GetToken(ase, token, sizeof(token));\r\n\tidx = atoi(token);\r\n\r\n\tif (idx < 0 || idx >= ase->nummaterials)\r\n\t{\r\n\t\tCon_Printf(\"invalid material index: %s\\n\", token);\r\n\t\tASE_SkipBlock(ase);\r\n\t\treturn;\r\n\t}\r\n\r\n\tASE_GetToken(ase, token, sizeof(token));\r\n\tASE_SkipLine(ase);\r\n\tif (strcmp(token, \"{\"))\r\n\t\treturn;\r\n\r\n\twhile (*ase->file)\r\n\t{\r\n\t\tASE_GetToken(ase, token, sizeof(token));\r\n\t\tif (!strcmp(token, \"}\"))\r\n\t\t\tbreak;\r\n\t\telse if (!strcmp(token, \"*MATERIAL_NAME\"))\r\n\t\t{\r\n\t\t\tASE_GetToken(ase, token, sizeof(token));\r\n\t\t\tQ_strlcpy(ase->materials[idx].name, token, sizeof(ase->materials[idx].name));\r\n\t\t}\r\n\t\telse if (!strcmp(token, \"*MATERIAL_CLASS\") || !strcmp(token, \"*MATERIAL_DIFFUSE\") || !strcmp(token, \"*MATERIAL_SHADING\") || !strcmp(token, \"*MAP_DIFFUSE\"))\r\n\t\t\tASE_SkipBlock(ase);\r\n\t\telse\r\n\t\t{\r\n\t\t\tCon_Printf(\"Unknown top-level identifier: %s\\n\", token);\r\n\t\t\tASE_SkipBlock(ase);\r\n\t\t}\r\n\t\tASE_SkipLine(ase);\r\n\t}\r\n}\r\nstatic void ASE_MaterialList(struct aseimport_s *ase)\r\n{\r\n\tchar token[1024];\r\n\tASE_GetToken(ase, token, sizeof(token));\r\n\tASE_SkipLine(ase);\r\n\tif (strcmp(token, \"{\"))\r\n\t\treturn;\r\n\r\n\twhile (*ase->file)\r\n\t{\r\n\t\tASE_GetToken(ase, token, sizeof(token));\r\n\t\tif (!strcmp(token, \"}\"))\r\n\t\t\tbreak;\r\n\t\telse if (!strcmp(token, \"*MATERIAL_COUNT\"))\r\n\t\t{\r\n\t\t\tASE_GetToken(ase, token, sizeof(token));\r\n\r\n\t\t\tif (!ase->materials)\r\n\t\t\t{\r\n\t\t\t\tase->nummaterials = atoi(token);\r\n\t\t\t\tase->materials = malloc(sizeof(*ase->materials) * ase->nummaterials);\r\n\t\t\t\tmemset(ase->materials, 0, sizeof(*ase->materials) * ase->nummaterials);\r\n\t\t\t}\r\n\t\t}\r\n\t\telse if (!strcmp(token, \"*MATERIAL\"))\r\n\t\t{\r\n\t\t\tASE_Material(ase);\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tCon_Printf(\"Unknown top-level identifier: %s\\n\", token);\r\n\t\t\tASE_SkipBlock(ase);\r\n\t\t}\r\n\t\tASE_SkipLine(ase);\r\n\t}\r\n}\r\n\r\nstatic void ASE_GeomObject(struct aseimport_s *ase)\r\n{\r\n\tsize_t materialidx = 0;\r\n\tsize_t numverts = 0;\r\n\tsize_t numtverts = 0;\r\n\tsize_t numtris = 0;\r\n\tstruct\r\n\t{\r\n\t\tvec3_t xyz;\r\n\t\tvec3_t norm;\r\n\t} *verts = NULL;\r\n\tstruct\r\n\t{\r\n\t\tvec3_t st;\r\n\t} *tverts = NULL;\r\n\tstruct\r\n\t{\r\n\t\tindex_t vidx[3];\r\n\t\tindex_t tidx[3];\r\n\t} *tris = NULL;\r\n\tsize_t idx;\r\n\r\n\tchar token[1024];\r\n\tASE_GetToken(ase, token, sizeof(token));\r\n\tASE_SkipLine(ase);\r\n\tif (strcmp(token, \"{\"))\r\n\t\treturn;\r\n\r\n\twhile (*ase->file)\r\n\t{\r\n\t\tASE_GetToken(ase, token, sizeof(token));\r\n\t\tif (!strcmp(token, \"}\"))\r\n\t\t\tbreak;\r\n\t\telse if (!strcmp(token, \"*MATERIAL_REF\"))\r\n\t\t{\r\n\t\t\tASE_GetToken(ase, token, sizeof(token));\r\n\t\t\tmaterialidx = atoi(token);\r\n\t\t}\r\n\t\telse if (!strcmp(token, \"*MESH\"))\r\n\t\t{\r\n\t\t\tASE_SkipLine(ase);\r\n\r\n\t\t\twhile (*ase->file)\r\n\t\t\t{\r\n\t\t\t\tASE_GetToken(ase, token, sizeof(token));\r\n\t\t\t\tif (!strcmp(token, \"}\"))\r\n\t\t\t\t\tbreak;\r\n\t\t\t\telse if (!strcmp(token, \"*TIMEVALUE\"))\r\n\t\t\t\t\tASE_SkipBlock(ase);\t//not useful\r\n\t\t\t\telse if (!strcmp(token, \"*MESH_NUMVERTEX\"))\r\n\t\t\t\t{\r\n\t\t\t\t\tASE_GetToken(ase, token, sizeof(token));\r\n\t\t\t\t\tif (!verts)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tnumverts = atoi(token);\r\n\t\t\t\t\t\tverts = malloc(sizeof(*verts) * numverts);\r\n\t\t\t\t\t\tmemset(verts, 0, sizeof(*verts) * numverts);\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t\telse if (!strcmp(token, \"*MESH_NUMFACES\"))\r\n\t\t\t\t{\r\n\t\t\t\t\tASE_GetToken(ase, token, sizeof(token));\r\n\t\t\t\t\tif (!tris)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tnumtris = atoi(token);\r\n\t\t\t\t\t\ttris = malloc(sizeof(*tris) * numtris);\r\n\t\t\t\t\t\tmemset(tris, 0, sizeof(*tris) * numtris);\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t\telse if (!strcmp(token, \"*COMMENT\"))\r\n\t\t\t\t\tASE_SkipBlock(ase);\t//unused\r\n\t\t\t\telse if (!strcmp(token, \"*MESH_VERTEX_LIST\"))\r\n\t\t\t\t{\r\n\t\t\t\t\tASE_SkipLine(ase);\r\n\t\t\t\t\twhile (*ase->file)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tASE_GetToken(ase, token, sizeof(token));\r\n\t\t\t\t\t\tif (!strcmp(token, \"}\"))\r\n\t\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t\telse if (!strcmp(token, \"*MESH_VERTEX\"))\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tASE_GetToken(ase, token, sizeof(token));\r\n\t\t\t\t\t\t\tidx = atoi(token);\r\n\t\t\t\t\t\t\tif (idx >= 0 && idx < numverts)\r\n\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\tASE_GetToken(ase, token, sizeof(token));\r\n\t\t\t\t\t\t\t\tverts[idx].xyz[0] = atof(token);\r\n\t\t\t\t\t\t\t\tASE_GetToken(ase, token, sizeof(token));\r\n\t\t\t\t\t\t\t\tverts[idx].xyz[1] = atof(token);\r\n\t\t\t\t\t\t\t\tASE_GetToken(ase, token, sizeof(token));\r\n\t\t\t\t\t\t\t\tverts[idx].xyz[2] = atof(token);\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\telse\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tCon_Printf(\"Unknown MESH_VERTEX_LIST identifier: %s\\n\", token);\r\n\t\t\t\t\t\t\tASE_SkipBlock(ase);\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\tASE_SkipLine(ase);\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t\telse if (!strcmp(token, \"*MESH_NORMALS\"))\r\n\t\t\t\t{\r\n\t\t\t\t\tASE_SkipLine(ase);\r\n\t\t\t\t\twhile (*ase->file)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tASE_GetToken(ase, token, sizeof(token));\r\n\t\t\t\t\t\tif (!strcmp(token, \"}\"))\r\n\t\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t\telse if (!strcmp(token, \"*MESH_FACENORMAL\"))\r\n\t\t\t\t\t\t\tASE_SkipBlock(ase);\t//we don't give a fuck about these. its not usable for us. we'll calculate these if we actually need them, that way we're sure it actually matches the geometry and doesn't bug out.\r\n\t\t\t\t\t\telse if (!strcmp(token, \"*MESH_VERTEXNORMAL\"))\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tASE_GetToken(ase, token, sizeof(token));\r\n\t\t\t\t\t\t\tidx = atoi(token);\r\n\t\t\t\t\t\t\tif (idx >= 0 && idx < numverts)\r\n\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\tASE_GetToken(ase, token, sizeof(token));\r\n\t\t\t\t\t\t\t\tverts[idx].norm[0] = atof(token);\r\n\t\t\t\t\t\t\t\tASE_GetToken(ase, token, sizeof(token));\r\n\t\t\t\t\t\t\t\tverts[idx].norm[1] = atof(token);\r\n\t\t\t\t\t\t\t\tASE_GetToken(ase, token, sizeof(token));\r\n\t\t\t\t\t\t\t\tverts[idx].norm[2] = atof(token);\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\telse\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tCon_Printf(\"Unknown MESH_NORMALS identifier: %s\\n\", token);\r\n\t\t\t\t\t\t\tASE_SkipBlock(ase);\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\tASE_SkipLine(ase);\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t\telse if (!strcmp(token, \"*MESH_FACE_LIST\"))\r\n\t\t\t\t{\r\n\t\t\t\t\tASE_SkipLine(ase);\r\n\t\t\t\t\twhile (*ase->file)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tASE_GetToken(ase, token, sizeof(token));\r\n\t\t\t\t\t\tif (!strcmp(token, \"}\"))\r\n\t\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t\telse if (!strcmp(token, \"*MESH_FACE\"))\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tASE_GetToken(ase, token, sizeof(token));\r\n\t\t\t\t\t\t\tidx = atoi(token);\r\n\t\t\t\t\t\t\tif (idx >= 0 && idx < numtris)\r\n\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\tASE_GetToken(ase, token, sizeof(token));\r\n\t\t\t\t\t\t\t\twhile(*token)\r\n\t\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\t\tif (!strcmp(token, \"A:\"))\r\n\t\t\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\t\t\tASE_GetToken(ase, token, sizeof(token));\r\n\t\t\t\t\t\t\t\t\t\ttris[idx].vidx[0] = atoi(token);\r\n\t\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t\t\telse if (!strcmp(token, \"B:\"))\r\n\t\t\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\t\t\tASE_GetToken(ase, token, sizeof(token));\r\n\t\t\t\t\t\t\t\t\t\ttris[idx].vidx[1] = atoi(token);\r\n\t\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t\t\telse if (!strcmp(token, \"C:\"))\r\n\t\t\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\t\t\tASE_GetToken(ase, token, sizeof(token));\r\n\t\t\t\t\t\t\t\t\t\ttris[idx].vidx[2] = atoi(token);\r\n\t\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t\t\telse if (!strcmp(token, \"AB:\") || !strcmp(token, \"BC:\") || !strcmp(token, \"CA:\") || !strcmp(token, \"*MESH_SMOOTHING\") || !strcmp(token, \"*MESH_MTLID\"))\r\n\t\t\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\t\t\tASE_GetToken(ase, token, sizeof(token));\r\n\t\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t\t\telse\r\n\t\t\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\t\t\tCon_Printf(\"Unknown MESH_FACE identifier: %s\\n\", token);\r\n\t\t\t\t\t\t\t\t\t\tASE_GetToken(ase, token, sizeof(token));\r\n\t\t\t\t\t\t\t\t\t}\r\n\r\n\t\t\t\t\t\t\t\t\tASE_GetToken(ase, token, sizeof(token));\r\n\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t\ttris[idx].tidx[0] = atoi(token);\r\n\t\t\t\t\t\t\t\tASE_GetToken(ase, token, sizeof(token));\r\n\t\t\t\t\t\t\t\ttris[idx].tidx[1] = atoi(token);\r\n\t\t\t\t\t\t\t\tASE_GetToken(ase, token, sizeof(token));\r\n\t\t\t\t\t\t\t\ttris[idx].tidx[2] = atoi(token);\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t}\t\r\n\t\t\t\t\t\telse\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tCon_Printf(\"Unknown MESH_FACE_LIST identifier: %s\\n\", token);\r\n\t\t\t\t\t\t\tASE_SkipBlock(ase);\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\tASE_SkipLine(ase);\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t\telse if (!strcmp(token, \"*MESH_NUMTVERTEX\"))\r\n\t\t\t\t{\r\n\t\t\t\t\tASE_GetToken(ase, token, sizeof(token));\r\n\t\t\t\t\tif (!tverts)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tnumtverts = atoi(token);\r\n\t\t\t\t\t\ttverts = malloc(sizeof(*tverts) * numtverts);\r\n\t\t\t\t\t\tmemset(tverts, 0, sizeof(*tverts) * numtverts);\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t\telse if (!strcmp(token, \"*MESH_TVERTLIST\"))\r\n\t\t\t\t{\r\n\t\t\t\t\tASE_SkipLine(ase);\r\n\t\t\t\t\twhile (*ase->file)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tASE_GetToken(ase, token, sizeof(token));\r\n\t\t\t\t\t\tif (!strcmp(token, \"}\"))\r\n\t\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t\telse if (!strcmp(token, \"*MESH_TVERT\"))\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tASE_GetToken(ase, token, sizeof(token));\r\n\t\t\t\t\t\t\tidx = atoi(token);\r\n\t\t\t\t\t\t\tif (idx >= 0 && idx < numverts)\r\n\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\tASE_GetToken(ase, token, sizeof(token));\r\n\t\t\t\t\t\t\t\ttverts[idx].st[0] = atof(token);\r\n\t\t\t\t\t\t\t\tASE_GetToken(ase, token, sizeof(token));\r\n\t\t\t\t\t\t\t\ttverts[idx].st[1] = atof(token);\r\n\t\t\t\t\t\t\t\tASE_GetToken(ase, token, sizeof(token));\r\n\t\t\t\t\t\t\t\ttverts[idx].st[2] = atof(token);\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\telse\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tCon_Printf(\"Unknown MESH_TVERTLIST identifier: %s\\n\", token);\r\n\t\t\t\t\t\t\tASE_SkipBlock(ase);\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\tASE_SkipLine(ase);\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t\telse if (!strcmp(token, \"*MESH_NUMTVFACES\"))\r\n\t\t\t\t\tASE_SkipBlock(ase);\t//should be equal to MESH_NUMFACES\r\n\t\t\t\telse if (!strcmp(token, \"*MESH_TFACELIST\"))\r\n\t\t\t\t{\r\n\t\t\t\t\tASE_SkipLine(ase);\r\n\t\t\t\t\twhile (*ase->file)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tASE_GetToken(ase, token, sizeof(token));\r\n\t\t\t\t\t\tif (!strcmp(token, \"}\"))\r\n\t\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t\telse if (!strcmp(token, \"*MESH_TFACE\"))\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tASE_GetToken(ase, token, sizeof(token));\r\n\t\t\t\t\t\t\tidx = atoi(token);\r\n\t\t\t\t\t\t\tif (idx >= 0 && idx < numtris)\r\n\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\tASE_GetToken(ase, token, sizeof(token));\r\n\t\t\t\t\t\t\t\ttris[idx].tidx[0] = atoi(token);\r\n\t\t\t\t\t\t\t\tASE_GetToken(ase, token, sizeof(token));\r\n\t\t\t\t\t\t\t\ttris[idx].tidx[1] = atoi(token);\r\n\t\t\t\t\t\t\t\tASE_GetToken(ase, token, sizeof(token));\r\n\t\t\t\t\t\t\t\ttris[idx].tidx[2] = atoi(token);\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\telse\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tCon_Printf(\"Unknown MESH_TFACELIST identifier: %s\\n\", token);\r\n\t\t\t\t\t\t\tASE_SkipBlock(ase);\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\tASE_SkipLine(ase);\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\tCon_Printf(\"Unknown top-level identifier: %s\\n\", token);\r\n\t\t\t\t\tASE_SkipBlock(ase);\r\n\t\t\t\t}\r\n\t\t\t\tASE_SkipLine(ase);\r\n\t\t\t}\r\n\t\t}\r\n\t\telse if (!strcmp(token, \"*NODE_NAME\") || !strcmp(token, \"*NODE_TM\")\r\n\t\t\t|| !strcmp(token, \"*PROP_MOTIONBLUR\")|| !strcmp(token, \"*PROP_CASTSHADOW\")|| !strcmp(token, \"*PROP_RECVSHADOW\"))\r\n\t\t\tASE_SkipBlock(ase);\r\n\t\telse\r\n\t\t{\r\n\t\t\tCon_Printf(\"Unknown top-level identifier: %s\\n\", token);\r\n\t\t\tASE_SkipBlock(ase);\r\n\t\t}\r\n\t\tASE_SkipLine(ase);\r\n\t}\r\n\r\n\t//merge into a mesh\r\n\tif (materialidx >= 0 && materialidx < ase->nummaterials)\r\n\t{\r\n\t\tsize_t v, tri, idx;\r\n\t\tstruct asemesh_s *mesh;\r\n\t\tstruct asevert_s asevert;\r\n\t\tmesh = ase->materials[materialidx].meshes;\r\n\r\n\t\t//don't let any single mesh exceed 65k verts. stuff bugs out then.\r\n\t\tif (!mesh || mesh->numverts + numtris*3 > 0xffff)\r\n\t\t{\r\n\t\t\tmesh = malloc(sizeof(*mesh));\r\n\t\t\tmemset(mesh, 0, sizeof(*mesh));\r\n\t\t\tmesh->next = ase->materials[materialidx].meshes;\r\n\t\t\tase->materials[materialidx].meshes = mesh;\r\n\t\t}\r\n\r\n\t\t//make sure there's going to be enough space\r\n\t\tif (mesh->numverts + numtris*3 > mesh->maxverts)\r\n\t\t{\r\n\t\t\tmesh->maxverts = (mesh->maxverts+numtris*3)*2;\r\n\t\t\tmesh->vert = realloc(mesh->vert, sizeof(*mesh->vert) * mesh->maxverts);\r\n\t\t}\r\n\t\tif (mesh->numindexes + numtris*3 > mesh->maxindexes)\r\n\t\t{\r\n\t\t\tmesh->maxindexes = (mesh->maxindexes+numtris*3)*2;\r\n\t\t\tmesh->index = realloc(mesh->index, sizeof(*mesh->index) * mesh->maxindexes);\r\n\t\t}\r\n\r\n\t\t//insert each triangle into the mesh\r\n\t\tfor (tri = 0; tri < numtris; tri++)\r\n\t\t{\r\n\t\t\tfor (v = 3; v --> 0; )\r\n\t\t\t{\r\n\t\t\t\tVectorCopy(verts[tris[tri].vidx[v]].xyz, asevert.xyz);\r\n\t\t\t\tVectorCopy(verts[tris[tri].vidx[v]].norm, asevert.norm);\r\n\t\t\t\tVector2Copy(tverts[tris[tri].tidx[v]].st, asevert.st);\r\n\r\n\t\t\t\tfor (idx = 0; idx < mesh->numverts; idx++)\r\n\t\t\t\t{\r\n\t\t\t\t\tif (!memcmp(&mesh->vert[idx], &asevert, sizeof(asevert)))\r\n\t\t\t\t\t\tbreak;\r\n\t\t\t\t}\r\n\t\t\t\tif (idx == mesh->numverts)\r\n\t\t\t\t\tmesh->vert[mesh->numverts++] = asevert;\r\n\t\t\t\tmesh->index[mesh->numindexes++] = idx;\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\tfree(verts);\r\n\tfree(tverts);\r\n\tfree(tris);\r\n}\r\n\r\nstatic void ASE_TopLevel(struct aseimport_s *ase)\r\n{\r\n\tchar token[1024];\r\n\twhile (*ase->file)\r\n\t{\r\n\t\tASE_GetToken(ase, token, sizeof(token));\r\n\t\tif (!strcmp(token, \"*3DSMAX_ASCIIEXPORT\"))\r\n\t\t{\r\n\t\t\tASE_GetToken(ase, token, sizeof(token));\t//version\r\n\t\t}\r\n\t\telse if (!strcmp(token, \"*COMMENT\") || !strcmp(token, \"*SCENE\"))\r\n\t\t\tASE_SkipBlock(ase);\r\n\t\telse if (!strcmp(token, \"*MATERIAL_LIST\"))\r\n\t\t\tASE_MaterialList(ase);\r\n\t\telse if (!strcmp(token, \"*GEOMOBJECT\"))\r\n\t\t\tASE_GeomObject(ase);\r\n\t\telse\r\n\t\t{\r\n\t\t\tCon_Printf(\"Unknown top-level identifier: %s\\n\", token);\r\n\t\t\tASE_SkipBlock(ase);\r\n\t\t}\r\n\r\n\t\tASE_SkipLine(ase);\r\n\t}\r\n}\r\nstatic qboolean QDECL Mod_LoadASEModel (struct model_s *mod, void *buffer, size_t fsize)\r\n{\r\n\tgaliaspose_t *pose;\r\n\tgaliasinfo_t *surf;\r\n\tsize_t i, m;\r\n\tstruct aseimport_s ase;\r\n\tmemset(&ase, 0, sizeof(ase));\r\n\tase.file = buffer;\r\n\tASE_TopLevel(&ase);\r\n\r\n\t//we need to generate engine meshes and clean up any dynamic memory\r\n\tfor (m = 0; m < ase.nummaterials; m++)\r\n\t{\r\n\t\tstruct asemesh_s *mesh;\r\n\t\twhile(ase.materials[m].meshes)\r\n\t\t{\r\n\t\t\tmesh = ase.materials[m].meshes;\r\n\t\t\tase.materials[m].meshes = mesh->next;\r\n\r\n\t\t\tsurf = modfuncs->ZG_Malloc(&mod->memgroup, sizeof(*surf));\r\n\t\t\tsurf->nextsurf = mod->meshinfo;\r\n\t\t\tmod->meshinfo = surf;\r\n\r\n\t\t\tsurf->numverts = mesh->numverts;\r\n\t\t\tsurf->numindexes = mesh->numindexes;\r\n\r\n\t\t\tsurf->ofs_indexes = modfuncs->ZG_Malloc(&mod->memgroup, sizeof(*surf->ofs_indexes) * mesh->numindexes);\r\n\t\t\tfor (i = 0; i < mesh->numindexes; i++)\r\n\t\t\t\tsurf->ofs_indexes[i] = mesh->index[i];\r\n\r\n\t\t\tsurf->numanimations = 1;\r\n\t\t\tsurf->ofsanimations = modfuncs->ZG_Malloc(&mod->memgroup, sizeof(*surf->ofsanimations));\r\n\t\t\tsurf->ofsanimations->poseofs = pose = modfuncs->ZG_Malloc(&mod->memgroup, sizeof(*pose));\r\n\t\t\tsurf->ofsanimations->loop = true;\r\n\t\t\tsurf->ofsanimations->numposes = 1;\r\n\t\t\tsurf->ofsanimations->rate = 10;\r\n\r\n\t\t\tpose->ofsverts = modfuncs->ZG_Malloc(&mod->memgroup, sizeof(*pose->ofsverts) * mesh->numverts);\r\n\t\t\tpose->ofsnormals = modfuncs->ZG_Malloc(&mod->memgroup, sizeof(*pose->ofsnormals) * mesh->numverts);\r\n\t\t\tsurf->ofs_st_array = modfuncs->ZG_Malloc(&mod->memgroup, sizeof(*surf->ofs_st_array) * mesh->numverts);\r\n\r\n\t\t\tfor (i = 0; i < mesh->numverts; i++)\r\n\t\t\t{\r\n\t\t\t\tVectorCopy(mesh->vert[i].xyz, pose->ofsverts[i]);\r\n\t\t\t\tVectorCopy(mesh->vert[i].norm, pose->ofsnormals[i]);\r\n\t\t\t\tVector2Copy(mesh->vert[i].st, surf->ofs_st_array[i]);\r\n\t\t\t}\r\n\r\n\t\t\tsurf->numskins = 1;\r\n\t\t\tsurf->ofsskins = modfuncs->ZG_Malloc(&mod->memgroup, sizeof(*surf->ofsskins));\r\n\t\t\tsurf->ofsskins->numframes = 1;\r\n\t\t\tsurf->ofsskins->skinspeed = 0.1;\r\n\t\t\tsurf->ofsskins->frame = modfuncs->ZG_Malloc(&mod->memgroup, sizeof(*surf->ofsskins->frame));\r\n\t\t\tQ_strlcpy(surf->ofsskins->frame->shadername, ase.materials[m].name, sizeof(surf->ofsskins->frame->shadername));\r\n\t\t\tQ_strlcpy(surf->ofsskins->name, ase.materials[m].name, sizeof(surf->ofsskins->name));\r\n\r\n\t\t\tfree(mesh->vert);\r\n\t\t\tfree(mesh->index);\r\n\t\t\tfree(mesh);\r\n\t\t}\r\n\t}\r\n\tfree(ase.materials);\r\n\r\n\tmod->type = mod_alias;\r\n\treturn !!mod->meshinfo;\r\n}\r\n#endif\r\n\r\n\r\n\r\n#ifdef LWOMODELS\r\nstatic char *LWO_ReadString(unsigned char **file)\r\n{\t//strings are just null terminated.\r\n\t//however, they may have an extra byte of padding. yay shorts...\r\n\t//so skip the null and round up the length.\r\n\tchar *ret = *file;\r\n\tsize_t len = strlen(ret);\r\n\t*file = *file + ((len + 2)&~1);\r\n\treturn ret;\r\n}\r\n\r\nstatic qboolean QDECL Mod_LoadLWOModel (struct model_s *mod, void *buffer, size_t fsize)\r\n{\r\n\tunsigned char *file = buffer, *fileend;\r\n\tchar *tags;\r\n\tvec3_t *points;\r\n\tsize_t numpoints;\r\n\tunsigned int tagssize;\r\n\tsize_t subsize;\r\n\tsubsize = file[7] | (file[6]<<8) | (file[5]<<16) | (file[4]<<24);\r\n\tif (strncmp(file, \"FORM\", 4) || subsize+8 != fsize)\r\n\t\treturn false;\t//not an lwo, or corrupt, or something\r\n\tfile += 8;\r\n\tfileend = file + subsize;\r\n\tif (strncmp(file, \"LWO2\", 4))\r\n\t\treturn false;\r\n\tfile += 4;\r\n\twhile (file < fileend)\r\n\t{\r\n\t\tif (!strncmp(file, \"TAGS\", 4))\r\n\t\t{\r\n\t\t\ttagssize = file[7] | (file[6]<<8) | (file[5]<<16) | (file[4]<<24);\r\n\t\t\ttags = file+8;\r\n\t\t\tfile += 8+tagssize;\r\n\t\t}\r\n\t\telse if (!strncmp(file, \"LAYR\", 4))\r\n\t\t{\r\n\t\t\tsubsize = file[7] | (file[6]<<8) | (file[5]<<16) | (file[4]<<24);\r\n\t\t\t//fixme:\r\n\t\t\tfile += 8+subsize;\r\n\t\t}\r\n\t\telse if (!strncmp(file, \"PNTS\", 4))\r\n\t\t{\r\n\t\t\tsubsize = file[7] | (file[6]<<8) | (file[5]<<16) | (file[4]<<24);\r\n\t\t\tnumpoints = subsize / sizeof(vec3_t);\r\n\t\t\tpoints = (vec3_t*)(file+8);\r\n\t\t\tfile += 8+subsize;\r\n\t\t}\r\n\t\telse if (!strncmp(file, \"BBOX\", 4))\r\n\t\t{\r\n\t\t\tsubsize = file[7] | (file[6]<<8) | (file[5]<<16) | (file[4]<<24);\r\n\t\t\t//we don't really care.\r\n\t\t\tfile += 8+subsize;\r\n\t\t}\r\n\t\telse if (!strncmp(file, \"VMAP\", 4))\r\n\t\t{\r\n\t\t\tsubsize = file[7] | (file[6]<<8) | (file[5]<<16) | (file[4]<<24);\r\n\t\t\tif (!strcmp(file, \"TXUV\"))\r\n\t\t\t{\r\n\t\t\t}\r\n//\t\t\telse if (!strcmp(file, \"RGBA\") || !strcmp(file, \"RGB\"))\r\n//\t\t\t{\r\n//\t\t\t}\r\n\t\t\tfile += 8+subsize;\r\n\t\t}\r\n\t\telse if (!strncmp(file, \"POLS\", 4))\r\n\t\t{\r\n\t\t\tsubsize = file[7] | (file[6]<<8) | (file[5]<<16) | (file[4]<<24);\r\n\t\t\tfile += 8;\r\n\t\t\tif (!strcmp(file, \"FACE\") || !strcmp(file, \"PTCH\"))\r\n\t\t\t{\r\n\r\n\t\t\t}\r\n\t\t\tfile += subsize;\r\n\t\t}\r\n\t\telse if (!strncmp(file, \"PTAG\", 4))\r\n\t\t{\r\n\t\t\tsubsize = file[7] | (file[6]<<8) | (file[5]<<16) | (file[4]<<24);\r\n\t\t\t//fixme:\r\n\t\t\tfile += 8+subsize;\r\n\t\t}\r\n\t\telse if (!strncmp(file, \"VMAD\", 4))\r\n\t\t{\r\n\t\t\tsubsize = file[7] | (file[6]<<8) | (file[5]<<16) | (file[4]<<24);\r\n\t\t\t//fixme:\r\n\t\t\tfile += 8+subsize;\r\n\t\t}\r\n\t\telse if (!strncmp(file, \"SURF\", 4))\r\n\t\t{\r\n\t\t\tchar *surfend;\r\n\t\t\tchar *surfname, *sourcename;\r\n\t\t\tsubsize = file[7] | (file[6]<<8) | (file[5]<<16) | (file[4]<<24);\r\n\t\t\tfile += 8;\r\n\t\t\tsurfend = file + subsize;\r\n\t\t\tsurfname = LWO_ReadString(&file);\r\n\t\t\tsourcename = LWO_ReadString(&file);\r\n\t\t\tCon_Printf(\"surf=%s source=%s\\n\", surfname, sourcename);\r\n\t\t\twhile (file < surfend)\r\n\t\t\t{\r\n\t\t\t\tif (!strncmp(file, \"COLR\", 4))\r\n\t\t\t\t{\r\n\t\t\t\t\tsubsize = file[5] | (file[4]<<8);\r\n\t\t\t\t\t//fixme\r\n\t\t\t\t\tfile += 6+subsize;\r\n\t\t\t\t}\r\n\t\t\t\telse if (!strncmp(file, \"DIFF\", 4))\r\n\t\t\t\t{\r\n\t\t\t\t\tsubsize = file[5] | (file[4]<<8);\r\n\t\t\t\t\t//fixme\r\n\t\t\t\t\tfile += 6+subsize;\r\n\t\t\t\t}\r\n\t\t\t\telse if (!strncmp(file, \"SMAN\", 4))\r\n\t\t\t\t{\r\n\t\t\t\t\tsubsize = file[5] | (file[4]<<8);\r\n\t\t\t\t\t//fixme\r\n\t\t\t\t\tfile += 6+subsize;\r\n\t\t\t\t}\r\n\t\t\t\telse if (!strncmp(file, \"BLOK\", 4))\r\n\t\t\t\t{\r\n\t\t\t\t\tchar *blokend;\r\n\t\t\t\t\tsubsize = file[5] | (file[4]<<8);\r\n\t\t\t\t\tfile += 6;\r\n\t\t\t\t\tblokend = file + subsize;\r\n\t\t\t\t\twhile (file < blokend)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tif (!strncmp(file, \"IMAP\", 4))\r\n\t\t\t\t\t\t\t;\r\n\t\t\t\t\t\telse if (!strncmp(file, \"TMAP\", 4))\r\n\t\t\t\t\t\t\t;\r\n\t\t\t\t\t\telse if (!strncmp(file, \"PROJ\", 4))\r\n\t\t\t\t\t\t\t;\r\n\t\t\t\t\t\telse if (!strncmp(file, \"AXIS\", 4))\r\n\t\t\t\t\t\t\t;\r\n\t\t\t\t\t\telse if (!strncmp(file, \"IMAG\", 4))\r\n\t\t\t\t\t\t\t;\r\n\t\t\t\t\t\telse if (!strncmp(file, \"WRAP\", 4))\r\n\t\t\t\t\t\t\t;\r\n\t\t\t\t\t\telse if (!strncmp(file, \"WRPW\", 4))\r\n\t\t\t\t\t\t\t;\r\n\t\t\t\t\t\telse if (!strncmp(file, \"WRPH\", 4))\r\n\t\t\t\t\t\t\t;\r\n\t\t\t\t\t\telse if (!strncmp(file, \"VMAP\", 4))\r\n\t\t\t\t\t\t\t;\r\n\t\t\t\t\t\telse if (!strncmp(file, \"AAST\", 4))\r\n\t\t\t\t\t\t\t;\r\n\t\t\t\t\t\telse if (!strncmp(file, \"PIXB\", 4))\r\n\t\t\t\t\t\t\t;\r\n\t\t\t\t\t\telse\r\n\t\t\t\t\t\t\tCon_Printf(\"Unknown BLOK ID: %c%c%c%c\\n\", file[0], file[1], file[2], file[3]);\r\n\t\t\t\t\t\tsubsize = file[5] | (file[4]<<8);\r\n\t\t\t\t\t\tfile += 6+subsize;\r\n\t\t\t\t\t}\r\n\t\t\t\t\tfile = blokend;\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\tCon_Printf(\"Unknown SURF ID: %c%c%c%c\\n\", file[0], file[1], file[2], file[3]);\r\n\t\t\t\t\tsubsize = file[5] | (file[4]<<8);\r\n\t\t\t\t\tfile += 6+subsize;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\tfile = surfend;\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tCon_Printf(\"Unknown ID: %c%c%c%c\\n\", file[0], file[1], file[2], file[3]);\r\n\t\t\tsubsize = file[7] | (file[6]<<8) | (file[5]<<16) | (file[4]<<24);\r\n\t\t\tfile += 8+subsize;\r\n\t\t\tcontinue;\r\n\t\t}\r\n\t}\r\n\r\n\treturn false;\r\n}\r\n#endif\r\n\r\nextern qboolean QDECL Mod_LoadGLTFModel (struct model_s *mod, void *buffer, size_t fsize);\r\nextern qboolean QDECL Mod_LoadGLBModel (struct model_s *mod, void *buffer, size_t fsize);\r\nextern void Mod_ExportIQM(char *fname, int flags, galiasinfo_t *mesh);\r\n\r\n#if !defined(SERVERONLY) && defined(SKELETALMODELS)\r\nstatic void Mod_ExportIQM_f(void)\r\n{\r\n\tchar tok[128];\r\n\tmodel_t *mod;\r\n\tcmdfuncs->Argv(1, tok, sizeof(tok));\r\n\tmod = modfuncs->GetModel(tok, MLV_WARNSYNC);\r\n\tif (!mod || mod->type != mod_alias || !mod->meshinfo)\r\n\t\tCon_Printf(\"Couldn't load \\\"%s\\\"\\n\", tok);\r\n\telse\r\n\t{\r\n\t\tcmdfuncs->Argv(2, tok, sizeof(tok));\r\n\t\tMod_ExportIQM(tok, mod->flags, mod->meshinfo);\r\n\t}\r\n}\r\n#endif\r\n\r\nqboolean Plug_Init(void)\r\n{\r\n\tfilefuncs = plugfuncs->GetEngineInterface(plugfsfuncs_name, sizeof(*filefuncs));\r\n\tmodfuncs = plugfuncs->GetEngineInterface(plugmodfuncs_name, sizeof(*modfuncs));\r\n\tif (modfuncs && modfuncs->version != MODPLUGFUNCS_VERSION)\r\n\t\tmodfuncs = NULL;\r\n\r\n\tif (modfuncs && filefuncs)\r\n\t{\r\n#ifdef ASEMODELS\r\n\t\tmodfuncs->RegisterModelFormatText(\"ASE models (ase)\", \"*3DSMAX_ASCIIEXPORT\", Mod_LoadASEModel);\r\n#endif\r\n#ifdef LWOMODELS\r\n\t\tmodfuncs->RegisterModelFormatMagic(\"LWO models (lwo)\", (('M'<<24)+('R'<<16)+('O'<<8)+'F'), Mod_LoadLWOModel);\r\n#endif\r\n#ifdef GLTFMODELS\r\n\t\tPlug_GLTF_Init();\r\n#endif\r\n\r\n#if !defined(SERVERONLY) && defined(SKELETALMODELS)\r\n\t\tcmdfuncs->AddCommand(\"exportiqm\", Mod_ExportIQM_f, NULL);\r\n#endif\r\n\t\treturn true;\r\n\t}\r\n\treturn false;\r\n}\r\n\r\n"
  },
  {
    "path": "plugins/mpq/blast.c",
    "content": "/* blast.c\r\n * Copyright (C) 2003 Mark Adler\r\n * For conditions of distribution and use, see copyright notice in blast.h\r\n * version 1.1, 16 Feb 2003\r\n *\r\n * blast.c decompresses data compressed by the PKWare Compression Library.\r\n * This function provides functionality similar to the explode() function of\r\n * the PKWare library, hence the name \"blast\".\r\n *\r\n * This decompressor is based on the excellent format description provided by\r\n * Ben Rudiak-Gould in comp.compression on August 13, 2001.  Interestingly, the\r\n * example Ben provided in the post is incorrect.  The distance 110001 should\r\n * instead be 111000.  When corrected, the example byte stream becomes:\r\n *\r\n *    00 04 82 24 25 8f 80 7f\r\n *\r\n * which decompresses to \"AIAIAIAIAIAIA\" (without the quotes).\r\n */\r\n\r\n/*\r\n * Change history:\r\n *\r\n * 1.0  12 Feb 2003     - First version\r\n * 1.1  16 Feb 2003     - Fixed distance check for > 4 GB uncompressed data\r\n */\r\n\r\n#include <setjmp.h>             /* for setjmp(), longjmp(), and jmp_buf */\r\n#include \"blast.h\"              /* prototype for blast() */\r\n\r\n#define local static            /* for local function definitions */\r\n#define MAXBITS 13              /* maximum code length */\r\n#define MAXWIN 4096             /* maximum window size */\r\n\r\n/* input and output state */\r\nstruct state {\r\n    /* input state */\r\n    blast_in infun;             /* input function provided by user */\r\n    void *inhow;                /* opaque information passed to infun() */\r\n    unsigned char *in;          /* next input location */\r\n    unsigned left;              /* available input at in */\r\n    int bitbuf;                 /* bit buffer */\r\n    int bitcnt;                 /* number of bits in bit buffer */\r\n\r\n    /* input limit error return state for bits() and decode() */\r\n    jmp_buf env;\r\n\r\n    /* output state */\r\n    blast_out outfun;           /* output function provided by user */\r\n    void *outhow;               /* opaque information passed to outfun() */\r\n    unsigned next;              /* index of next write location in out[] */\r\n    int first;                  /* true to check distances (for first 4K) */\r\n    unsigned char out[MAXWIN];  /* output buffer and sliding window */\r\n};\r\n\r\n/*\r\n * Return need bits from the input stream.  This always leaves less than\r\n * eight bits in the buffer.  bits() works properly for need == 0.\r\n *\r\n * Format notes:\r\n *\r\n * - Bits are stored in bytes from the least significant bit to the most\r\n *   significant bit.  Therefore bits are dropped from the bottom of the bit\r\n *   buffer, using shift right, and new bytes are appended to the top of the\r\n *   bit buffer, using shift left.\r\n */\r\nlocal int bits(struct state *s, int need)\r\n{\r\n    int val;            /* bit accumulator */\r\n\r\n    /* load at least need bits into val */\r\n    val = s->bitbuf;\r\n    while (s->bitcnt < need) {\r\n        if (s->left == 0) {\r\n            s->left = s->infun(s->inhow, &(s->in));\r\n            if (s->left == 0) longjmp(s->env, 1);       /* out of input */\r\n        }\r\n        val |= (int)(*(s->in)++) << s->bitcnt;          /* load eight bits */\r\n        s->left--;\r\n        s->bitcnt += 8;\r\n    }\r\n\r\n    /* drop need bits and update buffer, always zero to seven bits left */\r\n    s->bitbuf = val >> need;\r\n    s->bitcnt -= need;\r\n\r\n    /* return need bits, zeroing the bits above that */\r\n    return val & ((1 << need) - 1);\r\n}\r\n\r\n/*\r\n * Huffman code decoding tables.  count[1..MAXBITS] is the number of symbols of\r\n * each length, which for a canonical code are stepped through in order.\r\n * symbol[] are the symbol values in canonical order, where the number of\r\n * entries is the sum of the counts in count[].  The decoding process can be\r\n * seen in the function decode() below.\r\n */\r\nstruct huffman {\r\n    short *count;       /* number of symbols of each length */\r\n    short *symbol;      /* canonically ordered symbols */\r\n};\r\n\r\n/*\r\n * Decode a code from the stream s using huffman table h.  Return the symbol or\r\n * a negative value if there is an error.  If all of the lengths are zero, i.e.\r\n * an empty code, or if the code is incomplete and an invalid code is received,\r\n * then -9 is returned after reading MAXBITS bits.\r\n *\r\n * Format notes:\r\n *\r\n * - The codes as stored in the compressed data are bit-reversed relative to\r\n *   a simple integer ordering of codes of the same lengths.  Hence below the\r\n *   bits are pulled from the compressed data one at a time and used to\r\n *   build the code value reversed from what is in the stream in order to\r\n *   permit simple integer comparisons for decoding.\r\n *\r\n * - The first code for the shortest length is all ones.  Subsequent codes of\r\n *   the same length are simply integer decrements of the previous code.  When\r\n *   moving up a length, a one bit is appended to the code.  For a complete\r\n *   code, the last code of the longest length will be all zeros.  To support\r\n *   this ordering, the bits pulled during decoding are inverted to apply the\r\n *   more \"natural\" ordering starting with all zeros and incrementing.\r\n */\r\nlocal int decode(struct state *s, struct huffman *h)\r\n{\r\n    int len;            /* current number of bits in code */\r\n    int code;           /* len bits being decoded */\r\n    int first;          /* first code of length len */\r\n    int count;          /* number of codes of length len */\r\n    int index;          /* index of first code of length len in symbol table */\r\n    int bitbuf;         /* bits from stream */\r\n    int left;           /* bits left in next or left to process */\r\n    short *next;        /* next number of codes */\r\n\r\n    bitbuf = s->bitbuf;\r\n    left = s->bitcnt;\r\n    code = first = index = 0;\r\n    len = 1;\r\n    next = h->count + 1;\r\n    while (1) {\r\n        while (left--) {\r\n            code |= (bitbuf & 1) ^ 1;   /* invert code */\r\n            bitbuf >>= 1;\r\n            count = *next++;\r\n            if (code < first + count) { /* if length len, return symbol */\r\n                s->bitbuf = bitbuf;\r\n                s->bitcnt = (s->bitcnt - len) & 7;\r\n                return h->symbol[index + (code - first)];\r\n            }\r\n            index += count;             /* else update for next length */\r\n            first += count;\r\n            first <<= 1;\r\n            code <<= 1;\r\n            len++;\r\n        }\r\n        left = (MAXBITS+1) - len;\r\n        if (left == 0) break;\r\n        if (s->left == 0) {\r\n            s->left = s->infun(s->inhow, &(s->in));\r\n            if (s->left == 0) longjmp(s->env, 1);       /* out of input */\r\n        }\r\n        bitbuf = *(s->in)++;\r\n        s->left--;\r\n        if (left > 8) left = 8;\r\n    }\r\n    return -9;                          /* ran out of codes */\r\n}\r\n\r\n/*\r\n * Given a list of repeated code lengths rep[0..n-1], where each byte is a\r\n * count (high four bits + 1) and a code length (low four bits), generate the\r\n * list of code lengths.  This compaction reduces the size of the object code.\r\n * Then given the list of code lengths length[0..n-1] representing a canonical\r\n * Huffman code for n symbols, construct the tables required to decode those\r\n * codes.  Those tables are the number of codes of each length, and the symbols\r\n * sorted by length, retaining their original order within each length.  The\r\n * return value is zero for a complete code set, negative for an over-\r\n * subscribed code set, and positive for an incomplete code set.  The tables\r\n * can be used if the return value is zero or positive, but they cannot be used\r\n * if the return value is negative.  If the return value is zero, it is not\r\n * possible for decode() using that table to return an error--any stream of\r\n * enough bits will resolve to a symbol.  If the return value is positive, then\r\n * it is possible for decode() using that table to return an error for received\r\n * codes past the end of the incomplete lengths.\r\n */\r\nlocal int construct(struct huffman *h, const unsigned char *rep, int n)\r\n{\r\n    int symbol;         /* current symbol when stepping through length[] */\r\n    int len;            /* current length when stepping through h->count[] */\r\n    int left;           /* number of possible codes left of current length */\r\n    short offs[MAXBITS+1];      /* offsets in symbol table for each length */\r\n    short length[256];  /* code lengths */\r\n\r\n    /* convert compact repeat counts into symbol bit length list */\r\n    symbol = 0;\r\n    do {\r\n        len = *rep++;\r\n        left = (len >> 4) + 1;\r\n        len &= 15;\r\n        do {\r\n            length[symbol++] = len;\r\n        } while (--left);\r\n    } while (--n);\r\n    n = symbol;\r\n\r\n    /* count number of codes of each length */\r\n    for (len = 0; len <= MAXBITS; len++)\r\n        h->count[len] = 0;\r\n    for (symbol = 0; symbol < n; symbol++)\r\n        (h->count[length[symbol]])++;   /* assumes lengths are within bounds */\r\n    if (h->count[0] == n)               /* no codes! */\r\n        return 0;                       /* complete, but decode() will fail */\r\n\r\n    /* check for an over-subscribed or incomplete set of lengths */\r\n    left = 1;                           /* one possible code of zero length */\r\n    for (len = 1; len <= MAXBITS; len++) {\r\n        left <<= 1;                     /* one more bit, double codes left */\r\n        left -= h->count[len];          /* deduct count from possible codes */\r\n        if (left < 0) return left;      /* over-subscribed--return negative */\r\n    }                                   /* left > 0 means incomplete */\r\n\r\n    /* generate offsets into symbol table for each length for sorting */\r\n    offs[1] = 0;\r\n    for (len = 1; len < MAXBITS; len++)\r\n        offs[len + 1] = offs[len] + h->count[len];\r\n\r\n    /*\r\n     * put symbols in table sorted by length, by symbol order within each\r\n     * length\r\n     */\r\n    for (symbol = 0; symbol < n; symbol++)\r\n        if (length[symbol] != 0)\r\n            h->symbol[offs[length[symbol]]++] = symbol;\r\n\r\n    /* return zero for complete set, positive for incomplete set */\r\n    return left;\r\n}\r\n\r\n/*\r\n * Decode PKWare Compression Library stream.\r\n *\r\n * Format notes:\r\n *\r\n * - First byte is 0 if literals are uncoded or 1 if they are coded.  Second\r\n *   byte is 4, 5, or 6 for the number of extra bits in the distance code.\r\n *   This is the base-2 logarithm of the dictionary size minus six.\r\n *\r\n * - Compressed data is a combination of literals and length/distance pairs\r\n *   terminated by an end code.  Literals are either Huffman coded or\r\n *   uncoded bytes.  A length/distance pair is a coded length followed by a\r\n *   coded distance to represent a string that occurs earlier in the\r\n *   uncompressed data that occurs again at the current location.\r\n *\r\n * - A bit preceding a literal or length/distance pair indicates which comes\r\n *   next, 0 for literals, 1 for length/distance.\r\n *\r\n * - If literals are uncoded, then the next eight bits are the literal, in the\r\n *   normal bit order in th stream, i.e. no bit-reversal is needed. Similarly,\r\n *   no bit reversal is needed for either the length extra bits or the distance\r\n *   extra bits.\r\n *\r\n * - Literal bytes are simply written to the output.  A length/distance pair is\r\n *   an instruction to copy previously uncompressed bytes to the output.  The\r\n *   copy is from distance bytes back in the output stream, copying for length\r\n *   bytes.\r\n *\r\n * - Distances pointing before the beginning of the output data are not\r\n *   permitted.\r\n *\r\n * - Overlapped copies, where the length is greater than the distance, are\r\n *   allowed and common.  For example, a distance of one and a length of 518\r\n *   simply copies the last byte 518 times.  A distance of four and a length of\r\n *   twelve copies the last four bytes three times.  A simple forward copy\r\n *   ignoring whether the length is greater than the distance or not implements\r\n *   this correctly.\r\n */\r\nlocal int decomp(struct state *s)\r\n{\r\n    int lit;            /* true if literals are coded */\r\n    int dict;           /* log2(dictionary size) - 6 */\r\n    int symbol;         /* decoded symbol, extra bits for distance */\r\n    int len;            /* length for copy */\r\n    int dist;           /* distance for copy */\r\n    int copy;           /* copy counter */\r\n    unsigned char *from, *to;   /* copy pointers */\r\n    static int virgin = 1;                              /* build tables once */\r\n    static short litcnt[MAXBITS+1], litsym[256];        /* litcode memory */\r\n    static short lencnt[MAXBITS+1], lensym[16];         /* lencode memory */\r\n    static short distcnt[MAXBITS+1], distsym[64];       /* distcode memory */\r\n    static struct huffman litcode = {litcnt, litsym};   /* length code */\r\n    static struct huffman lencode = {lencnt, lensym};   /* length code */\r\n    static struct huffman distcode = {distcnt, distsym};/* distance code */\r\n        /* bit lengths of literal codes */\r\n    static const unsigned char litlen[] = {\r\n        11, 124, 8, 7, 28, 7, 188, 13, 76, 4, 10, 8, 12, 10, 12, 10, 8, 23, 8,\r\n        9, 7, 6, 7, 8, 7, 6, 55, 8, 23, 24, 12, 11, 7, 9, 11, 12, 6, 7, 22, 5,\r\n        7, 24, 6, 11, 9, 6, 7, 22, 7, 11, 38, 7, 9, 8, 25, 11, 8, 11, 9, 12,\r\n        8, 12, 5, 38, 5, 38, 5, 11, 7, 5, 6, 21, 6, 10, 53, 8, 7, 24, 10, 27,\r\n        44, 253, 253, 253, 252, 252, 252, 13, 12, 45, 12, 45, 12, 61, 12, 45,\r\n        44, 173};\r\n        /* bit lengths of length codes 0..15 */\r\n    static const unsigned char lenlen[] = {2, 35, 36, 53, 38, 23};\r\n        /* bit lengths of distance codes 0..63 */\r\n    static const unsigned char distlen[] = {2, 20, 53, 230, 247, 151, 248};\r\n    static const short base[16] = {     /* base for length codes */\r\n        3, 2, 4, 5, 6, 7, 8, 9, 10, 12, 16, 24, 40, 72, 136, 264};\r\n    static const char extra[16] = {     /* extra bits for length codes */\r\n        0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8};\r\n\r\n    /* set up decoding tables (once--might not be thread-safe) */\r\n    if (virgin) {\r\n        construct(&litcode, litlen, sizeof(litlen));\r\n        construct(&lencode, lenlen, sizeof(lenlen));\r\n        construct(&distcode, distlen, sizeof(distlen));\r\n        virgin = 0;\r\n    }\r\n\r\n    /* read header */\r\n    lit = bits(s, 8);\r\n    if (lit > 1) return -1;\r\n    dict = bits(s, 8);\r\n    if (dict < 4 || dict > 6) return -2;\r\n\r\n    /* decode literals and length/distance pairs */\r\n    do {\r\n        if (bits(s, 1)) {\r\n            /* get length */\r\n            symbol = decode(s, &lencode);\r\n            len = base[symbol] + bits(s, extra[symbol]);\r\n            if (len == 519) break;              /* end code */\r\n\r\n            /* get distance */\r\n            symbol = len == 2 ? 2 : dict;\r\n            dist = decode(s, &distcode) << symbol;\r\n            dist += bits(s, symbol);\r\n            dist++;\r\n            if (s->first && dist > s->next)\r\n                return -3;              /* distance too far back */\r\n\r\n            /* copy length bytes from distance bytes back */\r\n            do {\r\n                to = s->out + s->next;\r\n                from = to - dist;\r\n                copy = MAXWIN;\r\n                if (s->next < dist) {\r\n                    from += copy;\r\n                    copy = dist;\r\n                }\r\n                copy -= s->next;\r\n                if (copy > len) copy = len;\r\n                len -= copy;\r\n                s->next += copy;\r\n                do {\r\n                    *to++ = *from++;\r\n                } while (--copy);\r\n                if (s->next == MAXWIN) {\r\n                    if (s->outfun(s->outhow, s->out, s->next)) return 1;\r\n                    s->next = 0;\r\n                    s->first = 0;\r\n                }\r\n            } while (len != 0);\r\n        }\r\n        else {\r\n            /* get literal and write it */\r\n            symbol = lit ? decode(s, &litcode) : bits(s, 8);\r\n            s->out[s->next++] = symbol;\r\n            if (s->next == MAXWIN) {\r\n                if (s->outfun(s->outhow, s->out, s->next)) return 1;\r\n                s->next = 0;\r\n                s->first = 0;\r\n            }\r\n        }\r\n    } while (1);\r\n    return 0;\r\n}\r\n\r\n/* See comments in blast.h */\r\nint blast(blast_in infun, void *inhow, blast_out outfun, void *outhow)\r\n{\r\n    struct state s;             /* input/output state */\r\n    int err;                    /* return value */\r\n\r\n    /* initialize input state */\r\n    s.infun = infun;\r\n    s.inhow = inhow;\r\n    s.left = 0;\r\n    s.bitbuf = 0;\r\n    s.bitcnt = 0;\r\n\r\n    /* initialize output state */\r\n    s.outfun = outfun;\r\n    s.outhow = outhow;\r\n    s.next = 0;\r\n    s.first = 1;\r\n\r\n    /* return if bits() or decode() tries to read past available input */\r\n    if (setjmp(s.env) != 0)             /* if came back here via longjmp(), */\r\n        err = 2;                        /*  then skip decomp(), return error */\r\n    else\r\n        err = decomp(&s);               /* decompress */\r\n\r\n    /* write any leftover output and update the error code if needed */\r\n    if (err != 1 && s.next && s.outfun(s.outhow, s.out, s.next) && err == 0)\r\n        err = 1;\r\n    return err;\r\n}\r\n\r\n#ifdef TEST\r\n/* Example of how to use blast() */\r\n#include <stdio.h>\r\n#include <stdlib.h>\r\n\r\n#define CHUNK 16384\r\n\r\nlocal unsigned inf(void *how, unsigned char **buf)\r\n{\r\n    static unsigned char hold[CHUNK];\r\n\r\n    *buf = hold;\r\n    return fread(hold, 1, CHUNK, (FILE *)how);\r\n}\r\n\r\nlocal int outf(void *how, unsigned char *buf, unsigned len)\r\n{\r\n    return fwrite(buf, 1, len, (FILE *)how) != len;\r\n}\r\n\r\n/* Decompress a PKWare Compression Library stream from stdin to stdout */\r\nint main(void)\r\n{\r\n    int ret, n;\r\n\r\n    /* decompress to stdout */\r\n    ret = blast(inf, stdin, outf, stdout);\r\n    if (ret != 0) fprintf(stderr, \"blast error: %d\\n\", ret);\r\n\r\n    /* see if there are any leftover bytes */\r\n    n = 0;\r\n    while (getchar() != EOF) n++;\r\n    if (n) fprintf(stderr, \"blast warning: %d unused bytes of input\\n\", n);\r\n\r\n    /* return blast() error code */\r\n    return ret;\r\n}\r\n#endif\r\n"
  },
  {
    "path": "plugins/mpq/blast.h",
    "content": "/* blast.h -- interface for blast.c\r\n  Copyright (C) 2003 Mark Adler\r\n  version 1.1, 16 Feb 2003\r\n\r\n  This software is provided 'as-is', without any express or implied\r\n  warranty.  In no event will the author be held liable for any damages\r\n  arising from the use of this software.\r\n\r\n  Permission is granted to anyone to use this software for any purpose,\r\n  including commercial applications, and to alter it and redistribute it\r\n  freely, subject to the following restrictions:\r\n\r\n  1. The origin of this software must not be misrepresented; you must not\r\n     claim that you wrote the original software. If you use this software\r\n     in a product, an acknowledgment in the product documentation would be\r\n     appreciated but is not required.\r\n  2. Altered source versions must be plainly marked as such, and must not be\r\n     misrepresented as being the original software.\r\n  3. This notice may not be removed or altered from any source distribution.\r\n\r\n  Mark Adler    madler@alumni.caltech.edu\r\n */\r\n\r\n\r\n/*\r\n * blast() decompresses the PKWare Data Compression Library (DCL) compressed\r\n * format.  It provides the same functionality as the explode() function in\r\n * that library.  (Note: PKWare overused the \"implode\" verb, and the format\r\n * used by their library implode() function is completely different and\r\n * incompatible with the implode compression method supported by PKZIP.)\r\n */\r\n\r\n\r\ntypedef unsigned (*blast_in)(void *how, unsigned char **buf);\r\ntypedef int (*blast_out)(void *how, unsigned char *buf, unsigned len);\r\n/* Definitions for input/output functions passed to blast().  See below for\r\n * what the provided functions need to do.\r\n */\r\n\r\n\r\nint blast(blast_in infun, void *inhow, blast_out outfun, void *outhow);\r\n/* Decompress input to output using the provided infun() and outfun() calls.\r\n * On success, the return value of blast() is zero.  If there is an error in\r\n * the source data, i.e. it is not in the proper format, then a negative value\r\n * is returned.  If there is not enough input available or there is not enough\r\n * output space, then a positive error is returned.\r\n *\r\n * The input function is invoked: len = infun(how, &buf), where buf is set by\r\n * infun() to point to the input buffer, and infun() returns the number of\r\n * available bytes there.  If infun() returns zero, then blast() returns with\r\n * an input error.  (blast() only asks for input if it needs it.)  inhow is for\r\n * use by the application to pass an input descriptor to infun(), if desired.\r\n *\r\n * The output function is invoked: err = outfun(how, buf, len), where the bytes\r\n * to be written are buf[0..len-1].  If err is not zero, then blast() returns\r\n * with an output error.  outfun() is always called with len <= 4096.  outhow\r\n * is for use by the application to pass an output descriptor to outfun(), if\r\n * desired.\r\n *\r\n * The return codes are:\r\n *\r\n *   2:  ran out of input before completing decompression\r\n *   1:  output error before completing decompression\r\n *   0:  successful decompression\r\n *  -1:  literal flag not zero or one\r\n *  -2:  dictionary size not in 4..6\r\n *  -3:  distance is too far back\r\n *\r\n * At the bottom of blast.c is an example program that uses blast() that can be\r\n * compiled to produce a command-line decompression filter by defining TEST.\r\n */\r\n"
  },
  {
    "path": "plugins/mpq/fs_mpq.c",
    "content": "#include \"quakedef.h\"\n#include \"../plugin.h\"\n//#include \"../engine.h\"\n#include \"fs.h\"\n#include <assert.h>\n\n#ifdef AVAIL_ZLIB\n\t#include <zlib.h>\n#endif\n#include \"blast.h\"\n\n//http://bazaar.launchpad.net/~jeanfrancois.roy/mpqkit/trunk/files\n//http://www.zezula.net/en/mpq/main.html\n//http://www.wc3c.net/tools/specs/QuantamMPQFormat.txt\n\nstatic size_t activempqcount; //number of active archives. we can't unload the dll while we still have files open.\n#ifdef MULTITHREAD\nstatic plugthreadfuncs_t *threading;\n#define Sys_CreateMutex() (threading?threading->CreateMutex():NULL)\n#define Sys_LockMutex if(threading)threading->LockMutex\n#define Sys_UnlockMutex if(threading)threading->UnlockMutex\n#define Sys_DestroyMutex if(threading)threading->DestroyMutex\n#endif\n\ntypedef unsigned long long ofs_t;\n\ntypedef struct\n{\n\tchar mpq_magic[4];\n\tunsigned int header_size;\n\tunsigned int archive_size;\n\tunsigned short version;\n\tunsigned short sector_size_shift;\n\tunsigned int hash_table_offset;\n\tunsigned int block_table_offset;\n\tunsigned int hash_table_length;\n\tunsigned int block_table_length;\n} mpqheader_t;\n\nenum\n{\n\tMPQFileValid\t\t\t\t= 0x80000000,\n\tMPQFileHasSectorAdlers\t\t= 0x04000000,\n\tMPQFileStopSearchMarker\t\t= 0x02000000,\n\tMPQFileOneSector\t\t\t= 0x01000000,\n\tMPQFilePatch\t\t\t\t= 0x00100000,\n\tMPQFileOffsetAdjustedKey\t= 0x00020000,\n\tMPQFileEncrypted\t\t\t= 0x00010000,\n\tMPQFileCompressed\t\t\t= 0x00000200,\n\tMPQFileDiabloCompressed\t\t= 0x00000100,\n\tMPQFileFlagsMask\t\t\t= 0x87030300\n};\ntypedef struct\n{\n\tunsigned int offset;\n\tunsigned int archived_size;\n\tunsigned int size;\n\tunsigned int flags;\n} mpqblock_t;\n\ntypedef struct\n{\n\tunsigned int hash_a;\n\tunsigned int hash_b;\n\tunsigned short locale;\n\tunsigned short platform;\n\tunsigned int block_table_index;\n} mpqhash_t;\n\ntypedef struct \n{\n\tsearchpathfuncs_t pub;\n\n\tchar desc[MAX_OSPATH];\n\tvoid *mutex;\n\tvfsfile_t *file;\n\tofs_t filestart;\n\tofs_t fileend;\n\n\tunsigned int references;\n\n\tunsigned int sectorsize;\n\n\tmpqhash_t *hashdata;\n\tunsigned int hashentries;\n\n\tmpqblock_t *blockdata;\n\tunsigned int blockentries;\n\n\tchar *listfile;\n\n\tmpqheader_t header_0;\n\tstruct\n\t{\n\t\tunsigned long long extended_block_offset_table_offset;\n\t\tunsigned short hash_table_offset_high;\n\t\tunsigned short block_table_offset_high;\n\t} header_1;\n} mpqarchive_t;\n\ntypedef struct\n{\n\tvfsfile_t funcs;\n\tunsigned int flags;\n\tunsigned int encryptkey;\n\tmpqarchive_t *archive;\n\tqofs_t foffset;\n\tqofs_t flength;\t//decompressed size\n\tqofs_t alength;\t//size on disk\n\n\tofs_t archiveoffset;\n\n\tunsigned int buffersect;\n\tunsigned int bufferlength;\n\tchar *buffer;\n\tunsigned int *sectortab;\n} mpqfile_t;\n\n\nstatic qboolean crypt_table_initialized = false;\nstatic unsigned int crypt_table[0x500];\n\nstatic void mpq_init_cryptography(void)\n{\n\t// prepare crypt_table\n\tunsigned int seed   = 0x00100001;\n\tunsigned int index1 = 0;\n\tunsigned int index2 = 0;\n\tunsigned int i;\n\n\tif (!crypt_table_initialized)\n\t{\n\t\tcrypt_table_initialized = true;\n\n\t\tfor (index1 = 0; index1 < 0x100; index1++)\n\t\t{\n\t\t\tfor (index2 = index1, i = 0; i < 5; i++, index2 += 0x100)\n\t\t\t{\n\t\t\t\tunsigned int temp1, temp2;\n\n\t\t\t\tseed  = (seed * 125 + 3) % 0x2AAAAB;\n\t\t\t\ttemp1 = (seed & 0xFFFF) << 0x10;\n\n\t\t\t\tseed  = (seed * 125 + 3) % 0x2AAAAB;\n\t\t\t\ttemp2 = (seed & 0xFFFF);\n\n\t\t\t\tcrypt_table[index2] = (temp1 | temp2);\n\t\t\t}\n\t\t}\n\t}\n}\n\n#define HASH_POSITION 0\n#define HASH_NAME_A 1\n#define HASH_NAME_B 2\n#define HASH_KEY 3\nstatic unsigned int mpq_hash_cstring(const char *string, unsigned int type)\n{\n\tunsigned int seed1 = 0x7FED7FED;\n\tunsigned int seed2 = 0xEEEEEEEE;\n\tunsigned int shifted_type = (type << 8);\n\tunsigned int ch;\n\n\tassert(crypt_table_initialized);\n\tassert(string);\n\n\twhile (*string != 0)\n\t{\n\t\tch = *string++;\n\t\tif (ch == '/')\n\t\t\tch = '\\\\';\n\t\tif (ch > 0x60 && ch < 0x7b) ch -= 0x20;\n\n\t\tseed1 = crypt_table[shifted_type + ch] ^ (seed1 + seed2);\n\t\tseed2 = ch + seed1 + seed2 + (seed2 << 5) + 3;\n\t}\n\n\treturn seed1;\n}\n\n#define MPQSwapInt32LittleToHost(a) (a)\n#define MPQSwapInt32HostToLittle(a) (a)\n\nstatic void mpq_decrypt(void* data, size_t length, unsigned int key, qboolean disable_output_swapping)\n{\n\tunsigned int* buffer32 = (unsigned int*)data;\n\tunsigned int seed = 0xEEEEEEEE;\n\tunsigned int ch;\n\n\tassert(crypt_table_initialized);\n\tassert(data);\n\n\t// round to 4 bytes\n\tlength = length / 4;\n\n\tif (disable_output_swapping)\n\t{\n\t\twhile (length-- > 0)\n\t\t{\n\t\t\tch = MPQSwapInt32LittleToHost(*buffer32);\n\n\t\t\tseed += crypt_table[0x400 + (key & 0xFF)];\n\t\t\tch = ch ^ (key + seed);\n\n\t\t\tkey = ((~key << 0x15) + 0x11111111) | (key >> 0x0B);\n\t\t\tseed = ch + seed + (seed << 5) + 3;\n\n\t\t\t*buffer32++ = ch;\n\t\t}\n\t}\n\telse\n\t{\n\t\twhile (length-- > 0)\n\t\t{\n\t\t\tch = MPQSwapInt32LittleToHost(*buffer32);\n\n\t\t\tseed += crypt_table[0x400 + (key & 0xFF)];\n\t\t\tch = ch ^ (key + seed);\n\n\t\t\tkey = ((~key << 0x15) + 0x11111111) | (key >> 0x0B);\n\t\t\tseed = ch + seed + (seed << 5) + 3;\n\n\t\t\t*buffer32++ = MPQSwapInt32HostToLittle(ch);\n\t\t}\n\t}\n}\n\n#define HASH_TABLE_EMPTY 0xffffffff\n#define HASH_TABLE_DELETED 0xfffffffe\nstatic unsigned int mpq_lookuphash(mpqarchive_t *mpq, const char *filename, int locale)\n{\n\tunsigned int initial_position = mpq_hash_cstring(filename, HASH_POSITION) % mpq->hashentries;\n\tunsigned int current_position = initial_position;\n\tunsigned int hash_a = mpq_hash_cstring(filename, HASH_NAME_A);\n\tunsigned int hash_b = mpq_hash_cstring(filename, HASH_NAME_B);\n\n\t// Search through the hash table until we either find the file we're looking for, or we find an unused hash table entry, \n\t// indicating the end of the cluster of used hash table entries\n\twhile (mpq->hashdata[current_position].block_table_index != HASH_TABLE_EMPTY)\n\t{\n\t\tif (mpq->hashdata[current_position].block_table_index != HASH_TABLE_DELETED)\n\t\t{\n\t\t\tif (mpq->hashdata[current_position].hash_a == hash_a && \n\t\t\t\tmpq->hashdata[current_position].hash_b == hash_b && \n\t\t\t\tmpq->hashdata[current_position].locale == locale)\n\t\t\t{\n\t\t\t\treturn current_position;\n\t\t\t}\n\t\t}\n\n\t\tcurrent_position++;\n\t\tcurrent_position %= mpq->hashentries;\n\n\t\t//avoid infinity\n\t\tif (current_position == initial_position)\n\t\t\tbreak;\n\t}\n\n\treturn HASH_TABLE_EMPTY;\n}\n\nstatic vfsfile_t *MPQ_OpenVFS(searchpathfuncs_t *handle, flocation_t *loc, const char *mode);\nstatic void\tMPQ_ClosePath(searchpathfuncs_t *handle)\n{\n\tmpqarchive_t *mpq = (void*)handle;\n\tSys_LockMutex(mpq->mutex);\n\tif (--mpq->references)\n\t{\n\t\tSys_UnlockMutex(mpq->mutex);\n\t\treturn;\n\t}\n\tSys_UnlockMutex(mpq->mutex);\n\tSys_DestroyMutex(mpq->mutex);\n\tVFS_CLOSE(mpq->file);\n\tfree(mpq->blockdata);\n\tfree(mpq->hashdata);\n\tfree(mpq->listfile);\n\tfree(mpq);\n\tactivempqcount--;\n}\nstatic unsigned int MPQ_FindFile(searchpathfuncs_t *handle, flocation_t *loc, const char *name, void *hashedresult)\n{\n\tmpqarchive_t *mpq = (void*)handle;\n\tunsigned int blockentry;\n\n\tif (hashedresult)\n\t{\n\t\tmpqblock_t *block = hashedresult;\n\t\tif (block >= mpq->blockdata && block <= mpq->blockdata + mpq->blockentries)\n\t\t\tblockentry = (mpqblock_t*)block - mpq->blockdata;\n\t\telse\n\t\t\treturn FF_NOTFOUND;\n\t}\n\telse\n\t{\n\t\tunsigned int hashentry = mpq_lookuphash(mpq, name, 0);\n\t\tif (hashentry == HASH_TABLE_EMPTY)\n\t\t\treturn FF_NOTFOUND;\n\t\tblockentry = mpq->hashdata[hashentry].block_table_index;\n\t\tif (blockentry > mpq->blockentries)\n\t\t\treturn FF_NOTFOUND;\n\t}\n\tif (loc)\n\t{\n\t\tloc->fhandle = &mpq->blockdata[blockentry];\n\t\tloc->offset = 0;\n\t\t*loc->rawname = 0;\n\t\tloc->len = mpq->blockdata[blockentry].size;\n//\t\tloc->foo = foo;\n\t}\n\n\tif (mpq->blockdata[blockentry].flags & MPQFilePatch)\n\t{\n\t\tCon_DPrintf(\"Cannot cope with patch files\\n\");\n\t\treturn FF_NOTFOUND;\n\t}\n\treturn FF_FOUND;\n}\nstatic void\tMPQ_ReadFile(searchpathfuncs_t *handle, flocation_t *loc, char *buffer)\n{\n\tvfsfile_t *f;\n\tf = MPQ_OpenVFS(handle, loc, \"rb\");\n\tif (!f)\t//err...\n\t\treturn;\n\tVFS_READ(f, buffer, loc->len);\n\tVFS_CLOSE(f);\n}\n\nstatic int mpqwildcmp(const char *wild, const char *string, char **end)\n{\n\tint s, w;\n\twhile (*string)\n\t{\n\t\ts = *string;\n\t\tif (s == '\\r' || s == '\\n' || s == ';')\n\t\t\tbreak;\n\t\tw = *wild;\n\n\t\tif (s >= 'A' && s <= 'Z')\n\t\t\ts = s-'A'+'a';\n\t\tif (w >= 'A' && w <= 'Z')\n\t\t\tw = w-'A'+'a';\n\n\t\tif (w == '*')\n\t\t{\n\t\t\tw = wild[1];\n\t\t\tif (w >= 'A' && w <= 'Z')\n\t\t\t\tw = w-'A'+'a';\n\t\t\tif (w == s || s == '/' || s == '\\\\')\n\t\t\t{\n\t\t\t\t//* terminates if we get a match on the char following it, or if its a \\ or / char\n\t\t\t\twild++;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tstring++;\n\t\t}\n\t\telse if ((w == s) || (w == '?'))\n\t\t{\n\t\t\t//this char matches\n\t\t\twild++;\n\t\t\tstring++;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t//failure\n\t\t\twhile (*string && *string != '\\r' && *string != '\\n' && *string != ';')\n\t\t\t\tstring++;\n\t\t\t*end = (char*)string;\n\t\t\treturn false;\n\t\t}\n\t}\n\t*end = (char*)string;\n\n\twhile (*wild == '*')\n\t{\n\t\twild++;\n\t}\n\treturn !*wild;\n}\nstatic int MPQ_EnumerateFiles(searchpathfuncs_t *handle, const char *match, int (QDECL *func)(const char *fname, qofs_t fsize, time_t mtime, void *parm, searchpathfuncs_t *spath), void *parm)\n{\n\tint ok = 1;\n\tchar *s, *n;\n\tchar name[MAX_QPATH];\n\tflocation_t loc;\n\tmpqarchive_t *mpq = (mpqarchive_t*)handle;\n\tif (mpq->listfile)\n\t{\n\t\ts = mpq->listfile;\n\t\tfor (s = mpq->listfile; *s && ok; s = n)\n\t\t{\n\t\t\tif (mpqwildcmp(match, s, &n) && n - s < MAX_QPATH-1)\n\t\t\t{\n\t\t\t\tmemcpy(name, s, n - s);\n\t\t\t\tname[n-s] = 0;\n\n\t\t\t\tif (!MPQ_FindFile(handle, &loc, name, NULL))\n\t\t\t\t\tloc.len = 0;\n\t\t\t\tok = func(name, loc.len, 0, parm, handle);\n\t\t\t}\n\n\t\t\twhile (*n == '\\n' || *n == '\\r' || *n == ';')\n\t\t\t\tn++;\n\t\t}\n\t}\n\treturn ok;\n}\nstatic void\tMPQ_BuildHash(searchpathfuncs_t *handle, int depth, void (QDECL *AddFileHash)(int depth, const char *fname, fsbucket_t *filehandle, void *pathhandle))\n{\n\tchar *s, *n;\n\tchar name[MAX_QPATH];\n\tmpqarchive_t *mpq = (void*)handle;\n\tflocation_t loc;\n\tif (mpq->listfile)\n\t{\n\t\ts = mpq->listfile;\n\t\tfor (s = mpq->listfile; ; s = n)\n\t\t{\n\t\t\twhile (*s == '\\n' || *s == '\\r' || *s == ';')\n\t\t\t\ts++;\n\t\t\tif (!*s)\n\t\t\t\tbreak;\n\t\t\tn = s;\n\t\t\twhile (*n && *n != '\\r' && *n != '\\n' && *n != ';')\n\t\t\t\tn++;\n\n\t\t\tif (n-s >= sizeof(name))\n\t\t\t\tcontinue;\n\n\t\t\tmemcpy(name, s, n - s);\n\t\t\tname[n-s] = 0;\n\t\t\t//precompute the name->block lookup. fte normally does the hashing outside the archive code.\n\t\t\t//however, its possible multiple hash tables point to a single block, so we need to pass null for the third arg (or allocate fsbucket_ts one per hash instead of buckets).\n\t\t\tif (MPQ_FindFile(&mpq->pub, &loc, name, NULL))\n\t\t\t\tAddFileHash(depth, name, NULL, loc.fhandle);\n\t\t}\n\t}\n}\n\nstatic int\t\tMPQ_GeneratePureCRC (searchpathfuncs_t *handle, const int *seed)\n{\n\treturn 0;\n}\n\nstatic qboolean\tMPQ_PollChanges(searchpathfuncs_t *handle)\n{\n\treturn false;\n}\n\nstatic searchpathfuncs_t\t*MPQ_OpenArchive(vfsfile_t *file, const char *desc, const char *prefix)\n{\n\tflocation_t lloc;\n\tmpqarchive_t *mpq;\n\tmpqheader_t header;\n\tofs_t block_ofs;\n\tofs_t hash_ofs;\n\n\tVFS_SEEK(file, 0);\n\n\tif (prefix && *prefix)\n\t\treturn NULL;\t//not supported at this time\n\n\tif (VFS_READ(file, &header, sizeof(header)) != sizeof(header))\n\t\treturn NULL;\n\n\tif (memcmp(header.mpq_magic, \"MPQ\\x1a\", 4))\n\t\treturn NULL;\n\n\tmpq = malloc(sizeof(*mpq));\n\tmemset(mpq, 0, sizeof(*mpq));\n\tQ_strlcpy(mpq->desc, desc, sizeof(mpq->desc));\n\tmpq->header_0 = header;\n\tmpq->file = file;\n\tmpq->filestart = 0;\n\tmpq->sectorsize = 512 << mpq->header_0.sector_size_shift;\n\n\tblock_ofs = header.block_table_offset;\n\thash_ofs = header.hash_table_offset;\n\tif (header.version >= 1)\n\t{\n\t\tVFS_READ(file, &mpq->header_1, sizeof(mpq->header_1));\n\t}\n\tblock_ofs |= ((ofs_t)mpq->header_1.block_table_offset_high)<<32u;\n\thash_ofs |= ((ofs_t)mpq->header_1.hash_table_offset_high)<<32u;\n\n\tmpq->fileend = VFS_GETLEN(file);\n\n\tif (block_ofs + sizeof(*mpq->blockdata) * mpq->blockentries > mpq->fileend ||\n\t\thash_ofs + sizeof(*mpq->hashdata) * mpq->hashentries > mpq->fileend)\n\t{\n\t\tCon_Printf(\"\\\"%s\\\" appears truncated\\n\", desc);\n\t\tfree(mpq);\n\t\treturn NULL;\n\t}\n\n\n\tmpq->hashentries = mpq->header_0.hash_table_length;\n\tmpq->hashdata = malloc(sizeof(*mpq->hashdata) * mpq->hashentries);\n\tVFS_SEEK(file, hash_ofs);\n\tVFS_READ(file, mpq->hashdata, sizeof(*mpq->hashdata) * mpq->hashentries);\n\tmpq_decrypt(mpq->hashdata, sizeof(*mpq->hashdata) * mpq->hashentries, mpq_hash_cstring(\"(hash table)\", HASH_KEY), false);\n\n\tmpq->blockentries = mpq->header_0.block_table_length;\n\tmpq->blockdata = malloc(sizeof(*mpq->blockdata) * mpq->blockentries);\n\tVFS_SEEK(file, block_ofs);\n\tVFS_READ(file, mpq->blockdata, sizeof(*mpq->blockdata) * mpq->blockentries);\n\tmpq_decrypt(mpq->blockdata, sizeof(*mpq->blockdata) * mpq->blockentries, mpq_hash_cstring(\"(block table)\", HASH_KEY), true);\n\n\t/*for (i = 0; i < mpq->header_0.block_table_length; i++)\n\t{\n\t\tCon_Printf(\"offset = %08x, csize = %i, usize=%i, flags=%s%s%s%s%s%s%s%s%s\\n\", mpq->blockdata[i].offset, mpq->blockdata[i].archived_size, mpq->blockdata[i].size, \n\t\t\t\t(mpq->blockdata[i].flags & MPQFileValid)?\"valid \":\"\",\n\t\t\t\t(mpq->blockdata[i].flags & MPQFileHasSectorAdlers)?\"sectoradlers \":\"\",\n\t\t\t\t(mpq->blockdata[i].flags & MPQFileStopSearchMarker)?\"stopsearch \":\"\",\n\t\t\t\t(mpq->blockdata[i].flags & MPQFileOneSector)?\"singlesector \":\"\",\n\t\t\t\t(mpq->blockdata[i].flags & MPQFileOffsetAdjustedKey)?\"offsetadjust \":\"\",\n\t\t\t\t(mpq->blockdata[i].flags & MPQFileEncrypted)?\"encrypted \":\"\",\n\t\t\t\t(mpq->blockdata[i].flags & MPQFileCompressed)?\"compressed \":\"\",\n\t\t\t\t(mpq->blockdata[i].flags & MPQFileDiabloCompressed)?\"dcompressed \":\"\",\n\t\t\t\t(mpq->blockdata[i].flags & ~MPQFileFlagsMask)?\"OTHERS \":\"\"\n\t\t\t);\n\t}*/\n\n\tactivempqcount++;\n\tmpq->references = 1;\n\tmpq->mutex = Sys_CreateMutex();\n\n\tif (MPQ_FindFile(&mpq->pub, &lloc, \"(listfile)\", NULL))\n\t{\n\t\tchar *bs;\n\t\tmpq->listfile = malloc(lloc.len+2);\n\t\tmpq->listfile[0] = 0;\n\t\tmpq->listfile[lloc.len] = 0;\n\t\tmpq->listfile[lloc.len+1] = 0;\n\t\tMPQ_ReadFile(&mpq->pub, &lloc, mpq->listfile);\n\t\tbs = mpq->listfile;\n\t\twhile(1)\n\t\t{\n\t\t\tbs = strchr(bs, '\\\\');\n\t\t\tif (bs)\n\t\t\t\t*bs++ = '/';\n\t\t\telse\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\tmpq->pub.fsver\t\t\t\t= FSVER;\n\tmpq->pub.ClosePath\t\t\t= MPQ_ClosePath;\n\tmpq->pub.BuildHash\t\t\t= MPQ_BuildHash;\n\tmpq->pub.FindFile\t\t\t= MPQ_FindFile;\n\tmpq->pub.ReadFile\t\t\t= MPQ_ReadFile;\n\tmpq->pub.EnumerateFiles\t\t= MPQ_EnumerateFiles;\n\tmpq->pub.GeneratePureCRC\t= MPQ_GeneratePureCRC;\n\tmpq->pub.OpenVFS\t\t\t= MPQ_OpenVFS;\n\tmpq->pub.PollChanges\t\t= MPQ_PollChanges;\n\n\treturn &mpq->pub;\n}\n\n\nstruct blastdata_s\n{\n\tvoid *outdata;\n\tunsigned int outlen;\n\tvoid *indata;\n\tunsigned int inlen;\n};\nstatic unsigned mpqf_blastin(void *how, unsigned char **buf)\n{\n\tstruct blastdata_s *args = how;\n\t*buf = args->indata;\n\treturn args->inlen;\n}\nstatic int mpqf_blastout(void *how, unsigned char *buf, unsigned len)\n{\n\tstruct blastdata_s *args = how;\n\tif (len > args->outlen)\n\t\treturn 1;\n\tmemcpy(args->outdata, buf, len);\n\targs->outdata = (char*)args->outdata + len;\n\targs->outlen -= len;\n\treturn 0;\n}\nstatic void MPQF_decompress(qboolean legacymethod, void *outdata, unsigned int outlen, void *indata, unsigned int inlen)\n{\n#ifdef AVAIL_ZLIB\n\tint ret;\n#endif\n\n\tint methods;\n\tif (legacymethod)\n\t\tmethods = 8;\n\telse\n\t{\n\t\tmethods = *(unsigned char*)indata;\n\t\tindata = (char*)indata + 1;\n\t\tinlen--;\n\t}\n\n\tif (methods == 8)\n\t{\n\t\tstruct blastdata_s args = {outdata, outlen, indata, inlen};\n\t\tblast(mpqf_blastin, &args, mpqf_blastout, &args);\n\t}\n#ifdef AVAIL_ZLIB\n\telse if (methods == 2)\n\t{\n\t\tz_stream strm =\n\t\t{\n\t\t\tindata,\n\t\t\tinlen,\n\t\t\t0,\n\n\t\t\toutdata,\n\t\t\toutlen,\n\t\t\t0,\n\n\t\t\tNULL,\n\t\t\tNULL,\n\n\t\t\tNULL,\n\t\t\tNULL,\n\t\t\tNULL,\n\n\t\t\tZ_UNKNOWN,\n\t\t\t0,\n\t\t\t0\n\t\t};\n\n\t\tinflateInit2(&strm, MAX_WBITS);\n\n\t\twhile ((ret=inflate(&strm, Z_SYNC_FLUSH)) != Z_STREAM_END)\n\t\t{\n\t\t\tif (strm.avail_in == 0 || strm.avail_out == 0)\n\t\t\t{\n\t\t\t\tif (strm.avail_in == 0)\n\t\t\t\t{\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tif (strm.avail_out == 0)\n\t\t\t\t{\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t//doh, it terminated for no reason\n\t\t\tif (ret != Z_STREAM_END)\n\t\t\t{\n\t\t\t\tinflateEnd(&strm);\n\t\t\t\tCon_Printf(\"Couldn't decompress gz file\\n\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\tinflateEnd(&strm);\n\t}\n#endif\n\telse\n\t{\n\t\tCon_Printf(\"mpq: unsupported decompression method - %x\\n\", methods);\n\t\tmemset(outdata, 0, outlen);\n\t}\n}\n\nstatic int MPQF_readbytes (struct vfsfile_s *file, void *buffer, int bytestoread)\n{\n\tint bytesread = 0;\n\tmpqfile_t *f = (mpqfile_t *)file;\n\n\tif (bytestoread + f->foffset > f->flength)\n\t\tbytestoread = f->flength - f->foffset;\n\tif (bytestoread < 0)\n\t\treturn 0;\n\n\tif (!(f->flags & (MPQFileCompressed|MPQFileDiabloCompressed)))\n\t{\n\t\t//no compression, just a raw file.\n\t\tSys_LockMutex(f->archive->mutex);\n\t\tVFS_SEEK(f->archive->file, f->archiveoffset + f->foffset);\n\t\tbytesread = VFS_READ(f->archive->file, buffer, bytestoread);\n\t\tSys_UnlockMutex(f->archive->mutex);\n\t\tf->foffset += bytesread;\n\t}\n\telse if (f->flags & MPQFileOneSector)\n\t{\n\t\t//fairly simple packed data, no sector nonsense. decode in one go\n\t\tif (!f->buffer)\n\t\t{\n\t\t\tchar *cdata = malloc(f->alength);\n\t\t\tf->buffer = malloc(f->flength);\n\t\t\tSys_LockMutex(f->archive->mutex);\n\t\t\tVFS_SEEK(f->archive->file, f->archiveoffset);\n\t\t\tVFS_READ(f->archive->file, cdata, f->alength);\n\t\t\tSys_UnlockMutex(f->archive->mutex);\n\n\t\t\tif (f->flags & MPQFileEncrypted)\n\t\t\t{\n\t\t\t\tmpq_decrypt(cdata, f->alength, f->encryptkey, false);\n\t\t\t}\n\t\t\tif (f->flags & (MPQFileCompressed|MPQFileDiabloCompressed))\n\t\t\t{\n\t\t\t\t//decompress\n\t\t\t\tMPQF_decompress(!!(f->flags&MPQFileDiabloCompressed), f->buffer, f->flength, cdata, f->alength);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t//lazy...\n\t\t\t\tmemcpy(f->buffer, cdata, f->flength);\n\t\t\t}\n\t\t\tfree(cdata);\n\t\t}\n\t\tmemcpy((char*)buffer+bytesread, f->buffer + f->foffset, bytestoread);\n\t\tf->foffset += bytestoread;\n\t\tbytesread += bytestoread;\n\t}\n\telse\n\t{\n\t\t//sectors are weird.\n\t\t//sectors are allocated for decompressed size, not compressed. I have no idea how this works.\n\t\t//time to find out.\n\t\tfor (;;)\n\t\t{\n\t\t\tint numsects = (f->flength + (f->archive->sectorsize) - 1) / f->archive->sectorsize;\n\t\t\tint sectidx = f->foffset / f->archive->sectorsize;\n\t\t\tqboolean lastsect = false;\n\t\t\tint chunkofs, chunklen;\n\t\t\tif (sectidx >= numsects-1)\n\t\t\t{\n\t\t\t\tlastsect = true;\n\t\t\t\tsectidx = numsects-1;\n\t\t\t}\n\n\t\t\tif (sectidx != f->buffersect || !f->buffer)\n\t\t\t{\n\t\t\t\tint rawsize;\n\t\t\t\tchar *cdata;\n\t\t\t\tf->buffersect = sectidx;\n\t\t\t\tif (!f->sectortab)\n\t\t\t\t{\n\t\t\t\t\tf->sectortab = malloc((numsects+1) * sizeof(*f->sectortab));\n\t\t\t\t\tif (!f->sectortab)\n\t\t\t\t\t\tplugfuncs->Error(\"out of memory\");\n\t\t\t\t\tSys_LockMutex(f->archive->mutex);\n\t\t\t\t\tVFS_SEEK(f->archive->file, f->archiveoffset);\n\t\t\t\t\tVFS_READ(f->archive->file, f->sectortab, (numsects+1) * sizeof(*f->sectortab));\n\t\t\t\t\tSys_UnlockMutex(f->archive->mutex);\n\n\t\t\t\t\tif (f->flags & MPQFileEncrypted)\n\t\t\t\t\t\tmpq_decrypt(f->sectortab, (numsects+1) * sizeof(*f->sectortab), f->encryptkey-1, true);\n\t\t\t\t}\n\t\t\t\t//data is packed, sector table gives offsets. there's an extra index on the end which is the size of the last sector.\n\t\t\t\trawsize = f->sectortab[sectidx+1]-f->sectortab[sectidx];\n\n\t\t\t\tcdata = malloc(rawsize);\n\t\t\t\tif (!cdata)\n\t\t\t\t\tplugfuncs->Error(\"out of memory\");\n\t\t\t\tif (!f->buffer)\n\t\t\t\t\tf->buffer = malloc(f->archive->sectorsize);\n\t\t\t\tif (!f->buffer)\n\t\t\t\t\tplugfuncs->Error(\"out of memory\");\n\t\t\t\tSys_LockMutex(f->archive->mutex);\n\t\t\t\tVFS_SEEK(f->archive->file, f->archiveoffset + f->sectortab[sectidx]);\n\t\t\t\tVFS_READ(f->archive->file, cdata, rawsize);\n\t\t\t\tSys_UnlockMutex(f->archive->mutex);\n\n\t\t\t\tif (lastsect)\n\t\t\t\t\tf->bufferlength = f->flength - ((numsects-1)*f->archive->sectorsize);\n\t\t\t\telse\n\t\t\t\t\tf->bufferlength = f->archive->sectorsize;\n\n\t\t\t\tif (f->flags & MPQFileEncrypted)\n\t\t\t\t\tmpq_decrypt(cdata, rawsize, f->encryptkey+sectidx, false);\n\t\t\t\tif (f->flags & (MPQFileCompressed|MPQFileDiabloCompressed))\n\t\t\t\t{\n\t\t\t\t\t//decompress\n\t\t\t\t\tMPQF_decompress(!!(f->flags&MPQFileDiabloCompressed), f->buffer, f->bufferlength, cdata, rawsize);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t//lazy...\n\t\t\t\t\tmemcpy(f->buffer, cdata, f->bufferlength);\n\t\t\t\t}\n\t\t\t\tfree(cdata);\n\t\t\t}\n\n\t\t\tchunkofs = (f->foffset%f->archive->sectorsize);\n\t\t\tchunklen = f->archive->sectorsize - chunkofs;\n\t\t\tif (chunklen > bytestoread)\n\t\t\t\tchunklen = bytestoread;\n\t\t\tbytestoread -= chunklen;\n\t\t\tmemcpy((char*)buffer+bytesread, f->buffer + chunkofs, chunklen);\n\t\t\tf->foffset += chunklen;\n\t\t\tbytesread += chunklen;\n\t\t\tif (!chunklen || !bytestoread)\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\treturn bytesread;\n}\nstatic int MPQF_writebytes (struct vfsfile_s *file, const void *buffer, int bytestoread)\n{\n//\tmpqfile_t *f = (mpqfile_t *)file;\n\treturn 0;\n}\nstatic qboolean MPQF_seek (struct vfsfile_s *file, qofs_t pos)\n{\n\tmpqfile_t *f = (mpqfile_t *)file;\n\tif (pos > f->flength)\n\t\treturn false;\n\tf->foffset = pos;\n\treturn true;\n}\nstatic qofs_t MPQF_tell (struct vfsfile_s *file)\n{\n\tmpqfile_t *f = (mpqfile_t *)file;\n\treturn f->foffset;\n}\nstatic qofs_t MPQF_getlen (struct vfsfile_s *file)\n{\n\tmpqfile_t *f = (mpqfile_t *)file;\n\treturn f->flength;\n}\nstatic qboolean MPQF_close (struct vfsfile_s *file)\n{\n\tmpqfile_t *f = (mpqfile_t *)file;\n\tif (f->buffer)\n\t\tfree(f->buffer);\n\tif (f->sectortab)\n\t\tfree(f->sectortab);\n\n\tMPQ_ClosePath(&f->archive->pub);\n\n\tfree(f);\n\n\treturn true;\n}\nstatic void MPQF_flush (struct vfsfile_s *file)\n{\n}\n\nstatic qboolean MPQF_GetKey(unsigned int flags, unsigned int blockoffset, unsigned int blocksize, unsigned int *key)\n{\n\tif (flags & MPQFileEncrypted)\n\t{\n\t\t*key = mpq_hash_cstring(\"(listfile)\", HASH_KEY);\n\n\t\tif (flags & MPQFileOffsetAdjustedKey)\n\t\t\t*key = (*key + (unsigned int)(blockoffset)) ^ blocksize;\n\t}\n\telse\n\t\t*key = 0;\n\n\treturn true;\n}\n\nstatic vfsfile_t *MPQ_OpenVFS(searchpathfuncs_t *handle, flocation_t *loc, const char *mode)\n{\n\tmpqarchive_t *mpq = (void*)handle;\n\tmpqblock_t *block = loc->fhandle;\n\tmpqfile_t *f;\n\n\tif (block->flags & MPQFilePatch)\n\t{\n\t\tCon_Printf(\"Cannot cope with patch files\\n\");\n\t\treturn NULL;\n\t}\n\n\tf = malloc(sizeof(*f));\n\tf->buffer = NULL;\n\tf->buffersect = -1;\n\tf->sectortab = NULL;\n\tf->foffset = 0;\n\tf->archiveoffset = block->offset;\n\tMPQF_GetKey(block->flags, f->archiveoffset, block->size, &f->encryptkey);\n\tf->flags = block->flags;\n\tf->archive = mpq;\n\tf->flength = block->size;\n\tf->alength = block->archived_size;\n\tf->funcs.ReadBytes = MPQF_readbytes;\n\tf->funcs.WriteBytes = MPQF_writebytes;\n\tf->funcs.Seek = MPQF_seek;\n\tf->funcs.Tell = MPQF_tell;\n\tf->funcs.GetLen = MPQF_getlen;\n\tf->funcs.Close = MPQF_close;\n\tf->funcs.Flush = MPQF_flush;\n\n\tSys_LockMutex(mpq->mutex);\n\tmpq->references++;\n\tSys_UnlockMutex(mpq->mutex);\n\n\treturn &f->funcs;\n}\n\nqboolean MPQ_MayShutdown(void)\n{\n\treturn activempqcount==0;\n}\n\nqboolean Plug_Init(void)\n{\n\tmpq_init_cryptography();\n\n#ifdef MULTITHREAD\n\tthreading = plugfuncs->GetEngineInterface(\"Threading\", sizeof(*threading));\n#endif\n\n\t//we can't cope with being closed randomly. files cannot be orphaned safely.\n\t//so ask the engine to ensure we don't get closed before everything else is.\n\tplugfuncs->ExportFunction(\"MayShutdown\", NULL);\n\n\tif (!plugfuncs->ExportFunction(\"FS_RegisterArchiveType_mpq\", MPQ_OpenArchive))\n\t{\n\t\tCon_Printf(\"mpq: Engine doesn't support filesystem plugins\\n\");\n\t\treturn false;\n\t}\n\tif (!plugfuncs->ExportFunction(\"FS_RegisterArchiveType_MPQ\", MPQ_OpenArchive))\n\t{\n\t\tCon_Printf(\"mpq: Engine doesn't support filesystem plugins\\n\");\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\n"
  },
  {
    "path": "plugins/namemaker/namemaker.c",
    "content": "#include \"../plugin.h\"\n\nextern plugcorefuncs_t *plugfuncs;\nextern plugcmdfuncs_t *cmdfuncs;\nextern plugcvarfuncs_t *cvarfuncs;\nstatic plug2dfuncs_t *drawfuncs;\nstatic pluginputfuncs_t *inputfuncs;\n\nstatic int K_UPARROW;\nstatic int K_DOWNARROW;\nstatic int K_LEFTARROW;\nstatic int K_RIGHTARROW;\nstatic int K_ESCAPE;\nstatic int K_ENTER;\nstatic int K_KP_ENTER;\nstatic int K_MOUSE1;\nstatic int K_MOUSE2;\nstatic int K_HOME;\nstatic int K_SHIFT;\nstatic int K_MWHEELDOWN;\nstatic int K_MWHEELUP;\nstatic int K_PAGEUP;\nstatic int K_PAGEDOWN;\nstatic int K_BACKSPACE;\n\nstatic qhandle_t con_chars;\nstatic qhandle_t pic_cursor;\n\nstatic float drawscalex;\nstatic float drawscaley;\n\nstatic unsigned char namebuffer[256];\nstatic int insertpos;\n\nstatic void LoadPics(void)\n{\n\tchar buffer[256];\n\n//main bar (add cvars later)\n\tcon_chars = drawfuncs->LoadImage(\"gfx/conchars.lmp\");\n\tcvarfuncs->GetString(\"cl_cursor\", buffer, sizeof(buffer));\n\tif (*buffer)\n\t\tpic_cursor = drawfuncs->LoadImage(buffer);\n\telse\n\t\tpic_cursor = 0;\n}\n\nstatic void DrawChar(unsigned int c, int x, int y)\n{\n\tstatic const float size = 1.0f/16.0f;\n\tfloat s1 = size * (c&15);\n\tfloat t1 = size * (c>>4);\n//\tdrawfuncs->Character(x, y, 0xe000|c);\n\tdrawfuncs->Image((float)x*drawscalex, y*drawscaley, 16*drawscalex, 16*drawscaley, s1, t1, s1+size, t1+size, con_chars);\n}\n\nstatic qboolean AllowedChar(int c)\n{\n\t//normalise away any unicode chars...\n\tif (c >= 0xe000 && c <= 0xe0ff)\n\t\tc &= 0xff;\n\n\tif (c < 0x00 || c > 0xff)\n\t\treturn false;\t//not a byte\n\n\tif (c == 0)\t\treturn false;\t//block null chars\n\tif (c == '\\\\')\treturn false;\t//invalid in infokeys\n\tif (c == '\\\"')\treturn false;\t//breaks string escapes\n\tif (c == 255)\treturn false;\t//block this byte, as it causes illegible server messages in vanilla\n\n\treturn true;\t//other chars are okay.\n}\nstatic void InsertChar(int newchar)\n{\n\tint oldlen;\n\tif (!AllowedChar(newchar))\n\t\treturn;\n\n\toldlen = strlen(namebuffer);\n\tif (oldlen + 1 == sizeof(namebuffer))\n\t\treturn;\n\tnamebuffer[oldlen+1] = 0;\n\tfor (; oldlen > insertpos; oldlen--)\n\t\tnamebuffer[oldlen] = namebuffer[oldlen-1];\n\n\tnamebuffer[insertpos++] = newchar;\n}\n\nstatic void KeyPress(int key, int unicode, int mx, int my)\n{\n\tint oldlen;\n\tif (!key)\n\t\t; //invalid keys...\n\telse if (key == K_ESCAPE)\n\t\tinputfuncs->SetMenuFocus(false, NULL, 0, 0, 0); //release input focus\n\telse if (key == K_ENTER || key == K_KP_ENTER)\n\t{\n\t\tinputfuncs->SetMenuFocus(false, NULL, 0, 0, 0); //release input focus\n\t\tcvarfuncs->SetString(\"name\", (char*)namebuffer);\n\t}\n\telse if (key == K_MOUSE1)\n\t{\n\t\tmx -= ((640 - (480-16))/2);\n\t\tmy -= 16;\n\t\tmx /= (480-16)/16;\n\t\tmy /= (480-16)/16;\n\t\tif (mx < 0 || my < 0 || mx >= 16 || my >= 16)\n\t\t\treturn; //outside the grid\n\n\t\tInsertChar(mx + my*16);\n\t}\n\telse if (key == K_MOUSE2 || key == K_BACKSPACE)\n\t{\n\t\tif (insertpos > 0)\n\t\t\tinsertpos--;\n\t\tfor (oldlen = insertpos; namebuffer[oldlen]; oldlen++)\n\t\t\tnamebuffer[oldlen] = namebuffer[oldlen+1];\n\t}\n\telse if (key == K_LEFTARROW)\n\t{\n\t\tinsertpos--;\n\t\tif (insertpos < 0)\n\t\t\tinsertpos = 0;\n\t}\n\telse if (key == K_RIGHTARROW)\n\t{\n\t\tinsertpos++;\n\t\tif (insertpos > strlen(namebuffer))\n\t\t\tinsertpos = strlen(namebuffer);\n\t}\n\telse if (key == K_SHIFT)\n\t\treturn;\n\telse if ((unicode >= 0x20 && unicode <= 0x7f) || (unicode >= 0xe000 && unicode <= 0xe0ff))\n\t\tInsertChar(unicode);\n}\n\nstatic qboolean QDECL Plug_MenuEvent(int eventtype, int param, int unicode, float mousecursor_x, float mousecursor_y, float vidwidth, float vidheight)\n{\n\tint i;\n\tquintptr_t currenttime;\n\tdrawscalex = vidwidth/640.0f;\n\tdrawscaley = vidheight/480.0f;\n\n\tmousecursor_x /= drawscalex;\n\tmousecursor_y /= drawscaley;\n\n\tswitch(eventtype)\n\t{\n\tcase 0:\t//draw\n\t\tcurrenttime = plugfuncs->GetMilliseconds();\n\n\t\tdrawfuncs->Colour4f(1,1,1,1);\n\n\t\tdrawfuncs->Image(((640 - (480-16))/2)*drawscalex, 16*drawscaley, (480-16)*drawscalex, (480-16)*drawscaley, 0, 0, 1, 1, con_chars);\n\n\t\tfor (i = 0; namebuffer[i]; i++)\n\t\t\tDrawChar(namebuffer[i], i*16, 0);\n\t\tDrawChar(10 + (((currenttime/250)&1)==1), insertpos*16, 0);\n\t\tbreak;\n\tcase 1:\t//keydown\n\t\tKeyPress(param, unicode, mousecursor_x, mousecursor_y);\n\t\tbreak;\n\tcase 2:\t//keyup\n\t\tbreak;\n\tcase 3:\t//menu closed (this is called even if we change it).\n\t\tbreak;\n\tcase 4:\t//mousemove\n\t\tbreak;\n\t}\n\n\treturn 0;\n}\n\nstatic void Plug_NameMaker_f(void)\n{\n\tinputfuncs->SetMenuFocus(true, NULL, 0, 0, 0); //grab input focus\n\tcvarfuncs->GetString(\"name\", (char*)namebuffer, sizeof(namebuffer));\n\tinsertpos = strlen(namebuffer);\n}\n\nqboolean Plug_Init(void)\n{\n\tdrawfuncs = plugfuncs->GetEngineInterface(plug2dfuncs_name, sizeof(*drawfuncs));\n\tinputfuncs = plugfuncs->GetEngineInterface(pluginputfuncs_name, sizeof(*inputfuncs));\n\tif (drawfuncs && inputfuncs &&\n//\t\tplugfuncs->ExportFunction(\"SbarBase\", UI_StatusBar) &&\n//\t\tplugfuncs->ExportFunction(\"SbarOverlay\", UI_ScoreBoard) &&\n\t\tplugfuncs->ExportFunction(\"MenuEvent\", Plug_MenuEvent))\n\t{\n\n\t\tK_UPARROW\t\t= inputfuncs->GetKeyCode(\"uparrow\", NULL);\n\t\tK_DOWNARROW\t\t= inputfuncs->GetKeyCode(\"downarrow\", NULL);\n\t\tK_LEFTARROW\t\t= inputfuncs->GetKeyCode(\"leftarrow\", NULL);\n\t\tK_RIGHTARROW\t= inputfuncs->GetKeyCode(\"rightarrow\", NULL);\n\t\tK_ESCAPE\t\t= inputfuncs->GetKeyCode(\"escape\", NULL);\n\t\tK_ENTER\t\t\t= inputfuncs->GetKeyCode(\"enter\", NULL);\n\t\tK_KP_ENTER\t\t= inputfuncs->GetKeyCode(\"kp_enter\", NULL);\n\t\tK_HOME\t\t\t= inputfuncs->GetKeyCode(\"home\", NULL);\n\t\tK_MOUSE1\t\t= inputfuncs->GetKeyCode(\"mouse1\", NULL);\n\t\tK_MOUSE2\t\t= inputfuncs->GetKeyCode(\"mouse2\", NULL);\n\t\tK_MWHEELDOWN\t= inputfuncs->GetKeyCode(\"mwheeldown\", NULL);\n\t\tK_MWHEELUP\t\t= inputfuncs->GetKeyCode(\"mwheelup\", NULL);\n\t\tK_SHIFT\t\t\t= inputfuncs->GetKeyCode(\"shift\", NULL);\n\t\tK_PAGEUP\t\t= inputfuncs->GetKeyCode(\"pgup\", NULL);\n\t\tK_PAGEDOWN\t\t= inputfuncs->GetKeyCode(\"pgdn\", NULL);\n\t\tK_BACKSPACE\t\t= inputfuncs->GetKeyCode(\"backspace\", NULL);\n\n\t\tcmdfuncs->AddCommand(\"namemaker\", Plug_NameMaker_f, \"Provides a simple way to select from quake's glyphs.\");\n\n\t\tLoadPics();\n\n\t\treturn 1;\n\t}\n\treturn 0;\n}\n"
  },
  {
    "path": "plugins/namemaker/namemaker.q3asm",
    "content": "-o \"namemaker\"\nplugin\nnamemaker\nqvm_api"
  },
  {
    "path": "plugins/net_ssl_openssl.c",
    "content": "#ifndef FTEPLUGIN\n#include \"quakedef.h\"\n#define FTEENGINE\t//we're getting statically linked. lucky us.\n#define FTEPLUGIN\n#define Plug_Init Plug_OpenSSL_Init\n\n#define Q_strlcpy Q_strncpyz\n#endif\n\n#include \"plugin.h\"\n#include \"netinc.h\"\n\nstatic plugfsfuncs_t *fsfuncs;\nstatic plugnetfuncs_t *netfuncs;\n\nstatic cvar_t *pdtls_psk_hint, *pdtls_psk_user, *pdtls_psk_key;\n\n#undef SHA1\n#undef HMAC\n#include \"openssl/bio.h\"\n#include \"openssl/ssl.h\"\n#include \"openssl/err.h\"\n#include \"openssl/conf.h\"\n\n#define assert(c) do{if (!(c)) Con_Printf(\"assert failed: \"STRINGIFY(c)\"\\n\");}while(0)\n\nstatic qboolean OSSL_Init(void);\nstatic int ossl_fte_certctx;\nstruct fte_certctx_s\n{\n\tconst char *peername;\n\tqboolean dtls;\n\tqboolean failure;\n\n\thashfunc_t *hash;\t//if set peer's cert MUST match the specified digest (with this hash function)\n\tqbyte digest[DIGEST_MAXSIZE];\n};\n\nstatic struct\n{\n\tX509 *servercert;\n\tEVP_PKEY *privatekey;\n} vhost;\n\nstatic BIO_METHOD *biometh_vfs;\n\nstatic int OSSL_Bio_FWrite(BIO *h, const char *buf, int size)\n{\n\tvfsfile_t *f = BIO_get_data(h);\n\tint r = VFS_WRITE(f, buf, size);\n\tBIO_clear_retry_flags(h);\n\tif (r == 0)\n\t{\n\t\tBIO_set_retry_write(h);\n\t\tr = -1; //paranoia\n\t}\n//\telse if (r < 0)\n//\t\tCon_DPrintf(\"ossl Error: %i\\n\", r);\n\treturn r;\n}\nstatic int OSSL_Bio_FRead(BIO *h, char *buf, int size)\n{\n\tvfsfile_t *f = BIO_get_data(h);\n\tint r = VFS_READ(f, buf, size);\n\tBIO_clear_retry_flags(h);\n\tif (r == 0)\t//no data yet.\n\t{\n\t\tBIO_set_retry_read(h);\n\t\tr = -1;\t//shouldn't be needed, but I'm paranoid\n\t}\n\treturn r;\n}\nstatic int OSSL_Bio_FPuts(BIO *h, const char *buf)\n{\n\treturn OSSL_Bio_FWrite(h, buf, strlen(buf));\n}\nstatic long OSSL_Bio_FCtrl(BIO *h, int cmd, long arg1, void *arg2)\n{\n\tvfsfile_t *f = BIO_get_data(h);\n\tswitch(cmd)\n\t{\n\tcase BIO_CTRL_FLUSH:\n\t\tVFS_FLUSH(f);\n\t\treturn 1;\n\tdefault:\n//\t\tCon_Printf(\"OSSL_Bio_FCtrl: unknown cmd %i\\n\", cmd);\n\tcase BIO_CTRL_PUSH:\n\tcase BIO_CTRL_POP:\n\t\treturn 0;\n\t}\n\treturn 0;\t//failure\n}\nstatic long OSSL_Bio_FOtherCtrl(BIO *h, int cmd, BIO_info_cb *cb)\n{\n\tswitch(cmd)\n\t{\n\tdefault:\n//\t\tCon_Printf(\"OSSL_Bio_FOtherCtrl unknown cmd %i\\n\", cmd);\n\t\treturn 0;\n\t}\n\treturn 0;\t//failure\n}\nstatic int OSSL_Bio_FCreate(BIO *h)\n{\t//we'll have to fill this in after we create the bio.\n\tBIO_set_data(h, NULL);\n\treturn 1;\n}\nstatic int OSSL_Bio_FDestroy(BIO *h)\n{\n\tvfsfile_t *f = BIO_get_data(h);\n\tVFS_CLOSE(f);\n\tBIO_set_data(h, NULL);\n\treturn 1;\n}\n\nstatic int OSSL_PrintError_CB (const char *str, size_t len, void *u)\n{\n\tCon_Printf(\"%s\\n\", str);\n\treturn 1;\n}\n\ntypedef struct\n{\n\tvfsfile_t funcs;\n\tstruct fte_certctx_s cert;\n\tSSL_CTX *ctx;\n\tBIO *bio;\n\tSSL *ssl;\n} osslvfs_t;\nstatic int QDECL OSSL_FRead (struct vfsfile_s *file, void *buffer, int bytestoread)\n{\n\tosslvfs_t *o = (osslvfs_t*)file;\n\tint r = BIO_read(o->bio, buffer, bytestoread);\n\tif (r <= 0)\n\t{\n\t\tif (BIO_should_io_special(o->bio))\n\t\t{\n\t\t\tswitch(BIO_get_retry_reason(o->bio))\n\t\t\t{\n\t\t\t//these are temporary errors, try again later.\n\t\t\tcase BIO_RR_SSL_X509_LOOKUP:\n\t\t\t\treturn -1;\t//certificate failure.\n\t\t\tcase BIO_RR_ACCEPT:\n\t\t\tcase BIO_RR_CONNECT:\n\t\t\t\treturn -1;\t//should never happen\n\t\t\t}\n\t\t}\n\t\tif (BIO_should_retry(o->bio))\n\t\t\treturn 0;\n\t\treturn -1;\t//eof or something\n\t}\n\treturn r;\n}\nstatic int QDECL OSSL_FWrite (struct vfsfile_s *file, const void *buffer, int bytestowrite)\n{\n\tosslvfs_t *o = (osslvfs_t*)file;\n\tint r = BIO_write(o->bio, buffer, bytestowrite);\n\tif (r <= 0)\n\t{\n\t\tif (BIO_should_io_special(o->bio))\n\t\t{\n\t\t\tswitch(BIO_get_retry_reason(o->bio))\n\t\t\t{\n\t\t\t//these are temporary errors, try again later.\n\t\t\tcase BIO_RR_SSL_X509_LOOKUP:\n\t\t\t\treturn -1;\t//certificate failure.\n\t\t\tcase BIO_RR_ACCEPT:\n\t\t\tcase BIO_RR_CONNECT:\n\t\t\t\treturn -1;\t//should never happen\n\t\t\t}\n\t\t}\n\t\tif (BIO_should_retry(o->bio))\n\t\t\treturn 0;\n\t\treturn -1;\t//eof or something\n\t}\n\treturn r;\n}\n//static qboolean QDECL OSSL_Seek (struct vfsfile_s *file, qofs_t pos);\t//returns false for error\n//static qofs_t QDECL OSSL_Tell (struct vfsfile_s *file);\n//static qofs_t QDECL OSSL_GetLen (struct vfsfile_s *file);\t//could give some lag\nstatic qboolean QDECL OSSL_Close (struct vfsfile_s *file)\n{\n\tosslvfs_t *o = (osslvfs_t*)file;\n\tBIO_free(o->bio);\n\tSSL_CTX_free(o->ctx);\n\tfree(o);\n\treturn true;\t//success, I guess\n}\n//static void QDECL OSSL_Flush (struct vfsfile_s *file);\n\n\n\nstatic qboolean print_cn_name(X509_NAME* const name, const char *utf8match, const char *prefix)\n{\n    int idx = -1;\n\tqboolean success = 0;\n    unsigned char *utf8 = NULL;\n\tX509_NAME_ENTRY* entry;\n\tASN1_STRING* data;\n\tint length;\n\n    do\n    {\n        if(!name) break; /* failed */\n\n        idx = X509_NAME_get_index_by_NID(name, NID_commonName, -1);\n        if(!(idx > -1))  break; /* failed */\n\n        entry = X509_NAME_get_entry(name, idx);\n        if(!entry) break; /* failed */\n\n        data = X509_NAME_ENTRY_get_data(entry);\n        if(!data) break; /* failed */\n\n        length = ASN1_STRING_to_UTF8(&utf8, data);\n        if(!utf8 || !(length > 0))  break; /* failed */\n\n\t\tif (utf8match)\n\t\t\tsuccess = !strcmp(utf8, utf8match);\n\t\telse\n\t\t{\n\t\t\tCon_Printf(\"%s%s\", prefix, utf8);\n\t\t\tsuccess = 1;\n\t\t}\n\n    } while (0);\n\n    if(utf8)\n        OPENSSL_free(utf8);\n\n\treturn success;\n}\n\nstatic int OSSL_Verify_Peer(int preverify_ok, X509_STORE_CTX *x509_ctx)\n{\n\tSSL *ssl = X509_STORE_CTX_get_ex_data(x509_ctx, SSL_get_ex_data_X509_STORE_CTX_idx());\n\tstruct fte_certctx_s *uctx = SSL_get_ex_data(ssl, ossl_fte_certctx);\n\n\tif (uctx->hash)\n\t{\t//our special 'must-match-digest' mode without any other kind of trust.\n\t\tX509* cert = X509_STORE_CTX_get_current_cert(x509_ctx);\n\t\tsize_t blobsize;\n\t\tqbyte *blob;\n\t\tqbyte *end;\n\t\tqbyte digest[DIGEST_MAXSIZE];\n\t\tvoid *hctx = alloca(uctx->hash->contextsize);\n\t\tblobsize = i2d_X509(cert, NULL);\n\t\tblob = alloca(blobsize);\n\t\tend = blob;\n\t\ti2d_X509(cert, &end);\n\n\t\tuctx->hash->init(hctx);\n\t\tuctx->hash->process(hctx, blob, blobsize);\n\t\tuctx->hash->terminate(digest, hctx);\n\n\t\t//return 1 for success\n\t\tif (memcmp(digest, uctx->digest, uctx->hash->digestsize))\n\t\t{\n\t\t\tuctx->failure = true;\n\t\t\treturn 0;\n\t\t}\n\t\treturn 1;\n\t}\n\n\tif(preverify_ok == 0)\n\t{\n\t\tint depth = X509_STORE_CTX_get_error_depth(x509_ctx);\n\t\tint err = X509_STORE_CTX_get_error(x509_ctx);\n\n\t\tX509* cert = X509_STORE_CTX_get_current_cert(x509_ctx);\n\t\tX509_NAME* iname = cert ? X509_get_issuer_name(cert) : NULL;\n\t\t//X509_NAME* sname = cert ? X509_get_subject_name(cert) : NULL;\n\n\t\tif (err == X509_V_ERR_CERT_HAS_EXPIRED || err == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT)\n\t\t{\n\t\t\tsize_t knownsize;\n\t\t\tqbyte *knowndata = netfuncs->TLS_GetKnownCertificate(uctx->peername, &knownsize);\n\t\t\tif (knowndata)\n\t\t\t{\t//check\n\t\t\t\tsize_t blobsize;\n\t\t\t\tqbyte *blob;\n\t\t\t\tqbyte *end;\n\t\t\t\tblobsize = i2d_X509(cert, NULL);\n\t\t\t\tif (blobsize == knownsize)\n\t\t\t\t{\t//sizes must match.\n\t\t\t\t\tblob = alloca(blobsize);\n\t\t\t\t\tend = blob;\n\t\t\t\t\ti2d_X509(cert, &end);\n\t\t\t\t\tif (!memcmp(blob, knowndata, blobsize))\n\t\t\t\t\t\tpreverify_ok = 1;\t//exact match to a known cert. yay. allow it.\n\t\t\t\t}\n\t\t\t\tplugfuncs->Free(knowndata);\n\t\t\t\treturn preverify_ok;\n\t\t\t}\n\n#ifdef HAVE_CLIENT\n\t\t\tif (uctx->dtls)\n\t\t\t{\n\t\t\t\tunsigned int probs = 0;\n\t\t\t\tsize_t blobsize;\n\t\t\t\tqbyte *blob;\n\t\t\t\tqbyte *end;\n\t\t\t\tblobsize = i2d_X509(cert, NULL);\n\t\t\t\tblob = alloca(blobsize);\n\t\t\t\tend = blob;\n\t\t\t\ti2d_X509(cert, &end);\n\n\t\t\t\tswitch(err)\n\t\t\t\t{\n\t\t\t\tcase 0:\n\t\t\t\t\tprobs |= CERTLOG_WRONGHOST;\n\t\t\t\t\tbreak;\n\t\t\t\tcase X509_V_ERR_CERT_HAS_EXPIRED:\n\t\t\t\t\tprobs |= CERTLOG_EXPIRED;\n\t\t\t\t\tbreak;\n\t\t\t\tcase X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:\n\t\t\t\t\tprobs |= CERTLOG_MISSINGCA;\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tprobs |= CERTLOG_UNKNOWN;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tif (netfuncs->CertLog_ConnectOkay && netfuncs->CertLog_ConnectOkay(uctx->peername, blob, blobsize, probs))\n\t\t\t\t\treturn 1; //ignore the errors...\n\t\t\t\treturn 0;\t//allow it.\n\t\t\t}\n#endif\n\t\t}\n\n\t\tCon_Printf(CON_ERROR\"%s \", uctx->peername);\n\t\t//FIXME: this is probably on a worker thread. expect munged prints.\n\t\t//print_cn_name(sname, NULL, CON_ERROR);\n\t\tCon_Printf(\" (issued by \");\n\t\tprint_cn_name(iname, NULL, S_COLOR_YELLOW);\n\t\tCon_Printf(\")\");\n\t\tif(depth == 0) {\n\t\t\t/* If depth is 0, its the server's certificate. Print the SANs too */\n\t//\t\tprint_san_name(\"Subject (san)\", cert);\n\t\t}\n\n\t\tif(err == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY)\n\t\t\tCon_Printf(\":  Error = X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY\\n\");\n\t\telse if (err == X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE)\n\t\t\tCon_Printf(\":  Error = X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE\\n\");\n\t\telse if(err == X509_V_ERR_CERT_UNTRUSTED)\n\t\t\tCon_Printf(\":  Error = X509_V_ERR_CERT_UNTRUSTED\\n\");\n\t\telse if(err == X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN)\n\t\t\tCon_Printf(\":  Error = X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN\\n\");\n\t\telse if(err == X509_V_ERR_CERT_NOT_YET_VALID)\n\t\t\tCon_Printf(\":  Error = X509_V_ERR_CERT_NOT_YET_VALID\\n\");\n\t\telse if(err == X509_V_ERR_CERT_HAS_EXPIRED)\n\t\t\tCon_Printf(\":  Error = X509_V_ERR_CERT_HAS_EXPIRED\\n\");\n\t\telse if(err == X509_V_OK)\n\t\t\tCon_Printf(\":  Error = X509_V_OK\\n\");\n\t\telse if(err == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT)\n\t\t\tCon_Printf(\":  Error = X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT\\n\");\n\t\telse\n\t\t\tCon_Printf(\":  Error = %d\\n\", err);\n\t}\n\n\tif (!preverify_ok && cvarfuncs->GetFloat(\"tls_ignorecertificateerrors\"))\n\t{\n\t\tCon_Printf(CON_ERROR \"%s: Ignoring certificate errors (tls_ignorecertificateerrors is set)\\n\", uctx->peername);\n\t\treturn 1;\n\t}\n\n\treturn preverify_ok;\n}\n\n\nstatic vfsfile_t *OSSL_OpenPrivKeyFile(char *nativename, size_t nativesize)\n{\n#define privname \"privkey.pem\"\n\tvfsfile_t *privf;\n\tconst char *mode = nativename?\"wb\":\"rb\";\n\t/*int i = COM_CheckParm(\"-privkey\");\n\tif (i++)\n\t{\n\t\tif (nativename)\n\t\t\tQ_strncpyz(nativename, com_argv[i], nativesize);\n\t\tprivf = FS_OpenVFS(com_argv[i], mode, FS_SYSTEM);\n\t}\n\telse*/\n\t{\n\t\tif (nativename)\n\t\t\tif (!fsfuncs->NativePath(privname, FS_ROOT, nativename, nativesize))\n\t\t\t\treturn NULL;\n\n\t\tprivf = fsfuncs->OpenVFS(privname, mode, FS_ROOT);\n\t}\n\treturn privf;\n#undef privname\n}\nstatic vfsfile_t *OSSL_OpenPubKeyFile(char *nativename, size_t nativesize)\n{\n#define fullchainname \"fullchain.pem\"\n#define pubname \"cert.pem\"\n\tvfsfile_t *pubf = NULL;\n\tconst char *mode = nativename?\"wb\":\"rb\";\n\t/*int i = COM_CheckParm(\"-pubkey\");\n\tif (i++)\n\t{\n\t\tif (nativename)\n\t\t\tQ_strncpyz(nativename, com_argv[i], nativesize);\n\t\tpubf = FS_OpenVFS(com_argv[i], mode, FS_SYSTEM);\n\t}\n\telse*/\n\t{\n\t\tif (!pubf && (!nativename || fsfuncs->NativePath(fullchainname, FS_ROOT, nativename, nativesize)))\n\t\t\tpubf = fsfuncs->OpenVFS(fullchainname, mode, FS_ROOT);\n\t\tif (!pubf && (!nativename || fsfuncs->NativePath(pubname, FS_ROOT, nativename, nativesize)))\n\t\t\tpubf = fsfuncs->OpenVFS(pubname, mode, FS_ROOT);\n\t}\n\treturn pubf;\n#undef pubname\n}\nstatic BIO *OSSL_BioFromFile(vfsfile_t *f)\n{\n\tqbyte buf[4096];\n\tint r;\n\tBIO *b = BIO_new(BIO_s_mem());\n\tif (f)\n\t{\n\t\tfor(;;)\n\t\t{\n\t\t\tr = VFS_READ(f, buf, sizeof(buf));\n\t\t\tif (r <= 0)\n\t\t\t\tbreak;\n\t\t\tBIO_write(b, buf, r);\n\t\t}\n\t\tVFS_CLOSE(f);\n\t}\n\n\treturn b;\n}\nstatic void OSSL_OpenPrivKey(void)\n{\n\tBIO *bio = OSSL_BioFromFile(OSSL_OpenPrivKeyFile(NULL,0));\n\tvhost.privatekey = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);\n\tBIO_free(bio);\n}\nstatic void OSSL_OpenPubKey(void)\n{\n\tBIO *bio = OSSL_BioFromFile(OSSL_OpenPubKeyFile(NULL,0));\n\tvhost.servercert = PEM_read_bio_X509(bio, NULL, NULL, NULL);\n\tBIO_free(bio);\n}\n\nstatic char *OSSL_SetCertificateName(char *out, const char *hostname)\n{\t//glorified strcpy...\n\tint i;\n\tif (hostname)\n\t{\n\t\tconst char *host = strstr(hostname, \"://\");\n\t\tif (host)\n\t\t\thostname = host+3;\n\t\t//any dtls:// prefix will have been stripped now.\n\t\tif (*hostname == '[')\n\t\t{\t//eg: [::1]:foo - skip the lead [ and strip the ] and any trailing data (hopefully just a :port or nothing)\n\t\t\thostname++;\n\t\t\thost = strchr(hostname, ']');\n\t\t\tif (host)\n\t\t\t{\n\t\t\t\tmemcpy(out, hostname, host-hostname);\n\t\t\t\tout[host-hostname] = 0;\n\t\t\t\thostname = out;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\t//eg: 127.0.0.1:port - strip the port number if specified.\n\t\t\thost = strchr(hostname, ':');\n\t\t\tif (host)\n\t\t\t{\n\t\t\t\tmemcpy(out, hostname, host-hostname);\n\t\t\t\tout[host-hostname] = 0;\n\t\t\t\thostname = out;\n\t\t\t}\n\t\t}\n\t\tfor (i = 0; hostname[i]; i++)\n\t\t{\n\t\t\tif (hostname[i] >= 'a' && hostname[i] <= 'z')\n\t\t\t\t;\n\t\t\telse if (hostname[i] >= 'A' && hostname[i] <= 'Z')\n\t\t\t\t;\n\t\t\telse if (hostname[i] >= '0' && hostname[i] <= '9')\n\t\t\t\t;\n\t\t\telse if (hostname[i] == '-' || hostname[i] == '.')\n\t\t\t\t;\n\t\t\telse\n\t\t\t{\n\t\t\t\thostname = NULL;\t//something invalid. bum.\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\t//we should have a cleaned up host name now, ready for (ab)use in certificates.\n\t}\n\n\tif (!hostname)\n\t\t*out = 0;\n\telse if (hostname == out)\n\t\t;\n\telse\n\t\tmemcpy(out, hostname, strlen(hostname)+1);\n\n\treturn out;\n}\n\nstatic vfsfile_t *OSSL_OpenVFS(const char *hostname, vfsfile_t *source, qboolean isserver)\n{\n\tBIO *sink;\n\tosslvfs_t *n;\n\tif (!OSSL_Init())\n\t\treturn NULL;\t//FAIL!\n\n\tif (!hostname)\n\t\thostname = \"\";\n\n\tn = calloc(sizeof(*n) + strlen(hostname)+1, 1);\n\n\tn->funcs.ReadBytes = OSSL_FRead;\n\tn->funcs.WriteBytes = OSSL_FWrite;\n\tn->funcs.Seek = NULL;\n\tn->funcs.Tell = NULL;\n\tn->funcs.GetLen = NULL;\n\tn->funcs.Close = OSSL_Close;\n\tn->funcs.Flush = NULL;\n\tn->funcs.seekstyle = SS_UNSEEKABLE;\n\n\tn->cert.peername = OSSL_SetCertificateName((char*)(n+1), hostname);\n\tn->cert.dtls = false;\n\n\tERR_print_errors_cb(OSSL_PrintError_CB, NULL);\n\n\tsink = BIO_new(biometh_vfs);\n\tif (sink)\n\t{\n\t\tn->ctx = SSL_CTX_new(isserver?TLS_server_method():TLS_client_method());\n\t\tif (n->ctx)\n\t\t{\n\t\t\tassert(1==SSL_CTX_set_cipher_list(n->ctx, \"ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH\"));\n\n\t\t\tSSL_CTX_set_session_cache_mode(n->ctx, SSL_SESS_CACHE_OFF);\n\n\t\t\tSSL_CTX_set_default_verify_paths(n->ctx);\n\t\t\tSSL_CTX_set_verify(n->ctx, SSL_VERIFY_PEER, OSSL_Verify_Peer);\n\t\t\tSSL_CTX_set_verify_depth(n->ctx, 5);\n\t\t\tSSL_CTX_set_options(n->ctx, SSL_OP_NO_COMPRESSION);\t//compression allows guessing the contents of the stream somehow.\n\n\t\t\tif (isserver)\n\t\t\t{\n\t\t\t\tif (vhost.servercert && vhost.privatekey)\n\t\t\t\t{\n\t\t\t\t\tSSL_CTX_use_certificate(n->ctx, vhost.servercert);\n\t\t\t\t\tSSL_CTX_use_PrivateKey(n->ctx, vhost.privatekey);\n\t\t\t\t\tassert(1==SSL_CTX_check_private_key(n->ctx));\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tn->bio = BIO_new_ssl(n->ctx, !isserver);\n\n\t\t\t//set up the source/sink\n\t\t\tBIO_set_data(sink, source);\t//source now belongs to the bio\n\t\t\tBIO_set_init(sink, true);\t//our sink is now ready...\n\t\t\tn->bio = BIO_push(n->bio, sink);\n\t\t\tBIO_free(sink);\n\t\t\tsink = NULL;\n\n\t\t\tBIO_get_ssl(n->bio, &n->ssl);\n\t\t\tSSL_set_ex_data(n->ssl, ossl_fte_certctx, &n->cert);\n\t\t\tSSL_set_mode(n->ssl, SSL_MODE_ENABLE_PARTIAL_WRITE|SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);\n\t\t\tSSL_set_tlsext_host_name(n->ssl, n->cert.peername);\t//let the server know which cert to send\n\t\t\tBIO_do_connect(n->bio);\n\t\t\treturn &n->funcs;\n\t\t}\n\t\tBIO_free(sink);\n\t}\n\treturn NULL;\n}\n/*static int OSSL_GetChannelBinding(vfsfile_t *vf, qbyte *binddata, size_t *bindsize)\n{\n\t//FIXME: not yet supported. tbh I've no idea how to get that data. probably something convoluted.\n\treturn -1;\n}*/\n\n\n\nstatic BIO_METHOD *biometh_dtls;\ntypedef struct {\n\tstruct fte_certctx_s cert;\n\n\tvoid *cbctx;\n\tneterr_t(*push)(void *cbctx, const qbyte *data, size_t datasize);\n\n\tSSL_CTX *ctx;\n\tBIO *bio;\n\tSSL *ssl;\n\n//\tBIO *sink;\n\tqbyte *pending;\n\tsize_t pendingsize;\n\n\tvoid\t*peeraddr;\n\tsize_t\tpeeraddrsize;\n} ossldtls_t;\nstatic int OSSL_Bio_DWrite(BIO *h, const char *buf, int size)\n{\n\tossldtls_t *f = BIO_get_data(h);\n\tneterr_t r = f->push(f->cbctx, buf, size);\n\n\tBIO_clear_retry_flags(h);\n\tswitch(r)\n\t{\n\tcase NETERR_SENT:\n\t\treturn size;\n\tcase NETERR_NOROUTE:\n\tcase NETERR_DISCONNECTED:\n\t\treturn -1;\n\tcase NETERR_MTU:\n\t\treturn -1;\n\tcase NETERR_CLOGGED:\n\t\tBIO_set_retry_write(h);\n\t\treturn -1;\n\t}\n\treturn r;\n}\nstatic int OSSL_Bio_DRead(BIO *h, char *buf, int size)\n{\n\tossldtls_t *f = BIO_get_data(h);\n\n\tBIO_clear_retry_flags(h);\n\tif (f->pending)\n\t{\n\t\tsize = min(size, f->pendingsize);\n\t\tmemcpy(buf, f->pending, f->pendingsize);\n\n\t\t//we've read it now, don't read it again.\n\t\tf->pending = 0;\n\t\tf->pendingsize = 0;\n\t\treturn size;\n\t}\n\t//nothing available.\n\tBIO_set_retry_read(h);\n\treturn -1;\n}\nstatic long OSSL_Bio_DCtrl(BIO *h, int cmd, long arg1, void *arg2)\n{\n//\tossldtls_t *f = BIO_get_data(h);\n\tswitch(cmd)\n\t{\n\tcase BIO_CTRL_FLUSH:\n\t\treturn 1;\n\n\n\tcase BIO_CTRL_DGRAM_GET_PEER:\n\t\treturn 0;\n\n\tcase BIO_CTRL_DGRAM_SET_NEXT_TIMEOUT:\t//we're non-blocking, so this doesn't affect us.\n\tcase BIO_CTRL_DGRAM_GET_MTU_OVERHEAD:\n\tcase BIO_CTRL_WPENDING:\n\tcase BIO_CTRL_DGRAM_QUERY_MTU:\n\tcase BIO_CTRL_DGRAM_SET_MTU:\n\tcase BIO_CTRL_DGRAM_GET_FALLBACK_MTU:\n\t\treturn 0;\n\n\n\tdefault:\n//\t\tCon_Printf(\"OSSL_Bio_DCtrl: unknown cmd %i\\n\", cmd);\n\tcase BIO_CTRL_PUSH:\n\tcase BIO_CTRL_POP:\n\t\treturn 0;\n\t}\n\treturn 0;\t//failure\n}\nstatic long OSSL_Bio_DOtherCtrl(BIO *h, int cmd, BIO_info_cb *cb)\n{\n\tswitch(cmd)\n\t{\n\tdefault:\n//\t\tCon_Printf(\"OSSL_Bio_DOtherCtrl unknown cmd %i\\n\", cmd);\n\t\treturn 0;\n\t}\n\treturn 0;\t//failure\n}\nstatic int OSSL_Bio_DCreate(BIO *h)\n{\t//we'll have to fill this in after we create the bio.\n\tBIO_set_data(h, NULL);\n\treturn 1;\n}\nstatic int OSSL_Bio_DDestroy(BIO *h)\n{\n\tBIO_set_data(h, NULL);\n\treturn 1;\n}\n\nstatic int dehex(int i)\n{\n\tif      (i >= '0' && i <= '9')\n\t\treturn (i-'0');\n\telse if (i >= 'A' && i <= 'F')\n\t\treturn (i-'A'+10);\n\telse\n\t\treturn (i-'a'+10);\n}\nstatic size_t Base16_DecodeBlock_(const char *in, qbyte *out, size_t outsize)\n{\n\tqbyte *start = out;\n\tif (!out)\n\t\treturn ((strlen(in)+1)/2) + 1;\n\n\tfor (; ishexcode(in[0]) && ishexcode(in[1]) && outsize > 0; outsize--, in+=2)\n\t\t*out++ = (dehex(in[0])<<4) | dehex(in[1]);\n\treturn out-start;\n}\n\nstatic unsigned int OSSL_SV_Validate_PSK(SSL *ssl, const char *identity, unsigned char *psk, unsigned int max_psk_len)\n{\n\tif (!strcmp(identity, pdtls_psk_user->string))\n\t{\t//Yay! We know this one!\n\t\treturn Base16_DecodeBlock_(pdtls_psk_key->string, psk, max_psk_len);\n\t}\n\treturn 0;\t//0 for error, or something.\n}\nunsigned int OSSL_CL_Validate_PSK(SSL *ssl, const char *hint, char *identity, unsigned int max_identity_len, unsigned char *psk, unsigned int max_psk_len)\n{\t//if our hint cvar matches, then report our user+key cvars to the server\n\tif ((!*hint && *pdtls_psk_user->string && !*pdtls_psk_hint->string) || (*hint && !strcmp(hint, pdtls_psk_hint->string)))\n\t{\n#ifndef NOLEGACY\n\t\tif (*hint)\n\t\t{\n\t\t\t//Try to avoid crashing QE servers by recognising its hint and blocking it when the hashes of the user+key are wrong.\n\t\t\tquint32_t digest[SHA_DIGEST_LENGTH/4];\n\n\t\t\tSHA1(hint, strlen(hint), (qbyte*)digest);\n\t\t\tif ((digest[0]^digest[1]^digest[2]^digest[3]^digest[4]) == 0xb6c27b61)\n\t\t\t{\n\t\t\t\tSHA1(pdtls_psk_key->string, strlen(pdtls_psk_key->string), (qbyte*)digest);\n\t\t\t\tif (strcmp(hint, pdtls_psk_user->string) || (digest[0]^digest[1]^digest[2]^digest[3]^digest[4]) != 0x3dd348e4)\n\t\t\t\t{\n\t\t\t\t\tCon_Printf(CON_WARNING\t\"Possible QEx Server, please set your ^[%s\\\\type\\\\%s^] and ^[%s\\\\type\\\\%s^] cvars correctly, their current values are likely to crash the server.\\n\", pdtls_psk_user->name,pdtls_psk_user->name, pdtls_psk_key->name,pdtls_psk_key->name);\n\t\t\t\t\treturn 0; //don't report anything.\n\t\t\t\t}\n\t\t\t}\n\t\t}\n#endif\n\n\t\tQ_strlcpy(identity, pdtls_psk_user->string, max_identity_len);\n\t\treturn Base16_DecodeBlock_(pdtls_psk_key->string, psk, max_psk_len);\n\t}\n\telse if (*hint)\n\t\tCon_Printf(CON_WARNING\t\"Unable to supply PSK response to server (hint is \\\"%s\\\").\\n\"\n\t\t\t\t\t\t\t\t\"Please set ^[%s\\\\type\\\\%s^], ^[%s\\\\type\\\\%s^], and ^[%s\\\\type\\\\%s^] cvars to match the server.\\n\", hint, pdtls_psk_hint->name,pdtls_psk_hint->name, pdtls_psk_user->name,pdtls_psk_user->name, pdtls_psk_key->name,pdtls_psk_key->name);\n\treturn 0;\t//we don't know what to report.\n}\n\nstatic void *OSSL_CreateContext(const dtlscred_t *cred, void *cbctx, neterr_t(*push)(void *cbctx, const qbyte *data, size_t datasize), qboolean isserver)\n{\t//if remotehost is null then their certificate will not be validated.\n\tossldtls_t *n;\n\tBIO *sink;\n\tconst char *remotehost = cred?cred->peer.name:NULL;\n\n\tif (!remotehost)\n\t\tremotehost = \"\";\n\tn = calloc(sizeof(*n) + strlen(remotehost)+1, 1);\n\n\tn->cbctx = cbctx;\n\tn->push = push;\n\n\tn->ctx = SSL_CTX_new(isserver?DTLS_server_method():DTLS_client_method());\n\n\tn->cert.peername = OSSL_SetCertificateName((char*)(n+1), remotehost);\n\tn->cert.dtls = true;\n\tif (cred)\n\t{\n\t\tn->cert.hash = cred->peer.hash;\n\t\tmemcpy(n->cert.digest, cred->peer.digest, sizeof(cred->peer.digest));\n\t}\n\telse\n\t{\n\t\tn->cert.hash = NULL;\n\t\tmemset(n->cert.digest, 0, sizeof(n->cert.digest));\n\t}\n\n\tif (n->ctx)\n\t{\n\t\tassert(1==SSL_CTX_set_cipher_list(n->ctx, \"ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH\"));\n\n\t\tSSL_CTX_set_session_cache_mode(n->ctx, SSL_SESS_CACHE_OFF);\n\n\t\tSSL_CTX_set_verify(n->ctx, SSL_VERIFY_PEER|(n->cert.hash?SSL_VERIFY_FAIL_IF_NO_PEER_CERT:0), OSSL_Verify_Peer);\n\t\tSSL_CTX_set_verify_depth(n->ctx, 10);\n\t\tSSL_CTX_set_options(n->ctx, SSL_OP_NO_COMPRESSION|\t//compression allows guessing the contents of the stream somehow.\n\t\t\t\t\t\t\t\t\tSSL_OP_NO_RENEGOTIATION);\n\n\t\tif (cred && (cred->local.certsize||cred->local.keysize))\n\t\t{\n\t\t\tX509 *cert = NULL;\n\t\t\tEVP_PKEY *key = NULL;\n\t\t\tconst unsigned char *ffs;\n\t\t\tffs = cred->local.cert;\n\t\t\td2i_X509(&cert, &ffs, cred->local.certsize);\n\t\t\tSSL_CTX_use_certificate(n->ctx, cert);\n\n\t\t\tffs = cred->local.key;\n\t\t\td2i_PrivateKey(EVP_PKEY_RSA, &key, &ffs, cred->local.keysize);\n\t\t\tSSL_CTX_use_PrivateKey(n->ctx, key);\n\t\t}\n\t\telse if (isserver)\n\t\t{\n\t\t\tif (*pdtls_psk_user->string)\n\t\t\t{\n\t\t\t\tif (*pdtls_psk_user->string)\n\t\t\t\t\tSSL_CTX_use_psk_identity_hint(n->ctx, pdtls_psk_hint->string);\n\t\t\t\tSSL_CTX_set_psk_server_callback(n->ctx, OSSL_SV_Validate_PSK);\n\t\t\t}\n\n\t\t\tif (vhost.servercert && vhost.privatekey)\n\t\t\t{\n\t\t\t\tSSL_CTX_use_certificate(n->ctx, vhost.servercert);\n\t\t\t\tSSL_CTX_use_PrivateKey(n->ctx, vhost.privatekey);\n\t\t\t\tassert(1==SSL_CTX_check_private_key(n->ctx));\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n//\t\t\tif (*pdtls_psk_user->string)\n\t\t\t\tSSL_CTX_set_psk_client_callback(n->ctx, OSSL_CL_Validate_PSK);\n\t\t}\n\n\t\t//SSL_CTX_use_certificate_file\n\t\t//FIXME: SSL_CTX_use_certificate_file aka SSL_CTX_use_certificate(PEM_read_bio_X509)\n\t\t//FIXME: SSL_CTX_use_PrivateKey_file aka SSL_CTX_use_PrivateKey(PEM_read_bio_PrivateKey)\n\t\t//assert(1==SSL_CTX_check_private_key(n->ctx));\n\n\t\t{\n\t\t\tn->bio = BIO_new_ssl(n->ctx, !isserver);\n\n\t\t\t//set up the source/sink\n\t\t\tsink = BIO_new(biometh_dtls);\n\t\t\tif (sink)\n\t\t\t{\n\t\t\t\tBIO_set_data(sink, n);\n\t\t\t\tBIO_set_init(sink, true);\t//our sink is now ready...\n\t\t\t\tn->bio = BIO_push(n->bio, sink);\n\t\t\t\tBIO_free(sink);\n\t\t\t\tsink = NULL;\n\t\t\t}\n\n\t\t\tBIO_get_ssl(n->bio, &n->ssl);\n\t\t\tSSL_set_app_data(n->ssl, n);\n\t\t\tSSL_set_ex_data(n->ssl, ossl_fte_certctx, &n->cert);\n\t\t\tif (*n->cert.peername)\n\t\t\t\tSSL_set_tlsext_host_name(n->ssl, n->cert.peername);\t//let the server know which cert to send\n\t\t\tBIO_do_connect(n->bio);\n\t\t\tERR_print_errors_cb(OSSL_PrintError_CB, NULL);\n\t\t\treturn n;\n\t\t}\n\t}\n\n\treturn NULL;\n}\n\nstatic qbyte dtlscookiekey[16];\nstatic int OSSL_GenCookie(SSL *ssl, unsigned char *cookie, unsigned int *cookie_len)\n{\n\tossldtls_t *f = SSL_get_app_data(ssl);\n\tqbyte *blurgh = alloca(sizeof(dtlscookiekey) + f->peeraddrsize);\n\n\tmemcpy(blurgh, dtlscookiekey, sizeof(dtlscookiekey));\n\tmemcpy(blurgh+sizeof(dtlscookiekey), f->peeraddr, f->peeraddrsize);\n\n\tSHA1(blurgh, sizeof(dtlscookiekey) + f->peeraddrsize, cookie);\n\t*cookie_len = SHA_DIGEST_LENGTH;\n\n\treturn 1;\n}\nstatic int OSSL_VerifyCookie(SSL *ssl, const unsigned char *cookie, unsigned int cookie_len)\n{\n\tunsigned char match[DTLS1_COOKIE_LENGTH];\n\tunsigned int matchsize;\n\tif (OSSL_GenCookie(ssl, match, &matchsize))\n\t\tif (cookie_len == matchsize && !memcmp(cookie, match, matchsize))\n\t\t\treturn 1;\n\treturn 0;\t//not valid.\n}\nqboolean OSSL_CheckConnection(void *cbctx, void *peeraddr, size_t peeraddrsize, void *indata, size_t insize, neterr_t(*push)(void *cbctx, const qbyte *data, size_t datasize), void (*EstablishTrueContext)(void **cbctx, void *state))\n{\n\tint ret;\n\tstatic ossldtls_t *pending;\n\tBIO_ADDR *bioaddr = BIO_ADDR_new();\n\n\tif (!pending)\n\t{\n\t\tpending = OSSL_CreateContext(NULL, cbctx, push, true);\n\n\t\tSSL_CTX_set_cookie_generate_cb(pending->ctx, OSSL_GenCookie);\n\t\tSSL_CTX_set_cookie_verify_cb(pending->ctx, OSSL_VerifyCookie);\n\t}\n\n\tSSL_set_app_data(pending->ssl, pending);\n\n\t//make sure its kept current...\n\tpending->cbctx = cbctx;\n\tpending->push = push;\n\n\tpending->pending = indata;\n\tpending->pendingsize = insize;\n\tret = DTLSv1_listen(pending->ssl, bioaddr);\n\n\tBIO_ADDR_free(bioaddr);\n\n\tif (ret >= 1)\n\t{\n\t\tpending->pending = NULL;\n\t\tpending->pendingsize = 0;\n\n\t\tEstablishTrueContext(&pending->cbctx, pending);\n\t\tpending = NULL;\t//returned to called. next request gets a new one.\n\t\treturn true;\n\t}\n\t//0 = nonfatal\n\t//-1 = fatal\n\treturn false;\n}\nstatic void OSSL_DestroyContext(void *ctx)\n{\n\tossldtls_t *o = (ossldtls_t*)ctx;\n\tBIO_free(o->bio);\n\tSSL_CTX_free(o->ctx);\n\tfree(o);\n}\nstatic neterr_t OSSL_Transmit(void *ctx, const qbyte *data, size_t datasize)\n{\t//we're sending data\n\tossldtls_t *o = (ossldtls_t*)ctx;\n\tint r;\n\tif (datasize == 0)\t//liveness test. return clogged while handshaking and sent when finished. openssl doesn't like 0-byte writes.\n\t{\n\t\tif (o->cert.failure)\n\t\t\treturn NETERR_DISCONNECTED;\t//actual security happens elsewhere.\n\t\telse if (SSL_is_init_finished(o->ssl))\n\t\t\treturn NETERR_SENT;\t//go on, send stuff.\n\t\telse if (BIO_should_retry(o->bio))\n\t\t{\n\t\t\tif (BIO_do_handshake(o->bio) == 1)\n\t\t\t\treturn NETERR_SENT;\n\t\t}\n\t\treturn NETERR_CLOGGED;\t//can't send yet.\n\t}\n\telse\n\t\tr = BIO_write(o->bio, data, datasize);\n\tif (r <= 0)\n\t{\n\t\tif (BIO_should_io_special(o->bio))\n\t\t{\n\t\t\tswitch(BIO_get_retry_reason(o->bio))\n\t\t\t{\n\t\t\t//these are temporary errors, try again later.\n\t\t\tcase BIO_RR_SSL_X509_LOOKUP:\n\t\t\t\treturn NETERR_NOROUTE;\t//certificate failure.\n\t\t\tcase BIO_RR_ACCEPT:\n\t\t\tcase BIO_RR_CONNECT:\n\t\t\t\treturn NETERR_NOROUTE;\t//should never happen\n\t\t\t}\n\t\t}\n\t\tif (BIO_should_retry(o->bio))\n\t\t\treturn NETERR_CLOGGED;\n\t\treturn NETERR_DISCONNECTED;\t//eof or something\n\t}\n\treturn NETERR_SENT;\n}\nstatic neterr_t OSSL_Received(void *ctx, sizebuf_t *message)\n{\t//we have received some encrypted data...\n\tossldtls_t *o = (ossldtls_t*)ctx;\n\tint r;\n\n\tif (!message)\n\t\tr = BIO_read(o->bio, NULL, 0);\n\telse\n\t{\n\t\to->pending = message->data;\n\t\to->pendingsize = message->cursize;\n\t\tr = BIO_read(o->bio, message->data, message->maxsize);\n\t\to->pending = NULL;\n\t\to->pendingsize = 0;\n\t}\n\n\tif (r > 0)\n\t{\n\t\tmessage->cursize = r;\n\t\treturn NETERR_SENT;\n\t}\n\telse\n\t{\n\t\tif (BIO_should_io_special(o->bio))\n\t\t{\n\t\t\tswitch(BIO_get_retry_reason(o->bio))\n\t\t\t{\n\t\t\t//these are temporary errors, try again later.\n\t\t\tcase BIO_RR_SSL_X509_LOOKUP:\n\t\t\t\treturn NETERR_NOROUTE;\t//certificate failure.\n\t\t\tcase BIO_RR_ACCEPT:\n\t\t\tcase BIO_RR_CONNECT:\n\t\t\t\treturn NETERR_NOROUTE;\t//should never happen\n\t\t\t}\n\t\t}\n\t\tif (BIO_should_retry(o->bio))\n\t\t\treturn 0;\n\t\treturn NETERR_DISCONNECTED;\t//eof or something\n\t}\n\treturn NETERR_NOROUTE;\n}\nstatic neterr_t OSSL_Timeouts(void *ctx)\n{\t//keep it ticking over, or something.\n\treturn OSSL_Received(ctx, NULL);\n}\n\nstatic int OSSL_GetPeerCertificate(void *ctx, enum certprops_e prop, char *out, size_t outsize)\n{\n\tossldtls_t *o = (ossldtls_t*)ctx;\n\tX509 *cert;\n\n\tsafeswitch(prop)\n\t{\n\tcase QCERT_ISENCRYPTED:\n\t\treturn 0;\t//not an error.\n\tcase QCERT_PEERCERTIFICATE:\n\t\tcert = SSL_get_peer_certificate(o->ssl);\n\t\tgoto returncert;\n\tcase QCERT_LOCALCERTIFICATE:\n\t\tcert = vhost.servercert;\n\t\tgoto returncert;\nreturncert:\n\t\tif (cert)\n\t\t{\n\t\t\tsize_t blobsize = i2d_X509(cert, NULL);\n\t\t\tqbyte *end = out;\n\t\t\tif (blobsize <= outsize)\n\t\t\t\treturn i2d_X509(cert, &end);\n\t\t\treturn -1;\t//caller didn't pass a big enough buffer.\n\t\t}\n\t\treturn -1;\n\tcase QCERT_PEERSUBJECT:\n\t\t{\n\t\t\tint r;\n\t\t\tX509 *cert = SSL_get_peer_certificate(o->ssl);\n\t\t\tif (cert)\n\t\t\t{\n\t\t\t\tX509_NAME *iname = X509_get_subject_name(cert);\n\t\t\t\tBIO *bio = BIO_new(BIO_s_mem());\n\t\t\t\tX509_NAME_print_ex(bio, iname, 0, XN_FLAG_RFC2253);\n\t\t\t\tr = BIO_read(bio, out, outsize-1);\n\t\t\t\tout[(r<0)?0:r] = 0;\n\t\t\t\tBIO_free(bio);\n\t\t\t\treturn r;\n\t\t\t}\n\t\t}\n\t\treturn -1;\n\tsafedefault:\n\tcase QCERT_LOBBYSTATUS:\t\t//for special-case lobby wrappers.\n\tcase QCERT_LOBBYSENDCHAT:\t//to send chat via the stupid lobby instead of the game itself.\n\t\treturn -1;\n\t}\n}\nstatic qboolean OSSL_GenTempCertificate(const char *subject, struct dtlslocalcred_s *cred)\n{\n\tqbyte *ffs;\n#if OPENSSL_API_LEVEL >= 30000\n\tEVP_PKEY *pkey = EVP_RSA_gen(4096);\n#else\n\tEVP_PKEY*pkey = EVP_PKEY_new();\n\tRSA\t\t*rsa = RSA_new();\n\tBIGNUM\t*pkexponent = BN_new();\n\t//The pseudo-random number generator must be seeded prior to calling RSA_generate_key_ex().\n\tBN_set_word(pkexponent, RSA_F4);\n\tRSA_generate_key_ex(rsa, 2048, pkexponent, NULL);\n\tBN_free(pkexponent);\n\n\tEVP_PKEY_assign_RSA(pkey, rsa);\n#endif\n\tcred->keysize = i2d_PrivateKey(pkey, NULL);\n\tcred->key = ffs = plugfuncs->Malloc(cred->keysize);\n\tcred->keysize = i2d_PrivateKey(pkey, &ffs);\n\n\t{\n\t\tX509 *x509 = X509_new();\n\t\tASN1_INTEGER_set(X509_get_serialNumber(x509), 1);\n\t\tX509_gmtime_adj(X509_get_notBefore(x509), 0);\n\t\tX509_gmtime_adj(X509_get_notAfter(x509), 365*24*60*60);\t//lots of validity\n\t\tX509_set_pubkey(x509, pkey);\n\n\t\t{\n\t\t\tX509_NAME\t*name = X509_get_subject_name(x509);\n\t\t\tX509_NAME_add_entry_by_txt(name, \"CN\", MBSTRING_ASC, (subject?subject:\"localhost\"), -1, -1, 0);\n\t\t\tX509_set_issuer_name(x509, name);\n\t\t}\n\n\t\tX509_sign(x509, pkey, EVP_sha1());\n\n\t\tcred->certsize = i2d_X509(x509, NULL);\n\t\tcred->cert = ffs = plugfuncs->Malloc(cred->certsize);\n\t\tcred->certsize = i2d_X509(x509, &ffs);\n\n\t\tX509_free(x509);\n\t}\n\n\tEVP_PKEY_free(pkey);\t//also frees the rsa pointer.\n\n\treturn true;\n}\n\nstatic dtlsfuncs_t ossl_dtlsfuncs =\n{\n\tOSSL_CreateContext,\n\tOSSL_CheckConnection,\n\tOSSL_DestroyContext,\n\tOSSL_Transmit,\n\tOSSL_Received,\n\tOSSL_Timeouts,\n\tOSSL_GetPeerCertificate,\n\tOSSL_GenTempCertificate,\n};\nstatic const dtlsfuncs_t *OSSL_InitClient(void)\n{\n\tif (OSSL_Init())\n\t\treturn &ossl_dtlsfuncs;\n\treturn NULL;\n}\nstatic const dtlsfuncs_t *OSSL_InitServer(void)\n{\n\tif (OSSL_Init())\n\t\treturn &ossl_dtlsfuncs;\n\treturn NULL;\n}\n\n\n\n\n\n\n\n\n\n\nstatic struct\n{\n\tqboolean inited;\n\tqboolean init_success;\n} ossl;\nstatic qboolean OSSL_Init(void)\n{\n\tif (ossl.inited)\n\t\treturn ossl.init_success;\n\tossl.inited = true;\n#if 0//def LOADERTHREAD\n\tSys_LockMutex(com_resourcemutex);\n\tif (inited)\t//now check again, just in case\n\t{\n\t\tSys_UnlockMutex(com_resourcemutex);\n\t\treturn init_success;\n\t}\n#endif\n\n\tSSL_library_init();\n    SSL_load_error_strings();\n//\tOPENSSL_config(NULL);\n\tERR_print_errors_cb(OSSL_PrintError_CB, NULL);\n\n\tOSSL_OpenPubKey();\n\tOSSL_OpenPrivKey();\n\n\tbiometh_vfs = BIO_meth_new(BIO_get_new_index()|BIO_TYPE_SOURCE_SINK|BIO_TYPE_DESCRIPTOR, \"fte_vfs\");\n\tif (biometh_vfs)\n\t{\n\t\tBIO_meth_set_write(biometh_vfs, OSSL_Bio_FWrite);\n\t\tBIO_meth_set_read(biometh_vfs, OSSL_Bio_FRead);\n\t\tBIO_meth_set_puts(biometh_vfs, OSSL_Bio_FPuts);\t//I cannot see how gets/puts can work with dtls...\n//\t\tBIO_meth_set_gets(biometh_vfs, OSSL_Bio_FGets);\n\t\tBIO_meth_set_ctrl(biometh_vfs, OSSL_Bio_FCtrl);\n\t\tBIO_meth_set_create(biometh_vfs, OSSL_Bio_FCreate);\n\t\tBIO_meth_set_destroy(biometh_vfs, OSSL_Bio_FDestroy);\n\t\tBIO_meth_set_callback_ctrl(biometh_vfs, OSSL_Bio_FOtherCtrl);\n\t\tossl.init_success |= 1;\n\t}\n\n\tbiometh_dtls = BIO_meth_new(BIO_get_new_index()|BIO_TYPE_SOURCE_SINK|BIO_TYPE_DESCRIPTOR, \"fte_dtls\");\n\tif (biometh_dtls)\n\t{\n\t\tBIO_meth_set_write(biometh_dtls, OSSL_Bio_DWrite);\n\t\tBIO_meth_set_read(biometh_dtls, OSSL_Bio_DRead);\n//\t\tBIO_meth_set_puts(biometh_dtls, OSSL_Bio_DPuts);\t//I cannot see how gets/puts can work with dtls...\n//\t\tBIO_meth_set_gets(biometh_dtls, OSSL_Bio_DGets);\n\t\tBIO_meth_set_ctrl(biometh_dtls, OSSL_Bio_DCtrl);\n\t\tBIO_meth_set_create(biometh_dtls, OSSL_Bio_DCreate);\n\t\tBIO_meth_set_destroy(biometh_dtls, OSSL_Bio_DDestroy);\n\t\tBIO_meth_set_callback_ctrl(biometh_dtls, OSSL_Bio_DOtherCtrl);\n\t\tossl.init_success |= 2;\n\t}\n\n\tossl_fte_certctx = SSL_get_ex_new_index(0, \"ossl_fte_certctx\", NULL, NULL, NULL);\n\n#if 0//def LOADERTHREAD\n\tSys_UnlockMutex(com_resourcemutex);\n#endif\n\treturn ossl.init_success;\n}\n\nstatic enum hashvalidation_e OSSL_VerifyHash(const qbyte *hashdata, size_t hashsize, const qbyte *pubkeydata, size_t pubkeysize, const qbyte *signdata, size_t signsize)\n{\t//accepts either DER or PEM certs\n\tint result = VH_UNSUPPORTED;\n\tBIO *bio_pubkey = BIO_new_mem_buf(pubkeydata, pubkeysize);\n\tif (bio_pubkey)\n\t{\n\t\tX509 *x509_pubkey = PEM_read_bio_X509(bio_pubkey, NULL, NULL, NULL);\n\t\tif (x509_pubkey)\n\t\t{\n\t\t\tEVP_PKEY *evp_pubkey = X509_get_pubkey(x509_pubkey);\n\t\t\tif (evp_pubkey)\n\t\t\t{\n\t\t\t\tEVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new(evp_pubkey, NULL);\n\t\t\t\tif (ctx)\n\t\t\t\t{\n\t\t\t\t\tEVP_PKEY_verify_init(ctx);\n\t\t\t\t\tEVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_PADDING);\n\t\t\t\t\tEVP_PKEY_CTX_set_signature_md(ctx, EVP_sha512());\n\t\t\t\t\tif (1 == EVP_PKEY_verify(ctx, signdata, signsize, hashdata, hashsize))\n\t\t\t\t\t\tresult = VH_CORRECT;\n\t\t\t\t\telse\n\t\t\t\t\t\tresult = VH_INCORRECT;\n\t\t\t\t\tEVP_PKEY_CTX_free(ctx);\n\t\t\t\t}\n\t\t\t\tEVP_PKEY_free(evp_pubkey);\n\t\t\t}\n\t\t\tX509_free(x509_pubkey);\n\t\t}\n\t\tBIO_free(bio_pubkey);\n\t}\n\treturn result;\n}\n\nstatic ftecrypto_t crypto_openssl =\n{\n\t\"OpenSSL\",\n\tOSSL_OpenVFS,\n\tNULL,//OSSL_GetChannelBinding,\n\tOSSL_InitClient,\n\tOSSL_InitServer,\n\tOSSL_VerifyHash,\n\tNULL,\n};\n\nstatic void OSSL_PluginShutdown(void)\n{\n\tossl.inited = false;\n\tossl.init_success = false;\n\n\tX509_free(vhost.servercert);\n\tEVP_PKEY_free(vhost.privatekey);\n\tBIO_meth_free(biometh_vfs);\n\tBIO_meth_free(biometh_dtls);\n\n\tmemset(&vhost, 0, sizeof(vhost));\n}\nstatic qboolean OSSL_PluginMayShutdown(void)\n{\n\t//the engine has a habit of holding on to handles without any refcounts, so don't allow it to die early.\n\treturn false;\n}\n\nqboolean Plug_Init(void)\n{\n\tfsfuncs = plugfuncs->GetEngineInterface(plugfsfuncs_name, sizeof(*fsfuncs));\n\tnetfuncs = plugfuncs->GetEngineInterface(plugnetfuncs_name, sizeof(*netfuncs));\n\tif (!fsfuncs || !netfuncs)\n\t\treturn false;\n\n\tplugfuncs->ExportFunction(\"Shutdown\", OSSL_PluginShutdown);\n\tplugfuncs->ExportFunction(\"MayUnload\", OSSL_PluginMayShutdown);\n\n\tpdtls_psk_hint = cvarfuncs->GetNVFDG(\"dtls_psk_hint\", \"\", 0, NULL, \"DTLS stuff\");\n\tpdtls_psk_user = cvarfuncs->GetNVFDG(\"dtls_psk_user\", \"\", 0, NULL, \"DTLS stuff\");\n\tpdtls_psk_key  = cvarfuncs->GetNVFDG(\"dtls_psk_key\",  \"\", 0, NULL, \"DTLS stuff\");\n\tnetfuncs->RandomBytes(dtlscookiekey, sizeof(dtlscookiekey));\t//something random so people can't guess cookies for arbitrary victim IPs.\n\n\tOSSL_Init();\t//shoving this here solves threading issues (eg two loader threads racing to open an https image url)\n\n\treturn plugfuncs->ExportInterface(\"Crypto\", &crypto_openssl, sizeof(crypto_openssl)); //export a named interface struct to the engine\n}"
  },
  {
    "path": "plugins/openxr.c",
    "content": "#include \"plugin.h\"\nstatic plugfsfuncs_t *fsfuncs;\nstatic pluginputfuncs_t *inputfuncs;\n\n#include \"../engine/client/vr.h\"\n\n//#define XR_NO_PROTOTYPES\n\n//figure out which platforms(read: windowing apis) we need...\n#if defined(_WIN32)\n\t#define XR_USE_PLATFORM_WIN32\n//#elif defined(ANDROID)\n//\t#define XR_USE_PLATFORM_ANDROID\n#else\n\t#ifndef NO_X11\n\t\t#define XR_USE_PLATFORM_XLIB\n\t#endif\n\t#if defined(GLQUAKE) && defined(USE_EGL)\n\t\t//wayland, android, and x11-egl can all just use the EGL path...\n\t\t//at least once the openxr spec gets fixed (the wayland extension is apparently basically unusable)\n\t\t//note: XR_MND_egl_enable is a vendor extension to work around openxr stupidly trying to ignore it entirely.\n\t\t#define XR_USE_PLATFORM_EGL\n\t#endif\n#endif\n\n//figure out which graphics apis we need...\n#ifdef GLQUAKE\n\t#define XR_USE_GRAPHICS_API_OPENGL\n#endif\n#ifdef VKQUAKE\n\t#define XR_USE_GRAPHICS_API_VULKAN\n#endif\n#ifdef D3D11QUAKE\n\t#ifdef _WIN32\n\t\t#define XR_USE_GRAPHICS_API_D3D11\n\t#endif\n#endif\n\n//include any headers we need for things to make sense.\n#ifdef XR_USE_GRAPHICS_API_OPENGL\n\t#include \"glquake.h\"\n#endif\n#ifdef XR_USE_GRAPHICS_API_VULKAN\n\t#include \"../engine/vk/vkrenderer.h\"\n#endif\n#ifdef XR_USE_GRAPHICS_API_D3D11\n\t#include <d3d11.h>\n#endif\n#ifdef XR_USE_PLATFORM_EGL\n\t#include \"gl_videgl.h\"\n#endif\n#ifdef XR_USE_PLATFORM_XLIB\n\t#include <GL/glx.h>\n#endif\n\n//and finally include openxr stuff now that its hopefully not going to fail about missing typedefs.\n#include <openxr/openxr_platform.h>\n#if XR_CURRENT_API_VERSION < XR_MAKE_VERSION(1, 0, 16)\n#define XR_ERROR_RUNTIME_UNAVAILABLE -51\t//available starting 1.0.16\n#endif\n\n#ifdef XR_NO_PROTOTYPES\n#define XRFUNCS\t\t\\\n\t\tXRFUNC(xrGetInstanceProcAddr)\t\\\n\t\tXRFUNC(xrResultToString)\t\\\n\t\tXRFUNC(xrEnumerateApiLayerProperties) \\\n\t\tXRFUNC(xrEnumerateInstanceExtensionProperties)\t\\\n\t\tXRFUNC(xrCreateInstance)\t\\\n\t\tXRFUNC(xrGetInstanceProperties)\t\\\n\t\tXRFUNC(xrGetSystem)\t\\\n\t\tXRFUNC(xrGetSystemProperties)\t\\\n\t\tXRFUNC(xrEnumerateViewConfigurations) \\\n\t\tXRFUNC(xrEnumerateViewConfigurationViews)\t\\\n\t\tXRFUNC(xrCreateSession)\t\\\n\t\tXRFUNC(xrCreateReferenceSpace)\t\\\n\t\tXRFUNC(xrCreateActionSet)\t\\\n\t\tXRFUNC(xrStringToPath)\t\\\n\t\tXRFUNC(xrCreateAction)\t\\\n\t\tXRFUNC(xrSuggestInteractionProfileBindings)\t\\\n\t\tXRFUNC(xrCreateActionSpace)\t\\\n\t\tXRFUNC(xrAttachSessionActionSets)\t\\\n\t\tXRFUNC(xrSyncActions)\t\\\n\t\tXRFUNC(xrGetActionStatePose)\t\\\n\t\tXRFUNC(xrApplyHapticFeedback)\t\\\n\t\tXRFUNC(xrLocateSpace)\t\\\n\t\tXRFUNC(xrGetActionStateBoolean)\t\\\n\t\tXRFUNC(xrGetActionStateFloat)\t\\\n\t\tXRFUNC(xrGetActionStateVector2f)\t\\\n\t\tXRFUNC(xrGetCurrentInteractionProfile)\t\\\n\t\tXRFUNC(xrEnumerateBoundSourcesForAction)\t\\\n\t\tXRFUNC(xrGetInputSourceLocalizedName)\t\\\n\t\tXRFUNC(xrPathToString)\t\\\n\t\tXRFUNC(xrCreateSwapchain)\t\\\n\t\tXRFUNC(xrEnumerateSwapchainFormats)\t\\\n\t\tXRFUNC(xrEnumerateSwapchainImages)\t\\\n\t\tXRFUNC(xrPollEvent)\t\\\n\t\tXRFUNC(xrBeginSession)\t\\\n\t\tXRFUNC(xrWaitFrame)\t\\\n\t\tXRFUNC(xrBeginFrame)\t\\\n\t\tXRFUNC(xrLocateViews)\t\\\n\t\tXRFUNC(xrAcquireSwapchainImage)\t\\\n\t\tXRFUNC(xrWaitSwapchainImage)\t\\\n\t\tXRFUNC(xrReleaseSwapchainImage)\t\\\n\t\tXRFUNC(xrEndFrame)\t\\\n\t\tXRFUNC(xrRequestExitSession)\t\\\n\t\tXRFUNC(xrEndSession)\t\\\n\t\tXRFUNC(xrDestroySwapchain)\t\\\n\t\tXRFUNC(xrDestroySpace)\t\\\n\t\tXRFUNC(xrDestroySession)\t\\\n\t\tXRFUNC(xrDestroyInstance)\n#define XRFUNC(n) static PFN_##n n;\nXRFUNCS\n#undef XRFUNC\n#endif\n\n#ifdef XR_EXT_hand_tracking\nstatic PFN_xrCreateHandTrackerEXT\txrCreateHandTrackerEXT;\nstatic PFN_xrLocateHandJointsEXT\txrLocateHandJointsEXT;\nstatic PFN_xrDestroyHandTrackerEXT\txrDestroyHandTrackerEXT;\n#endif\n\n\n#ifdef SVNREVISION\n\t#define APPLICATIONVERSION atoi(STRINGIFY(SVNREVISION))\n\t#define ENGINEVERSION atoi(STRINGIFY(SVNREVISION))\n#else\n\t#define APPLICATIONVERSION 0\n\t#define ENGINEVERSION 0\n#endif\n\nstatic cvar_t *xr_enable;\nstatic cvar_t *xr_debug;\nstatic cvar_t *xr_formfactor;\nstatic cvar_t *xr_viewconfig;\nstatic cvar_t *xr_metresize;\nstatic cvar_t *xr_skipregularview;\nstatic cvar_t *xr_fingertracking;\n\nstatic void XR_SetupInputs_Instance(void);\n\n#define METRES_TO_QUAKE(x) ((x)*xr_metresize->value)\n#define QUAKE_TO_METRES(x) ((x)/xr_metresize->value)\n\n#define SECONDS_TO_NANOSECONDS(x) ((x)*1000000000)\n#define NANOSECONDS_TO_SECONDS(x) ((x)/1000000000.0)\n\nstatic void XR_PoseToAngOrg(const XrPosef *pose, vec3_t ang, vec3_t org)\n{\n\tXrQuaternionf q = pose->orientation;\n    const float sqw = q.w * q.w;\n    const float sqx = q.x * q.x;\n    const float sqy = q.y * q.y;\n    const float sqz = q.z * q.z;\n\n    ang[PITCH] = -asin(-2 * (q.y * q.z - q.w * q.x)) * (180/M_PI);\n    ang[YAW] = atan2(2 * (q.x * q.z + q.w * q.y), sqw - sqx - sqy + sqz) * (180/M_PI);\n    ang[ROLL] = -atan2(2 * (q.x * q.y + q.w * q.z), sqw - sqx + sqy - sqz) * (180/M_PI);\n\n#if 1\n\torg[0]  =     METRES_TO_QUAKE(-pose->position.z);\n\torg[1]  =     METRES_TO_QUAKE(-pose->position.x);\n\torg[2]  =     METRES_TO_QUAKE(pose->position.y);\n#else\n\torg[0]  =     METRES_TO_QUAKE(pose->position.x);\n\torg[1]  =     METRES_TO_QUAKE(pose->position.y);\n\torg[2]  =     METRES_TO_QUAKE(pose->position.z);\n#endif\n}\nstatic void XR_PoseToMat12(const XrPosef *pose, float *out)\n{\n\tvec3_t angles, origin;\n\tfloat\t\tangle;\n\tfloat\t\tsr, sp, sy, cr, cp, cy;\n\n\tXR_PoseToAngOrg(pose, angles, origin);\n\n\tangle = angles[YAW] * (M_PI*2 / 360);\n\tsy = sin(angle);\n\tcy = cos(angle);\n\tangle = angles[PITCH] * (M_PI*2 / 360);\n\tsp = sin(angle);\n\tcp = cos(angle);\n\tangle = angles[ROLL] * (M_PI*2 / 360);\n\tsr = sin(angle);\n\tcr = cos(angle);\n\n\tout[0] = cp*cy;\n\tout[1] = (sr*sp*cy+cr*-sy);\n\tout[2] = (cr*sp*cy+-sr*-sy);\n\tout[3] = origin[0];\n\n\tout[4] = cp*sy;\n\tout[5] = (sr*sp*sy+cr*cy);\n\tout[6] = (cr*sp*sy+-sr*cy);\n\tout[7] = origin[1];\n\n\tout[8] = -sp;\n\tout[9] = sr*cp;\n\tout[10] = cr*cp;\n\tout[11] = origin[2];\n}\n\nstatic void XR_Matrix3x4_RM_FromAngles(const vec3_t angles, const vec3_t origin, float *out)\n{\n\tfloat\t\tangle;\n\tfloat\t\tsr, sp, sy, cr, cp, cy;\n\n\tangle = angles[YAW] * (M_PI*2 / 360);\n\tsy = sin(angle);\n\tcy = cos(angle);\n\tangle = angles[PITCH] * (M_PI*2 / 360);\n\tsp = sin(angle);\n\tcp = cos(angle);\n\tangle = angles[ROLL] * (M_PI*2 / 360);\n\tsr = sin(angle);\n\tcr = cos(angle);\n\n\tout[0] = cp*cy;\n\tout[1] = (sr*sp*cy+cr*-sy);\n\tout[2] = (cr*sp*cy+-sr*-sy);\n\tout[3] = origin[0];\n\n\tout[4] = cp*sy;\n\tout[5] = (sr*sp*sy+cr*cy);\n\tout[6] = (cr*sp*sy+-sr*cy);\n\tout[7] = origin[1];\n\n\tout[8] = -sp;\n\tout[9] = sr*cp;\n\tout[10] = cr*cp;\n\tout[11] = origin[2];\n}\n\nenum actset_e\n{\n\tAS_COMMON,\n\tAS_MENU,\n\tAS_GAME,\n\tMAX_ACTIONSETS\n};\n\nstatic struct\n{\n//instance state (in case we want to start up)\n\tXrInstance instance;\t//loader context\n\tXrSystemId systemid;\t//device type thingie we're going for\n#define MAX_VIEW_COUNT 12\t//kinda abusive, but that's VR for you.\n\tunsigned int viewcount;\n\tXrViewConfigurationView *views;\n\tXrViewConfigurationType viewtype;\n\n//engine context info (for restarting sessions)\n\tint renderer;\t\t\t//rendering api we're using\n\tvoid *bindinginfo;\t\t//appropriate XrGraphicsBinding*KHR struct so we can restart sessions.\n\n//session state\n\tqboolean fake;\n\tXrSession session;\t\t//driver context\n\tXrSessionState state;\n\tqboolean beginning;\n\tqboolean ending;\n\tXrSpace space;\n\tstruct\n\t{\t//basically just swapchain state.\n\t\tXrSwapchain swapchain;\n\t\tunsigned int numswapimages;\n\t\tXrSwapchainSubImage subimage;\n\t\timage_t *swapimages;\n\t} eye[MAX_VIEW_COUNT];\t//note that eye is a vauge term.\n\n\tXrActiveActionSet actionset[MAX_ACTIONSETS];\n\n\tqboolean timeknown;\n\tXrTime time;\n\tXrFrameState framestate;\n\tqboolean needrender;\t//we MUST call xrBegin before the next xrWait\n\tint srgb;\t//<0 = gamma-only. 0 = no srgb at all, >0 full srgb, including textures and stuff\n\tint colourformat;\n\n\tunsigned int numactions;\n\tunsigned int maxactions;\n\tstruct\n\t{\n\t\tenum actset_e set;\n\t\tXrActionType acttype;\n\t\tconst char *actname;\t\t//doubles up as command names for buttons\n\t\tconst char *actdescription;\t//user-visible string (exposed via openxr runtime somehow)\n\t\tconst char *subactionpath;\t//somethingblahblah\n\n\t\tXrAction\taction;\t//for querying.\n\t\tXrPath\t\tpath;\t//for querying.\n\t\tXrSpace\t\tspace;\t//for poses.\n\t\tqboolean\theld;\t//for buttons.\n\t} *actions;\n\tqboolean inputsdirty;\t//mostly for printing them.\n\n#ifdef XR_EXT_hand_tracking\n\tstruct\n\t{\n\t\tXrHandTrackerEXT handle;\n\t\tqboolean active;\n\t\tXrHandJointLocationEXT jointloc[XR_HAND_JOINT_COUNT_EXT];\n\t\tXrHandJointVelocityEXT jointvel[XR_HAND_JOINT_COUNT_EXT];\n\t} hand[2];\n#endif\n} xr;\n\nstatic qboolean QDECL XR_PluginMayUnload(void)\n{\n\tif (xr.instance)\n\t\treturn false;\t//something is still using us... don't let our code go away.\n\treturn true;\n}\nstatic void XR_SessionEnded(void)\n{\n\tsize_t u;\n\tif (xr.space)\n\t{\n\t\txrDestroySpace(xr.space);\n\t\txr.space = XR_NULL_HANDLE;\n\t}\n\n\tfor (u = 0; u < countof(xr.eye); u++)\n\t{\n\t\tfree(xr.eye[u].swapimages);\n\t\txr.eye[u].swapimages = NULL;\n\t\txr.eye[u].numswapimages = 0;\n\t\tif (xr.eye[u].swapchain)\n\t\t{\n\t\t\txrDestroySwapchain(xr.eye[u].swapchain);\n\t\t\txr.eye[u].swapchain = XR_NULL_HANDLE;\n\t\t}\n\t}\n\n#ifdef XR_EXT_hand_tracking\n\tfor (u = 0; u < countof(xr.hand); u++)\n\t{\n\t\tif (xr.hand[u].handle)\n\t\t\txrDestroyHandTrackerEXT(xr.hand[u].handle);\n\t\txr.hand[u].handle = XR_NULL_HANDLE;\n\t\txr.hand[u].active = false;\n\t}\n#endif\n\n\tif (xr.session)\n\t{\n\t\txrDestroySession(xr.session);\n\t\txr.session = XR_NULL_HANDLE;\n\t}\n\txr.state = XR_SESSION_STATE_UNKNOWN;\n\txr.beginning = false;\n}\nstatic void XR_Shutdown(void)\n{\t//called on any kind of failure\n\tXR_SessionEnded();\n\n\tfree(xr.bindinginfo);\n\tfree(xr.views);\n\tif (xr.instance)\n\t\txrDestroyInstance(xr.instance);\n\n\tmemset(&xr, 0, sizeof(xr));\n}\n\nstatic const char *XR_StringForResult(XrResult res)\n{\n#if 0\n\t//this is a bit of a joke really. xrResultToString requires a valid instance so is unusable for printing out the various reasons why we might fail to create an instance.\n\tstatic char buffer[XR_MAX_RESULT_STRING_SIZE];\n\tif (XR_SUCCEEDED(res=xrResultToString(xr.instance, res, buffer)))\n\t\treturn buffer;\n\treturn va(\"XrResult %i\", res);\n#else\n\tswitch(res)\n\t{\n\tcase XR_SUCCESS: return \"XR_SUCCESS\";\n\tcase XR_TIMEOUT_EXPIRED: return \"XR_TIMEOUT_EXPIRED\";\n\tcase XR_SESSION_LOSS_PENDING: return \"XR_SESSION_LOSS_PENDING\";\n\tcase XR_EVENT_UNAVAILABLE: return \"XR_EVENT_UNAVAILABLE\";\n\tcase XR_SPACE_BOUNDS_UNAVAILABLE: return \"XR_SPACE_BOUNDS_UNAVAILABLE\";\n\tcase XR_SESSION_NOT_FOCUSED: return \"XR_SESSION_NOT_FOCUSED\";\n\tcase XR_FRAME_DISCARDED: return \"XR_FRAME_DISCARDED\";\n\tcase XR_ERROR_VALIDATION_FAILURE: return \"XR_ERROR_VALIDATION_FAILURE\";\n\tcase XR_ERROR_RUNTIME_FAILURE: return \"XR_ERROR_RUNTIME_FAILURE\";\n\tcase XR_ERROR_OUT_OF_MEMORY: return \"XR_ERROR_OUT_OF_MEMORY\";\n\tcase XR_ERROR_API_VERSION_UNSUPPORTED: return \"XR_ERROR_API_VERSION_UNSUPPORTED\";\n\tcase XR_ERROR_INITIALIZATION_FAILED: return \"XR_ERROR_INITIALIZATION_FAILED\";\n\tcase XR_ERROR_FUNCTION_UNSUPPORTED: return \"XR_ERROR_FUNCTION_UNSUPPORTED\";\n\tcase XR_ERROR_FEATURE_UNSUPPORTED: return \"XR_ERROR_FEATURE_UNSUPPORTED\";\n\tcase XR_ERROR_EXTENSION_NOT_PRESENT: return \"XR_ERROR_EXTENSION_NOT_PRESENT\";\n\tcase XR_ERROR_LIMIT_REACHED: return \"XR_ERROR_LIMIT_REACHED\";\n\tcase XR_ERROR_SIZE_INSUFFICIENT: return \"XR_ERROR_SIZE_INSUFFICIENT\";\n\tcase XR_ERROR_HANDLE_INVALID: return \"XR_ERROR_HANDLE_INVALID\";\n\tcase XR_ERROR_INSTANCE_LOST: return \"XR_ERROR_INSTANCE_LOST\";\n\tcase XR_ERROR_SESSION_RUNNING: return \"XR_ERROR_SESSION_RUNNING\";\n\tcase XR_ERROR_SESSION_NOT_RUNNING: return \"XR_ERROR_SESSION_NOT_RUNNING\";\n\tcase XR_ERROR_SESSION_LOST: return \"XR_ERROR_SESSION_LOST\";\n\tcase XR_ERROR_SYSTEM_INVALID: return \"XR_ERROR_SYSTEM_INVALID\";\n\tcase XR_ERROR_PATH_INVALID: return \"XR_ERROR_PATH_INVALID\";\n\tcase XR_ERROR_PATH_COUNT_EXCEEDED: return \"XR_ERROR_PATH_COUNT_EXCEEDED\";\n\tcase XR_ERROR_PATH_FORMAT_INVALID: return \"XR_ERROR_PATH_FORMAT_INVALID\";\n\tcase XR_ERROR_PATH_UNSUPPORTED: return \"XR_ERROR_PATH_UNSUPPORTED\";\n\tcase XR_ERROR_LAYER_INVALID: return \"XR_ERROR_LAYER_INVALID\";\n\tcase XR_ERROR_LAYER_LIMIT_EXCEEDED: return \"XR_ERROR_LAYER_LIMIT_EXCEEDED\";\n\tcase XR_ERROR_SWAPCHAIN_RECT_INVALID: return \"XR_ERROR_SWAPCHAIN_RECT_INVALID\";\n\tcase XR_ERROR_SWAPCHAIN_FORMAT_UNSUPPORTED: return \"XR_ERROR_SWAPCHAIN_FORMAT_UNSUPPORTED\";\n\tcase XR_ERROR_ACTION_TYPE_MISMATCH: return \"XR_ERROR_ACTION_TYPE_MISMATCH\";\n\tcase XR_ERROR_SESSION_NOT_READY: return \"XR_ERROR_SESSION_NOT_READY\";\n\tcase XR_ERROR_SESSION_NOT_STOPPING: return \"XR_ERROR_SESSION_NOT_STOPPING\";\n\tcase XR_ERROR_TIME_INVALID: return \"XR_ERROR_TIME_INVALID\";\n\tcase XR_ERROR_REFERENCE_SPACE_UNSUPPORTED: return \"XR_ERROR_REFERENCE_SPACE_UNSUPPORTED\";\n\tcase XR_ERROR_FILE_ACCESS_ERROR: return \"XR_ERROR_FILE_ACCESS_ERROR\";\n\tcase XR_ERROR_FILE_CONTENTS_INVALID: return \"XR_ERROR_FILE_CONTENTS_INVALID\";\n\tcase XR_ERROR_FORM_FACTOR_UNSUPPORTED: return \"XR_ERROR_FORM_FACTOR_UNSUPPORTED\";\n\tcase XR_ERROR_FORM_FACTOR_UNAVAILABLE: return \"XR_ERROR_FORM_FACTOR_UNAVAILABLE\";\n\tcase XR_ERROR_API_LAYER_NOT_PRESENT: return \"XR_ERROR_API_LAYER_NOT_PRESENT\";\n\tcase XR_ERROR_CALL_ORDER_INVALID: return \"XR_ERROR_CALL_ORDER_INVALID\";\n\tcase XR_ERROR_GRAPHICS_DEVICE_INVALID: return \"XR_ERROR_GRAPHICS_DEVICE_INVALID\";\n\tcase XR_ERROR_POSE_INVALID: return \"XR_ERROR_POSE_INVALID\";\n\tcase XR_ERROR_INDEX_OUT_OF_RANGE: return \"XR_ERROR_INDEX_OUT_OF_RANGE\";\n\tcase XR_ERROR_VIEW_CONFIGURATION_TYPE_UNSUPPORTED: return \"XR_ERROR_VIEW_CONFIGURATION_TYPE_UNSUPPORTED\";\n\tcase XR_ERROR_ENVIRONMENT_BLEND_MODE_UNSUPPORTED: return \"XR_ERROR_ENVIRONMENT_BLEND_MODE_UNSUPPORTED\";\n\tcase XR_ERROR_NAME_DUPLICATED: return \"XR_ERROR_NAME_DUPLICATED\";\n\tcase XR_ERROR_NAME_INVALID: return \"XR_ERROR_NAME_INVALID\";\n\tcase XR_ERROR_ACTIONSET_NOT_ATTACHED: return \"XR_ERROR_ACTIONSET_NOT_ATTACHED\";\n\tcase XR_ERROR_ACTIONSETS_ALREADY_ATTACHED: return \"XR_ERROR_ACTIONSETS_ALREADY_ATTACHED\";\n\tcase XR_ERROR_LOCALIZED_NAME_DUPLICATED: return \"XR_ERROR_LOCALIZED_NAME_DUPLICATED\";\n\tcase XR_ERROR_LOCALIZED_NAME_INVALID: return \"XR_ERROR_LOCALIZED_NAME_INVALID\";\n#if XR_CURRENT_API_VERSION >= XR_MAKE_VERSION(1, 0, 16)\n\tcase XR_ERROR_RUNTIME_UNAVAILABLE: return \"XR_ERROR_RUNTIME_UNAVAILABLE\";\n#endif\n\tdefault:\n\t\treturn va(\"XrResult %i\", res);\n\t}\n#endif\n}\n\n\nstatic XrBool32 XRAPI_CALL XR_DebugPrint(XrDebugUtilsMessageSeverityFlagsEXT messageSeverity, XrDebugUtilsMessageTypeFlagsEXT messageTypes, const XrDebugUtilsMessengerCallbackDataEXT *callbackData, void *userData)\n{\n\tchar *sev;\n\tswitch(messageSeverity)\n\t{\n\tcase 1/*XR_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT*/:\t\tsev = \"^8\";\t\t\tbreak;\n\tcase 16/*XR_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT*/:\t\tsev = \"\";\t\t\tbreak;\n\tcase 256/*XR_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT*/:\tsev = CON_WARNING;\tbreak;\n\tdefault:\n\tcase 4096/*XR_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT*/:\t\tsev = CON_ERROR;\tbreak;\n\t}\n\n\tCon_Printf(\"%s%s: %s\\n\", sev, callbackData->functionName, callbackData->message);\n\treturn XR_FALSE;\n}\n\nstatic qboolean XR_PreInit(vrsetup_t *qreqs)\n{\n\tXrResult res;\n\tconst char *xrexts[8], *ext;\n\tconst char *xrlayers[8];\n\tuint32_t numext = 0, numlayers = 0;\n\tqboolean havedebugutils = false;\n#ifdef XR_EXT_hand_tracking\n\tqboolean havehandtrack = false;\n#endif\n\n\tXR_Shutdown();\t//just in case...\n\n\tif (qreqs->structsize != sizeof(*qreqs) || xr_enable->ival < 0)\n\t\treturn false;\t//nope, get lost.\n\tif (!strncasecmp(xr_formfactor->string, \"none\", 4))\n\t\tqreqs->vrplatform = VR_HEADLESS;\n\tif (!strncasecmp(xr_formfactor->string, \"fake\", 4))\n\t{\n\t\txr.fake = true;\n\t\treturn true;\n\t}\n\tswitch(qreqs->vrplatform)\n\t{\n#ifdef XR_MND_HEADLESS_EXTENSION_NAME\n\tcase VR_HEADLESS:\n\t\text = XR_MND_HEADLESS_EXTENSION_NAME;\n\t\tbreak;\n#endif\n#ifdef XR_USE_GRAPHICS_API_VULKAN\n\tcase VR_VULKAN:\n\t\text = XR_KHR_VULKAN_ENABLE_EXTENSION_NAME;\n\t\tbreak;\n#endif\n#ifdef XR_USE_GRAPHICS_API_OPENGL\n#ifdef XR_MND_EGL_ENABLE_EXTENSION_NAME\n\tcase VR_EGL:\n\t\text = XR_MND_EGL_ENABLE_EXTENSION_NAME;\n\t\tbreak;\n#elif defined(XR_MNDX_EGL_ENABLE_EXTENSION_NAME)\n\tcase VR_EGL:\n\t\text = XR_MNDX_EGL_ENABLE_EXTENSION_NAME;\n\t\tbreak;\n#endif\n#ifdef XR_USE_PLATFORM_XLIB\n\tcase VR_X11_GLX:\n#endif\n#ifdef XR_USE_PLATFORM_WIN32\n\tcase VR_WIN_WGL:\n#endif\n\t\text = XR_KHR_OPENGL_ENABLE_EXTENSION_NAME;\n\t\tbreak;\n#endif\n#ifdef XR_USE_GRAPHICS_API_D3D11\n\tcase VR_D3D11:\n\t\text = XR_KHR_D3D11_ENABLE_EXTENSION_NAME;\n\t\tbreak;\n#endif\n\tdefault:\n\t\tCon_Printf(CON_ERROR\"OpenXR: windowing-api or rendering-api not supported\\n\");\n\t\treturn false;\n\t}\n\n\txr.instance = XR_NULL_HANDLE;\n\n\tif (xr_debug->ival)\n\t{\n\t\tuint32_t count, u;\n\t\tstruct XrApiLayerProperties *props;\n\t\tif (XR_SUCCEEDED(xrEnumerateApiLayerProperties(0, &count, NULL)))\n\t\t{\n\t\t\tprops = calloc(count, sizeof(*props));\n\t\t\tfor (u = 0; u < count; u++)\n\t\t\t\tprops[u].type = XR_TYPE_API_LAYER_PROPERTIES;\n\t\t\txrEnumerateApiLayerProperties(count, &count, props);\n\t\t\tfor (u = 0; u < count; u++)\n\t\t\t\tCon_Printf(\"OpenXR Layer %s: %s\\n\", props[u].layerName, props[u].description);\n\n\t\t\tif (xr_debug->ival>1)\n\t\t\t{\n\t\t\t\tfor (u = 0; u < count; u++)\n\t\t\t\t\tif (!strcmp(props[u].layerName, \"XR_APILAYER_LUNARG_core_validation\"))\n\t\t\t\t\t{\n\t\t\t\t\t\txrlayers[numlayers++] = \"XR_APILAYER_LUNARG_core_validation\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\tif (u==count)\n\t\t\t\t\tCon_Printf(\"OpenXR: Validation layers not found\\n\");\n\t\t\t}\n\t\t\tfree(props);\n\t\t}\n\t}\n\n\t{\n\t\tunsigned int exts = 0, u=0;\n\t\tXrExtensionProperties *extlist;\n\t\tres = xrEnumerateInstanceExtensionProperties(NULL, 0, &exts, NULL);\n\t\tif (res == XR_ERROR_RUNTIME_UNAVAILABLE || res == XR_ERROR_INSTANCE_LOST)\n\t\t{\n\t\t\tCon_Printf(CON_WARNING\"OpenXR: no runtime installed\\n\");\n\t\t\treturn false;\n\t\t}\n\t\telse if (XR_SUCCEEDED(res))\n\t\t{\n\t\t\textlist = calloc(exts, sizeof(*extlist));\n\t\t\tfor (u = 0; u < exts; u++)\n\t\t\t\textlist[u].type = XR_TYPE_EXTENSION_PROPERTIES;\n\t\t\txrEnumerateInstanceExtensionProperties(NULL, exts, &exts, extlist);\n\n\t\t\t//print a list of them all, if we can.\n\t\t\tif (xr_debug->ival)\n\t\t\t{\n\t\t\t\tCon_DPrintf(\"OpenXR:\\n\");\n\t\t\t\tfor (u = 0; u < exts; u++)\n\t\t\t\t\tCon_DPrintf(\"\\t%s\\n\", extlist[u].extensionName);\n\t\t\t}\n\n\t\t\t//make sure we have an appropriate extension for the API we're using.\n\t\t\tif (ext)\n\t\t\t{\n\t\t\t\tfor (u = 0; u < exts; u++)\n\t\t\t\t\tif (!strcmp(extlist[u].extensionName, ext))\n\t\t\t\t\t\tbreak;\n\t\t\t\tif (u == exts)\n\t\t\t\t{\n\t\t\t\t\tCon_Printf(CON_ERROR\"OpenXR: instance driver does not support required %s\\n\", ext);\n\t\t\t\t\tfree(extlist);\n\t\t\t\t\treturn false;\t//would just give an error on xrCreateInstance anyway.\n\t\t\t\t}\n\n\t\t\t\txrexts[numext++] = ext;\n\t\t\t}\n\n\t\t\t//look for some interesting extensions\n\t\t\tfor (u = 0; u < exts; u++)\n\t\t\t{\n\t\t\t\tif (!strcmp(extlist[u].extensionName, XR_EXT_DEBUG_UTILS_EXTENSION_NAME) && !havedebugutils && xr_debug->ival)\n\t\t\t\t\thavedebugutils = true, xrexts[numext++] = XR_EXT_DEBUG_UTILS_EXTENSION_NAME;\n\n#ifdef XR_EXT_hand_tracking\n\t\t\t\tif (!strcmp(extlist[u].extensionName, XR_EXT_HAND_TRACKING_EXTENSION_NAME) && !havehandtrack && xr_fingertracking->ival)\n\t\t\t\t\thavehandtrack = true, xrexts[numext++] = XR_EXT_HAND_TRACKING_EXTENSION_NAME;\n#endif\n\t\t\t}\n\n\t\t\tfree(extlist);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tCon_Printf(CON_ERROR\"OpenXR: xrEnumerateInstanceExtensionProperties failed (%s)\\n\", XR_StringForResult(res));\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t//create our instance\n\t{\n\t\tXrInstanceCreateInfo createinfo = {XR_TYPE_INSTANCE_CREATE_INFO};\n\t\tcreateinfo.createFlags = 0;\n\t\tQ_strlcpy(createinfo.applicationInfo.applicationName, FULLENGINENAME, sizeof(createinfo.applicationInfo.applicationName));\n\t\tcreateinfo.applicationInfo.applicationVersion = APPLICATIONVERSION;\n\t\tQ_strlcpy(createinfo.applicationInfo.engineName, \"FTEQW\", sizeof(createinfo.applicationInfo.engineName));\n\t\tcreateinfo.applicationInfo.engineVersion = ENGINEVERSION;\n\t\tcreateinfo.applicationInfo.apiVersion = XR_CURRENT_API_VERSION;\n\t\tcreateinfo.enabledApiLayerCount = numlayers;\n\t\tcreateinfo.enabledApiLayerNames = xrlayers;\n\t\tcreateinfo.enabledExtensionCount = numext;\n\t\tcreateinfo.enabledExtensionNames = xrexts;\n\t\tres = xrCreateInstance(&createinfo, &xr.instance);\n\t}\n\tif (res == XR_ERROR_RUNTIME_UNAVAILABLE || res == XR_ERROR_INSTANCE_LOST)\n\t{\n\t\tCon_Printf(CON_WARNING\"OpenXR: no runtime installed\\n\");\n\t\treturn false;\n\t}\n\tif (XR_FAILED(res) || !xr.instance)\n\t{\n\t\tCon_Printf(CON_ERROR\"OpenXR Runtime: xrCreateInstance failed (%s)\\n\", XR_StringForResult(res));\n\t\treturn false;\n\t}\n\n\tif (havedebugutils)\n\t{\n\t\tXrDebugUtilsMessengerEXT messenger1 = XR_NULL_HANDLE;\n\t\tXrDebugUtilsMessengerCreateInfoEXT cb = {XR_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT};\n\t\tPFN_xrCreateDebugUtilsMessengerEXT xrCreateDebugUtilsMessengerEXT;\n\t\tcb.messageSeverities = XR_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT | XR_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | XR_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT | XR_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT;\n\t\tcb.messageTypes = XR_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT |  XR_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | XR_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT | XR_DEBUG_UTILS_MESSAGE_TYPE_CONFORMANCE_BIT_EXT;\n\t\tcb.userCallback = XR_DebugPrint;\n\t\tcb.userData = NULL;\n\n\t\tif (!XR_FAILED(xrGetInstanceProcAddr(xr.instance, \"xrCreateDebugUtilsMessengerEXT\", (PFN_xrVoidFunction*) &xrCreateDebugUtilsMessengerEXT)))\n\t\t\txrCreateDebugUtilsMessengerEXT(xr.instance, &cb, &messenger1);\n\t}\n\n\tif (xr_debug->ival)\n\t{\n\t\tXrInstanceProperties props = {XR_TYPE_INSTANCE_PROPERTIES};\n\t\tif (!XR_FAILED(xrGetInstanceProperties(xr.instance, &props)))\n\t\t\tCon_Printf(\"OpenXR Runtime: %s    %u.%u.%u\\n\", props.runtimeName, XR_VERSION_MAJOR(props.runtimeVersion), XR_VERSION_MINOR(props.runtimeVersion), XR_VERSION_PATCH(props.runtimeVersion));\n\t\telse\n\t\t\tCon_Printf(\"OpenXR Runtime: Unable to determine runtime version (%s)\\n\", XR_StringForResult(res));\n\t}\n\n\t{\n\t\tXrSystemGetInfo systemInfo = { XR_TYPE_SYSTEM_GET_INFO };\n\t\tif (qreqs->vrplatform == VR_HEADLESS)\n\t\t\tsystemInfo.formFactor = XR_FORM_FACTOR_HEAD_MOUNTED_DISPLAY;\t//err... woteva\n\t\telse if (!strncasecmp(xr_formfactor->string, \"hand\", 4))\n\t\t\tsystemInfo.formFactor = XR_FORM_FACTOR_HANDHELD_DISPLAY;\n\t\telse if (!strncasecmp(xr_formfactor->string, \"head\",4))\n\t\t\tsystemInfo.formFactor = XR_FORM_FACTOR_HEAD_MOUNTED_DISPLAY;\n\t\telse\n\t\t{\n\t\t\tif (*xr_formfactor->string)\n\t\t\t\tCon_Printf(\"\\\"%s\\\" is not a recognised value for xr_formfactor\\n\", xr_formfactor->string);\n\t\t\telse\n\t\t\t\tCon_Printf(\"xr_formfactor not set, assuming headmounted\\n\");\n\t\t\tsystemInfo.formFactor = XR_FORM_FACTOR_HEAD_MOUNTED_DISPLAY;\n\t\t}\n\t\tres = xrGetSystem(xr.instance, &systemInfo, &xr.systemid);\n\t\tif (XR_FAILED(res) || !xr.systemid)\n\t\t\treturn false;\n\t}\n\n\t{\n\t\tXrSystemProperties props = {XR_TYPE_SYSTEM_PROPERTIES};\n#ifdef XR_EXT_hand_tracking\n\t\tXrSystemHandTrackingPropertiesEXT handtrackprops = {XR_TYPE_SYSTEM_HAND_TRACKING_PROPERTIES_EXT};\n\t\tif (havehandtrack)\n\t\t{\t//instance might support it, but the specific hardware we're trying to use might not, in which case don't try using it after all.\n\t\t\thandtrackprops.next = props.next;\n\t\t\tprops.next = &handtrackprops;\n\t\t}\n#endif\n\t\tif (qreqs->vrplatform != VR_HEADLESS)\n\t\t\tif (XR_SUCCEEDED(xrGetSystemProperties(xr.instance, xr.systemid, &props)))\n\t\t\t{\n\t\t\t\tif (xr_debug->ival)\n\t\t\t\t\tCon_Printf(\"OpenXR System: %s\\n\", props.systemName);\n\t\t\t}\n\n#ifdef XR_EXT_hand_tracking\n\t\thavehandtrack = handtrackprops.supportsHandTracking;\n#endif\n\t}\n\n#ifdef XR_EXT_hand_tracking\n\tif (havehandtrack)\n\t{\n\t\txrGetInstanceProcAddr(xr.instance, \"xrCreateHandTrackerEXT\", (PFN_xrVoidFunction*) &xrCreateHandTrackerEXT);\n\t\txrGetInstanceProcAddr(xr.instance, \"xrLocateHandJointsEXT\", (PFN_xrVoidFunction*) &xrLocateHandJointsEXT);\n\t\txrGetInstanceProcAddr(xr.instance, \"xrDestroyHandTrackerEXT\", (PFN_xrVoidFunction*) &xrDestroyHandTrackerEXT);\n\t}\n\telse\n\t{\n\t\txrCreateHandTrackerEXT = NULL;\n\t\txrLocateHandJointsEXT = NULL;\n\t\txrDestroyHandTrackerEXT = NULL;\n\t}\n#endif\n\n\tswitch(qreqs->vrplatform)\n\t{\n\tdefault:\n\t\tXR_Shutdown();\n\t\treturn false;\n\tcase VR_HEADLESS:\n\t\tbreak;\n\n#ifdef XR_USE_GRAPHICS_API_VULKAN\n\tcase VR_VULKAN:\n\t\t{\n\t\t\tXrGraphicsRequirementsVulkanKHR reqs = {XR_TYPE_GRAPHICS_REQUIREMENTS_VULKAN_KHR};\n\t\t\tVkInstance inst = VK_NULL_HANDLE;\n\t\t\tVkPhysicalDevice physdev;\n\t\t\tuint32_t extlen;\n\t\t\tchar *extstr;\t//space-delimited list, for some reason. writable though.\n\n\t\t\tPFN_xrGetVulkanGraphicsRequirementsKHR xrGetVulkanGraphicsRequirementsKHR;\n\t\t\tPFN_xrGetVulkanInstanceExtensionsKHR xrGetVulkanInstanceExtensionsKHR;\n\t\t\tPFN_xrGetVulkanDeviceExtensionsKHR xrGetVulkanDeviceExtensionsKHR;\n\t\t\tPFN_xrGetVulkanGraphicsDeviceKHR xrGetVulkanGraphicsDeviceKHR;\n\t\t\tif (XR_FAILED(xrGetInstanceProcAddr(xr.instance, \"xrGetVulkanGraphicsRequirementsKHR\", (PFN_xrVoidFunction*)&xrGetVulkanGraphicsRequirementsKHR)) ||\n\t\t\t\tXR_FAILED(xrGetInstanceProcAddr(xr.instance, \"xrGetVulkanInstanceExtensionsKHR\", (PFN_xrVoidFunction*)&xrGetVulkanInstanceExtensionsKHR)) ||\n\t\t\t\tXR_FAILED(xrGetInstanceProcAddr(xr.instance, \"xrGetVulkanDeviceExtensionsKHR\", (PFN_xrVoidFunction*)&xrGetVulkanDeviceExtensionsKHR)) ||\n\t\t\t\tXR_FAILED(xrGetInstanceProcAddr(xr.instance, \"xrGetVulkanGraphicsDeviceKHR\", (PFN_xrVoidFunction*)&xrGetVulkanGraphicsDeviceKHR)))\n\t\t\t\treturn false;\n\t\t\txrGetVulkanGraphicsRequirementsKHR(xr.instance, xr.systemid, &reqs);\n\t\t\tqreqs->maxver.major = XR_VERSION_MAJOR(reqs.maxApiVersionSupported);\n\t\t\tqreqs->maxver.minor = XR_VERSION_MINOR(reqs.maxApiVersionSupported);\n\t\t\tqreqs->minver.major = XR_VERSION_MAJOR(reqs.minApiVersionSupported);\n\t\t\tqreqs->minver.minor = XR_VERSION_MINOR(reqs.minApiVersionSupported);\n\n\t\t\txrGetVulkanInstanceExtensionsKHR(xr.instance, xr.systemid, 0, &extlen, NULL);\n\t\t\textstr = malloc(extlen);\n\t\t\txrGetVulkanInstanceExtensionsKHR(xr.instance, xr.systemid, extlen, &extlen, extstr);\n\n\t\t\t//create vulkan instance now...\n\t\t\tqreqs->createinstance(qreqs, extstr, &inst);\n\t\t\tfree(extstr);\n\n\t\t\txrGetVulkanDeviceExtensionsKHR(xr.instance, xr.systemid, 0, &extlen, NULL);\n\t\t\textstr = malloc(extlen);\n\t\t\txrGetVulkanDeviceExtensionsKHR(xr.instance, xr.systemid, extlen, &extlen, extstr);\n\n\t\t\tres = xrGetVulkanGraphicsDeviceKHR(xr.instance, xr.systemid, inst, &physdev);\n\n\t\t\tqreqs->deviceextensions = extstr;\n\t\t\tqreqs->vk.physicaldevice = physdev;\n\t\t}\n\t\tbreak;\n#endif\n\n#ifdef XR_USE_GRAPHICS_API_OPENGL\n\tcase VR_X11_GLX:\n\tcase VR_EGL:\n\tcase VR_WIN_WGL:\n\t\t{\n\t\t\tXrGraphicsRequirementsOpenGLKHR reqs = {XR_TYPE_GRAPHICS_REQUIREMENTS_OPENGL_KHR};\n\t\t\tPFN_xrGetOpenGLGraphicsRequirementsKHR xrGetOpenGLGraphicsRequirementsKHR;\n\t\t\tif (XR_SUCCEEDED(xrGetInstanceProcAddr(xr.instance, \"xrGetOpenGLGraphicsRequirementsKHR\", (PFN_xrVoidFunction*)&xrGetOpenGLGraphicsRequirementsKHR)))\n\t\t\t\txrGetOpenGLGraphicsRequirementsKHR(xr.instance, xr.systemid, &reqs);\n\n\t\t\tqreqs->maxver.major = XR_VERSION_MAJOR(reqs.maxApiVersionSupported);\n\t\t\tqreqs->maxver.minor = XR_VERSION_MINOR(reqs.maxApiVersionSupported);\n\t\t\tqreqs->minver.major = XR_VERSION_MAJOR(reqs.minApiVersionSupported);\n\t\t\tqreqs->minver.minor = XR_VERSION_MINOR(reqs.minApiVersionSupported);\n\t\t\t//caller must validate when creating its context.\n\t\t}\n\t\tbreak;\n#endif\n\n#ifdef XR_USE_GRAPHICS_API_D3D11\n\tcase VR_D3D11:\n\t\t{\n\t\t\tXrGraphicsRequirementsD3D11KHR reqs = {XR_TYPE_GRAPHICS_REQUIREMENTS_D3D11_KHR};\n\t\t\tPFN_xrGetD3D11GraphicsRequirementsKHR xrGetD3D11GraphicsRequirementsKHR;\n\t\t\tif (XR_SUCCEEDED(xrGetInstanceProcAddr(xr.instance, \"xrGetD3D11GraphicsRequirementsKHR\", (PFN_xrVoidFunction*)&xrGetD3D11GraphicsRequirementsKHR)))\n\t\t\t\txrGetD3D11GraphicsRequirementsKHR(xr.instance, xr.systemid, &reqs);\n\n\t\t\tqreqs->minver.major = reqs.minFeatureLevel;\n\t\t\tqreqs->deviceid[0] = reqs.adapterLuid.LowPart;\n\t\t\tqreqs->deviceid[1] = reqs.adapterLuid.HighPart;\n\t\t}\n\t\tbreak;\n#endif\n\t}\n\n\t{\n\t\tXrViewConfigurationType *viewtype;\n\t\tuint32_t viewtypes, u;\n\t\tres = xrEnumerateViewConfigurations(xr.instance, xr.systemid, 0, &viewtypes, NULL);\n\t\tviewtype = alloca(viewtypes*sizeof(viewtype));\n\t\tres = xrEnumerateViewConfigurations(xr.instance, xr.systemid, viewtypes, &viewtypes, viewtype);\n\t\txr.viewtype = (XrViewConfigurationType)0;\n\t\tfor (u = 0; u < viewtypes; u++)\n\t\t{\n\t\t\tswitch(viewtype[u])\n\t\t\t{\n\t\t\tcase XR_VIEW_CONFIGURATION_TYPE_PRIMARY_MONO:\n\t\t\t\tif (!strcasecmp(xr_viewconfig->string, \"mono\"))\n\t\t\t\t\txr.viewtype = viewtype[u];\n\t\t\t\tbreak;\n\t\t\tcase XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO:\n\t\t\t\tif (!strcasecmp(xr_viewconfig->string, \"stereo\"))\n\t\t\t\t\txr.viewtype = viewtype[u];\n\t\t\t\tbreak;\n\t\t\tcase XR_VIEW_CONFIGURATION_TYPE_PRIMARY_QUAD_VARJO:\n\t\t\t\tif (!strcasecmp(xr_viewconfig->string, \"quad\"))\n\t\t\t\t\txr.viewtype = viewtype[u];\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (!xr.viewtype)\n\t\t{\n\t\t\tif (viewtypes)\n\t\t\t\txr.viewtype = viewtype[0];\n\n\t\t\tif (*xr_viewconfig->string)\n\t\t\t{\n\t\t\t\tCon_Printf(\"OpenXR: Viewtype %s unavailable, using \", xr_viewconfig->string);\n\t\t\t\tswitch(xr.viewtype)\n\t\t\t\t{\n\t\t\t\tcase XR_VIEW_CONFIGURATION_TYPE_PRIMARY_MONO:\t\tCon_Printf(\"mono\\n\"); break;\n\t\t\t\tcase XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO:\t\tCon_Printf(\"stereo\\n\"); break;\n\t\t\t\tcase XR_VIEW_CONFIGURATION_TYPE_PRIMARY_QUAD_VARJO:\tCon_Printf(\"quad\\n\"); break;\n\t\t\t\tdefault:\n\t\t\t\t\tCon_Printf(\"unknown (%i)\\n\", xr.viewtype);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tif (qreqs->vrplatform == VR_HEADLESS)\n\t\treturn true;\n\n\tres = xrEnumerateViewConfigurationViews(xr.instance, xr.systemid, xr.viewtype, 0, &xr.viewcount, NULL);\n\tif (xr.viewcount > MAX_VIEW_COUNT)\n\t\txr.viewcount = MAX_VIEW_COUNT;\t//oh noes! evile!\n\txr.views = calloc(1,sizeof(*xr.views)*xr.viewcount);\n\t{\n\t\tuint32_t u;\n\t\tfor (u = 0; u < xr.viewcount; u++)\n\t\t\txr.views[u].type = XR_TYPE_VIEW_CONFIGURATION_VIEW;\n\t}\n\tres = xrEnumerateViewConfigurationViews(xr.instance, xr.systemid, xr.viewtype, xr.viewcount, &xr.viewcount, xr.views);\n\n\t//caller now knows what device/contextversion/etc to init with\n\treturn true;\n}\n\nstatic qboolean XR_Init(vrsetup_t *qreqs, rendererstate_t *info)\n{\n\tif (xr.fake)\n\t\treturn true;\t//nothing to do here.\n\n\txr.srgb = info->srgb;\n\tswitch(qreqs->vrplatform)\n\t{\n\tcase VR_HEADLESS:\n\t\tbreak;\n\tdefault:\n\t\treturn false;\t//error. not supported in this build.\n#ifdef XR_USE_GRAPHICS_API_VULKAN\n\tcase VR_VULKAN:\n\t\t{\n\t\t\tXrGraphicsBindingVulkanKHR *vk = xr.bindinginfo = calloc(1, sizeof(*vk));\n\t\t\tvk->type = XR_TYPE_GRAPHICS_BINDING_VULKAN_KHR;\n\t\t\tvk->instance = qreqs->vk.instance;\n\t\t\tvk->physicalDevice = qreqs->vk.physicaldevice;\n\t\t\tvk->device = qreqs->vk.device;\n\t\t\tvk->queueFamilyIndex = qreqs->vk.queuefamily;\n\t\t\tvk->queueIndex = qreqs->vk.queueindex;\n\t\t\txr.renderer = QR_VULKAN;\n\t\t}\n\t\tbreak;\n#endif\n#ifdef XR_USE_GRAPHICS_API_OPENGL\n#ifdef XR_MND_EGL_ENABLE_EXTENSION_NAME\n\tcase VR_EGL:\t//x11-egl, wayland, and hopefully android...\n\t\t{\n\t\t\tXrGraphicsBindingEGLMND *egl = xr.bindinginfo = calloc(1, sizeof(*egl));\n\t\t\tegl->type = XR_TYPE_GRAPHICS_BINDING_EGL_MND;\n\t\t\tegl->getProcAddress = qreqs->egl.getprocaddr;\n\t\t\tegl->display = qreqs->egl.egldisplay;\n\t\t\tegl->config = qreqs->egl.eglconfig;\n\t\t\tegl->context = qreqs->egl.eglcontext;\n\t\t\txr.renderer = QR_OPENGL;\n\t\t}\n\t\tbreak;\n#elif defined(XR_MNDX_EGL_ENABLE_EXTENSION_NAME)\n\tcase VR_EGL:\t//x11-egl, wayland, and hopefully android...\n\t\t{\n\t\t\tXrGraphicsBindingEGLMNDX *egl = xr.bindinginfo = calloc(1, sizeof(*egl));\n\t\t\tegl->type = XR_TYPE_GRAPHICS_BINDING_EGL_MNDX;\n\t\t\tegl->getProcAddress = (PFNEGLGETPROCADDRESSPROC)qreqs->egl.getprocaddr;\n\t\t\tegl->display = qreqs->egl.egldisplay;\n\t\t\tegl->config = qreqs->egl.eglconfig;\n\t\t\tegl->context = qreqs->egl.eglcontext;\n\t\t\txr.renderer = QR_OPENGL;\n\t\t}\n\t\tbreak;\n#endif\n#ifdef XR_USE_PLATFORM_XLIB\n\tcase VR_X11_GLX:\n\t\t{\n\t\t\tXrGraphicsBindingOpenGLXlibKHR *glx = xr.bindinginfo = calloc(1, sizeof(*glx));\n\t\t\tglx->type = XR_TYPE_GRAPHICS_BINDING_OPENGL_XLIB_KHR;\n\t\t\tglx->xDisplay = qreqs->x11_glx.display;\n\t\t\tglx->visualid = qreqs->x11_glx.visualid;\n\t\t\tglx->glxFBConfig = qreqs->x11_glx.glxfbconfig;\n\t\t\tglx->glxDrawable = qreqs->x11_glx.drawable;\n\t\t\tglx->glxContext = qreqs->x11_glx.glxcontext;\n\t\t\txr.renderer = QR_OPENGL;\n\t\t}\n\t\tbreak;\n#endif\n#ifdef XR_USE_PLATFORM_WIN32\n\tcase VR_WIN_WGL:\n\t\t{\n\t\t\tXrGraphicsBindingOpenGLWin32KHR *wgl = xr.bindinginfo = calloc(1, sizeof(*wgl));\n\t\t\twgl->type = XR_TYPE_GRAPHICS_BINDING_OPENGL_WIN32_KHR;\n\t\t\twgl->hDC = qreqs->wgl.hdc;\n\t\t\twgl->hGLRC = qreqs->wgl.hglrc;\n\t\t\txr.renderer = QR_OPENGL;\n\t\t}\n\t\tbreak;\n#endif\n#endif\t//def XR_USE_GRAPHICS_API_OPENGL\n#ifdef XR_USE_GRAPHICS_API_D3D11\n\tcase VR_D3D11:\n\t\t{\n\t\t\tXrGraphicsBindingD3D11KHR *d3d = xr.bindinginfo = calloc(1, sizeof(*d3d));\n\t\t\td3d->type = XR_TYPE_GRAPHICS_BINDING_D3D11_KHR;\n\t\t\td3d->device = qreqs->d3d.device;\n\t\t\txr.renderer = QR_DIRECT3D11;\n\t\t}\n\t\tbreak;\n#endif\n\t}\n\n\n\tXR_SetupInputs_Instance();\n\treturn true;\n}\n\nstatic void XR_HapticCommand_f(void)\n{\n\tsize_t u;\n\tchar actionname[XR_MAX_ACTION_NAME_SIZE];\n\tcmdfuncs->Argv(0, actionname, sizeof(actionname));\n\tfor (u = 0; u < xr.numactions; u++)\n\t{\n\t\tif (!strcasecmp(xr.actions[u].actname, actionname) && xr.actions[u].acttype == XR_ACTION_TYPE_VIBRATION_OUTPUT)\n\t\t{\n\t\t\tif (xr.session)\n\t\t\t{\n\t\t\t\tXrHapticActionInfo info = {XR_TYPE_HAPTIC_ACTION_INFO};\n\t\t\t\tXrHapticVibration haptic = {XR_TYPE_HAPTIC_VIBRATION};\n\t\t\t\tinfo.action = xr.actions[u].action;\n\t\t\t\tinfo.subactionPath = XR_NULL_PATH;\n\n\t\t\t\tcmdfuncs->Argv(1, actionname, sizeof(actionname));\n\t\t\t\thaptic.duration = *actionname?SECONDS_TO_NANOSECONDS(atof(actionname)):XR_MIN_HAPTIC_DURATION;\n\t\t\t\tcmdfuncs->Argv(2, actionname, sizeof(actionname));\n\t\t\t\thaptic.amplitude = *actionname?atof(actionname):0;\n\t\t\t\tcmdfuncs->Argv(3, actionname, sizeof(actionname));\n\t\t\t\thaptic.frequency = *actionname?atof(actionname):XR_FREQUENCY_UNSPECIFIED;\n\t\t\t\txrApplyHapticFeedback(xr.session, &info, (XrHapticBaseHeader*)&haptic);\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t}\n}\n\nstatic XrAction XR_DefineAction(enum actset_e set, XrActionType type, const char *name, const char *description, const char *root)\n{\n\tXrActionCreateInfo info = {XR_TYPE_ACTION_CREATE_INFO};\n\tXrResult res;\n\tchar *ffs;\n\n\tsize_t u;\n\tint nconflicts = 0;\n\tint dconflicts = 0;\n\tfor (u = 0; u < xr.numactions; u++)\n\t{\n\t\tif (xr.actions[u].set != set)\n\t\t\tcontinue;\n\t\tif ((xr.actions[u].acttype == type || !type) && !strcmp(xr.actions[u].actname, name) /*&& !strcmp(xr.actions[u].actdescription, description)*/ && !strcmp(xr.actions[u].subactionpath?xr.actions[u].subactionpath:\"\", root?root:\"\"))\n\t\t{\t//looks like a dupe...\n\t\t\treturn xr.actions[u].action;\n\t\t}\n\t\tif (!strcasecmp(xr.actions[u].actname, name))\n\t\t\tnconflicts++;\t//arse balls knob cock\n\t\tif (description && !strcasecmp(xr.actions[u].actdescription, description))\n\t\t\tdconflicts++;\t//arse balls knob cock\n\t}\n\tif (!description)\n\t\treturn XR_NULL_HANDLE;\t//none found\n\tif (u == xr.maxactions)\n\t{\n\t\tsize_t nm = (xr.maxactions+1)*2;\n\t\tvoid *n = plugfuncs->Realloc(xr.actions, sizeof(*xr.actions) * nm);\n\t\tif (!n)\n\t\t\treturn XR_NULL_HANDLE;\t//erk!\n\t\txr.actions = n;\n\t\txr.maxactions = nm;\n\t}\n\n\tmemset(&xr.actions[u], 0, sizeof(xr.actions[u]));\n\txr.actions[u].set = set;\n\txr.actions[u].acttype = type;\n\txr.actions[u].actname = strdup(name);\n\txr.actions[u].actdescription = strdup(description);\n\txr.actions[u].subactionpath = (root&&*root)?strdup(root):NULL;\n\txr.numactions++;\n\n\tif (xr.actions[u].subactionpath)\n\t\txrStringToPath(xr.instance, xr.actions[u].subactionpath, &xr.actions[u].path);\n\telse\n\t\txr.actions[u].path = XR_NULL_PATH;\n\tif (xr.actions[u].path == XR_NULL_PATH)\n\t{\n\t\tinfo.countSubactionPaths = 0;\n\t\tinfo.subactionPaths = NULL;\n\t}\n\telse\n\t{\n\t\tinfo.countSubactionPaths = 1;\n\t\tinfo.subactionPaths = &xr.actions[u].path;\n\t}\n\tinfo.actionType = xr.actions[u].acttype;\n\tQ_strlcpy(info.actionName, xr.actions[u].actname, sizeof(info.actionName));\n\tfor (ffs = info.actionName; *ffs; ffs++)\n\t{\n\t\tif (*ffs >= 'A' && *ffs < 'Z')\n\t\t\t*ffs += 'a'-'A';\t//must be lower-case\n\t\telse if (*ffs >= 'a' && *ffs <= 'z')\n\t\t\t;\t//allowed\n\t\telse if (*ffs >= '0' && *ffs <= '9')\n\t\t\t;\t//allowed\n\t\telse if (*ffs == '.' || *ffs == '-' || *ffs == '_')\n\t\t\t;\t//allowed\n\t\t// '/' is not allowed as it must be a single segment.\n\t\telse\n\t\t\t*ffs = '_';\t//everything else is blocked\n\t}\n\tQ_strlcpy(info.localizedActionName, xr.actions[u].actdescription, sizeof(info.localizedActionName));\n\tres = xrCreateAction(xr.actionset[set].actionSet, &info, &xr.actions[u].action);\n\tif (XR_FAILED(res))\n\t\tCon_Printf(\"openxr: Unable to create action %s [%s] - %s\\n\", info.actionName, info.localizedActionName, XR_StringForResult(res));\n\n\tif (info.actionType == XR_ACTION_TYPE_VIBRATION_OUTPUT)\n\t\tcmdfuncs->AddCommand(xr.actions[u].actname, XR_HapticCommand_f, \"Linked to an OpenXR haptic feedback.\");\n\n\treturn xr.actions[u].action;\n}\n\nstatic qboolean XR_ReadLine(const char **text, char *buffer, size_t buflen)\n{\n\tchar in;\n\tchar *out = buffer;\n\tsize_t len;\n\tif (buflen <= 1)\n\t\treturn false;\n\tlen = buflen-1;\n\twhile (len > 0)\n\t{\n\t\tin = *(*text);\n\t\tif (!in)\n\t\t{\n\t\t\tif (len == buflen-1)\n\t\t\t\treturn false;\n\t\t\t*out = 0;\n\t\t\treturn true;\n\t\t}\n\t\t(*text)++;\n\t\tif (in == '\\n')\n\t\t\tbreak;\n\t\t*out++ = in;\n\t\tlen--;\n\t}\n\t*out = '\\0';\n\n\t//if there's a trailing \\r, strip it.\n\tif (out > buffer)\n\t\tif (out[-1] == '\\r')\n\t\t\tout[-1] = 0;\n\n\treturn true;\n}\n\nstatic int XR_BindProfileStr(const char *fname, const char *file)\n{\n\tXrAction act;\n\tXrResult res;\n\tXrPath profilepath = XR_NULL_PATH;\n\tchar line[1024], *linestart;\n\tchar name[1024];\n\tchar type[256];\n\tchar desc[1024];\n\tchar bind[1024];\n\tchar root[1024];\n\tunsigned int p;\n\tchar prefix[2][1024];\n\tenum actset_e set;\n\n\tXrInteractionProfileSuggestedBinding suggestedbindings = {XR_TYPE_INTERACTION_PROFILE_SUGGESTED_BINDING};\n\tunsigned int acts = 0;\n\tXrActionSuggestedBinding bindings[256];\n\tunsigned int totalacts = 0;\n\n\twhile (XR_ReadLine(&file, line, sizeof(line)))\n\t{\n\t\tset = AS_COMMON;\n\t\tlinestart = line;\n\t\twhile (*linestart == ' ' || *linestart == '\\t')\n\t\t\tlinestart++;\n\n\t\tif (!strncasecmp(linestart, \"menu:\", 5))\n\t\t{\n\t\t\tset = AS_MENU;\n\t\t\tlinestart+=5;\n\t\t}\n\t\telse if (!strncasecmp(linestart, \"game:\", 5))\n\t\t{\n\t\t\tset = AS_GAME;\n\t\t\tlinestart+=5;\n\t\t}\n\t\telse if (!strncasecmp(linestart, \"common:\", 7))\n\t\t{\t//the default, but nice to be able to be explicit (especially if you want a colon in the action name).\n\t\t\tset = AS_COMMON;\n\t\t\tlinestart+=7;\n\t\t}\n\n\t\tcmdfuncs->TokenizeString(linestart);\n\t\tif (cmdfuncs->Argc())\n\t\t{\n\t\t\tcmdfuncs->Argv(0, name, sizeof(name));\n\n\t\t\tif (!strcasecmp(name, \"dev\"))\n\t\t\t{\n\t\t\t\tcmdfuncs->Argv(1, root, sizeof(root));\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\telse if (!strcasecmp(name, \"profile\"))\n\t\t\t{\n\t\t\t\tif (acts)\n\t\t\t\t{\n\t\t\t\t\tsuggestedbindings.interactionProfile = profilepath;\n\t\t\t\t\tsuggestedbindings.countSuggestedBindings = acts;\n\t\t\t\t\tsuggestedbindings.suggestedBindings = bindings;\n\t\t\t\t\tres = xrSuggestInteractionProfileBindings(xr.instance, &suggestedbindings);\n\t\t\t\t\tif (XR_FAILED(res))\n\t\t\t\t\t\tCon_Printf(CON_ERROR\"%s: xrSuggestInteractionProfileBindings failed - %s\\n\", fname, XR_StringForResult(res));\n\t\t\t\t\ttotalacts += acts;\n\t\t\t\t\tacts = 0;\n\t\t\t\t}\n\n\t\t\t\tcmdfuncs->Argv(1, name, sizeof(name));\n\t\t\t\tfor (p = 0; p < countof(prefix); p++)\n\t\t\t\t\tcmdfuncs->Argv(2+p, prefix[p], sizeof(prefix[p]));\n\t\t\t\t*root = 0;\n\t\t\t\tif (XR_FAILED(xrStringToPath(xr.instance, name, &profilepath)))\n\t\t\t\t\tprofilepath = XR_NULL_PATH;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\telse if (!strcasecmp(name, \"action\"))\n\t\t\t{\n\t\t\t\tcmdfuncs->Argv(1, name, sizeof(name));\n\t\t\t\tcmdfuncs->Argv(2, desc, sizeof(desc));\n\t\t\t\tcmdfuncs->Argv(3, type, sizeof(type));\n\t\t\t\tcmdfuncs->Argv(4, bind, sizeof(bind));\n\t\t\t}\n\t\t\telse if (cmdfuncs->Argc() == 2)\n\t\t\t{\n\t\t\t\t*desc = 0;\n\t\t\t\t*type = 0;\n\t\t\t\tcmdfuncs->Argv(1, bind, sizeof(desc));\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (cmdfuncs->Argc() < 4)\n\t\t\t\t{\n\t\t\t\t\tCon_Printf(\"Unknown action command \\\"%s\\\"\\n\", name);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tcmdfuncs->Argv(1, desc, sizeof(desc));\n\t\t\t\tcmdfuncs->Argv(2, type, sizeof(type));\n\t\t\t\tcmdfuncs->Argv(3, bind, sizeof(bind));\n\t\t\t\tif (cmdfuncs->Argc() >= 5)\n\t\t\t\t\tCon_Printf(\"%s: %s: Extra tokens found\\n\", fname, name);\n\t\t\t}\n\n\t\t\tif (*type)\n\t\t\t{\n\t\t\t\tXrActionType xrtype;\n\t\t\t\tif (!strcasecmp(type, \"button\"))\n\t\t\t\t\txrtype = XR_ACTION_TYPE_BOOLEAN_INPUT;\n\t\t\t\telse if (!strcasecmp(type, \"float\"))\n\t\t\t\t\txrtype = XR_ACTION_TYPE_FLOAT_INPUT;\n\t\t\t\telse if (!strcasecmp(type, \"vector2f\"))\n\t\t\t\t\txrtype = XR_ACTION_TYPE_VECTOR2F_INPUT;\n\t\t\t\telse if (!strcasecmp(type, \"pose\"))\n\t\t\t\t\txrtype = XR_ACTION_TYPE_POSE_INPUT;\n\t\t\t\telse if (!strcasecmp(type, \"vibration\") || !strcasecmp(type, \"haptic\"))\n\t\t\t\t\txrtype = XR_ACTION_TYPE_VIBRATION_OUTPUT;\n\t\t\t\telse\n\t\t\t\t\tcontinue;\n\n\t\t\t\t//define our action...\n\t\t\t\tact = XR_DefineAction(set, xrtype, name, desc, root);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tact = XR_DefineAction(set, (XrActionType)0, name, NULL, root);\n\t\t\t\tif(act==XR_NULL_HANDLE)\n\t\t\t\t\tCon_Printf(\"Action %s not defined yet\\n\", name);\n\t\t\t}\n\n\t\t\t//and add it to the profile we're building.\n\t\t\tif (act != XR_NULL_HANDLE && *bind && profilepath!=XR_NULL_PATH)\n\t\t\t{\n\t\t\t\tif (*bind == '/')\n\t\t\t\t{\n\t\t\t\t\tres = xrStringToPath(xr.instance, bind, &bindings[acts].binding);\n\t\t\t\t\tif (XR_SUCCEEDED(res) && acts < countof(bindings))\n\t\t\t\t\t\tbindings[acts++].action = act;\n\t\t\t\t}\n\t\t\t\telse if (*root == '/')\n\t\t\t\t{\n\t\t\t\t\tres = xrStringToPath(xr.instance, va(\"%s/%s\", root, bind), &bindings[acts].binding);\n\t\t\t\t\tif (XR_SUCCEEDED(res) && acts < countof(bindings))\n\t\t\t\t\t\tbindings[acts++].action = act;\n\t\t\t\t}\n\t\t\t\telse for (p = 0; p < countof(prefix) && *prefix[p]=='/'; p++)\n\t\t\t\t{\n\t\t\t\t\tres = xrStringToPath(xr.instance, va(\"%s%s\", prefix[p], bind), &bindings[acts].binding);\n\t\t\t\t\tif (XR_SUCCEEDED(res) && acts < countof(bindings))\n\t\t\t\t\t\tbindings[acts++].action = act;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tif (acts)\n\t{\n\t\tsuggestedbindings.interactionProfile = profilepath;\n\t\tsuggestedbindings.countSuggestedBindings = acts;\n\t\tsuggestedbindings.suggestedBindings = bindings;\n\t\tres = xrSuggestInteractionProfileBindings(xr.instance, &suggestedbindings);\n\t\tif (XR_FAILED(res))\n\t\t\tCon_Printf(CON_ERROR\"%s: xrSuggestInteractionProfileBindings failed - %s\\n\", fname, XR_StringForResult(res));\n\t\ttotalacts += acts;\n\t}\n\treturn totalacts;\n}\n\nstatic int QDECL XR_BindProfileFile(const char *fname, qofs_t fsize, time_t mtime, void *ctx, struct searchpathfuncs_s *package)\n{\n\tvfsfile_t *f = fsfuncs->OpenVFS(fname, \"rb\", FS_GAME);\n\tif (f)\n\t{\n\t\tsize_t len = VFS_GETLEN(f);\n\t\tchar *buf = malloc(len+1);\n\t\tVFS_READ(f, buf, len);\n\t\tbuf[len] = 0;\n\t\t*(unsigned int*)ctx += XR_BindProfileStr(fname, buf);\n\t\tfree(buf);\n\t\tVFS_CLOSE(f);\n\t}\n\treturn true;\n}\n\nstatic const struct\n{\n\tconst char *name;\n\tconst char *script;\n} xr_knownprofiles[] =\n{\n\t//FIXME: set up some proper bindings!\n\t{\"khr_simple\",\t\t\"profile /interaction_profiles/khr/simple_controller    /user/hand/left/ /user/hand/right/\\n\"\n\t\t\t\"dev /user/hand/left\\n\"\n\t\t\t\t\"+attack_left\t\t\t\\\"Left Attack\\\"\t\tbutton\tinput/select/click\\n\"\n\t\t\t\t\"+menu_left\t\t\t\t\\\"Left Menu\\\"\t\tbutton\tinput/menu/click\\n\"\n\t\t\t\t\"#POSE_LEFT\t\t\t\t\\\"Left Aim Pose\\\"\tpose\tinput/aim/pose\\n\"\n\t\t\t\t//\"left_grip\t\t\t\\\"Left Grip Pose\\\"\tpose\tinput/grip/pose\\n\"\n\t\t\t\t\"haptic_left\t\t\t\\\"Left Haptic\\\"\t\thaptic\toutput/haptic\\n\"\n\t\t\t\"dev /user/hand/right\\n\"\n\t\t\t\t\"menu: #GP_START\t\t\\\"Menu Enter\\\"\t\tbutton\tinput/select/click\\n\"\n\t\t\t\t\"menu: #GP_BACK\t\t\t\\\"Menu Escape\\\"\t\tbutton\tinput/menu/click\\n\"\n\t\t\t\t\"game: +attack_right\t\\\"Right Attack\\\"\tbutton\tinput/select/click\\n\"\n\t\t\t\t\"game: +menu_right\t\t\\\"Right Menu\\\"\t\tbutton\tinput/menu/click\\n\"\n\t\t\t\t\"#POSE_RIGHT\t\t\t\\\"Right Aim Pose\\\"\tpose\tinput/aim/pose\\n\"\n\t\t\t\t//\"right_grip\t\t\t\\\"Right Grip Pose\\\"\tpose\tinput/grip/pose\\n\"\n\t\t\t\t\"haptic_right\t\t\t\\\"Right Haptic\\\"\thaptic\toutput/haptic\\n\"\n\t},\n\n/*\t{\"valve_index\",\t\t\"profile /interaction_profiles/valve/index_controller    /user/hand/left/ /user/hand/right/\\n\"\n\t\t\t//\"unbound\t\t\\\"Unused Button\\\"\t\tbutton\t\tinput/system/click\\n\"\n\t\t\t//\"unbound\t\t\\\"Unused Button\\\"\t\tbutton\t\tinput/system/touch\\n\"\n\t\t\t//\"unbound\t\t\\\"Unused Button\\\"\t\tbutton\t\tinput/a/click\\n\"\n\t\t\t//\"unbound\t\t\\\"Unused Button\\\"\t\tbutton\t\tinput/a/touch\\n\"\n\t\t\t//\"unbound\t\t\\\"Unused Button\\\"\t\tbutton\t\tinput/b/click\\n\"\n\t\t\t//\"unbound\t\t\\\"Unused Button\\\"\t\tbutton\t\tinput/b/touch\\n\"\n\t\t\t//\"unbound\t\t\\\"Unused Button\\\"\t\tfloat\t\tinput/squeeze/value\\n\"\n\t\t\t//\"unbound\t\t\\\"Unused Button\\\"\t\tbutton\t\tinput/squeeze/force\\n\"\n\t\t\t//\"unbound\t\t\\\"Unused Button\\\"\t\tbutton\t\tinput/trigger/click\\n\"\n\t\t\t//\"unbound\t\t\\\"Unused Button\\\"\t\tfloat\t\tinput/trigger/value\\n\"\n\t\t\t//\"unbound\t\t\\\"Unused Button\\\"\t\tbutton\t\tinput/trigger/touch\\n\"\n\t\t\t//\"unbound\t\t\\\"Unused Button\\\"\t\tvector2f\tinput/thumbstick\\n\"\n\t\t\t//\"unbound\t\t\\\"Unused Button\\\"\t\tbutton\t\tinput/thumbstick/click\\n\"\n\t\t\t//\"unbound\t\t\\\"Unused Button\\\"\t\tbutton\t\tinput/thumbstick/touch\\n\"\n\t\t\t//\"unbound\t\t\\\"Unused Button\\\"\t\tvector2f\tinput/trackpad\\n\"\n\t\t\t//\"unbound\t\t\\\"Unused Button\\\"\t\tbutton\t\tinput/trackpad/force\\n\"\n\t\t\t//\"unbound\t\t\\\"Unused Button\\\"\t\tbutton\t\tinput/trackpad/touch\\n\"\n\t\t\t//\"unbound\t\t\\\"Unused Button\\\"\t\tpose\t\tinput/grip/pose\\n\"\n\t\t\t//\"unbound\t\t\\\"Unused Button\\\"\t\tpose\t\tinput/aim/pose\\n\"\n\t\t\t//\"unbound\t\t\\\"Unused Button\\\"\t\thaptic\t\toutput/haptic\\n\"\n\t},\n*/\n/*\t{\"htc_vive\",\t\t\"profile /interaction_profiles/htc/vive_controller    /user/hand/left/ /user/hand/right/\\n\"\n\t\t\t//\"unbound\t\t\\\"Unused Button\\\"\t\tbutton\t\tinput/system/click\\n\"\n\t\t\t//\"unbound\t\t\\\"Unused Button\\\"\t\tbutton\t\tinput/squeeze/click\\n\"\n\t\t\t//\"unbound\t\t\\\"Unused Button\\\"\t\tbutton\t\tinput/menu/click\\n\"\n\t\t\t//\"unbound\t\t\\\"Unused Button\\\"\t\tbutton\t\tinput/trigger/click\\n\"\n\t\t\t//\"unbound\t\t\\\"Unused Button\\\"\t\tfloat\t\tinput/trigger/value\\n\"\n\t\t\t//\"unbound\t\t\\\"Unused Button\\\"\t\tvector2f\tinput/trackpad\\n\"\n\t\t\t//\"unbound\t\t\\\"Unused Button\\\"\t\tbutton\t\tinput/trackpad/click\\n\"\n\t\t\t//\"unbound\t\t\\\"Unused Button\\\"\t\tbutton\t\tinput/trackpad/touch\\n\"\n\t\t\t//\"unbound\t\t\\\"Unused Button\\\"\t\tpose\t\tinput/grip/pose\\n\"\n\t\t\t//\"unbound\t\t\\\"Unused Button\\\"\t\tpose\t\tinput/aim/pose\\n\"\n\t\t\t//\"unbound\t\t\\\"Unused Button\\\"\t\thaptic\t\toutput/haptic\\n\"\n\t\t\t);\n*/\n/*\t{\"htc_vive_pro\",\t\"profile /interaction_profiles/htc/vive_pro    /user/head/\\n\"\n\t\t\t//\"unbound\t\t\\\"Unused Button\\\"\t\tbutton\t\tinput/system/click\\n\"\n\t\t\t//\"unbound\t\t\\\"Unused Button\\\"\t\tbutton\t\tinput/volume_up/click\\n\"\n\t\t\t//\"unbound\t\t\\\"Unused Button\\\"\t\tbutton\t\tinput/volume_down/click\\n\"\n\t\t\t//\"unbound\t\t\\\"Unused Button\\\"\t\tbutton\t\tinput/mute_mic/click\\n\"\n\t\t\t);\n*/\n\n\t//just map everything to quake's various buttons so that mods with proper gamepad mapping will work here too.\n\t{\"gamepad\", \"profile /interaction_profiles/microsoft/xbox_controller    /user/gamepad/\\n\"\n\t\t\t\"#GP_START\t\t\t\t\\\"Start\\\"\t\t\t\tbutton\t\tinput/menu/click\\n\"\n\t\t\t\"#GP_BACK\t\t\t\t\\\"Back\\\"\t\t\t\tbutton\t\tinput/view/click\\n\"\n\t\t\t\"#GP_A\t\t\t\t\t\\\"A Button\\\"\t\t\tbutton\t\tinput/a/click\\n\"\n\t\t\t\"#GP_B\t\t\t\t\t\\\"B Button\\\"\t\t\tbutton\t\tinput/b/click\\n\"\n\t\t\t\"#GP_X\t\t\t\t\t\\\"X Button\\\"\t\t\tbutton\t\tinput/x/click\\n\"\n\t\t\t\"#GP_Y\t\t\t\t\t\\\"Y Button\\\"\t\t\tbutton\t\tinput/y/click\\n\"\n\t\t\t\"#GP_DPAD_DOWN\t\t\t\\\"Move Backwards\\\"\t\tbutton\t\tinput/dpad_down/click\\n\"\n\t\t\t\"#GP_DPAD_RIGHT\t\t\t\\\"Move Right\\\"\t\t\tbutton\t\tinput/dpad_right/click\\n\"\n\t\t\t\"#GP_DPAD_UP\t\t\t\\\"Move Forward\\\"\t\tbutton\t\tinput/dpad_up/click\\n\"\n\t\t\t\"#GP_DPAD_LEFT\t\t\t\\\"Move Left\\\"\t\t\tbutton\t\tinput/dpad_left/click\\n\"\n\t\t\t\"#GP_LSHOULDER\t\t\t\\\"Jump\\\"\t\t\t\tbutton\t\tinput/shoulder_left/click\\n\"\n\t\t\t\"#GP_RSHOULDER\t\t\t\\\"Attack\\\"\t\t\t\tbutton\t\tinput/shoulder_right/click\\n\"\n\t\t\t\"#GP_LTHUMB\t\t\t\t\\\"Left Thumb\\\"\t\t\tbutton\t\tinput/thumbstick_left/click\\n\"\n\t\t\t\"#GP_RTHUMB\t\t\t\t\\\"Right Thumb\\\"\t\t\tbutton\t\tinput/thumbstick_right/click\\n\"\n\t\t\t\"#GP_AXIS_LTRIGGER\t\t\\\"Left Trigger\\\"\t\tfloat\t\tinput/trigger_left/value\\n\"\n\t\t\t\"#GP_AXIS_RTRIGGER\t\t\\\"Right Trigger\\\"\t\tfloat\t\tinput/trigger_right/value\\n\"\n\t\t\t\"#GP_AXIS_LEFT_X\t\t\\\"Left Thumbstick X\\\"\tfloat\t\tinput/thumbstick_left/x\\n\"\n\t\t\t\"#GP_AXIS_LEFT_Y\t\t\\\"Left Thumbstick y\\\"\tfloat\t\tinput/thumbstick_left/y\\n\"\n\t\t\t\"#GP_AXIS_RIGHT_X\t\t\\\"Right Thumbstick X\\\"\tfloat\t\tinput/thumbstick_right/x\\n\"\n\t\t\t\"#GP_AXIS_RIGHT_X\t\t\\\"Right Thumbstick Y\\\"\tfloat\t\tinput/thumbstick_right/y\\n\"\n\t\t\t\"haptic_gp_left\t\t\t\\\"Left Haptic (Main)\\\"\t\thaptic\toutput/haptic_left\\n\"\n\t\t\t\"haptic_gp_left_trigger\t\\\"Left-Trigger Haptic\\\"\t\thaptic\toutput/haptic_left_trigger\\n\"\n\t\t\t\"haptic_gp_right\t\t\\\"Right Haptic (Main)\\\"\t\thaptic\toutput/haptic_right\\n\"\n\t\t\t\"haptic_gp_right_trigger \\\"Right-Trigger Haptic\\\"\thaptic\toutput/haptic_right_trigger\\n\"\n\t},\n};\n\nstatic void XR_SetupInputs_Instance(void)\n{\n\tunsigned int h;\n\tXrResult res;\n\tint set;\n\tchar *actionsetNames[] = {\"genericactions\", \"menuactions\", \"gameactions\"};\n\tchar *actionsetNamesText[] = {\"Generic Actions\", \"Menu Actions\", \"Game Actions\"};\n\n\tfor (set = 0; set < MAX_ACTIONSETS; set++)\n\t{\n\t\tXrActionSetCreateInfo info = {XR_TYPE_ACTION_SET_CREATE_INFO};\n\t\tQ_strlcpy(info.actionSetName, actionsetNames[set], sizeof(info.actionSetName));\n\t\tQ_strlcpy(info.localizedActionSetName, actionsetNamesText[set], sizeof(info.localizedActionSetName));\n\t\tinfo.priority = 0;\n\n\t\txr.actionset[set].subactionPath = XR_NULL_PATH;\n\t\tres = xrCreateActionSet(xr.instance, &info, &xr.actionset[set].actionSet);\n\t\tif (XR_FAILED(res))\n\t\t\tCon_Printf(\"openxr: Unable to create actionset - %s\\n\", XR_StringForResult(res));\n\t}\n\n\th = 0;\n\tif (fsfuncs)\n\t\tfsfuncs->EnumerateFiles(FS_GAME, \"oxr_*.binds\", XR_BindProfileFile, &h);\n\tif (!h)\t//no user bindings defined, use fallbacks. probably this needs to be per-mod.\n\t{\n\t\tfor (h = 0; h < countof(xr_knownprofiles); h++)\n\t\t\tXR_BindProfileStr(xr_knownprofiles[h].name, xr_knownprofiles[h].script);\n\t}\n}\nstatic void XR_SetupInputs_Session(void)\n{\n\tunsigned int h;\n\tXrResult res;\n\n\t//create action space stuff.\n\tfor (h = 0; h < xr.numactions; h++)\n\t{\n\t\tswitch(xr.actions[h].acttype)\n\t\t{\n\t\tcase XR_ACTION_TYPE_POSE_INPUT:\n\t\t\t{\n\t\t\t\tXrActionSpaceCreateInfo info = {XR_TYPE_ACTION_SPACE_CREATE_INFO};\n\t\t\t\tinfo.action = xr.actions[h].action;\n\t\t\t\tinfo.subactionPath = xr.actions[h].path;\n\t\t\t\tinfo.poseInActionSpace.orientation.w = 1;\t//just fill with identity.\n\n\t\t\t\tres = xrCreateActionSpace(xr.session, &info, &xr.actions[h].space);\n\t\t\t\tif (XR_FAILED(res))\n\t\t\t\t\tCon_Printf(\"openxr: xrCreateActionSpace failed - %s\\n\", XR_StringForResult(res));\n\t\t\t}\n\t\t\tbreak;\n\t\tdefault:\n\t\t\txr.actions[h].space = XR_NULL_HANDLE;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\t//and attach it.\n\t{\n\t\tXrSessionActionSetsAttachInfo info = {XR_TYPE_SESSION_ACTION_SETS_ATTACH_INFO};\n\t\tXrActionSet sets[MAX_ACTIONSETS];\n\t\tunsigned int set;\n\t\tfor (set = 0; set < MAX_ACTIONSETS; set++)\n\t\t\tif (xr.actionset[set].actionSet)\n\t\t\t\tsets[info.countActionSets++] = xr.actionset[set].actionSet;\n\t\tinfo.actionSets = sets;\n\t\tres = xrAttachSessionActionSets(xr.session, &info);\n\t\tif (XR_FAILED(res))\n\t\t\tCon_Printf(\"openxr: xrAttachSessionActionSets failed - %s\\n\", XR_StringForResult(res));\n\t}\n\n#ifdef XR_EXT_hand_tracking\n\t//create some hand trackers... try to create one for each hand...\n\t//(note: this is more a finger-joint tracker than a hand tracker, though limited controllers generally mean its good only for fingers)\n\tif (xrCreateHandTrackerEXT)\n\tfor (h = 0; h < 2; h++)\n\t{\n\t\tXrHandTrackerCreateInfoEXT info = {XR_TYPE_HAND_TRACKER_CREATE_INFO_EXT};\n\t\tinfo.hand = h?XR_HAND_RIGHT_EXT:XR_HAND_LEFT_EXT;\n\t\tinfo.handJointSet = XR_HAND_JOINT_SET_DEFAULT_EXT;\n\t\tres = xrCreateHandTrackerEXT(xr.session, &info, &xr.hand[h].handle);\n\t\tif (XR_FAILED(res))\n\t\t\tCon_Printf(\"openxr: xrCreateHandTrackerEXT failed - %s\\n\", XR_StringForResult(res));\n\t}\n#endif\n}\n\nstatic void XR_PrintInputs(void)\n{\n\tXrResult res;\n\tXrInteractionProfileState profile = {XR_TYPE_INTERACTION_PROFILE_STATE};\n\tXrPath path;\n\tunsigned int u;\n\tstatic const char *paths[] = {\"/user/hand/left\", \"/user/hand/right\", \"/user/head\", \"/user/gamepad\"};\n\tCon_Printf(\"OpenXR Interaction Profiles:\\n\");\n\tfor (u = 0; u < countof(paths); u++)\n\t{\n\t\txrStringToPath(xr.instance, paths[u], &path);\n\t\tres = xrGetCurrentInteractionProfile(xr.session, path, &profile);\n\t\tif (XR_SUCCEEDED(res))\n\t\t{\n\t\t\tchar buf[256];\n\t\t\tuint32_t len = sizeof(buf);\n\t\t\tif (!profile.interactionProfile)\n\t\t\t\tCon_Printf(\"\\t%s: \"S_COLOR_GRAY\"no profile/device\\n\", paths[u]);\n\t\t\telse\n\t\t\t{\n\t\t\t\tres = xrPathToString(xr.instance, profile.interactionProfile, sizeof(buf), &len, buf);\n\t\t\t\tCon_Printf(\"\\t%s: \"S_COLOR_GREEN\"%s\\n\", paths[u], buf);\n\t\t\t}\n\t\t}\n\t}\n\n\n\tCon_Printf(\"Bound actions:\\n\");\n\tfor (u = 0; u < xr.numactions; u++)\n\t{\n\t\tXrBoundSourcesForActionEnumerateInfo info = {XR_TYPE_BOUND_SOURCES_FOR_ACTION_ENUMERATE_INFO};\n\t\tuint32_t inputs, i, bufsize;\n\t\tXrPath input[20];\n\t\tinfo.action = xr.actions[u].action;\n\t\tres = xrEnumerateBoundSourcesForAction(xr.session, &info, countof(input), &inputs, input);\n\t\tif (XR_SUCCEEDED(res))\n\t\t{\n\t\t\tCon_Printf(\"\\t%s \"S_COLOR_GRAY\"(%s)\"S_COLOR_WHITE\":\\n\", xr.actions[u].actname, xr.actions[u].actdescription);\n\t\t\tif (!inputs)\n\t\t\t\tCon_Printf(S_COLOR_GRAY\"\\t\\t(unbound)\\n\");\n\t\t\telse for (i = 0; i < inputs; i++)\n\t\t\t{\n\t\t\t\tchar buffer[8192];\n\t\t\t\tXrInputSourceLocalizedNameGetInfo info = {XR_TYPE_INPUT_SOURCE_LOCALIZED_NAME_GET_INFO};\n\t\t\t\tinfo.sourcePath = input[i];\n\t\t\t\tinfo.whichComponents =\tXR_INPUT_SOURCE_LOCALIZED_NAME_USER_PATH_BIT|\t//'left hand'\n\t\t\t\t\t\t\t\t\t\tXR_INPUT_SOURCE_LOCALIZED_NAME_INTERACTION_PROFILE_BIT|\t//'foo controller'\n\t\t\t\t\t\t\t\t\t\tXR_INPUT_SOURCE_LOCALIZED_NAME_COMPONENT_BIT;\t//'trigger'\n\t\t\t\tres = xrGetInputSourceLocalizedName(xr.session, &info, sizeof(buffer), &bufsize, buffer);\n\t\t\t\tif (XR_FAILED(res))\n\t\t\t\t\tQ_snprintf(buffer, sizeof(buffer), S_COLOR_RED\"error %i\", res);\n\t\t\t\telse\n\t\t\t\t\tCon_Printf(S_COLOR_GREEN\"\\t\\t%s\\n\", buffer);\n\t\t\t}\n\t\t}\n\t\telse if (res == XR_ERROR_HANDLE_INVALID)\t//monado reports this for unimplemented things.\n\t\t\tCon_Printf(S_COLOR_RED\"\\t%s: error XR_ERROR_HANDLE_INVALID (not implemented?)\\n\", xr.actions[u].actname);\n\t\telse\n\t\t\tCon_Printf(S_COLOR_RED\"\\t%s: error %s\\n\", xr.actions[u].actname, XR_StringForResult(res));\n\t}\n}\n\nstatic void XR_UpdateInputs(XrTime time)\n{\n\tXrResult res;\n\tsize_t h;\n\tqboolean activesets[MAX_ACTIONSETS] = {true};\n\n\tif (inputfuncs->GetKeyDest() & ~kdm_game)\n\t\tactivesets[AS_MENU] = true;\t//used while looking at the menuqc/emenus/console/etc.\n\telse\n\t\tactivesets[AS_GAME] = true;\t//used while the game has focus.\n\n\t{\n\t\tXrActionsSyncInfo syncinfo = {XR_TYPE_ACTIONS_SYNC_INFO};\n\t\tXrActiveActionSet sets[MAX_ACTIONSETS];\n\t\tfor (h = 0; h < MAX_ACTIONSETS; h++)\n\t\t\tif (activesets[h])\n\t\t\t\tsets[syncinfo.countActiveActionSets++] = xr.actionset[h];\n\t\tsyncinfo.activeActionSets = sets;\n\t\tres = xrSyncActions(xr.session, &syncinfo);\n\t\tif (res == XR_SESSION_NOT_FOCUSED)\n\t\t\t;\t//handle it anyway, giving us a chance to disable various inputs.\n\t\telse if (XR_FAILED(res))\n\t\t\treturn;\n\t}\n\n\tfor (h = 0; h < xr.numactions; h++)\n\t{\n\t\tif (xr.actions[h].action == XR_NULL_HANDLE)\t//failed to init\n\t\t\tcontinue;\n\t\tsafeswitch(xr.actions[h].acttype)\n\t\t{\n\t\tcase XR_ACTION_TYPE_POSE_INPUT:\n\t\t\t{\n\t\t\t\tXrActionStatePose pose = {XR_TYPE_ACTION_STATE_POSE};\n\t\t\t\tXrActionStateGetInfo info = {XR_TYPE_ACTION_STATE_GET_INFO};\n\t\t\t\tinfo.action = xr.actions[h].action;\n\t\t\t\tinfo.subactionPath = xr.actions[h].path;\n\n\t\t\t\tif (XR_FAILED(xrGetActionStatePose(xr.session, &info, &pose)))\n\t\t\t\t\tbreak;\n\t\t\t\tif (pose.isActive && activesets[xr.actions[h].set])\n\t\t\t\t{\t//its mapped to something, woo.\n\t\t\t\t\tXrSpaceVelocity vel = {XR_TYPE_SPACE_VELOCITY};\n\t\t\t\t\tXrSpaceLocation loc = {XR_TYPE_SPACE_LOCATION, &vel};\n\t\t\t\t\tvec3_t angles, org, lvel, avel;\n\t\t\t\t\tres = xrLocateSpace(xr.actions[h].space, xr.space, time, &loc);\n\t\t\t\t\tXR_PoseToAngOrg(&loc.pose, angles, org);\n\n\t\t\t\t\tVectorSet(lvel, vel.linearVelocity.x, vel.linearVelocity.y, vel.linearVelocity.z);\n\t\t\t\t\tVectorSet(avel, vel.angularVelocity.x, vel.angularVelocity.y, vel.angularVelocity.z);\n\t\t\t\t\tif (!strncasecmp(xr.actions[h].actname, \"#POSE_\", 6))\n\t\t\t\t\t{\n\t\t\t\t\t\tif (inputfuncs->SetHandPosition(xr.actions[h].actname+6,\n\t\t\t\t\t\t\t\t(loc.locationFlags&XR_SPACE_LOCATION_POSITION_VALID_BIT)?org:NULL,\n\t\t\t\t\t\t\t\t(loc.locationFlags&XR_SPACE_LOCATION_ORIENTATION_VALID_BIT)?angles:NULL,\n\t\t\t\t\t\t\t\t(vel.velocityFlags&XR_SPACE_VELOCITY_LINEAR_VALID_BIT)?lvel:NULL,\n\t\t\t\t\t\t\t\t(vel.velocityFlags&XR_SPACE_VELOCITY_ANGULAR_VALID_BIT)?avel:NULL))\n\t\t\t\t\t\t\t;\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tCon_Printf(\"Pose \\\"%s\\\" for action \\\"%s\\\" is not a known pose name\\n\", xr.actions[h].actname+1, xr.actions[h].actdescription);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\t//custom poses that mods might want to handle themselves...\n\t\t\t\t\t\tchar cmd[256];\n\t\t\t\t\t\tunsigned int status = 0;\n\t\t\t\t\t\tstatus |= (loc.locationFlags&XR_SPACE_LOCATION_POSITION_VALID_BIT)?VRSTATUS_ORG:0;\n\t\t\t\t\t\tstatus |= (loc.locationFlags&XR_SPACE_LOCATION_ORIENTATION_VALID_BIT)?VRSTATUS_ANG:0;\n\t\t\t\t\t\tstatus |= (vel.velocityFlags&XR_SPACE_VELOCITY_LINEAR_VALID_BIT)?VRSTATUS_VEL:0;\n\t\t\t\t\t\tstatus |= (vel.velocityFlags&XR_SPACE_VELOCITY_ANGULAR_VALID_BIT)?VRSTATUS_AVEL:0;\n\t\t\t\t\t\tif (status & (VRSTATUS_VEL|VRSTATUS_AVEL))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tQ_snprintf(cmd, sizeof(cmd), \"%s %u %g %g %g %g %g %g %g %g %g %g %g %g\\n\", xr.actions[h].actname, status,\n\t\t\t\t\t\t\t\t\t\t\t angles[0], angles[1], angles[2], org[0], org[1], org[2],\n\t\t\t\t\t\t\t\t\t\t\t vel.angularVelocity.x, vel.angularVelocity.y, vel.angularVelocity.z, vel.linearVelocity.x, vel.linearVelocity.y, vel.linearVelocity.z);\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (status & VRSTATUS_ORG)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tQ_snprintf(cmd, sizeof(cmd), \"%s %u %g %g %g %g %g %g\\n\", xr.actions[h].actname, status,\n\t\t\t\t\t\t\t\t\t\t\t angles[0], angles[1], angles[2], org[0], org[1], org[2]);\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tQ_snprintf(cmd, sizeof(cmd), \"%s %u %g %g %g\\n\", xr.actions[h].actname, status,\n\t\t\t\t\t\t\t\t\t\t\t angles[0], angles[1], angles[2]);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcmdfuncs->AddText(cmd, false);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\t\tcase XR_ACTION_TYPE_BOOLEAN_INPUT:\n\t\t\t{\n\t\t\t\tXrActionStateBoolean state = {XR_TYPE_ACTION_STATE_BOOLEAN};\n\t\t\t\tXrActionStateGetInfo info = {XR_TYPE_ACTION_STATE_GET_INFO};\n\t\t\t\tinfo.action = xr.actions[h].action;\n\t\t\t\tinfo.subactionPath = xr.actions[h].path;\n\t\t\t\tif (XR_FAILED(xrGetActionStateBoolean(xr.session, &info, &state)))\n\t\t\t\t\tbreak;\n\t\t\t\tif (!state.isActive) state.currentState = XR_FALSE;\n\t\t\t\tif ((!!state.currentState) != xr.actions[h].held)\n\t\t\t\t{\n\t\t\t\t\txr.actions[h].held = !!state.currentState;\n\t\t\t\t\tif (xr.actions[h].held && !activesets[xr.actions[h].set])\n\t\t\t\t\t\tbreak;\t//don't fire the down event when this action's set isn't meant to be active...\n\t\t\t\t\tif (*xr.actions[h].actname == '#')\n\t\t\t\t\t{\n\t\t\t\t\t\tint k = inputfuncs->GetKeyCode(xr.actions[h].actname+1, NULL);\n\t\t\t\t\t\tif (k >= 0)\n\t\t\t\t\t\t\tinputfuncs->KeyEvent(0, xr.actions[h].held, k, 0);\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tCon_Printf(\"Key \\\"%s\\\" for action \\\"%s\\\" is not a known key code\\n\", xr.actions[h].actname+1, xr.actions[h].actdescription);\n\t\t\t\t\t}\n\t\t\t\t\telse if (xr.actions[h].held || *xr.actions[h].actname == '+')\n\t\t\t\t\t{\n\t\t\t\t\t\tchar cmd[256];\n\t\t\t\t\t\tQ_strlcpy(cmd, xr.actions[h].actname, sizeof(cmd));\n\t\t\t\t\t\tQ_strlcat(cmd, \"\\n\", sizeof(cmd));\n\t\t\t\t\t\tif (!xr.actions[h].held)\n\t\t\t\t\t\t\t*cmd = '-';\t//release events.\n\t\t\t\t\t\tcmdfuncs->AddText(cmd, false);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\t\tcase XR_ACTION_TYPE_FLOAT_INPUT:\n\t\t\t{\n\t\t\t\tXrActionStateFloat state = {XR_TYPE_ACTION_STATE_FLOAT};\n\t\t\t\tXrActionStateGetInfo info = {XR_TYPE_ACTION_STATE_GET_INFO};\n\t\t\t\tinfo.action = xr.actions[h].action;\n\t\t\t\tinfo.subactionPath = xr.actions[h].path;\n\t\t\t\tif (XR_FAILED(xrGetActionStateFloat(xr.session, &info, &state)))\n\t\t\t\t\tbreak;\n\n\t\t\t\tif (state.isActive && activesets[xr.actions[h].set])\n\t\t\t\t{\n\t\t\t\t\tchar cmd[256];\n\t\t\t\t\tif (!strncasecmp(xr.actions[h].actname, \"#GP_AXIS_\", 9))\n\t\t\t\t\t{\n\t\t\t\t\t\tint axis = -1;\n\t\t\t\t\t\tif (!strcasecmp(xr.actions[h].actname+9, \"LTRIGGER\"))\n\t\t\t\t\t\t\taxis = GPAXIS_LT_TRIGGER;\n\t\t\t\t\t\telse if (!strcasecmp(xr.actions[h].actname+9, \"RTRIGGER\"))\n\t\t\t\t\t\t\taxis = GPAXIS_RT_TRIGGER;\n\t\t\t\t\t\telse if (!strcasecmp(xr.actions[h].actname+9, \"LEFT_X\"))\n\t\t\t\t\t\t\taxis = GPAXIS_LT_RIGHT;\n\t\t\t\t\t\telse if (!strcasecmp(xr.actions[h].actname+9, \"LEFT_Y\"))\n\t\t\t\t\t\t\taxis = GPAXIS_LT_DOWN;\n\t\t\t\t\t\telse if (!strcasecmp(xr.actions[h].actname+9, \"RIGHT_X\"))\n\t\t\t\t\t\t\taxis = GPAXIS_RT_RIGHT;\n\t\t\t\t\t\telse if (!strcasecmp(xr.actions[h].actname+9, \"RIGHT_Y\"))\n\t\t\t\t\t\t\taxis = GPAXIS_RT_DOWN;\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tCon_Printf(\"Unknown gamepad axis: \\\"%s\\\"\\n\", xr.actions[h].actname+1);\n\t\t\t\t\t\tinputfuncs->JoystickAxisEvent(0, axis, state.currentState);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tQ_snprintf(cmd, sizeof(cmd), \"%s %g\\n\", xr.actions[h].actname, state.currentState);\n\t\t\t\t\t\tcmdfuncs->AddText(cmd, false);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\t\tcase XR_ACTION_TYPE_VECTOR2F_INPUT:\n\t\t\t{\n\t\t\t\tXrActionStateVector2f state = {XR_TYPE_ACTION_STATE_VECTOR2F};\n\t\t\t\tXrActionStateGetInfo info = {XR_TYPE_ACTION_STATE_GET_INFO};\n\t\t\t\tinfo.action = xr.actions[h].action;\n\t\t\t\tinfo.subactionPath = xr.actions[h].path;\n\t\t\t\tif (XR_FAILED(xrGetActionStateVector2f(xr.session, &info, &state)))\n\t\t\t\t\tbreak;\n\n\t\t\t\tif (state.isActive && activesets[xr.actions[h].set])\n\t\t\t\t{\n\t\t\t\t\tchar cmd[256];\n\t\t\t\t\tQ_snprintf(cmd, sizeof(cmd), \"%s %g %g\\n\", xr.actions[h].actname, state.currentState.x, state.currentState.y);\n\t\t\t\t\tcmdfuncs->AddText(cmd, false);\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\t\tcase XR_ACTION_TYPE_VIBRATION_OUTPUT:\t//output only, nothing to read.\n\t\tcase XR_ACTION_TYPE_MAX_ENUM: //not a real value\n\t\tsafedefault:\n\t\t\tbreak;\n\t\t}\n\t}\n}\n\nstatic int64_t XR_CheckSwapFormats(int64_t *tryformat, int64_t *fmts, size_t fmtcount)\n{\n\tsize_t i, j;\n\tfor (j = 0; j < fmtcount; j++)\n\t{\t//the openxr driver lists is supposed to list formats in the order that it favours, so favour those.\n\t\tfor (i = 0; tryformat[i]; i++)\n\t\t{\t//now make sure its one that we allow. (steamvr lists ones that are NOT renderable - even though they should be)\n\t\t\tif (tryformat[i] == fmts[j])\n\t\t\t\treturn fmts[j];\n\t\t}\n\t}\n\treturn 0;\n}\nstatic int64_t XR_FindSwapFormat(int64_t **tryformats, int64_t *fmts, size_t fmtcount)\n{\n\tint64_t fmt;\n\t/*try to use a format that matches the user's choice*/\n\tif (xr.srgb == 2)\n\t\tfmt = XR_CheckSwapFormats(tryformats[0], fmts, fmtcount);\n\telse if (xr.srgb)\n\t\tfmt = XR_CheckSwapFormats(tryformats[1], fmts, fmtcount);\n\telse\n\t\tfmt = XR_CheckSwapFormats(tryformats[2], fmts, fmtcount);\n\n\t/*try others out of desperation*/\n\tif (!fmt)\n\t\tfmt = XR_CheckSwapFormats(tryformats[1], fmts, fmtcount);\n\tif (!fmt)\n\t\tfmt = XR_CheckSwapFormats(tryformats[0], fmts, fmtcount);\n\tif (!fmt)\n\t\tfmt = XR_CheckSwapFormats(tryformats[2], fmts, fmtcount);\n\tif (!fmt)\n\t\tfmt = tryformats[1][0];\t//fall back on the first srgb format we know of, in the hope that the driver just failed to list it.\n\treturn fmt;\n}\nstatic qboolean XR_Begin(void)\n{\n\tuint32_t u;\n\tXrResult res;\n\tuint32_t swapfmts;\n\tint64_t *fmts, fmttouse=0;\n\n\txr.beginning = false;\n\txr.ending = false;\n\txr.inputsdirty = true;\n\n\t{\n\t\tXrSessionCreateInfo sessioninfo = {XR_TYPE_SESSION_CREATE_INFO};\n\t\tsessioninfo.next = xr.bindinginfo;\n\t\tsessioninfo.createFlags = 0;\n\t\tsessioninfo.systemId = xr.systemid;\n\t\tres = xrCreateSession(xr.instance, &sessioninfo, &xr.session);\n\t}\n\tif (XR_FAILED(res))\n\t{\n\t\tCon_Printf(\"OpenXR: xrCreateSession failed (%s)\\n\", XR_StringForResult(res));\n\t\treturn false;\n\t}\n\n\t{\n\t\tXrReferenceSpaceCreateInfo info = {XR_TYPE_REFERENCE_SPACE_CREATE_INFO};\n\t\tinfo.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_LOCAL;\n\t\tinfo.poseInReferenceSpace.orientation.w = 1;\n\t\tres = xrCreateReferenceSpace(xr.session, &info, &xr.space);\n\t\tif (XR_FAILED(res))\n\t\t\treturn false;\n\t}\n\n\txrEnumerateSwapchainFormats(xr.session, 0, &swapfmts, NULL);\n\tfmts = alloca(sizeof(*fmts)*swapfmts);\n\tres = xrEnumerateSwapchainFormats(xr.session, swapfmts, &swapfmts, fmts);\n\tif (xr.renderer == QR_HEADLESS)\n\t\t;\n\telse if (!swapfmts)\n\t\tCon_Printf(\"OpenXR: No swapchain formats to use (%s)\\n\", XR_StringForResult(res));\n#ifdef XR_USE_GRAPHICS_API_OPENGL\n\telse if (xr.renderer == QR_OPENGL)\n\t{\n\t\tstatic int64_t hdrformats[] = {GL_RGBA16F,GL_RGBA32F, 0};\n\t\tstatic int64_t srgbformats[] = {GL_SRGB8_ALPHA8_EXT, GL_SRGB8_EXT, 0};\n\t\tstatic int64_t rgbformats[] = {GL_RGB10_A2, GL_RGBA8, /*broken on steamvr - GL_RGBA16_EXT,*/ GL_RGB8, 0};\n\t\tstatic int64_t *formats[] = {hdrformats, srgbformats, rgbformats};\n\t\tfmttouse = XR_FindSwapFormat(formats, fmts, swapfmts);\n\t}\n#endif\n#ifdef XR_USE_GRAPHICS_API_VULKAN\n\telse if (xr.renderer == QR_VULKAN)\n\t{\n\t\tstatic int64_t hdrformats[] = {VK_FORMAT_R16G16B16A16_SFLOAT,VK_FORMAT_R32G32B32A32_SFLOAT, VK_FORMAT_R16G16B16_SFLOAT,VK_FORMAT_R32G32B32_SFLOAT, 0};\n\t\tstatic int64_t srgbformats[] = {VK_FORMAT_R8G8B8A8_SRGB, VK_FORMAT_B8G8R8A8_SRGB, VK_FORMAT_R8G8B8_SRGB, VK_FORMAT_B8G8R8_SRGB, 0};\n\t\tstatic int64_t rgbformats[] = {VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_R8G8B8_UNORM, VK_FORMAT_B8G8R8_UNORM, 0};\n\t\tstatic int64_t *formats[] = {hdrformats, srgbformats, rgbformats};\n\t\tfmttouse = XR_FindSwapFormat(formats, fmts, swapfmts);\n\t}\n#endif\n#ifdef XR_USE_GRAPHICS_API_D3D11\n\telse if (xr.renderer == QR_DIRECT3D11)\n\t{\n\t\tstatic int64_t hdrformats[] = {DXGI_FORMAT_R16G16B16A16_FLOAT, 0};\n\t\tstatic int64_t srgbformats[] = {DXGI_FORMAT_R8G8B8A8_UNORM_SRGB, DXGI_FORMAT_B8G8R8A8_UNORM_SRGB, DXGI_FORMAT_B8G8R8X8_UNORM_SRGB, 0};\n\t\tstatic int64_t rgbformats[] = {DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_FORMAT_B8G8R8X8_UNORM, 0};\n\t\tstatic int64_t *formats[] = {hdrformats, srgbformats, rgbformats};\n\t\tfmttouse = XR_FindSwapFormat(formats, fmts, swapfmts);\n\t}\n#endif\n\telse\n\t{\n\t\tfmttouse = fmts[0];\n\t\tfor (u = 0; u < swapfmts; u++)\n\t\t\tCon_Printf(\"fmt%u: %u / %x\\n\", u, (unsigned)fmts[u], (unsigned)fmts[u]);\n\t}\n\n\txr.colourformat = fmttouse;\n\tfor (u = 0; u < xr.viewcount; u++)\n\t{\n\t\tXrSwapchainCreateInfo swapinfo = {XR_TYPE_SWAPCHAIN_CREATE_INFO};\n\t\tswapinfo.createFlags = 0;\n\t\tswapinfo.usageFlags = XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT;\n\t\tswapinfo.format = fmttouse;\n\t\tswapinfo.sampleCount = 1;//xr.views->recommendedSwapchainSampleCount;\n\t\tswapinfo.width = xr.views->recommendedImageRectWidth;\n\t\tswapinfo.height = xr.views->recommendedImageRectHeight;\n\t\tswapinfo.faceCount = 1;\t//2d, not a cube\n\t\tswapinfo.arraySize = 1;\t//1 for 2d, a 1d array isn't allowed\n\t\tswapinfo.mipCount = 1;\n\t\tres = xrCreateSwapchain(xr.session, &swapinfo, &xr.eye[u].swapchain);\n\t\tif (XR_FAILED(res))\n\t\t{\n\t\t\tCon_Printf(\"OpenXR: xrCreateSwapchain failed (%s)\\n\", XR_StringForResult(res));\n\t\t\treturn false;\n\t\t}\n\t\tres = xrEnumerateSwapchainImages(xr.eye[u].swapchain, 0, &xr.eye[u].numswapimages, NULL);\n\t\tif (XR_FAILED(res))\n\t\t{\n\t\t\tCon_Printf(\"OpenXR: xrEnumerateSwapchainImages failed (%s)\\n\", XR_StringForResult(res));\n\t\t\treturn false;\n\t\t}\n\n\t\t//using a separate swapchain for each eye, so just depend upon npot here and use the whole image.\n\t\txr.eye[u].subimage.imageRect.offset.x = 0;\n\t\txr.eye[u].subimage.imageRect.offset.y = 0;\n\t\txr.eye[u].subimage.imageRect.extent.width = swapinfo.width;\n\t\txr.eye[u].subimage.imageRect.extent.height = swapinfo.height;\n\t\txr.eye[u].subimage.swapchain = xr.eye[u].swapchain;\n\t\txr.eye[u].subimage.imageArrayIndex = 0;\n\n\t\t//okay, this is annoying. the returned array size has different strides for different apis, etc.\n\t\t//translate it into something the relevant backend should understand.\n\t\tswitch(xr.renderer)\n\t\t{\n\t\tdefault:\n\t\t\treturn false;\t//erk?\n#ifdef XR_USE_GRAPHICS_API_D3D11\n\t\tcase QR_DIRECT3D11:\n\t\t\t{\n\t\t\t\tuint32_t i;\n\t\t\t\tXrSwapchainImageD3D11KHR *xrimg = calloc(xr.eye[u].numswapimages, sizeof(*xrimg));\n\t\t\t\tfor (i = 0; i < xr.eye[u].numswapimages; i++)\n\t\t\t\t\txrimg[i].type = XR_TYPE_SWAPCHAIN_IMAGE_VULKAN_KHR;\n\t\t\t\tres = xrEnumerateSwapchainImages(xr.eye[u].swapchain, xr.eye[u].numswapimages, &xr.eye[u].numswapimages, (XrSwapchainImageBaseHeader*)xrimg);\n\t\t\t\tif (XR_FAILED(res))\n\t\t\t\t\txr.eye[u].numswapimages = 0;\n\n\t\t\t\txr.eye[u].swapimages = calloc(xr.eye[u].numswapimages, sizeof(*xr.eye[u].swapimages));\n\t\t\t\tfor (i = 0; i < xr.eye[u].numswapimages; i++)\n\t\t\t\t{\n\t\t\t\t\txr.eye[u].swapimages[i].ptr = xrimg[i].texture;\n\t\t\t\t\txr.eye[u].swapimages[i].ptr2 = NULL;\t//view\n\t\t\t\t\txr.eye[u].swapimages[i].width = swapinfo.width;\n\t\t\t\t\txr.eye[u].swapimages[i].height = swapinfo.height;\n\t\t\t\t\txr.eye[u].swapimages[i].depth = 1;\n\t\t\t\t\txr.eye[u].swapimages[i].status = TEX_LOADED;\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n#endif\n#ifdef XR_USE_GRAPHICS_API_VULKAN\n\t\tcase QR_VULKAN:\n\t\t\t{\n\t\t\t\tuint32_t i;\n\t\t\t\tXrSwapchainImageVulkanKHR *xrimg = calloc(xr.eye[u].numswapimages, sizeof(*xrimg));\n\t\t\t\tstruct vk_image_s *vkimg;\n\t\t\t\tfor (i = 0; i < xr.eye[u].numswapimages; i++)\n\t\t\t\t\txrimg[i].type = XR_TYPE_SWAPCHAIN_IMAGE_VULKAN_KHR;\n\t\t\t\tres = xrEnumerateSwapchainImages(xr.eye[u].swapchain, xr.eye[u].numswapimages, &xr.eye[u].numswapimages, (XrSwapchainImageBaseHeader*)xrimg);\n\t\t\t\tif (XR_FAILED(res))\n\t\t\t\t\txr.eye[u].numswapimages = 0;\n\n\t\t\t\txr.eye[u].swapimages = calloc(xr.eye[u].numswapimages, sizeof(*xr.eye[u].swapimages)+sizeof(struct vk_image_s));\n\t\t\t\tvkimg = (struct vk_image_s*)&xr.eye[u].swapimages[xr.eye[u].numswapimages];\n\t\t\t\tfor (i = 0; i < xr.eye[u].numswapimages; i++)\n\t\t\t\t{\n\t\t\t\t\txr.eye[u].swapimages[i].vkimage = &vkimg[i];\n\t\t\t\t\tvkimg[i].vkformat = fmttouse;\n\t\t\t\t\tvkimg[i].image = xrimg[i].image;\n\t\t\t\t\t//vkimg[i].mem.* = 0;\n\t\t\t\t\tvkimg[i].view = VK_NULL_HANDLE;\n\t\t\t\t\tvkimg[i].sampler = VK_NULL_HANDLE;\n\t\t\t\t\tvkimg[i].layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;\n\t\t\t\t\tvkimg[i].width = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;\n\t\t\t\t\txr.eye[u].swapimages[i].width = vkimg[i].width = swapinfo.width;\n\t\t\t\t\txr.eye[u].swapimages[i].height = vkimg[i].height = swapinfo.height;\n\t\t\t\t\txr.eye[u].swapimages[i].depth = vkimg[i].layers = 1;\n\t\t\t\t\tvkimg[i].mipcount = swapinfo.mipCount;\n\t\t\t\t\tvkimg[i].encoding = PTI_INVALID; //blurgh, is this needed?\n\t\t\t\t\tvkimg[i].type = PTI_2D;\n\t\t\t\t\txr.eye[u].swapimages[i].status = TEX_LOADED;\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n#endif\n#ifdef XR_USE_GRAPHICS_API_OPENGL\n\t\tcase QR_OPENGL:\n\t\t\t{\n\t\t\t\tuint32_t i;\n\t\t\t\tXrSwapchainImageOpenGLKHR *xrimg = calloc(xr.eye[u].numswapimages, sizeof(*xrimg));\n\t\t\t\tfor (i = 0; i < xr.eye[u].numswapimages; i++)\n\t\t\t\t\txrimg[i].type = XR_TYPE_SWAPCHAIN_IMAGE_OPENGL_KHR;\n\t\t\t\tres = xrEnumerateSwapchainImages(xr.eye[u].swapchain, xr.eye[u].numswapimages, &xr.eye[u].numswapimages, (XrSwapchainImageBaseHeader*)xrimg);\n\t\t\t\tif (XR_FAILED(res))\n\t\t\t\t\txr.eye[u].numswapimages = 0;\n\n\t\t\t\txr.eye[u].swapimages = calloc(xr.eye[u].numswapimages, sizeof(*xr.eye[u].swapimages));\n\t\t\t\tfor (i = 0; i < xr.eye[u].numswapimages; i++)\n\t\t\t\t{\n\t\t\t\t\txr.eye[u].swapimages[i].format = swapinfo.format;\n\t\t\t\t\txr.eye[u].swapimages[i].num = xrimg[i].image;\n\t\t\t\t\txr.eye[u].swapimages[i].width = swapinfo.width;\n\t\t\t\t\txr.eye[u].swapimages[i].height = swapinfo.height;\n\t\t\t\t\txr.eye[u].swapimages[i].depth = 1;\n\t\t\t\t\txr.eye[u].swapimages[i].status = TEX_LOADED;\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n#endif\n\t\t}\n\t}\n\tif (XR_FAILED(res))\n\t\treturn false;\n\n\tXR_SetupInputs_Session();\n\n\treturn true;\n}\n\nstatic void XR_ProcessEvents(void)\n{\n\tXrEventDataBuffer ev;\n\tXrResult res;\n\tfor (;;)\n\t{\n\t\tev.type = XR_TYPE_EVENT_DATA_BUFFER;\n\t\tev.next = NULL;\n\t\tres = xrPollEvent(xr.instance, &ev);\n\t\tif (res == XR_EVENT_UNAVAILABLE || XR_FAILED(res))\n\t\t\treturn;\t//nothing interesting here folks\n\n\t\tswitch(ev.type)\n\t\t{\n\t\tdefault:\t//no idea wtf that is\n\t\t\tCon_Printf(\"openxr event %u\\n\", ev.type);\n\t\t\tbreak;\n\n\t\tcase XR_TYPE_EVENT_DATA_INSTANCE_LOSS_PENDING:\n\t\t\tXR_Shutdown();\t//we're meant to try restarting, but that's a hassle. FIXME: expect the user to do a vid_restart.\n\t\t\treturn;\n\n\t\tcase XR_TYPE_EVENT_DATA_REFERENCE_SPACE_CHANGE_PENDING:\n\t\t\tbreak;\n\t\tcase XR_TYPE_EVENT_DATA_EVENTS_LOST:\n\t\t\t{\n\t\t\t\tXrEventDataEventsLost *s = (XrEventDataEventsLost*)&ev;\n\t\t\t\tCon_Printf(CON_ERROR\"OpenXR: Lost %u events!\\n\", s->lostEventCount);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase XR_TYPE_EVENT_DATA_INTERACTION_PROFILE_CHANGED:\n\t\t\txr.inputsdirty = true;\n\t\t\tbreak;\n\n\t\tcase XR_TYPE_EVENT_DATA_SESSION_STATE_CHANGED:\n\t\t\t{\n\t\t\t\tXrEventDataSessionStateChanged *s = (XrEventDataSessionStateChanged*)&ev;\n\t\t\t\txr.state = s->state;\n\t\t\t\treturn;\t//make sure the outer loop actually sees each state change.\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t}\n}\nstatic unsigned int XR_SyncFrame(double *frametime)\n{\n\tunsigned int ret = 0;\n\tXrResult res;\n\n\tif (xr.fake)\n\t{\n\t\txr.time += SECONDS_TO_NANOSECONDS(*frametime);\n\t\treturn VRF_UIACTIVE;\t//not syncing here. let the engine run at its normal rate\n\t}\n\n\tif (!xr.instance)\n\t\treturn 0;\n\n\tif (xr.needrender)\n\t{\t//something screwed up.\n//\t\t*frametime = 0;\n\t\treturn VRF_UIACTIVE;\n\t}\n\n\tif (!xr.session)\n\t{\n\t\tif (xr_enable->ival && !XR_Begin())\n\t\t{\n\t\t\tXR_Shutdown();\n\t\t\treturn 0;\n\t\t}\n\t}\n\telse\n\t{\n\t\tif (!xr_enable->ival && !xr.ending)\n\t\t{\t//user doesn't want a session apparently. try and end the current session cleanly.\n\t\t\tres = xrRequestExitSession(xr.session);\n\t\t\tif (XR_FAILED(res))\n\t\t\t\tCon_Printf(\"openxr: Unable to request session end: %s\\n\", XR_StringForResult(res));\n\t\t\txr.ending = true;\n\t\t}\n\t}\n\n\tXR_ProcessEvents();\n\n\tmemset(&xr.framestate, 0, sizeof(xr.framestate));\n\txr.framestate.type = XR_TYPE_FRAME_STATE;\n\tsafeswitch(xr.state)\n\t{\n\tcase XR_SESSION_STATE_IDLE:\t\t//not allowed to progress till the user puts it on their head/etc\n\t\txr.beginning = false;\n\t\tbreak;\n\tcase XR_SESSION_STATE_READY:\n\t\tif (!xr.beginning)\n\t\t{\n\t\t\tXrSessionBeginInfo info = {XR_TYPE_SESSION_BEGIN_INFO};\n\t\t\tinfo.primaryViewConfigurationType = xr.viewtype;\n\t\t\tres = xrBeginSession(xr.session, &info);\n\t\t\tif (XR_FAILED(res))\n\t\t\t\tCon_Printf(\"Unable to begin session: %s\\n\", XR_StringForResult(res));\n\t\t\txr.beginning = true;\t//begin our xr loop... (and/or stop the spam just above)\n\t\t}\n\t\tbreak;\n\tcase XR_SESSION_STATE_SYNCHRONIZED:\t//no rendering or input yet\n\tcase XR_SESSION_STATE_VISIBLE:\t\t//now generating video frames, but no input yet\n\tcase XR_SESSION_STATE_FOCUSED:\t\t//we have inputs! (and still generating video frames)\n\t\tbreak;\n\tcase XR_SESSION_STATE_STOPPING:\t\t//going back to idle (user took it off their head). we'll go back to rendering to our window again.\n\t\txrEndSession(xr.session);\n\t\txr.beginning = false;\n\t\tbreak;\n\tcase XR_SESSION_STATE_LOSS_PENDING:\t//terminate for now. recreate later if you want.\n\t\tXR_SessionEnded();\t//destroys the session but not the instance, so it can be started up again if desired.\n\t\txr.beginning = false;\n\t\tbreak;\n\tcase XR_SESSION_STATE_EXITING:\t\t//terminate with prejudice.\n\t\tXR_SessionEnded();\n\t\txr.beginning = false;\n\t\tif (!xr.ending)\n\t\t\tXR_Shutdown();\t//this doesn't look like one we requested... don't let it start back up again.\n\t\tbreak;\n\tcase XR_SESSION_STATE_UNKNOWN:\n\tcase XR_SESSION_STATE_MAX_ENUM:\n\tsafedefault:\n\t\txr.beginning = false;\t//some weird error.\n\t\tbreak;\n\t}\n\n\tif (xr.beginning)\n\t{\n\t\tXrTime time;\n\t\tres = xrWaitFrame(xr.session, NULL, &xr.framestate);\n\t\tif (XR_FAILED(res))\n\t\t{\n\t\t\tCon_Printf(\"xrWaitFrame: %s\\n\", XR_StringForResult(res));\n\t\t\treturn false;\n\t\t}\n\t\tret |= VRF_OVERRIDEFRAMETIME;\n\t\tif (xr.framestate.shouldRender)\n\t\t\tret |= VRF_UIACTIVE;\n\n\t\ttime = xr.framestate.predictedDisplayTime;\n\t\tif (xr.timeknown)\n\t\t{\n\t\t\tif (time < xr.time)\t//make sure time doesn't go backward...\n\t\t\t\ttime = xr.time;\n\t\t\t*frametime = NANOSECONDS_TO_SECONDS(time-xr.time);\n\t\t}\n\t\txr.time = time;\n\t\txr.timeknown = true;\n\n\t\txr.needrender = true;\n\t}\n\n\tif (xr.session)\n\t\tXR_UpdateInputs(xr.framestate.predictedDisplayTime);\n\n\treturn ret;\n}\nstatic qboolean XR_Render(void(*rendereye)(texid_t tex, const pxrect_t *viewport, const vec4_t fovoverride, const float projmatrix[16], const float eyematrix[12]))\n{\n\tXrFrameEndInfo endframeinfo = {XR_TYPE_FRAME_END_INFO};\n\tunsigned int u;\n\tXrResult res;\n\n\tXrCompositionLayerProjection proj = {XR_TYPE_COMPOSITION_LAYER_PROJECTION};\n\tconst XrCompositionLayerBaseHeader *projlist[] = {(XrCompositionLayerBaseHeader*)&proj};\n\tXrCompositionLayerProjectionView projviews[MAX_VIEW_COUNT];\n\n\tif (xr.fake)\n\t{\n\t\tstatic vec3_t newhax[3], oldhax[3];\n\t\tstatic XrTime lastup;\n\t\tvec3_t org, ang = {0, 0, 0};\n\t\tfloat frac;\n\t\tconst float UPDATEFREQ=1.0;\n\t\tfloat eyemat[12];\n\n\t\tif ((xr.time - lastup) > SECONDS_TO_NANOSECONDS(UPDATEFREQ))\n\t\t{\n\t\t\tlastup = xr.time;\n\t\t\tmemcpy(oldhax, newhax, sizeof(oldhax));\n\t\t\tnewhax[0][0] = crandom()*10;\n\t\t\tnewhax[0][1] = crandom()*10;\n\t\t\tnewhax[0][2] = crandom()*10;\n\t\t\tnewhax[1][0] = crandom()*30;\n\t\t\tnewhax[1][1] = crandom()*30;\n\t\t\tnewhax[1][2] = crandom()*30;\n\t\t\tnewhax[2][0] = crandom()*30;\n\t\t\tnewhax[2][1] = crandom()*30;\n\t\t\tnewhax[2][2] = crandom()*30;\n\t\t}\n\t\tfrac = NANOSECONDS_TO_SECONDS(xr.time - lastup)/UPDATEFREQ;\n\n\t\t//randomly screw with these inputs. you'll have to handle the clicks separately. :(\n\t\tVectorInterpolate(oldhax[0], frac, newhax[0], ang);\n\t\tXR_Matrix3x4_RM_FromAngles(ang, org, eyemat);\n\t\tinputfuncs->SetHandPosition(\"head\", org, ang, NULL, NULL);\n\t\tVectorInterpolate(oldhax[1], frac, newhax[1], ang);\n\t\tinputfuncs->SetHandPosition(\"right\", org, ang, NULL, NULL);\n\t\tVectorInterpolate(oldhax[2], frac, newhax[2], ang);\n\t\tinputfuncs->SetHandPosition(\"left\", org, ang, NULL, NULL);\n\n\t\trendereye(NULL, NULL, NULL, NULL, eyemat);\n\t\treturn true;\t//skip the main view.\n\t}\n\n\tif (!xr.instance)\n\t\treturn false;\t//err... noooes!\n\n\tif (!xr.session || !xr.beginning)\n\t\treturn false;\n\n\tif (xr.inputsdirty && xr.state==XR_SESSION_STATE_FOCUSED)\n\t{\n\t\txr.inputsdirty = false;\n\t\tif (xr_debug->ival)\n\t\t\tXR_PrintInputs();\n\t}\n#ifdef XR_EXT_hand_tracking\n\tfor (u = 0; u < 2; u++)\n\t{\n\t\tvec3_t ang, org;\n\t\tunsigned int j;\n\t\tstatic const char *jointnames[] = {\n\t\t\t\"PALM\",\n\t\t\t\"WRIST\",\n\t\t\t\"THUMB_METACARPAL\",\n\t\t\t\"THUMB_PROXIMAL\",\n\t\t\t\"THUMB_DISTAL\",\n\t\t\t\"THUMB_TIP\",\n\t\t\t\"INDEX_METACARPAL\",\n\t\t\t\"INDEX_PROXIMAL\",\n\t\t\t\"INDEX_INTERMEDIATE\",\n\t\t\t\"INDEX_DISTAL\",\n\t\t\t\"INDEX_TIP\",\n\t\t\t\"MIDDLE_METACARPAL\",\n\t\t\t\"MIDDLE_PROXIMAL\",\n\t\t\t\"MIDDLE_INTERMEDIATE\",\n\t\t\t\"MIDDLE_DISTAL\",\n\t\t\t\"MIDDLE_TIP\",\n\t\t\t\"RING_METACARPAL\",\n\t\t\t\"RING_PROXIMAL\",\n\t\t\t\"RING_INTERMEDIATE\",\n\t\t\t\"RING_DISTAL\",\n\t\t\t\"RING_TIP\",\n\t\t\t\"LITTLE_METACARPAL\",\n\t\t\t\"LITTLE_PROXIMAL\",\n\t\t\t\"LITTLE_INTERMEDIATE\",\n\t\t\t\"LITTLE_DISTAL\",\n\t\t\t\"LITTLE_TIP\",\n\t\t};\n\t\tXrHandJointsLocateInfoEXT info = {XR_TYPE_HAND_JOINTS_LOCATE_INFO_EXT};\n\t\tXrHandJointLocationsEXT loc = {XR_TYPE_HAND_JOINT_LOCATIONS_EXT};\n\t\tXrHandJointVelocitiesEXT vel = {XR_TYPE_HAND_JOINT_VELOCITIES_EXT};\n\t\tloc.next = &vel;\n\t\tloc.jointCount = countof(xr.hand[u].jointloc);\n\t\tloc.jointLocations = xr.hand[u].jointloc;\n\n\t\tvel.next = &vel;\n\t\tvel.jointCount = countof(xr.hand[u].jointvel);\n\t\tvel.jointVelocities = xr.hand[u].jointvel;\n\n\t\tif (xr.hand[u].handle)\n\t\t\txrLocateHandJointsEXT(xr.hand[u].handle, &info, &loc);\n\t\txr.hand[u].active = loc.isActive;\n\n\t\tif (!xr.hand[u].active || !xr_debug->ival)\n\t\t\tcontinue;\n\t\tfor (j = 0; j < countof(jointnames); j++)\n\t\t{\n\t\t\tif (!xr.hand[u].jointloc[j].locationFlags && !xr.hand[u].jointvel[j].velocityFlags)\n\t\t\t\tcontinue;\n\t\t\tXR_PoseToAngOrg(&xr.hand[u].jointloc[j].pose, ang, org);\n\n\t\t\tCon_Printf(\"%s %s: (%g %g %g) [%g %g %g] %g (%g %g %g) [%g %g %g]\\n\", u?\"Right\":\"Left\", jointnames[j],\n\t\t\t\tang[0],ang[1],ang[2],org[0],org[1],org[2], xr.hand[u].jointloc[j].radius,\n\t\t\t\txr.hand[u].jointvel[j].angularVelocity.x,xr.hand[u].jointvel[j].angularVelocity.y,xr.hand[u].jointvel[j].angularVelocity.z,\n\t\t\t\txr.hand[u].jointvel[j].linearVelocity.x,xr.hand[u].jointvel[j].linearVelocity.y,xr.hand[u].jointvel[j].linearVelocity.z);\n\t\t}\n\t}\n#endif\n\n\tif (!xr.needrender)\n\t\treturn false;\t//xrWaitFrame not called?\n\txr.needrender = false;\n\n\tres = xrBeginFrame(xr.session, NULL);\n\tif (XR_FAILED(res))\n\t{\n\t\tCon_Printf(\"xrBeginFrame: %s\\n\", XR_StringForResult(res));\n\t\tif(res == XR_ERROR_SESSION_LOST)\n\t\t\tXR_SessionEnded();\n\t\telse\n\t\t\tXR_Shutdown();\n\t\treturn false;\n\t}\n\tif (xr.framestate.shouldRender)\n\t{\n\t\tuint32_t eyecount;\n\t\tXrViewState viewstate = {XR_TYPE_VIEW_STATE};\n\t\tXrViewLocateInfo locateinfo = {XR_TYPE_VIEW_LOCATE_INFO};\n\t\tXrView eyeview[MAX_VIEW_COUNT]={};\n\t\tfor (u = 0; u < MAX_VIEW_COUNT; u++)\n\t\t\teyeview[u].type = XR_TYPE_VIEW;\n\n\t\tlocateinfo.displayTime = xr.framestate.predictedDisplayTime;\n\t\tlocateinfo.space = xr.space;\n\t\tres = xrLocateViews(xr.session, &locateinfo, &viewstate, xr.viewcount, &eyecount, eyeview);\n\t\tif (XR_FAILED(res))\n\t\t\tCon_Printf(\"xrLocateViews: %s\\n\", XR_StringForResult(res));\n\n\t\tproj.layerFlags = 0;\n\t\tproj.space = xr.space;\n\t\tproj.views = projviews;\n\t\tendframeinfo.layerCount = 1;\n\n\t\t//set up the head position, as an average of all the eyes, the eyes, the awful knowing eyes...\n\t\t{\n\t\t\tfloat scale;\n\t\t\tvec3_t ang, org;\n\t\t\tXrPosef apose = {0};\n\t\t\tfor (u = 0; u < xr.viewcount && u < eyecount; u++)\n\t\t\t{\t//add em up\n\t\t\t\tapose.orientation.x += eyeview[u].pose.orientation.x;\n\t\t\t\tapose.orientation.y += eyeview[u].pose.orientation.y;\n\t\t\t\tapose.orientation.z += eyeview[u].pose.orientation.z;\n\t\t\t\tapose.orientation.w += eyeview[u].pose.orientation.w;\n\t\t\t\tapose.position.x += eyeview[u].pose.position.x;\n\t\t\t\tapose.position.y += eyeview[u].pose.position.y;\n\t\t\t\tapose.position.z += eyeview[u].pose.position.z;\n\t\t\t}\n\t\t\t//normalize them\n\t\t\tscale = 1 / sqrt(apose.orientation.x*apose.orientation.x+apose.orientation.y*apose.orientation.y+apose.orientation.z*apose.orientation.z+apose.orientation.w*apose.orientation.w);\n\t\t\tapose.orientation.x *= scale;\n\t\t\tapose.orientation.y *= scale;\n\t\t\tapose.orientation.z *= scale;\n\t\t\tapose.orientation.w *= scale;\n\t\t\tapose.position.x /= xr.viewcount;\n\t\t\tapose.position.y /= xr.viewcount;\n\t\t\tapose.position.z /= xr.viewcount;\n\t\t\tXR_PoseToAngOrg(&apose, ang, org);\n\t\t\tinputfuncs->SetHandPosition(\"head\", org, ang, NULL, NULL);\n\t\t}\n\n\t\tfor (u = 0; u < xr.viewcount && u < eyecount; u++)\n\t\t{\n\t\t\tvec4_t fovoverride;\n\t\t\tXrSwapchainImageWaitInfo waitinfo = {XR_TYPE_SWAPCHAIN_IMAGE_WAIT_INFO};\n\t\t\tunsigned int imgidx = 0;\n\t\t\tfloat eyematrix[12];\n\t\t\tres = xrAcquireSwapchainImage(xr.eye[u].swapchain, NULL, &imgidx);\n\t\t\tif (XR_FAILED(res))\n\t\t\t\tCon_Printf(\"xrAcquireSwapchainImage: %s\\n\", XR_StringForResult(res));\n\n\t\t\tmemset(&projviews[u], 0, sizeof(projviews[u]));\n\t\t\tprojviews[u].type = XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW;\n\t\t\tprojviews[u].pose = eyeview[u].pose;\n\t\t\tprojviews[u].fov = eyeview[u].fov;\n\t\t\tprojviews[u].subImage = xr.eye[u].subimage;\n\n\t\t\tXR_PoseToMat12(&eyeview[u].pose, eyematrix);\n\n\t\t\tfovoverride[0] = eyeview[u].fov.angleLeft * (180/M_PI);\n\t\t\tfovoverride[1] = eyeview[u].fov.angleRight * (180/M_PI);\n\t\t\tfovoverride[2] = eyeview[u].fov.angleDown * (180/M_PI);\n\t\t\tfovoverride[3] = eyeview[u].fov.angleUp * (180/M_PI);\n\n\t\t\twaitinfo.timeout = SECONDS_TO_NANOSECONDS(0.1);\n\t\t\tres = xrWaitSwapchainImage(xr.eye[u].swapchain, &waitinfo);\n\t\t\tif (XR_FAILED(res))\n\t\t\t\tCon_Printf(\"xrWaitSwapchainImage: %s\\n\", XR_StringForResult(res));\n\t\t\trendereye(&xr.eye[u].swapimages[imgidx], NULL, fovoverride, NULL/*we're given fov info instead*/, eyematrix);\n\t\t\t//GL note: the OpenXR specification says NOTHING about the application having to glFlush or glFinish.\n\t\t\t//\tI take this to mean that the openxr runtime is responsible for setting up barriers or w/e inside ReleaseSwapchainImage.\n\t\t\t//VK note: the OpenXR spec does say that it needs to be color_attachment_optimal+owned by queue. which it is.\n\t\t\t//\tI take this to mean that the openxr runtime is responsible for barriers (as it'll need to transition it to general or shader-read anyway).\n\t\t\tres = xrReleaseSwapchainImage(xr.eye[u].swapchain, NULL);\n\t\t\tif (XR_FAILED(res))\n\t\t\t\tCon_Printf(\"xrReleaseSwapchainImage: %s\\n\", XR_StringForResult(res));\n\t\t}\n\t\tproj.viewCount = u;\n\t}\n\n\tendframeinfo.layers = projlist;\n\tendframeinfo.displayTime = xr.framestate.predictedDisplayTime;\n\tendframeinfo.environmentBlendMode = XR_ENVIRONMENT_BLEND_MODE_OPAQUE;\t//we don't do the alpha channel very well.\n\tres = xrEndFrame(xr.session, &endframeinfo);\n\tif (XR_FAILED(res))\n\t{\n\t\tCon_Printf(\"xrEndFrame: %s\\n\", XR_StringForResult(res));\n\t\tif (res == XR_ERROR_SESSION_LOST || res == XR_ERROR_SESSION_NOT_RUNNING || res == XR_ERROR_SWAPCHAIN_RECT_INVALID)\n\t\t\tXR_SessionEnded();\t//something sessiony\n\t\telse //if (res == XR_ERROR_INSTANCE_LOST)\n\t\t\tXR_Shutdown();\t//don't really know what it was, just kill everything\n\t}\n\n\treturn xr_skipregularview->ival;\n}\n\nstatic plugvrfuncs_t openxr =\n{\n\t\"OpenXR\",\n\tXR_PreInit,\n\tXR_Init,\n\tXR_SyncFrame,\n\tXR_Render,\n\tXR_Shutdown,\n};\n\nqboolean Plug_Init(void)\n{\n#ifdef XR_NO_PROTOTYPES\n\t{\n\t\tstatic dllhandle_t *lib;\n\t\tstatic dllfunction_t funcs[] = {\n\t\t\t#define XRFUNC(n) {(void*)&n, #n},\n\t\t\t\tXRFUNCS\n\t\t\t#undef XRFUNC\n\t\t\t{NULL}};\n#ifdef _WIN32\n\t#define XR_LOADER_LIBNAME \"openxr_loader\"ARCH_DL_POSTFIX\n#else\n\t#define XR_LOADER_LIBNAME \"libopenxr_loader\"ARCH_DL_POSTFIX\".1\"\n#endif\n\t\tif (!lib)\n\t\t\tlib = plugfuncs->LoadDLL(XR_LOADER_LIBNAME, funcs);\n\t\tif (!lib)\n\t\t{\n\t\t\tCon_Printf(CON_ERROR\"OpenXR: Unable to load \"XR_LOADER_LIBNAME\"\\n\");\n\t\t\treturn false;\n\t\t}\n\t}\n#endif\n\n\tfsfuncs = plugfuncs->GetEngineInterface(plugfsfuncs_name, sizeof(*fsfuncs));\n\tinputfuncs = plugfuncs->GetEngineInterface(pluginputfuncs_name, sizeof(*inputfuncs));\n\tplugfuncs->ExportFunction(\"MayUnload\", XR_PluginMayUnload);\n\tif (plugfuncs->ExportInterface(plugvrfuncs_name, &openxr, sizeof(openxr)))\n\t{\n\t\txr_enable\t\t\t= cvarfuncs->GetNVFDG(\"xr_enable\",\t\t\t\"1\",\t\t\t0,\t\t\t\t\"Controls whether to use openxr rendering or not.\",\t\t\t\t\t\t\t\t\t\"OpenXR configuration\");\n\t\txr_debug\t\t\t= cvarfuncs->GetNVFDG(\"xr_debug\",\t\t\t\"0\",\t\t\t0,\t\t\t\t\"Controls whether to spam debug info or not.\",\t\t\t\t\t\t\t\t\t\t\"OpenXR configuration\");\n\t\txr_formfactor\t\t= cvarfuncs->GetNVFDG(\"xr_formfactor\",\t\t\"head\",\t\t\tCVAR_ARCHIVE,\t\"Controls which VR system to try to use. Valid options are head, or hand\",\t\t\t\"OpenXR configuration\");\n\t\txr_viewconfig\t\t= cvarfuncs->GetNVFDG(\"xr_viewconfig\",\t\t\"\",\t\t\t\tCVAR_ARCHIVE,\t\"Controls the type of view we aim for. Valid options are mono, stereo, or quad\",\t\"OpenXR configuration\");\n\t\txr_metresize\t\t= cvarfuncs->GetNVFDG(\"xr_metresize\",\t\t\"26.24671916\",\tCVAR_ARCHIVE,\t\"Size of a metre in game units\",\t\t\t\t\t\t\t\t\t\t\t\t\t\"OpenXR configuration\");\n\t\txr_skipregularview\t= cvarfuncs->GetNVFDG(\"xr_skipregularview\", \"1\",\t\t\tCVAR_ARCHIVE,\t\"Skip rendering the regular view when OpenXR is active.\",\t\t\t\t\t\t\t\"OpenXR configuration\");\n\t\txr_fingertracking\t= cvarfuncs->GetNVFDG(\"xr_fingertracking\",\t\"0\",\t\t\t0,\t\"Attempt to track individual finger joints.\",\t\t\t\t\t\t\t\t\t\t\t\t\t\"OpenXR configuration\");\n\t\treturn true;\n\t}\n\treturn false;\n}\n"
  },
  {
    "path": "plugins/plugin.c",
    "content": "//contains generic plugin code for dll/qvm\n//it's this one or the engine...\n#include \"plugin.h\"\n#include <stdarg.h>\n#include <stdio.h>\n\n\nplugcorefuncs_t *plugfuncs;\nplugcmdfuncs_t *cmdfuncs;\nplugcvarfuncs_t *cvarfuncs;\n//plugclientfuncs_t *clientfuncs;\n\n\n\n\n/* An implementation of some 'standard' functions */\nvoid Q_strlncpy(char *d, const char *s, int sizeofd, int lenofs)\n{\n\tint i;\n\tsizeofd--;\n\tif (sizeofd < 0)\n\t\treturn;\t//this could be an error\n\n\tfor (i=0; lenofs-- > 0; i++)\n\t{\n\t\tif (i == sizeofd)\n\t\t\tbreak;\n\t\t*d++ = *s++;\n\t}\n\t*d='\\0';\n}\nvoid Q_strlcpy(char *d, const char *s, int n)\n{\n\tint i;\n\tn--;\n\tif (n < 0)\n\t\treturn;\t//this could be an error\n\n\tfor (i=0; *s; i++)\n\t{\n\t\tif (i == n)\n\t\t\tbreak;\n\t\t*d++ = *s++;\n\t}\n\t*d='\\0';\n}\nvoid Q_strlcat(char *d, const char *s, int n)\n{\n\tif (n)\n\t{\n\t\tint dlen = strlen(d);\n\t\tint slen = strlen(s)+1;\n\t\tif (slen > (n-1)-dlen)\n\t\t\tslen = (n-1)-dlen;\n\t\tmemcpy(d+dlen, s, slen);\n\t\td[n - 1] = 0;\n\t}\n}\n\nchar *Plug_Info_ValueForKey (const char *s, const char *key, char *out, size_t outsize)\n{\n\tint isvalue = 0;\n\tconst char *start;\n\tchar *oout = out;\n\t*out = 0;\n\tif (*s != '\\\\')\n\t\treturn out;\t//gah, get lost with your corrupt infostrings.\n\n\tstart = ++s;\n\twhile(1)\n\t{\n\t\twhile(s[0] == '\\\\' && s[1] == '\\\\')\n\t\t\ts+=2;\n\t\tif (s[0] != '\\\\' && *s)\n\t\t{\n\t\t\ts++;\n\t\t\tcontinue;\n\t\t}\n\n\t\t//okay, it terminates here\n\t\tisvalue = !isvalue;\n\t\tif (isvalue)\n\t\t{\n\t\t\tif (strlen(key) == (size_t)(s - start) && !strncmp(start, key, s - start))\n\t\t\t{\n\t\t\t\ts++;\n\t\t\t\twhile (outsize --> 1)\n\t\t\t\t{\n\t\t\t\t\tif (s[0] == '\\\\' && s[1] == '\\\\')\n\t\t\t\t\t\ts++;\n\t\t\t\t\telse if (s[0] == '\\\\' || !s[0])\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t*out++ = *s++;\n\t\t\t\t}\n\t\t\t\t*out++ = 0;\n\t\t\t\treturn oout;\n\t\t\t}\n\t\t}\n\t\tif (*s)\n\t\t\tstart = ++s;\n\t\telse\n\t\t\tbreak;\n\t}\n\treturn oout;\n}\n\n\n//returns true on truncation\nqboolean VARGS Q_vsnprintfz (char *dest, size_t size, const char *fmt, va_list argptr)\n{\n\tsize_t ret;\n#ifdef _WIN32\n\t//doesn't null terminate.\n\t//returns -1 on truncation\n\tret = _vsnprintf (dest, size, fmt, argptr);\n\tdest[size-1] = 0;\t//shitty paranoia\n#else\n\t//always null terminates.\n\t//returns length regardless of truncation.\n\tret = vsnprintf (dest, size, fmt, argptr);\n#endif\n#ifdef _DEBUG\n\tif (ret>=size)\n\t\tplugfuncs->Error(\"Q_vsnprintfz: Truncation\\n\");\n#endif\n\t//if ret is -1 (windows oversize, or general error) then it'll be treated as unsigned so really long. this makes the following check quite simple.\n\treturn (ret>=size) ? qtrue : qfalse;\n}\n//windows/linux have inconsistant snprintf\n//this is an attempt to get them consistant and safe\n//size is the total size of the buffer\n//returns true on overflow (will be truncated).\nqboolean VARGS Q_snprintfz (char *dest, size_t size, const char *fmt, ...)\n{\n\tva_list\t\targptr;\n\tsize_t ret;\n\n\tva_start (argptr, fmt);\n#ifdef _WIN32\n\t//doesn't null terminate.\n\t//returns -1 on truncation\n\tret = _vsnprintf (dest, size, fmt, argptr);\n\tdest[size-1] = 0;\t//shitty paranoia\n#else\n\t//always null terminates.\n\t//returns length regardless of truncation.\n\tret = vsnprintf (dest, size, fmt, argptr);\n#endif\n\tva_end (argptr);\n#ifdef _DEBUG\n\tif (ret>=size)\n\t\tplugfuncs->Error(\"Q_vsnprintfz: Truncation\\n\");\n#endif\n\t//if ret is -1 (windows oversize, or general error) then it'll be treated as unsigned so really long. this makes the following check quite simple.\n\treturn (ret>=size) ? qtrue : qfalse;\n}\n\nchar\t*va(const char *format, ...)\t//Identical in function to the one in Quake, though I can assure you that I wrote it...\n{\t\t\t\t\t//It's not exactly hard, just easy to use, so gets duplicated lots.\n\tva_list\t\targptr;\n\tstatic char\t\tstring[1024];\n\t\t\n\tva_start (argptr, format);\n\tQ_vsnprintfz (string, sizeof(string), format,argptr);\n\tva_end (argptr);\n\n\treturn string;\t\n}\n\n#ifdef _WIN32\n// don't use these functions in MSVC8\n#if (_MSC_VER < 1400)\nint QDECL linuxlike_snprintf(char *buffer, int size, const char *format, ...)\n{\n#undef _vsnprintf\n\tint ret;\n\tva_list\t\targptr;\n\n\tif (size <= 0)\n\t\treturn 0;\n\tsize--;\n\n\tva_start (argptr, format);\n\tret = _vsnprintf (buffer,size, format,argptr);\n\tva_end (argptr);\n\n\tbuffer[size] = '\\0';\n\n\treturn ret;\n}\nint QDECL linuxlike_vsnprintf(char *buffer, int size, const char *format, va_list argptr)\n{\n#undef _vsnprintf\n\tint ret;\n\n\tif (size <= 0)\n\t\treturn 0;\n\tsize--;\n\n\tret = _vsnprintf (buffer,size, format,argptr);\n\n\tbuffer[size] = '\\0';\n\n\treturn ret;\n}\n#elif (_MSC_VER < 1900)\nint VARGS linuxlike_snprintf_vc8(char *buffer, int size, const char *format, ...)\n{\n\tint ret;\n\tva_list\t\targptr;\n\n\tva_start (argptr, format);\n\tret = vsnprintf_s (buffer,size, _TRUNCATE, format,argptr);\n\tva_end (argptr);\n\n\treturn ret;\n}\n#endif\n#endif\n\nvoid Con_Printf(const char *format, ...)\n{\n\tva_list\t\targptr;\n\tstatic char\t\tstring[1024];\n\t\t\n\tva_start (argptr, format);\n\tQ_vsnprintfz (string, sizeof(string), format,argptr);\n\tva_end (argptr);\n\n\tplugfuncs->Print(string);\n}\nvoid Con_DPrintf(const char *format, ...)\n{\n\tva_list\t\targptr;\n\tstatic char\t\tstring[1024];\n\n\tif (!cvarfuncs->GetFloat(\"developer\"))\n\t\treturn;\n\t\t\n\tva_start (argptr, format);\n\tQ_vsnprintfz (string, sizeof(string), format,argptr);\n\tva_end (argptr);\n\n\tplugfuncs->Print(string);\n}\nvoid Sys_Errorf(const char *format, ...)\n{\n\tva_list\t\targptr;\n\tstatic char\t\tstring[1024];\n\t\t\n\tva_start (argptr, format);\n\tQ_vsnprintfz (string, sizeof(string), format,argptr);\n\tva_end (argptr);\n\n\tplugfuncs->Error(string);\n}\n\n\nqboolean ZF_ReallocElements(void **ptr, size_t *elements, size_t newelements, size_t elementsize)\n{\n\tvoid *n;\n\tsize_t oldsize;\n\tsize_t newsize;\n\n\t//protect against malicious overflows\n\tif (newelements > SIZE_MAX / elementsize)\n#ifdef __cplusplus\n\t\treturn qfalse;\n#else\n\t\treturn false;\n#endif\n\n\toldsize = *elements * elementsize;\n\tnewsize = newelements * elementsize;\n\n\tn = plugfuncs->Realloc(*ptr, newsize);\n\tif (!n)\n#ifdef __cplusplus\n\t\treturn qfalse;\n#else\n\t\treturn false;\n#endif\n\tif (newsize > oldsize)\n\t\tmemset((char*)n+oldsize, 0, newsize - oldsize);\n\t*elements = newelements;\n\t*ptr = n;\n#ifdef __cplusplus\n\t\treturn qtrue;\n#else\n\t\treturn true;\n#endif\n}\n\n// begin common.c\nshort\t\tShortSwap\t(short l)\n{\n\treturn\t((l>> 8)&0x00ff)|\n\t\t\t((l<< 8)&0xff00);\n}\nint\t\t\tLongSwap\t(int l)\n{\n\treturn\t((l>>24)&0x000000ff)|\n\t\t\t((l>> 8)&0x0000ff00)|\n\t\t\t((l<< 8)&0x00ff0000)|\n\t\t\t((l<<24)&0xff000000);\n}\nqint64_t    I64Swap\t\t(qint64_t l)\n{\n\treturn\t((l>>56)&        0x000000ff)|\n\t\t\t((l>>40)&        0x0000ff00)|\n\t\t\t((l>>24)&        0x00ff0000)|\n\t\t\t((l>> 8)&        0xff000000)|\n\t\t\t((l<< 8)&0x000000ff00000000)|\n\t\t\t((l<<24)&0x0000ff0000000000)|\n\t\t\t((l<<40)&0x00ff000000000000)|\n\t\t\t((l<<56)&0xff00000000000000);\n}\nfloat FloatSwap (float f)\n{\n\tunion\n\t{\n\t\tfloat\tf;\n\t\tqbyte\tb[4];\n\t} dat1, dat2;\n\n\n\tdat1.f = f;\n\tdat2.b[0] = dat1.b[3];\n\tdat2.b[1] = dat1.b[2];\n\tdat2.b[2] = dat1.b[1];\n\tdat2.b[3] = dat1.b[0];\n\treturn dat2.f;\n}\n// end common.c\n\n#ifdef __cplusplus\nextern \"C\"\n#endif\nqboolean NATIVEEXPORT FTEPlug_Init(plugcorefuncs_t *corefuncs)\n{\n\tplugfuncs = corefuncs;\n\tcmdfuncs = (plugcmdfuncs_t*)plugfuncs->GetEngineInterface(plugcmdfuncs_name, sizeof(*cmdfuncs));\n\tcvarfuncs = (plugcvarfuncs_t*)plugfuncs->GetEngineInterface(plugcvarfuncs_name, sizeof(*cvarfuncs));\n\tif (!plugfuncs || !cmdfuncs || !cvarfuncs)\n\t\treturn qfalse; //erk\n\n\treturn Plug_Init();\n}\n"
  },
  {
    "path": "plugins/plugin.def",
    "content": "EXPORTS\n\tFTEPlug_Init\n"
  },
  {
    "path": "plugins/plugin.h",
    "content": "#ifndef __PLUGIN_H__\n#define __PLUGIN_H__\n\n#ifdef FTEENGINE\n\t//included from fte itself, to borrow typedefs\n#elif defined(FTEPLUGIN)\n\t//plugin that needs fte internals\n\t#include \"quakedef.h\"\n#else\n\t//moderately generic plugin\n\t#ifdef __cplusplus\n\t\ttypedef enum {qfalse, qtrue} qboolean;\n\t#else\n\t\ttypedef enum {qfalse, qtrue} qboolean;\n\t\t#define false qfalse\n\t\t#define true qtrue\n\t#endif\n\ttypedef float vec4_t[4];\n\ttypedef float vec3_t[3];\n\ttypedef float vec2_t[2];\n\ttypedef unsigned char qbyte;\n\n\t#include <stdint.h>\n\t#define qint64_t int64_t\n\t#define quint64_t uint64_t\n\ttypedef quint64_t qofs_t;\n\n\ttypedef struct cvar_s cvar_t;\n\ttypedef struct usercmd_s usercmd_t;\n\ttypedef struct vfsfile_s vfsfile_t;\n\ttypedef struct netadr_s netadr_t;\n\tenum fs_relative;\n\tstruct searchpathfuncs_s;\n#endif\n\n#ifdef _WIN32\n#\tifndef strcasecmp\n#\t\tdefine strcasecmp stricmp\n#\t\tdefine strncasecmp strnicmp\n#\tendif\n#\tif defined(_MSC_VER) && _MSC_VER >= 1900\n#\t\tdefine Q_vsnprintf vsnprintf\n#\t\tdefine Q_snprintf snprintf\n#\tendif\n#else\n#\tdefine stricmp Q_strcasecmp\n#\tdefine strnicmp Q_strncasecmp\n#endif\n\n#include <string.h>\n#include <stdlib.h>\n#include <stdarg.h>\n#include <math.h>\n#include <time.h>\n\n#ifndef _VM_H\n\t#if __STDC_VERSION__ >= 199901L || defined(__GNUC__)\n\t\t//C99 has a stdint header which hopefully contains an intptr_t\n\t\t//its optional... but if its not in there then its unlikely you'll actually be able to get the engine to a stage where it *can* load anything\n\t\t#include <stdint.h>\n\t\t#define qintptr_t intptr_t\n\t\t#define quintptr_t uintptr_t\n\t#else\n\t\t#ifdef _WIN64\n\t\t\ttypedef long long qintptr_t;\n\t\t\ttypedef unsigned long long quintptr_t;\n\t\t#else\n\t\t\t#if !defined(_MSC_VER) || _MSC_VER < 1300\n\t\t\t\t#define __w64\n\t\t\t#endif\n\t\t\ttypedef long __w64 qintptr_t;\n\t\t\ttypedef unsigned long __w64 quintptr_t;\n\t\t#endif\n\t#endif\n#endif\n\n#ifndef NATIVEEXPORT\n\t#ifdef _WIN32\n\t\t#define NATIVEEXPORTPROTO __declspec(dllexport)\n\t\t#define NATIVEEXPORT NATIVEEXPORTPROTO\n\t#else\n\t\t#define NATIVEEXPORTPROTO\n\t\t#define NATIVEEXPORT __attribute__((visibility(\"default\")))\n\t#endif\n#endif\n\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n//DLLs need a wrapper to add the extra parameter and call a boring function.\n#ifndef QDECL\n#ifdef _WIN32\n#define QDECL __cdecl\n#else\n#define QDECL\n#endif\n#endif\n\n#ifndef LIKEPRINTF\n\t#if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1))\n\t\t#define LIKEPRINTF(x) __attribute__((format(printf,x,x+1)))\n\t#else\n\t\t#define LIKEPRINTF(x)\n\t#endif\n#endif\n\n#ifndef NATIVEEXPORT\n#define NATIVEEXPORT QDECL\n#endif\n\ntypedef int qhandle_t;\ntypedef void* funcptr_t;\n\n\n#define PLUGMAX_SCOREBOARDNAME 64\ntypedef struct {\n\tint topcolour;\n\tint bottomcolour;\n\tint frags;\n\tchar name[PLUGMAX_SCOREBOARDNAME];\n\tint ping;\n\tint pl;\n\tint activetime;\n\tint userid;\n\tint spectator;\n\tchar userinfo[2048];\n\tchar team[64];\n} plugclientinfo_t;\n\ntypedef struct\n{\n\tunsigned int client;\n\tunsigned int items;\n\tfloat armor;\n\tfloat health;\n\tvec3_t org;\n\tchar nick[16];\n} teamplayerinfo_t;\n\ntypedef struct {\n\tsize_t structsize;\n\tint seats;\n\tstruct\n\t{\n\t\tfloat s_avg;\n\t\tfloat s_mn;\n\t\tfloat s_mx;\n\t\tfloat ms_stddev;\t//calculated in milliseconds for more sane numbers\n\t\tfloat fr_avg;\n\t\tint fr_mn;\n\t\tint fr_mx;\n\t} ping;\n\tstruct\n\t{\t//decimals\n\t\tfloat dropped;\n\t\tfloat choked;\n\t\tfloat invalid;\n\t} loss;\n\tfloat mlatency;\n\tfloat mrate;\n\tfloat vlatency;\n\tfloat vrate;\n\tvec3_t speed;\t//player speed\n\n\tstruct\n\t{\n\t\tfloat in_pps;\n\t\tfloat in_bps;\n\t\tfloat out_pps;\n\t\tfloat out_bps;\n\t} clrate;\n\tstruct\n\t{\n\t\tfloat in_pps;\n\t\tfloat in_bps;\n\t\tfloat out_pps;\n\t\tfloat out_bps;\n\t} svrate;\n\tint capturing;\t//avi capturing\n} plugnetinfo_t;\n\nstruct wstats_s;\n\n\n#define F(t, n, args) t (QDECL *n) args\n#define dllhandle_t void\nstruct dllfunction_s;\nstruct zonegroup_s;\ntypedef struct\t//core stuff\n{\n\t//Basic builtins:\n\tF(void*,\tGetEngineInterface,\t(const char *interfacename, size_t structsize));\t//retrieve a named interface struct from the engine\n\tF(qboolean, ExportFunction,\t\t(const char *funcname, funcptr_t funcptr));\t\t\t//export a named function to the engine\n\tF(qboolean, ExportInterface,\t(const char *interfacename, void *interfaceptr, size_t structsize)); //export a named interface struct to the engine\n\tF(qboolean, GetPluginName,\t\t(int plugnum, char *buffer, size_t bufsize));\t\t\t\t//query loaded plugin names. -1 == active plugin\n\tF(void,\t\tPrint,\t\t\t\t(const char *message));\t//print on (main) console.\n\tF(void,\t\tError,\t\t\t\t(const char *message, ...));\t//abort the entire engine.\n\tF(void,\t\tEndGame,\t\t\t(const char *reason, ...));\t//some sort of networking problem happened and we need to disconnect. Engine can continue running (displaying the message to the user).\n\tF(quintptr_t,GetMilliseconds,\t(void));\n\tF(double,\tGetSeconds,\t\t\t(void));\n\n\t//for soft linking (with more readable error messages).\n\tF(dllhandle_t*,LoadDLL,\t\t\t(const char *modulename, struct dllfunction_s *funcs));\n\tF(void*,\tGetDLLSymbol,\t\t(dllhandle_t *handle, const char *symbolname));\n\tF(void,\t\tCloseDLL,\t\t\t(dllhandle_t *handle));\t//not guarenteed to actually do anything, of course.\n\n\t//general memory (mallocs and frees over dll boundaries is not usable on windows)\n\tF(void*,\tMalloc,\t\t\t\t(size_t size));\n\tF(void*,\tRealloc,\t\t\t(void *memptr, size_t size));   //doesn't zero-fill, so faster (when memptr is NULL).\n\tF(void,\t\tFree,\t\t\t\t(void *memptr));\n\n\t//for lazy mallocs\n\tF(void*,\tGMalloc,\t\t\t(struct zonegroup_s *ctx, size_t size));\n\tF(void,\t\tGFree,\t\t\t\t(struct zonegroup_s *ctx, void *ptr));\n\tF(void,\t\tGFreeAll,\t\t\t(struct zonegroup_s *ctx));\n#define plugcorefuncs_name \"Core\"\n} plugcorefuncs_t;\n\ntypedef struct\t//subconsole handling\n{\n\tF(qhandle_t,POpen,\t\t\t\t(const char *conname));\n\tF(qboolean,\tSubPrint,\t\t\t(const char *subname, const char *text));\t//on to sub console.\n\tF(qboolean,\tRenameSub,\t\t\t(const char *oldname, const char *newname));\t//rename a console.\n\tF(qboolean,\tIsActive,\t\t\t(const char *conname));\n\tF(qboolean,\tSetActive,\t\t\t(const char *conname));\n\tF(qboolean,\tDestroy,\t\t\t(const char *conname));\n\tF(qboolean,\tNameForNum,\t\t\t(qintptr_t connum, char *conname, size_t connamelen));\n\tF(float,\tGetConsoleFloat,\t(const char *conname, const char *attribname));\n\tF(qboolean,\tSetConsoleFloat,\t(const char *conname, const char *attribname, float newvalue));\n\tF(qboolean,\tGetConsoleString,\t(const char *conname, const char *attribname, char *outvalue, size_t valuesize));\n\tF(qboolean,\tSetConsoleString,\t(const char *conname, const char *attribname, const char *newvalue));\n#define plugsubconsolefuncs_name \"SubConsole\"\n} plugsubconsolefuncs_t;\n\nenum com_tokentype_e;\ntypedef struct\t//console command/tokenizing/cbuf functions\n{\n\tF(const char *,QuotedString,\t(const char *string, char *buf, int buflen, qboolean omitquotes));\t//generates a string with c-style markup and relevant quote types.\n\tF(char *,\tParseToken,\t\t\t(const char *data, char *token, size_t tokenlen, enum com_tokentype_e *tokentype));\t//standard quake-style token parsing.\n\tF(char *,\tParsePunctuation,\t(const char *data, const char *punctuation, char *token, size_t tokenlen, enum com_tokentype_e *tokentype));\t//use explicit punctuation.\n\n\tF(void,\t\tTokenizeString,\t\t(const char *msg));\t//tokenize a string.\n\tF(void,\t\tShiftArgs,\t\t\t(int args));\t//updates tokenize state to ignore arg 0 (and updates Args).\n\n\tF(void,\t\tArgs,\t\t\t\t(char *buffer, int bufsize));\t//Gets the extra args\n\tF(char *,\tArgv,\t\t\t\t(int argnum, char *buffer, size_t bufsize));\t//Gets a 0-based token\n\tF(int,\t\tArgc,\t\t\t\t(void));\t//gets the number of tokens available.\n\n\tF(qboolean,\tIsInsecure,\t\t\t(void));\n\tF(qboolean,\tAddCommand,\t\t\t(const char *cmdname, void (*func)(void), const char *desc));\t//Registers a console command.\n\n\tF(void,\t\tAddText,\t\t\t(const char *text, qboolean insert));\n#define plugcmdfuncs_name \"Cmd\"\n} plugcmdfuncs_t;\n\ntypedef struct\t//console command and cbuf functions\n{\n\tF(void,\t\tSetString,\t\t\t(const char *name, const char *value));\n\tF(void,\t\tSetFloat,\t\t\t(const char *name, float value));\n\tF(qboolean,\tGetString,\t\t\t(const char *name, char *retstring, quintptr_t sizeofretstring));\n\tF(float,\tGetFloat,\t\t\t(const char *name));\n\tF(cvar_t*,\tGetNVFDG,\t\t\t(const char *name, const char *defaultval, unsigned int flags, const char *description, const char *groupname));\n\tF(void,\t\tForceSetString,\t\t(const char *name, const char *value));\n#define plugcvarfuncs_name \"Cvar\"\n} plugcvarfuncs_t;\n\ntypedef struct\n{\n\tF(void,\t\t\tLocalSound,\t\t\t(const char *soundname, int channel, float volume));\n\tF(void,\t\t\tRawAudio,\t\t\t(int sourceid, void *data, int speed, int samples, int channels, int width, float volume));\n\n\tF(void,\t\t\tSpacialize,\t\t\t(unsigned int seat, int entnum, vec3_t origin, vec3_t *axis, int reverb, vec3_t velocity));\n\tF(qboolean,\t\tUpdateReverb,\t\t(size_t slot, void *reverb, size_t reverbsize));\n\tF(struct sfx_s*,PrecacheSound,\t\t(const char *sample));\n\tF(void,\t\t\tStartSound,\t\t\t(int entnum, int entchannel, struct sfx_s *sfx, vec3_t origin, vec3_t velocity, float fvol, float attenuation, float timeofs, float pitchadj, unsigned int flags));\n\tF(float,\t\tGetChannelLevel,\t(int entnum, int entchannel));\n\tF(int,\t\t\tVoip_ClientLoudness,(unsigned int plno));\n\tF(qboolean,\t\tChangeMusicTrack,\t(const char *initialtrack, const char *looptrack));\n\n#define plugaudiofuncs_name \"Audio\"\n} plugaudiofuncs_t;\n\ntypedef struct\n{\n\tF(void,\t\tBlockSizeForEncoding,(uploadfmt_t encoding, unsigned int *blockbytes, unsigned int *blockwidth, unsigned int *blockheight, unsigned int *blockdepth));\n\tF(const char *,FormatName,\t\t(uploadfmt_t encoding));\n#define plugimagefuncs_name \"Image\"\n} plugimagefuncs_t;\n\ntypedef struct\t//q1 client/network info\n{\n\tF(int,\t\tGetStats,\t\t\t(int seat, unsigned int *stats, int maxstats));\n\tF(void,\t\tGetPlayerInfo,\t\t(int seat, plugclientinfo_t *info));\n\tF(size_t,\tGetNetworkInfo,\t\t(plugnetinfo_t *ni, size_t sizeofni));\n\tF(size_t,\tGetLocalPlayerNumbers,(size_t firstseat, size_t numseats, int *playernums, int *spectracks));\n\tF(void,\t\tGetLocationName,\t(const float *pos, char *outbuffer, size_t bufferlen));\n\tF(qboolean,\tGetLastInputFrame,\t(int seat, usercmd_t *outcmd));\n\tF(void,\t\tGetServerInfoRaw,\t(char *info, size_t infolen));\n\tF(size_t,\tGetServerInfoBlob,\t(const char *keyname, void *buf, size_t bufsize)); //pass null buf to query size, returns 0 if it would truncate. does not null terminate.\n\tF(void,\t\tSetUserInfo,\t\t(int seat, const char *key, const char *value));\n\tF(void,\t\tSetUserInfoBlob,\t(int seat, const char *key, const void *value, size_t size));\n\tF(size_t,\tGetUserInfoBlob,\t(int seat, const char *key, void *buf, size_t bufsize)); //pass null buf to query size, returns 0 if it would truncate. does not null terminate.\n\t//EBUILTIN(void, SCR_CenterPrint, (const char *s));\n\n\t//FIXME: does this belong here?\n\tF(qboolean,\tMapLog_Query,\t\t(const char *packagename, const char *mapname, float *stats));\n\n\tF(size_t,\tGetTeamInfo,\t\t(teamplayerinfo_t *clients, size_t maxclients, qboolean showenemies, int seat));\n\tF(int,\t\tGetWeaponStats,\t\t(int player, struct wstats_s *result, size_t maxresults));\n\tF(float,\tGetTrackerOwnFrags,\t(int seat, char *text, size_t textsize));\n\tF(void,\t\tGetPredInfo,\t\t(int seat, vec3_t outvel));\n\n\tF(void,\t\tClearNotify,\t\t(void));\t//called for fast map restarts.\n\tF(void,\t\tClearClientState,\t(void));\t//called at the start of map changes.\n\tF(void,\t\tSetLoadingState,\t(qboolean newstate));\t//Change the client's loading screen state.\n\tF(void,\t\tUpdateGameTime,\t\t(double));\t//tells the client an updated snapshot time for interpolation/timedrift.\n\n\tvoid (*ForceCheatVars)\t\t\t(qboolean semicheats, qboolean absolutecheats);\n\tqboolean (*DownloadBegun)(qdownload_t *dl);\n\tvoid (*DownloadFinished)(qdownload_t *dl);\n\tdownloadlist_t *(*DownloadFailed)(const char *name, qdownload_t *qdl, enum dlfailreason_e failreason);\n#define plugclientfuncs_name \"Client2\"\n} plugclientfuncs_t;\n\nstruct menu_s;\ntypedef struct\t//for menu-like stuff\n{\n\t//for menus\n\tF(qboolean,\t\tSetMenuFocus,\t\t(qboolean wantkeyfocus, const char *cursorname, float hot_x, float hot_y, float scale)); //null cursorname=relmouse, set/empty cursorname=absmouse\n\tF(qboolean,\t\tHasMenuFocus,\t\t(void));\n\n\tF(void,\t\t\tMenu_Push,\t\t\t(struct menu_s *menu, qboolean prompt));\n\tF(void,\t\t\tMenu_Unlink,\t\t(struct menu_s *menu, qboolean forced));\n\n\t//for menu input\n\tF(int,\t\t\tGetKeyCode,\t\t\t(const char *keyname, int *out_modifier));\n\tF(const char*,\tGetKeyName,\t\t\t(int keycode, int modifier));\n\tF(int,\t\t\tFindKeysForCommand,\t(int bindmap, const char *command, int *out_keycodes, int *out_modifiers, int maxkeys));\n\tF(const char*,\tGetKeyBind,\t\t\t(int bindmap, int keynum, int modifier));\n\tF(void,\t\t\tSetKeyBind,\t\t\t(int bindmap, int keycode, int modifier, const char *newbinding));\n\n\tF(qboolean,\t\tIsKeyDown,\t\t\t(int keycode));\n\tF(void,\t\t\tClearKeyStates,\t\t(void));\t//forget any keys that are still held.\n\tF(void,\t\t\tSetSensitivityScale,(float newsensitivityscale));\t//this is a temporary sensitivity thing.\n\tF(unsigned int,\tGetMoveCount,\t\t(void));\n\tF(usercmd_t*,\tGetMoveEntry,\t\t(unsigned int move));\t//GetMoveEntry(GetMoveCount()) gives you the partial entry. forgotten entries return NULL.\n\n\tvoid (*ClipboardGet) (clipboardtype_t clipboardtype, void (*callback)(void *ctx, const char *utf8), void *ctx);\n\tvoid (*ClipboardSet) (clipboardtype_t clipboardtype, const char *utf8);\n\tunsigned int (*utf8_decode)(int *error, const void *in, char const**out);\n\tunsigned int (*utf8_encode)(void *out, unsigned int unicode, int maxlen);\n\n\tunsigned int (*GetKeyDest)\t\t(void);\n\tvoid (*KeyEvent)\t\t\t\t(unsigned int devid, int down, int keycode, int unicode);\n\tvoid (*MouseMove)\t\t\t\t(unsigned int devid, int abs, float x, float y, float z, float size);\n\tvoid (*JoystickAxisEvent)\t\t(unsigned int devid, int axis, float value);\n\tvoid (*Accelerometer)\t\t\t(unsigned int devid, float x, float y, float z);\n\tvoid (*Gyroscope)\t\t\t\t(unsigned int devid, float pitch, float yaw, float roll);\n\tqboolean (*SetHandPosition)\t\t(const char *devname, vec3_t org, vec3_t ang, vec3_t vel, vec3_t avel);\t//for VR.\n#define pluginputfuncs_name \"Input\"\n} pluginputfuncs_t;\n\n#if defined(FTEENGINE) || defined(FTEPLUGIN)\ntypedef struct\n{\n\tF(void,\t\tBeginReading,\t\t(sizebuf_t *sb, struct netprim_s prim));\n\tF(int,\t\tReadCount,\t\t\t(void));\n\tF(int,\t\tReadBits,\t\t\t(int bits));\n\tF(int,\t\tReadByte,\t\t\t(void));\n\tF(int,\t\tReadShort,\t\t\t(void));\n\tF(int,\t\tReadLong,\t\t\t(void));\n\tF(void,\t\tReadData,\t\t\t(void *data, int len));\n\tF(char*,\tReadString,\t\t\t(void));\n\n\tF(void,\t\tBeginWriting,\t\t(sizebuf_t *sb, struct netprim_s prim, void *bufferstorage, size_t buffersize));\n\tF(void,\t\tWriteBits,\t\t\t(sizebuf_t *sb, int value, int bits));\n\tF(void,\t\tWriteByte,\t\t\t(sizebuf_t *sb, int c));\n\tF(void,\t\tWriteShort,\t\t\t(sizebuf_t *sb, int c));\n\tF(void,\t\tWriteLong,\t\t\t(sizebuf_t *sb, int c));\n\tF(void,\t\tWriteData,\t\t\t(sizebuf_t *sb, const void *data, int len));\n\tF(void,\t\tWriteString,\t\t(sizebuf_t *sb, const char *s));\n\n\tF(qboolean,\tCompareAdr,\t\t\t(netadr_t *a, netadr_t *b));\n\tF(qboolean,\tCompareBaseAdr,\t\t(netadr_t *a, netadr_t *b));\n\tF(char*,\tAdrToString,\t\t(char *s, int len, netadr_t *a));\n\tF(size_t,\tStringToAdr,\t\t(const char *s, int defaultport, netadr_t *a, size_t addrcount, const char **pathstart));\n\tF(neterr_t,\tSendPacket,\t\t\t(struct ftenet_connections_s *col, int length, const void *data, netadr_t *to));\n#ifdef HUFFNETWORK\n\tF(huffman_t*,Huff_CompressionCRC,\t(int crc));\n\tF(void,\t\tHuff_EncryptPacket,\t\t(sizebuf_t *msg, int offset));\n\tF(void,\t\tHuff_DecryptPacket,\t\t(sizebuf_t *msg, int offset));\n#endif\n#define plugmsgfuncs_name \"Messaging\"\n} plugmsgfuncs_t;\n#endif\n\ntypedef struct\t//for huds and menus alike\n{\n\tF(qboolean,\tGetVideoSize,\t(float *vsize, unsigned int *psize));\t//returns false if there's no video yet...\n\t//note: these use handles instead of shaders, to make them persistent over renderer restarts.\n\tF(qhandle_t,LoadImageData,\t(const char *name, const char *mime, void *data, size_t datasize));\t//load/replace a named texture\n\tF(qhandle_t,LoadImageShader,(const char *name, const char *defaultshader));\t//loads a shader.\n\tF(qhandle_t,LoadImage,\t\t(const char *name));\t//wad image is ONLY for loading out of q1 gfx.wad. loads a shader. use gfx/foo.lmp for hud stuff.\n\tF(struct shader_s*,ShaderFromId,\t(qhandle_t shaderid));\n\tF(void,\t\tUnloadImage,\t(qhandle_t image));\n\tF(int,\t\tImage,\t\t\t(float x, float y, float w, float h, float s1, float t1, float s2, float t2, qhandle_t image));\n\tF(int,\t\tImage2dQuad,\t(const vec2_t *points, const vec2_t *tcoords, const vec4_t *colours, qhandle_t image));\n\tF(int,\t\tImageSize,\t\t(qhandle_t image, float *x, float *y));\n\tF(void,\t\tFill,\t\t\t(float x, float y, float w, float h));\n\tF(void,\t\tLine,\t\t\t(float x1, float y1, float x2, float y2));\n\tF(void,\t\tCharacter,\t\t(float x, float y, unsigned int character));\n\tF(void,\t\tString,\t\t\t(float x, float y, const char *string));\n\tF(void,\t\tCharacterH,\t\t(float x, float y, float h, unsigned int flags, unsigned int character));\n\tF(void,\t\tStringH,\t\t(float x, float y, float h, unsigned int flags, const char *string));\t//returns the vpixel width of the (coloured) string, in the current (variable-width) font.\n\tF(float,\tStringWidth,\t(float h, unsigned int flags, const char *string));\n\tF(void,\t\tColourpa,\t\t(int palcol, float a));\t//for legacy code\n\tF(void,\t\tColour4f,\t\t(float r, float g, float b, float a));\n\n\tF(void,\t\tRedrawScreen,\t(void));\t//redraws the entire screen and presents it. for loading screen type things.\n\n\tF(void,\t\tLocalSound,\t\t(const char *soundname, int channel, float volume));\n\n\tstruct\n\t{\n\t\t//basic media poking\n\t\tF(struct cin_s *,\tGetCinematic,\t(struct shader_s *s));\n\t\tF(void,\t\t\t\tSetState,\t\t(struct cin_s *cin, int newstate));\n\t\tF(int,\t\t\t\tGetState,\t\t(struct cin_s *cin));\n\t\tF(void,\t\t\t\tReset,\t\t\t(struct cin_s *cin));\n\n\t\t//complex media poking (web browser stuff)\n\t\tF(void,\t\t\t\tCommand,\t\t(struct cin_s *cin, const char *command));\n\t\tF(const char *,\t\tGetProperty,\t(struct cin_s *cin, const char *key));\n\t\tF(void,\t\t\t\tMouseMove,\t\t(struct cin_s *cin, float x, float y));\n\t\tF(void,\t\t\t\tResize,\t\t\t(struct cin_s *cin, int x, int y));\n\t\tF(void,\t\t\t\tGetSize,\t\t(struct cin_s *cin, int *x, int *y, float *aspect));\n\t\tF(void,\t\t\t\tKeyEvent,\t\t(struct cin_s *cin, int button, int unicode, int event));\n\t} media;\n#define plug2dfuncs_name \"2D\"\n} plug2dfuncs_t;\n\n#if defined(FTEENGINE) || defined(FTEPLUGIN)\nstruct entity_s;\ntypedef struct\n{\n\tstruct\n\t{\n\t\tfloat x,y,w,h;\n\t} rect;\n\tvec2_t fov;\n\tvec2_t fov_viewmodel;\n\tvec3_t viewaxisorg[4];\n\tvec3_t skyroom_org;\n\tfloat time;\n\tunsigned int flags;\n} plugrefdef_t;\ntypedef struct\t//for huds and menus alike\n{\n\tF(model_t *,\tLoadModel,\t\t\t(const char *modelname, enum mlverbosity_e sync));\n\tF(qhandle_t,\tModelToId,\t\t\t(model_t *model));\n\tF(model_t *,\tModelFromId,\t\t(qhandle_t modelid));\n\n\tF(void,\t\t\tRemapShader,\t\t(const char *sourcename, const char *destname, float timeoffset));\n\tF(qhandle_t,\tShaderForSkin,\t\t(qhandle_t modelid, int surfaceidx, int skinnum, float time));\n\tF(skinid_t,\t\tRegisterSkinFile,\t(const char *skinname));\n\tF(skinfile_t *,\tLookupSkin,\t\t\t(skinid_t id));\n\tF(int,\t\t\tTagNumForName,\t\t(struct model_s *model, const char *name, int firsttag));\n\tF(qboolean,\t\tGetTag,\t\t\t\t(struct model_s *model, int tagnum, framestate_t *framestate, float *transforms));\n\tF(void,\t\t\tClipDecal,\t\t\t(struct model_s *mod, vec3_t center, vec3_t normal, vec3_t tangent1, vec3_t tangent2, float size, unsigned int surfflagmask, unsigned int surflagmatch, void (*callback)(void *ctx, vec3_t *fte_restrict points, size_t numpoints, shader_t *shader), void *ctx));\n\n\tF(void,\t\t\tNewMap,\t\t\t\t(model_t *worldmode));\n\tF(void,\t\t\tClearScene,\t\t\t(void));\n\tF(void,\t\t\tAddEntity,\t\t\t(struct entity_s *ent));\n\tF(unsigned int,\tAddPolydata,\t\t(struct shader_s *s, unsigned int befflags, size_t numverts, size_t numidx, vecV_t **vertcoord, vec2_t **texcoord, vec4_t **colour, index_t **indexes));\t//allocates space for some polygons\n\tF(dlight_t *,\tNewDlight,\t\t\t(int key, const vec3_t origin, float radius, float time, float r, float g, float b));\n\tF(dlight_t *,\tAllocDlightOrg,\t\t(int keyidx, vec3_t keyorg));\n\tF(qboolean,\t\tCalcModelLighting,\t(entity_t *e, model_t *clmodel));\n\tF(void,\t\t\tRenderScene,\t\t(plugrefdef_t *viewer, size_t areabytes, const qbyte *areadata));\n#define plug3dfuncs_name \"3D\"\n} plug3dfuncs_t;\n\ntypedef struct\t//for collision stuff\n{\n\tF(model_t *,\tLoadModel,\t\t\t(const char *modelname, enum mlverbosity_e sync));\n\tF(const char *,\tFixName,\t\t\t(const char *modname, const char *worldname));\n\tF(const char *,\tGetEntitiesString,\t(struct model_s *mod));\n\n\tF(qboolean,\t\tTransformedTrace,\t(struct model_s *model, int hulloverride, framestate_t *framestate, vec3_t start, vec3_t end, vec3_t mins, vec3_t maxs, qboolean capsule, struct trace_s *trace, vec3_t origin, vec3_t angles, unsigned int hitcontentsmask));\n\tF(model_t *,\tTempBoxModel,\t\t(const vec3_t mins, const vec3_t maxs));\n\n\t//general things that probably shouldn't be here...\n\tF(size_t,\t\tIBufToInfo,\t\t\t(infobuf_t *info, char *infostring, size_t maxsize, const char **priority, const char **ignore, const char **exclusive, infosync_t *sync, void *synccontext));\n\tF(void,\t\t\tIBufFromInfo,\t\t(infobuf_t *info, const char *infostring, qboolean append));\n\tF(qboolean,\t\tSetIBufKey,\t\t\t(infobuf_t *info, const char *key, const char *val));\n\tF(char *,\t\tGetIBufKey,\t\t\t(infobuf_t *info, const char *key));\n\tF(char*,\t\tGetInfoKey,\t\t\t(const char *s, const char *key));\n\tF(void,\t\t\tSetInfoKey,\t\t\t(char *s, const char *key, const char *value, int maxsize));\n\n\t//server things, shouldn't really be here but small. null in client-only builds\n\tF(void,\t\t\tDropClient,\t\t\t(struct client_s *drop));\n\tF(void,\t\t\tExtractFromUserinfo,(struct client_s *cl, qboolean verbose));\n\tF(qboolean,\t\tChallengePasses,\t(int challenge));\n#define plugworldfuncs_name \"World\"\n} plugworldfuncs_t;\n\ntypedef struct\t//for querying master servers\n{\n\tF(size_t,\t\tStringToAdr,\t\t(const char *s, int defaultport, netadr_t *a, size_t numaddresses, const char **pathstart));\n\tF(char *,\t\tAdrToString,\t\t(char *s, int len, netadr_t *a));\n\tF(struct serverinfo_s*,InfoForServer,(netadr_t *addr, const char *brokerid));\n\tF(qboolean,\t\tQueryServers,\t\t(void));\n\tF(void,\t\t\tQueryServer,\t\t(struct serverinfo_s *server));\n\tF(void,\t\t\tCheckPollSockets,\t(void));\n\tF(unsigned int,\tTotalCount,\t\t\t(void));\n\tF(struct serverinfo_s*,InfoForNum,\t(int num));\n\tF(char *,\t\tReadKeyString,\t\t(struct serverinfo_s *server, unsigned int keynum));\n\tF(float,\t\tReadKeyFloat,\t\t(struct serverinfo_s *server, unsigned int keynum));\n\tF(void,\t\t\tWriteServers,\t\t(void));\n\tF(char *,\t\tServerToString,\t\t(char *s, int len, struct serverinfo_s *a));\n#define plugmasterfuncs_name \"Master\"\n} plugmasterfuncs_t;\n#endif\n\nstruct flocation_s;\ntypedef struct\t//for plugins that need to read/write files...\n{\n\tF(int,\t\tOpen,\t\t\t(const char *name, qhandle_t *handle, int mode));\n\tF(void,\t\tClose,\t\t\t(qhandle_t handle));\n\tF(int,\t\tWrite,\t\t\t(qhandle_t handle, void *data, int len));\n\tF(int,\t\tRead,\t\t\t(qhandle_t handle, void *data, int len));\n\tF(int,\t\tSeek,\t\t\t(qhandle_t handle, qofs_t offset));\n\tF(qboolean, GetLen,\t\t\t(qhandle_t handle, qofs_t *outsize));\n\n\n\tF(int,\t\tLocateFile,\t\t(const char *filename, unsigned int lflags, struct flocation_s *loc));\n\tF(vfsfile_t*,OpenVFS,\t\t(const char *filename, const char *mode, enum fs_relative relativeto));\t\t//opens a direct vfs file, without any access checks, and so can be used in threaded plugins\n\tF(qboolean,\tNativePath,\t\t(const char *name, enum fs_relative relativeto, char *out, int outlen));\n\tF(qboolean, Rename,\t\t\t(const char *oldf, const char *newf, enum fs_relative relativeto));\n\tF(qboolean,\tRemove,\t\t\t(const char *fname, enum fs_relative relativeto));\n\n\tF(void,\t\tEnumerateFiles,\t(enum fs_relative fsroot, const char *match, int (QDECL *callback)(const char *fname, qofs_t fsize, time_t mtime, void *ctx, struct searchpathfuncs_s *package), void *ctx));\n\n\t//helpers\n\tF(int,\t\tWildCmp,\t\t(const char *wild, const char *string));\n\tF(const char *,GetExtension,(const char *filename, const char *ignoreext));\n\tF(void,\t\tFileBase,\t\t(const char *in, char *out, int outlen));\n\tF(void,\t\tCleanUpPath,\t(char *str));\n\tF(unsigned int,BlockChecksum,(const void *buffer, size_t length));\t//mostly for pack hashes.\n\tF(void*,\tLoadFile,\t\t(const char *fname, size_t *fsize));\t//plugfuncs->Free\n\n\t//stuff that's useful for networking.\n\tF(char*,\tGetPackHashes,\t\t\t\t(char *buffer, int buffersize, qboolean referencedonly));\n\tF(char*,\tGetPackNames,\t\t\t\t(char *buffer, int buffersize, int referencedonly, qboolean ext));\n\tF(qboolean,\tGenCachedPakName,\t\t\t(const char *pname, const char *crc, char *local, int llen));\n\tF(void,\t\tPureMode,\t\t\t\t\t(const char *gamedir, int mode, char *purenamelist, char *purecrclist, char *refnamelist, char *refcrclist, int seed));\n\tF(char*,\tGenerateClientPacksList,\t(char *buffer, int maxlen, int basechecksum));\n#define plugfsfuncs_name \"Filesystem\"\n} plugfsfuncs_t;\n\ntypedef struct\t//for when you need basic socket access, hopefully rare...\n{\n\tF(qhandle_t,TCPConnect,\t\t(const char *ip, int port));\n\tF(qhandle_t,TCPListen,\t\t(const char *localip, int port, int maxcount));\n\tF(qhandle_t,Accept,\t\t\t(qhandle_t socket, char *address, int addresssize));\n\tF(int,\t\tRecv,\t\t\t(qhandle_t socket, void *buffer, int len));\n\tF(int,\t\tSend,\t\t\t(qhandle_t socket, void *buffer, int len));\n\tF(int,\t\tSendTo,\t\t\t(qhandle_t handle, void *data, int datasize, netadr_t *dest));\n\tF(void,\t\tClose,\t\t\t(qhandle_t socket));\n\tF(int,\t\tSetTLSClient,\t(qhandle_t sock, const char *certhostname));\t\t//adds a tls layer to the socket (and specifies the peer's required hostname)\n\tF(int,\t\tGetTLSBinding,\t(qhandle_t sock, char *outdata, int *datalen));\t//to avoid MITM attacks with compromised cert authorities\n\n\t//for (d)tls plugins to use.\n\tF(qboolean,\tRandomBytes,\t\t\t\t(qbyte *string, int len));\n\tF(void *,\tTLS_GetKnownCertificate,\t(const char *certname, size_t *size));\n\tF(qboolean,\tCertLog_ConnectOkay,\t\t(const char *hostname, void *cert, size_t certsize, unsigned int certlogproblems));\n\n\t#define N_WOULDBLOCK -1\n\t#define N_FATALERROR -2\n\t#define NET_CLIENTPORT -1\n\t#define NET_SERVERPORT -2\n#define plugnetfuncs_name \"Net\"\n} plugnetfuncs_t;\n\n//fixme: this sucks.\nstruct vm_s;\ntypedef qintptr_t (QDECL *sys_calldll_t) (qintptr_t arg, ...);\ntypedef int (*sys_callqvm_t) (void *offset, quintptr_t mask, int fn, const int *arg);\ntypedef struct\n{\n\tF(struct vm_s *,Create,\t\t\t(const char *dllname, sys_calldll_t syscalldll, const char *qvmname, sys_callqvm_t syscallqvm));\n\tF(qboolean,\t\tNonNative,\t\t(struct vm_s *vm));\n\tF(void *,\t\tMemoryBase,\t\t(struct vm_s *vm));\n\tF(qintptr_t,\tCall,\t\t\t(struct vm_s *vm, qintptr_t instruction, ...));\n\tF(void,\t\t\tDestroy,\t\t(struct vm_s *vm));\n#define plugq3vmfuncs_name \"Quake3 QVM\"\n} plugq3vmfuncs_t;\n\n#undef F\n\nextern plugcorefuncs_t *plugfuncs;\nextern plugcmdfuncs_t *cmdfuncs;\nextern plugcvarfuncs_t *cvarfuncs;\n\n#define Q_snprintf (void)Q_snprintfz\n#define Q_vsnprintf (void)Q_vsnprintfz\n#ifdef FTEENGINE\nextern plugcorefuncs_t plugcorefuncs;\n#else\nvoid Q_strlncpy(char *d, const char *s, int sizeofd, int lenofs);\nvoid Q_strlcpy(char *d, const char *s, int n);\nvoid Q_strlcat(char *d, const char *s, int n);\n\nqboolean VARGS Q_vsnprintfz (char *dest, size_t size, const char *fmt, va_list argptr);\nqboolean VARGS Q_snprintfz (char *dest, size_t size, const char *fmt, ...) LIKEPRINTF(3);\n\nchar\t*va(const char *format, ...);\nqboolean Plug_Init(void);\nvoid Con_Printf(const char *format, ...);\nvoid Con_DPrintf(const char *format, ...);\t//not a particuarly efficient implementation, so beware.\nvoid Sys_Errorf(const char *format, ...);\nvoid QDECL Q_strncpyz(char *d, const char *s, int n);\n\n#define PLUG_SHARED_BEGIN(t,p,b)\t\t\\\n {\t\t\t\t\t\t\t\t\t\t\\\n\t t *p;\t\t\t\t\t\t\t\t\\\n\t char inputbuffer[8192];\t\t\t\\\n\t *(b) = ReadInputBuffer(inputbuffer, sizeof(inputbuffer));\t\\\n\t if (*(b))\t\t\t\t\t\t\\\n\t\t p = (t*)inputbuffer;\t\t\t\\\n\t else\t\t\t\t\t\t\t\t\\\n\t\t p = NULL;\n#define PLUG_SHARED_END(p,b) UpdateInputBuffer(inputbuffer, b);}\n\ntypedef struct {\n\tchar *name;\n\tchar string[256];\n\tchar *group;\n\tint flags;\n\tfloat value;\n\tqhandle_t handle;\n\tint modificationcount;\n} vmcvar_t;\n\n#define VMCvar_Register(cv) (cv->handle=cvarfuncs->Register(cv->name, cv->string, cv->flags, cv->group))\n#define VMCvar_Update(cv) cvarfuncs->Update(handle, &cv->modcount, cv->string, sizeof(cv->string), &cv->value)\n#define VMCvar_SetString(c,v)\t\t\t\t\t\t\t\\\n\tdo{\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n\t\tstrcpy(c->string, v);\t\t\t\t\t\t\t\\\n\t\tc->value = (float)atof(v);\t\t\t\t\t\t\\\n\t\tCvar_SetString(c->name, c->string);\t\t\t\t\\\n\t} while (0)\n#define VMCvar_SetFloat(c,v)\t\t\t\t\t\t\t\\\n\tdo {\t\t\t\t\t\t\t\t\t\t\t\t\\\n\t\tsnprintf(c->string, sizeof(c->string), \"%f\", v);\\\n\t\tc->value = (float)(v);\t\t\t\t\t\t\t\\\n\t\tCvar_SetFloat(c->name, c->value);\t\t\t\t\\\n\t} while(0)\t\t\t\t\t\t\t\t\t\t\t\\\n\n\nchar *Plug_Info_ValueForKey (const char *s, const char *key, char *out, size_t outsize);\n#endif\n\n#ifdef __cplusplus\n}\n#endif\n#endif\n"
  },
  {
    "path": "plugins/qi/qi.c",
    "content": "#include \"quakedef.h\"\n#include \"fs.h\"\n#include \"../plugin.h\"\nstatic plugsubconsolefuncs_t *confuncs;\nstatic plugfsfuncs_t *filefuncs;\nstatic plugclientfuncs_t *clientfuncs;\n\n#include \"../jabber/xml.h\"\n\n#define DATABASEURL\t\t\"https://www.quaddicted.com/reviews/quaddicted_database.xml\"\n#define FILEIMAGEURL\t\"https://www.quaddicted.com/reviews/screenshots/%s_injector.jpg\"\n#define FILEDOWNLOADURL\t\"https://www.quaddicted.com/filebase/%s.zip\"\n#define FILEREVIEWURL\t\"https://www.quaddicted.com/reviews/%s.html\"\n#define WINDOWTITLE\t\t\"Quaddicted Map+Mod Archive\"\n#define WINDOWNAME\t\t\"QI\"\n\n/*\n<file id=\"downloadname\" type=\"1=map. 2=mod\" rating=\"5-star-rating\">\n\t<author>meh</author>\n\t<title>some readable name</title>\n\t<md5sum>xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx</md5sum>\n\t<size>size-in-kb</size>\n\t<date>dd.mm.yy</date>\n\t<description>HTML-encoded text. just to be awkward</description>\n\t<techinfo>\n\t\t<zipbasedir>additional path needed to make it relative to the quake directory</zipbasedir>\n\t\t<requirements>\n\t\t\t<file id=\"quoth\" />\n\t\t</requirements>\n\t</techinfo>\n</file>\n*/\n\nstatic xmltree_t *thedatabase;\nstatic qhandle_t dlcontext = -1;\nstatic vfsfile_t *packagemanager;\n\nstatic struct\n{\n\tchar namefilter[256];\n\tint minrating;\n\tint maxrating;\n\tint type;\n} filters;\n\nstatic struct\n{\n\tint width;\n\tint height;\n} pvid;\nstatic void QDECL QI_UpdateVideo(int width, int height, qboolean restarted)\n{\n\tpvid.width = width;\n\tpvid.height = height;\n}\n\nvoid Con_SubPrintf(const char *subname, char *format, ...)\n{\n\tva_list\t\targptr;\n\tstatic char\t\tstring[8192];\n\n\tva_start (argptr, format);\n\tQ_vsnprintf (string, sizeof(string), format,argptr);\n\tva_end (argptr);\n\n\tconfuncs->SubPrint(subname, string);\n}\n\n\nstatic void QI_Shutdown(void)\n{\n\tif (dlcontext != -1)\n\t{\t//we're still downloading something? :o\n\t\tfilefuncs->Close(dlcontext);\n\t\tdlcontext = -1;\n\t}\n\tif (thedatabase)\n\t\tXML_Destroy(thedatabase);\n\tthedatabase = NULL;\n}\n\nstatic qboolean QI_SetupWindow(const char *console, qboolean force)\n{\n\tif (!confuncs)\n\t\treturn false;\n\n\t//only redraw the window if it actually exists. if they closed it, then don't mess things up.\n\tif (!force && confuncs->GetConsoleFloat(console, \"iswindow\") <= 0)\n\t\treturn false;\n\n\tif (confuncs->GetConsoleFloat(console, \"iswindow\") != true)\n\t{\n\t\tconfuncs->SetConsoleString(console, \"title\", WINDOWTITLE);\n\t\tconfuncs->SetConsoleFloat(console, \"iswindow\", true);\n\t\tconfuncs->SetConsoleFloat(console, \"forceutf8\", true);\n\t\tconfuncs->SetConsoleFloat(console, \"linebuffered\", false);\n\t\tconfuncs->SetConsoleFloat(console, \"maxlines\", 16384);\t//the line limit is more a sanity thing than anything else. so long as we explicitly clear before spamming more, then its not an issue...\n\t\tconfuncs->SetConsoleFloat(console, \"wnd_x\", 8);\n\t\tconfuncs->SetConsoleFloat(console, \"wnd_y\", 8);\n\t\tconfuncs->SetConsoleFloat(console, \"wnd_w\", pvid.width-16);\n\t\tconfuncs->SetConsoleFloat(console, \"wnd_h\", pvid.height-16);\n\t\tconfuncs->SetConsoleString(console, \"footer\", \"\");\n\t}\n\tconfuncs->SetConsoleFloat(console, \"linecount\", 0);\t//clear it\n\tif (force)\n\t\tconfuncs->SetActive(console);\n\treturn true;\n}\nstatic void QI_DeHTML(const char *in, qboolean escapes, char *out, size_t outsize)\n{\n\toutsize--;\n\twhile(*in && outsize > 0)\n\t{\n\t\tif (*in == '\\r' || *in == '\\n')\n\t\t\tin++;\n\t\telse if (*in == '<')\n\t\t{\n\t\t\tchar tag[256];\n\t\t\tint i;\n\t\t\tqboolean open = false;\n\t\t\tqboolean close = false;\n\t\t\tin++;\n\t\t\tif (*in == '/')\n\t\t\t{\n\t\t\t\tin++;\n\t\t\t\tclose = true;\n\t\t\t}\n\t\t\telse\n\t\t\t\topen = true;\n\t\t\twhile (*in == ' ' || *in == '\\n' || *in == '\\t' || *in == '\\r')\n\t\t\t\tin++;\n\t\t\tfor (i = 0; i < countof(tag)-1; )\n\t\t\t{\n\t\t\t\tif (*in == '>')\n\t\t\t\t\tbreak;\n\t\t\t\tif (!*in || *in == ' ' || *in == '\\n' || *in == '\\t' || *in == '\\r' || (in[0] == '/' && in[1] == '>'))\n\t\t\t\t\tbreak;\n\t\t\t\ttag[i++] = *in++;\n\t\t\t}\n\t\t\ttag[i] = 0;\n\t\t\twhile (*in && *in)\n\t\t\t{\n\t\t\t\tif (*in == '/' && in[1] == '>')\n\t\t\t\t{\n\t\t\t\t\tin += 2;\n\t\t\t\t\tclose = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tif (*in++ == '>')\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (!strcmp(tag, \"br\") && open && outsize > 0)\n\t\t\t{\t//new lines!\n\t\t\t\t*out++ = '\\n';\n\t\t\t\toutsize--;\n\t\t\t}\n\t\t\telse if (!strcmp(tag, \"b\") && open != close && outsize > 1)\n\t\t\t{\t//bold\n\t\t\t\t*out++ = '^';\n\t\t\t\toutsize--;\n\t\t\t\t*out++ = 'a';\n\t\t\t\toutsize--;\n\t\t\t}\n\t\t\telse if (!strcmp(tag, \"i\") && open != close && outsize > 1)\n\t\t\t{\t//italics\n\t\t\t\t*out++ = '^';\n\t\t\t\toutsize--;\n\t\t\t\t*out++ = close?'7':'3';\n\t\t\t\toutsize--;\n\t\t\t}\n\t\t}\n\t\telse if (*in == '^')\n\t\t{\n\t\t\tif (outsize > 1)\n\t\t\t{\n\t\t\t\t*out++ = *in;\n\t\t\t\toutsize--;\n\t\t\t\t*out++ = *in++;\n\t\t\t\toutsize--;\n\t\t\t}\n\t\t}\n\t\telse if ((*in == '\\\"') && escapes && outsize >= 2)\n\t\t{\n\t\t\t*out++ = '\\\\';\n\t\t\t*out++ = *in++;\n\t\t\toutsize-=2;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t*out++ = *in++;\n\t\t\toutsize--;\n\t\t}\n\t}\n\t*out = 0;\n}\n\nstatic char *QI_strcasestr(const char *haystack, const char *needle)\n{\n\tint c1, c2, c2f;\n\tint i;\n\tc2f = *needle;\n\tif (c2f >= 'a' && c2f <= 'z')\n\t\tc2f -= ('a' - 'A');\n\tif (!c2f)\n\t\treturn (char*)haystack;\n\twhile (1)\n\t{\n\t\tc1 = *haystack;\n\t\tif (!c1)\n\t\t\treturn NULL;\n\t\tif (c1 >= 'a' && c1 <= 'z')\n\t\t\tc1 -= ('a' - 'A');\n\t\tif (c1 == c2f)\n\t\t{\n\t\t\tfor (i = 1; ; i++)\n\t\t\t{\n\t\t\t\tc1 = haystack[i];\n\t\t\t\tc2 = needle[i];\n\t\t\t\tif (c1 >= 'a' && c1 <= 'z')\n\t\t\t\t\tc1 -= ('a' - 'A');\n\t\t\t\tif (c2 >= 'a' && c2 <= 'z')\n\t\t\t\t\tc2 -= ('a' - 'A');\n\t\t\t\tif (!c2)\n\t\t\t\t\treturn (char*)haystack;\t//end of needle means we found a complete match\n\t\t\t\tif (!c1)\t//end of haystack means we can't possibly find needle in it any more\n\t\t\t\t\treturn NULL;\n\t\t\t\tif (c1 != c2)\t//mismatch means no match starting at haystack[0]\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\thaystack++;\n\t}\n\treturn NULL;\t//didn't find it\n}\n\nstatic void QI_RefreshMapList(qboolean forcedisplay)\n{\n\txmltree_t *file;\n\tconst char *console = WINDOWNAME;\n\tchar descbuf[1024];\n\tfloat donestats[4];\n\tif (!QI_SetupWindow(console, forcedisplay))\n\t\treturn;\n\n\tif (!thedatabase)\n\t{\n\t\tif (dlcontext != -1)\n\t\t\tCon_SubPrintf(console, \"Downloading database...\\n\");\n\t\telse\n\t\t\tCon_SubPrintf(console, \"Unable to download HTTP database\\n\");\n\t\treturn;\n\t}\n\n\tif (!thedatabase->child)\n\t{\n\t\tCon_SubPrintf(console, \"No maps in database... SPIRIT! YOU BROKE IT!\\n\");\n\t\treturn;\n\t}\n\n\tfor (file = thedatabase->child; file; file = file->sibling)\n\t{\n\t\tconst char *id = XML_GetParameter(file, \"id\", \"unnamed\");\n\t\tconst char *rating = XML_GetParameter(file, \"rating\", \"\");\n\t\tint ratingnum = atoi(rating);\n\t\tconst char *author = XML_GetChildBody(file, \"author\", \"unknown\");\n\t\tconst char *desc = XML_GetChildBody(file, \"description\", \"<NO DESCRIPTION>\");\n\t\tconst char *type;\n\t\tconst char *date;\n\t\tint year, month, day;\n\t\tint startmapnum, i;\n\t\tchar ratingtext[65];\n\t\txmltree_t *tech;\n\t\txmltree_t *startmap;\n\t\tif (strcmp(file->name, \"file\"))\n\t\t\tcontinue;\t//erk?\n\t\tif (atoi(XML_GetParameter(file, \"hide\", \"\")) || atoi(XML_GetParameter(file, \"fte_hide\", \"\")))\n\t\t\tcontinue;\n\t\ttype = XML_GetParameter(file, \"type\", \"\");\n\t\tif (filters.type && atoi(type) != filters.type)\n\t\t\tcontinue;\n\t\tswitch(atoi(type))\n\t\t{\n\t\tcase 1:\n\t\t\ttype = \"map\";\t//'single map file(s)'\n\t\t\tbreak;\n\t\tcase 2:\n\t\t\ttype = \"mod\";\t//'Partial conversion'\n\t\t\tbreak;\n\t\tcase 4:\n\t\t\ttype = \"spd\";\t//'speedmapping'\n\t\t\tbreak;\n\t\tcase 5:\n\t\t\ttype = \"otr\";\t//'misc files'\n\t\t\tbreak;\n\t\tdefault:\n\t\t\ttype = \"???\";\t//no idea\n\t\t\tbreak;\n\t\t}\n\n\t\tif (filters.maxrating>=0 && ratingnum > filters.maxrating)\n\t\t\tcontinue;\n\t\tif (filters.minrating>=0 && ratingnum < filters.minrating)\n\t\t\tcontinue;\n\n\t\ttech = XML_ChildOfTree(file, \"techinfo\", 0);\n\t\t//if the filter isn't contained in the id/desc then don't display it.\n\t\tif (*filters.namefilter)\n\t\t{\n\t\t\tif (!QI_strcasestr(id, filters.namefilter) && !QI_strcasestr(desc, filters.namefilter) && !QI_strcasestr(author, filters.namefilter))\n\t\t\t{\n\t\t\t\t//check map list too\n\t\t\t\tfor (startmapnum = 0; ; startmapnum++)\n\t\t\t\t{\n\t\t\t\t\tstartmap = XML_ChildOfTree(tech, \"startmap\", startmapnum);\n\t\t\t\t\tif (!startmap)\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tif (QI_strcasestr(startmap->body, filters.namefilter))\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tif (!startmap)\n\t\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\n\t\tif (ratingnum > (sizeof(ratingtext)-5)/6)\n\t\t\tratingnum = (sizeof(ratingtext)-5)/6;\n\t\tif (ratingnum)\n\t\t{\n\t\t\tQ_snprintf(ratingtext, sizeof(ratingtext), \"^a\");\n\t\t\tfor (i = 0; i < ratingnum; i++)\n\t\t\t\tQ_snprintf(ratingtext + i+2, sizeof(ratingtext)-i*2+2, \"*\");\n\t\t\tQ_snprintf(ratingtext + i+2, sizeof(ratingtext)-i*2+2, \"^a\");\n\t\t}\n\t\telse if (*rating)\n\t\t\tQ_snprintf(ratingtext, sizeof(ratingtext), \"%s\", rating);\n\t\telse\n\t\t\tQ_snprintf(ratingtext, sizeof(ratingtext), \"%s\", \"unrated\");\n\n\t\t\n\t\tdate = XML_GetChildBody(file, \"date\", \"1.1.1990\");\n\t\tday = atoi(date?date:\"1\");\n\t\tdate = date?strchr(date, '.'):NULL;\n\t\tmonth = atoi(date?date+1:\"1\");\n\t\tdate = date?strchr(date+1, '.'):NULL;\n\t\tyear = atoi(date?date+1:\"1990\");\n\t\tif (year < 90)\n\t\t\tyear += 2000;\n\t\telse if (year < 1900)\n\t\t\tyear += 1900;\n\t\tQ_snprintf(descbuf, sizeof(descbuf), \"^aId:^a %s\\n^aAuthor(s):^a %s\\n^aDate:^a %04u-%02u-%02u\\n^aRating:^a %s\\n\\n\", id, author, year, month, day, ratingtext);\n\n\t\tQI_DeHTML(desc, false, descbuf + strlen(descbuf), sizeof(descbuf) - strlen(descbuf));\n\t\tdesc = descbuf;\n\n\t\tCon_SubPrintf(console, \"%s %s ^[^4%s: ^1%s\\\\tip\\\\%s\\\\tipimg\\\\\"FILEIMAGEURL\"\\\\id\\\\%s^]\", type, ratingtext, id, XML_GetChildBody(file, \"title\", \"<NO TITLE>\"), desc, id, id);\n\t\tfor (startmapnum = 0; ; startmapnum++)\n\t\t{\n\t\t\tchar bspfile[MAX_QPATH];\n\t\t\tstartmap = XML_ChildOfTree(tech, \"startmap\", startmapnum);\n\t\t\tif (!startmap)\n\t\t\t\tbreak;\n\n\t\t\tQ_snprintf(bspfile, sizeof(bspfile), \"maps/%s.bsp\", startmap->body);\n\t\t\tif (clientfuncs && clientfuncs->MapLog_Query(va(FILEDOWNLOADURL, id), bspfile, donestats))\n\t\t\t\tCon_SubPrintf(console, \" ^[^2[%s, complete %.1f]\\\\tip\\\\^7^aBest Time:^a ^2%.9f^7\\n^aCompletion Time:^a %.9f\\n^aKills:^a %.9f\\n^aSecrets:^a %.9f\\n\\n\\n%s\"/*\"\\\\tipimg\\\\\"FILEIMAGEURL*/\"\\\\id\\\\%s\\\\startmap\\\\%s^]\", startmap->body, donestats[0], donestats[0], donestats[1], donestats[2], donestats[3], desc, /*id,*/ id, startmap->body);\n\t\t\telse\n\t\t\t\tCon_SubPrintf(console, \" ^[^4[%s]\\\\tip\\\\%s\"/*\"\\\\tipimg\\\\\"FILEIMAGEURL*/\"\\\\id\\\\%s\\\\startmap\\\\%s^]\", startmap->body, desc, /*id,*/ id, startmap->body);\n\t\t}\n\t\tCon_SubPrintf(console, \"\\n\");\n\t}\n\n\tCon_SubPrintf(console, \"\\nFilter:\\n\");\n\tif (*filters.namefilter)\n\t\tCon_SubPrintf(console, \"Contains: %s \", filters.namefilter);\n\tCon_SubPrintf(console, \"^[Change Filter^]\\n\");\n\tCon_SubPrintf(console, \"^[Maps^] %s\\n\", (filters.type!=2)?\"shown\":\"hidden\");\n\tCon_SubPrintf(console, \"^[Mods^] %s\\n\", (filters.type!=1)?\"shown\":\"hidden\");\n\tCon_SubPrintf(console, \"Sort: ^[Alphabetically^] ^[Date^] ^[Rating^]\\n\");\n\tif (filters.minrating == filters.maxrating)\n\t{\n\t\tchar *gah[] = {\"Any Rating\", \"Unrated\", \"1\",\"2\",\"3\",\"4\",\"5\"};\n\t\tint i;\n\t\tCon_SubPrintf(console, \"Rating\");\n\t\tfor (i = 0; i < countof(gah); i++)\n\t\t{\n\t\t\tif (i == filters.minrating+1)\n\t\t\t\tCon_SubPrintf(console, \" %s\", gah[i]);\n\t\t\telse\n\t\t\t\tCon_SubPrintf(console, \" ^[%s^]\", gah[i]);\n\t\t}\n\t\tCon_SubPrintf(console, \"\\n\");\n\t}\n\telse\n\t{\n\t\tif (filters.minrating)\n\t\t\tCon_SubPrintf(console, \"Min Rating: %i stars\\n\", filters.minrating);\n\t\tif (filters.maxrating)\n\t\t\tCon_SubPrintf(console, \"Max Rating: %i stars\\n\", filters.maxrating);\n\t}\n}\n\nstatic void QI_UpdateFilter(char *filtertext)\n{\n\tif (!strcmp(filtertext, \"all\"))\n\t{\n\t\tfilters.type = 0;\n\t\tfilters.minrating = filters.maxrating = -1;\n\t\tQ_strlcpy(filters.namefilter, \"\", sizeof(filters.namefilter));\n\t}\n\telse if (*filtertext == '>')\n\t\tfilters.minrating = atoi(filtertext+1);\n\telse if (*filtertext == '<')\n\t\tfilters.maxrating = atoi(filtertext+1);\n\telse if (*filtertext == '=')\n\t\tfilters.minrating = filters.maxrating = atoi(filtertext+1);\n\telse if (!strcmp(filtertext, \"any\"))\n\t{\n\t\tfilters.type = 0;\n\t\tfilters.minrating = filters.maxrating = -1;\n\t}\n\telse if (!strcmp(filtertext, \"maps\"))\n\t\tfilters.type = 1;\n\telse if (!strcmp(filtertext, \"mods\"))\n\t\tfilters.type = 2;\n\telse\n\t\tQ_strlcpy(filters.namefilter, filtertext, sizeof(filters.namefilter));\n}\n\nstatic xmltree_t *QI_FindArchive(const char *name)\n{\n\txmltree_t *file;\n\tfor (file = thedatabase->child; file; file = file->sibling)\n\t{\n\t\tconst char *id = XML_GetParameter(file, \"id\", \"unnamed\");\n\t\tif (strcmp(file->name, \"file\"))\n\t\t\tcontinue;\t//erk?\n\n\t\tif (!strcmp(id, name))\n\t\t\treturn file;\n\t}\n\treturn NULL;\n}\nstatic void QI_AddPackages(xmltree_t *qifile)\n{\n\tconst char *id;\n\tchar extra[1024];\n\tchar clean[512];\n\tconst char *hash;\n\tunsigned int i;\n\n\txmltree_t *tech;\n\tconst char *basedir;\n\txmltree_t *requires;\n\txmltree_t *req;\n\t//quaddicted's database contains various zips that are meant to be extracted to various places.\n\t//this can either be the quake root (no path), a mod directory (typically the name of the mod), or the maps directory (id1/maps or some such)\n\t//unfortunately, quake files are relative to the subdir, so we need to strip the first subdir. anyone that tries to put dlls in there is evil. and if there isn't one, we need to get the engine to compensate.\n\t//we also need to clean up the paths so they're not badly formed\n\ttech = XML_ChildOfTree(qifile, \"techinfo\", 0);\n\tbasedir = XML_GetChildBody(tech, \"zipbasedir\", \"\");\n\n\t//skip any dodgy leading slashes\n\twhile (*basedir == '/' || *basedir == '\\\\')\n\t\tbasedir++;\n\tif (!*basedir)\n\t\tstrcpy(clean, \"..\");\t//err, there wasn't a directory... we still need to 'strip' it though.\n\telse\n\t{\n\t\t//skip the gamedir\n\t\twhile (*basedir && *basedir != '/' && *basedir != '\\\\')\n\t\t\tbasedir++;\n\t\t//skip any trailing\n\t\twhile (*basedir == '/' || *basedir == '\\\\')\n\t\t\tbasedir++;\n\t\tfor (i = 0; *basedir; i++)\n\t\t{\n\t\t\tif (i >= sizeof(clean)-1)\n\t\t\t\tbreak;\n\t\t\tif (*basedir == '\\\\')\t//sigh\n\t\t\t\tclean[i] = '/';\n\t\t\telse\n\t\t\t\tclean[i] = *basedir;\n\t\t\tbasedir++;\n\t\t}\n\t\twhile (i > 0 && clean[i-1] == '/')\n\t\t\ti--;\n\t\tclean[i] = 0;\n\t}\n\n\trequires = XML_ChildOfTree(tech, \"requirements\", 0);\n\tif (requires)\n\t{\n\t\tfor (i = 0; ; i++)\n\t\t{\n\t\t\treq = XML_ChildOfTree(requires, \"file\", i);\n\t\t\tif (!req)\n\t\t\t\tbreak;\n\t\t\tid = XML_GetParameter(req, \"id\", \"unknown\");\n\t\t\tQI_AddPackages(QI_FindArchive(id));\n\t\t}\n\t}\n\n\thash = XML_GetChildBody(qifile, \"md5hash\", \"\");\n\tif (strchr(hash, '\\\\') || strchr(hash, '\\\"'))\t//if it has a dodgy escape-breaking char then drop it.\n\t\thash = \"\";\n\n\tid = XML_GetParameter(qifile, \"id\", \"unknown\");\n\tif (strchr(clean, '\\\\') || strchr(clean, '\\\"') || strchr(clean, '\\n') || strchr(clean, ';'))\n\t\treturn;\n\tif (strchr(id, '\\\\') || strchr(id, '\\\"') || strchr(id, '\\n') || strchr(id, ';'))\n\t\treturn;\n\n\n\tQ_snprintf(extra, sizeof(extra), \" package \\\"\"FILEDOWNLOADURL\"\\\" prefix \\\"%s\\\" hash \\\"md5:%s\\\"\", id, clean, hash);\n\tcmdfuncs->AddText(extra, false);\n}\nstatic void QI_RunMap(xmltree_t *qifile, const char *map)\n{\n\tchar gamedir[65];\n\tchar buf[256];\n\tif (!qifile)\n\t{\n\t\treturn;\n\t}\n\n\t//type 1 (maps) often don't list any map names\n\tif (!*map)// && atoi(XML_GetParameter(qifile, \"type\", \"0\")) == 1)\n\t\tmap = XML_GetParameter(qifile, \"id\", \"unknown\");\n\tif (!*map || strchr(map, '\\\\') || strchr(map, '\\\"') || strchr(map, '\\n') || strchr(map, ';'))\n\t\tmap = \"\";\n\n\tstrcpy(gamedir, \"id1\");\n\t{\n\t\tchar descbuf[1024];\n\t\txmltree_t *tech = XML_ChildOfTree(qifile, \"techinfo\", 0);\n\t\tconst char *cmdline = XML_GetChildBody(tech, \"commandline\", \"\");\n\t\twhile (cmdline)\n\t\t{\n\t\t\tcmdline = cmdfuncs->ParseToken(cmdline, descbuf, sizeof(descbuf), NULL);\n\t\t\tif (!strcmp(descbuf, \"-game\"))\n\t\t\t\tcmdline = cmdfuncs->ParseToken(cmdline, gamedir, sizeof(gamedir), NULL);\n\t\t\telse if (!strcmp(descbuf, \"-hipnotic\") || !strcmp(descbuf, \"-rogue\") || !strcmp(descbuf, \"-quoth\"))\n\t\t\t{\n\t\t\t\tif (!*gamedir)\n\t\t\t\t\tstrcpy(gamedir, descbuf+1);\n\t\t\t}\n\t\t}\n\t}\n\n\tcmdfuncs->AddText(\"fs_changemod\", false);\n\tif (*gamedir)\n\t{\n\t\tcmdfuncs->AddText(\" dir \", false);\n\t\tcmdfuncs->AddText(cmdfuncs->QuotedString(gamedir, buf, sizeof(buf), false), false);\n\t}\n\tif (map && *map)\n\t{\n\t\tcmdfuncs->AddText(\" spmap \", false);\n\t\tcmdfuncs->AddText(cmdfuncs->QuotedString(map, buf, sizeof(buf), false), false);\n\t}\n\tQI_AddPackages(qifile);\n//\tCon_Printf(\"Command: %s\\n\", cmd);\n\tcmdfuncs->AddText(\"\\n\", false);\n}\n\n\nvoid VARGS VFS_PRINTF(vfsfile_t *vf, const char *format, ...)\n{\n\tva_list\t\targptr;\n\tchar\t\tstring[1024];\n\n\tva_start (argptr, format);\n\tvsnprintf (string,sizeof(string)-1, format,argptr);\n\tva_end (argptr);\n\n\tVFS_PUTS(vf, string);\n}\nstatic void QI_WriteUpdateList(xmltree_t *database, vfsfile_t *pminfo)\n{\n\txmltree_t *file;\n\tchar descbuf[1024], *d, *nl;\n\n\tif (database)\n\tfor (file = database->child; file; file = file->sibling)\n\t{\n\t\tconst char *id = XML_GetParameter(file, \"id\", \"unnamed\");\n\t\tconst char *rating = XML_GetParameter(file, \"rating\", \"\");\n\t\tint ratingnum = atoi(rating);\n\t\tconst char *author = XML_GetChildBody(file, \"author\", \"unknown\");\n\t\tconst char *desc = XML_GetChildBody(file, \"description\", \"<NO DESCRIPTION>\");\n\t\tconst char *type = XML_GetParameter(file, \"type\", \"\");\n\t\tconst char *date = XML_GetChildBody(file, \"date\", \"1.1.1990\");\n\t\txmltree_t *tech = XML_ChildOfTree(file, \"techinfo\", 0);\n\t\tconst char *cmdline = XML_GetChildBody(tech, \"commandline\", \"\");\n\t\tconst char *zipbasedir = XML_GetChildBody(tech, \"zipbasedir\", \"\");\n\t\tint year, month, day;\n\t\tint startmapnum, i;\n\t\tchar ratingtext[65];\n\t\tchar gamedir[65];\n\t\tchar prefix[MAX_QPATH];\n\t\txmltree_t *startmap;\n\t\tif (strcmp(file->name, \"file\"))\n\t\t\tcontinue;\t//erk?\n\t\tif (atoi(XML_GetParameter(file, \"hide\", \"\")) || atoi(XML_GetParameter(file, \"fte_hide\", \"\")))\n\t\t\tcontinue;\n\t\tswitch(atoi(type))\n\t\t{\n\t\tcase 1:\n\t\t\ttype = \"map\";\t//'single map file(s)'\n\t\t\tbreak;\n\t\tcase 2:\n\t\t\ttype = \"mod\";\t//'Partial conversion'\n\t\t\tbreak;\n\t\tcase 4:\n\t\t\ttype = \"spd\";\t//'speedmapping'\n\t\t\tbreak;\n\t\tcase 5:\n\t\t\ttype = \"otr\";\t//'misc files'\n\t\t\tbreak;\n\t\tdefault:\n\t\t\ttype = \"???\";\t//no idea\n\t\t\tbreak;\n\t\t}\n\n\t\tif (ratingnum > (sizeof(ratingtext)-5)/6)\n\t\t\tratingnum = (sizeof(ratingtext)-5)/6;\n\t\tif (ratingnum)\n\t\t{\n\t\t\tQ_snprintf(ratingtext, sizeof(ratingtext), \"^a\");\n\t\t\tfor (i = 0; i < ratingnum; i++)\n\t\t\t\tQ_snprintf(ratingtext + i+2, sizeof(ratingtext)-i*2+2, \"*\");\n\t\t\tQ_snprintf(ratingtext + i+2, sizeof(ratingtext)-i*2+2, \"^a\");\n\t\t}\n\t\telse if (*rating)\n\t\t\tQ_snprintf(ratingtext, sizeof(ratingtext), \"%s\", rating);\n\t\telse\n\t\t\tQ_snprintf(ratingtext, sizeof(ratingtext), \"%s\", \"unrated\");\n\n\n\t\tday = atoi(date?date:\"1\");\n\t\tdate = date?strchr(date, '.'):NULL;\n\t\tmonth = atoi(date?date+1:\"1\");\n\t\tdate = date?strchr(date+1, '.'):NULL;\n\t\tyear = atoi(date?date+1:\"1990\");\n\t\tif (year < 90)\n\t\t\tyear += 2000;\n\t\telse if (year < 1900)\n\t\t\tyear += 1900;\n\n\t\tstrcpy(gamedir, \"id1\");\n\t\twhile (cmdline)\n\t\t{\n\t\t\tcmdline = cmdfuncs->ParseToken(cmdline, descbuf, sizeof(descbuf), NULL);\n\t\t\tif (!strcmp(descbuf, \"-game\"))\n\t\t\t\tcmdline = cmdfuncs->ParseToken(cmdline, gamedir, sizeof(gamedir), NULL);\n\t\t\telse if (!strcmp(descbuf, \"-hipnotic\") || !strcmp(descbuf, \"-rogue\") || !strcmp(descbuf, \"-quoth\"))\n\t\t\t{\n\t\t\t\tif (!*gamedir)\n\t\t\t\t\tstrcpy(gamedir, descbuf+1);\n\t\t\t}\n\t\t}\n\t\tif (!*gamedir)\n\t\t\tcontinue;\t//bad package\n\n\n\t\t*descbuf = 0;\n\t\tQI_DeHTML(desc, true, descbuf + strlen(descbuf), sizeof(descbuf) - strlen(descbuf));\n\t\tdesc = descbuf;\n\n\t\tVFS_PRINTF(pminfo, \"{\\n\"\n\t\t\t\t\t\t\t\t\"\\tpackage \\\"qi_%s\\\"\\n\"\n\t\t\t\t\t\t\t\t\"\\ttitle \\\"%s %s\\\"\\n\"\n\t\t\t\t\t\t\t\t\"\\tcategory \\\"Quaddicted - %i\\\"\\n\"\n\t\t\t\t\t\t\t\t\"\\tlicense \\\"Unknown\\\"\\n\"\n\t\t\t\t\t\t\t\t\"\\tauthor \\\"Unknown\\\"\\n\"\n\t\t\t\t\t\t\t\t\"\\tqhash \\\"\\\"\\n\"\n\t\t\t\t\t\t\t\t,id, ratingtext, XML_GetChildBody(file, \"title\", \"<NO TITLE>\"), year);\n\n\t\tVFS_PRINTF(pminfo, \t\t\"\\tgamedir \\\"%s\\\"\\n\", gamedir);\n\n\t\tVFS_PRINTF(pminfo, \t\t\"\\tver %04u-%02u-%02u\\n\", year, month, day);\n\t\tif (!strchr(author, '\\\"') && !strchr(author, '\\n'))\n\t\t\tVFS_PRINTF(pminfo, \t\t\"\\tauthor \\\"%s\\\"\\n\", author);\n\t\tVFS_PRINTF(pminfo, \t\t\"\\twebsite \\\"\"FILEREVIEWURL\"\\\"\\n\", id);\t//in lieu of an actual site, lets fill it with quaddicted's reviews.\n\n\t\tVFS_PRINTF(pminfo, \t\t\"\\turl \\\"\"FILEDOWNLOADURL\"\\\"\\n\", id);\n\n\n\t\t//skip any dodgy leading slashes\n\t\twhile (*zipbasedir == '/' || *zipbasedir == '\\\\')\n\t\t\tzipbasedir++;\n\t\tif (!*zipbasedir)\n\t\t\tstrcpy(prefix, \"..\");\t//err, there wasn't a directory... we still need to 'strip' it though.\n\t\telse\n\t\t{\n\t\t\t//skip the zip's gamedir\n\t\t\twhile (*zipbasedir && *zipbasedir != '/' && *zipbasedir != '\\\\')\n\t\t\t\tzipbasedir++;\n\t\t\t//skip any trailing\n\t\t\twhile (*zipbasedir == '/' || *zipbasedir == '\\\\')\n\t\t\t\tzipbasedir++;\n\t\t\tfor (i = 0; *zipbasedir; i++)\n\t\t\t{\n\t\t\t\tif (i >= sizeof(prefix)-1)\n\t\t\t\t\tbreak;\n\t\t\t\tif (*zipbasedir == '\\\\')\t//sigh\n\t\t\t\t\tprefix[i] = '/';\n\t\t\t\telse if (*zipbasedir == '\\\"' || *zipbasedir == '\\n' || *zipbasedir == '\\r')\n\t\t\t\t\tbreak;\t//bad char...\n\t\t\t\telse\n\t\t\t\t\tprefix[i] = *zipbasedir;\n\t\t\t\tzipbasedir++;\n\t\t\t}\n\t\t\twhile (i > 0 && prefix[i-1] == '/')\n\t\t\t\ti--;\n\t\t\tprefix[i] = 0;\n\t\t}\n\t\tif (*prefix)\n\t\t\tVFS_PRINTF(pminfo, \t\t\"\\tpackprefix \\\"%s\\\"\\n\", prefix);\n\n\t\tfor(d = descbuf;;)\n\t\t{\n\t\t\tnl = strchr(d, '\\n');\n\t\t\tif (nl)\n\t\t\t\t*nl++ = 0;\n\t\t\tVFS_PRINTF(pminfo, \t\t\"\\tdesc \\\"%s\\\"\\n\", d);\n\t\t\tif (!nl)\n\t\t\t\tbreak;\n\t\t\td = nl;\n\t\t}\n\n\t\t//VFS_PRINTF(pminfo,\t\t\"\\tpreview \\\"\"FILEIMAGEURL\"\\\"\\n\", id);\n\t\tfor (startmapnum = 0; ; startmapnum++)\n\t\t{\n\t\t\tstartmap = XML_ChildOfTree(tech, \"startmap\", startmapnum);\n\t\t\tif (!startmap)\n\t\t\t\tbreak;\n\n\t\t\tVFS_PRINTF(pminfo, \"\\tmap \\\"%s\\\"\\n\", startmap->body);\n\t\t}\n\t\tif (!startmapnum)\t//if there's no start maps listed, use the package's id instead. these are not intended for anything else.\n\t\t\tVFS_PRINTF(pminfo, \"\\tmap \\\"%s\\\"\\n\", id);\n\t\tVFS_PRINTF(pminfo, \"}\\n\");\n\t}\n}\n\nstatic unsigned int QI_GetDate(xmltree_t *file)\n{\n\tunsigned int day, month, year;\n\tconst char *date = XML_GetChildBody(file, \"date\", \"1.1.1990\");\n\tday = atoi(date?date:\"1\");\n\tdate = date?strchr(date, '.'):NULL;\n\tmonth = atoi(date?date+1:\"1\");\n\tdate = date?strchr(date+1, '.'):NULL;\n\tyear = atoi(date?date+1:\"1990\");\n\tif (year < 90)\n\t\tyear += 2000;\n\telse if (year < 1900)\n\t\tyear += 1900;\n\treturn year*10000 + month*100 + day;\n}\nstatic int QDECL QI_ResortName (const void *v1, const void *v2)\n{\n\tconst char *n1 = XML_GetChildBody(*(xmltree_t*const*)v1, \"title\", \"<NO TITLE>\");\n\tconst char *n2 = XML_GetChildBody(*(xmltree_t*const*)v2, \"title\", \"<NO TITLE>\");\n\treturn strcasecmp(n1,n2);\n}\nstatic int QDECL QI_ResortDate (const void *v1, const void *v2)\n{\n\tunsigned int d1 = QI_GetDate(*(xmltree_t*const*)v1);\n\tunsigned int d2 = QI_GetDate(*(xmltree_t*const*)v2);\n\n\tif (d1>d2)\n\t\treturn 1;\n\tif (d1<d2)\n\t\treturn -1;\n\treturn QI_ResortName(v1,v2);\n}\nstatic int QDECL QI_ResortRating (const void *v1, const void *v2)\n{\n\tint r1 = atoi(XML_GetParameter(*(xmltree_t*const*)v1, \"rating\", \"\"));\n\tint r2 = atoi(XML_GetParameter(*(xmltree_t*const*)v2, \"rating\", \"\"));\n\n\tif (r1>r2)\n\t\treturn 1;\n\tif (r1<r2)\n\t\treturn -1;\n\treturn QI_ResortDate(v1,v2);\t//equal... go by date.\n}\n\nstatic qboolean QI_Resort(int sorttype)\n{\n\txmltree_t *file;\n\txmltree_t **files;\n\tunsigned int count;\n\tif (!thedatabase || !thedatabase->child)\n\t\treturn true;\n\n\tfor (count = 0, file = thedatabase->child; file; file = file->sibling)\n\t\tcount++;\n\tfiles = malloc(sizeof(*files)*count);\n\tfor (count = 0, file = thedatabase->child; file; file = file->sibling)\n\t\tfiles[count++] = file;\n\tif (sorttype == 0)\n\t\tqsort(files, count, sizeof(*files), QI_ResortName);\n\telse if (sorttype == 1)\n\t\tqsort(files, count, sizeof(*files), QI_ResortDate);\n\telse if (sorttype == 2)\n\t\tqsort(files, count, sizeof(*files), QI_ResortRating);\n\tthedatabase->child = NULL;\n\twhile (count --> 0)\n\t{\n\t\tfile = files[count];\n\t\tfile->sibling = thedatabase->child;\n\t\tthedatabase->child = file;\n\t}\n\tQI_RefreshMapList(true);\n\treturn true;\n}\nstatic qboolean QDECL QI_ConsoleLink(void)\n{\n\txmltree_t *file;\n\tchar *map;\n\tchar *id;\n\tchar *e;\n\tchar text[2048];\n\tchar link[8192];\n\tcmdfuncs->Argv(0, text, sizeof(text));\n\tcmdfuncs->Argv(1, link, sizeof(link));\n\n\tif (!*link)\n\t{\n\t\tif (!strcmp(text, \"Change Filter\"))\n\t\t{\n\t\t\tconst char *console = WINDOWNAME;\n\t\t\tconfuncs->SetConsoleFloat(console, \"linebuffered\", true);\n\t\t\tconfuncs->SetConsoleString(console, \"footer\", \"Please enter filter:\");\n\t\t\treturn true;\n\t\t}\n\t\tif (!strcmp(text, \"Maps\"))\n\t\t{\n\t\t\tfilters.type = (filters.type==2)?0:2;\n\t\t\tQI_RefreshMapList(true);\n\t\t\treturn true;\n\t\t}\n\t\tif (!strcmp(text, \"Mods\"))\n\t\t{\n\t\t\tfilters.type = (filters.type==1)?0:1;\n\t\t\tQI_RefreshMapList(true);\n\t\t\treturn true;\n\t\t}\n\t\tif (!strcmp(text, \"Any Rating\"))\n\t\t{\n\t\t\tfilters.minrating = filters.maxrating = -1;\n\t\t\tQI_RefreshMapList(true);\n\t\t\treturn true;\n\t\t}\n\t\tif (atoi(text) || !strcmp(text, \"Unrated\"))\n\t\t{\n\t\t\tfilters.minrating = filters.maxrating = atoi(text);\n\t\t\tQI_RefreshMapList(true);\n\t\t\treturn true;\n\t\t}\n\t\tif (atoi(text) || !strcmp(text, \"Alphabetically\"))\n\t\t\treturn QI_Resort(0);\n\t\tif (atoi(text) || !strcmp(text, \"Date\"))\n\t\t\treturn QI_Resort(1);\n\t\tif (atoi(text) || !strcmp(text, \"Rating\"))\n\t\t\treturn QI_Resort(2);\n\t}\n\n\n\tid = strstr(link, \"\\\\id\\\\\");\n\tmap = strstr(link, \"\\\\startmap\\\\\");\n\tif (id)\n\t{\n\t\tid+=4;\n\t\te = strchr(id, '\\\\');\n\t\tif (e)\n\t\t\t*e = 0;\n\t\tif (map)\n\t\t{\n\t\t\tmap += 10;\n\t\t\te = strchr(map, '\\\\');\n\t\t\tif (e)\n\t\t\t\t*e = 0;\n\t\t}\n\t\telse\n\t\t\tmap = \"\";\n\n\t\tfile = QI_FindArchive(id);\n\t\tif (!file)\n\t\t{\n\t\t\tCon_Printf(\"Unknown file \\\"%s\\\"\\n\", id);\n\t\t\treturn true;\n\t\t}\n\t\tQI_RunMap(file, map);\n\t\treturn true;\n\t}\n\treturn false;\n}\nstatic void QDECL QI_Tick(double realtime, double gametime)\n{\n\tif (dlcontext != -1)\n\t{\n\t\tqofs_t flen=0;\n\t\tif (dlcontext < 0 || filefuncs->GetLen(dlcontext, &flen))\n\t\t{\n\t\t\tint ofs = 0;\n\t\t\tchar *file;\n\t\t\tqboolean archive = true;\n\t\t\tif (flen == 0)\n\t\t\t{\n\t\t\t\tfilefuncs->Close(dlcontext);\n\t\t\t\tflen = filefuncs->Open(\"**plugconfig\", &dlcontext, 1);\n\t\t\t\tif (dlcontext == -1)\n\t\t\t\t{\t\n\t\t\t\t\tQI_RefreshMapList(false);\n\n\t\t\t\t\tif (packagemanager)\n\t\t\t\t\t{\n\t\t\t\t\t\tVFS_CLOSE(packagemanager);\n\t\t\t\t\t\tpackagemanager = NULL;\n\t\t\t\t\t}\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tarchive = false;\n\t\t\t}\n\t\t\tfile = malloc(flen+1);\n\t\t\tfile[flen] = 0;\n\t\t\tfilefuncs->Read(dlcontext, file, flen);\n\t\t\tfilefuncs->Close(dlcontext);\n\t\t\tif (archive)\n\t\t\t{\n\t\t\t\tfilefuncs->Open(\"**plugconfig\", &dlcontext, 2);\n\t\t\t\tif (dlcontext != -1)\n\t\t\t\t{\n\t\t\t\t\tfilefuncs->Write(dlcontext, file, flen);\n\t\t\t\t\tfilefuncs->Close(dlcontext);\n\t\t\t\t}\n\t\t\t}\n\t\t\tdlcontext = -1;\n\t\t\tdo\n\t\t\t{\n\t\t\t\tif (thedatabase)\n\t\t\t\t\tXML_Destroy(thedatabase);\n\t\t\t\tthedatabase = XML_Parse(file, &ofs, flen, false, \"\");\n\t\t\t} while(thedatabase && !thedatabase->child);\n\t\t\tfree(file);\n\n\t\t\tQI_RefreshMapList(false);\n\n//\t\t\tXML_ConPrintTree(thedatabase, \"quadicted_xml\", 0);\n\t\t}\n\t}\n\telse if (packagemanager)\n\t{\n\t\tif (!thedatabase && dlcontext == -1)\n\t\t{\n\t\t\tdlcontext = -2;\n\t\t\tif (filefuncs->Open(DATABASEURL, &dlcontext, 1) >= 0)\n\t\t\t\treturn;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tVFS_PRINTF(packagemanager, \"version 3\\n\");\n\t\t\tQI_WriteUpdateList(thedatabase, packagemanager);\n\t\t}\n\t\tVFS_CLOSE(packagemanager);\n\t\tpackagemanager = NULL;\n\t}\n}\n\nstatic int QDECL QI_ConExecuteCommand(qboolean isinsecure)\n{\n\tchar console[256];\n\tchar filter[256];\n\tif (isinsecure)\n\t\treturn false;\n\n\tcmdfuncs->Argv(0, console, sizeof(console));\n\tcmdfuncs->Args(filter, sizeof(filter));\n\tQI_UpdateFilter(filter);\n\n\tQI_RefreshMapList(true);\n\tconfuncs->SetConsoleFloat(console, \"linebuffered\", false);\n\tconfuncs->SetConsoleString(console, \"footer\", \"\");\n\treturn true;\n}\n\nstatic void QI_ExecuteCommand_f(void)\n{\n\tchar cmd[256];\n\tif (cmdfuncs->Argc() > 1)\n\t{\n\t\tcmdfuncs->Args(cmd, sizeof(cmd));\n\t\tQI_UpdateFilter(cmd);\n\t}\n\telse if (QI_SetupWindow(WINDOWNAME, false))\n\t{\n\t\tconfuncs->SetActive(WINDOWNAME);\n\t\treturn;\n\t}\n\n\tif (!thedatabase && dlcontext == -1)\n\t\tfilefuncs->Open(DATABASEURL, &dlcontext, 1);\n\n\tQI_RefreshMapList(true);\n}\n\nvoid QI_GenPackages(const char *url, vfsfile_t *pipe, qboolean favourcache)\n{\n\tif (thedatabase)\n\t{\t//already read it?... fine.\n\t\tVFS_PRINTF(pipe, \"version 3\\n\");\n\t\tQI_WriteUpdateList(thedatabase, pipe);\n\t\tVFS_CLOSE(pipe);\n\t\treturn;\n\t}\n\telse if (favourcache)\n\t{\t//just read the cached copy from disk. use the menu to update it.\n\t\txmltree_t *t = NULL;\n\t\tqhandle_t fd = -1;\n\t\tint ofs = 0;\n\n\t\tint flen = filefuncs->Open(\"**plugconfig\", &fd, 1);\n\t\tif (flen >= 0)\n\t\t{\n\t\t\tqbyte *file = malloc(flen+1);\n\t\t\tfile[flen] = 0;\n\t\t\tfilefuncs->Read(fd, file, flen);\n\t\t\tfilefuncs->Close(fd);\n\n\t\t\tdo\n\t\t\t{\n\t\t\t\tif (t)\n\t\t\t\t\tXML_Destroy(t);\n\t\t\t\tt = XML_Parse(file, &ofs, flen, false, \"\");\n\t\t\t} while(t && !t->child);\n\t\t\tfree(file);\n\n\t\t\tVFS_PRINTF(pipe, \"version 3\\n\");\n\t\t\tQI_WriteUpdateList(t, pipe);\n\n\t\t\tif (t)\n\t\t\t\tXML_Destroy(t);\n\t\t\tVFS_CLOSE(pipe);\n\t\t\treturn;\n\t\t}\n\t}\n\n\t//fill it in once we've got it...\n\tif (packagemanager)\n\t\tVFS_CLOSE(packagemanager);\n\tpackagemanager = pipe;\n}\nstatic plugupdatesourcefuncs_t sourcefuncs =\n{\n\t\"Quaddicted Maps\",\n\tQI_GenPackages\n};\n\nqboolean Plug_Init(void)\n{\n\tconfuncs = plugfuncs->GetEngineInterface(plugsubconsolefuncs_name, sizeof(*confuncs));\n\tfilefuncs = plugfuncs->GetEngineInterface(plugfsfuncs_name, sizeof(*filefuncs));\n\tclientfuncs = plugfuncs->GetEngineInterface(plugclientfuncs_name, sizeof(*clientfuncs));\n\n\tplugfuncs->ExportInterface(plugupdatesourcefuncs_name, &sourcefuncs, sizeof(sourcefuncs));\n\n\tif (confuncs && filefuncs && clientfuncs)\n\t{\n\t\tfilters.minrating = filters.maxrating = -1;\n\t\tplugfuncs->ExportFunction(\"UpdateVideo\", QI_UpdateVideo);\n\t\tif (plugfuncs->ExportFunction(\"Tick\", QI_Tick) &&\n\t\t\tplugfuncs->ExportFunction(\"Shutdown\", QI_Shutdown) &&\n\t\t\tplugfuncs->ExportFunction(\"ConExecuteCommand\", QI_ConExecuteCommand) &&\n\t\t\tplugfuncs->ExportFunction(\"ConsoleLink\", QI_ConsoleLink))\n\t\t{\n\t\t\tcmdfuncs->AddCommand(\"qi\", QI_ExecuteCommand_f, \"Select or install maps or mods from Quaddicted's database.\");\n\t\t\tcmdfuncs->AddCommand(\"quaddicted\", QI_ExecuteCommand_f, \"Select or install maps or mods from Quaddicted's database.\");\n\t\t\treturn true;\n\t\t}\n\t}\n\t//else not available in dedicated servers\n\treturn false;\n}"
  },
  {
    "path": "plugins/quake3/botlib/aasfile.h",
    "content": "/*\n===========================================================================\nCopyright (C) 1999-2005 Id Software, Inc.\n\nThis file is part of Quake III Arena source code.\n\nQuake III Arena source code is free software; you can redistribute it\nand/or modify it under the terms of the GNU General Public License as\npublished by the Free Software Foundation; either version 2 of the License,\nor (at your option) any later version.\n\nQuake III Arena source code is distributed in the hope that it will be\nuseful, but WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Quake III Arena source code; if not, write to the Free Software\nFoundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n===========================================================================\n*/\n\n\n//NOTE:\tint =\tdefault signed\n//\t\t\t\tdefault long\n\n#define AASID\t\t\t\t\t\t(('S'<<24)+('A'<<16)+('A'<<8)+'E')\n#define AASVERSION_OLD\t\t\t\t4\n#define AASVERSION\t\t\t\t\t5\n\n//presence types\n#define PRESENCE_NONE\t\t\t\t1\n#define PRESENCE_NORMAL\t\t\t\t2\n#define PRESENCE_CROUCH\t\t\t\t4\n\n//travel types\n#define MAX_TRAVELTYPES\t\t\t\t32\n#define TRAVEL_INVALID\t\t\t\t1\t\t//temporary not possible\n#define TRAVEL_WALK\t\t\t\t\t2\t\t//walking\n#define TRAVEL_CROUCH\t\t\t\t3\t\t//crouching\n#define TRAVEL_BARRIERJUMP\t\t\t4\t\t//jumping onto a barrier\n#define TRAVEL_JUMP\t\t\t\t\t5\t\t//jumping\n#define TRAVEL_LADDER\t\t\t\t6\t\t//climbing a ladder\n#define TRAVEL_WALKOFFLEDGE\t\t\t7\t\t//walking of a ledge\n#define TRAVEL_SWIM\t\t\t\t\t8\t\t//swimming\n#define TRAVEL_WATERJUMP\t\t\t9\t\t//jump out of the water\n#define TRAVEL_TELEPORT\t\t\t\t10\t\t//teleportation\n#define TRAVEL_ELEVATOR\t\t\t\t11\t\t//travel by elevator\n#define TRAVEL_ROCKETJUMP\t\t\t12\t\t//rocket jumping required for travel\n#define TRAVEL_BFGJUMP\t\t\t\t13\t\t//bfg jumping required for travel\n#define TRAVEL_GRAPPLEHOOK\t\t\t14\t\t//grappling hook required for travel\n#define TRAVEL_DOUBLEJUMP\t\t\t15\t\t//double jump\n#define TRAVEL_RAMPJUMP\t\t\t\t16\t\t//ramp jump\n#define TRAVEL_STRAFEJUMP\t\t\t17\t\t//strafe jump\n#define TRAVEL_JUMPPAD\t\t\t\t18\t\t//jump pad\n#define TRAVEL_FUNCBOB\t\t\t\t19\t\t//func bob\n\n//additional travel flags\n#define TRAVELTYPE_MASK\t\t\t\t0xFFFFFF\n#define TRAVELFLAG_NOTTEAM1\t\t\t(1 << 24)\n#define TRAVELFLAG_NOTTEAM2\t\t\t(2 << 24)\n\n//face flags\n#define FACE_SOLID\t\t\t\t\t1\t\t//just solid at the other side\n#define FACE_LADDER\t\t\t\t\t2\t\t//ladder\n#define FACE_GROUND\t\t\t\t\t4\t\t//standing on ground when in this face\n#define FACE_GAP\t\t\t\t\t8\t\t//gap in the ground\n#define FACE_LIQUID\t\t\t\t\t16\t\t//face seperating two areas with liquid\n#define FACE_LIQUIDSURFACE\t\t\t32\t\t//face seperating liquid and air\n#define FACE_BRIDGE\t\t\t\t\t64\t\t//can walk over this face if bridge is closed\n\n//area contents\n#define AREACONTENTS_WATER\t\t\t\t1\n#define AREACONTENTS_LAVA\t\t\t\t2\n#define AREACONTENTS_SLIME\t\t\t\t4\n#define AREACONTENTS_CLUSTERPORTAL\t\t8\n#define AREACONTENTS_TELEPORTAL\t\t\t16\n#define AREACONTENTS_ROUTEPORTAL\t\t32\n#define AREACONTENTS_TELEPORTER\t\t\t64\n#define AREACONTENTS_JUMPPAD\t\t\t128\n#define AREACONTENTS_DONOTENTER\t\t\t256\n#define\tAREACONTENTS_VIEWPORTAL\t\t\t512\n#define AREACONTENTS_MOVER\t\t\t\t1024\n#define AREACONTENTS_NOTTEAM1\t\t\t2048\n#define AREACONTENTS_NOTTEAM2\t\t\t4096\n//number of model of the mover inside this area\n#define AREACONTENTS_MODELNUMSHIFT\t\t24\n#define AREACONTENTS_MAXMODELNUM\t\t0xFF\n#define AREACONTENTS_MODELNUM\t\t\t(AREACONTENTS_MAXMODELNUM << AREACONTENTS_MODELNUMSHIFT)\n\n//area flags\n#define AREA_GROUNDED\t\t\t\t1\t\t//bot can stand on the ground\n#define AREA_LADDER\t\t\t\t\t2\t\t//area contains one or more ladder faces\n#define AREA_LIQUID\t\t\t\t\t4\t\t//area contains a liquid\n#define AREA_DISABLED\t\t\t\t8\t\t//area is disabled for routing when set\n#define AREA_BRIDGE\t\t\t\t\t16\t\t//area ontop of a bridge\n\n//aas file header lumps\n#define AAS_LUMPS\t\t\t\t\t14\n#define AASLUMP_BBOXES\t\t\t\t0\n#define AASLUMP_VERTEXES\t\t\t1\n#define AASLUMP_PLANES\t\t\t\t2\n#define AASLUMP_EDGES\t\t\t\t3\n#define AASLUMP_EDGEINDEX\t\t\t4\n#define AASLUMP_FACES\t\t\t\t5\n#define AASLUMP_FACEINDEX\t\t\t6\n#define AASLUMP_AREAS\t\t\t\t7\n#define AASLUMP_AREASETTINGS\t\t8\n#define AASLUMP_REACHABILITY\t\t9\n#define AASLUMP_NODES\t\t\t\t10\n#define AASLUMP_PORTALS\t\t\t\t11\n#define AASLUMP_PORTALINDEX\t\t\t12\n#define AASLUMP_CLUSTERS\t\t\t13\n\n//========== bounding box =========\n\n//bounding box\ntypedef struct aas_bbox_s\n{\n\tint presencetype;\n\tint flags;\n\tvec3_t mins, maxs;\n} aas_bbox_t;\n\n//============ settings ===========\n\n//reachability to another area\ntypedef struct aas_reachability_s\n{\n\tint areanum;\t\t\t\t\t\t//number of the reachable area\n\tint facenum;\t\t\t\t\t\t//number of the face towards the other area\n\tint edgenum;\t\t\t\t\t\t//number of the edge towards the other area\n\tvec3_t start;\t\t\t\t\t\t//start point of inter area movement\n\tvec3_t end;\t\t\t\t\t\t\t//end point of inter area movement\n\tint traveltype;\t\t\t\t\t//type of travel required to get to the area\n\tunsigned short int traveltime;//travel time of the inter area movement\n} aas_reachability_t;\n\n//area settings\ntypedef struct aas_areasettings_s\n{\n\t//could also add all kind of statistic fields\n\tint contents;\t\t\t\t\t\t//contents of the area\n\tint areaflags;\t\t\t\t\t\t//several area flags\n\tint presencetype;\t\t\t\t\t//how a bot can be present in this area\n\tint cluster;\t\t\t\t\t\t//cluster the area belongs to, if negative it's a portal\n\tint clusterareanum;\t\t\t\t//number of the area in the cluster\n\tint numreachableareas;\t\t\t//number of reachable areas from this one\n\tint firstreachablearea;\t\t\t//first reachable area in the reachable area index\n} aas_areasettings_t;\n\n//cluster portal\ntypedef struct aas_portal_s\n{\n\tint areanum;\t\t\t\t\t\t//area that is the actual portal\n\tint frontcluster;\t\t\t\t\t//cluster at front of portal\n\tint backcluster;\t\t\t\t\t//cluster at back of portal\n\tint clusterareanum[2];\t\t\t//number of the area in the front and back cluster\n} aas_portal_t;\n\n//cluster portal index\ntypedef int aas_portalindex_t;\n\n//cluster\ntypedef struct aas_cluster_s\n{\n\tint numareas;\t\t\t\t\t\t//number of areas in the cluster\n\tint numreachabilityareas;\t\t\t//number of areas with reachabilities\n\tint numportals;\t\t\t\t\t\t//number of cluster portals\n\tint firstportal;\t\t\t\t\t//first cluster portal in the index\n} aas_cluster_t;\n\n//============ 3d definition ============\n\ntypedef vec3_t aas_vertex_t;\n\n//just a plane in the third dimension\ntypedef struct aas_plane_s\n{\n\tvec3_t normal;\t\t\t\t\t\t//normal vector of the plane\n\tfloat dist;\t\t\t\t\t\t\t//distance of the plane (normal vector * distance = point in plane)\n\tint type;\n} aas_plane_t;\n\n//edge\ntypedef struct aas_edge_s\n{\n\tint v[2];\t\t\t\t\t\t\t//numbers of the vertexes of this edge\n} aas_edge_t;\n\n//edge index, negative if vertexes are reversed\ntypedef int aas_edgeindex_t;\n\n//a face bounds an area, often it will also seperate two areas\ntypedef struct aas_face_s\n{\n\tint planenum;\t\t\t\t\t\t//number of the plane this face is in\n\tint faceflags;\t\t\t\t\t\t//face flags (no use to create face settings for just this field)\n\tint numedges;\t\t\t\t\t\t//number of edges in the boundary of the face\n\tint firstedge;\t\t\t\t\t\t//first edge in the edge index\n\tint frontarea;\t\t\t\t\t\t//area at the front of this face\n\tint backarea;\t\t\t\t\t\t//area at the back of this face\n} aas_face_t;\n\n//face index, stores a negative index if backside of face\ntypedef int aas_faceindex_t;\n\n//area with a boundary of faces\ntypedef struct aas_area_s\n{\n\tint areanum;\t\t\t\t\t\t//number of this area\n\t//3d definition\n\tint numfaces;\t\t\t\t\t\t//number of faces used for the boundary of the area\n\tint firstface;\t\t\t\t\t\t//first face in the face index used for the boundary of the area\n\tvec3_t mins;\t\t\t\t\t\t//mins of the area\n\tvec3_t maxs;\t\t\t\t\t\t//maxs of the area\n\tvec3_t center;\t\t\t\t\t\t//'center' of the area\n} aas_area_t;\n\n//nodes of the bsp tree\ntypedef struct aas_node_s\n{\n\tint planenum;\n\tint children[2];\t\t\t\t\t//child nodes of this node, or areas as leaves when negative\n\t\t\t\t\t\t\t\t\t\t//when a child is zero it's a solid leaf\n} aas_node_t;\n\n//=========== aas file ===============\n\n//header lump\ntypedef struct\n{\n\tint fileofs;\n\tint filelen;\n} aas_lump_t;\n\n//aas file header\ntypedef struct aas_header_s\n{\n\tint ident;\n\tint version;\n\tint bspchecksum;\n\t//data entries\n\taas_lump_t lumps[AAS_LUMPS];\n} aas_header_t;\n\n\n//====== additional information ======\n/*\n\n-\twhen a node child is a solid leaf the node child number is zero\n-\ttwo adjacent areas (sharing a plane at opposite sides) share a face\n\tthis face is a portal between the areas\n-\twhen an area uses a face from the faceindex with a positive index\n\tthen the face plane normal points into the area\n-\tthe face edges are stored counter clockwise using the edgeindex\n-\ttwo adjacent convex areas (sharing a face) only share One face\n\tthis is a simple result of the areas being convex\n-\tthe areas can't have a mixture of ground and gap faces\n\tother mixtures of faces in one area are allowed\n-\tareas with the AREACONTENTS_CLUSTERPORTAL in the settings have\n\tthe cluster number set to the negative portal number\n-\tedge zero is a dummy\n-\tface zero is a dummy\n-\tarea zero is a dummy\n-\tnode zero is a dummy\n*/\n"
  },
  {
    "path": "plugins/quake3/botlib/be_aas.h",
    "content": "/*\n===========================================================================\nCopyright (C) 1999-2005 Id Software, Inc.\n\nThis file is part of Quake III Arena source code.\n\nQuake III Arena source code is free software; you can redistribute it\nand/or modify it under the terms of the GNU General Public License as\npublished by the Free Software Foundation; either version 2 of the License,\nor (at your option) any later version.\n\nQuake III Arena source code is distributed in the hope that it will be\nuseful, but WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Quake III Arena source code; if not, write to the Free Software\nFoundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n===========================================================================\n*/\n//\n\n/*****************************************************************************\n * name:\t\tbe_aas.h\n *\n * desc:\t\tArea Awareness System, stuff exported to the AI\n *\n * $Archive: /source/code/botlib/be_aas.h $\n *\n *****************************************************************************/\n\n#ifndef MAX_STRINGFIELD\n#define MAX_STRINGFIELD\t\t\t\t80\n#endif\n\n//travel flags\n#define TFL_INVALID\t\t\t\t0x00000001\t//traveling temporary not possible\n#define TFL_WALK\t\t\t\t0x00000002\t//walking\n#define TFL_CROUCH\t\t\t\t0x00000004\t//crouching\n#define TFL_BARRIERJUMP\t\t\t0x00000008\t//jumping onto a barrier\n#define TFL_JUMP\t\t\t\t0x00000010\t//jumping\n#define TFL_LADDER\t\t\t\t0x00000020\t//climbing a ladder\n#define TFL_WALKOFFLEDGE\t\t0x00000080\t//walking of a ledge\n#define TFL_SWIM\t\t\t\t0x00000100\t//swimming\n#define TFL_WATERJUMP\t\t\t0x00000200\t//jumping out of the water\n#define TFL_TELEPORT\t\t\t0x00000400\t//teleporting\n#define TFL_ELEVATOR\t\t\t0x00000800\t//elevator\n#define TFL_ROCKETJUMP\t\t\t0x00001000\t//rocket jumping\n#define TFL_BFGJUMP\t\t\t\t0x00002000\t//bfg jumping\n#define TFL_GRAPPLEHOOK\t\t\t0x00004000\t//grappling hook\n#define TFL_DOUBLEJUMP\t\t\t0x00008000\t//double jump\n#define TFL_RAMPJUMP\t\t\t0x00010000\t//ramp jump\n#define TFL_STRAFEJUMP\t\t\t0x00020000\t//strafe jump\n#define TFL_JUMPPAD\t\t\t\t0x00040000\t//jump pad\n#define TFL_AIR\t\t\t\t\t0x00080000\t//travel through air\n#define TFL_WATER\t\t\t\t0x00100000\t//travel through water\n#define TFL_SLIME\t\t\t\t0x00200000\t//travel through slime\n#define TFL_LAVA\t\t\t\t0x00400000\t//travel through lava\n#define TFL_DONOTENTER\t\t\t0x00800000\t//travel through donotenter area\n#define TFL_FUNCBOB\t\t\t\t0x01000000\t//func bobbing\n#define TFL_FLIGHT\t\t\t\t0x02000000\t//flight\n#define TFL_BRIDGE\t\t\t\t0x04000000\t//move over a bridge\n//\n#define TFL_NOTTEAM1\t\t\t0x08000000\t//not team 1\n#define TFL_NOTTEAM2\t\t\t0x10000000\t//not team 2\n\n//default travel flags\n#define TFL_DEFAULT\tTFL_WALK|TFL_CROUCH|TFL_BARRIERJUMP|\\\n\tTFL_JUMP|TFL_LADDER|\\\n\tTFL_WALKOFFLEDGE|TFL_SWIM|TFL_WATERJUMP|\\\n\tTFL_TELEPORT|TFL_ELEVATOR|\\\n\tTFL_AIR|TFL_WATER|TFL_JUMPPAD|TFL_FUNCBOB\n\ntypedef enum\n{\n\tSOLID_NOT,\t\t\t// no interaction with other objects\n\tSOLID_TRIGGER,\t\t// only touch when inside, after moving\n\tSOLID_BBOX,\t\t\t// touch on edge\n\tSOLID_BSP\t\t\t// bsp clip, touch on edge\n} solid_t;\n\n//a trace is returned when a box is swept through the AAS world\ntypedef struct aas_trace_s\n{\n\tqboolean\tstartsolid;\t// if true, the initial point was in a solid area\n\tfloat\t\tfraction;\t// time completed, 1.0 = didn't hit anything\n\tvec3_t\t\tendpos;\t\t// final position\n\tint\t\t\tent;\t\t// entity blocking the trace\n\tint\t\t\tlastarea;\t// last area the trace was in (zero if none)\n\tint\t\t\tarea;\t\t// area blocking the trace (zero if none)\n\tint\t\t\tplanenum;\t// number of the plane that was hit\n} aas_trace_t;\n\n/* Defined in botlib.h\n\n//bsp_trace_t hit surface\ntypedef struct bsp_surface_s\n{\n\tchar name[16];\n\tint flags;\n\tint value;\n} bsp_surface_t;\n\n//a trace is returned when a box is swept through the BSP world\ntypedef struct bsp_trace_s\n{\n\tqboolean\t\tallsolid;\t// if true, plane is not valid\n\tqboolean\t\tstartsolid;\t// if true, the initial point was in a solid area\n\tfloat\t\t\tfraction;\t// time completed, 1.0 = didn't hit anything\n\tvec3_t\t\t\tendpos;\t\t// final position\n\tcplane_t\t\tplane;\t\t// surface normal at impact\n\tfloat\t\t\texp_dist;\t// expanded plane distance\n\tint\t\t\t\tsidenum;\t// number of the brush side hit\n\tbsp_surface_t\tsurface;\t// hit surface\n\tint\t\t\t\tcontents;\t// contents on other side of surface hit\n\tint\t\t\t\tent;\t\t// number of entity hit\n} bsp_trace_t;\n//\n*/\n\n//entity info\ntypedef struct aas_entityinfo_s\n{\n\tint\t\tvalid;\t\t\t// true if updated this frame\n\tint\t\ttype;\t\t\t// entity type\n\tint\t\tflags;\t\t\t// entity flags\n\tfloat\tltime;\t\t\t// local time\n\tfloat\tupdate_time;\t// time between last and current update\n\tint\t\tnumber;\t\t\t// number of the entity\n\tvec3_t\torigin;\t\t\t// origin of the entity\n\tvec3_t\tangles;\t\t\t// angles of the model\n\tvec3_t\told_origin;\t\t// for lerping\n\tvec3_t\tlastvisorigin;\t// last visible origin\n\tvec3_t\tmins;\t\t\t// bounding box minimums\n\tvec3_t\tmaxs;\t\t\t// bounding box maximums\n\tint\t\tgroundent;\t\t// ground entity\n\tint\t\tsolid;\t\t\t// solid type\n\tint\t\tmodelindex;\t\t// model used\n\tint\t\tmodelindex2;\t// weapons, CTF flags, etc\n\tint\t\tframe;\t\t\t// model frame number\n\tint\t\tevent;\t\t\t// impulse events -- muzzle flashes, footsteps, etc\n\tint\t\teventParm;\t\t// even parameter\n\tint\t\tpowerups;\t\t// bit flags\n\tint\t\tweapon;\t\t\t// determines weapon and flash model, etc\n\tint\t\tlegsAnim;\t\t// mask off ANIM_TOGGLEBIT\n\tint\t\ttorsoAnim;\t\t// mask off ANIM_TOGGLEBIT\n} aas_entityinfo_t;\n\n// area info\ntypedef struct aas_areainfo_s\n{\n\tint contents;\n\tint flags;\n\tint presencetype;\n\tint cluster;\n\tvec3_t mins;\n\tvec3_t maxs;\n\tvec3_t center;\n} aas_areainfo_t;\n\n// client movement prediction stop events, stop as soon as:\n#define SE_NONE\t\t\t\t\t0\n#define SE_HITGROUND\t\t\t1\t\t// the ground is hit\n#define SE_LEAVEGROUND\t\t\t2\t\t// there's no ground\n#define SE_ENTERWATER\t\t\t4\t\t// water is entered\n#define SE_ENTERSLIME\t\t\t8\t\t// slime is entered\n#define SE_ENTERLAVA\t\t\t16\t\t// lava is entered\n#define SE_HITGROUNDDAMAGE\t\t32\t\t// the ground is hit with damage\n#define SE_GAP\t\t\t\t\t64\t\t// there's a gap\n#define SE_TOUCHJUMPPAD\t\t\t128\t\t// touching a jump pad area\n#define SE_TOUCHTELEPORTER\t\t256\t\t// touching teleporter\n#define SE_ENTERAREA\t\t\t512\t\t// the given stoparea is entered\n#define SE_HITGROUNDAREA\t\t1024\t// a ground face in the area is hit\n#define SE_HITBOUNDINGBOX\t\t2048\t// hit the specified bounding box\n#define SE_TOUCHCLUSTERPORTAL\t4096\t// touching a cluster portal\n\ntypedef struct aas_clientmove_s\n{\n\tvec3_t endpos;\t\t\t//position at the end of movement prediction\n\tint endarea;\t\t\t//area at end of movement prediction\n\tvec3_t velocity;\t\t//velocity at the end of movement prediction\n\taas_trace_t trace;\t\t//last trace\n\tint presencetype;\t\t//presence type at end of movement prediction\n\tint stopevent;\t\t\t//event that made the prediction stop\n\tint endcontents;\t\t//contents at the end of movement prediction\n\tfloat time;\t\t\t\t//time predicted ahead\n\tint frames;\t\t\t\t//number of frames predicted ahead\n} aas_clientmove_t;\n\n// alternate route goals\n#define ALTROUTEGOAL_ALL\t\t\t\t1\n#define ALTROUTEGOAL_CLUSTERPORTALS\t\t2\n#define ALTROUTEGOAL_VIEWPORTALS\t\t4\n\ntypedef struct aas_altroutegoal_s\n{\n\tvec3_t origin;\n\tint areanum;\n\tunsigned short starttraveltime;\n\tunsigned short goaltraveltime;\n\tunsigned short extratraveltime;\n} aas_altroutegoal_t;\n\n// route prediction stop events\n#define RSE_NONE\t\t\t\t0\n#define RSE_NOROUTE\t\t\t\t1\t//no route to goal\n#define RSE_USETRAVELTYPE\t\t2\t//stop as soon as on of the given travel types is used\n#define RSE_ENTERCONTENTS\t\t4\t//stop when entering the given contents\n#define RSE_ENTERAREA\t\t\t8\t//stop when entering the given area\n\ntypedef struct aas_predictroute_s\n{\n\tvec3_t endpos;\t\t\t//position at the end of movement prediction\n\tint endarea;\t\t\t//area at end of movement prediction\n\tint stopevent;\t\t\t//event that made the prediction stop\n\tint endcontents;\t\t//contents at the end of movement prediction\n\tint endtravelflags;\t\t//end travel flags\n\tint numareas;\t\t\t//number of areas predicted ahead\n\tint time;\t\t\t\t//time predicted ahead (in hundreth of a sec)\n} aas_predictroute_t;\n"
  },
  {
    "path": "plugins/quake3/botlib/be_aas_bsp.h",
    "content": "/*\n===========================================================================\nCopyright (C) 1999-2005 Id Software, Inc.\n\nThis file is part of Quake III Arena source code.\n\nQuake III Arena source code is free software; you can redistribute it\nand/or modify it under the terms of the GNU General Public License as\npublished by the Free Software Foundation; either version 2 of the License,\nor (at your option) any later version.\n\nQuake III Arena source code is distributed in the hope that it will be\nuseful, but WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Quake III Arena source code; if not, write to the Free Software\nFoundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n===========================================================================\n*/\n\n/*****************************************************************************\n * name:\t\tbe_aas_bsp.h\n *\n * desc:\t\tAAS\n *\n * $Archive: /source/code/botlib/be_aas_bsp.h $\n *\n *****************************************************************************/\n\n#ifdef AASINTERN\n//loads the given BSP file\nint AAS_LoadBSPFile(void);\n//dump the loaded BSP data\nvoid AAS_DumpBSPData(void);\n//unlink the given entity from the bsp tree leaves\nvoid AAS_UnlinkFromBSPLeaves(bsp_link_t *leaves);\n//link the given entity to the bsp tree leaves of the given model\nbsp_link_t *AAS_BSPLinkEntity(vec3_t absmins,\n\t\t\t\t\t\t\t\t\t\tvec3_t absmaxs,\n\t\t\t\t\t\t\t\t\t\tint entnum,\n\t\t\t\t\t\t\t\t\t\tint modelnum);\n\n//calculates collision with given entity\nqboolean AAS_EntityCollision(int entnum,\n\t\t\t\t\t\t\t\t\t\tvec3_t start,\n\t\t\t\t\t\t\t\t\t\tvec3_t boxmins,\n\t\t\t\t\t\t\t\t\t\tvec3_t boxmaxs,\n\t\t\t\t\t\t\t\t\t\tvec3_t end,\n\t\t\t\t\t\t\t\t\t\tint contentmask,\n\t\t\t\t\t\t\t\t\t\tbsp_trace_t *trace);\n//for debugging\nvoid AAS_PrintFreeBSPLinks(char *str);\n//\n#endif //AASINTERN\n\n#define MAX_EPAIRKEY\t\t128\n\n//trace through the world\nbsp_trace_t AAS_Trace(\tvec3_t start,\n\t\t\t\t\t\t\t\tvec3_t mins,\n\t\t\t\t\t\t\t\tvec3_t maxs,\n\t\t\t\t\t\t\t\tvec3_t end,\n\t\t\t\t\t\t\t\tint passent,\n\t\t\t\t\t\t\t\tint contentmask);\n//returns the contents at the given point\nint AAS_PointContents(vec3_t point);\n//returns true when p2 is in the PVS of p1\nqboolean AAS_inPVS(vec3_t p1, vec3_t p2);\n//returns true when p2 is in the PHS of p1\nqboolean AAS_inPHS(vec3_t p1, vec3_t p2);\n//returns true if the given areas are connected\nqboolean AAS_AreasConnected(int area1, int area2);\n//creates a list with entities totally or partly within the given box\nint AAS_BoxEntities(vec3_t absmins, vec3_t absmaxs, int *list, int maxcount);\n//gets the mins, maxs and origin of a BSP model\nvoid AAS_BSPModelMinsMaxsOrigin(int modelnum, vec3_t angles, vec3_t mins, vec3_t maxs, vec3_t origin);\n//handle to the next bsp entity\nint AAS_NextBSPEntity(int ent);\n//return the value of the BSP epair key\nint AAS_ValueForBSPEpairKey(int ent, char *key, char *value, int size);\n//get a vector for the BSP epair key\nint AAS_VectorForBSPEpairKey(int ent, char *key, vec3_t v);\n//get a float for the BSP epair key\nint AAS_FloatForBSPEpairKey(int ent, char *key, float *value);\n//get an integer for the BSP epair key\nint AAS_IntForBSPEpairKey(int ent, char *key, int *value);\n\n"
  },
  {
    "path": "plugins/quake3/botlib/be_aas_bspq3.c",
    "content": "/*\n===========================================================================\nCopyright (C) 1999-2005 Id Software, Inc.\n\nThis file is part of Quake III Arena source code.\n\nQuake III Arena source code is free software; you can redistribute it\nand/or modify it under the terms of the GNU General Public License as\npublished by the Free Software Foundation; either version 2 of the License,\nor (at your option) any later version.\n\nQuake III Arena source code is distributed in the hope that it will be\nuseful, but WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Quake III Arena source code; if not, write to the Free Software\nFoundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n===========================================================================\n*/\n\n/*****************************************************************************\n * name:\t\tbe_aas_bspq3.c\n *\n * desc:\t\tBSP, Environment Sampling\n *\n * $Archive: /MissionPack/code/botlib/be_aas_bspq3.c $\n *\n *****************************************************************************/\n\n#include \"q_shared.h\"\n#include \"l_memory.h\"\n#include \"l_script.h\"\n#include \"l_precomp.h\"\n#include \"l_struct.h\"\n#include \"aasfile.h\"\n#include \"botlib.h\"\n#include \"be_aas.h\"\n#include \"be_aas_funcs.h\"\n#include \"be_aas_def.h\"\n\nextern botlib_import_t botimport;\n\n//#define TRACE_DEBUG\n\n#define ON_EPSILON\t\t0.005\n//#define DEG2RAD( a ) (( a * M_PI ) / 180.0F)\n\n#define MAX_BSPENTITIES\t\t2048\n\ntypedef struct rgb_s\n{\n\tint red;\n\tint green;\n\tint blue;\n} rgb_t;\n\n//bsp entity epair\ntypedef struct bsp_epair_s\n{\n\tchar *key;\n\tchar *value;\n\tstruct bsp_epair_s *next;\n} bsp_epair_t;\n\n//bsp data entity\ntypedef struct bsp_entity_s\n{\n\tbsp_epair_t *epairs;\n} bsp_entity_t;\n\n//id Sofware BSP data\ntypedef struct bsp_s\n{\n\t//true when bsp file is loaded\n\tint loaded;\n\t//entity data\n\tint entdatasize;\n\tchar *dentdata;\n\t//bsp entities\n\tint numentities;\n\tbsp_entity_t entities[MAX_BSPENTITIES];\n} bsp_t;\n\n//global bsp\nbsp_t bspworld;\n\n\n#ifdef BSP_DEBUG\ntypedef struct cname_s\n{\n\tint value;\n\tchar *name;\n} cname_t;\n\ncname_t contentnames[] =\n{\n\t{CONTENTS_SOLID,\"CONTENTS_SOLID\"},\n\t{CONTENTS_WINDOW,\"CONTENTS_WINDOW\"},\n\t{CONTENTS_AUX,\"CONTENTS_AUX\"},\n\t{CONTENTS_LAVA,\"CONTENTS_LAVA\"},\n\t{CONTENTS_SLIME,\"CONTENTS_SLIME\"},\n\t{CONTENTS_WATER,\"CONTENTS_WATER\"},\n\t{CONTENTS_MIST,\"CONTENTS_MIST\"},\n\t{LAST_VISIBLE_CONTENTS,\"LAST_VISIBLE_CONTENTS\"},\n\n\t{CONTENTS_AREAPORTAL,\"CONTENTS_AREAPORTAL\"},\n\t{CONTENTS_PLAYERCLIP,\"CONTENTS_PLAYERCLIP\"},\n\t{CONTENTS_MONSTERCLIP,\"CONTENTS_MONSTERCLIP\"},\n\t{CONTENTS_CURRENT_0,\"CONTENTS_CURRENT_0\"},\n\t{CONTENTS_CURRENT_90,\"CONTENTS_CURRENT_90\"},\n\t{CONTENTS_CURRENT_180,\"CONTENTS_CURRENT_180\"},\n\t{CONTENTS_CURRENT_270,\"CONTENTS_CURRENT_270\"},\n\t{CONTENTS_CURRENT_UP,\"CONTENTS_CURRENT_UP\"},\n\t{CONTENTS_CURRENT_DOWN,\"CONTENTS_CURRENT_DOWN\"},\n\t{CONTENTS_ORIGIN,\"CONTENTS_ORIGIN\"},\n\t{CONTENTS_MONSTER,\"CONTENTS_MONSTER\"},\n\t{CONTENTS_DEADMONSTER,\"CONTENTS_DEADMONSTER\"},\n\t{CONTENTS_DETAIL,\"CONTENTS_DETAIL\"},\n\t{CONTENTS_TRANSLUCENT,\"CONTENTS_TRANSLUCENT\"},\n\t{CONTENTS_LADDER,\"CONTENTS_LADDER\"},\n\t{0, 0}\n};\n\nvoid PrintContents(int contents)\n{\n\tint i;\n\n\tfor (i = 0; contentnames[i].value; i++)\n\t{\n\t\tif (contents & contentnames[i].value)\n\t\t{\n\t\t\tbotimport.Print(PRT_MESSAGE, \"%s\\n\", contentnames[i].name);\n\t\t} //end if\n\t} //end for\n} //end of the function PrintContents\n\n#endif // BSP_DEBUG\n//===========================================================================\n// traces axial boxes of any size through the world\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nbsp_trace_t AAS_Trace(vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, int passent, int contentmask)\n{\n\tbsp_trace_t bsptrace;\n\tbotimport.Trace(&bsptrace, start, mins, maxs, end, passent, contentmask);\n\treturn bsptrace;\n} //end of the function AAS_Trace\n//===========================================================================\n// returns the contents at the given point\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint AAS_PointContents(vec3_t point)\n{\n\treturn botimport.PointContents(point);\n} //end of the function AAS_PointContents\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nqboolean AAS_EntityCollision(int entnum,\n\t\t\t\t\tvec3_t start, vec3_t boxmins, vec3_t boxmaxs, vec3_t end,\n\t\t\t\t\t\t\t\tint contentmask, bsp_trace_t *trace)\n{\n\tbsp_trace_t enttrace;\n\n\tbotimport.EntityTrace(&enttrace, start, boxmins, boxmaxs, end, entnum, contentmask);\n\tif (enttrace.fraction < trace->fraction)\n\t{\n\t\tCom_Memcpy(trace, &enttrace, sizeof(bsp_trace_t));\n\t\treturn qtrue;\n\t} //end if\n\treturn qfalse;\n} //end of the function AAS_EntityCollision\n//===========================================================================\n// returns true if in Potentially Hearable Set\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nqboolean AAS_inPVS(vec3_t p1, vec3_t p2)\n{\n\treturn botimport.inPVS(p1, p2)?qtrue:qfalse;\n} //end of the function AAS_InPVS\n//===========================================================================\n// returns true if in Potentially Visible Set\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nqboolean AAS_inPHS(vec3_t p1, vec3_t p2)\n{\n\treturn qtrue;\n} //end of the function AAS_inPHS\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid AAS_BSPModelMinsMaxsOrigin(int modelnum, vec3_t angles, vec3_t mins, vec3_t maxs, vec3_t origin)\n{\n\tbotimport.BSPModelMinsMaxsOrigin(modelnum, angles, mins, maxs, origin);\n} //end of the function AAS_BSPModelMinsMaxs\n//===========================================================================\n// unlinks the entity from all leaves\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid AAS_UnlinkFromBSPLeaves(bsp_link_t *leaves)\n{\n} //end of the function AAS_UnlinkFromBSPLeaves\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nbsp_link_t *AAS_BSPLinkEntity(vec3_t absmins, vec3_t absmaxs, int entnum, int modelnum)\n{\n\treturn NULL;\n} //end of the function AAS_BSPLinkEntity\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint AAS_BoxEntities(vec3_t absmins, vec3_t absmaxs, int *list, int maxcount)\n{\n\treturn 0;\n} //end of the function AAS_BoxEntities\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint AAS_NextBSPEntity(int ent)\n{\n\tent++;\n\tif (ent >= 1 && ent < bspworld.numentities) return ent;\n\treturn 0;\n} //end of the function AAS_NextBSPEntity\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint AAS_BSPEntityInRange(int ent)\n{\n\tif (ent <= 0 || ent >= bspworld.numentities)\n\t{\n\t\tbotimport.Print(PRT_MESSAGE, \"bsp entity out of range\\n\");\n\t\treturn qfalse;\n\t} //end if\n\treturn qtrue;\n} //end of the function AAS_BSPEntityInRange\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint AAS_ValueForBSPEpairKey(int ent, char *key, char *value, int size)\n{\n\tbsp_epair_t *epair;\n\n\tvalue[0] = '\\0';\n\tif (!AAS_BSPEntityInRange(ent)) return qfalse;\n\tfor (epair = bspworld.entities[ent].epairs; epair; epair = epair->next)\n\t{\n\t\tif (!strcmp(epair->key, key))\n\t\t{\n\t\t\tstrncpy(value, epair->value, size-1);\n\t\t\tvalue[size-1] = '\\0';\n\t\t\treturn qtrue;\n\t\t} //end if\n\t} //end for\n\treturn qfalse;\n} //end of the function AAS_FindBSPEpair\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint AAS_VectorForBSPEpairKey(int ent, char *key, vec3_t v)\n{\n\tchar buf[MAX_EPAIRKEY];\n\tdouble v1, v2, v3;\n\n\tVectorClear(v);\n\tif (!AAS_ValueForBSPEpairKey(ent, key, buf, MAX_EPAIRKEY)) return qfalse;\n\t//scanf into doubles, then assign, so it is vec_t size independent\n\tv1 = v2 = v3 = 0;\n\tsscanf(buf, \"%lf %lf %lf\", &v1, &v2, &v3);\n\tv[0] = v1;\n\tv[1] = v2;\n\tv[2] = v3;\n\treturn qtrue;\n} //end of the function AAS_VectorForBSPEpairKey\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint AAS_FloatForBSPEpairKey(int ent, char *key, float *value)\n{\n\tchar buf[MAX_EPAIRKEY];\n\t\n\t*value = 0;\n\tif (!AAS_ValueForBSPEpairKey(ent, key, buf, MAX_EPAIRKEY)) return qfalse;\n\t*value = atof(buf);\n\treturn qtrue;\n} //end of the function AAS_FloatForBSPEpairKey\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint AAS_IntForBSPEpairKey(int ent, char *key, int *value)\n{\n\tchar buf[MAX_EPAIRKEY];\n\t\n\t*value = 0;\n\tif (!AAS_ValueForBSPEpairKey(ent, key, buf, MAX_EPAIRKEY)) return qfalse;\n\t*value = atoi(buf);\n\treturn qtrue;\n} //end of the function AAS_IntForBSPEpairKey\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid AAS_FreeBSPEntities(void)\n{\n\tint i;\n\tbsp_entity_t *ent;\n\tbsp_epair_t *epair, *nextepair;\n\n\tfor (i = 1; i < bspworld.numentities; i++)\n\t{\n\t\tent = &bspworld.entities[i];\n\t\tfor (epair = ent->epairs; epair; epair = nextepair)\n\t\t{\n\t\t\tnextepair = epair->next;\n\t\t\t//\n\t\t\tif (epair->key) FreeMemory(epair->key);\n\t\t\tif (epair->value) FreeMemory(epair->value);\n\t\t\tFreeMemory(epair);\n\t\t} //end for\n\t} //end for\n\tbspworld.numentities = 0;\n} //end of the function AAS_FreeBSPEntities\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid AAS_ParseBSPEntities(void)\n{\n\tscript_t *script;\n\ttoken_t token;\n\tbsp_entity_t *ent;\n\tbsp_epair_t *epair;\n\n\tscript = LoadScriptMemory(bspworld.dentdata, bspworld.entdatasize, \"entdata\");\n\tSetScriptFlags(script, SCFL_NOSTRINGWHITESPACES|SCFL_NOSTRINGESCAPECHARS);//SCFL_PRIMITIVE);\n\n\tbspworld.numentities = 1;\n\n\twhile(PS_ReadToken(script, &token))\n\t{\n\t\tif (strcmp(token.string, \"{\"))\n\t\t{\n\t\t\tScriptError(script, \"invalid %s\\n\", token.string);\n\t\t\tAAS_FreeBSPEntities();\n\t\t\tFreeScript(script);\n\t\t\treturn;\n\t\t} //end if\n\t\tif (bspworld.numentities >= MAX_BSPENTITIES)\n\t\t{\n\t\t\tbotimport.Print(PRT_MESSAGE, \"too many entities in BSP file\\n\");\n\t\t\tbreak;\n\t\t} //end if\n\t\tent = &bspworld.entities[bspworld.numentities];\n\t\tbspworld.numentities++;\n\t\tent->epairs = NULL;\n\t\twhile(PS_ReadToken(script, &token))\n\t\t{\n\t\t\tif (!strcmp(token.string, \"}\")) break;\n\t\t\tepair = (bsp_epair_t *) GetClearedHunkMemory(sizeof(bsp_epair_t));\n\t\t\tepair->next = ent->epairs;\n\t\t\tent->epairs = epair;\n\t\t\tif (token.type != TT_STRING)\n\t\t\t{\n\t\t\t\tScriptError(script, \"invalid %s\\n\", token.string);\n\t\t\t\tAAS_FreeBSPEntities();\n\t\t\t\tFreeScript(script);\n\t\t\t\treturn;\n\t\t\t} //end if\n\t\t\tStripDoubleQuotes(token.string);\n\t\t\tepair->key = (char *) GetHunkMemory(strlen(token.string) + 1);\n\t\t\tstrcpy(epair->key, token.string);\n\t\t\tif (!PS_ExpectTokenType(script, TT_STRING, 0, &token))\n\t\t\t{\n\t\t\t\tAAS_FreeBSPEntities();\n\t\t\t\tFreeScript(script);\n\t\t\t\treturn;\n\t\t\t} //end if\n\t\t\tStripDoubleQuotes(token.string);\n\t\t\tepair->value = (char *) GetHunkMemory(strlen(token.string) + 1);\n\t\t\tstrcpy(epair->value, token.string);\n\t\t} //end while\n\t\tif (strcmp(token.string, \"}\"))\n\t\t{\n\t\t\tScriptError(script, \"missing }\\n\");\n\t\t\tAAS_FreeBSPEntities();\n\t\t\tFreeScript(script);\n\t\t\treturn;\n\t\t} //end if\n\t} //end while\n\tFreeScript(script);\n} //end of the function AAS_ParseBSPEntities\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint AAS_BSPTraceLight(vec3_t start, vec3_t end, vec3_t endpos, int *red, int *green, int *blue)\n{\n\treturn 0;\n} //end of the function AAS_BSPTraceLight\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid AAS_DumpBSPData(void)\n{\n\tAAS_FreeBSPEntities();\n\n\tif (bspworld.dentdata) FreeMemory(bspworld.dentdata);\n\tbspworld.dentdata = NULL;\n\tbspworld.entdatasize = 0;\n\t//\n\tbspworld.loaded = qfalse;\n\tCom_Memset( &bspworld, 0, sizeof(bspworld) );\n} //end of the function AAS_DumpBSPData\n//===========================================================================\n// load an bsp file\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint AAS_LoadBSPFile(void)\n{\n\tAAS_DumpBSPData();\n\tbspworld.entdatasize = strlen(botimport.BSPEntityData()) + 1;\n\tbspworld.dentdata = (char *) GetClearedHunkMemory(bspworld.entdatasize);\n\tCom_Memcpy(bspworld.dentdata, botimport.BSPEntityData(), bspworld.entdatasize);\n\tAAS_ParseBSPEntities();\n\tbspworld.loaded = qtrue;\n\treturn BLERR_NOERROR;\n} //end of the function AAS_LoadBSPFile\n"
  },
  {
    "path": "plugins/quake3/botlib/be_aas_cluster.c",
    "content": "/*\n===========================================================================\nCopyright (C) 1999-2005 Id Software, Inc.\n\nThis file is part of Quake III Arena source code.\n\nQuake III Arena source code is free software; you can redistribute it\nand/or modify it under the terms of the GNU General Public License as\npublished by the Free Software Foundation; either version 2 of the License,\nor (at your option) any later version.\n\nQuake III Arena source code is distributed in the hope that it will be\nuseful, but WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Quake III Arena source code; if not, write to the Free Software\nFoundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n===========================================================================\n*/\n\n/*****************************************************************************\n * name:\t\tbe_aas_cluster.c\n *\n * desc:\t\tarea clustering\n *\n * $Archive: /MissionPack/code/botlib/be_aas_cluster.c $\n *\n *****************************************************************************/\n\n#include \"q_shared.h\"\n#include \"l_memory.h\"\n#include \"l_script.h\"\n#include \"l_precomp.h\"\n#include \"l_struct.h\"\n#include \"l_log.h\"\n#include \"l_memory.h\"\n#include \"l_libvar.h\"\n#include \"aasfile.h\"\n#include \"botlib.h\"\n#include \"be_aas.h\"\n#include \"be_aas_funcs.h\"\n#include \"be_aas_def.h\"\n#include \"be_aas_cluster.h\"\n\nextern botlib_import_t botimport;\n\n#define AAS_MAX_PORTALS\t\t\t\t\t65536\n#define AAS_MAX_PORTALINDEXSIZE\t\t\t65536\n#define AAS_MAX_CLUSTERS\t\t\t\t65536\n//\n#define MAX_PORTALAREAS\t\t\t1024\n\n// do not flood through area faces, only use reachabilities\nint nofaceflood = qtrue;\n\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid AAS_RemoveClusterAreas(void)\n{\n\tint i;\n\n\tfor (i = 1; i < aasworld.numareas; i++)\n\t{\n\t\taasworld.areasettings[i].cluster = 0;\n\t} //end for\n} //end of the function AAS_RemoveClusterAreas\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid AAS_ClearCluster(int clusternum)\n{\n\tint i;\n\n\tfor (i = 1; i < aasworld.numareas; i++)\n\t{\n\t\tif (aasworld.areasettings[i].cluster == clusternum)\n\t\t{\n\t\t\taasworld.areasettings[i].cluster = 0;\n\t\t} //end if\n\t} //end for\n} //end of the function AAS_ClearCluster\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid AAS_RemovePortalsClusterReference(int clusternum)\n{\n\tint portalnum;\n\n\tfor (portalnum = 1; portalnum < aasworld.numportals; portalnum++)\n\t{\n\t\tif (aasworld.portals[portalnum].frontcluster == clusternum)\n\t\t{\n\t\t\taasworld.portals[portalnum].frontcluster = 0;\n\t\t} //end if\n\t\tif (aasworld.portals[portalnum].backcluster == clusternum)\n\t\t{\n\t\t\taasworld.portals[portalnum].backcluster = 0;\n\t\t} //end if\n\t} //end for\n} //end of the function AAS_RemovePortalsClusterReference\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint AAS_UpdatePortal(int areanum, int clusternum)\n{\n\tint portalnum;\n\taas_portal_t *portal;\n\taas_cluster_t *cluster;\n\n\t//find the portal of the area\n\tfor (portalnum = 1; portalnum < aasworld.numportals; portalnum++)\n\t{\n\t\tif (aasworld.portals[portalnum].areanum == areanum) break;\n\t} //end for\n\t//\n\tif (portalnum == aasworld.numportals)\n\t{\n\t\tAAS_Error(\"no portal of area %d\", areanum);\n\t\treturn qtrue;\n\t} //end if\n\t//\n\tportal = &aasworld.portals[portalnum];\n\t//if the portal is already fully updated\n\tif (portal->frontcluster == clusternum) return qtrue;\n\tif (portal->backcluster == clusternum) return qtrue;\n\t//if the portal has no front cluster yet\n\tif (!portal->frontcluster)\n\t{\n\t\tportal->frontcluster = clusternum;\n\t} //end if\n\t//if the portal has no back cluster yet\n\telse if (!portal->backcluster)\n\t{\n\t\tportal->backcluster = clusternum;\n\t} //end else if\n\telse\n\t{\n\t\t//remove the cluster portal flag contents\n\t\taasworld.areasettings[areanum].contents &= ~AREACONTENTS_CLUSTERPORTAL;\n\t\tLog_Write(\"portal area %d is seperating more than two clusters\\r\\n\", areanum);\n\t\treturn qfalse;\n\t} //end else\n\tif (aasworld.portalindexsize >= AAS_MAX_PORTALINDEXSIZE)\n\t{\n\t\tAAS_Error(\"AAS_MAX_PORTALINDEXSIZE\");\n\t\treturn qtrue;\n\t} //end if\n\t//set the area cluster number to the negative portal number\n\taasworld.areasettings[areanum].cluster = -portalnum;\n\t//add the portal to the cluster using the portal index\n\tcluster = &aasworld.clusters[clusternum];\n\taasworld.portalindex[cluster->firstportal + cluster->numportals] = portalnum;\n\taasworld.portalindexsize++;\n\tcluster->numportals++;\n\treturn qtrue;\n} //end of the function AAS_UpdatePortal\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint AAS_FloodClusterAreas_r(int areanum, int clusternum)\n{\n\taas_area_t *area;\n\taas_face_t *face;\n\tint facenum, i;\n\n\t//\n\tif (areanum <= 0 || areanum >= aasworld.numareas)\n\t{\n\t\tAAS_Error(\"AAS_FloodClusterAreas_r: areanum out of range\");\n\t\treturn qfalse;\n\t} //end if\n\t//if the area is already part of a cluster\n\tif (aasworld.areasettings[areanum].cluster > 0)\n\t{\n\t\tif (aasworld.areasettings[areanum].cluster == clusternum) return qtrue;\n\t\t//\n\t\t//there's a reachability going from one cluster to another only in one direction\n\t\t//\n\t\tAAS_Error(\"cluster %d touched cluster %d at area %d\\r\\n\",\n\t\t\t\tclusternum, aasworld.areasettings[areanum].cluster, areanum);\n\t\treturn qfalse;\n\t} //end if\n\t//don't add the cluster portal areas to the clusters\n\tif (aasworld.areasettings[areanum].contents & AREACONTENTS_CLUSTERPORTAL)\n\t{\n\t\treturn AAS_UpdatePortal(areanum, clusternum);\n\t} //end if\n\t//set the area cluster number\n\taasworld.areasettings[areanum].cluster = clusternum;\n\taasworld.areasettings[areanum].clusterareanum =\n\t\t\t\taasworld.clusters[clusternum].numareas;\n\t//the cluster has an extra area\n\taasworld.clusters[clusternum].numareas++;\n\n\tarea = &aasworld.areas[areanum];\n\t//use area faces to flood into adjacent areas\n\tif (!nofaceflood)\n\t{\n\t\tfor (i = 0; i < area->numfaces; i++)\n\t\t{\n\t\t\tfacenum = abs(aasworld.faceindex[area->firstface + i]);\n\t\t\tface = &aasworld.faces[facenum];\n\t\t\tif (face->frontarea == areanum)\n\t\t\t{\n\t\t\t\tif (face->backarea) if (!AAS_FloodClusterAreas_r(face->backarea, clusternum)) return qfalse;\n\t\t\t} //end if\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (face->frontarea) if (!AAS_FloodClusterAreas_r(face->frontarea, clusternum)) return qfalse;\n\t\t\t} //end else\n\t\t} //end for\n\t} //end if\n\t//use the reachabilities to flood into other areas\n\tfor (i = 0; i < aasworld.areasettings[areanum].numreachableareas; i++)\n\t{\n\t\tif (!aasworld.reachability[\n\t\t\t\t\taasworld.areasettings[areanum].firstreachablearea + i].areanum)\n\t\t{\n\t\t\tcontinue;\n\t\t} //end if\n\t\tif (!AAS_FloodClusterAreas_r(aasworld.reachability[\n\t\t\t\taasworld.areasettings[areanum].firstreachablearea + i].areanum, clusternum)) return qfalse;\n\t} //end for\n\treturn qtrue;\n} //end of the function AAS_FloodClusterAreas_r\n//===========================================================================\n// try to flood from all areas without cluster into areas with a cluster set\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint AAS_FloodClusterAreasUsingReachabilities(int clusternum)\n{\n\tint i, j, areanum;\n\n\tfor (i = 1; i < aasworld.numareas; i++)\n\t{\n\t\t//if this area already has a cluster set\n\t\tif (aasworld.areasettings[i].cluster)\n\t\t\tcontinue;\n\t\t//if this area is a cluster portal\n\t\tif (aasworld.areasettings[i].contents & AREACONTENTS_CLUSTERPORTAL)\n\t\t\tcontinue;\n\t\t//loop over the reachable areas from this area\n\t\tfor (j = 0; j < aasworld.areasettings[i].numreachableareas; j++)\n\t\t{\n\t\t\t//the reachable area\n\t\t\tareanum = aasworld.reachability[aasworld.areasettings[i].firstreachablearea + j].areanum;\n\t\t\t//if this area is a cluster portal\n\t\t\tif (aasworld.areasettings[areanum].contents & AREACONTENTS_CLUSTERPORTAL)\n\t\t\t\tcontinue;\n\t\t\t//if this area has a cluster set\n\t\t\tif (aasworld.areasettings[areanum].cluster)\n\t\t\t{\n\t\t\t\tif (!AAS_FloodClusterAreas_r(i, clusternum))\n\t\t\t\t\treturn qfalse;\n\t\t\t\ti = 0;\n\t\t\t\tbreak;\n\t\t\t} //end if\n\t\t} //end for\n\t} //end for\n\treturn qtrue;\n} //end of the function AAS_FloodClusterAreasUsingReachabilities\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid AAS_NumberClusterPortals(int clusternum)\n{\n\tint i, portalnum;\n\taas_cluster_t *cluster;\n\taas_portal_t *portal;\n\n\tcluster = &aasworld.clusters[clusternum];\n\tfor (i = 0; i < cluster->numportals; i++)\n\t{\n\t\tportalnum = aasworld.portalindex[cluster->firstportal + i];\n\t\tportal = &aasworld.portals[portalnum];\n\t\tif (portal->frontcluster == clusternum)\n\t\t{\n\t\t\tportal->clusterareanum[0] = cluster->numareas++;\n\t\t} //end if\n\t\telse\n\t\t{\n\t\t\tportal->clusterareanum[1] = cluster->numareas++;\n\t\t} //end else\n\t} //end for\n} //end of the function AAS_NumberClusterPortals\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid AAS_NumberClusterAreas(int clusternum)\n{\n\tint i, portalnum;\n\taas_cluster_t *cluster;\n\taas_portal_t *portal;\n\n\taasworld.clusters[clusternum].numareas = 0;\n\taasworld.clusters[clusternum].numreachabilityareas = 0;\n\t//number all areas in this cluster WITH reachabilities\n\tfor (i = 1; i < aasworld.numareas; i++)\n\t{\n\t\t//\n\t\tif (aasworld.areasettings[i].cluster != clusternum) continue;\n\t\t//\n\t\tif (!AAS_AreaReachability(i)) continue;\n\t\t//\n\t\taasworld.areasettings[i].clusterareanum = aasworld.clusters[clusternum].numareas;\n\t\t//the cluster has an extra area\n\t\taasworld.clusters[clusternum].numareas++;\n\t\taasworld.clusters[clusternum].numreachabilityareas++;\n\t} //end for\n\t//number all portals in this cluster WITH reachabilities\n\tcluster = &aasworld.clusters[clusternum];\n\tfor (i = 0; i < cluster->numportals; i++)\n\t{\n\t\tportalnum = aasworld.portalindex[cluster->firstportal + i];\n\t\tportal = &aasworld.portals[portalnum];\n\t\tif (!AAS_AreaReachability(portal->areanum)) continue;\n\t\tif (portal->frontcluster == clusternum)\n\t\t{\n\t\t\tportal->clusterareanum[0] = cluster->numareas++;\n\t\t\taasworld.clusters[clusternum].numreachabilityareas++;\n\t\t} //end if\n\t\telse\n\t\t{\n\t\t\tportal->clusterareanum[1] = cluster->numareas++;\n\t\t\taasworld.clusters[clusternum].numreachabilityareas++;\n\t\t} //end else\n\t} //end for\n\t//number all areas in this cluster WITHOUT reachabilities\n\tfor (i = 1; i < aasworld.numareas; i++)\n\t{\n\t\t//\n\t\tif (aasworld.areasettings[i].cluster != clusternum) continue;\n\t\t//\n\t\tif (AAS_AreaReachability(i)) continue;\n\t\t//\n\t\taasworld.areasettings[i].clusterareanum = aasworld.clusters[clusternum].numareas;\n\t\t//the cluster has an extra area\n\t\taasworld.clusters[clusternum].numareas++;\n\t} //end for\n\t//number all portals in this cluster WITHOUT reachabilities\n\tcluster = &aasworld.clusters[clusternum];\n\tfor (i = 0; i < cluster->numportals; i++)\n\t{\n\t\tportalnum = aasworld.portalindex[cluster->firstportal + i];\n\t\tportal = &aasworld.portals[portalnum];\n\t\tif (AAS_AreaReachability(portal->areanum)) continue;\n\t\tif (portal->frontcluster == clusternum)\n\t\t{\n\t\t\tportal->clusterareanum[0] = cluster->numareas++;\n\t\t} //end if\n\t\telse\n\t\t{\n\t\t\tportal->clusterareanum[1] = cluster->numareas++;\n\t\t} //end else\n\t} //end for\n} //end of the function AAS_NumberClusterAreas\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint AAS_FindClusters(void)\n{\n\tint i;\n\taas_cluster_t *cluster;\n\n\tAAS_RemoveClusterAreas();\n\t//\n\tfor (i = 1; i < aasworld.numareas; i++)\n\t{\n\t\t//if the area is already part of a cluster\n\t\tif (aasworld.areasettings[i].cluster)\n\t\t\tcontinue;\n\t\t// if not flooding through faces only use areas that have reachabilities\n\t\tif (nofaceflood)\n\t\t{\n\t\t\tif (!aasworld.areasettings[i].numreachableareas)\n\t\t\t\tcontinue;\n\t\t} //end if\n\t\t//if the area is a cluster portal\n\t\tif (aasworld.areasettings[i].contents & AREACONTENTS_CLUSTERPORTAL)\n\t\t\tcontinue;\n\t\tif (aasworld.numclusters >= AAS_MAX_CLUSTERS)\n\t\t{\n\t\t\tAAS_Error(\"AAS_MAX_CLUSTERS\");\n\t\t\treturn qfalse;\n\t\t} //end if\n\t\tcluster = &aasworld.clusters[aasworld.numclusters];\n\t\tcluster->numareas = 0;\n\t\tcluster->numreachabilityareas = 0;\n\t\tcluster->firstportal = aasworld.portalindexsize;\n\t\tcluster->numportals = 0;\n\t\t//flood the areas in this cluster\n\t\tif (!AAS_FloodClusterAreas_r(i, aasworld.numclusters))\n\t\t\treturn qfalse;\n\t\tif (!AAS_FloodClusterAreasUsingReachabilities(aasworld.numclusters))\n\t\t\treturn qfalse;\n\t\t//number the cluster areas\n\t\t//AAS_NumberClusterPortals(aasworld.numclusters);\n\t\tAAS_NumberClusterAreas(aasworld.numclusters);\n\t\t//Log_Write(\"cluster %d has %d areas\\r\\n\", aasworld.numclusters, cluster->numareas);\n\t\taasworld.numclusters++;\n\t} //end for\n\treturn qtrue;\n} //end of the function AAS_FindClusters\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid AAS_CreatePortals(void)\n{\n\tint i;\n\taas_portal_t *portal;\n\n\tfor (i = 1; i < aasworld.numareas; i++)\n\t{\n\t\t//if the area is a cluster portal\n\t\tif (aasworld.areasettings[i].contents & AREACONTENTS_CLUSTERPORTAL)\n\t\t{\n\t\t\tif (aasworld.numportals >= AAS_MAX_PORTALS)\n\t\t\t{\n\t\t\t\tAAS_Error(\"AAS_MAX_PORTALS\");\n\t\t\t\treturn;\n\t\t\t} //end if\n\t\t\tportal = &aasworld.portals[aasworld.numportals];\n\t\t\tportal->areanum = i;\n\t\t\tportal->frontcluster = 0;\n\t\t\tportal->backcluster = 0;\n\t\t\taasworld.numportals++;\n\t\t} //end if\n\t} //end for\n} //end of the function AAS_CreatePortals\n/*\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint AAS_MapContainsTeleporters(void)\n{\n\tbsp_entity_t *entities, *ent;\n\tchar *classname;\n\n\tentities = AAS_ParseBSPEntities();\n\n\tfor (ent = entities; ent; ent = ent->next)\n\t{\n\t\tclassname = AAS_ValueForBSPEpairKey(ent, \"classname\");\n\t\tif (classname && !strcmp(classname, \"misc_teleporter\"))\n\t\t{\n\t\t\tAAS_FreeBSPEntities(entities);\n\t\t\treturn qtrue;\n\t\t} //end if\n\t} //end for\n\treturn qfalse;\n} //end of the function AAS_MapContainsTeleporters\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint AAS_NonConvexFaces(aas_face_t *face1, aas_face_t *face2, int side1, int side2)\n{\n\tint i, j, edgenum;\n\taas_plane_t *plane1, *plane2;\n\taas_edge_t *edge;\n\t\n\n\tplane1 = &aasworld.planes[face1->planenum ^ side1];\n\tplane2 = &aasworld.planes[face2->planenum ^ side2];\n\n\t//check if one of the points of face1 is at the back of the plane of face2\n\tfor (i = 0; i < face1->numedges; i++)\n\t{\n\t\tedgenum = abs(aasworld.edgeindex[face1->firstedge + i]);\n\t\tedge = &aasworld.edges[edgenum];\n\t\tfor (j = 0; j < 2; j++)\n\t\t{\n\t\t\tif (DotProduct(plane2->normal, aasworld.vertexes[edge->v[j]]) -\n\t\t\t\t\t\t\tplane2->dist < -0.01) return qtrue;\n\t\t} //end for\n\t} //end for\n\tfor (i = 0; i < face2->numedges; i++)\n\t{\n\t\tedgenum = abs(aasworld.edgeindex[face2->firstedge + i]);\n\t\tedge = &aasworld.edges[edgenum];\n\t\tfor (j = 0; j < 2; j++)\n\t\t{\n\t\t\tif (DotProduct(plane1->normal, aasworld.vertexes[edge->v[j]]) -\n\t\t\t\t\t\t\tplane1->dist < -0.01) return qtrue;\n\t\t} //end for\n\t} //end for\n\n\treturn qfalse;\n} //end of the function AAS_NonConvexFaces\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nqboolean AAS_CanMergeAreas(int *areanums, int numareas)\n{\n\tint i, j, s, face1num, face2num, side1, side2, fn1, fn2;\n\taas_face_t *face1, *face2;\n\taas_area_t *area1, *area2;\n\n\tfor (i = 0; i < numareas; i++)\n\t{\n\t\tarea1 = &aasworld.areas[areanums[i]];\n\t\tfor (fn1 = 0; fn1 < area1->numfaces; fn1++)\n\t\t{\n\t\t\tface1num = abs(aasworld.faceindex[area1->firstface + fn1]);\n\t\t\tface1 = &aasworld.faces[face1num];\n\t\t\tside1 = face1->frontarea != areanums[i];\n\t\t\t//check if the face isn't a shared one with one of the other areas\n\t\t\tfor (s = 0; s < numareas; s++)\n\t\t\t{\n\t\t\t\tif (s == i) continue;\n\t\t\t\tif (face1->frontarea == s || face1->backarea == s) break;\n\t\t\t} //end for\n\t\t\t//if the face was a shared one\n\t\t\tif (s != numareas) continue;\n\t\t\t//\n\t\t\tfor (j = 0; j < numareas; j++)\n\t\t\t{\n\t\t\t\tif (j == i) continue;\n\t\t\t\tarea2 = &aasworld.areas[areanums[j]];\n\t\t\t\tfor (fn2 = 0; fn2 < area2->numfaces; fn2++)\n\t\t\t\t{\n\t\t\t\t\tface2num = abs(aasworld.faceindex[area2->firstface + fn2]);\n\t\t\t\t\tface2 = &aasworld.faces[face2num];\n\t\t\t\t\tside2 = face2->frontarea != areanums[j];\n\t\t\t\t\t//check if the face isn't a shared one with one of the other areas\n\t\t\t\t\tfor (s = 0; s < numareas; s++)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (s == j) continue;\n\t\t\t\t\t\tif (face2->frontarea == s || face2->backarea == s) break;\n\t\t\t\t\t} //end for\n\t\t\t\t\t//if the face was a shared one\n\t\t\t\t\tif (s != numareas) continue;\n\t\t\t\t\t//\n\t\t\t\t\tif (AAS_NonConvexFaces(face1, face2, side1, side2)) return qfalse;\n\t\t\t\t} //end for\n\t\t\t} //end for\n\t\t} //end for\n\t} //end for\n\treturn qtrue;\n} //end of the function AAS_CanMergeAreas\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nqboolean AAS_NonConvexEdges(aas_edge_t *edge1, aas_edge_t *edge2, int side1, int side2, int planenum)\n{\n\tint i;\n\tvec3_t edgevec1, edgevec2, normal1, normal2;\n\tfloat dist1, dist2;\n\taas_plane_t *plane;\n\n\tplane = &aasworld.planes[planenum];\n\tVectorSubtract(aasworld.vertexes[edge1->v[1]], aasworld.vertexes[edge1->v[0]], edgevec1);\n\tVectorSubtract(aasworld.vertexes[edge2->v[1]], aasworld.vertexes[edge2->v[0]], edgevec2);\n\tif (side1) VectorInverse(edgevec1);\n\tif (side2) VectorInverse(edgevec2);\n\t//\n\tCrossProduct(edgevec1, plane->normal, normal1);\n\tdist1 = DotProduct(normal1, aasworld.vertexes[edge1->v[0]]);\n\tCrossProduct(edgevec2, plane->normal, normal2);\n\tdist2 = DotProduct(normal2, aasworld.vertexes[edge2->v[0]]);\n\n\tfor (i = 0; i < 2; i++)\n\t{\n\t\tif (DotProduct(aasworld.vertexes[edge1->v[i]], normal2) - dist2 < -0.01) return qfalse;\n\t} //end for\n\tfor (i = 0; i < 2; i++)\n\t{\n\t\tif (DotProduct(aasworld.vertexes[edge2->v[i]], normal1) - dist1 < -0.01) return qfalse;\n\t} //end for\n\treturn qtrue;\n} //end of the function AAS_NonConvexEdges\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nqboolean AAS_CanMergeFaces(int *facenums, int numfaces, int planenum)\n{\n\tint i, j, s, edgenum1, edgenum2, side1, side2, en1, en2, ens;\n\taas_face_t *face1, *face2, *otherface;\n\taas_edge_t *edge1, *edge2;\n\n\tfor (i = 0; i < numfaces; i++)\n\t{\n\t\tface1 = &aasworld.faces[facenums[i]];\n\t\tfor (en1 = 0; en1 < face1->numedges; en1++)\n\t\t{\n\t\t\tedgenum1 = aasworld.edgeindex[face1->firstedge + en1];\n\t\t\tside1 = (edgenum1 < 0) ^ (face1->planenum != planenum);\n\t\t\tedgenum1 = abs(edgenum1);\n\t\t\tedge1 = &aasworld.edges[edgenum1];\n\t\t\t//check if the edge is shared with another face\n\t\t\tfor (s = 0; s < numfaces; s++)\n\t\t\t{\n\t\t\t\tif (s == i) continue;\n\t\t\t\totherface = &aasworld.faces[facenums[s]];\n\t\t\t\tfor (ens = 0; ens < otherface->numedges; ens++)\n\t\t\t\t{\n\t\t\t\t\tif (edgenum1 == abs(aasworld.edgeindex[otherface->firstedge + ens])) break;\n\t\t\t\t} //end for\n\t\t\t\tif (ens != otherface->numedges) break;\n\t\t\t} //end for\n\t\t\t//if the edge was shared\n\t\t\tif (s != numfaces) continue;\n\t\t\t//\n\t\t\tfor (j = 0; j < numfaces; j++)\n\t\t\t{\n\t\t\t\tif (j == i) continue;\n\t\t\t\tface2 = &aasworld.faces[facenums[j]];\n\t\t\t\tfor (en2 = 0; en2 < face2->numedges; en2++)\n\t\t\t\t{\n\t\t\t\t\tedgenum2 = aasworld.edgeindex[face2->firstedge + en2];\n\t\t\t\t\tside2 = (edgenum2 < 0) ^ (face2->planenum != planenum);\n\t\t\t\t\tedgenum2 = abs(edgenum2);\n\t\t\t\t\tedge2 = &aasworld.edges[edgenum2];\n\t\t\t\t\t//check if the edge is shared with another face\n\t\t\t\t\tfor (s = 0; s < numfaces; s++)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (s == i) continue;\n\t\t\t\t\t\totherface = &aasworld.faces[facenums[s]];\n\t\t\t\t\t\tfor (ens = 0; ens < otherface->numedges; ens++)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (edgenum2 == abs(aasworld.edgeindex[otherface->firstedge + ens])) break;\n\t\t\t\t\t\t} //end for\n\t\t\t\t\t\tif (ens != otherface->numedges) break;\n\t\t\t\t\t} //end for\n\t\t\t\t\t//if the edge was shared\n\t\t\t\t\tif (s != numfaces) continue;\n\t\t\t\t\t//\n\t\t\t\t\tif (AAS_NonConvexEdges(edge1, edge2, side1, side2, planenum)) return qfalse;\n\t\t\t\t} //end for\n\t\t\t} //end for\n\t\t} //end for\n\t} //end for\n\treturn qtrue;\n} //end of the function AAS_CanMergeFaces*/\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid AAS_ConnectedAreas_r(int *areanums, int numareas, int *connectedareas, int curarea)\n{\n\tint i, j, otherareanum, facenum;\n\taas_area_t *area;\n\taas_face_t *face;\n\n\tconnectedareas[curarea] = qtrue;\n\tarea = &aasworld.areas[areanums[curarea]];\n\tfor (i = 0; i < area->numfaces; i++)\n\t{\n\t\tfacenum = abs(aasworld.faceindex[area->firstface + i]);\n\t\tface = &aasworld.faces[facenum];\n\t\t//if the face is solid\n\t\tif (face->faceflags & FACE_SOLID) continue;\n\t\t//get the area at the other side of the face\n\t\tif (face->frontarea != areanums[curarea]) otherareanum = face->frontarea;\n\t\telse otherareanum = face->backarea;\n\t\t//check if the face is leading to one of the other areas\n\t\tfor (j = 0; j < numareas; j++)\n\t\t{\n\t\t\tif (areanums[j] == otherareanum) break;\n\t\t} //end for\n\t\t//if the face isn't leading to one of the other areas\n\t\tif (j == numareas) continue;\n\t\t//if the other area is already connected\n\t\tif (connectedareas[j]) continue;\n\t\t//recursively proceed with the other area\n\t\tAAS_ConnectedAreas_r(areanums, numareas, connectedareas, j);\n\t} //end for\n} //end of the function AAS_ConnectedAreas_r\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nqboolean AAS_ConnectedAreas(int *areanums, int numareas)\n{\n\tint connectedareas[MAX_PORTALAREAS], i;\n\n\tCom_Memset(connectedareas, 0, sizeof(connectedareas));\n\tif (numareas < 1) return qfalse;\n\tif (numareas == 1) return qtrue;\n\tAAS_ConnectedAreas_r(areanums, numareas, connectedareas, 0);\n\tfor (i = 0; i < numareas; i++)\n\t{\n\t\tif (!connectedareas[i]) return qfalse;\n\t} //end for\n\treturn qtrue;\n} //end of the function AAS_ConnectedAreas\n//===========================================================================\n// gets adjacent areas with less presence types recursively\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint AAS_GetAdjacentAreasWithLessPresenceTypes_r(int *areanums, int numareas, int curareanum)\n{\n\tint i, j, presencetype, otherpresencetype, otherareanum, facenum;\n\taas_area_t *area;\n\taas_face_t *face;\n\n\tareanums[numareas++] = curareanum;\n\tarea = &aasworld.areas[curareanum];\n\tpresencetype = aasworld.areasettings[curareanum].presencetype;\n\tfor (i = 0; i < area->numfaces; i++)\n\t{\n\t\tfacenum = abs(aasworld.faceindex[area->firstface + i]);\n\t\tface = &aasworld.faces[facenum];\n\t\t//if the face is solid\n\t\tif (face->faceflags & FACE_SOLID) continue;\n\t\t//the area at the other side of the face\n\t\tif (face->frontarea != curareanum) otherareanum = face->frontarea;\n\t\telse otherareanum = face->backarea;\n\t\t//\n\t\totherpresencetype = aasworld.areasettings[otherareanum].presencetype;\n\t\t//if the other area has less presence types\n\t\tif ((presencetype & ~otherpresencetype) &&\n\t\t\t\t!(otherpresencetype & ~presencetype))\n\t\t{\n\t\t\t//check if the other area isn't already in the list\n\t\t\tfor (j = 0; j < numareas; j++)\n\t\t\t{\n\t\t\t\tif (otherareanum == areanums[j]) break;\n\t\t\t} //end for\n\t\t\t//if the other area isn't already in the list\n\t\t\tif (j == numareas)\n\t\t\t{\n\t\t\t\tif (numareas >= MAX_PORTALAREAS)\n\t\t\t\t{\n\t\t\t\t\tAAS_Error(\"MAX_PORTALAREAS\");\n\t\t\t\t\treturn numareas;\n\t\t\t\t} //end if\n\t\t\t\tnumareas = AAS_GetAdjacentAreasWithLessPresenceTypes_r(areanums, numareas, otherareanum);\n\t\t\t} //end if\n\t\t} //end if\n\t} //end for\n\treturn numareas;\n} //end of the function AAS_GetAdjacentAreasWithLessPresenceTypes_r\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint AAS_CheckAreaForPossiblePortals(int areanum)\n{\n\tint i, j, k, fen, ben, frontedgenum, backedgenum, facenum;\n\tint areanums[MAX_PORTALAREAS], numareas, otherareanum;\n\tint numareafrontfaces[MAX_PORTALAREAS], numareabackfaces[MAX_PORTALAREAS];\n\tint frontfacenums[MAX_PORTALAREAS], backfacenums[MAX_PORTALAREAS];\n\tint numfrontfaces, numbackfaces;\n\tint frontareanums[MAX_PORTALAREAS], backareanums[MAX_PORTALAREAS];\n\tint numfrontareas, numbackareas;\n\tint frontplanenum, backplanenum, faceplanenum;\n\taas_area_t *area;\n\taas_face_t *frontface, *backface, *face;\n\n\t//if it isn't already a portal\n\tif (aasworld.areasettings[areanum].contents & AREACONTENTS_CLUSTERPORTAL) return 0;\n\t//it must be a grounded area\n\tif (!(aasworld.areasettings[areanum].areaflags & AREA_GROUNDED)) return 0;\n\t//\n\tCom_Memset(numareafrontfaces, 0, sizeof(numareafrontfaces));\n\tCom_Memset(numareabackfaces, 0, sizeof(numareabackfaces));\n\tnumareas = numfrontfaces = numbackfaces = 0;\n\tnumfrontareas = numbackareas = 0;\n\tfrontplanenum = backplanenum = -1;\n\t//add any adjacent areas with less presence types\n\tnumareas = AAS_GetAdjacentAreasWithLessPresenceTypes_r(areanums, 0, areanum);\n\t//\n\tfor (i = 0; i < numareas; i++)\n\t{\n\t\tarea = &aasworld.areas[areanums[i]];\n\t\tfor (j = 0; j < area->numfaces; j++)\n\t\t{\n\t\t\tfacenum = abs(aasworld.faceindex[area->firstface + j]);\n\t\t\tface = &aasworld.faces[facenum];\n\t\t\t//if the face is solid\n\t\t\tif (face->faceflags & FACE_SOLID) continue;\n\t\t\t//check if the face is shared with one of the other areas\n\t\t\tfor (k = 0; k < numareas; k++)\n\t\t\t{\n\t\t\t\tif (k == i) continue;\n\t\t\t\tif (face->frontarea == areanums[k] || face->backarea == areanums[k]) break;\n\t\t\t} //end for\n\t\t\t//if the face is shared\n\t\t\tif (k != numareas) continue;\n\t\t\t//the number of the area at the other side of the face\n\t\t\tif (face->frontarea == areanums[i]) otherareanum = face->backarea;\n\t\t\telse otherareanum = face->frontarea;\n\t\t\t//if the other area already is a cluter portal\n\t\t\tif (aasworld.areasettings[otherareanum].contents & AREACONTENTS_CLUSTERPORTAL) return 0;\n\t\t\t//number of the plane of the area\n\t\t\tfaceplanenum = face->planenum & ~1;\n\t\t\t//\n\t\t\tif (frontplanenum < 0 || faceplanenum == frontplanenum)\n\t\t\t{\n\t\t\t\tfrontplanenum = faceplanenum;\n\t\t\t\tfrontfacenums[numfrontfaces++] = facenum;\n\t\t\t\tfor (k = 0; k < numfrontareas; k++)\n\t\t\t\t{\n\t\t\t\t\tif (frontareanums[k] == otherareanum) break;\n\t\t\t\t} //end for\n\t\t\t\tif (k == numfrontareas) frontareanums[numfrontareas++] = otherareanum;\n\t\t\t\tnumareafrontfaces[i]++;\n\t\t\t} //end if\n\t\t\telse if (backplanenum < 0 || faceplanenum == backplanenum)\n\t\t\t{\n\t\t\t\tbackplanenum = faceplanenum;\n\t\t\t\tbackfacenums[numbackfaces++] = facenum;\n\t\t\t\tfor (k = 0; k < numbackareas; k++)\n\t\t\t\t{\n\t\t\t\t\tif (backareanums[k] == otherareanum) break;\n\t\t\t\t} //end for\n\t\t\t\tif (k == numbackareas) backareanums[numbackareas++] = otherareanum;\n\t\t\t\tnumareabackfaces[i]++;\n\t\t\t} //end else\n\t\t\telse\n\t\t\t{\n\t\t\t\treturn 0;\n\t\t\t} //end else\n\t\t} //end for\n\t} //end for\n\t//every area should have at least one front face and one back face\n\tfor (i = 0; i < numareas; i++)\n\t{\n\t\tif (!numareafrontfaces[i] || !numareabackfaces[i]) return 0;\n\t} //end for\n\t//the front areas should all be connected\n\tif (!AAS_ConnectedAreas(frontareanums, numfrontareas)) return 0;\n\t//the back areas should all be connected\n\tif (!AAS_ConnectedAreas(backareanums, numbackareas)) return 0;\n\t//none of the front faces should have a shared edge with a back face\n\tfor (i = 0; i < numfrontfaces; i++)\n\t{\n\t\tfrontface = &aasworld.faces[frontfacenums[i]];\n\t\tfor (fen = 0; fen < frontface->numedges; fen++)\n\t\t{\n\t\t\tfrontedgenum = abs(aasworld.edgeindex[frontface->firstedge + fen]);\n\t\t\tfor (j = 0; j < numbackfaces; j++)\n\t\t\t{\n\t\t\t\tbackface = &aasworld.faces[backfacenums[j]];\n\t\t\t\tfor (ben = 0; ben < backface->numedges; ben++)\n\t\t\t\t{\n\t\t\t\t\tbackedgenum = abs(aasworld.edgeindex[backface->firstedge + ben]);\n\t\t\t\t\tif (frontedgenum == backedgenum) break;\n\t\t\t\t} //end for\n\t\t\t\tif (ben != backface->numedges) break;\n\t\t\t} //end for\n\t\t\tif (j != numbackfaces) break;\n\t\t} //end for\n\t\tif (fen != frontface->numedges) break;\n\t} //end for\n\tif (i != numfrontfaces) return 0;\n\t//set the cluster portal contents\n\tfor (i = 0; i < numareas; i++)\n\t{\n\t\taasworld.areasettings[areanums[i]].contents |= AREACONTENTS_CLUSTERPORTAL;\n\t\t//this area can be used as a route portal\n\t\taasworld.areasettings[areanums[i]].contents |= AREACONTENTS_ROUTEPORTAL;\n\t\tLog_Write(\"possible portal: %d\\r\\n\", areanums[i]);\n\t} //end for\n\t//\n\treturn numareas;\n} //end of the function AAS_CheckAreaForPossiblePortals\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid AAS_FindPossiblePortals(void)\n{\n\tint i, numpossibleportals;\n\n\tnumpossibleportals = 0;\n\tfor (i = 1; i < aasworld.numareas; i++)\n\t{\n\t\tnumpossibleportals += AAS_CheckAreaForPossiblePortals(i);\n\t} //end for\n\tbotimport.Print(PRT_MESSAGE, \"\\r%6d possible portal areas\\n\", numpossibleportals);\n} //end of the function AAS_FindPossiblePortals\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid AAS_RemoveAllPortals(void)\n{\n\tint i;\n\n\tfor (i = 1; i < aasworld.numareas; i++)\n\t{\n\t\taasworld.areasettings[i].contents &= ~AREACONTENTS_CLUSTERPORTAL;\n\t} //end for\n} //end of the function AAS_RemoveAllPortals\n\n#if 0\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid AAS_FloodCluster_r(int areanum, int clusternum)\n{\n\tint i, otherareanum;\n\taas_face_t *face;\n\taas_area_t *area;\n\n\t//set cluster mark\n\taasworld.areasettings[areanum].cluster = clusternum;\n\t//if the area is a portal\n\t//if (aasworld.areasettings[areanum].contents & AREACONTENTS_CLUSTERPORTAL) return;\n\t//\n\tarea = &aasworld.areas[areanum];\n\t//use area faces to flood into adjacent areas\n\tfor (i = 0; i < area->numfaces; i++)\n\t{\n\t\tface = &aasworld.faces[abs(aasworld.faceindex[area->firstface + i])];\n\t\t//\n\t\tif (face->frontarea != areanum) otherareanum = face->frontarea;\n\t\telse otherareanum = face->backarea;\n\t\t//if there's no area at the other side\n\t\tif (!otherareanum) continue;\n\t\t//if the area is a portal\n\t\tif (aasworld.areasettings[otherareanum].contents & AREACONTENTS_CLUSTERPORTAL) continue;\n\t\t//if the area is already marked\n\t\tif (aasworld.areasettings[otherareanum].cluster) continue;\n\t\t//\n\t\tAAS_FloodCluster_r(otherareanum, clusternum);\n\t} //end for\n\t//use the reachabilities to flood into other areas\n\tfor (i = 0; i < aasworld.areasettings[areanum].numreachableareas; i++)\n\t{\n\t\totherareanum = aasworld.reachability[\n\t\t\t\t\taasworld.areasettings[areanum].firstreachablearea + i].areanum;\n\t\tif (!otherareanum)\n\t\t{\n\t\t\tcontinue;\n\t\t\tAAS_Error(\"reachability %d has zero area\\n\", aasworld.areasettings[areanum].firstreachablearea + i);\n\t\t} //end if\n\t\t//if the area is a portal\n\t\tif (aasworld.areasettings[otherareanum].contents & AREACONTENTS_CLUSTERPORTAL) continue;\n\t\t//if the area is already marked\n\t\tif (aasworld.areasettings[otherareanum].cluster) continue;\n\t\t//\n\t\tAAS_FloodCluster_r(otherareanum, clusternum);\n\t} //end for\n} //end of the function AAS_FloodCluster_r\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid AAS_RemoveTeleporterPortals(void)\n{\n\tint i, j, areanum;\n\n\tfor (i = 1; i < aasworld.numareas; i++)\n\t{\n\t\tfor (j = 0; j < aasworld.areasettings[i].numreachableareas; j++)\n\t\t{\n\t\t\tareanum = aasworld.reachability[aasworld.areasettings[i].firstreachablearea + j].areanum;\n\t\t\tif (aasworld.reachability[aasworld.areasettings[i].firstreachablearea + j].traveltype == TRAVEL_TELEPORT)\n\t\t\t{\n\t\t\t\taasworld.areasettings[i].contents &= ~AREACONTENTS_CLUSTERPORTAL;\n\t\t\t\taasworld.areasettings[areanum].contents &= ~AREACONTENTS_CLUSTERPORTAL;\n\t\t\t\tbreak;\n\t\t\t} //end if\n\t\t} //end for\n\t} //end for\n} //end of the function AAS_RemoveTeleporterPortals\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid AAS_FloodClusterReachabilities(int clusternum)\n{\n\tint i, j, areanum;\n\n\tfor (i = 1; i < aasworld.numareas; i++)\n\t{\n\t\t//if this area already has a cluster set\n\t\tif (aasworld.areasettings[i].cluster) continue;\n\t\t//if this area is a cluster portal\n\t\tif (aasworld.areasettings[i].contents & AREACONTENTS_CLUSTERPORTAL) continue;\n\t\t//loop over the reachable areas from this area\n\t\tfor (j = 0; j < aasworld.areasettings[i].numreachableareas; j++)\n\t\t{\n\t\t\t//the reachable area\n\t\t\tareanum = aasworld.reachability[aasworld.areasettings[i].firstreachablearea + j].areanum;\n\t\t\t//if this area is a cluster portal\n\t\t\tif (aasworld.areasettings[areanum].contents & AREACONTENTS_CLUSTERPORTAL) continue;\n\t\t\t//if this area has a cluster set\n\t\t\tif (aasworld.areasettings[areanum].cluster == clusternum)\n\t\t\t{\n\t\t\t\tAAS_FloodCluster_r(i, clusternum);\n\t\t\t\ti = 0;\n\t\t\t\tbreak;\n\t\t\t} //end if\n\t\t} //end for\n\t} //end for\n} //end of the function AAS_FloodClusterReachabilities\n\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid AAS_RemoveNotClusterClosingPortals(void)\n{\n\tint i, j, k, facenum, otherareanum, nonclosingportals;\n\taas_area_t *area;\n\taas_face_t *face;\n\n\tAAS_RemoveTeleporterPortals();\n\t//\n\tnonclosingportals = 0;\n\tfor (i = 1; i < aasworld.numareas; i++)\n\t{\n\t\tif (!(aasworld.areasettings[i].contents & AREACONTENTS_CLUSTERPORTAL)) continue;\n\t\t//find a non-portal area adjacent to the portal area and flood\n\t\t//the cluster from there\n\t\tarea = &aasworld.areas[i];\n\t\tfor (j = 0; j < area->numfaces; j++)\n\t\t{\n\t\t\tfacenum = abs(aasworld.faceindex[area->firstface + j]);\n\t\t\tface = &aasworld.faces[facenum];\n\t\t\t//\n\t\t\tif (face->frontarea != i) otherareanum = face->frontarea;\n\t\t\telse otherareanum = face->backarea;\n\t\t\t//\n\t\t\tif (!otherareanum) continue;\n\t\t\t//\n\t\t\tif (aasworld.areasettings[otherareanum].contents & AREACONTENTS_CLUSTERPORTAL)\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t} //end if\n\t\t\t//reset all cluster fields\n\t\t\tAAS_RemoveClusterAreas();\n\t\t\t//\n\t\t\tAAS_FloodCluster_r(otherareanum, 1);\n\t\t\tAAS_FloodClusterReachabilities(1);\n\t\t\t//check if all adjacent non-portal areas have a cluster set\n\t\t\tfor (k = 0; k < area->numfaces; k++)\n\t\t\t{\n\t\t\t\tfacenum = abs(aasworld.faceindex[area->firstface + k]);\n\t\t\t\tface = &aasworld.faces[facenum];\n\t\t\t\t//\n\t\t\t\tif (face->frontarea != i) otherareanum = face->frontarea;\n\t\t\t\telse otherareanum = face->backarea;\n\t\t\t\t//\n\t\t\t\tif (!otherareanum) continue;\n\t\t\t\t//\n\t\t\t\tif (aasworld.areasettings[otherareanum].contents & AREACONTENTS_CLUSTERPORTAL)\n\t\t\t\t{\n\t\t\t\t\tcontinue;\n\t\t\t\t} //end if\n\t\t\t\t//\n\t\t\t\tif (!aasworld.areasettings[otherareanum].cluster) break;\n\t\t\t} //end for\n\t\t\t//if all adjacent non-portal areas have a cluster set then the portal\n\t\t\t//didn't seal a cluster\n\t\t\tif (k >= area->numfaces)\n\t\t\t{\n\t\t\t\taasworld.areasettings[i].contents &= ~AREACONTENTS_CLUSTERPORTAL;\n\t\t\t\tnonclosingportals++;\n\t\t\t\t//recheck all the other portals again\n\t\t\t\ti = 0;\n\t\t\t\tbreak;\n\t\t\t} //end if\n\t\t} //end for\n\t} //end for\n\tbotimport.Print(PRT_MESSAGE, \"\\r%6d non closing portals removed\\n\", nonclosingportals);\n} //end of the function AAS_RemoveNotClusterClosingPortals\n\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\n\nvoid AAS_RemoveNotClusterClosingPortals(void)\n{\n\tint i, j, facenum, otherareanum, nonclosingportals, numseperatedclusters;\n\taas_area_t *area;\n\taas_face_t *face;\n\n\tAAS_RemoveTeleporterPortals();\n\t//\n\tnonclosingportals = 0;\n\tfor (i = 1; i < aasworld.numareas; i++)\n\t{\n\t\tif (!(aasworld.areasettings[i].contents & AREACONTENTS_CLUSTERPORTAL)) continue;\n\t\t//\n\t\tnumseperatedclusters = 0;\n\t\t//reset all cluster fields\n\t\tAAS_RemoveClusterAreas();\n\t\t//find a non-portal area adjacent to the portal area and flood\n\t\t//the cluster from there\n\t\tarea = &aasworld.areas[i];\n\t\tfor (j = 0; j < area->numfaces; j++)\n\t\t{\n\t\t\tfacenum = abs(aasworld.faceindex[area->firstface + j]);\n\t\t\tface = &aasworld.faces[facenum];\n\t\t\t//\n\t\t\tif (face->frontarea != i) otherareanum = face->frontarea;\n\t\t\telse otherareanum = face->backarea;\n\t\t\t//if not solid at the other side of the face\n\t\t\tif (!otherareanum) continue;\n\t\t\t//don't flood into other portals\n\t\t\tif (aasworld.areasettings[otherareanum].contents & AREACONTENTS_CLUSTERPORTAL) continue;\n\t\t\t//if the area already has a cluster set\n\t\t\tif (aasworld.areasettings[otherareanum].cluster) continue;\n\t\t\t//another cluster is seperated by this portal\n\t\t\tnumseperatedclusters++;\n\t\t\t//flood the cluster\n\t\t\tAAS_FloodCluster_r(otherareanum, numseperatedclusters);\n\t\t\tAAS_FloodClusterReachabilities(numseperatedclusters);\n\t\t} //end for\n\t\t//use the reachabilities to flood into other areas\n\t\tfor (j = 0; j < aasworld.areasettings[i].numreachableareas; j++)\n\t\t{\n\t\t\totherareanum = aasworld.reachability[\n\t\t\t\t\t\taasworld.areasettings[i].firstreachablearea + j].areanum;\n\t\t\t//this should never be qtrue but we check anyway\n\t\t\tif (!otherareanum) continue;\n\t\t\t//don't flood into other portals\n\t\t\tif (aasworld.areasettings[otherareanum].contents & AREACONTENTS_CLUSTERPORTAL) continue;\n\t\t\t//if the area already has a cluster set\n\t\t\tif (aasworld.areasettings[otherareanum].cluster) continue;\n\t\t\t//another cluster is seperated by this portal\n\t\t\tnumseperatedclusters++;\n\t\t\t//flood the cluster\n\t\t\tAAS_FloodCluster_r(otherareanum, numseperatedclusters);\n\t\t\tAAS_FloodClusterReachabilities(numseperatedclusters);\n\t\t} //end for\n\t\t//a portal must seperate no more and no less than 2 clusters\n\t\tif (numseperatedclusters != 2)\n\t\t{\n\t\t\taasworld.areasettings[i].contents &= ~AREACONTENTS_CLUSTERPORTAL;\n\t\t\tnonclosingportals++;\n\t\t\t//recheck all the other portals again\n\t\t\ti = 0;\n\t\t} //end if\n\t} //end for\n\tbotimport.Print(PRT_MESSAGE, \"\\r%6d non closing portals removed\\n\", nonclosingportals);\n} //end of the function AAS_RemoveNotClusterClosingPortals\n\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\n\nvoid AAS_AddTeleporterPortals(void)\n{\n\tint j, area2num, facenum, otherareanum;\n\tchar *target, *targetname, *classname;\n\tbsp_entity_t *entities, *ent, *dest;\n\tvec3_t origin, destorigin, mins, maxs, end;\n\tvec3_t bbmins, bbmaxs;\n\taas_area_t *area;\n\taas_face_t *face;\n\taas_trace_t trace;\n\taas_link_t *areas, *link;\n\n\tentities = AAS_ParseBSPEntities();\n\n\tfor (ent = entities; ent; ent = ent->next)\n\t{\n\t\tclassname = AAS_ValueForBSPEpairKey(ent, \"classname\");\n\t\tif (classname && !strcmp(classname, \"misc_teleporter\"))\n\t\t{\n\t\t\tif (!AAS_VectorForBSPEpairKey(ent, \"origin\", origin))\n\t\t\t{\n\t\t\t\tbotimport.Print(PRT_ERROR, \"teleporter (%s) without origin\\n\", target);\n\t\t\t\tcontinue;\n\t\t\t} //end if\n\t\t\t//\n\t\t\ttarget = AAS_ValueForBSPEpairKey(ent, \"target\");\n\t\t\tif (!target)\n\t\t\t{\n\t\t\t\tbotimport.Print(PRT_ERROR, \"teleporter (%s) without target\\n\", target);\n\t\t\t\tcontinue;\n\t\t\t} //end if\n\t\t\tfor (dest = entities; dest; dest = dest->next)\n\t\t\t{\n\t\t\t\tclassname = AAS_ValueForBSPEpairKey(dest, \"classname\");\n\t\t\t\tif (classname && !strcmp(classname, \"misc_teleporter_dest\"))\n\t\t\t\t{\n\t\t\t\t\ttargetname = AAS_ValueForBSPEpairKey(dest, \"targetname\");\n\t\t\t\t\tif (targetname && !strcmp(targetname, target))\n\t\t\t\t\t{\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t} //end if\n\t\t\t\t} //end if\n\t\t\t} //end for\n\t\t\tif (!dest)\n\t\t\t{\n\t\t\t\tbotimport.Print(PRT_ERROR, \"teleporter without destination (%s)\\n\", target);\n\t\t\t\tcontinue;\n\t\t\t} //end if\n\t\t\tif (!AAS_VectorForBSPEpairKey(dest, \"origin\", destorigin))\n\t\t\t{\n\t\t\t\tbotimport.Print(PRT_ERROR, \"teleporter destination (%s) without origin\\n\", target);\n\t\t\t\tcontinue;\n\t\t\t} //end if\n\t\t\tdestorigin[2] += 24; //just for q2e1m2, the dork has put the telepads in the ground\n\t\t\tVectorCopy(destorigin, end);\n\t\t\tend[2] -= 100;\n\t\t\ttrace = AAS_TraceClientBBox(destorigin, end, PRESENCE_CROUCH, -1);\n\t\t\tif (trace.startsolid)\n\t\t\t{\n\t\t\t\tbotimport.Print(PRT_ERROR, \"teleporter destination (%s) in solid\\n\", target);\n\t\t\t\tcontinue;\n\t\t\t} //end if\n\t\t\tVectorCopy(trace.endpos, destorigin);\n\t\t\tarea2num = AAS_PointAreaNum(destorigin);\n\t\t\t//reset all cluster fields\n\t\t\tfor (j = 0; j < aasworld.numareas; j++)\n\t\t\t{\n\t\t\t\taasworld.areasettings[j].cluster = 0;\n\t\t\t} //end for\n\t\t\t//\n\t\t\tVectorSet(mins, -8, -8, 8);\n\t\t\tVectorSet(maxs, 8, 8, 24);\n\t\t\t//\n\t\t\tAAS_PresenceTypeBoundingBox(PRESENCE_CROUCH, bbmins, bbmaxs);\n\t\t\t//\n\t\t\tVectorAdd(origin, mins, mins);\n\t\t\tVectorAdd(origin, maxs, maxs);\n\t\t\t//add bounding box size\n\t\t\tVectorSubtract(mins, bbmaxs, mins);\n\t\t\tVectorSubtract(maxs, bbmins, maxs);\n\t\t\t//link an invalid (-1) entity\n\t\t\tareas = AAS_AASLinkEntity(mins, maxs, -1);\n\t\t\t//\n\t\t\tfor (link = areas; link; link = link->next_area)\n\t\t\t{\n\t\t\t\tif (!AAS_AreaGrounded(link->areanum)) continue;\n\t\t\t\t//add the teleporter portal mark\n\t\t\t\taasworld.areasettings[link->areanum].contents |= AREACONTENTS_CLUSTERPORTAL |\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tAREACONTENTS_TELEPORTAL;\n\t\t\t} //end for\n\t\t\t//\n\t\t\tfor (link = areas; link; link = link->next_area)\n\t\t\t{\n\t\t\t\tif (!AAS_AreaGrounded(link->areanum)) continue;\n\t\t\t\t//find a non-portal area adjacent to the portal area and flood\n\t\t\t\t//the cluster from there\n\t\t\t\tarea = &aasworld.areas[link->areanum];\n\t\t\t\tfor (j = 0; j < area->numfaces; j++)\n\t\t\t\t{\n\t\t\t\t\tfacenum = abs(aasworld.faceindex[area->firstface + j]);\n\t\t\t\t\tface = &aasworld.faces[facenum];\n\t\t\t\t\t//\n\t\t\t\t\tif (face->frontarea != link->areanum) otherareanum = face->frontarea;\n\t\t\t\t\telse otherareanum = face->backarea;\n\t\t\t\t\t//\n\t\t\t\t\tif (!otherareanum) continue;\n\t\t\t\t\t//\n\t\t\t\t\tif (aasworld.areasettings[otherareanum].contents & AREACONTENTS_CLUSTERPORTAL)\n\t\t\t\t\t{\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t} //end if\n\t\t\t\t\t//\n\t\t\t\t\tAAS_FloodCluster_r(otherareanum, 1);\n\t\t\t\t} //end for\n\t\t\t} //end for\n\t\t\t//if the teleport destination IS in the same cluster\n\t\t\tif (aasworld.areasettings[area2num].cluster)\n\t\t\t{\n\t\t\t\tfor (link = areas; link; link = link->next_area)\n\t\t\t\t{\n\t\t\t\t\tif (!AAS_AreaGrounded(link->areanum)) continue;\n\t\t\t\t\t//add the teleporter portal mark\n\t\t\t\t\taasworld.areasettings[link->areanum].contents &= ~(AREACONTENTS_CLUSTERPORTAL |\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tAREACONTENTS_TELEPORTAL);\n\t\t\t\t} //end for\n\t\t\t} //end if\n\t\t} //end if\n\t} //end for\n\tAAS_FreeBSPEntities(entities);\n} //end of the function AAS_AddTeleporterPortals\n\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid AAS_AddTeleporterPortals(void)\n{\n\tint i, j, areanum;\n\n\tfor (i = 1; i < aasworld.numareas; i++)\n\t{\n\t\tfor (j = 0; j < aasworld.areasettings[i].numreachableareas; j++)\n\t\t{\n\t\t\tif (aasworld.reachability[aasworld.areasettings[i].firstreachablearea + j].traveltype != TRAVEL_TELEPORT) continue;\n\t\t\tareanum = aasworld.reachability[aasworld.areasettings[i].firstreachablearea + j].areanum;\n\t\t\taasworld.areasettings[areanum].contents |= AREACONTENTS_CLUSTERPORTAL;\n\t\t} //end for\n\t} //end for\n} //end of the function AAS_AddTeleporterPortals\n\n#endif\n\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint AAS_TestPortals(void)\n{\n\tint i;\n\taas_portal_t *portal;\n\n\tfor (i = 1; i < aasworld.numportals; i++)\n\t{\n\t\tportal = &aasworld.portals[i];\n\t\tif (!portal->frontcluster)\n\t\t{\n\t\t\taasworld.areasettings[portal->areanum].contents &= ~AREACONTENTS_CLUSTERPORTAL;\n\t\t\tLog_Write(\"portal area %d has no front cluster\\r\\n\", portal->areanum);\n\t\t\treturn qfalse;\n\t\t} //end if\n\t\tif (!portal->backcluster)\n\t\t{\n\t\t\taasworld.areasettings[portal->areanum].contents &= ~AREACONTENTS_CLUSTERPORTAL;\n\t\t\tLog_Write(\"portal area %d has no back cluster\\r\\n\", portal->areanum);\n\t\t\treturn qfalse;\n\t\t} //end if\n\t} //end for\n\treturn qtrue;\n} //end of the function\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid AAS_CountForcedClusterPortals(void)\n{\n\tint num, i;\n\n\tnum = 0;\n\tfor (i = 1; i < aasworld.numareas; i++)\n\t{\n\t\tif (aasworld.areasettings[i].contents & AREACONTENTS_CLUSTERPORTAL)\n\t\t{\n\t\t\tLog_Write(\"area %d is a forced portal area\\r\\n\", i);\n\t\t\tnum++;\n\t\t} //end if\n\t} //end for\n\tbotimport.Print(PRT_MESSAGE, \"%6d forced portal areas\\n\", num);\n} //end of the function AAS_CountForcedClusterPortals\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid AAS_CreateViewPortals(void)\n{\n\tint i;\n\n\tfor (i = 1; i < aasworld.numareas; i++)\n\t{\n\t\tif (aasworld.areasettings[i].contents & AREACONTENTS_CLUSTERPORTAL)\n\t\t{\n\t\t\taasworld.areasettings[i].contents |= AREACONTENTS_VIEWPORTAL;\n\t\t} //end if\n\t} //end for\n} //end of the function AAS_CreateViewPortals\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid AAS_SetViewPortalsAsClusterPortals(void)\n{\n\tint i;\n\n\tfor (i = 1; i < aasworld.numareas; i++)\n\t{\n\t\tif (aasworld.areasettings[i].contents & AREACONTENTS_VIEWPORTAL)\n\t\t{\n\t\t\taasworld.areasettings[i].contents |= AREACONTENTS_CLUSTERPORTAL;\n\t\t} //end if\n\t} //end for\n} //end of the function AAS_SetViewPortalsAsClusterPortals\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid AAS_InitClustering(void)\n{\n\tint i, removedPortalAreas;\n\tint n, total, numreachabilityareas;\n\n\tif (!aasworld.loaded) return;\n\t//if there are clusters\n\tif (aasworld.numclusters >= 1)\n\t{\n#ifndef BSPC\n\t\t//if clustering isn't forced\n\t\tif (!((int)LibVarGetValue(\"forceclustering\")) &&\n\t\t\t!((int)LibVarGetValue(\"forcereachability\"))) return;\n#endif\n\t} //end if\n\t//set all view portals as cluster portals in case we re-calculate the reachabilities and clusters (with -reach)\n\tAAS_SetViewPortalsAsClusterPortals();\n\t//count the number of forced cluster portals\n\tAAS_CountForcedClusterPortals();\n\t//remove all area cluster marks\n\tAAS_RemoveClusterAreas();\n\t//find possible cluster portals\n\tAAS_FindPossiblePortals();\n\t//craete portals to for the bot view\n\tAAS_CreateViewPortals();\n\t//remove all portals that are not closing a cluster\n\t//AAS_RemoveNotClusterClosingPortals();\n\t//initialize portal memory\n\tif (aasworld.portals) FreeMemory(aasworld.portals);\n\taasworld.portals = (aas_portal_t *) GetClearedMemory(AAS_MAX_PORTALS * sizeof(aas_portal_t));\n\t//initialize portal index memory\n\tif (aasworld.portalindex) FreeMemory(aasworld.portalindex);\n\taasworld.portalindex = (aas_portalindex_t *) GetClearedMemory(AAS_MAX_PORTALINDEXSIZE * sizeof(aas_portalindex_t));\n\t//initialize cluster memory\n\tif (aasworld.clusters) FreeMemory(aasworld.clusters);\n\taasworld.clusters = (aas_cluster_t *) GetClearedMemory(AAS_MAX_CLUSTERS * sizeof(aas_cluster_t));\n\t//\n\tremovedPortalAreas = 0;\n\tbotimport.Print(PRT_MESSAGE, \"\\r%6d removed portal areas\", removedPortalAreas);\n\twhile(1)\n\t{\n\t\tbotimport.Print(PRT_MESSAGE, \"\\r%6d\", removedPortalAreas);\n\t\t//initialize the number of portals and clusters\n\t\taasworld.numportals = 1;\t\t//portal 0 is a dummy\n\t\taasworld.portalindexsize = 0;\n\t\taasworld.numclusters = 1;\t\t//cluster 0 is a dummy\n\t\t//create the portals from the portal areas\n\t\tAAS_CreatePortals();\n\t\t//\n\t\tremovedPortalAreas++;\n\t\t//find the clusters\n\t\tif (!AAS_FindClusters())\n\t\t\tcontinue;\n\t\t//test the portals\n\t\tif (!AAS_TestPortals())\n\t\t\tcontinue;\n\t\t//\n\t\tbreak;\n\t} //end while\n\tbotimport.Print(PRT_MESSAGE, \"\\n\");\n\t//the AAS file should be saved\n\taasworld.savefile = qtrue;\n\t//write the portal areas to the log file\n\tfor (i = 1; i < aasworld.numportals; i++)\n\t{\n\t\tLog_Write(\"portal %d: area %d\\r\\n\", i, aasworld.portals[i].areanum);\n\t} //end for\n\t// report cluster info\n\tbotimport.Print(PRT_MESSAGE, \"%6d portals created\\n\", aasworld.numportals);\n\tbotimport.Print(PRT_MESSAGE, \"%6d clusters created\\n\", aasworld.numclusters);\n\tfor (i = 1; i < aasworld.numclusters; i++)\n\t{\n\t\tbotimport.Print(PRT_MESSAGE, \"cluster %d has %d reachability areas\\n\", i,\n\t\t\t\taasworld.clusters[i].numreachabilityareas);\n\t} //end for\n\t// report AAS file efficiency\n\tnumreachabilityareas = 0;\n\ttotal = 0;\n\tfor (i = 0; i < aasworld.numclusters; i++) {\n\t\tn = aasworld.clusters[i].numreachabilityareas;\n\t\tnumreachabilityareas += n;\n\t\ttotal += n * n;\n\t}\n\ttotal += numreachabilityareas * aasworld.numportals;\n\t//\n\tbotimport.Print(PRT_MESSAGE, \"%6i total reachability areas\\n\", numreachabilityareas);\n\tbotimport.Print(PRT_MESSAGE, \"%6i AAS memory/CPU usage (the lower the better)\\n\", total * 3);\n} //end of the function AAS_InitClustering\n"
  },
  {
    "path": "plugins/quake3/botlib/be_aas_cluster.h",
    "content": "/*\n===========================================================================\nCopyright (C) 1999-2005 Id Software, Inc.\n\nThis file is part of Quake III Arena source code.\n\nQuake III Arena source code is free software; you can redistribute it\nand/or modify it under the terms of the GNU General Public License as\npublished by the Free Software Foundation; either version 2 of the License,\nor (at your option) any later version.\n\nQuake III Arena source code is distributed in the hope that it will be\nuseful, but WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Quake III Arena source code; if not, write to the Free Software\nFoundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n===========================================================================\n*/\n\n/*****************************************************************************\n * name:\t\tbe_aas_cluster.h\n *\n * desc:\t\tAAS\n *\n * $Archive: /source/code/botlib/be_aas_cluster.h $\n *\n *****************************************************************************/\n\n#ifdef AASINTERN\n//initialize the AAS clustering\nvoid AAS_InitClustering(void);\n//\nvoid AAS_SetViewPortalsAsClusterPortals(void);\n#endif //AASINTERN\n\n"
  },
  {
    "path": "plugins/quake3/botlib/be_aas_debug.c",
    "content": "/*\n===========================================================================\nCopyright (C) 1999-2005 Id Software, Inc.\n\nThis file is part of Quake III Arena source code.\n\nQuake III Arena source code is free software; you can redistribute it\nand/or modify it under the terms of the GNU General Public License as\npublished by the Free Software Foundation; either version 2 of the License,\nor (at your option) any later version.\n\nQuake III Arena source code is distributed in the hope that it will be\nuseful, but WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Quake III Arena source code; if not, write to the Free Software\nFoundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n===========================================================================\n*/\n\n/*****************************************************************************\n * name:\t\tbe_aas_debug.c\n *\n * desc:\t\tAAS debug code\n *\n * $Archive: /MissionPack/code/botlib/be_aas_debug.c $\n *\n *****************************************************************************/\n\n#include \"q_shared.h\"\n#include \"l_memory.h\"\n#include \"l_script.h\"\n#include \"l_precomp.h\"\n#include \"l_struct.h\"\n#include \"l_libvar.h\"\n#include \"aasfile.h\"\n#include \"botlib.h\"\n#include \"be_aas.h\"\n#include \"be_interface.h\"\n#include \"be_aas_funcs.h\"\n#include \"be_aas_def.h\"\n\n#define MAX_DEBUGLINES\t\t\t\t1024\n#define MAX_DEBUGPOLYGONS\t\t\t8192\n\nint debuglines[MAX_DEBUGLINES];\nint debuglinevisible[MAX_DEBUGLINES];\nint numdebuglines;\n\nstatic int debugpolygons[MAX_DEBUGPOLYGONS];\n\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid AAS_ClearShownPolygons(void)\n{\n\tint i;\n//*\n\tfor (i = 0; i < MAX_DEBUGPOLYGONS; i++)\n\t{\n\t\tif (debugpolygons[i]) botimport.DebugPolygonDelete(debugpolygons[i]);\n\t\tdebugpolygons[i] = 0;\n\t} //end for\n//*/\n/*\n\tfor (i = 0; i < MAX_DEBUGPOLYGONS; i++)\n\t{\n\t\tbotimport.DebugPolygonDelete(i);\n\t\tdebugpolygons[i] = 0;\n\t} //end for\n*/\n} //end of the function AAS_ClearShownPolygons\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid AAS_ShowPolygon(int color, int numpoints, vec3_t *points)\n{\n\tint i;\n\n\tfor (i = 0; i < MAX_DEBUGPOLYGONS; i++)\n\t{\n\t\tif (!debugpolygons[i])\n\t\t{\n\t\t\tdebugpolygons[i] = botimport.DebugPolygonCreate(color, numpoints, points);\n\t\t\tbreak;\n\t\t} //end if\n\t} //end for\n} //end of the function AAS_ShowPolygon\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid AAS_ClearShownDebugLines(void)\n{\n\tint i;\n\n\t//make all lines invisible\n\tfor (i = 0; i < MAX_DEBUGLINES; i++)\n\t{\n\t\tif (debuglines[i])\n\t\t{\n\t\t\t//botimport.DebugLineShow(debuglines[i], NULL, NULL, LINECOLOR_NONE);\n\t\t\tbotimport.DebugLineDelete(debuglines[i]);\n\t\t\tdebuglines[i] = 0;\n\t\t\tdebuglinevisible[i] = qfalse;\n\t\t} //end if\n\t} //end for\n} //end of the function AAS_ClearShownDebugLines\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid AAS_DebugLine(vec3_t start, vec3_t end, int color)\n{\n\tint line;\n\n\tfor (line = 0; line < MAX_DEBUGLINES; line++)\n\t{\n\t\tif (!debuglines[line])\n\t\t{\n\t\t\tdebuglines[line] = botimport.DebugLineCreate();\n\t\t\tdebuglinevisible[line] = qfalse;\n\t\t\tnumdebuglines++;\n\t\t} //end if\n\t\tif (!debuglinevisible[line])\n\t\t{\n\t\t\tbotimport.DebugLineShow(debuglines[line], start, end, color);\n\t\t\tdebuglinevisible[line] = qtrue;\n\t\t\treturn;\n\t\t} //end else\n\t} //end for\n} //end of the function AAS_DebugLine\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid AAS_PermanentLine(vec3_t start, vec3_t end, int color)\n{\n\tint line;\n\n\tline = botimport.DebugLineCreate();\n\tbotimport.DebugLineShow(line, start, end, color);\n} //end of the function AAS_PermenentLine\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid AAS_DrawPermanentCross(vec3_t origin, float size, int color)\n{\n\tint i, debugline;\n\tvec3_t start, end;\n\n\tfor (i = 0; i < 3; i++)\n\t{\n\t\tVectorCopy(origin, start);\n\t\tstart[i] += size;\n\t\tVectorCopy(origin, end);\n\t\tend[i] -= size;\n\t\tAAS_DebugLine(start, end, color);\n\t\tdebugline = botimport.DebugLineCreate();\n\t\tbotimport.DebugLineShow(debugline, start, end, color);\n\t} //end for\n} //end of the function AAS_DrawPermanentCross\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid AAS_DrawPlaneCross(vec3_t point, vec3_t normal, float dist, int type, int color)\n{\n\tint n0, n1, n2, j, line, lines[2];\n\tvec3_t start1, end1, start2, end2;\n\n\t//make a cross in the hit plane at the hit point\n\tVectorCopy(point, start1);\n\tVectorCopy(point, end1);\n\tVectorCopy(point, start2);\n\tVectorCopy(point, end2);\n\n\tn0 = type % 3;\n\tn1 = (type + 1) % 3;\n\tn2 = (type + 2) % 3;\n\tstart1[n1] -= 6;\n\tstart1[n2] -= 6;\n\tend1[n1] += 6;\n\tend1[n2] += 6;\n\tstart2[n1] += 6;\n\tstart2[n2] -= 6;\n\tend2[n1] -= 6;\n\tend2[n2] += 6;\n\n\tstart1[n0] = (dist - (start1[n1] * normal[n1] +\n\t\t\t\tstart1[n2] * normal[n2])) / normal[n0];\n\tend1[n0] = (dist - (end1[n1] * normal[n1] +\n\t\t\t\tend1[n2] * normal[n2])) / normal[n0];\n\tstart2[n0] = (dist - (start2[n1] * normal[n1] +\n\t\t\t\tstart2[n2] * normal[n2])) / normal[n0];\n\tend2[n0] = (dist - (end2[n1] * normal[n1] +\n\t\t\t\tend2[n2] * normal[n2])) / normal[n0];\n\n\tfor (j = 0, line = 0; j < 2 && line < MAX_DEBUGLINES; line++)\n\t{\n\t\tif (!debuglines[line])\n\t\t{\n\t\t\tdebuglines[line] = botimport.DebugLineCreate();\n\t\t\tlines[j++] = debuglines[line];\n\t\t\tdebuglinevisible[line] = qtrue;\n\t\t\tnumdebuglines++;\n\t\t} //end if\n\t\telse if (!debuglinevisible[line])\n\t\t{\n\t\t\tlines[j++] = debuglines[line];\n\t\t\tdebuglinevisible[line] = qtrue;\n\t\t} //end else\n\t} //end for\n\tbotimport.DebugLineShow(lines[0], start1, end1, color);\n\tbotimport.DebugLineShow(lines[1], start2, end2, color);\n} //end of the function AAS_DrawPlaneCross\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid AAS_ShowBoundingBox(vec3_t origin, vec3_t mins, vec3_t maxs)\n{\n\tvec3_t bboxcorners[8];\n\tint lines[3];\n\tint i, j, line;\n\n\t//upper corners\n\tbboxcorners[0][0] = origin[0] + maxs[0];\n\tbboxcorners[0][1] = origin[1] + maxs[1];\n\tbboxcorners[0][2] = origin[2] + maxs[2];\n\t//\n\tbboxcorners[1][0] = origin[0] + mins[0];\n\tbboxcorners[1][1] = origin[1] + maxs[1];\n\tbboxcorners[1][2] = origin[2] + maxs[2];\n\t//\n\tbboxcorners[2][0] = origin[0] + mins[0];\n\tbboxcorners[2][1] = origin[1] + mins[1];\n\tbboxcorners[2][2] = origin[2] + maxs[2];\n\t//\n\tbboxcorners[3][0] = origin[0] + maxs[0];\n\tbboxcorners[3][1] = origin[1] + mins[1];\n\tbboxcorners[3][2] = origin[2] + maxs[2];\n\t//lower corners\n\tCom_Memcpy(bboxcorners[4], bboxcorners[0], sizeof(vec3_t) * 4);\n\tfor (i = 0; i < 4; i++) bboxcorners[4 + i][2] = origin[2] + mins[2];\n\t//draw bounding box\n\tfor (i = 0; i < 4; i++)\n\t{\n\t\tfor (j = 0, line = 0; j < 3 && line < MAX_DEBUGLINES; line++)\n\t\t{\n\t\t\tif (!debuglines[line])\n\t\t\t{\n\t\t\t\tdebuglines[line] = botimport.DebugLineCreate();\n\t\t\t\tlines[j++] = debuglines[line];\n\t\t\t\tdebuglinevisible[line] = qtrue;\n\t\t\t\tnumdebuglines++;\n\t\t\t} //end if\n\t\t\telse if (!debuglinevisible[line])\n\t\t\t{\n\t\t\t\tlines[j++] = debuglines[line];\n\t\t\t\tdebuglinevisible[line] = qtrue;\n\t\t\t} //end else\n\t\t} //end for\n\t\t//top plane\n\t\tbotimport.DebugLineShow(lines[0], bboxcorners[i],\n\t\t\t\t\t\t\t\t\tbboxcorners[(i+1)&3], LINECOLOR_RED);\n\t\t//bottom plane\n\t\tbotimport.DebugLineShow(lines[1], bboxcorners[4+i],\n\t\t\t\t\t\t\t\t\tbboxcorners[4+((i+1)&3)], LINECOLOR_RED);\n\t\t//vertical lines\n\t\tbotimport.DebugLineShow(lines[2], bboxcorners[i],\n\t\t\t\t\t\t\t\t\tbboxcorners[4+i], LINECOLOR_RED);\n\t} //end for\n} //end of the function AAS_ShowBoundingBox\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid AAS_ShowFace(int facenum)\n{\n\tint i, color, edgenum;\n\taas_edge_t *edge;\n\taas_face_t *face;\n\taas_plane_t *plane;\n\tvec3_t start, end;\n\n\tcolor = LINECOLOR_YELLOW;\n\t//check if face number is in range\n\tif (facenum >= aasworld.numfaces)\n\t{\n\t\tbotimport.Print(PRT_ERROR, \"facenum %d out of range\\n\", facenum);\n\t} //end if\n\tface = &aasworld.faces[facenum];\n\t//walk through the edges of the face\n\tfor (i = 0; i < face->numedges; i++)\n\t{\n\t\t//edge number\n\t\tedgenum = abs(aasworld.edgeindex[face->firstedge + i]);\n\t\t//check if edge number is in range\n\t\tif (edgenum >= aasworld.numedges)\n\t\t{\n\t\t\tbotimport.Print(PRT_ERROR, \"edgenum %d out of range\\n\", edgenum);\n\t\t} //end if\n\t\tedge = &aasworld.edges[edgenum];\n\t\tif (color == LINECOLOR_RED) color = LINECOLOR_GREEN;\n\t\telse if (color == LINECOLOR_GREEN) color = LINECOLOR_BLUE;\n\t\telse if (color == LINECOLOR_BLUE) color = LINECOLOR_YELLOW;\n\t\telse color = LINECOLOR_RED;\n\t\tAAS_DebugLine(aasworld.vertexes[edge->v[0]],\n\t\t\t\t\t\t\t\t\t\taasworld.vertexes[edge->v[1]],\n\t\t\t\t\t\t\t\t\t\tcolor);\n\t} //end for\n\tplane = &aasworld.planes[face->planenum];\n\tedgenum = abs(aasworld.edgeindex[face->firstedge]);\n\tedge = &aasworld.edges[edgenum];\n\tVectorCopy(aasworld.vertexes[edge->v[0]], start);\n\tVectorMA(start, 20, plane->normal, end);\n\tAAS_DebugLine(start, end, LINECOLOR_RED);\n} //end of the function AAS_ShowFace\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid AAS_ShowFacePolygon(int facenum, int color, int flip)\n{\n\tint i, edgenum, numpoints;\n\tvec3_t points[128];\n\taas_edge_t *edge;\n\taas_face_t *face;\n\n\t//check if face number is in range\n\tif (facenum >= aasworld.numfaces)\n\t{\n\t\tbotimport.Print(PRT_ERROR, \"facenum %d out of range\\n\", facenum);\n\t} //end if\n\tface = &aasworld.faces[facenum];\n\t//walk through the edges of the face\n\tnumpoints = 0;\n\tif (flip)\n\t{\n\t\tfor (i = face->numedges-1; i >= 0; i--)\n\t\t{\n\t\t\t//edge number\n\t\t\tedgenum = aasworld.edgeindex[face->firstedge + i];\n\t\t\tedge = &aasworld.edges[abs(edgenum)];\n\t\t\tVectorCopy(aasworld.vertexes[edge->v[edgenum < 0]], points[numpoints]);\n\t\t\tnumpoints++;\n\t\t} //end for\n\t} //end if\n\telse\n\t{\n\t\tfor (i = 0; i < face->numedges; i++)\n\t\t{\n\t\t\t//edge number\n\t\t\tedgenum = aasworld.edgeindex[face->firstedge + i];\n\t\t\tedge = &aasworld.edges[abs(edgenum)];\n\t\t\tVectorCopy(aasworld.vertexes[edge->v[edgenum < 0]], points[numpoints]);\n\t\t\tnumpoints++;\n\t\t} //end for\n\t} //end else\n\tAAS_ShowPolygon(color, numpoints, points);\n} //end of the function AAS_ShowFacePolygon\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid AAS_ShowArea(int areanum, int groundfacesonly)\n{\n\tint areaedges[MAX_DEBUGLINES];\n\tint numareaedges, i, j, n, color = 0, line;\n\tint facenum, edgenum;\n\taas_area_t *area;\n\taas_face_t *face;\n\taas_edge_t *edge;\n\n\t//\n\tnumareaedges = 0;\n\t//\n\tif (areanum < 0 || areanum >= aasworld.numareas)\n\t{\n\t\tbotimport.Print(PRT_ERROR, \"area %d out of range [0, %d]\\n\",\n\t\t\t\t\t\t\t\tareanum, aasworld.numareas);\n\t\treturn;\n\t} //end if\n\t//pointer to the convex area\n\tarea = &aasworld.areas[areanum];\n\t//walk through the faces of the area\n\tfor (i = 0; i < area->numfaces; i++)\n\t{\n\t\tfacenum = abs(aasworld.faceindex[area->firstface + i]);\n\t\t//check if face number is in range\n\t\tif (facenum >= aasworld.numfaces)\n\t\t{\n\t\t\tbotimport.Print(PRT_ERROR, \"facenum %d out of range\\n\", facenum);\n\t\t} //end if\n\t\tface = &aasworld.faces[facenum];\n\t\t//ground faces only\n\t\tif (groundfacesonly)\n\t\t{\n\t\t\tif (!(face->faceflags & (FACE_GROUND | FACE_LADDER))) continue;\n\t\t} //end if\n\t\t//walk through the edges of the face\n\t\tfor (j = 0; j < face->numedges; j++)\n\t\t{\n\t\t\t//edge number\n\t\t\tedgenum = abs(aasworld.edgeindex[face->firstedge + j]);\n\t\t\t//check if edge number is in range\n\t\t\tif (edgenum >= aasworld.numedges)\n\t\t\t{\n\t\t\t\tbotimport.Print(PRT_ERROR, \"edgenum %d out of range\\n\", edgenum);\n\t\t\t} //end if\n\t\t\t//check if the edge is stored already\n\t\t\tfor (n = 0; n < numareaedges; n++)\n\t\t\t{\n\t\t\t\tif (areaedges[n] == edgenum) break;\n\t\t\t} //end for\n\t\t\tif (n == numareaedges && numareaedges < MAX_DEBUGLINES)\n\t\t\t{\n\t\t\t\tareaedges[numareaedges++] = edgenum;\n\t\t\t} //end if\n\t\t} //end for\n\t\t//AAS_ShowFace(facenum);\n\t} //end for\n\t//draw all the edges\n\tfor (n = 0; n < numareaedges; n++)\n\t{\n\t\tfor (line = 0; line < MAX_DEBUGLINES; line++)\n\t\t{\n\t\t\tif (!debuglines[line])\n\t\t\t{\n\t\t\t\tdebuglines[line] = botimport.DebugLineCreate();\n\t\t\t\tdebuglinevisible[line] = qfalse;\n\t\t\t\tnumdebuglines++;\n\t\t\t} //end if\n\t\t\tif (!debuglinevisible[line])\n\t\t\t{\n\t\t\t\tbreak;\n\t\t\t} //end else\n\t\t} //end for\n\t\tif (line >= MAX_DEBUGLINES) return;\n\t\tedge = &aasworld.edges[areaedges[n]];\n\t\tif (color == LINECOLOR_RED) color = LINECOLOR_BLUE;\n\t\telse if (color == LINECOLOR_BLUE) color = LINECOLOR_GREEN;\n\t\telse if (color == LINECOLOR_GREEN) color = LINECOLOR_YELLOW;\n\t\telse color = LINECOLOR_RED;\n\t\tbotimport.DebugLineShow(debuglines[line],\n\t\t\t\t\t\t\t\t\taasworld.vertexes[edge->v[0]],\n\t\t\t\t\t\t\t\t\taasworld.vertexes[edge->v[1]],\n\t\t\t\t\t\t\t\t\tcolor);\n\t\tdebuglinevisible[line] = qtrue;\n\t} //end for*/\n} //end of the function AAS_ShowArea\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid AAS_ShowAreaPolygons(int areanum, int color, int groundfacesonly)\n{\n\tint i, facenum;\n\taas_area_t *area;\n\taas_face_t *face;\n\n\t//\n\tif (areanum < 0 || areanum >= aasworld.numareas)\n\t{\n\t\tbotimport.Print(PRT_ERROR, \"area %d out of range [0, %d]\\n\",\n\t\t\t\t\t\t\t\tareanum, aasworld.numareas);\n\t\treturn;\n\t} //end if\n\t//pointer to the convex area\n\tarea = &aasworld.areas[areanum];\n\t//walk through the faces of the area\n\tfor (i = 0; i < area->numfaces; i++)\n\t{\n\t\tfacenum = abs(aasworld.faceindex[area->firstface + i]);\n\t\t//check if face number is in range\n\t\tif (facenum >= aasworld.numfaces)\n\t\t{\n\t\t\tbotimport.Print(PRT_ERROR, \"facenum %d out of range\\n\", facenum);\n\t\t} //end if\n\t\tface = &aasworld.faces[facenum];\n\t\t//ground faces only\n\t\tif (groundfacesonly)\n\t\t{\n\t\t\tif (!(face->faceflags & (FACE_GROUND | FACE_LADDER))) continue;\n\t\t} //end if\n\t\tAAS_ShowFacePolygon(facenum, color, face->frontarea != areanum);\n\t} //end for\n} //end of the function AAS_ShowAreaPolygons\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid AAS_DrawCross(vec3_t origin, float size, int color)\n{\n\tint i;\n\tvec3_t start, end;\n\n\tfor (i = 0; i < 3; i++)\n\t{\n\t\tVectorCopy(origin, start);\n\t\tstart[i] += size;\n\t\tVectorCopy(origin, end);\n\t\tend[i] -= size;\n\t\tAAS_DebugLine(start, end, color);\n\t} //end for\n} //end of the function AAS_DrawCross\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid AAS_PrintTravelType(int traveltype)\n{\n#ifdef DEBUG\n\tchar *str;\n\t//\n\tswitch(traveltype & TRAVELTYPE_MASK)\n\t{\n\t\tcase TRAVEL_INVALID: str = \"TRAVEL_INVALID\"; break;\n\t\tcase TRAVEL_WALK: str = \"TRAVEL_WALK\"; break;\n\t\tcase TRAVEL_CROUCH: str = \"TRAVEL_CROUCH\"; break;\n\t\tcase TRAVEL_BARRIERJUMP: str = \"TRAVEL_BARRIERJUMP\"; break;\n\t\tcase TRAVEL_JUMP: str = \"TRAVEL_JUMP\"; break;\n\t\tcase TRAVEL_LADDER: str = \"TRAVEL_LADDER\"; break;\n\t\tcase TRAVEL_WALKOFFLEDGE: str = \"TRAVEL_WALKOFFLEDGE\"; break;\n\t\tcase TRAVEL_SWIM: str = \"TRAVEL_SWIM\"; break;\n\t\tcase TRAVEL_WATERJUMP: str = \"TRAVEL_WATERJUMP\"; break;\n\t\tcase TRAVEL_TELEPORT: str = \"TRAVEL_TELEPORT\"; break;\n\t\tcase TRAVEL_ELEVATOR: str = \"TRAVEL_ELEVATOR\"; break;\n\t\tcase TRAVEL_ROCKETJUMP: str = \"TRAVEL_ROCKETJUMP\"; break;\n\t\tcase TRAVEL_BFGJUMP: str = \"TRAVEL_BFGJUMP\"; break;\n\t\tcase TRAVEL_GRAPPLEHOOK: str = \"TRAVEL_GRAPPLEHOOK\"; break;\n\t\tcase TRAVEL_JUMPPAD: str = \"TRAVEL_JUMPPAD\"; break;\n\t\tcase TRAVEL_FUNCBOB: str = \"TRAVEL_FUNCBOB\"; break;\n\t\tdefault: str = \"UNKNOWN TRAVEL TYPE\"; break;\n\t} //end switch\n\tbotimport.Print(PRT_MESSAGE, \"%s\", str);\n#endif\n} //end of the function AAS_PrintTravelType\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid AAS_DrawArrow(vec3_t start, vec3_t end, int linecolor, int arrowcolor)\n{\n\tvec3_t dir, cross, p1, p2, up = {0, 0, 1};\n\tfloat dot;\n\n\tVectorSubtract(end, start, dir);\n\tVectorNormalize(dir);\n\tdot = DotProduct(dir, up);\n\tif (dot > 0.99 || dot < -0.99) VectorSet(cross, 1, 0, 0);\n\telse CrossProduct(dir, up, cross);\n\n\tVectorMA(end, -6, dir, p1);\n\tVectorCopy(p1, p2);\n\tVectorMA(p1, 6, cross, p1);\n\tVectorMA(p2, -6, cross, p2);\n\n\tAAS_DebugLine(start, end, linecolor);\n\tAAS_DebugLine(p1, end, arrowcolor);\n\tAAS_DebugLine(p2, end, arrowcolor);\n} //end of the function AAS_DrawArrow\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid AAS_ShowReachability(aas_reachability_t *reach)\n{\n\tvec3_t dir, cmdmove, velocity;\n\tfloat speed, zvel;\n\taas_clientmove_t move;\n\n\tAAS_ShowAreaPolygons(reach->areanum, 5, qtrue);\n\t//AAS_ShowArea(reach->areanum, qtrue);\n\tAAS_DrawArrow(reach->start, reach->end, LINECOLOR_BLUE, LINECOLOR_YELLOW);\n\t//\n\tif ((reach->traveltype & TRAVELTYPE_MASK) == TRAVEL_JUMP ||\n\t\t(reach->traveltype & TRAVELTYPE_MASK) == TRAVEL_WALKOFFLEDGE)\n\t{\n\t\tAAS_HorizontalVelocityForJump(aassettings.phys_jumpvel, reach->start, reach->end, &speed);\n\t\t//\n\t\tVectorSubtract(reach->end, reach->start, dir);\n\t\tdir[2] = 0;\n\t\tVectorNormalize(dir);\n\t\t//set the velocity\n\t\tVectorScale(dir, speed, velocity);\n\t\t//set the command movement\n\t\tVectorClear(cmdmove);\n\t\tcmdmove[2] = aassettings.phys_jumpvel;\n\t\t//\n\t\tAAS_PredictClientMovement(&move, -1, reach->start, PRESENCE_NORMAL, qtrue,\n\t\t\t\t\t\t\t\t\tvelocity, cmdmove, 3, 30, 0.1f,\n\t\t\t\t\t\t\t\t\tSE_HITGROUND|SE_ENTERWATER|SE_ENTERSLIME|\n\t\t\t\t\t\t\t\t\tSE_ENTERLAVA|SE_HITGROUNDDAMAGE, 0, qtrue);\n\t\t//\n\t\tif ((reach->traveltype & TRAVELTYPE_MASK) == TRAVEL_JUMP)\n\t\t{\n\t\t\tAAS_JumpReachRunStart(reach, dir);\n\t\t\tAAS_DrawCross(dir, 4, LINECOLOR_BLUE);\n\t\t} //end if\n\t} //end if\n\telse if ((reach->traveltype & TRAVELTYPE_MASK) == TRAVEL_ROCKETJUMP)\n\t{\n\t\tzvel = AAS_RocketJumpZVelocity(reach->start);\n\t\tAAS_HorizontalVelocityForJump(zvel, reach->start, reach->end, &speed);\n\t\t//\n\t\tVectorSubtract(reach->end, reach->start, dir);\n\t\tdir[2] = 0;\n\t\tVectorNormalize(dir);\n\t\t//get command movement\n\t\tVectorScale(dir, speed, cmdmove);\n\t\tVectorSet(velocity, 0, 0, zvel);\n\t\t//\n\t\tAAS_PredictClientMovement(&move, -1, reach->start, PRESENCE_NORMAL, qtrue,\n\t\t\t\t\t\t\t\t\tvelocity, cmdmove, 30, 30, 0.1f,\n\t\t\t\t\t\t\t\t\tSE_ENTERWATER|SE_ENTERSLIME|\n\t\t\t\t\t\t\t\t\tSE_ENTERLAVA|SE_HITGROUNDDAMAGE|\n\t\t\t\t\t\t\t\t\tSE_TOUCHJUMPPAD|SE_HITGROUNDAREA, reach->areanum, qtrue);\n\t} //end else if\n\telse if ((reach->traveltype & TRAVELTYPE_MASK) == TRAVEL_JUMPPAD)\n\t{\n\t\tVectorSet(cmdmove, 0, 0, 0);\n\t\t//\n\t\tVectorSubtract(reach->end, reach->start, dir);\n\t\tdir[2] = 0;\n\t\tVectorNormalize(dir);\n\t\t//set the velocity\n\t\t//NOTE: the edgenum is the horizontal velocity\n\t\tVectorScale(dir, reach->edgenum, velocity);\n\t\t//NOTE: the facenum is the Z velocity\n\t\tvelocity[2] = reach->facenum;\n\t\t//\n\t\tAAS_PredictClientMovement(&move, -1, reach->start, PRESENCE_NORMAL, qtrue,\n\t\t\t\t\t\t\t\t\tvelocity, cmdmove, 30, 30, 0.1f,\n\t\t\t\t\t\t\t\t\tSE_ENTERWATER|SE_ENTERSLIME|\n\t\t\t\t\t\t\t\t\tSE_ENTERLAVA|SE_HITGROUNDDAMAGE|\n\t\t\t\t\t\t\t\t\tSE_TOUCHJUMPPAD|SE_HITGROUNDAREA, reach->areanum, qtrue);\n\t} //end else if\n} //end of the function AAS_ShowReachability\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid AAS_ShowReachableAreas(int areanum)\n{\n\taas_areasettings_t *settings;\n\tstatic aas_reachability_t reach;\n\tstatic int index, lastareanum;\n\tstatic float lasttime;\n\n\tif (areanum != lastareanum)\n\t{\n\t\tindex = 0;\n\t\tlastareanum = areanum;\n\t} //end if\n\tsettings = &aasworld.areasettings[areanum];\n\t//\n\tif (!settings->numreachableareas) return;\n\t//\n\tif (index >= settings->numreachableareas) index = 0;\n\t//\n\tif (AAS_Time() - lasttime > 1.5)\n\t{\n\t\tCom_Memcpy(&reach, &aasworld.reachability[settings->firstreachablearea + index], sizeof(aas_reachability_t));\n\t\tindex++;\n\t\tlasttime = AAS_Time();\n\t\tAAS_PrintTravelType(reach.traveltype & TRAVELTYPE_MASK);\n\t\tbotimport.Print(PRT_MESSAGE, \"\\n\");\n\t} //end if\n\tAAS_ShowReachability(&reach);\n} //end of the function ShowReachableAreas\n\nvoid AAS_FloodAreas_r(int areanum, int cluster, int *done)\n{\n\tint nextareanum, i, facenum;\n\taas_area_t *area;\n\taas_face_t *face;\n\taas_areasettings_t *settings;\n\taas_reachability_t *reach;\n\n\tAAS_ShowAreaPolygons(areanum, 1, qtrue);\n\t//pointer to the convex area\n\tarea = &aasworld.areas[areanum];\n\tsettings = &aasworld.areasettings[areanum];\n\t//walk through the faces of the area\n\tfor (i = 0; i < area->numfaces; i++)\n\t{\n\t\tfacenum = abs(aasworld.faceindex[area->firstface + i]);\n\t\tface = &aasworld.faces[facenum];\n\t\tif (face->frontarea == areanum)\n\t\t\tnextareanum = face->backarea;\n\t\telse\n\t\t\tnextareanum = face->frontarea;\n\t\tif (!nextareanum)\n\t\t\tcontinue;\n\t\tif (done[nextareanum])\n\t\t\tcontinue;\n\t\tdone[nextareanum] = qtrue;\n\t\tif (aasworld.areasettings[nextareanum].contents & AREACONTENTS_VIEWPORTAL)\n\t\t\tcontinue;\n\t\tif (AAS_AreaCluster(nextareanum) != cluster)\n\t\t\tcontinue;\n\t\tAAS_FloodAreas_r(nextareanum, cluster, done);\n\t} //end for\n\t//\n\tfor (i = 0; i < settings->numreachableareas; i++)\n\t{\n\t\treach = &aasworld.reachability[settings->firstreachablearea + i];\n\t\tnextareanum = reach->areanum;\n\t\tif (!nextareanum)\n\t\t\tcontinue;\n\t\tif (done[nextareanum])\n\t\t\tcontinue;\n\t\tdone[nextareanum] = qtrue;\n\t\tif (aasworld.areasettings[nextareanum].contents & AREACONTENTS_VIEWPORTAL)\n\t\t\tcontinue;\n\t\tif (AAS_AreaCluster(nextareanum) != cluster)\n\t\t\tcontinue;\n\t\t/*\n\t\tif ((reach->traveltype & TRAVELTYPE_MASK) == TRAVEL_WALKOFFLEDGE)\n\t\t{\n\t\t\tAAS_DebugLine(reach->start, reach->end, 1);\n\t\t}\n\t\t*/\n\t\tAAS_FloodAreas_r(nextareanum, cluster, done);\n\t}\n}\n\nvoid AAS_FloodAreas(vec3_t origin)\n{\n\tint areanum, cluster, *done;\n\n\tdone = (int *) GetClearedMemory(aasworld.numareas * sizeof(int));\n\tareanum = AAS_PointAreaNum(origin);\n\tcluster = AAS_AreaCluster(areanum);\n\tAAS_FloodAreas_r(areanum, cluster, done);\n}\n"
  },
  {
    "path": "plugins/quake3/botlib/be_aas_debug.h",
    "content": "/*\n===========================================================================\nCopyright (C) 1999-2005 Id Software, Inc.\n\nThis file is part of Quake III Arena source code.\n\nQuake III Arena source code is free software; you can redistribute it\nand/or modify it under the terms of the GNU General Public License as\npublished by the Free Software Foundation; either version 2 of the License,\nor (at your option) any later version.\n\nQuake III Arena source code is distributed in the hope that it will be\nuseful, but WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Quake III Arena source code; if not, write to the Free Software\nFoundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n===========================================================================\n*/\n\n/*****************************************************************************\n * name:\t\tbe_aas_debug.h\n *\n * desc:\t\tAAS\n *\n * $Archive: /source/code/botlib/be_aas_debug.h $\n *\n *****************************************************************************/\n\n//clear the shown debug lines\nvoid AAS_ClearShownDebugLines(void);\n//\nvoid AAS_ClearShownPolygons(void);\n//show a debug line\nvoid AAS_DebugLine(vec3_t start, vec3_t end, int color);\n//show a permenent line\nvoid AAS_PermanentLine(vec3_t start, vec3_t end, int color);\n//show a permanent cross\nvoid AAS_DrawPermanentCross(vec3_t origin, float size, int color);\n//draw a cross in the plane\nvoid AAS_DrawPlaneCross(vec3_t point, vec3_t normal, float dist, int type, int color);\n//show a bounding box\nvoid AAS_ShowBoundingBox(vec3_t origin, vec3_t mins, vec3_t maxs);\n//show a face\nvoid AAS_ShowFace(int facenum);\n//show an area\nvoid AAS_ShowArea(int areanum, int groundfacesonly);\n//\nvoid AAS_ShowAreaPolygons(int areanum, int color, int groundfacesonly);\n//draw a cros\nvoid AAS_DrawCross(vec3_t origin, float size, int color);\n//print the travel type\nvoid AAS_PrintTravelType(int traveltype);\n//draw an arrow\nvoid AAS_DrawArrow(vec3_t start, vec3_t end, int linecolor, int arrowcolor);\n//visualize the given reachability\nvoid AAS_ShowReachability(struct aas_reachability_s *reach);\n//show the reachable areas from the given area\nvoid AAS_ShowReachableAreas(int areanum);\n\n"
  },
  {
    "path": "plugins/quake3/botlib/be_aas_def.h",
    "content": "/*\n===========================================================================\nCopyright (C) 1999-2005 Id Software, Inc.\n\nThis file is part of Quake III Arena source code.\n\nQuake III Arena source code is free software; you can redistribute it\nand/or modify it under the terms of the GNU General Public License as\npublished by the Free Software Foundation; either version 2 of the License,\nor (at your option) any later version.\n\nQuake III Arena source code is distributed in the hope that it will be\nuseful, but WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Quake III Arena source code; if not, write to the Free Software\nFoundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n===========================================================================\n*/\n\n/*****************************************************************************\n * name:\t\tbe_aas_def.h\n *\n * desc:\t\tAAS\n *\n * $Archive: /source/code/botlib/be_aas_def.h $\n *\n *****************************************************************************/\n\n//debugging on\n#define AAS_DEBUG\n\n#define MAX_CLIENTS\t\t\t64\n#define\tMAX_MODELS\t\t\t256\t\t// these are sent over the net as 8 bits\n#define\tMAX_SOUNDS\t\t\t256\t\t// so they cannot be blindly increased\n#define\tMAX_CONFIGSTRINGS\t1024\n\n#define\tCS_SCORES\t\t\t32\n#define\tCS_MODELS\t\t\t(CS_SCORES+MAX_CLIENTS)\n#define\tCS_SOUNDS\t\t\t(CS_MODELS+MAX_MODELS)\n\n#define DF_AASENTNUMBER(x)\t\t(x - aasworld.entities)\n#define DF_NUMBERAASENT(x)\t\t(&aasworld.entities[x])\n#define DF_AASENTCLIENT(x)\t\t(x - aasworld.entities - 1)\n#define DF_CLIENTAASENT(x)\t\t(&aasworld.entities[x + 1])\n\n#ifndef MAX_PATH\n\t#define MAX_PATH\t\t\t\tMAX_QPATH\n#endif\n\n//string index (for model, sound and image index)\ntypedef struct aas_stringindex_s\n{\n\tint numindexes;\n\tchar **index;\n} aas_stringindex_t;\n\n//structure to link entities to areas and areas to entities\ntypedef struct aas_link_s\n{\n\tint entnum;\n\tint areanum;\n\tstruct aas_link_s *next_ent, *prev_ent;\n\tstruct aas_link_s *next_area, *prev_area;\n} aas_link_t;\n\n//structure to link entities to leaves and leaves to entities\ntypedef struct bsp_link_s\n{\n\tint entnum;\n\tint leafnum;\n\tstruct bsp_link_s *next_ent, *prev_ent;\n\tstruct bsp_link_s *next_leaf, *prev_leaf;\n} bsp_link_t;\n\ntypedef struct bsp_entdata_s\n{\n\tvec3_t origin;\n\tvec3_t angles;\n\tvec3_t absmins;\n\tvec3_t absmaxs;\n\tint solid;\n\tint modelnum;\n} bsp_entdata_t;\n\n//entity\ntypedef struct aas_entity_s\n{\n\t//entity info\n\taas_entityinfo_t i;\n\t//links into the AAS areas\n\taas_link_t *areas;\n\t//links into the BSP leaves\n\tbsp_link_t *leaves;\n} aas_entity_t;\n\ntypedef struct aas_settings_s\n{\n\tvec3_t phys_gravitydirection;\n\tfloat phys_friction;\n\tfloat phys_stopspeed;\n\tfloat phys_gravity;\n\tfloat phys_waterfriction;\n\tfloat phys_watergravity;\n\tfloat phys_maxvelocity;\n\tfloat phys_maxwalkvelocity;\n\tfloat phys_maxcrouchvelocity;\n\tfloat phys_maxswimvelocity;\n\tfloat phys_walkaccelerate;\n\tfloat phys_airaccelerate;\n\tfloat phys_swimaccelerate;\n\tfloat phys_maxstep;\n\tfloat phys_maxsteepness;\n\tfloat phys_maxwaterjump;\n\tfloat phys_maxbarrier;\n\tfloat phys_jumpvel;\n\tfloat phys_falldelta5;\n\tfloat phys_falldelta10;\n\tfloat rs_waterjump;\n\tfloat rs_teleport;\n\tfloat rs_barrierjump;\n\tfloat rs_startcrouch;\n\tfloat rs_startgrapple;\n\tfloat rs_startwalkoffledge;\n\tfloat rs_startjump;\n\tfloat rs_rocketjump;\n\tfloat rs_bfgjump;\n\tfloat rs_jumppad;\n\tfloat rs_aircontrolledjumppad;\n\tfloat rs_funcbob;\n\tfloat rs_startelevator;\n\tfloat rs_falldamage5;\n\tfloat rs_falldamage10;\n\tfloat rs_maxfallheight;\n\tfloat rs_maxjumpfallheight;\n} aas_settings_t;\n\n#define CACHETYPE_PORTAL\t\t0\n#define CACHETYPE_AREA\t\t\t1\n\n//routing cache\ntypedef struct aas_routingcache_s\n{\n\tbyte type;\t\t\t\t\t\t\t\t\t//portal or area cache\n\tfloat time;\t\t\t\t\t\t\t\t\t//last time accessed or updated\n\tint size;\t\t\t\t\t\t\t\t\t//size of the routing cache\n\tint cluster;\t\t\t\t\t\t\t\t//cluster the cache is for\n\tint areanum;\t\t\t\t\t\t\t\t//area the cache is created for\n\tvec3_t origin;\t\t\t\t\t\t\t\t//origin within the area\n\tfloat starttraveltime;\t\t\t\t\t\t//travel time to start with\n\tint travelflags;\t\t\t\t\t\t\t//combinations of the travel flags\n\tstruct aas_routingcache_s *prev, *next;\n\tstruct aas_routingcache_s *time_prev, *time_next;\n\tunsigned char *reachabilities;\t\t\t\t//reachabilities used for routing\n\tunsigned short int traveltimes[1];\t\t\t//travel time for every area (variable sized)\n} aas_routingcache_t;\n\n//fields for the routing algorithm\ntypedef struct aas_routingupdate_s\n{\n\tint cluster;\n\tint areanum;\t\t\t\t\t\t\t\t//area number of the update\n\tvec3_t start;\t\t\t\t\t\t\t\t//start point the area was entered\n\tunsigned short int tmptraveltime;\t\t\t//temporary travel time\n\tunsigned short int *areatraveltimes;\t\t//travel times within the area\n\tqboolean inlist;\t\t\t\t\t\t\t//true if the update is in the list\n\tstruct aas_routingupdate_s *next;\n\tstruct aas_routingupdate_s *prev;\n} aas_routingupdate_t;\n\n//reversed reachability link\ntypedef struct aas_reversedlink_s\n{\n\tint linknum;\t\t\t\t\t\t\t\t//the aas_areareachability_t\n\tint areanum;\t\t\t\t\t\t\t\t//reachable from this area\n\tstruct aas_reversedlink_s *next;\t\t\t//next link\n} aas_reversedlink_t;\n\n//reversed area reachability\ntypedef struct aas_reversedreachability_s\n{\n\tint numlinks;\n\taas_reversedlink_t *first;\n} aas_reversedreachability_t;\n\n//areas a reachability goes through\ntypedef struct aas_reachabilityareas_s\n{\n\tint firstarea, numareas;\n} aas_reachabilityareas_t;\n\ntypedef struct aas_s\n{\n\tint loaded;\t\t\t\t\t\t\t\t\t//true when an AAS file is loaded\n\tint initialized;\t\t\t\t\t\t\t//true when AAS has been initialized\n\tint savefile;\t\t\t\t\t\t\t\t//set true when file should be saved\n\tint bspchecksum;\n\t//current time\n\tfloat time;\n\tint numframes;\n\t//name of the aas file\n\tchar filename[MAX_PATH];\n\tchar mapname[MAX_PATH];\n\t//bounding boxes\n\tint numbboxes;\n\taas_bbox_t *bboxes;\n\t//vertexes\n\tint numvertexes;\n\taas_vertex_t *vertexes;\n\t//planes\n\tint numplanes;\n\taas_plane_t *planes;\n\t//edges\n\tint numedges;\n\taas_edge_t *edges;\n\t//edge index\n\tint edgeindexsize;\n\taas_edgeindex_t *edgeindex;\n\t//faces\n\tint numfaces;\n\taas_face_t *faces;\n\t//face index\n\tint faceindexsize;\n\taas_faceindex_t *faceindex;\n\t//convex areas\n\tint numareas;\n\taas_area_t *areas;\n\t//convex area settings\n\tint numareasettings;\n\taas_areasettings_t *areasettings;\n\t//reachablity list\n\tint reachabilitysize;\n\taas_reachability_t *reachability;\n\t//nodes of the bsp tree\n\tint numnodes;\n\taas_node_t *nodes;\n\t//cluster portals\n\tint numportals;\n\taas_portal_t *portals;\n\t//cluster portal index\n\tint portalindexsize;\n\taas_portalindex_t *portalindex;\n\t//clusters\n\tint numclusters;\n\taas_cluster_t *clusters;\n\t//\n\tint numreachabilityareas;\n\tfloat reachabilitytime;\n\t//enities linked in the areas\n\taas_link_t *linkheap;\t\t\t\t\t\t//heap with link structures\n\tint linkheapsize;\t\t\t\t\t\t\t//size of the link heap\n\taas_link_t *freelinks;\t\t\t\t\t\t//first free link\n\taas_link_t **arealinkedentities;\t\t\t//entities linked into areas\n\t//entities\n\tint maxentities;\n\tint maxclients;\n\taas_entity_t *entities;\n\t//string indexes\n\tchar *configstrings[MAX_CONFIGSTRINGS];\n\tint indexessetup;\n\t//index to retrieve travel flag for a travel type\n\tint travelflagfortype[MAX_TRAVELTYPES];\n\t//travel flags for each area based on contents\n\tint *areacontentstravelflags;\n\t//routing update\n\taas_routingupdate_t *areaupdate;\n\taas_routingupdate_t *portalupdate;\n\t//number of routing updates during a frame (reset every frame)\n\tint frameroutingupdates;\n\t//reversed reachability links\n\taas_reversedreachability_t *reversedreachability;\n\t//travel times within the areas\n\tunsigned short ***areatraveltimes;\n\t//array of size numclusters with cluster cache\n\taas_routingcache_t ***clusterareacache;\n\taas_routingcache_t **portalcache;\n\t//cache list sorted on time\n\taas_routingcache_t *oldestcache;\t\t// start of cache list sorted on time\n\taas_routingcache_t *newestcache;\t\t// end of cache list sorted on time\n\t//maximum travel time through portal areas\n\tint *portalmaxtraveltimes;\n\t//areas the reachabilities go through\n\tint *reachabilityareaindex;\n\taas_reachabilityareas_t *reachabilityareas;\n} aas_t;\n\n#define AASINTERN\n\n#ifndef BSPCINCLUDE\n\n#include \"be_aas_main.h\"\n#include \"be_aas_entity.h\"\n#include \"be_aas_sample.h\"\n#include \"be_aas_cluster.h\"\n#include \"be_aas_reach.h\"\n#include \"be_aas_route.h\"\n#include \"be_aas_routealt.h\"\n#include \"be_aas_debug.h\"\n#include \"be_aas_file.h\"\n#include \"be_aas_optimize.h\"\n#include \"be_aas_bsp.h\"\n#include \"be_aas_move.h\"\n\n#endif //BSPCINCLUDE\n"
  },
  {
    "path": "plugins/quake3/botlib/be_aas_entity.c",
    "content": "/*\n===========================================================================\nCopyright (C) 1999-2005 Id Software, Inc.\n\nThis file is part of Quake III Arena source code.\n\nQuake III Arena source code is free software; you can redistribute it\nand/or modify it under the terms of the GNU General Public License as\npublished by the Free Software Foundation; either version 2 of the License,\nor (at your option) any later version.\n\nQuake III Arena source code is distributed in the hope that it will be\nuseful, but WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Quake III Arena source code; if not, write to the Free Software\nFoundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n===========================================================================\n*/\n\n/*****************************************************************************\n * name:\t\tbe_aas_entity.c\n *\n * desc:\t\tAAS entities\n *\n * $Archive: /MissionPack/code/botlib/be_aas_entity.c $\n *\n *****************************************************************************/\n\n#include \"q_shared.h\"\n#include \"l_memory.h\"\n#include \"l_script.h\"\n#include \"l_precomp.h\"\n#include \"l_struct.h\"\n#include \"l_utils.h\"\n#include \"l_log.h\"\n#include \"aasfile.h\"\n#include \"botlib.h\"\n#include \"be_aas.h\"\n#include \"be_aas_funcs.h\"\n#include \"be_interface.h\"\n#include \"be_aas_def.h\"\n\n#define MASK_SOLID\t\tCONTENTS_PLAYERCLIP\n\n//FIXME: these might change\nenum {\n\tET_GENERAL,\n\tET_PLAYER,\n\tET_ITEM,\n\tET_MISSILE,\n\tET_MOVER\n};\n\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint AAS_UpdateEntity(int entnum, bot_entitystate_t *state)\n{\n\tint relink;\n\taas_entity_t *ent;\n\tvec3_t absmins, absmaxs;\n\n\tif (!aasworld.loaded)\n\t{\n\t\tbotimport.Print(PRT_MESSAGE, \"AAS_UpdateEntity: not loaded\\n\");\n\t\treturn BLERR_NOAASFILE;\n\t} //end if\n\n\tent = &aasworld.entities[entnum];\n\n\tif (!state) {\n\t\t//unlink the entity\n\t\tAAS_UnlinkFromAreas(ent->areas);\n\t\t//unlink the entity from the BSP leaves\n\t\tAAS_UnlinkFromBSPLeaves(ent->leaves);\n\t\t//\n\t\tent->areas = NULL;\n\t\t//\n\t\tent->leaves = NULL;\n\t\treturn BLERR_NOERROR;\n\t}\n\n\tent->i.update_time = AAS_Time() - ent->i.ltime;\n\tent->i.type = state->type;\n\tent->i.flags = state->flags;\n\tent->i.ltime = AAS_Time();\n\tVectorCopy(ent->i.origin, ent->i.lastvisorigin);\n\tVectorCopy(state->old_origin, ent->i.old_origin);\n\tent->i.solid = state->solid;\n\tent->i.groundent = state->groundent;\n\tent->i.modelindex = state->modelindex;\n\tent->i.modelindex2 = state->modelindex2;\n\tent->i.frame = state->frame;\n\tent->i.event = state->event;\n\tent->i.eventParm = state->eventParm;\n\tent->i.powerups = state->powerups;\n\tent->i.weapon = state->weapon;\n\tent->i.legsAnim = state->legsAnim;\n\tent->i.torsoAnim = state->torsoAnim;\n\t//number of the entity\n\tent->i.number = entnum;\n\t//updated so set valid flag\n\tent->i.valid = qtrue;\n\t//link everything the first frame\n\tif (aasworld.numframes == 1) relink = qtrue;\n\telse relink = qfalse;\n\t//\n\tif (ent->i.solid == SOLID_BSP)\n\t{\n\t\t//if the angles of the model changed\n\t\tif (!VectorCompare(state->angles, ent->i.angles))\n\t\t{\n\t\t\tVectorCopy(state->angles, ent->i.angles);\n\t\t\trelink = qtrue;\n\t\t} //end if\n\t\t//get the mins and maxs of the model\n\t\t//FIXME: rotate mins and maxs\n\t\tAAS_BSPModelMinsMaxsOrigin(ent->i.modelindex, ent->i.angles, ent->i.mins, ent->i.maxs, NULL);\n\t} //end if\n\telse if (ent->i.solid == SOLID_BBOX)\n\t{\n\t\t//if the bounding box size changed\n\t\tif (!VectorCompare(state->mins, ent->i.mins) ||\n\t\t\t\t!VectorCompare(state->maxs, ent->i.maxs))\n\t\t{\n\t\t\tVectorCopy(state->mins, ent->i.mins);\n\t\t\tVectorCopy(state->maxs, ent->i.maxs);\n\t\t\trelink = qtrue;\n\t\t} //end if\n\t\tVectorCopy(state->angles, ent->i.angles);\n\t} //end if\n\t//if the origin changed\n\tif (!VectorCompare(state->origin, ent->i.origin))\n\t{\n\t\tVectorCopy(state->origin, ent->i.origin);\n\t\trelink = qtrue;\n\t} //end if\n\t//if the entity should be relinked\n\tif (relink)\n\t{\n\t\t//don't link the world model\n\t\tif (entnum != ENTITYNUM_WORLD)\n\t\t{\n\t\t\t//absolute mins and maxs\n\t\t\tVectorAdd(ent->i.mins, ent->i.origin, absmins);\n\t\t\tVectorAdd(ent->i.maxs, ent->i.origin, absmaxs);\n\t\t\t//unlink the entity\n\t\t\tAAS_UnlinkFromAreas(ent->areas);\n\t\t\t//relink the entity to the AAS areas (use the larges bbox)\n\t\t\tent->areas = AAS_LinkEntityClientBBox(absmins, absmaxs, entnum, PRESENCE_NORMAL);\n\t\t\t//unlink the entity from the BSP leaves\n\t\t\tAAS_UnlinkFromBSPLeaves(ent->leaves);\n\t\t\t//link the entity to the world BSP tree\n\t\t\tent->leaves = AAS_BSPLinkEntity(absmins, absmaxs, entnum, 0);\n\t\t} //end if\n\t} //end if\n\treturn BLERR_NOERROR;\n} //end of the function AAS_UpdateEntity\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid AAS_EntityInfo(int entnum, aas_entityinfo_t *info)\n{\n\tif (!aasworld.initialized)\n\t{\n\t\tbotimport.Print(PRT_FATAL, \"AAS_EntityInfo: aasworld not initialized\\n\");\n\t\tCom_Memset(info, 0, sizeof(aas_entityinfo_t));\n\t\treturn;\n\t} //end if\n\n\tif (entnum < 0 || entnum >= aasworld.maxentities)\n\t{\n\t\tbotimport.Print(PRT_FATAL, \"AAS_EntityInfo: entnum %d out of range\\n\", entnum);\n\t\tCom_Memset(info, 0, sizeof(aas_entityinfo_t));\n\t\treturn;\n\t} //end if\n\n\tCom_Memcpy(info, &aasworld.entities[entnum].i, sizeof(aas_entityinfo_t));\n} //end of the function AAS_EntityInfo\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid AAS_EntityOrigin(int entnum, vec3_t origin)\n{\n\tif (entnum < 0 || entnum >= aasworld.maxentities)\n\t{\n\t\tbotimport.Print(PRT_FATAL, \"AAS_EntityOrigin: entnum %d out of range\\n\", entnum);\n\t\tVectorClear(origin);\n\t\treturn;\n\t} //end if\n\n\tVectorCopy(aasworld.entities[entnum].i.origin, origin);\n} //end of the function AAS_EntityOrigin\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint AAS_EntityModelindex(int entnum)\n{\n\tif (entnum < 0 || entnum >= aasworld.maxentities)\n\t{\n\t\tbotimport.Print(PRT_FATAL, \"AAS_EntityModelindex: entnum %d out of range\\n\", entnum);\n\t\treturn 0;\n\t} //end if\n\treturn aasworld.entities[entnum].i.modelindex;\n} //end of the function AAS_EntityModelindex\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint AAS_EntityType(int entnum)\n{\n\tif (!aasworld.initialized) return 0;\n\n\tif (entnum < 0 || entnum >= aasworld.maxentities)\n\t{\n\t\tbotimport.Print(PRT_FATAL, \"AAS_EntityType: entnum %d out of range\\n\", entnum);\n\t\treturn 0;\n\t} //end if\n\treturn aasworld.entities[entnum].i.type;\n} //end of the AAS_EntityType\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint AAS_EntityModelNum(int entnum)\n{\n\tif (!aasworld.initialized) return 0;\n\n\tif (entnum < 0 || entnum >= aasworld.maxentities)\n\t{\n\t\tbotimport.Print(PRT_FATAL, \"AAS_EntityModelNum: entnum %d out of range\\n\", entnum);\n\t\treturn 0;\n\t} //end if\n\treturn aasworld.entities[entnum].i.modelindex;\n} //end of the function AAS_EntityModelNum\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint AAS_OriginOfMoverWithModelNum(int modelnum, vec3_t origin)\n{\n\tint i;\n\taas_entity_t *ent;\n\n\tfor (i = 0; i < aasworld.maxentities; i++)\n\t{\n\t\tent = &aasworld.entities[i];\n\t\tif (ent->i.type == ET_MOVER)\n\t\t{\n\t\t\tif (ent->i.modelindex == modelnum)\n\t\t\t{\n\t\t\t\tVectorCopy(ent->i.origin, origin);\n\t\t\t\treturn qtrue;\n\t\t\t} //end if\n\t\t} //end if\n\t} //end for\n\treturn qfalse;\n} //end of the function AAS_OriginOfMoverWithModelNum\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid AAS_EntitySize(int entnum, vec3_t mins, vec3_t maxs)\n{\n\taas_entity_t *ent;\n\n\tif (!aasworld.initialized) return;\n\n\tif (entnum < 0 || entnum >= aasworld.maxentities)\n\t{\n\t\tbotimport.Print(PRT_FATAL, \"AAS_EntitySize: entnum %d out of range\\n\", entnum);\n\t\treturn;\n\t} //end if\n\n\tent = &aasworld.entities[entnum];\n\tVectorCopy(ent->i.mins, mins);\n\tVectorCopy(ent->i.maxs, maxs);\n} //end of the function AAS_EntitySize\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid AAS_EntityBSPData(int entnum, bsp_entdata_t *entdata)\n{\n\taas_entity_t *ent;\n\n\tent = &aasworld.entities[entnum];\n\tVectorCopy(ent->i.origin, entdata->origin);\n\tVectorCopy(ent->i.angles, entdata->angles);\n\tVectorAdd(ent->i.origin, ent->i.mins, entdata->absmins);\n\tVectorAdd(ent->i.origin, ent->i.maxs, entdata->absmaxs);\n\tentdata->solid = ent->i.solid;\n\tentdata->modelnum = ent->i.modelindex - 1;\n} //end of the function AAS_EntityBSPData\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid AAS_ResetEntityLinks(void)\n{\n\tint i;\n\tfor (i = 0; i < aasworld.maxentities; i++)\n\t{\n\t\taasworld.entities[i].areas = NULL;\n\t\taasworld.entities[i].leaves = NULL;\n\t} //end for\n} //end of the function AAS_ResetEntityLinks\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid AAS_InvalidateEntities(void)\n{\n\tint i;\n\tfor (i = 0; i < aasworld.maxentities; i++)\n\t{\n\t\taasworld.entities[i].i.valid = qfalse;\n\t\taasworld.entities[i].i.number = i;\n\t} //end for\n} //end of the function AAS_InvalidateEntities\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid AAS_UnlinkInvalidEntities(void)\n{\n\tint i;\n\taas_entity_t *ent;\n\n\tfor (i = 0; i < aasworld.maxentities; i++)\n\t{\n\t\tent = &aasworld.entities[i];\n\t\tif (!ent->i.valid)\n\t\t{\n\t\t\tAAS_UnlinkFromAreas( ent->areas );\n\t\t\tent->areas = NULL;\n\t\t\tAAS_UnlinkFromBSPLeaves( ent->leaves );\n\t\t\tent->leaves = NULL;\n\t\t} //end for\n\t} //end for\n} //end of the function AAS_UnlinkInvalidEntities\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint AAS_NearestEntity(vec3_t origin, int modelindex)\n{\n\tint i, bestentnum;\n\tfloat dist, bestdist;\n\taas_entity_t *ent;\n\tvec3_t dir;\n\n\tbestentnum = 0;\n\tbestdist = 99999;\n\tfor (i = 0; i < aasworld.maxentities; i++)\n\t{\n\t\tent = &aasworld.entities[i];\n\t\tif (ent->i.modelindex != modelindex) continue;\n\t\tVectorSubtract(ent->i.origin, origin, dir);\n\t\tif (fabs(dir[0]) < 40)\n\t\t{\n\t\t\tif (fabs(dir[1]) < 40)\n\t\t\t{\n\t\t\t\tdist = VectorLength(dir);\n\t\t\t\tif (dist < bestdist)\n\t\t\t\t{\n\t\t\t\t\tbestdist = dist;\n\t\t\t\t\tbestentnum = i;\n\t\t\t\t} //end if\n\t\t\t} //end if\n\t\t} //end if\n\t} //end for\n\treturn bestentnum;\n} //end of the function AAS_NearestEntity\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint AAS_BestReachableEntityArea(int entnum)\n{\n\taas_entity_t *ent;\n\n\tent = &aasworld.entities[entnum];\n\treturn AAS_BestReachableLinkArea(ent->areas);\n} //end of the function AAS_BestReachableEntityArea\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint AAS_NextEntity(int entnum)\n{\n\tif (!aasworld.loaded) return 0;\n\n\tif (entnum < 0) entnum = -1;\n\twhile(++entnum < aasworld.maxentities)\n\t{\n\t\tif (aasworld.entities[entnum].i.valid) return entnum;\n\t} //end while\n\treturn 0;\n} //end of the function AAS_NextEntity\n"
  },
  {
    "path": "plugins/quake3/botlib/be_aas_entity.h",
    "content": "/*\n===========================================================================\nCopyright (C) 1999-2005 Id Software, Inc.\n\nThis file is part of Quake III Arena source code.\n\nQuake III Arena source code is free software; you can redistribute it\nand/or modify it under the terms of the GNU General Public License as\npublished by the Free Software Foundation; either version 2 of the License,\nor (at your option) any later version.\n\nQuake III Arena source code is distributed in the hope that it will be\nuseful, but WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Quake III Arena source code; if not, write to the Free Software\nFoundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n===========================================================================\n*/\n\n/*****************************************************************************\n * name:\t\tbe_aas_entity.h\n *\n * desc:\t\tAAS\n *\n * $Archive: /source/code/botlib/be_aas_entity.h $\n *\n *****************************************************************************/\n\n#ifdef AASINTERN\n//invalidates all entity infos\nvoid AAS_InvalidateEntities(void);\n//unlink not updated entities\nvoid AAS_UnlinkInvalidEntities(void);\n//resets the entity AAS and BSP links (sets areas and leaves pointers to NULL)\nvoid AAS_ResetEntityLinks(void);\n//updates an entity\nint AAS_UpdateEntity(int ent, bot_entitystate_t *state);\n//gives the entity data used for collision detection\nvoid AAS_EntityBSPData(int entnum, bsp_entdata_t *entdata);\n#endif //AASINTERN\n\n//returns the size of the entity bounding box in mins and maxs\nvoid AAS_EntitySize(int entnum, vec3_t mins, vec3_t maxs);\n//returns the BSP model number of the entity\nint AAS_EntityModelNum(int entnum);\n//returns the origin of an entity with the given model number\nint AAS_OriginOfMoverWithModelNum(int modelnum, vec3_t origin);\n//returns the best reachable area the entity is situated in\nint AAS_BestReachableEntityArea(int entnum);\n//returns the info of the given entity\nvoid AAS_EntityInfo(int entnum, aas_entityinfo_t *info);\n//returns the next entity\nint AAS_NextEntity(int entnum);\n//returns the origin of the entity\nvoid AAS_EntityOrigin(int entnum, vec3_t origin);\n//returns the entity type\nint AAS_EntityType(int entnum);\n//returns the model index of the entity\nint AAS_EntityModelindex(int entnum);\n\n"
  },
  {
    "path": "plugins/quake3/botlib/be_aas_file.c",
    "content": "/*\n===========================================================================\nCopyright (C) 1999-2005 Id Software, Inc.\n\nThis file is part of Quake III Arena source code.\n\nQuake III Arena source code is free software; you can redistribute it\nand/or modify it under the terms of the GNU General Public License as\npublished by the Free Software Foundation; either version 2 of the License,\nor (at your option) any later version.\n\nQuake III Arena source code is distributed in the hope that it will be\nuseful, but WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Quake III Arena source code; if not, write to the Free Software\nFoundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n===========================================================================\n*/\n\n/*****************************************************************************\n * name:\t\tbe_aas_file.c\n *\n * desc:\t\tAAS file loading/writing\n *\n * $Archive: /MissionPack/code/botlib/be_aas_file.c $\n *\n *****************************************************************************/\n\n#include \"q_shared.h\"\n#include \"l_memory.h\"\n#include \"l_script.h\"\n#include \"l_precomp.h\"\n#include \"l_struct.h\"\n#include \"l_libvar.h\"\n#include \"l_utils.h\"\n#include \"aasfile.h\"\n#include \"botlib.h\"\n#include \"be_aas.h\"\n#include \"be_aas_funcs.h\"\n#include \"be_interface.h\"\n#include \"be_aas_def.h\"\n\n//#define AASFILEDEBUG\n\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid AAS_SwapAASData(void)\n{\n\tint i, j;\n\t//bounding boxes\n\tfor (i = 0; i < aasworld.numbboxes; i++)\n\t{\n\t\taasworld.bboxes[i].presencetype = LittleLong(aasworld.bboxes[i].presencetype);\n\t\taasworld.bboxes[i].flags = LittleLong(aasworld.bboxes[i].flags);\n\t\tfor (j = 0; j < 3; j++)\n\t\t{\n\t\t\taasworld.bboxes[i].mins[j] = LittleLong(aasworld.bboxes[i].mins[j]);\n\t\t\taasworld.bboxes[i].maxs[j] = LittleLong(aasworld.bboxes[i].maxs[j]);\n\t\t} //end for\n\t} //end for\n\t//vertexes\n\tfor (i = 0; i < aasworld.numvertexes; i++)\n\t{\n\t\tfor (j = 0; j < 3; j++)\n\t\t\taasworld.vertexes[i][j] = LittleFloat(aasworld.vertexes[i][j]);\n\t} //end for\n\t//planes\n\tfor (i = 0; i < aasworld.numplanes; i++)\n\t{\n\t\tfor (j = 0; j < 3; j++)\n\t\t\taasworld.planes[i].normal[j] = LittleFloat(aasworld.planes[i].normal[j]);\n\t\taasworld.planes[i].dist = LittleFloat(aasworld.planes[i].dist);\n\t\taasworld.planes[i].type = LittleLong(aasworld.planes[i].type);\n\t} //end for\n\t//edges\n\tfor (i = 0; i < aasworld.numedges; i++)\n\t{\n\t\taasworld.edges[i].v[0] = LittleLong(aasworld.edges[i].v[0]);\n\t\taasworld.edges[i].v[1] = LittleLong(aasworld.edges[i].v[1]);\n\t} //end for\n\t//edgeindex\n\tfor (i = 0; i < aasworld.edgeindexsize; i++)\n\t{\n\t\taasworld.edgeindex[i] = LittleLong(aasworld.edgeindex[i]);\n\t} //end for\n\t//faces\n\tfor (i = 0; i < aasworld.numfaces; i++)\n\t{\n\t\taasworld.faces[i].planenum = LittleLong(aasworld.faces[i].planenum);\n\t\taasworld.faces[i].faceflags = LittleLong(aasworld.faces[i].faceflags);\n\t\taasworld.faces[i].numedges = LittleLong(aasworld.faces[i].numedges);\n\t\taasworld.faces[i].firstedge = LittleLong(aasworld.faces[i].firstedge);\n\t\taasworld.faces[i].frontarea = LittleLong(aasworld.faces[i].frontarea);\n\t\taasworld.faces[i].backarea = LittleLong(aasworld.faces[i].backarea);\n\t} //end for\n\t//face index\n\tfor (i = 0; i < aasworld.faceindexsize; i++)\n\t{\n\t\taasworld.faceindex[i] = LittleLong(aasworld.faceindex[i]);\n\t} //end for\n\t//convex areas\n\tfor (i = 0; i < aasworld.numareas; i++)\n\t{\n\t\taasworld.areas[i].areanum = LittleLong(aasworld.areas[i].areanum);\n\t\taasworld.areas[i].numfaces = LittleLong(aasworld.areas[i].numfaces);\n\t\taasworld.areas[i].firstface = LittleLong(aasworld.areas[i].firstface);\n\t\tfor (j = 0; j < 3; j++)\n\t\t{\n\t\t\taasworld.areas[i].mins[j] = LittleFloat(aasworld.areas[i].mins[j]);\n\t\t\taasworld.areas[i].maxs[j] = LittleFloat(aasworld.areas[i].maxs[j]);\n\t\t\taasworld.areas[i].center[j] = LittleFloat(aasworld.areas[i].center[j]);\n\t\t} //end for\n\t} //end for\n\t//area settings\n\tfor (i = 0; i < aasworld.numareasettings; i++)\n\t{\n\t\taasworld.areasettings[i].contents = LittleLong(aasworld.areasettings[i].contents);\n\t\taasworld.areasettings[i].areaflags = LittleLong(aasworld.areasettings[i].areaflags);\n\t\taasworld.areasettings[i].presencetype = LittleLong(aasworld.areasettings[i].presencetype);\n\t\taasworld.areasettings[i].cluster = LittleLong(aasworld.areasettings[i].cluster);\n\t\taasworld.areasettings[i].clusterareanum = LittleLong(aasworld.areasettings[i].clusterareanum);\n\t\taasworld.areasettings[i].numreachableareas = LittleLong(aasworld.areasettings[i].numreachableareas);\n\t\taasworld.areasettings[i].firstreachablearea = LittleLong(aasworld.areasettings[i].firstreachablearea);\n\t} //end for\n\t//area reachability\n\tfor (i = 0; i < aasworld.reachabilitysize; i++)\n\t{\n\t\taasworld.reachability[i].areanum = LittleLong(aasworld.reachability[i].areanum);\n\t\taasworld.reachability[i].facenum = LittleLong(aasworld.reachability[i].facenum);\n\t\taasworld.reachability[i].edgenum = LittleLong(aasworld.reachability[i].edgenum);\n\t\tfor (j = 0; j < 3; j++)\n\t\t{\n\t\t\taasworld.reachability[i].start[j] = LittleFloat(aasworld.reachability[i].start[j]);\n\t\t\taasworld.reachability[i].end[j] = LittleFloat(aasworld.reachability[i].end[j]);\n\t\t} //end for\n\t\taasworld.reachability[i].traveltype = LittleLong(aasworld.reachability[i].traveltype);\n\t\taasworld.reachability[i].traveltime = LittleShort(aasworld.reachability[i].traveltime);\n\t} //end for\n\t//nodes\n\tfor (i = 0; i < aasworld.numnodes; i++)\n\t{\n\t\taasworld.nodes[i].planenum = LittleLong(aasworld.nodes[i].planenum);\n\t\taasworld.nodes[i].children[0] = LittleLong(aasworld.nodes[i].children[0]);\n\t\taasworld.nodes[i].children[1] = LittleLong(aasworld.nodes[i].children[1]);\n\t} //end for\n\t//cluster portals\n\tfor (i = 0; i < aasworld.numportals; i++)\n\t{\n\t\taasworld.portals[i].areanum = LittleLong(aasworld.portals[i].areanum);\n\t\taasworld.portals[i].frontcluster = LittleLong(aasworld.portals[i].frontcluster);\n\t\taasworld.portals[i].backcluster = LittleLong(aasworld.portals[i].backcluster);\n\t\taasworld.portals[i].clusterareanum[0] = LittleLong(aasworld.portals[i].clusterareanum[0]);\n\t\taasworld.portals[i].clusterareanum[1] = LittleLong(aasworld.portals[i].clusterareanum[1]);\n\t} //end for\n\t//cluster portal index\n\tfor (i = 0; i < aasworld.portalindexsize; i++)\n\t{\n\t\taasworld.portalindex[i] = LittleLong(aasworld.portalindex[i]);\n\t} //end for\n\t//cluster\n\tfor (i = 0; i < aasworld.numclusters; i++)\n\t{\n\t\taasworld.clusters[i].numareas = LittleLong(aasworld.clusters[i].numareas);\n\t\taasworld.clusters[i].numreachabilityareas = LittleLong(aasworld.clusters[i].numreachabilityareas);\n\t\taasworld.clusters[i].numportals = LittleLong(aasworld.clusters[i].numportals);\n\t\taasworld.clusters[i].firstportal = LittleLong(aasworld.clusters[i].firstportal);\n\t} //end for\n} //end of the function AAS_SwapAASData\n//===========================================================================\n// dump the current loaded aas file\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid AAS_DumpAASData(void)\n{\n\taasworld.numbboxes = 0;\n\tif (aasworld.bboxes) FreeMemory(aasworld.bboxes);\n\taasworld.bboxes = NULL;\n\taasworld.numvertexes = 0;\n\tif (aasworld.vertexes) FreeMemory(aasworld.vertexes);\n\taasworld.vertexes = NULL;\n\taasworld.numplanes = 0;\n\tif (aasworld.planes) FreeMemory(aasworld.planes);\n\taasworld.planes = NULL;\n\taasworld.numedges = 0;\n\tif (aasworld.edges) FreeMemory(aasworld.edges);\n\taasworld.edges = NULL;\n\taasworld.edgeindexsize = 0;\n\tif (aasworld.edgeindex) FreeMemory(aasworld.edgeindex);\n\taasworld.edgeindex = NULL;\n\taasworld.numfaces = 0;\n\tif (aasworld.faces) FreeMemory(aasworld.faces);\n\taasworld.faces = NULL;\n\taasworld.faceindexsize = 0;\n\tif (aasworld.faceindex) FreeMemory(aasworld.faceindex);\n\taasworld.faceindex = NULL;\n\taasworld.numareas = 0;\n\tif (aasworld.areas) FreeMemory(aasworld.areas);\n\taasworld.areas = NULL;\n\taasworld.numareasettings = 0;\n\tif (aasworld.areasettings) FreeMemory(aasworld.areasettings);\n\taasworld.areasettings = NULL;\n\taasworld.reachabilitysize = 0;\n\tif (aasworld.reachability) FreeMemory(aasworld.reachability);\n\taasworld.reachability = NULL;\n\taasworld.numnodes = 0;\n\tif (aasworld.nodes) FreeMemory(aasworld.nodes);\n\taasworld.nodes = NULL;\n\taasworld.numportals = 0;\n\tif (aasworld.portals) FreeMemory(aasworld.portals);\n\taasworld.portals = NULL;\n\taasworld.numportals = 0;\n\tif (aasworld.portalindex) FreeMemory(aasworld.portalindex);\n\taasworld.portalindex = NULL;\n\taasworld.portalindexsize = 0;\n\tif (aasworld.clusters) FreeMemory(aasworld.clusters);\n\taasworld.clusters = NULL;\n\taasworld.numclusters = 0;\n\t//\n\taasworld.loaded = qfalse;\n\taasworld.initialized = qfalse;\n\taasworld.savefile = qfalse;\n} //end of the function AAS_DumpAASData\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\n#ifdef AASFILEDEBUG\nvoid AAS_FileInfo(void)\n{\n\tint i, n, optimized;\n\n\tbotimport.Print(PRT_MESSAGE, \"version = %d\\n\", AASVERSION);\n\tbotimport.Print(PRT_MESSAGE, \"numvertexes = %d\\n\", aasworld.numvertexes);\n\tbotimport.Print(PRT_MESSAGE, \"numplanes = %d\\n\", aasworld.numplanes);\n\tbotimport.Print(PRT_MESSAGE, \"numedges = %d\\n\", aasworld.numedges);\n\tbotimport.Print(PRT_MESSAGE, \"edgeindexsize = %d\\n\", aasworld.edgeindexsize);\n\tbotimport.Print(PRT_MESSAGE, \"numfaces = %d\\n\", aasworld.numfaces);\n\tbotimport.Print(PRT_MESSAGE, \"faceindexsize = %d\\n\", aasworld.faceindexsize);\n\tbotimport.Print(PRT_MESSAGE, \"numareas = %d\\n\", aasworld.numareas);\n\tbotimport.Print(PRT_MESSAGE, \"numareasettings = %d\\n\", aasworld.numareasettings);\n\tbotimport.Print(PRT_MESSAGE, \"reachabilitysize = %d\\n\", aasworld.reachabilitysize);\n\tbotimport.Print(PRT_MESSAGE, \"numnodes = %d\\n\", aasworld.numnodes);\n\tbotimport.Print(PRT_MESSAGE, \"numportals = %d\\n\", aasworld.numportals);\n\tbotimport.Print(PRT_MESSAGE, \"portalindexsize = %d\\n\", aasworld.portalindexsize);\n\tbotimport.Print(PRT_MESSAGE, \"numclusters = %d\\n\", aasworld.numclusters);\n\t//\n\tfor (n = 0, i = 0; i < aasworld.numareasettings; i++)\n\t{\n\t\tif (aasworld.areasettings[i].areaflags & AREA_GROUNDED) n++;\n\t} //end for\n\tbotimport.Print(PRT_MESSAGE, \"num grounded areas = %d\\n\", n);\n\t//\n\tbotimport.Print(PRT_MESSAGE, \"planes size %d bytes\\n\", aasworld.numplanes * sizeof(aas_plane_t));\n\tbotimport.Print(PRT_MESSAGE, \"areas size %d bytes\\n\", aasworld.numareas * sizeof(aas_area_t));\n\tbotimport.Print(PRT_MESSAGE, \"areasettings size %d bytes\\n\", aasworld.numareasettings * sizeof(aas_areasettings_t));\n\tbotimport.Print(PRT_MESSAGE, \"nodes size %d bytes\\n\", aasworld.numnodes * sizeof(aas_node_t));\n\tbotimport.Print(PRT_MESSAGE, \"reachability size %d bytes\\n\", aasworld.reachabilitysize * sizeof(aas_reachability_t));\n\tbotimport.Print(PRT_MESSAGE, \"portals size %d bytes\\n\", aasworld.numportals * sizeof(aas_portal_t));\n\tbotimport.Print(PRT_MESSAGE, \"clusters size %d bytes\\n\", aasworld.numclusters * sizeof(aas_cluster_t));\n\n\toptimized = aasworld.numplanes * sizeof(aas_plane_t) +\n\t\t\t\t\taasworld.numareas * sizeof(aas_area_t) +\n\t\t\t\t\taasworld.numareasettings * sizeof(aas_areasettings_t) +\n\t\t\t\t\taasworld.numnodes * sizeof(aas_node_t) +\n\t\t\t\t\taasworld.reachabilitysize * sizeof(aas_reachability_t) +\n\t\t\t\t\taasworld.numportals * sizeof(aas_portal_t) +\n\t\t\t\t\taasworld.numclusters * sizeof(aas_cluster_t);\n\tbotimport.Print(PRT_MESSAGE, \"optimzed size %d KB\\n\", optimized >> 10);\n} //end of the function AAS_FileInfo\n#endif //AASFILEDEBUG\n//===========================================================================\n// allocate memory and read a lump of a AAS file\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nchar *AAS_LoadAASLump(fileHandle_t fp, int offset, int length, int *lastoffset, int size)\n{\n\tchar *buf;\n\t//\n\tif (!length)\n\t{\n\t\t//just alloc a dummy\n\t\treturn (char *) GetClearedHunkMemory(size+1);\n\t} //end if\n\t//seek to the data\n\tif (offset != *lastoffset)\n\t{\n\t\tbotimport.Print(PRT_WARNING, \"AAS file not sequentially read\\n\");\n\t\tif (botimport.FS_Seek(fp, offset, FS_SEEK_SET))\n\t\t{\n\t\t\tAAS_Error(\"can't seek to aas lump\\n\");\n\t\t\tAAS_DumpAASData();\n\t\t\tbotimport.FS_FCloseFile(fp);\n\t\t\treturn NULL;\n\t\t} //end if\n\t} //end if\n\t//allocate memory\n\tbuf = (char *) GetClearedHunkMemory(length+1);\n\t//read the data\n\tif (length)\n\t{\n\t\tbotimport.FS_Read(buf, length, fp );\n\t\t*lastoffset += length;\n\t} //end if\n\treturn buf;\n} //end of the function AAS_LoadAASLump\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid AAS_DData(unsigned char *data, int size)\n{\n\tint i;\n\n\tfor (i = 0; i < size; i++)\n\t{\n\t\tdata[i] ^= ((unsigned char) i * 119) & 0xff;\n\t} //end for\n} //end of the function AAS_DData\n//===========================================================================\n// load an aas file\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint AAS_LoadAASFile(char *filename)\n{\n\tfileHandle_t fp;\n\taas_header_t header;\n\tint offset, length, lastoffset;\n\n\tbotimport.Print(PRT_MESSAGE, \"trying to load %s\\n\", filename);\n\t//dump current loaded aas file\n\tAAS_DumpAASData();\n\t//open the file\n\tbotimport.FS_FOpenFile( filename, &fp, FS_READ );\n\tif (!fp)\n\t{\n\t\tAAS_Error(\"can't open %s\\n\", filename);\n\t\treturn BLERR_CANNOTOPENAASFILE;\n\t} //end if\n\t//read the header\n\tbotimport.FS_Read(&header, sizeof(aas_header_t), fp );\n\tlastoffset = sizeof(aas_header_t);\n\t//check header identification\n\theader.ident = LittleLong(header.ident);\n\tif (header.ident != AASID)\n\t{\n\t\tAAS_Error(\"%s is not an AAS file\\n\", filename);\n\t\tbotimport.FS_FCloseFile(fp);\n\t\treturn BLERR_WRONGAASFILEID;\n\t} //end if\n\t//check the version\n\theader.version = LittleLong(header.version);\n\t//\n\tif (header.version != AASVERSION_OLD && header.version != AASVERSION)\n\t{\n\t\tAAS_Error(\"aas file %s is version %i, not %i\\n\", filename, header.version, AASVERSION);\n\t\tbotimport.FS_FCloseFile(fp);\n\t\treturn BLERR_WRONGAASFILEVERSION;\n\t} //end if\n\t//\n\tif (header.version == AASVERSION)\n\t{\n\t\tAAS_DData((unsigned char *) &header + 8, sizeof(aas_header_t) - 8);\n\t} //end if\n\t//\n\taasworld.bspchecksum = atoi(LibVarGetString( \"sv_mapChecksum\"));\n\tif (LittleLong(header.bspchecksum) != aasworld.bspchecksum)\n\t{\n\t\tAAS_Error(\"aas file %s is out of date\\n\", filename);\n\t\tbotimport.FS_FCloseFile(fp);\n\t\treturn BLERR_WRONGAASFILEVERSION;\n\t} //end if\n\t//load the lumps:\n\t//bounding boxes\n\toffset = LittleLong(header.lumps[AASLUMP_BBOXES].fileofs);\n\tlength = LittleLong(header.lumps[AASLUMP_BBOXES].filelen);\n\taasworld.bboxes = (aas_bbox_t *) AAS_LoadAASLump(fp, offset, length, &lastoffset, sizeof(aas_bbox_t));\n\taasworld.numbboxes = length / sizeof(aas_bbox_t);\n\tif (aasworld.numbboxes && !aasworld.bboxes) return BLERR_CANNOTREADAASLUMP;\n\t//vertexes\n\toffset = LittleLong(header.lumps[AASLUMP_VERTEXES].fileofs);\n\tlength = LittleLong(header.lumps[AASLUMP_VERTEXES].filelen);\n\taasworld.vertexes = (aas_vertex_t *) AAS_LoadAASLump(fp, offset, length, &lastoffset, sizeof(aas_vertex_t));\n\taasworld.numvertexes = length / sizeof(aas_vertex_t);\n\tif (aasworld.numvertexes && !aasworld.vertexes) return BLERR_CANNOTREADAASLUMP;\n\t//planes\n\toffset = LittleLong(header.lumps[AASLUMP_PLANES].fileofs);\n\tlength = LittleLong(header.lumps[AASLUMP_PLANES].filelen);\n\taasworld.planes = (aas_plane_t *) AAS_LoadAASLump(fp, offset, length, &lastoffset, sizeof(aas_plane_t));\n\taasworld.numplanes = length / sizeof(aas_plane_t);\n\tif (aasworld.numplanes && !aasworld.planes) return BLERR_CANNOTREADAASLUMP;\n\t//edges\n\toffset = LittleLong(header.lumps[AASLUMP_EDGES].fileofs);\n\tlength = LittleLong(header.lumps[AASLUMP_EDGES].filelen);\n\taasworld.edges = (aas_edge_t *) AAS_LoadAASLump(fp, offset, length, &lastoffset, sizeof(aas_edge_t));\n\taasworld.numedges = length / sizeof(aas_edge_t);\n\tif (aasworld.numedges && !aasworld.edges) return BLERR_CANNOTREADAASLUMP;\n\t//edgeindex\n\toffset = LittleLong(header.lumps[AASLUMP_EDGEINDEX].fileofs);\n\tlength = LittleLong(header.lumps[AASLUMP_EDGEINDEX].filelen);\n\taasworld.edgeindex = (aas_edgeindex_t *) AAS_LoadAASLump(fp, offset, length, &lastoffset, sizeof(aas_edgeindex_t));\n\taasworld.edgeindexsize = length / sizeof(aas_edgeindex_t);\n\tif (aasworld.edgeindexsize && !aasworld.edgeindex) return BLERR_CANNOTREADAASLUMP;\n\t//faces\n\toffset = LittleLong(header.lumps[AASLUMP_FACES].fileofs);\n\tlength = LittleLong(header.lumps[AASLUMP_FACES].filelen);\n\taasworld.faces = (aas_face_t *) AAS_LoadAASLump(fp, offset, length, &lastoffset, sizeof(aas_face_t));\n\taasworld.numfaces = length / sizeof(aas_face_t);\n\tif (aasworld.numfaces && !aasworld.faces) return BLERR_CANNOTREADAASLUMP;\n\t//faceindex\n\toffset = LittleLong(header.lumps[AASLUMP_FACEINDEX].fileofs);\n\tlength = LittleLong(header.lumps[AASLUMP_FACEINDEX].filelen);\n\taasworld.faceindex = (aas_faceindex_t *) AAS_LoadAASLump(fp, offset, length, &lastoffset, sizeof(aas_faceindex_t));\n\taasworld.faceindexsize = length / sizeof(aas_faceindex_t);\n\tif (aasworld.faceindexsize && !aasworld.faceindex) return BLERR_CANNOTREADAASLUMP;\n\t//convex areas\n\toffset = LittleLong(header.lumps[AASLUMP_AREAS].fileofs);\n\tlength = LittleLong(header.lumps[AASLUMP_AREAS].filelen);\n\taasworld.areas = (aas_area_t *) AAS_LoadAASLump(fp, offset, length, &lastoffset, sizeof(aas_area_t));\n\taasworld.numareas = length / sizeof(aas_area_t);\n\tif (aasworld.numareas && !aasworld.areas) return BLERR_CANNOTREADAASLUMP;\n\t//area settings\n\toffset = LittleLong(header.lumps[AASLUMP_AREASETTINGS].fileofs);\n\tlength = LittleLong(header.lumps[AASLUMP_AREASETTINGS].filelen);\n\taasworld.areasettings = (aas_areasettings_t *) AAS_LoadAASLump(fp, offset, length, &lastoffset, sizeof(aas_areasettings_t));\n\taasworld.numareasettings = length / sizeof(aas_areasettings_t);\n\tif (aasworld.numareasettings && !aasworld.areasettings) return BLERR_CANNOTREADAASLUMP;\n\t//reachability list\n\toffset = LittleLong(header.lumps[AASLUMP_REACHABILITY].fileofs);\n\tlength = LittleLong(header.lumps[AASLUMP_REACHABILITY].filelen);\n\taasworld.reachability = (aas_reachability_t *) AAS_LoadAASLump(fp, offset, length, &lastoffset, sizeof(aas_reachability_t));\n\taasworld.reachabilitysize = length / sizeof(aas_reachability_t);\n\tif (aasworld.reachabilitysize && !aasworld.reachability) return BLERR_CANNOTREADAASLUMP;\n\t//nodes\n\toffset = LittleLong(header.lumps[AASLUMP_NODES].fileofs);\n\tlength = LittleLong(header.lumps[AASLUMP_NODES].filelen);\n\taasworld.nodes = (aas_node_t *) AAS_LoadAASLump(fp, offset, length, &lastoffset, sizeof(aas_node_t));\n\taasworld.numnodes = length / sizeof(aas_node_t);\n\tif (aasworld.numnodes && !aasworld.nodes) return BLERR_CANNOTREADAASLUMP;\n\t//cluster portals\n\toffset = LittleLong(header.lumps[AASLUMP_PORTALS].fileofs);\n\tlength = LittleLong(header.lumps[AASLUMP_PORTALS].filelen);\n\taasworld.portals = (aas_portal_t *) AAS_LoadAASLump(fp, offset, length, &lastoffset, sizeof(aas_portal_t));\n\taasworld.numportals = length / sizeof(aas_portal_t);\n\tif (aasworld.numportals && !aasworld.portals) return BLERR_CANNOTREADAASLUMP;\n\t//cluster portal index\n\toffset = LittleLong(header.lumps[AASLUMP_PORTALINDEX].fileofs);\n\tlength = LittleLong(header.lumps[AASLUMP_PORTALINDEX].filelen);\n\taasworld.portalindex = (aas_portalindex_t *) AAS_LoadAASLump(fp, offset, length, &lastoffset, sizeof(aas_portalindex_t));\n\taasworld.portalindexsize = length / sizeof(aas_portalindex_t);\n\tif (aasworld.portalindexsize && !aasworld.portalindex) return BLERR_CANNOTREADAASLUMP;\n\t//clusters\n\toffset = LittleLong(header.lumps[AASLUMP_CLUSTERS].fileofs);\n\tlength = LittleLong(header.lumps[AASLUMP_CLUSTERS].filelen);\n\taasworld.clusters = (aas_cluster_t *) AAS_LoadAASLump(fp, offset, length, &lastoffset, sizeof(aas_cluster_t));\n\taasworld.numclusters = length / sizeof(aas_cluster_t);\n\tif (aasworld.numclusters && !aasworld.clusters) return BLERR_CANNOTREADAASLUMP;\n\t//swap everything\n\tAAS_SwapAASData();\n\t//aas file is loaded\n\taasworld.loaded = qtrue;\n\t//close the file\n\tbotimport.FS_FCloseFile(fp);\n\t//\n#ifdef AASFILEDEBUG\n\tAAS_FileInfo();\n#endif //AASFILEDEBUG\n\t//\n\treturn BLERR_NOERROR;\n} //end of the function AAS_LoadAASFile\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nstatic int AAS_WriteAASLump_offset;\n\nint AAS_WriteAASLump(fileHandle_t fp, aas_header_t *h, int lumpnum, void *data, int length)\n{\n\taas_lump_t *lump;\n\n\tlump = &h->lumps[lumpnum];\n\t\n\tlump->fileofs = LittleLong(AAS_WriteAASLump_offset);\t//LittleLong(ftell(fp));\n\tlump->filelen = LittleLong(length);\n\n\tif (length > 0)\n\t{\n\t\tbotimport.FS_Write(data, length, fp );\n\t} //end if\n\n\tAAS_WriteAASLump_offset += length;\n\n\treturn qtrue;\n} //end of the function AAS_WriteAASLump\n//===========================================================================\n// aas data is useless after writing to file because it is byte swapped\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nqboolean AAS_WriteAASFile(char *filename)\n{\n\taas_header_t header;\n\tfileHandle_t fp;\n\n\tbotimport.Print(PRT_MESSAGE, \"writing %s\\n\", filename);\n\t//swap the aas data\n\tAAS_SwapAASData();\n\t//initialize the file header\n\tCom_Memset(&header, 0, sizeof(aas_header_t));\n\theader.ident = LittleLong(AASID);\n\theader.version = LittleLong(AASVERSION);\n\theader.bspchecksum = LittleLong(aasworld.bspchecksum);\n\t//open a new file\n\tbotimport.FS_FOpenFile( filename, &fp, FS_WRITE );\n\tif (!fp)\n\t{\n\t\tbotimport.Print(PRT_ERROR, \"error opening %s\\n\", filename);\n\t\treturn qfalse;\n\t} //end if\n\t//write the header\n\tbotimport.FS_Write(&header, sizeof(aas_header_t), fp);\n\tAAS_WriteAASLump_offset = sizeof(aas_header_t);\n\t//add the data lumps to the file\n\tif (!AAS_WriteAASLump(fp, &header, AASLUMP_BBOXES, aasworld.bboxes,\n\t\taasworld.numbboxes * sizeof(aas_bbox_t))) return qfalse;\n\tif (!AAS_WriteAASLump(fp, &header, AASLUMP_VERTEXES, aasworld.vertexes,\n\t\taasworld.numvertexes * sizeof(aas_vertex_t))) return qfalse;\n\tif (!AAS_WriteAASLump(fp, &header, AASLUMP_PLANES, aasworld.planes,\n\t\taasworld.numplanes * sizeof(aas_plane_t))) return qfalse;\n\tif (!AAS_WriteAASLump(fp, &header, AASLUMP_EDGES, aasworld.edges,\n\t\taasworld.numedges * sizeof(aas_edge_t))) return qfalse;\n\tif (!AAS_WriteAASLump(fp, &header, AASLUMP_EDGEINDEX, aasworld.edgeindex,\n\t\taasworld.edgeindexsize * sizeof(aas_edgeindex_t))) return qfalse;\n\tif (!AAS_WriteAASLump(fp, &header, AASLUMP_FACES, aasworld.faces,\n\t\taasworld.numfaces * sizeof(aas_face_t))) return qfalse;\n\tif (!AAS_WriteAASLump(fp, &header, AASLUMP_FACEINDEX, aasworld.faceindex,\n\t\taasworld.faceindexsize * sizeof(aas_faceindex_t))) return qfalse;\n\tif (!AAS_WriteAASLump(fp, &header, AASLUMP_AREAS, aasworld.areas,\n\t\taasworld.numareas * sizeof(aas_area_t))) return qfalse;\n\tif (!AAS_WriteAASLump(fp, &header, AASLUMP_AREASETTINGS, aasworld.areasettings,\n\t\taasworld.numareasettings * sizeof(aas_areasettings_t))) return qfalse;\n\tif (!AAS_WriteAASLump(fp, &header, AASLUMP_REACHABILITY, aasworld.reachability,\n\t\taasworld.reachabilitysize * sizeof(aas_reachability_t))) return qfalse;\n\tif (!AAS_WriteAASLump(fp, &header, AASLUMP_NODES, aasworld.nodes,\n\t\taasworld.numnodes * sizeof(aas_node_t))) return qfalse;\n\tif (!AAS_WriteAASLump(fp, &header, AASLUMP_PORTALS, aasworld.portals,\n\t\taasworld.numportals * sizeof(aas_portal_t))) return qfalse;\n\tif (!AAS_WriteAASLump(fp, &header, AASLUMP_PORTALINDEX, aasworld.portalindex,\n\t\taasworld.portalindexsize * sizeof(aas_portalindex_t))) return qfalse;\n\tif (!AAS_WriteAASLump(fp, &header, AASLUMP_CLUSTERS, aasworld.clusters,\n\t\taasworld.numclusters * sizeof(aas_cluster_t))) return qfalse;\n\t//rewrite the header with the added lumps\n\tbotimport.FS_Seek(fp, 0, FS_SEEK_SET);\n\tAAS_DData((unsigned char *) &header + 8, sizeof(aas_header_t) - 8);\n\tbotimport.FS_Write(&header, sizeof(aas_header_t), fp);\n\t//close the file\n\tbotimport.FS_FCloseFile(fp);\n\treturn qtrue;\n} //end of the function AAS_WriteAASFile\n"
  },
  {
    "path": "plugins/quake3/botlib/be_aas_file.h",
    "content": "/*\n===========================================================================\nCopyright (C) 1999-2005 Id Software, Inc.\n\nThis file is part of Quake III Arena source code.\n\nQuake III Arena source code is free software; you can redistribute it\nand/or modify it under the terms of the GNU General Public License as\npublished by the Free Software Foundation; either version 2 of the License,\nor (at your option) any later version.\n\nQuake III Arena source code is distributed in the hope that it will be\nuseful, but WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Quake III Arena source code; if not, write to the Free Software\nFoundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n===========================================================================\n*/\n\n/*****************************************************************************\n * name:\t\tbe_aas_file.h\n *\n * desc:\t\tAAS\n *\n * $Archive: /source/code/botlib/be_aas_file.h $\n *\n *****************************************************************************/\n\n#ifdef AASINTERN\n//loads the AAS file with the given name\nint AAS_LoadAASFile(char *filename);\n//writes an AAS file with the given name\nqboolean AAS_WriteAASFile(char *filename);\n//dumps the loaded AAS data\nvoid AAS_DumpAASData(void);\n//print AAS file information\nvoid AAS_FileInfo(void);\n#endif //AASINTERN\n\n"
  },
  {
    "path": "plugins/quake3/botlib/be_aas_funcs.h",
    "content": "/*\n===========================================================================\nCopyright (C) 1999-2005 Id Software, Inc.\n\nThis file is part of Quake III Arena source code.\n\nQuake III Arena source code is free software; you can redistribute it\nand/or modify it under the terms of the GNU General Public License as\npublished by the Free Software Foundation; either version 2 of the License,\nor (at your option) any later version.\n\nQuake III Arena source code is distributed in the hope that it will be\nuseful, but WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Quake III Arena source code; if not, write to the Free Software\nFoundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n===========================================================================\n*/\n\n/*****************************************************************************\n * name:\t\tbe_aas_funcs.h\n *\n * desc:\t\tAAS\n *\n * $Archive: /source/code/botlib/be_aas_funcs.h $\n *\n *****************************************************************************/\n\n#ifndef BSPCINCLUDE\n\n#include \"be_aas_main.h\"\n#include \"be_aas_entity.h\"\n#include \"be_aas_sample.h\"\n#include \"be_aas_cluster.h\"\n#include \"be_aas_reach.h\"\n#include \"be_aas_route.h\"\n#include \"be_aas_routealt.h\"\n#include \"be_aas_debug.h\"\n#include \"be_aas_file.h\"\n#include \"be_aas_optimize.h\"\n#include \"be_aas_bsp.h\"\n#include \"be_aas_move.h\"\n\n#endif //BSPCINCLUDE\n"
  },
  {
    "path": "plugins/quake3/botlib/be_aas_main.c",
    "content": "/*\n===========================================================================\nCopyright (C) 1999-2005 Id Software, Inc.\n\nThis file is part of Quake III Arena source code.\n\nQuake III Arena source code is free software; you can redistribute it\nand/or modify it under the terms of the GNU General Public License as\npublished by the Free Software Foundation; either version 2 of the License,\nor (at your option) any later version.\n\nQuake III Arena source code is distributed in the hope that it will be\nuseful, but WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Quake III Arena source code; if not, write to the Free Software\nFoundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n===========================================================================\n*/\n\n/*****************************************************************************\n * name:\t\tbe_aas_main.c\n *\n * desc:\t\tAAS\n *\n * $Archive: /MissionPack/code/botlib/be_aas_main.c $\n *\n *****************************************************************************/\n\n#include \"q_shared.h\"\n#include \"l_memory.h\"\n#include \"l_libvar.h\"\n#include \"l_utils.h\"\n#include \"l_script.h\"\n#include \"l_precomp.h\"\n#include \"l_struct.h\"\n#include \"l_log.h\"\n#include \"aasfile.h\"\n#include \"botlib.h\"\n#include \"be_aas.h\"\n#include \"be_aas_funcs.h\"\n#include \"be_interface.h\"\n#include \"be_aas_def.h\"\n\n#ifdef _WIN64\n        #ifdef FTE_SDL\n                #define vsnprintf linuxlike_vsnprintf\n        #endif\n#endif\n\naas_t aasworld;\n\nlibvar_t *saveroutingcache;\n\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid QDECL AAS_Error(char *fmt, ...)\n{\n\tchar str[1024];\n\tva_list arglist;\n\n\tva_start(arglist, fmt);\n\tvsnprintf(str, sizeof(str), fmt, arglist);\n\tva_end(arglist);\n\tbotimport.Print(PRT_FATAL, \"%s\", str);\n} //end of the function AAS_Error\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nchar *AAS_StringFromIndex(char *indexname, char *stringindex[], int numindexes, int index)\n{\n\tif (!aasworld.indexessetup)\n\t{\n\t\tbotimport.Print(PRT_ERROR, \"%s: index %d not setup\\n\", indexname, index);\n\t\treturn \"\";\n\t} //end if\n\tif (index < 0 || index >= numindexes)\n\t{\n\t\tbotimport.Print(PRT_ERROR, \"%s: index %d out of range\\n\", indexname, index);\n\t\treturn \"\";\n\t} //end if\n\tif (!stringindex[index])\n\t{\n\t\tif (index)\n\t\t{\n\t\t\tbotimport.Print(PRT_ERROR, \"%s: reference to unused index %d\\n\", indexname, index);\n\t\t} //end if\n\t\treturn \"\";\n\t} //end if\n\treturn stringindex[index];\n} //end of the function AAS_StringFromIndex\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint AAS_IndexFromString(char *indexname, char *stringindex[], int numindexes, char *string)\n{\n\tint i;\n\tif (!aasworld.indexessetup)\n\t{\n\t\tbotimport.Print(PRT_ERROR, \"%s: index not setup \\\"%s\\\"\\n\", indexname, string);\n\t\treturn 0;\n\t} //end if\n\tfor (i = 0; i < numindexes; i++)\n\t{\n\t\tif (!stringindex[i]) continue;\n\t\tif (!Q_stricmp(stringindex[i], string)) return i;\n\t} //end for\n\treturn 0;\n} //end of the function AAS_IndexFromString\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nchar *AAS_ModelFromIndex(int index)\n{\n\treturn AAS_StringFromIndex(\"ModelFromIndex\", &aasworld.configstrings[CS_MODELS], MAX_MODELS, index);\n} //end of the function AAS_ModelFromIndex\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint AAS_IndexFromModel(char *modelname)\n{\n\treturn AAS_IndexFromString(\"IndexFromModel\", &aasworld.configstrings[CS_MODELS], MAX_MODELS, modelname);\n} //end of the function AAS_IndexFromModel\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid AAS_UpdateStringIndexes(int numconfigstrings, char *configstrings[])\n{\n\tint i;\n\t//set string pointers and copy the strings\n\tfor (i = 0; i < numconfigstrings; i++)\n\t{\n\t\tif (configstrings[i])\n\t\t{\n\t\t\t//if (aasworld.configstrings[i]) FreeMemory(aasworld.configstrings[i]);\n\t\t\taasworld.configstrings[i] = (char *) GetMemory(strlen(configstrings[i]) + 1);\n\t\t\tstrcpy(aasworld.configstrings[i], configstrings[i]);\n\t\t} //end if\n\t} //end for\n\taasworld.indexessetup = qtrue;\n} //end of the function AAS_UpdateStringIndexes\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint AAS_Loaded(void)\n{\n\treturn aasworld.loaded;\n} //end of the function AAS_Loaded\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint AAS_Initialized(void)\n{\n\treturn aasworld.initialized;\n} //end of the function AAS_Initialized\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid AAS_SetInitialized(void)\n{\n\taasworld.initialized = qtrue;\n\tbotimport.Print(PRT_MESSAGE, \"AAS initialized.\\n\");\n#ifdef DEBUG\n\t//create all the routing cache\n\t//AAS_CreateAllRoutingCache();\n\t//\n\t//AAS_RoutingInfo();\n#endif\n} //end of the function AAS_SetInitialized\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid AAS_ContinueInit(float time)\n{\n\t//if no AAS file loaded\n\tif (!aasworld.loaded) return;\n\t//if AAS is already initialized\n\tif (aasworld.initialized) return;\n\t//calculate reachability, if not finished return\n\tif (AAS_ContinueInitReachability(time)) return;\n\t//initialize clustering for the new map\n\tAAS_InitClustering();\n\t//if reachability has been calculated and an AAS file should be written\n\t//or there is a forced data optimization\n\tif (aasworld.savefile || ((int)LibVarGetValue(\"forcewrite\")))\n\t{\n\t\t//optimize the AAS data\n\t\tif ((int)LibVarValue(\"aasoptimize\", \"0\")) AAS_Optimize();\n\t\t//save the AAS file\n\t\tif (AAS_WriteAASFile(aasworld.filename))\n\t\t{\n\t\t\tbotimport.Print(PRT_MESSAGE, \"%s written successfully\\n\", aasworld.filename);\n\t\t} //end if\n\t\telse\n\t\t{\n\t\t\tbotimport.Print(PRT_ERROR, \"couldn't write %s\\n\", aasworld.filename);\n\t\t} //end else\n\t} //end if\n\t//initialize the routing\n\tAAS_InitRouting();\n\t//at this point AAS is initialized\n\tAAS_SetInitialized();\n} //end of the function AAS_ContinueInit\n//===========================================================================\n// called at the start of every frame\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint AAS_StartFrame(float time)\n{\n\taasworld.time = time;\n\t//unlink all entities that were not updated last frame\n\tAAS_UnlinkInvalidEntities();\n\t//invalidate the entities\n\tAAS_InvalidateEntities();\n\t//initialize AAS\n\tAAS_ContinueInit(time);\n\t//\n\taasworld.frameroutingupdates = 0;\n\t//\n\tif (botDeveloper)\n\t{\n\t\tif (LibVarGetValue(\"showcacheupdates\"))\n\t\t{\n\t\t\tAAS_RoutingInfo();\n\t\t\tLibVarSet(\"showcacheupdates\", \"0\");\n\t\t} //end if\n\t\tif (LibVarGetValue(\"showmemoryusage\"))\n\t\t{\n\t\t\tPrintUsedMemorySize();\n\t\t\tLibVarSet(\"showmemoryusage\", \"0\");\n\t\t} //end if\n\t\tif (LibVarGetValue(\"memorydump\"))\n\t\t{\n\t\t\tPrintMemoryLabels();\n\t\t\tLibVarSet(\"memorydump\", \"0\");\n\t\t} //end if\n\t} //end if\n\t//\n\tif (saveroutingcache->value)\n\t{\n\t\tAAS_WriteRouteCache();\n\t\tLibVarSet(\"saveroutingcache\", \"0\");\n\t} //end if\n\t//\n\taasworld.numframes++;\n\treturn BLERR_NOERROR;\n} //end of the function AAS_StartFrame\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nfloat AAS_Time(void)\n{\n\treturn aasworld.time;\n} //end of the function AAS_Time\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid AAS_ProjectPointOntoVector( vec3_t point, vec3_t vStart, vec3_t vEnd, vec3_t vProj )\n{\n\tvec3_t pVec, vec;\n\n\tVectorSubtract( point, vStart, pVec );\n\tVectorSubtract( vEnd, vStart, vec );\n\tVectorNormalize( vec );\n\t// project onto the directional vector for this segment\n\tVectorMA( vStart, DotProduct( pVec, vec ), vec, vProj );\n} //end of the function AAS_ProjectPointOntoVector\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint AAS_LoadFiles(const char *mapname)\n{\n\tint errnum;\n\tchar aasfile[MAX_PATH];\n//\tchar bspfile[MAX_PATH];\n\n\tstrcpy(aasworld.mapname, mapname);\n\t//NOTE: first reset the entity links into the AAS areas and BSP leaves\n\t// the AAS link heap and BSP link heap are reset after respectively the\n\t// AAS file and BSP file are loaded\n\tAAS_ResetEntityLinks();\n\t// load bsp info\n\tAAS_LoadBSPFile();\n\n\t//load the aas file\n\tCom_sprintf(aasfile, MAX_PATH, \"maps/%s.aas\", mapname);\n\terrnum = AAS_LoadAASFile(aasfile);\n\tif (errnum != BLERR_NOERROR)\n\t\treturn errnum;\n\n\tbotimport.Print(PRT_MESSAGE, \"loaded %s\\n\", aasfile);\n\tstrncpy(aasworld.filename, aasfile, MAX_PATH);\n\treturn BLERR_NOERROR;\n} //end of the function AAS_LoadFiles\n//===========================================================================\n// called everytime a map changes\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint AAS_LoadMap(const char *mapname)\n{\n\tint\terrnum;\n\n\t//if no mapname is provided then the string indexes are updated\n\tif (!mapname)\n\t{\n\t\treturn 0;\n\t} //end if\n\t//\n\taasworld.initialized = qfalse;\n\t//NOTE: free the routing caches before loading a new map because\n\t// to free the caches the old number of areas, number of clusters\n\t// and number of areas in a clusters must be available\n\tAAS_FreeRoutingCaches();\n\t//load the map\n\terrnum = AAS_LoadFiles(mapname);\n\tif (errnum != BLERR_NOERROR)\n\t{\n\t\taasworld.loaded = qfalse;\n\t\treturn errnum;\n\t} //end if\n\t//\n\tAAS_InitSettings();\n\t//initialize the AAS link heap for the new map\n\tAAS_InitAASLinkHeap();\n\t//initialize the AAS linked entities for the new map\n\tAAS_InitAASLinkedEntities();\n\t//initialize reachability for the new map\n\tAAS_InitReachability();\n\t//initialize the alternative routing\n\tAAS_InitAlternativeRouting();\n\t//everything went ok\n\treturn 0;\n} //end of the function AAS_LoadMap\n//===========================================================================\n// called when the library is first loaded\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint AAS_Setup(void)\n{\n\taasworld.maxclients = (int) LibVarValue(\"maxclients\", \"128\");\n\taasworld.maxentities = (int) LibVarValue(\"maxentities\", \"1024\");\n\t// as soon as it's set to 1 the routing cache will be saved\n\tsaveroutingcache = LibVar(\"saveroutingcache\", \"0\");\n\t//allocate memory for the entities\n\tif (aasworld.entities) FreeMemory(aasworld.entities);\n\taasworld.entities = (aas_entity_t *) GetClearedHunkMemory(aasworld.maxentities * sizeof(aas_entity_t));\n\t//invalidate all the entities\n\tAAS_InvalidateEntities();\n\t//force some recalculations\n\t//LibVarSet(\"forceclustering\", \"1\");\t\t\t//force clustering calculation\n\t//LibVarSet(\"forcereachability\", \"1\");\t\t//force reachability calculation\n\taasworld.numframes = 0;\n\treturn BLERR_NOERROR;\n} //end of the function AAS_Setup\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid AAS_Shutdown(void)\n{\n\tAAS_ShutdownAlternativeRouting();\n\t//\n\tAAS_DumpBSPData();\n\t//free routing caches\n\tAAS_FreeRoutingCaches();\n\t//free aas link heap\n\tAAS_FreeAASLinkHeap();\n\t//free aas linked entities\n\tAAS_FreeAASLinkedEntities();\n\t//free the aas data\n\tAAS_DumpAASData();\n\t//free the entities\n\tif (aasworld.entities) FreeMemory(aasworld.entities);\n\t//clear the aasworld structure\n\tCom_Memset(&aasworld, 0, sizeof(aas_t));\n\t//aas has not been initialized\n\taasworld.initialized = qfalse;\n\t//NOTE: as soon as a new .bsp file is loaded the .bsp file memory is\n\t// freed an reallocated, so there's no need to free that memory here\n\t//print shutdown\n\tbotimport.Print(PRT_MESSAGE, \"AAS shutdown.\\n\");\n} //end of the function AAS_Shutdown\n"
  },
  {
    "path": "plugins/quake3/botlib/be_aas_main.h",
    "content": "/*\n===========================================================================\nCopyright (C) 1999-2005 Id Software, Inc.\n\nThis file is part of Quake III Arena source code.\n\nQuake III Arena source code is free software; you can redistribute it\nand/or modify it under the terms of the GNU General Public License as\npublished by the Free Software Foundation; either version 2 of the License,\nor (at your option) any later version.\n\nQuake III Arena source code is distributed in the hope that it will be\nuseful, but WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Quake III Arena source code; if not, write to the Free Software\nFoundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n===========================================================================\n*/\n\n/*****************************************************************************\n * name:\t\tbe_aas_main.h\n *\n * desc:\t\tAAS\n *\n * $Archive: /source/code/botlib/be_aas_main.h $\n *\n *****************************************************************************/\n\n#ifdef AASINTERN\n\nextern aas_t aasworld;\n\n//AAS error message\nvoid QDECL AAS_Error(char *fmt, ...);\n//set AAS initialized\nvoid AAS_SetInitialized(void);\n//setup AAS with the given number of entities and clients\nint AAS_Setup(void);\n//shutdown AAS\nvoid AAS_Shutdown(void);\n//start a new map\nint AAS_LoadMap(const char *mapname);\n//start a new time frame\nint AAS_StartFrame(float time);\n#endif //AASINTERN\n\n//returns true if AAS is initialized\nint AAS_Initialized(void);\n//returns true if the AAS file is loaded\nint AAS_Loaded(void);\n//returns the model name from the given index\nchar *AAS_ModelFromIndex(int index);\n//returns the index from the given model name\nint AAS_IndexFromModel(char *modelname);\n//returns the current time\nfloat AAS_Time(void);\n//\nvoid AAS_ProjectPointOntoVector( vec3_t point, vec3_t vStart, vec3_t vEnd, vec3_t vProj );\n"
  },
  {
    "path": "plugins/quake3/botlib/be_aas_move.c",
    "content": "/*\n===========================================================================\nCopyright (C) 1999-2005 Id Software, Inc.\n\nThis file is part of Quake III Arena source code.\n\nQuake III Arena source code is free software; you can redistribute it\nand/or modify it under the terms of the GNU General Public License as\npublished by the Free Software Foundation; either version 2 of the License,\nor (at your option) any later version.\n\nQuake III Arena source code is distributed in the hope that it will be\nuseful, but WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Quake III Arena source code; if not, write to the Free Software\nFoundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n===========================================================================\n*/\n\n/*****************************************************************************\n * name:\t\tbe_aas_move.c\n *\n * desc:\t\tAAS\n *\n * $Archive: /MissionPack/code/botlib/be_aas_move.c $\n *\n *****************************************************************************/\n\n#include \"q_shared.h\"\n#include \"l_memory.h\"\n#include \"l_script.h\"\n#include \"l_precomp.h\"\n#include \"l_struct.h\"\n#include \"l_libvar.h\"\n#include \"aasfile.h\"\n#include \"botlib.h\"\n#include \"be_aas.h\"\n#include \"be_aas_funcs.h\"\n#include \"be_aas_def.h\"\n\nextern botlib_import_t botimport;\n\naas_settings_t aassettings;\n\n//#define AAS_MOVE_DEBUG\n\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint AAS_DropToFloor(vec3_t origin, vec3_t mins, vec3_t maxs)\n{\n\tvec3_t end;\n\tbsp_trace_t trace;\n\n\tVectorCopy(origin, end);\n\tend[2] -= 100;\n\ttrace = AAS_Trace(origin, mins, maxs, end, 0, CONTENTS_SOLID);\n\tif (trace.startsolid) return qfalse;\n\tVectorCopy(trace.endpos, origin);\n\treturn qtrue;\n} //end of the function AAS_DropToFloor\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid AAS_InitSettings(void)\n{\n\taassettings.phys_gravitydirection[0]\t= 0;\n\taassettings.phys_gravitydirection[1]\t= 0;\n\taassettings.phys_gravitydirection[2]\t= -1;\n\taassettings.phys_friction\t\t\t\t= LibVarValue(\"phys_friction\", \"6\");\n\taassettings.phys_stopspeed\t\t\t\t= LibVarValue(\"phys_stopspeed\", \"100\");\n\taassettings.phys_gravity\t\t\t\t= LibVarValue(\"phys_gravity\", \"800\");\n\taassettings.phys_waterfriction\t\t\t= LibVarValue(\"phys_waterfriction\", \"1\");\n\taassettings.phys_watergravity\t\t\t= LibVarValue(\"phys_watergravity\", \"400\");\n\taassettings.phys_maxvelocity\t\t\t= LibVarValue(\"phys_maxvelocity\", \"320\");\n\taassettings.phys_maxwalkvelocity\t\t= LibVarValue(\"phys_maxwalkvelocity\", \"320\");\n\taassettings.phys_maxcrouchvelocity\t\t= LibVarValue(\"phys_maxcrouchvelocity\", \"100\");\n\taassettings.phys_maxswimvelocity\t\t= LibVarValue(\"phys_maxswimvelocity\", \"150\");\n\taassettings.phys_walkaccelerate\t\t\t= LibVarValue(\"phys_walkaccelerate\", \"10\");\n\taassettings.phys_airaccelerate\t\t\t= LibVarValue(\"phys_airaccelerate\", \"1\");\n\taassettings.phys_swimaccelerate\t\t\t= LibVarValue(\"phys_swimaccelerate\", \"4\");\n\taassettings.phys_maxstep\t\t\t\t= LibVarValue(\"phys_maxstep\", \"19\");\n\taassettings.phys_maxsteepness\t\t\t= LibVarValue(\"phys_maxsteepness\", \"0.7\");\n\taassettings.phys_maxwaterjump\t\t\t= LibVarValue(\"phys_maxwaterjump\", \"18\");\n\taassettings.phys_maxbarrier\t\t\t\t= LibVarValue(\"phys_maxbarrier\", \"33\");\n\taassettings.phys_jumpvel\t\t\t\t= LibVarValue(\"phys_jumpvel\", \"270\");\n\taassettings.phys_falldelta5\t\t\t\t= LibVarValue(\"phys_falldelta5\", \"40\");\n\taassettings.phys_falldelta10\t\t\t= LibVarValue(\"phys_falldelta10\", \"60\");\n\taassettings.rs_waterjump\t\t\t\t= LibVarValue(\"rs_waterjump\", \"400\");\n\taassettings.rs_teleport\t\t\t\t\t= LibVarValue(\"rs_teleport\", \"50\");\n\taassettings.rs_barrierjump\t\t\t\t= LibVarValue(\"rs_barrierjump\", \"100\");\n\taassettings.rs_startcrouch\t\t\t\t= LibVarValue(\"rs_startcrouch\", \"300\");\n\taassettings.rs_startgrapple\t\t\t\t= LibVarValue(\"rs_startgrapple\", \"500\");\n\taassettings.rs_startwalkoffledge\t\t= LibVarValue(\"rs_startwalkoffledge\", \"70\");\n\taassettings.rs_startjump\t\t\t\t= LibVarValue(\"rs_startjump\", \"300\");\n\taassettings.rs_rocketjump\t\t\t\t= LibVarValue(\"rs_rocketjump\", \"500\");\n\taassettings.rs_bfgjump\t\t\t\t\t= LibVarValue(\"rs_bfgjump\", \"500\");\n\taassettings.rs_jumppad\t\t\t\t\t= LibVarValue(\"rs_jumppad\", \"250\");\n\taassettings.rs_aircontrolledjumppad\t\t= LibVarValue(\"rs_aircontrolledjumppad\", \"300\");\n\taassettings.rs_funcbob\t\t\t\t\t= LibVarValue(\"rs_funcbob\", \"300\");\n\taassettings.rs_startelevator\t\t\t= LibVarValue(\"rs_startelevator\", \"50\");\n\taassettings.rs_falldamage5\t\t\t\t= LibVarValue(\"rs_falldamage5\", \"300\");\n\taassettings.rs_falldamage10\t\t\t\t= LibVarValue(\"rs_falldamage10\", \"500\");\n\taassettings.rs_maxfallheight\t\t\t= LibVarValue(\"rs_maxfallheight\", \"0\");\n\taassettings.rs_maxjumpfallheight\t\t= LibVarValue(\"rs_maxjumpfallheight\", \"450\");\n} //end of the function AAS_InitSettings\n//===========================================================================\n// returns qtrue if the bot is against a ladder\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint AAS_AgainstLadder(vec3_t origin)\n{\n\tint areanum, i, facenum, side;\n\tvec3_t org;\n\taas_plane_t *plane;\n\taas_face_t *face;\n\taas_area_t *area;\n\n\tVectorCopy(origin, org);\n\tareanum = AAS_PointAreaNum(org);\n\tif (!areanum)\n\t{\n\t\torg[0] += 1;\n\t\tareanum = AAS_PointAreaNum(org);\n\t\tif (!areanum)\n\t\t{\n\t\t\torg[1] += 1;\n\t\t\tareanum = AAS_PointAreaNum(org);\n\t\t\tif (!areanum)\n\t\t\t{\n\t\t\t\torg[0] -= 2;\n\t\t\t\tareanum = AAS_PointAreaNum(org);\n\t\t\t\tif (!areanum)\n\t\t\t\t{\n\t\t\t\t\torg[1] -= 2;\n\t\t\t\t\tareanum = AAS_PointAreaNum(org);\n\t\t\t\t} //end if\n\t\t\t} //end if\n\t\t} //end if\n\t} //end if\n\t//if in solid... wrrr shouldn't happen\n\tif (!areanum) return qfalse;\n\t//if not in a ladder area\n\tif (!(aasworld.areasettings[areanum].areaflags & AREA_LADDER)) return qfalse;\n\t//if a crouch only area\n\tif (!(aasworld.areasettings[areanum].presencetype & PRESENCE_NORMAL)) return qfalse;\n\t//\n\tarea = &aasworld.areas[areanum];\n\tfor (i = 0; i < area->numfaces; i++)\n\t{\n\t\tfacenum = aasworld.faceindex[area->firstface + i];\n\t\tside = facenum < 0;\n\t\tface = &aasworld.faces[abs(facenum)];\n\t\t//if the face isn't a ladder face\n\t\tif (!(face->faceflags & FACE_LADDER)) continue;\n\t\t//get the plane the face is in\n\t\tplane = &aasworld.planes[face->planenum ^ side];\n\t\t//if the origin is pretty close to the plane\n\t\tif (fabs(DotProduct(plane->normal, origin) - plane->dist) < 3)\n\t\t{\n\t\t\tif (AAS_PointInsideFace(abs(facenum), origin, 0.1f)) return qtrue;\n\t\t} //end if\n\t} //end for\n\treturn qfalse;\n} //end of the function AAS_AgainstLadder\n//===========================================================================\n// returns qtrue if the bot is on the ground\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint AAS_OnGround(vec3_t origin, int presencetype, int passent)\n{\n\taas_trace_t trace;\n\tvec3_t end, up = {0, 0, 1};\n\taas_plane_t *plane;\n\n\tVectorCopy(origin, end);\n\tend[2] -= 10;\n\n\ttrace = AAS_TraceClientBBox(origin, end, presencetype, passent);\n\n\t//if in solid\n\tif (trace.startsolid) return qfalse;\n\t//if nothing hit at all\n\tif (trace.fraction >= 1.0) return qfalse;\n\t//if too far from the hit plane\n\tif (origin[2] - trace.endpos[2] > 10) return qfalse;\n\t//check if the plane isn't too steep\n\tplane = AAS_PlaneFromNum(trace.planenum);\n\tif (DotProduct(plane->normal, up) < aassettings.phys_maxsteepness) return qfalse;\n\t//the bot is on the ground\n\treturn qtrue;\n} //end of the function AAS_OnGround\n//===========================================================================\n// returns qtrue if a bot at the given position is swimming\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint AAS_Swimming(vec3_t origin)\n{\n\tvec3_t testorg;\n\n\tVectorCopy(origin, testorg);\n\ttestorg[2] -= 2;\n\tif (AAS_PointContents(testorg) & (CONTENTS_LAVA|CONTENTS_SLIME|CONTENTS_WATER)) return qtrue;\n\treturn qfalse;\n} //end of the function AAS_Swimming\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nstatic vec3_t VEC_UP\t\t\t= {0, -1,  0};\nstatic vec3_t MOVEDIR_UP\t\t= {0,  0,  1};\nstatic vec3_t VEC_DOWN\t\t= {0, -2,  0};\nstatic vec3_t MOVEDIR_DOWN\t= {0,  0, -1};\n\nvoid AAS_SetMovedir(vec3_t angles, vec3_t movedir)\n{\n\tif (VectorCompare(angles, VEC_UP))\n\t{\n\t\tVectorCopy(MOVEDIR_UP, movedir);\n\t} //end if\n\telse if (VectorCompare(angles, VEC_DOWN))\n\t{\n\t\tVectorCopy(MOVEDIR_DOWN, movedir);\n\t} //end else if\n\telse\n\t{\n\t\tAngleVectors(angles, movedir, NULL, NULL);\n\t} //end else\n} //end of the function AAS_SetMovedir\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid AAS_JumpReachRunStart(aas_reachability_t *reach, vec3_t runstart)\n{\n\tvec3_t hordir, start, cmdmove;\n\taas_clientmove_t move;\n\n\t//\n\thordir[0] = reach->start[0] - reach->end[0];\n\thordir[1] = reach->start[1] - reach->end[1];\n\thordir[2] = 0;\n\tVectorNormalize(hordir);\n\t//start point\n\tVectorCopy(reach->start, start);\n\tstart[2] += 1;\n\t//get command movement\n\tVectorScale(hordir, 400, cmdmove);\n\t//\n\tAAS_PredictClientMovement(&move, -1, start, PRESENCE_NORMAL, qtrue,\n\t\t\t\t\t\t\t\tvec3_origin, cmdmove, 1, 2, 0.1f,\n\t\t\t\t\t\t\t\tSE_ENTERWATER|SE_ENTERSLIME|SE_ENTERLAVA|\n\t\t\t\t\t\t\t\tSE_HITGROUNDDAMAGE|SE_GAP, 0, qfalse);\n\tVectorCopy(move.endpos, runstart);\n\t//don't enter slime or lava and don't fall from too high\n\tif (move.stopevent & (SE_ENTERSLIME|SE_ENTERLAVA|SE_HITGROUNDDAMAGE))\n\t{\n\t\tVectorCopy(start, runstart);\n\t} //end if\n} //end of the function AAS_JumpReachRunStart\n//===========================================================================\n// returns the Z velocity when rocket jumping at the origin\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nfloat AAS_WeaponJumpZVelocity(vec3_t origin, float radiusdamage)\n{\n\tvec3_t kvel, v, start, end, forward, right, viewangles, dir;\n\tfloat\tmass, knockback, points;\n\tvec3_t rocketoffset = {8, 8, -8};\n\tvec3_t botmins = {-16, -16, -24};\n\tvec3_t botmaxs = {16, 16, 32};\n\tbsp_trace_t bsptrace;\n\n\t//look down (90 degrees)\n\tviewangles[PITCH] = 90;\n\tviewangles[YAW] = 0;\n\tviewangles[ROLL] = 0;\n\t//get the start point shooting from\n\tVectorCopy(origin, start);\n\tstart[2] += 8; //view offset Z\n\tAngleVectors(viewangles, forward, right, NULL);\n\tstart[0] += forward[0] * rocketoffset[0] + right[0] * rocketoffset[1];\n\tstart[1] += forward[1] * rocketoffset[0] + right[1] * rocketoffset[1];\n\tstart[2] += forward[2] * rocketoffset[0] + right[2] * rocketoffset[1] + rocketoffset[2];\n\t//end point of the trace\n\tVectorMA(start, 500, forward, end);\n\t//trace a line to get the impact point\n\tbsptrace = AAS_Trace(start, NULL, NULL, end, 1, CONTENTS_SOLID);\n\t//calculate the damage the bot will get from the rocket impact\n\tVectorAdd(botmins, botmaxs, v);\n\tVectorMA(origin, 0.5, v, v);\n\tVectorSubtract(bsptrace.endpos, v, v);\n\t//\n\tpoints = radiusdamage - 0.5 * VectorLength(v);\n\tif (points < 0) points = 0;\n\t//the owner of the rocket gets half the damage\n\tpoints *= 0.5;\n\t//mass of the bot (p_client.c: PutClientInServer)\n\tmass = 200;\n\t//knockback is the same as the damage points\n\tknockback = points;\n\t//direction of the damage (from trace.endpos to bot origin)\n\tVectorSubtract(origin, bsptrace.endpos, dir);\n\tVectorNormalize(dir);\n\t//damage velocity\n\tVectorScale(dir, 1600.0 * (float)knockback / mass, kvel);\t//the rocket jump hack...\n\t//rocket impact velocity + jump velocity\n\treturn kvel[2] + aassettings.phys_jumpvel;\n} //end of the function AAS_WeaponJumpZVelocity\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nfloat AAS_RocketJumpZVelocity(vec3_t origin)\n{\n\t//rocket radius damage is 120 (p_weapon.c: Weapon_RocketLauncher_Fire)\n\treturn AAS_WeaponJumpZVelocity(origin, 120);\n} //end of the function AAS_RocketJumpZVelocity\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nfloat AAS_BFGJumpZVelocity(vec3_t origin)\n{\n\t//bfg radius damage is 1000 (p_weapon.c: weapon_bfg_fire)\n\treturn AAS_WeaponJumpZVelocity(origin, 120);\n} //end of the function AAS_BFGJumpZVelocity\n//===========================================================================\n// applies ground friction to the given velocity\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid AAS_Accelerate(vec3_t velocity, float frametime, vec3_t wishdir, float wishspeed, float accel)\n{\n\t// q2 style\n\tint\t\t\ti;\n\tfloat\t\taddspeed, accelspeed, currentspeed;\n\n\tcurrentspeed = DotProduct(velocity, wishdir);\n\taddspeed = wishspeed - currentspeed;\n\tif (addspeed <= 0) {\n\t\treturn;\n\t}\n\taccelspeed = accel*frametime*wishspeed;\n\tif (accelspeed > addspeed) {\n\t\taccelspeed = addspeed;\n\t}\n\t\n\tfor (i=0 ; i<3 ; i++) {\n\t\tvelocity[i] += accelspeed*wishdir[i];\t\n\t}\n} //end of the function AAS_Accelerate\n/*\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid AAS_AirControl(vec3_t start, vec3_t end, vec3_t velocity, vec3_t cmdmove)\n{\n\tvec3_t dir;\n\n\tVectorSubtract(end, start, dir);\n} //end of the function AAS_AirControl\n*/\n//===========================================================================\n// applies ground friction to the given velocity\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid AAS_ApplyFriction(vec3_t vel, float friction, float stopspeed,\n\t\t\t\t\t\t\t\t\t\t\t\t\tfloat frametime)\n{\n\tfloat speed, control, newspeed;\n\n\t//horizontal speed\n\tspeed = sqrt(vel[0] * vel[0] + vel[1] * vel[1]);\n\tif (speed)\n\t{\n\t\tcontrol = speed < stopspeed ? stopspeed : speed;\n\t\tnewspeed = speed - frametime * control * friction;\n\t\tif (newspeed < 0) newspeed = 0;\n\t\tnewspeed /= speed;\n\t\tvel[0] *= newspeed;\n\t\tvel[1] *= newspeed;\n\t} //end if\n} //end of the function AAS_ApplyFriction\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint AAS_ClipToBBox(aas_trace_t *trace, vec3_t start, vec3_t end, int presencetype, vec3_t mins, vec3_t maxs)\n{\n\tint i, j, side;\n\tfloat front, back, frac, planedist;\n\tvec3_t bboxmins, bboxmaxs, absmins, absmaxs, dir, mid;\n\n\tAAS_PresenceTypeBoundingBox(presencetype, bboxmins, bboxmaxs);\n\tVectorSubtract(mins, bboxmaxs, absmins);\n\tVectorSubtract(maxs, bboxmins, absmaxs);\n\t//\n\tVectorCopy(end, trace->endpos);\n\ttrace->fraction = 1;\n\tfor (i = 0; i < 3; i++)\n\t{\n\t\tif (start[i] < absmins[i] && end[i] < absmins[i]) return qfalse;\n\t\tif (start[i] > absmaxs[i] && end[i] > absmaxs[i]) return qfalse;\n\t} //end for\n\t//check bounding box collision\n\tVectorSubtract(end, start, dir);\n\tfrac = 1;\n\tfor (i = 0; i < 3; i++)\n\t{\n\t\t//get plane to test collision with for the current axis direction\n\t\tif (dir[i] > 0) planedist = absmins[i];\n\t\telse planedist = absmaxs[i];\n\t\t//calculate collision fraction\n\t\tfront = start[i] - planedist;\n\t\tback = end[i] - planedist;\n\t\tfrac = front / (front-back);\n\t\t//check if between bounding planes of next axis\n\t\tside = i + 1;\n\t\tif (side > 2) side = 0;\n\t\tmid[side] = start[side] + dir[side] * frac;\n\t\tif (mid[side] > absmins[side] && mid[side] < absmaxs[side])\n\t\t{\n\t\t\t//check if between bounding planes of next axis\n\t\t\tside++;\n\t\t\tif (side > 2) side = 0;\n\t\t\tmid[side] = start[side] + dir[side] * frac;\n\t\t\tif (mid[side] > absmins[side] && mid[side] < absmaxs[side])\n\t\t\t{\n\t\t\t\tmid[i] = planedist;\n\t\t\t\tbreak;\n\t\t\t} //end if\n\t\t} //end if\n\t} //end for\n\t//if there was a collision\n\tif (i != 3)\n\t{\n\t\ttrace->startsolid = qfalse;\n\t\ttrace->fraction = frac;\n\t\ttrace->ent = 0;\n\t\ttrace->planenum = 0;\n\t\ttrace->area = 0;\n\t\ttrace->lastarea = 0;\n\t\t//trace endpos\n\t\tfor (j = 0; j < 3; j++) trace->endpos[j] = start[j] + dir[j] * frac;\n\t\treturn qtrue;\n\t} //end if\n\treturn qfalse;\n} //end of the function AAS_ClipToBBox\n//===========================================================================\n// predicts the movement\n// assumes regular bounding box sizes\n// NOTE: out of water jumping is not included\n// NOTE: grappling hook is not included\n//\n// Parameter:\t\t\torigin\t\t\t: origin to start with\n//\t\t\t\t\t\tpresencetype\t: presence type to start with\n//\t\t\t\t\t\tvelocity\t\t: velocity to start with\n//\t\t\t\t\t\tcmdmove\t\t\t: client command movement\n//\t\t\t\t\t\tcmdframes\t\t: number of frame cmdmove is valid\n//\t\t\t\t\t\tmaxframes\t\t: maximum number of predicted frames\n//\t\t\t\t\t\tframetime\t\t: duration of one predicted frame\n//\t\t\t\t\t\tstopevent\t\t: events that stop the prediction\n//\t\t\t\t\t\tstopareanum\t\t: stop as soon as entered this area\n// Returns:\t\t\t\taas_clientmove_t\n// Changes Globals:\t\t-\n//===========================================================================\nint AAS_ClientMovementPrediction(struct aas_clientmove_s *move,\n\t\t\t\t\t\t\t\tint entnum, vec3_t origin,\n\t\t\t\t\t\t\t\tint presencetype, int onground,\n\t\t\t\t\t\t\t\tvec3_t velocity, vec3_t cmdmove,\n\t\t\t\t\t\t\t\tint cmdframes,\n\t\t\t\t\t\t\t\tint maxframes, float frametime,\n\t\t\t\t\t\t\t\tint stopevent, int stopareanum,\n\t\t\t\t\t\t\t\tvec3_t mins, vec3_t maxs, int visualize)\n{\n\tfloat phys_friction, phys_stopspeed, phys_gravity, phys_waterfriction;\n\tfloat phys_watergravity;\n\tfloat phys_walkaccelerate, phys_airaccelerate, phys_swimaccelerate;\n\tfloat phys_maxwalkvelocity, phys_maxcrouchvelocity, phys_maxswimvelocity;\n\tfloat phys_maxstep, phys_maxsteepness, phys_jumpvel, friction;\n\tfloat gravity, delta, maxvel, wishspeed, accelerate;\n\t//float velchange, newvel;\n\tint n, i, j, pc, step, swimming, crouch, event, jump_frame, areanum;\n\tint areas[20], numareas;\n\tvec3_t points[20];\n\tvec3_t org, end, feet, start, stepend, lastorg, wishdir;\n\tvec3_t frame_test_vel, old_frame_test_vel, left_test_vel;\n\tvec3_t up = {0, 0, 1};\n\taas_plane_t *plane, *plane2;\n\taas_trace_t trace, steptrace;\n\t\n\tif (frametime <= 0) frametime = 0.1f;\n\t//\n\tphys_friction = aassettings.phys_friction;\n\tphys_stopspeed = aassettings.phys_stopspeed;\n\tphys_gravity = aassettings.phys_gravity;\n\tphys_waterfriction = aassettings.phys_waterfriction;\n\tphys_watergravity = aassettings.phys_watergravity;\n\tphys_maxwalkvelocity = aassettings.phys_maxwalkvelocity;// * frametime;\n\tphys_maxcrouchvelocity = aassettings.phys_maxcrouchvelocity;// * frametime;\n\tphys_maxswimvelocity = aassettings.phys_maxswimvelocity;// * frametime;\n\tphys_walkaccelerate = aassettings.phys_walkaccelerate;\n\tphys_airaccelerate = aassettings.phys_airaccelerate;\n\tphys_swimaccelerate = aassettings.phys_swimaccelerate;\n\tphys_maxstep = aassettings.phys_maxstep;\n\tphys_maxsteepness = aassettings.phys_maxsteepness;\n\tphys_jumpvel = aassettings.phys_jumpvel * frametime;\n\t//\n\tCom_Memset(move, 0, sizeof(aas_clientmove_t));\n\tCom_Memset(&trace, 0, sizeof(aas_trace_t));\n\t//start at the current origin\n\tVectorCopy(origin, org);\n\torg[2] += 0.25;\n\t//velocity to test for the first frame\n\tVectorScale(velocity, frametime, frame_test_vel);\n\t//\n\tjump_frame = -1;\n\t//predict a maximum of 'maxframes' ahead\n\tfor (n = 0; n < maxframes; n++)\n\t{\n\t\tswimming = AAS_Swimming(org);\n\t\t//get gravity depending on swimming or not\n\t\tgravity = swimming ? phys_watergravity : phys_gravity;\n\t\t//apply gravity at the START of the frame\n\t\tframe_test_vel[2] = frame_test_vel[2] - (gravity * 0.1 * frametime);\n\t\t//if on the ground or swimming\n\t\tif (onground || swimming)\n\t\t{\n\t\t\tfriction = swimming ? phys_friction : phys_waterfriction;\n\t\t\t//apply friction\n\t\t\tVectorScale(frame_test_vel, 1/frametime, frame_test_vel);\n\t\t\tAAS_ApplyFriction(frame_test_vel, friction, phys_stopspeed, frametime);\n\t\t\tVectorScale(frame_test_vel, frametime, frame_test_vel);\n\t\t} //end if\n\t\tcrouch = qfalse;\n\t\t//apply command movement\n\t\tif (n < cmdframes)\n\t\t{\n\t\t\tmaxvel = phys_maxwalkvelocity;\n\t\t\taccelerate = phys_airaccelerate;\n\t\t\tVectorCopy(cmdmove, wishdir);\n\t\t\tif (onground)\n\t\t\t{\n\t\t\t\tif (cmdmove[2] < -300)\n\t\t\t\t{\n\t\t\t\t\tcrouch = qtrue;\n\t\t\t\t\tmaxvel = phys_maxcrouchvelocity;\n\t\t\t\t} //end if\n\t\t\t\t//if not swimming and upmove is positive then jump\n\t\t\t\tif (!swimming && cmdmove[2] > 1)\n\t\t\t\t{\n\t\t\t\t\t//jump velocity minus the gravity for one frame + 5 for safety\n\t\t\t\t\tframe_test_vel[2] = phys_jumpvel - (gravity * 0.1 * frametime) + 5;\n\t\t\t\t\tjump_frame = n;\n\t\t\t\t\t//jumping so air accelerate\n\t\t\t\t\taccelerate = phys_airaccelerate;\n\t\t\t\t} //end if\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\taccelerate = phys_walkaccelerate;\n\t\t\t\t} //end else\n\t\t\t} //end if\n\t\t\tif (swimming)\n\t\t\t{\n\t\t\t\tmaxvel = phys_maxswimvelocity;\n\t\t\t\taccelerate = phys_swimaccelerate;\n\t\t\t} //end if\n\t\t\telse\n\t\t\t{\n\t\t\t\twishdir[2] = 0;\n\t\t\t} //end else\n\t\t\t//\n\t\t\twishspeed = VectorNormalize(wishdir);\n\t\t\tif (wishspeed > maxvel) wishspeed = maxvel;\n\t\t\tVectorScale(frame_test_vel, 1/frametime, frame_test_vel);\n\t\t\tAAS_Accelerate(frame_test_vel, frametime, wishdir, wishspeed, accelerate);\n\t\t\tVectorScale(frame_test_vel, frametime, frame_test_vel);\n\t\t\t/*\n\t\t\tfor (i = 0; i < ax; i++)\n\t\t\t{\n\t\t\t\tvelchange = (cmdmove[i] * frametime) - frame_test_vel[i];\n\t\t\t\tif (velchange > phys_maxacceleration) velchange = phys_maxacceleration;\n\t\t\t\telse if (velchange < -phys_maxacceleration) velchange = -phys_maxacceleration;\n\t\t\t\tnewvel = frame_test_vel[i] + velchange;\n\t\t\t\t//\n\t\t\t\tif (frame_test_vel[i] <= maxvel && newvel > maxvel) frame_test_vel[i] = maxvel;\n\t\t\t\telse if (frame_test_vel[i] >= -maxvel && newvel < -maxvel) frame_test_vel[i] = -maxvel;\n\t\t\t\telse frame_test_vel[i] = newvel;\n\t\t\t} //end for\n\t\t\t*/\n\t\t} //end if\n\t\tif (crouch)\n\t\t{\n\t\t\tpresencetype = PRESENCE_CROUCH;\n\t\t} //end if\n\t\telse if (presencetype == PRESENCE_CROUCH)\n\t\t{\n\t\t\tif (AAS_PointPresenceType(org) & PRESENCE_NORMAL)\n\t\t\t{\n\t\t\t\tpresencetype = PRESENCE_NORMAL;\n\t\t\t} //end if\n\t\t} //end else\n\t\t//save the current origin\n\t\tVectorCopy(org, lastorg);\n\t\t//move linear during one frame\n\t\tVectorCopy(frame_test_vel, left_test_vel);\n\t\tj = 0;\n\t\tdo\n\t\t{\n\t\t\tVectorAdd(org, left_test_vel, end);\n\t\t\t//trace a bounding box\n\t\t\ttrace = AAS_TraceClientBBox(org, end, presencetype, entnum);\n\t\t\t//\n//#ifdef AAS_MOVE_DEBUG\n\t\t\tif (visualize)\n\t\t\t{\n\t\t\t\tif (trace.startsolid) botimport.Print(PRT_MESSAGE, \"PredictMovement: start solid\\n\");\n\t\t\t\tAAS_DebugLine(org, trace.endpos, LINECOLOR_RED);\n\t\t\t} //end if\n//#endif //AAS_MOVE_DEBUG\n\t\t\t//\n\t\t\tif (stopevent & (SE_ENTERAREA|SE_TOUCHJUMPPAD|SE_TOUCHTELEPORTER|SE_TOUCHCLUSTERPORTAL))\n\t\t\t{\n\t\t\t\tnumareas = AAS_TraceAreas(org, trace.endpos, areas, points, 20);\n\t\t\t\tfor (i = 0; i < numareas; i++)\n\t\t\t\t{\n\t\t\t\t\tif (stopevent & SE_ENTERAREA)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (areas[i] == stopareanum)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tVectorCopy(points[i], move->endpos);\n\t\t\t\t\t\t\tVectorScale(frame_test_vel, 1/frametime, move->velocity);\n\t\t\t\t\t\t\tmove->endarea = areas[i];\n\t\t\t\t\t\t\tmove->trace = trace;\n\t\t\t\t\t\t\tmove->stopevent = SE_ENTERAREA;\n\t\t\t\t\t\t\tmove->presencetype = presencetype;\n\t\t\t\t\t\t\tmove->endcontents = 0;\n\t\t\t\t\t\t\tmove->time = n * frametime;\n\t\t\t\t\t\t\tmove->frames = n;\n\t\t\t\t\t\t\treturn qtrue;\n\t\t\t\t\t\t} //end if\n\t\t\t\t\t} //end if\n\t\t\t\t\t//NOTE: if not the first frame\n\t\t\t\t\tif ((stopevent & SE_TOUCHJUMPPAD) && n)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (aasworld.areasettings[areas[i]].contents & AREACONTENTS_JUMPPAD)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tVectorCopy(points[i], move->endpos);\n\t\t\t\t\t\t\tVectorScale(frame_test_vel, 1/frametime, move->velocity);\n\t\t\t\t\t\t\tmove->endarea = areas[i];\n\t\t\t\t\t\t\tmove->trace = trace;\n\t\t\t\t\t\t\tmove->stopevent = SE_TOUCHJUMPPAD;\n\t\t\t\t\t\t\tmove->presencetype = presencetype;\n\t\t\t\t\t\t\tmove->endcontents = 0;\n\t\t\t\t\t\t\tmove->time = n * frametime;\n\t\t\t\t\t\t\tmove->frames = n;\n\t\t\t\t\t\t\treturn qtrue;\n\t\t\t\t\t\t} //end if\n\t\t\t\t\t} //end if\n\t\t\t\t\tif (stopevent & SE_TOUCHTELEPORTER)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (aasworld.areasettings[areas[i]].contents & AREACONTENTS_TELEPORTER)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tVectorCopy(points[i], move->endpos);\n\t\t\t\t\t\t\tmove->endarea = areas[i];\n\t\t\t\t\t\t\tVectorScale(frame_test_vel, 1/frametime, move->velocity);\n\t\t\t\t\t\t\tmove->trace = trace;\n\t\t\t\t\t\t\tmove->stopevent = SE_TOUCHTELEPORTER;\n\t\t\t\t\t\t\tmove->presencetype = presencetype;\n\t\t\t\t\t\t\tmove->endcontents = 0;\n\t\t\t\t\t\t\tmove->time = n * frametime;\n\t\t\t\t\t\t\tmove->frames = n;\n\t\t\t\t\t\t\treturn qtrue;\n\t\t\t\t\t\t} //end if\n\t\t\t\t\t} //end if\n\t\t\t\t\tif (stopevent & SE_TOUCHCLUSTERPORTAL)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (aasworld.areasettings[areas[i]].contents & AREACONTENTS_CLUSTERPORTAL)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tVectorCopy(points[i], move->endpos);\n\t\t\t\t\t\t\tmove->endarea = areas[i];\n\t\t\t\t\t\t\tVectorScale(frame_test_vel, 1/frametime, move->velocity);\n\t\t\t\t\t\t\tmove->trace = trace;\n\t\t\t\t\t\t\tmove->stopevent = SE_TOUCHCLUSTERPORTAL;\n\t\t\t\t\t\t\tmove->presencetype = presencetype;\n\t\t\t\t\t\t\tmove->endcontents = 0;\n\t\t\t\t\t\t\tmove->time = n * frametime;\n\t\t\t\t\t\t\tmove->frames = n;\n\t\t\t\t\t\t\treturn qtrue;\n\t\t\t\t\t\t} //end if\n\t\t\t\t\t} //end if\n\t\t\t\t} //end for\n\t\t\t} //end if\n\t\t\t//\n\t\t\tif (stopevent & SE_HITBOUNDINGBOX)\n\t\t\t{\n\t\t\t\tif (AAS_ClipToBBox(&trace, org, trace.endpos, presencetype, mins, maxs))\n\t\t\t\t{\n\t\t\t\t\tVectorCopy(trace.endpos, move->endpos);\n\t\t\t\t\tmove->endarea = AAS_PointAreaNum(move->endpos);\n\t\t\t\t\tVectorScale(frame_test_vel, 1/frametime, move->velocity);\n\t\t\t\t\tmove->trace = trace;\n\t\t\t\t\tmove->stopevent = SE_HITBOUNDINGBOX;\n\t\t\t\t\tmove->presencetype = presencetype;\n\t\t\t\t\tmove->endcontents = 0;\n\t\t\t\t\tmove->time = n * frametime;\n\t\t\t\t\tmove->frames = n;\n\t\t\t\t\treturn qtrue;\n\t\t\t\t} //end if\n\t\t\t} //end if\n\t\t\t//move the entity to the trace end point\n\t\t\tVectorCopy(trace.endpos, org);\n\t\t\t//if there was a collision\n\t\t\tif (trace.fraction < 1.0)\n\t\t\t{\n\t\t\t\t//get the plane the bounding box collided with\n\t\t\t\tplane = AAS_PlaneFromNum(trace.planenum);\n\t\t\t\t//\n\t\t\t\tif (stopevent & SE_HITGROUNDAREA)\n\t\t\t\t{\n\t\t\t\t\tif (DotProduct(plane->normal, up) > phys_maxsteepness)\n\t\t\t\t\t{\n\t\t\t\t\t\tVectorCopy(org, start);\n\t\t\t\t\t\tstart[2] += 0.5;\n\t\t\t\t\t\tif (AAS_PointAreaNum(start) == stopareanum)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tVectorCopy(start, move->endpos);\n\t\t\t\t\t\t\tmove->endarea = stopareanum;\n\t\t\t\t\t\t\tVectorScale(frame_test_vel, 1/frametime, move->velocity);\n\t\t\t\t\t\t\tmove->trace = trace;\n\t\t\t\t\t\t\tmove->stopevent = SE_HITGROUNDAREA;\n\t\t\t\t\t\t\tmove->presencetype = presencetype;\n\t\t\t\t\t\t\tmove->endcontents = 0;\n\t\t\t\t\t\t\tmove->time = n * frametime;\n\t\t\t\t\t\t\tmove->frames = n;\n\t\t\t\t\t\t\treturn qtrue;\n\t\t\t\t\t\t} //end if\n\t\t\t\t\t} //end if\n\t\t\t\t} //end if\n\t\t\t\t//assume there's no step\n\t\t\t\tstep = qfalse;\n\t\t\t\t//if it is a vertical plane and the bot didn't jump recently\n\t\t\t\tif (plane->normal[2] == 0 && (jump_frame < 0 || n - jump_frame > 2))\n\t\t\t\t{\n\t\t\t\t\t//check for a step\n\t\t\t\t\tVectorMA(org, -0.25, plane->normal, start);\n\t\t\t\t\tVectorCopy(start, stepend);\n\t\t\t\t\tstart[2] += phys_maxstep;\n\t\t\t\t\tsteptrace = AAS_TraceClientBBox(start, stepend, presencetype, entnum);\n\t\t\t\t\t//\n\t\t\t\t\tif (!steptrace.startsolid)\n\t\t\t\t\t{\n\t\t\t\t\t\tplane2 = AAS_PlaneFromNum(steptrace.planenum);\n\t\t\t\t\t\tif (DotProduct(plane2->normal, up) > phys_maxsteepness)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tVectorSubtract(end, steptrace.endpos, left_test_vel);\n\t\t\t\t\t\t\tleft_test_vel[2] = 0;\n\t\t\t\t\t\t\tframe_test_vel[2] = 0;\n//#ifdef AAS_MOVE_DEBUG\n\t\t\t\t\t\t\tif (visualize)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tif (steptrace.endpos[2] - org[2] > 0.125)\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tVectorCopy(org, start);\n\t\t\t\t\t\t\t\t\tstart[2] = steptrace.endpos[2];\n\t\t\t\t\t\t\t\t\tAAS_DebugLine(org, start, LINECOLOR_BLUE);\n\t\t\t\t\t\t\t\t} //end if\n\t\t\t\t\t\t\t} //end if\n//#endif //AAS_MOVE_DEBUG\n\t\t\t\t\t\t\torg[2] = steptrace.endpos[2];\n\t\t\t\t\t\t\tstep = qtrue;\n\t\t\t\t\t\t} //end if\n\t\t\t\t\t} //end if\n\t\t\t\t} //end if\n\t\t\t\t//\n\t\t\t\tif (!step)\n\t\t\t\t{\n\t\t\t\t\t//velocity left to test for this frame is the projection\n\t\t\t\t\t//of the current test velocity into the hit plane \n\t\t\t\t\tVectorMA(left_test_vel, -DotProduct(left_test_vel, plane->normal),\n\t\t\t\t\t\t\t\t\t\tplane->normal, left_test_vel);\n\t\t\t\t\t//store the old velocity for landing check\n\t\t\t\t\tVectorCopy(frame_test_vel, old_frame_test_vel);\n\t\t\t\t\t//test velocity for the next frame is the projection\n\t\t\t\t\t//of the velocity of the current frame into the hit plane \n\t\t\t\t\tVectorMA(frame_test_vel, -DotProduct(frame_test_vel, plane->normal),\n\t\t\t\t\t\t\t\t\t\tplane->normal, frame_test_vel);\n\t\t\t\t\t//check for a landing on an almost horizontal floor\n\t\t\t\t\tif (DotProduct(plane->normal, up) > phys_maxsteepness)\n\t\t\t\t\t{\n\t\t\t\t\t\tonground = qtrue;\n\t\t\t\t\t} //end if\n\t\t\t\t\tif (stopevent & SE_HITGROUNDDAMAGE)\n\t\t\t\t\t{\n\t\t\t\t\t\tdelta = 0;\n\t\t\t\t\t\tif (old_frame_test_vel[2] < 0 &&\n\t\t\t\t\t\t\t\tframe_test_vel[2] > old_frame_test_vel[2] &&\n\t\t\t\t\t\t\t\t!onground)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tdelta = old_frame_test_vel[2];\n\t\t\t\t\t\t} //end if\n\t\t\t\t\t\telse if (onground)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tdelta = frame_test_vel[2] - old_frame_test_vel[2];\n\t\t\t\t\t\t} //end else\n\t\t\t\t\t\tif (delta)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tdelta = delta * 10;\n\t\t\t\t\t\t\tdelta = delta * delta * 0.0001;\n\t\t\t\t\t\t\tif (swimming) delta = 0;\n\t\t\t\t\t\t\t// never take falling damage if completely underwater\n\t\t\t\t\t\t\t/*\n\t\t\t\t\t\t\tif (ent->waterlevel == 3) return;\n\t\t\t\t\t\t\tif (ent->waterlevel == 2) delta *= 0.25;\n\t\t\t\t\t\t\tif (ent->waterlevel == 1) delta *= 0.5;\n\t\t\t\t\t\t\t*/\n\t\t\t\t\t\t\tif (delta > 40)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tVectorCopy(org, move->endpos);\n\t\t\t\t\t\t\t\tmove->endarea = AAS_PointAreaNum(org);\n\t\t\t\t\t\t\t\tVectorCopy(frame_test_vel, move->velocity);\n\t\t\t\t\t\t\t\tmove->trace = trace;\n\t\t\t\t\t\t\t\tmove->stopevent = SE_HITGROUNDDAMAGE;\n\t\t\t\t\t\t\t\tmove->presencetype = presencetype;\n\t\t\t\t\t\t\t\tmove->endcontents = 0;\n\t\t\t\t\t\t\t\tmove->time = n * frametime;\n\t\t\t\t\t\t\t\tmove->frames = n;\n\t\t\t\t\t\t\t\treturn qtrue;\n\t\t\t\t\t\t\t} //end if\n\t\t\t\t\t\t} //end if\n\t\t\t\t\t} //end if\n\t\t\t\t} //end if\n\t\t\t} //end if\n\t\t\t//extra check to prevent endless loop\n\t\t\tif (++j > 20) return qfalse;\n\t\t//while there is a plane hit\n\t\t} while(trace.fraction < 1.0);\n\t\t//if going down\n\t\tif (frame_test_vel[2] <= 10)\n\t\t{\n\t\t\t//check for a liquid at the feet of the bot\n\t\t\tVectorCopy(org, feet);\n\t\t\tfeet[2] -= 22;\n\t\t\tpc = AAS_PointContents(feet);\n\t\t\t//get event from pc\n\t\t\tevent = SE_NONE;\n\t\t\tif (pc & CONTENTS_LAVA) event |= SE_ENTERLAVA;\n\t\t\tif (pc & CONTENTS_SLIME) event |= SE_ENTERSLIME;\n\t\t\tif (pc & CONTENTS_WATER) event |= SE_ENTERWATER;\n\t\t\t//\n\t\t\tareanum = AAS_PointAreaNum(org);\n\t\t\tif (aasworld.areasettings[areanum].contents & AREACONTENTS_LAVA)\n\t\t\t\tevent |= SE_ENTERLAVA;\n\t\t\tif (aasworld.areasettings[areanum].contents & AREACONTENTS_SLIME)\n\t\t\t\tevent |= SE_ENTERSLIME;\n\t\t\tif (aasworld.areasettings[areanum].contents & AREACONTENTS_WATER)\n\t\t\t\tevent |= SE_ENTERWATER;\n\t\t\t//if in lava or slime\n\t\t\tif (event & stopevent)\n\t\t\t{\n\t\t\t\tVectorCopy(org, move->endpos);\n\t\t\t\tmove->endarea = areanum;\n\t\t\t\tVectorScale(frame_test_vel, 1/frametime, move->velocity);\n\t\t\t\tmove->stopevent = event & stopevent;\n\t\t\t\tmove->presencetype = presencetype;\n\t\t\t\tmove->endcontents = pc;\n\t\t\t\tmove->time = n * frametime;\n\t\t\t\tmove->frames = n;\n\t\t\t\treturn qtrue;\n\t\t\t} //end if\n\t\t} //end if\n\t\t//\n\t\tonground = AAS_OnGround(org, presencetype, entnum);\n\t\t//if onground and on the ground for at least one whole frame\n\t\tif (onground)\n\t\t{\n\t\t\tif (stopevent & SE_HITGROUND)\n\t\t\t{\n\t\t\t\tVectorCopy(org, move->endpos);\n\t\t\t\tmove->endarea = AAS_PointAreaNum(org);\n\t\t\t\tVectorScale(frame_test_vel, 1/frametime, move->velocity);\n\t\t\t\tmove->trace = trace;\n\t\t\t\tmove->stopevent = SE_HITGROUND;\n\t\t\t\tmove->presencetype = presencetype;\n\t\t\t\tmove->endcontents = 0;\n\t\t\t\tmove->time = n * frametime;\n\t\t\t\tmove->frames = n;\n\t\t\t\treturn qtrue;\n\t\t\t} //end if\n\t\t} //end if\n\t\telse if (stopevent & SE_LEAVEGROUND)\n\t\t{\n\t\t\tVectorCopy(org, move->endpos);\n\t\t\tmove->endarea = AAS_PointAreaNum(org);\n\t\t\tVectorScale(frame_test_vel, 1/frametime, move->velocity);\n\t\t\tmove->trace = trace;\n\t\t\tmove->stopevent = SE_LEAVEGROUND;\n\t\t\tmove->presencetype = presencetype;\n\t\t\tmove->endcontents = 0;\n\t\t\tmove->time = n * frametime;\n\t\t\tmove->frames = n;\n\t\t\treturn qtrue;\n\t\t} //end else if\n\t\telse if (stopevent & SE_GAP)\n\t\t{\n\t\t\taas_trace_t gaptrace;\n\n\t\t\tVectorCopy(org, start);\n\t\t\tVectorCopy(start, end);\n\t\t\tend[2] -= 48 + aassettings.phys_maxbarrier;\n\t\t\tgaptrace = AAS_TraceClientBBox(start, end, PRESENCE_CROUCH, -1);\n\t\t\t//if solid is found the bot cannot walk any further and will not fall into a gap\n\t\t\tif (!gaptrace.startsolid)\n\t\t\t{\n\t\t\t\t//if it is a gap (lower than one step height)\n\t\t\t\tif (gaptrace.endpos[2] < org[2] - aassettings.phys_maxstep - 1)\n\t\t\t\t{\n\t\t\t\t\tif (!(AAS_PointContents(end) & CONTENTS_WATER))\n\t\t\t\t\t{\n\t\t\t\t\t\tVectorCopy(lastorg, move->endpos);\n\t\t\t\t\t\tmove->endarea = AAS_PointAreaNum(lastorg);\n\t\t\t\t\t\tVectorScale(frame_test_vel, 1/frametime, move->velocity);\n\t\t\t\t\t\tmove->trace = trace;\n\t\t\t\t\t\tmove->stopevent = SE_GAP;\n\t\t\t\t\t\tmove->presencetype = presencetype;\n\t\t\t\t\t\tmove->endcontents = 0;\n\t\t\t\t\t\tmove->time = n * frametime;\n\t\t\t\t\t\tmove->frames = n;\n\t\t\t\t\t\treturn qtrue;\n\t\t\t\t\t} //end if\n\t\t\t\t} //end if\n\t\t\t} //end if\n\t\t} //end else if\n\t} //end for\n\t//\n\tVectorCopy(org, move->endpos);\n\tmove->endarea = AAS_PointAreaNum(org);\n\tVectorScale(frame_test_vel, 1/frametime, move->velocity);\n\tmove->stopevent = SE_NONE;\n\tmove->presencetype = presencetype;\n\tmove->endcontents = 0;\n\tmove->time = n * frametime;\n\tmove->frames = n;\n\t//\n\treturn qtrue;\n} //end of the function AAS_ClientMovementPrediction\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint AAS_PredictClientMovement(struct aas_clientmove_s *move,\n\t\t\t\t\t\t\t\tint entnum, vec3_t origin,\n\t\t\t\t\t\t\t\tint presencetype, int onground,\n\t\t\t\t\t\t\t\tvec3_t velocity, vec3_t cmdmove,\n\t\t\t\t\t\t\t\tint cmdframes,\n\t\t\t\t\t\t\t\tint maxframes, float frametime,\n\t\t\t\t\t\t\t\tint stopevent, int stopareanum, int visualize)\n{\n\tvec3_t mins, maxs;\n\treturn AAS_ClientMovementPrediction(move, entnum, origin, presencetype, onground,\n\t\t\t\t\t\t\t\t\t\tvelocity, cmdmove, cmdframes, maxframes,\n\t\t\t\t\t\t\t\t\t\tframetime, stopevent, stopareanum,\n\t\t\t\t\t\t\t\t\t\tmins, maxs, visualize);\n} //end of the function AAS_PredictClientMovement\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint AAS_ClientMovementHitBBox(struct aas_clientmove_s *move,\n\t\t\t\t\t\t\t\tint entnum, vec3_t origin,\n\t\t\t\t\t\t\t\tint presencetype, int onground,\n\t\t\t\t\t\t\t\tvec3_t velocity, vec3_t cmdmove,\n\t\t\t\t\t\t\t\tint cmdframes,\n\t\t\t\t\t\t\t\tint maxframes, float frametime,\n\t\t\t\t\t\t\t\tvec3_t mins, vec3_t maxs, int visualize)\n{\n\treturn AAS_ClientMovementPrediction(move, entnum, origin, presencetype, onground,\n\t\t\t\t\t\t\t\t\t\tvelocity, cmdmove, cmdframes, maxframes,\n\t\t\t\t\t\t\t\t\t\tframetime, SE_HITBOUNDINGBOX, 0,\n\t\t\t\t\t\t\t\t\t\tmins, maxs, visualize);\n} //end of the function AAS_ClientMovementHitBBox\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid AAS_TestMovementPrediction(int entnum, vec3_t origin, vec3_t dir)\n{\n\tvec3_t velocity, cmdmove;\n\taas_clientmove_t move;\n\n\tVectorClear(velocity);\n\tif (!AAS_Swimming(origin)) dir[2] = 0;\n\tVectorNormalize(dir);\n\tVectorScale(dir, 400, cmdmove);\n\tcmdmove[2] = 224;\n\tAAS_ClearShownDebugLines();\n\tAAS_PredictClientMovement(&move, entnum, origin, PRESENCE_NORMAL, qtrue,\n\t\t\t\t\t\t\t\t\tvelocity, cmdmove, 13, 13, 0.1f, SE_HITGROUND, 0, qtrue);//SE_LEAVEGROUND);\n\tif (move.stopevent & SE_LEAVEGROUND)\n\t{\n\t\tbotimport.Print(PRT_MESSAGE, \"leave ground\\n\");\n\t} //end if\n} //end of the function TestMovementPrediction\n//===========================================================================\n// calculates the horizontal velocity needed to perform a jump from start\n// to end\n//\n// Parameter:\t\t\tzvel\t: z velocity for jump\n//\t\t\t\t\t\tstart\t: start position of jump\n//\t\t\t\t\t\tend\t\t: end position of jump\n//\t\t\t\t\t\t*speed\t: returned speed for jump\n// Returns:\t\t\t\tqfalse if too high or too far from start to end\n// Changes Globals:\t\t-\n//===========================================================================\nint AAS_HorizontalVelocityForJump(float zvel, vec3_t start, vec3_t end, float *velocity)\n{\n\tfloat phys_gravity, phys_maxvelocity;\n\tfloat maxjump, height2fall, t, top;\n\tvec3_t dir;\n\n\tphys_gravity = aassettings.phys_gravity;\n\tphys_maxvelocity = aassettings.phys_maxvelocity;\n\n\t//maximum height a player can jump with the given initial z velocity\n\tmaxjump = 0.5 * phys_gravity * (zvel / phys_gravity) * (zvel / phys_gravity);\n\t//top of the parabolic jump\n\ttop = start[2] + maxjump;\n\t//height the bot will fall from the top\n\theight2fall = top - end[2];\n\t//if the goal is to high to jump to\n\tif (height2fall < 0)\n\t{\n\t\t*velocity = phys_maxvelocity;\n\t\treturn 0;\n\t} //end if\n\t//time a player takes to fall the height\n\tt = sqrt(height2fall / (0.5 * phys_gravity));\n  \t//direction from start to end\n\tVectorSubtract(end, start, dir);\n\t//\n\tif ( (t + zvel / phys_gravity) == 0.0f ) {\n\t\t*velocity = phys_maxvelocity;\n\t\treturn 0;\n\t}\n\t//calculate horizontal speed\n\t*velocity = sqrt(dir[0]*dir[0] + dir[1]*dir[1]) / (t + zvel / phys_gravity);\n\t//the horizontal speed must be lower than the max speed\n\tif (*velocity > phys_maxvelocity)\n\t{\n\t\t*velocity = phys_maxvelocity;\n\t\treturn 0;\n\t} //end if\n\treturn 1;\n} //end of the function AAS_HorizontalVelocityForJump\n"
  },
  {
    "path": "plugins/quake3/botlib/be_aas_move.h",
    "content": "/*\n===========================================================================\nCopyright (C) 1999-2005 Id Software, Inc.\n\nThis file is part of Quake III Arena source code.\n\nQuake III Arena source code is free software; you can redistribute it\nand/or modify it under the terms of the GNU General Public License as\npublished by the Free Software Foundation; either version 2 of the License,\nor (at your option) any later version.\n\nQuake III Arena source code is distributed in the hope that it will be\nuseful, but WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Quake III Arena source code; if not, write to the Free Software\nFoundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n===========================================================================\n*/\n\n/*****************************************************************************\n * name:\t\tbe_aas_move.h\n *\n * desc:\t\tAAS\n *\n * $Archive: /source/code/botlib/be_aas_move.h $\n *\n *****************************************************************************/\n\n#ifdef AASINTERN\nextern aas_settings_t aassettings;\n#endif //AASINTERN\n\n//movement prediction\nint AAS_PredictClientMovement(struct aas_clientmove_s *move,\n\t\t\t\t\t\t\tint entnum, vec3_t origin,\n\t\t\t\t\t\t\tint presencetype, int onground,\n\t\t\t\t\t\t\tvec3_t velocity, vec3_t cmdmove,\n\t\t\t\t\t\t\tint cmdframes,\n\t\t\t\t\t\t\tint maxframes, float frametime,\n\t\t\t\t\t\t\tint stopevent, int stopareanum, int visualize);\n//predict movement until bounding box is hit\nint AAS_ClientMovementHitBBox(struct aas_clientmove_s *move,\n\t\t\t\t\t\t\t\tint entnum, vec3_t origin,\n\t\t\t\t\t\t\t\tint presencetype, int onground,\n\t\t\t\t\t\t\t\tvec3_t velocity, vec3_t cmdmove,\n\t\t\t\t\t\t\t\tint cmdframes,\n\t\t\t\t\t\t\t\tint maxframes, float frametime,\n\t\t\t\t\t\t\t\tvec3_t mins, vec3_t maxs, int visualize);\n//returns true if on the ground at the given origin\nint AAS_OnGround(vec3_t origin, int presencetype, int passent);\n//returns true if swimming at the given origin\nint AAS_Swimming(vec3_t origin);\n//returns the jump reachability run start point\nvoid AAS_JumpReachRunStart(struct aas_reachability_s *reach, vec3_t runstart);\n//returns true if against a ladder at the given origin\nint AAS_AgainstLadder(vec3_t origin);\n//rocket jump Z velocity when rocket-jumping at origin\nfloat AAS_RocketJumpZVelocity(vec3_t origin);\n//bfg jump Z velocity when bfg-jumping at origin\nfloat AAS_BFGJumpZVelocity(vec3_t origin);\n//calculates the horizontal velocity needed for a jump and returns true this velocity could be calculated\nint AAS_HorizontalVelocityForJump(float zvel, vec3_t start, vec3_t end, float *velocity);\n//\nvoid AAS_SetMovedir(vec3_t angles, vec3_t movedir);\n//\nint AAS_DropToFloor(vec3_t origin, vec3_t mins, vec3_t maxs);\n//\nvoid AAS_InitSettings(void);\n"
  },
  {
    "path": "plugins/quake3/botlib/be_aas_optimize.c",
    "content": "/*\n===========================================================================\nCopyright (C) 1999-2005 Id Software, Inc.\n\nThis file is part of Quake III Arena source code.\n\nQuake III Arena source code is free software; you can redistribute it\nand/or modify it under the terms of the GNU General Public License as\npublished by the Free Software Foundation; either version 2 of the License,\nor (at your option) any later version.\n\nQuake III Arena source code is distributed in the hope that it will be\nuseful, but WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Quake III Arena source code; if not, write to the Free Software\nFoundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n===========================================================================\n*/\n\n/*****************************************************************************\n * name:\t\tbe_aas_optimize.c\n *\n * desc:\t\tdecreases the .aas file size after the reachabilities have\n *\t\t\t\tbeen calculated, just dumps all the faces, edges and vertexes\n *\n * $Archive: /MissionPack/code/botlib/be_aas_optimize.c $\n *\n *****************************************************************************/\n\n#include \"q_shared.h\"\n#include \"l_libvar.h\"\n#include \"l_memory.h\"\n#include \"l_script.h\"\n#include \"l_precomp.h\"\n#include \"l_struct.h\"\n#include \"aasfile.h\"\n#include \"botlib.h\"\n#include \"be_aas.h\"\n#include \"be_aas_funcs.h\"\n#include \"be_interface.h\"\n#include \"be_aas_def.h\"\n\ntypedef struct optimized_s\n{\n\t//vertexes\n\tint numvertexes;\n\taas_vertex_t *vertexes;\n\t//edges\n\tint numedges;\n\taas_edge_t *edges;\n\t//edge index\n\tint edgeindexsize;\n\taas_edgeindex_t *edgeindex;\n\t//faces\n\tint numfaces;\n\taas_face_t *faces;\n\t//face index\n\tint faceindexsize;\n\taas_faceindex_t *faceindex;\n\t//convex areas\n\tint numareas;\n\taas_area_t *areas;\n\t//\n\tint *vertexoptimizeindex;\n\tint *edgeoptimizeindex;\n\tint *faceoptimizeindex;\n} optimized_t;\n\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint AAS_KeepEdge(aas_edge_t *edge)\n{\n\treturn 1;\n} //end of the function AAS_KeepFace\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint AAS_OptimizeEdge(optimized_t *optimized, int edgenum)\n{\n\tint i, optedgenum;\n\taas_edge_t *edge, *optedge;\n\n\tedge = &aasworld.edges[abs(edgenum)];\n\tif (!AAS_KeepEdge(edge)) return 0;\n\n\toptedgenum = optimized->edgeoptimizeindex[abs(edgenum)];\n\tif (optedgenum)\n\t{\n\t\t//keep the edge reversed sign\n\t\tif (edgenum > 0) return optedgenum;\n\t\telse return -optedgenum;\n\t} //end if\n\n\toptedge = &optimized->edges[optimized->numedges];\n\n\tfor (i = 0; i < 2; i++)\n\t{\n\t\tif (optimized->vertexoptimizeindex[edge->v[i]])\n\t\t{\n\t\t\toptedge->v[i] = optimized->vertexoptimizeindex[edge->v[i]];\n\t\t} //end if\n\t\telse\n\t\t{\n\t\t\tVectorCopy(aasworld.vertexes[edge->v[i]], optimized->vertexes[optimized->numvertexes]);\n\t\t\toptedge->v[i] = optimized->numvertexes;\n\t\t\toptimized->vertexoptimizeindex[edge->v[i]] = optimized->numvertexes;\n\t\t\toptimized->numvertexes++;\n\t\t} //end else\n\t} //end for\n\toptimized->edgeoptimizeindex[abs(edgenum)] = optimized->numedges;\n\toptedgenum = optimized->numedges;\n\toptimized->numedges++;\n\t//keep the edge reversed sign\n\tif (edgenum > 0) return optedgenum;\n\telse return -optedgenum;\n} //end of the function AAS_OptimizeEdge\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint AAS_KeepFace(aas_face_t *face)\n{\n\tif (!(face->faceflags & FACE_LADDER)) return 0;\n\telse return 1;\n} //end of the function AAS_KeepFace\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint AAS_OptimizeFace(optimized_t *optimized, int facenum)\n{\n\tint i, edgenum, optedgenum, optfacenum;\n\taas_face_t *face, *optface;\n\n\tface = &aasworld.faces[abs(facenum)];\n\tif (!AAS_KeepFace(face)) return 0;\n\n\toptfacenum = optimized->faceoptimizeindex[abs(facenum)];\n\tif (optfacenum)\n\t{\n\t\t//keep the face side sign\n\t\tif (facenum > 0) return optfacenum;\n\t\telse return -optfacenum;\n\t} //end if\n\n\toptface = &optimized->faces[optimized->numfaces];\n\tCom_Memcpy(optface, face, sizeof(aas_face_t));\n\n\toptface->numedges = 0;\n\toptface->firstedge = optimized->edgeindexsize;\n\tfor (i = 0; i < face->numedges; i++)\n\t{\n\t\tedgenum = aasworld.edgeindex[face->firstedge + i];\n\t\toptedgenum = AAS_OptimizeEdge(optimized, edgenum);\n\t\tif (optedgenum)\n\t\t{\n\t\t\toptimized->edgeindex[optface->firstedge + optface->numedges] = optedgenum;\n\t\t\toptface->numedges++;\n\t\t\toptimized->edgeindexsize++;\n\t\t} //end if\n\t} //end for\n\toptimized->faceoptimizeindex[abs(facenum)] = optimized->numfaces;\n\toptfacenum = optimized->numfaces;\n\toptimized->numfaces++;\n\t//keep the face side sign\n\tif (facenum > 0) return optfacenum;\n\telse return -optfacenum;\n} //end of the function AAS_OptimizeFace\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid AAS_OptimizeArea(optimized_t *optimized, int areanum)\n{\n\tint i, facenum, optfacenum;\n\taas_area_t *area, *optarea;\n\n\tarea = &aasworld.areas[areanum];\n\toptarea = &optimized->areas[areanum];\n\tCom_Memcpy(optarea, area, sizeof(aas_area_t));\n\n\toptarea->numfaces = 0;\n\toptarea->firstface = optimized->faceindexsize;\n\tfor (i = 0; i < area->numfaces; i++)\n\t{\n\t\tfacenum = aasworld.faceindex[area->firstface + i];\n\t\toptfacenum = AAS_OptimizeFace(optimized, facenum);\n\t\tif (optfacenum)\n\t\t{\n\t\t\toptimized->faceindex[optarea->firstface + optarea->numfaces] = optfacenum;\n\t\t\toptarea->numfaces++;\n\t\t\toptimized->faceindexsize++;\n\t\t} //end if\n\t} //end for\n} //end of the function AAS_OptimizeArea\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid AAS_OptimizeAlloc(optimized_t *optimized)\n{\n\toptimized->vertexes = (aas_vertex_t *) GetClearedMemory(aasworld.numvertexes * sizeof(aas_vertex_t));\n\toptimized->numvertexes = 0;\n\toptimized->edges = (aas_edge_t *) GetClearedMemory(aasworld.numedges * sizeof(aas_edge_t));\n\toptimized->numedges = 1; //edge zero is a dummy\n\toptimized->edgeindex = (aas_edgeindex_t *) GetClearedMemory(aasworld.edgeindexsize * sizeof(aas_edgeindex_t));\n\toptimized->edgeindexsize = 0;\n\toptimized->faces = (aas_face_t *) GetClearedMemory(aasworld.numfaces * sizeof(aas_face_t));\n\toptimized->numfaces = 1; //face zero is a dummy\n\toptimized->faceindex = (aas_faceindex_t *) GetClearedMemory(aasworld.faceindexsize * sizeof(aas_faceindex_t));\n\toptimized->faceindexsize = 0;\n\toptimized->areas = (aas_area_t *) GetClearedMemory(aasworld.numareas * sizeof(aas_area_t));\n\toptimized->numareas = aasworld.numareas;\n\t//\n\toptimized->vertexoptimizeindex = (int *) GetClearedMemory(aasworld.numvertexes * sizeof(int));\n\toptimized->edgeoptimizeindex = (int *) GetClearedMemory(aasworld.numedges * sizeof(int));\n\toptimized->faceoptimizeindex = (int *) GetClearedMemory(aasworld.numfaces * sizeof(int));\n} //end of the function AAS_OptimizeAlloc\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid AAS_OptimizeStore(optimized_t *optimized)\n{\n\t//store the optimized vertexes\n\tif (aasworld.vertexes) FreeMemory(aasworld.vertexes);\n\taasworld.vertexes = optimized->vertexes;\n\taasworld.numvertexes = optimized->numvertexes;\n\t//store the optimized edges\n\tif (aasworld.edges) FreeMemory(aasworld.edges);\n\taasworld.edges = optimized->edges;\n\taasworld.numedges = optimized->numedges;\n\t//store the optimized edge index\n\tif (aasworld.edgeindex) FreeMemory(aasworld.edgeindex);\n\taasworld.edgeindex = optimized->edgeindex;\n\taasworld.edgeindexsize = optimized->edgeindexsize;\n\t//store the optimized faces\n\tif (aasworld.faces) FreeMemory(aasworld.faces);\n\taasworld.faces = optimized->faces;\n\taasworld.numfaces = optimized->numfaces;\n\t//store the optimized face index\n\tif (aasworld.faceindex) FreeMemory(aasworld.faceindex);\n\taasworld.faceindex = optimized->faceindex;\n\taasworld.faceindexsize = optimized->faceindexsize;\n\t//store the optimized areas\n\tif (aasworld.areas) FreeMemory(aasworld.areas);\n\taasworld.areas = optimized->areas;\n\taasworld.numareas = optimized->numareas;\n\t//free optimize indexes\n\tFreeMemory(optimized->vertexoptimizeindex);\n\tFreeMemory(optimized->edgeoptimizeindex);\n\tFreeMemory(optimized->faceoptimizeindex);\n} //end of the function AAS_OptimizeStore\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid AAS_Optimize(void)\n{\n\tint i, sign;\n\toptimized_t optimized;\n\n\tAAS_OptimizeAlloc(&optimized);\n\tfor (i = 1; i < aasworld.numareas; i++)\n\t{\n\t\tAAS_OptimizeArea(&optimized, i);\n\t} //end for\n\t//reset the reachability face pointers\n\tfor (i = 0; i < aasworld.reachabilitysize; i++)\n\t{\n\t\t//NOTE: for TRAVEL_ELEVATOR the facenum is the model number of\n\t\t//\t\tthe elevator\n\t\tif ((aasworld.reachability[i].traveltype & TRAVELTYPE_MASK) == TRAVEL_ELEVATOR) continue;\n\t\t//NOTE: for TRAVEL_JUMPPAD the facenum is the Z velocity and the edgenum is the hor velocity\n\t\tif ((aasworld.reachability[i].traveltype & TRAVELTYPE_MASK) == TRAVEL_JUMPPAD) continue;\n\t\t//NOTE: for TRAVEL_FUNCBOB the facenum and edgenum contain other coded information\n\t\tif ((aasworld.reachability[i].traveltype & TRAVELTYPE_MASK) == TRAVEL_FUNCBOB) continue;\n\t\t//\n\t\tsign = aasworld.reachability[i].facenum;\n\t\taasworld.reachability[i].facenum = optimized.faceoptimizeindex[abs(aasworld.reachability[i].facenum)];\n\t\tif (sign < 0) aasworld.reachability[i].facenum = -aasworld.reachability[i].facenum;\n\t\tsign = aasworld.reachability[i].edgenum;\n\t\taasworld.reachability[i].edgenum = optimized.edgeoptimizeindex[abs(aasworld.reachability[i].edgenum)];\n\t\tif (sign < 0) aasworld.reachability[i].edgenum = -aasworld.reachability[i].edgenum;\n\t} //end for\n\t//store the optimized AAS data into aasworld\n\tAAS_OptimizeStore(&optimized);\n\t//print some nice stuff :)\n\tbotimport.Print(PRT_MESSAGE, \"AAS data optimized.\\n\");\n} //end of the function AAS_Optimize\n"
  },
  {
    "path": "plugins/quake3/botlib/be_aas_optimize.h",
    "content": "/*\n===========================================================================\nCopyright (C) 1999-2005 Id Software, Inc.\n\nThis file is part of Quake III Arena source code.\n\nQuake III Arena source code is free software; you can redistribute it\nand/or modify it under the terms of the GNU General Public License as\npublished by the Free Software Foundation; either version 2 of the License,\nor (at your option) any later version.\n\nQuake III Arena source code is distributed in the hope that it will be\nuseful, but WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Quake III Arena source code; if not, write to the Free Software\nFoundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n===========================================================================\n*/\n\n/*****************************************************************************\n * name:\t\tbe_aas_optimize.h\n *\n * desc:\t\tAAS\n *\n * $Archive: /source/code/botlib/be_aas_optimize.h $\n *\n *****************************************************************************/\n\nvoid AAS_Optimize(void);\n\n"
  },
  {
    "path": "plugins/quake3/botlib/be_aas_reach.c",
    "content": "/*\n===========================================================================\nCopyright (C) 1999-2005 Id Software, Inc.\n\nThis file is part of Quake III Arena source code.\n\nQuake III Arena source code is free software; you can redistribute it\nand/or modify it under the terms of the GNU General Public License as\npublished by the Free Software Foundation; either version 2 of the License,\nor (at your option) any later version.\n\nQuake III Arena source code is distributed in the hope that it will be\nuseful, but WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Quake III Arena source code; if not, write to the Free Software\nFoundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n===========================================================================\n*/\n\n/*****************************************************************************\n * name:\t\tbe_aas_reach.c\n *\n * desc:\t\treachability calculations\n *\n * $Archive: /MissionPack/code/botlib/be_aas_reach.c $\n *\n *****************************************************************************/\n\n#include \"q_shared.h\"\n#include \"l_log.h\"\n#include \"l_memory.h\"\n#include \"l_script.h\"\n#include \"l_libvar.h\"\n#include \"l_precomp.h\"\n#include \"l_struct.h\"\n#include \"aasfile.h\"\n#include \"botlib.h\"\n#include \"be_aas.h\"\n#include \"be_aas_funcs.h\"\n#include \"be_aas_def.h\"\n\nextern int Sys_MilliSeconds(void);\n\n\nextern botlib_import_t botimport;\n\n//#define REACH_DEBUG\n\n//NOTE: all travel times are in hundreth of a second\n//maximum number of reachability links\n#define AAS_MAX_REACHABILITYSIZE\t\t\t65536\n//number of areas reachability is calculated for each frame\n#define REACHABILITYAREASPERCYCLE\t\t\t15\n//number of units reachability points are placed inside the areas\n#define INSIDEUNITS\t\t\t\t\t\t\t2\n#define INSIDEUNITS_WALKEND\t\t\t\t\t5\n#define INSIDEUNITS_WALKSTART\t\t\t\t0.1\n#define INSIDEUNITS_WATERJUMP\t\t\t\t15\n//area flag used for weapon jumping\n#define AREA_WEAPONJUMP\t\t\t\t\t\t8192\t//valid area to weapon jump to\n//number of reachabilities of each type\nint reach_swim;\t\t\t//swim\nint reach_equalfloor;\t//walk on floors with equal height\nint reach_step;\t\t\t//step up\nint reach_walk;\t\t\t//walk of step\nint reach_barrier;\t\t//jump up to a barrier\nint reach_waterjump;\t//jump out of water\nint reach_walkoffledge;\t//walk of a ledge\nint reach_jump;\t\t\t//jump\nint reach_ladder;\t\t//climb or descent a ladder\nint reach_teleport;\t\t//teleport\nint reach_elevator;\t\t//use an elevator\nint reach_funcbob;\t\t//use a func bob\nint reach_grapple;\t\t//grapple hook\nint reach_doublejump;\t//double jump\nint reach_rampjump;\t\t//ramp jump\nint reach_strafejump;\t//strafe jump (just normal jump but further)\nint reach_rocketjump;\t//rocket jump\nint reach_bfgjump;\t\t//bfg jump\nint reach_jumppad;\t\t//jump pads\n//if true grapple reachabilities are skipped\nint calcgrapplereach;\n//linked reachability\ntypedef struct aas_lreachability_s\n{\n\tint areanum;\t\t\t\t\t//number of the reachable area\n\tint facenum;\t\t\t\t\t//number of the face towards the other area\n\tint edgenum;\t\t\t\t\t//number of the edge towards the other area\n\tvec3_t start;\t\t\t\t\t//start point of inter area movement\n\tvec3_t end;\t\t\t\t\t\t//end point of inter area movement\n\tint traveltype;\t\t\t\t\t//type of travel required to get to the area\n\tunsigned short int traveltime;\t//travel time of the inter area movement\n\t//\n\tstruct aas_lreachability_s *next;\n} aas_lreachability_t;\n//temporary reachabilities\naas_lreachability_t *reachabilityheap;\t//heap with reachabilities\naas_lreachability_t *nextreachability;\t//next free reachability from the heap\naas_lreachability_t **areareachability;\t//reachability links for every area\nint numlreachabilities;\n\n//===========================================================================\n// returns the surface area of the given face\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nfloat AAS_FaceArea(aas_face_t *face)\n{\n\tint i, edgenum, side;\n\tfloat total;\n\tvec_t *v;\n\tvec3_t d1, d2, cross;\n\taas_edge_t *edge;\n\n\tedgenum = aasworld.edgeindex[face->firstedge];\n\tside = edgenum < 0;\n\tedge = &aasworld.edges[abs(edgenum)];\n\tv = aasworld.vertexes[edge->v[side]];\n\n\ttotal = 0;\n\tfor (i = 1; i < face->numedges - 1; i++)\n\t{\n\t\tedgenum = aasworld.edgeindex[face->firstedge + i];\n\t\tside = edgenum < 0;\n\t\tedge = &aasworld.edges[abs(edgenum)];\n\t\tVectorSubtract(aasworld.vertexes[edge->v[side]], v, d1);\n\t\tVectorSubtract(aasworld.vertexes[edge->v[!side]], v, d2);\n\t\tCrossProduct(d1, d2, cross);\n\t\ttotal += 0.5 * VectorLength(cross);\n\t} //end for\n\treturn total;\n} //end of the function AAS_FaceArea\n//===========================================================================\n// returns the volume of an area\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nfloat AAS_AreaVolume(int areanum)\n{\n\tint i, edgenum, facenum, side;\n\tvec_t d, a, volume;\n\tvec3_t corner;\n\taas_plane_t *plane;\n\taas_edge_t *edge;\n\taas_face_t *face;\n\taas_area_t *area;\n\n\tarea = &aasworld.areas[areanum];\n\tfacenum = aasworld.faceindex[area->firstface];\n\tface = &aasworld.faces[abs(facenum)];\n\tedgenum = aasworld.edgeindex[face->firstedge];\n\tedge = &aasworld.edges[abs(edgenum)];\n\t//\n\tVectorCopy(aasworld.vertexes[edge->v[0]], corner);\n\n\t//make tetrahedrons to all other faces\n\tvolume = 0;\n\tfor (i = 0; i < area->numfaces; i++)\n\t{\n\t\tfacenum = abs(aasworld.faceindex[area->firstface + i]);\n\t\tface = &aasworld.faces[facenum];\n\t\tside = face->backarea != areanum;\n\t\tplane = &aasworld.planes[face->planenum ^ side];\n\t\td = -(DotProduct (corner, plane->normal) - plane->dist);\n\t\ta = AAS_FaceArea(face);\n\t\tvolume += d * a;\n\t} //end for\n\n\tvolume /= 3;\n\treturn volume;\n} //end of the function AAS_AreaVolume\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint AAS_BestReachableLinkArea(aas_link_t *areas)\n{\n\taas_link_t *link;\n\n\tfor (link = areas; link; link = link->next_area)\n\t{\n\t\tif (AAS_AreaGrounded(link->areanum) || AAS_AreaSwim(link->areanum))\n\t\t{\n\t\t\treturn link->areanum;\n\t\t} //end if\n\t} //end for\n\t//\n\tfor (link = areas; link; link = link->next_area)\n\t{\n\t\tif (link->areanum) return link->areanum;\n\t\t//FIXME: this is a bad idea when the reachability is not yet\n\t\t// calculated when the level items are loaded\n\t\tif (AAS_AreaReachability(link->areanum))\n\t\t\treturn link->areanum;\n\t} //end for\n\treturn 0;\n} //end of the function AAS_BestReachableLinkArea\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint AAS_GetJumpPadInfo(int ent, vec3_t areastart, vec3_t absmins, vec3_t absmaxs, vec3_t velocity)\n{\n\tint modelnum, ent2;\n\tfloat speed, height, gravity, time, dist, forward;\n\tvec3_t origin, angles, teststart, ent2origin;\n\taas_trace_t trace;\n\tchar model[MAX_EPAIRKEY];\n\tchar target[MAX_EPAIRKEY], targetname[MAX_EPAIRKEY];\n\n\t//\n\tAAS_FloatForBSPEpairKey(ent, \"speed\", &speed);\n\tif (!speed) speed = 1000;\n\tVectorClear(angles);\n\t//get the mins, maxs and origin of the model\n\tAAS_ValueForBSPEpairKey(ent, \"model\", model, MAX_EPAIRKEY);\n\tif (model[0]) modelnum = atoi(model+1);\n\telse modelnum = 0;\n\tAAS_BSPModelMinsMaxsOrigin(modelnum, angles, absmins, absmaxs, origin);\n\tVectorAdd(origin, absmins, absmins);\n\tVectorAdd(origin, absmaxs, absmaxs);\n\tVectorAdd(absmins, absmaxs, origin);\n\tVectorScale (origin, 0.5, origin);\n\n\t//get the start areas\n\tVectorCopy(origin, teststart);\n\tteststart[2] += 64;\n\ttrace = AAS_TraceClientBBox(teststart, origin, PRESENCE_CROUCH, -1);\n\tif (trace.startsolid)\n\t{\n\t\tbotimport.Print(PRT_MESSAGE, \"trigger_push start solid\\n\");\n\t\tVectorCopy(origin, areastart);\n\t} //end if\n\telse\n\t{\n\t\tVectorCopy(trace.endpos, areastart);\n\t} //end else\n\tareastart[2] += 0.125;\n\t//\n\t//AAS_DrawPermanentCross(origin, 4, 4);\n\t//get the target entity\n\tAAS_ValueForBSPEpairKey(ent, \"target\", target, MAX_EPAIRKEY);\n\tfor (ent2 = AAS_NextBSPEntity(0); ent2; ent2 = AAS_NextBSPEntity(ent2))\n\t{\n\t\tif (!AAS_ValueForBSPEpairKey(ent2, \"targetname\", targetname, MAX_EPAIRKEY)) continue;\n\t\tif (!strcmp(targetname, target)) break;\n\t} //end for\n\tif (!ent2)\n\t{\n\t\tbotimport.Print(PRT_MESSAGE, \"trigger_push without target entity %s\\n\", target);\n\t\treturn qfalse;\n\t} //end if\n\tAAS_VectorForBSPEpairKey(ent2, \"origin\", ent2origin);\n\t//\n\theight = ent2origin[2] - origin[2];\n\tgravity = aassettings.phys_gravity;\n\ttime = sqrt( height / ( 0.5 * gravity ) );\n\tif (!time)\n\t{\n\t\tbotimport.Print(PRT_MESSAGE, \"trigger_push without time\\n\");\n\t\treturn qfalse;\n\t} //end if\n\t// set s.origin2 to the push velocity\n\tVectorSubtract ( ent2origin, origin, velocity);\n\tdist = VectorNormalize( velocity);\n\tforward = dist / time;\n\t//FIXME: why multiply by 1.1\n\tforward *= 1.1f;\n\tVectorScale(velocity, forward, velocity);\n\tvelocity[2] = time * gravity;\n\treturn qtrue;\n} //end of the function AAS_GetJumpPadInfo\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint AAS_BestReachableFromJumpPadArea(vec3_t origin, vec3_t mins, vec3_t maxs)\n{\n\tint ent, bot_visualizejumppads, bestareanum;\n\tfloat volume, bestareavolume;\n\tvec3_t areastart, cmdmove, bboxmins, bboxmaxs;\n\tvec3_t absmins, absmaxs, velocity;\n\taas_clientmove_t move;\n\taas_link_t *areas, *link;\n\tchar classname[MAX_EPAIRKEY];\n\n#ifdef BSPC\n\tbot_visualizejumppads = 0;\n#else\n\tbot_visualizejumppads = LibVarValue(\"bot_visualizejumppads\", \"0\");\n#endif\n\tVectorAdd(origin, mins, bboxmins);\n\tVectorAdd(origin, maxs, bboxmaxs);\n\tfor (ent = AAS_NextBSPEntity(0); ent; ent = AAS_NextBSPEntity(ent))\n\t{\n\t\tif (!AAS_ValueForBSPEpairKey(ent, \"classname\", classname, MAX_EPAIRKEY)) continue;\n\t\tif (strcmp(classname, \"trigger_push\")) continue;\n\t\t//\n\t\tif (!AAS_GetJumpPadInfo(ent, areastart, absmins, absmaxs, velocity)) continue;\n\t\t//get the areas the jump pad brush is in\n\t\tareas = AAS_LinkEntityClientBBox(absmins, absmaxs, -1, PRESENCE_CROUCH);\n\t\tfor (link = areas; link; link = link->next_area)\n\t\t{\n\t\t\tif (AAS_AreaJumpPad(link->areanum)) break;\n\t\t} //end for\n\t\tif (!link)\n\t\t{\n\t\t\tbotimport.Print(PRT_MESSAGE, \"trigger_push not in any jump pad area\\n\");\n\t\t\tAAS_UnlinkFromAreas(areas);\n\t\t\tcontinue;\n\t\t} //end if\n\t\t//\n\t\t//botimport.Print(PRT_MESSAGE, \"found a trigger_push with velocity %f %f %f\\n\", velocity[0], velocity[1], velocity[2]);\n\t\t//\n\t\tVectorSet(cmdmove, 0, 0, 0);\n\t\tCom_Memset(&move, 0, sizeof(aas_clientmove_t));\n\t\tAAS_ClientMovementHitBBox(&move, -1, areastart, PRESENCE_NORMAL, qfalse,\n\t\t\t\t\t\t\t\tvelocity, cmdmove, 0, 30, 0.1f, bboxmins, bboxmaxs, bot_visualizejumppads);\n\t\tif (move.frames < 30)\n\t\t{\n\t\t\tbestareanum = 0;\n\t\t\tbestareavolume = 0;\n\t\t\tfor (link = areas; link; link = link->next_area)\n\t\t\t{\n\t\t\t\tif (!AAS_AreaJumpPad(link->areanum)) continue;\n\t\t\t\tvolume = AAS_AreaVolume(link->areanum);\n\t\t\t\tif (volume >= bestareavolume)\n\t\t\t\t{\n\t\t\t\t\tbestareanum = link->areanum;\n\t\t\t\t\tbestareavolume = volume;\n\t\t\t\t} //end if\n\t\t\t} //end if\n\t\t\tAAS_UnlinkFromAreas(areas);\n\t\t\treturn bestareanum;\n\t\t} //end if\n\t\tAAS_UnlinkFromAreas(areas);\n\t} //end for\n\treturn 0;\n} //end of the function AAS_BestReachableFromJumpPadArea\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint AAS_BestReachableArea(vec3_t origin, vec3_t mins, vec3_t maxs, vec3_t goalorigin)\n{\n\tint areanum, i, j, k, l;\n\taas_link_t *areas;\n\tvec3_t absmins, absmaxs;\n\t//vec3_t bbmins, bbmaxs;\n\tvec3_t start, end;\n\taas_trace_t trace;\n\n\tif (!aasworld.loaded)\n\t{\n\t\tbotimport.Print(PRT_ERROR, \"AAS_BestReachableArea: aas not loaded\\n\");\n\t\treturn 0;\n\t} //end if\n\t//find a point in an area\n\tVectorCopy(origin, start);\n\tareanum = AAS_PointAreaNum(start);\n\t//while no area found fudge around a little\n\tfor (i = 0; i < 5 && !areanum; i++)\n\t{\n\t\tfor (j = 0; j < 5 && !areanum; j++)\n\t\t{\n\t\t\tfor (k = -1; k <= 1 && !areanum; k++)\n\t\t\t{\n\t\t\t\tfor (l = -1; l <= 1 && !areanum; l++)\n\t\t\t\t{\n\t\t\t\t\tVectorCopy(origin, start);\n\t\t\t\t\tstart[0] += (float) j * 4 * k;\n\t\t\t\t\tstart[1] += (float) j * 4 * l;\n\t\t\t\t\tstart[2] += (float) i * 4;\n\t\t\t\t\tareanum = AAS_PointAreaNum(start);\n\t\t\t\t} //end for\n\t\t\t} //end for\n\t\t} //end for\n\t} //end for\n\t//if an area was found\n\tif (areanum)\n\t{\n\t\t//drop client bbox down and try again\n\t\tVectorCopy(start, end);\n\t\tstart[2] += 0.25;\n\t\tend[2] -= 50;\n\t\ttrace = AAS_TraceClientBBox(start, end, PRESENCE_CROUCH, -1);\n\t\tif (!trace.startsolid)\n\t\t{\n\t\t\tareanum = AAS_PointAreaNum(trace.endpos);\n\t\t\tVectorCopy(trace.endpos, goalorigin);\n\t\t\t//FIXME: cannot enable next line right now because the reachability\n\t\t\t// does not have to be calculated when the level items are loaded\n\t\t\t//if the origin is in an area with reachability\n\t\t\t//if (AAS_AreaReachability(areanum)) return areanum;\n\t\t\tif (areanum) return areanum;\n\t\t} //end if\n\t\telse\n\t\t{\n\t\t\t//it can very well happen that the AAS_PointAreaNum function tells that\n\t\t\t//a point is in an area and that starting a AAS_TraceClientBBox from that\n\t\t\t//point will return trace.startsolid qtrue\n#if 0\n\t\t\tif (AAS_PointAreaNum(start))\n\t\t\t{\n\t\t\t\tLog_Write(\"point %f %f %f in area %d but trace startsolid\", start[0], start[1], start[2], areanum);\n\t\t\t\tAAS_DrawPermanentCross(start, 4, LINECOLOR_RED);\n\t\t\t} //end if\n\t\t\tbotimport.Print(PRT_MESSAGE, \"AAS_BestReachableArea: start solid\\n\");\n#endif\n\t\t\tVectorCopy(start, goalorigin);\n\t\t\treturn areanum;\n\t\t} //end else\n\t} //end if\n\t//\n\t//AAS_PresenceTypeBoundingBox(PRESENCE_CROUCH, bbmins, bbmaxs);\n\t//NOTE: the goal origin does not have to be in the goal area\n\t// because the bot will have to move towards the item origin anyway\n\tVectorCopy(origin, goalorigin);\n\t//\n\tVectorAdd(origin, mins, absmins);\n\tVectorAdd(origin, maxs, absmaxs);\n\t//add bounding box size\n\t//VectorSubtract(absmins, bbmaxs, absmins);\n\t//VectorSubtract(absmaxs, bbmins, absmaxs);\n\t//link an invalid (-1) entity\n\tareas = AAS_LinkEntityClientBBox(absmins, absmaxs, -1, PRESENCE_CROUCH);\n\t//get the reachable link arae\n\tareanum = AAS_BestReachableLinkArea(areas);\n\t//unlink the invalid entity\n\tAAS_UnlinkFromAreas(areas);\n\t//\n\treturn areanum;\n} //end of the function AAS_BestReachableArea\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid AAS_SetupReachabilityHeap(void)\n{\n\tint i;\n\n\treachabilityheap = (aas_lreachability_t *) GetClearedMemory(\n\t\t\t\t\t\tAAS_MAX_REACHABILITYSIZE * sizeof(aas_lreachability_t));\n\tfor (i = 0; i < AAS_MAX_REACHABILITYSIZE-1; i++)\n\t{\n\t\treachabilityheap[i].next = &reachabilityheap[i+1];\n\t} //end for\n\treachabilityheap[AAS_MAX_REACHABILITYSIZE-1].next = NULL;\n\tnextreachability = reachabilityheap;\n\tnumlreachabilities = 0;\n} //end of the function AAS_InitReachabilityHeap\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid AAS_ShutDownReachabilityHeap(void)\n{\n\tFreeMemory(reachabilityheap);\n\tnumlreachabilities = 0;\n} //end of the function AAS_ShutDownReachabilityHeap\n//===========================================================================\n// returns a reachability link\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\naas_lreachability_t *AAS_AllocReachability(void)\n{\n\taas_lreachability_t *r;\n\n\tif (!nextreachability) return NULL;\n\t//make sure the error message only shows up once\n\tif (!nextreachability->next) AAS_Error(\"AAS_MAX_REACHABILITYSIZE\");\n\t//\n\tr = nextreachability;\n\tnextreachability = nextreachability->next;\n\tnumlreachabilities++;\n\treturn r;\n} //end of the function AAS_AllocReachability\n//===========================================================================\n// frees a reachability link\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid AAS_FreeReachability(aas_lreachability_t *lreach)\n{\n\tCom_Memset(lreach, 0, sizeof(aas_lreachability_t));\n\n\tlreach->next = nextreachability;\n\tnextreachability = lreach;\n\tnumlreachabilities--;\n} //end of the function AAS_FreeReachability\n//===========================================================================\n// returns qtrue if the area has reachability links\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint AAS_AreaReachability(int areanum)\n{\n\tif (areanum < 0 || areanum >= aasworld.numareas)\n\t{\n\t\tAAS_Error(\"AAS_AreaReachability: areanum %d out of range\", areanum);\n\t\treturn 0;\n\t} //end if\n\treturn aasworld.areasettings[areanum].numreachableareas;\n} //end of the function AAS_AreaReachability\n//===========================================================================\n// returns the surface area of all ground faces together of the area\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nfloat AAS_AreaGroundFaceArea(int areanum)\n{\n\tint i;\n\tfloat total;\n\taas_area_t *area;\n\taas_face_t *face;\n\n\ttotal = 0;\n\tarea = &aasworld.areas[areanum];\n\tfor (i = 0; i < area->numfaces; i++)\n\t{\n\t\tface = &aasworld.faces[abs(aasworld.faceindex[area->firstface + i])];\n\t\tif (!(face->faceflags & FACE_GROUND)) continue;\n\t\t//\n\t\ttotal += AAS_FaceArea(face);\n\t} //end for\n\treturn total;\n} //end of the function AAS_AreaGroundFaceArea\n//===========================================================================\n// returns the center of a face\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid AAS_FaceCenter(int facenum, vec3_t center)\n{\n\tint i;\n\tfloat scale;\n\taas_face_t *face;\n\taas_edge_t *edge;\n\n\tface = &aasworld.faces[facenum];\n\n\tVectorClear(center);\n\tfor (i = 0; i < face->numedges; i++)\n\t{\n\t\tedge = &aasworld.edges[abs(aasworld.edgeindex[face->firstedge + i])];\n\t\tVectorAdd(center, aasworld.vertexes[edge->v[0]], center);\n\t\tVectorAdd(center, aasworld.vertexes[edge->v[1]], center);\n\t} //end for\n\tscale = 0.5 / face->numedges;\n\tVectorScale(center, scale, center);\n} //end of the function AAS_FaceCenter\n//===========================================================================\n// returns the maximum distance a player can fall before being damaged\n// damage = deltavelocity*deltavelocity  * 0.0001\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint AAS_FallDamageDistance(void)\n{\n\tfloat maxzvelocity, gravity, t;\n\n\tmaxzvelocity = sqrt(30 * 10000);\n\tgravity = aassettings.phys_gravity;\n\tt = maxzvelocity / gravity;\n\treturn 0.5 * gravity * t * t;\n} //end of the function AAS_FallDamageDistance\n//===========================================================================\n// distance = 0.5 * gravity * t * t\n// vel = t * gravity\n// damage = vel * vel * 0.0001\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nfloat AAS_FallDelta(float distance)\n{\n\tfloat t, delta, gravity;\n\n\tgravity = aassettings.phys_gravity;\n\tt = sqrt(fabs(distance) * 2 / gravity);\n\tdelta = t * gravity;\n\treturn delta * delta * 0.0001;\n} //end of the function AAS_FallDelta\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nfloat AAS_MaxJumpHeight(float phys_jumpvel)\n{\n\tfloat phys_gravity;\n\n\tphys_gravity = aassettings.phys_gravity;\n\t//maximum height a player can jump with the given initial z velocity\n\treturn 0.5 * phys_gravity * (phys_jumpvel / phys_gravity) * (phys_jumpvel / phys_gravity);\n} //end of the function MaxJumpHeight\n//===========================================================================\n// returns true if a player can only crouch in the area\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nfloat AAS_MaxJumpDistance(float phys_jumpvel)\n{\n\tfloat phys_gravity, phys_maxvelocity, t;\n\n\tphys_gravity = aassettings.phys_gravity;\n\tphys_maxvelocity = aassettings.phys_maxvelocity;\n\t//time a player takes to fall the height\n\tt = sqrt(aassettings.rs_maxjumpfallheight / (0.5 * phys_gravity));\n   //maximum distance\n\treturn phys_maxvelocity * (t + phys_jumpvel / phys_gravity);\n} //end of the function AAS_MaxJumpDistance\n//===========================================================================\n// returns true if a player can only crouch in the area\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint AAS_AreaCrouch(int areanum)\n{\n\tif (!(aasworld.areasettings[areanum].presencetype & PRESENCE_NORMAL)) return qtrue;\n\telse return qfalse;\n} //end of the function AAS_AreaCrouch\n//===========================================================================\n// returns qtrue if it is possible to swim in the area\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint AAS_AreaSwim(int areanum)\n{\n\tif (aasworld.areasettings[areanum].areaflags & AREA_LIQUID) return qtrue;\n\telse return qfalse;\n} //end of the function AAS_AreaSwim\n//===========================================================================\n// returns qtrue if the area contains a liquid\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint AAS_AreaLiquid(int areanum)\n{\n\tif (aasworld.areasettings[areanum].areaflags & AREA_LIQUID) return qtrue;\n\telse return qfalse;\n} //end of the function AAS_AreaLiquid\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint AAS_AreaLava(int areanum)\n{\n\treturn (aasworld.areasettings[areanum].contents & AREACONTENTS_LAVA);\n} //end of the function AAS_AreaLava\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint AAS_AreaSlime(int areanum)\n{\n\treturn (aasworld.areasettings[areanum].contents & AREACONTENTS_SLIME);\n} //end of the function AAS_AreaSlime\n//===========================================================================\n// returns qtrue if the area contains ground faces\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint AAS_AreaGrounded(int areanum)\n{\n\treturn (aasworld.areasettings[areanum].areaflags & AREA_GROUNDED);\n} //end of the function AAS_AreaGround\n//===========================================================================\n// returns true if the area contains ladder faces\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint AAS_AreaLadder(int areanum)\n{\n\treturn (aasworld.areasettings[areanum].areaflags & AREA_LADDER);\n} //end of the function AAS_AreaLadder\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint AAS_AreaJumpPad(int areanum)\n{\n\treturn (aasworld.areasettings[areanum].contents & AREACONTENTS_JUMPPAD);\n} //end of the function AAS_AreaJumpPad\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint AAS_AreaTeleporter(int areanum)\n{\n\treturn (aasworld.areasettings[areanum].contents & AREACONTENTS_TELEPORTER);\n} //end of the function AAS_AreaTeleporter\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint AAS_AreaClusterPortal(int areanum)\n{\n\treturn (aasworld.areasettings[areanum].contents & AREACONTENTS_CLUSTERPORTAL);\n} //end of the function AAS_AreaClusterPortal\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint AAS_AreaDoNotEnter(int areanum)\n{\n\treturn (aasworld.areasettings[areanum].contents & AREACONTENTS_DONOTENTER);\n} //end of the function AAS_AreaDoNotEnter\n//===========================================================================\n// returns the time it takes perform a barrier jump\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nunsigned short int AAS_BarrierJumpTravelTime(void)\n{\n\treturn aassettings.phys_jumpvel / (aassettings.phys_gravity * 0.1);\n} //end op the function AAS_BarrierJumpTravelTime\n//===========================================================================\n// returns true if there already exists a reachability from area1 to area2\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nqboolean AAS_ReachabilityExists(int area1num, int area2num)\n{\n\taas_lreachability_t *r;\n\n\tfor (r = areareachability[area1num]; r; r = r->next)\n\t{\n\t\tif (r->areanum == area2num) return qtrue;\n\t} //end for\n\treturn qfalse;\n} //end of the function AAS_ReachabilityExists\n//===========================================================================\n// returns true if there is a solid just after the end point when going\n// from start to end\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint AAS_NearbySolidOrGap(vec3_t start, vec3_t end)\n{\n\tvec3_t dir, testpoint;\n\tint areanum;\n\n\tVectorSubtract(end, start, dir);\n\tdir[2] = 0;\n\tVectorNormalize(dir);\n\tVectorMA(end, 48, dir, testpoint);\n\n\tareanum = AAS_PointAreaNum(testpoint);\n\tif (!areanum)\n\t{\n\t\ttestpoint[2] += 16;\n\t\tareanum = AAS_PointAreaNum(testpoint);\n\t\tif (!areanum) return qtrue;\n\t} //end if\n\tVectorMA(end, 64, dir, testpoint);\n\tareanum = AAS_PointAreaNum(testpoint);\n\tif (areanum)\n\t{\n\t\tif (!AAS_AreaSwim(areanum) && !AAS_AreaGrounded(areanum)) return qtrue;\n\t} //end if\n\treturn qfalse;\n} //end of the function AAS_SolidGapTime\n//===========================================================================\n// searches for swim reachabilities between adjacent areas\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint AAS_Reachability_Swim(int area1num, int area2num)\n{\n\tint i, j, face1num, face2num, side1;\n\taas_area_t *area1, *area2;\n\taas_lreachability_t *lreach;\n\taas_face_t *face1;\n\taas_plane_t *plane;\n\tvec3_t start;\n\n\tif (!AAS_AreaSwim(area1num) || !AAS_AreaSwim(area2num)) return qfalse;\n\t//if the second area is crouch only\n\tif (!(aasworld.areasettings[area2num].presencetype & PRESENCE_NORMAL)) return qfalse;\n\n\tarea1 = &aasworld.areas[area1num];\n\tarea2 = &aasworld.areas[area2num];\n\n\t//if the areas are not near anough\n\tfor (i = 0; i < 3; i++)\n\t{\n\t\tif (area1->mins[i] > area2->maxs[i] + 10) return qfalse;\n\t\tif (area1->maxs[i] < area2->mins[i] - 10) return qfalse;\n\t} //end for\n\t//find a shared face and create a reachability link\n\tfor (i = 0; i < area1->numfaces; i++)\n\t{\n\t\tface1num = aasworld.faceindex[area1->firstface + i];\n\t\tside1 = face1num < 0;\n\t\tface1num = abs(face1num);\n\t\t//\n\t\tfor (j = 0; j < area2->numfaces; j++)\n\t\t{\n\t\t\tface2num = abs(aasworld.faceindex[area2->firstface + j]);\n\t\t\t//\n\t\t\tif (face1num == face2num)\n\t\t\t{\n\t\t\t\tAAS_FaceCenter(face1num, start);\n\t\t\t\t//\n\t\t\t\tif (AAS_PointContents(start) & (CONTENTS_LAVA|CONTENTS_SLIME|CONTENTS_WATER))\n\t\t\t\t{\n\t\t\t\t\t//\n\t\t\t\t\tface1 = &aasworld.faces[face1num];\n\t\t\t\t\t//create a new reachability link\n\t\t\t\t\tlreach = AAS_AllocReachability();\n\t\t\t\t\tif (!lreach) return qfalse;\n\t\t\t\t\tlreach->areanum = area2num;\n\t\t\t\t\tlreach->facenum = face1num;\n\t\t\t\t\tlreach->edgenum = 0;\n\t\t\t\t\tVectorCopy(start, lreach->start);\n\t\t\t\t\tplane = &aasworld.planes[face1->planenum ^ side1];\n\t\t\t\t\tVectorMA(lreach->start, -INSIDEUNITS, plane->normal, lreach->end);\n\t\t\t\t\tlreach->traveltype = TRAVEL_SWIM;\n\t\t\t\t\tlreach->traveltime = 1;\n\t\t\t\t\t//if the volume of the area is rather small\n\t\t\t\t\tif (AAS_AreaVolume(area2num) < 800)\n\t\t\t\t\t\tlreach->traveltime += 200;\n\t\t\t\t\t//if (!(AAS_PointContents(start) & MASK_WATER)) lreach->traveltime += 500;\n\t\t\t\t\t//link the reachability\n\t\t\t\t\tlreach->next = areareachability[area1num];\n\t\t\t\t\tareareachability[area1num] = lreach;\n\t\t\t\t\treach_swim++;\n\t\t\t\t\treturn qtrue;\n\t\t\t\t} //end if\n\t\t\t} //end if\n\t\t} //end for\n\t} //end for\n\treturn qfalse;\n} //end of the function AAS_Reachability_Swim\n//===========================================================================\n// searches for reachabilities between adjacent areas with equal floor\n// heights\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint AAS_Reachability_EqualFloorHeight(int area1num, int area2num)\n{\n\tint i, j, edgenum, edgenum1, edgenum2, foundreach, side;\n\tfloat height, bestheight, length, bestlength;\n\tvec3_t dir, start, end, normal, invgravity, gravitydirection = {0, 0, -1};\n\tvec3_t edgevec;\n\taas_area_t *area1, *area2;\n\taas_face_t *face1, *face2;\n\taas_edge_t *edge;\n\taas_plane_t *plane2;\n\taas_lreachability_t lr, *lreach;\n\n\tif (!AAS_AreaGrounded(area1num) || !AAS_AreaGrounded(area2num)) return qfalse;\n\n\tarea1 = &aasworld.areas[area1num];\n\tarea2 = &aasworld.areas[area2num];\n\t//if the areas are not near anough in the x-y direction\n\tfor (i = 0; i < 2; i++)\n\t{\n\t\tif (area1->mins[i] > area2->maxs[i] + 10) return qfalse;\n\t\tif (area1->maxs[i] < area2->mins[i] - 10) return qfalse;\n\t} //end for\n\t//if area 2 is too high above area 1\n\tif (area2->mins[2] > area1->maxs[2]) return qfalse;\n\t//\n\tVectorCopy(gravitydirection, invgravity);\n\tVectorInverse(invgravity);\n\t//\n\tbestheight = 99999;\n\tbestlength = 0;\n\tfoundreach = qfalse;\n\tCom_Memset(&lr, 0, sizeof(aas_lreachability_t)); //make the compiler happy\n\t//\n\t//check if the areas have ground faces with a common edge\n\t//if existing use the lowest common edge for a reachability link\n\tfor (i = 0; i < area1->numfaces; i++)\n\t{\n\t\tface1 = &aasworld.faces[abs(aasworld.faceindex[area1->firstface + i])];\n\t\tif (!(face1->faceflags & FACE_GROUND)) continue;\n\t\t//\n\t\tfor (j = 0; j < area2->numfaces; j++)\n\t\t{\n\t\t\tface2 = &aasworld.faces[abs(aasworld.faceindex[area2->firstface + j])];\n\t\t\tif (!(face2->faceflags & FACE_GROUND)) continue;\n\t\t\t//if there is a common edge\n\t\t\tfor (edgenum1 = 0; edgenum1 < face1->numedges; edgenum1++)\n\t\t\t{\n\t\t\t\tfor (edgenum2 = 0; edgenum2 < face2->numedges; edgenum2++)\n\t\t\t\t{\n\t\t\t\t\tif (abs(aasworld.edgeindex[face1->firstedge + edgenum1]) !=\n\t\t\t\t\t\t\tabs(aasworld.edgeindex[face2->firstedge + edgenum2]))\n\t\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\tedgenum = aasworld.edgeindex[face1->firstedge + edgenum1];\n\t\t\t\t\tside = edgenum < 0;\n\t\t\t\t\tedge = &aasworld.edges[abs(edgenum)];\n\t\t\t\t\t//get the length of the edge\n\t\t\t\t\tVectorSubtract(aasworld.vertexes[edge->v[1]],\n\t\t\t\t\t\t\t\taasworld.vertexes[edge->v[0]], dir);\n\t\t\t\t\tlength = VectorLength(dir);\n\t\t\t\t\t//get the start point\n\t\t\t\t\tVectorAdd(aasworld.vertexes[edge->v[0]],\n\t\t\t\t\t\t\t\taasworld.vertexes[edge->v[1]], start);\n\t\t\t\t\tVectorScale(start, 0.5, start);\n\t\t\t\t\tVectorCopy(start, end);\n\t\t\t\t\t//get the end point several units inside area2\n\t\t\t\t\t//and the start point several units inside area1\n\t\t\t\t\t//NOTE: normal is pointing into area2 because the\n\t\t\t\t\t//face edges are stored counter clockwise\n\t\t\t\t\tVectorSubtract(aasworld.vertexes[edge->v[side]],\n\t\t\t\t\t\t\t\taasworld.vertexes[edge->v[!side]], edgevec);\n\t\t\t\t\tplane2 = &aasworld.planes[face2->planenum];\n\t\t\t\t\tCrossProduct(edgevec, plane2->normal, normal);\n\t\t\t\t\tVectorNormalize(normal);\n\t\t\t\t\t//\n\t\t\t\t\t//VectorMA(start, -1, normal, start);\n\t\t\t\t\tVectorMA(end, INSIDEUNITS_WALKEND, normal, end);\n\t\t\t\t\tVectorMA(start, INSIDEUNITS_WALKSTART, normal, start);\n\t\t\t\t\tend[2] += 0.125;\n\t\t\t\t\t//\n\t\t\t\t\theight = DotProduct(invgravity, start);\n\t\t\t\t\t//NOTE: if there's nearby solid or a gap area after this area\n\t\t\t\t\t//disabled this crap\n\t\t\t\t\t//if (AAS_NearbySolidOrGap(start, end)) height += 200;\n\t\t\t\t\t//NOTE: disabled because it disables reachabilities to very small areas\n\t\t\t\t\t//if (AAS_PointAreaNum(end) != area2num) continue;\n\t\t\t\t\t//get the longest lowest edge\n\t\t\t\t\tif (height < bestheight ||\n\t\t\t\t\t\t\t(height < bestheight + 1 && length > bestlength))\n\t\t\t\t\t{\n\t\t\t\t\t\tbestheight = height;\n\t\t\t\t\t\tbestlength = length;\n\t\t\t\t\t\t//create a new reachability link\n\t\t\t\t\t\tlr.areanum = area2num;\n\t\t\t\t\t\tlr.facenum = 0;\n\t\t\t\t\t\tlr.edgenum = edgenum;\n\t\t\t\t\t\tVectorCopy(start, lr.start);\n\t\t\t\t\t\tVectorCopy(end, lr.end);\n\t\t\t\t\t\tlr.traveltype = TRAVEL_WALK;\n\t\t\t\t\t\tlr.traveltime = 1;\n\t\t\t\t\t\tfoundreach = qtrue;\n\t\t\t\t\t} //end if\n\t\t\t\t} //end for\n\t\t\t} //end for\n\t\t} //end for\n\t} //end for\n\tif (foundreach)\n\t{\n\t\t//create a new reachability link\n\t\tlreach = AAS_AllocReachability();\n\t\tif (!lreach) return qfalse;\n\t\tlreach->areanum = lr.areanum;\n\t\tlreach->facenum = lr.facenum;\n\t\tlreach->edgenum = lr.edgenum;\n\t\tVectorCopy(lr.start, lreach->start);\n\t\tVectorCopy(lr.end, lreach->end);\n\t\tlreach->traveltype = lr.traveltype;\n\t\tlreach->traveltime = lr.traveltime;\n\t\tlreach->next = areareachability[area1num];\n\t\tareareachability[area1num] = lreach;\n\t\t//if going into a crouch area\n\t\tif (!AAS_AreaCrouch(area1num) && AAS_AreaCrouch(area2num))\n\t\t{\n\t\t\tlreach->traveltime += aassettings.rs_startcrouch;\n\t\t} //end if\n\t\t/*\n\t\t//NOTE: if there's nearby solid or a gap area after this area\n\t\tif (!AAS_NearbySolidOrGap(lreach->start, lreach->end))\n\t\t{\n\t\t\tlreach->traveltime += 100;\n\t\t} //end if\n\t\t*/\n\t\t//avoid rather small areas\n\t\t//if (AAS_AreaGroundFaceArea(lreach->areanum) < 500) lreach->traveltime += 100;\n\t\t//\n\t\treach_equalfloor++;\n\t\treturn qtrue;\n\t} //end if\n\treturn qfalse;\n} //end of the function AAS_Reachability_EqualFloorHeight\n//===========================================================================\n// searches step, barrier, waterjump and walk off ledge reachabilities\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\n#ifdef _MSC_VER\n#pragma optimize(\"\", off)\t//work around msvc ICE\n#endif\nint AAS_Reachability_Step_Barrier_WaterJump_WalkOffLedge(int area1num, int area2num)\n{\n\tint i, j, k, l, edge1num, edge2num, areas[10], numareas;\n\tint ground_bestarea2groundedgenum, ground_foundreach;\n\tint water_bestarea2groundedgenum, water_foundreach;\n\tint side1, area1swim, faceside1, groundface1num;\n\tfloat dist, dist1, dist2, diff, ortdot;\n\tfloat x1, x2, x3, x4, y1, y2, y3, y4, tmp, y;\n\tfloat length, ground_bestlength, water_bestlength, ground_bestdist, water_bestdist;\n\tvec3_t v1, v2, v3, v4, tmpv, p1area1, p1area2, p2area1, p2area2;\n\tvec3_t normal, ort, edgevec, start, end, dir;\n\tvec3_t ground_beststart = {0, 0, 0}, ground_bestend = {0, 0, 0}, ground_bestnormal = {0, 0, 0};\n\tvec3_t water_beststart = {0, 0, 0}, water_bestend = {0, 0, 0}, water_bestnormal = {0, 0, 0};\n\tvec3_t invgravity = {0, 0, 1};\n\tvec3_t testpoint;\n\taas_plane_t *plane;\n\taas_area_t *area1, *area2;\n\taas_face_t *groundface1, *groundface2;\n\taas_edge_t *edge1, *edge2;\n\taas_lreachability_t *lreach;\n\taas_trace_t trace;\n\n\t//must be able to walk or swim in the first area\n\tif (!AAS_AreaGrounded(area1num) && !AAS_AreaSwim(area1num)) return qfalse;\n\t//\n\tif (!AAS_AreaGrounded(area2num) && !AAS_AreaSwim(area2num)) return qfalse;\n\t//\n\tarea1 = &aasworld.areas[area1num];\n\tarea2 = &aasworld.areas[area2num];\n\t//if the first area contains a liquid\n\tarea1swim = AAS_AreaSwim(area1num);\n\t//if the areas are not near anough in the x-y direction\n\tfor (i = 0; i < 2; i++)\n\t{\n\t\tif (area1->mins[i] > area2->maxs[i] + 10) return qfalse;\n\t\tif (area1->maxs[i] < area2->mins[i] - 10) return qfalse;\n\t} //end for\n\t//\n\tground_foundreach = qfalse;\n\tground_bestdist = 99999;\n\tground_bestlength = 0;\n\tground_bestarea2groundedgenum = 0;\n\t//\n\twater_foundreach = qfalse;\n\twater_bestdist = 99999;\n\twater_bestlength = 0;\n\twater_bestarea2groundedgenum = 0;\n\t//\n\tfor (i = 0; i < area1->numfaces; i++)\n\t{\n\t\tgroundface1num = aasworld.faceindex[area1->firstface + i];\n\t\tfaceside1 = groundface1num < 0;\n\t\tgroundface1 = &aasworld.faces[abs(groundface1num)];\n\t\t//if this isn't a ground face\n\t\tif (!(groundface1->faceflags & FACE_GROUND))\n\t\t{\n\t\t\t//if we can swim in the first area\n\t\t\tif (area1swim)\n\t\t\t{\n\t\t\t\t//face plane must be more or less horizontal\n\t\t\t\tplane = &aasworld.planes[groundface1->planenum ^ (!faceside1)];\n\t\t\t\tif (DotProduct(plane->normal, invgravity) < 0.7) continue;\n\t\t\t} //end if\n\t\t\telse\n\t\t\t{\n\t\t\t\t//if we can't swim in the area it must be a ground face\n\t\t\t\tcontinue;\n\t\t\t} //end else\n\t\t} //end if\n\t\t//\n\t\tfor (k = 0; k < groundface1->numedges; k++)\n\t\t{\n\t\t\tedge1num = aasworld.edgeindex[groundface1->firstedge + k];\n\t\t\tside1 = (edge1num < 0);\n\t\t\t//NOTE: for water faces we must take the side area 1 is\n\t\t\t// on into account because the face is shared and doesn't\n\t\t\t// have to be oriented correctly\n\t\t\tif (!(groundface1->faceflags & FACE_GROUND)) side1 = (side1 == faceside1);\n\t\t\tedge1num = abs(edge1num);\n\t\t\tedge1 = &aasworld.edges[edge1num];\n\t\t\t//vertexes of the edge\n\t\t\tVectorCopy(aasworld.vertexes[edge1->v[!side1]], v1);\n\t\t\tVectorCopy(aasworld.vertexes[edge1->v[side1]], v2);\n\t\t\t//get a vertical plane through the edge\n\t\t\t//NOTE: normal is pointing into area 2 because the\n\t\t\t//face edges are stored counter clockwise\n\t\t\tVectorSubtract(v2, v1, edgevec);\n\t\t\tCrossProduct(edgevec, invgravity, normal);\n\t\t\tVectorNormalize(normal);\n\t\t\tdist = DotProduct(normal, v1);\n\t\t\t//check the faces from the second area\n\t\t\tfor (j = 0; j < area2->numfaces; j++)\n\t\t\t{\n\t\t\t\tgroundface2 = &aasworld.faces[abs(aasworld.faceindex[area2->firstface + j])];\n\t\t\t\t//must be a ground face\n\t\t\t\tif (!(groundface2->faceflags & FACE_GROUND)) continue;\n\t\t\t\t//check the edges of this ground face\n\t\t\t\tfor (l = 0; l < groundface2->numedges; l++)\n\t\t\t\t{\n\t\t\t\t\tedge2num = abs(aasworld.edgeindex[groundface2->firstedge + l]);\n\t\t\t\t\tedge2 = &aasworld.edges[edge2num];\n\t\t\t\t\t//vertexes of the edge\n\t\t\t\t\tVectorCopy(aasworld.vertexes[edge2->v[0]], v3);\n\t\t\t\t\tVectorCopy(aasworld.vertexes[edge2->v[1]], v4);\n\t\t\t\t\t//check the distance between the two points and the vertical plane\n\t\t\t\t\t//through the edge of area1\n\t\t\t\t\tdiff = DotProduct(normal, v3) - dist;\n\t\t\t\t\tif (diff < -0.1 || diff > 0.1) continue;\n\t\t\t\t\tdiff = DotProduct(normal, v4) - dist;\n\t\t\t\t\tif (diff < -0.1 || diff > 0.1) continue;\n\t\t\t\t\t//\n\t\t\t\t\t//project the two ground edges into the step side plane\n\t\t\t\t\t//and calculate the shortest distance between the two\n\t\t\t\t\t//edges if they overlap in the direction orthogonal to\n\t\t\t\t\t//the gravity direction\n\t\t\t\t\tCrossProduct(invgravity, normal, ort);\n\t\t\t\t\t//invgravitydot = DotProduct(invgravity, invgravity);\n\t\t\t\t\tortdot = DotProduct(ort, ort);\n\t\t\t\t\t//projection into the step plane\n\t\t\t\t\t//NOTE: since gravity is vertical this is just the z coordinate\n\t\t\t\t\ty1 = v1[2];//DotProduct(v1, invgravity) / invgravitydot;\n\t\t\t\t\ty2 = v2[2];//DotProduct(v2, invgravity) / invgravitydot;\n\t\t\t\t\ty3 = v3[2];//DotProduct(v3, invgravity) / invgravitydot;\n\t\t\t\t\ty4 = v4[2];//DotProduct(v4, invgravity) / invgravitydot;\n\t\t\t\t\t//\n\t\t\t\t\tx1 = DotProduct(v1, ort) / ortdot;\n\t\t\t\t\tx2 = DotProduct(v2, ort) / ortdot;\n\t\t\t\t\tx3 = DotProduct(v3, ort) / ortdot;\n\t\t\t\t\tx4 = DotProduct(v4, ort) / ortdot;\n\t\t\t\t\t//\n\t\t\t\t\tif (x1 > x2)\n\t\t\t\t\t{\n\t\t\t\t\t\ttmp = x1; x1 = x2; x2 = tmp;\n\t\t\t\t\t\ttmp = y1; y1 = y2; y2 = tmp;\n\t\t\t\t\t\tVectorCopy(v1, tmpv); VectorCopy(v2, v1); VectorCopy(tmpv, v2);\n\t\t\t\t\t} //end if\n\t\t\t\t\tif (x3 > x4)\n\t\t\t\t\t{\n\t\t\t\t\t\ttmp = x3; x3 = x4; x4 = tmp;\n\t\t\t\t\t\ttmp = y3; y3 = y4; y4 = tmp;\n\t\t\t\t\t\tVectorCopy(v3, tmpv); VectorCopy(v4, v3); VectorCopy(tmpv, v4);\n\t\t\t\t\t} //end if\n\t\t\t\t\t//if the two projected edge lines have no overlap\n\t\t\t\t\tif (x2 <= x3 || x4 <= x1)\n\t\t\t\t\t{\n//\t\t\t\t\t\tLog_Write(\"lines no overlap: from area %d to %d\\r\\n\", area1num, area2num);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t} //end if\n\t\t\t\t\t//if the two lines fully overlap\n\t\t\t\t\tif ((x1 - 0.5 < x3 && x4 < x2 + 0.5) &&\n\t\t\t\t\t\t\t(x3 - 0.5 < x1 && x2 < x4 + 0.5))\n\t\t\t\t\t{\n\t\t\t\t\t\tdist1 = y3 - y1;\n\t\t\t\t\t\tdist2 = y4 - y2;\n\t\t\t\t\t\tVectorCopy(v1, p1area1);\n\t\t\t\t\t\tVectorCopy(v2, p2area1);\n\t\t\t\t\t\tVectorCopy(v3, p1area2);\n\t\t\t\t\t\tVectorCopy(v4, p2area2);\n\t\t\t\t\t} //end if\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\t//if the points are equal\n\t\t\t\t\t\tif (x1 > x3 - 0.1 && x1 < x3 + 0.1)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tdist1 = y3 - y1;\n\t\t\t\t\t\t\tVectorCopy(v1, p1area1);\n\t\t\t\t\t\t\tVectorCopy(v3, p1area2);\n\t\t\t\t\t\t} //end if\n\t\t\t\t\t\telse if (x1 < x3)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ty = y1 + (x3 - x1) * (y2 - y1) / (x2 - x1);\n\t\t\t\t\t\t\tdist1 = y3 - y;\n\t\t\t\t\t\t\tVectorCopy(v3, p1area1);\n\t\t\t\t\t\t\tp1area1[2] = y;\n\t\t\t\t\t\t\tVectorCopy(v3, p1area2);\n\t\t\t\t\t\t} //end if\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ty = y3 + (x1 - x3) * (y4 - y3) / (x4 - x3);\n\t\t\t\t\t\t\tdist1 = y - y1;\n\t\t\t\t\t\t\tVectorCopy(v1, p1area1);\n\t\t\t\t\t\t\tVectorCopy(v1, p1area2);\n\t\t\t\t\t\t\tp1area2[2] = y;\n\t\t\t\t\t\t} //end if\n\t\t\t\t\t\t//if the points are equal\n\t\t\t\t\t\tif (x2 > x4 - 0.1 && x2 < x4 + 0.1)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tdist2 = y4 - y2;\n\t\t\t\t\t\t\tVectorCopy(v2, p2area1);\n\t\t\t\t\t\t\tVectorCopy(v4, p2area2);\n\t\t\t\t\t\t} //end if\n\t\t\t\t\t\telse if (x2 < x4)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ty = y3 + (x2 - x3) * (y4 - y3) / (x4 - x3);\n\t\t\t\t\t\t\tdist2 = y - y2;\n\t\t\t\t\t\t\tVectorCopy(v2, p2area1);\n\t\t\t\t\t\t\tVectorCopy(v2, p2area2);\n\t\t\t\t\t\t\tp2area2[2] = y;\n\t\t\t\t\t\t} //end if\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ty = y1 + (x4 - x1) * (y2 - y1) / (x2 - x1);\n\t\t\t\t\t\t\tdist2 = y4 - y;\n\t\t\t\t\t\t\tVectorCopy(v4, p2area1);\n\t\t\t\t\t\t\tp2area1[2] = y;\n\t\t\t\t\t\t\tVectorCopy(v4, p2area2);\n\t\t\t\t\t\t} //end else\n\t\t\t\t\t} //end else\n\t\t\t\t\t//if both distances are pretty much equal\n\t\t\t\t\t//then we take the middle of the points\n\t\t\t\t\tif (dist1 > dist2 - 1 && dist1 < dist2 + 1)\n\t\t\t\t\t{\n\t\t\t\t\t\tdist = dist1;\n\t\t\t\t\t\tVectorAdd(p1area1, p2area1, start);\n\t\t\t\t\t\tVectorScale(start, 0.5, start);\n\t\t\t\t\t\tVectorAdd(p1area2, p2area2, end);\n\t\t\t\t\t\tVectorScale(end, 0.5, end);\n\t\t\t\t\t} //end if\n\t\t\t\t\telse if (dist1 < dist2)\n\t\t\t\t\t{\n\t\t\t\t\t\tdist = dist1;\n\t\t\t\t\t\tVectorCopy(p1area1, start);\n\t\t\t\t\t\tVectorCopy(p1area2, end);\n\t\t\t\t\t} //end else if\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tdist = dist2;\n\t\t\t\t\t\tVectorCopy(p2area1, start);\n\t\t\t\t\t\tVectorCopy(p2area2, end);\n\t\t\t\t\t} //end else\n\t\t\t\t\t//get the length of the overlapping part of the edges of the two areas\n\t\t\t\t\tVectorSubtract(p2area2, p1area2, dir);\n\t\t\t\t\tlength = VectorLength(dir);\n\t\t\t\t\t//\n\t\t\t\t\tif (groundface1->faceflags & FACE_GROUND)\n\t\t\t\t\t{\n\t\t\t\t\t\t//if the vertical distance is smaller\n\t\t\t\t\t\tif (dist < ground_bestdist ||\n\t\t\t\t\t\t\t\t//or the vertical distance is pretty much the same\n\t\t\t\t\t\t\t\t//but the overlapping part of the edges is longer\n\t\t\t\t\t\t\t\t(dist < ground_bestdist + 1 && length > ground_bestlength))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tground_bestdist = dist;\n\t\t\t\t\t\t\tground_bestlength = length;\n\t\t\t\t\t\t\tground_foundreach = qtrue;\n\t\t\t\t\t\t\tground_bestarea2groundedgenum = edge1num;\n\t\t\t\t\t\t\t//best point towards area1\n\t\t\t\t\t\t\tVectorCopy(start, ground_beststart);\n\t\t\t\t\t\t\t//normal is pointing into area2\n\t\t\t\t\t\t\tVectorCopy(normal, ground_bestnormal);\n\t\t\t\t\t\t\t//best point towards area2\n\t\t\t\t\t\t\tVectorCopy(end, ground_bestend);\n\t\t\t\t\t\t} //end if\n\t\t\t\t\t} //end if\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\t//if the vertical distance is smaller\n\t\t\t\t\t\tif (dist < water_bestdist ||\n\t\t\t\t\t\t\t\t//or the vertical distance is pretty much the same\n\t\t\t\t\t\t\t\t//but the overlapping part of the edges is longer\n\t\t\t\t\t\t\t\t(dist < water_bestdist + 1 && length > water_bestlength))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\twater_bestdist = dist;\n\t\t\t\t\t\t\twater_bestlength = length;\n\t\t\t\t\t\t\twater_foundreach = qtrue;\n\t\t\t\t\t\t\twater_bestarea2groundedgenum = edge1num;\n\t\t\t\t\t\t\t//best point towards area1\n\t\t\t\t\t\t\tVectorCopy(start, water_beststart);\n\t\t\t\t\t\t\t//normal is pointing into area2\n\t\t\t\t\t\t\tVectorCopy(normal, water_bestnormal);\n\t\t\t\t\t\t\t//best point towards area2\n\t\t\t\t\t\t\tVectorCopy(end, water_bestend);\n\t\t\t\t\t\t} //end if\n\t\t\t\t\t} //end else\n\t\t\t\t} //end for\n\t\t\t} //end for\n\t\t} //end for\n\t} //end for\n\t//\n\t// NOTE: swim reachabilities are already filtered out\n\t//\n\t// Steps\n\t//\n\t//        ---------\n\t//        |          step height -> TRAVEL_WALK\n\t//--------|\n\t//\n\t//        ---------\n\t//~~~~~~~~|          step height and low water -> TRAVEL_WALK\n\t//--------|\n\t//\n\t//~~~~~~~~~~~~~~~~~~\n\t//        ---------\n\t//        |          step height and low water up to the step -> TRAVEL_WALK\n\t//--------|\n\t//\n\t//check for a step reachability\n\tif (ground_foundreach)\n\t{\n\t\t//if area2 is higher but lower than the maximum step height\n\t\t//NOTE: ground_bestdist >= 0 also catches equal floor reachabilities\n\t\tif (ground_bestdist >= 0 && ground_bestdist < aassettings.phys_maxstep)\n\t\t{\n\t\t\t//create walk reachability from area1 to area2\n\t\t\tlreach = AAS_AllocReachability();\n\t\t\tif (!lreach) return qfalse;\n\t\t\tlreach->areanum = area2num;\n\t\t\tlreach->facenum = 0;\n\t\t\tlreach->edgenum = ground_bestarea2groundedgenum;\n\t\t\tVectorMA(ground_beststart, INSIDEUNITS_WALKSTART, ground_bestnormal, lreach->start);\n\t\t\tVectorMA(ground_bestend, INSIDEUNITS_WALKEND, ground_bestnormal, lreach->end);\n\t\t\tlreach->traveltype = TRAVEL_WALK;\n\t\t\tlreach->traveltime = 0;//1;\n\t\t\t//if going into a crouch area\n\t\t\tif (!AAS_AreaCrouch(area1num) && AAS_AreaCrouch(area2num))\n\t\t\t{\n\t\t\t\tlreach->traveltime += aassettings.rs_startcrouch;\n\t\t\t} //end if\n\t\t\tlreach->next = areareachability[area1num];\n\t\t\tareareachability[area1num] = lreach;\n\t\t\t//NOTE: if there's nearby solid or a gap area after this area\n\t\t\t/*\n\t\t\tif (!AAS_NearbySolidOrGap(lreach->start, lreach->end))\n\t\t\t{\n\t\t\t\tlreach->traveltime += 100;\n\t\t\t} //end if\n\t\t\t*/\n\t\t\t//avoid rather small areas\n\t\t\t//if (AAS_AreaGroundFaceArea(lreach->areanum) < 500) lreach->traveltime += 100;\n\t\t\t//\n\t\t\treach_step++;\n\t\t\treturn qtrue;\n\t\t} //end if\n\t} //end if\n\t//\n\t// Water Jumps\n\t//\n\t//        ---------\n\t//        |\n\t//~~~~~~~~|\n\t//        |\n\t//        |          higher than step height and water up to waterjump height -> TRAVEL_WATERJUMP\n\t//--------|\n\t//\n\t//~~~~~~~~~~~~~~~~~~\n\t//        ---------\n\t//        |\n\t//        |\n\t//        |\n\t//        |          higher than step height and low water up to the step -> TRAVEL_WATERJUMP\n\t//--------|\n\t//\n\t//check for a waterjump reachability\n\tif (water_foundreach)\n\t{\n\t\t//get a test point a little bit towards area1\n\t\tVectorMA(water_bestend, -INSIDEUNITS, water_bestnormal, testpoint);\n\t\t//go down the maximum waterjump height\n\t\ttestpoint[2] -= aassettings.phys_maxwaterjump;\n\t\t//if there IS water the sv_maxwaterjump height below the bestend point\n\t\tif (aasworld.areasettings[AAS_PointAreaNum(testpoint)].areaflags & AREA_LIQUID)\n\t\t{\n\t\t\t//don't create rediculous water jump reachabilities from areas very far below\n\t\t\t//the water surface\n\t\t\tif (water_bestdist < aassettings.phys_maxwaterjump + 24)\n\t\t\t{\n\t\t\t\t//waterjumping from or towards a crouch only area is not possible in Quake2\n\t\t\t\tif ((aasworld.areasettings[area1num].presencetype & PRESENCE_NORMAL) &&\n\t\t\t\t\t\t(aasworld.areasettings[area2num].presencetype & PRESENCE_NORMAL))\n\t\t\t\t{\n\t\t\t\t\t//create water jump reachability from area1 to area2\n\t\t\t\t\tlreach = AAS_AllocReachability();\n\t\t\t\t\tif (!lreach) return qfalse;\n\t\t\t\t\tlreach->areanum = area2num;\n\t\t\t\t\tlreach->facenum = 0;\n\t\t\t\t\tlreach->edgenum = water_bestarea2groundedgenum;\n\t\t\t\t\tVectorCopy(water_beststart, lreach->start);\n\t\t\t\t\tVectorMA(water_bestend, INSIDEUNITS_WATERJUMP, water_bestnormal, lreach->end);\n\t\t\t\t\tlreach->traveltype = TRAVEL_WATERJUMP;\n\t\t\t\t\tlreach->traveltime = aassettings.rs_waterjump;\n\t\t\t\t\tlreach->next = areareachability[area1num];\n\t\t\t\t\tareareachability[area1num] = lreach;\n\t\t\t\t\t//we've got another waterjump reachability\n\t\t\t\t\treach_waterjump++;\n\t\t\t\t\treturn qtrue;\n\t\t\t\t} //end if\n\t\t\t} //end if\n\t\t} //end if\n\t} //end if\n\t//\n\t// Barrier Jumps\n\t//\n\t//        ---------\n\t//        |\n\t//        |\n\t//        |\n\t//        |         higher than step height lower than barrier height -> TRAVEL_BARRIERJUMP\n\t//--------|\n\t//\n\t//        ---------\n\t//        |\n\t//        |\n\t//        |\n\t//~~~~~~~~|         higher than step height lower than barrier height\n\t//--------|         and a thin layer of water in the area to jump from -> TRAVEL_BARRIERJUMP\n\t//\n\t//check for a barrier jump reachability\n\tif (ground_foundreach)\n\t{\n\t\t//if area2 is higher but lower than the maximum barrier jump height\n\t\tif (ground_bestdist > 0 && ground_bestdist < aassettings.phys_maxbarrier)\n\t\t{\n\t\t\t//if no water in area1 or a very thin layer of water on the ground\n\t\t\tif (!water_foundreach || (ground_bestdist - water_bestdist < 16))\n\t\t\t{\n\t\t\t\t//cannot perform a barrier jump towards or from a crouch area in Quake2\n\t\t\t\tif (!AAS_AreaCrouch(area1num) && !AAS_AreaCrouch(area2num))\n\t\t\t\t{\n\t\t\t\t\t//create barrier jump reachability from area1 to area2\n\t\t\t\t\tlreach = AAS_AllocReachability();\n\t\t\t\t\tif (!lreach) return qfalse;\n\t\t\t\t\tlreach->areanum = area2num;\n\t\t\t\t\tlreach->facenum = 0;\n\t\t\t\t\tlreach->edgenum = ground_bestarea2groundedgenum;\n\t\t\t\t\tVectorMA(ground_beststart, INSIDEUNITS_WALKSTART, ground_bestnormal, lreach->start);\n\t\t\t\t\tVectorMA(ground_bestend, INSIDEUNITS_WALKEND, ground_bestnormal, lreach->end);\n\t\t\t\t\tlreach->traveltype = TRAVEL_BARRIERJUMP;\n\t\t\t\t\tlreach->traveltime = aassettings.rs_barrierjump;//AAS_BarrierJumpTravelTime();\n\t\t\t\t\tlreach->next = areareachability[area1num];\n\t\t\t\t\tareareachability[area1num] = lreach;\n\t\t\t\t\t//we've got another barrierjump reachability\n\t\t\t\t\treach_barrier++;\n\t\t\t\t\treturn qtrue;\n\t\t\t\t} //end if\n\t\t\t} //end if\n\t\t} //end if\n\t} //end if\n\t//\n\t// Walk and Walk Off Ledge\n\t//\n\t//--------|\n\t//        |          can walk or step back -> TRAVEL_WALK\n\t//        ---------\n\t//\n\t//--------|\n\t//        |\n\t//        |\n\t//        |\n\t//        |          cannot walk/step back -> TRAVEL_WALKOFFLEDGE\n\t//        ---------\n\t//\n\t//--------|\n\t//        |\n\t//        |~~~~~~~~\n\t//        |\n\t//        |          cannot step back but can waterjump back -> TRAVEL_WALKOFFLEDGE\n\t//        ---------  FIXME: create TRAVEL_WALK reach??\n\t//\n\t//check for a walk or walk off ledge reachability\n\tif (ground_foundreach)\n\t{\n\t\tif (ground_bestdist < 0)\n\t\t{\n\t\t\tif (ground_bestdist > -aassettings.phys_maxstep)\n\t\t\t{\n\t\t\t\t//create walk reachability from area1 to area2\n\t\t\t\tlreach = AAS_AllocReachability();\n\t\t\t\tif (!lreach) return qfalse;\n\t\t\t\tlreach->areanum = area2num;\n\t\t\t\tlreach->facenum = 0;\n\t\t\t\tlreach->edgenum = ground_bestarea2groundedgenum;\n\t\t\t\tVectorMA(ground_beststart, INSIDEUNITS_WALKSTART, ground_bestnormal, lreach->start);\n\t\t\t\tVectorMA(ground_bestend, INSIDEUNITS_WALKEND, ground_bestnormal, lreach->end);\n\t\t\t\tlreach->traveltype = TRAVEL_WALK;\n\t\t\t\tlreach->traveltime = 1;\n\t\t\t\tlreach->next = areareachability[area1num];\n\t\t\t\tareareachability[area1num] = lreach;\n\t\t\t\t//we've got another walk reachability\n\t\t\t\treach_walk++;\n\t\t\t\treturn qtrue;\n\t\t\t} //end if\n\t\t\t// if no maximum fall height set or less than the max\n\t\t\tif (!aassettings.rs_maxfallheight || fabs(ground_bestdist) < aassettings.rs_maxfallheight) {\n\t\t\t\t//trace a bounding box vertically to check for solids\n\t\t\t\tVectorMA(ground_bestend, INSIDEUNITS, ground_bestnormal, ground_bestend);\n\t\t\t\tVectorCopy(ground_bestend, start);\n\t\t\t\tstart[2] = ground_beststart[2];\n\t\t\t\tVectorCopy(ground_bestend, end);\n\t\t\t\tend[2] += 4;\n\t\t\t\ttrace = AAS_TraceClientBBox(start, end, PRESENCE_NORMAL, -1);\n\t\t\t\t//if no solids were found\n\t\t\t\tif (!trace.startsolid && trace.fraction >= 1.0)\n\t\t\t\t{\n\t\t\t\t\t//the trace end point must be in the goal area\n\t\t\t\t\ttrace.endpos[2] += 1;\n\t\t\t\t\tif (AAS_PointAreaNum(trace.endpos) == area2num)\n\t\t\t\t\t{\n\t\t\t\t\t\t//if not going through a cluster portal\n\t\t\t\t\t\tnumareas = AAS_TraceAreas(start, end, areas, NULL, ARRAY_LEN(areas));\n\t\t\t\t\t\tfor (i = 0; i < numareas; i++)\n\t\t\t\t\t\t\tif (AAS_AreaClusterPortal(areas[i]))\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tif (i >= numareas)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t//create a walk off ledge reachability from area1 to area2\n\t\t\t\t\t\t\tlreach = AAS_AllocReachability();\n\t\t\t\t\t\t\tif (!lreach) return qfalse;\n\t\t\t\t\t\t\tlreach->areanum = area2num;\n\t\t\t\t\t\t\tlreach->facenum = 0;\n\t\t\t\t\t\t\tlreach->edgenum = ground_bestarea2groundedgenum;\n\t\t\t\t\t\t\tVectorCopy(ground_beststart, lreach->start);\n\t\t\t\t\t\t\tVectorCopy(ground_bestend, lreach->end);\n\t\t\t\t\t\t\tlreach->traveltype = TRAVEL_WALKOFFLEDGE;\n\t\t\t\t\t\t\tlreach->traveltime = aassettings.rs_startwalkoffledge + fabs(ground_bestdist) * 50 / aassettings.phys_gravity;\n\t\t\t\t\t\t\t//if falling from too high and not falling into water\n\t\t\t\t\t\t\tif (!AAS_AreaSwim(area2num) && !AAS_AreaJumpPad(area2num))\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tif (AAS_FallDelta(ground_bestdist) > aassettings.phys_falldelta5)\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tlreach->traveltime += aassettings.rs_falldamage5;\n\t\t\t\t\t\t\t\t} //end if\n\t\t\t\t\t\t\t\tif (AAS_FallDelta(ground_bestdist) > aassettings.phys_falldelta10)\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tlreach->traveltime += aassettings.rs_falldamage10;\n\t\t\t\t\t\t\t\t} //end if\n\t\t\t\t\t\t\t} //end if\n\t\t\t\t\t\t\tlreach->next = areareachability[area1num];\n\t\t\t\t\t\t\tareareachability[area1num] = lreach;\n\t\t\t\t\t\t\t//\n\t\t\t\t\t\t\treach_walkoffledge++;\n\t\t\t\t\t\t\t//NOTE: don't create a weapon (rl, bfg) jump reachability here\n\t\t\t\t\t\t\t//because it interferes with other reachabilities\n\t\t\t\t\t\t\t//like the ladder reachability\n\t\t\t\t\t\t\treturn qtrue;\n\t\t\t\t\t\t} //end if\n\t\t\t\t\t} //end if\n\t\t\t\t} //end if\n\t\t\t} //end if\n\t\t} //end else\n\t} //end if\n\treturn qfalse;\n} //end of the function AAS_Reachability_Step_Barrier_WaterJump_WalkOffLedge\n#ifdef _MSC_VER\n#pragma optimize(\"\", on)\t//work around msvc ICE\n#endif\n//===========================================================================\n// returns the distance between the two vectors\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nfloat VectorDistance(vec3_t v1, vec3_t v2)\n{\n\tvec3_t dir;\n\n\tVectorSubtract(v2, v1, dir);\n\treturn VectorLength(dir);\n} //end of the function VectorDistance\n//===========================================================================\n// returns true if the first vector is between the last two vectors\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint VectorBetweenVectors(vec3_t v, vec3_t v1, vec3_t v2)\n{\n\tvec3_t dir1, dir2;\n\n\tVectorSubtract(v, v1, dir1);\n\tVectorSubtract(v, v2, dir2);\n\treturn (DotProduct(dir1, dir2) <= 0);\n} //end of the function VectorBetweenVectors\n//===========================================================================\n// returns the mid point between the two vectors\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid VectorMiddle(vec3_t v1, vec3_t v2, vec3_t middle)\n{\n\tVectorAdd(v1, v2, middle);\n\tVectorScale(middle, 0.5, middle);\n} //end of the function VectorMiddle\n//===========================================================================\n// calculate a range of points closest to each other on both edges\n//\n// Parameter:\t\t\tbeststart1\t\tstart of the range of points on edge v1-v2\n//\t\t\t\t\t\tbeststart2\t\tend of the range of points  on edge v1-v2\n//\t\t\t\t\t\tbestend1\t\tstart of the range of points on edge v3-v4\n//\t\t\t\t\t\tbestend2\t\tend of the range of points  on edge v3-v4\n//\t\t\t\t\t\tbestdist\t\tbest distance so far\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\n/*\nfloat AAS_ClosestEdgePoints(vec3_t v1, vec3_t v2, vec3_t v3, vec3_t v4,\n\t\t\t\t\t\t\taas_plane_t *plane1, aas_plane_t *plane2,\n\t\t\t\t\t\t\tvec3_t beststart, vec3_t bestend, float bestdist)\n{\n\tvec3_t dir1, dir2, p1, p2, p3, p4;\n\tfloat a1, a2, b1, b2, dist;\n\tint founddist;\n\n\t//edge vectors\n\tVectorSubtract(v2, v1, dir1);\n\tVectorSubtract(v4, v3, dir2);\n\t//get the horizontal directions\n\tdir1[2] = 0;\n\tdir2[2] = 0;\n\t//\n\t// p1 = point on an edge vector of area2 closest to v1\n\t// p2 = point on an edge vector of area2 closest to v2\n\t// p3 = point on an edge vector of area1 closest to v3\n\t// p4 = point on an edge vector of area1 closest to v4\n\t//\n\tif (dir2[0])\n\t{\n\t\ta2 = dir2[1] / dir2[0];\n\t\tb2 = v3[1] - a2 * v3[0];\n\t\t//point on the edge vector of area2 closest to v1\n\t\tp1[0] = (DotProduct(v1, dir2) - (a2 * dir2[0] + b2 * dir2[1])) / dir2[0];\n\t\tp1[1] = a2 * p1[0] + b2;\n\t\t//point on the edge vector of area2 closest to v2\n\t\tp2[0] = (DotProduct(v2, dir2) - (a2 * dir2[0] + b2 * dir2[1])) / dir2[0];\n\t\tp2[1] = a2 * p2[0] + b2;\n\t} //end if\n\telse\n\t{\n\t\t//point on the edge vector of area2 closest to v1\n\t\tp1[0] = v3[0];\n\t\tp1[1] = v1[1];\n\t\t//point on the edge vector of area2 closest to v2\n\t\tp2[0] = v3[0];\n\t\tp2[1] = v2[1];\n\t} //end else\n\t//\n\tif (dir1[0])\n\t{\n\t\t//\n\t\ta1 = dir1[1] / dir1[0];\n\t\tb1 = v1[1] - a1 * v1[0];\n\t\t//point on the edge vector of area1 closest to v3\n\t\tp3[0] = (DotProduct(v3, dir1) - (a1 * dir1[0] + b1 * dir1[1])) / dir1[0];\n\t\tp3[1] = a1 * p3[0] + b1;\n\t\t//point on the edge vector of area1 closest to v4\n\t\tp4[0] = (DotProduct(v4, dir1) - (a1 * dir1[0] + b1 * dir1[1])) / dir1[0];\n\t\tp4[1] = a1 * p4[0] + b1;\n\t} //end if\n\telse\n\t{\n\t\t//point on the edge vector of area1 closest to v3\n\t\tp3[0] = v1[0];\n\t\tp3[1] = v3[1];\n\t\t//point on the edge vector of area1 closest to v4\n\t\tp4[0] = v1[0];\n\t\tp4[1] = v4[1];\n\t} //end else\n\t//start with zero z-coordinates\n\tp1[2] = 0;\n\tp2[2] = 0;\n\tp3[2] = 0;\n\tp4[2] = 0;\n\t//calculate the z-coordinates from the ground planes\n\tp1[2] = (plane2->dist - DotProduct(plane2->normal, p1)) / plane2->normal[2];\n\tp2[2] = (plane2->dist - DotProduct(plane2->normal, p2)) / plane2->normal[2];\n\tp3[2] = (plane1->dist - DotProduct(plane1->normal, p3)) / plane1->normal[2];\n\tp4[2] = (plane1->dist - DotProduct(plane1->normal, p4)) / plane1->normal[2];\n\t//\n\tfounddist = qfalse;\n\t//\n\tif (VectorBetweenVectors(p1, v3, v4))\n\t{\n\t\tdist = VectorDistance(v1, p1);\n\t\tif (dist > bestdist - 0.5 && dist < bestdist + 0.5)\n\t\t{\n\t\t\tVectorMiddle(beststart, v1, beststart);\n\t\t\tVectorMiddle(bestend, p1, bestend);\n\t\t} //end if\n\t\telse if (dist < bestdist)\n\t\t{\n\t\t\tbestdist = dist;\n\t\t\tVectorCopy(v1, beststart);\n\t\t\tVectorCopy(p1, bestend);\n\t\t} //end if\n\t\tfounddist = qtrue;\n\t} //end if\n\tif (VectorBetweenVectors(p2, v3, v4))\n\t{\n\t\tdist = VectorDistance(v2, p2);\n\t\tif (dist > bestdist - 0.5 && dist < bestdist + 0.5)\n\t\t{\n\t\t\tVectorMiddle(beststart, v2, beststart);\n\t\t\tVectorMiddle(bestend, p2, bestend);\n\t\t} //end if\n\t\telse if (dist < bestdist)\n\t\t{\n\t\t\tbestdist = dist;\n\t\t\tVectorCopy(v2, beststart);\n\t\t\tVectorCopy(p2, bestend);\n\t\t} //end if\n\t\tfounddist = qtrue;\n\t} //end else if\n\tif (VectorBetweenVectors(p3, v1, v2))\n\t{\n\t\tdist = VectorDistance(v3, p3);\n\t\tif (dist > bestdist - 0.5 && dist < bestdist + 0.5)\n\t\t{\n\t\t\tVectorMiddle(beststart, p3, beststart);\n\t\t\tVectorMiddle(bestend, v3, bestend);\n\t\t} //end if\n\t\telse if (dist < bestdist)\n\t\t{\n\t\t\tbestdist = dist;\n\t\t\tVectorCopy(p3, beststart);\n\t\t\tVectorCopy(v3, bestend);\n\t\t} //end if\n\t\tfounddist = qtrue;\n\t} //end else if\n\tif (VectorBetweenVectors(p4, v1, v2))\n\t{\n\t\tdist = VectorDistance(v4, p4);\n\t\tif (dist > bestdist - 0.5 && dist < bestdist + 0.5)\n\t\t{\n\t\t\tVectorMiddle(beststart, p4, beststart);\n\t\t\tVectorMiddle(bestend, v4, bestend);\n\t\t} //end if\n\t\telse if (dist < bestdist)\n\t\t{\n\t\t\tbestdist = dist;\n\t\t\tVectorCopy(p4, beststart);\n\t\t\tVectorCopy(v4, bestend);\n\t\t} //end if\n\t\tfounddist = qtrue;\n\t} //end else if\n\t//if no shortest distance was found the shortest distance\n\t//is between one of the vertexes of edge1 and one of edge2\n\tif (!founddist)\n\t{\n\t\tdist = VectorDistance(v1, v3);\n\t\tif (dist < bestdist)\n\t\t{\n\t\t\tbestdist = dist;\n\t\t\tVectorCopy(v1, beststart);\n\t\t\tVectorCopy(v3, bestend);\n\t\t} //end if\n\t\tdist = VectorDistance(v1, v4);\n\t\tif (dist < bestdist)\n\t\t{\n\t\t\tbestdist = dist;\n\t\t\tVectorCopy(v1, beststart);\n\t\t\tVectorCopy(v4, bestend);\n\t\t} //end if\n\t\tdist = VectorDistance(v2, v3);\n\t\tif (dist < bestdist)\n\t\t{\n\t\t\tbestdist = dist;\n\t\t\tVectorCopy(v2, beststart);\n\t\t\tVectorCopy(v3, bestend);\n\t\t} //end if\n\t\tdist = VectorDistance(v2, v4);\n\t\tif (dist < bestdist)\n\t\t{\n\t\t\tbestdist = dist;\n\t\t\tVectorCopy(v2, beststart);\n\t\t\tVectorCopy(v4, bestend);\n\t\t} //end if\n\t} //end if\n\treturn bestdist;\n} //end of the function AAS_ClosestEdgePoints*/\n\nfloat AAS_ClosestEdgePoints(vec3_t v1, vec3_t v2, vec3_t v3, vec3_t v4,\n\t\t\t\t\t\t\taas_plane_t *plane1, aas_plane_t *plane2,\n\t\t\t\t\t\t\tvec3_t beststart1, vec3_t bestend1,\n\t\t\t\t\t\t\tvec3_t beststart2, vec3_t bestend2, float bestdist)\n{\n\tvec3_t dir1, dir2, p1, p2, p3, p4;\n\tfloat a1, a2, b1, b2, dist, dist1, dist2;\n\tint founddist;\n\n\t//edge vectors\n\tVectorSubtract(v2, v1, dir1);\n\tVectorSubtract(v4, v3, dir2);\n\t//get the horizontal directions\n\tdir1[2] = 0;\n\tdir2[2] = 0;\n\t//\n\t// p1 = point on an edge vector of area2 closest to v1\n\t// p2 = point on an edge vector of area2 closest to v2\n\t// p3 = point on an edge vector of area1 closest to v3\n\t// p4 = point on an edge vector of area1 closest to v4\n\t//\n\tif (dir2[0])\n\t{\n\t\ta2 = dir2[1] / dir2[0];\n\t\tb2 = v3[1] - a2 * v3[0];\n\t\t//point on the edge vector of area2 closest to v1\n\t\tp1[0] = (DotProduct(v1, dir2) - (a2 * dir2[0] + b2 * dir2[1])) / dir2[0];\n\t\tp1[1] = a2 * p1[0] + b2;\n\t\t//point on the edge vector of area2 closest to v2\n\t\tp2[0] = (DotProduct(v2, dir2) - (a2 * dir2[0] + b2 * dir2[1])) / dir2[0];\n\t\tp2[1] = a2 * p2[0] + b2;\n\t} //end if\n\telse\n\t{\n\t\t//point on the edge vector of area2 closest to v1\n\t\tp1[0] = v3[0];\n\t\tp1[1] = v1[1];\n\t\t//point on the edge vector of area2 closest to v2\n\t\tp2[0] = v3[0];\n\t\tp2[1] = v2[1];\n\t} //end else\n\t//\n\tif (dir1[0])\n\t{\n\t\t//\n\t\ta1 = dir1[1] / dir1[0];\n\t\tb1 = v1[1] - a1 * v1[0];\n\t\t//point on the edge vector of area1 closest to v3\n\t\tp3[0] = (DotProduct(v3, dir1) - (a1 * dir1[0] + b1 * dir1[1])) / dir1[0];\n\t\tp3[1] = a1 * p3[0] + b1;\n\t\t//point on the edge vector of area1 closest to v4\n\t\tp4[0] = (DotProduct(v4, dir1) - (a1 * dir1[0] + b1 * dir1[1])) / dir1[0];\n\t\tp4[1] = a1 * p4[0] + b1;\n\t} //end if\n\telse\n\t{\n\t\t//point on the edge vector of area1 closest to v3\n\t\tp3[0] = v1[0];\n\t\tp3[1] = v3[1];\n\t\t//point on the edge vector of area1 closest to v4\n\t\tp4[0] = v1[0];\n\t\tp4[1] = v4[1];\n\t} //end else\n\t//start with zero z-coordinates\n\tp1[2] = 0;\n\tp2[2] = 0;\n\tp3[2] = 0;\n\tp4[2] = 0;\n\t//calculate the z-coordinates from the ground planes\n\tp1[2] = (plane2->dist - DotProduct(plane2->normal, p1)) / plane2->normal[2];\n\tp2[2] = (plane2->dist - DotProduct(plane2->normal, p2)) / plane2->normal[2];\n\tp3[2] = (plane1->dist - DotProduct(plane1->normal, p3)) / plane1->normal[2];\n\tp4[2] = (plane1->dist - DotProduct(plane1->normal, p4)) / plane1->normal[2];\n\t//\n\tfounddist = qfalse;\n\t//\n\tif (VectorBetweenVectors(p1, v3, v4))\n\t{\n\t\tdist = VectorDistance(v1, p1);\n\t\tif (dist > bestdist - 0.5 && dist < bestdist + 0.5)\n\t\t{\n\t\t\tdist1 = VectorDistance(beststart1, v1);\n\t\t\tdist2 = VectorDistance(beststart2, v1);\n\t\t\tif (dist1 > dist2)\n\t\t\t{\n\t\t\t\tif (dist1 > VectorDistance(beststart1, beststart2)) VectorCopy(v1, beststart2);\n\t\t\t} //end if\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (dist2 > VectorDistance(beststart1, beststart2)) VectorCopy(v1, beststart1);\n\t\t\t} //end else\n\t\t\tdist1 = VectorDistance(bestend1, p1);\n\t\t\tdist2 = VectorDistance(bestend2, p1);\n\t\t\tif (dist1 > dist2)\n\t\t\t{\n\t\t\t\tif (dist1 > VectorDistance(bestend1, bestend2)) VectorCopy(p1, bestend2);\n\t\t\t} //end if\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (dist2 > VectorDistance(bestend1, bestend2)) VectorCopy(p1, bestend1);\n\t\t\t} //end else\n\t\t} //end if\n\t\telse if (dist < bestdist)\n\t\t{\n\t\t\tbestdist = dist;\n\t\t\tVectorCopy(v1, beststart1);\n\t\t\tVectorCopy(v1, beststart2);\n\t\t\tVectorCopy(p1, bestend1);\n\t\t\tVectorCopy(p1, bestend2);\n\t\t} //end if\n\t\tfounddist = qtrue;\n\t} //end if\n\tif (VectorBetweenVectors(p2, v3, v4))\n\t{\n\t\tdist = VectorDistance(v2, p2);\n\t\tif (dist > bestdist - 0.5 && dist < bestdist + 0.5)\n\t\t{\n\t\t\tdist1 = VectorDistance(beststart1, v2);\n\t\t\tdist2 = VectorDistance(beststart2, v2);\n\t\t\tif (dist1 > dist2)\n\t\t\t{\n\t\t\t\tif (dist1 > VectorDistance(beststart1, beststart2)) VectorCopy(v2, beststart2);\n\t\t\t} //end if\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (dist2 > VectorDistance(beststart1, beststart2)) VectorCopy(v2, beststart1);\n\t\t\t} //end else\n\t\t\tdist1 = VectorDistance(bestend1, p2);\n\t\t\tdist2 = VectorDistance(bestend2, p2);\n\t\t\tif (dist1 > dist2)\n\t\t\t{\n\t\t\t\tif (dist1 > VectorDistance(bestend1, bestend2)) VectorCopy(p2, bestend2);\n\t\t\t} //end if\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (dist2 > VectorDistance(bestend1, bestend2)) VectorCopy(p2, bestend1);\n\t\t\t} //end else\n\t\t} //end if\n\t\telse if (dist < bestdist)\n\t\t{\n\t\t\tbestdist = dist;\n\t\t\tVectorCopy(v2, beststart1);\n\t\t\tVectorCopy(v2, beststart2);\n\t\t\tVectorCopy(p2, bestend1);\n\t\t\tVectorCopy(p2, bestend2);\n\t\t} //end if\n\t\tfounddist = qtrue;\n\t} //end else if\n\tif (VectorBetweenVectors(p3, v1, v2))\n\t{\n\t\tdist = VectorDistance(v3, p3);\n\t\tif (dist > bestdist - 0.5 && dist < bestdist + 0.5)\n\t\t{\n\t\t\tdist1 = VectorDistance(beststart1, p3);\n\t\t\tdist2 = VectorDistance(beststart2, p3);\n\t\t\tif (dist1 > dist2)\n\t\t\t{\n\t\t\t\tif (dist1 > VectorDistance(beststart1, beststart2)) VectorCopy(p3, beststart2);\n\t\t\t} //end if\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (dist2 > VectorDistance(beststart1, beststart2)) VectorCopy(p3, beststart1);\n\t\t\t} //end else\n\t\t\tdist1 = VectorDistance(bestend1, v3);\n\t\t\tdist2 = VectorDistance(bestend2, v3);\n\t\t\tif (dist1 > dist2)\n\t\t\t{\n\t\t\t\tif (dist1 > VectorDistance(bestend1, bestend2)) VectorCopy(v3, bestend2);\n\t\t\t} //end if\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (dist2 > VectorDistance(bestend1, bestend2)) VectorCopy(v3, bestend1);\n\t\t\t} //end else\n\t\t} //end if\n\t\telse if (dist < bestdist)\n\t\t{\n\t\t\tbestdist = dist;\n\t\t\tVectorCopy(p3, beststart1);\n\t\t\tVectorCopy(p3, beststart2);\n\t\t\tVectorCopy(v3, bestend1);\n\t\t\tVectorCopy(v3, bestend2);\n\t\t} //end if\n\t\tfounddist = qtrue;\n\t} //end else if\n\tif (VectorBetweenVectors(p4, v1, v2))\n\t{\n\t\tdist = VectorDistance(v4, p4);\n\t\tif (dist > bestdist - 0.5 && dist < bestdist + 0.5)\n\t\t{\n\t\t\tdist1 = VectorDistance(beststart1, p4);\n\t\t\tdist2 = VectorDistance(beststart2, p4);\n\t\t\tif (dist1 > dist2)\n\t\t\t{\n\t\t\t\tif (dist1 > VectorDistance(beststart1, beststart2)) VectorCopy(p4, beststart2);\n\t\t\t} //end if\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (dist2 > VectorDistance(beststart1, beststart2)) VectorCopy(p4, beststart1);\n\t\t\t} //end else\n\t\t\tdist1 = VectorDistance(bestend1, v4);\n\t\t\tdist2 = VectorDistance(bestend2, v4);\n\t\t\tif (dist1 > dist2)\n\t\t\t{\n\t\t\t\tif (dist1 > VectorDistance(bestend1, bestend2)) VectorCopy(v4, bestend2);\n\t\t\t} //end if\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (dist2 > VectorDistance(bestend1, bestend2)) VectorCopy(v4, bestend1);\n\t\t\t} //end else\n\t\t} //end if\n\t\telse if (dist < bestdist)\n\t\t{\n\t\t\tbestdist = dist;\n\t\t\tVectorCopy(p4, beststart1);\n\t\t\tVectorCopy(p4, beststart2);\n\t\t\tVectorCopy(v4, bestend1);\n\t\t\tVectorCopy(v4, bestend2);\n\t\t} //end if\n\t\tfounddist = qtrue;\n\t} //end else if\n\t//if no shortest distance was found the shortest distance\n\t//is between one of the vertexes of edge1 and one of edge2\n\tif (!founddist)\n\t{\n\t\tdist = VectorDistance(v1, v3);\n\t\tif (dist < bestdist)\n\t\t{\n\t\t\tbestdist = dist;\n\t\t\tVectorCopy(v1, beststart1);\n\t\t\tVectorCopy(v1, beststart2);\n\t\t\tVectorCopy(v3, bestend1);\n\t\t\tVectorCopy(v3, bestend2);\n\t\t} //end if\n\t\tdist = VectorDistance(v1, v4);\n\t\tif (dist < bestdist)\n\t\t{\n\t\t\tbestdist = dist;\n\t\t\tVectorCopy(v1, beststart1);\n\t\t\tVectorCopy(v1, beststart2);\n\t\t\tVectorCopy(v4, bestend1);\n\t\t\tVectorCopy(v4, bestend2);\n\t\t} //end if\n\t\tdist = VectorDistance(v2, v3);\n\t\tif (dist < bestdist)\n\t\t{\n\t\t\tbestdist = dist;\n\t\t\tVectorCopy(v2, beststart1);\n\t\t\tVectorCopy(v2, beststart2);\n\t\t\tVectorCopy(v3, bestend1);\n\t\t\tVectorCopy(v3, bestend2);\n\t\t} //end if\n\t\tdist = VectorDistance(v2, v4);\n\t\tif (dist < bestdist)\n\t\t{\n\t\t\tbestdist = dist;\n\t\t\tVectorCopy(v2, beststart1);\n\t\t\tVectorCopy(v2, beststart2);\n\t\t\tVectorCopy(v4, bestend1);\n\t\t\tVectorCopy(v4, bestend2);\n\t\t} //end if\n\t} //end if\n\treturn bestdist;\n} //end of the function AAS_ClosestEdgePoints\n//===========================================================================\n// creates possible jump reachabilities between the areas\n//\n// The two closest points on the ground of the areas are calculated\n// One of the points will be on an edge of a ground face of area1 and\n// one on an edge of a ground face of area2.\n// If there is a range of closest points the point in the middle of this range\n// is selected.\n// Between these two points there must be one or more gaps.\n// If the gaps exist a potential jump is predicted.\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint AAS_Reachability_Jump(int area1num, int area2num)\n{\n\tint i, j, k, l, face1num, face2num, edge1num, edge2num, traveltype;\n\tint stopevent, areas[10], numareas;\n\tfloat phys_jumpvel, maxjumpdistance, maxjumpheight, height, bestdist, speed;\n\tvec_t *v1, *v2, *v3, *v4;\n\tvec3_t beststart, beststart2, bestend, bestend2;\n\tvec3_t teststart, testend, dir, velocity, cmdmove, up = {0, 0, 1}, sidewards;\n\taas_area_t *area1, *area2;\n\taas_face_t *face1, *face2;\n\taas_edge_t *edge1, *edge2;\n\taas_plane_t *plane1, *plane2, *plane;\n\taas_trace_t trace;\n\taas_clientmove_t move;\n\taas_lreachability_t *lreach;\n\n\tif (!AAS_AreaGrounded(area1num) || !AAS_AreaGrounded(area2num)) return qfalse;\n\t//cannot jump from or to a crouch area\n\tif (AAS_AreaCrouch(area1num) || AAS_AreaCrouch(area2num)) return qfalse;\n\t//\n\tarea1 = &aasworld.areas[area1num];\n\tarea2 = &aasworld.areas[area2num];\n\t//\n\tphys_jumpvel = aassettings.phys_jumpvel;\n\t//maximum distance a player can jump\n\tmaxjumpdistance = 2 * AAS_MaxJumpDistance(phys_jumpvel);\n\t//maximum height a player can jump with the given initial z velocity\n\tmaxjumpheight = AAS_MaxJumpHeight(phys_jumpvel);\n\n\t//if the areas are not near anough in the x-y direction\n\tfor (i = 0; i < 2; i++)\n\t{\n\t\tif (area1->mins[i] > area2->maxs[i] + maxjumpdistance) return qfalse;\n\t\tif (area1->maxs[i] < area2->mins[i] - maxjumpdistance) return qfalse;\n\t} //end for\n\t//if area2 is way to high to jump up to\n\tif (area2->mins[2] > area1->maxs[2] + maxjumpheight) return qfalse;\n\t//\n\tbestdist = 999999;\n\t//\n\tfor (i = 0; i < area1->numfaces; i++)\n\t{\n\t\tface1num = aasworld.faceindex[area1->firstface + i];\n\t\tface1 = &aasworld.faces[abs(face1num)];\n\t\t//if not a ground face\n\t\tif (!(face1->faceflags & FACE_GROUND)) continue;\n\t\t//\n\t\tfor (j = 0; j < area2->numfaces; j++)\n\t\t{\n\t\t\tface2num = aasworld.faceindex[area2->firstface + j];\n\t\t\tface2 = &aasworld.faces[abs(face2num)];\n\t\t\t//if not a ground face\n\t\t\tif (!(face2->faceflags & FACE_GROUND)) continue;\n\t\t\t//\n\t\t\tfor (k = 0; k < face1->numedges; k++)\n\t\t\t{\n\t\t\t\tedge1num = abs(aasworld.edgeindex[face1->firstedge + k]);\n\t\t\t\tedge1 = &aasworld.edges[edge1num];\n\t\t\t\tfor (l = 0; l < face2->numedges; l++)\n\t\t\t\t{\n\t\t\t\t\tedge2num = abs(aasworld.edgeindex[face2->firstedge + l]);\n\t\t\t\t\tedge2 = &aasworld.edges[edge2num];\n\t\t\t\t\t//calculate the minimum distance between the two edges\n\t\t\t\t\tv1 = aasworld.vertexes[edge1->v[0]];\n\t\t\t\t\tv2 = aasworld.vertexes[edge1->v[1]];\n\t\t\t\t\tv3 = aasworld.vertexes[edge2->v[0]];\n\t\t\t\t\tv4 = aasworld.vertexes[edge2->v[1]];\n\t\t\t\t\t//get the ground planes\n\t\t\t\t\tplane1 = &aasworld.planes[face1->planenum];\n\t\t\t\t\tplane2 = &aasworld.planes[face2->planenum];\n\t\t\t\t\t//\n\t\t\t\t\tbestdist = AAS_ClosestEdgePoints(v1, v2, v3, v4, plane1, plane2,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tbeststart, bestend,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tbeststart2, bestend2, bestdist);\n\t\t\t\t} //end for\n\t\t\t} //end for\n\t\t} //end for\n\t} //end for\n\tVectorMiddle(beststart, beststart2, beststart);\n\tVectorMiddle(bestend, bestend2, bestend);\n\tif (bestdist > 4 && bestdist < maxjumpdistance)\n\t{\n//\t\tLog_Write(\"shortest distance between %d and %d is %f\\r\\n\", area1num, area2num, bestdist);\n\t\t// if very close and almost no height difference then the bot can walk\n\t\tif (bestdist <= 48 && fabs(beststart[2] - bestend[2]) < 8)\n\t\t{\n\t\t\tspeed = 400;\n\t\t\ttraveltype = TRAVEL_WALKOFFLEDGE;\n\t\t} //end if\n\t\telse if (AAS_HorizontalVelocityForJump(0, beststart, bestend, &speed))\n\t\t{\n\t\t\t//FIXME: why multiply with 1.2???\n\t\t\tspeed *= 1.2f;\n\t\t\ttraveltype = TRAVEL_WALKOFFLEDGE;\n\t\t} //end else if\n\t\telse\n\t\t{\n\t\t\t//get the horizontal speed for the jump, if it isn't possible to calculate this\n\t\t\t//speed (the jump is not possible) then there's no jump reachability created\n\t\t\tif (!AAS_HorizontalVelocityForJump(phys_jumpvel, beststart, bestend, &speed))\n\t\t\t\treturn qfalse;\n\t\t\tspeed *= 1.05f;\n\t\t\ttraveltype = TRAVEL_JUMP;\n\t\t\t//\n\t\t\t//NOTE: test if the horizontal distance isn't too small\n\t\t\tVectorSubtract(bestend, beststart, dir);\n\t\t\tdir[2] = 0;\n\t\t\tif (VectorLength(dir) < 10)\n\t\t\t\treturn qfalse;\n\t\t} //end if\n\t\t//\n\t\tVectorSubtract(bestend, beststart, dir);\n\t\tVectorNormalize(dir);\n\t\tVectorMA(beststart, 1, dir, teststart);\n\t\t//\n\t\tVectorCopy(teststart, testend);\n\t\ttestend[2] -= 100;\n\t\ttrace = AAS_TraceClientBBox(teststart, testend, PRESENCE_NORMAL, -1);\n\t\t//\n\t\tif (trace.startsolid)\n\t\t\treturn qfalse;\n\t\tif (trace.fraction < 1)\n\t\t{\n\t\t\tplane = &aasworld.planes[trace.planenum];\n\t\t\t// if the bot can stand on the surface\n\t\t\tif (DotProduct(plane->normal, up) >= 0.7)\n\t\t\t{\n\t\t\t\t// if no lava or slime below\n\t\t\t\tif (!(AAS_PointContents(trace.endpos) & (CONTENTS_LAVA|CONTENTS_SLIME)))\n\t\t\t\t{\n\t\t\t\t\tif (teststart[2] - trace.endpos[2] <= aassettings.phys_maxbarrier)\n\t\t\t\t\t\treturn qfalse;\n\t\t\t\t} //end if\n\t\t\t} //end if\n\t\t} //end if\n\t\t//\n\t\tVectorMA(bestend, -1, dir, teststart);\n\t\t//\n\t\tVectorCopy(teststart, testend);\n\t\ttestend[2] -= 100;\n\t\ttrace = AAS_TraceClientBBox(teststart, testend, PRESENCE_NORMAL, -1);\n\t\t//\n\t\tif (trace.startsolid)\n\t\t\treturn qfalse;\n\t\tif (trace.fraction < 1)\n\t\t{\n\t\t\tplane = &aasworld.planes[trace.planenum];\n\t\t\t// if the bot can stand on the surface\n\t\t\tif (DotProduct(plane->normal, up) >= 0.7)\n\t\t\t{\n\t\t\t\t// if no lava or slime below\n\t\t\t\tif (!(AAS_PointContents(trace.endpos) & (CONTENTS_LAVA|CONTENTS_SLIME)))\n\t\t\t\t{\n\t\t\t\t\tif (teststart[2] - trace.endpos[2] <= aassettings.phys_maxbarrier)\n\t\t\t\t\t\treturn qfalse;\n\t\t\t\t} //end if\n\t\t\t} //end if\n\t\t} //end if\n\t\t//\n\t\t// get command movement\n\t\tVectorClear(cmdmove);\n\t\tif ((traveltype & TRAVELTYPE_MASK) == TRAVEL_JUMP)\n\t\t\tcmdmove[2] = aassettings.phys_jumpvel;\n\t\telse\n\t\t\tcmdmove[2] = 0;\n\t\t//\n\t\tVectorSubtract(bestend, beststart, dir);\n\t\tdir[2] = 0;\n\t\tVectorNormalize(dir);\n\t\tCrossProduct(dir, up, sidewards);\n\t\t//\n\t\tstopevent = SE_HITGROUND|SE_ENTERWATER|SE_ENTERSLIME|SE_ENTERLAVA|SE_HITGROUNDDAMAGE;\n\t\tif (!AAS_AreaClusterPortal(area1num) && !AAS_AreaClusterPortal(area2num))\n\t\t\tstopevent |= SE_TOUCHCLUSTERPORTAL;\n\t\t//\n\t\tfor (i = 0; i < 3; i++)\n\t\t{\n\t\t\t//\n\t\t\tif (i == 1)\n\t\t\t\tVectorAdd(testend, sidewards, testend);\n\t\t\telse if (i == 2)\n\t\t\t\tVectorSubtract(bestend, sidewards, testend);\n\t\t\telse\n\t\t\t\tVectorCopy(bestend, testend);\n\t\t\tVectorSubtract(testend, beststart, dir);\n\t\t\tdir[2] = 0;\n\t\t\tVectorNormalize(dir);\n\t\t\tVectorScale(dir, speed, velocity);\n\t\t\t//\n\t\t\tAAS_PredictClientMovement(&move, -1, beststart, PRESENCE_NORMAL, qtrue,\n\t\t\t\t\t\t\t\t\t\tvelocity, cmdmove, 3, 30, 0.1f,\n\t\t\t\t\t\t\t\t\t\tstopevent, 0, qfalse);\n\t\t\t// if prediction time wasn't enough to fully predict the movement\n\t\t\tif (move.frames >= 30)\n\t\t\t\treturn qfalse;\n\t\t\t// don't enter slime or lava and don't fall from too high\n\t\t\tif (move.stopevent & (SE_ENTERSLIME|SE_ENTERLAVA))\n\t\t\t\treturn qfalse;\n\t\t\t// never jump or fall through a cluster portal\n\t\t\tif (move.stopevent & SE_TOUCHCLUSTERPORTAL)\n\t\t\t\treturn qfalse;\n\t\t\t//the end position should be in area2, also test a little bit back\n\t\t\t//because the predicted jump could have rushed through the area\n\t\t\tVectorMA(move.endpos, -64, dir, teststart);\n\t\t\tteststart[2] += 1;\n\t\t\tnumareas = AAS_TraceAreas(move.endpos, teststart, areas, NULL, ARRAY_LEN(areas));\n\t\t\tfor (j = 0; j < numareas; j++)\n\t\t\t{\n\t\t\t\tif (areas[j] == area2num)\n\t\t\t\t\tbreak;\n\t\t\t} //end for\n\t\t\tif (j < numareas)\n\t\t\t\tbreak;\n\t\t}\n\t\tif (i >= 3)\n\t\t\treturn qfalse;\n\t\t//\n#ifdef REACH_DEBUG\n\t\t//create the reachability\n\t\tLog_Write(\"jump reachability between %d and %d\\r\\n\", area1num, area2num);\n#endif //REACH_DEBUG\n\t\t//create a new reachability link\n\t\tlreach = AAS_AllocReachability();\n\t\tif (!lreach) return qfalse;\n\t\tlreach->areanum = area2num;\n\t\tlreach->facenum = 0;\n\t\tlreach->edgenum = 0;\n\t\tVectorCopy(beststart, lreach->start);\n\t\tVectorCopy(bestend, lreach->end);\n\t\tlreach->traveltype = traveltype;\n\n\t\tVectorSubtract(bestend, beststart, dir);\n\t\theight = dir[2];\n\t\tdir[2] = 0;\n\t\tif ((traveltype & TRAVELTYPE_MASK) == TRAVEL_WALKOFFLEDGE && height > VectorLength(dir))\n\t\t{\n\t\t\tlreach->traveltime = aassettings.rs_startwalkoffledge + height * 50 / aassettings.phys_gravity;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tlreach->traveltime = aassettings.rs_startjump + VectorDistance(bestend, beststart) * 240 / aassettings.phys_maxwalkvelocity;\n\t\t} //end if\n\t\t//\n\t\tif (!AAS_AreaJumpPad(area2num))\n\t\t{\n\t\t\tif (AAS_FallDelta(beststart[2] - bestend[2]) > aassettings.phys_falldelta5)\n\t\t\t{\n\t\t\t\tlreach->traveltime += aassettings.rs_falldamage5;\n\t\t\t} //end if\n\t\t\telse if (AAS_FallDelta(beststart[2] - bestend[2]) > aassettings.phys_falldelta10)\n\t\t\t{\n\t\t\t\tlreach->traveltime += aassettings.rs_falldamage10;\n\t\t\t} //end if\n\t\t} //end if\n\t\tlreach->next = areareachability[area1num];\n\t\tareareachability[area1num] = lreach;\n\t\t//\n\t\tif ((traveltype & TRAVELTYPE_MASK) == TRAVEL_JUMP)\n\t\t\treach_jump++;\n\t\telse\n\t\t\treach_walkoffledge++;\n\t} //end if\n\treturn qfalse;\n} //end of the function AAS_Reachability_Jump\n//===========================================================================\n// create a possible ladder reachability from area1 to area2\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint AAS_Reachability_Ladder(int area1num, int area2num)\n{\n\tint i, j, k, l, edge1num, edge2num, sharededgenum = 0, lowestedgenum = 0;\n\tint face1num, face2num, ladderface1num = 0, ladderface2num = 0;\n\tint ladderface1vertical, ladderface2vertical, firstv;\n\tfloat face1area, face2area, bestface1area = -9999, bestface2area = -9999;\n\tfloat phys_jumpvel, maxjumpheight;\n\tvec3_t area1point, area2point, v1, v2, up = {0, 0, 1};\n\tvec3_t mid, lowestpoint = {0, 0}, start, end, sharededgevec, dir;\n\taas_area_t *area1, *area2;\n\taas_face_t *face1, *face2, *ladderface1 = NULL, *ladderface2 = NULL;\n\taas_plane_t *plane1, *plane2;\n\taas_edge_t *sharededge, *edge1;\n\taas_lreachability_t *lreach;\n\taas_trace_t trace;\n\n\tif (!AAS_AreaLadder(area1num) || !AAS_AreaLadder(area2num)) return qfalse;\n\t//\n\tphys_jumpvel = aassettings.phys_jumpvel;\n\t//maximum height a player can jump with the given initial z velocity\n\tmaxjumpheight = AAS_MaxJumpHeight(phys_jumpvel);\n\n\tarea1 = &aasworld.areas[area1num];\n\tarea2 = &aasworld.areas[area2num];\n\t\n\tfor (i = 0; i < area1->numfaces; i++)\n\t{\n\t\tface1num = aasworld.faceindex[area1->firstface + i];\n\t\tface1 = &aasworld.faces[abs(face1num)];\n\t\t//if not a ladder face\n\t\tif (!(face1->faceflags & FACE_LADDER)) continue;\n\t\t//\n\t\tfor (j = 0; j < area2->numfaces; j++)\n\t\t{\n\t\t\tface2num = aasworld.faceindex[area2->firstface + j];\n\t\t\tface2 = &aasworld.faces[abs(face2num)];\n\t\t\t//if not a ladder face\n\t\t\tif (!(face2->faceflags & FACE_LADDER)) continue;\n\t\t\t//check if the faces share an edge\n\t\t\tfor (k = 0; k < face1->numedges; k++)\n\t\t\t{\n\t\t\t\tedge1num = aasworld.edgeindex[face1->firstedge + k];\n\t\t\t\tfor (l = 0; l < face2->numedges; l++)\n\t\t\t\t{\n\t\t\t\t\tedge2num = aasworld.edgeindex[face2->firstedge + l];\n\t\t\t\t\tif (abs(edge1num) == abs(edge2num))\n\t\t\t\t\t{\n\t\t\t\t\t\t//get the face with the largest area\n\t\t\t\t\t\tface1area = AAS_FaceArea(face1);\n\t\t\t\t\t\tface2area = AAS_FaceArea(face2);\n\t\t\t\t\t\tif (face1area > bestface1area && face2area > bestface2area)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tbestface1area = face1area;\n\t\t\t\t\t\t\tbestface2area = face2area;\n\t\t\t\t\t\t\tladderface1 = face1;\n\t\t\t\t\t\t\tladderface2 = face2;\n\t\t\t\t\t\t\tladderface1num = face1num;\n\t\t\t\t\t\t\tladderface2num = face2num;\n\t\t\t\t\t\t\tsharededgenum = edge1num;\n\t\t\t\t\t\t} //end if\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t} //end if\n\t\t\t\t} //end for\n\t\t\t\tif (l != face2->numedges) break;\n\t\t\t} //end for\n\t\t} //end for\n\t} //end for\n\t//\n\tif (ladderface1 && ladderface2)\n\t{\n\t\t//get the middle of the shared edge\n\t\tsharededge = &aasworld.edges[abs(sharededgenum)];\n\t\tfirstv = sharededgenum < 0;\n\t\t//\n\t\tVectorCopy(aasworld.vertexes[sharededge->v[firstv]], v1);\n\t\tVectorCopy(aasworld.vertexes[sharededge->v[!firstv]], v2);\n\t\tVectorAdd(v1, v2, area1point);\n\t\tVectorScale(area1point, 0.5, area1point);\n\t\tVectorCopy(area1point, area2point);\n\t\t//\n\t\t//if the face plane in area 1 is pretty much vertical\n\t\tplane1 = &aasworld.planes[ladderface1->planenum ^ (ladderface1num < 0)];\n\t\tplane2 = &aasworld.planes[ladderface2->planenum ^ (ladderface2num < 0)];\n\t\t//\n\t\t//get the points really into the areas\n\t\tVectorSubtract(v2, v1, sharededgevec);\n\t\tCrossProduct(plane1->normal, sharededgevec, dir);\n\t\tVectorNormalize(dir);\n\t\t//NOTE: 32 because that's larger than 16 (bot bbox x,y)\n\t\tVectorMA(area1point, -32, dir, area1point);\n\t\tVectorMA(area2point, 32, dir, area2point);\n\t\t//\n\t\tladderface1vertical = fabs(DotProduct(plane1->normal, up)) < 0.1;\n\t\tladderface2vertical = fabs(DotProduct(plane2->normal, up)) < 0.1;\n\t\t//there's only reachability between vertical ladder faces\n\t\tif (!ladderface1vertical && !ladderface2vertical) return qfalse;\n\t\t//if both vertical ladder faces\n\t\tif (ladderface1vertical && ladderface2vertical\n\t\t\t\t\t//and the ladder faces do not make a sharp corner\n\t\t\t\t\t&& DotProduct(plane1->normal, plane2->normal) > 0.7\n\t\t\t\t\t//and the shared edge is not too vertical\n\t\t\t\t\t&& fabs(DotProduct(sharededgevec, up)) < 0.7)\n\t\t{\n\t\t\t//create a new reachability link\n\t\t\tlreach = AAS_AllocReachability();\n\t\t\tif (!lreach) return qfalse;\n\t\t\tlreach->areanum = area2num;\n\t\t\tlreach->facenum = ladderface1num;\n\t\t\tlreach->edgenum = abs(sharededgenum);\n\t\t\tVectorCopy(area1point, lreach->start);\n\t\t\t//VectorCopy(area2point, lreach->end);\n\t\t\tVectorMA(area2point, -3, plane1->normal, lreach->end);\n\t\t\tlreach->traveltype = TRAVEL_LADDER;\n\t\t\tlreach->traveltime = 10;\n\t\t\tlreach->next = areareachability[area1num];\n\t\t\tareareachability[area1num] = lreach;\n\t\t\t//\n\t\t\treach_ladder++;\n\t\t\t//create a new reachability link\n\t\t\tlreach = AAS_AllocReachability();\n\t\t\tif (!lreach) return qfalse;\n\t\t\tlreach->areanum = area1num;\n\t\t\tlreach->facenum = ladderface2num;\n\t\t\tlreach->edgenum = abs(sharededgenum);\n\t\t\tVectorCopy(area2point, lreach->start);\n\t\t\t//VectorCopy(area1point, lreach->end);\n\t\t\tVectorMA(area1point, -3, plane1->normal, lreach->end);\n\t\t\tlreach->traveltype = TRAVEL_LADDER;\n\t\t\tlreach->traveltime = 10;\n\t\t\tlreach->next = areareachability[area2num];\n\t\t\tareareachability[area2num] = lreach;\n\t\t\t//\n\t\t\treach_ladder++;\n\t\t\t//\n\t\t\treturn qtrue;\n\t\t} //end if\n\t\t//if the second ladder face is also a ground face\n\t\t//create ladder end (just ladder) reachability and\n\t\t//walk off a ladder (ledge) reachability\n\t\tif (ladderface1vertical && (ladderface2->faceflags & FACE_GROUND))\n\t\t{\n\t\t\t//create a new reachability link\n\t\t\tlreach = AAS_AllocReachability();\n\t\t\tif (!lreach) return qfalse;\n\t\t\tlreach->areanum = area2num;\n\t\t\tlreach->facenum = ladderface1num;\n\t\t\tlreach->edgenum = abs(sharededgenum);\n\t\t\tVectorCopy(area1point, lreach->start);\n\t\t\tVectorCopy(area2point, lreach->end);\n\t\t\tlreach->end[2] += 16;\n\t\t\tVectorMA(lreach->end, -15, plane1->normal, lreach->end);\n\t\t\tlreach->traveltype = TRAVEL_LADDER;\n\t\t\tlreach->traveltime = 10;\n\t\t\tlreach->next = areareachability[area1num];\n\t\t\tareareachability[area1num] = lreach;\n\t\t\t//\n\t\t\treach_ladder++;\n\t\t\t//create a new reachability link\n\t\t\tlreach = AAS_AllocReachability();\n\t\t\tif (!lreach) return qfalse;\n\t\t\tlreach->areanum = area1num;\n\t\t\tlreach->facenum = ladderface2num;\n\t\t\tlreach->edgenum = abs(sharededgenum);\n\t\t\tVectorCopy(area2point, lreach->start);\n\t\t\tVectorCopy(area1point, lreach->end);\n\t\t\tlreach->traveltype = TRAVEL_WALKOFFLEDGE;\n\t\t\tlreach->traveltime = 10;\n\t\t\tlreach->next = areareachability[area2num];\n\t\t\tareareachability[area2num] = lreach;\n\t\t\t//\n\t\t\treach_walkoffledge++;\n\t\t\t//\n\t\t\treturn qtrue;\n\t\t} //end if\n\t\t//\n\t\tif (ladderface1vertical)\n\t\t{\n\t\t\t//find lowest edge of the ladder face\n\t\t\tlowestpoint[2] = 99999;\n\t\t\tfor (i = 0; i < ladderface1->numedges; i++)\n\t\t\t{\n\t\t\t\tedge1num = abs(aasworld.edgeindex[ladderface1->firstedge + i]);\n\t\t\t\tedge1 = &aasworld.edges[edge1num];\n\t\t\t\t//\n\t\t\t\tVectorCopy(aasworld.vertexes[edge1->v[0]], v1);\n\t\t\t\tVectorCopy(aasworld.vertexes[edge1->v[1]], v2);\n\t\t\t\t//\n\t\t\t\tVectorAdd(v1, v2, mid);\n\t\t\t\tVectorScale(mid, 0.5, mid);\n\t\t\t\t//\n\t\t\t\tif (mid[2] < lowestpoint[2])\n\t\t\t\t{\n\t\t\t\t\tVectorCopy(mid, lowestpoint);\n\t\t\t\t\tlowestedgenum = edge1num;\n\t\t\t\t} //end if\n\t\t\t} //end for\n\t\t\t//\n\t\t\tplane1 = &aasworld.planes[ladderface1->planenum];\n\t\t\t//trace down in the middle of this edge\n\t\t\tVectorMA(lowestpoint, 5, plane1->normal, start);\n\t\t\tVectorCopy(start, end);\n\t\t\tstart[2] += 5;\n\t\t\tend[2] -= 100;\n\t\t\t//trace without entity collision\n\t\t\ttrace = AAS_TraceClientBBox(start, end, PRESENCE_NORMAL, -1);\n\t\t\t//\n\t\t\t//\n#ifdef REACH_DEBUG\n\t\t\tif (trace.startsolid)\n\t\t\t{\n\t\t\t\tLog_Write(\"trace from area %d started in solid\\r\\n\", area1num);\n\t\t\t} //end if\n#endif //REACH_DEBUG\n\t\t\t//\n\t\t\ttrace.endpos[2] += 1;\n\t\t\tarea2num = AAS_PointAreaNum(trace.endpos);\n\t\t\t//\n\t\t\tarea2 = &aasworld.areas[area2num];\n\t\t\tfor (i = 0; i < area2->numfaces; i++)\n\t\t\t{\n\t\t\t\tface2num = aasworld.faceindex[area2->firstface + i];\n\t\t\t\tface2 = &aasworld.faces[abs(face2num)];\n\t\t\t\t//\n\t\t\t\tif (face2->faceflags & FACE_LADDER)\n\t\t\t\t{\n\t\t\t\t\tplane2 = &aasworld.planes[face2->planenum];\n\t\t\t\t\tif (fabs(DotProduct(plane2->normal, up)) < 0.1) break;\n\t\t\t\t} //end if\n\t\t\t} //end for\n\t\t\t//if from another area without vertical ladder faces\n\t\t\tif (i >= area2->numfaces && area2num != area1num &&\n\t\t\t\t\t\t//the reachabilities shouldn't exist already\n\t\t\t\t\t\t!AAS_ReachabilityExists(area1num, area2num) &&\n\t\t\t\t\t\t!AAS_ReachabilityExists(area2num, area1num))\n\t\t\t{\n\t\t\t\t//if the height is jumpable\n\t\t\t\tif (start[2] - trace.endpos[2] < maxjumpheight)\n\t\t\t\t{\n\t\t\t\t\t//create a new reachability link\n\t\t\t\t\tlreach = AAS_AllocReachability();\n\t\t\t\t\tif (!lreach) return qfalse;\n\t\t\t\t\tlreach->areanum = area2num;\n\t\t\t\t\tlreach->facenum = ladderface1num;\n\t\t\t\t\tlreach->edgenum = lowestedgenum;\n\t\t\t\t\tVectorCopy(lowestpoint, lreach->start);\n\t\t\t\t\tVectorCopy(trace.endpos, lreach->end);\n\t\t\t\t\tlreach->traveltype = TRAVEL_LADDER;\n\t\t\t\t\tlreach->traveltime = 10;\n\t\t\t\t\tlreach->next = areareachability[area1num];\n\t\t\t\t\tareareachability[area1num] = lreach;\n\t\t\t\t\t//\n\t\t\t\t\treach_ladder++;\n\t\t\t\t\t//create a new reachability link\n\t\t\t\t\tlreach = AAS_AllocReachability();\n\t\t\t\t\tif (!lreach) return qfalse;\n\t\t\t\t\tlreach->areanum = area1num;\n\t\t\t\t\tlreach->facenum = ladderface1num;\n\t\t\t\t\tlreach->edgenum = lowestedgenum;\n\t\t\t\t\tVectorCopy(trace.endpos, lreach->start);\n\t\t\t\t\t//get the end point a little bit into the ladder\n\t\t\t\t\tVectorMA(lowestpoint, -5, plane1->normal, lreach->end);\n\t\t\t\t\t//get the end point a little higher\n\t\t\t\t\tlreach->end[2] += 10;\n\t\t\t\t\tlreach->traveltype = TRAVEL_JUMP;\n\t\t\t\t\tlreach->traveltime = 10;\n\t\t\t\t\tlreach->next = areareachability[area2num];\n\t\t\t\t\tareareachability[area2num] = lreach;\n\t\t\t\t\t//\n\t\t\t\t\treach_jump++;\t\n\t\t\t\t\t//\n\t\t\t\t\treturn qtrue;\n#ifdef REACH_DEBUG\n\t\t\t\t\tLog_Write(\"jump up to ladder reach between %d and %d\\r\\n\", area2num, area1num);\n#endif //REACH_DEBUG\n\t\t\t\t} //end if\n#ifdef REACH_DEBUG\n\t\t\t\telse Log_Write(\"jump too high between area %d and %d\\r\\n\", area2num, area1num);\n#endif //REACH_DEBUG\n\t\t\t} //end if\n\t\t\t/*//if slime or lava below the ladder\n\t\t\t//try jump reachability from far towards the ladder\n\t\t\tif (aasworld.areasettings[area2num].contents & (AREACONTENTS_SLIME\n\t\t\t\t\t\t\t\t\t\t\t\t\t| AREACONTENTS_LAVA))\n\t\t\t{\n\t\t\t\tfor (i = 20; i <= 120; i += 20)\n\t\t\t\t{\n\t\t\t\t\t//trace down in the middle of this edge\n\t\t\t\t\tVectorMA(lowestpoint, i, plane1->normal, start);\n\t\t\t\t\tVectorCopy(start, end);\n\t\t\t\t\tstart[2] += 5;\n\t\t\t\t\tend[2] -= 100;\n\t\t\t\t\t//trace without entity collision\n\t\t\t\t\ttrace = AAS_TraceClientBBox(start, end, PRESENCE_NORMAL, -1);\n\t\t\t\t\t//\n\t\t\t\t\tif (trace.startsolid) break;\n\t\t\t\t\ttrace.endpos[2] += 1;\n\t\t\t\t\tarea2num = AAS_PointAreaNum(trace.endpos);\n\t\t\t\t\tif (area2num == area1num) continue;\n\t\t\t\t\t//\n\t\t\t\t\tif (start[2] - trace.endpos[2] > maxjumpheight) continue;\n\t\t\t\t\tif (aasworld.areasettings[area2num].contents & (AREACONTENTS_SLIME\n\t\t\t\t\t\t\t\t\t\t\t\t| AREACONTENTS_LAVA)) continue;\n\t\t\t\t\t//\n\t\t\t\t\t//create a new reachability link\n\t\t\t\t\tlreach = AAS_AllocReachability();\n\t\t\t\t\tif (!lreach) return qfalse;\n\t\t\t\t\tlreach->areanum = area1num;\n\t\t\t\t\tlreach->facenum = ladderface1num;\n\t\t\t\t\tlreach->edgenum = lowestedgenum;\n\t\t\t\t\tVectorCopy(trace.endpos, lreach->start);\n\t\t\t\t\tVectorCopy(lowestpoint, lreach->end);\n\t\t\t\t\tlreach->end[2] += 5;\n\t\t\t\t\tlreach->traveltype = TRAVEL_JUMP;\n\t\t\t\t\tlreach->traveltime = 10;\n\t\t\t\t\tlreach->next = areareachability[area2num];\n\t\t\t\t\tareareachability[area2num] = lreach;\n\t\t\t\t\t//\n\t\t\t\t\treach_jump++;\n\t\t\t\t\t//\n\t\t\t\t\tLog_Write(\"jump far to ladder reach between %d and %d\\r\\n\", area2num, area1num);\n\t\t\t\t\t//\n\t\t\t\t\tbreak;\n\t\t\t\t} //end for\n\t\t\t} //end if*/\n\t\t} //end if\n\t} //end if\n\treturn qfalse;\n} //end of the function AAS_Reachability_Ladder\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint AAS_TravelFlagsForTeam(int ent)\n{\n\tint notteam;\n\n\tif (!AAS_IntForBSPEpairKey(ent, \"bot_notteam\", &notteam))\n\t\treturn 0;\n\tif (notteam == 1)\n\t\treturn TRAVELFLAG_NOTTEAM1;\n\tif (notteam == 2)\n\t\treturn TRAVELFLAG_NOTTEAM2;\n\treturn 0;\n} //end of the function AAS_TravelFlagsForTeam\n//===========================================================================\n// create possible teleporter reachabilities\n// this is very game dependent.... :(\n//\n// classname = trigger_multiple or trigger_teleport\n// target = \"t1\"\n//\n// classname = target_teleporter\n// targetname = \"t1\"\n// target = \"t2\"\n//\n// classname = misc_teleporter_dest\n// targetname = \"t2\"\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid AAS_Reachability_Teleport(void)\n{\n\tint area1num, area2num;\n\tchar target[MAX_EPAIRKEY], targetname[MAX_EPAIRKEY];\n\tchar classname[MAX_EPAIRKEY], model[MAX_EPAIRKEY];\n\tint ent, dest;\n\tfloat angle;\n\tvec3_t origin, destorigin, mins, maxs, end, angles;\n\tvec3_t mid, velocity, cmdmove;\n\taas_lreachability_t *lreach;\n\taas_clientmove_t move;\n\taas_trace_t trace;\n\taas_link_t *areas, *link;\n\n\tfor (ent = AAS_NextBSPEntity(0); ent; ent = AAS_NextBSPEntity(ent))\n\t{\n\t\tif (!AAS_ValueForBSPEpairKey(ent, \"classname\", classname, MAX_EPAIRKEY)) continue;\n\t\tif (!strcmp(classname, \"trigger_multiple\"))\n\t\t{\n\t\t\tAAS_ValueForBSPEpairKey(ent, \"model\", model, MAX_EPAIRKEY);\n//#ifdef REACH_DEBUG\n\t\t\tbotimport.Print(PRT_MESSAGE, \"trigger_multiple model = \\\"%s\\\"\\n\", model);\n//#endif REACH_DEBUG\n\t\t\tVectorClear(angles);\n\t\t\tAAS_BSPModelMinsMaxsOrigin(atoi(model+1), angles, mins, maxs, origin);\n\t\t\t//\n\t\t\tif (!AAS_ValueForBSPEpairKey(ent, \"target\", target, MAX_EPAIRKEY))\n\t\t\t{\n\t\t\t\tbotimport.Print(PRT_ERROR, \"trigger_multiple at %1.0f %1.0f %1.0f without target\\n\",\n\t\t\t\t\t\t\t\t\torigin[0], origin[1], origin[2]);\n\t\t\t\tcontinue;\n\t\t\t} //end if\n\t\t\tfor (dest = AAS_NextBSPEntity(0); dest; dest = AAS_NextBSPEntity(dest))\n\t\t\t{\n\t\t\t\tif (!AAS_ValueForBSPEpairKey(dest, \"classname\", classname, MAX_EPAIRKEY)) continue;\n\t\t\t\tif (!strcmp(classname, \"target_teleporter\"))\n\t\t\t\t{\n\t\t\t\t\tif (!AAS_ValueForBSPEpairKey(dest, \"targetname\", targetname, MAX_EPAIRKEY)) continue;\n\t\t\t\t\tif (!strcmp(targetname, target))\n\t\t\t\t\t{\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t} //end if\n\t\t\t\t} //end if\n\t\t\t} //end for\n\t\t\tif (!dest)\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t} //end if\n\t\t\tif (!AAS_ValueForBSPEpairKey(dest, \"target\", target, MAX_EPAIRKEY))\n\t\t\t{\n\t\t\t\tbotimport.Print(PRT_ERROR, \"target_teleporter without target\\n\");\n\t\t\t\tcontinue;\n\t\t\t} //end if\n\t\t} //end else\n\t\telse if (!strcmp(classname, \"trigger_teleport\"))\n\t\t{\n\t\t\tAAS_ValueForBSPEpairKey(ent, \"model\", model, MAX_EPAIRKEY);\n//#ifdef REACH_DEBUG\n\t\t\tbotimport.Print(PRT_MESSAGE, \"trigger_teleport model = \\\"%s\\\"\\n\", model);\n//#endif REACH_DEBUG\n\t\t\tVectorClear(angles);\n\t\t\tAAS_BSPModelMinsMaxsOrigin(atoi(model+1), angles, mins, maxs, origin);\n\t\t\t//\n\t\t\tif (!AAS_ValueForBSPEpairKey(ent, \"target\", target, MAX_EPAIRKEY))\n\t\t\t{\n\t\t\t\tbotimport.Print(PRT_ERROR, \"trigger_teleport at %1.0f %1.0f %1.0f without target\\n\",\n\t\t\t\t\t\t\t\t\torigin[0], origin[1], origin[2]);\n\t\t\t\tcontinue;\n\t\t\t} //end if\n\t\t} //end if\n\t\telse\n\t\t{\n\t\t\tcontinue;\n\t\t} //end else\n\t\t//\n\t\tfor (dest = AAS_NextBSPEntity(0); dest; dest = AAS_NextBSPEntity(dest))\n\t\t{\n\t\t\t//classname should be misc_teleporter_dest\n\t\t\t//but I've also seen target_position and actually any\n\t\t\t//entity could be used... burp\n\t\t\tif (AAS_ValueForBSPEpairKey(dest, \"targetname\", targetname, MAX_EPAIRKEY))\n\t\t\t{\n\t\t\t\tif (!strcmp(targetname, target))\n\t\t\t\t{\n\t\t\t\t\tbreak;\n\t\t\t\t} //end if\n\t\t\t} //end if\n\t\t} //end for\n\t\tif (!dest)\n\t\t{\n\t\t\tbotimport.Print(PRT_ERROR, \"teleporter without misc_teleporter_dest (%s)\\n\", target);\n\t\t\tcontinue;\n\t\t} //end if\n\t\tif (!AAS_VectorForBSPEpairKey(dest, \"origin\", destorigin))\n\t\t{\n\t\t\tbotimport.Print(PRT_ERROR, \"teleporter destination (%s) without origin\\n\", target);\n\t\t\tcontinue;\n\t\t} //end if\n\t\t//\n\t\tarea2num = AAS_PointAreaNum(destorigin);\n\t\t//if not teleported into a teleporter or into a jumppad\n\t\tif (!AAS_AreaTeleporter(area2num) && !AAS_AreaJumpPad(area2num))\n\t\t{\n\t\t\tVectorCopy(destorigin, end);\n\t\t\tend[2] -= 64;\n\t\t\ttrace = AAS_TraceClientBBox(destorigin, end, PRESENCE_CROUCH, -1);\n\t\t\tif (trace.startsolid)\n\t\t\t{\n\t\t\t\tbotimport.Print(PRT_ERROR, \"teleporter destination (%s) in solid\\n\", target);\n\t\t\t\tcontinue;\n\t\t\t} //end if\n\t\t\tarea2num = AAS_PointAreaNum(trace.endpos);\n\t\t\t//\n\t\t\t/*\n\t\t\tif (!AAS_AreaTeleporter(area2num) &&\n\t\t\t\t!AAS_AreaJumpPad(area2num) &&\n\t\t\t\t!AAS_AreaGrounded(area2num))\n\t\t\t{\n\t\t\t\tVectorCopy(trace.endpos, destorigin);\n\t\t\t}\n\t\t\telse*/\n\t\t\t{\n\t\t\t\t//predict where you'll end up\n\t\t\t\tAAS_FloatForBSPEpairKey(dest, \"angle\", &angle);\n\t\t\t\tif (angle)\n\t\t\t\t{\n\t\t\t\t\tVectorSet(angles, 0, angle, 0);\n\t\t\t\t\tAngleVectors(angles, velocity, NULL, NULL);\n\t\t\t\t\tVectorScale(velocity, 400, velocity);\n\t\t\t\t} //end if\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tVectorClear(velocity);\n\t\t\t\t} //end else\n\t\t\t\tVectorClear(cmdmove);\n\t\t\t\tAAS_PredictClientMovement(&move, -1, destorigin, PRESENCE_NORMAL, qfalse,\n\t\t\t\t\t\t\t\t\t\tvelocity, cmdmove, 0, 30, 0.1f,\n\t\t\t\t\t\t\t\t\t\tSE_HITGROUND|SE_ENTERWATER|SE_ENTERSLIME|\n\t\t\t\t\t\t\t\t\t\tSE_ENTERLAVA|SE_HITGROUNDDAMAGE|SE_TOUCHJUMPPAD|SE_TOUCHTELEPORTER, 0, qfalse); //qtrue);\n\t\t\t\tarea2num = AAS_PointAreaNum(move.endpos);\n\t\t\t\tif (move.stopevent & (SE_ENTERSLIME|SE_ENTERLAVA))\n\t\t\t\t{\n\t\t\t\t\tbotimport.Print(PRT_WARNING, \"teleported into slime or lava at dest %s\\n\", target);\n\t\t\t\t} //end if\n\t\t\t\tVectorCopy(move.endpos, destorigin);\n\t\t\t} //end else\n\t\t} //end if\n\t\t//\n\t\t//botimport.Print(PRT_MESSAGE, \"teleporter brush origin at %f %f %f\\n\", origin[0], origin[1], origin[2]);\n\t\t//botimport.Print(PRT_MESSAGE, \"teleporter brush mins = %f %f %f\\n\", mins[0], mins[1], mins[2]);\n\t\t//botimport.Print(PRT_MESSAGE, \"teleporter brush maxs = %f %f %f\\n\", maxs[0], maxs[1], maxs[2]);\n\t\tVectorAdd(origin, mins, mins);\n\t\tVectorAdd(origin, maxs, maxs);\n\t\t//\n\t\tVectorAdd(mins, maxs, mid);\n\t\tVectorScale(mid, 0.5, mid);\n\t\t//link an invalid (-1) entity\n\t\tareas = AAS_LinkEntityClientBBox(mins, maxs, -1, PRESENCE_CROUCH);\n\t\tif (!areas) botimport.Print(PRT_MESSAGE, \"trigger_multiple not in any area\\n\");\n\t\t//\n\t\tfor (link = areas; link; link = link->next_area)\n\t\t{\n\t\t\t//if (!AAS_AreaGrounded(link->areanum)) continue;\n\t\t\tif (!AAS_AreaTeleporter(link->areanum)) continue;\n\t\t\t//\n\t\t\tarea1num = link->areanum;\n\t\t\t//create a new reachability link\n\t\t\tlreach = AAS_AllocReachability();\n\t\t\tif (!lreach) break;\n\t\t\tlreach->areanum = area2num;\n\t\t\tlreach->facenum = 0;\n\t\t\tlreach->edgenum = 0;\n\t\t\tVectorCopy(mid, lreach->start);\n\t\t\tVectorCopy(destorigin, lreach->end);\n\t\t\tlreach->traveltype = TRAVEL_TELEPORT;\n\t\t\tlreach->traveltype |= AAS_TravelFlagsForTeam(ent);\n\t\t\tlreach->traveltime = aassettings.rs_teleport;\n\t\t\tlreach->next = areareachability[area1num];\n\t\t\tareareachability[area1num] = lreach;\n\t\t\t//\n\t\t\treach_teleport++;\n\t\t} //end for\n\t\t//unlink the invalid entity\n\t\tAAS_UnlinkFromAreas(areas);\n\t} //end for\n} //end of the function AAS_Reachability_Teleport\n//===========================================================================\n// create possible elevator (func_plat) reachabilities\n// this is very game dependent.... :(\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid AAS_Reachability_Elevator(void)\n{\n\tint area1num, area2num, modelnum, i, j, k, l, n, p;\n\tfloat lip, height, speed;\n\tchar model[MAX_EPAIRKEY], classname[MAX_EPAIRKEY];\n\tint ent;\n\tvec3_t mins, maxs, origin, angles = {0, 0, 0};\n\tvec3_t pos1, pos2, mids, platbottom, plattop;\n\tvec3_t bottomorg, toporg, start, end, dir;\n\tvec_t xvals[8], yvals[8], xvals_top[8], yvals_top[8];\n\taas_lreachability_t *lreach;\n\taas_trace_t trace;\n\n#ifdef REACH_DEBUG\n\tLog_Write(\"AAS_Reachability_Elevator\\r\\n\");\n#endif //REACH_DEBUG\n\tfor (ent = AAS_NextBSPEntity(0); ent; ent = AAS_NextBSPEntity(ent))\n\t{\n\t\tif (!AAS_ValueForBSPEpairKey(ent, \"classname\", classname, MAX_EPAIRKEY)) continue;\n\t\tif (!strcmp(classname, \"func_plat\"))\n\t\t{\n#ifdef REACH_DEBUG\n\t\t\tLog_Write(\"found func plat\\r\\n\");\n#endif //REACH_DEBUG\n\t\t\tif (!AAS_ValueForBSPEpairKey(ent, \"model\", model, MAX_EPAIRKEY))\n\t\t\t{\n\t\t\t\tbotimport.Print(PRT_ERROR, \"func_plat without model\\n\");\n\t\t\t\tcontinue;\n\t\t\t} //end if\n\t\t\t//get the model number, and skip the leading *\n\t\t\tmodelnum = atoi(model+1);\n\t\t\tif (modelnum <= 0)\n\t\t\t{\n\t\t\t\tbotimport.Print(PRT_ERROR, \"func_plat with invalid model number\\n\");\n\t\t\t\tcontinue;\n\t\t\t} //end if\n\t\t\t//get the mins, maxs and origin of the model\n\t\t\t//NOTE: the origin is usually (0,0,0) and the mins and maxs\n\t\t\t//      are the absolute mins and maxs\n\t\t\tAAS_BSPModelMinsMaxsOrigin(modelnum, angles, mins, maxs, origin);\n\t\t\t//\n\t\t\tAAS_VectorForBSPEpairKey(ent, \"origin\", origin);\n\t\t\t//pos1 is the top position, pos2 is the bottom\n\t\t\tVectorCopy(origin, pos1);\n\t\t\tVectorCopy(origin, pos2);\n\t\t\t//get the lip of the plat\n\t\t\tAAS_FloatForBSPEpairKey(ent, \"lip\", &lip);\n\t\t\tif (!lip) lip = 8;\n\t\t\t//get the movement height of the plat\n\t\t\tAAS_FloatForBSPEpairKey(ent, \"height\", &height);\n\t\t\tif (!height) height = (maxs[2] - mins[2]) - lip;\n\t\t\t//get the speed of the plat\n\t\t\tAAS_FloatForBSPEpairKey(ent, \"speed\", &speed);\n\t\t\tif (!speed) speed = 200;\n\t\t\t//get bottom position below pos1\n\t\t\tpos2[2] -= height;\n\t\t\t//\n\t\t\t//get a point just above the plat in the bottom position\n\t\t\tVectorAdd(mins, maxs, mids);\n\t\t\tVectorMA(pos2, 0.5, mids, platbottom);\n\t\t\tplatbottom[2] = maxs[2] - (pos1[2] - pos2[2]) + 2;\n\t\t\t//get a point just above the plat in the top position\n\t\t\tVectorAdd(mins, maxs, mids);\n\t\t\tVectorMA(pos2, 0.5, mids, plattop);\n\t\t\tplattop[2] = maxs[2] + 2;\n\t\t\t//\n\t\t\t/*if (!area1num)\n\t\t\t{\n\t\t\t\tLog_Write(\"no grounded area near plat bottom\\r\\n\");\n\t\t\t\tcontinue;\n\t\t\t} //end if*/\n\t\t\t//get the mins and maxs a little larger\n\t\t\tfor (i = 0; i < 3; i++)\n\t\t\t{\n\t\t\t\tmins[i] -= 1;\n\t\t\t\tmaxs[i] += 1;\n\t\t\t} //end for\n\t\t\t//\n\t\t\t//botimport.Print(PRT_MESSAGE, \"platbottom[2] = %1.1f plattop[2] = %1.1f\\n\", platbottom[2], plattop[2]);\n\t\t\t//\n\t\t\tVectorAdd(mins, maxs, mids);\n\t\t\tVectorScale(mids, 0.5, mids);\n\t\t\t//\n\t\t\txvals[0] = mins[0]; xvals[1] = mids[0]; xvals[2] = maxs[0]; xvals[3] = mids[0];\n\t\t\tyvals[0] = mids[1]; yvals[1] = maxs[1]; yvals[2] = mids[1]; yvals[3] = mins[1];\n\t\t\t//\n\t\t\txvals[4] = mins[0]; xvals[5] = maxs[0]; xvals[6] = maxs[0]; xvals[7] = mins[0];\n\t\t\tyvals[4] = maxs[1]; yvals[5] = maxs[1]; yvals[6] = mins[1]; yvals[7] = mins[1];\n\t\t\t//find adjacent areas around the bottom of the plat\n\t\t\tfor (i = 0; i < 9; i++)\n\t\t\t{\n\t\t\t\tif (i < 8) //check at the sides of the plat\n\t\t\t\t{\n\t\t\t\t\tbottomorg[0] = origin[0] + xvals[i];\n\t\t\t\t\tbottomorg[1] = origin[1] + yvals[i];\n\t\t\t\t\tbottomorg[2] = platbottom[2] + 16;\n\t\t\t\t\t//get a grounded or swim area near the plat in the bottom position\n\t\t\t\t\tarea1num = AAS_PointAreaNum(bottomorg);\n\t\t\t\t\tfor (k = 0; k < 16; k++)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (area1num)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (AAS_AreaGrounded(area1num) || AAS_AreaSwim(area1num)) break;\n\t\t\t\t\t\t} //end if\n\t\t\t\t\t\tbottomorg[2] += 4;\n\t\t\t\t\t\tarea1num = AAS_PointAreaNum(bottomorg);\n\t\t\t\t\t} //end if\n\t\t\t\t\t//if in solid\n\t\t\t\t\tif (k >= 16)\n\t\t\t\t\t{\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t} //end if\n\t\t\t\t} //end if\n\t\t\t\telse //at the middle of the plat\n\t\t\t\t{\n\t\t\t\t\tVectorCopy(plattop, bottomorg);\n\t\t\t\t\tbottomorg[2] += 24;\n\t\t\t\t\tarea1num = AAS_PointAreaNum(bottomorg);\n\t\t\t\t\tif (!area1num) continue;\n\t\t\t\t\tVectorCopy(platbottom, bottomorg);\n\t\t\t\t\tbottomorg[2] += 24;\n\t\t\t\t} //end else\n\t\t\t\t//look at adjacent areas around the top of the plat\n\t\t\t\t//make larger steps to outside the plat everytime\n\t\t\t\tfor (n = 0; n < 3; n++)\n\t\t\t\t{\n\t\t\t\t\tfor (k = 0; k < 3; k++)\n\t\t\t\t\t{\n\t\t\t\t\t\tmins[k] -= 4;\n\t\t\t\t\t\tmaxs[k] += 4;\n\t\t\t\t\t} //end for\n\t\t\t\t\txvals_top[0] = mins[0]; xvals_top[1] = mids[0]; xvals_top[2] = maxs[0]; xvals_top[3] = mids[0];\n\t\t\t\t\tyvals_top[0] = mids[1]; yvals_top[1] = maxs[1]; yvals_top[2] = mids[1]; yvals_top[3] = mins[1];\n\t\t\t\t\t//\n\t\t\t\t\txvals_top[4] = mins[0]; xvals_top[5] = maxs[0]; xvals_top[6] = maxs[0]; xvals_top[7] = mins[0];\n\t\t\t\t\tyvals_top[4] = maxs[1]; yvals_top[5] = maxs[1]; yvals_top[6] = mins[1]; yvals_top[7] = mins[1];\n\t\t\t\t\t//\n\t\t\t\t\tfor (j = 0; j < 8; j++)\n\t\t\t\t\t{\n\t\t\t\t\t\ttoporg[0] = origin[0] + xvals_top[j];\n\t\t\t\t\t\ttoporg[1] = origin[1] + yvals_top[j];\n\t\t\t\t\t\ttoporg[2] = plattop[2] + 16;\n\t\t\t\t\t\t//get a grounded or swim area near the plat in the top position\n\t\t\t\t\t\tarea2num = AAS_PointAreaNum(toporg);\n\t\t\t\t\t\tfor (l = 0; l < 16; l++)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (area2num)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tif (AAS_AreaGrounded(area2num) || AAS_AreaSwim(area2num))\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tVectorCopy(plattop, start);\n\t\t\t\t\t\t\t\t\tstart[2] += 32;\n\t\t\t\t\t\t\t\t\tVectorCopy(toporg, end);\n\t\t\t\t\t\t\t\t\tend[2] += 1;\n\t\t\t\t\t\t\t\t\ttrace = AAS_TraceClientBBox(start, end, PRESENCE_CROUCH, -1);\n\t\t\t\t\t\t\t\t\tif (trace.fraction >= 1) break;\n\t\t\t\t\t\t\t\t} //end if\n\t\t\t\t\t\t\t} //end if\n\t\t\t\t\t\t\ttoporg[2] += 4;\n\t\t\t\t\t\t\tarea2num = AAS_PointAreaNum(toporg);\n\t\t\t\t\t\t} //end if\n\t\t\t\t\t\t//if in solid\n\t\t\t\t\t\tif (l >= 16) continue;\n\t\t\t\t\t\t//never create a reachability in the same area\n\t\t\t\t\t\tif (area2num == area1num) continue;\n\t\t\t\t\t\t//if the area isn't grounded\n\t\t\t\t\t\tif (!AAS_AreaGrounded(area2num)) continue;\n\t\t\t\t\t\t//if there already exists reachability between the areas\n\t\t\t\t\t\tif (AAS_ReachabilityExists(area1num, area2num)) continue;\n\t\t\t\t\t\t//if the reachability start is within the elevator bounding box\n\t\t\t\t\t\tVectorSubtract(bottomorg, platbottom, dir);\n\t\t\t\t\t\tVectorNormalize(dir);\n\t\t\t\t\t\tdir[0] = bottomorg[0] + 24 * dir[0];\n\t\t\t\t\t\tdir[1] = bottomorg[1] + 24 * dir[1];\n\t\t\t\t\t\tdir[2] = bottomorg[2];\n\t\t\t\t\t\t//\n\t\t\t\t\t\tfor (p = 0; p < 3; p++)\n\t\t\t\t\t\t\tif (dir[p] < origin[p] + mins[p] || dir[p] > origin[p] + maxs[p]) break;\n\t\t\t\t\t\tif (p >= 3) continue;\n\t\t\t\t\t\t//create a new reachability link\n\t\t\t\t\t\tlreach = AAS_AllocReachability();\n\t\t\t\t\t\tif (!lreach) continue;\n\t\t\t\t\t\tlreach->areanum = area2num;\n\t\t\t\t\t\t//the facenum is the model number\n\t\t\t\t\t\tlreach->facenum = modelnum;\n\t\t\t\t\t\t//the edgenum is the height\n\t\t\t\t\t\tlreach->edgenum = (int) height;\n\t\t\t\t\t\t//\n\t\t\t\t\t\tVectorCopy(dir, lreach->start);\n\t\t\t\t\t\tVectorCopy(toporg, lreach->end);\n\t\t\t\t\t\tlreach->traveltype = TRAVEL_ELEVATOR;\n\t\t\t\t\t\tlreach->traveltype |= AAS_TravelFlagsForTeam(ent);\n\t\t\t\t\t\tlreach->traveltime = aassettings.rs_startelevator + height * 100 / speed;\n\t\t\t\t\t\tlreach->next = areareachability[area1num];\n\t\t\t\t\t\tareareachability[area1num] = lreach;\n\t\t\t\t\t\t//don't go any further to the outside\n\t\t\t\t\t\tn = 9999;\n\t\t\t\t\t\t//\n#ifdef REACH_DEBUG\n\t\t\t\t\t\tLog_Write(\"elevator reach from %d to %d\\r\\n\", area1num, area2num);\n#endif //REACH_DEBUG\n\t\t\t\t\t\t//\n\t\t\t\t\t\treach_elevator++;\n\t\t\t\t\t} //end for\n\t\t\t\t} //end for\n\t\t\t} //end for\n\t\t} //end if\n\t} //end for\n} //end of the function AAS_Reachability_Elevator\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\naas_lreachability_t *AAS_FindFaceReachabilities(vec3_t *facepoints, int numpoints, aas_plane_t *plane, int towardsface)\n{\n\tint i, j, k, l;\n\tint facenum, edgenum, bestfacenum;\n\tfloat *v1, *v2, *v3, *v4;\n\tfloat bestdist, speed, hordist, dist;\n\tvec3_t beststart, beststart2, bestend, bestend2, tmp, hordir, testpoint;\n\taas_lreachability_t *lreach, *lreachabilities;\n\taas_area_t *area;\n\taas_face_t *face;\n\taas_edge_t *edge;\n\taas_plane_t *faceplane, *bestfaceplane;\n\n\t//\n\tlreachabilities = NULL;\n\tbestfacenum = 0;\n\tbestfaceplane = NULL;\n\t//\n\tfor (i = 1; i < aasworld.numareas; i++)\n\t{\n\t\tarea = &aasworld.areas[i];\n\t\t// get the shortest distance between one of the func_bob start edges and\n\t\t// one of the face edges of area1\n\t\tbestdist = 999999;\n\t\tfor (j = 0; j < area->numfaces; j++)\n\t\t{\n\t\t\tfacenum = aasworld.faceindex[area->firstface + j];\n\t\t\tface = &aasworld.faces[abs(facenum)];\n\t\t\t//if not a ground face\n\t\t\tif (!(face->faceflags & FACE_GROUND)) continue;\n\t\t\t//get the ground planes\n\t\t\tfaceplane = &aasworld.planes[face->planenum];\n\t\t\t//\n\t\t\tfor (k = 0; k < face->numedges; k++)\n\t\t\t{\n\t\t\t\tedgenum = abs(aasworld.edgeindex[face->firstedge + k]);\n\t\t\t\tedge = &aasworld.edges[edgenum];\n\t\t\t\t//calculate the minimum distance between the two edges\n\t\t\t\tv1 = aasworld.vertexes[edge->v[0]];\n\t\t\t\tv2 = aasworld.vertexes[edge->v[1]];\n\t\t\t\t//\n\t\t\t\tfor (l = 0; l < numpoints; l++)\n\t\t\t\t{\n\t\t\t\t\tv3 = facepoints[l];\n\t\t\t\t\tv4 = facepoints[(l+1) % numpoints];\n\t\t\t\t\tdist = AAS_ClosestEdgePoints(v1, v2, v3, v4, faceplane, plane,\n\t\t\t\t\t\t\t\t\t\t\t\t\tbeststart, bestend,\n\t\t\t\t\t\t\t\t\t\t\t\t\tbeststart2, bestend2, bestdist);\n\t\t\t\t\tif (dist < bestdist)\n\t\t\t\t\t{\n\t\t\t\t\t\tbestfacenum = facenum;\n\t\t\t\t\t\tbestfaceplane = faceplane;\n\t\t\t\t\t\tbestdist = dist;\n\t\t\t\t\t} //end if\n\t\t\t\t} //end for\n\t\t\t} //end for\n\t\t} //end for\n\t\t//\n\t\tif (bestdist > 192) continue;\n\t\t//\n\t\tVectorMiddle(beststart, beststart2, beststart);\n\t\tVectorMiddle(bestend, bestend2, bestend);\n\t\t//\n\t\tif (!towardsface)\n\t\t{\n\t\t\tVectorCopy(beststart, tmp);\n\t\t\tVectorCopy(bestend, beststart);\n\t\t\tVectorCopy(tmp, bestend);\n\t\t} //end if\n\t\t//\n\t\tVectorSubtract(bestend, beststart, hordir);\n\t\thordir[2] = 0;\n\t\thordist = VectorLength(hordir);\n\t\t//\n\t\tif (hordist > 2 * AAS_MaxJumpDistance(aassettings.phys_jumpvel)) continue;\n\t\t//the end point should not be significantly higher than the start point\n\t\tif (bestend[2] - 32 > beststart[2]) continue;\n\t\t//don't fall down too far\n\t\tif (bestend[2] < beststart[2] - 128) continue;\n\t\t//the distance should not be too far\n\t\tif (hordist > 32)\n\t\t{\n\t\t\t//check for walk off ledge\n\t\t\tif (!AAS_HorizontalVelocityForJump(0, beststart, bestend, &speed)) continue;\n\t\t} //end if\n\t\t//\n\t\tbeststart[2] += 1;\n\t\tbestend[2] += 1;\n\t\t//\n\t\tif (towardsface) VectorCopy(bestend, testpoint);\n\t\telse VectorCopy(beststart, testpoint);\n\t\ttestpoint[2] = 0;\n\t\ttestpoint[2] = (bestfaceplane->dist - DotProduct(bestfaceplane->normal, testpoint)) / bestfaceplane->normal[2];\n\t\t//\n\t\tif (!AAS_PointInsideFace(bestfacenum, testpoint, 0.1f))\n\t\t{\n\t\t\t//if the faces are not overlapping then only go down\n\t\t\tif (bestend[2] - 16 > beststart[2]) continue;\n\t\t} //end if\n\t\tlreach = AAS_AllocReachability();\n\t\tif (!lreach) return lreachabilities;\n\t\tlreach->areanum = i;\n\t\tlreach->facenum = 0;\n\t\tlreach->edgenum = 0;\n\t\tVectorCopy(beststart, lreach->start);\n\t\tVectorCopy(bestend, lreach->end);\n\t\tlreach->traveltype = 0;\n\t\tlreach->traveltime = 0;\n\t\tlreach->next = lreachabilities;\n\t\tlreachabilities = lreach;\n#ifndef BSPC\n\t\tif (towardsface) AAS_PermanentLine(lreach->start, lreach->end, 1);\n\t\telse AAS_PermanentLine(lreach->start, lreach->end, 2);\n#endif\n\t} //end for\n\treturn lreachabilities;\n} //end of the function AAS_FindFaceReachabilities\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid AAS_Reachability_FuncBobbing(void)\n{\n\tint ent, spawnflags, modelnum, axis;\n\tint i, numareas, areas[10];\n\tchar classname[MAX_EPAIRKEY], model[MAX_EPAIRKEY];\n\tvec3_t origin, move_end, move_start, move_start_top, move_end_top;\n\tvec3_t mins, maxs, angles = {0, 0, 0};\n\tvec3_t start_edgeverts[4], end_edgeverts[4], mid;\n\tvec3_t org, start, end, dir, points[10];\n\tfloat height;\n\taas_plane_t start_plane, end_plane;\n\taas_lreachability_t *startreach, *endreach, *nextstartreach, *nextendreach, *lreach;\n\taas_lreachability_t *firststartreach, *firstendreach;\n\n\tfor (ent = AAS_NextBSPEntity(0); ent; ent = AAS_NextBSPEntity(ent))\n\t{\n\t\tif (!AAS_ValueForBSPEpairKey(ent, \"classname\", classname, MAX_EPAIRKEY)) continue;\n\t\tif (strcmp(classname, \"func_bobbing\")) continue;\n\t\tAAS_FloatForBSPEpairKey(ent, \"height\", &height);\n\t\tif (!height) height = 32;\n\t\t//\n\t\tif (!AAS_ValueForBSPEpairKey(ent, \"model\", model, MAX_EPAIRKEY))\n\t\t{\n\t\t\tbotimport.Print(PRT_ERROR, \"func_bobbing without model\\n\");\n\t\t\tcontinue;\n\t\t} //end if\n\t\t//get the model number, and skip the leading *\n\t\tmodelnum = atoi(model+1);\n\t\tif (modelnum <= 0)\n\t\t{\n\t\t\tbotimport.Print(PRT_ERROR, \"func_bobbing with invalid model number\\n\");\n\t\t\tcontinue;\n\t\t} //end if\n\t\t//if the entity has an origin set then use it\n\t\tif (!AAS_VectorForBSPEpairKey(ent, \"origin\", origin))\n\t\t\tVectorSet(origin, 0, 0, 0);\n\t\t//\n\t\tAAS_BSPModelMinsMaxsOrigin(modelnum, angles, mins, maxs, NULL);\n\t\t//\n\t\tVectorAdd(mins, origin, mins);\n\t\tVectorAdd(maxs, origin, maxs);\n\t\t//\n\t\tVectorAdd(mins, maxs, mid);\n\t\tVectorScale(mid, 0.5, mid);\n\t\tVectorCopy(mid, origin);\n\t\t//\n\t\tVectorCopy(origin, move_end);\n\t\tVectorCopy(origin, move_start);\n\t\t//\n\t\tAAS_IntForBSPEpairKey(ent, \"spawnflags\", &spawnflags);\n\t\t// set the axis of bobbing\n\t\tif (spawnflags & 1) axis = 0;\n\t\telse if (spawnflags & 2) axis = 1;\n\t\telse axis = 2;\n\t\t//\n\t\tmove_start[axis] -= height;\n\t\tmove_end[axis] += height;\n\t\t//\n\t\tLog_Write(\"funcbob model %d, start = {%1.1f, %1.1f, %1.1f} end = {%1.1f, %1.1f, %1.1f}\\n\",\n\t\t\t\t\tmodelnum, move_start[0], move_start[1], move_start[2], move_end[0], move_end[1], move_end[2]);\n\t\t//\n#ifndef BSPC\n\t\t/*\n\t\tAAS_DrawPermanentCross(move_start, 4, 1);\n\t\tAAS_DrawPermanentCross(move_end, 4, 2);\n\t\t*/\n#endif\n\t\t//\n\t\tfor (i = 0; i < 4; i++)\n\t\t{\n\t\t\tVectorCopy(move_start, start_edgeverts[i]);\n\t\t\tstart_edgeverts[i][2] += maxs[2] - mid[2]; //+ bbox maxs z\n\t\t\tstart_edgeverts[i][2] += 24;\t//+ player origin to ground dist\n\t\t} //end for\n\t\tstart_edgeverts[0][0] += maxs[0] - mid[0];\n\t\tstart_edgeverts[0][1] += maxs[1] - mid[1];\n\t\tstart_edgeverts[1][0] += maxs[0] - mid[0];\n\t\tstart_edgeverts[1][1] += mins[1] - mid[1];\n\t\tstart_edgeverts[2][0] += mins[0] - mid[0];\n\t\tstart_edgeverts[2][1] += mins[1] - mid[1];\n\t\tstart_edgeverts[3][0] += mins[0] - mid[0];\n\t\tstart_edgeverts[3][1] += maxs[1] - mid[1];\n\t\t//\n\t\tstart_plane.dist = start_edgeverts[0][2];\n\t\tVectorSet(start_plane.normal, 0, 0, 1);\n\t\t//\n\t\tfor (i = 0; i < 4; i++)\n\t\t{\n\t\t\tVectorCopy(move_end, end_edgeverts[i]);\n\t\t\tend_edgeverts[i][2] += maxs[2] - mid[2]; //+ bbox maxs z\n\t\t\tend_edgeverts[i][2] += 24;\t//+ player origin to ground dist\n\t\t} //end for\n\t\tend_edgeverts[0][0] += maxs[0] - mid[0];\n\t\tend_edgeverts[0][1] += maxs[1] - mid[1];\n\t\tend_edgeverts[1][0] += maxs[0] - mid[0];\n\t\tend_edgeverts[1][1] += mins[1] - mid[1];\n\t\tend_edgeverts[2][0] += mins[0] - mid[0];\n\t\tend_edgeverts[2][1] += mins[1] - mid[1];\n\t\tend_edgeverts[3][0] += mins[0] - mid[0];\n\t\tend_edgeverts[3][1] += maxs[1] - mid[1];\n\t\t//\n\t\tend_plane.dist = end_edgeverts[0][2];\n\t\tVectorSet(end_plane.normal, 0, 0, 1);\n\t\t//\n#ifndef BSPC\n#if 0\n\t\tfor (i = 0; i < 4; i++)\n\t\t{\n\t\t\tAAS_PermanentLine(start_edgeverts[i], start_edgeverts[(i+1)%4], 1);\n\t\t\tAAS_PermanentLine(end_edgeverts[i], end_edgeverts[(i+1)%4], 1);\n\t\t} //end for\n#endif\n#endif\n\t\tVectorCopy(move_start, move_start_top);\n\t\tmove_start_top[2] += maxs[2] - mid[2] + 24; //+ bbox maxs z\n\t\tVectorCopy(move_end, move_end_top);\n\t\tmove_end_top[2] += maxs[2] - mid[2] + 24; //+ bbox maxs z\n\t\t//\n\t\tif (!AAS_PointAreaNum(move_start_top)) continue;\n\t\tif (!AAS_PointAreaNum(move_end_top)) continue;\n\t\t//\n\t\tfor (i = 0; i < 2; i++)\n\t\t{\n\t\t\tfirststartreach = firstendreach = NULL;\n\t\t\t//\n\t\t\tif (i == 0)\n\t\t\t{\n\t\t\t\tfirststartreach = AAS_FindFaceReachabilities(start_edgeverts, 4, &start_plane, qtrue);\n\t\t\t\tfirstendreach = AAS_FindFaceReachabilities(end_edgeverts, 4, &end_plane, qfalse);\n\t\t\t} //end if\n\t\t\telse\n\t\t\t{\n\t\t\t\tfirststartreach = AAS_FindFaceReachabilities(end_edgeverts, 4, &end_plane, qtrue);\n\t\t\t\tfirstendreach = AAS_FindFaceReachabilities(start_edgeverts, 4, &start_plane, qfalse);\n\t\t\t} //end else\n\t\t\t//\n\t\t\t//create reachabilities from start to end\n\t\t\tfor (startreach = firststartreach; startreach; startreach = nextstartreach)\n\t\t\t{\n\t\t\t\tnextstartreach = startreach->next;\n\t\t\t\t//\n\t\t\t\t//trace = AAS_TraceClientBBox(startreach->start, move_start_top, PRESENCE_NORMAL, -1);\n\t\t\t\t//if (trace.fraction < 1) continue;\n\t\t\t\t//\n\t\t\t\tfor (endreach = firstendreach; endreach; endreach = nextendreach)\n\t\t\t\t{\n\t\t\t\t\tnextendreach = endreach->next;\n\t\t\t\t\t//\n\t\t\t\t\t//trace = AAS_TraceClientBBox(endreach->end, move_end_top, PRESENCE_NORMAL, -1);\n\t\t\t\t\t//if (trace.fraction < 1) continue;\n\t\t\t\t\t//\n\t\t\t\t\tLog_Write(\"funcbob reach from area %d to %d\\n\", startreach->areanum, endreach->areanum);\n\t\t\t\t\t//\n\t\t\t\t\t//\n\t\t\t\t\tif (i == 0) VectorCopy(move_start_top, org);\n\t\t\t\t\telse VectorCopy(move_end_top, org);\n\t\t\t\t\tVectorSubtract(startreach->start, org, dir);\n\t\t\t\t\tdir[2] = 0;\n\t\t\t\t\tVectorNormalize(dir);\n\t\t\t\t\tVectorCopy(startreach->start, start);\n\t\t\t\t\tVectorMA(startreach->start, 1, dir, start);\n\t\t\t\t\tstart[2] += 1;\n\t\t\t\t\tVectorMA(startreach->start, 16, dir, end);\n\t\t\t\t\tend[2] += 1;\n\t\t\t\t\t//\n\t\t\t\t\tnumareas = AAS_TraceAreas(start, end, areas, points, 10);\n\t\t\t\t\tif (numareas <= 0) continue;\n\t\t\t\t\tif (numareas > 1) VectorCopy(points[1], startreach->start);\n\t\t\t\t\telse VectorCopy(end, startreach->start);\n\t\t\t\t\t//\n\t\t\t\t\tif (!AAS_PointAreaNum(startreach->start)) continue;\n\t\t\t\t\tif (!AAS_PointAreaNum(endreach->end)) continue;\n\t\t\t\t\t//\n\t\t\t\t\tlreach = AAS_AllocReachability();\n\t\t\t\t\tlreach->areanum = endreach->areanum;\n\t\t\t\t\tif (i == 0) lreach->edgenum = ((int)move_start[axis] << 16) | ((int) move_end[axis] & 0x0000ffff);\n\t\t\t\t\telse lreach->edgenum = ((int)move_end[axis] << 16) | ((int) move_start[axis] & 0x0000ffff);\n\t\t\t\t\tlreach->facenum = (spawnflags << 16) | modelnum;\n\t\t\t\t\tVectorCopy(startreach->start, lreach->start);\n\t\t\t\t\tVectorCopy(endreach->end, lreach->end);\n#ifndef BSPC\n//\t\t\t\t\tAAS_DrawArrow(lreach->start, lreach->end, LINECOLOR_BLUE, LINECOLOR_YELLOW);\n//\t\t\t\t\tAAS_PermanentLine(lreach->start, lreach->end, 1);\n#endif\n\t\t\t\t\tlreach->traveltype = TRAVEL_FUNCBOB;\n\t\t\t\t\tlreach->traveltype |= AAS_TravelFlagsForTeam(ent);\n\t\t\t\t\tlreach->traveltime = aassettings.rs_funcbob;\n\t\t\t\t\treach_funcbob++;\n\t\t\t\t\tlreach->next = areareachability[startreach->areanum];\n\t\t\t\t\tareareachability[startreach->areanum] = lreach;\n\t\t\t\t\t//\n\t\t\t\t} //end for\n\t\t\t} //end for\n\t\t\tfor (startreach = firststartreach; startreach; startreach = nextstartreach)\n\t\t\t{\n\t\t\t\tnextstartreach = startreach->next;\n\t\t\t\tAAS_FreeReachability(startreach);\n\t\t\t} //end for\n\t\t\tfor (endreach = firstendreach; endreach; endreach = nextendreach)\n\t\t\t{\n\t\t\t\tnextendreach = endreach->next;\n\t\t\t\tAAS_FreeReachability(endreach);\n\t\t\t} //end for\n\t\t\t//only go up with func_bobbing entities that go up and down\n\t\t\tif (!(spawnflags & 1) && !(spawnflags & 2)) break;\n\t\t} //end for\n\t} //end for\n} //end of the function AAS_Reachability_FuncBobbing\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid AAS_Reachability_JumpPad(void)\n{\n\tint face2num, i, ret, area2num, visualize, ent, bot_visualizejumppads;\n\t//int modelnum, ent2;\n\t//float dist, time, height, gravity, forward;\n\tfloat speed, zvel;\n\taas_face_t *face2;\n\taas_area_t *area2;\n\taas_lreachability_t *lreach;\n\tvec3_t areastart, facecenter, dir, cmdmove;\n\tvec3_t velocity, absmins, absmaxs;\n\t//vec3_t origin, ent2origin, angles, teststart;\n\taas_clientmove_t move;\n\t//aas_trace_t trace;\n\taas_link_t *areas, *link;\n\t//char target[MAX_EPAIRKEY], targetname[MAX_EPAIRKEY], model[MAX_EPAIRKEY];\n\tchar classname[MAX_EPAIRKEY];\n\n#ifdef BSPC\n\tbot_visualizejumppads = 0;\n#else\n\tbot_visualizejumppads = LibVarValue(\"bot_visualizejumppads\", \"0\");\n#endif\n\tfor (ent = AAS_NextBSPEntity(0); ent; ent = AAS_NextBSPEntity(ent))\n\t{\n\t\tif (!AAS_ValueForBSPEpairKey(ent, \"classname\", classname, MAX_EPAIRKEY)) continue;\n\t\tif (strcmp(classname, \"trigger_push\")) continue;\n\t\t//\n\t\tif (!AAS_GetJumpPadInfo(ent, areastart, absmins, absmaxs, velocity)) continue;\n\t\t/*\n\t\t//\n\t\tAAS_FloatForBSPEpairKey(ent, \"speed\", &speed);\n\t\tif (!speed) speed = 1000;\n//\t\tAAS_VectorForBSPEpairKey(ent, \"angles\", angles);\n//\t\tAAS_SetMovedir(angles, velocity);\n//\t\tVectorScale(velocity, speed, velocity);\n\t\tVectorClear(angles);\n\t\t//get the mins, maxs and origin of the model\n\t\tAAS_ValueForBSPEpairKey(ent, \"model\", model, MAX_EPAIRKEY);\n\t\tif (model[0]) modelnum = atoi(model+1);\n\t\telse modelnum = 0;\n\t\tAAS_BSPModelMinsMaxsOrigin(modelnum, angles, absmins, absmaxs, origin);\n\t\tVectorAdd(origin, absmins, absmins);\n\t\tVectorAdd(origin, absmaxs, absmaxs);\n\t\t//\n#ifdef REACH_DEBUG\n\t\tbotimport.Print(PRT_MESSAGE, \"absmins = %f %f %f\\n\", absmins[0], absmins[1], absmins[2]);\n\t\tbotimport.Print(PRT_MESSAGE, \"absmaxs = %f %f %f\\n\", absmaxs[0], absmaxs[1], absmaxs[2]);\n#endif REACH_DEBUG\n\t\tVectorAdd(absmins, absmaxs, origin);\n\t\tVectorScale (origin, 0.5, origin);\n\n\t\t//get the start areas\n\t\tVectorCopy(origin, teststart);\n\t\tteststart[2] += 64;\n\t\ttrace = AAS_TraceClientBBox(teststart, origin, PRESENCE_CROUCH, -1);\n\t\tif (trace.startsolid)\n\t\t{\n\t\t\tbotimport.Print(PRT_MESSAGE, \"trigger_push start solid\\n\");\n\t\t\tVectorCopy(origin, areastart);\n\t\t} //end if\n\t\telse\n\t\t{\n\t\t\tVectorCopy(trace.endpos, areastart);\n\t\t} //end else\n\t\tareastart[2] += 0.125;\n\t\t//\n\t\t//AAS_DrawPermanentCross(origin, 4, 4);\n\t\t//get the target entity\n\t\tAAS_ValueForBSPEpairKey(ent, \"target\", target, MAX_EPAIRKEY);\n\t\tfor (ent2 = AAS_NextBSPEntity(0); ent2; ent2 = AAS_NextBSPEntity(ent2))\n\t\t{\n\t\t\tif (!AAS_ValueForBSPEpairKey(ent2, \"targetname\", targetname, MAX_EPAIRKEY)) continue;\n\t\t\tif (!strcmp(targetname, target)) break;\n\t\t} //end for\n\t\tif (!ent2)\n\t\t{\n\t\t\tbotimport.Print(PRT_MESSAGE, \"trigger_push without target entity %s\\n\", target);\n\t\t\tcontinue;\n\t\t} //end if\n\t\tAAS_VectorForBSPEpairKey(ent2, \"origin\", ent2origin);\n\t\t//\n\t\theight = ent2origin[2] - origin[2];\n\t\tgravity = aassettings.sv_gravity;\n\t\ttime = sqrt( height / ( 0.5 * gravity ) );\n\t\tif (!time)\n\t\t{\n\t\t\tbotimport.Print(PRT_MESSAGE, \"trigger_push without time\\n\");\n\t\t\tcontinue;\n\t\t} //end if\n\t\t// set s.origin2 to the push velocity\n\t\tVectorSubtract ( ent2origin, origin, velocity);\n\t\tdist = VectorNormalize( velocity);\n\t\tforward = dist / time;\n\t\t//FIXME: why multiply by 1.1\n\t\tforward *= 1.1;\n\t\tVectorScale(velocity, forward, velocity);\n\t\tvelocity[2] = time * gravity;\n\t\t*/\n\t\t//get the areas the jump pad brush is in\n\t\tareas = AAS_LinkEntityClientBBox(absmins, absmaxs, -1, PRESENCE_CROUCH);\n\t\t/*\n\t\tfor (link = areas; link; link = link->next_area)\n\t\t{\n\t\t\tif (link->areanum == 563)\n\t\t\t{\n\t\t\t\tret = qfalse;\n\t\t\t}\n\t\t}\n        */\n\t\tfor (link = areas; link; link = link->next_area)\n\t\t{\n\t\t\tif (AAS_AreaJumpPad(link->areanum)) break;\n\t\t} //end for\n\t\tif (!link)\n\t\t{\n\t\t\tbotimport.Print(PRT_MESSAGE, \"trigger_push not in any jump pad area\\n\");\n\t\t\tAAS_UnlinkFromAreas(areas);\n\t\t\tcontinue;\n\t\t} //end if\n\t\t//\n\t\tbotimport.Print(PRT_MESSAGE, \"found a trigger_push with velocity %f %f %f\\n\", velocity[0], velocity[1], velocity[2]);\n\t\t//if there is a horizontal velocity check for a reachability without air control\n\t\tif (velocity[0] || velocity[1])\n\t\t{\n\t\t\tVectorSet(cmdmove, 0, 0, 0);\n\t\t\t//VectorCopy(velocity, cmdmove);\n\t\t\t//cmdmove[2] = 0;\n\t\t\tCom_Memset(&move, 0, sizeof(aas_clientmove_t));\n\t\t\tarea2num = 0;\n\t\t\tfor (i = 0; i < 20; i++)\n\t\t\t{\n\t\t\t\tAAS_PredictClientMovement(&move, -1, areastart, PRESENCE_NORMAL, qfalse,\n\t\t\t\t\t\t\t\t\t\tvelocity, cmdmove, 0, 30, 0.1f,\n\t\t\t\t\t\t\t\t\t\tSE_HITGROUND|SE_ENTERWATER|SE_ENTERSLIME|\n\t\t\t\t\t\t\t\t\t\tSE_ENTERLAVA|SE_HITGROUNDDAMAGE|SE_TOUCHJUMPPAD|SE_TOUCHTELEPORTER, 0, bot_visualizejumppads);\n\t\t\t\tarea2num = move.endarea;\n\t\t\t\tfor (link = areas; link; link = link->next_area)\n\t\t\t\t{\n\t\t\t\t\tif (!AAS_AreaJumpPad(link->areanum)) continue;\n\t\t\t\t\tif (link->areanum == area2num) break;\n\t\t\t\t} //end if\n\t\t\t\tif (!link) break;\n\t\t\t\tVectorCopy(move.endpos, areastart);\n\t\t\t\tVectorCopy(move.velocity, velocity);\n\t\t\t} //end for\n\t\t\tif (area2num && i < 20)\n\t\t\t{\n\t\t\t\tfor (link = areas; link; link = link->next_area)\n\t\t\t\t{\n\t\t\t\t\tif (!AAS_AreaJumpPad(link->areanum)) continue;\n\t\t\t\t\tif (AAS_ReachabilityExists(link->areanum, area2num)) continue;\n\t\t\t\t\t//create a rocket or bfg jump reachability from area1 to area2\n\t\t\t\t\tlreach = AAS_AllocReachability();\n\t\t\t\t\tif (!lreach)\n\t\t\t\t\t{\n\t\t\t\t\t\tAAS_UnlinkFromAreas(areas);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t} //end if\n\t\t\t\t\tlreach->areanum = area2num;\n\t\t\t\t\t//NOTE: the facenum is the Z velocity\n\t\t\t\t\tlreach->facenum = velocity[2];\n\t\t\t\t\t//NOTE: the edgenum is the horizontal velocity\n\t\t\t\t\tlreach->edgenum = sqrt(velocity[0] * velocity[0] + velocity[1] * velocity[1]);\n\t\t\t\t\tVectorCopy(areastart, lreach->start);\n\t\t\t\t\tVectorCopy(move.endpos, lreach->end);\n\t\t\t\t\tlreach->traveltype = TRAVEL_JUMPPAD;\n\t\t\t\t\tlreach->traveltype |= AAS_TravelFlagsForTeam(ent);\n\t\t\t\t\tlreach->traveltime = aassettings.rs_jumppad;\n\t\t\t\t\tlreach->next = areareachability[link->areanum];\n\t\t\t\t\tareareachability[link->areanum] = lreach;\n\t\t\t\t\t//\n\t\t\t\t\treach_jumppad++;\n\t\t\t\t} //end for\n\t\t\t} //end if\n\t\t} //end if\n\t\t//\n\t\tif (fabs(velocity[0]) > 100 || fabs(velocity[1]) > 100) continue;\n\t\t//check for areas we can reach with air control\n\t\tfor (area2num = 1; area2num < aasworld.numareas; area2num++)\n\t\t{\n\t\t\tvisualize = qfalse;\n\t\t\t/*\n\t\t\tif (area2num == 3568)\n\t\t\t{\n\t\t\t\tfor (link = areas; link; link = link->next_area)\n\t\t\t\t{\n\t\t\t\t\tif (link->areanum == 3380)\n\t\t\t\t\t{\n\t\t\t\t\t\tvisualize = qtrue;\n\t\t\t\t\t\tbotimport.Print(PRT_MESSAGE, \"bah\\n\");\n\t\t\t\t\t} //end if\n\t\t\t\t} //end for\n\t\t\t} //end if*/\n\t\t\t//never try to go back to one of the original jumppad areas\n\t\t\t//and don't create reachabilities if they already exist\n\t\t\tfor (link = areas; link; link = link->next_area)\n\t\t\t{\n\t\t\t\tif (AAS_ReachabilityExists(link->areanum, area2num)) break;\n\t\t\t\tif (AAS_AreaJumpPad(link->areanum))\n\t\t\t\t{\n\t\t\t\t\tif (link->areanum == area2num) break;\n\t\t\t\t} //end if\n\t\t\t} //end if\n\t\t\tif (link) continue;\n\t\t\t//\n\t\t\tarea2 = &aasworld.areas[area2num];\n\t\t\tfor (i = 0; i < area2->numfaces; i++)\n\t\t\t{\n\t\t\t\tface2num = aasworld.faceindex[area2->firstface + i];\n\t\t\t\tface2 = &aasworld.faces[abs(face2num)];\n\t\t\t\t//if it is not a ground face\n\t\t\t\tif (!(face2->faceflags & FACE_GROUND)) continue;\n\t\t\t\t//get the center of the face\n\t\t\t\tAAS_FaceCenter(face2num, facecenter);\n\t\t\t\t//only go higher up\n\t\t\t\tif (facecenter[2] < areastart[2]) continue;\n\t\t\t\t//get the jumppad jump z velocity\n\t\t\t\tzvel = velocity[2];\n\t\t\t\t//get the horizontal speed for the jump, if it isn't possible to calculate this\n\t\t\t\t//speed\n\t\t\t\tret = AAS_HorizontalVelocityForJump(zvel, areastart, facecenter, &speed);\n\t\t\t\tif (ret && speed < 150)\n\t\t\t\t{\n\t\t\t\t\t//direction towards the face center\n\t\t\t\t\tVectorSubtract(facecenter, areastart, dir);\n\t\t\t\t\tdir[2] = 0;\n\t\t\t\t\t//hordist = VectorNormalize(dir);\n\t\t\t\t\t//if (hordist < 1.6 * facecenter[2] - areastart[2])\n\t\t\t\t\t{\n\t\t\t\t\t\t//get command movement\n\t\t\t\t\t\tVectorScale(dir, speed, cmdmove);\n\t\t\t\t\t\t//\n\t\t\t\t\t\tAAS_PredictClientMovement(&move, -1, areastart, PRESENCE_NORMAL, qfalse,\n\t\t\t\t\t\t\t\t\t\t\t\t\tvelocity, cmdmove, 30, 30, 0.1f,\n\t\t\t\t\t\t\t\t\t\t\t\t\tSE_ENTERWATER|SE_ENTERSLIME|\n\t\t\t\t\t\t\t\t\t\t\t\t\tSE_ENTERLAVA|SE_HITGROUNDDAMAGE|\n\t\t\t\t\t\t\t\t\t\t\t\t\tSE_TOUCHJUMPPAD|SE_TOUCHTELEPORTER|SE_HITGROUNDAREA, area2num, visualize);\n\t\t\t\t\t\t//if prediction time wasn't enough to fully predict the movement\n\t\t\t\t\t\t//don't enter slime or lava and don't fall from too high\n\t\t\t\t\t\tif (move.frames < 30 && \n\t\t\t\t\t\t\t\t!(move.stopevent & (SE_ENTERSLIME|SE_ENTERLAVA|SE_HITGROUNDDAMAGE))\n\t\t\t\t\t\t\t\t&& (move.stopevent & (SE_HITGROUNDAREA|SE_TOUCHJUMPPAD|SE_TOUCHTELEPORTER)))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t//never go back to the same jumppad\n\t\t\t\t\t\t\tfor (link = areas; link; link = link->next_area)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tif (link->areanum == move.endarea) break;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (!link)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tfor (link = areas; link; link = link->next_area)\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tif (!AAS_AreaJumpPad(link->areanum)) continue;\n\t\t\t\t\t\t\t\t\tif (AAS_ReachabilityExists(link->areanum, area2num)) continue;\n\t\t\t\t\t\t\t\t\t//create a jumppad reachability from area1 to area2\n\t\t\t\t\t\t\t\t\tlreach = AAS_AllocReachability();\n\t\t\t\t\t\t\t\t\tif (!lreach)\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tAAS_UnlinkFromAreas(areas);\n\t\t\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t\t\t} //end if\n\t\t\t\t\t\t\t\t\tlreach->areanum = move.endarea;\n\t\t\t\t\t\t\t\t\t//NOTE: the facenum is the Z velocity\n\t\t\t\t\t\t\t\t\tlreach->facenum = velocity[2];\n\t\t\t\t\t\t\t\t\t//NOTE: the edgenum is the horizontal velocity\n\t\t\t\t\t\t\t\t\tlreach->edgenum = sqrt(cmdmove[0] * cmdmove[0] + cmdmove[1] * cmdmove[1]);\n\t\t\t\t\t\t\t\t\tVectorCopy(areastart, lreach->start);\n\t\t\t\t\t\t\t\t\tVectorCopy(facecenter, lreach->end);\n\t\t\t\t\t\t\t\t\tlreach->traveltype = TRAVEL_JUMPPAD;\n\t\t\t\t\t\t\t\t\tlreach->traveltype |= AAS_TravelFlagsForTeam(ent);\n\t\t\t\t\t\t\t\t\tlreach->traveltime = aassettings.rs_aircontrolledjumppad;\n\t\t\t\t\t\t\t\t\tlreach->next = areareachability[link->areanum];\n\t\t\t\t\t\t\t\t\tareareachability[link->areanum] = lreach;\n\t\t\t\t\t\t\t\t\t//\n\t\t\t\t\t\t\t\t\treach_jumppad++;\n\t\t\t\t\t\t\t\t} //end for\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} //end if\n\t\t\t\t\t} //end if\n\t\t\t\t} //end for\n\t\t\t} //end for\n\t\t} //end for\n\t\tAAS_UnlinkFromAreas(areas);\n\t} //end for\n} //end of the function AAS_Reachability_JumpPad\n//===========================================================================\n// never point at ground faces\n// always a higher and pretty far area\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint AAS_Reachability_Grapple(int area1num, int area2num)\n{\n\tint face2num, i, j, areanum, numareas, areas[20];\n\tfloat mingrappleangle, z, hordist;\n\tbsp_trace_t bsptrace;\n\taas_trace_t trace;\n\taas_face_t *face2;\n\taas_area_t *area1, *area2;\n\taas_lreachability_t *lreach;\n\tvec3_t areastart, facecenter, start, end, dir, down = {0, 0, -1};\n\tvec_t *v;\n\n\t//only grapple when on the ground or swimming\n\tif (!AAS_AreaGrounded(area1num) && !AAS_AreaSwim(area1num)) return qfalse;\n\t//don't grapple from a crouch area\n\tif (!(AAS_AreaPresenceType(area1num) & PRESENCE_NORMAL)) return qfalse;\n\t//NOTE: disabled area swim it doesn't work right\n\tif (AAS_AreaSwim(area1num)) return qfalse;\n\t//\n\tarea1 = &aasworld.areas[area1num];\n\tarea2 = &aasworld.areas[area2num];\n\t//don't grapple towards way lower areas\n\tif (area2->maxs[2] < area1->mins[2]) return qfalse;\n\t//\n\tVectorCopy(aasworld.areas[area1num].center, start);\n\t//if not a swim area\n\tif (!AAS_AreaSwim(area1num))\n\t{\n\t\tif (!AAS_PointAreaNum(start)) Log_Write(\"area %d center %f %f %f in solid?\\r\\n\", area1num,\n\t\t\t\t\t\t\t\tstart[0], start[1], start[2]);\n\t\tVectorCopy(start, end);\n\t\tend[2] -= 1000;\n\t\ttrace = AAS_TraceClientBBox(start, end, PRESENCE_CROUCH, -1);\n\t\tif (trace.startsolid) return qfalse;\n\t\tVectorCopy(trace.endpos, areastart);\n\t} //end if\n\telse\n\t{\n\t\tif (!(AAS_PointContents(start) & (CONTENTS_LAVA|CONTENTS_SLIME|CONTENTS_WATER))) return qfalse;\n\t} //end else\n\t//\n\t//start is now the start point\n\t//\n\tfor (i = 0; i < area2->numfaces; i++)\n\t{\n\t\tface2num = aasworld.faceindex[area2->firstface + i];\n\t\tface2 = &aasworld.faces[abs(face2num)];\n\t\t//if it is not a solid face\n\t\tif (!(face2->faceflags & FACE_SOLID)) continue;\n\t\t//direction towards the first vertex of the face\n\t\tv = aasworld.vertexes[aasworld.edges[abs(aasworld.edgeindex[face2->firstedge])].v[0]];\n\t\tVectorSubtract(v, areastart, dir);\n\t\t//if the face plane is facing away\n\t\tif (DotProduct(aasworld.planes[face2->planenum].normal, dir) > 0) continue;\n\t\t//get the center of the face\n\t\tAAS_FaceCenter(face2num, facecenter);\n\t\t//only go higher up with the grapple\n\t\tif (facecenter[2] < areastart[2] + 64) continue;\n\t\t//only use vertical faces or downward facing faces\n\t\tif (DotProduct(aasworld.planes[face2->planenum].normal, down) < 0) continue;\n\t\t//direction towards the face center\n\t\tVectorSubtract(facecenter, areastart, dir);\n\t\t//\n\t\tz = dir[2];\n\t\tdir[2] = 0;\n\t\thordist = VectorLength(dir);\n\t\tif (!hordist) continue;\n\t\t//if too far\n\t\tif (hordist > 2000) continue;\n\t\t//check the minimal angle of the movement\n\t\tmingrappleangle = 15; //15 degrees\n\t\tif (z / hordist < tan(2 * M_PI * mingrappleangle / 360)) continue;\n\t\t//\n\t\tVectorCopy(facecenter, start);\n\t\tVectorMA(facecenter, -500, aasworld.planes[face2->planenum].normal, end);\n\t\t//\n\t\tbsptrace = AAS_Trace(start, NULL, NULL, end, 0, CONTENTS_SOLID);\n\t\t//the grapple won't stick to the sky and the grapple point should be near the AAS wall\n\t\tif ((bsptrace.surface.flags & SURF_SKY) || (bsptrace.fraction * 500 > 32)) continue;\n\t\t//trace a full bounding box from the area center on the ground to\n\t\t//the center of the face\n\t\tVectorSubtract(facecenter, areastart, dir);\n\t\tVectorNormalize(dir);\n\t\tVectorMA(areastart, 4, dir, start);\n\t\tVectorCopy(bsptrace.endpos, end);\n\t\ttrace = AAS_TraceClientBBox(start, end, PRESENCE_NORMAL, -1);\n\t\tVectorSubtract(trace.endpos, facecenter, dir);\n\t\tif (VectorLength(dir) > 24) continue;\n\t\t//\n\t\tVectorCopy(trace.endpos, start);\n\t\tVectorCopy(trace.endpos, end);\n\t\tend[2] -= AAS_FallDamageDistance();\n\t\ttrace = AAS_TraceClientBBox(start, end, PRESENCE_NORMAL, -1);\n\t\tif (trace.fraction >= 1) continue;\n\t\t//area to end in\n\t\tareanum = AAS_PointAreaNum(trace.endpos);\n\t\t//if not in lava or slime\n\t\tif (aasworld.areasettings[areanum].contents & (AREACONTENTS_SLIME|AREACONTENTS_LAVA))\n\t\t{\n\t\t\tcontinue;\n\t\t} //end if\n\t\t//do not go the the source area\n\t\tif (areanum == area1num) continue;\n\t\t//don't create reachabilities if they already exist\n\t\tif (AAS_ReachabilityExists(area1num, areanum)) continue;\n\t\t//only end in areas we can stand\n\t\tif (!AAS_AreaGrounded(areanum)) continue;\n\t\t//never go through cluster portals!!\n\t\tnumareas = AAS_TraceAreas(areastart, bsptrace.endpos, areas, NULL, 20);\n\t\tif (numareas >= 20) continue;\n\t\tfor (j = 0; j < numareas; j++)\n\t\t{\n\t\t\tif (aasworld.areasettings[areas[j]].contents & AREACONTENTS_CLUSTERPORTAL) break;\n\t\t} //end for\n\t\tif (j < numareas) continue;\n\t\t//create a new reachability link\n\t\tlreach = AAS_AllocReachability();\n\t\tif (!lreach) return qfalse;\n\t\tlreach->areanum = areanum;\n\t\tlreach->facenum = face2num;\n\t\tlreach->edgenum = 0;\n\t\tVectorCopy(areastart, lreach->start);\n\t\t//VectorCopy(facecenter, lreach->end);\n\t\tVectorCopy(bsptrace.endpos, lreach->end);\n\t\tlreach->traveltype = TRAVEL_GRAPPLEHOOK;\n\t\tVectorSubtract(lreach->end, lreach->start, dir);\n\t\tlreach->traveltime = aassettings.rs_startgrapple + VectorLength(dir) * 0.25;\n\t\tlreach->next = areareachability[area1num];\n\t\tareareachability[area1num] = lreach;\n\t\t//\n\t\treach_grapple++;\n\t} //end for\n\t//\n\treturn qfalse;\n} //end of the function AAS_Reachability_Grapple\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid AAS_SetWeaponJumpAreaFlags(void)\n{\n\tint ent, i;\n\tvec3_t mins = {-15, -15, -15}, maxs = {15, 15, 15};\n\tvec3_t origin;\n\tint areanum, weaponjumpareas, spawnflags;\n\tchar classname[MAX_EPAIRKEY];\n\n\tweaponjumpareas = 0;\n\tfor (ent = AAS_NextBSPEntity(0); ent; ent = AAS_NextBSPEntity(ent))\n\t{\n\t\tif (!AAS_ValueForBSPEpairKey(ent, \"classname\", classname, MAX_EPAIRKEY)) continue;\n\t\tif (\n\t\t\t!strcmp(classname, \"item_armor_body\") ||\n\t\t\t!strcmp(classname, \"item_armor_combat\") ||\n\t\t\t!strcmp(classname, \"item_health_mega\") ||\n\t\t\t!strcmp(classname, \"weapon_grenadelauncher\") ||\n\t\t\t!strcmp(classname, \"weapon_rocketlauncher\") ||\n\t\t\t!strcmp(classname, \"weapon_lightning\") ||\n\t\t\t!strcmp(classname, \"weapon_plasmagun\") ||\n\t\t\t!strcmp(classname, \"weapon_railgun\") ||\n\t\t\t!strcmp(classname, \"weapon_bfg\") ||\n\t\t\t!strcmp(classname, \"item_quad\") ||\n\t\t\t!strcmp(classname, \"item_regen\") ||\n\t\t\t!strcmp(classname, \"item_invulnerability\"))\n\t\t{\n\t\t\tif (AAS_VectorForBSPEpairKey(ent, \"origin\", origin))\n\t\t\t{\n\t\t\t\tspawnflags = 0;\n\t\t\t\tAAS_IntForBSPEpairKey(ent, \"spawnflags\", &spawnflags);\n\t\t\t\t//if not a stationary item\n\t\t\t\tif (!(spawnflags & 1))\n\t\t\t\t{\n\t\t\t\t\tif (!AAS_DropToFloor(origin, mins, maxs))\n\t\t\t\t\t{\n\t\t\t\t\t\tbotimport.Print(PRT_MESSAGE, \"%s in solid at (%1.1f %1.1f %1.1f)\\n\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tclassname, origin[0], origin[1], origin[2]);\n\t\t\t\t\t} //end if\n\t\t\t\t} //end if\n\t\t\t\t//areanum = AAS_PointAreaNum(origin);\n\t\t\t\tareanum = AAS_BestReachableArea(origin, mins, maxs, origin);\n\t\t\t\t//the bot may rocket jump towards this area\n\t\t\t\taasworld.areasettings[areanum].areaflags |= AREA_WEAPONJUMP;\n\t\t\t\t//\n\t\t\t\t//if (!AAS_AreaGrounded(areanum))\n\t\t\t\t//\tbotimport.Print(PRT_MESSAGE, \"area not grounded\\n\");\n\t\t\t\t//\n\t\t\t\tweaponjumpareas++;\n\t\t\t} //end if\n\t\t} //end if\n\t} //end for\n\tfor (i = 1; i < aasworld.numareas; i++)\n\t{\n\t\tif (aasworld.areasettings[i].contents & AREACONTENTS_JUMPPAD)\n\t\t{\n\t\t\taasworld.areasettings[i].areaflags |= AREA_WEAPONJUMP;\n\t\t\tweaponjumpareas++;\n\t\t} //end if\n\t} //end for\n\tbotimport.Print(PRT_MESSAGE, \"%d weapon jump areas\\n\", weaponjumpareas);\n} //end of the function AAS_SetWeaponJumpAreaFlags\n//===========================================================================\n// create a possible weapon jump reachability from area1 to area2\n//\n// check if there's a cool item in the second area\n// check if area1 is lower than area2\n// check if the bot can rocketjump from area1 to area2\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint AAS_Reachability_WeaponJump(int area1num, int area2num)\n{\n\tint face2num, i, n, ret, visualize;\n\tfloat speed, zvel;\n\taas_face_t *face2;\n\taas_area_t *area1, *area2;\n\taas_lreachability_t *lreach;\n\tvec3_t areastart, facecenter, start, end, dir, cmdmove;// teststart;\n\tvec3_t velocity;\n\taas_clientmove_t move;\n\taas_trace_t trace;\n\n\tvisualize = qfalse;\n//\tif (area1num == 4436 && area2num == 4318)\n//\t{\n//\t\tvisualize = qtrue;\n//\t}\n\tif (!AAS_AreaGrounded(area1num) || AAS_AreaSwim(area1num)) return qfalse;\n\tif (!AAS_AreaGrounded(area2num)) return qfalse;\n\t//NOTE: only weapon jump towards areas with an interesting item in it??\n\tif (!(aasworld.areasettings[area2num].areaflags & AREA_WEAPONJUMP)) return qfalse;\n\t//\n\tarea1 = &aasworld.areas[area1num];\n\tarea2 = &aasworld.areas[area2num];\n\t//don't weapon jump towards way lower areas\n\tif (area2->maxs[2] < area1->mins[2]) return qfalse;\n\t//\n\tVectorCopy(aasworld.areas[area1num].center, start);\n\t//if not a swim area\n\tif (!AAS_PointAreaNum(start)) Log_Write(\"area %d center %f %f %f in solid?\\r\\n\", area1num,\n\t\t\t\t\t\t\tstart[0], start[1], start[2]);\n\tVectorCopy(start, end);\n\tend[2] -= 1000;\n\ttrace = AAS_TraceClientBBox(start, end, PRESENCE_CROUCH, -1);\n\tif (trace.startsolid) return qfalse;\n\tVectorCopy(trace.endpos, areastart);\n\t//\n\t//areastart is now the start point\n\t//\n\tfor (i = 0; i < area2->numfaces; i++)\n\t{\n\t\tface2num = aasworld.faceindex[area2->firstface + i];\n\t\tface2 = &aasworld.faces[abs(face2num)];\n\t\t//if it is not a solid face\n\t\tif (!(face2->faceflags & FACE_GROUND)) continue;\n\t\t//get the center of the face\n\t\tAAS_FaceCenter(face2num, facecenter);\n\t\t//only go higher up with weapon jumps\n\t\tif (facecenter[2] < areastart[2] + 64) continue;\n\t\t//NOTE: set to 2 to allow bfg jump reachabilities\n\t\tfor (n = 0; n < 1/*2*/; n++)\n\t\t{\n\t\t\t//get the rocket jump z velocity\n\t\t\tif (n) zvel = AAS_BFGJumpZVelocity(areastart);\n\t\t\telse zvel = AAS_RocketJumpZVelocity(areastart);\n\t\t\t//get the horizontal speed for the jump, if it isn't possible to calculate this\n\t\t\t//speed (the jump is not possible) then there's no jump reachability created\n\t\t\tret = AAS_HorizontalVelocityForJump(zvel, areastart, facecenter, &speed);\n\t\t\tif (ret && speed < 300)\n\t\t\t{\n\t\t\t\t//direction towards the face center\n\t\t\t\tVectorSubtract(facecenter, areastart, dir);\n\t\t\t\tdir[2] = 0;\n\t\t\t\t//hordist = VectorNormalize(dir);\n\t\t\t\t//if (hordist < 1.6 * (facecenter[2] - areastart[2]))\n\t\t\t\t{\n\t\t\t\t\t//get command movement\n\t\t\t\t\tVectorScale(dir, speed, cmdmove);\n\t\t\t\t\tVectorSet(velocity, 0, 0, zvel);\n\t\t\t\t\t/*\n\t\t\t\t\t//get command movement\n\t\t\t\t\tVectorScale(dir, speed, velocity);\n\t\t\t\t\tvelocity[2] = zvel;\n\t\t\t\t\tVectorSet(cmdmove, 0, 0, 0);\n\t\t\t\t\t*/\n\t\t\t\t\t//\n\t\t\t\t\tAAS_PredictClientMovement(&move, -1, areastart, PRESENCE_NORMAL, qtrue,\n\t\t\t\t\t\t\t\t\t\t\t\tvelocity, cmdmove, 30, 30, 0.1f,\n\t\t\t\t\t\t\t\t\t\t\t\tSE_ENTERWATER|SE_ENTERSLIME|\n\t\t\t\t\t\t\t\t\t\t\t\tSE_ENTERLAVA|SE_HITGROUNDDAMAGE|\n\t\t\t\t\t\t\t\t\t\t\t\tSE_TOUCHJUMPPAD|SE_HITGROUND|SE_HITGROUNDAREA, area2num, visualize);\n\t\t\t\t\t//if prediction time wasn't enough to fully predict the movement\n\t\t\t\t\t//don't enter slime or lava and don't fall from too high\n\t\t\t\t\tif (move.frames < 30 && \n\t\t\t\t\t\t\t!(move.stopevent & (SE_ENTERSLIME|SE_ENTERLAVA|SE_HITGROUNDDAMAGE))\n\t\t\t\t\t\t\t\t&& (move.stopevent & (SE_HITGROUNDAREA|SE_TOUCHJUMPPAD)))\n\t\t\t\t\t{\n\t\t\t\t\t\t//create a rocket or bfg jump reachability from area1 to area2\n\t\t\t\t\t\tlreach = AAS_AllocReachability();\n\t\t\t\t\t\tif (!lreach) return qfalse;\n\t\t\t\t\t\tlreach->areanum = area2num;\n\t\t\t\t\t\tlreach->facenum = 0;\n\t\t\t\t\t\tlreach->edgenum = 0;\n\t\t\t\t\t\tVectorCopy(areastart, lreach->start);\n\t\t\t\t\t\tVectorCopy(facecenter, lreach->end);\n\t\t\t\t\t\tif (n)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tlreach->traveltype = TRAVEL_BFGJUMP;\n\t\t\t\t\t\t\tlreach->traveltime = aassettings.rs_bfgjump;\n\t\t\t\t\t\t} //end if\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tlreach->traveltype = TRAVEL_ROCKETJUMP;\n\t\t\t\t\t\t\tlreach->traveltime = aassettings.rs_rocketjump;\n\t\t\t\t\t\t} //end else\n\t\t\t\t\t\tlreach->next = areareachability[area1num];\n\t\t\t\t\t\tareareachability[area1num] = lreach;\n\t\t\t\t\t\t//\n\t\t\t\t\t\treach_rocketjump++;\n\t\t\t\t\t\treturn qtrue;\n\t\t\t\t\t} //end if\n\t\t\t\t} //end if\n\t\t\t} //end if\n\t\t} //end for\n\t} //end for\n\t//\n\treturn qfalse;\n} //end of the function AAS_Reachability_WeaponJump\n//===========================================================================\n// calculates additional walk off ledge reachabilities for the given area\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid AAS_Reachability_WalkOffLedge(int areanum)\n{\n\tint i, j, k, l, m, n, p, areas[10], numareas;\n\tint face1num, face2num, face3num, edge1num, edge2num, edge3num;\n\tint otherareanum, gap, reachareanum, side;\n\taas_area_t *area, *area2;\n\taas_face_t *face1, *face2, *face3;\n\taas_edge_t *edge;\n\taas_plane_t *plane;\n\tvec_t *v1, *v2;\n\tvec3_t sharededgevec, mid, dir, testend;\n\taas_lreachability_t *lreach;\n\taas_trace_t trace;\n\n\tif (!AAS_AreaGrounded(areanum) || AAS_AreaSwim(areanum)) return;\n\t//\n\tarea = &aasworld.areas[areanum];\n\t//\n\tfor (i = 0; i < area->numfaces; i++)\n\t{\n\t\tface1num = aasworld.faceindex[area->firstface + i];\n\t\tface1 = &aasworld.faces[abs(face1num)];\n\t\t//face 1 must be a ground face\n\t\tif (!(face1->faceflags & FACE_GROUND)) continue;\n\t\t//go through all the edges of this ground face\n\t\tfor (k = 0; k < face1->numedges; k++)\n\t\t{\n\t\t\tedge1num = aasworld.edgeindex[face1->firstedge + k];\n\t\t\t//find another not ground face using this same edge\n\t\t\tfor (j = 0; j < area->numfaces; j++)\n\t\t\t{\n\t\t\t\tface2num = aasworld.faceindex[area->firstface + j];\n\t\t\t\tface2 = &aasworld.faces[abs(face2num)];\n\t\t\t\t//face 2 may not be a ground face\n\t\t\t\tif (face2->faceflags & FACE_GROUND) continue;\n\t\t\t\t//compare all the edges\n\t\t\t\tfor (l = 0; l < face2->numedges; l++)\n\t\t\t\t{\n\t\t\t\t\tedge2num = aasworld.edgeindex[face2->firstedge + l];\n\t\t\t\t\tif (abs(edge1num) == abs(edge2num))\n\t\t\t\t\t{\n\t\t\t\t\t\t//get the area at the other side of the face\n\t\t\t\t\t\tif (face2->frontarea == areanum) otherareanum = face2->backarea;\n\t\t\t\t\t\telse otherareanum = face2->frontarea;\n\t\t\t\t\t\t//\n\t\t\t\t\t\tarea2 = &aasworld.areas[otherareanum];\n\t\t\t\t\t\t//if the other area is grounded!\n\t\t\t\t\t\tif (aasworld.areasettings[otherareanum].areaflags & AREA_GROUNDED)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t//check for a possible gap\n\t\t\t\t\t\t\tgap = qfalse;\n\t\t\t\t\t\t\tfor (n = 0; n < area2->numfaces; n++)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tface3num = aasworld.faceindex[area2->firstface + n];\n\t\t\t\t\t\t\t\t//may not be the shared face of the two areas\n\t\t\t\t\t\t\t\tif (abs(face3num) == abs(face2num)) continue;\n\t\t\t\t\t\t\t\t//\n\t\t\t\t\t\t\t\tface3 = &aasworld.faces[abs(face3num)];\n\t\t\t\t\t\t\t\t//find an edge shared by all three faces\n\t\t\t\t\t\t\t\tfor (m = 0; m < face3->numedges; m++)\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tedge3num = aasworld.edgeindex[face3->firstedge + m];\n\t\t\t\t\t\t\t\t\t//but the edge should be shared by all three faces\n\t\t\t\t\t\t\t\t\tif (abs(edge3num) == abs(edge1num))\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tif (!(face3->faceflags & FACE_SOLID))\n\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\tgap = qtrue;\n\t\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t\t\t} //end if\n\t\t\t\t\t\t\t\t\t\t//\n\t\t\t\t\t\t\t\t\t\tif (face3->faceflags & FACE_GROUND)\n\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\tgap = qfalse;\n\t\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t\t\t} //end if\n\t\t\t\t\t\t\t\t\t\t//FIXME: there are more situations to be handled\n\t\t\t\t\t\t\t\t\t\tgap = qtrue;\n\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t\t} //end if\n\t\t\t\t\t\t\t\t} //end for\n\t\t\t\t\t\t\t\tif (m < face3->numedges) break;\n\t\t\t\t\t\t\t} //end for\n\t\t\t\t\t\t\tif (!gap) break;\n\t\t\t\t\t\t} //end if\n\t\t\t\t\t\t//check for a walk off ledge reachability\n\t\t\t\t\t\tedge = &aasworld.edges[abs(edge1num)];\n\t\t\t\t\t\tside = edge1num < 0;\n\t\t\t\t\t\t//\n\t\t\t\t\t\tv1 = aasworld.vertexes[edge->v[side]];\n\t\t\t\t\t\tv2 = aasworld.vertexes[edge->v[!side]];\n\t\t\t\t\t\t//\n\t\t\t\t\t\tplane = &aasworld.planes[face1->planenum];\n\t\t\t\t\t\t//get the points really into the areas\n\t\t\t\t\t\tVectorSubtract(v2, v1, sharededgevec);\n\t\t\t\t\t\tCrossProduct(plane->normal, sharededgevec, dir);\n\t\t\t\t\t\tVectorNormalize(dir);\n\t\t\t\t\t\t//\n\t\t\t\t\t\tVectorAdd(v1, v2, mid);\n\t\t\t\t\t\tVectorScale(mid, 0.5, mid);\n\t\t\t\t\t\tVectorMA(mid, 8, dir, mid);\n\t\t\t\t\t\t//\n\t\t\t\t\t\tVectorCopy(mid, testend);\n\t\t\t\t\t\ttestend[2] -= 1000;\n\t\t\t\t\t\ttrace = AAS_TraceClientBBox(mid, testend, PRESENCE_CROUCH, -1);\n\t\t\t\t\t\t//\n\t\t\t\t\t\tif (trace.startsolid)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t//Log_Write(\"area %d: trace.startsolid\\r\\n\", areanum);\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t} //end if\n\t\t\t\t\t\treachareanum = AAS_PointAreaNum(trace.endpos);\n\t\t\t\t\t\tif (reachareanum == areanum)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t//Log_Write(\"area %d: same area\\r\\n\", areanum);\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t} //end if\n\t\t\t\t\t\tif (AAS_ReachabilityExists(areanum, reachareanum))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t//Log_Write(\"area %d: reachability already exists\\r\\n\", areanum);\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t} //end if\n\t\t\t\t\t\tif (!AAS_AreaGrounded(reachareanum) && !AAS_AreaSwim(reachareanum))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t//Log_Write(\"area %d, reach area %d: not grounded and not swim\\r\\n\", areanum, reachareanum);\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t} //end if\n\t\t\t\t\t\t//\n\t\t\t\t\t\tif (aasworld.areasettings[reachareanum].contents & (AREACONTENTS_SLIME\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t| AREACONTENTS_LAVA))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t//Log_Write(\"area %d, reach area %d: lava or slime\\r\\n\", areanum, reachareanum);\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t} //end if\n\t\t\t\t\t\t//if not going through a cluster portal\n\t\t\t\t\t\tnumareas = AAS_TraceAreas(mid, testend, areas, NULL, ARRAY_LEN(areas));\n\t\t\t\t\t\tfor (p = 0; p < numareas; p++)\n\t\t\t\t\t\t\tif (AAS_AreaClusterPortal(areas[p]))\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tif (p < numareas)\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t// if a maximum fall height is set and the bot would fall down further\n\t\t\t\t\t\tif (aassettings.rs_maxfallheight && fabs(mid[2] - trace.endpos[2]) > aassettings.rs_maxfallheight)\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t//\n\t\t\t\t\t\tlreach = AAS_AllocReachability();\n\t\t\t\t\t\tif (!lreach) break;\n\t\t\t\t\t\tlreach->areanum = reachareanum;\n\t\t\t\t\t\tlreach->facenum = 0;\n\t\t\t\t\t\tlreach->edgenum = edge1num;\n\t\t\t\t\t\tVectorCopy(mid, lreach->start);\n\t\t\t\t\t\tVectorCopy(trace.endpos, lreach->end);\n\t\t\t\t\t\tlreach->traveltype = TRAVEL_WALKOFFLEDGE;\n\t\t\t\t\t\tlreach->traveltime = aassettings.rs_startwalkoffledge + fabs(mid[2] - trace.endpos[2]) * 50 / aassettings.phys_gravity;\n\t\t\t\t\t\tif (!AAS_AreaSwim(reachareanum) && !AAS_AreaJumpPad(reachareanum))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (AAS_FallDelta(mid[2] - trace.endpos[2]) > aassettings.phys_falldelta5)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tlreach->traveltime += aassettings.rs_falldamage5;\n\t\t\t\t\t\t\t} //end if\n\t\t\t\t\t\t\telse if (AAS_FallDelta(mid[2] - trace.endpos[2]) > aassettings.phys_falldelta10)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tlreach->traveltime += aassettings.rs_falldamage10;\n\t\t\t\t\t\t\t} //end if\n\t\t\t\t\t\t} //end if\n\t\t\t\t\t\tlreach->next = areareachability[areanum];\n\t\t\t\t\t\tareareachability[areanum] = lreach;\n\t\t\t\t\t\t//we've got another walk off ledge reachability\n\t\t\t\t\t\treach_walkoffledge++;\n\t\t\t\t\t} //end if\n\t\t\t\t} //end for\n\t\t\t} //end for\n\t\t} //end for\n\t} //end for\n} //end of the function AAS_Reachability_WalkOffLedge\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid AAS_StoreReachability(void)\n{\n\tint i;\n\taas_areasettings_t *areasettings;\n\taas_lreachability_t *lreach;\n\taas_reachability_t *reach;\n\n\tif (aasworld.reachability) FreeMemory(aasworld.reachability);\n\taasworld.reachability = (aas_reachability_t *) GetClearedMemory((numlreachabilities + 10) * sizeof(aas_reachability_t));\n\taasworld.reachabilitysize = 1;\n\tfor (i = 0; i < aasworld.numareas; i++)\n\t{\n\t\tareasettings = &aasworld.areasettings[i];\n\t\tareasettings->firstreachablearea = aasworld.reachabilitysize;\n\t\tareasettings->numreachableareas = 0;\n\t\tfor (lreach = areareachability[i]; lreach; lreach = lreach->next)\n\t\t{\n\t\t\treach = &aasworld.reachability[areasettings->firstreachablearea +\n\t\t\t\t\t\t\t\t\t\t\t\t\tareasettings->numreachableareas];\n\t\t\treach->areanum = lreach->areanum;\n\t\t\treach->facenum = lreach->facenum;\n\t\t\treach->edgenum = lreach->edgenum;\n\t\t\tVectorCopy(lreach->start, reach->start);\n\t\t\tVectorCopy(lreach->end, reach->end);\n\t\t\treach->traveltype = lreach->traveltype;\n\t\t\treach->traveltime = lreach->traveltime;\n\t\t\t//\n\t\t\tareasettings->numreachableareas++;\n\t\t} //end for\n\t\taasworld.reachabilitysize += areasettings->numreachableareas;\n\t} //end for\n} //end of the function AAS_StoreReachability\n//===========================================================================\n//\n// TRAVEL_WALK\t\t\t\t\t100%\tequal floor height + steps\n// TRAVEL_CROUCH\t\t\t\t100%\n// TRAVEL_BARRIERJUMP\t\t\t100%\n// TRAVEL_JUMP\t\t\t\t\t 80%\n// TRAVEL_LADDER\t\t\t\t100%\t+ fall down from ladder + jump up to ladder\n// TRAVEL_WALKOFFLEDGE\t\t\t 90%\twalk off very steep walls?\n// TRAVEL_SWIM\t\t\t\t\t100%\n// TRAVEL_WATERJUMP\t\t\t\t100%\n// TRAVEL_TELEPORT\t\t\t\t100%\n// TRAVEL_ELEVATOR\t\t\t\t100%\n// TRAVEL_GRAPPLEHOOK\t\t\t100%\n// TRAVEL_DOUBLEJUMP\t\t\t  0%\n// TRAVEL_RAMPJUMP\t\t\t\t  0%\n// TRAVEL_STRAFEJUMP\t\t\t  0%\n// TRAVEL_ROCKETJUMP\t\t\t100%\t(currently limited towards areas with items)\n// TRAVEL_BFGJUMP\t\t\t\t  0%\t(currently disabled)\n// TRAVEL_JUMPPAD\t\t\t\t100%\n// TRAVEL_FUNCBOB\t\t\t\t100%\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\ttrue if NOT finished\n// Changes Globals:\t\t-\n//===========================================================================\nint AAS_ContinueInitReachability(float time)\n{\n\tint i, j, todo, start_time;\n\tstatic float framereachability, reachability_delay;\n\tstatic int lastpercentage;\n\n\tif (!aasworld.loaded) return qfalse;\n\t//if reachability is calculated for all areas\n\tif (aasworld.numreachabilityareas >= aasworld.numareas + 2) return qfalse;\n\t//if starting with area 1 (area 0 is a dummy)\n\tif (aasworld.numreachabilityareas == 1)\n\t{\n\t\tbotimport.Print(PRT_MESSAGE, \"calculating reachability...\\n\");\n\t\tlastpercentage = 0;\n\t\tframereachability = 2000;\n\t\treachability_delay = 1000;\n\t} //end if\n\t//number of areas to calculate reachability for this cycle\n\ttodo = aasworld.numreachabilityareas + (int) framereachability;\n\tstart_time = Sys_MilliSeconds();\n\t//loop over the areas\n\tfor (i = aasworld.numreachabilityareas; i < aasworld.numareas && i < todo; i++)\n\t{\n\t\taasworld.numreachabilityareas++;\n\t\t//only create jumppad reachabilities from jumppad areas\n\t\tif (aasworld.areasettings[i].contents & AREACONTENTS_JUMPPAD)\n\t\t{\n\t\t\tcontinue;\n\t\t} //end if\n\t\t//loop over the areas\n\t\tfor (j = 1; j < aasworld.numareas; j++)\n\t\t{\n\t\t\tif (i == j) continue;\n\t\t\t//never create reachabilities from teleporter or jumppad areas to regular areas\n\t\t\tif (aasworld.areasettings[i].contents & (AREACONTENTS_TELEPORTER|AREACONTENTS_JUMPPAD))\n\t\t\t{\n\t\t\t\tif (!(aasworld.areasettings[j].contents & (AREACONTENTS_TELEPORTER|AREACONTENTS_JUMPPAD)))\n\t\t\t\t{\n\t\t\t\t\tcontinue;\n\t\t\t\t} //end if\n\t\t\t} //end if\n\t\t\t//if there already is a reachability link from area i to j\n\t\t\tif (AAS_ReachabilityExists(i, j)) continue;\n\t\t\t//check for a swim reachability\n\t\t\tif (AAS_Reachability_Swim(i, j)) continue;\n\t\t\t//check for a simple walk on equal floor height reachability\n\t\t\tif (AAS_Reachability_EqualFloorHeight(i, j)) continue;\n\t\t\t//check for step, barrier, waterjump and walk off ledge reachabilities\n\t\t\tif (AAS_Reachability_Step_Barrier_WaterJump_WalkOffLedge(i, j)) continue;\n\t\t\t//check for ladder reachabilities\n\t\t\tif (AAS_Reachability_Ladder(i, j)) continue;\n\t\t\t//check for a jump reachability\n\t\t\tif (AAS_Reachability_Jump(i, j)) continue;\n\t\t} //end for\n\t\t//never create these reachabilities from teleporter or jumppad areas\n\t\tif (aasworld.areasettings[i].contents & (AREACONTENTS_TELEPORTER|AREACONTENTS_JUMPPAD))\n\t\t{\n\t\t\tcontinue;\n\t\t} //end if\n\t\t//loop over the areas\n\t\tfor (j = 1; j < aasworld.numareas; j++)\n\t\t{\n\t\t\tif (i == j) continue;\n\t\t\t//\n\t\t\tif (AAS_ReachabilityExists(i, j)) continue;\n\t\t\t//check for a grapple hook reachability\n\t\t\tif (calcgrapplereach) AAS_Reachability_Grapple(i, j);\n\t\t\t//check for a weapon jump reachability\n\t\t\tAAS_Reachability_WeaponJump(i, j);\n\t\t} //end for\n\t\t//if the calculation took more time than the max reachability delay\n\t\tif (Sys_MilliSeconds() - start_time > (int) reachability_delay) break;\n\t\t//\n\t\tif (aasworld.numreachabilityareas * 1000 / aasworld.numareas > lastpercentage) break;\n\t} //end for\n\t//\n\tif (aasworld.numreachabilityareas == aasworld.numareas)\n\t{\n\t\tbotimport.Print(PRT_MESSAGE, \"\\r%6.1f%%\", (float) 100.0);\n\t\tbotimport.Print(PRT_MESSAGE, \"\\nplease wait while storing reachability...\\n\");\n\t\taasworld.numreachabilityareas++;\n\t} //end if\n\t//if this is the last step in the reachability calculations\n\telse if (aasworld.numreachabilityareas == aasworld.numareas + 1)\n\t{\n\t\t//create additional walk off ledge reachabilities for every area\n\t\tfor (i = 1; i < aasworld.numareas; i++)\n\t\t{\n\t\t\t//only create jumppad reachabilities from jumppad areas\n\t\t\tif (aasworld.areasettings[i].contents & AREACONTENTS_JUMPPAD)\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t} //end if\n\t\t\tAAS_Reachability_WalkOffLedge(i);\n\t\t} //end for\n\t\t//create jump pad reachabilities\n\t\tAAS_Reachability_JumpPad();\n\t\t//create teleporter reachabilities\n\t\tAAS_Reachability_Teleport();\n\t\t//create elevator (func_plat) reachabilities\n\t\tAAS_Reachability_Elevator();\n\t\t//create func_bobbing reachabilities\n\t\tAAS_Reachability_FuncBobbing();\n\t\t//\n#ifdef DEBUG\n\t\tbotimport.Print(PRT_MESSAGE, \"%6d reach swim\\n\", reach_swim);\n\t\tbotimport.Print(PRT_MESSAGE, \"%6d reach equal floor\\n\", reach_equalfloor);\n\t\tbotimport.Print(PRT_MESSAGE, \"%6d reach step\\n\", reach_step);\n\t\tbotimport.Print(PRT_MESSAGE, \"%6d reach barrier\\n\", reach_barrier);\n\t\tbotimport.Print(PRT_MESSAGE, \"%6d reach waterjump\\n\", reach_waterjump);\n\t\tbotimport.Print(PRT_MESSAGE, \"%6d reach walkoffledge\\n\", reach_walkoffledge);\n\t\tbotimport.Print(PRT_MESSAGE, \"%6d reach jump\\n\", reach_jump);\n\t\tbotimport.Print(PRT_MESSAGE, \"%6d reach ladder\\n\", reach_ladder);\n\t\tbotimport.Print(PRT_MESSAGE, \"%6d reach walk\\n\", reach_walk);\n\t\tbotimport.Print(PRT_MESSAGE, \"%6d reach teleport\\n\", reach_teleport);\n\t\tbotimport.Print(PRT_MESSAGE, \"%6d reach funcbob\\n\", reach_funcbob);\n\t\tbotimport.Print(PRT_MESSAGE, \"%6d reach elevator\\n\", reach_elevator);\n\t\tbotimport.Print(PRT_MESSAGE, \"%6d reach grapple\\n\", reach_grapple);\n\t\tbotimport.Print(PRT_MESSAGE, \"%6d reach rocketjump\\n\", reach_rocketjump);\n\t\tbotimport.Print(PRT_MESSAGE, \"%6d reach jumppad\\n\", reach_jumppad);\n#endif\n\t\t//*/\n\t\t//store all the reachabilities\n\t\tAAS_StoreReachability();\n\t\t//free the reachability link heap\n\t\tAAS_ShutDownReachabilityHeap();\n\t\t//\n\t\tFreeMemory(areareachability);\n\t\t//\n\t\taasworld.numreachabilityareas++;\n\t\t//\n\t\tbotimport.Print(PRT_MESSAGE, \"calculating clusters...\\n\");\n\t} //end if\n\telse\n\t{\n\t\tlastpercentage = aasworld.numreachabilityareas * 1000 / aasworld.numareas;\n\t\tbotimport.Print(PRT_MESSAGE, \"\\r%6.1f%%\", (float) lastpercentage / 10);\n\t} //end else\n\t//not yet finished\n\treturn qtrue;\n} //end of the function AAS_ContinueInitReachability\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid AAS_InitReachability(void)\n{\n\tif (!aasworld.loaded) return;\n\n\tif (aasworld.reachabilitysize)\n\t{\n#ifndef BSPC\n\t\tif (!((int)LibVarGetValue(\"forcereachability\")))\n\t\t{\n\t\t\taasworld.numreachabilityareas = aasworld.numareas + 2;\n\t\t\treturn;\n\t\t} //end if\n#else\n\t\taasworld.numreachabilityareas = aasworld.numareas + 2;\n\t\treturn;\n#endif //BSPC\n\t} //end if\n#ifndef BSPC\n\tcalcgrapplereach = LibVarGetValue(\"grapplereach\");\n#endif\n\taasworld.savefile = qtrue;\n\t//start with area 1 because area zero is a dummy\n\taasworld.numreachabilityareas = 1;\n\t////aasworld.numreachabilityareas = aasworld.numareas + 1;\t\t//only calculate entity reachabilities\n\t//setup the heap with reachability links\n\tAAS_SetupReachabilityHeap();\n\t//allocate area reachability link array\n\tareareachability = (aas_lreachability_t **) GetClearedMemory(\n\t\t\t\t\t\t\t\t\taasworld.numareas * sizeof(aas_lreachability_t *));\n\t//\n\tAAS_SetWeaponJumpAreaFlags();\n} //end of the function AAS_InitReachable\n"
  },
  {
    "path": "plugins/quake3/botlib/be_aas_reach.h",
    "content": "/*\n===========================================================================\nCopyright (C) 1999-2005 Id Software, Inc.\n\nThis file is part of Quake III Arena source code.\n\nQuake III Arena source code is free software; you can redistribute it\nand/or modify it under the terms of the GNU General Public License as\npublished by the Free Software Foundation; either version 2 of the License,\nor (at your option) any later version.\n\nQuake III Arena source code is distributed in the hope that it will be\nuseful, but WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Quake III Arena source code; if not, write to the Free Software\nFoundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n===========================================================================\n*/\n\n/*****************************************************************************\n * name:\t\tbe_aas_reach.h\n *\n * desc:\t\tAAS\n *\n * $Archive: /source/code/botlib/be_aas_reach.h $\n *\n *****************************************************************************/\n\n#ifdef AASINTERN\n//initialize calculating the reachabilities\nvoid AAS_InitReachability(void);\n//continue calculating the reachabilities\nint AAS_ContinueInitReachability(float time);\n//\nint AAS_BestReachableLinkArea(aas_link_t *areas);\n#endif //AASINTERN\n\n//returns true if the are has reachabilities to other areas\nint AAS_AreaReachability(int areanum);\n//returns the best reachable area and goal origin for a bounding box at the given origin\nint AAS_BestReachableArea(vec3_t origin, vec3_t mins, vec3_t maxs, vec3_t goalorigin);\n//returns the best jumppad area from which the bbox at origin is reachable\nint AAS_BestReachableFromJumpPadArea(vec3_t origin, vec3_t mins, vec3_t maxs);\n//returns the next reachability using the given model\nint AAS_NextModelReachability(int num, int modelnum);\n//returns the total area of the ground faces of the given area\nfloat AAS_AreaGroundFaceArea(int areanum);\n//returns true if the area is crouch only\nint AAS_AreaCrouch(int areanum);\n//returns true if a player can swim in this area\nint AAS_AreaSwim(int areanum);\n//returns true if the area is filled with a liquid\nint AAS_AreaLiquid(int areanum);\n//returns true if the area contains lava\nint AAS_AreaLava(int areanum);\n//returns true if the area contains slime\nint AAS_AreaSlime(int areanum);\n//returns true if the area has one or more ground faces\nint AAS_AreaGrounded(int areanum);\n//returns true if the area has one or more ladder faces\nint AAS_AreaLadder(int areanum);\n//returns true if the area is a jump pad\nint AAS_AreaJumpPad(int areanum);\n//returns true if the area is donotenter\nint AAS_AreaDoNotEnter(int areanum);\n"
  },
  {
    "path": "plugins/quake3/botlib/be_aas_route.c",
    "content": "/*\n===========================================================================\nCopyright (C) 1999-2005 Id Software, Inc.\n\nThis file is part of Quake III Arena source code.\n\nQuake III Arena source code is free software; you can redistribute it\nand/or modify it under the terms of the GNU General Public License as\npublished by the Free Software Foundation; either version 2 of the License,\nor (at your option) any later version.\n\nQuake III Arena source code is distributed in the hope that it will be\nuseful, but WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Quake III Arena source code; if not, write to the Free Software\nFoundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n===========================================================================\n*/\n\n/*****************************************************************************\n * name:\t\tbe_aas_route.c\n *\n * desc:\t\tAAS\n *\n * $Archive: /MissionPack/code/botlib/be_aas_route.c $\n *\n *****************************************************************************/\n\n#include \"q_shared.h\"\n#include \"l_utils.h\"\n#include \"l_memory.h\"\n#include \"l_log.h\"\n#include \"l_crc.h\"\n#include \"l_libvar.h\"\n#include \"l_script.h\"\n#include \"l_precomp.h\"\n#include \"l_struct.h\"\n#include \"aasfile.h\"\n#include \"botlib.h\"\n#include \"be_aas.h\"\n#include \"be_aas_funcs.h\"\n#include \"be_interface.h\"\n#include \"be_aas_def.h\"\n\n#define ROUTING_DEBUG\n\n//travel time in hundreths of a second = distance * 100 / speed\n#define DISTANCEFACTOR_CROUCH\t\t1.3f\t\t//crouch speed = 100\n#define DISTANCEFACTOR_SWIM\t\t\t1\t\t//should be 0.66, swim speed = 150\n#define DISTANCEFACTOR_WALK\t\t\t0.33f\t//walk speed = 300\n\n//cache refresh time\n#define CACHE_REFRESHTIME\t\t15.0f\t//15 seconds refresh time\n\n//maximum number of routing updates each frame\n#define MAX_FRAMEROUTINGUPDATES\t\t10\n\n\n/*\n\n  area routing cache:\n  stores the distances within one cluster to a specific goal area\n  this goal area is in this same cluster and could be a cluster portal\n  for every cluster there's a list with routing cache for every area\n  in that cluster (including the portals of that cluster)\n  area cache stores aasworld.clusters[?].numreachabilityareas travel times\n\n  portal routing cache:\n  stores the distances of all portals to a specific goal area\n  this goal area could be in any cluster and could also be a cluster portal\n  for every area (aasworld.numareas) the portal cache stores\n  aasworld.numportals travel times\n\n*/\n\n#ifdef ROUTING_DEBUG\nint numareacacheupdates;\nint numportalcacheupdates;\n#endif //ROUTING_DEBUG\n\nint routingcachesize;\nint max_routingcachesize;\n\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\n#ifdef ROUTING_DEBUG\nvoid AAS_RoutingInfo(void)\n{\n\tbotimport.Print(PRT_MESSAGE, \"%d area cache updates\\n\", numareacacheupdates);\n\tbotimport.Print(PRT_MESSAGE, \"%d portal cache updates\\n\", numportalcacheupdates);\n\tbotimport.Print(PRT_MESSAGE, \"%d bytes routing cache\\n\", routingcachesize);\n} //end of the function AAS_RoutingInfo\n#endif //ROUTING_DEBUG\n//===========================================================================\n// returns the number of the area in the cluster\n// assumes the given area is in the given cluster or a portal of the cluster\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nID_INLINE int AAS_ClusterAreaNum(int cluster, int areanum)\n{\n\tint side, areacluster;\n\n\tareacluster = aasworld.areasettings[areanum].cluster;\n\tif (areacluster > 0) return aasworld.areasettings[areanum].clusterareanum;\n\telse\n\t{\n/*#ifdef ROUTING_DEBUG\n\t\tif (aasworld.portals[-areacluster].frontcluster != cluster &&\n\t\t\t\taasworld.portals[-areacluster].backcluster != cluster)\n\t\t{\n\t\t\tbotimport.Print(PRT_ERROR, \"portal %d: does not belong to cluster %d\\n\"\n\t\t\t\t\t\t\t\t\t\t\t, -areacluster, cluster);\n\t\t} //end if\n#endif //ROUTING_DEBUG*/\n\t\tside = aasworld.portals[-areacluster].frontcluster != cluster;\n\t\treturn aasworld.portals[-areacluster].clusterareanum[side];\n\t} //end else\n} //end of the function AAS_ClusterAreaNum\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid AAS_InitTravelFlagFromType(void)\n{\n\tint i;\n\n\tfor (i = 0; i < MAX_TRAVELTYPES; i++)\n\t{\n\t\taasworld.travelflagfortype[i] = TFL_INVALID;\n\t} //end for\n\taasworld.travelflagfortype[TRAVEL_INVALID] = TFL_INVALID;\n\taasworld.travelflagfortype[TRAVEL_WALK] = TFL_WALK;\n\taasworld.travelflagfortype[TRAVEL_CROUCH] = TFL_CROUCH;\n\taasworld.travelflagfortype[TRAVEL_BARRIERJUMP] = TFL_BARRIERJUMP;\n\taasworld.travelflagfortype[TRAVEL_JUMP] = TFL_JUMP;\n\taasworld.travelflagfortype[TRAVEL_LADDER] = TFL_LADDER;\n\taasworld.travelflagfortype[TRAVEL_WALKOFFLEDGE] = TFL_WALKOFFLEDGE;\n\taasworld.travelflagfortype[TRAVEL_SWIM] = TFL_SWIM;\n\taasworld.travelflagfortype[TRAVEL_WATERJUMP] = TFL_WATERJUMP;\n\taasworld.travelflagfortype[TRAVEL_TELEPORT] = TFL_TELEPORT;\n\taasworld.travelflagfortype[TRAVEL_ELEVATOR] = TFL_ELEVATOR;\n\taasworld.travelflagfortype[TRAVEL_ROCKETJUMP] = TFL_ROCKETJUMP;\n\taasworld.travelflagfortype[TRAVEL_BFGJUMP] = TFL_BFGJUMP;\n\taasworld.travelflagfortype[TRAVEL_GRAPPLEHOOK] = TFL_GRAPPLEHOOK;\n\taasworld.travelflagfortype[TRAVEL_DOUBLEJUMP] = TFL_DOUBLEJUMP;\n\taasworld.travelflagfortype[TRAVEL_RAMPJUMP] = TFL_RAMPJUMP;\n\taasworld.travelflagfortype[TRAVEL_STRAFEJUMP] = TFL_STRAFEJUMP;\n\taasworld.travelflagfortype[TRAVEL_JUMPPAD] = TFL_JUMPPAD;\n\taasworld.travelflagfortype[TRAVEL_FUNCBOB] = TFL_FUNCBOB;\n} //end of the function AAS_InitTravelFlagFromType\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nID_INLINE int AAS_TravelFlagForType_inline(int traveltype)\n{\n\tint tfl;\n\n\ttfl = 0;\n\tif (tfl & TRAVELFLAG_NOTTEAM1)\n\t\ttfl |= TFL_NOTTEAM1;\n\tif (tfl & TRAVELFLAG_NOTTEAM2)\n\t\ttfl |= TFL_NOTTEAM2;\n\ttraveltype &= TRAVELTYPE_MASK;\n\tif (traveltype < 0 || traveltype >= MAX_TRAVELTYPES)\n\t\treturn TFL_INVALID;\n\ttfl |= aasworld.travelflagfortype[traveltype];\n\treturn tfl;\n} //end of the function AAS_TravelFlagForType_inline\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint AAS_TravelFlagForType(int traveltype)\n{\n\treturn AAS_TravelFlagForType_inline(traveltype);\n} //end of the function AAS_TravelFlagForType_inline\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid AAS_UnlinkCache(aas_routingcache_t *cache)\n{\n\tif (cache->time_next) cache->time_next->time_prev = cache->time_prev;\n\telse aasworld.newestcache = cache->time_prev;\n\tif (cache->time_prev) cache->time_prev->time_next = cache->time_next;\n\telse aasworld.oldestcache = cache->time_next;\n\tcache->time_next = NULL;\n\tcache->time_prev = NULL;\n} //end of the function AAS_UnlinkCache\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid AAS_LinkCache(aas_routingcache_t *cache)\n{\n\tif (aasworld.newestcache)\n\t{\n\t\taasworld.newestcache->time_next = cache;\n\t\tcache->time_prev = aasworld.newestcache;\n\t} //end if\n\telse\n\t{\n\t\taasworld.oldestcache = cache;\n\t\tcache->time_prev = NULL;\n\t} //end else\n\tcache->time_next = NULL;\n\taasworld.newestcache = cache;\n} //end of the function AAS_LinkCache\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid AAS_FreeRoutingCache(aas_routingcache_t *cache)\n{\n\tAAS_UnlinkCache(cache);\n\troutingcachesize -= cache->size;\n\tFreeMemory(cache);\n} //end of the function AAS_FreeRoutingCache\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid AAS_RemoveRoutingCacheInCluster( int clusternum )\n{\n\tint i;\n\taas_routingcache_t *cache, *nextcache;\n\taas_cluster_t *cluster;\n\n\tif (!aasworld.clusterareacache)\n\t\treturn;\n\tcluster = &aasworld.clusters[clusternum];\n\tfor (i = 0; i < cluster->numareas; i++)\n\t{\n\t\tfor (cache = aasworld.clusterareacache[clusternum][i]; cache; cache = nextcache)\n\t\t{\n\t\t\tnextcache = cache->next;\n\t\t\tAAS_FreeRoutingCache(cache);\n\t\t} //end for\n\t\taasworld.clusterareacache[clusternum][i] = NULL;\n\t} //end for\n} //end of the function AAS_RemoveRoutingCacheInCluster\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid AAS_RemoveRoutingCacheUsingArea( int areanum )\n{\n\tint i, clusternum;\n\taas_routingcache_t *cache, *nextcache;\n\n\tclusternum = aasworld.areasettings[areanum].cluster;\n\tif (clusternum > 0)\n\t{\n\t\t//remove all the cache in the cluster the area is in\n\t\tAAS_RemoveRoutingCacheInCluster( clusternum );\n\t} //end if\n\telse\n\t{\n\t\t// if this is a portal remove all cache in both the front and back cluster\n\t\tAAS_RemoveRoutingCacheInCluster( aasworld.portals[-clusternum].frontcluster );\n\t\tAAS_RemoveRoutingCacheInCluster( aasworld.portals[-clusternum].backcluster );\n\t} //end else\n\t// remove all portal cache\n\tfor (i = 0; i < aasworld.numareas; i++)\n\t{\n\t\t//refresh portal cache\n\t\tfor (cache = aasworld.portalcache[i]; cache; cache = nextcache)\n\t\t{\n\t\t\tnextcache = cache->next;\n\t\t\tAAS_FreeRoutingCache(cache);\n\t\t} //end for\n\t\taasworld.portalcache[i] = NULL;\n\t} //end for\n} //end of the function AAS_RemoveRoutingCacheUsingArea\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint AAS_EnableRoutingArea(int areanum, int enable)\n{\n\tint flags;\n\n\tif (areanum <= 0 || areanum >= aasworld.numareas)\n\t{\n\t\tif (botDeveloper)\n\t\t{\n\t\t\tbotimport.Print(PRT_ERROR, \"AAS_EnableRoutingArea: areanum %d out of range\\n\", areanum);\n\t\t} //end if\n\t\treturn 0;\n\t} //end if\n\tflags = aasworld.areasettings[areanum].areaflags & AREA_DISABLED;\n\tif (enable < 0)\n\t\treturn !flags;\n\n\tif (enable)\n\t\taasworld.areasettings[areanum].areaflags &= ~AREA_DISABLED;\n\telse\n\t\taasworld.areasettings[areanum].areaflags |= AREA_DISABLED;\n\t// if the status of the area changed\n\tif ( (flags & AREA_DISABLED) != (aasworld.areasettings[areanum].areaflags & AREA_DISABLED) )\n\t{\n\t\t//remove all routing cache involving this area\n\t\tAAS_RemoveRoutingCacheUsingArea( areanum );\n\t} //end if\n\treturn !flags;\n} //end of the function AAS_EnableRoutingArea\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nID_INLINE float AAS_RoutingTime(void)\n{\n\treturn AAS_Time();\n} //end of the function AAS_RoutingTime\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint AAS_GetAreaContentsTravelFlags(int areanum)\n{\n\tint contents, tfl;\n\n\tcontents = aasworld.areasettings[areanum].contents;\n\ttfl = 0;\n\tif (contents & AREACONTENTS_WATER)\n\t\ttfl |= TFL_WATER;\n\telse if (contents & AREACONTENTS_SLIME)\n\t\ttfl |= TFL_SLIME;\n\telse if (contents & AREACONTENTS_LAVA)\n\t\ttfl |= TFL_LAVA;\n\telse\n\t\ttfl |= TFL_AIR;\n\tif (contents & AREACONTENTS_DONOTENTER)\n\t\ttfl |= TFL_DONOTENTER;\n\tif (contents & AREACONTENTS_NOTTEAM1)\n\t\ttfl |= TFL_NOTTEAM1;\n\tif (contents & AREACONTENTS_NOTTEAM2)\n\t\ttfl |= TFL_NOTTEAM2;\n\tif (aasworld.areasettings[areanum].areaflags & AREA_BRIDGE)\n\t\ttfl |= TFL_BRIDGE;\n\treturn tfl;\n} //end of the function AAS_GetAreaContentsTravelFlags\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nID_INLINE int AAS_AreaContentsTravelFlags_inline(int areanum)\n{\n\treturn aasworld.areacontentstravelflags[areanum];\n} //end of the function AAS_AreaContentsTravelFlags\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint AAS_AreaContentsTravelFlags(int areanum)\n{\n\treturn aasworld.areacontentstravelflags[areanum];\n} //end of the function AAS_AreaContentsTravelFlags\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid AAS_InitAreaContentsTravelFlags(void)\n{\n\tint i;\n\n\tif (aasworld.areacontentstravelflags) FreeMemory(aasworld.areacontentstravelflags);\n\taasworld.areacontentstravelflags = (int *) GetClearedMemory(aasworld.numareas * sizeof(int));\n\t//\n\tfor (i = 0; i < aasworld.numareas; i++) {\n\t\taasworld.areacontentstravelflags[i] = AAS_GetAreaContentsTravelFlags(i);\n\t}\n} //end of the function AAS_InitAreaContentsTravelFlags\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid AAS_CreateReversedReachability(void)\n{\n\tint i, n;\n\taas_reversedlink_t *revlink;\n\taas_reachability_t *reach;\n\taas_areasettings_t *settings;\n\tchar *ptr;\n#ifdef DEBUG\n\tint starttime;\n\n\tstarttime = Sys_MilliSeconds();\n#endif\n\t//free reversed links that have already been created\n\tif (aasworld.reversedreachability) FreeMemory(aasworld.reversedreachability);\n\t//allocate memory for the reversed reachability links\n\tptr = (char *) GetClearedMemory(aasworld.numareas * sizeof(aas_reversedreachability_t) +\n\t\t\t\t\t\t\taasworld.reachabilitysize * sizeof(aas_reversedlink_t));\n\t//\n\taasworld.reversedreachability = (aas_reversedreachability_t *) ptr;\n\t//pointer to the memory for the reversed links\n\tptr += aasworld.numareas * sizeof(aas_reversedreachability_t);\n\t//check all reachabilities of all areas\n\tfor (i = 1; i < aasworld.numareas; i++)\n\t{\n\t\t//settings of the area\n\t\tsettings = &aasworld.areasettings[i];\n\t\t//\n\t\tif (settings->numreachableareas >= 128)\n\t\t\tbotimport.Print(PRT_WARNING, \"area %d has more than 128 reachabilities\\n\", i);\n\t\t//create reversed links for the reachabilities\n\t\tfor (n = 0; n < settings->numreachableareas && n < 128; n++)\n\t\t{\n\t\t\t//reachability link\n\t\t\treach = &aasworld.reachability[settings->firstreachablearea + n];\n\t\t\t//\n\t\t\trevlink = (aas_reversedlink_t *) ptr;\n\t\t\tptr += sizeof(aas_reversedlink_t);\n\t\t\t//\n\t\t\trevlink->areanum = i;\n\t\t\trevlink->linknum = settings->firstreachablearea + n;\n\t\t\trevlink->next = aasworld.reversedreachability[reach->areanum].first;\n\t\t\taasworld.reversedreachability[reach->areanum].first = revlink;\n\t\t\taasworld.reversedreachability[reach->areanum].numlinks++;\n\t\t} //end for\n\t} //end for\n#ifdef DEBUG\n\tbotimport.Print(PRT_MESSAGE, \"reversed reachability %d msec\\n\", Sys_MilliSeconds() - starttime);\n#endif\n} //end of the function AAS_CreateReversedReachability\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nunsigned short int AAS_AreaTravelTime(int areanum, vec3_t start, vec3_t end)\n{\n\tint intdist;\n\tfloat dist;\n\tvec3_t dir;\n\n\tVectorSubtract(start, end, dir);\n\tdist = VectorLength(dir);\n\t//if crouch only area\n\tif (AAS_AreaCrouch(areanum)) dist *= DISTANCEFACTOR_CROUCH;\n\t//if swim area\n\telse if (AAS_AreaSwim(areanum)) dist *= DISTANCEFACTOR_SWIM;\n\t//normal walk area\n\telse dist *= DISTANCEFACTOR_WALK;\n\t//\n\tintdist = (int) dist;\n\t//make sure the distance isn't zero\n\tif (intdist <= 0) intdist = 1;\n\treturn intdist;\n} //end of the function AAS_AreaTravelTime\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid AAS_CalculateAreaTravelTimes(void)\n{\n\tint i, l, n, size;\n\tchar *ptr;\n\tvec3_t end;\n\taas_reversedreachability_t *revreach;\n\taas_reversedlink_t *revlink;\n\taas_reachability_t *reach;\n\taas_areasettings_t *settings;\n#ifdef DEBUG\n\tint starttime = Sys_MilliSeconds();\n#endif\n\n\t//if there are still area travel times, free the memory\n\tif (aasworld.areatraveltimes) FreeMemory(aasworld.areatraveltimes);\n\t//get the total size of all the area travel times\n\tsize = aasworld.numareas * sizeof(unsigned short **);\n\tfor (i = 0; i < aasworld.numareas; i++)\n\t{\n\t\trevreach = &aasworld.reversedreachability[i];\n\t\t//settings of the area\n\t\tsettings = &aasworld.areasettings[i];\n\t\t//\n\t\tsize += settings->numreachableareas * sizeof(unsigned short *);\n\t\t//\n\t\tsize += settings->numreachableareas *\n\t\t\tPAD(revreach->numlinks, sizeof(long)) * sizeof(unsigned short);\n\t} //end for\n\t//allocate memory for the area travel times\n\tptr = (char *) GetClearedMemory(size);\n\taasworld.areatraveltimes = (unsigned short ***) ptr;\n\tptr += aasworld.numareas * sizeof(unsigned short **);\n\t//calcluate the travel times for all the areas\n\tfor (i = 0; i < aasworld.numareas; i++)\n\t{\n\t\t//reversed reachabilities of this area\n\t\trevreach = &aasworld.reversedreachability[i];\n\t\t//settings of the area\n\t\tsettings = &aasworld.areasettings[i];\n\t\t//\n\t\taasworld.areatraveltimes[i] = (unsigned short **) ptr;\n\t\tptr += settings->numreachableareas * sizeof(unsigned short *);\n\t\t//\n\t\tfor (l = 0; l < settings->numreachableareas; l++)\n\t\t{\n\t\t\taasworld.areatraveltimes[i][l] = (unsigned short *) ptr;\n\t\t\tptr += PAD(revreach->numlinks, sizeof(long)) * sizeof(unsigned short);\n\t\t\t//reachability link\n\t\t\treach = &aasworld.reachability[settings->firstreachablearea + l];\n\t\t\t//\n\t\t\tfor (n = 0, revlink = revreach->first; revlink; revlink = revlink->next, n++)\n\t\t\t{\n\t\t\t\tVectorCopy(aasworld.reachability[revlink->linknum].end, end);\n\t\t\t\t//\n\t\t\t\taasworld.areatraveltimes[i][l][n] = AAS_AreaTravelTime(i, end, reach->start);\n\t\t\t} //end for\n\t\t} //end for\n\t} //end for\n#ifdef DEBUG\n\tbotimport.Print(PRT_MESSAGE, \"area travel times %d msec\\n\", Sys_MilliSeconds() - starttime);\n#endif\n} //end of the function AAS_CalculateAreaTravelTimes\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint AAS_PortalMaxTravelTime(int portalnum)\n{\n\tint l, n, t, maxt;\n\taas_portal_t *portal;\n\taas_reversedreachability_t *revreach;\n\taas_reversedlink_t *revlink;\n\taas_areasettings_t *settings;\n\n\tportal = &aasworld.portals[portalnum];\n\t//reversed reachabilities of this portal area\n\trevreach = &aasworld.reversedreachability[portal->areanum];\n\t//settings of the portal area\n\tsettings = &aasworld.areasettings[portal->areanum];\n\t//\n\tmaxt = 0;\n\tfor (l = 0; l < settings->numreachableareas; l++)\n\t{\n\t\tfor (n = 0, revlink = revreach->first; revlink; revlink = revlink->next, n++)\n\t\t{\n\t\t\tt = aasworld.areatraveltimes[portal->areanum][l][n];\n\t\t\tif (t > maxt)\n\t\t\t{\n\t\t\t\tmaxt = t;\n\t\t\t} //end if\n\t\t} //end for\n\t} //end for\n\treturn maxt;\n} //end of the function AAS_PortalMaxTravelTime\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid AAS_InitPortalMaxTravelTimes(void)\n{\n\tint i;\n\n\tif (aasworld.portalmaxtraveltimes) FreeMemory(aasworld.portalmaxtraveltimes);\n\n\taasworld.portalmaxtraveltimes = (int *) GetClearedMemory(aasworld.numportals * sizeof(int));\n\n\tfor (i = 0; i < aasworld.numportals; i++)\n\t{\n\t\taasworld.portalmaxtraveltimes[i] = AAS_PortalMaxTravelTime(i);\n\t\t//botimport.Print(PRT_MESSAGE, \"portal %d max tt = %d\\n\", i, aasworld.portalmaxtraveltimes[i]);\n\t} //end for\n} //end of the function AAS_InitPortalMaxTravelTimes\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\n/*\nint AAS_FreeOldestCache(void)\n{\n\tint i, j, bestcluster, bestarea, freed;\n\tfloat besttime;\n\taas_routingcache_t *cache, *bestcache;\n\n\tfreed = qfalse;\n\tbesttime = 999999999;\n\tbestcache = NULL;\n\tbestcluster = 0;\n\tbestarea = 0;\n\t//refresh cluster cache\n\tfor (i = 0; i < aasworld.numclusters; i++)\n\t{\n\t\tfor (j = 0; j < aasworld.clusters[i].numareas; j++)\n\t\t{\n\t\t\tfor (cache = aasworld.clusterareacache[i][j]; cache; cache = cache->next)\n\t\t\t{\n\t\t\t\t//never remove cache leading towards a portal\n\t\t\t\tif (aasworld.areasettings[cache->areanum].cluster < 0) continue;\n\t\t\t\t//if this cache is older than the cache we found so far\n\t\t\t\tif (cache->time < besttime)\n\t\t\t\t{\n\t\t\t\t\tbestcache = cache;\n\t\t\t\t\tbestcluster = i;\n\t\t\t\t\tbestarea = j;\n\t\t\t\t\tbesttime = cache->time;\n\t\t\t\t} //end if\n\t\t\t} //end for\n\t\t} //end for\n\t} //end for\n\tif (bestcache)\n\t{\n\t\tcache = bestcache;\n\t\tif (cache->prev) cache->prev->next = cache->next;\n\t\telse aasworld.clusterareacache[bestcluster][bestarea] = cache->next;\n\t\tif (cache->next) cache->next->prev = cache->prev;\n\t\tAAS_FreeRoutingCache(cache);\n\t\tfreed = qtrue;\n\t} //end if\n\tbesttime = 999999999;\n\tbestcache = NULL;\n\tbestarea = 0;\n\tfor (i = 0; i < aasworld.numareas; i++)\n\t{\n\t\t//refresh portal cache\n\t\tfor (cache = aasworld.portalcache[i]; cache; cache = cache->next)\n\t\t{\n\t\t\tif (cache->time < besttime)\n\t\t\t{\n\t\t\t\tbestcache = cache;\n\t\t\t\tbestarea = i;\n\t\t\t\tbesttime = cache->time;\n\t\t\t} //end if\n\t\t} //end for\n\t} //end for\n\tif (bestcache)\n\t{\n\t\tcache = bestcache;\n\t\tif (cache->prev) cache->prev->next = cache->next;\n\t\telse aasworld.portalcache[bestarea] = cache->next;\n\t\tif (cache->next) cache->next->prev = cache->prev;\n\t\tAAS_FreeRoutingCache(cache);\n\t\tfreed = qtrue;\n\t} //end if\n\treturn freed;\n} //end of the function AAS_FreeOldestCache\n*/\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint AAS_FreeOldestCache(void)\n{\n\tint clusterareanum;\n\taas_routingcache_t *cache;\n\n\tfor (cache = aasworld.oldestcache; cache; cache = cache->time_next) {\n\t\t// never free area cache leading towards a portal\n\t\tif (cache->type == CACHETYPE_AREA && aasworld.areasettings[cache->areanum].cluster < 0) {\n\t\t\tcontinue;\n\t\t}\n\t\tbreak;\n\t}\n\tif (cache) {\n\t\t// unlink the cache\n\t\tif (cache->type == CACHETYPE_AREA) {\n\t\t\t//number of the area in the cluster\n\t\t\tclusterareanum = AAS_ClusterAreaNum(cache->cluster, cache->areanum);\n\t\t\t// unlink from cluster area cache\n\t\t\tif (cache->prev) cache->prev->next = cache->next;\n\t\t\telse aasworld.clusterareacache[cache->cluster][clusterareanum] = cache->next;\n\t\t\tif (cache->next) cache->next->prev = cache->prev;\n\t\t}\n\t\telse {\n\t\t\t// unlink from portal cache\n\t\t\tif (cache->prev) cache->prev->next = cache->next;\n\t\t\telse aasworld.portalcache[cache->areanum] = cache->next;\n\t\t\tif (cache->next) cache->next->prev = cache->prev;\n\t\t}\n\t\tAAS_FreeRoutingCache(cache);\n\t\treturn qtrue;\n\t}\n\treturn qfalse;\n} //end of the function AAS_FreeOldestCache\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\naas_routingcache_t *AAS_AllocRoutingCache(int numtraveltimes)\n{\n\taas_routingcache_t *cache;\n\tint size;\n\n\t//\n\tsize = sizeof(aas_routingcache_t)\n\t\t\t\t\t\t+ numtraveltimes * sizeof(unsigned short int)\n\t\t\t\t\t\t+ numtraveltimes * sizeof(unsigned char);\n\t//\n\troutingcachesize += size;\n\t//\n\tcache = (aas_routingcache_t *) GetClearedMemory(size);\n\tcache->reachabilities = (unsigned char *) cache + sizeof(aas_routingcache_t)\n\t\t\t\t\t\t\t\t+ numtraveltimes * sizeof(unsigned short int);\n\tcache->size = size;\n\treturn cache;\n} //end of the function AAS_AllocRoutingCache\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid AAS_FreeAllClusterAreaCache(void)\n{\n\tint i, j;\n\taas_routingcache_t *cache, *nextcache;\n\taas_cluster_t *cluster;\n\n\t//free all cluster cache if existing\n\tif (!aasworld.clusterareacache) return;\n\t//free caches\n\tfor (i = 0; i < aasworld.numclusters; i++)\n\t{\n\t\tcluster = &aasworld.clusters[i];\n\t\tfor (j = 0; j < cluster->numareas; j++)\n\t\t{\n\t\t\tfor (cache = aasworld.clusterareacache[i][j]; cache; cache = nextcache)\n\t\t\t{\n\t\t\t\tnextcache = cache->next;\n\t\t\t\tAAS_FreeRoutingCache(cache);\n\t\t\t} //end for\n\t\t\taasworld.clusterareacache[i][j] = NULL;\n\t\t} //end for\n\t} //end for\n\t//free the cluster cache array\n\tFreeMemory(aasworld.clusterareacache);\n\taasworld.clusterareacache = NULL;\n} //end of the function AAS_FreeAllClusterAreaCache\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid AAS_InitClusterAreaCache(void)\n{\n\tint i, size;\n\tchar *ptr;\n\n\t//\n\tfor (size = 0, i = 0; i < aasworld.numclusters; i++)\n\t{\n\t\tsize += aasworld.clusters[i].numareas;\n\t} //end for\n\t//two dimensional array with pointers for every cluster to routing cache\n\t//for every area in that cluster\n\tptr = (char *) GetClearedMemory(\n\t\t\t\taasworld.numclusters * sizeof(aas_routingcache_t **) +\n\t\t\t\tsize * sizeof(aas_routingcache_t *));\n\taasworld.clusterareacache = (aas_routingcache_t ***) ptr;\n\tptr += aasworld.numclusters * sizeof(aas_routingcache_t **);\n\tfor (i = 0; i < aasworld.numclusters; i++)\n\t{\n\t\taasworld.clusterareacache[i] = (aas_routingcache_t **) ptr;\n\t\tptr += aasworld.clusters[i].numareas * sizeof(aas_routingcache_t *);\n\t} //end for\n} //end of the function AAS_InitClusterAreaCache\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid AAS_FreeAllPortalCache(void)\n{\n\tint i;\n\taas_routingcache_t *cache, *nextcache;\n\n\t//free all portal cache if existing\n\tif (!aasworld.portalcache) return;\n\t//free portal caches\n\tfor (i = 0; i < aasworld.numareas; i++)\n\t{\n\t\tfor (cache = aasworld.portalcache[i]; cache; cache = nextcache)\n\t\t{\n\t\t\tnextcache = cache->next;\n\t\t\tAAS_FreeRoutingCache(cache);\n\t\t} //end for\n\t\taasworld.portalcache[i] = NULL;\n\t} //end for\n\tFreeMemory(aasworld.portalcache);\n\taasworld.portalcache = NULL;\n} //end of the function AAS_FreeAllPortalCache\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid AAS_InitPortalCache(void)\n{\n\t//\n\taasworld.portalcache = (aas_routingcache_t **) GetClearedMemory(\n\t\t\t\t\t\t\t\taasworld.numareas * sizeof(aas_routingcache_t *));\n} //end of the function AAS_InitPortalCache\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid AAS_InitRoutingUpdate(void)\n{\n\tint i, maxreachabilityareas;\n\n\t//free routing update fields if already existing\n\tif (aasworld.areaupdate) FreeMemory(aasworld.areaupdate);\n\t//\n\tmaxreachabilityareas = 0;\n\tfor (i = 0; i < aasworld.numclusters; i++)\n\t{\n\t\tif (aasworld.clusters[i].numreachabilityareas > maxreachabilityareas)\n\t\t{\n\t\t\tmaxreachabilityareas = aasworld.clusters[i].numreachabilityareas;\n\t\t} //end if\n\t} //end for\n\t//allocate memory for the routing update fields\n\taasworld.areaupdate = (aas_routingupdate_t *) GetClearedMemory(\n\t\t\t\t\t\t\t\t\tmaxreachabilityareas * sizeof(aas_routingupdate_t));\n\t//\n\tif (aasworld.portalupdate) FreeMemory(aasworld.portalupdate);\n\t//allocate memory for the portal update fields\n\taasworld.portalupdate = (aas_routingupdate_t *) GetClearedMemory(\n\t\t\t\t\t\t\t\t\t(aasworld.numportals+1) * sizeof(aas_routingupdate_t));\n} //end of the function AAS_InitRoutingUpdate\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid AAS_CreateAllRoutingCache(void)\n{\n\tint i, j;\n\n\taasworld.initialized = qtrue;\n\tbotimport.Print(PRT_MESSAGE, \"AAS_CreateAllRoutingCache\\n\");\n\tfor (i = 1; i < aasworld.numareas; i++)\n\t{\n\t\tif (!AAS_AreaReachability(i)) continue;\n\t\tfor (j = 1; j < aasworld.numareas; j++)\n\t\t{\n\t\t\tif (i == j) continue;\n\t\t\tif (!AAS_AreaReachability(j)) continue;\n\t\t\t/*t =*/ AAS_AreaTravelTimeToGoalArea(i, aasworld.areas[i].center, j, TFL_DEFAULT);\n\t\t\t//Log_Write(\"traveltime from %d to %d is %d\", i, j, t);\n\t\t} //end for\n\t} //end for\n\taasworld.initialized = qfalse;\n} //end of the function AAS_CreateAllRoutingCache\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\n\n//the route cache header\n//this header is followed by numportalcache + numareacache aas_routingcache_t\n//structures that store routing cache\ntypedef struct routecacheheader_s\n{\n\tint ident;\n\tint version;\n\tint numareas;\n\tint numclusters;\n\tint areacrc;\n\tint clustercrc;\n\tint numportalcache;\n\tint numareacache;\n} routecacheheader_t;\n\n#define RCID\t\t\t\t\t\t(('C'<<24)+('R'<<16)+('E'<<8)+'M')\n#define RCVERSION\t\t\t\t\t2\n\n//void AAS_DecompressVis(byte *in, int numareas, byte *decompressed);\n//int AAS_CompressVis(byte *vis, int numareas, byte *dest);\n\nvoid AAS_WriteRouteCache(void)\n{\n\tint i, j, numportalcache, numareacache, totalsize;\n\taas_routingcache_t *cache;\n\taas_cluster_t *cluster;\n\tfileHandle_t fp;\n\tchar filename[MAX_QPATH];\n\troutecacheheader_t routecacheheader;\n\n\tnumportalcache = 0;\n\tfor (i = 0; i < aasworld.numareas; i++)\n\t{\n\t\tfor (cache = aasworld.portalcache[i]; cache; cache = cache->next)\n\t\t{\n\t\t\tnumportalcache++;\n\t\t} //end for\n\t} //end for\n\tnumareacache = 0;\n\tfor (i = 0; i < aasworld.numclusters; i++)\n\t{\n\t\tcluster = &aasworld.clusters[i];\n\t\tfor (j = 0; j < cluster->numareas; j++)\n\t\t{\n\t\t\tfor (cache = aasworld.clusterareacache[i][j]; cache; cache = cache->next)\n\t\t\t{\n\t\t\t\tnumareacache++;\n\t\t\t} //end for\n\t\t} //end for\n\t} //end for\n\t// open the file for writing\n\tCom_sprintf(filename, MAX_QPATH, \"maps/%s.rcd\", aasworld.mapname);\n\tbotimport.FS_FOpenFile( filename, &fp, FS_WRITE );\n\tif (!fp)\n\t{\n\t\tAAS_Error(\"Unable to open file: %s\\n\", filename);\n\t\treturn;\n\t} //end if\n\t//create the header\n\troutecacheheader.ident = RCID;\n\troutecacheheader.version = RCVERSION;\n\troutecacheheader.numareas = aasworld.numareas;\n\troutecacheheader.numclusters = aasworld.numclusters;\n\troutecacheheader.areacrc = CRC_ProcessString( (unsigned char *)aasworld.areas, sizeof(aas_area_t) * aasworld.numareas );\n\troutecacheheader.clustercrc = CRC_ProcessString( (unsigned char *)aasworld.clusters, sizeof(aas_cluster_t) * aasworld.numclusters );\n\troutecacheheader.numportalcache = numportalcache;\n\troutecacheheader.numareacache = numareacache;\n\t//write the header\n\tbotimport.FS_Write(&routecacheheader, sizeof(routecacheheader_t), fp);\n\t//\n\ttotalsize = 0;\n\t//write all the cache\n\tfor (i = 0; i < aasworld.numareas; i++)\n\t{\n\t\tfor (cache = aasworld.portalcache[i]; cache; cache = cache->next)\n\t\t{\n\t\t\tbotimport.FS_Write(cache, cache->size, fp);\n\t\t\ttotalsize += cache->size;\n\t\t} //end for\n\t} //end for\n\tfor (i = 0; i < aasworld.numclusters; i++)\n\t{\n\t\tcluster = &aasworld.clusters[i];\n\t\tfor (j = 0; j < cluster->numareas; j++)\n\t\t{\n\t\t\tfor (cache = aasworld.clusterareacache[i][j]; cache; cache = cache->next)\n\t\t\t{\n\t\t\t\tbotimport.FS_Write(cache, cache->size, fp);\n\t\t\t\ttotalsize += cache->size;\n\t\t\t} //end for\n\t\t} //end for\n\t} //end for\n\t// write the visareas\n\t/*\n\tfor (i = 0; i < aasworld.numareas; i++)\n\t{\n\t\tif (!aasworld.areavisibility[i]) {\n\t\t\tsize = 0;\n\t\t\tbotimport.FS_Write(&size, sizeof(int), fp);\n\t\t\tcontinue;\n\t\t}\n\t\tAAS_DecompressVis( aasworld.areavisibility[i], aasworld.numareas, aasworld.decompressedvis );\n\t\tsize = AAS_CompressVis( aasworld.decompressedvis, aasworld.numareas, aasworld.decompressedvis );\n\t\tbotimport.FS_Write(&size, sizeof(int), fp);\n\t\tbotimport.FS_Write(aasworld.decompressedvis, size, fp);\n\t}\n\t*/\n\t//\n\tbotimport.FS_FCloseFile(fp);\n\tbotimport.Print(PRT_MESSAGE, \"\\nroute cache written to %s\\n\", filename);\n\tbotimport.Print(PRT_MESSAGE, \"written %d bytes of routing cache\\n\", totalsize);\n} //end of the function AAS_WriteRouteCache\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\naas_routingcache_t *AAS_ReadCache(fileHandle_t fp)\n{\n\tint size;\n\taas_routingcache_t *cache;\n\n\tbotimport.FS_Read(&size, sizeof(size), fp);\n\tcache = (aas_routingcache_t *) GetMemory(size);\n\tcache->size = size;\n\tbotimport.FS_Read((unsigned char *)cache + sizeof(size), size - sizeof(size), fp);\n\tcache->reachabilities = (unsigned char *) cache + sizeof(aas_routingcache_t) - sizeof(unsigned short) +\n\t\t(size - sizeof(aas_routingcache_t) + sizeof(unsigned short)) / 3 * 2;\n\treturn cache;\n} //end of the function AAS_ReadCache\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint AAS_ReadRouteCache(void)\n{\n\tint i, clusterareanum;//, size;\n\tfileHandle_t fp;\n\tchar filename[MAX_QPATH];\n\troutecacheheader_t routecacheheader;\n\taas_routingcache_t *cache;\n\n\tCom_sprintf(filename, MAX_QPATH, \"maps/%s.rcd\", aasworld.mapname);\n\tbotimport.FS_FOpenFile( filename, &fp, FS_READ );\n\tif (!fp)\n\t{\n\t\treturn qfalse;\n\t} //end if\n\tbotimport.FS_Read(&routecacheheader, sizeof(routecacheheader_t), fp );\n\tif (routecacheheader.ident != RCID)\n\t{\n\t\tAAS_Error(\"%s is not a route cache dump\\n\");\n\t\treturn qfalse;\n\t} //end if\n\tif (routecacheheader.version != RCVERSION)\n\t{\n\t\tAAS_Error(\"route cache dump has wrong version %d, should be %d\", routecacheheader.version, RCVERSION);\n\t\treturn qfalse;\n\t} //end if\n\tif (routecacheheader.numareas != aasworld.numareas)\n\t{\n\t\t//AAS_Error(\"route cache dump has wrong number of areas\\n\");\n\t\treturn qfalse;\n\t} //end if\n\tif (routecacheheader.numclusters != aasworld.numclusters)\n\t{\n\t\t//AAS_Error(\"route cache dump has wrong number of clusters\\n\");\n\t\treturn qfalse;\n\t} //end if\n\tif (routecacheheader.areacrc !=\n\t\tCRC_ProcessString( (unsigned char *)aasworld.areas, sizeof(aas_area_t) * aasworld.numareas ))\n\t{\n\t\t//AAS_Error(\"route cache dump area CRC incorrect\\n\");\n\t\treturn qfalse;\n\t} //end if\n\tif (routecacheheader.clustercrc !=\n\t\tCRC_ProcessString( (unsigned char *)aasworld.clusters, sizeof(aas_cluster_t) * aasworld.numclusters ))\n\t{\n\t\t//AAS_Error(\"route cache dump cluster CRC incorrect\\n\");\n\t\treturn qfalse;\n\t} //end if\n\t//read all the portal cache\n\tfor (i = 0; i < routecacheheader.numportalcache; i++)\n\t{\n\t\tcache = AAS_ReadCache(fp);\n\t\tcache->next = aasworld.portalcache[cache->areanum];\n\t\tcache->prev = NULL;\n\t\tif (aasworld.portalcache[cache->areanum])\n\t\t\taasworld.portalcache[cache->areanum]->prev = cache;\n\t\taasworld.portalcache[cache->areanum] = cache;\n\t} //end for\n\t//read all the cluster area cache\n\tfor (i = 0; i < routecacheheader.numareacache; i++)\n\t{\n\t\tcache = AAS_ReadCache(fp);\n\t\tclusterareanum = AAS_ClusterAreaNum(cache->cluster, cache->areanum);\n\t\tcache->next = aasworld.clusterareacache[cache->cluster][clusterareanum];\n\t\tcache->prev = NULL;\n\t\tif (aasworld.clusterareacache[cache->cluster][clusterareanum])\n\t\t\taasworld.clusterareacache[cache->cluster][clusterareanum]->prev = cache;\n\t\taasworld.clusterareacache[cache->cluster][clusterareanum] = cache;\n\t} //end for\n\t// read the visareas\n\t/*\n\taasworld.areavisibility = (byte **) GetClearedMemory(aasworld.numareas * sizeof(byte *));\n\taasworld.decompressedvis = (byte *) GetClearedMemory(aasworld.numareas * sizeof(byte));\n\tfor (i = 0; i < aasworld.numareas; i++)\n\t{\n\t\tbotimport.FS_Read(&size, sizeof(size), fp );\n\t\tif (size) {\n\t\t\taasworld.areavisibility[i] = (byte *) GetMemory(size);\n\t\t\tbotimport.FS_Read(aasworld.areavisibility[i], size, fp );\n\t\t}\n\t}\n\t*/\n\t//\n\tbotimport.FS_FCloseFile(fp);\n\treturn qtrue;\n} //end of the function AAS_ReadRouteCache\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\n#define MAX_REACHABILITYPASSAREAS\t\t32\n\nvoid AAS_InitReachabilityAreas(void)\n{\n\tint i, j, numareas, areas[MAX_REACHABILITYPASSAREAS];\n\tint numreachareas;\n\taas_reachability_t *reach;\n\tvec3_t start, end;\n\n\tif (aasworld.reachabilityareas)\n\t\tFreeMemory(aasworld.reachabilityareas);\n\tif (aasworld.reachabilityareaindex)\n\t\tFreeMemory(aasworld.reachabilityareaindex);\n\n\taasworld.reachabilityareas = (aas_reachabilityareas_t *)\n\t\t\t\tGetClearedMemory(aasworld.reachabilitysize * sizeof(aas_reachabilityareas_t));\n\taasworld.reachabilityareaindex = (int *)\n\t\t\t\tGetClearedMemory(aasworld.reachabilitysize * MAX_REACHABILITYPASSAREAS * sizeof(int));\n\tnumreachareas = 0;\n\tfor (i = 0; i < aasworld.reachabilitysize; i++)\n\t{\n\t\treach = &aasworld.reachability[i];\n\t\tnumareas = 0;\n\t\tswitch(reach->traveltype & TRAVELTYPE_MASK)\n\t\t{\n\t\t\t//trace areas from start to end\n\t\t\tcase TRAVEL_BARRIERJUMP:\n\t\t\tcase TRAVEL_WATERJUMP:\n\t\t\t\tVectorCopy(reach->start, end);\n\t\t\t\tend[2] = reach->end[2];\n\t\t\t\tnumareas = AAS_TraceAreas(reach->start, end, areas, NULL, MAX_REACHABILITYPASSAREAS);\n\t\t\t\tbreak;\n\t\t\tcase TRAVEL_WALKOFFLEDGE:\n\t\t\t\tVectorCopy(reach->end, start);\n\t\t\t\tstart[2] = reach->start[2];\n\t\t\t\tnumareas = AAS_TraceAreas(start, reach->end, areas, NULL, MAX_REACHABILITYPASSAREAS);\n\t\t\t\tbreak;\n\t\t\tcase TRAVEL_GRAPPLEHOOK:\n\t\t\t\tnumareas = AAS_TraceAreas(reach->start, reach->end, areas, NULL, MAX_REACHABILITYPASSAREAS);\n\t\t\t\tbreak;\n\n\t\t\t//trace arch\n\t\t\tcase TRAVEL_JUMP: break;\n\t\t\tcase TRAVEL_ROCKETJUMP: break;\n\t\t\tcase TRAVEL_BFGJUMP: break;\n\t\t\tcase TRAVEL_JUMPPAD: break;\n\n\t\t\t//trace from reach->start to entity center, along entity movement\n\t\t\t//and from entity center to reach->end\n\t\t\tcase TRAVEL_ELEVATOR: break;\n\t\t\tcase TRAVEL_FUNCBOB: break;\n\n\t\t\t//no areas in between\n\t\t\tcase TRAVEL_WALK: break;\n\t\t\tcase TRAVEL_CROUCH: break;\n\t\t\tcase TRAVEL_LADDER: break;\n\t\t\tcase TRAVEL_SWIM: break;\n\t\t\tcase TRAVEL_TELEPORT: break;\n\t\t\tdefault: break;\n\t\t} //end switch\n\t\taasworld.reachabilityareas[i].firstarea = numreachareas;\n\t\taasworld.reachabilityareas[i].numareas = numareas;\n\t\tfor (j = 0; j < numareas; j++)\n\t\t{\n\t\t\taasworld.reachabilityareaindex[numreachareas++] = areas[j];\n\t\t} //end for\n\t} //end for\n} //end of the function AAS_InitReachabilityAreas\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid AAS_InitRouting(void)\n{\n\tAAS_InitTravelFlagFromType();\n\t//\n\tAAS_InitAreaContentsTravelFlags();\n\t//initialize the routing update fields\n\tAAS_InitRoutingUpdate();\n\t//create reversed reachability links used by the routing update algorithm\n\tAAS_CreateReversedReachability();\n\t//initialize the cluster cache\n\tAAS_InitClusterAreaCache();\n\t//initialize portal cache\n\tAAS_InitPortalCache();\n\t//initialize the area travel times\n\tAAS_CalculateAreaTravelTimes();\n\t//calculate the maximum travel times through portals\n\tAAS_InitPortalMaxTravelTimes();\n\t//get the areas reachabilities go through\n\tAAS_InitReachabilityAreas();\n\t//\n#ifdef ROUTING_DEBUG\n\tnumareacacheupdates = 0;\n\tnumportalcacheupdates = 0;\n#endif //ROUTING_DEBUG\n\t//\n\troutingcachesize = 0;\n\tmax_routingcachesize = 1024 * (int) LibVarValue(\"max_routingcache\", \"4096\");\n\t// read any routing cache if available\n\tAAS_ReadRouteCache();\n} //end of the function AAS_InitRouting\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid AAS_FreeRoutingCaches(void)\n{\n\t// free all the existing cluster area cache\n\tAAS_FreeAllClusterAreaCache();\n\t// free all the existing portal cache\n\tAAS_FreeAllPortalCache();\n\t// free cached travel times within areas\n\tif (aasworld.areatraveltimes) FreeMemory(aasworld.areatraveltimes);\n\taasworld.areatraveltimes = NULL;\n\t// free cached maximum travel time through cluster portals\n\tif (aasworld.portalmaxtraveltimes) FreeMemory(aasworld.portalmaxtraveltimes);\n\taasworld.portalmaxtraveltimes = NULL;\n\t// free reversed reachability links\n\tif (aasworld.reversedreachability) FreeMemory(aasworld.reversedreachability);\n\taasworld.reversedreachability = NULL;\n\t// free routing algorithm memory\n\tif (aasworld.areaupdate) FreeMemory(aasworld.areaupdate);\n\taasworld.areaupdate = NULL;\n\tif (aasworld.portalupdate) FreeMemory(aasworld.portalupdate);\n\taasworld.portalupdate = NULL;\n\t// free lists with areas the reachabilities go through\n\tif (aasworld.reachabilityareas) FreeMemory(aasworld.reachabilityareas);\n\taasworld.reachabilityareas = NULL;\n\t// free the reachability area index\n\tif (aasworld.reachabilityareaindex) FreeMemory(aasworld.reachabilityareaindex);\n\taasworld.reachabilityareaindex = NULL;\n\t// free area contents travel flags look up table\n\tif (aasworld.areacontentstravelflags) FreeMemory(aasworld.areacontentstravelflags);\n\taasworld.areacontentstravelflags = NULL;\n} //end of the function AAS_FreeRoutingCaches\n//===========================================================================\n// update the given routing cache\n//\n// Parameter:\t\t\tareacache\t\t: routing cache to update\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid AAS_UpdateAreaRoutingCache(aas_routingcache_t *areacache)\n{\n\tint i, nextareanum, cluster, badtravelflags, clusterareanum, linknum;\n\tint numreachabilityareas;\n\tunsigned short int t, startareatraveltimes[128]; //NOTE: not more than 128 reachabilities per area allowed\n\taas_routingupdate_t *updateliststart, *updatelistend, *curupdate, *nextupdate;\n\taas_reachability_t *reach;\n\taas_reversedreachability_t *revreach;\n\taas_reversedlink_t *revlink;\n\n#ifdef ROUTING_DEBUG\n\tnumareacacheupdates++;\n#endif //ROUTING_DEBUG\n\t//number of reachability areas within this cluster\n\tnumreachabilityareas = aasworld.clusters[areacache->cluster].numreachabilityareas;\n\t//\n\taasworld.frameroutingupdates++;\n\t//clear the routing update fields\n//\tCom_Memset(aasworld.areaupdate, 0, aasworld.numareas * sizeof(aas_routingupdate_t));\n\t//\n\tbadtravelflags = ~areacache->travelflags;\n\t//\n\tclusterareanum = AAS_ClusterAreaNum(areacache->cluster, areacache->areanum);\n\tif (clusterareanum >= numreachabilityareas) return;\n\t//\n\tCom_Memset(startareatraveltimes, 0, sizeof(startareatraveltimes));\n\t//\n\tcurupdate = &aasworld.areaupdate[clusterareanum];\n\tcurupdate->areanum = areacache->areanum;\n\t//VectorCopy(areacache->origin, curupdate->start);\n\tcurupdate->areatraveltimes = startareatraveltimes;\n\tcurupdate->tmptraveltime = areacache->starttraveltime;\n\t//\n\tareacache->traveltimes[clusterareanum] = areacache->starttraveltime;\n\t//put the area to start with in the current read list\n\tcurupdate->next = NULL;\n\tcurupdate->prev = NULL;\n\tupdateliststart = curupdate;\n\tupdatelistend = curupdate;\n\t//while there are updates in the current list\n\twhile (updateliststart)\n\t{\n\t\tcurupdate = updateliststart;\n\t\t//\n\t\tif (curupdate->next) curupdate->next->prev = NULL;\n\t\telse updatelistend = NULL;\n\t\tupdateliststart = curupdate->next;\n\t\t//\n\t\tcurupdate->inlist = qfalse;\n\t\t//check all reversed reachability links\n\t\trevreach = &aasworld.reversedreachability[curupdate->areanum];\n\t\t//\n\t\tfor (i = 0, revlink = revreach->first; revlink; revlink = revlink->next, i++)\n\t\t{\n\t\t\tlinknum = revlink->linknum;\n\t\t\treach = &aasworld.reachability[linknum];\n\t\t\t//if there is used an undesired travel type\n\t\t\tif (AAS_TravelFlagForType_inline(reach->traveltype) & badtravelflags) continue;\n\t\t\t//if not allowed to enter the next area\n\t\t\tif (aasworld.areasettings[reach->areanum].areaflags & AREA_DISABLED) continue;\n\t\t\t//if the next area has a not allowed travel flag\n\t\t\tif (AAS_AreaContentsTravelFlags_inline(reach->areanum) & badtravelflags) continue;\n\t\t\t//number of the area the reversed reachability leads to\n\t\t\tnextareanum = revlink->areanum;\n\t\t\t//get the cluster number of the area\n\t\t\tcluster = aasworld.areasettings[nextareanum].cluster;\n\t\t\t//don't leave the cluster\n\t\t\tif (cluster > 0 && cluster != areacache->cluster) continue;\n\t\t\t//get the number of the area in the cluster\n\t\t\tclusterareanum = AAS_ClusterAreaNum(areacache->cluster, nextareanum);\n\t\t\tif (clusterareanum >= numreachabilityareas) continue;\n\t\t\t//time already travelled plus the traveltime through\n\t\t\t//the current area plus the travel time from the reachability\n\t\t\tt = curupdate->tmptraveltime +\n\t\t\t\t\t\t//AAS_AreaTravelTime(curupdate->areanum, curupdate->start, reach->end) +\n\t\t\t\t\t\tcurupdate->areatraveltimes[i] +\n\t\t\t\t\t\t\treach->traveltime;\n\t\t\t//\n\t\t\tif (!areacache->traveltimes[clusterareanum] ||\n\t\t\t\t\tareacache->traveltimes[clusterareanum] > t)\n\t\t\t{\n\t\t\t\tareacache->traveltimes[clusterareanum] = t;\n\t\t\t\tareacache->reachabilities[clusterareanum] = linknum - aasworld.areasettings[nextareanum].firstreachablearea;\n\t\t\t\tnextupdate = &aasworld.areaupdate[clusterareanum];\n\t\t\t\tnextupdate->areanum = nextareanum;\n\t\t\t\tnextupdate->tmptraveltime = t;\n\t\t\t\t//VectorCopy(reach->start, nextupdate->start);\n\t\t\t\tnextupdate->areatraveltimes = aasworld.areatraveltimes[nextareanum][linknum -\n\t\t\t\t\t\t\t\t\t\t\t\t\taasworld.areasettings[nextareanum].firstreachablearea];\n\t\t\t\tif (!nextupdate->inlist)\n\t\t\t\t{\n\t\t\t\t\t// we add the update to the end of the list\n\t\t\t\t\t// we could also use a B+ tree to have a real sorted list\n\t\t\t\t\t// on travel time which makes for faster routing updates\n\t\t\t\t\tnextupdate->next = NULL;\n\t\t\t\t\tnextupdate->prev = updatelistend;\n\t\t\t\t\tif (updatelistend) updatelistend->next = nextupdate;\n\t\t\t\t\telse updateliststart = nextupdate;\n\t\t\t\t\tupdatelistend = nextupdate;\n\t\t\t\t\tnextupdate->inlist = qtrue;\n\t\t\t\t} //end if\n\t\t\t} //end if\n\t\t} //end for\n\t} //end while\n} //end of the function AAS_UpdateAreaRoutingCache\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\naas_routingcache_t *AAS_GetAreaRoutingCache(int clusternum, int areanum, int travelflags)\n{\n\tint clusterareanum;\n\taas_routingcache_t *cache, *clustercache;\n\n\t//number of the area in the cluster\n\tclusterareanum = AAS_ClusterAreaNum(clusternum, areanum);\n\t//pointer to the cache for the area in the cluster\n\tclustercache = aasworld.clusterareacache[clusternum][clusterareanum];\n\t//find the cache without undesired travel flags\n\tfor (cache = clustercache; cache; cache = cache->next)\n\t{\n\t\t//if there aren't used any undesired travel types for the cache\n\t\tif (cache->travelflags == travelflags) break;\n\t} //end for\n\t//if there was no cache\n\tif (!cache)\n\t{\n\t\tcache = AAS_AllocRoutingCache(aasworld.clusters[clusternum].numreachabilityareas);\n\t\tcache->cluster = clusternum;\n\t\tcache->areanum = areanum;\n\t\tVectorCopy(aasworld.areas[areanum].center, cache->origin);\n\t\tcache->starttraveltime = 1;\n\t\tcache->travelflags = travelflags;\n\t\tcache->prev = NULL;\n\t\tcache->next = clustercache;\n\t\tif (clustercache) clustercache->prev = cache;\n\t\taasworld.clusterareacache[clusternum][clusterareanum] = cache;\n\t\tAAS_UpdateAreaRoutingCache(cache);\n\t} //end if\n\telse\n\t{\n\t\tAAS_UnlinkCache(cache);\n\t} //end else\n\t//the cache has been accessed\n\tcache->time = AAS_RoutingTime();\n\tcache->type = CACHETYPE_AREA;\n\tAAS_LinkCache(cache);\n\treturn cache;\n} //end of the function AAS_GetAreaRoutingCache\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid AAS_UpdatePortalRoutingCache(aas_routingcache_t *portalcache)\n{\n\tint i, portalnum, clusterareanum, clusternum;\n\tunsigned short int t;\n\taas_portal_t *portal;\n\taas_cluster_t *cluster;\n\taas_routingcache_t *cache;\n\taas_routingupdate_t *updateliststart, *updatelistend, *curupdate, *nextupdate;\n\n#ifdef ROUTING_DEBUG\n\tnumportalcacheupdates++;\n#endif //ROUTING_DEBUG\n\t//clear the routing update fields\n//\tCom_Memset(aasworld.portalupdate, 0, (aasworld.numportals+1) * sizeof(aas_routingupdate_t));\n\t//\n\tcurupdate = &aasworld.portalupdate[aasworld.numportals];\n\tcurupdate->cluster = portalcache->cluster;\n\tcurupdate->areanum = portalcache->areanum;\n\tcurupdate->tmptraveltime = portalcache->starttraveltime;\n\t//if the start area is a cluster portal, store the travel time for that portal\n\tclusternum = aasworld.areasettings[portalcache->areanum].cluster;\n\tif (clusternum < 0)\n\t{\n\t\tportalcache->traveltimes[-clusternum] = portalcache->starttraveltime;\n\t} //end if\n\t//put the area to start with in the current read list\n\tcurupdate->next = NULL;\n\tcurupdate->prev = NULL;\n\tupdateliststart = curupdate;\n\tupdatelistend = curupdate;\n\t//while there are updates in the current list\n\twhile (updateliststart)\n\t{\n\t\tcurupdate = updateliststart;\n\t\t//remove the current update from the list\n\t\tif (curupdate->next) curupdate->next->prev = NULL;\n\t\telse updatelistend = NULL;\n\t\tupdateliststart = curupdate->next;\n\t\t//current update is removed from the list\n\t\tcurupdate->inlist = qfalse;\n\t\t//\n\t\tcluster = &aasworld.clusters[curupdate->cluster];\n\t\t//\n\t\tcache = AAS_GetAreaRoutingCache(curupdate->cluster,\n\t\t\t\t\t\t\t\tcurupdate->areanum, portalcache->travelflags);\n\t\t//take all portals of the cluster\n\t\tfor (i = 0; i < cluster->numportals; i++)\n\t\t{\n\t\t\tportalnum = aasworld.portalindex[cluster->firstportal + i];\n\t\t\tportal = &aasworld.portals[portalnum];\n\t\t\t//if this is the portal of the current update continue\n\t\t\tif (portal->areanum == curupdate->areanum) continue;\n\t\t\t//\n\t\t\tclusterareanum = AAS_ClusterAreaNum(curupdate->cluster, portal->areanum);\n\t\t\tif (clusterareanum >= cluster->numreachabilityareas) continue;\n\t\t\t//\n\t\t\tt = cache->traveltimes[clusterareanum];\n\t\t\tif (!t) continue;\n\t\t\tt += curupdate->tmptraveltime;\n\t\t\t//\n\t\t\tif (!portalcache->traveltimes[portalnum] ||\n\t\t\t\t\tportalcache->traveltimes[portalnum] > t)\n\t\t\t{\n\t\t\t\tportalcache->traveltimes[portalnum] = t;\n\t\t\t\tnextupdate = &aasworld.portalupdate[portalnum];\n\t\t\t\tif (portal->frontcluster == curupdate->cluster)\n\t\t\t\t{\n\t\t\t\t\tnextupdate->cluster = portal->backcluster;\n\t\t\t\t} //end if\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tnextupdate->cluster = portal->frontcluster;\n\t\t\t\t} //end else\n\t\t\t\tnextupdate->areanum = portal->areanum;\n\t\t\t\t//add travel time through the actual portal area for the next update\n\t\t\t\tnextupdate->tmptraveltime = t + aasworld.portalmaxtraveltimes[portalnum];\n\t\t\t\tif (!nextupdate->inlist)\n\t\t\t\t{\n\t\t\t\t\t// we add the update to the end of the list\n\t\t\t\t\t// we could also use a B+ tree to have a real sorted list\n\t\t\t\t\t// on travel time which makes for faster routing updates\n\t\t\t\t\tnextupdate->next = NULL;\n\t\t\t\t\tnextupdate->prev = updatelistend;\n\t\t\t\t\tif (updatelistend) updatelistend->next = nextupdate;\n\t\t\t\t\telse updateliststart = nextupdate;\n\t\t\t\t\tupdatelistend = nextupdate;\n\t\t\t\t\tnextupdate->inlist = qtrue;\n\t\t\t\t} //end if\n\t\t\t} //end if\n\t\t} //end for\n\t} //end while\n} //end of the function AAS_UpdatePortalRoutingCache\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\naas_routingcache_t *AAS_GetPortalRoutingCache(int clusternum, int areanum, int travelflags)\n{\n\taas_routingcache_t *cache;\n\n\t//find the cached portal routing if existing\n\tfor (cache = aasworld.portalcache[areanum]; cache; cache = cache->next)\n\t{\n\t\tif (cache->travelflags == travelflags) break;\n\t} //end for\n\t//if the portal routing isn't cached\n\tif (!cache)\n\t{\n\t\tcache = AAS_AllocRoutingCache(aasworld.numportals);\n\t\tcache->cluster = clusternum;\n\t\tcache->areanum = areanum;\n\t\tVectorCopy(aasworld.areas[areanum].center, cache->origin);\n\t\tcache->starttraveltime = 1;\n\t\tcache->travelflags = travelflags;\n\t\t//add the cache to the cache list\n\t\tcache->prev = NULL;\n\t\tcache->next = aasworld.portalcache[areanum];\n\t\tif (aasworld.portalcache[areanum]) aasworld.portalcache[areanum]->prev = cache;\n\t\taasworld.portalcache[areanum] = cache;\n\t\t//update the cache\n\t\tAAS_UpdatePortalRoutingCache(cache);\n\t} //end if\n\telse\n\t{\n\t\tAAS_UnlinkCache(cache);\n\t} //end else\n\t//the cache has been accessed\n\tcache->time = AAS_RoutingTime();\n\tcache->type = CACHETYPE_PORTAL;\n\tAAS_LinkCache(cache);\n\treturn cache;\n} //end of the function AAS_GetPortalRoutingCache\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint AAS_AreaRouteToGoalArea(int areanum, vec3_t origin, int goalareanum, int travelflags, int *traveltime, int *reachnum)\n{\n\tint clusternum, goalclusternum, portalnum, i, clusterareanum, bestreachnum;\n\tunsigned short int t, besttime;\n\taas_portal_t *portal;\n\taas_cluster_t *cluster;\n\taas_routingcache_t *areacache, *portalcache;\n\taas_reachability_t *reach;\n\n\tif (!aasworld.initialized) return qfalse;\n\n\tif (areanum == goalareanum)\n\t{\n\t\t*traveltime = 1;\n\t\t*reachnum = 0;\n\t\treturn qtrue;\n\t}\n\t//\n\tif (areanum <= 0 || areanum >= aasworld.numareas)\n\t{\n\t\tif (botDeveloper)\n\t\t{\n\t\t\tbotimport.Print(PRT_ERROR, \"AAS_AreaTravelTimeToGoalArea: areanum %d out of range\\n\", areanum);\n\t\t} //end if\n\t\treturn qfalse;\n\t} //end if\n\tif (goalareanum <= 0 || goalareanum >= aasworld.numareas)\n\t{\n\t\tif (botDeveloper)\n\t\t{\n\t\t\tbotimport.Print(PRT_ERROR, \"AAS_AreaTravelTimeToGoalArea: goalareanum %d out of range\\n\", goalareanum);\n\t\t} //end if\n\t\treturn qfalse;\n\t} //end if\n\t// make sure the routing cache doesn't grow to large\n\twhile(AvailableMemory() < 1 * 1024 * 1024) {\n\t\tif (!AAS_FreeOldestCache()) break;\n\t}\n\t//\n\tif (AAS_AreaDoNotEnter(areanum) || AAS_AreaDoNotEnter(goalareanum))\n\t{\n\t\ttravelflags |= TFL_DONOTENTER;\n\t} //end if\n\t//NOTE: the number of routing updates is limited per frame\n\t/*\n\tif (aasworld.frameroutingupdates > MAX_FRAMEROUTINGUPDATES)\n\t{\n#ifdef DEBUG\n\t\t//Log_Write(\"WARNING: AAS_AreaTravelTimeToGoalArea: frame routing updates overflowed\");\n#endif\n\t\treturn 0;\n\t} //end if\n\t*/\n\t//\n\tclusternum = aasworld.areasettings[areanum].cluster;\n\tgoalclusternum = aasworld.areasettings[goalareanum].cluster;\n\t//check if the area is a portal of the goal area cluster\n\tif (clusternum < 0 && goalclusternum > 0)\n\t{\n\t\tportal = &aasworld.portals[-clusternum];\n\t\tif (portal->frontcluster == goalclusternum ||\n\t\t\t\tportal->backcluster == goalclusternum)\n\t\t{\n\t\t\tclusternum = goalclusternum;\n\t\t} //end if\n\t} //end if\n\t//check if the goalarea is a portal of the area cluster\n\telse if (clusternum > 0 && goalclusternum < 0)\n\t{\n\t\tportal = &aasworld.portals[-goalclusternum];\n\t\tif (portal->frontcluster == clusternum ||\n\t\t\t\tportal->backcluster == clusternum)\n\t\t{\n\t\t\tgoalclusternum = clusternum;\n\t\t} //end if\n\t} //end if\n\t//if both areas are in the same cluster\n\t//NOTE: there might be a shorter route via another cluster!!! but we don't care\n\tif (clusternum > 0 && goalclusternum > 0 && clusternum == goalclusternum)\n\t{\n\t\t//\n\t\tareacache = AAS_GetAreaRoutingCache(clusternum, goalareanum, travelflags);\n\t\t//the number of the area in the cluster\n\t\tclusterareanum = AAS_ClusterAreaNum(clusternum, areanum);\n\t\t//the cluster the area is in\n\t\tcluster = &aasworld.clusters[clusternum];\n\t\t//if the area is NOT a reachability area\n\t\tif (clusterareanum >= cluster->numreachabilityareas) return 0;\n\t\t//if it is possible to travel to the goal area through this cluster\n\t\tif (areacache->traveltimes[clusterareanum] != 0)\n\t\t{\n\t\t\t*reachnum = aasworld.areasettings[areanum].firstreachablearea +\n\t\t\t\t\t\t\tareacache->reachabilities[clusterareanum];\n\t\t\tif (!origin) {\n\t\t\t\t*traveltime = areacache->traveltimes[clusterareanum];\n\t\t\t\treturn qtrue;\n\t\t\t}\n\t\t\treach = &aasworld.reachability[*reachnum];\n\t\t\t*traveltime = areacache->traveltimes[clusterareanum] +\n\t\t\t\t\t\t\tAAS_AreaTravelTime(areanum, origin, reach->start);\n\t\t\t//\n\t\t\treturn qtrue;\n\t\t} //end if\n\t} //end if\n\t//\n\tclusternum = aasworld.areasettings[areanum].cluster;\n\tgoalclusternum = aasworld.areasettings[goalareanum].cluster;\n\t//if the goal area is a portal\n\tif (goalclusternum < 0)\n\t{\n\t\t//just assume the goal area is part of the front cluster\n\t\tportal = &aasworld.portals[-goalclusternum];\n\t\tgoalclusternum = portal->frontcluster;\n\t} //end if\n\t//get the portal routing cache\n\tportalcache = AAS_GetPortalRoutingCache(goalclusternum, goalareanum, travelflags);\n\t//if the area is a cluster portal, read directly from the portal cache\n\tif (clusternum < 0)\n\t{\n\t\t*traveltime = portalcache->traveltimes[-clusternum];\n\t\t*reachnum = aasworld.areasettings[areanum].firstreachablearea +\n\t\t\t\t\t\tportalcache->reachabilities[-clusternum];\n\t\treturn qtrue;\n\t} //end if\n\t//\n\tbesttime = 0;\n\tbestreachnum = -1;\n\t//the cluster the area is in\n\tcluster = &aasworld.clusters[clusternum];\n\t//find the portal of the area cluster leading towards the goal area\n\tfor (i = 0; i < cluster->numportals; i++)\n\t{\n\t\tportalnum = aasworld.portalindex[cluster->firstportal + i];\n\t\t//if the goal area isn't reachable from the portal\n\t\tif (!portalcache->traveltimes[portalnum]) continue;\n\t\t//\n\t\tportal = &aasworld.portals[portalnum];\n\t\t//get the cache of the portal area\n\t\tareacache = AAS_GetAreaRoutingCache(clusternum, portal->areanum, travelflags);\n\t\t//current area inside the current cluster\n\t\tclusterareanum = AAS_ClusterAreaNum(clusternum, areanum);\n\t\t//if the area is NOT a reachability area\n\t\tif (clusterareanum >= cluster->numreachabilityareas) continue;\n\t\t//if the portal is NOT reachable from this area\n\t\tif (!areacache->traveltimes[clusterareanum]) continue;\n\t\t//total travel time is the travel time the portal area is from\n\t\t//the goal area plus the travel time towards the portal area\n\t\tt = portalcache->traveltimes[portalnum] + areacache->traveltimes[clusterareanum];\n\t\t//FIXME: add the exact travel time through the actual portal area\n\t\t//NOTE: for now we just add the largest travel time through the portal area\n\t\t//\t\tbecause we can't directly calculate the exact travel time\n\t\t//\t\tto be more specific we don't know which reachability was used to travel\n\t\t//\t\tinto the portal area\n\t\tt += aasworld.portalmaxtraveltimes[portalnum];\n\t\t//\n\n\t\t*reachnum = aasworld.areasettings[areanum].firstreachablearea +\n\t\t\t\t\t\tareacache->reachabilities[clusterareanum];\n\t\tif (origin)\n\t\t{\n\t\t\treach = aasworld.reachability + *reachnum;\n\t\t\tt += AAS_AreaTravelTime(areanum, origin, reach->start);\n\t\t} //end if\n\t\t//if the time is better than the one already found\n\t\tif (!besttime || t < besttime)\n\t\t{\n\t\t\tbestreachnum = *reachnum;\n\t\t\tbesttime = t;\n\t\t} //end if\n\t} //end for\n\tif (bestreachnum < 0) {\n\t\treturn qfalse;\n\t}\n\t*reachnum = bestreachnum;\n\t*traveltime = besttime;\n\treturn qtrue;\n} //end of the function AAS_AreaRouteToGoalArea\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint AAS_AreaTravelTimeToGoalArea(int areanum, vec3_t origin, int goalareanum, int travelflags)\n{\n\tint traveltime, reachnum;\n\n\tif (AAS_AreaRouteToGoalArea(areanum, origin, goalareanum, travelflags, &traveltime, &reachnum))\n\t{\n\t\treturn traveltime;\n\t}\n\treturn 0;\n} //end of the function AAS_AreaTravelTimeToGoalArea\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint AAS_AreaReachabilityToGoalArea(int areanum, vec3_t origin, int goalareanum, int travelflags)\n{\n\tint traveltime, reachnum;\n\n\tif (AAS_AreaRouteToGoalArea(areanum, origin, goalareanum, travelflags, &traveltime, &reachnum))\n\t{\n\t\treturn reachnum;\n\t}\n\treturn 0;\n} //end of the function AAS_AreaReachabilityToGoalArea\n//===========================================================================\n// predict the route and stop on one of the stop events\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint AAS_PredictRoute(struct aas_predictroute_s *route, int areanum, vec3_t origin,\n\t\t\t\t\t\t\tint goalareanum, int travelflags, int maxareas, int maxtime,\n\t\t\t\t\t\t\tint stopevent, int stopcontents, int stoptfl, int stopareanum)\n{\n\tint curareanum, reachnum, i, j, testareanum;\n\tvec3_t curorigin;\n\taas_reachability_t *reach;\n\taas_reachabilityareas_t *reachareas;\n\n\t//init output\n\troute->stopevent = RSE_NONE;\n\troute->endarea = goalareanum;\n\troute->endcontents = 0;\n\troute->endtravelflags = 0;\n\tVectorCopy(origin, route->endpos);\n\troute->time = 0;\n\n\tcurareanum = areanum;\n\tVectorCopy(origin, curorigin);\n\n\tfor (i = 0; curareanum != goalareanum && (!maxareas || i < maxareas) && i < aasworld.numareas; i++)\n\t{\n\t\treachnum = AAS_AreaReachabilityToGoalArea(curareanum, curorigin, goalareanum, travelflags);\n\t\tif (!reachnum)\n\t\t{\n\t\t\troute->stopevent = RSE_NOROUTE;\n\t\t\treturn qfalse;\n\t\t} //end if\n\t\treach = &aasworld.reachability[reachnum];\n\t\t//\n\t\tif (stopevent & RSE_USETRAVELTYPE)\n\t\t{\n\t\t\tif (AAS_TravelFlagForType_inline(reach->traveltype) & stoptfl)\n\t\t\t{\n\t\t\t\troute->stopevent = RSE_USETRAVELTYPE;\n\t\t\t\troute->endarea = curareanum;\n\t\t\t\troute->endcontents = aasworld.areasettings[curareanum].contents;\n\t\t\t\troute->endtravelflags = AAS_TravelFlagForType_inline(reach->traveltype);\n\t\t\t\tVectorCopy(reach->start, route->endpos);\n\t\t\t\treturn qtrue;\n\t\t\t} //end if\n\t\t\tif (AAS_AreaContentsTravelFlags_inline(reach->areanum) & stoptfl)\n\t\t\t{\n\t\t\t\troute->stopevent = RSE_USETRAVELTYPE;\n\t\t\t\troute->endarea = reach->areanum;\n\t\t\t\troute->endcontents = aasworld.areasettings[reach->areanum].contents;\n\t\t\t\troute->endtravelflags = AAS_AreaContentsTravelFlags_inline(reach->areanum);\n\t\t\t\tVectorCopy(reach->end, route->endpos);\n\t\t\t\troute->time += AAS_AreaTravelTime(areanum, origin, reach->start);\n\t\t\t\troute->time += reach->traveltime;\n\t\t\t\treturn qtrue;\n\t\t\t} //end if\n\t\t} //end if\n\t\treachareas = &aasworld.reachabilityareas[reachnum];\n\t\tfor (j = 0; j < reachareas->numareas + 1; j++)\n\t\t{\n\t\t\tif (j >= reachareas->numareas)\n\t\t\t\ttestareanum = reach->areanum;\n\t\t\telse\n\t\t\t\ttestareanum = aasworld.reachabilityareaindex[reachareas->firstarea + j];\n\t\t\tif (stopevent & RSE_ENTERCONTENTS)\n\t\t\t{\n\t\t\t\tif (aasworld.areasettings[testareanum].contents & stopcontents)\n\t\t\t\t{\n\t\t\t\t\troute->stopevent = RSE_ENTERCONTENTS;\n\t\t\t\t\troute->endarea = testareanum;\n\t\t\t\t\troute->endcontents = aasworld.areasettings[testareanum].contents;\n\t\t\t\t\tVectorCopy(reach->end, route->endpos);\n\t\t\t\t\troute->time += AAS_AreaTravelTime(areanum, origin, reach->start);\n\t\t\t\t\troute->time += reach->traveltime;\n\t\t\t\t\treturn qtrue;\n\t\t\t\t} //end if\n\t\t\t} //end if\n\t\t\tif (stopevent & RSE_ENTERAREA)\n\t\t\t{\n\t\t\t\tif (testareanum == stopareanum)\n\t\t\t\t{\n\t\t\t\t\troute->stopevent = RSE_ENTERAREA;\n\t\t\t\t\troute->endarea = testareanum;\n\t\t\t\t\troute->endcontents = aasworld.areasettings[testareanum].contents;\n\t\t\t\t\tVectorCopy(reach->start, route->endpos);\n\t\t\t\t\treturn qtrue;\n\t\t\t\t} //end if\n\t\t\t} //end if\n\t\t} //end for\n\n\t\troute->time += AAS_AreaTravelTime(areanum, origin, reach->start);\n\t\troute->time += reach->traveltime;\n\t\troute->endarea = reach->areanum;\n\t\troute->endcontents = aasworld.areasettings[reach->areanum].contents;\n\t\troute->endtravelflags = AAS_TravelFlagForType_inline(reach->traveltype);\n\t\tVectorCopy(reach->end, route->endpos);\n\t\t//\n\t\tcurareanum = reach->areanum;\n\t\tVectorCopy(reach->end, curorigin);\n\t\t//\n\t\tif (maxtime && route->time > maxtime)\n\t\t\tbreak;\n\t} //end while\n\tif (curareanum != goalareanum)\n\t\treturn qfalse;\n\treturn qtrue;\n} //end of the function AAS_PredictRoute\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint AAS_BridgeWalkable(int areanum)\n{\n\treturn qfalse;\n} //end of the function AAS_BridgeWalkable\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid AAS_ReachabilityFromNum(int num, struct aas_reachability_s *reach)\n{\n\tif (!aasworld.initialized)\n\t{\n\t\tCom_Memset(reach, 0, sizeof(aas_reachability_t));\n\t\treturn;\n\t} //end if\n\tif (num < 0 || num >= aasworld.reachabilitysize)\n\t{\n\t\tCom_Memset(reach, 0, sizeof(aas_reachability_t));\n\t\treturn;\n\t} //end if\n\tCom_Memcpy(reach, &aasworld.reachability[num], sizeof(aas_reachability_t));;\n} //end of the function AAS_ReachabilityFromNum\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint AAS_NextAreaReachability(int areanum, int reachnum)\n{\n\taas_areasettings_t *settings;\n\n\tif (!aasworld.initialized) return 0;\n\n\tif (areanum <= 0 || areanum >= aasworld.numareas)\n\t{\n\t\tbotimport.Print(PRT_ERROR, \"AAS_NextAreaReachability: areanum %d out of range\\n\", areanum);\n\t\treturn 0;\n\t} //end if\n\n\tsettings = &aasworld.areasettings[areanum];\n\tif (!reachnum)\n\t{\n\t\treturn settings->firstreachablearea;\n\t} //end if\n\tif (reachnum < settings->firstreachablearea)\n\t{\n\t\tbotimport.Print(PRT_FATAL, \"AAS_NextAreaReachability: reachnum < settings->firstreachableara\");\n\t\treturn 0;\n\t} //end if\n\treachnum++;\n\tif (reachnum >= settings->firstreachablearea + settings->numreachableareas)\n\t{\n\t\treturn 0;\n\t} //end if\n\treturn reachnum;\n} //end of the function AAS_NextAreaReachability\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint AAS_NextModelReachability(int num, int modelnum)\n{\n\tint i;\n\n\tif (num <= 0) num = 1;\n\telse if (num >= aasworld.reachabilitysize) return 0;\n\telse num++;\n\t//\n\tfor (i = num; i < aasworld.reachabilitysize; i++)\n\t{\n\t\tif ((aasworld.reachability[i].traveltype & TRAVELTYPE_MASK) == TRAVEL_ELEVATOR)\n\t\t{\n\t\t\tif (aasworld.reachability[i].facenum == modelnum) return i;\n\t\t} //end if\n\t\telse if ((aasworld.reachability[i].traveltype & TRAVELTYPE_MASK) == TRAVEL_FUNCBOB)\n\t\t{\n\t\t\tif ((aasworld.reachability[i].facenum & 0x0000FFFF) == modelnum) return i;\n\t\t} //end if\n\t} //end for\n\treturn 0;\n} //end of the function AAS_NextModelReachability\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint AAS_RandomGoalArea(int areanum, int travelflags, int *goalareanum, vec3_t goalorigin)\n{\n\tint i, n, t;\n\tvec3_t start, end;\n\taas_trace_t trace;\n\n\t//if the area has no reachabilities\n\tif (!AAS_AreaReachability(areanum)) return qfalse;\n\t//\n\tn = aasworld.numareas * random();\n\tfor (i = 0; i < aasworld.numareas; i++)\n\t{\n\t\tif (n <= 0) n = 1;\n\t\tif (n >= aasworld.numareas) n = 1;\n\t\tif (AAS_AreaReachability(n))\n\t\t{\n\t\t\tt = AAS_AreaTravelTimeToGoalArea(areanum, aasworld.areas[areanum].center, n, travelflags);\n\t\t\t//if the goal is reachable\n\t\t\tif (t > 0)\n\t\t\t{\n\t\t\t\tif (AAS_AreaSwim(n))\n\t\t\t\t{\n\t\t\t\t\t*goalareanum = n;\n\t\t\t\t\tVectorCopy(aasworld.areas[n].center, goalorigin);\n\t\t\t\t\t//botimport.Print(PRT_MESSAGE, \"found random goal area %d\\n\", *goalareanum);\n\t\t\t\t\treturn qtrue;\n\t\t\t\t} //end if\n\t\t\t\tVectorCopy(aasworld.areas[n].center, start);\n\t\t\t\tif (!AAS_PointAreaNum(start))\n\t\t\t\t\tLog_Write(\"area %d center %f %f %f in solid?\", n, start[0], start[1], start[2]);\n\t\t\t\tVectorCopy(start, end);\n\t\t\t\tend[2] -= 300;\n\t\t\t\ttrace = AAS_TraceClientBBox(start, end, PRESENCE_CROUCH, -1);\n\t\t\t\tif (!trace.startsolid && trace.fraction < 1 && AAS_PointAreaNum(trace.endpos) == n)\n\t\t\t\t{\n\t\t\t\t\tif (AAS_AreaGroundFaceArea(n) > 300)\n\t\t\t\t\t{\n\t\t\t\t\t\t*goalareanum = n;\n\t\t\t\t\t\tVectorCopy(trace.endpos, goalorigin);\n\t\t\t\t\t\t//botimport.Print(PRT_MESSAGE, \"found random goal area %d\\n\", *goalareanum);\n\t\t\t\t\t\treturn qtrue;\n\t\t\t\t\t} //end if\n\t\t\t\t} //end if\n\t\t\t} //end if\n\t\t} //end if\n\t\tn++;\n\t} //end for\n\treturn qfalse;\n} //end of the function AAS_RandomGoalArea\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint AAS_AreaVisible(int srcarea, int destarea)\n{\n\treturn qfalse;\n} //end of the function AAS_AreaVisible\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nfloat DistancePointToLine(vec3_t v1, vec3_t v2, vec3_t point)\n{\n\tvec3_t vec, p2;\n\n\tAAS_ProjectPointOntoVector(point, v1, v2, p2);\n\tVectorSubtract(point, p2, vec);\n\treturn VectorLength(vec);\n} //end of the function DistancePointToLine\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint AAS_NearestHideArea(int srcnum, vec3_t origin, int areanum, int enemynum, vec3_t enemyorigin, int enemyareanum, int travelflags)\n{\n\tint i, j, nextareanum, badtravelflags, numreach, bestarea;\n\tunsigned short int t, besttraveltime;\n\tstatic unsigned short int *hidetraveltimes;\n\taas_routingupdate_t *updateliststart, *updatelistend, *curupdate, *nextupdate;\n\taas_reachability_t *reach;\n\tfloat dist1, dist2;\n\tvec3_t v1, v2, p;\n\tqboolean startVisible;\n\n\t//\n\tif (!hidetraveltimes)\n\t{\n\t\thidetraveltimes = (unsigned short int *) GetClearedMemory(aasworld.numareas * sizeof(unsigned short int));\n\t} //end if\n\telse\n\t{\n\t\tCom_Memset(hidetraveltimes, 0, aasworld.numareas * sizeof(unsigned short int));\n\t} //end else\n\tbesttraveltime = 0;\n\tbestarea = 0;\n\t//assume visible\n\tstartVisible = qtrue;\n\t//\n\tbadtravelflags = ~travelflags;\n\t//\n\tcurupdate = &aasworld.areaupdate[areanum];\n\tcurupdate->areanum = areanum;\n\tVectorCopy(origin, curupdate->start);\n\tcurupdate->areatraveltimes = aasworld.areatraveltimes[areanum][0];\n\tcurupdate->tmptraveltime = 0;\n\t//put the area to start with in the current read list\n\tcurupdate->next = NULL;\n\tcurupdate->prev = NULL;\n\tupdateliststart = curupdate;\n\tupdatelistend = curupdate;\n\t//while there are updates in the list\n\twhile (updateliststart)\n\t{\n\t\tcurupdate = updateliststart;\n\t\t//\n\t\tif (curupdate->next) curupdate->next->prev = NULL;\n\t\telse updatelistend = NULL;\n\t\tupdateliststart = curupdate->next;\n\t\t//\n\t\tcurupdate->inlist = qfalse;\n\t\t//check all reversed reachability links\n\t\tnumreach = aasworld.areasettings[curupdate->areanum].numreachableareas;\n\t\treach = &aasworld.reachability[aasworld.areasettings[curupdate->areanum].firstreachablearea];\n\t\t//\n\t\tfor (i = 0; i < numreach; i++, reach++)\n\t\t{\n\t\t\t//if an undesired travel type is used\n\t\t\tif (AAS_TravelFlagForType_inline(reach->traveltype) & badtravelflags) continue;\n\t\t\t//\n\t\t\tif (AAS_AreaContentsTravelFlags_inline(reach->areanum) & badtravelflags) continue;\n\t\t\t//number of the area the reachability leads to\n\t\t\tnextareanum = reach->areanum;\n\t\t\t// if this moves us into the enemies area, skip it\n\t\t\tif (nextareanum == enemyareanum) continue;\n\t\t\t//time already travelled plus the traveltime through\n\t\t\t//the current area plus the travel time from the reachability\n\t\t\tt = curupdate->tmptraveltime +\n\t\t\t\t\t\tAAS_AreaTravelTime(curupdate->areanum, curupdate->start, reach->start) +\n\t\t\t\t\t\t\treach->traveltime;\n\n\t\t\t//avoid going near the enemy\n\t\t\tAAS_ProjectPointOntoVector(enemyorigin, curupdate->start, reach->end, p);\n\t\t\tfor (j = 0; j < 3; j++)\n\t\t\t\tif ((p[j] > curupdate->start[j] && p[j] > reach->end[j]) ||\n\t\t\t\t\t(p[j] < curupdate->start[j] && p[j] < reach->end[j]))\n\t\t\t\t\tbreak;\n\t\t\tif (j < 3)\n\t\t\t{\n\t\t\t\tVectorSubtract(enemyorigin, reach->end, v2);\n\t\t\t} //end if\n\t\t\telse\n\t\t\t{\n\t\t\t\tVectorSubtract(enemyorigin, p, v2);\n\t\t\t} //end else\n\t\t\tdist2 = VectorLength(v2);\n\t\t\t//never go through the enemy\n\t\t\tif (dist2 < 40) continue;\n\t\t\t//\n\t\t\tVectorSubtract(enemyorigin, curupdate->start, v1);\n\t\t\tdist1 = VectorLength(v1);\n\t\t\t//\n\t\t\tif (dist2 < dist1)\n\t\t\t{\n\t\t\t\tt += (dist1 - dist2) * 10;\n\t\t\t}\n\t\t\t// if we weren't visible when starting, make sure we don't move into their view\n\t\t\tif (!startVisible && AAS_AreaVisible(enemyareanum, nextareanum)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t//\n\t\t\tif (besttraveltime && t >= besttraveltime) continue;\n\t\t\t//\n\t\t\tif (!hidetraveltimes[nextareanum] ||\n\t\t\t\t\thidetraveltimes[nextareanum] > t)\n\t\t\t{\n\t\t\t\t//if the nextarea is not visible from the enemy area\n\t\t\t\tif (!AAS_AreaVisible(enemyareanum, nextareanum))\n\t\t\t\t{\n\t\t\t\t\tbesttraveltime = t;\n\t\t\t\t\tbestarea = nextareanum;\n\t\t\t\t} //end if\n\t\t\t\thidetraveltimes[nextareanum] = t;\n\t\t\t\tnextupdate = &aasworld.areaupdate[nextareanum];\n\t\t\t\tnextupdate->areanum = nextareanum;\n\t\t\t\tnextupdate->tmptraveltime = t;\n\t\t\t\t//remember where we entered this area\n\t\t\t\tVectorCopy(reach->end, nextupdate->start);\n\t\t\t\t//if this update is not in the list yet\n\t\t\t\tif (!nextupdate->inlist)\n\t\t\t\t{\n\t\t\t\t\t//add the new update to the end of the list\n\t\t\t\t\tnextupdate->next = NULL;\n\t\t\t\t\tnextupdate->prev = updatelistend;\n\t\t\t\t\tif (updatelistend) updatelistend->next = nextupdate;\n\t\t\t\t\telse updateliststart = nextupdate;\n\t\t\t\t\tupdatelistend = nextupdate;\n\t\t\t\t\tnextupdate->inlist = qtrue;\n\t\t\t\t} //end if\n\t\t\t} //end if\n\t\t} //end for\n\t} //end while\n\treturn bestarea;\n} //end of the function AAS_NearestHideArea\n"
  },
  {
    "path": "plugins/quake3/botlib/be_aas_route.h",
    "content": "/*\n===========================================================================\nCopyright (C) 1999-2005 Id Software, Inc.\n\nThis file is part of Quake III Arena source code.\n\nQuake III Arena source code is free software; you can redistribute it\nand/or modify it under the terms of the GNU General Public License as\npublished by the Free Software Foundation; either version 2 of the License,\nor (at your option) any later version.\n\nQuake III Arena source code is distributed in the hope that it will be\nuseful, but WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Quake III Arena source code; if not, write to the Free Software\nFoundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n===========================================================================\n*/\n\n/*****************************************************************************\n * name:\t\tbe_aas_route.h\n *\n * desc:\t\tAAS\n *\n * $Archive: /source/code/botlib/be_aas_route.h $\n *\n *****************************************************************************/\n\n#ifdef AASINTERN\n//initialize the AAS routing\nvoid AAS_InitRouting(void);\n//free the AAS routing caches\nvoid AAS_FreeRoutingCaches(void);\n//returns the travel time from start to end in the given area\nunsigned short int AAS_AreaTravelTime(int areanum, vec3_t start, vec3_t end);\n//\nvoid AAS_CreateAllRoutingCache(void);\nvoid AAS_WriteRouteCache(void);\n//\nvoid AAS_RoutingInfo(void);\n#endif //AASINTERN\n\n//returns the travel flag for the given travel type\nint AAS_TravelFlagForType(int traveltype);\n//return the travel flag(s) for traveling through this area\nint AAS_AreaContentsTravelFlags(int areanum);\n//returns the index of the next reachability for the given area\nint AAS_NextAreaReachability(int areanum, int reachnum);\n//returns the reachability with the given index\nvoid AAS_ReachabilityFromNum(int num, struct aas_reachability_s *reach);\n//returns a random goal area and goal origin\nint AAS_RandomGoalArea(int areanum, int travelflags, int *goalareanum, vec3_t goalorigin);\n//enable or disable an area for routing\nint AAS_EnableRoutingArea(int areanum, int enable);\n//returns the travel time within the given area from start to end\nunsigned short int AAS_AreaTravelTime(int areanum, vec3_t start, vec3_t end);\n//returns the travel time from the area to the goal area using the given travel flags\nint AAS_AreaTravelTimeToGoalArea(int areanum, vec3_t origin, int goalareanum, int travelflags);\n//predict a route up to a stop event\nint AAS_PredictRoute(struct aas_predictroute_s *route, int areanum, vec3_t origin,\n\t\t\t\t\t\t\tint goalareanum, int travelflags, int maxareas, int maxtime,\n\t\t\t\t\t\t\tint stopevent, int stopcontents, int stoptfl, int stopareanum);\n\n\n"
  },
  {
    "path": "plugins/quake3/botlib/be_aas_routealt.c",
    "content": "/*\n===========================================================================\nCopyright (C) 1999-2005 Id Software, Inc.\n\nThis file is part of Quake III Arena source code.\n\nQuake III Arena source code is free software; you can redistribute it\nand/or modify it under the terms of the GNU General Public License as\npublished by the Free Software Foundation; either version 2 of the License,\nor (at your option) any later version.\n\nQuake III Arena source code is distributed in the hope that it will be\nuseful, but WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Quake III Arena source code; if not, write to the Free Software\nFoundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n===========================================================================\n*/\n\n/*****************************************************************************\n * name:\t\tbe_aas_routealt.c\n *\n * desc:\t\tAAS\n *\n * $Archive: /MissionPack/code/botlib/be_aas_routealt.c $\n *\n *****************************************************************************/\n\n#include \"q_shared.h\"\n#include \"l_utils.h\"\n#include \"l_memory.h\"\n#include \"l_log.h\"\n#include \"l_script.h\"\n#include \"l_precomp.h\"\n#include \"l_struct.h\"\n#include \"aasfile.h\"\n#include \"botlib.h\"\n#include \"be_aas.h\"\n#include \"be_aas_funcs.h\"\n#include \"be_interface.h\"\n#include \"be_aas_def.h\"\n\n#define ENABLE_ALTROUTING\n//#define ALTROUTE_DEBUG\n\ntypedef struct midrangearea_s\n{\n\tint valid;\n\tunsigned short starttime;\n\tunsigned short goaltime;\n} midrangearea_t;\n\nmidrangearea_t *midrangeareas;\nint *clusterareas;\nint numclusterareas;\n\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid AAS_AltRoutingFloodCluster_r(int areanum)\n{\n\tint i, otherareanum;\n\taas_area_t *area;\n\taas_face_t *face;\n\n\t//add the current area to the areas of the current cluster\n\tclusterareas[numclusterareas] = areanum;\n\tnumclusterareas++;\n\t//remove the area from the mid range areas\n\tmidrangeareas[areanum].valid = qfalse;\n\t//flood to other areas through the faces of this area\n\tarea = &aasworld.areas[areanum];\n\tfor (i = 0; i < area->numfaces; i++)\n\t{\n\t\tface = &aasworld.faces[abs(aasworld.faceindex[area->firstface + i])];\n\t\t//get the area at the other side of the face\n\t\tif (face->frontarea == areanum) otherareanum = face->backarea;\n\t\telse otherareanum = face->frontarea;\n\t\t//if there is an area at the other side of this face\n\t\tif (!otherareanum) continue;\n\t\t//if the other area is not a midrange area\n\t\tif (!midrangeareas[otherareanum].valid) continue;\n\t\t//\n\t\tAAS_AltRoutingFloodCluster_r(otherareanum);\n\t} //end for\n} //end of the function AAS_AltRoutingFloodCluster_r\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint AAS_AlternativeRouteGoals(vec3_t start, int startareanum, vec3_t goal, int goalareanum, int travelflags,\n\t\t\t\t\t\t\t\t\t\t aas_altroutegoal_t *altroutegoals, int maxaltroutegoals,\n\t\t\t\t\t\t\t\t\t\t int type)\n{\n#ifndef ENABLE_ALTROUTING\n\treturn 0;\n#else\n\tint i, j, bestareanum;\n\tint numaltroutegoals, nummidrangeareas;\n\tint starttime, goaltime, goaltraveltime;\n\tfloat dist, bestdist;\n\tvec3_t mid, dir;\n#ifdef ALTROUTE_DEBUG\n\tint startmillisecs;\n\n\tstartmillisecs = Sys_MilliSeconds();\n#endif\n\n\tif (!startareanum || !goalareanum)\n\t\treturn 0;\n\t//travel time towards the goal area\n\tgoaltraveltime = AAS_AreaTravelTimeToGoalArea(startareanum, start, goalareanum, travelflags);\n\t//clear the midrange areas\n\tCom_Memset(midrangeareas, 0, aasworld.numareas * sizeof(midrangearea_t));\n\tnumaltroutegoals = 0;\n\t//\n\tnummidrangeareas = 0;\n\t//\n\tfor (i = 1; i < aasworld.numareas; i++)\n\t{\n\t\t//\n\t\tif (!(type & ALTROUTEGOAL_ALL))\n\t\t{\n\t\t\tif (!(type & ALTROUTEGOAL_CLUSTERPORTALS && (aasworld.areasettings[i].contents & AREACONTENTS_CLUSTERPORTAL)))\n\t\t\t{\n\t\t\t\tif (!(type & ALTROUTEGOAL_VIEWPORTALS && (aasworld.areasettings[i].contents & AREACONTENTS_VIEWPORTAL)))\n\t\t\t\t{\n\t\t\t\t\tcontinue;\n\t\t\t\t} //end if\n\t\t\t} //end if\n\t\t} //end if\n\t\t//if the area has no reachabilities\n\t\tif (!AAS_AreaReachability(i)) continue;\n\t\t//tavel time from the area to the start area\n\t\tstarttime = AAS_AreaTravelTimeToGoalArea(startareanum, start, i, travelflags);\n\t\tif (!starttime) continue;\n\t\t//if the travel time from the start to the area is greater than the shortest goal travel time\n\t\tif (starttime > (float) 1.1 * goaltraveltime) continue;\n\t\t//travel time from the area to the goal area\n\t\tgoaltime = AAS_AreaTravelTimeToGoalArea(i, NULL, goalareanum, travelflags);\n\t\tif (!goaltime) continue;\n\t\t//if the travel time from the area to the goal is greater than the shortest goal travel time\n\t\tif (goaltime > (float) 0.8 * goaltraveltime) continue;\n\t\t//this is a mid range area\n\t\tmidrangeareas[i].valid = qtrue;\n\t\tmidrangeareas[i].starttime = starttime;\n\t\tmidrangeareas[i].goaltime = goaltime;\n\t\tLog_Write(\"%d midrange area %d\", nummidrangeareas, i);\n\t\tnummidrangeareas++;\n\t} //end for\n\t//\n\tfor (i = 1; i < aasworld.numareas; i++)\n\t{\n\t\tif (!midrangeareas[i].valid) continue;\n\t\t//get the areas in one cluster\n\t\tnumclusterareas = 0;\n\t\tAAS_AltRoutingFloodCluster_r(i);\n\t\t//now we've got a cluster with areas through which an alternative route could go\n\t\t//get the 'center' of the cluster\n\t\tVectorClear(mid);\n\t\tfor (j = 0; j < numclusterareas; j++)\n\t\t{\n\t\t\tVectorAdd(mid, aasworld.areas[clusterareas[j]].center, mid);\n\t\t} //end for\n\t\tVectorScale(mid, 1.0 / numclusterareas, mid);\n\t\t//get the area closest to the center of the cluster\n\t\tbestdist = 999999;\n\t\tbestareanum = 0;\n\t\tfor (j = 0; j < numclusterareas; j++)\n\t\t{\n\t\t\tVectorSubtract(mid, aasworld.areas[clusterareas[j]].center, dir);\n\t\t\tdist = VectorLength(dir);\n\t\t\tif (dist < bestdist)\n\t\t\t{\n\t\t\t\tbestdist = dist;\n\t\t\t\tbestareanum = clusterareas[j];\n\t\t\t} //end if\n\t\t} //end for\n\t\t//now we've got an area for an alternative route\n\t\t//FIXME: add alternative goal origin\n\t\tVectorCopy(aasworld.areas[bestareanum].center, altroutegoals[numaltroutegoals].origin);\n\t\taltroutegoals[numaltroutegoals].areanum = bestareanum;\n\t\taltroutegoals[numaltroutegoals].starttraveltime = midrangeareas[bestareanum].starttime;\n\t\taltroutegoals[numaltroutegoals].goaltraveltime = midrangeareas[bestareanum].goaltime;\n\t\taltroutegoals[numaltroutegoals].extratraveltime =\n\t\t\t\t\t(midrangeareas[bestareanum].starttime + midrangeareas[bestareanum].goaltime) -\n\t\t\t\t\t\t\t\tgoaltraveltime;\n\t\tnumaltroutegoals++;\n\t\t//\n#ifdef ALTROUTE_DEBUG\n\t\tAAS_ShowAreaPolygons(bestareanum, 1, qtrue);\n#endif\n\t\t//don't return more than the maximum alternative route goals\n\t\tif (numaltroutegoals >= maxaltroutegoals) break;\n\t} //end for\n#ifdef ALTROUTE_DEBUG\n\tbotimport.Print(PRT_MESSAGE, \"alternative route goals in %d msec\\n\", Sys_MilliSeconds() - startmillisecs);\n#endif\n\treturn numaltroutegoals;\n#endif\n} //end of the function AAS_AlternativeRouteGoals\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid AAS_InitAlternativeRouting(void)\n{\n#ifdef ENABLE_ALTROUTING\n\tif (midrangeareas) FreeMemory(midrangeareas);\n\tmidrangeareas = (midrangearea_t *) GetMemory(aasworld.numareas * sizeof(midrangearea_t));\n\tif (clusterareas) FreeMemory(clusterareas);\n\tclusterareas = (int *) GetMemory(aasworld.numareas * sizeof(int));\n#endif\n} //end of the function AAS_InitAlternativeRouting\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid AAS_ShutdownAlternativeRouting(void)\n{\n#ifdef ENABLE_ALTROUTING\n\tif (midrangeareas) FreeMemory(midrangeareas);\n\tmidrangeareas = NULL;\n\tif (clusterareas) FreeMemory(clusterareas);\n\tclusterareas = NULL;\n\tnumclusterareas = 0;\n#endif\n} //end of the function AAS_ShutdownAlternativeRouting\n"
  },
  {
    "path": "plugins/quake3/botlib/be_aas_routealt.h",
    "content": "/*\n===========================================================================\nCopyright (C) 1999-2005 Id Software, Inc.\n\nThis file is part of Quake III Arena source code.\n\nQuake III Arena source code is free software; you can redistribute it\nand/or modify it under the terms of the GNU General Public License as\npublished by the Free Software Foundation; either version 2 of the License,\nor (at your option) any later version.\n\nQuake III Arena source code is distributed in the hope that it will be\nuseful, but WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Quake III Arena source code; if not, write to the Free Software\nFoundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n===========================================================================\n*/\n\n/*****************************************************************************\n * name:\t\tbe_aas_routealt.h\n *\n * desc:\t\tAAS\n *\n * $Archive: /source/code/botlib/be_aas_routealt.h $\n *\n *****************************************************************************/\n\n#ifdef AASINTERN\nvoid AAS_InitAlternativeRouting(void);\nvoid AAS_ShutdownAlternativeRouting(void);\n#endif //AASINTERN\n\n\nint AAS_AlternativeRouteGoals(vec3_t start, int startareanum, vec3_t goal, int goalareanum, int travelflags,\n\t\t\t\t\t\t\t\t\t\taas_altroutegoal_t *altroutegoals, int maxaltroutegoals,\n\t\t\t\t\t\t\t\t\t\tint type);\n"
  },
  {
    "path": "plugins/quake3/botlib/be_aas_sample.c",
    "content": "/*\n===========================================================================\nCopyright (C) 1999-2005 Id Software, Inc.\n\nThis file is part of Quake III Arena source code.\n\nQuake III Arena source code is free software; you can redistribute it\nand/or modify it under the terms of the GNU General Public License as\npublished by the Free Software Foundation; either version 2 of the License,\nor (at your option) any later version.\n\nQuake III Arena source code is distributed in the hope that it will be\nuseful, but WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Quake III Arena source code; if not, write to the Free Software\nFoundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n===========================================================================\n*/\n\n/*****************************************************************************\n * name:\t\tbe_aas_sample.c\n *\n * desc:\t\tAAS environment sampling\n *\n * $Archive: /MissionPack/code/botlib/be_aas_sample.c $\n *\n *****************************************************************************/\n\n#include \"q_shared.h\"\n#include \"l_memory.h\"\n#include \"l_script.h\"\n#include \"l_precomp.h\"\n#include \"l_struct.h\"\n#ifndef BSPC\n#include \"l_libvar.h\"\n#endif\n#include \"aasfile.h\"\n#include \"botlib.h\"\n#include \"be_aas.h\"\n#include \"be_interface.h\"\n#include \"be_aas_funcs.h\"\n#include \"be_aas_def.h\"\n\n\n//#define AAS_SAMPLE_DEBUG\n\n#define BBOX_NORMAL_EPSILON\t\t0.001\n\n#define ON_EPSILON\t\t\t\t\t0 //0.0005\n\n#define TRACEPLANE_EPSILON\t\t\t0.125\n\ntypedef struct aas_tracestack_s\n{\n\tvec3_t start;\t\t//start point of the piece of line to trace\n\tvec3_t end;\t\t\t//end point of the piece of line to trace\n\tint planenum;\t\t//last plane used as splitter\n\tint nodenum;\t\t//node found after splitting with planenum\n} aas_tracestack_t;\n\nint numaaslinks;\n\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid AAS_PresenceTypeBoundingBox(int presencetype, vec3_t mins, vec3_t maxs)\n{\n\tint index;\n\t//bounding box size for each presence type\n\tvec3_t boxmins[3] = {{0, 0, 0}, {-15, -15, -24}, {-15, -15, -24}};\n\tvec3_t boxmaxs[3] = {{0, 0, 0}, { 15,  15,  32}, { 15,  15,   8}};\n\n\tif (presencetype == PRESENCE_NORMAL) index = 1;\n\telse if (presencetype == PRESENCE_CROUCH) index = 2;\n\telse\n\t{\n\t\tbotimport.Print(PRT_FATAL, \"AAS_PresenceTypeBoundingBox: unknown presence type\\n\");\n\t\tindex = 2;\n\t} //end if\n\tVectorCopy(boxmins[index], mins);\n\tVectorCopy(boxmaxs[index], maxs);\n} //end of the function AAS_PresenceTypeBoundingBox\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid AAS_InitAASLinkHeap(void)\n{\n\tint i, max_aaslinks;\n\n\tmax_aaslinks = aasworld.linkheapsize;\n\t//if there's no link heap present\n\tif (!aasworld.linkheap)\n\t{\n#ifdef BSPC\n\t\tmax_aaslinks = 6144;\n#else\n\t\tmax_aaslinks = (int) LibVarValue(\"max_aaslinks\", \"6144\");\n#endif\n\t\tif (max_aaslinks < 0) max_aaslinks = 0;\n\t\taasworld.linkheapsize = max_aaslinks;\n\t\taasworld.linkheap = (aas_link_t *) GetHunkMemory(max_aaslinks * sizeof(aas_link_t));\n\t} //end if\n\t//link the links on the heap\n\taasworld.linkheap[0].prev_ent = NULL;\n\taasworld.linkheap[0].next_ent = &aasworld.linkheap[1];\n\tfor (i = 1; i < max_aaslinks-1; i++)\n\t{\n\t\taasworld.linkheap[i].prev_ent = &aasworld.linkheap[i - 1];\n\t\taasworld.linkheap[i].next_ent = &aasworld.linkheap[i + 1];\n\t} //end for\n\taasworld.linkheap[max_aaslinks-1].prev_ent = &aasworld.linkheap[max_aaslinks-2];\n\taasworld.linkheap[max_aaslinks-1].next_ent = NULL;\n\t//pointer to the first free link\n\taasworld.freelinks = &aasworld.linkheap[0];\n\t//\n\tnumaaslinks = max_aaslinks;\n} //end of the function AAS_InitAASLinkHeap\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid AAS_FreeAASLinkHeap(void)\n{\n\tif (aasworld.linkheap) FreeMemory(aasworld.linkheap);\n\taasworld.linkheap = NULL;\n\taasworld.linkheapsize = 0;\n} //end of the function AAS_FreeAASLinkHeap\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\naas_link_t *AAS_AllocAASLink(void)\n{\n\taas_link_t *link;\n\n\tlink = aasworld.freelinks;\n\tif (!link)\n\t{\n#ifndef BSPC\n\t\tif (botDeveloper)\n#endif\n\t\t{\n\t\t\tbotimport.Print(PRT_FATAL, \"empty aas link heap\\n\");\n\t\t} //end if\n\t\treturn NULL;\n\t} //end if\n\tif (aasworld.freelinks) aasworld.freelinks = aasworld.freelinks->next_ent;\n\tif (aasworld.freelinks) aasworld.freelinks->prev_ent = NULL;\n\tnumaaslinks--;\n\treturn link;\n} //end of the function AAS_AllocAASLink\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid AAS_DeAllocAASLink(aas_link_t *link)\n{\n\tif (aasworld.freelinks) aasworld.freelinks->prev_ent = link;\n\tlink->prev_ent = NULL;\n\tlink->next_ent = aasworld.freelinks;\n\tlink->prev_area = NULL;\n\tlink->next_area = NULL;\n\taasworld.freelinks = link;\n\tnumaaslinks++;\n} //end of the function AAS_DeAllocAASLink\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid AAS_InitAASLinkedEntities(void)\n{\n\tif (!aasworld.loaded) return;\n\tif (aasworld.arealinkedentities) FreeMemory(aasworld.arealinkedentities);\n\taasworld.arealinkedentities = (aas_link_t **) GetClearedHunkMemory(\n\t\t\t\t\t\taasworld.numareas * sizeof(aas_link_t *));\n} //end of the function AAS_InitAASLinkedEntities\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid AAS_FreeAASLinkedEntities(void)\n{\n\tif (aasworld.arealinkedentities) FreeMemory(aasworld.arealinkedentities);\n\taasworld.arealinkedentities = NULL;\n} //end of the function AAS_InitAASLinkedEntities\n//===========================================================================\n// returns the AAS area the point is in\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint AAS_PointAreaNum(vec3_t point)\n{\n\tint nodenum;\n\tvec_t\tdist;\n\taas_node_t *node;\n\taas_plane_t *plane;\n\n\tif (!aasworld.loaded)\n\t{\n\t\tbotimport.Print(PRT_ERROR, \"AAS_PointAreaNum: aas not loaded\\n\");\n\t\treturn 0;\n\t} //end if\n\n\t//start with node 1 because node zero is a dummy used for solid leafs\n\tnodenum = 1;\n\twhile (nodenum > 0)\n\t{\n//\t\tbotimport.Print(PRT_MESSAGE, \"[%d]\", nodenum);\n#ifdef AAS_SAMPLE_DEBUG\n\t\tif (nodenum >= aasworld.numnodes)\n\t\t{\n\t\t\tbotimport.Print(PRT_ERROR, \"nodenum = %d >= aasworld.numnodes = %d\\n\", nodenum, aasworld.numnodes);\n\t\t\treturn 0;\n\t\t} //end if\n#endif //AAS_SAMPLE_DEBUG\n\t\tnode = &aasworld.nodes[nodenum];\n#ifdef AAS_SAMPLE_DEBUG\n\t\tif (node->planenum < 0 || node->planenum >= aasworld.numplanes)\n\t\t{\n\t\t\tbotimport.Print(PRT_ERROR, \"node->planenum = %d >= aasworld.numplanes = %d\\n\", node->planenum, aasworld.numplanes);\n\t\t\treturn 0;\n\t\t} //end if\n#endif //AAS_SAMPLE_DEBUG\n\t\tplane = &aasworld.planes[node->planenum];\n\t\tdist = DotProduct(point, plane->normal) - plane->dist;\n\t\tif (dist > 0) nodenum = node->children[0];\n\t\telse nodenum = node->children[1];\n\t} //end while\n\tif (!nodenum)\n\t{\n#ifdef AAS_SAMPLE_DEBUG\n\t\tbotimport.Print(PRT_MESSAGE, \"in solid\\n\");\n#endif //AAS_SAMPLE_DEBUG\n\t\treturn 0;\n\t} //end if\n\treturn -nodenum;\n} //end of the function AAS_PointAreaNum\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint AAS_PointReachabilityAreaIndex( vec3_t origin )\n{\n\tint areanum, cluster, i, index;\n\n\tif (!aasworld.initialized)\n\t\treturn 0;\n\n\tif ( !origin )\n\t{\n\t\tindex = 0;\n\t\tfor (i = 0; i < aasworld.numclusters; i++)\n\t\t{\n\t\t\tindex += aasworld.clusters[i].numreachabilityareas;\n\t\t} //end for\n\t\treturn index;\n\t} //end if\n\n\tareanum = AAS_PointAreaNum( origin );\n\tif ( !areanum || !AAS_AreaReachability(areanum) )\n\t\treturn 0;\n\tcluster = aasworld.areasettings[areanum].cluster;\n\tareanum = aasworld.areasettings[areanum].clusterareanum;\n\tif (cluster < 0)\n\t{\n\t\tcluster = aasworld.portals[-cluster].frontcluster;\n\t\tareanum = aasworld.portals[-cluster].clusterareanum[0];\n\t} //end if\n\n\tindex = 0;\n\tfor (i = 0; i < cluster; i++)\n\t{\n\t\tindex += aasworld.clusters[i].numreachabilityareas;\n\t} //end for\n\tindex += areanum;\n\treturn index;\n} //end of the function AAS_PointReachabilityAreaIndex\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint AAS_AreaCluster(int areanum)\n{\n\tif (areanum <= 0 || areanum >= aasworld.numareas)\n\t{\n\t\tbotimport.Print(PRT_ERROR, \"AAS_AreaCluster: invalid area number\\n\");\n\t\treturn 0;\n\t} //end if\n\treturn aasworld.areasettings[areanum].cluster;\n} //end of the function AAS_AreaCluster\n//===========================================================================\n// returns the presence types of the given area\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint AAS_AreaPresenceType(int areanum)\n{\n\tif (!aasworld.loaded) return 0;\n\tif (areanum <= 0 || areanum >= aasworld.numareas)\n\t{\n\t\tbotimport.Print(PRT_ERROR, \"AAS_AreaPresenceType: invalid area number\\n\");\n\t\treturn 0;\n\t} //end if\n\treturn aasworld.areasettings[areanum].presencetype;\n} //end of the function AAS_AreaPresenceType\n//===========================================================================\n// returns the presence type at the given point\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint AAS_PointPresenceType(vec3_t point)\n{\n\tint areanum;\n\n\tif (!aasworld.loaded) return 0;\n\n\tareanum = AAS_PointAreaNum(point);\n\tif (!areanum) return PRESENCE_NONE;\n\treturn aasworld.areasettings[areanum].presencetype;\n} //end of the function AAS_PointPresenceType\n//===========================================================================\n// calculates the minimum distance between the origin of the box and the\n// given plane when both will collide on the given side of the plane\n//\n// normal\t=\tnormal vector of plane to calculate distance from\n// mins\t\t=\tminimums of box relative to origin\n// maxs\t\t=\tmaximums of box relative to origin\n// side\t\t=\tside of the plane we want to calculate the distance from\n//\t\t\t\t\t0 normal vector side\n//\t\t\t\t\t1 not normal vector side\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvec_t AAS_BoxOriginDistanceFromPlane(vec3_t normal, vec3_t mins, vec3_t maxs, int side)\n{\n\tvec3_t v1, v2;\n\tint i;\n\n\t//swap maxs and mins when on the other side of the plane\n\tif (side)\n\t{\n\t\t//get a point of the box that would be one of the first\n\t\t//to collide with the plane\n\t\tfor (i = 0; i < 3; i++)\n\t\t{\n\t\t\tif (normal[i] > BBOX_NORMAL_EPSILON) v1[i] = maxs[i];\n\t\t\telse if (normal[i] < -BBOX_NORMAL_EPSILON) v1[i] = mins[i];\n\t\t\telse v1[i] = 0;\n\t\t} //end for\n\t} //end if\n\telse\n\t{\n\t\t//get a point of the box that would be one of the first\n\t\t//to collide with the plane\n\t\tfor (i = 0; i < 3; i++)\n\t\t{\n\t\t\tif (normal[i] > BBOX_NORMAL_EPSILON) v1[i] = mins[i];\n\t\t\telse if (normal[i] < -BBOX_NORMAL_EPSILON) v1[i] = maxs[i];\n\t\t\telse v1[i] = 0;\n\t\t} //end for\n\t} //end else\n\t//\n\tVectorCopy(normal, v2);\n\tVectorInverse(v2);\n//\tVectorNegate(normal, v2);\n\treturn DotProduct(v1, v2);\n} //end of the function AAS_BoxOriginDistanceFromPlane\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nqboolean AAS_AreaEntityCollision(int areanum, vec3_t start, vec3_t end,\n\t\t\t\t\t\t\t\t\t\tint presencetype, int passent, aas_trace_t *trace)\n{\n\tint collision;\n\tvec3_t boxmins, boxmaxs;\n\taas_link_t *link;\n\tbsp_trace_t bsptrace;\n\n\tAAS_PresenceTypeBoundingBox(presencetype, boxmins, boxmaxs);\n\n\tCom_Memset(&bsptrace, 0, sizeof(bsp_trace_t)); //make compiler happy\n\t//assume no collision\n\tbsptrace.fraction = 1;\n\tcollision = qfalse;\n\tfor (link = aasworld.arealinkedentities[areanum]; link; link = link->next_ent)\n\t{\n\t\t//ignore the pass entity\n\t\tif (link->entnum == passent) continue;\n\t\t//\n\t\tif (AAS_EntityCollision(link->entnum, start, boxmins, boxmaxs, end,\n\t\t\t\t\t\t\t\t\t\t\t\tCONTENTS_SOLID|CONTENTS_PLAYERCLIP, &bsptrace))\n\t\t{\n\t\t\tcollision = qtrue;\n\t\t} //end if\n\t} //end for\n\tif (collision)\n\t{\n\t\ttrace->startsolid = bsptrace.startsolid;\n\t\ttrace->ent = bsptrace.ent;\n\t\tVectorCopy(bsptrace.endpos, trace->endpos);\n\t\ttrace->area = 0;\n\t\ttrace->planenum = 0;\n\t\treturn qtrue;\n\t} //end if\n\treturn qfalse;\n} //end of the function AAS_AreaEntityCollision\n//===========================================================================\n// recursive subdivision of the line by the BSP tree.\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\naas_trace_t AAS_TraceClientBBox(vec3_t start, vec3_t end, int presencetype,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tint passent)\n{\n\tint side, nodenum, tmpplanenum;\n\tfloat front, back, frac;\n\tvec3_t cur_start, cur_end, cur_mid, v1, v2;\n\taas_tracestack_t tracestack[127];\n\taas_tracestack_t *tstack_p;\n\taas_node_t *aasnode;\n\taas_plane_t *plane;\n\taas_trace_t trace;\n\n\t//clear the trace structure\n\tCom_Memset(&trace, 0, sizeof(aas_trace_t));\n\n\tif (!aasworld.loaded) return trace;\n\t\n\ttstack_p = tracestack;\n\t//we start with the whole line on the stack\n\tVectorCopy(start, tstack_p->start);\n\tVectorCopy(end, tstack_p->end);\n\ttstack_p->planenum = 0;\n\t//start with node 1 because node zero is a dummy for a solid leaf\n\ttstack_p->nodenum = 1;\t\t//starting at the root of the tree\n\ttstack_p++;\n\t\n\twhile (1)\n\t{\n\t\t//pop up the stack\n\t\ttstack_p--;\n\t\t//if the trace stack is empty (ended up with a piece of the\n\t\t//line to be traced in an area)\n\t\tif (tstack_p < tracestack)\n\t\t{\n\t\t\ttstack_p++;\n\t\t\t//nothing was hit\n\t\t\ttrace.startsolid = qfalse;\n\t\t\ttrace.fraction = 1.0;\n\t\t\t//endpos is the end of the line\n\t\t\tVectorCopy(end, trace.endpos);\n\t\t\t//nothing hit\n\t\t\ttrace.ent = 0;\n\t\t\ttrace.area = 0;\n\t\t\ttrace.planenum = 0;\n\t\t\treturn trace;\n\t\t} //end if\n\t\t//number of the current node to test the line against\n\t\tnodenum = tstack_p->nodenum;\n\t\t//if it is an area\n\t\tif (nodenum < 0)\n\t\t{\n#ifdef AAS_SAMPLE_DEBUG\n\t\t\tif (-nodenum > aasworld.numareasettings)\n\t\t\t{\n\t\t\t\tbotimport.Print(PRT_ERROR, \"AAS_TraceBoundingBox: -nodenum out of range\\n\");\n\t\t\t\treturn trace;\n\t\t\t} //end if\n#endif //AAS_SAMPLE_DEBUG\n\t\t\t//botimport.Print(PRT_MESSAGE, \"areanum = %d, must be %d\\n\", -nodenum, AAS_PointAreaNum(start));\n\t\t\t//if can't enter the area because it hasn't got the right presence type\n\t\t\tif (!(aasworld.areasettings[-nodenum].presencetype & presencetype))\n\t\t\t{\n\t\t\t\t//if the start point is still the initial start point\n\t\t\t\t//NOTE: no need for epsilons because the points will be\n\t\t\t\t//exactly the same when they're both the start point\n\t\t\t\tif (tstack_p->start[0] == start[0] &&\n\t\t\t\t\t\ttstack_p->start[1] == start[1] &&\n\t\t\t\t\t\ttstack_p->start[2] == start[2])\n\t\t\t\t{\n\t\t\t\t\ttrace.startsolid = qtrue;\n\t\t\t\t\ttrace.fraction = 0.0;\n\t\t\t\t\tVectorClear(v1);\n\t\t\t\t} //end if\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\ttrace.startsolid = qfalse;\n\t\t\t\t\tVectorSubtract(end, start, v1);\n\t\t\t\t\tVectorSubtract(tstack_p->start, start, v2);\n\t\t\t\t\ttrace.fraction = VectorLength(v2) / VectorNormalize(v1);\n\t\t\t\t\tVectorMA(tstack_p->start, -0.125, v1, tstack_p->start);\n\t\t\t\t} //end else\n\t\t\t\tVectorCopy(tstack_p->start, trace.endpos);\n\t\t\t\ttrace.ent = 0;\n\t\t\t\ttrace.area = -nodenum;\n//\t\t\t\tVectorSubtract(end, start, v1);\n\t\t\t\ttrace.planenum = tstack_p->planenum;\n\t\t\t\t//always take the plane with normal facing towards the trace start\n\t\t\t\tplane = &aasworld.planes[trace.planenum];\n\t\t\t\tif (DotProduct(v1, plane->normal) > 0) trace.planenum ^= 1;\n\t\t\t\treturn trace;\n\t\t\t} //end if\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (passent >= 0)\n\t\t\t\t{\n\t\t\t\t\tif (AAS_AreaEntityCollision(-nodenum, tstack_p->start,\n\t\t\t\t\t\t\t\t\t\t\t\t\ttstack_p->end, presencetype, passent,\n\t\t\t\t\t\t\t\t\t\t\t\t\t&trace))\n\t\t\t\t\t{\n\t\t\t\t\t\tif (!trace.startsolid)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tVectorSubtract(end, start, v1);\n\t\t\t\t\t\t\tVectorSubtract(trace.endpos, start, v2);\n\t\t\t\t\t\t\ttrace.fraction = VectorLength(v2) / VectorLength(v1);\n\t\t\t\t\t\t} //end if\n\t\t\t\t\t\treturn trace;\n\t\t\t\t\t} //end if\n\t\t\t\t} //end if\n\t\t\t} //end else\n\t\t\ttrace.lastarea = -nodenum;\n\t\t\tcontinue;\n\t\t} //end if\n\t\t//if it is a solid leaf\n\t\tif (!nodenum)\n\t\t{\n\t\t\t//if the start point is still the initial start point\n\t\t\t//NOTE: no need for epsilons because the points will be\n\t\t\t//exactly the same when they're both the start point\n\t\t\tif (tstack_p->start[0] == start[0] &&\n\t\t\t\t\ttstack_p->start[1] == start[1] &&\n\t\t\t\t\ttstack_p->start[2] == start[2])\n\t\t\t{\n\t\t\t\ttrace.startsolid = qtrue;\n\t\t\t\ttrace.fraction = 0.0;\n\t\t\t\tVectorClear(v1);\n\t\t\t} //end if\n\t\t\telse\n\t\t\t{\n\t\t\t\ttrace.startsolid = qfalse;\n\t\t\t\tVectorSubtract(end, start, v1);\n\t\t\t\tVectorSubtract(tstack_p->start, start, v2);\n\t\t\t\ttrace.fraction = VectorLength(v2) / VectorNormalize(v1);\n\t\t\t\tVectorMA(tstack_p->start, -0.125, v1, tstack_p->start);\n\t\t\t} //end else\n\t\t\tVectorCopy(tstack_p->start, trace.endpos);\n\t\t\ttrace.ent = 0;\n\t\t\ttrace.area = 0;\t//hit solid leaf\n//\t\t\tVectorSubtract(end, start, v1);\n\t\t\ttrace.planenum = tstack_p->planenum;\n\t\t\t//always take the plane with normal facing towards the trace start\n\t\t\tplane = &aasworld.planes[trace.planenum];\n\t\t\tif (DotProduct(v1, plane->normal) > 0) trace.planenum ^= 1;\n\t\t\treturn trace;\n\t\t} //end if\n#ifdef AAS_SAMPLE_DEBUG\n\t\tif (nodenum > aasworld.numnodes)\n\t\t{\n\t\t\tbotimport.Print(PRT_ERROR, \"AAS_TraceBoundingBox: nodenum out of range\\n\");\n\t\t\treturn trace;\n\t\t} //end if\n#endif //AAS_SAMPLE_DEBUG\n\t\t//the node to test against\n\t\taasnode = &aasworld.nodes[nodenum];\n\t\t//start point of current line to test against node\n\t\tVectorCopy(tstack_p->start, cur_start);\n\t\t//end point of the current line to test against node\n\t\tVectorCopy(tstack_p->end, cur_end);\n\t\t//the current node plane\n\t\tplane = &aasworld.planes[aasnode->planenum];\n\n\t\tswitch(plane->type)\n\t\t{/*FIXME: wtf doesn't this work? obviously the axial node planes aren't always facing positive!!!\n\t\t\t//check for axial planes\n\t\t\tcase PLANE_X:\n\t\t\t{\n\t\t\t\tfront = cur_start[0] - plane->dist;\n\t\t\t\tback = cur_end[0] - plane->dist;\n\t\t\t\tbreak;\n\t\t\t} //end case\n\t\t\tcase PLANE_Y:\n\t\t\t{\n\t\t\t\tfront = cur_start[1] - plane->dist;\n\t\t\t\tback = cur_end[1] - plane->dist;\n\t\t\t\tbreak;\n\t\t\t} //end case\n\t\t\tcase PLANE_Z:\n\t\t\t{\n\t\t\t\tfront = cur_start[2] - plane->dist;\n\t\t\t\tback = cur_end[2] - plane->dist;\n\t\t\t\tbreak;\n\t\t\t} //end case*/\n\t\t\tdefault: //gee it's not an axial plane\n\t\t\t{\n\t\t\t\tfront = DotProduct(cur_start, plane->normal) - plane->dist;\n\t\t\t\tback = DotProduct(cur_end, plane->normal) - plane->dist;\n\t\t\t\tbreak;\n\t\t\t} //end default\n\t\t} //end switch\n\t\t// bk010221 - old location of FPE hack and divide by zero expression\n\t\t//if the whole to be traced line is totally at the front of this node\n\t\t//only go down the tree with the front child\n\t\tif ((front >= -ON_EPSILON && back >= -ON_EPSILON))\n\t\t{\n\t\t\t//keep the current start and end point on the stack\n\t\t\t//and go down the tree with the front child\n\t\t\ttstack_p->nodenum = aasnode->children[0];\n\t\t\ttstack_p++;\n\t\t\tif (tstack_p >= &tracestack[127])\n\t\t\t{\n\t\t\t\tbotimport.Print(PRT_ERROR, \"AAS_TraceBoundingBox: stack overflow\\n\");\n\t\t\t\treturn trace;\n\t\t\t} //end if\n\t\t} //end if\n\t\t//if the whole to be traced line is totally at the back of this node\n\t\t//only go down the tree with the back child\n\t\telse if ((front < ON_EPSILON && back < ON_EPSILON))\n\t\t{\n\t\t\t//keep the current start and end point on the stack\n\t\t\t//and go down the tree with the back child\n\t\t\ttstack_p->nodenum = aasnode->children[1];\n\t\t\ttstack_p++;\n\t\t\tif (tstack_p >= &tracestack[127])\n\t\t\t{\n\t\t\t\tbotimport.Print(PRT_ERROR, \"AAS_TraceBoundingBox: stack overflow\\n\");\n\t\t\t\treturn trace;\n\t\t\t} //end if\n\t\t} //end if\n\t\t//go down the tree both at the front and back of the node\n\t\telse\n\t\t{\n\t\t\ttmpplanenum = tstack_p->planenum;\n\t\t\t// bk010221 - new location of divide by zero (see above)\n\t\t\tif ( front == back ) front -= 0.001f; // bk0101022 - hack/FPE \n                \t//calculate the hitpoint with the node (split point of the line)\n\t\t\t//put the crosspoint TRACEPLANE_EPSILON pixels on the near side\n\t\t\tif (front < 0) frac = (front + TRACEPLANE_EPSILON)/(front-back);\n\t\t\telse frac = (front - TRACEPLANE_EPSILON)/(front-back); // bk010221\n\t\t\t//\n\t\t\tif (frac < 0)\n\t\t\t\tfrac = 0.001f; //0\n\t\t\telse if (frac > 1)\n\t\t\t\tfrac = 0.999f; //1\n\t\t\t//frac = front / (front-back);\n\t\t\t//\n\t\t\tcur_mid[0] = cur_start[0] + (cur_end[0] - cur_start[0]) * frac;\n\t\t\tcur_mid[1] = cur_start[1] + (cur_end[1] - cur_start[1]) * frac;\n\t\t\tcur_mid[2] = cur_start[2] + (cur_end[2] - cur_start[2]) * frac;\n\n//\t\t\tAAS_DrawPlaneCross(cur_mid, plane->normal, plane->dist, plane->type, LINECOLOR_RED);\n\t\t\t//side the front part of the line is on\n\t\t\tside = front < 0;\n\t\t\t//first put the end part of the line on the stack (back side)\n\t\t\tVectorCopy(cur_mid, tstack_p->start);\n\t\t\t//not necesary to store because still on stack\n\t\t\t//VectorCopy(cur_end, tstack_p->end);\n\t\t\ttstack_p->planenum = aasnode->planenum;\n\t\t\ttstack_p->nodenum = aasnode->children[!side];\n\t\t\ttstack_p++;\n\t\t\tif (tstack_p >= &tracestack[127])\n\t\t\t{\n\t\t\t\tbotimport.Print(PRT_ERROR, \"AAS_TraceBoundingBox: stack overflow\\n\");\n\t\t\t\treturn trace;\n\t\t\t} //end if\n\t\t\t//now put the part near the start of the line on the stack so we will\n\t\t\t//continue with thats part first. This way we'll find the first\n\t\t\t//hit of the bbox\n\t\t\tVectorCopy(cur_start, tstack_p->start);\n\t\t\tVectorCopy(cur_mid, tstack_p->end);\n\t\t\ttstack_p->planenum = tmpplanenum;\n\t\t\ttstack_p->nodenum = aasnode->children[side];\n\t\t\ttstack_p++;\n\t\t\tif (tstack_p >= &tracestack[127])\n\t\t\t{\n\t\t\t\tbotimport.Print(PRT_ERROR, \"AAS_TraceBoundingBox: stack overflow\\n\");\n\t\t\t\treturn trace;\n\t\t\t} //end if\n\t\t} //end else\n\t} //end while\n//\treturn trace;\n} //end of the function AAS_TraceClientBBox\n//===========================================================================\n// recursive subdivision of the line by the BSP tree.\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint AAS_TraceAreas(vec3_t start, vec3_t end, int *areas, vec3_t *points, int maxareas)\n{\n\tint side, nodenum, tmpplanenum;\n\tint numareas;\n\tfloat front, back, frac;\n\tvec3_t cur_start, cur_end, cur_mid;\n\taas_tracestack_t tracestack[127];\n\taas_tracestack_t *tstack_p;\n\taas_node_t *aasnode;\n\taas_plane_t *plane;\n\n\tnumareas = 0;\n\tareas[0] = 0;\n\tif (!aasworld.loaded) return numareas;\n\n\ttstack_p = tracestack;\n\t//we start with the whole line on the stack\n\tVectorCopy(start, tstack_p->start);\n\tVectorCopy(end, tstack_p->end);\n\ttstack_p->planenum = 0;\n\t//start with node 1 because node zero is a dummy for a solid leaf\n\ttstack_p->nodenum = 1;\t\t//starting at the root of the tree\n\ttstack_p++;\n\n\twhile (1)\n\t{\n\t\t//pop up the stack\n\t\ttstack_p--;\n\t\t//if the trace stack is empty (ended up with a piece of the\n\t\t//line to be traced in an area)\n\t\tif (tstack_p < tracestack)\n\t\t{\n\t\t\treturn numareas;\n\t\t} //end if\n\t\t//number of the current node to test the line against\n\t\tnodenum = tstack_p->nodenum;\n\t\t//if it is an area\n\t\tif (nodenum < 0)\n\t\t{\n#ifdef AAS_SAMPLE_DEBUG\n\t\t\tif (-nodenum > aasworld.numareasettings)\n\t\t\t{\n\t\t\t\tbotimport.Print(PRT_ERROR, \"AAS_TraceAreas: -nodenum = %d out of range\\n\", -nodenum);\n\t\t\t\treturn numareas;\n\t\t\t} //end if\n#endif //AAS_SAMPLE_DEBUG\n\t\t\t//botimport.Print(PRT_MESSAGE, \"areanum = %d, must be %d\\n\", -nodenum, AAS_PointAreaNum(start));\n\t\t\tareas[numareas] = -nodenum;\n\t\t\tif (points) VectorCopy(tstack_p->start, points[numareas]);\n\t\t\tnumareas++;\n\t\t\tif (numareas >= maxareas) return numareas;\n\t\t\tcontinue;\n\t\t} //end if\n\t\t//if it is a solid leaf\n\t\tif (!nodenum)\n\t\t{\n\t\t\tcontinue;\n\t\t} //end if\n#ifdef AAS_SAMPLE_DEBUG\n\t\tif (nodenum > aasworld.numnodes)\n\t\t{\n\t\t\tbotimport.Print(PRT_ERROR, \"AAS_TraceAreas: nodenum out of range\\n\");\n\t\t\treturn numareas;\n\t\t} //end if\n#endif //AAS_SAMPLE_DEBUG\n\t\t//the node to test against\n\t\taasnode = &aasworld.nodes[nodenum];\n\t\t//start point of current line to test against node\n\t\tVectorCopy(tstack_p->start, cur_start);\n\t\t//end point of the current line to test against node\n\t\tVectorCopy(tstack_p->end, cur_end);\n\t\t//the current node plane\n\t\tplane = &aasworld.planes[aasnode->planenum];\n\n\t\tswitch(plane->type)\n\t\t{/*FIXME: wtf doesn't this work? obviously the node planes aren't always facing positive!!!\n\t\t\t//check for axial planes\n\t\t\tcase PLANE_X:\n\t\t\t{\n\t\t\t\tfront = cur_start[0] - plane->dist;\n\t\t\t\tback = cur_end[0] - plane->dist;\n\t\t\t\tbreak;\n\t\t\t} //end case\n\t\t\tcase PLANE_Y:\n\t\t\t{\n\t\t\t\tfront = cur_start[1] - plane->dist;\n\t\t\t\tback = cur_end[1] - plane->dist;\n\t\t\t\tbreak;\n\t\t\t} //end case\n\t\t\tcase PLANE_Z:\n\t\t\t{\n\t\t\t\tfront = cur_start[2] - plane->dist;\n\t\t\t\tback = cur_end[2] - plane->dist;\n\t\t\t\tbreak;\n\t\t\t} //end case*/\n\t\t\tdefault: //gee it's not an axial plane\n\t\t\t{\n\t\t\t\tfront = DotProduct(cur_start, plane->normal) - plane->dist;\n\t\t\t\tback = DotProduct(cur_end, plane->normal) - plane->dist;\n\t\t\t\tbreak;\n\t\t\t} //end default\n\t\t} //end switch\n\n\t\t//if the whole to be traced line is totally at the front of this node\n\t\t//only go down the tree with the front child\n\t\tif (front > 0 && back > 0)\n\t\t{\n\t\t\t//keep the current start and end point on the stack\n\t\t\t//and go down the tree with the front child\n\t\t\ttstack_p->nodenum = aasnode->children[0];\n\t\t\ttstack_p++;\n\t\t\tif (tstack_p >= &tracestack[127])\n\t\t\t{\n\t\t\t\tbotimport.Print(PRT_ERROR, \"AAS_TraceAreas: stack overflow\\n\");\n\t\t\t\treturn numareas;\n\t\t\t} //end if\n\t\t} //end if\n\t\t//if the whole to be traced line is totally at the back of this node\n\t\t//only go down the tree with the back child\n\t\telse if (front <= 0 && back <= 0)\n\t\t{\n\t\t\t//keep the current start and end point on the stack\n\t\t\t//and go down the tree with the back child\n\t\t\ttstack_p->nodenum = aasnode->children[1];\n\t\t\ttstack_p++;\n\t\t\tif (tstack_p >= &tracestack[127])\n\t\t\t{\n\t\t\t\tbotimport.Print(PRT_ERROR, \"AAS_TraceAreas: stack overflow\\n\");\n\t\t\t\treturn numareas;\n\t\t\t} //end if\n\t\t} //end if\n\t\t//go down the tree both at the front and back of the node\n\t\telse\n\t\t{\n\t\t\ttmpplanenum = tstack_p->planenum;\n\t\t\t//calculate the hitpoint with the node (split point of the line)\n\t\t\t//put the crosspoint TRACEPLANE_EPSILON pixels on the near side\n\t\t\tif (front < 0) frac = (front)/(front-back);\n\t\t\telse frac = (front)/(front-back);\n\t\t\tif (frac < 0) frac = 0;\n\t\t\telse if (frac > 1) frac = 1;\n\t\t\t//frac = front / (front-back);\n\t\t\t//\n\t\t\tcur_mid[0] = cur_start[0] + (cur_end[0] - cur_start[0]) * frac;\n\t\t\tcur_mid[1] = cur_start[1] + (cur_end[1] - cur_start[1]) * frac;\n\t\t\tcur_mid[2] = cur_start[2] + (cur_end[2] - cur_start[2]) * frac;\n\n//\t\t\tAAS_DrawPlaneCross(cur_mid, plane->normal, plane->dist, plane->type, LINECOLOR_RED);\n\t\t\t//side the front part of the line is on\n\t\t\tside = front < 0;\n\t\t\t//first put the end part of the line on the stack (back side)\n\t\t\tVectorCopy(cur_mid, tstack_p->start);\n\t\t\t//not necesary to store because still on stack\n\t\t\t//VectorCopy(cur_end, tstack_p->end);\n\t\t\ttstack_p->planenum = aasnode->planenum;\n\t\t\ttstack_p->nodenum = aasnode->children[!side];\n\t\t\ttstack_p++;\n\t\t\tif (tstack_p >= &tracestack[127])\n\t\t\t{\n\t\t\t\tbotimport.Print(PRT_ERROR, \"AAS_TraceAreas: stack overflow\\n\");\n\t\t\t\treturn numareas;\n\t\t\t} //end if\n\t\t\t//now put the part near the start of the line on the stack so we will\n\t\t\t//continue with thats part first. This way we'll find the first\n\t\t\t//hit of the bbox\n\t\t\tVectorCopy(cur_start, tstack_p->start);\n\t\t\tVectorCopy(cur_mid, tstack_p->end);\n\t\t\ttstack_p->planenum = tmpplanenum;\n\t\t\ttstack_p->nodenum = aasnode->children[side];\n\t\t\ttstack_p++;\n\t\t\tif (tstack_p >= &tracestack[127])\n\t\t\t{\n\t\t\t\tbotimport.Print(PRT_ERROR, \"AAS_TraceAreas: stack overflow\\n\");\n\t\t\t\treturn numareas;\n\t\t\t} //end if\n\t\t} //end else\n\t} //end while\n//\treturn numareas;\n} //end of the function AAS_TraceAreas\n//===========================================================================\n// a simple cross product\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\n// void AAS_OrthogonalToVectors(vec3_t v1, vec3_t v2, vec3_t res)\n#define AAS_OrthogonalToVectors(v1, v2, res) \\\n\t(res)[0] = ((v1)[1] * (v2)[2]) - ((v1)[2] * (v2)[1]);\\\n\t(res)[1] = ((v1)[2] * (v2)[0]) - ((v1)[0] * (v2)[2]);\\\n\t(res)[2] = ((v1)[0] * (v2)[1]) - ((v1)[1] * (v2)[0]);\n//===========================================================================\n// tests if the given point is within the face boundaries\n//\n// Parameter:\t\t\t\tface\t\t: face to test if the point is in it\n//\t\t\t\t\t\t\t\tpnormal\t: normal of the plane to use for the face\n//\t\t\t\t\t\t\t\tpoint\t\t: point to test if inside face boundaries\n// Returns:\t\t\t\t\tqtrue if the point is within the face boundaries\n// Changes Globals:\t\t-\n//===========================================================================\nqboolean AAS_InsideFace(aas_face_t *face, vec3_t pnormal, vec3_t point, float epsilon)\n{\n\tint i, firstvertex, edgenum;\n\tvec3_t v0;\n\tvec3_t edgevec, pointvec, sepnormal;\n\taas_edge_t *edge;\n#ifdef AAS_SAMPLE_DEBUG\n\tint lastvertex = 0;\n#endif //AAS_SAMPLE_DEBUG\n\n\tif (!aasworld.loaded) return qfalse;\n\n\tfor (i = 0; i < face->numedges; i++)\n\t{\n\t\tedgenum = aasworld.edgeindex[face->firstedge + i];\n\t\tedge = &aasworld.edges[abs(edgenum)];\n\t\t//get the first vertex of the edge\n\t\tfirstvertex = edgenum < 0;\n\t\tVectorCopy(aasworld.vertexes[edge->v[firstvertex]], v0);\n\t\t//edge vector\n\t\tVectorSubtract(aasworld.vertexes[edge->v[!firstvertex]], v0, edgevec);\n\t\t//\n#ifdef AAS_SAMPLE_DEBUG\n\t\tif (lastvertex && lastvertex != edge->v[firstvertex])\n\t\t{\n\t\t\tbotimport.Print(PRT_MESSAGE, \"winding not counter clockwise\\n\");\n\t\t} //end if\n\t\tlastvertex = edge->v[!firstvertex];\n#endif //AAS_SAMPLE_DEBUG\n\t\t//vector from first edge point to point possible in face\n\t\tVectorSubtract(point, v0, pointvec);\n\t\t//get a vector pointing inside the face orthogonal to both the\n\t\t//edge vector and the normal vector of the plane the face is in\n\t\t//this vector defines a plane through the origin (first vertex of\n\t\t//edge) and through both the edge vector and the normal vector\n\t\t//of the plane\n\t\tAAS_OrthogonalToVectors(edgevec, pnormal, sepnormal);\n\t\t//check on wich side of the above plane the point is\n\t\t//this is done by checking the sign of the dot product of the\n\t\t//vector orthogonal vector from above and the vector from the\n\t\t//origin (first vertex of edge) to the point \n\t\t//if the dotproduct is smaller than zero the point is outside the face\n\t\tif (DotProduct(pointvec, sepnormal) < -epsilon) return qfalse;\n\t} //end for\n\treturn qtrue;\n} //end of the function AAS_InsideFace\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nqboolean AAS_PointInsideFace(int facenum, vec3_t point, float epsilon)\n{\n\tint i, firstvertex, edgenum;\n\tvec_t *v1, *v2;\n\tvec3_t edgevec, pointvec, sepnormal;\n\taas_edge_t *edge;\n\taas_plane_t *plane;\n\taas_face_t *face;\n\n\tif (!aasworld.loaded) return qfalse;\n\n\tface = &aasworld.faces[facenum];\n\tplane = &aasworld.planes[face->planenum];\n\t//\n\tfor (i = 0; i < face->numedges; i++)\n\t{\n\t\tedgenum = aasworld.edgeindex[face->firstedge + i];\n\t\tedge = &aasworld.edges[abs(edgenum)];\n\t\t//get the first vertex of the edge\n\t\tfirstvertex = edgenum < 0;\n\t\tv1 = aasworld.vertexes[edge->v[firstvertex]];\n\t\tv2 = aasworld.vertexes[edge->v[!firstvertex]];\n\t\t//edge vector\n\t\tVectorSubtract(v2, v1, edgevec);\n\t\t//vector from first edge point to point possible in face\n\t\tVectorSubtract(point, v1, pointvec);\n\t\t//\n\t\tCrossProduct(edgevec, plane->normal, sepnormal);\n\t\t//\n\t\tif (DotProduct(pointvec, sepnormal) < -epsilon) return qfalse;\n\t} //end for\n\treturn qtrue;\n} //end of the function AAS_PointInsideFace\n//===========================================================================\n// returns the ground face the given point is above in the given area\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\naas_face_t *AAS_AreaGroundFace(int areanum, vec3_t point)\n{\n\tint i, facenum;\n\tvec3_t up = {0, 0, 1};\n\tvec3_t normal;\n\taas_area_t *area;\n\taas_face_t *face;\n\n\tif (!aasworld.loaded) return NULL;\n\n\tarea = &aasworld.areas[areanum];\n\tfor (i = 0; i < area->numfaces; i++)\n\t{\n\t\tfacenum = aasworld.faceindex[area->firstface + i];\n\t\tface = &aasworld.faces[abs(facenum)];\n\t\t//if this is a ground face\n\t\tif (face->faceflags & FACE_GROUND)\n\t\t{\n\t\t\t//get the up or down normal\n\t\t\tif (aasworld.planes[face->planenum].normal[2] < 0) VectorNegate(up, normal);\n\t\t\telse VectorCopy(up, normal);\n\t\t\t//check if the point is in the face\n\t\t\tif (AAS_InsideFace(face, normal, point, 0.01f)) return face;\n\t\t} //end if\n\t} //end for\n\treturn NULL;\n} //end of the function AAS_AreaGroundFace\n//===========================================================================\n// returns the face the trace end position is situated in\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid AAS_FacePlane(int facenum, vec3_t normal, float *dist)\n{\n\taas_plane_t *plane;\n\n\tplane = &aasworld.planes[aasworld.faces[facenum].planenum];\n\tVectorCopy(plane->normal, normal);\n\t*dist = plane->dist;\n} //end of the function AAS_FacePlane\n//===========================================================================\n// returns the face the trace end position is situated in\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\naas_face_t *AAS_TraceEndFace(aas_trace_t *trace)\n{\n\tint i, facenum;\n\taas_area_t *area;\n\taas_face_t *face, *firstface = NULL;\n\n\tif (!aasworld.loaded) return NULL;\n\n\t//if started in solid no face was hit\n\tif (trace->startsolid) return NULL;\n\t//trace->lastarea is the last area the trace was in\n\tarea = &aasworld.areas[trace->lastarea];\n\t//check which face the trace.endpos was in\n\tfor (i = 0; i < area->numfaces; i++)\n\t{\n\t\tfacenum = aasworld.faceindex[area->firstface + i];\n\t\tface = &aasworld.faces[abs(facenum)];\n\t\t//if the face is in the same plane as the trace end point\n\t\tif ((face->planenum & ~1) == (trace->planenum & ~1))\n\t\t{\n\t\t\t//firstface is used for optimization, if theres only one\n\t\t\t//face in the plane then it has to be the good one\n\t\t\t//if there are more faces in the same plane then always\n\t\t\t//check the one with the fewest edges first\n/*\t\t\tif (firstface)\n\t\t\t{\n\t\t\t\tif (firstface->numedges < face->numedges)\n\t\t\t\t{\n\t\t\t\t\tif (AAS_InsideFace(firstface,\n\t\t\t\t\t\taasworld.planes[face->planenum].normal, trace->endpos))\n\t\t\t\t\t{\n\t\t\t\t\t\treturn firstface;\n\t\t\t\t\t} //end if\n\t\t\t\t\tfirstface = face;\n\t\t\t\t} //end if\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tif (AAS_InsideFace(face,\n\t\t\t\t\t\taasworld.planes[face->planenum].normal, trace->endpos))\n\t\t\t\t\t{\n\t\t\t\t\t\treturn face;\n\t\t\t\t\t} //end if\n\t\t\t\t} //end else\n\t\t\t} //end if\n\t\t\telse\n\t\t\t{\n\t\t\t\tfirstface = face;\n\t\t\t} //end else*/\n\t\t\tif (AAS_InsideFace(face,\n\t\t\t\t\t\taasworld.planes[face->planenum].normal, trace->endpos, 0.01f)) return face;\n\t\t} //end if\n\t} //end for\n\treturn firstface;\n} //end of the function AAS_TraceEndFace\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint AAS_BoxOnPlaneSide2(vec3_t absmins, vec3_t absmaxs, aas_plane_t *p)\n{\n\tint i, sides;\n\tfloat dist1, dist2;\n\tvec3_t corners[2];\n\n\tfor (i = 0; i < 3; i++)\n\t{\n\t\tif (p->normal[i] < 0)\n\t\t{\n\t\t\tcorners[0][i] = absmins[i];\n\t\t\tcorners[1][i] = absmaxs[i];\n\t\t} //end if\n\t\telse\n\t\t{\n\t\t\tcorners[1][i] = absmins[i];\n\t\t\tcorners[0][i] = absmaxs[i];\n\t\t} //end else\n\t} //end for\n\tdist1 = DotProduct(p->normal, corners[0]) - p->dist;\n\tdist2 = DotProduct(p->normal, corners[1]) - p->dist;\n\tsides = 0;\n\tif (dist1 >= 0) sides = 1;\n\tif (dist2 < 0) sides |= 2;\n\n\treturn sides;\n} //end of the function AAS_BoxOnPlaneSide2\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\n//int AAS_BoxOnPlaneSide(vec3_t absmins, vec3_t absmaxs, aas_plane_t *p)\n#define AAS_BoxOnPlaneSide(absmins, absmaxs, p) (\\\n\t( (p)->type < 3) ?\\\n\t(\\\n\t\t( (p)->dist <= (absmins)[(p)->type]) ?\\\n\t\t(\\\n\t\t\t1\\\n\t\t)\\\n\t\t:\\\n\t\t(\\\n\t\t\t( (p)->dist >= (absmaxs)[(p)->type]) ?\\\n\t\t\t(\\\n\t\t\t\t2\\\n\t\t\t)\\\n\t\t\t:\\\n\t\t\t(\\\n\t\t\t\t3\\\n\t\t\t)\\\n\t\t)\\\n\t)\\\n\t:\\\n\t(\\\n\t\tAAS_BoxOnPlaneSide2((absmins), (absmaxs), (p))\\\n\t)\\\n) //end of the function AAS_BoxOnPlaneSide\n//===========================================================================\n// remove the links to this entity from all areas\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid AAS_UnlinkFromAreas(aas_link_t *areas)\n{\n\taas_link_t *link, *nextlink;\n\n\tfor (link = areas; link; link = nextlink)\n\t{\n\t\t//next area the entity is linked in\n\t\tnextlink = link->next_area;\n\t\t//remove the entity from the linked list of this area\n\t\tif (link->prev_ent) link->prev_ent->next_ent = link->next_ent;\n\t\telse aasworld.arealinkedentities[link->areanum] = link->next_ent;\n\t\tif (link->next_ent) link->next_ent->prev_ent = link->prev_ent;\n\t\t//deallocate the link structure\n\t\tAAS_DeAllocAASLink(link);\n\t} //end for\n} //end of the function AAS_UnlinkFromAreas\n//===========================================================================\n// link the entity to the areas the bounding box is totally or partly\n// situated in. This is done with recursion down the tree using the\n// bounding box to test for plane sides\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\n\ntypedef struct\n{\n\tint nodenum;\t\t//node found after splitting\n} aas_linkstack_t;\n\naas_link_t *AAS_AASLinkEntity(vec3_t absmins, vec3_t absmaxs, int entnum)\n{\n\tint side, nodenum;\n\taas_linkstack_t linkstack[128];\n\taas_linkstack_t *lstack_p;\n\taas_node_t *aasnode;\n\taas_plane_t *plane;\n\taas_link_t *link, *areas;\n\n\tif (!aasworld.loaded)\n\t{\n\t\tbotimport.Print(PRT_ERROR, \"AAS_LinkEntity: aas not loaded\\n\");\n\t\treturn NULL;\n\t} //end if\n\n\tareas = NULL;\n\t//\n\tlstack_p = linkstack;\n\t//we start with the whole line on the stack\n\t//start with node 1 because node zero is a dummy used for solid leafs\n\tlstack_p->nodenum = 1;\t\t//starting at the root of the tree\n\tlstack_p++;\n\t\n\twhile (1)\n\t{\n\t\t//pop up the stack\n\t\tlstack_p--;\n\t\t//if the trace stack is empty (ended up with a piece of the\n\t\t//line to be traced in an area)\n\t\tif (lstack_p < linkstack) break;\n\t\t//number of the current node to test the line against\n\t\tnodenum = lstack_p->nodenum;\n\t\t//if it is an area\n\t\tif (nodenum < 0)\n\t\t{\n\t\t\t//NOTE: the entity might have already been linked into this area\n\t\t\t// because several node children can point to the same area\n\t\t\tfor (link = aasworld.arealinkedentities[-nodenum]; link; link = link->next_ent)\n\t\t\t{\n\t\t\t\tif (link->entnum == entnum) break;\n\t\t\t} //end for\n\t\t\tif (link) continue;\n\t\t\t//\n\t\t\tlink = AAS_AllocAASLink();\n\t\t\tif (!link) return areas;\n\t\t\tlink->entnum = entnum;\n\t\t\tlink->areanum = -nodenum;\n\t\t\t//put the link into the double linked area list of the entity\n\t\t\tlink->prev_area = NULL;\n\t\t\tlink->next_area = areas;\n\t\t\tif (areas) areas->prev_area = link;\n\t\t\tareas = link;\n\t\t\t//put the link into the double linked entity list of the area\n\t\t\tlink->prev_ent = NULL;\n\t\t\tlink->next_ent = aasworld.arealinkedentities[-nodenum];\n\t\t\tif (aasworld.arealinkedentities[-nodenum])\n\t\t\t\t\taasworld.arealinkedentities[-nodenum]->prev_ent = link;\n\t\t\taasworld.arealinkedentities[-nodenum] = link;\n\t\t\t//\n\t\t\tcontinue;\n\t\t} //end if\n\t\t//if solid leaf\n\t\tif (!nodenum) continue;\n\t\t//the node to test against\n\t\taasnode = &aasworld.nodes[nodenum];\n\t\t//the current node plane\n\t\tplane = &aasworld.planes[aasnode->planenum];\n\t\t//get the side(s) the box is situated relative to the plane\n\t\tside = AAS_BoxOnPlaneSide2(absmins, absmaxs, plane);\n\t\t//if on the front side of the node\n\t\tif (side & 1)\n\t\t{\n\t\t\tlstack_p->nodenum = aasnode->children[0];\n\t\t\tlstack_p++;\n\t\t} //end if\n\t\tif (lstack_p >= &linkstack[127])\n\t\t{\n\t\t\tbotimport.Print(PRT_ERROR, \"AAS_LinkEntity: stack overflow\\n\");\n\t\t\tbreak;\n\t\t} //end if\n\t\t//if on the back side of the node\n\t\tif (side & 2)\n\t\t{\n\t\t\tlstack_p->nodenum = aasnode->children[1];\n\t\t\tlstack_p++;\n\t\t} //end if\n\t\tif (lstack_p >= &linkstack[127])\n\t\t{\n\t\t\tbotimport.Print(PRT_ERROR, \"AAS_LinkEntity: stack overflow\\n\");\n\t\t\tbreak;\n\t\t} //end if\n\t} //end while\n\treturn areas;\n} //end of the function AAS_AASLinkEntity\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\naas_link_t *AAS_LinkEntityClientBBox(vec3_t absmins, vec3_t absmaxs, int entnum, int presencetype)\n{\n\tvec3_t mins, maxs;\n\tvec3_t newabsmins, newabsmaxs;\n\n\tAAS_PresenceTypeBoundingBox(presencetype, mins, maxs);\n\tVectorSubtract(absmins, maxs, newabsmins);\n\tVectorSubtract(absmaxs, mins, newabsmaxs);\n\t//relink the entity\n\treturn AAS_AASLinkEntity(newabsmins, newabsmaxs, entnum);\n} //end of the function AAS_LinkEntityClientBBox\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint AAS_BBoxAreas(vec3_t absmins, vec3_t absmaxs, int *areas, int maxareas)\n{\n\taas_link_t *linkedareas, *link;\n\tint num;\n\n\tlinkedareas = AAS_AASLinkEntity(absmins, absmaxs, -1);\n\tnum = 0;\n\tfor (link = linkedareas; link; link = link->next_area)\n\t{\n\t\tareas[num] = link->areanum;\n\t\tnum++;\n\t\tif (num >= maxareas)\n\t\t\tbreak;\n\t} //end for\n\tAAS_UnlinkFromAreas(linkedareas);\n\treturn num;\n} //end of the function AAS_BBoxAreas\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint AAS_AreaInfo( int areanum, aas_areainfo_t *info )\n{\n\taas_areasettings_t *settings;\n\tif (!info)\n\t\treturn 0;\n\tif (areanum <= 0 || areanum >= aasworld.numareas)\n\t{\n\t\tbotimport.Print(PRT_ERROR, \"AAS_AreaInfo: areanum %d out of range\\n\", areanum);\n\t\treturn 0;\n\t} //end if\n\tsettings = &aasworld.areasettings[areanum];\n\tinfo->cluster = settings->cluster;\n\tinfo->contents = settings->contents;\n\tinfo->flags = settings->areaflags;\n\tinfo->presencetype = settings->presencetype;\n\tVectorCopy(aasworld.areas[areanum].mins, info->mins);\n\tVectorCopy(aasworld.areas[areanum].maxs, info->maxs);\n\tVectorCopy(aasworld.areas[areanum].center, info->center);\n\treturn sizeof(aas_areainfo_t);\n} //end of the function AAS_AreaInfo\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\naas_plane_t *AAS_PlaneFromNum(int planenum)\n{\n\tif (!aasworld.loaded) return NULL;\n\n\treturn &aasworld.planes[planenum];\n} //end of the function AAS_PlaneFromNum\n"
  },
  {
    "path": "plugins/quake3/botlib/be_aas_sample.h",
    "content": "/*\n===========================================================================\nCopyright (C) 1999-2005 Id Software, Inc.\n\nThis file is part of Quake III Arena source code.\n\nQuake III Arena source code is free software; you can redistribute it\nand/or modify it under the terms of the GNU General Public License as\npublished by the Free Software Foundation; either version 2 of the License,\nor (at your option) any later version.\n\nQuake III Arena source code is distributed in the hope that it will be\nuseful, but WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Quake III Arena source code; if not, write to the Free Software\nFoundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n===========================================================================\n*/\n\n/*****************************************************************************\n * name:\t\tbe_aas_sample.h\n *\n * desc:\t\tAAS\n *\n * $Archive: /source/code/botlib/be_aas_sample.h $\n *\n *****************************************************************************/\n\n#ifdef AASINTERN\nvoid AAS_InitAASLinkHeap(void);\nvoid AAS_InitAASLinkedEntities(void);\nvoid AAS_FreeAASLinkHeap(void);\nvoid AAS_FreeAASLinkedEntities(void);\naas_face_t *AAS_AreaGroundFace(int areanum, vec3_t point);\naas_face_t *AAS_TraceEndFace(aas_trace_t *trace);\naas_plane_t *AAS_PlaneFromNum(int planenum);\naas_link_t *AAS_AASLinkEntity(vec3_t absmins, vec3_t absmaxs, int entnum);\naas_link_t *AAS_LinkEntityClientBBox(vec3_t absmins, vec3_t absmaxs, int entnum, int presencetype);\nqboolean AAS_PointInsideFace(int facenum, vec3_t point, float epsilon);\nqboolean AAS_InsideFace(aas_face_t *face, vec3_t pnormal, vec3_t point, float epsilon);\nvoid AAS_UnlinkFromAreas(aas_link_t *areas);\n#endif //AASINTERN\n\n//returns the mins and maxs of the bounding box for the given presence type\nvoid AAS_PresenceTypeBoundingBox(int presencetype, vec3_t mins, vec3_t maxs);\n//returns the cluster the area is in (negative portal number if the area is a portal)\nint AAS_AreaCluster(int areanum);\n//returns the presence type(s) of the area\nint AAS_AreaPresenceType(int areanum);\n//returns the presence type(s) at the given point\nint AAS_PointPresenceType(vec3_t point);\n//returns the result of the trace of a client bbox\naas_trace_t AAS_TraceClientBBox(vec3_t start, vec3_t end, int presencetype, int passent);\n//stores the areas the trace went through and returns the number of passed areas\nint AAS_TraceAreas(vec3_t start, vec3_t end, int *areas, vec3_t *points, int maxareas);\n//returns the areas the bounding box is in\nint AAS_BBoxAreas(vec3_t absmins, vec3_t absmaxs, int *areas, int maxareas);\n//return area information\nint AAS_AreaInfo( int areanum, aas_areainfo_t *info );\n//returns the area the point is in\nint AAS_PointAreaNum(vec3_t point);\n//\nint AAS_PointReachabilityAreaIndex( vec3_t point );\n//returns the plane the given face is in\nvoid AAS_FacePlane(int facenum, vec3_t normal, float *dist);\n\n"
  },
  {
    "path": "plugins/quake3/botlib/be_ai_char.c",
    "content": "/*\n===========================================================================\nCopyright (C) 1999-2005 Id Software, Inc.\n\nThis file is part of Quake III Arena source code.\n\nQuake III Arena source code is free software; you can redistribute it\nand/or modify it under the terms of the GNU General Public License as\npublished by the Free Software Foundation; either version 2 of the License,\nor (at your option) any later version.\n\nQuake III Arena source code is distributed in the hope that it will be\nuseful, but WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Quake III Arena source code; if not, write to the Free Software\nFoundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n===========================================================================\n*/\n\n/*****************************************************************************\n * name:\t\tbe_ai_char.c\n *\n * desc:\t\tbot characters\n *\n * $Archive: /MissionPack/code/botlib/be_ai_char.c $\n *\n *****************************************************************************/\n\n#include \"q_shared.h\"\n#include \"l_log.h\"\n#include \"l_memory.h\"\n#include \"l_utils.h\"\n#include \"l_script.h\"\n#include \"l_precomp.h\"\n#include \"l_struct.h\"\n#include \"l_libvar.h\"\n#include \"aasfile.h\"\n#include \"botlib.h\"\n#include \"be_aas.h\"\n#include \"be_aas_funcs.h\"\n#include \"be_interface.h\"\n#include \"be_ai_char.h\"\n\n#define MAX_CHARACTERISTICS\t\t80\n\n#define CT_INTEGER\t\t\t\t1\n#define CT_FLOAT\t\t\t\t2\n#define CT_STRING\t\t\t\t3\n\n#define DEFAULT_CHARACTER\t\t\"bots/default_c.c\"\n\n//characteristic value\nunion cvalue\n{\n\tint integer;\n\tfloat _float;\n\tchar *string;\n};\n//a characteristic\ntypedef struct bot_characteristic_s\n{\n\tchar type;\t\t\t\t\t\t//characteristic type\n\tunion cvalue value;\t\t\t\t//characteristic value\n} bot_characteristic_t;\n\n//a bot character\ntypedef struct bot_character_s\n{\n\tchar filename[MAX_QPATH];\n\tfloat skill;\n\tbot_characteristic_t c[1];\t\t//variable sized\n} bot_character_t;\n\nbot_character_t *botcharacters[MAX_CLIENTS + 1];\n\n//========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//========================================================================\nbot_character_t *BotCharacterFromHandle(int handle)\n{\n\tif (handle <= 0 || handle > MAX_CLIENTS)\n\t{\n\t\tbotimport.Print(PRT_FATAL, \"character handle %d out of range\\n\", handle);\n\t\treturn NULL;\n\t} //end if\n\tif (!botcharacters[handle])\n\t{\n\t\tbotimport.Print(PRT_FATAL, \"invalid character %d\\n\", handle);\n\t\treturn NULL;\n\t} //end if\n\treturn botcharacters[handle];\n} //end of the function BotCharacterFromHandle\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid BotDumpCharacter(bot_character_t *ch)\n{\n\tint i;\n\n\tLog_Write(\"%s\\n\", ch->filename);\n\tLog_Write(\"skill %.1f\\n\", ch->skill);\n\tLog_Write(\"{\\n\");\n\tfor (i = 0; i < MAX_CHARACTERISTICS; i++)\n\t{\n\t\tswitch(ch->c[i].type)\n\t\t{\n\t\t\tcase CT_INTEGER: Log_Write(\" %4d %d\\n\", i, ch->c[i].value.integer); break;\n\t\t\tcase CT_FLOAT: Log_Write(\" %4d %f\\n\", i, ch->c[i].value._float); break;\n\t\t\tcase CT_STRING: Log_Write(\" %4d %s\\n\", i, ch->c[i].value.string); break;\n\t\t} //end case\n\t} //end for\n\tLog_Write(\"}\\n\");\n} //end of the function BotDumpCharacter\n//========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//========================================================================\nvoid BotFreeCharacterStrings(bot_character_t *ch)\n{\n\tint i;\n\n\tfor (i = 0; i < MAX_CHARACTERISTICS; i++)\n\t{\n\t\tif (ch->c[i].type == CT_STRING)\n\t\t{\n\t\t\tFreeMemory(ch->c[i].value.string);\n\t\t} //end if\n\t} //end for\n} //end of the function BotFreeCharacterStrings\n//========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//========================================================================\nvoid BotFreeCharacter2(int handle)\n{\n\tif (handle <= 0 || handle > MAX_CLIENTS)\n\t{\n\t\tbotimport.Print(PRT_FATAL, \"character handle %d out of range\\n\", handle);\n\t\treturn;\n\t} //end if\n\tif (!botcharacters[handle])\n\t{\n\t\tbotimport.Print(PRT_FATAL, \"invalid character %d\\n\", handle);\n\t\treturn;\n\t} //end if\n\tBotFreeCharacterStrings(botcharacters[handle]);\n\tFreeMemory(botcharacters[handle]);\n\tbotcharacters[handle] = NULL;\n} //end of the function BotFreeCharacter2\n//========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//========================================================================\nvoid BotFreeCharacter(int handle)\n{\n\tif (!LibVarGetValue(\"bot_reloadcharacters\")) return;\n\tBotFreeCharacter2(handle);\n} //end of the function BotFreeCharacter\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid BotDefaultCharacteristics(bot_character_t *ch, bot_character_t *defaultch)\n{\n\tint i;\n\n\tfor (i = 0; i < MAX_CHARACTERISTICS; i++)\n\t{\n\t\tif (ch->c[i].type) continue;\n\t\t//\n\t\tif (defaultch->c[i].type == CT_FLOAT)\n\t\t{\n\t\t\tch->c[i].type = CT_FLOAT;\n\t\t\tch->c[i].value._float = defaultch->c[i].value._float;\n\t\t} //end if\n\t\telse if (defaultch->c[i].type == CT_INTEGER)\n\t\t{\n\t\t\tch->c[i].type = CT_INTEGER;\n\t\t\tch->c[i].value.integer = defaultch->c[i].value.integer;\n\t\t} //end else if\n\t\telse if (defaultch->c[i].type == CT_STRING)\n\t\t{\n\t\t\tch->c[i].type = CT_STRING;\n\t\t\tch->c[i].value.string = (char *) GetMemory(strlen(defaultch->c[i].value.string)+1);\n\t\t\tstrcpy(ch->c[i].value.string, defaultch->c[i].value.string);\n\t\t} //end else if\n\t} //end for\n} //end of the function BotDefaultCharacteristics\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nbot_character_t *BotLoadCharacterFromFile(char *charfile, int skill)\n{\n\tint indent, index, foundcharacter;\n\tbot_character_t *ch;\n\tsource_t *source;\n\ttoken_t token;\n\n\tfoundcharacter = qfalse;\n\t//a bot character is parsed in two phases\n\tPC_SetBaseFolder(BOTFILESBASEFOLDER);\n\tsource = LoadSourceFile(charfile);\n\tif (!source)\n\t{\n\t\tbotimport.Print(PRT_ERROR, \"counldn't load %s\\n\", charfile);\n\t\treturn NULL;\n\t} //end if\n\tch = (bot_character_t *) GetClearedMemory(sizeof(bot_character_t) +\n\t\t\t\t\tMAX_CHARACTERISTICS * sizeof(bot_characteristic_t));\n\tstrcpy(ch->filename, charfile);\n\twhile(PC_ReadToken(source, &token))\n\t{\n\t\tif (!strcmp(token.string, \"skill\"))\n\t\t{\n\t\t\tif (!PC_ExpectTokenType(source, TT_NUMBER, 0, &token))\n\t\t\t{\n\t\t\t\tFreeSource(source);\n\t\t\t\tBotFreeCharacterStrings(ch);\n\t\t\t\tFreeMemory(ch);\n\t\t\t\treturn NULL;\n\t\t\t} //end if\n\t\t\tif (!PC_ExpectTokenString(source, \"{\"))\n\t\t\t{\n\t\t\t\tFreeSource(source);\n\t\t\t\tBotFreeCharacterStrings(ch);\n\t\t\t\tFreeMemory(ch);\n\t\t\t\treturn NULL;\n\t\t\t} //end if\n\t\t\t//if it's the correct skill\n\t\t\tif (skill < 0 || token.intvalue == skill)\n\t\t\t{\n\t\t\t\tfoundcharacter = qtrue;\n\t\t\t\tch->skill = token.intvalue;\n\t\t\t\twhile(PC_ExpectAnyToken(source, &token))\n\t\t\t\t{\n\t\t\t\t\tif (!strcmp(token.string, \"}\")) break;\n\t\t\t\t\tif (token.type != TT_NUMBER || !(token.subtype & TT_INTEGER))\n\t\t\t\t\t{\n\t\t\t\t\t\tSourceError(source, \"expected integer index, found %s\\n\", token.string);\n\t\t\t\t\t\tFreeSource(source);\n\t\t\t\t\t\tBotFreeCharacterStrings(ch);\n\t\t\t\t\t\tFreeMemory(ch);\n\t\t\t\t\t\treturn NULL;\n\t\t\t\t\t} //end if\n\t\t\t\t\tindex = token.intvalue;\n\t\t\t\t\tif (index < 0 || index > MAX_CHARACTERISTICS)\n\t\t\t\t\t{\n\t\t\t\t\t\tSourceError(source, \"characteristic index out of range [0, %d]\\n\", MAX_CHARACTERISTICS);\n\t\t\t\t\t\tFreeSource(source);\n\t\t\t\t\t\tBotFreeCharacterStrings(ch);\n\t\t\t\t\t\tFreeMemory(ch);\n\t\t\t\t\t\treturn NULL;\n\t\t\t\t\t} //end if\n\t\t\t\t\tif (ch->c[index].type)\n\t\t\t\t\t{\n\t\t\t\t\t\tSourceError(source, \"characteristic %d already initialized\\n\", index);\n\t\t\t\t\t\tFreeSource(source);\n\t\t\t\t\t\tBotFreeCharacterStrings(ch);\n\t\t\t\t\t\tFreeMemory(ch);\n\t\t\t\t\t\treturn NULL;\n\t\t\t\t\t} //end if\n\t\t\t\t\tif (!PC_ExpectAnyToken(source, &token))\n\t\t\t\t\t{\n\t\t\t\t\t\tFreeSource(source);\n\t\t\t\t\t\tBotFreeCharacterStrings(ch);\n\t\t\t\t\t\tFreeMemory(ch);\n\t\t\t\t\t\treturn NULL;\n\t\t\t\t\t} //end if\n\t\t\t\t\tif (token.type == TT_NUMBER)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (token.subtype & TT_FLOAT)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tch->c[index].value._float = token.floatvalue;\n\t\t\t\t\t\t\tch->c[index].type = CT_FLOAT;\n\t\t\t\t\t\t} //end if\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tch->c[index].value.integer = token.intvalue;\n\t\t\t\t\t\t\tch->c[index].type = CT_INTEGER;\n\t\t\t\t\t\t} //end else\n\t\t\t\t\t} //end if\n\t\t\t\t\telse if (token.type == TT_STRING)\n\t\t\t\t\t{\n\t\t\t\t\t\tStripDoubleQuotes(token.string);\n\t\t\t\t\t\tch->c[index].value.string = GetMemory(strlen(token.string)+1);\n\t\t\t\t\t\tstrcpy(ch->c[index].value.string, token.string);\n\t\t\t\t\t\tch->c[index].type = CT_STRING;\n\t\t\t\t\t} //end else if\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tSourceError(source, \"expected integer, float or string, found %s\\n\", token.string);\n\t\t\t\t\t\tFreeSource(source);\n\t\t\t\t\t\tBotFreeCharacterStrings(ch);\n\t\t\t\t\t\tFreeMemory(ch);\n\t\t\t\t\t\treturn NULL;\n\t\t\t\t\t} //end else\n\t\t\t\t} //end if\n\t\t\t\tbreak;\n\t\t\t} //end if\n\t\t\telse\n\t\t\t{\n\t\t\t\tindent = 1;\n\t\t\t\twhile(indent)\n\t\t\t\t{\n\t\t\t\t\tif (!PC_ExpectAnyToken(source, &token))\n\t\t\t\t\t{\n\t\t\t\t\t\tFreeSource(source);\n\t\t\t\t\t\tBotFreeCharacterStrings(ch);\n\t\t\t\t\t\tFreeMemory(ch);\n\t\t\t\t\t\treturn NULL;\n\t\t\t\t\t} //end if\n\t\t\t\t\tif (!strcmp(token.string, \"{\")) indent++;\n\t\t\t\t\telse if (!strcmp(token.string, \"}\")) indent--;\n\t\t\t\t} //end while\n\t\t\t} //end else\n\t\t} //end if\n\t\telse\n\t\t{\n\t\t\tSourceError(source, \"unknown definition %s\\n\", token.string);\n\t\t\tFreeSource(source);\n\t\t\tBotFreeCharacterStrings(ch);\n\t\t\tFreeMemory(ch);\n\t\t\treturn NULL;\n\t\t} //end else\n\t} //end while\n\tFreeSource(source);\n\t//\n\tif (!foundcharacter)\n\t{\n\t\tBotFreeCharacterStrings(ch);\n\t\tFreeMemory(ch);\n\t\treturn NULL;\n\t} //end if\n\treturn ch;\n} //end of the function BotLoadCharacterFromFile\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint BotFindCachedCharacter(char *charfile, float skill)\n{\n\tint handle;\n\n\tfor (handle = 1; handle <= MAX_CLIENTS; handle++)\n\t{\n\t\tif ( !botcharacters[handle] ) continue;\n\t\tif ( strcmp( botcharacters[handle]->filename, charfile ) == 0 &&\n\t\t\t(skill < 0 || fabs(botcharacters[handle]->skill - skill) < 0.01) )\n\t\t{\n\t\t\treturn handle;\n\t\t} //end if\n\t} //end for\n\treturn 0;\n} //end of the function BotFindCachedCharacter\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint BotLoadCachedCharacter(char *charfile, float skill, int reload)\n{\n\tint handle, cachedhandle, intskill;\n\tbot_character_t *ch = NULL;\n#ifdef DEBUG\n\tint starttime;\n\n\tstarttime = Sys_MilliSeconds();\n#endif //DEBUG\n\n\t//find a free spot for a character\n\tfor (handle = 1; handle <= MAX_CLIENTS; handle++)\n\t{\n\t\tif (!botcharacters[handle]) break;\n\t} //end for\n\tif (handle > MAX_CLIENTS) return 0;\n\t//try to load a cached character with the given skill\n\tif (!reload)\n\t{\n\t\tcachedhandle = BotFindCachedCharacter(charfile, skill);\n\t\tif (cachedhandle)\n\t\t{\n\t\t\tbotimport.Print(PRT_MESSAGE, \"loaded cached skill %f from %s\\n\", skill, charfile);\n\t\t\treturn cachedhandle;\n\t\t} //end if\n\t} //end else\n\t//\n\tintskill = (int) (skill + 0.5);\n\t//try to load the character with the given skill\n\tch = BotLoadCharacterFromFile(charfile, intskill);\n\tif (ch)\n\t{\n\t\tbotcharacters[handle] = ch;\n\t\t//\n\t\tbotimport.Print(PRT_MESSAGE, \"loaded skill %d from %s\\n\", intskill, charfile);\n#ifdef DEBUG\n\t\tif (botDeveloper)\n\t\t{\n\t\t\tbotimport.Print(PRT_MESSAGE, \"skill %d loaded in %d msec from %s\\n\", intskill, Sys_MilliSeconds() - starttime, charfile);\n\t\t} //end if\n#endif //DEBUG\n\t\treturn handle;\n\t} //end if\n\t//\n\tbotimport.Print(PRT_WARNING, \"couldn't find skill %d in %s\\n\", intskill, charfile);\n\t//\n\tif (!reload)\n\t{\n\t\t//try to load a cached default character with the given skill\n\t\tcachedhandle = BotFindCachedCharacter(DEFAULT_CHARACTER, skill);\n\t\tif (cachedhandle)\n\t\t{\n\t\t\tbotimport.Print(PRT_MESSAGE, \"loaded cached default skill %d from %s\\n\", intskill, charfile);\n\t\t\treturn cachedhandle;\n\t\t} //end if\n\t} //end if\n\t//try to load the default character with the given skill\n\tch = BotLoadCharacterFromFile(DEFAULT_CHARACTER, intskill);\n\tif (ch)\n\t{\n\t\tbotcharacters[handle] = ch;\n\t\tbotimport.Print(PRT_MESSAGE, \"loaded default skill %d from %s\\n\", intskill, charfile);\n\t\treturn handle;\n\t} //end if\n\t//\n\tif (!reload)\n\t{\n\t\t//try to load a cached character with any skill\n\t\tcachedhandle = BotFindCachedCharacter(charfile, -1);\n\t\tif (cachedhandle)\n\t\t{\n\t\t\tbotimport.Print(PRT_MESSAGE, \"loaded cached skill %f from %s\\n\", botcharacters[cachedhandle]->skill, charfile);\n\t\t\treturn cachedhandle;\n\t\t} //end if\n\t} //end if\n\t//try to load a character with any skill\n\tch = BotLoadCharacterFromFile(charfile, -1);\n\tif (ch)\n\t{\n\t\tbotcharacters[handle] = ch;\n\t\tbotimport.Print(PRT_MESSAGE, \"loaded skill %f from %s\\n\", ch->skill, charfile);\n\t\treturn handle;\n\t} //end if\n\t//\n\tif (!reload)\n\t{\n\t\t//try to load a cached character with any skill\n\t\tcachedhandle = BotFindCachedCharacter(DEFAULT_CHARACTER, -1);\n\t\tif (cachedhandle)\n\t\t{\n\t\t\tbotimport.Print(PRT_MESSAGE, \"loaded cached default skill %f from %s\\n\", botcharacters[cachedhandle]->skill, charfile);\n\t\t\treturn cachedhandle;\n\t\t} //end if\n\t} //end if\n\t//try to load a character with any skill\n\tch = BotLoadCharacterFromFile(DEFAULT_CHARACTER, -1);\n\tif (ch)\n\t{\n\t\tbotcharacters[handle] = ch;\n\t\tbotimport.Print(PRT_MESSAGE, \"loaded default skill %f from %s\\n\", ch->skill, charfile);\n\t\treturn handle;\n\t} //end if\n\t//\n\tbotimport.Print(PRT_WARNING, \"couldn't load any skill from %s\\n\", charfile);\n\t//couldn't load any character\n\treturn 0;\n} //end of the function BotLoadCachedCharacter\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint BotLoadCharacterSkill(char *charfile, float skill)\n{\n\tint ch, defaultch;\n\n\tdefaultch = BotLoadCachedCharacter(DEFAULT_CHARACTER, skill, qfalse);\n\tch = BotLoadCachedCharacter(charfile, skill, LibVarGetValue(\"bot_reloadcharacters\"));\n\n\tif (defaultch && ch)\n\t{\n\t\tBotDefaultCharacteristics(botcharacters[ch], botcharacters[defaultch]);\n\t} //end if\n\n\treturn ch;\n} //end of the function BotLoadCharacterSkill\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint BotInterpolateCharacters(int handle1, int handle2, float desiredskill)\n{\n\tbot_character_t *ch1, *ch2, *out;\n\tint i, handle;\n\tfloat scale;\n\n\tch1 = BotCharacterFromHandle(handle1);\n\tch2 = BotCharacterFromHandle(handle2);\n\tif (!ch1 || !ch2)\n\t\treturn 0;\n\t//find a free spot for a character\n\tfor (handle = 1; handle <= MAX_CLIENTS; handle++)\n\t{\n\t\tif (!botcharacters[handle]) break;\n\t} //end for\n\tif (handle > MAX_CLIENTS) return 0;\n\tout = (bot_character_t *) GetClearedMemory(sizeof(bot_character_t) +\n\t\t\t\t\tMAX_CHARACTERISTICS * sizeof(bot_characteristic_t));\n\tout->skill = desiredskill;\n\tstrcpy(out->filename, ch1->filename);\n\tbotcharacters[handle] = out;\n\n\tscale = (float) (desiredskill - ch1->skill) / (ch2->skill - ch1->skill);\n\tfor (i = 0; i < MAX_CHARACTERISTICS; i++)\n\t{\n\t\t//\n\t\tif (ch1->c[i].type == CT_FLOAT && ch2->c[i].type == CT_FLOAT)\n\t\t{\n\t\t\tout->c[i].type = CT_FLOAT;\n\t\t\tout->c[i].value._float = ch1->c[i].value._float +\n\t\t\t\t\t\t\t\t(ch2->c[i].value._float - ch1->c[i].value._float) * scale;\n\t\t} //end if\n\t\telse if (ch1->c[i].type == CT_INTEGER)\n\t\t{\n\t\t\tout->c[i].type = CT_INTEGER;\n\t\t\tout->c[i].value.integer = ch1->c[i].value.integer;\n\t\t} //end else if\n\t\telse if (ch1->c[i].type == CT_STRING)\n\t\t{\n\t\t\tout->c[i].type = CT_STRING;\n\t\t\tout->c[i].value.string = (char *) GetMemory(strlen(ch1->c[i].value.string)+1);\n\t\t\tstrcpy(out->c[i].value.string, ch1->c[i].value.string);\n\t\t} //end else if\n\t} //end for\n\treturn handle;\n} //end of the function BotInterpolateCharacters\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint BotLoadCharacter(char *charfile, float skill)\n{\n\tint firstskill, secondskill, handle;\n\n\t//make sure the skill is in the valid range\n\tif (skill < 1.0) skill = 1.0;\n\telse if (skill > 5.0) skill = 5.0;\n\t//skill 1, 4 and 5 should be available in the character files\n\tif (skill == 1.0 || skill == 4.0 || skill == 5.0)\n\t{\n\t\treturn BotLoadCharacterSkill(charfile, skill);\n\t} //end if\n\t//check if there's a cached skill\n\thandle = BotFindCachedCharacter(charfile, skill);\n\tif (handle)\n\t{\n\t\tbotimport.Print(PRT_MESSAGE, \"loaded cached skill %f from %s\\n\", skill, charfile);\n\t\treturn handle;\n\t} //end if\n\tif (skill < 4.0)\n\t{\n\t\t//load skill 1 and 4\n\t\tfirstskill = BotLoadCharacterSkill(charfile, 1);\n\t\tif (!firstskill) return 0;\n\t\tsecondskill = BotLoadCharacterSkill(charfile, 4);\n\t\tif (!secondskill) return firstskill;\n\t} //end if\n\telse\n\t{\n\t\t//load skill 4 and 5\n\t\tfirstskill = BotLoadCharacterSkill(charfile, 4);\n\t\tif (!firstskill) return 0;\n\t\tsecondskill = BotLoadCharacterSkill(charfile, 5);\n\t\tif (!secondskill) return firstskill;\n\t} //end else\n\t//interpolate between the two skills\n\thandle = BotInterpolateCharacters(firstskill, secondskill, skill);\n\tif (!handle) return 0;\n\t//write the character to the log file\n\tBotDumpCharacter(botcharacters[handle]);\n\t//\n\treturn handle;\n} //end of the function BotLoadCharacter\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint CheckCharacteristicIndex(int character, int index)\n{\n\tbot_character_t *ch;\n\n\tch = BotCharacterFromHandle(character);\n\tif (!ch) return qfalse;\n\tif (index < 0 || index >= MAX_CHARACTERISTICS)\n\t{\n\t\tbotimport.Print(PRT_ERROR, \"characteristic %d does not exist\\n\", index);\n\t\treturn qfalse;\n\t} //end if\n\tif (!ch->c[index].type)\n\t{\n\t\tbotimport.Print(PRT_ERROR, \"characteristic %d is not initialized\\n\", index);\n\t\treturn qfalse;\n\t} //end if\n\treturn qtrue;\n} //end of the function CheckCharacteristicIndex\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nfloat Characteristic_Float(int character, int index)\n{\n\tbot_character_t *ch;\n\n\tch = BotCharacterFromHandle(character);\n\tif (!ch) return 0;\n\t//check if the index is in range\n\tif (!CheckCharacteristicIndex(character, index)) return 0;\n\t//an integer will be converted to a float\n\tif (ch->c[index].type == CT_INTEGER)\n\t{\n\t\treturn (float) ch->c[index].value.integer;\n\t} //end if\n\t//floats are just returned\n\telse if (ch->c[index].type == CT_FLOAT)\n\t{\n\t\treturn ch->c[index].value._float;\n\t} //end else if\n\t//cannot convert a string pointer to a float\n\telse\n\t{\n\t\tbotimport.Print(PRT_ERROR, \"characteristic %d is not a float\\n\", index);\n\t\treturn 0;\n\t} //end else if\n//\treturn 0;\n} //end of the function Characteristic_Float\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nfloat Characteristic_BFloat(int character, int index, float min, float max)\n{\n\tfloat value;\n\tbot_character_t *ch;\n\n\tch = BotCharacterFromHandle(character);\n\tif (!ch) return 0;\n\tif (min > max)\n\t{\n\t\tbotimport.Print(PRT_ERROR, \"cannot bound characteristic %d between %f and %f\\n\", index, min, max);\n\t\treturn 0;\n\t} //end if\n\tvalue = Characteristic_Float(character, index);\n\tif (value < min) return min;\n\tif (value > max) return max;\n\treturn value;\n} //end of the function Characteristic_BFloat\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint Characteristic_Integer(int character, int index)\n{\n\tbot_character_t *ch;\n\n\tch = BotCharacterFromHandle(character);\n\tif (!ch) return 0;\n\t//check if the index is in range\n\tif (!CheckCharacteristicIndex(character, index)) return 0;\n\t//an integer will just be returned\n\tif (ch->c[index].type == CT_INTEGER)\n\t{\n\t\treturn ch->c[index].value.integer;\n\t} //end if\n\t//floats are casted to integers\n\telse if (ch->c[index].type == CT_FLOAT)\n\t{\n\t\treturn (int) ch->c[index].value._float;\n\t} //end else if\n\telse\n\t{\n\t\tbotimport.Print(PRT_ERROR, \"characteristic %d is not a integer\\n\", index);\n\t\treturn 0;\n\t} //end else if\n//\treturn 0;\n} //end of the function Characteristic_Integer\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint Characteristic_BInteger(int character, int index, int min, int max)\n{\n\tint value;\n\tbot_character_t *ch;\n\n\tch = BotCharacterFromHandle(character);\n\tif (!ch) return 0;\n\tif (min > max)\n\t{\n\t\tbotimport.Print(PRT_ERROR, \"cannot bound characteristic %d between %d and %d\\n\", index, min, max);\n\t\treturn 0;\n\t} //end if\n\tvalue = Characteristic_Integer(character, index);\n\tif (value < min) return min;\n\tif (value > max) return max;\n\treturn value;\n} //end of the function Characteristic_BInteger\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid Characteristic_String(int character, int index, char *buf, int size)\n{\n\tbot_character_t *ch;\n\n\tch = BotCharacterFromHandle(character);\n\tif (!ch) return;\n\t//check if the index is in range\n\tif (!CheckCharacteristicIndex(character, index)) return;\n\t//an integer will be converted to a float\n\tif (ch->c[index].type == CT_STRING)\n\t{\n\t\tstrncpy(buf, ch->c[index].value.string, size-1);\n\t\tbuf[size-1] = '\\0';\n\t\treturn;\n\t} //end if\n\telse\n\t{\n\t\tbotimport.Print(PRT_ERROR, \"characteristic %d is not a string\\n\", index);\n\t\treturn;\n\t} //end else if\n\treturn;\n} //end of the function Characteristic_String\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid BotShutdownCharacters(void)\n{\n\tint handle;\n\n\tfor (handle = 1; handle <= MAX_CLIENTS; handle++)\n\t{\n\t\tif (botcharacters[handle])\n\t\t{\n\t\t\tBotFreeCharacter2(handle);\n\t\t} //end if\n\t} //end for\n} //end of the function BotShutdownCharacters\n\n"
  },
  {
    "path": "plugins/quake3/botlib/be_ai_char.h",
    "content": "/*\n===========================================================================\nCopyright (C) 1999-2005 Id Software, Inc.\n\nThis file is part of Quake III Arena source code.\n\nQuake III Arena source code is free software; you can redistribute it\nand/or modify it under the terms of the GNU General Public License as\npublished by the Free Software Foundation; either version 2 of the License,\nor (at your option) any later version.\n\nQuake III Arena source code is distributed in the hope that it will be\nuseful, but WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Quake III Arena source code; if not, write to the Free Software\nFoundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n===========================================================================\n*/\n//\n\n/*****************************************************************************\n * name:\t\tbe_ai_char.h\n *\n * desc:\t\tbot characters\n *\n * $Archive: /source/code/botlib/be_ai_char.h $\n *\n *****************************************************************************/\n\n//loads a bot character from a file\nint BotLoadCharacter(char *charfile, float skill);\n//frees a bot character\nvoid BotFreeCharacter(int character);\n//returns a float characteristic\nfloat Characteristic_Float(int character, int index);\n//returns a bounded float characteristic\nfloat Characteristic_BFloat(int character, int index, float min, float max);\n//returns an integer characteristic\nint Characteristic_Integer(int character, int index);\n//returns a bounded integer characteristic\nint Characteristic_BInteger(int character, int index, int min, int max);\n//returns a string characteristic\nvoid Characteristic_String(int character, int index, char *buf, int size);\n//free cached bot characters\nvoid BotShutdownCharacters(void);\n"
  },
  {
    "path": "plugins/quake3/botlib/be_ai_chat.c",
    "content": "/*\n===========================================================================\nCopyright (C) 1999-2005 Id Software, Inc.\n\nThis file is part of Quake III Arena source code.\n\nQuake III Arena source code is free software; you can redistribute it\nand/or modify it under the terms of the GNU General Public License as\npublished by the Free Software Foundation; either version 2 of the License,\nor (at your option) any later version.\n\nQuake III Arena source code is distributed in the hope that it will be\nuseful, but WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Quake III Arena source code; if not, write to the Free Software\nFoundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n===========================================================================\n*/\n\n/*****************************************************************************\n * name:\t\tbe_ai_chat.c\n *\n * desc:\t\tbot chat AI\n *\n * $Archive: /MissionPack/code/botlib/be_ai_chat.c $\n *\n *****************************************************************************/\n\n#include \"q_shared.h\"\n#include \"l_memory.h\"\n#include \"l_libvar.h\"\n#include \"l_script.h\"\n#include \"l_precomp.h\"\n#include \"l_struct.h\"\n#include \"l_utils.h\"\n#include \"l_log.h\"\n#include \"aasfile.h\"\n#include \"botlib.h\"\n#include \"be_aas.h\"\n#include \"be_aas_funcs.h\"\n#include \"be_interface.h\"\n#include \"be_ea.h\"\n#include \"be_ai_chat.h\"\n\n\n//escape character\n#define ESCAPE_CHAR\t\t\t\t0x01\t//'_'\n//\n// \"hi \", people, \" \", 0, \" entered the game\"\n//becomes:\n// \"hi _rpeople_ _v0_ entered the game\"\n//\n\n//match piece types\n#define MT_VARIABLE\t\t\t\t\t1\t\t//variable match piece\n#define MT_STRING\t\t\t\t\t2\t\t//string match piece\n//reply chat key flags\n#define RCKFL_AND\t\t\t\t\t1\t\t//key must be present\n#define RCKFL_NOT\t\t\t\t\t2\t\t//key must be absent\n#define RCKFL_NAME\t\t\t\t\t4\t\t//name of bot must be present\n#define RCKFL_STRING\t\t\t\t8\t\t//key is a string\n#define RCKFL_VARIABLES\t\t\t\t16\t\t//key is a match template\n#define RCKFL_BOTNAMES\t\t\t\t32\t\t//key is a series of botnames\n#define RCKFL_GENDERFEMALE\t\t\t64\t\t//bot must be female\n#define RCKFL_GENDERMALE\t\t\t128\t\t//bot must be male\n#define RCKFL_GENDERLESS\t\t\t256\t\t//bot must be genderless\n//time to ignore a chat message after using it\n#define CHATMESSAGE_RECENTTIME\t20\n\n//the actuall chat messages\ntypedef struct bot_chatmessage_s\n{\n\tchar *chatmessage;\t\t\t\t\t//chat message string\n\tfloat time;\t\t\t\t\t\t\t//last time used\n\tstruct bot_chatmessage_s *next;\t\t//next chat message in a list\n} bot_chatmessage_t;\n//bot chat type with chat lines\ntypedef struct bot_chattype_s\n{\n\tchar name[MAX_CHATTYPE_NAME];\n\tint numchatmessages;\n\tbot_chatmessage_t *firstchatmessage;\n\tstruct bot_chattype_s *next;\n} bot_chattype_t;\n//bot chat lines\ntypedef struct bot_chat_s\n{\n\tbot_chattype_t *types;\n} bot_chat_t;\n\n//random string\ntypedef struct bot_randomstring_s\n{\n\tchar *string;\n\tstruct bot_randomstring_s *next;\n} bot_randomstring_t;\n//list with random strings\ntypedef struct bot_randomlist_s\n{\n\tchar *string;\n\tint numstrings;\n\tbot_randomstring_t *firstrandomstring;\n\tstruct bot_randomlist_s *next;\n} bot_randomlist_t;\n\n//synonym\ntypedef struct bot_synonym_s\n{\n\tchar *string;\n\tfloat weight;\n\tstruct bot_synonym_s *next;\n} bot_synonym_t;\n//list with synonyms\ntypedef struct bot_synonymlist_s\n{\n\tunsigned long int context;\n\tfloat totalweight;\n\tbot_synonym_t *firstsynonym;\n\tstruct bot_synonymlist_s *next;\n} bot_synonymlist_t;\n\n//fixed match string\ntypedef struct bot_matchstring_s\n{\n\tchar *string;\n\tstruct bot_matchstring_s *next;\n} bot_matchstring_t;\n\n//piece of a match template\ntypedef struct bot_matchpiece_s\n{\n\tint type;\n\tbot_matchstring_t *firststring;\n\tint variable;\n\tstruct bot_matchpiece_s *next;\n} bot_matchpiece_t;\n//match template\ntypedef struct bot_matchtemplate_s\n{\n\tunsigned long int context;\n\tint type;\n\tint subtype;\n\tbot_matchpiece_t *first;\n\tstruct bot_matchtemplate_s *next;\n} bot_matchtemplate_t;\n\n//reply chat key\ntypedef struct bot_replychatkey_s\n{\n\tint flags;\n\tchar *string;\n\tbot_matchpiece_t *match;\n\tstruct bot_replychatkey_s *next;\n} bot_replychatkey_t;\n//reply chat\ntypedef struct bot_replychat_s\n{\n\tbot_replychatkey_t *keys;\n\tfloat priority;\n\tint numchatmessages;\n\tbot_chatmessage_t *firstchatmessage;\n\tstruct bot_replychat_s *next;\n} bot_replychat_t;\n\n//string list\ntypedef struct bot_stringlist_s\n{\n\tchar *string;\n\tstruct bot_stringlist_s *next;\n} bot_stringlist_t;\n\n//chat state of a bot\ntypedef struct bot_chatstate_s\n{\n\tint gender;\t\t\t\t\t\t\t\t\t\t\t//0=it, 1=female, 2=male\n\tint client;\t\t\t\t\t\t\t\t\t\t\t//client number\n\tchar name[32];\t\t\t\t\t\t\t\t\t\t//name of the bot\n\tchar chatmessage[MAX_MESSAGE_SIZE];\n\tint handle;\n\t//the console messages visible to the bot\n\tbot_consolemessage_t *firstmessage;\t\t\t//first message is the first typed message\n\tbot_consolemessage_t *lastmessage;\t\t\t//last message is the last typed message, bottom of console\n\t//number of console messages stored in the state\n\tint numconsolemessages;\n\t//the bot chat lines\n\tbot_chat_t *chat;\n} bot_chatstate_t;\n\ntypedef struct {\n\tbot_chat_t\t*chat;\n\tchar\t\tfilename[MAX_QPATH];\n\tchar\t\tchatname[MAX_QPATH];\n} bot_ichatdata_t;\n\nbot_ichatdata_t\t*ichatdata[MAX_CLIENTS];\n\nbot_chatstate_t *botchatstates[MAX_CLIENTS+1];\n//console message heap\nbot_consolemessage_t *consolemessageheap = NULL;\nbot_consolemessage_t *freeconsolemessages = NULL;\n//list with match strings\nbot_matchtemplate_t *matchtemplates = NULL;\n//list with synonyms\nbot_synonymlist_t *synonyms = NULL;\n//list with random strings\nbot_randomlist_t *randomstrings = NULL;\n//reply chats\nbot_replychat_t *replychats = NULL;\n\n//========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//========================================================================\nbot_chatstate_t *BotChatStateFromHandle(int handle)\n{\n\tif (handle <= 0 || handle > MAX_CLIENTS)\n\t{\n\t\tbotimport.Print(PRT_FATAL, \"chat state handle %d out of range\\n\", handle);\n\t\treturn NULL;\n\t} //end if\n\tif (!botchatstates[handle])\n\t{\n\t\tbotimport.Print(PRT_FATAL, \"invalid chat state %d\\n\", handle);\n\t\treturn NULL;\n\t} //end if\n\treturn botchatstates[handle];\n} //end of the function BotChatStateFromHandle\n//===========================================================================\n// initialize the heap with unused console messages\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid InitConsoleMessageHeap(void)\n{\n\tint i, max_messages;\n\n\tif (consolemessageheap) FreeMemory(consolemessageheap);\n\t//\n\tmax_messages = (int) LibVarValue(\"max_messages\", \"1024\");\n\tconsolemessageheap = (bot_consolemessage_t *) GetClearedHunkMemory(max_messages *\n\t\t\t\t\t\t\t\t\t\t\t\tsizeof(bot_consolemessage_t));\n\tconsolemessageheap[0].prev = NULL;\n\tconsolemessageheap[0].next = &consolemessageheap[1];\n\tfor (i = 1; i < max_messages-1; i++)\n\t{\n\t\tconsolemessageheap[i].prev = &consolemessageheap[i - 1];\n\t\tconsolemessageheap[i].next = &consolemessageheap[i + 1];\n\t} //end for\n\tconsolemessageheap[max_messages-1].prev = &consolemessageheap[max_messages-2];\n\tconsolemessageheap[max_messages-1].next = NULL;\n\t//pointer to the free console messages\n\tfreeconsolemessages = consolemessageheap;\n} //end of the function InitConsoleMessageHeap\n//===========================================================================\n// allocate one console message from the heap\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nbot_consolemessage_t *AllocConsoleMessage(void)\n{\n\tbot_consolemessage_t *message;\n\tmessage = freeconsolemessages;\n\tif (freeconsolemessages) freeconsolemessages = freeconsolemessages->next;\n\tif (freeconsolemessages) freeconsolemessages->prev = NULL;\n\treturn message;\n} //end of the function AllocConsoleMessage\n//===========================================================================\n// deallocate one console message from the heap\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid FreeConsoleMessage(bot_consolemessage_t *message)\n{\n\tif (freeconsolemessages) freeconsolemessages->prev = message;\n\tmessage->prev = NULL;\n\tmessage->next = freeconsolemessages;\n\tfreeconsolemessages = message;\n} //end of the function FreeConsoleMessage\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid BotRemoveConsoleMessage(int chatstate, int handle)\n{\n\tbot_consolemessage_t *m, *nextm;\n\tbot_chatstate_t *cs;\n\n\tcs = BotChatStateFromHandle(chatstate);\n\tif (!cs) return;\n\n\tfor (m = cs->firstmessage; m; m = nextm)\n\t{\n\t\tnextm = m->next;\n\t\tif (m->handle == handle)\n\t\t{\n\t\t\tif (m->next) m->next->prev = m->prev;\n\t\t\telse cs->lastmessage = m->prev;\n\t\t\tif (m->prev) m->prev->next = m->next;\n\t\t\telse cs->firstmessage = m->next;\n\n\t\t\tFreeConsoleMessage(m);\n\t\t\tcs->numconsolemessages--;\n\t\t\tbreak;\n\t\t} //end if\n\t} //end for\n} //end of the function BotRemoveConsoleMessage\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid BotQueueConsoleMessage(int chatstate, int type, char *message)\n{\n\tbot_consolemessage_t *m;\n\tbot_chatstate_t *cs;\n\n\tcs = BotChatStateFromHandle(chatstate);\n\tif (!cs) return;\n\n\tm = AllocConsoleMessage();\n\tif (!m)\n\t{\n\t\tbotimport.Print(PRT_ERROR, \"empty console message heap\\n\");\n\t\treturn;\n\t} //end if\n\tcs->handle++;\n\tif (cs->handle <= 0 || cs->handle > 8192) cs->handle = 1;\n\tm->handle = cs->handle;\n\tm->time = AAS_Time();\n\tm->type = type;\n\tstrncpy(m->message, message, MAX_MESSAGE_SIZE-1);\n\tm->message[MAX_MESSAGE_SIZE-1] = 0;\n\tm->next = NULL;\n\tif (cs->lastmessage)\n\t{\n\t\tcs->lastmessage->next = m;\n\t\tm->prev = cs->lastmessage;\n\t\tcs->lastmessage = m;\n\t} //end if\n\telse\n\t{\n\t\tcs->lastmessage = m;\n\t\tcs->firstmessage = m;\n\t\tm->prev = NULL;\n\t} //end if\n\tcs->numconsolemessages++;\n} //end of the function BotQueueConsoleMessage\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint BotNextConsoleMessage(int chatstate, bot_consolemessage_t *cm)\n{\n\tbot_chatstate_t *cs;\n\tbot_consolemessage_t *firstmsg;\n\n\tcs = BotChatStateFromHandle(chatstate);\n\tif (!cs) return 0;\n\tif ((firstmsg = cs->firstmessage))\n\t{\n\t\tcm->handle = firstmsg->handle;\n\t\tcm->time = firstmsg->time;\n\t\tcm->type = firstmsg->type;\n\t\tQ_strncpyz(cm->message, firstmsg->message,\n\t\t\t   sizeof(cm->message));\n\t\t\n\t\t/* We omit setting the two pointers in cm because pointer\n\t\t * size in the VM differs between the size in the engine on\n\t\t * 64 bit machines, which would lead to a buffer overflow if\n\t\t * this functions is called from the VM. The pointers are\n\t\t * of no interest to functions calling\n\t\t * BotNextConsoleMessage anyways.\n\t\t */\n\t\t\n\t\treturn cm->handle;\n\t} //end if\n\treturn 0;\n} //end of the function BotConsoleMessage\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint BotNumConsoleMessages(int chatstate)\n{\n\tbot_chatstate_t *cs;\n\n\tcs = BotChatStateFromHandle(chatstate);\n\tif (!cs) return 0;\n\treturn cs->numconsolemessages;\n} //end of the function BotNumConsoleMessages\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint IsWhiteSpace(char c)\n{\n\tif ((c >= 'a' && c <= 'z')\n\t\t|| (c >= 'A' && c <= 'Z')\n\t\t|| (c >= '0' && c <= '9')\n\t\t|| c == '(' || c == ')'\n\t\t|| c == '?' || c == ':'\n\t\t|| c == '\\''|| c == '/'\n\t\t|| c == ',' || c == '.'\n\t\t|| c == '['\t|| c == ']'\n\t\t|| c == '-' || c == '_'\n\t\t|| c == '+' || c == '=') return qfalse;\n\treturn qtrue;\n} //end of the function IsWhiteSpace\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid BotRemoveTildes(char *message)\n{\n\tint i;\n\n\t//remove all tildes from the chat message\n\tfor (i = 0; message[i]; i++)\n\t{\n\t\tif (message[i] == '~')\n\t\t{\n\t\t\tmemmove(&message[i], &message[i+1], strlen(&message[i+1])+1);\n\t\t} //end if\n\t} //end for\n} //end of the function BotRemoveTildes\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid UnifyWhiteSpaces(char *string)\n{\n\tchar *ptr, *oldptr;\n\n\tfor (ptr = oldptr = string; *ptr; oldptr = ptr)\n\t{\n\t\twhile(*ptr && IsWhiteSpace(*ptr)) ptr++;\n\t\tif (ptr > oldptr)\n\t\t{\n\t\t\t//if not at the start and not at the end of the string\n\t\t\t//write only one space\n\t\t\tif (oldptr > string && *ptr) *oldptr++ = ' ';\n\t\t\t//remove all other white spaces\n\t\t\tif (ptr > oldptr) memmove(oldptr, ptr, strlen(ptr)+1);\n\t\t} //end if\n\t\twhile(*ptr && !IsWhiteSpace(*ptr)) ptr++;\n\t} //end while\n} //end of the function UnifyWhiteSpaces\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint StringContains(char *str1, char *str2, int casesensitive)\n{\n\tint len, i, j, index;\n\n\tif (str1 == NULL || str2 == NULL) return -1;\n\n\tlen = strlen(str1) - strlen(str2);\n\tindex = 0;\n\tfor (i = 0; i <= len; i++, str1++, index++)\n\t{\n\t\tfor (j = 0; str2[j]; j++)\n\t\t{\n\t\t\tif (casesensitive)\n\t\t\t{\n\t\t\t\tif (str1[j] != str2[j]) break;\n\t\t\t} //end if\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (toupper(str1[j]) != toupper(str2[j])) break;\n\t\t\t} //end else\n\t\t} //end for\n\t\tif (!str2[j]) return index;\n\t} //end for\n\treturn -1;\n} //end of the function StringContains\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nchar *StringContainsWord(char *str1, char *str2, int casesensitive)\n{\n\tint len, i, j;\n\n\tlen = strlen(str1) - strlen(str2);\n\tfor (i = 0; i <= len; i++, str1++)\n\t{\n\t\t//if not at the start of the string\n\t\tif (i)\n\t\t{\n\t\t\t//skip to the start of the next word\n\t\t\twhile(*str1 && *str1 != ' ' && *str1 != '.' && *str1 != ',' && *str1 != '!') str1++;\n\t\t\tif (!*str1) break;\n\t\t\tstr1++;\n\t\t} //end for\n\t\t//compare the word\n\t\tfor (j = 0; str2[j]; j++)\n\t\t{\n\t\t\tif (casesensitive)\n\t\t\t{\n\t\t\t\tif (str1[j] != str2[j]) break;\n\t\t\t} //end if\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (toupper(str1[j]) != toupper(str2[j])) break;\n\t\t\t} //end else\n\t\t} //end for\n\t\t//if there was a word match\n\t\tif (!str2[j])\n\t\t{\n\t\t\t//if the first string has an end of word\n\t\t\tif (!str1[j] || str1[j] == ' ' || str1[j] == '.' || str1[j] == ',' || str1[j] == '!') return str1;\n\t\t} //end if\n\t} //end for\n\treturn NULL;\n} //end of the function StringContainsWord\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid StringReplaceWords(char *string, char *synonym, char *replacement)\n{\n\tchar *str, *str2;\n\n\t//find the synonym in the string\n\tstr = StringContainsWord(string, synonym, qfalse);\n\t//if the synonym occured in the string\n\twhile(str)\n\t{\n\t\t//if the synonym isn't part of the replacement which is already in the string\n\t\t//usefull for abreviations\n\t\tstr2 = StringContainsWord(string, replacement, qfalse);\n\t\twhile(str2)\n\t\t{\n\t\t\tif (str2 <= str && str < str2 + strlen(replacement)) break;\n\t\t\tstr2 = StringContainsWord(str2+1, replacement, qfalse);\n\t\t} //end while\n\t\tif (!str2)\n\t\t{\n\t\t\tmemmove(str + strlen(replacement), str+strlen(synonym), strlen(str+strlen(synonym))+1);\n\t\t\t//append the synonum replacement\n\t\t\tCom_Memcpy(str, replacement, strlen(replacement));\n\t\t} //end if\n\t\t//find the next synonym in the string\n\t\tstr = StringContainsWord(str+strlen(replacement), synonym, qfalse);\n\t} //end if\n} //end of the function StringReplaceWords\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid BotDumpSynonymList(bot_synonymlist_t *synlist)\n{\n\tFILE *fp;\n\tbot_synonymlist_t *syn;\n\tbot_synonym_t *synonym;\n\n\tfp = Log_FilePointer();\n\tif (!fp) return;\n\tfor (syn = synlist; syn; syn = syn->next)\n\t{\n\t        fprintf(fp, \"%ld : [\", syn->context);\n\t\tfor (synonym = syn->firstsynonym; synonym; synonym = synonym->next)\n\t\t{\n\t\t\tfprintf(fp, \"(\\\"%s\\\", %1.2f)\", synonym->string, synonym->weight);\n\t\t\tif (synonym->next) fprintf(fp, \", \");\n\t\t} //end for\n\t\tfprintf(fp, \"]\\n\");\n\t} //end for\n} //end of the function BotDumpSynonymList\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nbot_synonymlist_t *BotLoadSynonyms(char *filename)\n{\n\tint pass, size, contextlevel, numsynonyms;\n\tunsigned long int context, contextstack[32];\n\tchar *ptr = NULL;\n\tsource_t *source;\n\ttoken_t token;\n\tbot_synonymlist_t *synlist, *lastsyn, *syn;\n\tbot_synonym_t *synonym, *lastsynonym;\n\n\tsize = 0;\n\tsynlist = NULL; //make compiler happy\n\tsyn = NULL; //make compiler happy\n\tsynonym = NULL; //make compiler happy\n\t//the synonyms are parsed in two phases\n\tfor (pass = 0; pass < 2; pass++)\n\t{\n\t\t//\n\t\tif (pass && size) ptr = (char *) GetClearedHunkMemory(size);\n\t\t//\n\t\tPC_SetBaseFolder(BOTFILESBASEFOLDER);\n\t\tsource = LoadSourceFile(filename);\n\t\tif (!source)\n\t\t{\n\t\t\tbotimport.Print(PRT_ERROR, \"counldn't load %s\\n\", filename);\n\t\t\treturn NULL;\n\t\t} //end if\n\t\t//\n\t\tcontext = 0;\n\t\tcontextlevel = 0;\n\t\tsynlist = NULL; //list synonyms\n\t\tlastsyn = NULL; //last synonym in the list\n\t\t//\n\t\twhile(PC_ReadToken(source, &token))\n\t\t{\n\t\t\tif (token.type == TT_NUMBER)\n\t\t\t{\n\t\t\t\tcontext |= token.intvalue;\n\t\t\t\tcontextstack[contextlevel] = token.intvalue;\n\t\t\t\tcontextlevel++;\n\t\t\t\tif (contextlevel >= 32)\n\t\t\t\t{\n\t\t\t\t\tSourceError(source, \"more than 32 context levels\");\n\t\t\t\t\tFreeSource(source);\n\t\t\t\t\treturn NULL;\n\t\t\t\t} //end if\n\t\t\t\tif (!PC_ExpectTokenString(source, \"{\"))\n\t\t\t\t{\n\t\t\t\t\tFreeSource(source);\n\t\t\t\t\treturn NULL;\n\t\t\t\t} //end if\n\t\t\t} //end if\n\t\t\telse if (token.type == TT_PUNCTUATION)\n\t\t\t{\n\t\t\t\tif (!strcmp(token.string, \"}\"))\n\t\t\t\t{\n\t\t\t\t\tcontextlevel--;\n\t\t\t\t\tif (contextlevel < 0)\n\t\t\t\t\t{\n\t\t\t\t\t\tSourceError(source, \"too many }\");\n\t\t\t\t\t\tFreeSource(source);\n\t\t\t\t\t\treturn NULL;\n\t\t\t\t\t} //end if\n\t\t\t\t\tcontext &= ~contextstack[contextlevel];\n\t\t\t\t} //end if\n\t\t\t\telse if (!strcmp(token.string, \"[\"))\n\t\t\t\t{\n\t\t\t\t\tsize += sizeof(bot_synonymlist_t);\n\t\t\t\t\tif (pass)\n\t\t\t\t\t{\n\t\t\t\t\t\tsyn = (bot_synonymlist_t *) ptr;\n\t\t\t\t\t\tptr += sizeof(bot_synonymlist_t);\n\t\t\t\t\t\tsyn->context = context;\n\t\t\t\t\t\tsyn->firstsynonym = NULL;\n\t\t\t\t\t\tsyn->next = NULL;\n\t\t\t\t\t\tif (lastsyn) lastsyn->next = syn;\n\t\t\t\t\t\telse synlist = syn;\n\t\t\t\t\t\tlastsyn = syn;\n\t\t\t\t\t} //end if\n\t\t\t\t\tnumsynonyms = 0;\n\t\t\t\t\tlastsynonym = NULL;\n\t\t\t\t\twhile(1)\n\t\t\t\t\t{\n\t\t\t\t\t\tsize_t len;\n\t\t\t\t\t\tif (!PC_ExpectTokenString(source, \"(\") ||\n\t\t\t\t\t\t\t!PC_ExpectTokenType(source, TT_STRING, 0, &token))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tFreeSource(source);\n\t\t\t\t\t\t\treturn NULL;\n\t\t\t\t\t\t} //end if\n\t\t\t\t\t\tStripDoubleQuotes(token.string);\n\t\t\t\t\t\tif (strlen(token.string) <= 0)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tSourceError(source, \"empty string\", token.string);\n\t\t\t\t\t\t\tFreeSource(source);\n\t\t\t\t\t\t\treturn NULL;\n\t\t\t\t\t\t} //end if\n\t\t\t\t\t\tlen = strlen(token.string) + 1;\n\t\t\t\t\t\tlen = PAD(len, sizeof(long));\n\t\t\t\t\t\tsize += sizeof(bot_synonym_t) + len;\n\t\t\t\t\t\tif (pass)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tsynonym = (bot_synonym_t *) ptr;\n\t\t\t\t\t\t\tptr += sizeof(bot_synonym_t);\n\t\t\t\t\t\t\tsynonym->string = ptr;\n\t\t\t\t\t\t\tptr += len;\n\t\t\t\t\t\t\tstrcpy(synonym->string, token.string);\n\t\t\t\t\t\t\t//\n\t\t\t\t\t\t\tif (lastsynonym) lastsynonym->next = synonym;\n\t\t\t\t\t\t\telse syn->firstsynonym = synonym;\n\t\t\t\t\t\t\tlastsynonym = synonym;\n\t\t\t\t\t\t} //end if\n\t\t\t\t\t\tnumsynonyms++;\n\t\t\t\t\t\tif (!PC_ExpectTokenString(source, \",\") ||\n\t\t\t\t\t\t\t!PC_ExpectTokenType(source, TT_NUMBER, 0, &token) ||\n\t\t\t\t\t\t\t!PC_ExpectTokenString(source, \")\"))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tFreeSource(source);\n\t\t\t\t\t\t\treturn NULL;\n\t\t\t\t\t\t} //end if\n\t\t\t\t\t\tif (pass)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tsynonym->weight = token.floatvalue;\n\t\t\t\t\t\t\tsyn->totalweight += synonym->weight;\n\t\t\t\t\t\t} //end if\n\t\t\t\t\t\tif (PC_CheckTokenString(source, \"]\")) break;\n\t\t\t\t\t\tif (!PC_ExpectTokenString(source, \",\"))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tFreeSource(source);\n\t\t\t\t\t\t\treturn NULL;\n\t\t\t\t\t\t} //end if\n\t\t\t\t\t} //end while\n\t\t\t\t\tif (numsynonyms < 2)\n\t\t\t\t\t{\n\t\t\t\t\t\tSourceError(source, \"synonym must have at least two entries\\n\");\n\t\t\t\t\t\tFreeSource(source);\n\t\t\t\t\t\treturn NULL;\n\t\t\t\t\t} //end if\n\t\t\t\t} //end else\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tSourceError(source, \"unexpected %s\", token.string);\n\t\t\t\t\tFreeSource(source);\n\t\t\t\t\treturn NULL;\n\t\t\t\t} //end if\n\t\t\t} //end else if\n\t\t} //end while\n\t\t//\n\t\tFreeSource(source);\n\t\t//\n\t\tif (contextlevel > 0)\n\t\t{\n\t\t\tSourceError(source, \"missing }\");\n\t\t\treturn NULL;\n\t\t} //end if\n\t} //end for\n\tbotimport.Print(PRT_MESSAGE, \"loaded %s\\n\", filename);\n\t//\n\t//BotDumpSynonymList(synlist);\n\t//\n\treturn synlist;\n} //end of the function BotLoadSynonyms\n//===========================================================================\n// replace all the synonyms in the string\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid BotReplaceSynonyms(char *string, unsigned long int context)\n{\n\tbot_synonymlist_t *syn;\n\tbot_synonym_t *synonym;\n\n\tfor (syn = synonyms; syn; syn = syn->next)\n\t{\n\t\tif (!(syn->context & context)) continue;\n\t\tfor (synonym = syn->firstsynonym->next; synonym; synonym = synonym->next)\n\t\t{\n\t\t\tStringReplaceWords(string, synonym->string, syn->firstsynonym->string);\n\t\t} //end for\n\t} //end for\n} //end of the function BotReplaceSynonyms\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid BotReplaceWeightedSynonyms(char *string, unsigned long int context)\n{\n\tbot_synonymlist_t *syn;\n\tbot_synonym_t *synonym, *replacement;\n\tfloat weight, curweight;\n\n\tfor (syn = synonyms; syn; syn = syn->next)\n\t{\n\t\tif (!(syn->context & context)) continue;\n\t\t//choose a weighted random replacement synonym\n\t\tweight = random() * syn->totalweight;\n\t\tif (!weight) continue;\n\t\tcurweight = 0;\n\t\tfor (replacement = syn->firstsynonym; replacement; replacement = replacement->next)\n\t\t{\n\t\t\tcurweight += replacement->weight;\n\t\t\tif (weight < curweight) break;\n\t\t} //end for\n\t\tif (!replacement) continue;\n\t\t//replace all synonyms with the replacement\n\t\tfor (synonym = syn->firstsynonym; synonym; synonym = synonym->next)\n\t\t{\n\t\t\tif (synonym == replacement) continue;\n\t\t\tStringReplaceWords(string, synonym->string, replacement->string);\n\t\t} //end for\n\t} //end for\n} //end of the function BotReplaceWeightedSynonyms\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid BotReplaceReplySynonyms(char *string, unsigned long int context)\n{\n\tchar *str1, *str2, *replacement;\n\tbot_synonymlist_t *syn;\n\tbot_synonym_t *synonym;\n\n\tfor (str1 = string; *str1; )\n\t{\n\t\t//go to the start of the next word\n\t\twhile(*str1 && *str1 <= ' ') str1++;\n\t\tif (!*str1) break;\n\t\t//\n\t\tfor (syn = synonyms; syn; syn = syn->next)\n\t\t{\n\t\t\tif (!(syn->context & context)) continue;\n\t\t\tfor (synonym = syn->firstsynonym->next; synonym; synonym = synonym->next)\n\t\t\t{\n\t\t\t\tstr2 = synonym->string;\n\t\t\t\t//if the synonym is not at the front of the string continue\n\t\t\t\tstr2 = StringContainsWord(str1, synonym->string, qfalse);\n\t\t\t\tif (!str2 || str2 != str1) continue;\n\t\t\t\t//\n\t\t\t\treplacement = syn->firstsynonym->string;\n\t\t\t\t//if the replacement IS in front of the string continue\n\t\t\t\tstr2 = StringContainsWord(str1, replacement, qfalse);\n\t\t\t\tif (str2 && str2 == str1) continue;\n\t\t\t\t//\n\t\t\t\tmemmove(str1 + strlen(replacement), str1+strlen(synonym->string),\n\t\t\t\t\t\t\tstrlen(str1+strlen(synonym->string)) + 1);\n\t\t\t\t//append the synonum replacement\n\t\t\t\tCom_Memcpy(str1, replacement, strlen(replacement));\n\t\t\t\t//\n\t\t\t\tbreak;\n\t\t\t} //end for\n\t\t\t//if a synonym has been replaced\n\t\t\tif (synonym) break;\n\t\t} //end for\n\t\t//skip over this word\n\t\twhile(*str1 && *str1 > ' ') str1++;\n\t\tif (!*str1) break;\n\t} //end while\n} //end of the function BotReplaceReplySynonyms\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint BotLoadChatMessage(source_t *source, char *chatmessagestring)\n{\n\tchar *ptr;\n\ttoken_t token;\n\n\tptr = chatmessagestring;\n\t*ptr = 0;\n\t//\n\twhile(1)\n\t{\n\t\tif (!PC_ExpectAnyToken(source, &token)) return qfalse;\n\t\t//fixed string\n\t\tif (token.type == TT_STRING)\n\t\t{\n\t\t\tStripDoubleQuotes(token.string);\n\t\t\tif (strlen(ptr) + strlen(token.string) + 1 > MAX_MESSAGE_SIZE)\n\t\t\t{\n\t\t\t\tSourceError(source, \"chat message too long\\n\");\n\t\t\t\treturn qfalse;\n\t\t\t} //end if\n\t\t\tstrcat(ptr, token.string);\n\t\t} //end else if\n\t\t//variable string\n\t\telse if (token.type == TT_NUMBER && (token.subtype & TT_INTEGER))\n\t\t{\n\t\t\tif (strlen(ptr) + 7 > MAX_MESSAGE_SIZE)\n\t\t\t{\n\t\t\t\tSourceError(source, \"chat message too long\\n\");\n\t\t\t\treturn qfalse;\n\t\t\t} //end if\n\t\t\tsprintf(&ptr[strlen(ptr)], \"%cv%ld%c\", ESCAPE_CHAR, token.intvalue, ESCAPE_CHAR);\n\t\t} //end if\n\t\t//random string\n\t\telse if (token.type == TT_NAME)\n\t\t{\n\t\t\tif (strlen(ptr) + 7 > MAX_MESSAGE_SIZE)\n\t\t\t{\n\t\t\t\tSourceError(source, \"chat message too long\\n\");\n\t\t\t\treturn qfalse;\n\t\t\t} //end if\n\t\t\tsprintf(&ptr[strlen(ptr)], \"%cr%s%c\", ESCAPE_CHAR, token.string, ESCAPE_CHAR);\n\t\t} //end else if\n\t\telse\n\t\t{\n\t\t\tSourceError(source, \"unknown message component %s\\n\", token.string);\n\t\t\treturn qfalse;\n\t\t} //end else\n\t\tif (PC_CheckTokenString(source, \";\")) break;\n\t\tif (!PC_ExpectTokenString(source, \",\")) return qfalse;\n\t} //end while\n\t//\n\treturn qtrue;\n} //end of the function BotLoadChatMessage\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid BotDumpRandomStringList(bot_randomlist_t *randomlist)\n{\n\tFILE *fp;\n\tbot_randomlist_t *random;\n\tbot_randomstring_t *rs;\n\n\tfp = Log_FilePointer();\n\tif (!fp) return;\n\tfor (random = randomlist; random; random = random->next)\n\t{\n\t\tfprintf(fp, \"%s = {\", random->string);\n\t\tfor (rs = random->firstrandomstring; rs; rs = rs->next)\n\t\t{\n\t\t\tfprintf(fp, \"\\\"%s\\\"\", rs->string);\n\t\t\tif (rs->next) fprintf(fp, \", \");\n\t\t\telse fprintf(fp, \"}\\n\");\n\t\t} //end for\n\t} //end for\n} //end of the function BotDumpRandomStringList\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nbot_randomlist_t *BotLoadRandomStrings(char *filename)\n{\n\tint pass, size;\n\tchar *ptr = NULL, chatmessagestring[MAX_MESSAGE_SIZE];\n\tsource_t *source;\n\ttoken_t token;\n\tbot_randomlist_t *randomlist, *lastrandom, *random;\n\tbot_randomstring_t *randomstring;\n\n#ifdef DEBUG\n\tint starttime = Sys_MilliSeconds();\n#endif //DEBUG\n\n\tsize = 0;\n\trandomlist = NULL;\n\trandom = NULL;\n\t//the synonyms are parsed in two phases\n\tfor (pass = 0; pass < 2; pass++)\n\t{\n\t\t//\n\t\tif (pass && size) ptr = (char *) GetClearedHunkMemory(size);\n\t\t//\n\t\tPC_SetBaseFolder(BOTFILESBASEFOLDER);\n\t\tsource = LoadSourceFile(filename);\n\t\tif (!source)\n\t\t{\n\t\t\tbotimport.Print(PRT_ERROR, \"counldn't load %s\\n\", filename);\n\t\t\treturn NULL;\n\t\t} //end if\n\t\t//\n\t\trandomlist = NULL; //list\n\t\tlastrandom = NULL; //last\n\t\t//\n\t\twhile(PC_ReadToken(source, &token))\n\t\t{\n\t\t\tsize_t len;\n\t\t\tif (token.type != TT_NAME)\n\t\t\t{\n\t\t\t\tSourceError(source, \"unknown random %s\", token.string);\n\t\t\t\tFreeSource(source);\n\t\t\t\treturn NULL;\n\t\t\t} //end if\n\t\t\tlen = strlen(token.string) + 1;\n\t\t\tlen = PAD(len, sizeof(long));\n\t\t\tsize += sizeof(bot_randomlist_t) + len;\n\t\t\tif (pass)\n\t\t\t{\n\t\t\t\trandom = (bot_randomlist_t *) ptr;\n\t\t\t\tptr += sizeof(bot_randomlist_t);\n\t\t\t\trandom->string = ptr;\n\t\t\t\tptr += len;\n\t\t\t\tstrcpy(random->string, token.string);\n\t\t\t\trandom->firstrandomstring = NULL;\n\t\t\t\trandom->numstrings = 0;\n\t\t\t\t//\n\t\t\t\tif (lastrandom) lastrandom->next = random;\n\t\t\t\telse randomlist = random;\n\t\t\t\tlastrandom = random;\n\t\t\t} //end if\n\t\t\tif (!PC_ExpectTokenString(source, \"=\") ||\n\t\t\t\t!PC_ExpectTokenString(source, \"{\"))\n\t\t\t{\n\t\t\t\tFreeSource(source);\n\t\t\t\treturn NULL;\n\t\t\t} //end if\n\t\t\twhile(!PC_CheckTokenString(source, \"}\"))\n\t\t\t{\n\t\t\t\tsize_t len;\n\t\t\t\tif (!BotLoadChatMessage(source, chatmessagestring))\n\t\t\t\t{\n\t\t\t\t\tFreeSource(source);\n\t\t\t\t\treturn NULL;\n\t\t\t\t} //end if\n\t\t\t\tlen = strlen(chatmessagestring) + 1;\n\t\t\t\tlen = PAD(len, sizeof(long));\n\t\t\t\tsize += sizeof(bot_randomstring_t) + len;\n\t\t\t\tif (pass)\n\t\t\t\t{\n\t\t\t\t\trandomstring = (bot_randomstring_t *) ptr;\n\t\t\t\t\tptr += sizeof(bot_randomstring_t);\n\t\t\t\t\trandomstring->string = ptr;\n\t\t\t\t\tptr += len;\n\t\t\t\t\tstrcpy(randomstring->string, chatmessagestring);\n\t\t\t\t\t//\n\t\t\t\t\trandom->numstrings++;\n\t\t\t\t\trandomstring->next = random->firstrandomstring;\n\t\t\t\t\trandom->firstrandomstring = randomstring;\n\t\t\t\t} //end if\n\t\t\t} //end while\n\t\t} //end while\n\t\t//free the source after one pass\n\t\tFreeSource(source);\n\t} //end for\n\tbotimport.Print(PRT_MESSAGE, \"loaded %s\\n\", filename);\n\t//\n#ifdef DEBUG\n\tbotimport.Print(PRT_MESSAGE, \"random strings %d msec\\n\", Sys_MilliSeconds() - starttime);\n\t//BotDumpRandomStringList(randomlist);\n#endif //DEBUG\n\t//\n\treturn randomlist;\n} //end of the function BotLoadRandomStrings\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nchar *RandomString(char *name)\n{\n\tbot_randomlist_t *random;\n\tbot_randomstring_t *rs;\n\tint i;\n\n\tfor (random = randomstrings; random; random = random->next)\n\t{\n\t\tif (!strcmp(random->string, name))\n\t\t{\n\t\t\ti = random() * random->numstrings;\n\t\t\tfor (rs = random->firstrandomstring; rs; rs = rs->next)\n\t\t\t{\n\t\t\t\tif (--i < 0) break;\n\t\t\t} //end for\n\t\t\tif (rs)\n\t\t\t{\n\t\t\t\treturn rs->string;\n\t\t\t} //end if\n\t\t} //end for\n\t} //end for\n\treturn NULL;\n} //end of the function RandomString\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid BotDumpMatchTemplates(bot_matchtemplate_t *matches)\n{\n\tFILE *fp;\n\tbot_matchtemplate_t *mt;\n\tbot_matchpiece_t *mp;\n\tbot_matchstring_t *ms;\n\n\tfp = Log_FilePointer();\n\tif (!fp) return;\n\tfor (mt = matches; mt; mt = mt->next)\n\t{\n\t        fprintf(fp, \"{ \" );\n\t\tfor (mp = mt->first; mp; mp = mp->next)\n\t\t{\n\t\t\tif (mp->type == MT_STRING)\n\t\t\t{\n\t\t\t\tfor (ms = mp->firststring; ms; ms = ms->next)\n\t\t\t\t{\n\t\t\t\t\tfprintf(fp, \"\\\"%s\\\"\", ms->string);\n\t\t\t\t\tif (ms->next) fprintf(fp, \"|\");\n\t\t\t\t} //end for\n\t\t\t} //end if\n\t\t\telse if (mp->type == MT_VARIABLE)\n\t\t\t{\n\t\t\t\tfprintf(fp, \"%d\", mp->variable);\n\t\t\t} //end else if\n\t\t\tif (mp->next) fprintf(fp, \", \");\n\t\t} //end for\n\t\tfprintf(fp, \" = (%d, %d);}\\n\", mt->type, mt->subtype);\n\t} //end for\n} //end of the function BotDumpMatchTemplates\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid BotFreeMatchPieces(bot_matchpiece_t *matchpieces)\n{\n\tbot_matchpiece_t *mp, *nextmp;\n\tbot_matchstring_t *ms, *nextms;\n\n\tfor (mp = matchpieces; mp; mp = nextmp)\n\t{\n\t\tnextmp = mp->next;\n\t\tif (mp->type == MT_STRING)\n\t\t{\n\t\t\tfor (ms = mp->firststring; ms; ms = nextms)\n\t\t\t{\n\t\t\t\tnextms = ms->next;\n\t\t\t\tFreeMemory(ms);\n\t\t\t} //end for\n\t\t} //end if\n\t\tFreeMemory(mp);\n\t} //end for\n} //end of the function BotFreeMatchPieces\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nbot_matchpiece_t *BotLoadMatchPieces(source_t *source, char *endtoken)\n{\n\tint lastwasvariable, emptystring;\n\ttoken_t token;\n\tbot_matchpiece_t *matchpiece, *firstpiece, *lastpiece;\n\tbot_matchstring_t *matchstring, *lastmatchstring;\n\n\tfirstpiece = NULL;\n\tlastpiece = NULL;\n\t//\n\tlastwasvariable = qfalse;\n\t//\n\twhile(PC_ReadToken(source, &token))\n\t{\n\t\tif (token.type == TT_NUMBER && (token.subtype & TT_INTEGER))\n\t\t{\n\t\t\tif (token.intvalue < 0 || token.intvalue >= MAX_MATCHVARIABLES)\n\t\t\t{\n\t\t\t\tSourceError(source, \"can't have more than %d match variables\\n\", MAX_MATCHVARIABLES);\n\t\t\t\tFreeSource(source);\n\t\t\t\tBotFreeMatchPieces(firstpiece);\n\t\t\t\treturn NULL;\n\t\t\t} //end if\n\t\t\tif (lastwasvariable)\n\t\t\t{\n\t\t\t\tSourceError(source, \"not allowed to have adjacent variables\\n\");\n\t\t\t\tFreeSource(source);\n\t\t\t\tBotFreeMatchPieces(firstpiece);\n\t\t\t\treturn NULL;\n\t\t\t} //end if\n\t\t\tlastwasvariable = qtrue;\n\t\t\t//\n\t\t\tmatchpiece = (bot_matchpiece_t *) GetClearedHunkMemory(sizeof(bot_matchpiece_t));\n\t\t\tmatchpiece->type = MT_VARIABLE;\n\t\t\tmatchpiece->variable = token.intvalue;\n\t\t\tmatchpiece->next = NULL;\n\t\t\tif (lastpiece) lastpiece->next = matchpiece;\n\t\t\telse firstpiece = matchpiece;\n\t\t\tlastpiece = matchpiece;\n\t\t} //end if\n\t\telse if (token.type == TT_STRING)\n\t\t{\n\t\t\t//\n\t\t\tmatchpiece = (bot_matchpiece_t *) GetClearedHunkMemory(sizeof(bot_matchpiece_t));\n\t\t\tmatchpiece->firststring = NULL;\n\t\t\tmatchpiece->type = MT_STRING;\n\t\t\tmatchpiece->variable = 0;\n\t\t\tmatchpiece->next = NULL;\n\t\t\tif (lastpiece) lastpiece->next = matchpiece;\n\t\t\telse firstpiece = matchpiece;\n\t\t\tlastpiece = matchpiece;\n\t\t\t//\n\t\t\tlastmatchstring = NULL;\n\t\t\temptystring = qfalse;\n\t\t\t//\n\t\t\tdo\n\t\t\t{\n\t\t\t\tif (matchpiece->firststring)\n\t\t\t\t{\n\t\t\t\t\tif (!PC_ExpectTokenType(source, TT_STRING, 0, &token))\n\t\t\t\t\t{\n\t\t\t\t\t\tFreeSource(source);\n\t\t\t\t\t\tBotFreeMatchPieces(firstpiece);\n\t\t\t\t\t\treturn NULL;\n\t\t\t\t\t} //end if\n\t\t\t\t} //end if\n\t\t\t\tStripDoubleQuotes(token.string);\n\t\t\t\tmatchstring = (bot_matchstring_t *) GetClearedHunkMemory(sizeof(bot_matchstring_t) + strlen(token.string) + 1);\n\t\t\t\tmatchstring->string = (char *) matchstring + sizeof(bot_matchstring_t);\n\t\t\t\tstrcpy(matchstring->string, token.string);\n\t\t\t\tif (!strlen(token.string)) emptystring = qtrue;\n\t\t\t\tmatchstring->next = NULL;\n\t\t\t\tif (lastmatchstring) lastmatchstring->next = matchstring;\n\t\t\t\telse matchpiece->firststring = matchstring;\n\t\t\t\tlastmatchstring = matchstring;\n\t\t\t} while(PC_CheckTokenString(source, \"|\"));\n\t\t\t//if there was no empty string found\n\t\t\tif (!emptystring) lastwasvariable = qfalse;\n\t\t} //end if\n\t\telse\n\t\t{\n\t\t\tSourceError(source, \"invalid token %s\\n\", token.string);\n\t\t\tFreeSource(source);\n\t\t\tBotFreeMatchPieces(firstpiece);\n\t\t\treturn NULL;\n\t\t} //end else\n\t\tif (PC_CheckTokenString(source, endtoken)) break;\n\t\tif (!PC_ExpectTokenString(source, \",\"))\n\t\t{\n\t\t\tFreeSource(source);\n\t\t\tBotFreeMatchPieces(firstpiece);\n\t\t\treturn NULL;\n\t\t} //end if\n\t} //end while\n\treturn firstpiece;\n} //end of the function BotLoadMatchPieces\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid BotFreeMatchTemplates(bot_matchtemplate_t *mt)\n{\n\tbot_matchtemplate_t *nextmt;\n\n\tfor (; mt; mt = nextmt)\n\t{\n\t\tnextmt = mt->next;\n\t\tBotFreeMatchPieces(mt->first);\n\t\tFreeMemory(mt);\n\t} //end for\n} //end of the function BotFreeMatchTemplates\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nbot_matchtemplate_t *BotLoadMatchTemplates(char *matchfile)\n{\n\tsource_t *source;\n\ttoken_t token;\n\tbot_matchtemplate_t *matchtemplate, *matches, *lastmatch;\n\tunsigned long int context;\n\n\tPC_SetBaseFolder(BOTFILESBASEFOLDER);\n\tsource = LoadSourceFile(matchfile);\n\tif (!source)\n\t{\n\t\tbotimport.Print(PRT_ERROR, \"counldn't load %s\\n\", matchfile);\n\t\treturn NULL;\n\t} //end if\n\t//\n\tmatches = NULL; //list with matches\n\tlastmatch = NULL; //last match in the list\n\n\twhile(PC_ReadToken(source, &token))\n\t{\n\t\tif (token.type != TT_NUMBER || !(token.subtype & TT_INTEGER))\n\t\t{\n\t\t\tSourceError(source, \"expected integer, found %s\\n\", token.string);\n\t\t\tBotFreeMatchTemplates(matches);\n\t\t\tFreeSource(source);\n\t\t\treturn NULL;\n\t\t} //end if\n\t\t//the context\n\t\tcontext = token.intvalue;\n\t\t//\n\t\tif (!PC_ExpectTokenString(source, \"{\"))\n\t\t{\n\t\t\tBotFreeMatchTemplates(matches);\n\t\t\tFreeSource(source);\n\t\t\treturn NULL;\n\t\t} //end if\n\t\t//\n\t\twhile(PC_ReadToken(source, &token))\n\t\t{\n\t\t\tif (!strcmp(token.string, \"}\")) break;\n\t\t\t//\n\t\t\tPC_UnreadLastToken(source);\n\t\t\t//\n\t\t\tmatchtemplate = (bot_matchtemplate_t *) GetClearedHunkMemory(sizeof(bot_matchtemplate_t));\n\t\t\tmatchtemplate->context = context;\n\t\t\tmatchtemplate->next = NULL;\n\t\t\t//add the match template to the list\n\t\t\tif (lastmatch) lastmatch->next = matchtemplate;\n\t\t\telse matches = matchtemplate;\n\t\t\tlastmatch = matchtemplate;\n\t\t\t//load the match template\n\t\t\tmatchtemplate->first = BotLoadMatchPieces(source, \"=\");\n\t\t\tif (!matchtemplate->first)\n\t\t\t{\n\t\t\t\tBotFreeMatchTemplates(matches);\n\t\t\t\treturn NULL;\n\t\t\t} //end if\n\t\t\t//read the match type\n\t\t\tif (!PC_ExpectTokenString(source, \"(\") ||\n\t\t\t\t!PC_ExpectTokenType(source, TT_NUMBER, TT_INTEGER, &token))\n\t\t\t{\n\t\t\t\tBotFreeMatchTemplates(matches);\n\t\t\t\tFreeSource(source);\n\t\t\t\treturn NULL;\n\t\t\t} //end if\n\t\t\tmatchtemplate->type = token.intvalue;\n\t\t\t//read the match subtype\n\t\t\tif (!PC_ExpectTokenString(source, \",\") ||\n\t\t\t\t!PC_ExpectTokenType(source, TT_NUMBER, TT_INTEGER, &token))\n\t\t\t{\n\t\t\t\tBotFreeMatchTemplates(matches);\n\t\t\t\tFreeSource(source);\n\t\t\t\treturn NULL;\n\t\t\t} //end if\n\t\t\tmatchtemplate->subtype = token.intvalue;\n\t\t\t//read trailing punctuations\n\t\t\tif (!PC_ExpectTokenString(source, \")\") ||\n\t\t\t\t!PC_ExpectTokenString(source, \";\"))\n\t\t\t{\n\t\t\t\tBotFreeMatchTemplates(matches);\n\t\t\t\tFreeSource(source);\n\t\t\t\treturn NULL;\n\t\t\t} //end if\n\t\t} //end while\n\t} //end while\n\t//free the source\n\tFreeSource(source);\n\tbotimport.Print(PRT_MESSAGE, \"loaded %s\\n\", matchfile);\n\t//\n\t//BotDumpMatchTemplates(matches);\n\t//\n\treturn matches;\n} //end of the function BotLoadMatchTemplates\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint StringsMatch(bot_matchpiece_t *pieces, bot_match_t *match)\n{\n\tint lastvariable, index;\n\tchar *strptr, *newstrptr;\n\tbot_matchpiece_t *mp;\n\tbot_matchstring_t *ms;\n\n\t//no last variable\n\tlastvariable = -1;\n\t//pointer to the string to compare the match string with\n\tstrptr = match->string;\n\t//Log_Write(\"match: %s\", strptr);\n\t//compare the string with the current match string\n\tfor (mp = pieces; mp; mp = mp->next)\n\t{\n\t\t//if it is a piece of string\n\t\tif (mp->type == MT_STRING)\n\t\t{\n\t\t\tnewstrptr = NULL;\n\t\t\tfor (ms = mp->firststring; ms; ms = ms->next)\n\t\t\t{\n\t\t\t\tif (!strlen(ms->string))\n\t\t\t\t{\n\t\t\t\t\tnewstrptr = strptr;\n\t\t\t\t\tbreak;\n\t\t\t\t} //end if\n\t\t\t\t//Log_Write(\"MT_STRING: %s\", mp->string);\n\t\t\t\tindex = StringContains(strptr, ms->string, qfalse);\n\t\t\t\tif (index >= 0)\n\t\t\t\t{\n\t\t\t\t\tnewstrptr = strptr + index;\n\t\t\t\t\tif (lastvariable >= 0)\n\t\t\t\t\t{\n\t\t\t\t\t\tmatch->variables[lastvariable].length =\n\t\t\t\t\t\t\t\t(newstrptr - match->string) - match->variables[lastvariable].offset;\n\t\t\t\t\t\t\t\t//newstrptr - match->variables[lastvariable].ptr;\n\t\t\t\t\t\tlastvariable = -1;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t} //end if\n\t\t\t\t\telse if (index == 0)\n\t\t\t\t\t{\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t} //end else\n\t\t\t\t\tnewstrptr = NULL;\n\t\t\t\t} //end if\n\t\t\t} //end for\n\t\t\tif (!newstrptr) return qfalse;\n\t\t\tstrptr = newstrptr + strlen(ms->string);\n\t\t} //end if\n\t\t//if it is a variable piece of string\n\t\telse if (mp->type == MT_VARIABLE)\n\t\t{\n\t\t\t//Log_Write(\"MT_VARIABLE\");\n\t\t\tmatch->variables[mp->variable].offset = strptr - match->string;\n\t\t\tlastvariable = mp->variable;\n\t\t} //end else if\n\t} //end for\n\t//if a match was found\n\tif (!mp && (lastvariable >= 0 || !strlen(strptr)))\n\t{\n\t\t//if the last piece was a variable string\n\t\tif (lastvariable >= 0)\n\t\t{\n        \t\tassert( match->variables[lastvariable].offset >= 0 );\n\t\t\tmatch->variables[lastvariable].length =\n\t\t\t\tstrlen(&match->string[ (int) match->variables[lastvariable].offset]);\n\t\t} //end if\n\t\treturn qtrue;\n\t} //end if\n\treturn qfalse;\n} //end of the function StringsMatch\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint BotFindMatch(char *str, bot_match_t *match, unsigned long int context)\n{\n\tint i;\n\tbot_matchtemplate_t *ms;\n\n\tstrncpy(match->string, str, MAX_MESSAGE_SIZE-1);\n\tmatch->string[MAX_MESSAGE_SIZE-1] = 0;\n\t//remove any trailing enters\n\twhile(strlen(match->string) &&\n\t\t\tmatch->string[strlen(match->string)-1] == '\\n')\n\t{\n\t\tmatch->string[strlen(match->string)-1] = '\\0';\n\t} //end while\n\t//compare the string with all the match strings\n\tfor (ms = matchtemplates; ms; ms = ms->next)\n\t{\n\t\tif (!(ms->context & context)) continue;\n\t\t//reset the match variable offsets\n\t\tfor (i = 0; i < MAX_MATCHVARIABLES; i++) match->variables[i].offset = -1;\n\t\t//\n\t\tif (StringsMatch(ms->first, match))\n\t\t{\n\t\t\tmatch->type = ms->type;\n\t\t\tmatch->subtype = ms->subtype;\n\t\t\treturn qtrue;\n\t\t} //end if\n\t} //end for\n\treturn qfalse;\n} //end of the function BotFindMatch\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid BotMatchVariable(bot_match_t *match, int variable, char *buf, int size)\n{\n\tif (variable < 0 || variable >= MAX_MATCHVARIABLES)\n\t{\n\t\tbotimport.Print(PRT_FATAL, \"BotMatchVariable: variable out of range\\n\");\n\t\tstrcpy(buf, \"\");\n\t\treturn;\n\t} //end if\n\n\tif (match->variables[variable].offset >= 0)\n\t{\n\t\tif (match->variables[variable].length < size)\n\t\t\tsize = match->variables[variable].length+1;\n\t\tassert( match->variables[variable].offset >= 0 );\n\t\tstrncpy(buf, &match->string[ (int) match->variables[variable].offset], size-1);\n\t\tbuf[size-1] = '\\0';\n\t} //end if\n\telse\n\t{\n\t\tstrcpy(buf, \"\");\n\t} //end else\n\treturn;\n} //end of the function BotMatchVariable\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nbot_stringlist_t *BotFindStringInList(bot_stringlist_t *list, char *string)\n{\n\tbot_stringlist_t *s;\n\n\tfor (s = list; s; s = s->next)\n\t{\n\t\tif (!strcmp(s->string, string)) return s;\n\t} //end for\n\treturn NULL;\n} //end of the function BotFindStringInList\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nbot_stringlist_t *BotCheckChatMessageIntegrety(char *message, bot_stringlist_t *stringlist)\n{\n\tint i;\n\tchar *msgptr;\n\tchar temp[MAX_MESSAGE_SIZE];\n\tbot_stringlist_t *s;\n\n\tmsgptr = message;\n\t//\n\twhile(*msgptr)\n\t{\n\t\tif (*msgptr == ESCAPE_CHAR)\n\t\t{\n\t\t\tmsgptr++;\n\t\t\tswitch(*msgptr)\n\t\t\t{\n\t\t\t\tcase 'v': //variable\n\t\t\t\t{\n\t\t\t\t\t//step over the 'v'\n\t\t\t\t\tmsgptr++;\n\t\t\t\t\twhile(*msgptr && *msgptr != ESCAPE_CHAR) msgptr++;\n\t\t\t\t\t//step over the trailing escape char\n\t\t\t\t\tif (*msgptr) msgptr++;\n\t\t\t\t\tbreak;\n\t\t\t\t} //end case\n\t\t\t\tcase 'r': //random\n\t\t\t\t{\n\t\t\t\t\t//step over the 'r'\n\t\t\t\t\tmsgptr++;\n\t\t\t\t\tfor (i = 0; (*msgptr && *msgptr != ESCAPE_CHAR); i++)\n\t\t\t\t\t{\n\t\t\t\t\t\ttemp[i] = *msgptr++;\n\t\t\t\t\t} //end while\n\t\t\t\t\ttemp[i] = '\\0';\n\t\t\t\t\t//step over the trailing escape char\n\t\t\t\t\tif (*msgptr) msgptr++;\n\t\t\t\t\t//find the random keyword\n\t\t\t\t\tif (!RandomString(temp))\n\t\t\t\t\t{\n\t\t\t\t\t\tif (!BotFindStringInList(stringlist, temp))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tLog_Write(\"%s = {\\\"%s\\\"} //MISSING RANDOM\\r\\n\", temp, temp);\n\t\t\t\t\t\t\ts = GetClearedMemory(sizeof(bot_stringlist_t) + strlen(temp) + 1);\n\t\t\t\t\t\t\ts->string = (char *) s + sizeof(bot_stringlist_t);\n\t\t\t\t\t\t\tstrcpy(s->string, temp);\n\t\t\t\t\t\t\ts->next = stringlist;\n\t\t\t\t\t\t\tstringlist = s;\n\t\t\t\t\t\t} //end if\n\t\t\t\t\t} //end if\n\t\t\t\t\tbreak;\n\t\t\t\t} //end case\n\t\t\t\tdefault:\n\t\t\t\t{\n\t\t\t\t\tbotimport.Print(PRT_FATAL, \"BotCheckChatMessageIntegrety: message \\\"%s\\\" invalid escape char\\n\", message);\n\t\t\t\t\tbreak;\n\t\t\t\t} //end default\n\t\t\t} //end switch\n\t\t} //end if\n\t\telse\n\t\t{\n\t\t\tmsgptr++;\n\t\t} //end else\n\t} //end while\n\treturn stringlist;\n} //end of the function BotCheckChatMessageIntegrety\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid BotCheckInitialChatIntegrety(bot_chat_t *chat)\n{\n\tbot_chattype_t *t;\n\tbot_chatmessage_t *cm;\n\tbot_stringlist_t *stringlist, *s, *nexts;\n\n\tstringlist = NULL;\n\tfor (t = chat->types; t; t = t->next)\n\t{\n\t\tfor (cm = t->firstchatmessage; cm; cm = cm->next)\n\t\t{\n\t\t\tstringlist = BotCheckChatMessageIntegrety(cm->chatmessage, stringlist);\n\t\t} //end for\n\t} //end for\n\tfor (s = stringlist; s; s = nexts)\n\t{\n\t\tnexts = s->next;\n\t\tFreeMemory(s);\n\t} //end for\n} //end of the function BotCheckInitialChatIntegrety\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid BotCheckReplyChatIntegrety(bot_replychat_t *replychat)\n{\n\tbot_replychat_t *rp;\n\tbot_chatmessage_t *cm;\n\tbot_stringlist_t *stringlist, *s, *nexts;\n\n\tstringlist = NULL;\n\tfor (rp = replychat; rp; rp = rp->next)\n\t{\n\t\tfor (cm = rp->firstchatmessage; cm; cm = cm->next)\n\t\t{\n\t\t\tstringlist = BotCheckChatMessageIntegrety(cm->chatmessage, stringlist);\n\t\t} //end for\n\t} //end for\n\tfor (s = stringlist; s; s = nexts)\n\t{\n\t\tnexts = s->next;\n\t\tFreeMemory(s);\n\t} //end for\n} //end of the function BotCheckReplyChatIntegrety\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid BotDumpReplyChat(bot_replychat_t *replychat)\n{\n\tFILE *fp;\n\tbot_replychat_t *rp;\n\tbot_replychatkey_t *key;\n\tbot_chatmessage_t *cm;\n\tbot_matchpiece_t *mp;\n\n\tfp = Log_FilePointer();\n\tif (!fp) return;\n\tfprintf(fp, \"BotDumpReplyChat:\\n\");\n\tfor (rp = replychat; rp; rp = rp->next)\n\t{\n\t\tfprintf(fp, \"[\");\n\t\tfor (key = rp->keys; key; key = key->next)\n\t\t{\n\t\t\tif (key->flags & RCKFL_AND) fprintf(fp, \"&\");\n\t\t\telse if (key->flags & RCKFL_NOT) fprintf(fp, \"!\");\n\t\t\t//\n\t\t\tif (key->flags & RCKFL_NAME) fprintf(fp, \"name\");\n\t\t\telse if (key->flags & RCKFL_GENDERFEMALE) fprintf(fp, \"female\");\n\t\t\telse if (key->flags & RCKFL_GENDERMALE) fprintf(fp, \"male\");\n\t\t\telse if (key->flags & RCKFL_GENDERLESS) fprintf(fp, \"it\");\n\t\t\telse if (key->flags & RCKFL_VARIABLES)\n\t\t\t{\n\t\t\t\tfprintf(fp, \"(\");\n\t\t\t\tfor (mp = key->match; mp; mp = mp->next)\n\t\t\t\t{\n\t\t\t\t\tif (mp->type == MT_STRING) fprintf(fp, \"\\\"%s\\\"\", mp->firststring->string);\n\t\t\t\t\telse fprintf(fp, \"%d\", mp->variable);\n\t\t\t\t\tif (mp->next) fprintf(fp, \", \");\n\t\t\t\t} //end for\n\t\t\t\tfprintf(fp, \")\");\n\t\t\t} //end if\n\t\t\telse if (key->flags & RCKFL_STRING)\n\t\t\t{\n\t\t\t\tfprintf(fp, \"\\\"%s\\\"\", key->string);\n\t\t\t} //end if\n\t\t\tif (key->next) fprintf(fp, \", \");\n\t\t\telse fprintf(fp, \"] = %1.0f\\n\", rp->priority);\n\t\t} //end for\n\t\tfprintf(fp, \"{\\n\");\n\t\tfor (cm = rp->firstchatmessage; cm; cm = cm->next)\n\t\t{\n\t\t\tfprintf(fp, \"\\t\\\"%s\\\";\\n\", cm->chatmessage);\n\t\t} //end for\n\t\tfprintf(fp, \"}\\n\");\n\t} //end for\n} //end of the function BotDumpReplyChat\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid BotFreeReplyChat(bot_replychat_t *replychat)\n{\n\tbot_replychat_t *rp, *nextrp;\n\tbot_replychatkey_t *key, *nextkey;\n\tbot_chatmessage_t *cm, *nextcm;\n\n\tfor (rp = replychat; rp; rp = nextrp)\n\t{\n\t\tnextrp = rp->next;\n\t\tfor (key = rp->keys; key; key = nextkey)\n\t\t{\n\t\t\tnextkey = key->next;\n\t\t\tif (key->match) BotFreeMatchPieces(key->match);\n\t\t\tif (key->string) FreeMemory(key->string);\n\t\t\tFreeMemory(key);\n\t\t} //end for\n\t\tfor (cm = rp->firstchatmessage; cm; cm = nextcm)\n\t\t{\n\t\t\tnextcm = cm->next;\n\t\t\tFreeMemory(cm);\n\t\t} //end for\n\t\tFreeMemory(rp);\n\t} //end for\n} //end of the function BotFreeReplyChat\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid BotCheckValidReplyChatKeySet(source_t *source, bot_replychatkey_t *keys)\n{\n\tint allprefixed, hasvariableskey, hasstringkey;\n\tbot_matchpiece_t *m;\n\tbot_matchstring_t *ms;\n\tbot_replychatkey_t *key, *key2;\n\n\t//\n\tallprefixed = qtrue;\n\thasvariableskey = hasstringkey = qfalse;\n\tfor (key = keys; key; key = key->next)\n\t{\n\t\tif (!(key->flags & (RCKFL_AND|RCKFL_NOT)))\n\t\t{\n\t\t\tallprefixed = qfalse;\n\t\t\tif (key->flags & RCKFL_VARIABLES)\n\t\t\t{\n\t\t\t\tfor (m = key->match; m; m = m->next)\n\t\t\t\t{\n\t\t\t\t\tif (m->type == MT_VARIABLE) hasvariableskey = qtrue;\n\t\t\t\t} //end for\n\t\t\t} //end if\n\t\t\telse if (key->flags & RCKFL_STRING)\n\t\t\t{\n\t\t\t\thasstringkey = qtrue;\n\t\t\t} //end else if\n\t\t} //end if\n\t\telse if ((key->flags & RCKFL_AND) && (key->flags & RCKFL_STRING))\n\t\t{\n\t\t\tfor (key2 = keys; key2; key2 = key2->next)\n\t\t\t{\n\t\t\t\tif (key2 == key) continue;\n\t\t\t\tif (key2->flags & RCKFL_NOT) continue;\n\t\t\t\tif (key2->flags & RCKFL_VARIABLES)\n\t\t\t\t{\n\t\t\t\t\tfor (m = key2->match; m; m = m->next)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (m->type == MT_STRING)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tfor (ms = m->firststring; ms; ms = ms->next)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tif (StringContains(ms->string, key->string, qfalse) != -1)\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t} //end if\n\t\t\t\t\t\t\t} //end for\n\t\t\t\t\t\t\tif (ms) break;\n\t\t\t\t\t\t} //end if\n\t\t\t\t\t\telse if (m->type == MT_VARIABLE)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t} //end if\n\t\t\t\t\t} //end for\n\t\t\t\t\tif (!m)\n\t\t\t\t\t{\n\t\t\t\t\t\tSourceWarning(source, \"one of the match templates does not \"\n\t\t\t\t\t\t\t\t\t\t\"leave space for the key %s with the & prefix\", key->string);\n\t\t\t\t\t} //end if\n\t\t\t\t} //end if\n\t\t\t} //end for\n\t\t} //end else\n\t\tif ((key->flags & RCKFL_NOT) && (key->flags & RCKFL_STRING))\n\t\t{\n\t\t\tfor (key2 = keys; key2; key2 = key2->next)\n\t\t\t{\n\t\t\t\tif (key2 == key) continue;\n\t\t\t\tif (key2->flags & RCKFL_NOT) continue;\n\t\t\t\tif (key2->flags & RCKFL_STRING)\n\t\t\t\t{\n\t\t\t\t\tif (StringContains(key2->string, key->string, qfalse) != -1)\n\t\t\t\t\t{\n\t\t\t\t\t\tSourceWarning(source, \"the key %s with prefix ! is inside the key %s\", key->string, key2->string);\n\t\t\t\t\t} //end if\n\t\t\t\t} //end if\n\t\t\t\telse if (key2->flags & RCKFL_VARIABLES)\n\t\t\t\t{\n\t\t\t\t\tfor (m = key2->match; m; m = m->next)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (m->type == MT_STRING)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tfor (ms = m->firststring; ms; ms = ms->next)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tif (StringContains(ms->string, key->string, qfalse) != -1)\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tSourceWarning(source, \"the key %s with prefix ! is inside \"\n\t\t\t\t\t\t\t\t\t\t\t\t\"the match template string %s\", key->string, ms->string);\n\t\t\t\t\t\t\t\t} //end if\n\t\t\t\t\t\t\t} //end for\n\t\t\t\t\t\t} //end if\n\t\t\t\t\t} //end for\n\t\t\t\t} //end else if\n\t\t\t} //end for\n\t\t} //end if\n\t} //end for\n\tif (allprefixed) SourceWarning(source, \"all keys have a & or ! prefix\");\n\tif (hasvariableskey && hasstringkey)\n\t{\n\t\tSourceWarning(source, \"variables from the match template(s) could be \"\n\t\t\t\t\t\t\t\t\"invalid when outputting one of the chat messages\");\n\t} //end if\n} //end of the function BotCheckValidReplyChatKeySet\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nbot_replychat_t *BotLoadReplyChat(char *filename)\n{\n\tchar chatmessagestring[MAX_MESSAGE_SIZE];\n\tchar namebuffer[MAX_MESSAGE_SIZE];\n\tsource_t *source;\n\ttoken_t token;\n\tbot_chatmessage_t *chatmessage = NULL;\n\tbot_replychat_t *replychat, *replychatlist;\n\tbot_replychatkey_t *key;\n\n\tPC_SetBaseFolder(BOTFILESBASEFOLDER);\n\tsource = LoadSourceFile(filename);\n\tif (!source)\n\t{\n\t\tbotimport.Print(PRT_ERROR, \"counldn't load %s\\n\", filename);\n\t\treturn NULL;\n\t} //end if\n\t//\n\treplychatlist = NULL;\n\t//\n\twhile(PC_ReadToken(source, &token))\n\t{\n\t\tif (strcmp(token.string, \"[\"))\n\t\t{\n\t\t\tSourceError(source, \"expected [, found %s\", token.string);\n\t\t\tBotFreeReplyChat(replychatlist);\n\t\t\tFreeSource(source);\n\t\t\treturn NULL;\n\t\t} //end if\n\t\t//\n\t\treplychat = GetClearedHunkMemory(sizeof(bot_replychat_t));\n\t\treplychat->keys = NULL;\n\t\treplychat->next = replychatlist;\n\t\treplychatlist = replychat;\n\t\t//read the keys, there must be at least one key\n\t\tdo\n\t\t{\n\t\t\t//allocate a key\n\t\t\tkey = (bot_replychatkey_t *) GetClearedHunkMemory(sizeof(bot_replychatkey_t));\n\t\t\tkey->flags = 0;\n\t\t\tkey->string = NULL;\n\t\t\tkey->match = NULL;\n\t\t\tkey->next = replychat->keys;\n\t\t\treplychat->keys = key;\n\t\t\t//check for MUST BE PRESENT and MUST BE ABSENT keys\n\t\t\tif (PC_CheckTokenString(source, \"&\")) key->flags |= RCKFL_AND;\n\t\t\telse if (PC_CheckTokenString(source, \"!\")) key->flags |= RCKFL_NOT;\n\t\t\t//special keys\n\t\t\tif (PC_CheckTokenString(source, \"name\")) key->flags |= RCKFL_NAME;\n\t\t\telse if (PC_CheckTokenString(source, \"female\")) key->flags |= RCKFL_GENDERFEMALE;\n\t\t\telse if (PC_CheckTokenString(source, \"male\")) key->flags |= RCKFL_GENDERMALE;\n\t\t\telse if (PC_CheckTokenString(source, \"it\")) key->flags |= RCKFL_GENDERLESS;\n\t\t\telse if (PC_CheckTokenString(source, \"(\")) //match key\n\t\t\t{\n\t\t\t\tkey->flags |= RCKFL_VARIABLES;\n\t\t\t\tkey->match = BotLoadMatchPieces(source, \")\");\n\t\t\t\tif (!key->match)\n\t\t\t\t{\n\t\t\t\t\tBotFreeReplyChat(replychatlist);\n\t\t\t\t\treturn NULL;\n\t\t\t\t} //end if\n\t\t\t} //end else if\n\t\t\telse if (PC_CheckTokenString(source, \"<\")) //bot names\n\t\t\t{\n\t\t\t\tkey->flags |= RCKFL_BOTNAMES;\n\t\t\t\tstrcpy(namebuffer, \"\");\n\t\t\t\tdo\n\t\t\t\t{\n\t\t\t\t\tif (!PC_ExpectTokenType(source, TT_STRING, 0, &token))\n\t\t\t\t\t{\n\t\t\t\t\t\tBotFreeReplyChat(replychatlist);\n\t\t\t\t\t\tFreeSource(source);\n\t\t\t\t\t\treturn NULL;\n\t\t\t\t\t} //end if\n\t\t\t\t\tStripDoubleQuotes(token.string);\n\t\t\t\t\tif (strlen(namebuffer)) strcat(namebuffer, \"\\\\\");\n\t\t\t\t\tstrcat(namebuffer, token.string);\n\t\t\t\t} while(PC_CheckTokenString(source, \",\"));\n\t\t\t\tif (!PC_ExpectTokenString(source, \">\"))\n\t\t\t\t{\n\t\t\t\t\tBotFreeReplyChat(replychatlist);\n\t\t\t\t\tFreeSource(source);\n\t\t\t\t\treturn NULL;\n\t\t\t\t} //end if\n\t\t\t\tkey->string = (char *) GetClearedHunkMemory(strlen(namebuffer) + 1);\n\t\t\t\tstrcpy(key->string, namebuffer);\n\t\t\t} //end else if\n\t\t\telse //normal string key\n\t\t\t{\n\t\t\t\tkey->flags |= RCKFL_STRING;\n\t\t\t\tif (!PC_ExpectTokenType(source, TT_STRING, 0, &token))\n\t\t\t\t{\n\t\t\t\t\tBotFreeReplyChat(replychatlist);\n\t\t\t\t\tFreeSource(source);\n\t\t\t\t\treturn NULL;\n\t\t\t\t} //end if\n\t\t\t\tStripDoubleQuotes(token.string);\n\t\t\t\tkey->string = (char *) GetClearedHunkMemory(strlen(token.string) + 1);\n\t\t\t\tstrcpy(key->string, token.string);\n\t\t\t} //end else\n\t\t\t//\n\t\t\tPC_CheckTokenString(source, \",\");\n\t\t} while(!PC_CheckTokenString(source, \"]\"));\n\t\t//\n\t\tBotCheckValidReplyChatKeySet(source, replychat->keys);\n\t\t//read the = sign and the priority\n\t\tif (!PC_ExpectTokenString(source, \"=\") ||\n\t\t\t!PC_ExpectTokenType(source, TT_NUMBER, 0, &token))\n\t\t{\n\t\t\tBotFreeReplyChat(replychatlist);\n\t\t\tFreeSource(source);\n\t\t\treturn NULL;\n\t\t} //end if\n\t\treplychat->priority = token.floatvalue;\n\t\t//read the leading {\n\t\tif (!PC_ExpectTokenString(source, \"{\"))\n\t\t{\n\t\t\tBotFreeReplyChat(replychatlist);\n\t\t\tFreeSource(source);\n\t\t\treturn NULL;\n\t\t} //end if\n\t\treplychat->numchatmessages = 0;\n\t\t//while the trailing } is not found\n\t\twhile(!PC_CheckTokenString(source, \"}\"))\n\t\t{\n\t\t\tif (!BotLoadChatMessage(source, chatmessagestring))\n\t\t\t{\n\t\t\t\tBotFreeReplyChat(replychatlist);\n\t\t\t\tFreeSource(source);\n\t\t\t\treturn NULL;\n\t\t\t} //end if\n\t\t\tchatmessage = (bot_chatmessage_t *) GetClearedHunkMemory(sizeof(bot_chatmessage_t) + strlen(chatmessagestring) + 1);\n\t\t\tchatmessage->chatmessage = (char *) chatmessage + sizeof(bot_chatmessage_t);\n\t\t\tstrcpy(chatmessage->chatmessage, chatmessagestring);\n\t\t\tchatmessage->time = -2*CHATMESSAGE_RECENTTIME;\n\t\t\tchatmessage->next = replychat->firstchatmessage;\n\t\t\t//add the chat message to the reply chat\n\t\t\treplychat->firstchatmessage = chatmessage;\n\t\t\treplychat->numchatmessages++;\n\t\t} //end while\n\t} //end while\n\tFreeSource(source);\n\tbotimport.Print(PRT_MESSAGE, \"loaded %s\\n\", filename);\n\t//\n\t//BotDumpReplyChat(replychatlist);\n\tif (botDeveloper)\n\t{\n\t\tBotCheckReplyChatIntegrety(replychatlist);\n\t} //end if\n\t//\n\tif (!replychatlist) botimport.Print(PRT_MESSAGE, \"no rchats\\n\");\n\t//\n\treturn replychatlist;\n} //end of the function BotLoadReplyChat\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid BotDumpInitialChat(bot_chat_t *chat)\n{\n\tbot_chattype_t *t;\n\tbot_chatmessage_t *m;\n\n\tLog_Write(\"{\");\n\tfor (t = chat->types; t; t = t->next)\n\t{\n\t\tLog_Write(\" type \\\"%s\\\"\", t->name);\n\t\tLog_Write(\" {\");\n\t\tLog_Write(\"  numchatmessages = %d\", t->numchatmessages);\n\t\tfor (m = t->firstchatmessage; m; m = m->next)\n\t\t{\n\t\t\tLog_Write(\"  \\\"%s\\\"\", m->chatmessage);\n\t\t} //end for\n\t\tLog_Write(\" }\");\n\t} //end for\n\tLog_Write(\"}\");\n} //end of the function BotDumpInitialChat\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nbot_chat_t *BotLoadInitialChat(char *chatfile, char *chatname)\n{\n\tint pass, foundchat, indent, size;\n\tchar *ptr = NULL;\n\tchar chatmessagestring[MAX_MESSAGE_SIZE];\n\tsource_t *source;\n\ttoken_t token;\n\tbot_chat_t *chat = NULL;\n\tbot_chattype_t *chattype = NULL;\n\tbot_chatmessage_t *chatmessage = NULL;\n#ifdef DEBUG\n\tint starttime;\n\n\tstarttime = Sys_MilliSeconds();\n#endif //DEBUG\n\t//\n\tsize = 0;\n\tfoundchat = qfalse;\n\t//a bot chat is parsed in two phases\n\tfor (pass = 0; pass < 2; pass++)\n\t{\n\t\t//allocate memory\n\t\tif (pass && size) ptr = (char *) GetClearedMemory(size);\n\t\t//load the source file\n\t\tPC_SetBaseFolder(BOTFILESBASEFOLDER);\n\t\tsource = LoadSourceFile(chatfile);\n\t\tif (!source)\n\t\t{\n\t\t\tbotimport.Print(PRT_ERROR, \"counldn't load %s\\n\", chatfile);\n\t\t\treturn NULL;\n\t\t} //end if\n\t\t//chat structure\n\t\tif (pass)\n\t\t{\n\t\t\tchat = (bot_chat_t *) ptr;\n\t\t\tptr += sizeof(bot_chat_t);\n\t\t} //end if\n\t\tsize = sizeof(bot_chat_t);\n\t\t//\n\t\twhile(PC_ReadToken(source, &token))\n\t\t{\n\t\t\tif (!strcmp(token.string, \"chat\"))\n\t\t\t{\n\t\t\t\tif (!PC_ExpectTokenType(source, TT_STRING, 0, &token))\n\t\t\t\t{\n\t\t\t\t\tFreeSource(source);\n\t\t\t\t\treturn NULL;\n\t\t\t\t} //end if\n\t\t\t\tStripDoubleQuotes(token.string);\n\t\t\t\t//after the chat name we expect a opening brace\n\t\t\t\tif (!PC_ExpectTokenString(source, \"{\"))\n\t\t\t\t{\n\t\t\t\t\tFreeSource(source);\n\t\t\t\t\treturn NULL;\n\t\t\t\t} //end if\n\t\t\t\t//if the chat name is found\n\t\t\t\tif (!Q_stricmp(token.string, chatname))\n\t\t\t\t{\n\t\t\t\t\tfoundchat = qtrue;\n\t\t\t\t\t//read the chat types\n\t\t\t\t\twhile(1)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (!PC_ExpectAnyToken(source, &token))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tFreeSource(source);\n\t\t\t\t\t\t\treturn NULL;\n\t\t\t\t\t\t} //end if\n\t\t\t\t\t\tif (!strcmp(token.string, \"}\")) break;\n\t\t\t\t\t\tif (strcmp(token.string, \"type\"))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tSourceError(source, \"expected type found %s\\n\", token.string);\n\t\t\t\t\t\t\tFreeSource(source);\n\t\t\t\t\t\t\treturn NULL;\n\t\t\t\t\t\t} //end if\n\t\t\t\t\t\t//expect the chat type name\n\t\t\t\t\t\tif (!PC_ExpectTokenType(source, TT_STRING, 0, &token) ||\n\t\t\t\t\t\t\t!PC_ExpectTokenString(source, \"{\"))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tFreeSource(source);\n\t\t\t\t\t\t\treturn NULL;\n\t\t\t\t\t\t} //end if\n\t\t\t\t\t\tStripDoubleQuotes(token.string);\n\t\t\t\t\t\tif (pass)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tchattype = (bot_chattype_t *) ptr;\n\t\t\t\t\t\t\tstrncpy(chattype->name, token.string, MAX_CHATTYPE_NAME-1);\n\t\t\t\t\t\t\tchattype->name[MAX_CHATTYPE_NAME-1] = 0;\n\t\t\t\t\t\t\tchattype->firstchatmessage = NULL;\n\t\t\t\t\t\t\t//add the chat type to the chat\n\t\t\t\t\t\t\tchattype->next = chat->types;\n\t\t\t\t\t\t\tchat->types = chattype;\n\t\t\t\t\t\t\t//\n\t\t\t\t\t\t\tptr += sizeof(bot_chattype_t);\n\t\t\t\t\t\t} //end if\n\t\t\t\t\t\tsize += sizeof(bot_chattype_t);\n\t\t\t\t\t\t//read the chat messages\n\t\t\t\t\t\twhile(!PC_CheckTokenString(source, \"}\"))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tsize_t len;\n\t\t\t\t\t\t\tif (!BotLoadChatMessage(source, chatmessagestring))\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tFreeSource(source);\n\t\t\t\t\t\t\t\treturn NULL;\n\t\t\t\t\t\t\t} //end if\n\t\t\t\t\t\t\tlen = strlen(chatmessagestring) + 1;\n\t\t\t\t\t\t\tlen = PAD(len, sizeof(long));\n\t\t\t\t\t\t\tif (pass)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tchatmessage = (bot_chatmessage_t *) ptr;\n\t\t\t\t\t\t\t\tchatmessage->time = -2*CHATMESSAGE_RECENTTIME;\n\t\t\t\t\t\t\t\t//put the chat message in the list\n\t\t\t\t\t\t\t\tchatmessage->next = chattype->firstchatmessage;\n\t\t\t\t\t\t\t\tchattype->firstchatmessage = chatmessage;\n\t\t\t\t\t\t\t\t//store the chat message\n\t\t\t\t\t\t\t\tptr += sizeof(bot_chatmessage_t);\n\t\t\t\t\t\t\t\tchatmessage->chatmessage = ptr;\n\t\t\t\t\t\t\t\tstrcpy(chatmessage->chatmessage, chatmessagestring);\n\t\t\t\t\t\t\t\tptr += len;\n\t\t\t\t\t\t\t\t//the number of chat messages increased\n\t\t\t\t\t\t\t\tchattype->numchatmessages++;\n\t\t\t\t\t\t\t} //end if\n\t\t\t\t\t\t\tsize += sizeof(bot_chatmessage_t) + len;\n\t\t\t\t\t\t} //end if\n\t\t\t\t\t} //end while\n\t\t\t\t} //end if\n\t\t\t\telse //skip the bot chat\n\t\t\t\t{\n\t\t\t\t\tindent = 1;\n\t\t\t\t\twhile(indent)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (!PC_ExpectAnyToken(source, &token))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tFreeSource(source);\n\t\t\t\t\t\t\treturn NULL;\n\t\t\t\t\t\t} //end if\n\t\t\t\t\t\tif (!strcmp(token.string, \"{\")) indent++;\n\t\t\t\t\t\telse if (!strcmp(token.string, \"}\")) indent--;\n\t\t\t\t\t} //end while\n\t\t\t\t} //end else\n\t\t\t} //end if\n\t\t\telse\n\t\t\t{\n\t\t\t\tSourceError(source, \"unknown definition %s\\n\", token.string);\n\t\t\t\tFreeSource(source);\n\t\t\t\treturn NULL;\n\t\t\t} //end else\n\t\t} //end while\n\t\t//free the source\n\t\tFreeSource(source);\n\t\t//if the requested character is not found\n\t\tif (!foundchat)\n\t\t{\n\t\t\tbotimport.Print(PRT_ERROR, \"couldn't find chat %s in %s\\n\", chatname, chatfile);\n\t\t\treturn NULL;\n\t\t} //end if\n\t} //end for\n\t//\n\tbotimport.Print(PRT_MESSAGE, \"loaded %s from %s\\n\", chatname, chatfile);\n\t//\n\t//BotDumpInitialChat(chat);\n\tif (botDeveloper)\n\t{\n\t\tBotCheckInitialChatIntegrety(chat);\n\t} //end if\n#ifdef DEBUG\n\tbotimport.Print(PRT_MESSAGE, \"initial chats loaded in %d msec\\n\", Sys_MilliSeconds() - starttime);\n#endif //DEBUG\n\t//character was read successfully\n\treturn chat;\n} //end of the function BotLoadInitialChat\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid BotFreeChatFile(int chatstate)\n{\n\tbot_chatstate_t *cs;\n\n\tcs = BotChatStateFromHandle(chatstate);\n\tif (!cs) return;\n\tif (cs->chat) FreeMemory(cs->chat);\n\tcs->chat = NULL;\n} //end of the function BotFreeChatFile\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint BotLoadChatFile(int chatstate, char *chatfile, char *chatname)\n{\n\tbot_chatstate_t *cs;\n\tint n, avail = 0;\n\n\tcs = BotChatStateFromHandle(chatstate);\n\tif (!cs) return BLERR_CANNOTLOADICHAT;\n\tBotFreeChatFile(chatstate);\n\n\tif (!LibVarGetValue(\"bot_reloadcharacters\"))\n\t{\n\t\tavail = -1;\n\t\tfor( n = 0; n < MAX_CLIENTS; n++ ) {\n\t\t\tif( !ichatdata[n] ) {\n\t\t\t\tif( avail == -1 ) {\n\t\t\t\t\tavail = n;\n\t\t\t\t}\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif( strcmp( chatfile, ichatdata[n]->filename ) != 0 ) { \n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif( strcmp( chatname, ichatdata[n]->chatname ) != 0 ) { \n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tcs->chat = ichatdata[n]->chat;\n\t\t//\t\tbotimport.Print( PRT_MESSAGE, \"retained %s from %s\\n\", chatname, chatfile );\n\t\t\treturn BLERR_NOERROR;\n\t\t}\n\n\t\tif( avail == -1 ) {\n\t\t\tbotimport.Print(PRT_FATAL, \"ichatdata table full; couldn't load chat %s from %s\\n\", chatname, chatfile);\n\t\t\treturn BLERR_CANNOTLOADICHAT;\n\t\t}\n\t}\n\n\tcs->chat = BotLoadInitialChat(chatfile, chatname);\n\tif (!cs->chat)\n\t{\n\t\tbotimport.Print(PRT_FATAL, \"couldn't load chat %s from %s\\n\", chatname, chatfile);\n\t\treturn BLERR_CANNOTLOADICHAT;\n\t} //end if\n\tif (!LibVarGetValue(\"bot_reloadcharacters\"))\n\t{\n\t\tichatdata[avail] = GetClearedMemory( sizeof(bot_ichatdata_t) );\n\t\tichatdata[avail]->chat = cs->chat;\n\t\tQ_strncpyz( ichatdata[avail]->chatname, chatname, sizeof(ichatdata[avail]->chatname) );\n\t\tQ_strncpyz( ichatdata[avail]->filename, chatfile, sizeof(ichatdata[avail]->filename) );\n\t} //end if\n\n\treturn BLERR_NOERROR;\n} //end of the function BotLoadChatFile\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint BotExpandChatMessage(char *outmessage, char *message, unsigned long mcontext,\n\t\t\t\t\t\t\t bot_match_t *match, unsigned long vcontext, int reply)\n{\n\tint num, len, i, expansion;\n\tchar *outputbuf, *ptr, *msgptr;\n\tchar temp[MAX_MESSAGE_SIZE];\n\n\texpansion = qfalse;\n\tmsgptr = message;\n\toutputbuf = outmessage;\n\tlen = 0;\n\t//\n\twhile(*msgptr)\n\t{\n\t\tif (*msgptr == ESCAPE_CHAR)\n\t\t{\n\t\t\tmsgptr++;\n\t\t\tswitch(*msgptr)\n\t\t\t{\n\t\t\t\tcase 'v': //variable\n\t\t\t\t{\n\t\t\t\t\tmsgptr++;\n\t\t\t\t\tnum = 0;\n\t\t\t\t\twhile(*msgptr && *msgptr != ESCAPE_CHAR)\n\t\t\t\t\t{\n\t\t\t\t\t\tnum = num * 10 + (*msgptr++) - '0';\n\t\t\t\t\t} //end while\n\t\t\t\t\t//step over the trailing escape char\n\t\t\t\t\tif (*msgptr) msgptr++;\n\t\t\t\t\tif (num > MAX_MATCHVARIABLES)\n\t\t\t\t\t{\n\t\t\t\t\t\tbotimport.Print(PRT_ERROR, \"BotConstructChat: message %s variable %d out of range\\n\", message, num);\n\t\t\t\t\t\treturn qfalse;\n\t\t\t\t\t} //end if\n\t\t\t\t\tif (match->variables[num].offset >= 0)\n\t\t\t\t\t{\n\t\t\t\t\t        assert( match->variables[num].offset >= 0 );\n\t\t\t\t\t\tptr = &match->string[ (int) match->variables[num].offset];\n\t\t\t\t\t\tfor (i = 0; i < match->variables[num].length; i++)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttemp[i] = ptr[i];\n\t\t\t\t\t\t} //end for\n\t\t\t\t\t\ttemp[i] = 0;\n\t\t\t\t\t\t//if it's a reply message\n\t\t\t\t\t\tif (reply)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t//replace the reply synonyms in the variables\n\t\t\t\t\t\t\tBotReplaceReplySynonyms(temp, vcontext);\n\t\t\t\t\t\t} //end if\n\t\t\t\t\t\telse \n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t//replace synonyms in the variable context\n\t\t\t\t\t\t\tBotReplaceSynonyms(temp, vcontext);\n\t\t\t\t\t\t} //end else\n\t\t\t\t\t\t//\n\t\t\t\t\t\tif (len + strlen(temp) >= MAX_MESSAGE_SIZE)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tbotimport.Print(PRT_ERROR, \"BotConstructChat: message %s too long\\n\", message);\n\t\t\t\t\t\t\treturn qfalse;\n\t\t\t\t\t\t} //end if\n\t\t\t\t\t\tstrcpy(&outputbuf[len], temp);\n\t\t\t\t\t\tlen += strlen(temp);\n\t\t\t\t\t} //end if\n\t\t\t\t\tbreak;\n\t\t\t\t} //end case\n\t\t\t\tcase 'r': //random\n\t\t\t\t{\n\t\t\t\t\tmsgptr++;\n\t\t\t\t\tfor (i = 0; (*msgptr && *msgptr != ESCAPE_CHAR); i++)\n\t\t\t\t\t{\n\t\t\t\t\t\ttemp[i] = *msgptr++;\n\t\t\t\t\t} //end while\n\t\t\t\t\ttemp[i] = '\\0';\n\t\t\t\t\t//step over the trailing escape char\n\t\t\t\t\tif (*msgptr) msgptr++;\n\t\t\t\t\t//find the random keyword\n\t\t\t\t\tptr = RandomString(temp);\n\t\t\t\t\tif (!ptr)\n\t\t\t\t\t{\n\t\t\t\t\t\tbotimport.Print(PRT_ERROR, \"BotConstructChat: unknown random string %s\\n\", temp);\n\t\t\t\t\t\treturn qfalse;\n\t\t\t\t\t} //end if\n\t\t\t\t\tif (len + strlen(ptr) >= MAX_MESSAGE_SIZE)\n\t\t\t\t\t{\n\t\t\t\t\t\tbotimport.Print(PRT_ERROR, \"BotConstructChat: message \\\"%s\\\" too long\\n\", message);\n\t\t\t\t\t\treturn qfalse;\n\t\t\t\t\t} //end if\n\t\t\t\t\tstrcpy(&outputbuf[len], ptr);\n\t\t\t\t\tlen += strlen(ptr);\n\t\t\t\t\texpansion = qtrue;\n\t\t\t\t\tbreak;\n\t\t\t\t} //end case\n\t\t\t\tdefault:\n\t\t\t\t{\n\t\t\t\t\tbotimport.Print(PRT_FATAL, \"BotConstructChat: message \\\"%s\\\" invalid escape char\\n\", message);\n\t\t\t\t\tbreak;\n\t\t\t\t} //end default\n\t\t\t} //end switch\n\t\t} //end if\n\t\telse\n\t\t{\n\t\t\toutputbuf[len++] = *msgptr++;\n\t\t\tif (len >= MAX_MESSAGE_SIZE)\n\t\t\t{\n\t\t\t\tbotimport.Print(PRT_ERROR, \"BotConstructChat: message \\\"%s\\\" too long\\n\", message);\n\t\t\t\tbreak;\n\t\t\t} //end if\n\t\t} //end else\n\t} //end while\n\toutputbuf[len] = '\\0';\n\t//replace synonyms weighted in the message context\n\tBotReplaceWeightedSynonyms(outputbuf, mcontext);\n\t//return true if a random was expanded\n\treturn expansion;\n} //end of the function BotExpandChatMessage\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid BotConstructChatMessage(bot_chatstate_t *chatstate, char *message, unsigned long mcontext,\n\t\t\t\t\t\t\t bot_match_t *match, unsigned long vcontext, int reply)\n{\n\tint i;\n\tchar srcmessage[MAX_MESSAGE_SIZE];\n\n\tstrcpy(srcmessage, message);\n\tfor (i = 0; i < 10; i++)\n\t{\n\t\tif (!BotExpandChatMessage(chatstate->chatmessage, srcmessage, mcontext, match, vcontext, reply))\n\t\t{\n\t\t\tbreak;\n\t\t} //end if\n\t\tstrcpy(srcmessage, chatstate->chatmessage);\n\t} //end for\n\tif (i >= 10)\n\t{\n\t\tbotimport.Print(PRT_WARNING, \"too many expansions in chat message\\n\");\n\t\tbotimport.Print(PRT_WARNING, \"%s\\n\", chatstate->chatmessage);\n\t} //end if\n} //end of the function BotConstructChatMessage\n//===========================================================================\n// randomly chooses one of the chat message of the given type\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nchar *BotChooseInitialChatMessage(bot_chatstate_t *cs, char *type)\n{\n\tint n, numchatmessages;\n\tfloat besttime;\n\tbot_chattype_t *t;\n\tbot_chatmessage_t *m, *bestchatmessage;\n\tbot_chat_t *chat;\n\n\tchat = cs->chat;\n\tfor (t = chat->types; t; t = t->next)\n\t{\n\t\tif (!Q_stricmp(t->name, type))\n\t\t{\n\t\t\tnumchatmessages = 0;\n\t\t\tfor (m = t->firstchatmessage; m; m = m->next)\n\t\t\t{\n\t\t\t\tif (m->time > AAS_Time()) continue;\n\t\t\t\tnumchatmessages++;\n\t\t\t} //end if\n\t\t\t//if all chat messages have been used recently\n\t\t\tif (numchatmessages <= 0)\n\t\t\t{\n\t\t\t\tbesttime = 0;\n\t\t\t\tbestchatmessage = NULL;\n\t\t\t\tfor (m = t->firstchatmessage; m; m = m->next)\n\t\t\t\t{\n\t\t\t\t\tif (!besttime || m->time < besttime)\n\t\t\t\t\t{\n\t\t\t\t\t\tbestchatmessage = m;\n\t\t\t\t\t\tbesttime = m->time;\n\t\t\t\t\t} //end if\n\t\t\t\t} //end for\n\t\t\t\tif (bestchatmessage) return bestchatmessage->chatmessage;\n\t\t\t} //end if\n\t\t\telse //choose a chat message randomly\n\t\t\t{\n\t\t\t\tn = random() * numchatmessages;\n\t\t\t\tfor (m = t->firstchatmessage; m; m = m->next)\n\t\t\t\t{\n\t\t\t\t\tif (m->time > AAS_Time()) continue;\n\t\t\t\t\tif (--n < 0)\n\t\t\t\t\t{\n\t\t\t\t\t\tm->time = AAS_Time() + CHATMESSAGE_RECENTTIME;\n\t\t\t\t\t\treturn m->chatmessage;\n\t\t\t\t\t} //end if\n\t\t\t\t} //end for\n\t\t\t} //end else\n\t\t\treturn NULL;\n\t\t} //end if\n\t} //end for\n\treturn NULL;\n} //end of the function BotChooseInitialChatMessage\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint BotNumInitialChats(int chatstate, char *type)\n{\n\tbot_chatstate_t *cs;\n\tbot_chattype_t *t;\n\n\tcs = BotChatStateFromHandle(chatstate);\n\tif (!cs) return 0;\n\n\tfor (t = cs->chat->types; t; t = t->next)\n\t{\n\t\tif (!Q_stricmp(t->name, type))\n\t\t{\n\t\t\tif (LibVarGetValue(\"bot_testichat\")) {\n\t\t\t\tbotimport.Print(PRT_MESSAGE, \"%s has %d chat lines\\n\", type, t->numchatmessages);\n\t\t\t\tbotimport.Print(PRT_MESSAGE, \"-------------------\\n\");\n\t\t\t}\n\t\t\treturn t->numchatmessages;\n\t\t} //end if\n\t} //end for\n\treturn 0;\n} //end of the function BotNumInitialChats\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid BotInitialChat(int chatstate, char *type, int mcontext, char *var0, char *var1, char *var2, char *var3, char *var4, char *var5, char *var6, char *var7)\n{\n\tchar *message;\n\tint index;\n\tbot_match_t match;\n\tbot_chatstate_t *cs;\n\n\tcs = BotChatStateFromHandle(chatstate);\n\tif (!cs) return;\n\t//if no chat file is loaded\n\tif (!cs->chat) return;\n\t//choose a chat message randomly of the given type\n\tmessage = BotChooseInitialChatMessage(cs, type);\n\t//if there's no message of the given type\n\tif (!message)\n\t{\n#ifdef DEBUG\n\t\tbotimport.Print(PRT_MESSAGE, \"no chat messages of type %s\\n\", type);\n#endif //DEBUG\n\t\treturn;\n\t} //end if\n\t//\n\tCom_Memset(&match, 0, sizeof(match));\n\tindex = 0;\n\tif( var0 ) {\n\t\tstrcat(match.string, var0);\n\t\tmatch.variables[0].offset = index;\n\t\tmatch.variables[0].length = strlen(var0);\n\t\tindex += strlen(var0);\n\t}\n\tif( var1 ) {\n\t\tstrcat(match.string, var1);\n\t\tmatch.variables[1].offset = index;\n\t\tmatch.variables[1].length = strlen(var1);\n\t\tindex += strlen(var1);\n\t}\n\tif( var2 ) {\n\t\tstrcat(match.string, var2);\n\t\tmatch.variables[2].offset = index;\n\t\tmatch.variables[2].length = strlen(var2);\n\t\tindex += strlen(var2);\n\t}\n\tif( var3 ) {\n\t\tstrcat(match.string, var3);\n\t\tmatch.variables[3].offset = index;\n\t\tmatch.variables[3].length = strlen(var3);\n\t\tindex += strlen(var3);\n\t}\n\tif( var4 ) {\n\t\tstrcat(match.string, var4);\n\t\tmatch.variables[4].offset = index;\n\t\tmatch.variables[4].length = strlen(var4);\n\t\tindex += strlen(var4);\n\t}\n\tif( var5 ) {\n\t\tstrcat(match.string, var5);\n\t\tmatch.variables[5].offset = index;\n\t\tmatch.variables[5].length = strlen(var5);\n\t\tindex += strlen(var5);\n\t}\n\tif( var6 ) {\n\t\tstrcat(match.string, var6);\n\t\tmatch.variables[6].offset = index;\n\t\tmatch.variables[6].length = strlen(var6);\n\t\tindex += strlen(var6);\n\t}\n\tif( var7 ) {\n\t\tstrcat(match.string, var7);\n\t\tmatch.variables[7].offset = index;\n\t\tmatch.variables[7].length = strlen(var7);\n\t\tindex += strlen(var7);\n\t}\n \t//\n\tBotConstructChatMessage(cs, message, mcontext, &match, 0, qfalse);\n} //end of the function BotInitialChat\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid BotPrintReplyChatKeys(bot_replychat_t *replychat)\n{\n\tbot_replychatkey_t *key;\n\tbot_matchpiece_t *mp;\n\n\tbotimport.Print(PRT_MESSAGE, \"[\");\n\tfor (key = replychat->keys; key; key = key->next)\n\t{\n\t\tif (key->flags & RCKFL_AND) botimport.Print(PRT_MESSAGE, \"&\");\n\t\telse if (key->flags & RCKFL_NOT) botimport.Print(PRT_MESSAGE, \"!\");\n\t\t//\n\t\tif (key->flags & RCKFL_NAME) botimport.Print(PRT_MESSAGE, \"name\");\n\t\telse if (key->flags & RCKFL_GENDERFEMALE) botimport.Print(PRT_MESSAGE, \"female\");\n\t\telse if (key->flags & RCKFL_GENDERMALE) botimport.Print(PRT_MESSAGE, \"male\");\n\t\telse if (key->flags & RCKFL_GENDERLESS) botimport.Print(PRT_MESSAGE, \"it\");\n\t\telse if (key->flags & RCKFL_VARIABLES)\n\t\t{\n\t\t\tbotimport.Print(PRT_MESSAGE, \"(\");\n\t\t\tfor (mp = key->match; mp; mp = mp->next)\n\t\t\t{\n\t\t\t\tif (mp->type == MT_STRING) botimport.Print(PRT_MESSAGE, \"\\\"%s\\\"\", mp->firststring->string);\n\t\t\t\telse botimport.Print(PRT_MESSAGE, \"%d\", mp->variable);\n\t\t\t\tif (mp->next) botimport.Print(PRT_MESSAGE, \", \");\n\t\t\t} //end for\n\t\t\tbotimport.Print(PRT_MESSAGE, \")\");\n\t\t} //end if\n\t\telse if (key->flags & RCKFL_STRING)\n\t\t{\n\t\t\tbotimport.Print(PRT_MESSAGE, \"\\\"%s\\\"\", key->string);\n\t\t} //end if\n\t\tif (key->next) botimport.Print(PRT_MESSAGE, \", \");\n\t\telse botimport.Print(PRT_MESSAGE, \"] = %1.0f\\n\", replychat->priority);\n\t} //end for\n\tbotimport.Print(PRT_MESSAGE, \"{\\n\");\n} //end of the function BotPrintReplyChatKeys\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint BotReplyChat(int chatstate, char *message, int mcontext, int vcontext, char *var0, char *var1, char *var2, char *var3, char *var4, char *var5, char *var6, char *var7)\n{\n\tbot_replychat_t *rchat, *bestrchat;\n\tbot_replychatkey_t *key;\n\tbot_chatmessage_t *m, *bestchatmessage;\n\tbot_match_t match, bestmatch;\n\tint bestpriority, num, found, res, numchatmessages, index;\n\tbot_chatstate_t *cs;\n\n\tcs = BotChatStateFromHandle(chatstate);\n\tif (!cs) return qfalse;\n\tCom_Memset(&match, 0, sizeof(bot_match_t));\n\tstrcpy(match.string, message);\n\tbestpriority = -1;\n\tbestchatmessage = NULL;\n\tbestrchat = NULL;\n\t//go through all the reply chats\n\tfor (rchat = replychats; rchat; rchat = rchat->next)\n\t{\n\t\tfound = qfalse;\n\t\tfor (key = rchat->keys; key; key = key->next)\n\t\t{\n\t\t\tres = qfalse;\n\t\t\t//get the match result\n\t\t\tif (key->flags & RCKFL_NAME) res = (StringContains(message, cs->name, qfalse) != -1);\n\t\t\telse if (key->flags & RCKFL_BOTNAMES) res = (StringContains(key->string, cs->name, qfalse) != -1);\n\t\t\telse if (key->flags & RCKFL_GENDERFEMALE) res = (cs->gender == CHAT_GENDERFEMALE);\n\t\t\telse if (key->flags & RCKFL_GENDERMALE) res = (cs->gender == CHAT_GENDERMALE);\n\t\t\telse if (key->flags & RCKFL_GENDERLESS) res = (cs->gender == CHAT_GENDERLESS);\n\t\t\telse if (key->flags & RCKFL_VARIABLES) res = StringsMatch(key->match, &match);\n\t\t\telse if (key->flags & RCKFL_STRING) res = (StringContainsWord(message, key->string, qfalse) != NULL);\n\t\t\t//if the key must be present\n\t\t\tif (key->flags & RCKFL_AND)\n\t\t\t{\n\t\t\t\tif (!res)\n\t\t\t\t{\n\t\t\t\t\tfound = qfalse;\n\t\t\t\t\tbreak;\n\t\t\t\t} //end if\n\t\t\t} //end else if\n\t\t\t//if the key must be absent\n\t\t\telse if (key->flags & RCKFL_NOT)\n\t\t\t{\n\t\t\t\tif (res)\n\t\t\t\t{\n\t\t\t\t\tfound = qfalse;\n\t\t\t\t\tbreak;\n\t\t\t\t} //end if\n\t\t\t} //end if\n\t\t\telse if (res)\n\t\t\t{\n\t\t\t\tfound = qtrue;\n\t\t\t} //end else\n\t\t} //end for\n\t\t//\n\t\tif (found)\n\t\t{\n\t\t\tif (rchat->priority > bestpriority)\n\t\t\t{\n\t\t\t\tnumchatmessages = 0;\n\t\t\t\tfor (m = rchat->firstchatmessage; m; m = m->next)\n\t\t\t\t{\n\t\t\t\t\tif (m->time > AAS_Time()) continue;\n\t\t\t\t\tnumchatmessages++;\n\t\t\t\t} //end if\n\t\t\t\tnum = random() * numchatmessages;\n\t\t\t\tfor (m = rchat->firstchatmessage; m; m = m->next)\n\t\t\t\t{\n\t\t\t\t\tif (--num < 0) break;\n\t\t\t\t\tif (m->time > AAS_Time()) continue;\n\t\t\t\t} //end for\n\t\t\t\t//if the reply chat has a message\n\t\t\t\tif (m)\n\t\t\t\t{\n\t\t\t\t\tCom_Memcpy(&bestmatch, &match, sizeof(bot_match_t));\n\t\t\t\t\tbestchatmessage = m;\n\t\t\t\t\tbestrchat = rchat;\n\t\t\t\t\tbestpriority = rchat->priority;\n\t\t\t\t} //end if\n\t\t\t} //end if\n\t\t} //end if\n\t} //end for\n\tif (bestchatmessage)\n\t{\n\t\tindex = strlen(bestmatch.string);\n\t\tif( var0 ) {\n\t\t\tstrcat(bestmatch.string, var0);\n\t\t\tbestmatch.variables[0].offset = index;\n\t\t\tbestmatch.variables[0].length = strlen(var0);\n\t\t\tindex += strlen(var0);\n\t\t}\n\t\tif( var1 ) {\n\t\t\tstrcat(bestmatch.string, var1);\n\t\t\tbestmatch.variables[1].offset = index;\n\t\t\tbestmatch.variables[1].length = strlen(var1);\n\t\t\tindex += strlen(var1);\n\t\t}\n\t\tif( var2 ) {\n\t\t\tstrcat(bestmatch.string, var2);\n\t\t\tbestmatch.variables[2].offset = index;\n\t\t\tbestmatch.variables[2].length = strlen(var2);\n\t\t\tindex += strlen(var2);\n\t\t}\n\t\tif( var3 ) {\n\t\t\tstrcat(bestmatch.string, var3);\n\t\t\tbestmatch.variables[3].offset = index;\n\t\t\tbestmatch.variables[3].length = strlen(var3);\n\t\t\tindex += strlen(var3);\n\t\t}\n\t\tif( var4 ) {\n\t\t\tstrcat(bestmatch.string, var4);\n\t\t\tbestmatch.variables[4].offset = index;\n\t\t\tbestmatch.variables[4].length = strlen(var4);\n\t\t\tindex += strlen(var4);\n\t\t}\n\t\tif( var5 ) {\n\t\t\tstrcat(bestmatch.string, var5);\n\t\t\tbestmatch.variables[5].offset = index;\n\t\t\tbestmatch.variables[5].length = strlen(var5);\n\t\t\tindex += strlen(var5);\n\t\t}\n\t\tif( var6 ) {\n\t\t\tstrcat(bestmatch.string, var6);\n\t\t\tbestmatch.variables[6].offset = index;\n\t\t\tbestmatch.variables[6].length = strlen(var6);\n\t\t\tindex += strlen(var6);\n\t\t}\n\t\tif( var7 ) {\n\t\t\tstrcat(bestmatch.string, var7);\n\t\t\tbestmatch.variables[7].offset = index;\n\t\t\tbestmatch.variables[7].length = strlen(var7);\n\t\t\tindex += strlen(var7);\n\t\t}\n\t\tif (LibVarGetValue(\"bot_testrchat\"))\n\t\t{\n\t\t\tfor (m = bestrchat->firstchatmessage; m; m = m->next)\n\t\t\t{\n\t\t\t\tBotConstructChatMessage(cs, m->chatmessage, mcontext, &bestmatch, vcontext, qtrue);\n\t\t\t\tBotRemoveTildes(cs->chatmessage);\n\t\t\t\tbotimport.Print(PRT_MESSAGE, \"%s\\n\", cs->chatmessage);\n\t\t\t} //end if\n\t\t} //end if\n\t\telse\n\t\t{\n\t\t\tbestchatmessage->time = AAS_Time() + CHATMESSAGE_RECENTTIME;\n\t\t\tBotConstructChatMessage(cs, bestchatmessage->chatmessage, mcontext, &bestmatch, vcontext, qtrue);\n\t\t} //end else\n\t\treturn qtrue;\n\t} //end if\n\treturn qfalse;\n} //end of the function BotReplyChat\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint BotChatLength(int chatstate)\n{\n\tbot_chatstate_t *cs;\n\n\tcs = BotChatStateFromHandle(chatstate);\n\tif (!cs) return 0;\n\treturn strlen(cs->chatmessage);\n} //end of the function BotChatLength\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid BotEnterChat(int chatstate, int clientto, int sendto)\n{\n\tbot_chatstate_t *cs;\n\n\tcs = BotChatStateFromHandle(chatstate);\n\tif (!cs) return;\n\n\tif (strlen(cs->chatmessage))\n\t{\n\t\tBotRemoveTildes(cs->chatmessage);\n\t\tif (LibVarGetValue(\"bot_testichat\")) {\n\t\t\tbotimport.Print(PRT_MESSAGE, \"%s\\n\", cs->chatmessage);\n\t\t}\n\t\telse {\n\t\t\tswitch(sendto) {\n\t\t\t\tcase CHAT_TEAM:\n\t\t\t\t\tEA_Command(cs->client, va(\"say_team %s\", cs->chatmessage));\n\t\t\t\t\tbreak;\n\t\t\t\tcase CHAT_TELL:\n\t\t\t\t\tEA_Command(cs->client, va(\"tell %d %s\", clientto, cs->chatmessage));\n\t\t\t\t\tbreak;\n\t\t\t\tdefault: //CHAT_ALL\n\t\t\t\t\tEA_Command(cs->client, va(\"say %s\", cs->chatmessage));\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\t//clear the chat message from the state\n\t\tstrcpy(cs->chatmessage, \"\");\n\t} //end if\n} //end of the function BotEnterChat\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid BotGetChatMessage(int chatstate, char *buf, int size)\n{\n\tbot_chatstate_t *cs;\n\n\tcs = BotChatStateFromHandle(chatstate);\n\tif (!cs) return;\n\n\tBotRemoveTildes(cs->chatmessage);\n\tstrncpy(buf, cs->chatmessage, size-1);\n\tbuf[size-1] = '\\0';\n\t//clear the chat message from the state\n\tstrcpy(cs->chatmessage, \"\");\n} //end of the function BotGetChatMessage\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid BotSetChatGender(int chatstate, int gender)\n{\n\tbot_chatstate_t *cs;\n\n\tcs = BotChatStateFromHandle(chatstate);\n\tif (!cs) return;\n\tswitch(gender)\n\t{\n\t\tcase CHAT_GENDERFEMALE: cs->gender = CHAT_GENDERFEMALE; break;\n\t\tcase CHAT_GENDERMALE: cs->gender = CHAT_GENDERMALE; break;\n\t\tdefault: cs->gender = CHAT_GENDERLESS; break;\n\t} //end switch\n} //end of the function BotSetChatGender\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid BotSetChatName(int chatstate, char *name, int client)\n{\n\tbot_chatstate_t *cs;\n\n\tcs = BotChatStateFromHandle(chatstate);\n\tif (!cs) return;\n\tcs->client = client;\n\tCom_Memset(cs->name, 0, sizeof(cs->name));\n\tstrncpy(cs->name, name, sizeof(cs->name));\n\tcs->name[sizeof(cs->name)-1] = '\\0';\n} //end of the function BotSetChatName\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid BotResetChatAI(void)\n{\n\tbot_replychat_t *rchat;\n\tbot_chatmessage_t *m;\n\n\tfor (rchat = replychats; rchat; rchat = rchat->next)\n\t{\n\t\tfor (m = rchat->firstchatmessage; m; m = m->next)\n\t\t{\n\t\t\tm->time = 0;\n\t\t} //end for\n\t} //end for\n} //end of the function BotResetChatAI\n//========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//========================================================================\nint BotAllocChatState(void)\n{\n\tint i;\n\n\tfor (i = 1; i <= MAX_CLIENTS; i++)\n\t{\n\t\tif (!botchatstates[i])\n\t\t{\n\t\t\tbotchatstates[i] = GetClearedMemory(sizeof(bot_chatstate_t));\n\t\t\treturn i;\n\t\t} //end if\n\t} //end for\n\treturn 0;\n} //end of the function BotAllocChatState\n//========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//========================================================================\nvoid BotFreeChatState(int handle)\n{\n\t//bot_chatstate_t *cs;\n\tbot_consolemessage_t m;\n\tint h;\n\n\tif (handle <= 0 || handle > MAX_CLIENTS)\n\t{\n\t\tbotimport.Print(PRT_FATAL, \"chat state handle %d out of range\\n\", handle);\n\t\treturn;\n\t} //end if\n\tif (!botchatstates[handle])\n\t{\n\t\tbotimport.Print(PRT_FATAL, \"invalid chat state %d\\n\", handle);\n\t\treturn;\n\t} //end if\n\t//cs = botchatstates[handle];\n\tif (LibVarGetValue(\"bot_reloadcharacters\"))\n\t{\n\t\tBotFreeChatFile(handle);\n\t} //end if\n\t//free all the console messages left in the chat state\n\tfor (h = BotNextConsoleMessage(handle, &m); h; h = BotNextConsoleMessage(handle, &m))\n\t{\n\t\t//remove the console message\n\t\tBotRemoveConsoleMessage(handle, h);\n\t} //end for\n\tFreeMemory(botchatstates[handle]);\n\tbotchatstates[handle] = NULL;\n} //end of the function BotFreeChatState\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint BotSetupChatAI(void)\n{\n\tchar *file;\n\n#ifdef DEBUG\n\tint starttime = Sys_MilliSeconds();\n#endif //DEBUG\n\n\tfile = LibVarString(\"synfile\", \"syn.c\");\n\tsynonyms = BotLoadSynonyms(file);\n\tfile = LibVarString(\"rndfile\", \"rnd.c\");\n\trandomstrings = BotLoadRandomStrings(file);\n\tfile = LibVarString(\"matchfile\", \"match.c\");\n\tmatchtemplates = BotLoadMatchTemplates(file);\n\t//\n\tif (!LibVarValue(\"nochat\", \"0\"))\n\t{\n\t\tfile = LibVarString(\"rchatfile\", \"rchat.c\");\n\t\treplychats = BotLoadReplyChat(file);\n\t} //end if\n\n\tInitConsoleMessageHeap();\n\n#ifdef DEBUG\n\tbotimport.Print(PRT_MESSAGE, \"setup chat AI %d msec\\n\", Sys_MilliSeconds() - starttime);\n#endif //DEBUG\n\treturn BLERR_NOERROR;\n} //end of the function BotSetupChatAI\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid BotShutdownChatAI(void)\n{\n\tint i;\n\n\t//free all remaining chat states\n\tfor(i = 0; i < MAX_CLIENTS; i++)\n\t{\n\t\tif (botchatstates[i])\n\t\t{\n\t\t\tBotFreeChatState(i);\n\t\t} //end if\n\t} //end for\n\t//free all cached chats\n\tfor(i = 0; i < MAX_CLIENTS; i++)\n\t{\n\t\tif (ichatdata[i])\n\t\t{\n\t\t\tFreeMemory(ichatdata[i]->chat);\n\t\t\tFreeMemory(ichatdata[i]);\n\t\t\tichatdata[i] = NULL;\n\t\t} //end if\n\t} //end for\n\tif (consolemessageheap) FreeMemory(consolemessageheap);\n\tconsolemessageheap = NULL;\n\tif (matchtemplates) BotFreeMatchTemplates(matchtemplates);\n\tmatchtemplates = NULL;\n\tif (randomstrings) FreeMemory(randomstrings);\n\trandomstrings = NULL;\n\tif (synonyms) FreeMemory(synonyms);\n\tsynonyms = NULL;\n\tif (replychats) BotFreeReplyChat(replychats);\n\treplychats = NULL;\n} //end of the function BotShutdownChatAI\n"
  },
  {
    "path": "plugins/quake3/botlib/be_ai_chat.h",
    "content": "/*\n===========================================================================\nCopyright (C) 1999-2005 Id Software, Inc.\n\nThis file is part of Quake III Arena source code.\n\nQuake III Arena source code is free software; you can redistribute it\nand/or modify it under the terms of the GNU General Public License as\npublished by the Free Software Foundation; either version 2 of the License,\nor (at your option) any later version.\n\nQuake III Arena source code is distributed in the hope that it will be\nuseful, but WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Quake III Arena source code; if not, write to the Free Software\nFoundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n===========================================================================\n*/\n//\n/*****************************************************************************\n * name:\t\tbe_ai_chat.h\n *\n * desc:\t\tchar AI\n *\n * $Archive: /source/code/botlib/be_ai_chat.h $\n *\n *****************************************************************************/\n\n#define MAX_MESSAGE_SIZE\t\t256\n#define MAX_CHATTYPE_NAME\t\t32\n#define MAX_MATCHVARIABLES\t\t8\n\n#define CHAT_GENDERLESS\t\t\t0\n#define CHAT_GENDERFEMALE\t\t1\n#define CHAT_GENDERMALE\t\t\t2\n\n#define CHAT_ALL\t\t\t\t\t0\n#define CHAT_TEAM\t\t\t\t\t1\n#define CHAT_TELL\t\t\t\t\t2\n\n//a console message\ntypedef struct bot_consolemessage_s\n{\n\tint handle;\n\tfloat time;\t\t\t\t\t\t\t\t\t//message time\n\tint type;\t\t\t\t\t\t\t\t\t//message type\n\tchar message[MAX_MESSAGE_SIZE];\t\t\t\t//message\n\tstruct bot_consolemessage_s *prev, *next;\t//prev and next in list\n} bot_consolemessage_t;\n\n//match variable\ntypedef struct bot_matchvariable_s\n{\n\tchar offset;\n\tint length;\n} bot_matchvariable_t;\n//returned to AI when a match is found\ntypedef struct bot_match_s\n{\n\tchar string[MAX_MESSAGE_SIZE];\n\tint type;\n\tint subtype;\n\tbot_matchvariable_t variables[MAX_MATCHVARIABLES];\n} bot_match_t;\n\n//setup the chat AI\nint BotSetupChatAI(void);\n//shutdown the chat AI\nvoid BotShutdownChatAI(void);\n//returns the handle to a newly allocated chat state\nint BotAllocChatState(void);\n//frees the chatstate\nvoid BotFreeChatState(int handle);\n//adds a console message to the chat state\nvoid BotQueueConsoleMessage(int chatstate, int type, char *message);\n//removes the console message from the chat state\nvoid BotRemoveConsoleMessage(int chatstate, int handle);\n//returns the next console message from the state\nint BotNextConsoleMessage(int chatstate, bot_consolemessage_t *cm);\n//returns the number of console messages currently stored in the state\nint BotNumConsoleMessages(int chatstate);\n//selects a chat message of the given type\nvoid BotInitialChat(int chatstate, char *type, int mcontext, char *var0, char *var1, char *var2, char *var3, char *var4, char *var5, char *var6, char *var7);\n//returns the number of initial chat messages of the given type\nint BotNumInitialChats(int chatstate, char *type);\n//find and select a reply for the given message\nint BotReplyChat(int chatstate, char *message, int mcontext, int vcontext, char *var0, char *var1, char *var2, char *var3, char *var4, char *var5, char *var6, char *var7);\n//returns the length of the currently selected chat message\nint BotChatLength(int chatstate);\n//enters the selected chat message\nvoid BotEnterChat(int chatstate, int clientto, int sendto);\n//get the chat message ready to be output\nvoid BotGetChatMessage(int chatstate, char *buf, int size);\n//checks if the first string contains the second one, returns index into first string or -1 if not found\nint StringContains(char *str1, char *str2, int casesensitive);\n//finds a match for the given string using the match templates\nint BotFindMatch(char *str, bot_match_t *match, unsigned long int context);\n//returns a variable from a match\nvoid BotMatchVariable(bot_match_t *match, int variable, char *buf, int size);\n//unify all the white spaces in the string\nvoid UnifyWhiteSpaces(char *string);\n//replace all the context related synonyms in the string\nvoid BotReplaceSynonyms(char *string, unsigned long int context);\n//loads a chat file for the chat state\nint BotLoadChatFile(int chatstate, char *chatfile, char *chatname);\n//store the gender of the bot in the chat state\nvoid BotSetChatGender(int chatstate, int gender);\n//store the bot name in the chat state\nvoid BotSetChatName(int chatstate, char *name, int client);\n\n"
  },
  {
    "path": "plugins/quake3/botlib/be_ai_gen.c",
    "content": "/*\n===========================================================================\nCopyright (C) 1999-2005 Id Software, Inc.\n\nThis file is part of Quake III Arena source code.\n\nQuake III Arena source code is free software; you can redistribute it\nand/or modify it under the terms of the GNU General Public License as\npublished by the Free Software Foundation; either version 2 of the License,\nor (at your option) any later version.\n\nQuake III Arena source code is distributed in the hope that it will be\nuseful, but WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Quake III Arena source code; if not, write to the Free Software\nFoundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n===========================================================================\n*/\n\n/*****************************************************************************\n * name:\t\tbe_ai_gen.c\n *\n * desc:\t\tgenetic selection\n *\n * $Archive: /MissionPack/code/botlib/be_ai_gen.c $\n *\n *****************************************************************************/\n\n#include \"q_shared.h\"\n#include \"l_memory.h\"\n#include \"l_log.h\"\n#include \"l_utils.h\"\n#include \"l_script.h\"\n#include \"l_precomp.h\"\n#include \"l_struct.h\"\n#include \"aasfile.h\"\n#include \"botlib.h\"\n#include \"be_aas.h\"\n#include \"be_aas_funcs.h\"\n#include \"be_interface.h\"\n#include \"be_ai_gen.h\"\n\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint GeneticSelection(int numranks, float *rankings)\n{\n\tfloat sum;\n\tint i, index;\n\n\tsum = 0;\n\tfor (i = 0; i < numranks; i++)\n\t{\n\t\tif (rankings[i] < 0) continue;\n\t\tsum += rankings[i];\n\t} //end for\n\tif (sum > 0)\n\t{\n\t\t//select a bot where the ones with the higest rankings have\n\t\t//the highest chance of being selected\n//\t\tselect = random() * sum;\n\t\tfor (i = 0; i < numranks; i++)\n\t\t{\n\t\t\tif (rankings[i] < 0) continue;\n\t\t\tsum -= rankings[i];\n\t\t\tif (sum <= 0) return i;\n\t\t} //end for\n\t} //end if\n\t//select a bot randomly\n\tindex = random() * numranks;\n\tfor (i = 0; i < numranks; i++)\n\t{\n\t\tif (rankings[index] >= 0) return index;\n\t\tindex = (index + 1) % numranks;\n\t} //end for\n\treturn 0;\n} //end of the function GeneticSelection\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint GeneticParentsAndChildSelection(int numranks, float *ranks, int *parent1, int *parent2, int *child)\n{\n\tfloat rankings[256], max;\n\tint i;\n\n\tif (numranks > 256)\n\t{\n\t\tbotimport.Print(PRT_WARNING, \"GeneticParentsAndChildSelection: too many bots\\n\");\n\t\t*parent1 = *parent2 = *child = 0;\n\t\treturn qfalse;\n\t} //end if\n\tfor (max = 0, i = 0; i < numranks; i++)\n\t{\n\t\tif (ranks[i] < 0) continue;\n\t\tmax++;\n\t} //end for\n\tif (max < 3)\n\t{\n\t\tbotimport.Print(PRT_WARNING, \"GeneticParentsAndChildSelection: too few valid bots\\n\");\n\t\t*parent1 = *parent2 = *child = 0;\n\t\treturn qfalse;\n\t} //end if\n\tCom_Memcpy(rankings, ranks, sizeof(float) * numranks);\n\t//select first parent\n\t*parent1 = GeneticSelection(numranks, rankings);\n\trankings[*parent1] = -1;\n\t//select second parent\n\t*parent2 = GeneticSelection(numranks, rankings);\n\trankings[*parent2] = -1;\n\t//reverse the rankings\n\tmax = 0;\n\tfor (i = 0; i < numranks; i++)\n\t{\n\t\tif (rankings[i] < 0) continue;\n\t\tif (rankings[i] > max) max = rankings[i];\n\t} //end for\n\tfor (i = 0; i < numranks; i++)\n\t{\n\t\tif (rankings[i] < 0) continue;\n\t\trankings[i] = max - rankings[i];\n\t} //end for\n\t//select child\n\t*child = GeneticSelection(numranks, rankings);\n\treturn qtrue;\n} //end of the function GeneticParentsAndChildSelection\n"
  },
  {
    "path": "plugins/quake3/botlib/be_ai_gen.h",
    "content": "/*\n===========================================================================\nCopyright (C) 1999-2005 Id Software, Inc.\n\nThis file is part of Quake III Arena source code.\n\nQuake III Arena source code is free software; you can redistribute it\nand/or modify it under the terms of the GNU General Public License as\npublished by the Free Software Foundation; either version 2 of the License,\nor (at your option) any later version.\n\nQuake III Arena source code is distributed in the hope that it will be\nuseful, but WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Quake III Arena source code; if not, write to the Free Software\nFoundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n===========================================================================\n*/\n//\n\n/*****************************************************************************\n * name:\t\tbe_ai_gen.h\n *\n * desc:\t\tgenetic selection\n *\n * $Archive: /source/code/botlib/be_ai_gen.h $\n *\n *****************************************************************************/\n\nint GeneticParentsAndChildSelection(int numranks, float *ranks, int *parent1, int *parent2, int *child);\n"
  },
  {
    "path": "plugins/quake3/botlib/be_ai_goal.c",
    "content": "/*\n===========================================================================\nCopyright (C) 1999-2005 Id Software, Inc.\n\nThis file is part of Quake III Arena source code.\n\nQuake III Arena source code is free software; you can redistribute it\nand/or modify it under the terms of the GNU General Public License as\npublished by the Free Software Foundation; either version 2 of the License,\nor (at your option) any later version.\n\nQuake III Arena source code is distributed in the hope that it will be\nuseful, but WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Quake III Arena source code; if not, write to the Free Software\nFoundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n===========================================================================\n*/\n\n/*****************************************************************************\n * name:\t\tbe_ai_goal.c\n *\n * desc:\t\tgoal AI\n *\n * $Archive: /MissionPack/code/botlib/be_ai_goal.c $\n *\n *****************************************************************************/\n\n#include \"q_shared.h\"\n#include \"l_utils.h\"\n#include \"l_libvar.h\"\n#include \"l_memory.h\"\n#include \"l_log.h\"\n#include \"l_script.h\"\n#include \"l_precomp.h\"\n#include \"l_struct.h\"\n#include \"aasfile.h\"\n#include \"botlib.h\"\n#include \"be_aas.h\"\n#include \"be_aas_funcs.h\"\n#include \"be_interface.h\"\n#include \"be_ai_weight.h\"\n#include \"be_ai_goal.h\"\n#include \"be_ai_move.h\"\n\n//#define DEBUG_AI_GOAL\n#ifdef RANDOMIZE\n#define UNDECIDEDFUZZY\n#endif //RANDOMIZE\n#define DROPPEDWEIGHT\n//minimum avoid goal time\n#define AVOID_MINIMUM_TIME\t\t10\n//default avoid goal time\n#define AVOID_DEFAULT_TIME\t\t30\n//avoid dropped goal time\n#define AVOID_DROPPED_TIME\t\t10\n//\n#define TRAVELTIME_SCALE\t\t0.01\n//item flags\n#define IFL_NOTFREE\t\t\t\t1\t\t//not in free for all\n#define IFL_NOTTEAM\t\t\t\t2\t\t//not in team play\n#define IFL_NOTSINGLE\t\t\t4\t\t//not in single player\n#define IFL_NOTBOT\t\t\t\t8\t\t//bot should never go for this\n#define IFL_ROAM\t\t\t\t16\t\t//bot roam goal\n\n//location in the map \"target_location\"\ntypedef struct maplocation_s\n{\n\tvec3_t origin;\n\tint areanum;\n\tchar name[MAX_EPAIRKEY];\n\tstruct maplocation_s *next;\n} maplocation_t;\n\n//camp spots \"info_camp\"\ntypedef struct campspot_s\n{\n\tvec3_t origin;\n\tint areanum;\n\tchar name[MAX_EPAIRKEY];\n\tfloat range;\n\tfloat weight;\n\tfloat wait;\n\tfloat random;\n\tstruct campspot_s *next;\n} campspot_t;\n\n//FIXME: these are game specific\ntypedef enum {\n\tGT_FFA,\t\t\t\t// free for all\n\tGT_TOURNAMENT,\t\t// one on one tournament\n\tGT_SINGLE_PLAYER,\t// single player tournament\n\n\t//-- team games go after this --\n\n\tGT_TEAM,\t\t\t// team deathmatch\n\tGT_CTF,\t\t\t\t// capture the flag\n#ifdef MISSIONPACK\n\tGT_1FCTF,\n\tGT_OBELISK,\n\tGT_HARVESTER,\n#endif\n\tGT_MAX_GAME_TYPE\n} gametype_t;\n\ntypedef struct levelitem_s\n{\n\tint number;\t\t\t\t\t\t\t//number of the level item\n\tint iteminfo;\t\t\t\t\t\t//index into the item info\n\tint flags;\t\t\t\t\t\t\t//item flags\n\tfloat weight;\t\t\t\t\t\t//fixed roam weight\n\tvec3_t origin;\t\t\t\t\t\t//origin of the item\n\tint goalareanum;\t\t\t\t\t//area the item is in\n\tvec3_t goalorigin;\t\t\t\t\t//goal origin within the area\n\tint entitynum;\t\t\t\t\t\t//entity number\n\tfloat timeout;\t\t\t\t\t\t//item is removed after this time\n\tstruct levelitem_s *prev, *next;\n} levelitem_t;\n\ntypedef struct iteminfo_s\n{\n\tchar classname[32];\t\t\t\t\t//classname of the item\n\tchar name[MAX_STRINGFIELD];\t\t\t//name of the item\n\tchar model[MAX_STRINGFIELD];\t\t//model of the item\n\tint modelindex;\t\t\t\t\t\t//model index\n\tint type;\t\t\t\t\t\t\t//item type\n\tint index;\t\t\t\t\t\t\t//index in the inventory\n\tfloat respawntime;\t\t\t\t\t//respawn time\n\tvec3_t mins;\t\t\t\t\t\t//mins of the item\n\tvec3_t maxs;\t\t\t\t\t\t//maxs of the item\n\tint number;\t\t\t\t\t\t\t//number of the item info\n} iteminfo_t;\n\n#define ITEMINFO_OFS(x)\t(size_t)&(((iteminfo_t *)0)->x)\n\nfielddef_t iteminfo_fields[] =\n{\n{\"name\", ITEMINFO_OFS(name), FT_STRING},\n{\"model\", ITEMINFO_OFS(model), FT_STRING},\n{\"modelindex\", ITEMINFO_OFS(modelindex), FT_INT},\n{\"type\", ITEMINFO_OFS(type), FT_INT},\n{\"index\", ITEMINFO_OFS(index), FT_INT},\n{\"respawntime\", ITEMINFO_OFS(respawntime), FT_FLOAT},\n{\"mins\", ITEMINFO_OFS(mins), FT_FLOAT|FT_ARRAY, 3},\n{\"maxs\", ITEMINFO_OFS(maxs), FT_FLOAT|FT_ARRAY, 3},\n{NULL, 0, 0}\n};\n\nstructdef_t iteminfo_struct =\n{\n\tsizeof(iteminfo_t), iteminfo_fields\n};\n\ntypedef struct itemconfig_s\n{\n\tint numiteminfo;\n\titeminfo_t *iteminfo;\n} itemconfig_t;\n\n//goal state\ntypedef struct bot_goalstate_s\n{\n\tstruct weightconfig_s *itemweightconfig;\t//weight config\n\tint *itemweightindex;\t\t\t\t\t\t//index from item to weight\n\t//\n\tint client;\t\t\t\t\t\t\t\t\t//client using this goal state\n\tint lastreachabilityarea;\t\t\t\t\t//last area with reachabilities the bot was in\n\t//\n\tbot_goal_t goalstack[MAX_GOALSTACK];\t\t//goal stack\n\tint goalstacktop;\t\t\t\t\t\t\t//the top of the goal stack\n\t//\n\tint avoidgoals[MAX_AVOIDGOALS];\t\t\t\t//goals to avoid\n\tfloat avoidgoaltimes[MAX_AVOIDGOALS];\t\t//times to avoid the goals\n} bot_goalstate_t;\n\nbot_goalstate_t *botgoalstates[MAX_CLIENTS + 1]; // FIXME: init?\n//item configuration\nitemconfig_t *itemconfig = NULL;\n//level items\nlevelitem_t *levelitemheap = NULL;\nlevelitem_t *freelevelitems = NULL;\nlevelitem_t *levelitems = NULL;\nint numlevelitems = 0;\n//map locations\nmaplocation_t *maplocations = NULL;\n//camp spots\ncampspot_t *campspots = NULL;\n//the game type\nint g_gametype = 0;\n//additional dropped item weight\nlibvar_t *droppedweight = NULL;\n\n//========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//========================================================================\nbot_goalstate_t *BotGoalStateFromHandle(int handle)\n{\n\tif (handle <= 0 || handle > MAX_CLIENTS)\n\t{\n\t\tbotimport.Print(PRT_FATAL, \"goal state handle %d out of range\\n\", handle);\n\t\treturn NULL;\n\t} //end if\n\tif (!botgoalstates[handle])\n\t{\n\t\tbotimport.Print(PRT_FATAL, \"invalid goal state %d\\n\", handle);\n\t\treturn NULL;\n\t} //end if\n\treturn botgoalstates[handle];\n} //end of the function BotGoalStateFromHandle\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid BotInterbreedGoalFuzzyLogic(int parent1, int parent2, int child)\n{\n\tbot_goalstate_t *p1, *p2, *c;\n\n\tp1 = BotGoalStateFromHandle(parent1);\n\tp2 = BotGoalStateFromHandle(parent2);\n\tc = BotGoalStateFromHandle(child);\n\n\tInterbreedWeightConfigs(p1->itemweightconfig, p2->itemweightconfig,\n\t\t\t\t\t\t\t\t\tc->itemweightconfig);\n} //end of the function BotInterbreedingGoalFuzzyLogic\n\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid BotSaveGoalFuzzyLogic(int goalstate, char *filename)\n{\n\t//bot_goalstate_t *gs;\n\n\t//gs = BotGoalStateFromHandle(goalstate);\n\n\t//WriteWeightConfig(filename, gs->itemweightconfig);\n} //end of the function BotSaveGoalFuzzyLogic\n\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid BotMutateGoalFuzzyLogic(int goalstate, float range)\n{\n\tbot_goalstate_t *gs;\n\n\tgs = BotGoalStateFromHandle(goalstate);\n\n\tEvolveWeightConfig(gs->itemweightconfig);\n} //end of the function BotMutateGoalFuzzyLogic\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nitemconfig_t *LoadItemConfig(char *filename)\n{\n\tint max_iteminfo;\n\ttoken_t token;\n\tchar path[MAX_PATH];\n\tsource_t *source;\n\titemconfig_t *ic;\n\titeminfo_t *ii;\n\n\tmax_iteminfo = (int) LibVarValue(\"max_iteminfo\", \"256\");\n\tif (max_iteminfo < 0)\n\t{\n\t\tbotimport.Print(PRT_ERROR, \"max_iteminfo = %d\\n\", max_iteminfo);\n\t\tmax_iteminfo = 256;\n\t\tLibVarSet( \"max_iteminfo\", \"256\" );\n\t}\n\n\tstrncpy( path, filename, MAX_PATH-1 );\n\tpath[MAX_PATH-1] = 0;\n\tPC_SetBaseFolder(BOTFILESBASEFOLDER);\n\tsource = LoadSourceFile( path );\n\tif( !source ) {\n\t\tbotimport.Print( PRT_ERROR, \"counldn't load %s\\n\", path );\n\t\treturn NULL;\n\t} //end if\n\t//initialize item config\n\tic = (itemconfig_t *) GetClearedHunkMemory(sizeof(itemconfig_t) +\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tmax_iteminfo * sizeof(iteminfo_t));\n\tic->iteminfo = (iteminfo_t *) ((char *) ic + sizeof(itemconfig_t));\n\tic->numiteminfo = 0;\n\t//parse the item config file\n\twhile(PC_ReadToken(source, &token))\n\t{\n\t\tif (!strcmp(token.string, \"iteminfo\"))\n\t\t{\n\t\t\tif (ic->numiteminfo >= max_iteminfo)\n\t\t\t{\n\t\t\t\tSourceError(source, \"more than %d item info defined\\n\", max_iteminfo);\n\t\t\t\tFreeMemory(ic);\n\t\t\t\tFreeSource(source);\n\t\t\t\treturn NULL;\n\t\t\t} //end if\n\t\t\tii = &ic->iteminfo[ic->numiteminfo];\n\t\t\tCom_Memset(ii, 0, sizeof(iteminfo_t));\n\t\t\tif (!PC_ExpectTokenType(source, TT_STRING, 0, &token))\n\t\t\t{\n\t\t\t\tFreeMemory(ic);\n\t\t\t\tFreeMemory(source);\n\t\t\t\treturn NULL;\n\t\t\t} //end if\n\t\t\tStripDoubleQuotes(token.string);\n\t\t\tif (strlen(token.string) >= sizeof(ii->classname))\n\t\t\t{\n\t\t\t\tSourceError(source, \"more than %d chars\\n\", sizeof(ii->classname)-1);\n\t\t\t\tFreeMemory(ic);\n\t\t\t\tFreeSource(source);\n\t\t\t\treturn NULL;\n\t\t\t}\n\t\t\tstrcpy(ii->classname, token.string);\n\t\t\tif (!ReadStructure(source, &iteminfo_struct, (char *) ii))\n\t\t\t{\n\t\t\t\tFreeMemory(ic);\n\t\t\t\tFreeSource(source);\n\t\t\t\treturn NULL;\n\t\t\t} //end if\n\t\t\tii->number = ic->numiteminfo;\n\t\t\tic->numiteminfo++;\n\t\t} //end if\n\t\telse\n\t\t{\n\t\t\tSourceError(source, \"unknown definition %s\\n\", token.string);\n\t\t\tFreeMemory(ic);\n\t\t\tFreeSource(source);\n\t\t\treturn NULL;\n\t\t} //end else\n\t} //end while\n\tFreeSource(source);\n\t//\n\tif (!ic->numiteminfo) botimport.Print(PRT_WARNING, \"no item info loaded\\n\");\n\tbotimport.Print(PRT_MESSAGE, \"loaded %s\\n\", path);\n\treturn ic;\n} //end of the function LoadItemConfig\n//===========================================================================\n// index to find the weight function of an iteminfo\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint *ItemWeightIndex(weightconfig_t *iwc, itemconfig_t *ic)\n{\n\tint *index, i;\n\n\t//initialize item weight index\n\tindex = (int *) GetClearedMemory(sizeof(int) * ic->numiteminfo);\n\n\tfor (i = 0; i < ic->numiteminfo; i++)\n\t{\n\t\tindex[i] = FindFuzzyWeight(iwc, ic->iteminfo[i].classname);\n\t\tif (index[i] < 0)\n\t\t{\n\t\t\tLog_Write(\"item info %d \\\"%s\\\" has no fuzzy weight\\r\\n\", i, ic->iteminfo[i].classname);\n\t\t} //end if\n\t} //end for\n\treturn index;\n} //end of the function ItemWeightIndex\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid InitLevelItemHeap(void)\n{\n\tint i, max_levelitems;\n\n\tif (levelitemheap) FreeMemory(levelitemheap);\n\n\tmax_levelitems = (int) LibVarValue(\"max_levelitems\", \"256\");\n\tlevelitemheap = (levelitem_t *) GetClearedMemory(max_levelitems * sizeof(levelitem_t));\n\n\tfor (i = 0; i < max_levelitems-1; i++)\n\t{\n\t\tlevelitemheap[i].next = &levelitemheap[i + 1];\n\t} //end for\n\tlevelitemheap[max_levelitems-1].next = NULL;\n\t//\n\tfreelevelitems = levelitemheap;\n} //end of the function InitLevelItemHeap\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nlevelitem_t *AllocLevelItem(void)\n{\n\tlevelitem_t *li;\n\n\tli = freelevelitems;\n\tif (!li)\n\t{\n\t\tbotimport.Print(PRT_FATAL, \"out of level items\\n\");\n\t\treturn NULL;\n\t} //end if\n\t//\n\tfreelevelitems = freelevelitems->next;\n\tCom_Memset(li, 0, sizeof(levelitem_t));\n\treturn li;\n} //end of the function AllocLevelItem\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid FreeLevelItem(levelitem_t *li)\n{\n\tli->next = freelevelitems;\n\tfreelevelitems = li;\n} //end of the function FreeLevelItem\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid AddLevelItemToList(levelitem_t *li)\n{\n\tif (levelitems) levelitems->prev = li;\n\tli->prev = NULL;\n\tli->next = levelitems;\n\tlevelitems = li;\n} //end of the function AddLevelItemToList\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid RemoveLevelItemFromList(levelitem_t *li)\n{\n\tif (li->prev) li->prev->next = li->next;\n\telse levelitems = li->next;\n\tif (li->next) li->next->prev = li->prev;\n} //end of the function RemoveLevelItemFromList\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid BotFreeInfoEntities(void)\n{\n\tmaplocation_t *ml, *nextml;\n\tcampspot_t *cs, *nextcs;\n\n\tfor (ml = maplocations; ml; ml = nextml)\n\t{\n\t\tnextml = ml->next;\n\t\tFreeMemory(ml);\n\t} //end for\n\tmaplocations = NULL;\n\tfor (cs = campspots; cs; cs = nextcs)\n\t{\n\t\tnextcs = cs->next;\n\t\tFreeMemory(cs);\n\t} //end for\n\tcampspots = NULL;\n} //end of the function BotFreeInfoEntities\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid BotInitInfoEntities(void)\n{\n\tchar classname[MAX_EPAIRKEY];\n\tmaplocation_t *ml;\n\tcampspot_t *cs;\n\tint ent, numlocations, numcampspots;\n\n\tBotFreeInfoEntities();\n\t//\n\tnumlocations = 0;\n\tnumcampspots = 0;\n\tfor (ent = AAS_NextBSPEntity(0); ent; ent = AAS_NextBSPEntity(ent))\n\t{\n\t\tif (!AAS_ValueForBSPEpairKey(ent, \"classname\", classname, MAX_EPAIRKEY)) continue;\n\n\t\t//map locations\n\t\tif (!strcmp(classname, \"target_location\"))\n\t\t{\n\t\t\tml = (maplocation_t *) GetClearedMemory(sizeof(maplocation_t));\n\t\t\tAAS_VectorForBSPEpairKey(ent, \"origin\", ml->origin);\n\t\t\tAAS_ValueForBSPEpairKey(ent, \"message\", ml->name, sizeof(ml->name));\n\t\t\tml->areanum = AAS_PointAreaNum(ml->origin);\n\t\t\tml->next = maplocations;\n\t\t\tmaplocations = ml;\n\t\t\tnumlocations++;\n\t\t} //end if\n\t\t//camp spots\n\t\telse if (!strcmp(classname, \"info_camp\"))\n\t\t{\n\t\t\tcs = (campspot_t *) GetClearedMemory(sizeof(campspot_t));\n\t\t\tAAS_VectorForBSPEpairKey(ent, \"origin\", cs->origin);\n\t\t\t//cs->origin[2] += 16;\n\t\t\tAAS_ValueForBSPEpairKey(ent, \"message\", cs->name, sizeof(cs->name));\n\t\t\tAAS_FloatForBSPEpairKey(ent, \"range\", &cs->range);\n\t\t\tAAS_FloatForBSPEpairKey(ent, \"weight\", &cs->weight);\n\t\t\tAAS_FloatForBSPEpairKey(ent, \"wait\", &cs->wait);\n\t\t\tAAS_FloatForBSPEpairKey(ent, \"random\", &cs->random);\n\t\t\tcs->areanum = AAS_PointAreaNum(cs->origin);\n\t\t\tif (!cs->areanum)\n\t\t\t{\n\t\t\t\tbotimport.Print(PRT_MESSAGE, \"camp spot at %1.1f %1.1f %1.1f in solid\\n\", cs->origin[0], cs->origin[1], cs->origin[2]);\n\t\t\t\tFreeMemory(cs);\n\t\t\t\tcontinue;\n\t\t\t} //end if\n\t\t\tcs->next = campspots;\n\t\t\tcampspots = cs;\n\t\t\t//AAS_DrawPermanentCross(cs->origin, 4, LINECOLOR_YELLOW);\n\t\t\tnumcampspots++;\n\t\t} //end else if\n\t} //end for\n\tif (botDeveloper)\n\t{\n\t\tbotimport.Print(PRT_MESSAGE, \"%d map locations\\n\", numlocations);\n\t\tbotimport.Print(PRT_MESSAGE, \"%d camp spots\\n\", numcampspots);\n\t} //end if\n} //end of the function BotInitInfoEntities\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid BotInitLevelItems(void)\n{\n\tint i, spawnflags, value;\n\tchar classname[MAX_EPAIRKEY];\n\tvec3_t origin, end;\n\tint ent, goalareanum;\n\titemconfig_t *ic;\n\tlevelitem_t *li;\n\tbsp_trace_t trace;\n\n\t//initialize the map locations and camp spots\n\tBotInitInfoEntities();\n\n\t//initialize the level item heap\n\tInitLevelItemHeap();\n\tlevelitems = NULL;\n\tnumlevelitems = 0;\n\t//\n\tic = itemconfig;\n\tif (!ic) return;\n\n\t//if there's no AAS file loaded\n\tif (!AAS_Loaded()) return;\n\n\t//update the modelindexes of the item info\n\tfor (i = 0; i < ic->numiteminfo; i++)\n\t{\n\t\t//ic->iteminfo[i].modelindex = AAS_IndexFromModel(ic->iteminfo[i].model);\n\t\tif (!ic->iteminfo[i].modelindex)\n\t\t{\n\t\t\tLog_Write(\"item %s has modelindex 0\", ic->iteminfo[i].classname);\n\t\t} //end if\n\t} //end for\n\n\tfor (ent = AAS_NextBSPEntity(0); ent; ent = AAS_NextBSPEntity(ent))\n\t{\n\t\tif (!AAS_ValueForBSPEpairKey(ent, \"classname\", classname, MAX_EPAIRKEY)) continue;\n\t\t//\n\t\tspawnflags = 0;\n\t\tAAS_IntForBSPEpairKey(ent, \"spawnflags\", &spawnflags);\n\t\t//\n\t\tfor (i = 0; i < ic->numiteminfo; i++)\n\t\t{\n\t\t\tif (!strcmp(classname, ic->iteminfo[i].classname)) break;\n\t\t} //end for\n\t\tif (i >= ic->numiteminfo)\n\t\t{\n\t\t\tLog_Write(\"entity %s unknown item\\r\\n\", classname);\n\t\t\tcontinue;\n\t\t} //end if\n\t\t//get the origin of the item\n\t\tif (!AAS_VectorForBSPEpairKey(ent, \"origin\", origin))\n\t\t{\n\t\t\tbotimport.Print(PRT_ERROR, \"item %s without origin\\n\", classname);\n\t\t\tcontinue;\n\t\t} //end else\n\t\t//\n\t\tgoalareanum = 0;\n\t\t//if it is a floating item\n\t\tif (spawnflags & 1)\n\t\t{\n\t\t\t//if the item is not floating in water\n\t\t\tif (!(AAS_PointContents(origin) & CONTENTS_WATER))\n\t\t\t{\n\t\t\t\tVectorCopy(origin, end);\n\t\t\t\tend[2] -= 32;\n\t\t\t\ttrace = AAS_Trace(origin, ic->iteminfo[i].mins, ic->iteminfo[i].maxs, end, -1, CONTENTS_SOLID|CONTENTS_PLAYERCLIP);\n\t\t\t\t//if the item not near the ground\n\t\t\t\tif (trace.fraction >= 1)\n\t\t\t\t{\n\t\t\t\t\t//if the item is not reachable from a jumppad\n\t\t\t\t\tgoalareanum = AAS_BestReachableFromJumpPadArea(origin, ic->iteminfo[i].mins, ic->iteminfo[i].maxs);\n\t\t\t\t\tLog_Write(\"item %s reachable from jumppad area %d\\r\\n\", ic->iteminfo[i].classname, goalareanum);\n\t\t\t\t\t//botimport.Print(PRT_MESSAGE, \"item %s reachable from jumppad area %d\\r\\n\", ic->iteminfo[i].classname, goalareanum);\n\t\t\t\t\tif (!goalareanum) continue;\n\t\t\t\t} //end if\n\t\t\t} //end if\n\t\t} //end if\n\n\t\tli = AllocLevelItem();\n\t\tif (!li) return;\n\t\t//\n\t\tli->number = ++numlevelitems;\n\t\tli->timeout = 0;\n\t\tli->entitynum = 0;\n\t\t//\n\t\tli->flags = 0;\n\t\tAAS_IntForBSPEpairKey(ent, \"notfree\", &value);\n\t\tif (value) li->flags |= IFL_NOTFREE;\n\t\tAAS_IntForBSPEpairKey(ent, \"notteam\", &value);\n\t\tif (value) li->flags |= IFL_NOTTEAM;\n\t\tAAS_IntForBSPEpairKey(ent, \"notsingle\", &value);\n\t\tif (value) li->flags |= IFL_NOTSINGLE;\n\t\tAAS_IntForBSPEpairKey(ent, \"notbot\", &value);\n\t\tif (value) li->flags |= IFL_NOTBOT;\n\t\tif (!strcmp(classname, \"item_botroam\"))\n\t\t{\n\t\t\tli->flags |= IFL_ROAM;\n\t\t\tAAS_FloatForBSPEpairKey(ent, \"weight\", &li->weight);\n\t\t} //end if\n\t\t//if not a stationary item\n\t\tif (!(spawnflags & 1))\n\t\t{\n\t\t\tif (!AAS_DropToFloor(origin, ic->iteminfo[i].mins, ic->iteminfo[i].maxs))\n\t\t\t{\n\t\t\t\tbotimport.Print(PRT_MESSAGE, \"%s in solid at (%1.1f %1.1f %1.1f)\\n\",\n\t\t\t\t\t\t\t\t\t\t\t\tclassname, origin[0], origin[1], origin[2]);\n\t\t\t} //end if\n\t\t} //end if\n\t\t//item info of the level item\n\t\tli->iteminfo = i;\n\t\t//origin of the item\n\t\tVectorCopy(origin, li->origin);\n\t\t//\n\t\tif (goalareanum)\n\t\t{\n\t\t\tli->goalareanum = goalareanum;\n\t\t\tVectorCopy(origin, li->goalorigin);\n\t\t} //end if\n\t\telse\n\t\t{\n\t\t\t//get the item goal area and goal origin\n\t\t\tli->goalareanum = AAS_BestReachableArea(origin,\n\t\t\t\t\t\t\tic->iteminfo[i].mins, ic->iteminfo[i].maxs,\n\t\t\t\t\t\t\tli->goalorigin);\n\t\t\tif (!li->goalareanum)\n\t\t\t{\n\t\t\t\tbotimport.Print(PRT_MESSAGE, \"%s not reachable for bots at (%1.1f %1.1f %1.1f)\\n\",\n\t\t\t\t\t\t\t\t\t\t\t\tclassname, origin[0], origin[1], origin[2]);\n\t\t\t} //end if\n\t\t} //end else\n\t\t//\n\t\tAddLevelItemToList(li);\n\t} //end for\n\tbotimport.Print(PRT_MESSAGE, \"found %d level items\\n\", numlevelitems);\n} //end of the function BotInitLevelItems\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid BotGoalName(int number, char *name, int size)\n{\n\tlevelitem_t *li;\n\n\tif (!itemconfig) return;\n\t//\n\tfor (li = levelitems; li; li = li->next)\n\t{\n\t\tif (li->number == number)\n\t\t{\n\t\t\tsize_t r = strlen(itemconfig->iteminfo[li->iteminfo].name);\n\t\t\tif (r > size-1)\n\t\t\t\tr = size-1;\t//truncate...\n\t\t\tmemcpy(name, itemconfig->iteminfo[li->iteminfo].name, r);\n\t\t\tname[r] = '\\0';\n\t\t\treturn;\n\t\t} //end for\n\t} //end for\n\tstrcpy(name, \"\");\n\treturn;\n} //end of the function BotGoalName\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid BotResetAvoidGoals(int goalstate)\n{\n\tbot_goalstate_t *gs;\n\n\tgs = BotGoalStateFromHandle(goalstate);\n\tif (!gs) return;\n\tCom_Memset(gs->avoidgoals, 0, MAX_AVOIDGOALS * sizeof(int));\n\tCom_Memset(gs->avoidgoaltimes, 0, MAX_AVOIDGOALS * sizeof(float));\n} //end of the function BotResetAvoidGoals\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid BotDumpAvoidGoals(int goalstate)\n{\n\tint i;\n\tbot_goalstate_t *gs;\n\tchar name[32];\n\n\tgs = BotGoalStateFromHandle(goalstate);\n\tif (!gs) return;\n\tfor (i = 0; i < MAX_AVOIDGOALS; i++)\n\t{\n\t\tif (gs->avoidgoaltimes[i] >= AAS_Time())\n\t\t{\n\t\t\tBotGoalName(gs->avoidgoals[i], name, 32);\n\t\t\tLog_Write(\"avoid goal %s, number %d for %f seconds\", name,\n\t\t\t\tgs->avoidgoals[i], gs->avoidgoaltimes[i] - AAS_Time());\n\t\t} //end if\n\t} //end for\n} //end of the function BotDumpAvoidGoals\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid BotAddToAvoidGoals(bot_goalstate_t *gs, int number, float avoidtime)\n{\n\tint i;\n\n\tfor (i = 0; i < MAX_AVOIDGOALS; i++)\n\t{\n\t\t//if the avoid goal is already stored\n\t\tif (gs->avoidgoals[i] == number)\n\t\t{\n\t\t\tgs->avoidgoals[i] = number;\n\t\t\tgs->avoidgoaltimes[i] = AAS_Time() + avoidtime;\n\t\t\treturn;\n\t\t} //end if\n\t} //end for\n\n\tfor (i = 0; i < MAX_AVOIDGOALS; i++)\n\t{\n\t\t//if this avoid goal has expired\n\t\tif (gs->avoidgoaltimes[i] < AAS_Time())\n\t\t{\n\t\t\tgs->avoidgoals[i] = number;\n\t\t\tgs->avoidgoaltimes[i] = AAS_Time() + avoidtime;\n\t\t\treturn;\n\t\t} //end if\n\t} //end for\n} //end of the function BotAddToAvoidGoals\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid BotRemoveFromAvoidGoals(int goalstate, int number)\n{\n\tint i;\n\tbot_goalstate_t *gs;\n\n\tgs = BotGoalStateFromHandle(goalstate);\n\tif (!gs) return;\n\t//don't use the goals the bot wants to avoid\n\tfor (i = 0; i < MAX_AVOIDGOALS; i++)\n\t{\n\t\tif (gs->avoidgoals[i] == number && gs->avoidgoaltimes[i] >= AAS_Time())\n\t\t{\n\t\t\tgs->avoidgoaltimes[i] = 0;\n\t\t\treturn;\n\t\t} //end if\n\t} //end for\n} //end of the function BotRemoveFromAvoidGoals\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nfloat BotAvoidGoalTime(int goalstate, int number)\n{\n\tint i;\n\tbot_goalstate_t *gs;\n\n\tgs = BotGoalStateFromHandle(goalstate);\n\tif (!gs) return 0;\n\t//don't use the goals the bot wants to avoid\n\tfor (i = 0; i < MAX_AVOIDGOALS; i++)\n\t{\n\t\tif (gs->avoidgoals[i] == number && gs->avoidgoaltimes[i] >= AAS_Time())\n\t\t{\n\t\t\treturn gs->avoidgoaltimes[i] - AAS_Time();\n\t\t} //end if\n\t} //end for\n\treturn 0;\n} //end of the function BotAvoidGoalTime\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid BotSetAvoidGoalTime(int goalstate, int number, float avoidtime)\n{\n\tbot_goalstate_t *gs;\n\tlevelitem_t *li;\n\n\tgs = BotGoalStateFromHandle(goalstate);\n\tif (!gs)\n\t\treturn;\n\tif (avoidtime < 0)\n\t{\n\t\tif (!itemconfig)\n\t\t\treturn;\n\t\t//\n\t\tfor (li = levelitems; li; li = li->next)\n\t\t{\n\t\t\tif (li->number == number)\n\t\t\t{\n\t\t\t\tavoidtime = itemconfig->iteminfo[li->iteminfo].respawntime;\n\t\t\t\tif (!avoidtime)\n\t\t\t\t\tavoidtime = AVOID_DEFAULT_TIME;\n\t\t\t\tif (avoidtime < AVOID_MINIMUM_TIME)\n\t\t\t\t\tavoidtime = AVOID_MINIMUM_TIME;\n\t\t\t\tBotAddToAvoidGoals(gs, number, avoidtime);\n\t\t\t\treturn;\n\t\t\t} //end for\n\t\t} //end for\n\t\treturn;\n\t} //end if\n\telse\n\t{\n\t\tBotAddToAvoidGoals(gs, number, avoidtime);\n\t} //end else\n} //end of the function BotSetAvoidGoalTime\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint BotGetLevelItemGoal(int index, char *name, bot_goal_t *goal)\n{\n\tlevelitem_t *li;\n\n\tif (!itemconfig) return -1;\n\tli = levelitems;\n\tif (index >= 0)\n\t{\n\t\tfor (; li; li = li->next)\n\t\t{\n\t\t\tif (li->number == index)\n\t\t\t{\n\t\t\t\tli = li->next;\n\t\t\t\tbreak;\n\t\t\t} //end if\n\t\t} //end for\n\t} //end for\n\tfor (; li; li = li->next)\n\t{\n\t\t//\n\t\tif (g_gametype == GT_SINGLE_PLAYER) {\n\t\t\tif (li->flags & IFL_NOTSINGLE) continue;\n\t\t}\n\t\telse if (g_gametype >= GT_TEAM) {\n\t\t\tif (li->flags & IFL_NOTTEAM) continue;\n\t\t}\n\t\telse {\n\t\t\tif (li->flags & IFL_NOTFREE) continue;\n\t\t}\n\t\tif (li->flags & IFL_NOTBOT) continue;\n\t\t//\n\t\tif (!Q_stricmp(name, itemconfig->iteminfo[li->iteminfo].name))\n\t\t{\n\t\t\tgoal->areanum = li->goalareanum;\n\t\t\tVectorCopy(li->goalorigin, goal->origin);\n\t\t\tgoal->entitynum = li->entitynum;\n\t\t\tVectorCopy(itemconfig->iteminfo[li->iteminfo].mins, goal->mins);\n\t\t\tVectorCopy(itemconfig->iteminfo[li->iteminfo].maxs, goal->maxs);\n\t\t\tgoal->number = li->number;\n\t\t\tgoal->flags = GFL_ITEM;\n\t\t\tif (li->timeout) goal->flags |= GFL_DROPPED;\n\t\t\t//botimport.Print(PRT_MESSAGE, \"found li %s\\n\", itemconfig->iteminfo[li->iteminfo].name);\n\t\t\treturn li->number;\n\t\t} //end if\n\t} //end for\n\treturn -1;\n} //end of the function BotGetLevelItemGoal\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint BotGetMapLocationGoal(char *name, bot_goal_t *goal)\n{\n\tmaplocation_t *ml;\n\tvec3_t mins = {-8, -8, -8}, maxs = {8, 8, 8};\n\n\tfor (ml = maplocations; ml; ml = ml->next)\n\t{\n\t\tif (!Q_stricmp(ml->name, name))\n\t\t{\n\t\t\tgoal->areanum = ml->areanum;\n\t\t\tVectorCopy(ml->origin, goal->origin);\n\t\t\tgoal->entitynum = 0;\n\t\t\tVectorCopy(mins, goal->mins);\n\t\t\tVectorCopy(maxs, goal->maxs);\n\t\t\treturn qtrue;\n\t\t} //end if\n\t} //end for\n\treturn qfalse;\n} //end of the function BotGetMapLocationGoal\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint BotGetNextCampSpotGoal(int num, bot_goal_t *goal)\n{\n\tint i;\n\tcampspot_t *cs;\n\tvec3_t mins = {-8, -8, -8}, maxs = {8, 8, 8};\n\n\tif (num < 0) num = 0;\n\ti = num;\n\tfor (cs = campspots; cs; cs = cs->next)\n\t{\n\t\tif (--i < 0)\n\t\t{\n\t\t\tgoal->areanum = cs->areanum;\n\t\t\tVectorCopy(cs->origin, goal->origin);\n\t\t\tgoal->entitynum = 0;\n\t\t\tVectorCopy(mins, goal->mins);\n\t\t\tVectorCopy(maxs, goal->maxs);\n\t\t\treturn num+1;\n\t\t} //end if\n\t} //end for\n\treturn 0;\n} //end of the function BotGetNextCampSpotGoal\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid BotFindEntityForLevelItem(levelitem_t *li)\n{\n\tint ent, modelindex;\n\titemconfig_t *ic;\n\taas_entityinfo_t entinfo;\n\tvec3_t dir;\n\n\tic = itemconfig;\n\tif (!itemconfig) return;\n\tfor (ent = AAS_NextEntity(0); ent; ent = AAS_NextEntity(ent))\n\t{\n\t\t//get the model index of the entity\n\t\tmodelindex = AAS_EntityModelindex(ent);\n\t\t//\n\t\tif (!modelindex) continue;\n\t\t//get info about the entity\n\t\tAAS_EntityInfo(ent, &entinfo);\n\t\t//if the entity is still moving\n\t\tif (entinfo.origin[0] != entinfo.lastvisorigin[0] ||\n\t\t\t\tentinfo.origin[1] != entinfo.lastvisorigin[1] ||\n\t\t\t\tentinfo.origin[2] != entinfo.lastvisorigin[2]) continue;\n\t\t//\n\t\tif (ic->iteminfo[li->iteminfo].modelindex == modelindex)\n\t\t{\n\t\t\t//check if the entity is very close\n\t\t\tVectorSubtract(li->origin, entinfo.origin, dir);\n\t\t\tif (VectorLength(dir) < 30)\n\t\t\t{\n\t\t\t\t//found an entity for this level item\n\t\t\t\tli->entitynum = ent;\n\t\t\t} //end if\n\t\t} //end if\n\t} //end for\n} //end of the function BotFindEntityForLevelItem\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\n\n//NOTE: enum entityType_t in bg_public.h\n#define ET_ITEM\t\t\t2\n\nvoid BotUpdateEntityItems(void)\n{\n\tint ent, i, modelindex;\n\tvec3_t dir;\n\tlevelitem_t *li, *nextli;\n\taas_entityinfo_t entinfo;\n\titemconfig_t *ic;\n\n\t//timeout current entity items if necessary\n\tfor (li = levelitems; li; li = nextli)\n\t{\n\t\tnextli = li->next;\n\t\t//if it is a item that will time out\n\t\tif (li->timeout)\n\t\t{\n\t\t\t//timeout the item\n\t\t\tif (li->timeout < AAS_Time())\n\t\t\t{\n\t\t\t\tRemoveLevelItemFromList(li);\n\t\t\t\tFreeLevelItem(li);\n\t\t\t} //end if\n\t\t} //end if\n\t} //end for\n\t//find new entity items\n\tic = itemconfig;\n\tif (!itemconfig) return;\n\t//\n\tfor (ent = AAS_NextEntity(0); ent; ent = AAS_NextEntity(ent))\n\t{\n\t\tif (AAS_EntityType(ent) != ET_ITEM) continue;\n\t\t//get the model index of the entity\n\t\tmodelindex = AAS_EntityModelindex(ent);\n\t\t//\n\t\tif (!modelindex) continue;\n\t\t//get info about the entity\n\t\tAAS_EntityInfo(ent, &entinfo);\n\t\t//FIXME: don't do this\n\t\t//skip all floating items for now\n\t\t//if (entinfo.groundent != ENTITYNUM_WORLD) continue;\n\t\t//if the entity is still moving\n\t\tif (entinfo.origin[0] != entinfo.lastvisorigin[0] ||\n\t\t\t\tentinfo.origin[1] != entinfo.lastvisorigin[1] ||\n\t\t\t\tentinfo.origin[2] != entinfo.lastvisorigin[2]) continue;\n\t\t//check if the entity is already stored as a level item\n\t\tfor (li = levelitems; li; li = li->next)\n\t\t{\n\t\t\t//if the level item is linked to an entity\n\t\t\tif (li->entitynum && li->entitynum == ent)\n\t\t\t{\n\t\t\t\t//the entity is re-used if the models are different\n\t\t\t\tif (ic->iteminfo[li->iteminfo].modelindex != modelindex)\n\t\t\t\t{\n\t\t\t\t\t//remove this level item\n\t\t\t\t\tRemoveLevelItemFromList(li);\n\t\t\t\t\tFreeLevelItem(li);\n\t\t\t\t\tli = NULL;\n\t\t\t\t\tbreak;\n\t\t\t\t} //end if\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tif (entinfo.origin[0] != li->origin[0] ||\n\t\t\t\t\t\tentinfo.origin[1] != li->origin[1] ||\n\t\t\t\t\t\tentinfo.origin[2] != li->origin[2])\n\t\t\t\t\t{\n\t\t\t\t\t\tVectorCopy(entinfo.origin, li->origin);\n\t\t\t\t\t\t//also update the goal area number\n\t\t\t\t\t\tli->goalareanum = AAS_BestReachableArea(li->origin,\n\t\t\t\t\t\t\t\t\t\tic->iteminfo[li->iteminfo].mins, ic->iteminfo[li->iteminfo].maxs,\n\t\t\t\t\t\t\t\t\t\tli->goalorigin);\n\t\t\t\t\t} //end if\n\t\t\t\t\tbreak;\n\t\t\t\t} //end else\n\t\t\t} //end if\n\t\t} //end for\n\t\tif (li) continue;\n\t\t//try to link the entity to a level item\n\t\tfor (li = levelitems; li; li = li->next)\n\t\t{\n\t\t\t//if this level item is already linked\n\t\t\tif (li->entitynum) continue;\n\t\t\t//\n\t\t\tif (g_gametype == GT_SINGLE_PLAYER) {\n\t\t\t\tif (li->flags & IFL_NOTSINGLE) continue;\n\t\t\t}\n\t\t\telse if (g_gametype >= GT_TEAM) {\n\t\t\t\tif (li->flags & IFL_NOTTEAM) continue;\n\t\t\t}\n\t\t\telse {\n\t\t\t\tif (li->flags & IFL_NOTFREE) continue;\n\t\t\t}\n\t\t\t//if the model of the level item and the entity are the same\n\t\t\tif (ic->iteminfo[li->iteminfo].modelindex == modelindex)\n\t\t\t{\n\t\t\t\t//check if the entity is very close\n\t\t\t\tVectorSubtract(li->origin, entinfo.origin, dir);\n\t\t\t\tif (VectorLength(dir) < 30)\n\t\t\t\t{\n\t\t\t\t\t//found an entity for this level item\n\t\t\t\t\tli->entitynum = ent;\n\t\t\t\t\t//if the origin is different\n\t\t\t\t\tif (entinfo.origin[0] != li->origin[0] ||\n\t\t\t\t\t\tentinfo.origin[1] != li->origin[1] ||\n\t\t\t\t\t\tentinfo.origin[2] != li->origin[2])\n\t\t\t\t\t{\n\t\t\t\t\t\t//update the level item origin\n\t\t\t\t\t\tVectorCopy(entinfo.origin, li->origin);\n\t\t\t\t\t\t//also update the goal area number\n\t\t\t\t\t\tli->goalareanum = AAS_BestReachableArea(li->origin,\n\t\t\t\t\t\t\t\t\t\tic->iteminfo[li->iteminfo].mins, ic->iteminfo[li->iteminfo].maxs,\n\t\t\t\t\t\t\t\t\t\tli->goalorigin);\n\t\t\t\t\t} //end if\n#ifdef DEBUG\n\t\t\t\t\tLog_Write(\"linked item %s to an entity\", ic->iteminfo[li->iteminfo].classname);\n#endif //DEBUG\n\t\t\t\t\tbreak;\n\t\t\t\t} //end if\n\t\t\t} //end else\n\t\t} //end for\n\t\tif (li) continue;\n\t\t//check if the model is from a known item\n\t\tfor (i = 0; i < ic->numiteminfo; i++)\n\t\t{\n\t\t\tif (ic->iteminfo[i].modelindex == modelindex)\n\t\t\t{\n\t\t\t\tbreak;\n\t\t\t} //end if\n\t\t} //end for\n\t\t//if the model is not from a known item\n\t\tif (i >= ic->numiteminfo) continue;\n\t\t//allocate a new level item\n\t\tli = AllocLevelItem();\n\t\t//\n\t\tif (!li) continue;\n\t\t//entity number of the level item\n\t\tli->entitynum = ent;\n\t\t//number for the level item\n\t\tli->number = numlevelitems + ent;\n\t\t//set the item info index for the level item\n\t\tli->iteminfo = i;\n\t\t//origin of the item\n\t\tVectorCopy(entinfo.origin, li->origin);\n\t\t//get the item goal area and goal origin\n\t\tli->goalareanum = AAS_BestReachableArea(li->origin,\n\t\t\t\t\t\t\t\t\tic->iteminfo[i].mins, ic->iteminfo[i].maxs,\n\t\t\t\t\t\t\t\t\tli->goalorigin);\n\t\t//never go for items dropped into jumppads\n\t\tif (AAS_AreaJumpPad(li->goalareanum))\n\t\t{\n\t\t\tFreeLevelItem(li);\n\t\t\tcontinue;\n\t\t} //end if\n\t\t//time this item out after 30 seconds\n\t\t//dropped items disappear after 30 seconds\n\t\tli->timeout = AAS_Time() + 30;\n\t\t//add the level item to the list\n\t\tAddLevelItemToList(li);\n\t\t//botimport.Print(PRT_MESSAGE, \"found new level item %s\\n\", ic->iteminfo[i].classname);\n\t} //end for\n\t/*\n\tfor (li = levelitems; li; li = li->next)\n\t{\n\t\tif (!li->entitynum)\n\t\t{\n\t\t\tBotFindEntityForLevelItem(li);\n\t\t} //end if\n\t} //end for*/\n} //end of the function BotUpdateEntityItems\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid BotDumpGoalStack(int goalstate)\n{\n\tint i;\n\tbot_goalstate_t *gs;\n\tchar name[32];\n\n\tgs = BotGoalStateFromHandle(goalstate);\n\tif (!gs) return;\n\tfor (i = 1; i <= gs->goalstacktop; i++)\n\t{\n\t\tBotGoalName(gs->goalstack[i].number, name, 32);\n\t\tLog_Write(\"%d: %s\", i, name);\n\t} //end for\n} //end of the function BotDumpGoalStack\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid BotPushGoal(int goalstate, bot_goal_t *goal)\n{\n\tbot_goalstate_t *gs;\n\n\tgs = BotGoalStateFromHandle(goalstate);\n\tif (!gs) return;\n\tif (gs->goalstacktop >= MAX_GOALSTACK-1)\n\t{\n\t\tbotimport.Print(PRT_ERROR, \"goal heap overflow\\n\");\n\t\tBotDumpGoalStack(goalstate);\n\t\treturn;\n\t} //end if\n\tgs->goalstacktop++;\n\tCom_Memcpy(&gs->goalstack[gs->goalstacktop], goal, sizeof(bot_goal_t));\n} //end of the function BotPushGoal\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid BotPopGoal(int goalstate)\n{\n\tbot_goalstate_t *gs;\n\n\tgs = BotGoalStateFromHandle(goalstate);\n\tif (!gs) return;\n\tif (gs->goalstacktop > 0) gs->goalstacktop--;\n} //end of the function BotPopGoal\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid BotEmptyGoalStack(int goalstate)\n{\n\tbot_goalstate_t *gs;\n\n\tgs = BotGoalStateFromHandle(goalstate);\n\tif (!gs) return;\n\tgs->goalstacktop = 0;\n} //end of the function BotEmptyGoalStack\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint BotGetTopGoal(int goalstate, bot_goal_t *goal)\n{\n\tbot_goalstate_t *gs;\n\n\tgs = BotGoalStateFromHandle(goalstate);\n\tif (!gs) return qfalse;\n\tif (!gs->goalstacktop) return qfalse;\n\tCom_Memcpy(goal, &gs->goalstack[gs->goalstacktop], sizeof(bot_goal_t));\n\treturn qtrue;\n} //end of the function BotGetTopGoal\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint BotGetSecondGoal(int goalstate, bot_goal_t *goal)\n{\n\tbot_goalstate_t *gs;\n\n\tgs = BotGoalStateFromHandle(goalstate);\n\tif (!gs) return qfalse;\n\tif (gs->goalstacktop <= 1) return qfalse;\n\tCom_Memcpy(goal, &gs->goalstack[gs->goalstacktop-1], sizeof(bot_goal_t));\n\treturn qtrue;\n} //end of the function BotGetSecondGoal\n//===========================================================================\n// pops a new long term goal on the goal stack in the goalstate\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint BotChooseLTGItem(int goalstate, vec3_t origin, int *inventory, int travelflags)\n{\n\tint areanum, t, weightnum;\n\tfloat weight, bestweight, avoidtime;\n\titeminfo_t *iteminfo;\n\titemconfig_t *ic;\n\tlevelitem_t *li, *bestitem;\n\tbot_goal_t goal;\n\tbot_goalstate_t *gs;\n\n\tgs = BotGoalStateFromHandle(goalstate);\n\tif (!gs)\n\t\treturn qfalse;\n\tif (!gs->itemweightconfig)\n\t\treturn qfalse;\n\t//get the area the bot is in\n\tareanum = BotReachabilityArea(origin, gs->client);\n\t//if the bot is in solid or if the area the bot is in has no reachability links\n\tif (!areanum || !AAS_AreaReachability(areanum))\n\t{\n\t\t//use the last valid area the bot was in\n\t\tareanum = gs->lastreachabilityarea;\n\t} //end if\n\t//remember the last area with reachabilities the bot was in\n\tgs->lastreachabilityarea = areanum;\n\t//if still in solid\n\tif (!areanum)\n\t\treturn qfalse;\n\t//the item configuration\n\tic = itemconfig;\n\tif (!itemconfig)\n\t\treturn qfalse;\n\t//best weight and item so far\n\tbestweight = 0;\n\tbestitem = NULL;\n\tCom_Memset(&goal, 0, sizeof(bot_goal_t));\n\t//go through the items in the level\n\tfor (li = levelitems; li; li = li->next)\n\t{\n\t\tif (g_gametype == GT_SINGLE_PLAYER) {\n\t\t\tif (li->flags & IFL_NOTSINGLE)\n\t\t\t\tcontinue;\n\t\t}\n\t\telse if (g_gametype >= GT_TEAM) {\n\t\t\tif (li->flags & IFL_NOTTEAM)\n\t\t\t\tcontinue;\n\t\t}\n\t\telse {\n\t\t\tif (li->flags & IFL_NOTFREE)\n\t\t\t\tcontinue;\n\t\t}\n\t\tif (li->flags & IFL_NOTBOT)\n\t\t\tcontinue;\n\t\t//if the item is not in a possible goal area\n\t\tif (!li->goalareanum)\n\t\t\tcontinue;\n\t\t//FIXME: is this a good thing? added this for items that never spawned into the game (f.i. CTF flags in obelisk)\n\t\tif (!li->entitynum && !(li->flags & IFL_ROAM))\n\t\t\tcontinue;\n\t\t//get the fuzzy weight function for this item\n\t\titeminfo = &ic->iteminfo[li->iteminfo];\n\t\tweightnum = gs->itemweightindex[iteminfo->number];\n\t\tif (weightnum < 0)\n\t\t\tcontinue;\n\n#ifdef UNDECIDEDFUZZY\n\t\tweight = FuzzyWeightUndecided(inventory, gs->itemweightconfig, weightnum);\n#else\n\t\tweight = FuzzyWeight(inventory, gs->itemweightconfig, weightnum);\n#endif //UNDECIDEDFUZZY\n#ifdef DROPPEDWEIGHT\n\t\t//HACK: to make dropped items more attractive\n\t\tif (li->timeout)\n\t\t\tweight += droppedweight->value;\n#endif //DROPPEDWEIGHT\n\t\t//use weight scale for item_botroam\n\t\tif (li->flags & IFL_ROAM) weight *= li->weight;\n\t\t//\n\t\tif (weight > 0)\n\t\t{\n\t\t\t//get the travel time towards the goal area\n\t\t\tt = AAS_AreaTravelTimeToGoalArea(areanum, origin, li->goalareanum, travelflags);\n\t\t\t//if the goal is reachable\n\t\t\tif (t > 0)\n\t\t\t{\n\t\t\t\t//if this item won't respawn before we get there\n\t\t\t\tavoidtime = BotAvoidGoalTime(goalstate, li->number);\n\t\t\t\tif (avoidtime - t * 0.009 > 0)\n\t\t\t\t\tcontinue;\n\t\t\t\t//\n\t\t\t\tweight /= (float) t * TRAVELTIME_SCALE;\n\t\t\t\t//\n\t\t\t\tif (weight > bestweight)\n\t\t\t\t{\n\t\t\t\t\tbestweight = weight;\n\t\t\t\t\tbestitem = li;\n\t\t\t\t} //end if\n\t\t\t} //end if\n\t\t} //end if\n\t} //end for\n\t//if no goal item found\n\tif (!bestitem)\n\t{\n\t\t/*\n\t\t//if not in lava or slime\n\t\tif (!AAS_AreaLava(areanum) && !AAS_AreaSlime(areanum))\n\t\t{\n\t\t\tif (AAS_RandomGoalArea(areanum, travelflags, &goal.areanum, goal.origin))\n\t\t\t{\n\t\t\t\tVectorSet(goal.mins, -15, -15, -15);\n\t\t\t\tVectorSet(goal.maxs, 15, 15, 15);\n\t\t\t\tgoal.entitynum = 0;\n\t\t\t\tgoal.number = 0;\n\t\t\t\tgoal.flags = GFL_ROAM;\n\t\t\t\tgoal.iteminfo = 0;\n\t\t\t\t//push the goal on the stack\n\t\t\t\tBotPushGoal(goalstate, &goal);\n\t\t\t\t//\n#ifdef DEBUG\n\t\t\t\tbotimport.Print(PRT_MESSAGE, \"chosen roam goal area %d\\n\", goal.areanum);\n#endif //DEBUG\n\t\t\t\treturn qtrue;\n\t\t\t} //end if\n\t\t} //end if\n\t\t*/\n\t\treturn qfalse;\n\t} //end if\n\t//create a bot goal for this item\n\titeminfo = &ic->iteminfo[bestitem->iteminfo];\n\tVectorCopy(bestitem->goalorigin, goal.origin);\n\tVectorCopy(iteminfo->mins, goal.mins);\n\tVectorCopy(iteminfo->maxs, goal.maxs);\n\tgoal.areanum = bestitem->goalareanum;\n\tgoal.entitynum = bestitem->entitynum;\n\tgoal.number = bestitem->number;\n\tgoal.flags = GFL_ITEM;\n\tif (bestitem->timeout)\n\t\tgoal.flags |= GFL_DROPPED;\n\tif (bestitem->flags & IFL_ROAM)\n\t\tgoal.flags |= GFL_ROAM;\n\tgoal.iteminfo = bestitem->iteminfo;\n\t//if it's a dropped item\n\tif (bestitem->timeout)\n\t{\n\t\tavoidtime = AVOID_DROPPED_TIME;\n\t} //end if\n\telse\n\t{\n\t\tavoidtime = iteminfo->respawntime;\n\t\tif (!avoidtime)\n\t\t\tavoidtime = AVOID_DEFAULT_TIME;\n\t\tif (avoidtime < AVOID_MINIMUM_TIME)\n\t\t\tavoidtime = AVOID_MINIMUM_TIME;\n\t} //end else\n\t//add the chosen goal to the goals to avoid for a while\n\tBotAddToAvoidGoals(gs, bestitem->number, avoidtime);\n\t//push the goal on the stack\n\tBotPushGoal(goalstate, &goal);\n\t//\n\treturn qtrue;\n} //end of the function BotChooseLTGItem\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint BotChooseNBGItem(int goalstate, vec3_t origin, int *inventory, int travelflags,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tbot_goal_t *ltg, float maxtime)\n{\n\tint areanum, t, weightnum, ltg_time;\n\tfloat weight, bestweight, avoidtime;\n\titeminfo_t *iteminfo;\n\titemconfig_t *ic;\n\tlevelitem_t *li, *bestitem;\n\tbot_goal_t goal;\n\tbot_goalstate_t *gs;\n\n\tgs = BotGoalStateFromHandle(goalstate);\n\tif (!gs)\n\t\treturn qfalse;\n\tif (!gs->itemweightconfig)\n\t\treturn qfalse;\n\t//get the area the bot is in\n\tareanum = BotReachabilityArea(origin, gs->client);\n\t//if the bot is in solid or if the area the bot is in has no reachability links\n\tif (!areanum || !AAS_AreaReachability(areanum))\n\t{\n\t\t//use the last valid area the bot was in\n\t\tareanum = gs->lastreachabilityarea;\n\t} //end if\n\t//remember the last area with reachabilities the bot was in\n\tgs->lastreachabilityarea = areanum;\n\t//if still in solid\n\tif (!areanum)\n\t\treturn qfalse;\n\t//\n\tif (ltg) ltg_time = AAS_AreaTravelTimeToGoalArea(areanum, origin, ltg->areanum, travelflags);\n\telse ltg_time = 99999;\n\t//the item configuration\n\tic = itemconfig;\n\tif (!itemconfig)\n\t\treturn qfalse;\n\t//best weight and item so far\n\tbestweight = 0;\n\tbestitem = NULL;\n\tCom_Memset(&goal, 0, sizeof(bot_goal_t));\n\t//go through the items in the level\n\tfor (li = levelitems; li; li = li->next)\n\t{\n\t\tif (g_gametype == GT_SINGLE_PLAYER) {\n\t\t\tif (li->flags & IFL_NOTSINGLE)\n\t\t\t\tcontinue;\n\t\t}\n\t\telse if (g_gametype >= GT_TEAM) {\n\t\t\tif (li->flags & IFL_NOTTEAM)\n\t\t\t\tcontinue;\n\t\t}\n\t\telse {\n\t\t\tif (li->flags & IFL_NOTFREE)\n\t\t\t\tcontinue;\n\t\t}\n\t\tif (li->flags & IFL_NOTBOT)\n\t\t\tcontinue;\n\t\t//if the item is in a possible goal area\n\t\tif (!li->goalareanum)\n\t\t\tcontinue;\n\t\t//FIXME: is this a good thing? added this for items that never spawned into the game (f.i. CTF flags in obelisk)\n\t\tif (!li->entitynum && !(li->flags & IFL_ROAM))\n\t\t\tcontinue;\n\t\t//get the fuzzy weight function for this item\n\t\titeminfo = &ic->iteminfo[li->iteminfo];\n\t\tweightnum = gs->itemweightindex[iteminfo->number];\n\t\tif (weightnum < 0)\n\t\t\tcontinue;\n\t\t//\n#ifdef UNDECIDEDFUZZY\n\t\tweight = FuzzyWeightUndecided(inventory, gs->itemweightconfig, weightnum);\n#else\n\t\tweight = FuzzyWeight(inventory, gs->itemweightconfig, weightnum);\n#endif //UNDECIDEDFUZZY\n#ifdef DROPPEDWEIGHT\n\t\t//HACK: to make dropped items more attractive\n\t\tif (li->timeout)\n\t\t\tweight += droppedweight->value;\n#endif //DROPPEDWEIGHT\n\t\t//use weight scale for item_botroam\n\t\tif (li->flags & IFL_ROAM) weight *= li->weight;\n\t\t//\n\t\tif (weight > 0)\n\t\t{\n\t\t\t//get the travel time towards the goal area\n\t\t\tt = AAS_AreaTravelTimeToGoalArea(areanum, origin, li->goalareanum, travelflags);\n\t\t\t//if the goal is reachable\n\t\t\tif (t > 0 && t < maxtime)\n\t\t\t{\n\t\t\t\t//if this item won't respawn before we get there\n\t\t\t\tavoidtime = BotAvoidGoalTime(goalstate, li->number);\n\t\t\t\tif (avoidtime - t * 0.009 > 0)\n\t\t\t\t\tcontinue;\n\t\t\t\t//\n\t\t\t\tweight /= (float) t * TRAVELTIME_SCALE;\n\t\t\t\t//\n\t\t\t\tif (weight > bestweight)\n\t\t\t\t{\n\t\t\t\t\tt = 0;\n\t\t\t\t\tif (ltg && !li->timeout)\n\t\t\t\t\t{\n\t\t\t\t\t\t//get the travel time from the goal to the long term goal\n\t\t\t\t\t\tt = AAS_AreaTravelTimeToGoalArea(li->goalareanum, li->goalorigin, ltg->areanum, travelflags);\n\t\t\t\t\t} //end if\n\t\t\t\t\t//if the travel back is possible and doesn't take too long\n\t\t\t\t\tif (t <= ltg_time)\n\t\t\t\t\t{\n\t\t\t\t\t\tbestweight = weight;\n\t\t\t\t\t\tbestitem = li;\n\t\t\t\t\t} //end if\n\t\t\t\t} //end if\n\t\t\t} //end if\n\t\t} //end if\n\t} //end for\n\t//if no goal item found\n\tif (!bestitem)\n\t\treturn qfalse;\n\t//create a bot goal for this item\n\titeminfo = &ic->iteminfo[bestitem->iteminfo];\n\tVectorCopy(bestitem->goalorigin, goal.origin);\n\tVectorCopy(iteminfo->mins, goal.mins);\n\tVectorCopy(iteminfo->maxs, goal.maxs);\n\tgoal.areanum = bestitem->goalareanum;\n\tgoal.entitynum = bestitem->entitynum;\n\tgoal.number = bestitem->number;\n\tgoal.flags = GFL_ITEM;\n\tif (bestitem->timeout)\n\t\tgoal.flags |= GFL_DROPPED;\n\tif (bestitem->flags & IFL_ROAM)\n\t\tgoal.flags |= GFL_ROAM;\n\tgoal.iteminfo = bestitem->iteminfo;\n\t//if it's a dropped item\n\tif (bestitem->timeout)\n\t{\n\t\tavoidtime = AVOID_DROPPED_TIME;\n\t} //end if\n\telse\n\t{\n\t\tavoidtime = iteminfo->respawntime;\n\t\tif (!avoidtime)\n\t\t\tavoidtime = AVOID_DEFAULT_TIME;\n\t\tif (avoidtime < AVOID_MINIMUM_TIME)\n\t\t\tavoidtime = AVOID_MINIMUM_TIME;\n\t} //end else\n\t//add the chosen goal to the goals to avoid for a while\n\tBotAddToAvoidGoals(gs, bestitem->number, avoidtime);\n\t//push the goal on the stack\n\tBotPushGoal(goalstate, &goal);\n\t//\n\treturn qtrue;\n} //end of the function BotChooseNBGItem\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint BotTouchingGoal(vec3_t origin, bot_goal_t *goal)\n{\n\tint i;\n\tvec3_t boxmins, boxmaxs;\n\tvec3_t absmins, absmaxs;\n\tvec3_t safety_maxs = {0, 0, 0}; //{4, 4, 10};\n\tvec3_t safety_mins = {0, 0, 0}; //{-4, -4, 0};\n\n\tAAS_PresenceTypeBoundingBox(PRESENCE_NORMAL, boxmins, boxmaxs);\n\tVectorSubtract(goal->mins, boxmaxs, absmins);\n\tVectorSubtract(goal->maxs, boxmins, absmaxs);\n\tVectorAdd(absmins, goal->origin, absmins);\n\tVectorAdd(absmaxs, goal->origin, absmaxs);\n\t//make the box a little smaller for safety\n\tVectorSubtract(absmaxs, safety_maxs, absmaxs);\n\tVectorSubtract(absmins, safety_mins, absmins);\n\n\tfor (i = 0; i < 3; i++)\n\t{\n\t\tif (origin[i] < absmins[i] || origin[i] > absmaxs[i]) return qfalse;\n\t} //end for\n\treturn qtrue;\n} //end of the function BotTouchingGoal\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint BotItemGoalInVisButNotVisible(int viewer, vec3_t eye, vec3_t viewangles, bot_goal_t *goal)\n{\n\taas_entityinfo_t entinfo;\n\tbsp_trace_t trace;\n\tvec3_t middle;\n\n\tif (!(goal->flags & GFL_ITEM)) return qfalse;\n\t//\n\tVectorAdd(goal->mins, goal->mins, middle);\n\tVectorScale(middle, 0.5, middle);\n\tVectorAdd(goal->origin, middle, middle);\n\t//\n\ttrace = AAS_Trace(eye, NULL, NULL, middle, viewer, CONTENTS_SOLID);\n\t//if the goal middle point is visible\n\tif (trace.fraction >= 1)\n\t{\n\t\t//the goal entity number doesn't have to be valid\n\t\t//just assume it's valid\n\t\tif (goal->entitynum <= 0)\n\t\t\treturn qfalse;\n\t\t//\n\t\t//if the entity data isn't valid\n\t\tAAS_EntityInfo(goal->entitynum, &entinfo);\n\t\t//NOTE: for some wacko reason entities are sometimes\n\t\t// not updated\n\t\t//if (!entinfo.valid) return qtrue;\n\t\tif (entinfo.ltime < AAS_Time() - 0.5)\n\t\t\treturn qtrue;\n\t} //end if\n\treturn qfalse;\n} //end of the function BotItemGoalInVisButNotVisible\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid BotResetGoalState(int goalstate)\n{\n\tbot_goalstate_t *gs;\n\n\tgs = BotGoalStateFromHandle(goalstate);\n\tif (!gs) return;\n\tCom_Memset(gs->goalstack, 0, MAX_GOALSTACK * sizeof(bot_goal_t));\n\tgs->goalstacktop = 0;\n\tBotResetAvoidGoals(goalstate);\n} //end of the function BotResetGoalState\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint BotLoadItemWeights(int goalstate, char *filename)\n{\n\tbot_goalstate_t *gs;\n\n\tgs = BotGoalStateFromHandle(goalstate);\n\tif (!gs) return BLERR_CANNOTLOADITEMWEIGHTS;\n\t//load the weight configuration\n\tgs->itemweightconfig = ReadWeightConfig(filename);\n\tif (!gs->itemweightconfig)\n\t{\n\t\tbotimport.Print(PRT_FATAL, \"couldn't load weights\\n\");\n\t\treturn BLERR_CANNOTLOADITEMWEIGHTS;\n\t} //end if\n\t//if there's no item configuration\n\tif (!itemconfig) return BLERR_CANNOTLOADITEMWEIGHTS;\n\t//create the item weight index\n\tgs->itemweightindex = ItemWeightIndex(gs->itemweightconfig, itemconfig);\n\t//everything went ok\n\treturn BLERR_NOERROR;\n} //end of the function BotLoadItemWeights\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid BotFreeItemWeights(int goalstate)\n{\n\tbot_goalstate_t *gs;\n\n\tgs = BotGoalStateFromHandle(goalstate);\n\tif (!gs) return;\n\tif (gs->itemweightconfig) FreeWeightConfig(gs->itemweightconfig);\n\tif (gs->itemweightindex) FreeMemory(gs->itemweightindex);\n} //end of the function BotFreeItemWeights\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint BotAllocGoalState(int client)\n{\n\tint i;\n\n\tfor (i = 1; i <= MAX_CLIENTS; i++)\n\t{\n\t\tif (!botgoalstates[i])\n\t\t{\n\t\t\tbotgoalstates[i] = GetClearedMemory(sizeof(bot_goalstate_t));\n\t\t\tbotgoalstates[i]->client = client;\n\t\t\treturn i;\n\t\t} //end if\n\t} //end for\n\treturn 0;\n} //end of the function BotAllocGoalState\n//========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//========================================================================\nvoid BotFreeGoalState(int handle)\n{\n\tif (handle <= 0 || handle > MAX_CLIENTS)\n\t{\n\t\tbotimport.Print(PRT_FATAL, \"goal state handle %d out of range\\n\", handle);\n\t\treturn;\n\t} //end if\n\tif (!botgoalstates[handle])\n\t{\n\t\tbotimport.Print(PRT_FATAL, \"invalid goal state handle %d\\n\", handle);\n\t\treturn;\n\t} //end if\n\tBotFreeItemWeights(handle);\n\tFreeMemory(botgoalstates[handle]);\n\tbotgoalstates[handle] = NULL;\n} //end of the function BotFreeGoalState\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint BotSetupGoalAI(void)\n{\n\tchar *filename;\n\n\t//check if teamplay is on\n\tg_gametype = LibVarValue(\"g_gametype\", \"0\");\n\t//item configuration file\n\tfilename = LibVarString(\"itemconfig\", \"items.c\");\n\t//load the item configuration\n\titemconfig = LoadItemConfig(filename);\n\tif (!itemconfig)\n\t{\n\t\tbotimport.Print(PRT_FATAL, \"couldn't load item config\\n\");\n\t\treturn BLERR_CANNOTLOADITEMCONFIG;\n\t} //end if\n\t//\n\tdroppedweight = LibVar(\"droppedweight\", \"1000\");\n\t//everything went ok\n\treturn BLERR_NOERROR;\n} //end of the function BotSetupGoalAI\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid BotShutdownGoalAI(void)\n{\n\tint i;\n\n\tif (itemconfig) FreeMemory(itemconfig);\n\titemconfig = NULL;\n\tif (levelitemheap) FreeMemory(levelitemheap);\n\tlevelitemheap = NULL;\n\tfreelevelitems = NULL;\n\tlevelitems = NULL;\n\tnumlevelitems = 0;\n\n\tBotFreeInfoEntities();\n\n\tfor (i = 1; i <= MAX_CLIENTS; i++)\n\t{\n\t\tif (botgoalstates[i])\n\t\t{\n\t\t\tBotFreeGoalState(i);\n\t\t} //end if\n\t} //end for\n} //end of the function BotShutdownGoalAI\n"
  },
  {
    "path": "plugins/quake3/botlib/be_ai_goal.h",
    "content": "/*\n===========================================================================\nCopyright (C) 1999-2005 Id Software, Inc.\n\nThis file is part of Quake III Arena source code.\n\nQuake III Arena source code is free software; you can redistribute it\nand/or modify it under the terms of the GNU General Public License as\npublished by the Free Software Foundation; either version 2 of the License,\nor (at your option) any later version.\n\nQuake III Arena source code is distributed in the hope that it will be\nuseful, but WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Quake III Arena source code; if not, write to the Free Software\nFoundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n===========================================================================\n*/\n//\n/*****************************************************************************\n * name:\t\tbe_ai_goal.h\n *\n * desc:\t\tgoal AI\n *\n * $Archive: /source/code/botlib/be_ai_goal.h $\n *\n *****************************************************************************/\n\n#define MAX_AVOIDGOALS\t\t\t256\n#define MAX_GOALSTACK\t\t\t8\n\n#define GFL_NONE\t\t\t\t0\n#define GFL_ITEM\t\t\t\t1\n#define GFL_ROAM\t\t\t\t2\n#define GFL_DROPPED\t\t\t\t4\n\n//a bot goal\ntypedef struct bot_goal_s\n{\n\tvec3_t origin;\t\t\t\t//origin of the goal\n\tint areanum;\t\t\t\t//area number of the goal\n\tvec3_t mins, maxs;\t\t\t//mins and maxs of the goal\n\tint entitynum;\t\t\t\t//number of the goal entity\n\tint number;\t\t\t\t\t//goal number\n\tint flags;\t\t\t\t\t//goal flags\n\tint iteminfo;\t\t\t\t//item information\n} bot_goal_t;\n\n//reset the whole goal state, but keep the item weights\nvoid BotResetGoalState(int goalstate);\n//reset avoid goals\nvoid BotResetAvoidGoals(int goalstate);\n//remove the goal with the given number from the avoid goals\nvoid BotRemoveFromAvoidGoals(int goalstate, int number);\n//push a goal onto the goal stack\nvoid BotPushGoal(int goalstate, bot_goal_t *goal);\n//pop a goal from the goal stack\nvoid BotPopGoal(int goalstate);\n//empty the bot's goal stack\nvoid BotEmptyGoalStack(int goalstate);\n//dump the avoid goals\nvoid BotDumpAvoidGoals(int goalstate);\n//dump the goal stack\nvoid BotDumpGoalStack(int goalstate);\n//get the name name of the goal with the given number\nvoid BotGoalName(int number, char *name, int size);\n//get the top goal from the stack\nint BotGetTopGoal(int goalstate, bot_goal_t *goal);\n//get the second goal on the stack\nint BotGetSecondGoal(int goalstate, bot_goal_t *goal);\n//choose the best long term goal item for the bot\nint BotChooseLTGItem(int goalstate, vec3_t origin, int *inventory, int travelflags);\n//choose the best nearby goal item for the bot\n//the item may not be further away from the current bot position than maxtime\n//also the travel time from the nearby goal towards the long term goal may not\n//be larger than the travel time towards the long term goal from the current bot position\nint BotChooseNBGItem(int goalstate, vec3_t origin, int *inventory, int travelflags,\n\t\t\t\t\t\t\tbot_goal_t *ltg, float maxtime);\n//returns true if the bot touches the goal\nint BotTouchingGoal(vec3_t origin, bot_goal_t *goal);\n//returns true if the goal should be visible but isn't\nint BotItemGoalInVisButNotVisible(int viewer, vec3_t eye, vec3_t viewangles, bot_goal_t *goal);\n//search for a goal for the given classname, the index can be used\n//as a start point for the search when multiple goals are available with that same classname\nint BotGetLevelItemGoal(int index, char *classname, bot_goal_t *goal);\n//get the next camp spot in the map\nint BotGetNextCampSpotGoal(int num, bot_goal_t *goal);\n//get the map location with the given name\nint BotGetMapLocationGoal(char *name, bot_goal_t *goal);\n//returns the avoid goal time\nfloat BotAvoidGoalTime(int goalstate, int number);\n//set the avoid goal time\nvoid BotSetAvoidGoalTime(int goalstate, int number, float avoidtime);\n//initializes the items in the level\nvoid BotInitLevelItems(void);\n//regularly update dynamic entity items (dropped weapons, flags etc.)\nvoid BotUpdateEntityItems(void);\n//interbreed the goal fuzzy logic\nvoid BotInterbreedGoalFuzzyLogic(int parent1, int parent2, int child);\n//save the goal fuzzy logic to disk\nvoid BotSaveGoalFuzzyLogic(int goalstate, char *filename);\n//mutate the goal fuzzy logic\nvoid BotMutateGoalFuzzyLogic(int goalstate, float range);\n//loads item weights for the bot\nint BotLoadItemWeights(int goalstate, char *filename);\n//frees the item weights of the bot\nvoid BotFreeItemWeights(int goalstate);\n//returns the handle of a newly allocated goal state\nint BotAllocGoalState(int client);\n//free the given goal state\nvoid BotFreeGoalState(int handle);\n//setup the goal AI\nint BotSetupGoalAI(void);\n//shut down the goal AI\nvoid BotShutdownGoalAI(void);\n"
  },
  {
    "path": "plugins/quake3/botlib/be_ai_move.c",
    "content": "/*\n===========================================================================\nCopyright (C) 1999-2005 Id Software, Inc.\n\nThis file is part of Quake III Arena source code.\n\nQuake III Arena source code is free software; you can redistribute it\nand/or modify it under the terms of the GNU General Public License as\npublished by the Free Software Foundation; either version 2 of the License,\nor (at your option) any later version.\n\nQuake III Arena source code is distributed in the hope that it will be\nuseful, but WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Quake III Arena source code; if not, write to the Free Software\nFoundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n===========================================================================\n*/\n\n/*****************************************************************************\n * name:\t\tbe_ai_move.c\n *\n * desc:\t\tbot movement AI\n *\n * $Archive: /MissionPack/code/botlib/be_ai_move.c $\n *\n *****************************************************************************/\n\n#include \"q_shared.h\"\n#include \"l_memory.h\"\n#include \"l_libvar.h\"\n#include \"l_utils.h\"\n#include \"l_script.h\"\n#include \"l_precomp.h\"\n#include \"l_struct.h\"\n#include \"aasfile.h\"\n#include \"botlib.h\"\n#include \"be_aas.h\"\n#include \"be_aas_funcs.h\"\n#include \"be_interface.h\"\n\n#include \"be_ea.h\"\n#include \"be_ai_goal.h\"\n#include \"be_ai_move.h\"\n\n\n//#define DEBUG_AI_MOVE\n//#define DEBUG_ELEVATOR\n//#define DEBUG_GRAPPLE\n\n//movement state\n//NOTE: the moveflags MFL_ONGROUND, MFL_TELEPORTED, MFL_WATERJUMP and\n//\t\tMFL_GRAPPLEPULL must be set outside the movement code\ntypedef struct bot_movestate_s\n{\n\t//input vars (all set outside the movement code)\n\tvec3_t origin;\t\t\t\t\t\t\t\t//origin of the bot\n\tvec3_t velocity;\t\t\t\t\t\t\t//velocity of the bot\n\tvec3_t viewoffset;\t\t\t\t\t\t\t//view offset\n\tint entitynum;\t\t\t\t\t\t\t\t//entity number of the bot\n\tint client;\t\t\t\t\t\t\t\t\t//client number of the bot\n\tfloat thinktime;\t\t\t\t\t\t\t//time the bot thinks\n\tint presencetype;\t\t\t\t\t\t\t//presencetype of the bot\n\tvec3_t viewangles;\t\t\t\t\t\t\t//view angles of the bot\n\t//state vars\n\tint areanum;\t\t\t\t\t\t\t\t//area the bot is in\n\tint lastareanum;\t\t\t\t\t\t\t//last area the bot was in\n\tint lastgoalareanum;\t\t\t\t\t\t//last goal area number\n\tint lastreachnum;\t\t\t\t\t\t\t//last reachability number\n\tvec3_t lastorigin;\t\t\t\t\t\t\t//origin previous cycle\n\tint reachareanum;\t\t\t\t\t\t\t//area number of the reachabilty\n\tint moveflags;\t\t\t\t\t\t\t\t//movement flags\n\tint jumpreach;\t\t\t\t\t\t\t\t//set when jumped\n\tfloat grapplevisible_time;\t\t\t\t\t//last time the grapple was visible\n\tfloat lastgrappledist;\t\t\t\t\t\t//last distance to the grapple end\n\tfloat reachability_time;\t\t\t\t\t//time to use current reachability\n\tint avoidreach[MAX_AVOIDREACH];\t\t\t\t//reachabilities to avoid\n\tfloat avoidreachtimes[MAX_AVOIDREACH];\t\t//times to avoid the reachabilities\n\tint avoidreachtries[MAX_AVOIDREACH];\t\t//number of tries before avoiding\n\t//\n\tbot_avoidspot_t avoidspots[MAX_AVOIDSPOTS];\t//spots to avoid\n\tint numavoidspots;\n} bot_movestate_t;\n\n//used to avoid reachability links for some time after being used\n#define AVOIDREACH\n#define AVOIDREACH_TIME\t\t\t6\t\t//avoid links for 6 seconds after use\n#define AVOIDREACH_TRIES\t\t4\n//prediction times\n#define PREDICTIONTIME_JUMP\t3\t\t//in seconds\n#define PREDICTIONTIME_MOVE\t2\t\t//in seconds\n//weapon indexes for weapon jumping\n#define WEAPONINDEX_ROCKET_LAUNCHER\t\t5\n#define WEAPONINDEX_BFG\t\t\t\t\t9\n\n#define MODELTYPE_FUNC_PLAT\t\t1\n#define MODELTYPE_FUNC_BOB\t\t2\n#define MODELTYPE_FUNC_DOOR\t\t3\n#define MODELTYPE_FUNC_STATIC\t4\n\nlibvar_t *sv_maxstep;\nlibvar_t *sv_maxbarrier;\nlibvar_t *blsv_gravity;\nlibvar_t *weapindex_rocketlauncher;\nlibvar_t *weapindex_bfg10k;\nlibvar_t *weapindex_grapple;\nlibvar_t *entitytypemissile;\nlibvar_t *offhandgrapple;\nlibvar_t *cmd_grappleoff;\nlibvar_t *cmd_grappleon;\n//type of model, func_plat or func_bobbing\nint modeltypes[MAX_MODELS];\n\nbot_movestate_t *botmovestates[MAX_CLIENTS+1];\n\n//========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//========================================================================\nint BotAllocMoveState(void)\n{\n\tint i;\n\n\tfor (i = 1; i <= MAX_CLIENTS; i++)\n\t{\n\t\tif (!botmovestates[i])\n\t\t{\n\t\t\tbotmovestates[i] = GetClearedMemory(sizeof(bot_movestate_t));\n\t\t\treturn i;\n\t\t} //end if\n\t} //end for\n\treturn 0;\n} //end of the function BotAllocMoveState\n//========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//========================================================================\nvoid BotFreeMoveState(int handle)\n{\n\tif (handle <= 0 || handle > MAX_CLIENTS)\n\t{\n\t\tbotimport.Print(PRT_FATAL, \"move state handle %d out of range\\n\", handle);\n\t\treturn;\n\t} //end if\n\tif (!botmovestates[handle])\n\t{\n\t\tbotimport.Print(PRT_FATAL, \"invalid move state %d\\n\", handle);\n\t\treturn;\n\t} //end if\n\tFreeMemory(botmovestates[handle]);\n\tbotmovestates[handle] = NULL;\n} //end of the function BotFreeMoveState\n//========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//========================================================================\nbot_movestate_t *BotMoveStateFromHandle(int handle)\n{\n\tif (handle <= 0 || handle > MAX_CLIENTS)\n\t{\n\t\tbotimport.Print(PRT_FATAL, \"move state handle %d out of range\\n\", handle);\n\t\treturn NULL;\n\t} //end if\n\tif (!botmovestates[handle])\n\t{\n\t\tbotimport.Print(PRT_FATAL, \"invalid move state %d\\n\", handle);\n\t\treturn NULL;\n\t} //end if\n\treturn botmovestates[handle];\n} //end of the function BotMoveStateFromHandle\n//========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//========================================================================\nvoid BotInitMoveState(int handle, bot_initmove_t *initmove)\n{\n\tbot_movestate_t *ms;\n\n\tms = BotMoveStateFromHandle(handle);\n\tif (!ms) return;\n\tVectorCopy(initmove->origin, ms->origin);\n\tVectorCopy(initmove->velocity, ms->velocity);\n\tVectorCopy(initmove->viewoffset, ms->viewoffset);\n\tms->entitynum = initmove->entitynum;\n\tms->client = initmove->client;\n\tms->thinktime = initmove->thinktime;\n\tms->presencetype = initmove->presencetype;\n\tVectorCopy(initmove->viewangles, ms->viewangles);\n\t//\n\tms->moveflags &= ~MFL_ONGROUND;\n\tif (initmove->or_moveflags & MFL_ONGROUND) ms->moveflags |= MFL_ONGROUND;\n\tms->moveflags &= ~MFL_TELEPORTED;\t\n\tif (initmove->or_moveflags & MFL_TELEPORTED) ms->moveflags |= MFL_TELEPORTED;\n\tms->moveflags &= ~MFL_WATERJUMP;\n\tif (initmove->or_moveflags & MFL_WATERJUMP) ms->moveflags |= MFL_WATERJUMP;\n\tms->moveflags &= ~MFL_WALK;\n\tif (initmove->or_moveflags & MFL_WALK) ms->moveflags |= MFL_WALK;\n\tms->moveflags &= ~MFL_GRAPPLEPULL;\n\tif (initmove->or_moveflags & MFL_GRAPPLEPULL) ms->moveflags |= MFL_GRAPPLEPULL;\n} //end of the function BotInitMoveState\n//========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//========================================================================\nfloat AngleDiff(float ang1, float ang2)\n{\n\tfloat diff;\n\n\tdiff = ang1 - ang2;\n\tif (ang1 > ang2)\n\t{\n\t\tif (diff > 180.0) diff -= 360.0;\n\t} //end if\n\telse\n\t{\n\t\tif (diff < -180.0) diff += 360.0;\n\t} //end else\n\treturn diff;\n} //end of the function AngleDiff\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint BotFuzzyPointReachabilityArea(vec3_t origin)\n{\n\tint firstareanum, j, x, y, z;\n\tint areas[10], numareas, areanum, bestareanum;\n\tfloat dist, bestdist;\n\tvec3_t points[10], v, end;\n\n\tfirstareanum = 0;\n\tareanum = AAS_PointAreaNum(origin);\n\tif (areanum)\n\t{\n\t\tfirstareanum = areanum;\n\t\tif (AAS_AreaReachability(areanum)) return areanum;\n\t} //end if\n\tVectorCopy(origin, end);\n\tend[2] += 4;\n\tnumareas = AAS_TraceAreas(origin, end, areas, points, 10);\n\tfor (j = 0; j < numareas; j++)\n\t{\n\t\tif (AAS_AreaReachability(areas[j])) return areas[j];\n\t} //end for\n\tbestdist = 999999;\n\tbestareanum = 0;\n\tfor (z = 1; z >= -1; z -= 1)\n\t{\n\t\tfor (x = 1; x >= -1; x -= 1)\n\t\t{\n\t\t\tfor (y = 1; y >= -1; y -= 1)\n\t\t\t{\n\t\t\t\tVectorCopy(origin, end);\n\t\t\t\tend[0] += x * 8;\n\t\t\t\tend[1] += y * 8;\n\t\t\t\tend[2] += z * 12;\n\t\t\t\tnumareas = AAS_TraceAreas(origin, end, areas, points, 10);\n\t\t\t\tfor (j = 0; j < numareas; j++)\n\t\t\t\t{\n\t\t\t\t\tif (AAS_AreaReachability(areas[j]))\n\t\t\t\t\t{\n\t\t\t\t\t\tVectorSubtract(points[j], origin, v);\n\t\t\t\t\t\tdist = VectorLength(v);\n\t\t\t\t\t\tif (dist < bestdist)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tbestareanum = areas[j];\n\t\t\t\t\t\t\tbestdist = dist;\n\t\t\t\t\t\t} //end if\n\t\t\t\t\t} //end if\n\t\t\t\t\tif (!firstareanum) firstareanum = areas[j];\n\t\t\t\t} //end for\n\t\t\t} //end for\n\t\t} //end for\n\t\tif (bestareanum) return bestareanum;\n\t} //end for\n\treturn firstareanum;\n} //end of the function BotFuzzyPointReachabilityArea\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint BotReachabilityArea(vec3_t origin, int client)\n{\n\tint modelnum, modeltype, reachnum, areanum;\n\taas_reachability_t reach;\n\tvec3_t org, end, mins, maxs, up = {0, 0, 1};\n\tbsp_trace_t bsptrace;\n\taas_trace_t trace;\n\n\t//check if the bot is standing on something\n\tAAS_PresenceTypeBoundingBox(PRESENCE_CROUCH, mins, maxs);\n\tVectorMA(origin, -3, up, end);\n\tbsptrace = AAS_Trace(origin, mins, maxs, end, client, CONTENTS_SOLID|CONTENTS_PLAYERCLIP);\n\tif (!bsptrace.startsolid && bsptrace.fraction < 1 && bsptrace.ent != ENTITYNUM_NONE)\n\t{\n\t\t//if standing on the world the bot should be in a valid area\n\t\tif (bsptrace.ent == ENTITYNUM_WORLD)\n\t\t{\n\t\t\treturn BotFuzzyPointReachabilityArea(origin);\n\t\t} //end if\n\n\t\tmodelnum = AAS_EntityModelindex(bsptrace.ent);\n\t\tmodeltype = modeltypes[modelnum];\n\n\t\t//if standing on a func_plat or func_bobbing then the bot is assumed to be\n\t\t//in the area the reachability points to\n\t\tif (modeltype == MODELTYPE_FUNC_PLAT || modeltype == MODELTYPE_FUNC_BOB)\n\t\t{\n\t\t\treachnum = AAS_NextModelReachability(0, modelnum);\n\t\t\tif (reachnum)\n\t\t\t{\n\t\t\t\tAAS_ReachabilityFromNum(reachnum, &reach);\n\t\t\t\treturn reach.areanum;\n\t\t\t} //end if\n\t\t} //end else if\n\n\t\t//if the bot is swimming the bot should be in a valid area\n\t\tif (AAS_Swimming(origin))\n\t\t{\n\t\t\treturn BotFuzzyPointReachabilityArea(origin);\n\t\t} //end if\n\t\t//\n\t\tareanum = BotFuzzyPointReachabilityArea(origin);\n\t\t//if the bot is in an area with reachabilities\n\t\tif (areanum && AAS_AreaReachability(areanum)) return areanum;\n\t\t//trace down till the ground is hit because the bot is standing on some other entity\n\t\tVectorCopy(origin, org);\n\t\tVectorCopy(org, end);\n\t\tend[2] -= 800;\n\t\ttrace = AAS_TraceClientBBox(org, end, PRESENCE_CROUCH, -1);\n\t\tif (!trace.startsolid)\n\t\t{\n\t\t\tVectorCopy(trace.endpos, org);\n\t\t} //end if\n\t\t//\n\t\treturn BotFuzzyPointReachabilityArea(org);\n\t} //end if\n\t//\n\treturn BotFuzzyPointReachabilityArea(origin);\n} //end of the function BotReachabilityArea\n//===========================================================================\n// returns the reachability area the bot is in\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\n/*\nint BotReachabilityArea(vec3_t origin, int testground)\n{\n\tint firstareanum, i, j, x, y, z;\n\tint areas[10], numareas, areanum, bestareanum;\n\tfloat dist, bestdist;\n\tvec3_t org, end, points[10], v;\n\taas_trace_t trace;\n\n\tfirstareanum = 0;\n\tfor (i = 0; i < 2; i++)\n\t{\n\t\tVectorCopy(origin, org);\n\t\t//if test at the ground (used when bot is standing on an entity)\n\t\tif (i > 0)\n\t\t{\n\t\t\tVectorCopy(origin, end);\n\t\t\tend[2] -= 800;\n\t\t\ttrace = AAS_TraceClientBBox(origin, end, PRESENCE_CROUCH, -1);\n\t\t\tif (!trace.startsolid)\n\t\t\t{\n\t\t\t\tVectorCopy(trace.endpos, org);\n\t\t\t} //end if\n\t\t} //end if\n\n\t\tfirstareanum = 0;\n\t\tareanum = AAS_PointAreaNum(org);\n\t\tif (areanum)\n\t\t{\n\t\t\tfirstareanum = areanum;\n\t\t\tif (AAS_AreaReachability(areanum)) return areanum;\n\t\t} //end if\n\t\tbestdist = 999999;\n\t\tbestareanum = 0;\n\t\tfor (z = 1; z >= -1; z -= 1)\n\t\t{\n\t\t\tfor (x = 1; x >= -1; x -= 1)\n\t\t\t{\n\t\t\t\tfor (y = 1; y >= -1; y -= 1)\n\t\t\t\t{\n\t\t\t\t\tVectorCopy(org, end);\n\t\t\t\t\tend[0] += x * 8;\n\t\t\t\t\tend[1] += y * 8;\n\t\t\t\t\tend[2] += z * 12;\n\t\t\t\t\tnumareas = AAS_TraceAreas(org, end, areas, points, 10);\n\t\t\t\t\tfor (j = 0; j < numareas; j++)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (AAS_AreaReachability(areas[j]))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tVectorSubtract(points[j], org, v);\n\t\t\t\t\t\t\tdist = VectorLength(v);\n\t\t\t\t\t\t\tif (dist < bestdist)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tbestareanum = areas[j];\n\t\t\t\t\t\t\t\tbestdist = dist;\n\t\t\t\t\t\t\t} //end if\n\t\t\t\t\t\t} //end if\n\t\t\t\t\t} //end for\n\t\t\t\t} //end for\n\t\t\t} //end for\n\t\t\tif (bestareanum) return bestareanum;\n\t\t} //end for\n\t\tif (!testground) break;\n\t} //end for\n//#ifdef DEBUG\n\t//botimport.Print(PRT_MESSAGE, \"no reachability area\\n\");\n//#endif //DEBUG\n\treturn firstareanum;\n} //end of the function BotReachabilityArea*/\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint BotOnMover(vec3_t origin, int entnum, aas_reachability_t *reach)\n{\n\tint i, modelnum;\n\tvec3_t mins, maxs, modelorigin, org, end;\n\tvec3_t angles = {0, 0, 0};\n\tvec3_t boxmins = {-16, -16, -8}, boxmaxs = {16, 16, 8};\n\tbsp_trace_t trace;\n\n\tmodelnum = reach->facenum & 0x0000FFFF;\n\t//get some bsp model info\n\tAAS_BSPModelMinsMaxsOrigin(modelnum, angles, mins, maxs, NULL);\n\t//\n\tif (!AAS_OriginOfMoverWithModelNum(modelnum, modelorigin))\n\t{\n\t\tbotimport.Print(PRT_MESSAGE, \"no entity with model %d\\n\", modelnum);\n\t\treturn qfalse;\n\t} //end if\n\t//\n\tfor (i = 0; i < 2; i++)\n\t{\n\t\tif (origin[i] > modelorigin[i] + maxs[i] + 16) return qfalse;\n\t\tif (origin[i] < modelorigin[i] + mins[i] - 16) return qfalse;\n\t} //end for\n\t//\n\tVectorCopy(origin, org);\n\torg[2] += 24;\n\tVectorCopy(origin, end);\n\tend[2] -= 48;\n\t//\n\ttrace = AAS_Trace(org, boxmins, boxmaxs, end, entnum, CONTENTS_SOLID|CONTENTS_PLAYERCLIP);\n\tif (!trace.startsolid && !trace.allsolid)\n\t{\n\t\t//NOTE: the reachability face number is the model number of the elevator\n\t\tif (trace.ent != ENTITYNUM_NONE && AAS_EntityModelNum(trace.ent) == modelnum)\n\t\t{\n\t\t\treturn qtrue;\n\t\t} //end if\n\t} //end if\n\treturn qfalse;\n} //end of the function BotOnMover\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint MoverDown(aas_reachability_t *reach)\n{\n\tint modelnum;\n\tvec3_t mins, maxs, origin;\n\tvec3_t angles = {0, 0, 0};\n\n\tmodelnum = reach->facenum & 0x0000FFFF;\n\t//get some bsp model info\n\tAAS_BSPModelMinsMaxsOrigin(modelnum, angles, mins, maxs, origin);\n\t//\n\tif (!AAS_OriginOfMoverWithModelNum(modelnum, origin))\n\t{\n\t\tbotimport.Print(PRT_MESSAGE, \"no entity with model %d\\n\", modelnum);\n\t\treturn qfalse;\n\t} //end if\n\t//if the top of the plat is below the reachability start point\n\tif (origin[2] + maxs[2] < reach->start[2]) return qtrue;\n\treturn qfalse;\n} //end of the function MoverDown\n//========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//========================================================================\nvoid BotSetBrushModelTypes(void)\n{\n\tint ent, modelnum;\n\tchar classname[MAX_EPAIRKEY], model[MAX_EPAIRKEY];\n\n\tCom_Memset(modeltypes, 0, MAX_MODELS * sizeof(int));\n\t//\n\tfor (ent = AAS_NextBSPEntity(0); ent; ent = AAS_NextBSPEntity(ent))\n\t{\n\t\tif (!AAS_ValueForBSPEpairKey(ent, \"classname\", classname, MAX_EPAIRKEY)) continue;\n\t\tif (!AAS_ValueForBSPEpairKey(ent, \"model\", model, MAX_EPAIRKEY)) continue;\n\t\tif (model[0]) modelnum = atoi(model+1);\n\t\telse modelnum = 0;\n\n\t\tif (modelnum < 0 || modelnum > MAX_MODELS)\n\t\t{\n\t\t\tbotimport.Print(PRT_MESSAGE, \"entity %s model number out of range\\n\", classname);\n\t\t\tcontinue;\n\t\t} //end if\n\n\t\tif (!Q_stricmp(classname, \"func_bobbing\"))\n\t\t\tmodeltypes[modelnum] = MODELTYPE_FUNC_BOB;\n\t\telse if (!Q_stricmp(classname, \"func_plat\"))\n\t\t\tmodeltypes[modelnum] = MODELTYPE_FUNC_PLAT;\n\t\telse if (!Q_stricmp(classname, \"func_door\"))\n\t\t\tmodeltypes[modelnum] = MODELTYPE_FUNC_DOOR;\n\t\telse if (!Q_stricmp(classname, \"func_static\"))\n\t\t\tmodeltypes[modelnum] = MODELTYPE_FUNC_STATIC;\n\t} //end for\n} //end of the function BotSetBrushModelTypes\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint BotOnTopOfEntity(bot_movestate_t *ms)\n{\n\tvec3_t mins, maxs, end, up = {0, 0, 1};\n\tbsp_trace_t trace;\n\n\tAAS_PresenceTypeBoundingBox(ms->presencetype, mins, maxs);\n\tVectorMA(ms->origin, -3, up, end);\n\ttrace = AAS_Trace(ms->origin, mins, maxs, end, ms->entitynum, CONTENTS_SOLID|CONTENTS_PLAYERCLIP);\n\tif (!trace.startsolid && (trace.ent != ENTITYNUM_WORLD && trace.ent != ENTITYNUM_NONE) )\n\t{\n\t\treturn trace.ent;\n\t} //end if\n\treturn -1;\n} //end of the function BotOnTopOfEntity\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint BotValidTravel(vec3_t origin, aas_reachability_t *reach, int travelflags)\n{\n\t//if the reachability uses an unwanted travel type\n\tif (AAS_TravelFlagForType(reach->traveltype) & ~travelflags) return qfalse;\n\t//don't go into areas with bad travel types\n\tif (AAS_AreaContentsTravelFlags(reach->areanum) & ~travelflags) return qfalse;\n\treturn qtrue;\n} //end of the function BotValidTravel\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid BotAddToAvoidReach(bot_movestate_t *ms, int number, float avoidtime)\n{\n\tint i;\n\n\tfor (i = 0; i < MAX_AVOIDREACH; i++)\n\t{\n\t\tif (ms->avoidreach[i] == number)\n\t\t{\n\t\t\tif (ms->avoidreachtimes[i] > AAS_Time()) ms->avoidreachtries[i]++;\n\t\t\telse ms->avoidreachtries[i] = 1;\n\t\t\tms->avoidreachtimes[i] = AAS_Time() + avoidtime;\n\t\t\treturn;\n\t\t} //end if\n\t} //end for\n\t//add the reachability to the reachabilities to avoid for a while\n\tfor (i = 0; i < MAX_AVOIDREACH; i++)\n\t{\n\t\tif (ms->avoidreachtimes[i] < AAS_Time())\n\t\t{\n\t\t\tms->avoidreach[i] = number;\n\t\t\tms->avoidreachtimes[i] = AAS_Time() + avoidtime;\n\t\t\tms->avoidreachtries[i] = 1;\n\t\t\treturn;\n\t\t} //end if\n\t} //end for\n} //end of the function BotAddToAvoidReach\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nfloat DistanceFromLineSquared(vec3_t p, vec3_t lp1, vec3_t lp2)\n{\n\tvec3_t proj, dir;\n\tint j;\n\n\tAAS_ProjectPointOntoVector(p, lp1, lp2, proj);\n\tfor (j = 0; j < 3; j++)\n\t\tif ((proj[j] > lp1[j] && proj[j] > lp2[j]) ||\n\t\t\t(proj[j] < lp1[j] && proj[j] < lp2[j]))\n\t\t\tbreak;\n\tif (j < 3) {\n\t\tif (fabs(proj[j] - lp1[j]) < fabs(proj[j] - lp2[j]))\n\t\t\tVectorSubtract(p, lp1, dir);\n\t\telse\n\t\t\tVectorSubtract(p, lp2, dir);\n\t\treturn VectorLengthSquared(dir);\n\t}\n\tVectorSubtract(p, proj, dir);\n\treturn VectorLengthSquared(dir);\n} //end of the function DistanceFromLineSquared\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nfloat VectorDistanceSquared(vec3_t p1, vec3_t p2)\n{\n\tvec3_t dir;\n\tVectorSubtract(p2, p1, dir);\n\treturn VectorLengthSquared(dir);\n} //end of the function VectorDistanceSquared\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint BotAvoidSpots(vec3_t origin, aas_reachability_t *reach, bot_avoidspot_t *avoidspots, int numavoidspots)\n{\n\tint checkbetween, i, type;\n\tfloat squareddist, squaredradius;\n\n\tswitch(reach->traveltype & TRAVELTYPE_MASK)\n\t{\n\t\tcase TRAVEL_WALK: checkbetween = qtrue; break;\n\t\tcase TRAVEL_CROUCH: checkbetween = qtrue; break;\n\t\tcase TRAVEL_BARRIERJUMP: checkbetween = qtrue; break;\n\t\tcase TRAVEL_LADDER: checkbetween = qtrue; break;\n\t\tcase TRAVEL_WALKOFFLEDGE: checkbetween = qfalse; break;\n\t\tcase TRAVEL_JUMP: checkbetween = qfalse; break;\n\t\tcase TRAVEL_SWIM: checkbetween = qtrue; break;\n\t\tcase TRAVEL_WATERJUMP: checkbetween = qtrue; break;\n\t\tcase TRAVEL_TELEPORT: checkbetween = qfalse; break;\n\t\tcase TRAVEL_ELEVATOR: checkbetween = qfalse; break;\n\t\tcase TRAVEL_GRAPPLEHOOK: checkbetween = qfalse; break;\n\t\tcase TRAVEL_ROCKETJUMP: checkbetween = qfalse; break;\n\t\tcase TRAVEL_BFGJUMP: checkbetween = qfalse; break;\n\t\tcase TRAVEL_JUMPPAD: checkbetween = qfalse; break;\n\t\tcase TRAVEL_FUNCBOB: checkbetween = qfalse; break;\n\t\tdefault: checkbetween = qtrue; break;\n\t} //end switch\n\n\ttype = AVOID_CLEAR;\n\tfor (i = 0; i < numavoidspots; i++)\n\t{\n\t\tsquaredradius = Square(avoidspots[i].radius);\n\t\tsquareddist = DistanceFromLineSquared(avoidspots[i].origin, origin, reach->start);\n\t\t// if moving towards the avoid spot\n\t\tif (squareddist < squaredradius &&\n\t\t\tVectorDistanceSquared(avoidspots[i].origin, origin) > squareddist)\n\t\t{\n\t\t\ttype = avoidspots[i].type;\n\t\t} //end if\n\t\telse if (checkbetween) {\n\t\t\tsquareddist = DistanceFromLineSquared(avoidspots[i].origin, reach->start, reach->end);\n\t\t\t// if moving towards the avoid spot\n\t\t\tif (squareddist < squaredradius &&\n\t\t\t\tVectorDistanceSquared(avoidspots[i].origin, reach->start) > squareddist)\n\t\t\t{\n\t\t\t\ttype = avoidspots[i].type;\n\t\t\t} //end if\n\t\t} //end if\n\t\telse\n\t\t{\n\t\t\tVectorDistanceSquared(avoidspots[i].origin, reach->end);\n\t\t\t// if the reachability leads closer to the avoid spot\n\t\t\tif (squareddist < squaredradius && \n\t\t\t\tVectorDistanceSquared(avoidspots[i].origin, reach->start) > squareddist)\n\t\t\t{\n\t\t\t\ttype = avoidspots[i].type;\n\t\t\t} //end if\n\t\t} //end else\n\t\tif (type == AVOID_ALWAYS)\n\t\t\treturn type;\n\t} //end for\n\treturn type;\n} //end of the function BotAvoidSpots\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid BotAddAvoidSpot(int movestate, vec3_t origin, float radius, int type)\n{\n\tbot_movestate_t *ms;\n\n\tms = BotMoveStateFromHandle(movestate);\n\tif (!ms) return;\n\tif (type == AVOID_CLEAR)\n\t{\n\t\tms->numavoidspots = 0;\n\t\treturn;\n\t} //end if\n\n\tif (ms->numavoidspots >= MAX_AVOIDSPOTS)\n\t\treturn;\n\tVectorCopy(origin, ms->avoidspots[ms->numavoidspots].origin);\n\tms->avoidspots[ms->numavoidspots].radius = radius;\n\tms->avoidspots[ms->numavoidspots].type = type;\n\tms->numavoidspots++;\n} //end of the function BotAddAvoidSpot\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint BotGetReachabilityToGoal(vec3_t origin, int areanum,\n\t\t\t\t\t\t\t\t\t  int lastgoalareanum, int lastareanum,\n\t\t\t\t\t\t\t\t\t  int *avoidreach, float *avoidreachtimes, int *avoidreachtries,\n\t\t\t\t\t\t\t\t\t  bot_goal_t *goal, int travelflags, int movetravelflags,\n\t\t\t\t\t\t\t\t\t  struct bot_avoidspot_s *avoidspots, int numavoidspots, int *flags)\n{\n\tint i, t, besttime, bestreachnum, reachnum;\n\taas_reachability_t reach;\n\n\t//if not in a valid area\n\tif (!areanum) return 0;\n\t//\n\tif (AAS_AreaDoNotEnter(areanum) || AAS_AreaDoNotEnter(goal->areanum))\n\t{\n\t\ttravelflags |= TFL_DONOTENTER;\n\t\tmovetravelflags |= TFL_DONOTENTER;\n\t} //end if\n\t//use the routing to find the next area to go to\n\tbesttime = 0;\n\tbestreachnum = 0;\n\t//\n\tfor (reachnum = AAS_NextAreaReachability(areanum, 0); reachnum;\n\t\treachnum = AAS_NextAreaReachability(areanum, reachnum))\n\t{\n#ifdef AVOIDREACH\n\t\t//check if it isn't an reachability to avoid\n\t\tfor (i = 0; i < MAX_AVOIDREACH; i++)\n\t\t{\n\t\t\tif (avoidreach[i] == reachnum && avoidreachtimes[i] >= AAS_Time()) break;\n\t\t} //end for\n\t\tif (i != MAX_AVOIDREACH && avoidreachtries[i] > AVOIDREACH_TRIES)\n\t\t{\n#ifdef DEBUG\n\t\t\tif (botDeveloper)\n\t\t\t{\n\t\t\t\tbotimport.Print(PRT_MESSAGE, \"avoiding reachability %d\\n\", avoidreach[i]);\n\t\t\t} //end if\n#endif //DEBUG\n\t\t\tcontinue;\n\t\t} //end if\n#endif //AVOIDREACH\n\t\t//get the reachability from the number\n\t\tAAS_ReachabilityFromNum(reachnum, &reach);\n\t\t//NOTE: do not go back to the previous area if the goal didn't change\n\t\t//NOTE: is this actually avoidance of local routing minima between two areas???\n\t\tif (lastgoalareanum == goal->areanum && reach.areanum == lastareanum) continue;\n\t\t//if (AAS_AreaContentsTravelFlags(reach.areanum) & ~travelflags) continue;\n\t\t//if the travel isn't valid\n\t\tif (!BotValidTravel(origin, &reach, movetravelflags)) continue;\n\t\t//get the travel time\n\t\tt = AAS_AreaTravelTimeToGoalArea(reach.areanum, reach.end, goal->areanum, travelflags);\n\t\t//if the goal area isn't reachable from the reachable area\n\t\tif (!t) continue;\n\t\t//if the bot should not use this reachability to avoid bad spots\n\t\tif (BotAvoidSpots(origin, &reach, avoidspots, numavoidspots)) {\n\t\t\tif (flags) {\n\t\t\t\t*flags |= MOVERESULT_BLOCKEDBYAVOIDSPOT;\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\t//add the travel time towards the area\n\t\tt += reach.traveltime;// + AAS_AreaTravelTime(areanum, origin, reach.start);\n\t\t//if the travel time is better than the ones already found\n\t\tif (!besttime || t < besttime)\n\t\t{\n\t\t\tbesttime = t;\n\t\t\tbestreachnum = reachnum;\n\t\t} //end if\n\t} //end for\n\t//\n\treturn bestreachnum;\n} //end of the function BotGetReachabilityToGoal\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint BotAddToTarget(vec3_t start, vec3_t end, float maxdist, float *dist, vec3_t target)\n{\n\tvec3_t dir;\n\tfloat curdist;\n\n\tVectorSubtract(end, start, dir);\n\tcurdist = VectorNormalize(dir);\n\tif (*dist + curdist < maxdist)\n\t{\n\t\tVectorCopy(end, target);\n\t\t*dist += curdist;\n\t\treturn qfalse;\n\t} //end if\n\telse\n\t{\n\t\tVectorMA(start, maxdist - *dist, dir, target);\n\t\t*dist = maxdist;\n\t\treturn qtrue;\n\t} //end else\n} //end of the function BotAddToTarget\n\nint BotMovementViewTarget(int movestate, bot_goal_t *goal, int travelflags, float lookahead, vec3_t target)\n{\n\taas_reachability_t reach;\n\tint reachnum, lastareanum;\n\tbot_movestate_t *ms;\n\tvec3_t end;\n\tfloat dist;\n\n\tms = BotMoveStateFromHandle(movestate);\n\tif (!ms) return qfalse;\n\treachnum = 0;\n\t//if the bot has no goal or no last reachability\n\tif (!ms->lastreachnum || !goal) return qfalse;\n\n\treachnum = ms->lastreachnum;\n\tVectorCopy(ms->origin, end);\n\tlastareanum = ms->lastareanum;\n\tdist = 0;\n\twhile(reachnum && dist < lookahead)\n\t{\n\t\tAAS_ReachabilityFromNum(reachnum, &reach);\n\t\tif (BotAddToTarget(end, reach.start, lookahead, &dist, target)) return qtrue;\n\t\t//never look beyond teleporters\n\t\tif ((reach.traveltype & TRAVELTYPE_MASK) == TRAVEL_TELEPORT) return qtrue;\n\t\t//never look beyond the weapon jump point\n\t\tif ((reach.traveltype & TRAVELTYPE_MASK) == TRAVEL_ROCKETJUMP) return qtrue;\n\t\tif ((reach.traveltype & TRAVELTYPE_MASK) == TRAVEL_BFGJUMP) return qtrue;\n\t\t//don't add jump pad distances\n\t\tif ((reach.traveltype & TRAVELTYPE_MASK) != TRAVEL_JUMPPAD &&\n\t\t\t(reach.traveltype & TRAVELTYPE_MASK) != TRAVEL_ELEVATOR &&\n\t\t\t(reach.traveltype & TRAVELTYPE_MASK) != TRAVEL_FUNCBOB)\n\t\t{\n\t\t\tif (BotAddToTarget(reach.start, reach.end, lookahead, &dist, target)) return qtrue;\n\t\t} //end if\n\t\treachnum = BotGetReachabilityToGoal(reach.end, reach.areanum,\n\t\t\t\t\t\tms->lastgoalareanum, lastareanum,\n\t\t\t\t\t\t\tms->avoidreach, ms->avoidreachtimes, ms->avoidreachtries,\n\t\t\t\t\t\t\t\t\tgoal, travelflags, travelflags, NULL, 0, NULL);\n\t\tVectorCopy(reach.end, end);\n\t\tlastareanum = reach.areanum;\n\t\tif (lastareanum == goal->areanum)\n\t\t{\n\t\t\tBotAddToTarget(reach.end, goal->origin, lookahead, &dist, target);\n\t\t\treturn qtrue;\n\t\t} //end if\n\t} //end while\n\t//\n\treturn qfalse;\n} //end of the function BotMovementViewTarget\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint BotVisible(int ent, vec3_t eye, vec3_t target)\n{\n\tbsp_trace_t trace;\n\n\ttrace = AAS_Trace(eye, NULL, NULL, target, ent, CONTENTS_SOLID|CONTENTS_PLAYERCLIP);\n\tif (trace.fraction >= 1) return qtrue;\n\treturn qfalse;\n} //end of the function BotVisible\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint BotPredictVisiblePosition(vec3_t origin, int areanum, bot_goal_t *goal, int travelflags, vec3_t target)\n{\n\taas_reachability_t reach;\n\tint reachnum, lastgoalareanum, lastareanum, i;\n\tint avoidreach[MAX_AVOIDREACH];\n\tfloat avoidreachtimes[MAX_AVOIDREACH];\n\tint avoidreachtries[MAX_AVOIDREACH];\n\tvec3_t end;\n\n\t//if the bot has no goal or no last reachability\n\tif (!goal) return qfalse;\n\t//if the areanum is not valid\n\tif (!areanum) return qfalse;\n\t//if the goal areanum is not valid\n\tif (!goal->areanum) return qfalse;\n\n\tCom_Memset(avoidreach, 0, MAX_AVOIDREACH * sizeof(int));\n\tlastgoalareanum = goal->areanum;\n\tlastareanum = areanum;\n\tVectorCopy(origin, end);\n\t//only do 20 hops\n\tfor (i = 0; i < 20 && (areanum != goal->areanum); i++)\n\t{\n\t\t//\n\t\treachnum = BotGetReachabilityToGoal(end, areanum,\n\t\t\t\t\t\tlastgoalareanum, lastareanum,\n\t\t\t\t\t\t\tavoidreach, avoidreachtimes, avoidreachtries,\n\t\t\t\t\t\t\t\t\tgoal, travelflags, travelflags, NULL, 0, NULL);\n\t\tif (!reachnum) return qfalse;\n\t\tAAS_ReachabilityFromNum(reachnum, &reach);\n\t\t//\n\t\tif (BotVisible(goal->entitynum, goal->origin, reach.start))\n\t\t{\n\t\t\tVectorCopy(reach.start, target);\n\t\t\treturn qtrue;\n\t\t} //end if\n\t\t//\n\t\tif (BotVisible(goal->entitynum, goal->origin, reach.end))\n\t\t{\n\t\t\tVectorCopy(reach.end, target);\n\t\t\treturn qtrue;\n\t\t} //end if\n\t\t//\n\t\tif (reach.areanum == goal->areanum)\n\t\t{\n\t\t\tVectorCopy(reach.end, target);\n\t\t\treturn qtrue;\n\t\t} //end if\n\t\t//\n\t\tlastareanum = areanum;\n\t\tareanum = reach.areanum;\n\t\tVectorCopy(reach.end, end);\n\t\t//\n\t} //end while\n\t//\n\treturn qfalse;\n} //end of the function BotPredictVisiblePosition\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid MoverBottomCenter(aas_reachability_t *reach, vec3_t bottomcenter)\n{\n\tint modelnum;\n\tvec3_t mins, maxs, origin, mids;\n\tvec3_t angles = {0, 0, 0};\n\n\tmodelnum = reach->facenum & 0x0000FFFF;\n\t//get some bsp model info\n\tAAS_BSPModelMinsMaxsOrigin(modelnum, angles, mins, maxs, origin);\n\t//\n\tif (!AAS_OriginOfMoverWithModelNum(modelnum, origin))\n\t{\n\t\tbotimport.Print(PRT_MESSAGE, \"no entity with model %d\\n\", modelnum);\n\t} //end if\n\t//get a point just above the plat in the bottom position\n\tVectorAdd(mins, maxs, mids);\n\tVectorMA(origin, 0.5, mids, bottomcenter);\n\tbottomcenter[2] = reach->start[2];\n} //end of the function MoverBottomCenter\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nfloat BotGapDistance(vec3_t origin, vec3_t hordir, int entnum)\n{\n\tfloat dist, startz;\n\tvec3_t start, end;\n\taas_trace_t trace;\n\n\t//do gap checking\n\tstartz = origin[2];\n\t//this enables walking down stairs more fluidly\n\t{\n\t\tVectorCopy(origin, start);\n\t\tVectorCopy(origin, end);\n\t\tend[2] -= 60;\n\t\ttrace = AAS_TraceClientBBox(start, end, PRESENCE_CROUCH, entnum);\n\t\tif (trace.fraction >= 1) return 1;\n\t\tstartz = trace.endpos[2] + 1;\n\t}\n\t//\n\tfor (dist = 8; dist <= 100; dist += 8)\n\t{\n\t\tVectorMA(origin, dist, hordir, start);\n\t\tstart[2] = startz + 24;\n\t\tVectorCopy(start, end);\n\t\tend[2] -= 48 + sv_maxbarrier->value;\n\t\ttrace = AAS_TraceClientBBox(start, end, PRESENCE_CROUCH, entnum);\n\t\t//if solid is found the bot can't walk any further and fall into a gap\n\t\tif (!trace.startsolid)\n\t\t{\n\t\t\t//if it is a gap\n\t\t\tif (trace.endpos[2] < startz - sv_maxstep->value - 8)\n\t\t\t{\n\t\t\t\tVectorCopy(trace.endpos, end);\n\t\t\t\tend[2] -= 20;\n\t\t\t\tif (AAS_PointContents(end) & CONTENTS_WATER) break;\n\t\t\t\t//if a gap is found slow down\n\t\t\t\t//botimport.Print(PRT_MESSAGE, \"gap at %f\\n\", dist);\n\t\t\t\treturn dist;\n\t\t\t} //end if\n\t\t\tstartz = trace.endpos[2];\n\t\t} //end if\n\t} //end for\n\treturn 0;\n} //end of the function BotGapDistance\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint BotCheckBarrierJump(bot_movestate_t *ms, vec3_t dir, float speed)\n{\n\tvec3_t start, hordir, end;\n\taas_trace_t trace;\n\n\tVectorCopy(ms->origin, end);\n\tend[2] += sv_maxbarrier->value;\n\t//trace right up\n\ttrace = AAS_TraceClientBBox(ms->origin, end, PRESENCE_NORMAL, ms->entitynum);\n\t//this shouldn't happen... but we check anyway\n\tif (trace.startsolid) return qfalse;\n\t//if very low ceiling it isn't possible to jump up to a barrier\n\tif (trace.endpos[2] - ms->origin[2] < sv_maxstep->value) return qfalse;\n\t//\n\thordir[0] = dir[0];\n\thordir[1] = dir[1];\n\thordir[2] = 0;\n\tVectorNormalize(hordir);\n\tVectorMA(ms->origin, ms->thinktime * speed * 0.5, hordir, end);\n\tVectorCopy(trace.endpos, start);\n\tend[2] = trace.endpos[2];\n\t//trace from previous trace end pos horizontally in the move direction\n\ttrace = AAS_TraceClientBBox(start, end, PRESENCE_NORMAL, ms->entitynum);\n\t//again this shouldn't happen\n\tif (trace.startsolid) return qfalse;\n\t//\n\tVectorCopy(trace.endpos, start);\n\tVectorCopy(trace.endpos, end);\n\tend[2] = ms->origin[2];\n\t//trace down from the previous trace end pos\n\ttrace = AAS_TraceClientBBox(start, end, PRESENCE_NORMAL, ms->entitynum);\n\t//if solid\n\tif (trace.startsolid) return qfalse;\n\t//if no obstacle at all\n\tif (trace.fraction >= 1.0) return qfalse;\n\t//if less than the maximum step height\n\tif (trace.endpos[2] - ms->origin[2] < sv_maxstep->value) return qfalse;\n\t//\n\tEA_Jump(ms->client);\n\tEA_Move(ms->client, hordir, speed);\n\tms->moveflags |= MFL_BARRIERJUMP;\n\t//there is a barrier\n\treturn qtrue;\n} //end of the function BotCheckBarrierJump\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint BotSwimInDirection(bot_movestate_t *ms, vec3_t dir, float speed, int type)\n{\n\tvec3_t normdir;\n\n\tVectorCopy(dir, normdir);\n\tVectorNormalize(normdir);\n\tEA_Move(ms->client, normdir, speed);\n\treturn qtrue;\n} //end of the function BotSwimInDirection\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint BotWalkInDirection(bot_movestate_t *ms, vec3_t dir, float speed, int type)\n{\n\tvec3_t hordir, cmdmove, velocity, tmpdir, origin;\n\tint presencetype, maxframes, cmdframes, stopevent;\n\taas_clientmove_t move;\n\tfloat dist;\n\n\tif (AAS_OnGround(ms->origin, ms->presencetype, ms->entitynum)) ms->moveflags |= MFL_ONGROUND;\n\t//if the bot is on the ground\n\tif (ms->moveflags & MFL_ONGROUND)\n\t{\n\t\t//if there is a barrier the bot can jump on\n\t\tif (BotCheckBarrierJump(ms, dir, speed)) return qtrue;\n\t\t//remove barrier jump flag\n\t\tms->moveflags &= ~MFL_BARRIERJUMP;\n\t\t//get the presence type for the movement\n\t\tif ((type & MOVE_CROUCH) && !(type & MOVE_JUMP)) presencetype = PRESENCE_CROUCH;\n\t\telse presencetype = PRESENCE_NORMAL;\n\t\t//horizontal direction\n\t\thordir[0] = dir[0];\n\t\thordir[1] = dir[1];\n\t\thordir[2] = 0;\n\t\tVectorNormalize(hordir);\n\t\t//if the bot is not supposed to jump\n\t\tif (!(type & MOVE_JUMP))\n\t\t{\n\t\t\t//if there is a gap, try to jump over it\n\t\t\tif (BotGapDistance(ms->origin, hordir, ms->entitynum) > 0) type |= MOVE_JUMP;\n\t\t} //end if\n\t\t//get command movement\n\t\tVectorScale(hordir, speed, cmdmove);\n\t\tVectorCopy(ms->velocity, velocity);\n\t\t//\n\t\tif (type & MOVE_JUMP)\n\t\t{\n\t\t\t//botimport.Print(PRT_MESSAGE, \"trying jump\\n\");\n\t\t\tcmdmove[2] = 400;\n\t\t\tmaxframes = PREDICTIONTIME_JUMP / 0.1;\n\t\t\tcmdframes = 1;\n\t\t\tstopevent = SE_HITGROUND|SE_HITGROUNDDAMAGE|\n\t\t\t\t\t\tSE_ENTERWATER|SE_ENTERSLIME|SE_ENTERLAVA;\n\t\t} //end if\n\t\telse\n\t\t{\n\t\t\tmaxframes = 2;\n\t\t\tcmdframes = 2;\n\t\t\tstopevent = SE_HITGROUNDDAMAGE|\n\t\t\t\t\t\tSE_ENTERWATER|SE_ENTERSLIME|SE_ENTERLAVA;\n\t\t} //end else\n\t\t//AAS_ClearShownDebugLines();\n\t\t//\n\t\tVectorCopy(ms->origin, origin);\n\t\torigin[2] += 0.5;\n\t\tAAS_PredictClientMovement(&move, ms->entitynum, origin, presencetype, qtrue,\n\t\t\t\t\t\t\t\t\tvelocity, cmdmove, cmdframes, maxframes, 0.1f,\n\t\t\t\t\t\t\t\t\tstopevent, 0, qfalse);//qtrue);\n\t\t//if prediction time wasn't enough to fully predict the movement\n\t\tif (move.frames >= maxframes && (type & MOVE_JUMP))\n\t\t{\n\t\t\t//botimport.Print(PRT_MESSAGE, \"client %d: max prediction frames\\n\", ms->client);\n\t\t\treturn qfalse;\n\t\t} //end if\n\t\t//don't enter slime or lava and don't fall from too high\n\t\tif (move.stopevent & (SE_ENTERSLIME|SE_ENTERLAVA|SE_HITGROUNDDAMAGE))\n\t\t{\n\t\t\t//botimport.Print(PRT_MESSAGE, \"client %d: would be hurt \", ms->client);\n\t\t\t//if (move.stopevent & SE_ENTERSLIME) botimport.Print(PRT_MESSAGE, \"slime\\n\");\n\t\t\t//if (move.stopevent & SE_ENTERLAVA) botimport.Print(PRT_MESSAGE, \"lava\\n\");\n\t\t\t//if (move.stopevent & SE_HITGROUNDDAMAGE) botimport.Print(PRT_MESSAGE, \"hitground\\n\");\n\t\t\treturn qfalse;\n\t\t} //end if\n\t\t//if ground was hit\n\t\tif (move.stopevent & SE_HITGROUND)\n\t\t{\n\t\t\t//check for nearby gap\n\t\t\tVectorNormalize2(move.velocity, tmpdir);\n\t\t\tdist = BotGapDistance(move.endpos, tmpdir, ms->entitynum);\n\t\t\tif (dist > 0) return qfalse;\n\t\t\t//\n\t\t\tdist = BotGapDistance(move.endpos, hordir, ms->entitynum);\n\t\t\tif (dist > 0) return qfalse;\n\t\t} //end if\n\t\t//get horizontal movement\n\t\ttmpdir[0] = move.endpos[0] - ms->origin[0];\n\t\ttmpdir[1] = move.endpos[1] - ms->origin[1];\n\t\ttmpdir[2] = 0;\n\t\t//\n\t\t//AAS_DrawCross(move.endpos, 4, LINECOLOR_BLUE);\n\t\t//the bot is blocked by something\n\t\tif (VectorLength(tmpdir) < speed * ms->thinktime * 0.5) return qfalse;\n\t\t//perform the movement\n\t\tif (type & MOVE_JUMP) EA_Jump(ms->client);\n\t\tif (type & MOVE_CROUCH) EA_Crouch(ms->client);\n\t\tEA_Move(ms->client, hordir, speed);\n\t\t//movement was succesfull\n\t\treturn qtrue;\n\t} //end if\n\telse\n\t{\n\t\tif (ms->moveflags & MFL_BARRIERJUMP)\n\t\t{\n\t\t\t//if near the top or going down\n\t\t\tif (ms->velocity[2] < 50)\n\t\t\t{\n\t\t\t\tEA_Move(ms->client, dir, speed);\n\t\t\t} //end if\n\t\t} //end if\n\t\t//FIXME: do air control to avoid hazards\n\t\treturn qtrue;\n\t} //end else\n} //end of the function BotWalkInDirection\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint BotMoveInDirection(int movestate, vec3_t dir, float speed, int type)\n{\n\tbot_movestate_t *ms;\n\n\tms = BotMoveStateFromHandle(movestate);\n\tif (!ms) return qfalse;\n\t//if swimming\n\tif (AAS_Swimming(ms->origin))\n\t{\n\t\treturn BotSwimInDirection(ms, dir, speed, type);\n\t} //end if\n\telse\n\t{\n\t\treturn BotWalkInDirection(ms, dir, speed, type);\n\t} //end else\n} //end of the function BotMoveInDirection\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint Intersection(vec2_t p1, vec2_t p2, vec2_t p3, vec2_t p4, vec2_t out)\n{\n   float x1, dx1, dy1, x2, dx2, dy2, d;\n\n   dx1 = p2[0] - p1[0];\n   dy1 = p2[1] - p1[1];\n   dx2 = p4[0] - p3[0];\n   dy2 = p4[1] - p3[1];\n\n   d = dy1 * dx2 - dx1 * dy2;\n   if (d != 0)\n   {\n      x1 = p1[1] * dx1 - p1[0] * dy1;\n      x2 = p3[1] * dx2 - p3[0] * dy2;\n      out[0] = (int) ((dx1 * x2 - dx2 * x1) / d);\n      out[1] = (int) ((dy1 * x2 - dy2 * x1) / d);\n\t\treturn qtrue;\n   } //end if\n   else\n   {\n      return qfalse;\n   } //end else\n} //end of the function Intersection\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid BotCheckBlocked(bot_movestate_t *ms, vec3_t dir, int checkbottom, bot_moveresult_t *result)\n{\n\tvec3_t mins, maxs, end, up = {0, 0, 1};\n\tbsp_trace_t trace;\n\n\t//test for entities obstructing the bot's path\n\tAAS_PresenceTypeBoundingBox(ms->presencetype, mins, maxs);\n\t//\n\tif (fabs(DotProduct(dir, up)) < 0.7)\n\t{\n\t\tmins[2] += sv_maxstep->value; //if the bot can step on\n\t\tmaxs[2] -= 10; //a little lower to avoid low ceiling\n\t} //end if\n\tVectorMA(ms->origin, 3, dir, end);\n\ttrace = AAS_Trace(ms->origin, mins, maxs, end, ms->entitynum, CONTENTS_SOLID|CONTENTS_PLAYERCLIP|CONTENTS_BODY);\n\t//if not started in solid and not hitting the world entity\n\tif (!trace.startsolid && (trace.ent != ENTITYNUM_WORLD && trace.ent != ENTITYNUM_NONE) )\n\t{\n\t\tresult->blocked = qtrue;\n\t\tresult->blockentity = trace.ent;\n#ifdef DEBUG\n\t\t//botimport.Print(PRT_MESSAGE, \"%d: BotCheckBlocked: I'm blocked\\n\", ms->client);\n#endif //DEBUG\n\t} //end if\n\t//if not in an area with reachability\n\telse if (checkbottom && !AAS_AreaReachability(ms->areanum))\n\t{\n\t\t//check if the bot is standing on something\n\t\tAAS_PresenceTypeBoundingBox(ms->presencetype, mins, maxs);\n\t\tVectorMA(ms->origin, -3, up, end);\n\t\ttrace = AAS_Trace(ms->origin, mins, maxs, end, ms->entitynum, CONTENTS_SOLID|CONTENTS_PLAYERCLIP);\n\t\tif (!trace.startsolid && (trace.ent != ENTITYNUM_WORLD && trace.ent != ENTITYNUM_NONE) )\n\t\t{\n\t\t\tresult->blocked = qtrue;\n\t\t\tresult->blockentity = trace.ent;\n\t\t\tresult->flags |= MOVERESULT_ONTOPOFOBSTACLE;\n#ifdef DEBUG\n\t\t\t//botimport.Print(PRT_MESSAGE, \"%d: BotCheckBlocked: I'm blocked\\n\", ms->client);\n#endif //DEBUG\n\t\t} //end if\n\t} //end else\n} //end of the function BotCheckBlocked\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nbot_moveresult_t BotTravel_Walk(bot_movestate_t *ms, aas_reachability_t *reach)\n{\n\tfloat dist, speed;\n\tvec3_t hordir;\n\tbot_moveresult_t_cleared( result );\n\n\t//first walk straight to the reachability start\n\thordir[0] = reach->start[0] - ms->origin[0];\n\thordir[1] = reach->start[1] - ms->origin[1];\n\thordir[2] = 0;\n\tdist = VectorNormalize(hordir);\n\t//\n\tBotCheckBlocked(ms, hordir, qtrue, &result);\n\t//\n\tif (dist < 10)\n\t{\n\t\t//walk straight to the reachability end\n\t\thordir[0] = reach->end[0] - ms->origin[0];\n\t\thordir[1] = reach->end[1] - ms->origin[1];\n\t\thordir[2] = 0;\n\t\tdist = VectorNormalize(hordir);\n\t} //end if\n\t//if going towards a crouch area\n\tif (!(AAS_AreaPresenceType(reach->areanum) & PRESENCE_NORMAL))\n\t{\n\t\t//if pretty close to the reachable area\n\t\tif (dist < 20) EA_Crouch(ms->client);\n\t} //end if\n\t//\n\tdist = BotGapDistance(ms->origin, hordir, ms->entitynum);\n\t//\n\tif (ms->moveflags & MFL_WALK)\n\t{\n\t\tif (dist > 0) speed = 200 - (180 - 1 * dist);\n\t\telse speed = 200;\n\t\tEA_Walk(ms->client);\n\t} //end if\n\telse\n\t{\n\t\tif (dist > 0) speed = 400 - (360 - 2 * dist);\n\t\telse speed = 400;\n\t} //end else\n\t//elemantary action move in direction\n\tEA_Move(ms->client, hordir, speed);\n\tVectorCopy(hordir, result.movedir);\n\t//\n\treturn result;\n} //end of the function BotTravel_Walk\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nbot_moveresult_t BotFinishTravel_Walk(bot_movestate_t *ms, aas_reachability_t *reach)\n{\n\tvec3_t hordir;\n\tfloat dist, speed;\n\tbot_moveresult_t_cleared( result );\n\t//if not on the ground and changed areas... don't walk back!!\n\t//(doesn't seem to help)\n\t/*\n\tms->areanum = BotFuzzyPointReachabilityArea(ms->origin);\n\tif (ms->areanum == reach->areanum)\n\t{\n#ifdef DEBUG\n\t\tbotimport.Print(PRT_MESSAGE, \"BotFinishTravel_Walk: already in reach area\\n\");\n#endif //DEBUG\n\t\treturn result;\n\t} //end if*/\n\t//go straight to the reachability end\n\thordir[0] = reach->end[0] - ms->origin[0];\n\thordir[1] = reach->end[1] - ms->origin[1];\n\thordir[2] = 0;\n\tdist = VectorNormalize(hordir);\n\t//\n\tif (dist > 100) dist = 100;\n\tspeed = 400 - (400 - 3 * dist);\n\t//\n\tEA_Move(ms->client, hordir, speed);\n\tVectorCopy(hordir, result.movedir);\n\t//\n\treturn result;\n} //end of the function BotFinishTravel_Walk\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nbot_moveresult_t BotTravel_Crouch(bot_movestate_t *ms, aas_reachability_t *reach)\n{\n\tfloat speed;\n\tvec3_t hordir;\n\tbot_moveresult_t_cleared( result );\n\n\t//\n\tspeed = 400;\n\t//walk straight to reachability end\n\thordir[0] = reach->end[0] - ms->origin[0];\n\thordir[1] = reach->end[1] - ms->origin[1];\n\thordir[2] = 0;\n\tVectorNormalize(hordir);\n\t//\n\tBotCheckBlocked(ms, hordir, qtrue, &result);\n\t//elemantary actions\n\tEA_Crouch(ms->client);\n\tEA_Move(ms->client, hordir, speed);\n\t//\n\tVectorCopy(hordir, result.movedir);\n\t//\n\treturn result;\n} //end of the function BotTravel_Crouch\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nbot_moveresult_t BotTravel_BarrierJump(bot_movestate_t *ms, aas_reachability_t *reach)\n{\n\tfloat dist, speed;\n\tvec3_t hordir;\n\tbot_moveresult_t_cleared( result );\n\n\t//walk straight to reachability start\n\thordir[0] = reach->start[0] - ms->origin[0];\n\thordir[1] = reach->start[1] - ms->origin[1];\n\thordir[2] = 0;\n\tdist = VectorNormalize(hordir);\n\t//\n\tBotCheckBlocked(ms, hordir, qtrue, &result);\n\t//if pretty close to the barrier\n\tif (dist < 9)\n\t{\n\t\tEA_Jump(ms->client);\n\t} //end if\n\telse\n\t{\n\t\tif (dist > 60) dist = 60;\n\t\tspeed = 360 - (360 - 6 * dist);\n\t\tEA_Move(ms->client, hordir, speed);\n\t} //end else\n\tVectorCopy(hordir, result.movedir);\n\t//\n\treturn result;\n} //end of the function BotTravel_BarrierJump\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nbot_moveresult_t BotFinishTravel_BarrierJump(bot_movestate_t *ms, aas_reachability_t *reach)\n{\n\tvec3_t hordir;\n\tbot_moveresult_t_cleared( result );\n\n\t//if near the top or going down\n\tif (ms->velocity[2] < 250)\n\t{\n\t\thordir[0] = reach->end[0] - ms->origin[0];\n\t\thordir[1] = reach->end[1] - ms->origin[1];\n\t\thordir[2] = 0;\n\t\tVectorNormalize(hordir);\n\t\t//\n\t\tBotCheckBlocked(ms, hordir, qtrue, &result);\n\t\t//\n\t\tEA_Move(ms->client, hordir, 400);\n\t\tVectorCopy(hordir, result.movedir);\n\t} //end if\n\t//\n\treturn result;\n} //end of the function BotFinishTravel_BarrierJump\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nbot_moveresult_t BotTravel_Swim(bot_movestate_t *ms, aas_reachability_t *reach)\n{\n\tvec3_t dir;\n\tbot_moveresult_t_cleared( result );\n\n\t//swim straight to reachability end\n\tVectorSubtract(reach->start, ms->origin, dir);\n\tVectorNormalize(dir);\n\t//\n\tBotCheckBlocked(ms, dir, qtrue, &result);\n\t//elemantary actions\n\tEA_Move(ms->client, dir, 400);\n\t//\n\tVectorCopy(dir, result.movedir);\n\tVector2Angles(dir, result.ideal_viewangles);\n\tresult.flags |= MOVERESULT_SWIMVIEW;\n\t//\n\treturn result;\n} //end of the function BotTravel_Swim\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nbot_moveresult_t BotTravel_WaterJump(bot_movestate_t *ms, aas_reachability_t *reach)\n{\n\tvec3_t dir, hordir;\n\tfloat dist;\n\tbot_moveresult_t_cleared( result );\n\n\t//swim straight to reachability end\n\tVectorSubtract(reach->end, ms->origin, dir);\n\tVectorCopy(dir, hordir);\n\thordir[2] = 0;\n\tdir[2] += 15 + crandom() * 40;\n\t//botimport.Print(PRT_MESSAGE, \"BotTravel_WaterJump: dir[2] = %f\\n\", dir[2]);\n\tVectorNormalize(dir);\n\tdist = VectorNormalize(hordir);\n\t//elemantary actions\n\t//EA_Move(ms->client, dir, 400);\n\tEA_MoveForward(ms->client);\n\t//move up if close to the actual out of water jump spot\n\tif (dist < 40) EA_MoveUp(ms->client);\n\t//set the ideal view angles\n\tVector2Angles(dir, result.ideal_viewangles);\n\tresult.flags |= MOVERESULT_MOVEMENTVIEW;\n\t//\n\tVectorCopy(dir, result.movedir);\n\t//\n\treturn result;\n} //end of the function BotTravel_WaterJump\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nbot_moveresult_t BotFinishTravel_WaterJump(bot_movestate_t *ms, aas_reachability_t *reach)\n{\n\tvec3_t dir, pnt;\n\tbot_moveresult_t_cleared( result );\n\n\t//botimport.Print(PRT_MESSAGE, \"BotFinishTravel_WaterJump\\n\");\n\t//if waterjumping there's nothing to do\n\tif (ms->moveflags & MFL_WATERJUMP) return result;\n\t//if not touching any water anymore don't do anything\n\t//otherwise the bot sometimes keeps jumping?\n\tVectorCopy(ms->origin, pnt);\n\tpnt[2] -= 32;\t//extra for q2dm4 near red armor/mega health\n\tif (!(AAS_PointContents(pnt) & (CONTENTS_LAVA|CONTENTS_SLIME|CONTENTS_WATER))) return result;\n\t//swim straight to reachability end\n\tVectorSubtract(reach->end, ms->origin, dir);\n\tdir[0] += crandom() * 10;\n\tdir[1] += crandom() * 10;\n\tdir[2] += 70 + crandom() * 10;\n\tVectorNormalize(dir);\n\t//elemantary actions\n\tEA_Move(ms->client, dir, 400);\n\t//set the ideal view angles\n\tVector2Angles(dir, result.ideal_viewangles);\n\tresult.flags |= MOVERESULT_MOVEMENTVIEW;\n\t//\n\tVectorCopy(dir, result.movedir);\n\t//\n\treturn result;\n} //end of the function BotFinishTravel_WaterJump\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nbot_moveresult_t BotTravel_WalkOffLedge(bot_movestate_t *ms, aas_reachability_t *reach)\n{\n\tvec3_t hordir, dir;\n\tfloat dist, speed, reachhordist;\n\tbot_moveresult_t_cleared( result );\n\n\t//check if the bot is blocked by anything\n\tVectorSubtract(reach->start, ms->origin, dir);\n\tVectorNormalize(dir);\n\tBotCheckBlocked(ms, dir, qtrue, &result);\n\t//if the reachability start and end are practially above each other\n\tVectorSubtract(reach->end, reach->start, dir);\n\tdir[2] = 0;\n\treachhordist = VectorLength(dir);\n\t//walk straight to the reachability start\n\thordir[0] = reach->start[0] - ms->origin[0];\n\thordir[1] = reach->start[1] - ms->origin[1];\n\thordir[2] = 0;\n\tdist = VectorNormalize(hordir);\n\t//if pretty close to the start focus on the reachability end\n\tif (dist < 48)\n\t{\n\t\thordir[0] = reach->end[0] - ms->origin[0];\n\t\thordir[1] = reach->end[1] - ms->origin[1];\n\t\thordir[2] = 0;\n\t\tVectorNormalize(hordir);\n\t\t//\n\t\tif (reachhordist < 20)\n\t\t{\n\t\t\tspeed = 100;\n\t\t} //end if\n\t\telse if (!AAS_HorizontalVelocityForJump(0, reach->start, reach->end, &speed))\n\t\t{\n\t\t\tspeed = 400;\n\t\t} //end if\n\t} //end if\n\telse\n\t{\n\t\tif (reachhordist < 20)\n\t\t{\n\t\t\tif (dist > 64) dist = 64;\n\t\t\tspeed = 400 - (256 - 4 * dist);\n\t\t} //end if\n\t\telse\n\t\t{\n\t\t\tspeed = 400;\n\t\t} //end else\n\t} //end else\n\t//\n\tBotCheckBlocked(ms, hordir, qtrue, &result);\n\t//elemantary action\n\tEA_Move(ms->client, hordir, speed);\n\tVectorCopy(hordir, result.movedir);\n\t//\n\treturn result;\n} //end of the function BotTravel_WalkOffLedge\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint BotAirControl(vec3_t origin, vec3_t velocity, vec3_t goal, vec3_t dir, float *speed)\n{\n\tvec3_t org, vel;\n\tfloat dist;\n\tint i;\n\n\tVectorCopy(origin, org);\n\tVectorScale(velocity, 0.1, vel);\n\tfor (i = 0; i < 50; i++)\n\t{\n\t\tvel[2] -= blsv_gravity->value * 0.01;\n\t\t//if going down and next position would be below the goal\n\t\tif (vel[2] < 0 && org[2] + vel[2] < goal[2])\n\t\t{\n\t\t\tVectorScale(vel, (goal[2] - org[2]) / vel[2], vel);\n\t\t\tVectorAdd(org, vel, org);\n\t\t\tVectorSubtract(goal, org, dir);\n\t\t\tdist = VectorNormalize(dir);\n\t\t\tif (dist > 32) dist = 32;\n\t\t\t*speed = 400 - (400 - 13 * dist);\n\t\t\treturn qtrue;\n\t\t} //end if\n\t\telse\n\t\t{\n\t\t\tVectorAdd(org, vel, org);\n\t\t} //end else\n\t} //end for\n\tVectorSet(dir, 0, 0, 0);\n\t*speed = 400;\n\treturn qfalse;\n} //end of the function BotAirControl\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nbot_moveresult_t BotFinishTravel_WalkOffLedge(bot_movestate_t *ms, aas_reachability_t *reach)\n{\n\tvec3_t dir, hordir, end, v;\n\tfloat dist, speed;\n\tbot_moveresult_t_cleared( result );\n\n\t//\n\tVectorSubtract(reach->end, ms->origin, dir);\n\tBotCheckBlocked(ms, dir, qtrue, &result);\n\t//\n\tVectorSubtract(reach->end, ms->origin, v);\n\tv[2] = 0;\n\tdist = VectorNormalize(v);\n\tif (dist > 16) VectorMA(reach->end, 16, v, end);\n\telse VectorCopy(reach->end, end);\n\t//\n\tif (!BotAirControl(ms->origin, ms->velocity, end, hordir, &speed))\n\t{\n\t\t//go straight to the reachability end\n\t\tVectorCopy(dir, hordir);\n\t\thordir[2] = 0;\n\t\t//\n\t\tdist = VectorNormalize(hordir);\n\t\tspeed = 400;\n\t} //end if\n\t//\n\tEA_Move(ms->client, hordir, speed);\n\tVectorCopy(hordir, result.movedir);\n\t//\n\treturn result;\n} //end of the function BotFinishTravel_WalkOffLedge\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\n/*\nbot_moveresult_t BotTravel_Jump(bot_movestate_t *ms, aas_reachability_t *reach)\n{\n\tvec3_t hordir;\n\tfloat dist, gapdist, speed, horspeed, sv_jumpvel;\n\tbot_moveresult_t_cleared( result );\n\n\t//\n\tsv_jumpvel = botlibglobals.sv_jumpvel->value;\n\t//walk straight to the reachability start\n\thordir[0] = reach->start[0] - ms->origin[0];\n\thordir[1] = reach->start[1] - ms->origin[1];\n\thordir[2] = 0;\n\tdist = VectorNormalize(hordir);\n\t//\n\tspeed = 350;\n\t//\n\tgapdist = BotGapDistance(ms, hordir, ms->entitynum);\n\t//if pretty close to the start focus on the reachability end\n\tif (dist < 50 || (gapdist && gapdist < 50))\n\t{\n\t\t//NOTE: using max speed (400) works best\n\t\t//if (AAS_HorizontalVelocityForJump(sv_jumpvel, ms->origin, reach->end, &horspeed))\n\t\t//{\n\t\t//\tspeed = horspeed * 400 / botlibglobals.sv_maxwalkvelocity->value;\n\t\t//} //end if\n\t\thordir[0] = reach->end[0] - ms->origin[0];\n\t\thordir[1] = reach->end[1] - ms->origin[1];\n\t\tVectorNormalize(hordir);\n\t\t//elemantary action jump\n\t\tEA_Jump(ms->client);\n\t\t//\n\t\tms->jumpreach = ms->lastreachnum;\n\t\tspeed = 600;\n\t} //end if\n\telse\n\t{\n\t\tif (AAS_HorizontalVelocityForJump(sv_jumpvel, reach->start, reach->end, &horspeed))\n\t\t{\n\t\t\tspeed = horspeed * 400 / botlibglobals.sv_maxwalkvelocity->value;\n\t\t} //end if\n\t} //end else\n\t//elemantary action\n\tEA_Move(ms->client, hordir, speed);\n\tVectorCopy(hordir, result.movedir);\n\t//\n\treturn result;\n} //end of the function BotTravel_Jump*/\n/*\nbot_moveresult_t BotTravel_Jump(bot_movestate_t *ms, aas_reachability_t *reach)\n{\n\tvec3_t hordir, dir1, dir2, mins, maxs, start, end;\n\tfloat dist1, dist2, speed;\n\tbot_moveresult_t_cleared( result );\n\tbsp_trace_t trace;\n\n\t//\n\thordir[0] = reach->start[0] - reach->end[0];\n\thordir[1] = reach->start[1] - reach->end[1];\n\thordir[2] = 0;\n\tVectorNormalize(hordir);\n\t//\n\tVectorCopy(reach->start, start);\n\tstart[2] += 1;\n\t//minus back the bouding box size plus 16\n\tVectorMA(reach->start, 80, hordir, end);\n\t//\n\tAAS_PresenceTypeBoundingBox(PRESENCE_NORMAL, mins, maxs);\n\t//check for solids\n\ttrace = AAS_Trace(start, mins, maxs, end, ms->entitynum, MASK_PLAYERSOLID);\n\tif (trace.startsolid) VectorCopy(start, trace.endpos);\n\t//check for a gap\n\tfor (dist1 = 0; dist1 < 80; dist1 += 10)\n\t{\n\t\tVectorMA(start, dist1+10, hordir, end);\n\t\tend[2] += 1;\n\t\tif (AAS_PointAreaNum(end) != ms->reachareanum) break;\n\t} //end for\n\tif (dist1 < 80) VectorMA(reach->start, dist1, hordir, trace.endpos);\n//\tdist1 = BotGapDistance(start, hordir, ms->entitynum);\n//\tif (dist1 && dist1 <= trace.fraction * 80) VectorMA(reach->start, dist1-20, hordir, trace.endpos);\n\t//\n\tVectorSubtract(ms->origin, reach->start, dir1);\n\tdir1[2] = 0;\n\tdist1 = VectorNormalize(dir1);\n\tVectorSubtract(ms->origin, trace.endpos, dir2);\n\tdir2[2] = 0;\n\tdist2 = VectorNormalize(dir2);\n\t//if just before the reachability start\n\tif (DotProduct(dir1, dir2) < -0.8 || dist2 < 5)\n\t{\n\t\t//botimport.Print(PRT_MESSAGE, \"between jump start and run to point\\n\");\n\t\thordir[0] = reach->end[0] - ms->origin[0];\n\t\thordir[1] = reach->end[1] - ms->origin[1];\n\t\thordir[2] = 0;\n\t\tVectorNormalize(hordir);\n\t\t//elemantary action jump\n\t\tif (dist1 < 24) EA_Jump(ms->client);\n\t\telse if (dist1 < 32) EA_DelayedJump(ms->client);\n\t\tEA_Move(ms->client, hordir, 600);\n\t\t//\n\t\tms->jumpreach = ms->lastreachnum;\n\t} //end if\n\telse\n\t{\n\t\t//botimport.Print(PRT_MESSAGE, \"going towards run to point\\n\");\n\t\thordir[0] = trace.endpos[0] - ms->origin[0];\n\t\thordir[1] = trace.endpos[1] - ms->origin[1];\n\t\thordir[2] = 0;\n\t\tVectorNormalize(hordir);\n\t\t//\n\t\tif (dist2 > 80) dist2 = 80;\n\t\tspeed = 400 - (400 - 5 * dist2);\n\t\tEA_Move(ms->client, hordir, speed);\n\t} //end else\n\tVectorCopy(hordir, result.movedir);\n\t//\n\treturn result;\n} //end of the function BotTravel_Jump*/\n//*\nbot_moveresult_t BotTravel_Jump(bot_movestate_t *ms, aas_reachability_t *reach)\n{\n\tvec3_t hordir, dir1, dir2, start, end, runstart;\n//\tvec3_t runstart, dir1, dir2, hordir;\n\tfloat dist1, dist2, speed;\n\tbot_moveresult_t_cleared( result );\n\n\t//\n\tAAS_JumpReachRunStart(reach, runstart);\n\t//*\n\thordir[0] = runstart[0] - reach->start[0];\n\thordir[1] = runstart[1] - reach->start[1];\n\thordir[2] = 0;\n\tVectorNormalize(hordir);\n\t//\n\tVectorCopy(reach->start, start);\n\tstart[2] += 1;\n\tVectorMA(reach->start, 80, hordir, runstart);\n\t//check for a gap\n\tfor (dist1 = 0; dist1 < 80; dist1 += 10)\n\t{\n\t\tVectorMA(start, dist1+10, hordir, end);\n\t\tend[2] += 1;\n\t\tif (AAS_PointAreaNum(end) != ms->reachareanum) break;\n\t} //end for\n\tif (dist1 < 80) VectorMA(reach->start, dist1, hordir, runstart);\n\t//\n\tVectorSubtract(ms->origin, reach->start, dir1);\n\tdir1[2] = 0;\n\tdist1 = VectorNormalize(dir1);\n\tVectorSubtract(ms->origin, runstart, dir2);\n\tdir2[2] = 0;\n\tdist2 = VectorNormalize(dir2);\n\t//if just before the reachability start\n\tif (DotProduct(dir1, dir2) < -0.8 || dist2 < 5)\n\t{\n//\t\tbotimport.Print(PRT_MESSAGE, \"between jump start and run start point\\n\");\n\t\thordir[0] = reach->end[0] - ms->origin[0];\n\t\thordir[1] = reach->end[1] - ms->origin[1];\n\t\thordir[2] = 0;\n\t\tVectorNormalize(hordir);\n\t\t//elemantary action jump\n\t\tif (dist1 < 24) EA_Jump(ms->client);\n\t\telse if (dist1 < 32) EA_DelayedJump(ms->client);\n\t\tEA_Move(ms->client, hordir, 600);\n\t\t//\n\t\tms->jumpreach = ms->lastreachnum;\n\t} //end if\n\telse\n\t{\n//\t\tbotimport.Print(PRT_MESSAGE, \"going towards run start point\\n\");\n\t\thordir[0] = runstart[0] - ms->origin[0];\n\t\thordir[1] = runstart[1] - ms->origin[1];\n\t\thordir[2] = 0;\n\t\tVectorNormalize(hordir);\n\t\t//\n\t\tif (dist2 > 80) dist2 = 80;\n\t\tspeed = 400 - (400 - 5 * dist2);\n\t\tEA_Move(ms->client, hordir, speed);\n\t} //end else\n\tVectorCopy(hordir, result.movedir);\n\t//\n\treturn result;\n} //end of the function BotTravel_Jump*/\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nbot_moveresult_t BotFinishTravel_Jump(bot_movestate_t *ms, aas_reachability_t *reach)\n{\n\tvec3_t hordir, hordir2;\n\tfloat speed, dist;\n\tbot_moveresult_t_cleared( result );\n\n\t//if not jumped yet\n\tif (!ms->jumpreach) return result;\n\t//go straight to the reachability end\n\thordir[0] = reach->end[0] - ms->origin[0];\n\thordir[1] = reach->end[1] - ms->origin[1];\n\thordir[2] = 0;\n\tdist = VectorNormalize(hordir);\n\t//\n\thordir2[0] = reach->end[0] - reach->start[0];\n\thordir2[1] = reach->end[1] - reach->start[1];\n\thordir2[2] = 0;\n\tVectorNormalize(hordir2);\n\t//\n\tif (DotProduct(hordir, hordir2) < -0.5 && dist < 24) return result;\n\t//always use max speed when traveling through the air\n\tspeed = 800;\n\t//\n\tEA_Move(ms->client, hordir, speed);\n\tVectorCopy(hordir, result.movedir);\n\t//\n\treturn result;\n} //end of the function BotFinishTravel_Jump\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nbot_moveresult_t BotTravel_Ladder(bot_movestate_t *ms, aas_reachability_t *reach)\n{\n\t//float dist, speed;\n\tvec3_t dir, viewdir;//, hordir;\n\tvec3_t origin = {0, 0, 0};\n//\tvec3_t up = {0, 0, 1};\n\tbot_moveresult_t_cleared( result );\n\n\t//\n//\tif ((ms->moveflags & MFL_AGAINSTLADDER))\n\t\t//NOTE: not a good idea for ladders starting in water\n\t\t// || !(ms->moveflags & MFL_ONGROUND))\n\t{\n\t\t//botimport.Print(PRT_MESSAGE, \"against ladder or not on ground\\n\");\n\t\tVectorSubtract(reach->end, ms->origin, dir);\n\t\tVectorNormalize(dir);\n\t\t//set the ideal view angles, facing the ladder up or down\n\t\tviewdir[0] = dir[0];\n\t\tviewdir[1] = dir[1];\n\t\tviewdir[2] = 3 * dir[2];\n\t\tVector2Angles(viewdir, result.ideal_viewangles);\n\t\t//elemantary action\n\t\tEA_Move(ms->client, origin, 0);\n\t\tEA_MoveForward(ms->client);\n\t\t//set movement view flag so the AI can see the view is focussed\n\t\tresult.flags |= MOVERESULT_MOVEMENTVIEW;\n\t} //end if\n/*\telse\n\t{\n\t\t//botimport.Print(PRT_MESSAGE, \"moving towards ladder\\n\");\n\t\tVectorSubtract(reach->end, ms->origin, dir);\n\t\t//make sure the horizontal movement is large anough\n\t\tVectorCopy(dir, hordir);\n\t\thordir[2] = 0;\n\t\tdist = VectorNormalize(hordir);\n\t\t//\n\t\tdir[0] = hordir[0];\n\t\tdir[1] = hordir[1];\n\t\tif (dir[2] > 0) dir[2] = 1;\n\t\telse dir[2] = -1;\n\t\tif (dist > 50) dist = 50;\n\t\tspeed = 400 - (200 - 4 * dist);\n\t\tEA_Move(ms->client, dir, speed);\n\t} //end else*/\n\t//save the movement direction\n\tVectorCopy(dir, result.movedir);\n\t//\n\treturn result;\n} //end of the function BotTravel_Ladder\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nbot_moveresult_t BotTravel_Teleport(bot_movestate_t *ms, aas_reachability_t *reach)\n{\n\tvec3_t hordir;\n\tfloat dist;\n\tbot_moveresult_t_cleared( result );\n\n\t//if the bot is being teleported\n\tif (ms->moveflags & MFL_TELEPORTED) return result;\n\n\t//walk straight to center of the teleporter\n\tVectorSubtract(reach->start, ms->origin, hordir);\n\tif (!(ms->moveflags & MFL_SWIMMING)) hordir[2] = 0;\n\tdist = VectorNormalize(hordir);\n\t//\n\tBotCheckBlocked(ms, hordir, qtrue, &result);\n\n\tif (dist < 30) EA_Move(ms->client, hordir, 200);\n\telse EA_Move(ms->client, hordir, 400);\n\n\tif (ms->moveflags & MFL_SWIMMING) result.flags |= MOVERESULT_SWIMVIEW;\n\n\tVectorCopy(hordir, result.movedir);\n\treturn result;\n} //end of the function BotTravel_Teleport\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nbot_moveresult_t BotTravel_Elevator(bot_movestate_t *ms, aas_reachability_t *reach)\n{\n\tvec3_t dir, dir1, dir2, hordir, bottomcenter;\n\tfloat dist, dist1, dist2, speed;\n\tbot_moveresult_t_cleared( result );\n\n\t//if standing on the plat\n\tif (BotOnMover(ms->origin, ms->entitynum, reach))\n\t{\n#ifdef DEBUG_ELEVATOR\n\t\tbotimport.Print(PRT_MESSAGE, \"bot on elevator\\n\");\n#endif //DEBUG_ELEVATOR\n\t\t//if vertically not too far from the end point\n\t\tif (fabs(ms->origin[2] - reach->end[2]) < sv_maxbarrier->value)\n\t\t{\n#ifdef DEBUG_ELEVATOR\n\t\t\tbotimport.Print(PRT_MESSAGE, \"bot moving to end\\n\");\n#endif //DEBUG_ELEVATOR\n\t\t\t//move to the end point\n\t\t\tVectorSubtract(reach->end, ms->origin, hordir);\n\t\t\thordir[2] = 0;\n\t\t\tVectorNormalize(hordir);\n\t\t\tif (!BotCheckBarrierJump(ms, hordir, 100))\n\t\t\t{\n\t\t\t\tEA_Move(ms->client, hordir, 400);\n\t\t\t} //end if\n\t\t\tVectorCopy(hordir, result.movedir);\n\t\t} //end else\n\t\t//if not really close to the center of the elevator\n\t\telse\n\t\t{\n\t\t\tMoverBottomCenter(reach, bottomcenter);\n\t\t\tVectorSubtract(bottomcenter, ms->origin, hordir);\n\t\t\thordir[2] = 0;\n\t\t\tdist = VectorNormalize(hordir);\n\t\t\t//\n\t\t\tif (dist > 10)\n\t\t\t{\n#ifdef DEBUG_ELEVATOR\n\t\t\t\tbotimport.Print(PRT_MESSAGE, \"bot moving to center\\n\");\n#endif //DEBUG_ELEVATOR\n\t\t\t\t//move to the center of the plat\n\t\t\t\tif (dist > 100) dist = 100;\n\t\t\t\tspeed = 400 - (400 - 4 * dist);\n\t\t\t\t//\n\t\t\t\tEA_Move(ms->client, hordir, speed);\n\t\t\t\tVectorCopy(hordir, result.movedir);\n\t\t\t} //end if\n\t\t} //end else\n\t} //end if\n\telse\n\t{\n#ifdef DEBUG_ELEVATOR\n\t\tbotimport.Print(PRT_MESSAGE, \"bot not on elevator\\n\");\n#endif //DEBUG_ELEVATOR\n\t\t//if very near the reachability end\n\t\tVectorSubtract(reach->end, ms->origin, dir);\n\t\tdist = VectorLength(dir);\n\t\tif (dist < 64)\n\t\t{\n\t\t\tif (dist > 60) dist = 60;\n\t\t\tspeed = 360 - (360 - 6 * dist);\n\t\t\t//\n\t\t\tif ((ms->moveflags & MFL_SWIMMING) || !BotCheckBarrierJump(ms, dir, 50))\n\t\t\t{\n\t\t\t\tif (speed > 5) EA_Move(ms->client, dir, speed);\n\t\t\t} //end if\n\t\t\tVectorCopy(dir, result.movedir);\n\t\t\t//\n\t\t\tif (ms->moveflags & MFL_SWIMMING) result.flags |= MOVERESULT_SWIMVIEW;\n\t\t\t//stop using this reachability\n\t\t\tms->reachability_time = 0;\n\t\t\treturn result;\n\t\t} //end if\n\t\t//get direction and distance to reachability start\n\t\tVectorSubtract(reach->start, ms->origin, dir1);\n\t\tif (!(ms->moveflags & MFL_SWIMMING)) dir1[2] = 0;\n\t\tdist1 = VectorNormalize(dir1);\n\t\t//if the elevator isn't down\n\t\tif (!MoverDown(reach))\n\t\t{\n#ifdef DEBUG_ELEVATOR\n\t\t\tbotimport.Print(PRT_MESSAGE, \"elevator not down\\n\");\n#endif //DEBUG_ELEVATOR\n\t\t\tdist = dist1;\n\t\t\tVectorCopy(dir1, dir);\n\t\t\t//\n\t\t\tBotCheckBlocked(ms, dir, qfalse, &result);\n\t\t\t//\n\t\t\tif (dist > 60) dist = 60;\n\t\t\tspeed = 360 - (360 - 6 * dist);\n\t\t\t//\n\t\t\tif (!(ms->moveflags & MFL_SWIMMING) && !BotCheckBarrierJump(ms, dir, 50))\n\t\t\t{\n\t\t\t\tif (speed > 5) EA_Move(ms->client, dir, speed);\n\t\t\t} //end if\n\t\t\tVectorCopy(dir, result.movedir);\n\t\t\t//\n\t\t\tif (ms->moveflags & MFL_SWIMMING) result.flags |= MOVERESULT_SWIMVIEW;\n\t\t\t//this isn't a failure... just wait till the elevator comes down\n\t\t\tresult.type = RESULTTYPE_ELEVATORUP;\n\t\t\tresult.flags |= MOVERESULT_WAITING;\n\t\t\treturn result;\n\t\t} //end if\n\t\t//get direction and distance to elevator bottom center\n\t\tMoverBottomCenter(reach, bottomcenter);\n\t\tVectorSubtract(bottomcenter, ms->origin, dir2);\n\t\tif (!(ms->moveflags & MFL_SWIMMING)) dir2[2] = 0;\n\t\tdist2 = VectorNormalize(dir2);\n\t\t//if very close to the reachability start or\n\t\t//closer to the elevator center or\n\t\t//between reachability start and elevator center\n\t\tif (dist1 < 20 || dist2 < dist1 || DotProduct(dir1, dir2) < 0)\n\t\t{\n#ifdef DEBUG_ELEVATOR\n\t\t\tbotimport.Print(PRT_MESSAGE, \"bot moving to center\\n\");\n#endif //DEBUG_ELEVATOR\n\t\t\tdist = dist2;\n\t\t\tVectorCopy(dir2, dir);\n\t\t} //end if\n\t\telse //closer to the reachability start\n\t\t{\n#ifdef DEBUG_ELEVATOR\n\t\t\tbotimport.Print(PRT_MESSAGE, \"bot moving to start\\n\");\n#endif //DEBUG_ELEVATOR\n\t\t\tdist = dist1;\n\t\t\tVectorCopy(dir1, dir);\n\t\t} //end else\n\t\t//\n\t\tBotCheckBlocked(ms, dir, qfalse, &result);\n\t\t//\n\t\tif (dist > 60) dist = 60;\n\t\tspeed = 400 - (400 - 6 * dist);\n\t\t//\n\t\tif (!(ms->moveflags & MFL_SWIMMING) && !BotCheckBarrierJump(ms, dir, 50))\n\t\t{\n\t\t\tEA_Move(ms->client, dir, speed);\n\t\t} //end if\n\t\tVectorCopy(dir, result.movedir);\n\t\t//\n\t\tif (ms->moveflags & MFL_SWIMMING) result.flags |= MOVERESULT_SWIMVIEW;\n\t} //end else\n\treturn result;\n} //end of the function BotTravel_Elevator\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nbot_moveresult_t BotFinishTravel_Elevator(bot_movestate_t *ms, aas_reachability_t *reach)\n{\n\tvec3_t bottomcenter, bottomdir, topdir;\n\tbot_moveresult_t_cleared( result );\n\n\t//\n\tMoverBottomCenter(reach, bottomcenter);\n\tVectorSubtract(bottomcenter, ms->origin, bottomdir);\n\t//\n\tVectorSubtract(reach->end, ms->origin, topdir);\n\t//\n\tif (fabs(bottomdir[2]) < fabs(topdir[2]))\n\t{\n\t\tVectorNormalize(bottomdir);\n\t\tEA_Move(ms->client, bottomdir, 300);\n\t} //end if\n\telse\n\t{\n\t\tVectorNormalize(topdir);\n\t\tEA_Move(ms->client, topdir, 300);\n\t} //end else\n\treturn result;\n} //end of the function BotFinishTravel_Elevator\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid BotFuncBobStartEnd(aas_reachability_t *reach, vec3_t start, vec3_t end, vec3_t origin)\n{\n\tint spawnflags, modelnum;\n\tvec3_t mins, maxs, mid, angles = {0, 0, 0};\n\tint num0, num1;\n\n\tmodelnum = reach->facenum & 0x0000FFFF;\n\tif (!AAS_OriginOfMoverWithModelNum(modelnum, origin))\n\t{\n\t\tbotimport.Print(PRT_MESSAGE, \"BotFuncBobStartEnd: no entity with model %d\\n\", modelnum);\n\t\tVectorSet(start, 0, 0, 0);\n\t\tVectorSet(end, 0, 0, 0);\n\t\treturn;\n\t} //end if\n\tAAS_BSPModelMinsMaxsOrigin(modelnum, angles, mins, maxs, NULL);\n\tVectorAdd(mins, maxs, mid);\n\tVectorScale(mid, 0.5, mid);\n\tVectorCopy(mid, start);\n\tVectorCopy(mid, end);\n\tspawnflags = reach->facenum >> 16;\n\tnum0 = reach->edgenum >> 16;\n\tif (num0 > 0x00007FFF) num0 |= 0xFFFF0000;\n\tnum1 = reach->edgenum & 0x0000FFFF;\n\tif (num1 > 0x00007FFF) num1 |= 0xFFFF0000;\n\tif (spawnflags & 1)\n\t{\n\t\tstart[0] = num0;\n\t\tend[0] = num1;\n\t\t//\n\t\torigin[0] += mid[0];\n\t\torigin[1] = mid[1];\n\t\torigin[2] = mid[2];\n\t} //end if\n\telse if (spawnflags & 2)\n\t{\n\t\tstart[1] = num0;\n\t\tend[1] = num1;\n\t\t//\n\t\torigin[0] = mid[0];\n\t\torigin[1] += mid[1];\n\t\torigin[2] = mid[2];\n\t} //end else if\n\telse\n\t{\n\t\tstart[2] = num0;\n\t\tend[2] = num1;\n\t\t//\n\t\torigin[0] = mid[0];\n\t\torigin[1] = mid[1];\n\t\torigin[2] += mid[2];\n\t} //end else\n} //end of the function BotFuncBobStartEnd\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nbot_moveresult_t BotTravel_FuncBobbing(bot_movestate_t *ms, aas_reachability_t *reach)\n{\n\tvec3_t dir, dir1, dir2, hordir, bottomcenter, bob_start, bob_end, bob_origin;\n\tfloat dist, dist1, dist2, speed;\n\tbot_moveresult_t_cleared( result );\n\n\t//\n\tBotFuncBobStartEnd(reach, bob_start, bob_end, bob_origin);\n\t//if standing ontop of the func_bobbing\n\tif (BotOnMover(ms->origin, ms->entitynum, reach))\n\t{\n#ifdef DEBUG_FUNCBOB\n\t\tbotimport.Print(PRT_MESSAGE, \"bot on func_bobbing\\n\");\n#endif\n\t\t//if near end point of reachability\n\t\tVectorSubtract(bob_origin, bob_end, dir);\n\t\tif (VectorLength(dir) < 24)\n\t\t{\n#ifdef DEBUG_FUNCBOB\n\t\t\tbotimport.Print(PRT_MESSAGE, \"bot moving to reachability end\\n\");\n#endif\n\t\t\t//move to the end point\n\t\t\tVectorSubtract(reach->end, ms->origin, hordir);\n\t\t\thordir[2] = 0;\n\t\t\tVectorNormalize(hordir);\n\t\t\tif (!BotCheckBarrierJump(ms, hordir, 100))\n\t\t\t{\n\t\t\t\tEA_Move(ms->client, hordir, 400);\n\t\t\t} //end if\n\t\t\tVectorCopy(hordir, result.movedir);\n\t\t} //end else\n\t\t//if not really close to the center of the elevator\n\t\telse\n\t\t{\n\t\t\tMoverBottomCenter(reach, bottomcenter);\n\t\t\tVectorSubtract(bottomcenter, ms->origin, hordir);\n\t\t\thordir[2] = 0;\n\t\t\tdist = VectorNormalize(hordir);\n\t\t\t//\n\t\t\tif (dist > 10)\n\t\t\t{\n#ifdef DEBUG_FUNCBOB\n\t\t\t\tbotimport.Print(PRT_MESSAGE, \"bot moving to func_bobbing center\\n\");\n#endif\n\t\t\t\t//move to the center of the plat\n\t\t\t\tif (dist > 100) dist = 100;\n\t\t\t\tspeed = 400 - (400 - 4 * dist);\n\t\t\t\t//\n\t\t\t\tEA_Move(ms->client, hordir, speed);\n\t\t\t\tVectorCopy(hordir, result.movedir);\n\t\t\t} //end if\n\t\t} //end else\n\t} //end if\n\telse\n\t{\n#ifdef DEBUG_FUNCBOB\n\t\tbotimport.Print(PRT_MESSAGE, \"bot not ontop of func_bobbing\\n\");\n#endif\n\t\t//if very near the reachability end\n\t\tVectorSubtract(reach->end, ms->origin, dir);\n\t\tdist = VectorLength(dir);\n\t\tif (dist < 64)\n\t\t{\n#ifdef DEBUG_FUNCBOB\n\t\t\tbotimport.Print(PRT_MESSAGE, \"bot moving to end\\n\");\n#endif\n\t\t\tif (dist > 60) dist = 60;\n\t\t\tspeed = 360 - (360 - 6 * dist);\n\t\t\t//if swimming or no barrier jump\n\t\t\tif ((ms->moveflags & MFL_SWIMMING) || !BotCheckBarrierJump(ms, dir, 50))\n\t\t\t{\n\t\t\t\tif (speed > 5) EA_Move(ms->client, dir, speed);\n\t\t\t} //end if\n\t\t\tVectorCopy(dir, result.movedir);\n\t\t\t//\n\t\t\tif (ms->moveflags & MFL_SWIMMING) result.flags |= MOVERESULT_SWIMVIEW;\n\t\t\t//stop using this reachability\n\t\t\tms->reachability_time = 0;\n\t\t\treturn result;\n\t\t} //end if\n\t\t//get direction and distance to reachability start\n\t\tVectorSubtract(reach->start, ms->origin, dir1);\n\t\tif (!(ms->moveflags & MFL_SWIMMING)) dir1[2] = 0;\n\t\tdist1 = VectorNormalize(dir1);\n\t\t//if func_bobbing is Not its start position\n\t\tVectorSubtract(bob_origin, bob_start, dir);\n\t\tif (VectorLength(dir) > 16)\n\t\t{\n#ifdef DEBUG_FUNCBOB\n\t\t\tbotimport.Print(PRT_MESSAGE, \"func_bobbing not at start\\n\");\n#endif\n\t\t\tdist = dist1;\n\t\t\tVectorCopy(dir1, dir);\n\t\t\t//\n\t\t\tBotCheckBlocked(ms, dir, qfalse, &result);\n\t\t\t//\n\t\t\tif (dist > 60) dist = 60;\n\t\t\tspeed = 360 - (360 - 6 * dist);\n\t\t\t//\n\t\t\tif (!(ms->moveflags & MFL_SWIMMING) && !BotCheckBarrierJump(ms, dir, 50))\n\t\t\t{\n\t\t\t\tif (speed > 5) EA_Move(ms->client, dir, speed);\n\t\t\t} //end if\n\t\t\tVectorCopy(dir, result.movedir);\n\t\t\t//\n\t\t\tif (ms->moveflags & MFL_SWIMMING) result.flags |= MOVERESULT_SWIMVIEW;\n\t\t\t//this isn't a failure... just wait till the func_bobbing arrives\n\t\t\tresult.type = RESULTTYPE_WAITFORFUNCBOBBING;\n\t\t\tresult.flags |= MOVERESULT_WAITING;\n\t\t\treturn result;\n\t\t} //end if\n\t\t//get direction and distance to func_bob bottom center\n\t\tMoverBottomCenter(reach, bottomcenter);\n\t\tVectorSubtract(bottomcenter, ms->origin, dir2);\n\t\tif (!(ms->moveflags & MFL_SWIMMING)) dir2[2] = 0;\n\t\tdist2 = VectorNormalize(dir2);\n\t\t//if very close to the reachability start or\n\t\t//closer to the elevator center or\n\t\t//between reachability start and func_bobbing center\n\t\tif (dist1 < 20 || dist2 < dist1 || DotProduct(dir1, dir2) < 0)\n\t\t{\n#ifdef DEBUG_FUNCBOB\n\t\t\tbotimport.Print(PRT_MESSAGE, \"bot moving to func_bobbing center\\n\");\n#endif\n\t\t\tdist = dist2;\n\t\t\tVectorCopy(dir2, dir);\n\t\t} //end if\n\t\telse //closer to the reachability start\n\t\t{\n#ifdef DEBUG_FUNCBOB\n\t\t\tbotimport.Print(PRT_MESSAGE, \"bot moving to reachability start\\n\");\n#endif\n\t\t\tdist = dist1;\n\t\t\tVectorCopy(dir1, dir);\n\t\t} //end else\n\t\t//\n\t\tBotCheckBlocked(ms, dir, qfalse, &result);\n\t\t//\n\t\tif (dist > 60) dist = 60;\n\t\tspeed = 400 - (400 - 6 * dist);\n\t\t//\n\t\tif (!(ms->moveflags & MFL_SWIMMING) && !BotCheckBarrierJump(ms, dir, 50))\n\t\t{\n\t\t\tEA_Move(ms->client, dir, speed);\n\t\t} //end if\n\t\tVectorCopy(dir, result.movedir);\n\t\t//\n\t\tif (ms->moveflags & MFL_SWIMMING) result.flags |= MOVERESULT_SWIMVIEW;\n\t} //end else\n\treturn result;\n} //end of the function BotTravel_FuncBobbing\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nbot_moveresult_t BotFinishTravel_FuncBobbing(bot_movestate_t *ms, aas_reachability_t *reach)\n{\n\tvec3_t bob_origin, bob_start, bob_end, dir, hordir, bottomcenter;\n\tbot_moveresult_t_cleared( result );\n\tfloat dist, speed;\n\n\t//\n\tBotFuncBobStartEnd(reach, bob_start, bob_end, bob_origin);\n\t//\n\tVectorSubtract(bob_origin, bob_end, dir);\n\tdist = VectorLength(dir);\n\t//if the func_bobbing is near the end\n\tif (dist < 16)\n\t{\n\t\tVectorSubtract(reach->end, ms->origin, hordir);\n\t\tif (!(ms->moveflags & MFL_SWIMMING)) hordir[2] = 0;\n\t\tdist = VectorNormalize(hordir);\n\t\t//\n\t\tif (dist > 60) dist = 60;\n\t\tspeed = 360 - (360 - 6 * dist);\n\t\t//\n\t\tif (speed > 5) EA_Move(ms->client, dir, speed);\n\t\tVectorCopy(dir, result.movedir);\n\t\t//\n\t\tif (ms->moveflags & MFL_SWIMMING) result.flags |= MOVERESULT_SWIMVIEW;\n\t} //end if\n\telse\n\t{\n\t\tMoverBottomCenter(reach, bottomcenter);\n\t\tVectorSubtract(bottomcenter, ms->origin, hordir);\n\t\tif (!(ms->moveflags & MFL_SWIMMING)) hordir[2] = 0;\n\t\tdist = VectorNormalize(hordir);\n\t\t//\n\t\tif (dist > 5)\n\t\t{\n\t\t\t//move to the center of the plat\n\t\t\tif (dist > 100) dist = 100;\n\t\t\tspeed = 400 - (400 - 4 * dist);\n\t\t\t//\n\t\t\tEA_Move(ms->client, hordir, speed);\n\t\t\tVectorCopy(hordir, result.movedir);\n\t\t} //end if\n\t} //end else\n\treturn result;\n} //end of the function BotFinishTravel_FuncBobbing\n//===========================================================================\n// 0  no valid grapple hook visible\n// 1  the grapple hook is still flying\n// 2  the grapple hooked into a wall\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint GrappleState(bot_movestate_t *ms, aas_reachability_t *reach)\n{\n\tint i;\n\taas_entityinfo_t entinfo;\n\n\t//if the grapple hook is pulling\n\tif (ms->moveflags & MFL_GRAPPLEPULL)\n\t\treturn 2;\n\t//check for a visible grapple missile entity\n\t//or visible grapple entity\n\tfor (i = AAS_NextEntity(0); i; i = AAS_NextEntity(i))\n\t{\n\t\tif (AAS_EntityType(i) == (int) entitytypemissile->value)\n\t\t{\n\t\t\tAAS_EntityInfo(i, &entinfo);\n\t\t\tif (entinfo.weapon == (int) weapindex_grapple->value)\n\t\t\t{\n\t\t\t\treturn 1;\n\t\t\t} //end if\n\t\t} //end if\n\t} //end for\n\t//no valid grapple at all\n\treturn 0;\n} //end of the function GrappleState\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid BotResetGrapple(bot_movestate_t *ms)\n{\n\taas_reachability_t reach;\n\n\tAAS_ReachabilityFromNum(ms->lastreachnum, &reach);\n\t//if not using the grapple hook reachability anymore\n\tif ((reach.traveltype & TRAVELTYPE_MASK) != TRAVEL_GRAPPLEHOOK)\n\t{\n\t\tif ((ms->moveflags & MFL_ACTIVEGRAPPLE) || ms->grapplevisible_time)\n\t\t{\n\t\t\tif (offhandgrapple->value)\n\t\t\t\tEA_Command(ms->client, cmd_grappleoff->string);\n\t\t\tms->moveflags &= ~MFL_ACTIVEGRAPPLE;\n\t\t\tms->grapplevisible_time = 0;\n#ifdef DEBUG_GRAPPLE\n\t\t\tbotimport.Print(PRT_MESSAGE, \"reset grapple\\n\");\n#endif //DEBUG_GRAPPLE\n\t\t} //end if\n\t} //end if\n} //end of the function BotResetGrapple\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nbot_moveresult_t BotTravel_Grapple(bot_movestate_t *ms, aas_reachability_t *reach)\n{\n\tbot_moveresult_t_cleared( result );\n\tfloat dist, speed;\n\tvec3_t dir, viewdir, org;\n\tint state, areanum;\n\tbsp_trace_t trace;\n\n#ifdef DEBUG_GRAPPLE\n\tstatic int debugline;\n\tif (!debugline) debugline = botimport.DebugLineCreate();\n\tbotimport.DebugLineShow(debugline, reach->start, reach->end, LINECOLOR_BLUE);\n#endif //DEBUG_GRAPPLE\n\n\t//\n\tif (ms->moveflags & MFL_GRAPPLERESET)\n\t{\n\t\tif (offhandgrapple->value)\n\t\t\tEA_Command(ms->client, cmd_grappleoff->string);\n\t\tms->moveflags &= ~MFL_ACTIVEGRAPPLE;\n\t\treturn result;\n\t} //end if\n\t//\n\tif (!(int) offhandgrapple->value)\n\t{\n\t\tresult.weapon = weapindex_grapple->value;\n\t\tresult.flags |= MOVERESULT_MOVEMENTWEAPON;\n\t} //end if\n\t//\n\tif (ms->moveflags & MFL_ACTIVEGRAPPLE)\n\t{\n#ifdef DEBUG_GRAPPLE\n\t\tbotimport.Print(PRT_MESSAGE, \"BotTravel_Grapple: active grapple\\n\");\n#endif //DEBUG_GRAPPLE\n\t\t//\n\t\tstate = GrappleState(ms, reach);\n\t\t//\n\t\tVectorSubtract(reach->end, ms->origin, dir);\n\t\tdir[2] = 0;\n\t\tdist = VectorLength(dir);\n\t\t//if very close to the grapple end or the grappled is hooked and\n\t\t//the bot doesn't get any closer\n\t\tif (state && dist < 48)\n\t\t{\n\t\t\tif (ms->lastgrappledist - dist < 1)\n\t\t\t{\n#ifdef DEBUG_GRAPPLE\n\t\t\t\tbotimport.Print(PRT_ERROR, \"grapple normal end\\n\");\n#endif //DEBUG_GRAPPLE\n\t\t\t\tif (offhandgrapple->value)\n\t\t\t\t\tEA_Command(ms->client, cmd_grappleoff->string);\n\t\t\t\tms->moveflags &= ~MFL_ACTIVEGRAPPLE;\n\t\t\t\tms->moveflags |= MFL_GRAPPLERESET;\n\t\t\t\tms->reachability_time = 0;\t//end the reachability\n\t\t\t\treturn result;\n\t\t\t} //end if\n\t\t} //end if\n\t\t//if no valid grapple at all, or the grapple hooked and the bot\n\t\t//isn't moving anymore\n\t\telse if (!state || (state == 2 && dist > ms->lastgrappledist - 2))\n\t\t{\n\t\t\tif (ms->grapplevisible_time < AAS_Time() - 0.4)\n\t\t\t{\n#ifdef DEBUG_GRAPPLE\n\t\t\t\tbotimport.Print(PRT_ERROR, \"grapple not visible\\n\");\n#endif //DEBUG_GRAPPLE\n\t\t\t\tif (offhandgrapple->value)\n\t\t\t\t\tEA_Command(ms->client, cmd_grappleoff->string);\n\t\t\t\tms->moveflags &= ~MFL_ACTIVEGRAPPLE;\n\t\t\t\tms->moveflags |= MFL_GRAPPLERESET;\n\t\t\t\tms->reachability_time = 0;\t//end the reachability\n\t\t\t\treturn result;\n\t\t\t} //end if\n\t\t} //end if\n\t\telse\n\t\t{\n\t\t\tms->grapplevisible_time = AAS_Time();\n\t\t} //end else\n\t\t//\n\t\tif (!(int) offhandgrapple->value)\n\t\t{\n\t\t\tEA_Attack(ms->client);\n\t\t} //end if\n\t\t//remember the current grapple distance\n\t\tms->lastgrappledist = dist;\n\t} //end if\n\telse\n\t{\n#ifdef DEBUG_GRAPPLE\n\t\tbotimport.Print(PRT_MESSAGE, \"BotTravel_Grapple: inactive grapple\\n\");\n#endif //DEBUG_GRAPPLE\n\t\t//\n\t\tms->grapplevisible_time = AAS_Time();\n\t\t//\n\t\tVectorSubtract(reach->start, ms->origin, dir);\n\t\tif (!(ms->moveflags & MFL_SWIMMING)) dir[2] = 0;\n\t\tVectorAdd(ms->origin, ms->viewoffset, org);\n\t\tVectorSubtract(reach->end, org, viewdir);\n\t\t//\n\t\tdist = VectorNormalize(dir);\n\t\tVector2Angles(viewdir, result.ideal_viewangles);\n\t\tresult.flags |= MOVERESULT_MOVEMENTVIEW;\n\t\t//\n\t\tif (dist < 5 &&\n\t\t\tfabs(AngleDiff(result.ideal_viewangles[0], ms->viewangles[0])) < 2 &&\n\t\t\tfabs(AngleDiff(result.ideal_viewangles[1], ms->viewangles[1])) < 2)\n\t\t{\n#ifdef DEBUG_GRAPPLE\n\t\t\tbotimport.Print(PRT_MESSAGE, \"BotTravel_Grapple: activating grapple\\n\");\n#endif //DEBUG_GRAPPLE\n\t\t\t//check if the grapple missile path is clear\n\t\t\tVectorAdd(ms->origin, ms->viewoffset, org);\n\t\t\ttrace = AAS_Trace(org, NULL, NULL, reach->end, ms->entitynum, CONTENTS_SOLID);\n\t\t\tVectorSubtract(reach->end, trace.endpos, dir);\n\t\t\tif (VectorLength(dir) > 16)\n\t\t\t{\n\t\t\t\tresult.failure = qtrue;\n\t\t\t\treturn result;\n\t\t\t} //end if\n\t\t\t//activate the grapple\n\t\t\tif (offhandgrapple->value)\n\t\t\t{\n\t\t\t\tEA_Command(ms->client, cmd_grappleon->string);\n\t\t\t} //end if\n\t\t\telse\n\t\t\t{\n\t\t\t\tEA_Attack(ms->client);\n\t\t\t} //end else\n\t\t\tms->moveflags |= MFL_ACTIVEGRAPPLE;\n\t\t\tms->lastgrappledist = 999999;\n\t\t} //end if\n\t\telse\n\t\t{\n\t\t\tif (dist < 70) speed = 300 - (300 - 4 * dist);\n\t\t\telse speed = 400;\n\t\t\t//\n\t\t\tBotCheckBlocked(ms, dir, qtrue, &result);\n\t\t\t//elemantary action move in direction\n\t\t\tEA_Move(ms->client, dir, speed);\n\t\t\tVectorCopy(dir, result.movedir);\n\t\t} //end else\n\t\t//if in another area before actually grappling\n\t\tareanum = AAS_PointAreaNum(ms->origin);\n\t\tif (areanum && areanum != ms->reachareanum) ms->reachability_time = 0;\n\t} //end else\n\treturn result;\n} //end of the function BotTravel_Grapple\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t\t-\n//===========================================================================\nbot_moveresult_t BotTravel_RocketJump(bot_movestate_t *ms, aas_reachability_t *reach)\n{\n\tvec3_t hordir;\n\tfloat dist, speed;\n\tbot_moveresult_t_cleared( result );\n\n\t//botimport.Print(PRT_MESSAGE, \"BotTravel_RocketJump: bah\\n\");\n\t//\n\thordir[0] = reach->start[0] - ms->origin[0];\n\thordir[1] = reach->start[1] - ms->origin[1];\n\thordir[2] = 0;\n\t//\n\tdist = VectorNormalize(hordir);\n\t//look in the movement direction\n\tVector2Angles(hordir, result.ideal_viewangles);\n\t//look straight down\n\tresult.ideal_viewangles[PITCH] = 90;\n\t//\n\tif (dist < 5 &&\n\t\t\tfabs(AngleDiff(result.ideal_viewangles[0], ms->viewangles[0])) < 5 &&\n\t\t\tfabs(AngleDiff(result.ideal_viewangles[1], ms->viewangles[1])) < 5)\n\t{\n\t\t//botimport.Print(PRT_MESSAGE, \"between jump start and run start point\\n\");\n\t\thordir[0] = reach->end[0] - ms->origin[0];\n\t\thordir[1] = reach->end[1] - ms->origin[1];\n\t\thordir[2] = 0;\n\t\tVectorNormalize(hordir);\n\t\t//elemantary action jump\n\t\tEA_Jump(ms->client);\n\t\tEA_Attack(ms->client);\n\t\tEA_Move(ms->client, hordir, 800);\n\t\t//\n\t\tms->jumpreach = ms->lastreachnum;\n\t} //end if\n\telse\n\t{\n\t\tif (dist > 80) dist = 80;\n\t\tspeed = 400 - (400 - 5 * dist);\n\t\tEA_Move(ms->client, hordir, speed);\n\t} //end else\n\t//look in the movement direction\n\tVector2Angles(hordir, result.ideal_viewangles);\n\t//look straight down\n\tresult.ideal_viewangles[PITCH] = 90;\n\t//set the view angles directly\n\tEA_View(ms->client, result.ideal_viewangles);\n\t//view is important for the movment\n\tresult.flags |= MOVERESULT_MOVEMENTVIEWSET;\n\t//select the rocket launcher\n\tEA_SelectWeapon(ms->client, (int) weapindex_rocketlauncher->value);\n\t//weapon is used for movement\n\tresult.weapon = (int) weapindex_rocketlauncher->value;\n\tresult.flags |= MOVERESULT_MOVEMENTWEAPON;\n\t//\n\tVectorCopy(hordir, result.movedir);\n\t//\n\treturn result;\n} //end of the function BotTravel_RocketJump\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nbot_moveresult_t BotTravel_BFGJump(bot_movestate_t *ms, aas_reachability_t *reach)\n{\n\tvec3_t hordir;\n\tfloat dist, speed;\n\tbot_moveresult_t_cleared( result );\n\n\t//botimport.Print(PRT_MESSAGE, \"BotTravel_BFGJump: bah\\n\");\n\t//\n\thordir[0] = reach->start[0] - ms->origin[0];\n\thordir[1] = reach->start[1] - ms->origin[1];\n\thordir[2] = 0;\n\t//\n\tdist = VectorNormalize(hordir);\n\t//\n\tif (dist < 5 &&\n\t\t\tfabs(AngleDiff(result.ideal_viewangles[0], ms->viewangles[0])) < 5 &&\n\t\t\tfabs(AngleDiff(result.ideal_viewangles[1], ms->viewangles[1])) < 5)\n\t{\n\t\t//botimport.Print(PRT_MESSAGE, \"between jump start and run start point\\n\");\n\t\thordir[0] = reach->end[0] - ms->origin[0];\n\t\thordir[1] = reach->end[1] - ms->origin[1];\n\t\thordir[2] = 0;\n\t\tVectorNormalize(hordir);\n\t\t//elemantary action jump\n\t\tEA_Jump(ms->client);\n\t\tEA_Attack(ms->client);\n\t\tEA_Move(ms->client, hordir, 800);\n\t\t//\n\t\tms->jumpreach = ms->lastreachnum;\n\t} //end if\n\telse\n\t{\n\t\tif (dist > 80) dist = 80;\n\t\tspeed = 400 - (400 - 5 * dist);\n\t\tEA_Move(ms->client, hordir, speed);\n\t} //end else\n\t//look in the movement direction\n\tVector2Angles(hordir, result.ideal_viewangles);\n\t//look straight down\n\tresult.ideal_viewangles[PITCH] = 90;\n\t//set the view angles directly\n\tEA_View(ms->client, result.ideal_viewangles);\n\t//view is important for the movment\n\tresult.flags |= MOVERESULT_MOVEMENTVIEWSET;\n\t//select the rocket launcher\n\tEA_SelectWeapon(ms->client, (int) weapindex_bfg10k->value);\n\t//weapon is used for movement\n\tresult.weapon = (int) weapindex_bfg10k->value;\n\tresult.flags |= MOVERESULT_MOVEMENTWEAPON;\n\t//\n\tVectorCopy(hordir, result.movedir);\n\t//\n\treturn result;\n} //end of the function BotTravel_BFGJump\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nbot_moveresult_t BotFinishTravel_WeaponJump(bot_movestate_t *ms, aas_reachability_t *reach)\n{\n\tvec3_t hordir;\n\tfloat speed;\n\tbot_moveresult_t_cleared( result );\n\n\t//if not jumped yet\n\tif (!ms->jumpreach) return result;\n\t/*\n\t//go straight to the reachability end\n\thordir[0] = reach->end[0] - ms->origin[0];\n\thordir[1] = reach->end[1] - ms->origin[1];\n\thordir[2] = 0;\n\tVectorNormalize(hordir);\n\t//always use max speed when traveling through the air\n\tEA_Move(ms->client, hordir, 800);\n\tVectorCopy(hordir, result.movedir);\n\t*/\n\t//\n\tif (!BotAirControl(ms->origin, ms->velocity, reach->end, hordir, &speed))\n\t{\n\t\t//go straight to the reachability end\n\t\tVectorSubtract(reach->end, ms->origin, hordir);\n\t\thordir[2] = 0;\n\t\tVectorNormalize(hordir);\n\t\tspeed = 400;\n\t} //end if\n\t//\n\tEA_Move(ms->client, hordir, speed);\n\tVectorCopy(hordir, result.movedir);\n\t//\n\treturn result;\n} //end of the function BotFinishTravel_WeaponJump\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nbot_moveresult_t BotTravel_JumpPad(bot_movestate_t *ms, aas_reachability_t *reach)\n{\n\tfloat speed;\n\tvec3_t hordir;\n\tbot_moveresult_t_cleared( result );\n\n\t//first walk straight to the reachability start\n\thordir[0] = reach->start[0] - ms->origin[0];\n\thordir[1] = reach->start[1] - ms->origin[1];\n\thordir[2] = 0;\n\tVectorNormalize(hordir);\n\t//\n\tBotCheckBlocked(ms, hordir, qtrue, &result);\n\tspeed = 400;\n\t//elemantary action move in direction\n\tEA_Move(ms->client, hordir, speed);\n\tVectorCopy(hordir, result.movedir);\n\t//\n\treturn result;\n} //end of the function BotTravel_JumpPad\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nbot_moveresult_t BotFinishTravel_JumpPad(bot_movestate_t *ms, aas_reachability_t *reach)\n{\n\tfloat speed;\n\tvec3_t hordir;\n\tbot_moveresult_t_cleared( result );\n\n\tif (!BotAirControl(ms->origin, ms->velocity, reach->end, hordir, &speed))\n\t{\n\t\thordir[0] = reach->end[0] - ms->origin[0];\n\t\thordir[1] = reach->end[1] - ms->origin[1];\n\t\thordir[2] = 0;\n\t\tVectorNormalize(hordir);\n\t\tspeed = 400;\n\t} //end if\n\tBotCheckBlocked(ms, hordir, qtrue, &result);\n\t//elemantary action move in direction\n\tEA_Move(ms->client, hordir, speed);\n\tVectorCopy(hordir, result.movedir);\n\t//\n\treturn result;\n} //end of the function BotFinishTravel_JumpPad\n//===========================================================================\n// time before the reachability times out\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint BotReachabilityTime(aas_reachability_t *reach)\n{\n\tswitch(reach->traveltype & TRAVELTYPE_MASK)\n\t{\n\t\tcase TRAVEL_WALK: return 5;\n\t\tcase TRAVEL_CROUCH: return 5;\n\t\tcase TRAVEL_BARRIERJUMP: return 5;\n\t\tcase TRAVEL_LADDER: return 6;\n\t\tcase TRAVEL_WALKOFFLEDGE: return 5;\n\t\tcase TRAVEL_JUMP: return 5;\n\t\tcase TRAVEL_SWIM: return 5;\n\t\tcase TRAVEL_WATERJUMP: return 5;\n\t\tcase TRAVEL_TELEPORT: return 5;\n\t\tcase TRAVEL_ELEVATOR: return 10;\n\t\tcase TRAVEL_GRAPPLEHOOK: return 8;\n\t\tcase TRAVEL_ROCKETJUMP: return 6;\n\t\tcase TRAVEL_BFGJUMP: return 6;\n\t\tcase TRAVEL_JUMPPAD: return 10;\n\t\tcase TRAVEL_FUNCBOB: return 10;\n\t\tdefault:\n\t\t{\n\t\t\tbotimport.Print(PRT_ERROR, \"travel type %d not implemented yet\\n\", reach->traveltype);\n\t\t\treturn 8;\n\t\t} //end case\n\t} //end switch\n} //end of the function BotReachabilityTime\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nbot_moveresult_t BotMoveInGoalArea(bot_movestate_t *ms, bot_goal_t *goal)\n{\n\tbot_moveresult_t_cleared( result );\n\tvec3_t dir;\n\tfloat dist, speed;\n\n#ifdef DEBUG\n\t//botimport.Print(PRT_MESSAGE, \"%s: moving straight to goal\\n\", ClientName(ms->entitynum-1));\n\t//AAS_ClearShownDebugLines();\n\t//AAS_DebugLine(ms->origin, goal->origin, LINECOLOR_RED);\n#endif //DEBUG\n\t//walk straight to the goal origin\n\tdir[0] = goal->origin[0] - ms->origin[0];\n\tdir[1] = goal->origin[1] - ms->origin[1];\n\tif (ms->moveflags & MFL_SWIMMING)\n\t{\n\t\tdir[2] = goal->origin[2] - ms->origin[2];\n\t\tresult.traveltype = TRAVEL_SWIM;\n\t} //end if\n\telse\n\t{\n\t\tdir[2] = 0;\n\t\tresult.traveltype = TRAVEL_WALK;\n\t} //endif\n\t//\n\tdist = VectorNormalize(dir);\n\tif (dist > 100) dist = 100;\n\tspeed = 400 - (400 - 4 * dist);\n\tif (speed < 10) speed = 0;\n\t//\n\tBotCheckBlocked(ms, dir, qtrue, &result);\n\t//elemantary action move in direction\n\tEA_Move(ms->client, dir, speed);\n\tVectorCopy(dir, result.movedir);\n\t//\n\tif (ms->moveflags & MFL_SWIMMING)\n\t{\n\t\tVector2Angles(dir, result.ideal_viewangles);\n\t\tresult.flags |= MOVERESULT_SWIMVIEW;\n\t} //end if\n\t//if (!debugline) debugline = botimport.DebugLineCreate();\n\t//botimport.DebugLineShow(debugline, ms->origin, goal->origin, LINECOLOR_BLUE);\n\t//\n\tms->lastreachnum = 0;\n\tms->lastareanum = 0;\n\tms->lastgoalareanum = goal->areanum;\n\tVectorCopy(ms->origin, ms->lastorigin);\n\t//\n\treturn result;\n} //end of the function BotMoveInGoalArea\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid BotMoveToGoal(bot_moveresult_t *result, int movestate, bot_goal_t *goal, int travelflags)\n{\n\tint reachnum, lastreachnum, foundjumppad, ent, resultflags;\n\taas_reachability_t reach, lastreach;\n\tbot_movestate_t *ms;\n\t//vec3_t mins, maxs, up = {0, 0, 1};\n\t//bsp_trace_t trace;\n\t//static int debugline;\n\n\tresult->failure = qfalse;\n\tresult->type = 0;\n\tresult->blocked = qfalse;\n\tresult->blockentity = 0;\n\tresult->traveltype = 0;\n\tresult->flags = 0;\n\n\t//\n\tms = BotMoveStateFromHandle(movestate);\n\tif (!ms) return;\n\t//reset the grapple before testing if the bot has a valid goal\n\t//because the bot could lose all its goals when stuck to a wall\n\tBotResetGrapple(ms);\n\t//\n\tif (!goal)\n\t{\n#ifdef DEBUG\n\t\tbotimport.Print(PRT_MESSAGE, \"client %d: movetogoal -> no goal\\n\", ms->client);\n#endif //DEBUG\n\t\tresult->failure = qtrue;\n\t\treturn;\n\t} //end if\n\t//botimport.Print(PRT_MESSAGE, \"numavoidreach = %d\\n\", ms->numavoidreach);\n\t//remove some of the move flags\n\tms->moveflags &= ~(MFL_SWIMMING|MFL_AGAINSTLADDER);\n\t//set some of the move flags\n\t//NOTE: the MFL_ONGROUND flag is also set in the higher AI\n\tif (AAS_OnGround(ms->origin, ms->presencetype, ms->entitynum)) ms->moveflags |= MFL_ONGROUND;\n\t//\n\tif (ms->moveflags & MFL_ONGROUND)\n\t{\n\t\tint modeltype, modelnum;\n\n\t\tent = BotOnTopOfEntity(ms);\n\n\t\tif (ent != -1)\n\t\t{\n\t\t\tmodelnum = AAS_EntityModelindex(ent);\n\t\t\tif (modelnum >= 0 && modelnum < MAX_MODELS)\n\t\t\t{\n\t\t\t\tmodeltype = modeltypes[modelnum];\n\n\t\t\t\tif (modeltype == MODELTYPE_FUNC_PLAT)\n\t\t\t\t{\n\t\t\t\t\tAAS_ReachabilityFromNum(ms->lastreachnum, &reach);\n\t\t\t\t\t//if the bot is Not using the elevator\n\t\t\t\t\tif ((reach.traveltype & TRAVELTYPE_MASK) != TRAVEL_ELEVATOR ||\n\t\t\t\t\t\t//NOTE: the face number is the plat model number\n\t\t\t\t\t\t(reach.facenum & 0x0000FFFF) != modelnum)\n\t\t\t\t\t{\n\t\t\t\t\t\treachnum = AAS_NextModelReachability(0, modelnum);\n\t\t\t\t\t\tif (reachnum)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t//botimport.Print(PRT_MESSAGE, \"client %d: accidentally ended up on func_plat\\n\", ms->client);\n\t\t\t\t\t\t\tAAS_ReachabilityFromNum(reachnum, &reach);\n\t\t\t\t\t\t\tms->lastreachnum = reachnum;\n\t\t\t\t\t\t\tms->reachability_time = AAS_Time() + BotReachabilityTime(&reach);\n\t\t\t\t\t\t} //end if\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (botDeveloper)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tbotimport.Print(PRT_MESSAGE, \"client %d: on func_plat without reachability\\n\", ms->client);\n\t\t\t\t\t\t\t} //end if\n\t\t\t\t\t\t\tresult->blocked = qtrue;\n\t\t\t\t\t\t\tresult->blockentity = ent;\n\t\t\t\t\t\t\tresult->flags |= MOVERESULT_ONTOPOFOBSTACLE;\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t} //end else\n\t\t\t\t\t} //end if\n\t\t\t\t\tresult->flags |= MOVERESULT_ONTOPOF_ELEVATOR;\n\t\t\t\t} //end if\n\t\t\t\telse if (modeltype == MODELTYPE_FUNC_BOB)\n\t\t\t\t{\n\t\t\t\t\tAAS_ReachabilityFromNum(ms->lastreachnum, &reach);\n\t\t\t\t\t//if the bot is Not using the func bobbing\n\t\t\t\t\tif ((reach.traveltype & TRAVELTYPE_MASK) != TRAVEL_FUNCBOB ||\n\t\t\t\t\t\t//NOTE: the face number is the func_bobbing model number\n\t\t\t\t\t\t(reach.facenum & 0x0000FFFF) != modelnum)\n\t\t\t\t\t{\n\t\t\t\t\t\treachnum = AAS_NextModelReachability(0, modelnum);\n\t\t\t\t\t\tif (reachnum)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t//botimport.Print(PRT_MESSAGE, \"client %d: accidentally ended up on func_bobbing\\n\", ms->client);\n\t\t\t\t\t\t\tAAS_ReachabilityFromNum(reachnum, &reach);\n\t\t\t\t\t\t\tms->lastreachnum = reachnum;\n\t\t\t\t\t\t\tms->reachability_time = AAS_Time() + BotReachabilityTime(&reach);\n\t\t\t\t\t\t} //end if\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (botDeveloper)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tbotimport.Print(PRT_MESSAGE, \"client %d: on func_bobbing without reachability\\n\", ms->client);\n\t\t\t\t\t\t\t} //end if\n\t\t\t\t\t\t\tresult->blocked = qtrue;\n\t\t\t\t\t\t\tresult->blockentity = ent;\n\t\t\t\t\t\t\tresult->flags |= MOVERESULT_ONTOPOFOBSTACLE;\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t} //end else\n\t\t\t\t\t} //end if\n\t\t\t\t\tresult->flags |= MOVERESULT_ONTOPOF_FUNCBOB;\n\t\t\t\t} //end if\n\t\t\t\telse if (modeltype == MODELTYPE_FUNC_STATIC || modeltype == MODELTYPE_FUNC_DOOR)\n\t\t\t\t{\n\t\t\t\t\t// check if ontop of a door bridge ?\n\t\t\t\t\tms->areanum = BotFuzzyPointReachabilityArea(ms->origin);\n\t\t\t\t\t// if not in a reachability area\n\t\t\t\t\tif (!AAS_AreaReachability(ms->areanum))\n\t\t\t\t\t{\n\t\t\t\t\t\tresult->blocked = qtrue;\n\t\t\t\t\t\tresult->blockentity = ent;\n\t\t\t\t\t\tresult->flags |= MOVERESULT_ONTOPOFOBSTACLE;\n\t\t\t\t\t\treturn;\n\t\t\t\t\t} //end if\n\t\t\t\t} //end else if\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tresult->blocked = qtrue;\n\t\t\t\t\tresult->blockentity = ent;\n\t\t\t\t\tresult->flags |= MOVERESULT_ONTOPOFOBSTACLE;\n\t\t\t\t\treturn;\n\t\t\t\t} //end else\n\t\t\t} //end if\n\t\t} //end if\n\t} //end if\n\t//if swimming\n\tif (AAS_Swimming(ms->origin)) ms->moveflags |= MFL_SWIMMING;\n\t//if against a ladder\n\tif (AAS_AgainstLadder(ms->origin)) ms->moveflags |= MFL_AGAINSTLADDER;\n\t//if the bot is on the ground, swimming or against a ladder\n\tif (ms->moveflags & (MFL_ONGROUND|MFL_SWIMMING|MFL_AGAINSTLADDER))\n\t{\n\t\t//botimport.Print(PRT_MESSAGE, \"%s: onground, swimming or against ladder\\n\", ClientName(ms->entitynum-1));\n\t\t//\n\t\tAAS_ReachabilityFromNum(ms->lastreachnum, &lastreach);\n\t\t//reachability area the bot is in\n\t\t//ms->areanum = BotReachabilityArea(ms->origin, ((lastreach.traveltype & TRAVELTYPE_MASK) != TRAVEL_ELEVATOR));\n\t\tms->areanum = BotFuzzyPointReachabilityArea(ms->origin);\n\t\t//\n\t\tif ( !ms->areanum )\n\t\t{\n\t\t\tresult->failure = qtrue;\n\t\t\tresult->blocked = qtrue;\n\t\t\tresult->blockentity = 0;\n\t\t\tresult->type = RESULTTYPE_INSOLIDAREA;\n\t\t\treturn;\n\t\t} //end if\n\t\t//if the bot is in the goal area\n\t\tif (ms->areanum == goal->areanum)\n\t\t{\n\t\t\t*result = BotMoveInGoalArea(ms, goal);\n\t\t\treturn;\n\t\t} //end if\n\t\t//assume we can use the reachability from the last frame\n\t\treachnum = ms->lastreachnum;\n\t\t//if there is a last reachability\n\t\tif (reachnum)\n\t\t{\n\t\t\tAAS_ReachabilityFromNum(reachnum, &reach);\n\t\t\t//check if the reachability is still valid\n\t\t\tif (!(AAS_TravelFlagForType(reach.traveltype) & travelflags))\n\t\t\t{\n\t\t\t\treachnum = 0;\n\t\t\t} //end if\n\t\t\t//special grapple hook case\n\t\t\telse if ((reach.traveltype & TRAVELTYPE_MASK) == TRAVEL_GRAPPLEHOOK)\n\t\t\t{\n\t\t\t\tif (ms->reachability_time < AAS_Time() ||\n\t\t\t\t\t(ms->moveflags & MFL_GRAPPLERESET))\n\t\t\t\t{\n\t\t\t\t\treachnum = 0;\n\t\t\t\t} //end if\n\t\t\t} //end if\n\t\t\t//special elevator case\n\t\t\telse if ((reach.traveltype & TRAVELTYPE_MASK) == TRAVEL_ELEVATOR ||\n\t\t\t\t(reach.traveltype & TRAVELTYPE_MASK) == TRAVEL_FUNCBOB)\n\t\t\t{\n\t\t\t\tif ((result->flags & MOVERESULT_ONTOPOF_FUNCBOB) ||\n\t\t\t\t\t(result->flags & MOVERESULT_ONTOPOF_FUNCBOB))\n\t\t\t\t{\n\t\t\t\t\tms->reachability_time = AAS_Time() + 5;\n\t\t\t\t} //end if\n\t\t\t\t//if the bot was going for an elevator and reached the reachability area\n\t\t\t\tif (ms->areanum == reach.areanum ||\n\t\t\t\t\tms->reachability_time < AAS_Time())\n\t\t\t\t{\n\t\t\t\t\treachnum = 0;\n\t\t\t\t} //end if\n\t\t\t} //end if\n\t\t\telse\n\t\t\t{\n#ifdef DEBUG\n\t\t\t\tif (botDeveloper)\n\t\t\t\t{\n\t\t\t\t\tif (ms->reachability_time < AAS_Time())\n\t\t\t\t\t{\n\t\t\t\t\t\tbotimport.Print(PRT_MESSAGE, \"client %d: reachability timeout in \", ms->client);\n\t\t\t\t\t\tAAS_PrintTravelType(reach.traveltype & TRAVELTYPE_MASK);\n\t\t\t\t\t\tbotimport.Print(PRT_MESSAGE, \"\\n\");\n\t\t\t\t\t} //end if\n\t\t\t\t\t/*\n\t\t\t\t\tif (ms->lastareanum != ms->areanum)\n\t\t\t\t\t{\n\t\t\t\t\t\tbotimport.Print(PRT_MESSAGE, \"changed from area %d to %d\\n\", ms->lastareanum, ms->areanum);\n\t\t\t\t\t} //end if*/\n\t\t\t\t} //end if\n#endif //DEBUG\n\t\t\t\t//if the goal area changed or the reachability timed out\n\t\t\t\t//or the area changed\n\t\t\t\tif (ms->lastgoalareanum != goal->areanum ||\n\t\t\t\t\t\tms->reachability_time < AAS_Time() ||\n\t\t\t\t\t\tms->lastareanum != ms->areanum)\n\t\t\t\t{\n\t\t\t\t\treachnum = 0;\n\t\t\t\t\t//botimport.Print(PRT_MESSAGE, \"area change or timeout\\n\");\n\t\t\t\t} //end else if\n\t\t\t} //end else\n\t\t} //end if\n\t\tresultflags = 0;\n\t\t//if the bot needs a new reachability\n\t\tif (!reachnum)\n\t\t{\n\t\t\t//if the area has no reachability links\n\t\t\tif (!AAS_AreaReachability(ms->areanum))\n\t\t\t{\n#ifdef DEBUG\n\t\t\t\tif (botDeveloper)\n\t\t\t\t{\n\t\t\t\t\tbotimport.Print(PRT_MESSAGE, \"area %d no reachability\\n\", ms->areanum);\n\t\t\t\t} //end if\n#endif //DEBUG\n\t\t\t} //end if\n\t\t\t//get a new reachability leading towards the goal\n\t\t\treachnum = BotGetReachabilityToGoal(ms->origin, ms->areanum,\n\t\t\t\t\t\t\t\tms->lastgoalareanum, ms->lastareanum,\n\t\t\t\t\t\t\t\t\t\t\tms->avoidreach, ms->avoidreachtimes, ms->avoidreachtries,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tgoal, travelflags, travelflags,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tms->avoidspots, ms->numavoidspots, &resultflags);\n\t\t\t//the area number the reachability starts in\n\t\t\tms->reachareanum = ms->areanum;\n\t\t\t//reset some state variables\n\t\t\tms->jumpreach = 0;\t\t\t\t\t\t//for TRAVEL_JUMP\n\t\t\tms->moveflags &= ~MFL_GRAPPLERESET;\t//for TRAVEL_GRAPPLEHOOK\n\t\t\t//if there is a reachability to the goal\n\t\t\tif (reachnum)\n\t\t\t{\n\t\t\t\tAAS_ReachabilityFromNum(reachnum, &reach);\n\t\t\t\t//set a timeout for this reachability\n\t\t\t\tms->reachability_time = AAS_Time() + BotReachabilityTime(&reach);\n\t\t\t\t//\n#ifdef AVOIDREACH\n\t\t\t\t//add the reachability to the reachabilities to avoid for a while\n\t\t\t\tBotAddToAvoidReach(ms, reachnum, AVOIDREACH_TIME);\n#endif //AVOIDREACH\n\t\t\t} //end if\n#ifdef DEBUG\n\t\t\t\n\t\t\telse if (botDeveloper)\n\t\t\t{\n\t\t\t\tbotimport.Print(PRT_MESSAGE, \"goal not reachable\\n\");\n\t\t\t\tCom_Memset(&reach, 0, sizeof(aas_reachability_t)); //make compiler happy\n\t\t\t} //end else\n\t\t\tif (botDeveloper)\n\t\t\t{\n\t\t\t\t//if still going for the same goal\n\t\t\t\tif (ms->lastgoalareanum == goal->areanum)\n\t\t\t\t{\n\t\t\t\t\tif (ms->lastareanum == reach.areanum)\n\t\t\t\t\t{\n\t\t\t\t\t\tbotimport.Print(PRT_MESSAGE, \"same goal, going back to previous area\\n\");\n\t\t\t\t\t} //end if\n\t\t\t\t} //end if\n\t\t\t} //end if\n#endif //DEBUG\n\t\t} //end else\n\t\t//\n\t\tms->lastreachnum = reachnum;\n\t\tms->lastgoalareanum = goal->areanum;\n\t\tms->lastareanum = ms->areanum;\n\t\t//if the bot has a reachability\n\t\tif (reachnum)\n\t\t{\n\t\t\t//get the reachability from the number\n\t\t\tAAS_ReachabilityFromNum(reachnum, &reach);\n\t\t\tresult->traveltype = reach.traveltype;\n\t\t\t//\n#ifdef DEBUG_AI_MOVE\n\t\t\tAAS_ClearShownDebugLines();\n\t\t\tAAS_PrintTravelType(reach.traveltype & TRAVELTYPE_MASK);\n\t\t\tAAS_ShowReachability(&reach);\n#endif //DEBUG_AI_MOVE\n\t\t\t//\n#ifdef DEBUG\n\t\t\t//botimport.Print(PRT_MESSAGE, \"client %d: \", ms->client);\n\t\t\t//AAS_PrintTravelType(reach.traveltype);\n\t\t\t//botimport.Print(PRT_MESSAGE, \"\\n\");\n#endif //DEBUG\n\t\t\tswitch(reach.traveltype & TRAVELTYPE_MASK)\n\t\t\t{\n\t\t\t\tcase TRAVEL_WALK: *result = BotTravel_Walk(ms, &reach); break;\n\t\t\t\tcase TRAVEL_CROUCH: *result = BotTravel_Crouch(ms, &reach); break;\n\t\t\t\tcase TRAVEL_BARRIERJUMP: *result = BotTravel_BarrierJump(ms, &reach); break;\n\t\t\t\tcase TRAVEL_LADDER: *result = BotTravel_Ladder(ms, &reach); break;\n\t\t\t\tcase TRAVEL_WALKOFFLEDGE: *result = BotTravel_WalkOffLedge(ms, &reach); break;\n\t\t\t\tcase TRAVEL_JUMP: *result = BotTravel_Jump(ms, &reach); break;\n\t\t\t\tcase TRAVEL_SWIM: *result = BotTravel_Swim(ms, &reach); break;\n\t\t\t\tcase TRAVEL_WATERJUMP: *result = BotTravel_WaterJump(ms, &reach); break;\n\t\t\t\tcase TRAVEL_TELEPORT: *result = BotTravel_Teleport(ms, &reach); break;\n\t\t\t\tcase TRAVEL_ELEVATOR: *result = BotTravel_Elevator(ms, &reach); break;\n\t\t\t\tcase TRAVEL_GRAPPLEHOOK: *result = BotTravel_Grapple(ms, &reach); break;\n\t\t\t\tcase TRAVEL_ROCKETJUMP: *result = BotTravel_RocketJump(ms, &reach); break;\n\t\t\t\tcase TRAVEL_BFGJUMP: *result = BotTravel_BFGJump(ms, &reach); break;\n\t\t\t\tcase TRAVEL_JUMPPAD: *result = BotTravel_JumpPad(ms, &reach); break;\n\t\t\t\tcase TRAVEL_FUNCBOB: *result = BotTravel_FuncBobbing(ms, &reach); break;\n\t\t\t\tdefault:\n\t\t\t\t{\n\t\t\t\t\tbotimport.Print(PRT_FATAL, \"travel type %d not implemented yet\\n\", (reach.traveltype & TRAVELTYPE_MASK));\n\t\t\t\t\tbreak;\n\t\t\t\t} //end case\n\t\t\t} //end switch\n\t\t\tresult->traveltype = reach.traveltype;\n\t\t\tresult->flags |= resultflags;\n\t\t} //end if\n\t\telse\n\t\t{\n\t\t\tresult->failure = qtrue;\n\t\t\tresult->flags |= resultflags;\n\t\t\tCom_Memset(&reach, 0, sizeof(aas_reachability_t));\n\t\t} //end else\n#ifdef DEBUG\n\t\tif (botDeveloper)\n\t\t{\n\t\t\tif (result->failure)\n\t\t\t{\n\t\t\t\tbotimport.Print(PRT_MESSAGE, \"client %d: movement failure in \", ms->client);\n\t\t\t\tAAS_PrintTravelType(reach.traveltype & TRAVELTYPE_MASK);\n\t\t\t\tbotimport.Print(PRT_MESSAGE, \"\\n\");\n\t\t\t} //end if\n\t\t} //end if\n#endif //DEBUG\n\t} //end if\n\telse\n\t{\n\t\tint i, numareas, areas[16];\n\t\tvec3_t end;\n\n\t\t//special handling of jump pads when the bot uses a jump pad without knowing it\n\t\tfoundjumppad = qfalse;\n\t\tVectorMA(ms->origin, -2 * ms->thinktime, ms->velocity, end);\n\t\tnumareas = AAS_TraceAreas(ms->origin, end, areas, NULL, 16);\n\t\tfor (i = numareas-1; i >= 0; i--)\n\t\t{\n\t\t\tif (AAS_AreaJumpPad(areas[i]))\n\t\t\t{\n\t\t\t\t//botimport.Print(PRT_MESSAGE, \"client %d used a jumppad without knowing, area %d\\n\", ms->client, areas[i]);\n\t\t\t\tfoundjumppad = qtrue;\n\t\t\t\tlastreachnum = BotGetReachabilityToGoal(end, areas[i],\n\t\t\t\t\t\t\tms->lastgoalareanum, ms->lastareanum,\n\t\t\t\t\t\t\tms->avoidreach, ms->avoidreachtimes, ms->avoidreachtries,\n\t\t\t\t\t\t\tgoal, travelflags, TFL_JUMPPAD, ms->avoidspots, ms->numavoidspots, NULL);\n\t\t\t\tif (lastreachnum)\n\t\t\t\t{\n\t\t\t\t\tms->lastreachnum = lastreachnum;\n\t\t\t\t\tms->lastareanum = areas[i];\n\t\t\t\t\t//botimport.Print(PRT_MESSAGE, \"found jumppad reachability\\n\");\n\t\t\t\t\tbreak;\n\t\t\t\t} //end if\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tfor (lastreachnum = AAS_NextAreaReachability(areas[i], 0); lastreachnum;\n\t\t\t\t\t\tlastreachnum = AAS_NextAreaReachability(areas[i], lastreachnum))\n\t\t\t\t\t{\n\t\t\t\t\t\t//get the reachability from the number\n\t\t\t\t\t\tAAS_ReachabilityFromNum(lastreachnum, &reach);\n\t\t\t\t\t\tif ((reach.traveltype & TRAVELTYPE_MASK) == TRAVEL_JUMPPAD)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tms->lastreachnum = lastreachnum;\n\t\t\t\t\t\t\tms->lastareanum = areas[i];\n\t\t\t\t\t\t\t//botimport.Print(PRT_MESSAGE, \"found jumppad reachability hard!!\\n\");\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t} //end if\n\t\t\t\t\t} //end for\n\t\t\t\t\tif (lastreachnum) break;\n\t\t\t\t} //end else\n\t\t\t} //end if\n\t\t} //end for\n\t\tif (botDeveloper)\n\t\t{\n\t\t\t//if a jumppad is found with the trace but no reachability is found\n\t\t\tif (foundjumppad && !ms->lastreachnum)\n\t\t\t{\n\t\t\t\tbotimport.Print(PRT_MESSAGE, \"client %d didn't find jumppad reachability\\n\", ms->client);\n\t\t\t} //end if\n\t\t} //end if\n\t\t//\n\t\tif (ms->lastreachnum)\n\t\t{\n\t\t\t//botimport.Print(PRT_MESSAGE, \"%s: NOT onground, swimming or against ladder\\n\", ClientName(ms->entitynum-1));\n\t\t\tAAS_ReachabilityFromNum(ms->lastreachnum, &reach);\n\t\t\tresult->traveltype = reach.traveltype;\n#ifdef DEBUG\n\t\t\t//botimport.Print(PRT_MESSAGE, \"client %d finish: \", ms->client);\n\t\t\t//AAS_PrintTravelType(reach.traveltype & TRAVELTYPE_MASK);\n\t\t\t//botimport.Print(PRT_MESSAGE, \"\\n\");\n#endif //DEBUG\n\t\t\t//\n\t\t\tswitch(reach.traveltype & TRAVELTYPE_MASK)\n\t\t\t{\n\t\t\t\tcase TRAVEL_WALK: *result = BotTravel_Walk(ms, &reach); break;//BotFinishTravel_Walk(ms, &reach); break;\n\t\t\t\tcase TRAVEL_CROUCH: /*do nothing*/ break;\n\t\t\t\tcase TRAVEL_BARRIERJUMP: *result = BotFinishTravel_BarrierJump(ms, &reach); break;\n\t\t\t\tcase TRAVEL_LADDER: *result = BotTravel_Ladder(ms, &reach); break;\n\t\t\t\tcase TRAVEL_WALKOFFLEDGE: *result = BotFinishTravel_WalkOffLedge(ms, &reach); break;\n\t\t\t\tcase TRAVEL_JUMP: *result = BotFinishTravel_Jump(ms, &reach); break;\n\t\t\t\tcase TRAVEL_SWIM: *result = BotTravel_Swim(ms, &reach); break;\n\t\t\t\tcase TRAVEL_WATERJUMP: *result = BotFinishTravel_WaterJump(ms, &reach); break;\n\t\t\t\tcase TRAVEL_TELEPORT: /*do nothing*/ break;\n\t\t\t\tcase TRAVEL_ELEVATOR: *result = BotFinishTravel_Elevator(ms, &reach); break;\n\t\t\t\tcase TRAVEL_GRAPPLEHOOK: *result = BotTravel_Grapple(ms, &reach); break;\n\t\t\t\tcase TRAVEL_ROCKETJUMP:\n\t\t\t\tcase TRAVEL_BFGJUMP: *result = BotFinishTravel_WeaponJump(ms, &reach); break;\n\t\t\t\tcase TRAVEL_JUMPPAD: *result = BotFinishTravel_JumpPad(ms, &reach); break;\n\t\t\t\tcase TRAVEL_FUNCBOB: *result = BotFinishTravel_FuncBobbing(ms, &reach); break;\n\t\t\t\tdefault:\n\t\t\t\t{\n\t\t\t\t\tbotimport.Print(PRT_FATAL, \"(last) travel type %d not implemented yet\\n\", (reach.traveltype & TRAVELTYPE_MASK));\n\t\t\t\t\tbreak;\n\t\t\t\t} //end case\n\t\t\t} //end switch\n\t\t\tresult->traveltype = reach.traveltype;\n#ifdef DEBUG\n\t\t\tif (botDeveloper)\n\t\t\t{\n\t\t\t\tif (result->failure)\n\t\t\t\t{\n\t\t\t\t\tbotimport.Print(PRT_MESSAGE, \"client %d: movement failure in finish \", ms->client);\n\t\t\t\t\tAAS_PrintTravelType(reach.traveltype & TRAVELTYPE_MASK);\n\t\t\t\t\tbotimport.Print(PRT_MESSAGE, \"\\n\");\n\t\t\t\t} //end if\n\t\t\t} //end if\n#endif //DEBUG\n\t\t} //end if\n\t} //end else\n\t//FIXME: is it right to do this here?\n\tif (result->blocked) ms->reachability_time -= 10 * ms->thinktime;\n\t//copy the last origin\n\tVectorCopy(ms->origin, ms->lastorigin);\n\t//return the movement result\n\treturn;\n} //end of the function BotMoveToGoal\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid BotResetAvoidReach(int movestate)\n{\n\tbot_movestate_t *ms;\n\n\tms = BotMoveStateFromHandle(movestate);\n\tif (!ms) return;\n\tCom_Memset(ms->avoidreach, 0, MAX_AVOIDREACH * sizeof(int));\n\tCom_Memset(ms->avoidreachtimes, 0, MAX_AVOIDREACH * sizeof(float));\n\tCom_Memset(ms->avoidreachtries, 0, MAX_AVOIDREACH * sizeof(int));\n} //end of the function BotResetAvoidReach\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid BotResetLastAvoidReach(int movestate)\n{\n\tint i, latest;\n\tfloat latesttime;\n\tbot_movestate_t *ms;\n\n\tms = BotMoveStateFromHandle(movestate);\n\tif (!ms) return;\n\tlatesttime = 0;\n\tlatest = 0;\n\tfor (i = 0; i < MAX_AVOIDREACH; i++)\n\t{\n\t\tif (ms->avoidreachtimes[i] > latesttime)\n\t\t{\n\t\t\tlatesttime = ms->avoidreachtimes[i];\n\t\t\tlatest = i;\n\t\t} //end if\n\t} //end for\n\tif (latesttime)\n\t{\n\t\tms->avoidreachtimes[latest] = 0;\n\t\tif (ms->avoidreachtries[latest] > 0) ms->avoidreachtries[latest]--;\n\t} //end if\n} //end of the function BotResetLastAvoidReach\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid BotResetMoveState(int movestate)\n{\n\tbot_movestate_t *ms;\n\n\tms = BotMoveStateFromHandle(movestate);\n\tif (!ms) return;\n\tCom_Memset(ms, 0, sizeof(bot_movestate_t));\n} //end of the function BotResetMoveState\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint BotSetupMoveAI(void)\n{\n\tBotSetBrushModelTypes();\n\tsv_maxstep = LibVar(\"sv_step\", \"18\");\n\tsv_maxbarrier = LibVar(\"sv_maxbarrier\", \"32\");\n\tblsv_gravity = LibVar(\"sv_gravity\", \"800\");\n\tweapindex_rocketlauncher = LibVar(\"weapindex_rocketlauncher\", \"5\");\n\tweapindex_bfg10k = LibVar(\"weapindex_bfg10k\", \"9\");\n\tweapindex_grapple = LibVar(\"weapindex_grapple\", \"10\");\n\tentitytypemissile = LibVar(\"entitytypemissile\", \"3\");\n\toffhandgrapple = LibVar(\"offhandgrapple\", \"0\");\n\tcmd_grappleon = LibVar(\"cmd_grappleon\", \"grappleon\");\n\tcmd_grappleoff = LibVar(\"cmd_grappleoff\", \"grappleoff\");\n\treturn BLERR_NOERROR;\n} //end of the function BotSetupMoveAI\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid BotShutdownMoveAI(void)\n{\n\tint i;\n\n\tfor (i = 1; i <= MAX_CLIENTS; i++)\n\t{\n\t\tif (botmovestates[i])\n\t\t{\n\t\t\tFreeMemory(botmovestates[i]);\n\t\t\tbotmovestates[i] = NULL;\n\t\t} //end if\n\t} //end for\n} //end of the function BotShutdownMoveAI\n\n\n"
  },
  {
    "path": "plugins/quake3/botlib/be_ai_move.h",
    "content": "/*\n===========================================================================\nCopyright (C) 1999-2005 Id Software, Inc.\n\nThis file is part of Quake III Arena source code.\n\nQuake III Arena source code is free software; you can redistribute it\nand/or modify it under the terms of the GNU General Public License as\npublished by the Free Software Foundation; either version 2 of the License,\nor (at your option) any later version.\n\nQuake III Arena source code is distributed in the hope that it will be\nuseful, but WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Quake III Arena source code; if not, write to the Free Software\nFoundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n===========================================================================\n*/\n//\n\n/*****************************************************************************\n * name:\t\tbe_ai_move.h\n *\n * desc:\t\tmovement AI\n *\n * $Archive: /source/code/botlib/be_ai_move.h $\n *\n *****************************************************************************/\n\n//movement types\n#define MOVE_WALK\t\t\t\t\t\t1\n#define MOVE_CROUCH\t\t\t\t\t\t2\n#define MOVE_JUMP\t\t\t\t\t\t4\n#define MOVE_GRAPPLE\t\t\t\t\t8\n#define MOVE_ROCKETJUMP\t\t\t\t\t16\n#define MOVE_BFGJUMP\t\t\t\t\t32\n//move flags\n#define MFL_BARRIERJUMP\t\t\t\t\t1\t\t//bot is performing a barrier jump\n#define MFL_ONGROUND\t\t\t\t\t2\t\t//bot is in the ground\n#define MFL_SWIMMING\t\t\t\t\t4\t\t//bot is swimming\n#define MFL_AGAINSTLADDER\t\t\t\t8\t\t//bot is against a ladder\n#define MFL_WATERJUMP\t\t\t\t\t16\t\t//bot is waterjumping\n#define MFL_TELEPORTED\t\t\t\t\t32\t\t//bot is being teleported\n#define MFL_GRAPPLEPULL\t\t\t\t\t64\t\t//bot is being pulled by the grapple\n#define MFL_ACTIVEGRAPPLE\t\t\t\t128\t\t//bot is using the grapple hook\n#define MFL_GRAPPLERESET\t\t\t\t256\t\t//bot has reset the grapple\n#define MFL_WALK\t\t\t\t\t\t512\t\t//bot should walk slowly\n// move result flags\n#define MOVERESULT_MOVEMENTVIEW\t\t\t1\t\t//bot uses view for movement\n#define MOVERESULT_SWIMVIEW\t\t\t\t2\t\t//bot uses view for swimming\n#define MOVERESULT_WAITING\t\t\t\t4\t\t//bot is waiting for something\n#define MOVERESULT_MOVEMENTVIEWSET\t\t8\t\t//bot has set the view in movement code\n#define MOVERESULT_MOVEMENTWEAPON\t\t16\t\t//bot uses weapon for movement\n#define MOVERESULT_ONTOPOFOBSTACLE\t\t32\t\t//bot is ontop of obstacle\n#define MOVERESULT_ONTOPOF_FUNCBOB\t\t64\t\t//bot is ontop of a func_bobbing\n#define MOVERESULT_ONTOPOF_ELEVATOR\t\t128\t\t//bot is ontop of an elevator (func_plat)\n#define MOVERESULT_BLOCKEDBYAVOIDSPOT\t256\t\t//bot is blocked by an avoid spot\n//\n#define MAX_AVOIDREACH\t\t\t\t\t1\n#define MAX_AVOIDSPOTS\t\t\t\t\t32\n// avoid spot types\n#define AVOID_CLEAR\t\t\t\t\t\t0\t\t//clear all avoid spots\n#define AVOID_ALWAYS\t\t\t\t\t1\t\t//avoid always\n#define AVOID_DONTBLOCK\t\t\t\t\t2\t\t//never totally block\n// restult types\n#define RESULTTYPE_ELEVATORUP\t\t\t1\t\t//elevator is up\n#define RESULTTYPE_WAITFORFUNCBOBBING\t2\t\t//waiting for func bobbing to arrive\n#define RESULTTYPE_BADGRAPPLEPATH\t\t4\t\t//grapple path is obstructed\n#define RESULTTYPE_INSOLIDAREA\t\t\t8\t\t//stuck in solid area, this is bad\n\n//structure used to initialize the movement state\n//the or_moveflags MFL_ONGROUND, MFL_TELEPORTED and MFL_WATERJUMP come from the playerstate\ntypedef struct bot_initmove_s\n{\n\tvec3_t origin;\t\t\t\t//origin of the bot\n\tvec3_t velocity;\t\t\t//velocity of the bot\n\tvec3_t viewoffset;\t\t\t//view offset\n\tint entitynum;\t\t\t\t//entity number of the bot\n\tint client;\t\t\t\t\t//client number of the bot\n\tfloat thinktime;\t\t\t//time the bot thinks\n\tint presencetype;\t\t\t//presencetype of the bot\n\tvec3_t viewangles;\t\t\t//view angles of the bot\n\tint or_moveflags;\t\t\t//values ored to the movement flags\n} bot_initmove_t;\n\n//NOTE: the ideal_viewangles are only valid if MFL_MOVEMENTVIEW is set\ntypedef struct bot_moveresult_s\n{\n\tint failure;\t\t\t\t//true if movement failed all together\n\tint type;\t\t\t\t\t//failure or blocked type\n\tint blocked;\t\t\t\t//true if blocked by an entity\n\tint blockentity;\t\t\t//entity blocking the bot\n\tint traveltype;\t\t\t\t//last executed travel type\n\tint flags;\t\t\t\t\t//result flags\n\tint weapon;\t\t\t\t\t//weapon used for movement\n\tvec3_t movedir;\t\t\t\t//movement direction\n\tvec3_t ideal_viewangles;\t//ideal viewangles for the movement\n} bot_moveresult_t;\n\n#define bot_moveresult_t_cleared(x) bot_moveresult_t (x) = {0, 0, 0, 0, 0, 0, 0, {0, 0, 0}, {0, 0, 0}}\n\ntypedef struct bot_avoidspot_s\n{\n\tvec3_t origin;\n\tfloat radius;\n\tint type;\n} bot_avoidspot_t;\n\n//resets the whole move state\nvoid BotResetMoveState(int movestate);\n//moves the bot to the given goal\nvoid BotMoveToGoal(bot_moveresult_t *result, int movestate, bot_goal_t *goal, int travelflags);\n//moves the bot in the specified direction using the specified type of movement\nint BotMoveInDirection(int movestate, vec3_t dir, float speed, int type);\n//reset avoid reachability\nvoid BotResetAvoidReach(int movestate);\n//resets the last avoid reachability\nvoid BotResetLastAvoidReach(int movestate);\n//returns a reachability area if the origin is in one\nint BotReachabilityArea(vec3_t origin, int client);\n//view target based on movement\nint BotMovementViewTarget(int movestate, bot_goal_t *goal, int travelflags, float lookahead, vec3_t target);\n//predict the position of a player based on movement towards a goal\nint BotPredictVisiblePosition(vec3_t origin, int areanum, bot_goal_t *goal, int travelflags, vec3_t target);\n//returns the handle of a newly allocated movestate\nint BotAllocMoveState(void);\n//frees the movestate with the given handle\nvoid BotFreeMoveState(int handle);\n//initialize movement state before performing any movement\nvoid BotInitMoveState(int handle, bot_initmove_t *initmove);\n//add a spot to avoid (if type == AVOID_CLEAR all spots are removed)\nvoid BotAddAvoidSpot(int movestate, vec3_t origin, float radius, int type);\n//must be called every map change\nvoid BotSetBrushModelTypes(void);\n//setup movement AI\nint BotSetupMoveAI(void);\n//shutdown movement AI\nvoid BotShutdownMoveAI(void);\n\n"
  },
  {
    "path": "plugins/quake3/botlib/be_ai_weap.c",
    "content": "/*\n===========================================================================\nCopyright (C) 1999-2005 Id Software, Inc.\n\nThis file is part of Quake III Arena source code.\n\nQuake III Arena source code is free software; you can redistribute it\nand/or modify it under the terms of the GNU General Public License as\npublished by the Free Software Foundation; either version 2 of the License,\nor (at your option) any later version.\n\nQuake III Arena source code is distributed in the hope that it will be\nuseful, but WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Quake III Arena source code; if not, write to the Free Software\nFoundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n===========================================================================\n*/\n\n/*****************************************************************************\n * name:\t\tbe_ai_weap.c\n *\n * desc:\t\tweapon AI\n *\n * $Archive: /MissionPack/code/botlib/be_ai_weap.c $\n *\n *****************************************************************************/\n\n#include \"q_shared.h\"\n#include \"l_libvar.h\"\n#include \"l_log.h\"\n#include \"l_memory.h\"\n#include \"l_utils.h\"\n#include \"l_script.h\"\n#include \"l_precomp.h\"\n#include \"l_struct.h\"\n#include \"aasfile.h\"\n#include \"botlib.h\"\n#include \"be_aas.h\"\n#include \"be_aas_funcs.h\"\n#include \"be_interface.h\"\n#include \"be_ai_weight.h\"\t\t//fuzzy weights\n#include \"be_ai_weap.h\"\n\n//#define DEBUG_AI_WEAP\n\n//structure field offsets\n#define WEAPON_OFS(x) (size_t)&(((weaponinfo_t *)0)->x)\n#define PROJECTILE_OFS(x) (size_t)&(((projectileinfo_t *)0)->x)\n\n//weapon definition\nstatic fielddef_t weaponinfo_fields[] =\n{\n{\"number\", WEAPON_OFS(number), FT_INT},\t\t\t\t\t\t//weapon number\n{\"name\", WEAPON_OFS(name), FT_STRING},\t\t\t\t\t\t\t//name of the weapon\n{\"level\", WEAPON_OFS(level), FT_INT},\n{\"model\", WEAPON_OFS(model), FT_STRING},\t\t\t\t\t\t//model of the weapon\n{\"weaponindex\", WEAPON_OFS(weaponindex), FT_INT},\t\t\t//index of weapon in inventory\n{\"flags\", WEAPON_OFS(flags), FT_INT},\t\t\t\t\t\t\t//special flags\n{\"projectile\", WEAPON_OFS(projectile), FT_STRING},\t\t\t//projectile used by the weapon\n{\"numprojectiles\", WEAPON_OFS(numprojectiles), FT_INT},\t//number of projectiles\n{\"hspread\", WEAPON_OFS(hspread), FT_FLOAT},\t\t\t\t\t//horizontal spread of projectiles (degrees from middle)\n{\"vspread\", WEAPON_OFS(vspread), FT_FLOAT},\t\t\t\t\t//vertical spread of projectiles (degrees from middle)\n{\"speed\", WEAPON_OFS(speed), FT_FLOAT},\t\t\t\t\t\t//speed of the projectile (0 = instant hit)\n{\"acceleration\", WEAPON_OFS(acceleration), FT_FLOAT},\t\t//\"acceleration\" * time (in seconds) + \"speed\" = projectile speed\n{\"recoil\", WEAPON_OFS(recoil), FT_FLOAT|FT_ARRAY, 3},\t\t//amount of recoil the player gets from the weapon\n{\"offset\", WEAPON_OFS(offset), FT_FLOAT|FT_ARRAY, 3},\t\t//projectile start offset relative to eye and view angles\n{\"angleoffset\", WEAPON_OFS(angleoffset), FT_FLOAT|FT_ARRAY, 3},//offset of the shoot angles relative to the view angles\n{\"extrazvelocity\", WEAPON_OFS(extrazvelocity), FT_FLOAT},//extra z velocity the projectile gets\n{\"ammoamount\", WEAPON_OFS(ammoamount), FT_INT},\t\t\t\t//ammo amount used per shot\n{\"ammoindex\", WEAPON_OFS(ammoindex), FT_INT},\t\t\t\t//index of ammo in inventory\n{\"activate\", WEAPON_OFS(activate), FT_FLOAT},\t\t\t\t//time it takes to select the weapon\n{\"reload\", WEAPON_OFS(reload), FT_FLOAT},\t\t\t\t\t\t//time it takes to reload the weapon\n{\"spinup\", WEAPON_OFS(spinup), FT_FLOAT},\t\t\t\t\t\t//time it takes before first shot\n{\"spindown\", WEAPON_OFS(spindown), FT_FLOAT},\t\t\t\t//time it takes before weapon stops firing\n{NULL, 0, 0, 0}\n};\n\n//projectile definition\nstatic fielddef_t projectileinfo_fields[] =\n{\n{\"name\", PROJECTILE_OFS(name), FT_STRING},\t\t\t\t\t//name of the projectile\n{\"model\", PROJECTILE_OFS(model), FT_STRING},\t\t\t\t\t//model of the projectile\n{\"flags\", PROJECTILE_OFS(flags), FT_INT},\t\t\t\t\t\t//special flags\n{\"gravity\", PROJECTILE_OFS(gravity), FT_FLOAT},\t\t\t\t//amount of gravity applied to the projectile [0,1]\n{\"damage\", PROJECTILE_OFS(damage), FT_INT},\t\t\t\t\t//damage of the projectile\n{\"radius\", PROJECTILE_OFS(radius), FT_FLOAT},\t\t\t\t//radius of damage\n{\"visdamage\", PROJECTILE_OFS(visdamage), FT_INT},\t\t\t//damage of the projectile to visible entities\n{\"damagetype\", PROJECTILE_OFS(damagetype), FT_INT},\t\t//type of damage (combination of the DAMAGETYPE_? flags)\n{\"healthinc\", PROJECTILE_OFS(healthinc), FT_INT},\t\t\t//health increase the owner gets\n{\"push\", PROJECTILE_OFS(push), FT_FLOAT},\t\t\t\t\t\t//amount a player is pushed away from the projectile impact\n{\"detonation\", PROJECTILE_OFS(detonation), FT_FLOAT},\t\t//time before projectile explodes after fire pressed\n{\"bounce\", PROJECTILE_OFS(bounce), FT_FLOAT},\t\t\t\t//amount the projectile bounces\n{\"bouncefric\", PROJECTILE_OFS(bouncefric), FT_FLOAT}, \t//amount the bounce decreases per bounce\n{\"bouncestop\", PROJECTILE_OFS(bouncestop), FT_FLOAT},\t\t//minimum bounce value before bouncing stops\n//recurive projectile definition??\n{NULL, 0, 0, 0}\n};\n\nstatic structdef_t weaponinfo_struct =\n{\n\tsizeof(weaponinfo_t), weaponinfo_fields\n};\nstatic structdef_t projectileinfo_struct =\n{\n\tsizeof(projectileinfo_t), projectileinfo_fields\n};\n\n//weapon configuration: set of weapons with projectiles\ntypedef struct weaponconfig_s\n{\n\tint numweapons;\n\tint numprojectiles;\n\tprojectileinfo_t *projectileinfo;\n\tweaponinfo_t *weaponinfo;\n} weaponconfig_t;\n\n//the bot weapon state\ntypedef struct bot_weaponstate_s\n{\n\tstruct weightconfig_s *weaponweightconfig;\t\t//weapon weight configuration\n\tint *weaponweightindex;\t\t\t\t\t\t\t//weapon weight index\n} bot_weaponstate_t;\n\nstatic bot_weaponstate_t *botweaponstates[MAX_CLIENTS+1];\nstatic weaponconfig_t *weaponconfig;\n\n//========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//========================================================================\nint BotValidWeaponNumber(int weaponnum)\n{\n\tif (weaponnum <= 0 || weaponnum > weaponconfig->numweapons)\n\t{\n\t\tbotimport.Print(PRT_ERROR, \"weapon number out of range\\n\");\n\t\treturn qfalse;\n\t} //end if\n\treturn qtrue;\n} //end of the function BotValidWeaponNumber\n//========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//========================================================================\nbot_weaponstate_t *BotWeaponStateFromHandle(int handle)\n{\n\tif (handle <= 0 || handle > MAX_CLIENTS)\n\t{\n\t\tbotimport.Print(PRT_FATAL, \"move state handle %d out of range\\n\", handle);\n\t\treturn NULL;\n\t} //end if\n\tif (!botweaponstates[handle])\n\t{\n\t\tbotimport.Print(PRT_FATAL, \"invalid move state %d\\n\", handle);\n\t\treturn NULL;\n\t} //end if\n\treturn botweaponstates[handle];\n} //end of the function BotWeaponStateFromHandle\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\n#ifdef DEBUG_AI_WEAP\nvoid DumpWeaponConfig(weaponconfig_t *wc)\n{\n\tFILE *fp;\n\tint i;\n\n\tfp = Log_FileStruct();\n\tif (!fp) return;\n\tfor (i = 0; i < wc->numprojectiles; i++)\n\t{\n\t\tWriteStructure(fp, &projectileinfo_struct, (char *) &wc->projectileinfo[i]);\n\t\tLog_Flush();\n\t} //end for\n\tfor (i = 0; i < wc->numweapons; i++)\n\t{\n\t\tWriteStructure(fp, &weaponinfo_struct, (char *) &wc->weaponinfo[i]);\n\t\tLog_Flush();\n\t} //end for\n} //end of the function DumpWeaponConfig\n#endif //DEBUG_AI_WEAP\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nweaponconfig_t *LoadWeaponConfig(char *filename)\n{\n\tint max_weaponinfo, max_projectileinfo;\n\ttoken_t token;\n\tchar path[MAX_PATH];\n\tint i, j;\n\tsource_t *source;\n\tweaponconfig_t *wc;\n\tweaponinfo_t weaponinfo;\n\n\tmax_weaponinfo = (int) LibVarValue(\"max_weaponinfo\", \"32\");\n\tif (max_weaponinfo < 0)\n\t{\n\t\tbotimport.Print(PRT_ERROR, \"max_weaponinfo = %d\\n\", max_weaponinfo);\n\t\tmax_weaponinfo = 32;\n\t\tLibVarSet(\"max_weaponinfo\", \"32\");\n\t} //end if\n\tmax_projectileinfo = (int) LibVarValue(\"max_projectileinfo\", \"32\");\n\tif (max_projectileinfo < 0)\n\t{\n\t\tbotimport.Print(PRT_ERROR, \"max_projectileinfo = %d\\n\", max_projectileinfo);\n\t\tmax_projectileinfo = 32;\n\t\tLibVarSet(\"max_projectileinfo\", \"32\");\n\t} //end if\n\tstrncpy(path, filename, sizeof(path)-1);\n\tpath[sizeof(path)-1] = 0;\n\tPC_SetBaseFolder(BOTFILESBASEFOLDER);\n\tsource = LoadSourceFile(path);\n\tif (!source)\n\t{\n\t\tbotimport.Print(PRT_ERROR, \"counldn't load %s\\n\", path);\n\t\treturn NULL;\n\t} //end if\n\t//initialize weapon config\n\twc = (weaponconfig_t *) GetClearedHunkMemory(sizeof(weaponconfig_t) +\n\t\t\t\t\t\t\t\t\t\tmax_weaponinfo * sizeof(weaponinfo_t) +\n\t\t\t\t\t\t\t\t\t\tmax_projectileinfo * sizeof(projectileinfo_t));\n\twc->weaponinfo = (weaponinfo_t *) ((char *) wc + sizeof(weaponconfig_t));\n\twc->projectileinfo = (projectileinfo_t *) ((char *) wc->weaponinfo +\n\t\t\t\t\t\t\t\t\t\tmax_weaponinfo * sizeof(weaponinfo_t));\n\twc->numweapons = max_weaponinfo;\n\twc->numprojectiles = 0;\n\t//parse the source file\n\twhile(PC_ReadToken(source, &token))\n\t{\n\t\tif (!strcmp(token.string, \"weaponinfo\"))\n\t\t{\n\t\t\tCom_Memset(&weaponinfo, 0, sizeof(weaponinfo_t));\n\t\t\tif (!ReadStructure(source, &weaponinfo_struct, (char *) &weaponinfo))\n\t\t\t{\n\t\t\t\tFreeMemory(wc);\n\t\t\t\tFreeSource(source);\n\t\t\t\treturn NULL;\n\t\t\t} //end if\n\t\t\tif (weaponinfo.number < 0 || weaponinfo.number >= max_weaponinfo)\n\t\t\t{\n\t\t\t\tbotimport.Print(PRT_ERROR, \"weapon info number %d out of range in %s\\n\", weaponinfo.number, path);\n\t\t\t\tFreeMemory(wc);\n\t\t\t\tFreeSource(source);\n\t\t\t\treturn NULL;\n\t\t\t} //end if\n\t\t\tCom_Memcpy(&wc->weaponinfo[weaponinfo.number], &weaponinfo, sizeof(weaponinfo_t));\n\t\t\twc->weaponinfo[weaponinfo.number].valid = qtrue;\n\t\t} //end if\n\t\telse if (!strcmp(token.string, \"projectileinfo\"))\n\t\t{\n\t\t\tif (wc->numprojectiles >= max_projectileinfo)\n\t\t\t{\n\t\t\t\tbotimport.Print(PRT_ERROR, \"more than %d projectiles defined in %s\\n\", max_projectileinfo, path);\n\t\t\t\tFreeMemory(wc);\n\t\t\t\tFreeSource(source);\n\t\t\t\treturn NULL;\n\t\t\t} //end if\n\t\t\tCom_Memset(&wc->projectileinfo[wc->numprojectiles], 0, sizeof(projectileinfo_t));\n\t\t\tif (!ReadStructure(source, &projectileinfo_struct, (char *) &wc->projectileinfo[wc->numprojectiles]))\n\t\t\t{\n\t\t\t\tFreeMemory(wc);\n\t\t\t\tFreeSource(source);\n\t\t\t\treturn NULL;\n\t\t\t} //end if\n\t\t\twc->numprojectiles++;\n\t\t} //end if\n\t\telse\n\t\t{\n\t\t\tbotimport.Print(PRT_ERROR, \"unknown definition %s in %s\\n\", token.string, path);\n\t\t\tFreeMemory(wc);\n\t\t\tFreeSource(source);\n\t\t\treturn NULL;\n\t\t} //end else\n\t} //end while\n\tFreeSource(source);\n\t//fix up weapons\n\tfor (i = 0; i < wc->numweapons; i++)\n\t{\n\t\tif (!wc->weaponinfo[i].valid) continue;\n\t\tif (!wc->weaponinfo[i].name[0])\n\t\t{\n\t\t\tbotimport.Print(PRT_ERROR, \"weapon %d has no name in %s\\n\", i, path);\n\t\t\tFreeMemory(wc);\n\t\t\treturn NULL;\n\t\t} //end if\n\t\tif (!wc->weaponinfo[i].projectile[0])\n\t\t{\n\t\t\tbotimport.Print(PRT_ERROR, \"weapon %s has no projectile in %s\\n\", wc->weaponinfo[i].name, path);\n\t\t\tFreeMemory(wc);\n\t\t\treturn NULL;\n\t\t} //end if\n\t\t//find the projectile info and copy it to the weapon info\n\t\tfor (j = 0; j < wc->numprojectiles; j++)\n\t\t{\n\t\t\tif (!strcmp(wc->projectileinfo[j].name, wc->weaponinfo[i].projectile))\n\t\t\t{\n\t\t\t\tCom_Memcpy(&wc->weaponinfo[i].proj, &wc->projectileinfo[j], sizeof(projectileinfo_t));\n\t\t\t\tbreak;\n\t\t\t} //end if\n\t\t} //end for\n\t\tif (j == wc->numprojectiles)\n\t\t{\n\t\t\tbotimport.Print(PRT_ERROR, \"weapon %s uses undefined projectile in %s\\n\", wc->weaponinfo[i].name, path);\n\t\t\tFreeMemory(wc);\n\t\t\treturn NULL;\n\t\t} //end if\n\t} //end for\n\tif (!wc->numweapons) botimport.Print(PRT_WARNING, \"no weapon info loaded\\n\");\n\tbotimport.Print(PRT_MESSAGE, \"loaded %s\\n\", path);\n\treturn wc;\n} //end of the function LoadWeaponConfig\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint *WeaponWeightIndex(weightconfig_t *wwc, weaponconfig_t *wc)\n{\n\tint *index, i;\n\n\t//initialize item weight index\n\tindex = (int *) GetClearedMemory(sizeof(int) * wc->numweapons);\n\n\tfor (i = 0; i < wc->numweapons; i++)\n\t{\n\t\tindex[i] = FindFuzzyWeight(wwc, wc->weaponinfo[i].name);\n\t} //end for\n\treturn index;\n} //end of the function WeaponWeightIndex\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid BotFreeWeaponWeights(int weaponstate)\n{\n\tbot_weaponstate_t *ws;\n\n\tws = BotWeaponStateFromHandle(weaponstate);\n\tif (!ws) return;\n\tif (ws->weaponweightconfig) FreeWeightConfig(ws->weaponweightconfig);\n\tif (ws->weaponweightindex) FreeMemory(ws->weaponweightindex);\n} //end of the function BotFreeWeaponWeights\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint BotLoadWeaponWeights(int weaponstate, char *filename)\n{\n\tbot_weaponstate_t *ws;\n\n\tws = BotWeaponStateFromHandle(weaponstate);\n\tif (!ws) return BLERR_CANNOTLOADWEAPONWEIGHTS;\n\tBotFreeWeaponWeights(weaponstate);\n\t//\n\tws->weaponweightconfig = ReadWeightConfig(filename);\n\tif (!ws->weaponweightconfig)\n\t{\n\t\tbotimport.Print(PRT_FATAL, \"couldn't load weapon config %s\\n\", filename);\n\t\treturn BLERR_CANNOTLOADWEAPONWEIGHTS;\n\t} //end if\n\tif (!weaponconfig) return BLERR_CANNOTLOADWEAPONCONFIG;\n\tws->weaponweightindex = WeaponWeightIndex(ws->weaponweightconfig, weaponconfig);\n\treturn BLERR_NOERROR;\n} //end of the function BotLoadWeaponWeights\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid BotGetWeaponInfo(int weaponstate, int weapon, weaponinfo_t *weaponinfo)\n{\n\tbot_weaponstate_t *ws;\n\n\tif (!BotValidWeaponNumber(weapon)) return;\n\tws = BotWeaponStateFromHandle(weaponstate);\n\tif (!ws) return;\n\tif (!weaponconfig) return;\n\tCom_Memcpy(weaponinfo, &weaponconfig->weaponinfo[weapon], sizeof(weaponinfo_t));\n} //end of the function BotGetWeaponInfo\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint BotChooseBestFightWeapon(int weaponstate, int *inventory)\n{\n\tint i, index, bestweapon;\n\tfloat weight, bestweight;\n\tweaponconfig_t *wc;\n\tbot_weaponstate_t *ws;\n\n\tws = BotWeaponStateFromHandle(weaponstate);\n\tif (!ws) return 0;\n\twc = weaponconfig;\n\tif (!weaponconfig) return 0;\n\n\t//if the bot has no weapon weight configuration\n\tif (!ws->weaponweightconfig) return 0;\n\n\tbestweight = 0;\n\tbestweapon = 0;\n\tfor (i = 0; i < wc->numweapons; i++)\n\t{\n\t\tif (!wc->weaponinfo[i].valid) continue;\n\t\tindex = ws->weaponweightindex[i];\n\t\tif (index < 0) continue;\n\t\tweight = FuzzyWeight(inventory, ws->weaponweightconfig, index);\n\t\tif (weight > bestweight)\n\t\t{\n\t\t\tbestweight = weight;\n\t\t\tbestweapon = i;\n\t\t} //end if\n\t} //end for\n\treturn bestweapon;\n} //end of the function BotChooseBestFightWeapon\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid BotResetWeaponState(int weaponstate)\n{\n\tstruct weightconfig_s *weaponweightconfig;\n\tint *weaponweightindex;\n\tbot_weaponstate_t *ws;\n\n\tws = BotWeaponStateFromHandle(weaponstate);\n\tif (!ws) return;\n\tweaponweightconfig = ws->weaponweightconfig;\n\tweaponweightindex = ws->weaponweightindex;\n\n\t//Com_Memset(ws, 0, sizeof(bot_weaponstate_t));\n\tws->weaponweightconfig = weaponweightconfig;\n\tws->weaponweightindex = weaponweightindex;\n} //end of the function BotResetWeaponState\n//========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//========================================================================\nint BotAllocWeaponState(void)\n{\n\tint i;\n\n\tfor (i = 1; i <= MAX_CLIENTS; i++)\n\t{\n\t\tif (!botweaponstates[i])\n\t\t{\n\t\t\tbotweaponstates[i] = GetClearedMemory(sizeof(bot_weaponstate_t));\n\t\t\treturn i;\n\t\t} //end if\n\t} //end for\n\treturn 0;\n} //end of the function BotAllocWeaponState\n//========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//========================================================================\nvoid BotFreeWeaponState(int handle)\n{\n\tif (handle <= 0 || handle > MAX_CLIENTS)\n\t{\n\t\tbotimport.Print(PRT_FATAL, \"move state handle %d out of range\\n\", handle);\n\t\treturn;\n\t} //end if\n\tif (!botweaponstates[handle])\n\t{\n\t\tbotimport.Print(PRT_FATAL, \"invalid move state %d\\n\", handle);\n\t\treturn;\n\t} //end if\n\tBotFreeWeaponWeights(handle);\n\tFreeMemory(botweaponstates[handle]);\n\tbotweaponstates[handle] = NULL;\n} //end of the function BotFreeWeaponState\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint BotSetupWeaponAI(void)\n{\n\tchar *file;\n\n\tfile = LibVarString(\"weaponconfig\", \"weapons.c\");\n\tweaponconfig = LoadWeaponConfig(file);\n\tif (!weaponconfig)\n\t{\n\t\tbotimport.Print(PRT_FATAL, \"couldn't load the weapon config\\n\");\n\t\treturn BLERR_CANNOTLOADWEAPONCONFIG;\n\t} //end if\n\n#ifdef DEBUG_AI_WEAP\n\tDumpWeaponConfig(weaponconfig);\n#endif //DEBUG_AI_WEAP\n\t//\n\treturn BLERR_NOERROR;\n} //end of the function BotSetupWeaponAI\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid BotShutdownWeaponAI(void)\n{\n\tint i;\n\n\tif (weaponconfig) FreeMemory(weaponconfig);\n\tweaponconfig = NULL;\n\n\tfor (i = 1; i <= MAX_CLIENTS; i++)\n\t{\n\t\tif (botweaponstates[i])\n\t\t{\n\t\t\tBotFreeWeaponState(i);\n\t\t} //end if\n\t} //end for\n} //end of the function BotShutdownWeaponAI\n\n"
  },
  {
    "path": "plugins/quake3/botlib/be_ai_weap.h",
    "content": "/*\n===========================================================================\nCopyright (C) 1999-2005 Id Software, Inc.\n\nThis file is part of Quake III Arena source code.\n\nQuake III Arena source code is free software; you can redistribute it\nand/or modify it under the terms of the GNU General Public License as\npublished by the Free Software Foundation; either version 2 of the License,\nor (at your option) any later version.\n\nQuake III Arena source code is distributed in the hope that it will be\nuseful, but WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Quake III Arena source code; if not, write to the Free Software\nFoundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n===========================================================================\n*/\n//\n\n/*****************************************************************************\n * name:\t\tbe_ai_weap.h\n *\n * desc:\t\tweapon AI\n *\n * $Archive: /source/code/botlib/be_ai_weap.h $\n *\n *****************************************************************************/\n\n//projectile flags\n#define PFL_WINDOWDAMAGE\t\t\t1\t\t//projectile damages through window\n#define PFL_RETURN\t\t\t\t\t2\t\t//set when projectile returns to owner\n//weapon flags\n#define WFL_FIRERELEASED\t\t\t1\t\t//set when projectile is fired with key-up event\n//damage types\n#define DAMAGETYPE_IMPACT\t\t\t1\t\t//damage on impact\n#define DAMAGETYPE_RADIAL\t\t\t2\t\t//radial damage\n#define DAMAGETYPE_VISIBLE\t\t\t4\t\t//damage to all entities visible to the projectile\n\ntypedef struct projectileinfo_s\n{\n\tchar name[MAX_STRINGFIELD];\n\tchar model[MAX_STRINGFIELD];\n\tint flags;\n\tfloat gravity;\n\tint damage;\n\tfloat radius;\n\tint visdamage;\n\tint damagetype;\n\tint healthinc;\n\tfloat push;\n\tfloat detonation;\n\tfloat bounce;\n\tfloat bouncefric;\n\tfloat bouncestop;\n} projectileinfo_t;\n\ntypedef struct weaponinfo_s\n{\n\tint valid;\t\t\t\t\t//true if the weapon info is valid\n\tint number;\t\t\t\t\t\t\t\t\t//number of the weapon\n\tchar name[MAX_STRINGFIELD];\n\tchar model[MAX_STRINGFIELD];\n\tint level;\n\tint weaponindex;\n\tint flags;\n\tchar projectile[MAX_STRINGFIELD];\n\tint numprojectiles;\n\tfloat hspread;\n\tfloat vspread;\n\tfloat speed;\n\tfloat acceleration;\n\tvec3_t recoil;\n\tvec3_t offset;\n\tvec3_t angleoffset;\n\tfloat extrazvelocity;\n\tint ammoamount;\n\tint ammoindex;\n\tfloat activate;\n\tfloat reload;\n\tfloat spinup;\n\tfloat spindown;\n\tprojectileinfo_t proj;\t\t\t\t\t\t//pointer to the used projectile\n} weaponinfo_t;\n\n//setup the weapon AI\nint BotSetupWeaponAI(void);\n//shut down the weapon AI\nvoid BotShutdownWeaponAI(void);\n//returns the best weapon to fight with\nint BotChooseBestFightWeapon(int weaponstate, int *inventory);\n//returns the information of the current weapon\nvoid BotGetWeaponInfo(int weaponstate, int weapon, weaponinfo_t *weaponinfo);\n//loads the weapon weights\nint BotLoadWeaponWeights(int weaponstate, char *filename);\n//returns a handle to a newly allocated weapon state\nint BotAllocWeaponState(void);\n//frees the weapon state\nvoid BotFreeWeaponState(int weaponstate);\n//resets the whole weapon state\nvoid BotResetWeaponState(int weaponstate);\n"
  },
  {
    "path": "plugins/quake3/botlib/be_ai_weight.c",
    "content": "/*\n===========================================================================\nCopyright (C) 1999-2005 Id Software, Inc.\n\nThis file is part of Quake III Arena source code.\n\nQuake III Arena source code is free software; you can redistribute it\nand/or modify it under the terms of the GNU General Public License as\npublished by the Free Software Foundation; either version 2 of the License,\nor (at your option) any later version.\n\nQuake III Arena source code is distributed in the hope that it will be\nuseful, but WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Quake III Arena source code; if not, write to the Free Software\nFoundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n===========================================================================\n*/\n\n/*****************************************************************************\n * name:\t\tbe_ai_weight.c\n *\n * desc:\t\tfuzzy logic\n *\n * $Archive: /MissionPack/code/botlib/be_ai_weight.c $\n *\n *****************************************************************************/\n\n#include \"q_shared.h\"\n#include \"l_memory.h\"\n#include \"l_log.h\"\n#include \"l_utils.h\"\n#include \"l_script.h\"\n#include \"l_precomp.h\"\n#include \"l_struct.h\"\n#include \"l_libvar.h\"\n#include \"aasfile.h\"\n#include \"botlib.h\"\n#include \"be_aas.h\"\n#include \"be_aas_funcs.h\"\n#include \"be_interface.h\"\n#include \"be_ai_weight.h\"\n\n#define MAX_INVENTORYVALUE\t\t\t999999\n#define EVALUATERECURSIVELY\n\n#define MAX_WEIGHT_FILES\t\t\t128\nweightconfig_t\t*weightFileList[MAX_WEIGHT_FILES];\n\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint ReadValue(source_t *source, float *value)\n{\n\ttoken_t token;\n\n\tif (!PC_ExpectAnyToken(source, &token)) return qfalse;\n\tif (!strcmp(token.string, \"-\"))\n\t{\n\t\tSourceWarning(source, \"negative value set to zero\\n\");\n\n\t\tif(!PC_ExpectAnyToken(source, &token))\n\t\t{\n\t\t\tSourceError(source, \"Missing return value\\n\");\n\t\t\treturn qfalse;\n\t\t}\n\t}\n\n\tif (token.type != TT_NUMBER)\n\t{\n\t\tSourceError(source, \"invalid return value %s\\n\", token.string);\n\t\treturn qfalse;\n\t}\n\t\n\t*value = token.floatvalue;\n\treturn qtrue;\n} //end of the function ReadValue\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint ReadFuzzyWeight(source_t *source, fuzzyseperator_t *fs)\n{\n\tif (PC_CheckTokenString(source, \"balance\"))\n\t{\n\t\tfs->type = WT_BALANCE;\n\t\tif (!PC_ExpectTokenString(source, \"(\")) return qfalse;\n\t\tif (!ReadValue(source, &fs->weight)) return qfalse;\n\t\tif (!PC_ExpectTokenString(source, \",\")) return qfalse;\n\t\tif (!ReadValue(source, &fs->minweight)) return qfalse;\n\t\tif (!PC_ExpectTokenString(source, \",\")) return qfalse;\n\t\tif (!ReadValue(source, &fs->maxweight)) return qfalse;\n\t\tif (!PC_ExpectTokenString(source, \")\")) return qfalse;\n\t} //end if\n\telse\n\t{\n\t\tfs->type = 0;\n\t\tif (!ReadValue(source, &fs->weight)) return qfalse;\n\t\tfs->minweight = fs->weight;\n\t\tfs->maxweight = fs->weight;\n\t} //end if\n\tif (!PC_ExpectTokenString(source, \";\")) return qfalse;\n\treturn qtrue;\n} //end of the function ReadFuzzyWeight\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid FreeFuzzySeperators_r(fuzzyseperator_t *fs)\n{\n\tif (!fs) return;\n\tif (fs->child) FreeFuzzySeperators_r(fs->child);\n\tif (fs->next) FreeFuzzySeperators_r(fs->next);\n\tFreeMemory(fs);\n} //end of the function FreeFuzzySeperators\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid FreeWeightConfig2(weightconfig_t *config)\n{\n\tint i;\n\n\tfor (i = 0; i < config->numweights; i++)\n\t{\n\t\tFreeFuzzySeperators_r(config->weights[i].firstseperator);\n\t\tif (config->weights[i].name) FreeMemory(config->weights[i].name);\n\t} //end for\n\tFreeMemory(config);\n} //end of the function FreeWeightConfig2\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid FreeWeightConfig(weightconfig_t *config)\n{\n\tif (!LibVarGetValue(\"bot_reloadcharacters\")) return;\n\tFreeWeightConfig2(config);\n} //end of the function FreeWeightConfig\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nfuzzyseperator_t *ReadFuzzySeperators_r(source_t *source)\n{\n\tint newindent, index, def, founddefault;\n\ttoken_t token;\n\tfuzzyseperator_t *fs, *lastfs, *firstfs;\n\n\tfounddefault = qfalse;\n\tfirstfs = NULL;\n\tlastfs = NULL;\n\tif (!PC_ExpectTokenString(source, \"(\")) return NULL;\n\tif (!PC_ExpectTokenType(source, TT_NUMBER, TT_INTEGER, &token)) return NULL;\n\tindex = token.intvalue;\n\tif (!PC_ExpectTokenString(source, \")\")) return NULL;\n\tif (!PC_ExpectTokenString(source, \"{\")) return NULL;\n\tif (!PC_ExpectAnyToken(source, &token)) return NULL;\n\tdo\n\t{\n\t\tdef = !strcmp(token.string, \"default\");\n\t\tif (def || !strcmp(token.string, \"case\"))\n\t\t{\n\t\t\tfs = (fuzzyseperator_t *) GetClearedMemory(sizeof(fuzzyseperator_t));\n\t\t\tfs->index = index;\n\t\t\tif (lastfs) lastfs->next = fs;\n\t\t\telse firstfs = fs;\n\t\t\tlastfs = fs;\n\t\t\tif (def)\n\t\t\t{\n\t\t\t\tif (founddefault)\n\t\t\t\t{\n\t\t\t\t\tSourceError(source, \"switch already has a default\\n\");\n\t\t\t\t\tFreeFuzzySeperators_r(firstfs);\n\t\t\t\t\treturn NULL;\n\t\t\t\t} //end if\n\t\t\t\tfs->value = MAX_INVENTORYVALUE;\n\t\t\t\tfounddefault = qtrue;\n\t\t\t} //end if\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (!PC_ExpectTokenType(source, TT_NUMBER, TT_INTEGER, &token))\n\t\t\t\t{\n\t\t\t\t\tFreeFuzzySeperators_r(firstfs);\n\t\t\t\t\treturn NULL;\n\t\t\t\t} //end if\n\t\t\t\tfs->value = token.intvalue;\n\t\t\t} //end else\n\t\t\tif (!PC_ExpectTokenString(source, \":\") || !PC_ExpectAnyToken(source, &token))\n\t\t\t{\n\t\t\t\tFreeFuzzySeperators_r(firstfs);\n\t\t\t\treturn NULL;\n\t\t\t} //end if\n\t\t\tnewindent = qfalse;\n\t\t\tif (!strcmp(token.string, \"{\"))\n\t\t\t{\n\t\t\t\tnewindent = qtrue;\n\t\t\t\tif (!PC_ExpectAnyToken(source, &token))\n\t\t\t\t{\n\t\t\t\t\tFreeFuzzySeperators_r(firstfs);\n\t\t\t\t\treturn NULL;\n\t\t\t\t} //end if\n\t\t\t} //end if\n\t\t\tif (!strcmp(token.string, \"return\"))\n\t\t\t{\n\t\t\t\tif (!ReadFuzzyWeight(source, fs))\n\t\t\t\t{\n\t\t\t\t\tFreeFuzzySeperators_r(firstfs);\n\t\t\t\t\treturn NULL;\n\t\t\t\t} //end if\n\t\t\t} //end if\n\t\t\telse if (!strcmp(token.string, \"switch\"))\n\t\t\t{\n\t\t\t\tfs->child = ReadFuzzySeperators_r(source);\n\t\t\t\tif (!fs->child)\n\t\t\t\t{\n\t\t\t\t\tFreeFuzzySeperators_r(firstfs);\n\t\t\t\t\treturn NULL;\n\t\t\t\t} //end if\n\t\t\t} //end else if\n\t\t\telse\n\t\t\t{\n\t\t\t\tSourceError(source, \"invalid name %s\\n\", token.string);\n\t\t\t\treturn NULL;\n\t\t\t} //end else\n\t\t\tif (newindent)\n\t\t\t{\n\t\t\t\tif (!PC_ExpectTokenString(source, \"}\"))\n\t\t\t\t{\n\t\t\t\t\tFreeFuzzySeperators_r(firstfs);\n\t\t\t\t\treturn NULL;\n\t\t\t\t} //end if\n\t\t\t} //end if\n\t\t} //end if\n\t\telse\n\t\t{\n\t\t\tFreeFuzzySeperators_r(firstfs);\n\t\t\tSourceError(source, \"invalid name %s\\n\", token.string);\n\t\t\treturn NULL;\n\t\t} //end else\n\t\tif (!PC_ExpectAnyToken(source, &token))\n\t\t{\n\t\t\tFreeFuzzySeperators_r(firstfs);\n\t\t\treturn NULL;\n\t\t} //end if\n\t} while(strcmp(token.string, \"}\"));\n\t//\n\tif (!founddefault)\n\t{\n\t\tSourceWarning(source, \"switch without default\\n\");\n\t\tfs = (fuzzyseperator_t *) GetClearedMemory(sizeof(fuzzyseperator_t));\n\t\tfs->index = index;\n\t\tfs->value = MAX_INVENTORYVALUE;\n\t\tfs->weight = 0;\n\t\tfs->next = NULL;\n\t\tfs->child = NULL;\n\t\tif (lastfs) lastfs->next = fs;\n\t\telse firstfs = fs;\n\t\tlastfs = fs;\n\t} //end if\n\t//\n\treturn firstfs;\n} //end of the function ReadFuzzySeperators_r\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nweightconfig_t *ReadWeightConfig(char *filename)\n{\n\tint newindent, avail = 0, n;\n\ttoken_t token;\n\tsource_t *source;\n\tfuzzyseperator_t *fs;\n\tweightconfig_t *config = NULL;\n#ifdef DEBUG\n\tint starttime;\n\n\tstarttime = Sys_MilliSeconds();\n#endif //DEBUG\n\n\tif (!LibVarGetValue(\"bot_reloadcharacters\"))\n\t{\n\t\tavail = -1;\n\t\tfor( n = 0; n < MAX_WEIGHT_FILES; n++ )\n\t\t{\n\t\t\tconfig = weightFileList[n];\n\t\t\tif( !config )\n\t\t\t{\n\t\t\t\tif( avail == -1 )\n\t\t\t\t{\n\t\t\t\t\tavail = n;\n\t\t\t\t} //end if\n\t\t\t\tcontinue;\n\t\t\t} //end if\n\t\t\tif( strcmp( filename, config->filename ) == 0 )\n\t\t\t{\n\t\t\t\t//botimport.Print( PRT_MESSAGE, \"retained %s\\n\", filename );\n\t\t\t\treturn config;\n\t\t\t} //end if\n\t\t} //end for\n\n\t\tif( avail == -1 )\n\t\t{\n\t\t\tbotimport.Print( PRT_ERROR, \"weightFileList was full trying to load %s\\n\", filename );\n\t\t\treturn NULL;\n\t\t} //end if\n\t} //end if\n\n\tPC_SetBaseFolder(BOTFILESBASEFOLDER);\n\tsource = LoadSourceFile(filename);\n\tif (!source)\n\t{\n\t\tbotimport.Print(PRT_ERROR, \"counldn't load %s\\n\", filename);\n\t\treturn NULL;\n\t} //end if\n\t//\n\tconfig = (weightconfig_t *) GetClearedMemory(sizeof(weightconfig_t));\n\tconfig->numweights = 0;\n\tQ_strncpyz( config->filename, filename, sizeof(config->filename) );\n\t//parse the item config file\n\twhile(PC_ReadToken(source, &token))\n\t{\n\t\tif (!strcmp(token.string, \"weight\"))\n\t\t{\n\t\t\tif (config->numweights >= MAX_WEIGHTS)\n\t\t\t{\n\t\t\t\tSourceWarning(source, \"too many fuzzy weights\\n\");\n\t\t\t\tbreak;\n\t\t\t} //end if\n\t\t\tif (!PC_ExpectTokenType(source, TT_STRING, 0, &token))\n\t\t\t{\n\t\t\t\tFreeWeightConfig(config);\n\t\t\t\tFreeSource(source);\n\t\t\t\treturn NULL;\n\t\t\t} //end if\n\t\t\tStripDoubleQuotes(token.string);\n\t\t\tconfig->weights[config->numweights].name = (char *) GetClearedMemory(strlen(token.string) + 1);\n\t\t\tstrcpy(config->weights[config->numweights].name, token.string);\n\t\t\tif (!PC_ExpectAnyToken(source, &token))\n\t\t\t{\n\t\t\t\tFreeWeightConfig(config);\n\t\t\t\tFreeSource(source);\n\t\t\t\treturn NULL;\n\t\t\t} //end if\n\t\t\tnewindent = qfalse;\n\t\t\tif (!strcmp(token.string, \"{\"))\n\t\t\t{\n\t\t\t\tnewindent = qtrue;\n\t\t\t\tif (!PC_ExpectAnyToken(source, &token))\n\t\t\t\t{\n\t\t\t\t\tFreeWeightConfig(config);\n\t\t\t\t\tFreeSource(source);\n\t\t\t\t\treturn NULL;\n\t\t\t\t} //end if\n\t\t\t} //end if\n\t\t\tif (!strcmp(token.string, \"switch\"))\n\t\t\t{\n\t\t\t\tfs = ReadFuzzySeperators_r(source);\n\t\t\t\tif (!fs)\n\t\t\t\t{\n\t\t\t\t\tFreeWeightConfig(config);\n\t\t\t\t\tFreeSource(source);\n\t\t\t\t\treturn NULL;\n\t\t\t\t} //end if\n\t\t\t\tconfig->weights[config->numweights].firstseperator = fs;\n\t\t\t} //end if\n\t\t\telse if (!strcmp(token.string, \"return\"))\n\t\t\t{\n\t\t\t\tfs = (fuzzyseperator_t *) GetClearedMemory(sizeof(fuzzyseperator_t));\n\t\t\t\tfs->index = 0;\n\t\t\t\tfs->value = MAX_INVENTORYVALUE;\n\t\t\t\tfs->next = NULL;\n\t\t\t\tfs->child = NULL;\n\t\t\t\tif (!ReadFuzzyWeight(source, fs))\n\t\t\t\t{\n\t\t\t\t\tFreeMemory(fs);\n\t\t\t\t\tFreeWeightConfig(config);\n\t\t\t\t\tFreeSource(source);\n\t\t\t\t\treturn NULL;\n\t\t\t\t} //end if\n\t\t\t\tconfig->weights[config->numweights].firstseperator = fs;\n\t\t\t} //end else if\n\t\t\telse\n\t\t\t{\n\t\t\t\tSourceError(source, \"invalid name %s\\n\", token.string);\n\t\t\t\tFreeWeightConfig(config);\n\t\t\t\tFreeSource(source);\n\t\t\t\treturn NULL;\n\t\t\t} //end else\n\t\t\tif (newindent)\n\t\t\t{\n\t\t\t\tif (!PC_ExpectTokenString(source, \"}\"))\n\t\t\t\t{\n\t\t\t\t\tFreeWeightConfig(config);\n\t\t\t\t\tFreeSource(source);\n\t\t\t\t\treturn NULL;\n\t\t\t\t} //end if\n\t\t\t} //end if\n\t\t\tconfig->numweights++;\n\t\t} //end if\n\t\telse\n\t\t{\n\t\t\tSourceError(source, \"invalid name %s\\n\", token.string);\n\t\t\tFreeWeightConfig(config);\n\t\t\tFreeSource(source);\n\t\t\treturn NULL;\n\t\t} //end else\n\t} //end while\n\t//free the source at the end of a pass\n\tFreeSource(source);\n\t//if the file was located in a pak file\n\tbotimport.Print(PRT_MESSAGE, \"loaded %s\\n\", filename);\n#ifdef DEBUG\n\tif (botDeveloper)\n\t{\n\t\tbotimport.Print(PRT_MESSAGE, \"weights loaded in %d msec\\n\", Sys_MilliSeconds() - starttime);\n\t} //end if\n#endif //DEBUG\n\t//\n\tif (!LibVarGetValue(\"bot_reloadcharacters\"))\n\t{\n\t\tweightFileList[avail] = config;\n\t} //end if\n\t//\n\treturn config;\n} //end of the function ReadWeightConfig\n#if 0\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nqboolean WriteFuzzyWeight(FILE *fp, fuzzyseperator_t *fs)\n{\n\tif (fs->type == WT_BALANCE)\n\t{\n\t\tif (fprintf(fp, \" return balance(\") < 0) return qfalse;\n\t\tif (!WriteFloat(fp, fs->weight)) return qfalse;\n\t\tif (fprintf(fp, \",\") < 0) return qfalse;\n\t\tif (!WriteFloat(fp, fs->minweight)) return qfalse;\n\t\tif (fprintf(fp, \",\") < 0) return qfalse;\n\t\tif (!WriteFloat(fp, fs->maxweight)) return qfalse;\n\t\tif (fprintf(fp, \");\\n\") < 0) return qfalse;\n\t} //end if\n\telse\n\t{\n\t\tif (fprintf(fp, \" return \") < 0) return qfalse;\n\t\tif (!WriteFloat(fp, fs->weight)) return qfalse;\n\t\tif (fprintf(fp, \";\\n\") < 0) return qfalse;\n\t} //end else\n\treturn qtrue;\n} //end of the function WriteFuzzyWeight\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nqboolean WriteFuzzySeperators_r(FILE *fp, fuzzyseperator_t *fs, int indent)\n{\n\tif (!WriteIndent(fp, indent)) return qfalse;\n\tif (fprintf(fp, \"switch(%d)\\n\", fs->index) < 0) return qfalse;\n\tif (!WriteIndent(fp, indent)) return qfalse;\n\tif (fprintf(fp, \"{\\n\") < 0) return qfalse;\n\tindent++;\n\tdo\n\t{\n\t\tif (!WriteIndent(fp, indent)) return qfalse;\n\t\tif (fs->next)\n\t\t{\n\t\t\tif (fprintf(fp, \"case %d:\", fs->value) < 0) return qfalse;\n\t\t} //end if\n\t\telse\n\t\t{\n\t\t\tif (fprintf(fp, \"default:\") < 0) return qfalse;\n\t\t} //end else\n\t\tif (fs->child)\n\t\t{\n\t\t\tif (fprintf(fp, \"\\n\") < 0) return qfalse;\n\t\t\tif (!WriteIndent(fp, indent)) return qfalse;\n\t\t\tif (fprintf(fp, \"{\\n\") < 0) return qfalse;\n\t\t\tif (!WriteFuzzySeperators_r(fp, fs->child, indent + 1)) return qfalse;\n\t\t\tif (!WriteIndent(fp, indent)) return qfalse;\n\t\t\tif (fs->next)\n\t\t\t{\n\t\t\t\tif (fprintf(fp, \"} //end case\\n\") < 0) return qfalse;\n\t\t\t} //end if\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (fprintf(fp, \"} //end default\\n\") < 0) return qfalse;\n\t\t\t} //end else\n\t\t} //end if\n\t\telse\n\t\t{\n\t\t\tif (!WriteFuzzyWeight(fp, fs)) return qfalse;\n\t\t} //end else\n\t\tfs = fs->next;\n\t} while(fs);\n\tindent--;\n\tif (!WriteIndent(fp, indent)) return qfalse;\n\tif (fprintf(fp, \"} //end switch\\n\") < 0) return qfalse;\n\treturn qtrue;\n} //end of the function WriteItemFuzzyWeights_r\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nqboolean WriteWeightConfig(char *filename, weightconfig_t *config)\n{\n\tint i;\n\tFILE *fp;\n\tweight_t *ifw;\n\n\tfp = fopen(filename, \"wb\");\n\tif (!fp) return qfalse;\n\n\tfor (i = 0; i < config->numweights; i++)\n\t{\n\t\tifw = &config->weights[i];\n\t\tif (fprintf(fp, \"\\nweight \\\"%s\\\"\\n\", ifw->name) < 0) return qfalse;\n\t\tif (fprintf(fp, \"{\\n\") < 0) return qfalse;\n\t\tif (ifw->firstseperator->index > 0)\n\t\t{\n\t\t\tif (!WriteFuzzySeperators_r(fp, ifw->firstseperator, 1)) return qfalse;\n\t\t} //end if\n\t\telse\n\t\t{\n\t\t\tif (!WriteIndent(fp, 1)) return qfalse;\n\t\t\tif (!WriteFuzzyWeight(fp, ifw->firstseperator)) return qfalse;\n\t\t} //end else\n\t\tif (fprintf(fp, \"} //end weight\\n\") < 0) return qfalse;\n\t} //end for\n\tfclose(fp);\n\treturn qtrue;\n} //end of the function WriteWeightConfig\n#endif\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint FindFuzzyWeight(weightconfig_t *wc, char *name)\n{\n\tint i;\n\n\tfor (i = 0; i < wc->numweights; i++)\n\t{\n\t\tif (!strcmp(wc->weights[i].name, name))\n\t\t{\n\t\t\treturn i;\n\t\t} //end if\n\t} //end if\n\treturn -1;\n} //end of the function FindFuzzyWeight\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nfloat FuzzyWeight_r(int *inventory, fuzzyseperator_t *fs)\n{\n\tfloat scale, w1, w2;\n\n\tif (inventory[fs->index] < fs->value)\n\t{\n\t\tif (fs->child) return FuzzyWeight_r(inventory, fs->child);\n\t\telse return fs->weight;\n\t} //end if\n\telse if (fs->next)\n\t{\n\t\tif (inventory[fs->index] < fs->next->value)\n\t\t{\n\t\t\t//first weight\n\t\t\tif (fs->child) w1 = FuzzyWeight_r(inventory, fs->child);\n\t\t\telse w1 = fs->weight;\n\t\t\t//second weight\n\t\t\tif (fs->next->child) w2 = FuzzyWeight_r(inventory, fs->next->child);\n\t\t\telse w2 = fs->next->weight;\n\t\t\t//the scale factor\n\t\t\tif(fs->next->value == MAX_INVENTORYVALUE) // is fs->next the default case?\n        \t\treturn w2;      // can't interpolate, return default weight\n\t\t\telse\n\t\t\t\tscale = (float) (inventory[fs->index] - fs->value) / (fs->next->value - fs->value);\n\t\t\t//scale between the two weights\n\t\t\treturn (1 - scale) * w1 + scale * w2;\n\t\t} //end if\n\t\treturn FuzzyWeight_r(inventory, fs->next);\n\t} //end else if\n\treturn fs->weight;\n} //end of the function FuzzyWeight_r\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nfloat FuzzyWeightUndecided_r(int *inventory, fuzzyseperator_t *fs)\n{\n\tfloat scale, w1, w2;\n\n\tif (inventory[fs->index] < fs->value)\n\t{\n\t\tif (fs->child) return FuzzyWeightUndecided_r(inventory, fs->child);\n\t\telse return fs->minweight + random() * (fs->maxweight - fs->minweight);\n\t} //end if\n\telse if (fs->next)\n\t{\n\t\tif (inventory[fs->index] < fs->next->value)\n\t\t{\n\t\t\t//first weight\n\t\t\tif (fs->child) w1 = FuzzyWeightUndecided_r(inventory, fs->child);\n\t\t\telse w1 = fs->minweight + random() * (fs->maxweight - fs->minweight);\n\t\t\t//second weight\n\t\t\tif (fs->next->child) w2 = FuzzyWeight_r(inventory, fs->next->child);\n\t\t\telse w2 = fs->next->minweight + random() * (fs->next->maxweight - fs->next->minweight);\n\t\t\t//the scale factor\n\t\t\tif(fs->next->value == MAX_INVENTORYVALUE) // is fs->next the default case?\n        \t\treturn w2;      // can't interpolate, return default weight\n\t\t\telse\n\t\t\t\tscale = (float) (inventory[fs->index] - fs->value) / (fs->next->value - fs->value);\n\t\t\t//scale between the two weights\n\t\t\treturn (1 - scale) * w1 + scale * w2;\n\t\t} //end if\n\t\treturn FuzzyWeightUndecided_r(inventory, fs->next);\n\t} //end else if\n\treturn fs->weight;\n} //end of the function FuzzyWeightUndecided_r\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nfloat FuzzyWeight(int *inventory, weightconfig_t *wc, int weightnum)\n{\n#ifdef EVALUATERECURSIVELY\n\treturn FuzzyWeight_r(inventory, wc->weights[weightnum].firstseperator);\n#else\n\tfuzzyseperator_t *s;\n\n\ts = wc->weights[weightnum].firstseperator;\n\tif (!s) return 0;\n\twhile(1)\n\t{\n\t\tif (inventory[s->index] < s->value)\n\t\t{\n\t\t\tif (s->child) s = s->child;\n\t\t\telse return s->weight;\n\t\t} //end if\n\t\telse\n\t\t{\n\t\t\tif (s->next) s = s->next;\n\t\t\telse return s->weight;\n\t\t} //end else\n\t} //end if\n\treturn 0;\n#endif\n} //end of the function FuzzyWeight\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nfloat FuzzyWeightUndecided(int *inventory, weightconfig_t *wc, int weightnum)\n{\n#ifdef EVALUATERECURSIVELY\n\treturn FuzzyWeightUndecided_r(inventory, wc->weights[weightnum].firstseperator);\n#else\n\tfuzzyseperator_t *s;\n\n\ts = wc->weights[weightnum].firstseperator;\n\tif (!s) return 0;\n\twhile(1)\n\t{\n\t\tif (inventory[s->index] < s->value)\n\t\t{\n\t\t\tif (s->child) s = s->child;\n\t\t\telse return s->minweight + random() * (s->maxweight - s->minweight);\n\t\t} //end if\n\t\telse\n\t\t{\n\t\t\tif (s->next) s = s->next;\n\t\t\telse return s->minweight + random() * (s->maxweight - s->minweight);\n\t\t} //end else\n\t} //end if\n\treturn 0;\n#endif\n} //end of the function FuzzyWeightUndecided\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid EvolveFuzzySeperator_r(fuzzyseperator_t *fs)\n{\n\tif (fs->child)\n\t{\n\t\tEvolveFuzzySeperator_r(fs->child);\n\t} //end if\n\telse if (fs->type == WT_BALANCE)\n\t{\n\t\t//every once in a while an evolution leap occurs, mutation\n\t\tif (random() < 0.01) fs->weight += crandom() * (fs->maxweight - fs->minweight);\n\t\telse fs->weight += crandom() * (fs->maxweight - fs->minweight) * 0.5;\n\t\t//modify bounds if necesary because of mutation\n\t\tif (fs->weight < fs->minweight) fs->minweight = fs->weight;\n\t\telse if (fs->weight > fs->maxweight) fs->maxweight = fs->weight;\n\t} //end else if\n\tif (fs->next) EvolveFuzzySeperator_r(fs->next);\n} //end of the function EvolveFuzzySeperator_r\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid EvolveWeightConfig(weightconfig_t *config)\n{\n\tint i;\n\n\tfor (i = 0; i < config->numweights; i++)\n\t{\n\t\tEvolveFuzzySeperator_r(config->weights[i].firstseperator);\n\t} //end for\n} //end of the function EvolveWeightConfig\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid ScaleFuzzySeperator_r(fuzzyseperator_t *fs, float scale)\n{\n\tif (fs->child)\n\t{\n\t\tScaleFuzzySeperator_r(fs->child, scale);\n\t} //end if\n\telse if (fs->type == WT_BALANCE)\n\t{\n\t\t//\n\t\tfs->weight = (float) (fs->maxweight + fs->minweight) * scale;\n\t\t//get the weight between bounds\n\t\tif (fs->weight < fs->minweight) fs->weight = fs->minweight;\n\t\telse if (fs->weight > fs->maxweight) fs->weight = fs->maxweight;\n\t} //end else if\n\tif (fs->next) ScaleFuzzySeperator_r(fs->next, scale);\n} //end of the function ScaleFuzzySeperator_r\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid ScaleWeight(weightconfig_t *config, char *name, float scale)\n{\n\tint i;\n\n\tif (scale < 0) scale = 0;\n\telse if (scale > 1) scale = 1;\n\tfor (i = 0; i < config->numweights; i++)\n\t{\n\t\tif (!strcmp(name, config->weights[i].name))\n\t\t{\n\t\t\tScaleFuzzySeperator_r(config->weights[i].firstseperator, scale);\n\t\t\tbreak;\n\t\t} //end if\n\t} //end for\n} //end of the function ScaleWeight\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid ScaleFuzzySeperatorBalanceRange_r(fuzzyseperator_t *fs, float scale)\n{\n\tif (fs->child)\n\t{\n\t\tScaleFuzzySeperatorBalanceRange_r(fs->child, scale);\n\t} //end if\n\telse if (fs->type == WT_BALANCE)\n\t{\n\t\tfloat mid = (fs->minweight + fs->maxweight) * 0.5;\n\t\t//get the weight between bounds\n\t\tfs->maxweight = mid + (fs->maxweight - mid) * scale;\n\t\tfs->minweight = mid + (fs->minweight - mid) * scale;\n\t\tif (fs->maxweight < fs->minweight)\n\t\t{\n\t\t\tfs->maxweight = fs->minweight;\n\t\t} //end if\n\t} //end else if\n\tif (fs->next) ScaleFuzzySeperatorBalanceRange_r(fs->next, scale);\n} //end of the function ScaleFuzzySeperatorBalanceRange_r\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid ScaleFuzzyBalanceRange(weightconfig_t *config, float scale)\n{\n\tint i;\n\n\tif (scale < 0) scale = 0;\n\telse if (scale > 100) scale = 100;\n\tfor (i = 0; i < config->numweights; i++)\n\t{\n\t\tScaleFuzzySeperatorBalanceRange_r(config->weights[i].firstseperator, scale);\n\t} //end for\n} //end of the function ScaleFuzzyBalanceRange\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint InterbreedFuzzySeperator_r(fuzzyseperator_t *fs1, fuzzyseperator_t *fs2,\n\t\t\t\t\t\t\t\tfuzzyseperator_t *fsout)\n{\n\tif (fs1->child)\n\t{\n\t\tif (!fs2->child || !fsout->child)\n\t\t{\n\t\t\tbotimport.Print(PRT_ERROR, \"cannot interbreed weight configs, unequal child\\n\");\n\t\t\treturn qfalse;\n\t\t} //end if\n\t\tif (!InterbreedFuzzySeperator_r(fs2->child, fs2->child, fsout->child))\n\t\t{\n\t\t\treturn qfalse;\n\t\t} //end if\n\t} //end if\n\telse if (fs1->type == WT_BALANCE)\n\t{\n\t\tif (fs2->type != WT_BALANCE || fsout->type != WT_BALANCE)\n\t\t{\n\t\t\tbotimport.Print(PRT_ERROR, \"cannot interbreed weight configs, unequal balance\\n\");\n\t\t\treturn qfalse;\n\t\t} //end if\n\t\tfsout->weight = (fs1->weight + fs2->weight) / 2;\n\t\tif (fsout->weight > fsout->maxweight) fsout->maxweight = fsout->weight;\n\t\tif (fsout->weight > fsout->minweight) fsout->minweight = fsout->weight;\n\t} //end else if\n\tif (fs1->next)\n\t{\n\t\tif (!fs2->next || !fsout->next)\n\t\t{\n\t\t\tbotimport.Print(PRT_ERROR, \"cannot interbreed weight configs, unequal next\\n\");\n\t\t\treturn qfalse;\n\t\t} //end if\n\t\tif (!InterbreedFuzzySeperator_r(fs1->next, fs2->next, fsout->next))\n\t\t{\n\t\t\treturn qfalse;\n\t\t} //end if\n\t} //end if\n\treturn qtrue;\n} //end of the function InterbreedFuzzySeperator_r\n//===========================================================================\n// config1 and config2 are interbreeded and stored in configout\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid InterbreedWeightConfigs(weightconfig_t *config1, weightconfig_t *config2,\n\t\t\t\t\t\t\t\tweightconfig_t *configout)\n{\n\tint i;\n\n\tif (config1->numweights != config2->numweights ||\n\t\tconfig1->numweights != configout->numweights)\n\t{\n\t\tbotimport.Print(PRT_ERROR, \"cannot interbreed weight configs, unequal numweights\\n\");\n\t\treturn;\n\t} //end if\n\tfor (i = 0; i < config1->numweights; i++)\n\t{\n\t\tInterbreedFuzzySeperator_r(config1->weights[i].firstseperator,\n\t\t\t\t\t\t\t\t\tconfig2->weights[i].firstseperator,\n\t\t\t\t\t\t\t\t\tconfigout->weights[i].firstseperator);\n\t} //end for\n} //end of the function InterbreedWeightConfigs\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid BotShutdownWeights(void)\n{\n\tint i;\n\n\tfor( i = 0; i < MAX_WEIGHT_FILES; i++ )\n\t{\n\t\tif (weightFileList[i])\n\t\t{\n\t\t\tFreeWeightConfig2(weightFileList[i]);\n\t\t\tweightFileList[i] = NULL;\n\t\t} //end if\n\t} //end for\n} //end of the function BotShutdownWeights\n"
  },
  {
    "path": "plugins/quake3/botlib/be_ai_weight.h",
    "content": "/*\n===========================================================================\nCopyright (C) 1999-2005 Id Software, Inc.\n\nThis file is part of Quake III Arena source code.\n\nQuake III Arena source code is free software; you can redistribute it\nand/or modify it under the terms of the GNU General Public License as\npublished by the Free Software Foundation; either version 2 of the License,\nor (at your option) any later version.\n\nQuake III Arena source code is distributed in the hope that it will be\nuseful, but WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Quake III Arena source code; if not, write to the Free Software\nFoundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n===========================================================================\n*/\n\n/*****************************************************************************\n * name:\t\tbe_ai_weight.h\n *\n * desc:\t\tfuzzy weights\n *\n * $Archive: /source/code/botlib/be_ai_weight.h $\n *\n *****************************************************************************/\n\n#define WT_BALANCE\t\t\t1\n#define MAX_WEIGHTS\t\t\t128\n\n//fuzzy seperator\ntypedef struct fuzzyseperator_s\n{\n\tint index;\n\tint value;\n\tint type;\n\tfloat weight;\n\tfloat minweight;\n\tfloat maxweight;\n\tstruct fuzzyseperator_s *child;\n\tstruct fuzzyseperator_s *next;\n} fuzzyseperator_t;\n\n//fuzzy weight\ntypedef struct weight_s\n{\n\tchar *name;\n\tstruct fuzzyseperator_s *firstseperator;\n} weight_t;\n\n//weight configuration\ntypedef struct weightconfig_s\n{\n\tint numweights;\n\tweight_t weights[MAX_WEIGHTS];\n\tchar\t\tfilename[MAX_QPATH];\n} weightconfig_t;\n\n//reads a weight configuration\nweightconfig_t *ReadWeightConfig(char *filename);\n//free a weight configuration\nvoid FreeWeightConfig(weightconfig_t *config);\n//writes a weight configuration, returns true if successfull\nqboolean WriteWeightConfig(char *filename, weightconfig_t *config);\n//find the fuzzy weight with the given name\nint FindFuzzyWeight(weightconfig_t *wc, char *name);\n//returns the fuzzy weight for the given inventory and weight\nfloat FuzzyWeight(int *inventory, weightconfig_t *wc, int weightnum);\nfloat FuzzyWeightUndecided(int *inventory, weightconfig_t *wc, int weightnum);\n//scales the weight with the given name\nvoid ScaleWeight(weightconfig_t *config, char *name, float scale);\n//scale the balance range\nvoid ScaleBalanceRange(weightconfig_t *config, float scale);\n//evolves the weight configuration\nvoid EvolveWeightConfig(weightconfig_t *config);\n//interbreed the weight configurations and stores the interbreeded one in configout\nvoid InterbreedWeightConfigs(weightconfig_t *config1, weightconfig_t *config2, weightconfig_t *configout);\n//frees cached weight configurations\nvoid BotShutdownWeights(void);\n"
  },
  {
    "path": "plugins/quake3/botlib/be_ea.c",
    "content": "/*\n===========================================================================\nCopyright (C) 1999-2005 Id Software, Inc.\n\nThis file is part of Quake III Arena source code.\n\nQuake III Arena source code is free software; you can redistribute it\nand/or modify it under the terms of the GNU General Public License as\npublished by the Free Software Foundation; either version 2 of the License,\nor (at your option) any later version.\n\nQuake III Arena source code is distributed in the hope that it will be\nuseful, but WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Quake III Arena source code; if not, write to the Free Software\nFoundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n===========================================================================\n*/\n\n/*****************************************************************************\n * name:\t\tbe_ea.c\n *\n * desc:\t\telementary actions\n *\n * $Archive: /MissionPack/code/botlib/be_ea.c $\n *\n *****************************************************************************/\n\n#include \"q_shared.h\"\n#include \"l_memory.h\"\n#include \"l_script.h\"\n#include \"l_precomp.h\"\n#include \"l_struct.h\"\n#include \"botlib.h\"\n#include \"be_interface.h\"\n#include \"be_ea.h\"\n\n#define MAX_USERMOVE\t\t\t\t400\n#define MAX_COMMANDARGUMENTS\t\t10\n\nbot_input_t *botinputs;\n\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid EA_Say(int client, char *str)\n{\n\tbotimport.BotClientCommand(client, va(\"say %s\", str) );\n} //end of the function EA_Say\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid EA_SayTeam(int client, char *str)\n{\n\tbotimport.BotClientCommand(client, va(\"say_team %s\", str));\n} //end of the function EA_SayTeam\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid EA_Tell(int client, int clientto, char *str)\n{\n\tbotimport.BotClientCommand(client, va(\"tell %d, %s\", clientto, str));\n} //end of the function EA_SayTeam\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid EA_UseItem(int client, char *it)\n{\n\tbotimport.BotClientCommand(client, va(\"use %s\", it));\n} //end of the function EA_UseItem\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid EA_DropItem(int client, char *it)\n{\n\tbotimport.BotClientCommand(client, va(\"drop %s\", it));\n} //end of the function EA_DropItem\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid EA_UseInv(int client, char *inv)\n{\n\tbotimport.BotClientCommand(client, va(\"invuse %s\", inv));\n} //end of the function EA_UseInv\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid EA_DropInv(int client, char *inv)\n{\n\tbotimport.BotClientCommand(client, va(\"invdrop %s\", inv));\n} //end of the function EA_DropInv\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid EA_Gesture(int client)\n{\n\tbot_input_t *bi;\n\n\tbi = &botinputs[client];\n\n\tbi->actionflags |= ACTION_GESTURE;\n} //end of the function EA_Gesture\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid EA_Command(int client, char *command)\n{\n\tbotimport.BotClientCommand(client, command);\n} //end of the function EA_Command\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid EA_SelectWeapon(int client, int weapon)\n{\n\tbot_input_t *bi;\n\n\tbi = &botinputs[client];\n\n\tbi->weapon = weapon;\n} //end of the function EA_SelectWeapon\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid EA_Attack(int client)\n{\n\tbot_input_t *bi;\n\n\tbi = &botinputs[client];\n\n\tbi->actionflags |= ACTION_ATTACK;\n} //end of the function EA_Attack\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid EA_Talk(int client)\n{\n\tbot_input_t *bi;\n\n\tbi = &botinputs[client];\n\n\tbi->actionflags |= ACTION_TALK;\n} //end of the function EA_Talk\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid EA_Use(int client)\n{\n\tbot_input_t *bi;\n\n\tbi = &botinputs[client];\n\n\tbi->actionflags |= ACTION_USE;\n} //end of the function EA_Use\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid EA_Respawn(int client)\n{\n\tbot_input_t *bi;\n\n\tbi = &botinputs[client];\n\n\tbi->actionflags |= ACTION_RESPAWN;\n} //end of the function EA_Respawn\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid EA_Jump(int client)\n{\n\tbot_input_t *bi;\n\n\tbi = &botinputs[client];\n\n\tif (bi->actionflags & ACTION_JUMPEDLASTFRAME)\n\t{\n\t\tbi->actionflags &= ~ACTION_JUMP;\n\t} //end if\n\telse\n\t{\n\t\tbi->actionflags |= ACTION_JUMP;\n\t} //end if\n} //end of the function EA_Jump\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid EA_DelayedJump(int client)\n{\n\tbot_input_t *bi;\n\n\tbi = &botinputs[client];\n\n\tif (bi->actionflags & ACTION_JUMPEDLASTFRAME)\n\t{\n\t\tbi->actionflags &= ~ACTION_DELAYEDJUMP;\n\t} //end if\n\telse\n\t{\n\t\tbi->actionflags |= ACTION_DELAYEDJUMP;\n\t} //end if\n} //end of the function EA_DelayedJump\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid EA_Crouch(int client)\n{\n\tbot_input_t *bi;\n\n\tbi = &botinputs[client];\n\n\tbi->actionflags |= ACTION_CROUCH;\n} //end of the function EA_Crouch\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid EA_Walk(int client)\n{\n\tbot_input_t *bi;\n\n\tbi = &botinputs[client];\n\n\tbi->actionflags |= ACTION_WALK;\n} //end of the function EA_Walk\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid EA_Action(int client, int action)\n{\n\tbot_input_t *bi;\n\n\tbi = &botinputs[client];\n\n\tbi->actionflags |= action;\n} //end of function EA_Action\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid EA_MoveUp(int client)\n{\n\tbot_input_t *bi;\n\n\tbi = &botinputs[client];\n\n\tbi->actionflags |= ACTION_MOVEUP;\n} //end of the function EA_MoveUp\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid EA_MoveDown(int client)\n{\n\tbot_input_t *bi;\n\n\tbi = &botinputs[client];\n\n\tbi->actionflags |= ACTION_MOVEDOWN;\n} //end of the function EA_MoveDown\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid EA_MoveForward(int client)\n{\n\tbot_input_t *bi;\n\n\tbi = &botinputs[client];\n\n\tbi->actionflags |= ACTION_MOVEFORWARD;\n} //end of the function EA_MoveForward\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid EA_MoveBack(int client)\n{\n\tbot_input_t *bi;\n\n\tbi = &botinputs[client];\n\n\tbi->actionflags |= ACTION_MOVEBACK;\n} //end of the function EA_MoveBack\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid EA_MoveLeft(int client)\n{\n\tbot_input_t *bi;\n\n\tbi = &botinputs[client];\n\n\tbi->actionflags |= ACTION_MOVELEFT;\n} //end of the function EA_MoveLeft\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid EA_MoveRight(int client)\n{\n\tbot_input_t *bi;\n\n\tbi = &botinputs[client];\n\n\tbi->actionflags |= ACTION_MOVERIGHT;\n} //end of the function EA_MoveRight\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid EA_Move(int client, vec3_t dir, float speed)\n{\n\tbot_input_t *bi;\n\n\tbi = &botinputs[client];\n\n\tVectorCopy(dir, bi->dir);\n\t//cap speed\n\tif (speed > MAX_USERMOVE) speed = MAX_USERMOVE;\n\telse if (speed < -MAX_USERMOVE) speed = -MAX_USERMOVE;\n\tbi->speed = speed;\n} //end of the function EA_Move\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid EA_View(int client, vec3_t viewangles)\n{\n\tbot_input_t *bi;\n\n\tbi = &botinputs[client];\n\n\tVectorCopy(viewangles, bi->viewangles);\n} //end of the function EA_View\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid EA_EndRegular(int client, float thinktime)\n{\n} //end of the function EA_EndRegular\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid EA_GetInput(int client, float thinktime, bot_input_t *input)\n{\n\tbot_input_t *bi;\n\n\tbi = &botinputs[client];\n\tbi->thinktime = thinktime;\n\tCom_Memcpy(input, bi, sizeof(bot_input_t));\n} //end of the function EA_GetInput\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid EA_ResetInput(int client)\n{\n\tbot_input_t *bi;\n\tint jumped = qfalse;\n\n\tbi = &botinputs[client];\n\n\tbi->thinktime = 0;\n\tVectorClear(bi->dir);\n\tbi->speed = 0;\n\tjumped = bi->actionflags & ACTION_JUMP;\n\tbi->actionflags = 0;\n\tif (jumped) bi->actionflags |= ACTION_JUMPEDLASTFRAME;\n} //end of the function EA_ResetInput\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint EA_Setup(void)\n{\n\t//initialize the bot inputs\n\tbotinputs = (bot_input_t *) GetClearedHunkMemory(\n\t\t\t\t\t\t\t\t\tbotlibglobals.maxclients * sizeof(bot_input_t));\n\treturn BLERR_NOERROR;\n} //end of the function EA_Setup\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid EA_Shutdown(void)\n{\n\tFreeMemory(botinputs);\n\tbotinputs = NULL;\n} //end of the function EA_Shutdown\n"
  },
  {
    "path": "plugins/quake3/botlib/be_ea.h",
    "content": "/*\n===========================================================================\nCopyright (C) 1999-2005 Id Software, Inc.\n\nThis file is part of Quake III Arena source code.\n\nQuake III Arena source code is free software; you can redistribute it\nand/or modify it under the terms of the GNU General Public License as\npublished by the Free Software Foundation; either version 2 of the License,\nor (at your option) any later version.\n\nQuake III Arena source code is distributed in the hope that it will be\nuseful, but WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Quake III Arena source code; if not, write to the Free Software\nFoundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n===========================================================================\n*/\n//\n\n/*****************************************************************************\n * name:\t\tbe_ea.h\n *\n * desc:\t\telementary actions\n *\n * $Archive: /source/code/botlib/be_ea.h $\n *\n *****************************************************************************/\n\n//ClientCommand elementary actions\nvoid EA_Say(int client, char *str);\nvoid EA_SayTeam(int client, char *str);\nvoid EA_Command(int client, char *command );\n\nvoid EA_Action(int client, int action);\nvoid EA_Crouch(int client);\nvoid EA_Walk(int client);\nvoid EA_MoveUp(int client);\nvoid EA_MoveDown(int client);\nvoid EA_MoveForward(int client);\nvoid EA_MoveBack(int client);\nvoid EA_MoveLeft(int client);\nvoid EA_MoveRight(int client);\nvoid EA_Attack(int client);\nvoid EA_Respawn(int client);\nvoid EA_Talk(int client);\nvoid EA_Gesture(int client);\nvoid EA_Use(int client);\n\n//regular elementary actions\nvoid EA_SelectWeapon(int client, int weapon);\nvoid EA_Jump(int client);\nvoid EA_DelayedJump(int client);\nvoid EA_Move(int client, vec3_t dir, float speed);\nvoid EA_View(int client, vec3_t viewangles);\n\n//send regular input to the server\nvoid EA_EndRegular(int client, float thinktime);\nvoid EA_GetInput(int client, float thinktime, bot_input_t *input);\nvoid EA_ResetInput(int client);\n//setup and shutdown routines\nint EA_Setup(void);\nvoid EA_Shutdown(void);\n"
  },
  {
    "path": "plugins/quake3/botlib/be_interface.c",
    "content": "/*\n===========================================================================\nCopyright (C) 1999-2005 Id Software, Inc.\n\nThis file is part of Quake III Arena source code.\n\nQuake III Arena source code is free software; you can redistribute it\nand/or modify it under the terms of the GNU General Public License as\npublished by the Free Software Foundation; either version 2 of the License,\nor (at your option) any later version.\n\nQuake III Arena source code is distributed in the hope that it will be\nuseful, but WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Quake III Arena source code; if not, write to the Free Software\nFoundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n===========================================================================\n*/\n\n/*****************************************************************************\n * name:\t\tbe_interface.c\n *\n * desc:\t\tbot library interface\n *\n * $Archive: /MissionPack/code/botlib/be_interface.c $\n *\n *****************************************************************************/\n\n#include \"q_shared.h\"\n#include \"l_memory.h\"\n#include \"l_log.h\"\n#include \"l_libvar.h\"\n#include \"l_script.h\"\n#include \"l_precomp.h\"\n#include \"l_struct.h\"\n#include \"aasfile.h\"\n#include \"botlib.h\"\n#include \"be_aas.h\"\n#include \"be_aas_funcs.h\"\n#include \"be_aas_def.h\"\n#include \"be_interface.h\"\n\n#include \"be_ea.h\"\n#include \"be_ai_weight.h\"\n#include \"be_ai_goal.h\"\n#include \"be_ai_move.h\"\n#include \"be_ai_weap.h\"\n#include \"be_ai_chat.h\"\n#include \"be_ai_char.h\"\n#include \"be_ai_gen.h\"\n\n//library globals in a structure\nbotlib_globals_t botlibglobals;\n\nbotlib_export_t be_botlib_export;\nbotlib_import_t botimport;\n//\nint botDeveloper;\n//qtrue if the library is setup\nint botlibsetup = qfalse;\n\n//===========================================================================\n//\n// several functions used by the exported functions\n//\n//===========================================================================\n\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint Sys_MilliSeconds(void)\n{\n\treturn clock() * 1000 / CLOCKS_PER_SEC;\n} //end of the function Sys_MilliSeconds\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nqboolean ValidClientNumber(int num, char *str)\n{\n\tif (num < 0 || num > botlibglobals.maxclients)\n\t{\n\t\t//weird: the disabled stuff results in a crash\n\t\tbotimport.Print(PRT_ERROR, \"%s: invalid client number %d, [0, %d]\\n\",\n\t\t\t\t\t\t\t\t\t\tstr, num, botlibglobals.maxclients);\n\t\treturn qfalse;\n\t} //end if\n\treturn qtrue;\n} //end of the function BotValidateClientNumber\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nqboolean ValidEntityNumber(int num, char *str)\n{\n\tif (num < 0 || num > botlibglobals.maxentities)\n\t{\n\t\tbotimport.Print(PRT_ERROR, \"%s: invalid entity number %d, [0, %d]\\n\",\n\t\t\t\t\t\t\t\t\t\tstr, num, botlibglobals.maxentities);\n\t\treturn qfalse;\n\t} //end if\n\treturn qtrue;\n} //end of the function BotValidateClientNumber\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nqboolean BotLibSetup(char *str)\n{\n\tif (!botlibglobals.botlibsetup)\n\t{\n\t\tbotimport.Print(PRT_ERROR, \"%s: bot library used before being setup\\n\", str);\n\t\treturn qfalse;\n\t} //end if\n\treturn qtrue;\n} //end of the function BotLibSetup\n\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint Export_BotLibSetup(void)\n{\n\tint\t\terrnum;\n\t\n\tbotDeveloper = LibVarGetValue(\"bot_developer\");\n \tmemset( &botlibglobals, 0, sizeof(botlibglobals) );\n\t//initialize byte swapping (litte endian etc.)\n//\tSwap_Init();\n\n\tif(botDeveloper)\n\t{\n\t\tchar *homedir, *gamedir, *basedir;\n\t\tchar logfilename[MAX_OSPATH];\n\n\t\thomedir = LibVarGetString(\"homedir\");\n\t\tgamedir = LibVarGetString(\"gamedir\");\n\t\tbasedir = LibVarGetString(\"com_basegame\");\n\n\t\tif (*homedir)\n\t\t{\n\t\t\tif(*gamedir)\n\t\t\t\tCom_sprintf(logfilename, sizeof(logfilename), \"%s%c%s%cbotlib.log\", homedir, PATH_SEP, gamedir, PATH_SEP);\n\t\t\telse if(*basedir)\n\t\t\t\tCom_sprintf(logfilename, sizeof(logfilename), \"%s%c%s%cbotlib.log\", homedir, PATH_SEP, basedir, PATH_SEP);\n\t\t\telse\n\t\t\t\tCom_sprintf(logfilename, sizeof(logfilename), \"%s%c\" BASEGAME \"%cbotlib.log\", homedir, PATH_SEP, PATH_SEP);\n\t\t}\n\t\telse\n\t\t\tCom_sprintf(logfilename, sizeof(logfilename), \"botlib.log\");\n\t\n\t\tLog_Open(logfilename);\n\t}\n\n\tbotimport.Print(PRT_MESSAGE, \"------- BotLib Initialization -------\\n\");\n\n\tbotlibglobals.maxclients = (int) LibVarValue(\"maxclients\", \"128\");\n\tbotlibglobals.maxentities = (int) LibVarValue(\"maxentities\", \"1024\");\n\n\terrnum = AAS_Setup();\t\t\t//be_aas_main.c\n\tif (errnum != BLERR_NOERROR) return errnum;\n\terrnum = EA_Setup();\t\t\t//be_ea.c\n\tif (errnum != BLERR_NOERROR) return errnum;\n\terrnum = BotSetupWeaponAI();\t//be_ai_weap.c\n\tif (errnum != BLERR_NOERROR)return errnum;\n\terrnum = BotSetupGoalAI();\t\t//be_ai_goal.c\n\tif (errnum != BLERR_NOERROR) return errnum;\n\terrnum = BotSetupChatAI();\t\t//be_ai_chat.c\n\tif (errnum != BLERR_NOERROR) return errnum;\n\terrnum = BotSetupMoveAI();\t\t//be_ai_move.c\n\tif (errnum != BLERR_NOERROR) return errnum;\n\n\tbotlibsetup = qtrue;\n\tbotlibglobals.botlibsetup = qtrue;\n\n\treturn BLERR_NOERROR;\n} //end of the function Export_BotLibSetup\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint Export_BotLibShutdown(void)\n{\n\tif (!BotLibSetup(\"BotLibShutdown\")) return BLERR_LIBRARYNOTSETUP;\n#ifndef DEMO\n\t//DumpFileCRCs();\n#endif //DEMO\n\t//\n\tBotShutdownChatAI();\t\t//be_ai_chat.c\n\tBotShutdownMoveAI();\t\t//be_ai_move.c\n\tBotShutdownGoalAI();\t\t//be_ai_goal.c\n\tBotShutdownWeaponAI();\t\t//be_ai_weap.c\n\tBotShutdownWeights();\t\t//be_ai_weight.c\n\tBotShutdownCharacters();\t//be_ai_char.c\n\t//shud down aas\n\tAAS_Shutdown();\n\t//shut down bot elemantary actions\n\tEA_Shutdown();\n\t//free all libvars\n\tLibVarDeAllocAll();\n\t//remove all global defines from the pre compiler\n\tPC_RemoveAllGlobalDefines();\n\n\t//dump all allocated memory\n//\tDumpMemory();\n#ifdef DEBUG\n\tPrintMemoryLabels();\n#endif\n\t//shut down library log file\n\tLog_Shutdown();\n\t//\n\tbotlibsetup = qfalse;\n\tbotlibglobals.botlibsetup = qfalse;\n\t// print any files still open\n\tPC_CheckOpenSourceHandles();\n\t//\n\treturn BLERR_NOERROR;\n} //end of the function Export_BotLibShutdown\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint Export_BotLibVarSet(char *var_name, char *value)\n{\n\tLibVarSet(var_name, value);\n\treturn BLERR_NOERROR;\n} //end of the function Export_BotLibVarSet\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint Export_BotLibVarGet(char *var_name, char *value, int size)\n{\n\tchar *varvalue;\n\n\tvarvalue = LibVarGetString(var_name);\n\tstrncpy(value, varvalue, size-1);\n\tvalue[size-1] = '\\0';\n\treturn BLERR_NOERROR;\n} //end of the function Export_BotLibVarGet\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint Export_BotLibStartFrame(float time)\n{\n\tif (!BotLibSetup(\"BotStartFrame\")) return BLERR_LIBRARYNOTSETUP;\n\treturn AAS_StartFrame(time);\n} //end of the function Export_BotLibStartFrame\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint Export_BotLibLoadMap(const char *mapname)\n{\n#ifdef DEBUG\n\tint starttime = Sys_MilliSeconds();\n#endif\n\tint errnum;\n\n\tif (!BotLibSetup(\"BotLoadMap\")) return BLERR_LIBRARYNOTSETUP;\n\t//\n\tbotimport.Print(PRT_MESSAGE, \"------------ Map Loading ------------\\n\");\n\t//startup AAS for the current map, model and sound index\n\terrnum = AAS_LoadMap(mapname);\n\tif (errnum != BLERR_NOERROR) return errnum;\n\t//initialize the items in the level\n\tBotInitLevelItems();\t\t//be_ai_goal.h\n\tBotSetBrushModelTypes();\t//be_ai_move.h\n\t//\n\tbotimport.Print(PRT_MESSAGE, \"-------------------------------------\\n\");\n#ifdef DEBUG\n\tbotimport.Print(PRT_MESSAGE, \"map loaded in %d msec\\n\", Sys_MilliSeconds() - starttime);\n#endif\n\t//\n\treturn BLERR_NOERROR;\n} //end of the function Export_BotLibLoadMap\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint Export_BotLibUpdateEntity(int ent, bot_entitystate_t *state)\n{\n\tif (!BotLibSetup(\"BotUpdateEntity\")) return BLERR_LIBRARYNOTSETUP;\n\tif (!ValidEntityNumber(ent, \"BotUpdateEntity\")) return BLERR_INVALIDENTITYNUMBER;\n\n\treturn AAS_UpdateEntity(ent, state);\n} //end of the function Export_BotLibUpdateEntity\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid AAS_TestMovementPrediction(int entnum, vec3_t origin, vec3_t dir);\nvoid ElevatorBottomCenter(aas_reachability_t *reach, vec3_t bottomcenter);\nint BotGetReachabilityToGoal(vec3_t origin, int areanum,\n\t\t\t\t\t\t\t\t\t  int lastgoalareanum, int lastareanum,\n\t\t\t\t\t\t\t\t\t  int *avoidreach, float *avoidreachtimes, int *avoidreachtries,\n\t\t\t\t\t\t\t\t\t  bot_goal_t *goal, int travelflags, int movetravelflags,\n\t\t\t\t\t\t\t\t\t  struct bot_avoidspot_s *avoidspots, int numavoidspots, int *flags);\n\nint AAS_PointLight(vec3_t origin, int *red, int *green, int *blue);\n\nint AAS_TraceAreas(vec3_t start, vec3_t end, int *areas, vec3_t *points, int maxareas);\n\nint AAS_Reachability_WeaponJump(int area1num, int area2num);\n\nint BotFuzzyPointReachabilityArea(vec3_t origin);\n\nfloat BotGapDistance(vec3_t origin, vec3_t hordir, int entnum);\n\nvoid AAS_FloodAreas(vec3_t origin);\n\nint BotExportTest(int parm0, char *parm1, vec3_t parm2, vec3_t parm3)\n{\n\n//\treturn AAS_PointLight(parm2, NULL, NULL, NULL);\n\n#ifdef DEBUG\n\tstatic int area = -1;\n\tstatic int line[2];\n\tint newarea, i, highlightarea, flood;\n//\tint reachnum;\n\tvec3_t eye, forward, right, origin;\n//\tvec3_t bottomcenter;\n//\taas_trace_t trace;\n//\taas_face_t *face;\n//\taas_entity_t *ent;\n//\tbsp_trace_t bsptrace;\n//\taas_reachability_t reach;\n//\tbot_goal_t goal;\n\n\t// clock_t start_time, end_time;\n\t//vec3_t mins = {-16, -16, -24};\n\t//vec3_t maxs = {16, 16, 32};\n\n//\tint areas[10], numareas;\n\n\n\t//return 0;\n\n\tif (!aasworld.loaded) return 0;\n\n\t/*\n\tif (parm0 & 1)\n\t{\n\t\tAAS_ClearShownPolygons();\n\t\tAAS_FloodAreas(parm2);\n\t} //end if\n\treturn 0;\n\t*/\n\tfor (i = 0; i < 2; i++) if (!line[i]) line[i] = botimport.DebugLineCreate();\n\n//\tAAS_ClearShownDebugLines();\n\n\t//if (AAS_AgainstLadder(parm2)) botimport.Print(PRT_MESSAGE, \"against ladder\\n\");\n\t//BotOnGround(parm2, PRESENCE_NORMAL, 1, &newarea, &newarea);\n\t//botimport.Print(PRT_MESSAGE, \"%f %f %f\\n\", parm2[0], parm2[1], parm2[2]);\n\t//*\n\thighlightarea = LibVarGetValue(\"bot_highlightarea\");\n\tif (highlightarea > 0)\n\t{\n\t\tnewarea = highlightarea;\n\t} //end if\n\telse\n\t{\n\t\tVectorCopy(parm2, origin);\n\t\torigin[2] += 0.5;\n\t\t//newarea = AAS_PointAreaNum(origin);\n\t\tnewarea = BotFuzzyPointReachabilityArea(origin);\n\t} //end else\n\n\tbotimport.Print(PRT_MESSAGE, \"\\rtravel time to goal (%d) = %d  \", botlibglobals.goalareanum,\n\t\tAAS_AreaTravelTimeToGoalArea(newarea, origin, botlibglobals.goalareanum, TFL_DEFAULT));\n\t//newarea = BotReachabilityArea(origin, qtrue);\n\tif (newarea != area)\n\t{\n\t\tbotimport.Print(PRT_MESSAGE, \"origin = %f, %f, %f\\n\", origin[0], origin[1], origin[2]);\n\t\tarea = newarea;\n\t\tbotimport.Print(PRT_MESSAGE, \"new area %d, cluster %d, presence type %d\\n\",\n\t\t\t\t\tarea, AAS_AreaCluster(area), AAS_PointPresenceType(origin));\n\t\tbotimport.Print(PRT_MESSAGE, \"area contents: \");\n\t\tif (aasworld.areasettings[area].contents & AREACONTENTS_WATER)\n\t\t{\n\t\t\tbotimport.Print(PRT_MESSAGE, \"water &\");\n\t\t} //end if\n\t\tif (aasworld.areasettings[area].contents & AREACONTENTS_LAVA)\n\t\t{\n\t\t\tbotimport.Print(PRT_MESSAGE, \"lava &\");\n\t\t} //end if\n\t\tif (aasworld.areasettings[area].contents & AREACONTENTS_SLIME)\n\t\t{\n\t\t\tbotimport.Print(PRT_MESSAGE, \"slime &\");\n\t\t} //end if\n\t\tif (aasworld.areasettings[area].contents & AREACONTENTS_JUMPPAD)\n\t\t{\n\t\t\tbotimport.Print(PRT_MESSAGE, \"jump pad &\");\n\t\t} //end if\n\t\tif (aasworld.areasettings[area].contents & AREACONTENTS_CLUSTERPORTAL)\n\t\t{\n\t\t\tbotimport.Print(PRT_MESSAGE, \"cluster portal &\");\n\t\t} //end if\n\t\tif (aasworld.areasettings[area].contents & AREACONTENTS_VIEWPORTAL)\n\t\t{\n\t\t\tbotimport.Print(PRT_MESSAGE, \"view portal &\");\n\t\t} //end if\n\t\tif (aasworld.areasettings[area].contents & AREACONTENTS_DONOTENTER)\n\t\t{\n\t\t\tbotimport.Print(PRT_MESSAGE, \"do not enter &\");\n\t\t} //end if\n\t\tif (aasworld.areasettings[area].contents & AREACONTENTS_MOVER)\n\t\t{\n\t\t\tbotimport.Print(PRT_MESSAGE, \"mover &\");\n\t\t} //end if\n\t\tif (!aasworld.areasettings[area].contents)\n\t\t{\n\t\t\tbotimport.Print(PRT_MESSAGE, \"empty\");\n\t\t} //end if\n\t\tbotimport.Print(PRT_MESSAGE, \"\\n\");\n\t\tbotimport.Print(PRT_MESSAGE, \"travel time to goal (%d) = %d\\n\", botlibglobals.goalareanum,\n\t\t\t\t\tAAS_AreaTravelTimeToGoalArea(newarea, origin, botlibglobals.goalareanum, TFL_DEFAULT|TFL_ROCKETJUMP));\n\t\t/*\n\t\tVectorCopy(origin, end);\n\t\tend[2] += 5;\n\t\tnumareas = AAS_TraceAreas(origin, end, areas, NULL, 10);\n\t\tAAS_TraceClientBBox(origin, end, PRESENCE_CROUCH, -1);\n\t\tbotimport.Print(PRT_MESSAGE, \"num areas = %d, area = %d\\n\", numareas, areas[0]);\n\t\t*/\n\t\t/*\n\t\tbotlibglobals.goalareanum = newarea;\n\t\tVectorCopy(parm2, botlibglobals.goalorigin);\n\t\tbotimport.Print(PRT_MESSAGE, \"new goal %2.1f %2.1f %2.1f area %d\\n\",\n\t\t\t\t\t\t\t\torigin[0], origin[1], origin[2], newarea);\n\t\t*/\n\t} //end if\n\t//*\n\tflood = LibVarGetValue(\"bot_flood\");\n\tif (parm0 & 1)\n\t{\n\t\tif (flood)\n\t\t{\n\t\t\tAAS_ClearShownPolygons();\n\t\t\tAAS_ClearShownDebugLines();\n\t\t\tAAS_FloodAreas(parm2);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tbotlibglobals.goalareanum = newarea;\n\t\t\tVectorCopy(parm2, botlibglobals.goalorigin);\n\t\t\tbotimport.Print(PRT_MESSAGE, \"new goal %2.1f %2.1f %2.1f area %d\\n\",\n\t\t\t\t\t\t\t\t\torigin[0], origin[1], origin[2], newarea);\n\t\t}\n\t} //end if*/\n\tif (flood)\n\t\treturn 0;\n//\tif (parm0 & BUTTON_USE)\n//\t{\n//\t\tbotlibglobals.runai = !botlibglobals.runai;\n//\t\tif (botlibglobals.runai) botimport.Print(PRT_MESSAGE, \"started AI\\n\");\n//\t\telse botimport.Print(PRT_MESSAGE, \"stopped AI\\n\");\n\t\t//* /\n\t\t/*\n\t\tgoal.areanum = botlibglobals.goalareanum;\n\t\treachnum = BotGetReachabilityToGoal(parm2, newarea, 1,\n\t\t\t\t\t\t\t\t\t\tms.avoidreach, ms.avoidreachtimes,\n\t\t\t\t\t\t\t\t\t\t&goal, TFL_DEFAULT);\n\t\tif (!reachnum)\n\t\t{\n\t\t\tbotimport.Print(PRT_MESSAGE, \"goal not reachable\\n\");\n\t\t} //end if\n\t\telse\n\t\t{\n\t\t\tAAS_ReachabilityFromNum(reachnum, &reach);\n\t\t\tAAS_ClearShownDebugLines();\n\t\t\tAAS_ShowArea(area, qtrue);\n\t\t\tAAS_ShowArea(reach.areanum, qtrue);\n\t\t\tAAS_DrawCross(reach.start, 6, LINECOLOR_BLUE);\n\t\t\tAAS_DrawCross(reach.end, 6, LINECOLOR_RED);\n\t\t\t//\n\t\t\tif ((reach.traveltype & TRAVELTYPE_MASK) == TRAVEL_ELEVATOR)\n\t\t\t{\n\t\t\t\tElevatorBottomCenter(&reach, bottomcenter);\n\t\t\t\tAAS_DrawCross(bottomcenter, 10, LINECOLOR_GREEN);\n\t\t\t} //end if\n\t\t} //end else*/\n//\t\tbotimport.Print(PRT_MESSAGE, \"travel time to goal = %d\\n\",\n//\t\t\t\t\tAAS_AreaTravelTimeToGoalArea(area, origin, botlibglobals.goalareanum, TFL_DEFAULT));\n//\t\tbotimport.Print(PRT_MESSAGE, \"test rj from 703 to 716\\n\");\n//\t\tAAS_Reachability_WeaponJump(703, 716);\n//\t} //end if*/\n\n/*\tface = AAS_AreaGroundFace(newarea, parm2);\n\tif (face)\n\t{\n\t\tAAS_ShowFace(face - aasworld.faces);\n\t} //end if*/\n\t/*\n\tAAS_ClearShownDebugLines();\n\tAAS_ShowArea(newarea, parm0 & BUTTON_USE);\n\tAAS_ShowReachableAreas(area);\n\t*/\n\tAAS_ClearShownPolygons();\n\tAAS_ClearShownDebugLines();\n\tAAS_ShowAreaPolygons(newarea, 1, parm0 & 4);\n\tif (parm0 & 2) AAS_ShowReachableAreas(area);\n\telse\n\t{\n\t\tstatic int lastgoalareanum, lastareanum;\n\t\tstatic int avoidreach[MAX_AVOIDREACH];\n\t\tstatic float avoidreachtimes[MAX_AVOIDREACH];\n\t\tstatic int avoidreachtries[MAX_AVOIDREACH];\n\t\tint reachnum, resultFlags;\n\t\tbot_goal_t goal;\n\t\taas_reachability_t reach;\n\n\t\t/*\n\t\tgoal.areanum = botlibglobals.goalareanum;\n\t\tVectorCopy(botlibglobals.goalorigin, goal.origin);\n\t\treachnum = BotGetReachabilityToGoal(origin, newarea,\n\t\t\t\t\t\t\t\t\t  lastgoalareanum, lastareanum,\n\t\t\t\t\t\t\t\t\t  avoidreach, avoidreachtimes, avoidreachtries,\n\t\t\t\t\t\t\t\t\t  &goal, TFL_DEFAULT|TFL_FUNCBOB|TFL_ROCKETJUMP, TFL_DEFAULT|TFL_FUNCBOB|TFL_ROCKETJUMP,\n\t\t\t\t\t\t\t\t\t  NULL, 0, &resultFlags);\n\t\tAAS_ReachabilityFromNum(reachnum, &reach);\n\t\tAAS_ShowReachability(&reach);\n\t\t*/\n\t\tint curarea;\n\t\tvec3_t curorigin;\n\n\t\tgoal.areanum = botlibglobals.goalareanum;\n\t\tVectorCopy(botlibglobals.goalorigin, goal.origin);\n\t\tVectorCopy(origin, curorigin);\n\t\tcurarea = newarea;\n\t\tfor ( i = 0; i < 100; i++ ) {\n\t\t\tif ( curarea == goal.areanum ) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\treachnum = BotGetReachabilityToGoal(curorigin, curarea,\n\t\t\t\t\t\t\t\t\t\t  lastgoalareanum, lastareanum,\n\t\t\t\t\t\t\t\t\t\t  avoidreach, avoidreachtimes, avoidreachtries,\n\t\t\t\t\t\t\t\t\t\t  &goal, TFL_DEFAULT|TFL_FUNCBOB|TFL_ROCKETJUMP, TFL_DEFAULT|TFL_FUNCBOB|TFL_ROCKETJUMP,\n\t\t\t\t\t\t\t\t\t\t  NULL, 0, &resultFlags);\n\t\t\tAAS_ReachabilityFromNum(reachnum, &reach);\n\t\t\tAAS_ShowReachability(&reach);\n\t\t\tVectorCopy(reach.end, origin);\n\t\t\tlastareanum = curarea;\n\t\t\tcurarea = reach.areanum;\n\t\t}\n\t} //end else\n\tVectorClear(forward);\n\t//BotGapDistance(origin, forward, 0);\n\t/*\n\tif (parm0 & BUTTON_USE)\n\t{\n\t\tbotimport.Print(PRT_MESSAGE, \"test rj from 703 to 716\\n\");\n\t\tAAS_Reachability_WeaponJump(703, 716);\n\t} //end if*/\n\n\tAngleVectors(parm3, forward, right, NULL);\n\t//get the eye 16 units to the right of the origin\n\tVectorMA(parm2, 8, right, eye);\n\t//get the eye 24 units up\n\teye[2] += 24;\n\t//get the end point for the line to be traced\n\t//VectorMA(eye, 800, forward, end);\n\n//\tAAS_TestMovementPrediction(1, parm2, forward);\n/*\n    //trace the line to find the hit point\n\ttrace = AAS_TraceClientBBox(eye, end, PRESENCE_NORMAL, 1);\n\tif (!line[0]) line[0] = botimport.DebugLineCreate();\n\tbotimport.DebugLineShow(line[0], eye, trace.endpos, LINECOLOR_BLUE);\n\t//\n\tAAS_ClearShownDebugLines();\n\tif (trace.ent)\n\t{\n\t\tent = &aasworld.entities[trace.ent];\n\t\tAAS_ShowBoundingBox(ent->origin, ent->mins, ent->maxs);\n\t} //end if\n*/\n\n/*\n\tstart_time = clock();\n\tfor (i = 0; i < 2000; i++)\n\t{\n\t\tAAS_Trace2(eye, mins, maxs, end, 1, MASK_PLAYERSOLID);\n//\t\tAAS_TraceClientBBox(eye, end, PRESENCE_NORMAL, 1);\n\t} //end for\n\tend_time = clock();\n\tbotimport.Print(PRT_MESSAGE, \"me %lu clocks, %lu CLOCKS_PER_SEC\\n\", end_time - start_time, CLOCKS_PER_SEC);\n\tstart_time = clock();\n\tfor (i = 0; i < 2000; i++)\n\t{\n\t\tAAS_Trace(eye, mins, maxs, end, 1, MASK_PLAYERSOLID);\n\t} //end for\n\tend_time = clock();\n\tbotimport.Print(PRT_MESSAGE, \"id %lu clocks, %lu CLOCKS_PER_SEC\\n\", end_time - start_time, CLOCKS_PER_SEC);\n*/\n\n    // TTimo: nested comments are BAD for gcc -Werror, use #if 0 instead..\n#if 0\n\tAAS_ClearShownDebugLines();\n\t//bsptrace = AAS_Trace(eye, NULL, NULL, end, 1, MASK_PLAYERSOLID);\n\tbsptrace = AAS_Trace(eye, mins, maxs, end, 1, MASK_PLAYERSOLID);\n\tif (!line[0]) line[0] = botimport.DebugLineCreate();\n\tbotimport.DebugLineShow(line[0], eye, bsptrace.endpos, LINECOLOR_YELLOW);\n\tif (bsptrace.fraction < 1.0)\n\t{\n\t\tface = AAS_TraceEndFace(&trace);\n\t\tif (face)\n\t\t{\n\t\t\tAAS_ShowFace(face - aasworld.faces);\n\t\t} //end if\n\t\t\n\t\tAAS_DrawPlaneCross(bsptrace.endpos,\n\t\t\t\t\t\t\t\t\tbsptrace.plane.normal,\n\t\t\t\t\t\t\t\t\tbsptrace.plane.dist + bsptrace.exp_dist,\n\t\t\t\t\t\t\t\t\tbsptrace.plane.type, LINECOLOR_GREEN);\n\t\tif (trace.ent)\n\t\t{\n\t\t\tent = &aasworld.entities[trace.ent];\n\t\t\tAAS_ShowBoundingBox(ent->origin, ent->mins, ent->maxs);\n\t\t} //end if\n\t} //end if\n\t//bsptrace = AAS_Trace2(eye, NULL, NULL, end, 1, MASK_PLAYERSOLID);\n\tbsptrace = AAS_Trace2(eye, mins, maxs, end, 1, MASK_PLAYERSOLID);\n\tbotimport.DebugLineShow(line[1], eye, bsptrace.endpos, LINECOLOR_BLUE);\n\tif (bsptrace.fraction < 1.0)\n\t{\n\t\tAAS_DrawPlaneCross(bsptrace.endpos,\n\t\t\t\t\t\t\t\t\tbsptrace.plane.normal,\n\t\t\t\t\t\t\t\t\tbsptrace.plane.dist,// + bsptrace.exp_dist,\n\t\t\t\t\t\t\t\t\tbsptrace.plane.type, LINECOLOR_RED);\n\t\tif (bsptrace.ent)\n\t\t{\n\t\t\tent = &aasworld.entities[bsptrace.ent];\n\t\t\tAAS_ShowBoundingBox(ent->origin, ent->mins, ent->maxs);\n\t\t} //end if\n\t} //end if\n#endif\n#endif\n\treturn 0;\n} //end of the function BotExportTest\n\n\n/*\n============\nInit_AAS_Export\n============\n*/\nstatic void Init_AAS_Export( aas_export_t *aas ) {\n\t//--------------------------------------------\n\t// be_aas_entity.c\n\t//--------------------------------------------\n\taas->AAS_EntityInfo = AAS_EntityInfo;\n\t//--------------------------------------------\n\t// be_aas_main.c\n\t//--------------------------------------------\n\taas->AAS_Initialized = AAS_Initialized;\n\taas->AAS_PresenceTypeBoundingBox = AAS_PresenceTypeBoundingBox;\n\taas->AAS_Time = AAS_Time;\n\t//--------------------------------------------\n\t// be_aas_sample.c\n\t//--------------------------------------------\n\taas->AAS_PointAreaNum = AAS_PointAreaNum;\n\taas->AAS_PointReachabilityAreaIndex = AAS_PointReachabilityAreaIndex;\n\taas->AAS_TraceAreas = AAS_TraceAreas;\n\taas->AAS_BBoxAreas = AAS_BBoxAreas;\n\taas->AAS_AreaInfo = AAS_AreaInfo;\n\t//--------------------------------------------\n\t// be_aas_bspq3.c\n\t//--------------------------------------------\n\taas->AAS_PointContents = AAS_PointContents;\n\taas->AAS_NextBSPEntity = AAS_NextBSPEntity;\n\taas->AAS_ValueForBSPEpairKey = AAS_ValueForBSPEpairKey;\n\taas->AAS_VectorForBSPEpairKey = AAS_VectorForBSPEpairKey;\n\taas->AAS_FloatForBSPEpairKey = AAS_FloatForBSPEpairKey;\n\taas->AAS_IntForBSPEpairKey = AAS_IntForBSPEpairKey;\n\t//--------------------------------------------\n\t// be_aas_reach.c\n\t//--------------------------------------------\n\taas->AAS_AreaReachability = AAS_AreaReachability;\n\t//--------------------------------------------\n\t// be_aas_route.c\n\t//--------------------------------------------\n\taas->AAS_AreaTravelTimeToGoalArea = AAS_AreaTravelTimeToGoalArea;\n\taas->AAS_EnableRoutingArea = AAS_EnableRoutingArea;\n\taas->AAS_PredictRoute = AAS_PredictRoute;\n\t//--------------------------------------------\n\t// be_aas_altroute.c\n\t//--------------------------------------------\n\taas->AAS_AlternativeRouteGoals = AAS_AlternativeRouteGoals;\n\t//--------------------------------------------\n\t// be_aas_move.c\n\t//--------------------------------------------\n\taas->AAS_Swimming = AAS_Swimming;\n\taas->AAS_PredictClientMovement = AAS_PredictClientMovement;\n}\n\n  \n/*\n============\nInit_EA_Export\n============\n*/\nstatic void Init_EA_Export( ea_export_t *ea ) {\n\t//ClientCommand elementary actions\n\tea->EA_Command = EA_Command;\n\tea->EA_Say = EA_Say;\n\tea->EA_SayTeam = EA_SayTeam;\n\n\tea->EA_Action = EA_Action;\n\tea->EA_Gesture = EA_Gesture;\n\tea->EA_Talk = EA_Talk;\n\tea->EA_Attack = EA_Attack;\n\tea->EA_Use = EA_Use;\n\tea->EA_Respawn = EA_Respawn;\n\tea->EA_Crouch = EA_Crouch;\n\tea->EA_MoveUp = EA_MoveUp;\n\tea->EA_MoveDown = EA_MoveDown;\n\tea->EA_MoveForward = EA_MoveForward;\n\tea->EA_MoveBack = EA_MoveBack;\n\tea->EA_MoveLeft = EA_MoveLeft;\n\tea->EA_MoveRight = EA_MoveRight;\n\n\tea->EA_SelectWeapon = EA_SelectWeapon;\n\tea->EA_Jump = EA_Jump;\n\tea->EA_DelayedJump = EA_DelayedJump;\n\tea->EA_Move = EA_Move;\n\tea->EA_View = EA_View;\n\tea->EA_GetInput = EA_GetInput;\n\tea->EA_EndRegular = EA_EndRegular;\n\tea->EA_ResetInput = EA_ResetInput;\n}\n\n\n/*\n============\nInit_AI_Export\n============\n*/\nstatic void Init_AI_Export( ai_export_t *ai ) {\n\t//-----------------------------------\n\t// be_ai_char.h\n\t//-----------------------------------\n\tai->BotLoadCharacter = BotLoadCharacter;\n\tai->BotFreeCharacter = BotFreeCharacter;\n\tai->Characteristic_Float = Characteristic_Float;\n\tai->Characteristic_BFloat = Characteristic_BFloat;\n\tai->Characteristic_Integer = Characteristic_Integer;\n\tai->Characteristic_BInteger = Characteristic_BInteger;\n\tai->Characteristic_String = Characteristic_String;\n\t//-----------------------------------\n\t// be_ai_chat.h\n\t//-----------------------------------\n\tai->BotAllocChatState = BotAllocChatState;\n\tai->BotFreeChatState = BotFreeChatState;\n\tai->BotQueueConsoleMessage = BotQueueConsoleMessage;\n\tai->BotRemoveConsoleMessage = BotRemoveConsoleMessage;\n\tai->BotNextConsoleMessage = BotNextConsoleMessage;\n\tai->BotNumConsoleMessages = BotNumConsoleMessages;\n\tai->BotInitialChat = BotInitialChat;\n\tai->BotNumInitialChats = BotNumInitialChats;\n\tai->BotReplyChat = BotReplyChat;\n\tai->BotChatLength = BotChatLength;\n\tai->BotEnterChat = BotEnterChat;\n\tai->BotGetChatMessage = BotGetChatMessage;\n\tai->StringContains = StringContains;\n\tai->BotFindMatch = BotFindMatch;\n\tai->BotMatchVariable = BotMatchVariable;\n\tai->UnifyWhiteSpaces = UnifyWhiteSpaces;\n\tai->BotReplaceSynonyms = BotReplaceSynonyms;\n\tai->BotLoadChatFile = BotLoadChatFile;\n\tai->BotSetChatGender = BotSetChatGender;\n\tai->BotSetChatName = BotSetChatName;\n\t//-----------------------------------\n\t// be_ai_goal.h\n\t//-----------------------------------\n\tai->BotResetGoalState = BotResetGoalState;\n\tai->BotResetAvoidGoals = BotResetAvoidGoals;\n\tai->BotRemoveFromAvoidGoals = BotRemoveFromAvoidGoals;\n\tai->BotPushGoal = BotPushGoal;\n\tai->BotPopGoal = BotPopGoal;\n\tai->BotEmptyGoalStack = BotEmptyGoalStack;\n\tai->BotDumpAvoidGoals = BotDumpAvoidGoals;\n\tai->BotDumpGoalStack = BotDumpGoalStack;\n\tai->BotGoalName = BotGoalName;\n\tai->BotGetTopGoal = BotGetTopGoal;\n\tai->BotGetSecondGoal = BotGetSecondGoal;\n\tai->BotChooseLTGItem = BotChooseLTGItem;\n\tai->BotChooseNBGItem = BotChooseNBGItem;\n\tai->BotTouchingGoal = BotTouchingGoal;\n\tai->BotItemGoalInVisButNotVisible = BotItemGoalInVisButNotVisible;\n\tai->BotGetLevelItemGoal = BotGetLevelItemGoal;\n\tai->BotGetNextCampSpotGoal = BotGetNextCampSpotGoal;\n\tai->BotGetMapLocationGoal = BotGetMapLocationGoal;\n\tai->BotAvoidGoalTime = BotAvoidGoalTime;\n\tai->BotSetAvoidGoalTime = BotSetAvoidGoalTime;\n\tai->BotInitLevelItems = BotInitLevelItems;\n\tai->BotUpdateEntityItems = BotUpdateEntityItems;\n\tai->BotLoadItemWeights = BotLoadItemWeights;\n\tai->BotFreeItemWeights = BotFreeItemWeights;\n\tai->BotInterbreedGoalFuzzyLogic = BotInterbreedGoalFuzzyLogic;\n\tai->BotSaveGoalFuzzyLogic = BotSaveGoalFuzzyLogic;\n\tai->BotMutateGoalFuzzyLogic = BotMutateGoalFuzzyLogic;\n\tai->BotAllocGoalState = BotAllocGoalState;\n\tai->BotFreeGoalState = BotFreeGoalState;\n\t//-----------------------------------\n\t// be_ai_move.h\n\t//-----------------------------------\n\tai->BotResetMoveState = BotResetMoveState;\n\tai->BotMoveToGoal = BotMoveToGoal;\n\tai->BotMoveInDirection = BotMoveInDirection;\n\tai->BotResetAvoidReach = BotResetAvoidReach;\n\tai->BotResetLastAvoidReach = BotResetLastAvoidReach;\n\tai->BotReachabilityArea = BotReachabilityArea;\n\tai->BotMovementViewTarget = BotMovementViewTarget;\n\tai->BotPredictVisiblePosition = BotPredictVisiblePosition;\n\tai->BotAllocMoveState = BotAllocMoveState;\n\tai->BotFreeMoveState = BotFreeMoveState;\n\tai->BotInitMoveState = BotInitMoveState;\n\tai->BotAddAvoidSpot = BotAddAvoidSpot;\n\t//-----------------------------------\n\t// be_ai_weap.h\n\t//-----------------------------------\n\tai->BotChooseBestFightWeapon = BotChooseBestFightWeapon;\n\tai->BotGetWeaponInfo = BotGetWeaponInfo;\n\tai->BotLoadWeaponWeights = BotLoadWeaponWeights;\n\tai->BotAllocWeaponState = BotAllocWeaponState;\n\tai->BotFreeWeaponState = BotFreeWeaponState;\n\tai->BotResetWeaponState = BotResetWeaponState;\n\t//-----------------------------------\n\t// be_ai_gen.h\n\t//-----------------------------------\n\tai->GeneticParentsAndChildSelection = GeneticParentsAndChildSelection;\n}\n\n\n/*\n============\nGetBotLibAPI\n============\n*/\n#ifdef EXTERNALBOTLIB\n#ifdef _WIN32\n\t__declspec(dllexport)\n#else\n\t__attribute__((visibility(\"default\")))\n#endif\n#endif\nbotlib_export_t *QDECL GetBotLibAPI(int apiVersion, botlib_import_t *import) {\n\tassert(import);\n\tbotimport = *import;\n\tassert(botimport.Print);\n\n\tCom_Memset( &be_botlib_export, 0, sizeof( be_botlib_export ) );\n\n\tif ( apiVersion != BOTLIB_API_VERSION ) {\n\t\tbotimport.Print( PRT_ERROR, \"Mismatched BOTLIB_API_VERSION: expected %i, got %i\\n\", BOTLIB_API_VERSION, apiVersion );\n\t\treturn NULL;\n\t}\n\n\tInit_AAS_Export(&be_botlib_export.aas);\n\tInit_EA_Export(&be_botlib_export.ea);\n\tInit_AI_Export(&be_botlib_export.ai);\n\n\tbe_botlib_export.BotLibSetup = Export_BotLibSetup;\n\tbe_botlib_export.BotLibShutdown = Export_BotLibShutdown;\n\tbe_botlib_export.BotLibVarSet = Export_BotLibVarSet;\n\tbe_botlib_export.BotLibVarGet = Export_BotLibVarGet;\n\n\tbe_botlib_export.PC_AddGlobalDefine = PC_AddGlobalDefine;\n\tbe_botlib_export.PC_LoadSourceHandle = PC_LoadSourceHandle;\n\tbe_botlib_export.PC_FreeSourceHandle = PC_FreeSourceHandle;\n\tbe_botlib_export.PC_ReadTokenHandle = PC_ReadTokenHandle;\n\tbe_botlib_export.PC_SourceFileAndLine = PC_SourceFileAndLine;\n\n\tbe_botlib_export.BotLibStartFrame = Export_BotLibStartFrame;\n\tbe_botlib_export.BotLibLoadMap = Export_BotLibLoadMap;\n\tbe_botlib_export.BotLibUpdateEntity = Export_BotLibUpdateEntity;\n\tbe_botlib_export.Test = BotExportTest;\n\n\treturn &be_botlib_export;\n}\n"
  },
  {
    "path": "plugins/quake3/botlib/be_interface.h",
    "content": "/*\n===========================================================================\nCopyright (C) 1999-2005 Id Software, Inc.\n\nThis file is part of Quake III Arena source code.\n\nQuake III Arena source code is free software; you can redistribute it\nand/or modify it under the terms of the GNU General Public License as\npublished by the Free Software Foundation; either version 2 of the License,\nor (at your option) any later version.\n\nQuake III Arena source code is distributed in the hope that it will be\nuseful, but WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Quake III Arena source code; if not, write to the Free Software\nFoundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n===========================================================================\n*/\n\n/*****************************************************************************\n * name:\t\tbe_interface.h\n *\n * desc:\t\tbotlib interface\n *\n * $Archive: /source/code/botlib/be_interface.h $\n *\n *****************************************************************************/\n\n//#define DEBUG\t\t\t//debug code\n#define RANDOMIZE\t\t//randomize bot behaviour\n\n//FIXME: get rid of this global structure\ntypedef struct botlib_globals_s\n{\n\tint botlibsetup;\t\t\t\t\t\t//true when the bot library has been setup\n\tint maxentities;\t\t\t\t\t\t//maximum number of entities\n\tint maxclients;\t\t\t\t\t\t\t//maximum number of clients\n\tfloat time;\t\t\t\t\t\t\t\t//the global time\n#ifdef DEBUG\n\tqboolean debug;\t\t\t\t\t\t\t//true if debug is on\n\tint goalareanum;\n\tvec3_t goalorigin;\n\tint runai;\n#endif\n} botlib_globals_t;\n\n\nextern botlib_globals_t botlibglobals;\nextern botlib_import_t botimport;\nextern int botDeveloper;\t\t\t\t\t//true if developer is on\n\n//\nint Sys_MilliSeconds(void);\n\n"
  },
  {
    "path": "plugins/quake3/botlib/botlib.h",
    "content": "/*\n===========================================================================\nCopyright (C) 1999-2005 Id Software, Inc.\n\nThis file is part of Quake III Arena source code.\n\nQuake III Arena source code is free software; you can redistribute it\nand/or modify it under the terms of the GNU General Public License as\npublished by the Free Software Foundation; either version 2 of the License,\nor (at your option) any later version.\n\nQuake III Arena source code is distributed in the hope that it will be\nuseful, but WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Quake III Arena source code; if not, write to the Free Software\nFoundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n===========================================================================\n*/\n//\n/*****************************************************************************\n * name:\t\tbotlib.h\n *\n * desc:\t\tbot AI library\n *\n * $Archive: /source/code/game/botai.h $\n *\n *****************************************************************************/\n\n#define\tBOTLIB_API_VERSION\t\t2\n\nstruct aas_clientmove_s;\nstruct aas_entityinfo_s;\nstruct aas_areainfo_s;\nstruct aas_altroutegoal_s;\nstruct aas_predictroute_s;\nstruct bot_consolemessage_s;\nstruct bot_match_s;\nstruct bot_goal_s;\nstruct bot_moveresult_s;\nstruct bot_initmove_s;\nstruct weaponinfo_s;\n\n#define BOTFILESBASEFOLDER\t\t\"botfiles\"\n//debug line colors\n#define LINECOLOR_NONE\t\t\t-1\n#define LINECOLOR_RED\t\t\t1//0xf2f2f0f0L\n#define LINECOLOR_GREEN\t\t\t2//0xd0d1d2d3L\n#define LINECOLOR_BLUE\t\t\t3//0xf3f3f1f1L\n#define LINECOLOR_YELLOW\t\t4//0xdcdddedfL\n#define LINECOLOR_ORANGE\t\t5//0xe0e1e2e3L\n\n//Print types\n#define PRT_MESSAGE\t\t\t\t1\n#define PRT_WARNING\t\t\t\t2\n#define PRT_ERROR\t\t\t\t3\n#define PRT_FATAL\t\t\t\t4\n#define PRT_EXIT\t\t\t\t5\n\n//console message types\n#define CMS_NORMAL\t\t\t\t0\n#define CMS_CHAT\t\t\t\t1\n\n//botlib error codes\n#define BLERR_NOERROR\t\t\t\t\t0\t//no error\n#define BLERR_LIBRARYNOTSETUP\t\t\t1\t//library not setup\n#define BLERR_INVALIDENTITYNUMBER\t\t2\t//invalid entity number\n#define BLERR_NOAASFILE\t\t\t\t\t3\t//no AAS file available\n#define BLERR_CANNOTOPENAASFILE\t\t\t4\t//cannot open AAS file\n#define BLERR_WRONGAASFILEID\t\t\t5\t//incorrect AAS file id\n#define BLERR_WRONGAASFILEVERSION\t\t6\t//incorrect AAS file version\n#define BLERR_CANNOTREADAASLUMP\t\t\t7\t//cannot read AAS file lump\n#define BLERR_CANNOTLOADICHAT\t\t\t8\t//cannot load initial chats\n#define BLERR_CANNOTLOADITEMWEIGHTS\t\t9\t//cannot load item weights\n#define BLERR_CANNOTLOADITEMCONFIG\t\t10\t//cannot load item config\n#define BLERR_CANNOTLOADWEAPONWEIGHTS\t11\t//cannot load weapon weights\n#define BLERR_CANNOTLOADWEAPONCONFIG\t12\t//cannot load weapon config\n\n//action flags\n#define ACTION_ATTACK\t\t\t0x00000001\n#define ACTION_USE\t\t\t0x00000002\n#define ACTION_RESPAWN\t\t\t0x00000008\n#define ACTION_JUMP\t\t\t0x00000010\n#define ACTION_MOVEUP\t\t\t0x00000020\n#define ACTION_CROUCH\t\t\t0x00000080\n#define ACTION_MOVEDOWN\t\t\t0x00000100\n#define ACTION_MOVEFORWARD\t\t0x00000200\n#define ACTION_MOVEBACK\t\t\t0x00000800\n#define ACTION_MOVELEFT\t\t\t0x00001000\n#define ACTION_MOVERIGHT\t\t0x00002000\n#define ACTION_DELAYEDJUMP\t\t0x00008000\n#define ACTION_TALK\t\t\t0x00010000\n#define ACTION_GESTURE\t\t\t0x00020000\n#define ACTION_WALK\t\t\t0x00080000\n#define ACTION_AFFIRMATIVE\t\t0x00100000\n#define ACTION_NEGATIVE\t\t\t0x00200000\n#define ACTION_GETFLAG\t\t\t0x00800000\n#define ACTION_GUARDBASE\t\t0x01000000\n#define ACTION_PATROL\t\t\t0x02000000\n#define ACTION_FOLLOWME\t\t\t0x08000000\n#define ACTION_JUMPEDLASTFRAME\t\t0x10000000\n\n//the bot input, will be converted to an usercmd_t\ntypedef struct bot_input_s\n{\n\tfloat thinktime;\t\t//time since last output (in seconds)\n\tvec3_t dir;\t\t\t\t//movement direction\n\tfloat speed;\t\t\t//speed in the range [0, 400]\n\tvec3_t viewangles;\t\t//the view angles\n\tint actionflags;\t\t//one of the ACTION_? flags\n\tint weapon;\t\t\t\t//weapon to use\n} bot_input_t;\n\n#ifndef BSPTRACE\n\n#define BSPTRACE\n\n//bsp_trace_t hit surface\ntypedef struct bsp_surface_s\n{\n\tchar name[16];\n\tint flags;\n\tint value;\n} bsp_surface_t;\n\n//remove the bsp_trace_s structure definition l8r on\n//a trace is returned when a box is swept through the world\ntypedef struct bsp_trace_s\n{\n\tqboolean\t\tallsolid;\t// if true, plane is not valid\n\tqboolean\t\tstartsolid;\t// if true, the initial point was in a solid area\n\tfloat\t\t\tfraction;\t// time completed, 1.0 = didn't hit anything\n\tvec3_t\t\t\tendpos;\t\t// final position\n\tcplane_t\t\tplane;\t\t// surface normal at impact\n\tfloat\t\t\texp_dist;\t// expanded plane distance\n\tint\t\t\t\tsidenum;\t// number of the brush side hit\n\tbsp_surface_t\tsurface;\t// the hit point surface\n\tint\t\t\t\tcontents;\t// contents on other side of surface hit\n\tint\t\t\t\tent;\t\t// number of entity hit\n} bsp_trace_t;\n\n#endif\t// BSPTRACE\n\n//entity state\ntypedef struct bot_entitystate_s\n{\n\tint\t\ttype;\t\t\t// entity type\n\tint\t\tflags;\t\t\t// entity flags\n\tvec3_t\torigin;\t\t\t// origin of the entity\n\tvec3_t\tangles;\t\t\t// angles of the model\n\tvec3_t\told_origin;\t\t// for lerping\n\tvec3_t\tmins;\t\t\t// bounding box minimums\n\tvec3_t\tmaxs;\t\t\t// bounding box maximums\n\tint\t\tgroundent;\t\t// ground entity\n\tint\t\tsolid;\t\t\t// solid type\n\tint\t\tmodelindex;\t\t// model used\n\tint\t\tmodelindex2;\t// weapons, CTF flags, etc\n\tint\t\tframe;\t\t\t// model frame number\n\tint\t\tevent;\t\t\t// impulse events -- muzzle flashes, footsteps, etc\n\tint\t\teventParm;\t\t// even parameter\n\tint\t\tpowerups;\t\t// bit flags\n\tint\t\tweapon;\t\t\t// determines weapon and flash model, etc\n\tint\t\tlegsAnim;\t\t// mask off ANIM_TOGGLEBIT\n\tint\t\ttorsoAnim;\t\t// mask off ANIM_TOGGLEBIT\n} bot_entitystate_t;\n\n//bot AI library exported functions\ntypedef struct botlib_import_s\n{\n\t//print messages from the bot library\n\tvoid\t\t(QDECL *Print)(int type, char *fmt, ...);\n\t//trace a bbox through the world\n\tvoid\t\t(*Trace)(bsp_trace_t *trace, vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, int passent, int contentmask);\n\t//trace a bbox against a specific entity\n\tvoid\t\t(*EntityTrace)(bsp_trace_t *trace, vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, int entnum, int contentmask);\n\t//retrieve the contents at the given point\n\tint\t\t\t(*PointContents)(vec3_t point);\n\t//check if the point is in potential visible sight\n\tint\t\t\t(*inPVS)(vec3_t p1, vec3_t p2);\n\t//retrieve the BSP entity data lump\n\tchar\t\t*(*BSPEntityData)(void);\n\t//\n\tvoid\t\t(*BSPModelMinsMaxsOrigin)(int modelnum, vec3_t angles, vec3_t mins, vec3_t maxs, vec3_t origin);\n\t//send a bot client command\n\tvoid\t\t(*BotClientCommand)(int client, char *command);\n\t//memory allocation\n\tvoid\t\t*(*GetMemory)(int size);\t\t// allocate from Zone\n\tvoid\t\t(*FreeMemory)(void *ptr);\t\t// free memory from Zone\n\tint\t\t\t(*AvailableMemory)(void);\t\t// available Zone memory\n\tvoid\t\t*(*HunkAlloc)(int size);\t\t// allocate from hunk\n\t//file system access\n\tint\t\t\t(*FS_FOpenFile)( const char *qpath, fileHandle_t *file, fsMode_t mode );\n\tint\t\t\t(*FS_Read)( void *buffer, int len, fileHandle_t f );\n\tint\t\t\t(*FS_Write)( const void *buffer, int len, fileHandle_t f );\n\tvoid\t\t(*FS_FCloseFile)( fileHandle_t f );\n\tint\t\t\t(*FS_Seek)( fileHandle_t f, long offset, int origin );\n\t//debug visualisation stuff\n\tint\t\t\t(*DebugLineCreate)(void);\n\tvoid\t\t(*DebugLineDelete)(int line);\n\tvoid\t\t(*DebugLineShow)(int line, vec3_t start, vec3_t end, int color);\n\t//\n\tint\t\t\t(*DebugPolygonCreate)(int color, int numPoints, vec3_t *points);\n\tvoid\t\t(*DebugPolygonDelete)(int id);\n\n\tvoid\t\t(*Error)(const char *msg);\t//for unrecoverable errors only. Will quit out.\n} botlib_import_t;\n\ntypedef struct aas_export_s\n{\n\t//-----------------------------------\n\t// be_aas_entity.h\n\t//-----------------------------------\n\tvoid\t\t(*AAS_EntityInfo)(int entnum, struct aas_entityinfo_s *info);\n\t//-----------------------------------\n\t// be_aas_main.h\n\t//-----------------------------------\n\tint\t\t\t(*AAS_Initialized)(void);\n\tvoid\t\t(*AAS_PresenceTypeBoundingBox)(int presencetype, vec3_t mins, vec3_t maxs);\n\tfloat\t\t(*AAS_Time)(void);\n\t//--------------------------------------------\n\t// be_aas_sample.c\n\t//--------------------------------------------\n\tint\t\t\t(*AAS_PointAreaNum)(vec3_t point);\n\tint\t\t\t(*AAS_PointReachabilityAreaIndex)( vec3_t point );\n\tint\t\t\t(*AAS_TraceAreas)(vec3_t start, vec3_t end, int *areas, vec3_t *points, int maxareas);\n\tint\t\t\t(*AAS_BBoxAreas)(vec3_t absmins, vec3_t absmaxs, int *areas, int maxareas);\n\tint\t\t\t(*AAS_AreaInfo)( int areanum, struct aas_areainfo_s *info );\n\t//--------------------------------------------\n\t// be_aas_bspq3.c\n\t//--------------------------------------------\n\tint\t\t\t(*AAS_PointContents)(vec3_t point);\n\tint\t\t\t(*AAS_NextBSPEntity)(int ent);\n\tint\t\t\t(*AAS_ValueForBSPEpairKey)(int ent, char *key, char *value, int size);\n\tint\t\t\t(*AAS_VectorForBSPEpairKey)(int ent, char *key, vec3_t v);\n\tint\t\t\t(*AAS_FloatForBSPEpairKey)(int ent, char *key, float *value);\n\tint\t\t\t(*AAS_IntForBSPEpairKey)(int ent, char *key, int *value);\n\t//--------------------------------------------\n\t// be_aas_reach.c\n\t//--------------------------------------------\n\tint\t\t\t(*AAS_AreaReachability)(int areanum);\n\t//--------------------------------------------\n\t// be_aas_route.c\n\t//--------------------------------------------\n\tint\t\t\t(*AAS_AreaTravelTimeToGoalArea)(int areanum, vec3_t origin, int goalareanum, int travelflags);\n\tint\t\t\t(*AAS_EnableRoutingArea)(int areanum, int enable);\n\tint\t\t\t(*AAS_PredictRoute)(struct aas_predictroute_s *route, int areanum, vec3_t origin,\n\t\t\t\t\t\t\tint goalareanum, int travelflags, int maxareas, int maxtime,\n\t\t\t\t\t\t\tint stopevent, int stopcontents, int stoptfl, int stopareanum);\n\t//--------------------------------------------\n\t// be_aas_altroute.c\n\t//--------------------------------------------\n\tint\t\t\t(*AAS_AlternativeRouteGoals)(vec3_t start, int startareanum, vec3_t goal, int goalareanum, int travelflags,\n\t\t\t\t\t\t\t\t\t\tstruct aas_altroutegoal_s *altroutegoals, int maxaltroutegoals,\n\t\t\t\t\t\t\t\t\t\tint type);\n\t//--------------------------------------------\n\t// be_aas_move.c\n\t//--------------------------------------------\n\tint\t\t\t(*AAS_Swimming)(vec3_t origin);\n\tint\t\t\t(*AAS_PredictClientMovement)(struct aas_clientmove_s *move,\n\t\t\t\t\t\t\t\t\t\t\tint entnum, vec3_t origin,\n\t\t\t\t\t\t\t\t\t\t\tint presencetype, int onground,\n\t\t\t\t\t\t\t\t\t\t\tvec3_t velocity, vec3_t cmdmove,\n\t\t\t\t\t\t\t\t\t\t\tint cmdframes,\n\t\t\t\t\t\t\t\t\t\t\tint maxframes, float frametime,\n\t\t\t\t\t\t\t\t\t\t\tint stopevent, int stopareanum, int visualize);\n} aas_export_t;\n\ntypedef struct ea_export_s\n{\n\t//ClientCommand elementary actions\n\tvoid\t(*EA_Command)(int client, char *command );\n\tvoid\t(*EA_Say)(int client, char *str);\n\tvoid\t(*EA_SayTeam)(int client, char *str);\n\t//\n\tvoid\t(*EA_Action)(int client, int action);\n\tvoid\t(*EA_Gesture)(int client);\n\tvoid\t(*EA_Talk)(int client);\n\tvoid\t(*EA_Attack)(int client);\n\tvoid\t(*EA_Use)(int client);\n\tvoid\t(*EA_Respawn)(int client);\n\tvoid\t(*EA_MoveUp)(int client);\n\tvoid\t(*EA_MoveDown)(int client);\n\tvoid\t(*EA_MoveForward)(int client);\n\tvoid\t(*EA_MoveBack)(int client);\n\tvoid\t(*EA_MoveLeft)(int client);\n\tvoid\t(*EA_MoveRight)(int client);\n\tvoid\t(*EA_Crouch)(int client);\n\n\tvoid\t(*EA_SelectWeapon)(int client, int weapon);\n\tvoid\t(*EA_Jump)(int client);\n\tvoid\t(*EA_DelayedJump)(int client);\n\tvoid\t(*EA_Move)(int client, vec3_t dir, float speed);\n\tvoid\t(*EA_View)(int client, vec3_t viewangles);\n\t//send regular input to the server\n\tvoid\t(*EA_EndRegular)(int client, float thinktime);\n\tvoid\t(*EA_GetInput)(int client, float thinktime, bot_input_t *input);\n\tvoid\t(*EA_ResetInput)(int client);\n} ea_export_t;\n\ntypedef struct ai_export_s\n{\n\t//-----------------------------------\n\t// be_ai_char.h\n\t//-----------------------------------\n\tint\t\t(*BotLoadCharacter)(char *charfile, float skill);\n\tvoid\t(*BotFreeCharacter)(int character);\n\tfloat\t(*Characteristic_Float)(int character, int index);\n\tfloat\t(*Characteristic_BFloat)(int character, int index, float min, float max);\n\tint\t\t(*Characteristic_Integer)(int character, int index);\n\tint\t\t(*Characteristic_BInteger)(int character, int index, int min, int max);\n\tvoid\t(*Characteristic_String)(int character, int index, char *buf, int size);\n\t//-----------------------------------\n\t// be_ai_chat.h\n\t//-----------------------------------\n\tint\t\t(*BotAllocChatState)(void);\n\tvoid\t(*BotFreeChatState)(int handle);\n\tvoid\t(*BotQueueConsoleMessage)(int chatstate, int type, char *message);\n\tvoid\t(*BotRemoveConsoleMessage)(int chatstate, int handle);\n\tint\t\t(*BotNextConsoleMessage)(int chatstate, struct bot_consolemessage_s *cm);\n\tint\t\t(*BotNumConsoleMessages)(int chatstate);\n\tvoid\t(*BotInitialChat)(int chatstate, char *type, int mcontext, char *var0, char *var1, char *var2, char *var3, char *var4, char *var5, char *var6, char *var7);\n\tint\t\t(*BotNumInitialChats)(int chatstate, char *type);\n\tint\t\t(*BotReplyChat)(int chatstate, char *message, int mcontext, int vcontext, char *var0, char *var1, char *var2, char *var3, char *var4, char *var5, char *var6, char *var7);\n\tint\t\t(*BotChatLength)(int chatstate);\n\tvoid\t(*BotEnterChat)(int chatstate, int client, int sendto);\n\tvoid\t(*BotGetChatMessage)(int chatstate, char *buf, int size);\n\tint\t\t(*StringContains)(char *str1, char *str2, int casesensitive);\n\tint\t\t(*BotFindMatch)(char *str, struct bot_match_s *match, unsigned long int context);\n\tvoid\t(*BotMatchVariable)(struct bot_match_s *match, int variable, char *buf, int size);\n\tvoid\t(*UnifyWhiteSpaces)(char *string);\n\tvoid\t(*BotReplaceSynonyms)(char *string, unsigned long int context);\n\tint\t\t(*BotLoadChatFile)(int chatstate, char *chatfile, char *chatname);\n\tvoid\t(*BotSetChatGender)(int chatstate, int gender);\n\tvoid\t(*BotSetChatName)(int chatstate, char *name, int client);\n\t//-----------------------------------\n\t// be_ai_goal.h\n\t//-----------------------------------\n\tvoid\t(*BotResetGoalState)(int goalstate);\n\tvoid\t(*BotResetAvoidGoals)(int goalstate);\n\tvoid\t(*BotRemoveFromAvoidGoals)(int goalstate, int number);\n\tvoid\t(*BotPushGoal)(int goalstate, struct bot_goal_s *goal);\n\tvoid\t(*BotPopGoal)(int goalstate);\n\tvoid\t(*BotEmptyGoalStack)(int goalstate);\n\tvoid\t(*BotDumpAvoidGoals)(int goalstate);\n\tvoid\t(*BotDumpGoalStack)(int goalstate);\n\tvoid\t(*BotGoalName)(int number, char *name, int size);\n\tint\t\t(*BotGetTopGoal)(int goalstate, struct bot_goal_s *goal);\n\tint\t\t(*BotGetSecondGoal)(int goalstate, struct bot_goal_s *goal);\n\tint\t\t(*BotChooseLTGItem)(int goalstate, vec3_t origin, int *inventory, int travelflags);\n\tint\t\t(*BotChooseNBGItem)(int goalstate, vec3_t origin, int *inventory, int travelflags,\n\t\t\t\t\t\t\t\tstruct bot_goal_s *ltg, float maxtime);\n\tint\t\t(*BotTouchingGoal)(vec3_t origin, struct bot_goal_s *goal);\n\tint\t\t(*BotItemGoalInVisButNotVisible)(int viewer, vec3_t eye, vec3_t viewangles, struct bot_goal_s *goal);\n\tint\t\t(*BotGetLevelItemGoal)(int index, char *classname, struct bot_goal_s *goal);\n\tint\t\t(*BotGetNextCampSpotGoal)(int num, struct bot_goal_s *goal);\n\tint\t\t(*BotGetMapLocationGoal)(char *name, struct bot_goal_s *goal);\n\tfloat\t(*BotAvoidGoalTime)(int goalstate, int number);\n\tvoid\t(*BotSetAvoidGoalTime)(int goalstate, int number, float avoidtime);\n\tvoid\t(*BotInitLevelItems)(void);\n\tvoid\t(*BotUpdateEntityItems)(void);\n\tint\t\t(*BotLoadItemWeights)(int goalstate, char *filename);\n\tvoid\t(*BotFreeItemWeights)(int goalstate);\n\tvoid\t(*BotInterbreedGoalFuzzyLogic)(int parent1, int parent2, int child);\n\tvoid\t(*BotSaveGoalFuzzyLogic)(int goalstate, char *filename);\n\tvoid\t(*BotMutateGoalFuzzyLogic)(int goalstate, float range);\n\tint\t\t(*BotAllocGoalState)(int client);\n\tvoid\t(*BotFreeGoalState)(int handle);\n\t//-----------------------------------\n\t// be_ai_move.h\n\t//-----------------------------------\n\tvoid\t(*BotResetMoveState)(int movestate);\n\tvoid\t(*BotMoveToGoal)(struct bot_moveresult_s *result, int movestate, struct bot_goal_s *goal, int travelflags);\n\tint\t\t(*BotMoveInDirection)(int movestate, vec3_t dir, float speed, int type);\n\tvoid\t(*BotResetAvoidReach)(int movestate);\n\tvoid\t(*BotResetLastAvoidReach)(int movestate);\n\tint\t\t(*BotReachabilityArea)(vec3_t origin, int testground);\n\tint\t\t(*BotMovementViewTarget)(int movestate, struct bot_goal_s *goal, int travelflags, float lookahead, vec3_t target);\n\tint\t\t(*BotPredictVisiblePosition)(vec3_t origin, int areanum, struct bot_goal_s *goal, int travelflags, vec3_t target);\n\tint\t\t(*BotAllocMoveState)(void);\n\tvoid\t(*BotFreeMoveState)(int handle);\n\tvoid\t(*BotInitMoveState)(int handle, struct bot_initmove_s *initmove);\n\tvoid\t(*BotAddAvoidSpot)(int movestate, vec3_t origin, float radius, int type);\n\t//-----------------------------------\n\t// be_ai_weap.h\n\t//-----------------------------------\n\tint\t\t(*BotChooseBestFightWeapon)(int weaponstate, int *inventory);\n\tvoid\t(*BotGetWeaponInfo)(int weaponstate, int weapon, struct weaponinfo_s *weaponinfo);\n\tint\t\t(*BotLoadWeaponWeights)(int weaponstate, char *filename);\n\tint\t\t(*BotAllocWeaponState)(void);\n\tvoid\t(*BotFreeWeaponState)(int weaponstate);\n\tvoid\t(*BotResetWeaponState)(int weaponstate);\n\t//-----------------------------------\n\t// be_ai_gen.h\n\t//-----------------------------------\n\tint\t\t(*GeneticParentsAndChildSelection)(int numranks, float *ranks, int *parent1, int *parent2, int *child);\n} ai_export_t;\n\n//bot AI library imported functions\ntypedef struct botlib_export_s\n{\n\t//Area Awareness System functions\n\taas_export_t aas;\n\t//Elementary Action functions\n\tea_export_t ea;\n\t//AI functions\n\tai_export_t ai;\n\t//setup the bot library, returns BLERR_\n\tint (*BotLibSetup)(void);\n\t//shutdown the bot library, returns BLERR_\n\tint (*BotLibShutdown)(void);\n\t//sets a library variable returns BLERR_\n\tint (*BotLibVarSet)(char *var_name, char *value);\n\t//gets a library variable returns BLERR_\n\tint (*BotLibVarGet)(char *var_name, char *value, int size);\n\n\t//sets a C-like define returns BLERR_\n\tint (*PC_AddGlobalDefine)(char *string);\n\tint (*PC_LoadSourceHandle)(const char *filename);\n\tint (*PC_FreeSourceHandle)(int handle);\n\tint (*PC_ReadTokenHandle)(int handle, pc_token_t *pc_token);\n\tint (*PC_SourceFileAndLine)(int handle, char *filename, int *line);\n\n\t//start a frame in the bot library\n\tint (*BotLibStartFrame)(float time);\n\t//load a new map in the bot library\n\tint (*BotLibLoadMap)(const char *mapname);\n\t//entity updates\n\tint (*BotLibUpdateEntity)(int ent, bot_entitystate_t *state);\n\t//just for testing\n\tint (*Test)(int parm0, char *parm1, vec3_t parm2, vec3_t parm3);\n} botlib_export_t;\n\n//linking of bot library\n#ifndef EXTERNALBOTLIB\nbotlib_export_t *QDECL GetBotLibAPI( int apiVersion, botlib_import_t *import );\n#endif\n\n/* Library variables:\n\nname:\t\t\t\t\t\tdefault:\t\t\tmodule(s):\t\t\tdescription:\n\n\"basedir\"\t\t\t\t\t\"\"\t\t\t\t\tl_utils.c\t\t\tbase directory\n\"gamedir\"\t\t\t\t\t\"\"\t\t\t\t\tl_utils.c\t\t\tgame directory\n\"cddir\"\t\t\t\t\t\t\"\"\t\t\t\t\tl_utils.c\t\t\tCD directory\n\n\"log\"\t\t\t\t\t\t\"0\"\t\t\t\t\tl_log.c\t\t\t\tenable/disable creating a log file\n\"maxclients\"\t\t\t\t\"4\"\t\t\t\t\tbe_interface.c\t\tmaximum number of clients\n\"maxentities\"\t\t\t\t\"1024\"\t\t\t\tbe_interface.c\t\tmaximum number of entities\n\"bot_developer\"\t\t\t\t\"0\"\t\t\t\t\tbe_interface.c\t\tbot developer mode (it's \"botDeveloper\" in C to prevent symbol clash).\n\n\"phys_friction\"\t\t\t\t\"6\"\t\t\t\t\tbe_aas_move.c\t\tground friction\n\"phys_stopspeed\"\t\t\t\"100\"\t\t\t\tbe_aas_move.c\t\tstop speed\n\"phys_gravity\"\t\t\t\t\"800\"\t\t\t\tbe_aas_move.c\t\tgravity value\n\"phys_waterfriction\"\t\t\"1\"\t\t\t\t\tbe_aas_move.c\t\twater friction\n\"phys_watergravity\"\t\t\t\"400\"\t\t\t\tbe_aas_move.c\t\tgravity in water\n\"phys_maxvelocity\"\t\t\t\"320\"\t\t\t\tbe_aas_move.c\t\tmaximum velocity\n\"phys_maxwalkvelocity\"\t\t\"320\"\t\t\t\tbe_aas_move.c\t\tmaximum walk velocity\n\"phys_maxcrouchvelocity\"\t\"100\"\t\t\t\tbe_aas_move.c\t\tmaximum crouch velocity\n\"phys_maxswimvelocity\"\t\t\"150\"\t\t\t\tbe_aas_move.c\t\tmaximum swim velocity\n\"phys_walkaccelerate\"\t\t\"10\"\t\t\t\tbe_aas_move.c\t\twalk acceleration\n\"phys_airaccelerate\"\t\t\"1\"\t\t\t\t\tbe_aas_move.c\t\tair acceleration\n\"phys_swimaccelerate\"\t\t\"4\"\t\t\t\t\tbe_aas_move.c\t\tswim acceleration\n\"phys_maxstep\"\t\t\t\t\"18\"\t\t\t\tbe_aas_move.c\t\tmaximum step height\n\"phys_maxsteepness\"\t\t\t\"0.7\"\t\t\t\tbe_aas_move.c\t\tmaximum floor steepness\n\"phys_maxbarrier\"\t\t\t\"32\"\t\t\t\tbe_aas_move.c\t\tmaximum barrier height\n\"phys_maxwaterjump\"\t\t\t\"19\"\t\t\t\tbe_aas_move.c\t\tmaximum waterjump height\n\"phys_jumpvel\"\t\t\t\t\"270\"\t\t\t\tbe_aas_move.c\t\tjump z velocity\n\"phys_falldelta5\"\t\t\t\"40\"\t\t\t\tbe_aas_move.c\n\"phys_falldelta10\"\t\t\t\"60\"\t\t\t\tbe_aas_move.c\n\"rs_waterjump\"\t\t\t\t\"400\"\t\t\t\tbe_aas_move.c\n\"rs_teleport\"\t\t\t\t\"50\"\t\t\t\tbe_aas_move.c\n\"rs_barrierjump\"\t\t\t\"100\"\t\t\t\tbe_aas_move.c\n\"rs_startcrouch\"\t\t\t\"300\"\t\t\t\tbe_aas_move.c\n\"rs_startgrapple\"\t\t\t\"500\"\t\t\t\tbe_aas_move.c\n\"rs_startwalkoffledge\"\t\t\"70\"\t\t\t\tbe_aas_move.c\n\"rs_startjump\"\t\t\t\t\"300\"\t\t\t\tbe_aas_move.c\n\"rs_rocketjump\"\t\t\t\t\"500\"\t\t\t\tbe_aas_move.c\n\"rs_bfgjump\"\t\t\t\t\"500\"\t\t\t\tbe_aas_move.c\n\"rs_jumppad\"\t\t\t\t\"250\"\t\t\t\tbe_aas_move.c\n\"rs_aircontrolledjumppad\"\t\"300\"\t\t\t\tbe_aas_move.c\n\"rs_funcbob\"\t\t\t\t\"300\"\t\t\t\tbe_aas_move.c\n\"rs_startelevator\"\t\t\t\"50\"\t\t\t\tbe_aas_move.c\n\"rs_falldamage5\"\t\t\t\"300\"\t\t\t\tbe_aas_move.c\n\"rs_falldamage10\"\t\t\t\"500\"\t\t\t\tbe_aas_move.c\n\"rs_maxjumpfallheight\"\t\t\"450\"\t\t\t\tbe_aas_move.c\n\n\"max_aaslinks\"\t\t\t\t\"4096\"\t\t\t\tbe_aas_sample.c\t\tmaximum links in the AAS\n\"max_routingcache\"\t\t\t\"4096\"\t\t\t\tbe_aas_route.c\t\tmaximum routing cache size in KB\n\"forceclustering\"\t\t\t\"0\"\t\t\t\t\tbe_aas_main.c\t\tforce recalculation of clusters\n\"forcereachability\"\t\t\t\"0\"\t\t\t\t\tbe_aas_main.c\t\tforce recalculation of reachabilities\n\"forcewrite\"\t\t\t\t\"0\"\t\t\t\t\tbe_aas_main.c\t\tforce writing of aas file\n\"aasoptimize\"\t\t\t\t\"0\"\t\t\t\t\tbe_aas_main.c\t\tenable aas optimization\n\"sv_mapChecksum\"\t\t\t\"0\"\t\t\t\t\tbe_aas_main.c\t\tBSP file checksum\n\"bot_visualizejumppads\"\t\t\"0\"\t\t\t\t\tbe_aas_reach.c\t\tvisualize jump pads\n\n\"bot_reloadcharacters\"\t\t\"0\"\t\t\t\t\t-\t\t\t\t\treload bot character files\n\"ai_gametype\"\t\t\t\t\"0\"\t\t\t\t\tbe_ai_goal.c\t\tgame type\n\"droppedweight\"\t\t\t\t\"1000\"\t\t\t\tbe_ai_goal.c\t\tadditional dropped item weight\n\"weapindex_rocketlauncher\"\t\"5\"\t\t\t\t\tbe_ai_move.c\t\trl weapon index for rocket jumping\n\"weapindex_bfg10k\"\t\t\t\"9\"\t\t\t\t\tbe_ai_move.c\t\tbfg weapon index for bfg jumping\n\"weapindex_grapple\"\t\t\t\"10\"\t\t\t\tbe_ai_move.c\t\tgrapple weapon index for grappling\n\"entitytypemissile\"\t\t\t\"3\"\t\t\t\t\tbe_ai_move.c\t\tET_MISSILE\n\"offhandgrapple\"\t\t\t\"0\"\t\t\t\t\tbe_ai_move.c\t\tenable off hand grapple hook\n\"cmd_grappleon\"\t\t\t\t\"grappleon\"\t\t\tbe_ai_move.c\t\tcommand to activate off hand grapple\n\"cmd_grappleoff\"\t\t\t\"grappleoff\"\t\tbe_ai_move.c\t\tcommand to deactivate off hand grapple\n\"itemconfig\"\t\t\t\t\"items.c\"\t\t\tbe_ai_goal.c\t\titem configuration file\n\"weaponconfig\"\t\t\t\t\"weapons.c\"\t\t\tbe_ai_weap.c\t\tweapon configuration file\n\"synfile\"\t\t\t\t\t\"syn.c\"\t\t\t\tbe_ai_chat.c\t\tfile with synonyms\n\"rndfile\"\t\t\t\t\t\"rnd.c\"\t\t\t\tbe_ai_chat.c\t\tfile with random strings\n\"matchfile\"\t\t\t\t\t\"match.c\"\t\t\tbe_ai_chat.c\t\tfile with match strings\n\"nochat\"\t\t\t\t\t\"0\"\t\t\t\t\tbe_ai_chat.c\t\tdisable chats\n\"max_messages\"\t\t\t\t\"1024\"\t\t\t\tbe_ai_chat.c\t\tconsole message heap size\n\"max_weaponinfo\"\t\t\t\"32\"\t\t\t\tbe_ai_weap.c\t\tmaximum number of weapon info\n\"max_projectileinfo\"\t\t\"32\"\t\t\t\tbe_ai_weap.c\t\tmaximum number of projectile info\n\"max_iteminfo\"\t\t\t\t\"256\"\t\t\t\tbe_ai_goal.c\t\tmaximum number of item info\n\"max_levelitems\"\t\t\t\"256\"\t\t\t\tbe_ai_goal.c\t\tmaximum number of level items\n\n*/\n\n"
  },
  {
    "path": "plugins/quake3/botlib/l_crc.c",
    "content": "/*\n===========================================================================\nCopyright (C) 1999-2005 Id Software, Inc.\n\nThis file is part of Quake III Arena source code.\n\nQuake III Arena source code is free software; you can redistribute it\nand/or modify it under the terms of the GNU General Public License as\npublished by the Free Software Foundation; either version 2 of the License,\nor (at your option) any later version.\n\nQuake III Arena source code is distributed in the hope that it will be\nuseful, but WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Quake III Arena source code; if not, write to the Free Software\nFoundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n===========================================================================\n*/\n\n/*****************************************************************************\n * name:\t\tl_crc.c\n *\n * desc:\t\tCRC calculation\n *\n * $Archive: /MissionPack/CODE/botlib/l_crc.c $\n *\n *****************************************************************************/\n\n#include <stdlib.h>\n#include <stdio.h>\n#include <string.h>\n\n#include \"q_shared.h\"\n#include \"botlib.h\"\n#include \"be_interface.h\"\t\t\t//for botimport.Print\n#include \"l_crc.h\"\n\n\n// FIXME: byte swap?\n\n// this is a 16 bit, non-reflected CRC using the polynomial 0x1021\n// and the initial and final xor values shown below...  in other words, the\n// CCITT standard CRC used by XMODEM\n\n#define CRC_INIT_VALUE\t0xffff\n#define CRC_XOR_VALUE\t0x0000\n\nunsigned short crctable[257] =\n{\n\t0x0000,\t0x1021,\t0x2042,\t0x3063,\t0x4084,\t0x50a5,\t0x60c6,\t0x70e7,\n\t0x8108,\t0x9129,\t0xa14a,\t0xb16b,\t0xc18c,\t0xd1ad,\t0xe1ce,\t0xf1ef,\n\t0x1231,\t0x0210,\t0x3273,\t0x2252,\t0x52b5,\t0x4294,\t0x72f7,\t0x62d6,\n\t0x9339,\t0x8318,\t0xb37b,\t0xa35a,\t0xd3bd,\t0xc39c,\t0xf3ff,\t0xe3de,\n\t0x2462,\t0x3443,\t0x0420,\t0x1401,\t0x64e6,\t0x74c7,\t0x44a4,\t0x5485,\n\t0xa56a,\t0xb54b,\t0x8528,\t0x9509,\t0xe5ee,\t0xf5cf,\t0xc5ac,\t0xd58d,\n\t0x3653,\t0x2672,\t0x1611,\t0x0630,\t0x76d7,\t0x66f6,\t0x5695,\t0x46b4,\n\t0xb75b,\t0xa77a,\t0x9719,\t0x8738,\t0xf7df,\t0xe7fe,\t0xd79d,\t0xc7bc,\n\t0x48c4,\t0x58e5,\t0x6886,\t0x78a7,\t0x0840,\t0x1861,\t0x2802,\t0x3823,\n\t0xc9cc,\t0xd9ed,\t0xe98e,\t0xf9af,\t0x8948,\t0x9969,\t0xa90a,\t0xb92b,\n\t0x5af5,\t0x4ad4,\t0x7ab7,\t0x6a96,\t0x1a71,\t0x0a50,\t0x3a33,\t0x2a12,\n\t0xdbfd,\t0xcbdc,\t0xfbbf,\t0xeb9e,\t0x9b79,\t0x8b58,\t0xbb3b,\t0xab1a,\n\t0x6ca6,\t0x7c87,\t0x4ce4,\t0x5cc5,\t0x2c22,\t0x3c03,\t0x0c60,\t0x1c41,\n\t0xedae,\t0xfd8f,\t0xcdec,\t0xddcd,\t0xad2a,\t0xbd0b,\t0x8d68,\t0x9d49,\n\t0x7e97,\t0x6eb6,\t0x5ed5,\t0x4ef4,\t0x3e13,\t0x2e32,\t0x1e51,\t0x0e70,\n\t0xff9f,\t0xefbe,\t0xdfdd,\t0xcffc,\t0xbf1b,\t0xaf3a,\t0x9f59,\t0x8f78,\n\t0x9188,\t0x81a9,\t0xb1ca,\t0xa1eb,\t0xd10c,\t0xc12d,\t0xf14e,\t0xe16f,\n\t0x1080,\t0x00a1,\t0x30c2,\t0x20e3,\t0x5004,\t0x4025,\t0x7046,\t0x6067,\n\t0x83b9,\t0x9398,\t0xa3fb,\t0xb3da,\t0xc33d,\t0xd31c,\t0xe37f,\t0xf35e,\n\t0x02b1,\t0x1290,\t0x22f3,\t0x32d2,\t0x4235,\t0x5214,\t0x6277,\t0x7256,\n\t0xb5ea,\t0xa5cb,\t0x95a8,\t0x8589,\t0xf56e,\t0xe54f,\t0xd52c,\t0xc50d,\n\t0x34e2,\t0x24c3,\t0x14a0,\t0x0481,\t0x7466,\t0x6447,\t0x5424,\t0x4405,\n\t0xa7db,\t0xb7fa,\t0x8799,\t0x97b8,\t0xe75f,\t0xf77e,\t0xc71d,\t0xd73c,\n\t0x26d3,\t0x36f2,\t0x0691,\t0x16b0,\t0x6657,\t0x7676,\t0x4615,\t0x5634,\n\t0xd94c,\t0xc96d,\t0xf90e,\t0xe92f,\t0x99c8,\t0x89e9,\t0xb98a,\t0xa9ab,\n\t0x5844,\t0x4865,\t0x7806,\t0x6827,\t0x18c0,\t0x08e1,\t0x3882,\t0x28a3,\n\t0xcb7d,\t0xdb5c,\t0xeb3f,\t0xfb1e,\t0x8bf9,\t0x9bd8,\t0xabbb,\t0xbb9a,\n\t0x4a75,\t0x5a54,\t0x6a37,\t0x7a16,\t0x0af1,\t0x1ad0,\t0x2ab3,\t0x3a92,\n\t0xfd2e,\t0xed0f,\t0xdd6c,\t0xcd4d,\t0xbdaa,\t0xad8b,\t0x9de8,\t0x8dc9,\n\t0x7c26,\t0x6c07,\t0x5c64,\t0x4c45,\t0x3ca2,\t0x2c83,\t0x1ce0,\t0x0cc1,\n\t0xef1f,\t0xff3e,\t0xcf5d,\t0xdf7c,\t0xaf9b,\t0xbfba,\t0x8fd9,\t0x9ff8,\n\t0x6e17,\t0x7e36,\t0x4e55,\t0x5e74,\t0x2e93,\t0x3eb2,\t0x0ed1,\t0x1ef0\n};\n\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid CRC_Init(unsigned short *crcvalue)\n{\n\t*crcvalue = CRC_INIT_VALUE;\n} //end of the function CRC_Init\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid CRC_ProcessByte(unsigned short *crcvalue, byte data)\n{\n\t*crcvalue = (*crcvalue << 8) ^ crctable[(*crcvalue >> 8) ^ data];\n} //end of the function CRC_ProcessByte\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nunsigned short CRC_Value(unsigned short crcvalue)\n{\n\treturn crcvalue ^ CRC_XOR_VALUE;\n} //end of the function CRC_Value\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nunsigned short CRC_ProcessString(unsigned char *data, int length)\n{\n\tunsigned short crcvalue;\n\tint i, ind;\n\n\tCRC_Init(&crcvalue);\n\n\tfor (i = 0; i < length; i++)\n\t{\n\t\tind = (crcvalue >> 8) ^ data[i];\n\t\tif (ind < 0 || ind > 256) ind = 0;\n\t\tcrcvalue = (crcvalue << 8) ^ crctable[ind];\n\t} //end for\n\treturn CRC_Value(crcvalue);\n} //end of the function CRC_ProcessString\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid CRC_ContinueProcessString(unsigned short *crc, char *data, int length)\n{\n\tint i;\n\n\tfor (i = 0; i < length; i++)\n\t{\n\t\t*crc = (*crc << 8) ^ crctable[(*crc >> 8) ^ data[i]];\n\t} //end for\n} //end of the function CRC_ProcessString\n"
  },
  {
    "path": "plugins/quake3/botlib/l_crc.h",
    "content": "/*\n===========================================================================\nCopyright (C) 1999-2005 Id Software, Inc.\n\nThis file is part of Quake III Arena source code.\n\nQuake III Arena source code is free software; you can redistribute it\nand/or modify it under the terms of the GNU General Public License as\npublished by the Free Software Foundation; either version 2 of the License,\nor (at your option) any later version.\n\nQuake III Arena source code is distributed in the hope that it will be\nuseful, but WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Quake III Arena source code; if not, write to the Free Software\nFoundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n===========================================================================\n*/\n\ntypedef unsigned short crc_t;\n\nvoid CRC_Init(unsigned short *crcvalue);\nvoid CRC_ProcessByte(unsigned short *crcvalue, byte data);\nunsigned short CRC_Value(unsigned short crcvalue);\nunsigned short CRC_ProcessString(unsigned char *data, int length);\nvoid CRC_ContinueProcessString(unsigned short *crc, char *data, int length);\n"
  },
  {
    "path": "plugins/quake3/botlib/l_libvar.c",
    "content": "/*\n===========================================================================\nCopyright (C) 1999-2005 Id Software, Inc.\n\nThis file is part of Quake III Arena source code.\n\nQuake III Arena source code is free software; you can redistribute it\nand/or modify it under the terms of the GNU General Public License as\npublished by the Free Software Foundation; either version 2 of the License,\nor (at your option) any later version.\n\nQuake III Arena source code is distributed in the hope that it will be\nuseful, but WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Quake III Arena source code; if not, write to the Free Software\nFoundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n===========================================================================\n*/\n\n/*****************************************************************************\n * name:\t\tl_libvar.c\n *\n * desc:\t\tbot library variables\n *\n * $Archive: /MissionPack/code/botlib/l_libvar.c $\n *\n *****************************************************************************/\n\n#include \"q_shared.h\"\n#include \"l_memory.h\"\n#include \"l_libvar.h\"\n\n//list with library variables\nlibvar_t *libvarlist = NULL;\n\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nfloat LibVarStringValue(char *string)\n{\n\tint dotfound = 0;\n\tfloat value = 0;\n\n\twhile(*string)\n\t{\n\t\tif (*string < '0' || *string > '9')\n\t\t{\n\t\t\tif (dotfound || *string != '.')\n\t\t\t{\n\t\t\t\treturn 0;\n\t\t\t} //end if\n\t\t\telse\n\t\t\t{\n\t\t\t\tdotfound = 10;\n\t\t\t\tstring++;\n\t\t\t} //end if\n\t\t} //end if\n\t\tif (dotfound)\n\t\t{\n\t\t\tvalue = value + (float) (*string - '0') / (float) dotfound;\n\t\t\tdotfound *= 10;\n\t\t} //end if\n\t\telse\n\t\t{\n\t\t\tvalue = value * 10.0 + (float) (*string - '0');\n\t\t} //end else\n\t\tstring++;\n\t} //end while\n\treturn value;\n} //end of the function LibVarStringValue\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nlibvar_t *LibVarAlloc(char *var_name)\n{\n\tlibvar_t *v;\n\n\tv = (libvar_t *) GetMemory(sizeof(libvar_t));\n\tCom_Memset(v, 0, sizeof(libvar_t));\n\tv->name = (char *) GetMemory(strlen(var_name)+1);\n\tstrcpy(v->name, var_name);\n\t//add the variable in the list\n\tv->next = libvarlist;\n\tlibvarlist = v;\n\treturn v;\n} //end of the function LibVarAlloc\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid LibVarDeAlloc(libvar_t *v)\n{\n\tif (v->string) FreeMemory(v->string);\n\tFreeMemory(v->name);\n\tFreeMemory(v);\n} //end of the function LibVarDeAlloc\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid LibVarDeAllocAll(void)\n{\n\tlibvar_t *v;\n\n\tfor (v = libvarlist; v; v = libvarlist)\n\t{\n\t\tlibvarlist = libvarlist->next;\n\t\tLibVarDeAlloc(v);\n\t} //end for\n\tlibvarlist = NULL;\n} //end of the function LibVarDeAllocAll\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nlibvar_t *LibVarGet(char *var_name)\n{\n\tlibvar_t *v;\n\n\tfor (v = libvarlist; v; v = v->next)\n\t{\n\t\tif (!Q_stricmp(v->name, var_name))\n\t\t{\n\t\t\treturn v;\n\t\t} //end if\n\t} //end for\n\treturn NULL;\n} //end of the function LibVarGet\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nchar *LibVarGetString(char *var_name)\n{\n\tlibvar_t *v;\n\n\tv = LibVarGet(var_name);\n\tif (v)\n\t{\n\t\treturn v->string;\n\t} //end if\n\telse\n\t{\n\t\treturn \"\";\n\t} //end else\n} //end of the function LibVarGetString\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nfloat LibVarGetValue(char *var_name)\n{\n\tlibvar_t *v;\n\n\tv = LibVarGet(var_name);\n\tif (v)\n\t{\n\t\treturn v->value;\n\t} //end if\n\telse\n\t{\n\t\treturn 0;\n\t} //end else\n} //end of the function LibVarGetValue\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nlibvar_t *LibVar(char *var_name, char *value)\n{\n\tlibvar_t *v;\n\tv = LibVarGet(var_name);\n\tif (v) return v;\n\t//create new variable\n\tv = LibVarAlloc(var_name);\n\t//variable string\n\tv->string = (char *) GetMemory(strlen(value) + 1);\n\tstrcpy(v->string, value);\n\t//the value\n\tv->value = LibVarStringValue(v->string);\n\t//variable is modified\n\tv->modified = qtrue;\n\t//\n\treturn v;\n} //end of the function LibVar\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nchar *LibVarString(char *var_name, char *value)\n{\n\tlibvar_t *v;\n\n\tv = LibVar(var_name, value);\n\treturn v->string;\n} //end of the function LibVarString\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nfloat LibVarValue(char *var_name, char *value)\n{\n\tlibvar_t *v;\n\n\tv = LibVar(var_name, value);\n\treturn v->value;\n} //end of the function LibVarValue\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid LibVarSet(char *var_name, char *value)\n{\n\tlibvar_t *v;\n\n\tv = LibVarGet(var_name);\n\tif (v)\n\t{\n\t\tFreeMemory(v->string);\n\t} //end if\n\telse\n\t{\n\t\tv = LibVarAlloc(var_name);\n\t} //end else\n\t//variable string\n\tv->string = (char *) GetMemory(strlen(value) + 1);\n\tstrcpy(v->string, value);\n\t//the value\n\tv->value = LibVarStringValue(v->string);\n\t//variable is modified\n\tv->modified = qtrue;\n} //end of the function LibVarSet\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nqboolean LibVarChanged(char *var_name)\n{\n\tlibvar_t *v;\n\n\tv = LibVarGet(var_name);\n\tif (v)\n\t{\n\t\treturn v->modified;\n\t} //end if\n\telse\n\t{\n\t\treturn qfalse;\n\t} //end else\n} //end of the function LibVarChanged\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid LibVarSetNotModified(char *var_name)\n{\n\tlibvar_t *v;\n\n\tv = LibVarGet(var_name);\n\tif (v)\n\t{\n\t\tv->modified = qfalse;\n\t} //end if\n} //end of the function LibVarSetNotModified\n"
  },
  {
    "path": "plugins/quake3/botlib/l_libvar.h",
    "content": "/*\n===========================================================================\nCopyright (C) 1999-2005 Id Software, Inc.\n\nThis file is part of Quake III Arena source code.\n\nQuake III Arena source code is free software; you can redistribute it\nand/or modify it under the terms of the GNU General Public License as\npublished by the Free Software Foundation; either version 2 of the License,\nor (at your option) any later version.\n\nQuake III Arena source code is distributed in the hope that it will be\nuseful, but WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Quake III Arena source code; if not, write to the Free Software\nFoundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n===========================================================================\n*/\n\n/*****************************************************************************\n * name:\t\tl_libvar.h\n *\n * desc:\t\tbotlib vars\n *\n * $Archive: /source/code/botlib/l_libvar.h $\n *\n *****************************************************************************/\n\n//library variable\ntypedef struct libvar_s\n{\n\tchar\t\t*name;\n\tchar\t\t*string;\n\tint\t\tflags;\n\tqboolean\tmodified;\t// set each time the cvar is changed\n\tfloat\t\tvalue;\n\tstruct\tlibvar_s *next;\n} libvar_t;\n\n//removes all library variables\nvoid LibVarDeAllocAll(void);\n//gets the library variable with the given name\nlibvar_t *LibVarGet(char *var_name);\n//gets the string of the library variable with the given name\nchar *LibVarGetString(char *var_name);\n//gets the value of the library variable with the given name\nfloat LibVarGetValue(char *var_name);\n//creates the library variable if not existing already and returns it\nlibvar_t *LibVar(char *var_name, char *value);\n//creates the library variable if not existing already and returns the value\nfloat LibVarValue(char *var_name, char *value);\n//creates the library variable if not existing already and returns the value string\nchar *LibVarString(char *var_name, char *value);\n//sets the library variable\nvoid LibVarSet(char *var_name, char *value);\n//returns true if the library variable has been modified\nqboolean LibVarChanged(char *var_name);\n//sets the library variable to unmodified\nvoid LibVarSetNotModified(char *var_name);\n\n"
  },
  {
    "path": "plugins/quake3/botlib/l_log.c",
    "content": "/*\n===========================================================================\nCopyright (C) 1999-2005 Id Software, Inc.\n\nThis file is part of Quake III Arena source code.\n\nQuake III Arena source code is free software; you can redistribute it\nand/or modify it under the terms of the GNU General Public License as\npublished by the Free Software Foundation; either version 2 of the License,\nor (at your option) any later version.\n\nQuake III Arena source code is distributed in the hope that it will be\nuseful, but WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Quake III Arena source code; if not, write to the Free Software\nFoundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n===========================================================================\n*/\n\n/*****************************************************************************\n * name:\t\tl_log.c\n *\n * desc:\t\tlog file\n *\n * $Archive: /MissionPack/CODE/botlib/l_log.c $\n *\n *****************************************************************************/\n\n#include <stdlib.h>\n#include <stdio.h>\n#include <string.h>\n\n#include \"q_shared.h\"\n#include \"botlib.h\"\n#include \"be_interface.h\"\t\t\t//for botimport.Print\n#include \"l_libvar.h\"\n#include \"l_log.h\"\n\n#define MAX_LOGFILENAMESIZE\t\t1024\n\ntypedef struct logfile_s\n{\n\tchar filename[MAX_LOGFILENAMESIZE];\n\tFILE *fp;\n\tint numwrites;\n} logfile_t;\n\nstatic logfile_t logfile;\n\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid Log_Open(char *filename)\n{\n\tif (!LibVarValue(\"log\", \"0\")) return;\n\tif (!filename || !strlen(filename))\n\t{\n\t\tbotimport.Print(PRT_MESSAGE, \"openlog <filename>\\n\");\n\t\treturn;\n\t} //end if\n\tif (logfile.fp)\n\t{\n\t\tbotimport.Print(PRT_ERROR, \"log file %s is already opened\\n\", logfile.filename);\n\t\treturn;\n\t} //end if\n\tlogfile.fp = fopen(filename, \"wb\");\n\tif (!logfile.fp)\n\t{\n\t\tbotimport.Print(PRT_ERROR, \"can't open the log file %s\\n\", filename);\n\t\treturn;\n\t} //end if\n\tstrncpy(logfile.filename, filename, MAX_LOGFILENAMESIZE-1);\n\tlogfile.filename[MAX_LOGFILENAMESIZE-1] = 0;\n\tbotimport.Print(PRT_MESSAGE, \"Opened log %s\\n\", logfile.filename);\n} //end of the function Log_Create\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid Log_Close(void)\n{\n\tif (!logfile.fp) return;\n\tif (fclose(logfile.fp))\n\t{\n\t\tbotimport.Print(PRT_ERROR, \"can't close log file %s\\n\", logfile.filename);\n\t\treturn;\n\t} //end if\n\tlogfile.fp = NULL;\n\tbotimport.Print(PRT_MESSAGE, \"Closed log %s\\n\", logfile.filename);\n} //end of the function Log_Close\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid Log_Shutdown(void)\n{\n\tif (logfile.fp) Log_Close();\n} //end of the function Log_Shutdown\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid QDECL Log_Write(char *fmt, ...)\n{\n\tva_list ap;\n\n\tif (!logfile.fp) return;\n\tva_start(ap, fmt);\n\tvfprintf(logfile.fp, fmt, ap);\n\tva_end(ap);\n\t//fprintf(logfile.fp, \"\\r\\n\");\n\tfflush(logfile.fp);\n} //end of the function Log_Write\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid QDECL Log_WriteTimeStamped(char *fmt, ...)\n{\n\tva_list ap;\n\n\tif (!logfile.fp) return;\n\tfprintf(logfile.fp, \"%d   %02d:%02d:%02d:%02d   \",\n\t\t\t\t\tlogfile.numwrites,\n\t\t\t\t\t(int) (botlibglobals.time / 60 / 60),\n\t\t\t\t\t(int) (botlibglobals.time / 60),\n\t\t\t\t\t(int) (botlibglobals.time),\n\t\t\t\t\t(int) ((int) (botlibglobals.time * 100)) -\n\t\t\t\t\t\t\t((int) botlibglobals.time) * 100);\n\tva_start(ap, fmt);\n\tvfprintf(logfile.fp, fmt, ap);\n\tva_end(ap);\n\tfprintf(logfile.fp, \"\\r\\n\");\n\tlogfile.numwrites++;\n\tfflush(logfile.fp);\n} //end of the function Log_Write\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nFILE *Log_FilePointer(void)\n{\n\treturn logfile.fp;\n} //end of the function Log_FilePointer\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid Log_Flush(void)\n{\n\tif (logfile.fp) fflush(logfile.fp);\n} //end of the function Log_Flush\n\n"
  },
  {
    "path": "plugins/quake3/botlib/l_log.h",
    "content": "/*\n===========================================================================\nCopyright (C) 1999-2005 Id Software, Inc.\n\nThis file is part of Quake III Arena source code.\n\nQuake III Arena source code is free software; you can redistribute it\nand/or modify it under the terms of the GNU General Public License as\npublished by the Free Software Foundation; either version 2 of the License,\nor (at your option) any later version.\n\nQuake III Arena source code is distributed in the hope that it will be\nuseful, but WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Quake III Arena source code; if not, write to the Free Software\nFoundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n===========================================================================\n*/\n\n/*****************************************************************************\n * name:\t\tl_log.h\n *\n * desc:\t\tlog file\n *\n * $Archive: /source/code/botlib/l_log.h $\n *\n *****************************************************************************/\n\n//open a log file\nvoid Log_Open(char *filename);\n//close the current log file\nvoid Log_Close(void);\n//close log file if present\nvoid Log_Shutdown(void);\n//write to the current opened log file\nvoid QDECL Log_Write(char *fmt, ...);\n//write to the current opened log file with a time stamp\nvoid QDECL Log_WriteTimeStamped(char *fmt, ...);\n//returns a pointer to the log file\nFILE *Log_FilePointer(void);\n//flush log file\nvoid Log_Flush(void);\n\n"
  },
  {
    "path": "plugins/quake3/botlib/l_memory.c",
    "content": "/*\n===========================================================================\nCopyright (C) 1999-2005 Id Software, Inc.\n\nThis file is part of Quake III Arena source code.\n\nQuake III Arena source code is free software; you can redistribute it\nand/or modify it under the terms of the GNU General Public License as\npublished by the Free Software Foundation; either version 2 of the License,\nor (at your option) any later version.\n\nQuake III Arena source code is distributed in the hope that it will be\nuseful, but WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Quake III Arena source code; if not, write to the Free Software\nFoundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n===========================================================================\n*/\n\n/*****************************************************************************\n * name:\t\tl_memory.c\n *\n * desc:\t\tmemory allocation\n *\n * $Archive: /MissionPack/code/botlib/l_memory.c $\n *\n *****************************************************************************/\n\n#include \"q_shared.h\"\n#include \"botlib.h\"\n#include \"l_log.h\"\n#include \"l_memory.h\"\n#include \"be_interface.h\"\n\n//#define MEMDEBUG\n//#define MEMORYMANEGER\n\n#define MEM_ID\t\t0x12345678l\n#define HUNK_ID\t\t0x87654321l\n\nint allocatedmemory;\nint totalmemorysize;\nint numblocks;\n\n#ifdef MEMORYMANEGER\n\ntypedef struct memoryblock_s\n{\n\tunsigned long int id;\n\tvoid *ptr;\n\tint size;\n#ifdef MEMDEBUG\n\tchar *label;\n\tchar *file;\n\tint line;\n#endif //MEMDEBUG\n\tstruct memoryblock_s *prev, *next;\n} memoryblock_t;\n\nmemoryblock_t *memory;\n\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid LinkMemoryBlock(memoryblock_t *block)\n{\n\tblock->prev = NULL;\n\tblock->next = memory;\n\tif (memory) memory->prev = block;\n\tmemory = block;\n} //end of the function LinkMemoryBlock\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid UnlinkMemoryBlock(memoryblock_t *block)\n{\n\tif (block->prev) block->prev->next = block->next;\n\telse memory = block->next;\n\tif (block->next) block->next->prev = block->prev;\n} //end of the function UnlinkMemoryBlock\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\n#ifdef MEMDEBUG\nvoid *GetMemoryDebug(unsigned long size, char *label, char *file, int line)\n#else\nvoid *GetMemory(unsigned long size)\n#endif //MEMDEBUG\n{\n\tvoid *ptr;\n\tmemoryblock_t *block;\n\tassert(botimport.GetMemory);\n\tptr = botimport.GetMemory(size + sizeof(memoryblock_t));\n\tblock = (memoryblock_t *) ptr;\n\tblock->id = MEM_ID;\n\tblock->ptr = (char *) ptr + sizeof(memoryblock_t);\n\tblock->size = size + sizeof(memoryblock_t);\n#ifdef MEMDEBUG\n\tblock->label = label;\n\tblock->file = file;\n\tblock->line = line;\n#endif //MEMDEBUG\n\tLinkMemoryBlock(block);\n\tallocatedmemory += block->size;\n\ttotalmemorysize += block->size + sizeof(memoryblock_t);\n\tnumblocks++;\n\treturn block->ptr;\n} //end of the function GetMemoryDebug\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\n#ifdef MEMDEBUG\nvoid *GetClearedMemoryDebug(unsigned long size, char *label, char *file, int line)\n#else\nvoid *GetClearedMemory(unsigned long size)\n#endif //MEMDEBUG\n{\n\tvoid *ptr;\n#ifdef MEMDEBUG\n\tptr = GetMemoryDebug(size, label, file, line);\n#else\n\tptr = GetMemory(size);\n#endif //MEMDEBUG\n\tCom_Memset(ptr, 0, size);\n\treturn ptr;\n} //end of the function GetClearedMemory\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\n#ifdef MEMDEBUG\nvoid *GetHunkMemoryDebug(unsigned long size, char *label, char *file, int line)\n#else\nvoid *GetHunkMemory(unsigned long size)\n#endif //MEMDEBUG\n{\n\tvoid *ptr;\n\tmemoryblock_t *block;\n\n\tptr = botimport.HunkAlloc(size + sizeof(memoryblock_t));\n\tblock = (memoryblock_t *) ptr;\n\tblock->id = HUNK_ID;\n\tblock->ptr = (char *) ptr + sizeof(memoryblock_t);\n\tblock->size = size + sizeof(memoryblock_t);\n#ifdef MEMDEBUG\n\tblock->label = label;\n\tblock->file = file;\n\tblock->line = line;\n#endif //MEMDEBUG\n\tLinkMemoryBlock(block);\n\tallocatedmemory += block->size;\n\ttotalmemorysize += block->size + sizeof(memoryblock_t);\n\tnumblocks++;\n\treturn block->ptr;\n} //end of the function GetHunkMemoryDebug\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\n#ifdef MEMDEBUG\nvoid *GetClearedHunkMemoryDebug(unsigned long size, char *label, char *file, int line)\n#else\nvoid *GetClearedHunkMemory(unsigned long size)\n#endif //MEMDEBUG\n{\n\tvoid *ptr;\n#ifdef MEMDEBUG\n\tptr = GetHunkMemoryDebug(size, label, file, line);\n#else\n\tptr = GetHunkMemory(size);\n#endif //MEMDEBUG\n\tCom_Memset(ptr, 0, size);\n\treturn ptr;\n} //end of the function GetClearedHunkMemory\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nmemoryblock_t *BlockFromPointer(void *ptr, char *str)\n{\n\tmemoryblock_t *block;\n\n\tif (!ptr)\n\t{\n#ifdef MEMDEBUG\n\t\t//char *crash = (char *) NULL;\n\t\t//crash[0] = 1;\n\t\tbotimport.Print(PRT_FATAL, \"%s: NULL pointer\\n\", str);\n#endif // MEMDEBUG\n\t\treturn NULL;\n\t} //end if\n\tblock = (memoryblock_t *) ((char *) ptr - sizeof(memoryblock_t));\n\tif (block->id != MEM_ID && block->id != HUNK_ID)\n\t{\n\t\tbotimport.Print(PRT_FATAL, \"%s: invalid memory block\\n\", str);\n\t\treturn NULL;\n\t} //end if\n\tif (block->ptr != ptr)\n\t{\n\t\tbotimport.Print(PRT_FATAL, \"%s: memory block pointer invalid\\n\", str);\n\t\treturn NULL;\n\t} //end if\n\treturn block;\n} //end of the function BlockFromPointer\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid FreeMemory(void *ptr)\n{\n\tmemoryblock_t *block;\n\n\tblock = BlockFromPointer(ptr, \"FreeMemory\");\n\tif (!block) return;\n\tUnlinkMemoryBlock(block);\n\tallocatedmemory -= block->size;\n\ttotalmemorysize -= block->size + sizeof(memoryblock_t);\n\tnumblocks--;\n\t//\n\tif (block->id == MEM_ID)\n\t{\n\t\tbotimport.FreeMemory(block);\n\t} //end if\n} //end of the function FreeMemory\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint AvailableMemory(void)\n{\n\treturn botimport.AvailableMemory();\n} //end of the function AvailableMemory\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint MemoryByteSize(void *ptr)\n{\n\tmemoryblock_t *block;\n\n\tblock = BlockFromPointer(ptr, \"MemoryByteSize\");\n\tif (!block) return 0;\n\treturn block->size;\n} //end of the function MemoryByteSize\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid PrintUsedMemorySize(void)\n{\n\tbotimport.Print(PRT_MESSAGE, \"total allocated memory: %d KB\\n\", allocatedmemory >> 10);\n\tbotimport.Print(PRT_MESSAGE, \"total botlib memory: %d KB\\n\", totalmemorysize >> 10);\n\tbotimport.Print(PRT_MESSAGE, \"total memory blocks: %d\\n\", numblocks);\n} //end of the function PrintUsedMemorySize\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid PrintMemoryLabels(void)\n{\n\tmemoryblock_t *block;\n\tint i;\n\n\tPrintUsedMemorySize();\n\ti = 0;\n\tLog_Write(\"============= Botlib memory log ==============\\r\\n\");\n\tLog_Write(\"\\r\\n\");\n\tfor (block = memory; block; block = block->next)\n\t{\n#ifdef MEMDEBUG\n\t\tif (block->id == HUNK_ID)\n\t\t{\n\t\t\tLog_Write(\"%6d, hunk %p, %8d: %24s line %6d: %s\\r\\n\", i, block->ptr, block->size, block->file, block->line, block->label);\n\t\t} //end if\n\t\telse\n\t\t{\n\t\t\tLog_Write(\"%6d,      %p, %8d: %24s line %6d: %s\\r\\n\", i, block->ptr, block->size, block->file, block->line, block->label);\n\t\t} //end else\n#endif //MEMDEBUG\n\t\ti++;\n\t} //end for\n} //end of the function PrintMemoryLabels\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid DumpMemory(void)\n{\n\tmemoryblock_t *block;\n\n\tfor (block = memory; block; block = memory)\n\t{\n\t\tFreeMemory(block->ptr);\n\t} //end for\n\ttotalmemorysize = 0;\n\tallocatedmemory = 0;\n} //end of the function DumpMemory\n\n#else\n\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\n#ifdef MEMDEBUG\nvoid *GetMemoryDebug(unsigned long size, char *label, char *file, int line)\n#else\nvoid *GetMemory(unsigned long size)\n#endif //MEMDEBUG\n{\n\tvoid *ptr;\n\tunsigned long int *memid;\n\n\tptr = botimport.GetMemory(size + sizeof(unsigned long int));\n\tif (!ptr) return NULL;\n\tmemid = (unsigned long int *) ptr;\n\t*memid = MEM_ID;\n\treturn (unsigned long int *) ((char *) ptr + sizeof(unsigned long int));\n} //end of the function GetMemory\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\n#ifdef MEMDEBUG\nvoid *GetClearedMemoryDebug(unsigned long size, char *label, char *file, int line)\n#else\nvoid *GetClearedMemory(unsigned long size)\n#endif //MEMDEBUG\n{\n\tvoid *ptr;\n#ifdef MEMDEBUG\n\tptr = GetMemoryDebug(size, label, file, line);\n#else\n\tptr = GetMemory(size);\n#endif //MEMDEBUG\n\tCom_Memset(ptr, 0, size);\n\treturn ptr;\n} //end of the function GetClearedMemory\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\n#ifdef MEMDEBUG\nvoid *GetHunkMemoryDebug(unsigned long size, char *label, char *file, int line)\n#else\nvoid *GetHunkMemory(unsigned long size)\n#endif //MEMDEBUG\n{\n\tvoid *ptr;\n\tunsigned long int *memid;\n\n\tptr = botimport.HunkAlloc(size + sizeof(unsigned long int));\n\tif (!ptr) return NULL;\n\tmemid = (unsigned long int *) ptr;\n\t*memid = HUNK_ID;\n\treturn (unsigned long int *) ((char *) ptr + sizeof(unsigned long int));\n} //end of the function GetHunkMemory\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\n#ifdef MEMDEBUG\nvoid *GetClearedHunkMemoryDebug(unsigned long size, char *label, char *file, int line)\n#else\nvoid *GetClearedHunkMemory(unsigned long size)\n#endif //MEMDEBUG\n{\n\tvoid *ptr;\n#ifdef MEMDEBUG\n\tptr = GetHunkMemoryDebug(size, label, file, line);\n#else\n\tptr = GetHunkMemory(size);\n#endif //MEMDEBUG\n\tCom_Memset(ptr, 0, size);\n\treturn ptr;\n} //end of the function GetClearedHunkMemory\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid FreeMemory(void *ptr)\n{\n\tunsigned long int *memid;\n\n\tmemid = (unsigned long int *) ((char *) ptr - sizeof(unsigned long int));\n\n\tif (*memid == MEM_ID)\n\t{\n\t\tbotimport.FreeMemory(memid);\n\t} //end if\n} //end of the function FreeMemory\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint AvailableMemory(void)\n{\n\treturn botimport.AvailableMemory();\n} //end of the function AvailableMemory\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid PrintUsedMemorySize(void)\n{\n} //end of the function PrintUsedMemorySize\n//===========================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid PrintMemoryLabels(void)\n{\n} //end of the function PrintMemoryLabels\n\n#endif\n"
  },
  {
    "path": "plugins/quake3/botlib/l_memory.h",
    "content": "/*\n===========================================================================\nCopyright (C) 1999-2005 Id Software, Inc.\n\nThis file is part of Quake III Arena source code.\n\nQuake III Arena source code is free software; you can redistribute it\nand/or modify it under the terms of the GNU General Public License as\npublished by the Free Software Foundation; either version 2 of the License,\nor (at your option) any later version.\n\nQuake III Arena source code is distributed in the hope that it will be\nuseful, but WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Quake III Arena source code; if not, write to the Free Software\nFoundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n===========================================================================\n*/\n\n/*****************************************************************************\n * name:\t\tl_memory.h\n *\n * desc:\t\tmemory management\n *\n * $Archive: /source/code/botlib/l_memory.h $\n *\n *****************************************************************************/\n\n//#define MEMDEBUG\n\n#ifdef MEMDEBUG\n#define GetMemory(size)\t\t\t\tGetMemoryDebug(size, #size, __FILE__, __LINE__);\n#define GetClearedMemory(size)\t\tGetClearedMemoryDebug(size, #size, __FILE__, __LINE__);\n//allocate a memory block of the given size\nvoid *GetMemoryDebug(unsigned long size, char *label, char *file, int line);\n//allocate a memory block of the given size and clear it\nvoid *GetClearedMemoryDebug(unsigned long size, char *label, char *file, int line);\n//\n#define GetHunkMemory(size)\t\t\tGetHunkMemoryDebug(size, #size, __FILE__, __LINE__);\n#define GetClearedHunkMemory(size)\tGetClearedHunkMemoryDebug(size, #size, __FILE__, __LINE__);\n//allocate a memory block of the given size\nvoid *GetHunkMemoryDebug(unsigned long size, char *label, char *file, int line);\n//allocate a memory block of the given size and clear it\nvoid *GetClearedHunkMemoryDebug(unsigned long size, char *label, char *file, int line);\n#else\n//allocate a memory block of the given size\nvoid *GetMemory(unsigned long size);\n//allocate a memory block of the given size and clear it\nvoid *GetClearedMemory(unsigned long size);\n//\n#ifdef BSPC\n#define GetHunkMemory GetMemory\n#define GetClearedHunkMemory GetClearedMemory\n#else\n//allocate a memory block of the given size\nvoid *GetHunkMemory(unsigned long size);\n//allocate a memory block of the given size and clear it\nvoid *GetClearedHunkMemory(unsigned long size);\n#endif\n#endif\n\n//free the given memory block\nvoid FreeMemory(void *ptr);\n//returns the amount available memory\nint AvailableMemory(void);\n//prints the total used memory size\nvoid PrintUsedMemorySize(void);\n//print all memory blocks with label\nvoid PrintMemoryLabels(void);\n//returns the size of the memory block in bytes\nint MemoryByteSize(void *ptr);\n//free all allocated memory\nvoid DumpMemory(void);\n"
  },
  {
    "path": "plugins/quake3/botlib/l_precomp.c",
    "content": "/*\n===========================================================================\nCopyright (C) 1999-2005 Id Software, Inc.\n\nThis file is part of Quake III Arena source code.\n\nQuake III Arena source code is free software; you can redistribute it\nand/or modify it under the terms of the GNU General Public License as\npublished by the Free Software Foundation; either version 2 of the License,\nor (at your option) any later version.\n\nQuake III Arena source code is distributed in the hope that it will be\nuseful, but WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Quake III Arena source code; if not, write to the Free Software\nFoundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n===========================================================================\n*/\n//\n\n/*****************************************************************************\n * name:\t\tl_precomp.c\n *\n * desc:\t\tpre compiler\n *\n * $Archive: /MissionPack/code/botlib/l_precomp.c $\n *\n *****************************************************************************/\n\n//Notes:\t\t\tfix: PC_StringizeTokens\n\n//#define SCREWUP\n//#define BOTLIB\n//#define QUAKE\n//#define QUAKEC\n//#define MEQCC\n\n#ifdef SCREWUP\n#include <stdio.h>\n#include <stdlib.h>\n#include <limits.h>\n#include <string.h>\n#include <stdarg.h>\n#include <time.h>\n#include \"l_memory.h\"\n#include \"l_script.h\"\n#include \"l_precomp.h\"\n\ntypedef enum {qfalse, qtrue}\tqboolean;\n#endif //SCREWUP\n\n#ifdef BOTLIB\n#include \"q_shared.h\"\n#include \"botlib.h\"\n#include \"be_interface.h\"\n#include \"l_memory.h\"\n#include \"l_script.h\"\n#include \"l_precomp.h\"\n#include \"l_log.h\"\n#endif //BOTLIB\n\n#ifdef MEQCC\n#include \"qcc.h\"\n#include \"time.h\"   //time & ctime\n#include \"math.h\"   //fabs\n#include \"l_memory.h\"\n#include \"l_script.h\"\n#include \"l_precomp.h\"\n#include \"l_log.h\"\n\n#define qtrue\ttrue\n#define qfalse\tfalse\n#endif //MEQCC\n\n#ifdef BSPC\n//include files for usage in the BSP Converter\n#include \"../bspc/qbsp.h\"\n#include \"../bspc/l_log.h\"\n#include \"../bspc/l_mem.h\"\n#include \"l_precomp.h\"\n\n#define qtrue\ttrue\n#define qfalse\tfalse\n#define Q_stricmp\tstricmp\n\n#endif //BSPC\n\n#if defined(QUAKE) && !defined(BSPC)\n#include \"l_utils.h\"\n#endif //QUAKE\n\n//#define DEBUG_EVAL\n\n#define MAX_DEFINEPARMS\t\t\t128\n\n#define DEFINEHASHING\t\t\t1\n\n//directive name with parse function\ntypedef struct directive_s\n{\n\tchar *name;\n\tint (*func)(source_t *source);\n} directive_t;\n\n#define DEFINEHASHSIZE\t\t1024\n\n#define TOKEN_HEAP_SIZE\t\t4096\n\n#ifdef _WIN64\n        #ifdef FTE_SDL\n                #define vsnprintf linuxlike_vsnprintf\n        #endif\n#endif\n\nint numtokens;\n/*\nint tokenheapinitialized;\t\t\t\t//true when the token heap is initialized\ntoken_t token_heap[TOKEN_HEAP_SIZE];\t//heap with tokens\ntoken_t *freetokens;\t\t\t\t\t//free tokens from the heap\n*/\n\n//list with global defines added to every source loaded\ndefine_t *globaldefines;\n\n//============================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//============================================================================\nvoid QDECL SourceError(source_t *source, char *str, ...)\n{\n\tchar text[1024];\n\tva_list ap;\n\n\tva_start(ap, str);\n\tvsnprintf(text, sizeof(text), str, ap);\n\tva_end(ap);\n#ifdef BOTLIB\n\tbotimport.Print(PRT_ERROR, \"file %s, line %d: %s\\n\", source->scriptstack->filename, source->scriptstack->line, text);\n#endif\t//BOTLIB\n#ifdef MEQCC\n\tprintf(\"error: file %s, line %d: %s\\n\", source->scriptstack->filename, source->scriptstack->line, text);\n#endif //MEQCC\n#ifdef BSPC\n\tLog_Print(\"error: file %s, line %d: %s\\n\", source->scriptstack->filename, source->scriptstack->line, text);\n#endif //BSPC\n} //end of the function SourceError\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid QDECL SourceWarning(source_t *source, char *str, ...)\n{\n\tchar text[1024];\n\tva_list ap;\n\n\tva_start(ap, str);\n\tvsnprintf(text, sizeof(text), str, ap);\n\tva_end(ap);\n#ifdef BOTLIB\n\tbotimport.Print(PRT_WARNING, \"file %s, line %d: %s\\n\", source->scriptstack->filename, source->scriptstack->line, text);\n#endif //BOTLIB\n#ifdef MEQCC\n\tprintf(\"warning: file %s, line %d: %s\\n\", source->scriptstack->filename, source->scriptstack->line, text);\n#endif //MEQCC\n#ifdef BSPC\n\tLog_Print(\"warning: file %s, line %d: %s\\n\", source->scriptstack->filename, source->scriptstack->line, text);\n#endif //BSPC\n} //end of the function ScriptWarning\n//============================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//============================================================================\nvoid PC_PushIndent(source_t *source, int type, int skip)\n{\n\tindent_t *indent;\n\n\tindent = (indent_t *) GetMemory(sizeof(indent_t));\n\tindent->type = type;\n\tindent->script = source->scriptstack;\n\tindent->skip = (skip != 0);\n\tsource->skip += indent->skip;\n\tindent->next = source->indentstack;\n\tsource->indentstack = indent;\n} //end of the function PC_PushIndent\n//============================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//============================================================================\nvoid PC_PopIndent(source_t *source, int *type, int *skip)\n{\n\tindent_t *indent;\n\n\t*type = 0;\n\t*skip = 0;\n\n\tindent = source->indentstack;\n\tif (!indent) return;\n\n\t//must be an indent from the current script\n\tif (source->indentstack->script != source->scriptstack) return;\n\n\t*type = indent->type;\n\t*skip = indent->skip;\n\tsource->indentstack = source->indentstack->next;\n\tsource->skip -= indent->skip;\n\tFreeMemory(indent);\n} //end of the function PC_PopIndent\n//============================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//============================================================================\nvoid PC_PushScript(source_t *source, script_t *script)\n{\n\tscript_t *s;\n\n\tfor (s = source->scriptstack; s; s = s->next)\n\t{\n\t\tif (!Q_stricmp(s->filename, script->filename))\n\t\t{\n\t\t\tSourceError(source, \"%s recursively included\", script->filename);\n\t\t\treturn;\n\t\t} //end if\n\t} //end for\n\t//push the script on the script stack\n\tscript->next = source->scriptstack;\n\tsource->scriptstack = script;\n} //end of the function PC_PushScript\n//============================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//============================================================================\nvoid PC_InitTokenHeap(void)\n{\n\t/*\n\tint i;\n\n\tif (tokenheapinitialized) return;\n\tfreetokens = NULL;\n\tfor (i = 0; i < TOKEN_HEAP_SIZE; i++)\n\t{\n\t\ttoken_heap[i].next = freetokens;\n\t\tfreetokens = &token_heap[i];\n\t} //end for\n\ttokenheapinitialized = qtrue;\n\t*/\n} //end of the function PC_InitTokenHeap\n//============================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//============================================================================\ntoken_t *PC_CopyToken(token_t *token)\n{\n\ttoken_t *t;\n\n//\tt = (token_t *) malloc(sizeof(token_t));\n\tt = (token_t *) GetMemory(sizeof(token_t));\n//\tt = freetokens;\n\tif (!t)\n\t{\n#ifdef BSPC\n\t\tError(\"out of token space\\n\");\n#else\n\t\tbotimport.Error(\"out of token space\");\n#endif\n\t\treturn NULL;\n\t} //end if\n//\tfreetokens = freetokens->next;\n\tCom_Memcpy(t, token, sizeof(token_t));\n\tt->next = NULL;\n\tnumtokens++;\n\treturn t;\n} //end of the function PC_CopyToken\n//============================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//============================================================================\nvoid PC_FreeToken(token_t *token)\n{\n\t//free(token);\n\tFreeMemory(token);\n//\ttoken->next = freetokens;\n//\tfreetokens = token;\n\tnumtokens--;\n} //end of the function PC_FreeToken\n//============================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//============================================================================\nint PC_ReadSourceToken(source_t *source, token_t *token)\n{\n\ttoken_t *t;\n\tscript_t *script;\n\tint type, skip;\n\n\t//if there's no token already available\n\twhile(!source->tokens)\n\t{\n\t\t//if there's a token to read from the script\n\t\tif (PS_ReadToken(source->scriptstack, token)) return qtrue;\n\t\t//if at the end of the script\n\t\tif (EndOfScript(source->scriptstack))\n\t\t{\n\t\t\t//remove all indents of the script\n\t\t\twhile(source->indentstack &&\n\t\t\t\t\tsource->indentstack->script == source->scriptstack)\n\t\t\t{\n\t\t\t\tSourceWarning(source, \"missing #endif\");\n\t\t\t\tPC_PopIndent(source, &type, &skip);\n\t\t\t} //end if\n\t\t} //end if\n\t\t//if this was the initial script\n\t\tif (!source->scriptstack->next) return qfalse;\n\t\t//remove the script and return to the last one\n\t\tscript = source->scriptstack;\n\t\tsource->scriptstack = source->scriptstack->next;\n\t\tFreeScript(script);\n\t} //end while\n\t//copy the already available token\n\tCom_Memcpy(token, source->tokens, sizeof(token_t));\n\t//free the read token\n\tt = source->tokens;\n\tsource->tokens = source->tokens->next;\n\tPC_FreeToken(t);\n\treturn qtrue;\n} //end of the function PC_ReadSourceToken\n//============================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//============================================================================\nint PC_UnreadSourceToken(source_t *source, token_t *token)\n{\n\ttoken_t *t;\n\n\tt = PC_CopyToken(token);\n\tt->next = source->tokens;\n\tsource->tokens = t;\n\treturn qtrue;\n} //end of the function PC_UnreadSourceToken\n//============================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//============================================================================\nint PC_ReadDefineParms(source_t *source, define_t *define, token_t **parms, int maxparms)\n{\n\ttoken_t token, *t, *last;\n\tint i, done, lastcomma, numparms, indent;\n\n\tif (!PC_ReadSourceToken(source, &token))\n\t{\n\t\tSourceError(source, \"define %s missing parms\", define->name);\n\t\treturn qfalse;\n\t} //end if\n\t//\n\tif (define->numparms > maxparms)\n\t{\n\t\tSourceError(source, \"define with more than %d parameters\", maxparms);\n\t\treturn qfalse;\n\t} //end if\n\t//\n\tfor (i = 0; i < define->numparms; i++) parms[i] = NULL;\n\t//if no leading \"(\"\n\tif (strcmp(token.string, \"(\"))\n\t{\n\t\tPC_UnreadSourceToken(source, &token);\n\t\tSourceError(source, \"define %s missing parms\", define->name);\n\t\treturn qfalse;\n\t} //end if\n\t//read the define parameters\n\tfor (done = 0, numparms = 0, indent = 0; !done;)\n\t{\n\t\tif (numparms >= maxparms)\n\t\t{\n\t\t\tSourceError(source, \"define %s with too many parms\", define->name);\n\t\t\treturn qfalse;\n\t\t} //end if\n\t\tif (numparms >= define->numparms)\n\t\t{\n\t\t\tSourceWarning(source, \"define %s has too many parms\", define->name);\n\t\t\treturn qfalse;\n\t\t} //end if\n\t\tparms[numparms] = NULL;\n\t\tlastcomma = 1;\n\t\tlast = NULL;\n\t\twhile(!done)\n\t\t{\n\t\t\t//\n\t\t\tif (!PC_ReadSourceToken(source, &token))\n\t\t\t{\n\t\t\t\tSourceError(source, \"define %s incomplete\", define->name);\n\t\t\t\treturn qfalse;\n\t\t\t} //end if\n\t\t\t//\n\t\t\tif (!strcmp(token.string, \",\"))\n\t\t\t{\n\t\t\t\tif (indent <= 0)\n\t\t\t\t{\n\t\t\t\t\tif (lastcomma) SourceWarning(source, \"too many comma's\");\n\t\t\t\t\tlastcomma = 1;\n\t\t\t\t\tbreak;\n\t\t\t\t} //end if\n\t\t\t} //end if\n\t\t\tlastcomma = 0;\n\t\t\t//\n\t\t\tif (!strcmp(token.string, \"(\"))\n\t\t\t{\n\t\t\t\tindent++;\n\t\t\t\tcontinue;\n\t\t\t} //end if\n\t\t\telse if (!strcmp(token.string, \")\"))\n\t\t\t{\n\t\t\t\tif (--indent <= 0)\n\t\t\t\t{\n\t\t\t\t\tif (!parms[define->numparms-1])\n\t\t\t\t\t{\n\t\t\t\t\t\tSourceWarning(source, \"too few define parms\");\n\t\t\t\t\t} //end if\n\t\t\t\t\tdone = 1;\n\t\t\t\t\tbreak;\n\t\t\t\t} //end if\n\t\t\t} //end if\n\t\t\t//\n\t\t\tif (numparms < define->numparms)\n\t\t\t{\n\t\t\t\t//\n\t\t\t\tt = PC_CopyToken(&token);\n\t\t\t\tt->next = NULL;\n\t\t\t\tif (last) last->next = t;\n\t\t\t\telse parms[numparms] = t;\n\t\t\t\tlast = t;\n\t\t\t} //end if\n\t\t} //end while\n\t\tnumparms++;\n\t} //end for\n\treturn qtrue;\n} //end of the function PC_ReadDefineParms\n//============================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//============================================================================\nint PC_StringizeTokens(token_t *tokens, token_t *token)\n{\n\tint ret = qtrue;\n\ttoken_t *t;\n\n\ttoken->type = TT_STRING;\n\ttoken->whitespace_p = NULL;\n\ttoken->endwhitespace_p = NULL;\n\ttoken->string[0] = '\\0';\n\tstrcat(token->string, \"\\\"\");\n\tfor (t = tokens; t; t = t->next)\n\t{\n\t\tif (strlen(token->string)+strlen(t->string) >= MAX_TOKEN-1)\n\t\t{\n\t\t\tret = qfalse;\n\t\t\tbreak;\n\t\t}\n\t\tstrcat(token->string, t->string);\n\t} //end for\n\tstrncat(token->string, \"\\\"\", MAX_TOKEN - strlen(token->string) - 1);\n\treturn ret;\n} //end of the function PC_StringizeTokens\n//============================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//============================================================================\nint PC_MergeTokens(token_t *t1, token_t *t2)\n{\n\t//merging of a name with a name or number\n\tif (t1->type == TT_NAME && (t2->type == TT_NAME || t2->type == TT_NUMBER))\n\t{\n\t\tstrcat(t1->string, t2->string);\n\t\treturn qtrue;\n\t} //end if\n\t//merging of two strings\n\tif (t1->type == TT_STRING && t2->type == TT_STRING)\n\t{\n\t\t//remove trailing double quote\n\t\tt1->string[strlen(t1->string)-1] = '\\0';\n\t\t//concat without leading double quote\n\t\tstrcat(t1->string, &t2->string[1]);\n\t\treturn qtrue;\n\t} //end if\n\t//FIXME: merging of two number of the same sub type\n\treturn qfalse;\n} //end of the function PC_MergeTokens\n//============================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//============================================================================\n/*\nvoid PC_PrintDefine(define_t *define)\n{\n\tprintf(\"define->name = %s\\n\", define->name);\n\tprintf(\"define->flags = %d\\n\", define->flags);\n\tprintf(\"define->builtin = %d\\n\", define->builtin);\n\tprintf(\"define->numparms = %d\\n\", define->numparms);\n//\ttoken_t *parms;\t\t\t\t\t//define parameters\n//\ttoken_t *tokens;\t\t\t\t\t//macro tokens (possibly containing parm tokens)\n//\tstruct define_s *next;\t\t\t//next defined macro in a list\n} //end of the function PC_PrintDefine*/\n#if DEFINEHASHING\n//============================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//============================================================================\nvoid PC_PrintDefineHashTable(define_t **definehash)\n{\n\tint i;\n\tdefine_t *d;\n\n\tfor (i = 0; i < DEFINEHASHSIZE; i++)\n\t{\n\t\tLog_Write(\"%4d:\", i);\n\t\tfor (d = definehash[i]; d; d = d->hashnext)\n\t\t{\n\t\t\tLog_Write(\" %s\", d->name);\n\t\t} //end for\n\t\tLog_Write(\"\\n\");\n\t} //end for\n} //end of the function PC_PrintDefineHashTable\n//============================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//============================================================================\n//char primes[16] = {1, 3, 5, 7, 11, 13, 17, 19, 23, 27, 29, 31, 37, 41, 43, 47};\n\nint PC_NameHash(char *name)\n{\n\tregister int hash, i;\n\n\thash = 0;\n\tfor (i = 0; name[i] != '\\0'; i++)\n\t{\n\t\thash += name[i] * (119 + i);\n\t\t//hash += (name[i] << 7) + i;\n\t\t//hash += (name[i] << (i&15));\n\t} //end while\n\thash = (hash ^ (hash >> 10) ^ (hash >> 20)) & (DEFINEHASHSIZE-1);\n\treturn hash;\n} //end of the function PC_NameHash\n//============================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//============================================================================\nvoid PC_AddDefineToHash(define_t *define, define_t **definehash)\n{\n\tint hash;\n\n\thash = PC_NameHash(define->name);\n\tdefine->hashnext = definehash[hash];\n\tdefinehash[hash] = define;\n} //end of the function PC_AddDefineToHash\n//============================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//============================================================================\ndefine_t *PC_FindHashedDefine(define_t **definehash, char *name)\n{\n\tdefine_t *d;\n\tint hash;\n\n\thash = PC_NameHash(name);\n\tfor (d = definehash[hash]; d; d = d->hashnext)\n\t{\n\t\tif (!strcmp(d->name, name)) return d;\n\t} //end for\n\treturn NULL;\n} //end of the function PC_FindHashedDefine\n#endif //DEFINEHASHING\n//============================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//============================================================================\ndefine_t *PC_FindDefine(define_t *defines, char *name)\n{\n\tdefine_t *d;\n\n\tfor (d = defines; d; d = d->next)\n\t{\n\t\tif (!strcmp(d->name, name)) return d;\n\t} //end for\n\treturn NULL;\n} //end of the function PC_FindDefine\n//============================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\tnumber of the parm\n//\t\t\t\t\t\t\t\tif no parm found with the given name -1 is returned\n// Changes Globals:\t\t-\n//============================================================================\nint PC_FindDefineParm(define_t *define, char *name)\n{\n\ttoken_t *p;\n\tint i;\n\n\ti = 0;\n\tfor (p = define->parms; p; p = p->next)\n\t{\n\t\tif (!strcmp(p->string, name)) return i;\n\t\ti++;\n\t} //end for\n\treturn -1;\n} //end of the function PC_FindDefineParm\n//============================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//============================================================================\nvoid PC_FreeDefine(define_t *define)\n{\n\ttoken_t *t, *next;\n\n\t//free the define parameters\n\tfor (t = define->parms; t; t = next)\n\t{\n\t\tnext = t->next;\n\t\tPC_FreeToken(t);\n\t} //end for\n\t//free the define tokens\n\tfor (t = define->tokens; t; t = next)\n\t{\n\t\tnext = t->next;\n\t\tPC_FreeToken(t);\n\t} //end for\n\t//free the define\n\tFreeMemory(define->name);\n\tFreeMemory(define);\n} //end of the function PC_FreeDefine\n//============================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//============================================================================\nvoid PC_AddBuiltinDefines(source_t *source)\n{\n\tint i;\n\tdefine_t *define;\n\tstruct builtin\n\t{\n\t\tchar *string;\n\t\tint builtin;\n\t} builtin[] = {\n\t\t{ \"__LINE__\",\tBUILTIN_LINE },\n\t\t{ \"__FILE__\",\tBUILTIN_FILE },\n\t\t{ \"__DATE__\",\tBUILTIN_DATE },\n\t\t{ \"__TIME__\",\tBUILTIN_TIME },\n//\t\t{ \"__STDC__\", BUILTIN_STDC },\n\t\t{ NULL, 0 }\n\t};\n\n\tfor (i = 0; builtin[i].string; i++)\n\t{\n\t\tdefine = (define_t *) GetMemory(sizeof(define_t));\n\t\tCom_Memset(define, 0, sizeof(define_t));\n\t\tdefine->name = (char *) GetMemory(strlen(builtin[i].string) + 1);\n\t\tstrcpy(define->name, builtin[i].string);\n\t\tdefine->flags |= DEFINE_FIXED;\n\t\tdefine->builtin = builtin[i].builtin;\n\t\t//add the define to the source\n#if DEFINEHASHING\n\t\tPC_AddDefineToHash(define, source->definehash);\n#else\n\t\tdefine->next = source->defines;\n\t\tsource->defines = define;\n#endif //DEFINEHASHING\n\t} //end for\n} //end of the function PC_AddBuiltinDefines\n//============================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//============================================================================\nint PC_ExpandBuiltinDefine(source_t *source, token_t *deftoken, define_t *define,\n\t\t\t\t\t\t\t\t\t\ttoken_t **firsttoken, token_t **lasttoken)\n{\n\ttoken_t *token;\n\ttime_t t;\n\t\n\tchar *curtime;\n\n\ttoken = PC_CopyToken(deftoken);\n\tswitch(define->builtin)\n\t{\n\t\tcase BUILTIN_LINE:\n\t\t{\n\t\t\tsprintf(token->string, \"%d\", deftoken->line);\n#ifdef NUMBERVALUE\n\t\t\ttoken->intvalue = deftoken->line;\n\t\t\ttoken->floatvalue = deftoken->line;\n#endif //NUMBERVALUE\n\t\t\ttoken->type = TT_NUMBER;\n\t\t\ttoken->subtype = TT_DECIMAL | TT_INTEGER;\n\t\t\t*firsttoken = token;\n\t\t\t*lasttoken = token;\n\t\t\tbreak;\n\t\t} //end case\n\t\tcase BUILTIN_FILE:\n\t\t{\n\t\t\tstrcpy(token->string, source->scriptstack->filename);\n\t\t\ttoken->type = TT_NAME;\n\t\t\ttoken->subtype = strlen(token->string);\n\t\t\t*firsttoken = token;\n\t\t\t*lasttoken = token;\n\t\t\tbreak;\n\t\t} //end case\n\t\tcase BUILTIN_DATE:\n\t\t{\n\t\t\tt = time(NULL);\n\t\t\tcurtime = ctime(&t);\n\t\t\tstrcpy(token->string, \"\\\"\");\n\t\t\tstrncat(token->string, curtime+4, 7);\n\t\t\tstrncat(token->string+7, curtime+20, 4);\n\t\t\tstrcat(token->string, \"\\\"\");\n\t\t\tfree(curtime);\n\t\t\ttoken->type = TT_NAME;\n\t\t\ttoken->subtype = strlen(token->string);\n\t\t\t*firsttoken = token;\n\t\t\t*lasttoken = token;\n\t\t\tbreak;\n\t\t} //end case\n\t\tcase BUILTIN_TIME:\n\t\t{\n\t\t\tt = time(NULL);\n\t\t\tcurtime = ctime(&t);\n\t\t\tstrcpy(token->string, \"\\\"\");\n\t\t\tstrncat(token->string, curtime+11, 8);\n\t\t\tstrcat(token->string, \"\\\"\");\n\t\t\tfree(curtime);\n\t\t\ttoken->type = TT_NAME;\n\t\t\ttoken->subtype = strlen(token->string);\n\t\t\t*firsttoken = token;\n\t\t\t*lasttoken = token;\n\t\t\tbreak;\n\t\t} //end case\n\t\tcase BUILTIN_STDC:\n\t\tdefault:\n\t\t{\n\t\t\t*firsttoken = NULL;\n\t\t\t*lasttoken = NULL;\n\t\t\tbreak;\n\t\t} //end case\n\t} //end switch\n\treturn qtrue;\n} //end of the function PC_ExpandBuiltinDefine\n//============================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//============================================================================\nint PC_ExpandDefine(source_t *source, token_t *deftoken, define_t *define,\n\t\t\t\t\t\t\t\t\t\ttoken_t **firsttoken, token_t **lasttoken)\n{\n\ttoken_t *parms[MAX_DEFINEPARMS], *dt, *pt, *t;\n\ttoken_t *t1, *t2, *first, *last, *nextpt, token;\n\tint parmnum, i;\n\n\t//if it is a builtin define\n\tif (define->builtin)\n\t{\n\t\treturn PC_ExpandBuiltinDefine(source, deftoken, define, firsttoken, lasttoken);\n\t} //end if\n\t//if the define has parameters\n\tif (define->numparms)\n\t{\n\t\tif (!PC_ReadDefineParms(source, define, parms, MAX_DEFINEPARMS)) return qfalse;\n#ifdef DEBUG_EVAL\n\t\tfor (i = 0; i < define->numparms; i++)\n\t\t{\n\t\t\tLog_Write(\"define parms %d:\", i);\n\t\t\tfor (pt = parms[i]; pt; pt = pt->next)\n\t\t\t{\n\t\t\t\tLog_Write(\"%s\", pt->string);\n\t\t\t} //end for\n\t\t} //end for\n#endif //DEBUG_EVAL\n\t} //end if\n\t//empty list at first\n\tfirst = NULL;\n\tlast = NULL;\n\t//create a list with tokens of the expanded define\n\tfor (dt = define->tokens; dt; dt = dt->next)\n\t{\n\t\tparmnum = -1;\n\t\t//if the token is a name, it could be a define parameter\n\t\tif (dt->type == TT_NAME)\n\t\t{\n\t\t\tparmnum = PC_FindDefineParm(define, dt->string);\n\t\t} //end if\n\t\t//if it is a define parameter\n\t\tif (parmnum >= 0)\n\t\t{\n\t\t\tfor (pt = parms[parmnum]; pt; pt = pt->next)\n\t\t\t{\n\t\t\t\tt = PC_CopyToken(pt);\n\t\t\t\t//add the token to the list\n\t\t\t\tt->next = NULL;\n\t\t\t\tif (last) last->next = t;\n\t\t\t\telse first = t;\n\t\t\t\tlast = t;\n\t\t\t} //end for\n\t\t} //end if\n\t\telse\n\t\t{\n\t\t\t//if stringizing operator\n\t\t\tif (dt->string[0] == '#' && dt->string[1] == '\\0')\n\t\t\t{\n\t\t\t\t//the stringizing operator must be followed by a define parameter\n\t\t\t\tif (dt->next) parmnum = PC_FindDefineParm(define, dt->next->string);\n\t\t\t\telse parmnum = -1;\n\t\t\t\t//\n\t\t\t\tif (parmnum >= 0)\n\t\t\t\t{\n\t\t\t\t\t//step over the stringizing operator\n\t\t\t\t\tdt = dt->next;\n\t\t\t\t\t//stringize the define parameter tokens\n\t\t\t\t\tif (!PC_StringizeTokens(parms[parmnum], &token))\n\t\t\t\t\t{\n\t\t\t\t\t\tSourceError(source, \"can't stringize tokens\");\n\t\t\t\t\t\treturn qfalse;\n\t\t\t\t\t} //end if\n\t\t\t\t\tt = PC_CopyToken(&token);\n\t\t\t\t} //end if\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tSourceWarning(source, \"stringizing operator without define parameter\");\n\t\t\t\t\tcontinue;\n\t\t\t\t} //end if\n\t\t\t} //end if\n\t\t\telse\n\t\t\t{\n\t\t\t\tt = PC_CopyToken(dt);\n\t\t\t} //end else\n\t\t\t//add the token to the list\n\t\t\tt->next = NULL;\n\t\t\tif (last) last->next = t;\n\t\t\telse first = t;\n\t\t\tlast = t;\n\t\t} //end else\n\t} //end for\n\t//check for the merging operator\n\tfor (t = first; t; )\n\t{\n\t\tif (t->next)\n\t\t{\n\t\t\t//if the merging operator\n\t\t\tif (t->next->string[0] == '#' && t->next->string[1] == '#')\n\t\t\t{\n\t\t\t\tt1 = t;\n\t\t\t\tt2 = t->next->next;\n\t\t\t\tif (t2)\n\t\t\t\t{\n\t\t\t\t\tif (!PC_MergeTokens(t1, t2))\n\t\t\t\t\t{\n\t\t\t\t\t\tSourceError(source, \"can't merge %s with %s\", t1->string, t2->string);\n\t\t\t\t\t\treturn qfalse;\n\t\t\t\t\t} //end if\n\t\t\t\t\tPC_FreeToken(t1->next);\n\t\t\t\t\tt1->next = t2->next;\n\t\t\t\t\tif (t2 == last) last = t1;\n\t\t\t\t\tPC_FreeToken(t2);\n\t\t\t\t\tcontinue;\n\t\t\t\t} //end if\n\t\t\t} //end if\n\t\t} //end if\n\t\tt = t->next;\n\t} //end for\n\t//store the first and last token of the list\n\t*firsttoken = first;\n\t*lasttoken = last;\n\t//free all the parameter tokens\n\tfor (i = 0; i < define->numparms; i++)\n\t{\n\t\tfor (pt = parms[i]; pt; pt = nextpt)\n\t\t{\n\t\t\tnextpt = pt->next;\n\t\t\tPC_FreeToken(pt);\n\t\t} //end for\n\t} //end for\n\t//\n\treturn qtrue;\n} //end of the function PC_ExpandDefine\n//============================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//============================================================================\nint PC_ExpandDefineIntoSource(source_t *source, token_t *deftoken, define_t *define)\n{\n\ttoken_t *firsttoken, *lasttoken;\n\n\tif (!PC_ExpandDefine(source, deftoken, define, &firsttoken, &lasttoken)) return qfalse;\n\n\tif (firsttoken && lasttoken)\n\t{\n\t\tlasttoken->next = source->tokens;\n\t\tsource->tokens = firsttoken;\n\t\treturn qtrue;\n\t} //end if\n\treturn qfalse;\n} //end of the function PC_ExpandDefineIntoSource\n//============================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//============================================================================\nvoid PC_ConvertPath(char *path)\n{\n\tchar *ptr;\n\n\t//remove double path seperators\n\tfor (ptr = path; *ptr;)\n\t{\n\t\tif ((*ptr == '\\\\' || *ptr == '/') &&\n\t\t\t\t(*(ptr+1) == '\\\\' || *(ptr+1) == '/'))\n\t\t{\n\t\t\tmemmove(ptr, ptr+1, strlen(ptr));\n\t\t} //end if\n\t\telse\n\t\t{\n\t\t\tptr++;\n\t\t} //end else\n\t} //end while\n\t//set OS dependent path seperators\n\tfor (ptr = path; *ptr;)\n\t{\n\t\tif (*ptr == '/' || *ptr == '\\\\') *ptr = PATHSEPERATOR_CHAR;\n\t\tptr++;\n\t} //end while\n} //end of the function PC_ConvertPath\n//============================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//============================================================================\nint PC_Directive_include(source_t *source)\n{\n\tscript_t *script;\n\ttoken_t token;\n\tchar path[MAX_PATH];\n#ifdef QUAKE\n\tfoundfile_t file;\n#endif //QUAKE\n\n\tif (source->skip > 0) return qtrue;\n\t//\n\tif (!PC_ReadSourceToken(source, &token))\n\t{\n\t\tSourceError(source, \"#include without file name\");\n\t\treturn qfalse;\n\t} //end if\n\tif (token.linescrossed > 0)\n\t{\n\t\tSourceError(source, \"#include without file name\");\n\t\treturn qfalse;\n\t} //end if\n\tif (token.type == TT_STRING)\n\t{\n\t\tStripDoubleQuotes(token.string);\n\t\tPC_ConvertPath(token.string);\n\t\tscript = LoadScriptFile(token.string);\n\t\tif (!script)\n\t\t{\n\t\t\tstrcpy(path, source->includepath);\n\t\t\tstrcat(path, token.string);\n\t\t\tscript = LoadScriptFile(path);\n\t\t} //end if\n\t} //end if\n\telse if (token.type == TT_PUNCTUATION && *token.string == '<')\n\t{\n\t\tstrcpy(path, source->includepath);\n\t\twhile(PC_ReadSourceToken(source, &token))\n\t\t{\n\t\t\tif (token.linescrossed > 0)\n\t\t\t{\n\t\t\t\tPC_UnreadSourceToken(source, &token);\n\t\t\t\tbreak;\n\t\t\t} //end if\n\t\t\tif (token.type == TT_PUNCTUATION && *token.string == '>') break;\n\t\t\tif (strlen(path) + strlen(token.string) >= MAX_PATH)\n\t\t\t\tSourceWarning(source, \"#include truncation\");\n\t\t\telse\t\t\t\n\t\t\t\tstrcat(path, token.string);\n\t\t} //end while\n\t\tif (*token.string != '>')\n\t\t{\n\t\t\tSourceWarning(source, \"#include missing trailing >\");\n\t\t} //end if\n\t\tif (!strlen(path))\n\t\t{\n\t\t\tSourceError(source, \"#include without file name between < >\");\n\t\t\treturn qfalse;\n\t\t} //end if\n\t\tPC_ConvertPath(path);\n\t\tscript = LoadScriptFile(path);\n\t} //end if\n\telse\n\t{\n\t\tSourceError(source, \"#include without file name\");\n\t\treturn qfalse;\n\t} //end else\n#ifdef QUAKE\n\tif (!script)\n\t{\n\t\tCom_Memset(&file, 0, sizeof(foundfile_t));\n\t\tscript = LoadScriptFile(path);\n\t\tif (script) strncpy(script->filename, path, MAX_PATH);\n\t} //end if\n#endif //QUAKE\n\tif (!script)\n\t{\n#ifdef SCREWUP\n\t\tSourceWarning(source, \"file %s not found\", path);\n\t\treturn qtrue;\n#else\n\t\tSourceError(source, \"file %s not found\", path);\n\t\treturn qfalse;\n#endif //SCREWUP\n\t} //end if\n\tPC_PushScript(source, script);\n\treturn qtrue;\n} //end of the function PC_Directive_include\n//============================================================================\n// reads a token from the current line, continues reading on the next\n// line only if a backslash '\\' is encountered.\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//============================================================================\nint PC_ReadLine(source_t *source, token_t *token)\n{\n\tint crossline;\n\n\tcrossline = 0;\n\tdo\n\t{\n\t\tif (!PC_ReadSourceToken(source, token)) return qfalse;\n\t\t\n\t\tif (token->linescrossed > crossline)\n\t\t{\n\t\t\tPC_UnreadSourceToken(source, token);\n\t\t\treturn qfalse;\n\t\t} //end if\n\t\tcrossline = 1;\n\t} while(!strcmp(token->string, \"\\\\\"));\n\treturn qtrue;\n} //end of the function PC_ReadLine\n//============================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//============================================================================\nint PC_WhiteSpaceBeforeToken(token_t *token)\n{\n\treturn token->endwhitespace_p - token->whitespace_p > 0;\n} //end of the function PC_WhiteSpaceBeforeToken\n//============================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//============================================================================\nvoid PC_ClearTokenWhiteSpace(token_t *token)\n{\n\ttoken->whitespace_p = NULL;\n\ttoken->endwhitespace_p = NULL;\n\ttoken->linescrossed = 0;\n} //end of the function PC_ClearTokenWhiteSpace\n//============================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//============================================================================\nint PC_Directive_undef(source_t *source)\n{\n\ttoken_t token;\n\tdefine_t *define, *lastdefine;\n\tint hash;\n\n\tif (source->skip > 0) return qtrue;\n\t//\n\tif (!PC_ReadLine(source, &token))\n\t{\n\t\tSourceError(source, \"undef without name\");\n\t\treturn qfalse;\n\t} //end if\n\tif (token.type != TT_NAME)\n\t{\n\t\tPC_UnreadSourceToken(source, &token);\n\t\tSourceError(source, \"expected name, found %s\", token.string);\n\t\treturn qfalse;\n\t} //end if\n#if DEFINEHASHING\n\n\thash = PC_NameHash(token.string);\n\tfor (lastdefine = NULL, define = source->definehash[hash]; define; define = define->hashnext)\n\t{\n\t\tif (!strcmp(define->name, token.string))\n\t\t{\n\t\t\tif (define->flags & DEFINE_FIXED)\n\t\t\t{\n\t\t\t\tSourceWarning(source, \"can't undef %s\", token.string);\n\t\t\t} //end if\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (lastdefine) lastdefine->hashnext = define->hashnext;\n\t\t\t\telse source->definehash[hash] = define->hashnext;\n\t\t\t\tPC_FreeDefine(define);\n\t\t\t} //end else\n\t\t\tbreak;\n\t\t} //end if\n\t\tlastdefine = define;\n\t} //end for\n#else //DEFINEHASHING\n\tfor (lastdefine = NULL, define = source->defines; define; define = define->next)\n\t{\n\t\tif (!strcmp(define->name, token.string))\n\t\t{\n\t\t\tif (define->flags & DEFINE_FIXED)\n\t\t\t{\n\t\t\t\tSourceWarning(source, \"can't undef %s\", token.string);\n\t\t\t} //end if\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (lastdefine) lastdefine->next = define->next;\n\t\t\t\telse source->defines = define->next;\n\t\t\t\tPC_FreeDefine(define);\n\t\t\t} //end else\n\t\t\tbreak;\n\t\t} //end if\n\t\tlastdefine = define;\n\t} //end for\n#endif //DEFINEHASHING\n\treturn qtrue;\n} //end of the function PC_Directive_undef\n//============================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//============================================================================\nint PC_Directive_define(source_t *source)\n{\n\ttoken_t token, *t, *last;\n\tdefine_t *define;\n\n\tif (source->skip > 0) return qtrue;\n\t//\n\tif (!PC_ReadLine(source, &token))\n\t{\n\t\tSourceError(source, \"#define without name\");\n\t\treturn qfalse;\n\t} //end if\n\tif (token.type != TT_NAME)\n\t{\n\t\tPC_UnreadSourceToken(source, &token);\n\t\tSourceError(source, \"expected name after #define, found %s\", token.string);\n\t\treturn qfalse;\n\t} //end if\n\t//check if the define already exists\n#if DEFINEHASHING\n\tdefine = PC_FindHashedDefine(source->definehash, token.string);\n#else\n\tdefine = PC_FindDefine(source->defines, token.string);\n#endif //DEFINEHASHING\n\tif (define)\n\t{\n\t\tif (define->flags & DEFINE_FIXED)\n\t\t{\n\t\t\tSourceError(source, \"can't redefine %s\", token.string);\n\t\t\treturn qfalse;\n\t\t} //end if\n\t\tSourceWarning(source, \"redefinition of %s\", token.string);\n\t\t//unread the define name before executing the #undef directive\n\t\tPC_UnreadSourceToken(source, &token);\n\t\tif (!PC_Directive_undef(source)) return qfalse;\n\t\t//if the define was not removed (define->flags & DEFINE_FIXED)\n#if DEFINEHASHING\n\t\tdefine = PC_FindHashedDefine(source->definehash, token.string);\n#else\n\t\tdefine = PC_FindDefine(source->defines, token.string);\n#endif //DEFINEHASHING\n\t} //end if\n\t//allocate define\n\tdefine = (define_t *) GetMemory(sizeof(define_t));\n\tCom_Memset(define, 0, sizeof(define_t));\n\tdefine->name = (char *) GetMemory(strlen(token.string) + 1);\n\tstrcpy(define->name, token.string);\n\t//add the define to the source\n#if DEFINEHASHING\n\tPC_AddDefineToHash(define, source->definehash);\n#else //DEFINEHASHING\n\tdefine->next = source->defines;\n\tsource->defines = define;\n#endif //DEFINEHASHING\n\t//if nothing is defined, just return\n\tif (!PC_ReadLine(source, &token)) return qtrue;\n\t//if it is a define with parameters\n\tif (!PC_WhiteSpaceBeforeToken(&token) && !strcmp(token.string, \"(\"))\n\t{\n\t\t//read the define parameters\n\t\tlast = NULL;\n\t\tif (!PC_CheckTokenString(source, \")\"))\n\t\t{\n\t\t\twhile(1)\n\t\t\t{\n\t\t\t\tif (!PC_ReadLine(source, &token))\n\t\t\t\t{\n\t\t\t\t\tSourceError(source, \"expected define parameter\");\n\t\t\t\t\treturn qfalse;\n\t\t\t\t} //end if\n\t\t\t\t//if it isn't a name\n\t\t\t\tif (token.type != TT_NAME)\n\t\t\t\t{\n\t\t\t\t\tSourceError(source, \"invalid define parameter\");\n\t\t\t\t\treturn qfalse;\n\t\t\t\t} //end if\n\t\t\t\t//\n\t\t\t\tif (PC_FindDefineParm(define, token.string) >= 0)\n\t\t\t\t{\n\t\t\t\t\tSourceError(source, \"two the same define parameters\");\n\t\t\t\t\treturn qfalse;\n\t\t\t\t} //end if\n\t\t\t\t//add the define parm\n\t\t\t\tt = PC_CopyToken(&token);\n\t\t\t\tPC_ClearTokenWhiteSpace(t);\n\t\t\t\tt->next = NULL;\n\t\t\t\tif (last) last->next = t;\n\t\t\t\telse define->parms = t;\n\t\t\t\tlast = t;\n\t\t\t\tdefine->numparms++;\n\t\t\t\t//read next token\n\t\t\t\tif (!PC_ReadLine(source, &token))\n\t\t\t\t{\n\t\t\t\t\tSourceError(source, \"define parameters not terminated\");\n\t\t\t\t\treturn qfalse;\n\t\t\t\t} //end if\n\t\t\t\t//\n\t\t\t\tif (!strcmp(token.string, \")\")) break;\n\t\t\t\t//then it must be a comma\n\t\t\t\tif (strcmp(token.string, \",\"))\n\t\t\t\t{\n\t\t\t\t\tSourceError(source, \"define not terminated\");\n\t\t\t\t\treturn qfalse;\n\t\t\t\t} //end if\n\t\t\t} //end while\n\t\t} //end if\n\t\tif (!PC_ReadLine(source, &token)) return qtrue;\n\t} //end if\n\t//read the defined stuff\n\tlast = NULL;\n\tdo\n\t{\n\t\tt = PC_CopyToken(&token);\n\t\tif (t->type == TT_NAME && !strcmp(t->string, define->name))\n\t\t{\n\t\t\tSourceError(source, \"recursive define (removed recursion)\");\n\t\t\tcontinue;\n\t\t} //end if\n\t\tPC_ClearTokenWhiteSpace(t);\n\t\tt->next = NULL;\n\t\tif (last) last->next = t;\n\t\telse define->tokens = t;\n\t\tlast = t;\n\t} while(PC_ReadLine(source, &token));\n\t//\n\tif (last)\n\t{\n\t\t//check for merge operators at the beginning or end\n\t\tif (!strcmp(define->tokens->string, \"##\") ||\n\t\t\t\t!strcmp(last->string, \"##\"))\n\t\t{\n\t\t\tSourceError(source, \"define with misplaced ##\");\n\t\t\treturn qfalse;\n\t\t} //end if\n\t} //end if\n\treturn qtrue;\n} //end of the function PC_Directive_define\n//============================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//============================================================================\ndefine_t *PC_DefineFromString(char *string)\n{\n\tscript_t *script;\n\tsource_t src;\n\ttoken_t *t;\n\tint res, i;\n\tdefine_t *def;\n\n\tPC_InitTokenHeap();\n\n\tscript = LoadScriptMemory(string, strlen(string), \"*extern\");\n\t//create a new source\n\tCom_Memset(&src, 0, sizeof(source_t));\n\tstrncpy(src.filename, \"*extern\", MAX_PATH);\n\tsrc.scriptstack = script;\n#if DEFINEHASHING\n\tsrc.definehash = GetClearedMemory(DEFINEHASHSIZE * sizeof(define_t *));\n#endif //DEFINEHASHING\n\t//create a define from the source\n\tres = PC_Directive_define(&src);\n\t//free any tokens if left\n\tfor (t = src.tokens; t; t = src.tokens)\n\t{\n\t\tsrc.tokens = src.tokens->next;\n\t\tPC_FreeToken(t);\n\t} //end for\n#ifdef DEFINEHASHING\n\tdef = NULL;\n\tfor (i = 0; i < DEFINEHASHSIZE; i++)\n\t{\n\t\tif (src.definehash[i])\n\t\t{\n\t\t\tdef = src.definehash[i];\n\t\t\tbreak;\n\t\t} //end if\n\t} //end for\n#else\n\tdef = src.defines;\n#endif //DEFINEHASHING\n\t//\n#if DEFINEHASHING\n\tFreeMemory(src.definehash);\n#endif //DEFINEHASHING\n\t//\n\tFreeScript(script);\n\t//if the define was created successfully\n\tif (res > 0) return def;\n\t//free the define is created\n\tif (src.defines) PC_FreeDefine(def);\n\t//\n\treturn NULL;\n} //end of the function PC_DefineFromString\n//============================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//============================================================================\nint PC_AddDefine(source_t *source, char *string)\n{\n\tdefine_t *define;\n\n\tdefine = PC_DefineFromString(string);\n\tif (!define) return qfalse;\n#if DEFINEHASHING\n\tPC_AddDefineToHash(define, source->definehash);\n#else //DEFINEHASHING\n\tdefine->next = source->defines;\n\tsource->defines = define;\n#endif //DEFINEHASHING\n\treturn qtrue;\n} //end of the function PC_AddDefine\n//============================================================================\n// add a globals define that will be added to all opened sources\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//============================================================================\nint PC_AddGlobalDefine(char *string)\n{\n\tdefine_t *define;\n\n\tdefine = PC_DefineFromString(string);\n\tif (!define) return qfalse;\n\tdefine->next = globaldefines;\n\tglobaldefines = define;\n\treturn qtrue;\n} //end of the function PC_AddGlobalDefine\n//============================================================================\n// remove the given global define\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//============================================================================\nint PC_RemoveGlobalDefine(char *name)\n{\n\tdefine_t *define;\n\n\tdefine = PC_FindDefine(globaldefines, name);\n\tif (define)\n\t{\n\t\tPC_FreeDefine(define);\n\t\treturn qtrue;\n\t} //end if\n\treturn qfalse;\n} //end of the function PC_RemoveGlobalDefine\n//============================================================================\n// remove all globals defines\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//============================================================================\nvoid PC_RemoveAllGlobalDefines(void)\n{\n\tdefine_t *define;\n\n\tfor (define = globaldefines; define; define = globaldefines)\n\t{\n\t\tglobaldefines = globaldefines->next;\n\t\tPC_FreeDefine(define);\n\t} //end for\n} //end of the function PC_RemoveAllGlobalDefines\n//============================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//============================================================================\ndefine_t *PC_CopyDefine(source_t *source, define_t *define)\n{\n\tdefine_t *newdefine;\n\ttoken_t *token, *newtoken, *lasttoken;\n\n\tnewdefine = (define_t *) GetMemory(sizeof(define_t));\n\t//copy the define name\n\tnewdefine->name = (char *) GetMemory(strlen(define->name) + 1);\n\tstrcpy(newdefine->name, define->name);\n\tnewdefine->flags = define->flags;\n\tnewdefine->builtin = define->builtin;\n\tnewdefine->numparms = define->numparms;\n\t//the define is not linked\n\tnewdefine->next = NULL;\n\tnewdefine->hashnext = NULL;\n\t//copy the define tokens\n\tnewdefine->tokens = NULL;\n\tfor (lasttoken = NULL, token = define->tokens; token; token = token->next)\n\t{\n\t\tnewtoken = PC_CopyToken(token);\n\t\tnewtoken->next = NULL;\n\t\tif (lasttoken) lasttoken->next = newtoken;\n\t\telse newdefine->tokens = newtoken;\n\t\tlasttoken = newtoken;\n\t} //end for\n\t//copy the define parameters\n\tnewdefine->parms = NULL;\n\tfor (lasttoken = NULL, token = define->parms; token; token = token->next)\n\t{\n\t\tnewtoken = PC_CopyToken(token);\n\t\tnewtoken->next = NULL;\n\t\tif (lasttoken) lasttoken->next = newtoken;\n\t\telse newdefine->parms = newtoken;\n\t\tlasttoken = newtoken;\n\t} //end for\n\treturn newdefine;\n} //end of the function PC_CopyDefine\n//============================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//============================================================================\nvoid PC_AddGlobalDefinesToSource(source_t *source)\n{\n\tdefine_t *define, *newdefine;\n\n\tfor (define = globaldefines; define; define = define->next)\n\t{\n\t\tnewdefine = PC_CopyDefine(source, define);\n#if DEFINEHASHING\n\t\tPC_AddDefineToHash(newdefine, source->definehash);\n#else //DEFINEHASHING\n\t\tnewdefine->next = source->defines;\n\t\tsource->defines = newdefine;\n#endif //DEFINEHASHING\n\t} //end for\n} //end of the function PC_AddGlobalDefinesToSource\n//============================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//============================================================================\nint PC_Directive_if_def(source_t *source, int type)\n{\n\ttoken_t token;\n\tdefine_t *d;\n\tint skip;\n\n\tif (!PC_ReadLine(source, &token))\n\t{\n\t\tSourceError(source, \"#ifdef without name\");\n\t\treturn qfalse;\n\t} //end if\n\tif (token.type != TT_NAME)\n\t{\n\t\tPC_UnreadSourceToken(source, &token);\n\t\tSourceError(source, \"expected name after #ifdef, found %s\", token.string);\n\t\treturn qfalse;\n\t} //end if\n#if DEFINEHASHING\n\td = PC_FindHashedDefine(source->definehash, token.string);\n#else\n\td = PC_FindDefine(source->defines, token.string);\n#endif //DEFINEHASHING\n\tskip = (type == INDENT_IFDEF) == (d == NULL);\n\tPC_PushIndent(source, type, skip);\n\treturn qtrue;\n} //end of the function PC_Directiveif_def\n//============================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//============================================================================\nint PC_Directive_ifdef(source_t *source)\n{\n\treturn PC_Directive_if_def(source, INDENT_IFDEF);\n} //end of the function PC_Directive_ifdef\n//============================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//============================================================================\nint PC_Directive_ifndef(source_t *source)\n{\n\treturn PC_Directive_if_def(source, INDENT_IFNDEF);\n} //end of the function PC_Directive_ifndef\n//============================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//============================================================================\nint PC_Directive_else(source_t *source)\n{\n\tint type, skip;\n\n\tPC_PopIndent(source, &type, &skip);\n\tif (!type)\n\t{\n\t\tSourceError(source, \"misplaced #else\");\n\t\treturn qfalse;\n\t} //end if\n\tif (type == INDENT_ELSE)\n\t{\n\t\tSourceError(source, \"#else after #else\");\n\t\treturn qfalse;\n\t} //end if\n\tPC_PushIndent(source, INDENT_ELSE, !skip);\n\treturn qtrue;\n} //end of the function PC_Directive_else\n//============================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//============================================================================\nint PC_Directive_endif(source_t *source)\n{\n\tint type, skip;\n\n\tPC_PopIndent(source, &type, &skip);\n\tif (!type)\n\t{\n\t\tSourceError(source, \"misplaced #endif\");\n\t\treturn qfalse;\n\t} //end if\n\treturn qtrue;\n} //end of the function PC_Directive_endif\n//============================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//============================================================================\ntypedef struct operator_s\n{\n\tint operator;\n\tint priority;\n\tint parentheses;\n\tstruct operator_s *prev, *next;\n} operator_t;\n\ntypedef struct value_s\n{\n\tsigned long int intvalue;\n\tfloat floatvalue;\n\tint parentheses;\n\tstruct value_s *prev, *next;\n} value_t;\n\nint PC_OperatorPriority(int op)\n{\n\tswitch(op)\n\t{\n\t\tcase P_MUL: return 15;\n\t\tcase P_DIV: return 15;\n\t\tcase P_MOD: return 15;\n\t\tcase P_ADD: return 14;\n\t\tcase P_SUB: return 14;\n\n\t\tcase P_LOGIC_AND: return 7;\n\t\tcase P_LOGIC_OR: return 6;\n\t\tcase P_LOGIC_GEQ: return 12;\n\t\tcase P_LOGIC_LEQ: return 12;\n\t\tcase P_LOGIC_EQ: return 11;\n\t\tcase P_LOGIC_UNEQ: return 11;\n\n\t\tcase P_LOGIC_NOT: return 16;\n\t\tcase P_LOGIC_GREATER: return 12;\n\t\tcase P_LOGIC_LESS: return 12;\n\n\t\tcase P_RSHIFT: return 13;\n\t\tcase P_LSHIFT: return 13;\n\n\t\tcase P_BIN_AND: return 10;\n\t\tcase P_BIN_OR: return 8;\n\t\tcase P_BIN_XOR: return 9;\n\t\tcase P_BIN_NOT: return 16;\n\n\t\tcase P_COLON: return 5;\n\t\tcase P_QUESTIONMARK: return 5;\n\t} //end switch\n\treturn qfalse;\n} //end of the function PC_OperatorPriority\n\n//#define AllocValue()\t\t\tGetClearedMemory(sizeof(value_t));\n//#define FreeValue(val)\t\tFreeMemory(val)\n//#define AllocOperator(op)\t\top = (operator_t *) GetClearedMemory(sizeof(operator_t));\n//#define FreeOperator(op)\t\tFreeMemory(op);\n\n#define MAX_VALUES\t\t64\n#define MAX_OPERATORS\t64\n#define AllocValue(val)\t\t\t\t\t\t\t\t\t\\\n\tif (numvalues >= MAX_VALUES) {\t\t\t\t\t\t\\\n\t\tSourceError(source, \"out of value space\\n\");\t\t\\\n\t\terror = 1;\t\t\t\t\t\t\t\t\t\t\\\n\t\tbreak;\t\t\t\t\t\t\t\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n\telse\t\t\t\t\t\t\t\t\t\t\t\t\\\n\t\tval = &value_heap[numvalues++];\n#define FreeValue(val)\n//\n#define AllocOperator(op)\t\t\t\t\t\t\t\t\\\n\tif (numoperators >= MAX_OPERATORS) {\t\t\t\t\\\n\t\tSourceError(source, \"out of operator space\\n\");\t\\\n\t\terror = 1;\t\t\t\t\t\t\t\t\t\t\\\n\t\tbreak;\t\t\t\t\t\t\t\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n\telse\t\t\t\t\t\t\t\t\t\t\t\t\\\n\t\top = &operator_heap[numoperators++];\n#define FreeOperator(op)\n\nint PC_EvaluateTokens(source_t *source, token_t *tokens, signed long int *intvalue,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tfloat *floatvalue, int integer)\n{\n\toperator_t *o, *firstoperator, *lastoperator;\n\tvalue_t *v, *firstvalue, *lastvalue, *v1, *v2;\n\ttoken_t *t;\n\tint brace = 0;\n\tint parentheses = 0;\n\tint error = 0;\n\tint lastwasvalue = 0;\n\tint negativevalue = 0;\n\tint questmarkintvalue = 0;\n\tfloat questmarkfloatvalue = 0;\n\tint gotquestmarkvalue = qfalse;\n\t//int lastoperatortype = 0;\n\t//\n\toperator_t operator_heap[MAX_OPERATORS];\n\tint numoperators = 0;\n\tvalue_t value_heap[MAX_VALUES];\n\tint numvalues = 0;\n\n\tfirstoperator = lastoperator = NULL;\n\tfirstvalue = lastvalue = NULL;\n\tif (intvalue) *intvalue = 0;\n\tif (floatvalue) *floatvalue = 0;\n\tfor (t = tokens; t; t = t->next)\n\t{\n\t\tswitch(t->type)\n\t\t{\n\t\t\tcase TT_NAME:\n\t\t\t{\n\t\t\t\tif (lastwasvalue || negativevalue)\n\t\t\t\t{\n\t\t\t\t\tSourceError(source, \"syntax error in #if/#elif\");\n\t\t\t\t\terror = 1;\n\t\t\t\t\tbreak;\n\t\t\t\t} //end if\n\t\t\t\tif (strcmp(t->string, \"defined\"))\n\t\t\t\t{\n\t\t\t\t\tSourceError(source, \"undefined name %s in #if/#elif\", t->string);\n\t\t\t\t\terror = 1;\n\t\t\t\t\tbreak;\n\t\t\t\t} //end if\n\t\t\t\tt = t->next;\n\t\t\t\tif (!strcmp(t->string, \"(\"))\n\t\t\t\t{\n\t\t\t\t\tbrace = qtrue;\n\t\t\t\t\tt = t->next;\n\t\t\t\t} //end if\n\t\t\t\tif (!t || t->type != TT_NAME)\n\t\t\t\t{\n\t\t\t\t\tSourceError(source, \"defined without name in #if/#elif\");\n\t\t\t\t\terror = 1;\n\t\t\t\t\tbreak;\n\t\t\t\t} //end if\n\t\t\t\t//v = (value_t *) GetClearedMemory(sizeof(value_t));\n\t\t\t\tAllocValue(v);\n#if DEFINEHASHING\n\t\t\t\tif (PC_FindHashedDefine(source->definehash, t->string))\n#else\t\t\t\n\t\t\t\tif (PC_FindDefine(source->defines, t->string))\n#endif //DEFINEHASHING\n\t\t\t\t{\n\t\t\t\t\tv->intvalue = 1;\n\t\t\t\t\tv->floatvalue = 1;\n\t\t\t\t} //end if\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tv->intvalue = 0;\n\t\t\t\t\tv->floatvalue = 0;\n\t\t\t\t} //end else\n\t\t\t\tv->parentheses = parentheses;\n\t\t\t\tv->next = NULL;\n\t\t\t\tv->prev = lastvalue;\n\t\t\t\tif (lastvalue) lastvalue->next = v;\n\t\t\t\telse firstvalue = v;\n\t\t\t\tlastvalue = v;\n\t\t\t\tif (brace)\n\t\t\t\t{\n\t\t\t\t\tt = t->next;\n\t\t\t\t\tif (!t || strcmp(t->string, \")\"))\n\t\t\t\t\t{\n\t\t\t\t\t\tSourceError(source, \"defined without ) in #if/#elif\");\n\t\t\t\t\t\terror = 1;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t} //end if\n\t\t\t\t} //end if\n\t\t\t\tbrace = qfalse;\n\t\t\t\t// defined() creates a value\n\t\t\t\tlastwasvalue = 1;\n\t\t\t\tbreak;\n\t\t\t} //end case\n\t\t\tcase TT_NUMBER:\n\t\t\t{\n\t\t\t\tif (lastwasvalue)\n\t\t\t\t{\n\t\t\t\t\tSourceError(source, \"syntax error in #if/#elif\");\n\t\t\t\t\terror = 1;\n\t\t\t\t\tbreak;\n\t\t\t\t} //end if\n\t\t\t\t//v = (value_t *) GetClearedMemory(sizeof(value_t));\n\t\t\t\tAllocValue(v);\n\t\t\t\tif (negativevalue)\n\t\t\t\t{\n\t\t\t\t\tv->intvalue = - (signed int) t->intvalue;\n\t\t\t\t\tv->floatvalue = - t->floatvalue;\n\t\t\t\t} //end if\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tv->intvalue = t->intvalue;\n\t\t\t\t\tv->floatvalue = t->floatvalue;\n\t\t\t\t} //end else\n\t\t\t\tv->parentheses = parentheses;\n\t\t\t\tv->next = NULL;\n\t\t\t\tv->prev = lastvalue;\n\t\t\t\tif (lastvalue) lastvalue->next = v;\n\t\t\t\telse firstvalue = v;\n\t\t\t\tlastvalue = v;\n\t\t\t\t//last token was a value\n\t\t\t\tlastwasvalue = 1;\n\t\t\t\t//\n\t\t\t\tnegativevalue = 0;\n\t\t\t\tbreak;\n\t\t\t} //end case\n\t\t\tcase TT_PUNCTUATION:\n\t\t\t{\n\t\t\t\tif (negativevalue)\n\t\t\t\t{\n\t\t\t\t\tSourceError(source, \"misplaced minus sign in #if/#elif\");\n\t\t\t\t\terror = 1;\n\t\t\t\t\tbreak;\n\t\t\t\t} //end if\n\t\t\t\tif (t->subtype == P_PARENTHESESOPEN)\n\t\t\t\t{\n\t\t\t\t\tparentheses++;\n\t\t\t\t\tbreak;\n\t\t\t\t} //end if\n\t\t\t\telse if (t->subtype == P_PARENTHESESCLOSE)\n\t\t\t\t{\n\t\t\t\t\tparentheses--;\n\t\t\t\t\tif (parentheses < 0)\n\t\t\t\t\t{\n\t\t\t\t\t\tSourceError(source, \"too many ) in #if/#elsif\");\n\t\t\t\t\t\terror = 1;\n\t\t\t\t\t} //end if\n\t\t\t\t\tbreak;\n\t\t\t\t} //end else if\n\t\t\t\t//check for invalid operators on floating point values\n\t\t\t\tif (!integer)\n\t\t\t\t{\n\t\t\t\t\tif (t->subtype == P_BIN_NOT || t->subtype == P_MOD ||\n\t\t\t\t\t\tt->subtype == P_RSHIFT || t->subtype == P_LSHIFT ||\n\t\t\t\t\t\tt->subtype == P_BIN_AND || t->subtype == P_BIN_OR ||\n\t\t\t\t\t\tt->subtype == P_BIN_XOR)\n\t\t\t\t\t{\n\t\t\t\t\t\tSourceError(source, \"illigal operator %s on floating point operands\\n\", t->string);\n\t\t\t\t\t\terror = 1;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t} //end if\n\t\t\t\t} //end if\n\t\t\t\tswitch(t->subtype)\n\t\t\t\t{\n\t\t\t\t\tcase P_LOGIC_NOT:\n\t\t\t\t\tcase P_BIN_NOT:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (lastwasvalue)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tSourceError(source, \"! or ~ after value in #if/#elif\");\n\t\t\t\t\t\t\terror = 1;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t} //end if\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t} //end case\n\t\t\t\t\tcase P_INC:\n\t\t\t\t\tcase P_DEC:\n\t\t\t\t\t{\n\t\t\t\t\t\tSourceError(source, \"++ or -- used in #if/#elif\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t} //end case\n\t\t\t\t\tcase P_SUB:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (!lastwasvalue)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tnegativevalue = 1;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t} //end if\n\t\t\t\t\t} //end case\n\t\t\t\t\t\n\t\t\t\t\tcase P_MUL:\n\t\t\t\t\tcase P_DIV:\n\t\t\t\t\tcase P_MOD:\n\t\t\t\t\tcase P_ADD:\n\n\t\t\t\t\tcase P_LOGIC_AND:\n\t\t\t\t\tcase P_LOGIC_OR:\n\t\t\t\t\tcase P_LOGIC_GEQ:\n\t\t\t\t\tcase P_LOGIC_LEQ:\n\t\t\t\t\tcase P_LOGIC_EQ:\n\t\t\t\t\tcase P_LOGIC_UNEQ:\n\n\t\t\t\t\tcase P_LOGIC_GREATER:\n\t\t\t\t\tcase P_LOGIC_LESS:\n\n\t\t\t\t\tcase P_RSHIFT:\n\t\t\t\t\tcase P_LSHIFT:\n\n\t\t\t\t\tcase P_BIN_AND:\n\t\t\t\t\tcase P_BIN_OR:\n\t\t\t\t\tcase P_BIN_XOR:\n\n\t\t\t\t\tcase P_COLON:\n\t\t\t\t\tcase P_QUESTIONMARK:\n\t\t\t\t\t{\n\t\t\t\t\t\tif (!lastwasvalue)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tSourceError(source, \"operator %s after operator in #if/#elif\", t->string);\n\t\t\t\t\t\t\terror = 1;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t} //end if\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t} //end case\n\t\t\t\t\tdefault:\n\t\t\t\t\t{\n\t\t\t\t\t\tSourceError(source, \"invalid operator %s in #if/#elif\", t->string);\n\t\t\t\t\t\terror = 1;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t} //end default\n\t\t\t\t} //end switch\n\t\t\t\tif (!error && !negativevalue)\n\t\t\t\t{\n\t\t\t\t\t//o = (operator_t *) GetClearedMemory(sizeof(operator_t));\n\t\t\t\t\tAllocOperator(o);\n\t\t\t\t\to->operator = t->subtype;\n\t\t\t\t\to->priority = PC_OperatorPriority(t->subtype);\n\t\t\t\t\to->parentheses = parentheses;\n\t\t\t\t\to->next = NULL;\n\t\t\t\t\to->prev = lastoperator;\n\t\t\t\t\tif (lastoperator) lastoperator->next = o;\n\t\t\t\t\telse firstoperator = o;\n\t\t\t\t\tlastoperator = o;\n\t\t\t\t\tlastwasvalue = 0;\n\t\t\t\t} //end if\n\t\t\t\tbreak;\n\t\t\t} //end case\n\t\t\tdefault:\n\t\t\t{\n\t\t\t\tSourceError(source, \"unknown %s in #if/#elif\", t->string);\n\t\t\t\terror = 1;\n\t\t\t\tbreak;\n\t\t\t} //end default\n\t\t} //end switch\n\t\tif (error) break;\n\t} //end for\n\tif (!error)\n\t{\n\t\tif (!lastwasvalue)\n\t\t{\n\t\t\tSourceError(source, \"trailing operator in #if/#elif\");\n\t\t\terror = 1;\n\t\t} //end if\n\t\telse if (parentheses)\n\t\t{\n\t\t\tSourceError(source, \"too many ( in #if/#elif\");\n\t\t\terror = 1;\n\t\t} //end else if\n\t} //end if\n\t//\n\tgotquestmarkvalue = qfalse;\n\tquestmarkintvalue = 0;\n\tquestmarkfloatvalue = 0;\n\t//while there are operators\n\twhile(!error && firstoperator)\n\t{\n\t\tv = firstvalue;\n\t\tfor (o = firstoperator; o->next; o = o->next)\n\t\t{\n\t\t\t//if the current operator is nested deeper in parentheses\n\t\t\t//than the next operator\n\t\t\tif (o->parentheses > o->next->parentheses) break;\n\t\t\t//if the current and next operator are nested equally deep in parentheses\n\t\t\tif (o->parentheses == o->next->parentheses)\n\t\t\t{\n\t\t\t\t//if the priority of the current operator is equal or higher\n\t\t\t\t//than the priority of the next operator\n\t\t\t\tif (o->priority >= o->next->priority) break;\n\t\t\t} //end if\n\t\t\t//if the arity of the operator isn't equal to 1\n\t\t\tif (o->operator != P_LOGIC_NOT\n\t\t\t\t\t&& o->operator != P_BIN_NOT) v = v->next;\n\t\t\t//if there's no value or no next value\n\t\t\tif (!v)\n\t\t\t{\n\t\t\t\tSourceError(source, \"mising values in #if/#elif\");\n\t\t\t\terror = 1;\n\t\t\t\tbreak;\n\t\t\t} //end if\n\t\t} //end for\n\t\tif (error) break;\n\t\tv1 = v;\n\t\tv2 = v->next;\n#ifdef DEBUG_EVAL\n\t\tif (integer)\n\t\t{\n\t\t\tLog_Write(\"operator %s, value1 = %d\", PunctuationFromNum(source->scriptstack, o->operator), v1->intvalue);\n\t\t\tif (v2) Log_Write(\"value2 = %d\", v2->intvalue);\n\t\t} //end if\n\t\telse\n\t\t{\n\t\t\tLog_Write(\"operator %s, value1 = %f\", PunctuationFromNum(source->scriptstack, o->operator), v1->floatvalue);\n\t\t\tif (v2) Log_Write(\"value2 = %f\", v2->floatvalue);\n\t\t} //end else\n#endif //DEBUG_EVAL\n\t\tswitch(o->operator)\n\t\t{\n\t\t\tcase P_LOGIC_NOT:\t\tv1->intvalue = !v1->intvalue;\n\t\t\t\t\t\t\t\t\tv1->floatvalue = !v1->floatvalue; break;\n\t\t\tcase P_BIN_NOT:\t\t\tv1->intvalue = ~v1->intvalue;\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\tcase P_MUL:\t\t\t\tv1->intvalue *= v2->intvalue;\n\t\t\t\t\t\t\t\t\tv1->floatvalue *= v2->floatvalue; break;\n\t\t\tcase P_DIV:\t\t\t\tif (!v2->intvalue || !v2->floatvalue)\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tSourceError(source, \"divide by zero in #if/#elif\\n\");\n\t\t\t\t\t\t\t\t\t\terror = 1;\n\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tv1->intvalue /= v2->intvalue;\n\t\t\t\t\t\t\t\t\tv1->floatvalue /= v2->floatvalue; break;\n\t\t\tcase P_MOD:\t\t\t\tif (!v2->intvalue)\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tSourceError(source, \"divide by zero in #if/#elif\\n\");\n\t\t\t\t\t\t\t\t\t\terror = 1;\n\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tv1->intvalue %= v2->intvalue; break;\n\t\t\tcase P_ADD:\t\t\t\tv1->intvalue += v2->intvalue;\n\t\t\t\t\t\t\t\t\tv1->floatvalue += v2->floatvalue; break;\n\t\t\tcase P_SUB:\t\t\t\tv1->intvalue -= v2->intvalue;\n\t\t\t\t\t\t\t\t\tv1->floatvalue -= v2->floatvalue; break;\n\t\t\tcase P_LOGIC_AND:\t\tv1->intvalue = v1->intvalue && v2->intvalue;\n\t\t\t\t\t\t\t\t\tv1->floatvalue = v1->floatvalue && v2->floatvalue; break;\n\t\t\tcase P_LOGIC_OR:\t\tv1->intvalue = v1->intvalue || v2->intvalue;\n\t\t\t\t\t\t\t\t\tv1->floatvalue = v1->floatvalue || v2->floatvalue; break;\n\t\t\tcase P_LOGIC_GEQ:\t\tv1->intvalue = v1->intvalue >= v2->intvalue;\n\t\t\t\t\t\t\t\t\tv1->floatvalue = v1->floatvalue >= v2->floatvalue; break;\n\t\t\tcase P_LOGIC_LEQ:\t\tv1->intvalue = v1->intvalue <= v2->intvalue;\n\t\t\t\t\t\t\t\t\tv1->floatvalue = v1->floatvalue <= v2->floatvalue; break;\n\t\t\tcase P_LOGIC_EQ:\t\tv1->intvalue = v1->intvalue == v2->intvalue;\n\t\t\t\t\t\t\t\t\tv1->floatvalue = v1->floatvalue == v2->floatvalue; break;\n\t\t\tcase P_LOGIC_UNEQ:\t\tv1->intvalue = v1->intvalue != v2->intvalue;\n\t\t\t\t\t\t\t\t\tv1->floatvalue = v1->floatvalue != v2->floatvalue; break;\n\t\t\tcase P_LOGIC_GREATER:\tv1->intvalue = v1->intvalue > v2->intvalue;\n\t\t\t\t\t\t\t\t\tv1->floatvalue = v1->floatvalue > v2->floatvalue; break;\n\t\t\tcase P_LOGIC_LESS:\t\tv1->intvalue = v1->intvalue < v2->intvalue;\n\t\t\t\t\t\t\t\t\tv1->floatvalue = v1->floatvalue < v2->floatvalue; break;\n\t\t\tcase P_RSHIFT:\t\t\tv1->intvalue >>= v2->intvalue;\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\tcase P_LSHIFT:\t\t\tv1->intvalue <<= v2->intvalue;\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\tcase P_BIN_AND:\t\t\tv1->intvalue &= v2->intvalue;\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\tcase P_BIN_OR:\t\t\tv1->intvalue |= v2->intvalue;\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\tcase P_BIN_XOR:\t\t\tv1->intvalue ^= v2->intvalue;\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\tcase P_COLON:\n\t\t\t{\n\t\t\t\tif (!gotquestmarkvalue)\n\t\t\t\t{\n\t\t\t\t\tSourceError(source, \": without ? in #if/#elif\");\n\t\t\t\t\terror = 1;\n\t\t\t\t\tbreak;\n\t\t\t\t} //end if\n\t\t\t\tif (integer)\n\t\t\t\t{\n\t\t\t\t\tif (!questmarkintvalue) v1->intvalue = v2->intvalue;\n\t\t\t\t} //end if\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tif (!questmarkfloatvalue) v1->floatvalue = v2->floatvalue;\n\t\t\t\t} //end else\n\t\t\t\tgotquestmarkvalue = qfalse;\n\t\t\t\tbreak;\n\t\t\t} //end case\n\t\t\tcase P_QUESTIONMARK:\n\t\t\t{\n\t\t\t\tif (gotquestmarkvalue)\n\t\t\t\t{\n\t\t\t\t\tSourceError(source, \"? after ? in #if/#elif\");\n\t\t\t\t\terror = 1;\n\t\t\t\t\tbreak;\n\t\t\t\t} //end if\n\t\t\t\tquestmarkintvalue = v1->intvalue;\n\t\t\t\tquestmarkfloatvalue = v1->floatvalue;\n\t\t\t\tgotquestmarkvalue = qtrue;\n\t\t\t\tbreak;\n\t\t\t} //end if\n\t\t} //end switch\n#ifdef DEBUG_EVAL\n\t\tif (integer) Log_Write(\"result value = %d\", v1->intvalue);\n\t\telse Log_Write(\"result value = %f\", v1->floatvalue);\n#endif //DEBUG_EVAL\n\t\tif (error) break;\n\t\t//lastoperatortype = o->operator;\n\t\t//if not an operator with arity 1\n\t\tif (o->operator != P_LOGIC_NOT\n\t\t\t\t&& o->operator != P_BIN_NOT)\n\t\t{\n\t\t\t//remove the second value if not question mark operator\n\t\t\tif (o->operator != P_QUESTIONMARK) v = v->next;\n\t\t\t//\n\t\t\tif (v->prev) v->prev->next = v->next;\n\t\t\telse firstvalue = v->next;\n\t\t\tif (v->next) v->next->prev = v->prev;\n\t\t\telse lastvalue = v->prev;\n\t\t\t//FreeMemory(v);\n\t\t\tFreeValue(v);\n\t\t} //end if\n\t\t//remove the operator\n\t\tif (o->prev) o->prev->next = o->next;\n\t\telse firstoperator = o->next;\n\t\tif (o->next) o->next->prev = o->prev;\n\t\telse lastoperator = o->prev;\n\t\t//FreeMemory(o);\n\t\tFreeOperator(o);\n\t} //end while\n\tif (firstvalue)\n\t{\n\t\tif (intvalue) *intvalue = firstvalue->intvalue;\n\t\tif (floatvalue) *floatvalue = firstvalue->floatvalue;\n\t} //end if\n\tfor (o = firstoperator; o; o = lastoperator)\n\t{\n\t\tlastoperator = o->next;\n\t\t//FreeMemory(o);\n\t\tFreeOperator(o);\n\t} //end for\n\tfor (v = firstvalue; v; v = lastvalue)\n\t{\n\t\tlastvalue = v->next;\n\t\t//FreeMemory(v);\n\t\tFreeValue(v);\n\t} //end for\n\tif (!error) return qtrue;\n\tif (intvalue) *intvalue = 0;\n\tif (floatvalue) *floatvalue = 0;\n\treturn qfalse;\n} //end of the function PC_EvaluateTokens\n//============================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//============================================================================\nint PC_Evaluate(source_t *source, signed long int *intvalue,\n\t\t\t\t\t\t\t\t\t\t\t\tfloat *floatvalue, int integer)\n{\n\ttoken_t token, *firsttoken, *lasttoken;\n\ttoken_t *t, *nexttoken;\n\tdefine_t *define;\n\tint defined = qfalse;\n\n\tif (intvalue) *intvalue = 0;\n\tif (floatvalue) *floatvalue = 0;\n\t//\n\tif (!PC_ReadLine(source, &token))\n\t{\n\t\tSourceError(source, \"no value after #if/#elif\");\n\t\treturn qfalse;\n\t} //end if\n\tfirsttoken = NULL;\n\tlasttoken = NULL;\n\tdo\n\t{\n\t\t//if the token is a name\n\t\tif (token.type == TT_NAME)\n\t\t{\n\t\t\tif (defined)\n\t\t\t{\n\t\t\t\tdefined = qfalse;\n\t\t\t\tt = PC_CopyToken(&token);\n\t\t\t\tt->next = NULL;\n\t\t\t\tif (lasttoken) lasttoken->next = t;\n\t\t\t\telse firsttoken = t;\n\t\t\t\tlasttoken = t;\n\t\t\t} //end if\n\t\t\telse if (!strcmp(token.string, \"defined\"))\n\t\t\t{\n\t\t\t\tdefined = qtrue;\n\t\t\t\tt = PC_CopyToken(&token);\n\t\t\t\tt->next = NULL;\n\t\t\t\tif (lasttoken) lasttoken->next = t;\n\t\t\t\telse firsttoken = t;\n\t\t\t\tlasttoken = t;\n\t\t\t} //end if\n\t\t\telse\n\t\t\t{\n\t\t\t\t//then it must be a define\n#if DEFINEHASHING\n\t\t\t\tdefine = PC_FindHashedDefine(source->definehash, token.string);\n#else\n\t\t\t\tdefine = PC_FindDefine(source->defines, token.string);\n#endif //DEFINEHASHING\n\t\t\t\tif (!define)\n\t\t\t\t{\n\t\t\t\t\tSourceError(source, \"can't evaluate %s, not defined\", token.string);\n\t\t\t\t\treturn qfalse;\n\t\t\t\t} //end if\n\t\t\t\tif (!PC_ExpandDefineIntoSource(source, &token, define)) return qfalse;\n\t\t\t} //end else\n\t\t} //end if\n\t\t//if the token is a number or a punctuation\n\t\telse if (token.type == TT_NUMBER || token.type == TT_PUNCTUATION)\n\t\t{\n\t\t\tt = PC_CopyToken(&token);\n\t\t\tt->next = NULL;\n\t\t\tif (lasttoken) lasttoken->next = t;\n\t\t\telse firsttoken = t;\n\t\t\tlasttoken = t;\n\t\t} //end else\n\t\telse //can't evaluate the token\n\t\t{\n\t\t\tSourceError(source, \"can't evaluate %s\", token.string);\n\t\t\treturn qfalse;\n\t\t} //end else\n\t} while(PC_ReadLine(source, &token));\n\t//\n\tif (!PC_EvaluateTokens(source, firsttoken, intvalue, floatvalue, integer)) return qfalse;\n\t//\n#ifdef DEBUG_EVAL\n\tLog_Write(\"eval:\");\n#endif //DEBUG_EVAL\n\tfor (t = firsttoken; t; t = nexttoken)\n\t{\n#ifdef DEBUG_EVAL\n\t\tLog_Write(\" %s\", t->string);\n#endif //DEBUG_EVAL\n\t\tnexttoken = t->next;\n\t\tPC_FreeToken(t);\n\t} //end for\n#ifdef DEBUG_EVAL\n\tif (integer) Log_Write(\"eval result: %d\", *intvalue);\n\telse Log_Write(\"eval result: %f\", *floatvalue);\n#endif //DEBUG_EVAL\n\t//\n\treturn qtrue;\n} //end of the function PC_Evaluate\n//============================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//============================================================================\nint PC_DollarEvaluate(source_t *source, signed long int *intvalue,\n\t\t\t\t\t\t\t\t\t\t\t\tfloat *floatvalue, int integer)\n{\n\tint indent, defined = qfalse;\n\ttoken_t token, *firsttoken, *lasttoken;\n\ttoken_t *t, *nexttoken;\n\tdefine_t *define;\n\n\tif (intvalue) *intvalue = 0;\n\tif (floatvalue) *floatvalue = 0;\n\t//\n\tif (!PC_ReadSourceToken(source, &token))\n\t{\n\t\tSourceError(source, \"no leading ( after $evalint/$evalfloat\");\n\t\treturn qfalse;\n\t} //end if\n\tif (!PC_ReadSourceToken(source, &token))\n\t{\n\t\tSourceError(source, \"nothing to evaluate\");\n\t\treturn qfalse;\n\t} //end if\n\tindent = 1;\n\tfirsttoken = NULL;\n\tlasttoken = NULL;\n\tdo\n\t{\n\t\t//if the token is a name\n\t\tif (token.type == TT_NAME)\n\t\t{\n\t\t\tif (defined)\n\t\t\t{\n\t\t\t\tdefined = qfalse;\n\t\t\t\tt = PC_CopyToken(&token);\n\t\t\t\tt->next = NULL;\n\t\t\t\tif (lasttoken) lasttoken->next = t;\n\t\t\t\telse firsttoken = t;\n\t\t\t\tlasttoken = t;\n\t\t\t} //end if\n\t\t\telse if (!strcmp(token.string, \"defined\"))\n\t\t\t{\n\t\t\t\tdefined = qtrue;\n\t\t\t\tt = PC_CopyToken(&token);\n\t\t\t\tt->next = NULL;\n\t\t\t\tif (lasttoken) lasttoken->next = t;\n\t\t\t\telse firsttoken = t;\n\t\t\t\tlasttoken = t;\n\t\t\t} //end if\n\t\t\telse\n\t\t\t{\n\t\t\t\t//then it must be a define\n#if DEFINEHASHING\n\t\t\t\tdefine = PC_FindHashedDefine(source->definehash, token.string);\n#else\n\t\t\t\tdefine = PC_FindDefine(source->defines, token.string);\n#endif //DEFINEHASHING\n\t\t\t\tif (!define)\n\t\t\t\t{\n\t\t\t\t\tSourceError(source, \"can't evaluate %s, not defined\", token.string);\n\t\t\t\t\treturn qfalse;\n\t\t\t\t} //end if\n\t\t\t\tif (!PC_ExpandDefineIntoSource(source, &token, define)) return qfalse;\n\t\t\t} //end else\n\t\t} //end if\n\t\t//if the token is a number or a punctuation\n\t\telse if (token.type == TT_NUMBER || token.type == TT_PUNCTUATION)\n\t\t{\n\t\t\tif (*token.string == '(') indent++;\n\t\t\telse if (*token.string == ')') indent--;\n\t\t\tif (indent <= 0) break;\n\t\t\tt = PC_CopyToken(&token);\n\t\t\tt->next = NULL;\n\t\t\tif (lasttoken) lasttoken->next = t;\n\t\t\telse firsttoken = t;\n\t\t\tlasttoken = t;\n\t\t} //end else\n\t\telse //can't evaluate the token\n\t\t{\n\t\t\tSourceError(source, \"can't evaluate %s\", token.string);\n\t\t\treturn qfalse;\n\t\t} //end else\n\t} while(PC_ReadSourceToken(source, &token));\n\t//\n\tif (!PC_EvaluateTokens(source, firsttoken, intvalue, floatvalue, integer)) return qfalse;\n\t//\n#ifdef DEBUG_EVAL\n\tLog_Write(\"$eval:\");\n#endif //DEBUG_EVAL\n\tfor (t = firsttoken; t; t = nexttoken)\n\t{\n#ifdef DEBUG_EVAL\n\t\tLog_Write(\" %s\", t->string);\n#endif //DEBUG_EVAL\n\t\tnexttoken = t->next;\n\t\tPC_FreeToken(t);\n\t} //end for\n#ifdef DEBUG_EVAL\n\tif (integer) Log_Write(\"$eval result: %d\", *intvalue);\n\telse Log_Write(\"$eval result: %f\", *floatvalue);\n#endif //DEBUG_EVAL\n\t//\n\treturn qtrue;\n} //end of the function PC_DollarEvaluate\n//============================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//============================================================================\nint PC_Directive_elif(source_t *source)\n{\n\tsigned long int value;\n\tint type, skip;\n\n\tPC_PopIndent(source, &type, &skip);\n\tif (!type || type == INDENT_ELSE)\n\t{\n\t\tSourceError(source, \"misplaced #elif\");\n\t\treturn qfalse;\n\t} //end if\n\tif (!PC_Evaluate(source, &value, NULL, qtrue)) return qfalse;\n\tskip = (value == 0);\n\tPC_PushIndent(source, INDENT_ELIF, skip);\n\treturn qtrue;\n} //end of the function PC_Directive_elif\n//============================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//============================================================================\nint PC_Directive_if(source_t *source)\n{\n\tsigned long int value;\n\tint skip;\n\n\tif (!PC_Evaluate(source, &value, NULL, qtrue)) return qfalse;\n\tskip = (value == 0);\n\tPC_PushIndent(source, INDENT_IF, skip);\n\treturn qtrue;\n} //end of the function PC_Directive\n//============================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//============================================================================\nint PC_Directive_line(source_t *source)\n{\n\tSourceError(source, \"#line directive not supported\");\n\treturn qfalse;\n} //end of the function PC_Directive_line\n//============================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//============================================================================\nint PC_Directive_error(source_t *source)\n{\n\ttoken_t token;\n\n\tstrcpy(token.string, \"\");\n\tPC_ReadSourceToken(source, &token);\n\tSourceError(source, \"#error directive: %s\", token.string);\n\treturn qfalse;\n} //end of the function PC_Directive_error\n//============================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//============================================================================\nint PC_Directive_pragma(source_t *source)\n{\n\ttoken_t token;\n\n\tSourceWarning(source, \"#pragma directive not supported\");\n\twhile(PC_ReadLine(source, &token)) ;\n\treturn qtrue;\n} //end of the function PC_Directive_pragma\n//============================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//============================================================================\nvoid UnreadSignToken(source_t *source)\n{\n\ttoken_t token;\n\n\ttoken.line = source->scriptstack->line;\n\ttoken.whitespace_p = source->scriptstack->script_p;\n\ttoken.endwhitespace_p = source->scriptstack->script_p;\n\ttoken.linescrossed = 0;\n\tstrcpy(token.string, \"-\");\n\ttoken.type = TT_PUNCTUATION;\n\ttoken.subtype = P_SUB;\n\tPC_UnreadSourceToken(source, &token);\n} //end of the function UnreadSignToken\n//============================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//============================================================================\nint PC_Directive_eval(source_t *source)\n{\n\tsigned long int value;\n\ttoken_t token;\n\n\tif (!PC_Evaluate(source, &value, NULL, qtrue)) return qfalse;\n\t//\n\ttoken.line = source->scriptstack->line;\n\ttoken.whitespace_p = source->scriptstack->script_p;\n\ttoken.endwhitespace_p = source->scriptstack->script_p;\n\ttoken.linescrossed = 0;\n\tsprintf(token.string, \"%d\", abs((int)value));\n\ttoken.type = TT_NUMBER;\n\ttoken.subtype = TT_INTEGER|TT_LONG|TT_DECIMAL;\n\tPC_UnreadSourceToken(source, &token);\n\tif (value < 0) UnreadSignToken(source);\n\treturn qtrue;\n} //end of the function PC_Directive_eval\n//============================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//============================================================================\nint PC_Directive_evalfloat(source_t *source)\n{\n\tfloat value;\n\ttoken_t token;\n\n\tif (!PC_Evaluate(source, NULL, &value, qfalse)) return qfalse;\n\ttoken.line = source->scriptstack->line;\n\ttoken.whitespace_p = source->scriptstack->script_p;\n\ttoken.endwhitespace_p = source->scriptstack->script_p;\n\ttoken.linescrossed = 0;\n\tsprintf(token.string, \"%1.2f\", fabs(value));\n\ttoken.type = TT_NUMBER;\n\ttoken.subtype = TT_FLOAT|TT_LONG|TT_DECIMAL;\n\tPC_UnreadSourceToken(source, &token);\n\tif (value < 0) UnreadSignToken(source);\n\treturn qtrue;\n} //end of the function PC_Directive_evalfloat\n//============================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//============================================================================\ndirective_t directives[20] =\n{\n\t{\"if\", PC_Directive_if},\n\t{\"ifdef\", PC_Directive_ifdef},\n\t{\"ifndef\", PC_Directive_ifndef},\n\t{\"elif\", PC_Directive_elif},\n\t{\"else\", PC_Directive_else},\n\t{\"endif\", PC_Directive_endif},\n\t{\"include\", PC_Directive_include},\n\t{\"define\", PC_Directive_define},\n\t{\"undef\", PC_Directive_undef},\n\t{\"line\", PC_Directive_line},\n\t{\"error\", PC_Directive_error},\n\t{\"pragma\", PC_Directive_pragma},\n\t{\"eval\", PC_Directive_eval},\n\t{\"evalfloat\", PC_Directive_evalfloat},\n\t{NULL, NULL}\n};\n\nint PC_ReadDirective(source_t *source)\n{\n\ttoken_t token;\n\tint i;\n\n\t//read the directive name\n\tif (!PC_ReadSourceToken(source, &token))\n\t{\n\t\tSourceError(source, \"found # without name\");\n\t\treturn qfalse;\n\t} //end if\n\t//directive name must be on the same line\n\tif (token.linescrossed > 0)\n\t{\n\t\tPC_UnreadSourceToken(source, &token);\n\t\tSourceError(source, \"found # at end of line\");\n\t\treturn qfalse;\n\t} //end if\n\t//if if is a name\n\tif (token.type == TT_NAME)\n\t{\n\t\t//find the precompiler directive\n\t\tfor (i = 0; directives[i].name; i++)\n\t\t{\n\t\t\tif (!strcmp(directives[i].name, token.string))\n\t\t\t{\n\t\t\t\treturn directives[i].func(source);\n\t\t\t} //end if\n\t\t} //end for\n\t} //end if\n\tSourceError(source, \"unknown precompiler directive %s\", token.string);\n\treturn qfalse;\n} //end of the function PC_ReadDirective\n//============================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//============================================================================\nint PC_DollarDirective_evalint(source_t *source)\n{\n\tsigned long int value;\n\ttoken_t token;\n\n\tif (!PC_DollarEvaluate(source, &value, NULL, qtrue)) return qfalse;\n\t//\n\ttoken.line = source->scriptstack->line;\n\ttoken.whitespace_p = source->scriptstack->script_p;\n\ttoken.endwhitespace_p = source->scriptstack->script_p;\n\ttoken.linescrossed = 0;\n\tsprintf(token.string, \"%d\", abs((int)value));\n\ttoken.type = TT_NUMBER;\n\ttoken.subtype = TT_INTEGER|TT_LONG|TT_DECIMAL;\n\n#ifdef NUMBERVALUE\n\ttoken.intvalue = labs(value);\n\ttoken.floatvalue = token.intvalue;\n#endif //NUMBERVALUE\n\n\tPC_UnreadSourceToken(source, &token);\n\tif (value < 0)\n\t\tUnreadSignToken(source);\n\n\treturn qtrue;\n} //end of the function PC_DollarDirective_evalint\n//============================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//============================================================================\nint PC_DollarDirective_evalfloat(source_t *source)\n{\n\tfloat value;\n\ttoken_t token;\n\n\tif (!PC_DollarEvaluate(source, NULL, &value, qfalse)) return qfalse;\n\ttoken.line = source->scriptstack->line;\n\ttoken.whitespace_p = source->scriptstack->script_p;\n\ttoken.endwhitespace_p = source->scriptstack->script_p;\n\ttoken.linescrossed = 0;\n\tsprintf(token.string, \"%1.2f\", fabs(value));\n\ttoken.type = TT_NUMBER;\n\ttoken.subtype = TT_FLOAT|TT_LONG|TT_DECIMAL;\n\n#ifdef NUMBERVALUE\n\ttoken.floatvalue = fabs(value);\n\ttoken.intvalue = (unsigned long) token.floatvalue;\n#endif //NUMBERVALUE\n\n\tPC_UnreadSourceToken(source, &token);\n\tif (value < 0)\n\t\tUnreadSignToken(source);\n\n\treturn qtrue;\n} //end of the function PC_DollarDirective_evalfloat\n//============================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//============================================================================\ndirective_t dollardirectives[20] =\n{\n\t{\"evalint\", PC_DollarDirective_evalint},\n\t{\"evalfloat\", PC_DollarDirective_evalfloat},\n\t{NULL, NULL}\n};\n\nint PC_ReadDollarDirective(source_t *source)\n{\n\ttoken_t token;\n\tint i;\n\n\t//read the directive name\n\tif (!PC_ReadSourceToken(source, &token))\n\t{\n\t\tSourceError(source, \"found $ without name\");\n\t\treturn qfalse;\n\t} //end if\n\t//directive name must be on the same line\n\tif (token.linescrossed > 0)\n\t{\n\t\tPC_UnreadSourceToken(source, &token);\n\t\tSourceError(source, \"found $ at end of line\");\n\t\treturn qfalse;\n\t} //end if\n\t//if if is a name\n\tif (token.type == TT_NAME)\n\t{\n\t\t//find the precompiler directive\n\t\tfor (i = 0; dollardirectives[i].name; i++)\n\t\t{\n\t\t\tif (!strcmp(dollardirectives[i].name, token.string))\n\t\t\t{\n\t\t\t\treturn dollardirectives[i].func(source);\n\t\t\t} //end if\n\t\t} //end for\n\t} //end if\n\tPC_UnreadSourceToken(source, &token);\n\tSourceError(source, \"unknown precompiler directive %s\", token.string);\n\treturn qfalse;\n} //end of the function PC_ReadDirective\n\n#ifdef QUAKEC\n//============================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//============================================================================\nint BuiltinFunction(source_t *source)\n{\n\ttoken_t token;\n\n\tif (!PC_ReadSourceToken(source, &token)) return qfalse;\n\tif (token.type == TT_NUMBER)\n\t{\n\t\tPC_UnreadSourceToken(source, &token);\n\t\treturn qtrue;\n\t} //end if\n\telse\n\t{\n\t\tPC_UnreadSourceToken(source, &token);\n\t\treturn qfalse;\n\t} //end else\n} //end of the function BuiltinFunction\n//============================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//============================================================================\nint QuakeCMacro(source_t *source)\n{\n\tint i;\n\ttoken_t token;\n\n\tif (!PC_ReadSourceToken(source, &token)) return qtrue;\n\tif (token.type != TT_NAME)\n\t{\n\t\tPC_UnreadSourceToken(source, &token);\n\t\treturn qtrue;\n\t} //end if\n\t//find the precompiler directive\n\tfor (i = 0; dollardirectives[i].name; i++)\n\t{\n\t\tif (!strcmp(dollardirectives[i].name, token.string))\n\t\t{\n\t\t\tPC_UnreadSourceToken(source, &token);\n\t\t\treturn qfalse;\n\t\t} //end if\n\t} //end for\n\tPC_UnreadSourceToken(source, &token);\n\treturn qtrue;\n} //end of the function QuakeCMacro\n#endif //QUAKEC\n//============================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//============================================================================\nint PC_ReadToken(source_t *source, token_t *token)\n{\n\tdefine_t *define;\n\n\twhile(1)\n\t{\n\t\tif (!PC_ReadSourceToken(source, token)) return qfalse;\n\t\t//check for precompiler directives\n\t\tif (token->type == TT_PUNCTUATION && *token->string == '#')\n\t\t{\n#ifdef QUAKEC\n\t\t\tif (!BuiltinFunction(source))\n#endif //QUAKC\n\t\t\t{\n\t\t\t\t//read the precompiler directive\n\t\t\t\tif (!PC_ReadDirective(source)) return qfalse;\n\t\t\t\tcontinue;\n\t\t\t} //end if\n\t\t} //end if\n\t\tif (token->type == TT_PUNCTUATION && *token->string == '$')\n\t\t{\n#ifdef QUAKEC\n\t\t\tif (!QuakeCMacro(source))\n#endif //QUAKEC\n\t\t\t{\n\t\t\t\t//read the precompiler directive\n\t\t\t\tif (!PC_ReadDollarDirective(source)) return qfalse;\n\t\t\t\tcontinue;\n\t\t\t} //end if\n\t\t} //end if\n\t\t// recursively concatenate strings that are behind each other still resolving defines\n\t\tif (token->type == TT_STRING)\n\t\t{\n\t\t\ttoken_t newtoken;\n\t\t\tif (PC_ReadToken(source, &newtoken))\n\t\t\t{\n\t\t\t\tif (newtoken.type == TT_STRING)\n\t\t\t\t{\n\t\t\t\t\ttoken->string[strlen(token->string)-1] = '\\0';\n\t\t\t\t\tif (strlen(token->string) + strlen(newtoken.string+1) + 1 >= MAX_TOKEN)\n\t\t\t\t\t{\n\t\t\t\t\t\tSourceError(source, \"string longer than MAX_TOKEN %d\\n\", MAX_TOKEN);\n\t\t\t\t\t\treturn qfalse;\n\t\t\t\t\t}\n\t\t\t\t\tstrcat(token->string, newtoken.string+1);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tPC_UnreadToken(source, &newtoken);\n\t\t\t\t}\n\t\t\t}\n\t\t} //end if\n\t\t//if skipping source because of conditional compilation\n\t\tif (source->skip) continue;\n\t\t//if the token is a name\n\t\tif (token->type == TT_NAME)\n\t\t{\n\t\t\t//check if the name is a define macro\n#if DEFINEHASHING\n\t\t\tdefine = PC_FindHashedDefine(source->definehash, token->string);\n#else\n\t\t\tdefine = PC_FindDefine(source->defines, token->string);\n#endif //DEFINEHASHING\n\t\t\t//if it is a define macro\n\t\t\tif (define)\n\t\t\t{\n\t\t\t\t//expand the defined macro\n\t\t\t\tif (!PC_ExpandDefineIntoSource(source, token, define)) return qfalse;\n\t\t\t\tcontinue;\n\t\t\t} //end if\n\t\t} //end if\n\t\t//copy token for unreading\n\t\tCom_Memcpy(&source->token, token, sizeof(token_t));\n\t\t//found a token\n\t\treturn qtrue;\n\t} //end while\n} //end of the function PC_ReadToken\n//============================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//============================================================================\nint PC_ExpectTokenString(source_t *source, char *string)\n{\n\ttoken_t token;\n\n\tif (!PC_ReadToken(source, &token))\n\t{\n\t\tSourceError(source, \"couldn't find expected %s\", string);\n\t\treturn qfalse;\n\t} //end if\n\n\tif (strcmp(token.string, string))\n\t{\n\t\tSourceError(source, \"expected %s, found %s\", string, token.string);\n\t\treturn qfalse;\n\t} //end if\n\treturn qtrue;\n} //end of the function PC_ExpectTokenString\n//============================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//============================================================================\nint PC_ExpectTokenType(source_t *source, int type, int subtype, token_t *token)\n{\n\tchar str[MAX_TOKEN];\n\n\tif (!PC_ReadToken(source, token))\n\t{\n\t\tSourceError(source, \"couldn't read expected token\");\n\t\treturn qfalse;\n\t} //end if\n\n\tif (token->type != type)\n\t{\n\t\tstrcpy(str, \"\");\n\t\tif (type == TT_STRING) strcpy(str, \"string\");\n\t\tif (type == TT_LITERAL) strcpy(str, \"literal\");\n\t\tif (type == TT_NUMBER) strcpy(str, \"number\");\n\t\tif (type == TT_NAME) strcpy(str, \"name\");\n\t\tif (type == TT_PUNCTUATION) strcpy(str, \"punctuation\");\n\t\tSourceError(source, \"expected a %s, found %s\", str, token->string);\n\t\treturn qfalse;\n\t} //end if\n\tif (token->type == TT_NUMBER)\n\t{\n\t\tif ((token->subtype & subtype) != subtype)\n\t\t{\n\t\t\tif (subtype & TT_DECIMAL) strcpy(str, \"decimal\");\n\t\t\tif (subtype & TT_HEX) strcpy(str, \"hex\");\n\t\t\tif (subtype & TT_OCTAL) strcpy(str, \"octal\");\n\t\t\tif (subtype & TT_BINARY) strcpy(str, \"binary\");\n\t\t\tif (subtype & TT_LONG) strcat(str, \" long\");\n\t\t\tif (subtype & TT_UNSIGNED) strcat(str, \" unsigned\");\n\t\t\tif (subtype & TT_FLOAT) strcat(str, \" float\");\n\t\t\tif (subtype & TT_INTEGER) strcat(str, \" integer\");\n\t\t\tSourceError(source, \"expected %s, found %s\", str, token->string);\n\t\t\treturn qfalse;\n\t\t} //end if\n\t} //end if\n\telse if (token->type == TT_PUNCTUATION)\n\t{\n\t\tif (token->subtype != subtype)\n\t\t{\n\t\t\tSourceError(source, \"found %s\", token->string);\n\t\t\treturn qfalse;\n\t\t} //end if\n\t} //end else if\n\treturn qtrue;\n} //end of the function PC_ExpectTokenType\n//============================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//============================================================================\nint PC_ExpectAnyToken(source_t *source, token_t *token)\n{\n\tif (!PC_ReadToken(source, token))\n\t{\n\t\tSourceError(source, \"couldn't read expected token\");\n\t\treturn qfalse;\n\t} //end if\n\telse\n\t{\n\t\treturn qtrue;\n\t} //end else\n} //end of the function PC_ExpectAnyToken\n//============================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//============================================================================\nint PC_CheckTokenString(source_t *source, char *string)\n{\n\ttoken_t tok;\n\n\tif (!PC_ReadToken(source, &tok)) return qfalse;\n\t//if the token is available\n\tif (!strcmp(tok.string, string)) return qtrue;\n\t//\n\tPC_UnreadSourceToken(source, &tok);\n\treturn qfalse;\n} //end of the function PC_CheckTokenString\n//============================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//============================================================================\nint PC_CheckTokenType(source_t *source, int type, int subtype, token_t *token)\n{\n\ttoken_t tok;\n\n\tif (!PC_ReadToken(source, &tok)) return qfalse;\n\t//if the type matches\n\tif (tok.type == type &&\n\t\t\t(tok.subtype & subtype) == subtype)\n\t{\n\t\tCom_Memcpy(token, &tok, sizeof(token_t));\n\t\treturn qtrue;\n\t} //end if\n\t//\n\tPC_UnreadSourceToken(source, &tok);\n\treturn qfalse;\n} //end of the function PC_CheckTokenType\n//============================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//============================================================================\nint PC_SkipUntilString(source_t *source, char *string)\n{\n\ttoken_t token;\n\n\twhile(PC_ReadToken(source, &token))\n\t{\n\t\tif (!strcmp(token.string, string)) return qtrue;\n\t} //end while\n\treturn qfalse;\n} //end of the function PC_SkipUntilString\n//============================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//============================================================================\nvoid PC_UnreadLastToken(source_t *source)\n{\n\tPC_UnreadSourceToken(source, &source->token);\n} //end of the function PC_UnreadLastToken\n//============================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//============================================================================\nvoid PC_UnreadToken(source_t *source, token_t *token)\n{\n\tPC_UnreadSourceToken(source, token);\n} //end of the function PC_UnreadToken\n//============================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//============================================================================\nvoid PC_SetIncludePath(source_t *source, char *path)\n{\n\tstrncpy(source->includepath, path, MAX_PATH);\n\t//add trailing path seperator\n\tif (source->includepath[strlen(source->includepath)-1] != '\\\\' &&\n\t\tsource->includepath[strlen(source->includepath)-1] != '/')\n\t{\n\t\tstrcat(source->includepath, PATHSEPERATOR_STR);\n\t} //end if\n} //end of the function PC_SetIncludePath\n//============================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//============================================================================\nvoid PC_SetPunctuations(source_t *source, punctuation_t *p)\n{\n\tsource->punctuations = p;\n} //end of the function PC_SetPunctuations\n//============================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//============================================================================\nsource_t *LoadSourceFile(const char *filename)\n{\n\tsource_t *source;\n\tscript_t *script;\n\n\tPC_InitTokenHeap();\n\n\tscript = LoadScriptFile(filename);\n\tif (!script) return NULL;\n\n\tscript->next = NULL;\n\n\tsource = (source_t *) GetMemory(sizeof(source_t));\n\tCom_Memset(source, 0, sizeof(source_t));\n\n\tstrncpy(source->filename, filename, MAX_PATH);\n\tsource->scriptstack = script;\n\tsource->tokens = NULL;\n\tsource->defines = NULL;\n\tsource->indentstack = NULL;\n\tsource->skip = 0;\n\n#if DEFINEHASHING\n\tsource->definehash = GetClearedMemory(DEFINEHASHSIZE * sizeof(define_t *));\n#endif //DEFINEHASHING\n\tPC_AddGlobalDefinesToSource(source);\n\treturn source;\n} //end of the function LoadSourceFile\n//============================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//============================================================================\nsource_t *LoadSourceMemory(char *ptr, int length, char *name)\n{\n\tsource_t *source;\n\tscript_t *script;\n\n\tPC_InitTokenHeap();\n\n\tscript = LoadScriptMemory(ptr, length, name);\n\tif (!script) return NULL;\n\tscript->next = NULL;\n\n\tsource = (source_t *) GetMemory(sizeof(source_t));\n\tCom_Memset(source, 0, sizeof(source_t));\n\n\tstrncpy(source->filename, name, MAX_PATH);\n\tsource->scriptstack = script;\n\tsource->tokens = NULL;\n\tsource->defines = NULL;\n\tsource->indentstack = NULL;\n\tsource->skip = 0;\n\n#if DEFINEHASHING\n\tsource->definehash = GetClearedMemory(DEFINEHASHSIZE * sizeof(define_t *));\n#endif //DEFINEHASHING\n\tPC_AddGlobalDefinesToSource(source);\n\treturn source;\n} //end of the function LoadSourceMemory\n//============================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//============================================================================\nvoid FreeSource(source_t *source)\n{\n\tscript_t *script;\n\ttoken_t *token;\n\tdefine_t *define;\n\tindent_t *indent;\n\tint i;\n\n\t//PC_PrintDefineHashTable(source->definehash);\n\t//free all the scripts\n\twhile(source->scriptstack)\n\t{\n\t\tscript = source->scriptstack;\n\t\tsource->scriptstack = source->scriptstack->next;\n\t\tFreeScript(script);\n\t} //end for\n\t//free all the tokens\n\twhile(source->tokens)\n\t{\n\t\ttoken = source->tokens;\n\t\tsource->tokens = source->tokens->next;\n\t\tPC_FreeToken(token);\n\t} //end for\n#if DEFINEHASHING\n\tfor (i = 0; i < DEFINEHASHSIZE; i++)\n\t{\n\t\twhile(source->definehash[i])\n\t\t{\n\t\t\tdefine = source->definehash[i];\n\t\t\tsource->definehash[i] = source->definehash[i]->hashnext;\n\t\t\tPC_FreeDefine(define);\n\t\t} //end while\n\t} //end for\n#else //DEFINEHASHING\n\t//free all defines\n\twhile(source->defines)\n\t{\n\t\tdefine = source->defines;\n\t\tsource->defines = source->defines->next;\n\t\tPC_FreeDefine(define);\n\t} //end for\n#endif //DEFINEHASHING\n\t//free all indents\n\twhile(source->indentstack)\n\t{\n\t\tindent = source->indentstack;\n\t\tsource->indentstack = source->indentstack->next;\n\t\tFreeMemory(indent);\n\t} //end for\n#if DEFINEHASHING\n\t//\n\tif (source->definehash) FreeMemory(source->definehash);\n#endif //DEFINEHASHING\n\t//free the source itself\n\tFreeMemory(source);\n} //end of the function FreeSource\n//============================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//============================================================================\n\n#define MAX_SOURCEFILES\t\t64\n\nsource_t *sourceFiles[MAX_SOURCEFILES];\n\nint PC_LoadSourceHandle(const char *filename)\n{\n\tsource_t *source;\n\tint i;\n\n\tfor (i = 1; i < MAX_SOURCEFILES; i++)\n\t{\n\t\tif (!sourceFiles[i])\n\t\t\tbreak;\n\t} //end for\n\tif (i >= MAX_SOURCEFILES)\n\t\treturn 0;\n\tPS_SetBaseFolder(\"\");\n\tsource = LoadSourceFile(filename);\n\tif (!source)\n\t\treturn 0;\n\tsourceFiles[i] = source;\n\treturn i;\n} //end of the function PC_LoadSourceHandle\n//============================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//============================================================================\nint PC_FreeSourceHandle(int handle)\n{\n\tif (handle < 1 || handle >= MAX_SOURCEFILES)\n\t\treturn qfalse;\n\tif (!sourceFiles[handle])\n\t\treturn qfalse;\n\n\tFreeSource(sourceFiles[handle]);\n\tsourceFiles[handle] = NULL;\n\treturn qtrue;\n} //end of the function PC_FreeSourceHandle\n//============================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//============================================================================\nint PC_ReadTokenHandle(int handle, pc_token_t *pc_token)\n{\n\ttoken_t token;\n\tint ret;\n\n\tif (handle < 1 || handle >= MAX_SOURCEFILES)\n\t\treturn 0;\n\tif (!sourceFiles[handle])\n\t\treturn 0;\n\n\tret = PC_ReadToken(sourceFiles[handle], &token);\n\tstrcpy(pc_token->string, token.string);\n\tpc_token->type = token.type;\n\tpc_token->subtype = token.subtype;\n\tpc_token->intvalue = token.intvalue;\n\tpc_token->floatvalue = token.floatvalue;\n\tif (pc_token->type == TT_STRING)\n\t\tStripDoubleQuotes(pc_token->string);\n\treturn ret;\n} //end of the function PC_ReadTokenHandle\n//============================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//============================================================================\nint PC_SourceFileAndLine(int handle, char *filename, int *line)\n{\n\tif (handle < 1 || handle >= MAX_SOURCEFILES)\n\t\treturn qfalse;\n\tif (!sourceFiles[handle])\n\t\treturn qfalse;\n\n\tstrcpy(filename, sourceFiles[handle]->filename);\n\tif (sourceFiles[handle]->scriptstack)\n\t\t*line = sourceFiles[handle]->scriptstack->line;\n\telse\n\t\t*line = 0;\n\treturn qtrue;\n} //end of the function PC_SourceFileAndLine\n//============================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//============================================================================\nvoid PC_SetBaseFolder(char *path)\n{\n\tPS_SetBaseFolder(path);\n} //end of the function PC_SetBaseFolder\n//============================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//============================================================================\nvoid PC_CheckOpenSourceHandles(void)\n{\n\tint i;\n\n\tfor (i = 1; i < MAX_SOURCEFILES; i++)\n\t{\n\t\tif (sourceFiles[i])\n\t\t{\n#ifdef BOTLIB\n\t\t\tbotimport.Print(PRT_ERROR, \"file %s still open in precompiler\\n\", sourceFiles[i]->scriptstack->filename);\n#endif\t//BOTLIB\n\t\t} //end if\n\t} //end for\n} //end of the function PC_CheckOpenSourceHandles\n\n"
  },
  {
    "path": "plugins/quake3/botlib/l_precomp.h",
    "content": "/*\n===========================================================================\nCopyright (C) 1999-2005 Id Software, Inc.\n\nThis file is part of Quake III Arena source code.\n\nQuake III Arena source code is free software; you can redistribute it\nand/or modify it under the terms of the GNU General Public License as\npublished by the Free Software Foundation; either version 2 of the License,\nor (at your option) any later version.\n\nQuake III Arena source code is distributed in the hope that it will be\nuseful, but WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Quake III Arena source code; if not, write to the Free Software\nFoundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n===========================================================================\n*/\n\n/*****************************************************************************\n * name:\t\tl_precomp.h\n *\n * desc:\t\tpre compiler\n *\n * $Archive: /source/code/botlib/l_precomp.h $\n *\n *****************************************************************************/\n\n#ifndef MAX_PATH\n\t#define MAX_PATH\t\t\tMAX_QPATH\n#endif\n\n#ifndef PATH_SEPERATORSTR\n\t#if defined(WIN32)|defined(_WIN32)|defined(__NT__)|defined(__WINDOWS__)|defined(__WINDOWS_386__)\n\t\t#define PATHSEPERATOR_STR\t\t\"\\\\\"\n\t#else\n\t\t#define PATHSEPERATOR_STR\t\t\"/\"\n\t#endif\n#endif\n#ifndef PATH_SEPERATORCHAR\n\t#if defined(WIN32)|defined(_WIN32)|defined(__NT__)|defined(__WINDOWS__)|defined(__WINDOWS_386__)\n\t\t#define PATHSEPERATOR_CHAR\t\t'\\\\'\n\t#else\n\t\t#define PATHSEPERATOR_CHAR\t\t'/'\n\t#endif\n#endif\n\n#if defined(BSPC) && !defined(QDECL)\n#define QDECL\n#endif\n\n\n#define DEFINE_FIXED\t\t\t0x0001\n\n#define BUILTIN_LINE\t\t\t1\n#define BUILTIN_FILE\t\t\t2\n#define BUILTIN_DATE\t\t\t3\n#define BUILTIN_TIME\t\t\t4\n#define BUILTIN_STDC\t\t\t5\n\n#define INDENT_IF\t\t\t\t0x0001\n#define INDENT_ELSE\t\t\t\t0x0002\n#define INDENT_ELIF\t\t\t\t0x0004\n#define INDENT_IFDEF\t\t\t0x0008\n#define INDENT_IFNDEF\t\t\t0x0010\n\n//macro definitions\ntypedef struct define_s\n{\n\tchar *name;\t\t\t\t\t\t\t//define name\n\tint flags;\t\t\t\t\t\t\t//define flags\n\tint builtin;\t\t\t\t\t\t// > 0 if builtin define\n\tint numparms;\t\t\t\t\t\t//number of define parameters\n\ttoken_t *parms;\t\t\t\t\t\t//define parameters\n\ttoken_t *tokens;\t\t\t\t\t//macro tokens (possibly containing parm tokens)\n\tstruct define_s *next;\t\t\t\t//next defined macro in a list\n\tstruct define_s *hashnext;\t\t\t//next define in the hash chain\n} define_t;\n\n//indents\n//used for conditional compilation directives:\n//#if, #else, #elif, #ifdef, #ifndef\ntypedef struct indent_s\n{\n\tint type;\t\t\t\t\t\t\t\t//indent type\n\tint skip;\t\t\t\t\t\t\t\t//true if skipping current indent\n\tscript_t *script;\t\t\t\t\t\t//script the indent was in\n\tstruct indent_s *next;\t\t\t\t\t//next indent on the indent stack\n} indent_t;\n\n//source file\ntypedef struct source_s\n{\n\tchar filename[1024];\t\t\t\t\t//file name of the script\n\tchar includepath[1024];\t\t\t\t\t//path to include files\n\tpunctuation_t *punctuations;\t\t\t//punctuations to use\n\tscript_t *scriptstack;\t\t\t\t\t//stack with scripts of the source\n\ttoken_t *tokens;\t\t\t\t\t\t//tokens to read first\n\tdefine_t *defines;\t\t\t\t\t\t//list with macro definitions\n\tdefine_t **definehash;\t\t\t\t\t//hash chain with defines\n\tindent_t *indentstack;\t\t\t\t\t//stack with indents\n\tint skip;\t\t\t\t\t\t\t\t// > 0 if skipping conditional code\n\ttoken_t token;\t\t\t\t\t\t\t//last read token\n} source_t;\n\n\n//read a token from the source\nint PC_ReadToken(source_t *source, token_t *token);\n//expect a certain token\nint PC_ExpectTokenString(source_t *source, char *string);\n//expect a certain token type\nint PC_ExpectTokenType(source_t *source, int type, int subtype, token_t *token);\n//expect a token\nint PC_ExpectAnyToken(source_t *source, token_t *token);\n//returns true when the token is available\nint PC_CheckTokenString(source_t *source, char *string);\n//returns true an reads the token when a token with the given type is available\nint PC_CheckTokenType(source_t *source, int type, int subtype, token_t *token);\n//skip tokens until the given token string is read\nint PC_SkipUntilString(source_t *source, char *string);\n//unread the last token read from the script\nvoid PC_UnreadLastToken(source_t *source);\n//unread the given token\nvoid PC_UnreadToken(source_t *source, token_t *token);\n//read a token only if on the same line, lines are concatenated with a slash\nint PC_ReadLine(source_t *source, token_t *token);\n//returns true if there was a white space in front of the token\nint PC_WhiteSpaceBeforeToken(token_t *token);\n//add a define to the source\nint PC_AddDefine(source_t *source, char *string);\n//add a globals define that will be added to all opened sources\nint PC_AddGlobalDefine(char *string);\n//remove the given global define\nint PC_RemoveGlobalDefine(char *name);\n//remove all globals defines\nvoid PC_RemoveAllGlobalDefines(void);\n//add builtin defines\nvoid PC_AddBuiltinDefines(source_t *source);\n//set the source include path\nvoid PC_SetIncludePath(source_t *source, char *path);\n//set the punction set\nvoid PC_SetPunctuations(source_t *source, punctuation_t *p);\n//set the base folder to load files from\nvoid PC_SetBaseFolder(char *path);\n//load a source file\nsource_t *LoadSourceFile(const char *filename);\n//load a source from memory\nsource_t *LoadSourceMemory(char *ptr, int length, char *name);\n//free the given source\nvoid FreeSource(source_t *source);\n//print a source error\nvoid QDECL SourceError(source_t *source, char *str, ...);\n//print a source warning\nvoid QDECL SourceWarning(source_t *source, char *str, ...);\n\n#ifdef BSPC\n// some of BSPC source does include game/q_shared.h and some does not\n// we define pc_token_s pc_token_t if needed (yes, it's ugly)\n#ifndef __Q_SHARED_H\n#define MAX_TOKENLENGTH\t\t1024\ntypedef struct pc_token_s\n{\n\tint type;\n\tint subtype;\n\tint intvalue;\n\tfloat floatvalue;\n\tchar string[MAX_TOKENLENGTH];\n} pc_token_t;\n#endif //!_Q_SHARED_H\n#endif //BSPC\n\n//\nint PC_LoadSourceHandle(const char *filename);\nint PC_FreeSourceHandle(int handle);\nint PC_ReadTokenHandle(int handle, pc_token_t *pc_token);\nint PC_SourceFileAndLine(int handle, char *filename, int *line);\nvoid PC_CheckOpenSourceHandles(void);\n"
  },
  {
    "path": "plugins/quake3/botlib/l_script.c",
    "content": "/*\n===========================================================================\nCopyright (C) 1999-2005 Id Software, Inc.\n\nThis file is part of Quake III Arena source code.\n\nQuake III Arena source code is free software; you can redistribute it\nand/or modify it under the terms of the GNU General Public License as\npublished by the Free Software Foundation; either version 2 of the License,\nor (at your option) any later version.\n\nQuake III Arena source code is distributed in the hope that it will be\nuseful, but WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Quake III Arena source code; if not, write to the Free Software\nFoundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n===========================================================================\n*/\n\n/*****************************************************************************\n * name:\t\tl_script.c\n *\n * desc:\t\tlexicographical parser\n *\n * $Archive: /MissionPack/code/botlib/l_script.c $\n *\n *****************************************************************************/\n\n//#define SCREWUP\n//#define BOTLIB\n//#define MEQCC\n//#define BSPC\n\n#ifdef SCREWUP\n#include <stdio.h>\n#include <stdlib.h>\n#include <limits.h>\n#include <string.h>\n#include <stdarg.h>\n#include \"l_memory.h\"\n#include \"l_script.h\"\n\ntypedef enum {qfalse, qtrue}\tqboolean;\n\n#endif //SCREWUP\n\n#ifdef BOTLIB\n//include files for usage in the bot library\n#include \"q_shared.h\"\n#include \"botlib.h\"\n#include \"be_interface.h\"\n#include \"l_script.h\"\n#include \"l_memory.h\"\n#include \"l_log.h\"\n#include \"l_libvar.h\"\n#endif //BOTLIB\n\n#ifdef MEQCC\n//include files for usage in MrElusive's QuakeC Compiler\n#include \"qcc.h\"\n#include \"l_script.h\"\n#include \"l_memory.h\"\n#include \"l_log.h\"\n\n#define qtrue\ttrue\n#define qfalse\tfalse\n#endif //MEQCC\n\n#ifdef BSPC\n//include files for usage in the BSP Converter\n#include \"../bspc/qbsp.h\"\n#include \"../bspc/l_log.h\"\n#include \"../bspc/l_mem.h\"\n\n#define qtrue\ttrue\n#define qfalse\tfalse\n#endif //BSPC\n\n\n#define PUNCTABLE\n\n//longer punctuations first\npunctuation_t default_punctuations[] =\n{\n\t//binary operators\n\t{\">>=\",P_RSHIFT_ASSIGN, NULL},\n\t{\"<<=\",P_LSHIFT_ASSIGN, NULL},\n\t//\n\t{\"...\",P_PARMS, NULL},\n\t//define merge operator\n\t{\"##\",P_PRECOMPMERGE, NULL},\n\t//logic operators\n\t{\"&&\",P_LOGIC_AND, NULL},\n\t{\"||\",P_LOGIC_OR, NULL},\n\t{\">=\",P_LOGIC_GEQ, NULL},\n\t{\"<=\",P_LOGIC_LEQ, NULL},\n\t{\"==\",P_LOGIC_EQ, NULL},\n\t{\"!=\",P_LOGIC_UNEQ, NULL},\n\t//arithmatic operators\n\t{\"*=\",P_MUL_ASSIGN, NULL},\n\t{\"/=\",P_DIV_ASSIGN, NULL},\n\t{\"%=\",P_MOD_ASSIGN, NULL},\n\t{\"+=\",P_ADD_ASSIGN, NULL},\n\t{\"-=\",P_SUB_ASSIGN, NULL},\n\t{\"++\",P_INC, NULL},\n\t{\"--\",P_DEC, NULL},\n\t//binary operators\n\t{\"&=\",P_BIN_AND_ASSIGN, NULL},\n\t{\"|=\",P_BIN_OR_ASSIGN, NULL},\n\t{\"^=\",P_BIN_XOR_ASSIGN, NULL},\n\t{\">>\",P_RSHIFT, NULL},\n\t{\"<<\",P_LSHIFT, NULL},\n\t//reference operators\n\t{\"->\",P_POINTERREF, NULL},\n\t//C++\n\t{\"::\",P_CPP1, NULL},\n\t{\".*\",P_CPP2, NULL},\n\t//arithmatic operators\n\t{\"*\",P_MUL, NULL},\n\t{\"/\",P_DIV, NULL},\n\t{\"%\",P_MOD, NULL},\n\t{\"+\",P_ADD, NULL},\n\t{\"-\",P_SUB, NULL},\n\t{\"=\",P_ASSIGN, NULL},\n\t//binary operators\n\t{\"&\",P_BIN_AND, NULL},\n\t{\"|\",P_BIN_OR, NULL},\n\t{\"^\",P_BIN_XOR, NULL},\n\t{\"~\",P_BIN_NOT, NULL},\n\t//logic operators\n\t{\"!\",P_LOGIC_NOT, NULL},\n\t{\">\",P_LOGIC_GREATER, NULL},\n\t{\"<\",P_LOGIC_LESS, NULL},\n\t//reference operator\n\t{\".\",P_REF, NULL},\n\t//seperators\n\t{\",\",P_COMMA, NULL},\n\t{\";\",P_SEMICOLON, NULL},\n\t//label indication\n\t{\":\",P_COLON, NULL},\n\t//if statement\n\t{\"?\",P_QUESTIONMARK, NULL},\n\t//embracements\n\t{\"(\",P_PARENTHESESOPEN, NULL},\n\t{\")\",P_PARENTHESESCLOSE, NULL},\n\t{\"{\",P_BRACEOPEN, NULL},\n\t{\"}\",P_BRACECLOSE, NULL},\n\t{\"[\",P_SQBRACKETOPEN, NULL},\n\t{\"]\",P_SQBRACKETCLOSE, NULL},\n\t//\n\t{\"\\\\\",P_BACKSLASH, NULL},\n\t//precompiler operator\n\t{\"#\",P_PRECOMP, NULL},\n#ifdef DOLLAR\n\t{\"$\",P_DOLLAR, NULL},\n#endif //DOLLAR\n\t{NULL, 0}\n};\n\n#ifdef BSPC\nchar basefolder[MAX_PATH];\n#else\nchar basefolder[MAX_QPATH];\n#endif\n\n#ifdef _WIN64\n        #ifdef FTE_SDL\n                #define vsnprintf linuxlike_vsnprintf\n        #endif\n#endif\n\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid PS_CreatePunctuationTable(script_t *script, punctuation_t *punctuations)\n{\n\tint i;\n\tpunctuation_t *p, *lastp, *newp;\n\n\t//get memory for the table\n\tif (!script->punctuationtable) script->punctuationtable = (punctuation_t **)\n\t\t\t\t\t\t\t\t\t\t\t\tGetMemory(256 * sizeof(punctuation_t *));\n\tCom_Memset(script->punctuationtable, 0, 256 * sizeof(punctuation_t *));\n\t//add the punctuations in the list to the punctuation table\n\tfor (i = 0; punctuations[i].p; i++)\n\t{\n\t\tnewp = &punctuations[i];\n\t\tlastp = NULL;\n\t\t//sort the punctuations in this table entry on length (longer punctuations first)\n\t\tfor (p = script->punctuationtable[(unsigned int) newp->p[0]]; p; p = p->next)\n\t\t{\n\t\t\tif (strlen(p->p) < strlen(newp->p))\n\t\t\t{\n\t\t\t\tnewp->next = p;\n\t\t\t\tif (lastp) lastp->next = newp;\n\t\t\t\telse script->punctuationtable[(unsigned int) newp->p[0]] = newp;\n\t\t\t\tbreak;\n\t\t\t} //end if\n\t\t\tlastp = p;\n\t\t} //end for\n\t\tif (!p)\n\t\t{\n\t\t\tnewp->next = NULL;\n\t\t\tif (lastp) lastp->next = newp;\n\t\t\telse script->punctuationtable[(unsigned int) newp->p[0]] = newp;\n\t\t} //end if\n\t} //end for\n} //end of the function PS_CreatePunctuationTable\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nchar *PunctuationFromNum(script_t *script, int num)\n{\n\tint i;\n\n\tfor (i = 0; script->punctuations[i].p; i++)\n\t{\n\t\tif (script->punctuations[i].n == num) return script->punctuations[i].p;\n\t} //end for\n\treturn \"unkown punctuation\";\n} //end of the function PunctuationFromNum\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid QDECL ScriptError(script_t *script, char *str, ...)\n{\n\tchar text[1024];\n\tva_list ap;\n\n\tif (script->flags & SCFL_NOERRORS) return;\n\n\tva_start(ap, str);\n\tvsnprintf(text, sizeof(text), str, ap);\n\tva_end(ap);\n#ifdef BOTLIB\n\tbotimport.Print(PRT_ERROR, \"file %s, line %d: %s\\n\", script->filename, script->line, text);\n#endif //BOTLIB\n#ifdef MEQCC\n\tprintf(\"error: file %s, line %d: %s\\n\", script->filename, script->line, text);\n#endif //MEQCC\n#ifdef BSPC\n\tLog_Print(\"error: file %s, line %d: %s\\n\", script->filename, script->line, text);\n#endif //BSPC\n} //end of the function ScriptError\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid QDECL ScriptWarning(script_t *script, char *str, ...)\n{\n\tchar text[1024];\n\tva_list ap;\n\n\tif (script->flags & SCFL_NOWARNINGS) return;\n\n\tva_start(ap, str);\n\tvsnprintf(text, sizeof(text), str, ap);\n\tva_end(ap);\n#ifdef BOTLIB\n\tbotimport.Print(PRT_WARNING, \"file %s, line %d: %s\\n\", script->filename, script->line, text);\n#endif //BOTLIB\n#ifdef MEQCC\n\tprintf(\"warning: file %s, line %d: %s\\n\", script->filename, script->line, text);\n#endif //MEQCC\n#ifdef BSPC\n\tLog_Print(\"warning: file %s, line %d: %s\\n\", script->filename, script->line, text);\n#endif //BSPC\n} //end of the function ScriptWarning\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nvoid SetScriptPunctuations(script_t *script, punctuation_t *p)\n{\n#ifdef PUNCTABLE\n\tif (p) PS_CreatePunctuationTable(script, p);\n\telse  PS_CreatePunctuationTable(script, default_punctuations);\n#endif //PUNCTABLE\n\tif (p) script->punctuations = p;\n\telse script->punctuations = default_punctuations;\n} //end of the function SetScriptPunctuations\n//============================================================================\n// Reads spaces, tabs, C-like comments etc.\n// When a newline character is found the scripts line counter is increased.\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//============================================================================\nint PS_ReadWhiteSpace(script_t *script)\n{\n\twhile(1)\n\t{\n\t\t//skip white space\n\t\twhile(*script->script_p <= ' ')\n\t\t{\n\t\t\tif (!*script->script_p) return 0;\n\t\t\tif (*script->script_p == '\\n') script->line++;\n\t\t\tscript->script_p++;\n\t\t} //end while\n\t\t//skip comments\n\t\tif (*script->script_p == '/')\n\t\t{\n\t\t\t//comments //\n\t\t\tif (*(script->script_p+1) == '/')\n\t\t\t{\n\t\t\t\tscript->script_p++;\n\t\t\t\tdo\n\t\t\t\t{\n\t\t\t\t\tscript->script_p++;\n\t\t\t\t\tif (!*script->script_p) return 0;\n\t\t\t\t} //end do\n\t\t\t\twhile(*script->script_p != '\\n');\n\t\t\t\tscript->line++;\n\t\t\t\tscript->script_p++;\n\t\t\t\tif (!*script->script_p) return 0;\n\t\t\t\tcontinue;\n\t\t\t} //end if\n\t\t\t//comments /* */\n\t\t\telse if (*(script->script_p+1) == '*')\n\t\t\t{\n\t\t\t\tscript->script_p++;\n\t\t\t\tdo\n\t\t\t\t{\n\t\t\t\t\tscript->script_p++;\n\t\t\t\t\tif (!*script->script_p) return 0;\n\t\t\t\t\tif (*script->script_p == '\\n') script->line++;\n\t\t\t\t} //end do\n\t\t\t\twhile(!(*script->script_p == '*' && *(script->script_p+1) == '/'));\n\t\t\t\tscript->script_p++;\n\t\t\t\tif (!*script->script_p) return 0;\n\t\t\t\tscript->script_p++;\n\t\t\t\tif (!*script->script_p) return 0;\n\t\t\t\tcontinue;\n\t\t\t} //end if\n\t\t} //end if\n\t\tbreak;\n\t} //end while\n\treturn 1;\n} //end of the function PS_ReadWhiteSpace\n//============================================================================\n// Reads an escape character.\n//\n// Parameter:\t\t\t\tscript\t\t: script to read from\n//\t\t\t\t\t\t\t\tch\t\t\t\t: place to store the read escape character\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//============================================================================\nint PS_ReadEscapeCharacter(script_t *script, char *ch)\n{\n\tint c, val, i;\n\n\t//step over the leading '\\\\'\n\tscript->script_p++;\n\t//determine the escape character\n\tswitch(*script->script_p)\n\t{\n\t\tcase '\\\\': c = '\\\\'; break;\n\t\tcase 'n': c = '\\n'; break;\n\t\tcase 'r': c = '\\r'; break;\n\t\tcase 't': c = '\\t'; break;\n\t\tcase 'v': c = '\\v'; break;\n\t\tcase 'b': c = '\\b'; break;\n\t\tcase 'f': c = '\\f'; break;\n\t\tcase 'a': c = '\\a'; break;\n\t\tcase '\\'': c = '\\''; break;\n\t\tcase '\\\"': c = '\\\"'; break;\n\t\tcase '\\?': c = '\\?'; break;\n\t\tcase 'x':\n\t\t{\n\t\t\tscript->script_p++;\n\t\t\tfor (i = 0, val = 0; ; i++, script->script_p++)\n\t\t\t{\n\t\t\t\tc = *script->script_p;\n\t\t\t\tif (c >= '0' && c <= '9') c = c - '0';\n\t\t\t\telse if (c >= 'A' && c <= 'Z') c = c - 'A' + 10;\n\t\t\t\telse if (c >= 'a' && c <= 'z') c = c - 'a' + 10;\n\t\t\t\telse break;\n\t\t\t\tval = (val << 4) + c;\n\t\t\t} //end for\n\t\t\tscript->script_p--;\n\t\t\tif (val > 0xFF)\n\t\t\t{\n\t\t\t\tScriptWarning(script, \"too large value in escape character\");\n\t\t\t\tval = 0xFF;\n\t\t\t} //end if\n\t\t\tc = val;\n\t\t\tbreak;\n\t\t} //end case\n\t\tdefault: //NOTE: decimal ASCII code, NOT octal\n\t\t{\n\t\t\tif (*script->script_p < '0' || *script->script_p > '9') ScriptError(script, \"unknown escape char\");\n\t\t\tfor (i = 0, val = 0; ; i++, script->script_p++)\n\t\t\t{\n\t\t\t\tc = *script->script_p;\n\t\t\t\tif (c >= '0' && c <= '9') c = c - '0';\n\t\t\t\telse break;\n\t\t\t\tval = val * 10 + c;\n\t\t\t} //end for\n\t\t\tscript->script_p--;\n\t\t\tif (val > 0xFF)\n\t\t\t{\n\t\t\t\tScriptWarning(script, \"too large value in escape character\");\n\t\t\t\tval = 0xFF;\n\t\t\t} //end if\n\t\t\tc = val;\n\t\t\tbreak;\n\t\t} //end default\n\t} //end switch\n\t//step over the escape character or the last digit of the number\n\tscript->script_p++;\n\t//store the escape character\n\t*ch = c;\n\t//successfully read escape character\n\treturn 1;\n} //end of the function PS_ReadEscapeCharacter\n//============================================================================\n// Reads C-like string. Escape characters are interpretted.\n// Quotes are included with the string.\n// Reads two strings with a white space between them as one string.\n//\n// Parameter:\t\t\t\tscript\t\t: script to read from\n//\t\t\t\t\t\t\t\ttoken\t\t\t: buffer to store the string\n// Returns:\t\t\t\t\tqtrue when a string was read successfully\n// Changes Globals:\t\t-\n//============================================================================\nint PS_ReadString(script_t *script, token_t *token, int quote)\n{\n\tint len, tmpline;\n\tchar *tmpscript_p;\n\n\tif (quote == '\\\"') token->type = TT_STRING;\n\telse token->type = TT_LITERAL;\n\n\tlen = 0;\n\t//leading quote\n\ttoken->string[len++] = *script->script_p++;\n\t//\n\twhile(1)\n\t{\n\t\t//minus 2 because trailing double quote and zero have to be appended\n\t\tif (len >= MAX_TOKEN - 2)\n\t\t{\n\t\t\tScriptError(script, \"string longer than MAX_TOKEN = %d\", MAX_TOKEN);\n\t\t\treturn 0;\n\t\t} //end if\n\t\t//if there is an escape character and\n\t\t//if escape characters inside a string are allowed\n\t\tif (*script->script_p == '\\\\' && !(script->flags & SCFL_NOSTRINGESCAPECHARS))\n\t\t{\n\t\t\tif (!PS_ReadEscapeCharacter(script, &token->string[len]))\n\t\t\t{\n\t\t\t\ttoken->string[len] = 0;\n\t\t\t\treturn 0;\n\t\t\t} //end if\n\t\t\tlen++;\n\t\t} //end if\n\t\t//if a trailing quote\n\t\telse if (*script->script_p == quote)\n\t\t{\n\t\t\t//step over the double quote\n\t\t\tscript->script_p++;\n\t\t\t//if white spaces in a string are not allowed\n\t\t\tif (script->flags & SCFL_NOSTRINGWHITESPACES) break;\n\t\t\t//\n\t\t\ttmpscript_p = script->script_p;\n\t\t\ttmpline = script->line;\n\t\t\t//read unusefull stuff between possible two following strings\n\t\t\tif (!PS_ReadWhiteSpace(script))\n\t\t\t{\n\t\t\t\tscript->script_p = tmpscript_p;\n\t\t\t\tscript->line = tmpline;\n\t\t\t\tbreak;\n\t\t\t} //end if\n\t\t\t//if there's no leading double qoute\n\t\t\tif (*script->script_p != quote)\n\t\t\t{\n\t\t\t\tscript->script_p = tmpscript_p;\n\t\t\t\tscript->line = tmpline;\n\t\t\t\tbreak;\n\t\t\t} //end if\n\t\t\t//step over the new leading double quote\n\t\t\tscript->script_p++;\n\t\t} //end if\n\t\telse\n\t\t{\n\t\t\tif (*script->script_p == '\\0')\n\t\t\t{\n\t\t\t\ttoken->string[len] = 0;\n\t\t\t\tScriptError(script, \"missing trailing quote\");\n\t\t\t\treturn 0;\n\t\t\t} //end if\n\t      if (*script->script_p == '\\n')\n\t\t\t{\n\t\t\t\ttoken->string[len] = 0;\n\t\t\t\tScriptError(script, \"newline inside string %s\", token->string);\n\t\t\t\treturn 0;\n\t\t\t} //end if\n\t\t\ttoken->string[len++] = *script->script_p++;\n\t\t} //end else\n\t} //end while\n\t//trailing quote\n\ttoken->string[len++] = quote;\n\t//end string with a zero\n\ttoken->string[len] = '\\0';\n\t//the sub type is the length of the string\n\ttoken->subtype = len;\n\treturn 1;\n} //end of the function PS_ReadString\n//============================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//============================================================================\nint PS_ReadName(script_t *script, token_t *token)\n{\n\tint len = 0;\n\tchar c;\n\n\ttoken->type = TT_NAME;\n\tdo\n\t{\n\t\ttoken->string[len++] = *script->script_p++;\n\t\tif (len >= MAX_TOKEN)\n\t\t{\n\t\t\tScriptError(script, \"name longer than MAX_TOKEN = %d\", MAX_TOKEN);\n\t\t\treturn 0;\n\t\t} //end if\n\t\tc = *script->script_p;\n   } while ((c >= 'a' && c <= 'z') ||\n\t\t\t\t(c >= 'A' && c <= 'Z') ||\n\t\t\t\t(c >= '0' && c <= '9') ||\n\t\t\t\tc == '_');\n\ttoken->string[len] = '\\0';\n\t//the sub type is the length of the name\n\ttoken->subtype = len;\n\treturn 1;\n} //end of the function PS_ReadName\n//============================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//============================================================================\nvoid NumberValue(char *string, int subtype, unsigned long int *intvalue,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tfloat *floatvalue)\n{\n\tunsigned long int dotfound = 0;\n\n\t*intvalue = 0;\n\t*floatvalue = 0;\n\t//floating point number\n\tif (subtype & TT_FLOAT)\n\t{\n\t\twhile(*string)\n\t\t{\n\t\t\tif (*string == '.')\n\t\t\t{\n\t\t\t\tif (dotfound) return;\n\t\t\t\tdotfound = 10;\n\t\t\t\tstring++;\n\t\t\t} //end if\n\t\t\tif (dotfound)\n\t\t\t{\n\t\t\t\t*floatvalue = *floatvalue + (float) (*string - '0') /\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(float) dotfound;\n\t\t\t\tdotfound *= 10;\n\t\t\t} //end if\n\t\t\telse\n\t\t\t{\n\t\t\t\t*floatvalue = *floatvalue * 10.0 + (float) (*string - '0');\n\t\t\t} //end else\n\t\t\tstring++;\n\t\t} //end while\n\t\t*intvalue = (unsigned long) *floatvalue;\n\t} //end if\n\telse if (subtype & TT_DECIMAL)\n\t{\n\t\twhile(*string) *intvalue = *intvalue * 10 + (*string++ - '0');\n\t\t*floatvalue = *intvalue;\n\t} //end else if\n\telse if (subtype & TT_HEX)\n\t{\n\t\t//step over the leading 0x or 0X\n\t\tstring += 2;\n\t\twhile(*string)\n\t\t{\n\t\t\t*intvalue <<= 4;\n\t\t\tif (*string >= 'a' && *string <= 'f') *intvalue += *string - 'a' + 10;\n\t\t\telse if (*string >= 'A' && *string <= 'F') *intvalue += *string - 'A' + 10;\n\t\t\telse *intvalue += *string - '0';\n\t\t\tstring++;\n\t\t} //end while\n\t\t*floatvalue = *intvalue;\n\t} //end else if\n\telse if (subtype & TT_OCTAL)\n\t{\n\t\t//step over the first zero\n\t\tstring += 1;\n\t\twhile(*string) *intvalue = (*intvalue << 3) + (*string++ - '0');\n\t\t*floatvalue = *intvalue;\n\t} //end else if\n\telse if (subtype & TT_BINARY)\n\t{\n\t\t//step over the leading 0b or 0B\n\t\tstring += 2;\n\t\twhile(*string) *intvalue = (*intvalue << 1) + (*string++ - '0');\n\t\t*floatvalue = *intvalue;\n\t} //end else if\n} //end of the function NumberValue\n//============================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//============================================================================\nint PS_ReadNumber(script_t *script, token_t *token)\n{\n\tint len = 0, i;\n\tint octal, dot;\n\tchar c;\n//\tunsigned long int intvalue = 0;\n//\tdouble floatvalue = 0;\n\n\ttoken->type = TT_NUMBER;\n\t//check for a hexadecimal number\n\tif (*script->script_p == '0' &&\n\t\t(*(script->script_p + 1) == 'x' ||\n\t\t*(script->script_p + 1) == 'X'))\n\t{\n\t\ttoken->string[len++] = *script->script_p++;\n\t\ttoken->string[len++] = *script->script_p++;\n\t\tc = *script->script_p;\n\t\t//hexadecimal\n\t\twhile((c >= '0' && c <= '9') ||\n\t\t\t\t\t(c >= 'a' && c <= 'f') ||\n\t\t\t\t\t(c >= 'A' && c <= 'A'))\n\t\t{\n\t\t\ttoken->string[len++] = *script->script_p++;\n\t\t\tif (len >= MAX_TOKEN)\n\t\t\t{\n\t\t\t\tScriptError(script, \"hexadecimal number longer than MAX_TOKEN = %d\", MAX_TOKEN);\n\t\t\t\treturn 0;\n\t\t\t} //end if\n\t\t\tc = *script->script_p;\n\t\t} //end while\n\t\ttoken->subtype |= TT_HEX;\n\t} //end if\n#ifdef BINARYNUMBERS\n\t//check for a binary number\n\telse if (*script->script_p == '0' &&\n\t\t(*(script->script_p + 1) == 'b' ||\n\t\t*(script->script_p + 1) == 'B'))\n\t{\n\t\ttoken->string[len++] = *script->script_p++;\n\t\ttoken->string[len++] = *script->script_p++;\n\t\tc = *script->script_p;\n\t\t//binary\n\t\twhile(c == '0' || c == '1')\n\t\t{\n\t\t\ttoken->string[len++] = *script->script_p++;\n\t\t\tif (len >= MAX_TOKEN)\n\t\t\t{\n\t\t\t\tScriptError(script, \"binary number longer than MAX_TOKEN = %d\", MAX_TOKEN);\n\t\t\t\treturn 0;\n\t\t\t} //end if\n\t\t\tc = *script->script_p;\n\t\t} //end while\n\t\ttoken->subtype |= TT_BINARY;\n\t} //end if\n#endif //BINARYNUMBERS\n\telse //decimal or octal integer or floating point number\n\t{\n\t\toctal = qfalse;\n\t\tdot = qfalse;\n\t\tif (*script->script_p == '0') octal = qtrue;\n\t\twhile(1)\n\t\t{\n\t\t\tc = *script->script_p;\n\t\t\tif (c == '.') dot = qtrue;\n\t\t\telse if (c == '8' || c == '9') octal = qfalse;\n\t\t\telse if (c < '0' || c > '9') break;\n\t\t\ttoken->string[len++] = *script->script_p++;\n\t\t\tif (len >= MAX_TOKEN - 1)\n\t\t\t{\n\t\t\t\tScriptError(script, \"number longer than MAX_TOKEN = %d\", MAX_TOKEN);\n\t\t\t\treturn 0;\n\t\t\t} //end if\n\t\t} //end while\n\t\tif (octal) token->subtype |= TT_OCTAL;\n\t\telse token->subtype |= TT_DECIMAL;\n\t\tif (dot) token->subtype |= TT_FLOAT;\n\t} //end else\n\tfor (i = 0; i < 2; i++)\n\t{\n\t\tc = *script->script_p;\n\t\t//check for a LONG number\n\t\tif ( (c == 'l' || c == 'L')\n\t\t     && !(token->subtype & TT_LONG))\n\t\t{\n\t\t\tscript->script_p++;\n\t\t\ttoken->subtype |= TT_LONG;\n\t\t} //end if\n\t\t//check for an UNSIGNED number\n\t\telse if ( (c == 'u' || c == 'U')\n\t\t\t  && !(token->subtype & (TT_UNSIGNED | TT_FLOAT)))\n\t\t{\n\t\t\tscript->script_p++;\n\t\t\ttoken->subtype |= TT_UNSIGNED;\n\t\t} //end if\n\t} //end for\n\ttoken->string[len] = '\\0';\n#ifdef NUMBERVALUE\n\tNumberValue(token->string, token->subtype, &token->intvalue, &token->floatvalue);\n#endif //NUMBERVALUE\n\tif (!(token->subtype & TT_FLOAT)) token->subtype |= TT_INTEGER;\n\treturn 1;\n} //end of the function PS_ReadNumber\n//============================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//============================================================================\nint PS_ReadLiteral(script_t *script, token_t *token)\n{\n\ttoken->type = TT_LITERAL;\n\t//first quote\n\ttoken->string[0] = *script->script_p++;\n\t//check for end of file\n\tif (!*script->script_p)\n\t{\n\t\tScriptError(script, \"end of file before trailing \\'\");\n\t\treturn 0;\n\t} //end if\n\t//if it is an escape character\n\tif (*script->script_p == '\\\\')\n\t{\n\t\tif (!PS_ReadEscapeCharacter(script, &token->string[1])) return 0;\n\t} //end if\n\telse\n\t{\n\t\ttoken->string[1] = *script->script_p++;\n\t} //end else\n\t//check for trailing quote\n\tif (*script->script_p != '\\'')\n\t{\n\t\tScriptWarning(script, \"too many characters in literal, ignored\");\n\t\twhile(*script->script_p &&\n\t\t\t\t*script->script_p != '\\'' &&\n\t\t\t\t*script->script_p != '\\n')\n\t\t{\n\t\t\tscript->script_p++;\n\t\t} //end while\n\t\tif (*script->script_p == '\\'') script->script_p++;\n\t} //end if\n\t//store the trailing quote\n\ttoken->string[2] = *script->script_p++;\n\t//store trailing zero to end the string\n\ttoken->string[3] = '\\0';\n\t//the sub type is the integer literal value\n\ttoken->subtype = token->string[1];\n\t//\n\treturn 1;\n} //end of the function PS_ReadLiteral\n//============================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//============================================================================\nint PS_ReadPunctuation(script_t *script, token_t *token)\n{\n\tint len;\n\tchar *p;\n\tpunctuation_t *punc;\n\n#ifdef PUNCTABLE\n\tfor (punc = script->punctuationtable[(unsigned int)*script->script_p]; punc; punc = punc->next)\n\t{\n#else\n\tint i;\n\n\tfor (i = 0; script->punctuations[i].p; i++)\n\t{\n\t\tpunc = &script->punctuations[i];\n#endif //PUNCTABLE\n\t\tp = punc->p;\n\t\tlen = strlen(p);\n\t\t//if the script contains at least as much characters as the punctuation\n\t\tif (script->script_p + len <= script->end_p)\n\t\t{\n\t\t\t//if the script contains the punctuation\n\t\t\tif (!strncmp(script->script_p, p, len))\n\t\t\t{\n\t\t\t\tstrncpy(token->string, p, MAX_TOKEN);\n\t\t\t\tscript->script_p += len;\n\t\t\t\ttoken->type = TT_PUNCTUATION;\n\t\t\t\t//sub type is the number of the punctuation\n\t\t\t\ttoken->subtype = punc->n;\n\t\t\t\treturn 1;\n\t\t\t} //end if\n\t\t} //end if\n\t} //end for\n\treturn 0;\n} //end of the function PS_ReadPunctuation\n//============================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//============================================================================\nint PS_ReadPrimitive(script_t *script, token_t *token)\n{\n\tint len;\n\n\tlen = 0;\n\twhile(*script->script_p > ' ' && *script->script_p != ';')\n\t{\n\t\tif (len >= MAX_TOKEN)\n\t\t{\n\t\t\tScriptError(script, \"primitive token longer than MAX_TOKEN = %d\", MAX_TOKEN);\n\t\t\treturn 0;\n\t\t} //end if\n\t\ttoken->string[len++] = *script->script_p++;\n\t} //end while\n\ttoken->string[len] = 0;\n\t//copy the token into the script structure\n\tCom_Memcpy(&script->token, token, sizeof(token_t));\n\t//primitive reading successfull\n\treturn 1;\n} //end of the function PS_ReadPrimitive\n//============================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//============================================================================\nint PS_ReadToken(script_t *script, token_t *token)\n{\n\t//if there is a token available (from UnreadToken)\n\tif (script->tokenavailable)\n\t{\n\t\tscript->tokenavailable = 0;\n\t\tCom_Memcpy(token, &script->token, sizeof(token_t));\n\t\treturn 1;\n\t} //end if\n\t//save script pointer\n\tscript->lastscript_p = script->script_p;\n\t//save line counter\n\tscript->lastline = script->line;\n\t//clear the token stuff\n\tCom_Memset(token, 0, sizeof(token_t));\n\t//start of the white space\n\tscript->whitespace_p = script->script_p;\n\ttoken->whitespace_p = script->script_p;\n\t//read unusefull stuff\n\tif (!PS_ReadWhiteSpace(script)) return 0;\n\t//end of the white space\n\tscript->endwhitespace_p = script->script_p;\n\ttoken->endwhitespace_p = script->script_p;\n\t//line the token is on\n\ttoken->line = script->line;\n\t//number of lines crossed before token\n\ttoken->linescrossed = script->line - script->lastline;\n\t//if there is a leading double quote\n\tif (*script->script_p == '\\\"')\n\t{\n\t\tif (!PS_ReadString(script, token, '\\\"')) return 0;\n\t} //end if\n\t//if an literal\n\telse if (*script->script_p == '\\'')\n\t{\n\t\t//if (!PS_ReadLiteral(script, token)) return 0;\n\t\tif (!PS_ReadString(script, token, '\\'')) return 0;\n\t} //end if\n\t//if there is a number\n\telse if ((*script->script_p >= '0' && *script->script_p <= '9') ||\n\t\t\t\t(*script->script_p == '.' &&\n\t\t\t\t(*(script->script_p + 1) >= '0' && *(script->script_p + 1) <= '9')))\n\t{\n\t\tif (!PS_ReadNumber(script, token)) return 0;\n\t} //end if\n\t//if this is a primitive script\n\telse if (script->flags & SCFL_PRIMITIVE)\n\t{\n\t\treturn PS_ReadPrimitive(script, token);\n\t} //end else if\n\t//if there is a name\n\telse if ((*script->script_p >= 'a' && *script->script_p <= 'z') ||\n\t\t(*script->script_p >= 'A' && *script->script_p <= 'Z') ||\n\t\t*script->script_p == '_')\n\t{\n\t\tif (!PS_ReadName(script, token)) return 0;\n\t} //end if\n\t//check for punctuations\n\telse if (!PS_ReadPunctuation(script, token))\n\t{\n\t\tScriptError(script, \"can't read token\");\n\t\treturn 0;\n\t} //end if\n\t//copy the token into the script structure\n\tCom_Memcpy(&script->token, token, sizeof(token_t));\n\t//successfully read a token\n\treturn 1;\n} //end of the function PS_ReadToken\n//============================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//============================================================================\nint PS_ExpectTokenString(script_t *script, char *string)\n{\n\ttoken_t token;\n\n\tif (!PS_ReadToken(script, &token))\n\t{\n\t\tScriptError(script, \"couldn't find expected %s\", string);\n\t\treturn 0;\n\t} //end if\n\n\tif (strcmp(token.string, string))\n\t{\n\t\tScriptError(script, \"expected %s, found %s\", string, token.string);\n\t\treturn 0;\n\t} //end if\n\treturn 1;\n} //end of the function PS_ExpectToken\n//============================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//============================================================================\nint PS_ExpectTokenType(script_t *script, int type, int subtype, token_t *token)\n{\n\tchar str[MAX_TOKEN];\n\n\tif (!PS_ReadToken(script, token))\n\t{\n\t\tScriptError(script, \"couldn't read expected token\");\n\t\treturn 0;\n\t} //end if\n\n\tif (token->type != type)\n\t{\n\t\tif (type == TT_STRING) strcpy(str, \"string\");\n\t\tif (type == TT_LITERAL) strcpy(str, \"literal\");\n\t\tif (type == TT_NUMBER) strcpy(str, \"number\");\n\t\tif (type == TT_NAME) strcpy(str, \"name\");\n\t\tif (type == TT_PUNCTUATION) strcpy(str, \"punctuation\");\n\t\tScriptError(script, \"expected a %s, found %s\", str, token->string);\n\t\treturn 0;\n\t} //end if\n\tif (token->type == TT_NUMBER)\n\t{\n\t\tif ((token->subtype & subtype) != subtype)\n\t\t{\n\t\t\tif (subtype & TT_DECIMAL) strcpy(str, \"decimal\");\n\t\t\tif (subtype & TT_HEX) strcpy(str, \"hex\");\n\t\t\tif (subtype & TT_OCTAL) strcpy(str, \"octal\");\n\t\t\tif (subtype & TT_BINARY) strcpy(str, \"binary\");\n\t\t\tif (subtype & TT_LONG) strcat(str, \" long\");\n\t\t\tif (subtype & TT_UNSIGNED) strcat(str, \" unsigned\");\n\t\t\tif (subtype & TT_FLOAT) strcat(str, \" float\");\n\t\t\tif (subtype & TT_INTEGER) strcat(str, \" integer\");\n\t\t\tScriptError(script, \"expected %s, found %s\", str, token->string);\n\t\t\treturn 0;\n\t\t} //end if\n\t} //end if\n\telse if (token->type == TT_PUNCTUATION)\n\t{\n\t\tif (subtype < 0)\n\t\t{\n\t\t\tScriptError(script, \"BUG: wrong punctuation subtype\");\n\t\t\treturn 0;\n\t\t} //end if\n\t\tif (token->subtype != subtype)\n\t\t{\n\t\t\tScriptError(script, \"expected %s, found %s\",\n\t\t\t\t\t\t\tscript->punctuations[subtype], token->string);\n\t\t\treturn 0;\n\t\t} //end if\n\t} //end else if\n\treturn 1;\n} //end of the function PS_ExpectTokenType\n//============================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//============================================================================\nint PS_ExpectAnyToken(script_t *script, token_t *token)\n{\n\tif (!PS_ReadToken(script, token))\n\t{\n\t\tScriptError(script, \"couldn't read expected token\");\n\t\treturn 0;\n\t} //end if\n\telse\n\t{\n\t\treturn 1;\n\t} //end else\n} //end of the function PS_ExpectAnyToken\n//============================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//============================================================================\nint PS_CheckTokenString(script_t *script, char *string)\n{\n\ttoken_t tok;\n\n\tif (!PS_ReadToken(script, &tok)) return 0;\n\t//if the token is available\n\tif (!strcmp(tok.string, string)) return 1;\n\t//token not available\n\tscript->script_p = script->lastscript_p;\n\treturn 0;\n} //end of the function PS_CheckTokenString\n//============================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//============================================================================\nint PS_CheckTokenType(script_t *script, int type, int subtype, token_t *token)\n{\n\ttoken_t tok;\n\n\tif (!PS_ReadToken(script, &tok)) return 0;\n\t//if the type matches\n\tif (tok.type == type &&\n\t\t\t(tok.subtype & subtype) == subtype)\n\t{\n\t\tCom_Memcpy(token, &tok, sizeof(token_t));\n\t\treturn 1;\n\t} //end if\n\t//token is not available\n\tscript->script_p = script->lastscript_p;\n\treturn 0;\n} //end of the function PS_CheckTokenType\n//============================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//============================================================================\nint PS_SkipUntilString(script_t *script, char *string)\n{\n\ttoken_t token;\n\n\twhile(PS_ReadToken(script, &token))\n\t{\n\t\tif (!strcmp(token.string, string)) return 1;\n\t} //end while\n\treturn 0;\n} //end of the function PS_SkipUntilString\n//============================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//============================================================================\nvoid PS_UnreadLastToken(script_t *script)\n{\n\tscript->tokenavailable = 1;\n} //end of the function UnreadLastToken\n//============================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//============================================================================\nvoid PS_UnreadToken(script_t *script, token_t *token)\n{\n\tCom_Memcpy(&script->token, token, sizeof(token_t));\n\tscript->tokenavailable = 1;\n} //end of the function UnreadToken\n//============================================================================\n// returns the next character of the read white space, returns NULL if none\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//============================================================================\nchar PS_NextWhiteSpaceChar(script_t *script)\n{\n\tif (script->whitespace_p != script->endwhitespace_p)\n\t{\n\t\treturn *script->whitespace_p++;\n\t} //end if\n\telse\n\t{\n\t\treturn 0;\n\t} //end else\n} //end of the function PS_NextWhiteSpaceChar\n//============================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//============================================================================\nvoid StripDoubleQuotes(char *string)\n{\n\tif (*string == '\\\"')\n\t{\n\t\tmemmove(string, string+1, strlen(string));\n\t} //end if\n\tif (string[strlen(string)-1] == '\\\"')\n\t{\n\t\tstring[strlen(string)-1] = '\\0';\n\t} //end if\n} //end of the function StripDoubleQuotes\n//============================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//============================================================================\nvoid StripSingleQuotes(char *string)\n{\n\tif (*string == '\\'')\n\t{\n\t\tmemmove(string, string+1, strlen(string));\n\t} //end if\n\tif (string[strlen(string)-1] == '\\'')\n\t{\n\t\tstring[strlen(string)-1] = '\\0';\n\t} //end if\n} //end of the function StripSingleQuotes\n//============================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//============================================================================\nfloat ReadSignedFloat(script_t *script)\n{\n\ttoken_t token;\n\tfloat sign = 1.0;\n\n\tPS_ExpectAnyToken(script, &token);\n\tif (!strcmp(token.string, \"-\"))\n\t{\n\t\tif(!PS_ExpectAnyToken(script, &token))\n\t\t{\n\t\t\tScriptError(script, \"Missing float value\\n\", token.string);\n\t\t\treturn 0;\n\t\t}\n\n\t\tsign = -1.0;\n\t}\n\n\tif (token.type != TT_NUMBER)\n\t{\n\t\tScriptError(script, \"expected float value, found %s\\n\", token.string);\n\t\treturn 0;\n\t}\n\n\treturn sign * token.floatvalue;\n} //end of the function ReadSignedFloat\n//============================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//============================================================================\nsigned long int ReadSignedInt(script_t *script)\n{\n\ttoken_t token;\n\tsigned long int sign = 1;\n\n\tPS_ExpectAnyToken(script, &token);\n\tif (!strcmp(token.string, \"-\"))\n\t{\n\t\tif(!PS_ExpectAnyToken(script, &token))\n\t\t{\n\t\t\tScriptError(script, \"Missing integer value\\n\", token.string);\n\t\t\treturn 0;\n\t\t}\n\n\t\tsign = -1;\n\t}\n\n\tif (token.type != TT_NUMBER || token.subtype == TT_FLOAT)\n\t{\n\t\tScriptError(script, \"expected integer value, found %s\\n\", token.string);\n\t\treturn 0;\n\t}\n\n\treturn sign * token.intvalue;\n} //end of the function ReadSignedInt\n//============================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//============================================================================\nvoid SetScriptFlags(script_t *script, int flags)\n{\n\tscript->flags = flags;\n} //end of the function SetScriptFlags\n//============================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//============================================================================\nint GetScriptFlags(script_t *script)\n{\n\treturn script->flags;\n} //end of the function GetScriptFlags\n//============================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//============================================================================\nvoid ResetScript(script_t *script)\n{\n\t//pointer in script buffer\n\tscript->script_p = script->buffer;\n\t//pointer in script buffer before reading token\n\tscript->lastscript_p = script->buffer;\n\t//begin of white space\n\tscript->whitespace_p = NULL;\n\t//end of white space\n\tscript->endwhitespace_p = NULL;\n\t//set if there's a token available in script->token\n\tscript->tokenavailable = 0;\n\t//\n\tscript->line = 1;\n\tscript->lastline = 1;\n\t//clear the saved token\n\tCom_Memset(&script->token, 0, sizeof(token_t));\n} //end of the function ResetScript\n//============================================================================\n// returns true if at the end of the script\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//============================================================================\nint EndOfScript(script_t *script)\n{\n\treturn script->script_p >= script->end_p;\n} //end of the function EndOfScript\n//============================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//============================================================================\nint NumLinesCrossed(script_t *script)\n{\n\treturn script->line - script->lastline;\n} //end of the function NumLinesCrossed\n//============================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//============================================================================\nint ScriptSkipTo(script_t *script, char *value)\n{\n\tint len;\n\tchar firstchar;\n\n\tfirstchar = *value;\n\tlen = strlen(value);\n\tdo\n\t{\n\t\tif (!PS_ReadWhiteSpace(script)) return 0;\n\t\tif (*script->script_p == firstchar)\n\t\t{\n\t\t\tif (!strncmp(script->script_p, value, len))\n\t\t\t{\n\t\t\t\treturn 1;\n\t\t\t} //end if\n\t\t} //end if\n\t\tscript->script_p++;\n\t} while(1);\n} //end of the function ScriptSkipTo\n#ifndef BOTLIB\n//============================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//============================================================================\nint FileLength(FILE *fp)\n{\n\tint pos;\n\tint end;\n\n\tpos = ftell(fp);\n\tfseek(fp, 0, SEEK_END);\n\tend = ftell(fp);\n\tfseek(fp, pos, SEEK_SET);\n\n\treturn end;\n} //end of the function FileLength\n#endif\n//============================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//============================================================================\nscript_t *LoadScriptFile(const char *filename)\n{\n#ifdef BOTLIB\n\tfileHandle_t fp;\n\tchar pathname[MAX_QPATH];\n#else\n\tFILE *fp;\n#endif\n\tint length;\n\tvoid *buffer;\n\tscript_t *script;\n\n#ifdef BOTLIB\n\tif (strlen(basefolder))\n\t\tCom_sprintf(pathname, sizeof(pathname), \"%s/%s\", basefolder, filename);\n\telse\n\t\tCom_sprintf(pathname, sizeof(pathname), \"%s\", filename);\n\tlength = botimport.FS_FOpenFile( pathname, &fp, FS_READ );\n\tif (!fp) return NULL;\n#else\n\tfp = fopen(filename, \"rb\");\n\tif (!fp) return NULL;\n\n\tlength = FileLength(fp);\n#endif\n\n\tbuffer = GetClearedMemory(sizeof(script_t) + length + 1);\n\tscript = (script_t *) buffer;\n\tCom_Memset(script, 0, sizeof(script_t));\n\tstrcpy(script->filename, filename);\n\tscript->buffer = (char *) buffer + sizeof(script_t);\n\tscript->buffer[length] = 0;\n\tscript->length = length;\n\t//pointer in script buffer\n\tscript->script_p = script->buffer;\n\t//pointer in script buffer before reading token\n\tscript->lastscript_p = script->buffer;\n\t//pointer to end of script buffer\n\tscript->end_p = &script->buffer[length];\n\t//set if there's a token available in script->token\n\tscript->tokenavailable = 0;\n\t//\n\tscript->line = 1;\n\tscript->lastline = 1;\n\t//\n\tSetScriptPunctuations(script, NULL);\n\t//\n#ifdef BOTLIB\n\tbotimport.FS_Read(script->buffer, length, fp);\n\tbotimport.FS_FCloseFile(fp);\n#else\n\tif (fread(script->buffer, length, 1, fp) != 1)\n\t{\n\t\tFreeMemory(buffer);\n\t\tscript = NULL;\n\t} //end if\n\tfclose(fp);\n#endif\n\n\treturn script;\n} //end of the function LoadScriptFile\n//============================================================================\n//\n// Parameter:\t\t\t-\n// Returns:\t\t\t\t-\n// Changes Globals:\t\t-\n//============================================================================\nscript_t *LoadScriptMemory(char *ptr, int length, char *name)\n{\n\tvoid *buffer;\n\tscript_t *script;\n\n\tbuffer = GetClearedMemory(sizeof(script_t) + length + 1);\n\tscript = (script_t *) buffer;\n\tCom_Memset(script, 0, sizeof(script_t));\n\tstrcpy(script->filename, name);\n\tscript->buffer = (char *) buffer + sizeof(script_t);\n\tscript->buffer[length] = 0;\n\tscript->length = length;\n\t//pointer in script buffer\n\tscript->script_p = script->buffer;\n\t//pointer in script buffer before reading token\n\tscript->lastscript_p = script->buffer;\n\t//pointer to end of script buffer\n\tscript->end_p = &script->buffer[length];\n\t//set if there's a token available in script->token\n\tscript->tokenavailable = 0;\n\t//\n\tscript->line = 1;\n\tscript->lastline = 1;\n\t//\n\tSetScriptPunctuations(script, NULL);\n\t//\n\tCom_Memcpy(script->buffer, ptr, length);\n\t//\n\treturn script;\n} //end of the function LoadScriptMemory\n//============================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//============================================================================\nvoid FreeScript(script_t *script)\n{\n#ifdef PUNCTABLE\n\tif (script->punctuationtable) FreeMemory(script->punctuationtable);\n#endif //PUNCTABLE\n\tFreeMemory(script);\n} //end of the function FreeScript\n//============================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//============================================================================\nvoid PS_SetBaseFolder(char *path)\n{\n#ifdef BSPC\n\tsprintf(basefolder, path);\n#else\n\tCom_sprintf(basefolder, sizeof(basefolder), \"%s\", path);\n#endif\n} //end of the function PS_SetBaseFolder\n"
  },
  {
    "path": "plugins/quake3/botlib/l_script.h",
    "content": "/*\n===========================================================================\nCopyright (C) 1999-2005 Id Software, Inc.\n\nThis file is part of Quake III Arena source code.\n\nQuake III Arena source code is free software; you can redistribute it\nand/or modify it under the terms of the GNU General Public License as\npublished by the Free Software Foundation; either version 2 of the License,\nor (at your option) any later version.\n\nQuake III Arena source code is distributed in the hope that it will be\nuseful, but WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Quake III Arena source code; if not, write to the Free Software\nFoundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n===========================================================================\n*/\n\n/*****************************************************************************\n * name:\t\tl_script.h\n *\n * desc:\t\tlexicographical parser\n *\n * $Archive: /source/code/botlib/l_script.h $\n *\n *****************************************************************************/\n\n//undef if binary numbers of the form 0b... or 0B... are not allowed\n#define BINARYNUMBERS\n//undef if not using the token.intvalue and token.floatvalue\n#define NUMBERVALUE\n//use dollar sign also as punctuation\n#define DOLLAR\n\n//maximum token length\n#define MAX_TOKEN\t\t\t\t\t1024\n\n#if defined(BSPC) && !defined(QDECL)\n#define QDECL\n#endif\n\n\n//script flags\n#define SCFL_NOERRORS\t\t\t\t0x0001\n#define SCFL_NOWARNINGS\t\t\t\t0x0002\n#define SCFL_NOSTRINGWHITESPACES\t0x0004\n#define SCFL_NOSTRINGESCAPECHARS\t0x0008\n#define SCFL_PRIMITIVE\t\t\t\t0x0010\n#define SCFL_NOBINARYNUMBERS\t\t0x0020\n#define SCFL_NONUMBERVALUES\t\t0x0040\n\n//token types\n#define TT_STRING\t\t\t\t\t\t1\t\t\t// string\n#define TT_LITERAL\t\t\t\t\t2\t\t\t// literal\n#define TT_NUMBER\t\t\t\t\t\t3\t\t\t// number\n#define TT_NAME\t\t\t\t\t\t4\t\t\t// name\n#define TT_PUNCTUATION\t\t\t\t5\t\t\t// punctuation\n\n//string sub type\n//---------------\n//\t\tthe length of the string\n//literal sub type\n//----------------\n//\t\tthe ASCII code of the literal\n//number sub type\n//---------------\n#define TT_DECIMAL\t\t\t\t\t0x0008\t// decimal number\n#define TT_HEX\t\t\t\t\t\t\t0x0100\t// hexadecimal number\n#define TT_OCTAL\t\t\t\t\t\t0x0200\t// octal number\n#ifdef BINARYNUMBERS\n#define TT_BINARY\t\t\t\t\t\t0x0400\t// binary number\n#endif //BINARYNUMBERS\n#define TT_FLOAT\t\t\t\t\t\t0x0800\t// floating point number\n#define TT_INTEGER\t\t\t\t\t0x1000\t// integer number\n#define TT_LONG\t\t\t\t\t\t0x2000\t// long number\n#define TT_UNSIGNED\t\t\t\t\t0x4000\t// unsigned number\n//punctuation sub type\n//--------------------\n#define P_RSHIFT_ASSIGN\t\t\t\t1\n#define P_LSHIFT_ASSIGN\t\t\t\t2\n#define P_PARMS\t\t\t\t\t\t3\n#define P_PRECOMPMERGE\t\t\t\t4\n\n#define P_LOGIC_AND\t\t\t\t\t5\n#define P_LOGIC_OR\t\t\t\t\t6\n#define P_LOGIC_GEQ\t\t\t\t\t7\n#define P_LOGIC_LEQ\t\t\t\t\t8\n#define P_LOGIC_EQ\t\t\t\t\t9\n#define P_LOGIC_UNEQ\t\t\t\t\t10\n\n#define P_MUL_ASSIGN\t\t\t\t\t11\n#define P_DIV_ASSIGN\t\t\t\t\t12\n#define P_MOD_ASSIGN\t\t\t\t\t13\n#define P_ADD_ASSIGN\t\t\t\t\t14\n#define P_SUB_ASSIGN\t\t\t\t\t15\n#define P_INC\t\t\t\t\t\t\t16\n#define P_DEC\t\t\t\t\t\t\t17\n\n#define P_BIN_AND_ASSIGN\t\t\t18\n#define P_BIN_OR_ASSIGN\t\t\t\t19\n#define P_BIN_XOR_ASSIGN\t\t\t20\n#define P_RSHIFT\t\t\t\t\t\t21\n#define P_LSHIFT\t\t\t\t\t\t22\n\n#define P_POINTERREF\t\t\t\t\t23\n#define P_CPP1\t\t\t\t\t\t\t24\n#define P_CPP2\t\t\t\t\t\t\t25\n#define P_MUL\t\t\t\t\t\t\t26\n#define P_DIV\t\t\t\t\t\t\t27\n#define P_MOD\t\t\t\t\t\t\t28\n#define P_ADD\t\t\t\t\t\t\t29\n#define P_SUB\t\t\t\t\t\t\t30\n#define P_ASSIGN\t\t\t\t\t\t31\n\n#define P_BIN_AND\t\t\t\t\t\t32\n#define P_BIN_OR\t\t\t\t\t\t33\n#define P_BIN_XOR\t\t\t\t\t\t34\n#define P_BIN_NOT\t\t\t\t\t\t35\n\n#define P_LOGIC_NOT\t\t\t\t\t36\n#define P_LOGIC_GREATER\t\t\t\t37\n#define P_LOGIC_LESS\t\t\t\t\t38\n\n#define P_REF\t\t\t\t\t\t\t39\n#define P_COMMA\t\t\t\t\t\t40\n#define P_SEMICOLON\t\t\t\t\t41\n#define P_COLON\t\t\t\t\t\t42\n#define P_QUESTIONMARK\t\t\t\t43\n\n#define P_PARENTHESESOPEN\t\t\t44\n#define P_PARENTHESESCLOSE\t\t\t45\n#define P_BRACEOPEN\t\t\t\t\t46\n#define P_BRACECLOSE\t\t\t\t\t47\n#define P_SQBRACKETOPEN\t\t\t\t48\n#define P_SQBRACKETCLOSE\t\t\t49\n#define P_BACKSLASH\t\t\t\t\t50\n\n#define P_PRECOMP\t\t\t\t\t\t51\n#define P_DOLLAR\t\t\t\t\t\t52\n//name sub type\n//-------------\n//\t\tthe length of the name\n\n//punctuation\ntypedef struct punctuation_s\n{\n\tchar *p;\t\t\t\t\t\t//punctuation character(s)\n\tint n;\t\t\t\t\t\t\t//punctuation indication\n\tstruct punctuation_s *next;\t\t//next punctuation\n} punctuation_t;\n\n//token\ntypedef struct token_s\n{\n\tchar string[MAX_TOKEN];\t\t\t//available token\n\tint type;\t\t\t\t\t\t//last read token type\n\tint subtype;\t\t\t\t\t//last read token sub type\n#ifdef NUMBERVALUE\n\tunsigned long int intvalue;\t//integer value\n\tfloat floatvalue;\t\t\t//floating point value\n#endif //NUMBERVALUE\n\tchar *whitespace_p;\t\t\t\t//start of white space before token\n\tchar *endwhitespace_p;\t\t\t//start of white space before token\n\tint line;\t\t\t\t\t\t//line the token was on\n\tint linescrossed;\t\t\t\t//lines crossed in white space\n\tstruct token_s *next;\t\t\t//next token in chain\n} token_t;\n\n//script file\ntypedef struct script_s\n{\n\tchar filename[1024];\t\t\t//file name of the script\n\tchar *buffer;\t\t\t\t\t//buffer containing the script\n\tchar *script_p;\t\t\t\t\t//current pointer in the script\n\tchar *end_p;\t\t\t\t\t//pointer to the end of the script\n\tchar *lastscript_p;\t\t\t\t//script pointer before reading token\n\tchar *whitespace_p;\t\t\t\t//begin of the white space\n\tchar *endwhitespace_p;\t\t\t//end of the white space\n\tint length;\t\t\t\t\t\t//length of the script in bytes\n\tint line;\t\t\t\t\t\t//current line in script\n\tint lastline;\t\t\t\t\t//line before reading token\n\tint tokenavailable;\t\t\t\t//set by UnreadLastToken\n\tint flags;\t\t\t\t\t\t//several script flags\n\tpunctuation_t *punctuations;\t//the punctuations used in the script\n\tpunctuation_t **punctuationtable;\n\ttoken_t token;\t\t\t\t\t//available token\n\tstruct script_s *next;\t\t\t//next script in a chain\n} script_t;\n\n//read a token from the script\nint PS_ReadToken(script_t *script, token_t *token);\n//expect a certain token\nint PS_ExpectTokenString(script_t *script, char *string);\n//expect a certain token type\nint PS_ExpectTokenType(script_t *script, int type, int subtype, token_t *token);\n//expect a token\nint PS_ExpectAnyToken(script_t *script, token_t *token);\n//returns true when the token is available\nint PS_CheckTokenString(script_t *script, char *string);\n//returns true an reads the token when a token with the given type is available\nint PS_CheckTokenType(script_t *script, int type, int subtype, token_t *token);\n//skip tokens until the given token string is read\nint PS_SkipUntilString(script_t *script, char *string);\n//unread the last token read from the script\nvoid PS_UnreadLastToken(script_t *script);\n//unread the given token\nvoid PS_UnreadToken(script_t *script, token_t *token);\n//returns the next character of the read white space, returns NULL if none\nchar PS_NextWhiteSpaceChar(script_t *script);\n//remove any leading and trailing double quotes from the token\nvoid StripDoubleQuotes(char *string);\n//remove any leading and trailing single quotes from the token\nvoid StripSingleQuotes(char *string);\n//read a possible signed integer\nsigned long int ReadSignedInt(script_t *script);\n//read a possible signed floating point number\nfloat ReadSignedFloat(script_t *script);\n//set an array with punctuations, NULL restores default C/C++ set\nvoid SetScriptPunctuations(script_t *script, punctuation_t *p);\n//set script flags\nvoid SetScriptFlags(script_t *script, int flags);\n//get script flags\nint GetScriptFlags(script_t *script);\n//reset a script\nvoid ResetScript(script_t *script);\n//returns true if at the end of the script\nint EndOfScript(script_t *script);\n//returns a pointer to the punctuation with the given number\nchar *PunctuationFromNum(script_t *script, int num);\n//load a script from the given file at the given offset with the given length\nscript_t *LoadScriptFile(const char *filename);\n//load a script from the given memory with the given length\nscript_t *LoadScriptMemory(char *ptr, int length, char *name);\n//free a script\nvoid FreeScript(script_t *script);\n//set the base folder to load files from\nvoid PS_SetBaseFolder(char *path);\n//print a script error with filename and line number\nvoid QDECL ScriptError(script_t *script, char *str, ...);\n//print a script warning with filename and line number\nvoid QDECL ScriptWarning(script_t *script, char *str, ...);\n\n\n"
  },
  {
    "path": "plugins/quake3/botlib/l_struct.c",
    "content": "/*\n===========================================================================\nCopyright (C) 1999-2005 Id Software, Inc.\n\nThis file is part of Quake III Arena source code.\n\nQuake III Arena source code is free software; you can redistribute it\nand/or modify it under the terms of the GNU General Public License as\npublished by the Free Software Foundation; either version 2 of the License,\nor (at your option) any later version.\n\nQuake III Arena source code is distributed in the hope that it will be\nuseful, but WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Quake III Arena source code; if not, write to the Free Software\nFoundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n===========================================================================\n*/\n\n/*****************************************************************************\n * name:\t\tl_struct.c\n *\n * desc:\t\tstructure reading / writing\n *\n * $Archive: /MissionPack/CODE/botlib/l_struct.c $\n *\n *****************************************************************************/\n\n#ifdef BOTLIB\n#include \"q_shared.h\"\n#include \"botlib.h\"\t\t\t\t//for the include of be_interface.h\n#include \"l_script.h\"\n#include \"l_precomp.h\"\n#include \"l_struct.h\"\n#include \"l_utils.h\"\n#include \"be_interface.h\"\n#endif //BOTLIB\n\n#ifdef BSPC\n//include files for usage in the BSP Converter\n#include \"../bspc/qbsp.h\"\n#include \"../bspc/l_log.h\"\n#include \"../bspc/l_mem.h\"\n#include \"l_precomp.h\"\n#include \"l_struct.h\"\n\n#define qtrue\ttrue\n#define qfalse\tfalse\n#endif //BSPC\n\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nfielddef_t *FindField(fielddef_t *defs, char *name)\n{\n\tint i;\n\n\tfor (i = 0; defs[i].name; i++)\n\t{\n\t\tif (!strcmp(defs[i].name, name)) return &defs[i];\n\t} //end for\n\treturn NULL;\n} //end of the function FindField\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nqboolean ReadNumber(source_t *source, fielddef_t *fd, void *p)\n{\n\ttoken_t token;\n\tint negative = qfalse;\n\tlong int intval, intmin = 0, intmax = 0;\n\tdouble floatval;\n\n\tif (!PC_ExpectAnyToken(source, &token)) return 0;\n\n\t//check for minus sign\n\tif (token.type == TT_PUNCTUATION)\n\t{\n\t\tif (fd->type & FT_UNSIGNED)\n\t\t{\n\t\t\tSourceError(source, \"expected unsigned value, found %s\", token.string);\n\t\t\treturn 0;\n\t\t} //end if\n\t\t//if not a minus sign\n\t\tif (strcmp(token.string, \"-\"))\n\t\t{\n\t\t\tSourceError(source, \"unexpected punctuation %s\", token.string);\n\t\t\treturn 0;\n\t\t} //end if\n\t\tnegative = qtrue;\n\t\t//read the number\n\t\tif (!PC_ExpectAnyToken(source, &token)) return 0;\n\t} //end if\n\t//check if it is a number\n\tif (token.type != TT_NUMBER)\n\t{\n\t\tSourceError(source, \"expected number, found %s\", token.string);\n\t\treturn 0;\n\t} //end if\n\t//check for a float value\n\tif (token.subtype & TT_FLOAT)\n\t{\n\t\tif ((fd->type & FT_TYPE) != FT_FLOAT)\n\t\t{\n\t\t\tSourceError(source, \"unexpected float\");\n\t\t\treturn 0;\n\t\t} //end if\n\t\tfloatval = token.floatvalue;\n\t\tif (negative) floatval = -floatval;\n\t\tif (fd->type & FT_BOUNDED)\n\t\t{\n\t\t\tif (floatval < fd->floatmin || floatval > fd->floatmax)\n\t\t\t{\n\t\t\t\tSourceError(source, \"float out of range [%f, %f]\", fd->floatmin, fd->floatmax);\n\t\t\t\treturn 0;\n\t\t\t} //end if\n\t\t} //end if\n\t\t*(float *) p = (float) floatval;\n\t\treturn 1;\n\t} //end if\n\t//\n\tintval = token.intvalue;\n\tif (negative) intval = -intval;\n\t//check bounds\n\tif ((fd->type & FT_TYPE) == FT_CHAR)\n\t{\n\t\tif (fd->type & FT_UNSIGNED) {intmin = 0; intmax = 255;}\n\t\telse {intmin = -128; intmax = 127;}\n\t} //end if\n\tif ((fd->type & FT_TYPE) == FT_INT)\n\t{\n\t\tif (fd->type & FT_UNSIGNED) {intmin = 0; intmax = 65535;}\n\t\telse {intmin = -32768; intmax = 32767;}\n\t} //end else if\n\tif ((fd->type & FT_TYPE) == FT_CHAR || (fd->type & FT_TYPE) == FT_INT)\n\t{\n\t\tif (fd->type & FT_BOUNDED)\n\t\t{\n\t\t\tintmin = Maximum(intmin, fd->floatmin);\n\t\t\tintmax = Minimum(intmax, fd->floatmax);\n\t\t} //end if\n\t\tif (intval < intmin || intval > intmax)\n\t\t{\n\t\t\tSourceError(source, \"value %d out of range [%d, %d]\", intval, intmin, intmax);\n\t\t\treturn 0;\n\t\t} //end if\n\t} //end if\n\telse if ((fd->type & FT_TYPE) == FT_FLOAT)\n\t{\n\t\tif (fd->type & FT_BOUNDED)\n\t\t{\n\t\t\tif (intval < fd->floatmin || intval > fd->floatmax)\n\t\t\t{\n\t\t\t\tSourceError(source, \"value %d out of range [%f, %f]\", intval, fd->floatmin, fd->floatmax);\n\t\t\t\treturn 0;\n\t\t\t} //end if\n\t\t} //end if\n\t} //end else if\n\t//store the value\n\tif ((fd->type & FT_TYPE) == FT_CHAR)\n\t{\n\t\tif (fd->type & FT_UNSIGNED) *(unsigned char *) p = (unsigned char) intval;\n\t\telse *(char *) p = (char) intval;\n\t} //end if\n\telse if ((fd->type & FT_TYPE) == FT_INT)\n\t{\n\t\tif (fd->type & FT_UNSIGNED) *(unsigned int *) p = (unsigned int) intval;\n\t\telse *(int *) p = (int) intval;\n\t} //end else\n\telse if ((fd->type & FT_TYPE) == FT_FLOAT)\n\t{\n\t\t*(float *) p = (float) intval;\n\t} //end else\n\treturn 1;\n} //end of the function ReadNumber\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nqboolean ReadChar(source_t *source, fielddef_t *fd, void *p)\n{\n\ttoken_t token;\n\n\tif (!PC_ExpectAnyToken(source, &token)) return 0;\n\n\t//take literals into account\n\tif (token.type == TT_LITERAL)\n\t{\n\t\tStripSingleQuotes(token.string);\n\t\t*(char *) p = token.string[0];\n\t} //end if\n\telse\n\t{\n\t\tPC_UnreadLastToken(source);\n\t\tif (!ReadNumber(source, fd, p)) return 0;\n\t} //end if\n\treturn 1;\n} //end of the function ReadChar\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint ReadString(source_t *source, fielddef_t *fd, void *p)\n{\n\ttoken_t token;\n\tsize_t s;\n\n\tif (!PC_ExpectTokenType(source, TT_STRING, 0, &token)) return 0;\n\t//remove the double quotes\n\tStripDoubleQuotes(token.string);\n\t//copy the string\n\ts = strlen(token.string);\n\tif (s >= MAX_STRINGFIELD)\n\t{\n\t\ts = MAX_STRINGFIELD-1;\n\t\tmemcpy(p, token.string, s);\n\t\t((char*)p)[s] = 0;\n\t\treturn 0;\t//truncated\n\t}\n\tstrcpy((char*)p, token.string);\n\treturn 1;\n} //end of the function ReadString\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint ReadStructure(source_t *source, structdef_t *def, char *structure)\n{\n\ttoken_t token;\n\tfielddef_t *fd;\n\tvoid *p;\n\tint num;\n\n\tif (!PC_ExpectTokenString(source, \"{\")) return 0;\n\twhile(1)\n\t{\n\t\tif (!PC_ExpectAnyToken(source, &token)) return qfalse;\n\t\t//if end of structure\n\t\tif (!strcmp(token.string, \"}\")) break;\n\t\t//find the field with the name\n\t\tfd = FindField(def->fields, token.string);\n\t\tif (!fd)\n\t\t{\n\t\t\tSourceError(source, \"unknown structure field %s\", token.string);\n\t\t\treturn qfalse;\n\t\t} //end if\n\t\tif (fd->type & FT_ARRAY)\n\t\t{\n\t\t\tnum = fd->maxarray;\n\t\t\tif (!PC_ExpectTokenString(source, \"{\")) return qfalse;\n\t\t} //end if\n\t\telse\n\t\t{\n\t\t\tnum = 1;\n\t\t} //end else\n\t\tp = (void *)(structure + fd->offset);\n\t\twhile (num-- > 0)\n\t\t{\n\t\t\tif (fd->type & FT_ARRAY)\n\t\t\t{\n\t\t\t\tif (PC_CheckTokenString(source, \"}\")) break;\n\t\t\t} //end if\n\t\t\tswitch(fd->type & FT_TYPE)\n\t\t\t{\n\t\t\t\tcase FT_CHAR:\n\t\t\t\t{\n\t\t\t\t\tif (!ReadChar(source, fd, p)) return qfalse;\n\t\t\t\t\tp = (char *) p + sizeof(char);\n\t\t\t\t\tbreak;\n\t\t\t\t} //end case\n\t\t\t\tcase FT_INT:\n\t\t\t\t{\n\t\t\t\t\tif (!ReadNumber(source, fd, p)) return qfalse;\n\t\t\t\t\tp = (char *) p + sizeof(int);\n\t\t\t\t\tbreak;\n\t\t\t\t} //end case\n\t\t\t\tcase FT_FLOAT:\n\t\t\t\t{\n\t\t\t\t\tif (!ReadNumber(source, fd, p)) return qfalse;\n\t\t\t\t\tp = (char *) p + sizeof(float);\n\t\t\t\t\tbreak;\n\t\t\t\t} //end case\n\t\t\t\tcase FT_STRING:\n\t\t\t\t{\n\t\t\t\t\tif (!ReadString(source, fd, p)) return qfalse;\n\t\t\t\t\tp = (char *) p + MAX_STRINGFIELD;\n\t\t\t\t\tbreak;\n\t\t\t\t} //end case\n\t\t\t\tcase FT_STRUCT:\n\t\t\t\t{\n\t\t\t\t\tif (!fd->substruct)\n\t\t\t\t\t{\n\t\t\t\t\t\tSourceError(source, \"BUG: no sub structure defined\");\n\t\t\t\t\t\treturn qfalse;\n\t\t\t\t\t} //end if\n\t\t\t\t\tReadStructure(source, fd->substruct, (char *) p);\n\t\t\t\t\tp = (char *) p + fd->substruct->size;\n\t\t\t\t\tbreak;\n\t\t\t\t} //end case\n\t\t\t} //end switch\n\t\t\tif (fd->type & FT_ARRAY)\n\t\t\t{\n\t\t\t\tif (!PC_ExpectAnyToken(source, &token)) return qfalse;\n\t\t\t\tif (!strcmp(token.string, \"}\")) break;\n\t\t\t\tif (strcmp(token.string, \",\"))\n\t\t\t\t{\n\t\t\t\t\tSourceError(source, \"expected a comma, found %s\", token.string);\n\t\t\t\t\treturn qfalse;\n\t\t\t\t} //end if\n\t\t\t} //end if\n\t\t} //end while\n\t} //end while\n\treturn qtrue;\n} //end of the function ReadStructure\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint WriteIndent(FILE *fp, int indent)\n{\n\twhile(indent-- > 0)\n\t{\n\t\tif (fprintf(fp, \"\\t\") < 0) return qfalse;\n\t} //end while\n\treturn qtrue;\n} //end of the function WriteIndent\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint WriteFloat(FILE *fp, float value)\n{\n\tchar buf[128];\n\tint l;\n\n\tCom_sprintf(buf, sizeof(buf), \"%f\", value);\n\tl = strlen(buf);\n\t//strip any trailing zeros\n\twhile(l-- > 1)\n\t{\n\t\tif (buf[l] != '0' && buf[l] != '.') break;\n\t\tif (buf[l] == '.')\n\t\t{\n\t\t\tbuf[l] = 0;\n\t\t\tbreak;\n\t\t} //end if\n\t\tbuf[l] = 0;\n\t} //end while\n\t//write the float to file\n\tif (fprintf(fp, \"%s\", buf) < 0) return 0;\n\treturn 1;\n} //end of the function WriteFloat\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint WriteStructWithIndent(FILE *fp, structdef_t *def, char *structure, int indent)\n{\n\tint i, num;\n\tvoid *p;\n\tfielddef_t *fd;\n\n\tif (!WriteIndent(fp, indent)) return qfalse;\n\tif (fprintf(fp, \"{\\r\\n\") < 0) return qfalse;\n\n\tindent++;\n\tfor (i = 0; def->fields[i].name; i++)\n\t{\n\t\tfd = &def->fields[i];\n\t\tif (!WriteIndent(fp, indent)) return qfalse;\n\t\tif (fprintf(fp, \"%s\\t\", fd->name) < 0) return qfalse;\n\t\tp = (void *)(structure + fd->offset);\n\t\tif (fd->type & FT_ARRAY)\n\t\t{\n\t\t\tnum = fd->maxarray;\n\t\t\tif (fprintf(fp, \"{\") < 0) return qfalse;\n\t\t} //end if\n\t\telse\n\t\t{\n\t\t\tnum = 1;\n\t\t} //end else\n\t\twhile(num-- > 0)\n\t\t{\n\t\t\tswitch(fd->type & FT_TYPE)\n\t\t\t{\n\t\t\t\tcase FT_CHAR:\n\t\t\t\t{\n\t\t\t\t\tif (fprintf(fp, \"%d\", *(char *) p) < 0) return qfalse;\n\t\t\t\t\tp = (char *) p + sizeof(char);\n\t\t\t\t\tbreak;\n\t\t\t\t} //end case\n\t\t\t\tcase FT_INT:\n\t\t\t\t{\n\t\t\t\t\tif (fprintf(fp, \"%d\", *(int *) p) < 0) return qfalse;\n\t\t\t\t\tp = (char *) p + sizeof(int);\n\t\t\t\t\tbreak;\n\t\t\t\t} //end case\n\t\t\t\tcase FT_FLOAT:\n\t\t\t\t{\n\t\t\t\t\tif (!WriteFloat(fp, *(float *)p)) return qfalse;\n\t\t\t\t\tp = (char *) p + sizeof(float);\n\t\t\t\t\tbreak;\n\t\t\t\t} //end case\n\t\t\t\tcase FT_STRING:\n\t\t\t\t{\n\t\t\t\t\tif (fprintf(fp, \"\\\"%s\\\"\", (char *) p) < 0) return qfalse;\n\t\t\t\t\tp = (char *) p + MAX_STRINGFIELD;\n\t\t\t\t\tbreak;\n\t\t\t\t} //end case\n\t\t\t\tcase FT_STRUCT:\n\t\t\t\t{\n\t\t\t\t\tif (!WriteStructWithIndent(fp, fd->substruct, structure, indent)) return qfalse;\n\t\t\t\t\tp = (char *) p + fd->substruct->size;\n\t\t\t\t\tbreak;\n\t\t\t\t} //end case\n\t\t\t} //end switch\n\t\t\tif (fd->type & FT_ARRAY)\n\t\t\t{\n\t\t\t\tif (num > 0)\n\t\t\t\t{\n\t\t\t\t\tif (fprintf(fp, \",\") < 0) return qfalse;\n\t\t\t\t} //end if\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tif (fprintf(fp, \"}\") < 0) return qfalse;\n\t\t\t\t} //end else\n\t\t\t} //end if\n\t\t} //end while\n\t\tif (fprintf(fp, \"\\r\\n\") < 0) return qfalse;\n\t} //end for\n\tindent--;\n\n\tif (!WriteIndent(fp, indent)) return qfalse;\n\tif (fprintf(fp, \"}\\r\\n\") < 0) return qfalse;\n\treturn qtrue;\n} //end of the function WriteStructWithIndent\n//===========================================================================\n//\n// Parameter:\t\t\t\t-\n// Returns:\t\t\t\t\t-\n// Changes Globals:\t\t-\n//===========================================================================\nint WriteStructure(FILE *fp, structdef_t *def, char *structure)\n{\n\treturn WriteStructWithIndent(fp, def, structure, 0);\n} //end of the function WriteStructure\n\n"
  },
  {
    "path": "plugins/quake3/botlib/l_struct.h",
    "content": "/*\n===========================================================================\nCopyright (C) 1999-2005 Id Software, Inc.\n\nThis file is part of Quake III Arena source code.\n\nQuake III Arena source code is free software; you can redistribute it\nand/or modify it under the terms of the GNU General Public License as\npublished by the Free Software Foundation; either version 2 of the License,\nor (at your option) any later version.\n\nQuake III Arena source code is distributed in the hope that it will be\nuseful, but WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Quake III Arena source code; if not, write to the Free Software\nFoundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n===========================================================================\n*/\n\n/*****************************************************************************\n * name:\t\tl_struct.h\n *\n * desc:\t\tstructure reading/writing\n *\n * $Archive: /source/code/botlib/l_struct.h $\n *\n *****************************************************************************/\n\n\n#define MAX_STRINGFIELD\t\t\t\t80\n//field types\n#define FT_CHAR\t\t\t\t\t\t1\t\t\t// char\n#define FT_INT\t\t\t\t\t\t\t2\t\t\t// int\n#define FT_FLOAT\t\t\t\t\t\t3\t\t\t// float\n#define FT_STRING\t\t\t\t\t\t4\t\t\t// char [MAX_STRINGFIELD]\n#define FT_STRUCT\t\t\t\t\t\t6\t\t\t// struct (sub structure)\n//type only mask\n#define FT_TYPE\t\t\t\t\t\t0x00FF\t// only type, clear subtype\n//sub types\n#define FT_ARRAY\t\t\t\t\t\t0x0100\t// array of type\n#define FT_BOUNDED\t\t\t\t\t0x0200\t// bounded value\n#define FT_UNSIGNED\t\t\t\t\t0x0400\n\n//structure field definition\ntypedef struct fielddef_s\n{\n\tchar *name;\t\t\t\t\t\t\t\t\t\t//name of the field\n\tint offset;\t\t\t\t\t\t\t\t\t\t//offset in the structure\n\tint type;\t\t\t\t\t\t\t\t\t\t//type of the field\n\t//type specific fields\n\tint maxarray;\t\t\t\t\t\t\t\t\t//maximum array size\n\tfloat floatmin, floatmax;\t\t\t\t\t//float min and max\n\tstruct structdef_s *substruct;\t\t\t//sub structure\n} fielddef_t;\n\n//structure definition\ntypedef struct structdef_s\n{\n\tint size;\n\tfielddef_t *fields;\n} structdef_t;\n\n//read a structure from a script\nint ReadStructure(source_t *source, structdef_t *def, char *structure);\n//write a structure to a file\nint WriteStructure(FILE *fp, structdef_t *def, char *structure);\n//writes indents\nint WriteIndent(FILE *fp, int indent);\n//writes a float without traling zeros\nint WriteFloat(FILE *fp, float value);\n\n\n"
  },
  {
    "path": "plugins/quake3/botlib/l_utils.h",
    "content": "/*\n===========================================================================\nCopyright (C) 1999-2005 Id Software, Inc.\n\nThis file is part of Quake III Arena source code.\n\nQuake III Arena source code is free software; you can redistribute it\nand/or modify it under the terms of the GNU General Public License as\npublished by the Free Software Foundation; either version 2 of the License,\nor (at your option) any later version.\n\nQuake III Arena source code is distributed in the hope that it will be\nuseful, but WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Quake III Arena source code; if not, write to the Free Software\nFoundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n===========================================================================\n*/\n\n/*****************************************************************************\n * name:\t\tl_util.h\n *\n * desc:\t\tutils\n *\n * $Archive: /source/code/botlib/l_util.h $\n *\n *****************************************************************************/\n\nvoid QDECL VectorAngles(float *forward, float *up, float *result, qboolean meshpitch);\n#define Vector2Angles(v,a)\t\tVectorAngles(v,NULL,a,qfalse)\n#ifndef MAX_PATH\n#define MAX_PATH\t\t\t\tMAX_QPATH\n#endif\n#define Maximum(x,y)\t\t\t(x > y ? x : y)\n#define Minimum(x,y)\t\t\t(x < y ? x : y)\n"
  },
  {
    "path": "plugins/quake3/botlib/q_platform.h",
    "content": "/*\r\n===========================================================================\r\nCopyright (C) 1999-2005 Id Software, Inc.\r\n\r\nThis file is part of Quake III Arena source code.\r\n\r\nQuake III Arena source code is free software; you can redistribute it\r\nand/or modify it under the terms of the GNU General Public License as\r\npublished by the Free Software Foundation; either version 2 of the License,\r\nor (at your option) any later version.\r\n\r\nQuake III Arena source code is distributed in the hope that it will be\r\nuseful, but WITHOUT ANY WARRANTY; without even the implied warranty of\r\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r\nGNU General Public License for more details.\r\n\r\nYou should have received a copy of the GNU General Public License\r\nalong with Quake III Arena source code; if not, write to the Free Software\r\nFoundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\r\n===========================================================================\r\n*/\r\n\r\n#ifndef __Q_PLATFORM_H\r\n#define __Q_PLATFORM_H\r\n\r\n// this is for determining if we have an asm version of a C function\r\n#define idx64 0\r\n\r\n#ifdef Q3_VM\r\n\r\n#define id386 0\r\n#define idppc 0\r\n#define idppc_altivec 0\r\n#define idsparc 0\r\n\r\n#else\r\n\r\n#if (defined _M_IX86 || defined __i386__) && !defined(C_ONLY)\r\n#define id386 1\r\n#else\r\n#define id386 0\r\n#endif\r\n\r\n#if (defined(powerc) || defined(powerpc) || defined(ppc) || \\\r\n\tdefined(__ppc) || defined(__ppc__)) && !defined(C_ONLY)\r\n#define idppc 1\r\n#if defined(__VEC__)\r\n#define idppc_altivec 1\r\n#ifdef MACOS_X  // Apple's GCC does this differently than the FSF.\r\n#define VECCONST_UINT8(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p) \\\r\n\t(vector unsigned char) (a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p)\r\n#else\r\n#define VECCONST_UINT8(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p) \\\r\n\t(vector unsigned char) {a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p}\r\n#endif\r\n#else\r\n#define idppc_altivec 0\r\n#endif\r\n#else\r\n#define idppc 0\r\n#define idppc_altivec 0\r\n#endif\r\n\r\n#if defined(__sparc__) && !defined(C_ONLY)\r\n#define idsparc 1\r\n#else\r\n#define idsparc 0\r\n#endif\r\n\r\n#endif\r\n\r\n#ifndef __ASM_I386__ // don't include the C bits if included from qasm.h\r\n\r\n// for windows fastcall option\r\n#define QDECL\r\n\r\n//================================================================= WIN64/32 ===\r\n\r\n#if defined(_WIN64) || defined(__WIN64__)\r\n\r\n#undef idx64\r\n#define idx64 1\r\n\r\n#undef QDECL\r\n#define QDECL __cdecl\r\n\r\n#if defined( _MSC_VER )\r\n#define OS_STRING \"win_msvc64\"\r\n#elif defined __MINGW64__\r\n#define OS_STRING \"win_mingw64\"\r\n#else\r\n#define OS_STRING \"win64\"\r\n#endif\r\n\r\n#define ID_INLINE static __inline\r\n#define PATH_SEP '\\\\'\r\n\r\n#if defined( __WIN64__ ) \r\n#define ARCH_STRING \"x86_64\"\r\n#elif defined _M_ALPHA\r\n#define ARCH_STRING \"AXP\"\r\n#endif\r\n\r\n#define Q3_LITTLE_ENDIAN\r\n\r\n#define DLL_EXT \".dll\"\r\n\r\n#elif defined(_WIN32) || defined(__WIN32__)\r\n\r\n#undef QDECL\r\n#define QDECL __cdecl\r\n\r\n#if defined( _MSC_VER )\r\n#define OS_STRING \"win_msvc\"\r\n#elif defined __MINGW32__\r\n#define OS_STRING \"win_mingw\"\r\n#endif\r\n\r\n#define ID_INLINE static __inline\r\n#define PATH_SEP '\\\\'\r\n\r\n#if defined( _M_IX86 ) || defined( __i386__ )\r\n#define ARCH_STRING \"x86\"\r\n#elif defined _M_ALPHA\r\n#define ARCH_STRING \"AXP\"\r\n#endif\r\n\r\n#define Q3_LITTLE_ENDIAN\r\n\r\n#define DLL_EXT \".dll\"\r\n\r\n#endif\r\n\r\n//============================================================== MAC OS X ===\r\n\r\n#if defined(MACOS_X) || defined(__APPLE_CC__)\r\n\r\n// make sure this is defined, just for sanity's sake...\r\n#ifndef MACOS_X\r\n#define MACOS_X\r\n#endif\r\n\r\n#define OS_STRING \"macosx\"\r\n#define ID_INLINE static inline\r\n#define PATH_SEP '/'\r\n\r\n#ifdef __ppc__\r\n#define ARCH_STRING \"ppc\"\r\n#define Q3_BIG_ENDIAN\r\n#elif defined __i386__\r\n#define ARCH_STRING \"i386\"\r\n#define Q3_LITTLE_ENDIAN\r\n#elif defined __x86_64__\r\n#undef idx64\r\n#define idx64 1\r\n#define ARCH_STRING \"x86_64\"\r\n#define Q3_LITTLE_ENDIAN\r\n#elif defined __aarch64__\r\n#define ARCH_STRING \"aarch64\"\r\n#define Q3_LITTLE_ENDIAN\r\n#endif\r\n\r\n#define DLL_EXT \".dylib\"\r\n\r\n#endif\r\n\r\n//================================================================= LINUX ===\r\n\r\n#if defined(__linux__) || defined(__FreeBSD_kernel__) || defined(ANDROID) || defined(__ANDROID__) || defined(__HAIKU__)\r\n\r\n#include <endian.h>\r\n\r\n#if defined(ANDROID) || defined(__ANDROID__)\r\n#define OS_STRING \"android\"\r\n#elif defined(__linux__)\r\n#define OS_STRING \"linux\"\r\n#elif defined(__HAIKU__)\r\n#define OS_STRING \"Haiku\"\r\n#else\r\n#define OS_STRING \"kFreeBSD\"\r\n#endif\r\n\r\n#define ID_INLINE static inline\r\n#define PATH_SEP '/'\r\n\r\n#if defined __i386__\r\n#define ARCH_STRING \"i386\"\r\n#elif defined __x86_64__\r\n#undef idx64\r\n#define idx64 1\r\n#define ARCH_STRING \"x86_64\"\r\n#elif defined __ppc64le__\r\n#define ARCH_STRING \"ppc64le\"\t//more common than ppc64[be] on linux nowadays.\r\n#elif defined __powerpc64__\r\n#define ARCH_STRING \"ppc64\"\t\t//big-endian/original variant.\r\n#elif defined __powerpc__\r\n#define ARCH_STRING \"ppc\"\r\n#elif defined __s390__\r\n#define ARCH_STRING \"s390\"\r\n#elif defined __s390x__\r\n#define ARCH_STRING \"s390x\"\r\n#elif defined __ia64__\r\n#define ARCH_STRING \"ia64\"\r\n#elif defined __alpha__\r\n#define ARCH_STRING \"alpha\"\r\n#elif defined __sparc__\r\n#define ARCH_STRING \"sparc\"\r\n#elif defined __arm__\r\n#define ARCH_STRING \"arm\"\r\n#elif defined __cris__\r\n#define ARCH_STRING \"cris\"\r\n#elif defined __hppa__\r\n#define ARCH_STRING \"hppa\"\r\n#elif defined __mips__\r\n#define ARCH_STRING \"mips\"\r\n#elif defined __sh__\r\n#define ARCH_STRING \"sh\"\r\n#endif\r\n\r\n#if __FLOAT_WORD_ORDER == __BIG_ENDIAN\r\n#define Q3_BIG_ENDIAN\r\n#else\r\n#define Q3_LITTLE_ENDIAN\r\n#endif\r\n\r\n#define DLL_EXT \".so\"\r\n\r\n#endif\r\n\r\n//=================================================================== BSD ===\r\n\r\n#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__)\r\n\r\n#include <sys/types.h>\r\n#include <machine/endian.h>\r\n\r\n#ifndef __BSD__\r\n  #define __BSD__\r\n#endif\r\n\r\n#if defined(__FreeBSD__)\r\n#define OS_STRING \"freebsd\"\r\n#elif defined(__OpenBSD__)\r\n#define OS_STRING \"openbsd\"\r\n#elif defined(__NetBSD__)\r\n#define OS_STRING \"netbsd\"\r\n#endif\r\n\r\n#define ID_INLINE static inline\r\n#define PATH_SEP '/'\r\n\r\n#ifdef __i386__\r\n#define ARCH_STRING \"i386\"\r\n#elif defined __amd64__\r\n#undef idx64\r\n#define idx64 1\r\n#define ARCH_STRING \"amd64\"\r\n#elif defined __axp__\r\n#define ARCH_STRING \"alpha\"\r\n#endif\r\n\r\n#if BYTE_ORDER == BIG_ENDIAN\r\n#define Q3_BIG_ENDIAN\r\n#else\r\n#define Q3_LITTLE_ENDIAN\r\n#endif\r\n\r\n#define DLL_EXT \".so\"\r\n\r\n#endif\r\n\r\n//================================================================= SUNOS ===\r\n\r\n#ifdef __sun\r\n\r\n#include <stdint.h>\r\n#include <sys/byteorder.h>\r\n\r\n#define OS_STRING \"solaris\"\r\n#define ID_INLINE static inline\r\n#define PATH_SEP '/'\r\n\r\n#ifdef __i386__\r\n#define ARCH_STRING \"i386\"\r\n#elif defined __sparc\r\n#define ARCH_STRING \"sparc\"\r\n#endif\r\n\r\n#if defined( _BIG_ENDIAN )\r\n#define Q3_BIG_ENDIAN\r\n#elif defined( _LITTLE_ENDIAN )\r\n#define Q3_LITTLE_ENDIAN\r\n#endif\r\n\r\n#define DLL_EXT \".so\"\r\n\r\n#endif\r\n\r\n//================================================================== IRIX ===\r\n\r\n#ifdef __sgi\r\n\r\n#define OS_STRING \"irix\"\r\n#define ID_INLINE static __inline\r\n#define PATH_SEP '/'\r\n\r\n#define ARCH_STRING \"mips\"\r\n\r\n#define Q3_BIG_ENDIAN // SGI's MIPS are always big endian\r\n\r\n#define DLL_EXT \".so\"\r\n\r\n#endif\r\n\r\n//=============================================================== MORPHOS ===\r\n\r\n#ifdef __MORPHOS__\r\n\r\n#define OS_STRING \"morphos\"\r\n#define ID_INLINE static inline\r\n#define PATH_SEP '/'\r\n\r\n#define ARCH_STRING \"ppc\"\r\n\r\n#define Q3_BIG_ENDIAN\r\n\r\n#define DLL_EXT \".so\"\r\n\r\n#endif\r\n\r\n#ifdef __CYGWIN__\r\n#define OS_STRING \"cygwin\"\r\n#define ID_INLINE static inline\r\n#define PATH_SEP '/'\r\n\r\n#define ARCH_STRING \"x86\"\r\n\r\n#define Q3_LITTLE_ENDIAN\r\n\r\n#define DLL_EXT \".dll\"\r\n\r\n#endif\r\n\r\n#ifdef __DJGPP__\r\n#define OS_STRING \"msdos\"\r\n#define ID_INLINE static inline\r\n#define PATH_SEP '/'\r\n\r\n#define ARCH_STRING \"dos\"\r\n\r\n#define Q3_LITTLE_ENDIAN\r\n\r\n#define DLL_EXT \".dll\"\r\n#endif\r\n\r\n\r\n#ifdef FTE_TARGET_WEB\r\n#define OS_STRING \"emscripten\"\r\n#define ID_INLINE static inline\r\n#define PATH_SEP '/'\r\n\r\n#define ARCH_STRING \"web\"\r\n\r\n#define Q3_LITTLE_ENDIAN\r\n\r\n#define DLL_EXT \".so\"\r\n#endif\r\n\r\n//================================================================== Q3VM ===\r\n\r\n#ifdef Q3_VM\r\n\r\n#define OS_STRING \"q3vm\"\r\n#define ID_INLINE static\r\n#define PATH_SEP '/'\r\n\r\n#define ARCH_STRING \"bytecode\"\r\n\r\n#define DLL_EXT \".qvm\"\r\n\r\n#endif\r\n\r\n//===========================================================================\r\n\r\n//catch missing defines in above blocks\r\n#if !defined( OS_STRING )\r\n#define ARCH_STRING \"unknown\"\r\n//#error \"Operating system not supported\"\r\n#endif\r\n\r\n#if !defined( ARCH_STRING )\r\n#define ARCH_STRING \"unk\"\r\n//#error \"Architecture not supported\"\r\n#endif\r\n\r\n#ifndef ID_INLINE\r\n#define ID_INLINE static\r\n//#error \"ID_INLINE not defined\"\r\n#endif\r\n\r\n#ifndef PATH_SEP\r\n#define PATH_SEP '/'\r\n//#error \"PATH_SEP not defined\"\r\n#endif\r\n\r\n#ifndef DLL_EXT\r\n#define DLL_EXT \".so\"\r\n//#error \"DLL_EXT not defined\"\r\n#endif\r\n\r\n\r\n//endianness\r\nshort ShortSwap (short l);\r\nint LongSwap (int l);\r\nfloat FloatSwap (const float *f);\r\n\r\n#if defined( Q3_BIG_ENDIAN ) && defined( Q3_LITTLE_ENDIAN )\r\n#error \"Endianness defined as both big and little\"\r\n#elif defined( Q3_BIG_ENDIAN )\r\n\r\n#define LittleShort(x) ShortSwap(x)\r\n#define LittleLong(x) LongSwap(x)\r\n#define LittleFloat(x) FloatSwap(&x)\r\n#define BigShort\r\n#define BigLong\r\n#define BigFloat\r\n\r\n#elif defined( Q3_LITTLE_ENDIAN )\r\n\r\n#define LittleShort\r\n#define LittleLong\r\n#define LittleFloat\r\n#define BigShort(x) ShortSwap(x)\r\n#define BigLong(x) LongSwap(x)\r\n#define BigFloat(x) FloatSwap(&x)\r\n\r\n#elif defined( Q3_VM )\r\n\r\n#define LittleShort\r\n#define LittleLong\r\n#define LittleFloat\r\n#define BigShort\r\n#define BigLong\r\n#define BigFloat\r\n\r\n#else\r\n#error \"Endianness not defined\"\r\n#endif\r\n\r\n\r\n//platform string\r\n#ifdef NDEBUG\r\n#define PLATFORM_STRING OS_STRING \"-\" ARCH_STRING\r\n#else\r\n#define PLATFORM_STRING OS_STRING \"-\" ARCH_STRING \"-debug\"\r\n#endif\r\n\r\n#endif\r\n\r\n#endif\r\n"
  },
  {
    "path": "plugins/quake3/botlib/q_shared.h",
    "content": "/*\n===========================================================================\nCopyright (C) 1999-2005 Id Software, Inc.\n\nThis file is part of Quake III Arena source code.\n\nQuake III Arena source code is free software; you can redistribute it\nand/or modify it under the terms of the GNU General Public License as\npublished by the Free Software Foundation; either version 2 of the License,\nor (at your option) any later version.\n\nQuake III Arena source code is distributed in the hope that it will be\nuseful, but WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Quake III Arena source code; if not, write to the Free Software\nFoundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n===========================================================================\n*/\n//\n#ifndef __Q_SHARED_H\n#define __Q_SHARED_H\n\n// q_shared.h -- included first by ALL program modules.\n// A user mod should never modify this file\n\n#ifdef STANDALONE\n  #define PRODUCT_NAME\t\t\t\"iofoo3\"\n  #define BASEGAME\t\t\t\"foobar\"\n  #define CLIENT_WINDOW_TITLE     \t\"changeme\"\n  #define CLIENT_WINDOW_MIN_TITLE \t\"changeme2\"\n  #define GAMENAME_FOR_MASTER\t\t\"iofoo3\"\t\t// must NOT contain whitespaces\n  #define HEARTBEAT_FOR_MASTER\t\tGAMENAME_FOR_MASTER\n  #define FLATLINE_FOR_MASTER\t\tGAMENAME_FOR_MASTER \"dead\"\n  #define HOMEPATH_NAME_UNIX\t\t\".foo\"\n  #define HOMEPATH_NAME_WIN\t\t\"FooBar\"\n  #define HOMEPATH_NAME_MACOSX\t\tHOMEPATH_NAME_WIN\n#else\n  #define PRODUCT_NAME\t\t\t\"ioq3\"\n  #define BASEGAME\t\t\t\"baseq3\"\n  #define CLIENT_WINDOW_TITLE     \t\"ioquake3\"\n  #define CLIENT_WINDOW_MIN_TITLE \t\"ioq3\"\n  #define GAMENAME_FOR_MASTER\t\t\"Quake3Arena\"\n  #define HEARTBEAT_FOR_MASTER\t\t\"QuakeArena-1\"\n  #define FLATLINE_FOR_MASTER\t\tHEARTBEAT_FOR_MASTER\n  #define HOMEPATH_NAME_UNIX\t\t\".q3a\"\n  #define HOMEPATH_NAME_WIN\t\t\"Quake3\"\n  #define HOMEPATH_NAME_MACOSX\t\tHOMEPATH_NAME_WIN\n#endif\n\n#define BASETA\t\t\t\t\"missionpack\"\n\n#ifdef _MSC_VER\n  #define PRODUCT_VERSION \"1.36\"\n#endif\n\n#define Q3_VERSION PRODUCT_NAME \" \" PRODUCT_VERSION\n\n#define MAX_TEAMNAME\t\t32\n#define MAX_MASTER_SERVERS      5\t// number of supported master servers\n\n#define DEMOEXT\t\"dm_\"\t\t\t// standard demo extension\n\n#ifdef _MSC_VER\n\n#pragma warning(disable : 4018)     // signed/unsigned mismatch\n#pragma warning(disable : 4032)\n#pragma warning(disable : 4051)\n#pragma warning(disable : 4057)\t\t// slightly different base types\n#pragma warning(disable : 4100)\t\t// unreferenced formal parameter\n#pragma warning(disable : 4115)\n#pragma warning(disable : 4125)\t\t// decimal digit terminates octal escape sequence\n#pragma warning(disable : 4127)\t\t// conditional expression is constant\n#pragma warning(disable : 4136)\n#pragma warning(disable : 4152)\t\t// nonstandard extension, function/data pointer conversion in expression\n//#pragma warning(disable : 4201)\n//#pragma warning(disable : 4214)\n#pragma warning(disable : 4244)\n#pragma warning(disable : 4142)\t\t// benign redefinition\n//#pragma warning(disable : 4305)\t\t// truncation from const double to float\n//#pragma warning(disable : 4310)\t\t// cast truncates constant value\n//#pragma warning(disable:  4505) \t// unreferenced local function has been removed\n#pragma warning(disable : 4514)\n#pragma warning(disable : 4702)\t\t// unreachable code\n#pragma warning(disable : 4711)\t\t// selected for automatic inline expansion\n#pragma warning(disable : 4220)\t\t// varargs matches remaining parameters\n//#pragma intrinsic( memset, memcpy )\n#endif\n\n//Ignore __attribute__ on non-gcc platforms\n#ifndef __GNUC__\n#ifndef __attribute__\n#define __attribute__(x)\n#endif\n#endif\n\n#if (defined _MSC_VER)\n#define Q_EXPORT __declspec(dllexport)\n#elif (defined __SUNPRO_C)\n#define Q_EXPORT __global\n#elif ((__GNUC__ >= 3) && (!__EMX__) && (!sun))\n#define Q_EXPORT __attribute__((visibility(\"default\")))\n#else\n#define Q_EXPORT\n#endif\n\n/**********************************************************************\n  VM Considerations\n\n  The VM can not use the standard system headers because we aren't really\n  using the compiler they were meant for.  We use bg_lib.h which contains\n  prototypes for the functions we define for our own use in bg_lib.c.\n\n  When writing mods, please add needed headers HERE, do not start including\n  stuff like <stdio.h> in the various .c files that make up each of the VMs\n  since you will be including system headers files can will have issues.\n\n  Remember, if you use a C library function that is not defined in bg_lib.c,\n  you will have to add your own version for support in the VM.\n\n **********************************************************************/\n\n#ifdef Q3_VM\n\n#include \"../game/bg_lib.h\"\n\ntypedef int intptr_t;\n\n#else\n\n#include <assert.h>\n#include <math.h>\n#include <stdio.h>\n#include <stdarg.h>\n#include <string.h>\n#include <stdlib.h>\n#include <time.h>\n#include <ctype.h>\n#include <limits.h>\n\n#ifdef _MSC_VER\n  #include <io.h>\n\n  typedef __int64 int64_t;\n  typedef __int32 int32_t;\n  typedef __int16 int16_t;\n  typedef __int8 int8_t;\n  typedef unsigned __int64 uint64_t;\n  typedef unsigned __int32 uint32_t;\n  typedef unsigned __int16 uint16_t;\n  typedef unsigned __int8 uint8_t;\n\n  // vsnprintf is ISO/IEC 9899:1999\n  // abstracting this to make it portable\n  int Q_vsnprintf(char *str, size_t size, const char *format, va_list ap);\n#else\n  #include <stdint.h>\n\n  #define Q_vsnprintf vsnprintf\n#endif\n\n#endif\n\n\n#include \"q_platform.h\"\n\n//=============================================================\n\ntypedef unsigned char \t\tbyte;\n\ntypedef enum {qfalse, qtrue}\tqboolean;\n\ntypedef union {\n\tfloat f;\n\tint i;\n\tunsigned int ui;\n} floatint_t;\n\ntypedef int\t\tqhandle_t;\ntypedef int\t\tsfxHandle_t;\ntypedef int\t\tfileHandle_t;\ntypedef int\t\tclipHandle_t;\n\n#define PAD(base, alignment)\t(((base)+(alignment)-1) & ~((alignment)-1))\n#define PADLEN(base, alignment)\t(PAD((base), (alignment)) - (base))\n\n#define PADP(base, alignment)\t((void *) PAD((intptr_t) (base), (alignment)))\n\n#ifdef __GNUC__\n#define QALIGN(x) __attribute__((aligned(x)))\n#else\n#define QALIGN(x)\n#endif\n\n#ifndef NULL\n#define NULL ((void *)0)\n#endif\n\n#define STRING(s)\t\t\t#s\n// expand constants before stringifying them\n#define XSTRING(s)\t\t\tSTRING(s)\n\n#define\tMAX_QINT\t\t\t0x7fffffff\n#define\tMIN_QINT\t\t\t(-MAX_QINT-1)\n\n#define ARRAY_LEN(x)\t\t\t(sizeof(x) / sizeof(*(x)))\n\n\n// angle indexes\n#define\tPITCH\t\t\t\t0\t\t// up / down\n#define\tYAW\t\t\t\t\t1\t\t// left / right\n#define\tROLL\t\t\t\t2\t\t// fall over\n\n// the game guarantees that no string from the network will ever\n// exceed MAX_STRING_CHARS\n#define\tMAX_STRING_CHARS\t1024\t// max length of a string passed to Cmd_TokenizeString\n#define\tMAX_STRING_TOKENS\t1024\t// max tokens resulting from Cmd_TokenizeString\n#define\tMAX_TOKEN_CHARS\t\t1024\t// max length of an individual token\n\n#define\tMAX_INFO_STRING\t\t1024\n#define\tMAX_INFO_KEY\t\t  1024\n#define\tMAX_INFO_VALUE\t\t1024\n\n#define\tBIG_INFO_STRING\t\t8192  // used for system info key only\n#define\tBIG_INFO_KEY\t\t  8192\n#define\tBIG_INFO_VALUE\t\t8192\n\n\n#define\tMAX_QPATH\t\t\t64\t\t// max length of a quake game pathname\n#ifdef PATH_MAX\n#define MAX_OSPATH\t\t\tPATH_MAX\n#else\n#define\tMAX_OSPATH\t\t\t256\t\t// max length of a filesystem pathname\n#endif\n\n#define\tMAX_NAME_LENGTH\t\t32\t\t// max length of a client name\n\n#define\tMAX_SAY_TEXT\t150\n\n// paramters for command buffer stuffing\ntypedef enum {\n\tEXEC_NOW,\t\t\t// don't return until completed, a VM should NEVER use this,\n\t\t\t\t\t\t// because some commands might cause the VM to be unloaded...\n\tEXEC_INSERT,\t\t// insert at current position, but don't run yet\n\tEXEC_APPEND\t\t\t// add to end of the command buffer (normal case)\n} cbufExec_t;\n\n\n//\n// these aren't needed by any of the VMs.  put in another header?\n//\n#define\tMAX_MAP_AREA_BYTES\t\t32\t\t// bit vector of area visibility\n\n\n// print levels from renderer (FIXME: set up for game / cgame?)\ntypedef enum {\n\tPRINT_ALL,\n\tPRINT_DEVELOPER,\t\t// only print when \"developer 1\"\n\tPRINT_WARNING,\n\tPRINT_ERROR\n} printParm_t;\n\n\n#ifdef ERR_FATAL\n#undef ERR_FATAL\t\t\t// this is be defined in malloc.h\n#endif\n\n// parameters to the main Error routine\ntypedef enum {\n\tERR_FATAL,\t\t\t\t\t// exit the entire game with a popup window\n\tERR_DROP,\t\t\t\t\t// print to console and disconnect from game\n\tERR_SERVERDISCONNECT,\t\t// don't kill server\n\tERR_DISCONNECT,\t\t\t\t// client disconnected from the server\n\tERR_NEED_CD\t\t\t\t\t// pop up the need-cd dialog\n} errorParm_t;\n\n\n// font rendering values used by ui and cgame\n\n#define PROP_GAP_WIDTH\t\t\t3\n#define PROP_SPACE_WIDTH\t\t8\n#define PROP_HEIGHT\t\t\t\t27\n#define PROP_SMALL_SIZE_SCALE\t0.75\n\n#define BLINK_DIVISOR\t\t\t200\n#define PULSE_DIVISOR\t\t\t75\n\n#define UI_LEFT\t\t\t0x00000000\t// default\n#define UI_CENTER\t\t0x00000001\n#define UI_RIGHT\t\t0x00000002\n#define UI_FORMATMASK\t0x00000007\n#define UI_SMALLFONT\t0x00000010\n#define UI_BIGFONT\t\t0x00000020\t// default\n#define UI_GIANTFONT\t0x00000040\n#define UI_DROPSHADOW\t0x00000800\n#define UI_BLINK\t\t0x00001000\n#define UI_INVERSE\t\t0x00002000\n#define UI_PULSE\t\t0x00004000\n\n#if defined(_DEBUG) && !defined(BSPC)\n\t#define HUNK_DEBUG\n#endif\n\ntypedef enum {\n\th_high,\n\th_low,\n\th_dontcare\n} ha_pref;\n\n#ifdef HUNK_DEBUG\n#define Hunk_Alloc( size, preference )\t\t\t\tHunk_AllocDebug(size, preference, #size, __FILE__, __LINE__)\nvoid *Hunk_AllocDebug( int size, ha_pref preference, char *label, char *file, int line );\n#else\nvoid *Hunk_Alloc( int size, ha_pref preference );\n#endif\n\n#define Com_Memset memset\n#define Com_Memcpy memcpy\n\n#define CIN_system\t1\n#define CIN_loop\t2\n#define\tCIN_hold\t4\n#define CIN_silent\t8\n#define CIN_shader\t16\n\n/*\n==============================================================\n\nMATHLIB\n\n==============================================================\n*/\n\n\ntypedef float vec_t;\ntypedef vec_t vec2_t[2];\ntypedef vec_t vec3_t[3];\ntypedef vec_t vec4_t[4];\ntypedef vec_t vec5_t[5];\n\ntypedef\tint\tfixed4_t;\ntypedef\tint\tfixed8_t;\ntypedef\tint\tfixed16_t;\n\n#ifndef M_PI\n#define M_PI\t\t3.14159265358979323846f\t// matches value in gcc v2 math.h\n#endif\n\n#define NUMVERTEXNORMALS\t162\nextern\tvec3_t\tbytedirs[NUMVERTEXNORMALS];\n\n// all drawing is done to a 640*480 virtual screen size\n// and will be automatically scaled to the real resolution\n#define\tSCREEN_WIDTH\t\t640\n#define\tSCREEN_HEIGHT\t\t480\n\n#define TINYCHAR_WIDTH\t\t(SMALLCHAR_WIDTH)\n#define TINYCHAR_HEIGHT\t\t(SMALLCHAR_HEIGHT/2)\n\n#define SMALLCHAR_WIDTH\t\t8\n#define SMALLCHAR_HEIGHT\t16\n\n#define BIGCHAR_WIDTH\t\t16\n#define BIGCHAR_HEIGHT\t\t16\n\n#define\tGIANTCHAR_WIDTH\t\t32\n#define\tGIANTCHAR_HEIGHT\t48\n\nextern\tvec4_t\t\tcolorBlack;\nextern\tvec4_t\t\tcolorRed;\nextern\tvec4_t\t\tcolorGreen;\nextern\tvec4_t\t\tcolorBlue;\nextern\tvec4_t\t\tcolorYellow;\nextern\tvec4_t\t\tcolorMagenta;\nextern\tvec4_t\t\tcolorCyan;\nextern\tvec4_t\t\tcolorWhite;\nextern\tvec4_t\t\tcolorLtGrey;\nextern\tvec4_t\t\tcolorMdGrey;\nextern\tvec4_t\t\tcolorDkGrey;\n\n#define Q_COLOR_ESCAPE\t'^'\n#define Q_IsColorString(p)\t((p) && *(p) == Q_COLOR_ESCAPE && *((p)+1) && isalnum(*((p)+1))) // ^[0-9a-zA-Z]\n\n#define COLOR_BLACK\t'0'\n#define COLOR_RED\t'1'\n#define COLOR_GREEN\t'2'\n#define COLOR_YELLOW\t'3'\n#define COLOR_BLUE\t'4'\n#define COLOR_CYAN\t'5'\n#define COLOR_MAGENTA\t'6'\n#define COLOR_WHITE\t'7'\n#define ColorIndex(c)\t(((c) - '0') & 0x07)\n\n#define S_COLOR_BLACK\t\"^0\"\n#define S_COLOR_RED\t\"^1\"\n#define S_COLOR_GREEN\t\"^2\"\n#define S_COLOR_YELLOW\t\"^3\"\n#define S_COLOR_BLUE\t\"^4\"\n#define S_COLOR_CYAN\t\"^5\"\n#define S_COLOR_MAGENTA\t\"^6\"\n#define S_COLOR_WHITE\t\"^7\"\n\nextern vec4_t\tg_color_table[8];\n\n#define\tMAKERGB( v, r, g, b ) v[0]=r;v[1]=g;v[2]=b\n#define\tMAKERGBA( v, r, g, b, a ) v[0]=r;v[1]=g;v[2]=b;v[3]=a\n\n#define DEG2RAD( a ) ( ( (a) * M_PI ) / 180.0F )\n#define RAD2DEG( a ) ( ( (a) * 180.0f ) / M_PI )\n\nstruct cplane_s;\n\nextern\tvec3_t\tvec3_origin;\nextern\tvec3_t\taxisDefault[3];\n\n#define\tnanmask (255<<23)\n\n#define\tIS_NAN(x) (((*(int *)&x)&nanmask)==nanmask)\n\nint Q_isnan(float x);\n\n#if idx64\n  extern long qftolsse(float f);\n  extern int qvmftolsse(void);\n  extern void qsnapvectorsse(vec3_t vec);\n\n  #define Q_ftol qftolsse\n  #define Q_SnapVector qsnapvectorsse\n\n  extern int (*Q_VMftol)(void);\n#elif id386\n  extern long QDECL qftolx87(float f);\n  extern long QDECL qftolsse(float f);\n  extern int QDECL qvmftolx87(void);\n  extern int QDECL qvmftolsse(void);\n  extern void QDECL qsnapvectorx87(vec3_t vec);\n  extern void QDECL qsnapvectorsse(vec3_t vec);\n\n  extern long (QDECL *Q_ftol)(float f);\n  extern int (QDECL *Q_VMftol)(void);\n  extern void (QDECL *Q_SnapVector)(vec3_t vec);\n#else\n  #define Q_ftol(f) lrintf((f))\n  #define Q_SnapVector(vec)\\\n\tdo\\\n\t{\\\n\t\tvec3_t *temp = (vec);\\\n\t\t\\\n\t\t(*temp)[0] = round((*temp)[0]);\\\n\t\t(*temp)[1] = round((*temp)[1]);\\\n\t\t(*temp)[2] = round((*temp)[2]);\\\n\t} while(0)\n#endif\n/*\n// if your system does not have lrintf() and round() you can try this block. Please also open a bug report at bugzilla.icculus.org\n// or write a mail to the ioq3 mailing list.\n#else\n  #define Q_ftol(v) ((long) (v))\n  #define Q_round(v) do { if((v) < 0) (v) -= 0.5f; else (v) += 0.5f; (v) = Q_ftol((v)); } while(0)\n  #define Q_SnapVector(vec) \\\n\tdo\\\n\t{\\\n\t\tvec3_t *temp = (vec);\\\n\t\t\\\n\t\tQ_round((*temp)[0]);\\\n\t\tQ_round((*temp)[1]);\\\n\t\tQ_round((*temp)[2]);\\\n\t} while(0)\n#endif\n*/\n\n#if idppc\n\nID_INLINE float Q_rsqrt( float number ) {\n\t\tfloat x = 0.5f * number;\n                float y;\n#ifdef __GNUC__            \n                asm(\"frsqrte %0,%1\" : \"=f\" (y) : \"f\" (number));\n#else\n\t\ty = __frsqrte( number );\n#endif\n\t\treturn y * (1.5f - (x * y * y));\n\t}\n\n#ifdef __GNUC__            \nID_INLINE float Q_fabs(float x) {\n    float abs_x;\n    \n    asm(\"fabs %0,%1\" : \"=f\" (abs_x) : \"f\" (x));\n    return abs_x;\n}\n#else\n#define Q_fabs __fabsf\n#endif\n\n#else\nfloat Q_fabs( float f );\nfloat Q_rsqrt( float f );\t\t// reciprocal square root\n#endif\n\n#define SQRTFAST( x ) ( (x) * Q_rsqrt( x ) )\n\nsigned char ClampChar( int i );\nsigned short ClampShort( int i );\n\n// this isn't a real cheap function to call!\nint DirToByte( vec3_t dir );\nvoid ByteToDir( int b, vec3_t dir );\n\n#if\t1\n\n#define DotProduct(x,y)\t\t\t((x)[0]*(y)[0]+(x)[1]*(y)[1]+(x)[2]*(y)[2])\n#define VectorSubtract(a,b,c)\t((c)[0]=(a)[0]-(b)[0],(c)[1]=(a)[1]-(b)[1],(c)[2]=(a)[2]-(b)[2])\n#define VectorAdd(a,b,c)\t\t((c)[0]=(a)[0]+(b)[0],(c)[1]=(a)[1]+(b)[1],(c)[2]=(a)[2]+(b)[2])\n#define VectorCopy(a,b)\t\t\t((b)[0]=(a)[0],(b)[1]=(a)[1],(b)[2]=(a)[2])\n#define\tVectorScale(v, s, o)\t((o)[0]=(v)[0]*(s),(o)[1]=(v)[1]*(s),(o)[2]=(v)[2]*(s))\n#define\tVectorMA(v, s, b, o)\t((o)[0]=(v)[0]+(b)[0]*(s),(o)[1]=(v)[1]+(b)[1]*(s),(o)[2]=(v)[2]+(b)[2]*(s))\n\n#else\n\n#define DotProduct(x,y)\t\t\t_DotProduct(x,y)\n#define VectorSubtract(a,b,c)\t_VectorSubtract(a,b,c)\n#define VectorAdd(a,b,c)\t\t_VectorAdd(a,b,c)\n#define VectorCopy(a,b)\t\t\t_VectorCopy(a,b)\n#define\tVectorScale(v, s, o)\t_VectorScale(v,s,o)\n#define\tVectorMA(v, s, b, o)\t_VectorMA(v,s,b,o)\n\n#endif\n\n#ifdef Q3_VM\n#ifdef VectorCopy\n#undef VectorCopy\n// this is a little hack to get more efficient copies in our interpreter\ntypedef struct {\n\tfloat\tv[3];\n} vec3struct_t;\n#define VectorCopy(a,b)\t(*(vec3struct_t *)b=*(vec3struct_t *)a)\n#endif\n#endif\n\n#define VectorClear(a)\t\t\t((a)[0]=(a)[1]=(a)[2]=0)\n#define VectorNegate(a,b)\t\t((b)[0]=-(a)[0],(b)[1]=-(a)[1],(b)[2]=-(a)[2])\n#define VectorSet(v, x, y, z)\t((v)[0]=(x), (v)[1]=(y), (v)[2]=(z))\n#define Vector4Copy(a,b)\t\t((b)[0]=(a)[0],(b)[1]=(a)[1],(b)[2]=(a)[2],(b)[3]=(a)[3])\n\n#define\tSnapVector(v) {v[0]=((int)(v[0]));v[1]=((int)(v[1]));v[2]=((int)(v[2]));}\n// just in case you do't want to use the macros\nvec_t _DotProduct( const vec3_t v1, const vec3_t v2 );\nvoid _VectorSubtract( const vec3_t veca, const vec3_t vecb, vec3_t out );\nvoid _VectorAdd( const vec3_t veca, const vec3_t vecb, vec3_t out );\nvoid _VectorCopy( const vec3_t in, vec3_t out );\nvoid _VectorScale( const vec3_t in, float scale, vec3_t out );\nvoid _VectorMA( const vec3_t veca, float scale, const vec3_t vecb, vec3_t vecc );\n\nunsigned ColorBytes3 (float r, float g, float b);\nunsigned ColorBytes4 (float r, float g, float b, float a);\n\nfloat NormalizeColor( const vec3_t in, vec3_t out );\n\nfloat RadiusFromBounds( const vec3_t mins, const vec3_t maxs );\nvoid ClearBounds( vec3_t mins, vec3_t maxs );\nvoid AddPointToBounds( const vec3_t v, vec3_t mins, vec3_t maxs );\n\n#if !defined( Q3_VM ) || ( defined( Q3_VM ) && defined( __Q3_VM_MATH ) )\nID_INLINE int VectorCompare( const vec3_t v1, const vec3_t v2 ) {\n\tif (v1[0] != v2[0] || v1[1] != v2[1] || v1[2] != v2[2]) {\n\t\treturn 0;\n\t}\t\t\t\n\treturn 1;\n}\n\nID_INLINE vec_t VectorLength( const vec3_t v ) {\n\treturn (vec_t)sqrt (v[0]*v[0] + v[1]*v[1] + v[2]*v[2]);\n}\n\nID_INLINE vec_t VectorLengthSquared( const vec3_t v ) {\n\treturn (v[0]*v[0] + v[1]*v[1] + v[2]*v[2]);\n}\n\nID_INLINE vec_t Distance( const vec3_t p1, const vec3_t p2 ) {\n\tvec3_t\tv;\n\n\tVectorSubtract (p2, p1, v);\n\treturn VectorLength( v );\n}\n\nID_INLINE vec_t DistanceSquared( const vec3_t p1, const vec3_t p2 ) {\n\tvec3_t\tv;\n\n\tVectorSubtract (p2, p1, v);\n\treturn v[0]*v[0] + v[1]*v[1] + v[2]*v[2];\n}\n\n// fast vector normalize routine that does not check to make sure\n// that length != 0, nor does it return length, uses rsqrt approximation\nID_INLINE void VectorNormalizeFast( vec3_t v )\n{\n\tfloat ilength;\n\n\tilength = Q_rsqrt( DotProduct( v, v ) );\n\n\tv[0] *= ilength;\n\tv[1] *= ilength;\n\tv[2] *= ilength;\n}\n\nID_INLINE void VectorInverse( vec3_t v ){\n\tv[0] = -v[0];\n\tv[1] = -v[1];\n\tv[2] = -v[2];\n}\n\nID_INLINE void CrossProduct( const vec3_t v1, const vec3_t v2, vec3_t cross ) {\n\tcross[0] = v1[1]*v2[2] - v1[2]*v2[1];\n\tcross[1] = v1[2]*v2[0] - v1[0]*v2[2];\n\tcross[2] = v1[0]*v2[1] - v1[1]*v2[0];\n}\n\n#else\nint VectorCompare( const vec3_t v1, const vec3_t v2 );\n\nvec_t VectorLength( const vec3_t v );\n\nvec_t VectorLengthSquared( const vec3_t v );\n\nvec_t Distance( const vec3_t p1, const vec3_t p2 );\n\nvec_t DistanceSquared( const vec3_t p1, const vec3_t p2 );\n\nvoid VectorNormalizeFast( vec3_t v );\n\nvoid VectorInverse( vec3_t v );\n\nvoid CrossProduct( const vec3_t v1, const vec3_t v2, vec3_t cross );\n\n#endif\n\nvec_t VectorNormalize (vec3_t v);\t\t// returns vector length\nvec_t VectorNormalize2( const vec3_t v, vec3_t out );\nvoid Vector4Scale( const vec4_t in, vec_t scale, vec4_t out );\nvoid VectorRotate( vec3_t in, vec3_t matrix[3], vec3_t out );\nint Q_log2(int val);\n\nfloat Q_acos(float c);\n\nint\t\tQ_rand( int *seed );\nfloat\tQ_random( int *seed );\nfloat\tQ_crandom( int *seed );\n\n#define random()\t((rand () & 0x7fff) / ((float)0x7fff))\n#define crandom()\t(2.0 * (random() - 0.5))\n\nvoid AnglesToAxis( const vec3_t angles, vec3_t axis[3] );\n\nvoid AxisClear( vec3_t axis[3] );\nvoid AxisCopy( vec3_t in[3], vec3_t out[3] );\n\nvoid SetPlaneSignbits( struct cplane_s *out );\nint BoxOnPlaneSide (vec3_t emins, vec3_t emaxs, struct cplane_s *plane);\n\nqboolean BoundsIntersect(const vec3_t mins, const vec3_t maxs,\n\t\tconst vec3_t mins2, const vec3_t maxs2);\nqboolean BoundsIntersectSphere(const vec3_t mins, const vec3_t maxs,\n\t\tconst vec3_t origin, vec_t radius);\nqboolean BoundsIntersectPoint(const vec3_t mins, const vec3_t maxs,\n\t\tconst vec3_t origin);\n\nfloat\tAngleMod(float a);\nfloat\tLerpAngle (float from, float to, float frac);\nfloat\tAngleSubtract( float a1, float a2 );\nvoid\tAnglesSubtract( vec3_t v1, vec3_t v2, vec3_t v3 );\n\nfloat AngleNormalize360 ( float angle );\nfloat AngleNormalize180 ( float angle );\nfloat AngleDelta ( float angle1, float angle2 );\n\nqboolean PlaneFromPoints( vec4_t plane, const vec3_t a, const vec3_t b, const vec3_t c );\nvoid ProjectPointOnPlane( vec3_t dst, const vec3_t p, const vec3_t normal );\nvoid RotatePointAroundVector( vec3_t dst, const vec3_t dir, const vec3_t point, float degrees );\nvoid RotateAroundDirection( vec3_t axis[3], float yaw );\nvoid MakeNormalVectors( const vec3_t forward, vec3_t right, vec3_t up );\n// perpendicular vector could be replaced by this\n\n//int\tPlaneTypeForNormal (vec3_t normal);\n\nvoid MatrixMultiply(float in1[3][3], float in2[3][3], float out[3][3]);\nvoid AngleVectors( const vec3_t angles, vec3_t forward, vec3_t right, vec3_t up);\nvoid PerpendicularVector( vec3_t dst, const vec3_t src );\n\n#ifndef MAX\n#define MAX(x,y) ((x)>(y)?(x):(y))\n#endif\n\n#ifndef MIN\n#define MIN(x,y) ((x)<(y)?(x):(y))\n#endif\n\n//=============================================\n\nfloat Com_Clamp( float min, float max, float value );\n\nchar\t*COM_SkipPath( char *pathname );\nconst char\t*COM_GetExtension( const char *name );\nvoid\tCOM_StripExtension(const char *in, char *out, int destsize);\nvoid\tCOM_DefaultExtension( char *path, int maxSize, const char *extension );\n\nvoid\tCOM_BeginParseSession( const char *name );\nint\t\tCOM_GetCurrentParseLine( void );\nchar\t*COM_Parse( char **data_p );\nchar\t*COM_ParseExt( char **data_p, qboolean allowLineBreak );\nint\t\tCOM_Compress( char *data_p );\nvoid\tCOM_ParseError( char *format, ... ) __attribute__ ((format (printf, 1, 2)));\nvoid\tCOM_ParseWarning( char *format, ... ) __attribute__ ((format (printf, 1, 2)));\n//int\t\tCOM_ParseInfos( char *buf, int max, char infos[][MAX_INFO_STRING] );\n\n#define MAX_TOKENLENGTH\t\t1024\n\n#ifndef TT_STRING\n//token types\n#define TT_STRING\t\t\t\t\t1\t\t\t// string\n#define TT_LITERAL\t\t\t\t\t2\t\t\t// literal\n#define TT_NUMBER\t\t\t\t\t3\t\t\t// number\n#define TT_NAME\t\t\t\t\t\t4\t\t\t// name\n#define TT_PUNCTUATION\t\t\t\t5\t\t\t// punctuation\n#endif\n\ntypedef struct pc_token_s\n{\n\tint type;\n\tint subtype;\n\tint intvalue;\n\tfloat floatvalue;\n\tchar string[MAX_TOKENLENGTH];\n} pc_token_t;\n\n// data is an in/out parm, returns a parsed out token\n\nvoid\tCOM_MatchToken( char**buf_p, char *match );\n\nvoid SkipBracedSection (char **program);\nvoid SkipRestOfLine ( char **data );\n\nvoid Parse1DMatrix (char **buf_p, int x, float *m);\nvoid Parse2DMatrix (char **buf_p, int y, int x, float *m);\nvoid Parse3DMatrix (char **buf_p, int z, int y, int x, float *m);\nint Com_HexStrToInt( const char *str );\n\nvoid\tQDECL Com_sprintf (char *dest, int size, const char *fmt, ...) __attribute__ ((format (printf, 3, 4)));\n\nchar *Com_SkipTokens( char *s, int numTokens, char *sep );\nchar *Com_SkipCharset( char *s, char *sep );\n\nvoid Com_RandomBytes( byte *string, int len );\n\n// mode parm for FS_FOpenFile\ntypedef enum {\n\tFS_READ,\n\tFS_WRITE,\n\tFS_APPEND,\n\tFS_APPEND_SYNC\n} fsMode_t;\n\ntypedef enum {\n\tFS_SEEK_CUR,\n\tFS_SEEK_END,\n\tFS_SEEK_SET\n} fsOrigin_t;\n\n//=============================================\n\nint Q_isprint( int c );\nint Q_islower( int c );\nint Q_isupper( int c );\nint Q_isalpha( int c );\nqboolean Q_isanumber( const char *s );\nqboolean Q_isintegral( float f );\n\n// portable case insensitive compare\nint\t\tQ_stricmp (const char *s1, const char *s2);\nint\t\tQ_strncmp (const char *s1, const char *s2, int n);\nint\t\tQ_stricmpn (const char *s1, const char *s2, int n);\nchar\t*Q_strlwr( char *s1 );\nchar\t*Q_strupr( char *s1 );\nconst char\t*Q_stristr( const char *s, const char *find);\n\n// buffer size safe library replacements\nvoid\tQ_strncpyz( char *dest, const char *src, int destsize );\nvoid\tQ_strcat( char *dest, int size, const char *src );\n\n// strlen that discounts Quake color sequences\nint Q_PrintStrlen( const char *string );\n// removes color sequences from string\nchar *Q_CleanStr( char *string );\n// Count the number of char tocount encountered in string\nint Q_CountChar(const char *string, char tocount);\n\n//=============================================\n\n// 64-bit integers for global rankings interface\n// implemented as a struct for qvm compatibility\ntypedef struct\n{\n\tbyte\tb0;\n\tbyte\tb1;\n\tbyte\tb2;\n\tbyte\tb3;\n\tbyte\tb4;\n\tbyte\tb5;\n\tbyte\tb6;\n\tbyte\tb7;\n} qint64;\n\n//=============================================\n/*\nshort\tBigShort(short l);\nshort\tLittleShort(short l);\nint\t\tBigLong (int l);\nint\t\tLittleLong (int l);\nqint64  BigLong64 (qint64 l);\nqint64  LittleLong64 (qint64 l);\nfloat\tBigFloat (const float *l);\nfloat\tLittleFloat (const float *l);\n\nvoid\tSwap_Init (void);\n*/\nchar\t* QDECL va(char *format, ...) __attribute__ ((format (printf, 1, 2)));\n\n#define TRUNCATE_LENGTH\t64\nvoid Com_TruncateLongString( char *buffer, const char *s );\n\n//=============================================\n\n//\n// key / value info strings\n//\nchar *Info_ValueForKey( const char *s, const char *key );\nvoid Info_RemoveKey( char *s, const char *key );\nvoid Info_RemoveKey_big( char *s, const char *key );\nvoid Info_SetValueForKey( char *s, const char *key, const char *value );\nvoid Info_SetValueForKey_Big( char *s, const char *key, const char *value );\nqboolean Info_Validate( const char *s );\nvoid Info_NextPair( const char **s, char *key, char *value );\n\n// this is only here so the functions in q_shared.c and bg_*.c can link\nvoid\tQDECL Com_Error( int level, const char *error, ... ) __attribute__ ((format (printf, 2, 3)));\nvoid\tQDECL Com_Printf( const char *msg, ... ) __attribute__ ((format (printf, 1, 2)));\n\n\n/*\n==========================================================\n\nCVARS (console variables)\n\nMany variables can be used for cheating purposes, so when\ncheats is zero, force all unspecified variables to their\ndefault values.\n==========================================================\n*/\n\n#define\tCVAR_ARCHIVE\t\t0x0001\t// set to cause it to be saved to vars.rc\n\t\t\t\t\t// used for system variables, not for player\n\t\t\t\t\t// specific configurations\n#define\tCVAR_USERINFO\t\t0x0002\t// sent to server on connect or change\n#define\tCVAR_SERVERINFO\t\t0x0004\t// sent in response to front end requests\n#define\tCVAR_SYSTEMINFO\t\t0x0008\t// these cvars will be duplicated on all clients\n#define\tCVAR_INIT\t\t0x0010\t// don't allow change from console at all,\n\t\t\t\t\t// but can be set from the command line\n#define\tCVAR_LATCH\t\t0x0020\t// will only change when C code next does\n\t\t\t\t\t// a Cvar_Get(), so it can't be changed\n\t\t\t\t\t// without proper initialization.  modified\n\t\t\t\t\t// will be set, even though the value hasn't\n\t\t\t\t\t// changed yet\n#define\tCVAR_ROM\t\t0x0040\t// display only, cannot be set by user at all\n#define\tCVAR_USER_CREATED\t0x0080\t// created by a set command\n#define\tCVAR_TEMP\t\t0x0100\t// can be set even when cheats are disabled, but is not archived\n#define CVAR_CHEAT\t\t0x0200\t// can not be changed if cheats are disabled\n#define CVAR_NORESTART\t\t0x0400\t// do not clear when a cvar_restart is issued\n\n#define CVAR_SERVER_CREATED\t0x0800\t// cvar was created by a server the client connected to.\n#define CVAR_VM_CREATED\t\t0x1000\t// cvar was created exclusively in one of the VMs.\n#define CVAR_PROTECTED\t\t0x2000\t// prevent modifying this var from VMs or the server\n#define CVAR_NONEXISTENT\t0xFFFFFFFF\t// Cvar doesn't exist.\n\n// nothing outside the Cvar_*() functions should modify these fields!\ntypedef struct cvar_s cvar_t;\n\nstruct cvar_s {\n\tchar\t\t\t*name;\n\tchar\t\t\t*string;\n\tchar\t\t\t*resetString;\t\t// cvar_restart will reset to this value\n\tchar\t\t\t*latchedString;\t\t// for CVAR_LATCH vars\n\tint\t\t\t\tflags;\n\tqboolean\tmodified;\t\t\t// set each time the cvar is changed\n\tint\t\t\t\tmodificationCount;\t// incremented each time the cvar is changed\n\tfloat\t\t\tvalue;\t\t\t\t// atof( string )\n\tint\t\t\t\tinteger;\t\t\t// atoi( string )\n\tqboolean\tvalidate;\n\tqboolean\tintegral;\n\tfloat\t\t\tmin;\n\tfloat\t\t\tmax;\n\n\tcvar_t *next;\n\tcvar_t *prev;\n\tcvar_t *hashNext;\n\tcvar_t *hashPrev;\n\tint\t\t\thashIndex;\n};\n\n#define\tMAX_CVAR_VALUE_STRING\t256\n\ntypedef int\tcvarHandle_t;\n\n// the modules that run in the virtual machine can't access the cvar_t directly,\n// so they must ask for structured updates\ntypedef struct {\n\tcvarHandle_t\thandle;\n\tint\t\t\tmodificationCount;\n\tfloat\t\tvalue;\n\tint\t\t\tinteger;\n\tchar\t\tstring[MAX_CVAR_VALUE_STRING];\n} vmCvar_t;\n\n/*\n==============================================================\n\nCOLLISION DETECTION\n\n==============================================================\n*/\n\n#include \"surfaceflags.h\"\t\t\t// shared with the q3map utility\n\n// plane types are used to speed some tests\n// 0-2 are axial planes\n#define\tPLANE_X\t\t\t0\n#define\tPLANE_Y\t\t\t1\n#define\tPLANE_Z\t\t\t2\n#define\tPLANE_NON_AXIAL\t3\n\n\n/*\n=================\nPlaneTypeForNormal\n=================\n*/\n\n#define PlaneTypeForNormal(x) (x[0] == 1.0 ? PLANE_X : (x[1] == 1.0 ? PLANE_Y : (x[2] == 1.0 ? PLANE_Z : PLANE_NON_AXIAL) ) )\n\n// plane_t structure\n// !!! if this is changed, it must be changed in asm code too !!!\ntypedef struct cplane_s {\n\tvec3_t\tnormal;\n\tfloat\tdist;\n\tbyte\ttype;\t\t\t// for fast side tests: 0,1,2 = axial, 3 = nonaxial\n\tbyte\tsignbits;\t\t// signx + (signy<<1) + (signz<<2), used as lookup during collision\n\tbyte\tpad[2];\n} cplane_t;\n\n\n// a trace is returned when a box is swept through the world\ntypedef struct {\n\tqboolean\tallsolid;\t// if true, plane is not valid\n\tqboolean\tstartsolid;\t// if true, the initial point was in a solid area\n\tfloat\t\tfraction;\t// time completed, 1.0 = didn't hit anything\n\tvec3_t\t\tendpos;\t\t// final position\n\tcplane_t\tplane;\t\t// surface normal at impact, transformed to world space\n\tint\t\t\tsurfaceFlags;\t// surface hit\n\tint\t\t\tcontents;\t// contents on other side of surface hit\n\tint\t\t\tentityNum;\t// entity the contacted sirface is a part of\n} trace_t;\n\n// trace->entityNum can also be 0 to (MAX_GENTITIES-1)\n// or ENTITYNUM_NONE, ENTITYNUM_WORLD\n\n\n// markfragments are returned by CM_MarkFragments()\ntypedef struct {\n\tint\t\tfirstPoint;\n\tint\t\tnumPoints;\n} markFragment_t;\n\n\n\ntypedef struct {\n\tvec3_t\t\torigin;\n\tvec3_t\t\taxis[3];\n} orientation_t;\n\n//=====================================================================\n\n\n// in order from highest priority to lowest\n// if none of the catchers are active, bound key strings will be executed\n#define KEYCATCH_CONSOLE\t\t0x0001\n#define\tKEYCATCH_UI\t\t\t\t\t0x0002\n#define\tKEYCATCH_MESSAGE\t\t0x0004\n#define\tKEYCATCH_CGAME\t\t\t0x0008\n\n\n// sound channels\n// channel 0 never willingly overrides\n// other channels will allways override a playing sound on that channel\ntypedef enum {\n\tCHAN_AUTO,\n\tCHAN_LOCAL,\t\t// menu sounds, etc\n\tCHAN_WEAPON,\n\tCHAN_VOICE,\n\tCHAN_ITEM,\n\tCHAN_BODY,\n\tCHAN_LOCAL_SOUND,\t// chat messages, etc\n\tCHAN_ANNOUNCER\t\t// announcer voices, etc\n} soundChannel_t;\n\n\n/*\n========================================================================\n\n  ELEMENTS COMMUNICATED ACROSS THE NET\n\n========================================================================\n*/\n\n#define\tANGLE2SHORT(x)\t((int)((x)*65536/360) & 65535)\n#define\tSHORT2ANGLE(x)\t((x)*(360.0/65536))\n\n#define\tSNAPFLAG_RATE_DELAYED\t1\n#define\tSNAPFLAG_NOT_ACTIVE\t\t2\t// snapshot used during connection and for zombies\n#define SNAPFLAG_SERVERCOUNT\t4\t// toggled every map_restart so transitions can be detected\n\n//\n// per-level limits\n//\n#define\tMAX_CLIENTS\t\t\t64\t\t// absolute limit\n#define MAX_LOCATIONS\t\t64\n\n#define\tGENTITYNUM_BITS\t\t10\t\t// don't need to send any more\n#define\tMAX_GENTITIES\t\t(1<<GENTITYNUM_BITS)\n\n// entitynums are communicated with GENTITY_BITS, so any reserved\n// values that are going to be communcated over the net need to\n// also be in this range\n#define\tENTITYNUM_NONE\t\t(MAX_GENTITIES-1)\n#define\tENTITYNUM_WORLD\t\t(MAX_GENTITIES-2)\n#define\tENTITYNUM_MAX_NORMAL\t(MAX_GENTITIES-2)\n\n\n#define\tMAX_MODELS\t\t\t256\t\t// these are sent over the net as 8 bits\n#define\tMAX_SOUNDS\t\t\t256\t\t// so they cannot be blindly increased\n\n\n#define\tMAX_CONFIGSTRINGS\t1024\n\n// these are the only configstrings that the system reserves, all the\n// other ones are strictly for servergame to clientgame communication\n#define\tCS_SERVERINFO\t\t0\t\t// an info string with all the serverinfo cvars\n#define\tCS_SYSTEMINFO\t\t1\t\t// an info string for server system to client system configuration (timescale, etc)\n\n#define\tRESERVED_CONFIGSTRINGS\t2\t// game can't modify below this, only the system can\n\n#define\tMAX_GAMESTATE_CHARS\t16000\ntypedef struct {\n\tint\t\t\tstringOffsets[MAX_CONFIGSTRINGS];\n\tchar\t\tstringData[MAX_GAMESTATE_CHARS];\n\tint\t\t\tdataCount;\n} gameState_t;\n\n//=========================================================\n\n// bit field limits\n#define\tMAX_STATS\t\t\t\t16\n#define\tMAX_PERSISTANT\t\t\t16\n#define\tMAX_POWERUPS\t\t\t16\n#define\tMAX_WEAPONS\t\t\t\t16\t\t\n\n#define\tMAX_PS_EVENTS\t\t\t2\n\n#define PS_PMOVEFRAMECOUNTBITS\t6\n\n// playerState_t is the information needed by both the client and server\n// to predict player motion and actions\n// nothing outside of pmove should modify these, or some degree of prediction error\n// will occur\n\n// you can't add anything to this without modifying the code in msg.c\n\n// playerState_t is a full superset of entityState_t as it is used by players,\n// so if a playerState_t is transmitted, the entityState_t can be fully derived\n// from it.\ntypedef struct playerState_s {\n\tint\t\t\tcommandTime;\t// cmd->serverTime of last executed command\n\tint\t\t\tpm_type;\n\tint\t\t\tbobCycle;\t\t// for view bobbing and footstep generation\n\tint\t\t\tpm_flags;\t\t// ducked, jump_held, etc\n\tint\t\t\tpm_time;\n\n\tvec3_t\t\torigin;\n\tvec3_t\t\tvelocity;\n\tint\t\t\tweaponTime;\n\tint\t\t\tgravity;\n\tint\t\t\tspeed;\n\tint\t\t\tdelta_angles[3];\t// add to command angles to get view direction\n\t\t\t\t\t\t\t\t\t// changed by spawns, rotating objects, and teleporters\n\n\tint\t\t\tgroundEntityNum;// ENTITYNUM_NONE = in air\n\n\tint\t\t\tlegsTimer;\t\t// don't change low priority animations until this runs out\n\tint\t\t\tlegsAnim;\t\t// mask off ANIM_TOGGLEBIT\n\n\tint\t\t\ttorsoTimer;\t\t// don't change low priority animations until this runs out\n\tint\t\t\ttorsoAnim;\t\t// mask off ANIM_TOGGLEBIT\n\n\tint\t\t\tmovementDir;\t// a number 0 to 7 that represents the reletive angle\n\t\t\t\t\t\t\t\t// of movement to the view angle (axial and diagonals)\n\t\t\t\t\t\t\t\t// when at rest, the value will remain unchanged\n\t\t\t\t\t\t\t\t// used to twist the legs during strafing\n\n\tvec3_t\t\tgrapplePoint;\t// location of grapple to pull towards if PMF_GRAPPLE_PULL\n\n\tint\t\t\teFlags;\t\t\t// copied to entityState_t->eFlags\n\n\tint\t\t\teventSequence;\t// pmove generated events\n\tint\t\t\tevents[MAX_PS_EVENTS];\n\tint\t\t\teventParms[MAX_PS_EVENTS];\n\n\tint\t\t\texternalEvent;\t// events set on player from another source\n\tint\t\t\texternalEventParm;\n\tint\t\t\texternalEventTime;\n\n\tint\t\t\tclientNum;\t\t// ranges from 0 to MAX_CLIENTS-1\n\tint\t\t\tweapon;\t\t\t// copied to entityState_t->weapon\n\tint\t\t\tweaponstate;\n\n\tvec3_t\t\tviewangles;\t\t// for fixed views\n\tint\t\t\tviewheight;\n\n\t// damage feedback\n\tint\t\t\tdamageEvent;\t// when it changes, latch the other parms\n\tint\t\t\tdamageYaw;\n\tint\t\t\tdamagePitch;\n\tint\t\t\tdamageCount;\n\n\tint\t\t\tstats[MAX_STATS];\n\tint\t\t\tpersistant[MAX_PERSISTANT];\t// stats that aren't cleared on death\n\tint\t\t\tpowerups[MAX_POWERUPS];\t// level.time that the powerup runs out\n\tint\t\t\tammo[MAX_WEAPONS];\n\n\tint\t\t\tgeneric1;\n\tint\t\t\tloopSound;\n\tint\t\t\tjumppad_ent;\t// jumppad entity hit this frame\n\n\t// not communicated over the net at all\n\tint\t\t\tping;\t\t\t// server to game info for scoreboard\n\tint\t\t\tpmove_framecount;\t// FIXME: don't transmit over the network\n\tint\t\t\tjumppad_frame;\n\tint\t\t\tentityEventSequence;\n} playerState_t;\n\n\n//====================================================================\n\n\n//\n// usercmd_t->button bits, many of which are generated by the client system,\n// so they aren't game/cgame only definitions\n//\n#define\tBUTTON_ATTACK\t\t1\n#define\tBUTTON_TALK\t\t\t2\t\t\t// displays talk balloon and disables actions\n#define\tBUTTON_USE_HOLDABLE\t4\n#define\tBUTTON_GESTURE\t\t8\n#define\tBUTTON_WALKING\t\t16\t\t\t// walking can't just be infered from MOVE_RUN\n\t\t\t\t\t\t\t\t\t\t// because a key pressed late in the frame will\n\t\t\t\t\t\t\t\t\t\t// only generate a small move value for that frame\n\t\t\t\t\t\t\t\t\t\t// walking will use different animations and\n\t\t\t\t\t\t\t\t\t\t// won't generate footsteps\n#define BUTTON_AFFIRMATIVE\t32\n#define\tBUTTON_NEGATIVE\t\t64\n\n#define BUTTON_GETFLAG\t\t128\n#define BUTTON_GUARDBASE\t256\n#define BUTTON_PATROL\t\t512\n#define BUTTON_FOLLOWME\t\t1024\n\n#define\tBUTTON_ANY\t\t\t2048\t\t\t// any key whatsoever\n\n#define\tMOVE_RUN\t\t\t120\t\t\t// if forwardmove or rightmove are >= MOVE_RUN,\n\t\t\t\t\t\t\t\t\t\t// then BUTTON_WALKING should be set\n\n// usercmd_t is sent to the server each client frame\ntypedef struct usercmd_s {\n\tint\t\t\t\tserverTime;\n\tint\t\t\t\tangles[3];\n\tint \t\t\tbuttons;\n\tbyte\t\t\tweapon;           // weapon \n\tsigned char\tforwardmove, rightmove, upmove;\n} usercmd_t;\n\n//===================================================================\n\n// if entityState->solid == SOLID_BMODEL, modelindex is an inline model number\n#define\tSOLID_BMODEL\t0xffffff\n\ntypedef enum {\n\tTR_STATIONARY,\n\tTR_INTERPOLATE,\t\t\t\t// non-parametric, but interpolate between snapshots\n\tTR_LINEAR,\n\tTR_LINEAR_STOP,\n\tTR_SINE,\t\t\t\t\t// value = base + sin( time / duration ) * delta\n\tTR_GRAVITY\n} trType_t;\n\ntypedef struct {\n\ttrType_t\ttrType;\n\tint\t\ttrTime;\n\tint\t\ttrDuration;\t\t\t// if non 0, trTime + trDuration = stop time\n\tvec3_t\ttrBase;\n\tvec3_t\ttrDelta;\t\t\t// velocity, etc\n} trajectory_t;\n\n// entityState_t is the information conveyed from the server\n// in an update message about entities that the client will\n// need to render in some way\n// Different eTypes may use the information in different ways\n// The messages are delta compressed, so it doesn't really matter if\n// the structure size is fairly large\n\ntypedef struct entityState_s {\n\tint\t\tnumber;\t\t\t// entity index\n\tint\t\teType;\t\t\t// entityType_t\n\tint\t\teFlags;\n\n\ttrajectory_t\tpos;\t// for calculating position\n\ttrajectory_t\tapos;\t// for calculating angles\n\n\tint\t\ttime;\n\tint\t\ttime2;\n\n\tvec3_t\torigin;\n\tvec3_t\torigin2;\n\n\tvec3_t\tangles;\n\tvec3_t\tangles2;\n\n\tint\t\totherEntityNum;\t// shotgun sources, etc\n\tint\t\totherEntityNum2;\n\n\tint\t\tgroundEntityNum;\t// -1 = in air\n\n\tint\t\tconstantLight;\t// r + (g<<8) + (b<<16) + (intensity<<24)\n\tint\t\tloopSound;\t\t// constantly loop this sound\n\n\tint\t\tmodelindex;\n\tint\t\tmodelindex2;\n\tint\t\tclientNum;\t\t// 0 to (MAX_CLIENTS - 1), for players and corpses\n\tint\t\tframe;\n\n\tint\t\tsolid;\t\t\t// for client side prediction, trap_linkentity sets this properly\n\n\tint\t\tevent;\t\t\t// impulse events -- muzzle flashes, footsteps, etc\n\tint\t\teventParm;\n\n\t// for players\n\tint\t\tpowerups;\t\t// bit flags\n\tint\t\tweapon;\t\t\t// determines weapon and flash model, etc\n\tint\t\tlegsAnim;\t\t// mask off ANIM_TOGGLEBIT\n\tint\t\ttorsoAnim;\t\t// mask off ANIM_TOGGLEBIT\n\n\tint\t\tgeneric1;\n} entityState_t;\n\ntypedef enum {\n\tCA_UNINITIALIZED,\n\tCA_DISCONNECTED, \t// not talking to a server\n\tCA_AUTHORIZING,\t\t// not used any more, was checking cd key \n\tCA_CONNECTING,\t\t// sending request packets to the server\n\tCA_CHALLENGING,\t\t// sending challenge packets to the server\n\tCA_CONNECTED,\t\t// netchan_t established, getting gamestate\n\tCA_LOADING,\t\t\t// only during cgame initialization, never during main loop\n\tCA_PRIMED,\t\t\t// got gamestate, waiting for first frame\n\tCA_ACTIVE,\t\t\t// game views should be displayed\n\tCA_CINEMATIC\t\t// playing a cinematic or a static pic, not connected to a server\n} connstate_t;\n\n// font support \n\n#define GLYPH_START 0\n#define GLYPH_END 255\n#define GLYPH_CHARSTART 32\n#define GLYPH_CHAREND 127\n#define GLYPHS_PER_FONT GLYPH_END - GLYPH_START + 1\ntypedef struct {\n  int height;       // number of scan lines\n  int top;          // top of glyph in buffer\n  int bottom;       // bottom of glyph in buffer\n  int pitch;        // width for copying\n  int xSkip;        // x adjustment\n  int imageWidth;   // width of actual image\n  int imageHeight;  // height of actual image\n  float s;          // x offset in image where glyph starts\n  float t;          // y offset in image where glyph starts\n  float s2;\n  float t2;\n  qhandle_t glyph;  // handle to the shader with the glyph\n  char shaderName[32];\n} glyphInfo_t;\n\ntypedef struct {\n  glyphInfo_t glyphs [GLYPHS_PER_FONT];\n  float glyphScale;\n  char name[MAX_QPATH];\n} fontInfo_t;\n\n#define Square(x) ((x)*(x))\n\n// real time\n//=============================================\n\n\ntypedef struct qtime_s {\n\tint tm_sec;     /* seconds after the minute - [0,59] */\n\tint tm_min;     /* minutes after the hour - [0,59] */\n\tint tm_hour;    /* hours since midnight - [0,23] */\n\tint tm_mday;    /* day of the month - [1,31] */\n\tint tm_mon;     /* months since January - [0,11] */\n\tint tm_year;    /* years since 1900 */\n\tint tm_wday;    /* days since Sunday - [0,6] */\n\tint tm_yday;    /* days since January 1 - [0,365] */\n\tint tm_isdst;   /* daylight savings time flag */\n} qtime_t;\n\n\n// server browser sources\n// TTimo: AS_MPLAYER is no longer used\n#define AS_LOCAL\t\t\t0\n#define AS_MPLAYER\t\t1\n#define AS_GLOBAL\t\t\t2\n#define AS_FAVORITES\t3\n\n\n// cinematic states\ntypedef enum {\n\tFMV_IDLE,\n\tFMV_PLAY,\t\t// play\n\tFMV_EOF,\t\t// all other conditions, i.e. stop/EOF/abort\n\tFMV_ID_BLT,\n\tFMV_ID_IDLE,\n\tFMV_LOOPED,\n\tFMV_ID_WAIT\n} e_status;\n\ntypedef enum _flag_status {\n\tFLAG_ATBASE = 0,\n\tFLAG_TAKEN,\t\t\t// CTF\n\tFLAG_TAKEN_RED,\t\t// One Flag CTF\n\tFLAG_TAKEN_BLUE,\t// One Flag CTF\n\tFLAG_DROPPED\n} flagStatus_t;\n\n\n\n#define\tMAX_GLOBAL_SERVERS\t\t\t\t4096\n#define\tMAX_OTHER_SERVERS\t\t\t\t\t128\n#define MAX_PINGREQUESTS\t\t\t\t\t32\n#define MAX_SERVERSTATUSREQUESTS\t16\n\n#define SAY_ALL\t\t0\n#define SAY_TEAM\t1\n#define SAY_TELL\t2\n\n#define CDKEY_LEN 16\n#define CDCHKSUM_LEN 2\n\n\n#define LERP( a, b, w ) ( ( a ) * ( 1.0f - ( w ) ) + ( b ) * ( w ) )\n#define LUMA( red, green, blue ) ( 0.2126f * ( red ) + 0.7152f * ( green ) + 0.0722f * ( blue ) )\n\n#endif\t// __Q_SHARED_H\n"
  },
  {
    "path": "plugins/quake3/botlib/standalone.c",
    "content": "//license GPLv2+\n//this file allows botlib to link as a dynamic lib with no external dependancies\n\n#include \"q_shared.h\"\n#include \"botlib.h\"\n\nvec3_t vec3_origin;\nvoid QDECL AngleVectors (const vec3_t angles, vec3_t forward, vec3_t right, vec3_t up)\n{\n\tfloat\t\tangle;\n\tfloat\t\tsr, sp, sy, cr, cp, cy;\n\n\tangle = angles[YAW] * (M_PI*2 / 360);\n\tsy = sin(angle);\n\tcy = cos(angle);\n\tangle = angles[PITCH] * (M_PI*2 / 360);\n\tsp = sin(angle);\n\tcp = cos(angle);\n\tangle = angles[ROLL] * (M_PI*2 / 360);\n\tsr = sin(angle);\n\tcr = cos(angle);\n\n\tif (forward)\n\t{\n\t\tforward[0] = cp*cy;\n\t\tforward[1] = cp*sy;\n\t\tforward[2] = -sp;\n\t}\n\tif (right)\n\t{\n\t\tright[0] = (-1*sr*sp*cy+-1*cr*-sy);\n\t\tright[1] = (-1*sr*sp*sy+-1*cr*cy);\n\t\tright[2] = -1*sr*cp;\n\t}\n\tif (up)\n\t{\n\t\tup[0] = (cr*sp*cy+-sr*-sy);\n\t\tup[1] = (cr*sp*sy+-sr*cy);\n\t\tup[2] = cr*cp;\n\t}\n}\nvoid QDECL VectorAngles(float *forward, float *up, float *result, qboolean meshpitch)\n{\n\tfloat\tyaw, pitch, roll;\n\n\tif (forward[1] == 0 && forward[0] == 0)\n\t{\n\t\tif (forward[2] > 0)\n\t\t{\n\t\t\tpitch = -M_PI * 0.5;\n\t\t\tyaw = up ? atan2(-up[1], -up[0]) : 0;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tpitch = M_PI * 0.5;\n\t\t\tyaw = up ? atan2(up[1], up[0]) : 0;\n\t\t}\n\t\troll = 0;\n\t}\n\telse\n\t{\n\t\tyaw = atan2(forward[1], forward[0]);\n\t\tpitch = -atan2(forward[2], sqrt (forward[0]*forward[0] + forward[1]*forward[1]));\n\n\t\tif (up)\n\t\t{\n\t\t\tvec_t cp = cos(pitch), sp = sin(pitch);\n\t\t\tvec_t cy = cos(yaw), sy = sin(yaw);\n\t\t\tvec3_t tleft, tup;\n\t\t\ttleft[0] = -sy;\n\t\t\ttleft[1] = cy;\n\t\t\ttleft[2] = 0;\n\t\t\ttup[0] = sp*cy;\n\t\t\ttup[1] = sp*sy;\n\t\t\ttup[2] = cp;\n\t\t\troll = -atan2(DotProduct(up, tleft), DotProduct(up, tup));\n\t\t}\n\t\telse\n\t\t\troll = 0;\n\t}\n\n\tpitch *= 180 / M_PI;\n\tyaw *= 180 / M_PI;\n\troll *= 180 / M_PI;\n//\tif (meshpitch)\n//\t\tpitch *= r_meshpitch.value;\n\tif (pitch < 0)\n\t\tpitch += 360;\n\tif (yaw < 0)\n\t\tyaw += 360;\n\tif (roll < 0)\n\t\troll += 360;\n\n\tresult[0] = pitch;\n\tresult[1] = yaw;\n\tresult[2] = roll;\n}\n\nvec_t QDECL VectorNormalize2 (const vec3_t v, vec3_t out)\n{\n\tfloat\tlength, ilength;\n\n\tlength = v[0]*v[0] + v[1]*v[1] + v[2]*v[2];\n\tlength = sqrt (length);\n\tif (length)\n\t{\n\t\tilength = 1/length;\n\t\tout[0] = v[0]*ilength;\n\t\tout[1] = v[1]*ilength;\n\t\tout[2] = v[2]*ilength;\n\t}\n\telse\n\t{\n\t\tVectorClear (out);\n\t}\n\n\treturn length;\n}\nfloat QDECL VectorNormalize (vec3_t v)\n{\n\treturn VectorNormalize2(v,v);\n}\n\nvoid\tQDECL Com_sprintf (char *dest, int size, const char *fmt, ...)\n{\n\tva_list\t\targptr;\n\n\tva_start (argptr, fmt);\n\tvsnprintf (dest, size, fmt, argptr);\n\tva_end (argptr);\n}\n\nint Q_strncasecmp (const char *s1, const char *s2, int n)\n{\n\tint\t\tc1, c2;\n\n\twhile (1)\n\t{\n\t\tc1 = *s1++;\n\t\tc2 = *s2++;\n\n\t\tif (!n--)\n\t\t\treturn 0;\t\t// strings are equal until end point\n\n\t\tif (c1 != c2)\n\t\t{\n\t\t\tif (c1 >= 'a' && c1 <= 'z')\n\t\t\t\tc1 -= ('a' - 'A');\n\t\t\tif (c2 >= 'a' && c2 <= 'z')\n\t\t\t\tc2 -= ('a' - 'A');\n\t\t\tif (c1 != c2)\n\t\t\t{\t// strings not equal\n\t\t\t\tif (c1 > c2)\n\t\t\t\t\treturn 1;\t\t// strings not equal\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t}\n\t\tif (!c1)\n\t\t\treturn 0;\t\t// strings are equal\n//\t\ts1++;\n//\t\ts2++;\n\t}\n\n\treturn -1;\n}\nint Q_strcasecmp (const char *s1, const char *s2)\n{\n\treturn Q_strncasecmp (s1, s2, 0x7fffffff);\n}\n\nint QDECL Q_stricmp (const char *s1, const char *s2)\n{\n\treturn Q_strncasecmp (s1, s2, 0x7fffffff);\n}\n\nvoid QDECL Q_strncpyz(char *d, const char *s, int n)\n{\n\tint i;\n\tn--;\n\tif (n < 0)\n\t\treturn;\t//this could be an error\n\n\tfor (i=0; *s; i++)\n\t{\n\t\tif (i == n)\n\t\t\tbreak;\n\t\t*d++ = *s++;\n\t}\n\t*d='\\0';\n}\n\n/*\nchar\t*QDECL va(char *format, ...)\n{\n#define VA_BUFFER_SIZE 1024\n\tva_list\t\targptr;\n\tstatic char\t\tstring[VA_BUFFER_SIZE];\n\n\tva_start (argptr, format);\n\tvsnprintf (string,sizeof(string)-1, format,argptr);\n\tva_end (argptr);\n\n\treturn string;\n}\n*/"
  },
  {
    "path": "plugins/quake3/botlib/surfaceflags.h",
    "content": "/*\n===========================================================================\nCopyright (C) 1999-2005 Id Software, Inc.\n\nThis file is part of Quake III Arena source code.\n\nQuake III Arena source code is free software; you can redistribute it\nand/or modify it under the terms of the GNU General Public License as\npublished by the Free Software Foundation; either version 2 of the License,\nor (at your option) any later version.\n\nQuake III Arena source code is distributed in the hope that it will be\nuseful, but WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Quake III Arena source code; if not, write to the Free Software\nFoundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n===========================================================================\n*/\n//\n// This file must be identical in the quake and utils directories\n\n// contents flags are seperate bits\n// a given brush can contribute multiple content bits\n\n// these definitions also need to be in q_shared.h!\n\n#define\tCONTENTS_SOLID\t\t\t1\t\t// an eye is never valid in a solid\n#define\tCONTENTS_LAVA\t\t\t8\n#define\tCONTENTS_SLIME\t\t\t16\n#define\tCONTENTS_WATER\t\t\t32\n#define\tCONTENTS_FOG\t\t\t64\n\n#define CONTENTS_NOTTEAM1\t\t0x0080\n#define CONTENTS_NOTTEAM2\t\t0x0100\n#define CONTENTS_NOBOTCLIP\t\t0x0200\n\n#define\tCONTENTS_AREAPORTAL\t\t0x8000\n\n#define\tCONTENTS_PLAYERCLIP\t\t0x10000\n#define\tCONTENTS_MONSTERCLIP\t0x20000\n//bot specific contents types\n#define\tCONTENTS_TELEPORTER\t\t0x40000\n#define\tCONTENTS_JUMPPAD\t\t0x80000\n#define CONTENTS_CLUSTERPORTAL\t0x100000\n#define CONTENTS_DONOTENTER\t\t0x200000\n#define CONTENTS_BOTCLIP\t\t0x400000\n#define CONTENTS_MOVER\t\t\t0x800000\n\n#define\tCONTENTS_ORIGIN\t\t\t0x1000000\t// removed before bsping an entity\n\n#define\tCONTENTS_BODY\t\t\t0x2000000\t// should never be on a brush, only in game\n#define\tCONTENTS_CORPSE\t\t\t0x4000000\n#define\tCONTENTS_DETAIL\t\t\t0x8000000\t// brushes not used for the bsp\n#define\tCONTENTS_STRUCTURAL\t\t0x10000000\t// brushes used for the bsp\n#define\tCONTENTS_TRANSLUCENT\t0x20000000\t// don't consume surface fragments inside\n#define\tCONTENTS_TRIGGER\t\t0x40000000\n#define\tCONTENTS_NODROP\t\t\t0x80000000\t// don't leave bodies or items (death fog, lava)\n\n#define\tSURF_NODAMAGE\t\t\t0x1\t\t// never give falling damage\n#define\tSURF_SLICK\t\t\t\t0x2\t\t// effects game physics\n#define\tSURF_SKY\t\t\t\t0x4\t\t// lighting from environment map\n#define\tSURF_LADDER\t\t\t\t0x8\n#define\tSURF_NOIMPACT\t\t\t0x10\t// don't make missile explosions\n#define\tSURF_NOMARKS\t\t\t0x20\t// don't leave missile marks\n#define\tSURF_FLESH\t\t\t\t0x40\t// make flesh sounds and effects\n#define\tSURF_NODRAW\t\t\t\t0x80\t// don't generate a drawsurface at all\n#define\tSURF_HINT\t\t\t\t0x100\t// make a primary bsp splitter\n#define\tSURF_SKIP\t\t\t\t0x200\t// completely ignore, allowing non-closed brushes\n#define\tSURF_NOLIGHTMAP\t\t\t0x400\t// surface doesn't need a lightmap\n#define\tSURF_POINTLIGHT\t\t\t0x800\t// generate lighting info at vertexes\n#define\tSURF_METALSTEPS\t\t\t0x1000\t// clanking footsteps\n#define\tSURF_NOSTEPS\t\t\t0x2000\t// no footstep sounds\n#define\tSURF_NONSOLID\t\t\t0x4000\t// don't collide against curves with this set\n#define\tSURF_LIGHTFILTER\t\t0x8000\t// act as a light filter during q3map -light\n#define\tSURF_ALPHASHADOW\t\t0x10000\t// do per-pixel light shadow casting in q3map\n#define\tSURF_NODLIGHT\t\t\t0x20000\t// don't dlight even if solid (solid lava, skies)\n#define SURF_DUST\t\t\t\t0x40000 // leave a dust trail when walking on this surface\n"
  },
  {
    "path": "plugins/quake3/clq3_cg.c",
    "content": "#include \"q3common.h\"\n#if defined(VM_CG) && defined(HAVE_CLIENT)\n#include \"clq3defs.h\"\n#include \"com_mesh.h\"\n\n//cl_ui.c\nvoid CG_Command_f(void);\n\n#define\tCGAME_IMPORT_API_VERSION\t4\n\nstatic model_t\t\t*box_model;\ncvar_t *cl_shownet_ptr, *cl_c2sdupe_ptr, *cl_nodelta_ptr;\n\ntypedef enum {\n\tCG_PRINT,\n\tCG_ERROR,\n\tCG_MILLISECONDS,\n\tCG_CVAR_REGISTER,\n\tCG_CVAR_UPDATE,\n\tCG_CVAR_SET,\n\tCG_CVAR_VARIABLESTRINGBUFFER,\n\tCG_ARGC,\n\tCG_ARGV,\n\tCG_ARGS,\n\tCG_FS_FOPENFILE,\t\t\t\t//10\n\tCG_FS_READ,\n\tCG_FS_WRITE,\n\tCG_FS_FCLOSEFILE,\n\tCG_SENDCONSOLECOMMAND,\n\tCG_ADDCOMMAND,\n\tCG_SENDCLIENTCOMMAND,\n\tCG_UPDATESCREEN,\n\tCG_CM_LOADMAP,\n\tCG_CM_NUMINLINEMODELS,\n\tCG_CM_INLINEMODEL,\t\t\t\t//20\n\tCG_CM_LOADMODEL,\n\tCG_CM_TEMPBOXMODEL,\n\tCG_CM_POINTCONTENTS,\n\tCG_CM_TRANSFORMEDPOINTCONTENTS,\n\tCG_CM_BOXTRACE,\n\tCG_CM_TRANSFORMEDBOXTRACE,\n\tCG_CM_MARKFRAGMENTS,\n\tCG_S_STARTSOUND,\n\tCG_S_STARTLOCALSOUND,\n\tCG_S_CLEARLOOPINGSOUNDS,\t\t//30\n\tCG_S_ADDLOOPINGSOUND,\n\tCG_S_UPDATEENTITYPOSITION,\n\tCG_S_RESPATIALIZE,\n\tCG_S_REGISTERSOUND,\n\tCG_S_STARTBACKGROUNDTRACK,\n\tCG_R_LOADWORLDMAP,\n\tCG_R_REGISTERMODEL,\n\tCG_R_REGISTERSKIN,\n\tCG_R_REGISTERSHADER,\n\tCG_R_CLEARSCENE,\t\t\t\t//40\n\tCG_R_ADDREFENTITYTOSCENE,\n\tCG_R_ADDPOLYTOSCENE,\n\tCG_R_ADDLIGHTTOSCENE,\n\tCG_R_RENDERSCENE,\n\tCG_R_SETCOLOR,\n\tCG_R_DRAWSTRETCHPIC,\n\tCG_R_MODELBOUNDS,\n\tCG_R_LERPTAG,\n\tCG_GETGLCONFIG,\n\tCG_GETGAMESTATE,\t\t\t\t//50\n\tCG_GETCURRENTSNAPSHOTNUMBER,\n\tCG_GETSNAPSHOT,\n\tCG_GETSERVERCOMMAND,\n\tCG_GETCURRENTCMDNUMBER,\n\tCG_GETUSERCMD,\n\tCG_SETUSERCMDVALUE,\n\tCG_R_REGISTERSHADERNOMIP,\n\tCG_MEMORY_REMAINING,\n\tCG_R_REGISTERFONT,\n\tCG_KEY_ISDOWN,\t\t\t\t\t//60\n\tCG_KEY_GETCATCHER,\n\tCG_KEY_SETCATCHER,\n\tCG_KEY_GETKEY,\n \tCG_PC_ADD_GLOBAL_DEFINE,\n\tCG_PC_LOAD_SOURCE,\n\tCG_PC_FREE_SOURCE,\n\tCG_PC_READ_TOKEN,\n\tCG_PC_SOURCE_FILE_AND_LINE,\n\tCG_S_STOPBACKGROUNDTRACK,\n\tCG_REAL_TIME,\t\t\t\t\t//70\n\tCG_SNAPVECTOR,\n\tCG_REMOVECOMMAND,\n\tCG_R_LIGHTFORPOINT,\t\t\t\t//73\n\tCG_CIN_PLAYCINEMATIC,\t\t\t//74\n\tCG_CIN_STOPCINEMATIC,\t\t\t//75\n\tCG_CIN_RUNCINEMATIC,\t\t\t//76\n\tCG_CIN_DRAWCINEMATIC,\t\t\t//77\n\tCG_CIN_SETEXTENTS,\t\t\t\t//78\n\tCG_R_REMAP_SHADER,\t\t\t\t//79\n\tCG_S_ADDREALLOOPINGSOUND,\t\t//80\n\tCG_S_STOPLOOPINGSOUND,\t\t\t//81\n\n\tCG_CM_TEMPCAPSULEMODEL,\t\t\t//82\n\tCG_CM_CAPSULETRACE,\t\t\t\t//83\n\tCG_CM_TRANSFORMEDCAPSULETRACE,\t//84\n\tCG_R_ADDADDITIVELIGHTTOSCENE,\t//85\n\tCG_GET_ENTITY_TOKEN,\t\t\t//86\n\tCG_R_ADDPOLYSTOSCENE,\t\t\t//87\n\tCG_R_INPVS,\t\t\t\t\t\t//88\n\t// 1.32\n\tCG_FS_SEEK,\t\t\t\t\t\t//89\n\n\tCG_MEMSET = 100,\n\tCG_MEMCPY,\n\tCG_STRNCPY,\n\tCG_SIN,\n\tCG_COS,\n\tCG_ATAN2,\n\tCG_SQRT,\n\tCG_FLOOR,\n\tCG_CEIL,\n\tCG_TESTPRINTINT,\n\tCG_TESTPRINTFLOAT,\n\tCG_ACOS,\n\n\n\t/*CG_FTE_FINDPARTICLEEFFECT = 200,\n\tCG_FTE_SPAWNPARTICLEEFFECT,\n\tCG_FTE_SPAWNPARTICLETRAIL,\n\tCG_FTE_FREEPARTICLESTATE*/\n} cgameImport_t;\n\n/*\nint CG_FTE_FINDPARTICLEEFFECT(char *particleeffectname) = #200;\nint CG_FTE_SPAWNPARTICLEEFFECT(vec3_t pos, vec3_t dir, float count, int effectnum, void *pstate) = #201;\nint CG_FTE_SPAWNPARTICLETRAIL(vec3_t start, vec3_t end, int effectnum, void *pstate) = #202;\nvoid CG_FTE_FREEPARTICLESTATE(void *pstate) = #203;\n*/\n\n/*\n==================================================================\n\nfunctions exported to the main executable\n\n==================================================================\n*/\n\ntypedef enum {\n\tCG_INIT,\n//\tvoid CG_Init( int serverMessageNum, int serverCommandSequence, int clientNum )\n\t// called when the level loads or when the renderer is restarted\n\t// all media should be registered at this time\n\t// cgame will display loading status by calling SCR_Update, which\n\t// will call CG_DrawInformation during the loading process\n\t// reliableCommandSequence will be 0 on fresh loads, but higher for\n\t// demos, tourney restarts, or vid_restarts\n\n\tCG_SHUTDOWN,\n//\tvoid (*CG_Shutdown)( void );\n\t// oportunity to flush and close any open files\n\n\tCG_CONSOLE_COMMAND,\n//\tqboolean (*CG_ConsoleCommand)( void );\n\t// a console command has been issued locally that is not recognized by the\n\t// main game system.\n\t// use Cmd_Argc() / Cmd_Argv() to read the command, return qfalse if the\n\t// command is not known to the game\n\n\tCG_DRAW_ACTIVE_FRAME,\n//\tvoid (*CG_DrawActiveFrame)( int serverTime, stereoFrame_t stereoView, qboolean demoPlayback );\n\t// Generates and draws a game scene and status information at the given time.\n\t// If demoPlayback is set, local movement prediction will not be enabled\n\n\tCG_CROSSHAIR_PLAYER,\n//\tint (*CG_CrosshairPlayer)( void );\n\n\tCG_LAST_ATTACKER,\n//\tint (*CG_LastAttacker)( void );\n\n\tCG_KEY_EVENT,\n//\tvoid\t(*CG_KeyEvent)( int key, qboolean down );\n\n\tCG_MOUSE_EVENT,\n//\tvoid\t(*CG_MouseEvent)( int dx, int dy );\n\tCG_EVENT_HANDLING\n//\tvoid (*CG_EventHandling)(int type);\n} cgameExport_t;\n\n\n\n\n\n\n\n\nunsigned int Contents_To_Q3(unsigned int fte)\n{\n\tint ret = 0;\n\n\tif (fte & FTECONTENTS_SOLID)\t//should use q3 constants.\n\t\tret |= 1;\n\tif (fte & FTECONTENTS_WATER)\t//should use q3 constants.\n\t\tret |= 32;\n\tif (fte & FTECONTENTS_SLIME)\t//should use q3 constants.\n\t\tret |= 16;\n\tif (fte & FTECONTENTS_LAVA)\t//should use q3 constants.\n\t\tret |= 8;\n\tif (fte & FTECONTENTS_SKY)\t//should use q3 constants.\n\t\tret |= 0x80000000;\n\n\treturn ret;\n}\nunsigned int Contents_From_Q3(unsigned int Q3)\n{\n\tint ret = 0;\n\n\tif (Q3 & 1)\t//should use q3 constants.\n\t\tret |= FTECONTENTS_SOLID;\n\tif (Q3 & 32)\t//should use q3 constants.\n\t\tret |= FTECONTENTS_WATER;\n\tif (Q3 & 16)\t//should use q3 constants.\n\t\tret |= FTECONTENTS_SLIME;\n\tif (Q3 & 8)\t//should use q3 constants.\n\t\tret |= FTECONTENTS_LAVA;\n\tif (Q3 & 0x80000000)\t//should use q3 constants.\n\t\tret |= FTECONTENTS_SKY;\n\n\treturn ret;\n}\n\ntypedef struct {\n\tint\t\t\tstringOffsets[MAX_Q3_CONFIGSTRINGS];\n\tchar\t\tstringData[MAX_Q3_GAMESTATE_CHARS];\n\tint\t\t\tdataCount;\n} gameState_t;\nstatic gameState_t cggamestate;\n\nvoid CG_ClearGameState(void)\n{\n\tmemset(&cggamestate, 0, sizeof(cggamestate));\n}\n\nvoid CG_InsertIntoGameState(int num, const char *str)\n{\n\tif (num < 5)\n\t{\n\t\tCon_DPrintf(\"%i: %s\", num, str);\n\t}\n\n\tif (num == CFGSTR_SYSINFO)\n\t{\n\t\t//check some things.\n\t\tccs.servercount = atoi(worldfuncs->GetInfoKey(str, \"sv_serverid\"));\n\t}\n\n\tif (cggamestate.dataCount + strlen(str)+1 > countof(cggamestate.stringData))\n\t{\n\t\tchar oldstringData[countof(cggamestate.stringData)];\n\t\tint i;\n\t\tchar *oldstr;\n\t\t//copy the old strings to a temporary buffer\n\t\tmemcpy(oldstringData, cggamestate.stringData, countof(cggamestate.stringData));\n\t\tcggamestate.dataCount = 0;\n\t\tfor (i = 0; i < countof(cggamestate.stringOffsets); i++)\n\t\t{\n\t\t\toldstr = oldstringData+cggamestate.stringOffsets[i];\n\t\t\tif (*oldstr)\n\t\t\t{\n\t\t\t\tif (cggamestate.dataCount + strlen(oldstr)+1 > countof(cggamestate.stringData))\n\t\t\t\t\tplugfuncs->EndGame(\"Too much configstring text\\n\");\n\n\t\t\t\tcggamestate.dataCount+=1;\n\t\t\t\tstrcpy(cggamestate.stringData+cggamestate.dataCount, oldstr);\n\t\t\t\tcggamestate.stringOffsets[i] = cggamestate.dataCount;\n\t\t\t\tcggamestate.dataCount += strlen(oldstr);\n\t\t\t}\n\t\t\telse\n\t\t\t\tcggamestate.stringOffsets[i] = 0;\n\t\t}\n\t}\n\n\tif (!*str)\n\t{\n\t\tcggamestate.stringOffsets[num] = 0;\n\t\treturn;\n\t}\n\n\tcggamestate.dataCount+=1;\n\tstrcpy(cggamestate.stringData+cggamestate.dataCount, str);\n\tcggamestate.stringOffsets[num] = cggamestate.dataCount;\n\tcggamestate.dataCount += strlen(str);\n}\n\nconst char *CG_GetConfigString(int num)\n{\n\tif ((unsigned)num >= countof(cggamestate.stringOffsets))\n\t\treturn \"\";\n\treturn cggamestate.stringData + cggamestate.stringOffsets[num];\n}\n\nint CG_GetGameState(gameState_t *gs)\n{\n\tmemcpy(gs, &cggamestate, sizeof(gameState_t));\n\treturn sizeof(gameState_t);\n}\n\nstatic int CGQ3_GetCurrentCmdNumber(void)\n{\t//Q3 sequences are 1-based, so 1<=idx<=latestsequence are valid\n\t//FTE's sequences are 0-based, so 0<=idx<latestsequence are valid\n\treturn inputfuncs->GetMoveCount()-1;\n}\nstatic qboolean CGQ3_GetUserCmd(int cmdNumber, q3usercmd_t *ucmd)\n{\n\t//q3 prediction uses for(int frame = CGQ3_GetCurrentCmdNumber() - CMD_BACKUP{64} + 1; frame <= CGQ3_GetCurrentCmdNumber(); frame++)\n\t//skipping any that are older than the snapshot's time.\n\n\tusercmd_t *cmd;\n\n\t//q3 does not do partials.\n\tif (cmdNumber < 0)\n\t\treturn false;\t//grr, stoopid q3.\n\tif (cmdNumber >= inputfuncs->GetMoveCount())\n\t\tplugfuncs->EndGame(\"CLQ3_GetUserCmd: %i >= %i\", cmdNumber, inputfuncs->GetMoveCount());\n\n\tcmd = inputfuncs->GetMoveEntry(cmdNumber);\n\tif (!cmd)\n\t\treturn false;\n\tucmd->angles[0] = cmd->angles[0];\n\tucmd->angles[1] = cmd->angles[1];\n\tucmd->angles[2] = cmd->angles[2];\n\tucmd->serverTime = cmd->servertime;\n\tucmd->forwardmove = cmd->forwardmove;\n\tucmd->rightmove = cmd->sidemove;\n\tucmd->upmove = cmd->upmove;\n\tucmd->buttons = cmd->buttons;\n\tucmd->weapon = cmd->weapon;\n\n\n\treturn true;\n}\n\nvm_t *cgvm;\nstatic const char *mapentspointer;\n\nextern int keycatcher;\n\nqboolean CG_GetServerCommand(int cmdnum)\n{\n\tstatic char bigconfigstring[65536];\n\tchar *arg0;\n\n\t//quote from cgame code:\n\t// get the gamestate from the client system, which will have the\n\t// new configstring already integrated\n\n\tchar *str = ccs.serverCommands[cmdnum & Q3TEXTCMD_MASK];\n\n\tCon_DPrintf(\"Dispaching %s\\n\", str);\n\tcmdfuncs->TokenizeString(str);\n\targ0 = cmdfuncs->Argv(0, NULL, 0);\n\n\tif (!strcmp(arg0, \"bcs0\"))\n\t{\t//start\n\t\tQ_snprintfz(bigconfigstring, sizeof(bigconfigstring), \"cs %s \\\"%s\", cmdfuncs->Argv(1, NULL, 0), cmdfuncs->Argv(2, NULL, 0));\n\t\treturn false;\n\t}\n\tif (!strcmp(arg0, \"bcs1\"))\n\t{\t//continuation\n\t\tQ_strncatz(bigconfigstring, cmdfuncs->Argv(2, NULL, 0), sizeof(bigconfigstring));\n\t\treturn false;\n\t}\n\tif (!strcmp(arg0, \"bcs2\"))\n\t{\t//end\n\t\tQ_strncatz(bigconfigstring, cmdfuncs->Argv(2, NULL, 0), sizeof(bigconfigstring));\n\t\tQ_strncatz(bigconfigstring, \"\\\"\", sizeof(bigconfigstring));\n\t\tcmdfuncs->TokenizeString(bigconfigstring);\n\t}\n\n\tif (!strcmp(arg0, \"cs\"))\n\t\tCG_InsertIntoGameState(atoi(cmdfuncs->Argv(1, NULL, 0)), cmdfuncs->Argv(2, NULL, 0));\n\telse if (!strcmp(arg0, \"map_restart\"))\n\t\tclientfuncs->ClearNotify();\n\telse if (!strcmp(arg0, \"disconnect\"))\n\t\tplugfuncs->EndGame(\"Server disconnected - %s\", (cmdfuncs->Argc()>1)?cmdfuncs->Argv(1, NULL, 0):\"No reason given\");\n\treturn true;\n}\n\n\ntypedef struct {\n\tint\t\tfirstPoint;\n\tint\t\tnumPoints;\n} q3markFragment_t;\ntypedef struct {\n\tfloat *points;\n\tsize_t maxpoints;\n\tsize_t numpoints;\n\tq3markFragment_t *frags;\n\tsize_t maxfrags;\n\tsize_t numfrags;\n} q3markFragment_ctx_t;\nstatic void CG_MarkFragments_Callback(void *vctx, vec3_t *fte_restrict points, size_t numtris, shader_t *shader)\n{\n\tq3markFragment_ctx_t *ctx = vctx;\n\tsize_t i;\n\tif (numtris > ctx->maxfrags-ctx->numfrags)\n\t\tnumtris = ctx->maxfrags-ctx->numfrags;\n\tif (numtris > (ctx->maxpoints-ctx->numpoints)/3)\n\t\tnumtris = (ctx->maxpoints-ctx->numpoints)/3;\n\tfor (i = 0; i < numtris; i++)\n\t{\n\t\tctx->frags[ctx->numfrags].numPoints = 3;\n\t\tctx->frags[ctx->numfrags].firstPoint = ctx->numpoints;\n\t\tVectorCopy(points[0], ctx->points+3*(ctx->numpoints+0));\n\t\tVectorCopy(points[1], ctx->points+3*(ctx->numpoints+1));\n\t\tVectorCopy(points[2], ctx->points+3*(ctx->numpoints+2));\n\t\tpoints += 3;\n\t\tctx->numfrags += 1;\n\t\tctx->numpoints += 3;\n\t}\n}\n\nint CG_MarkFragments( int numPoints, const vec3_t *points, const vec3_t projection,\n\t\t\t\t   int maxPoints, float *pointBuffer, int maxFragments, q3markFragment_t *fragmentBuffer )\n{\n\tvec3_t center;\n\tvec3_t axis[3];\n\tvec3_t p[4];\n\tint i;\n\tfloat radius;\n\tq3markFragment_ctx_t ctx;\n\n\tif (numPoints != 4)\n\t\treturn 0;\n\n\t/*\n\tq3 gamecode includes something like this\n\n\toriginalPoints[0][i] = origin[i] - radius * axis[1][i] - radius * axis[2][i];\n\toriginalPoints[1][i] = origin[i] + radius * axis[1][i] - radius * axis[2][i];\n\toriginalPoints[2][i] = origin[i] + radius * axis[1][i] + radius * axis[2][i];\n\toriginalPoints[3][i] = origin[i] - radius * axis[1][i] + radius * axis[2][i];\n\n\tWe want that origional axis and the origin\n\taxis[0] is given in the 'projection' parameter.\n\n\tYes, reversing this stuff means that we'll have no support for triangles.\n\t*/\n\n\tVectorClear(center);\n\tVectorMA(center, 0.25, points[0], center);\n\tVectorMA(center, 0.25, points[1], center);\n\tVectorMA(center, 0.25, points[2], center);\n\tVectorMA(center, 0.25, points[3], center);\n\n\tVectorSubtract(points[0], center, p[0]);\n\tVectorSubtract(points[1], center, p[1]);\n\tVectorSubtract(points[2], center, p[2]);\n\tVectorSubtract(points[3], center, p[3]);\n\n\tfor (i = 0; i < 3; i++)\n\t{\n\t\taxis[1][i] = (p[2][i]-p[1][i]);\n\t\taxis[2][i] = (p[3][i]-p[2][i]);\n\t}\n\n\tradius = VectorNormalize(axis[1]);\n\tVectorNormalize(axis[2]);\n\tVectorNormalize2(projection, axis[0]);\n\n\tctx.points = pointBuffer;\n\tctx.maxpoints = maxPoints;\n\tctx.numpoints = 0;\n\tctx.frags = fragmentBuffer;\n\tctx.numfrags = 0;\n\tctx.maxfrags = maxFragments;\n\tscenefuncs->ClipDecal(ccs.worldmodel, center, axis[0], axis[1], axis[2], radius, 0,0, CG_MarkFragments_Callback, &ctx);\n\treturn ctx.numfrags;\n}\n\n\n\n//called by the sound code.\nstatic struct\n{\n\tunsigned int entnum;\n\tvec3_t origin;\n//\tvec3_t velocity;\n\tsfx_t *sfx;\n\tqboolean ispersistent;\n} *loopers;\nstatic size_t numloopers;\nstatic size_t maxloopers;\nunsigned int CG_GatherLoopingSounds(vec3_t *positions, unsigned int *entnums, sfx_t **sounds, unsigned int max)\n{\n\tsize_t i;\n\tif (max > numloopers)\n\t\tmax = numloopers;\n\tfor (i = 0; i < max; i++)\n\t{\n\t\tentnums[i] = loopers[i].entnum;\n\t\tVectorCopy(loopers[i].origin, positions[i]);\n\t\tsounds[i] = loopers[i].sfx;\n\t}\n\treturn i;\n}\nstatic void CG_StopLoopingSounds(unsigned int entnum)\n{\n\tsize_t i;\n\tfor (i = 0; i < numloopers; i++)\n\t{\n\t\tif (loopers[i].entnum == entnum)\n\t\t\tbreak;\n\t}\n\tif (i == numloopers)\n\t\treturn;\n\tloopers[i] = loopers[numloopers-1];\n\tnumloopers--;\n}\nstatic void CG_StartLoopingSounds(unsigned int entnum, float *origin, float *velocity, int range, const char *soundname, float volume, qboolean persistent)\n{\n\tsize_t i;\n\tfor (i = 0; i < numloopers; i++)\n\t{\n\t\tif (loopers[i].entnum == entnum)\n\t\t\tbreak;\n\t}\n\n\tif (i == numloopers)\n\t{\n\t\tif (numloopers == maxloopers)\n\t\t\tZ_ReallocElements((void**)&loopers, &maxloopers, maxloopers+1, sizeof(*loopers));\n\t\tnumloopers++;\n\t}\n\tloopers[i].entnum = entnum;\n\tVectorCopy(origin, loopers[i].origin);\n\t//VectorCopy(velocity, loopers[i].velocity);\n\tloopers[i].sfx = audiofuncs->PrecacheSound(soundname);\n\tloopers[i].ispersistent = persistent;\n//\tloopers[i].range = range;\n//\tloopers[i].volume = volume;\n}\n\nstatic void CG_ClearLoopingSounds(qboolean clearall)\n{\n\tif (clearall)\n\t\tnumloopers = 0;\n\telse\n\t{\n\t\tsize_t i;\n\t\tfor (i = 0; i < numloopers; )\n\t\t{\n\t\t\tif (!loopers[i].ispersistent)\n\t\t\t{\n\t\t\t\tloopers[i] = loopers[numloopers-1];\n\t\t\t\tnumloopers--;\n\t\t\t}\n\t\t\telse\n\t\t\t\ti++;\n\t\t}\n\t}\n}\n\n/* same principle as the looper code above */\nstatic struct\n{\n\tunsigned int entnum;\n\tvec3_t origin;\n} *entsounds;\nstatic size_t numentsounds;\nstatic size_t maxentsounds;\n\n/* called by CG_S_UPDATEENTITYPOSITION to inform us of an ent its actual position */\nstatic void CG_UpdateEntityPosition(unsigned int entnum, float *origin)\n{\n\tsize_t i;\n\n\tif (entnum == ENTITYNUM_NONE)\n\t\treturn;\n\n\tfor (i = 0; i < numentsounds; i++)\n\t{\n\t\tif (entsounds[i].entnum == entnum)\n\t\t\tbreak;\n\t}\n\n\t/* not present in list. */\n\tif (i == numentsounds)\n\t{\n\t\tif (numentsounds == maxentsounds)\n\t\t\tZ_ReallocElements((void**)&entsounds, & maxentsounds, maxentsounds+1, sizeof(*entsounds));\n\t\tnumentsounds++;\n\n\t\tentsounds[i].entnum = entnum;\n\t}\n\n\tVectorCopy(origin, entsounds[i].origin);\n}\n\n/* returns the last updated position of an entity. */\nstatic float* CG_GetEntityPosition(unsigned int entnum)\n{\n\tint i;\n\n\tfor (i = 0; i < numentsounds; i++)\n\t{\n\t\tif (entsounds[i].entnum == entnum) {\n\t\t\treturn entsounds[i].origin;\n\t\t}\n\t}\n\n\tCon_Printf(\"CG_GetEntityPosition: entity num %i not known.\\n\", entnum);\n\treturn NULL;\n}\n\n\nint VM_LerpTag(float *out, model_t *model, int f1, int f2, float l2, char *tagname);\n\n#define VALIDATEPOINTER(o,l) if ((int)o + l >= mask || VM_POINTER(o) < offset) plugfuncs->EndGame(\"Call to cgame trap %u passes invalid pointer\\n\", (unsigned int)fn);\t//out of bounds.\n\nstatic qintptr_t CG_SystemCalls(void *offset, quintptr_t mask, qintptr_t fn, const qintptr_t *arg)\n{\n\tint ret=0;\n\n\t//Remember to range check pointers.\n\t//The QVM must not be allowed to write to anything outside it's memory.\n\t//This includes getting the exe to copy it for it.\n\n\t//don't bother with reading, as this isn't a virus risk.\n\t//could be a cheat risk, but hey.\n\n\t//make sure that any called functions are also range checked.\n\t//like reading from files copies names into alternate buffers, allowing stack screwups.\n//OutputDebugString(va(\"cl_cg: %i\\n\", fn));\n\tswitch((cgameImport_t)fn)\n\t{\n\tcase CG_PRINT:\n\t\t{\n\t\t\tconst char *text = VM_POINTER(arg[0]);\n\t\t\t\tCon_Printf(\"%s\", text);\n\t\t}\n\t\tbreak;\n\tcase CG_ERROR:\n\t\tplugfuncs->EndGame(\"cgame: %s\", (char*)VM_POINTER(arg[0]));\n\t\tbreak;\n\n\tcase CG_ARGC:\n\t\tVM_LONG(ret) = cmdfuncs->Argc();\n\t\tbreak;\n\tcase CG_ARGV:\n\t\tVALIDATEPOINTER(arg[1], arg[2]);\n\t\tcmdfuncs->Argv(VM_LONG(arg[0]), VM_POINTER(arg[1]), VM_LONG(arg[2]));\n\t\tbreak;\n\tcase CG_ARGS:\n\t\tVALIDATEPOINTER(arg[0], arg[1]);\n\t\tcmdfuncs->Args(VM_POINTER(arg[0]), VM_LONG(arg[1]));\n\t\tbreak;\n\tcase CG_CVAR_REGISTER:\n\t\tif (arg[0])\n\t\t\tVALIDATEPOINTER(arg[0], sizeof(q3vmcvar_t));\n\t\treturn VMQ3_Cvar_Register(VM_POINTER(arg[0]), VM_POINTER(arg[1]), VM_POINTER(arg[2]), VM_LONG(arg[3]));\n\tcase CG_CVAR_UPDATE:\n\t\tVALIDATEPOINTER(arg[0], sizeof(q3vmcvar_t));\n\t\treturn VMQ3_Cvar_Update(VM_POINTER(arg[0]));\n\n\tcase CG_CVAR_SET:\n\t\tcvarfuncs->SetString(VM_POINTER(arg[0]), VM_POINTER(arg[1])?VM_POINTER(arg[1]):\"\");\n\t\tbreak;\n\tcase CG_CVAR_VARIABLESTRINGBUFFER:\n\t\t{\n\t\t\tcvar_t *var = cvarfuncs->GetNVFDG(VM_POINTER(arg[0]), NULL, 0, NULL, \"Q3CG created\");\n\t\t\tif (!VM_LONG(arg[2]))\n\t\t\t\tVM_LONG(ret) = 0;\n\t\t\telse if (!var)\n\t\t\t{\n\t\t\t\tVALIDATEPOINTER(arg[1], 1);\n\t\t\t\t*(char *)VM_POINTER(arg[1]) = '\\0';\n\t\t\t\tVM_LONG(ret) = -1;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tVALIDATEPOINTER(arg[1], arg[2]);\n\t\t\t\tQ_strncpyz(VM_POINTER(arg[1]), var->string, VM_LONG(arg[2]));\n\t\t\t}\n\t\t}\n\t\tbreak;\n\n\tcase CG_SENDCONSOLECOMMAND:\n\t\tCon_DPrintf(\"CG_SENDCONSOLECOMMAND: %s\", (char*)VM_POINTER(arg[0]));\n\t\tcmdfuncs->AddText(VM_POINTER(arg[0]), false);\n\t\tbreak;\n\tcase CG_ADDCOMMAND:\n\t\tcmdfuncs->AddCommand(VM_POINTER(arg[0]), NULL, NULL);\n\t\tbreak;\n\tcase CG_SENDCLIENTCOMMAND:\n\t\tCon_DPrintf(\"CG_SENDCLIENTCOMMAND: %s\", (char*)VM_POINTER(arg[0]));\n\t\tCLQ3_SendClientCommand(\"%s\", (char*)VM_POINTER(arg[0]));\n\t\tbreak;\n\n\tcase CG_UPDATESCREEN:\t//force a buffer swap cos loading won't refresh it soon.\n\t\tdrawfuncs->RedrawScreen();\n\t\tbreak;\n\n\tcase CG_FS_FOPENFILE: //fopen\n\t\tif (arg[1])\n\t\t\tVALIDATEPOINTER(arg[1], 4);\n\t\tVM_LONG(ret) = VM_fopen(VM_POINTER(arg[0]), VM_POINTER(arg[1]), VM_LONG(arg[2]), 1);\n\t\tbreak;\n\n\tcase CG_FS_READ:\t//fread\n\t\tVALIDATEPOINTER(arg[1], 4);\n\t\tVM_LONG(ret) = VM_FRead(VM_POINTER(arg[0]), VM_LONG(arg[1]), VM_LONG(arg[2]), 1);\n\t\tbreak;\n\tcase CG_FS_WRITE:\t//fwrite\n\t\tCon_DPrintf(\"CG_FS_WRITE: not implemented\\n\");\n\t\tbreak;\n\tcase CG_FS_SEEK:\n\t\treturn VM_FSeek(arg[0], arg[1], arg[2], 1);\n\tcase CG_FS_FCLOSEFILE:\t//fclose\n\t\tVM_fclose(VM_LONG(arg[0]), 1);\n\t\tbreak;\n\n\tcase CG_CM_POINTCONTENTS: //int\t\t\ttrap_CM_PointContents( const vec3_t p, clipHandle_t model );\n\t\t{\n\t\t\tunsigned int pc;\n\t\t\tunsigned int modhandle = VM_LONG(arg[1]);\n\t\t\tmodel_t *mod;\n\t\t\tif (modhandle >= countof(ccs.model_precache))\n\t\t\t{\n//\t\t\t\tif (modhandle == countof(ccs.model_precache)+1)\n//\t\t\t\t\tmod = &capsule_model;\n//\t\t\t\telse\n\t\t\t\t\tmod = box_model;\n\t\t\t}\n\t\t\telse\n\t\t\t\tmod = ccs.model_precache[modhandle];\n\t\t\tif (mod && mod->loadstate == MLS_LOADED)\n\t\t\t\tpc = mod->funcs.NativeContents(mod, 0, 0, NULL, VM_POINTER(arg[0]), vec3_origin, vec3_origin);\n\t\t\telse\n\t\t\t\tpc = 1;//FTECONTENTS_SOLID;\n\t\t\tVM_LONG(ret) = pc;//Contents_To_Q3(pc);\n\t\t}\n\t\tbreak;\n\n\tcase CG_CM_TRANSFORMEDPOINTCONTENTS: //int\t\ttrap_CM_TransformedPointContents( const vec3_t p, clipHandle_t model, const vec3_t origin, const vec3_t angles ) {\n\t\t{\n\t\t\tunsigned int pc;\n\t\t\tfloat *p = VM_POINTER(arg[0]);\n\t\t\tunsigned int modhandle = VM_LONG(arg[1]);\n\t\t\tfloat *origin = VM_POINTER(arg[2]);\n\t\t\tfloat *angles = VM_POINTER(arg[3]);\n\t\t\tmodel_t *mod;\n\t\t\tif (modhandle >= countof(ccs.model_precache))\n\t\t\t{\n//\t\t\t\tif (modhandle == countof(ccs.model_precache)+1)\n//\t\t\t\t\tmod = &capsule_model;\n//\t\t\t\telse\n\t\t\t\t\tmod = box_model;\n\t\t\t}\n\t\t\telse\n\t\t\t\tmod = ccs.model_precache[modhandle];\n\n\t\t\tif (mod && mod->loadstate == MLS_LOADED)\n\t\t\t{\n\t\t\t\tvec3_t\t\tp_l;\n\t\t\t\tvec3_t\t\taxis[3];\n\n\t\t\t\t// subtract origin offset\n\t\t\t\tVectorSubtract (p, origin, p_l);\n\n\t\t\t\t// rotate start and end into the models frame of reference\n\t\t\t\tif (angles[0] || angles[1] || angles[2])\n\t\t\t\t{\n\t\t\t\t\tAngleVectors (angles, axis[0], axis[1], axis[2]);\n\t\t\t\t\tVectorNegate(axis[1], axis[1]);\n\t\t\t\t\tpc = mod->funcs.NativeContents(mod, 0, 0, axis, p_l, vec3_origin, vec3_origin);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tpc = mod->funcs.NativeContents(mod, 0, 0, NULL, p_l, vec3_origin, vec3_origin);\n\t\t\t}\n\t\t\telse\n\t\t\t\tpc = Q3CONTENTS_SOLID;\n\t\t\tVM_LONG(ret) = pc;\n\t\t}\n\t\tbreak;\n\n\tcase CG_CM_TRANSFORMEDCAPSULETRACE:\n\tcase CG_CM_TRANSFORMEDBOXTRACE:\n//\t\tvoid\t\ttrap_CM_BoxTrace( trace_t *results, const vec3_t start, const vec3_t end,\n//\t\t\t\t\t  const vec3_t mins, const vec3_t maxs,\n//\t\t\t\t\t  clipHandle_t model, int brushmask );\n\t\t{\n//FIXME: no protection of result trace.\n\t\t\ttrace_t tr;\n\t\t\tq3trace_t *results\t= VM_POINTER(arg[0]);\n\t\t\tfloat *start\t\t= VM_POINTER(arg[1]);\n\t\t\tfloat *end\t\t\t= VM_POINTER(arg[2]);\n\t\t\tfloat *mins\t\t\t= VM_POINTER(arg[3]);\n\t\t\tfloat *maxs\t\t\t= VM_POINTER(arg[4]);\n\t\t\tunsigned int modhandle\t\t= VM_LONG(arg[5]);\n\t\t\tint brushmask\t\t= VM_LONG(arg[6]);\n\t\t\tfloat *origin\t\t= VM_POINTER(arg[7]);\n\t\t\tfloat *angles\t\t= VM_POINTER(arg[8]);\n\t\t\tmodel_t *mod;\n\t\t\tif (modhandle >= countof(ccs.model_precache))\n\t\t\t{\n//\t\t\t\tif (modhandle == countof(ccs.model_precache)+1)\n//\t\t\t\t\tmod = &capsule_model;\n//\t\t\t\telse\n\t\t\t\t\tmod = box_model;\n\t\t\t}\n\t\t\telse\n\t\t\t\tmod = ccs.model_precache[modhandle];\n\n\t\t\tif (!mins)\n\t\t\t\tmins = vec3_origin;\n\t\t\tif (!maxs)\n\t\t\t\tmaxs = vec3_origin;\n\t\t\tif (!origin)\n\t\t\t\torigin = vec3_origin;\n\t\t\tif (!angles)\n\t\t\t\tangles = vec3_origin;\n\t\t\tif (mod && mod->loadstate == MLS_LOADED)\n\t\t\t\tworldfuncs->TransformedTrace(mod, 0, 0, start, end, mins, maxs, fn==CG_CM_TRANSFORMEDCAPSULETRACE, &tr, origin, angles, brushmask);\n\t\t\telse\n\t\t\t{\n\t\t\t\tmemset(&tr, 0, sizeof(tr));\n\t\t\t\ttr.allsolid = tr.startsolid = true;\n\t\t\t\ttr.contents = 1;\n\t\t\t}\n\t\t\tresults->allsolid = tr.allsolid;\n\t\t\tresults->contents = tr.contents;\n\t\t\tresults->fraction = tr.fraction;\n\t\t\tresults->entityNum = 0;\n\t\t\tresults->startsolid = tr.startsolid;\n\t\t\tresults->surfaceFlags = tr.surface?tr.surface->flags:0;\n\t\t\tmemcpy(results->endpos, tr.endpos, sizeof(vec3_t));\n\t\t\tmemcpy(&results->plane, &tr.plane, sizeof(cplane_t));\n\t\t}\n\t\tbreak;\n\tcase CG_CM_CAPSULETRACE:\n\tcase CG_CM_BOXTRACE:\n//\t\tvoid\t\ttrap_CM_BoxTrace( trace_t *results, const vec3_t start, const vec3_t end,\n//\t\t\t\t\t  const vec3_t mins, const vec3_t maxs,\n//\t\t\t\t\t  clipHandle_t model, int brushmask );\n\t\t{\n//FIXME: no protection of result trace.\n\t\t\ttrace_t tr;\n\t\t\tq3trace_t *results\t= VM_POINTER(arg[0]);\n\t\t\tfloat *start\t\t= VM_POINTER(arg[1]);\n\t\t\tfloat *end\t\t\t= VM_POINTER(arg[2]);\n\t\t\tfloat *mins\t\t\t= VM_POINTER(arg[3]);\n\t\t\tfloat *maxs\t\t\t= VM_POINTER(arg[4]);\n\t\t\tunsigned int modhandle\t= VM_LONG(arg[5]);\n\t\t\tunsigned int brushmask\t= VM_LONG(arg[6]);\n\t\t\tmodel_t *mod;\n\t\t\tif (modhandle >= countof(ccs.model_precache))\n\t\t\t{\n//\t\t\t\tif (modhandle == countof(ccs.model_precache)+1)\n//\t\t\t\t\tmod = &capsule_model;\n//\t\t\t\telse\n\t\t\t\t\tmod = box_model;\n\t\t\t}\n\t\t\telse\n\t\t\t\tmod = ccs.model_precache[modhandle];\n\n\t\t\tif (mod->loadstate != MLS_LOADED)\n\t\t\t{\n\t\t\t\tif (mod->loadstate == MLS_NOTLOADED)\n\t\t\t\t\tscenefuncs->LoadModel(mod->publicname, MLV_SILENTSYNC);\n\t\t\t\tif (mod->loadstate == MLS_LOADING && threadfuncs)\n\t\t\t\t\tthreadfuncs->WaitForCompletion(mod, &mod->loadstate, MLS_LOADING);\n\t\t\t\tif (mod->loadstate != MLS_LOADED)\n\t\t\t\t{\n\t\t\t\t\tmemset(results, 0, sizeof(*results));\n\t\t\t\t\tresults->fraction = 1;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (!mins)\n\t\t\t\tmins = vec3_origin;\n\t\t\tif (!maxs)\n\t\t\t\tmaxs = vec3_origin;\n\t\t\tmod->funcs.NativeTrace(mod, 0, NULLFRAMESTATE, NULL, start, end, mins, maxs, fn==CG_CM_CAPSULETRACE, brushmask, &tr);\n\t\t\tresults->allsolid = tr.allsolid;\n\t\t\tresults->contents = tr.contents;\n\t\t\tresults->fraction = tr.fraction;\n\t\t\tresults->entityNum = 0;\n\t\t\tresults->startsolid = tr.startsolid;\n\t\t\tresults->surfaceFlags = tr.surface?tr.surface->flags:0;\n\t\t\tmemcpy(results->endpos, tr.endpos, sizeof(vec3_t));\n\t\t\tmemcpy(&results->plane, &tr.plane, sizeof(cplane_t));\n\t\t}\n\t\tbreak;\n\n\tcase CG_R_LOADWORLDMAP:\n\t\t{\t//rendering\n\t\t\tscenefuncs->NewMap(worldfuncs->LoadModel(VM_POINTER(arg[0]), MLV_SILENTSYNC));\n\t\t}\n\t\tbreak;\n\n\tcase CG_CM_LOADMAP:\n\t\t{\t//collisions\n\t\t\tint i;\n\t\t\tchar *mapname = VM_POINTER(arg[0]);\n\t\t\tccs.worldmodel = ccs.model_precache[0] = worldfuncs->LoadModel(mapname, MLV_SILENTSYNC);\n\t\t\tif (ccs.worldmodel->loadstate != MLS_LOADED)\n\t\t\t\tplugfuncs->EndGame(\"Couldn't load map %s\", mapname);\n\n\n\t\t\tfor (i=1 ; i<=ccs.worldmodel->numsubmodels && i < countof(ccs.model_precache); i++)\n\t\t\t\tccs.model_precache[i] = worldfuncs->LoadModel(worldfuncs->FixName(va(\"*%i\", i), mapname), MLV_SILENTSYNC);\n\t\t}\n\n\t\tbreak;\n\n\tcase CG_CM_INLINEMODEL:\n\t\tif ((unsigned int)VM_LONG(arg[0]) > (ccs.worldmodel?ccs.worldmodel->numsubmodels:0))\n\t\t\tplugfuncs->EndGame(\"cgame asked for invalid model number\\n\");\n\t\tVM_LONG(ret) = VM_LONG(arg[0]);\n\t\tbreak;\n\tcase CG_CM_NUMINLINEMODELS:\n\t\tVM_LONG(ret) = ccs.worldmodel?ccs.worldmodel->numsubmodels:0;\n\t\tbreak;\n\n\tcase CG_CM_TEMPBOXMODEL:\n\t\tbox_model = worldfuncs->TempBoxModel(VM_POINTER(arg[0]), VM_POINTER(arg[1]));\n\t\tVM_LONG(ret) = countof(ccs.model_precache);\n\t\tbreak;\n\tcase CG_CM_TEMPCAPSULEMODEL:\n\t\tbox_model = worldfuncs->TempBoxModel(VM_POINTER(arg[0]), VM_POINTER(arg[1]));\n\t\tVM_LONG(ret) = countof(ccs.model_precache)+1;\n\t\tbreak;\n\n\tcase CG_R_MODELBOUNDS:\n\t\tVALIDATEPOINTER(arg[1], sizeof(vec3_t));\n\t\tVALIDATEPOINTER(arg[2], sizeof(vec3_t));\n\t\t{\n\t\t\tmodel_t *mod = scenefuncs->ModelFromId(arg[0]);\n\t\t\tif (mod)\n\t\t\t{\n\t\t\t\tif (mod->loadstate == MLS_LOADING && threadfuncs)\n\t\t\t\t\tthreadfuncs->WaitForCompletion(mod, &mod->loadstate, MLS_LOADING);\n\n\t\t\t\tVectorCopy(mod->mins, ((float*)VM_POINTER(arg[1])));\n\t\t\t\tVectorCopy(mod->maxs, ((float*)VM_POINTER(arg[2])));\n\t\t\t}\n\t\t}\n\t\tbreak;\n\n\tcase CG_R_REGISTERMODEL:\t//precache model\n\t\t{\n\t\t\tchar *name = VM_POINTER(arg[0]);\n\t\t\tmodel_t *mod;\n\t\t\tif (!name)\n\t\t\t\treturn 0;\n\t\t\tmod = scenefuncs->LoadModel(worldfuncs->FixName(name, ccs.worldmodel->name), MLV_SILENTSYNC);\n\t\t\tif (mod->loadstate == MLS_FAILED || mod->type == mod_dummy)\n\t\t\t\tVM_LONG(ret) = 0;\n\t\t\telse\n\t\t\t\tVM_LONG(ret) = scenefuncs->ModelToId(mod);\n\t\t}\n\t\tbreak;\n\n\tcase CG_R_REGISTERSKIN:\n\t\tVM_LONG(ret) = scenefuncs->RegisterSkinFile(VM_POINTER(arg[0]));\n\t\tbreak;\n\n\tcase CG_R_REGISTERSHADER:\n\t\tif (!*(char*)VM_POINTER(arg[0]))\n\t\t\tVM_LONG(ret) = 0;\n\t\telse\n\t\t\tVM_LONG(ret) = drawfuncs->LoadImage(VM_POINTER(arg[0]));\n\t\tbreak;\n\tcase CG_R_REGISTERSHADERNOMIP:\n\t\tif (!*(char*)VM_POINTER(arg[0]))\n\t\t\tVM_LONG(ret) = 0;\n\t\telse\n\t\t\tVM_LONG(ret) = drawfuncs->LoadImage(VM_POINTER(arg[0]));\n\t\tbreak;\n\n\tcase CG_R_CLEARSCENE:\t//clear scene (not rtlights, only dynamic ones)\n\t\tscenefuncs->ClearScene();\n\t\tbreak;\n\tcase CG_R_ADDPOLYTOSCENE:\n\t\tVQ3_AddPolys(drawfuncs->ShaderFromId(arg[0]), VM_LONG(arg[1]), VM_POINTER(arg[2]), 1);\n\t\tbreak;\n\tcase CG_R_ADDPOLYSTOSCENE:\n\t\tVQ3_AddPolys(drawfuncs->ShaderFromId(arg[0]), VM_LONG(arg[1]), VM_POINTER(arg[2]), VM_LONG(arg[3]));\n\t\tbreak;\n\tcase CG_R_ADDREFENTITYTOSCENE:\t//add ent to scene\n\t\tVQ3_AddEntity(VM_POINTER(arg[0]));\n\t\tbreak;\n\tcase CG_R_ADDADDITIVELIGHTTOSCENE:\n\tcase CG_R_ADDLIGHTTOSCENE:\t//add light to scene.\n\t\t{\n\t\t\tdlight_t *dl = scenefuncs->AllocDlightOrg(-1, VM_POINTER(arg[0]));\n\t\t\tdl->flags = LFLAG_NORMALMODE|LFLAG_REALTIMEMODE;\n\t\t\tdl->radius = VM_FLOAT(arg[1]);\n\t\t\tdl->die = ccs.time+0.1;\n\t\t\tVectorSet(dl->color, VM_FLOAT(arg[2]), VM_FLOAT(arg[3]), VM_FLOAT(arg[4]));\n\t\t}\n\t\tbreak;\n\tcase CG_R_RENDERSCENE:\t//render scene\n\t\tVQ3_RenderView(VM_POINTER(arg[0]));\n\t\tbreak;\n\n\tcase CG_R_SETCOLOR:\t//setcolour float*\n\t\t{\n\t\t\tfloat *f = VM_POINTER(arg[0]);\n\t\t\tif (f)\n\t\t\t\tdrawfuncs->Colour4f(f[0], f[1], f[2], f[3]);\n\t\t\telse\n\t\t\t\tdrawfuncs->Colour4f(1, 1, 1, 1);\n\t\t}\n\t\tbreak;\n\n\tcase CG_R_DRAWSTRETCHPIC:\n\t\tdrawfuncs->Image(VM_FLOAT(arg[0]), VM_FLOAT(arg[1]), VM_FLOAT(arg[2]), VM_FLOAT(arg[3]), VM_FLOAT(arg[4]), VM_FLOAT(arg[5]), VM_FLOAT(arg[6]), VM_FLOAT(arg[7]), VM_LONG(arg[8]));\n\t\tbreak;\n\n\tcase CG_R_LERPTAG:\t//Lerp tag...\n\t\tVALIDATEPOINTER(arg[0], sizeof(float)*12);\n\t\tVM_LONG(ret) = VM_LerpTag(VM_POINTER(arg[0]), scenefuncs->ModelFromId(arg[1]), VM_LONG(arg[2]), VM_LONG(arg[3]), VM_FLOAT(arg[4]), VM_POINTER(arg[5]));\n\t\tbreak;\n\n\tcase CG_S_REGISTERSOUND:\n\t\t{\n\t\t\tsfx_t *sfx;\n\t\t\tsfx = audiofuncs->PrecacheSound(VM_POINTER(arg[0]));\n\t\t\tif (sfx)\n\t\t\t\tVM_LONG(ret) = VM_TOSTRCACHE(arg[0]);\n\t\t\telse\n\t\t\t\tVM_LONG(ret) = -1;\n\t\t}\n\t\tbreak;\n\n\tcase CG_S_STARTLOCALSOUND:\n\t\tif (VM_LONG(arg[0]) != -1 && arg[0])\n\t\t\taudiofuncs->LocalSound(VM_FROMSTRCACHE(arg[0]), VM_LONG(arg[1]), 1.0);\n\t\tbreak;\n\n\tcase CG_S_STARTSOUND:// ( vec3_t origin, int entityNum, int entchannel, sfxHandle_t sfx )\n\t\t/* if NULL gets passed for an origin, it wants us to look at those\n\t\t   provided by CG_S_UPDATEENTITYPOSITION updates instead. */\n\t\tif (VM_POINTER(arg[0]) == NULL)\n\t\t\taudiofuncs->StartSound(VM_LONG(arg[1])+1, VM_LONG(arg[2]), audiofuncs->PrecacheSound(VM_FROMSTRCACHE(arg[3])), CG_GetEntityPosition(VM_LONG(arg[1])+1), NULL, 1, 1, 0, 0, CF_CLI_NODUPES | CF_FOLLOW);\n\t\telse\n\t\t\taudiofuncs->StartSound(VM_LONG(arg[1])+1, VM_LONG(arg[2]), audiofuncs->PrecacheSound(VM_FROMSTRCACHE(arg[3])), VM_POINTER(arg[0]), NULL, 1, 1, 0, 0, CF_CLI_NODUPES);\n\t\tbreak;\n\n\tcase CG_S_ADDLOOPINGSOUND:\n\t\t//entnum, origin, velocity, sfx\n\t\tCG_StartLoopingSounds(VM_LONG(arg[0])+1, VM_POINTER(arg[1]), VM_POINTER(arg[2]), -1, VM_FROMSTRCACHE(arg[3]), 1, false);\n\t\tbreak;\n\tcase CG_S_ADDREALLOOPINGSOUND:\n\t\t//entnum, origin, velocity, sfx\n\t\tCG_StartLoopingSounds(VM_LONG(arg[0])+1, VM_POINTER(arg[1]), VM_POINTER(arg[2]), -1, VM_FROMSTRCACHE(arg[3]), 1, true);\n\t\tbreak;\n\tcase CG_S_STOPLOOPINGSOUND:\n\t\t//entnum\n\t\tCG_StopLoopingSounds(VM_LONG(arg[0])+1);\n\t\tbreak;\n\tcase CG_S_CLEARLOOPINGSOUNDS:\n\t\t//clearall\n\t\tCG_ClearLoopingSounds(VM_LONG(arg[0]));\n\t\tbreak;\n\tcase CG_S_UPDATEENTITYPOSITION://void\t\ttrap_S_UpdateEntityPosition( int entityNum, const vec3_t origin );\n\t\t//entnum, org\n\t\tCG_UpdateEntityPosition(VM_LONG(arg[0])+1, VM_POINTER(arg[1]));\n\t\tbreak;\n\n\tcase CG_S_STARTBACKGROUNDTRACK:\n\t\taudiofuncs->ChangeMusicTrack(VM_POINTER(arg[0]), VM_POINTER(arg[1]));\n\t\treturn 0;\n\tcase CG_S_STOPBACKGROUNDTRACK:\n\t\taudiofuncs->ChangeMusicTrack(NULL, NULL);\n\t\treturn 0;\n\n\tcase CG_S_RESPATIALIZE://void\t\ttrap_S_Respatialize( int entityNum, const vec3_t origin, vec3_t axis[3], int inwater );\n\t\taudiofuncs->Spacialize(0, VM_LONG(arg[0])+1, VM_POINTER(arg[1]), VM_POINTER(arg[2]), VM_LONG(arg[3])?1:0, vec3_origin);\n\t\tbreak;\n\n\tcase CG_KEY_ISDOWN:\n\t\t{\n\t\t\tif (inputfuncs->IsKeyDown(VM_LONG(arg[0])))\n\t\t\t\tVM_LONG(ret) = 1;\n\t\t\telse\n\t\t\t\tVM_LONG(ret) = 0;\n\t\t}\n\t\tbreak;\n\n\tcase CG_KEY_GETKEY:\n\t\t{\n\t\t\tint ret[1];\n\t\t\tinputfuncs->FindKeysForCommand(0, VM_POINTER(arg[0]), ret, NULL, countof(ret));\n\t\t\treturn ret[0];\n\t\t}\n\t\tbreak;\n\n\tcase CG_KEY_GETCATCHER:\n\t\tVM_LONG(ret) = Q3_GetKeyCatcher();\n\t\tbreak;\n\tcase CG_KEY_SETCATCHER:\n\t\tQ3_SetKeyCatcher(VM_LONG(arg[0]));\n\t\tbreak;\n\n\tcase CG_GETGLCONFIG:\n\t\t{\n\t\t\tfloat vsize[2];\n\t\t\tq3glconfig_t *cfg;\n\t\t\tVALIDATEPOINTER(arg[0], sizeof(q3glconfig_t));\n\t\t\tdrawfuncs->GetVideoSize(vsize, NULL);\n\t\t\tcfg = VM_POINTER(arg[0]);\n\n\t\t\t//do any needed work\n\t\t\tmemset(cfg, 0, sizeof(*cfg));\n\n\t\t\tQ_strncpyz(cfg->renderer_string, \"\", sizeof(cfg->renderer_string));\n\t\t\tQ_strncpyz(cfg->vendor_string, \"\", sizeof(cfg->vendor_string));\n\t\t\tQ_strncpyz(cfg->version_string, \"\", sizeof(cfg->version_string));\n\t\t\tQ_strncpyz(cfg->extensions_string, \"\", sizeof(cfg->extensions_string));\n\n\t\t\tcfg->colorBits = 32;\n\t\t\tcfg->depthBits = 24;\n\t\t\tcfg->stencilBits = 8;//sh_config.stencilbits;\n\t\t\tcfg->textureCompression = true;//!!sh_config.hw_bc;\n\t\t\tcfg->textureEnvAddAvailable = true;//sh_config.env_add;\n\n\t\t\t//these are the only three that really matter.\n\t\t\tcfg->vidWidth = vsize[0];\n\t\t\tcfg->vidHeight = vsize[1];\n\t\t\tcfg->windowAspect = (float)vsize[0]/vsize[1];\n\t\t}\n\t\tbreak;\n\n\tcase CG_GETGAMESTATE:\n\t\tVALIDATEPOINTER(arg[0], sizeof(gameState_t));\n\t\tVM_LONG(ret) = CG_GetGameState(VM_POINTER(arg[0]));\n\t\tbreak;\n\n\tcase CG_CM_MARKFRAGMENTS:\n\t\tVM_LONG(ret) = CG_MarkFragments( VM_LONG(arg[0]), VM_POINTER(arg[1]), VM_POINTER(arg[2]), VM_LONG(arg[3]), VM_POINTER(arg[4]), VM_LONG(arg[5]), VM_POINTER(arg[6]) );\n\t\tbreak;\n\n\tcase CG_GETCURRENTSNAPSHOTNUMBER:\n\t\tVALIDATEPOINTER(arg[0], sizeof(int));\n\t\tVALIDATEPOINTER(arg[1], sizeof(int));\n\t\t*(int *)VM_POINTER(arg[0]) = ccs.snap.serverMessageNum;\n\t\t*(int *)VM_POINTER(arg[1]) = ccs.snap.serverTime;\n\t\tbreak;\n\n\tcase CG_GETSNAPSHOT:\n\t\tVALIDATEPOINTER(arg[1], sizeof(snapshot_t));\n\t\tVM_LONG(ret) = CG_FillQ3Snapshot(VM_LONG(arg[0]), VM_POINTER(arg[1]));\n\t\tbreak;\n\n\tcase CG_GETCURRENTCMDNUMBER:\n\t\tVM_LONG(ret) = CGQ3_GetCurrentCmdNumber();\n\t\tbreak;\n\tcase CG_GETUSERCMD:\n\t\tVALIDATEPOINTER(arg[1], sizeof(q3usercmd_t));\n\t\tVM_LONG(ret) = CGQ3_GetUserCmd(VM_LONG(arg[0]), VM_POINTER(arg[1]));\n\t\tbreak;\n\tcase CG_SETUSERCMDVALUE:\t//weaponselect, zoomsensitivity.\n\t\tccs.selected_weapon = VM_LONG(arg[0]);\n\t\tinputfuncs->SetSensitivityScale(VM_FLOAT(arg[1]));\n\t\tbreak;\n\n\tcase CG_GETSERVERCOMMAND:\n\t\tVM_LONG(ret) = CG_GetServerCommand(VM_LONG(arg[0]));\n\t\tbreak;\n\n\tcase CG_MEMORY_REMAINING:\n\t\tVM_LONG(ret) = 1024*1024*8;//Hunk_LowMemAvailable();\n\t\tbreak;\n\n\tcase CG_MILLISECONDS:\n\t\tVM_LONG(ret) = plugfuncs->GetMilliseconds();\n\t\tbreak;\n\tcase CG_REAL_TIME:\n\t\tVALIDATEPOINTER(arg[0], sizeof(q3time_t));\n\t\treturn Q3VM_GetRealtime(VM_POINTER(arg[0]));\n\n\tcase CG_SNAPVECTOR: // ( float *v )\n\t\tVALIDATEPOINTER(arg[0], sizeof(vec3_t));\n\t\t{\n\t\t\tfloat *fp = (float *)VM_POINTER(arg[0]);\n#define rint(x) (int)((x > 0)?(x + 0.5f):(x-0.5f))\n\t\t\tfp[0] = rint(fp[0]);\n\t\t\tfp[1] = rint(fp[1]);\n\t\t\tfp[2] = rint(fp[2]);\n\t\t}\n\t\tbreak;\n\n\tcase CG_PC_ADD_GLOBAL_DEFINE:\n\t\tCon_Printf(\"CG_PC_ADD_GLOBAL_DEFINE not supported\\n\");\n\t\tbreak;\n\tcase CG_PC_SOURCE_FILE_AND_LINE:\n\t\tScript_Get_File_And_Line(arg[0], VM_POINTER(arg[1]), VM_POINTER(arg[2]));\n\t\tbreak;\n\n\tcase CG_PC_LOAD_SOURCE:\n\t\treturn Script_LoadFile(VM_POINTER(arg[0]));\n\tcase CG_PC_FREE_SOURCE:\n\t\tScript_Free(arg[0]);\n\t\tbreak;\n\tcase CG_PC_READ_TOKEN:\n\t\t//fixme: memory protect.\n\t\tVALIDATEPOINTER(arg[1], sizeof(struct pc_token_s));\n\t\treturn Script_Read(arg[0], VM_POINTER(arg[1]));\n\n// standard Q3\n\tcase CG_MEMSET:\n\t\tVALIDATEPOINTER(arg[0], arg[2]);\n\t\t{\n\t\t\tvoid *dst = VM_POINTER(arg[0]);\n\t\t\tmemset(dst, arg[1], arg[2]);\n\t\t}\n\t\tbreak;\n\tcase CG_MEMCPY:\n\t\tVALIDATEPOINTER(arg[0], arg[2]);\n\t\t{\n\t\t\tvoid *dst = VM_POINTER(arg[0]);\n\t\t\tvoid *src = VM_POINTER(arg[1]);\n\t\t\tmemcpy(dst, src, arg[2]);\n\t\t}\n\t\tbreak;\n\tcase CG_STRNCPY:\n\t\tVALIDATEPOINTER(arg[0], arg[2]);\n\t\t{\n\t\t\tvoid *dst = VM_POINTER(arg[0]);\n\t\t\tvoid *src = VM_POINTER(arg[1]);\n\t\t\tstrncpy(dst, src, arg[2]);\n\t\t}\n\t\tbreak;\n\tcase CG_SIN:\n\t\tVM_FLOAT(ret)=(float)sin(VM_FLOAT(arg[0]));\n\t\tbreak;\n\tcase CG_COS:\n\t\tVM_FLOAT(ret)=(float)cos(VM_FLOAT(arg[0]));\n\t\tbreak;\n\tcase CG_ACOS:\n\t\tVM_FLOAT(ret)=(float)acos(VM_FLOAT(arg[0]));\n\t\tbreak;\n\tcase CG_ATAN2:\n\t\tVM_FLOAT(ret)=(float)atan2(VM_FLOAT(arg[0]), VM_FLOAT(arg[1]));\n\t\tbreak;\n\tcase CG_SQRT:\n\t\tVM_FLOAT(ret)=(float)sqrt(VM_FLOAT(arg[0]));\n\t\tbreak;\n\tcase CG_FLOOR:\n\t\tVM_FLOAT(ret)=(float)floor(VM_FLOAT(arg[0]));\n\t\tbreak;\n\tcase CG_CEIL:\n\t\tVM_FLOAT(ret)=(float)ceil(VM_FLOAT(arg[0]));\n\t\tbreak;\n\n\tcase CG_R_REMAP_SHADER:\n\t\tscenefuncs->RemapShader(VM_POINTER(arg[0]), VM_POINTER(arg[1]), VM_FLOAT(arg[2]));\n\t\tbreak;\n\n\tcase CG_R_REGISTERFONT:\n\t\tVALIDATEPOINTER(arg[2], sizeof(fontInfo_t));\n\t\tUI_RegisterFont(VM_POINTER(arg[0]), VM_LONG(arg[1]), VM_POINTER(arg[2]));\n\t\tbreak;\n\n\tcase CG_GET_ENTITY_TOKEN:\n\t\tmapentspointer = cmdfuncs->ParseToken(mapentspointer, VM_POINTER(arg[0]), arg[1], NULL);\n\t\treturn !!mapentspointer;\n\n\n\tcase CG_CIN_PLAYCINEMATIC:\n\t\treturn UI_Cin_Play(VM_POINTER(arg[0]), VM_LONG(arg[1]), VM_LONG(arg[2]), VM_LONG(arg[3]), VM_LONG(arg[4]), VM_LONG(arg[5]));\n\tcase CG_CIN_STOPCINEMATIC:\n\t\treturn UI_Cin_Stop(VM_LONG(arg[0]));\n\tcase CG_CIN_RUNCINEMATIC:\n\t\treturn UI_Cin_Run(VM_LONG(arg[0]));\n\tcase CG_CIN_DRAWCINEMATIC:\n\t\treturn UI_Cin_Draw(VM_LONG(arg[0]));\n\tcase CG_CIN_SETEXTENTS:\n\t\treturn UI_Cin_SetExtents(VM_LONG(arg[0]), VM_LONG(arg[1]), VM_LONG(arg[2]), VM_LONG(arg[3]), VM_LONG(arg[4]));\n\n/*\tcase CG_FTE_FINDPARTICLEEFFECT:\n\t\treturn pe->FindParticleType(VM_POINTER(arg[0]));\n\tcase CG_FTE_SPAWNPARTICLEEFFECT:\n\t\treturn pe->RunParticleEffectState(VM_POINTER(arg[0]), VM_POINTER(arg[1]), VM_FLOAT(arg[2]), VM_LONG(arg[3]), VM_POINTER(arg[4]));\n\tcase CG_FTE_SPAWNPARTICLETRAIL:\n\t\treturn pe->ParticleTrail(VM_POINTER(arg[0]), VM_POINTER(arg[1]), VM_LONG(arg[2]), 0, 1, NULL, VM_POINTER(arg[3]));\n\tcase CG_FTE_FREEPARTICLESTATE:\n\t\tpe->DelinkTrailstate(VM_POINTER(arg[0]));\n\t\tbreak;\n*/\n\tcase CG_CM_LOADMODEL:\n\tcase CG_REMOVECOMMAND:\n\tcase CG_TESTPRINTINT:\n\tcase CG_TESTPRINTFLOAT:\n\tcase CG_R_INPVS:\n\tcase CG_R_LIGHTFORPOINT:\n//\tdefault:\n\t\tCon_Printf(\"Q3CG: Bad system trap: %i\\n\", (int)fn);\n\t}\n\n\treturn ret;\n}\n\nstatic int CG_SystemCallsVM(void *offset, quintptr_t mask, int fn, const int *arg)\n{\n\tif (sizeof(qintptr_t) == sizeof(int))\n\t\treturn CG_SystemCalls(offset, mask, fn, (qintptr_t*)arg);\n\telse\n\t{\n\t\tqintptr_t args[10];\n\n\t\targs[0]=arg[0];\n\t\targs[1]=arg[1];\n\t\targs[2]=arg[2];\n\t\targs[3]=arg[3];\n\t\targs[4]=arg[4];\n\t\targs[5]=arg[5];\n\t\targs[6]=arg[6];\n\t\targs[7]=arg[7];\n\t\targs[8]=arg[8];\n\t\targs[9]=arg[9];\n\n\t\treturn CG_SystemCalls(offset, mask, fn, args);\n\t}\n}\n\n//I'm not keen on this.\n//but dlls call it without saying what sort of vm it comes from, so I've got to have them as specifics\nstatic qintptr_t EXPORT_FN CG_SystemCallsNative(qintptr_t arg, ...)\n{\n\tqintptr_t args[10];\n\tva_list argptr;\n\n\tva_start(argptr, arg);\n\targs[0]=va_arg(argptr, qintptr_t);\n\targs[1]=va_arg(argptr, qintptr_t);\n\targs[2]=va_arg(argptr, qintptr_t);\n\targs[3]=va_arg(argptr, qintptr_t);\n\targs[4]=va_arg(argptr, qintptr_t);\n\targs[5]=va_arg(argptr, qintptr_t);\n\targs[6]=va_arg(argptr, qintptr_t);\n\targs[7]=va_arg(argptr, qintptr_t);\n\targs[8]=va_arg(argptr, qintptr_t);\n\targs[9]=va_arg(argptr, qintptr_t);\n\tva_end(argptr);\n\n\treturn CG_SystemCalls(NULL, ~(quintptr_t)0, arg, args);\n}\n\nint CG_Refresh(double time)\n{\n\tif (!cgvm)\n\t\treturn false;\n\n\tccs.time = time;\n\tvmfuncs->Call(cgvm, CG_DRAW_ACTIVE_FRAME, (int)(ccs.time*1000), 0, false);\n\tdrawfuncs->Colour4f(1, 1, 1, 1);\n\treturn true;\n}\n\n\n\nvoid CG_Stop (void)\n{\n\tQ3_SetKeyCatcher(Q3_GetKeyCatcher()&~2);\n\tif (cgvm)\n\t{\n\t\tvmfuncs->Call(cgvm, CG_SHUTDOWN);\n\t\tvmfuncs->Destroy(cgvm);\n\t\tVM_fcloseall(1);\n\t\tcgvm = NULL;\n\n\t}\n}\n\nqboolean CG_VideoRestarted(void)\n{\n\tif (cgvm)\n\t{\n\t\tvmfuncs->Call(cgvm, CG_INIT, ccs.serverMessageNum, ccs.lastServerCommandNum, ccs.playernum);\n\t\treturn true;\n\t}\n\treturn false;\n}\n\nvoid CG_Start (void)\n{\n\tCG_Stop();\n\n\tbox_model = worldfuncs->TempBoxModel(vec3_origin, vec3_origin);\t//just in case.\n\n\tclientfuncs->SetLoadingState(true);\n\tcgvm = vmfuncs->Create(\"cgame\", cvarfuncs->GetFloat(\"com_gamedirnativecode\")?CG_SystemCallsNative:NULL, \"vm/cgame\", CG_SystemCallsVM);\n\tclientfuncs->SetLoadingState(false);\n\tif (cgvm)\n\t{\t//hu... cgame doesn't appear to have a query version call!\n\t\tvmfuncs->Call(cgvm, CG_INIT, ccs.serverMessageNum, ccs.lastServerCommandNum, ccs.playernum);\n\t}\n\telse\n\t\tplugfuncs->EndGame(\"Failed to initialise cgame module\\n\");\n}\n\nqboolean CG_ConsoleCommand(void)\n{\n\tif (!cgvm)\n\t\treturn false;\n//\tCon_DPrintf(\"CG_Command: %s %s\\n\", cmdfuncs->Argv(0), cmdfuncs->Args());\n\treturn vmfuncs->Call(cgvm, CG_CONSOLE_COMMAND);\n}\n\n/*void CG_Command_f(void)\n{\n\tif (cgvm)\n\t{\n\t\tCon_DPrintf(\"CG_Command_f: %s %s\\n\", Cmd_Argv(0), Cmd_Args());\n\t\tif (!VM_Call(cgvm, CG_CONSOLE_COMMAND))\n\t\t{\n\t\t\tCmd_ForwardToServer();\n\t\t}\n\t}\n}*/\n\nqboolean CG_KeyPressed(int key, int unicode, int down)\n{\n\n\tif (!cgvm || !(Q3_GetKeyCatcher()&8))\n\t\treturn false;\n\n\t/* if you change this here, it'll have to be changed in cl_cg.c too */\n\tswitch (key)\n\t{\n\t\t/* all these get interpreted as enter in Q3's UI... */\n\t\tcase K_JOY1:\n\t\tcase K_JOY2:\n\t\tcase K_JOY3:\n\t\tcase K_JOY4:\n\t\tcase K_AUX1:\n\t\tcase K_AUX2:\n\t\tcase K_AUX3:\n\t\tcase K_AUX4:\n\t\tcase K_AUX5:\n\t\tcase K_AUX6:\n\t\tcase K_AUX7:\n\t\tcase K_AUX8:\n\t\tcase K_AUX9:\n\t\tcase K_AUX10:\n\t\tcase K_AUX11:\n\t\tcase K_AUX14:\n\t\tcase K_AUX15:\n\t\tcase K_AUX16:\n\t\t\treturn true;\n\t\t\tbreak;\n\t\t/* Q3 doesn't know about these keys, remap them */\n\t\tcase K_GP_START:\n\t\t\tkey = K_ESCAPE;\n\t\t\tbreak;\n\t\tcase K_GP_DPAD_UP:\n\t\t\tkey = K_UPARROW;\n\t\t\tbreak;\n\t\tcase K_GP_DPAD_DOWN:\n\t\t\tkey = K_DOWNARROW;\n\t\t\tbreak;\n\t\tcase K_GP_DPAD_LEFT:\n\t\t\tkey = K_LEFTARROW;\n\t\t\tbreak;\n\t\tcase K_GP_DPAD_RIGHT:\n\t\t\tkey = K_RIGHTARROW;\n\t\t\tbreak;\n\t\tcase K_GP_A:\n\t\t\tkey = K_ENTER;\n\t\t\tbreak;\n\t\tcase K_GP_B:\n\t\t\tkey = K_ESCAPE;\n\t\t\tbreak;\n\t\tcase K_GP_X:\n\t\t\tkey = K_BACKSPACE;\n\t\t\tbreak;\n\t}\n\n\treturn vmfuncs->Call(cgvm, CG_KEY_EVENT, key, down);\n}\n\nvoid CG_Restart(void)\n{\n\tCG_Stop();\n\tCG_Start();\n}\n\n#endif\n"
  },
  {
    "path": "plugins/quake3/clq3_parse.c",
    "content": "#include \"q3common.h\"\n#include \"shader.h\"\n\n\n\n#include \"glquake.h\"\n#include \"shader.h\"\n#include \"cl_master.h\"\n\n//urm, yeah, this is more than just parse.\n\n#ifdef Q3CLIENT\n\n#include \"clq3defs.h\"\n\n#define SHOWSTRING(s) if(cl_shownet_ptr->value==2)Con_Printf (\"%s\\n\", s);\n#define SHOWNET(x) if(cl_shownet_ptr->value==2)Con_Printf (\"%3i:%s\\n\", msg->currentbit-8, x);\n#define SHOWNET2(x, y) if(cl_shownet_ptr->value==2)Con_Printf (\"%3i:%3i:%s\\n\", msg->currentbit-8, y, x);\n\nvoid MSG_WriteBits(sizebuf_t *msg, int value, int bits);\nstatic qboolean CLQ3_Netchan_Process(sizebuf_t *msg);\n\n\nClientConnectionState_t ccs;\n\n\n\nqboolean CG_FillQ3Snapshot(int snapnum, snapshot_t *snapshot)\n{\n\tint i;\n\tclientSnap_t\t*snap;\n\n\tif (snapnum > ccs.serverMessageNum)\n\t{\n\t\tplugfuncs->EndGame(\"CG_FillQ3Snapshot: snapshotNumber > cl.snap.serverMessageNum\");\n\t}\n\n\tif (ccs.serverMessageNum - snapnum >= Q3UPDATE_BACKUP)\n\t{\n\t\treturn false; // too old\n\t}\n\n\tsnap = &ccs.snapshots[snapnum & Q3UPDATE_MASK];\n\tif(!snap->valid || snap->serverMessageNum != snapnum)\n\t{\n\t\treturn false; // invalid\n\t}\n\n\tmemcpy(&snapshot->ps, &snap->playerstate, sizeof(snapshot->ps));\n\tsnapshot->numEntities = snap->numEntities;\n\tfor (i=0; i<snapshot->numEntities; i++)\n\t{\n\t\tmemcpy(&snapshot->entities[i], &ccs.parseEntities[(snap->firstEntity+i) & Q3PARSE_ENTITIES_MASK], sizeof(snapshot->entities[0]));\n\t}\n\n\tmemcpy( &snapshot->areamask, snap->areabits, sizeof( snapshot->areamask ) );\n\n\tsnapshot->snapFlags = snap->snapFlags;\n\tsnapshot->ping = snap->ping;\n\n\tsnapshot->serverTime = snap->serverTime;\n\n\tsnapshot->numServerCommands = snap->serverCommandNum;\n\tsnapshot->serverCommandSequence = ccs.lastServerCommandNum;\n\n\treturn true;\n}\n\n/*\n=====================\nCLQ3_ParseServerCommand\n=====================\n*/\nvoid CLQ3_ParseServerCommand(void)\n {\n\tint\t\tnumber;\n\tchar\t*string;\n\n\tnumber = msgfuncs->ReadLong();\n//\tSHOWNET(va(\"%i\", number));\n\n\tstring = msgfuncs->ReadString();\n\tSHOWSTRING(string);\n\n\tif( number <= ccs.lastServerCommandNum )\n\t{\n\t\treturn; // we have already received this command\n\t}\n\n\tccs.lastServerCommandNum++;\n\n\tif( number > ccs.lastServerCommandNum+Q3TEXTCMD_MASK-1 )\n\t{\n\t\tCon_Printf(\"Warning: Lost %i reliable serverCommands\\n\",\n\t\t\tnumber - ccs.lastServerCommandNum );\n\t}\n\n\t// archive the command to be processed by cgame later\n\tQ_strncpyz( ccs.serverCommands[number & Q3TEXTCMD_MASK], string, sizeof( ccs.serverCommands[0] ) );\n}\n\n/*\n==================\nCL_DeltaEntity\n\nParses deltas from the given base and adds the resulting entity\nto the current frame\n==================\n*/\nstatic void CLQ3_DeltaEntity( clientSnap_t *frame, int newnum, q3entityState_t *old, qboolean unchanged )\n{\n\tq3entityState_t *state;\n\n\tstate = &ccs.parseEntities[ccs.firstParseEntity & Q3PARSE_ENTITIES_MASK];\n\n\tif( unchanged )\n\t{\n\t\tmemcpy( state, old, sizeof(*state) ); // don't read any bits\n\t}\n\telse\n\t{\n\t\tif (!MSG_Q3_ReadDeltaEntity(old, state, newnum)) // the entity present in oldframe is not in the current frame\n\t\t\treturn;\n\t}\n\n\tccs.firstParseEntity++;\n\tframe->numEntities++;\n}\n\n/*\n==================\nCL_ParsePacketEntities\n\nAn svc_packetentities has just been parsed, deal with the\nrest of the data stream.\n==================\n*/\nstatic void CLQ3_ParsePacketEntities( clientSnap_t *oldframe, clientSnap_t *newframe )\n{\n\tint\t\t\t\tnumentities;\n\tint\t\t\t\toldnum;\n\tint\t\t\t\tnewnum;\n\tq3entityState_t\t*oldstate;\n\n\toldstate = NULL;\n\tnewframe->firstEntity = ccs.firstParseEntity;\n\tnewframe->numEntities = 0;\n\n\t// delta from the entities present in oldframe\n\tnumentities = 0;\n\tif( !oldframe )\n\t{\n\t\toldnum = 99999;\n\t}\n\telse if( oldframe->numEntities <= 0 )\n\t{\n\t\toldnum = 99999;\n\t}\n\telse\n\t{\n\t\toldstate = &ccs.parseEntities[oldframe->firstEntity & Q3PARSE_ENTITIES_MASK];\n\t\toldnum = oldstate->number;\n\t}\n\n\twhile( 1 )\n\t{\n\t\tnewnum = msgfuncs->ReadBits( GENTITYNUM_BITS );\n\t\tif (newnum < 0)\n\t\t\tplugfuncs->EndGame(\"CLQ3_ParsePacketEntities: end of message\");\n\t\telse if (newnum >= MAX_GENTITIES )\n\t\t\tplugfuncs->EndGame(\"CLQ3_ParsePacketEntities: bad number %i\", newnum);\n\n\t\t// end of packetentities\n\t\tif( newnum == ENTITYNUM_NONE )\n\t\t{\n\t\t\tbreak;\n\t\t}\n\n\t\twhile( oldnum < newnum )\n\t\t{\n\t\t\t// one or more entities from the old packet are unchanged\n\t\t\tSHOWSTRING( va( \"unchanged: %i\", oldnum ) );\n\n\t\t\tCLQ3_DeltaEntity( newframe, oldnum, oldstate, true );\n\n\t\t\tnumentities++;\n\n\t\t\tif( numentities >= oldframe->numEntities )\n\t\t\t{\n\t\t\t\toldnum = 99999;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\toldstate = &ccs.parseEntities[(oldframe->firstEntity + numentities) & Q3PARSE_ENTITIES_MASK];\n\t\t\t\toldnum = oldstate->number;\n\t\t\t}\n\t\t}\n\n\t\tif( oldnum == newnum )\n\t\t{\n\t\t\t// delta from previous state\n\t\t\tSHOWSTRING( va( \"delta: %i\", newnum ) );\n\n\t\t\tCLQ3_DeltaEntity( newframe, newnum, oldstate, false );\n\n\t\t\tnumentities++;\n\n\t\t\tif( numentities >= oldframe->numEntities )\n\t\t\t{\n\t\t\t\toldnum = 99999;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\toldstate = &ccs.parseEntities[(oldframe->firstEntity + numentities) & Q3PARSE_ENTITIES_MASK];\n\t\t\t\toldnum = oldstate->number;\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\n\t\tif( oldnum > newnum )\n\t\t{\n\t\t\t// delta from baseline\n\t\t\tSHOWSTRING( va( \"baseline: %i\", newnum ) );\n\n\t\t\tCLQ3_DeltaEntity( newframe, newnum, &ccs.baselines[newnum], false );\n\t\t}\n\t}\n\n\t// any remaining entities in the old frame are copied over\n\twhile( oldnum != 99999 )\n\t{\n\t\t// one or more entities from the old packet are unchanged\n\t\tSHOWSTRING( va( \"unchanged: %i\", oldnum ) );\n\n\t\tCLQ3_DeltaEntity( newframe, oldnum, oldstate, true );\n\n\t\tnumentities++;\n\n\t\tif( numentities >= oldframe->numEntities )\n\t\t{\n\t\t\toldnum = 99999;\n\t\t}\n\t\telse\n\t\t{\n\t\t\toldstate = &ccs.parseEntities[(oldframe->firstEntity + numentities) & Q3PARSE_ENTITIES_MASK];\n\t\t\toldnum = oldstate->number;\n\t\t}\n\t}\n}\n\nvoid CLQ3_ParseSnapshot(void)\n{\n\tclientSnap_t\tsnap, *oldsnap;\n\tint\t\t\t\tdelta;\n\tint\t\t\t\tlen;\n//\tint\t\t\t\ti;\n//\toutframe_t\t\t*frame;\n//\tusercmd_t\t\t*ucmd;\n//\tint\t\t\t\tcommandTime;\n\n\tmemset(&snap, 0, sizeof(snap));\n\tsnap.serverMessageNum = ccs.serverMessageNum;\n\tsnap.serverCommandNum = ccs.lastServerCommandNum;\n\tsnap.serverTime = msgfuncs->ReadLong();\n\n\t//so we can delta to it properly.\n\tclientfuncs->UpdateGameTime(snap.serverTime / 1000.0f);\n\n\t// If the frame is delta compressed from data that we\n\t// no longer have available, we must suck up the rest of\n\t// the frame, but not use it, then ask for a non-compressed message\n\tdelta = msgfuncs->ReadByte();\n\tif(delta)\n\t{\n\t\tsnap.deltaFrame = ccs.serverMessageNum - delta;\n\t\toldsnap = &ccs.snapshots[snap.deltaFrame & Q3UPDATE_MASK];\n\n\t\tif(!oldsnap->valid)\n\t\t{\n\t\t\t// should never happen\n\t\t\tCon_Printf( \"Delta from invalid frame (not supposed to happen!).\\n\");\n\t\t}\n\t\telse if( oldsnap->serverMessageNum != snap.deltaFrame )\n\t\t{\n\t\t\t// The frame that the server did the delta from\n\t\t\t// is too old, so we can't reconstruct it properly.\n\t\t\tCon_DPrintf( \"Delta frame too old.\\n\" );\n\t\t}\n\t\telse if(ccs.firstParseEntity - oldsnap->firstEntity >\n\t\t\tQ3MAX_PARSE_ENTITIES - MAX_ENTITIES_IN_SNAPSHOT)\n\t\t{\n\t\t\tCon_DPrintf( \"Delta parse_entities too old.\\n\" );\n\t\t}\n\t\telse\n\t\t{\n\t\t\tsnap.valid = true; // valid delta parse\n\t\t}\n\t}\n\telse\n\t{\n\t\toldsnap = NULL;\n\t\tsnap.deltaFrame = -1;\n\t\tsnap.valid = true; // uncompressed frame\n\t}\n\n\t// read snapFlags\n\tsnap.snapFlags = msgfuncs->ReadByte();\n\n\t// read areabits\n\tlen = msgfuncs->ReadByte();\n\tmsgfuncs->ReadData(snap.areabits, len );\n\n\t// read playerinfo\n\tSHOWSTRING(\"playerstate\");\n\tMSG_Q3_ReadDeltaPlayerstate(oldsnap ? &oldsnap->playerstate : NULL, &snap.playerstate);\n\n\t// read packet entities\n\tSHOWSTRING(\"packet entities\");\n\tCLQ3_ParsePacketEntities(oldsnap, &snap);\n\n\tif (!snap.valid)\n\t{\n\t\treturn;\n\t}\n\n//\tcl.adjustTimeDelta = true;\n\n\t// Find last usercmd server has processed and calculate snap.ping\n\n\tsnap.ping = 3;\n/*\tfor (i=ccs.netchan.outgoing_sequence-1 ; i>ccs.netchan.outgoing_sequence-Q3CMD_BACKUP ; i--)\n\t{\n\t\tframe = &cl.outframes[i & Q3CMD_MASK];\n\t\tif (frame->server_message_num == snap.deltaFrame)\n\t\t{\n\t\t\tsnap.ping = plugfuncs->GetMilliseconds() - frame->client_time;\n\t\t\tbreak;\n\t\t}\n\t}*/\n\n\tmemcpy(&ccs.snap, &snap, sizeof(snap));\n\tmemcpy(&ccs.snapshots[ccs.serverMessageNum & Q3UPDATE_MASK], &snap, sizeof(snap));\n\n\tSHOWSTRING(va(\"snapshot:%i  delta:%i  ping:%i\", snap.serverMessageNum, snap.deltaFrame, snap.ping));\n}\n\n#define MAXCHUNKSIZE 65536\nvoid CLQ3_ParseDownload(void)\n{\n\tqdownload_t *dl = ccs.download;\n\tunsigned int chunknum;\n\tunsigned int chunksize;\n\tunsigned char chunkdata[MAXCHUNKSIZE];\n\tint i;\n\tchar *s;\n\n\tchunknum = (unsigned short) msgfuncs->ReadShort();\n\tchunknum |= ccs.downloadchunknum&~0xffff;\t\t//add the chunk number, truncated by the network protocol.\n\n\tif (!chunknum)\n\t{\n\t\tdl->size = (unsigned int)msgfuncs->ReadLong();\n\t\tcvarfuncs->SetFloat( cvarfuncs->GetNVFDG(\"cl_downloadSize\", \"0\", 0, NULL, \"Q3 Compat\")->name, dl->size );\t//so the gamecode knows download progress.\n\t}\n\n\tif (dl->size == (unsigned int)-1)\n\t{\t//the only downloads we should be getting is pk3s.\n\t\t//if they're advertised-but-failing then its probably due to permissions rather than file-not-found\n\t\ts = msgfuncs->ReadString();\n\t\tclientfuncs->DownloadFailed(dl->remotename, dl, DLFAIL_SERVERCVAR);\n\t\tplugfuncs->EndGame(\"%s\", s);\n\t\treturn;\n\t}\n\n\tchunksize = (unsigned short)msgfuncs->ReadShort();\n\tif (chunksize > MAXCHUNKSIZE)\n\t\tplugfuncs->EndGame(\"Server sent a download chunk of size %i (it's too damn big!)\\n\", chunksize);\n\n\tfor (i = 0; i < chunksize; i++)\n\t\tchunkdata[i] = msgfuncs->ReadByte();\n\n\tif (ccs.downloadchunknum != chunknum)\t//the q3 client is rather lame.\n\t{\t\t\t\t\t\t\t\t\t\t//ccs.downloadchunknum holds the chunk number.\n\t\tCon_DPrintf(\"PACKETLOSS WHEN DOWNLOADING!!!!\\n\");\n\t\treturn;\t//let the server try again some time\n\t}\n\tccs.downloadchunknum++;\n\n\tif (!dl || dl->method != DL_Q3)\n\t{\n\t\tCon_Printf(\"Server sending download, but no download was requested\\n\");\n\t\tCLQ3_SendClientCommand(\"stopdl\");\n\t\treturn;\n\t}\n\n\tif (!dl->file)\n\t{\n\t\tif (!clientfuncs->DownloadBegun(dl))\n\t\t{\n\t\t\tclientfuncs->DownloadFailed(dl->remotename, dl, DLFAIL_CLIENTFILE);\n\t\t\treturn;\n\t\t}\n\t}\n\n\tCon_DPrintf(\"dl: chnk %u, size %u, csize %u\\n\", (unsigned int)chunknum, (unsigned int)dl->size, (unsigned int)chunksize);\n\n\tif (!chunksize)\n\t{\n\t\tclientfuncs->DownloadFinished(dl);\n\n\t\tccs.servercount = -1;\t//make sure the server resends us that vital gamestate.\n\t\tccs.downloadchunknum = -1;\n\t\treturn;\n\t}\n\telse\n\t{\n\t\tVFS_WRITE(dl->file, chunkdata, chunksize);\n\t\tdl->ratebytes += chunksize;\n\t\tchunksize=VFS_TELL(dl->file);\n//\t\tCon_Printf(\"Recieved %i\\n\", chunksize);\n\n\t\tdl->percent = (100.0 * chunksize) / dl->size;\n\t}\n\n\n\tCLQ3_SendClientCommand(\"nextdl %u\", chunknum);\n}\n\nstatic qboolean CLQ3_SendDownloads(char *rc, char *rn)\n{\n\twhile(rn)\n\t{\n\t\tqdownload_t *dl;\n\t\tchar localname[MAX_QPATH];\n\t\tchar tempname[MAX_QPATH];\n\t\tchar filename[MAX_QPATH];\n\t\tchar crc[64];\n\t\tvfsfile_t *f;\n\t\trc = cmdfuncs->ParseToken(rc, crc, sizeof(crc), NULL);\n\t\trn = cmdfuncs->ParseToken(rn, filename, sizeof(filename), NULL);\n\t\tif (!*filename)\n\t\t\tbreak;\n\n\t\tif (!strchr(filename, '/'))\t//don't let some muppet tell us to download quake3.exe\n\t\t\tbreak;\n\n\t\t//as much as I'd like to use COM_FCheckExists, this stuf is relative to root, not the gamedir.\n\t\tf = fsfuncs->OpenVFS(va(\"%s.pk3\", filename), \"rb\", FS_ROOT);\n\t\tif (f)\n\t\t{\n\t\t\tVFS_CLOSE(f);\n\t\t\tcontinue;\n\t\t}\n\t\tif (!fsfuncs->GenCachedPakName(va(\"%s.pk3\", filename), crc, localname, sizeof(localname)))\n\t\t\tcontinue;\n\t\tf = fsfuncs->OpenVFS(localname, \"rb\", FS_ROOT);\n\t\tif (f)\n\t\t{\n\t\t\tVFS_CLOSE(f);\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (!fsfuncs->GenCachedPakName(va(\"%s.tmp\", filename), crc, tempname, sizeof(tempname)))\n\t\t\tcontinue;\n\n\t\tif (!cvarfuncs->GetFloat(\"cl_downloads\"))\n\t\t{\n\t\t\tCon_Printf(CON_WARNING \"Need to download %s.pk3, but downloads are disabled\\n\", filename);\n\t\t\tcontinue;\n\t\t}\n\n\t\t//fixme: request to download it\n\t\tCon_Printf(\"Sending request to download %s.pk3\\n\", filename);\n\t\tCLQ3_SendClientCommand(\"download %s.pk3\", filename);\n\t\tccs.downloadchunknum = 0;\n\t\tdl = Z_Malloc(sizeof(*dl));\n\t\t//q3's downloads are relative to root, but they do at least force a pk3 extension.\n\t\tQ_snprintfz(dl->localname, sizeof(dl->localname), \"package/%s\", localname);\n\t\tQ_snprintfz(dl->tempname, sizeof(dl->tempname), \"package/%s\", tempname);\n\t\tdl->prefixbytes = 8;\n\t\tdl->fsroot = FS_ROOT;\n\n\t\tQ_snprintfz(dl->remotename, sizeof(dl->remotename), \"%s.pk3\", filename);\n\t\tdl->method = DL_Q3;\n\t\tdl->percent = 0;\n\t\tccs.download = dl;\n\t\treturn true;\n\t}\n\treturn false;\n}\n\nqboolean CLQ3_SystemInfoChanged(const char *str)\n{\n\tqboolean usingpure, usingcheats;\n\tchar *value;\n\tchar *pc, *pn;\n\tchar *rc, *rn;\n\n\tCon_Printf(\"Server's sv_pure: \\\"%s\\\"\\n\", worldfuncs->GetInfoKey(str, \"sv_pure\"));\n\tusingpure = atoi(worldfuncs->GetInfoKey(str, \"sv_pure\"));\n\tusingcheats = atoi(worldfuncs->GetInfoKey(str, \"sv_cheats\"));\n\tclientfuncs->ForceCheatVars(usingpure||usingcheats, usingcheats);\n\n//\tif (atoi(value))\n//\t\tHost_EndGame(\"Unable to connect to Q3 Pure Servers\\n\");\n\tvalue = worldfuncs->GetInfoKey(str, \"fs_game\");\n\n\tif (!*value)\n\t{\n\t\tvalue = \"baseq3\";\n\t}\n\n\trc = worldfuncs->GetInfoKey(str, \"sv_referencedPaks\");\t//the ones that we should download.\n\trn = worldfuncs->GetInfoKey(str, \"sv_referencedPakNames\");\n\tif (CLQ3_SendDownloads(rc, rn))\n\t\treturn false;\n\n\tpc = worldfuncs->GetInfoKey(str, \"sv_paks\");\t\t//the ones that we are allowed to use (in order!)\n\tpn = worldfuncs->GetInfoKey(str, \"sv_pakNames\");\n\tfsfuncs->PureMode(value, usingpure?2:0, pn, pc, rn, rc, ccs.fs_key);\n\n\treturn true;\t//yay, we're in\n}\n\nvoid CLQ3_ParseGameState(sizebuf_t *msg)\n{\n\tint\t\tc;\n\tint\t\tindex;\n\tchar\t*configString;\n\n//\n// wipe the client_state_t struct\n//\n\tclientfuncs->ClearClientState();\n\tCG_ClearGameState();\n\tccs.firstParseEntity = 0;\n\tmemset(ccs.parseEntities, 0, sizeof(ccs.parseEntities));\n\tmemset(ccs.baselines, 0, sizeof(ccs.baselines));\n\n\tccs.lastServerCommandNum = msgfuncs->ReadLong();\n\n\tfor(;;)\n\t{\n\t\tc = msgfuncs->ReadByte();\n\n\t\tif(c < 0)\n\t\t{\n\t\t\tplugfuncs->EndGame(\"CLQ3_ParseGameState: read past end of server message\");\n\t\t\tbreak;\n\t\t}\n\n\t\tif(c == svcq3_eom)\n\t\t{\n\t\t\tbreak;\n\t\t}\n\n\t\tSHOWNET(va(\"%i\", c));\n\n\t\tswitch(c)\n\t\t{\n\t\tdefault:\n\t\t\tplugfuncs->EndGame(\"CLQ3_ParseGameState: bad command byte %i\", c);\n\t\t\tbreak;\n\n\t\tcase svcq3_configstring:\n\t\t\tindex = msgfuncs->ReadBits(16);\n\t\t\tif (index < 0 || index >= MAX_Q3_CONFIGSTRINGS)\n\t\t\t{\n\t\t\t\tplugfuncs->EndGame(\"CLQ3_ParseGameState: configString index %i out of range\", index);\n\t\t\t}\n\t\t\tconfigString = msgfuncs->ReadString();\n\n\t\t\tCG_InsertIntoGameState(index, configString);\n\t\t\tbreak;\n\n\t\tcase svcq3_baseline:\n\t\t\tindex = msgfuncs->ReadBits(GENTITYNUM_BITS);\n\t\t\tif (index < 0 || index >= MAX_GENTITIES)\n\t\t\t{\n\t\t\t\tplugfuncs->EndGame(\"CLQ3_ParseGameState: baseline index %i out of range\", index);\n\t\t\t}\n\t\t\tMSG_Q3_ReadDeltaEntity(NULL, &ccs.baselines[index], index);\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tccs.playernum = msgfuncs->ReadLong();\n\tccs.fs_key = msgfuncs->ReadLong();\n\n\tif (!CLQ3_SystemInfoChanged(CG_GetConfigString(CFGSTR_SYSINFO)))\n\t{\n\t\tUI_Restart_f();\n\t\treturn;\n\t}\n\n\tscenefuncs->NewMap(NULL);\n\n\tCG_Restart();\n\tUI_Restart_f();\n\n\tif (!ccs.worldmodel)\n\t\tplugfuncs->EndGame(\"CGame didn't set a map.\\n\");\n\n\tclientfuncs->SetLoadingState(false);\n\n\tccs.state = ca_active;\n\n\t{\n\t\tchar buffer[2048];\n\t\tstrcpy(buffer, va(\"cp %i \", ccs.servercount));\n\t\tfsfuncs->GenerateClientPacksList(buffer, sizeof(buffer), ccs.fs_key);\n\t\tCLQ3_SendClientCommand(\"%s\", buffer); // warning: format not a string literal and no format arguments\n\t}\n\n\t// load cgame, etc\n//\tCL_ChangeLevel();\n\n\tcvarfuncs->ForceSetString(\"cl_paused\", \"0\");\n}\n\nint CLQ3_ParseServerMessage (sizebuf_t *msg)\n{\n\tint cmd;\n\tif (!CLQ3_Netchan_Process(msg))\n\t\treturn ccs.state;\t//was a fragment.\n\n\tif (cl_shownet_ptr->value == 1)\n\t\tCon_Printf (\"%i \", msg->cursize);\n\telse if (cl_shownet_ptr->value == 2)\n\t\tCon_Printf (\"------------------\\n\");\n\n\tmsgfuncs->BeginReading(msg, msg_nullnetprim);\n\tccs.serverMessageNum = msgfuncs->ReadLong();\n\tmsg->packing = SZ_HUFFMAN;\t//the rest is huffman compressed.\n\n\t// read last client command number server received\n\tccs.lastClientCommandNum = msgfuncs->ReadLong();\n\tif( ccs.lastClientCommandNum <= ccs.numClientCommands - Q3TEXTCMD_BACKUP )\n\t{\n\t\tccs.lastClientCommandNum = ccs.numClientCommands - Q3TEXTCMD_BACKUP + 1;\n\t}\n\telse if( ccs.lastClientCommandNum > ccs.numClientCommands )\n\t{\n\t\tccs.lastClientCommandNum = ccs.numClientCommands;\n\t}\n\n//\n// parse the message\n//\n\tfor(;;)\n\t{\n\t\tcmd = msgfuncs->ReadByte();\n\n\t\tif(cmd < 0)\t//hm, we have an eom, so only stop when the message is bad.\n\t\t{\n\t\t\tplugfuncs->EndGame(\"CLQ3_ParseServerMessage: read past end of server message\");\n\t\t\tbreak;\n\t\t}\n\n\t\tif(cmd == svcq3_eom)\n\t\t{\n\t\t\tSHOWNET2(\"END OF MESSAGE\", 2);\n\t\t\tbreak;\n\t\t}\n\n\t\tSHOWNET(va(\"%i\", cmd));\n\n\t// other commands\n\t\tswitch(cmd)\n\t\t{\n\t\tdefault:\n\t\t\tplugfuncs->EndGame(\"CLQ3_ParseServerMessage: Illegible server message\");\n\t\t\tbreak;\n\t\tcase svcq3_nop:\n\t\t\tbreak;\n\t\tcase svcq3_gamestate:\n\t\t\tCLQ3_ParseGameState(msg);\n\t\t\tbreak;\n\t\tcase svcq3_serverCommand:\n\t\t\tCLQ3_ParseServerCommand();\n\t\t\tbreak;\n\t\tcase svcq3_download:\n\t\t\tCLQ3_ParseDownload();\n\t\t\tbreak;\n\t\tcase svcq3_snapshot:\n\t\t\tCLQ3_ParseSnapshot();\n\t\t\tbreak;\n\t\t}\n\t}\n\treturn ccs.state;\n}\n\n\n\n\nstatic qboolean CLQ3_Netchan_Process(sizebuf_t *msg)\n{\n\tif(Netchan_ProcessQ3(&ccs.netchan, msg))\n\t{\n#ifndef Q3_NOENCRYPT\n\t\tint\t\tsequence;\n\t\tint\t\tlastClientCommandNum;\n\t\tqbyte\tbitmask;\n\t\tqbyte\tc;\n\t\tint\t\ti, j;\n\t\tchar\t*string;\n\t\tint\t\treadcount;\n\n\t\tmsgfuncs->BeginReading(msg, msg_nullnetprim);\n\t\tsequence = msgfuncs->ReadLong();\n\t\tmsg->packing = SZ_HUFFMAN;\n\t\treadcount = msg->currentbit>>3;\n\t\tlastClientCommandNum = msgfuncs->ReadLong();\n\n\t\t// calculate bitmask\n\t\tbitmask = (sequence ^ ccs.challenge) & 0xff;\n\t\tstring = ccs.clientCommands[lastClientCommandNum & Q3TEXTCMD_MASK];\n\n\t\t// decrypt the packet\n\t\tfor(i=readcount+4,j=0 ; i<msg->cursize ; i++,j++)\n\t\t{\n\t\t\tif(!string[j])\n\t\t\t\tj = 0; // another way around\n\t\t\tc = string[j];\n\t\t\tif(c > 127 || c == '%')\n\t\t\t\tc = '.';\n\t\t\tbitmask ^= c << ((i-readcount) & 1);\n\t\t\tmsg->data[i] ^= bitmask;\n\t\t}\n\t\tmsg->packing = SZ_RAWBITS;\t//first bit was plain...\n#endif\n\t\treturn true;\t//all good\n\t}\n\treturn false;\t//its bad dude, bad.\n}\n\nvoid CL_Netchan_Transmit(struct ftenet_connections_s *socket, int length, const qbyte *data )\n{\n#ifndef Q3_NOENCRYPT\n\tsizebuf_t msg;\n\tchar msgdata[MAX_OVERALLMSGLEN];\n\tint\t\t\tserverid;\n\tint\t\t\tlastSequence;\n\tint\t\t\tlastServerCommandNum;\n\tqbyte\t\tbitmask;\n\tqbyte\t\tc;\n\tint\t\t\ti, j;\n\tchar\t\t*string;\n\n\tmsgfuncs->BeginWriting(&msg, msg_nullnetprim, msgdata, sizeof(msgdata));\n\tmsgfuncs->WriteData(&msg, data, length);\n\n\tif (msg.overflowed)\n\t{\n\t\tplugfuncs->EndGame(\"Client message overflowed\");\n\t}\n\n\tmsgfuncs->BeginReading(&msg, msg_nullnetprim);\n\tmsg.packing = SZ_HUFFMAN;\n\n\tserverid = msgfuncs->ReadLong();\n\tlastSequence = msgfuncs->ReadLong();\n\tlastServerCommandNum = msgfuncs->ReadLong();\n\n\t// calculate bitmask\n\tbitmask = (lastSequence ^ serverid ^ ccs.challenge) & 0xff;\n\tstring = ccs.serverCommands[lastServerCommandNum & Q3TEXTCMD_MASK];\n\n\t// encrypt the packet\n\tfor (i=12,j=0 ; i<msg.cursize ; i++,j++)\n\t{\n\t\tif (!string[j])\n\t\t{\n\t\t\tj = 0; // another way around\n\t\t}\n\t\tc = string[j];\n\t\tif (c > 127 || c == '%')\n\t\t{\n\t\t\tc = '.';\n\t\t}\n\t\tbitmask ^= c << (i & 1);\n\t\tmsg.data[i] ^= bitmask;\n\t}\n\tdata = msg.data;\n\tlength = msg.cursize;\n#endif\n\tNetchan_TransmitQ3(socket, &ccs.netchan, length, data);\n}\n\n\n\n\nstatic void MSG_WriteDeltaKey( sizebuf_t *msg, int key, int from, int to, int bits )\n{\n\tif( from == to )\n\t{\n\t\tmsgfuncs->WriteBits( msg, 0, 1 );\n\t\treturn; // unchanged\n\t}\n\n\tmsgfuncs->WriteBits( msg, 1, 1 );\n\tmsgfuncs->WriteBits( msg, to ^ key, bits );\n}\n\nvoid MSG_Q3_WriteDeltaUsercmd( sizebuf_t *msg, int key, const usercmd_t *from, const usercmd_t *to )\n{\n\t// figure out how to pack serverTime\n\tif( to->servertime - from->servertime < 255 )\n\t{\n\t\tmsgfuncs->WriteBits(msg, 1, 1);\n\t\tmsgfuncs->WriteBits(msg, to->servertime - from->servertime, 8);\n\t}\n\telse\n\t{\n\t\tmsgfuncs->WriteBits( msg, 0, 1 );\n\t\tmsgfuncs->WriteBits( msg, to->servertime, 32);\n\t}\n\n\tif( !memcmp( (qbyte *)from + 4, (qbyte *)to + 4, sizeof( usercmd_t ) - 4 ) )\n\t{\n\t\tmsgfuncs->WriteBits(msg, 0, 1);\n\t\treturn; // nothing changed\n\t}\n\tkey ^= to->servertime;\n\tmsgfuncs->WriteBits(msg, 1, 1);\n\tMSG_WriteDeltaKey(msg, key, from->angles[0],\t\tto->angles[0],\t\t16);\n\tMSG_WriteDeltaKey(msg, key, from->angles[1],\t\tto->angles[1],\t\t16);\n\tMSG_WriteDeltaKey(msg, key, from->angles[2],\t\tto->angles[2],\t\t16);\n\tMSG_WriteDeltaKey(msg, key, from->forwardmove,\t\tto->forwardmove,\t 8);\n\tMSG_WriteDeltaKey(msg, key, from->sidemove,\t\t\tto->sidemove,\t\t 8);\n\tMSG_WriteDeltaKey(msg, key, from->upmove,\t\t\tto->upmove,\t\t\t 8);\n\tMSG_WriteDeltaKey(msg, key, from->buttons,\t\t\tto->buttons,\t\t16);\n\tMSG_WriteDeltaKey(msg, key, from->weapon,\t\t\tto->weapon,\t\t\t 8);\n}\n\n\n\nvoid VARGS CLQ3_SendClientCommand(const char *fmt, ...)\n{\n\tva_list\targptr;\n\tchar\tcommand[MAX_STRING_CHARS];\n\n\tva_start( argptr, fmt );\n\tvsnprintf( command, sizeof(command), fmt, argptr );\n\tva_end( argptr );\n\n\t// create new clientCommand\n\tccs.numClientCommands++;\n\n\t// check if server will lose some of our clientCommands\n\tif(ccs.numClientCommands - ccs.lastClientCommandNum >= Q3TEXTCMD_BACKUP)\n\t\tplugfuncs->EndGame(\"Client command overflow\");\n\n\tQ_strncpyz(ccs.clientCommands[ccs.numClientCommands & Q3TEXTCMD_MASK], command, sizeof(ccs.clientCommands[0]));\n\tCon_DPrintf(\"Sending %s\\n\", command);\n}\n\nvoid CLQ3_SendCmd(struct ftenet_connections_s *socket, usercmd_t *cmd, unsigned int movesequence, double gametime)\n{\n\tchar *string;\n\tint i;\n\tchar data[MAX_OVERALLMSGLEN];\n\tsizebuf_t msg;\n//\toutframe_t *frame;\n\tunsigned int oldsequence;\n\tint cmdcount, key;\n\tconst usercmd_t *to, *from;\n\tstatic usercmd_t nullcmd;\n\n\t//reuse the q1 array\n\tcmd->servertime = gametime*1000;\n\tcmd->weapon = ccs.selected_weapon;\n\n\n\tcmd->forwardmove *= 127/400.0f;\n\tcmd->sidemove *= 127/400.0f;\n\tcmd->upmove *= 127/400.0f;\n\tif (cmd->forwardmove > 127)\n\t\tcmd->forwardmove = 127;\n\tif (cmd->forwardmove < -127)\n\t\tcmd->forwardmove = -127;\n\tif (cmd->sidemove > 127)\n\t\tcmd->sidemove = 127;\n\tif (cmd->sidemove < -127)\n\t\tcmd->sidemove = -127;\n\tif (cmd->upmove > 127)\n\t\tcmd->upmove = 127;\n\tif (cmd->upmove < -127)\n\t\tcmd->upmove = -127;\n\n\tif (cmd->buttons & 2)\t//jump\n\t{\n\t\tcmd->upmove = 100;\n\t\tcmd->buttons &= ~2;\n\t}\n\tif (inputfuncs->GetKeyDest() & ~kdm_game)\n\t\tcmd->buttons |= 2;\t//add in the 'at console' button\n\n\t//FIXME: q3 generates a new command every video frame, but a new packet at a more limited rate.\n\t//FIXME: we should return here if its not yet time for a network frame.\n\n/*\tframe = &cl.outframes[ccs.netchan.outgoing_sequence & Q3CMD_MASK];\n\tframe->cmd_sequence = movesequence;\n\tframe->server_message_num = ccs.serverMessageNum;\n\tframe->server_time = gametime;\n\tframe->client_time = plugfuncs->GetMilliseconds();\n*/\n\tmemset(&msg, 0, sizeof(msg));\n\tmsg.maxsize = sizeof(data);\n\tmsg.data = data;\n\tmsg.packing = SZ_HUFFMAN;\n\n\tmsgfuncs->WriteBits(&msg, ccs.servercount, 32);\n\tmsgfuncs->WriteBits(&msg, ccs.serverMessageNum, 32);\n\tmsgfuncs->WriteBits(&msg, ccs.lastServerCommandNum, 32);\n\n\t// write clientCommands not acknowledged by server yet\n\tfor (i=ccs.lastClientCommandNum+1; i<=ccs.numClientCommands; i++)\n\t{\n\t\tmsgfuncs->WriteBits(&msg, clcq3_clientCommand, 8);\n\t\tmsgfuncs->WriteBits(&msg, i, 32);\n\t\tstring = ccs.clientCommands[i & Q3TEXTCMD_MASK];\n\t\twhile(*string)\n\t\t\tmsgfuncs->WriteBits(&msg, *string++, 8);\n\t\tmsgfuncs->WriteBits(&msg, 0, 8);\n\t}\n\n\ti = ccs.netchan.outgoing_sequence;\n\ti -= bound(0, cl_c2sdupe_ptr->ival, 5); //extra age, if desired\n\ti--;\n\tif (i < ccs.netchan.outgoing_sequence-Q3CMD_MASK)\n\t\ti = ccs.netchan.outgoing_sequence-Q3CMD_MASK;\n\toldsequence = movesequence-1;//cl.outframes[i & Q3CMD_MASK].cmd_sequence;\n\tcmdcount = movesequence - oldsequence;\n\tif (cmdcount > Q3CMD_MASK)\n\t\tcmdcount = Q3CMD_MASK;\n\n\t// begin a client move command, if any\n\tif (cmdcount)\n\t{\n\t\tif(cl_nodelta_ptr->value || !ccs.snap.valid || ccs.snap.serverMessageNum != ccs.serverMessageNum)\n\t\t\tmsgfuncs->WriteBits(&msg, clcq3_nodeltaMove, 8); // no compression\n\t\telse\n\t\t\tmsgfuncs->WriteBits(&msg, clcq3_move, 8);\n\n\t\t// write cmdcount\n\t\tmsgfuncs->WriteBits(&msg, cmdcount, 8);\n\n\t\t// calculate key\n\t\tstring = ccs.serverCommands[ccs.lastServerCommandNum & Q3TEXTCMD_MASK];\n\t\tkey = ccs.fs_key ^ ccs.serverMessageNum ^ StringKey(string, 32);\n\n\t\t//note that q3 uses timestamps so sequences are not important\n\t\t//we can also send dupes without issue.\n\t\tfrom = &nullcmd;\n\t\tfor (i = movesequence-cmdcount; i < movesequence; i++)\n\t\t{\n\t\t\tto = inputfuncs->GetMoveEntry(i);\n\t\t\tif (!to)\n\t\t\t\tto = from;\n\t\t\tMSG_Q3_WriteDeltaUsercmd( &msg, key, from, to );\n\t\t\tfrom = to;\n\t\t}\n\t}\n\n\tmsgfuncs->WriteBits(&msg, clcq3_eom, 8);\n\n\tCL_Netchan_Transmit(socket, msg.cursize, msg.data );\n\twhile(ccs.netchan.reliable_length)\n\t\tNetchan_TransmitNextFragment(socket, &ccs.netchan);\n}\n\nvoid CLQ3_SendAuthPacket(struct ftenet_connections_s *socket, netadr_t *gameserver)\n{\n#ifdef HAVE_PACKET\n\tchar data[2048];\n\tsizebuf_t msg;\n\n//send the auth packet\n//this should be the right code, but it doesn't work.\n\tif (gameserver->type == NA_IP && gameserver->prot == NP_DGRAM)\n\t{\n\t\tchar *key = cvarfuncs->GetNVFDG(\"cl_cdkey\", \"\", CVAR_ARCHIVE, \"Quake3 auth\", \"Q3 Compat\")->string;\n\t\tnetadr_t authaddr;\n#define\tQ3_AUTHORIZE_SERVER_NAME\t\"authorize.quake3arena.com:27952\"\n\t\tif (*key)\n\t\t{\n\t\t\tCon_Printf(\"Resolving %s\\n\", Q3_AUTHORIZE_SERVER_NAME);\n\t\t\tif (masterfuncs->StringToAdr(Q3_AUTHORIZE_SERVER_NAME, 0, &authaddr, 1, NULL))\n\t\t\t{\n\t\t\t\tmsgfuncs->BeginWriting(&msg, msg_nullnetprim, data, sizeof(data));\n\t\t\t\tmsgfuncs->WriteLong(&msg, -1);\n\t\t\t\tmsgfuncs->WriteString(&msg, \"getKeyAuthorize 0 \");\n\t\t\t\tmsg.cursize--;\n\t\t\t\twhile(*key)\n\t\t\t\t{\n\t\t\t\t\tif ((*key >= 'a' && *key <= 'z') || (*key >= 'A' && *key <= 'Z') || (*key >= '0' && *key <= '9'))\n\t\t\t\t\t\tmsgfuncs->WriteByte(&msg, *key);\n\t\t\t\t\tkey++;\n\t\t\t\t}\n\t\t\t\tmsgfuncs->WriteByte(&msg, 0);\n\n\t\t\t\tmsgfuncs->SendPacket(socket, msg.cursize, msg.data, &authaddr);\n\t\t\t}\n\t\t\telse\n\t\t\t\tCon_Printf(\"    failed\\n\");\n\t\t}\n\t}\n#endif\n}\n\nvoid CLQ3_SendConnectPacket(struct ftenet_connections_s *socket, netadr_t *to, int challenge, int qport, infobuf_t *userinfo)\n{\n\tchar infostr[1024];\n\tchar data[2048];\n\tsizebuf_t msg;\n\tstatic const char *priorityq3[] = {\"*\", \"name\", NULL};\n\tstatic const char *nonq3[] = {\"challenge\", \"qport\", \"protocol\", \"ip\", \"chat\", NULL};\n\tint protocol = cvarfuncs->GetFloat(\"com_protocolversion\");\n\n\tmemset(&ccs, 0, sizeof(ccs));\n\tccs.servercount = -1;\n\tccs.challenge = challenge;\n\tNetchan_SetupQ3(NCF_CLIENT, &ccs.netchan, to, qport);\n\n\tworldfuncs->IBufToInfo(userinfo, infostr, sizeof(infostr), priorityq3, nonq3, NULL, NULL,/*&cls.userinfosync,*/ userinfo);\n\n\tmsgfuncs->BeginWriting(&msg, msg_nullnetprim, data, sizeof(data));\n\tmsgfuncs->WriteLong(&msg, -1);\n\tmsgfuncs->WriteString(&msg, va(\"connect \\\"\\\\challenge\\\\%i\\\\qport\\\\%i\\\\protocol\\\\%i%s\\\"\", challenge, qport, protocol, infostr));\n#ifdef HUFFNETWORK\n\tif (msgfuncs->Huff_EncryptPacket)\n\t\tmsgfuncs->Huff_EncryptPacket(&msg, 12);\n\tif (!msgfuncs->Huff_CompressionCRC || !msgfuncs->Huff_CompressionCRC(HUFFCRC_QUAKE3))\n\t{\n\t\tCon_Printf(\"Huffman compression error\\n\");\n\t\treturn;\n\t}\n#endif\n\tmsgfuncs->SendPacket (socket, msg.cursize, msg.data, to);\n}\n\nvoid CLQ3_Established(void)\n{\n\tccs.state = ca_connected;\n}\n\nvoid CLQ3_Disconnect(struct ftenet_connections_s *socket)\n{\n\tccs.state = ca_disconnected;\n}\n#endif\n\n"
  },
  {
    "path": "plugins/quake3/clq3_ui.c",
    "content": "#include \"q3common.h\"\n#ifdef VM_UI\n#include \"clq3defs.h\"\n#include \"ui_public.h\"\n#include \"cl_master.h\"\n#include \"shader.h\"\n\nstatic int keycatcher;\n\n#include \"botlib/botlib.h\"\nvoid SV_InitBotLib(void);\nextern botlib_export_t *botlib;\nqboolean CG_GetLimboString(int index, char *outbuf);\n\n#define TT_STRING\t\t\t\t\t1\t\t\t// string\n#define TT_LITERAL\t\t\t\t\t2\t\t\t// literal\n#define TT_NUMBER\t\t\t\t\t3\t\t\t// number\n#define TT_NAME\t\t\t\t\t\t4\t\t\t// name\n#define TT_PUNCTUATION\t\t\t\t5\t\t\t// punctuation\n\n#define SCRIPT_MAXDEPTH 64\n#define SCRIPT_DEFINELENGTH 256\ntypedef struct {\n\tchar *filestack[SCRIPT_MAXDEPTH];\n\tchar *originalfilestack[SCRIPT_MAXDEPTH];\n\tchar *lastreadptr;\n\tint lastreaddepth;\n\tchar filename[MAX_QPATH][SCRIPT_MAXDEPTH];\n\tint stackdepth;\n\n\tchar *defines;\n\tint numdefines;\n} script_t;\nstatic script_t *scripts;\nstatic int maxscripts;\nstatic float ui_size[2];\t//to track when it needs to be restarted (the api has no video mode changed event)\nstatic menu_t uimenu;\n\nvoid Q3_SetKeyCatcher(int newcatcher)\n{\n\tint delta = newcatcher^keycatcher;\n\tkeycatcher = newcatcher;\n\tif (delta & 2)\n\t{\n\t\tuimenu.isopaque = false; //no surprises.\n\t\tif (newcatcher&2)\n\t\t\tinputfuncs->Menu_Push(&uimenu, false);\n\t\telse\n\t\t\tinputfuncs->Menu_Unlink(&uimenu, false);\n\t}\n}\nint Q3_GetKeyCatcher(void)\n{\n\treturn keycatcher;\n}\n#define Q3SCRIPTPUNCTUATION \"(,{})(\\':;=!><&|+-\\\"\"\nvoid StripCSyntax (char *s)\n{\n\twhile(*s)\n\t{\n\t\tif (*s == '\\\\')\n\t\t{\n\t\t\tmemmove(s, s+1, strlen(s+1)+1);\n\t\t\tswitch (*s)\n\t\t\t{\n\t\t\tcase 'r':\n\t\t\t\t*s = '\\r';\n\t\t\t\tbreak;\n\t\t\tcase 'n':\n\t\t\t\t*s = '\\n';\n\t\t\t\tbreak;\n\t\t\tcase '\\\\':\n\t\t\t\t*s = '\\\\';\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\t*s = '?';\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\ts++;\n\t}\n}\nint Script_Read(int handle, struct pc_token_s *token)\n{\n\tchar *s;\n\tchar readstring[8192];\n\tint i;\n\tscript_t *sc = scripts+handle-1;\n\tchar thetoken[1024];\n\tcom_tokentype_t tokentype;\n\n\tfor(;;)\n\t{\n\t\tif (!sc->stackdepth)\n\t\t{\n\t\t\tmemset(token, 0, sizeof(*token));\n\t\t\treturn 0;\n\t\t}\n\n\t\ts = sc->filestack[sc->stackdepth-1];\n\t\tsc->lastreadptr = s;\n\t\tsc->lastreaddepth = sc->stackdepth;\n\n\t\ts = (char *)cmdfuncs->ParsePunctuation(s, Q3SCRIPTPUNCTUATION, thetoken, sizeof(thetoken), &tokentype);\n\t\tQ_strncpyz(readstring, thetoken, sizeof(readstring));\n\t\tif (tokentype == TTP_STRING)\n\t\t{\n\t\t\twhile(s)\n\t\t\t{\n\t\t\t\twhile (*s > '\\0' && *s <= ' ')\n\t\t\t\t\ts++;\n\t\t\t\tif (*s == '/' && s[1] == '/')\n\t\t\t\t{\n\t\t\t\t\twhile(*s && *s != '\\n')\n\t\t\t\t\t\ts++;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\twhile (*s > '\\0' && *s <= ' ')\n\t\t\t\t\ts++;\n\t\t\t\tif (*s == '\\\"')\n\t\t\t\t{\n\t\t\t\t\ts = (char*)cmdfuncs->ParsePunctuation(s, Q3SCRIPTPUNCTUATION, thetoken, sizeof(thetoken), &tokentype);\n\t\t\t\t\tQ_strncatz(readstring, thetoken, sizeof(readstring));\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tsc->filestack[sc->stackdepth-1] = s;\n\t\tif (tokentype == TTP_LINEENDING)\n\t\t\tcontinue;\t//apparently we shouldn't stop on linebreaks\n\n\t\tif (!strcmp(readstring, \"#include\"))\n\t\t{\n\t\t\tsc->filestack[sc->stackdepth-1] = (char *)cmdfuncs->ParsePunctuation(sc->filestack[sc->stackdepth-1], Q3SCRIPTPUNCTUATION, thetoken, sizeof(thetoken), &tokentype);\n\n\t\t\tif (sc->stackdepth == SCRIPT_MAXDEPTH)\t//just don't enter it\n\t\t\t\tcontinue;\n\n\t\t\tif (sc->originalfilestack[sc->stackdepth])\n\t\t\t\tplugfuncs->Free(sc->originalfilestack[sc->stackdepth]);\n\t\t\tsc->filestack[sc->stackdepth] = sc->originalfilestack[sc->stackdepth] = fsfuncs->LoadFile(thetoken, NULL);\n\t\t\tQ_strncpyz(sc->filename[sc->stackdepth], thetoken, MAX_QPATH);\n\t\t\tsc->stackdepth++;\n\t\t\tcontinue;\n\t\t}\n\t\tif (!strcmp(readstring, \"#define\"))\n\t\t{\n\t\t\tsc->numdefines++;\n\t\t\tsc->defines = plugfuncs->Realloc(sc->defines, sc->numdefines*SCRIPT_DEFINELENGTH*2);\n\t\t\tsc->filestack[sc->stackdepth-1] = (char *)cmdfuncs->ParsePunctuation(sc->filestack[sc->stackdepth-1], Q3SCRIPTPUNCTUATION, thetoken, sizeof(thetoken), &tokentype);\n\t\t\tQ_strncpyz(sc->defines+SCRIPT_DEFINELENGTH*2*(sc->numdefines-1), thetoken, SCRIPT_DEFINELENGTH);\n\t\t\tsc->filestack[sc->stackdepth-1] = (char *)cmdfuncs->ParsePunctuation(sc->filestack[sc->stackdepth-1], Q3SCRIPTPUNCTUATION, thetoken, sizeof(thetoken), &tokentype);\n\t\t\tQ_strncpyz(sc->defines+SCRIPT_DEFINELENGTH*2*(sc->numdefines-1)+SCRIPT_DEFINELENGTH, thetoken, SCRIPT_DEFINELENGTH);\n\n\t\t\tcontinue;\n\t\t}\n\t\tif (!*readstring && tokentype != TTP_STRING)\n\t\t{\n\t\t\tif (sc->stackdepth==0)\n\t\t\t{\n\t\t\t\tmemset(token, 0, sizeof(*token));\n\t\t\t\treturn 0;\n\t\t\t}\n\n\t\t\tsc->stackdepth--;\n\t\t\tcontinue;\n\t\t}\n\t\tbreak;\n\t}\n\tif (tokentype == TTP_STRING)\n\t{\n\t\ti = sc->numdefines;\n\t}\n\telse\n\t{\n\t\tfor (i = 0; i < sc->numdefines; i++)\n\t\t{\n\t\t\tif (!strcmp(readstring, sc->defines+SCRIPT_DEFINELENGTH*2*i))\n\t\t\t{\n\t\t\t\tQ_strncpyz(token->string, sc->defines+SCRIPT_DEFINELENGTH*2*i+SCRIPT_DEFINELENGTH, sizeof(token->string));\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\tif (i == sc->numdefines)\t//otherwise\n\t\tQ_strncpyz(token->string, readstring, sizeof(token->string));\n\n\tStripCSyntax(token->string);\n\n\tif (token->string[0] == '0' && (token->string[1] == 'x'||token->string[1] == 'X'))\n\t{\n\t\ttoken->intvalue = strtoul(token->string, NULL, 16);\n\t\ttoken->floatvalue = token->intvalue;\n\t\ttoken->type = TT_NUMBER;\n\t\ttoken->subtype = 0x100;//TT_HEX;\n\t}\n\telse\n\t{\n\t\ttoken->intvalue = atoi(token->string);\n\t\ttoken->floatvalue = atof(token->string);\n\t\tif (token->floatvalue || *token->string == '0' || *token->string == '.')\n\t\t{\n\t\t\ttoken->type = TT_NUMBER;\n\t\t\ttoken->subtype = 0;\n\t\t}\n\t\telse if (tokentype == TTP_STRING)\n\t\t{\n\t\t\ttoken->type = TT_STRING;\n\t\t\ttoken->subtype = strlen(token->string);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (token->string[1] == '\\0')\n\t\t\t{\n\t\t\t\ttoken->type = TT_PUNCTUATION;\n\t\t\t\ttoken->subtype = token->string[0];\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\ttoken->type = TT_NAME;\n\t\t\t\ttoken->subtype = strlen(token->string);\n\t\t\t}\n\t\t}\n\t}\n\n//\tCon_Printf(\"Found %s (%i, %i)\\n\", token->string, token->type, token->subtype);\n\treturn tokentype != TTP_EOF;\n}\n\nint Script_LoadFile(char *filename)\n{\n\tint i;\n\tscript_t *sc;\n\tfor (i = 0; i < maxscripts; i++)\n\t\tif (!scripts[i].stackdepth)\n\t\t\tbreak;\n\tif (i == maxscripts)\n\t{\n\t\tmaxscripts++;\n\t\tscripts = plugfuncs->Realloc(scripts, sizeof(script_t)*maxscripts);\n\t}\n\t\n\tsc = scripts+i;\n\tmemset(sc, 0, sizeof(*sc));\n\tsc->filestack[0] = sc->originalfilestack[0] = fsfuncs->LoadFile(filename, NULL);\n\tQ_strncpyz(sc->filename[sc->stackdepth], filename, MAX_QPATH);\n\tsc->stackdepth = 1;\n\n\treturn i+1;\n}\n\nvoid Script_Free(int handle)\n{\n\tint i;\n\tscript_t *sc = scripts+handle-1;\n\tif (sc->defines)\n\t\tplugfuncs->Free(sc->defines);\n\n\tfor (i = 0; i < sc->stackdepth; i++)\n\t\tplugfuncs->Free(sc->originalfilestack[i]);\n\n\tsc->stackdepth = 0;\n}\n\nvoid Script_Get_File_And_Line(int handle, char *filename, int *line)\n{\n\tscript_t *sc = scripts+handle-1;\n\tchar *src;\n\tchar *start;\n\n\tif (!sc->lastreaddepth)\n\t{\n\t\t*line = 0;\n\t\tQ_strncpyz(filename, sc->filename[0], MAX_QPATH);\n\t\treturn;\n\t}\n\t*line = 1;\n\n\tsrc = sc->lastreadptr;\n\tstart = sc->originalfilestack[sc->lastreaddepth-1];\n\n\twhile(start < src)\n\t{\n\t\tif (*start == '\\n')\n\t\t\t(*line)++;\n\t\tstart++;\n\t}\n\n\tQ_strncpyz(filename, sc->filename[sc->lastreaddepth-1], MAX_QPATH);\n\n}\n\n\n\n\n\n\n\n\n\n\n\nstatic vm_t *uivm;\n\n#define MAX_PINGREQUESTS 32\n\nstatic struct\n{\n\tunsigned int startms;\n\tnetadr_t adr;\n\tchar adrstring[64];\n\tconst char *broker;\n} ui_pings[MAX_PINGREQUESTS];\n\n#define UITAGNUM 2452\n\nstruct q3refEntity_s {\n\trefEntityType_t\treType;\n\tint\t\t\trenderfx;\n\n\tint hModel;\t\t\t\t// opaque type outside refresh\n\n\t// most recent data\n\tvec3_t\t\tlightingOrigin;\t\t// so multi-part models can be lit identically (RF_LIGHTING_ORIGIN)\n\tfloat\t\tshadowPlane;\t\t// projection shadows go here, stencils go slightly lower\n\n\tvec3_t\t\taxis[3];\t\t\t// rotation vectors\n\tqboolean\tnonNormalizedAxes;\t// axis are not normalized, i.e. they have scale\n\tfloat\t\torigin[3];\t\t\t// also used as MODEL_BEAM's \"from\"\n\tint\t\t\tframe;\t\t\t\t// also used as MODEL_BEAM's diameter\n\n\t// previous data for frame interpolation\n\tfloat\t\toldorigin[3];\t\t// also used as MODEL_BEAM's \"to\"\n\tint\t\t\toldframe;\n\tfloat\t\tbacklerp;\t\t\t// 0.0 = current, 1.0 = old\n\n\t// texturing\n\tint\t\t\tskinNum;\t\t\t// inline skin index\n\tskinid_t\tcustomSkin;\t\t\t// NULL for default skin\n\tint\t\t\tcustomShader;\t\t// use one image for the entire thing\n\n\t// misc\n\tqbyte\t\tshaderRGBA[4];\t\t// colors used by rgbgen entity shaders\n\tfloat\t\tshaderTexCoord[2];\t// texture coordinates used by tcMod entity modifiers\n\tfloat\t\tshaderTime;\t\t\t// subtracted from refdef time to control effect start times\n\n\t// extra sprite information\n\tfloat\t\tradius;\n\tfloat\t\trotation;\n};\n\nstruct q3polyvert_s\n{\n\tvec3_t org;\n\tvec2_t tcoord;\n\tqbyte colours[4];\n};\n\n#define Q3RF_MINLIGHT\t\t\t1\n#define\tQ3RF_THIRD_PERSON\t\t2\t\t// don't draw through eyes, only mirrors (player bodies, chat sprites)\n#define\tQ3RF_FIRST_PERSON\t\t4\t\t// only draw through eyes (view weapon, damage blood blob)\n#define\tQ3RF_DEPTHHACK\t\t\t8\t\t// for view weapon Z crunching\n#define Q3RF_NOSHADOW\t\t\t64\n#define Q3RF_LIGHTING_ORIGIN\t128\n\n#define MAX_VMQ3_CACHED_STRINGS 2048\nstatic char *stringcache[MAX_VMQ3_CACHED_STRINGS];\n\nvoid VMQ3_FlushStringHandles(void)\n{\n\tint i;\n\tfor (i = 0; i < MAX_VMQ3_CACHED_STRINGS; i++)\n\t{\n\t\tif (stringcache[i])\n\t\t{\n\t\t\tZ_Free(stringcache[i]);\n\t\t\tstringcache[i] = NULL;\n\t\t}\n\t}\n}\n\nchar *VMQ3_StringFromHandle(int handle)\n{\n\tif (!handle)\n\t\treturn \"\";\n\thandle--;\n\tif ((unsigned) handle >= MAX_VMQ3_CACHED_STRINGS)\n\t\treturn \"\";\n\treturn stringcache[handle];\n}\n\nint VMQ3_StringToHandle(char *str)\n{\n\tint i;\n\tfor (i = 0; i < MAX_VMQ3_CACHED_STRINGS; i++)\n\t{\n\t\tif (!stringcache[i])\n\t\t\tbreak;\n\t\tif (!strcmp(str, stringcache[i]))\n\t\t\treturn i+1;\n\t}\n\tif (i == MAX_VMQ3_CACHED_STRINGS)\n\t{\n\t\tCon_Printf(\"Q3VM out of string handle space\\n\");\n\t\treturn 0;\n\t}\n\tstringcache[i] = Z_Malloc(strlen(str)+1);\n\tstrcpy(stringcache[i], str);\n\treturn i+1;\n}\n\n#define VM_TOSTRCACHE(a) VMQ3_StringToHandle(VM_POINTER(a))\n#define VM_FROMSTRCACHE(a) VMQ3_StringFromHandle(a)\n\nvoid VQ3_AddEntity(const q3refEntity_t *q3)\n{\n\tentity_t ent;\n\tmemset(&ent, 0, sizeof(ent));\n\tent.model = scenefuncs->ModelFromId(q3->hModel);\n\tent.framestate.g[FS_REG].frame[0] = q3->frame;\n\tent.framestate.g[FS_REG].frame[1] = q3->oldframe;\n\tmemcpy(ent.axis, q3->axis, sizeof(q3->axis));\n\tent.framestate.g[FS_REG].lerpweight[1] = q3->backlerp;\n\tent.framestate.g[FS_REG].lerpweight[0] = 1 - ent.framestate.g[FS_REG].lerpweight[1];\n\tif (q3->reType == RT_SPRITE)\n\t{\n\t\tent.scale = q3->radius;\n\t\tent.rotation = q3->rotation;\n\t}\n\telse\n\t\tent.scale = 1;\n\tent.rtype = q3->reType;\n\n\tent.customskin = q3->customSkin;\n\tent.skinnum = q3->skinNum;\n\n\tent.shaderRGBAf[0] = q3->shaderRGBA[0]/255.0f;\n\tent.shaderRGBAf[1] = q3->shaderRGBA[1]/255.0f;\n\tent.shaderRGBAf[2] = q3->shaderRGBA[2]/255.0f;\n\tent.shaderRGBAf[3] = q3->shaderRGBA[3]/255.0f;\n\n\t/*don't set force-translucent etc, the shader is meant to already be correct*/\n//\tif (ent.shaderRGBAf[3] <= 0)\n//\t\treturn;\n\n\tent.forcedshader = drawfuncs->ShaderFromId(q3->customShader);\n\tent.shaderTime = q3->shaderTime;\n\n\tif (q3->renderfx & Q3RF_FIRST_PERSON)\n\t\tent.flags |= RF_FIRSTPERSON;\n\tif (q3->renderfx & Q3RF_DEPTHHACK)\n\t\tent.flags |= RF_DEPTHHACK;\n\tif (q3->renderfx & Q3RF_THIRD_PERSON)\n\t\tent.flags |= RF_EXTERNALMODEL;\n\tif (q3->renderfx & Q3RF_NOSHADOW)\n\t\tent.flags |= RF_NOSHADOW;\n\n\tent.topcolour = TOP_DEFAULT;\n\tent.bottomcolour = BOTTOM_DEFAULT;\n\tent.playerindex = -1;\n\n\n\tif ((q3->renderfx & Q3RF_LIGHTING_ORIGIN) && ent.model)\n\t{\n\t\tVectorCopy(q3->lightingOrigin, ent.origin);\n\t\tscenefuncs->CalcModelLighting(&ent, ent.model);\n\t}\n\n\tVectorCopy(q3->origin, ent.origin);\n\tVectorCopy(q3->oldorigin, ent.oldorigin);\n\n\tscenefuncs->AddEntity(&ent);\n}\n\nvoid VQ3_AddPolys(shader_t *s, int numverts, q3polyvert_t *verts, size_t polycount)\n{\n\tunsigned int v;\n\n\tfor (; polycount-->0; verts += numverts)\n\t{\n\t\tvecV_t *vertcoord;\n\t\tvec2_t *texcoord;\n\t\tvec4_t *colour;\n\t\tindex_t *indexes;\n\t\tindex_t indexbias = scenefuncs->AddPolydata(s, BEF_NODLIGHT|BEF_NOSHADOWS, numverts, (numverts-2)*3, &vertcoord, &texcoord, &colour, &indexes);\n\n\t\t//and splurge the data out.\n\t\tfor (v = 0; v < numverts; v++)\n\t\t{\n\t\t\tVectorCopy(verts[v].org, vertcoord[v]);\n\t\t\tVector2Copy(verts[v].tcoord, texcoord[v]);\n\t\t\tVector4Scale(verts[v].colours, (1/255.0f), colour[v]);\n\t\t}\n\t\tfor (v = 2; v < numverts; v++)\n\t\t{\n\t\t\t*indexes++ = indexbias + 0;\n\t\t\t*indexes++ = indexbias + (v-1);\n\t\t\t*indexes++ = indexbias + v;\n\t\t}\n\t}\n}\n\nint VM_LerpTag(void *out, model_t *model, int f1, int f2, float l2, char *tagname)\n{\n\tint tagnum;\n\tfloat *ang;\n\tfloat *org;\n\n\tfloat tr[12];\n\tqboolean found;\n\tframestate_t fstate;\n\n\torg = (float*)out;\n\tang = ((float*)out+3);\n\n\tmemset(&fstate, 0, sizeof(fstate));\n\tfstate.g[FS_REG].frame[0] = f1;\n\tfstate.g[FS_REG].frame[1] = f2;\n\tfstate.g[FS_REG].lerpweight[0] = 1 - l2;\n\tfstate.g[FS_REG].lerpweight[1] = l2;\n\n\ttagnum = scenefuncs->TagNumForName(model, tagname, 0);\n\tfound = scenefuncs->GetTag(model, tagnum, &fstate, tr);\n\n\tif (found && tagnum)\n\t{\n\t\tang[0] = tr[0];\n\t\tang[1] = tr[1];\n\t\tang[2] = tr[2];\n\t\torg[0] = tr[3];\n\n\t\tang[3] = tr[4];\n\t\tang[4] = tr[5];\n\t\tang[5] = tr[6];\n\t\torg[1] = tr[7];\n\n\t\tang[6] = tr[8];\n\t\tang[7] = tr[9];\n\t\tang[8] = tr[10];\n\t\torg[2] = tr[11];\n\n\t\treturn true;\n\t}\n\telse\n\t{\n\t\torg[0] = 0;\n\t\torg[1] = 0;\n\t\torg[2] = 0;\n\n\t\tang[0] = 1;\n\t\tang[1] = 0;\n\t\tang[2] = 0;\n\n\t\tang[3] = 0;\n\t\tang[4] = 1;\n\t\tang[5] = 0;\n\n\t\tang[6] = 0;\n\t\tang[7] = 0;\n\t\tang[8] = 1;\n\n\t\treturn false;\n\t}\n}\n\n#define\tMAX_RENDER_STRINGS\t\t\t8\n#define\tMAX_RENDER_STRING_LENGTH\t32\n\nstruct q3refdef_s {\n\tint\t\t\tx, y, width, height;\n\tfloat\t\tfov_x, fov_y;\n\tvec3_t\t\tvieworg;\n\tvec3_t\t\tviewaxis[3];\t\t// transformation matrix\n\n\t// time in milliseconds for shader effects and other time dependent rendering issues\n\tint\t\t\ttime;\n\n\tint\t\t\trdflags;\t\t\t// RDF_NOWORLDMODEL, etc\n\n\t// 1 bits will prevent the associated area from rendering at all\n\tqbyte\t\tareamask[MAX_MAP_AREA_BYTES];\n\n\t// text messages for deform text shaders\n\tchar\t\ttext[MAX_RENDER_STRINGS][MAX_RENDER_STRING_LENGTH];\n};\n\nvoid VQ3_RenderView(const q3refdef_t *ref)\n{\n\tplugrefdef_t scene;\n\n\tscene.flags = 0;\n\n\n\tscene.rect.x = ref->x;\n\tscene.rect.y = ref->y;\n\tscene.rect.w = ref->width;\n\tscene.rect.h = ref->height;\n\tscene.fov[0] = scene.fov_viewmodel[0] = ref->fov_x;\n\tscene.fov[1] = scene.fov_viewmodel[1] = ref->fov_y;\n\tVectorCopy(ref->viewaxis[0], scene.viewaxisorg[0]);\n\tVectorCopy(ref->viewaxis[1], scene.viewaxisorg[1]);\n\tVectorCopy(ref->viewaxis[2], scene.viewaxisorg[2]);\n\tVectorCopy(ref->vieworg, scene.viewaxisorg[3]);\n\tscene.time = ref->time/1000.0f;\n\n\tif (ref->rdflags & 1/*Q3RDF_NOWORLDMODEL*/)\n\t\tscene.flags |= RDF_NOWORLDMODEL;\n\n\tscenefuncs->RenderScene(&scene, sizeof(ref->areamask), ref->areamask);\n}\n\nstatic void *Little4Block(void *out, qbyte *in, size_t count)\n{\n\twhile (count-->0)\n\t{\n\t\t*(unsigned int*)out = (in[0]<<0)|(in[1]<<8)|(in[2]<<16)|(in[3]<<24);\n\t\tout = (qbyte*)out + 4;\n\t\tin+=4;\n\t}\n\treturn in;\n}\nvoid UI_RegisterFont(char *fontName, int pointSize, fontInfo_t *font)\n{\n\tunion \n\t{\n\t\tchar *c;\n\t\tint *i;\n\t\tfloat *f;\n\t} in;\n\tint i;\n\tchar name[MAX_QPATH];\n\tvoid *filedata;\n\tsize_t sz;\n\n\tsnprintf(name, sizeof(name), \"fonts/fontImage_%i.dat\",pointSize);\n\n\tin.c = filedata = fsfuncs->LoadFile(name, &sz);\n\tif (sz == sizeof(fontInfo_t))\n\t{\n\t\tfor(i=0; i<GLYPHS_PER_FONT; i++)\n\t\t{\n\t\t\tin.c = Little4Block(&font->glyphs[i].height, in.c, (int*)font->glyphs[i].shaderName-&font->glyphs[i].height);\n\t\t\tmemcpy(font->glyphs[i].shaderName, in.i, 32);\n\t\t\tin.c += 32;\n\t\t}\n\t\tin.c = Little4Block(&font->glyphScale, in.c, 1);\n\t\tmemcpy(font->name, in.i, sizeof(font->name));\n\n//\t\tCom_Memcpy(font, faceData, sizeof(fontInfo_t));\n\t\tQ_strncpyz(font->name, name, sizeof(font->name));\n\t\tfor (i = GLYPH_START; i < GLYPH_END; i++)\n\t\t\tfont->glyphs[i].glyph = drawfuncs->LoadImage(font->glyphs[i].shaderName);\n\t}\n\tplugfuncs->Free(filedata);\n}\n\nstatic struct\n{\n\tint shaderhandle;\n\tint x, y, w, h;\n\tqboolean loop;\n} uicinematics[16];\nint UI_Cin_Play(const char *name, int x, int y, int w, int h, unsigned int flags)\n{\n\tint idx;\n\tqhandle_t mediashader;\n\tcin_t *cin;\n\tfor (idx = 0; ; idx++)\n\t{\n\t\tif (idx == countof(uicinematics))\n\t\t\treturn -1;\t//out of handles\n\t\tif (uicinematics[idx].shaderhandle)\n\t\t\tcontinue;\t//slot in use\n\t\tbreak;\t//this slot is usable\n\t}\n\n\tmediashader = drawfuncs->LoadImageShader(name, va(\n\t\t\t\"{\\n\"\n\t\t\t\t\"program default2d\\n\"\n\t\t\t\t\"{\\n\"\n\t\t\t\t\t\"videomap \\\"video/%s\\\"\\n\"\n\t\t\t\t\t\"blendfunc gl_one gl_one_minus_src_alpha\\n\"\n\t\t\t\t\"}\\n\"\n\t\t\t\"}\\n\", name));\n\tif (!mediashader)\n\t\treturn -1;\t//wtf?\n\tcin = drawfuncs->media.GetCinematic(drawfuncs->ShaderFromId(mediashader));\n\tif (cin)\n\t\tdrawfuncs->media.SetState(cin, CINSTATE_PLAY);\n\telse\n\t\treturn -1;\t//FAIL!\n\n\tuicinematics[idx].x = x;\n\tuicinematics[idx].y = y;\n\tuicinematics[idx].w = w;\n\tuicinematics[idx].h = h;\n\tuicinematics[idx].loop = !!(flags&1);\n\tuicinematics[idx].shaderhandle = mediashader;\n\n\treturn idx;\n}\nint UI_Cin_Stop(int idx)\n{\n\tif (idx >= 0 && idx < countof(uicinematics))\n\t{\n\t\tdrawfuncs->UnloadImage(uicinematics[idx].shaderhandle);\n\t\tuicinematics[idx].shaderhandle = 0;\n\t}\n\treturn 0;\n}\nint UI_Cin_Run(int idx)\n{\n    enum {\n\t\tFMV_IDLE,\n\t\tFMV_PLAY,       // play\n\t\tFMV_EOF,        // all other conditions, i.e. stop/EOF/abort\n\t\tFMV_ID_BLT,\n\t\tFMV_ID_IDLE,\n\t\tFMV_LOOPED,\n\t\tFMV_ID_WAIT\n    };\n\tint ret = FMV_IDLE;\n\n\n\tif (idx >= 0 && idx < countof(uicinematics))\n\t{\n\t\tshader_t *shader = drawfuncs->ShaderFromId(uicinematics[idx].shaderhandle);\n\t\tcin_t *cin = drawfuncs->media.GetCinematic(shader);\n\t\tif (cin)\n\t\t{\n\t\t\tswitch((cinstates_t)drawfuncs->media.GetState(cin))\n\t\t\t{\n\t\t\tcase CINSTATE_INVALID:\tret = FMV_IDLE;\tbreak;\n\t\t\tcase CINSTATE_PLAY:\t\tret = FMV_PLAY; break;\n\t\t\tcase CINSTATE_LOOP:\t\tret = FMV_PLAY; break;\n\t\t\tcase CINSTATE_PAUSE:\tret = FMV_PLAY; break;\n\t\t\tcase CINSTATE_ENDED:\n\t\t\t\tdrawfuncs->media.SetState(cin, CINSTATE_FLUSHED);\n\t\t\t\tret = FMV_EOF;\n\t\t\t\tbreak;\n\t\t\tcase CINSTATE_FLUSHED:\n\t\t\t\t//FIXME: roq decoder has no reset method!\n\t\t\t\tdrawfuncs->media.Reset(cin);\n\t\t\t\tret = FMV_LOOPED;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\treturn ret;\n}\nint UI_Cin_Draw(int idx)\n{\n\tif (idx >= 0 && idx < countof(uicinematics))\n\t{\n\t\tunsigned int size[2];\n\t\tqhandle_t shader = uicinematics[idx].shaderhandle;\n\t\tfloat x = uicinematics[idx].x;\n\t\tfloat y = uicinematics[idx].y;\n\t\tfloat w = uicinematics[idx].w;\n\t\tfloat h = uicinematics[idx].h;\n\n\t\tdrawfuncs->GetVideoSize(NULL, size);\n\n\t\t//gah! q3 compat sucks!\n\t\tx *= size[0]/640.0;\n\t\tw *= size[0]/640.0;\n\t\ty *= size[1]/480.0;\n\t\th *= size[1]/480.0;\n\n\t\tdrawfuncs->Image(x, y, w, h, 0, 0, 1, 1, shader);\n\t}\n\treturn 0;\n}\nint UI_Cin_SetExtents(int idx, int x, int y, int w, int h)\n{\n\tif (idx >= 0 && idx < countof(uicinematics))\n\t{\n\t\tuicinematics[idx].x = x;\n\t\tuicinematics[idx].y = y;\n\t\tuicinematics[idx].w = w;\n\t\tuicinematics[idx].h = h;\n\t}\n\treturn 0;\n}\n\nstatic const char *Cvar_FixQ3Name (const char *var_name)\n{\n\tstruct {\n\t\tconst char *q3;\n\t\tconst char *fte;\n\t} cvarremaps[] =\n\t{\n\t\t{\"s_musicvolume\",\t\"bgmvolume\"},\n\t\t{\"r_gamma\",\t\t\t\"gamma\"},\n\t\t{\"s_sdlSpeed\",\t\t\"s_khz\"},\n\t\t{\"r_fullscreen\",\t\"vid_fullscreen\"},\n\t\t{\"r_picmip\",\t\t\"gl_picmip\"},\n\t\t{\"r_textureMode\",\t\"gl_texturemode\"},\n\t\t{\"r_lodBias\",\t\t\"d_lodbias\"},\n\t\t{\"r_colorbits\",\t\t\"vid_bpp\"},\n\t\t{\"r_dynamiclight\",\t\"r_dynamic\"},\n\t\t{\"r_finish\",\t\t\"gl_finish\"},\n\t\t{\"protocol\",\t\t\"com_protocolversion\"},\n//\t\t{\"r_glDriver\",\t\tNULL},\n//\t\t{\"r_depthbits\",\t\tNULL},\n//\t\t{\"r_stencilbits\",\tNULL},\n//\t\t{\"s_compression\",\tNULL},\n//\t\t{\"r_texturebits\",\tNULL},\n//\t\t{\"r_allowExtensions\",NULL},\n//\t\t{\"s_useOpenAL\",\t\tNULL},\n//\t\t{\"sv_running\",\t\tNULL},\n//\t\t{\"sv_killserver\",\tNULL},\n//\t\t{\"color1\",\t\t\tNULL},\n//\t\t{\"in_joystick\",\t\tNULL},\n//\t\t{\"joy_threshold\",\tNULL},\n//\t\t{\"cl_freelook\",\t\tNULL},\n//\t\t{\"color1\",\t\t\tNULL},\n//\t\t{\"r_availableModes\",NULL},\n//\t\t{\"r_mode\",\t\t\tNULL},\n\t};\n\tsize_t i;\n\tfor (i = 0; i < countof(cvarremaps); i++)\n\t{\n\t\tif (!strcmp(cvarremaps[i].q3, var_name))\n\t\t\treturn cvarremaps[i].fte;\n\t}\n\treturn var_name;\n}\n\nstatic void UI_SimulateTextEntry(void *cb, const char *utf8)\n{\n\tconst char *line = utf8;\n\tunsigned int unicode;\n\tint err;\n\twhile(*line)\n\t{\n\t\tunicode = inputfuncs->utf8_decode(&err, line, &line);\n\t\tif (uivm)\n\t\t\tvmfuncs->Call(uivm, UI_KEY_EVENT, unicode|1024, true);\n\t}\n}\n\n#define VALIDATEPOINTER(o,l) if ((quintptr_t)o + l >= mask || VM_POINTER(o) < offset) plugfuncs->EndGame(\"Call to ui trap %i passes invalid pointer\\n\", (int)fn);\t//out of bounds.\n\nstatic qintptr_t UI_SystemCalls(void *offset, quintptr_t mask, qintptr_t fn, const qintptr_t *arg)\n{\n\tstatic int overstrikemode;\n\tint ret=0;\n\n\t//Remember to range check pointers.\n\t//The QVM must not be allowed to write to anything outside it's memory.\n\t//This includes getting the exe to copy it for it.\n\n\t//don't bother with reading, as this isn't a virus risk.\n\t//could be a cheat risk, but hey.\n\n\t//make sure that any called functions are also range checked.\n\t//like reading from files copies names into alternate buffers, allowing stack screwups.\n\n\tswitch((uiImport_t)fn)\n\t{\n\tcase UI_CVAR_CREATE:\t\t\t\t\t\tCon_Printf(\"Q3UI: Not implemented system trap: %s\\n\", \"UI_CVAR_CREATE\"); return 0;\n\tcase UI_CVAR_INFOSTRINGBUFFER:\t\t\t\tCon_Printf(\"Q3UI: Not implemented system trap: %s\\n\", \"UI_CVAR_INFOSTRINGBUFFER\"); return 0;\n\tcase UI_R_ADDPOLYTOSCENE:\t\t\t\t\tCon_Printf(\"Q3UI: Not implemented system trap: %s\\n\", \"UI_R_ADDPOLYTOSCENE\"); return 0;\n\tcase UI_R_REMAP_SHADER:\t\t\t\t\t\tCon_Printf(\"Q3UI: Not implemented system trap: %s\\n\", \"UI_R_REMAP_SHADER\"); return 0;\n\tcase UI_UPDATESCREEN:\t\t\t\t\t\tCon_Printf(\"Q3UI: Not implemented system trap: %s\\n\", \"UI_UPDATESCREEN\"); return 0;\n\tcase UI_CM_LOADMODEL:\t\t\t\t\t\tCon_Printf(\"Q3UI: Not implemented system trap: %s\\n\", \"UI_CM_LOADMODEL\"); return 0;\n\tcase UI_ERROR:\n\t\tCon_Printf(\"%s\", (char*)VM_POINTER(arg[0]));\n\t\tUI_Stop();\n\t\tplugfuncs->EndGame(\"UI error\");\n\t\tbreak;\n\tcase UI_PRINT:\n\t\tCon_Printf(\"%s\", (char*)VM_POINTER(arg[0]));\n\t\tbreak;\n\n\tcase UI_MILLISECONDS:\n\t\tVM_LONG(ret) = plugfuncs->GetMilliseconds();\n\t\tbreak;\n\n\tcase UI_ARGC:\n\t\tVM_LONG(ret) = cmdfuncs->Argc();\n\t\tbreak;\n\tcase UI_ARGV:\n//\t\tVALIDATEPOINTER(arg[1], arg[2]);\n\t\tcmdfuncs->Argv(VM_LONG(arg[0]), VM_POINTER(arg[1]), VM_LONG(arg[2]));\n\t\tbreak;\n/*\tcase UI_ARGS:\n\t\tVALIDATEPOINTER(arg[0], arg[1]);\n\t\tQ_strncpyz(VM_POINTER(arg[0]), Cmd_Args(), VM_LONG(arg[1]));\n\t\tbreak;\n*/\n\n\tcase UI_CVAR_SET:\n\t\tcvarfuncs->SetString(Cvar_FixQ3Name(VM_POINTER(arg[0])), VM_POINTER(arg[1]));\n\t\tbreak;\n\tcase UI_CVAR_VARIABLEVALUE:\n\t\tVM_FLOAT(ret) = cvarfuncs->GetFloat(Cvar_FixQ3Name(VM_POINTER(arg[0])));\n\t\tbreak;\n\tcase UI_CVAR_VARIABLESTRINGBUFFER:\n\t\tif (arg[1] + arg[2] >= mask || VM_POINTER(arg[1]) < offset)\n\t\t\tVM_LONG(ret) = -2;\t//out of bounds.\n\t\tcvarfuncs->GetString(Cvar_FixQ3Name(VM_POINTER(arg[0])), VM_POINTER(arg[1]), VM_LONG(arg[2]));\n\t\tbreak;\n\n\tcase UI_CVAR_SETVALUE:\n\t\tcvarfuncs->SetFloat(Cvar_FixQ3Name(VM_POINTER(arg[0])), VM_FLOAT(arg[1]));\n\t\tbreak;\n\n\tcase UI_CVAR_RESET:\t//cvar reset\n\t\tcvarfuncs->SetString(Cvar_FixQ3Name(VM_POINTER(arg[0])), NULL);\n\t\tbreak;\n\n\tcase UI_CMD_EXECUTETEXT:\n\t\t{\n\t\t\tchar *cmdtext = VM_POINTER(arg[1]);\n#ifdef CL_MASTER\n\t\t\tif (!strncmp(cmdtext, \"ping \", 5))\n\t\t\t{\n\t\t\t\tchar token[1024];\n\t\t\t\tint i;\n\t\t\t\tfor (i = 0; i < MAX_PINGREQUESTS; i++)\n\t\t\t\t\tif (ui_pings[i].adr.type == NA_INVALID && ui_pings[i].adr.prot == NP_INVALID)\n\t\t\t\t\t{\n\t\t\t\t\t\tconst char *p = NULL;\n\t\t\t\t\t\tcmdfuncs->ParseToken(cmdtext + 5, token, sizeof(token), NULL);\n\t\t\t\t\t\tui_pings[i].startms = plugfuncs->GetMilliseconds();\n\t\t\t\t\t\tQ_strncpyz(ui_pings[i].adrstring, token, sizeof(ui_pings[i].adrstring));\n\t\t\t\t\t\tif (masterfuncs->StringToAdr(ui_pings[i].adrstring, 0, &ui_pings[i].adr, 1, &p))\n\t\t\t\t\t\t{\n#ifdef HAVE_PACKET\n\t\t\t\t\t\t\tserverinfo_t *info = masterfuncs->InfoForServer(&ui_pings[i].adr, p);\n\t\t\t\t\t\t\tif (info)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tinfo->special |= SS_KEEPINFO;\n\t\t\t\t\t\t\t\tinfo->sends++;\n\t\t\t\t\t\t\t\tmasterfuncs->QueryServer(info);\n\t\t\t\t\t\t\t}\n#endif\n\t\t\t\t\t\t}\n\t\t\t\t\t\tui_pings[i].broker = p;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t}\n\t\t\telse\n#endif\n\t\t\t\tcmdfuncs->AddText(cmdtext, false);\n\t\t}\n\t\tbreak;\n\n\tcase UI_FS_FOPENFILE: //fopen\n\t\tif ((int)arg[1] + 4 >= mask || VM_POINTER(arg[1]) < offset)\n\t\t\tbreak;\t//out of bounds.\n\t\tVM_LONG(ret) = VM_fopen(VM_POINTER(arg[0]), VM_POINTER(arg[1]), VM_LONG(arg[2]), 0);\n\t\tbreak;\n\n\tcase UI_FS_READ:\t//fread\n\t\tif ((int)arg[0] + VM_LONG(arg[1]) >= mask || VM_POINTER(arg[0]) < offset)\n\t\t\tbreak;\t//out of bounds.\n\n\t\tVM_LONG(ret) = VM_FRead(VM_POINTER(arg[0]), VM_LONG(arg[1]), VM_LONG(arg[2]), 0);\n\t\tbreak;\n\tcase UI_FS_WRITE:\t//fwrite\n\t\tbreak;\n\tcase UI_FS_SEEK:\n\t\treturn VM_FSeek(arg[0], arg[1], arg[2], 0);\n\tcase UI_FS_FCLOSEFILE:\t//fclose\n\t\tVM_fclose(VM_LONG(arg[0]), 0);\n\t\tbreak;\n\n\tcase UI_FS_GETFILELIST:\t//fs listing\n\t\tif ((int)arg[2] + arg[3] >= mask || VM_POINTER(arg[2]) < offset)\n\t\t\tbreak;\t//out of bounds.\n\t\treturn VM_GetFileList(VM_POINTER(arg[0]), VM_POINTER(arg[1]), VM_POINTER(arg[2]), VM_LONG(arg[3]));\n\n\tcase UI_R_REGISTERMODEL:\t//precache model\n\t\t{\n\t\t\tchar *name = VM_POINTER(arg[0]);\n\t\t\tmodel_t *mod = scenefuncs->LoadModel(name, MLV_SILENTSYNC);\n\t\t\tif (!mod || mod->loadstate != MLS_LOADED || mod->type == mod_dummy)\n\t\t\t\tVM_LONG(ret) = 0;\n\t\t\telse\n\t\t\t\tVM_LONG(ret) = scenefuncs->ModelToId(mod);\n\t\t}\n\t\tbreak;\n\tcase UI_R_MODELBOUNDS:\n\t\t{\n\t\t\tVALIDATEPOINTER(arg[1], sizeof(vec3_t));\n\t\t\tVALIDATEPOINTER(arg[2], sizeof(vec3_t));\n\t\t\t{\n\t\t\t\tmodel_t *mod = scenefuncs->ModelFromId(arg[0]);\n\t\t\t\tif (mod)\n\t\t\t\t{\n\t\t\t\t\tVectorCopy(mod->mins, ((float*)VM_POINTER(arg[1])));\n\t\t\t\t\tVectorCopy(mod->maxs, ((float*)VM_POINTER(arg[2])));\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tVectorClear(((float*)VM_POINTER(arg[1])));\n\t\t\t\t\tVectorClear(((float*)VM_POINTER(arg[2])));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tbreak;\n\n\tcase UI_R_REGISTERSKIN:\n\t\tVM_LONG(ret) = scenefuncs->RegisterSkinFile(VM_POINTER(arg[0]));\n\t\tbreak;\n\n\tcase UI_R_REGISTERFONT:\t//register font\n\t\tUI_RegisterFont(VM_POINTER(arg[0]), arg[1], VM_POINTER(arg[2]));\n\t\tbreak;\n\tcase UI_R_REGISTERSHADERNOMIP:\n\t\tif (!*(char*)VM_POINTER(arg[0]))\n\t\t\tVM_LONG(ret) = 0;\n\t\telse\n\t\t\tVM_LONG(ret) = drawfuncs->LoadImage(VM_POINTER(arg[0]));\n\t\tif (!drawfuncs->ImageSize(VM_LONG(ret), NULL, NULL))\n\t\t\tVM_LONG(ret) = 0;\t//not found...\n\t\tbreak;\n\n\tcase UI_R_CLEARSCENE:\t//clear scene\n\t\tscenefuncs->ClearScene();\n\t\tbreak;\n\tcase UI_R_ADDREFENTITYTOSCENE:\t//add ent to scene\n\t\tVQ3_AddEntity(VM_POINTER(arg[0]));\n\t\tbreak;\n\tcase UI_R_ADDLIGHTTOSCENE:\t//add light to scene.\n\t\tbreak;\n\tcase UI_R_RENDERSCENE:\t//render scene\n\t\tVQ3_RenderView(VM_POINTER(arg[0]));\n\t\tbreak;\n\n\tcase UI_R_SETCOLOR:\t//setcolour float*\n\t\t{\n\t\t\tfloat *fl =VM_POINTER(arg[0]);\n\t\t\tif (!fl)\n\t\t\t\tdrawfuncs->Colour4f(1, 1, 1, 1);\n\t\t\telse\n\t\t\t\tdrawfuncs->Colour4f(fl[0], fl[1], fl[2], fl[3]);\n\t\t}\n\t\tbreak;\n\n\tcase UI_R_DRAWSTRETCHPIC:\n\t\tdrawfuncs->Image(VM_FLOAT(arg[0]), VM_FLOAT(arg[1]), VM_FLOAT(arg[2]), VM_FLOAT(arg[3]), VM_FLOAT(arg[4]), VM_FLOAT(arg[5]), VM_FLOAT(arg[6]), VM_FLOAT(arg[7]), VM_LONG(arg[8]));\n\t\tbreak;\n\n\tcase UI_CM_LERPTAG:\t//Lerp tag...\n\t\tVALIDATEPOINTER(arg[0], sizeof(float)*12);\n\t\tVM_LONG(ret) = VM_LerpTag(VM_POINTER(arg[0]), scenefuncs->ModelFromId(arg[1]), VM_LONG(arg[2]), VM_LONG(arg[3]), VM_FLOAT(arg[4]), VM_POINTER(arg[5]));\n\t\tbreak;\n\n\tcase UI_S_REGISTERSOUND:\n\t\t{\n\t\t\tsfx_t *sfx;\n\t\t\tsfx = audiofuncs->PrecacheSound(va(\"%s\", (char*)VM_POINTER(arg[0])));\n\t\t\tif (sfx)\n\t\t\t\tVM_LONG(ret) = VM_TOSTRCACHE(arg[0]);\t//return handle is the parameter they just gave\n\t\t\telse\n\t\t\t\tVM_LONG(ret) = -1;\n\t\t}\n\t\tbreak;\n\tcase UI_S_STARTLOCALSOUND:\n\t\tif (VM_LONG(arg[0]) != -1 && arg[0])\n\t\t\taudiofuncs->LocalSound(VM_FROMSTRCACHE(arg[0]), CHAN_AUTO, 1.0);\t//now we can fix up the sound name\n\t\tbreak;\n\n\tcase UI_S_STARTBACKGROUNDTRACK:\n\t\taudiofuncs->ChangeMusicTrack(VM_POINTER(arg[0]), VM_POINTER(arg[1]));\n\t\tbreak;\n\tcase UI_S_STOPBACKGROUNDTRACK:\n\t\taudiofuncs->ChangeMusicTrack(NULL, NULL);\n\t\tbreak;\n\n\t//q3 shares insert mode between its ui and console. whereas fte doesn't support it (FIXME: add to Key_EntryInsert).\n\tcase UI_KEY_SETOVERSTRIKEMODE:\n\t\toverstrikemode = arg[0];\n\t\treturn 0;\n\tcase UI_KEY_GETOVERSTRIKEMODE:\n\t\treturn overstrikemode;\n\n\tcase UI_GETCLIPBOARDDATA:\n\t\tif (VM_OOB(arg[0], VM_LONG(arg[1])))\n\t\t\tbreak;\t//out of bounds.\n\t\t//our clipboard doesn't allow us to simply query without blocking (would result in stalls on x11/wayland)\n\t\t//so just return nothing - we can send the text as if it were regular text entry for a similar result.\n\t\tQ_strncpyz(VM_POINTER(arg[0]), \"\", VM_LONG(arg[1]));\n\n\t\t//but do we really want to let mods read the system clipboard? I suppose it SHOULD be okay if the UI was manually installed by the user.\n\t\t//side note: q3's text entry logic is kinda flawed.\n\t\tinputfuncs->ClipboardGet(CBT_CLIPBOARD, UI_SimulateTextEntry, NULL);\n\t\tbreak;\n\n\tcase UI_KEY_KEYNUMTOSTRINGBUF:\n\t\tif (VM_LONG(arg[0]) < 0 || VM_LONG(arg[0]) >= K_MAX || (int)arg[1] + VM_LONG(arg[2]) >= mask || VM_POINTER(arg[1]) < offset || VM_LONG(arg[2]) < 1)\n\t\t\tbreak;\t//out of bounds.\n\n\t\tQ_strncpyz(VM_POINTER(arg[1]), inputfuncs->GetKeyName(VM_LONG(arg[0]), 0), VM_LONG(arg[2]));\n\t\tbreak;\n\n\tcase UI_KEY_GETBINDINGBUF:\n\t\tif (VM_LONG(arg[0]) < 0 || VM_LONG(arg[0]) >= K_MAX || (int)arg[1] + VM_LONG(arg[2]) >= mask || VM_POINTER(arg[1]) < offset || VM_LONG(arg[2]) < 1)\n\t\t\tbreak;\t//out of bounds.\n\n\t\t{\n\t\t\tconst char *binding = inputfuncs->GetKeyBind(0, VM_LONG(arg[0]), 0);\n\t\t\tif (binding)\n\t\t\t\tQ_strncpyz(VM_POINTER(arg[1]), binding, VM_LONG(arg[2]));\n\t\t\telse\n\t\t\t\t*(char *)VM_POINTER(arg[1]) = '\\0';\n\t\t}\n\t\tbreak;\n\n\tcase UI_KEY_SETBINDING:\n\t\tinputfuncs->SetKeyBind(0, VM_LONG(arg[0]), 0, VM_POINTER(arg[1]));\n\t\tbreak;\n\n\tcase UI_KEY_ISDOWN:\n\t\tVM_LONG(ret) = inputfuncs->IsKeyDown(VM_LONG(arg[0]));\n\t\tbreak;\n\n\tcase UI_KEY_CLEARSTATES:\n\t\tinputfuncs->ClearKeyStates();\n\t\tbreak;\n\tcase UI_KEY_GETCATCHER:\n\t\tif (inputfuncs->GetKeyDest() & kdm_console)\n\t\t\tVM_LONG(ret) = keycatcher | 1;\n\t\telse\n\t\t\tVM_LONG(ret) = keycatcher;\n\t\tbreak;\n\tcase UI_KEY_SETCATCHER:\n\t\tQ3_SetKeyCatcher(VM_LONG(arg[0]));\n\t\tbreak;\n\n\tcase UI_GETGLCONFIG:\t//get glconfig\n\t\t{\n\t\t\tq3glconfig_t *cfg;\n\t\t\tfloat vsize[2] = {0,0};\n\t\t\tif ((int)arg[0] + sizeof(q3glconfig_t) >= mask || VM_POINTER(arg[0]) < offset)\n\t\t\t\tbreak;\t//out of bounds.\n\t\t\tdrawfuncs->GetVideoSize(vsize, NULL);\n\t\t\tcfg = VM_POINTER(arg[0]);\n\n\t\t\t//do any needed work\n\t\t\tmemset(cfg, 0, sizeof(*cfg));\n\n\t\t\tQ_strncpyz(cfg->renderer_string, \"\", sizeof(cfg->renderer_string));\n\t\t\tQ_strncpyz(cfg->vendor_string, \"\", sizeof(cfg->vendor_string));\n\t\t\tQ_strncpyz(cfg->version_string, \"\", sizeof(cfg->version_string));\n\t\t\tQ_strncpyz(cfg->extensions_string, \"\", sizeof(cfg->extensions_string));\n\n\t\t\tcfg->colorBits = 32;\n\t\t\tcfg->depthBits = 24;\n\t\t\tcfg->stencilBits = 8;//sh_config.stencilbits;\n\t\t\tcfg->textureCompression = true;//!!sh_config.texfmt[PTI_BC1_RGBA];\n\t\t\tcfg->textureEnvAddAvailable = true;//sh_config.env_add;\n\n\t\t\t//these are the only three that really matter.\n\t\t\tcfg->vidWidth = vsize[0];\n\t\t\tcfg->vidHeight = vsize[1];\n\t\t\tcfg->windowAspect = vsize[0]/vsize[1];;\n#if 0\n\t\t\tchar *cfg;\n\t\t\tif ((int)arg[0] + 11332/*sizeof(glconfig_t)*/ >= mask || VM_POINTER(arg[0]) < offset)\n\t\t\t\tbreak;\t//out of bounds.\n\t\t\tcfg = VM_POINTER(arg[0]);\n\t\t\n\n\t\t\t//do any needed work\n\t\t\tmemset(cfg, 0, 11304);\n\t\t\t*(int *)(cfg+11304) = vsize[0];\n\t\t\t*(int *)(cfg+11308) = vid.height;\n\t\t\t*(float *)(cfg+11312) = (float)vsize[0]/vid.height;\n\t\t\tmemset(cfg+11316, 0, 11332-11316);\n#endif\n\t\t}\n\t\tbreak;\n\n\tcase UI_GETCLIENTSTATE:\t//get client state\n\t\t//fixme: we need to fill in a structure.\n//\t\tCon_Printf(\"ui_getclientstate\\n\");\n\t\tVALIDATEPOINTER(arg[0], sizeof(uiClientState_t));\n\t\t{\n\t\t\tuiClientState_t *state = VM_POINTER(arg[0]);\n\t\t\tstate->connectPacketCount = 0;//clc.connectPacketCount;\n\n\t\t\tcvarfuncs->GetString(\"cl_servername\", state->servername, sizeof(state->servername));\t//server we're connected to\n\t\t\tswitch(ccs.state)\n\t\t\t{\n\t\t\tcase ca_disconnected:\n\t\t\t\tif (*state->servername)\n\t\t\t\t\tstate->connState = Q3CA_CONNECTING;\n\t\t\t\telse\n\t\t\t\t\tstate->connState = Q3CA_DISCONNECTED;\n\t\t\t\tbreak;\n\t\t\tcase ca_demostart:\n\t\t\t\tstate->connState = Q3CA_CONNECTING;\n\t\t\t\tbreak;\n\t\t\tcase ca_connected:\n\t\t\t\tstate->connState = Q3CA_CONNECTED;\n\t\t\t\tbreak;\n\t\t\tcase ca_onserver:\n\t\t\t\tstate->connState = Q3CA_PRIMED;\n\t\t\t\tbreak;\n\t\t\tcase ca_active:\n\t\t\t\tstate->connState = Q3CA_ACTIVE;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tQ_strncpyz(state->updateInfoString, \"FTE!\", sizeof(state->updateInfoString));\t//warning/motd message from update server\n\t\t\tcvarfuncs->GetString(\"com_errorMessage\", state->messageString, sizeof(state->messageString));\t//error message from game server\n\t\t\tstate->clientNum = ccs.playernum;\n\t\t}\n\t\tbreak;\n\n\tcase UI_GETCONFIGSTRING:\n\t\tif (arg[1] + VM_LONG(arg[2]) >= mask || VM_POINTER(arg[1]) < offset || VM_LONG(arg[2]) < 1)\n\t\t{\n\t\t\tVM_LONG(ret) = 0;\n\t\t\tbreak;\t//out of bounds.\n\t\t}\n#ifdef VM_CG\n\t\tQ_strncpyz(VM_POINTER(arg[1]), CG_GetConfigString(VM_LONG(arg[0])), VM_LONG(arg[2]));\n#endif\n\t\tbreak;\n\n#ifdef CL_MASTER\n\tcase UI_LAN_GETPINGQUEUECOUNT:\t//these four are master server polling.\n\t\t{\n\t\t\tint i;\n\t\t\tfor (i = 0; i < MAX_PINGREQUESTS; i++)\n\t\t\t\tif (ui_pings[i].adr.type != NA_INVALID || ui_pings[i].adr.prot != NP_INVALID)\n\t\t\t\t\tVM_LONG(ret)++;\n\t\t}\n\t\tbreak;\n\tcase UI_LAN_CLEARPING:\t//clear ping\n\t\t//void (int pingnum)\n\t\tif (VM_LONG(arg[0])>= 0 && VM_LONG(arg[0]) < MAX_PINGREQUESTS)\n\t\t\tui_pings[VM_LONG(arg[0])].adr.type = NA_INVALID, ui_pings[VM_LONG(arg[0])].adr.prot = NP_INVALID;\n\t\tbreak;\n\tcase UI_LAN_GETPING:\n\t\t//void (int pingnum, char *buffer, int buflen, int *ping)\n\t\tif ((int)arg[1] + VM_LONG(arg[2]) >= mask || VM_POINTER(arg[1]) < offset)\n\t\t\tbreak;\t//out of bounds.\n\t\tif ((int)arg[3] + sizeof(int) >= mask || VM_POINTER(arg[3]) < offset)\n\t\t\tbreak;\t//out of bounds.\n\n\t\tmasterfuncs->CheckPollSockets();\n\t\tif (VM_LONG(arg[0])>= 0 && VM_LONG(arg[0]) < MAX_PINGREQUESTS)\n\t\t{\n\t\t\tint i = VM_LONG(arg[0]);\n\t\t\tchar *buf = VM_POINTER(arg[1]);\n\t\t\tsize_t bufsize = VM_LONG(arg[2]);\n\t\t\tint *ping = VM_POINTER(arg[3]);\n\t\t\tserverinfo_t *info;\n\t\t\tif (ui_pings[i].adr.type != NA_INVALID || ui_pings[i].adr.prot != NP_INVALID)\n\t\t\t\tinfo = masterfuncs->InfoForServer(&ui_pings[i].adr, ui_pings[i].broker);\n\t\t\telse\n\t\t\t\tinfo = NULL;\n\t\t\tQ_strncpyz(buf, ui_pings[i].adrstring, bufsize);\n\n\t\t\tif (info && /*(info->status & SRVSTATUS_ALIVE) &&*/ info->moreinfo)\n\t\t\t{\n\t\t\t\tVM_LONG(ret) = true;\n\t\t\t\t*ping = (info->ping == PING_UNKNOWN)?1:info->ping;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\ti = plugfuncs->GetMilliseconds()-ui_pings[i].startms;\n\t\t\tif (i < 999)\n\t\t\t\ti = 0;\t//don't time out yet.\n\t\t\t*ping = i;\n\t\t}\n\t\tbreak;\n\tcase UI_LAN_GETPINGINFO:\n\t\t//void (int pingnum, char *buffer, int buflen)\n\t\tif ((int)arg[1] + VM_LONG(arg[2]) >= mask || VM_POINTER(arg[1]) < offset)\n\t\t\tbreak;\t//out of bounds.\n\t\tif ((int)arg[3] + sizeof(int) >= mask || VM_POINTER(arg[3]) < offset)\n\t\t\tbreak;\t//out of bounds.\n\n\t\tmasterfuncs->CheckPollSockets();\n\t\tif (VM_LONG(arg[0])>= 0 && VM_LONG(arg[0]) < MAX_PINGREQUESTS)\n\t\t{\n\t\t\tint i = VM_LONG(arg[0]);\n\t\t\tchar *buf = VM_POINTER(arg[1]);\n\t\t\tsize_t bufsize = VM_LONG(arg[2]);\n\t\t\tchar *adr;\n\t\t\tserverinfo_t *info = masterfuncs->InfoForServer(&ui_pings[i].adr, ui_pings[i].broker);\n\n\t\t\tif (info && /*(info->status & SRVSTATUS_ALIVE) &&*/ info->moreinfo)\n\t\t\t{\n\t\t\t\tadr = info->moreinfo->info;\n\t\t\t\tif (!adr)\n\t\t\t\t\tadr = \"\";\n\t\t\t\tif (strlen(adr) < bufsize)\n\t\t\t\t{\n\t\t\t\t\tstrcpy(buf, adr);\n\t\t\t\t\tworldfuncs->SetInfoKey(buf, \"mapname\", info->map, bufsize);\n\t\t\t\t\tworldfuncs->SetInfoKey(buf, \"hostname\", info->name, bufsize);\n\t\t\t\t\tworldfuncs->SetInfoKey(buf, \"g_humanplayers\", va(\"%i\", info->numhumans), bufsize);\n\t\t\t\t\tworldfuncs->SetInfoKey(buf, \"sv_maxclients\", va(\"%i\", info->maxplayers), bufsize);\n\t\t\t\t\tworldfuncs->SetInfoKey(buf, \"clients\", va(\"%i\", info->players), bufsize);\n\t\t\t\t\tif (info->adr.type == NA_IPV6 && info->adr.prot == NP_DGRAM)\n\t\t\t\t\t\tworldfuncs->SetInfoKey(buf, \"nettype\", \"2\", bufsize);\n\t\t\t\t\telse if (info->adr.type == NA_IP && info->adr.prot == NP_DGRAM)\n\t\t\t\t\t\tworldfuncs->SetInfoKey(buf, \"nettype\", \"1\", bufsize);\n\t\t\t\t\telse\n\t\t\t\t\t\tworldfuncs->SetInfoKey(buf, \"nettype\", \"\", bufsize);\n\t\t\t\t\tVM_LONG(ret) = true;\n\t\t\t\t}\t\n\t\t\t}\n\t\t\telse\n\t\t\t\tstrcpy(buf, \"\");\n\t\t}\n\t\tbreak;\n\n\tcase UI_LAN_UPDATEVISIBLEPINGS:\n\t\treturn masterfuncs->QueryServers();\t//return true while we're still going.\n\tcase UI_LAN_RESETPINGS:\n\t\treturn 0;\n\tcase UI_LAN_ADDSERVER:\n\t\treturn 0;\n\tcase UI_LAN_REMOVESERVER:\n\t\treturn 0;\n\tcase UI_LAN_SERVERSTATUS:\n\t\t//*address, *status, statuslen\n\t\treturn 0;\n\tcase UI_LAN_COMPARESERVERS:\n\t\t{\n\t\t\thostcachekey_t q3tofte[] = {SLKEY_NAME, SLKEY_MAP, SLKEY_NUMHUMANS, SLKEY_GAMEDIR, SLKEY_PING, 0};\n\t\t\tqboolean keyisstring[] = {true, true, false, true, false, false};\n\t\t\t//int source = VM_LONG(arg[0]);\n\t\t\tint key = bound(0, VM_LONG(arg[1]), countof(q3tofte));\n\t\t\tint sortdir = VM_LONG(arg[2]);\n\t\t\tserverinfo_t *s1 = masterfuncs->InfoForNum(VM_LONG(arg[3]));\n\t\t\tserverinfo_t *s2 = masterfuncs->InfoForNum(VM_LONG(arg[4]));\n\t\t\tif (keyisstring[key])\n\t\t\t\tret = Q_strcasecmp(masterfuncs->ReadKeyString(s2, q3tofte[key]), masterfuncs->ReadKeyString(s1, q3tofte[key]));\n\t\t\telse\n\t\t\t{\n\t\t\t\tret = masterfuncs->ReadKeyFloat(s2, q3tofte[key]) - masterfuncs->ReadKeyFloat(s1, q3tofte[key]);\n\t\t\t\tif (ret < 0)\n\t\t\t\t\tret = -1;\n\t\t\t\telse if (ret > 0)\n\t\t\t\t\tret = 1;\n\t\t\t}\n\t\t\tif (sortdir)\n\t\t\t\tret = -ret;\n\t\t}\n\t\treturn ret;\n\n\tcase UI_LAN_GETSERVERCOUNT:\t//LAN Get server count\n\t\t//int (int source)\n\t\tmasterfuncs->CheckPollSockets();\n\t\tVM_LONG(ret) = masterfuncs->TotalCount();\n\t\tbreak;\n\tcase UI_LAN_GETSERVERADDRESSSTRING:\t//LAN get server address\n\t\t//void (int source, int svnum, char *buffer, int buflen)\n\t\tif ((int)arg[2] + VM_LONG(arg[3]) >= mask || VM_POINTER(arg[2]) < offset)\n\t\t\tbreak;\t//out of bounds.\n\t\t{\n\t\t\tchar *buf = VM_POINTER(arg[2]);\n\t\t\tchar *adr;\n\t\t\tchar adrbuf[MAX_ADR_SIZE];\n\t\t\tserverinfo_t *info = masterfuncs->InfoForNum(VM_LONG(arg[1]));\n\t\t\tstrcpy(buf, \"\");\n\t\t\tif (info)\n\t\t\t{\n\t\t\t\tadr = masterfuncs->ServerToString(adrbuf, sizeof(adrbuf), info);\n\t\t\t\tif (strlen(adr) < VM_LONG(arg[3]))\n\t\t\t\t{\n\t\t\t\t\tstrcpy(buf, adr);\n\t\t\t\t\tVM_LONG(ret) = true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tbreak;\n\tcase UI_LAN_LOADCACHEDSERVERS:\n\t\tbreak;\n\tcase UI_LAN_SAVECACHEDSERVERS:\n\t\tbreak;\n\tcase UI_LAN_GETSERVERPING:\n\t\treturn 50;\n\tcase UI_LAN_GETSERVERINFO:\n\t\tif (VM_OOB(arg[2], arg[3]) || !arg[3])\n\t\t\tbreak;\t//out of bounds.\n\t\t{\n\t\t\t//int source = VM_LONG(arg[0]);\n\t\t\tint servernum = VM_LONG(arg[1]);\n\t\t\tchar *out = VM_POINTER(arg[2]);\n\t\t\tint maxsize = VM_LONG(arg[3]);\n\t\t\tchar adr[MAX_ADR_SIZE];\n\t\t\tserverinfo_t *info = masterfuncs->InfoForNum(servernum);\n\t\t\t*out = 0;\n\t\t\tif (info)\n\t\t\t{\n\t\t\t\tworldfuncs->SetInfoKey(out, \"hostname\", info->name, maxsize);\n\t\t\t\tworldfuncs->SetInfoKey(out, \"mapname\", info->map, maxsize);\n\t\t\t\tworldfuncs->SetInfoKey(out, \"clients\", va(\"%i\", info->players), maxsize);\n\t\t\t\tworldfuncs->SetInfoKey(out, \"sv_maxclients\", va(\"%i\", info->maxplayers), maxsize);\n\t\t\t\tworldfuncs->SetInfoKey(out, \"ping\", va(\"%i\", info->ping), maxsize);\n//\t\t\t\tworldfuncs->SetInfoKey(out, \"minping\", info->map, maxsize);\n//\t\t\t\tworldfuncs->SetInfoKey(out, \"maxping\", info->map, maxsize);\n//\t\t\t\tworldfuncs->SetInfoKey(out, \"game\", info->map, maxsize);\n//\t\t\t\tworldfuncs->SetInfoKey(out, \"gametype\", info->map, maxsize);\n//\t\t\t\tworldfuncs->SetInfoKey(out, \"nettype\", info->map, maxsize);\n\t\t\t\tworldfuncs->SetInfoKey(out, \"addr\", masterfuncs->AdrToString(adr, sizeof(adr), &info->adr), maxsize);\n//\t\t\t\tworldfuncs->SetInfoKey(out, \"punkbuster\", info->map, maxsize);\n//\t\t\t\tworldfuncs->SetInfoKey(out, \"g_needpass\", info->map, maxsize);\n//\t\t\t\tworldfuncs->SetInfoKey(out, \"g_humanplayers\", info->map, maxsize);\n\t\t\t}\n\t\t}\n\t\tbreak;\n\tcase UI_LAN_MARKSERVERVISIBLE:\n\t\t/*not implemented*/\n\t\treturn 0;\n\tcase UI_LAN_SERVERISVISIBLE:\n\t\treturn 1;\n#endif\n\n\tcase UI_CVAR_REGISTER:\n\t\tif (VM_OOB(arg[0], sizeof(q3vmcvar_t)))\n\t\t\tbreak;\t//out of bounds.\n\t\treturn VMQ3_Cvar_Register(VM_POINTER(arg[0]), VM_POINTER(arg[1]), VM_POINTER(arg[2]), VM_LONG(arg[3]));\n\n\tcase UI_CVAR_UPDATE:\n\t\tif (VM_OOB(arg[0], sizeof(q3vmcvar_t)))\n\t\t\tbreak;\t//out of bounds.\n\t\treturn VMQ3_Cvar_Update(VM_POINTER(arg[0]));\n\n\tcase UI_MEMORY_REMAINING:\n\t\tVM_LONG(ret) = 1024*1024*8;//Hunk_LowMemAvailable();\n\t\tbreak;\n\n\tcase UI_GET_CDKEY:\t//get cd key\n\t\t{\n\t\t\tcvar_t *cvar = cvarfuncs->GetNVFDG(\"cl_cdkey\", \"\", CVAR_ARCHIVE, \"Quake3 auth\", \"Q3 Compat\");\n\t\t\tchar *keydest = VM_POINTER(arg[0]);\n\t\t\tif (!cvar || (int)arg[0] + VM_LONG(arg[1]) >= mask || VM_POINTER(arg[0]) < offset)\n\t\t\t\tbreak;\t//out of bounds.\n\t\t\tQ_strncpyz(keydest, cvar->string, VM_LONG(arg[1]));\n\t\t}\n\t\tbreak;\n\tcase UI_SET_CDKEY:\t//set cd key\n\t\t{\n\t\t\tchar *keysrc = VM_POINTER(arg[0]);\n\t\t\tif ((int)arg[0] + strlen(keysrc) >= mask || VM_POINTER(arg[0]) < offset)\n\t\t\t\tbreak;\t//out of bounds.\n\t\t\tcvarfuncs->SetString(\"cl_cdkey\", keysrc);\n\t\t}\n\t\tbreak;\n\n\tcase UI_REAL_TIME:\n\t\tVALIDATEPOINTER(arg[0], sizeof(q3time_t));\n\t\treturn Q3VM_GetRealtime(VM_POINTER(arg[0]));\n\n\tcase UI_VERIFY_CDKEY:\n\t\tVM_LONG(ret) = true;\n\t\tbreak;\n\n\tcase UI_SET_PBCLSTATUS:\n\t\tbreak;\n\n// standard Q3\n\tcase UI_MEMSET:\n\t\t{\n\t\t\tvoid *dest = VM_POINTER(arg[0]);\n\t\t\tif ((int)arg[0] + arg[2] >= mask || dest < offset)\n\t\t\t\tbreak;\t//out of bounds.\n\t\t\tmemset(dest, arg[1], arg[2]);\n\t\t}\n\t\tbreak;\n\tcase UI_MEMCPY:\n\t\t{\n\t\t\tvoid *dest = VM_POINTER(arg[0]);\n\t\t\tvoid *src = VM_POINTER(arg[1]);\n\t\t\tif ((int)arg[0] + arg[2] >= mask || VM_POINTER(arg[0]) < offset)\n\t\t\t\tbreak;\t//out of bounds.\n\t\t\tmemcpy(dest, src, arg[2]);\n\t\t}\n\t\tbreak;\n\tcase UI_STRNCPY:\n\t\t{\n\t\t\tvoid *dest = VM_POINTER(arg[0]);\n\t\t\tvoid *src = VM_POINTER(arg[1]);\n\t\t\tif (arg[0] + arg[2] >= mask || VM_POINTER(arg[0]) < offset)\n\t\t\t\tbreak;\t//out of bounds.\n\t\t\tQ_strncpyS(dest, src, arg[2]);\n\t\t}\n\t\tbreak;\n\tcase UI_SIN:\n\t\tVM_FLOAT(ret)=(float)sin(VM_FLOAT(arg[0]));\n\t\tbreak;\n\tcase UI_COS:\n\t\tVM_FLOAT(ret)=(float)cos(VM_FLOAT(arg[0]));\n\t\tbreak;\n\tcase UI_ATAN2:\n\t\tVM_FLOAT(ret)=(float)atan2(VM_FLOAT(arg[0]), VM_FLOAT(arg[1]));\n\t\tbreak;\n\tcase UI_SQRT:\n\t\tVM_FLOAT(ret)=(float)sqrt(VM_FLOAT(arg[0]));\n\t\tbreak;\n\tcase UI_FLOOR:\n\t\tVM_FLOAT(ret)=(float)floor(VM_FLOAT(arg[0]));\n\t\tbreak;\n\tcase UI_CEIL:\n\t\tVM_FLOAT(ret)=(float)ceil(VM_FLOAT(arg[0]));\n\t\tbreak;\n/*\n\tcase UI_GETPLAYERINFO:\n\t\tif (arg[1] + sizeof(vmuiclientinfo_t) >= mask || VM_POINTER(arg[1]) < offset)\n\t\t\tbreak;\t//out of bounds.\n\t\tif (VM_LONG(arg[0]) < -1 || VM_LONG(arg[0] ) >= MAX_CLIENTS)\n\t\t\tbreak;\n\t\t{\n\t\t\tint i = VM_LONG(arg[0]);\n\t\t\tvmuiclientinfo_t *vci = VM_POINTER(arg[1]);\n\t\t\tif (i == -1)\n\t\t\t{\n\t\t\t\ti = cl.playernum[0];\n\t\t\t\tif (i < 0)\n\t\t\t\t{\n\t\t\t\t\tmemset(vci, 0, sizeof(*vci));\n\t\t\t\t\treturn 0;\n\t\t\t\t}\t\n\t\t\t}\n\t\t\tvci->bottomcolour = cl.players[i].rbottomcolor;\n\t\t\tvci->frags = cl.players[i].frags;\n\t\t\tQ_strncpyz(vci->name, cl.players[i].name, UIMAX_SCOREBOARDNAME);\n\t\t\tvci->ping = cl.players[i].ping;\n\t\t\tvci->pl = cl.players[i].pl;\n\t\t\tvci->starttime = cl.players[i].entertime;\n\t\t\tvci->topcolour = cl.players[i].rtopcolor;\n\t\t\tvci->userid = cl.players[i].userid;\n\t\t\tQ_strncpyz(vci->userinfo, cl.players[i].userinfo, sizeof(vci->userinfo));\n\t\t}\n\t\tbreak;\n\tcase UI_GETSTAT:\n\t\tif (VM_LONG(arg[0]) < 0 || VM_LONG(arg[0]) >= MAX_CL_STATS)\n\t\t\tVM_LONG(ret) = 0;\t//invalid stat num.\n\t\telse\n\t\t\tVM_LONG(ret) = cl.stats[0][VM_LONG(arg[0])];\n\t\tbreak;\n\tcase UI_GETVIDINFO:\n\t\t{\n\t\t\tvidinfo_t *vi;\n\t\t\tif (arg[0] + VM_LONG(arg[1]) >= mask || VM_POINTER(arg[1]) < offset)\n\t\t\t{\n\t\t\t\tVM_LONG(ret) = 0;\n\t\t\t\tbreak;\t//out of bounds.\n\t\t\t}\n\t\t\tvi = VM_POINTER(arg[0]);\n\t\t\tif (VM_LONG(arg[1]) < sizeof(vidinfo_t))\n\t\t\t{\n\t\t\t\tVM_LONG(ret) = 0;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tVM_LONG(ret) = sizeof(vidinfo_t);\n\t\t\tvi->width = vsize[0];\n\t\t\tvi->height = vsize[1];\n\t\t\tvi->refreshrate = 60;\n\t\t\tvi->fullscreen = 1;\n\t\t\tQ_strncpyz(vi->renderername, q_renderername, sizeof(vi->renderername));\n\t\t}\n\t\tbreak;\n*/\n\n#ifdef HAVE_SERVER\n\tcase UI_PC_ADD_GLOBAL_DEFINE:\n\t\treturn botlib->PC_AddGlobalDefine(VM_POINTER(arg[0]));\n\tcase UI_PC_LOAD_SOURCE:\n\t\treturn botlib->PC_LoadSourceHandle(VM_POINTER(arg[0]));\n\tcase UI_PC_FREE_SOURCE:\n\t\treturn botlib->PC_FreeSourceHandle(VM_LONG(arg[0]));\n\tcase UI_PC_READ_TOKEN:\n\t\treturn botlib->PC_ReadTokenHandle(VM_LONG(arg[0]), VM_POINTER(arg[1]));\n\tcase UI_PC_SOURCE_FILE_AND_LINE:\n\t\treturn botlib->PC_SourceFileAndLine(VM_LONG(arg[0]), VM_POINTER(arg[1]), VM_POINTER(arg[2]));\n#else\n\tcase UI_PC_ADD_GLOBAL_DEFINE:\n\t\tCon_Printf(\"UI_PC_ADD_GLOBAL_DEFINE not supported\\n\");\n\t\tbreak;\n\tcase UI_PC_SOURCE_FILE_AND_LINE:\n\t\tScript_Get_File_And_Line(arg[0], VM_POINTER(arg[1]), VM_POINTER(arg[2]));\n\t\tbreak;\n\n\tcase UI_PC_LOAD_SOURCE:\n\t\treturn Script_LoadFile(VM_POINTER(arg[0]));\n\tcase UI_PC_FREE_SOURCE:\n\t\tScript_Free(arg[0]);\n\t\tbreak;\n\tcase UI_PC_READ_TOKEN:\n\t\t//fixme: memory protect.\n\t\treturn Script_Read(arg[0], VM_POINTER(arg[1]));\n#endif\n\n\tcase UI_CIN_PLAYCINEMATIC:\n\t\treturn UI_Cin_Play(VM_POINTER(arg[0]), VM_LONG(arg[1]), VM_LONG(arg[2]), VM_LONG(arg[3]), VM_LONG(arg[4]), VM_LONG(arg[5]));\n\tcase UI_CIN_STOPCINEMATIC:\n\t\treturn UI_Cin_Stop(VM_LONG(arg[0]));\n\tcase UI_CIN_RUNCINEMATIC:\n\t\treturn UI_Cin_Run(VM_LONG(arg[0]));\n\tcase UI_CIN_DRAWCINEMATIC:\n\t\treturn UI_Cin_Draw(VM_LONG(arg[0]));\n\tcase UI_CIN_SETEXTENTS:\n\t\treturn UI_Cin_SetExtents(VM_LONG(arg[0]), VM_LONG(arg[1]), VM_LONG(arg[2]), VM_LONG(arg[3]), VM_LONG(arg[4]));\n\n\n\n\n\n\n\n\n\n#ifndef _DEBUG\n\tdefault:\n#endif\n\t\tCon_Printf(\"Q3UI: Unknown system trap: %i\\n\", (int)fn);\n\t\treturn 0;\n\t}\n\n\treturn ret;\n}\n\nstatic int UI_SystemCallsVM(void *offset, quintptr_t mask, int fn, const int *arg)\n{\t//this is so we can use edit and continue properly (vc doesn't like function pointers for edit+continue)\n#if __WORDSIZE == 32\n\treturn UI_SystemCalls(offset, mask, fn, (qintptr_t*)arg);\n#else\n\tqintptr_t args[9];\n\n\targs[0]=arg[0];\n\targs[1]=arg[1];\n\targs[2]=arg[2];\n\targs[3]=arg[3];\n\targs[4]=arg[4];\n\targs[5]=arg[5];\n\targs[6]=arg[6];\n\targs[7]=arg[7];\n\targs[8]=arg[8];\n\n\treturn UI_SystemCalls(offset, mask, fn, args);\n#endif\n}\n\n//I'm not keen on this.\n//but dlls call it without saying what sort of vm it comes from, so I've got to have them as specifics\nstatic qintptr_t EXPORT_FN UI_SystemCallsNative(qintptr_t arg, ...)\n{\n\tqintptr_t args[9];\n\tva_list argptr;\n\n\tva_start(argptr, arg);\n\targs[0]=va_arg(argptr, qintptr_t);\n\targs[1]=va_arg(argptr, qintptr_t);\n\targs[2]=va_arg(argptr, qintptr_t);\n\targs[3]=va_arg(argptr, qintptr_t);\n\targs[4]=va_arg(argptr, qintptr_t);\n\targs[5]=va_arg(argptr, qintptr_t);\n\targs[6]=va_arg(argptr, qintptr_t);\n\targs[7]=va_arg(argptr, qintptr_t);\n\targs[8]=va_arg(argptr, qintptr_t);\n\tva_end(argptr);\n\n\treturn UI_SystemCalls(NULL, ~(quintptr_t)0, arg, args);\n}\n\nstatic void UI_Release(menu_t *m, qboolean forced)\n{\n\tkeycatcher &= ~2;\n}\nstatic void UI_DrawMenu(menu_t *m)\n{\n\tif (uivm)\n\t{\n\t\tfloat vsize[2];\n\n\t\tif (drawfuncs->GetVideoSize(vsize, NULL) && (ui_size[0] != vsize[0] || ui_size[1] != vsize[1]))\n\t\t{\t//should probably just rescale stuff instead.\n\t\t\tqboolean hadfocus = keycatcher&2;\n\t\t\tkeycatcher &= ~2;\n\t\t\tui_size[0] = vsize[0];\n\t\t\tui_size[1] = vsize[1];\n\t\t\tvmfuncs->Call(uivm, UI_INIT);\n\t\t\tif (hadfocus)\n\t\t\t{\n\t\t\t\tif (ccs.state)\n\t\t\t\t\tvmfuncs->Call(uivm, UI_SET_ACTIVE_MENU, 2);\n\t\t\t\telse\n\t\t\t\t\tvmfuncs->Call(uivm, UI_SET_ACTIVE_MENU, 1);\n\t\t\t}\n\t\t}\n\t\tvmfuncs->Call(uivm, UI_REFRESH, plugfuncs->GetMilliseconds());\n\n\n\t\tuimenu.isopaque = vmfuncs->Call(uivm, UI_IS_FULLSCREEN);\n\t}\n}\nqboolean UI_KeyPress(struct menu_s *m, qboolean isdown, unsigned int devid, int key, int unicode)\n{\n//\tqboolean result;\n\tif (!uivm)\n\t\treturn false;\n//\tif (key_dest == key_menu)\n//\t\treturn false;\n\tif (!(keycatcher&2))\n\t{\n\t\tif (key == K_ESCAPE && isdown)\n\t\t{\n\t\t\tUI_OpenMenu();\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/* if you change this here, it'll have to be changed in cl_cg.c too */\n\tswitch (key)\n\t{\n\t\t/* all these get interpreted as enter in Q3's UI... */\n\t\tcase K_JOY1:\n\t\tcase K_JOY2:\n\t\tcase K_JOY3:\n\t\tcase K_JOY4:\n\t\tcase K_AUX1:\n\t\tcase K_AUX2:\n\t\tcase K_AUX3:\n\t\tcase K_AUX4:\n\t\tcase K_AUX5:\n\t\tcase K_AUX6:\n\t\tcase K_AUX7:\n\t\tcase K_AUX8:\n\t\tcase K_AUX9:\n\t\tcase K_AUX10:\n\t\tcase K_AUX11:\n\t\tcase K_AUX14:\n\t\tcase K_AUX15:\n\t\tcase K_AUX16:\n\t\t\treturn true;\n\t\t\tbreak;\n\t\t/* Q3 doesn't know about these keys, remap them */\n\t\tcase K_GP_START:\n\t\t\tkey = K_ESCAPE;\n\t\t\tbreak;\n\t\tcase K_GP_DPAD_UP:\n\t\t\tkey = K_UPARROW;\n\t\t\tbreak;\n\t\tcase K_GP_DPAD_DOWN:\n\t\t\tkey = K_DOWNARROW;\n\t\t\tbreak;\n\t\tcase K_GP_DPAD_LEFT:\n\t\t\tkey = K_LEFTARROW;\n\t\t\tbreak;\n\t\tcase K_GP_DPAD_RIGHT:\n\t\t\tkey = K_RIGHTARROW;\n\t\t\tbreak;\n\t\tcase K_GP_A:\n\t\t\tkey = K_ENTER;\n\t\t\tbreak;\n\t\tcase K_GP_B:\n\t\t\tkey = K_ESCAPE;\n\t\t\tbreak;\n\t\tcase K_GP_X:\n\t\t\tkey = K_BACKSPACE;\n\t\t\tbreak;\n\t}\n\n\tif (key && key < 1024)\n\t\t/*result = */vmfuncs->Call(uivm, UI_KEY_EVENT, key, isdown);\n\tif (unicode && unicode < 1024)\n\t\t/*result = */vmfuncs->Call(uivm, UI_KEY_EVENT, unicode|1024, isdown);\n\treturn true;\n}\n\nstatic qboolean UI_MousePosition(struct menu_s *m, qboolean abs, unsigned int devid, float x, float y)\n{\t//q3ui is a peice of poo and only accepts relative mouse movements.\n\t//which it then clamps arbitrarily\n\t//which means we can't use hardware cursors\n\t//which results in clumsyness when switching between q3ui and everything else.\n\n\tif (uivm && !abs)\n\t{\n\t\tint px = x, py = y;\n\t\tvmfuncs->Call(uivm, UI_MOUSE_EVENT, px, py);\n\t\treturn true;\n\t}\n\treturn false;\n}\n\nvoid UI_Reset(void)\n{\n\tinputfuncs->Menu_Unlink(&uimenu, true);\n\n\tif (!drawfuncs->GetVideoSize(NULL,NULL))\t//no renderer loaded\n\t\tUI_Stop();\n\telse if (uivm)\n\t\tvmfuncs->Call(uivm, UI_INIT);\n}\n\nvoid UI_Stop (void)\n{\n\tinputfuncs->Menu_Unlink(&uimenu, true);\n\n\tif (uivm)\n\t{\n\t\tvmfuncs->Call(uivm, UI_SHUTDOWN);\n\t\tvmfuncs->Destroy(uivm);\n\t\tVM_fcloseall(0);\n\t\tuivm = NULL;\n\n\t\t//mimic Q3 and save the config if anything got changed.\n\t\t//note that q3 checks every frame. we only check when the ui is closed.\n\t\tcmdfuncs->AddText(\"cfg_save_ifmodified\\n\", true);\n#if defined(CL_MASTER)\n\t\tmasterfuncs->WriteServers();\n#endif\n\t}\n}\n\nvoid UI_Start (void)\n{\n\tint i;\n\tint apiversion;\n\tif (!cl_shownet_ptr)\n\t\treturn;\t//make sure UI_Init was called. if it wasn't then something messed up and we can't do client stuff.\n\tif (!drawfuncs->GetVideoSize(ui_size,NULL))\n\t\treturn;\n\n\tUI_Stop();\n\n\tfor (i = 0; i < MAX_PINGREQUESTS; i++)\n\t\tui_pings[i].adr.type = NA_INVALID;\n\n\tuimenu.drawmenu = UI_DrawMenu;\n\tuimenu.mousemove = UI_MousePosition;\n\tuimenu.keyevent = UI_KeyPress;\n\tuimenu.release = UI_Release;\n\tuimenu.lowpriority = true;\n\n#ifdef HAVE_SERVER\n\tSV_InitBotLib();\n#endif\n\tuivm = vmfuncs->Create(\"ui\", cvarfuncs->GetFloat(\"com_gamedirnativecode\")?UI_SystemCallsNative:NULL, \"vm/ui\", UI_SystemCallsVM);\n\tif (uivm)\n\t{\n\t\tapiversion = vmfuncs->Call(uivm, UI_GETAPIVERSION, 6);\n\t\tif (apiversion != 4 && apiversion != 6)\t//make sure we can run the thing\n\t\t{\n\t\t\tCon_Printf(\"User-Interface VM uses incompatible API version (%i)\\n\", apiversion);\n\t\t\tvmfuncs->Destroy(uivm);\n\t\t\tVM_fcloseall(0);\n\t\t\tuivm = NULL;\n\t\t\treturn;\n\t\t}\n\t\tvmfuncs->Call(uivm, UI_INIT);\n\n\t\tUI_OpenMenu();\n\t}\n}\n\nvoid UI_Restart_f(void)\n{\n\tchar arg1[256];\n\tcmdfuncs->Argv(1, arg1, sizeof(arg1));\n\tUI_Stop();\n\tif (strcmp(arg1, \"off\"))\n\t{\n\t\tUI_Start();\n\n\t\tif (uivm)\n\t\t{\n\t\t\tif (ccs.state)\n\t\t\t\tvmfuncs->Call(uivm, UI_SET_ACTIVE_MENU, 2);\n\t\t\telse\n\t\t\t\tvmfuncs->Call(uivm, UI_SET_ACTIVE_MENU, 1);\n\t\t}\n\t}\n}\n\nqboolean UI_OpenMenu(void)\n{\n\tif (!uivm)\n\t\tUI_Start();\n\tif (uivm)\n\t{\n\t\tif (ccs.state)\n\t\t\tvmfuncs->Call(uivm, UI_SET_ACTIVE_MENU, 2);\n\t\telse\n\t\t\tvmfuncs->Call(uivm, UI_SET_ACTIVE_MENU, 1);\n\t\treturn true;\n\t}\n\treturn false;\n}\n\nqboolean UI_ConsoleCommand(void)\n{\n\tif (uivm)\n\t\treturn vmfuncs->Call(uivm, UI_CONSOLE_COMMAND, plugfuncs->GetMilliseconds());\n\treturn false;\n}\n\nqboolean UI_IsRunning(void)\n{\n\tif (uivm)\n\t\treturn true;\n\treturn false;\n}\n\nvm_t *UI_GetUIVM(void)\n{\n\treturn uivm;\n}\n\n\nvoid UI_Init (void)\n{\n\tcl_shownet_ptr = cvarfuncs->GetNVFDG(\"cl_shownet\", \"\", 0, NULL, \"Q3 Compat\");\n\tcl_c2sdupe_ptr = cvarfuncs->GetNVFDG(\"cl_c2sdupe\", \"\", 0, NULL, \"Q3 Compat\");\n\tcl_nodelta_ptr = cvarfuncs->GetNVFDG(\"cl_nodelta\", \"\", 0, NULL, \"Q3 Compat\");\n\n\tcmdfuncs->AddCommand(\"ui_restart\", UI_Restart_f, \"Reload the Q3-based User Interface module\");\n}\n#endif\n\n"
  },
  {
    "path": "plugins/quake3/clq3defs.h",
    "content": "#ifndef _Q3DEFS_H_\n#define _Q3DEFS_H_\n\nvoid Q3_SetKeyCatcher(int newcatcher);\nint Q3_GetKeyCatcher(void);\n\nint StringKey( const char *string, int length );\n\ntypedef struct {\n\tqboolean\tallsolid;\t// if true, plane is not valid\n\tqboolean\tstartsolid;\t// if true, the initial point was in a solid area\n\tfloat\t\tfraction;\t// time completed, 1.0 = didn't hit anything\n\tvec3_t\t\tendpos;\t\t// final position\n\tcplane_t\tplane;\t\t// surface normal at impact, transformed to world space\n\tint\t\t\tsurfaceFlags;\t// surface hit\n\tint\t\t\tcontents;\t// contents on other side of surface hit\n\tint\t\t\tentityNum;\t// entity the contacted surface is a part of\n} q3trace_t;\n\n#define\tMAX_Q3_STATS\t\t\t\t16\n#define\tMAX_Q3_PERSISTANT\t\t\t16\n#define\tMAX_Q3_POWERUPS\t\t\t\t16\n#define\tMAX_Q3_WEAPONS\t\t\t\t16\t\t\n#define\tMAX_PS_EVENTS\t\t\t\t2\ntypedef struct q3playerState_s {\n\tint\t\t\tcommandTime;\t// cmd->serverTime of last executed command\n\tint\t\t\tpm_type;\n\tint\t\t\tbobCycle;\t\t// for view bobbing and footstep generation\n\tint\t\t\tpm_flags;\t\t// ducked, jump_held, etc\n\tint\t\t\tpm_time;\n\n\tvec3_t\t\torigin;\n\tvec3_t\t\tvelocity;\n\tint\t\t\tweaponTime;\n\tint\t\t\tgravity;\n\tint\t\t\tspeed;\n\tint\t\t\tdelta_angles[3];\t// add to command angles to get view direction\n\t\t\t\t\t\t\t\t\t// changed by spawns, rotating objects, and teleporters\n\n\tint\t\t\tgroundEntityNum;// ENTITYNUM_NONE = in air\n\n\tint\t\t\tlegsTimer;\t\t// don't change low priority animations until this runs out\n\tint\t\t\tlegsAnim;\t\t// mask off ANIM_TOGGLEBIT\n\n\tint\t\t\ttorsoTimer;\t\t// don't change low priority animations until this runs out\n\tint\t\t\ttorsoAnim;\t\t// mask off ANIM_TOGGLEBIT\n\n\tint\t\t\tmovementDir;\t// a number 0 to 7 that represents the reletive angle\n\t\t\t\t\t\t\t\t// of movement to the view angle (axial and diagonals)\n\t\t\t\t\t\t\t\t// when at rest, the value will remain unchanged\n\t\t\t\t\t\t\t\t// used to twist the legs during strafing\n\n\tvec3_t\t\tgrapplePoint;\t// location of grapple to pull towards if PMF_GRAPPLE_PULL\n\n\tint\t\t\teFlags;\t\t\t// copied to entityState_t->eFlags\n\n\tint\t\t\teventSequence;\t// pmove generated events\n\tint\t\t\tevents[MAX_PS_EVENTS];\n\tint\t\t\teventParms[MAX_PS_EVENTS];\n\n\tint\t\t\texternalEvent;\t// events set on player from another source\n\tint\t\t\texternalEventParm;\n\tint\t\t\texternalEventTime;\n\n\tint\t\t\tclientNum;\t\t// ranges from 0 to MAX_CLIENTS-1\n\tint\t\t\tweapon;\t\t\t// copied to entityState_t->weapon\n\tint\t\t\tweaponstate;\n\n\tvec3_t\t\tviewangles;\t\t// for fixed views\n\tint\t\t\tviewheight;\n\n\t// damage feedback\n\tint\t\t\tdamageEvent;\t// when it changes, latch the other parms\n\tint\t\t\tdamageYaw;\n\tint\t\t\tdamagePitch;\n\tint\t\t\tdamageCount;\n\n\tint\t\t\tstats[MAX_Q3_STATS];\n\tint\t\t\tpersistant[MAX_Q3_PERSISTANT];\t// stats that aren't cleared on death\n\tint\t\t\tpowerups[MAX_Q3_POWERUPS];\t// level.time that the powerup runs out\n\tint\t\t\tammo[MAX_Q3_WEAPONS];\n\tint\t\t\tgeneric1;\n\tint\t\t\tloopSound;\n\tint\t\t\tjumppad_ent;\t// jumppad entity hit this frame\n\t// not communicated over the net at all\n\tint\t\t\tping;\t\t\t// server to game info for scoreboard\n\tint\t\t\tpmove_framecount;\t// FIXME: don't transmit over the network\n\tint\t\t\tjumppad_frame;\n\tint\t\t\tentityEventSequence;\n\n} q3playerState_t;\n\n\n\ntypedef enum {\n\tTR_STATIONARY,\n\tTR_INTERPOLATE,\t\t\t\t// non-parametric, but interpolate between snapshots\n\tTR_LINEAR,\n\tTR_LINEAR_STOP,\n\tTR_SINE,\t\t\t\t\t// value = base + sin( time / duration ) * delta\n\tTR_GRAVITY\n} trType_t;\ntypedef struct {\n\ttrType_t\ttrType;\n\tint\t\ttrTime;\n\tint\t\ttrDuration;\t\t\t// if non 0, trTime + trDuration = stop time\n\tvec3_t\ttrBase;\n\tvec3_t\ttrDelta;\t\t\t// velocity, etc\n} trajectory_t;\ntypedef struct q3entityState_s {\n\tint\t\tnumber;\t\t\t// entity index\n\tint\t\teType;\t\t\t// entityType_t\n\tint\t\teFlags;\n\n\ttrajectory_t\tpos;\t// for calculating position\n\ttrajectory_t\tapos;\t// for calculating angles\n\n\tint\t\ttime;\n\tint\t\ttime2;\n\n\tvec3_t\torigin;\n\tvec3_t\torigin2;\n\n\tvec3_t\tangles;\n\tvec3_t\tangles2;\n\n\tint\t\totherEntityNum;\t// shotgun sources, etc\n\tint\t\totherEntityNum2;\n\n\tint\t\tgroundEntityNum;\t// -1 = in air\n\n\tint\t\tconstantLight;\t// r + (g<<8) + (b<<16) + (intensity<<24)\n\tint\t\tloopSound;\t\t// constantly loop this sound\n\n\tint\t\tmodelindex;\n\tint\t\tmodelindex2;\n\tint\t\tclientNum;\t\t// 0 to (MAX_CLIENTS - 1), for players and corpses\n\tint\t\tframe;\n\n\tint\t\tsolid;\t\t\t// for client side prediction, trap_linkentity sets this properly\n\n\tint\t\tevent;\t\t\t// impulse events -- muzzle flashes, footsteps, etc\n\tint\t\teventParm;\n\n\n\t// for players\n\tint\t\tpowerups;\t\t// bit flags\n\tint\t\tweapon;\t\t\t// determines weapon and flash model, etc\n\tint\t\tlegsAnim;\t\t// mask off ANIM_TOGGLEBIT\n\tint\t\ttorsoAnim;\t\t// mask off ANIM_TOGGLEBIT\n\n\tint\t\tgeneric1;\n\n} q3entityState_t;\n\n#define MAX_MAP_AREA_BYTES 32\n\n#define MAX_ENTITIES_IN_SNAPSHOT 256\n//This struct is exposed to the cgame\ntypedef struct snapshot_s {\n\tint\t\t\t\tsnapFlags;\t\t\t// SNAPFLAG_RATE_DELAYED, etc\n\tint\t\t\t\tping;\n\n\tint\t\t\t\tserverTime;\t\t// server time the message is valid for (in msec)\n\n\tqbyte\t\t\tareamask[MAX_MAP_AREA_BYTES];\t\t// portalarea visibility bits\n\n\tq3playerState_t\tps;\t\t\t\t\t\t// complete information about the current player at this time\n\n\tint\t\t\t\tnumEntities;\t\t\t// all of the entities that need to be presented\n\tq3entityState_t\tentities[MAX_ENTITIES_IN_SNAPSHOT];\t// at the time of this snapshot\n\n\tint\t\t\t\tnumServerCommands;\t\t// text based server commands to execute when this\n\tint\t\t\t\tserverCommandSequence;\t// snapshot becomes current\n} snapshot_t;\n#define\tSNAPFLAG_NOT_ACTIVE\t\t2\n\n\n\n//this struct is used internally, using a ringbuffer for entities instead of worst-case memory usage.\ntypedef struct clientSnap_s {\n\tqboolean\t\tvalid;\t\t\t// cleared if delta parsing was invalid\n\tint\t\t\t\tsnapFlags;\n\tint\t\t\t\tserverMessageNum;\n\tint\t\t\t\tserverCommandNum;\n\tint\t\t\t\tserverTime;\t\t// server time the message is valid for (in msec)\n\tint\t\t\t\tdeltaFrame;\n\tqbyte\t\t\tareabits[MAX_MAP_AREA_BYTES];\t\t// portalarea visibility bits\n\tq3playerState_t\tplayerstate;\n\tint\t\t\t\tnumEntities;\n\tint\t\t\t\tfirstEntity;\t// non-masked index into cl.parseEntities[] array\n\tint\t\t\t\tping;\n} clientSnap_t;\n\n// for ping calculation, rate estimation, usercmd delta'ing\ntypedef struct frame_s {\n\tint\t\tuserCmdNumber;\n\tint\t\tserverMessageNum;\n\tint\t\tclientTime;\n\tint\t\tserverTime;\n} q3frame_t;\n\ntypedef struct {\n\tint\t\t\t\tserverTime;\n\tint\t\t\t\tangles[3];\n\tint \t\t\tbuttons;\n\tqbyte\t\t\tweapon;           // weapon\n\tsigned char\tforwardmove, rightmove, upmove;\n} q3usercmd_t;\n#define Q3CMD_BACKUP 64\t\t//number of q3usercmd_ts that the client can queue before acks\n#define Q3CMD_MASK\t (Q3CMD_BACKUP-1)\n\n#define Q3MAX_PARSE_ENTITIES 2048\n#define Q3PARSE_ENTITIES_MASK (Q3MAX_PARSE_ENTITIES-1)\n\n#define\tMAX_Q3_GAMESTATE_CHARS\t16000\n#define\tMAX_STRING_CHARS\t1024\n\t#define MAX_Q3_CONFIGSTRINGS 1024\n\t#define Q3TEXTCMD_BACKUP\t64\t\t\t//number of reliable text commands that can be queued, must be power of two\n#define Q3TEXTCMD_MASK\t\t(Q3TEXTCMD_BACKUP-1)\n\n#define CFGSTR_SYSINFO 1\n\n\t#define MODELINDEX_BITS 8\n#define GENTITYNUM_BITS 10\n#define\tMAX_GENTITIES\t\t(1<<GENTITYNUM_BITS)\n#define ENTITYNUM_NONE (MAX_GENTITIES-1)\n\ntypedef struct {\n\tcactive_t\tstate;\n\tdouble time;\n\tnetchan_t netchan;\n\tunsigned int challenge;\n\tint lastClientCommandNum;\n\tint lastServerCommandNum;\n\tint numClientCommands;\n\n\tint\tservercount;\t//bumped on map changes\n\tint\tserverMessageNum;\n\n\tint downloadchunknum;\n\n\tint firstParseEntity;\n\n\tint playernum;\n\tint fs_key;\n\n\t//this stuff gets inserted into usercmd_t messages.\n\tint selected_weapon;\n\n\tqdownload_t *download;\n\tmodel_t *worldmodel;\n\tmodel_t *model_precache[1u<<MODELINDEX_BITS];\n\n\tclientSnap_t snapshots[Q3UPDATE_BACKUP];\n\tclientSnap_t snap;\n\n\tq3entityState_t parseEntities[Q3MAX_PARSE_ENTITIES];\n\n\tq3entityState_t baselines[MAX_GENTITIES];\n\n\tchar\t\tclientCommands[Q3TEXTCMD_BACKUP][MAX_STRING_CHARS];\n\tchar\t\tserverCommands[Q3TEXTCMD_BACKUP][MAX_STRING_CHARS];\n} ClientConnectionState_t;\nextern ClientConnectionState_t ccs;\n\n\ntypedef enum {\n\tsvcq3_bad,\n\tsvcq3_nop,\n\tsvcq3_gamestate,\n\tsvcq3_configstring,\n\tsvcq3_baseline,\n\tsvcq3_serverCommand,\n\tsvcq3_download,\n\tsvcq3_snapshot,\n\tsvcq3_eom\n} svc_ops_t;\n\ntypedef enum {\n\tclcq3_bad,\n\tclcq3_nop,\n\tclcq3_move,\n\tclcq3_nodeltaMove,\n\tclcq3_clientCommand,\n\tclcq3_eom\n} clc_ops_t;\n\n\n\n\n\n\n\n\n\n\n\n//fonts... *sigh*...\n#define GLYPH_START 0\n#define GLYPH_END 255\n#define GLYPH_CHARSTART 32\n#define GLYPH_CHAREND 127\n#define GLYPHS_PER_FONT GLYPH_END - GLYPH_START + 1\ntypedef struct {\n  int height;       // number of scan lines\n  int top;          // top of glyph in buffer\n  int bottom;       // bottom of glyph in buffer\n  int pitch;        // width for copying\n  int xSkip;        // x adjustment\n  int imageWidth;   // width of actual image\n  int imageHeight;  // height of actual image\n  float s;          // x offset in image where glyph starts\n  float t;          // y offset in image where glyph starts\n  float s2;\n  float t2;\n  int glyph;  // handle to the shader with the glyph\n  char shaderName[32];\n} glyphInfo_t;\ntypedef struct {\n  glyphInfo_t glyphs [GLYPHS_PER_FONT];\n  float glyphScale;\n  char name[OLD_MAX_QPATH];\n} fontInfo_t;\nvoid UI_RegisterFont(char *fontName, int pointSize, fontInfo_t *font);\nqboolean UI_OpenMenu(void);\n\nint UI_Cin_Play(const char *name, int x, int y, int w, int h, unsigned int flags);\nint UI_Cin_Stop(int idx);\nint UI_Cin_Run(int idx);\nint UI_Cin_Draw(int idx);\nint UI_Cin_SetExtents(int idx, int x, int y, int w, int h);\n\ntypedef struct {\n\tchar\t\trenderer_string[MAX_STRING_CHARS];\n\tchar\t\tvendor_string[MAX_STRING_CHARS];\n\tchar\t\tversion_string[MAX_STRING_CHARS];\n\tchar\t\textensions_string[8192];\n\n\tint\t\t\tmaxTextureSize;         // queried from GL\n\tint\t\t\tnumTextureUnits;        // multitexture ability\n\n\tint\t\t\tcolorBits, depthBits, stencilBits;\n\n\tint\t\t\tdriverType;\t//glDriverType_t\n\tint\t\t\thardwareType;\t//glHardwareType_t\n\n\tqboolean\tdeviceSupportsGamma;\n\tint\t\t\ttextureCompression;\t//textureCompression_t\n\tqboolean\ttextureEnvAddAvailable;\n\n\tint\t\t\tvidWidth, vidHeight;\n\tfloat\t\twindowAspect;\n\n\tint\t\t\tdisplayFrequency;\n\n\tqboolean\tisFullscreen;\n\tqboolean\tstereoEnabled;\n\tqboolean\tsmpActive;\n} q3glconfig_t;\n\n\nvoid Netchan_SetupQ3(unsigned int flags, netchan_t *chan, netadr_t *adr, int qport);\nvoid Netchan_TransmitNextFragment(struct ftenet_connections_s *socket, netchan_t *chan);\nvoid Netchan_TransmitQ3(struct ftenet_connections_s *socket, netchan_t *chan, int length, const qbyte *data);\nqboolean Netchan_ProcessQ3 (netchan_t *chan, sizebuf_t *msg);\n\nqboolean MSG_Q3_ReadDeltaEntity(const q3entityState_t *from, q3entityState_t *to, int number);\nvoid MSGQ3_WriteDeltaEntity(sizebuf_t *msg, const q3entityState_t *from, const q3entityState_t *to, qboolean force);\nvoid MSG_Q3_ReadDeltaPlayerstate(const q3playerState_t *from, q3playerState_t *to);\nvoid MSGQ3_WriteDeltaPlayerstate(sizebuf_t *msg, const q3playerState_t *from, const q3playerState_t *to);\n\nvoid MSG_Q3_ReadDeltaUsercmd(int key, const usercmd_t *from, usercmd_t *to);\n\n\nvoid MSG_WriteBits(sizebuf_t *msg, int value, int bits);\n\n\n\ntypedef struct q3refEntity_s q3refEntity_t;\nvoid VQ3_AddEntity(const q3refEntity_t *q3);\ntypedef struct q3polyvert_s q3polyvert_t;\nvoid VQ3_AddPolys(shader_t *s, int num, q3polyvert_t *verts, size_t count);\ntypedef struct q3refdef_s q3refdef_t;\nvoid VQ3_RenderView(const q3refdef_t *ref);\n\n\n\n\nqboolean CG_FillQ3Snapshot(int snapnum, snapshot_t *snapshot);\nvoid CG_ClearGameState(void);\nvoid CG_InsertIntoGameState(int num, const char *str);\nconst char *CG_GetConfigString(int num);\nvoid CG_Restart(void);\nvoid UI_Restart_f(void);\nvm_t *UI_GetUIVM(void);\nint Script_LoadFile(char *filename);\nvoid Script_Get_File_And_Line(int handle, char *filename, int *line);\nint Script_Read(int handle, struct pc_token_s *token);\nvoid Script_Free(int handle);\n\n\nvoid VARGS CLQ3_SendClientCommand(const char *fmt, ...) LIKEPRINTF(1);\nvoid CLQ3_SendAuthPacket(struct ftenet_connections_s *socket, netadr_t *gameserver);\nvoid CLQ3_SendConnectPacket(struct ftenet_connections_s *socket, netadr_t *to, int challenge, int qport, infobuf_t *userinfo);\nvoid CLQ3_Established(void);\nvoid CLQ3_Disconnect(struct ftenet_connections_s *socket);\nvoid CLQ3_SendCmd(struct ftenet_connections_s *socket, usercmd_t *cmd, unsigned int movesequence, double gametime);\nint CLQ3_ParseServerMessage (sizebuf_t *msg);\n\nvoid CG_Stop (void);\nvoid CG_Start (void);\nint CG_Refresh(double time);\nqboolean CG_ConsoleCommand(void);\nqboolean CG_KeyPressed(int key, int unicode, int down);\nunsigned int CG_GatherLoopingSounds(vec3_t *positions, unsigned int *entnums, sfx_t **sounds, unsigned int max);\n\nqboolean UI_IsRunning(void);\nqboolean UI_ConsoleCommand(void);\nvoid UI_Init (void);\nvoid UI_Start (void);\nvoid UI_Stop (void);\nqboolean UI_OpenMenu(void);\nvoid UI_Reset(void);\n\n#ifdef HAVE_SERVER\nvoid SVQ3_ShutdownGame(qboolean restarting);\nqboolean SVQ3_InitGame(server_static_t *server_state_static, server_t *server_state, qboolean restart);\nqboolean SVQ3_ConsoleCommand(void);\nqboolean SVQ3_PrefixedConsoleCommand(void);\nqboolean SVQ3_HandleClient(netadr_t *from, sizebuf_t *msg);\nvoid SVQ3_DirectConnect(netadr_t *from, sizebuf_t *msg);\nvoid SVQ3_NewMapConnects(void);\nvoid SVQ3_DropClient(struct client_s *cl);\nvoid SVQ3_RunFrame(void);\nvoid SVQ3_SendMessage(struct client_s *client);\nqboolean SVQ3_RestartGamecode(void);\nvoid SVQ3_ServerinfoChanged(const char *key);\n#endif\n#endif\n"
  },
  {
    "path": "plugins/quake3/q3common.c",
    "content": "#include \"q3common.h\"\nplug2dfuncs_t\t\t*drawfuncs;\nplug3dfuncs_t\t\t*scenefuncs;\nplugaudiofuncs_t\t*audiofuncs;\nplugq3vmfuncs_t\t\t*vmfuncs;\nplugfsfuncs_t\t\t*fsfuncs;\npluginputfuncs_t\t*inputfuncs;\nplugclientfuncs_t\t*clientfuncs;\nplugmsgfuncs_t\t\t*msgfuncs;\nplugworldfuncs_t\t*worldfuncs;\nplugmasterfuncs_t\t*masterfuncs;\nplugthreadfuncs_t\t*threadfuncs;\n\n#ifndef STATIC_Q3\ndouble realtime;\nstruct netprim_s msg_nullnetprim;\n#endif\n\n#ifdef HAVE_SERVER\n//mostly for access to sv.state or svs.sockets\nq3serverstate_t sv3;\n#endif\n\n//this file contains q3 netcode related things.\n//field info, netchan, and the WriteBits stuff (which should probably be moved to common.c with the others) \n//also contains vm filesystem\n\n#define MAX_VM_FILES 64\n\ntypedef struct {\n\tchar name[256];\n\tvfsfile_t *file;\n\tint accessmode;\n\tint owner;\n} vm_fopen_files_t;\nvm_fopen_files_t vm_fopen_files[MAX_VM_FILES];\n//FIXME: why does this not use the VFS system?\nqofs_t VM_fopen (const char *name, int *handle, int fmode, int owner)\n{\n\tint i;\n\n\tif (!handle)\n\t\treturn !!fsfuncs->LocateFile(name, FSLF_IFFOUND, NULL);\n\n\t*handle = 0;\n\n\tfor (i = 0; i < MAX_VM_FILES; i++)\n\t\tif (!vm_fopen_files[i].file)\n\t\t\tbreak;\n\n\tif (i == MAX_VM_FILES)\t//too many already open\n\t{\n\t\treturn -1;\n\t}\n\n\tif (name[1] == ':' ||\t//dos filename absolute path specified - reject.\n\t\t*name == '\\\\' || *name == '/' ||\t//absolute path was given - reject\n\t\tstrstr(name, \"..\"))\t//someone tried to be cleaver.\n\t{\n\t\treturn -1;\n\t}\n\n\tswitch (fmode)\n\t{\n\tcase VM_FS_READ:\n\t\tvm_fopen_files[i].file = fsfuncs->OpenVFS(name, \"rb\", FS_GAME);\n\t\tbreak;\n\tcase VM_FS_APPEND:\n\tcase VM_FS_APPEND_SYNC:\n\t\tvm_fopen_files[i].file = fsfuncs->OpenVFS(name, \"ab\", FS_GAMEONLY);\n\t\tbreak;\n\tcase VM_FS_WRITE:\n\t\tvm_fopen_files[i].file = fsfuncs->OpenVFS(name, \"wb\", FS_GAMEONLY);\n\t\tbreak;\n\tdefault: //bad\n\t\treturn -1;\n\t}\n\tif (!vm_fopen_files[i].file)\n\t\treturn -1;\n\n\tQ_strncpyz(vm_fopen_files[i].name, name, sizeof(vm_fopen_files[i].name));\n\tvm_fopen_files[i].accessmode = fmode;\n\tvm_fopen_files[i].owner = owner;\n\n\t*handle = i+1;\n\treturn VFS_GETLEN(vm_fopen_files[i].file);\n}\n\nvoid VM_fclose (int fnum, int owner)\n{\n\tfnum--;\n\n\tif (fnum < 0 || fnum >= MAX_VM_FILES)\n\t\treturn;\t//out of range\n\n\tif (vm_fopen_files[fnum].owner != owner)\n\t\treturn;\t//cgs?\n\n\tif (!vm_fopen_files[fnum].file)\n\t\treturn;\t//not open\n\tVFS_CLOSE(vm_fopen_files[fnum].file);\n\tvm_fopen_files[fnum].file = NULL;\n}\n\nint VM_FRead (char *dest, int quantity, int fnum, int owner)\n{\n\tfnum--;\n\tif (fnum < 0 || fnum >= MAX_VM_FILES)\n\t\treturn 0;\t//out of range\n\n\tif (vm_fopen_files[fnum].owner != owner)\n\t\treturn 0;\t//cgs?\n\n\tif (!vm_fopen_files[fnum].file)\n\t\treturn 0;\t//not open\n\tif (!vm_fopen_files[fnum].file->ReadBytes)\n\t\treturn 0;\n\treturn VFS_READ(vm_fopen_files[fnum].file, dest, quantity);\n}\nint VM_FWrite (const char *dest, int quantity, int fnum, int owner)\n{\n\tfnum--;\n\tif (fnum < 0 || fnum >= MAX_VM_FILES)\n\t\treturn 0;\t//out of range\n\n\tif (vm_fopen_files[fnum].owner != owner)\n\t\treturn 0;\t//cgs?\n\n\tif (!vm_fopen_files[fnum].file)\n\t\treturn 0;\t//not open\n\tif (!vm_fopen_files[fnum].file->WriteBytes)\n\t\treturn 0;\n\tquantity = VFS_WRITE(vm_fopen_files[fnum].file, dest, quantity);\n\n\tif (vm_fopen_files[fnum].accessmode == VM_FS_APPEND_SYNC)\n\t\tVFS_FLUSH(vm_fopen_files[fnum].file);\n\treturn quantity;\n}\nqboolean VM_FSeek (int fnum, qofs_t offset, int seektype, int owner)\n{\n\tqofs_t fsize;\n\tfnum--;\n\tif (fnum < 0 || fnum >= MAX_VM_FILES)\n\t\treturn false;\t//out of range\n\tif (vm_fopen_files[fnum].owner != owner)\n\t\treturn false;\t//cgs?\n\tif (!vm_fopen_files[fnum].file)\n\t\treturn false;\t//not open\n\tswitch(vm_fopen_files[fnum].file->seekstyle)\n\t{\n\tcase SS_SEEKABLE:\n\tcase SS_SLOW:\n\t\tfsize = VFS_GETLEN(vm_fopen_files[fnum].file);\t//can't cache it if we're writing\n\t\tswitch(seektype)\n\t\t{\n\t\tcase 0:\n\t\t\toffset += VFS_TELL(vm_fopen_files[fnum].file);\n\t\t\treturn 0;\n\t\tcase 1:\n\t\t\toffset += fsize;\n\t\t\tbreak;\n\t\tdefault:\n\t\tcase 2:\n\t\t\toffset += 0;\n\t\t\tbreak;\n\t\t}\n\t\tif (offset > fsize)\n\t\t\treturn false;\n\t\tVFS_SEEK(vm_fopen_files[fnum].file, offset);\n\t\treturn true;\n\tcase SS_PIPE:\n\tcase SS_UNSEEKABLE:\n\t\treturn false;\n\t}\n\treturn false;\t//should be unreachable.\n}\nqofs_t VM_FTell (int fnum, int owner)\n{\n\tfnum--;\n\tif (fnum < 0 || fnum >= MAX_VM_FILES)\n\t\treturn 0;\t//out of range\n\tif (vm_fopen_files[fnum].owner != owner)\n\t\treturn 0;\t//cgs?\n\tif (!vm_fopen_files[fnum].file)\n\t\treturn 0;\t//not open\n\n\treturn VFS_TELL(vm_fopen_files[fnum].file);\n}\nvoid VM_fcloseall (int owner)\n{\n\tint i;\n\tfor (i = 1; i <= MAX_VM_FILES; i++)\n\t{\n\t\tVM_fclose(i, owner);\n\t}\n}\n\n\n\n\n\n\n\n\n\n\n\n\n//filesystem searches result in a tightly-packed blob of null-terminated filenames (along with a count for how many entries)\n//$modlist searches give both gamedir AND description strings (in that order) instead of just one string per entry (loaded via fs_game cvar along with a vid_restart).\ntypedef struct {\n\tchar *initialbuffer;\n\tchar *buffer;\n\tint found;\n\tint bufferleft;\n\tint skip;\n} vmsearch_t;\nstatic int QDECL VMEnum(const char *match, qofs_t size, time_t mtime, void *args, searchpathfuncs_t *spath)\n{\n\tchar *check;\n\tint newlen;\n\tmatch += ((vmsearch_t *)args)->skip;\n\tnewlen = strlen(match)+1;\n\tif (newlen > ((vmsearch_t *)args)->bufferleft)\n\t\treturn false;\t//too many files for the buffer\n\n\tcheck = ((vmsearch_t *)args)->initialbuffer;\n\twhile(check < ((vmsearch_t *)args)->buffer)\n\t{\n\t\tif (!Q_strcasecmp(check, match))\n\t\t\treturn true;\t//we found this one already\n\t\tcheck += strlen(check)+1;\n\t}\n\n\tmemcpy(((vmsearch_t *)args)->buffer, match, newlen);\n\t((vmsearch_t *)args)->buffer+=newlen;\n\t((vmsearch_t *)args)->bufferleft-=newlen;\n\t((vmsearch_t *)args)->found++;\n\treturn true;\n}\n\nstatic int QDECL IfFound(const char *match, qofs_t size, time_t modtime, void *args, searchpathfuncs_t *spath)\n{\n\t*(qboolean*)args = true;\n\treturn true;\n}\n\nstatic int QDECL VMEnumMods(const char *match, qofs_t size, time_t modtime, void *args, searchpathfuncs_t *spath)\n{\n\tconst int continuesearch = true;\n\tconst int abortsearch = false;\n\tchar *check;\n\tchar desc[1024];\n\tint newlen;\n\tint desclen;\n\tqboolean foundone;\n\tvfsfile_t *f;\n\n\tnewlen = strlen(match)+1;\n\n\tif (newlen <= 2)\n\t\treturn continuesearch;\n\n\t//make sure match is a directory\n\tif (match[newlen-2] != '/')\n\t\treturn continuesearch;\n\n\tif (!Q_strcasecmp(match, \"baseq3/\"))\n\t\treturn continuesearch;\t//we don't want baseq3. FIXME: should be any basedir, rather than hardcoded.\n\n\tfoundone = false;\n\tfsfuncs->EnumerateFiles(FS_ROOT, va(\"%s/*.pk3\", match), IfFound, &foundone);\n\tif (foundone == false)\n\t\treturn continuesearch;\t//we only count directories with a pk3 file\n\n\tQ_strncpyz(desc, match, sizeof(desc));\n\tf = fsfuncs->OpenVFS(va(\"%sdescription.txt\", match), \"rb\", FS_ROOT);\n\tif (f)\n\t{\n\t\tchar *e;\n\t\tVFS_READ(f, desc, sizeof(desc)-1);\n\t\tVFS_CLOSE(f);\n\t\tdesc[sizeof(desc)-1] = 0;\n\t\tfor (e = desc; *e; e++)\n\t\t\tif (*e == '\\n' || *e == '\\r')\n\t\t\t{\n\t\t\t\t*e = 0;\n\t\t\t\tbreak;\n\t\t\t}\n\t}\n\n\tdesclen = strlen(desc)+1;\n\n\tif (newlen+desclen+5 > ((vmsearch_t *)args)->bufferleft)\n\t\treturn abortsearch;\t//too many files for the buffer\n\n\tcheck = ((vmsearch_t *)args)->initialbuffer;\n\twhile(check < ((vmsearch_t *)args)->buffer)\n\t{\n\t\tif (!Q_strcasecmp(check, match))\n\t\t\treturn continuesearch;\t//we found this one already\n\t\tcheck += strlen(check)+1;\n\t\tcheck += strlen(check)+1;\n\t}\n\n\tmemcpy(((vmsearch_t *)args)->buffer, match, newlen);\n\tif (newlen > 1 && match[newlen-2] == '/')\n\t\t((vmsearch_t *)args)->buffer[--newlen-1] = 0;\n\t((vmsearch_t *)args)->buffer+=newlen;\n\t((vmsearch_t *)args)->bufferleft-=newlen;\n\n\tmemcpy(((vmsearch_t *)args)->buffer, desc, desclen);\n\t((vmsearch_t *)args)->buffer+=desclen;\n\t((vmsearch_t *)args)->bufferleft-=desclen;\n\n\t((vmsearch_t *)args)->found++;\n\treturn continuesearch;\n}\n\nint VM_GetFileList(const char *path, const char *ext, char *output, int buffersize)\n{\n\tvmsearch_t vms;\n\tvms.initialbuffer = vms.buffer = output;\n\tvms.skip = strlen(path)+1;\n\tvms.bufferleft = buffersize;\n\tvms.found=0;\n\tif (!strcmp(path, \"$modlist\"))\n\t{\n\t\tvms.skip=0;\n\t\tfsfuncs->EnumerateFiles(FS_ROOT, \"*\", VMEnumMods, &vms);\n\t}\n\telse if (*(char *)ext == '.' || *(char *)ext == '/')\n\t\tfsfuncs->EnumerateFiles(FS_GAME, va(\"%s/*%s\", path, ext), VMEnum, &vms);\n\telse\n\t\tfsfuncs->EnumerateFiles(FS_GAME, va(\"%s/*.%s\", path, ext), VMEnum, &vms);\n\treturn vms.found;\n}\n\n\n\n\n\n\n\n\n\n\n#if defined(Q3SERVER) || defined(Q3CLIENT)\n\n#include \"clq3defs.h\"\t//okay, urr, this is bad for dedicated servers. urhum. Maybe they're not looking? It's only typedefs and one extern.\n\n#define MAX_VMQ3_CVARS 512\t//can be blindly increased\nstatic cvar_t *q3cvlist[MAX_VMQ3_CVARS];\nint VMQ3_Cvar_Register(q3vmcvar_t *v, char *name, char *defval, int flags)\n{\n\tint i;\n\tint fteflags = 0;\n\tcvar_t *c;\n\n\tfteflags = flags & (CVAR_ARCHIVE | CVAR_USERINFO | CVAR_SERVERINFO);\n\n\tc = cvarfuncs->GetNVFDG(name, defval, fteflags, NULL, \"Q3VM cvars\");\n\tif (!c)\t//command name, etc\n\t\treturn 0;\n\tfor (i = 0; i < MAX_VMQ3_CVARS; i++)\n\t{\n\t\tif (!q3cvlist[i])\n\t\t\tq3cvlist[i] = c;\n\t\tif (q3cvlist[i] == c)\n\t\t{\n\t\t\tif (v)\n\t\t\t{\n\t\t\t\tv->handle = i+1;\n\n\t\t\t\tVMQ3_Cvar_Update(v);\n\t\t\t}\n\t\t\treturn i+1;\n\t\t}\n\t}\n\n\tCon_Printf(\"Ran out of VMQ3 cvar handles\\n\");\n\n\treturn 0;\n}\nint VMQ3_Cvar_Update(q3vmcvar_t *v)\n{\n\tcvar_t *c;\n\tint i;\n\tif (!v)\n\t\treturn 0;\t//ERROR!\n\ti = v->handle-1;\n\tif ((unsigned)i >= MAX_VMQ3_CVARS)\n\t\treturn 0;\t//a hack attempt\n\n\tc = q3cvlist[i];\n\tif (!c)\n\t\treturn 0;\t//that slot isn't active yet\n//\tif (v->modificationCount == c->modifiedcount)\n//\t\treturn 1;\t//no changes, don't waste time on an strcpy\n\n\tv->integer = c->ival;\n\tv->value = c->value;\n\tv->modificationCount = c->modifiedcount;\n\tQ_strncpyz(v->string, c->string, sizeof(v->string));\n\n\treturn 1;\n}\n\n\n\n////////////////////////////////////////////////////////////////////////////////\n//q3 netchan\n//note that the sv and cl both have their own wrappers, to handle encryption.\n\n\n\n\n\n\n\n#define\tMAX_PACKETLEN\t\t\t1400\n#define STUNDEMULTIPLEX_MASK\t0x40000000\t//stun requires that we set this bit to avoid surprises, and ignore packets without it.\n#define FRAGMENT_MASK\t\t\t0x80000000\n#define FRAGMENTATION_TRESHOLD\t(MAX_PACKETLEN-100)\nvoid Netchan_SetupQ3(unsigned int flags, netchan_t *chan, netadr_t *adr, int qport)\n{\n\tmemset (chan, 0, sizeof(*chan));\n\n\tchan->flags = flags;\n\tchan->remote_address = *adr;\n\tchan->last_received = realtime;\n\tchan->incoming_unreliable = -1;\n\n\tchan->message.data = chan->message_buf;\n\tchan->message.allowoverflow = true;\n\tchan->message.maxsize = MAX_QWMSGLEN;\n\n\tchan->qport = qport;\n\n\tchan->qportsize = 2;\n}\nqboolean Netchan_ProcessQ3 (netchan_t *chan, sizebuf_t *msg)\n{\n//incoming_reliable_sequence is perhaps wrongly used...\n\tint\t\t\tsequence;\n\tqboolean\tfragment;\n\tint\t\t\tfragmentStart;\n\tint\t\t\tfragmentLength;\n\tchar\t\tadr[MAX_ADR_SIZE];\n\n\t// Get sequence number\n\tmsgfuncs->BeginReading(msg, msg_nullnetprim);\n\tsequence = msgfuncs->ReadBits(32);\n\n\tif (chan->flags & NCF_STUNAWARE)\n\t{\n\t\tsequence = ((sequence&0xff000000)>>24)|((sequence&0x00ff0000)>>8)|((sequence&0x0000ff00)<<8)|((sequence&0x000000ff)<<24);\t//reinterpret it as big-endian\n\t\tif (sequence & STUNDEMULTIPLEX_MASK)\n\t\t\tsequence-= STUNDEMULTIPLEX_MASK;\n\t\telse\n\t\t\treturn false;\n\t}\n\n\t// Read the qport if we are a server\n\tif (!(chan->flags&NCF_CLIENT))\n\t{\n\t\tmsgfuncs->ReadBits(16);\n\t}\n\n\t// Check if packet is a message fragment\n\tif (sequence & FRAGMENT_MASK)\n\t{\n\t\tsequence &= ~FRAGMENT_MASK;\n\n\t\tfragment = true;\n\t\tfragmentStart = msgfuncs->ReadBits(16);\n\t\tfragmentLength = msgfuncs->ReadBits(16);\n\t}\n\telse\n\t{\n\t\tfragment = false;\n\t\tfragmentStart = 0;\n\t\tfragmentLength = 0;\n\t}\n\n/*\tif (net_showpackets->integer)\n\t{\n\t\tif (fragment)\n\t\t{\n\t\t\tCon_Printf(\"%s recv %4i : s=%i fragment=%i,%i\\n\", (chan->sock == NS_CLIENT) ? \"client\" : \"server\", net_message.cursize, sequence, fragmentStart, fragmentLength);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tCon_Printf(\"%s recv %4i : s=%i\\n\", (chan->sock == NS_CLIENT) ? \"client\" : \"server\", net_message.cursize, sequence);\n\t\t}\n\t}*/\n\n\t// Discard stale or duplicated packets\n\tif (sequence <= chan->incoming_sequence)\n\t{\n/*\t\tif (net_showdrop->integer || net_showpackets->integer)\n\t\t{\n\t\t\tCon_Printf(\"%s:Out of order packet %i at %i\\n\", NET_AdrToString(chan->remote_address), chan->incoming_sequence);\n\t\t}*/\n\t\treturn false;\n\t}\n\n\t// Dropped packets don't keep the message from being used\n\tchan->drop_count = sequence - (chan->incoming_sequence + 1);\n\n\tif (chan->drop_count > 0)// && (net_showdrop->integer || net_showpackets->integer))\n\t{\n\t\tCon_DPrintf(\"%s:Dropped %i packets at %i\\n\", masterfuncs->AdrToString(adr, sizeof(adr), &chan->remote_address), chan->drop_count, sequence);\n\t}\n\n\tif (!fragment)\n\t{ // not fragmented\n\t\tchan->incoming_sequence = sequence;\n\t\tchan->last_received = realtime;\n\t\treturn true;\n\t}\n\n\t// Check for new fragmented message\n\tif (chan->incoming_reliable_sequence != sequence)\n\t{\n\t\tchan->incoming_reliable_sequence = sequence;\n\t\tchan->in_fragment_length = 0;\n\t}\n\n\t// Check fragments sequence\n\tif (chan->in_fragment_length != fragmentStart)\n\t{\n//\t\tif(net_showdrop->integer || net_showpackets->integer)\n\t\t{\n\t\t\tCon_Printf(\"%s:Dropped a message fragment\\n\", masterfuncs->AdrToString(adr, sizeof(adr), &chan->remote_address));\n\t\t}\n\t\treturn false;\n\t}\n\n\t// Check if fragmentLength is valid\n\tif (fragmentLength < 0 || fragmentLength > FRAGMENTATION_TRESHOLD || msgfuncs->ReadCount() + fragmentLength > msg->cursize || chan->in_fragment_length + fragmentLength > sizeof(chan->in_fragment_buf))\n\t{\n/*\t\tif (net_showdrop->integer || net_showpackets->integer)\n\t\t{\n\t\t\tCon_Printf(\"%s:illegal fragment length\\n\", NET_AdrToString(chan->remote_address));\n\t\t}\n*/\t\treturn false;\n\t}\n\n\t// Append to the incoming fragment buffer\n\tmemcpy(chan->in_fragment_buf + chan->in_fragment_length, msg->data + msgfuncs->ReadCount(), fragmentLength);\n\n\tchan->in_fragment_length += fragmentLength;\n\tif (fragmentLength == FRAGMENTATION_TRESHOLD)\n\t{\n\t\treturn false; // there are more fragments of this message\n\t}\n\n\t// Check if assembled message fits in buffer\n\tif (chan->in_fragment_length > msg->maxsize)\n\t{\n\t\tCon_Printf(\"%s:fragmentLength %i > net_message.maxsize\\n\", masterfuncs->AdrToString(adr, sizeof(adr), &chan->remote_address), chan->in_fragment_length);\n\t\treturn false;\n\t}\n\n\t//\n\t// Reconstruct message properly\n\t//\n\tmsgfuncs->BeginWriting(msg, msg_nullnetprim, NULL, 0);\n\tmsgfuncs->WriteLong(msg, sequence);\n\tmsgfuncs->WriteData(msg, chan->in_fragment_buf, chan->in_fragment_length);\n\n\tmsgfuncs->BeginReading(msg, msg_nullnetprim);\n\tmsgfuncs->ReadLong();\n\n\t// No more fragments\n\tchan->in_fragment_length = 0;\n\tchan->incoming_reliable_sequence = 0;\n\tchan->incoming_sequence = sequence;\n\tchan->last_received = realtime;\n\n\treturn true;\n}\n\n\n/*\n=================\nNetchan_TransmitNextFragment\n=================\n*/\nvoid Netchan_TransmitNextFragment(struct ftenet_connections_s *socket, netchan_t *chan )\n{\n\t//'reliable' is badly named. it should be 'fragment' instead.\n\t//but in the interests of a smaller netchan_t...\n\tsizebuf_t\tsend;\n\tqbyte\t\tsend_buf[MAX_PACKETLEN];\n\tint\t\t\tfragmentLength;\n\n\tint sequence = chan->outgoing_sequence | FRAGMENT_MASK;\n\tif (chan->flags&NCF_STUNAWARE)\n\t{\n\t\tsequence+= STUNDEMULTIPLEX_MASK;\n\t\tsequence = ((sequence&0xff000000)>>24)|((sequence&0x00ff0000)>>8)|((sequence&0x0000ff00)<<8)|((sequence&0x000000ff)<<24);\t//reinterpret it as big-endian\n\t}\n\t\n\t// Write the packet header\n\tmemset(&send, 0, sizeof(send));\n\tsend.packing = SZ_RAWBYTES;\n\tsend.maxsize = sizeof(send_buf);\n\tsend.data = send_buf;\n\tmsgfuncs->WriteLong( &send, sequence );\n#ifndef SERVERONLY\n\t// Send the qport if we are a client\n\tif (chan->flags&NCF_CLIENT)\n\t{\n\t\tmsgfuncs->WriteShort( &send, chan->qport);\n\t}\n#endif\n\tfragmentLength = chan->reliable_length - chan->reliable_start;\n\tif( fragmentLength > FRAGMENTATION_TRESHOLD ) {\n\t\t// remaining fragment is still too large\n\t\tfragmentLength = FRAGMENTATION_TRESHOLD;\n\t}\n\n\t// Write the fragment header\n\tmsgfuncs->WriteShort( &send, chan->reliable_start );\n\tmsgfuncs->WriteShort( &send, fragmentLength );\n\n\t// Copy message fragment to the packet\n\tmsgfuncs->WriteData(&send, chan->reliable_buf + chan->reliable_start, fragmentLength);\n\n\t// Send the datagram\n\tmsgfuncs->SendPacket(socket, send.cursize, send.data, &chan->remote_address);\n\n//\tif( net_showpackets->integer )\n//\t{\n//\t\tCon_Printf( \"%s send %4i : s=%i fragment=%i,%i\\n\", (chan->sock == NS_CLIENT) ? \"client\" : \"server\", send.cursize, chan->outgoing_sequence, chan->reliable_start, fragmentLength );\n//\t}\n\n\t// Even if we have sent the whole message,\n\t// but if fragmentLength == FRAGMENTATION_TRESHOLD we have to write empty\n\t// fragment later, because Netchan_Process expects it...\n\tchan->reliable_start += fragmentLength;\n\tif( chan->reliable_start == chan->reliable_length && fragmentLength != FRAGMENTATION_TRESHOLD )\n\t{\n\t\t// we have sent the whole message!\n\t\tchan->outgoing_sequence++;\n\t\tchan->reliable_length = 0;\n\t\tchan->reliable_start = 0;\n\n//\t\ti = chan->outgoing_sequence & (MAX_LATENT-1);\n//\t\tchan->outgoing_size[i] = send.cursize;\n//\t\tchan->outgoing_time[i] = realtime;\n\t}\n}\n\n/*\n=================\nNetchan_Transmit\n=================\n*/\nvoid Netchan_TransmitQ3(struct ftenet_connections_s *socket, netchan_t *chan, int length, const qbyte *data )\n{\n\tsizebuf_t\tsend;\n\tqbyte\t\tsend_buf[MAX_OVERALLMSGLEN+6];\n\tchar\t\tadr[MAX_ADR_SIZE];\n\tint\t\t\tsequence;\n\t\n\t// Check for message overflow\n\tif( length > MAX_OVERALLMSGLEN )\n\t{\n\t\tCon_Printf( \"%s: outgoing message overflow\\n\", masterfuncs->AdrToString( adr, sizeof(adr), &chan->remote_address ) );\n\t\treturn;\n\t}\n\n\tif( length < 0 )\n\t{\n\t\tplugfuncs->Error(\"Netchan_Transmit: length = %i\", length);\n\t}\n\n\t// Don't send if there are still unsent fragments\n\tif( chan->reliable_length )\n\t{\n\t\tNetchan_TransmitNextFragment(socket, chan);\n\t\tif( chan->reliable_length )\n\t\t{\n\t\t\tCon_DPrintf( \"%s: unsent fragments\\n\", masterfuncs->AdrToString( adr, sizeof(adr), &chan->remote_address ) );\n\t\t\treturn;\n\t\t}\n\t\t/*drop the outgoing packet if we fragmented*/\n\t\t/*failure to do this results in the wrong encoding due to the outgoing sequence*/\n\t\treturn;\n\t}\n\n\t// See if this message is too large and should be fragmented\n\tif( length >= FRAGMENTATION_TRESHOLD )\n\t{\n\t\tchan->reliable_length = length;\n\t\tchan->reliable_start = 0;\n\t\tmemcpy( chan->reliable_buf, data, length );\n\t\tNetchan_TransmitNextFragment(socket, chan);\n\t\treturn;\n\t}\n\n\tsequence = chan->outgoing_sequence;\n\tif (chan->flags & NCF_STUNAWARE)\n\t{\n\t\tsequence+= STUNDEMULTIPLEX_MASK;\n\t\tsequence = ((sequence&0xff000000)>>24)|((sequence&0x00ff0000)>>8)|((sequence&0x0000ff00)<<8)|((sequence&0x000000ff)<<24);\t//reinterpret it as big-endian\n\t}\n\n\t// Write the packet header\n\tmsgfuncs->BeginWriting(&send, msg_nullnetprim, send_buf, sizeof(send_buf));\n\tmsgfuncs->WriteLong(&send, chan->outgoing_sequence);\n#ifndef SERVERONLY\n\t// Send the qport if we are a client\n\tif (chan->flags == NCF_CLIENT)\n\t\tmsgfuncs->WriteShort(&send, chan->qport);\n#endif\n\t// Copy the message to the packet\n\tmsgfuncs->WriteData(&send, data, length);\n\n\t// Send the datagram\n\tmsgfuncs->SendPacket( socket, send.cursize, send.data, &chan->remote_address );\n\n/*\tif( net_showpackets->integer )\n\t{\n\t\tCon_Printf( \"%s send %4i : s=%i ack=%i\\n\", (chan->sock == NS_SERVER) ? \"server\" : \"client\", send.cursize , chan->outgoing_sequence, chan->incoming_sequence );\n\t}\n*/\n\tchan->outgoing_sequence++;\n\n//\ti = chan->outgoing_sequence & (MAX_LATENT-1);\n//\tchan->outgoing_size[i] = send.cursize;\n//\tchan->outgoing_time[i] = realtime;\n}\n\n\n//////////////\n\n\nint StringKey( const char *string, int length )\n{\n\tint i;\n\tint key = 0;\n\n\tfor( i=0 ; i<length && string[i] ; i++ )\n\t{\n\t\tkey += string[i] * (119 + i);\n\t}\n\n\treturn (key ^ (key >> 10) ^ (key >> 20));\n}\n\n\n\n\n\n\n\n\n\n\n\n\n\ntypedef struct {\n#ifdef MSG_SHOWNET\n\tconst char\t*name;\n#endif // MSG_SHOWNET\n\tint\t\toffset;\n\tint\t\tbits; \t// bits > 0  -->  unsigned integer\n\t\t\t\t\t// bits = 0  -->  float value\n\t\t\t\t\t// bits < 0  -->  signed integer\n} q3field_t;\n\n// field declarations\n#ifdef MSG_SHOWNET\n#\tdefine PS_FIELD(n,b)\t{ #n, ((size_t)&(((q3playerState_t *)0)->n)), b }\n#\tdefine ES_FIELD(n,b)\t{ #n, ((size_t)&(((q3entityState_t *)0)->n)), b }\n#else\n#\tdefine PS_FIELD(n,b)\t{ ((size_t)&(((q3playerState_t *)0)->n)), b }\n#\tdefine ES_FIELD(n,b)\t{ ((size_t)&(((q3entityState_t *)0)->n)), b }\n#endif\n\n// field data accessing\n#define FIELD_INTEGER(s)\t(*(int   *)((qbyte *)(s)+field->offset))\n#define FIELD_FLOAT(s)\t\t(*(float *)((qbyte *)(s)+field->offset))\n\n#define SNAPPED_BITS\t\t13\n#define MAX_SNAPPED\t\t\t(1<<SNAPPED_BITS)\n\n\n//\n// entityState_t\n//\nstatic const q3field_t esFieldTable[] = {\n\tES_FIELD( pos.trTime,\t\t\t32 ),\n\tES_FIELD( pos.trBase[0],\t\t 0 ),\n\tES_FIELD( pos.trBase[1],\t\t 0 ),\n\tES_FIELD( pos.trDelta[0],\t\t 0 ),\n\tES_FIELD( pos.trDelta[1],\t\t 0 ),\n\tES_FIELD( pos.trBase[2],\t\t 0 ),\n\tES_FIELD( apos.trBase[1],\t\t 0 ),\n\tES_FIELD( pos.trDelta[2],\t\t 0 ),\n\tES_FIELD( apos.trBase[0],\t\t 0 ),\n\tES_FIELD( event,\t\t\t\t10 ),\n\tES_FIELD( angles2[1],\t\t\t 0 ),\n\tES_FIELD( eType,\t\t\t\t 8 ),\n\tES_FIELD( torsoAnim,\t\t\t 8 ),\n\tES_FIELD( eventParm,\t\t\t 8 ),\n\tES_FIELD( legsAnim,\t\t\t\t 8 ),\n\tES_FIELD( groundEntityNum,\t\t10 ),\n\tES_FIELD( pos.trType,\t\t\t 8 ),\n\tES_FIELD( eFlags,\t\t\t\t19 ),\n\tES_FIELD( otherEntityNum,\t\t10 ),\n\tES_FIELD( weapon,\t\t\t\t 8 ),\n\tES_FIELD( clientNum,\t\t\t 8 ),\n\tES_FIELD( angles[1],\t\t\t 0 ),\n\tES_FIELD( pos.trDuration,\t\t32 ),\n\tES_FIELD( apos.trType,\t\t\t 8 ),\n\tES_FIELD( origin[0],\t\t\t 0 ),\n\tES_FIELD( origin[1],\t\t\t 0 ),\n\tES_FIELD( origin[2],\t\t\t 0 ),\n\tES_FIELD( solid,\t\t\t\t24 ),\n\tES_FIELD( powerups,\t\t\t\tMAX_Q3_POWERUPS ),\n\tES_FIELD( modelindex,\t\t\tMODELINDEX_BITS ),\n\tES_FIELD( otherEntityNum2,\t\t10 ),\n\tES_FIELD( loopSound,\t\t\t 8 ),\n\tES_FIELD( generic1,\t\t\t\t 8 ),\n\tES_FIELD( origin2[2],\t\t\t 0 ),\n\tES_FIELD( origin2[0],\t\t\t 0 ),\n\tES_FIELD( origin2[1],\t\t\t 0 ),\n\tES_FIELD( modelindex2,\t\t\tMODELINDEX_BITS ),\n\tES_FIELD( angles[0],\t\t\t 0 ),\n\tES_FIELD( time,\t\t\t\t\t32 ),\n\tES_FIELD( apos.trTime,\t\t\t32 ),\n\tES_FIELD( apos.trDuration,\t\t32 ),\n\tES_FIELD( apos.trBase[2],\t\t 0 ),\n\tES_FIELD( apos.trDelta[0],\t\t 0 ),\n\tES_FIELD( apos.trDelta[1],\t\t 0 ),\n\tES_FIELD( apos.trDelta[2],\t\t 0 ),\n\tES_FIELD( time2,\t\t\t\t32 ),\n\tES_FIELD( angles[2],\t\t\t 0 ),\n\tES_FIELD( angles2[0],\t\t\t 0 ),\n\tES_FIELD( angles2[2],\t\t\t 0 ),\n\tES_FIELD( constantLight,\t\t32 ),\n\tES_FIELD( frame,\t\t\t\t16 )\n};\n\nstatic const int esTableSize = sizeof( esFieldTable ) / sizeof( esFieldTable[0] );\n\nq3entityState_t nullEntityState;\n\n/*\n============\nMSG_ReadDeltaEntity\n\n  'from' == NULL  -->  nodelta update\n  'to'   == NULL  -->  do nothing\n\nreturns false if the ent was removed.\n============\n*/\n#ifndef SERVERONLY\nqboolean MSG_Q3_ReadDeltaEntity( const q3entityState_t *from, q3entityState_t *to, int number )\n{\n\tconst q3field_t\t*field;\n\tint\t\t\t\tto_integer;\n\tint\t\t\t\tmaxFieldNum;\n#ifdef MSG_SHOWNET\n\tint\t\t\t\tstartbits;\n\tqboolean\t\tdump;\n#endif\n\tint\t\t\t\ti;\n\n\n\tif( number < 0 || number >= MAX_GENTITIES )\n\t{\n\t\tplugfuncs->EndGame(\"MSG_ReadDeltaEntity: Bad delta entity number: %i\\n\", number);\n\t}\n\n\tif( !to )\n\t{\n\t\treturn true;\n\t}\n\n#ifdef MSG_SHOWNET\n\tdump = (qboolean)(cl_shownet->integer >= 2);\n\n\tif( dump )\n\t{\n\t\tstartbits = msg->bit;\n\t}\n#endif\n\n\tif (msgfuncs->ReadBits(1))\n\t{ \n\t\tmemset( to, 0, sizeof( *to ) );\n\t\tto->number = ENTITYNUM_NONE;\n\n#ifdef MSG_SHOWNET\n\t\tif( dump )\n\t\t{\n\t\t\tCon_Printf( \"%3i: #%-3i remove\\n\", msg->readcount, number );\n\t\t}\n#endif\n\t\treturn false;\t// removed\t\n\t}\n\n\tif( !from )\n\t{\n\t\tmemset( to, 0, sizeof( *to ) );\n\t}\n\telse\n\t{\n\t\tmemcpy( to, from, sizeof( *to ) );\n\t}\n\tto->number = number;\n\n\tif( !msgfuncs->ReadBits( 1 ) )\n\t{\n\t\treturn true; // unchanged\n\t}\n\n#ifdef MSG_SHOWNET\n\tif( dump )\n\t{\n\t\tCon_Printf( \"%3i: #%-3i \", msg->readcount, to->number );\n\t}\n#endif\n\n\tmaxFieldNum = msgfuncs->ReadByte();\n\n#ifdef MSG_SHOWNET\n\tif( dump )\n\t{\n\t\tCon_Printf( \"<%i> \", maxFieldNum );\n\t}\n#endif\n\n\tif( maxFieldNum > esTableSize )\n\t{\n\t\tplugfuncs->EndGame(\"MSG_ReadDeltaEntity: maxFieldNum > esTableSize\");\n\t}\n\n\tfor( i=0, field=esFieldTable ; i<maxFieldNum ; i++, field++ )\n\t{\n\t\tif( !msgfuncs->ReadBits( 1 ) )\n\t\t\tcontinue; // field unchanged\n\n\t\tif( !msgfuncs->ReadBits( 1 ) )\n\t\t{\n\t\t\tFIELD_INTEGER( to ) = 0;\n#ifdef MSG_SHOWNET\n\t\t\tif( dump )\n\t\t\t{\n\t\t\t\tCon_Printf( \"%s:%i \", field->name, 0 );\n\t\t\t}\n#endif\t\n\t\t\tcontinue; // field set to zero\n\t\t}\n\n\t\tif( field->bits )\n\t\t{\n\t\t\tto_integer = msgfuncs->ReadBits( field->bits );\n\t\t\tFIELD_INTEGER( to ) = to_integer;\n#ifdef MSG_SHOWNET\n\t\t\tif( dump )\n\t\t\t{\n\t\t\t\tCon_Printf( \"%s:%i \", field->name, to_integer );\n\t\t\t}\n#endif\t\n\t\t\tcontinue;\t// integer value\n\t\t}\n\n\t\n\t\tif( !msgfuncs->ReadBits( 1 ) )\n\t\t{\n\t\t\tto_integer = msgfuncs->ReadBits( 13 ) - 0x1000;\n\t\t\tFIELD_FLOAT( to ) = (float)to_integer;\n#ifdef MSG_SHOWNET\n\t\t\tif( dump )\n\t\t\t{\n\t\t\t\tCon_Printf( \"%s:%i \", field->name, to_integer );\n\t\t\t}\n#endif\t\t\n\t\t}\n\t\telse\n\t\t{\n\t\t\tFIELD_INTEGER( to ) = msgfuncs->ReadLong();\n#ifdef MSG_SHOWNET\n\t\t\tif( dump )\n\t\t\t{\n\t\t\t\tCon_Printf( \"%s:%f \", field->name, FIELD_FLOAT( to ) );\n\t\t\t}\n#endif\n\t\t}\n\t}\n\n#ifdef MSG_SHOWNET\n\tif( dump )\n\t{\n\t\tCon_Printf( \" (%i bits)\\n\", msg->bit - startbits );\n\t}\n#endif\n\n\treturn true;\n}\n#endif\n\n/*\n============\nMSG_WriteDeltaEntity\n\n  If 'force' parm is false, this won't result any bits\n  emitted if entity didn't changed at all\n\n  'from' == NULL  -->  nodelta update\n  'to'   == NULL  -->  entity removed\n============\n*/\n#ifndef CLIENTONLY\nvoid MSGQ3_WriteDeltaEntity(sizebuf_t *msg, const q3entityState_t *from, const q3entityState_t *to, qboolean force)\n{\n\tconst q3field_t\t*field;\n\tint\t\t\t\tto_value;\n\tint\t\t\t\tto_integer;\n\tfloat\t\t\tto_float;\n\tint\t\t\t\tmaxFieldNum;\n\tint\t\t\t\ti;\n\n\tif(!to)\n\t{\n\t\tif(from)\n\t\t{\n\t\t\tmsgfuncs->WriteBits(msg, from->number, GENTITYNUM_BITS);\n\t\t\tmsgfuncs->WriteBits(msg, 1, 1);\n\t\t}\n\t\treturn; // removed\n\t}\n\n\tif(to->number < 0 || to->number > MAX_GENTITIES)\n\t\tplugfuncs->EndGame(\"MSG_WriteDeltaEntity: Bad entity number: %i\", to->number);\n\n\tif(!from)\n\t\tfrom = &nullEntityState; // nodelta update\n\n\t//\n\t// find last modified field in table\n\t//\n\tmaxFieldNum = 0;\n\tfor(i=0, field=esFieldTable; i<esTableSize; i++, field++ )\n\t{\n\t\tif( FIELD_INTEGER( from ) != FIELD_INTEGER(to))\n\t\t\tmaxFieldNum = i + 1;\n\t}\n\n\tif(!maxFieldNum)\n\t{\n\t\tif(!force)\n\t\t\treturn; // don't emit any bits at all\n\n\t\tmsgfuncs->WriteBits(msg, to->number, GENTITYNUM_BITS);\n\t\tmsgfuncs->WriteBits(msg, 0, 1);\n\t\tmsgfuncs->WriteBits(msg, 0, 1);\n\t\treturn; // unchanged\n\t}\n\n\tmsgfuncs->WriteBits(msg, to->number, GENTITYNUM_BITS);\n\tmsgfuncs->WriteBits(msg, 0, 1);\n\tmsgfuncs->WriteBits(msg, 1, 1);\n\tmsgfuncs->WriteBits(msg, maxFieldNum, 8);\n\n\t//\n\t// write all modified fields\n\t//\n\tfor(i=0, field=esFieldTable; i<maxFieldNum ; i++, field++)\n\t{\n\t\tto_value = FIELD_INTEGER(to);\n\t\t\n\t\tif(FIELD_INTEGER(from) == to_value)\n\t\t{\n\t\t\tmsgfuncs->WriteBits( msg, 0, 1 );\n\t\t\tcontinue; // field unchanged\n\t\t}\n\t\tmsgfuncs->WriteBits(msg, 1, 1);\n\n\t\tif(!to_value)\n\t\t{\n\t\t\tmsgfuncs->WriteBits(msg, 0, 1);\n\t\t\tcontinue; // field set to zero\n\t\t}\n\t\tmsgfuncs->WriteBits(msg, 1, 1);\n\n\t\tif(field->bits)\n\t\t{\n\t\t\tmsgfuncs->WriteBits(msg, to_value, field->bits);\n\t\t\tcontinue; // integer value\n\t\t}\n\n\t\t//\n\t\t// figure out how to pack float value\n\t\t//\n\t\tto_float = FIELD_FLOAT(to);\n\t\tto_integer = (int)to_float;\n\n#ifdef MSG_PROFILING\n\t\tmsg_vectorsEmitted++;\n#endif // MSG_PROFILING\n\n\t\tif((float)to_integer == to_float\n\t\t\t&& to_integer + MAX_SNAPPED/2 >= 0\n\t\t\t&& to_integer + MAX_SNAPPED/2 < MAX_SNAPPED)\n\t\t{\n\t\t\tmsgfuncs->WriteBits(msg, 0, 1 ); // pack in 13 bits\n\t\t\tmsgfuncs->WriteBits(msg, to_integer + MAX_SNAPPED/2, SNAPPED_BITS);\n\n#ifdef MSG_PROFILING\n\t\t\tmsg_vectorsCompressed++;\n#endif // MSG_PROFILING\n\n\t\t} else {\n\t\t\tmsgfuncs->WriteBits(msg, 1, 1 ); // pack in 32 bits\n\t\t\tmsgfuncs->WriteBits(msg, to_value, 32);\n\t\t}\n\t}\n}\n#endif\n\n\n\n\n\n/////////////////////////////////////////////////////\n//player state\n\n\n//\n// playerState_t\n//\nstatic const q3field_t psFieldTable[] = {\n\tPS_FIELD( commandTime,\t\t\t32 ),\n\tPS_FIELD( origin[0],\t\t\t 0 ),\n\tPS_FIELD( origin[1],\t\t\t 0 ),\n\tPS_FIELD( bobCycle,\t\t\t\t 8 ),\n\tPS_FIELD( velocity[0],\t\t\t 0 ),\n\tPS_FIELD( velocity[1],\t\t\t 0 ),\n\tPS_FIELD( viewangles[1],\t\t 0 ),\n\tPS_FIELD( viewangles[0],\t\t 0 ),\n\tPS_FIELD( weaponTime,\t\t   -16 ),\n\tPS_FIELD( origin[2],\t\t\t 0 ),\n\tPS_FIELD( velocity[2],\t\t\t 0 ),\n\tPS_FIELD( legsTimer,\t\t\t 8 ),\n\tPS_FIELD( pm_time,\t\t\t   -16 ),\n\tPS_FIELD( eventSequence,\t\t16 ),\n\tPS_FIELD( torsoAnim,\t\t\t 8 ),\n\tPS_FIELD( movementDir,\t\t\t 4 ),\n\tPS_FIELD( events[0],\t\t\t 8 ),\n\tPS_FIELD( legsAnim,\t\t\t\t 8 ),\n\tPS_FIELD( events[1],\t\t\t 8 ),\n\tPS_FIELD( pm_flags,\t\t\t\t16 ),\n\tPS_FIELD( groundEntityNum,\t\t10 ),\n\tPS_FIELD( weaponstate,\t\t\t 4 ),\n\tPS_FIELD( eFlags,\t\t\t\t16 ),\n\tPS_FIELD( externalEvent,\t\t10 ),\n\tPS_FIELD( gravity,\t\t\t\t16 ),\n\tPS_FIELD( speed,\t\t\t\t16 ),\n\tPS_FIELD( delta_angles[1],\t\t16 ),\n\tPS_FIELD( externalEventParm,\t 8 ),\n\tPS_FIELD( viewheight,\t\t\t-8 ),\n\tPS_FIELD( damageEvent,\t\t\t 8 ),\n\tPS_FIELD( damageYaw,\t\t\t 8 ),\n\tPS_FIELD( damagePitch,\t\t\t 8 ),\n\tPS_FIELD( damageCount,\t\t\t 8 ),\n\tPS_FIELD( generic1,\t\t\t\t 8 ),\n\tPS_FIELD( pm_type,\t\t\t\t 8 ),\n\tPS_FIELD( delta_angles[0],\t\t16 ),\n\tPS_FIELD( delta_angles[2],\t\t16 ),\n\tPS_FIELD( torsoTimer,\t\t\t12 ),\n\tPS_FIELD( eventParms[0],\t\t 8 ),\n\tPS_FIELD( eventParms[1],\t\t 8 ),\n\tPS_FIELD( clientNum,\t\t\t 8 ),\n\tPS_FIELD( weapon,\t\t\t\t 5 ),\n\tPS_FIELD( viewangles[2],\t\t 0 ),\n\tPS_FIELD( grapplePoint[0],\t\t 0 ),\n\tPS_FIELD( grapplePoint[1],\t\t 0 ),\n\tPS_FIELD( grapplePoint[2],\t\t 0 ),\n\tPS_FIELD( jumppad_ent,\t\t\t10 ),\n\tPS_FIELD( loopSound,\t\t\t16 )\n};\nstatic const int psTableSize = sizeof( psFieldTable ) / sizeof( psFieldTable[0] );\nq3playerState_t nullPlayerState;\n\n/*\n============\nMSG_WriteDeltaPlayerstate\n\n  'from' == NULL  -->  nodelta update\n  'to'   == NULL  -->  do nothing\n============\n*/\n#ifndef CLIENTONLY\nvoid MSGQ3_WriteDeltaPlayerstate(sizebuf_t *msg, const q3playerState_t *from, const q3playerState_t *to)\n{\n\tconst q3field_t\t*field;\n\tint\t\t\t\tto_value;\n\tfloat\t\t\tto_float;\n\tint\t\t\t\tto_integer;\n\tint\t\t\t\tmaxFieldNum;\n\tunsigned int\tstatsMask;\n\tunsigned int\tpersistantMask;\n\tunsigned int\tammoMask;\n\tunsigned int\tpowerupsMask;\n\tint\t\t\t\ti, j;\n\n\tif(!to)\n\t{\n\t\treturn;\n\t}\n\n\tif(!from)\n\t{\n\t\tfrom = &nullPlayerState; // nodelta update\n\t}\n\n\t//\n\t// find last modified field in table\n\t//\n\tmaxFieldNum = 0;\n\tfor(i=0, field=psFieldTable ; i<psTableSize ; i++, field++)\n\t{\n\t\tif(FIELD_INTEGER(from) != FIELD_INTEGER(to))\n\t\t{\n\t\t\tmaxFieldNum = i + 1;\n\t\t}\n\t}\n\n\tmsgfuncs->WriteBits(msg, maxFieldNum, 8);\n\n\t//\n\t// write all modified fields\n\t//\n\tfor( i=0, field=psFieldTable ; i<maxFieldNum ; i++, field++ )\n\t{\n\t\tto_value = FIELD_INTEGER( to );\n\t\t\n\t\tif( FIELD_INTEGER( from ) == to_value )\n\t\t{\n\t\t\tmsgfuncs->WriteBits( msg, 0, 1 );\n\t\t\tcontinue; // field unchanged\n\t\t}\n\t\tmsgfuncs->WriteBits( msg, 1, 1 );\n\n\t\tif( field->bits )\n\t\t{\n\t\t\tmsgfuncs->WriteBits( msg, to_value, field->bits );\n\t\t\tcontinue; // integer value\n\t\t}\n\n\t\t//\n\t\t// figure out how to pack float value\n\t\t//\n\t\tto_float = FIELD_FLOAT( to );\n\t\tto_integer = (int)to_float;\n\n#ifdef MSG_PROFILING\n\t\tmsg_vectorsEmitted++;\n#endif // MSG_PROFILING\n\n\t\tif( (float)to_integer == to_float\n\t\t\t&& to_integer + MAX_SNAPPED/2 >= 0\n\t\t\t&& to_integer + MAX_SNAPPED/2 < MAX_SNAPPED )\n\t\t{\n\t\t\tmsgfuncs->WriteBits( msg, 0, 1 ); // pack in 13 bits\n\t\t\tmsgfuncs->WriteBits( msg, to_integer + MAX_SNAPPED/2, SNAPPED_BITS );\n\n#ifdef MSG_PROFILING\n\t\t\tmsg_vectorsCompressed++;\n#endif // MSG_PROFILING\n\n\t\t} else {\n\t\t\tmsgfuncs->WriteBits(msg, 1, 1); // pack in 32 bits\n\t\t\tmsgfuncs->WriteBits(msg, to_value, 32);\n\t\t}\n\t}\n\n\t//\n\t// find modified arrays\n\t//\n\tstatsMask = 0;\n\tfor(i=0; i<MAX_Q3_STATS; i++)\n\t{\n\t\tif(from->stats[i] != to->stats[i])\n\t\t\tstatsMask |= (1u << i);\n\t}\n\n\tpersistantMask = 0;\n\tfor(i=0 ; i<MAX_Q3_PERSISTANT ; i++)\n\t{\n\t\tif(from->persistant[i] != to->persistant[i])\n\t\t\tpersistantMask |= (1u << i);\n\t}\n\n\tammoMask = 0;\n\tfor(i=0 ; i<MAX_Q3_WEAPONS ; i++ )\n\t{\n\t\tif(from->ammo[i] != to->ammo[i])\n\t\t\tammoMask |= (1u << i);\n\t}\n\n\tpowerupsMask = 0;\n\tfor( i=0 ; i<MAX_Q3_POWERUPS ; i++ )\n\t{\n\t\tif(from->powerups[i] != to->powerups[i])\n\t\t\tpowerupsMask |= (1u << i);\n\t}\n\n\tif(statsMask || persistantMask\n\t\t\t|| ammoMask\n\t\t\t|| powerupsMask)\n\t{\n\t\t//\n\t\t// write all modified arrays\n\t\t//\n\t\tmsgfuncs->WriteBits(msg, 1, 1);\n\n\t\t// PS_STATS\n\t\tif (statsMask)\n\t\t{\n\t\t\tmsgfuncs->WriteBits(msg, 1, 1);\n\t\t\tmsgfuncs->WriteBits(msg, statsMask, 16);\n\t\t\tfor(i=0; i<MAX_Q3_STATS; i++)\n\t\t\t\tif(statsMask & (1 << i))\n\t\t\t\t\tmsgfuncs->WriteBits(msg, to->stats[i], -16);\n\t\t}\n\t\telse\n\t\t\tmsgfuncs->WriteBits(msg, 0, 1); // unchanged\n\n\t\t// PS_PERSISTANT\n\t\tif (persistantMask)\n\t\t{\n\t\t\tmsgfuncs->WriteBits(msg, 1, 1);\n\t\t\tmsgfuncs->WriteBits(msg, persistantMask, 16);\n\t\t\tfor(i=0; i<MAX_Q3_PERSISTANT; i++)\n\t\t\t\tif(persistantMask & (1 << i))\n\t\t\t\t\tmsgfuncs->WriteBits(msg, to->persistant[i], -16);\n\t\t}\n\t\telse\n\t\t\tmsgfuncs->WriteBits(msg, 0, 1); // unchanged\n\n\t\tfor (j = 0; j < MAX_Q3_WEAPONS/16; j++)\n\t\t{\n\t\t\t// PS_AMMO\n\t\t\tif (ammoMask & (0xffffu<<j))\n\t\t\t{\n\t\t\t\tmsgfuncs->WriteBits(msg, 1, 1);\n\t\t\t\tmsgfuncs->WriteBits(msg, ammoMask>>(j*16), 16);\n\t\t\t\tfor(i=0; i<16; i++)\n\t\t\t\t\tif(ammoMask & ((quint64_t)1u << (j*16+i)))\n\t\t\t\t\t\tmsgfuncs->WriteBits(msg, to->ammo[j*16+i], 16);\n\t\t\t}\n\t\t\telse\n\t\t\t\tmsgfuncs->WriteBits(msg, 0, 1); // unchanged\n\t\t}\n\n\t\t// PS_POWERUPS\n\t\tif(powerupsMask)\n\t\t{\n\t\t\tmsgfuncs->WriteBits(msg, 1, 1);\n\t\t\tmsgfuncs->WriteBits(msg, powerupsMask, 16);\n\t\t\tfor(i=0; i<MAX_Q3_POWERUPS; i++)\n\t\t\t{\n\t\t\t\tif(powerupsMask & (1 << i))\n\t\t\t\t\tmsgfuncs->WriteBits(msg, to->powerups[i], 32); // WARNING: powerups use 32 bits, not 16\n\t\t\t}\n\t\t}\n\t\telse\n\t\t\tmsgfuncs->WriteBits(msg, 0, 1); // unchanged\n\t}\n\telse\n\t\tmsgfuncs->WriteBits(msg, 0, 1);\n\n}\n#endif\n\n#ifndef SERVERONLY\nvoid MSG_Q3_ReadDeltaPlayerstate( const q3playerState_t *from, q3playerState_t *to ) {\n\tconst q3field_t\t*field;\n\tint\t\t\t\tto_integer;\n\tint\t\t\t\tmaxFieldNum;\n\tunsigned int\tbitmask;\n#ifdef MSG_SHOWNET\n\tint\t\t\t\tstartbits;\n\tqboolean\t\tdump;\n\tqboolean\t\tmoredump;\n#endif\n\tint\t\t\t\ti, j;\n\n\tif( !to )\n\t{\n\t\treturn;\n\t}\n\n#ifdef MSG_SHOWNET\n\tdump = (qboolean)(cl_shownet->integer >= 2);\n\tmoredump = (qboolean)(cl_shownet->integer >= 4);\n\n\tif( dump )\n\t{\n\t\tstartbits = msg->bit;\n\n\t\tCom_Printf( \"%3i: playerstate \", msg->readcount );\n\t}\n#endif\n\n\tif( !from )\n\t{\n\t\tmemset( to, 0, sizeof( *to ) );\n\t}\n\telse\n\t{\n\t\tmemcpy( to, from, sizeof( *to ) );\n\t}\n\n\tmaxFieldNum = msgfuncs->ReadByte();\n\n\tif( maxFieldNum > psTableSize )\n\t{\n\t\tplugfuncs->EndGame( \"MSG_ReadDeltaPlayerstate: maxFieldNum > psTableSize\" );\n\t}\n\n\tfor( i=0, field=psFieldTable ; i<maxFieldNum ; i++, field++ )\n\t{\n\t\tif(!msgfuncs->ReadBits(1))\n\t\t{\n\t\t\tcontinue; // field unchanged\n\t\t}\n\n\t\tif( field->bits )\n\t\t{\n\t\t\tto_integer = msgfuncs->ReadBits(field->bits);\n\t\t\tFIELD_INTEGER( to ) = to_integer;\n#ifdef MSG_SHOWNET\n\t\t\tif( dump )\n\t\t\t{\n\t\t\t\tCom_Printf( \"%s:%i \", field->name, to_integer );\n\t\t\t}\n#endif\t\n\t\t\tcontinue;\t// integer value\n\t\t}\n\n\t\n\t\tif(!msgfuncs->ReadBits(1))\n\t\t{\n\t\t\tto_integer = msgfuncs->ReadBits(13) - 0x1000;\n\t\t\tFIELD_FLOAT( to ) = (float)to_integer;\n#ifdef MSG_SHOWNET\n\t\t\tif( dump )\n\t\t\t{\n\t\t\t\tCom_Printf( \"%s:%i \", field->name, to_integer );\n\t\t\t}\n#endif\t\t\n\t\t}\n\t\telse\n\t\t{\n\t\t\tFIELD_INTEGER( to ) = msgfuncs->ReadLong();\n#ifdef MSG_SHOWNET\n\t\t\tif( dump )\n\t\t\t{\n\t\t\t\tCom_Printf( \"%s:%f \", field->name, FIELD_FLOAT( to ) );\n\t\t\t}\n#endif\n\t\t}\n\t}\n\n\tif( msgfuncs->ReadBits(1) )\n\t{\n\t\t// PS_STATS\n\t\tif( msgfuncs->ReadBits(1) )\n\t\t{ \n#ifdef MSG_SHOWNET\n\t\t\tif( moredump )\n\t\t\t{\n\t\t\t\tCom_Printf( \"PS_STATS \" );\n\t\t\t}\n#endif\n\t\t\tbitmask = msgfuncs->ReadBits(16);\n\t\t\tfor( i=0 ; i<MAX_Q3_STATS ; i++ )\n\t\t\t{\n\t\t\t\tif( bitmask & (1 << i) )\n\t\t\t\t{\n\t\t\t\t\tto->stats[i] = (signed short)msgfuncs->ReadBits(-16);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// PS_PERSISTANT\n\t\tif( msgfuncs->ReadBits(1 ) )\n\t\t{\n#ifdef MSG_SHOWNET\n\t\t\tif( moredump )\n\t\t\t{\n\t\t\t\tCom_Printf( \"PS_PERSISTANT \" );\n\t\t\t}\n#endif\n\n\t\t\tbitmask = msgfuncs->ReadBits(16);\n\t\t\tfor( i=0 ; i<MAX_Q3_PERSISTANT ; i++ )\n\t\t\t{\n\t\t\t\tif( bitmask & (1 << i) )\n\t\t\t\t{\n\t\t\t\t\tto->persistant[i] = (signed short)msgfuncs->ReadBits(-16);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// PS_AMMO\n\t\tfor (j=0; j < MAX_Q3_WEAPONS/16; j++)\n\t\t{\n\t\t\tif( msgfuncs->ReadBits(1) )\n\t\t\t{\n#ifdef MSG_SHOWNET\n\t\t\t\tif( moredump )\n\t\t\t\t{\n\t\t\t\t\tCom_Printf( \"PS_AMMO \" );\n\t\t\t\t}\n#endif\n\t\t\t\tbitmask = msgfuncs->ReadBits(16);\n\t\t\t\tfor( i=0 ; i<16 ; i++ )\n\t\t\t\t{\n\t\t\t\t\tif( bitmask & (1u << i) )\n\t\t\t\t\t{\n\t\t\t\t\t\tto->ammo[j*16+i] = (signed short)msgfuncs->ReadBits(16);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t// PS_POWERUPS\n\t\tif( msgfuncs->ReadBits(1) )\n\t\t{\n#ifdef MSG_SHOWNET\n\t\t\tif( moredump ) {\n\t\t\t\tCom_Printf( \"PS_POWERUPS \" );\n\t\t\t}\n#endif\n\n\t\t\tbitmask = msgfuncs->ReadBits(16);\n\t\t\tfor( i=0 ; i<MAX_Q3_POWERUPS ; i++ )\n\t\t\t{\n\t\t\t\tif( bitmask & (1 << i) )\n\t\t\t\t{\n\t\t\t\t\tto->powerups[i] = msgfuncs->ReadLong();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\n\n#ifdef MSG_SHOWNET\n\tif( dump )\n\t{\n\t\tCom_Printf( \"       (%i bits)\\n\", msg->bit - startbits );\n\t}\n#endif\n}\n#endif\n\n////////////////////////////////////////////////////////////\n//user commands\n\nint kbitmask[] = {\n\t0,\n\t0x00000001, 0x00000003, 0x00000007, 0x0000000F,\n\t0x0000001F,\t0x0000003F,\t0x0000007F,\t0x000000FF,\n\t0x000001FF,\t0x000003FF,\t0x000007FF,\t0x00000FFF,\n\t0x00001FFF,\t0x00003FFF,\t0x00007FFF,\t0x0000FFFF,\n\t0x0001FFFF,\t0x0003FFFF,\t0x0007FFFF,\t0x000FFFFF,\n\t0x001FFFFf,\t0x003FFFFF,\t0x007FFFFF,\t0x00FFFFFF,\n\t0x01FFFFFF,\t0x03FFFFFF,\t0x07FFFFFF,\t0x0FFFFFFF,\n\t0x1FFFFFFF,\t0x3FFFFFFF,\t0x7FFFFFFF,\t0xFFFFFFFF,\n};\n\nstatic int MSG_ReadDeltaKey(int key, int from, int bits)\n{\n\tif (msgfuncs->ReadBits(1))\n\t\treturn msgfuncs->ReadBits(bits) ^ (key & kbitmask[bits]);\n\telse\n\t\treturn from;\n}\nvoid MSG_Q3_ReadDeltaUsercmd(int key, const usercmd_t *from, usercmd_t *to)\n{\n\tif (msgfuncs->ReadBits(1))\n\t\tto->servertime = msgfuncs->ReadBits(8) + from->servertime;\n\telse\n\t\tto->servertime = msgfuncs->ReadBits(32);\n\tto->msec = 0;\t//first of a packet should always be an absolute value, which makes the old value awkward.\n\n\tif (!msgfuncs->ReadBits(1))\n\t{\n\t\tto->angles[0] = from->angles[0];\n\t\tto->angles[1] = from->angles[1];\n\t\tto->angles[2] = from->angles[2];\n\t\tto->forwardmove = from->forwardmove;\n\t\tto->sidemove = from->sidemove;\n\t\tto->upmove = from->upmove;\n\t\tto->buttons = from->buttons;\n\t\tto->weapon = from->weapon;\n\t}\n\telse\n\t{\n\t\tkey ^= to->servertime;\n\t\tto->angles[0]\t= MSG_ReadDeltaKey(key, from->angles[0],\t16);\n\t\tto->angles[1]\t= MSG_ReadDeltaKey(key, from->angles[1],\t16);\n\t\tto->angles[2]\t= MSG_ReadDeltaKey(key, from->angles[2],\t16);\n\t\t//yeah, this is messy\n\t\tto->forwardmove\t= (signed char)(unsigned char)MSG_ReadDeltaKey(key, (unsigned char)(signed char)from->forwardmove,\t8);\n\t\tto->sidemove\t= (signed char)(unsigned char)MSG_ReadDeltaKey(key, (unsigned char)(signed char)from->sidemove,\t\t8);\n\t\tto->upmove\t= (signed char)(unsigned char)MSG_ReadDeltaKey(key, (unsigned char)(signed char)from->upmove,\t\t8);\n\t\tto->buttons\t\t= MSG_ReadDeltaKey(key, from->buttons,\t\t16);\n\t\tto->weapon\t\t= MSG_ReadDeltaKey(key, from->weapon,\t\t8);\n\n\t}\n}\n\nqint64_t Q3VM_GetRealtime(q3time_t *qtime)\n{\t//this is useful mostly for saved games, or other weird stuff.\n\ttime_t t = time(NULL);\n\tif (qtime)\n\t{\n\t\tstruct tm *tm = localtime(&t);\n\t\tif (tm)\n\t\t{\n\t\t\tqtime->tm_sec = tm->tm_sec;\n\t\t\tqtime->tm_hour = tm->tm_hour;\n\t\t\tqtime->tm_mday = tm->tm_mday;\n\t\t\tqtime->tm_mon = tm->tm_mon;\n\t\t\tqtime->tm_year = tm->tm_year;\n\t\t\tqtime->tm_wday = tm->tm_wday;\n\t\t\tqtime->tm_yday = tm->tm_yday;\n\t\t\tqtime->tm_isdst = tm->tm_isdst;\n\t\t}\n\t\telse\n\t\t\tmemset(qtime, 0, sizeof(*qtime));\n\t}\n\treturn t;\n}\n\n\n\n\n\nstatic struct q3gamecode_s q3funcs =\n{\n#ifdef HAVE_CLIENT\n\t{\n\t\tCLQ3_SendAuthPacket,\n\t\tCLQ3_SendConnectPacket,\n\t\tCLQ3_Established,\n\t\tCLQ3_SendClientCommand,\n\t\tCLQ3_SendCmd,\n\t\tCLQ3_ParseServerMessage,\n\t\tCLQ3_Disconnect,\n\t},\n\n\t{\n\t\tCG_Restart,\n\t\tCG_Refresh,\n\t\tCG_ConsoleCommand,\n\t\tCG_KeyPressed,\n\t\tCG_GatherLoopingSounds,\n\t},\n\n\t{\n\t\tUI_IsRunning,\n\t\tUI_ConsoleCommand,\n\t\tUI_Start,\n\t\tUI_OpenMenu,\n\t\tUI_Reset,\n\t},\n#else\n{NULL},{NULL},{NULL},\n#endif\n\n#ifdef HAVE_SERVER\n\t{\n\t\tSVQ3_ShutdownGame,\n\t\tSVQ3_InitGame,\n\t\tSVQ3_ConsoleCommand,\n\t\tSVQ3_PrefixedConsoleCommand,\n\t\tSVQ3_HandleClient,\n\t\tSVQ3_DirectConnect,\n\t\tSVQ3_NewMapConnects,\n\t\tSVQ3_DropClient,\n\t\tSVQ3_RunFrame,\n\t\tSVQ3_SendMessage,\n\t\tSVQ3_RestartGamecode,\n\t\tSVQ3_ServerinfoChanged,\n\t},\n#else\n\t{NULL},\n#endif\n};\n\n#ifndef STATIC_Q3\nvoid Q3_Frame(double enginetime, double gametime)\n{\n\trealtime = enginetime;\n}\n#endif\n\nvoid Q3_Shutdown(void)\n{\n#ifdef HAVE_SERVER\n\tSVQ3_ShutdownGame(false);\n#endif\n#ifdef HAVE_CLIENT\n\tCG_Stop();\n\tUI_Stop();\n\n\tVMQ3_FlushStringHandles();\n#endif\n}\n\n#ifdef STATIC_Q3\n#define Plug_Init Plug_Q3_Init\n#endif\n\nqboolean Plug_Init(void)\n{\n\tvmfuncs = plugfuncs->GetEngineInterface(plugq3vmfuncs_name, sizeof(*vmfuncs));\n\tfsfuncs = plugfuncs->GetEngineInterface(plugfsfuncs_name, sizeof(*fsfuncs));\n\tmsgfuncs = plugfuncs->GetEngineInterface(plugmsgfuncs_name, sizeof(*msgfuncs));\n\tworldfuncs = plugfuncs->GetEngineInterface(plugworldfuncs_name, sizeof(*worldfuncs));\n\tthreadfuncs = plugfuncs->GetEngineInterface(plugthreadfuncs_name, sizeof(*threadfuncs));\n\n\tif (!vmfuncs || !fsfuncs || !msgfuncs || !worldfuncs/* || !threadfuncs -- checked on use*/)\n\t{\n\t\tCon_Printf(\"Engine functionality missing, cannot enable q3 gamecode support.\\n\");\n\t\treturn false;\n\t}\n\n\tif (!plugfuncs->ExportFunction(\"Shutdown\", Q3_Shutdown) ||\n\t\t!plugfuncs->ExportInterface(\"Quake3Plugin\", &q3funcs, sizeof(q3funcs)))\n\t{\n\t\tCon_Printf(\"Engine is already using a q3-derived gamecode plugin.\\n\");\n\t\treturn false;\n\t}\n#ifndef STATIC_Q3\n\tplugfuncs->ExportFunction(\"Tick\", Q3_Frame);\n#endif\n\n#ifdef HAVE_CLIENT\n\tdrawfuncs = plugfuncs->GetEngineInterface(plug2dfuncs_name, sizeof(*drawfuncs));\n\tscenefuncs = plugfuncs->GetEngineInterface(plug3dfuncs_name, sizeof(*scenefuncs));\n\tinputfuncs = plugfuncs->GetEngineInterface(pluginputfuncs_name, sizeof(*inputfuncs));\n\tclientfuncs = plugfuncs->GetEngineInterface(plugclientfuncs_name, sizeof(*clientfuncs));\n\taudiofuncs = plugfuncs->GetEngineInterface(plugaudiofuncs_name, sizeof(*audiofuncs));\n\tmasterfuncs = plugfuncs->GetEngineInterface(plugmasterfuncs_name, sizeof(*masterfuncs));\n\tif (drawfuncs && scenefuncs && inputfuncs && clientfuncs && audiofuncs && masterfuncs)\n\t\tUI_Init();\n#endif\n\treturn true;\n}\n#else\nqboolean Plug_Init(void)\n{\n\tCon_Printf(\"Quake3 plugin without any support...\\n\");\n\treturn false;\n}\n#endif\n"
  },
  {
    "path": "plugins/quake3/q3common.h",
    "content": "#ifndef FTEPLUGIN\n#define FTEPLUGIN\n#endif\n#include \"../plugins/plugin.h\"\n#include \"clq3defs.h\"\n\n#define VM_TOSTRCACHE(a) VMQ3_StringToHandle(VM_POINTER(a))\n#define VM_FROMSTRCACHE(a) VMQ3_StringFromHandle(a)\nchar *VMQ3_StringFromHandle(int handle);\nint VMQ3_StringToHandle(char *str);\nvoid VMQ3_FlushStringHandles(void);\n\n//#define Q3_NOENCRYPT\t//a debugging property, makes it incompatible with q3\n\n#ifdef HAVE_CLIENT\nextern plug2dfuncs_t\t*drawfuncs;\nextern plug3dfuncs_t\t*scenefuncs;\nextern pluginputfuncs_t\t*inputfuncs;\nextern plugaudiofuncs_t\t*audiofuncs;\nextern plugmasterfuncs_t*masterfuncs;\nextern plugclientfuncs_t*clientfuncs;\n#endif\n\nextern plugq3vmfuncs_t\t*vmfuncs;\nextern plugfsfuncs_t\t*fsfuncs;\nextern plugmsgfuncs_t\t*msgfuncs;\nextern plugworldfuncs_t\t*worldfuncs;\nextern plugthreadfuncs_t\t*threadfuncs;\n\nextern cvar_t *sv_maxclients;\nextern cvar_t *cl_shownet_ptr, *cl_c2sdupe_ptr, *cl_nodelta_ptr;\n\n#define Q_snprintfz Q_snprintf\n#define Sys_Error plugfuncs->Error\n\n#define Z_Malloc plugfuncs->Malloc\n#define Z_Free plugfuncs->Free\n\ntypedef struct\t//merge?\n{\n\tint\t\t\t\t\tflags;\n\tint\t\t\t\t\tareabytes;\n\tqbyte\t\t\t\tareabits[MAX_Q2MAP_AREAS/8];\t\t// portalarea visibility bits\n\tq3playerState_t\t\tps;\n\tint\t\t\t\t\tnum_entities;\n\tint\t\t\t\t\tfirst_entity;\t\t// into the circular sv_packet_entities[]\n\tint\t\t\t\t\tsenttime;\t\t\t// for ping calculations\n\n\n\tint\t\t\t\tserverMessageNum;\n\tint\t\t\t\tserverCommandNum;\n\tint\t\t\t\tserverTime;\t\t// server time the message is valid for (in msec)\n\tint\t\t\t\tlocalTime;\n\tint\t\t\t\tdeltaFrame;\n} q3client_frame_t;\n\n\n\n#ifdef HAVE_SERVER\ntypedef struct\n{\n\tworld_t *world;\n\tmodel_t *models[1u<<MODELINDEX_BITS];\n\n\tqboolean restarting;\n\tfloat restartedtime;\n\n\tvec2_t\t\t\tgridbias;\n\tvec2_t\t\t\tgridscale;\n\tsize_t\t\t\tgridsize[2];\n\tareagridlink_t\t*gridareas;\t\t//[gridsize[0]*gridsize[1]]\n\tareagridlink_t\tjumboarea;\t\t//node containing ents too large to fit.\n\tint\t\t\t\tareagridsequence;\n\n\tserver_static_t\t*server_state_static;\n\tserver_t\t\t*server_state;\n} q3serverstate_t;\nextern q3serverstate_t sv3;\n#undef CALCAREAGRIDBOUNDS\n#endif\n\n"
  },
  {
    "path": "plugins/quake3/q3g_public.h",
    "content": "/*\n===========================================================================\nCopyright (C) 1999-2005 Id Software, Inc.\n\nThis file is part of Quake III Arena source code.\n\nQuake III Arena source code is free software; you can redistribute it\nand/or modify it under the terms of the GNU General Public License as\npublished by the Free Software Foundation; either version 2 of the License,\nor (at your option) any later version.\n\nQuake III Arena source code is distributed in the hope that it will be\nuseful, but WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Foobar; if not, write to the Free Software\nFoundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n===========================================================================\n*/\n//\n\n// g_public.h -- game module information visible to server\n\n#define\tGAME_API_VERSION\t8\n\n// entity->svFlags\n// the server does not know how to interpret most of the values\n// in entityStates (level eType), so the game must explicitly flag\n// special server behaviors\n#define\tSVF_NOCLIENT\t\t\t0x00000001\t// don't send entity to clients, even if it has effects\n\n// TTimo\n// https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=551\n#define SVF_CLIENTMASK 0x00000002\n\n#define SVF_BOT\t\t\t\t\t0x00000008\t// set if the entity is a bot\n#define\tSVF_BROADCAST\t\t\t0x00000020\t// send to all connected clients\n#define\tSVF_PORTAL\t\t\t\t0x00000040\t// merge a second pvs at origin2 into snapshots\n#define\tSVF_USE_CURRENT_ORIGIN\t0x00000080\t// entity->r.currentOrigin instead of entity->s.origin\n\t\t\t\t\t\t\t\t\t\t\t// for link position (missiles and movers)\n#define SVF_SINGLECLIENT\t\t0x00000100\t// only send to a single client (entityShared_t->singleClient)\n#define SVF_NOSERVERINFO\t\t0x00000200\t// don't send CS_SERVERINFO updates to this client\n\t\t\t\t\t\t\t\t\t\t\t// so that it can be updated for ping tools without\n\t\t\t\t\t\t\t\t\t\t\t// lagging clients\n#define SVF_CAPSULE\t\t\t\t0x00000400\t// use capsule for collision detection instead of bbox\n#define SVF_NOTSINGLECLIENT\t\t0x00000800\t// send entity to everyone but one client\n\t\t\t\t\t\t\t\t\t\t\t// (entityShared_t->singleClient)\n\n\n\n//===============================================================\n\n\ntypedef struct {\n\tq3entityState_t\ts;\t\t\t\t// communicated by server to clients\n\n\tqboolean\tlinked;\t\t\t\t// qfalse if not in any good cluster\n\tint\t\t\tlinkcount;\n\n\tint\t\t\tsvFlags;\t\t\t// SVF_NOCLIENT, SVF_BROADCAST, etc\n\n\t// only send to this client when SVF_SINGLECLIENT is set\t\n\t// if SVF_CLIENTMASK is set, use bitmask for clients to send to (maxclients must be <= 32, up to the mod to enforce this)\n\tint\t\t\tsingleClient;\t\t\n\n\tqboolean\tbmodel;\t\t\t\t// if false, assume an explicit mins / maxs bounding box\n\t\t\t\t\t\t\t\t\t// only set by trap_SetBrushModel\n\tvec3_t\t\tmins, maxs;\n\tint\t\t\tcontents;\t\t\t// CONTENTS_TRIGGER, CONTENTS_SOLID, CONTENTS_BODY, etc\n\t\t\t\t\t\t\t\t\t// a non-solid entity should set to 0\n\n\tvec3_t\t\tabsmin, absmax;\t\t// derived from mins/maxs and origin + rotation\n\n\t// currentOrigin will be used for all collision detection and world linking.\n\t// it will not necessarily be the same as the trajectory evaluation for the current\n\t// time, because each entity must be moved one at a time after time is advanced\n\t// to avoid simultanious collision issues\n\tvec3_t\t\tcurrentOrigin;\n\tvec3_t\t\tcurrentAngles;\n\n\t// when a trace call is made and passEntityNum != ENTITYNUM_NONE,\n\t// an ent will be excluded from testing if:\n\t// ent->s.number == passEntityNum\t(don't interact with self)\n\t// ent->s.ownerNum = passEntityNum\t(don't interact with your own missiles)\n\t// entity[ent->s.ownerNum].ownerNum = passEntityNum\t(don't interact with other missiles from owner)\n\tint\t\t\townerNum;\n} q3entityShared_t;\n\n\n\n// the server looks at a sharedEntity, which is the start of the game's gentity_t structure\ntypedef struct {\n\tq3entityState_t\ts;\t\t\t\t// communicated by server to clients\n\tq3entityShared_t\tr;\t\t\t\t// shared by both the server system and game - I *really* don't understand this, it looks like a bug.\n} q3sharedEntity_t;\n\n//===============================================================\n\n//\n// system traps provided by the main engine\n//\ntypedef enum {\n\t//============== general Quake services ==================\n\n\tG_PRINT,\t\t// ( const char *string );\n\t// print message on the local console\n\n\tG_ERROR,\t\t// ( const char *string );\n\t// abort the game\n\n\n\tG_MILLISECONDS,\t// ( void );\n\t// get current time for profiling reasons\n\t// this should NOT be used for any game related tasks,\n\t// because it is not journaled\n\n\t// console variable interaction\n\tG_CVAR_REGISTER,\t// ( vmCvar_t *vmCvar, const char *varName, const char *defaultValue, int flags );\n\tG_CVAR_UPDATE,\t// ( vmCvar_t *vmCvar );\n\tG_CVAR_SET,\t\t// ( const char *var_name, const char *value );\n\tG_CVAR_VARIABLE_INTEGER_VALUE,\t// ( const char *var_name );\n\n\tG_CVAR_VARIABLE_STRING_BUFFER,\t// ( const char *var_name, char *buffer, int bufsize );\n\n\tG_ARGC,\t\t\t// ( void );\n\t// ClientCommand and ServerCommand parameter access\n\n\tG_ARGV,\t\t\t// ( int n, char *buffer, int bufferLength );\n//10\n\tG_FS_FOPEN_FILE,\t// ( const char *qpath, fileHandle_t *file, fsMode_t mode );\n\tG_FS_READ,\t\t// ( void *buffer, int len, fileHandle_t f );\n\tG_FS_WRITE,\t\t// ( const void *buffer, int len, fileHandle_t f );\n\tG_FS_FCLOSE_FILE,\t\t// ( fileHandle_t f );\n\n\tG_SEND_CONSOLE_COMMAND,\t// ( const char *text );\n\t// add commands to the console as if they were typed in\n\t// for map changing, etc\n\n\n\t//=========== server specific functionality =============\n\n\tG_LOCATE_GAME_DATA,\t\t// ( gentity_t *gEnts, int numGEntities, int sizeofGEntity_t,\n\t//\t\t\t\t\t\t\tplayerState_t *clients, int sizeofGameClient );\n\t// the game needs to let the server system know where and how big the gentities\n\t// are, so it can look at them directly without going through an interface\n\n\tG_DROP_CLIENT,\t\t// ( int clientNum, const char *reason );\n\t// kick a client off the server with a message\n\n\tG_SEND_SERVER_COMMAND,\t// ( int clientNum, const char *fmt, ... );\n\t// reliably sends a command string to be interpreted by the given\n\t// client.  If clientNum is -1, it will be sent to all clients\n\n\tG_SET_CONFIGSTRING,\t// ( int num, const char *string );\n\t// config strings hold all the index strings, and various other information\n\t// that is reliably communicated to all clients\n\t// All of the current configstrings are sent to clients when\n\t// they connect, and changes are sent to all connected clients.\n\t// All confgstrings are cleared at each level start.\n\n\tG_GET_CONFIGSTRING,\t// ( int num, char *buffer, int bufferSize );\n//20\n\tG_GET_USERINFO,\t\t// ( int num, char *buffer, int bufferSize );\n\t// userinfo strings are maintained by the server system, so they\n\t// are persistant across level loads, while all other game visible\n\t// data is completely reset\n\n\tG_SET_USERINFO,\t\t// ( int num, const char *buffer );\n\n\tG_GET_SERVERINFO,\t// ( char *buffer, int bufferSize );\n\t// the serverinfo info string has all the cvars visible to server browsers\n\n\tG_SET_BRUSH_MODEL,\t// ( gentity_t *ent, const char *name );\n\t// sets mins and maxs based on the brushmodel name\n\n\tG_TRACE,\t// ( trace_t *results, const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int passEntityNum, int contentmask );\n\t// collision detection against all linked entities\n\n\tG_POINT_CONTENTS,\t// ( const vec3_t point, int passEntityNum );\n\t// point contents against all linked entities\n\n\tG_IN_PVS,\t\t\t// ( const vec3_t p1, const vec3_t p2 );\n\n\tG_IN_PVS_IGNORE_PORTALS,\t// ( const vec3_t p1, const vec3_t p2 );\n\n\tG_ADJUST_AREA_PORTAL_STATE,\t// ( gentity_t *ent, qboolean open );\n\n\tG_AREAS_CONNECTED,\t// ( int area1, int area2 );\n//30\n\tG_LINKENTITY,\t\t// ( gentity_t *ent );\n\t// an entity will never be sent to a client or used for collision\n\t// if it is not passed to linkentity.  If the size, position, or\n\t// solidity changes, it must be relinked.\n\n\tG_UNLINKENTITY,\t\t// ( gentity_t *ent );\t\t\n\t// call before removing an interactive entity\n\n\tG_ENTITIES_IN_BOX,\t// ( const vec3_t mins, const vec3_t maxs, gentity_t **list, int maxcount );\n\t// EntitiesInBox will return brush models based on their bounding box,\n\t// so exact determination must still be done with EntityContact\n\n\tG_ENTITY_CONTACT,\t// ( const vec3_t mins, const vec3_t maxs, const gentity_t *ent );\n\t// perform an exact check against inline brush models of non-square shape\n\n\t// access for bots to get and free a server client (FIXME?)\n\tG_BOT_ALLOCATE_CLIENT,\t// ( void );\n\n\tG_BOT_FREE_CLIENT,\t// ( int clientNum );\n\n\tG_GET_USERCMD,\t// ( int clientNum, usercmd_t *cmd )\n\n\tG_GET_ENTITY_TOKEN,\t// qboolean ( char *buffer, int bufferSize )\n\t// Retrieves the next string token from the entity spawn text, returning\n\t// false when all tokens have been parsed.\n\t// This should only be done at GAME_INIT time.\n\n\tG_FS_GETFILELIST,\n\tG_DEBUG_POLYGON_CREATE,\n//40\n\tG_DEBUG_POLYGON_DELETE,\n\tG_REAL_TIME,\n\tG_SNAPVECTOR,\n\n\tG_TRACECAPSULE,\t// ( trace_t *results, const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int passEntityNum, int contentmask );\n\tG_ENTITY_CONTACTCAPSULE,\t// ( const vec3_t mins, const vec3_t maxs, const gentity_t *ent );\n\t\n\t// 1.32\n\tG_FS_SEEK,\n\n\tG_MEMSET = 100,\n\tG_MEMCPY,\n\tG_STRNCPY,\n\tG_SIN,\n\tG_COS,\n\tG_ATAN2,\n\tG_SQRT,\n\tG_MATRIXMULTIPLY,\n\tG_ANGLEVECTORS,\n\tG_PERPENDICULARVECTOR,\n\tG_FLOOR,\n\tG_CEIL,\n\n\tBOTLIB_SETUP = 200,\t\t\t\t// ( void );\n\tBOTLIB_SHUTDOWN,\t\t\t\t// ( void );\n\tBOTLIB_LIBVAR_SET,\n\tBOTLIB_LIBVAR_GET,\n\tBOTLIB_PC_ADD_GLOBAL_DEFINE,\n\tBOTLIB_START_FRAME,\n\tBOTLIB_LOAD_MAP,\n\tBOTLIB_UPDATENTITY,\n\tBOTLIB_TEST,\n\n\tBOTLIB_GET_SNAPSHOT_ENTITY,\t\t// ( int client, int ent );\n\tBOTLIB_GET_CONSOLE_MESSAGE,\t\t// ( int client, char *message, int size );\n\tBOTLIB_USER_COMMAND,\t\t\t// ( int client, usercmd_t *ucmd );\n\n\tBOTLIB_AAS_ENABLE_ROUTING_AREA = 300,\n\tBOTLIB_AAS_BBOX_AREAS,\n\tBOTLIB_AAS_AREA_INFO,\n\tBOTLIB_AAS_ENTITY_INFO,\n\n\tBOTLIB_AAS_INITIALIZED,\n\tBOTLIB_AAS_PRESENCE_TYPE_BOUNDING_BOX,\n\tBOTLIB_AAS_TIME,\n\n\tBOTLIB_AAS_POINT_AREA_NUM,\n\tBOTLIB_AAS_TRACE_AREAS,\n\n\tBOTLIB_AAS_POINT_CONTENTS,\n\tBOTLIB_AAS_NEXT_BSP_ENTITY,\n\tBOTLIB_AAS_VALUE_FOR_BSP_EPAIR_KEY,\n\tBOTLIB_AAS_VECTOR_FOR_BSP_EPAIR_KEY,\n\tBOTLIB_AAS_FLOAT_FOR_BSP_EPAIR_KEY,\n\tBOTLIB_AAS_INT_FOR_BSP_EPAIR_KEY,\n\n\tBOTLIB_AAS_AREA_REACHABILITY,\n\n\tBOTLIB_AAS_AREA_TRAVEL_TIME_TO_GOAL_AREA,\n\n\tBOTLIB_AAS_SWIMMING,\n\tBOTLIB_AAS_PREDICT_CLIENT_MOVEMENT,\n\n\n\tBOTLIB_EA_SAY = 400,\n\tBOTLIB_EA_SAY_TEAM,\n\tBOTLIB_EA_COMMAND,\n\n\tBOTLIB_EA_ACTION,\n\tBOTLIB_EA_GESTURE,\n\tBOTLIB_EA_TALK,\n\tBOTLIB_EA_ATTACK,\n\tBOTLIB_EA_USE,\n\tBOTLIB_EA_RESPAWN,\n\tBOTLIB_EA_CROUCH,\n\tBOTLIB_EA_MOVE_UP,\n\tBOTLIB_EA_MOVE_DOWN,\n\tBOTLIB_EA_MOVE_FORWARD,\n\tBOTLIB_EA_MOVE_BACK,\n\tBOTLIB_EA_MOVE_LEFT,\n\tBOTLIB_EA_MOVE_RIGHT,\n\n\tBOTLIB_EA_SELECT_WEAPON,\n\tBOTLIB_EA_JUMP,\n\tBOTLIB_EA_DELAYED_JUMP,\n\tBOTLIB_EA_MOVE,\n\tBOTLIB_EA_VIEW,\n\n\tBOTLIB_EA_END_REGULAR,\n\tBOTLIB_EA_GET_INPUT,\n\tBOTLIB_EA_RESET_INPUT,\n\n\n\tBOTLIB_AI_LOAD_CHARACTER = 500,\n\tBOTLIB_AI_FREE_CHARACTER,\n\tBOTLIB_AI_CHARACTERISTIC_FLOAT,\n\tBOTLIB_AI_CHARACTERISTIC_BFLOAT,\n\tBOTLIB_AI_CHARACTERISTIC_INTEGER,\n\tBOTLIB_AI_CHARACTERISTIC_BINTEGER,\n\tBOTLIB_AI_CHARACTERISTIC_STRING,\n\n\tBOTLIB_AI_ALLOC_CHAT_STATE,\n\tBOTLIB_AI_FREE_CHAT_STATE,\n\tBOTLIB_AI_QUEUE_CONSOLE_MESSAGE,\n\tBOTLIB_AI_REMOVE_CONSOLE_MESSAGE,\n\tBOTLIB_AI_NEXT_CONSOLE_MESSAGE,\n\tBOTLIB_AI_NUM_CONSOLE_MESSAGE,\n\tBOTLIB_AI_INITIAL_CHAT,\n\tBOTLIB_AI_REPLY_CHAT,\n\tBOTLIB_AI_CHAT_LENGTH,\n\tBOTLIB_AI_ENTER_CHAT,\n\tBOTLIB_AI_STRING_CONTAINS,\n\tBOTLIB_AI_FIND_MATCH,\n\tBOTLIB_AI_MATCH_VARIABLE,\n\tBOTLIB_AI_UNIFY_WHITE_SPACES,\n\tBOTLIB_AI_REPLACE_SYNONYMS,\n\tBOTLIB_AI_LOAD_CHAT_FILE,\n\tBOTLIB_AI_SET_CHAT_GENDER,\n\tBOTLIB_AI_SET_CHAT_NAME,\n\n\tBOTLIB_AI_RESET_GOAL_STATE,\n\tBOTLIB_AI_RESET_AVOID_GOALS,\n\tBOTLIB_AI_PUSH_GOAL,\n\tBOTLIB_AI_POP_GOAL,\n\tBOTLIB_AI_EMPTY_GOAL_STACK,\n\tBOTLIB_AI_DUMP_AVOID_GOALS,\n\tBOTLIB_AI_DUMP_GOAL_STACK,\n\tBOTLIB_AI_GOAL_NAME,\n\tBOTLIB_AI_GET_TOP_GOAL,\n\tBOTLIB_AI_GET_SECOND_GOAL,\n\tBOTLIB_AI_CHOOSE_LTG_ITEM,\n\tBOTLIB_AI_CHOOSE_NBG_ITEM,\n\tBOTLIB_AI_TOUCHING_GOAL,\n\tBOTLIB_AI_ITEM_GOAL_IN_VIS_BUT_NOT_VISIBLE,\n\tBOTLIB_AI_GET_LEVEL_ITEM_GOAL,\n\tBOTLIB_AI_AVOID_GOAL_TIME,\n\tBOTLIB_AI_INIT_LEVEL_ITEMS,\n\tBOTLIB_AI_UPDATE_ENTITY_ITEMS,\n\tBOTLIB_AI_LOAD_ITEM_WEIGHTS,\n\tBOTLIB_AI_FREE_ITEM_WEIGHTS,\n\tBOTLIB_AI_SAVE_GOAL_FUZZY_LOGIC,\n\tBOTLIB_AI_ALLOC_GOAL_STATE,\n\tBOTLIB_AI_FREE_GOAL_STATE,\n\n\tBOTLIB_AI_RESET_MOVE_STATE,\n\tBOTLIB_AI_MOVE_TO_GOAL,\n\tBOTLIB_AI_MOVE_IN_DIRECTION,\n\tBOTLIB_AI_RESET_AVOID_REACH,\n\tBOTLIB_AI_RESET_LAST_AVOID_REACH,\n\tBOTLIB_AI_REACHABILITY_AREA,\n\tBOTLIB_AI_MOVEMENT_VIEW_TARGET,\n\tBOTLIB_AI_ALLOC_MOVE_STATE,\n\tBOTLIB_AI_FREE_MOVE_STATE,\n\tBOTLIB_AI_INIT_MOVE_STATE,\n\tBOTLIB_AI_CHOOSE_BEST_FIGHT_WEAPON,\n\tBOTLIB_AI_GET_WEAPON_INFO,\n\tBOTLIB_AI_LOAD_WEAPON_WEIGHTS,\n\tBOTLIB_AI_ALLOC_WEAPON_STATE,\n\tBOTLIB_AI_FREE_WEAPON_STATE,\n\tBOTLIB_AI_RESET_WEAPON_STATE,\n\n\tBOTLIB_AI_GENETIC_PARENTS_AND_CHILD_SELECTION,\n\tBOTLIB_AI_INTERBREED_GOAL_FUZZY_LOGIC,\n\tBOTLIB_AI_MUTATE_GOAL_FUZZY_LOGIC,\n\tBOTLIB_AI_GET_NEXT_CAMP_SPOT_GOAL,\n\tBOTLIB_AI_GET_MAP_LOCATION_GOAL,\n\tBOTLIB_AI_NUM_INITIAL_CHATS,\n\tBOTLIB_AI_GET_CHAT_MESSAGE,\n\tBOTLIB_AI_REMOVE_FROM_AVOID_GOALS,\n\tBOTLIB_AI_PREDICT_VISIBLE_POSITION,\n\n\tBOTLIB_AI_SET_AVOID_GOAL_TIME,\n\tBOTLIB_AI_ADD_AVOID_SPOT,\n\tBOTLIB_AAS_ALTERNATIVE_ROUTE_GOAL,\n\tBOTLIB_AAS_PREDICT_ROUTE,\n\tBOTLIB_AAS_POINT_REACHABILITY_AREA_INDEX,\n\n\tBOTLIB_PC_LOAD_SOURCE,\n\tBOTLIB_PC_FREE_SOURCE,\n\tBOTLIB_PC_READ_TOKEN,\n\tBOTLIB_PC_SOURCE_FILE_AND_LINE,\n\n\n\tG_DEFAULTCASEWARNINGDISABLE //note: not an allowed index, just exists to prevent clang from warning about the default case.\n} q3ggameImport_t;\n\n\n//\n// functions exported by the game subsystem\n//\ntypedef enum {\n\tGAME_INIT,\t// ( int levelTime, int randomSeed, int restart );\n\t// init and shutdown will be called every single level\n\t// The game should call G_GET_ENTITY_TOKEN to parse through all the\n\t// entity configuration text and spawn gentities.\n\n\tGAME_SHUTDOWN,\t// (void);\n\n\tGAME_CLIENT_CONNECT,\t// ( int clientNum, qboolean firstTime, qboolean isBot );\n\t// return NULL if the client is allowed to connect, otherwise return\n\t// a text string with the reason for denial\n\n\tGAME_CLIENT_BEGIN,\t\t\t\t// ( int clientNum );\n\n\tGAME_CLIENT_USERINFO_CHANGED,\t// ( int clientNum );\n\n\tGAME_CLIENT_DISCONNECT,\t\t\t// ( int clientNum );\n\n\tGAME_CLIENT_COMMAND,\t\t\t// ( int clientNum );\n\n\tGAME_CLIENT_THINK,\t\t\t\t// ( int clientNum );\n\n\tGAME_RUN_FRAME,\t\t\t\t\t// ( int levelTime );\n\n\tGAME_CONSOLE_COMMAND,\t\t\t// ( void );\n\t// ConsoleCommand will be called when a command has been issued\n\t// that is not recognized as a builtin function.\n\t// The game can issue trap_argc() / trap_argv() commands to get the command\n\t// and parameters.  Return qfalse if the game doesn't recognize it as a command.\n\n\tBOTAI_START_FRAME\t\t\t\t// ( int time );\n} gameExport_t;\n\n\n"
  },
  {
    "path": "plugins/quake3/svq3_game.c",
    "content": "#include \"q3common.h\"\n#define net_message asrgsaerge\n\n//An implementation of a Q3 server...\n//requires qvm implementation and existing q3 client stuff (or at least the overlapping stuff in q3common.c).\n\n#ifdef Q3SERVER\n#define USEBOTLIB\n\n#ifdef USEBOTLIB\n\n\n#define fileHandle_t int\n#define fsMode_t int\n#define pc_token_t void\n#include \"botlib/botlib.h\"\n\n#define Z_TAG_BOTLIB 221726\nstatic zonegroup_t botlibmem;\nstatic zonegroup_t botlibhunkmem;\n\nstatic botlib_export_t *FTE_GetBotLibAPI(int apiVersion, botlib_import_t *import)\n{\t//a stub that will prevent botlib from loading.\n#ifdef BOTLIB_STATIC\n\treturn GetBotLibAPI(apiVersion, import);\n#else\n\tstatic void *botlib;\n\tstatic botlib_export_t *(QDECL *pGetBotLibAPI)(int apiVersion, botlib_import_t *import);\n\n\tdllfunction_t funcs[] =\n\t{\n\t\t{(void**)&pGetBotLibAPI, \"GetBotLibAPI\"},\n\t\t{NULL}\n\t};\n\n\tif (!botlib && host_parms.binarydir)\n\t\tbotlib = Sys_LoadLibrary(va(\"%slibfteq3bot\", host_parms.binarydir), funcs);\n\tif (!botlib)\n\t\tbotlib = Sys_LoadLibrary(\"botlib\", funcs);\n\tif (!botlib)\n\t\treturn NULL;\n\treturn pGetBotLibAPI(apiVersion, import);\n#endif\n}\n\nbotlib_export_t *botlib;\n#endif\n\n\n\n#include \"clq3defs.h\"\n#include \"q3g_public.h\"\n\n#define svs (*sv3.server_state_static)\n#define sv (*sv3.server_state)\n\nvm_t *q3gamevm;\n#ifdef VM_CG\nextern vm_t *cgvm;\n#endif\n\n\n#define fs_key 0\n\nstatic qboolean q3_serverinfo_dirty; //signals that the serverinfo has changed. this involves networking so we use a flag to gather changes to splurge at once instead of many minor changes that each send out new config string updates\nstatic char *svq3_configstrings[MAX_Q3_CONFIGSTRINGS];\n\nstatic qboolean q3_serverinfo_dirty;\nstatic q3sharedEntity_t *q3_entarray;\nstatic int\tnumq3entities;\nstatic int sizeofq3gentity;\nstatic q3playerState_t *q3playerstates;\nstatic int sizeofGameClient;\n\nstatic int q3_num_snapshot_entities;\nstatic int q3_next_snapshot_entities;\nstatic q3entityState_t *q3_snapshot_entities;\nstatic q3entityState_t *q3_baselines;\ncvar_t *sv_maxclients;\n\n#define NUM_FOR_GENTITY(ge) (((char*)ge - (char*)q3_entarray) / sizeofq3gentity)\n#define NUM_FOR_SENTITY(se) (se - q3_sentities)\n#define GENTITY_FOR_NUM(num) ((q3sharedEntity_t*)((char *)q3_entarray + sizeofq3gentity*(num)))\n#define SENTITY_FOR_NUM(num) ((q3serverEntity_t*)((char *)q3_sentities + sizeof(q3serverEntity_t)*(num)))\n#define SENTITY_FOR_GENTITY(ge) (SENTITY_FOR_NUM(NUM_FOR_GENTITY(ge)))\n#define GENTITY_FOR_SENTITY(se) (GENTITY_FOR_NUM(NUM_FOR_SENTITY(se)))\n\nstatic qboolean BoundsIntersect (vec3_t mins1, vec3_t maxs1, vec3_t mins2, vec3_t maxs2);\n\nstatic int SVQ3_AddBot(void);\nvoid SVQ3_CreateBaseline(void);\nvoid SVQ3_ClientThink(client_t *cl);\n#ifdef QWOVERQ3\nstatic void SVQ3Q1_ConvertEntStateQ1ToQ3(entity_state_t *q1, q3entityState_t *q3);\n#endif\n\nstatic const char *mapentspointer;\n\n#define\tQ3SOLID_BMODEL\t0xffffff\n\n#define PS_FOR_NUM(n) ((q3playerState_t *)((qbyte *)q3playerstates + sizeofGameClient*(n)))\n\n#define clamp(v,min,max) v = (v>max)?max:((v < min)?min:v)\n#define Q_rint(x) (int)((x > 0)?(x + 0.5f):(x-0.5f))\n\n\n\n// entity->svFlags\n// the server does not know how to interpret most of the values\n// in entityStates (level eType), so the game must explicitly flag\n// special server behaviors\n#define\tSVF_NOCLIENT\t\t\t0x00000001\t// don't send entity to clients, even if it has effects\n\n// TTimo\n// https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=551\n#define SVF_CLIENTMASK 0x00000002\n\n#define SVF_BOT\t\t\t\t\t0x00000008\t// set if the entity is a bot\n#define\tSVF_BROADCAST\t\t\t0x00000020\t// send to all connected clients\n#define\tSVF_PORTAL\t\t\t\t0x00000040\t// merge a second pvs at origin2 into snapshots\n#define\tSVF_USE_CURRENT_ORIGIN\t0x00000080\t// entity->r.currentOrigin instead of entity->s.origin\n\t\t\t\t\t\t\t\t\t\t\t// for link position (missiles and movers)\n#define SVF_SINGLECLIENT\t\t0x00000100\t// only send to a single client (entityShared_t->singleClient)\n#define SVF_NOSERVERINFO\t\t0x00000200\t// don't send CS_SERVERINFO updates to this client\n\t\t\t\t\t\t\t\t\t\t\t// so that it can be updated for ping tools without\n\t\t\t\t\t\t\t\t\t\t\t// lagging clients\n#define SVF_CAPSULE\t\t\t\t0x00000400\t// use capsule for collision detection instead of bbox\n#define SVF_NOTSINGLECLIENT\t\t0x00000800\t// send entity to everyone but one client\n\t\t\t\t\t\t\t\t\t\t\t// (entityShared_t->singleClient)\n\n\n\n\n//these entities are private to the engine. gamecode shall not see this.\ntypedef struct {\n\tareagridlink_t areas[16];\n\tsize_t areagridsequence;\n\n\tqboolean linked;\n\n\tpvscache_t pvscache;\n} q3serverEntity_t;\nstatic q3serverEntity_t *q3_sentities;\n\n// ClearLink is used for new headnodes\nstatic void W_ClearLink (link_t *l)\n{\n\tl->prev = l->next = l;\n}\n\nstatic void W_RemoveLink (link_t *l)\n{\n\tl->next->prev = l->prev;\n\tl->prev->next = l->next;\n}\n\nstatic void W_InsertLinkBefore (link_t *l, link_t *before)\n{\n\tl->next = before;\n\tl->prev = before->prev;\n\tl->prev->next = l;\n\tl->next->prev = l;\n}\n\nstatic void Q3G_UnlinkEntity(q3sharedEntity_t *ent)\n{\n\tint i;\n\tq3serverEntity_t *sent;\n\n\tent->r.linked = false;\n\n\tsent = SENTITY_FOR_GENTITY(ent);\n\n\tif(!sent->linked)\n\t{\n\t\treturn;\t\t// not linked in anywhere\n\t}\n\n\tfor (i = 0; i < countof(sent->areas); i++)\n\t{\n\t\tif (!sent->areas[i].ed)\n\t\t\tbreak;\n\t\tW_RemoveLink(&sent->areas[i].l);\n\t\tsent->areas[i].ed = NULL;\n\t}\n\n\tsent->linked = false;\n}\n#define CALCAREAGRIDBOUNDS(min,max) \\\n\t\tming[0] = floor(((min)[0]+sv3.gridbias[0]) / sv3.gridscale[0]);\t\\\n\t\tming[1] = floor(((min)[1]+sv3.gridbias[1]) / sv3.gridscale[1]);\t\\\n\t\tmaxg[0] = floor(((max)[0]+sv3.gridbias[0]) / sv3.gridscale[0]);\t\\\n\t\tmaxg[1] = floor(((max)[1]+sv3.gridbias[1]) / sv3.gridscale[1]);\t\\\n\t\tming[0] = bound(0, ming[0], sv3.gridsize[0]-1);\t\\\n\t\tming[1] = bound(0, ming[1], sv3.gridsize[1]-1);\t\\\n\t\tmaxg[0] = bound(ming[0], maxg[0], sv3.gridsize[0]-1)+1;\t\\\n\t\tmaxg[1] = bound(ming[1], maxg[1], sv3.gridsize[1]-1)+1;\n\n#define MAX_TOTAL_ENT_LEAFS\t\t256\n\nstatic model_t *Q3G_GetCModel(unsigned int modelindex)\n{\n\t//0 is world\n\t//1 == *1\n\t//this is not how quake's precaches normally work.\n\tmodelindex++;\n\tif ((unsigned int)modelindex < countof(sv3.models))\n\t{\n\t\tif (!sv3.models[modelindex])\n\t\t{\n\t\t\tif (modelindex == 1)\n\t\t\t\tsv3.models[modelindex] = sv3.world->worldmodel;\n\t\t\telse\n\t\t\t\tsv3.models[modelindex] = worldfuncs->LoadModel(worldfuncs->FixName(va(\"*%i\", modelindex-1), sv.modelname), MLV_WARNSYNC);\n\t\t}\n\n\t\tif (sv3.models[modelindex]->loadstate == MLS_LOADED)\n\t\t\treturn sv3.models[modelindex];\n\t}\n\treturn NULL;\n}\n\nstatic void Q3G_LinkEntity(q3sharedEntity_t *ent)\n{\n#ifdef USEAREAGRID\n\tint ming[2], maxg[2], g[2], ga;\n#else\n\tareanode_t\t*node;\n#endif\n\tq3serverEntity_t\t*sent;\n\tint\t\t\ti, j, k;\n\tconst float\t\t*origin;\n\tconst float\t\t*angles;\n\n\tsent = SENTITY_FOR_GENTITY(ent);\n\n\tif(sent->linked)\n\t\tQ3G_UnlinkEntity(ent);\t// unlink from old position\n\n\t// encode the size into the entity_state for client prediction\n\tif(ent->r.bmodel)\n\t\tent->s.solid = Q3SOLID_BMODEL;\n\telse if(ent->r.contents & (Q3CONTENTS_BODY|Q3CONTENTS_SOLID))\n\t{\n\t\t// assume that x/y are equal and symetric\n\t\ti = ent->r.maxs[0];\n\t\tclamp(i, 1, 255);\n\n\t\t// z is not symetric\n\t\tj = -ent->r.mins[2];\n\t\tclamp(j, 1, 255);\n\n\t\t// and z maxs can be negative...\n\t\tk = ent->r.maxs[2]+32;\n\t\tclamp(k, 1, 255);\n\n\t\tent->s.solid = (((k << 8) | j) << 8) | i;\n\t}\n\telse\n\t\tent->s.solid = 0;\n\n\t// always use currentOrigin\n\torigin = ent->r.currentOrigin;\n\tangles = ent->r.currentAngles;\n\n\t// set the abs box\n\tif(ent->r.bmodel && (angles[0] || angles[1] || angles[2]))\n\t{\n\t\t// expand for rotation\n\t\tfloat\t\tmax, v;\n\t\tint\t\t\ti;\n\n\t\tmax = 0;\n\t\tfor(i=0; i<3; i++)\n\t\t{\n\t\t\tv = fabs(ent->r.mins[i]);\n\t\t\tif(v > max)\n\t\t\t\tmax = v;\n\t\t\tv = fabs(ent->r.maxs[i]);\n\t\t\tif(v > max)\n\t\t\t\tmax = v;\n\t\t}\n\t\tfor(i=0; i<3; i++)\n\t\t{\n\t\t\tent->r.absmin[i] = origin[i] - max;\n\t\t\tent->r.absmax[i] = origin[i] + max;\n\t\t}\n\t}\n\telse\n\t{\n\t\t// normal\n\t\tVectorAdd(origin, ent->r.mins, ent->r.absmin);\n\t\tVectorAdd(origin, ent->r.maxs, ent->r.absmax);\n\t}\n\n\t// because movement is clipped an epsilon away from an actual edge,\n\t// we must fully check even when bounding boxes don't quite touch\n\tent->r.absmin[0] -= 1;\n\tent->r.absmin[1] -= 1;\n\tent->r.absmin[2] -= 1;\n\tent->r.absmax[0] += 1;\n\tent->r.absmax[1] += 1;\n\tent->r.absmax[2] += 1;\n\n// link to PVS leafs\n\tsv3.world->worldmodel->funcs.FindTouchedLeafs(sv3.world->worldmodel, &sent->pvscache, ent->r.absmin, ent->r.absmax);\n\t//FIXME: return if no leafs\n\n\tent->r.linkcount++;\n\tent->r.linked = true;\n\tsent->linked = true;\n\n// find the first node that the ent's box crosses\n\tCALCAREAGRIDBOUNDS(ent->r.absmin, ent->r.absmax);\n\tif ((maxg[0]-ming[0])*(maxg[1]-ming[1]) > countof(sent->areas))\n\t{\t//entity is too large to fit in our grid. shove it in the overflow\n\t\tsent->areas[0].ed = sent;\n\t\tW_InsertLinkBefore (&sent->areas[0].l, &sv3.jumboarea.l);\n\t}\n\telse\n\t{\n\t\tfor (ga = 0, g[0] = ming[0]; g[0] < maxg[0]; g[0]++)\n\t\t\tfor (    g[1] = ming[1]; g[1] < maxg[1]; g[1]++, ga++)\n\t\t\t{\n\t\t\t\tsent->areas[ga].ed = sent;\n\t\t\t\tW_InsertLinkBefore (&sent->areas[ga].l, &sv3.gridareas[g[0] + g[1]*sv3.gridsize[0]].l);\n\t\t\t}\n\t}\n}\n\nstatic void SVQ3_ClearWorld(void)\n{\n\tint numareas = 1;\n\tint i;\n\tvec3_t mins, maxs, size;\n\tif (sv3.world->worldmodel)\n\t{\n\t\tVectorCopy(sv3.world->worldmodel->mins, mins);\n\t\tVectorCopy(sv3.world->worldmodel->maxs, maxs);\n\t}\n\telse\n\t{\n\t\tVectorSet(mins, -4096, -4096, -4096);\n\t\tVectorSet(maxs, 4096, 4096, 4096);\n\t}\n\tVector2Set(sv3.gridsize, 128, 128);\n\tfor (i = 0; i < 2; i++)\n\t{\n\t\tsize[i] = maxs[i] - mins[i];\n\t\tsize[i] /= sv3.gridsize[i];\n\t//enforce a minimum grid size, so things don't end up getting added to every single node\n\t\tif (size[i] < 128)\n\t\t{\n\t\t\tmins[i] -= (128-size[i])/2 * sv3.gridsize[i];\n\t\t\tsize[i] = 128;\n\t\t}\n\t\tsv3.gridscale[i] = size[i];\n\t\tsv3.gridbias[i] = -mins[i];\n\n\t\tnumareas *= sv3.gridsize[i];\n\t}\n\n\tif (sv3.gridareas)\n\t\tmemset (sv3.gridareas, 0, sizeof(*sv3.gridareas)*numareas);\n\telse\n\t\tsv3.gridareas = Z_Malloc(sizeof(*sv3.gridareas)*numareas);\n\n\tfor (i = 0; i < numareas; i++)\n\t\tW_ClearLink (&sv3.gridareas[i].l);\n\tW_ClearLink (&sv3.jumboarea.l);\n}\n\nstatic int SVQ3_EntitiesInBoxNode(areagridlink_t *node, vec3_t mins, vec3_t maxs, int *list, int maxcount)\n{\n\tlink_t\t\t*l, *next;\n\tq3serverEntity_t\t\t*sent;\n\tq3sharedEntity_t\t\t*gent;\n\n\tint linkcount = 0;\n\n\t//work out who they are first.\n\tfor (l = node->l.next ; l != &node->l ; l = next)\n\t{\n\t\tif (maxcount == linkcount)\n\t\t\treturn linkcount;\n\n\t\tnext = l->next;\n\t\tsent = ((areagridlink_t*)l)->ed;\n\t\tif (sent->areagridsequence != sv3.areagridsequence)\n\t\t{\n\t\t\tsent->areagridsequence = sv3.areagridsequence;\n\t\t\tgent = GENTITY_FOR_SENTITY(sent);\n\n\t\t\tif (!BoundsIntersect(mins, maxs, gent->r.absmin, gent->r.absmax))\n\t\t\t\tcontinue;\n\n\t\t\tlist[linkcount++] = NUM_FOR_GENTITY(gent);\n\t\t}\n\t}\n\n\treturn linkcount;\n}\nstatic int SVQ3_EntitiesInBox(vec3_t mins, vec3_t maxs, int *list, int maxcount)\n{\n\tint ming[2], maxg[2], g[2], ga;\n\tint linkcount = 0;\n\tsv3.areagridsequence++;\n\tlinkcount += SVQ3_EntitiesInBoxNode(&sv3.jumboarea, mins, maxs, list+linkcount, maxcount-linkcount);\n\tCALCAREAGRIDBOUNDS(mins, maxs);\n\tfor (ga = 0, g[0] = ming[0]; g[0] < maxg[0]; g[0]++)\n\t\tfor (    g[1] = ming[1]; g[1] < maxg[1]; g[1]++, ga++)\n\t\t\tlinkcount += SVQ3_EntitiesInBoxNode(&sv3.gridareas[g[0] + g[1]*sv3.gridsize[0]], mins, maxs, list+linkcount, maxcount-linkcount);\n\treturn linkcount;\n}\n\n#define\tENTITYNUM_NONE\t\t(MAX_GENTITIES-1)\n#define\tENTITYNUM_WORLD\t\t(MAX_GENTITIES-2)\nstatic void SVQ3_Trace(q3trace_t *result, vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, int entnum, int contentmask, qboolean capsule)\n{\n\tint contactlist[128];\n\ttrace_t tr;\n\tvec3_t mmins, mmaxs;\n\tint i;\n\tq3sharedEntity_t *es;\n\tmodel_t *mod;\n\tint ourowner;\n\n\tif (!mins)\n\t\tmins = vec3_origin;\n\tif (!maxs)\n\t\tmaxs = vec3_origin;\n\n\tsv3.world->worldmodel->funcs.NativeTrace(sv3.world->worldmodel, 0, NULLFRAMESTATE, NULL, start, end, mins, maxs, capsule, contentmask, &tr);\n\tresult->allsolid = tr.allsolid;\n\tresult->contents = tr.contents;\n\tVectorCopy(tr.endpos, result->endpos);\n\tresult->entityNum = (tr.fraction==1.0)?ENTITYNUM_NONE:ENTITYNUM_WORLD;\n\tresult->fraction = tr.fraction;\n\tresult->plane = tr.plane;\n\tresult->startsolid = tr.startsolid;\n\tif (tr.surface)\n\t\tresult->surfaceFlags = tr.surface->flags;\n\telse\n\t\tresult->surfaceFlags = 0;\n\n\tif (result->allsolid)\n\t\treturn;\n\n\tfor (i = 0; i < 3; i++)\n\t{\n\t\tif (start[i] < end[i])\n\t\t{\n\t\t\tmmins[i] = start[i]+mins[i]-1;\n\t\t\tmmaxs[i] = end[i]+maxs[i]+1;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tmmins[i] = end[i]+mins[i]-1;\n\t\t\tmmaxs[i] = start[i]+maxs[i]+1;\n\t\t}\n\t}\n\n\tif (entnum == -1)\n\t\tourowner = -1;\n\telse if (entnum != ENTITYNUM_WORLD)\n\t{\n\t\tourowner = GENTITY_FOR_NUM(entnum)->r.ownerNum;\n\t\tif (ourowner == ENTITYNUM_NONE)\n\t\t\tourowner = -1;\n\t}\n\telse\n\t\tourowner = -1;\n\n\tfor (i = SVQ3_EntitiesInBox(mmins, mmaxs, contactlist, sizeof(contactlist)/sizeof(contactlist[0]))-1; i >= 0; i--)\n\t{\n\t\tif (contactlist[i] == entnum)\n\t\t\tcontinue;\t//don't collide with self.\n\n\t\tes = GENTITY_FOR_NUM(contactlist[i]);\n\t\tif (!(es->r.contents & contentmask))\n\t\t\tcontinue;\n\n\t\tif (entnum != ENTITYNUM_WORLD)\n\t\t{\n\t\t\tif (contactlist[i] == entnum)\n\t\t\t\tcontinue;\n\t\t\tif (es->r.ownerNum == entnum)\n\t\t\t\tcontinue;\n\t\t\tif (es->r.ownerNum == ourowner)\n\t\t\t\tcontinue;\n\t\t}\n\n\t\tif (es->r.bmodel)\n\t\t{\n\t\t\tmod = Q3G_GetCModel(es->s.modelindex);\n\t\t\tif (!mod)\n\t\t\t\tcontinue;\n\t\t\tworldfuncs->TransformedTrace(mod, 0, 0, start, end, mins, maxs, capsule, &tr, es->r.currentOrigin, es->r.currentAngles, contentmask);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (es->r.svFlags & SVF_CAPSULE)\n\t\t\t\tmod = worldfuncs->TempBoxModel(es->r.mins, es->r.maxs);\n\t\t\telse\n\t\t\t\tmod = worldfuncs->TempBoxModel(es->r.mins, es->r.maxs);\n\t\t\tworldfuncs->TransformedTrace(mod, 0, 0, start, end, mins, maxs, capsule, &tr, es->r.currentOrigin, vec3_origin, contentmask);\n\t\t}\n\t\tif (tr.allsolid)\n\t\t\ttr.fraction = 0;\n\n/*\t\tif (tr.fraction < result->fraction)\n\t\t{\n\t\t\tresult->allsolid |= tr.allsolid;\n\t\t\tresult->contents = tr.contents;\n\t\t\tVectorCopy(tr.endpos, result->endpos);\n\t\t\tresult->entityNum = contactlist[i];\n\t\t\tresult->fraction = tr.fraction;\n\t\t\tresult->plane = tr.plane;\n\t\t\tresult->startsolid |= tr.startsolid;\n//\t\t\tif (tr.surface)\n//\t\t\t\tresult->surfaceFlags = tr.surface->flags;\n//\t\t\telse\n\t\t\t\tresult->surfaceFlags = 0;\n\t\t}\n\t\telse if (tr.startsolid)\n\t\t{\n\t\t\tresult->entityNum = contactlist[i];\n\t\t\tresult->startsolid = true;\n\t\t\tif (tr.allsolid)\n\t\t\t\tresult->allsolid = true;\n\t\t}\n*/\n\n\t\tif ( tr.allsolid || tr.fraction < result->fraction ) {\n\t\t\tresult->allsolid |= tr.allsolid;\n\t\t\tresult->contents = tr.contents;\n\t\t\tVectorCopy(tr.endpos, result->endpos);\n\t\t\tresult->entityNum = contactlist[i];\n\t\t\tresult->fraction = tr.fraction;\n\t\t\tresult->plane = tr.plane;\n\t\t\tresult->startsolid |= tr.startsolid;\n//\t\t\tif (tr.surface)\n//\t\t\t\tresult->surfaceFlags = tr.surface->flags;\n//\t\t\telse\n\t\t\t\tresult->surfaceFlags = 0;\n\n\t\t} else if ( tr.startsolid ) {\n\t\t\tresult->startsolid = qtrue;\n\t\t}\n\t\tif ( result->allsolid ) {\n\t\t\treturn;\n\t\t}\n\t}\n}\n\nstatic int SVQ3_PointContents(vec3_t pos, int entnum)\n{\n\tint contactlist[128];\n\ttrace_t tr;\n\tint i;\n\tq3sharedEntity_t *es;\n\tmodel_t *mod;\n\tint ourowner;\n\n\tint cont;\n\n\tcont = sv3.world->worldmodel->funcs.NativeContents (sv3.world->worldmodel, 0, 0, NULL, pos, vec3_origin, vec3_origin);\n\n\tif ((unsigned)entnum >= MAX_GENTITIES)\n\t\tourowner = -1;\n\telse if ( entnum != ENTITYNUM_WORLD )\n\t{\n\t\tourowner = GENTITY_FOR_NUM(entnum)->r.ownerNum;\n\t\tif (ourowner == ENTITYNUM_WORLD)\n\t\t\tourowner = -1;\n\t}\n\telse\n\t\tourowner = -1;\n\n\tfor (i = SVQ3_EntitiesInBox(pos, pos, contactlist, sizeof(contactlist)/sizeof(contactlist[0]))-1; i >= 0; i--)\n\t{\n\t\tif (contactlist[i] == entnum)\n\t\t\tcontinue;\t//don't collide with self.\n\n\t\tes = GENTITY_FOR_NUM(contactlist[i]);\n\n\t\tif (entnum != ENTITYNUM_WORLD)\n\t\t{\n\t\t\tif (contactlist[i] == entnum)\n\t\t\t\tcontinue;\t// don't clip against the pass entity\n\t\t\tif (es->r.ownerNum == entnum)\n\t\t\t\tcontinue;\t// don't clip against own missiles\n\t\t\tif (es->r.ownerNum == ourowner)\n\t\t\t\tcontinue;\t// don't clip against other missiles from our owner\n\t\t}\n\n\t\tif (es->r.bmodel)\n\t\t{\n\t\t\tmod = Q3G_GetCModel(es->s.modelindex);\n\t\t\tif (!mod)\n\t\t\t\tcontinue;\n\t\t\tworldfuncs->TransformedTrace(mod, 0, NULL, pos, pos, vec3_origin, vec3_origin, false, &tr, es->r.currentOrigin, es->r.currentAngles, 0xffffffff);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tmod = worldfuncs->TempBoxModel(es->r.mins, es->r.maxs);\n\t\t\tworldfuncs->TransformedTrace(mod, 0, NULL, pos, pos, vec3_origin, vec3_origin, false, &tr, es->r.currentOrigin, vec3_origin, 0xffffffff);\n\t\t}\n\n\t\tcont |= tr.contents;\n\t}\n\treturn cont;\n}\n\nstatic int SVQ3_Contact(vec3_t mins, vec3_t maxs, q3sharedEntity_t *ent, qboolean capsule)\n{\n\tmodel_t *mod;\n\ttrace_t tr;\n\tfloat *ang;\n\n\tif (ent->r.bmodel)\n\t{\n\t\tang = ent->r.currentAngles;\n\t\tmod = Q3G_GetCModel(ent->s.modelindex);\n\t}\n\telse\n\t{\n\t\tang = vec3_origin;\n\t\tmod = worldfuncs->TempBoxModel(ent->r.mins, ent->r.maxs);\n\t}\n\n\tif (!mod || !mod->funcs.NativeTrace)\n\t\treturn false;\n\n\tworldfuncs->TransformedTrace(mod, 0, 0, vec3_origin, vec3_origin, mins, maxs, capsule, &tr, ent->r.currentOrigin, ang, 0xffffffff);\n\n\tif (tr.startsolid)\n\t\treturn true;\n\treturn false;\n}\n\nstatic void SVQ3_SetBrushModel(q3sharedEntity_t *ent, char *modelname)\n{\n\tint modelindex;\n\tmodel_t *mod;\n\tif (!modelname || *modelname != '*')\n\t\tplugfuncs->EndGame(\"SVQ3_SetBrushModel: not an inline model\");\n\tmodelindex = atoi(modelname+1);\n\tmod = Q3G_GetCModel(modelindex);\n\tif (mod)\n\t{\n\t\tVectorCopy(mod->mins, ent->r.mins);\n\t\tVectorCopy(mod->maxs, ent->r.maxs);\n\t}\n\telse\n\t{\n\t\tVectorCopy(vec3_origin, ent->r.mins);\n\t\tVectorCopy(vec3_origin, ent->r.maxs);\n\t}\n\tent->r.bmodel = true;\n\tent->r.contents = -1;\n\tent->s.modelindex = modelindex;\n\n\tQ3G_LinkEntity( ent );\n}\n\nstatic qboolean BoundsIntersect (vec3_t mins1, vec3_t maxs1, vec3_t mins2, vec3_t maxs2)\n{\n\treturn (mins1[0] <= maxs2[0] && mins1[1] <= maxs2[1] && mins1[2] <= maxs2[2] &&\n\t\t maxs1[0] >= mins2[0] && maxs1[1] >= mins2[1] && maxs1[2] >= mins2[2]);\n}\n\nstatic qboolean SVQ3_GetUserCmd(int clientnumber, q3usercmd_t *ucmd)\n{\n\tusercmd_t *cmd;\n\n\tif (clientnumber < 0 || clientnumber >= sv.allocated_client_slots)\n\t\tplugfuncs->EndGame(\"SVQ3_GetUserCmd: Client out of range\");\n\n\tcmd = &svs.clients[clientnumber].lastcmd;\n\tucmd->angles[0] = cmd->angles[0];\n\tucmd->angles[1] = cmd->angles[1];\n\tucmd->angles[2] = cmd->angles[2];\n\tucmd->serverTime = cmd->servertime;\n\tucmd->forwardmove = (signed char)cmd->forwardmove;\n\tucmd->rightmove = cmd->sidemove;\n\tucmd->upmove = cmd->upmove;\n\tucmd->buttons = cmd->buttons;\n\tucmd->weapon = cmd->weapon;\n\n\n\treturn true;\n}\n\nvoid SVQ3_SendServerCommand(client_t *cl, char *str)\n{\n\tif (!cl)\n\t{\t//broadcast\n\t\tint i;\n\t\tfor (i = 0; i < sv.allocated_client_slots; i++)\n\t\t{\n\t\t\tif (svs.clients[i].state>=cs_connected)\n\t\t\t{\n\t\t\t\tSVQ3_SendServerCommand(&svs.clients[i], str);\t//go for consistancy.\n\t\t\t}\n\t\t}\n\t\treturn;\n\t}\n\n\tcl->server_command_sequence++;\n\tif (cl->protocol!=SCP_BAD && cl->server_command_sequence-cl->server_command_ack >= countof(cl->server_commands))\n\t\tCon_Printf(\"%s: Reliable overflow %i-%i>%i\\n\", cl->name, cl->server_command_sequence,cl->server_command_ack, (int)countof(cl->server_commands));\n\tQ_strncpyz(cl->server_commands[cl->server_command_sequence & Q3TEXTCMD_MASK], str, sizeof(cl->server_commands[0]));\n}\n\nvoid SVQ3_SendConfigString(client_t *dest, int num, char *string)\n{\n\tint len = strlen(string);\n#define CONFIGSTRING_MAXCHUNK (1024-24)\n\tif (len > CONFIGSTRING_MAXCHUNK)\n\t{\n\t\tchar *cmd;\n\t\tchar buf[CONFIGSTRING_MAXCHUNK+1];\n\t\tint off = 0;\n\t\tfor (;;)\n\t\t{\n\t\t\tint chunk = len - off;\n\t\t\tif (chunk > CONFIGSTRING_MAXCHUNK)\n\t\t\t\tchunk = CONFIGSTRING_MAXCHUNK;\n\t\t\t//split it up into multiple commands.\n\t\t\tif (!off)\n\t\t\t\tcmd = \"bcs0\";\t//initial chunk\n\t\t\telse if (off + chunk == len)\n\t\t\t\tcmd = \"bcs2\";\t//terminator\n\t\t\telse\n\t\t\t\tcmd = \"bcs1\";\t//mid chunk\n\t\t\tmemcpy(buf, string+off, chunk);\n\t\t\tbuf[chunk] = 0;\n\t\t\tSVQ3_SendServerCommand(dest, va(\"%s %i \\\"%s\\\"\\n\", cmd, num, buf));\n\t\t\toff += chunk;\n\t\t\tif (off == len)\n\t\t\t\tbreak;\n\t\t}\n\t}\n\telse\n\t\tSVQ3_SendServerCommand(dest, va(\"cs %i \\\"%s\\\"\\n\", num, string));\n}\n\nvoid SVQ3_SetConfigString(int num, char *string)\n{\n\tint len;\n\tif (num < 0 || num >= MAX_Q3_CONFIGSTRINGS)\n\t\treturn;\t//no exploits please\n\n\tif (!string)\n\t\tstring = \"\";\n\tif (!strcmp(svq3_configstrings[num]?svq3_configstrings[num]:\"\", string))\n\t\treturn;\n\tlen = strlen(string);\n\tif (svq3_configstrings[num])\n\t\tZ_Free(svq3_configstrings[num]);\n\tsvq3_configstrings[num] = Z_Malloc(len+1);\n\tstrcpy(svq3_configstrings[num], string);\n\n\tif (sv.state == ss_loading && !sv3.restarting)\n\t\treturn;\t//don't spam these, the svcq3_gamestate will have a copy anyway and the gamecode can get confused.\n\tSVQ3_SendConfigString(NULL, num, string);\n}\n\nstatic int FloatAsInt(float f)\n{\n\treturn *(int*)&f;\n}\n\nstatic int SVQ3_BotGetConsoleMessage( int client, char *buf, int size )\n{\n\t//retrieves server->client commands that were sent to a bot\n\tclient_t\t*cl;\n\tint\t\t\tindex;\n\n\tif ((unsigned)client >= sv.allocated_client_slots)\n\t\treturn false;\n\n\tcl = &svs.clients[client];\n//\tcl->lastPacketTime = svs.time;\n\n\tif (cl->server_command_ack == cl->server_command_sequence)\n\t\treturn false;\n\n\tcl->server_command_ack++;\n\tindex = cl->server_command_ack & Q3TEXTCMD_MASK;\n\n\tif ( !cl->server_commands[index][0] )\n\t\treturn false;\n\n\tQ_strncpyz( buf, cl->server_commands[index], size );\n\treturn true;\n}\nstatic int SVQ3_BotGetSnapshotEntity(int client, int entnum)\n{\n\t//fixme: does the bot actually use this?...\n\treturn -1;\n}\n\nstatic void SVQ3_Adjust_Area_Portal_State(q3sharedEntity_t *ge, qboolean open)\n{\n\tq3serverEntity_t *se = SENTITY_FOR_GENTITY(ge);\n\tif (se->pvscache.areanum == -1 || se->pvscache.areanum2 == -1) //not linked properly.\n\t\treturn;\n\tsv3.world->worldmodel->funcs.SetAreaPortalState(sv3.world->worldmodel, -1, se->pvscache.areanum, se->pvscache.areanum2, open);\n}\n\nstatic qboolean SV_InPVS(vec3_t p1, vec3_t p2)\n{\n\tmodel_t *worldmodel = sv3.world->worldmodel;\n\n\tif (!worldmodel || worldmodel->loadstate != MLS_LOADED)\n\t\treturn false;\t//still loading, don't give bad results.\n\telse if (!worldmodel->funcs.FatPVS)\n\t\treturn true;\t//no pvs info, assume everything is visible\n\telse\n\t{\n\t\tint a1, c1 = worldmodel->funcs.ClusterForPoint(worldmodel, p1, &a1);\n\t\tint a2, c2 = worldmodel->funcs.ClusterForPoint(worldmodel, p2, &a2);\n\t\tqbyte *pvs;\n\t\tif (c1 < 0 || c2 < 0)\n\t\t\treturn (c1<0);\t//outside can see in, inside cannot (normally) see out.\n\t\tpvs = worldmodel->funcs.ClusterPVS(worldmodel, c1, NULL, PVM_FAST);\n\t\tif (pvs[c2>>3] & (1<<(c2&7)))\n\t\t{\n\t\t\tif (worldmodel->funcs.AreasConnected(worldmodel, a1, a2))\n\t\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n}\n\n#define VALIDATEPOINTER(o,l) if ((int)o + l >= mask || VM_POINTER(o) < offset) plugfuncs->EndGame(\"Call to game trap %u passes invalid pointer\\n\", (unsigned int)fn);\t//out of bounds.\nstatic qintptr_t Q3G_SystemCalls(void *offset, quintptr_t mask, qintptr_t fn, const qintptr_t *arg)\n{\n\tint ret = 0;\n\tswitch((q3ggameImport_t)fn)\n\t{\n\tcase G_PRINT:\t\t// ( const char *string );\n\t\tCon_Printf(\"%s\", (char*)VM_POINTER(arg[0]));\n\t\tbreak;\n\tcase G_ERROR:\t\t// ( const char *string );\n\t\tplugfuncs->EndGame(\"Q3 Game error: %s\", (char*)VM_POINTER(arg[0]));\n\t\tbreak;\n\tcase G_MILLISECONDS:\n\t\treturn plugfuncs->GetMilliseconds();\n\n\tcase G_CVAR_REGISTER:// ( vmCvar_t *vmCvar, const char *varName, const char *defaultValue, int flags );\n\t\tif (arg[0])\n\t\t\tVALIDATEPOINTER(arg[0], sizeof(q3vmcvar_t));\n\t\treturn VMQ3_Cvar_Register(VM_POINTER(arg[0]), VM_POINTER(arg[1]), VM_POINTER(arg[2]), VM_LONG(arg[3]));\n\tcase G_CVAR_UPDATE:// ( vmCvar_t *vmCvar );\n\t\tVALIDATEPOINTER(arg[0], sizeof(q3vmcvar_t));\n\t\treturn VMQ3_Cvar_Update(VM_POINTER(arg[0]));\n\n\tcase G_CVAR_SET:// ( const char *var_name, const char *value );\n\t\tcvarfuncs->SetString(VM_POINTER(arg[0]), VM_POINTER(arg[1]));\t//set it\n\t\tbreak;\n\tcase G_CVAR_VARIABLE_INTEGER_VALUE:// ( const char *var_name );\n\t\t{\n\t\t\tcvar_t *var;\n\t\t\tvar = cvarfuncs->GetNVFDG(VM_POINTER(arg[0]), \"0\", 0, NULL, \"Q3-Game-Code created\");\n\t\t\tif (var)\n\t\t\t\treturn var->ival;\n\t\t}\n\t\tbreak;\n\tcase G_CVAR_VARIABLE_STRING_BUFFER:// ( const char *var_name, char *buffer, int bufsize );\n\t\t{\n\t\t\tcvar_t *var = cvarfuncs->GetNVFDG(VM_POINTER(arg[0]), NULL, 0, NULL, \"Q3-Game-Code created\");\n\t\t\tif (!VM_LONG(arg[2]))\n\t\t\t\treturn 0;\n\t\t\telse if (!var)\n\t\t\t{\n\t\t\t\tVALIDATEPOINTER(arg[1], 1);\n\t\t\t\t*(char *)VM_POINTER(arg[1]) = '\\0';\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tVALIDATEPOINTER(arg[1], arg[2]);\n\t\t\t\tQ_strncpyz(VM_POINTER(arg[1]), var->string, VM_LONG(arg[2]));\n\t\t\t}\n\t\t}\n\t\tbreak;\n\n\tcase G_BOT_FREE_CLIENT:\n\tcase G_DROP_CLIENT:\n\t\tif ((unsigned)VM_LONG(arg[0]) < sv.allocated_client_slots)\n\t\t\tworldfuncs->DropClient(&svs.clients[VM_LONG(arg[0])]);\n\t\tbreak;\n\n\tcase G_BOT_ALLOCATE_CLIENT:\n\t\treturn SVQ3_AddBot();\n\n\tcase G_ARGC:\t\t//8\n\t\treturn cmdfuncs->Argc();\n\tcase G_ARGV:\t\t\t\t//9\n\t\tVALIDATEPOINTER(arg[1], arg[2]);\n\t\tcmdfuncs->Argv(VM_LONG(arg[0]), VM_POINTER(arg[1]), VM_LONG(arg[2]));\n\t\tbreak;\n\n\tcase G_SEND_CONSOLE_COMMAND:\n\t\tcmdfuncs->AddText(VM_POINTER(arg[1]), false);\n\t\treturn 0;\n\n\n\tcase G_FS_FOPEN_FILE: //fopen\n\t\tif ((int)arg[1] + 4 >= mask || VM_POINTER(arg[1]) < offset)\n\t\t\tbreak;\t//out of bounds.\n\t\tret = VM_fopen(VM_POINTER(arg[0]), VM_POINTER(arg[1]), VM_LONG(arg[2]), 0);\n\t\tbreak;\n\n\tcase G_FS_READ:\t//fread\n\t\tif ((int)arg[0] + VM_LONG(arg[1]) >= mask || VM_POINTER(arg[0]) < offset)\n\t\t\tbreak;\t//out of bounds.\n\n\t\tret = VM_FRead(VM_POINTER(arg[0]), VM_LONG(arg[1]), VM_LONG(arg[2]), 0);\n\t\tbreak;\n\tcase G_FS_WRITE:\t//fwrite\n\t\tret = VM_FWrite(VM_POINTER(arg[0]), VM_LONG(arg[1]), VM_LONG(arg[2]), 0);\n\t\tbreak;\n\tcase G_FS_SEEK:\n\t\treturn VM_FSeek(arg[0], arg[1], arg[2], 0);\n\tcase G_FS_FCLOSE_FILE:\t//fclose\n\t\tVM_fclose(VM_LONG(arg[0]), 0);\n\t\tbreak;\n\n\tcase G_FS_GETFILELIST:\t//fs listing\n\t\tif ((int)arg[2] + arg[3] >= mask || VM_POINTER(arg[2]) < offset)\n\t\t\tbreak;\t//out of bounds.\n\t\treturn VM_GetFileList(VM_POINTER(arg[0]), VM_POINTER(arg[1]), VM_POINTER(arg[2]), VM_LONG(arg[3]));\n\n\tcase G_LOCATE_GAME_DATA:\t\t// ( gentity_t *gEnts, int numGEntities, int sizeofGEntity_t,\t15\n\t//\t\t\t\t\t\t\tplayerState_t *clients, int sizeofGameClient );\n\t\tif (VM_OOB(arg[0], arg[1]*arg[2]) || VM_OOB(arg[3], arg[4]*MAX_CLIENTS))\n\t\t\tplugfuncs->EndGame(\"Gamedata is out of bounds\\n\");\n\t\tq3_entarray = VM_POINTER(arg[0]);\n\t\tnumq3entities = VM_LONG(arg[1]);\n\t\tsizeofq3gentity = VM_LONG(arg[2]);\n\t\tq3playerstates = VM_POINTER(arg[3]);\n\t\tsizeofGameClient = VM_LONG(arg[4]);\n\n\t\tif (numq3entities > MAX_GENTITIES)\n\t\t\tplugfuncs->EndGame(\"Gamecode specifies too many entities\");\n\t\tbreak;\n\n\tcase G_SEND_SERVER_COMMAND:\t\t// ( int clientNum, const char *fmt, ... );\t\t\t\t\t\t17\n\t\tCon_DPrintf(\"Game dispatching %s\\n\", (char*)VM_POINTER(arg[1]));\n\t\tif (VM_LONG(arg[0]) == -1)\n\t\t{\t//broadcast\n\t\t\tSVQ3_SendServerCommand(NULL, VM_POINTER(arg[1]));\n\t\t}\n\t\telse\n\t\t{\n\t\t\tint i = VM_LONG(arg[0]);\n\t\t\tif (i < 0 || i >= sv.allocated_client_slots)\n\t\t\t\treturn false;\n\t\t\tSVQ3_SendServerCommand(&svs.clients[i], VM_POINTER(arg[1]));\n\t\t}\n\t\tbreak;\n\n\tcase G_SET_CONFIGSTRING:\t// ( int num, const char *string );\t\t\t\t\t\t\t\t\t18\n\t\tSVQ3_SetConfigString(arg[0], VM_POINTER(arg[1]));\n\t\tbreak;\n\tcase G_GET_CONFIGSTRING:\t// ( int num, char *buffer, int bufferSize );\t\t\t\t\t\t19\n\t\tif (arg[0] < 0 || arg[0] >= MAX_Q3_CONFIGSTRINGS || !arg[2])\n\t\t\treturn 0;\n\t\tVALIDATEPOINTER(arg[1], arg[2]);\n\t\tif (svq3_configstrings[arg[0]])\n\t\t\tQ_strncpyz(VM_POINTER(arg[1]), svq3_configstrings[arg[0]], arg[2]);\n\t\telse\n\t\t\t*(char*)VM_POINTER(arg[1]) = '\\0';\n\t\tbreak;\n\n\tcase G_GET_SERVERINFO:\n\t\t{\n\t\t\tchar *dest = VM_POINTER(arg[0]);\n\t\t\tint length = VM_LONG(arg[1]);\n\t\t\tif (VM_OOB(arg[1], arg[2]))\n\t\t\t\treturn 0;\n\t\t\tworldfuncs->IBufToInfo(&svs.info, dest, length, NULL, NULL, NULL, NULL, NULL);\n\t\t}\n\t\treturn true;\n\tcase G_GET_USERINFO://int num, char *buffer, int bufferSize\t\t\t\t\t\t\t\t\t\t20\n\t\tif (VM_OOB(arg[1], arg[2]))\n\t\t\treturn 0;\n\t\tif ((unsigned)VM_LONG(arg[0]) >= sv.allocated_client_slots)\n\t\t\treturn 0;\n\t\tworldfuncs->IBufToInfo(&svs.clients[VM_LONG(arg[0])].userinfo, VM_POINTER(arg[1]), VM_LONG(arg[2]), NULL, NULL, NULL, NULL, NULL);\n\t\tbreak;\n\n\tcase G_SET_USERINFO://int num, char *buffer\t\t\t\t\t\t\t\t\t\t20\n\t\tif (VM_OOB(arg[1], 1))\n\t\t\treturn 0;\n\t\tworldfuncs->IBufFromInfo(&svs.clients[VM_LONG(arg[0])].userinfo, VM_POINTER(arg[1]), false);\n\t\tworldfuncs->ExtractFromUserinfo(&svs.clients[VM_LONG(arg[0])], false);\n\t\tbreak;\n\n\tcase G_LINKENTITY:\t\t// ( gentity_t *ent );\t\t\t\t\t\t\t\t\t\t\t\t\t30\n\t\tQ3G_LinkEntity(VM_POINTER(arg[0]));\n\t\tbreak;\n\tcase G_UNLINKENTITY:\t\t// ( gentity_t *ent );\t\t\t\t\t\t\t\t\t\t\t\t31\n\t\tQ3G_UnlinkEntity(VM_POINTER(arg[0]));\n\t\tbreak;\n\tcase G_TRACE:\t// ( trace_t *results, const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int passEntityNum, int contentmask );\n\t\tVALIDATEPOINTER(arg[0], sizeof(q3trace_t));\n\t\tSVQ3_Trace(VM_POINTER(arg[0]), VM_POINTER(arg[1]), VM_POINTER(arg[2]), VM_POINTER(arg[3]), VM_POINTER(arg[4]), VM_LONG(arg[5]), VM_LONG(arg[6]), false);\n\t\tbreak;\n\tcase G_TRACECAPSULE:\t// ( trace_t *results, const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int passEntityNum, int contentmask );\n\t\tVALIDATEPOINTER(arg[0], sizeof(q3trace_t));\n\t\tSVQ3_Trace(VM_POINTER(arg[0]), VM_POINTER(arg[1]), VM_POINTER(arg[2]), VM_POINTER(arg[3]), VM_POINTER(arg[4]), VM_LONG(arg[5]), VM_LONG(arg[6]), true);\n\t\tbreak;\n\tcase G_ENTITY_CONTACT:\n\t\t\t// ( const vec3_t mins, const vec3_t maxs, const gentity_t *ent );\t33\n\t// perform an exact check against inline brush models of non-square shape\n\t\treturn SVQ3_Contact(VM_POINTER(arg[0]), VM_POINTER(arg[1]), VM_POINTER(arg[2]), false);\n\tcase G_ENTITY_CONTACTCAPSULE:\n\t\treturn SVQ3_Contact(VM_POINTER(arg[0]), VM_POINTER(arg[1]), VM_POINTER(arg[2]), true);\n\n\tcase G_ENTITIES_IN_BOX:\t// ( const vec3_t mins, const vec3_t maxs, gentity_t **list, int maxcount );\t32\n\t// EntitiesInBox will return brush models based on their bounding box,\n\t// so exact determination must still be done with EntityContact\n\t\tVALIDATEPOINTER(arg[2], sizeof(int)*VM_LONG(arg[3]));\n\t\treturn SVQ3_EntitiesInBox(VM_POINTER(arg[0]), VM_POINTER(arg[1]), VM_POINTER(arg[2]), VM_LONG(arg[3]));\n\n\tcase G_ADJUST_AREA_PORTAL_STATE:\n\t\tSVQ3_Adjust_Area_Portal_State(VM_POINTER(arg[0]), arg[1]);\n\t\tbreak;\n\tcase G_POINT_CONTENTS:\n\t\treturn SVQ3_PointContents(VM_POINTER(arg[0]), -1);\n\tcase G_SET_BRUSH_MODEL:\t//ent, name\n\t\tVALIDATEPOINTER(arg[0], sizeof(q3sharedEntity_t));\n\t\tSVQ3_SetBrushModel(VM_POINTER(arg[0]), VM_POINTER(arg[1]));\n\t\tbreak;\n\tcase G_GET_USERCMD:\t\t// ( int clientNum, usercmd_t *cmd )\t\t\t\t\t\t\t\t\t36\n\t\tVALIDATEPOINTER(arg[1], sizeof(q3usercmd_t));\n\t\tSVQ3_GetUserCmd(VM_LONG(arg[0]), VM_POINTER(arg[1]));\n\t\tbreak;\n\tcase G_GET_ENTITY_TOKEN:\t// qboolean ( char *buffer, int bufferSize )\t\t\t\t\t\t37\n\t\tmapentspointer = cmdfuncs->ParseToken(mapentspointer, VM_POINTER(arg[0]), arg[1], NULL);\n\t\treturn !!mapentspointer;\n\n\tcase G_REAL_TIME:\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t//\t41\n\t\tVALIDATEPOINTER(arg[0], sizeof(q3time_t));\n\t\treturn Q3VM_GetRealtime(VM_POINTER(arg[0]));\n\tcase G_SNAPVECTOR:\n\t\t{\n\t\t\tfloat *fp = (float *)VM_POINTER( arg[0] );\n\t\t\tVALIDATEPOINTER(arg[0], sizeof(vec3_t));\n\t\t\tfp[0] = Q_rint(fp[0]);\n\t\t\tfp[1] = Q_rint(fp[1]);\n\t\t\tfp[2] = Q_rint(fp[2]);\n\t\t}\n\t\tbreak;\n\t// standard Q3\n\tcase G_MEMSET:\n\t\tVALIDATEPOINTER(arg[0], arg[2]);\n\t\t{\n\t\t\tvoid *dst = VM_POINTER(arg[0]);\n\t\t\tmemset(dst, arg[1], arg[2]);\n\t\t}\n\t\tbreak;\n\tcase G_MEMCPY:\n\t\tVALIDATEPOINTER(arg[0], arg[2]);\n\t\t{\n\t\t\tvoid *dst = VM_POINTER(arg[0]);\n\t\t\tvoid *src = VM_POINTER(arg[1]);\n\t\t\tmemmove(dst, src, arg[2]);\n\t\t}\n\t\tbreak;\n\tcase G_STRNCPY:\n\t\tVALIDATEPOINTER(arg[0], arg[2]);\n\t\t{\n\t\t\tvoid *dst = VM_POINTER(arg[0]);\n\t\t\tvoid *src = VM_POINTER(arg[1]);\n\t\t\tQ_strncpyS(dst, src, arg[2]);\n\t\t}\n\t\tbreak;\n\tcase G_SIN:\n\t\tVM_FLOAT(ret)=(float)sin(VM_FLOAT(arg[0]));\n\t\tbreak;\n\tcase G_COS:\n\t\tVM_FLOAT(ret)=(float)cos(VM_FLOAT(arg[0]));\n\t\tbreak;\n//\tcase G_ACOS:\n//\t\tVM_FLOAT(ret)=(float)acos(VM_FLOAT(arg[0]));\n//\t\tbreak;\n\tcase G_ATAN2:\n\t\tVM_FLOAT(ret)=(float)atan2(VM_FLOAT(arg[0]), VM_FLOAT(arg[1]));\n\t\tbreak;\n\tcase G_SQRT:\n\t\tVM_FLOAT(ret)=(float)sqrt(VM_FLOAT(arg[0]));\n\t\tbreak;\n\tcase G_FLOOR:\n\t\tVM_FLOAT(ret)=(float)floor(VM_FLOAT(arg[0]));\n\t\tbreak;\n\tcase G_CEIL:\n\t\tVM_FLOAT(ret)=(float)ceil(VM_FLOAT(arg[0]));\n\t\tbreak;\n\n#ifdef USEBOTLIB\n\tcase BOTLIB_SETUP:\n\t\treturn botlib->BotLibSetup();\n\tcase BOTLIB_SHUTDOWN:\n\t\treturn botlib->BotLibShutdown();\n\tcase BOTLIB_LIBVAR_SET:\n\t\treturn botlib->BotLibVarSet(VM_POINTER(arg[0]), VM_POINTER(arg[1]));\n\tcase BOTLIB_LIBVAR_GET:\n\t\tVALIDATEPOINTER(arg[1], arg[2]);\n\t\treturn botlib->BotLibVarGet(VM_POINTER(arg[0]), VM_POINTER(arg[1]), VM_LONG(arg[2]));\n\tcase BOTLIB_PC_ADD_GLOBAL_DEFINE:\n\t\treturn botlib->PC_AddGlobalDefine(VM_POINTER(arg[0]));\n\tcase BOTLIB_START_FRAME:\n\t\treturn botlib->BotLibStartFrame(VM_FLOAT(arg[0]));\n\tcase BOTLIB_LOAD_MAP:\n\t\treturn botlib->BotLibLoadMap(VM_POINTER(arg[0]));\n\n\tcase BOTLIB_UPDATENTITY:\n\t\treturn botlib->BotLibUpdateEntity(VM_LONG(arg[0]), VM_POINTER(arg[1]));\n\tcase BOTLIB_TEST:\n\t\treturn botlib->Test(VM_LONG(arg[0]), VM_POINTER(arg[1]), VM_POINTER(arg[2]), VM_POINTER(arg[3]));\n\tcase BOTLIB_GET_SNAPSHOT_ENTITY:\n\t\treturn SVQ3_BotGetSnapshotEntity(VM_LONG(arg[0]), VM_LONG(arg[1]));\n\tcase BOTLIB_GET_CONSOLE_MESSAGE:\n\t\tVALIDATEPOINTER(arg[1], arg[2]);\n\t\treturn SVQ3_BotGetConsoleMessage(arg[0], VM_POINTER(arg[1]), VM_LONG(arg[2]));\n\tcase BOTLIB_USER_COMMAND:\n\t\t{\n\t\t\tq3usercmd_t *uc = VM_POINTER(arg[1]);\n\t\t\tint i = VM_LONG(arg[0]);\n\t\t\tif ((unsigned)i >= sv.allocated_client_slots)\n\t\t\t\treturn 1;\n\t\t\tsvs.clients[i].lastcmd.angles[0] = uc->angles[0];\n\t\t\tsvs.clients[i].lastcmd.angles[1] = uc->angles[1];\n\t\t\tsvs.clients[i].lastcmd.angles[2] = uc->angles[2];\n\t\t\tsvs.clients[i].lastcmd.upmove = uc->upmove;\n\t\t\tsvs.clients[i].lastcmd.sidemove = uc->rightmove;\n\t\t\tsvs.clients[i].lastcmd.forwardmove = uc->forwardmove;\n\t\t\tsvs.clients[i].lastcmd.servertime = uc->serverTime;\n\t\t\tsvs.clients[i].lastcmd.weapon = uc->weapon;\n\t\t\tsvs.clients[i].lastcmd.buttons = uc->buttons;\n\t\t\tSVQ3_ClientThink(&svs.clients[i]);\n\t\t}\n\t\treturn 0;\n\n\tcase BOTLIB_AAS_ENABLE_ROUTING_AREA:\n\t\treturn botlib->aas.AAS_EnableRoutingArea(VM_LONG(arg[0]), VM_LONG(arg[1]));\n\tcase BOTLIB_AAS_BBOX_AREAS:\n\t\t//FIXME: validatepointer arg2\n\t\treturn botlib->aas.AAS_BBoxAreas(VM_POINTER(arg[0]), VM_POINTER(arg[1]), VM_POINTER(arg[2]), VM_LONG(arg[3]));\n\tcase BOTLIB_AAS_AREA_INFO:\n\t\treturn botlib->aas.AAS_AreaInfo(VM_LONG(arg[0]), VM_POINTER(arg[1]));\n\tcase BOTLIB_AAS_ENTITY_INFO:\n\t\tbotlib->aas.AAS_EntityInfo(VM_LONG(arg[0]), VM_POINTER(arg[1]));\n\t\treturn 0;\n\n\tcase BOTLIB_AAS_INITIALIZED:\n\t\treturn botlib->aas.AAS_Initialized();\n\tcase BOTLIB_AAS_PRESENCE_TYPE_BOUNDING_BOX:\n\t\tbotlib->aas.AAS_PresenceTypeBoundingBox(VM_LONG(arg[0]), VM_POINTER(arg[1]), VM_POINTER(arg[2]));\n\t\treturn 0;\n\tcase BOTLIB_AAS_TIME:\n\t\treturn FloatAsInt(botlib->aas.AAS_Time());\n\n\tcase BOTLIB_AAS_POINT_AREA_NUM:\n\t\treturn botlib->aas.AAS_PointAreaNum(VM_POINTER(arg[0]));\n\tcase BOTLIB_AAS_TRACE_AREAS:\n\t\treturn botlib->aas.AAS_TraceAreas(VM_POINTER(arg[0]), VM_POINTER(arg[1]), VM_POINTER(arg[2]), VM_POINTER(arg[3]), VM_LONG(arg[4]));\n\n\tcase BOTLIB_AAS_POINT_CONTENTS:\n\t\treturn botlib->aas.AAS_PointContents(VM_POINTER(arg[0]));\n\tcase BOTLIB_AAS_NEXT_BSP_ENTITY:\n\t\treturn botlib->aas.AAS_NextBSPEntity(VM_LONG(arg[0]));\n\tcase BOTLIB_AAS_VALUE_FOR_BSP_EPAIR_KEY:\n\t\tVALIDATEPOINTER(arg[2], arg[3]);\n\t\treturn botlib->aas.AAS_ValueForBSPEpairKey(VM_LONG(arg[0]), VM_POINTER(arg[1]), VM_POINTER(arg[2]), VM_LONG(arg[3]));\n\tcase BOTLIB_AAS_VECTOR_FOR_BSP_EPAIR_KEY:\n\t\tVALIDATEPOINTER(arg[2], sizeof(vec3_t));\n\t\treturn botlib->aas.AAS_VectorForBSPEpairKey(VM_LONG(arg[0]), VM_POINTER(arg[1]), VM_POINTER(arg[2]));\n\tcase BOTLIB_AAS_FLOAT_FOR_BSP_EPAIR_KEY:\n\t\tVALIDATEPOINTER(arg[2], sizeof(float));\n\t\treturn botlib->aas.AAS_FloatForBSPEpairKey(VM_LONG(arg[0]), VM_POINTER(arg[1]), VM_POINTER(arg[2]));\n\tcase BOTLIB_AAS_INT_FOR_BSP_EPAIR_KEY:\n\t\tVALIDATEPOINTER(arg[2], sizeof(int));\n\t\treturn botlib->aas.AAS_IntForBSPEpairKey(VM_LONG(arg[0]), VM_POINTER(arg[1]), VM_POINTER(arg[2]));\n\n\tcase BOTLIB_AAS_AREA_REACHABILITY:\n\t\treturn botlib->aas.AAS_AreaReachability(VM_LONG(arg[0]));\n\n\tcase BOTLIB_AAS_AREA_TRAVEL_TIME_TO_GOAL_AREA:\n\t\treturn botlib->aas.AAS_AreaTravelTimeToGoalArea(VM_LONG(arg[0]), VM_POINTER(arg[1]), VM_LONG(arg[2]), VM_LONG(arg[3]));\n\n\tcase BOTLIB_AAS_SWIMMING:\n\t\treturn botlib->aas.AAS_Swimming(VM_POINTER(arg[0]));\n\tcase BOTLIB_AAS_PREDICT_CLIENT_MOVEMENT:\n\t\treturn botlib->aas.AAS_PredictClientMovement(VM_POINTER(arg[0]),\n\t\t\t\t\t\t\t\t\t\t\tVM_LONG(arg[1]), VM_POINTER(arg[2]),\n\t\t\t\t\t\t\t\t\t\t\tVM_LONG(arg[3]), VM_LONG(arg[4]),\n\t\t\t\t\t\t\t\t\t\t\tVM_POINTER(arg[5]), VM_POINTER(arg[6]),\n\t\t\t\t\t\t\t\t\t\t\tVM_LONG(arg[7]),\n\t\t\t\t\t\t\t\t\t\t\tVM_LONG(arg[8]), VM_FLOAT(arg[9]),\n\t\t\t\t\t\t\t\t\t\t\tVM_LONG(arg[10]), VM_LONG(arg[11]), VM_LONG(arg[12]));\n\n\tcase BOTLIB_EA_SAY:\n\t\tbotlib->ea.EA_Say(VM_LONG(arg[0]), VM_POINTER(arg[1]));\n\t\treturn 0;\n\tcase BOTLIB_EA_SAY_TEAM:\n\t\tbotlib->ea.EA_SayTeam(VM_LONG(arg[0]), VM_POINTER(arg[1]));\n\t\treturn 0;\n\tcase BOTLIB_EA_COMMAND:\n\t\tbotlib->ea.EA_Command(VM_LONG(arg[0]), VM_POINTER(arg[1]));\n\t\treturn 0;\n\n\tcase BOTLIB_EA_ACTION:\n\t\tbotlib->ea.EA_Action(VM_LONG(arg[0]), VM_LONG(arg[1]));\n\t\treturn 0;\n\tcase BOTLIB_EA_GESTURE:\n\t\tbotlib->ea.EA_Gesture(VM_LONG(arg[0]));\n\t\treturn 0;\n\tcase BOTLIB_EA_TALK:\n\t\tbotlib->ea.EA_Talk(VM_LONG(arg[0]));\n\t\treturn 0;\n\tcase BOTLIB_EA_ATTACK:\n\t\tbotlib->ea.EA_Attack(VM_LONG(arg[0]));\n\t\treturn 0;\n\tcase BOTLIB_EA_USE:\n\t\tbotlib->ea.EA_Use(VM_LONG(arg[0]));\n\t\treturn 0;\n\tcase BOTLIB_EA_RESPAWN:\n\t\tbotlib->ea.EA_Respawn(VM_LONG(arg[0]));\n\t\treturn 0;\n\tcase BOTLIB_EA_CROUCH:\n\t\tbotlib->ea.EA_Crouch(VM_LONG(arg[0]));\n\t\treturn 0;\n\tcase BOTLIB_EA_MOVE_UP:\n\t\tbotlib->ea.EA_MoveUp(VM_LONG(arg[0]));\n\t\treturn 0;\n\tcase BOTLIB_EA_MOVE_DOWN:\n\t\tbotlib->ea.EA_MoveDown(VM_LONG(arg[0]));\n\t\treturn 0;\n\tcase BOTLIB_EA_MOVE_FORWARD:\n\t\tbotlib->ea.EA_MoveForward(VM_LONG(arg[0]));\n\t\treturn 0;\n\tcase BOTLIB_EA_MOVE_BACK:\n\t\tbotlib->ea.EA_MoveBack(VM_LONG(arg[0]));\n\t\treturn 0;\n\tcase BOTLIB_EA_MOVE_LEFT:\n\t\tbotlib->ea.EA_MoveLeft(VM_LONG(arg[0]));\n\t\treturn 0;\n\tcase BOTLIB_EA_MOVE_RIGHT:\n\t\tbotlib->ea.EA_MoveRight(VM_LONG(arg[0]));\n\t\treturn 0;\n\n\tcase BOTLIB_EA_SELECT_WEAPON:\n\t\tbotlib->ea.EA_SelectWeapon(VM_LONG(arg[0]), VM_LONG(arg[1]));\n\t\treturn 0;\n\tcase BOTLIB_EA_JUMP:\n\t\tbotlib->ea.EA_Jump(VM_LONG(arg[0]));\n\t\treturn 0;\n\tcase BOTLIB_EA_DELAYED_JUMP:\n\t\tbotlib->ea.EA_DelayedJump(VM_LONG(arg[0]));\n\t\treturn 0;\n\tcase BOTLIB_EA_MOVE:\n\t\tbotlib->ea.EA_Move(VM_LONG(arg[0]), VM_POINTER(arg[1]), VM_FLOAT(arg[2]));\n\t\treturn 0;\n\tcase BOTLIB_EA_VIEW:\n\t\tbotlib->ea.EA_View(VM_LONG(arg[0]), VM_POINTER(arg[1]));\n\t\treturn 0;\n\n\tcase BOTLIB_EA_END_REGULAR:\n\t\tbotlib->ea.EA_EndRegular(VM_LONG(arg[0]), VM_FLOAT(arg[1]));\n\t\treturn 0;\n\tcase BOTLIB_EA_GET_INPUT:\n\t\tbotlib->ea.EA_GetInput(VM_LONG(arg[0]), VM_FLOAT(arg[1]), VM_POINTER(arg[2]));\n\t\treturn 0;\n\tcase BOTLIB_EA_RESET_INPUT:\n\t\tbotlib->ea.EA_ResetInput(VM_LONG(arg[0]));\n\t\treturn 0;\n\n\n\tcase BOTLIB_AI_LOAD_CHARACTER:\n\t\treturn botlib->ai.BotLoadCharacter(VM_POINTER(arg[0]), VM_FLOAT(arg[1]));\n\tcase BOTLIB_AI_FREE_CHARACTER:\n\t\tbotlib->ai.BotFreeCharacter(arg[0]);\n\t\treturn 0;\n\tcase BOTLIB_AI_CHARACTERISTIC_FLOAT:\n\t\treturn FloatAsInt(botlib->ai.Characteristic_Float(arg[0], arg[1]));\n\tcase BOTLIB_AI_CHARACTERISTIC_BFLOAT:\n\t\treturn FloatAsInt(botlib->ai.Characteristic_BFloat(arg[0], arg[1], VM_FLOAT(arg[2]), VM_FLOAT(arg[3])));\n\tcase BOTLIB_AI_CHARACTERISTIC_INTEGER:\n\t\treturn botlib->ai.Characteristic_Integer(arg[0], arg[1]);\n\tcase BOTLIB_AI_CHARACTERISTIC_BINTEGER:\n\t\treturn botlib->ai.Characteristic_BInteger(arg[0], arg[1], arg[2], arg[3]);\n\tcase BOTLIB_AI_CHARACTERISTIC_STRING:\n\t\tVALIDATEPOINTER(arg[2], arg[3]);\n\t\tbotlib->ai.Characteristic_String(arg[0], arg[1], VM_POINTER(arg[2]), arg[3]);\n\t\treturn 0;\n\n\tcase BOTLIB_AI_ALLOC_CHAT_STATE:\n\t\treturn botlib->ai.BotAllocChatState();\n\tcase BOTLIB_AI_FREE_CHAT_STATE:\n\t\tbotlib->ai.BotFreeChatState(arg[0]);\n\t\treturn 0;\n\n\tcase BOTLIB_AI_QUEUE_CONSOLE_MESSAGE:\n\t\tbotlib->ai.BotQueueConsoleMessage(arg[0], arg[1], VM_POINTER(arg[2]));\n\t\treturn 0;\n\tcase BOTLIB_AI_REMOVE_CONSOLE_MESSAGE:\n\t\tbotlib->ai.BotRemoveConsoleMessage(arg[0], arg[1]);\n\t\treturn 0;\n\tcase BOTLIB_AI_NEXT_CONSOLE_MESSAGE:\n\t\treturn botlib->ai.BotNextConsoleMessage(arg[0], VM_POINTER(arg[1]));\n\tcase BOTLIB_AI_NUM_CONSOLE_MESSAGE:\n\t\treturn botlib->ai.BotNumConsoleMessages(arg[0]);\n\tcase BOTLIB_AI_INITIAL_CHAT:\n\t\tbotlib->ai.BotInitialChat(arg[0], VM_POINTER(arg[1]), arg[2], VM_POINTER(arg[3]), VM_POINTER(arg[4]), VM_POINTER(arg[5]), VM_POINTER(arg[6]), VM_POINTER(arg[7]), VM_POINTER(arg[8]), VM_POINTER(arg[9]), VM_POINTER(arg[10]));\n\t\treturn 0;\n\tcase BOTLIB_AI_REPLY_CHAT:\n\t\treturn botlib->ai.BotReplyChat(arg[0], VM_POINTER(arg[1]), arg[2], arg[3], VM_POINTER(arg[4]), VM_POINTER(arg[5]), VM_POINTER(arg[6]), VM_POINTER(arg[7]), VM_POINTER(arg[8]), VM_POINTER(arg[9]), VM_POINTER(arg[10]), VM_POINTER(arg[11]));\n\tcase BOTLIB_AI_CHAT_LENGTH:\n\t\treturn botlib->ai.BotChatLength(arg[0]);\n\tcase BOTLIB_AI_ENTER_CHAT:\n\t\tbotlib->ai.BotEnterChat(arg[0], arg[1], arg[2]);\n\t\treturn 0;\n\tcase BOTLIB_AI_STRING_CONTAINS:\n\t\treturn botlib->ai.StringContains(VM_POINTER(arg[0]), VM_POINTER(arg[1]), arg[2]);\n\tcase BOTLIB_AI_FIND_MATCH:\n\t\treturn botlib->ai.BotFindMatch(VM_POINTER(arg[0]), VM_POINTER(arg[1]), arg[2]);\n\tcase BOTLIB_AI_MATCH_VARIABLE:\n\t\tbotlib->ai.BotMatchVariable(VM_POINTER(arg[0]), arg[1], VM_POINTER(arg[2]), arg[3]);\n\t\treturn 0;\n\tcase BOTLIB_AI_UNIFY_WHITE_SPACES:\n\t\tbotlib->ai.UnifyWhiteSpaces(VM_POINTER(arg[0]));\n\t\treturn 0;\n\tcase BOTLIB_AI_REPLACE_SYNONYMS:\n\t\tbotlib->ai.BotReplaceSynonyms(VM_POINTER(arg[0]), arg[1]);\n\t\treturn 0;\n\tcase BOTLIB_AI_LOAD_CHAT_FILE:\n\t\treturn botlib->ai.BotLoadChatFile(arg[0], VM_POINTER(arg[1]), VM_POINTER(arg[2]));\n\tcase BOTLIB_AI_SET_CHAT_GENDER:\n\t\tbotlib->ai.BotSetChatGender(arg[0], arg[1]);\n\t\treturn 0;\n\tcase BOTLIB_AI_SET_CHAT_NAME:\n\t\tbotlib->ai.BotSetChatName(arg[0], VM_POINTER(arg[1]),\n\t\t\targ[2]\n\t\t\t);\n\t\treturn 0;\n\n\n\n\tcase BOTLIB_AI_RESET_GOAL_STATE:\n\t\tbotlib->ai.BotResetGoalState(VM_LONG(arg[0]));\n\t\treturn 0;\n\tcase BOTLIB_AI_RESET_AVOID_GOALS:\n\t\tbotlib->ai.BotResetAvoidGoals(VM_LONG(arg[0]));\n\t\treturn 0;\n\tcase BOTLIB_AI_PUSH_GOAL:\n\t\tbotlib->ai.BotPushGoal(VM_LONG(arg[0]), VM_POINTER(arg[1]));\n\t\treturn 0;\n\tcase BOTLIB_AI_POP_GOAL:\n\t\tbotlib->ai.BotPopGoal(VM_LONG(arg[0]));\n\t\treturn 0;\n\n\tcase BOTLIB_AI_EMPTY_GOAL_STACK:\n\t\tbotlib->ai.BotEmptyGoalStack(VM_LONG(arg[0]));\n\t\treturn 0;\n\tcase BOTLIB_AI_DUMP_AVOID_GOALS:\n\t\tbotlib->ai.BotDumpAvoidGoals(VM_LONG(arg[0]));\n\t\treturn 0;\n\tcase BOTLIB_AI_DUMP_GOAL_STACK:\n\t\tbotlib->ai.BotDumpGoalStack(VM_LONG(arg[0]));\n\t\treturn 0;\n\n\tcase BOTLIB_AI_GOAL_NAME:\n\t\tVALIDATEPOINTER(arg[1], arg[2]);\n\t\tbotlib->ai.BotGoalName(VM_LONG(arg[0]), VM_POINTER(arg[1]), VM_LONG(arg[2]));\n\t\treturn 0;\n\n\tcase BOTLIB_AI_GET_TOP_GOAL:\n\t\t//FIXME: validatepointer ?\n\t\treturn botlib->ai.BotGetTopGoal(VM_LONG(arg[0]), VM_POINTER(arg[1]));\n\tcase BOTLIB_AI_GET_SECOND_GOAL:\n\t\t//FIXME: validatepointer ?\n\t\treturn botlib->ai.BotGetSecondGoal(VM_LONG(arg[0]), VM_POINTER(arg[1]));\n\tcase BOTLIB_AI_CHOOSE_LTG_ITEM:\n\t\t//FIXME: validatepointer ?\n\t\treturn botlib->ai.BotChooseLTGItem(VM_LONG(arg[0]), VM_POINTER(arg[1]), VM_POINTER(arg[2]), VM_LONG(arg[3]));\n\tcase BOTLIB_AI_CHOOSE_NBG_ITEM:\n\t\t//FIXME: validatepointer ?\n\t\treturn botlib->ai.BotChooseNBGItem(VM_LONG(arg[0]), VM_POINTER(arg[1]), VM_POINTER(arg[2]), VM_LONG(arg[3]), VM_POINTER(arg[4]), VM_FLOAT(arg[5]));\n\tcase BOTLIB_AI_TOUCHING_GOAL:\n\t\treturn botlib->ai.BotTouchingGoal(VM_POINTER(arg[0]), VM_POINTER(arg[1]));\n\tcase BOTLIB_AI_ITEM_GOAL_IN_VIS_BUT_NOT_VISIBLE:\n\t\treturn botlib->ai.BotItemGoalInVisButNotVisible(arg[0], VM_POINTER(arg[1]), VM_POINTER(arg[2]), VM_POINTER(arg[3]));\n\n\tcase BOTLIB_AI_GET_LEVEL_ITEM_GOAL:\n\t\treturn botlib->ai.BotGetLevelItemGoal(arg[0], VM_POINTER(arg[1]), VM_POINTER(arg[2]));\n\tcase BOTLIB_AI_AVOID_GOAL_TIME:\n\t\treturn botlib->ai.BotAvoidGoalTime(arg[0], arg[1]);\n\tcase BOTLIB_AI_INIT_LEVEL_ITEMS:\n\t\tbotlib->ai.BotInitLevelItems();\n\t\treturn 0;\n\n\tcase BOTLIB_AI_UPDATE_ENTITY_ITEMS:\n\t\tbotlib->ai.BotUpdateEntityItems();\n\t\treturn 0;\n\n\tcase BOTLIB_AI_LOAD_ITEM_WEIGHTS:\n\t\treturn botlib->ai.BotLoadItemWeights(arg[0], VM_POINTER(arg[1]));\n\n\tcase BOTLIB_AI_FREE_ITEM_WEIGHTS:\n\t\tbotlib->ai.BotFreeItemWeights(arg[0]);\n\t\treturn 0;\n\n\tcase BOTLIB_AI_SAVE_GOAL_FUZZY_LOGIC:\n\t\tbotlib->ai.BotSaveGoalFuzzyLogic(arg[0], VM_POINTER(arg[1]));\n\t\treturn 0;\n\tcase BOTLIB_AI_ALLOC_GOAL_STATE:\n\t\treturn botlib->ai.BotAllocGoalState(VM_LONG(arg[0]));\n\tcase BOTLIB_AI_FREE_GOAL_STATE:\n\t\tbotlib->ai.BotFreeGoalState(VM_LONG(arg[0]));\n\t\treturn 0;\n\n\tcase BOTLIB_AI_RESET_MOVE_STATE:\n\t\tbotlib->ai.BotResetMoveState(VM_LONG(arg[0]));\n\t\treturn 0;\n\tcase BOTLIB_AI_MOVE_TO_GOAL:\n\t\tbotlib->ai.BotMoveToGoal(VM_POINTER(arg[0]), VM_LONG(arg[1]), VM_POINTER(arg[2]), VM_LONG(arg[3]));\n\t\treturn 0;\n\tcase BOTLIB_AI_MOVE_IN_DIRECTION:\n\t\treturn botlib->ai.BotMoveInDirection(VM_LONG(arg[0]), VM_POINTER(arg[1]), VM_FLOAT(arg[2]), VM_LONG(arg[3]));\n\n\tcase BOTLIB_AI_RESET_AVOID_REACH:\n\t\tbotlib->ai.BotUpdateEntityItems();\n\t\treturn 0;\n\n\tcase BOTLIB_AI_RESET_LAST_AVOID_REACH:\n\t\tbotlib->ai.BotResetLastAvoidReach(arg[0]);\n\t\treturn 0;\n\tcase BOTLIB_AI_REACHABILITY_AREA:\n\t\treturn botlib->ai.BotReachabilityArea(VM_POINTER(arg[0]), arg[1]);\n\n\tcase BOTLIB_AI_MOVEMENT_VIEW_TARGET:\n\t\treturn botlib->ai.BotMovementViewTarget(arg[0], VM_POINTER(arg[1]), arg[2], VM_FLOAT(arg[3]), VM_POINTER(arg[4]));\n\n\tcase BOTLIB_AI_ALLOC_MOVE_STATE:\n\t\treturn botlib->ai.BotAllocMoveState();\n\tcase BOTLIB_AI_FREE_MOVE_STATE:\n\t\tbotlib->ai.BotFreeMoveState(VM_LONG(arg[0]));\n\t\treturn 0;\n\tcase BOTLIB_AI_INIT_MOVE_STATE:\n\t\t//FIXME: validatepointer?\n\t\tbotlib->ai.BotInitMoveState(VM_LONG(arg[0]), VM_POINTER(arg[1]));\n\t\treturn 0;\n\n\tcase BOTLIB_AI_CHOOSE_BEST_FIGHT_WEAPON:\n\t\treturn botlib->ai.BotChooseBestFightWeapon(VM_LONG(arg[0]), VM_POINTER(arg[1]));\n\tcase BOTLIB_AI_GET_WEAPON_INFO:\n\t\tbotlib->ai.BotGetWeaponInfo(VM_LONG(arg[0]), VM_LONG(arg[1]), VM_POINTER(arg[2]));\n\t\treturn 0;\n\tcase BOTLIB_AI_LOAD_WEAPON_WEIGHTS:\n\t\treturn botlib->ai.BotLoadWeaponWeights(VM_LONG(arg[0]), VM_POINTER(arg[1]));\n\tcase BOTLIB_AI_ALLOC_WEAPON_STATE:\n\t\treturn botlib->ai.BotAllocWeaponState();\n\tcase BOTLIB_AI_FREE_WEAPON_STATE:\n\t\tbotlib->ai.BotFreeWeaponState(VM_LONG(arg[0]));\n\t\treturn 0;\n\tcase BOTLIB_AI_RESET_WEAPON_STATE:\n\t\tbotlib->ai.BotResetWeaponState(VM_LONG(arg[0]));\n\t\treturn 0;\n\n\tcase BOTLIB_AI_GENETIC_PARENTS_AND_CHILD_SELECTION:\n\t\treturn botlib->ai.GeneticParentsAndChildSelection(VM_LONG(arg[0]), VM_POINTER(arg[1]), VM_POINTER(arg[2]), VM_POINTER(arg[3]), VM_POINTER(arg[4]));\n\tcase BOTLIB_AI_INTERBREED_GOAL_FUZZY_LOGIC:\n\t\tbotlib->ai.BotInterbreedGoalFuzzyLogic(VM_LONG(arg[0]), VM_LONG(arg[1]), VM_LONG(arg[2]));\n\t\treturn 0;\n\tcase BOTLIB_AI_MUTATE_GOAL_FUZZY_LOGIC:\n\t\tbotlib->ai.BotMutateGoalFuzzyLogic(VM_LONG(arg[0]), VM_FLOAT(arg[1]));\n\t\treturn 0;\n\tcase BOTLIB_AI_GET_NEXT_CAMP_SPOT_GOAL:\n\t\treturn botlib->ai.BotGetNextCampSpotGoal(VM_LONG(arg[0]), VM_POINTER(arg[1]));\n\tcase BOTLIB_AI_GET_MAP_LOCATION_GOAL:\n\t\treturn botlib->ai.BotGetMapLocationGoal(VM_POINTER(arg[0]), VM_POINTER(arg[1]));\n\tcase BOTLIB_AI_NUM_INITIAL_CHATS:\n\t\treturn botlib->ai.BotNumInitialChats(VM_LONG(arg[0]), VM_POINTER(arg[1]));\n\tcase BOTLIB_AI_GET_CHAT_MESSAGE:\n\t\tVALIDATEPOINTER(arg[1], arg[2]);\n\t\tbotlib->ai.BotGetChatMessage(VM_LONG(arg[0]), VM_POINTER(arg[1]), VM_LONG(arg[2]));\n\t\treturn 0;\n\tcase BOTLIB_AI_REMOVE_FROM_AVOID_GOALS:\n\t\tbotlib->ai.BotRemoveFromAvoidGoals(VM_LONG(arg[0]), VM_LONG(arg[1]));\n\t\treturn 0;\n\tcase BOTLIB_AI_PREDICT_VISIBLE_POSITION:\n\t\treturn botlib->ai.BotPredictVisiblePosition(VM_POINTER(arg[0]), VM_LONG(arg[1]), VM_POINTER(arg[2]), VM_LONG(arg[3]), VM_POINTER(arg[4]));\n\n\tcase BOTLIB_AI_SET_AVOID_GOAL_TIME:\n\t\tbotlib->ai.BotSetAvoidGoalTime(VM_LONG(arg[0]), VM_LONG(arg[1]), VM_FLOAT(arg[2]));\n\t\treturn 0;\n\n\tcase BOTLIB_AI_ADD_AVOID_SPOT:\n\t\tbotlib->ai.BotAddAvoidSpot(VM_LONG(arg[0]), VM_POINTER(arg[1]), VM_FLOAT(arg[2]), VM_LONG(arg[3]));\n\t\treturn 0;\n\n\tcase BOTLIB_AAS_ALTERNATIVE_ROUTE_GOAL:\n\t\treturn botlib->aas.AAS_AlternativeRouteGoals(VM_POINTER(arg[0]), arg[1], VM_POINTER(arg[2]), arg[3], arg[4],\n\t\t\t\t\t\t\tVM_POINTER(arg[5]), arg[6], arg[7]);\n\tcase BOTLIB_AAS_PREDICT_ROUTE:\n\t\treturn botlib->aas.AAS_PredictRoute(VM_POINTER(arg[0]), arg[1], VM_POINTER(arg[2]), arg[3], arg[4], arg[5], arg[6],\n\t\t\t\t\t\t\targ[7], arg[8], arg[9], arg[10]);\n\tcase BOTLIB_AAS_POINT_REACHABILITY_AREA_INDEX:\n\t\treturn botlib->aas.AAS_PointReachabilityAreaIndex(VM_POINTER(arg[0]));\n\n  \tcase BOTLIB_PC_LOAD_SOURCE:\n\t\tif (!botlib)\n\t\t{\n\t\t\tplugfuncs->EndGame(\"Botlib is not installed (trap BOTLIB_PC_LOAD_SOURCE)\\n\");\n\t\t}\n\t\treturn botlib->PC_LoadSourceHandle(VM_POINTER(arg[0]));\n\tcase BOTLIB_PC_FREE_SOURCE:\n\t\treturn botlib->PC_FreeSourceHandle(VM_LONG(arg[0]));\n\tcase BOTLIB_PC_READ_TOKEN:\n\t\t//fixme: validatepointer\n\t\treturn botlib->PC_ReadTokenHandle(VM_LONG(arg[0]), VM_POINTER(arg[1]));\n\tcase BOTLIB_PC_SOURCE_FILE_AND_LINE:\n\t\t//fixme: validatepointer\n\t\treturn botlib->PC_SourceFileAndLine(VM_LONG(arg[0]), VM_POINTER(arg[1]), VM_POINTER(arg[2]));\n\n#endif\n\n\n\tcase G_IN_PVS:\n\t\treturn SV_InPVS(VM_POINTER(arg[0]), VM_POINTER(arg[1]));\n\tcase G_AREAS_CONNECTED:\t\t\tCon_Printf(\"Q3Game: builtin %s is not implemented\\n\", \"G_AREAS_CONNECTED\");\t\t\treturn ret;\n\tcase G_DEBUG_POLYGON_CREATE:\tCon_Printf(\"Q3Game: builtin %s is not implemented\\n\", \"G_DEBUG_POLYGON_CREATE\");\treturn ret;\n\tcase G_DEBUG_POLYGON_DELETE:\tCon_Printf(\"Q3Game: builtin %s is not implemented\\n\", \"G_DEBUG_POLYGON_DELETE\");\treturn ret;\n\tcase G_IN_PVS_IGNORE_PORTALS:\tCon_Printf(\"Q3Game: builtin %s is not implemented\\n\", \"G_IN_PVS_IGNORE_PORTALS\");\treturn ret;\n\tcase G_MATRIXMULTIPLY:\t\t\tCon_Printf(\"Q3Game: builtin %s is not implemented\\n\", \"G_MATRIXMULTIPLY\");\t\t\treturn ret;\n\tcase G_ANGLEVECTORS:\n\t\tVALIDATEPOINTER(arg[1], sizeof(vec3_t));\n\t\tVALIDATEPOINTER(arg[2], sizeof(vec3_t));\n\t\tVALIDATEPOINTER(arg[3], sizeof(vec3_t));\n\t\tAngleVectors(VM_POINTER(arg[0]), VM_POINTER(arg[1]), VM_POINTER(arg[2]), VM_POINTER(arg[3]));\n\t\tbreak;\n\tcase G_PERPENDICULARVECTOR:\t\tCon_Printf(\"Q3Game: builtin %s is not implemented\\n\", \"G_PERPENDICULARVECTOR\");\t\treturn ret;\n\t\tVALIDATEPOINTER(arg[1], sizeof(vec3_t));\n\t\tPerpendicularVector(VM_POINTER(arg[0]), VM_POINTER(arg[1]));\n\t\tbreak;\n\n\t//case G_DEFAULTCASEWARNINGDISABLE: NOT A REAL VALUE\n//\tnotimplemented:\n\tdefault:\n\t\tCon_Printf(\"Q3Game: builtin %i is not known\\n\", (int)fn);\n\t}\n\treturn ret;\n}\n\nstatic int Q3G_SystemCallsVM(void *offset, quintptr_t mask, int fn, const int *arg)\n{\n\tqintptr_t args[13];\n\n\targs[0]=arg[0];\n\targs[1]=arg[1];\n\targs[2]=arg[2];\n\targs[3]=arg[3];\n\targs[4]=arg[4];\n\targs[5]=arg[5];\n\targs[6]=arg[6];\n\targs[7]=arg[7];\n\targs[8]=arg[8];\n\targs[9]=arg[9];\n\targs[10]=arg[10];\n\targs[11]=arg[11];\n\targs[12]=arg[12];\n\n\treturn Q3G_SystemCalls(offset, mask, fn, args);\n}\n\nstatic qintptr_t EXPORT_FN Q3G_SystemCallsNative(qintptr_t arg, ...)\n{\n\tqintptr_t args[13];\n\tva_list argptr;\n\n\tva_start(argptr, arg);\n\targs[0]=va_arg(argptr, qintptr_t);\n\targs[1]=va_arg(argptr, qintptr_t);\n\targs[2]=va_arg(argptr, qintptr_t);\n\targs[3]=va_arg(argptr, qintptr_t);\n\targs[4]=va_arg(argptr, qintptr_t);\n\targs[5]=va_arg(argptr, qintptr_t);\n\targs[6]=va_arg(argptr, qintptr_t);\n\targs[7]=va_arg(argptr, qintptr_t);\n\targs[8]=va_arg(argptr, qintptr_t);\n\targs[9]=va_arg(argptr, qintptr_t);\n\targs[10]=va_arg(argptr, qintptr_t);\n\targs[11]=va_arg(argptr, qintptr_t);\n\targs[12]=va_arg(argptr, qintptr_t);\n\tva_end(argptr);\n\n\treturn Q3G_SystemCalls(NULL, ~(quintptr_t)0, arg, args);\n}\n\nvoid SVQ3_ShutdownGame(qboolean restarting)\n{\n\tint i;\n#ifdef HAVE_CLIENT\n\tif (!restarting)\n\t\tCG_Stop();\n#endif\n\tif (!q3gamevm)\n\t\treturn;\n\n\tif (!restarting)\n\t{\n#ifdef USEBOTLIB\n\t\tif (botlib)\n\t\t{\t//it crashes otherwise, probably due to our huck clearage\n\t\t\tbotlib->BotLibShutdown();\n\t\t}\n\t\tplugfuncs->GFreeAll(&botlibmem);\n\t\tplugfuncs->GFreeAll(&botlibhunkmem);\n\t\tVM_fcloseall(Z_TAG_BOTLIB);\n#endif\n\n\t\tfor (i = 0; i < countof(svq3_configstrings); i++)\n\t\t{\n\t\t\tif (svq3_configstrings[i])\n\t\t\t{\n\t\t\t\tZ_Free(svq3_configstrings[i]);\n\t\t\t\tsvq3_configstrings[i] = NULL;\n\t\t\t}\n\t\t}\n\n\t\tplugfuncs->Free(q3_sentities);\n\t\tq3_sentities = NULL;\n\t\tplugfuncs->Free(q3_snapshot_entities);\n\t\tq3_snapshot_entities = NULL;\n\t}\n\n\tvmfuncs->Destroy(q3gamevm);\n\tq3gamevm = NULL;\n\n\tVM_fcloseall(0);\n\n\tcvarfuncs->SetString(\"sv_running\", \"0\");\n}\n\n#ifdef USEBOTLIB\nstatic void VARGS BL_Print(int l, char *fmt, ...)\n{\n\tva_list\t\targptr;\n\tchar\t\ttext[1024];\n\n\tva_start (argptr, fmt);\n\tvsnprintf (text, sizeof(text), fmt, argptr);\n\tva_end (argptr);\n\n\tCon_Printf(\"%s\", text);\n}\n\nstatic int botlibmemoryavailable;\nstatic int QDECL BL_AvailableMemory(void)\n{\n\treturn botlibmemoryavailable;\n}\nstatic void *QDECL BL_Malloc(int size)\n{\n\tint *mem;\n\tbotlibmemoryavailable-=size;\n\n\tmem = plugfuncs->GMalloc(&botlibmem, sizeof(int)+size);\n\tmem[0] = size;\n\n\treturn (void *)(mem + 1);\n}\nstatic void QDECL BL_Free(void *mem)\n{\n\tint *memref = ((int *)mem) - 1;\n\tbotlibmemoryavailable+=memref[0];\n\tplugfuncs->GFree(&botlibmem, memref);\n}\nstatic void *QDECL BL_HunkMalloc(int size)\n{\n\treturn plugfuncs->GMalloc(&botlibhunkmem, sizeof(int)+size);\n}\n\nstatic int QDECL BL_FOpenFile(const char *name, fileHandle_t *handle, fsMode_t mode)\n{\n\treturn VM_fopen((char*)name, (int*)handle, mode, Z_TAG_BOTLIB);\n}\nstatic int QDECL BL_FRead(void *buffer, int len, fileHandle_t f)\n{\n\treturn VM_FRead(buffer, len, (int)f, Z_TAG_BOTLIB);\n}\nstatic int QDECL BL_FWrite(const void *buffer, int len, fileHandle_t f)\n{\n\treturn VM_FWrite(buffer, len, (int)f, Z_TAG_BOTLIB);\n}\nstatic void QDECL BL_FCloseFile(fileHandle_t f)\n{\n\tVM_fclose((int)f, Z_TAG_BOTLIB);\n}\nstatic int QDECL BL_Seek(fileHandle_t f, long offset, int seektype)\n{\t// on success, apparently returns 0\n\treturn VM_FSeek((int)f, offset, seektype, Z_TAG_BOTLIB)?0:-1;\n}\nstatic char *QDECL BL_BSPEntityData(void)\n{\n\treturn (char*)worldfuncs->GetEntitiesString(sv3.world->worldmodel);\n}\nstatic void QDECL BL_Trace(bsp_trace_t *trace, vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, int passent, int contentmask)\n{\n\tq3trace_t tr;\n\tSVQ3_Trace(&tr, start, mins, maxs, end, passent, contentmask, false);\n\n\ttrace->allsolid = tr.allsolid;\n\ttrace->startsolid = tr.startsolid;\n\ttrace->fraction = tr.fraction;\n\tVectorCopy(tr.endpos, trace->endpos);\n\ttrace->plane = tr.plane;\n\ttrace->exp_dist = 0;\n\ttrace->sidenum = 0;\n\t//trace->surface.name\n\t//trace->surface.flags\n\ttrace->surface.value = tr.surfaceFlags;\n\ttrace->contents = 0;//tr.contents;\n\ttrace->ent = tr.entityNum;\n}\nstatic int QDECL BL_PointContents(vec3_t point)\n{\n\treturn SVQ3_PointContents(point, -1);\n}\n\nstatic int QDECL BL_inPVS(vec3_t p1, vec3_t p2)\n{\n\treturn true;// FIXME: :(\n}\n\nstatic void QDECL BL_EntityTrace(bsp_trace_t *trace, vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, int entnum, int contentmask)\n{\n\ttrace->allsolid = 0;//tr.allsolid;\n\ttrace->startsolid = 0;//tr.startsolid;\n\ttrace->fraction = 1;//tr.fraction;\n\tVectorCopy(end, trace->endpos);\n//\ttrace->plane = tr.plane;\n\ttrace->exp_dist = 0;\n\ttrace->sidenum = 0;\n\t//trace->surface.name\n\t//trace->surface.flags\n//\ttrace->surface.value = tr.surfaceFlags;\n\ttrace->contents = 0;//tr.contents;\n//\ttrace->ent = tr.entityNum;\n}\n\nstatic void QDECL BL_BSPModelMinsMaxsOrigin(int modelnum, vec3_t angles, vec3_t outmins, vec3_t outmaxs, vec3_t origin)\n{\n\tmodel_t *mod;\n\tvec3_t mins, maxs;\n\tfloat max;\n\tint\ti;\n\n\tmod = Q3G_GetCModel(modelnum);\n\tif (mod)\n\t{\n\t\tVectorCopy(mod->mins, mins);\n\t\tVectorCopy(mod->maxs, maxs);\n\t}\n\telse\n\t{\n\t\tVectorCopy(vec3_origin, mins);\n\t\tVectorCopy(vec3_origin, maxs);\n\t}\n\n\t//if the model is rotated\n\tif ((angles[0] || angles[1] || angles[2]))\n\t{\n\t\t// expand for rotation\n\t\tvec3_t\tcorner;\n\t\tfor (i=0 ; i<3 ; i++)\n\t\t\tcorner[i] = fabs(mins[i]) > fabs(maxs[i]) ? fabs(mins[i]) : fabs(maxs[i]);\n\t\tmax = sqrt(DotProduct(corner,corner));\n\t\tfor (i = 0; i < 3; i++)\n\t\t{\n\t\t\tmins[i] = -max;\n\t\t\tmaxs[i] = max;\n\t\t}\n\t}\n\tif (outmins)\n\t\tVectorCopy(mins, outmins);\n\tif (outmaxs)\n\t\tVectorCopy(maxs, outmaxs);\n\tif (origin)\n\t\tVectorClear(origin);\n}\nstatic void QDECL BL_BotClientCommand(int clientnum, char *command)\n{\n\tcmdfuncs->TokenizeString(command);\n\tvmfuncs->Call(q3gamevm, GAME_CLIENT_COMMAND, clientnum);\n}\n\nstatic int QDECL BL_DebugLineCreate(void) {return 0;}\nstatic void QDECL BL_DebugLineDelete(int line) {}\nstatic void QDECL BL_DebugLineShow(int line, vec3_t start, vec3_t end, int color) {}\nstatic int QDECL BL_DebugPolygonCreate(int color, int numPoints, vec3_t *points) {return 0;}\nstatic void QDECL BL_DebugPolygonDelete(int id) {}\n\nvoid QDECL BL_Error(const char *msg)\n{\n\tplugfuncs->Error(\"%s\", msg);\n}\n#endif\n\nvoid SV_InitBotLib(void)\n{\n\tcvar_t *bot_enable = cvarfuncs->GetNVFDG(\"bot_enable\", \"1\", 0, \"Controls whether bots may be used by the gamecode or not.\", \"Q3 compatability\");\n\n#ifdef USEBOTLIB\n\tbotlib_import_t import;\n\tif (!bot_enable)\n\t\treturn;\t//o.O\n\n\tif (sv3.world && sv3.world->worldmodel)\n\t\tcvarfuncs->SetString(\"sv_mapChecksum\", va(\"%i\", sv3.world->worldmodel->checksum));\n\n\tmemset(&import, 0, sizeof(import));\n\timport.Print = BL_Print;\n\timport.Trace = BL_Trace;\n\timport.EntityTrace = BL_EntityTrace;\n\timport.PointContents = BL_PointContents;\n\timport.inPVS = BL_inPVS;\n\timport.BSPEntityData = BL_BSPEntityData;\n\timport.BSPModelMinsMaxsOrigin = BL_BSPModelMinsMaxsOrigin;\n\timport.BotClientCommand = BL_BotClientCommand;\n\timport.GetMemory = BL_Malloc;\n\timport.FreeMemory = BL_Free;\n\timport.AvailableMemory = BL_AvailableMemory;\n\timport.HunkAlloc = BL_HunkMalloc;\n\timport.FS_FOpenFile = BL_FOpenFile;\n\timport.FS_Read = BL_FRead;\n\timport.FS_Write = BL_FWrite;\n\timport.FS_FCloseFile = BL_FCloseFile;\n\timport.FS_Seek = BL_Seek;\n\n\timport.DebugLineCreate = BL_DebugLineCreate;\n\timport.DebugLineDelete = BL_DebugLineDelete;\n\timport.DebugLineShow = BL_DebugLineShow;\n\timport.DebugPolygonCreate= BL_DebugPolygonCreate;\n\timport.DebugPolygonDelete = BL_DebugPolygonDelete;\n\n\timport.Error = BL_Error;\n\n\tbotlibmemoryavailable = 1024*1024*16;\n\tif (bot_enable->value)\n\t\tbotlib = FTE_GetBotLibAPI(BOTLIB_API_VERSION, &import);\n\telse\n\t\tbotlib = NULL;\n\tif (!botlib)\n\t{\n\t\tbot_enable->flags |= CVAR_MAPLATCH;\n\t\tcvarfuncs->ForceSetString(bot_enable->name, \"0\");\n\t}\n#else\n\n//make sure it's switched off.\n\tcvarfuncs->ForceSetString(bot_enable->name, \"0\");\n\tbot_enable->flags |= CVAR_NOSET;\n#endif\n}\n\nvoid SVQ3_ServerinfoChanged(const char *key)\n{\t//roll up multiple updates into a single splurge. SVQ3_UpdateServerinfo will be called after the next frame.\n\tq3_serverinfo_dirty = true;\n}\nstatic void SVQ3_UpdateServerinfo(void)\n{\t/*qw serverinfo settings are not normally visible in the q3 serverinfo, so strip them from the configstring*/\n\tchar buffer[8192];\n\tstatic const char *ignores[] = {\"maxclients\", \"map\", \"sv_maxclients\", \"*z_ext\", \"*bspversion\", \"*gamedir\", NULL};\n\tworldfuncs->IBufToInfo(&svs.info, buffer, sizeof(buffer), NULL, ignores, NULL, NULL, NULL);\n\t//add in maxclients.. the q3 version\n\tQ_strncatz(buffer, va(\"\\\\sv_maxclients\\\\%s\", sv_maxclients->string), sizeof(buffer));\n\tSVQ3_SetConfigString(0, buffer);\n\n\n\tq3_serverinfo_dirty = false;\n}\n\nqboolean SVQ3_InitGame(server_static_t *server_state_static, server_t *server_state, qboolean restart)\n{\n\tint i;\n\tchar ssqcprogs[MAX_QPATH];\n\n\tmemset(sv3.models, 0, sizeof(sv3.models));\n\tsv3.server_state_static = server_state_static;\n\tsv3.server_state = server_state;\n\tsv3.world = &server_state->world;\n\tif (sv3.world->worldmodel->type == mod_heightmap)\n\t{\n\t}\n\telse\n\t{\n\t\tif (sv3.world->worldmodel->fromgame == fg_quake || sv3.world->worldmodel->fromgame == fg_halflife || sv3.world->worldmodel->fromgame == fg_quake2)\n\t\t\treturn false;\t//always fail on q1bsp\n\t}\n\n\tcvarfuncs->GetString(\"pr_ssqc_progs\", ssqcprogs, sizeof(ssqcprogs));\n\tif (*ssqcprogs)\t//don't load q3 gamecode if we're explicitally told to load a progs.\n\t\treturn false;\n\n\tsv3.restarting = restart;\n\n\tSVQ3_ShutdownGame(restart);\n\n\tsv_maxclients = cvarfuncs->GetNVFDG(\"sv_maxclients\", \"\", 0, NULL, \"Q3 Compat\");\n\n\tq3gamevm = vmfuncs->Create(\"qagame\", cvarfuncs->GetFloat(\"com_gamedirnativecode\")?Q3G_SystemCallsNative:NULL, \"vm/qagame\", Q3G_SystemCallsVM);\n\n\tif (!q3gamevm)\n\t\treturn false;\n\n\t//q3 needs mapname (while qw has map serverinfo)\n\tcvarfuncs->SetString(\"mapname\", svs.name);\n\n\tSV_InitBotLib();\n\n\tSVQ3_ClearWorld();\n\n\tq3_sentities = Z_Malloc(sizeof(q3serverEntity_t)*MAX_GENTITIES);\n\n\tcvarfuncs->SetString(\"sv_running\", \"1\");\t//so the ui knows\n\n\tif (!restart)\n\t{\n\t\tchar buffer[8192];\n\t\tchar sysinfo[8192];\n\t\t/*update the system info*/\n\t\tsysinfo[0] = '\\0';\n\t\tworldfuncs->SetInfoKey(sysinfo, \"sv_serverid\", va(\"%i\", svs.spawncount), MAX_SERVERINFO_STRING);\n\n\t\tworldfuncs->SetInfoKey(sysinfo,\t\t\t   \"sv_paks\", fsfuncs->GetPackHashes(buffer, sizeof(buffer), false       ), MAX_SERVERINFO_STRING);\n\t\tworldfuncs->SetInfoKey(sysinfo,\t\t   \"sv_pakNames\", fsfuncs->GetPackNames (buffer, sizeof(buffer), false, false), MAX_SERVERINFO_STRING);\n\t\tworldfuncs->SetInfoKey(sysinfo,\t \"sv_referencedPaks\", fsfuncs->GetPackHashes(buffer, sizeof(buffer), true        ), MAX_SERVERINFO_STRING);\n\t\tworldfuncs->SetInfoKey(sysinfo, \"sv_referencedPakNames\", fsfuncs->GetPackNames (buffer, sizeof(buffer), true, false ), MAX_SERVERINFO_STRING);\n\n\t\tcvarfuncs->GetString(\"sv_pure\", buffer, sizeof(buffer));\n\t\tworldfuncs->SetInfoKey(sysinfo, \"sv_pure\", buffer, MAX_SERVERINFO_STRING);\n\t\tSVQ3_SetConfigString(1, sysinfo);\n\t\tq3_serverinfo_dirty = true;\n\t}\n\n\tmapentspointer = worldfuncs->GetEntitiesString(sv3.world->worldmodel);\n\tvmfuncs->Call(q3gamevm, GAME_INIT, (intptr_t)(sv3.world->physicstime*1000), (int)rand(), restart);\n\n\tif (!restart)\n\t{\t//restart means same initial gamestate, meaning don't mess with baselines.\n\t\tSVQ3_CreateBaseline();\n\n\t\tq3_num_snapshot_entities = 32 * Q3UPDATE_BACKUP * 32;\n\t\tif (q3_snapshot_entities)\n\t\t\tplugfuncs->Free(q3_snapshot_entities);\n\t\tq3_next_snapshot_entities = 0;\n\t\tq3_snapshot_entities = Z_Malloc(sizeof( q3entityState_t ) * q3_num_snapshot_entities);\n\t}\n\n\t// run a few frames to allow everything to settle\n\tfor (i = 0; i < 3; i++)\n\t{\n\t\tSVQ3_RunFrame();\n\t\tsv3.world->physicstime += 0.1;\n\t}\n\n\treturn true;\n}\n\nvoid SVQ3_RunFrame(void)\n{\n#ifdef USEBOTLIB\n\tif (botlib)\n\t\tvmfuncs->Call(q3gamevm, BOTAI_START_FRAME, (int)(sv3.world->physicstime*1000));\n#endif\n\tvmfuncs->Call(q3gamevm, GAME_RUN_FRAME, (int)(sv3.world->physicstime*1000));\n\n\tif (q3_serverinfo_dirty)\n\t\tSVQ3_UpdateServerinfo();\n}\n\nvoid SVQ3_ClientCommand(client_t *cl)\n{\n\tvmfuncs->Call(q3gamevm, GAME_CLIENT_COMMAND, (int)(cl-svs.clients));\n}\n\nvoid SVQ3_ClientBegin(client_t *cl)\n{\n\tvmfuncs->Call(q3gamevm, GAME_CLIENT_BEGIN, (int)(cl-svs.clients));\n\tsv.spawned_client_slots++;\n\tcl->spawned = true;\n}\n\nvoid SVQ3_ClientThink(client_t *cl)\n{\n\tvmfuncs->Call(q3gamevm, GAME_CLIENT_THINK, (int)(cl-svs.clients));\n}\n\nqboolean SVQ3_ConsoleCommand(void)\n{\n\tif (!q3gamevm)\n\t\treturn false;\n\n\treturn vmfuncs->Call(q3gamevm, GAME_CONSOLE_COMMAND);\n}\n\nqboolean SVQ3_PrefixedConsoleCommand(void)\n{\n\tif (!q3gamevm)\n\t\treturn false;\n\tcmdfuncs->ShiftArgs(1);\n\tvmfuncs->Call(q3gamevm, GAME_CONSOLE_COMMAND);\n\treturn true;\n}\n\nstatic void SVQ3_Netchan_Transmit( client_t *client, int length, qbyte *data );\n\nvoid SVQ3_CreateBaseline(void)\n{\n\tq3sharedEntity_t\t*ent;\n\tint\t\t\t\tentnum;\n\n\tif (q3_baselines)\n\t\tZ_Free(q3_baselines);\n\n\tq3_baselines = Z_Malloc(sizeof(q3entityState_t)*MAX_GENTITIES);\n\tfor(entnum=0; entnum<numq3entities; entnum++)\n\t{\n\t\tent = GENTITY_FOR_NUM(entnum);\n\n\t\tif(!ent->r.linked)\n\t\t\tcontinue;\n\n\t\t// FIXME - is this check correct?\n\t\tif(ent->r.svFlags & (SVF_NOCLIENT|/*SVF_CLIENTMASK|*/SVF_SINGLECLIENT))\n\t\t\tcontinue;\n\n\t\tif (ent->s.number < 0)\n\t\t\tcontinue;\t//hey!\n\n\t\t//\n\t\t// take current state as baseline\n\t\t//\n\t\tmemcpy(&q3_baselines[entnum], &ent->s, sizeof(q3_baselines[0]));\n\t}\n}\n\n//Writes the entities to the clients\nvoid SVQ3_EmitPacketEntities(client_t *client, q3client_frame_t *from, q3client_frame_t *to, sizebuf_t *msg)\n{\n\tq3entityState_t\t*oldent, *newent;\n\tint\t\t\t\toldindex, newindex;\n\tint\t\t\t\toldnum, newnum;\n\tint\t\t\t\tfrom_num_entities;\n\n\tif(!from )\n\t{\n\t\tfrom_num_entities = 0;\n\t}\n\telse\n\t{\n\t\tfrom_num_entities = from->num_entities;\n\t}\n\n\tnewindex = 0;\n\toldindex = 0;\n\twhile(newindex < to->num_entities || oldindex < from_num_entities)\n\t{\n\t\tif(newindex >= to->num_entities)\n\t\t{\n\t\t\tnewent = NULL;\n\t\t\tnewnum = 99999;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tnewent = &q3_snapshot_entities[(to->first_entity + newindex) % q3_num_snapshot_entities];\n\t\t\tnewnum = newent->number;\n\t\t}\n\n\t\tif(oldindex >= from_num_entities)\n\t\t{\n\t\t\toldent = NULL;\n\t\t\toldnum = 99999;\n\t\t}\n\t\telse\n\t\t{\n\t\t\toldent = &q3_snapshot_entities[(from->first_entity + oldindex) % q3_num_snapshot_entities];\n\t\t\toldnum = oldent->number;\n\t\t}\n\n\t\tif(newnum == oldnum)\n\t\t{\n\t\t\t// delta update from old position\n\t\t\t// because the force parm is false, this will not result\n\t\t\t// in any bytes being emited if the entity has not changed at all\n\t\t\tMSGQ3_WriteDeltaEntity(msg, oldent, newent, false);\n\t\t\toldindex++;\n\t\t\tnewindex++;\n\t\t\tcontinue;\n\t\t}\n\n\t\tif(newnum < oldnum)\n\t\t{\n\t\t\t// this is a new entity, send it from the baseline\n#ifdef QWOVERQ3\n\t\t\tif (svs.gametype != GT_QUAKE3)\n\t\t\t{\n\t\t\t\tq3entityState_t q3base;\n\t\t\t\tedict_t *e;\n\t\t\t\te = EDICT_NUM(svprogfuncs, newnum);\n\t\t\t\tSVQ3Q1_ConvertEntStateQ1ToQ3(&e->baseline, &q3base);\n\t\t\t\tMSGQ3_WriteDeltaEntity( msg, &q3base, newent, true );\n\t\t\t}\n\t\t\telse\n#endif\n\t\t\t\tMSGQ3_WriteDeltaEntity( msg, &q3_baselines[newnum], newent, true );\n\t\t\tnewindex++;\n\t\t\tcontinue;\n\t\t}\n\n\t\tif(newnum > oldnum)\n\t\t{\n\t\t\t// the old entity isn't present in the new message\n\t\t\tMSGQ3_WriteDeltaEntity( msg, oldent, NULL, true );\n\t\t\toldindex++;\n\t\t\tcontinue;\n\t\t}\n\t}\n\n\tmsgfuncs->WriteBits(msg, ENTITYNUM_NONE, GENTITYNUM_BITS); // end of packetentities\n}\n\nvoid SVQ3_WriteSnapshotToClient(client_t *client, sizebuf_t *msg)\n{\n\tq3client_frame_t\t*oldsnap;\n\tq3client_frame_t\t*snap;\n\tint\t\t\t\tdelta;\n\tint\t\t\t\ti;\n\n\t// this is the frame we are transmitting\n\tsnap = client->frameunion.q3frames;\n\tsnap += client->netchan.outgoing_sequence & Q3UPDATE_MASK;\n\n\tif(client->state < cs_spawned)\n\t{\n\t\t// not fully in game yet\n\t\tdelta = 0;\n\t\toldsnap = NULL;\n\t\treturn;\n\t}\n\telse if(client->delta_sequence < 0)\n\t{\n\t\t// client is asking for a retransmit\n\t\tdelta = 0;\n\t\toldsnap = NULL;\n\t}\n\telse if(client->netchan.outgoing_sequence - client->delta_sequence >= Q3UPDATE_BACKUP - 3)\n\t{\n\t\t// client hasn't gotten a good message through in a long time\n\t\tCon_DPrintf( \"%s: Delta request from out of date packet.\\n\", client->name );\n\t\tdelta = 0;\n\t\toldsnap = NULL;\n\t}\n\telse\n\t{\n\t\t// we have a valid message to delta from\n\t\tdelta = client->netchan.outgoing_sequence - client->delta_sequence;\n\t\toldsnap = client->frameunion.q3frames;\n\t\toldsnap += client->delta_sequence & Q3UPDATE_MASK;\n\n\t\tif(oldsnap->first_entity <= q3_next_snapshot_entities - q3_num_snapshot_entities)\n\t\t{\n\t\t\t// oldsnap entities are too old\n\t\t\tCon_DPrintf(\"%s: Delta request from out of date entities.\\n\", client->name);\n\t\t\tdelta = 0;\n\t\t\toldsnap = NULL;\n\t\t}\n\t}\n\n//\tif( client->surpressCount ) {\n//\t\tsnap->snapFlags |= SNAPFLAG_RATE_DELAYED;\n//\t\tclient->surpressCount = 0;\n//\t}\n\n\t// write snapshot header\n\tmsgfuncs->WriteBits(msg, svcq3_snapshot, 8);\n\tmsgfuncs->WriteBits(msg, snap->serverTime, 32);\n\tmsgfuncs->WriteBits(msg, delta, 8); // what we are delta'ing from\n\n\t// write snapFlags\n\tmsgfuncs->WriteBits(msg, snap->flags, 8);\n\n\t// send over the areabits\n\tmsgfuncs->WriteBits(msg, snap->areabytes, 8);\n\tfor (i = 0; i < snap->areabytes; i++)\n\t\tmsgfuncs->WriteBits(msg, snap->areabits[i], 8);\n\n\t// delta encode the playerstate\n\tMSGQ3_WriteDeltaPlayerstate(msg, oldsnap ? &oldsnap->ps : NULL, &snap->ps);\n\n\t// delta encode the entities\n\tSVQ3_EmitPacketEntities(client, oldsnap, snap, msg);\n\n//\twhile( msg.cursize < sv_padPackets->integer ) { // FIXME?\n//\tfor( i=0 ; i<sv_padPackets->integer ; i++ )\n//\t{\n//\t\tMSG_WriteByte( msg, svcq3_nop );\n//\t}\n}\n\n\nstatic int clientNum;\nstatic int clientarea;\nstatic qbyte\t\t*bitvector;\n\nstatic int VARGS SVQ3_QsortEntityStates( const void *arg1, const void *arg2 )\n{\n\tconst q3entityState_t *s1 = *(const q3entityState_t **)arg1;\n\tconst q3entityState_t *s2 = *(const q3entityState_t **)arg2;\n\n\n\tif( s1->number > s2->number )\n\t{\n\t\treturn 1;\n\t}\n\n\tif( s1->number < s2->number )\n\t{\n\t\treturn -1;\n\t}\n\n\tplugfuncs->EndGame(\"SV_QsortEntityStates: duplicated entity\");\n\n\treturn 0;\n\n}\n\nstatic qboolean SVQ3_EntityIsVisible(q3client_frame_t *snap, q3sharedEntity_t *ent)\n{\n\tq3serverEntity_t *sent;\n\tif (!ent->r.linked)\n\t{\n\t\treturn false; // not active entity\n\t}\n\n\tif (ent->r.svFlags & SVF_NOCLIENT )\n\t{\n\t\treturn false; // set to invisible\n\t}\n\n\tif (ent->r.svFlags & SVF_CLIENTMASK)\n\t{\n\t\tif (clientNum > 32)\n\t\t{\n\t\t\tplugfuncs->EndGame(\"SVF_CLIENTMASK: clientNum > 32\" );\n\t\t}\n\t\tif (ent->r.singleClient & (1 << (clientNum & 7)))\n\t\t{\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\tif (ent->r.svFlags & SVF_SINGLECLIENT)\n\t{\n\t\tif ( ent->r.singleClient == clientNum)\n\t\t{\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\tif (ent->r.svFlags & SVF_NOTSINGLECLIENT)\n\t{\n\t\tif (ent->r.singleClient == clientNum)\n\t\t{\n\t\t\treturn false;\n\t\t}\n\t\t// FIXME: fall through\n\t}\n\n\tif (ent->r.svFlags & SVF_BROADCAST)\n\t{\n\t\treturn true;\n\t}\n\n\t//\n\t// ignore if not touching a PV leaf\n\t//\n\tsent = SENTITY_FOR_GENTITY( ent );\n\n\t// check area\n\tif (sent->pvscache.areanum < 0 || !(snap->areabits[sent->pvscache.areanum >> 3] & (1 << (sent->pvscache.areanum & 7))))\n\t{\n\t\t// doors can legally straddle two areas, so\n\t\t// we may need to check another one\n\t\tif (sent->pvscache.areanum2 < 0 || !(snap->areabits[sent->pvscache.areanum2 >> 3] & (1 << (sent->pvscache.areanum2 & 7))))\n\t\t{\n\t\t\treturn false;\t\t// blocked by a door\n\t\t}\n\t}\n\n\treturn sv3.world->worldmodel->funcs.EdictInFatPVS(sv3.world->worldmodel, &sent->pvscache, bitvector, NULL/*using the snapshots areabits rather than the bsp's*/);\n}\n\n#ifdef Q3OVERQW\nstatic q3playerState_t *SVQ3Q1_BuildPlayerState(client_t *client)\n{\n\tstatic q3playerState_t state;\n\textern cvar_t sv_gravity;\n\n\tmemset(&state, 0, sizeof(state));\n\n#ifdef warningmsg\n#pragma warningmsg(\"qwoverq3: other things will need to be packed into here.\")\n#endif\n\n\tstate.commandTime = client->lastcmd.servertime;\n\n\tstate.pm_type = client->edict->v->movetype;\n\tstate.origin[0] = client->edict->v->origin[0];\n\tstate.origin[1] = client->edict->v->origin[1];\n\tstate.origin[2] = client->edict->v->origin[2];\n\tstate.velocity[0] = client->edict->v->velocity[0];\n\tstate.velocity[1] = client->edict->v->velocity[1];\n\tstate.velocity[2] = client->edict->v->velocity[2];\n\n\tclient->maxspeed = client->edict->xv->maxspeed;\n\tif (!client->maxspeed)\n\t\tclient->maxspeed = sv_maxspeed.value;\n\tclient->entgravity = client->edict->xv->gravity * sv_gravity.value;\n\tif (!client->entgravity)\n\t\tclient->entgravity = sv_gravity.value;\n\tif (client->edict->xv->hasted)\n\t\tclient->maxspeed *= client->edict->xv->hasted;\n\tstate.speed = client->maxspeed;\n\tstate.gravity = client->entgravity;\n\n\tstate.viewangles[0] = client->edict->v->angles[0];\n\tstate.viewangles[1] = client->edict->v->angles[1];\n\tstate.viewangles[2] = client->edict->v->angles[2];\n\n\tstate.clientNum = client - svs.clients;\n\tstate.weapon = client->edict->v->weapon;\n\n\tstate.stats[0] = client->edict->v->health;\n\tstate.stats[2] = (int)client->edict->v->items&1023;\n\tstate.stats[3] = client->edict->v->armorvalue;\n\tstate.stats[4] = client->edict->v->angles[1];\n//\tstate.stats[6] = client->edict->v->max_health;\n\tstate.persistant[0] = client->edict->v->frags;\n\tstate.ammo[0] = client->edict->v->currentammo;\n\tstate.ammo[1] = client->edict->v->ammo_shells;\n\tstate.ammo[2] = client->edict->v->ammo_nails;\n\tstate.ammo[3] = client->edict->v->ammo_rockets;\n\tstate.ammo[4] = client->edict->v->ammo_cells;\n\treturn &state;\n}\n#endif\n\nvoid SVQ3_BuildClientSnapshot( client_t *client )\n{\n\tq3entityState_t\t\t*entityStates[MAX_ENTITIES_IN_SNAPSHOT];\n\tvec3_t\t\t\t\torg;\n\tq3sharedEntity_t\t\t*ent;\n\tq3sharedEntity_t\t\t*clent;\n\tq3client_frame_t\t*snap;\n\tq3entityState_t\t\t*es;\n\tq3playerState_t\t\t*ps;\n\tint\t\t\t\t\tportalarea;\n\tint\t\t\t\t\ti;\n\tstatic pvsbuffer_t pvsbuffer;\n\n\tif (!q3_snapshot_entities)\n\t{\n\t\tq3_num_snapshot_entities = 32 * Q3UPDATE_BACKUP * 32;\n\t\tq3_next_snapshot_entities = 0;\n\t\tq3_snapshot_entities = Z_Malloc(sizeof( q3entityState_t ) * q3_num_snapshot_entities);\n\t}\n\n\tclientNum = client - svs.clients;\n#ifdef Q3OVERQW\n\tif (svs.gametype != GT_QUAKE3)\n\t{\n\t\tclent = NULL;\n\t\tps = SVQ3Q1_BuildPlayerState(client);\n\t}\n\telse\n#endif\n\t{\n\t\tclent = GENTITY_FOR_NUM( clientNum );\n\t\tps = PS_FOR_NUM( clientNum );\n\t}\n\n\t// this is the frame we are creating\n\tsnap = client->frameunion.q3frames;\n\tsnap += client->netchan.outgoing_sequence & Q3UPDATE_MASK;\n\n\tsnap->serverTime = sv3.world->physicstime*1000; // save it for ping calc later\n\tsnap->flags = 0;\n\n\tif( client->state < cs_spawned )\n\t{\n\t\t// not in game yet\n\t\tmemcpy(&snap->ps, ps, sizeof(snap->ps));\n\t\tsnap->flags |= SNAPFLAG_NOT_ACTIVE;\n\t\tsnap->areabytes = 1;\n\t\tsnap->areabits[0] = 0;\n\t\tsnap->num_entities = 0;\n\t\tsnap->first_entity = q3_next_snapshot_entities;\n\t\treturn;\n\t}\n\n\t// find the client's PVS\n\tVectorCopy( ps->origin, org );\n\torg[2] += ps->viewheight;\n\n\tbitvector = sv3.world->worldmodel->funcs.ClusterPVS(sv3.world->worldmodel, sv.world.worldmodel->funcs.ClusterForPoint(sv.world.worldmodel, org, &clientarea), &pvsbuffer, PVM_REPLACE);\n\n\t// calculate the visible areas\n\tsnap->areabytes = sv3.world->worldmodel->funcs.WriteAreaBits(sv3.world->worldmodel, snap->areabits, sizeof(snap->areabits), clientarea, false);\n\n\t// grab the current playerState_t\n\tmemcpy(&snap->ps, ps, sizeof(snap->ps));\n\n\t// build up the list of visible entities\n\tsnap->num_entities = 0;\n\tsnap->first_entity = q3_next_snapshot_entities;\n\n\tif (svs.gametype == GT_QUAKE3)\n\t{\n\t// check for SVF_PORTAL entities first\n\t\tfor( i=0 ; i<numq3entities ; i++)\n\t\t{\n\t\t\tent = GENTITY_FOR_NUM(i);\n\n\t\t\tif(ent == clent )\n\t\t\t\tcontinue;\n\t\t\tif(!(ent->r.svFlags & SVF_PORTAL))\n\t\t\t\tcontinue;\n\t\t\tif(!SVQ3_EntityIsVisible(snap, ent))\n\t\t\t\tcontinue;\n\n\t\t\t//merge pvs bits so we can see other ents through it\n\t\t\tsv3.world->worldmodel->funcs.ClusterPVS(sv3.world->worldmodel, sv3.world->worldmodel->funcs.ClusterForPoint(sv3.world->worldmodel, ent->s.origin2, &portalarea), &pvsbuffer, PVM_MERGE);\n\t\t\t//and areabits too\n\t\t\tsv3.world->worldmodel->funcs.WriteAreaBits(sv3.world->worldmodel, snap->areabits, snap->areabytes, portalarea, true);\n\t\t}\n\n\t\t// add all visible entities\n\t\tfor (i=0 ; i<numq3entities ; i++)\n\t\t{\n\t\t\tent = GENTITY_FOR_NUM(i);\n\n\t\t\tif (ent == clent)\n\t\t\t\tcontinue;\n\t\t\tif (!SVQ3_EntityIsVisible(snap, ent))\n\t\t\t\tcontinue;\n\n\t\t\tif (ent->s.number != i)\n\t\t\t{\n\t\t\t\tCon_DPrintf( \"FIXING ENT->S.NUMBER!!!\\n\" );\n\t\t\t\tent->s.number = i;\n\t\t\t}\n\n\t\t\tentityStates[snap->num_entities++] = &ent->s;\n\n\t\t\tif( snap->num_entities >= MAX_ENTITIES_IN_SNAPSHOT )\n\t\t\t{\n\t\t\t\tCon_DPrintf( \"MAX_ENTITIES_IN_SNAPSHOT\\n\" );\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n#ifdef QWOVERQ3\n\telse\n\t{\t//our q1->q3 converter\n\t\tpacket_entities_t pack;\n\t\tentity_state_t packentities[64];\n\t\tq3entityState_t q3packentities[64];\n\t\tpack.entities = packentities;\n\t\tpack.max_entities = sizeof(packentities)/sizeof(packentities[0]);\n\t\t//get the q1 code to generate a packet\n\t\tSVQ3Q1_BuildEntityPacket(client, &pack);\n\t\tfor (i = 0; i < pack.num_entities; i++)\n\t\t{\t//map the packet fields to q3.\n\t\t\tSVQ3Q1_ConvertEntStateQ1ToQ3(&pack.entities[i], &q3packentities[i]);\n\t\t\tentityStates[snap->num_entities++] = &q3packentities[i];\n\t\t}\n\t}\n#endif\n\n\tif( q3_next_snapshot_entities + snap->num_entities >= 0x7FFFFFFE )\n\t{\n\t\tplugfuncs->EndGame(\"q3_next_snapshot_entities wrapped\");\n\t}\n\n\t// find duplicated entities\n\tqsort( entityStates, snap->num_entities, sizeof( entityStates[0] ), SVQ3_QsortEntityStates );\n\n\t// add them to the circular snapshotEntities array\n\tfor( i=0 ; i<snap->num_entities ; i++ )\n\t{\n\t\tes = &q3_snapshot_entities[q3_next_snapshot_entities % q3_num_snapshot_entities];\n\t\tmemcpy( es, entityStates[i], sizeof( *es ) );\n\n\t\tq3_next_snapshot_entities++;\n\t}\n\n\n\n\tfor (i = 0; i < snap->areabytes;i++)\n\t{\t//fix areabits, q2->q3 style..\n\t\tsnap->areabits[i]^=255;\n\t}\n}\n\n#ifdef QWOVERQ3\nstatic void SVQ3Q1_ConvertEntStateQ1ToQ3(entity_state_t *q1, q3entityState_t *q3)\n{\n#ifdef warningmsg\n#pragma warningmsg(\"qwoverq3: This _WILL_ need extending\")\n#endif\n\tq3->number = q1->number;\n\n\tq3->pos.trTime = 0;\n\tq3->pos.trBase[0] = 0;\n\tq3->pos.trBase[1] = 0;\n\tq3->pos.trDelta[0] = 0;\n\tq3->pos.trDelta[1] = 0;\n\tq3->pos.trBase[2] = 0;\n\tq3->apos.trBase[1] = 0;\n\tq3->pos.trDelta[2] = 0;\n\tq3->apos.trBase[0] = 0;\n\tq3->event = 0;\n\tq3->angles2[1] = 0;\n\tq3->eType = 0;\n\tq3->torsoAnim = q1->skinnum;\n\tq3->eventParm = 0;\n\tq3->legsAnim = 0;\n\tq3->groundEntityNum = 0;\n\tq3->pos.trType = 0;\n\tq3->eFlags = 0;\n\tq3->otherEntityNum = 0;\n\tq3->weapon = 0;\n\tq3->clientNum = q1->colormap;\n\tq3->angles[1] = q1->angles[0];\n\tq3->pos.trDuration = 0;\n\tq3->apos.trType = 0;\n\tq3->origin[0] = q1->origin[0];\n\tq3->origin[1] = q1->origin[1];\n\tq3->origin[2] = q1->origin[2];\n\tq3->solid = q1->solidsize;\n\tq3->powerups = q1->effects;\n\tq3->modelindex = q1->modelindex;\n\tq3->otherEntityNum2 = 0;\n\tq3->loopSound = 0;\n\tq3->generic1 = q1->trans;\n\tq3->origin2[2] = 0;//q1->old_origin[2];\n\tq3->origin2[0] = 0;//q1->old_origin[0];\n\tq3->origin2[1] = 0;//q1->old_origin[1];\n\tq3->modelindex2 = 0;//q1->modelindex2;\n\tq3->angles[0] = q1->angles[0];\n\tq3->time = 0;\n\tq3->apos.trTime = 0;\n\tq3->apos.trDuration = 0;\n\tq3->apos.trBase[2] = 0;\n\tq3->apos.trDelta[0] = 0;\n\tq3->apos.trDelta[1] = 0;\n\tq3->apos.trDelta[2] = 0;\n\tq3->time2 = 0;\n\tq3->angles[2] = q1->angles[2];\n\tq3->angles2[0] = 0;\n\tq3->angles2[2] = 0;\n\tq3->constantLight = q1->abslight;\n\tq3->frame = q1->frame;\n}\n\nstatic void SVQ3Q1_SendGamestateConfigstrings(sizebuf_t *msg)\n{\n\tconst int cs_models = 32;\n\tconst int cs_sounds = cs_models + 256;\n\tconst int cs_players = cs_sounds + 256;\n\n\tint i, j;\n\n\tconst char *str;\n\tchar sysinfo[MAX_SERVERINFO_STRING];\n\tconst char *cfgstr[MAX_CONFIGSTRINGS];\n\n\t//an empty crc string means we let the client use any\n\t//but then it doesn't download our qwoverq3 thing.\n\tchar *refpackcrcs = \"\";//\"-1309355180 0 0 0 0 0 0 0 0 0\";\n\tchar *refpacknames = \"baseq3/pak0 baseq3/pak1 baseq3/pak2 baseq3/pak3 baseq3/pak4 baseq3/pak5 baseq3/pak6 baseq3/pak7 baseq3/pak8 fte/qwoverq3\";\n\n\tmemset((void*)cfgstr, 0, sizeof(cfgstr));\n\n\tsysinfo[0] = 0;\n\tInfo_SetValueForKey(sysinfo, \"sv_serverid\", va(\"%i\", svs.spawncount), sizeof(sysinfo));\n\n\tstr = refpackcrcs;//FS_GetPackHashes(buffer, sizeof(buffer), false);\n\tInfo_SetValueForKey(sysinfo, \"sv_paks\", str, sizeof(sysinfo));\n\n\tstr = refpacknames;//FS_GetPackNames(buffer, sizeof(buffer), false);\n\tInfo_SetValueForKey(sysinfo, \"sv_pakNames\", str, sizeof(sysinfo));\n\n\tstr = refpackcrcs;//FS_GetPackHashes(buffer, sizeof(buffer), true);\n\tInfo_SetValueForKey(sysinfo, \"sv_referencedPaks\", str, sizeof(sysinfo));\n\n\tstr = refpacknames;//FS_GetPackNames(buffer, sizeof(buffer), true);\n\tInfo_SetValueForKey(sysinfo, \"sv_referencedPakNames\", str, sizeof(sysinfo));\n\n//Con_Printf(\"Sysinfo: %s\\n\", sysinfo);\n\tstr = \"0\";\n\tInfo_SetValueForKey(sysinfo, \"sv_pure\", str, sizeof(sysinfo));\n\n\tstr = \"qwoverq3\";\n\tInfo_SetValueForKey(sysinfo, \"fs_game\", str, sizeof(sysinfo));\n\n\tcfgstr[0] = svs.info;\n\tcfgstr[1] = sysinfo;\n\n\tcfgstr[2] = NULL;//sv.cdtrack;\n\tcfgstr[3] = sv.mapname;\n\tcfgstr[20] = \"QuakeWorld-Over-Q3\";\t//you can get the gamedir out of the serverinfo\n\n\t//add in 32 clients\n\tfor (i = 0; i < sv.allocated_client_slots; i++)\n\t{\n\t\tcfgstr[cs_players+i] = svs.clients[i].userinfo;\n\t}\n\n\t//fill up the last half with sound/model names\n\t//note that we're limited to only 256 models/sounds\n\tfor (i = 2; i < 256; i++)\n\t{\n\t\tcfgstr[cs_models+i] = sv.strings.model_precache[i];\n\t}\n\tfor (i = 0; i < 256; i++)\n\t{\n\t\tcfgstr[cs_sounds+i] = sv.strings.sound_precache[i];\n\t}\n\n\t// write configstrings\n\tfor (i = 0; i < MAX_CONFIGSTRINGS; i++)\n\t{\n\t\tstr = cfgstr[i];\n\t\tif (!str || !*str)\n\t\t\tcontinue;\n\n\t\tMSG_WriteBits(msg, svcq3_configstring, 8);\n\t\tMSG_WriteBits(msg, i, 16);\n\t\tfor (j = 0; str[j]; j++)\n\t\t\tMSG_WriteBits(msg, str[j], 8);\n\t\tMSG_WriteBits(msg, 0, 8);\n\t}\n}\n#endif\n\nstatic void SVQ3_WriteServerCommandsToClient(client_t *client, sizebuf_t *msg)\n{\n\tint\ti;\n\tint j, len;\n\tchar *str;\n\n\tfor(i=client->server_command_ack+1; i<=client->server_command_sequence; i++)\n\t{\n\t\tmsgfuncs->WriteBits(msg, svcq3_serverCommand, 8);\n\t\tmsgfuncs->WriteBits(msg, i, 32);\n\t\tstr = client->server_commands[i & Q3TEXTCMD_MASK];\n\t\tlen = strlen(str);\n\t\tfor (j = 0; j <= len; j++)\n\t\t\tmsgfuncs->WriteBits(msg, str[j], 8);\n\t}\n}\n\n//writes initial gamestate\nvoid SVQ3_SendGameState(client_t *client)\n{\n\tsizebuf_t\t\tmsg;\n\tunsigned char\t\t\tbuffer[MAX_OVERALLMSGLEN];\n\tint\t\t\t\ti;\n\tint\t\t\t\tj;\n\tchar\t\t\t*configString;\n\n\tCon_DPrintf( \"SV_SendClientGameState() for %s\\n\", client->name );\n\n\tmemset(&msg, 0, sizeof(msg));\n\tmsg.maxsize =  sizeof(buffer);\n\tmsg.data = buffer;\n\tmsg.packing = SZ_HUFFMAN;\n\n\t// write last clientCommand number we have processed\n\tmsgfuncs->WriteBits(&msg, client->last_client_command_num, 32);\n\n\tSVQ3_WriteServerCommandsToClient(client, &msg);\n\n\tmsgfuncs->WriteBits(&msg, svcq3_gamestate, 8 );\n\tmsgfuncs->WriteBits(&msg, client->server_command_sequence, 32);\n\n\tswitch (svs.gametype)\n\t{\n\tcase GT_QUAKE3:\n\t\t// write configstrings\n\t\tfor( i=0; i<countof(svq3_configstrings); i++ )\n\t\t{\n\t\t\tconfigString = svq3_configstrings[i];\n\t\t\tif( !configString )\n\t\t\t\tcontinue;\n\n\t\t\tmsgfuncs->WriteBits( &msg, svcq3_configstring, 8);\n\t\t\tmsgfuncs->WriteBits( &msg, i, 16 );\n\t\t\tfor (j = 0; configString[j]; j++)\n\t\t\t\tmsgfuncs->WriteBits(&msg, configString[j], 8);\n\t\t\tmsgfuncs->WriteBits(&msg, 0, 8);\n\t\t}\n\n\t\t// write baselines\n\t\tfor( i=0; i<MAX_GENTITIES; i++ )\n\t\t{\n\t\t\tif (!q3_baselines[i].number)\n\t\t\t\tcontinue;\n\n\t\t\tmsgfuncs->WriteBits(&msg, svcq3_baseline, 8);\n\t\t\tMSGQ3_WriteDeltaEntity( &msg, NULL, &q3_baselines[i], true );\n\t\t}\n\t\tbreak;\n#ifdef QWOVERQ3\n\tcase GT_PROGS:\n\tcase GT_Q1QVM:\n\t\tSVQ3Q1_SendGamestateConfigstrings(&msg);\n\n\t\tfor (i = sv.allocated_client_slots+1; i < sv3.world->num_edicts; i++)\n\t\t{\n\t\t\tedict_t *e = EDICT_NUM(svprogfuncs, i);\n\t\t\tif (e->baseline.modelindex)\n\t\t\t{\n\t\t\t\tq3entityState_t q3base;\n\t\t\t\tSVQ3Q1_ConvertEntStateQ1ToQ3(&e->baseline, &q3base);\n\t\t\t\tMSG_WriteBits(&msg, svcq3_baseline, 8);\n\t\t\t\tMSGQ3_WriteDeltaEntity(&msg, NULL, &q3base, true);\n\t\t\t}\n\t\t}\n\t\tbreak;\n#endif\n\tdefault:\n\t\tclient->drop = true;\n\t\tbreak;\n\t}\n\n\t// write svc_eom command\n\tmsgfuncs->WriteBits(&msg, svcq3_eom, 8);\n\n\tmsgfuncs->WriteBits(&msg, client - svs.clients, 32);\n\tmsgfuncs->WriteBits(&msg, fs_key, 32);\n\n\t// end of message marker\n\tmsgfuncs->WriteBits(&msg, svcq3_eom, 8);\n\n\t// send the datagram\n\tSVQ3_Netchan_Transmit(client, msg.cursize, msg.data);\n\n\t// calculate client->sendTime\n//\tSV_RateDrop(client, msg.cursize);\n\n\tclient->state = cs_connected;\n\tclient->gamestatesequence = client->last_sequence;\n}\n\nvoid SVQ3_SendMessage(client_t *client)\n{\n\tqbyte\t\tbuffer[MAX_OVERALLMSGLEN];\n\tsizebuf_t\tmsg;\n\tmemset(&msg, 0, sizeof(msg));\n\tmsg.maxsize =  sizeof(buffer);\n\tmsg.data = buffer;\n\tmsg.packing = SZ_HUFFMAN;\n\n\tSVQ3_BuildClientSnapshot(client);\n\n\tmsgfuncs->WriteBits(&msg, client->last_client_command_num, 32);\n\n\t// write pending serverCommands\n\tSVQ3_WriteServerCommandsToClient(client, &msg);\n\n\t// send over all the relevant entityState_t\n\t// and the playerState_t\n\tSVQ3_WriteSnapshotToClient(client, &msg);\n\n\t// SV_WriteDownloadToClient(client, &msg);\n\n\t// end of message marker\n\tmsgfuncs->WriteBits(&msg, svcq3_eom, 8);\n\n\tSVQ3_Netchan_Transmit(client, msg.cursize, msg.data);\n}\n\n\n\n\nclient_t *SVQ3_FindEmptyPlayerSlot(void)\n{\n\tint pcount = 0;\n\tint i;\n\tfor (i = 0; i < sv.allocated_client_slots; i++)\n\t{\n\t\tif (svs.clients[i].state)\n\t\t\tpcount++;\n\t}\n\t//in q3, spectators are not special\n\tif (pcount >= sv_maxclients->value)\n\t\treturn NULL;\n\tfor (i = 0; i < sv.allocated_client_slots; i++)\n\t{\n\t\tif (!svs.clients[i].state)\n\t\t\treturn &svs.clients[i];\n\t}\n\treturn NULL;\n}\nclient_t *SVQ3_FindExistingPlayerByIP(netadr_t *na, int qport)\n{\n\tint i;\n\tfor (i = 0; i < sv.allocated_client_slots; i++)\n\t{\n\t\tif (svs.clients[i].state && msgfuncs->CompareAdr(&svs.clients[i].netchan.remote_address, na))\n\t\t\treturn &svs.clients[i];\n\t}\n\treturn NULL;\n}\n\nstatic qboolean SVQ3_Netchan_Process(client_t *client, sizebuf_t *msg)\n{\n\tif (Netchan_ProcessQ3(&client->netchan, msg))\n\t{\n#ifndef Q3_NOENCRYPT\n\t\tint\t\tserverid;\n\t\tint\t\tlastSequence;\n\t\tint\t\tlastServerCommandNum;\n\t\tqbyte\tbitmask;\n\t\tqbyte\tc;\n\t\tint\t\ti, j;\n\t\tchar\t*string;\n\t\tint\t\tbit;\n\n\t\t// archive buffer state\n\t\tbit = msg->currentbit;\n\t\tmsg->packing = SZ_HUFFMAN;\n\n\t\tserverid = msgfuncs->ReadBits(32);\n\t\tlastSequence = msgfuncs->ReadBits(32);\n\t\tlastServerCommandNum = msgfuncs->ReadBits(32);\n\n\t\t// restore buffer state\n\t\tmsg->currentbit = bit;\n\t\tmsg->packing = SZ_RAWBYTES;\n\n\t\t// calculate bitmask\n\t\tbitmask = (serverid ^ lastSequence ^ client->challenge) & 0xff;\n\t\tstring = client->server_commands[lastServerCommandNum & Q3TEXTCMD_MASK];\n\n\t\t// decrypt the packet\n\t\tfor(i=msgfuncs->ReadCount()+12,j=0; i<msg->cursize; i++,j++)\n\t\t{\n\t\t\tif(!string[j])\n\t\t\t{\n\t\t\t\tj = 0; // another way around\n\t\t\t}\n\t\t\tc = string[j];\n\t\t\tif(c > 127 || c == '%')\n\t\t\t{\n\t\t\t\tc = '.';\n\t\t\t}\n\t\t\tbitmask ^= c << (i & 1);\n\t\t\tmsg->data[i] ^= bitmask;\n\t\t}\n#endif\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\nstatic void SVQ3_Netchan_Transmit(client_t *client, int length, qbyte *data)\n{\n#ifndef Q3_NOENCRYPT\n\tqbyte\t\tbuffer[MAX_OVERALLMSGLEN];\n\tint\t\t\ti;\n\tqbyte\t\tbitmask;\n\tqbyte\t\tc;\n\tint\t\t\tj;\n\tchar\t\t*string;\n\n\t// calculate bitmask\n\tbitmask = (client->netchan.outgoing_sequence ^ client->challenge) & 0xff;\n\tstring = client->last_client_command;\n\n\t//first four bytes are not encrypted.\n\tfor(i=0; i<4 ; i++)\n\t\tbuffer[i] = data[i];\n\t// encrypt the packet\n\tfor(j=0 ; i<length ; i++, j++)\n\t{\n\t\tif(!string[j])\n\t\t\tj = 0;\t//restart\n\t\tc = string[j];\n\t\tif (c > 127 || c == '%')\n\t\t\tc = '.';\n\t\tbitmask ^= c << (i & 1);\n\t\tbuffer[i] = data[i]^bitmask;\n\t}\n\tdata = buffer;\n#endif\n\n\t// deliver the message\n\tNetchan_TransmitQ3(sv3.server_state_static->sockets, &client->netchan, length, data);\n}\n\nint StringKey(const char *string, int length);\n#define MAX_PACKET_USERCMDS 64\nvoid SVQ3_ParseUsercmd(client_t *client, qboolean delta)\n{\n\tstatic usercmd_t\tnullcmd;\n\tusercmd_t\t\t\tcommands[MAX_PACKET_USERCMDS];\n\tusercmd_t\t\t\t*from;\n\tusercmd_t\t\t\t*to;\n\tint\t\t\t\t\ti;\n\tint\t\t\t\t\tkey;\n\tint\t\t\t\t\tcmdCount;\n\tchar *string;\n\n\tif(delta)\n\t{\n\t\tclient->delta_sequence = client->last_sequence;\n//\t\tclient->snapLatency[client->last_sequence & (LATENCY_COUNTS-1)] = Sys_Milliseconds()/*svs.levelTime*/ - client->snapshots[client->last_sequence & UPDATE_MASK].serverTime;\n\t}\n\telse\n\t{\n\t\tclient->delta_sequence = -1; // client is asking for retransmit\n//\t\tclient->snapLatency[client->last_sequence & (LATENCY_COUNTS-1)] = -1;\n\t}\n\n\t// read number of usercmds in a packet\n\tcmdCount = msgfuncs->ReadBits(8);\n\tif (cmdCount < 1 ||\n\t\tcmdCount > MAX_PACKET_USERCMDS ||\n\t\tsvs.gametype != GT_QUAKE3)\n\t{\n\t\tworldfuncs->DropClient(client);\n\t\treturn;\n\t}\n\n\tif(client->state < cs_connected)\n\t\treturn; // was dropped\n\n\t// calculate key for usercmd decryption\n\tstring = client->server_commands[client->server_command_ack & Q3TEXTCMD_MASK];\n\tkey = client->last_sequence ^ fs_key ^ StringKey(string, 32);\n\n\t// read delta sequenced usercmds\n\tfrom = &nullcmd;\n\tfor(i=0, to=commands; i<cmdCount; i++, to++)\n\t{\n\t\tMSG_Q3_ReadDeltaUsercmd(key, from, to);\n\t\tfrom = to;\n\t}\n\n\tswitch(client->state)\n\t{\n\tcase cs_connected:\n\t\t// transition from CS_PRIMED to CS_ACTIVE\n\t\tmemcpy(&client->lastcmd, &commands[cmdCount-1], sizeof(client->lastcmd));\n\t\tSVQ3_ClientBegin(client);\n\t\tclient->state = cs_spawned;\n\n\t\tclient->lastcmd.servertime = sv.time*1000;\n\t\tbreak;\n\tcase cs_spawned:\n\t\t// run G_ClientThink() on each usercmd\n\t\tfor(i=0,to=commands; i<cmdCount; i++, to++)\n\t\t{\n\t\t\tif(to->servertime <= client->lastcmd.servertime)\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (to->servertime-10 > sv.time*1000)\t//10 ms allows some server latency...\n\t\t\t{\n\t\t\t\tCon_Printf(\"ignoring command from the future...\\n\");\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tmemcpy(&client->lastcmd, to, sizeof(client->lastcmd));\n\t\t\tSVQ3_ClientThink(client);\n\t\t}\n\t\tbreak;\n\tdefault:\n\t\tbreak; // outdated usercmd packet\n\t}\n\n}\n\nvoid SVQ3_UpdateUserinfo_f(client_t *cl)\n{\n\tworldfuncs->IBufFromInfo(&cl->userinfo, cmdfuncs->Argv(1, NULL, 0), false);\n\tworldfuncs->ExtractFromUserinfo (cl, true);\n\n\tif (svs.gametype == GT_QUAKE3 && cl->spawned)\n\t\tvmfuncs->Call(q3gamevm, GAME_CLIENT_USERINFO_CHANGED, (int)(cl-svs.clients));\n}\n\nstatic void SVQ3_Drop_f(client_t *cl)\n{\n\tworldfuncs->DropClient(cl);\n}\n\n//part of the sv_pure mechanism. verifies the client's pack list and kicks if they're wrong.\n//safe to ignore, if you're okay with potential cheats.\nstatic void SVQ3_ClientPacks_f(client_t *cl)\n{\n}\n\nstatic void SVQ3_Download_f(client_t *cl)\n{\n\t//clients might end up waiting for the download which will never come.\n\t//kick them so that doesn't happen. downloads are not supported at this time. not even reporting failure! :s\n\tworldfuncs->DropClient(cl);\n//\tshort 0\n//\tlong -1\n}\nstatic void SVQ3_NextDL_f(client_t *cl)\n{\n\t//send next chunk\n}\nstatic void SVQ3_StopDL_f(client_t *cl)\n{\n\t//abort/close current download, if any\n}\nstatic void SVQ3_DoneDL_f(client_t *cl)\n{\n\t//send new gamestate\n}\n\ntypedef struct ucmd_s\n{\n\tchar\t*name;\n\tvoid\t(*func)(client_t *);\n} ucmd_t;\n\nstatic const ucmd_t ucmds[] =\n{\n\t{\"userinfo\",\t\tSVQ3_UpdateUserinfo_f},\n\t{\"disconnect\",\t\tSVQ3_Drop_f},\n\n\t{\"cp\",\t\t\t\tSVQ3_ClientPacks_f},\n\t{\"download\",\t\tSVQ3_Download_f},\n\t{\"nextdl\",\t\t\tSVQ3_NextDL_f},\n\t{\"stopdl\",\t\t\tSVQ3_StopDL_f},\n\t{\"donedl\",\t\t\tSVQ3_DoneDL_f},\n\n\t{NULL,\t\t\t\tNULL}\n};\nvoid SVQ3_ParseClientCommand(client_t *client)\n{\n\tint commandNum;\n\tchar *command;\n\tconst ucmd_t\t*u;\n\tchar buffer[2048];\n\tchar arg0[256];\n\tint i;\n\n\tcommandNum = msgfuncs->ReadBits(32);\n\tfor (i = 0; ; i++)\n\t{\n\t\tbuffer[i] = msgfuncs->ReadBits(8);\n\t\tif (!buffer[i])\n\t\t\tbreak;\n\t}\n\tcommand = buffer;\n\n\tif(commandNum <= client->last_client_command_num)\n\t\treturn; // we have already received this command\n\n//\tCon_Printf(\"ClientCommand %i: %s\\n\", commandNum, buffer);\n\n//\tCon_DPrintf(\"clientCommand: %s : %i : %s\\n\", client->name, commandNum, Com_TranslateLinefeeds(command));\n\n\tclient->last_client_command_num++;\n\n\tif(commandNum > client->last_client_command_num)\n\t{\n\t\tCon_Printf(\"Client %s lost %i clientCommands\\n\", client->name, commandNum - client->last_client_command_num);\n\t\tworldfuncs->DropClient(client);\n\t\treturn;\n\t}\n\n\t// copy current command for netchan encryption\n\tQ_strncpyz(client->last_client_command, command, sizeof(client->last_client_command));\n\n\tcmdfuncs->TokenizeString(command);\n\n\t// check for server private commands first\n\tcmdfuncs->Argv(0, arg0, sizeof(arg0));\n\tfor(u=ucmds; u->name; u++)\n\t{\n\t\tif(!Q_strcasecmp(arg0, u->name))\n\t\t{\n\t\t\tif(u->func)\n\t\t\t\tu->func(client);\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tif (svs.gametype == GT_QUAKE3)\n\tif(!u->name && sv.state == ss_active)\n\t\tSVQ3_ClientCommand(client);\n}\n\nvoid SVQ3_ParseClientMessage(client_t *client, sizebuf_t *msg)\n{\n\tint serverid;\t//sorta like the level number.\n\tint c;\n\n\t// remaining data is compressed\n\tmsg->packing = SZ_HUFFMAN;\n\tmsg->currentbit = msgfuncs->ReadCount()*8;\n\n\t// read serverid\n\tserverid = msgfuncs->ReadBits(32);\n\n\t// read last server message sequence client received\n\tclient->last_sequence = msgfuncs->ReadBits(32);\n\tif( client->last_sequence < 0 )\n\t{\n\t\treturn; // this shouldn't happen\n\t}\n\n\t// read last server command number client received\n\tclient->server_command_ack = msgfuncs->ReadBits(32);\n\tif( client->server_command_ack <= client->server_command_sequence - Q3TEXTCMD_BACKUP )\n\t\tclient->server_command_ack = client->server_command_sequence - Q3TEXTCMD_BACKUP + 1; //too old\n\telse if( client->server_command_ack > client->server_command_sequence )\n\t\tclient->server_command_ack = client->server_command_sequence;\t//client is from the future? o.O make fatal?\n\n\t// check if message is from a previous level\n\tif( serverid != svs.spawncount )\n\t{\n\t\tif(client->gamestatesequence>=0)\n\t\t{\n\t\t\tif (client->last_sequence - client->gamestatesequence < 100)\n\t\t\t{\n\t\t\t\treturn; // don't resend gameState too frequently\n\t\t\t}\n\n\t\t\tCon_DPrintf(\"%s : dropped gamestate, resending\\n\", client->name);\n\t\t}\n\t\tclient->lastcmd.servertime = sv.time*1000;\n\t\tSVQ3_SendGameState(client);\n\t\treturn;\n\t}\n\n\tclient->send_message = true;\n\n\n\t//\n\t// parse the message\n\t//\n\twhile(1)\n\t{\n\t\tif(client->state < cs_connected)\n\t\t\treturn; // parsed command caused client to disconnect\n\n\t\tc = msgfuncs->ReadBits(8);\n\t\tif (c < 0)\n\t\t{\n\t\t\tCon_Printf(\"corrupt packet from %s\\n\", client->name);\n\t\t\tclient->drop = true;\n\t\t\treturn;\n\t\t}\n\t\tif (c == clcq3_eom)\n\t\t{\n\t\t\tbreak;\n\t\t}\n\n\t\tswitch(c)\n\t\t{\n\t\tdefault:\n\t\t\tCon_Printf(\"corrupt packet from %s\\n\", client->name);\n\t\t\tclient->drop = true;\n\t\t\treturn;\n\t\tcase clcq3_nop:\n\t\t\tbreak;\n\t\tcase clcq3_move:\n\t\t\tSVQ3_ParseUsercmd(client, true);\n\t\t\tbreak;\n\t\tcase clcq3_nodeltaMove:\n\t\t\tSVQ3_ParseUsercmd(client, false);\n\t\t\tbreak;\n\t\tcase clcq3_clientCommand:\n\t\t\tSVQ3_ParseClientCommand(client);\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tif (msg->currentbit+8 < (msg->cursize<<3))\n\t{\n\t\tCon_Printf(CON_WARNING \"WARNING: Junk at end of packet for client %s\\n\", client->name );\n\t}\n};\nqboolean SVQ3_HandleClient(netadr_t *from, sizebuf_t *msg)\n{\n\tint i;\n\tint qport;\n\n\tmsgfuncs->BeginReading(msg, msg_nullnetprim);\n\tmsgfuncs->ReadBits(32);\n\tqport = msgfuncs->ReadBits(16);\n\tif (qport < 0)\n\t\treturn false;\n\n\tfor (i = 0; i < sv.allocated_client_slots; i++)\n\t{\n\t\tif (svs.clients[i].state < cs_connected)\n\t\t\tcontinue;\n\t\tif (svs.clients[i].netchan.qport != qport)\n\t\t\tcontinue;\n\t\tif (!msgfuncs->CompareBaseAdr(&svs.clients[i].netchan.remote_address, from))\n\t\t\tcontinue;\n\t\tif (!ISQ3CLIENT(&svs.clients[i]))\n\t\t\tcontinue;\n\n\t\t//found them.\n\t\tbreak;\n\t}\n\tif (i == sv.allocated_client_slots)\n\t\treturn false;\t//nope\n\n\tif (!SVQ3_Netchan_Process(&svs.clients[i], msg))\n\t{\n\t\treturn true; // wasn't accepted for some reason\n\t}\n\n\tSVQ3_ParseClientMessage(&svs.clients[i], msg);\n\treturn true;\n}\n\n\n//Q3 gamecode does map_restart weirdly.\n//it simply reloads the gamecode without changing any maps/models/sounds\n//this won't work for q1/q2, but q3 expects it.\n//note that time continues from its prior value without any kind of reset.\nqboolean SVQ3_RestartGamecode(void)\n{\n\tint i;\n\tint newmaxclients = max(8,sv_maxclients->ival);\n\tif (sv.allocated_client_slots != newmaxclients)\n\t\treturn false;\t//can't do it if maxclients needs to change.\n\n\t//reload the gamecode\n\tsv.state = ss_loading;\n\tif (!SVQ3_InitGame(sv3.server_state_static, sv3.server_state, true))\n\t\treturn false;\n\n//\tsvs.spawncount++;\t//so new snapshots get sent\n\n\t//and then reconnect the players as appropriate\n//\tSVQ3_NewMapConnects();\n\n\tfor (i = 0; i < sv.allocated_client_slots; i++)\n\t{\n\t\tif (svs.clients[i].state < cs_connected)\n\t\t\tcontinue;\n\n\t\tif (vmfuncs->Call(q3gamevm, GAME_CLIENT_CONNECT, i, false, svs.clients[i].protocol == SCP_BAD))\n\t\t{\n\t\t\tworldfuncs->DropClient(&svs.clients[i]);\n\t\t\tcontinue;\n\t\t}\n\t\tif (svs.clients[i].spawned)\n\t\t{\n\t\t\tsv.spawned_client_slots--;\n\t\t \tSVQ3_ClientBegin(&svs.clients[i]);\n\t\t }\n\t}\n\n\tsv.starttime = plugfuncs->GetSeconds() - sv.time;\n#ifdef SAVEDGAMES\n\tsv.autosave_time = sv.time + cvarfuncs->GetFloat(\"sv_autosave\")*60;\n#endif\n\n\t//basically done\n\tsv.state = ss_active;\n\tsv3.restarting = false;\n\n\t//and an extra physics frame for luck\n\tsv.time+=0.1;\n\tsv3.world->physicstime=sv.time;\n\tSVQ3_RunFrame();\n\n\tSVQ3_SendServerCommand(NULL, \"map_restart\");\n\n\treturn true;\t//yup, we did it.\n}\n\nvoid SVQ3_NewMapConnects(void)\n{\n\tint i;\n\tqintptr_t ret;\n\n\t/* Kick old bots in SP - eukara */\n\tcvar_t *gametype;\n\tgametype = cvarfuncs->GetNVFDG(\"g_gametype\", \"\", CVAR_MAPLATCH|CVAR_SERVERINFO, NULL, \"Q3 compatability\");\n\n\tfor (i = 0; i < sv.allocated_client_slots; i++)\n\t{\n\t\tif (svs.clients[i].state < cs_connected)\n\t\t\tcontinue;\n\n\t\tif (gametype->value == 2 && svs.clients[i].protocol == SCP_BAD)\n\t\t\tret = true;\n\t\telse\n\t\t\tret = vmfuncs->Call(q3gamevm, GAME_CLIENT_CONNECT, i, false, svs.clients[i].protocol == SCP_BAD);\n\t\tif (ret)\n\t\t{\n\t\t\tworldfuncs->DropClient(&svs.clients[i]);\n\t\t}\n\t\telse if (svs.clients[i].protocol == SCP_BAD)\n\t\t{\t//spawn bots now.\n\t\t\tsvs.clients[i].state = cs_spawned;\n\t\t\tSVQ3_ClientBegin(&svs.clients[i]);\n\t\t}\n\t}\n}\n\nvoid SVQ3_DirectConnect(netadr_t *from, sizebuf_t *msg)\t//Actually connect the client, use up a slot, and let the gamecode know of it.\n{\n\t//this is only called when running q3 gamecode\n\tchar *reason;\n\tclient_t *cl;\n\tchar *userinfo = NULL;\n\tqintptr_t ret;\n\tint challenge;\n\tint qport;\n\tchar adr[MAX_ADR_SIZE];\n\n\tif (msg->cursize < 12)\n\t\treturn;\n\tif (msgfuncs->Huff_DecryptPacket)\n\t\tmsgfuncs->Huff_DecryptPacket(msg, 12);\n\n\tcmdfuncs->TokenizeString((char*)msg->data+4);\n\tuserinfo = cmdfuncs->Argv(1, NULL, 0);\n\tqport = atoi(worldfuncs->GetInfoKey(userinfo, \"qport\"));\n\tchallenge = atoi(worldfuncs->GetInfoKey(userinfo, \"challenge\"));\n\n\tcl = SVQ3_FindExistingPlayerByIP(from, qport);\t//use a duplicate first.\n\tif (!cl)\n\t\tcl = SVQ3_FindEmptyPlayerSlot();\n\n\tif (!msgfuncs->Huff_CompressionCRC || !msgfuncs->Huff_CompressionCRC(HUFFCRC_QUAKE3))\n\t{\n\t\treason = \"Could not set up compression.\";\n\t\tuserinfo = NULL;\n\t}\n\telse if (!cl)\n\t{\n\t\treason = \"Server is full.\";\n\t\tuserinfo = NULL;\n\t}\n\telse\n\t{\n\t\tif (cl->frameunion.q3frames)\n\t\t\tplugfuncs->Free(cl->frameunion.q3frames);\n\t\tmemset(cl, 0, sizeof(*cl));\n\t\tchallenge = atoi(worldfuncs->GetInfoKey(userinfo, \"challenge\"));\n\n\t\tif (from->type != NA_LOOPBACK && !worldfuncs->ChallengePasses(challenge))\n\t\t\treason = \"Invalid challenge\\n\";\n\t\telse\n\t\t{\n\t\t\tworldfuncs->IBufFromInfo(&cl->userinfo, userinfo, false);\n\t\t\tif (from->type == NA_LOOPBACK)\n\t\t\t\treason = \"localhost\";\t//Q3 uses this specific string for listen servers.\n\t\t\telse\n\t\t\t\treason = msgfuncs->AdrToString(adr, sizeof(adr), from);\n\t\t\tworldfuncs->SetIBufKey(&cl->userinfo, \"ip\", reason);\t//q3 gamecode needs to know the client's ip (server's perception of the client, NOT QW client's perception of the server/proxy)\n\n\t\t\tret = vmfuncs->Call(q3gamevm, GAME_CLIENT_CONNECT, (int)(cl-svs.clients), true/*firsttime*/, false/*isbot*/);\n\t\t\tif (!ret)\n\t\t\t\treason = NULL;\n\t\t\telse\n\t\t\t\treason = (char*)vmfuncs->MemoryBase(q3gamevm)+ret;\n\t\t}\n\t}\n\n\tif (reason)\n\t{\n\t\tCon_Printf(\"%s\\n\", reason);\n\t\treason = va(\"\\377\\377\\377\\377print\\n%s\", reason);\n\t\tmsgfuncs->SendPacket (svs.sockets, strlen(reason), reason, from);\n\t\treturn;\n\t}\n\n\tcl->protocol = SCP_QUAKE3;\n\tcl->state = cs_connected;\n\tcl->connection_started = realtime;\n\tcl->name = cl->namebuf;\n\tcl->team = cl->teambuf;\n\tworldfuncs->ExtractFromUserinfo(cl, true);\n\tNetchan_SetupQ3(NCF_SERVER, &cl->netchan, from, qport);\n\tcl->netchan.outgoing_sequence = 1;\n\n\tcl->challenge = challenge;\n\tcl->userid = (cl - svs.clients)+1;\n\n\tcl->gamestatesequence = -1;\n\n\tmsgfuncs->SendPacket (svs.sockets, 19, \"\\377\\377\\377\\377connectResponse\", from);\n\n\tcl->frameunion.q3frames = plugfuncs->Malloc(Q3UPDATE_BACKUP*sizeof(q3client_frame_t));\n}\n\nstatic int SVQ3_AddBot(void)\n{\n\tclient_t *cl;\n\n\tcl = SVQ3_FindEmptyPlayerSlot();\n\tif (!cl)\n\t\treturn -1;\t//failure, no slots\n\n\tcl->protocol = SCP_BAD;\n\tcl->connection_started = realtime;\n\tcl->state = cs_connected;\n\tcl->name = cl->namebuf;\n\tcl->team = cl->teambuf;\n\n\tcl->challenge = 0;\n\tcl->userid = (cl - svs.clients)+1;\n\tcl->state = cs_spawned;\n\tmemset(&cl->netchan.remote_address, 0, sizeof(cl->netchan.remote_address));\n\n\tGENTITY_FOR_NUM(cl-svs.clients)->s.number = cl-svs.clients;\n\treturn cl - svs.clients;\n}\n\nvoid SVQ3_DropClient(client_t *cl)\n{\n\tif (q3gamevm)\n\t\tvmfuncs->Call(q3gamevm, GAME_CLIENT_DISCONNECT, (int)(cl-svs.clients));\n}\n\n#endif\n"
  },
  {
    "path": "plugins/serverb/cl_master.h",
    "content": "#include \"../plugin.h\"\n\n\n#define\tMAX_INFO_STRING\t196\n#define\tMAX_SERVERINFO_STRING\t512\n#define\tMAX_LOCALINFO_STRING\t32768\n#define MAX_CLIENTS 32\n\n\n#define\tA2C_PRINT\t\t\t'n'\t// print a message on client\n#define M2C_MASTER_REPLY\t'd'\t// + \\n + qw server port list\n\n\n#define PORT_ANY -1\n\n#ifdef _WIN32\n#include \"winsock.h\"\n#define EWOULDBLOCK\t\tWSAEWOULDBLOCK\n#define ECONNREFUSED\tWSAECONNREFUSED\n#define EADDRNOTAVAIL\tWSAEADDRNOTAVAIL\n#define EMSGSIZE\t\tWSAEMSGSIZE\n#define ECONNABORTED\tWSAECONNABORTED\n#define ECONNRESET\t\tWSAECONNRESET\n\n#define qerrno WSAGetLastError()\n#else\n#define qerrno errno\n\n#include <sys/socket.h>\n#include <sys/types.h>\n#include <netinet/in.h>\n#include <errno.h>\ntypedef int SOCKET;\n#endif\n\n\n\n\n\n#define SS_FTESERVER\t1\t//hehehe...\n#define SS_QUAKE2\t\t2\t//useful (and cool). Could be blamed for swamping.\n#define SS_NETQUAKE\t\t4\n#define SS_FAVORITE\t\t8\t//filter all others.\n#define SS_KEEPINFO\t\t16\n\n\n//despite not supporting nq or q2, we still load them. We just filter them. This is to make sure we properly write the listing files.\n#define MT_BAD\t\t\t0\t//this would be an error\n#define MT_BCASTQW\t\t1\t//-1status\n#define MT_BCASTQ2\t\t2\t//-1status\n#define MT_BCASTNQ\t\t3\t//see code\n#define MT_SINGLEQW\t\t4\t//-1status\n#define MT_SINGLEQ2\t\t5\t//-1status\n#define MT_SINGLENQ\t\t6\t//see code.\n#define MT_MASTERQW\t\t7\t//c\\n\\0\n#define MT_MASTERQ2\t\t8\t//query\n\n\n//contains info about a server in greater detail. Could be too mem intensive.\ntypedef struct serverdetailedinfo_s {\n\tchar info[MAX_SERVERINFO_STRING];\n\n\tint numplayers;\n\n\tstruct {\n\t\tint userid;\n\t\tint frags;\n\t\tfloat time;\n\t\tint ping;\n\t\tchar name[64];\n\t\tchar skin[64];\n\t\tchar topc;\n\t\tchar botc;\t\t\t\t\n\t} players[MAX_CLIENTS];\n} serverdetailedinfo_t;\n\n//hold minimum info.\ntypedef struct serverinfo_s {\n\tchar name[64];\t//hostname.\n\tstruct sockaddr_in adr;\n\n\tshort players;\n\tshort maxplayers;\n\n\tshort tl;\n\tshort fl;\n\tchar gamedir[8+1];\n\tchar map[8+1];\n\n\tint refreshtime;\t//msecs\n\tqbyte special;\t//flags\n\tunsigned short ping;\n\tint sends;\n\n\tserverdetailedinfo_t *moreinfo;\n\n\tstruct serverinfo_s *next;\n} serverinfo_t;\n\ntypedef struct master_s{\n\tstruct master_s *next;\n\tstruct sockaddr_in adr;\n\tint type;\n\tchar name[1];\n} master_t;\n\nstruct {\n\tqboolean inuse;\n\tstruct sockaddr_in adr;\n\n\tserverdetailedinfo_t *detail;\n\n\tint linenum;\n} selectedserver;\n\ntypedef struct player_s {\n\tchar name[16];\n\tint frags;\n\tint colour;\n\tchar skin[8];\n\tstruct sockaddr_in adr;\n\n\tstruct player_s *next;\n} player_t;\n\nvoid SListOptionChanged(serverinfo_t *newserver);\n\nextern serverinfo_t *firstserver;\nextern master_t *master;\nextern player_t *mplayers;\n\nvoid CL_QueryServers(void);\nint NET_CheckPollSockets(void);\nserverinfo_t *Master_InfoForServer (struct sockaddr_in addr);\nserverinfo_t *Master_InfoForNum (int num);\nint Master_TotalCount(void);\nvoid Master_QueryServer(serverinfo_t *server);\nvoid MasterInfo_WriteServers(void);\nvoid MasterInfo_Begin(void);\n\n\nchar *NET_AdrToString(const struct sockaddr_in *a);\n\n\n#define Q_strncpyS(d, s, n) do{const char *____in=(s);char *____out=(d);int ____i; for (____i=0;*(____in); ____i++){if (____i == (n))break;*____out++ = *____in++;}if (____i < (n))*____out='\\0';}while(0)\t//only use this when it should be used. If undiciided, use N\n#define Q_strncpyN(d, s, n) do{if (n < 0)Sys_Error(\"Bad length in strncpyz\");Q_strncpyS((d), (s), (n));((char *)(d))[n] = '\\0';}while(0)\t//this'll stop me doing buffer overflows. (guarenteed to overflow if you tried the wrong size.)\n#define Q_strncpyNCHECKSIZE(d, s, n) do{if (n < 1)Sys_Error(\"Bad length in strncpyz\");Q_strncpyS((d), (s), (n));((char *)(d))[n-1] = '\\0';((char *)(d))[n] = '255';}while(0)\t//This forces nothing else to be within the buffer. Should be used for testing and nothing else.\n#define Q_strncpyz(d, s, n) Q_strncpyN(d, s, (n)-1)\n"
  },
  {
    "path": "plugins/serverb/m_master.c",
    "content": "#include \"cl_master.h\"\n\n\n\n\nint\tK_UPARROW,\n\tK_DOWNARROW,\n\tK_ENTER,\n\tK_DEL,\n\tK_BACKSPACE,\n\tK_ESCAPE,\n\tK_PGDN,\n\tK_PGUP,\n\tK_SPACE,\n\tK_LEFTARROW,\n\tK_RIGHTARROW;\n\n#define RESTRICT_LOCAL 64\n\nenum {\nSLISTTYPE_SERVERS,\nSLISTTYPE_FAVORITES,\nSLISTTYPE_SOURCES,\nSLISTTYPE_OPTIONS\t//must be last\n} slist_option;\n\nint slist_numoptions;\nint slist_firstoption;\n\nint slist_type;\n\nvoid M_DrawServers(void);\nvoid M_SListKey(int key);\n\n#define CVAR_ARCHIVE 0\n\n#define cvargroup \"Server Browser Vars\"\n//filtering\nvmcvar_t\tsb_hideempty\t\t= {\"sb_hideempty\",\t\t\"0\",\tcvargroup, CVAR_ARCHIVE};\nvmcvar_t\tsb_hidenotempty\t\t= {\"sb_hidenotempty\",\t\"0\",\tcvargroup, CVAR_ARCHIVE};\nvmcvar_t\tsb_hidefull\t\t\t= {\"sb_hidefull\",\t\t\"0\",\tcvargroup, CVAR_ARCHIVE};\nvmcvar_t\tsb_hidedead\t\t\t= {\"sb_hidedead\",\t\t\"1\",\tcvargroup, CVAR_ARCHIVE};\nvmcvar_t\tsb_hidequake2\t\t= {\"sb_hidequake2\",\t\t\"1\",\tcvargroup, CVAR_ARCHIVE};\nvmcvar_t\tsb_hidenetquake\t\t= {\"sb_hidenetquake\",\t\"1\",\tcvargroup, CVAR_ARCHIVE};\nvmcvar_t\tsb_hidequakeworld\t= {\"sb_hidequakeworld\",\t\"0\",\tcvargroup, CVAR_ARCHIVE};\nvmcvar_t\tsb_maxping\t\t\t= {\"sb_maxping\",\t\t\"0\",\tcvargroup, CVAR_ARCHIVE};\nvmcvar_t\tsb_gamedir\t\t\t= {\"sb_gamedir\",\t\t\"\",\t\tcvargroup, CVAR_ARCHIVE};\nvmcvar_t\tsb_mapname\t\t\t= {\"sb_mapname\",\t\t\"\",\t\tcvargroup, CVAR_ARCHIVE};\n\nvmcvar_t\tsb_showping\t\t\t= {\"sb_showping\",\t\t\"1\",\tcvargroup, CVAR_ARCHIVE};\nvmcvar_t\tsb_showaddress\t\t= {\"sb_showaddress\",\t\"0\",\tcvargroup, CVAR_ARCHIVE};\nvmcvar_t\tsb_showmap\t\t\t= {\"sb_showmap\",\t\t\"0\",\tcvargroup, CVAR_ARCHIVE};\nvmcvar_t\tsb_showgamedir\t\t= {\"sb_showgamedir\",\t\"0\",\tcvargroup, CVAR_ARCHIVE};\nvmcvar_t\tsb_showplayers\t\t= {\"sb_showplayers\",\t\"1\",\tcvargroup, CVAR_ARCHIVE};\nvmcvar_t\tsb_showfraglimit\t= {\"sb_showfraglimit\",\t\"0\",\tcvargroup, CVAR_ARCHIVE};\nvmcvar_t\tsb_showtimelimit\t= {\"sb_showtimelimit\",\t\"0\",\tcvargroup, CVAR_ARCHIVE};\n\nvmcvar_t\tsb_filterkey\t\t= {\"sb_filterkey\",\t\t\"hostname\", cvargroup, CVAR_ARCHIVE};\nvmcvar_t\tsb_filtervalue\t\t= {\"sb_filtervalue\",\t\"\",\t\tcvargroup, CVAR_ARCHIVE};\n#undef cvargroup \n\nvmcvar_t\t*cvarlist[] ={\n\t&sb_hideempty,\n\t&sb_hidenotempty,\n\t&sb_hidefull,\n\t&sb_hidedead,\n\t&sb_hidequake2,\n\t&sb_hidenetquake,\n\t&sb_hidequakeworld,\n\t&sb_maxping,\n\t&sb_gamedir,\n\t&sb_mapname,\n\n\t&sb_showping,\n\t&sb_showaddress,\n\t&sb_showmap,\n\t&sb_showgamedir,\n\t&sb_showplayers,\n\t&sb_showfraglimit,\n\t&sb_showtimelimit,\n\n\t&sb_filterkey,\n\t&sb_filtervalue\n};\n\nvoid M_Serverlist_InitCvars(void)\n{\n\tvmcvar_t *v;\n\tint i;\n\tfor (v = cvarlist[0],i=0; i < sizeof(cvarlist)/sizeof(cvarlist[0]); v++, i++)\n\t\tv->handle = Cvar_Register(v->name, v->string, v->flags, v->group);\n}\n\nint msecstime;\nint Plug_Tick(int *args)\n{\n\tmsecstime = args[0];\n\n\treturn true;\n}\n\nint Plug_MenuEvent(int *args)\n{\n\tswitch(args[0])\n\t{\n\tcase 0:\t//draw\n\t\tM_DrawServers();\n\t\tbreak;\n\tcase 1:\t//keydown\n\t\tM_SListKey(args[1]);\n\t\tbreak;\n\tcase 2:\t//keyup\n\t\tbreak;\n\tcase 3:\t//menu closed (this is called even if we change it).\n\t\tbreak;\n\t}\n\n\treturn 0;\n}\n\nvoid Plug_StartBrowser(void)\n{\n\tMenu_Control(1);\n\tif (BUILTINISVALID(LocalSound))\n\t\tLocalSound(\"misc/menu2.wav\");\n\n\tMasterInfo_Begin();\n}\n\nint Plug_ExecuteCommand(int *args)\n{\n\tchar cmd[256];\n\tCmd_Argv(0, cmd, sizeof(cmd));\n\tif (!strcmp(\"plug_browser\", cmd))\n\t{\n\t\tPlug_StartBrowser();\n\t\treturn 1;\n\t}\n\treturn 0;\n}\n\nint Plug_Init(int *args)\n{\n\tif (Plug_Export(\"Tick\", Plug_Tick) &&\n\t\tPlug_Export(\"ExecuteCommand\", Plug_ExecuteCommand) &&\n\t\tPlug_Export(\"MenuEvent\", Plug_MenuEvent))\n\t\tCon_Print(\"IRC Client Plugin Loaded\\n\");\n\telse\n\t{\n\t\tCon_Print(\"IRC Client Plugin failed\\n\");\n\t\treturn false;\n\t}\n\n\tK_UPARROW\t\t= Key_GetKeyCode(\"uparrow\");\n\tK_DOWNARROW\t\t= Key_GetKeyCode(\"downarrow\");\n\tK_ENTER\t\t\t= Key_GetKeyCode(\"enter\");\n\tK_DEL\t\t\t= Key_GetKeyCode(\"del\");\n\tK_BACKSPACE\t\t= Key_GetKeyCode(\"backspace\");\n\tK_ESCAPE\t\t= Key_GetKeyCode(\"escape\");\n\tK_PGDN\t\t\t= Key_GetKeyCode(\"pgdn\");\n\tK_PGUP\t\t\t= Key_GetKeyCode(\"pgup\");\n\tK_SPACE\t\t\t= Key_GetKeyCode(\"space\");\n\tK_LEFTARROW\t\t= Key_GetKeyCode(\"leftarrow\");\n\tK_RIGHTARROW\t= Key_GetKeyCode(\"rightarrow\");\n\n\tM_Serverlist_InitCvars();\n\treturn true;\n\t\n}\n\n//doesn't use args or return value\nint Plugin_CvarUpdate(int *args)\n{\n\tvmcvar_t *v;\n\tint i;\n\tfor (v = cvarlist[0],i=0; i < sizeof(cvarlist)/sizeof(cvarlist[0]); v++, i++)\n\t\tv->modificationcount = Cvar_Update(v->handle, v->modificationcount, v->string, &v->value);\n\treturn 0;\n}\n\nstatic void NM_DrawCharacter (int cx, int line, unsigned int num)\n{\n\tDraw_Character(cx, line, num);\n}\nstatic void NM_Print (int cx, int cy, qbyte *str)\n{\n\twhile (*str)\n\t{\n\t\tDraw_Character (cx, cy, (*str)|128);\n\t\tstr++;\n\t\tcx += 8;\n\t}\n}\n\n\n\n\n\nqboolean M_IsFiltered(serverinfo_t *server)\t//figure out if we should filter a server.\n{\n\tif (slist_type == SLISTTYPE_FAVORITES)\n\t\tif (!(server->special & SS_FAVORITE))\n\t\t\treturn true;\n#ifdef Q2CLIENT\n\tif (sb_hidequake2.value)\n#endif\n\t\tif (server->special & SS_QUAKE2)\n\t\t\treturn true;\n#ifdef NQPROT\n\tif (sb_hidenetquake.value)\n#endif\n\t\tif (server->special & SS_NETQUAKE)\n\t\t\treturn true;\n\tif (sb_hidequakeworld.value)\n\t\tif (!(server->special & (SS_QUAKE2|SS_NETQUAKE)))\n\t\t\treturn true;\n\tif (sb_hideempty.value)\n\t\tif (!server->players)\n\t\t\treturn true;\n\tif (sb_hidenotempty.value)\n\t\tif (server->players)\n\t\t\treturn true;\n\tif (sb_hidefull.value)\n\t\tif (server->players == server->maxplayers)\n\t\t\treturn true;\n\tif (sb_hidedead.value)\n\t\tif (server->maxplayers == 0)\n\t\t\treturn true;\n\tif (sb_maxping.value)\n\t\tif (server->ping > sb_maxping.value)\n\t\t\treturn true;\n\tif (*sb_gamedir.string)\n\t\tif (strcmp(server->gamedir, sb_gamedir.string))\n\t\t\treturn true;\n\tif (*sb_mapname.string)\n\t\tif (!strstr(server->map, sb_mapname.string))\n\t\t\treturn true;\n\t\n\treturn false;\n}\n\nqboolean M_MasterIsFiltered(master_t *mast)\n{\n#ifndef Q2CLIENT\n\tif (mast->type == MT_BCASTQ2 || mast->type == MT_SINGLEQ2 || mast->type == MT_MASTERQ2)\n\t\treturn true;\n#endif\n#ifndef NQPROT\n\tif (mast->type == MT_BCASTNQ || mast->type == MT_SINGLENQ)\n\t\treturn true;\n#endif\n\treturn false;\n}\n\nstatic int\tSbar_ColorForMap (int m)\n{\n\tm = (m < 0) ? 0 : ((m > 13) ? 13 : m);\n\n\tm *= 16;\n\treturn m < 128 ? m + 8 : m + 8;\n}\n\nvoid M_DrawOneServer (int inity)\n{\n\tchar\tkey[512];\n\tchar\tvalue[512];\n\tchar\t*o;\n\tint\t\tl, i;\n\tchar *s;\n\t\n\tint miny=8*5;\n\tint y=8*(5-selectedserver.linenum);\t\n\n\tminy += inity;\n\ty += inity;\n\n\tif (!selectedserver.detail)\n\t{\n\t\tNM_Print (0, y, \"No details\\n\");\n\t\treturn;\n\t}\n\n\ts = selectedserver.detail->info;\n\n\tif (*s == '\\\\')\n\t\ts++;\n\twhile (*s)\n\t{\n\t\to = key;\n\t\twhile (*s && *s != '\\\\')\n\t\t\t*o++ = *s++;\n\n\t\tl = o - key;\n//\t\tif (l < 20)\n//\t\t{\n//\t\t\tmemset (o, ' ', 20-l);\n//\t\t\tkey[20] = 0;\n//\t\t}\n//\t\telse\n\t\t\t*o = 0;\n\t\tif (y>=miny)\n\t\t\tNM_Print (0, y, va(\"%19s\", key));\n\n\t\tif (!*s)\n\t\t{\n\t\t\tif (y>=miny)\n\t\t\t\tNM_Print (0, y, \"MISSING VALUE\\n\");\n\t\t\treturn;\n\t\t}\n\n\t\to = value;\n\t\ts++;\n\t\twhile (*s && *s != '\\\\')\n\t\t\t*o++ = *s++;\n\t\t*o = 0;\n\n\t\tif (*s)\n\t\t\ts++;\n\t\tif (y>=miny)\n\t\t\tNM_Print (320/2, y, va(\"%s\\n\", value));\n\n\t\ty+=8;\n\t}\n\n\tfor ( i = 0; i < selectedserver.detail->numplayers; i++)\n\t{\n\t\tif (y>=miny)\n\t\t{\n\t\t\tif (selectedserver.detail->players[i].frags>=-999)\t//wow, too low, assume mvd spectators...\n\t\t\t{\n\t\t\t\tDraw_Colourp(Sbar_ColorForMap(selectedserver.detail->players[i].topc));\n\t\t\t\tDraw_Fill (12, y, 28, 4);\n\t\t\t\tDraw_Colourp(Sbar_ColorForMap(selectedserver.detail->players[i].botc));\n\t\t\t\tDraw_Fill (12, y+4, 28, 4);\n\t\t\t\tDraw_Colour3f(1,1,1);\n\t\t\t\tNM_Print (12, y, va(\"%3i\", selectedserver.detail->players[i].frags));\n\t\t\t}\n\t\t\tNM_Print (12+8*4, y, selectedserver.detail->players[i].name);\n\t\t}\n\t\ty+=8;\n\t}\n\n\tif (y<=miny)\t//whoops, there was a hole at the end, try scrolling up.\n\t\tselectedserver.linenum--;\n}\n\nchar *Info_ValueForKey(char *buffer, char *key)\n{\n\tchar *s;\n\tchar *e;\n\tstatic char ret[64];\n\tfor (s = buffer; *s; s++)\n\t{\n\t\tif (*s == '\\\\')\n\t\t{\t//key starts here\n\t\t\tfor (e = s+1; *e; e++)\n\t\t\t{\n\t\t\t\t//find value start\n\t\t\t\tif (*e == '\\\\')\n\t\t\t\t{\n\t\t\t\t\tif (!strncmp(s+1, key, e - s-1))\n\t\t\t\t\t{\n\t\t\t\t\t\t//find the next \\\\ or \\0\n\t\t\t\t\t\ts = e;\n\t\t\t\t\t\tfor (e = s+1; *e && *e != '\\\\'; e++)\n\t\t\t\t\t\t{\n\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (e-s>=sizeof(ret))\n\t\t\t\t\t\t\te = s + 63;\n\t\t\t\t\t\tstrncpy(ret, s+1, e-s-1);\n\t\t\t\t\t\tret[e-s-1] = '\\0';\n\t\t\t\t\t\treturn ret;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\ts = e;\n\t\t}\n\t}\n\treturn \"\";\n}\n\nint M_AddColumn (int right, int y, char *text, int maxchars)\n{\n\tint left;\n\tleft = right - maxchars*8;\n\tif (left < 0)\n\t\treturn right;\n\n\tright = left;\n\twhile (*text && maxchars>0)\n\t{\n\t\tNM_DrawCharacter (right, y, *text);\n\t\ttext++;\n\t\tright += 8;\n\t\tmaxchars--;\n\t}\n\treturn left;\n}\nvoid M_DrawServerList(void)\n{\n\tserverinfo_t *server;\n\tint op=0, filtered=0;\n\tint snum=0;\n\tint blink = 0;\n\n\tint x;\n\tint y = 8*3;\n\n\tCL_QueryServers();\n\t\n\tslist_numoptions = 0;\n\n\t//find total servers.\n\tfor (server = firstserver; server; server = server->next)\n\t\tif (M_IsFiltered(server))\n\t\t\tfiltered++;\n\t\telse\n\t\t\tslist_numoptions++;\n\n\tif (!slist_numoptions)\n\t{\n\t\tchar *text, *text2=\"\", *text3=\"\";\n\t\tif (filtered)\n\t\t{\n\t\t\tif (slist_type == SLISTTYPE_FAVORITES)\n\t\t\t{\n\t\t\t\ttext\t= \"Highlight a server\";\n\t\t\t\ttext2\t= \"and press \\'f\\'\";\n\t\t\t\ttext3\t= \"to add it to this list\";\n\t\t\t}\n\t\t\telse\n\t\t\t\ttext = \"All servers were filtered out\";\n\t\t}\n\t\telse\n\t\t\ttext = \"No servers found\";\n\t\tNM_Print((vid.width-strlen(text)*8)/2, 8*5, text);\n\t\tNM_Print((vid.width-strlen(text2)*8)/2, 8*5+8, text2);\n\t\tNM_Print((vid.width-strlen(text3)*8)/2, 8*5+16, text3);\n\n\t\treturn;\n\t}\n\n\t\n\tif (slist_option >= slist_numoptions)\n\t\tslist_option = slist_numoptions-1;\n\top = vid.height/2/8;\n\top/=2;\n\top=slist_option-op;\n\tsnum = op;\n\n\n\tif (selectedserver.inuse == true)\n\t{\n\t\tM_DrawOneServer(8*5);\n\t\treturn;\n\t}\n\n\tif (op < 0)\n\t\top = 0;\n\tif (snum < 0)\n\t\tsnum = 0;\n\t//find the server that we want\n\tfor (server = firstserver; op>0; server=server->next)\n\t{\n\t\tif (M_IsFiltered(server))\n\t\t\tcontinue;\n\t\top--;\n\t}\n\n\ty = 8*2;\n\tx = vid.width;\n\tif (sb_showtimelimit.value)\n\t\tx = M_AddColumn(x, y, \"tl\",\t\t\t3);\n\tif (sb_showfraglimit.value)\n\t\tx = M_AddColumn(x, y, \"fl\",\t\t\t3);\n\tif (sb_showplayers.value)\n\t\tx = M_AddColumn(x, y, \"plyrs\",\t\t6);\n\tif (sb_showmap.value)\n\t\tx = M_AddColumn(x, y, \"map\",\t\t9);\n\tif (sb_showgamedir.value)\n\t\tx = M_AddColumn(x, y, \"gamedir\",\t9);\n\tif (sb_showping.value)\n\t\tx = M_AddColumn(x, y, \"png\",\t\t4);\n\tif (sb_showaddress.value)\n\t\tx = M_AddColumn(x, y, \"address\",\t21);\n\tx = M_AddColumn(x, y, \"name\",\t\tx/8-1);\n\n\ty = 8*3;\n\twhile(server)\n\t{\n\t\tif (M_IsFiltered(server))\n\t\t{\n\t\t\tserver = server->next;\n\t\t\tcontinue;\t//doesn't count\n\t\t}\n\n\t\tif (y > vid.height/2)\n\t\t\tbreak;\n\n\t\tif (slist_option == snum)\n\t\t\tblink = (msecstime/333)&1;\n\t\tif (*server->name)\n\t\t{\n\t\t\tif (blink)\n\t\t\t\tDraw_Colour3f(1,1,0);\n\t\t\telse if (server->special & SS_FAVORITE)\n\t\t\t\tDraw_Colour3f(0,1,0);\n\t\t\telse if (server->special & SS_FTESERVER)\n\t\t\t\tDraw_Colour3f(1,0,0);\n\t\t\telse if (server->special & SS_QUAKE2)\n\t\t\t\tDraw_Colour3f(0,0,1);\n\t\t\telse if (server->special & SS_NETQUAKE)\n\t\t\t\tDraw_Colour3f(1,0,1);\n\t\t\telse\n\t\t\t\tDraw_Colour3f(1,1,1);\n\n\t\t\tx = vid.width;\n\n\t\t\tif (sb_showtimelimit.value)\n\t\t\t\tx = M_AddColumn(x, y, va(\"%i\", server->tl),\t\t\t3);\t//time limit\n\t\t\tif (sb_showfraglimit.value)\n\t\t\t\tx = M_AddColumn(x, y, va(\"%i\", server->fl),\t\t\t3);\t//frag limit\n\t\t\tif (sb_showplayers.value)\n\t\t\t\tx = M_AddColumn(x, y, va(\"%i/%i\", server->players, server->maxplayers),\t\t\t6);\n\t\t\tif (sb_showmap.value)\n\t\t\t\tx = M_AddColumn(x, y, server->map,\t\t9);\n\t\t\tif (sb_showgamedir.value)\n\t\t\t\tx = M_AddColumn(x, y, server->gamedir,\t9);\n\t\t\tif (sb_showping.value)\n\t\t\t\tx = M_AddColumn(x, y, va(\"%i\", server->ping),\t\t\t4);\t//frag limit\n\t\t\tif (sb_showaddress.value)\n\t\t\t\tx = M_AddColumn(x, y, NET_AdrToString(&server->adr),\t21);\n\t\t\tx = M_AddColumn(x, y, server->name,\t\tx/8-1);\n\t\t}\n\n\t\tblink = 0;\n\t\tif (*server->name)\n\t\t\ty+=8;\n\n\t\tserver = server->next;\n\n\t\tsnum++;\n\t}\n\n\tDraw_Colour3f(1,1,1);\n\n\tselectedserver.inuse=2;\n\tM_DrawOneServer(vid.height/2-4*8);\n}\n\nvoid M_DrawSources (void)\n{\n\tint blink;\n\tint snum=0;\n\tint op;\n\tint y = 3*8;\n\tmaster_t *mast;\n\n\tslist_numoptions = 0;\n\t//find total sources.\n\tfor (mast = master; mast; mast = mast->next)\n\t\tslist_numoptions++;\n\n\tif (!slist_numoptions)\n\t{\n\t\tchar *text;\n\t\tif (0)//filtered)\n\t\t\ttext = \"All servers were filtered out\\n\";\n\t\telse\n\t\t\ttext = \"No sources were found\\n\";\n\t\tNM_Print((vid.width-strlen(text)*8)/2, 8*5, text);\n\n\t\treturn;\n\t}\n\n\tif (slist_option >= slist_numoptions)\n\t\tslist_option = slist_numoptions-1;\n\top=slist_option-vid.height/2/8;\n\tsnum = op;\n\n\tif (op < 0)\n\t\top = 0;\n\tif (snum < 0)\n\t\tsnum = 0;\n\t//find the server that we want\n\tfor (mast = master; op>0; mast=mast->next)\n\t{\n\t\tif (M_MasterIsFiltered(mast))\n\t\t\tcontinue;\n\t\top--;\n\t}\n\n\tfor (; mast; mast = mast->next)\n\t{\n\t\tif (M_MasterIsFiltered(mast))\n\t\t\tcontinue;\n\n\t\tif (slist_option == snum)\n\t\t\tblink = (msecstime/333)&1;\n\t\telse\n\t\t\tblink = 0;\n\n\t\tif (blink)\n\t\t\tDraw_Colour3f(0,1,1);\n\t\telse\n\t\t\tif (mast->type == MT_MASTERQW || mast->type == MT_MASTERQ2)\n\t\t\tDraw_Colour3f(1,1,1);\n#ifdef NQPROT\n\t\telse if (mast->type == MT_SINGLENQ)\n\t\t\tDraw_Colour3f(0,1,0);\n#endif\n\t\telse if (mast->type == MT_SINGLEQW || mast->type == MT_SINGLEQ2)\n\t\t\tDraw_Colour3f(0,0,1);\n\t\telse\n\t\t\tDraw_Colour3f(1,0,0);\n\n\t\tNM_Print(46, y, va(\"%s\", mast->name));\t//white.\n\t\ty+=8;\n\t\tsnum++;\n\t}\n}\n\n#define NUMSLISTOPTIONS (7+7+3)\n\tstruct {\n\t\tchar *title;\n\t\tvmcvar_t *cvar;\n\t\tint type;\n\t} options[NUMSLISTOPTIONS] = {\n\t\t{\"Hide Empty\",\t\t&sb_hideempty},\n\t\t{\"Hide Not Empty\",\t&sb_hidenotempty},\n\t\t{\"Hide Full\",\t\t&sb_hidefull},\n\t\t{\"Hide Dead\",\t\t&sb_hidedead},\n\t\t{\"Hide Quake 2\",\t&sb_hidequake2},\n\t\t{\"Hide Quake 1\",\t&sb_hidenetquake},\n\t\t{\"Hide QuakeWorld\",\t&sb_hidequakeworld},\n\n\t\t{\"Show pings\",\t\t&sb_showping},\n\t\t{\"Show Addresses\",\t&sb_showaddress},\n\t\t{\"Show map\",\t\t&sb_showmap},\n\t\t{\"Show Game Dir\",\t&sb_showgamedir},\n\t\t{\"Show Players\",\t&sb_showplayers},\n\t\t{\"Show Fraglimit\",\t&sb_showfraglimit},\n\t\t{\"Show Timelimit\",\t&sb_showtimelimit},\n\n\t\t{\"Max ping\",\t\t&sb_maxping,\t1},\n\t\t{\"GameDir\",\t\t\t&sb_gamedir,\t2},\n\t\t{\"Using map\",\t\t&sb_mapname,\t2}\n\t};\n\nvoid M_DrawSListOptions (void)\n{\n\tint op;\n\n\tslist_numoptions = NUMSLISTOPTIONS;\n\n\tfor (op = 0; op < NUMSLISTOPTIONS; op++)\n\t{\n\t\tif (slist_option == op && (msecstime/333)&1)\n\t\t\tDraw_Colour3f(0.5, 0.5, 1);\n\t\telse\n\t\t{\n\t\t\tif (options[op].cvar->value>0 || (*options[op].cvar->string && *options[op].cvar->string != '0'))\n\t\t\t\tDraw_Colour3f(1, 0, 0);\n\t\t\telse\n\t\t\t\tDraw_Colour3f(1, 1, 1);\n\t\t}\n\t\tswitch(options[op].type)\n\t\t{\n\t\tdefault:\n\t\t\tNM_Print(46, op*8+8*3, options[op].title);\n\t\t\tbreak;\n\t\tcase 1:\n\t\t\tif (!options[op].cvar->value)\n\t\t\t{\n\t\t\t\tNM_Print(46, op*8+8*3, va(\"%s \", options[op].title));\n\t\t\t\tbreak;\n\t\t\t}\n\t\tcase 2:\n\t\t\tNM_Print(46, op*8+8*3, va(\"%s %s\", options[op].title, options[op].cvar->string));\n\t\t\tbreak;\n\t\t}\n\t}\n}\n\nvoid M_SListOptions_Key (int key)\n{\n\tif (key == K_UPARROW)\n\t{\n\t\tslist_option--;\n\t\tif (slist_option<0)\n\t\t\tslist_option=0;\n\t}\n\telse if (key == K_DOWNARROW)\n\t{\n\t\tslist_option++;\n\t\tif (slist_option >= slist_numoptions)\n\t\t\tslist_option = slist_numoptions-1;\n\t}\n\t\n\tswitch(options[slist_option].type)\n\t{\n\tdefault:\n\t\tif (key == K_ENTER)\n\t\t{\n\t\t\tif (options[slist_option].cvar->value)\n\t\t\t\tVMCvar_SetString(options[slist_option].cvar, \"0\");\n\t\t\telse\n\t\t\t\tVMCvar_SetString(options[slist_option].cvar, \"1\");\n\t\t}\n\t\tbreak;\n\tcase 1:\n\t\tif (key >= '0' && key <= '9')\n\t\t\tVMCvar_SetFloat(options[slist_option].cvar, options[slist_option].cvar->value*10+key-'0');\n\t\telse if (key == K_DEL)\n\t\t\tVMCvar_SetFloat(options[slist_option].cvar, 0);\n\t\telse if (key == K_BACKSPACE)\n\t\t\tVMCvar_SetFloat(options[slist_option].cvar, (int)options[slist_option].cvar->value/10);\n\t\tbreak;\n\tcase 2:\n\t\tif ((key >= '0' && key <= '9') || (key >= 'a' && key <= 'z') || key == '_')\n\t\t\tVMCvar_SetString(options[slist_option].cvar, va(\"%s%c\", options[slist_option].cvar->string, key));\n\t\telse if (key == K_DEL)\n\t\t\tVMCvar_SetString(options[slist_option].cvar, \"\");\n\t\telse if (key == K_BACKSPACE)\t//FIXME\n\t\t\tVMCvar_SetString(options[slist_option].cvar, \"\");\n\t\tbreak;\n\t}\n}\n\n\nvoid M_DrawServers(void)\n{\n#define NUMSLISTHEADERS (SLISTTYPE_OPTIONS+1)\n\tchar *titles[NUMSLISTHEADERS] = {\n\t\t\"Servers\",\n\t\t\"Favorites\",\n\t\t\"Sources\",\n//\t\t\"Players\",\n\t\t\"Options\"\n\t};\n\tint snum=0;\n\n\tint width, lofs;\n\n\tNET_CheckPollSockets();\t//see if we were told something important.\n\n\twidth = vid.width / NUMSLISTHEADERS;\n\tlofs = width/2 - 7*4;\n\tfor (snum = 0; snum < NUMSLISTHEADERS; snum++)\n\t{\n\t\tNM_Print(width*snum+width/2 - strlen(titles[snum])*4, slist_type==snum, titles[snum]);\n\t}\n\tNM_Print(8, 8, \"\\35\");\n\tfor (snum = 16; snum < vid.width-16; snum+=8)\n\t\tNM_Print(snum, 8, \"\\36\");\n\tNM_Print(snum, 8, \"\\37\");\n\n\tswitch(slist_type)\n\t{\n\tcase SLISTTYPE_SERVERS:\n\tcase SLISTTYPE_FAVORITES:\n\t\tM_DrawServerList();\n\t\tbreak;\n\tcase SLISTTYPE_SOURCES:\n\t\tM_DrawSources ();\n\t\tbreak;\n\tcase SLISTTYPE_OPTIONS:\n\t\tM_DrawSListOptions ();\n\t\tbreak;\n\t}\n}\n\nserverinfo_t *M_FindCurrentServer(void)\n{\n\tserverinfo_t *server;\n\tint op = slist_option;\n\tfor (server = firstserver; server; server = server->next)\n\t{\n\t\tif (M_IsFiltered(server))\n\t\t\tcontinue;\t//doesn't count\n\t\tif (!op--)\n\t\t\treturn server;\n\t}\n\treturn NULL;\n}\n\nmaster_t *M_FindCurrentMaster(void)\n{\n\tmaster_t *mast;\n\tint op = slist_option;\n\tfor (mast = master; mast; mast = mast->next)\n\t{\n\t\tif (M_MasterIsFiltered(mast))\n\t\t\tcontinue;\n\t\tif (!op--)\n\t\t\treturn mast;\n\t}\n\treturn NULL;\n}\n\nvoid M_SListKey(int key)\n{\n\tif (key == K_ESCAPE)\n\t{\n//\t\tif (selectedserver.inuse)\n//\t\t\tselectedserver.inuse = false;\n//\t\telse\n\t\t{\n\t\t\tMenu_Control(0);\n//\t\t\tCmd_AddText(\"togglemenu\\n\", false);\n\t\t}\n\t\treturn;\n\t}\n\telse if (key == K_LEFTARROW)\n\t{\n\t\tslist_type--;\n\t\tif (slist_type<0)\n\t\t\tslist_type=0;\n\n\t\tselectedserver.linenum--;\n\t\tif (selectedserver.linenum<0)\n\t\t\tselectedserver.linenum=0;\n\n\t\tslist_numoptions=0;\n\t\treturn;\n\t}\n\telse if (key == K_RIGHTARROW)\n\t{\n\t\tslist_type++;\n\t\tif (slist_type>NUMSLISTHEADERS-1)\n\t\t\tslist_type=NUMSLISTHEADERS-1;\n\n\t\tselectedserver.linenum++;\n\n\t\tslist_numoptions = 0;\n\t\treturn;\n\t}\n\telse if (key == 'q')\n\t\tselectedserver.linenum--;\n\telse if (key == 'a')\n\t\tselectedserver.linenum++;\n\n\tif (!slist_numoptions)\n\t\treturn;\n\n\tif (slist_type == SLISTTYPE_OPTIONS)\n\t{\n\t\tM_SListOptions_Key(key);\n\t\treturn;\n\t}\n\t\n\tif (key == K_UPARROW)\n\t{\n\t\tslist_option--;\n\t\tif (slist_option<0)\n\t\t\tslist_option=0;\n\n\t\tif (slist_type == SLISTTYPE_SERVERS)\n\t\t\tSListOptionChanged(M_FindCurrentServer());\t//go for these early.\n\t}\n\telse if (key == K_DOWNARROW)\n\t{\n\t\tslist_option++;\n\t\tif (slist_option >= slist_numoptions)\n\t\t\tslist_option = slist_numoptions-1;\n\n\t\tif (slist_type == SLISTTYPE_SERVERS)\n\t\t\tSListOptionChanged(M_FindCurrentServer());\t//go for these early.\n\t}\n\telse if (key == K_PGDN)\n\t{\n\t\tslist_option+=10;\n\t\tif (slist_option >= slist_numoptions)\n\t\t\tslist_option = slist_numoptions-1;\n\n\t\tif (slist_type == SLISTTYPE_SERVERS)\n\t\t\tSListOptionChanged(M_FindCurrentServer());\t//go for these early.\n\t}\n\telse if (key == K_PGUP)\n\t{\n\t\tslist_option-=10;\n\t\tif (slist_option<0)\n\t\t\tslist_option=0;\n\n\t\tif (slist_type == SLISTTYPE_SERVERS)\n\t\t\tSListOptionChanged(M_FindCurrentServer());\t//go for these early.\n\t}\n\telse if (key == 'r')\n\t\tMasterInfo_Begin();\n\telse if (key == K_SPACE)\n\t{\n\t\tif (slist_type == SLISTTYPE_SERVERS)\n\t\t{\n\t\t\tselectedserver.inuse = !selectedserver.inuse;\n\t\t\tif (selectedserver.inuse)\n\t\t\t\tSListOptionChanged(M_FindCurrentServer());\n\t\t}\n\t}\n\telse if (key == 'f')\n\t{\n\t\tserverinfo_t *server;\n\t\tif (slist_type == SLISTTYPE_SERVERS)\t//add to favorites\n\t\t{\n\t\t\tserver = M_FindCurrentServer();\n\t\t\tif (server)\n\t\t\t{\n\t\t\t\tserver->special |= SS_FAVORITE;\n\t\t\t\tMasterInfo_WriteServers();\n\t\t\t}\n\t\t}\n\t\tif (slist_type == SLISTTYPE_FAVORITES)\t//remove from favorites\n\t\t{\n\t\t\tserver = M_FindCurrentServer();\n\t\t\tif (server)\n\t\t\t{\n\t\t\t\tserver->special &= ~SS_FAVORITE;\n\t\t\t\tMasterInfo_WriteServers();\n\t\t\t}\n\t\t}\n\t}\n\telse if (key==K_ENTER)\n\t{\n\t\tserverinfo_t *server;\n\t\tif (slist_type == SLISTTYPE_SERVERS || slist_type == SLISTTYPE_FAVORITES)\n\t\t{\n\t\t\tif (!selectedserver.inuse)\n\t\t\t{\n\t\t\t\tselectedserver.inuse = true;\n\t\t\t\tSListOptionChanged(M_FindCurrentServer());\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tserver = M_FindCurrentServer();\n\t\t\tif (!server)\n\t\t\t\treturn;\t//ah. off the end.\n\n\t\t\tif (server->special & SS_NETQUAKE)\n\t\t\t\tCmd_AddText(va(\"nqconnect %s\\n\", NET_AdrToString(&server->adr)), false);\n\t\t\telse\n\t\t\t\tCmd_AddText(va(\"connect %s\\n\", NET_AdrToString(&server->adr)), false);\n\t\t}\n\t\telse if (slist_type == SLISTTYPE_SOURCES)\n\t\t{\n\t\t\tMasterInfo_Request(M_FindCurrentMaster());\n\t\t}\n\n\t\treturn;\n\t}\t\n}\n\n"
  },
  {
    "path": "plugins/serverb/net_master.c",
    "content": "#include \"cl_master.h\"\n\n#define NET_GAMENAME_NQ\t\t\"QUAKE\"\n\n//rename to cl_master.c sometime\n\n//the networking operates seperatly from the main code. This is so we can have full control over all parts of the server sending prints.\n//when we send status to the server, it replys with a print command. The text to print contains the serverinfo.\n//Q2's print command is a compleate 'print', while qw is just a 'p', thus we can distinguish the two easily.\n\n//save favorites and allow addition of new ones from game?\n//add filters some time\n\n//remove dead servers.\n//master was polled a minute ago and server was not on list - server on multiple masters would be awkward.\n\n\nextern int msecstime;\n\n//the number of servers should be limited only by memory.\n\nvmcvar_t slist_cacheinfo = {\"slist_cacheinfo\", \"0\"};\t//this proves dangerous, memory wise.\nvmcvar_t slist_writeserverstxt = {\"slist_writeservers\", \"0\"};\n\nvoid CL_MasterListParse(qbyte *buffer, qbyte *maxbuffer, qboolean isq2);\nvoid CL_QueryServers(void);\nint CL_ReadServerInfo(char *msg, int servertype, qboolean favorite, struct sockaddr_in net_from);\n\nmaster_t *master;\nplayer_t *mplayers;\nserverinfo_t *firstserver;\n\n\n#define POLLUDPSOCKETS 64\t//it's big so we can have lots of messages when behind a firewall. Basically if a firewall only allows replys, and only remembers 3 servers per socket, we need this big cos it can take a while for a packet to find a fast optimised route and we might be waiting for a few secs for a reply the first time around.\nSOCKET pollsocketsUDP[POLLUDPSOCKETS];\nint lastpollsockUDP;\n\nqboolean NET_StringToAdr(const char *s, struct sockaddr_in *a)\n{\n\tchar\tcopy[128];\n\tchar *colon;\n\tstruct hostent *he;\n\n\tstrcpy(copy, s);\n\tcolon = strchr(copy, ':');\n\tif (colon)\n\t{\n\t\ta->sin_port = htons((unsigned short)atoi(colon+1));\n\t\t*colon = '\\0';\n\t}\n\telse\n\t\ta->sin_port = 0;\n\n\t*(unsigned int*)&a->sin_addr = inet_addr(copy);\n\ta->sin_family = AF_INET;\n\n\tif (*(unsigned int*)&a->sin_addr != INADDR_NONE)\n\t\treturn true;\n\n\the = gethostbyname(copy);\n\tif (he)\n\t{\n\t\tif (he->h_addrtype != AF_INET)\n\t\t\treturn false;\t//whoops...\n\t\t\t\n\t\t*(int*)&a->sin_addr = *(int*)he->h_addr;\n\n\t\treturn true;\n\t}\n\n\treturn false;\n}\nqboolean NET_CompareAdr(const struct sockaddr_in *a, const struct sockaddr_in *b)\n{\n\tif (a->sin_family != b->sin_family)\n\t\treturn false;\n\tif (*(int*)&a->sin_addr != *(int*)&b->sin_addr)\n\t\treturn false;\n\tif (a->sin_port != b->sin_port)\n\t\treturn false;\n\treturn true;\n}\nchar *NET_AdrToString(const struct sockaddr_in *a)\n{\n\tstatic char buffer[24];\n\tconst qbyte *ad = (const qbyte *)&a->sin_addr;\n\tsnprintf(buffer, sizeof(buffer), \"%i.%i.%i.%i:%i\", ad[0], ad[1], ad[2], ad[3], ntohs(a->sin_port));\n\treturn buffer;\n}\n\nvoid Master_AddMaster (char *address, int type, char *description)\n{\n\tstruct sockaddr_in adr;\n\tmaster_t *mast;\n\n\tif (!NET_StringToAdr(address, &adr))\n\t{\n\t\tCon_Printf(\"Failed to resolve address \\\"%s\\\"\\n\", address);\n\t\treturn;\n\t}\n\n\tfor (mast = master; mast; mast = mast->next)\n\t{\n\t\tif (NET_CompareAdr(&mast->adr, &adr))\t//already exists.\n\t\t\treturn;\n\t}\n\tmast = malloc(sizeof(master_t)+strlen(description));\n\tmast->adr = adr;\n\tmast->type = type;\n\tstrcpy(mast->name, description);\n\n\tmast->next = master;\n\tmaster = mast;\n}\n\n//build a linked list of masters.\tDoesn't duplicate addresses.\nqboolean Master_LoadMasterList (char *filename, int defaulttype, int depth)\n{\n\n\treturn false;\n/*\n\textern char\tcom_basedir[MAX_OSPATH];\n\tFILE *f;\n\tchar line[1024];\n\tchar file[1024];\n\tchar *name, *next;\n\tint servertype;\n\n\tif (depth <= 0)\n\t\treturn false;\n\tdepth--;\n\n\tf = fopen(va(\"%s/%s\", com_basedir, filename), \"rb\");\n\tif (!f)\n\t\treturn false;\n\n\twhile(fgets(line, sizeof(line)-1, f))\n\t{\n\t\tif (*line == '#')\t//comment\n\t\t\tcontinue;\n\n\t\tnext = COM_Parse(line);\n\t\tif (!*com_token)\n\t\t\tcontinue;\n\n\t\tif (!strcmp(com_token, \"file\"))\t//special case. Add a port if you have a server named 'file'... (unlikly)\n\t\t{\n\t\t\tnext = COM_Parse(next);\n\t\t\tif (!next)\n\t\t\t\tcontinue;\n\t\t\tQ_strncpyz(file, com_token, sizeof(file));\n\t\t}\n\t\telse\n\t\t\t*file = '\\0';\n\n\t\t*next = '\\0';\n\t\tnext++;\n\t\tname = COM_Parse(next);\n\t\tservertype = -1;\n\n\t\tif (!strcmp(com_token, \"single:qw\"))\n\t\t\tservertype = MT_SINGLEQW;\n\t\telse if (!strcmp(com_token, \"single:q2\"))\n\t\t\tservertype = MT_SINGLEQ2;\n\t\telse if (!strcmp(com_token, \"single:nq\") || !strcmp(com_token, \"single:q1\"))\n\t\t\tservertype = MT_SINGLENQ;\n\t\telse if (!strcmp(com_token, \"single\"))\n\t\t\tservertype = MT_SINGLEQW;\n\n\t\telse if (!strcmp(com_token, \"master:qw\"))\n\t\t\tservertype = MT_MASTERQW;\n\t\telse if (!strcmp(com_token, \"master:q2\"))\n\t\t\tservertype = MT_MASTERQ2;\n\t\telse if (!strcmp(com_token, \"master\"))\t//any other sort of master, assume it's a qw master.\n\t\t\tservertype = MT_MASTERQW;\n\n\t\telse if (!strcmp(com_token, \"bcast:qw\"))\n\t\t\tservertype = MT_BCASTQW;\n\t\telse if (!strcmp(com_token, \"bcast:q2\"))\n\t\t\tservertype = MT_BCASTQ2;\n\t\telse if (!strcmp(com_token, \"bcast:nq\"))\n\t\t\tservertype = MT_BCASTNQ;\n\t\telse if (!strcmp(com_token, \"bcast\"))\n\t\t\tservertype = MT_BCASTQW;\n\n\t\telse if (!strcmp(com_token, \"favorite:qw\"))\n\t\t\tservertype = -MT_SINGLEQW;\n\t\telse if (!strcmp(com_token, \"favorite:q2\"))\n\t\t\tservertype = -MT_SINGLEQ2;\n\t\telse if (!strcmp(com_token, \"favorite:nq\"))\n\t\t\tservertype = -MT_SINGLENQ;\n\t\telse if (!strcmp(com_token, \"favorite\"))\n\t\t\tservertype = -MT_SINGLEQW;\n\n\n\t\telse\n\t\t{\n\t\t\tname = next;\t//go back one token.\n\t\t\tservertype = defaulttype;\n\t\t}\n\n\t\twhile(*name <= ' ' && *name != 0)\t//skip whitespace\n\t\t\tname++;\n\n\t\tnext = name + strlen(name)-1;\n\t\twhile(*next <= ' ' && next > name)\n\t\t{\n\t\t\t*next = '\\0';\n\t\t\tnext--;\n\t\t}\n\n\n\t\tif (*file)\n\t\t\tMaster_LoadMasterList(file, servertype, depth);\n\t\telse if (servertype < 0)\n\t\t{\n\t\t\tif (NET_StringToAdr(line, &net_from))\n\t\t\t\tCL_ReadServerInfo(va(\"\\\\hostname\\\\%s\", name), -servertype, true);\n\t\t\telse\n\t\t\t\tCon_Printf(\"Failed to resolve address - \\\"%s\\\"\\n\", line);\n\t\t}\n\t\telse\n\t\t\tMaster_AddMaster(line, servertype, name);\n\t}\n\tfclose(f);\n\n\treturn true;\n*/\n}\n\nint UDP_OpenSocket (int port, qboolean bcast)\n{\n\tint newsocket;\n\tstruct sockaddr_in address;\n\tunsigned long _true = true;\n\tint i;\nint maxport = port + 100;\n\n\tif ((newsocket = socket (PF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1)\n\t\tSys_Errorf (\"UDP_OpenSocket: socket: %s\", strerror(qerrno));\n\n\tif (ioctlsocket (newsocket, FIONBIO, &_true) == -1)\n\t\tSys_Errorf (\"UDP_OpenSocket: ioctl FIONBIO: %s\", strerror(qerrno));\n\n\tif (bcast)\n\t{\n\t\t_true = true;\n\t\tif (setsockopt(newsocket, SOL_SOCKET, SO_BROADCAST, (char *)&_true, sizeof(_true)) == -1)\n\t\t{\n\t\t\tCon_Printf(\"Cannot create broadcast socket\\n\");\n\t\t\treturn 0;\n\t\t}\n\t}\n\n\taddress.sin_family = AF_INET;\n\taddress.sin_addr.s_addr = INADDR_ANY;\n\n\tfor(;;)\n\t{\n\t\tif (port == PORT_ANY)\n\t\t\taddress.sin_port = 0;\n\t\telse\n\t\t\taddress.sin_port = htons((short)port);\n\t\n\t\tif( bind (newsocket, (void *)&address, sizeof(address)) == -1)\n\t\t{\n\t\t\tif (!port)\n\t\t\t\tSys_Errorf (\"UDP_OpenSocket: bind: %s\", strerror(qerrno));\n\t\t\tport++;\n\t\t\tif (port > maxport)\n\t\t\t\tSys_Errorf (\"UDP_OpenSocket: bind: %s\", strerror(qerrno));\n\t\t}\n\t\telse\n\t\t\tbreak;\n\t}\n\n\treturn newsocket;\n}\n\nvoid NET_SendPollPacket(int len, void *data, struct sockaddr_in addr)\n{\n\tint ret;\n\n#ifdef USEIPX\n\tif (addr.sa_family == AF_IPX)\n\t{\n\t\tlastpollsockIPX++;\n\t\tif (lastpollsockIPX>=POLLIPXSOCKETS)\n\t\t\tlastpollsockIPX=0;\n\t\tif (!pollsocketsIPX[lastpollsockIPX])\n\t\t\tpollsocketsIPX[lastpollsockIPX] = IPX_OpenSocket(PORT_ANY, true);\n\t\tret = sendto (pollsocketsIPX[lastpollsockIPX], data, len, 0, (struct sockaddr *)&addr, sizeof(addr) );\n\t}\n\telse\n#endif\n\t{\n\t\tlastpollsockUDP++;\n\t\tif (lastpollsockUDP>=POLLUDPSOCKETS)\n\t\t\tlastpollsockUDP=0;\n\t\tif (!pollsocketsUDP[lastpollsockUDP])\n\t\t\tpollsocketsUDP[lastpollsockUDP] = UDP_OpenSocket(PORT_ANY, true);\n\t\tret = sendto (pollsocketsUDP[lastpollsockUDP], data, len, 0, (struct sockaddr *)&addr, sizeof(addr) );\n\t}\n\n\tif (ret == -1)\n\t{\n// wouldblock is silent\n\t\tif (qerrno == EWOULDBLOCK)\n\t\t\treturn;\n\n\t\tif (qerrno == ECONNREFUSED)\n\t\t\treturn;\n\n\t\tif (qerrno != EADDRNOTAVAIL)\n\t\t\tCon_Printf (\"NET_SendPacket ERROR: %i\\n\", qerrno);\n//\t\telse\n//\t\t\tCon_DPrintf(\"NET_SendPacket Warning: %i\\n\", qerrno);\n\t}\n}\n\nint NET_CheckPollSockets(void)\n{\n#define\tMAX_UDP_PACKET\t8192\t//hmm... while on windows, the max size is 1400 or so...\n\tqbyte\t\tnet_message_buffer[MAX_UDP_PACKET];\n\tqbyte\t\t*readpoint;\n\tqbyte\t\t*maxread;\n\tint sock;\n\tSOCKET usesocket;\n\tfor (sock = 0; sock < POLLUDPSOCKETS; sock++)\n\t{\n\t\tint \tret;\n\t\tstruct sockaddr_in\tfrom;\n\t\tint\t\tfromlen;\n\n\t\tusesocket = pollsocketsUDP[sock];\n\n\t\tif (!usesocket)\n\t\t\tcontinue;\n\t\tfromlen = sizeof(from);\n\t\tret = recvfrom (usesocket, (char *)net_message_buffer, sizeof(net_message_buffer), 0, (struct sockaddr *)&from, &fromlen);\n\n\t\tif (ret == -1)\n\t\t{\n\t\t\tif (qerrno == EWOULDBLOCK)\n\t\t\t\tcontinue;\n\t\t\tif (qerrno == EMSGSIZE)\n\t\t\t{\n\t\t\t\tCon_Printf (\"Warning:  Oversize packet from %s\\n\",\n\t\t\t\t\tNET_AdrToString (&from));\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (qerrno == ECONNABORTED || qerrno == ECONNRESET)\n\t\t\t{\n//\t\t\t\tCon_Printf (\"Connection lost or aborted\\n\");\n\t\t\t\tcontinue;\n\t\t\t}\n\n\n\t\t\tCon_Printf (\"NET_CheckPollSockets: %s\", strerror(qerrno));\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (ret >= sizeof(net_message_buffer) )\n\t\t{\n\t\t\tCon_Printf (\"Oversize packet from %s\\n\", NET_AdrToString (&from));\n\t\t\tcontinue;\n\t\t}\n\t\treadpoint = net_message_buffer;\n\t\tmaxread = readpoint+ret;\n\n\t\tif (*(int*)net_message_buffer == 0xffffffff)\n\t\t{\n\t\t\tint c;\n#ifdef Q2CLIENT\n\t\t\tchar *s;\n#endif\n\t\t\treadpoint += 4;\n\n#ifdef Q2CLIENT\n\t\t\ts = strchr('\\n');\n\t\t\tif (!strncmp(readpoint, \"print\", 5))\n\t\t\t{\n\t\t\t\tCL_ReadServerInfo(s+1, MT_SINGLEQ2, false);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\telse if (!strncmp(readpoint, \"info\", 4))\t//parse a bit more...\n\t\t\t{\n\t\t\t\tCL_ReadServerInfo(s+1, MT_SINGLEQ2, false);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\telse if (!strncmp(s, \"servers\", 6))\t//parse a bit more...\n\t\t\t{\n\t\t\t\treadpoint = orp+7;\n\t\t\t\tCL_MasterListParse(readpoint, maxread, true);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\treadpoint = orp;\n#endif\n\n\t\t\tc = *readpoint++;\n\n\t\t\tif (c == A2C_PRINT)\t//qw server reply.\n\t\t\t{\n\t\t\t\tCL_ReadServerInfo(readpoint, MT_SINGLEQW, false, from);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (c == M2C_MASTER_REPLY)\t//qw master reply.\n\t\t\t{\t\t\n\t\t\t\tCL_MasterListParse(readpoint, maxread, false);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n#ifdef NQPROT\n\t\telse\n\t\t{\t//connected packet? Must be a NQ packet.\n\t\t\tchar name[32];\n\t\t\tchar map[16];\n\t\t\tint users, maxusers;\n\n\t\t\tint control;\n\n\t\t\tMSG_BeginReading ();\n\t\t\tcontrol = BigLong(*((int *)net_message.data));\n\t\t\tMSG_ReadLong();\n\t\t\tif (control == -1)\n\t\t\t\tcontinue;\n\t\t\tif ((control & (~NETFLAG_LENGTH_MASK)) !=  NETFLAG_CTL)\n\t\t\t\tcontinue;\n\t\t\tif ((control & NETFLAG_LENGTH_MASK) != ret)\n\t\t\t\tcontinue;\n\n\t\t\tif (MSG_ReadByte() != CCREP_SERVER_INFO)\n\t\t\t\tcontinue;\n\n\t\t\tNET_StringToAdr(MSG_ReadString(), &net_from);\n\n\t\t\tQ_strncpyz(name, MSG_ReadString(), sizeof(name));\n\t\t\tQ_strncpyz(map, MSG_ReadString(), sizeof(map));\n\t\t\tusers = MSG_ReadByte();\n\t\t\tmaxusers = MSG_ReadByte();\n\t\t\tif (MSG_ReadByte() != NET_PROTOCOL_VERSION)\n\t\t\t{\n//\t\t\t\tQ_strcpy(name, \"*\");\n//\t\t\t\tQ_strcat(name, name);\n\t\t\t}\n\n\t\t\tCL_ReadServerInfo(va(\"\\\\hostname\\\\%s\\\\map\\\\%s\\\\maxclients\\\\%i\", name, map, maxusers), MT_SINGLENQ, false);\n\t\t}\n#endif\n\t\tcontinue;\t\t\n\t}\n\treturn 0;\n}\n\nvoid Master_RemoveKeepInfo(serverinfo_t *sv)\n{\n\tsv->special &= ~SS_KEEPINFO;\n\tif (sv->moreinfo)\n\t{\n\t\tfree(sv->moreinfo);\n\t\tsv->moreinfo = NULL;\n\t}\n}\n\nvoid SListOptionChanged(serverinfo_t *newserver)\n{\n\tif (selectedserver.inuse)\n\t{\n\t\tchar data[16];\n\t\tserverinfo_t *oldserver;\n\n\t\tselectedserver.detail = NULL;\n\n\t\tif (!slist_cacheinfo.value)\t//we have to flush it. That's the rules.\n\t\t{\n\t\t\tfor (oldserver = firstserver; oldserver; oldserver=oldserver->next)\n\t\t\t{\n\t\t\t\tif (NET_CompareAdr(&selectedserver.adr, &oldserver->adr))//*(int*)selectedserver.ipaddress == *(int*)server->ipaddress && selectedserver.port == server->port)\n\t\t\t\t{\n\t\t\t\t\tif (oldserver->moreinfo)\n\t\t\t\t\t{\n\t\t\t\t\t\tfree(oldserver->moreinfo);\n\t\t\t\t\t\toldserver->moreinfo = NULL;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (!newserver)\n\t\t\treturn;\n\n\t\tselectedserver.adr = newserver->adr;\n\n\t\tif (newserver->moreinfo)\t//we cached it.\n\t\t{\n\t\t\tselectedserver.detail = newserver->moreinfo;\n\t\t\treturn;\n\t\t}\n//we don't know all the info, so send a request for it.\n\t\tselectedserver.detail = newserver->moreinfo = malloc(sizeof(serverdetailedinfo_t));\n\t\t\n\t\tnewserver->moreinfo->numplayers = newserver->players;\n\t\tsnprintf(newserver->moreinfo->info, sizeof(newserver->moreinfo->info), \"\\\\%s\\\\%s\", \"hostname\", newserver->name);\n\n\t\tnewserver->refreshtime = msecstime;\n\t\tsnprintf(data, sizeof(data), \"%c%c%c%cstatus\\n\", 255, 255, 255, 255);\n\t\tNET_SendPollPacket (strlen(data), data, newserver->adr);\n\t}\n}\n\n\n//don't try sending to servers we don't support\nvoid MasterInfo_Request(master_t *mast)\n{\n\tif (!mast)\n\t\treturn;\n\tswitch(mast->type)\n\t{\n#ifdef Q2CLIENT\n\tcase MT_BCASTQ2:\n\tcase MT_SINGLEQ2:\n#endif\n\tcase MT_SINGLEQW:\n\tcase MT_BCASTQW:\n\t\tNET_SendPollPacket (11, va(\"%c%c%c%cstatus\\n\", 255, 255, 255, 255), mast->adr);\n\t\tbreak;\n#ifdef NQPROT\n\tcase MT_BCASTNQ:\n\tcase MT_SINGLENQ:\n\t\tSZ_Clear(&net_message);\n\t\tMSG_WriteLong(&net_message, 0);// save space for the header, filled in later\n\t\tMSG_WriteByte(&net_message, CCREQ_SERVER_INFO);\n\t\tMSG_WriteString(&net_message, NET_GAMENAME_NQ);\t//look for either sort of server\n\t\tMSG_WriteByte(&net_message, NET_PROTOCOL_VERSION);\n\t\t*((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));\n\t\tNET_SendPollPacket(net_message.cursize, net_message.data, mast->adr);\n\t\tSZ_Clear(&net_message);\n\t\tbreak;\n#endif\n\tcase MT_MASTERQW:\n\t\tNET_SendPollPacket (3, \"c\\n\", mast->adr);\n\t\tbreak;\n#ifdef Q2CLIENT\n\tcase MT_MASTERQ2:\n\t\tif (COM_FDepthFile(\"pics/colormap.pcx\", true)!=0x7fffffff)\t//only query this master if we expect to be able to load it's maps.\n\t\t\tNET_SendPollPacket (6, \"query\", mast->adr);\n\t\tbreak;\n#endif\n\t}\n}\n\n\nvoid MasterInfo_WriteServers(void)\n{\n/*\n\tchar *typename;\n\tmaster_t *mast;\n\tserverinfo_t *server;\n\tFILE *mf, *qws;\n\t\n\tmf = fopen(\"masters.txt\", \"wt\");\n\tif (!mf)\n\t{\n\t\tCon_Printf(\"Couldn't write masters.txt\");\n\t\treturn;\n\t}\n\t\n\tfor (mast = master; mast; mast=mast->next)\n\t{\n\t\tswitch(mast->type)\n\t\t{\n\t\tcase MT_MASTERQW:\n\t\t\ttypename = \"master:qw\";\n\t\t\tbreak;\n\t\tcase MT_MASTERQ2:\n\t\t\ttypename = \"master:q2\";\n\t\t\tbreak;\n\t\tcase MT_BCASTQW:\n\t\t\ttypename = \"bcast:qw\";\n\t\t\tbreak;\n\t\tcase MT_BCASTQ2:\n\t\t\ttypename = \"bcast:q2\";\n\t\t\tbreak;\n\t\tcase MT_BCASTNQ:\n\t\t\ttypename = \"bcast:nq\";\n\t\t\tbreak;\n\t\tcase MT_SINGLEQW:\n\t\t\ttypename = \"single:qw\";\n\t\t\tbreak;\n\t\tcase MT_SINGLEQ2:\n\t\t\ttypename = \"single:q2\";\n\t\t\tbreak;\n\t\tcase MT_SINGLENQ:\n\t\t\ttypename = \"single:nq\";\n\t\t\tbreak;\n\t\tdefault:\n\t\t\ttypename = \"writeerror\";\n\t\t}\n\t\tfprintf(mf, \"%s\\t%s\\t%s\\n\", NET_AdrToString(mast->adr), typename, mast->name);\n\t}\n\t\n\tif (slist_writeserverstxt.value)\n\t\tqws = fopen(\"server.txt\", \"wt\");\n\telse\n\t\tqws = NULL;\n\tif (qws)\n\t\tfprintf(mf, \"\\n%s\\t%s\\t%s\\n\\n\", \"file servers.txt\", \"favorite:qw\", \"personal server list\");\n\t\t\n\tfor (server = firstserver; server; server = server->next)\n\t{\n\t\tif (server->special & SS_FAVORITE)\n\t\t{\n\t\t\tif (server->special & SS_QUAKE2)\n\t\t\t\tfprintf(mf, \"%s\\t%s\\t%s\\n\", NET_AdrToString(server->adr), \"favorite:q2\", server->name);\n\t\t\telse if (server->special & SS_NETQUAKE)\n\t\t\t\tfprintf(mf, \"%s\\t%s\\t%s\\n\", NET_AdrToString(server->adr), \"favorite:nq\", server->name);\n\t\t\telse if (qws)\t//servers.txt doesn't support the extra info.\n\t\t\t\tfprintf(qws, \"%s\\t%s\\n\", NET_AdrToString(server->adr), server->name);\n\t\t\telse\t//read only? damn them!\n\t\t\t\tfprintf(mf, \"%s\\t%s\\t%s\\n\", NET_AdrToString(server->adr), \"favorite:qw\", server->name);\n\t\t}\n\t}\n\t\n\tif (qws)\n\t\tfclose(qws);\n\t\n\t\n\tfclose(mf);\n*/\n}\n\n//poll master servers for server lists.\nvoid MasterInfo_Begin(void)\n{\n\tmaster_t *mast;\n\tif (!Master_LoadMasterList(\"masters.txt\",\t\t\tMT_MASTERQW, 5))\n\t{\n\t\tMaster_LoadMasterList(\"servers.txt\",\t\t\tMT_SINGLEQW, 1);\n//\t\tif (q1servers)\n\t\t{\n\t\t\tMaster_AddMaster(\"255.255.255.255:26000\",\t\tMT_BCASTNQ, \"Nearby Quake1 servers\");\n\t\t\tMaster_AddMaster(\"255.255.255.255:27500\",\t\tMT_BCASTQW, \"Nearby QuakeWorld UDP servers.\");\n\t\t}\n\n//\t\tif (q2servers)\n\t\t{\n\t\t\tMaster_AddMaster(\"255.255.255.255:27910\",\t\tMT_BCASTQ2, \"Nearby Quake2 UDP servers.\");\n//\t\t\tMaster_AddMaster(\"00000000:ffffffffffff:27910\",\tMT_BCASTQ2, \"Nearby Quake2 IPX servers.\");\n\t\t}\n\n\n//\t\tif (q1servers)\n\t\t{\n\t\t\tMaster_AddMaster(\"192.246.40.37:27000\",\t\t\tMT_MASTERQW, \"id Limbo\");\n\t\t\tMaster_AddMaster(\"192.246.40.37:27002\",\t\t\tMT_MASTERQW, \"id CTF\");\n\t\t\tMaster_AddMaster(\"192.246.40.37:27003\",\t\t\tMT_MASTERQW, \"id TeamFortress\");\n\t\t\tMaster_AddMaster(\"192.246.40.37:27004\",\t\t\tMT_MASTERQW, \"id Miscilaneous\");\n\t\t\tMaster_AddMaster(\"192.246.40.37:27006\",\t\t\tMT_MASTERQW, \"id Deathmatch Only\");\n\t\t\tMaster_AddMaster(\"150.254.66.120:27000\",\t\tMT_MASTERQW, \"Poland's master server.\");\n\t\t\tMaster_AddMaster(\"62.112.145.129:27000\",\t\tMT_MASTERQW, \"Ocrana master server.\");\n\t\t}\n\n//\t\tif (q2servers)\n\t\t{\n\t\t\tMaster_AddMaster(\"192.246.40.37:27900\",\t\t\tMT_MASTERQ2, \"id q2 Master.\");\n\t\t}\n\n\t}\n\n\tfor (mast = master; mast; mast=mast->next)\n\t{\n\t\tMasterInfo_Request(mast);\n\t}\n}\n\nvoid Master_QueryServer(serverinfo_t *server)\n{\n\tchar\tdata[2048];\n\tserver->sends++;\n\tserver->refreshtime = msecstime;\n\tsnprintf(data, sizeof(data), \"%c%c%c%cstatus\", 255, 255, 255, 255);\n\tNET_SendPollPacket (strlen(data), data, server->adr);\n}\n//send a packet to each server in sequence.\nvoid CL_QueryServers(void)\n{\n\tstatic int poll;\n\tint op;\n\tserverinfo_t *server;\t\n\top = poll;\n\t\n\n\tfor (server = firstserver; op>0 && server; server=server->next, op--);\n\n\tif (!server)\n\t{\n\t\tpoll = 0;\n\t\treturn;\n\t}\n\n\tif (op == 0)\n\t{\n\t\tif (server->sends < 1)\n\t\t{\n\t\t\tMaster_QueryServer(server);\n\t\t}\n\t\tpoll++;\n\t\treturn;\n\t}\n\t\n\n\tpoll = 0;\n}\n\nint Master_TotalCount(void)\n{\n\tint count=0;\n\tserverinfo_t *info;\n\n\tfor (info = firstserver; info; info = info->next)\n\t{\n\t\tcount++;\n\t}\n\treturn count;\n}\n\n//true if server is on a different master's list.\nserverinfo_t *Master_InfoForServer (struct sockaddr_in addr)\n{\n\tserverinfo_t *info;\n\n\tfor (info = firstserver; info; info = info->next)\n\t{\n\t\tif (NET_CompareAdr(&info->adr, &addr))\n\t\t\treturn info;\n\t}\n\treturn NULL;\n}\nserverinfo_t *Master_InfoForNum (int num)\n{\n\tserverinfo_t *info;\n\n\tfor (info = firstserver; info; info = info->next)\n\t{\n\t\tif (num-- <=0)\n\t\t\treturn info;\n\t}\n\treturn NULL;\n}\n\nvoid MasterInfo_RemovePlayers(struct sockaddr_in adr)\n{\n\tplayer_t *p, *prev;\n\tprev = NULL;\n\tfor (p = mplayers; p; )\n\t{\n\t\tif (NET_CompareAdr(&p->adr, &adr))\n\t\t{\n\t\t\tif (prev)\n\t\t\t\tprev->next = p->next;\n\t\t\telse\n\t\t\t\tmplayers = p->next;\n\t\t\tfree(p);\n\t\t\tp=prev;\n\n\t\t\tcontinue;\n\t\t}\n\t\telse\n\t\t\tprev = p;\n\n\t\tp = p->next;\n\t}\n}\n\nvoid MasterInfo_AddPlayer(struct sockaddr_in serveradr, char *name, int ping, int frags, int colours, char *skin)\n{\n\tplayer_t *p;\n\tp = malloc(sizeof(player_t));\n\tp->next = mplayers;\n\tp->adr = serveradr;\n\tp->colour = colours;\n\tp->frags = frags;\n\tstrncpy(p->name, name, sizeof(p->name));\n\tp->name[sizeof(p->name)-1] = '\\0';\n\tstrncpy(p->skin, skin, sizeof(p->skin));\n\tp->skin[sizeof(p->skin)-1] = '\\0';\n\tmplayers = p;\n}\n\n//we got told about a server, parse it's info\nint CL_ReadServerInfo(char *msg, int servertype, qboolean favorite, struct sockaddr_in net_from)\n{\n\tserverdetailedinfo_t details;\n\n\tchar *token;\n\tchar *nl;\n\tchar *name;\t\n\tint ping;\n\tint len;\n\tserverinfo_t *info;\n\n\tinfo = Master_InfoForServer(net_from);\n\n\tif (!info)\t//not found...\n\t{\n\t\tinfo = malloc(sizeof(serverinfo_t));\n\t\tmemset(info, 0, sizeof(*info));\n\n\t\tinfo->adr = net_from;\n\n\t\tsnprintf(info->name, sizeof(info->name), \"%s\", NET_AdrToString(&info->adr));\n\n\t\tinfo->next = firstserver;\n\t\tfirstserver = info;\n\n\t}\n\telse\n\t{\n\t\tMasterInfo_RemovePlayers(info->adr);\n\t}\n\n\tnl = strchr(msg, '\\n');\n\tif (nl)\n\t{\n\t\t*nl = '\\0';\n\t\tnl++;\n\t}\n\tname = Info_ValueForKey(msg, \"hostname\");\n\tQ_strncpyz(info->name, name, sizeof(info->name));\n\tinfo->special = info->special & (SS_FAVORITE | SS_KEEPINFO);\t//favorite is never cleared\n\tif (!strcmp(\"FTE\", Info_ValueForKey(msg, \"*distrib\")))\n\t\tinfo->special |= SS_FTESERVER;\n\telse if (!strncmp(\"FTE\", Info_ValueForKey(msg, \"*version\"), 3))\n\t\tinfo->special |= SS_FTESERVER;\n\t\t\n\t\t\n\tif (servertype == MT_SINGLEQ2)\n\t\tinfo->special |= SS_QUAKE2;\n\telse if (servertype == MT_SINGLENQ)\n\t\tinfo->special |= SS_NETQUAKE;\n\tif (favorite)\t//was specifically named, not retrieved from a master.\n\t\tinfo->special |= SS_FAVORITE;\n\n\tping = msecstime - info->refreshtime;\n\tif (ping > 0xffff)\n\t\tinfo->ping = 0xffff;\n\telse\n\t\tinfo->ping = ping;\n\n\tinfo->players = 0;\n\tinfo->maxplayers = atoi(Info_ValueForKey(msg, \"maxclients\"));\n\n\tinfo->tl = atoi(Info_ValueForKey(msg, \"timelimit\"));\n\tinfo->fl = atoi(Info_ValueForKey(msg, \"fraglimit\"));\n\n\tif (servertype == MT_SINGLEQ2)\n\t{\n\t\tQ_strncpyz(info->gamedir,\tInfo_ValueForKey(msg, \"gamename\"),\tsizeof(info->gamedir));\n\t\tQ_strncpyz(info->map,\t\tInfo_ValueForKey(msg, \"mapname\"),\tsizeof(info->map));\n\t}\n\telse\n\t{\n\t\tQ_strncpyz(info->gamedir,\tInfo_ValueForKey(msg, \"*gamedir\"),\tsizeof(info->gamedir));\n\t\tQ_strncpyz(info->map,\t\tInfo_ValueForKey(msg, \"map\"),\t\tsizeof(info->map));\n\t}\n\n\t{\n\t\tint clnum;\n\t\tstrcpy(details.info, msg);\n\t\tmsg = msg+strlen(msg)+1;\n\n\t\tinfo->players=details.numplayers = 0;\n\t\tfor (clnum=0; clnum < MAX_CLIENTS; clnum++)\n\t\t{\n\t\t\tnl = strchr(msg, '\\n');\n\t\t\tif (!nl)\n\t\t\t\tbreak;\n\t\t\t*nl = '\\0';\n\n\t\t\ttoken = msg;\n\t\t\tif (!token)\n\t\t\t\tbreak;\n\t\t\tdetails.players[clnum].userid = atoi(token);\n\t\t\ttoken = strchr(token+1, ' ');\n\t\t\tif (!token)\n\t\t\t\tbreak;\n\t\t\tdetails.players[clnum].frags = atoi(token);\n\t\t\ttoken = strchr(token+1, ' ');\n\t\t\tif (!token)\n\t\t\t\tbreak;\n\t\t\tdetails.players[clnum].time = (float)atof(token);\n\t\t\tmsg = token;\n\t\t\ttoken = strchr(msg+1, ' ');\n\t\t\tif (!token)\t//probably q2 response\n\t\t\t{\n\t\t\t\t//see if this is actually a Quake2 server.\n\t\t\t\ttoken = strchr(msg+1, '\\\"');\n\t\t\t\tif (!token)\t//it wasn't.\n\t\t\t\t\tbreak;\n\n\t\t\t\tdetails.players[clnum].ping = details.players[clnum].frags;\n\t\t\t\tdetails.players[clnum].frags = details.players[clnum].userid;\n\n\t\t\t\tmsg = strchr(token+1, '\\\"');\n\t\t\t\tif (!msg)\n\t\t\t\t\tbreak;\n\t\t\t\tlen = msg - token;\n\t\t\t\tif (len >= sizeof(details.players[clnum].name))\n\t\t\t\t\tlen = sizeof(details.players[clnum].name);\n\t\t\t\tQ_strncpyz(details.players[clnum].name, token+1, len);\n\n\t\t\t\tdetails.players[clnum].skin[0] = '\\0';\n\n\t\t\t\tdetails.players[clnum].topc = 0;\n\t\t\t\tdetails.players[clnum].botc = 0;\n\t\t\t\tdetails.players[clnum].time = 0;\n\t\t\t}\n\t\t\telse\t//qw responce\n\t\t\t{\n\t\t\t\tdetails.players[clnum].time = (float)atof(token);\n\t\t\t\tmsg = token;\n\t\t\t\ttoken = strchr(msg+1, ' ');\n\t\t\t\tif (!token)\n\t\t\t\t\tbreak;\n\n\t\t\t\tdetails.players[clnum].ping = atoi(token);\n\n\t\t\t\ttoken = strchr(token+1, '\\\"');\n\t\t\t\tif (!token)\n\t\t\t\t\tbreak;\n\t\t\t\tmsg = strchr(token+1, '\\\"');\n\t\t\t\tif (!msg)\n\t\t\t\t\tbreak;\n\t\t\t\tlen = msg - token;\n\t\t\t\tif (len >= sizeof(details.players[clnum].name))\n\t\t\t\t\tlen = sizeof(details.players[clnum].name);\n\t\t\t\tQ_strncpyz(details.players[clnum].name, token+1, len);\n\t\t\t\tdetails.players[clnum].name[len] = '\\0';\n\n\t\t\t\ttoken = strchr(msg+1, '\\\"');\n\t\t\t\tif (!token)\n\t\t\t\t\tbreak;\n\t\t\t\tmsg = strchr(token+1, '\\\"');\n\t\t\t\tif (!msg)\n\t\t\t\t\tbreak;\n\t\t\t\tlen = msg - token;\n\t\t\t\tif (len >= sizeof(details.players[clnum].skin))\n\t\t\t\t\tlen = sizeof(details.players[clnum].skin);\n\t\t\t\tQ_strncpyz(details.players[clnum].skin, token+1, len);\n\t\t\t\tdetails.players[clnum].skin[len] = '\\0';\n\n\t\t\t\ttoken = strchr(msg+1, ' ');\n\t\t\t\tif (!token)\n\t\t\t\t\tbreak;\n\t\t\t\tdetails.players[clnum].topc = atoi(token);\n\t\t\t\ttoken = strchr(token+1, ' ');\n\t\t\t\tif (!token)\n\t\t\t\t\tbreak;\n\t\t\t\tdetails.players[clnum].botc = atoi(token);\n\t\t\t}\n\n\t\t\tMasterInfo_AddPlayer(info->adr, details.players[clnum].name, details.players[clnum].ping, details.players[clnum].frags, details.players[clnum].topc*4 | details.players[clnum].botc, details.players[clnum].skin);\n\n\t\t\tinfo->players = ++details.numplayers;\n\n\t\t\tmsg = nl;\n\t\t\tif (!msg)\n\t\t\t\tbreak;\t//erm...\n\t\t\tmsg++;\n\t\t}\n\t}\n\tif (!info->moreinfo && ((slist_cacheinfo.value == 2 || NET_CompareAdr(&info->adr, &selectedserver.adr)) || (info->special & SS_KEEPINFO)))\n\t\tinfo->moreinfo = malloc(sizeof(serverdetailedinfo_t));\n\tif (NET_CompareAdr(&info->adr, &selectedserver.adr))\n\t\tselectedserver.detail = info->moreinfo;\n\n\tif (info->moreinfo)\n\t\tmemcpy(info->moreinfo, &details, sizeof(serverdetailedinfo_t));\n\n\treturn true;\n}\n\n//rewrite to scan for existing server instead of wiping all.\nvoid CL_MasterListParse(qbyte *buffer, qbyte *maxbuffer, qboolean isq2)\n{\n\tserverinfo_t *info;\n\tserverinfo_t *last, *old;\n\n\tint p1, p2;\n\tbuffer++;\n\n\tlast = firstserver;\n\n\twhile(buffer+1 <= maxbuffer)\n\t{\n\t\tinfo = malloc(sizeof(serverinfo_t));\n\t\tmemset(info, 0, sizeof(*info));\n\t\tinfo->adr.sin_family = AF_INET;\n\t\t((qbyte *)&info->adr.sin_addr)[0] = *buffer++;\n\t\t((qbyte *)&info->adr.sin_addr)[1] = *buffer++;\n\t\t((qbyte *)&info->adr.sin_addr)[2] = *buffer++;\n\t\t((qbyte *)&info->adr.sin_addr)[3] = *buffer++;\n\n\t\tp1 = *buffer++;\n\t\tp2 = *buffer++;\n\t\tinfo->adr.sin_port = (short)(p1 + (p2<<8));\n\t\tif ((old = Master_InfoForServer(info->adr)))\t//remove if the server already exists.\n\t\t{\n\t\t\told->sends = 0;\t//reset.\n\t\t\tfree(info);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tinfo->special = isq2?SS_QUAKE2:0;\n\t\t\tinfo->refreshtime = 0;\n\n\t\t\tsnprintf(info->name, sizeof(info->name), \"%s\", NET_AdrToString(&info->adr));\n\n\t\t\tinfo->next = last;\n\t\t\tlast = info;\n\t\t}\t\t\n\t}\n\n\tfirstserver = last;\n}\n"
  },
  {
    "path": "plugins/spaceinv/spaceinv.c",
    "content": "#include \"../plugin.h\"\n\n#define DEFAULTHUDNAME \"ftehud.hud\"\n\n#define MAX_ELEMENTS 128\n\nint testvar;\nint testvar2;\nint testvar3;\nint testvar4;\nint K_UPARROW;\nint K_DOWNARROW;\nint K_LEFTARROW;\nint K_RIGHTARROW;\nint K_ESCAPE;\nint K_MOUSE1;\nint K_MOUSE2;\nint K_HOME;\nint K_SHIFT;\nint K_SPACE;\nint K_F1;\nint K_F2;\n\n\n#define texture qhandle_t\n#define bool qboolean\n\nfloat realtime;\n\n#define Sound_Start(x)\n\n\n\n\n\n\nvoid vecNorm(float *in, float *out)\n{\t\n\tfloat\tnew;\t\n\n\tnew = in[0] * in[0] + in[1] * in[1] + in[2]*in[2];\n\tnew = (float)sqrt(new);\n\t\n\tif (new == 0)\n\t\tout[0] = out[1] = out[2] = 0;\n\telse\n\t{\n\t\tnew = 1/new;\n\t\tout[0] = in[0] * new;\n\t\tout[1] = in[1] * new;\n\t\tout[2] = in[2] * new;\n\t}\n\t\n}\n\n#if 0\n#define MAXCANNONS 4\n#define MAXBULLETS 256\n#define MAXMONSTERS 256\n#define ENEMYTYPES 10\n#define SHOTLEVELS 10\n#else\nenum {\n\tM_CANNON1,\n\tM_CANNON2,\n\tM_CANNON3,\n\tM_CANNON4,\n\tM_CANNON5,\n\tM_CANNON6,\n\tM_CANNON7,\n\tM_CANNON8,\n\tM_CANNON9,\n\tM_CANNON10,\n\tM_CANNON11,\n\tM_CANNON12,\n\n\tM_ROCKETL1 = 50,\n\tM_ROCKETL2,\n\tM_ROCKETL3,\n\tM_ROCKETL4,\n\tM_ROCKETL5,\n\tM_ROCKETL6,\n\tM_ROCKETL7,\n\tM_ROCKETL8,\n\tM_ROCKETL9,\n\tM_ROCKETL10,\n\tM_ROCKETL11,\n\tM_ROCKETL12,\n\n\tM_FORWARD = 100,\n\tM_DIAG,\n\tM_DIAG2,\n\tM_SIDE,\n\n\tMAXADDONS\n};\n#define FIRSTCANNON M_CANNON1\n#define LASTCANNON M_ROCKETL1-1\n\n#define FIRSTROCKET M_ROCKETL1\n#define LASTROCKET M_FORWARD-1\n\n#define FIRSTCENTRALMOD M_FORWARD\n#define LASTCENTRALMOD MAXADDONS-1\n\nint MAXBULLETS = 255;\nint MAXMONSTERS = 256;\nint MAXSQUADS = 256;\nint MAXROUTES = 64;\nint MAXROUTEPOINTS=10;\n#define ENEMYTYPES monsterpics\n#define SHOTLEVELS weaponlevels\n#endif\n#define CONT_LEFTKEY\t1\n#define CONT_RIGHTKEY\t2\n#define CONT_FIREKEY\t4\n#define CONT_UPKEY\t\t8\n#define CONT_DOWNKEY\t16\n#define CONT_MOUSE\t\t32\t//right mouse button pressed\n\n#define PLAYERSHIPSPEEDX\t2\n#define PLAYERSHIPSPEEDY\t2\n#define ENEMYSHIPSPEEDX\t(random()-0.5)\n#define ENEMYSHIPSPEEDY\t(random()*2+(float)1)\n\n\n#define PUP_SHRUNKEN\t1\t//one round only\n\nvoid SIRestartGame (void);\nfloat random(void);\n\nint SIlevel;\nchar SILevelName[128];\n\nint weaponlevels;\nint monsterpics;\n\n//Textures in structures are pointers to the texture number. This means we don't have to cycle through each one when we reload the textures.\n\ntypedef struct SIEnemyType_s\n{\n\ttexture *picture;\n\tfloat width;\n\tfloat height;\n\tint health;\n\n\tfloat FireProb;\n\n\tint reward;\n\n\tint number;\n\n\tint AI;\n\tint healthregen;\n\n\tint shotdamage;\n\ttexture *shotpicture;\n} SIEnemyType_t;\n\ntypedef struct SIRoute_s\n{\n\tint numpoints;\n\tvec3_t pos[1];\n} SIRoute_t;\n\ntypedef struct SISquad_s\n{\n\tint route;\n\tint type;\n\tint number;\n\tfloat interval;\n\tfloat time;\n\tfloat speed;\n} SISquad_t;\n\n\ntypedef struct SIaddon_s\n{\n\tfloat xoff;\n\tfloat yoff;\n\n\tint power;\n\tfloat refire;\n\tfloat reloadtime;\n\ttexture *tex;\n} SIaddon_t;\n\ntypedef struct SIp_s\n{\n\tfloat x;\n\tfloat y;\n\tfloat width;\n\tfloat height;\n\tint cash;\n\tint health;\t\n\n\tint contols;\n\tint powerups;\t\n\t\t\n\tSIaddon_t cannon[MAXADDONS];\n} SIp_t;\n\ntypedef struct SImonster_s\n{\n\tfloat xpos;\n\tfloat ypos;\n\tfloat width;\n\tfloat height;\n\n\tint routenum;\n\tint destnum;\n\n\tfloat xvel;\n\tfloat yvel;\n\n\tint health;\n\n\tint timetoregen;\n\n\tSIEnemyType_t *EnemyType;\n} SImonster_t;\n\ntypedef struct SIbullet_s\n{\n\tint type;\n\tint charge;\n\n\tfloat xpos;\n\tfloat ypos;\n\tfloat width;\n\tfloat height;\n\tfloat xvel;\n\tfloat yvel;\n\tfloat yaccel;\n\n\tqboolean inuse;\n\tint damage;\n\tfloat alpha;\n\tfloat alphachange;\n\n\ttexture *tex;\n} SIbullet_t;\n\ntypedef struct ShopRegion_s\n{\n\tfloat x;\n\tfloat y;\n\tfloat width;\n\tfloat height;\n\n\tchar *text;\n\n\ttexture *tex;\n\tqboolean (*AppearCondition) (int ident);\n\tvoid (*CallFunc) (int ident);\n\ttexture *(*Texture) (texture *def, int ident);\n\tint ident;\n} ShopRegion_t;\n\nint notenoughcash;\n\nSIRoute_t\t*SIRoute;\nSISquad_t\t*SISquad;\nSIEnemyType_t *SIEnemyType;\nSIp_t SIp;\nSImonster_t *SImonster;\nSIbullet_t *SIbullet;\nSIbullet_t *SIweapons;\t//prototypes\n\nqboolean SIShop;\nqboolean lastlevel;\nfloat deadtime;\nfloat leveltime;\n\nint livingmonsters;\n\ntexture SIplayertexture;\t//both\ntexture *SIbaddietexture;\t//game only\ntexture *SIbullettexture;\t//game only\ntexture SIexplosiontexture;\t//game only\ntexture SIhealthtexture;\t\t//shop only\ntexture SIleaveshoptexture;\t//shop only\ntexture SIshrinktexture;\t\t//shop only\ntexture SIcannontexture;\t//game+shop\ntexture SIsideshottexture;\t//shop only\ntexture SIrapidtexture;\ntexture SIsavegametexture;\ntexture SIloadgametexture;\ntexture SIquittexture;\n/*\nsound SoundWin;\nsound SoundLoose;\nsound SoundFire;\nsound SoundExplosion;\nsound SoundHit;\t//when we don't kill\n*/\nvoid SISPHealth (int ident)\n{\n\tif (SIp.health >= 9)\n\t\treturn;\n\tif (SIp.cash < 250)\n\t{\n\t\tnotenoughcash = 250;\n\t\treturn;\n\t}\n\tSIp.health++;\n\tSIp.cash -= 250;\n}\nqboolean SISPHealthThere (int ident)\n{\n\tif (SIp.health < 9)\n\t\treturn true;\n\treturn false;\n}\n\nvoid SISPUpgrade1 (int ident)\n{\n\tif (SIp.cannon[M_FORWARD].power >= weaponlevels-1)\n\t\treturn;\n\n\tif (SIp.cash < 500)\n\t{\n\t\tnotenoughcash = 250;\n\t\treturn;\n\t}\n\tSIp.cannon[M_FORWARD].power++;\n\tSIp.cash -= 500;\n}\ntexture *SISPUpgrade1Texture (texture *def, int ident)\n{\n\treturn &SIbullettexture[SIp.cannon[M_FORWARD].power+1];\n}\nqboolean SISPUpgrade1There (int ident)\n{\n\tif (SIp.cannon[M_FORWARD].power < weaponlevels-1)\n\t\treturn true;\n\treturn false;\n}\n\nvoid SISPShrink (int ident)\n{\n\tif (SIp.powerups & PUP_SHRUNKEN)\t//can't reshrink\n\t\treturn;\n\n\tif (SIp.cash < 100)\n\t{\n\t\tnotenoughcash = 250;\n\t\treturn;\n\t}\n\tSIp.powerups |= PUP_SHRUNKEN;\n\tSIp.cash -= 100;\n}\nbool SISPShrinkThere (int ident)\n{\n\tif (SIp.powerups & PUP_SHRUNKEN)\n\t\treturn false;\n\treturn true;\n}\n\nvoid SISPCannon (int ident)\n{\n\tint a;\n\n\tif (SIp.cash < 1500)\n\t{\n\t\tnotenoughcash = 250;\n\t\treturn;\n\t}\n\n\tfor (a = FIRSTCANNON; a <= LASTCANNON; a++)\n\t{\n\t\tif (SIp.cannon[a].power == 0)\n\t\t{\n\t\t\tSIp.cannon[a].tex = &SIcannontexture;\n\t\t\tSIp.cannon[a].power = 1;\n\n\t\t\tSIp.cash -= 1500;\n\t\t\treturn;\n\t\t}\n\t}\n}\n\nbool SISPCannonThere (int ident)\n{\n\tint a;\n\n\tfor (a = FIRSTCANNON; a <= LASTCANNON; a++)\n\t{\n\t\tif (SIp.cannon[a].power == 0)\n\t\t{\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n\ntexture *SISPCannonUpgradeTexture (texture *def, int ident)\n{\n\treturn &SIbullettexture[SIp.cannon[ident].power];\n}\nvoid SISPCannonUpgrade(int ident)\n{\n\tif (SIp.cannon[ident].power >= weaponlevels)\n\t\treturn;\n\n\tif (SIp.cash < 600)\n\t{\n\t\tnotenoughcash = 250;\n\t\treturn;\n\t}\n\tSIp.cannon[ident].power++;\t\n\tSIp.cash -= 600;\n}\nbool SISPCannonUpgradeThere(int ident)\n{\t\n\tif (SIp.cannon[ident].power >= weaponlevels || SIp.cannon[ident].power <= 0)\n\t\treturn false;\n\treturn true;\n}\n\nvoid SISPCannonBoost(int ident)\n{\n\tif (SIp.cannon[ident].reloadtime <= 0.1f)\n\t\treturn;\n\n\tif (SIp.cash < 600)\n\t{\n\t\tnotenoughcash = 250;\n\t\treturn;\n\t}\n\tSIp.cannon[ident].reloadtime -= 0.1f;\t\n\tSIp.cash -= 600;\n}\nbool SISPCannonBoostThere(int ident)\n{\t\n\tif (SIp.cannon[ident].reloadtime <= 0.1f || SIp.cannon[ident].power <= 0)\n\t\treturn false;\n\treturn true;\n}\n\nvoid SISPRocket (int ident)\n{\n\tint a;\n\n\tif (SIp.cash < 1500)\n\t{\n\t\tnotenoughcash = 250;\n\t\treturn;\n\t}\n\n\tfor (a = FIRSTROCKET; a <= LASTROCKET; a++)\n\t{\n\t\tif (SIp.cannon[a].power == 0)\n\t\t{\n\t\t\tSIp.cannon[a].tex = &SIcannontexture;\n\t\t\tSIp.cannon[a].power = 1;\n\n\t\t\tSIp.cash -= 1500;\n\t\t\treturn;\n\t\t}\n\t}\n}\n\nbool SISPRocketThere (int ident)\n{\n\tint a;\n\n\tfor (a = FIRSTROCKET; a <= LASTROCKET; a++)\n\t{\n\t\tif (SIp.cannon[a].power == 0)\n\t\t{\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n\ntexture *SISPSideBurstTexture (texture *def, int ident)\n{\n\treturn &SIbullettexture[SIp.cannon[M_SIDE].power];\n}\nvoid SISPSideBurst (int ident)\n{\n\tif (SIp.cannon[M_SIDE].power >= weaponlevels)\n\t\treturn;\n\n\tif (SIp.cash < 1000)\n\t{\n\t\tnotenoughcash = 250;\n\t\treturn;\n\t}\n\n\tSIp.cash -= 1000;\n\n\tSIp.cannon[M_SIDE].power+=1;\n}\nbool SISPSideBurstThere (int ident)\n{\n\tif (SIp.cannon[M_SIDE].power >= weaponlevels)\n\t\treturn false;\n\treturn true;\n}\n\nvoid SISPRapidFire (int ident)\n{\n\tif (SIp.cannon[M_FORWARD].reloadtime <= 0.1)\n\t\treturn;\n\n\tif (SIp.cash < 250)\n\t{\n\t\tnotenoughcash = 250;\n\t\treturn;\n\t}\n\n\tSIp.cash -= 250;\n\n\tSIp.cannon[M_FORWARD].reloadtime -= 0.1f;\n}\nbool SISPRapidFireThere (int ident)\n{\n\tif (SIp.cannon[M_FORWARD].reloadtime <= 0.1)\n\t\treturn false;\n\treturn true;\n}\n\nvoid NextLevel (void);\nvoid LeaveShop (int ident)\n{\n\tNextLevel();\n}\n\nbool gamesaved = 2;\nvoid SISaveGame(int ident)\n{\n\tint a;\n\ttexture handle;\n\n\tFS_Open(\"spaceinv/spaceinv.sav\", &handle, 2);\n\n\tFS_Write(handle, &SIp.cash, sizeof(SIp.cash));\n\tFS_Write(handle, &SIp.powerups, sizeof(SIp.powerups));\n\tFS_Write(handle, &SIp.health, sizeof(SIp.health));\n\tFS_Write(handle, &SIlevel, sizeof(SIlevel));\n\n\tfor (a = 0; a < MAXADDONS; a++)\n\t{\n\t\tFS_Write(handle, &SIp.cannon[a].power, sizeof(SIp.cannon[a].power));\n\t\tFS_Write(handle, &SIp.cannon[a].reloadtime, sizeof(SIp.cannon[a].reloadtime));\n\t}\n\n\tFS_Close(handle);\n\n\tgamesaved = true;\n}\n\nvoid SILoadGame(int ident)\n{\n\tint a;\n\tint len;\n\ttexture handle;\n\n\tlen = FS_Open(\"spaceinv/spaceinv.sav\", &handle, 1);\n\tif (len < 0)\n\t\treturn;\n\n\tFS_Read(handle, &SIp.cash, sizeof(SIp.cash));\n\tFS_Read(handle, &SIp.powerups, sizeof(SIp.powerups));\n\tFS_Read(handle, &SIp.health, sizeof(SIp.health));\n\tFS_Read(handle, &SIlevel, sizeof(SIlevel));\n\n\tfor (a = 0; a < MAXADDONS; a++)\n\t{\n\t\tFS_Read(handle, &SIp.cannon[a].power, sizeof(SIp.cannon[a].power));\n\t\tFS_Read(handle, &SIp.cannon[a].reloadtime, sizeof(SIp.cannon[a].reloadtime));\n\n\t\tif (SIp.cannon[a].power > weaponlevels)\n\t\t\tSIp.cannon[a].power = weaponlevels;\n\n\t}\n\n\tFS_Close(handle);\n}\n\nbool SILoadGameThere(int ident)\n{\n\ttexture handle;\n\tif (gamesaved = 2)\n\t{\n\t\tif (FS_Open(\"spaceinv/spaceinv.sav\", &handle, 1) >= 0)\n\t\t{\n\t\t\tFS_Close(handle);\n\t\t\tgamesaved = true;\n\t\t}\n\t\telse\n\t\t\tgamesaved = false;\n\t}\n\treturn gamesaved;\t\n}\n\nbool SIFTRUE (int ident) {return true;}\ntexture *SIShopTexture (texture *def, int ident)\n{\n\treturn def;\n}\n\nShopRegion_t ShopRegion[] =\n{\t\n\t{40,\t40,\t\t40,\t40,\t\"3Leave Shop\",\t\t\t\t&SIleaveshoptexture,\tSIFTRUE,\t\t\tLeaveShop,\t\tSIShopTexture\t\t},\n\t{40,\t80,\t\t40,\t40,\t\"3       Heal     (250)\",\t&SIhealthtexture,\t\tSISPHealthThere,\tSISPHealth,\t\tSIShopTexture\t\t},\n\t{40,\t120,\t40,\t40,\t\"3   Rapid Fire   (250)\",\t&SIrapidtexture,\t\tSISPRapidFireThere,\tSISPRapidFire,\tSIShopTexture\t\t},\n\t{80,\t40,\t\t40,\t40,\t\"3Upgrade Weapons (500)\",\tNULL,\t\t\t\t\tSISPUpgrade1There,\tSISPUpgrade1,\tSISPUpgrade1Texture\t},\n\t{80,\t80,\t\t40,\t40,\t\"3      Shrink    (100)\",\t&SIshrinktexture,\t\tSISPShrinkThere,\tSISPShrink,\t\tSIShopTexture\t\t},\n//\t{80,\t120,\t40,\t40,\t\"4Quit Game\",\t\t\t\t&SIquittexture,\t\t\tSIFTRUE,\t\t\tSIQuit,\t\t\tSIShopTexture\t\t},\n\t{120,\t40,\t\t40,\t40,\t\"3   Side Cannon (1500)\",\t&SIcannontexture,\t\tSISPCannonThere,\tSISPCannon,\t\tSIShopTexture,\t1\t},\n\t{120,\t80,\t\t40,\t40,\t\"3    Spreader   (1000)\",\t&SIsideshottexture,\t\tSISPSideBurstThere,\tSISPSideBurst,\tSIShopTexture\t\t},\n\t{120,\t80,\t\t20,\t20,\t\"3    Spreader   (1000)\",\tNULL,\t\t\t\t\tSISPSideBurstThere,\tSISPSideBurst,\tSISPSideBurstTexture},\n\t{120,\t120,\t40,\t40,\t\"3Save Game\",\t\t\t\t&SIsavegametexture,\t\tSIFTRUE,\t\t\tSISaveGame,\t\tSIShopTexture\t\t},\n\t{120,\t160,\t40,\t40,\t\"3Load Game\",\t\t\t\t&SIloadgametexture,\t\tSILoadGameThere,\tSILoadGame,\t\tSIShopTexture\t\t},\n\t{80,\t160,\t40,\t40,\t\"3RocketLauncher (1500)\",\t&SIcannontexture,\t\tSISPRocketThere,\tSISPRocket,\t\tSIShopTexture,\t1\t},\n\n\t{160,\t40,\t\t40,\t40,\t\"3Upgrade Cannon1(600)\",\tNULL,\t\t\t\t\tSISPCannonUpgradeThere,\tSISPCannonUpgrade,\tSISPCannonUpgradeTexture, 0},\n\t{200,\t40,\t\t40,\t40,\t\"3Upgrade Cannon2(600)\",\tNULL,\t\t\t\t\tSISPCannonUpgradeThere,\tSISPCannonUpgrade,\tSISPCannonUpgradeTexture, 1},\n\t{240,\t40,\t\t40,\t40,\t\"3Upgrade Cannon3(600)\",\tNULL,\t\t\t\t\tSISPCannonUpgradeThere,\tSISPCannonUpgrade,\tSISPCannonUpgradeTexture, 2},\n\t{280,\t40,\t\t40,\t40,\t\"3Upgrade Cannon4(600)\",\tNULL,\t\t\t\t\tSISPCannonUpgradeThere,\tSISPCannonUpgrade,\tSISPCannonUpgradeTexture, 3},\n\t{320,\t40,\t\t40,\t40,\t\"3Upgrade Cannon5(600)\",\tNULL,\t\t\t\t\tSISPCannonUpgradeThere,\tSISPCannonUpgrade,\tSISPCannonUpgradeTexture, 4},\n\t{360,\t40,\t\t40,\t40,\t\"3Upgrade Cannon6(600)\",\tNULL,\t\t\t\t\tSISPCannonUpgradeThere,\tSISPCannonUpgrade,\tSISPCannonUpgradeTexture, 5},\n\t{400,\t40,\t\t40,\t40,\t\"3Upgrade Cannon7(600)\",\tNULL,\t\t\t\t\tSISPCannonUpgradeThere,\tSISPCannonUpgrade,\tSISPCannonUpgradeTexture, 6},\n\t{440,\t40,\t\t40,\t40,\t\"3Upgrade Cannon8(600)\",\tNULL,\t\t\t\t\tSISPCannonUpgradeThere,\tSISPCannonUpgrade,\tSISPCannonUpgradeTexture, 7},\n\t{480,\t40,\t\t40,\t40,\t\"3Upgrade Cannon9(600)\",\tNULL,\t\t\t\t\tSISPCannonUpgradeThere,\tSISPCannonUpgrade,\tSISPCannonUpgradeTexture, 8},\n\t{520,\t40,\t\t40,\t40,\t\"3Upgrade Cannon10(600)\",\tNULL,\t\t\t\t\tSISPCannonUpgradeThere,\tSISPCannonUpgrade,\tSISPCannonUpgradeTexture, 9},\n\t{560,\t40,\t\t40,\t40,\t\"3Upgrade Cannon11(600)\",\tNULL,\t\t\t\t\tSISPCannonUpgradeThere,\tSISPCannonUpgrade,\tSISPCannonUpgradeTexture, 10},\n\t{600,\t40,\t\t40,\t40,\t\"3Upgrade Cannon12(600)\",\tNULL,\t\t\t\t\tSISPCannonUpgradeThere,\tSISPCannonUpgrade,\tSISPCannonUpgradeTexture, 11},\n\n\t{160,\t80,\t\t40,\t40,\t\"3 Boost Cannon1 (600)\",\t&SIrapidtexture,\t\t\t\t\tSISPCannonBoostThere,\tSISPCannonBoost,\tSIShopTexture, 0},\n\t{200,\t80,\t\t40,\t40,\t\"3 Boost Cannon2 (600)\",\t&SIrapidtexture,\t\t\t\t\tSISPCannonBoostThere,\tSISPCannonBoost,\tSIShopTexture, 1},\n\t{240,\t80,\t\t40,\t40,\t\"3 Boost Cannon3 (600)\",\t&SIrapidtexture,\t\t\t\t\tSISPCannonBoostThere,\tSISPCannonBoost,\tSIShopTexture, 2},\n\t{280,\t80,\t\t40,\t40,\t\"3 Boost Cannon4 (600)\",\t&SIrapidtexture,\t\t\t\t\tSISPCannonBoostThere,\tSISPCannonBoost,\tSIShopTexture, 3},\n\t{320,\t80,\t\t40,\t40,\t\"3 Boost Cannon5 (600)\",\t&SIrapidtexture,\t\t\t\t\tSISPCannonBoostThere,\tSISPCannonBoost,\tSIShopTexture, 4},\n\t{360,\t80,\t\t40,\t40,\t\"3 Boost Cannon6 (600)\",\t&SIrapidtexture,\t\t\t\t\tSISPCannonBoostThere,\tSISPCannonBoost,\tSIShopTexture, 5},\n\t{400,\t80,\t\t40,\t40,\t\"3 Boost Cannon7 (600)\",\t&SIrapidtexture,\t\t\t\t\tSISPCannonBoostThere,\tSISPCannonBoost,\tSIShopTexture, 6},\n\t{440,\t80,\t\t40,\t40,\t\"3 Boost Cannon8 (600)\",\t&SIrapidtexture,\t\t\t\t\tSISPCannonBoostThere,\tSISPCannonBoost,\tSIShopTexture, 7},\n\t{480,\t80,\t\t40,\t40,\t\"3 Boost Cannon9 (600)\",\t&SIrapidtexture,\t\t\t\t\tSISPCannonBoostThere,\tSISPCannonBoost,\tSIShopTexture, 8},\n\t{520,\t80,\t\t40,\t40,\t\"3 Boost Cannon10(600)\",\t&SIrapidtexture,\t\t\t\t\tSISPCannonBoostThere,\tSISPCannonBoost,\tSIShopTexture, 9},\n\t{560,\t80,\t\t40,\t40,\t\"3 Boost Cannon11(600)\",\t&SIrapidtexture,\t\t\t\t\tSISPCannonBoostThere,\tSISPCannonBoost,\tSIShopTexture, 10},\n\t{600,\t80,\t\t40,\t40,\t\"3 Boost Cannon12(600)\",\t&SIrapidtexture,\t\t\t\t\tSISPCannonBoostThere,\tSISPCannonBoost,\tSIShopTexture, 11},\n};\n\nShopRegion_t *CurrentRegion = &ShopRegion[0];\n\nSIbullet_t *SI_NewBullet (int bulletlevel)\n{\n\tint a;\t\n\tfor (a = 0; a < MAXBULLETS; a++)\n\t{\n\t\tif (!SIbullet[a].inuse)\n\t\t{\n#if 0\n\t\t\tmemset(&SIbullet[a], 0, sizeof(SIbullet_t));\t\t\t\n\t\t\tSIbullet[a].damage = bulletlevel+1;\n\t\t\tSIbullet[a].inuse = true;\n\t\t\tSIbullet[a].alpha = 1.0;\n\t\t\tSIbullet[a].tex = &SIbullettexture[bulletlevel];\n#else\n\t\t\tif (bulletlevel < 0)\n\t\t\t{\n\t\t\t\tmemcpy(&SIbullet[a], &SIweapons[0], sizeof(SIbullet_t));\n\t\t\t\tSIbullet[a].type = bulletlevel;\n\t\t\t\tSIbullet[a].alphachange+=0.05f;\n\t\t\t}\n\t\t\telse\t\t\t\n\t\t\t\tmemcpy(&SIbullet[a], &SIweapons[bulletlevel], sizeof(SIbullet_t));\n\t\t\tSIbullet[a].inuse = true;\t\t\t\t\t\t\n#endif\n \t\t\treturn &SIbullet[a];\n\t\t}\n\t}\n\treturn NULL;\n}\n\nSIbullet_t *SI_NewExplosion (float cx, float cy, float size)\n{\n\tint a;\n\tfor (a = 0; a < MAXBULLETS; a++)\n\t{\n\t\tif (!SIbullet[a].inuse)\n\t\t{\n\t\t\tmemset(&SIbullet[a], 0, sizeof(SIbullet_t));\n\t\t\tSIbullet[a].inuse = true;\n\t\t\tSIbullet[a].alpha = (float)0.005;\n\t\t\tSIbullet[a].alphachange = (float)0.01;\n\t\t\tSIbullet[a].tex = &SIexplosiontexture;\n\n\t\t\tSIbullet[a].width = size;\n\t\t\tSIbullet[a].height = size;\n\t\t\tSIbullet[a].xpos = cx - size/2;\n\t\t\tSIbullet[a].ypos = cy - size/2;\n\t\t\treturn &SIbullet[a];\n\t\t}\n\t}\n\treturn NULL;\n}\n\nfloat random(void)\n{\t\t\n\treturn (float)(rand() & 0x7fff) / (float)0x7fff;\n}\n\nvoid Draw_String2C(int x, int y, char *string, int extraparam)\n{\n\tstring++;\n\tx -= strlen(string)*4;\n\twhile(*string)\n\t{\n\t\tDraw_Character(x+=8, y, *string++);\n\t}\n}\n\nvoid Draw_Picture(texture tex, float x, float y, float w, float h)\n{\n\tDraw_Image(x, y, w, h, 0, 0, 1, 1, tex);\n}\n\nint levelnametime = 0;\nint frame = 0;\nvoid SI_2D (void)\n{\t\n\tint a;\n\tint b;\n\tint i;\n\tfloat x;\n\tfloat y;\n\n\tframe++;\n\tif (SIShop)\n\t{\n\t\tif (lastlevel)\n\t\t{\n\t\t\tDraw_Colour4f(1, 1, 1, 1);\n\t\t\tDraw_String2C(vid.width / 2, vid.height / 3, \"3You have compleated the game.\", 2);\n\t\t\tDraw_String2C(vid.width / 2, vid.height / 2, \"3Well done.\", 2);\n\t\t\tDraw_String2C(vid.width / 2, (vid.height / 3)*2, \"3Press any key to restart.\", 2);\n\n\t\t\tDraw_String2C(vid.width / 2, vid.height-8, va(\"3Cash: %i\", SIp.cash), 2);\n\t\t\treturn;\n\t\t}\n\t\tif (SIp.powerups & PUP_SHRUNKEN)\n\t\t{\n\t\t\tSIp.width = 16;\n\t\t\tSIp.height = 16;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tSIp.width = 32;\n\t\t\tSIp.height = 32;\n\t\t}\n\n\n\t\tfor (a = 0; a < sizeof(ShopRegion)/sizeof(ShopRegion_t); a++)\n\t\t{\t\t\t\n\t\t\tif ((*ShopRegion[a].AppearCondition) (ShopRegion[a].ident))\n\t\t\t\tDraw_Image(ShopRegion[a].x, ShopRegion[a].y, ShopRegion[a].width, ShopRegion[a].height, 0, 0, 1, 1, *(*ShopRegion[a].Texture) (ShopRegion[a].tex, ShopRegion[a].ident));\n\t\t}\n\n\t\tif (SIp.health < 4)\n\t\t\tDraw_String2C(8, 8, va(\"4%i\", SIp.health), 2);\n\t\telse\n\t\t\tDraw_String2C(8, 8, va(\"3%i\", SIp.health), 2);\n\t\t\n\t\tDraw_Picture(SIplayertexture, SIp.x, SIp.y, SIp.width, SIp.height);\n\t\tfor (a = 0; a < MAXADDONS; a++)\n\t\t{\n\t\t\tif (SIp.cannon[a].power)\n\t\t\t{\n\t\t\t\tif (SIp.cannon[a].tex)\n\t\t\t\t\tDraw_Picture(*SIp.cannon[a].tex, SIp.x + SIp.width*SIp.cannon[a].xoff, SIp.y + SIp.width*SIp.cannon[a].yoff, SIp.width, SIp.height);\n\t\t\t}\n\t\t}\n//\t\tif (SIp.powerups & PUP_CANNON)\n//\t\t\tDraw_Picture(SIcannontexture, SIp.x - SIp.width, SIp.y, SIp.width, SIp.height);\n//\t\tif (SIp.powerups & PUP_CANNON2)\n//\t\t\tDraw_Picture(SIcannontexture, SIp.x + SIp.width, SIp.y, SIp.width, SIp.height);\n\n\t\tif (notenoughcash)\n\t\t{\n\t\t\tnotenoughcash--;\t\t\t\n\t\t\tDraw_Colour4f(1, 1, 1, (float)notenoughcash / 250);\n\t\t\tDraw_String2C(vid.width / 2, vid.height / 2, \"4Not enough cash!\", 2);\n\t\t}\n\t\tDraw_Colour4f(1, 1, 1, 1);\n\t\tif (CurrentRegion)\n\t\t\tDraw_String2C(vid.width / 2, 8, CurrentRegion->text, 2);\n\t\tDraw_String2C(vid.width / 2, vid.height-8, va(\"3Cash: %i\", SIp.cash), 2);\n\t\treturn;\n\t}\n\n\tif (SIp.health > 0)\n\t{\n\t\tDraw_Colour4f(1, 1, 1,\t\t\t\t1);\n\t\t/*\t\t\n\n\t\twas a flame trail. :(\n\t\tgrDisable(GL_TEXTURE_2D);\n\t\tgrShadeModel(GL_SMOOTH);\n\t\tgrBegin(GL_TRIANGLES);\n\n\t\tx = SIp.x+SIp.width/2;\n\t\ty = SIp.y+SIp.height;\n\n\t\ti = 12+rand()%8;\n\n\t\tgrColor4f(0, 0, 1, 1);\n\t\tgrVertex2f(x, y);\n\t\tgrColor4f(1, 0, 1, 0);\n\t\tgrVertex2f(x, y-i/2);\n\t\tgrVertex2f(x+i, y);\n\n\t\tgrColor4f(0, 0, 1, 1);\n\t\tgrVertex2f(x, y);\n\t\tgrColor4f(1, 0, 1, 0);\n\t\tgrVertex2f(x+i, y);\n\t\tgrVertex2f(x, y+i*2);\n\n\t\tgrColor4f(0, 0, 1, 1);\n\t\tgrVertex2f(x, y);\n\t\tgrColor4f(1, 0, 1, 0);\n\t\tgrVertex2f(x-i, y);\n\t\tgrVertex2f(x, y+i*2);\n\n\t\tgrColor4f(0, 0, 0, 1);\n\t\tgrVertex2f(x, y);\n\t\tgrColor4f(1, 0, 1, 0);\n\t\tgrVertex2f(x, y-i/2);\n\t\tgrVertex2f(x-i, y);\n\n\t\t/*\n\t\tgrColor4f(1, 1, 1, 0);\n\t\tgrVertex2f(x+8, y);\n\t\tgrColor4f(1, 1, 1,\t\t\t\t1);\n\t\tgrVertex2f(x, y);\n\t\tgrVertex2f(x, 0);\n\t\tgrColor4f(1, 1, 1, 0);\n\t\tgrVertex2f(x+8, 0);\n\n\t\tgrColor4f(1, 1, 1,\t\t\t\t1);\n\t\tgrVertex2f(x, y);\n\t\tgrColor4f(1, 1, 1, 0);\n\t\tgrVertex2f(x-8, y);\n\t\tgrVertex2f(x-8, 0);\n\t\tgrColor4f(1, 1, 1,\t\t\t\t1);\n\t\tgrVertex2f(x, 0);\n\t\t\n\n\t\tgrColor4f(1, 1, 1, 0);\n\t\tgrVertex2f(x+8, y);\n\t\tgrColor4f(1, 1, 1,\t\t\t\t1);\n\t\tgrVertex2f(x, y);\n\t\tgrColor4f(1, 1, 1, 0);\n\t\tgrVertex2f(x, y+8);\n\t\tgrVertex2f(x+8, y+8);\n\n\t\tgrVertex2f(x, y+8);\n\t\tgrColor4f(1, 1, 1,\t\t\t\t1);\n\t\tgrVertex2f(x, y);\n\t\tgrColor4f(1, 1, 1, 0);\n\t\tgrVertex2f(x-8, y);\n\t\tgrVertex2f(x-8, y+8);\n\t\t*/\n/*\n\t\tgrEnd();\n\t\tDraw_Colour4f(1, 1, 1, 1);\n*/\n\t\tDraw_Picture(SIplayertexture, SIp.x, SIp.y, SIp.width, SIp.height);\n\t\tfor (a = 0; a < MAXADDONS; a++)\n\t\t{\n\t\t\tif (SIp.cannon[a].power)\n\t\t\t{\n\t\t\t\tif (SIp.cannon[a].tex)\n\t\t\t\t\tDraw_Picture(*SIp.cannon[a].tex, SIp.x + SIp.width*SIp.cannon[a].xoff, SIp.y + SIp.width*SIp.cannon[a].yoff, SIp.width, SIp.height);\n\t\t\t}\n\t\t}\n\t}\n\n\tfor (a = 0; a < MAXMONSTERS; a++)\n\t{\n\t\tif (SImonster[a].health > 0)\n\t\t{\n\t\t\tDraw_Picture(*SImonster[a].EnemyType->picture, SImonster[a].xpos, SImonster[a].ypos, SImonster[a].width, SImonster[a].height);\n\t\t}\n\t}\n\n\t//grEnable(GL_BLEND);\n\tfor (a = 0; a < MAXBULLETS; a++)\n\t{\n\t\tif (SIbullet[a].inuse)\n\t\t{\n\t\t\tswitch (SIbullet[a].type)\n\t\t\t{\n\t\t\tcase 1:\n\t\t\t\tfor (b = 0; b < SIbullet[a].charge; b++)\n\t\t\t\t{\n\t\t\t\t\tDraw_Colour4f(random(), random(), random(), SIbullet[a].alpha);\n\t\t\t\t\tDraw_Line (SIbullet[a].xpos+SIbullet[a].width/2,\n\t\t\t\t\t\tSIbullet[a].ypos+SIbullet[a].height/2,\n\t\t\t\t\t\tSIbullet[a].xpos+SIbullet[a].width/2+(float)sin((frame+2)*(a+1)*(b+1))*SIbullet[a].width,\n\t\t\t\t\t\tSIbullet[a].ypos+SIbullet[a].height/2+(float)cos((frame+2)*(a+1)*(b+1))*SIbullet[a].height);\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase 2:\n\t\t\t\tx=realtime*50;\n\n\t\t\t\tDraw_Colour4f(1, 1, 1, SIbullet[a].alpha);\n\t\t\t\tDraw_Picture(*SIbullet[a].tex, SIbullet[a].xpos, 0, SIbullet[a].width, SIbullet[a].ypos+SIbullet[a].height);\n\n\t\t\t\tbreak;\n\n/*\t\t\tcase 3:\n\t\t\t\tx=realtime*50;\n\t\t\t\tgrColor4f(1, 1, 1, SIbullet[a].alpha);\n\n\t\t\t\tgrDisable(GL_TEXTURE_2D);\n\t\t\t\tgrBegin(GL_LINE_STRIP);\n//\t\t\t\tgrVertex2f(SIbullet[a].xpos, SIbullet[a].ypos);\n\t\t\t\tfor (y = SIbullet[a].ypos; y>0; y-=SIbullet[a].yvel*5+0.2f,x+=1)\n\t\t\t\t\tgrVertex2f((float)SIbullet[a].xpos+(float)SIbullet[a].charge*(float)sin(x), y);\n\t\t\t\t\n//\t\t\t\tfor (y = SIbullet[a].ypos; y>0; y-=25,x+=1)\n//\t\t\t\t\tgrVertex2f((float)SIbullet[a].xpos+(float)SIbullet[a].charge*(float)sin(x), y);\t\t\t\t\n\n//\t\t\t\tfor (y = SIbullet[a].ypos; y>0; y-=25,x+=1)\n//\t\t\t\t\tgrVertex2f((float)SIbullet[a].xpos+(float)SIbullet[a].charge*(float)sin(x), y);\n\t\t\t\tgrEnd();\n\t\t\t\tgrEnable(GL_TEXTURE_2D);\n\t\t\t\tbreak;\n*/\n\t\t\tdefault:\n\t\t\t\tDraw_Colour4f(1, 1, 1, SIbullet[a].alpha);\n\t\t\t\tDraw_Picture(*SIbullet[a].tex, SIbullet[a].xpos, SIbullet[a].ypos, SIbullet[a].width, SIbullet[a].height);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\tDraw_Colour4f(1, 1, 1, 1);\n\n\tif (SIp.health <= 0)\n\t\tDraw_String2C(vid.width / 2, vid.height / 2, \"4You LOOSE!\", 2);\n\telse if (livingmonsters == 0)\n\t{\n\t\tDraw_String2C(vid.width / 2, vid.height / 2, \"4You WIN!\", 2);\n\t\tif (SIp.y + SIp.height < 0)\n\t\t\tDraw_String2C(vid.width / 2, (vid.height / 4) * 3, \"3Press space!\", 2);\n\t}\n\telse if (levelnametime)\n\t{\n\t\tDraw_Colour4f(1, 1, 1, (float)levelnametime/500);\n\t\tlevelnametime--;\n\t\tDraw_String2C(vid.width/2, vid.height/2, SILevelName, 2);\n\t\tDraw_Colour4f(1, 1, 1, 1);\n\t}\n\n\tDraw_String2C(vid.width / 2, 4, va(\"3%i\", SIp.cash), 1);\n\tif (SIp.health < 4)\n\t\tDraw_String2C(8, 8, va(\"4%i\", SIp.health), 2);\n\telse\n\t\tDraw_String2C(8, 8, va(\"3%i\", SIp.health), 2);\n}\n\nvoid SI_LoadTextures (void)\n{\n\tint a;\n\tfor (a = 0; a < monsterpics; a++)\n\t\tSIbaddietexture[a]\t= Draw_LoadImage(va(\"spaceinv/enemy%i\", a+1), false);\n\tfor (a = 0; a < weaponlevels;a++)\n\t\tSIbullettexture[a]\t= Draw_LoadImage(va(\"spaceinv/gun%i\", a+1), false);\n\tSIplayertexture\t\t= Draw_LoadImage(\"spaceinv/siplayer\", false);\n\tSIexplosiontexture\t= Draw_LoadImage(\"spaceinv/bang\", false);\n\tSIhealthtexture\t\t= Draw_LoadImage(\"spaceinv/health\", false);\n\tSIleaveshoptexture\t= Draw_LoadImage(\"spaceinv/leaveshop\", false);\n\tSIshrinktexture\t\t= Draw_LoadImage(\"spaceinv/shrink\",  false);\n\tSIcannontexture\t\t= Draw_LoadImage(\"spaceinv/cannon\", false);\n\tSIquittexture\t\t= Draw_LoadImage(\"spaceinv/quit\", false);\n\tSIsavegametexture\t= Draw_LoadImage(\"spaceinv/save\", false);\n\tSIloadgametexture\t= Draw_LoadImage(\"spaceinv/load\", false);\t\n\tSIsideshottexture\t= Draw_LoadImage(\"spaceinv/sideshot\", false);\n\tSIrapidtexture\t\t= Draw_LoadImage(\"spaceinv/rapid\", false);\t\n}\n\n//Add side cannons?\nbool SIHitPlayer(float x, float y, float width, float height)\n{\t\t\n\tif (x + width > SIp.x && x < SIp.x + SIp.width && y + height > SIp.y && y < SIp.y + SIp.height)\n\t\treturn true;\n\telse\n\t\treturn false;\n}\n\nSImonster_t *SIHitEnemy(float x, float y, float width, float height)\n{\n\tint a;\n\tfor (a = 0; a < MAXMONSTERS; a++)\n\t{\n\t\tif (SImonster[a].health)\n\t\t{\n\t\t\tif (x + width > SImonster[a].xpos && x < SImonster[a].xpos + SImonster[a].width && y + height > SImonster[a].ypos && y < SImonster[a].ypos + SImonster[a].height)\n\t\t\t\treturn &SImonster[a];\n\t\t}\n\t}\n\treturn NULL;\n}\n\nvoid SIKillEnemy(SImonster_t *en)\n{\n\ten->health = 0;\n\tlivingmonsters--;\n\n\tSI_NewExplosion(en->xpos + en->width/2, en->ypos+ en->height/2, (en->width + en->height)/2);\n\n\tSIp.cash += en->EnemyType->reward;\n\n\tSound_Start(SoundExplosion);\n\n\tif (livingmonsters <= 0)\n\t\tSound_Start(SoundWin);\n}\n\nvoid SIHurtPlayer(int dam)\n{\n\tif (SIp.health <= 0)\t//already dead\n\t\treturn;\n\tSIp.health -= dam;\n\tif (SIp.health < 0)\n\t\tSIp.health = 0;\t//no negative health\n\tif (SIp.health <= 0)\t//if final blow...\n\t{\n\t\tSI_NewExplosion(SIp.x + SIp.width/2, SIp.y + SIp.height/2, (SIp.width + SIp.height));\n\t\tSound_Start(SoundLoose);\n\n\t\tdeadtime = realtime + 4.0f;\n\t}\n\telse\n\t\tSound_Start(SoundHit);\n}\n\nfloat nextthink;\n\nvoid SI_MouseMove(int x, int y);\nvoid SI_Main(int mousex, int mousey)\n{\n\tint a;\n\tint i;\n\tint tm;\n\tfloat tmdist;\n\n\tSIbullet_t *bul;\n\tSImonster_t *en;\n\n\tvec3_t v;\n\n\tif (nextthink > realtime)\n\t{\n//\t\tCon_Printf(\"no time\\n\");\n\t\treturn;\n\t}\n//\tCon_Printf(\"time\\n\");\n\tnextthink += 0.02f;\n\n\tif (nextthink < realtime-1)\t//don't run more than a second behind\n\t\tnextthink = realtime-1;\n\n\tif (SIShop)\n\t{\t\t\n\t\tif (lastlevel)\n\t\t{\n//\t\t\tCon_Printf(\"Lastlevel\\n\");\n\t\t\treturn;\n\t\t}\n\n//\t\tCon_Printf(\"%i (%f %f)\\n\", SIp.contols, SIp.x, SIp.y);\n\n\t\tif (!SIp.health)\n\t\t\tSIp.health=1;\n\n\t\tif (SIp.contols & CONT_LEFTKEY)\n\t\t\tSIp.x -= PLAYERSHIPSPEEDX;\n\t\tif (SIp.contols & CONT_RIGHTKEY)\n\t\t\tSIp.x += PLAYERSHIPSPEEDX;\n\n\t\tif (SIp.x < 0)\n\t\t\tSIp.x = 0;\n\t\tif (SIp.x + SIp.width >= vid.width)\n\t\t\tSIp.x = vid.width - SIp.width;\n\n\t\tif (SIp.contols & CONT_DOWNKEY)\n\t\t\tSIp.y += PLAYERSHIPSPEEDY;\n\t\tif (SIp.contols & CONT_UPKEY)\n\t\t\tSIp.y -= PLAYERSHIPSPEEDY;\n\n\t\tif (SIp.y < 0)\n\t\t\tSIp.y = 0;\n\t\tif (SIp.y + SIp.height >= vid.height)\n\t\t\tSIp.y = vid.height - SIp.height;\n\n\t\tCurrentRegion = NULL;\n\t\tfor (a = 0; a < sizeof(ShopRegion) / sizeof(ShopRegion_t); a++)\n\t\t{\t\t\t\n\t\t\tif (SIp.x+SIp.width/2 < ShopRegion[a].x + ShopRegion[a].width && SIp.x+SIp.width/2 > ShopRegion[a].x &&\n\t\t\t\tSIp.y+SIp.height/2 < ShopRegion[a].y + ShopRegion[a].height && SIp.y+SIp.height/2 > ShopRegion[a].y)\n\t\t\t{\n\t\t\t\tif ((*ShopRegion[a].AppearCondition) (ShopRegion[a].ident))\n\t\t\t\t{\n\t\t\t\t\tCurrentRegion = &ShopRegion[a];\t\t\t\t\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (SIp.contols & CONT_FIREKEY && CurrentRegion)\n\t\t{\n\t\t\t(*CurrentRegion->CallFunc) (CurrentRegion->ident);\n\t\t\tSIp.contols &= ~CONT_FIREKEY;\n\t\t}\t\t\n\n\t\treturn;\n\t}\n\n\tleveltime += 0.02f;\n\n\n\tif (SIp.health > 0)\n\t{\n\t\tif (SIp.contols & CONT_LEFTKEY || (SIp.contols & CONT_MOUSE && SIp.x+SIp.width/2 > mousex))\n\t\t\tSIp.x -= PLAYERSHIPSPEEDX;\t\n\t\tif (SIp.contols & CONT_RIGHTKEY || (SIp.contols & CONT_MOUSE && SIp.x+SIp.width/2 < mousex))\n\t\t\tSIp.x += PLAYERSHIPSPEEDX;\n\n\t\tif (SIp.x < 0)\n\t\t\tSIp.x = 0;\n\t\tif (SIp.x + SIp.width >= vid.width)\n\t\t\tSIp.x = vid.width - SIp.width;\n\n\t\tif (livingmonsters > 0)\n\t\t{\n\t\t\tif (SIp.contols & CONT_DOWNKEY || (SIp.contols & CONT_MOUSE && SIp.y+SIp.height/2 < mousey))\n\t\t\t\tSIp.y += PLAYERSHIPSPEEDY;\n\t\t\tif (SIp.contols & CONT_UPKEY || (SIp.contols & CONT_MOUSE && SIp.y+SIp.height/2 > mousey))\n\t\t\t\tSIp.y -= PLAYERSHIPSPEEDY;\n\n\t\t\tif (SIp.y < (vid.height / 2))\n\t\t\t\tSIp.y = (float)(vid.height / 2);\n\t\t\tif (SIp.y + SIp.height >= vid.height)\n\t\t\t\tSIp.y = vid.height - SIp.height;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tSIp.y -= PLAYERSHIPSPEEDY;\n\t\t}\n\n\t\tif (SIp.contols & CONT_FIREKEY)\n\t\t{\t\t\n\t\t\t/*\n  \t\t\tif (bul = SI_NewBullet(SIp.weaponpower))\n\t\t\t{\t\t\t\t\t\t\n\n\t\t\t\tSound_Start(SoundFire);\n\n\t\t\t\tbul->ypos = SIp.y - bul->height;\n\t\t\t\tbul->xpos = SIp.x + (SIp.width - bul->width) / 2;\n\t\t\t\tbul->xvel = 0;\n\n\t\t\t\tSIp.refire = SIp.reloadtime;\n\t\t\t\t*/\n\n#if 1\n\t\t\t\tfor (a = FIRSTCANNON; a <= LASTCANNON; a++)\n\t\t\t\t{\n\t\t\t\t\tif (SIp.cannon[a].power && SIp.cannon[a].refire < leveltime)\n\t\t\t\t\t{\t\t\t\t\t\t\n\t\t\t\t\t\tif (bul = SI_NewBullet(SIp.cannon[a].power - 1))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tbul->ypos = SIp.y - bul->height;\n\t\t\t\t\t\t\tbul->xpos = SIp.x + SIp.width*SIp.cannon[a].xoff + (SIp.width - bul->width) / 2;\t\t\n\t\t\t\t\t\t\tbul->xvel = 0;\n\n\t\t\t\t\t\t\tSIp.cannon[a].refire = leveltime + SIp.cannon[a].reloadtime;\n\n\t\t\t\t\t\t\tSound_Start(SoundFire);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\t\n\t\t\t\t}\n\n\t\t\t\tfor (a = FIRSTCENTRALMOD; a <= LASTCENTRALMOD; a++)\n\t\t\t\t{\n\t\t\t\t\tif (SIp.cannon[a].power && SIp.cannon[a].refire < leveltime)\n\t\t\t\t\t{\t\t\t\t\t\t\n\t\t\t\t\t\tif (bul = SI_NewBullet(SIp.cannon[a].power - 1))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tbul->ypos = SIp.y - bul->height;\n\t\t\t\t\t\t\tbul->xpos = SIp.x + SIp.width*SIp.cannon[a].xoff + (SIp.width - bul->width) / 2;\t\t\n\t\t\t\t\t\t\tbul->xvel = 0;\t\t\t\t\t\t\t\n\n\t\t\t\t\t\t\tSIp.cannon[a].refire = leveltime + SIp.cannon[a].reloadtime;\n\n\t\t\t\t\t\t\tSound_Start(SoundFire);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\t\n\t\t\t\t}\n\n\t\t\t\t\n\t\t\t\tfor (a = FIRSTROCKET; a <= LASTROCKET; a++)\n\t\t\t\t{\n\t\t\t\t\tif (SIp.cannon[a].power && SIp.cannon[a].refire < leveltime)\n\t\t\t\t\t{\t\t\t\t\t\t\n\t\t\t\t\t\tif (bul = SI_NewBullet(-1))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tbul->ypos = SIp.y - bul->height;\n\t\t\t\t\t\t\tbul->xpos = SIp.x + SIp.width*SIp.cannon[a].xoff + (SIp.width - bul->width) / 2;\t\t\n\t\t\t\t\t\t\tbul->xvel = 0;\n\t\t\t\t\t\t\tbul->yvel = -1;\n\n\t\t\t\t\t\t\tSIp.cannon[a].refire = leveltime + SIp.cannon[a].reloadtime;\n\n\t\t\t\t\t\t\tSound_Start(SoundFire);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\t\n\t\t\t\t}\n\n#else\n\t\t\t\tif (SIp.powerups & PUP_CANNON)\n\t\t\t\t{\n\t\t\t\t\tbul = SI_NewBullet(SIp.weaponpower);\n\t\t\t\t\tif (bul)\n\t\t\t\t\t{\n\t\t\t\t\tbul->width = 10;\n\t\t\t\t\tbul->height = 10;\n\t\t\t\t\tbul->damage = 1;\n\t\t\t\t\tbul->ypos = SIp.y - bul->height;\n\t\t\t\t\tbul->xpos = SIp.x - SIp.width + (SIp.width - bul->width) / 2;\n\t\t\t\t\tbul->xvel = 0;\n\t\t\t\t\tbul->yvel = (float)0.004;\n\t\t\t\t\tbul->yaccel = (float)0.002;\t\t\t\t\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (SIp.powerups & PUP_CANNON2)\n\t\t\t\t{\n\t\t\t\t\tbul = SI_NewBullet(SIp.weaponpower);\n\t\t\t\t\tif (bul)\n\t\t\t\t\t{\n\t\t\t\t\t\tbul->width = 10;\n\t\t\t\t\t\tbul->height = 10;\n\t\t\t\t\t\tbul->damage = 1;\n\t\t\t\t\t\tbul->ypos = SIp.y - bul->height;\n\t\t\t\t\t\tbul->xpos = SIp.x + SIp.width + (SIp.width - bul->width) / 2;\n\t\t\t\t\t\tbul->xvel = 0;\n\t\t\t\t\t\tbul->yvel = (float)0.004;\n\t\t\t\t\t\tbul->yaccel = (float)0.002;\n\t\t\t\t\t}\n\t\t\t\t}\n#endif\n\t\t\t\t/*\n\t\t\t\tif (SIp.sidegun)\n\t\t\t\t{\t\t\t\t\t\n\t\t\t\t\tif (bul = SI_NewBullet(SIp.sidegun -1))\n\t\t\t\t\t{\n\t\t\t\t\t\tbul->ypos = SIp.y - bul->height;\n\t\t\t\t\t\tbul->xpos = SIp.x + (SIp.width - bul->width) / 2;\n\t\t\t\t\t\tbul->xvel = (float)(random()-0.5)*2;\n\t\t\t\t\t}\n\t\t\t\t}\t\t\t\t\n\t\t\t}\n\t\t\t*/\n\t\t}\n\t}\n\telse\n\t{\n\t\tif (deadtime < realtime)\n\t\t\tSIRestartGame();\n\t}\n\n\tfor (a = 0; a < MAXSQUADS; a++)\n\t{\n\t\tif (SISquad[a].number && SISquad[a].time < leveltime)\n\t\t{\n\t\t\tSISquad[a].time += SISquad[a].interval;\n\n\t\t\tfor (i = 0; i < MAXMONSTERS; i++)\n\t\t\t{\n\t\t\t\tif (SImonster[i].health <= 0)\n\t\t\t\t{\n\t\t\t\t\tSImonster[i].routenum = SISquad[a].route;\n\t\t\t\t\tSImonster[i].destnum = 1;\n\t\t\t\t\tSImonster[i].width = SIEnemyType[SISquad[a].type].width;\n\t\t\t\t\tSImonster[i].height = SIEnemyType[SISquad[a].type].height;\n\t\t\t\t\tSImonster[i].xpos = ((SIRoute[SImonster[i].routenum].pos[0])[0] - SImonster[i].width/2)/100.f*vid.height;\n\t\t\t\t\tSImonster[i].ypos = ((SIRoute[SImonster[i].routenum].pos[0])[1] - SImonster[i].height/2)/100.f*vid.height;\n\n\t\t\t\t\tSImonster[i].EnemyType = &SIEnemyType[SISquad[a].type];\n\t\t\t\t\tSImonster[i].health = SIEnemyType[SISquad[a].type].health;\n\t\t\t\t\tSImonster[i].yvel = ENEMYSHIPSPEEDY;\n\t\t\t\t\tSImonster[i].xvel = (float)ENEMYSHIPSPEEDX;\n\t\t\t\t\tSISquad[a].number-=1;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\ttm = -1;\n\ttmdist = 32767;\n\tfor (a = 0; a < MAXMONSTERS; a++)\n\t{\n\t\tif (SImonster[a].health > 0)\n\t\t{\t\t\t\n\t\t\tif (tmdist > sqrt((SIp.x - SImonster[a].xpos)*(SIp.x - SImonster[a].xpos)+(SIp.y - SImonster[a].ypos)*(SIp.y - SImonster[a].ypos)))\n\t\t\t{\n\t\t\t\ttm = a;\n\t\t\t\ttmdist = (float)sqrt((SIp.x - SImonster[a].xpos)*(SIp.x - SImonster[a].xpos)+(SIp.y - SImonster[a].ypos)*(SIp.y - SImonster[a].ypos));\n\t\t\t}\n\t\t\tif (SImonster[a].EnemyType->AI)\n\t\t\t{\n\t\t\t\tif (SImonster[a].xpos+SImonster[a].width/2 > SIp.x+SIp.width/2)\n\t\t\t\t{\n\t\t\t\t\tif (SImonster[a].xpos+SImonster[a].width/2 - 0.8 < SIp.x+SIp.width/2)\n\t\t\t\t\t\tSImonster[a].xvel = SIp.x+SIp.width/2-(SImonster[a].xpos+SImonster[a].width/2);\n\t\t\t\t\telse\n\t\t\t\t\t\tSImonster[a].xvel = (float)-0.8;\n\t\t\t\t}\n\t\t\t\telse if (SImonster[a].xpos < SIp.x)\n\t\t\t\t{\t\t\t\t\t\n\t\t\t\t\tif (SImonster[a].xpos+SImonster[a].width/2 + 0.8 > SIp.x+SIp.width/2)\n\t\t\t\t\t\tSImonster[a].xvel = SIp.x+SIp.width/2-(SImonster[a].xpos+SImonster[a].width/2);\n\t\t\t\t\telse\n\t\t\t\t\t\tSImonster[a].xvel = (float)0.8;\n\t\t\t\t}\n\n\t\t\t\tSImonster[a].ypos += SImonster[a].yvel;\n\t\t\t\tSImonster[a].xpos += SImonster[a].xvel;\n\t\t\t\tSImonster[a].yvel = 0;\n\n\t\t\t\tif (SImonster[a].xpos < 0)\n\t\t\t\t\tSImonster[a].xpos = 0;\n\t\t\t\tif (SImonster[a].xpos + SImonster[a].width >= vid.width)\n\t\t\t\t\tSImonster[a].xpos = vid.width - SImonster[a].width;\n\n\t\t\t\tif (SIHitPlayer(SImonster[a].xpos, SImonster[a].ypos, SImonster[a].width, SImonster[a].height) && SIp.health > 0)\n\t\t\t\t{\t\t\t\t\n\t\t\t\t\tSIHurtPlayer(SImonster[a].health);\n\t\t\t\t\tSIKillEnemy(&SImonster[a]);\t\t\t\t\n\t\t\t\t}\n\t\t\t\telse if (random() < SImonster[a].EnemyType->FireProb && SIp.health > 0 && SImonster[a].EnemyType->shotdamage >= 0)\n\t\t\t\t{\n\t\t\t\t\tif (bul = SI_NewBullet(SImonster[a].EnemyType->shotdamage))\n\t\t\t\t\t{\n\t\t\t\t\t\tbul->yvel *= -1;\n\t\t\t\t\t\tbul->yvel -= SImonster[a].yvel;\n\t\t\t\t\t\tbul->yaccel *= -1;\n\t\t\t\t\t\tbul->xpos = SImonster[a].xpos + SImonster[a].width/2-bul->width/2;\n\t\t\t\t\t\tbul->ypos = SImonster[a].ypos + SImonster[a].height;\n\t//\t\t\t\t\tbul->width = 10;\n\t//\t\t\t\t\tbul->height = 10;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tSImonster[a].ypos += SImonster[a].yvel;\n\t\t\t\tSImonster[a].xpos += SImonster[a].xvel;\n\n\t\t\t\tif (SImonster[a].xpos < 0)\n\t\t\t\t\tSImonster[a].xpos = 0;\n\t\t\t\tif (SImonster[a].xpos + SImonster[a].width >= vid.width)\n\t\t\t\t\tSImonster[a].xpos = vid.width - SImonster[a].width;\n\n\t\t\t\tif (SIHitPlayer(SImonster[a].xpos, SImonster[a].ypos, SImonster[a].width, SImonster[a].height) && SIp.health > 0)\n\t\t\t\t{\t\t\t\t\n\t\t\t\t\tSIHurtPlayer(SImonster[a].health);\n\t\t\t\t\tSIKillEnemy(&SImonster[a]);\t\t\t\t\n\t\t\t\t}\n\t\t\t\telse if (SImonster[a].ypos > vid.height)\n\t\t\t\t{\n\t\t\t\t\tSImonster[a].ypos = 0 - SImonster[a].height;\n\t\t\t\t\tSImonster[a].xpos = random() * vid.width;\n\t\t\t\t}\n\t\t\t\telse if (random() < SImonster[a].EnemyType->FireProb && SIp.health > 0 && SImonster[a].EnemyType->shotdamage >= 0)\n\t\t\t\t{\n\t\t\t\t\tif (bul = SI_NewBullet(SImonster[a].EnemyType->shotdamage))\n\t\t\t\t\t{\n\t\t\t\t\t\tbul->yvel *= -1;\n\t\t\t\t\t\tbul->yvel -= SImonster[a].yvel;\n\t\t\t\t\t\tbul->yaccel *= -1;\n\t\t\t\t\t\tbul->xpos = SImonster[a].xpos + SImonster[a].width/2-bul->width/2;\n\t\t\t\t\t\tbul->ypos = SImonster[a].ypos + SImonster[a].height;\n\t//\t\t\t\t\tbul->width = 10;\n\t//\t\t\t\t\tbul->height = 10;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse if (random() < 0.001)\n\t\t\t\t\tSImonster[a].xvel = (float)ENEMYSHIPSPEEDX;\n\t\t\t}\n\t\t\tif (SIEnemyType[a].healthregen)\n\t\t\t{\n\t\t\t\tif (SImonster[a].health < SImonster[a].health)\n\t\t\t\t{\n\t\t\t\t\tif (SIEnemyType[a].healthregen)\n\t\t\t\t\t\tSIEnemyType[a].healthregen--;\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tSIEnemyType[a].healthregen = SImonster[a].timetoregen;\n\t\t\t\t\t\tSIEnemyType[a].health++;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\t\n\t}\n\n\tfor (a = 0; a < MAXBULLETS; a++)\n\t{\n\t\tif (SIbullet[a].inuse)\n\t\t{\n\t\t\tif (SIbullet[a].type == -2)\n\t\t\t{\n\t\t\t\tSIbullet[a].ypos += SIbullet[a].yvel;\n\t\t\t\tSIbullet[a].xpos += SIbullet[a].xvel;\n\n\t\t\t\tSIbullet[a].alpha += SIbullet[a].alphachange;\n\t\t\t\tif (SIbullet[a].alpha >= 1 && SIbullet[a].alphachange > 0)\n\t\t\t\t\tSIbullet[a].alphachange *= -1;\n\t\t\t\telse if (SIbullet[a].alphachange && SIbullet[a].alpha <= 0)\n\t\t\t\t\tSIbullet[a].inuse = false;\n\t\t\t}\n\t\t\telse if (SIbullet[a].type == -1)\n\t\t\t{\n\t\t\t\tif (tm >= 0)\n\t\t\t\t{\n\t\t\t\t\tv[0] = SImonster[tm].xpos+SImonster[tm].width/2 - SIbullet[a].xpos;\n\t\t\t\t\tv[1] = SImonster[tm].ypos+SImonster[tm].height/2 - SIbullet[a].ypos;\n\t\t\t\t\tv[2] = 0;\n\t\t\t\t\tvecNorm(v, v);\n\n\t\t\t\t\tif (bul = SI_NewBullet(-2))\n\t\t\t\t\t{\n\t\t\t\t\t\tbul->xvel = -v[0]*3;\n\t\t\t\t\t\tbul->yvel = -v[1]*3;\n\t\t\t\t\t\tbul->alphachange = 0.05f;\n\t\t\t\t\t\tbul->xpos = SIbullet[a].xpos;\n\t\t\t\t\t\tbul->ypos = SIbullet[a].ypos;\n\t\t\t\t\t}\n\n\t\t\t\t\tv[0] += SIbullet[a].xvel;\n\t\t\t\t\tv[1] += SIbullet[a].yvel;\n\n\t\t\t\t\tvecNorm(v, v);\n\t\t\t\t\tSIbullet[a].xvel = v[0]*5;\n\t\t\t\t\tSIbullet[a].yvel = v[1]*5;\n\n\t\t\t\t}\n\t\t\t\tSIbullet[a].ypos += SIbullet[a].yvel;\n\t\t\t\tSIbullet[a].xpos += SIbullet[a].xvel;\n\n\t\t\t\tif (en = SIHitEnemy(SIbullet[a].xpos, SIbullet[a].ypos, SIbullet[a].width, SIbullet[a].height))\n\t\t\t\t{\n\t\t\t\t\ten->health -= SIbullet[a].damage;\n\t\t\t\t\tif (en->health <= 0)\n\t\t\t\t\t{\n\t\t\t\t\t\tSIKillEnemy(en);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tSound_Start(SoundHit);\n\t\t\t\t\t}\n\n\t\t\t\t\tSIbullet[a].alpha = 0.5f;\n\t\t\t\t\tSIbullet[a].alphachange = 0.04f;\n\t\t\t\t\tSIbullet[a].tex = &SIexplosiontexture;\n\t\t\t\t\tSIbullet[a].xvel = 0;\n\t\t\t\t\tSIbullet[a].yvel = 0;\n\t\t\t\t\tSIbullet[a].damage = 0;\n\t\t\t\t\tSIbullet[a].yaccel = 0;\n\t\t\t\t\tSIbullet[a].charge = 0;\n\t\t\t\t\tSIbullet[a].type = 0;\n\t\t\t\t}\n\n\t\t\t\tif (SIbullet[a].ypos + SIbullet[a].height < 0 || SIbullet[a].ypos > vid.height)\n\t\t\t\t\tSIbullet[a].inuse = false;\n\t\t\t}\n\t\t\telse if (SIbullet[a].ypos + SIbullet[a].height < 0 || SIbullet[a].ypos > vid.height)\n\t\t\t{\n\t\t\t\tSIbullet[a].inuse = false;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tSIbullet[a].yvel += SIbullet[a].yaccel;\n\t\t\t\tSIbullet[a].ypos -= SIbullet[a].yvel;\n\t\t\t\tSIbullet[a].xpos += SIbullet[a].xvel;\n\n\t\t\t\tif (SIbullet[a].type == 2)\n\t\t\t\t{\n\t\t\t\t\tif (en = SIHitEnemy(SIbullet[a].xpos, 0, SIbullet[a].width, SIbullet[a].ypos))\n\t\t\t\t\t{\n\t\t\t\t\t\ten->health -= SIbullet[a].damage;\n\t\t\t\t\t\tif (en->health <= 0)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tSIKillEnemy(en);\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tSound_Start(SoundHit);\n\t\t\t\t\t\t}\n\n//\t\t\t\t\t\tSIbullet[a].alpha = 0.5f;\n//\t\t\t\t\t\tSIbullet[a].alphachange = 0.04f;\n//\t\t\t\t\t\tSIbullet[a].tex = &SIexplosiontexture;\n//\t\t\t\t\t\tSIbullet[a].xvel = 0;\n//\t\t\t\t\t\tSIbullet[a].yvel = 0;\n//\t\t\t\t\t\tSIbullet[a].damage = 0;\n//\t\t\t\t\t\tSIbullet[a].yaccel = 0;\n//\t\t\t\t\t\tSIbullet[a].charge = 0;\n//\t\t\t\t\t\tSIbullet[a].type = 0;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse if (SIbullet[a].yvel > 0)\n\t\t\t\t{\n\t\t\t\t\tif (en = SIHitEnemy(SIbullet[a].xpos, SIbullet[a].ypos, SIbullet[a].width, SIbullet[a].height))\n\t\t\t\t\t{\n\t\t\t\t\t\ten->health -= SIbullet[a].damage;\n\t\t\t\t\t\tif (en->health <= 0)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tSIKillEnemy(en);\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tSound_Start(SoundHit);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tSIbullet[a].alpha = 0.5f;\n\t\t\t\t\t\tSIbullet[a].alphachange = 0.04f;\n\t\t\t\t\t\tSIbullet[a].tex = &SIexplosiontexture;\n\t\t\t\t\t\tSIbullet[a].xvel = 0;\n\t\t\t\t\t\tSIbullet[a].yvel = 0;\n\t\t\t\t\t\tSIbullet[a].damage = 0;\n\t\t\t\t\t\tSIbullet[a].yaccel = 0;\n\t\t\t\t\t\tSIbullet[a].charge = 0;\n\t\t\t\t\t\tSIbullet[a].type = 0;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse if (SIbullet[a].yvel < 0)\n\t\t\t\t{\n\t\t\t\t\tif (SIHitPlayer(SIbullet[a].xpos, SIbullet[a].ypos, SIbullet[a].width, SIbullet[a].height))\n\t\t\t\t\t{\n\t\t\t\t\t\tSIHurtPlayer(SIbullet[a].damage);\n\t\t\t\t\t\tSIbullet[a].alpha = 0.5f;\n\t\t\t\t\t\tSIbullet[a].alphachange = 0.04f;\n\t\t\t\t\t\tSIbullet[a].tex = &SIexplosiontexture;\n\t\t\t\t\t\tSIbullet[a].xvel = 0;\n\t\t\t\t\t\tSIbullet[a].yvel = 0;\n\t\t\t\t\t\tSIbullet[a].damage = 0;\n\t\t\t\t\t\tSIbullet[a].yaccel = 0;\n\t\t\t\t\t\tSIbullet[a].charge = 0;\n\t\t\t\t\t\tSIbullet[a].type = 0;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tSIbullet[a].alpha += SIbullet[a].alphachange;\n\t\t\t\tif (SIbullet[a].alpha >= 1 && SIbullet[a].alphachange > 0)\n\t\t\t\t\tSIbullet[a].alphachange *= -1;\n\t\t\t\telse if (SIbullet[a].alphachange && SIbullet[a].alpha <= 0)\n\t\t\t\t\tSIbullet[a].inuse = false;\n\t\t\t}\n\t\t}\n\t}\n}\n\nvoid SI_KeyUp(int k)\n{\n\tif (k == K_LEFTARROW)\n\t\tSIp.contols &= ~CONT_LEFTKEY;\n\telse if (k == K_RIGHTARROW)\n\t\tSIp.contols &= ~CONT_RIGHTKEY;\n\telse if (k == K_UPARROW)\n\t\tSIp.contols &= ~CONT_UPKEY;\n\telse if (k == K_DOWNARROW)\n\t\tSIp.contols &= ~CONT_DOWNKEY;\n\telse if (k == K_MOUSE1 || k == K_SPACE)\n\t\tSIp.contols &= ~CONT_FIREKEY;\n\telse if (k == K_MOUSE2)\n\t\tSIp.contols &= ~CONT_MOUSE;\n}\nvoid SI_KeyDown(int k)\n{\n\tif (SIShop && lastlevel)\n\t{\n\t\tSIlevel = 0;\n\t\tlastlevel = false;\n\t\treturn;\n\t}\n\telse if (livingmonsters <= 0 && SIp.y + SIp.height < 0 && SIShop == false)\n\t{\n\t\tSIShop = true;\n\t\tSIlevel+=1;\n\t\tSIp.x = ShopRegion[0].x + ShopRegion[0].width/2;\n\t\tSIp.y = ShopRegion[0].y + ShopRegion[0].height/2;\n\t\treturn;\n\t}\n\telse if (SIp.health <= 0)\n\t{\n\t\tif (k == K_SPACE && !(SIp.contols & CONT_FIREKEY))\n\t\t\tdeadtime += 1;\n\t\treturn;\n\t}\n\n\tif (k == K_LEFTARROW)\n\t\tSIp.contols |= CONT_LEFTKEY;\n\telse if (k == K_RIGHTARROW)\n\t\tSIp.contols |= CONT_RIGHTKEY;\n\telse if (k == K_UPARROW)\n\t\tSIp.contols |= CONT_UPKEY;\t\n\telse if (k == K_DOWNARROW)\n\t\tSIp.contols |= CONT_DOWNKEY;\n\telse if (k == K_MOUSE1 || k == K_SPACE)\n\t\tSIp.contols |= CONT_FIREKEY;\n\telse if (k == K_MOUSE2)\n\t\tSIp.contols |= CONT_MOUSE;\n\telse if (k == '1')\n\t\tSIp.health = 9;\n\telse if (k == '2')\n\t\tSIp.cash += 100;\n\telse if (k == K_ESCAPE)\n\t\tMenu_Control(0);\n\telse\n\t\tCon_Printf(\"key %i not recognised\\n\", k);\n\n\tCon_Printf(\"%i (%i)\\n\", SIp.contols, k);\n}\n\nvoid SI_MouseMove(int x, int y)\n{\n\tstatic int ox;\n\tstatic int oy;\n\tif (SIShop && (ox != x || oy != y))\n\t{\n\t\tSIp.x = (float)x - SIp.width/2;\n\t\tSIp.y = (float)y - SIp.height/2;\n\n\t\tox = x;\n\t\toy = y;\n\t}\n}\n\nqboolean FS_GetS(char *buffer, int buffersize, qhandle_t handle)\n{\n\tint i;\n\tbuffersize--;\n\tfor (i = 0; i < buffersize; i+=1)\n\t{\n\t\tif (FS_Read(handle, buffer+i, 1) <= 0) break;\n\t\tif (buffer[i] == '\\n') break;\n\t}\n\n\tbuffer[i] = '\\0';\n\n\tif (!i)\n\t\treturn false;\n\treturn true;\n}\n\nvoid SI_Initialize(void)\n{\n\tint a;\n\n/*\tSoundWin = Sound_LoadSound(\"spaceinv/sound/win.wav\");\n\tSoundLoose = Sound_LoadSound(\"spaceinv/sound/loose.wav\");\n\tSoundFire = Sound_LoadSound(\"spaceinv/sound/fire.wav\");\n\tSoundExplosion = Sound_LoadSound(\"spaceinv/sound/explode.wav\");\n\tSoundHit = Sound_LoadSound(\"spaceinv/sound/explode.wav\");\n*/\n\tif (SIEnemyType)\t//if one of these is defined, they must all be\n\t{\n\t\tfree(SIEnemyType);\n\t\tfree(SImonster);\n\t\tfree(SIbullet);\t\t\n\n\t\tfree(SIbaddietexture);\n\t\tfree(SIbullettexture);\n\t\tSIbullettexture = NULL;\n\n\t\tfree(SISquad);\n\t\tfree(SIRoute);\n\t}\n\tif (SIweapons)\n\t\tfree(SIweapons);\n\n\t{\t\n\t\tbool section = false;\n\t//\tFILE *F;\n\t\tchar line[1024];\n\t\tchar *s;\n\t\tchar *val;\n\t\tint len;\n\n\t\tqhandle_t f;\n\n\t\tlen = FS_Open(\"spaceinv/weapons.txt\", &f, 1);\n\t\tif (len>=0)\n\t\t{\n\t\t\ta = -1;\t\t\n\t\t\twhile (1)\n\t\t\t{\n\t\t\t\tif (!FS_GetS(line, 1024, f))\n\t\t\t\t\tbreak;\n\t//\t\t\tif (!fgets(line, 1024, F))\n\t//\t\t\t\tbreak;\t//eof\n\n\t\t\t\ts = line;\n\t\t\t\twhile(*s == ' ' || *s == '\\t')\t//ignore indents\n\t\t\t\t\ts++;\n\t\t\t\t\n\t\t\t\tif (*s == '\\n' || *s == '\\r' || *s == '#')\t//ignore comments/blank lines\n\t\t\t\t\tcontinue;\n\n\t\t\t\tval = s + strlen(s)-1;\n\t\t\t\twhile (*val == '\\n' || *val == '\\r' || *val == ' ' || *val == '\\t')\n\t\t\t\t{\n\t\t\t\t\t*val = 0;\n\t\t\t\t\tval--;\n\t\t\t\t}\n\n\n\t\t\t\tval = strchr(s, '=');\n\t\t\t\tif (val)\n\t\t\t\t{\n\t\t\t\t\t*val = 0;\n\t\t\t\t\tval++;\n\t\t\t\t}\n\n\t\t\t\tif (section == 2 && *s != '[')\n\t\t\t\t{\n\t\t\t\t\tif (!strcasecmp(s, \"monsterpics\"))\n\t\t\t\t\t\tmonsterpics = atoi(val);\n\t\t\t\t\telse if (!strcasecmp(s, \"maxmonsters\"))\n\t\t\t\t\t\tMAXMONSTERS = atoi(val);\n\t\t\t\t\telse if (!strcasecmp(s, \"maxbullets\"))\n\t\t\t\t\t\tMAXBULLETS = atoi(val);\n\t\t\t\t\telse if (!strcasecmp(s, \"maxroutepoints\"))\n\t\t\t\t\t\tMAXROUTEPOINTS = atoi(val);\n\t\t\t\t\telse if (!strcasecmp(s, \"maxroutes\"))\n\t\t\t\t\t\tMAXROUTES = atoi(val);\n\t\t\t\t\telse if (!strcasecmp(s, \"maxsquads\"))\n\t\t\t\t\t\tMAXSQUADS = atoi(val);\n\t\t\t\t\telse\n\t\t\t\t\t\tCon_Printf(\"Bad command \\\"%s\\\" in \\\"%s\\\"\\n\", s, \"spaceinv\\\\weapons.txt\");\n\t\t\t\t}\n\t\t\t\telse if (section == 1 && *s != '[')\n\t\t\t\t{\n\t\t\t\t\tif (!strcasecmp(s, \"width\"))\n\t\t\t\t\t\tSIweapons[a].width = (float)atof(val);\n\t\t\t\t\telse if (!strcasecmp(s, \"height\"))\n\t\t\t\t\t\tSIweapons[a].height = (float)atof(val);\n\t\t\t\t\telse if (!strcasecmp(s, \"yvel\"))\n\t\t\t\t\t\tSIweapons[a].yvel = (float)atof(val);\n\t\t\t\t\telse if (!strcasecmp(s, \"yaccel\"))\n\t\t\t\t\t\tSIweapons[a].yaccel = (float)atof(val);\n\t\t\t\t\telse if (!strcasecmp(s, \"damage\"))\n\t\t\t\t\t\tSIweapons[a].damage = atoi(val);\n\t\t\t\t\telse if (!strcasecmp(s, \"alpha\"))\n\t\t\t\t\t\tSIweapons[a].alpha = (float)atof(val);\n\t\t\t\t\telse if (!strcasecmp(s, \"alphachange\"))\n\t\t\t\t\t\tSIweapons[a].alphachange = (float)atof(val);\n\t\t\t\t\telse if (!strcasecmp(s, \"texturenum\"))\n\t\t\t\t\t{\n\t\t\t\t\t\tif (!SIbullettexture)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tSIbullettexture = malloc(sizeof(texture) * weaponlevels);\n\t\t\t\t\t\t\tmemset(SIbullettexture, 0, sizeof(texture) * weaponlevels);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tSIweapons[a].tex = &SIbullettexture[atoi(val)-1];\t//allow different pictures\n\t\t\t\t\t}\n\t\t\t\t\telse if (!strcasecmp(s, \"charge\"))\n\t\t\t\t\t\tSIweapons[a].charge = atoi(val);\t//allow different pictures\n\t\t\t\t\telse if (!strcasecmp(s, \"type\"))\n\t\t\t\t\t\tSIweapons[a].type = atoi(val);\n\t\t\t\t\telse\n\t\t\t\t\t\tCon_Printf(\"Bad command \\\"%s\\\" in \\\"%s\\\"\\n\", s, \"spaceinv\\\\weapons.txt\");\n\t\t\t\t}\n\t\t\t\telse if (!strcasecmp(s, \"weapons\"))\t\t\t\n\t\t\t\t{\n\t\t\t\t\tweaponlevels = atoi(val);\n\t//\t\t\t\tif (weaponlevels > SHOTLEVELS)\n\t//\t\t\t\t{\n\t//\t\t\t\t\tLog(\"Too many weapons set\\n\");\n\t//\t\t\t\t\tweaponlevels = SHOTLEVELS;\n\t//\t\t\t\t}\n\n\t\t\t\t\tSIweapons = malloc(sizeof(SIbullet_t) * weaponlevels);\n\t\t\t\t\tmemset(SIweapons, 0, sizeof(SIbullet_t) * weaponlevels);\n\t\t\t\t}\n\t\t\t\telse if (!strcasecmp(s, \"[weapon\"))\n\t\t\t\t{\n\t\t\t\t\tsection = 1;\n\t\t\t\t\tif (weaponlevels == 0)\n\t\t\t\t\t\tSys_Error(\"SILoadWeapons: number of weapons not set yet.\");\n\t\t\t\t\ta = atoi(val);\n\t\t\t\t\tif (a > SHOTLEVELS)\n\t\t\t\t\t{\n\t\t\t\t\t\ta=0;\n\t\t\t\t\t\tCon_Printf(\"Bad type\\n\");\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\ta-=1;\n\t\t\t\t}\n\t\t\t\telse if (!strcasecmp(s, \"[info]\"))\n\t\t\t\t{\n\t\t\t\t\tsection = 2;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tCon_Printf(\"Bad command \\\"%s\\\" in \\\"%s\\\"\\n\", s, \"spaceinv\\\\weapons.txt\");\n\n\t\t\t}\n\n\t\t\tFS_Close(f);\n\t\t}\n\t\telse\n\t\t{\n\t\t\t//GameCompleate = true;\n\t\t\t//NextLevel = 0;\n\t\t\tSys_Error(\"Couldn't open file\\n\");\n\t\t}\n\t}\n\n\tif (!SIbullettexture)\n\t{\n\t\tSIbullettexture = malloc(sizeof(texture) * weaponlevels);\n\t\tmemset(SIbullettexture, 0, sizeof(texture) * weaponlevels);\n\t}\n\n\tSIEnemyType = malloc(sizeof(SIEnemyType_t) * ENEMYTYPES);\n\tmemset(SIEnemyType, 0, sizeof(SIEnemyType_t) * ENEMYTYPES);\n\tSImonster = malloc(sizeof(SImonster_t) * MAXMONSTERS);\n\tmemset(SImonster, 0, sizeof(SImonster_t) * MAXMONSTERS);\n\tSIbullet = malloc(sizeof(SIbullet_t) * MAXBULLETS);\n\tmemset(SIbullet, 0, sizeof(SIbullet_t) * MAXBULLETS);\n\n\tSIbaddietexture = malloc(sizeof(texture) * ENEMYTYPES);\n\tmemset(SIbaddietexture, 0, sizeof(texture) * ENEMYTYPES);\n\n\tSIRoute = malloc((sizeof(SIRoute_t)+(MAXROUTEPOINTS-1)*sizeof(vec3_t)) * MAXROUTES);\n\tmemset(SIRoute, 0, (sizeof(SIRoute_t)+(MAXROUTEPOINTS-1)*sizeof(vec3_t)) * MAXROUTES);\n\tSISquad = malloc(sizeof(SISquad_t) * MAXSQUADS);\n\tmemset(SISquad, 0, sizeof(SISquad_t) * MAXSQUADS);\n\n\t//loaded elsewhere\n//\tSIweapons = mmalloc(sizeof(SIbullet_t) * weaponlevels);\n\n\tSI_LoadTextures ();\n \n\tSIRestartGame ();\n}\n\nvoid NextLevel (void)\n{\n\tint a;\n//\tint numenemysoftype[ENEMYTYPES] = {5, 5, 50};\n\tint etype = 0;\n\n\tSIEnemyType_t *et;\n\n \tif (SIp.powerups & PUP_SHRUNKEN)\n\t{\n\t\tSIp.height = 16;\n\t\tSIp.width = 16;\n\n\t\tSIp.powerups &= ~PUP_SHRUNKEN;\n\t}\n\telse\n\t{\n\t\tSIp.height = 32;\n\t\tSIp.width = 32;\n\t}\n\tSIp.y = vid.height - SIp.height;\n\tSIp.x = (vid.width - SIp.width) / 2;\t\n\tSIShop = false;\n\tdeadtime = 0;\n\n\tnextthink = realtime;\n\n\n\tleveltime = 0;\n\n\tfor (a = 0; a < MAXSQUADS; a++)\n\t\tSISquad[a].number = 0;\n\n#if 1\n{\n\tint i;\n\tint usedtypes = false;\t\n\tint sectiontype=0;\n\tqhandle_t F;\n\tchar line[1024];\n\tchar *s;\n\tchar *val;\n\n\tint len;\n\tint rofs = 0;\n\n\tlen = FS_Open(va(\"spaceinv/level%i.txt\", SIlevel+1), &F, 1);\n\tif (len < 0)\n\t{\n\t\tSIlevel = 0;\n\t\tlen = FS_Open(va(\"spaceinv/level%i.txt\", SIlevel+1), &F, 1);\t//loop back to the start\n\t}\n//\tF = fopen(Sva(\"%sspaceinv\\\\level%i.txt\", funcs->exe->gamepath, SIlevel+1), \"rb\");\n\tif (len>=0)\n\t{\n\t\ta = -1;\n\t\tmemset(SIEnemyType, 0, sizeof(SIEnemyType));\n//\t\tmemset(numenemysoftype, 0, sizeof(numenemysoftype));\n\t\twhile (1)\n\t\t{\n\t\t\tif (!FS_GetS(line, 1024, F))\n\t\t\t\tbreak;\t//eof\n\n\t\t\ts = line;\n\t\t\twhile(*s == ' ' || *s == '\\t')\t//ignore indents\n\t\t\t\ts++;\n\t\t\t\n\t\t\tif (*s == '\\n' || *s == '\\r' || *s == '#')\t//ignore comments/blank lines\n\t\t\t\tcontinue;\n\n\t\t\tval = s + strlen(s)-1;\n\t\t\twhile (*val == '\\n' || *val == '\\r' || *val == ' ' || *val == '\\t')\n\t\t\t{\n\t\t\t\t*val = 0;\n\t\t\t\tval--;\n\t\t\t}\n\n\n\t\t\tval = strchr(s, '=');\n\t\t\tif (val)\n\t\t\t{\n\t\t\t\t*val = 0;\n\t\t\t\tval++;\n\t\t\t}\n\n\t\t\tif (*s == '[')\n\t\t\t{\n\t\t\t\tif (!strcasecmp(s+1, \"info]\"))\n\t\t\t\t{\n\t\t\t\t\tsectiontype = 1;\n\t\t\t\t}\n\t\t\t\telse if (!strcasecmp(s+1, \"type\"))\n\t\t\t\t{\t\t\t\t\t\n\t\t\t\t\tif (usedtypes == 0)\n\t\t\t\t\t\tCon_Printf(\"SINextLevel: Types not set yet\");\n\t\t\t\t\ta = atoi(val);\n\t\t\t\t\tif (a > ENEMYTYPES || a < 1)\n\t\t\t\t\t{\n\t\t\t\t\t\ta=0;\n\t\t\t\t\t\tCon_Printf(\"Bad type\\n\");\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\ta-=1;\n\n\t\t\t\t\tmemset(&SIEnemyType[a], 0, sizeof(SIEnemyType_t));\n\n\t\t\t\t\tsectiontype = 2;\t\t\t\t\n\t\t\t\t}\n\t\t\t\telse if (!strcasecmp(s+1, \"route\"))\n\t\t\t\t{\t\t\t\t\t\n\t\t\t\t\ta = atoi(val);\n\t\t\t\t\tif (a > ENEMYTYPES || a < 1)\n\t\t\t\t\t{\n\t\t\t\t\t\ta=0;\n\t\t\t\t\t\tCon_Printf(\"Bad route\\n\");\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\ta-=1;\n\n\t\t\t\t\tmemset(&SIRoute[a], 0, sizeof(SIRoute_t)+(MAXROUTEPOINTS-1)*sizeof(vec3_t));\n\n\t\t\t\t\tsectiontype = 3;\t\t\t\t\n\t\t\t\t}\n\t\t\t\telse if (!strcasecmp(s+1, \"squad\"))\n\t\t\t\t{\t\t\t\t\t\n\t\t\t\t\ta = atoi(val);\n\t\t\t\t\tif (a > MAXSQUADS || a < 1)\n\t\t\t\t\t{\n\t\t\t\t\t\ta=0;\n\t\t\t\t\t\tCon_Printf(\"Bad squad\\n\");\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\ta-=1;\n\n\t\t\t\t\tmemset(&SISquad[a], 0, sizeof(SISquad_t));\n\n\t\t\t\t\tsectiontype = 4;\t\t\t\t\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tCon_Printf(\"Bad block \\\"%s\\\" in \\\"%s\\\"\\n\", s, va(\"level%i.txt\", SIlevel));\n\t\t\t}\n\t\t\telse if (sectiontype == 0)\n\t\t\t{\n\t\t\t\tif (!strcasecmp(s, \"types\"))\t\t\t\n\t\t\t\t\tusedtypes = atoi(val);\n\t\t\t\telse\n\t\t\t\t\tCon_Printf(\"Bad command \\\"%s\\\" in \\\"%s\\\" (out of block)\\n\", s, va(\"level%i.txt\", SIlevel));\n\t\t\t}\n\t\t\telse if (sectiontype == 1)\n\t\t\t{\n\t\t\t\tif (!strcasecmp(s, \"lastlevel\"))\n\t\t\t\t\tlastlevel = atoi(val);\n\t\t\t\telse if (!strcasecmp(s, \"levelname\"))\n\t\t\t\t{\n\t\t\t\t\tstrcpy(SILevelName, val);\n\t\t\t\t\tlevelnametime = 500;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tCon_Printf(\"Bad command \\\"%s\\\" in \\\"%s\\\"\\n\", s, va(\"level%i.txt\", SIlevel));\n\t\t\t}\t\t\t\n\t\t\telse if (sectiontype == 2)\n\t\t\t{\n\t\t\t\tif (!strcasecmp(s, \"width\"))\n\t\t\t\t\tSIEnemyType[a].width = (float)atof(val);\n\t\t\t\telse if (!strcasecmp(s, \"height\"))\n\t\t\t\t\tSIEnemyType[a].height = (float)atof(val);\n\t\t\t\telse if (!strcasecmp(s, \"power\"))\n\t\t\t\t{\t\t\t\t\t\n\t\t\t\t\tSIEnemyType[a].shotdamage = atoi(val) - 1;\n\t\t\t\t\tif (SIEnemyType[a].shotdamage >= weaponlevels)\n\t\t\t\t\t{\n\t\t\t\t\t\tCon_Printf(\"Enemy %i has weapon number %i\\n\", a+1, SIEnemyType[a].shotdamage+1);\n\t\t\t\t\t\tSIEnemyType[a].shotdamage = weaponlevels-1;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse if (!strcasecmp(s, \"health\"))\n\t\t\t\t\tSIEnemyType[a].health = atoi(val);\n\t\t\t\telse if (!strcasecmp(s, \"reward\"))\n\t\t\t\t\tSIEnemyType[a].reward = atoi(val);\n\t\t\t\telse if (!strcasecmp(s, \"number\"))\n\t\t\t\t\tSIEnemyType[a].number = atoi(val);\n\t\t\t\telse if (!strcasecmp(s, \"ai\"))\n\t\t\t\t\tSIEnemyType[a].AI = atoi(val);\n\t\t\t\telse if (!strcasecmp(s, \"healthregen\"))\n\t\t\t\t\tSIEnemyType[a].healthregen = atoi(val);\n\t\t\t\telse if (!strcasecmp(s, \"picture\"))\n\t\t\t\t{\n\t\t\t\t\ti = atoi(val) - 1;\n\t\t\t\t\tif (i < 0 || i >= monsterpics)\n\t\t\t\t\t{\n\t\t\t\t\t\tSys_Errorf(\"Monster picture has the wrong value (%i)\", i+1);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\tSIEnemyType[a].picture = &SIbaddietexture[i];\t//allow different pictures\n\t\t\t\t}\n\t\t\t\telse if (!strcasecmp(s, \"fireprob\"))\n\t\t\t\t\tSIEnemyType[a].FireProb = (float)atof(val);\t//allow different pictures\t\t\t\t\n\t\t\t\telse\n\t\t\t\t\tCon_Printf(\"Bad command \\\"%s\\\" in \\\"%s\\\"\\n\", s, va(\"level%i.txt\", SIlevel));\n\t\t\t}\n\t\t\telse if (sectiontype == 3)\n\t\t\t{\n\t\t\t\tif (!strcasecmp(s, \"coords\"))\n\t\t\t\t\tSIRoute[a].numpoints = atoi(val);\n\t\t\t\telse if (*s == 'x' || *s == 'X')\n\t\t\t\t\t(SIRoute[a].pos[atoi(s+1)-1])[0] = (float)atof(val);\n\t\t\t\telse if (*s == 'y' || *s == 'Y')\n\t\t\t\t\t(SIRoute[a].pos[atoi(s+1)-1])[1] = (float)atof(val);\n\t\t\t\telse if (*s == 'z' || *s == 'Z')\n\t\t\t\t\t(SIRoute[a].pos[atoi(s+1)-1])[2] = (float)atof(val);\n\t\t\t\telse\n\t\t\t\t\tCon_Printf(\"Bad route command \\\"%s\\\" in \\\"%s\\\"\\n\", s, va(\"level%i.txt\", SIlevel));\n\t\t\t}\n\t\t\telse if (sectiontype == 4)\n\t\t\t{\n\t\t\t\tif (!strcasecmp(s, \"route\"))\n\t\t\t\t\tSISquad[a].route = atoi(val)-1;\n\t\t\t\telse if (!strcasecmp(s, \"type\"))\n\t\t\t\t\tSISquad[a].type = atoi(val)-1;\n\t\t\t\telse if (!strcasecmp(s, \"number\"))\n\t\t\t\t\tSISquad[a].number = atoi(val);\n\t\t\t\telse if (!strcasecmp(s, \"interval\"))\n\t\t\t\t\tSISquad[a].interval = (float)atof(val);\n\t\t\t\telse if (!strcasecmp(s, \"speed\"))\n\t\t\t\t\tSISquad[a].speed = (float)atof(val);\n\t\t\t\telse if (!strcasecmp(s, \"time\"))\n\t\t\t\t\tSISquad[a].time = (float)atof(val);\n\t\t\t\telse\n\t\t\t\t\tCon_Printf(\"Bad squad command \\\"%s\\\" in \\\"%s\\\"\\n\", s, va(\"level%i.txt\", SIlevel));\n\t\t\t}\n\t\t\telse\n\t\t\t\tCon_Printf(\"Bad command \\\"%s\\\" in \\\"%s\\\"\\n\", s, va(\"level%i.txt\", SIlevel));\n\n\t\t}\n\n\t\tFS_Close(F);\n\t}\n\telse\n\t{\n\t\t//GameCompleate = true;\n\t\t//NextLevel = 0;\n\t\tSys_Errorf(\"Couldn't open file\\n\", va(\"spaceinv/level%i.txt\", SIlevel+1));\n\t}\n}\n#else\n\n\tfor (a = 0; a < ENEMYTYPES; a++)\n\t{\n\t\tSIEnemyType[a].health = a+1;\n\t\tSIEnemyType[a].height = (float)(16*(a+1));\n\t\tSIEnemyType[a].width = (float)(16*(a+1));\n\t\tSIEnemyType[a].picture = &SIbaddietexture;\n\t\tSIEnemyType[a].shotpicture = &SIbullettexture[0];\n\t\tSIEnemyType[a].shotdamage = a-1;\n\t\tSIEnemyType[a].reward = (a+1) * 25;\n\t}\n#endif\n\n\tmemset(SImonster, 0, sizeof(*SImonster)*MAXMONSTERS);\n\n\tlivingmonsters = 0;\n\twhile (SIEnemyType[etype].number <= 0)\n\t{\n\t\tetype++;\n\t\tif (etype >= ENEMYTYPES)\n\t\t\tbreak;\n\t}\n\tfor (a = 0; a < MAXMONSTERS; a++)\n\t{\n\t\tif (etype < ENEMYTYPES)\n\t\t{\n\t\t\tet = &SIEnemyType[etype];\n\t\t\tSImonster[a].EnemyType = et;\n\t\t\tSImonster[a].health = et->health;\n\t\t\tSImonster[a].xpos = random() * vid.width;\n\t\t\tSImonster[a].ypos = (random() * vid.height) / 2;\n\t\t\tSImonster[a].width = et->width;\n\t\t\tSImonster[a].height = et->height;\n\t\t\tSImonster[a].yvel = ENEMYSHIPSPEEDY;\n\t\t\tSImonster[a].xvel = (float)ENEMYSHIPSPEEDX;\n\t\t\tlivingmonsters++;\n\n\t\t\tSIEnemyType[etype].number-=1;\n\t\t\twhile (SIEnemyType[etype].number <= 0)\n\t\t\t{\n\t\t\t\tetype++;\n\t\t\t\tif (etype >= ENEMYTYPES)\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (SIEnemyType[etype].number <= 0)\n\t\t\t\tbreak;\n\t\t}\n\t\telse\n\t\t\tSImonster[a].health = 0;\n\t}\n\tfor (a = 0; a < MAXSQUADS; a++)\n\t\tlivingmonsters += SISquad[a].number;\n\tdeadtime = 0;\n\n\tmemset(SIbullet, 0, sizeof(SIbullet) * MAXBULLETS);\n\n\tfor (a = 0; a < MAXADDONS; a++)\n\t\tSIp.cannon[a].refire = 0;\n}\n\nvoid SIRestartGame (void)\n{\n\tint a;\n\tSIp.health = 9;\n\tSIp.cash = 500;\n\tSIp.powerups = 0;\t\t\n\n\tSIp.height = 32;\n\tSIp.width = 32;\n\n\tSIp.y = (vid.height - SIp.height)/2;\n\tSIp.x = (vid.width - SIp.width) / 2;\t\n\n\tfor (a = 0; a < MAXADDONS; a++)\n\t{\n\t\tSIp.cannon[a].xoff = 0;\n\t\tSIp.cannon[a].yoff = 0;\n\n\t\tSIp.cannon[a].power = 0;\n\t\tSIp.cannon[a].reloadtime = 0.8f;\n\n\t\tSIp.cannon[a].tex = NULL;\n\t}\n\n#if 1\n\tfor (a = FIRSTCANNON; a <= LASTCANNON; a++)\n\t{\n\t\tSIp.cannon[a].xoff = (int)((float)a-FIRSTCANNON+1.5f) / 2.0f;\n\t\tif (a%2)\n\t\t\tSIp.cannon[a].xoff *= -1;\n\t\telse\n\t\t\tSIp.cannon[a].xoff+=1;\n\t\tSIp.cannon[a].yoff = 0;\t\n\n\t\tSIp.cannon[a].tex = &SIcannontexture;\n\t}\n\tfor (a = FIRSTROCKET; a <= LASTROCKET; a++)\n\t{\n\t\tSIp.cannon[a].xoff = (int)(a-FIRSTROCKET+1.5f) / 2.0f;\n\t\tSIp.cannon[a].xoff -= 0.5f;\n\t\tif (a%2)\t\n\t\t\tSIp.cannon[a].xoff *= -1.0f;\n\t\telse\n\t\t\tSIp.cannon[a].xoff += 1.0f;\n\t\tSIp.cannon[a].yoff = 0;\n\n\t\tSIp.cannon[a].tex = &SIcannontexture;\n\t}\n#else\n\tSIp.cannon[0].xoff = -1;\n\tSIp.cannon[1].xoff = 1;\n\tSIp.cannon[2].xoff = -2;\n\tSIp.cannon[3].xoff = 2;\n#endif\n\n\tSIp.cannon[M_FORWARD].power = 1;\n\tSIp.cannon[M_FORWARD].tex = &SIplayertexture;\n\n\tSIlevel = 0;\n\tlastlevel = false;\n\tSIShop = true;\t\t\n\tdeadtime = 0;\n\tnextthink = 0;\n}\n\n\n\n\n\n\n\n\n\n\nint Plug_MenuEvent(int *args)\n{\n\n//\targs[2]=(int)(args[2]*640.0f/vid.width);\n//\targs[3]=(int)(args[3]*480.0f/vid.height);\n\n\tswitch(args[0])\n\t{\n\tcase 0:\t//draw\n\t\tSI_Main(args[2], args[3]);\n\t\tSI_2D();\n\t\tbreak;\n\tcase 1:\t//keydown\n\t\tSI_KeyDown(args[1]);\n\t\tbreak;\n\tcase 2:\t//keyup\n\t\tSI_KeyUp(args[1]);\n\t\tbreak;\n\tcase 3:\t//menu closed (this is called even if we change it).\n\t\tbreak;\n\tcase 4:\t//mousemove\n\t\tbreak;\n\t}\n\n\treturn 0;\n}\n\nint Plug_Tick(int *args)\n{\n\trealtime = args[0]/1000.0f;\n\treturn true;\n}\n\nint Plug_ExecuteCommand(int *args)\n{\n\tchar cmd[256];\n\tCmd_Argv(0, cmd, sizeof(cmd));\n\tif (!strcmp(\"spaceinv\", cmd))\n\t{\n\t\tCvar_SetFloat(\"gl_blend2d\", 1);\n\t\tSI_LoadTextures();\n\t\tMenu_Control(1);\n\t\treturn 1;\n\t}\n\treturn 0;\n}\n\nint Plug_Init(int *args)\n{\n\tif (Plug_Export(\"Tick\", Plug_Tick) &&\n\t\tPlug_Export(\"ExecuteCommand\", Plug_ExecuteCommand) &&\n\t\tPlug_Export(\"MenuEvent\", Plug_MenuEvent))\n\t{\n\n\t\tK_UPARROW\t\t= Key_GetKeyCode(\"uparrow\");\n\t\tK_DOWNARROW\t\t= Key_GetKeyCode(\"downarrow\");\n\t\tK_LEFTARROW\t\t= Key_GetKeyCode(\"leftarrow\");\n\t\tK_RIGHTARROW\t= Key_GetKeyCode(\"rightarrow\");\n\t\tK_ESCAPE\t\t= Key_GetKeyCode(\"escape\");\n\t\tK_HOME\t\t\t= Key_GetKeyCode(\"home\");\n\t\tK_MOUSE1\t\t= Key_GetKeyCode(\"mouse1\");\n\t\tK_MOUSE2\t\t= Key_GetKeyCode(\"mouse2\");\n\t\tK_SHIFT\t\t\t= Key_GetKeyCode(\"shift\");\n\t\tK_SPACE\t\t\t= Key_GetKeyCode(\"space\");\n\t\tK_F1\t\t\t= Key_GetKeyCode(\"f1\");\n\t\tK_F2\t\t\t= Key_GetKeyCode(\"f2\");\n\n\t\tCmd_AddCommand(\"spaceinv\");\n\n\t\tSI_Initialize();\n\t\tSI_LoadTextures();\n\n\t\treturn 1;\n\t}\n\treturn 0;\n}\n"
  },
  {
    "path": "plugins/spaceinv/spaceinv.q3asm",
    "content": "-o \"spaceinv\"\nplugin\nspaceinv\nqvm_api\nmemory"
  },
  {
    "path": "plugins/terrorgen/terragen.c",
    "content": "/*\r\nmod_terrain_create terrorgen; edit maps/terrorgen.hmp; map terrorgen\r\nyou can use mod_terrain_convert to generate+save the entire map for redistribution to people without this particular plugin version, ensuring longevity.\r\n(this paticular command was meant to load+save the entire map, once mod_terrain_savever 2 is default...)\r\n\r\nFIXME: no way to speciffy which gen plugin to use for a particular map\r\n*/\r\n\r\n#include \"../plugin.h\"\r\n#include \"glquake.h\"\r\n#include \"com_mesh.h\"\r\n#include \"gl_terrain.h\"\r\n\r\n#ifdef TERRAIN\r\n#define GENHIGHTSCALE 1024.0\r\n\r\nstatic plugterrainfuncs_t *terr;\r\nstatic plugmodfuncs_t *modfuncs;\r\n\r\nstruct rndctx_s\r\n{\r\n\tunsigned int x, y, z, w;\r\n};\r\nunsigned int myrand(struct rndctx_s *ctx)\r\n{\t//ripped from wikipedia (originally called xorshift128)\r\n\tunsigned int t = ctx->x ^ (ctx->x << 11);\r\n\tctx->x = ctx->y; ctx->y = ctx->z; ctx->z = ctx->w;\r\n\treturn ctx->w = ctx->w ^ (ctx->w >> 19) ^ t ^ (t >> 8);\r\n}\r\nfloat TerrorGen_PredCRandom(heightmap_t *hm, int x, int y)\r\n{\r\n\tint seed = atoi(hm->seed);\r\n\tstruct rndctx_s rctx = {x*3421 ^ y*35231, y*23423, seed, seed+seed*4321+x*432+y*423+x*y}; //overflows are fine\r\n\tunsigned int r = myrand(&rctx);\r\n\treturn ((r & 0xffffff) / (float)0x800000)-1;\r\n}\r\n\r\nfloat TerrorGen_CRandom(struct rndctx_s *rctx)\r\n{\r\n\tunsigned int r = myrand(rctx);\r\n\treturn ((r & 0xffffff) / (float)0x800000)-1;\r\n}\r\n\r\n\r\n//the four corners are defined in advance.\r\n//size is a power-of-two, h must be sized pow(size+1,2)\r\nstatic void GenHeights(heightmap_t *hm, struct rndctx_s *rctx, float *h, int size, int sx, int sy, float scale)\r\n{\r\n\tint stride = size+1;\r\n\tint x, y;\r\n\t//borders are left, right, top, bottom\r\n\twhile(size > 1)\r\n\t{\r\n\t\tint m = size/2;\r\n\r\n\t\t//find central points (diamond pattern)\r\n\t\tfor (x = 0; x < stride-1; x += size)\r\n\t\t{\r\n\t\t\tfor (y = 0; y < stride-1; y += size)\r\n\t\t\t{\r\n\t\t\t\tfloat mid = (h[(x)+(y)*stride] + h[(x+size)+(y)*stride] + h[(x)+(y+size)*stride] + h[(x+size)+(y+size)*stride])/4;\r\n\t\t\t\tmid += TerrorGen_CRandom(rctx)*scale;\r\n\t\t\t\th[(x+m) + (y+m)*stride] = mid;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t//find square points (square pattern)\r\n\t\tfor (x = 0; x < stride-1; x += size)\r\n\t\t{\r\n\t\t\tfor (y = 0; y < stride-1; y += size)\r\n\t\t\t{\r\n\t\t\t\tfloat mid;\r\n\t\t\t\t//left side\r\n\t\t\t\tmid = h[(x)+(y)*stride];\r\n\t\t\t\tmid += h[(x)+(y+size)*stride];\r\n\t\t\t\tif (x != 0) //not an outer edges\r\n\t\t\t\t{\r\n\t\t\t\t\tmid += h[(x-m)+(y+m)*stride];\r\n\t\t\t\t\tmid += h[(x+m)+(y+m)*stride];\r\n\t\t\t\t\tmid = mid/4 + TerrorGen_CRandom(rctx)*scale;\r\n\t\t\t\t}\r\n\t\t\t\telse\t//outer edge doesn't look inwards, because any neighbouring block would not know it\r\n\t\t\t\t\tmid = mid/2 + TerrorGen_PredCRandom(hm, sx+x, sy+y+m)*scale;\r\n\t\t\t\th[(x) + (y+m)*stride] = mid;\r\n\r\n\t\t\t\t//right\r\n\t\t\t\tmid = h[(x+size)+(y)*stride];\r\n\t\t\t\tmid += h[(x+size)+(y+size)*stride];\r\n\t\t\t\tif (x+size != stride-1) //not an outer edges\r\n\t\t\t\t{\r\n\t\t\t\t\tmid += h[(x+size-m)+(y+m)*stride];\r\n\t\t\t\t\tmid += h[(x+size+m)+(y+m)*stride];\r\n\t\t\t\t\tmid = mid/4 + TerrorGen_CRandom(rctx)*scale;\r\n\t\t\t\t}\r\n\t\t\t\telse\t//outer edge doesn't look inwards, because any neighbouring block would not know it\r\n\t\t\t\t\tmid = mid/2 + TerrorGen_PredCRandom(hm, sx+x+size, sy+y+m)*scale;\r\n\t\t\t\th[(x+size) + (y+m)*stride] = mid;\r\n\r\n\t\t\t\t//top\r\n\t\t\t\tmid = h[(x)+(y)*stride];\r\n\t\t\t\tmid += h[(x+size)+(y)*stride];\r\n\t\t\t\tif (y != 0) //not an outer edges\r\n\t\t\t\t{\r\n\t\t\t\t\tmid += h[(x+m)+(y-m)*stride];\r\n\t\t\t\t\tmid += h[(x+m)+(y+m)*stride];\r\n\t\t\t\t\tmid = mid/4 + TerrorGen_CRandom(rctx)*scale;\r\n\t\t\t\t}\r\n\t\t\t\telse\t//outer edge doesn't look inwards, because any neighbouring block would not know it\r\n\t\t\t\t\tmid = mid/2 + TerrorGen_PredCRandom(hm, sx+x+m, sy+y)*scale;\r\n\t\t\t\th[(x+m) + (y)*stride] = mid;\r\n\r\n\t\t\t\t//bottom\r\n\t\t\t\tmid = h[(x)+(y+size)*stride];\r\n\t\t\t\tmid += h[(x+size)+(y+size)*stride];\r\n\t\t\t\tif (y+size != stride-1) //not an outer edges\r\n\t\t\t\t{\r\n\t\t\t\t\tmid += h[(x+m)+(y+size-m)*stride];\r\n\t\t\t\t\tmid += h[(x+m)+(y+size+m)*stride];\r\n\t\t\t\t\tmid = mid/4 + TerrorGen_CRandom(rctx)*scale;\r\n\t\t\t\t}\r\n\t\t\t\telse\t//outer edge doesn't look inwards, because any neighbouring block would not know it\r\n\t\t\t\t\tmid = mid/2 + TerrorGen_PredCRandom(hm, sx+x+m, sy+y+size)*scale;\r\n\t\t\t\th[(x+m) + (y+size)*stride] = mid;\r\n\t\t\t}\r\n\t\t}\r\n\t\tsize = m;\r\n\t\tscale /= 2;\r\n\t}\r\n}\r\n\r\n\r\n\r\nstatic void TerrorGen_GenerateOne(heightmap_t *hm, struct rndctx_s *rctx, int sx, int sy, hmsection_t *s, float tl,float tr,float bl,float br)\r\n{\r\n\tint x,y,i;\r\n\tqbyte *lm;\r\n\r\n\ts->heights[0] = tl;\r\n\ts->heights[SECTHEIGHTSIZE-1] = tr;\r\n\ts->heights[0+(SECTHEIGHTSIZE-1)*SECTHEIGHTSIZE] = bl;\r\n\ts->heights[SECTHEIGHTSIZE-1+(SECTHEIGHTSIZE-1)*SECTHEIGHTSIZE] = br;\r\n\tGenHeights(hm, rctx, s->heights, SECTHEIGHTSIZE-1, sx*(SECTHEIGHTSIZE-1), sy*(SECTHEIGHTSIZE-1), GENHIGHTSCALE/16);\r\n\r\n\ts->flags |= TSF_RELIGHT;\r\n\r\n\t//pick the textures to blend between. I'm just hardcoding shit here. this is meant to be some sort example.\r\n\tQ_strlcpy(s->texname[0], \"city4_2\", sizeof(s->texname[0]));\r\n\tQ_strlcpy(s->texname[1], \"ground1_2\", sizeof(s->texname[1]));\r\n\tQ_strlcpy(s->texname[2], \"ground1_8\", sizeof(s->texname[2]));\r\n\tQ_strlcpy(s->texname[3], \"ground1_1\", sizeof(s->texname[3]));\r\n\r\n\tfor (y = 0, i=0; y < SECTHEIGHTSIZE; y++)\r\n\tfor (x = 0; x < SECTHEIGHTSIZE; x++, i++)\r\n\t{\r\n\t\t//calculate where it is in worldspace, if that's useful to you.\r\n//\t\tfloat wx = hm->sectionsize*(sx + x/(float)(SECTHEIGHTSIZE-1));\r\n//\t\tfloat wy = hm->sectionsize*(sy + y/(float)(SECTHEIGHTSIZE-1));\r\n\r\n\t\t//calculate the RGBA tint. these are floats, so you can oversaturate.\r\n\t\ts->colours[i][0] = 1;\r\n\t\ts->colours[i][1] = 1;\r\n\t\ts->colours[i][2] = 1;\r\n\t\ts->colours[i][3] = 1;\r\n\t}\r\n\r\n\t//make sure there's lightmap storage available\r\n\tterr->InitLightmap(s, /*fill with default values*/true);\r\n\tlm = terr->GetLightmap(s, 0, /*flag as edited*/true);\r\n\tif (lm)\r\n\t{\t//pleaseworkpleaseworkpleasework\r\n\t\tfor (y = 0; y < SECTTEXSIZE; y++, lm += (HMLMSTRIDE)*4)\r\n\t\tfor (x = 0; x < SECTTEXSIZE; x++)\r\n\t\t{\r\n\t\t\t//calculate where it is in worldspace, if that's useful to you.\r\n\t\t\tfloat wx = hm->sectionsize*(sx + x/(float)(SECTTEXSIZE-1));\r\n\t\t\tfloat wy = hm->sectionsize*(sy + y/(float)(SECTTEXSIZE-1));\r\n\r\n\t\t\t//calc which texture to use\r\n\t\t\t//adds to 1, with texture[3] taking the remainder.\r\n\t\t\tlm[x*4+0] = max(0, 255 - 255*fabs(wx/1024));\r\n\t\t\tlm[x*4+1] = max(0, 255 - 255*fabs(wy/1024));\r\n\t\t\tlm[x*4+2] = min(lm[x*4+0],lm[x*4+1]);\r\n\t\t\tlm[x*4+0] -= lm[x*4+2];\r\n\t\t\tlm[x*4+1] -= lm[x*4+2];\r\n\r\n\t\t\t//logically: lm[x*4+3] = 255-(lm[x*4+0]+lm[x*4+1]+lm[x*4+2]);\r\n\t\t\t//however, the fourth channel is actually used as a lighting multiplier.\r\n\t\t\tlm[x*4+3] = 255;\r\n\t\t}\r\n\t}\r\n\r\n/*\t//insert the occasional mesh...\r\n\tif ((sx&3) == 0 && (sy&3) == 0)\r\n\t{\r\n\t\tvec3_t ang, org, axis[3];\r\n\t\torg[0] = hm->sectionsize*sx;\r\n\t\torg[1] = hm->sectionsize*sy;\r\n\t\torg[2] = 128;\r\n\t\tVectorClear(ang);\r\n\t\tang[0] = sy*12.5;\t//lul\r\n\t\tang[1] = sx*12.5;\r\n\t\tmodfuncs->AngleVectors(ang, axis[0], axis[1], axis[2]);\r\n\t\tVectorNegate(axis[1],axis[1]);\t//axis[1] needs to be left, not right. silly quakeisms.\r\n\r\n\t\t//obviously you can insert mdls instead... preferably do that!\r\n\t\tterr->AddMesh(hm, TGS_NOLOAD, NULL, \"maps/dm4.bsp\", org, axis, 1);\r\n\t}*/\r\n}\r\n\r\n#define GENBLOCKSIZE 16\r\nstatic qboolean QDECL TerrorGen_GenerateBlock(heightmap_t *hm, int sx, int sy, unsigned int tgsflags)\r\n{\r\n\tfloat h[(GENBLOCKSIZE+1)*(GENBLOCKSIZE+1)];\r\n\thmsection_t *sect[GENBLOCKSIZE*GENBLOCKSIZE];\r\n\tint mx = sx & ~(GENBLOCKSIZE-1);\r\n\tint my = sy & ~(GENBLOCKSIZE-1);\r\n\tstruct rndctx_s rctx;\r\n\tint i;\r\n\r\n\tif (!terr->GenerateSections(hm, mx, my, GENBLOCKSIZE, sect))\r\n\t\treturn false;\r\n\r\n\tfor (i = 0; i < countof(h); i++)\r\n\t\th[i] = -1024;\r\n\r\n\t//generate global height values\r\n\th[           0+           0*(GENBLOCKSIZE+1)] = 0;//TerrorGen_PredRandom(hm, mx             , my             )*GENHIGHTSCALE*4;\r\n\th[GENBLOCKSIZE+           0*(GENBLOCKSIZE+1)] = 0;//TerrorGen_PredRandom(hm, mx+GENBLOCKSIZE, my             )*GENHIGHTSCALE*4;\r\n\th[             GENBLOCKSIZE*(GENBLOCKSIZE+1)] = 0;//TerrorGen_PredRandom(hm, mx             , my+GENBLOCKSIZE)*GENHIGHTSCALE*4;\r\n\th[GENBLOCKSIZE+GENBLOCKSIZE*(GENBLOCKSIZE+1)] = 0;//TerrorGen_PredRandom(hm, mx+GENBLOCKSIZE, my+GENBLOCKSIZE)*GENHIGHTSCALE*4;\r\n\trctx.x = mx*4142^mx*523423;\r\n\trctx.y = mx*4323;\r\n\trctx.z = mx*234;\r\n\trctx.w = 2535;\r\n\tGenHeights(hm, &rctx, h, GENBLOCKSIZE, (mx-CHUNKBIAS)*SECTHEIGHTSIZE, (my-CHUNKBIAS)*SECTHEIGHTSIZE, GENHIGHTSCALE);\r\n\r\n\tfor (sy = 0; sy < GENBLOCKSIZE; sy++)\r\n\t{\r\n\t\tfor (sx = 0; sx < GENBLOCKSIZE; sx++)\r\n\t\t{\r\n\t\t\tif (!sect[sx + sy*GENBLOCKSIZE])\r\n\t\t\t\tcontinue;\t//already in memory.\r\n\r\n\t\t\t//in case we skipped a section...\r\n\t\t\trctx.x = (mx*4141^mx*523423) + sx*4231 + sy*539;\r\n\t\t\trctx.y = mx*4323;\r\n\t\t\trctx.z = mx*231+sy;\r\n\t\t\trctx.w = 253553;\r\n\r\n\t\t\tTerrorGen_GenerateOne(hm, &rctx, mx+sx-CHUNKBIAS, my+sy-CHUNKBIAS, sect[sx + sy*GENBLOCKSIZE],\r\n\t\t\t\t\th[(sx  )+(sy  )*(GENBLOCKSIZE+1)],\r\n\t\t\t\t\th[(sx+1)+(sy  )*(GENBLOCKSIZE+1)],\r\n\t\t\t\t\th[(sx  )+(sy+1)*(GENBLOCKSIZE+1)],\r\n\t\t\t\t\th[(sx+1)+(sy+1)*(GENBLOCKSIZE+1)]);\r\n\t\t\tterr->FinishedSection(sect[sx + sy*GENBLOCKSIZE], true);\r\n\t\t}\r\n\t}\r\n\treturn true;\r\n}\r\n\r\nstatic qboolean TerrorGen_Shutdown(void)\r\n{\t//if its still us, make sure there's no dangling pointers.\r\n\tif (terr->AutogenerateSection == TerrorGen_GenerateBlock)\r\n\t\tterr->AutogenerateSection = NULL;\r\n\treturn true;\r\n}\r\nqboolean Plug_Init(void)\r\n{\r\n\tmodfuncs = plugfuncs->GetEngineInterface(plugmodfuncs_name, sizeof(*modfuncs));\r\n\tif (modfuncs && modfuncs->version < MODPLUGFUNCS_VERSION)\r\n\t\tmodfuncs = NULL;\r\n\tterr = plugfuncs->GetEngineInterface(plugterrainfuncs_name, sizeof(*terr));\r\n\tif (!terr)\r\n\t\treturn false;\r\n\tif (!plugfuncs->ExportFunction(\"Shutdown\", TerrorGen_Shutdown))\r\n\t\treturn false;\r\n\r\n\tterr->AutogenerateSection = TerrorGen_GenerateBlock;\r\n\treturn true;\r\n}\r\n#else\r\nqboolean Plug_Init(void)\r\n{\r\n\treturn false;\r\n}\r\n#endif"
  },
  {
    "path": "plugins/winamp/winamp.c",
    "content": "//mp3 menu and track selector.\n//was origonally an mp3 track selector, now handles lots of media specific stuff - like q3 films!\n//should rename to m_media.c\n\n/*«11:56:05 am» «@Spikester» EBUILTIN(void, Menu_Control, (int mnum));\n«11:56:05 am» «@Spikester» #define MENU_CLEAR 0\n«11:56:05 am» «@Spikester» #define MENU_GRAB 1\n«11:56:05 am» «@Spikester» EBUILTIN(int, Key_GetKeyCode, (char *keyname));\n«11:56:13 am» «@Spikester» that's how you do menus. :)*/\n\n#define BUILD 1\n\n#include <stdlib.h> // needed for itoi\n#include <stdio.h> // needed for itoi?\n\n#include <windows.h>\n\n#include \"../plugin.h\"\n\n#include \"winamp.h\"\nHWND hwnd_winamp;\n\n//int media_playing=true;//try to continue from the standard playlist\n//cvar_t winamp_dir = {\"winamp_dir\", \"c:/program files/winamp5/\"};\n//cvar_t winamp_exe = {\"winamp_exe\", \"winamp.exe\"};\n//cvar_t media_shuffle = {\"media_shuffle\", \"1\"};\n//cvar_t media_repeat = {\"media_repeat\", \"1\"};\n\nqboolean WinAmp_GetHandle (void)\n{\n\tif ((hwnd_winamp = FindWindow(\"Winamp\", NULL)))\n\t\treturn true;\n\tif ((hwnd_winamp = FindWindow(\"Winamp v1.x\", NULL)))\n\t\treturn true;\n\tif ((hwnd_winamp = FindWindow(\"winamp\", NULL)))\n\t\treturn true;\n\n\treturn false;\n}\n\n// Start Moodles Attempt at Winamp Commands\n// Note strange bug, load up FTE normally. And type winamp_version, for me the output is 0, but if I do winamp_version a second time it says 24604 (which is hex for 5010, my version of winamp is 5.10)\n\nvoid Winamp_Play_f(void)\n{\n\tif (!WinAmp_GetHandle())\n\t{\n\t\tCon_Printf(\"Winamp not loaded\\n\");\n\t\treturn;\n\t}\n\n\t//SendMessage(hwnd_winamp, WM_COMMAND, WINAMP_BUTTON2, 0); <- is below fails, uncomment this.\n\n\tSendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_STARTPLAY);\n\tCon_Printf(\"Attempting to start playback\\n\");\n}\n\nvoid Winamp_Version_f(void)\n{\n\tint version;\n\n\tif (!WinAmp_GetHandle())\n\t{\n\t\tCon_Printf(\"Winamp not loaded\\n\");\n\t\treturn;\n\t}\n\n\tversion = SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_GETVERSION);\n\n\t//itoa (version, temp, 16); // should convert it to hex\n\n\tCon_Printf(\"Winamp Version: %d\\n\",version);\n}\n\nvoid Winamp_TimeLeft_f(void)\n{\n\tint tracklength;\n\tint trackposition;\n\tint timeleft;\n\n\tif (!WinAmp_GetHandle())\n\t{\n\t\tCon_Printf(\"Winamp not loaded\\n\");\n\t\treturn;\n\t}\n\n\ttracklength = SendMessage(hwnd_winamp,WM_WA_IPC,1,IPC_GETOUTPUTTIME);\n\ttrackposition = SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_GETOUTPUTTIME);\n\n\ttimeleft = tracklength-(trackposition/1000);\n\n\tCon_Printf(\"Time Left: %d seconds\\n\",timeleft); // convert it to h:m:s later\n}\n\nvoid Winamp_JumpTo_f(void) // input is a percentage\n{\n\tint tracklength;\n\tfloat inputpercent;\n\tdouble trackpercent;\n\tchar input[20];\n\tint res;\n\n\tif (!WinAmp_GetHandle())\n\t{\n\t\tCon_Printf(\"Winamp not loaded\\n\");\n\t\treturn;\n\t}\n\n\ttracklength = SendMessage(hwnd_winamp,WM_WA_IPC,1,IPC_GETOUTPUTTIME);\n\n\tcmdfuncs->Argv(1,input,sizeof(input));\n\n\tinputpercent = atoi(input);\n\n\tif (inputpercent > 100)\n\t{\n\t\tCon_Printf(\"ERROR: Choose a percent between 0 and 100\\n\");\n\t\treturn;\n\t}\n\n\tinputpercent = inputpercent/100;\n\n\ttrackpercent = (tracklength*1000)*inputpercent;\n\n\tres = SendMessage(hwnd_winamp,WM_WA_IPC,trackpercent,IPC_JUMPTOTIME);\n\n\tif (res == 0)\n\t{\n\t\tCon_Printf(\"Successfully jumped to %s percent\\n\",input);\n\t\treturn;\n\t}\n\telse if (res == -1)\n\t{\n\t\tCon_Printf(\"There are no songs playing\\n\");\n\t\treturn;\n\t}\n\telse if (res == 1)\n\t{\n\t\tCon_Printf(\"End of file\\n\");\n\t}\n\n\tCon_Printf(\"Oh oh spagettioes you shouldn't see this\");\n}\n\nvoid Winamp_GoToPlayListPosition_f(void) // the playlist selecter doesn't actually work\n{\n\t//int length = SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_GETLISTLENGTH); //set a max\n\tchar input[20];\n\tint inputnumber;\n\n\tif (!WinAmp_GetHandle())\n\t{\n\t\tCon_Printf(\"Winamp not loaded\\n\");\n\t\treturn;\n\t}\n\n\tcmdfuncs->Argv(1,input,sizeof(input));\n\n\tinputnumber = atoi(input);\n\n\tSendMessage(hwnd_winamp,WM_WA_IPC,inputnumber,IPC_SETPLAYLISTPOS);\n\n\tSendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_STARTPLAY); // the above only selects it, doesn't actually play it.\n\n\tCon_Printf(\"Attemped to set playlist position %s\\n\",input);\n}\n\nvoid Winamp_Volume_f(void) // I think this only works when the client did the winamp_play\n{\n\tchar input[20];\n\tint inputnumber;\n\n\tif (!WinAmp_GetHandle())\n\t{\n\t\tCon_Printf(\"Winamp not loaded\\n\");\n\t\treturn;\n\t}\n\n\tcmdfuncs->Argv(1,input,sizeof(input));\n\n\tinputnumber = atoi(input);\n\n\tif ((!input[0]) || (inputnumber > 255))\n\t{\n\t\tCon_Printf(\"Choose a number between 0 and 255\\n\");\n\t\treturn;\n\t}\n\n\tSendMessage(hwnd_winamp,WM_WA_IPC,inputnumber,IPC_SETVOLUME);\n\n\tCon_Printf(\"Winamp volume set to: %s\\n\",input);\n}\n\nvoid Winamp_ChannelPanning_f(void) // doesn't seem to work for me\n{\n\tchar input[20];\n\tint inputnumber;\n\n\tif (!WinAmp_GetHandle())\n\t{\n\t\tCon_Printf(\"Winamp not loaded\\n\");\n\t\treturn;\n\t}\n\n\tcmdfuncs->Argv(1,input,sizeof(input));\n\n\tinputnumber = atoi(input);\n\n\tif ((!input[0]) || (inputnumber > 255))\n\t{\n\t\tCon_Printf(\"Choose a number between 0 (left) and 255 (right). Center is about 127\\n\");\n\t\treturn;\n\t}\n\n\tSendMessage(hwnd_winamp,WM_WA_IPC,inputnumber,IPC_SETPANNING);\n\n\tCon_Printf(\"Winamp channel panning set to: %s\\n\",input);\n}\n\nvoid Winamp_PlayListLength_f(void) // has a habit of returning 0 when you dont use winamp_play to start off playing\n{\n\tint length;\n\n\tif (!WinAmp_GetHandle())\n\t{\n\t\tCon_Printf(\"Winamp not loaded\\n\");\n\t\treturn;\n\t}\n\n\tlength = SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_GETLISTLENGTH);\n\n\tCon_Printf(\"Winamp playlist length: %d\\n\",length);\n}\n\nvoid Winamp_PlayListPosition_f(void) // has a habit of return 0 of 0\n{\n\tint pos;\n\tint length;\n\n\tif (!WinAmp_GetHandle())\n\t{\n\t\tCon_Printf(\"Winamp not loaded\\n\");\n\t\treturn;\n\t}\n\n\tpos = SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_GETLISTPOS);\n\tlength = SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_GETLISTLENGTH);\n\n\tCon_Printf(\"Winamp currently on position '%d' of '%d'\\n\",pos,length);\n}\n\nvoid Winamp_SongInfo_f(void)\n{\n\tchar title[255];\n\tint res;\n\tint samplerate;\n\tint bitrate;\n\tint channels;\n\n\tif (!WinAmp_GetHandle())\n\t{\n\t\tCon_Printf(\"Winamp not loaded\\n\");\n\t\treturn;\n\t}\n\n\tres = SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_ISPLAYING);\n\tsamplerate = SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_GETINFO);\n\tbitrate = SendMessage(hwnd_winamp,WM_WA_IPC,1,IPC_GETINFO);\n\tchannels = SendMessage(hwnd_winamp,WM_WA_IPC,2,IPC_GETINFO);\n\n\tGetWindowText(hwnd_winamp, title, sizeof(title));\n\n\tif (res == 0)\n\t{\n\t\tCon_Printf(\"WinAmp is off\\n\");\n\t\treturn;\n\t}\n\telse if (res == 1)\n\t{\n\t\tCon_Printf(\"Currently playing: %s\\nSamplerate: %dkHz\\nBitrate: %dkbps \\nChannels: %d\\n\",title,samplerate,bitrate,channels);\n\t\treturn;\n\t}\n\telse if (res == 3)\n\t{\n\t\tCon_Printf(\"Winamp is paused\\n\");\n\t\treturn;\n\t}\n}\n\nvoid Winamp_Restart_f(void)\n{\n\tif (!WinAmp_GetHandle())\n\t{\n\t\tCon_Printf(\"Winamp not loaded\\n\");\n\t\treturn;\n\t}\n\n\tSendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_RESTARTWINAMP);\n\n\tCon_Printf(\"Attempting to restart winamp\\n\");\n}\n\nvoid Winamp_Shuffle_f(void) //it works, thats all i can say lol\n{\n\tchar input[20];\n\tint inputnumber;\n\tint inputnumber2;\n\tint get;\n\n\tif (!WinAmp_GetHandle())\n\t{\n\t\tCon_Printf(\"Winamp not loaded\\n\");\n\t\treturn;\n\t}\n\n\tget = SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_GET_SHUFFLE);\n\n\tcmdfuncs->Argv(1,input,sizeof(input));\n\n\tinputnumber2 = atoi(input);\n\n\t//inputnumber = Cmd_Argc();\n\tinputnumber = 1; // fix later\n\n\tif (inputnumber2 == 1)\n\t{\n\t\tSendMessage(hwnd_winamp,WM_WA_IPC,1,IPC_SET_SHUFFLE);\n\t\tCon_Printf(\"Winamp shuffle turned on\\n\");\n\t\treturn;\n\t}\n\telse if ((inputnumber2 == 0) && (inputnumber == 2))\n\t{\n\t\tSendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_SET_SHUFFLE);\n\t\tCon_Printf(\"Winamp shuffle turned off\\n\");\n\t\treturn;\n\t}\n\telse if (get == 1)\n\t{\n\t\tCon_Printf(\"Winamp shuffle is currently on\\n\");\n\t}\n\telse if (get == 0)\n\t{\n\t\tCon_Printf(\"Winamp shuffle is currently off\\n\");\n\t}\n\n\t\tCon_Printf(\"Enter 1 to to turn Winamp shuffle on, 0 to turn it off\\n\");\n\t\treturn;\n}\n\nvoid Winamp_Repeat_f(void) // it works, thats all i can say lol\n{\n\tchar input[20];\n\tint inputnumber;\n\tint inputnumber2;\n\tint get;\n\n\tif (!WinAmp_GetHandle())\n\t{\n\t\tCon_Printf(\"Winamp not loaded\\n\");\n\t\treturn;\n\t}\n\n\tget = SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_GET_REPEAT);\n\n\tcmdfuncs->Argv(1,input,sizeof(input));\n\n\tinputnumber2 = atoi(input);\n\n\t//inputnumber = Cmd_Argc();\n\tinputnumber = 2; // fix later\n\n\tif (inputnumber2 == 1)\n\t{\n\t\tSendMessage(hwnd_winamp,WM_WA_IPC,1,IPC_SET_REPEAT);\n\t\tCon_Printf(\"Winamp repeat turned on\\n\");\n\t\treturn;\n\t}\n\telse if ((inputnumber2 == 0) && (inputnumber == 2))\n\t{\n\t\tSendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_SET_REPEAT);\n\t\tCon_Printf(\"Winamp repeat turned off\\n\");\n\t\treturn;\n\t}\n\telse if (get == 1)\n\t{\n\t\tCon_Printf(\"Winamp repeat is currently on\\n\");\n\t}\n\telse if (get == 0)\n\t{\n\t\tCon_Printf(\"Winamp repeat is currently off\\n\");\n\t}\n\n\t\tCon_Printf(\"Enter 1 to to turn Winamp repeat on, 0 to turn it off\\n\");\n\t\treturn;\n}\n\nvoid Winamp_VolumeUp_f(void)\n{\n\tSendMessage(hwnd_winamp, WM_COMMAND, WINAMP_VOLUMEUP, 0);\n\n\tCon_Printf(\"Winamp volume incremented\\n\");\n}\n\nvoid Winamp_VolumeDown_f(void)\n{\n\tSendMessage(hwnd_winamp, WM_COMMAND, WINAMP_VOLUMEDOWN, 0);\n\n\tCon_Printf(\"Winamp volume decremented\\n\");\n}\n\nvoid Winamp_FastForward5Seconds_f(void)\n{\n\tSendMessage(hwnd_winamp, WM_COMMAND, WINAMP_FFWD5S, 0);\n\n\tCon_Printf(\"Winamp fast forwarded 5 seconds\\n\");\n}\n\nvoid Winamp_Rewind5Seconds_f(void)\n{\n\tSendMessage(hwnd_winamp, WM_COMMAND, WINAMP_REW5S, 0);\n\n\tCon_Printf(\"Winamp rewinded 5 seconds\\n\");\n}\n\n// End Moodles Attempt at Winamp Commands\n\nvoid Winamp_InitCommands(void)\n{\n\tcmdfuncs->AddCommand(\"winamp_play\", Winamp_Play_f, \"\");\n\tcmdfuncs->AddCommand(\"winamp_version\", Winamp_Version_f, \"\");\n\tcmdfuncs->AddCommand(\"winamp_timeleft\", Winamp_TimeLeft_f, \"\");\n\tcmdfuncs->AddCommand(\"winamp_jumpto\", Winamp_JumpTo_f, \"\");\n\tcmdfuncs->AddCommand(\"winamp_gotoplaylistposition\", Winamp_GoToPlayListPosition_f, \"\");\n\tcmdfuncs->AddCommand(\"winamp_volume\", Winamp_Volume_f, \"\");\n\tcmdfuncs->AddCommand(\"winamp_channelpanning\", Winamp_ChannelPanning_f, \"\");\n\tcmdfuncs->AddCommand(\"winamp_playlistlength\", Winamp_PlayListLength_f, \"\");\n\tcmdfuncs->AddCommand(\"winamp_playlistposition\", Winamp_PlayListPosition_f, \"\");\n\tcmdfuncs->AddCommand(\"winamp_songinfo\", Winamp_SongInfo_f, \"\");\n\tcmdfuncs->AddCommand(\"winamp_restart\", Winamp_Restart_f, \"\");\n\tcmdfuncs->AddCommand(\"winamp_shuffle\", Winamp_Shuffle_f, \"\");\n\tcmdfuncs->AddCommand(\"winamp_repeat\", Winamp_Repeat_f, \"\");\n\tcmdfuncs->AddCommand(\"winamp_volumeup\", Winamp_VolumeUp_f, \"\");\n\tcmdfuncs->AddCommand(\"winamp_volumedown\", Winamp_VolumeDown_f, \"\");\n\tcmdfuncs->AddCommand(\"winamp_fastforward5seconds\", Winamp_FastForward5Seconds_f, \"\");\n\tcmdfuncs->AddCommand(\"winamp_rewind5seconds\", Winamp_Rewind5Seconds_f, \"\");\n}\n\nqboolean Plug_Init(void)\n{\n\tWinamp_InitCommands();\n\treturn true;\n}\n"
  },
  {
    "path": "plugins/winamp/winamp.h",
    "content": "/*\n** Copyright (C) 2003 Nullsoft, Inc.\n**\n** This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held\n** liable for any damages arising from the use of this software.\n**\n** Permission is granted to anyone to use this software for any purpose, including commercial applications, and to\n** alter it and redistribute it freely, subject to the following restrictions:\n**\n**   1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software.\n**      If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.\n**\n**   2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.\n**\n**   3. This notice may not be removed or altered from any source distribution.\n**\n*/\n\n#ifndef _WA_IPC_H_\n#define _WA_IPC_H_\n\n/*\n** This is the modern replacement for the classic 'frontend.h'. Most of these\n** updates are designed for in-process use, i.e. from a plugin.\n**\n*/\n\n/* message used to sent many messages to winamp's main window.\n** most all of the IPC_* messages involve sending the message in the form of:\n**   result = SendMessage(hwnd_winamp,WM_WA_IPC,(parameter),IPC_*);\n*/\n#define WM_WA_IPC WM_USER\n/* but some of them use WM_COPYDATA. be afraid.\n*/\n\n#define IPC_GETVERSION 0\n/* int version = SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_GETVERSION);\n**\n** Version will be 0x20yx for winamp 2.yx. versions previous to Winamp 2.0\n** typically (but not always) use 0x1zyx for 1.zx versions. Weird, I know.\n*/\n\n#define IPC_GETREGISTEREDVERSION 770\n\n\ntypedef struct {\n  char *filename;\n  char *title;\n  int length;\n} enqueueFileWithMetaStruct; // send this to a IPC_PLAYFILE in a non WM_COPYDATA,\n// and you get the nice desired result. if title is NULL, it is treated as a \"thing\",\n// otherwise it's assumed to be a file (for speed)\n\n#define IPC_PLAYFILE 100  // dont be fooled, this is really the same as enqueufile\n#define IPC_ENQUEUEFILE 100\n/* sent as a WM_COPYDATA, with IPC_PLAYFILE as the dwData, and the string to play\n** as the lpData. Just enqueues, does not clear the playlist or change the playback\n** state.\n*/\n\n\n#define IPC_DELETE 101\n#define IPC_DELETE_INT 1101 // don't use this, it's used internally by winamp when\n                            // dealing with some lame explorer issues.\n/* SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_DELETE);\n** Use IPC_DELETE to clear Winamp's internal playlist.\n*/\n\n\n#define IPC_STARTPLAY 102   // starts playback. almost like hitting play in Winamp.\n#define IPC_STARTPLAY_INT 1102 // used internally, don't bother using it (won't be any fun)\n\n\n#define IPC_CHDIR 103\n/* sent as a WM_COPYDATA, with IPC_CHDIR as the dwData, and the directory to change to\n** as the lpData.\n*/\n\n\n#define IPC_ISPLAYING 104\n/* int res = SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_ISPLAYING);\n** If it returns 1, it is playing. if it returns 3, it is paused,\n** if it returns 0, it is not playing.\n*/\n\n\n#define IPC_GETOUTPUTTIME 105\n/* int res = SendMessage(hwnd_winamp,WM_WA_IPC,mode,IPC_GETOUTPUTTIME);\n** returns the position in milliseconds of the current track (mode = 0),\n** or the track length, in seconds (mode = 1). Returns -1 if not playing or error.\n*/\n\n\n#define IPC_JUMPTOTIME 106\n/* (requires Winamp 1.60+)\n** SendMessage(hwnd_winamp,WM_WA_IPC,ms,IPC_JUMPTOTIME);\n** IPC_JUMPTOTIME sets the position in milliseconds of the\n** current song (approximately).\n** Returns -1 if not playing, 1 on eof, or 0 if successful\n*/\n\n#define IPC_GETMODULENAME 109\n#define IPC_EX_ISRIGHTEXE 666\n/* usually shouldnt bother using these, but here goes:\n** send a WM_COPYDATA with IPC_GETMODULENAME, and an internal\n** flag gets set, which if you send a normal WM_WA_IPC message with\n** IPC_EX_ISRIGHTEXE, it returns whether or not that filename\n** matches. lame, I know.\n*/\n\n#define IPC_WRITEPLAYLIST 120\n/* (requires Winamp 1.666+)\n** SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_WRITEPLAYLIST);\n**\n** IPC_WRITEPLAYLIST writes the current playlist to <winampdir>\\\\Winamp.m3u,\n** and returns the current playlist position.\n** Kinda obsoleted by some of the 2.x new stuff, but still good for when\n** using a front-end (instead of a plug-in)\n*/\n\n\n#define IPC_SETPLAYLISTPOS 121\n/* (requires Winamp 2.0+)\n** SendMessage(hwnd_winamp,WM_WA_IPC,position,IPC_SETPLAYLISTPOS)\n** IPC_SETPLAYLISTPOS sets the playlist position to 'position'. It\n** does not change playback or anything, it just sets position, and\n** updates the view if necessary\n*/\n\n\n#define IPC_SETVOLUME 122\n/* (requires Winamp 2.0+)\n** SendMessage(hwnd_winamp,WM_WA_IPC,volume,IPC_SETVOLUME);\n** IPC_SETVOLUME sets the volume of Winamp (from 0-255).\n*/\n\n\n#define IPC_SETPANNING 123\n/* (requires Winamp 2.0+)\n** SendMessage(hwnd_winamp,WM_WA_IPC,panning,IPC_SETPANNING);\n** IPC_SETPANNING sets the panning of Winamp (from 0 (left) to 255 (right)).\n*/\n\n\n#define IPC_GETLISTLENGTH 124\n/* (requires Winamp 2.0+)\n** int length = SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_GETLISTLENGTH);\n** IPC_GETLISTLENGTH returns the length of the current playlist, in\n** tracks.\n*/\n\n\n#define IPC_GETLISTPOS 125\n/* (requires Winamp 2.05+)\n** int pos=SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_GETLISTPOS);\n** IPC_GETLISTPOS returns the playlist position. A lot like IPC_WRITEPLAYLIST\n** only faster since it doesn't have to write out the list. Heh, silly me.\n*/\n\n\n#define IPC_GETINFO 126\n/* (requires Winamp 2.05+)\n** int inf=SendMessage(hwnd_winamp,WM_WA_IPC,mode,IPC_GETINFO);\n** IPC_GETINFO returns info about the current playing song. The value\n** it returns depends on the value of 'mode'.\n** Mode      Meaning\n** ------------------\n** 0         Samplerate (i.e. 44100)\n** 1         Bitrate  (i.e. 128)\n** 2         Channels (i.e. 2)\n** 3 (5+)    Video LOWORD=w HIWORD=h\n** 4 (5+)    > 65536, string (video description)\n*/\n\n\n#define IPC_GETEQDATA 127\n/* (requires Winamp 2.05+)\n** int data=SendMessage(hwnd_winamp,WM_WA_IPC,pos,IPC_GETEQDATA);\n** IPC_GETEQDATA queries the status of the EQ.\n** The value returned depends on what 'pos' is set to:\n** Value      Meaning\n** ------------------\n** 0-9        The 10 bands of EQ data. 0-63 (+20db - -20db)\n** 10         The preamp value. 0-63 (+20db - -20db)\n** 11         Enabled. zero if disabled, nonzero if enabled.\n** 12         Autoload. zero if disabled, nonzero if enabled.\n*/\n\n\n#define IPC_SETEQDATA 128\n/* (requires Winamp 2.05+)\n** SendMessage(hwnd_winamp,WM_WA_IPC,pos,IPC_GETEQDATA);\n** SendMessage(hwnd_winamp,WM_WA_IPC,value,IPC_SETEQDATA);\n** IPC_SETEQDATA sets the value of the last position retrieved\n** by IPC_GETEQDATA. This is pretty lame, and we should provide\n** an extended version that lets you do a MAKELPARAM(pos,value).\n** someday...\n\n  new (2.92+):\n    if the high byte is set to 0xDB, then the third byte specifies\n    which band, and the bottom word specifies the value.\n*/\n\n#define IPC_ADDBOOKMARK 129\n/* (requires Winamp 2.4+)\n** Sent as a WM_COPYDATA, using IPC_ADDBOOKMARK, adds the specified\n** file/url to the Winamp bookmark list.\n*/\n/*\nIn winamp 5+, we use this as a normal WM_WA_IPC and the string:\n\n  \"filename\\0title\\0\"\n\n  to notify the library/bookmark editor that a bookmark\nwas added. Note that using this message in this context does not\nactually add the bookmark.\ndo not use :)\n*/\n\n\n#define IPC_INSTALLPLUGIN 130\n/* not implemented, but if it was you could do a WM_COPYDATA with\n** a path to a .wpz, and it would install it.\n*/\n\n\n#define IPC_RESTARTWINAMP 135\n/* (requires Winamp 2.2+)\n** SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_RESTARTWINAMP);\n** IPC_RESTARTWINAMP will restart Winamp (isn't that obvious ? :)\n*/\n\n\n#define IPC_ISFULLSTOP 400\n/* (requires winamp 2.7+ I think)\n** ret=SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_ISFULLSTOP);\n** useful for when you're an output plugin, and you want to see\n** if the stop/close is a full stop, or just between tracks.\n** returns nonzero if it's full, zero if it's just a new track.\n*/\n\n\n#define IPC_INETAVAILABLE 242\n/* (requires Winamp 2.05+)\n** val=SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_INETAVAILABLE);\n** IPC_INETAVAILABLE will return 1 if the Internet connection is available for Winamp.\n*/\n\n\n#define IPC_UPDTITLE 243\n/* (requires Winamp 2.2+)\n** SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_UPDTITLE);\n** IPC_UPDTITLE will ask Winamp to update the informations about the current title.\n*/\n\n\n#define IPC_REFRESHPLCACHE 247\n/* (requires Winamp 2.2+)\n** SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_REFRESHPLCACHE);\n** IPC_REFRESHPLCACHE will flush the playlist cache buffer.\n** (send this if you want it to go refetch titles for tracks)\n*/\n\n\n#define IPC_GET_SHUFFLE 250\n/* (requires Winamp 2.4+)\n** val=SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_GET_SHUFFLE);\n**\n** IPC_GET_SHUFFLE returns the status of the Shuffle option (1 if set)\n*/\n\n\n#define IPC_GET_REPEAT 251\n/* (requires Winamp 2.4+)\n** val=SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_GET_REPEAT);\n**\n** IPC_GET_REPEAT returns the status of the Repeat option (1 if set)\n*/\n\n\n#define IPC_SET_SHUFFLE 252\n/* (requires Winamp 2.4+)\n** SendMessage(hwnd_winamp,WM_WA_IPC,value,IPC_SET_SHUFFLE);\n**\n** IPC_SET_SHUFFLE sets the status of the Shuffle option (1 to turn it on)\n*/\n\n\n#define IPC_SET_REPEAT 253\n/* (requires Winamp 2.4+)\n** SendMessage(hwnd_winamp,WM_WA_IPC,value,IPC_SET_REPEAT);\n**\n** IPC_SET_REPEAT sets the status of the Repeat option (1 to turn it on)\n*/\n\n\n#define IPC_ENABLEDISABLE_ALL_WINDOWS 259 // 0xdeadbeef to disable\n/* (requires Winamp 2.9+)\n** SendMessage(hwnd_winamp,WM_WA_IPC,enable?0:0xdeadbeef,IPC_MBOPENREAL);\n** sending with 0xdeadbeef as the param disables all winamp windows,\n** any other values will enable all winamp windows.\n*/\n\n\n#define IPC_GETWND 260\n/* (requires Winamp 2.9+)\n** HWND h=SendMessage(hwnd_winamp,WM_WA_IPC,IPC_GETWND_xxx,IPC_GETWND);\n** returns the HWND of the window specified.\n*/\n  #define IPC_GETWND_EQ 0 // use one of these for the param\n  #define IPC_GETWND_PE 1\n  #define IPC_GETWND_MB 2\n  #define IPC_GETWND_VIDEO 3\n#define IPC_ISWNDVISIBLE 261 // same param as IPC_GETWND\n\n\n\n\n/************************************************************************\n***************** in-process only (WE LOVE PLUGINS)\n************************************************************************/\n\n\n#define IPC_SETSKIN 200\n/* (requires Winamp 2.04+, only usable from plug-ins (not external apps))\n** SendMessage(hwnd_winamp,WM_WA_IPC,(WPARAM)\"skinname\",IPC_SETSKIN);\n** IPC_SETSKIN sets the current skin to \"skinname\". Note that skinname\n** can be the name of a skin, a skin .zip file, with or without path.\n** If path isn't specified, the default search path is the winamp skins\n** directory.\n*/\n\n\n#define IPC_GETSKIN 201\n/* (requires Winamp 2.04+, only usable from plug-ins (not external apps))\n** SendMessage(hwnd_winamp,WM_WA_IPC,(WPARAM)skinname_buffer,IPC_GETSKIN);\n** IPC_GETSKIN puts the directory where skin bitmaps can be found\n** into  skinname_buffer.\n** skinname_buffer must be MAX_PATH characters in length.\n** When using a .zip'd skin file, it'll return a temporary directory\n** where the ZIP was decompressed.\n*/\n\n\n#define IPC_EXECPLUG 202\n/* (requires Winamp 2.04+, only usable from plug-ins (not external apps))\n** SendMessage(hwnd_winamp,WM_WA_IPC,(WPARAM)\"vis_file.dll\",IPC_EXECPLUG);\n** IPC_EXECPLUG executes a visualization plug-in pointed to by WPARAM.\n** the format of this string can be:\n** \"vis_whatever.dll\"\n** \"vis_whatever.dll,0\" // (first mod, file in winamp plug-in dir)\n** \"C:\\\\dir\\\\vis_whatever.dll,1\"\n*/\n\n\n#define IPC_GETPLAYLISTFILE 211\n/* (requires Winamp 2.04+, only usable from plug-ins (not external apps))\n** char *name=SendMessage(hwnd_winamp,WM_WA_IPC,index,IPC_GETPLAYLISTFILE);\n** IPC_GETPLAYLISTFILE gets the filename of the playlist entry [index].\n** returns a pointer to it. returns NULL on error.\n*/\n\n\n#define IPC_GETPLAYLISTTITLE 212\n/* (requires Winamp 2.04+, only usable from plug-ins (not external apps))\n** char *name=SendMessage(hwnd_winamp,WM_WA_IPC,index,IPC_GETPLAYLISTTITLE);\n**\n** IPC_GETPLAYLISTTITLE gets the title of the playlist entry [index].\n** returns a pointer to it. returns NULL on error.\n*/\n\n\n#define IPC_GETHTTPGETTER 240\n/* retrieves a function pointer to a HTTP retrieval function.\n** if this is unsupported, returns 1 or 0.\n** the function should be:\n** int (*httpRetrieveFile)(HWND hwnd, char *url, char *file, char *dlgtitle);\n** if you call this function, with a parent window, a URL, an output file, and a dialog title,\n** it will return 0 on successful download, 1 on error.\n*/\n\n\n#define IPC_MBOPEN 241\n/* (requires Winamp 2.05+)\n** SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_MBOPEN);\n** SendMessage(hwnd_winamp,WM_WA_IPC,(WPARAM)url,IPC_MBOPEN);\n** IPC_MBOPEN will open a new URL in the minibrowser. if url is NULL, it will open the Minibrowser window.\n*/\n\n\n\n#define IPC_CHANGECURRENTFILE 245\n/* (requires Winamp 2.05+)\n** SendMessage(hwnd_winamp,WM_WA_IPC,(WPARAM)file,IPC_CHANGECURRENTFILE);\n** IPC_CHANGECURRENTFILE will set the current playlist item.\n*/\n\n\n#define IPC_GETMBURL 246\n/* (requires Winamp 2.2+)\n** char buffer[4096]; // Urls can be VERY long\n** SendMessage(hwnd_winamp,WM_WA_IPC,(WPARAM)buffer,IPC_GETMBURL);\n** IPC_GETMBURL will retrieve the current Minibrowser URL into buffer.\n** buffer must be at least 4096 bytes long.\n*/\n\n\n#define IPC_MBBLOCK 248\n/* (requires Winamp 2.4+)\n** SendMessage(hwnd_winamp,WM_WA_IPC,value,IPC_MBBLOCK);\n**\n** IPC_MBBLOCK will block the Minibrowser from updates if value is set to 1\n*/\n\n#define IPC_MBOPENREAL 249\n/* (requires Winamp 2.4+)\n** SendMessage(hwnd_winamp,WM_WA_IPC,(WPARAM)url,IPC_MBOPENREAL);\n**\n** IPC_MBOPENREAL works the same as IPC_MBOPEN except that it will works even if\n** IPC_MBBLOCK has been set to 1\n*/\n\n#define IPC_ADJUST_OPTIONSMENUPOS 280\n/* (requires Winamp 2.9+)\n** int newpos=SendMessage(hwnd_winamp,WM_WA_IPC,(WPARAM)adjust_offset,IPC_ADJUST_OPTIONSMENUPOS);\n** moves where winamp expects the Options menu in the main menu. Useful if you wish to insert a\n** menu item above the options/skins/vis menus.\n*/\n\n#define IPC_GET_HMENU 281\n/* (requires Winamp 2.9+)\n** HMENU hMenu=SendMessage(hwnd_winamp,WM_WA_IPC,(WPARAM)0,IPC_GET_HMENU);\n** values for data:\n** 0 : main popup menu\n** 1 : main menubar file menu\n** 2 : main menubar options menu\n** 3 : main menubar windows menu\n** 4 : main menubar help menu\n** other values will return NULL.\n*/\n\n#define IPC_GET_EXTENDED_FILE_INFO 290 //pass a pointer to the following struct in wParam\n#define IPC_GET_EXTENDED_FILE_INFO_HOOKABLE 296\n/* (requires Winamp 2.9+)\n** to use, create an extendedFileInfoStruct, point the values filename and metadata to the\n** filename and metadata field you wish to query, and ret to a buffer, with retlen to the\n** length of that buffer, and then SendMessage(hwnd_winamp,WM_WA_IPC,&struct,IPC_GET_EXTENDED_FILE_INFO);\n** the results should be in the buffer pointed to by ret.\n** returns 1 if the decoder supports a getExtendedFileInfo method\n*/\ntypedef struct {\n  char *filename;\n  char *metadata;\n  char *ret;\n  int retlen;\n} extendedFileInfoStruct;\n\n#define IPC_GET_BASIC_FILE_INFO 291 //pass a pointer to the following struct in wParam\ntypedef struct {\n  char *filename;\n\n  int quickCheck; // set to 0 to always get, 1 for quick, 2 for default (if 2, quickCheck will be set to 0 if quick wasnot used)\n\n  // filled in by winamp\n  int length;\n  char *title;\n  int titlelen;\n} basicFileInfoStruct;\n\n#define IPC_GET_EXTLIST 292 //returns doublenull delimited. GlobalFree() it when done. if data is 0, returns raw extlist, if 1, returns something suitable for getopenfilename\n\n#define IPC_INFOBOX 293\n/*typedef struct {\n  HWND parent;\n  char *filename;\n} infoBoxParam;*/\n\n#define IPC_SET_EXTENDED_FILE_INFO 294 //pass a pointer to the a extendedFileInfoStruct in wParam\n/* (requires Winamp 2.9+)\n** to use, create an extendedFileInfoStruct, point the values filename and metadata to the\n** filename and metadata field you wish to write in ret. (retlen is not used). and then\n** SendMessage(hwnd_winamp,WM_WA_IPC,&struct,IPC_SET_EXTENDED_FILE_INFO);\n** returns 1 if the metadata is supported\n** Call IPC_WRITE_EXTENDED_FILE_INFO once you're done setting all the metadata you want to update\n*/\n\n#define IPC_WRITE_EXTENDED_FILE_INFO 295\n/* (requires Winamp 2.9+)\n** writes all the metadata set thru IPC_SET_EXTENDED_FILE_INFO to the file\n** returns 1 if the file has been successfully updated, 0 if error\n*/\n\n#define IPC_FORMAT_TITLE 297\ntypedef struct\n{\n  char *spec; // NULL=default winamp spec\n  void *p;\n\n  char *out;\n  int out_len;\n\n  char * (*TAGFUNC)(char * tag, void * p); //return 0 if not found\n  void (*TAGFREEFUNC)(char * tag,void * p);\n} waFormatTitle;\n\n#define IPC_GETUNCOMPRESSINTERFACE 331\n/* returns a function pointer to uncompress().\n** int (*uncompress)(unsigned char *dest, unsigned long *destLen, const unsigned char *source, unsigned long sourceLen);\n** right out of zlib, useful for decompressing zlibbed data.\n** if you pass the parm of 0x10100000, it will return a wa_inflate_struct * to an inflate API.\n*/\n\ntypedef struct {\n  int (*inflateReset)(void *strm);\n  int (*inflateInit_)(void *strm,const char *version, int stream_size);\n  int (*inflate)(void *strm, int flush);\n  int (*inflateEnd)(void *strm);\n  unsigned long (*crc32)(unsigned long crc, const unsigned  char *buf, unsigned int len);\n} wa_inflate_struct;\n\n\n#define IPC_ADD_PREFS_DLG 332\n#define IPC_REMOVE_PREFS_DLG 333\n/* (requires Winamp 2.9+)\n** to use, allocate a prefsDlgRec structure (either on the heap or some global\n** data, but NOT on the stack), initialze the members:\n** hInst to the DLL instance where the resource is located\n** dlgID to the ID of the dialog,\n** proc to the window procedure for the dialog\n** name to the name of the prefs page in the prefs.\n** where to 0 (eventually we may add more options)\n** then, SendMessage(hwnd_winamp,WM_WA_IPC,&prefsRec,IPC_ADD_PREFS_DLG);\n**\n** you can also IPC_REMOVE_PREFS_DLG with the address of the same prefsRec,\n** but you shouldn't really ever have to.\n**\n*/\n#define IPC_OPENPREFSTOPAGE 380 // pass an id of a builtin page, or a &prefsDlgRec of prefs page to open\n\n/*typedef struct _prefsDlgRec {\n  HINSTANCE hInst;\n  int dlgID;\n  void *proc;\n\n  char *name;\n  int where; // 0 for options, 1 for plugins, 2 for skins, 3 for bookmarks, 4 for prefs\n\n\n  int _id;\n  struct _prefsDlgRec *next;\n} prefsDlgRec;*/\n\n\n#define IPC_GETINIFILE 334 // returns a pointer to winamp.ini\n#define IPC_GETINIDIRECTORY 335 // returns a pointer to the directory to put config files in (if you dont want to use winamp.ini)\n\n#define IPC_SPAWNBUTTONPOPUP 361 // param =\n// 0 = eject\n// 1 = previous\n// 2 = next\n// 3 = pause\n// 4 = play\n// 5 = stop\n\n#define IPC_OPENURLBOX 360 // pass a HWND to a parent, returns a HGLOBAL that needs to be freed with GlobalFree(), if successful\n#define IPC_OPENFILEBOX 362 // pass a HWND to a parent\n#define IPC_OPENDIRBOX 363 // pass a HWND to a parent\n\n// pass an HWND to a parent. call this if you take over the whole UI so that the dialogs are not appearing on the\n// bottom right of the screen since the main winamp window is at 3000x3000, call again with NULL to reset\n#define IPC_SETDIALOGBOXPARENT 364\n\n\n\n// pass 0 for a copy of the skin HBITMAP\n// pass 1 for name of font to use for playlist editor likeness\n// pass 2 for font charset\n// pass 3 for font size\n#define IPC_GET_GENSKINBITMAP 503\n\n\n#define IPC_GET_EMBEDIF 505 // pass an embedWindowState\n// returns an HWND embedWindow(embedWindowState *); if the data is NULL, otherwise returns the HWND directly\n/*typedef struct\n{\n  HWND me; //hwnd of the window\n\n  int flags;\n\n  RECT r;\n\n  void *user_ptr; // for application use\n\n  int extra_data[64]; // for internal winamp use\n} embedWindowState;*/\n\n#define EMBED_FLAGS_NORESIZE 1 // set this bit in embedWindowState.flags to keep window from being resizable\n#define EMBED_FLAGS_NOTRANSPARENCY 2 // set this bit in embedWindowState.flags to make gen_ff turn transparency off for this wnd\n\n\n#define IPC_EMBED_ENUM 532\n/*typedef struct embedEnumStruct\n{\n  int (*enumProc)(embedWindowState *ws, struct embedEnumStruct *param); // return 1 to abort\n  int user_data; // or more :)\n} embedEnumStruct;*/\n  // pass\n\n#define IPC_EMBED_ISVALID 533\n\n#define IPC_CONVERTFILE 506\n/* (requires Winamp 2.92+)\n** Converts a given file to a different format (PCM, MP3, etc...)\n** To use, pass a pointer to a waFileConvertStruct struct\n** This struct can be either on the heap or some global\n** data, but NOT on the stack. At least, until the conversion is done.\n**\n** eg: SendMessage(hwnd_winamp,WM_WA_IPC,&myConvertStruct,IPC_CONVERTFILE);\n**\n** Return value:\n** 0: Can't start the conversion. Look at myConvertStruct->error for details.\n** 1: Conversion started. Status messages will be sent to the specified callbackhwnd.\n**    Be sure to call IPC_CONVERTFILE_END when your callback window receives the\n**    IPC_CB_CONVERT_DONE message.\n*/\n/*typedef struct\n{\n  char *sourcefile;  // \"c:\\\\source.mp3\"\n  char *destfile;    // \"c:\\\\dest.pcm\"\n  int destformat[8]; // like 'PCM ',srate,nch,bps\n  HWND callbackhwnd; // window that will receive the IPC_CB_CONVERT notification messages\n\n  //filled in by winamp.exe\n  char *error;        //if IPC_CONVERTFILE returns 0, the reason will be here\n\n  int bytes_done;     //you can look at both of these values for speed statistics\n  int bytes_total;\n  int bytes_out;\n\n  int killswitch;     // don't set it manually, use IPC_CONVERTFILE_END\n  int extra_data[64]; // for internal winamp use\n} convertFileStruct;*/\n\n#define IPC_CONVERTFILE_END 507\n/* (requires Winamp 2.92+)\n** Stop/ends a convert process started from IPC_CONVERTFILE\n** You need to call this when you receive the IPC_CB_CONVERTDONE message or when you\n** want to abort a conversion process\n**\n** eg: SendMessage(hwnd_winamp,WM_WA_IPC,&myConvertStruct,IPC_CONVERTFILE_END);\n**\n** No return value\n*/\n\n/*typedef struct {\n  HWND hwndParent;\n  int format;\n\n  //filled in by winamp.exe\n  HWND hwndConfig;\n  int extra_data[8];\n} convertConfigStruct;\n#define IPC_CONVERT_CONFIG 508\n#define IPC_CONVERT_CONFIG_END 509\n\ntypedef struct\n{\n  void (*enumProc)(int user_data, const char *desc, int fourcc);\n  int user_data;\n} converterEnumFmtStruct;*/\n#define IPC_CONVERT_CONFIG_ENUMFMTS 510\n/* (requires Winamp 2.92+)\n*/\n\n\n/*typedef struct\n{\n  char cdletter;\n  char *playlist_file;\n  HWND callback_hwnd;\n\n  //filled in by winamp.exe\n  char *error;\n} burnCDStruct;*/\n#define IPC_BURN_CD 511\n/* (requires Winamp 5.0+)\n*/\n\n/*typedef struct\n{\n  convertFileStruct *cfs;\n  int priority;\n} convertSetPriority;*/\n#define IPC_CONVERT_SET_PRIORITY 512\n\n/*typedef struct\n{\n  char *filename;\n  char *title; // 2048 bytes\n  int length;\n  int force_useformatting; // can set this to 1 if you want to force a url to use title formatting shit\n} waHookTitleStruct;*/\n// return TRUE if you hook this\n#define IPC_HOOK_TITLES 850\n\n#define IPC_GETSADATAFUNC 800\n// 0: returns a char *export_sa_get() that returns 150 bytes of data\n// 1: returns a export_sa_setreq(int want);\n\n#define IPC_ISMAINWNDVISIBLE 900\n\n\n#define IPC_SETPLEDITCOLORS 920\n/*typedef struct\n{\n  int numElems;\n  int *elems;\n  HBITMAP bm; // set if you want to override\n} waSetPlColorsStruct;*/\n\n\n// the following IPC use waSpawnMenuParms as parameter\n#define IPC_SPAWNEQPRESETMENU 933\n#define IPC_SPAWNFILEMENU 934 //menubar\n#define IPC_SPAWNOPTIONSMENU 935 //menubar\n#define IPC_SPAWNWINDOWSMENU 936 //menubar\n#define IPC_SPAWNHELPMENU 937 //menubar\n#define IPC_SPAWNPLAYMENU 938 //menubar\n#define IPC_SPAWNPEFILEMENU 939 //menubar\n#define IPC_SPAWNPEPLAYLISTMENU 940 //menubar\n#define IPC_SPAWNPESORTMENU 941 //menubar\n#define IPC_SPAWNPEHELPMENU 942 //menubar\n#define IPC_SPAWNMLFILEMENU 943 //menubar\n#define IPC_SPAWNMLVIEWMENU 944 //menubar\n#define IPC_SPAWNMLHELPMENU 945 //menubar\n#define IPC_SPAWNPELISTOFPLAYLISTS 946\n\n/*typedef struct\n{\n  HWND wnd;\n  int xpos; // in screen coordinates\n  int ypos;\n} waSpawnMenuParms;\n\n// waSpawnMenuParms2 is used by the menubar submenus\ntypedef struct\n{\n  HWND wnd;\n  int xpos; // in screen coordinates\n  int ypos;\n  int width;\n  int height;\n} waSpawnMenuParms2;*/\n\n\n// system tray sends this (you might want to simulate it)\n#define WM_WA_SYSTRAY WM_USER+1\n\n// input plugins send this when they are done playing back\n#define WM_WA_MPEG_EOF WM_USER+2\n\n\n\n//// video stuff\n\n#define IPC_IS_PLAYING_VIDEO 501 // returns >1 if playing, 0 if not, 1 if old version (so who knows):)\n#define IPC_GET_IVIDEOOUTPUT 500 // see below for IVideoOutput interface\n#define VIDEO_MAKETYPE(A,B,C,D) ((A) | ((B)<<8) | ((C)<<16) | ((D)<<24))\n#define VIDUSER_SET_INFOSTRING 0x1000\n#define VIDUSER_GET_VIDEOHWND  0x1001\n#define VIDUSER_SET_VFLIP      0x1002\n\n#ifndef NO_IVIDEO_DECLARE\n#ifdef __cplusplus\n\nclass VideoOutput;\nclass SubsItem;\n\ntypedef\tstruct {\n\tunsigned char*\tbaseAddr;\n\tlong\t\t\trowBytes;\n} YV12_PLANE;\n\ntypedef\tstruct {\n\tYV12_PLANE\ty;\n\tYV12_PLANE\tu;\n\tYV12_PLANE\tv;\n} YV12_PLANES;\n\nclass IVideoOutput\n{\n  public:\n    virtual ~IVideoOutput() { }\n    virtual int open(int w, int h, int vflip, double aspectratio, unsigned int fmt)=0;\n    virtual void setcallback(LRESULT (*msgcallback)(void *token, HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam), void *token) { }\n    virtual void close()=0;\n    virtual void draw(void *frame)=0;\n    virtual void drawSubtitle(SubsItem *item) { }\n    virtual void showStatusMsg(const char *text) { }\n    virtual int get_latency() { return 0; }\n    virtual void notifyBufferState(int bufferstate) { } /* 0-255*/\n\n    virtual int extended(int param1, int param2, int param3) { return 0; } // Dispatchable, eat this!\n};\n#endif //cplusplus\n#endif//NO_IVIDEO_DECLARE\n\n// these messages are callbacks that you can grab by subclassing the winamp window\n\n// wParam =\n#define IPC_CB_WND_EQ 0 // use one of these for the param\n#define IPC_CB_WND_PE 1\n#define IPC_CB_WND_MB 2\n#define IPC_CB_WND_VIDEO 3\n#define IPC_CB_WND_MAIN 4\n\n#define IPC_CB_ONSHOWWND 600\n#define IPC_CB_ONHIDEWND 601\n\n#define IPC_CB_GETTOOLTIP 602\n\n#define IPC_CB_MISC 603\n    #define IPC_CB_MISC_TITLE 0\n    #define IPC_CB_MISC_VOLUME 1 // volume/pan\n    #define IPC_CB_MISC_STATUS 2\n    #define IPC_CB_MISC_EQ 3\n    #define IPC_CB_MISC_INFO 4\n    #define IPC_CB_MISC_VIDEOINFO 5\n\n#define IPC_CB_CONVERT_STATUS 604 // param value goes from 0 to 100 (percent)\n#define IPC_CB_CONVERT_DONE   605\n\n#define IPC_ADJUST_FFWINDOWSMENUPOS 606\n/* (requires Winamp 2.9+)\n** int newpos=SendMessage(hwnd_winamp,WM_WA_IPC,(WPARAM)adjust_offset,IPC_ADJUST_FFWINDOWSMENUPOS);\n** moves where winamp expects the freeform windows in the menubar windows main menu. Useful if you wish to insert a\n** menu item above extra freeform windows.\n*/\n\n#define IPC_ISDOUBLESIZE 608\n\n#define IPC_ADJUST_FFOPTIONSMENUPOS 609\n/* (requires Winamp 2.9+)\n** int newpos=SendMessage(hwnd_winamp,WM_WA_IPC,(WPARAM)adjust_offset,IPC_ADJUST_FFOPTIONSMENUPOS);\n** moves where winamp expects the freeform preferences item in the menubar windows main menu. Useful if you wish to insert a\n** menu item above preferences item.\n*/\n\n#define IPC_GETTIMEDISPLAYMODE 610 // returns 0 if displaying elapsed time or 1 if displaying remaining time\n\n#define IPC_SETVISWND 611 // param is hwnd, setting this allows you to receive ID_VIS_NEXT/PREVOUS/RANDOM/FS wm_commands\n#define ID_VIS_NEXT                     40382\n#define ID_VIS_PREV                     40383\n#define ID_VIS_RANDOM                   40384\n#define ID_VIS_FS                       40389\n#define ID_VIS_CFG                      40390\n#define ID_VIS_MENU                     40391\n\n#define IPC_GETVISWND 612 // returns the vis cmd handler hwnd\n#define IPC_ISVISRUNNING 613\n#define IPC_CB_VISRANDOM 628 // param is status of random\n\n#define IPC_SETIDEALVIDEOSIZE 614 // sent by winamp to winamp, trap it if you need it. width=HIWORD(param), height=LOWORD(param)\n\n#define IPC_GETSTOPONVIDEOCLOSE 615\n#define IPC_SETSTOPONVIDEOCLOSE 616\n\n/*typedef struct {\n  HWND hwnd;\n  int uMsg;\n  int wParam;\n  int lParam;\n} transAccelStruct;*/\n\n#define IPC_TRANSLATEACCELERATOR 617\n\ntypedef struct {\n  int cmd;\n  int x;\n  int y;\n  int align;\n} windowCommand; // send this as param to an IPC_PLCMD, IPC_MBCMD, IPC_VIDCMD\n\n#define IPC_CB_ONTOGGLEAOT 618\n\n#define IPC_GETPREFSWND 619\n\n#define IPC_SET_PE_WIDTHHEIGHT 620 // data is a pointer to a POINT structure that holds width & height\n\n#define IPC_GETLANGUAGEPACKINSTANCE 621\n\n#define IPC_CB_PEINFOTEXT 622 // data is a string, ie: \"04:21/45:02\"\n\n#define IPC_CB_OUTPUTCHANGED 623 // output plugin was changed in config\n\n#define IPC_GETOUTPUTPLUGIN 625\n\n#define IPC_SETDRAWBORDERS 626\n#define IPC_DISABLESKINCURSORS 627\n#define IPC_CB_RESETFONT 629\n\n#define IPC_IS_FULLSCREEN 630 // returns 1 if video or vis is in fullscreen mode\n#define IPC_SET_VIS_FS_FLAG 631 // a vis should send this message with 1/as param to notify winamp that it has gone to or has come back from fullscreen mode\n\n#define IPC_SHOW_NOTIFICATION 632\n\n#define IPC_GETSKININFO 633\n\n// >>>>>>>>>>> Next is 634\n\n#define IPC_PLCMD  1000\n\n#define PLCMD_ADD  0\n#define PLCMD_REM  1\n#define PLCMD_SEL  2\n#define PLCMD_MISC 3\n#define PLCMD_LIST 4\n\n#define IPC_MBCMD  1001\n\n#define MBCMD_BACK    0\n#define MBCMD_FORWARD 1\n#define MBCMD_STOP    2\n#define MBCMD_RELOAD  3\n#define MBCMD_MISC  4\n\n#define IPC_VIDCMD 1002\n\n#define VIDCMD_FULLSCREEN 0\n#define VIDCMD_1X         1\n#define VIDCMD_2X         2\n#define VIDCMD_LIB        3\n#define VIDPOPUP_MISC     4\n\n#define IPC_MBURL       1003 //sets the URL\n#define IPC_MBGETCURURL 1004 //copies the current URL into wParam (have a 4096 buffer ready)\n#define IPC_MBGETDESC   1005 //copies the current URL description into wParam (have a 4096 buffer ready)\n#define IPC_MBCHECKLOCFILE 1006 //checks that the link file is up to date (otherwise updates it). wParam=parent HWND\n#define IPC_MBREFRESH   1007 //refreshes the \"now playing\" view in the library\n#define IPC_MBGETDEFURL 1008 //copies the default URL into wParam (have a 4096 buffer ready)\n\n#define IPC_STATS_LIBRARY_ITEMCNT 1300 // updates library count status\n\n// IPC 2000-3000 reserved for freeform messages, see gen_ff/ff_ipc.h\n#define IPC_FF_FIRST 2000\n#define IPC_FF_LAST  3000\n\n#define IPC_GETDROPTARGET 3001\n\n#define IPC_PLAYLIST_MODIFIED 3002 // sent to main wnd whenever the playlist is modified\n\n#define IPC_PLAYING_FILE 3003 // sent to main wnd with the file as parm whenever a file is played\n#define IPC_FILE_TAG_MAY_HAVE_UPDATED 3004 // sent to main wnd with the file as parm whenever a file tag might be updated\n\n\n#define IPC_ALLOW_PLAYTRACKING 3007\n// send nonzero to allow, zero to disallow\n\n#define IPC_HOOK_OKTOQUIT 3010 // return 0 to abort a quit, nonzero if quit is OK\n\n#define IPC_WRITECONFIG 3011 // pass 2 to write all, 1 to write playlist + common, 0 to write common+less common\n\n// pass a string to be the name to register, and returns a value > 65536, which is a unique value you can use\n// for custom WM_WA_IPC messages.\n#define IPC_REGISTER_WINAMP_IPCMESSAGE 65536\n\n/**************************************************************************/\n\n/*\n** Finally there are some WM_COMMAND messages that you can use to send\n** Winamp misc commands.\n**\n** To send these, use:\n**\n** SendMessage(hwnd_winamp, WM_COMMAND,command_name,0);\n*/\n\n#define WINAMP_OPTIONS_EQ               40036 // toggles the EQ window\n#define WINAMP_OPTIONS_PLEDIT           40040 // toggles the playlist window\n#define WINAMP_VOLUMEUP                 40058 // turns the volume up a little\n#define WINAMP_VOLUMEDOWN               40059 // turns the volume down a little\n#define WINAMP_FFWD5S                   40060 // fast forwards 5 seconds\n#define WINAMP_REW5S                    40061 // rewinds 5 seconds\n\n// the following are the five main control buttons, with optionally shift\n// or control pressed\n// (for the exact functions of each, just try it out)\n#define WINAMP_BUTTON1                  40044\n#define WINAMP_BUTTON2                  40045\n#define WINAMP_BUTTON3                  40046\n#define WINAMP_BUTTON4                  40047\n#define WINAMP_BUTTON5                  40048\n#define WINAMP_BUTTON1_SHIFT            40144\n#define WINAMP_BUTTON2_SHIFT            40145\n#define WINAMP_BUTTON3_SHIFT            40146\n#define WINAMP_BUTTON4_SHIFT            40147\n#define WINAMP_BUTTON5_SHIFT            40148\n#define WINAMP_BUTTON1_CTRL             40154\n#define WINAMP_BUTTON2_CTRL             40155\n#define WINAMP_BUTTON3_CTRL             40156\n#define WINAMP_BUTTON4_CTRL             40157\n#define WINAMP_BUTTON5_CTRL             40158\n\n#define WINAMP_FILE_PLAY                40029 // pops up the load file(s) box\n#define WINAMP_FILE_DIR                 40187 // pops up the load directory box\n#define WINAMP_OPTIONS_PREFS            40012 // pops up the preferences\n#define WINAMP_OPTIONS_AOT              40019 // toggles always on top\n#define WINAMP_HELP_ABOUT               40041 // pops up the about box :)\n\n#define ID_MAIN_PLAY_AUDIOCD1           40323 // starts playing the audio CD in the first CD reader\n#define ID_MAIN_PLAY_AUDIOCD2           40323 // plays the 2nd\n#define ID_MAIN_PLAY_AUDIOCD3           40323 // plays the 3nd\n#define ID_MAIN_PLAY_AUDIOCD4           40323 // plays the 4nd\n\n// IDs 42000 to 45000 are reserved for gen_ff\n// IDs from 45000 to 57000 are reserved for library\n\n#endif//_WA_IPC_H_\n\n/*\n** EOF.. Enjoy.\n*/"
  },
  {
    "path": "plugins/xsv/Xmd.h",
    "content": "/* $XFree86: xc/include/Xmd.h,v 3.16 2002/05/31 18:45:39 dawes Exp $ */\n/***********************************************************\n\nCopyright 1987, 1998  The Open Group\n\nPermission to use, copy, modify, distribute, and sell this software and its\ndocumentation for any purpose is hereby granted without fee, provided that\nthe above copyright notice appear in all copies and that both that\ncopyright notice and this permission notice appear in supporting\ndocumentation.\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\nOPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN\nAN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\nCONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\nExcept as contained in this notice, the name of The Open Group shall not be\nused in advertising or otherwise to promote the sale, use or other dealings\nin this Software without prior written authorization from The Open Group.\n\n\nCopyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts.\n\n                        All Rights Reserved\n\nPermission to use, copy, modify, and distribute this software and its \ndocumentation for any purpose and without fee is hereby granted, \nprovided that the above copyright notice appear in all copies and that\nboth that copyright notice and this permission notice appear in \nsupporting documentation, and that the name of Digital not be\nused in advertising or publicity pertaining to distribution of the\nsoftware without specific, written prior permission.  \n\nDIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING\nALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL\nDIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR\nANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,\nWHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,\nARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS\nSOFTWARE.\n\n******************************************************************/\n#ifndef XMD_H\n#define XMD_H 1\n/* $Xorg: Xmd.h,v 1.4 2001/02/09 02:03:22 xorgcvs Exp $ */\n/*\n *  Xmd.h: MACHINE DEPENDENT DECLARATIONS.\n */\n\n/*\n * Special per-machine configuration flags.\n */\n#ifdef CRAY\n#define WORD64\t\t\t\t/* 64-bit architecture */\n#endif\n#if defined(__alpha) || defined(__alpha__) || \\\n    defined(__ia64__) || defined(ia64) || \\\n    defined(__sparc64__) || \\\n    defined(__s390x__) || \\\n    (defined(__hppa__) && defined(__LP64__)) || \\\n    defined(__x86_64__) || defined(x86_64)\n#define LONG64\t\t\t\t/* 32/64-bit architecture */\n#endif\n#ifdef __sgi\n#if (_MIPS_SZLONG == 64)\n#define LONG64\n#endif\n#endif\n\n/*\n * Stuff to handle large architecture machines; the constants were generated\n * on a 32-bit machine and must coorespond to the protocol.\n */\n#ifdef WORD64\n#define MUSTCOPY\n#endif /* WORD64 */\n\n\n/*\n * Definition of macro used to set constants for size of network structures;\n * machines with preprocessors that can't handle all of the sz_ symbols\n * can define this macro to be sizeof(x) if and only if their compiler doesn't\n * pad out structures (esp. the xTextElt structure which contains only two \n * one-byte fields).  Network structures should always define sz_symbols.\n *\n * The sz_ prefix is used instead of something more descriptive so that the\n * symbols are no more than 32 characters long (which causes problems for some\n * compilers and preprocessors).\n *\n * The extra indirection in the __STDC__ case is to get macro arguments to\n * expand correctly before the concatenation, rather than afterward.\n */\n#if ((defined(__STDC__) || defined(__cplusplus) || defined(c_plusplus)) && !defined(UNIXCPP)) || defined(ANSICPP)\n#define _SIZEOF(x) sz_##x\n#define SIZEOF(x) _SIZEOF(x)\n#else\n#define SIZEOF(x) sz_/**/x\n#endif /* if ANSI C compiler else not */\n\n/*\n * Bitfield suffixes for the protocol structure elements, if you\n * need them.  Note that bitfields are not guarranteed to be signed\n * (or even unsigned) according to ANSI C.\n */\n#ifdef WORD64\ntypedef long INT64;\ntypedef unsigned long CARD64;\n#define B32 :32\n#define B16 :16\n#ifdef UNSIGNEDBITFIELDS\ntypedef unsigned int INT32;\ntypedef unsigned int INT16;\n#else\n#ifdef __STDC__\ntypedef signed int INT32;\ntypedef signed int INT16;\n#else\ntypedef int INT32;\ntypedef int INT16;\n#endif\n#endif\n#else\n#define B32\n#define B16\n#ifdef LONG64\ntypedef long INT64;\ntypedef int INT32;\n#else\ntypedef long INT32;\n#endif\ntypedef short INT16;\n#endif\n\n#if defined(__STDC__) || defined(sgi) || defined(AIXV3)\ntypedef signed char    INT8;\n#else\ntypedef char           INT8;\n#endif\n\n#ifdef LONG64\ntypedef unsigned long CARD64;\ntypedef unsigned int CARD32;\n#else\ntypedef unsigned long CARD32;\n#endif\ntypedef unsigned short CARD16;\ntypedef unsigned char  CARD8;\n\ntypedef CARD32\t\tBITS32;\ntypedef CARD16\t\tBITS16;\n\n#if !defined(I_NEED_OS2_H) && !defined(_WIN32)\ntypedef CARD8\t\tBYTE;\ntypedef CARD8\t\tBOOL;\n#else\n#define BYTE\tCARD8\n#define BOOL\tCARD8\n#endif\n\n/*\n * definitions for sign-extending bitfields on 64-bit architectures\n */\n#if defined(WORD64) && defined(UNSIGNEDBITFIELDS)\n#define cvtINT8toInt(val)   (((val) & 0x00000080) ? ((val) | 0xffffffffffffff00) : (val))\n#define cvtINT16toInt(val)  (((val) & 0x00008000) ? ((val) | 0xffffffffffff0000) : (val))\n#define cvtINT32toInt(val)  (((val) & 0x80000000) ? ((val) | 0xffffffff00000000) : (val))\n#define cvtINT8toShort(val)  cvtINT8toInt(val)\n#define cvtINT16toShort(val) cvtINT16toInt(val)\n#define cvtINT32toShort(val) cvtINT32toInt(val)\n#define cvtINT8toLong(val)  cvtINT8toInt(val)\n#define cvtINT16toLong(val) cvtINT16toInt(val)\n#define cvtINT32toLong(val) cvtINT32toInt(val)\n#else\n#define cvtINT8toInt(val) (val)\n#define cvtINT16toInt(val) (val)\n#define cvtINT32toInt(val) (val)\n#define cvtINT8toShort(val) (val)\n#define cvtINT16toShort(val) (val)\n#define cvtINT32toShort(val) (val)\n#define cvtINT8toLong(val) (val)\n#define cvtINT16toLong(val) (val)\n#define cvtINT32toLong(val) (val)\n#endif /* WORD64 and UNSIGNEDBITFIELDS */\n\n\n\n#ifdef MUSTCOPY\n/*\n * This macro must not cast or else pointers will get aligned and be wrong\n */\n#define NEXTPTR(p,t)  (((char *) p) + SIZEOF(t))\n#else /* else not MUSTCOPY, this is used for 32-bit machines */\n/*\n * this version should leave result of type (t *), but that should only be \n * used when not in MUSTCOPY\n */  \n#define NEXTPTR(p,t) (((t *)(p)) + 1)\n#endif /* MUSTCOPY - used machines whose C structs don't line up with proto */\n\n#endif /* XMD_H */\n"
  },
  {
    "path": "plugins/xsv/Xproto.h",
    "content": "//the only changes in this file for qux support are with header file locations.\n\n\n/*\n *\t$Xorg: Xproto.h,v 1.4 2001/02/09 02:03:23 xorgcvs Exp $\n */\n\n/* Definitions for the X window system used by server and c bindings */\n\n/*\n * This packet-construction scheme makes the following assumptions:\n *\n * 1. The compiler is able\n * to generate code which addresses one- and two-byte quantities.\n * In the worst case, this would be done with bit-fields.  If bit-fields\n * are used it may be necessary to reorder the request fields in this file,\n * depending on the order in which the machine assigns bit fields to\n * machine words.  There may also be a problem with sign extension,\n * as K+R specify that bitfields are always unsigned.\n *\n * 2. 2- and 4-byte fields in packet structures must be ordered by hand\n * such that they are naturally-aligned, so that no compiler will ever\n * insert padding bytes.\n *\n * 3. All packets are hand-padded to a multiple of 4 bytes, for\n * the same reason.\n */\n\n#ifndef XPROTO_H\n#define XPROTO_H\n\n/***********************************************************\n\nCopyright 1987, 1998  The Open Group\n\nPermission to use, copy, modify, distribute, and sell this software and its\ndocumentation for any purpose is hereby granted without fee, provided that\nthe above copyright notice appear in all copies and that both that\ncopyright notice and this permission notice appear in supporting\ndocumentation.\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\nOPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN\nAN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\nCONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\nExcept as contained in this notice, the name of The Open Group shall not be\nused in advertising or otherwise to promote the sale, use or other dealings\nin this Software without prior written authorization from The Open Group.\n\n\nCopyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts.\n\n                        All Rights Reserved\n\nPermission to use, copy, modify, and distribute this software and its \ndocumentation for any purpose and without fee is hereby granted, \nprovided that the above copyright notice appear in all copies and that\nboth that copyright notice and this permission notice appear in \nsupporting documentation, and that the name of Digital not be\nused in advertising or publicity pertaining to distribution of the\nsoftware without specific, written prior permission.  \n\nDIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING\nALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL\nDIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR\nANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,\nWHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,\nARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS\nSOFTWARE.\n\n******************************************************************/\n\n#include \"Xmd.h\"\n#include \"Xprotostr.h\"\n\n/*\n * Define constants for the sizes of the network packets.  The sz_ prefix is\n * used instead of something more descriptive so that the symbols are no more\n * than 32 characters in length (which causes problems for some compilers).\n */\n#define sz_xSegment 8\n#define sz_xPoint 4\n#define sz_xRectangle 8\n#define sz_xArc 12\n#define sz_xConnClientPrefix 12\n#define sz_xConnSetupPrefix 8\n#define sz_xConnSetup 32\n#define sz_xPixmapFormat 8\n#define sz_xDepth 8\n#define sz_xVisualType 24\n#define sz_xWindowRoot 40\n#define sz_xTimecoord 8\n#define sz_xHostEntry 4\n#define sz_xCharInfo 12\n#define sz_xFontProp 8\n#define sz_xTextElt 2\n#define sz_xColorItem 12\n#define sz_xrgb 8\n#define sz_xGenericReply 32\n#define sz_xGetWindowAttributesReply 44\n#define sz_xGetGeometryReply 32\n#define sz_xQueryTreeReply 32\n#define sz_xInternAtomReply 32\n#define sz_xGetAtomNameReply 32\n#define sz_xGetPropertyReply 32\n#define sz_xListPropertiesReply 32\n#define sz_xGetSelectionOwnerReply 32\n#define sz_xGrabPointerReply 32\n#define sz_xQueryPointerReply 32\n#define sz_xGetMotionEventsReply 32\n#define sz_xTranslateCoordsReply 32\n#define sz_xGetInputFocusReply 32\n#define sz_xQueryKeymapReply 40\n#define sz_xQueryFontReply 60\n#define sz_xQueryTextExtentsReply 32\n#define sz_xListFontsReply 32\n#define sz_xGetFontPathReply 32\n#define sz_xGetImageReply 32\n#define sz_xListInstalledColormapsReply 32\n#define sz_xAllocColorReply 32\n#define sz_xAllocNamedColorReply 32\n#define sz_xAllocColorCellsReply 32\n#define sz_xAllocColorPlanesReply 32\n#define sz_xQueryColorsReply 32\n#define sz_xLookupColorReply 32\n#define sz_xQueryBestSizeReply 32\n#define sz_xQueryExtensionReply 32\n#define sz_xListExtensionsReply 32\n#define sz_xSetMappingReply 32\n#define sz_xGetKeyboardControlReply 52\n#define sz_xGetPointerControlReply 32\n#define sz_xGetScreenSaverReply 32\n#define sz_xListHostsReply 32\n#define sz_xSetModifierMappingReply 32\n#define sz_xError 32\n#define sz_xEvent 32\n#define sz_xKeymapEvent 32\n#define sz_xReq 4\n#define sz_xResourceReq 8\n#define sz_xCreateWindowReq 32\n#define sz_xChangeWindowAttributesReq 12\n#define sz_xChangeSaveSetReq 8\n#define sz_xReparentWindowReq 16\n#define sz_xConfigureWindowReq 12\n#define sz_xCirculateWindowReq 8\n#define sz_xInternAtomReq 8\n#define sz_xChangePropertyReq 24\n#define sz_xDeletePropertyReq 12\n#define sz_xGetPropertyReq 24\n#define sz_xSetSelectionOwnerReq 16\n#define sz_xConvertSelectionReq 24\n#define sz_xSendEventReq 44\n#define sz_xGrabPointerReq 24\n#define sz_xGrabButtonReq 24\n#define sz_xUngrabButtonReq 12\n#define sz_xChangeActivePointerGrabReq 16\n#define sz_xGrabKeyboardReq 16\n#define sz_xGrabKeyReq 16\n#define sz_xUngrabKeyReq 12\n#define sz_xAllowEventsReq 8\n#define sz_xGetMotionEventsReq 16\n#define sz_xTranslateCoordsReq 16\n#define sz_xWarpPointerReq 24\n#define sz_xSetInputFocusReq 12\n#define sz_xOpenFontReq 12\n#define sz_xQueryTextExtentsReq 8\n#define sz_xListFontsReq 8\n#define sz_xSetFontPathReq 8\n#define sz_xCreatePixmapReq 16\n#define sz_xCreateGCReq 16\n#define sz_xChangeGCReq 12\n#define sz_xCopyGCReq 16\n#define sz_xSetDashesReq 12\n#define sz_xSetClipRectanglesReq 12\n#define sz_xCopyAreaReq 28\n#define sz_xCopyPlaneReq 32\n#define sz_xPolyPointReq 12\n#define sz_xPolySegmentReq 12\n#define sz_xFillPolyReq 16\n#define sz_xPutImageReq 24\n#define sz_xGetImageReq 20\n#define sz_xPolyTextReq 16\n#define sz_xImageTextReq 16\n#define sz_xCreateColormapReq 16\n#define sz_xCopyColormapAndFreeReq 12\n#define sz_xAllocColorReq 16\n#define sz_xAllocNamedColorReq 12\n#define sz_xAllocColorCellsReq 12\n#define sz_xAllocColorPlanesReq 16\n#define sz_xFreeColorsReq 12\n#define sz_xStoreColorsReq 8\n#define sz_xStoreNamedColorReq 16\n#define sz_xQueryColorsReq 8\n#define sz_xLookupColorReq 12\n#define sz_xCreateCursorReq 32\n#define sz_xCreateGlyphCursorReq 32\n#define sz_xRecolorCursorReq 20\n#define sz_xQueryBestSizeReq 12\n#define sz_xQueryExtensionReq 8\n#define sz_xChangeKeyboardControlReq 8\n#define sz_xBellReq 4\n#define sz_xChangePointerControlReq 12\n#define sz_xSetScreenSaverReq 12\n#define sz_xChangeHostsReq 8\n#define sz_xListHostsReq 4\n#define sz_xChangeModeReq 4\n#define sz_xRotatePropertiesReq 12\n#define sz_xReply 32\n#define sz_xGrabKeyboardReply 32\n#define sz_xListFontsWithInfoReply 60\n#define sz_xSetPointerMappingReply 32\n#define sz_xGetKeyboardMappingReply 32\n#define sz_xGetPointerMappingReply 32\n#define sz_xGetModifierMappingReply 32\n#define sz_xListFontsWithInfoReq 8\n#define sz_xPolyLineReq 12\n#define sz_xPolyArcReq 12\n#define sz_xPolyRectangleReq 12\n#define sz_xPolyFillRectangleReq 12\n#define sz_xPolyFillArcReq 12\n#define sz_xPolyText8Req 16\n#define sz_xPolyText16Req 16\n#define sz_xImageText8Req 16\n#define sz_xImageText16Req 16\n#define sz_xSetPointerMappingReq 4\n#define sz_xForceScreenSaverReq 4\n#define sz_xSetCloseDownModeReq 4\n#define sz_xClearAreaReq 16\n#define sz_xSetAccessControlReq 4\n#define sz_xGetKeyboardMappingReq 8\n#define sz_xSetModifierMappingReq 4\n#define sz_xPropIconSize 24\n#define sz_xChangeKeyboardMappingReq 8\n\n\n/* For the purpose of the structure definitions in this file,\nwe must redefine the following types in terms of Xmd.h's types, which may\ninclude bit fields.  All of these are #undef'd at the end of this file,\nrestoring the definitions in X.h.  */\n\n#define Window CARD32\n#define Drawable CARD32\n#define Font CARD32\n#define Pixmap CARD32\n#define Cursor CARD32\n#define Colormap CARD32\n#define GContext CARD32\n#define Atom CARD32\n#define VisualID CARD32\n#define Time CARD32\n#define KeyCode CARD8\n#define KeySym CARD32\n\n#define X_TCP_PORT 6000     /* add display number */\n\n#define xTrue        1\n#define xFalse       0\n\n\ntypedef CARD16 KeyButMask;\n\n/***************** \n   connection setup structure.  This is followed by\n   numRoots xWindowRoot structs.\n*****************/\n\ntypedef struct {\n    CARD8\tbyteOrder;\n    BYTE\tpad;\n    CARD16\tmajorVersion B16, minorVersion B16;\n    CARD16\tnbytesAuthProto B16;\t/* Authorization protocol */\n    CARD16\tnbytesAuthString B16;\t/* Authorization string */\n    CARD16\tpad2 B16;\n} xConnClientPrefix;\n\ntypedef struct {\n    CARD8          success;\n    BYTE           lengthReason; /*num bytes in string following if failure */\n    CARD16         majorVersion B16, \n                   minorVersion B16;\n    CARD16         length B16;  /* 1/4 additional bytes in setup info */\n} xConnSetupPrefix;\n\n\ntypedef struct {\n    CARD32         release B32;\n    CARD32         ridBase B32, \n                   ridMask B32;\n    CARD32         motionBufferSize B32;\n    CARD16         nbytesVendor B16;  /* number of bytes in vendor string */\n    CARD16         maxRequestSize B16;\n    CARD8          numRoots;          /* number of roots structs to follow */\n    CARD8          numFormats;        /* number of pixmap formats */\n    CARD8          imageByteOrder;        /* LSBFirst, MSBFirst */\n    CARD8          bitmapBitOrder;        /* LeastSignificant, MostSign...*/\n    CARD8          bitmapScanlineUnit,     /* 8, 16, 32 */\n                   bitmapScanlinePad;     /* 8, 16, 32 */\n    KeyCode\t   minKeyCode, maxKeyCode;\n    CARD32\t   pad2 B32;\n} xConnSetup;\n\ntypedef struct {\n    CARD8          depth;\n    CARD8          bitsPerPixel;\n    CARD8          scanLinePad;\n    CARD8          pad1;\n    CARD32\t   pad2 B32;\n} xPixmapFormat;\n\n/* window root */\n\ntypedef struct {\n    CARD8 \tdepth;\n    CARD8 \tpad1;\n    CARD16\tnVisuals B16;  /* number of xVisualType structures following */\n    CARD32\tpad2 B32;\n    } xDepth;\n\ntypedef struct {\n    VisualID visualID B32;\n#if defined(__cplusplus) || defined(c_plusplus)\n    CARD8 c_class;\n#else\n    CARD8 class;\n#endif\n    CARD8 bitsPerRGB;\n    CARD16 colormapEntries B16;\n    CARD32 redMask B32, greenMask B32, blueMask B32;\n    CARD32 pad B32;\n    } xVisualType;\n\ntypedef struct {\n    Window         windowId B32;\n    Colormap       defaultColormap B32;\n    CARD32         whitePixel B32, blackPixel B32;\n    CARD32         currentInputMask B32;   \n    CARD16         pixWidth B16, pixHeight B16;\n    CARD16         mmWidth B16, mmHeight B16;\n    CARD16         minInstalledMaps B16, maxInstalledMaps B16;\n    VisualID       rootVisualID B32;\n    CARD8          backingStore;\n    BOOL           saveUnders;\n    CARD8          rootDepth;\n    CARD8          nDepths;  /* number of xDepth structures following */\n} xWindowRoot;\n\f\n\n/*****************************************************************\n * Structure Defns\n *   Structures needed for replies \n *****************************************************************/\n\n/* Used in GetMotionEvents */\n\ntypedef struct {\n    CARD32 time B32;\n    INT16 x B16, y B16;\n} xTimecoord;\n\ntypedef struct {\n    CARD8 family;\n    BYTE pad;\n    CARD16 length B16;\n} xHostEntry;\n\ntypedef struct {\n    INT16 leftSideBearing B16,\n\t  rightSideBearing B16,\n\t  characterWidth B16,\n\t  ascent B16,\n\t  descent B16;\n    CARD16 attributes B16;\n} xCharInfo;\n\ntypedef struct {\n    Atom name B32;\n    CARD32 value B32;\n} xFontProp;\n\n/*\n * non-aligned big-endian font ID follows this struct\n */\ntypedef struct {           /* followed by string */\n    CARD8 len;\t/* number of *characters* in string, or FontChange (255)\n\t\t   for font change, or 0 if just delta given */\n    INT8 delta;\n} xTextElt;\n\n\ntypedef struct {        \n    CARD32 pixel B32;\n    CARD16 red B16, green B16, blue B16;\n    CARD8 flags;  /* DoRed, DoGreen, DoBlue booleans */\n    CARD8 pad;\n} xColorItem;\n\n\ntypedef struct {\n    CARD16 red B16, green B16, blue B16, pad B16;\n} xrgb;\n\ntypedef CARD8 KEYCODE;\n\f\n\n/*****************\n * XRep:\n *    meant to be 32 byte quantity \n *****************/\n\n/* GenericReply is the common format of all replies.  The \"data\" items\n   are specific to each individual reply type. */\n\ntypedef struct {\t\n    BYTE type;              /* X_Reply */\n    BYTE data1;             /* depends on reply type */\n    CARD16 sequenceNumber B16;  /* of last request received by server */\n    CARD32 length B32;      /* 4 byte quantities beyond size of GenericReply */\n    CARD32 data00 B32;\n    CARD32 data01 B32;\n    CARD32 data02 B32;\n    CARD32 data03 B32;\n    CARD32 data04 B32;\n    CARD32 data05 B32;\n    } xGenericReply;\n\n/* Individual reply formats. */\n\ntypedef struct {\n    BYTE type;  /* X_Reply */\n    CARD8 backingStore;\n    CARD16 sequenceNumber B16;\n    CARD32 length B32;\t/* NOT 0; this is an extra-large reply */\n    VisualID visualID B32;\n#if defined(__cplusplus) || defined(c_plusplus)\n    CARD16 c_class B16;\n#else\n    CARD16 class B16;\n#endif\n    CARD8 bitGravity;\n    CARD8 winGravity;\n    CARD32 backingBitPlanes B32;\n    CARD32 backingPixel B32;\n    BOOL saveUnder;\n    BOOL mapInstalled;\n    CARD8 mapState;\n    BOOL override;\n    Colormap colormap B32;\n    CARD32 allEventMasks B32;\n    CARD32 yourEventMask B32;\n    CARD16 doNotPropagateMask B16;\n    CARD16 pad B16;\n    } xGetWindowAttributesReply;\n\ntypedef struct {\n    BYTE type;   /* X_Reply */\n    CARD8 depth;\n    CARD16 sequenceNumber B16;\n    CARD32 length B32;  /* 0 */\n    Window root B32;\n    INT16 x B16, y B16;\n    CARD16 width B16, height B16;\n    CARD16 borderWidth B16;\n    CARD16 pad1 B16;\n    CARD32 pad2 B32;\n    CARD32 pad3 B32;\n    } xGetGeometryReply;\n\ntypedef struct {\n    BYTE type;  /* X_Reply */\n    BYTE pad1;\n    CARD16 sequenceNumber B16;\n    CARD32 length B32;\n    Window root B32, parent B32;\n    CARD16 nChildren B16;\n    CARD16 pad2 B16;\n    CARD32 pad3 B32;\n    CARD32 pad4 B32;\n    CARD32 pad5 B32;\n    } xQueryTreeReply;\n\ntypedef struct {\n    BYTE type;  /* X_Reply */\n    BYTE pad1;\n    CARD16 sequenceNumber B16;\n    CARD32 length B32; /* 0 */\n    Atom atom B32;\n    CARD32 pad2 B32;\n    CARD32 pad3 B32;\n    CARD32 pad4 B32;\n    CARD32 pad5 B32;\n    CARD32 pad6 B32;\n    } xInternAtomReply;\n\ntypedef struct {\n    BYTE type;  /* X_Reply */\n    BYTE pad1;\n    CARD16 sequenceNumber B16;\n    CARD32 length B32;  /* of additional bytes */\n    CARD16 nameLength B16;  /* # of characters in name */\n    CARD16 pad2 B16;\n    CARD32 pad3 B32;\n    CARD32 pad4 B32;\n    CARD32 pad5 B32;\n    CARD32 pad6 B32;\n    CARD32 pad7 B32;\n    } xGetAtomNameReply;\n\ntypedef struct {\n    BYTE type;  /* X_Reply */\n    CARD8 format;\n    CARD16 sequenceNumber B16;\n    CARD32 length B32; /* of additional bytes */\n    Atom propertyType B32;\n    CARD32 bytesAfter B32;\n    CARD32 nItems B32; /* # of 8, 16, or 32-bit entities in reply */\n    CARD32 pad1 B32;\n    CARD32 pad2 B32;\n    CARD32 pad3 B32;\n    } xGetPropertyReply;\n\ntypedef struct {\n    BYTE type;  /* X_Reply */\n    BYTE pad1;\n    CARD16 sequenceNumber B16;\n    CARD32 length B32;\n    CARD16 nProperties B16;\n    CARD16 pad2 B16;\n    CARD32 pad3 B32;\n    CARD32 pad4 B32;\n    CARD32 pad5 B32;\n    CARD32 pad6 B32;\n    CARD32 pad7 B32;\n    } xListPropertiesReply;\n\ntypedef struct {\n    BYTE type;  /* X_Reply */\n    BYTE pad1;\n    CARD16 sequenceNumber B16;\n    CARD32 length B32;  /* 0 */\n    Window owner B32;\n    CARD32 pad2 B32;\n    CARD32 pad3 B32;\n    CARD32 pad4 B32;\n    CARD32 pad5 B32;\n    CARD32 pad6 B32;\n    } xGetSelectionOwnerReply;\n\ntypedef struct {\n    BYTE type;  /* X_Reply */\n    BYTE status;\n    CARD16 sequenceNumber B16;\n    CARD32 length B32;  /* 0 */\n    CARD32 pad1 B32;\n    CARD32 pad2 B32;\n    CARD32 pad3 B32;\n    CARD32 pad4 B32;\n    CARD32 pad5 B32;\n    CARD32 pad6 B32;\n    } xGrabPointerReply;\n\ntypedef xGrabPointerReply xGrabKeyboardReply;\n\ntypedef struct {\n    BYTE type;  /* X_Reply */\n    BOOL sameScreen;\n    CARD16 sequenceNumber B16;\n    CARD32 length B32;  /* 0 */\n    Window root B32, child B32;\n    INT16 rootX B16, rootY B16, winX B16, winY B16;\n    CARD16 mask B16;\n    CARD16 pad1 B16;\n    CARD32 pad B32;\n    } xQueryPointerReply;\n\ntypedef struct {\n    BYTE type;  /* X_Reply */\n    BYTE pad1;\n    CARD16 sequenceNumber B16;\n    CARD32 length B32;\n    CARD32 nEvents B32;\n    CARD32 pad2 B32;\n    CARD32 pad3 B32;\n    CARD32 pad4 B32;\n    CARD32 pad5 B32;\n    CARD32 pad6 B32;\n    } xGetMotionEventsReply;\n\ntypedef struct {\n    BYTE type;  /* X_Reply */\n    BOOL sameScreen;\n    CARD16 sequenceNumber B16;\n    CARD32 length B32; /* 0 */\n    Window child B32;\n    INT16 dstX B16, dstY B16;\n    CARD32 pad2 B32;\n    CARD32 pad3 B32;\n    CARD32 pad4 B32;\n    CARD32 pad5 B32;\n    } xTranslateCoordsReply;\n\ntypedef struct {\n    BYTE type;  /* X_Reply */\n    CARD8 revertTo;\n    CARD16 sequenceNumber B16;\n    CARD32 length B32;  /* 0 */\n    Window focus B32;\n    CARD32 pad1 B32;\n    CARD32 pad2 B32;\n    CARD32 pad3 B32;\n    CARD32 pad4 B32;\n    CARD32 pad5 B32;\n    } xGetInputFocusReply;\n\ntypedef struct {\n    BYTE type;  /* X_Reply */\n    BYTE pad1;\n    CARD16 sequenceNumber B16;\n    CARD32 length B32;  /* 2, NOT 0; this is an extra-large reply */\n    BYTE map[32];\n    } xQueryKeymapReply;\n\n/* Warning: this MUST match (up to component renaming) xListFontsWithInfoReply */\ntypedef struct _xQueryFontReply {\n    BYTE type;  /* X_Reply */\n    BYTE pad1;\n    CARD16 sequenceNumber B16;\n    CARD32 length B32;  /* definitely > 0, even if \"nCharInfos\" is 0 */\n    xCharInfo minBounds; \n#ifndef WORD64\n    CARD32 walign1 B32;\n#endif\n    xCharInfo maxBounds; \n#ifndef WORD64\n    CARD32 walign2 B32;\n#endif\n    CARD16 minCharOrByte2 B16, maxCharOrByte2 B16;\n    CARD16 defaultChar B16;\n    CARD16 nFontProps B16;  /* followed by this many xFontProp structures */\n    CARD8 drawDirection;\n    CARD8 minByte1, maxByte1;\n    BOOL allCharsExist;\n    INT16 fontAscent B16, fontDescent B16;\n    CARD32 nCharInfos B32; /* followed by this many xCharInfo structures */\n} xQueryFontReply;\n\ntypedef struct {\n    BYTE type;  /* X_Reply */\n    CARD8 drawDirection;\n    CARD16 sequenceNumber B16;\n    CARD32 length B32;  /* 0 */\n    INT16 fontAscent B16, fontDescent B16;\n    INT16 overallAscent B16, overallDescent B16;\n    INT32 overallWidth B32, overallLeft B32, overallRight B32;\n    CARD32 pad B32;\n    } xQueryTextExtentsReply;\n\ntypedef struct {\n    BYTE type;  /* X_Reply */\n    BYTE pad1;\n    CARD16 sequenceNumber B16;\n    CARD32 length B32;\n    CARD16 nFonts B16;\n    CARD16 pad2 B16;\n    CARD32 pad3 B32;\n    CARD32 pad4 B32;\n    CARD32 pad5 B32;\n    CARD32 pad6 B32;\n    CARD32 pad7 B32;\n    } xListFontsReply;\n\n/* Warning: this MUST match (up to component renaming) xQueryFontReply */\ntypedef struct {\n    BYTE type;  /* X_Reply */\n    CARD8 nameLength;  /* 0 indicates end-of-reply-sequence */\n    CARD16 sequenceNumber B16;\n    CARD32 length B32;  /* definitely > 0, even if \"nameLength\" is 0 */\n    xCharInfo minBounds; \n#ifndef WORD64\n    CARD32 walign1 B32;\n#endif\n    xCharInfo maxBounds; \n#ifndef WORD64\n    CARD32 walign2 B32;\n#endif\n    CARD16 minCharOrByte2 B16, maxCharOrByte2 B16;\n    CARD16 defaultChar B16;\n    CARD16 nFontProps B16;  /* followed by this many xFontProp structures */\n    CARD8 drawDirection;\n    CARD8 minByte1, maxByte1;\n    BOOL allCharsExist;\n    INT16 fontAscent B16, fontDescent B16;\n    CARD32 nReplies B32;   /* hint as to how many more replies might be coming */\n} xListFontsWithInfoReply;\n\ntypedef struct {\n    BYTE type;  /* X_Reply */\n    BYTE pad1;\n    CARD16 sequenceNumber B16;\n    CARD32 length B32;\n    CARD16 nPaths B16;\n    CARD16 pad2 B16;\n    CARD32 pad3 B32;\n    CARD32 pad4 B32;\n    CARD32 pad5 B32;\n    CARD32 pad6 B32;\n    CARD32 pad7 B32;\n    } xGetFontPathReply;\n\ntypedef struct {\n    BYTE type;  /* X_Reply */\n    CARD8 depth;\n    CARD16 sequenceNumber B16;\n    CARD32 length B32;\n    VisualID visual B32;\n    CARD32 pad3 B32;\n    CARD32 pad4 B32;\n    CARD32 pad5 B32;\n    CARD32 pad6 B32;\n    CARD32 pad7 B32;\n    } xGetImageReply;\n\ntypedef struct {\n    BYTE type;  /* X_Reply */\n    BYTE pad1;\n    CARD16 sequenceNumber B16;\n    CARD32 length B32;\n    CARD16 nColormaps B16;\n    CARD16 pad2 B16;\n    CARD32 pad3 B32;\n    CARD32 pad4 B32;\n    CARD32 pad5 B32;\n    CARD32 pad6 B32;\n    CARD32 pad7 B32;\n    } xListInstalledColormapsReply;\n\ntypedef struct {\n    BYTE type; /* X_Reply */\n    BYTE pad1;\n    CARD16 sequenceNumber B16;\n    CARD32 length B32;   /* 0 */\n    CARD16 red B16, green B16, blue B16;\n    CARD16 pad2 B16;\n    CARD32 pixel B32;\n    CARD32 pad3 B32;\n    CARD32 pad4 B32;\n    CARD32 pad5 B32;\n    } xAllocColorReply;\n\ntypedef struct {\n    BYTE type; /* X_Reply */\n    BYTE pad1;\n    CARD16 sequenceNumber B16;\n    CARD32 length B32;  /* 0 */\n    CARD32 pixel B32;\n    CARD16 exactRed B16, exactGreen B16, exactBlue B16;\n    CARD16 screenRed B16, screenGreen B16, screenBlue B16;\n    CARD32 pad2 B32;\n    CARD32 pad3 B32;\n    } xAllocNamedColorReply;\n\ntypedef struct {\n    BYTE type;  /* X_Reply */\n    BYTE pad1;\n    CARD16 sequenceNumber B16;\n    CARD32 length B32;\n    CARD16 nPixels B16, nMasks B16;\n    CARD32 pad3 B32;\n    CARD32 pad4 B32;\n    CARD32 pad5 B32;\n    CARD32 pad6 B32;\n    CARD32 pad7 B32;\n    } xAllocColorCellsReply;\n\ntypedef struct {\n    BYTE type; /* X_Reply */\n    BYTE pad1;\n    CARD16 sequenceNumber B16;\n    CARD32 length B32;\n    CARD16 nPixels B16;\n    CARD16 pad2 B16;\n    CARD32 redMask B32, greenMask B32, blueMask B32;\n    CARD32 pad3 B32;\n    CARD32 pad4 B32;\n    } xAllocColorPlanesReply;\n\ntypedef struct {\n    BYTE type; /* X_Reply */\n    BYTE pad1;\n    CARD16 sequenceNumber B16;\n    CARD32 length B32;\n    CARD16 nColors B16;\n    CARD16 pad2 B16;\n    CARD32 pad3 B32;\n    CARD32 pad4 B32;\n    CARD32 pad5 B32;\n    CARD32 pad6 B32;\n    CARD32 pad7 B32;\n    } xQueryColorsReply;\n\ntypedef struct {\n    BYTE type;  /* X_Reply */\n    BYTE pad1;\n    CARD16 sequenceNumber B16;\n    CARD32 length B32;  /* 0 */\n    CARD16 exactRed B16, exactGreen B16, exactBlue B16;\n    CARD16 screenRed B16, screenGreen B16, screenBlue B16;\n    CARD32 pad3 B32;\n    CARD32 pad4 B32;\n    CARD32 pad5 B32;\n    } xLookupColorReply;\n\ntypedef struct {\n    BYTE type;  /* X_Reply */\n    BYTE pad1;\n    CARD16 sequenceNumber B16;\n    CARD32 length B32;  /* 0 */\n    CARD16 width B16, height B16;\n    CARD32 pad3 B32;\n    CARD32 pad4 B32;\n    CARD32 pad5 B32;\n    CARD32 pad6 B32;\n    CARD32 pad7 B32;\n    } xQueryBestSizeReply;\n\ntypedef struct {\n    BYTE type;  /* X_Reply */\n    BYTE pad1;\n    CARD16 sequenceNumber B16;\n    CARD32 length B32; /* 0 */\n    BOOL  present;\n    CARD8 major_opcode;\n    CARD8 first_event;\n    CARD8 first_error;\n    CARD32 pad3 B32;\n    CARD32 pad4 B32;\n    CARD32 pad5 B32;\n    CARD32 pad6 B32;\n    CARD32 pad7 B32;\n    } xQueryExtensionReply;\n\ntypedef struct {\n    BYTE type;  /* X_Reply */\n    CARD8 nExtensions;\n    CARD16 sequenceNumber B16;\n    CARD32 length B32;\n    CARD32 pad2 B32;\n    CARD32 pad3 B32;\n    CARD32 pad4 B32;\n    CARD32 pad5 B32;\n    CARD32 pad6 B32;\n    CARD32 pad7 B32;\n    } xListExtensionsReply;\n\n\ntypedef struct {\n    BYTE   type;  /* X_Reply */\n    CARD8  success;\n    CARD16 sequenceNumber B16;\n    CARD32 length B32;\n    CARD32 pad2 B32;\n    CARD32 pad3 B32;\n    CARD32 pad4 B32;\n    CARD32 pad5 B32;\n    CARD32 pad6 B32;\n    CARD32 pad7 B32;\n    } xSetMappingReply;\ntypedef xSetMappingReply xSetPointerMappingReply;\ntypedef xSetMappingReply xSetModifierMappingReply;\n\ntypedef struct {\n    BYTE type;  /* X_Reply */\n    CARD8 nElts;  /* how many elements does the map have */\n    CARD16 sequenceNumber B16;\n    CARD32 length B32;\n    CARD32 pad2 B32;\n    CARD32 pad3 B32;\n    CARD32 pad4 B32;\n    CARD32 pad5 B32;\n    CARD32 pad6 B32;\n    CARD32 pad7 B32;\n    } xGetPointerMappingReply;\n\ntypedef struct {\n    BYTE type;\n    CARD8 keySymsPerKeyCode;\n    CARD16 sequenceNumber B16;\n    CARD32 length B32;\n    CARD32 pad2 B32;\n    CARD32 pad3 B32;\n    CARD32 pad4 B32;\n    CARD32 pad5 B32;\n    CARD32 pad6 B32;\n    CARD32 pad7 B32;\n} xGetKeyboardMappingReply;    \n\ntypedef struct {\n    BYTE type;\n    CARD8 numKeyPerModifier;\n    CARD16 sequenceNumber B16;\n    CARD32 length B32;\n    CARD32 pad1 B32;\n    CARD32 pad2 B32;\n    CARD32 pad3 B32;\n    CARD32 pad4 B32;\n    CARD32 pad5 B32;\n    CARD32 pad6 B32;\n} xGetModifierMappingReply;\n\ntypedef struct {\n    BYTE type;  /* X_Reply */\n    BOOL globalAutoRepeat;\n    CARD16 sequenceNumber B16;\n    CARD32 length B32;  /* 5 */\n    CARD32 ledMask B32;\n    CARD8 keyClickPercent, bellPercent;\n    CARD16 bellPitch B16, bellDuration B16;\n    CARD16 pad B16;\n    BYTE map[32];  /* bit masks start here */\n    } xGetKeyboardControlReply;\n\ntypedef struct {\n    BYTE type;  /* X_Reply */\n    BYTE pad1;\n    CARD16 sequenceNumber B16;\n    CARD32 length B32;  /* 0 */\n    CARD16 accelNumerator B16, accelDenominator B16;\n    CARD16 threshold B16;\n    CARD16 pad2 B16;\n    CARD32 pad3 B32;\n    CARD32 pad4 B32;\n    CARD32 pad5 B32;\n    CARD32 pad6 B32;\n    } xGetPointerControlReply;\n\ntypedef struct {\n    BYTE type;  /* X_Reply */\n    BYTE pad1;\n    CARD16 sequenceNumber B16;\n    CARD32 length B32;  /* 0 */\n    CARD16 timeout B16, interval B16;\n    BOOL preferBlanking;\n    BOOL allowExposures;\n    CARD16 pad2 B16;\n    CARD32 pad3 B32;\n    CARD32 pad4 B32;\n    CARD32 pad5 B32;\n    CARD32 pad6 B32;\n    } xGetScreenSaverReply;\n\ntypedef struct {\n    BYTE type;  /* X_Reply */\n    BOOL enabled;\n    CARD16 sequenceNumber B16;\n    CARD32 length B32;\n    CARD16 nHosts B16;\n    CARD16 pad1 B16;\n    CARD32 pad3 B32;\n    CARD32 pad4 B32;\n    CARD32 pad5 B32;\n    CARD32 pad6 B32;\n    CARD32 pad7 B32;\n    } xListHostsReply;\n\n\f\n\n\n/*****************************************************************\n * Xerror\n *    All errors  are 32 bytes \n *****************************************************************/\n\ntypedef struct {\n    BYTE type;                  /* X_Error */\n    BYTE errorCode;\n    CARD16 sequenceNumber B16;       /* the nth request from this client */\n    CARD32 resourceID B32;\n    CARD16 minorCode B16;\n    CARD8 majorCode;\n    BYTE pad1;\n    CARD32 pad3 B32;\n    CARD32 pad4 B32;\n    CARD32 pad5 B32;\n    CARD32 pad6 B32;\n    CARD32 pad7 B32;\n} xError;\n\n/*****************************************************************\n * xEvent\n *    All events are 32 bytes\n *****************************************************************/\n\ntypedef struct _xEvent {\n    union {\n\tstruct {\n\t    BYTE type;\n\t    BYTE detail;\n\t    CARD16 sequenceNumber B16;\n\t    } u;\n\tstruct {\n            CARD32 pad00 B32;\n\t    Time time B32;\n\t    Window root B32, event B32, child B32;\n\t    INT16 rootX B16, rootY B16, eventX B16, eventY B16;\n\t    KeyButMask state B16;\n\t    BOOL sameScreen;\t\t\n\t    BYTE pad1;\n\t} keyButtonPointer;\n\tstruct {\n            CARD32 pad00 B32;\n            Time time B32;\n\t    Window root B32, event B32, child B32;\n\t    INT16 rootX B16, rootY B16, eventX B16, eventY B16;\n\t    KeyButMask state B16;\n\t    BYTE mode; \t\t\t/* really XMode */\n\t    BYTE flags;\t\t/* sameScreen and focus booleans, packed together */\n#define ELFlagFocus        (1<<0)\n#define ELFlagSameScreen   (1<<1)\n\t} enterLeave;\n\tstruct {\n            CARD32 pad00 B32;\n\t    Window window B32;\n\t    BYTE mode; \t\t\t/* really XMode */\n\t    BYTE pad1, pad2, pad3;\n\t} focus;\n\tstruct {\n            CARD32 pad00 B32;\n\t    Window window B32;\n\t    CARD16 x B16, y B16, width B16, height B16;\n\t    CARD16 count B16;\n\t    CARD16 pad2 B16;\n\t} expose;\n\tstruct {\n            CARD32 pad00 B32;\n\t    Drawable drawable B32;\n\t    CARD16 x B16, y B16, width B16, height B16;\n\t    CARD16 minorEvent B16;\n\t    CARD16 count B16;\n\t    BYTE majorEvent;\n\t    BYTE pad1, pad2, pad3;\n\t} graphicsExposure;\n\tstruct {\n            CARD32 pad00 B32;\n\t    Drawable drawable B32;\n\t    CARD16 minorEvent B16;\n\t    BYTE majorEvent;\n\t    BYTE bpad;\n\t} noExposure;\n\tstruct {\n            CARD32 pad00 B32;\n\t    Window window B32;\n\t    CARD8 state;\n\t    BYTE pad1, pad2, pad3;\n\t} visibility;\n\tstruct {\n            CARD32 pad00 B32;\n\t    Window parent B32, window B32;\n\t    INT16 x B16, y B16;\n\t    CARD16 width B16, height B16, borderWidth B16;\n\t    BOOL override;\n\t    BYTE bpad;\n        } createNotify;\n/*\n * The event feilds in the structures for DestroyNotify, UnmapNotify,\n * MapNotify, ReparentNotify, ConfigureNotify, CirclulateNotify, GravityNotify,\n * must be at the same offset because server internal code is depending upon\n * this to patch up the events before they are delivered.\n * Also note that MapRequest, ConfigureRequest and CirculateRequest have\n * the same offset for the event window.\n */\n\tstruct {\n            CARD32 pad00 B32;\n\t    Window event B32, window B32;\n\t} destroyNotify;\n\tstruct {\n            CARD32 pad00 B32;\n\t    Window event B32, window B32;\n\t    BOOL fromConfigure;\n\t    BYTE pad1, pad2, pad3;\n        } unmapNotify;\n\tstruct {\n            CARD32 pad00 B32;\n\t    Window event B32, window B32;\n\t    BOOL override;\n\t    BYTE pad1, pad2, pad3;\n        } mapNotify;\n\tstruct {\n            CARD32 pad00 B32;\n\t    Window parent B32, window B32;\n        } mapRequest;\n\tstruct {\n            CARD32 pad00 B32;\n\t    Window event B32, window B32, parent B32;\n\t    INT16 x B16, y B16;\n\t    BOOL override;\n\t    BYTE pad1, pad2, pad3;\n\t} reparent;\n\tstruct {\n            CARD32 pad00 B32;\n\t    Window event B32, window B32, aboveSibling B32;\n\t    INT16 x B16, y B16;\n\t    CARD16 width B16, height B16, borderWidth B16;\n\t    BOOL override;\t\t\n\t    BYTE bpad;\n\t} configureNotify;\n\tstruct {\n            CARD32 pad00 B32;\n\t    Window parent B32, window B32, sibling B32;\n\t    INT16 x B16, y B16;\n\t    CARD16 width B16, height B16, borderWidth B16;\n\t    CARD16 valueMask B16;\n\t    CARD32 pad1 B32;\n\t} configureRequest;\n\tstruct {\n            CARD32 pad00 B32;\n\t    Window event B32, window B32;\n\t    INT16 x B16, y B16;\n\t    CARD32 pad1 B32, pad2 B32, pad3 B32, pad4 B32;\n\t} gravity;\n\tstruct {\n            CARD32 pad00 B32;\n\t    Window window B32;\n\t    CARD16 width B16, height B16;\n\t} resizeRequest;\n\tstruct {\n/* The event field in the circulate record is really the parent when this\n   is used as a CirculateRequest insteaad of a CircluateNotify */\n            CARD32 pad00 B32;\n\t    Window event B32, window B32, parent B32;\n\t    BYTE place;\t\t\t/* Top or Bottom */\n\t    BYTE pad1, pad2, pad3;\n\t} circulate;\n\tstruct {\n            CARD32 pad00 B32;\n\t    Window window B32;\n\t    Atom atom B32;\n\t    Time time B32;\n\t    BYTE state;\t\t\t/* NewValue or Deleted */\n\t    BYTE pad1;\n\t    CARD16 pad2 B16;\n\t} property;\n\tstruct {\n            CARD32 pad00 B32;\n            Time time B32;     \n\t    Window window B32;\n\t    Atom atom B32;\n\t} selectionClear;\n\tstruct {\n            CARD32 pad00 B32;\n            Time time B32;    \n\t    Window owner B32, requestor B32;\n\t    Atom selection B32, target B32, property B32;\n\t} selectionRequest;\n\tstruct {\n            CARD32 pad00 B32;\n            Time time B32;   \n\t    Window requestor B32;\n\t    Atom selection B32, target B32, property B32;\n\t} selectionNotify;\n\tstruct {\n            CARD32 pad00 B32;\n\t    Window window B32;\n\t    Colormap colormap B32;\n#if defined(__cplusplus) || defined(c_plusplus)\n\t    BOOL c_new;\n#else\n\t    BOOL new;\n#endif\n\t    BYTE state;\t\t\t/* Installed or UnInstalled */\n\t    BYTE pad1, pad2;\n\t} colormap;\n\tstruct {\n\t    CARD32 pad00 B32;\n\t    CARD8 request;\n\t    KeyCode firstKeyCode;\n\t    CARD8 count;\n\t    BYTE pad1;\n\t} mappingNotify;\n\tstruct {\n            CARD32 pad00 B32;\n\t    Window window B32;\n\t    union {\n\t\tstruct {\n\t\t    Atom type B32;\n\t\t    INT32 longs0 B32;\n\t\t    INT32 longs1 B32;\n\t\t    INT32 longs2 B32;\n\t\t    INT32 longs3 B32;\n\t\t    INT32 longs4 B32;\n\t\t} l;\n\t\tstruct {\n\t\t    Atom type B32;\n\t\t    INT16 shorts0 B16;\n\t\t    INT16 shorts1 B16;\n\t\t    INT16 shorts2 B16;\n\t\t    INT16 shorts3 B16;\n\t\t    INT16 shorts4 B16;\n\t\t    INT16 shorts5 B16;\n\t\t    INT16 shorts6 B16;\n\t\t    INT16 shorts7 B16;\n\t\t    INT16 shorts8 B16;\n\t\t    INT16 shorts9 B16;\n\t\t} s;\n\t\tstruct {\n\t\t    Atom type B32;\n\t\t    INT8 bytes[20];\n\t\t} b;\n\t    } u; \n\t} clientMessage;\n    } u;\n} xEvent;\n\n/* KeymapNotify events are not included in the above union because they\n   are different from all other events: they do not have a \"detail\"\n   or \"sequenceNumber\", so there is room for a 248-bit key mask. */\n\ntypedef struct {\n    BYTE type;\n    BYTE map[31];\n    } xKeymapEvent;\n\n#define XEventSize (sizeof(xEvent))\n\n/* XReply is the union of all the replies above whose \"fixed part\"\nfits in 32 bytes.  It does NOT include GetWindowAttributesReply,\nQueryFontReply, QueryKeymapReply, or GetKeyboardControlReply \nListFontsWithInfoReply */\n\ntypedef union {\n    xGenericReply generic;\n    xGetGeometryReply geom;\n    xQueryTreeReply tree;\n    xInternAtomReply atom;\n    xGetAtomNameReply atomName;\n    xGetPropertyReply property;\n    xListPropertiesReply listProperties;\n    xGetSelectionOwnerReply selection;\n    xGrabPointerReply grabPointer;\n    xGrabKeyboardReply grabKeyboard;\n    xQueryPointerReply pointer;\n    xGetMotionEventsReply motionEvents;\n    xTranslateCoordsReply coords;\n    xGetInputFocusReply inputFocus;\n    xQueryTextExtentsReply textExtents;\n    xListFontsReply fonts;\n    xGetFontPathReply fontPath;\n    xGetImageReply image;\n    xListInstalledColormapsReply colormaps;\n    xAllocColorReply allocColor;\n    xAllocNamedColorReply allocNamedColor;\n    xAllocColorCellsReply colorCells;\n    xAllocColorPlanesReply colorPlanes;\n    xQueryColorsReply colors;\n    xLookupColorReply lookupColor;\n    xQueryBestSizeReply bestSize;\n    xQueryExtensionReply extension;\n    xListExtensionsReply extensions;\n    xSetModifierMappingReply setModifierMapping;\n    xGetModifierMappingReply getModifierMapping;\n    xSetPointerMappingReply setPointerMapping;\n    xGetKeyboardMappingReply getKeyboardMapping;\n    xGetPointerMappingReply getPointerMapping;\n    xGetPointerControlReply pointerControl;\n    xGetScreenSaverReply screenSaver;\n    xListHostsReply hosts;\n    xError error;\n    xEvent event;\n} xReply;\n\n\f\n\n/*****************************************************************\n * REQUESTS\n *****************************************************************/\n\n\n/* Request structure */\n\ntypedef struct _xReq {\n\tCARD8 reqType;\n\tCARD8 data;            /* meaning depends on request type */\n\tCARD16 length B16;         /* length in 4 bytes quantities \n\t\t\t\t  of whole request, including this header */\n} xReq;\n\n/*****************************************************************\n *  structures that follow request. \n *****************************************************************/\n\n/* ResourceReq is used for any request which has a resource ID \n   (or Atom or Time) as its one and only argument.  */\n\ntypedef struct {\n    CARD8 reqType;\n    BYTE pad;\n    CARD16 length B16;\n    CARD32 id B32;  /* a Window, Drawable, Font, GContext, Pixmap, etc. */\n    } xResourceReq;\n\ntypedef struct {\n    CARD8 reqType;\n    CARD8 depth;\n    CARD16 length B16;\n    Window wid B32, parent B32;\n    INT16 x B16, y B16;\n    CARD16 width B16, height B16, borderWidth B16;  \n#if defined(__cplusplus) || defined(c_plusplus)\n    CARD16 c_class B16;\n#else\n    CARD16 class B16;\n#endif\n    VisualID visual B32;\n    CARD32 mask B32;\n} xCreateWindowReq;\n\ntypedef struct {\n    CARD8 reqType;\n    BYTE pad;\n    CARD16 length B16;\n    Window window B32;\n    CARD32 valueMask B32; \n} xChangeWindowAttributesReq;\n\ntypedef struct {\n    CARD8 reqType;\n    BYTE mode;\n    CARD16 length B16;\n    Window window B32;\n} xChangeSaveSetReq;\n\ntypedef struct {\n    CARD8 reqType;\n    BYTE pad;\n    CARD16 length B16;\n    Window window B32, parent B32;\n    INT16 x B16, y B16;\n} xReparentWindowReq;\n\ntypedef struct {\n    CARD8 reqType;\n    CARD8 pad;\n    CARD16 length B16;\n    Window window B32;\n    CARD16 mask B16;\n    CARD16 pad2 B16;\n} xConfigureWindowReq;\n\ntypedef struct {\n    CARD8 reqType;\n    CARD8 direction;\n    CARD16 length B16;\n    Window window B32;\n} xCirculateWindowReq;\n\ntypedef struct {    /* followed by padded string */\n    CARD8 reqType;\n    BOOL onlyIfExists;\n    CARD16 length B16;\n    CARD16 nbytes  B16;    /* number of bytes in string */\n    CARD16 pad B16;\n} xInternAtomReq;\n\ntypedef struct {\n    CARD8 reqType;\n    CARD8 mode;\n    CARD16 length B16;\n    Window window B32;\n    Atom property B32, type B32;\n    CARD8 format;\n    BYTE pad[3];\n    CARD32 nUnits B32;     /* length of stuff following, depends on format */\n} xChangePropertyReq;\n\ntypedef struct {\n    CARD8 reqType;\n    BYTE pad;\n    CARD16 length B16;\n    Window window B32;\n    Atom property B32;\n} xDeletePropertyReq;\n\ntypedef struct {\n    CARD8 reqType;\n#if defined(__cplusplus) || defined(c_plusplus)\n    BOOL c_delete;\n#else\n    BOOL delete;\n#endif\n    CARD16 length B16;\n    Window window B32;\n    Atom property B32, type B32;\n    CARD32 longOffset B32;\n    CARD32 longLength B32;\n} xGetPropertyReq;\n \ntypedef struct {\n    CARD8 reqType;\n    BYTE pad;\n    CARD16 length B16;\n    Window window B32;\n    Atom selection B32;\n    Time time B32;\n} xSetSelectionOwnerReq;\n\ntypedef struct {\n    CARD8 reqType;\n    BYTE pad;\n    CARD16 length B16;\n    Window requestor B32;\n    Atom selection B32, target B32, property B32;\n    Time time B32;\n    } xConvertSelectionReq;\n\ntypedef struct {\n    CARD8 reqType;\n    BOOL propagate;\n    CARD16 length B16;\n    Window destination B32;\n    CARD32 eventMask B32;\n#ifdef WORD64\n    /* the structure should have been quad-aligned */\n    BYTE eventdata[SIZEOF(xEvent)];\n#else\n    xEvent event;\n#endif /* WORD64 */\n} xSendEventReq;\n\ntypedef struct {\n    CARD8 reqType;\n    BOOL ownerEvents;\n    CARD16 length B16;\n    Window grabWindow B32;\n    CARD16 eventMask B16;\n    BYTE pointerMode, keyboardMode;\n    Window confineTo B32;\n    Cursor cursor B32;\n    Time time B32;\n} xGrabPointerReq;\n\ntypedef struct {\n    CARD8 reqType;\n    BOOL ownerEvents;\n    CARD16 length B16;\n    Window grabWindow B32;\n    CARD16 eventMask B16;\n    BYTE pointerMode, keyboardMode;\n    Window confineTo B32;\n    Cursor cursor B32;\n    CARD8 button;\n    BYTE pad;\n    CARD16 modifiers B16;\n} xGrabButtonReq;\n\ntypedef struct {\n    CARD8 reqType;\n    CARD8 button;\n    CARD16 length B16;\n    Window grabWindow B32;\n    CARD16 modifiers B16;\n    CARD16 pad B16;\n} xUngrabButtonReq;\n\ntypedef struct {\n    CARD8 reqType;\n    BYTE pad;\n    CARD16 length B16;\n    Cursor cursor B32;\n    Time time B32;\n    CARD16 eventMask B16;\n    CARD16 pad2 B16;\n} xChangeActivePointerGrabReq;\n\ntypedef struct {\n    CARD8 reqType;\n    BOOL ownerEvents;\n    CARD16 length B16;\n    Window grabWindow B32;\n    Time time B32;\n    BYTE pointerMode, keyboardMode;  \n    CARD16 pad B16;\n} xGrabKeyboardReq;\n\ntypedef struct {\n    CARD8 reqType;\n    BOOL ownerEvents;\n    CARD16 length B16;\n    Window grabWindow B32;\n    CARD16 modifiers B16;\n    CARD8 key;\n    BYTE pointerMode, keyboardMode;  \n    BYTE pad1, pad2, pad3;\n} xGrabKeyReq;\n\ntypedef struct {\n    CARD8 reqType;\n    CARD8 key;\n    CARD16 length B16;\n    Window grabWindow B32;\n    CARD16 modifiers B16;\n    CARD16 pad B16;\n} xUngrabKeyReq;\n\ntypedef struct {\n    CARD8 reqType;\n    CARD8 mode;\n    CARD16 length B16;\n    Time time B32;\n} xAllowEventsReq;\n\ntypedef struct {\n    CARD8 reqType;\n    BYTE pad;\n    CARD16 length B16;\n    Window window B32;\n    Time start B32, stop B32;\n} xGetMotionEventsReq;\n\ntypedef struct {\n    CARD8 reqType;\n    BYTE pad;\n    CARD16 length B16;\n    Window srcWid B32, dstWid B32;\n    INT16 srcX B16, srcY B16;\n} xTranslateCoordsReq;\n\ntypedef struct {\n    CARD8 reqType;\n    BYTE pad;\n    CARD16 length B16;\n    Window srcWid B32, dstWid B32;\n    INT16 srcX B16, srcY B16;\n    CARD16 srcWidth B16, srcHeight B16;\n    INT16 dstX B16, dstY B16;\n} xWarpPointerReq;\n\ntypedef struct {\n    CARD8 reqType;\n    CARD8 revertTo;\n    CARD16 length B16;\n    Window focus B32;\n    Time time B32;\n} xSetInputFocusReq;\n\ntypedef struct {\n    CARD8 reqType;\n    BYTE pad;\n    CARD16 length B16;\n    Font fid B32;\n    CARD16 nbytes B16;\n    BYTE pad1, pad2;\t/* string follows on word boundary */\n} xOpenFontReq;\n\ntypedef struct {\n    CARD8 reqType;\n    BOOL oddLength;\n    CARD16 length B16;\n    Font fid B32;\n    } xQueryTextExtentsReq;\n\ntypedef struct {\n    CARD8 reqType;\n    BYTE pad;\n    CARD16 length B16;\n    CARD16 maxNames B16;\n    CARD16 nbytes B16;  /* followed immediately by string bytes */\n} xListFontsReq;\n\ntypedef xListFontsReq xListFontsWithInfoReq;\n\ntypedef struct {\n    CARD8 reqType;\n    BYTE pad;\n    CARD16 length B16;\n    CARD16 nFonts B16;\n    BYTE pad1, pad2;\t/* LISTofSTRING8 follows on word boundary */\n} xSetFontPathReq;\n\ntypedef struct {\n    CARD8 reqType;\n    CARD8 depth;\n    CARD16 length B16;\n    Pixmap pid B32;\n    Drawable drawable B32;\n    CARD16 width B16, height B16;\n} xCreatePixmapReq;\n\ntypedef struct {\n    CARD8 reqType;\n    BYTE pad;\n    CARD16 length B16;\n    GContext gc B32;\n    Drawable drawable B32;\n    CARD32 mask B32;\n} xCreateGCReq;\n\ntypedef struct {\n    CARD8 reqType;\n    BYTE pad;\n    CARD16 length B16;\n    GContext gc B32;\n    CARD32 mask B32;\n} xChangeGCReq;    \n\ntypedef struct {\n    CARD8 reqType;\n    BYTE pad;\n    CARD16 length B16;\n    GContext srcGC B32, dstGC B32;\n    CARD32 mask B32;\n} xCopyGCReq;    \n\ntypedef struct {\n    CARD8 reqType;\n    BYTE pad;\n    CARD16 length B16;\n    GContext gc B32;\n    CARD16 dashOffset B16;\n    CARD16 nDashes B16;        /* length LISTofCARD8 of values following */\n} xSetDashesReq;    \n\ntypedef struct {\n    CARD8 reqType;\n    BYTE ordering;\n    CARD16 length B16;\n    GContext gc B32;\n    INT16 xOrigin B16, yOrigin B16;\n} xSetClipRectanglesReq;    \n\ntypedef struct {\n    CARD8 reqType;\n    BOOL exposures;\n    CARD16 length B16;\n    Window window B32;\n    INT16 x B16, y B16;\n    CARD16 width B16, height B16;\n} xClearAreaReq;\n\ntypedef struct {\n    CARD8 reqType;\n    BYTE pad;\n    CARD16 length B16;\n    Drawable srcDrawable B32, dstDrawable B32;\n    GContext gc B32;\n    INT16 srcX B16, srcY B16, dstX B16, dstY B16;\n    CARD16 width B16, height B16;\n} xCopyAreaReq;    \n\ntypedef struct {\n    CARD8 reqType;\n    BYTE pad;\n    CARD16 length B16;\n    Drawable srcDrawable B32, dstDrawable B32;\n    GContext gc B32;\n    INT16 srcX B16, srcY B16, dstX B16, dstY B16;\n    CARD16 width B16, height B16;\n    CARD32 bitPlane B32;\n} xCopyPlaneReq;    \n\ntypedef struct {\n    CARD8 reqType;\n    BYTE coordMode;\n    CARD16 length B16;\n    Drawable drawable B32;\n    GContext gc B32;\n} xPolyPointReq;    \n\ntypedef xPolyPointReq xPolyLineReq;  /* same request structure */\n\n/* The following used for PolySegment, PolyRectangle, PolyArc, PolyFillRectangle, PolyFillArc */\n\ntypedef struct {\n    CARD8 reqType;\n    BYTE pad;\n    CARD16 length B16;\n    Drawable drawable B32;\n    GContext gc B32;\n} xPolySegmentReq;    \n\ntypedef xPolySegmentReq xPolyArcReq;\ntypedef xPolySegmentReq xPolyRectangleReq;\ntypedef xPolySegmentReq xPolyFillRectangleReq;\ntypedef xPolySegmentReq xPolyFillArcReq;\n\ntypedef struct _FillPolyReq {\n    CARD8 reqType;\n    BYTE pad;\n    CARD16 length B16;\n    Drawable drawable B32;\n    GContext gc B32;\n    BYTE shape;\n    BYTE coordMode;\n    CARD16 pad1 B16;\n} xFillPolyReq;    \n\n\ntypedef struct _PutImageReq {\n    CARD8 reqType;\n    CARD8 format;\n    CARD16 length B16;\n    Drawable drawable B32;\n    GContext gc B32;\n    CARD16 width B16, height B16;\n    INT16 dstX B16, dstY B16;\n    CARD8 leftPad;\n    CARD8 depth;\n    CARD16 pad B16;\n} xPutImageReq;    \n\ntypedef struct {\n    CARD8 reqType;\n    CARD8 format;\n    CARD16 length B16;\n    Drawable drawable B32;\n    INT16 x B16, y B16;\n    CARD16 width B16, height B16;\n    CARD32 planeMask B32;\n} xGetImageReq;    \n\n/* the folloiwng used by PolyText8 and PolyText16 */\n\ntypedef struct {\n    CARD8 reqType;\n    CARD8 pad;\n    CARD16 length B16;\n    Drawable drawable B32;\n    GContext gc B32;\n    INT16 x B16, y B16;\t\t/* items (xTextElt) start after struct */\n} xPolyTextReq;    \n\ntypedef xPolyTextReq xPolyText8Req;\ntypedef xPolyTextReq xPolyText16Req;\n\ntypedef struct {\n    CARD8 reqType;\n    BYTE nChars;\n    CARD16 length B16;\n    Drawable drawable B32;\n    GContext gc B32;\n    INT16 x B16, y B16;\n} xImageTextReq;    \n\ntypedef xImageTextReq xImageText8Req;\ntypedef xImageTextReq xImageText16Req;\n\ntypedef struct {\n    CARD8 reqType;\n    BYTE alloc;\n    CARD16 length B16;\n    Colormap mid B32;\n    Window window B32;\n    VisualID visual B32;\n} xCreateColormapReq;    \n\ntypedef struct {\n    CARD8 reqType;\n    BYTE pad;\n    CARD16 length B16;\n    Colormap mid B32;\n    Colormap srcCmap B32;\n} xCopyColormapAndFreeReq;    \n\ntypedef struct {\n    CARD8 reqType;\n    BYTE pad;\n    CARD16 length B16;\n    Colormap cmap B32;\n    CARD16 red B16, green B16, blue B16;\n    CARD16 pad2 B16;\n} xAllocColorReq;    \n\ntypedef struct {\n    CARD8 reqType;\n    BYTE pad;\n    CARD16 length B16;\n    Colormap cmap B32;\n    CARD16 nbytes B16;  /* followed by structure */\n    BYTE pad1, pad2;\n} xAllocNamedColorReq;    \n\ntypedef struct {\n    CARD8 reqType;\n    BOOL contiguous;\n    CARD16 length B16;\n    Colormap cmap B32;\n    CARD16 colors B16, planes B16;\n} xAllocColorCellsReq;    \n\ntypedef struct {\n    CARD8 reqType;\n    BOOL contiguous;\n    CARD16 length B16;\n    Colormap cmap B32;\n    CARD16 colors B16, red B16, green B16, blue B16;\n} xAllocColorPlanesReq;    \n\ntypedef struct {\n    CARD8 reqType;\n    BYTE pad;\n    CARD16 length B16;\n    Colormap cmap B32;\n    CARD32 planeMask B32;\n} xFreeColorsReq;    \n\ntypedef struct {\n    CARD8 reqType;\n    BYTE pad;\n    CARD16 length B16;\n    Colormap cmap B32;\n} xStoreColorsReq;    \n\ntypedef struct {\n    CARD8 reqType;\n    CARD8 flags;   /* DoRed, DoGreen, DoBlue, as in xColorItem */\n    CARD16 length B16;\n    Colormap cmap B32;\n    CARD32 pixel B32;\n    CARD16 nbytes B16;  /* number of name string bytes following structure */\n    BYTE pad1, pad2;\n    } xStoreNamedColorReq;\n\ntypedef struct {\n    CARD8 reqType;\n    BYTE pad;\n    CARD16 length B16;\n    Colormap cmap B32;\n} xQueryColorsReq;    \n\ntypedef struct {    /* followed  by string of length len */\n    CARD8 reqType;\n    BYTE pad;\n    CARD16 length B16;\n    Colormap cmap B32;\n    CARD16 nbytes B16;  /* number of string bytes following structure*/\n    BYTE pad1, pad2;\n} xLookupColorReq;    \n\ntypedef struct {\n    CARD8 reqType;\n    BYTE pad;\n    CARD16 length B16;\n    Cursor cid B32;\n    Pixmap source B32, mask B32;\n    CARD16 foreRed B16, foreGreen B16, foreBlue B16;\n    CARD16 backRed B16, backGreen B16, backBlue B16;\n    CARD16 x B16, y B16;\n} xCreateCursorReq;    \n\ntypedef struct {\n    CARD8 reqType;\n    BYTE pad;\n    CARD16 length B16;\n    Cursor cid B32;\n    Font source B32, mask B32;\n    CARD16 sourceChar B16, maskChar B16;\n    CARD16 foreRed B16, foreGreen B16, foreBlue B16;\n    CARD16 backRed B16, backGreen B16, backBlue B16;\n} xCreateGlyphCursorReq;    \n\ntypedef struct {\n    CARD8 reqType;\n    BYTE pad;\n    CARD16 length B16;\n    Cursor cursor B32;\n    CARD16 foreRed B16, foreGreen B16, foreBlue B16;\n    CARD16 backRed B16, backGreen B16, backBlue B16;\n} xRecolorCursorReq;    \n\ntypedef struct {\n    CARD8 reqType;\n#if defined(__cplusplus) || defined(c_plusplus)\n    CARD8 c_class;\n#else\n    CARD8 class;\n#endif\n    CARD16 length B16;\n    Drawable drawable B32;\n    CARD16 width B16, height B16;\n} xQueryBestSizeReq;    \n\ntypedef struct {\n    CARD8 reqType;\n    BYTE pad;\n    CARD16 length B16;\n    CARD16 nbytes B16;  /* number of string bytes following structure */\n    BYTE pad1, pad2;\n} xQueryExtensionReq;\n\ntypedef struct {\n    CARD8   reqType;\n    CARD8   numKeyPerModifier;\n    CARD16  length B16;\n} xSetModifierMappingReq;\n\ntypedef struct {\n    CARD8 reqType;\n    CARD8 nElts;  /* how many elements in the map */\n    CARD16 length B16;\n} xSetPointerMappingReq;\n\ntypedef struct {\n    CARD8 reqType;\n    BYTE pad;\n    CARD16 length B16;\n    KeyCode firstKeyCode;\n    CARD8 count;\n    CARD16 pad1 B16;\n} xGetKeyboardMappingReq;    \n\ntypedef struct {\n    CARD8 reqType;\n    CARD8 keyCodes;\n    CARD16 length B16;\n    KeyCode firstKeyCode;\n    CARD8 keySymsPerKeyCode;\n    CARD16 pad1 B16;\n} xChangeKeyboardMappingReq;\n\ntypedef struct {\n    CARD8 reqType;\n    BYTE pad;\n    CARD16 length B16;\n    CARD32 mask B32;\n} xChangeKeyboardControlReq;    \n\ntypedef struct {\n    CARD8 reqType;\n    INT8 percent;  /* -100 to 100 */\n    CARD16 length B16;\n} xBellReq;    \n\ntypedef struct {\n    CARD8 reqType;\n    BYTE pad;\n    CARD16 length B16;\n    INT16 accelNum B16, accelDenum B16;\n    INT16 threshold B16;             \n    BOOL doAccel, doThresh;\n} xChangePointerControlReq;    \n\ntypedef struct {\n    CARD8 reqType;\n    BYTE pad;\n    CARD16 length B16;\n    INT16 timeout B16, interval B16;\n    BYTE preferBlank, allowExpose;  \n    CARD16 pad2 B16;\n} xSetScreenSaverReq;    \n\ntypedef struct {\n    CARD8 reqType;\n    BYTE mode;\n    CARD16 length B16;\n    CARD8 hostFamily;\n    BYTE pad;\n    CARD16 hostLength B16;\n} xChangeHostsReq;    \n\ntypedef struct {\n    CARD8 reqType;\n    BYTE pad;\n    CARD16 length B16;\n    } xListHostsReq;\n\ntypedef struct {\n    CARD8 reqType;\n    BYTE mode;\n    CARD16 length B16;\n    } xChangeModeReq;\n\ntypedef xChangeModeReq xSetAccessControlReq;\ntypedef xChangeModeReq xSetCloseDownModeReq;\ntypedef xChangeModeReq xForceScreenSaverReq;\n\ntypedef struct { /* followed by LIST of ATOM */\n    CARD8 reqType;\n    BYTE pad;\n    CARD16 length B16;\n    Window window B32;\n    CARD16 nAtoms B16;\n    INT16 nPositions B16;\n    } xRotatePropertiesReq;\n    \n\f\n\n/* Reply codes */\n\n#define X_Reply\t\t1\t\t/* Normal reply */\n#define X_Error\t\t0\t\t/* Error */\n\n/* Request codes */\n\n#define X_CreateWindow                  1              \n#define X_ChangeWindowAttributes        2        \n#define X_GetWindowAttributes           3     \n#define X_DestroyWindow                 4\n#define X_DestroySubwindows             5   \n#define X_ChangeSaveSet                 6\n#define X_ReparentWindow                7\n#define X_MapWindow                     8\n#define X_MapSubwindows                 9\n#define X_UnmapWindow                  10\n#define X_UnmapSubwindows              11  \n#define X_ConfigureWindow              12  \n#define X_CirculateWindow              13  \n#define X_GetGeometry                  14\n#define X_QueryTree                    15\n#define X_InternAtom                   16\n#define X_GetAtomName                  17\n#define X_ChangeProperty               18 \n#define X_DeleteProperty               19 \n#define X_GetProperty                  20\n#define X_ListProperties               21 \n#define X_SetSelectionOwner            22    \n#define X_GetSelectionOwner            23    \n#define X_ConvertSelection             24   \n#define X_SendEvent                    25\n#define X_GrabPointer                  26\n#define X_UngrabPointer                27\n#define X_GrabButton                   28\n#define X_UngrabButton                 29\n#define X_ChangeActivePointerGrab      30          \n#define X_GrabKeyboard                 31\n#define X_UngrabKeyboard               32 \n#define X_GrabKey                      33\n#define X_UngrabKey                    34\n#define X_AllowEvents                  35       \n#define X_GrabServer                   36      \n#define X_UngrabServer                 37        \n#define X_QueryPointer                 38        \n#define X_GetMotionEvents              39           \n#define X_TranslateCoords              40                \n#define X_WarpPointer                  41       \n#define X_SetInputFocus                42         \n#define X_GetInputFocus                43         \n#define X_QueryKeymap                  44       \n#define X_OpenFont                     45    \n#define X_CloseFont                    46     \n#define X_QueryFont                    47\n#define X_QueryTextExtents             48     \n#define X_ListFonts                    49  \n#define X_ListFontsWithInfo    \t       50 \n#define X_SetFontPath                  51 \n#define X_GetFontPath                  52 \n#define X_CreatePixmap                 53        \n#define X_FreePixmap                   54      \n#define X_CreateGC                     55    \n#define X_ChangeGC                     56    \n#define X_CopyGC                       57  \n#define X_SetDashes                    58     \n#define X_SetClipRectangles            59             \n#define X_FreeGC                       60  \n#define X_ClearArea                    61             \n#define X_CopyArea                     62    \n#define X_CopyPlane                    63     \n#define X_PolyPoint                    64     \n#define X_PolyLine                     65    \n#define X_PolySegment                  66       \n#define X_PolyRectangle                67         \n#define X_PolyArc                      68   \n#define X_FillPoly                     69    \n#define X_PolyFillRectangle            70             \n#define X_PolyFillArc                  71       \n#define X_PutImage                     72    \n#define X_GetImage                     73 \n#define X_PolyText8                    74     \n#define X_PolyText16                   75      \n#define X_ImageText8                   76      \n#define X_ImageText16                  77       \n#define X_CreateColormap               78          \n#define X_FreeColormap                 79        \n#define X_CopyColormapAndFree          80               \n#define X_InstallColormap              81           \n#define X_UninstallColormap            82             \n#define X_ListInstalledColormaps       83                  \n#define X_AllocColor                   84      \n#define X_AllocNamedColor              85           \n#define X_AllocColorCells              86           \n#define X_AllocColorPlanes             87            \n#define X_FreeColors                   88      \n#define X_StoreColors                  89       \n#define X_StoreNamedColor              90           \n#define X_QueryColors                  91       \n#define X_LookupColor                  92       \n#define X_CreateCursor                 93        \n#define X_CreateGlyphCursor            94             \n#define X_FreeCursor                   95      \n#define X_RecolorCursor                96         \n#define X_QueryBestSize                97         \n#define X_QueryExtension               98          \n#define X_ListExtensions               99          \n#define X_ChangeKeyboardMapping        100\n#define X_GetKeyboardMapping           101\n#define X_ChangeKeyboardControl        102                \n#define X_GetKeyboardControl           103             \n#define X_Bell                         104\n#define X_ChangePointerControl         105\n#define X_GetPointerControl            106\n#define X_SetScreenSaver               107          \n#define X_GetScreenSaver               108          \n#define X_ChangeHosts                  109       \n#define X_ListHosts                    110     \n#define X_SetAccessControl             111               \n#define X_SetCloseDownMode             112\n#define X_KillClient                   113 \n#define X_RotateProperties\t       114\n#define X_ForceScreenSaver\t       115\n#define X_SetPointerMapping            116\n#define X_GetPointerMapping            117\n#define X_SetModifierMapping\t       118\n#define X_GetModifierMapping\t       119\n#define X_NoOperation                  127\n\n/* restore these definitions back to the typedefs in X.h */\n#undef Window\n#undef Drawable\n#undef Font\n#undef Pixmap\n#undef Cursor\n#undef Colormap\n#undef GContext\n#undef Atom\n#undef VisualID\n#undef Time\n#undef KeyCode\n#undef KeySym\n\n#endif /* XPROTO_H */\n"
  },
  {
    "path": "plugins/xsv/Xprotostr.h",
    "content": "//the only changes in this file for qux support are with header file locations.\n\n/* $Xorg: Xprotostr.h,v 1.4 2001/02/09 02:03:23 xorgcvs Exp $ */\n#ifndef XPROTOSTRUCTS_H\n#define XPROTOSTRUCTS_H\n\n/***********************************************************\n\nCopyright 1987, 1998  The Open Group\n\nPermission to use, copy, modify, distribute, and sell this software and its\ndocumentation for any purpose is hereby granted without fee, provided that\nthe above copyright notice appear in all copies and that both that\ncopyright notice and this permission notice appear in supporting\ndocumentation.\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\nOPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN\nAN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\nCONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\nExcept as contained in this notice, the name of The Open Group shall not be\nused in advertising or otherwise to promote the sale, use or other dealings\nin this Software without prior written authorization from The Open Group.\n\n\nCopyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts.\n\n                        All Rights Reserved\n\nPermission to use, copy, modify, and distribute this software and its \ndocumentation for any purpose and without fee is hereby granted, \nprovided that the above copyright notice appear in all copies and that\nboth that copyright notice and this permission notice appear in \nsupporting documentation, and that the name of Digital not be\nused in advertising or publicity pertaining to distribution of the\nsoftware without specific, written prior permission.  \n\nDIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING\nALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL\nDIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR\nANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,\nWHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,\nARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS\nSOFTWARE.\n\n******************************************************************/\n#include \"Xmd.h\"\n\n/* Used by PolySegment */\n\ntypedef struct _xSegment {\n    INT16 x1 B16, y1 B16, x2 B16, y2 B16;\n} xSegment;\n\n/* POINT */\n\ntypedef struct _xPoint {\n\tINT16\t\tx B16, y B16;\n} xPoint;\n\ntypedef struct _xRectangle {\n    INT16 x B16, y B16;\n    CARD16  width B16, height B16;\n} xRectangle;\n\n/*  ARC  */\n\ntypedef struct _xArc {\n    INT16 x B16, y B16;\n    CARD16   width B16, height B16;\n    INT16   angle1 B16, angle2 B16;\n} xArc;\n\n#endif /* XPROTOSTRUCTS_H */\n"
  },
  {
    "path": "plugins/xsv/bigreqstr.h",
    "content": "/* $Xorg: bigreqstr.h,v 1.4 2001/02/09 02:03:24 xorgcvs Exp $ */\n/*\n\nCopyright 1992, 1998  The Open Group\n\nPermission to use, copy, modify, distribute, and sell this software and its\ndocumentation for any purpose is hereby granted without fee, provided that\nthe above copyright notice appear in all copies and that both that\ncopyright notice and this permission notice appear in supporting\ndocumentation.\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\nOPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN\nAN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\nCONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\nExcept as contained in this notice, the name of The Open Group shall not be\nused in advertising or otherwise to promote the sale, use or other dealings\nin this Software without prior written authorization from The Open Group.\n\n*/\n\n#define X_BigReqEnable\t\t0\n\n#define XBigReqNumberEvents\t0\n\n#define XBigReqNumberErrors\t0\n\n#define XBigReqExtensionName\t\"BIG-REQUESTS\"\n\ntypedef struct {\n    CARD8\treqType;\t/* always XBigReqCode */\n    CARD8\tbrReqType;\t/* always X_BigReqEnable */\n    CARD16\tlength B16;\n} xBigReqEnableReq;\n#define sz_xBigReqEnableReq 4\n\ntypedef struct {\n    BYTE\ttype;\t\t\t/* X_Reply */\n    CARD8\tpad0;\n    CARD16\tsequenceNumber B16;\n    CARD32\tlength B32;\n    CARD32\tmax_request_size B32;\n    CARD32\tpad1 B32;\n    CARD32\tpad2 B32;\n    CARD32\tpad3 B32;\n    CARD32\tpad4 B32;\n    CARD32\tpad5 B32;\n} xBigReqEnableReply;\n#define sz_xBigReqEnableReply 32\n\n\ntypedef struct {\n\tCARD8 reqType;\n\tCARD8 data;\n\tCARD16 zero B16;\n        CARD32 length B32;\n} xBigReq;\n"
  },
  {
    "path": "plugins/xsv/keysymdef.h",
    "content": "/* $Xorg: keysymdef.h,v 1.4 2001/02/09 02:03:23 xorgcvs Exp $ */\n\n/***********************************************************\nCopyright 1987, 1994, 1998  The Open Group\n\nPermission to use, copy, modify, distribute, and sell this software and its\ndocumentation for any purpose is hereby granted without fee, provided that\nthe above copyright notice appear in all copies and that both that\ncopyright notice and this permission notice appear in supporting\ndocumentation.\n\nThe above copyright notice and this permission notice shall be included\nin all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS\nOR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\nIN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR\nOTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,\nARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR\nOTHER DEALINGS IN THE SOFTWARE.\n\nExcept as contained in this notice, the name of The Open Group shall\nnot be used in advertising or otherwise to promote the sale, use or\nother dealings in this Software without prior written authorization\nfrom The Open Group.\n\n\nCopyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts\n\n                        All Rights Reserved\n\nPermission to use, copy, modify, and distribute this software and its\ndocumentation for any purpose and without fee is hereby granted,\nprovided that the above copyright notice appear in all copies and that\nboth that copyright notice and this permission notice appear in\nsupporting documentation, and that the name of Digital not be\nused in advertising or publicity pertaining to distribution of the\nsoftware without specific, written prior permission.\n\nDIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING\nALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL\nDIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR\nANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,\nWHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,\nARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS\nSOFTWARE.\n\n******************************************************************/\n/* $XFree86: xc/include/keysymdef.h,v 1.12 2001/12/14 19:53:26 dawes Exp $ */\n\n#define XK_VoidSymbol\t\t0xFFFFFF\t/* void symbol */\n\n#ifdef XK_MISCELLANY\n/*\n * TTY Functions, cleverly chosen to map to ascii, for convenience of\n * programming, but could have been arbitrary (at the cost of lookup\n * tables in client code.\n */\n\n#define XK_BackSpace\t\t0xFF08\t/* back space, back char */\n#define XK_Tab\t\t\t0xFF09\n#define XK_Linefeed\t\t0xFF0A\t/* Linefeed, LF */\n#define XK_Clear\t\t0xFF0B\n#define XK_Return\t\t0xFF0D\t/* Return, enter */\n#define XK_Pause\t\t0xFF13\t/* Pause, hold */\n#define XK_Scroll_Lock\t\t0xFF14\n#define XK_Sys_Req\t\t0xFF15\n#define XK_Escape\t\t0xFF1B\n#define XK_Delete\t\t0xFFFF\t/* Delete, rubout */\n\n\n\n/* International & multi-key character composition */\n\n#define XK_Multi_key\t\t0xFF20  /* Multi-key character compose */\n#define XK_Codeinput\t\t0xFF37\n#define XK_SingleCandidate\t0xFF3C\n#define XK_MultipleCandidate\t0xFF3D\n#define XK_PreviousCandidate\t0xFF3E\n\n/* Japanese keyboard support */\n\n#define XK_Kanji\t\t0xFF21\t/* Kanji, Kanji convert */\n#define XK_Muhenkan\t\t0xFF22  /* Cancel Conversion */\n#define XK_Henkan_Mode\t\t0xFF23  /* Start/Stop Conversion */\n#define XK_Henkan\t\t0xFF23  /* Alias for Henkan_Mode */\n#define XK_Romaji\t\t0xFF24  /* to Romaji */\n#define XK_Hiragana\t\t0xFF25  /* to Hiragana */\n#define XK_Katakana\t\t0xFF26  /* to Katakana */\n#define XK_Hiragana_Katakana\t0xFF27  /* Hiragana/Katakana toggle */\n#define XK_Zenkaku\t\t0xFF28  /* to Zenkaku */\n#define XK_Hankaku\t\t0xFF29  /* to Hankaku */\n#define XK_Zenkaku_Hankaku\t0xFF2A  /* Zenkaku/Hankaku toggle */\n#define XK_Touroku\t\t0xFF2B  /* Add to Dictionary */\n#define XK_Massyo\t\t0xFF2C  /* Delete from Dictionary */\n#define XK_Kana_Lock\t\t0xFF2D  /* Kana Lock */\n#define XK_Kana_Shift\t\t0xFF2E  /* Kana Shift */\n#define XK_Eisu_Shift\t\t0xFF2F  /* Alphanumeric Shift */\n#define XK_Eisu_toggle\t\t0xFF30  /* Alphanumeric toggle */\n#define XK_Kanji_Bangou\t\t0xFF37  /* Codeinput */\n#define XK_Zen_Koho\t\t0xFF3D\t/* Multiple/All Candidate(s) */\n#define XK_Mae_Koho\t\t0xFF3E\t/* Previous Candidate */\n\n/* 0xFF31 thru 0xFF3F are under XK_KOREAN */\n\n/* Cursor control & motion */\n\n#define XK_Home\t\t\t0xFF50\n#define XK_Left\t\t\t0xFF51\t/* Move left, left arrow */\n#define XK_Up\t\t\t0xFF52\t/* Move up, up arrow */\n#define XK_Right\t\t0xFF53\t/* Move right, right arrow */\n#define XK_Down\t\t\t0xFF54\t/* Move down, down arrow */\n#define XK_Prior\t\t0xFF55\t/* Prior, previous */\n#define XK_Page_Up\t\t0xFF55\n#define XK_Next\t\t\t0xFF56\t/* Next */\n#define XK_Page_Down\t\t0xFF56\n#define XK_End\t\t\t0xFF57\t/* EOL */\n#define XK_Begin\t\t0xFF58\t/* BOL */\n\n\n/* Misc Functions */\n\n#define XK_Select\t\t0xFF60\t/* Select, mark */\n#define XK_Print\t\t0xFF61\n#define XK_Execute\t\t0xFF62\t/* Execute, run, do */\n#define XK_Insert\t\t0xFF63\t/* Insert, insert here */\n#define XK_Undo\t\t\t0xFF65\t/* Undo, oops */\n#define XK_Redo\t\t\t0xFF66\t/* redo, again */\n#define XK_Menu\t\t\t0xFF67\n#define XK_Find\t\t\t0xFF68\t/* Find, search */\n#define XK_Cancel\t\t0xFF69\t/* Cancel, stop, abort, exit */\n#define XK_Help\t\t\t0xFF6A\t/* Help */\n#define XK_Break\t\t0xFF6B\n#define XK_Mode_switch\t\t0xFF7E\t/* Character set switch */\n#define XK_script_switch        0xFF7E  /* Alias for mode_switch */\n#define XK_Num_Lock\t\t0xFF7F\n\n/* Keypad Functions, keypad numbers cleverly chosen to map to ascii */\n\n#define XK_KP_Space\t\t0xFF80\t/* space */\n#define XK_KP_Tab\t\t0xFF89\n#define XK_KP_Enter\t\t0xFF8D\t/* enter */\n#define XK_KP_F1\t\t0xFF91\t/* PF1, KP_A, ... */\n#define XK_KP_F2\t\t0xFF92\n#define XK_KP_F3\t\t0xFF93\n#define XK_KP_F4\t\t0xFF94\n#define XK_KP_Home\t\t0xFF95\n#define XK_KP_Left\t\t0xFF96\n#define XK_KP_Up\t\t0xFF97\n#define XK_KP_Right\t\t0xFF98\n#define XK_KP_Down\t\t0xFF99\n#define XK_KP_Prior\t\t0xFF9A\n#define XK_KP_Page_Up\t\t0xFF9A\n#define XK_KP_Next\t\t0xFF9B\n#define XK_KP_Page_Down\t\t0xFF9B\n#define XK_KP_End\t\t0xFF9C\n#define XK_KP_Begin\t\t0xFF9D\n#define XK_KP_Insert\t\t0xFF9E\n#define XK_KP_Delete\t\t0xFF9F\n#define XK_KP_Equal\t\t0xFFBD\t/* equals */\n#define XK_KP_Multiply\t\t0xFFAA\n#define XK_KP_Add\t\t0xFFAB\n#define XK_KP_Separator\t\t0xFFAC\t/* separator, often comma */\n#define XK_KP_Subtract\t\t0xFFAD\n#define XK_KP_Decimal\t\t0xFFAE\n#define XK_KP_Divide\t\t0xFFAF\n\n#define XK_KP_0\t\t\t0xFFB0\n#define XK_KP_1\t\t\t0xFFB1\n#define XK_KP_2\t\t\t0xFFB2\n#define XK_KP_3\t\t\t0xFFB3\n#define XK_KP_4\t\t\t0xFFB4\n#define XK_KP_5\t\t\t0xFFB5\n#define XK_KP_6\t\t\t0xFFB6\n#define XK_KP_7\t\t\t0xFFB7\n#define XK_KP_8\t\t\t0xFFB8\n#define XK_KP_9\t\t\t0xFFB9\n\n\n\n/*\n * Auxilliary Functions; note the duplicate definitions for left and right\n * function keys;  Sun keyboards and a few other manufactures have such\n * function key groups on the left and/or right sides of the keyboard.\n * We've not found a keyboard with more than 35 function keys total.\n */\n\n#define XK_F1\t\t\t0xFFBE\n#define XK_F2\t\t\t0xFFBF\n#define XK_F3\t\t\t0xFFC0\n#define XK_F4\t\t\t0xFFC1\n#define XK_F5\t\t\t0xFFC2\n#define XK_F6\t\t\t0xFFC3\n#define XK_F7\t\t\t0xFFC4\n#define XK_F8\t\t\t0xFFC5\n#define XK_F9\t\t\t0xFFC6\n#define XK_F10\t\t\t0xFFC7\n#define XK_F11\t\t\t0xFFC8\n#define XK_L1\t\t\t0xFFC8\n#define XK_F12\t\t\t0xFFC9\n#define XK_L2\t\t\t0xFFC9\n#define XK_F13\t\t\t0xFFCA\n#define XK_L3\t\t\t0xFFCA\n#define XK_F14\t\t\t0xFFCB\n#define XK_L4\t\t\t0xFFCB\n#define XK_F15\t\t\t0xFFCC\n#define XK_L5\t\t\t0xFFCC\n#define XK_F16\t\t\t0xFFCD\n#define XK_L6\t\t\t0xFFCD\n#define XK_F17\t\t\t0xFFCE\n#define XK_L7\t\t\t0xFFCE\n#define XK_F18\t\t\t0xFFCF\n#define XK_L8\t\t\t0xFFCF\n#define XK_F19\t\t\t0xFFD0\n#define XK_L9\t\t\t0xFFD0\n#define XK_F20\t\t\t0xFFD1\n#define XK_L10\t\t\t0xFFD1\n#define XK_F21\t\t\t0xFFD2\n#define XK_R1\t\t\t0xFFD2\n#define XK_F22\t\t\t0xFFD3\n#define XK_R2\t\t\t0xFFD3\n#define XK_F23\t\t\t0xFFD4\n#define XK_R3\t\t\t0xFFD4\n#define XK_F24\t\t\t0xFFD5\n#define XK_R4\t\t\t0xFFD5\n#define XK_F25\t\t\t0xFFD6\n#define XK_R5\t\t\t0xFFD6\n#define XK_F26\t\t\t0xFFD7\n#define XK_R6\t\t\t0xFFD7\n#define XK_F27\t\t\t0xFFD8\n#define XK_R7\t\t\t0xFFD8\n#define XK_F28\t\t\t0xFFD9\n#define XK_R8\t\t\t0xFFD9\n#define XK_F29\t\t\t0xFFDA\n#define XK_R9\t\t\t0xFFDA\n#define XK_F30\t\t\t0xFFDB\n#define XK_R10\t\t\t0xFFDB\n#define XK_F31\t\t\t0xFFDC\n#define XK_R11\t\t\t0xFFDC\n#define XK_F32\t\t\t0xFFDD\n#define XK_R12\t\t\t0xFFDD\n#define XK_F33\t\t\t0xFFDE\n#define XK_R13\t\t\t0xFFDE\n#define XK_F34\t\t\t0xFFDF\n#define XK_R14\t\t\t0xFFDF\n#define XK_F35\t\t\t0xFFE0\n#define XK_R15\t\t\t0xFFE0\n\n/* Modifiers */\n\n#define XK_Shift_L\t\t0xFFE1\t/* Left shift */\n#define XK_Shift_R\t\t0xFFE2\t/* Right shift */\n#define XK_Control_L\t\t0xFFE3\t/* Left control */\n#define XK_Control_R\t\t0xFFE4\t/* Right control */\n#define XK_Caps_Lock\t\t0xFFE5\t/* Caps lock */\n#define XK_Shift_Lock\t\t0xFFE6\t/* Shift lock */\n\n#define XK_Meta_L\t\t0xFFE7\t/* Left meta */\n#define XK_Meta_R\t\t0xFFE8\t/* Right meta */\n#define XK_Alt_L\t\t0xFFE9\t/* Left alt */\n#define XK_Alt_R\t\t0xFFEA\t/* Right alt */\n#define XK_Super_L\t\t0xFFEB\t/* Left super */\n#define XK_Super_R\t\t0xFFEC\t/* Right super */\n#define XK_Hyper_L\t\t0xFFED\t/* Left hyper */\n#define XK_Hyper_R\t\t0xFFEE\t/* Right hyper */\n#endif /* XK_MISCELLANY */\n\n/*\n * ISO 9995 Function and Modifier Keys\n * Byte 3 = 0xFE\n */\n\n#ifdef XK_XKB_KEYS\n#define\tXK_ISO_Lock\t\t\t\t\t0xFE01\n#define\tXK_ISO_Level2_Latch\t\t\t\t0xFE02\n#define\tXK_ISO_Level3_Shift\t\t\t\t0xFE03\n#define\tXK_ISO_Level3_Latch\t\t\t\t0xFE04\n#define\tXK_ISO_Level3_Lock\t\t\t\t0xFE05\n#define\tXK_ISO_Group_Shift\t\t0xFF7E\t/* Alias for mode_switch */\n#define\tXK_ISO_Group_Latch\t\t\t\t0xFE06\n#define\tXK_ISO_Group_Lock\t\t\t\t0xFE07\n#define\tXK_ISO_Next_Group\t\t\t\t0xFE08\n#define\tXK_ISO_Next_Group_Lock\t\t\t\t0xFE09\n#define\tXK_ISO_Prev_Group\t\t\t\t0xFE0A\n#define\tXK_ISO_Prev_Group_Lock\t\t\t\t0xFE0B\n#define\tXK_ISO_First_Group\t\t\t\t0xFE0C\n#define\tXK_ISO_First_Group_Lock\t\t\t\t0xFE0D\n#define\tXK_ISO_Last_Group\t\t\t\t0xFE0E\n#define\tXK_ISO_Last_Group_Lock\t\t\t\t0xFE0F\n\n#define\tXK_ISO_Left_Tab\t\t\t\t\t0xFE20\n#define\tXK_ISO_Move_Line_Up\t\t\t\t0xFE21\n#define\tXK_ISO_Move_Line_Down\t\t\t\t0xFE22\n#define\tXK_ISO_Partial_Line_Up\t\t\t\t0xFE23\n#define\tXK_ISO_Partial_Line_Down\t\t\t0xFE24\n#define\tXK_ISO_Partial_Space_Left\t\t\t0xFE25\n#define\tXK_ISO_Partial_Space_Right\t\t\t0xFE26\n#define\tXK_ISO_Set_Margin_Left\t\t\t\t0xFE27\n#define\tXK_ISO_Set_Margin_Right\t\t\t\t0xFE28\n#define\tXK_ISO_Release_Margin_Left\t\t\t0xFE29\n#define\tXK_ISO_Release_Margin_Right\t\t\t0xFE2A\n#define\tXK_ISO_Release_Both_Margins\t\t\t0xFE2B\n#define\tXK_ISO_Fast_Cursor_Left\t\t\t\t0xFE2C\n#define\tXK_ISO_Fast_Cursor_Right\t\t\t0xFE2D\n#define\tXK_ISO_Fast_Cursor_Up\t\t\t\t0xFE2E\n#define\tXK_ISO_Fast_Cursor_Down\t\t\t\t0xFE2F\n#define\tXK_ISO_Continuous_Underline\t\t\t0xFE30\n#define\tXK_ISO_Discontinuous_Underline\t\t\t0xFE31\n#define\tXK_ISO_Emphasize\t\t\t\t0xFE32\n#define\tXK_ISO_Center_Object\t\t\t\t0xFE33\n#define\tXK_ISO_Enter\t\t\t\t\t0xFE34\n\n#define\tXK_dead_grave\t\t\t\t\t0xFE50\n#define\tXK_dead_acute\t\t\t\t\t0xFE51\n#define\tXK_dead_circumflex\t\t\t\t0xFE52\n#define\tXK_dead_tilde\t\t\t\t\t0xFE53\n#define\tXK_dead_macron\t\t\t\t\t0xFE54\n#define\tXK_dead_breve\t\t\t\t\t0xFE55\n#define\tXK_dead_abovedot\t\t\t\t0xFE56\n#define\tXK_dead_diaeresis\t\t\t\t0xFE57\n#define\tXK_dead_abovering\t\t\t\t0xFE58\n#define\tXK_dead_doubleacute\t\t\t\t0xFE59\n#define\tXK_dead_caron\t\t\t\t\t0xFE5A\n#define\tXK_dead_cedilla\t\t\t\t\t0xFE5B\n#define\tXK_dead_ogonek\t\t\t\t\t0xFE5C\n#define\tXK_dead_iota\t\t\t\t\t0xFE5D\n#define\tXK_dead_voiced_sound\t\t\t\t0xFE5E\n#define\tXK_dead_semivoiced_sound\t\t\t0xFE5F\n#define\tXK_dead_belowdot\t\t\t\t0xFE60\n#define XK_dead_hook\t\t\t\t\t0xFE61\n#define XK_dead_horn\t\t\t\t\t0xFE62\n\n#define\tXK_First_Virtual_Screen\t\t\t\t0xFED0\n#define\tXK_Prev_Virtual_Screen\t\t\t\t0xFED1\n#define\tXK_Next_Virtual_Screen\t\t\t\t0xFED2\n#define\tXK_Last_Virtual_Screen\t\t\t\t0xFED4\n#define\tXK_Terminate_Server\t\t\t\t0xFED5\n\n#define\tXK_AccessX_Enable\t\t\t\t0xFE70\n#define\tXK_AccessX_Feedback_Enable\t\t\t0xFE71\n#define\tXK_RepeatKeys_Enable\t\t\t\t0xFE72\n#define\tXK_SlowKeys_Enable\t\t\t\t0xFE73\n#define\tXK_BounceKeys_Enable\t\t\t\t0xFE74\n#define\tXK_StickyKeys_Enable\t\t\t\t0xFE75\n#define\tXK_MouseKeys_Enable\t\t\t\t0xFE76\n#define\tXK_MouseKeys_Accel_Enable\t\t\t0xFE77\n#define\tXK_Overlay1_Enable\t\t\t\t0xFE78\n#define\tXK_Overlay2_Enable\t\t\t\t0xFE79\n#define\tXK_AudibleBell_Enable\t\t\t\t0xFE7A\n\n#define\tXK_Pointer_Left\t\t\t\t\t0xFEE0\n#define\tXK_Pointer_Right\t\t\t\t0xFEE1\n#define\tXK_Pointer_Up\t\t\t\t\t0xFEE2\n#define\tXK_Pointer_Down\t\t\t\t\t0xFEE3\n#define\tXK_Pointer_UpLeft\t\t\t\t0xFEE4\n#define\tXK_Pointer_UpRight\t\t\t\t0xFEE5\n#define\tXK_Pointer_DownLeft\t\t\t\t0xFEE6\n#define\tXK_Pointer_DownRight\t\t\t\t0xFEE7\n#define\tXK_Pointer_Button_Dflt\t\t\t\t0xFEE8\n#define\tXK_Pointer_Button1\t\t\t\t0xFEE9\n#define\tXK_Pointer_Button2\t\t\t\t0xFEEA\n#define\tXK_Pointer_Button3\t\t\t\t0xFEEB\n#define\tXK_Pointer_Button4\t\t\t\t0xFEEC\n#define\tXK_Pointer_Button5\t\t\t\t0xFEED\n#define\tXK_Pointer_DblClick_Dflt\t\t\t0xFEEE\n#define\tXK_Pointer_DblClick1\t\t\t\t0xFEEF\n#define\tXK_Pointer_DblClick2\t\t\t\t0xFEF0\n#define\tXK_Pointer_DblClick3\t\t\t\t0xFEF1\n#define\tXK_Pointer_DblClick4\t\t\t\t0xFEF2\n#define\tXK_Pointer_DblClick5\t\t\t\t0xFEF3\n#define\tXK_Pointer_Drag_Dflt\t\t\t\t0xFEF4\n#define\tXK_Pointer_Drag1\t\t\t\t0xFEF5\n#define\tXK_Pointer_Drag2\t\t\t\t0xFEF6\n#define\tXK_Pointer_Drag3\t\t\t\t0xFEF7\n#define\tXK_Pointer_Drag4\t\t\t\t0xFEF8\n#define\tXK_Pointer_Drag5\t\t\t\t0xFEFD\n\n#define\tXK_Pointer_EnableKeys\t\t\t\t0xFEF9\n#define\tXK_Pointer_Accelerate\t\t\t\t0xFEFA\n#define\tXK_Pointer_DfltBtnNext\t\t\t\t0xFEFB\n#define\tXK_Pointer_DfltBtnPrev\t\t\t\t0xFEFC\n\n#endif\n\n/*\n * 3270 Terminal Keys\n * Byte 3 = 0xFD\n */\n\n#ifdef XK_3270\n#define XK_3270_Duplicate      0xFD01\n#define XK_3270_FieldMark      0xFD02\n#define XK_3270_Right2         0xFD03\n#define XK_3270_Left2          0xFD04\n#define XK_3270_BackTab        0xFD05\n#define XK_3270_EraseEOF       0xFD06\n#define XK_3270_EraseInput     0xFD07\n#define XK_3270_Reset          0xFD08\n#define XK_3270_Quit           0xFD09\n#define XK_3270_PA1            0xFD0A\n#define XK_3270_PA2            0xFD0B\n#define XK_3270_PA3            0xFD0C\n#define XK_3270_Test           0xFD0D\n#define XK_3270_Attn           0xFD0E\n#define XK_3270_CursorBlink    0xFD0F\n#define XK_3270_AltCursor      0xFD10\n#define XK_3270_KeyClick       0xFD11\n#define XK_3270_Jump           0xFD12\n#define XK_3270_Ident          0xFD13\n#define XK_3270_Rule           0xFD14\n#define XK_3270_Copy           0xFD15\n#define XK_3270_Play           0xFD16\n#define XK_3270_Setup          0xFD17\n#define XK_3270_Record         0xFD18\n#define XK_3270_ChangeScreen   0xFD19\n#define XK_3270_DeleteWord     0xFD1A\n#define XK_3270_ExSelect       0xFD1B\n#define XK_3270_CursorSelect   0xFD1C\n#define XK_3270_PrintScreen    0xFD1D\n#define XK_3270_Enter          0xFD1E\n#endif\n\n/*\n *  Latin 1\n *  Byte 3 = 0\n */\n#ifdef XK_LATIN1\n#define XK_space               0x020\n#define XK_exclam              0x021\n#define XK_quotedbl            0x022\n#define XK_numbersign          0x023\n#define XK_dollar              0x024\n#define XK_percent             0x025\n#define XK_ampersand           0x026\n#define XK_apostrophe          0x027\n#define XK_quoteright          0x027\t/* deprecated */\n#define XK_parenleft           0x028\n#define XK_parenright          0x029\n#define XK_asterisk            0x02a\n#define XK_plus                0x02b\n#define XK_comma               0x02c\n#define XK_minus               0x02d\n#define XK_period              0x02e\n#define XK_slash               0x02f\n#define XK_0                   0x030\n#define XK_1                   0x031\n#define XK_2                   0x032\n#define XK_3                   0x033\n#define XK_4                   0x034\n#define XK_5                   0x035\n#define XK_6                   0x036\n#define XK_7                   0x037\n#define XK_8                   0x038\n#define XK_9                   0x039\n#define XK_colon               0x03a\n#define XK_semicolon           0x03b\n#define XK_less                0x03c\n#define XK_equal               0x03d\n#define XK_greater             0x03e\n#define XK_question            0x03f\n#define XK_at                  0x040\n#define XK_A                   0x041\n#define XK_B                   0x042\n#define XK_C                   0x043\n#define XK_D                   0x044\n#define XK_E                   0x045\n#define XK_F                   0x046\n#define XK_G                   0x047\n#define XK_H                   0x048\n#define XK_I                   0x049\n#define XK_J                   0x04a\n#define XK_K                   0x04b\n#define XK_L                   0x04c\n#define XK_M                   0x04d\n#define XK_N                   0x04e\n#define XK_O                   0x04f\n#define XK_P                   0x050\n#define XK_Q                   0x051\n#define XK_R                   0x052\n#define XK_S                   0x053\n#define XK_T                   0x054\n#define XK_U                   0x055\n#define XK_V                   0x056\n#define XK_W                   0x057\n#define XK_X                   0x058\n#define XK_Y                   0x059\n#define XK_Z                   0x05a\n#define XK_bracketleft         0x05b\n#define XK_backslash           0x05c\n#define XK_bracketright        0x05d\n#define XK_asciicircum         0x05e\n#define XK_underscore          0x05f\n#define XK_grave               0x060\n#define XK_quoteleft           0x060\t/* deprecated */\n#define XK_a                   0x061\n#define XK_b                   0x062\n#define XK_c                   0x063\n#define XK_d                   0x064\n#define XK_e                   0x065\n#define XK_f                   0x066\n#define XK_g                   0x067\n#define XK_h                   0x068\n#define XK_i                   0x069\n#define XK_j                   0x06a\n#define XK_k                   0x06b\n#define XK_l                   0x06c\n#define XK_m                   0x06d\n#define XK_n                   0x06e\n#define XK_o                   0x06f\n#define XK_p                   0x070\n#define XK_q                   0x071\n#define XK_r                   0x072\n#define XK_s                   0x073\n#define XK_t                   0x074\n#define XK_u                   0x075\n#define XK_v                   0x076\n#define XK_w                   0x077\n#define XK_x                   0x078\n#define XK_y                   0x079\n#define XK_z                   0x07a\n#define XK_braceleft           0x07b\n#define XK_bar                 0x07c\n#define XK_braceright          0x07d\n#define XK_asciitilde          0x07e\n\n#define XK_nobreakspace        0x0a0\n#define XK_exclamdown          0x0a1\n#define XK_cent        \t       0x0a2\n#define XK_sterling            0x0a3\n#define XK_currency            0x0a4\n#define XK_yen                 0x0a5\n#define XK_brokenbar           0x0a6\n#define XK_section             0x0a7\n#define XK_diaeresis           0x0a8\n#define XK_copyright           0x0a9\n#define XK_ordfeminine         0x0aa\n#define XK_guillemotleft       0x0ab\t/* left angle quotation mark */\n#define XK_notsign             0x0ac\n#define XK_hyphen              0x0ad\n#define XK_registered          0x0ae\n#define XK_macron              0x0af\n#define XK_degree              0x0b0\n#define XK_plusminus           0x0b1\n#define XK_twosuperior         0x0b2\n#define XK_threesuperior       0x0b3\n#define XK_acute               0x0b4\n#define XK_mu                  0x0b5\n#define XK_paragraph           0x0b6\n#define XK_periodcentered      0x0b7\n#define XK_cedilla             0x0b8\n#define XK_onesuperior         0x0b9\n#define XK_masculine           0x0ba\n#define XK_guillemotright      0x0bb\t/* right angle quotation mark */\n#define XK_onequarter          0x0bc\n#define XK_onehalf             0x0bd\n#define XK_threequarters       0x0be\n#define XK_questiondown        0x0bf\n#define XK_Agrave              0x0c0\n#define XK_Aacute              0x0c1\n#define XK_Acircumflex         0x0c2\n#define XK_Atilde              0x0c3\n#define XK_Adiaeresis          0x0c4\n#define XK_Aring               0x0c5\n#define XK_AE                  0x0c6\n#define XK_Ccedilla            0x0c7\n#define XK_Egrave              0x0c8\n#define XK_Eacute              0x0c9\n#define XK_Ecircumflex         0x0ca\n#define XK_Ediaeresis          0x0cb\n#define XK_Igrave              0x0cc\n#define XK_Iacute              0x0cd\n#define XK_Icircumflex         0x0ce\n#define XK_Idiaeresis          0x0cf\n#define XK_ETH                 0x0d0\n#define XK_Eth                 0x0d0\t/* deprecated */\n#define XK_Ntilde              0x0d1\n#define XK_Ograve              0x0d2\n#define XK_Oacute              0x0d3\n#define XK_Ocircumflex         0x0d4\n#define XK_Otilde              0x0d5\n#define XK_Odiaeresis          0x0d6\n#define XK_multiply            0x0d7\n#define XK_Ooblique            0x0d8\n#define XK_Oslash              XK_Ooblique\n#define XK_Ugrave              0x0d9\n#define XK_Uacute              0x0da\n#define XK_Ucircumflex         0x0db\n#define XK_Udiaeresis          0x0dc\n#define XK_Yacute              0x0dd\n#define XK_THORN               0x0de\n#define XK_Thorn               0x0de\t/* deprecated */\n#define XK_ssharp              0x0df\n#define XK_agrave              0x0e0\n#define XK_aacute              0x0e1\n#define XK_acircumflex         0x0e2\n#define XK_atilde              0x0e3\n#define XK_adiaeresis          0x0e4\n#define XK_aring               0x0e5\n#define XK_ae                  0x0e6\n#define XK_ccedilla            0x0e7\n#define XK_egrave              0x0e8\n#define XK_eacute              0x0e9\n#define XK_ecircumflex         0x0ea\n#define XK_ediaeresis          0x0eb\n#define XK_igrave              0x0ec\n#define XK_iacute              0x0ed\n#define XK_icircumflex         0x0ee\n#define XK_idiaeresis          0x0ef\n#define XK_eth                 0x0f0\n#define XK_ntilde              0x0f1\n#define XK_ograve              0x0f2\n#define XK_oacute              0x0f3\n#define XK_ocircumflex         0x0f4\n#define XK_otilde              0x0f5\n#define XK_odiaeresis          0x0f6\n#define XK_division            0x0f7\n#define XK_oslash              0x0f8\n#define XK_ooblique            XK_oslash\n#define XK_ugrave              0x0f9\n#define XK_uacute              0x0fa\n#define XK_ucircumflex         0x0fb\n#define XK_udiaeresis          0x0fc\n#define XK_yacute              0x0fd\n#define XK_thorn               0x0fe\n#define XK_ydiaeresis          0x0ff\n#endif /* XK_LATIN1 */\n\n/*\n *   Latin 2\n *   Byte 3 = 1\n */\n\n#ifdef XK_LATIN2\n#define XK_Aogonek             0x1a1\n#define XK_breve               0x1a2\n#define XK_Lstroke             0x1a3\n#define XK_Lcaron              0x1a5\n#define XK_Sacute              0x1a6\n#define XK_Scaron              0x1a9\n#define XK_Scedilla            0x1aa\n#define XK_Tcaron              0x1ab\n#define XK_Zacute              0x1ac\n#define XK_Zcaron              0x1ae\n#define XK_Zabovedot           0x1af\n#define XK_aogonek             0x1b1\n#define XK_ogonek              0x1b2\n#define XK_lstroke             0x1b3\n#define XK_lcaron              0x1b5\n#define XK_sacute              0x1b6\n#define XK_caron               0x1b7\n#define XK_scaron              0x1b9\n#define XK_scedilla            0x1ba\n#define XK_tcaron              0x1bb\n#define XK_zacute              0x1bc\n#define XK_doubleacute         0x1bd\n#define XK_zcaron              0x1be\n#define XK_zabovedot           0x1bf\n#define XK_Racute              0x1c0\n#define XK_Abreve              0x1c3\n#define XK_Lacute              0x1c5\n#define XK_Cacute              0x1c6\n#define XK_Ccaron              0x1c8\n#define XK_Eogonek             0x1ca\n#define XK_Ecaron              0x1cc\n#define XK_Dcaron              0x1cf\n#define XK_Dstroke             0x1d0\n#define XK_Nacute              0x1d1\n#define XK_Ncaron              0x1d2\n#define XK_Odoubleacute        0x1d5\n#define XK_Rcaron              0x1d8\n#define XK_Uring               0x1d9\n#define XK_Udoubleacute        0x1db\n#define XK_Tcedilla            0x1de\n#define XK_racute              0x1e0\n#define XK_abreve              0x1e3\n#define XK_lacute              0x1e5\n#define XK_cacute              0x1e6\n#define XK_ccaron              0x1e8\n#define XK_eogonek             0x1ea\n#define XK_ecaron              0x1ec\n#define XK_dcaron              0x1ef\n#define XK_dstroke             0x1f0\n#define XK_nacute              0x1f1\n#define XK_ncaron              0x1f2\n#define XK_odoubleacute        0x1f5\n#define XK_udoubleacute        0x1fb\n#define XK_rcaron              0x1f8\n#define XK_uring               0x1f9\n#define XK_tcedilla            0x1fe\n#define XK_abovedot            0x1ff\n#endif /* XK_LATIN2 */\n\n/*\n *   Latin 3\n *   Byte 3 = 2\n */\n\n#ifdef XK_LATIN3\n#define XK_Hstroke             0x2a1\n#define XK_Hcircumflex         0x2a6\n#define XK_Iabovedot           0x2a9\n#define XK_Gbreve              0x2ab\n#define XK_Jcircumflex         0x2ac\n#define XK_hstroke             0x2b1\n#define XK_hcircumflex         0x2b6\n#define XK_idotless            0x2b9\n#define XK_gbreve              0x2bb\n#define XK_jcircumflex         0x2bc\n#define XK_Cabovedot           0x2c5\n#define XK_Ccircumflex         0x2c6\n#define XK_Gabovedot           0x2d5\n#define XK_Gcircumflex         0x2d8\n#define XK_Ubreve              0x2dd\n#define XK_Scircumflex         0x2de\n#define XK_cabovedot           0x2e5\n#define XK_ccircumflex         0x2e6\n#define XK_gabovedot           0x2f5\n#define XK_gcircumflex         0x2f8\n#define XK_ubreve              0x2fd\n#define XK_scircumflex         0x2fe\n#endif /* XK_LATIN3 */\n\n\n/*\n *   Latin 4\n *   Byte 3 = 3\n */\n\n#ifdef XK_LATIN4\n#define XK_kra                 0x3a2\n#define XK_kappa               0x3a2\t/* deprecated */\n#define XK_Rcedilla            0x3a3\n#define XK_Itilde              0x3a5\n#define XK_Lcedilla            0x3a6\n#define XK_Emacron             0x3aa\n#define XK_Gcedilla            0x3ab\n#define XK_Tslash              0x3ac\n#define XK_rcedilla            0x3b3\n#define XK_itilde              0x3b5\n#define XK_lcedilla            0x3b6\n#define XK_emacron             0x3ba\n#define XK_gcedilla            0x3bb\n#define XK_tslash              0x3bc\n#define XK_ENG                 0x3bd\n#define XK_eng                 0x3bf\n#define XK_Amacron             0x3c0\n#define XK_Iogonek             0x3c7\n#define XK_Eabovedot           0x3cc\n#define XK_Imacron             0x3cf\n#define XK_Ncedilla            0x3d1\n#define XK_Omacron             0x3d2\n#define XK_Kcedilla            0x3d3\n#define XK_Uogonek             0x3d9\n#define XK_Utilde              0x3dd\n#define XK_Umacron             0x3de\n#define XK_amacron             0x3e0\n#define XK_iogonek             0x3e7\n#define XK_eabovedot           0x3ec\n#define XK_imacron             0x3ef\n#define XK_ncedilla            0x3f1\n#define XK_omacron             0x3f2\n#define XK_kcedilla            0x3f3\n#define XK_uogonek             0x3f9\n#define XK_utilde              0x3fd\n#define XK_umacron             0x3fe\n#endif /* XK_LATIN4 */\n\n/*\n * Latin-8\n * Byte 3 = 18\n */\n#ifdef XK_LATIN8\n#define XK_Babovedot           0x12a1\n#define XK_babovedot           0x12a2\n#define XK_Dabovedot           0x12a6\n#define XK_Wgrave              0x12a8\n#define XK_Wacute              0x12aa\n#define XK_dabovedot           0x12ab\n#define XK_Ygrave              0x12ac\n#define XK_Fabovedot           0x12b0\n#define XK_fabovedot           0x12b1\n#define XK_Mabovedot           0x12b4\n#define XK_mabovedot           0x12b5\n#define XK_Pabovedot           0x12b7\n#define XK_wgrave              0x12b8\n#define XK_pabovedot           0x12b9\n#define XK_wacute              0x12ba\n#define XK_Sabovedot           0x12bb\n#define XK_ygrave              0x12bc\n#define XK_Wdiaeresis          0x12bd\n#define XK_wdiaeresis          0x12be\n#define XK_sabovedot           0x12bf\n#define XK_Wcircumflex         0x12d0\n#define XK_Tabovedot           0x12d7\n#define XK_Ycircumflex         0x12de\n#define XK_wcircumflex         0x12f0\n#define XK_tabovedot           0x12f7\n#define XK_ycircumflex         0x12fe\n#endif /* XK_LATIN8 */\n\n/*\n * Latin-9 (a.k.a. Latin-0)\n * Byte 3 = 19\n */\n\n#ifdef XK_LATIN9\n#define XK_OE                  0x13bc\n#define XK_oe                  0x13bd\n#define XK_Ydiaeresis          0x13be\n#endif /* XK_LATIN9 */\n\n/*\n * Katakana\n * Byte 3 = 4\n */\n\n#ifdef XK_KATAKANA\n#define XK_overline\t\t\t\t       0x47e\n#define XK_kana_fullstop                               0x4a1\n#define XK_kana_openingbracket                         0x4a2\n#define XK_kana_closingbracket                         0x4a3\n#define XK_kana_comma                                  0x4a4\n#define XK_kana_conjunctive                            0x4a5\n#define XK_kana_middledot                              0x4a5  /* deprecated */\n#define XK_kana_WO                                     0x4a6\n#define XK_kana_a                                      0x4a7\n#define XK_kana_i                                      0x4a8\n#define XK_kana_u                                      0x4a9\n#define XK_kana_e                                      0x4aa\n#define XK_kana_o                                      0x4ab\n#define XK_kana_ya                                     0x4ac\n#define XK_kana_yu                                     0x4ad\n#define XK_kana_yo                                     0x4ae\n#define XK_kana_tsu                                    0x4af\n#define XK_kana_tu                                     0x4af  /* deprecated */\n#define XK_prolongedsound                              0x4b0\n#define XK_kana_A                                      0x4b1\n#define XK_kana_I                                      0x4b2\n#define XK_kana_U                                      0x4b3\n#define XK_kana_E                                      0x4b4\n#define XK_kana_O                                      0x4b5\n#define XK_kana_KA                                     0x4b6\n#define XK_kana_KI                                     0x4b7\n#define XK_kana_KU                                     0x4b8\n#define XK_kana_KE                                     0x4b9\n#define XK_kana_KO                                     0x4ba\n#define XK_kana_SA                                     0x4bb\n#define XK_kana_SHI                                    0x4bc\n#define XK_kana_SU                                     0x4bd\n#define XK_kana_SE                                     0x4be\n#define XK_kana_SO                                     0x4bf\n#define XK_kana_TA                                     0x4c0\n#define XK_kana_CHI                                    0x4c1\n#define XK_kana_TI                                     0x4c1  /* deprecated */\n#define XK_kana_TSU                                    0x4c2\n#define XK_kana_TU                                     0x4c2  /* deprecated */\n#define XK_kana_TE                                     0x4c3\n#define XK_kana_TO                                     0x4c4\n#define XK_kana_NA                                     0x4c5\n#define XK_kana_NI                                     0x4c6\n#define XK_kana_NU                                     0x4c7\n#define XK_kana_NE                                     0x4c8\n#define XK_kana_NO                                     0x4c9\n#define XK_kana_HA                                     0x4ca\n#define XK_kana_HI                                     0x4cb\n#define XK_kana_FU                                     0x4cc\n#define XK_kana_HU                                     0x4cc  /* deprecated */\n#define XK_kana_HE                                     0x4cd\n#define XK_kana_HO                                     0x4ce\n#define XK_kana_MA                                     0x4cf\n#define XK_kana_MI                                     0x4d0\n#define XK_kana_MU                                     0x4d1\n#define XK_kana_ME                                     0x4d2\n#define XK_kana_MO                                     0x4d3\n#define XK_kana_YA                                     0x4d4\n#define XK_kana_YU                                     0x4d5\n#define XK_kana_YO                                     0x4d6\n#define XK_kana_RA                                     0x4d7\n#define XK_kana_RI                                     0x4d8\n#define XK_kana_RU                                     0x4d9\n#define XK_kana_RE                                     0x4da\n#define XK_kana_RO                                     0x4db\n#define XK_kana_WA                                     0x4dc\n#define XK_kana_N                                      0x4dd\n#define XK_voicedsound                                 0x4de\n#define XK_semivoicedsound                             0x4df\n#define XK_kana_switch          0xFF7E  /* Alias for mode_switch */\n#endif /* XK_KATAKANA */\n\n/*\n *  Arabic\n *  Byte 3 = 5\n */\n\n#ifdef XK_ARABIC\n#define XK_Farsi_0                                     0x590\n#define XK_Farsi_1                                     0x591\n#define XK_Farsi_2                                     0x592\n#define XK_Farsi_3                                     0x593\n#define XK_Farsi_4                                     0x594\n#define XK_Farsi_5                                     0x595\n#define XK_Farsi_6                                     0x596\n#define XK_Farsi_7                                     0x597\n#define XK_Farsi_8                                     0x598\n#define XK_Farsi_9                                     0x599\n#define XK_Arabic_percent                              0x5a5\n#define XK_Arabic_superscript_alef                     0x5a6\n#define XK_Arabic_tteh                                 0x5a7\n#define XK_Arabic_peh                                  0x5a8\n#define XK_Arabic_tcheh                                0x5a9\n#define XK_Arabic_ddal                                 0x5aa\n#define XK_Arabic_rreh                                 0x5ab\n#define XK_Arabic_comma                                0x5ac\n#define XK_Arabic_fullstop                             0x5ae\n#define XK_Arabic_0                                    0x5b0\n#define XK_Arabic_1                                    0x5b1\n#define XK_Arabic_2                                    0x5b2\n#define XK_Arabic_3                                    0x5b3\n#define XK_Arabic_4                                    0x5b4\n#define XK_Arabic_5                                    0x5b5\n#define XK_Arabic_6                                    0x5b6\n#define XK_Arabic_7                                    0x5b7\n#define XK_Arabic_8                                    0x5b8\n#define XK_Arabic_9                                    0x5b9\n#define XK_Arabic_semicolon                            0x5bb\n#define XK_Arabic_question_mark                        0x5bf\n#define XK_Arabic_hamza                                0x5c1\n#define XK_Arabic_maddaonalef                          0x5c2\n#define XK_Arabic_hamzaonalef                          0x5c3\n#define XK_Arabic_hamzaonwaw                           0x5c4\n#define XK_Arabic_hamzaunderalef                       0x5c5\n#define XK_Arabic_hamzaonyeh                           0x5c6\n#define XK_Arabic_alef                                 0x5c7\n#define XK_Arabic_beh                                  0x5c8\n#define XK_Arabic_tehmarbuta                           0x5c9\n#define XK_Arabic_teh                                  0x5ca\n#define XK_Arabic_theh                                 0x5cb\n#define XK_Arabic_jeem                                 0x5cc\n#define XK_Arabic_hah                                  0x5cd\n#define XK_Arabic_khah                                 0x5ce\n#define XK_Arabic_dal                                  0x5cf\n#define XK_Arabic_thal                                 0x5d0\n#define XK_Arabic_ra                                   0x5d1\n#define XK_Arabic_zain                                 0x5d2\n#define XK_Arabic_seen                                 0x5d3\n#define XK_Arabic_sheen                                0x5d4\n#define XK_Arabic_sad                                  0x5d5\n#define XK_Arabic_dad                                  0x5d6\n#define XK_Arabic_tah                                  0x5d7\n#define XK_Arabic_zah                                  0x5d8\n#define XK_Arabic_ain                                  0x5d9\n#define XK_Arabic_ghain                                0x5da\n#define XK_Arabic_tatweel                              0x5e0\n#define XK_Arabic_feh                                  0x5e1\n#define XK_Arabic_qaf                                  0x5e2\n#define XK_Arabic_kaf                                  0x5e3\n#define XK_Arabic_lam                                  0x5e4\n#define XK_Arabic_meem                                 0x5e5\n#define XK_Arabic_noon                                 0x5e6\n#define XK_Arabic_ha                                   0x5e7\n#define XK_Arabic_heh                                  0x5e7  /* deprecated */\n#define XK_Arabic_waw                                  0x5e8\n#define XK_Arabic_alefmaksura                          0x5e9\n#define XK_Arabic_yeh                                  0x5ea\n#define XK_Arabic_fathatan                             0x5eb\n#define XK_Arabic_dammatan                             0x5ec\n#define XK_Arabic_kasratan                             0x5ed\n#define XK_Arabic_fatha                                0x5ee\n#define XK_Arabic_damma                                0x5ef\n#define XK_Arabic_kasra                                0x5f0\n#define XK_Arabic_shadda                               0x5f1\n#define XK_Arabic_sukun                                0x5f2\n#define XK_Arabic_madda_above                          0x5f3\n#define XK_Arabic_hamza_above                          0x5f4\n#define XK_Arabic_hamza_below                          0x5f5\n#define XK_Arabic_jeh                                  0x5f6\n#define XK_Arabic_veh                                  0x5f7\n#define XK_Arabic_keheh                                0x5f8\n#define XK_Arabic_gaf                                  0x5f9\n#define XK_Arabic_noon_ghunna                          0x5fa\n#define XK_Arabic_heh_doachashmee                      0x5fb\n#define XK_Farsi_yeh                                   0x5fc\n#define XK_Arabic_farsi_yeh                     XK_Farsi_yeh\n#define XK_Arabic_yeh_baree                            0x5fd\n#define XK_Arabic_heh_goal                             0x5fe\n#define XK_Arabic_switch        0xFF7E  /* Alias for mode_switch */\n#endif /* XK_ARABIC */\n\n/*\n * Cyrillic\n * Byte 3 = 6\n */\n#ifdef XK_CYRILLIC\n#define XK_Cyrillic_GHE_bar\t                           0x680\n#define XK_Cyrillic_ghe_bar\t                           0x690\n#define XK_Cyrillic_ZHE_descender\t                   0x681\n#define XK_Cyrillic_zhe_descender\t                   0x691\n#define XK_Cyrillic_KA_descender\t                   0x682\n#define XK_Cyrillic_ka_descender\t                   0x692\n#define XK_Cyrillic_KA_vertstroke\t                   0x683\n#define XK_Cyrillic_ka_vertstroke\t                   0x693\n#define XK_Cyrillic_EN_descender\t                   0x684\n#define XK_Cyrillic_en_descender\t                   0x694\n#define XK_Cyrillic_U_straight\t                       0x685\n#define XK_Cyrillic_u_straight\t                       0x695\n#define XK_Cyrillic_U_straight_bar\t                   0x686\n#define XK_Cyrillic_u_straight_bar\t                   0x696\n#define XK_Cyrillic_HA_descender\t                   0x687\n#define XK_Cyrillic_ha_descender\t                   0x697\n#define XK_Cyrillic_CHE_descender\t                   0x688\n#define XK_Cyrillic_che_descender\t                   0x698\n#define XK_Cyrillic_CHE_vertstroke\t                   0x689\n#define XK_Cyrillic_che_vertstroke\t                   0x699\n#define XK_Cyrillic_SHHA\t                           0x68a\n#define XK_Cyrillic_shha\t                           0x69a\n\n#define XK_Cyrillic_SCHWA\t                           0x68c\n#define XK_Cyrillic_schwa\t                           0x69c\n#define XK_Cyrillic_I_macron\t\t                   0x68d\n#define XK_Cyrillic_i_macron\t\t                   0x69d\n#define XK_Cyrillic_O_bar\t                           0x68e\n#define XK_Cyrillic_o_bar\t                           0x69e\n#define XK_Cyrillic_U_macron\t\t                   0x68f\n#define XK_Cyrillic_u_macron\t\t                   0x69f\n\n#define XK_Serbian_dje                                 0x6a1\n#define XK_Macedonia_gje                               0x6a2\n#define XK_Cyrillic_io                                 0x6a3\n#define XK_Ukrainian_ie                                0x6a4\n#define XK_Ukranian_je                                 0x6a4  /* deprecated */\n#define XK_Macedonia_dse                               0x6a5\n#define XK_Ukrainian_i                                 0x6a6\n#define XK_Ukranian_i                                  0x6a6  /* deprecated */\n#define XK_Ukrainian_yi                                0x6a7\n#define XK_Ukranian_yi                                 0x6a7  /* deprecated */\n#define XK_Cyrillic_je                                 0x6a8\n#define XK_Serbian_je                                  0x6a8  /* deprecated */\n#define XK_Cyrillic_lje                                0x6a9\n#define XK_Serbian_lje                                 0x6a9  /* deprecated */\n#define XK_Cyrillic_nje                                0x6aa\n#define XK_Serbian_nje                                 0x6aa  /* deprecated */\n#define XK_Serbian_tshe                                0x6ab\n#define XK_Macedonia_kje                               0x6ac\n#define XK_Ukrainian_ghe_with_upturn                   0x6ad\n#define XK_Byelorussian_shortu                         0x6ae\n#define XK_Cyrillic_dzhe                               0x6af\n#define XK_Serbian_dze                                 0x6af  /* deprecated */\n#define XK_numerosign                                  0x6b0\n#define XK_Serbian_DJE                                 0x6b1\n#define XK_Macedonia_GJE                               0x6b2\n#define XK_Cyrillic_IO                                 0x6b3\n#define XK_Ukrainian_IE                                0x6b4\n#define XK_Ukranian_JE                                 0x6b4  /* deprecated */\n#define XK_Macedonia_DSE                               0x6b5\n#define XK_Ukrainian_I                                 0x6b6\n#define XK_Ukranian_I                                  0x6b6  /* deprecated */\n#define XK_Ukrainian_YI                                0x6b7\n#define XK_Ukranian_YI                                 0x6b7  /* deprecated */\n#define XK_Cyrillic_JE                                 0x6b8\n#define XK_Serbian_JE                                  0x6b8  /* deprecated */\n#define XK_Cyrillic_LJE                                0x6b9\n#define XK_Serbian_LJE                                 0x6b9  /* deprecated */\n#define XK_Cyrillic_NJE                                0x6ba\n#define XK_Serbian_NJE                                 0x6ba  /* deprecated */\n#define XK_Serbian_TSHE                                0x6bb\n#define XK_Macedonia_KJE                               0x6bc\n#define XK_Ukrainian_GHE_WITH_UPTURN                   0x6bd\n#define XK_Byelorussian_SHORTU                         0x6be\n#define XK_Cyrillic_DZHE                               0x6bf\n#define XK_Serbian_DZE                                 0x6bf  /* deprecated */\n#define XK_Cyrillic_yu                                 0x6c0\n#define XK_Cyrillic_a                                  0x6c1\n#define XK_Cyrillic_be                                 0x6c2\n#define XK_Cyrillic_tse                                0x6c3\n#define XK_Cyrillic_de                                 0x6c4\n#define XK_Cyrillic_ie                                 0x6c5\n#define XK_Cyrillic_ef                                 0x6c6\n#define XK_Cyrillic_ghe                                0x6c7\n#define XK_Cyrillic_ha                                 0x6c8\n#define XK_Cyrillic_i                                  0x6c9\n#define XK_Cyrillic_shorti                             0x6ca\n#define XK_Cyrillic_ka                                 0x6cb\n#define XK_Cyrillic_el                                 0x6cc\n#define XK_Cyrillic_em                                 0x6cd\n#define XK_Cyrillic_en                                 0x6ce\n#define XK_Cyrillic_o                                  0x6cf\n#define XK_Cyrillic_pe                                 0x6d0\n#define XK_Cyrillic_ya                                 0x6d1\n#define XK_Cyrillic_er                                 0x6d2\n#define XK_Cyrillic_es                                 0x6d3\n#define XK_Cyrillic_te                                 0x6d4\n#define XK_Cyrillic_u                                  0x6d5\n#define XK_Cyrillic_zhe                                0x6d6\n#define XK_Cyrillic_ve                                 0x6d7\n#define XK_Cyrillic_softsign                           0x6d8\n#define XK_Cyrillic_yeru                               0x6d9\n#define XK_Cyrillic_ze                                 0x6da\n#define XK_Cyrillic_sha                                0x6db\n#define XK_Cyrillic_e                                  0x6dc\n#define XK_Cyrillic_shcha                              0x6dd\n#define XK_Cyrillic_che                                0x6de\n#define XK_Cyrillic_hardsign                           0x6df\n#define XK_Cyrillic_YU                                 0x6e0\n#define XK_Cyrillic_A                                  0x6e1\n#define XK_Cyrillic_BE                                 0x6e2\n#define XK_Cyrillic_TSE                                0x6e3\n#define XK_Cyrillic_DE                                 0x6e4\n#define XK_Cyrillic_IE                                 0x6e5\n#define XK_Cyrillic_EF                                 0x6e6\n#define XK_Cyrillic_GHE                                0x6e7\n#define XK_Cyrillic_HA                                 0x6e8\n#define XK_Cyrillic_I                                  0x6e9\n#define XK_Cyrillic_SHORTI                             0x6ea\n#define XK_Cyrillic_KA                                 0x6eb\n#define XK_Cyrillic_EL                                 0x6ec\n#define XK_Cyrillic_EM                                 0x6ed\n#define XK_Cyrillic_EN                                 0x6ee\n#define XK_Cyrillic_O                                  0x6ef\n#define XK_Cyrillic_PE                                 0x6f0\n#define XK_Cyrillic_YA                                 0x6f1\n#define XK_Cyrillic_ER                                 0x6f2\n#define XK_Cyrillic_ES                                 0x6f3\n#define XK_Cyrillic_TE                                 0x6f4\n#define XK_Cyrillic_U                                  0x6f5\n#define XK_Cyrillic_ZHE                                0x6f6\n#define XK_Cyrillic_VE                                 0x6f7\n#define XK_Cyrillic_SOFTSIGN                           0x6f8\n#define XK_Cyrillic_YERU                               0x6f9\n#define XK_Cyrillic_ZE                                 0x6fa\n#define XK_Cyrillic_SHA                                0x6fb\n#define XK_Cyrillic_E                                  0x6fc\n#define XK_Cyrillic_SHCHA                              0x6fd\n#define XK_Cyrillic_CHE                                0x6fe\n#define XK_Cyrillic_HARDSIGN                           0x6ff\n#endif /* XK_CYRILLIC */\n\n/*\n * Greek\n * Byte 3 = 7\n */\n\n#ifdef XK_GREEK\n#define XK_Greek_ALPHAaccent                           0x7a1\n#define XK_Greek_EPSILONaccent                         0x7a2\n#define XK_Greek_ETAaccent                             0x7a3\n#define XK_Greek_IOTAaccent                            0x7a4\n#define XK_Greek_IOTAdieresis                          0x7a5\n#define XK_Greek_IOTAdiaeresis         XK_Greek_IOTAdieresis /* old typo */\n#define XK_Greek_OMICRONaccent                         0x7a7\n#define XK_Greek_UPSILONaccent                         0x7a8\n#define XK_Greek_UPSILONdieresis                       0x7a9\n#define XK_Greek_OMEGAaccent                           0x7ab\n#define XK_Greek_accentdieresis                        0x7ae\n#define XK_Greek_horizbar                              0x7af\n#define XK_Greek_alphaaccent                           0x7b1\n#define XK_Greek_epsilonaccent                         0x7b2\n#define XK_Greek_etaaccent                             0x7b3\n#define XK_Greek_iotaaccent                            0x7b4\n#define XK_Greek_iotadieresis                          0x7b5\n#define XK_Greek_iotaaccentdieresis                    0x7b6\n#define XK_Greek_omicronaccent                         0x7b7\n#define XK_Greek_upsilonaccent                         0x7b8\n#define XK_Greek_upsilondieresis                       0x7b9\n#define XK_Greek_upsilonaccentdieresis                 0x7ba\n#define XK_Greek_omegaaccent                           0x7bb\n#define XK_Greek_ALPHA                                 0x7c1\n#define XK_Greek_BETA                                  0x7c2\n#define XK_Greek_GAMMA                                 0x7c3\n#define XK_Greek_DELTA                                 0x7c4\n#define XK_Greek_EPSILON                               0x7c5\n#define XK_Greek_ZETA                                  0x7c6\n#define XK_Greek_ETA                                   0x7c7\n#define XK_Greek_THETA                                 0x7c8\n#define XK_Greek_IOTA                                  0x7c9\n#define XK_Greek_KAPPA                                 0x7ca\n#define XK_Greek_LAMDA                                 0x7cb\n#define XK_Greek_LAMBDA                                0x7cb\n#define XK_Greek_MU                                    0x7cc\n#define XK_Greek_NU                                    0x7cd\n#define XK_Greek_XI                                    0x7ce\n#define XK_Greek_OMICRON                               0x7cf\n#define XK_Greek_PI                                    0x7d0\n#define XK_Greek_RHO                                   0x7d1\n#define XK_Greek_SIGMA                                 0x7d2\n#define XK_Greek_TAU                                   0x7d4\n#define XK_Greek_UPSILON                               0x7d5\n#define XK_Greek_PHI                                   0x7d6\n#define XK_Greek_CHI                                   0x7d7\n#define XK_Greek_PSI                                   0x7d8\n#define XK_Greek_OMEGA                                 0x7d9\n#define XK_Greek_alpha                                 0x7e1\n#define XK_Greek_beta                                  0x7e2\n#define XK_Greek_gamma                                 0x7e3\n#define XK_Greek_delta                                 0x7e4\n#define XK_Greek_epsilon                               0x7e5\n#define XK_Greek_zeta                                  0x7e6\n#define XK_Greek_eta                                   0x7e7\n#define XK_Greek_theta                                 0x7e8\n#define XK_Greek_iota                                  0x7e9\n#define XK_Greek_kappa                                 0x7ea\n#define XK_Greek_lamda                                 0x7eb\n#define XK_Greek_lambda                                0x7eb\n#define XK_Greek_mu                                    0x7ec\n#define XK_Greek_nu                                    0x7ed\n#define XK_Greek_xi                                    0x7ee\n#define XK_Greek_omicron                               0x7ef\n#define XK_Greek_pi                                    0x7f0\n#define XK_Greek_rho                                   0x7f1\n#define XK_Greek_sigma                                 0x7f2\n#define XK_Greek_finalsmallsigma                       0x7f3\n#define XK_Greek_tau                                   0x7f4\n#define XK_Greek_upsilon                               0x7f5\n#define XK_Greek_phi                                   0x7f6\n#define XK_Greek_chi                                   0x7f7\n#define XK_Greek_psi                                   0x7f8\n#define XK_Greek_omega                                 0x7f9\n#define XK_Greek_switch         0xFF7E  /* Alias for mode_switch */\n#endif /* XK_GREEK */\n\n/*\n * Technical\n * Byte 3 = 8\n */\n\n#ifdef XK_TECHNICAL\n#define XK_leftradical                                 0x8a1\n#define XK_topleftradical                              0x8a2\n#define XK_horizconnector                              0x8a3\n#define XK_topintegral                                 0x8a4\n#define XK_botintegral                                 0x8a5\n#define XK_vertconnector                               0x8a6\n#define XK_topleftsqbracket                            0x8a7\n#define XK_botleftsqbracket                            0x8a8\n#define XK_toprightsqbracket                           0x8a9\n#define XK_botrightsqbracket                           0x8aa\n#define XK_topleftparens                               0x8ab\n#define XK_botleftparens                               0x8ac\n#define XK_toprightparens                              0x8ad\n#define XK_botrightparens                              0x8ae\n#define XK_leftmiddlecurlybrace                        0x8af\n#define XK_rightmiddlecurlybrace                       0x8b0\n#define XK_topleftsummation                            0x8b1\n#define XK_botleftsummation                            0x8b2\n#define XK_topvertsummationconnector                   0x8b3\n#define XK_botvertsummationconnector                   0x8b4\n#define XK_toprightsummation                           0x8b5\n#define XK_botrightsummation                           0x8b6\n#define XK_rightmiddlesummation                        0x8b7\n#define XK_lessthanequal                               0x8bc\n#define XK_notequal                                    0x8bd\n#define XK_greaterthanequal                            0x8be\n#define XK_integral                                    0x8bf\n#define XK_therefore                                   0x8c0\n#define XK_variation                                   0x8c1\n#define XK_infinity                                    0x8c2\n#define XK_nabla                                       0x8c5\n#define XK_approximate                                 0x8c8\n#define XK_similarequal                                0x8c9\n#define XK_ifonlyif                                    0x8cd\n#define XK_implies                                     0x8ce\n#define XK_identical                                   0x8cf\n#define XK_radical                                     0x8d6\n#define XK_includedin                                  0x8da\n#define XK_includes                                    0x8db\n#define XK_intersection                                0x8dc\n#define XK_union                                       0x8dd\n#define XK_logicaland                                  0x8de\n#define XK_logicalor                                   0x8df\n#define XK_partialderivative                           0x8ef\n#define XK_function                                    0x8f6\n#define XK_leftarrow                                   0x8fb\n#define XK_uparrow                                     0x8fc\n#define XK_rightarrow                                  0x8fd\n#define XK_downarrow                                   0x8fe\n#endif /* XK_TECHNICAL */\n\n/*\n *  Special\n *  Byte 3 = 9\n */\n\n#ifdef XK_SPECIAL\n#define XK_blank                                       0x9df\n#define XK_soliddiamond                                0x9e0\n#define XK_checkerboard                                0x9e1\n#define XK_ht                                          0x9e2\n#define XK_ff                                          0x9e3\n#define XK_cr                                          0x9e4\n#define XK_lf                                          0x9e5\n#define XK_nl                                          0x9e8\n#define XK_vt                                          0x9e9\n#define XK_lowrightcorner                              0x9ea\n#define XK_uprightcorner                               0x9eb\n#define XK_upleftcorner                                0x9ec\n#define XK_lowleftcorner                               0x9ed\n#define XK_crossinglines                               0x9ee\n#define XK_horizlinescan1                              0x9ef\n#define XK_horizlinescan3                              0x9f0\n#define XK_horizlinescan5                              0x9f1\n#define XK_horizlinescan7                              0x9f2\n#define XK_horizlinescan9                              0x9f3\n#define XK_leftt                                       0x9f4\n#define XK_rightt                                      0x9f5\n#define XK_bott                                        0x9f6\n#define XK_topt                                        0x9f7\n#define XK_vertbar                                     0x9f8\n#endif /* XK_SPECIAL */\n\n/*\n *  Publishing\n *  Byte 3 = a\n */\n\n#ifdef XK_PUBLISHING\n#define XK_emspace                                     0xaa1\n#define XK_enspace                                     0xaa2\n#define XK_em3space                                    0xaa3\n#define XK_em4space                                    0xaa4\n#define XK_digitspace                                  0xaa5\n#define XK_punctspace                                  0xaa6\n#define XK_thinspace                                   0xaa7\n#define XK_hairspace                                   0xaa8\n#define XK_emdash                                      0xaa9\n#define XK_endash                                      0xaaa\n#define XK_signifblank                                 0xaac\n#define XK_ellipsis                                    0xaae\n#define XK_doubbaselinedot                             0xaaf\n#define XK_onethird                                    0xab0\n#define XK_twothirds                                   0xab1\n#define XK_onefifth                                    0xab2\n#define XK_twofifths                                   0xab3\n#define XK_threefifths                                 0xab4\n#define XK_fourfifths                                  0xab5\n#define XK_onesixth                                    0xab6\n#define XK_fivesixths                                  0xab7\n#define XK_careof                                      0xab8\n#define XK_figdash                                     0xabb\n#define XK_leftanglebracket                            0xabc\n#define XK_decimalpoint                                0xabd\n#define XK_rightanglebracket                           0xabe\n#define XK_marker                                      0xabf\n#define XK_oneeighth                                   0xac3\n#define XK_threeeighths                                0xac4\n#define XK_fiveeighths                                 0xac5\n#define XK_seveneighths                                0xac6\n#define XK_trademark                                   0xac9\n#define XK_signaturemark                               0xaca\n#define XK_trademarkincircle                           0xacb\n#define XK_leftopentriangle                            0xacc\n#define XK_rightopentriangle                           0xacd\n#define XK_emopencircle                                0xace\n#define XK_emopenrectangle                             0xacf\n#define XK_leftsinglequotemark                         0xad0\n#define XK_rightsinglequotemark                        0xad1\n#define XK_leftdoublequotemark                         0xad2\n#define XK_rightdoublequotemark                        0xad3\n#define XK_prescription                                0xad4\n#define XK_minutes                                     0xad6\n#define XK_seconds                                     0xad7\n#define XK_latincross                                  0xad9\n#define XK_hexagram                                    0xada\n#define XK_filledrectbullet                            0xadb\n#define XK_filledlefttribullet                         0xadc\n#define XK_filledrighttribullet                        0xadd\n#define XK_emfilledcircle                              0xade\n#define XK_emfilledrect                                0xadf\n#define XK_enopencircbullet                            0xae0\n#define XK_enopensquarebullet                          0xae1\n#define XK_openrectbullet                              0xae2\n#define XK_opentribulletup                             0xae3\n#define XK_opentribulletdown                           0xae4\n#define XK_openstar                                    0xae5\n#define XK_enfilledcircbullet                          0xae6\n#define XK_enfilledsqbullet                            0xae7\n#define XK_filledtribulletup                           0xae8\n#define XK_filledtribulletdown                         0xae9\n#define XK_leftpointer                                 0xaea\n#define XK_rightpointer                                0xaeb\n#define XK_club                                        0xaec\n#define XK_diamond                                     0xaed\n#define XK_heart                                       0xaee\n#define XK_maltesecross                                0xaf0\n#define XK_dagger                                      0xaf1\n#define XK_doubledagger                                0xaf2\n#define XK_checkmark                                   0xaf3\n#define XK_ballotcross                                 0xaf4\n#define XK_musicalsharp                                0xaf5\n#define XK_musicalflat                                 0xaf6\n#define XK_malesymbol                                  0xaf7\n#define XK_femalesymbol                                0xaf8\n#define XK_telephone                                   0xaf9\n#define XK_telephonerecorder                           0xafa\n#define XK_phonographcopyright                         0xafb\n#define XK_caret                                       0xafc\n#define XK_singlelowquotemark                          0xafd\n#define XK_doublelowquotemark                          0xafe\n#define XK_cursor                                      0xaff\n#endif /* XK_PUBLISHING */\n\n/*\n *  APL\n *  Byte 3 = b\n */\n\n#ifdef XK_APL\n#define XK_leftcaret                                   0xba3\n#define XK_rightcaret                                  0xba6\n#define XK_downcaret                                   0xba8\n#define XK_upcaret                                     0xba9\n#define XK_overbar                                     0xbc0\n#define XK_downtack                                    0xbc2\n#define XK_upshoe                                      0xbc3\n#define XK_downstile                                   0xbc4\n#define XK_underbar                                    0xbc6\n#define XK_jot                                         0xbca\n#define XK_quad                                        0xbcc\n#define XK_uptack                                      0xbce\n#define XK_circle                                      0xbcf\n#define XK_upstile                                     0xbd3\n#define XK_downshoe                                    0xbd6\n#define XK_rightshoe                                   0xbd8\n#define XK_leftshoe                                    0xbda\n#define XK_lefttack                                    0xbdc\n#define XK_righttack                                   0xbfc\n#endif /* XK_APL */\n\n/*\n * Hebrew\n * Byte 3 = c\n */\n\n#ifdef XK_HEBREW\n#define XK_hebrew_doublelowline                        0xcdf\n#define XK_hebrew_aleph                                0xce0\n#define XK_hebrew_bet                                  0xce1\n#define XK_hebrew_beth                                 0xce1  /* deprecated */\n#define XK_hebrew_gimel                                0xce2\n#define XK_hebrew_gimmel                               0xce2  /* deprecated */\n#define XK_hebrew_dalet                                0xce3\n#define XK_hebrew_daleth                               0xce3  /* deprecated */\n#define XK_hebrew_he                                   0xce4\n#define XK_hebrew_waw                                  0xce5\n#define XK_hebrew_zain                                 0xce6\n#define XK_hebrew_zayin                                0xce6  /* deprecated */\n#define XK_hebrew_chet                                 0xce7\n#define XK_hebrew_het                                  0xce7  /* deprecated */\n#define XK_hebrew_tet                                  0xce8\n#define XK_hebrew_teth                                 0xce8  /* deprecated */\n#define XK_hebrew_yod                                  0xce9\n#define XK_hebrew_finalkaph                            0xcea\n#define XK_hebrew_kaph                                 0xceb\n#define XK_hebrew_lamed                                0xcec\n#define XK_hebrew_finalmem                             0xced\n#define XK_hebrew_mem                                  0xcee\n#define XK_hebrew_finalnun                             0xcef\n#define XK_hebrew_nun                                  0xcf0\n#define XK_hebrew_samech                               0xcf1\n#define XK_hebrew_samekh                               0xcf1  /* deprecated */\n#define XK_hebrew_ayin                                 0xcf2\n#define XK_hebrew_finalpe                              0xcf3\n#define XK_hebrew_pe                                   0xcf4\n#define XK_hebrew_finalzade                            0xcf5\n#define XK_hebrew_finalzadi                            0xcf5  /* deprecated */\n#define XK_hebrew_zade                                 0xcf6\n#define XK_hebrew_zadi                                 0xcf6  /* deprecated */\n#define XK_hebrew_qoph                                 0xcf7\n#define XK_hebrew_kuf                                  0xcf7  /* deprecated */\n#define XK_hebrew_resh                                 0xcf8\n#define XK_hebrew_shin                                 0xcf9\n#define XK_hebrew_taw                                  0xcfa\n#define XK_hebrew_taf                                  0xcfa  /* deprecated */\n#define XK_Hebrew_switch        0xFF7E  /* Alias for mode_switch */\n#endif /* XK_HEBREW */\n\n/*\n * Thai\n * Byte 3 = d\n */\n\n#ifdef XK_THAI\n#define XK_Thai_kokai\t\t\t\t\t0xda1\n#define XK_Thai_khokhai\t\t\t\t\t0xda2\n#define XK_Thai_khokhuat\t\t\t\t0xda3\n#define XK_Thai_khokhwai\t\t\t\t0xda4\n#define XK_Thai_khokhon\t\t\t\t\t0xda5\n#define XK_Thai_khorakhang\t\t\t        0xda6  \n#define XK_Thai_ngongu\t\t\t\t\t0xda7  \n#define XK_Thai_chochan\t\t\t\t\t0xda8  \n#define XK_Thai_choching\t\t\t\t0xda9   \n#define XK_Thai_chochang\t\t\t\t0xdaa  \n#define XK_Thai_soso\t\t\t\t\t0xdab\n#define XK_Thai_chochoe\t\t\t\t\t0xdac\n#define XK_Thai_yoying\t\t\t\t\t0xdad\n#define XK_Thai_dochada\t\t\t\t\t0xdae\n#define XK_Thai_topatak\t\t\t\t\t0xdaf\n#define XK_Thai_thothan\t\t\t\t\t0xdb0\n#define XK_Thai_thonangmontho\t\t\t        0xdb1\n#define XK_Thai_thophuthao\t\t\t        0xdb2\n#define XK_Thai_nonen\t\t\t\t\t0xdb3\n#define XK_Thai_dodek\t\t\t\t\t0xdb4\n#define XK_Thai_totao\t\t\t\t\t0xdb5\n#define XK_Thai_thothung\t\t\t\t0xdb6\n#define XK_Thai_thothahan\t\t\t\t0xdb7\n#define XK_Thai_thothong\t \t\t\t0xdb8\n#define XK_Thai_nonu\t\t\t\t\t0xdb9\n#define XK_Thai_bobaimai\t\t\t\t0xdba\n#define XK_Thai_popla\t\t\t\t\t0xdbb\n#define XK_Thai_phophung\t\t\t\t0xdbc\n#define XK_Thai_fofa\t\t\t\t\t0xdbd\n#define XK_Thai_phophan\t\t\t\t\t0xdbe\n#define XK_Thai_fofan\t\t\t\t\t0xdbf\n#define XK_Thai_phosamphao\t\t\t        0xdc0\n#define XK_Thai_moma\t\t\t\t\t0xdc1\n#define XK_Thai_yoyak\t\t\t\t\t0xdc2\n#define XK_Thai_rorua\t\t\t\t\t0xdc3\n#define XK_Thai_ru\t\t\t\t\t0xdc4\n#define XK_Thai_loling\t\t\t\t\t0xdc5\n#define XK_Thai_lu\t\t\t\t\t0xdc6\n#define XK_Thai_wowaen\t\t\t\t\t0xdc7\n#define XK_Thai_sosala\t\t\t\t\t0xdc8\n#define XK_Thai_sorusi\t\t\t\t\t0xdc9\n#define XK_Thai_sosua\t\t\t\t\t0xdca\n#define XK_Thai_hohip\t\t\t\t\t0xdcb\n#define XK_Thai_lochula\t\t\t\t\t0xdcc\n#define XK_Thai_oang\t\t\t\t\t0xdcd\n#define XK_Thai_honokhuk\t\t\t\t0xdce\n#define XK_Thai_paiyannoi\t\t\t\t0xdcf\n#define XK_Thai_saraa\t\t\t\t\t0xdd0\n#define XK_Thai_maihanakat\t\t\t\t0xdd1\n#define XK_Thai_saraaa\t\t\t\t\t0xdd2\n#define XK_Thai_saraam\t\t\t\t\t0xdd3\n#define XK_Thai_sarai\t\t\t\t\t0xdd4   \n#define XK_Thai_saraii\t\t\t\t\t0xdd5   \n#define XK_Thai_saraue\t\t\t\t\t0xdd6    \n#define XK_Thai_sarauee\t\t\t\t\t0xdd7    \n#define XK_Thai_sarau\t\t\t\t\t0xdd8    \n#define XK_Thai_sarauu\t\t\t\t\t0xdd9   \n#define XK_Thai_phinthu\t\t\t\t\t0xdda\n#define XK_Thai_maihanakat_maitho   \t\t\t0xdde\n#define XK_Thai_baht\t\t\t\t\t0xddf\n#define XK_Thai_sarae\t\t\t\t\t0xde0    \n#define XK_Thai_saraae\t\t\t\t\t0xde1\n#define XK_Thai_sarao\t\t\t\t\t0xde2\n#define XK_Thai_saraaimaimuan\t\t\t\t0xde3   \n#define XK_Thai_saraaimaimalai\t\t\t\t0xde4  \n#define XK_Thai_lakkhangyao\t\t\t\t0xde5\n#define XK_Thai_maiyamok\t\t\t\t0xde6\n#define XK_Thai_maitaikhu\t\t\t\t0xde7\n#define XK_Thai_maiek\t\t\t\t\t0xde8   \n#define XK_Thai_maitho\t\t\t\t\t0xde9\n#define XK_Thai_maitri\t\t\t\t\t0xdea\n#define XK_Thai_maichattawa\t\t\t\t0xdeb\n#define XK_Thai_thanthakhat\t\t\t\t0xdec\n#define XK_Thai_nikhahit\t\t\t\t0xded\n#define XK_Thai_leksun\t\t\t\t\t0xdf0 \n#define XK_Thai_leknung\t\t\t\t\t0xdf1  \n#define XK_Thai_leksong\t\t\t\t\t0xdf2 \n#define XK_Thai_leksam\t\t\t\t\t0xdf3\n#define XK_Thai_leksi\t\t\t\t\t0xdf4  \n#define XK_Thai_lekha\t\t\t\t\t0xdf5  \n#define XK_Thai_lekhok\t\t\t\t\t0xdf6  \n#define XK_Thai_lekchet\t\t\t\t\t0xdf7  \n#define XK_Thai_lekpaet\t\t\t\t\t0xdf8  \n#define XK_Thai_lekkao\t\t\t\t\t0xdf9 \n#endif /* XK_THAI */\n\n/*\n *   Korean\n *   Byte 3 = e\n */\n\n#ifdef XK_KOREAN\n\n#define XK_Hangul\t\t0xff31    /* Hangul start/stop(toggle) */\n#define XK_Hangul_Start\t\t0xff32    /* Hangul start */\n#define XK_Hangul_End\t\t0xff33    /* Hangul end, English start */\n#define XK_Hangul_Hanja\t\t0xff34    /* Start Hangul->Hanja Conversion */\n#define XK_Hangul_Jamo\t\t0xff35    /* Hangul Jamo mode */\n#define XK_Hangul_Romaja\t0xff36    /* Hangul Romaja mode */\n#define XK_Hangul_Codeinput\t0xff37    /* Hangul code input mode */\n#define XK_Hangul_Jeonja\t0xff38    /* Jeonja mode */\n#define XK_Hangul_Banja\t\t0xff39    /* Banja mode */\n#define XK_Hangul_PreHanja\t0xff3a    /* Pre Hanja conversion */\n#define XK_Hangul_PostHanja\t0xff3b    /* Post Hanja conversion */\n#define XK_Hangul_SingleCandidate\t0xff3c    /* Single candidate */\n#define XK_Hangul_MultipleCandidate\t0xff3d    /* Multiple candidate */\n#define XK_Hangul_PreviousCandidate\t0xff3e    /* Previous candidate */\n#define XK_Hangul_Special\t0xff3f    /* Special symbols */\n#define XK_Hangul_switch\t0xFF7E    /* Alias for mode_switch */\n\n/* Hangul Consonant Characters */\n#define XK_Hangul_Kiyeog\t\t\t\t0xea1\n#define XK_Hangul_SsangKiyeog\t\t\t\t0xea2\n#define XK_Hangul_KiyeogSios\t\t\t\t0xea3\n#define XK_Hangul_Nieun\t\t\t\t\t0xea4\n#define XK_Hangul_NieunJieuj\t\t\t\t0xea5\n#define XK_Hangul_NieunHieuh\t\t\t\t0xea6\n#define XK_Hangul_Dikeud\t\t\t\t0xea7\n#define XK_Hangul_SsangDikeud\t\t\t\t0xea8\n#define XK_Hangul_Rieul\t\t\t\t\t0xea9\n#define XK_Hangul_RieulKiyeog\t\t\t\t0xeaa\n#define XK_Hangul_RieulMieum\t\t\t\t0xeab\n#define XK_Hangul_RieulPieub\t\t\t\t0xeac\n#define XK_Hangul_RieulSios\t\t\t\t0xead\n#define XK_Hangul_RieulTieut\t\t\t\t0xeae\n#define XK_Hangul_RieulPhieuf\t\t\t\t0xeaf\n#define XK_Hangul_RieulHieuh\t\t\t\t0xeb0\n#define XK_Hangul_Mieum\t\t\t\t\t0xeb1\n#define XK_Hangul_Pieub\t\t\t\t\t0xeb2\n#define XK_Hangul_SsangPieub\t\t\t\t0xeb3\n#define XK_Hangul_PieubSios\t\t\t\t0xeb4\n#define XK_Hangul_Sios\t\t\t\t\t0xeb5\n#define XK_Hangul_SsangSios\t\t\t\t0xeb6\n#define XK_Hangul_Ieung\t\t\t\t\t0xeb7\n#define XK_Hangul_Jieuj\t\t\t\t\t0xeb8\n#define XK_Hangul_SsangJieuj\t\t\t\t0xeb9\n#define XK_Hangul_Cieuc\t\t\t\t\t0xeba\n#define XK_Hangul_Khieuq\t\t\t\t0xebb\n#define XK_Hangul_Tieut\t\t\t\t\t0xebc\n#define XK_Hangul_Phieuf\t\t\t\t0xebd\n#define XK_Hangul_Hieuh\t\t\t\t\t0xebe\n\n/* Hangul Vowel Characters */\n#define XK_Hangul_A\t\t\t\t\t0xebf\n#define XK_Hangul_AE\t\t\t\t\t0xec0\n#define XK_Hangul_YA\t\t\t\t\t0xec1\n#define XK_Hangul_YAE\t\t\t\t\t0xec2\n#define XK_Hangul_EO\t\t\t\t\t0xec3\n#define XK_Hangul_E\t\t\t\t\t0xec4\n#define XK_Hangul_YEO\t\t\t\t\t0xec5\n#define XK_Hangul_YE\t\t\t\t\t0xec6\n#define XK_Hangul_O\t\t\t\t\t0xec7\n#define XK_Hangul_WA\t\t\t\t\t0xec8\n#define XK_Hangul_WAE\t\t\t\t\t0xec9\n#define XK_Hangul_OE\t\t\t\t\t0xeca\n#define XK_Hangul_YO\t\t\t\t\t0xecb\n#define XK_Hangul_U\t\t\t\t\t0xecc\n#define XK_Hangul_WEO\t\t\t\t\t0xecd\n#define XK_Hangul_WE\t\t\t\t\t0xece\n#define XK_Hangul_WI\t\t\t\t\t0xecf\n#define XK_Hangul_YU\t\t\t\t\t0xed0\n#define XK_Hangul_EU\t\t\t\t\t0xed1\n#define XK_Hangul_YI\t\t\t\t\t0xed2\n#define XK_Hangul_I\t\t\t\t\t0xed3\n\n/* Hangul syllable-final (JongSeong) Characters */\n#define XK_Hangul_J_Kiyeog\t\t\t\t0xed4\n#define XK_Hangul_J_SsangKiyeog\t\t\t\t0xed5\n#define XK_Hangul_J_KiyeogSios\t\t\t\t0xed6\n#define XK_Hangul_J_Nieun\t\t\t\t0xed7\n#define XK_Hangul_J_NieunJieuj\t\t\t\t0xed8\n#define XK_Hangul_J_NieunHieuh\t\t\t\t0xed9\n#define XK_Hangul_J_Dikeud\t\t\t\t0xeda\n#define XK_Hangul_J_Rieul\t\t\t\t0xedb\n#define XK_Hangul_J_RieulKiyeog\t\t\t\t0xedc\n#define XK_Hangul_J_RieulMieum\t\t\t\t0xedd\n#define XK_Hangul_J_RieulPieub\t\t\t\t0xede\n#define XK_Hangul_J_RieulSios\t\t\t\t0xedf\n#define XK_Hangul_J_RieulTieut\t\t\t\t0xee0\n#define XK_Hangul_J_RieulPhieuf\t\t\t\t0xee1\n#define XK_Hangul_J_RieulHieuh\t\t\t\t0xee2\n#define XK_Hangul_J_Mieum\t\t\t\t0xee3\n#define XK_Hangul_J_Pieub\t\t\t\t0xee4\n#define XK_Hangul_J_PieubSios\t\t\t\t0xee5\n#define XK_Hangul_J_Sios\t\t\t\t0xee6\n#define XK_Hangul_J_SsangSios\t\t\t\t0xee7\n#define XK_Hangul_J_Ieung\t\t\t\t0xee8\n#define XK_Hangul_J_Jieuj\t\t\t\t0xee9\n#define XK_Hangul_J_Cieuc\t\t\t\t0xeea\n#define XK_Hangul_J_Khieuq\t\t\t\t0xeeb\n#define XK_Hangul_J_Tieut\t\t\t\t0xeec\n#define XK_Hangul_J_Phieuf\t\t\t\t0xeed\n#define XK_Hangul_J_Hieuh\t\t\t\t0xeee\n\n/* Ancient Hangul Consonant Characters */\n#define XK_Hangul_RieulYeorinHieuh\t\t\t0xeef\n#define XK_Hangul_SunkyeongeumMieum\t\t\t0xef0\n#define XK_Hangul_SunkyeongeumPieub\t\t\t0xef1\n#define XK_Hangul_PanSios\t\t\t\t0xef2\n#define XK_Hangul_KkogjiDalrinIeung\t\t\t0xef3\n#define XK_Hangul_SunkyeongeumPhieuf\t\t\t0xef4\n#define XK_Hangul_YeorinHieuh\t\t\t\t0xef5\n\n/* Ancient Hangul Vowel Characters */\n#define XK_Hangul_AraeA\t\t\t\t\t0xef6\n#define XK_Hangul_AraeAE\t\t\t\t0xef7\n\n/* Ancient Hangul syllable-final (JongSeong) Characters */\n#define XK_Hangul_J_PanSios\t\t\t\t0xef8\n#define XK_Hangul_J_KkogjiDalrinIeung\t\t\t0xef9\n#define XK_Hangul_J_YeorinHieuh\t\t\t\t0xefa\n\n/* Korean currency symbol */\n#define XK_Korean_Won\t\t\t\t\t0xeff\n\n#endif /* XK_KOREAN */\n\n/*\n *   Armenian\n *   Byte 3 = 0x14\n */\n\n#ifdef XK_ARMENIAN\n#define XK_Armenian_eternity\t\t\t\t0x14a1\n#define XK_Armenian_ligature_ew\t\t\t\t0x14a2\n#define XK_Armenian_full_stop\t\t\t\t0x14a3\n#define XK_Armenian_verjaket\t\t\t\t0x14a3\n#define XK_Armenian_parenright\t\t\t\t0x14a4\n#define XK_Armenian_parenleft\t\t\t\t0x14a5\n#define XK_Armenian_guillemotright\t\t\t0x14a6\n#define XK_Armenian_guillemotleft\t\t\t0x14a7\n#define XK_Armenian_em_dash\t\t\t\t0x14a8\n#define XK_Armenian_dot\t\t\t\t\t0x14a9\n#define XK_Armenian_mijaket\t\t\t\t0x14a9\n#define XK_Armenian_separation_mark\t\t\t0x14aa\n#define XK_Armenian_but\t\t\t\t\t0x14aa\n#define XK_Armenian_comma\t\t\t\t0x14ab\n#define XK_Armenian_en_dash\t\t\t\t0x14ac\n#define XK_Armenian_hyphen\t\t\t\t0x14ad\n#define XK_Armenian_yentamna\t\t\t\t0x14ad\n#define XK_Armenian_ellipsis\t\t\t\t0x14ae\n#define XK_Armenian_exclam\t\t\t\t0x14af\n#define XK_Armenian_amanak\t\t\t\t0x14af\n#define XK_Armenian_accent\t\t\t\t0x14b0\n#define XK_Armenian_shesht\t\t\t\t0x14b0\n#define XK_Armenian_question\t\t\t\t0x14b1\n#define XK_Armenian_paruyk\t\t\t\t0x14b1\n#define XK_Armenian_AYB\t\t\t\t\t0x14b2\n#define XK_Armenian_ayb\t\t\t\t\t0x14b3\n#define XK_Armenian_BEN\t\t\t\t\t0x14b4\n#define XK_Armenian_ben\t\t\t\t\t0x14b5\n#define XK_Armenian_GIM\t\t\t\t\t0x14b6\n#define XK_Armenian_gim\t\t\t\t\t0x14b7\n#define XK_Armenian_DA\t\t\t\t\t0x14b8\n#define XK_Armenian_da\t\t\t\t\t0x14b9\n#define XK_Armenian_YECH\t\t\t\t0x14ba\n#define XK_Armenian_yech\t\t\t\t0x14bb\n#define XK_Armenian_ZA\t\t\t\t\t0x14bc\n#define XK_Armenian_za\t\t\t\t\t0x14bd\n#define XK_Armenian_E\t\t\t\t\t0x14be\n#define XK_Armenian_e\t\t\t\t\t0x14bf\n#define XK_Armenian_AT\t\t\t\t\t0x14c0\n#define XK_Armenian_at\t\t\t\t\t0x14c1\n#define XK_Armenian_TO\t\t\t\t\t0x14c2\n#define XK_Armenian_to\t\t\t\t\t0x14c3\n#define XK_Armenian_ZHE\t\t\t\t\t0x14c4\n#define XK_Armenian_zhe\t\t\t\t\t0x14c5\n#define XK_Armenian_INI\t\t\t\t\t0x14c6\n#define XK_Armenian_ini\t\t\t\t\t0x14c7\n#define XK_Armenian_LYUN\t\t\t\t0x14c8\n#define XK_Armenian_lyun\t\t\t\t0x14c9\n#define XK_Armenian_KHE\t\t\t\t\t0x14ca\n#define XK_Armenian_khe\t\t\t\t\t0x14cb\n#define XK_Armenian_TSA\t\t\t\t\t0x14cc\n#define XK_Armenian_tsa\t\t\t\t\t0x14cd\n#define XK_Armenian_KEN\t\t\t\t\t0x14ce\n#define XK_Armenian_ken\t\t\t\t\t0x14cf\n#define XK_Armenian_HO\t\t\t\t\t0x14d0\n#define XK_Armenian_ho\t\t\t\t\t0x14d1\n#define XK_Armenian_DZA\t\t\t\t\t0x14d2\n#define XK_Armenian_dza\t\t\t\t\t0x14d3\n#define XK_Armenian_GHAT\t\t\t\t0x14d4\n#define XK_Armenian_ghat\t\t\t\t0x14d5\n#define XK_Armenian_TCHE\t\t\t\t0x14d6\n#define XK_Armenian_tche\t\t\t\t0x14d7\n#define XK_Armenian_MEN\t\t\t\t\t0x14d8\n#define XK_Armenian_men\t\t\t\t\t0x14d9\n#define XK_Armenian_HI\t\t\t\t\t0x14da\n#define XK_Armenian_hi\t\t\t\t\t0x14db\n#define XK_Armenian_NU\t\t\t\t\t0x14dc\n#define XK_Armenian_nu\t\t\t\t\t0x14dd\n#define XK_Armenian_SHA\t\t\t\t\t0x14de\n#define XK_Armenian_sha\t\t\t\t\t0x14df\n#define XK_Armenian_VO\t\t\t\t\t0x14e0\n#define XK_Armenian_vo\t\t\t\t\t0x14e1\n#define XK_Armenian_CHA\t\t\t\t\t0x14e2\n#define XK_Armenian_cha\t\t\t\t\t0x14e3\n#define XK_Armenian_PE\t\t\t\t\t0x14e4\n#define XK_Armenian_pe\t\t\t\t\t0x14e5\n#define XK_Armenian_JE\t\t\t\t\t0x14e6\n#define XK_Armenian_je\t\t\t\t\t0x14e7\n#define XK_Armenian_RA\t\t\t\t\t0x14e8\n#define XK_Armenian_ra\t\t\t\t\t0x14e9\n#define XK_Armenian_SE\t\t\t\t\t0x14ea\n#define XK_Armenian_se\t\t\t\t\t0x14eb\n#define XK_Armenian_VEV\t\t\t\t\t0x14ec\n#define XK_Armenian_vev\t\t\t\t\t0x14ed\n#define XK_Armenian_TYUN\t\t\t\t0x14ee\n#define XK_Armenian_tyun\t\t\t\t0x14ef\n#define XK_Armenian_RE\t\t\t\t\t0x14f0\n#define XK_Armenian_re\t\t\t\t\t0x14f1\n#define XK_Armenian_TSO\t\t\t\t\t0x14f2\n#define XK_Armenian_tso\t\t\t\t\t0x14f3\n#define XK_Armenian_VYUN\t\t\t\t0x14f4\n#define XK_Armenian_vyun\t\t\t\t0x14f5\n#define XK_Armenian_PYUR\t\t\t\t0x14f6\n#define XK_Armenian_pyur\t\t\t\t0x14f7\n#define XK_Armenian_KE\t\t\t\t\t0x14f8\n#define XK_Armenian_ke\t\t\t\t\t0x14f9\n#define XK_Armenian_O\t\t\t\t\t0x14fa\n#define XK_Armenian_o\t\t\t\t\t0x14fb\n#define XK_Armenian_FE\t\t\t\t\t0x14fc\n#define XK_Armenian_fe\t\t\t\t\t0x14fd\n#define XK_Armenian_apostrophe\t\t\t\t0x14fe\n#define XK_Armenian_section_sign\t\t\t0x14ff\n#endif /* XK_ARMENIAN */\n\n/*\n *   Georgian\n *   Byte 3 = 0x15\n */\n\n#ifdef XK_GEORGIAN\n#define XK_Georgian_an\t\t\t\t\t0x15d0\n#define XK_Georgian_ban\t\t\t\t\t0x15d1\n#define XK_Georgian_gan\t\t\t\t\t0x15d2\n#define XK_Georgian_don\t\t\t\t\t0x15d3\n#define XK_Georgian_en\t\t\t\t\t0x15d4\n#define XK_Georgian_vin\t\t\t\t\t0x15d5\n#define XK_Georgian_zen\t\t\t\t\t0x15d6\n#define XK_Georgian_tan\t\t\t\t\t0x15d7\n#define XK_Georgian_in\t\t\t\t\t0x15d8\n#define XK_Georgian_kan\t\t\t\t\t0x15d9\n#define XK_Georgian_las\t\t\t\t\t0x15da\n#define XK_Georgian_man\t\t\t\t\t0x15db\n#define XK_Georgian_nar\t\t\t\t\t0x15dc\n#define XK_Georgian_on\t\t\t\t\t0x15dd\n#define XK_Georgian_par\t\t\t\t\t0x15de\n#define XK_Georgian_zhar\t\t\t\t0x15df\n#define XK_Georgian_rae\t\t\t\t\t0x15e0\n#define XK_Georgian_san\t\t\t\t\t0x15e1\n#define XK_Georgian_tar\t\t\t\t\t0x15e2\n#define XK_Georgian_un\t\t\t\t\t0x15e3\n#define XK_Georgian_phar\t\t\t\t0x15e4\n#define XK_Georgian_khar\t\t\t\t0x15e5\n#define XK_Georgian_ghan\t\t\t\t0x15e6\n#define XK_Georgian_qar\t\t\t\t\t0x15e7\n#define XK_Georgian_shin\t\t\t\t0x15e8\n#define XK_Georgian_chin\t\t\t\t0x15e9\n#define XK_Georgian_can\t\t\t\t\t0x15ea\n#define XK_Georgian_jil\t\t\t\t\t0x15eb\n#define XK_Georgian_cil\t\t\t\t\t0x15ec\n#define XK_Georgian_char\t\t\t\t0x15ed\n#define XK_Georgian_xan\t\t\t\t\t0x15ee\n#define XK_Georgian_jhan\t\t\t\t0x15ef\n#define XK_Georgian_hae\t\t\t\t\t0x15f0\n#define XK_Georgian_he\t\t\t\t\t0x15f1\n#define XK_Georgian_hie\t\t\t\t\t0x15f2\n#define XK_Georgian_we\t\t\t\t\t0x15f3\n#define XK_Georgian_har\t\t\t\t\t0x15f4\n#define XK_Georgian_hoe\t\t\t\t\t0x15f5\n#define XK_Georgian_fi\t\t\t\t\t0x15f6\n#endif /* XK_GEORGIAN */\n\n/*\n * Azeri (and other Turkic or Caucasian languages of ex-USSR)\n * Byte 3 = 0x16\n */\n\n#ifdef XK_CAUCASUS\n/* latin */\n#define XK_Ccedillaabovedot\t0x16a2\n#define XK_Xabovedot\t\t0x16a3\n#define XK_Qabovedot\t\t0x16a5\n#define\tXK_Ibreve\t\t0x16a6\n#define XK_IE\t\t\t0x16a7\n#define XK_UO\t\t\t0x16a8\n#define XK_Zstroke\t\t0x16a9\n#define\tXK_Gcaron\t\t0x16aa\n#define\tXK_Obarred\t\t0x16af\n#define XK_ccedillaabovedot\t0x16b2\n#define XK_xabovedot\t\t0x16b3\n#define\tXK_Ocaron\t\t0x16b4\n#define XK_qabovedot\t\t0x16b5\n#define\tXK_ibreve\t\t0x16b6\n#define XK_ie\t\t\t0x16b7\n#define XK_uo\t\t\t0x16b8\n#define XK_zstroke\t\t0x16b9\n#define\tXK_gcaron\t\t0x16ba\n#define\tXK_ocaron\t\t0x16bd\n#define\tXK_obarred\t\t0x16bf\n#define XK_SCHWA\t\t0x16c6\n#define XK_schwa\t\t0x16f6\n/* those are not really Caucasus, but I put them here for now */\n/* For Inupiak */\n#define XK_Lbelowdot\t\t0x16d1\n#define XK_Lstrokebelowdot\t0x16d2\n#define XK_lbelowdot\t\t0x16e1\n#define XK_lstrokebelowdot\t0x16e2\n/* For Guarani */\n#define XK_Gtilde\t\t0x16d3\n#define XK_gtilde\t\t0x16e3\n#endif /* XK_CAUCASUS */\n\n/*\n *   Vietnamese\n *   Byte 3 = 0x1e\n */\n \n#ifdef XK_VIETNAMESE\n#define XK_Abelowdot\t\t\t\t\t0x1ea0\n#define XK_abelowdot\t\t\t\t\t0x1ea1\n#define XK_Ahook\t\t\t\t\t0x1ea2\n#define XK_ahook\t\t\t\t\t0x1ea3\n#define XK_Acircumflexacute\t\t\t\t0x1ea4\n#define XK_acircumflexacute\t\t\t\t0x1ea5\n#define XK_Acircumflexgrave\t\t\t\t0x1ea6\n#define XK_acircumflexgrave\t\t\t\t0x1ea7\n#define XK_Acircumflexhook\t\t\t\t0x1ea8\n#define XK_acircumflexhook\t\t\t\t0x1ea9\n#define XK_Acircumflextilde\t\t\t\t0x1eaa\n#define XK_acircumflextilde\t\t\t\t0x1eab\n#define XK_Acircumflexbelowdot\t\t\t\t0x1eac\n#define XK_acircumflexbelowdot\t\t\t\t0x1ead\n#define XK_Abreveacute\t\t\t\t\t0x1eae\n#define XK_abreveacute\t\t\t\t\t0x1eaf\n#define XK_Abrevegrave\t\t\t\t\t0x1eb0\n#define XK_abrevegrave\t\t\t\t\t0x1eb1\n#define XK_Abrevehook\t\t\t\t\t0x1eb2\n#define XK_abrevehook\t\t\t\t\t0x1eb3\n#define XK_Abrevetilde\t\t\t\t\t0x1eb4\n#define XK_abrevetilde\t\t\t\t\t0x1eb5\n#define XK_Abrevebelowdot\t\t\t\t0x1eb6\n#define XK_abrevebelowdot\t\t\t\t0x1eb7\n#define XK_Ebelowdot\t\t\t\t\t0x1eb8\n#define XK_ebelowdot\t\t\t\t\t0x1eb9\n#define XK_Ehook\t\t\t\t\t0x1eba\n#define XK_ehook\t\t\t\t\t0x1ebb\n#define XK_Etilde\t\t\t\t\t0x1ebc\n#define XK_etilde\t\t\t\t\t0x1ebd\n#define XK_Ecircumflexacute\t\t\t\t0x1ebe\n#define XK_ecircumflexacute\t\t\t\t0x1ebf\n#define XK_Ecircumflexgrave\t\t\t\t0x1ec0\n#define XK_ecircumflexgrave\t\t\t\t0x1ec1\n#define XK_Ecircumflexhook\t\t\t\t0x1ec2\n#define XK_ecircumflexhook\t\t\t\t0x1ec3\n#define XK_Ecircumflextilde\t\t\t\t0x1ec4\n#define XK_ecircumflextilde\t\t\t\t0x1ec5\n#define XK_Ecircumflexbelowdot\t\t\t\t0x1ec6\n#define XK_ecircumflexbelowdot\t\t\t\t0x1ec7\n#define XK_Ihook\t\t\t\t\t0x1ec8\n#define XK_ihook\t\t\t\t\t0x1ec9\n#define XK_Ibelowdot\t\t\t\t\t0x1eca\n#define XK_ibelowdot\t\t\t\t\t0x1ecb\n#define XK_Obelowdot\t\t\t\t\t0x1ecc\n#define XK_obelowdot\t\t\t\t\t0x1ecd\n#define XK_Ohook\t\t\t\t\t0x1ece\n#define XK_ohook\t\t\t\t\t0x1ecf\n#define XK_Ocircumflexacute\t\t\t\t0x1ed0\n#define XK_ocircumflexacute\t\t\t\t0x1ed1\n#define XK_Ocircumflexgrave\t\t\t\t0x1ed2\n#define XK_ocircumflexgrave\t\t\t\t0x1ed3\n#define XK_Ocircumflexhook\t\t\t\t0x1ed4\n#define XK_ocircumflexhook\t\t\t\t0x1ed5\n#define XK_Ocircumflextilde\t\t\t\t0x1ed6\n#define XK_ocircumflextilde\t\t\t\t0x1ed7\n#define XK_Ocircumflexbelowdot\t\t\t\t0x1ed8\n#define XK_ocircumflexbelowdot\t\t\t\t0x1ed9\n#define XK_Ohornacute\t\t\t\t\t0x1eda\n#define XK_ohornacute\t\t\t\t\t0x1edb\n#define XK_Ohorngrave\t\t\t\t\t0x1edc\n#define XK_ohorngrave\t\t\t\t\t0x1edd\n#define XK_Ohornhook\t\t\t\t\t0x1ede\n#define XK_ohornhook\t\t\t\t\t0x1edf\n#define XK_Ohorntilde\t\t\t\t\t0x1ee0\n#define XK_ohorntilde\t\t\t\t\t0x1ee1\n#define XK_Ohornbelowdot\t\t\t\t0x1ee2\n#define XK_ohornbelowdot\t\t\t\t0x1ee3\n#define XK_Ubelowdot\t\t\t\t\t0x1ee4\n#define XK_ubelowdot\t\t\t\t\t0x1ee5\n#define XK_Uhook\t\t\t\t\t0x1ee6\n#define XK_uhook\t\t\t\t\t0x1ee7\n#define XK_Uhornacute\t\t\t\t\t0x1ee8\n#define XK_uhornacute\t\t\t\t\t0x1ee9\n#define XK_Uhorngrave\t\t\t\t\t0x1eea\n#define XK_uhorngrave\t\t\t\t\t0x1eeb\n#define XK_Uhornhook\t\t\t\t\t0x1eec\n#define XK_uhornhook\t\t\t\t\t0x1eed\n#define XK_Uhorntilde\t\t\t\t\t0x1eee\n#define XK_uhorntilde\t\t\t\t\t0x1eef\n#define XK_Uhornbelowdot\t\t\t\t0x1ef0\n#define XK_uhornbelowdot\t\t\t\t0x1ef1\n#define XK_Ybelowdot\t\t\t\t\t0x1ef4\n#define XK_ybelowdot\t\t\t\t\t0x1ef5\n#define XK_Yhook\t\t\t\t\t0x1ef6\n#define XK_yhook\t\t\t\t\t0x1ef7\n#define XK_Ytilde\t\t\t\t\t0x1ef8\n#define XK_ytilde\t\t\t\t\t0x1ef9\n#define XK_Ohorn\t\t\t\t\t0x1efa /* U+01a0 */\n#define XK_ohorn\t\t\t\t\t0x1efb /* U+01a1 */\n#define XK_Uhorn\t\t\t\t\t0x1efc /* U+01af */\n#define XK_uhorn\t\t\t\t\t0x1efd /* U+01b0 */\n\n#define XK_combining_tilde\t\t\t\t0x1e9f /* U+0303 */\n#define XK_combining_grave\t\t\t\t0x1ef2 /* U+0300 */\n#define XK_combining_acute\t\t\t\t0x1ef3 /* U+0301 */\n#define XK_combining_hook\t\t\t\t0x1efe /* U+0309 */\n#define XK_combining_belowdot\t\t\t\t0x1eff /* U+0323 */\n#endif /* XK_VIETNAMESE */\n\n#ifdef XK_CURRENCY\n#define XK_EcuSign\t\t\t\t\t0x20a0\n#define XK_ColonSign\t\t\t\t\t0x20a1\n#define XK_CruzeiroSign\t\t\t\t\t0x20a2\n#define XK_FFrancSign\t\t\t\t\t0x20a3\n#define XK_LiraSign\t\t\t\t\t0x20a4\n#define XK_MillSign\t\t\t\t\t0x20a5\n#define XK_NairaSign\t\t\t\t\t0x20a6\n#define XK_PesetaSign\t\t\t\t\t0x20a7\n#define XK_RupeeSign\t\t\t\t\t0x20a8\n#define XK_WonSign\t\t\t\t\t0x20a9\n#define XK_NewSheqelSign\t\t\t\t0x20aa\n#define XK_DongSign\t\t\t\t\t0x20ab\n#define XK_EuroSign\t\t\t\t\t0x20ac\n#endif\n"
  },
  {
    "path": "plugins/xsv/m_x.c",
    "content": "//network interface+main module\n\n//this code should work okay as a host server for Xepher.\n//issue these two commands and Xephyr will do all the stuff we don't support properly.\n//DISPLAY=127.0.0.1:0 Xephyr :1\n//wmaker -display :1 (or xterm or whatever)\n\n#include \"../plugin.h\"\nstatic plugnetfuncs_t *netfuncs;\nstatic plugsubconsolefuncs_t *confuncs;\n\n#include \"qux.h\"\n#include \"../engine.h\"\n\n#undef MULTITHREAD\n\nfloat mousecursor_x, mousecursor_y;\n\nstatic const int baseport = 6000;\nstatic xclient_t *xclients;\nstatic qhandle_t xlistensocket = -1;\n\nxwindow_t *xfocusedwindow;\n\nqboolean xrefreshed;\nqboolean xscreenmodified;\t//texture updated\n\nxclient_t *xgrabbedclient;\t//clients can ask the server to ignore other clients\nextern xwindow_t *xpgrabbedwindow;\n\n#define MAXREQUESTSIZE\t65535\n\nint ctrldown, altdown;\n\n#ifndef K_CTRL\nint K_BACKSPACE;\nint K_CTRL;\nint K_ALT;\nint K_MOUSE1;\nint K_MOUSE2;\nint K_MOUSE3;\nint K_MOUSE4;\nint K_MOUSE5;\n#endif\n\nint QKeyToScan(int qkey)\n{\t//X11 uses some variation of hardware scancodes.\n\t//custom keymaps tend to ignore the server and use some other table instead.\n\t//so we need to try to match what most servers expect\n\tswitch(qkey)\n\t{\n//\tcase K_:\t\t\treturn 1;\n//\tcase K_:\t\t\treturn 2;\n//\tcase K_:\t\t\treturn 3;\n//\tcase K_:\t\t\treturn 4;\n//\tcase K_:\t\t\treturn 5;\n//\tcase K_:\t\t\treturn 6;\n//\tcase K_:\t\t\treturn 7;\n//\tcase K_:\t\t\treturn 8;\n//\tcase K_:\t\t\treturn 9;\n\tcase '1':\t\t\treturn 10;\n\tcase '2':\t\t\treturn 11;\n\tcase '3':\t\t\treturn 12;\n\tcase '4':\t\t\treturn 13;\n\tcase '5':\t\t\treturn 14;\n\tcase '6':\t\t\treturn 15;\n\tcase '7':\t\t\treturn 16;\n\tcase '8':\t\t\treturn 17;\n\tcase '9':\t\t\treturn 18;\n\tcase '-':\t\t\treturn 19;\n\tcase '+':\t\t\treturn 20;\n\tcase '=':\t\t\treturn 21;\n\tcase K_BACKSPACE:\treturn 22;\n\n\tcase K_TAB:\t\t\treturn 23;\n\tcase 'q':\t\t\treturn 24;\n\tcase 'w':\t\t\treturn 25;\n\tcase 'e':\t\t\treturn 26;\n\tcase 'r':\t\t\treturn 27;\n\tcase 't':\t\t\treturn 28;\n\tcase 'y':\t\t\treturn 29;\n\tcase 'u':\t\t\treturn 30;\n\tcase 'i':\t\t\treturn 31;\n\tcase 'o':\t\t\treturn 32;\n\tcase 'p':\t\t\treturn 33;\n\tcase '[':\t\t\treturn 34;\n\tcase ']':\t\t\treturn 35;\n\tcase K_ENTER:\t\treturn 36;\n\n\tcase K_LCTRL:\t\treturn 37;\n\tcase 'a':\t\t\treturn 38;\n\tcase 's':\t\t\treturn 39;\n\tcase 'd':\t\t\treturn 40;\n\tcase 'f':\t\t\treturn 41;\n\tcase 'g':\t\t\treturn 42;\n\tcase 'h':\t\t\treturn 43;\n\tcase 'j':\t\t\treturn 44;\n\tcase 'k':\t\t\treturn 45;\n\tcase 'l':\t\t\treturn 46;\n\tcase ';':\t\t\treturn 47;\n\tcase '\\'':\t\t\treturn 48;\n\tcase '`':\t\t\treturn 49;\n\n\tcase K_LSHIFT:\t\treturn 50;\n\tcase '#':\t\t\treturn 51;\n\tcase 'z':\t\t\treturn 52;\n\tcase 'x':\t\t\treturn 53;\n\tcase 'c':\t\t\treturn 54;\n\tcase 'v':\t\t\treturn 55;\n\tcase 'b':\t\t\treturn 56;\n\tcase 'n':\t\t\treturn 57;\n\tcase 'm':\t\t\treturn 58;\n\tcase ',':\t\t\treturn 59;\n\tcase '.':\t\t\treturn 60;\n\tcase '/':\t\t\treturn 61;\n\tcase K_RSHIFT:\t\treturn 62;\n\n\tcase K_KP_STAR:\t\treturn 63;\n\tcase K_LALT:\t\treturn 64;\n\tcase K_SPACE:\t\treturn 65;\n\tcase K_CAPSLOCK:\treturn 66;\n\tcase K_F1:\t\t\treturn 67;\n\tcase K_F2:\t\t\treturn 68;\n\tcase K_F3:\t\t\treturn 69;\n\tcase K_F4:\t\t\treturn 70;\n\tcase K_F5:\t\t\treturn 71;\n\tcase K_F6:\t\t\treturn 72;\n\tcase K_F7:\t\t\treturn 73;\n\tcase K_F8:\t\t\treturn 74;\n\tcase K_F9:\t\t\treturn 75;\n\tcase K_F10:\t\t\treturn 76;\n\n\tcase K_KP_NUMLOCK:\treturn 77;\n\tcase K_SCRLCK:\t\treturn 78;\n\tcase K_KP_HOME:\t\treturn 79;\n\tcase K_KP_UPARROW:\treturn 80;\n\tcase K_KP_PGUP:\t\treturn 81;\n\tcase K_KP_MINUS:\treturn 82;\n\tcase K_KP_LEFTARROW:return 83;\n\tcase K_KP_5:\t\treturn 84;\n\tcase K_KP_RIGHTARROW:return 85;\n\tcase K_KP_PLUS:\t\treturn 86;\n\tcase K_KP_END:\t\treturn 87;\n\tcase K_KP_DOWNARROW:return 88;\n\tcase K_KP_PGDN:\t\treturn 89;\n\tcase K_KP_INS:\t\treturn 90;\n\tcase K_KP_DEL:\t\treturn 91;\n\n//\tcase K_L3SHIFT:\t\treturn 92;\n//\tcase K_:\t\t\treturn 93;\n\tcase '\\\\':\t\t\treturn 94;\n\tcase K_F11:\t\t\treturn 95;\n\tcase K_F12:\t\t\treturn 96;\n//\tcase K_:\t\t\treturn 97;\n//\tcase K_KATAKANA:\treturn 98;\n//\tcase K_HIRAGANA:\treturn 99;\n//\tcase K_HENKAN_MODE:\treturn 100;\n//\tcase K_HIRAGANA_KATAKANA:return 101;\n//\tcase K_MUHENKAN:\treturn 102;\n//\tcase K_:\t\t\treturn 103;\n\tcase K_KP_ENTER:\treturn 104;\n\tcase K_RCTRL:\t\treturn 105;\n\tcase K_KP_SLASH:\treturn 106;\n\tcase K_PRINTSCREEN:\treturn 107;\n//\tcase K_L3SHIFT:\t\treturn 108;\n//\tcase K_LINEFEED:\treturn 109;\n\tcase K_HOME:\t\treturn 110;\n\tcase K_UPARROW:\t\treturn 111;\n\tcase K_PGUP:\t\treturn 112;\n\tcase K_LEFTARROW:\treturn 113;\n\tcase K_RIGHTARROW:\treturn 114;\n\tcase K_END:\t\t\treturn 115;\n\tcase K_DOWNARROW:\treturn 116;\n\tcase K_PGDN:\t\treturn 117;\n\tcase K_INS:\t\t\treturn 118;\n\tcase K_DEL:\t\t\treturn 119;\n\n//\tcase K_:\t\t\treturn 120;\n\tcase K_MM_VOLUME_MUTE:return 121;\n\tcase K_VOLDOWN:\t\treturn 122;\n\tcase K_VOLUP:\t\treturn 123;\n\tcase K_POWER:\t\treturn 124;\n\tcase K_KP_EQUALS:\treturn 125;\n//\tcase K_PLUSMINUS:\treturn 126;\n\tcase K_PAUSE:\t\treturn 127;\n//\tcase K_LAUNCHA:\t\treturn 128;\n//\tcase K_KP_DECIMAL:\treturn 129;\n//\tcase K_HANGUL:\t\treturn 130;\n//\tcase K_HANGUL_HANJA:return 131;\n//\tcase K_:\t\t\treturn 132;\n\tcase K_LWIN:\t\treturn 133;\n\tcase K_RWIN:\t\treturn 134;\n\tcase K_APP:\t\t\treturn 135;\n//\tcase K_CANCEL:\t\treturn 136;\n//\tcase K_REDO:\t\treturn 137;\n//\tcase K_SUNPROPS:\treturn 138;\n//\tcase K_UNDO:\t\treturn 139;\n//\tcase K_SUNFRONT:\treturn 140;\n//\tcase K_COPY:\t\treturn 141;\n//\tcase K_OPEN:\t\treturn 142;\n//\tcase K_PASTE:\t\treturn 143;\n//\tcase K_FIND:\t\treturn 144;\n//\tcase K_CUT:\t\t\treturn 145;\n//\tcase K_HELP:\t\treturn 146;\n//\tcase K_MENUKB:\t\treturn 147;\n//\tcase K_CALCULATOR:\treturn 148;\n\n\n\n\tdefault:\t\t\treturn 0;\n\t}\n}\n\nvoid X_SendData(xclient_t *cl, void *data, int datalen)\n{\n#ifdef MULTITHREADWIN32\n\tif (cl->threadhandle)\n\t\tEnterCriticalSection(&cl->delecatesection);\n#endif\n\n\tif (cl->outbufferlen + datalen > cl->outbuffermaxlen)\n\t{\t//extend buffer size\n\t\tcl->outbuffermaxlen = cl->outbufferlen + datalen + 1024;\n\t\tcl->outbuffer = realloc(cl->outbuffer, cl->outbuffermaxlen);\n\t}\n\n\tmemcpy(cl->outbuffer+cl->outbufferlen, data, datalen);\n\tcl->outbufferlen += datalen;\n\n#ifdef MULTITHREADWIN32\n\tif (cl->threadhandle)\n\t\tLeaveCriticalSection(&cl->delecatesection);\n#endif\n}\n\nvoid X_SendNotification(xEvent *data)\n{\n\txclient_t *cl;\n\tfor (cl = xclients; cl; cl = cl->nextclient)\n\t{\n\t\tif (cl->stillinitialising)\n\t\t\tcontinue;\n\t\tif (cl->tobedropped)\n\t\t\tcontinue;\n\t\tif (cl->outbufferlen > MAXREQUESTSIZE*4)\n\t\t\tcontinue;\n\n\t\tdata->u.u.sequenceNumber = cl->requestnum;\n\t\tX_SendData(cl, data, sizeof(xEvent));\n\t}\n}\n\n\nqboolean X_NotifcationMaskPresent(xwindow_t *window, int mask, xclient_t *notfor)\n{\n\txnotificationmask_t *nm;\n\tnm = window->notificationmask;\n\n\tif (mask == SubstructureNotifyMask || mask == SubstructureRedirectMask)\n\t{\n\t\twindow = window->parent;\n//\t\tfor(;window;window=window->parent)\n\t\t{\n\t\t\tfor (nm = window->notificationmask; nm; nm = nm->next)\n\t\t\t{\n\t\t\t\tif (nm->mask & mask)\n\t\t\t\t\tif (nm->client != notfor)\n\t\t\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t}\n\n\telse\n\t{\n\t\tfor (nm = window->notificationmask; nm; nm = nm->next)\n\t\t{\n\t\t\tif (nm->mask & mask)\n\t\t\t\tif (nm->client != notfor)\n\t\t\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n\nint X_SendNotificationMasked(xEvent *data, xwindow_t *window, unsigned int mask)\n{\n\tint count=0;\n\txclient_t *cl;\n\txnotificationmask_t *nm;\n\n\txwindow_t *child = window;\n\n\tif (mask == SubstructureNotifyMask || mask == SubstructureRedirectMask)\n\t{\n\t\tfor (cl = xclients; cl; cl = cl->nextclient)\n\t\t{\n\t\t\t//don't send to if...\n\t\t\tif (cl->stillinitialising)\n\t\t\t\tcontinue;\n\t\t\tif (cl->tobedropped)\n\t\t\t\tcontinue;\n\t\t\tif (cl->outbufferlen > MAXREQUESTSIZE*4)\n\t\t\t{\n\t\t\t\tcl->tobedropped = true;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\twindow = child->parent;\n\t\t\tif (window)\n//\t\t\tfor (window = child; window; window = window->parent)\n\t\t\t{\n\t\t\t\tfor (nm = window->notificationmask; nm; nm = nm->next)\n\t\t\t\t{\n\t\t\t\t\tif (nm->client != cl)\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\tif (!(nm->mask & mask))\n\t\t\t\t\t\tcontinue;\n\n\t\t\t\t\tdata->u.reparent.event = window->res.id;\t//so the request/notification/whatever knows who asked for it.\n\n\t\t\t\t\tdata->u.u.sequenceNumber = cl->requestnum;\n\t\t\t\t\tX_SendData(cl, data, sizeof(xEvent));\n\t\t\t\t\tcount++;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n//\t\t\t\tif (nm)\n//\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\telse\n\t{\n\t\tfor (nm = window->notificationmask; nm; nm = nm->next)\n\t\t{\n\t\t\tif (!(nm->mask & mask))\n\t\t\t\tcontinue;\n\t\t\tcl = nm->client;\n\n\t\t\tif (cl->stillinitialising)\n\t\t\t\tcontinue;\n\t\t\tif (cl->tobedropped)\n\t\t\t\tcontinue;\n\t\t\tif (cl->outbufferlen > MAXREQUESTSIZE*4)\n\t\t\t{\n\t\t\t\tcl->tobedropped = true;\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tdata->u.u.sequenceNumber = cl->requestnum;\n\t\t\tX_SendData(cl, data, sizeof(xEvent));\n\t\t\tcount++;\n\t\t}\n\t}\n\treturn count;\n}\n\nint X_SendInputNotification(xEvent *data, xwindow_t *window, unsigned int mask)\n{\n\tint count=0;\n\txclient_t *cl;\n\txnotificationmask_t *nm;\n\n\txwindow_t *child = window;\n\txwindow_t *focus;\n\n\t//we go all the way to the root if needed.\n\n\tfor (cl = xclients; cl; cl = cl->nextclient)\n\t{\n\t\t//don't send to if...\n\t\tif (cl->stillinitialising)\n\t\t\tcontinue;\n\t\tif (cl->tobedropped)\n\t\t\tcontinue;\n\t\tif (cl->outbufferlen > MAXREQUESTSIZE*4)\n\t\t{\n\t\t\tcl->tobedropped = true;\n\t\t\tcontinue;\n\t\t}\n\t\twindow = child->parent;\n\n\t\tfor (window = child; window; window = window->parent)\n\t\t{\n\t\t\tfor (nm = window->notificationmask; nm; nm = nm->next)\n\t\t\t{\n\t\t\t\tif (nm->client != cl)\n\t\t\t\t\tcontinue;\n\t\t\t\tif (!(nm->mask & mask))\n\t\t\t\t\tcontinue;\n\n//\t\t\t\tCon_Printf(\"Sending input %i\\n\", data->u.u.type);\n\n\t\t\t\tif (data->u.u.type == FocusIn || data->u.u.type == FocusOut)\n\t\t\t\t{\n\t\t\t\t\tdata->u.u.sequenceNumber = cl->requestnum;\n\t\t\t\t\tX_SendData(cl, data, sizeof(xEvent));\n\t\t\t\t\tcount++;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tdata->u.keyButtonPointer.event = window->res.id;\t//so the request/notification/whatever knows who asked for it.\n\t\t\t\tdata->u.keyButtonPointer.eventX = data->u.keyButtonPointer.rootX;\n\t\t\t\tdata->u.keyButtonPointer.eventY = data->u.keyButtonPointer.rootY;\n\t\t\t\tfor (window = window; window; window = window->parent)\t//adjust event's xpos/ypos\n\t\t\t\t{\n\t\t\t\t\tdata->u.keyButtonPointer.eventX -= window->xpos;\n\t\t\t\t\tdata->u.keyButtonPointer.eventY -= window->ypos;\n\t\t\t\t}\n\n\t\t\t\tif (data->u.u.type == EnterNotify || data->u.u.type == LeaveNotify)\n\t\t\t\t{\n\t\t\t\t\tdata->u.enterLeave.flags &= ~ELFlagFocus;\n\n\t\t\t\t\tfocus = xfocusedwindow;\n\t\t\t\t\twhile(focus)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (focus->res.id == data->u.enterLeave.event)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tdata->u.enterLeave.flags |= ELFlagFocus;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tfocus = focus->parent;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tdata->u.u.sequenceNumber = cl->requestnum;\n\t\t\t\tif (data->u.keyButtonPointer.event == data->u.keyButtonPointer.child)\n\t\t\t\t{\n\t\t\t\t\tdata->u.keyButtonPointer.child = None;\n\t\t\t\t\tX_SendData(cl, data, sizeof(xEvent));\n\t\t\t\t\tdata->u.keyButtonPointer.child = data->u.keyButtonPointer.event;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tX_SendData(cl, data, sizeof(xEvent));\n\t\t\t\tcount++;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (nm || (window->donotpropagate & mask))\n\t\t\t\tbreak;\n\t\t}\n\t}\n\treturn count;\n}\n\nvoid X_SendError(xclient_t *cl, int errorcode, int assocresource, int major, int minor)\n{\n\txError err;\n\terr.type\t\t\t= X_Error;\n\terr.errorCode\t\t= errorcode;\n    err.sequenceNumber\t= cl->requestnum;       /* the nth request from this client */\n    err.resourceID\t\t= assocresource;\n    err.minorCode\t\t= minor;\n    err.majorCode\t\t= major;\n    err.pad1\t\t\t= 0;\n    err.pad3\t\t\t= 0;\n    err.pad4\t\t\t= 0;\n    err.pad5\t\t\t= 0;\n    err.pad6\t\t\t= 0;\n    err.pad7\t\t\t= 0;\n\n\tX_SendData(cl, &err, sizeof(err));\n}\n\nint X_NewRIDBase(void)\n{\n\txclient_t *cl;\n\tint ridbase = 0x200000;\n\twhile(ridbase)\t//it'll wrap at some point...\n\t{\n\t\tfor (cl = xclients; cl; cl = cl->nextclient)\n\t\t{\n\t\t\tif (cl->ridbase == ridbase)\t//someone has this range...\n\t\t\t{\n\t\t\t\tridbase+=0x200000;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (!cl)\n\t\t\treturn ridbase;\n\t}\n\n\t//err... bugger... that could be problematic...\n\t//try again, but start allocating half quantities and hope a client drops soon...\n\n\tridbase = 0x200000;\n\twhile(ridbase)\n\t{\n\t\tfor (cl = xclients; cl; cl = cl->nextclient)\n\t\t{\n\t\t\tif (cl->ridbase == ridbase)\t//someone has this range...\n\t\t\t{\n\t\t\t\tridbase+=0x100000;\n\t\t\t\tbreak;;\n\t\t\t}\n\t\t}\n\t\tif (!cl)\n\t\t\treturn ridbase;\n\t}\n\n\tif (ridbase)\n\t\treturn ridbase;\n\n\treturn 0;\n}\n\nvoid X_SendIntialResponse(xclient_t *cl)\n{\n\tint rid;\n\tchar buffer[8192];\n\txConnSetupPrefix *prefix;\n\txConnSetup\t*setup;\n\tchar *vendor;\n\txPixmapFormat *pixmapformats;\n\txnotificationmask_t *nm;\n\n\txWindowRoot *root;\n\txDepth *depth;\n\txVisualType *visualtype;\n\n\trid = X_NewRIDBase();\n\tcl->ridbase = rid;\n\n\tif (!rid)\n\t{\n\t\tprefix = (xConnSetupPrefix *)buffer;\n\t\tprefix->success = 0;\n\t\tprefix->lengthReason = 22;\n\t\tprefix->majorVersion = 11;\t//protocol version.\n\t\tprefix->minorVersion = 0;\n\t\tprefix->length = (prefix->lengthReason/4+3)&~3;\n\t\tstrcpy((char *)(prefix+1), \"No free resource range\");\n\t\tX_SendData(cl, prefix, sizeof(prefix)+(prefix->length+1)*4);\n\t\tcl->tobedropped = true;\n\t}\n\telse\n\t{\n\t\tprefix = (xConnSetupPrefix *)buffer;\n\t\tprefix->success = 1;\n\t\tprefix->lengthReason = 0;\n\t\tprefix->majorVersion = 11;\t//protocol version.\n\t\tprefix->minorVersion = 0;\n\n\t\tsetup = (xConnSetup\t*)(prefix+1);\n\t\tsetup->release\t\t\t\t= 0;//build_number();\t\t//our version number\n\t\tsetup->ridBase\t\t\t\t= rid;\n\t\tsetup->ridMask\t\t\t\t= 0x1fffff;\n\t\tsetup->motionBufferSize\t\t= 1;\n\t\tsetup->maxRequestSize\t\t= MAXREQUESTSIZE;\n\t\tsetup->numRoots\t\t\t\t= 1;\t\t\t//we only have one display. so only one root window please.\n\t\tsetup->imageByteOrder\t\t= LSBFirst;        /* LSBFirst, MSBFirst */\n\t\tsetup->bitmapBitOrder\t\t= LSBFirst;        /* LeastSignificant, MostSign...*/\n\t\tsetup->bitmapScanlineUnit\t= 32,     /* 8, 16, 32 */\n\t\tsetup->bitmapScanlinePad\t= 32;     /* 8, 16, 32 */\n\t\tsetup->minKeyCode\t\t\t= 1;\n\t\tsetup->maxKeyCode\t\t\t= 255;\n\n\t\tvendor = (char *)(setup+1);\n\t\tstrcpy(vendor, \"FTE X\");\n\t\tsetup->nbytesVendor = (strlen(vendor)+3)&~3;\n\n\t\tpixmapformats = (xPixmapFormat *)(vendor + setup->nbytesVendor);\n\t\tsetup->numFormats = 0;\n\n\t/*\tpixmapformats[setup->numFormats].depth = 16;\n\t\tpixmapformats[setup->numFormats].bitsPerPixel = 16;\n\t\tpixmapformats[setup->numFormats].scanLinePad = 16;\n\t\tpixmapformats[setup->numFormats].pad1=0;\n\t\tpixmapformats[setup->numFormats].pad2=0;\n\t\tsetup->numFormats++;*/\n\n\t\tpixmapformats[setup->numFormats].depth = 24;\n\t\tpixmapformats[setup->numFormats].bitsPerPixel = 32;\n\t\tpixmapformats[setup->numFormats].scanLinePad = 32;\n\t\tpixmapformats[setup->numFormats].pad1=0;\n\t\tpixmapformats[setup->numFormats].pad2=0;\n\t\tsetup->numFormats++;\n\n\t\troot = (xWindowRoot *)(pixmapformats + setup->numFormats);\n\t\troot->windowId\t\t\t= rootwindow->res.id;\n\t\troot->defaultColormap\t= 32;\n\t\troot->whitePixel\t\t= 0xffffff;\n\t\troot->blackPixel\t\t= 0;\n\t\troot->currentInputMask\t= 0;   \n\t\tfor (nm = rootwindow->notificationmask; nm; nm = nm->next)\n\t\t\troot->currentInputMask |= nm->mask;\n\t\troot->pixWidth\t\t\t= rootwindow->width;\n\t\troot->pixHeight\t\t\t= rootwindow->height;\n\t\troot->mmWidth\t\t\t= rootwindow->width/3;\n\t\troot->mmHeight\t\t\t= rootwindow->height/3;\n\t\troot->minInstalledMaps\t= 1;\n\t\troot->maxInstalledMaps\t= 1;\n\t\troot->rootVisualID\t\t= 0x22;\n\t\troot->backingStore\t\t= 0;\n\t\troot->saveUnders\t\t= false;\n\t\troot->rootDepth\t\t\t= 24;\n\t\troot->nDepths = 0;\n\n\t\tdepth = (xDepth*)(root + 1);\n\t\tdepth->depth = 24;\n\t\tdepth->pad1 = 0;\n\t\tdepth->nVisuals = 1;\n\t\tdepth->pad2 = 0;\n\t\troot->nDepths++;\n\n\t\tvisualtype = (xVisualType*)(depth+1);\n\t\tvisualtype->visualID = root->rootVisualID;\n\t\tvisualtype->class\t\t= TrueColor;\n\t\tvisualtype->bitsPerRGB\t= 24;\n\t\tvisualtype->colormapEntries = 256;\n\t\tvisualtype->redMask\t\t= 0xff0000;\n\t\tvisualtype->greenMask\t= 0x00ff00;\n\t\tvisualtype->blueMask\t= 0x0000ff;\n\t\tvisualtype->pad = 0;\n\n\t\tvisualtype++;\n\t\tprefix->length = ((char *)visualtype - (char *)setup)/4;\n\n\t\tX_SendData(cl, prefix, (char *)visualtype - (char *)prefix);\n\t}\n}\n\n\n\n\n\n\nqboolean XWindows_TendToClient(xclient_t *cl)\t//true says drop\n{\n\tint err;\n\tint len;\n\tunsigned int inlen;\n\tchar *input;\n\n\tif (!xgrabbedclient || xgrabbedclient == cl)\t//someone grabbed the server\n\tif (cl->outbufferlen < 256 && !cl->tobedropped)\t//don't accept new messages if we still have a lot to write.\n\t{\n#ifdef MULTITHREADWIN32\n\t\tif (!cl->threadhandle)\n#endif\n\t\t{\n\t\t\tif (cl->inbuffermaxlen - cl->inbufferlen < 1000)\t//do we need to expand this message?\n\t\t\t{\n\t\t\t\tchar *newbuffer;\n\t\t\t\tcl->inbuffermaxlen += 1000;\n\t\t\t\tnewbuffer = malloc(cl->inbuffermaxlen);\n\t\t\t\tif (cl->inbuffer)\n\t\t\t\t{\n\t\t\t\t\tmemcpy(newbuffer, cl->inbuffer, cl->inbufferlen);\n\t\t\t\t\tfree(cl->inbuffer);\n\t\t\t\t}\n\t\t\t\tcl->inbuffer = newbuffer;\n\t\t\t}\n\t\t\tlen = cl->inbuffermaxlen - cl->inbufferlen;\n//Con_Printf(\"recving\\n\");\n\t\t\tlen = netfuncs->Recv(cl->socket, cl->inbuffer + cl->inbufferlen, len);\n//Con_Printf(\"recved %i\\n\", len);\n\t\t\tif (len == 0)\t//connection was closed. bummer.\n\t\t\t{\n//Con_Printf(\"Closed\\n\");\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tif (len > 0)\n\t\t\t{\n\t\t\t\tcl->inbufferlen += len;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\terr = len;\n\t\t\t\tif (err != N_WOULDBLOCK)\n\t\t\t\t{\n\t\t\t\t\tCon_Printf(\"X read error %i\\n\", err);\n\t\t\t\t\tcl->tobedropped = true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n#ifdef MULTITHREADWIN32\n\t\telse\n\t\t\tEnterCriticalSection(&cl->delecatesection);\n#endif\n\n//\t\tif (len > 0)\t//the correct version\n\t\tif (cl->inbufferlen > 0)\t\t\t\t//temp\n\t\t{\n\t\t\tinput = cl->inbuffer;\nnextmessage:\n\t\t\tinlen = cl->inbufferlen - (input - cl->inbuffer);\n\t\t\tif (cl->stillinitialising)\n\t\t\t{\n\t\t\t\tif (inlen >= sizeof(xConnClientPrefix))\n\t\t\t\t{\n\t\t\t\t\txConnClientPrefix *prefix = (xConnClientPrefix *)input;\n\t\t\t\t\tcl->stillinitialising = false;\n\t\t\t\t\tif (prefix->byteOrder != 'l')\t//egad no. horrible.\n\t\t\t\t\t{\n#ifdef MULTITHREADWIN32\n\t\t\t\t\t\tLeaveCriticalSection(&cl->delecatesection);\n#endif\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t\tif (prefix->majorVersion != 11)\t//x11 only. Sorry.\n\t\t\t\t\t{\n#ifdef MULTITHREADWIN32\n\t\t\t\t\t\tLeaveCriticalSection(&cl->delecatesection);\n#endif\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t\tif (prefix->minorVersion != 0)\t//we don't know of any variations.\n\t\t\t\t\t{\n#ifdef MULTITHREADWIN32\n\t\t\t\t\t\tLeaveCriticalSection(&cl->delecatesection);\n#endif\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t\t/*if (prefix->nbytesAuthProto != 0)\t//we can't handle this\n\t\t\t\t\t{\n#ifdef MULTITHREADWIN32\n\t\t\t\t\t\tLeaveCriticalSection(&cl->delecatesection);\n#endif\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t\tif (prefix->nbytesAuthString != 0)\t//we can't handle this\n\t\t\t\t\t{\n#ifdef MULTITHREADWIN32\n\t\t\t\t\t\tLeaveCriticalSection(&cl->delecatesection);\n#endif\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}*/\n\n\t\t\t\t\tif (inlen >= sizeof(*prefix) + ((prefix->nbytesAuthProto+3)&~3) + ((prefix->nbytesAuthString+3)&~3))\n\t\t\t\t\t{\n\t\t\t\t\t\tinput += sizeof(*prefix) + ((prefix->nbytesAuthProto+3)&~3) + ((prefix->nbytesAuthString+3)&~3);\n\t\t\t\t\t\tX_SendIntialResponse(cl);\n\t\t\t\t\t\tgoto nextmessage;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (inlen >= sizeof(xReq))\n\t\t\t{\n\t\t\t\tunsigned int rlen;\n\t\t\t\txReq *req;\n\t\t\t\treq = (xReq *)input;\n\n\t\t\t\trlen = req->length;\n\t\t\t\tif (!rlen && inlen >= sizeof(xReq)+sizeof(CARD32))\t//BIG-REQUESTS says that if the length of a request is 0, then there's an extra 32bit int with the correct length imediatly after the 0.\n\t\t\t\t\trlen = *(CARD32 *)(req+1);\n\t\t\t\tif (rlen && inlen >= rlen*4)\n\t\t\t\t{\n\t\t\t\t\tcl->requestnum++;\n\n\t\t\t\t\tif (/*req->reqType < 0 || req->reqType >= 256 ||*/ !XRequests[req->reqType])\n\t\t\t\t\t{\n\t//\t\t\t\t\tCon_Printf(\"X request %i, len %i - NOT SUPPORTED\\n\", req->reqType, rlen*4);\n\n\t\t\t\t\t\t//this is a minimal implementation...\n\t\t\t\t\t\tX_SendError(cl, BadImplementation, 0, req->reqType, 0);\n//\t\t\t\t\t\tcl->tobedropped = true;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n//\t\t\t\t\t\tCon_Printf(\"X request %i, len %i\\n\", req->reqType, rlen*4);\n\n//Con_Printf(\"Request %i\\n\", req->reqType);\n//\t\t\t\t\t\tZ_CheckSentinals();\n\t\t\t\t\t\tif (!req->length)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tint rt, data;\n\n\t\t\t\t\t\t\trt = req->reqType;\t//save these off\n\t\t\t\t\t\t\tdata = req->data;\n\n\t\t\t\t\t\t\treq = (xReq *)((char *)req+sizeof(CARD32));\t//adjust correctly.\n\n\t\t\t\t\t\t\treq->reqType = rt;\t//and restore them into the space taken by the longer size.\n\t\t\t\t\t\t\treq->data\t= data;\n\t\t\t\t\t\t\treq->length = 0;\t//Don't rely on this. This isn't really needed.\n\n\t\t\t\t\t\t\tXRequests[req->reqType](cl, req);\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tXRequests[req->reqType](cl, req);\n//\t\t\t\t\t\tZ_CheckSentinals();\n//Con_Printf(\"Done request\\n\");\n\t\t\t\t\t}\n\n\t\t\t\t\tinput += rlen*4;\n\n\t\t\t\t\tgoto nextmessage;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tlen = input - cl->inbuffer;\n\t\t\tmemmove(cl->inbuffer, input, cl->inbufferlen - len);\n\t\t\tcl->inbufferlen -= len;\n\t\t}\n#ifdef MULTITHREADWIN32\n\t\tif (cl->threadhandle)\n\t\t\tLeaveCriticalSection(&cl->delecatesection);\n#endif\n\t}\n\n\tif (cl->outbufferlen)\t//still send if grabbed. don't let things build up this side.\n\t{\n#ifdef MULTITHREADWIN32\n\t\tif (!cl->threadhandle)\n#endif\n\t\t{\n\t\t\tlen = cl->outbufferlen;\n\t\t\tif (len > 8000)\n\t\t\t\tlen = 8000;\n\t\t\tlen = netfuncs->Send(cl->socket, cl->outbuffer, len);\n\t\t\tif (len>0)\n\t\t\t{\n\t\t\t\tmemmove(cl->outbuffer, cl->outbuffer+len, cl->outbufferlen - len);\n\t\t\t\tcl->outbufferlen -= len;\n\t\t\t}\n\t\t\tif (len == 0)\n\t\t\t\tcl->tobedropped = true;\n\t\t\tif (len < 0)\n\t\t\t{\n\t\t\t\tif (len != N_WOULDBLOCK)\n\t\t\t\t{\n\t\t\t\t\tCon_Printf(\"X send error %i\\n\", len);\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\telse if ((!xgrabbedclient || xgrabbedclient == cl) && cl->tobedropped)\n\t\treturn true;\t//grabbed servers do not allow altering state if a client drops\n\treturn false;\n}\n\n#ifdef MULTITHREAD\n\n#ifdef _WIN32\nDWORD WINAPI X_RunClient(void *parm)\n#else\nvoid X_RunClient(void *parm)\n#endif\n{\n\tchar buffer[8192*64];\n\tint read, len, err;\n\txclient_t *cl = parm;\n\n\twhile(cl->threadhandle)\n\t{\n\t\tif (cl->tobedropped)\n\t\t{\t//don't bother reading more.\n\t\t\tread = 0;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tread = recv(cl->socket, buffer, sizeof(buffer), 0);\n\t\t\tif (read<0 && !cl->outbufferlen)\n\t\t\t{\n\t\t\t\tif (qerrno != EWOULDBLOCK)\n\t\t\t\t\tcl->tobedropped = true;\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tSleep(1);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n#ifdef MULTITHREADWIN32\n\t\tEnterCriticalSection(&cl->delecatesection);\n#endif\n\n\t\tif (read > 0)\n\t\t{\n\t\t\tif (cl->inbuffermaxlen < cl->inbufferlen+read)\t//expand in buffer\n\t\t\t{\n\t\t\t\tcl->inbuffermaxlen = cl->inbufferlen+read + 1000;\t//add breathing room.\n\t\t\t\tcl->inbuffer = realloc(cl->inbuffer, cl->inbuffermaxlen);\n\t\t\t}\n\t\t\tmemcpy(cl->inbuffer+cl->inbufferlen, buffer, read);\n\t\t\tcl->inbufferlen += read;\n\t\t}\n\t\telse if (!read)\t//no more socket.\n\t\t\tcl->tobedropped = true;\n\t\telse\n\t\t{ \t//error of some sort\n\t\t\terr = qerrno;\n\t\t\tif (err != EWOULDBLOCK)\n\t\t\t\tcl->tobedropped = true;\n\t\t}\n\n\t\tif (cl->outbufferlen)\n\t\t{\n\t\t\tlen = cl->outbufferlen;\n\t\t\tif (len > 8000)\n\t\t\t\tlen = 8000;\n\t\t\tlen = send(cl->socket, cl->outbuffer, len, 0);\t//move out of critical section?\n\t\t\tif (len>0)\n\t\t\t{\n\t\t\t\tmemmove(cl->outbuffer, cl->outbuffer+len, cl->outbufferlen - len);\n\t\t\t\tcl->outbufferlen -= len;\n\t\t\t}\n\t\t\tif (len == 0)\n\t\t\t{\n\t\t\t\tcl->tobedropped = true;\n\t\t\t\tcl->outbufferlen=0;\n\t\t\t}\n\t\t\tif (len < 0)\n\t\t\t{\n\t\t\t\terr = qerrno;\n\t\t\t\tif (err != EWOULDBLOCK)\n\t\t\t\t{\n\t\t\t\t\tcl->tobedropped = true;\n\t\t\t\t\tcl->outbufferlen=0;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n#ifdef MULTITHREADWIN32\n\t\tLeaveCriticalSection(&cl->delecatesection);\n#endif\n\t}\n\n\tDeleteCriticalSection (&cl->delecatesection);\n\n\tclosesocket(cl->socket);\n\tif (cl->inbuffer)\n\t\tfree(cl->inbuffer);\n\tif (cl->outbuffer)\n\t\tfree(cl->outbuffer);\n\tfree(cl);\n\n#ifdef MULTITHREADWIN32\n\treturn 0;\n#endif\n}\n\n#endif\n\nvoid XWindows_TendToClients(void)\n{\n\txclient_t *cl, *prev=NULL;\n\tqhandle_t newclient;\n\n\tif (xlistensocket >= 0)\n\t{\n\t\tnewclient = netfuncs->Accept(xlistensocket, NULL, 0);\n\t\tif (newclient >= 0)\n\t\t{\n\t\t\tcl = malloc(sizeof(xclient_t));\n\t\t\tmemset(cl, 0, sizeof(xclient_t));\n\t\t\tcl->socket = newclient;\n\t\t\tcl->nextclient = xclients;\n\t\t\tcl->stillinitialising = 1;\n\t\t\txclients = cl;\n\n\n#ifdef MULTITHREADWIN32\n\t\t\tInitializeCriticalSection (&cl->delecatesection);\n\t\t\t{DWORD tid;\n\t\t\tcl->threadhandle = CreateThread(NULL, 0, X_RunClient, cl, 0, &tid);\n\t\t\t}\n\n\t\t\tif (!cl->threadhandle)\n\t\t\t\tDeleteCriticalSection (&cl->delecatesection);\n\n\t\t\tif (ioctlsocket(cl->socket, FIONBIO, &_false) == -1)\n\t\t\t\tSys_Error(\"Nonblocking failed\\n\");\n#endif\n\t\t}\n\t}\n\n\tfor (cl = xclients; cl; cl = cl->nextclient)\n\t{\n\t\tif (XWindows_TendToClient(cl))\n\t\t{\n\t\t\tif (prev)\n\t\t\t{\n\t\t\t\tprev->nextclient = cl->nextclient;\n\t\t\t}\n\t\t\telse\n\t\t\t\txclients = cl->nextclient;\n\n\t\t\tXS_DestroyResourcesOfClient(cl);\n\n#ifdef MULTITHREADWIN32\n\t\t\tif (cl->threadhandle)\n\t\t\t{\n\t\t\t\tcl->threadhandle = NULL;\n\t\t\t\tbreak;\n\t\t\t}\n#endif\n\t\t\tnetfuncs->Close(cl->socket);\n\t\t\tif (cl->inbuffer)\n\t\t\t\tfree(cl->inbuffer);\n\t\t\tif (cl->outbuffer)\n\t\t\t\tfree(cl->outbuffer);\n\t\t\tfree(cl);\n\t\t\tbreak;\n\t\t}\n\n\t\tprev = cl;\n\t}\n}\n\n#ifdef UNIXSOCKETS\n#include <unistd.h>\n#include <fcntl.h>\n#include <sys/file.h>\nint XWindows_UnixListen(int x11display)\n{\n\tchar lockfile[256];\n\tchar socketfile[256];\n\tint lock_fd, ret;\n\n\tQ_snprintf(lockfile, sizeof(lockfile), \"/tmp/.X%i-lock\", x11display);\n\tQ_snprintf(socketfile, sizeof(socketfile), \"/tmp/.X11-unix/X%i\", x11display);\n\n\tlock_fd = open(lockfile, O_RDONLY | O_CREAT, 0600);\n\tif (lock_fd == -1)\n\t\treturn -1;\t//can't do it, jim\n\n\t// try to acquire lock\n\tret = flock(lock_fd, LOCK_EX | LOCK_NB);\n\tif (ret != 0)\n\t{\n\t\tclose(lock_fd);\n\t\treturn -1;\n\t}\n\n\t// remove socket file\n\tunlink(socketfile);\n\n\treturn netfuncs->TCPListen(va(\"unix://%s\", socketfile), baseport+x11display, 3);\n}\n#endif\n\nvoid XWindows_Startup(int x11display)\t//initialise the server socket and do any initial setup as required.\n{\n\tif (xlistensocket < 0)\n\t{\n#ifdef UNIXSOCKETS\n\t\tif (x11display < 0)\n\t\t{\n\t\t\twhile(xlistensocket < 0)\n\t\t\t\txlistensocket = XWindows_UnixListen(++x11display);\n\t\t}\n\t\telse\n\t\t\txlistensocket = XWindows_UnixListen(x11display);\n#else\n\t\tif (x11display < 0)\n\t\t\tx11display = 0;\n\t\txlistensocket = netfuncs->TCPListen(NULL, baseport+x11display, 3);\n#endif\n\t\tif (xlistensocket < 0)\n\t\t{\n\t\t\txlistensocket = -1;\n\t\t\tCon_Printf(\"Failed to create tcp listen socket\\n\");\n\t\t\treturn;\n\t\t}\n\n\t\tX_InitRequests();\n\t\tXS_CreateInitialResources();\n\n\t\tsystem(va(\"DISPLAY=127.0.0.1:%i /usr/bin/x-terminal-emulator &\", x11display));\n\t}\n\n//\tMenu_Control(MENU_GRAB);\n}\n\nextern int x_windowwithfocus;\nextern int x_windowwithcursor;\nvoid XWindows_RefreshWindow(xwindow_t *wnd)\n{\n\txwindow_t *p;\n\tshort xpos;\n\tshort ypos;\n\tunsigned int *out, *in;\n\n\tint x, y;\n\tint maxx, maxy;\n\n\tif (wnd->inputonly)\t//no thanks.\n\t\treturn;\n\n\txpos = 0;\n\typos = 0;\n\tfor (p = wnd->parent; p; p = p->parent)\n\t{\n\t\txpos += p->xpos;\n\t\typos += p->ypos;\n\t}\n\n\ty = ypos + wnd->ypos;\n\tmaxy = y + wnd->height;\n\tif (y < ypos+wnd->ypos)\n\t{\n\t\ty = ypos+wnd->ypos;\n\t}\n\tif (y < 0)\n\t\ty = 0;\n\tif (maxy >= xscreenheight)\n\t\tmaxy = xscreenheight-1;\n\n\tif (!wnd->mapped)//&&rand()&1)\n\t{\t//unmapped windows are invisible.\n\t\treturn;\n\t}\n\n\n\t{\n\t\t/*if (x_windowwithcursor == wnd->res.id)\n\t\t{\n\t\t\tfor (; y < maxy; y++)\n\t\t\t{\n\t\t\t\tx = xpos + wnd->xpos;\n\t\t\t\tmaxx = x + wnd->width;\n\t\t\t\tif (x < xpos+wnd->xpos)\n\t\t\t\t{\n\t\t\t\t\tx = xpos+wnd->xpos;\n\t\t\t\t}\n\t\t\t\tif (x < 0)\n\t\t\t\t\tx = 0;\n\t\t\t\tif (maxx > xscreenwidth)\n\t\t\t\t\tmaxx = xscreenwidth;\n\n\t\t\t\tout = (unsigned int *)xscreen + (x+(y*xscreenwidth));\n\n\t\t\t\tfor (; x < maxx; x++)\n\t\t\t\t{\t\n\t\t\t\t\t*out++ = ((rand()&0xff)<<16)|((rand()&0xff)<<8)|(rand() & 0xff);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse */if (wnd->buffer)// && x_windowwithfocus != wnd->res.id)\n\t\t{\n\t\t\tfor (; y < maxy; y++)\n\t\t\t{\n\t\t\t\tx = xpos + wnd->xpos;\n\t\t\t\tmaxx = x + wnd->width;\n\t\t\t\tif (x < xpos+wnd->xpos)\n\t\t\t\t{\n\t\t\t\t\tx = xpos+wnd->xpos;\n\t\t\t\t}\n\t\t\t\tif (x < 0)\n\t\t\t\t\tx = 0;\n\t\t\t\tif (maxx > xscreenwidth)\n\t\t\t\t\tmaxx = xscreenwidth;\n\n\t\t\t\tout = (unsigned int *)xscreen + (x+(y*xscreenwidth));\n\t\t\t\tin = (unsigned int *)wnd->buffer + (x-xpos-wnd->xpos) + (y-ypos-wnd->ypos)*wnd->width;\n\n\t\t\t\tfor (; x < maxx; x++)\n\t\t\t\t{\t\n\t\t\t\t\t*out++ = *in++;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\n\n\t\t\tfor (; y < maxy; y++)\n\t\t\t{\n\t\t\t\tx = xpos + wnd->xpos;\n\t\t\t\tmaxx = x + wnd->width;\n\t\t\t\tif (x < xpos+wnd->xpos)\n\t\t\t\t{\n\t\t\t\t\tx = xpos+wnd->xpos;\n\t\t\t\t}\n\t\t\t\tif (x < 0)\n\t\t\t\t{\n\t\t\t\t\tx = 0;\n\t\t\t\t}\n\t\t\t\tif (maxx > xscreenwidth)\n\t\t\t\t\tmaxx = xscreenwidth;\n\n\t\t\t\tout = (unsigned int *)xscreen + (x+(y*xscreenwidth));\n\t\t\t\tfor (; x < maxx; x++)\n\t\t\t\t{\t\n\t\t\t\t\t*out++ = wnd->backpixel;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\twnd = wnd->child;\n\twhile(wnd)\n\t{\n\t\tXWindows_RefreshWindow(wnd);\n\t\twnd = wnd->sibling;\n\t}\n}\n/*\nvoid XWindows_DrawWindowTree(xwindow_t *wnd, short xofs, short yofs)\n{\n\tint x, y;\n\tint maxx, maxy;\n\tunsigned int *out;\n\n\tif (wnd->res.owner)\n\t{\n\t\ty = yofs + wnd->ypos;\n\t\tmaxy = y + wnd->width;\n\t\tif (y < 0)\n\t\t{\n\t\t\ty = 0;\n\t\t}\n\t\tif (maxy >= xscreenheight)\n\t\t\tmaxy = xscreenheight-1;\n\t\tfor (y = 0; y < wnd->height; y++)\n\t\t{\n\t\t\tx = xofs + wnd->xpos;\n\t\t\tmaxx = x + wnd->height;\n\t\t\tif (x < 0)\n\t\t\t{\n\t\t\t\tx = 0;\n\t\t\t}\n\t\t\tif (maxx >= xscreenwidth)\n\t\t\t\tmaxx = xscreenwidth-1;\n\n\t\t\tout = (unsigned int *)xscreen + (x+(y*xscreenwidth));\n\n\t\t\tfor (; x < maxx; x++)\n\t\t\t{\t\n\t\t\t\t*out = rand();\n\t\t\t\tout++;\n\t\t\t}\n\t\t}\n\t}\n\n\txofs += wnd->xpos;\n\tyofs += wnd->ypos;\n\n\twnd = wnd->child;\n\twhile(wnd)\n\t{\n\t\tXWindows_DrawWindowTree(wnd, xofs, yofs);\n\t\twnd = wnd->sibling;\n\t}\n}\n*/\n\n//quakie functions\nvoid XWindows_Init(void)\n{\n//\tCmd_AddCommand(\"startx\", XWindows_Startup);\n}\n\nint x_mousex;\nint x_mousey;\nint x_mousestate;\nint x_windowwithcursor;\nint x_windowwithfocus;\n\nint mousestate;\n\nvoid X_MoveCursorWindow(xwindow_t *ew, int mx, int my, int movemode)\n{\n\txEvent ev;\n\n#define MAX_WINDOW_CHAIN 64\n\tint od, nd;\n\tint d, i;\n\txwindow_t *ow;\n\txwindow_t *nw = ew;\n\txwindow_t *oc[MAX_WINDOW_CHAIN];\n\txwindow_t *nc[MAX_WINDOW_CHAIN];\n\n\n\tif (!nw)\n\t\tnw = rootwindow;\n\n\t/*its already got it*/\n\tif (nw->res.id == x_windowwithcursor)\n\t\treturn;\n\n\tif (XS_GetResource(x_windowwithcursor, (void**)&ow) != x_window)\n\t\treturn;\n\n\t//build the window chains into a simple list\n\tod = 0;\n\twhile(ow && od < MAX_WINDOW_CHAIN)\n\t{\n\t\toc[od++] = ow;\n\t\tow = ow->parent;\n\t}\n\n\tnd = 0;\n\twhile(nw && nd < MAX_WINDOW_CHAIN)\n\t{\n\t\tnc[nd++] = nw;\n\t\tnw = nw->parent;\n\t}\n\n\t//both chains have the root at the end\n\t//walk from the parent (last) window up to the top. if they diverge then we have the relevent common ancestor\n\tfor (d = 0; d < nd && d < od; )\n\t{\n\t\td++;\n\t\tif (nc[nd-d] != oc[od-d])\n\t\t\tbreak;\n\t}\n\tnd -= d;\n\tod -= d;\n\n\tif (!nd)\n\t{\n\t\t/*moved to a parent*/\n\n\t\t//LeaveNotify with detail Inferior is generated on A.\n\t\tev.u.u.type\t\t\t\t\t= LeaveNotify;\n\t\tev.u.u.detail\t\t\t\t= NotifyInferior;\n\t\tev.u.enterLeave.time\t\t= plugfuncs->GetMilliseconds();\n\t\tev.u.enterLeave.root\t\t= rootwindow->res.id;\n\t\tev.u.enterLeave.event\t\t= oc[0]->res.id;\n\t\tev.u.enterLeave.child\t\t= None;\n\t\tev.u.enterLeave.rootX\t\t= mx;\n\t\tev.u.enterLeave.rootY\t\t= my;\n\t\tev.u.enterLeave.eventX\t\t= mx - oc[0]->xpos;\n\t\tev.u.enterLeave.eventY\t\t= my - oc[0]->ypos;\n\t\tev.u.enterLeave.state\t\t= mousestate;\n\t\tev.u.enterLeave.mode\t\t= movemode;\n\t\tev.u.enterLeave.flags\t\t= ELFlagSameScreen;\n\t\tX_SendInputNotification(&ev, oc[0], LeaveWindowMask);\n\n\t\t//EnterNotify with detail Virtual is generated on each window between A and B exclusive (in that order).\n\t\tfor (i = od-1; i > 0; i--)\n\t\t{\n\t\t\tev.u.u.type\t\t\t\t\t= EnterNotify;\n\t\t\tev.u.u.detail\t\t\t\t= NotifyVirtual;\n\t\t\tev.u.enterLeave.time\t\t= plugfuncs->GetMilliseconds();\n\t\t\tev.u.enterLeave.root\t\t= rootwindow->res.id;\n\t\t\tev.u.enterLeave.event\t\t= oc[i]->res.id;\n\t\t\tev.u.enterLeave.child\t\t= oc[i-1]->res.id;\n\t\t\tev.u.enterLeave.rootX\t\t= mx;\n\t\t\tev.u.enterLeave.rootY\t\t= my;\n\t\t\tev.u.enterLeave.eventX\t\t= mx - oc[i]->xpos;\n\t\t\tev.u.enterLeave.eventY\t\t= my - oc[i]->ypos;\n\t\t\tev.u.enterLeave.state\t\t= mousestate;\n\t\t\tev.u.enterLeave.mode\t\t= movemode;\n\t\t\tev.u.enterLeave.flags\t\t= ELFlagSameScreen;\n\t\t\tX_SendInputNotification(&ev, oc[i], EnterWindowMask);\n\t\t}\n\n\t\t//EnterNotify with detail Ancestor is generated on B.\n\t\tev.u.u.type\t\t\t\t\t= EnterNotify;\n\t\tev.u.u.detail\t\t\t\t= NotifyInferior;\n\t\tev.u.enterLeave.time\t\t= plugfuncs->GetMilliseconds();\n\t\tev.u.enterLeave.root\t\t= rootwindow->res.id;\n\t\tev.u.enterLeave.event\t\t= nc[0]->res.id;\n\t\tev.u.enterLeave.child\t\t= None;\n\t\tev.u.enterLeave.rootX\t\t= mx;\n\t\tev.u.enterLeave.rootY\t\t= my;\n\t\tev.u.enterLeave.eventX\t\t= mx - nc[0]->xpos;\n\t\tev.u.enterLeave.eventY\t\t= my - nc[0]->ypos;\n\t\tev.u.enterLeave.state\t\t= mousestate;\n\t\tev.u.enterLeave.mode\t\t= movemode;\n\t\tev.u.enterLeave.flags\t\t= ELFlagSameScreen;\n\t\tX_SendInputNotification(&ev, nc[0], EnterWindowMask);\n\t}\n\telse if (!od)\n\t{\n\t\t/*moved to a child*/\n\t\t//LeaveNotify with detail Ancestor is generated on A.\n\t\tev.u.u.type\t\t\t\t\t= LeaveNotify;\n\t\tev.u.u.detail\t\t\t\t= NotifyAncestor;\n\t\tev.u.enterLeave.time\t\t= plugfuncs->GetMilliseconds();\n\t\tev.u.enterLeave.root\t\t= rootwindow->res.id;\n\t\tev.u.enterLeave.event\t\t= oc[0]->res.id;\n\t\tev.u.enterLeave.child\t\t= None;\n\t\tev.u.enterLeave.rootX\t\t= mx;\n\t\tev.u.enterLeave.rootY\t\t= my;\n\t\tev.u.enterLeave.eventX\t\t= mx - oc[0]->xpos;\n\t\tev.u.enterLeave.eventY\t\t= my - oc[0]->ypos;\n\t\tev.u.enterLeave.state\t\t= mousestate;\n\t\tev.u.enterLeave.mode\t\t= movemode;\n\t\tev.u.enterLeave.flags\t\t= ELFlagSameScreen;\n\t\tX_SendInputNotification(&ev, oc[0], LeaveWindowMask);\n\n\t\t//LeaveNotify with detail Virtual is generated on each window between A and B exclusive (in that order).\n\t\tfor (i = 1; i < nd; i++)\n\t\t{\n\t\t\tev.u.u.type\t\t\t\t\t= LeaveNotify;\n\t\t\tev.u.u.detail\t\t\t\t= NotifyVirtual;\n\t\t\tev.u.enterLeave.time\t\t= plugfuncs->GetMilliseconds();\n\t\t\tev.u.enterLeave.root\t\t= rootwindow->res.id;\n\t\t\tev.u.enterLeave.event\t\t= nc[i]->res.id;\n\t\t\tev.u.enterLeave.child\t\t= nc[i-1]->res.id;\n\t\t\tev.u.enterLeave.rootX\t\t= mx;\n\t\t\tev.u.enterLeave.rootY\t\t= my;\n\t\t\tev.u.enterLeave.eventX\t\t= mx - nc[i]->xpos;\n\t\t\tev.u.enterLeave.eventY\t\t= my - nc[i]->ypos;\n\t\t\tev.u.enterLeave.state\t\t= mousestate;\n\t\t\tev.u.enterLeave.mode\t\t= movemode;\n\t\t\tev.u.enterLeave.flags\t\t= ELFlagSameScreen;\n\t\t\tX_SendInputNotification(&ev, nc[i], LeaveWindowMask);\n\t\t}\n\n\t\t//EnterNotify with detail Inferior is generated on B.\n\t\tev.u.u.type\t\t\t\t\t= EnterNotify;\n\t\tev.u.u.detail\t\t\t\t= NotifyInferior;\n\t\tev.u.enterLeave.time\t\t= plugfuncs->GetMilliseconds();\n\t\tev.u.enterLeave.root\t\t= rootwindow->res.id;\n\t\tev.u.enterLeave.event\t\t= nc[0]->res.id;\n\t\tev.u.enterLeave.child\t\t= None;\n\t\tev.u.enterLeave.rootX\t\t= mx;\n\t\tev.u.enterLeave.rootY\t\t= my;\n\t\tev.u.enterLeave.eventX\t\t= mx - nc[0]->xpos;\n\t\tev.u.enterLeave.eventY\t\t= my - nc[0]->ypos;\n\t\tev.u.enterLeave.state\t\t= mousestate;\n\t\tev.u.enterLeave.mode\t\t= movemode;\n\t\tev.u.enterLeave.flags\t\t= ELFlagSameScreen;\n\t\tX_SendInputNotification(&ev, nc[0], EnterWindowMask);\n\t}\n\telse\n\t{\n\t\t/*moved up then down*/\n\n\t\t//LeaveNotify with detail Nonlinear is generated on A.\n\t\tev.u.u.type\t\t\t\t\t= LeaveNotify;\n\t\tev.u.u.detail\t\t\t\t= NotifyNonlinear;\n\t\tev.u.enterLeave.time\t\t= plugfuncs->GetMilliseconds();\n\t\tev.u.enterLeave.root\t\t= rootwindow->res.id;\n\t\tev.u.enterLeave.event\t\t= oc[0]->res.id;\n\t\tev.u.enterLeave.child\t\t= None;\n\t\tev.u.enterLeave.rootX\t\t= mx;\n\t\tev.u.enterLeave.rootY\t\t= my;\n\t\tev.u.enterLeave.eventX\t\t= mx - oc[0]->xpos;\n\t\tev.u.enterLeave.eventY\t\t= my - oc[0]->ypos;\n\t\tev.u.enterLeave.state\t\t= mousestate;\n\t\tev.u.enterLeave.mode\t\t= movemode;\n\t\tev.u.enterLeave.flags\t\t= ELFlagSameScreen;\n\t\tX_SendInputNotification(&ev, oc[0], LeaveWindowMask);\n\n\t\t//LeaveNotify with detail NonlinearVirtual is generated on each window between A and C exclusive (in that order).\n\t\tfor (i = 1; i < nd; i++)\n\t\t{\n\t\t\tev.u.u.type\t\t\t\t\t= LeaveNotify;\n\t\t\tev.u.u.detail\t\t\t\t= NotifyNonlinearVirtual;\n\t\t\tev.u.enterLeave.time\t\t= plugfuncs->GetMilliseconds();\n\t\t\tev.u.enterLeave.root\t\t= rootwindow->res.id;\n\t\t\tev.u.enterLeave.event\t\t= nc[i]->res.id;\n\t\t\tev.u.enterLeave.child\t\t= nc[i-1]->res.id;\n\t\t\tev.u.enterLeave.rootX\t\t= mx;\n\t\t\tev.u.enterLeave.rootY\t\t= my;\n\t\t\tev.u.enterLeave.eventX\t\t= mx - nc[i]->xpos;\n\t\t\tev.u.enterLeave.eventY\t\t= my - nc[i]->ypos;\n\t\t\tev.u.enterLeave.state\t\t= mousestate;\n\t\t\tev.u.enterLeave.mode\t\t= movemode;\n\t\t\tev.u.enterLeave.flags\t\t= ELFlagSameScreen;\n\t\t\tX_SendInputNotification(&ev, nc[i], LeaveWindowMask);\n\t\t}\n\t\t//EnterNotify with detail NonlinearVirtual is generated on each window between C and B exclusive (in that order).\n\t\tfor (i = od-1; i > 0; i--)\n\t\t{\n\t\t\tev.u.u.type\t\t\t\t\t= EnterNotify;\n\t\t\tev.u.u.detail\t\t\t\t= NotifyNonlinearVirtual;\n\t\t\tev.u.enterLeave.time\t\t= plugfuncs->GetMilliseconds();\n\t\t\tev.u.enterLeave.root\t\t= rootwindow->res.id;\n\t\t\tev.u.enterLeave.event\t\t= oc[i]->res.id;\n\t\t\tev.u.enterLeave.child\t\t= oc[i-1]->res.id;\n\t\t\tev.u.enterLeave.rootX\t\t= mx;\n\t\t\tev.u.enterLeave.rootY\t\t= my;\n\t\t\tev.u.enterLeave.eventX\t\t= mx - oc[i]->xpos;\n\t\t\tev.u.enterLeave.eventY\t\t= my - oc[i]->ypos;\n\t\t\tev.u.enterLeave.state\t\t= mousestate;\n\t\t\tev.u.enterLeave.mode\t\t= movemode;\n\t\t\tev.u.enterLeave.flags\t\t= ELFlagSameScreen;\n\t\t\tX_SendInputNotification(&ev, oc[i], EnterWindowMask);\n\t\t}\n\n\t\t//EnterNotify with detail Nonlinear is generated on B.\n\t\tev.u.u.type\t\t\t\t\t= EnterNotify;\n\t\tev.u.u.detail\t\t\t\t= NotifyNonlinear;\n\t\tev.u.enterLeave.time\t\t= plugfuncs->GetMilliseconds();\n\t\tev.u.enterLeave.root\t\t= rootwindow->res.id;\n\t\tev.u.enterLeave.event\t\t= nc[0]->res.id;\n\t\tev.u.enterLeave.child\t\t= None;\n\t\tev.u.enterLeave.rootX\t\t= mx;\n\t\tev.u.enterLeave.rootY\t\t= my;\n\t\tev.u.enterLeave.eventX\t\t= mx - nc[0]->xpos;\n\t\tev.u.enterLeave.eventY\t\t= my - nc[0]->ypos;\n\t\tev.u.enterLeave.state\t\t= mousestate;\n\t\tev.u.enterLeave.mode\t\t= movemode;\n\t\tev.u.enterLeave.flags\t\t= ELFlagSameScreen;\n\t\tX_SendInputNotification(&ev, nc[0], EnterWindowMask);\n\t}\n}\n\nvoid X_EvalutateCursorOwner(int movemode)\n{\n\txEvent ev;\n\txwindow_t *cursorowner, *wnd, *use;\n\tfloat mx, my;\n\tint wcx;\n\tint wcy;\n\n\textern xwindow_t *xpconfinewindow;\n\n\t{\n\t\tmx = mousecursor_x;\n\t\tmy = mousecursor_y;\n\t}\n\tif (mx >= xscreenwidth)\n\t\tmx = xscreenwidth-1;\n\tif (my >= xscreenheight)\n\t\tmy = xscreenheight-1;\n\tif (mx < 0)\n\t\tmx = 0;\n\tif (my < 0)\n\t\tmy = 0;\n\n\tif (xpconfinewindow)\t//don't leave me!\n\t{\n\t\tcursorowner = xpconfinewindow;\n\n\t\twcx = 0; wcy = 0;\n\n\t\tfor (wnd = cursorowner; wnd; wnd = wnd->parent)\n\t\t{\n\t\t\twcx += wnd->xpos;\n\t\t\twcy += wnd->ypos;\n\t\t}\n\n\t\tif (movemode == NotifyNormal)\n\t\t\tmovemode = NotifyWhileGrabbed;\n\t}\n\telse\n\t{\n\t\tcursorowner = rootwindow;\n\t\twcx = 0; wcy = 0;\n\t\twhile(1)\n\t\t{\n\t\t\tuse = NULL;\n\t\t\t//find the last window that contains the pointer (lower windows come first)\n\t\t\tfor (wnd = cursorowner->child; wnd; wnd = wnd->sibling)\n\t\t\t{\n\t\t\t\tif (/*!wnd->inputonly && */wnd->mapped)\n\t\t\t\t\tif (wcx+wnd->xpos <= mx && wcx+wnd->xpos+wnd->width >= mx)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (wcy+wnd->ypos <= my && wcy+wnd->ypos+wnd->height >= my)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tuse = wnd;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t}\n\t\t\twnd = use;\n\n\t\t\tif (wnd)\n\t\t\t{\n\t\t\t\tcursorowner = wnd;\n\t\t\t\twcx += wnd->xpos;\n\t\t\t\twcy += wnd->ypos;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tif (mx != x_mousex || my != x_mousey || x_mousestate != mousestate || x_windowwithcursor != cursorowner->res.id)\n\t{\n\t\tint mask = 0;\n//\t\textern qboolean\tkeydown[256];\n\n//\t\tCon_Printf(\"move %i %i\\n\", mx, my);\n\n\t\tX_MoveCursorWindow(cursorowner, mx, my, movemode);\n\t\tx_windowwithcursor = cursorowner->res.id;\n\n\t\tif (mx != x_mousex || my != x_mousey)\n\t\t{\n\t\t\tmask |= PointerMotionMask;\n\t\t\tif (mousestate)\n\t\t\t\tmask |= ButtonMotionMask;\n\t\t}\n\t\tif ((mousestate^x_mousestate) & Button1Mask)\n\t\t\tmask |= Button1MotionMask;\n\t\tif ((mousestate^x_mousestate) & Button2Mask)\n\t\t\tmask |= Button2MotionMask;\n\t\tif ((mousestate^x_mousestate) & Button3Mask)\n\t\t\tmask |= Button3MotionMask;\n\t\tif ((mousestate^x_mousestate) & Button4Mask)\n\t\t\tmask |= Button4MotionMask;\n\t\tif ((mousestate^x_mousestate) & Button5Mask)\n\t\t\tmask |= Button5MotionMask;\n\n\t\tx_mousex = mx;\n\t\tx_mousey = my;\n\t\tx_mousestate = mousestate;\n\n\t\tfor (; cursorowner && mask; cursorowner = cursorowner->parent)\n\t\t{\t//same window\n\n\t\t\tif (cursorowner->notificationmasks & mask)\n\t\t\t{\n\t\t\t\tev.u.keyButtonPointer.child\t\t= x_windowwithcursor;\n\n/*\t\t\t\t#define ButtonPress\t\t4\n#define ButtonRelease\t\t5\n#define MotionNotify\t\t6\n*/\n\t\t\t\tev.u.u.type\t\t\t\t\t\t= MotionNotify;\n\t\t\t\tev.u.u.detail\t\t\t\t\t= 0;\n\t\t\t\tev.u.u.sequenceNumber\t\t\t= 0;\n\t\t\t\tev.u.keyButtonPointer.time\t\t= plugfuncs->GetMilliseconds();\n\t\t\t\tev.u.keyButtonPointer.root\t\t= rootwindow->res.id;\n\t\t\t\tev.u.keyButtonPointer.event\t\t= cursorowner->res.id;\n\t\t\t\tev.u.keyButtonPointer.child\t\t= (x_windowwithcursor == cursorowner->res.id)?None:x_windowwithcursor;\n\t\t\t\tev.u.keyButtonPointer.rootX\t\t= mx;\n\t\t\t\tev.u.keyButtonPointer.rootY\t\t= my;\n\t\t\t\tev.u.keyButtonPointer.eventX\t= mx - cursorowner->xpos;\n\t\t\t\tev.u.keyButtonPointer.eventY\t= my - cursorowner->ypos;\n\t\t\t\tev.u.keyButtonPointer.state\t\t= mousestate;\n\t\t\t\tev.u.keyButtonPointer.sameScreen= true;\n\n\t\t\t\tX_SendNotificationMasked(&ev, cursorowner, cursorowner->notificationmasks&mask);\n\n\t\t\t\tmask &= ~cursorowner->notificationmasks;\n\t\t\t}\n\t\t}\n\t}\n}\n\nvoid X_EvalutateFocus(int movemode)\n{\n\txEvent ev;\n\txwindow_t *fo, *po, *wnd;\n\n\tif (XS_GetResource(x_windowwithcursor, (void**)&po) != x_window)\n\t\tpo = rootwindow;\n\n//\txfocusedwindow = NULL;\n\n\n\tif (!xfocusedwindow)\n\t{\n\t\tif (XS_GetResource(x_windowwithcursor, (void**)&fo) != x_window)\n\t\t\tfo = rootwindow;\n\t}\n\telse\n\t{\n\t\tfo = xfocusedwindow;\n\t}\n\n\tif (x_windowwithfocus != fo->res.id)\n\t{\n\t\tev.u.u.detail\t\t\t\t\t= 0;\n\t\tev.u.u.sequenceNumber\t\t\t= 0;\n\t\tev.u.focus.mode\t\t\t\t\t= movemode;\n\t\t{\n\t\t\txwindow_t *a,*b;\n\t\t\tint d1,d2;\n\n\t\t\tif (XS_GetResource(x_windowwithfocus, (void**)&wnd) != x_window)\n\t\t\t\twnd = rootwindow;\n\n\t\t\tx_windowwithfocus = fo->res.id;\n\n\t\t\t//count how deep the windows are\n\t\t\tfor (a = wnd,d1=0; a; a = a->parent)\n\t\t\t\td1++;\n\t\t\tfor (b = fo,d2=0; b; b = b->parent)\n\t\t\t\td2++;\n\n\t\t\ta = wnd;\n\t\t\tb = fo;\n\n\t\t\tif (d1>d2)\n\t\t\t{\n\t\t\t\twhile(d1>d2)\t//a is too deep\n\t\t\t\t{\n\t\t\t\t\ta = a->parent;\n\t\t\t\t\td1--;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\twhile(d2>d1)\n\t\t\t\t{\n\t\t\t\t\tb = b->parent;\n\t\t\t\t\td2--;\n\t\t\t\t}\n\t\t\t}\n\t\t\twhile(a != b)\t//find the common ancestor.\n\t\t\t{\n\t\t\t\ta = a->parent;\n\t\t\t\tb = b->parent;\n\t\t\t}\n\n\t\t\tev.u.enterLeave.mode = movemode;\n\t\t\tev.u.enterLeave.flags = ELFlagSameScreen;\t\t/* sameScreen and focus booleans, packed together */\n\n\t\t\t//the cursor moved from a to b via:\n//\t\t\tif (!a)\t//changed screen...\n//\t\t\t{\n//\t\t\t} else\n\t\t\tif (a != wnd && b != fo)\n\t\t\t{\t//changed via a common root, indirectly.\n\n//When the focus moves from window A to window B, window C is\n//their least common ancestor, and the pointer is in window P:\n\n//o    If P is an inferior of A, FocusOut with detail Pointer\n//     is generated on each window from P up to but not\n//     including A (in order).\n\n\t //FIXME\n\n//o    FocusOut with detail Nonlinear is generated on A.\n\n\t\t\t\tev.u.u.type\t\t\t\t\t\t= FocusOut;\n\t\t\t\tev.u.u.detail\t\t\t\t\t= NotifyNonlinear;\n\t\t\t\tev.u.focus.window\t\t\t\t= wnd->res.id;\n\t\t\t\tX_SendInputNotification(&ev, wnd, FocusChangeMask);\n\n\n\n\n//o    FocusOut with detail NonlinearVirtual is generated on\n//     each window between A and C exclusive (in order).\n\n\t\t\t\tfor (a = wnd->parent; a != b; a = a->parent)\n\t\t\t\t{\n\t\t\t\t\tev.u.u.type\t\t\t\t\t\t= FocusOut;\n\t\t\t\t\tev.u.u.detail\t\t\t\t\t= NotifyNonlinearVirtual;\n\t\t\t\t\tev.u.focus.window\t\t\t\t= a->res.id;\n\t\t\t\t\tX_SendInputNotification(&ev, a, FocusChangeMask);\n\t\t\t\t}\n\n//o    FocusIn with detail NonlinearVirtual is generated on\n//     each window between C and B exclusive (in order).\n\n\t\t\t\tfor (; b != fo; )\n\t\t\t\t{\n\t\t\t\t\tev.u.u.type\t\t\t\t\t\t= FocusIn;\n\t\t\t\t\tev.u.u.detail\t\t\t\t\t= NotifyNonlinearVirtual;\n\t\t\t\t\tev.u.focus.window\t\t= a->res.id;\n\t\t\t\t\tX_SendInputNotification(&ev, a, FocusChangeMask);\n\n\t\t\t\t\tfor (a = fo; ; a = a->parent)\t//we need to go through the children.\n\t\t\t\t\t{\n\t\t\t\t\t\tif (a->parent == b)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tb = a;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n//o    FocusIn with detail Nonlinear is generated on B.\n\n\t\t\t\tev.u.u.type\t\t\t\t\t\t= FocusIn;\n\t\t\t\tev.u.u.detail\t\t\t\t\t= NotifyNonlinear;\n\t\t\t\tev.u.focus.window\t\t= fo->res.id;\n\t\t\t\tX_SendInputNotification(&ev, fo, FocusChangeMask);\n\n//o    If P is an inferior of B, FocusIn with detail Pointer\n//     is generated on each window below B down to and includ-\n//     ing P (in order).\n\n\n\t//FIXME:\n\n\t\t\t}\n\t\t\telse if (a == wnd)\n\t\t\t{\t//b is a child of a\n\n//When the focus moves from window A to window B, B is an\n//inferior of A, and the pointer is in window P:\n\n//o    If P is an inferior of A but P is not an inferior of B\n//     or an ancestor of B, FocusOut with detail Pointer is\n//     generated on each window from P up to but not including\n//     A (in order).\n\n\t//FIXME\n\n//o    FocusOut with detail Inferior is generated on A.\n\n\t\t\t\tev.u.u.type\t\t\t\t\t\t= FocusOut;\n\t\t\t\tev.u.u.detail\t\t\t\t\t= NotifyInferior;\n\t\t\t\tev.u.focus.window\t\t= wnd->res.id;\n\t\t\t\tX_SendInputNotification(&ev, wnd, FocusChangeMask);\n\n//o    FocusIn with detail Virtual is generated on each window\n//     between A and B exclusive (in order).\n\n\t\t\t\tif (wnd != fo)\n\t\t\t\tfor (b = wnd; ; )\n\t\t\t\t{\n\t\t\t\t\tfor (a = fo; ; a = a->parent)\t//we need to go through the children.\n\t\t\t\t\t{\n\t\t\t\t\t\tif (a->parent == b)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tb = a;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif (b == fo)\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tev.u.u.type\t\t\t\t\t\t= FocusIn;\n\t\t\t\t\tev.u.u.detail\t\t\t\t\t= NotifyVirtual;\n\t\t\t\t\tev.u.focus.window\t\t= b->res.id;\n\t\t\t\t\tX_SendInputNotification(&ev, b, FocusChangeMask);\n\t\t\t\t}\n\n//o    FocusIn with detail Ancestor is generated on B.\n\n\t\t\t\tev.u.u.type\t\t\t\t\t\t= FocusIn;\n\t\t\t\tev.u.u.detail\t\t\t\t\t= NotifyAncestor;\n\t\t\t\tev.u.focus.window\t\t= fo->res.id;\n\t\t\t\tX_SendInputNotification(&ev, fo, FocusChangeMask);\n\t\t\t}\n\t\t\telse// if (b == cursorowner)\n\t\t\t{\t//a is a child of b\n\n//When the focus moves from window A to window B, A is an\n//inferior of B, and the pointer is in window P:\n\n//o    FocusOut with detail Ancestor is generated on A.\n\n\t\t\t\tev.u.u.type\t\t\t\t\t\t= FocusOut;\n\t\t\t\tev.u.u.detail\t\t\t\t\t= NotifyAncestor;\n\t\t\t\tev.u.focus.window\t\t\t\t= wnd->res.id;\n\t\t\t\tX_SendInputNotification(&ev, wnd, FocusChangeMask);\n\n//o    FocusOut with detail Virtual is generated on each win-\n//     dow between A and B exclusive (in order).\n\n\t\t\t\tfor (b = wnd; ; )\n\t\t\t\t{\n\t\t\t\t\tb = b->parent;\n\t\t\t\t\tif (b == fo)\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tev.u.u.type\t\t\t\t\t\t= FocusOut;\n\t\t\t\t\tev.u.u.detail\t\t\t\t\t= NotifyVirtual;\n\t\t\t\t\tev.u.focus.window\t\t\t\t= a->res.id;\n\t\t\t\t\tX_SendInputNotification(&ev, a, FocusChangeMask);\n\t\t\t\t}\n\n\n//o    FocusIn with detail Inferior is generated on B.\n\n\t\t\t\tev.u.u.type\t\t\t\t\t\t= FocusIn;\n\t\t\t\tev.u.u.detail\t\t\t\t\t= NotifyInferior;\n\t\t\t\tev.u.focus.window\t\t\t\t= fo->res.id;\n\t\t\t\tX_SendInputNotification(&ev, fo, FocusChangeMask);\n\n//o    If P is an inferior of B but P is not A or an inferior\n//     of A or an ancestor of A, FocusIn with detail Pointer\n//     is generated on each window below B down to and includ-\n//     ing P (in order).\n\n\t//FIXME: code missing\n\t\t\t}\n\t\t}\n\t}\n}\n\nvoid XWindows_Draw(void)\n{\n\t{\n\t\tX_EvalutateCursorOwner(NotifyNormal);\n\t}\n\n\tXWindows_TendToClients();\n\n/*\tif (rand()&15 == 15)\n\t\txrefreshed = true;*/\n\n//\tmemset(xscreen, 0, xscreenwidth*4*xscreenheight);\n\n\tXWindows_TendToClients();\n\n//\tXW_ExposeWindow(rootwindow, 0, 0, rootwindow->width, rootwindow->height);\n\n//\tXWindows_DrawWindowTree(rootwindow, 0, 0);\n//\tif (xrefreshed)\n\t{\n\t\tXWindows_RefreshWindow(rootwindow);\n\t\txrefreshed = false;\n\t\txscreenmodified = true; \n//\t\tCon_Printf(\"updated screen\\n\");\n\t}\n\n\t{\n\t\tunsigned int *out = (unsigned int *)xscreen + (x_mousex+(x_mousey*xscreenwidth));\n\t\t*out = rand();\n//\t\tout[64] = rand();\n\t}\n\n\tXWindows_TendToClients();\n\n//\tCon_DrawNotify();\n\n\tXWindows_TendToClients();\n}\n\nvoid XWindows_KeyDown(int key)\n{\n\tif (!key)\t//hrm\n\t\treturn;\n/*\n\tif (key == 'q' || (key == K_BACKSPACE && ctrldown && altdown))\t//kill off the server\n\t{\t//explicit kill\n\t\tMenu_Control(MENU_CLEAR);\n\t\treturn;\n\t}\n*/\n\tif (key == K_CTRL)\n\t\tctrldown = true;\n\tif (key == K_ALT)\n\t\taltdown = true;\n\n\n\t{\n\t\txEvent ev;\n\t\txwindow_t *wnd;\n\n\t\tX_EvalutateCursorOwner(NotifyNormal);\n\n\t\tX_EvalutateFocus(NotifyNormal);\n\n\t\tif (key == K_MOUSE1)\n\t\t{\n\t\t\tev.u.u.type\t\t\t\t\t\t= ButtonPress;\n\t\t\tev.u.u.detail\t\t\t\t\t= Button1;\n\t\t\tmousestate\t\t\t\t\t\t|= Button1Mask;\n\t\t\tev.u.keyButtonPointer.state\t\t= mousestate;\n\t\t\tev.u.keyButtonPointer.child\t\t= x_windowwithcursor;\n\t\t}\n\t\telse if (key == K_MOUSE3)\n\t\t{\n\t\t\tev.u.u.type\t\t\t\t\t\t= ButtonPress;\n\t\t\tev.u.u.detail\t\t\t\t\t= Button2;\n\t\t\tmousestate\t\t\t\t\t\t|= Button2Mask;\n\t\t\tev.u.keyButtonPointer.state\t\t= mousestate;\n\t\t\tev.u.keyButtonPointer.child\t\t= x_windowwithcursor;\n\t\t}\n\t\telse if (key == K_MOUSE2)\n\t\t{\n\t\t\tev.u.u.type\t\t\t\t\t\t= ButtonPress;\n\t\t\tev.u.u.detail\t\t\t\t\t= Button3;\n\t\t\tmousestate\t\t\t\t\t\t|= Button3Mask;\n\t\t\tev.u.keyButtonPointer.state\t\t= mousestate;\n\t\t\tev.u.keyButtonPointer.child\t\t= x_windowwithcursor;\n\t\t}\n\t\telse if (key == K_MOUSE4)\n\t\t{\n\t\t\tev.u.u.type\t\t\t\t\t\t= ButtonPress;\n\t\t\tev.u.u.detail\t\t\t\t\t= Button4;\n\t\t\tmousestate\t\t\t\t\t\t|= Button4Mask;\n\t\t\tev.u.keyButtonPointer.state\t\t= mousestate;\n\t\t\tev.u.keyButtonPointer.child\t\t= x_windowwithcursor;\n\t\t}\n\t\telse if (key == K_MOUSE5)\n\t\t{\n\t\t\tev.u.u.type\t\t\t\t\t\t= ButtonPress;\n\t\t\tev.u.u.detail\t\t\t\t\t= Button5;\n\t\t\tmousestate\t\t\t\t\t\t|= Button5Mask;\n\t\t\tev.u.keyButtonPointer.state\t\t= mousestate;\n\t\t\tev.u.keyButtonPointer.child\t\t= x_windowwithcursor;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tev.u.u.type\t\t\t\t\t\t= KeyPress;\n\t\t\tev.u.u.detail\t\t\t\t\t= QKeyToScan(key);\n\t\t\tif (!ev.u.u.detail)\n\t\t\t\treturn;\t//urm, never mind\n\t\t\tev.u.keyButtonPointer.state\t\t= 0;\n\t\t\tev.u.keyButtonPointer.child\t\t= x_windowwithfocus;\n\t\t}\n\t\tev.u.u.sequenceNumber\t\t\t= 0;\n\t\tev.u.keyButtonPointer.time\t\t= plugfuncs->GetMilliseconds();\n\t\tev.u.keyButtonPointer.rootX\t\t= x_mousex;\n\t\tev.u.keyButtonPointer.rootY\t\t= x_mousey;\n\t\tev.u.keyButtonPointer.sameScreen= true;\n\t\tev.u.keyButtonPointer.pad1\t\t= 0;\n\n//\t\tCon_Printf(\"key %i, %i %i\\n\", key, x_mousex, x_mousey);\n\n\t\tif (0)//xpointergrabclient)\n\t\t{\n\t\t\tev.u.keyButtonPointer.event\t\t= ev.u.keyButtonPointer.child;\n\t\t\tev.u.keyButtonPointer.eventX\t= ev.u.keyButtonPointer.rootX;\n\t\t\tev.u.keyButtonPointer.eventY\t= ev.u.keyButtonPointer.rootY;\n\t\t\tif (XS_GetResource(x_windowwithcursor, (void**)&wnd) == x_window)\n\t\t\t{\n\t\t\t\tev.u.u.sequenceNumber = xpointergrabclient->requestnum;\n\t\t\t\twhile(wnd)\n\t\t\t\t{\n\t\t\t\t\tev.u.keyButtonPointer.eventX -= wnd->xpos;\n\t\t\t\t\tev.u.keyButtonPointer.eventY -= wnd->ypos;\n\t\t\t\t\twnd = wnd->parent;\n\t\t\t\t}\n\t\t\t\tX_SendData(xpointergrabclient, &ev, sizeof(ev));\n\t\t\t}\n\t\t}\n\t\telse if (XS_GetResource(ev.u.keyButtonPointer.child, (void**)&wnd) == x_window)\n\t\t\tX_SendInputNotification(&ev, wnd, (ev.u.u.type==ButtonPress)?ButtonPressMask:KeyPressMask);\n\t}\n}\nvoid XWindows_Keyup(int key)\n{\n\tif (key == K_CTRL)\n\t\tctrldown = false;\n\tif (key == K_ALT)\n\t\taltdown = false;\n\n\t{\n\t\txEvent ev;\n\t\txwindow_t *wnd;\n\n\t\tX_EvalutateCursorOwner(NotifyNormal);\n\n\t\tX_EvalutateFocus(NotifyNormal);\n\n\t\tif (key == K_MOUSE1)\n\t\t{\n\t\t\tev.u.u.type\t\t\t\t\t\t= ButtonRelease;\n\t\t\tev.u.keyButtonPointer.state\t\t= mousestate;\n\t\t\tev.u.u.detail\t\t\t\t\t= Button1;\n\t\t\tmousestate\t\t\t\t\t\t&= ~Button1Mask;\n\t\t\tev.u.keyButtonPointer.state\t\t= mousestate;\n\t\t\tev.u.keyButtonPointer.child\t\t= x_windowwithcursor;\n\t\t}\n\t\telse if (key == K_MOUSE3)\n\t\t{\n\t\t\tev.u.u.type\t\t\t\t\t\t= ButtonRelease;\n\t\t\tev.u.keyButtonPointer.state\t\t= mousestate;\n\t\t\tev.u.u.detail\t\t\t\t\t= Button2;\n\t\t\tmousestate\t\t\t\t\t\t&= ~Button2Mask;\n\t\t\tev.u.keyButtonPointer.state\t\t= mousestate;\n\t\t\tev.u.keyButtonPointer.child\t\t= x_windowwithcursor;\n\t\t}\n\t\telse if (key == K_MOUSE2)\n\t\t{\n\t\t\tev.u.u.type\t\t\t\t\t\t= ButtonRelease;\n\t\t\tev.u.keyButtonPointer.state\t\t= mousestate;\n\t\t\tev.u.u.detail\t\t\t\t\t= Button3;\n\t\t\tmousestate\t\t\t\t\t\t&= ~Button3Mask;\n\t\t\tev.u.keyButtonPointer.state\t\t= mousestate;\n\t\t\tev.u.keyButtonPointer.child\t\t= x_windowwithcursor;\n\t\t}\n\t\telse if (key == K_MOUSE4)\n\t\t{\n\t\t\tev.u.u.type\t\t\t\t\t\t= ButtonRelease;\n\t\t\tev.u.keyButtonPointer.state\t\t= mousestate;\n\t\t\tev.u.u.detail\t\t\t\t\t= Button4;\n\t\t\tmousestate\t\t\t\t\t\t&= ~Button4Mask;\n\t\t\tev.u.keyButtonPointer.state\t\t= mousestate;\n\t\t\tev.u.keyButtonPointer.child\t\t= x_windowwithcursor;\n\t\t}\n\t\telse if (key == K_MOUSE5)\n\t\t{\n\t\t\tev.u.u.type\t\t\t\t\t\t= ButtonRelease;\n\t\t\tev.u.keyButtonPointer.state\t\t= mousestate;\n\t\t\tev.u.u.detail\t\t\t\t\t= Button5;\n\t\t\tmousestate\t\t\t\t\t\t&= ~Button5Mask;\n\t\t\tev.u.keyButtonPointer.state\t\t= mousestate;\n\t\t\tev.u.keyButtonPointer.child\t\t= x_windowwithcursor;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tev.u.u.type\t\t\t\t\t= KeyRelease;\n\t\t\tev.u.u.detail\t\t\t\t= QKeyToScan(key);\n\t\t\tif (!ev.u.u.detail)\n\t\t\t\treturn;\t//urm, never mind\n\t\t\tev.u.keyButtonPointer.child\t\t= x_windowwithfocus;\n\t\t}\n\t\tev.u.u.sequenceNumber\t\t\t= 0;\n\t\tev.u.keyButtonPointer.time\t\t= plugfuncs->GetMilliseconds();\n\t\tev.u.keyButtonPointer.rootX\t\t= x_mousex;\n\t\tev.u.keyButtonPointer.rootY\t\t= x_mousey;\n\t\tev.u.keyButtonPointer.state\t\t= 0;\n\t\tev.u.keyButtonPointer.sameScreen= true;\n\t\tev.u.keyButtonPointer.pad1\t\t= 0;\n\n//\t\tCon_Printf(\"keyup %i, %i %i\\n\", key, x_mousex, x_mousey);\n\n\t\tif (xpointergrabclient)\n\t\t{\n\t\t\tev.u.keyButtonPointer.event\t\t= ev.u.keyButtonPointer.child;\n\t\t\tev.u.keyButtonPointer.eventX\t= ev.u.keyButtonPointer.rootX;\n\t\t\tev.u.keyButtonPointer.eventY\t= ev.u.keyButtonPointer.rootY;\n\t\t\tif (XS_GetResource(x_windowwithcursor, (void**)&wnd) == x_window)\n\t\t\t{\n\t\t\t\tev.u.u.sequenceNumber = xpointergrabclient->requestnum;\n\t\t\t\twhile(wnd)\n\t\t\t\t{\n\t\t\t\t\tev.u.keyButtonPointer.eventX -= wnd->xpos;\n\t\t\t\t\tev.u.keyButtonPointer.eventY -= wnd->ypos;\n\t\t\t\t\twnd = wnd->parent;\n\t\t\t\t}\n\t\t\t\tX_SendData(xpointergrabclient, &ev, sizeof(ev));\n\t\t\t}\n\t\t}\n\t\telse if (XS_GetResource(ev.u.keyButtonPointer.child, (void**)&wnd) == x_window)\n\t\t{\n\t\t\tX_SendInputNotification(&ev, wnd, (ev.u.u.type==ButtonRelease)?ButtonReleaseMask:KeyReleaseMask);\n\t\t}\n\t}\n}\n\n/*qboolean QDECL X11_MenuEvent(int eventtype, int param, float mousecursor_x, float mousecursor_y)\n{\n\tmousecursor_x = args[2];\n\tmousecursor_y = args[3];\n\tswitch(args[0])\n\t{\n\tcase 0:\t//draw\n\t\tXWindows_Draw();\n\t\tbreak;\n\tcase 1:\t//keydown\n\t\tXWindows_KeyDown(args[1]);\n\t\tbreak;\n\tcase 2:\t//keyup\n\t\tXWindows_Keyup(args[1]);\n\t\tbreak;\n\tcase 3:\t//menu closed (this is called even if we change it).\n\t\tbreak;\n\t}\n\n\treturn 0;\n}*/\n\nstatic int X11_ExecuteCommand(qboolean isinsecure)\n{\n\tchar cmd[256];\n\tcmdfuncs->Argv(0, cmd, sizeof(cmd));\n\tif (!strcmp(\"startx\", cmd))\n\t{\n\t\tif (confuncs)\n\t\t{\n\t\t\tconst char *console = \"x11\";\n\t\t\tif (confuncs->GetConsoleFloat(console, \"iswindow\") != true)\n\t\t\t{\n\t\t\t\tconfuncs->SetConsoleString(console, \"title\", \"X11\");\n\t\t\t\tconfuncs->SetConsoleFloat(console, \"iswindow\", true);\n\t\t\t\tconfuncs->SetConsoleFloat(console, \"forceutf8\", true);\n\t\t\t\tconfuncs->SetConsoleFloat(console, \"linebuffered\", false);\n\t\t\t\tconfuncs->SetConsoleFloat(console, \"maxlines\", 0);\n\t\t\t\tconfuncs->SetConsoleFloat(console, \"wnd_x\", 0);\n\t\t\t\tconfuncs->SetConsoleFloat(console, \"wnd_y\", 0);\n\t\t\t\tconfuncs->SetConsoleFloat(console, \"wnd_w\", 640);\n\t\t\t\tconfuncs->SetConsoleFloat(console, \"wnd_h\", 480);\n\t\t\t\tconfuncs->SetConsoleString(console, \"footer\", \"\");\n\t\t\t}\n\t\t\tconfuncs->SetConsoleString(console, \"backvideomap\", \"x11\");\n\t\t\tconfuncs->SetActive(console);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tcmdfuncs->Argv(1, cmd, sizeof(cmd));\n\t\t\tXWindows_Startup(*cmd?atoi(cmd):-1);\n\t\t}\n\t\treturn 1;\n\t}\n\treturn 0;\n}\n\nstatic void QDECL X11_Tick(double realtime, double gametime)\n{\n\tXWindows_TendToClients();\n}\n\nstatic void *XWindows_Create(const char *medianame)\t//initialise the server socket and do any initial setup as required.\n{\n\tif (!strcmp(medianame, \"x11\"))\n\t{\n\t\tXWindows_Startup(-1);\n\t\treturn xscreen;\n\t}\n\treturn NULL;\n}\n\nstatic qboolean VARGS XWindows_DisplayFrame(void *ctx, qboolean nosound, qboolean forcevideo, double mediatime, void (QDECL *uploadtexture)(void *ectx, uploadfmt_t fmt, int width, int height, void *data, void *palette), void *ectx)\n{\n\tXWindows_Draw();\n\n\tif (forcevideo || xscreenmodified)\n\t\tuploadtexture(ectx, TF_BGRX32, xscreenwidth, xscreenheight, xscreen, NULL);\n\txscreenmodified = false;\n\n\treturn true;\n}\n\nstatic void XWindows_Shutdown(void *ctx)\n{\n\tnetfuncs->Close(xlistensocket);\n\txlistensocket = -1;\n}\n\nstatic qboolean XWindows_SetSize (void *ctx, int width, int height)\n{\n\tqbyte *ns;\n\tif (width < 64 || height < 64 || width > 16384 || height > 16384)\n\t\treturn false;\n\n\tns = realloc(xscreen, width*4*height);\n\tif (ns)\n\t{\n\t\txscreen = ns;\n\t\txscreenwidth = width;\n\t\txscreenheight = height;\n\t\txscreenmodified = true;\n\n\t\tif (rootwindow)\n\t\t{\n\t\t\tX_Resize(rootwindow, 0, 0, width, height);\n\t\t\tXW_ExposeWindow(rootwindow, 0, 0, rootwindow->width, rootwindow->height);\n\t\t}\n\t\treturn true;\n\t}\n\treturn false;\n}\n\nstatic void XWindows_GetSize (void *ctx, int *width, int *height)\t//retrieves the screen-space size\n{\n\t*width = xscreenwidth;\n\t*height = xscreenheight;\n}\n\nstatic void XWindows_CursorMove (void *ctx, float posx, float posy)\n{\n\tmousecursor_x = (int)(posx * xscreenwidth);\n\tmousecursor_y = (int)(posy * xscreenheight);\n}\n\nstatic void XWindows_Key (void *ctx, int code, int unicode, int isup)\n{\n\tif (isup)\n\t\tXWindows_Keyup(code);\n\telse\n\t\tXWindows_KeyDown(code);\n}\n\nmedia_decoder_funcs_t decoderfuncs =\n{\n\tsizeof(media_decoder_funcs_t),\n\t\"x11\",\n\tXWindows_Create,\n\tXWindows_DisplayFrame,\n\tXWindows_Shutdown,\n\tNULL,//rewind\n\n\tXWindows_CursorMove,\n\tXWindows_Key,\n\tXWindows_SetSize,\n\tXWindows_GetSize,\n\tNULL//changestream\n};\n\n\nqboolean Plug_Init(void)\n{\n\tnetfuncs = plugfuncs->GetEngineInterface(plugnetfuncs_name, sizeof(*netfuncs));\n\tconfuncs = plugfuncs->GetEngineInterface(plugsubconsolefuncs_name, sizeof(*confuncs));\n\n\tif (!netfuncs ||\n\t\t!plugfuncs->ExportFunction(\"ExecuteCommand\", X11_ExecuteCommand) ||\n//\t\t!plugfuncs->ExportFunction(\"MenuEvent\", X11_MenuEvent) ||\n\t\t!plugfuncs->ExportFunction(\"Tick\", X11_Tick))\n\t{\n\t\tCon_Printf(\"XServer plugin failed\\n\");\n\t\treturn false;\n\t}\n\n\tif (!plugfuncs->ExportInterface(\"Media_VideoDecoder\", &decoderfuncs, sizeof(decoderfuncs)))\n\t{\n\t\tCon_Printf(\"XServer plugin failed: Engine doesn't support media decoder plugins\\n\");\n\t\treturn false;\n\t}\n\n\tCon_Printf(\"XServer plugin started\\n\");\n\n\tcmdfuncs->AddCommand(\"startx\");\n\n#ifndef K_CTRL\n\tK_CTRL\t\t\t= pKey_GetKeyCode(\"ctrl\");\n\tK_ALT\t\t\t= pKey_GetKeyCode(\"alt\");\n\tK_MOUSE1\t\t= pKey_GetKeyCode(\"mouse1\");\n\tK_MOUSE2\t\t= pKey_GetKeyCode(\"mouse2\");\n\tK_MOUSE3\t\t= pKey_GetKeyCode(\"mouse3\");\n\tK_MOUSE4\t\t= pKey_GetKeyCode(\"mouse4\");\n\tK_MOUSE5\t\t= pKey_GetKeyCode(\"mouse5\");\n\tK_BACKSPACE\t\t= pKey_GetKeyCode(\"backspace\");\n/*\n\tK_UPARROW\t\t= Key_GetKeyCode(\"uparrow\");\n\tK_DOWNARROW\t\t= Key_GetKeyCode(\"downarrow\");\n\tK_ENTER\t\t\t= Key_GetKeyCode(\"enter\");\n\tK_DEL\t\t\t= Key_GetKeyCode(\"del\");\n\tK_ESCAPE\t\t= Key_GetKeyCode(\"escape\");\n\tK_PGDN\t\t\t= Key_GetKeyCode(\"pgdn\");\n\tK_PGUP\t\t\t= Key_GetKeyCode(\"pgup\");\n\tK_SPACE\t\t\t= Key_GetKeyCode(\"space\");\n\tK_LEFTARROW\t\t= Key_GetKeyCode(\"leftarrow\");\n\tK_RIGHTARROW\t= Key_GetKeyCode(\"rightarrow\");\n*/\n#endif\n\treturn true;\n}"
  },
  {
    "path": "plugins/xsv/qux.h",
    "content": "#include \"../plugin.h\"\n\n/*\n#ifdef _WIN32\t//for multithreaded reading\n#define BOOL WINDOWSSUCKS_BOOL\n#define INT32 WINDOWSSUCKS_INT32\n#include \"winquake.h\"\n#undef BOOL\n#undef INT32\n\n#define MULTITHREADWIN32\n#endif\n\n#ifdef MULTITHREADWIN32\n#define MULTITHREAD\n#endif\n*/\n\n#include \"Xproto.h\"\n#include \"x.h\"\n\n#include \"bigreqstr.h\"\n\n#define XK_MISCELLANY\n#define XK_LATIN1\n#include \"keysymdef.h\"\n\ntypedef struct xclient_s {\n\tint closedownmode;\n\tqhandle_t socket;\n\tint inbufferlen;\n\tint outbufferlen;\n\tint inbuffermaxlen;\n\tint outbuffermaxlen;\n\tchar *outbuffer;\n\tchar *inbuffer;\n\tqboolean tobedropped;\t//dropped when no more to send.\n\tqboolean stillinitialising;\n\tunsigned int requestnum;\n\tunsigned int ridbase;\n\tstruct xclient_s *nextclient;\n\n#ifdef MULTITHREADWIN32\n\tHANDLE threadhandle;\n\tCRITICAL_SECTION\t\tdelecatesection;\n#endif\n} xclient_t;\n\nextern xclient_t *xgrabbedclient;\t//stops reading other clients\nextern xclient_t *xpointergrabclient;\n\ntypedef struct xproperty_s {\n\tAtom atomid;\n\tAtom type;\n\tint datalen;\n\tstruct xproperty_s *next;\n\tint format;\n\tchar data[1];\n} xproperty_t;\n\ntypedef struct xnotificationmask_s {\n\txclient_t *client;\n\tunsigned int mask;\n\tstruct xnotificationmask_s *next;\n} xnotificationmask_t;\n\ntypedef struct xresource_s {\n\tenum {x_none, x_window, x_gcontext, x_pixmap, x_font, x_atom} restype;\n\tint id;\n\txclient_t *owner;\n\n\tstruct xresource_s *next, *prev;\n} xresource_t;\ntypedef struct xpixmap_s {\n\txresource_t res;\n\n\tint references;\n\tqboolean linked;\n\n\tint width;\n\tint height;\n\tint depth;\n\n\tqbyte *data;\n} xpixmap_t;\ntypedef struct xatom_s {\n\txresource_t res;\n\tint selectionownerwindowid;\n\txclient_t *selectionownerclient;\n\tchar atomname[1]; //must be last\n} xatom_t;\ntypedef struct xwindow_s {\n\txresource_t res;\n\n\tint xpos;\n\tint ypos;\n\tint width;\n\tint height;\n\tchar *buffer;\n\tint bg;\n\tstruct xwindow_s *parent;\n\tstruct xwindow_s *child;\n\tstruct xwindow_s *sibling;\n\n\txpixmap_t *backpixmap;\n\tunsigned int backpixel;\n\tunsigned int borderpixel;\n\tint bitgravity, wingravity;\n\tunsigned int donotpropagate;\n\tint colormap;\n\n\tqboolean mapped;\n\tqboolean overrideredirect;\n\n\tqboolean inputonly;\n\n\tint depth;\n\n\txproperty_t *properties;\n\txnotificationmask_t *notificationmask;\n\tunsigned int notificationmasks;\n} xwindow_t;\n\ntypedef struct xfont_s\n{\n\txresource_t res;\n\tint depth;\n\n\tchar name[256];\n\n\tint rowwidth;\n\tint rowheight;\n\n\tint charwidth;\n\tint charheight;\n\n\tunsigned int data[4];\n} xfont_t;\n\ntypedef struct xgcontext_s\n{\n\txresource_t res;\n\tint depth;\n\n\tint function;\n\tint fgcolour;\n\tint bgcolour;\n\n\txfont_t *font;\n} xgcontext_t;\n\nextern xwindow_t *rootwindow;\nextern qboolean xrefreshed;\t//something onscreen changed.\n\nint XS_GetResource(int id, void **data);\nvoid *XS_GetResourceType(int id, int requiredtype);\nvoid XS_SetProperty(xwindow_t *wnd, Atom atomid, Atom atomtype, char *data, int datalen, int format);\nint XS_GetProperty(xwindow_t *wnd, Atom atomid, Atom *type, char *output, int maxlen, int offset, int *extrabytes, int *format);\nvoid XS_DeleteProperty(xwindow_t *wnd, Atom atomid);\nxatom_t *XS_CreateAtom(Atom atomid, char *name, xclient_t *owner);\nAtom XS_FindAtom(char *name);\nxgcontext_t *XS_CreateGContext(int id, xclient_t *owner, xresource_t *drawable);\nint XS_NewResource(void);\nxwindow_t *XS_CreateWindow(int wid, xclient_t *owner, xwindow_t *parent, short x, short y, short width, short height);\nvoid X_Resize(xwindow_t *wnd, int newx, int newy, int neww, int newh);\nvoid XS_SetParent(xwindow_t *wnd, xwindow_t *parent);\nxpixmap_t *XS_CreatePixmap(int id, xclient_t *owner, int width, int height, int depth);\nxfont_t *XS_CreateFont(int id, xclient_t *owner, char *fontname);\nvoid XS_CreateInitialResources(void);\nvoid XS_DestroyResource(xresource_t *res);\nvoid XS_DestroyResourcesOfClient(xclient_t *cl);\n\n\nvoid XW_ExposeWindow(xwindow_t *root, int x, int y, int width, int height);\nvoid XW_ClearArea(xwindow_t *wnd, int xp, int yp, int width, int height);\n\n\n\ntypedef void (*XRequest) (xclient_t *cl, xReq *request);\nextern XRequest XRequests [256];\n\nvoid X_SendData(xclient_t *cl, void *data, int datalen);\nvoid X_SendNotification(xEvent *data);\nint X_SendNotificationMasked(xEvent *data, xwindow_t *window, unsigned int mask);\nqboolean X_NotifcationMaskPresent(xwindow_t *window, int mask, xclient_t *notfor);\nvoid X_SendError(xclient_t *cl, int errorcode, int assocresource, int major, int minor);\nvoid X_InitRequests(void);\n\nvoid X_EvalutateCursorOwner(int movemode);\nvoid X_EvalutateFocus(int movemode);\n\n\nextern qbyte *xscreen;\nextern short xscreenwidth;\nextern short xscreenheight;\n\n#ifndef K_CTRL\nextern int K_BACKSPACE;\nextern int K_CTRL;\nextern int K_ALT;\nextern int K_MOUSE1;\nextern int K_MOUSE2;\nextern int K_MOUSE3;\nextern int K_MOUSE4;\nextern int K_MOUSE5;\n#endif\n"
  },
  {
    "path": "plugins/xsv/x.h",
    "content": "/*\n *\t$Xorg: X.h,v 1.4 2001/02/09 02:03:22 xorgcvs Exp $\n */\n\n/* Definitions for the X window system likely to be used by applications */\n\n#ifndef X_H\n#define X_H\n\n/***********************************************************\n\nCopyright 1987, 1998  The Open Group\n\nPermission to use, copy, modify, distribute, and sell this software and its\ndocumentation for any purpose is hereby granted without fee, provided that\nthe above copyright notice appear in all copies and that both that\ncopyright notice and this permission notice appear in supporting\ndocumentation.\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\nOPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN\nAN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\nCONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\nExcept as contained in this notice, the name of The Open Group shall not be\nused in advertising or otherwise to promote the sale, use or other dealings\nin this Software without prior written authorization from The Open Group.\n\n\nCopyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts.\n\n                        All Rights Reserved\n\nPermission to use, copy, modify, and distribute this software and its \ndocumentation for any purpose and without fee is hereby granted, \nprovided that the above copyright notice appear in all copies and that\nboth that copyright notice and this permission notice appear in \nsupporting documentation, and that the name of Digital not be\nused in advertising or publicity pertaining to distribution of the\nsoftware without specific, written prior permission.  \n\nDIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING\nALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL\nDIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR\nANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,\nWHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,\nARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS\nSOFTWARE.\n\n******************************************************************/\n/* $XFree86: xc/include/X.h,v 1.5 2001/12/14 19:53:25 dawes Exp $ */\n\n#define X_PROTOCOL\t11\t\t/* current protocol version */\n#define X_PROTOCOL_REVISION 0\t\t/* current minor version */\n\n/* Resources */\n\n/*\n * _XSERVER64 must ONLY be defined when compiling X server sources on\n * systems where unsigned long is not 32 bits, must NOT be used in\n * client or library code.\n */\n#ifndef _XSERVER64\n#  ifndef _XTYPEDEF_XID\n#    define _XTYPEDEF_XID\ntypedef unsigned long XID;\n#  endif\n#  ifndef _XTYPEDEF_MASK\n#    define _XTYPEDEF_MASK\ntypedef unsigned long Mask;\n#  endif\n#  ifndef _XTYPEDEF_ATOM\n#    define _XTYPEDEF_ATOM\ntypedef unsigned long Atom;\t\t/* Also in Xdefs.h */\n#  endif\ntypedef unsigned long VisualID;\ntypedef unsigned long Time;\n#else\n#  include <X11/Xmd.h>\n#  ifndef _XTYPEDEF_XID\n#    define _XTYPEDEF_XID\ntypedef CARD32 XID;\n#  endif\n#  ifndef _XTYPEDEF_MASK\n#    define _XTYPEDEF_MASK\ntypedef CARD32 Mask;\n#  endif\n#  ifndef _XTYPEDEF_ATOM\n#    define _XTYPEDEF_ATOM\ntypedef CARD32 Atom;\n#  endif\ntypedef CARD32 VisualID;\ntypedef CARD32 Time;\n#endif\n\ntypedef XID Window;\ntypedef XID Drawable;\n#ifndef _XTYPEDEF_FONT\n#  define _XTYPEDEF_FONT\ntypedef XID Font;\n#endif\ntypedef XID Pixmap;\ntypedef XID Cursor;\ntypedef XID Colormap;\ntypedef XID GContext;\ntypedef XID KeySym;\n\ntypedef unsigned char KeyCode;\n\n/*****************************************************************\n * RESERVED RESOURCE AND CONSTANT DEFINITIONS\n *****************************************************************/\n\n#ifndef None\n#define None                 0L\t/* universal null resource or null atom */\n#endif\n\n#define ParentRelative       1L\t/* background pixmap in CreateWindow\n\t\t\t\t    and ChangeWindowAttributes */\n\n#define CopyFromParent       0L\t/* border pixmap in CreateWindow\n\t\t\t\t       and ChangeWindowAttributes\n\t\t\t\t   special VisualID and special window\n\t\t\t\t       class passed to CreateWindow */\n\n#define PointerWindow        0L\t/* destination window in SendEvent */\n#define InputFocus           1L\t/* destination window in SendEvent */\n\n#define PointerRoot          1L\t/* focus window in SetInputFocus */\n\n#define AnyPropertyType      0L\t/* special Atom, passed to GetProperty */\n\n#define AnyKey\t\t     0L\t/* special Key Code, passed to GrabKey */\n\n#define AnyButton            0L\t/* special Button Code, passed to GrabButton */\n\n#define AllTemporary         0L\t/* special Resource ID passed to KillClient */\n\n#define CurrentTime          0L\t/* special Time */\n\n#define NoSymbol\t     0L\t/* special KeySym */\n\n/***************************************************************** \n * EVENT DEFINITIONS \n *****************************************************************/\n\n/* Input Event Masks. Used as event-mask window attribute and as arguments\n   to Grab requests.  Not to be confused with event names.  */\n\n#define NoEventMask\t\t\t0L\n#define KeyPressMask\t\t\t(1L<<0)  \n#define KeyReleaseMask\t\t\t(1L<<1)  \n#define ButtonPressMask\t\t\t(1L<<2)  \n#define ButtonReleaseMask\t\t(1L<<3)  \n#define EnterWindowMask\t\t\t(1L<<4)  \n#define LeaveWindowMask\t\t\t(1L<<5)  \n#define PointerMotionMask\t\t(1L<<6)  \n#define PointerMotionHintMask\t\t(1L<<7)  \n#define Button1MotionMask\t\t(1L<<8)  \n#define Button2MotionMask\t\t(1L<<9)  \n#define Button3MotionMask\t\t(1L<<10) \n#define Button4MotionMask\t\t(1L<<11) \n#define Button5MotionMask\t\t(1L<<12) \n#define ButtonMotionMask\t\t(1L<<13) \n#define KeymapStateMask\t\t\t(1L<<14)\n#define ExposureMask\t\t\t(1L<<15) \n#define VisibilityChangeMask\t\t(1L<<16) \n#define StructureNotifyMask\t\t(1L<<17) \n#define ResizeRedirectMask\t\t(1L<<18) \n#define SubstructureNotifyMask\t\t(1L<<19) \n#define SubstructureRedirectMask\t(1L<<20) \n#define FocusChangeMask\t\t\t(1L<<21) \n#define PropertyChangeMask\t\t(1L<<22) \n#define ColormapChangeMask\t\t(1L<<23) \n#define OwnerGrabButtonMask\t\t(1L<<24) \n\n/* Event names.  Used in \"type\" field in XEvent structures.  Not to be\nconfused with event masks above.  They start from 2 because 0 and 1\nare reserved in the protocol for errors and replies. */\n\n#define KeyPress\t\t2\n#define KeyRelease\t\t3\n#define ButtonPress\t\t4\n#define ButtonRelease\t\t5\n#define MotionNotify\t\t6\n#define EnterNotify\t\t7\n#define LeaveNotify\t\t8\n#define FocusIn\t\t\t9\n#define FocusOut\t\t10\n#define KeymapNotify\t\t11\n#define Expose\t\t\t12\n#define GraphicsExpose\t\t13\n#define NoExpose\t\t14\n#define VisibilityNotify\t15\n#define CreateNotify\t\t16\n#define DestroyNotify\t\t17\n#define UnmapNotify\t\t18\n#define MapNotify\t\t19\n#define MapRequest\t\t20\n#define ReparentNotify\t\t21\n#define ConfigureNotify\t\t22\n#define ConfigureRequest\t23\n#define GravityNotify\t\t24\n#define ResizeRequest\t\t25\n#define CirculateNotify\t\t26\n#define CirculateRequest\t27\n#define PropertyNotify\t\t28\n#define SelectionClear\t\t29\n#define SelectionRequest\t30\n#define SelectionNotify\t\t31\n#define ColormapNotify\t\t32\n#define ClientMessage\t\t33\n#define MappingNotify\t\t34\n#define LASTEvent\t\t35\t/* must be bigger than any event # */\n\n\n/* Key masks. Used as modifiers to GrabButton and GrabKey, results of QueryPointer,\n   state in various key-, mouse-, and button-related events. */\n\n#define ShiftMask\t\t(1<<0)\n#define LockMask\t\t(1<<1)\n#define ControlMask\t\t(1<<2)\n#define Mod1Mask\t\t(1<<3)\n#define Mod2Mask\t\t(1<<4)\n#define Mod3Mask\t\t(1<<5)\n#define Mod4Mask\t\t(1<<6)\n#define Mod5Mask\t\t(1<<7)\n\n/* modifier names.  Used to build a SetModifierMapping request or\n   to read a GetModifierMapping request.  These correspond to the\n   masks defined above. */\n#define ShiftMapIndex\t\t0\n#define LockMapIndex\t\t1\n#define ControlMapIndex\t\t2\n#define Mod1MapIndex\t\t3\n#define Mod2MapIndex\t\t4\n#define Mod3MapIndex\t\t5\n#define Mod4MapIndex\t\t6\n#define Mod5MapIndex\t\t7\n\n\n/* button masks.  Used in same manner as Key masks above. Not to be confused\n   with button names below. */\n\n#define Button1Mask\t\t(1<<8)\n#define Button2Mask\t\t(1<<9)\n#define Button3Mask\t\t(1<<10)\n#define Button4Mask\t\t(1<<11)\n#define Button5Mask\t\t(1<<12)\n\n#define AnyModifier\t\t(1<<15)  /* used in GrabButton, GrabKey */\n\n\n/* button names. Used as arguments to GrabButton and as detail in ButtonPress\n   and ButtonRelease events.  Not to be confused with button masks above.\n   Note that 0 is already defined above as \"AnyButton\".  */\n\n#define Button1\t\t\t1\n#define Button2\t\t\t2\n#define Button3\t\t\t3\n#define Button4\t\t\t4\n#define Button5\t\t\t5\n\n/* Notify modes */\n\n#define NotifyNormal\t\t0\n#define NotifyGrab\t\t1\n#define NotifyUngrab\t\t2\n#define NotifyWhileGrabbed\t3\n\n#define NotifyHint\t\t1\t/* for MotionNotify events */\n\t\t       \n/* Notify detail */\n\n#define NotifyAncestor\t\t0\n#define NotifyVirtual\t\t1\n#define NotifyInferior\t\t2\n#define NotifyNonlinear\t\t3\n#define NotifyNonlinearVirtual\t4\n#define NotifyPointer\t\t5\n#define NotifyPointerRoot\t6\n#define NotifyDetailNone\t7\n\n/* Visibility notify */\n\n#define VisibilityUnobscured\t\t0\n#define VisibilityPartiallyObscured\t1\n#define VisibilityFullyObscured\t\t2\n\n/* Circulation request */\n\n#define PlaceOnTop\t\t0\n#define PlaceOnBottom\t\t1\n\n/* protocol families */\n\n#define FamilyInternet\t\t0\n#define FamilyDECnet\t\t1\n#define FamilyChaos\t\t2\n\n/* Property notification */\n\n#define PropertyNewValue\t0\n#define PropertyDelete\t\t1\n\n/* Color Map notification */\n\n#define ColormapUninstalled\t0\n#define ColormapInstalled\t1\n\n/* GrabPointer, GrabButton, GrabKeyboard, GrabKey Modes */\n\n#define GrabModeSync\t\t0\n#define GrabModeAsync\t\t1\n\n/* GrabPointer, GrabKeyboard reply status */\n\n#define GrabSuccess\t\t0\n#define AlreadyGrabbed\t\t1\n#define GrabInvalidTime\t\t2\n#define GrabNotViewable\t\t3\n#define GrabFrozen\t\t4\n\n/* AllowEvents modes */\n\n#define AsyncPointer\t\t0\n#define SyncPointer\t\t1\n#define ReplayPointer\t\t2\n#define AsyncKeyboard\t\t3\n#define SyncKeyboard\t\t4\n#define ReplayKeyboard\t\t5\n#define AsyncBoth\t\t6\n#define SyncBoth\t\t7\n\n/* Used in SetInputFocus, GetInputFocus */\n\n#define RevertToNone\t\t(int)None\n#define RevertToPointerRoot\t(int)PointerRoot\n#define RevertToParent\t\t2\n\n/*****************************************************************\n * ERROR CODES \n *****************************************************************/\n\n#define Success\t\t   0\t/* everything's okay */\n#define BadRequest\t   1\t/* bad request code */\n#define BadValue\t   2\t/* int parameter out of range */\n#define BadWindow\t   3\t/* parameter not a Window */\n#define BadPixmap\t   4\t/* parameter not a Pixmap */\n#define BadAtom\t\t   5\t/* parameter not an Atom */\n#define BadCursor\t   6\t/* parameter not a Cursor */\n#define BadFont\t\t   7\t/* parameter not a Font */\n#define BadMatch\t   8\t/* parameter mismatch */\n#define BadDrawable\t   9\t/* parameter not a Pixmap or Window */\n#define BadAccess\t  10\t/* depending on context:\n\t\t\t\t - key/button already grabbed\n\t\t\t\t - attempt to free an illegal \n\t\t\t\t   cmap entry \n\t\t\t\t- attempt to store into a read-only \n\t\t\t\t   color map entry.\n \t\t\t\t- attempt to modify the access control\n\t\t\t\t   list from other than the local host.\n\t\t\t\t*/\n#define BadAlloc\t  11\t/* insufficient resources */\n#define BadColor\t  12\t/* no such colormap */\n#define BadGC\t\t  13\t/* parameter not a GC */\n#define BadIDChoice\t  14\t/* choice not in range or already used */\n#define BadName\t\t  15\t/* font or color name doesn't exist */\n#define BadLength\t  16\t/* Request length incorrect */\n#define BadImplementation 17\t/* server is defective */\n\n#define FirstExtensionError\t128\n#define LastExtensionError\t255\n\n/*****************************************************************\n * WINDOW DEFINITIONS \n *****************************************************************/\n\n/* Window classes used by CreateWindow */\n/* Note that CopyFromParent is already defined as 0 above */\n\n#define InputOutput\t\t1\n#define InputOnly\t\t2\n\n/* Window attributes for CreateWindow and ChangeWindowAttributes */\n\n#define CWBackPixmap\t\t(1L<<0)\n#define CWBackPixel\t\t(1L<<1)\n#define CWBorderPixmap\t\t(1L<<2)\n#define CWBorderPixel           (1L<<3)\n#define CWBitGravity\t\t(1L<<4)\n#define CWWinGravity\t\t(1L<<5)\n#define CWBackingStore          (1L<<6)\n#define CWBackingPlanes\t        (1L<<7)\n#define CWBackingPixel\t        (1L<<8)\n#define CWOverrideRedirect\t(1L<<9)\n#define CWSaveUnder\t\t(1L<<10)\n#define CWEventMask\t\t(1L<<11)\n#define CWDontPropagate\t        (1L<<12)\n#define CWColormap\t\t(1L<<13)\n#define CWCursor\t        (1L<<14)\n\n/* ConfigureWindow structure */\n\n#define CWX\t\t\t(1<<0)\n#define CWY\t\t\t(1<<1)\n#define CWWidth\t\t\t(1<<2)\n#define CWHeight\t\t(1<<3)\n#define CWBorderWidth\t\t(1<<4)\n#define CWSibling\t\t(1<<5)\n#define CWStackMode\t\t(1<<6)\n\n\n/* Bit Gravity */\n\n#define ForgetGravity\t\t0\n#define NorthWestGravity\t1\n#define NorthGravity\t\t2\n#define NorthEastGravity\t3\n#define WestGravity\t\t4\n#define CenterGravity\t\t5\n#define EastGravity\t\t6\n#define SouthWestGravity\t7\n#define SouthGravity\t\t8\n#define SouthEastGravity\t9\n#define StaticGravity\t\t10\n\n/* Window gravity + bit gravity above */\n\n#define UnmapGravity\t\t0\n\n/* Used in CreateWindow for backing-store hint */\n\n#define NotUseful               0\n#define WhenMapped              1\n#define Always                  2\n\n/* Used in GetWindowAttributes reply */\n\n#define IsUnmapped\t\t0\n#define IsUnviewable\t\t1\n#define IsViewable\t\t2\n\n/* Used in ChangeSaveSet */\n\n#define SetModeInsert           0\n#define SetModeDelete           1\n\n/* Used in ChangeCloseDownMode */\n\n#define DestroyAll              0\n#define RetainPermanent         1\n#define RetainTemporary         2\n\n/* Window stacking method (in configureWindow) */\n\n#define Above                   0\n#define Below                   1\n#define TopIf                   2\n#define BottomIf                3\n#define Opposite                4\n\n/* Circulation direction */\n\n#define RaiseLowest             0\n#define LowerHighest            1\n\n/* Property modes */\n\n#define PropModeReplace         0\n#define PropModePrepend         1\n#define PropModeAppend          2\n\n/*****************************************************************\n * GRAPHICS DEFINITIONS\n *****************************************************************/\n\n/* graphics functions, as in GC.alu */\n\n#define\tGXclear\t\t\t0x0\t\t/* 0 */\n#define GXand\t\t\t0x1\t\t/* src AND dst */\n#define GXandReverse\t\t0x2\t\t/* src AND NOT dst */\n#define GXcopy\t\t\t0x3\t\t/* src */\n#define GXandInverted\t\t0x4\t\t/* NOT src AND dst */\n#define\tGXnoop\t\t\t0x5\t\t/* dst */\n#define GXxor\t\t\t0x6\t\t/* src XOR dst */\n#define GXor\t\t\t0x7\t\t/* src OR dst */\n#define GXnor\t\t\t0x8\t\t/* NOT src AND NOT dst */\n#define GXequiv\t\t\t0x9\t\t/* NOT src XOR dst */\n#define GXinvert\t\t0xa\t\t/* NOT dst */\n#define GXorReverse\t\t0xb\t\t/* src OR NOT dst */\n#define GXcopyInverted\t\t0xc\t\t/* NOT src */\n#define GXorInverted\t\t0xd\t\t/* NOT src OR dst */\n#define GXnand\t\t\t0xe\t\t/* NOT src OR NOT dst */\n#define GXset\t\t\t0xf\t\t/* 1 */\n\n/* LineStyle */\n\n#define LineSolid\t\t0\n#define LineOnOffDash\t\t1\n#define LineDoubleDash\t\t2\n\n/* capStyle */\n\n#define CapNotLast\t\t0\n#define CapButt\t\t\t1\n#define CapRound\t\t2\n#define CapProjecting\t\t3\n\n/* joinStyle */\n\n#define JoinMiter\t\t0\n#define JoinRound\t\t1\n#define JoinBevel\t\t2\n\n/* fillStyle */\n\n#define FillSolid\t\t0\n#define FillTiled\t\t1\n#define FillStippled\t\t2\n#define FillOpaqueStippled\t3\n\n/* fillRule */\n\n#define EvenOddRule\t\t0\n#define WindingRule\t\t1\n\n/* subwindow mode */\n\n#define ClipByChildren\t\t0\n#define IncludeInferiors\t1\n\n/* SetClipRectangles ordering */\n\n#define Unsorted\t\t0\n#define YSorted\t\t\t1\n#define YXSorted\t\t2\n#define YXBanded\t\t3\n\n/* CoordinateMode for drawing routines */\n\n#define CoordModeOrigin\t\t0\t/* relative to the origin */\n#define CoordModePrevious       1\t/* relative to previous point */\n\n/* Polygon shapes */\n\n#define Complex\t\t\t0\t/* paths may intersect */\n#define Nonconvex\t\t1\t/* no paths intersect, but not convex */\n#define Convex\t\t\t2\t/* wholly convex */\n\n/* Arc modes for PolyFillArc */\n\n#define ArcChord\t\t0\t/* join endpoints of arc */\n#define ArcPieSlice\t\t1\t/* join endpoints to center of arc */\n\n/* GC components: masks used in CreateGC, CopyGC, ChangeGC, OR'ed into\n   GC.stateChanges */\n\n#define GCFunction              (1L<<0)\n#define GCPlaneMask             (1L<<1)\n#define GCForeground            (1L<<2)\n#define GCBackground            (1L<<3)\n#define GCLineWidth             (1L<<4)\n#define GCLineStyle             (1L<<5)\n#define GCCapStyle              (1L<<6)\n#define GCJoinStyle\t\t(1L<<7)\n#define GCFillStyle\t\t(1L<<8)\n#define GCFillRule\t\t(1L<<9) \n#define GCTile\t\t\t(1L<<10)\n#define GCStipple\t\t(1L<<11)\n#define GCTileStipXOrigin\t(1L<<12)\n#define GCTileStipYOrigin\t(1L<<13)\n#define GCFont \t\t\t(1L<<14)\n#define GCSubwindowMode\t\t(1L<<15)\n#define GCGraphicsExposures     (1L<<16)\n#define GCClipXOrigin\t\t(1L<<17)\n#define GCClipYOrigin\t\t(1L<<18)\n#define GCClipMask\t\t(1L<<19)\n#define GCDashOffset\t\t(1L<<20)\n#define GCDashList\t\t(1L<<21)\n#define GCArcMode\t\t(1L<<22)\n\n#define GCLastBit\t\t22\n/*****************************************************************\n * FONTS \n *****************************************************************/\n\n/* used in QueryFont -- draw direction */\n\n#define FontLeftToRight\t\t0\n#define FontRightToLeft\t\t1\n\n#define FontChange\t\t255\n\n/*****************************************************************\n *  IMAGING \n *****************************************************************/\n\n/* ImageFormat -- PutImage, GetImage */\n\n#define XYBitmap\t\t0\t/* depth 1, XYFormat */\n#define XYPixmap\t\t1\t/* depth == drawable depth */\n#define ZPixmap\t\t\t2\t/* depth == drawable depth */\n\n/*****************************************************************\n *  COLOR MAP STUFF \n *****************************************************************/\n\n/* For CreateColormap */\n\n#define AllocNone\t\t0\t/* create map with no entries */\n#define AllocAll\t\t1\t/* allocate entire map writeable */\n\n\n/* Flags used in StoreNamedColor, StoreColors */\n\n#define DoRed\t\t\t(1<<0)\n#define DoGreen\t\t\t(1<<1)\n#define DoBlue\t\t\t(1<<2)\n\n/*****************************************************************\n * CURSOR STUFF\n *****************************************************************/\n\n/* QueryBestSize Class */\n\n#define CursorShape\t\t0\t/* largest size that can be displayed */\n#define TileShape\t\t1\t/* size tiled fastest */\n#define StippleShape\t\t2\t/* size stippled fastest */\n\n/***************************************************************** \n * KEYBOARD/POINTER STUFF\n *****************************************************************/\n\n#define AutoRepeatModeOff\t0\n#define AutoRepeatModeOn\t1\n#define AutoRepeatModeDefault\t2\n\n#define LedModeOff\t\t0\n#define LedModeOn\t\t1\n\n/* masks for ChangeKeyboardControl */\n\n#define KBKeyClickPercent\t(1L<<0)\n#define KBBellPercent\t\t(1L<<1)\n#define KBBellPitch\t\t(1L<<2)\n#define KBBellDuration\t\t(1L<<3)\n#define KBLed\t\t\t(1L<<4)\n#define KBLedMode\t\t(1L<<5)\n#define KBKey\t\t\t(1L<<6)\n#define KBAutoRepeatMode\t(1L<<7)\n\n#define MappingSuccess     \t0\n#define MappingBusy        \t1\n#define MappingFailed\t\t2\n\n#define MappingModifier\t\t0\n#define MappingKeyboard\t\t1\n#define MappingPointer\t\t2\n\n/*****************************************************************\n * SCREEN SAVER STUFF \n *****************************************************************/\n\n#define DontPreferBlanking\t0\n#define PreferBlanking\t\t1\n#define DefaultBlanking\t\t2\n\n#define DisableScreenSaver\t0\n#define DisableScreenInterval\t0\n\n#define DontAllowExposures\t0\n#define AllowExposures\t\t1\n#define DefaultExposures\t2\n\n/* for ForceScreenSaver */\n\n#define ScreenSaverReset 0\n#define ScreenSaverActive 1\n\n/*****************************************************************\n * HOSTS AND CONNECTIONS\n *****************************************************************/\n\n/* for ChangeHosts */\n\n#define HostInsert\t\t0\n#define HostDelete\t\t1\n\n/* for ChangeAccessControl */\n\n#define EnableAccess\t\t1      \n#define DisableAccess\t\t0\n\n/* Display classes  used in opening the connection \n * Note that the statically allocated ones are even numbered and the\n * dynamically changeable ones are odd numbered */\n\n#define StaticGray\t\t0\n#define GrayScale\t\t1\n#define StaticColor\t\t2\n#define PseudoColor\t\t3\n#define TrueColor\t\t4\n#define DirectColor\t\t5\n\n\n/* Byte order  used in imageByteOrder and bitmapBitOrder */\n\n#define LSBFirst\t\t0\n#define MSBFirst\t\t1\n\n#endif /* X_H */\n"
  },
  {
    "path": "plugins/xsv/x_reqs.c",
    "content": "#include \"../plugin.h\"\n#include <math.h>\n\n#include \"qux.h\"\n#undef strncpy\n\nXRequest XRequests [256];\n\n#ifdef XBigReqExtensionName\nint\tX_BigReqCode;\n#endif\n\nvoid XR_MapWindow(xclient_t *cl, xReq *request);\nvoid XR_UnmapWindow(xclient_t *cl, xReq *request);\n\n\n#define\tGXclear\t\t\t0x0\t\t/* 0 */\n#define GXand\t\t\t0x1\t\t/* src AND dst */\n#define GXandReverse\t\t0x2\t\t/* src AND NOT dst */\n#define GXcopy\t\t\t0x3\t\t/* src */\n#define GXandInverted\t\t0x4\t\t/* NOT src AND dst */\n#define\tGXnoop\t\t\t0x5\t\t/* dst */\n#define GXxor\t\t\t0x6\t\t/* src XOR dst */\n#define GXor\t\t\t0x7\t\t/* src OR dst */\n#define GXnor\t\t\t0x8\t\t/* NOT src AND NOT dst */\n#define GXequiv\t\t\t0x9\t\t/* NOT src XOR dst */\n#define GXinvert\t\t0xa\t\t/* NOT dst */\n#define GXorReverse\t\t0xb\t\t/* src OR NOT dst */\n#define GXcopyInverted\t\t0xc\t\t/* NOT src */\n#define GXorInverted\t\t0xd\t\t/* NOT src OR dst */\n#define GXnand\t\t\t0xe\t\t/* NOT src OR NOT dst */\n#define GXset\t\t\t0xf\t\t/* 1 */\n\n#define GCFunc(src, dst, fnc, out, setval)\t\\\nswitch(fnc)\t\t\t\t\t\t\t\\\n{\t\t\t\t\t\t\t\t\t\\\ncase GXclear:\t\t\t\t\t\t\\\n\tout = 0;\t\t\t\t\t\t\\\n\tbreak;\t\t\t\t\t\t\t\\\ncase GXand:\t\t\t\t\t\t\t\\\n\tout = src&dst;\t\t\t\t\t\\\n\tbreak;\t\t\t\t\t\t\t\\\ncase GXandReverse:\t\t\t\t\t\\\n\tout = src&~dst;\t\t\t\t\t\\\n\tbreak;\t\t\t\t\t\t\t\\\ncase GXcopy:\t\t\t\t\t\t\\\n\tout = src;\t\t\t\t\t\t\\\n\tbreak;\t\t\t\t\t\t\t\\\ncase GXandInverted:\t\t\t\t\t\\\n\tout = ~src&dst;\t\t\t\t\t\\\n\tbreak;\t\t\t\t\t\t\t\\\ncase GXnoop:\t\t\t\t\t\t\\\n\tout = dst;\t\t\t\t\t\t\\\n\tbreak;\t\t\t\t\t\t\t\\\ncase GXxor:\t\t\t\t\t\t\t\\\n\tout = src^dst;\t\t\t\t\t\\\n\tbreak;\t\t\t\t\t\t\t\\\ncase GXor:\t\t\t\t\t\t\t\\\n\tout = src|dst;\t\t\t\t\t\\\n\tbreak;\t\t\t\t\t\t\t\\\ncase GXnor:\t\t\t\t\t\t\t\\\n\tout = ~src&~dst;\t\t\t\t\\\n\tbreak;\t\t\t\t\t\t\t\\\ncase GXequiv:\t\t\t\t\t\t\\\n\tout = ~src^dst;\t\t\t\t\t\\\n\tbreak;\t\t\t\t\t\t\t\\\ncase GXinvert:\t\t\t\t\t\t\\\n\tout = ~dst;\t\t\t\t\t\t\\\n\tbreak;\t\t\t\t\t\t\t\\\ncase GXorReverse:\t\t\t\t\t\\\n\tout = src|~dst;\t\t\t\t\t\\\n\tbreak;\t\t\t\t\t\t\t\\\ncase GXcopyInverted:\t\t\t\t\\\n\tout = ~src;\t\t\t\t\t\t\\\n\tbreak;\t\t\t\t\t\t\t\\\ncase GXorInverted:\t\t\t\t\t\\\n\tout = ~src|dst;\t\t\t\t\t\\\n\tbreak;\t\t\t\t\t\t\t\\\ncase GXnand:\t\t\t\t\t\t\\\n\tout = ~src|~dst;\t\t\t\t\\\n\tbreak;\t\t\t\t\t\t\t\\\ncase GXset:\t\t\t\t\t\t\t\\\n\tout = setval;\t\t\t\t\t\\\n\tbreak;\t\t\t\t\t\t\t\\\n}\nvoid XW_ClearArea(xwindow_t *wnd, int xp, int yp, int width, int height);\n\nvoid XR_QueryExtension (xclient_t *cl, xReq *request)\n{\n\tchar extname[256];\n\txQueryExtensionReply rep;\n\txQueryExtensionReq *req = (xQueryExtensionReq *)request;\n\n\tif (req->nbytes > sizeof(extname)-1)\n\t\treq->nbytes = sizeof(extname)-1;\n\tmemcpy(extname, (char *)(req+1), req->nbytes);\n\textname[req->nbytes] = '\\0';\n\n#ifdef XBigReqExtensionName\n\tif (X_BigReqCode && !strcmp(extname, XBigReqExtensionName))\n\t{\n\t\trep.major_opcode\t= X_BigReqCode;\n\t\trep.present\t\t\t= true;\n\t\trep.first_event\t\t= 0;\n\t\trep.first_error\t\t= 0;\n\t}\n\telse\n#endif\n\t{\n\t\tCon_Printf(\"x11sv plugin: Extension %s not supported\\n\", extname);\n\t\trep.major_opcode\t= 0;\n\t\trep.present\t\t\t= false;\n\t\trep.first_event\t\t= 0;\n\t\trep.first_error\t\t= 0;\n\t}\n\n    rep.type\t\t\t= X_Reply;\n    rep.pad1\t\t\t= 0;\n    rep.sequenceNumber\t= cl->requestnum;\n    rep.length\t\t\t= 0;\n    rep.pad3\t\t\t= 0;\n    rep.pad4\t\t\t= 0;\n    rep.pad5\t\t\t= 0;\n    rep.pad6\t\t\t= 0;\n    rep.pad7\t\t\t= 0;\n\n\tX_SendData(cl, &rep, sizeof(rep));\n}\n\n\nvoid XW_ExposeWindowRegionInternal(xwindow_t *root, int x, int y, int width, int height)\n{\n\tint nx,ny,nw,nh;\n\txEvent ev;\n\tif (!root->mapped || root->inputonly)\n\t\treturn;\n\n\n\tev.u.u.type\t\t\t\t\t\t= VisibilityNotify;\n\tev.u.u.detail\t\t\t\t\t= 0;\n\tev.u.u.sequenceNumber\t\t\t= 0;\n\tev.u.visibility.window\t\t\t= root->res.id;\n\tev.u.visibility.state\t\t\t= VisibilityUnobscured;\n\tev.u.visibility.pad1\t\t\t= 0;\n\tev.u.visibility.pad2\t\t\t= 0;\n\tev.u.visibility.pad3\t\t\t= 0;\n\n\tX_SendNotificationMasked(&ev, root, VisibilityChangeMask);\n\n\tev.u.u.type\t\t\t\t\t\t= Expose;\n\tev.u.u.detail\t\t\t\t\t= 0;\n\tev.u.u.sequenceNumber\t\t\t= 0;\n\tev.u.expose.window\t\t\t\t= root->res.id;\n\tev.u.expose.x\t\t\t\t\t= x;\n\tev.u.expose.y\t\t\t\t\t= y;\n\tev.u.expose.width\t\t\t\t= width;\n\tev.u.expose.height\t\t\t\t= height;\n\tev.u.expose.count\t\t\t\t= false;\t//other expose events following (none - rewrite to group these then send all in one go...)\n\tev.u.expose.pad2\t\t\t\t= 0;\n\n\tX_SendNotificationMasked(&ev, root, ExposureMask);\n\n\tif (root->buffer && root != rootwindow)\n\t{\n//\t\tXW_ClearArea(root, 0, 0, root->width, root->height);\n//\t\tfree(root->buffer);\n//\t\troot->buffer = NULL;\n\t}\n\n\tfor (root = root->child; root; root = root->sibling)\n\t{\n\t\tif (!root->mapped || root->inputonly)\n\t\t\tcontinue;\n\n\t\t//subtract the minpos\n\t\tnx = x - root->xpos;\t\n\t\tnw = width;\n\t\tny = y - root->ypos;\n\t\tnh = height;\n\n\t\t//cap new minpos to the child window.\n\t\tif (nx < 0)\n\t\t{\n\t\t\tnw += nx;\n\t\t\tnx = 0;\n\t\t}\n\t\tif (ny < 0)\n\t\t{\n\t\t\tnh += ny;\n\t\t\tny = 0;\n\t\t}\n\n\t\t//cap new maxpos\n\t\tif (nx+nw > x + root->width)\n\t\t\tnw = x+root->width - nx;\n\t\tif (ny+nh > y + root->height)\n\t\t\tnh = y+root->height - ny;\n\n\t\tif (nw > 0 && nh > 0)\t//make sure some is valid.\n\t\t\tXW_ExposeWindowRegionInternal(root, nx, ny, nw, nh);\n\t}\n}\n\nvoid XW_ExposeWindow(xwindow_t *root, int x, int y, int width, int height)\n{//we have to go back to the root so we know the exact region, and can expose our sibling's windows.\n\twhile(root)\n\t{\n\t\tx += root->xpos;\n\t\ty += root->ypos;\n\t\troot = root->parent;\n\t}\n\t\n\tXW_ExposeWindowRegionInternal(rootwindow, x, y, width, height);\n}\n\nvoid XR_ListExtensions (xclient_t *cl, xReq *request)\n{\n\tchar buffer[8192];\n\txListExtensionsReply *rep = (xListExtensionsReply *)buffer;\n\tchar *out;\n\n    rep->type\t\t\t= X_Reply;\n    rep->nExtensions\t= 0;\n    rep->sequenceNumber\t= cl->requestnum;\n    rep->length\t\t\t= 0;\n    rep->pad2\t\t\t= 0;\n    rep->pad3\t\t\t= 0;\n    rep->pad4\t\t\t= 0;\n    rep->pad5\t\t\t= 0;\n    rep->pad6\t\t\t= 0;\n    rep->pad7\t\t\t= 0;\n\n\tout = (char *)(rep+1);\n\n#ifdef XBigReqExtensionName\n\trep->nExtensions++;\n\tstrcpy(out, XBigReqExtensionName);\n\tout+=strlen(out)+1;\n#endif\n\n\trep->length = (out-(char *)(rep+1) + 3)/4;\n\n\n\tX_SendData(cl, rep, sizeof(xListExtensionsReply) + rep->length*4);\n}\n\nvoid XR_SetCloseDownMode(xclient_t *cl, xReq *request)\n{\n\txSetCloseDownModeReq *req = (xSetCloseDownModeReq*)request;\n\n\tswitch(req->mode)\n\t{\n\tcase DestroyAll:\n\tcase RetainPermanent:\n\tcase RetainTemporary:\n\t\tbreak;\n\tdefault:\n\t\tX_SendError(cl, BadValue, req->mode, X_SetCloseDownMode, 0);\n\t\treturn;\n\t}\n\tcl->closedownmode = req->mode;\n}\n\nvoid XR_GetAtomName (xclient_t *cl, xReq *request)\n{\n\txResourceReq *req = (xResourceReq*)request;\n\tchar buffer[8192];\n\txGetAtomNameReply *rep = (xGetAtomNameReply*)buffer;\n\n\txatom_t\t*xa;\n\n\tif (XS_GetResource(req->id, (void**)&xa) != x_atom)\n\t{\n\t\tX_SendError(cl, BadAtom, req->id, X_GetAtomName, 0);\n\t\treturn;\n\t}\n\n\trep->type\t\t\t= X_Reply;\n\trep->pad1\t\t\t= 0;\n\trep->sequenceNumber\t= cl->requestnum;\n\trep->length\t\t\t= (strlen(xa->atomname)+3)/4;\n\trep->nameLength\t\t= strlen(xa->atomname);\n\trep->pad2\t\t\t= 0;\n\trep->pad3\t\t\t= 0;\n\trep->pad4\t\t\t= 0;\n\trep->pad5\t\t\t= 0;\n\trep->pad6\t\t\t= 0;\n\trep->pad7\t\t\t= 0;\n\tstrcpy((char *)(rep+1), xa->atomname);\n\n\tX_SendData(cl, rep, sizeof(*rep)+rep->length*4);\n}\n\nvoid XR_InternAtom (xclient_t *cl, xReq *request)\n{\n\txInternAtomReq *req = (xInternAtomReq*)request;\n\txInternAtomReply rep;\n\tchar atomname[1024];\n\tAtom atom;\n\n\tif (req->nbytes >= sizeof(atomname))\n\t{\t//exceeded that limit then...\n\t\tX_SendError(cl, BadImplementation, 0, X_InternAtom, 0);\n\t\treturn;\n\t}\n\n\tstrncpy(atomname, (char *)(req+1), req->nbytes);\n\tatomname[req->nbytes] = '\\0';\n\n\tatom = XS_FindAtom(atomname);\n\tif (atom == None && !req->onlyIfExists)\n\t{\n\t\tatom = XS_NewResource();\n\t\tXS_CreateAtom(atom, atomname, NULL);\t//global atom...\n\t}\n\n\trep.type\t= X_Reply;\n\trep.pad1\t= 0;\n\trep.sequenceNumber\t= cl->requestnum;\n\trep.length\t= 0;\n\trep.atom\t= atom;\n\trep.pad2\t= 0;\n\trep.pad3\t= 0;\n\trep.pad4\t= 0;\n\trep.pad5\t= 0;\n\trep.pad6\t= 0;\n\n\tX_SendData(cl, &rep, sizeof(rep));\n}\n\nvoid XR_GetProperty (xclient_t *cl, xReq *request)\n{\n\txGetPropertyReq *req = (xGetPropertyReq*)request;\n\tchar buffer[8192];\n\txwindow_t *wnd;\n\tint datalen;\n\tint format;\n\tint trailing;\n\txGetPropertyReply *rep = (xGetPropertyReply*)buffer;\n\tAtom proptype;\n\n\tif (XS_GetResource(req->window, (void**)&wnd) != x_window)\n\t{\t//wait a minute, That's not a window!!!\n\t\tX_SendError(cl, BadWindow, req->window, X_GetProperty, 0);\n\t\treturn;\n\t}\n\tif (XS_GetResource(req->property, (void**)NULL) != x_atom)\n\t{\t//whoops\n\t\tX_SendError(cl, BadAtom, req->property, X_GetProperty, 0);\n\t\treturn;\n\t}\n\n\tif (req->longLength > sizeof(buffer) - sizeof(req)/4)\n\t\treq->longLength = sizeof(buffer) - sizeof(req)/4;\n\n\tdatalen = XS_GetProperty(wnd, req->property, &proptype, (char *)(rep+1), req->longLength*4, req->longOffset*4, &trailing, &format);\n\n\trep->type\t\t\t= X_Reply;\n    rep->format\t\t\t= format;\n    rep->propertyType\t= proptype;\n    rep->sequenceNumber\t= cl->requestnum;\n    rep->length\t\t\t= (datalen+3)/4;\n\t//rep->propertyType\t= None;\n    rep->bytesAfter\t\t= trailing;\n\tif (format)\n\t\trep->nItems\t\t\t= datalen / (format/8);\n\telse\n\t\trep->nItems\t\t= 0;\n    rep->pad1\t\t\t= 0;\n    rep->pad2\t\t\t= 0;\n    rep->pad3\t\t\t= 0;\n\n\tX_SendData(cl, rep, rep->length*4 + sizeof(*rep));\n\n\tif (req->delete)\n\t{\n\t\txEvent ev;\n\n\t\tXS_DeleteProperty(wnd, req->property);\n\n\t\tev.u.u.type\t\t\t\t\t\t= PropertyNotify;\n\t\tev.u.u.detail\t\t\t\t\t= 0;\n\t\tev.u.u.sequenceNumber\t\t\t= 0;\n\t\tev.u.property.window\t\t\t= req->window;\n\t\tev.u.property.atom\t\t\t\t= req->property;\n\t\tev.u.property.time\t\t\t\t= plugfuncs->GetMilliseconds();\n\t\tev.u.property.state\t\t\t\t= PropertyDelete;\n\n\t\tev.u.property.pad1\t\t\t\t= 0;\n\t\tev.u.property.pad2\t\t\t\t= 0;\n\n\t\tX_SendNotificationMasked(&ev, wnd, PropertyChangeMask);\n\t}\n}\n\nvoid XR_ListProperties(xclient_t *cl, xReq *request)\n{\n\txproperty_t *xp;\n\txResourceReq *req = (xResourceReq*)request;\n\tchar buffer[65536];\n\txwindow_t *wnd;\n\txListPropertiesReply *rep = (xListPropertiesReply*)buffer;\n\tAtom *out = (Atom *)(rep+1);\n\n\tif (XS_GetResource(req->id, (void**)&wnd) != x_window)\n\t{\t//wait a minute, That's not a window!!!\n\t\tX_SendError(cl, BadWindow, req->id, X_GetProperty, 0);\n\t\treturn;\n\t}\n\n\n\trep->type\t\t\t= X_Reply;\n    rep->sequenceNumber\t= cl->requestnum;\n    rep->length\t\t\t= 0;\n\trep->nProperties\t= 0;\n    rep->pad1\t\t\t= 0;\n    rep->pad2\t\t\t= 0;\n    rep->pad3\t\t\t= 0;\n\trep->pad4\t\t\t= 0;\n\trep->pad5\t\t\t= 0;\n\trep->pad6\t\t\t= 0;\n\trep->pad7\t\t\t= 0;\n\n\tfor (xp = wnd->properties; xp; xp = xp->next)\n\t{\n\t\trep->nProperties++;\n\t\t*out = xp->atomid;\n\t}\n\n\trep->length = rep->nProperties;\n\n\tX_SendData(cl, rep, rep->length*4 + sizeof(*rep));\n}\n\nvoid XR_ChangeProperty (xclient_t *cl, xReq *request)\n{\n\txChangePropertyReq *req = (xChangePropertyReq*)request;\n\tint len;\n\n\txatom_t *atom;\n\txwindow_t *wnd;\n\n\tif (XS_GetResource(req->window, (void**)&wnd) != x_window)\n\t{\t//wait a minute, That's not a window!!!\n\t\tX_SendError(cl, BadWindow, req->window, X_ChangeProperty, 0);\n\t\treturn;\n\t}\n\n\tif (XS_GetResource(req->property, (void**)&atom) != x_atom)\n\t{\n\t\tX_SendError(cl, BadAtom, req->property, X_ChangeProperty, 0);\n\t\treturn;\n\t}\n\n\tlen = req->nUnits * (req->format/8);\n\n\tif (req->mode == PropModeReplace)\n\t\tXS_SetProperty(wnd, req->property, req->type, (char *)(req+1), len, req->format);\n\telse if (req->mode == PropModePrepend)\n\t{\n\t\tX_SendError(cl, BadImplementation, req->window, X_ChangeProperty, 0);\n\t\treturn;\n\t}\n\telse if (req->mode == PropModeAppend)\n\t{\n\t\tchar hugebuffer[65536];\n\t\tint trailing;\n\t\tint format, datalen;\n\t\tAtom proptype;\n\n\n\t\tdatalen = XS_GetProperty(wnd, req->property, &proptype, hugebuffer, sizeof(hugebuffer), 0, &trailing, &format);\n\t\tif (datalen+len > sizeof(hugebuffer))\n\t\t{\n\t\t\tX_SendError(cl, BadImplementation, req->window, X_ChangeProperty, 0);\n\t\t\treturn;\n\t\t}\n\t\tmemcpy(hugebuffer + datalen, (char *)(req+1), len);\n\n\t\tXS_SetProperty(wnd, req->property, proptype, hugebuffer, datalen+len, format);\n\t}\n\n\t{\n\t\txEvent ev;\n\n\t\tev.u.u.type\t\t\t\t\t\t= PropertyNotify;\n\t\tev.u.u.detail\t\t\t\t\t= 0;\n\t\tev.u.u.sequenceNumber\t\t\t= 0;\n\t\tev.u.property.window\t\t\t= req->window;\n\t\tev.u.property.atom\t\t\t\t= req->property;\n\t\tev.u.property.time\t\t\t\t= plugfuncs->GetMilliseconds();\n\t\tev.u.property.state\t\t\t\t= PropertyNewValue;\n\n\t\tev.u.property.pad1\t\t\t\t= 0;\n\t\tev.u.property.pad2\t\t\t\t= 0;\n\n\t\tX_SendNotificationMasked(&ev, wnd, PropertyChangeMask);\n\t}\n}\n\nvoid XR_DeleteProperty(xclient_t *cl, xReq *request)\n{\n\txDeletePropertyReq *req = (xDeletePropertyReq*)request;\n\n\txwindow_t *wnd;\n\n\tif (XS_GetResource(req->window, (void**)&wnd) != x_window)\n\t{\t//wait a minute, That's not a window!!!\n\t\tX_SendError(cl, BadWindow, req->window, X_DeleteProperty, 0);\n\t\treturn;\n\t}\n\n\tif (XS_GetResource(req->property, (void**)NULL) != x_atom)\n\t{\n\t\tX_SendError(cl, BadAtom, req->property, X_DeleteProperty, 0);\n\t\treturn;\n\t}\n\n\tXS_DeleteProperty(wnd, req->property);\n\n\t{\n\t\txEvent ev;\n\n\t\tev.u.u.type\t\t\t\t\t\t= PropertyNotify;\n\t\tev.u.u.detail\t\t\t\t\t= 0;\n\t\tev.u.u.sequenceNumber\t\t\t= 0;\n\t\tev.u.property.window\t\t\t= req->window;\n\t\tev.u.property.atom\t\t\t\t= req->property;\n\t\tev.u.property.time\t\t\t\t= plugfuncs->GetMilliseconds();\n\t\tev.u.property.state\t\t\t\t= PropertyDelete;\n\n\t\tev.u.property.pad1\t\t\t\t= 0;\n\t\tev.u.property.pad2\t\t\t\t= 0;\n\n\t\tX_SendNotificationMasked(&ev, wnd, PropertyChangeMask);\n\t}\n}\n\nvoid XR_GetSelectionOwner (xclient_t *cl, xReq *request)\n{\n\txResourceReq *req = (xResourceReq *)request;\n\txGetSelectionOwnerReply reply;\n\txatom_t *atom;\n\n\tif (XS_GetResource(req->id, (void**)&atom) != x_atom)\n\t{\n\t\tX_SendError(cl, BadAtom, req->id, X_GetSelectionOwner, 0);\n\t\treturn;\n\t}\n\n\tif (XS_GetResource(atom->selectionownerwindowid, (void**)NULL) != x_window)\t//make sure the window still exists.\n\t{\n\t\tatom->selectionownerwindowid = None;\n\t}\n\n\treply.type\t\t\t\t= X_Reply;\n    reply.sequenceNumber\t= cl->requestnum;\n    reply.length\t\t\t= 0;\n\treply.owner\t\t\t\t= atom->selectionownerwindowid;\n    reply.pad1\t\t\t\t= 0;\n    reply.pad2\t\t\t\t= 0;\n    reply.pad3\t\t\t\t= 0;\n\treply.pad4\t\t\t\t= 0;\n\treply.pad5\t\t\t\t= 0;\n\treply.pad6\t\t\t\t= 0;\n\n\tX_SendData(cl, &reply, sizeof(reply));\n}\nvoid XR_SetSelectionOwner (xclient_t *cl, xReq *request)\n{\n\txSetSelectionOwnerReq *req = (xSetSelectionOwnerReq *)request;\n\txatom_t *atom;\n\txwindow_t *window;\n\n\tif (XS_GetResource(req->selection, (void**)&atom) != x_atom)\n\t{\n\t\tX_SendError(cl, BadAtom, req->selection, X_SetSelectionOwner, 0);\n\t\treturn;\n\t}\n\n\tif (XS_GetResource(req->window, (void**)&window) != x_window)\t//make sure the window still exists.\n\t{\n\t\tX_SendError(cl, BadWindow, req->window, X_SetSelectionOwner, 0);\n\t\treturn;\n\t}\n\n\tif (req->window)\n\t{\n\t\tatom->selectionownerwindowid = req->window;\n\t\tatom->selectionownerclient = cl;\n\t}\n\telse\n\t{\n\t\tatom->selectionownerwindowid = None;\n\t\tatom->selectionownerclient = NULL;\n\t}\n}\n\nvoid XR_ConvertSelection (xclient_t *cl, xReq *request)\n{\n\txConvertSelectionReq *req = (void*)request;\n\txatom_t *atom = XS_GetResourceType(req->selection, x_atom);\n\txEvent rep;\n\n\tif (atom && atom->selectionownerwindowid)\n\t{\t//forward the request to the selection's owner\n\t\trep.u.u.type = SelectionRequest;\n\t\trep.u.u.detail = 0;\n\t\trep.u.u.sequenceNumber = 0;\n\t\trep.u.selectionRequest.time = req->time;\n\t\trep.u.selectionRequest.owner = atom->selectionownerwindowid;\n\t\trep.u.selectionRequest.target = req->target;\n\t\trep.u.selectionRequest.property = req->property;\n\t\trep.u.selectionRequest.requestor = req->requestor;\n\t\trep.u.selectionRequest.selection = req->selection;\n\t\tX_SendData(atom->selectionownerclient, &rep, sizeof(rep));\n\t}\n\telse\n\t{\t//return it back to the sender, or so\n\t\trep.u.u.type = SelectionNotify;\n\t\trep.u.u.detail = 0;\n\t\trep.u.u.sequenceNumber = 0;\n\t\trep.u.selectionNotify.time = req->time;\n\t\trep.u.selectionNotify.target = req->target;\n\t\trep.u.selectionNotify.property = req->property;\n\t\trep.u.selectionNotify.requestor = req->requestor;\n\t\trep.u.selectionNotify.selection = req->selection;\n\t\tX_SendData(cl, &rep, sizeof(rep));\n\t}\n}\n\n\nextern int x_windowwithcursor;\n\nvoid XR_GetInputFocus (xclient_t *cl, xReq *request)\n{\n\txGetInputFocusReply rep;\n\textern xwindow_t *xfocusedwindow;\n\n    rep.type\t\t\t= X_Reply;\n    rep.revertTo\t\t= None;\n    rep.sequenceNumber\t= cl->requestnum;\n    rep.length\t\t\t= 0;\n    rep.focus\t\t\t= xfocusedwindow?xfocusedwindow->res.id:None;\n    rep.pad1\t\t\t= 0;\n    rep.pad2 \t\t\t= 0;\n    rep.pad3\t\t\t= 0;\n    rep.pad4\t\t\t= 0;\n    rep.pad5\t\t\t= 0;\n\n\n\tX_SendData(cl, &rep, sizeof(rep));\n}\n\nvoid XR_SetInputFocus (xclient_t *cl, xReq *request)\n{\n\textern xwindow_t *xfocusedwindow;\n\txResourceReq\t*req = (xResourceReq *)request;\n\txwindow_t *wnd;\n\n\tif (XS_GetResource(req->id, (void**)&wnd) != x_window)\n\t{\n\t\tX_SendError(cl, BadDrawable, req->id, X_SetInputFocus, 0);\n\t\treturn;\n\t}\n\t\n\txfocusedwindow = wnd;\n\n\tX_EvalutateFocus(NotifyWhileGrabbed);\n}\n\nvoid XR_QueryBestSize (xclient_t *cl, xReq *request)\n{\n\txQueryBestSizeReq\t*req = (xQueryBestSizeReq\t*)request;\n\txQueryBestSizeReply rep;\n\n\tif (req->class == CursorShape && req->drawable != rootwindow->res.id)\n\t{\n\t\tX_SendError(cl, BadDrawable, req->drawable, X_QueryBestSize, req->class);\n\t\treturn;\n\t}\n\telse if (req->class != CursorShape)\n\t{\n\t\tX_SendError(cl, BadImplementation, req->drawable, X_QueryBestSize, req->class);\n\t\treturn;\n\t}\n\n\trep.type\t\t\t= X_Reply;\n\trep.pad1\t\t\t= 0;\n\trep.sequenceNumber\t= cl->requestnum;\n\trep.length\t\t\t= 0;\n\trep.width\t\t\t= req->width;\n\trep.height\t\t\t= req->height;\n\trep.pad3\t\t\t= 0;\n\trep.pad4\t\t\t= 0;\n\trep.pad5\t\t\t= 0;\n\trep.pad6\t\t\t= 0;\n\trep.pad7\t\t\t= 0;\n\n\tX_SendData(cl, &rep, sizeof(rep));\n}\n\nvoid XR_GetGeometry (xclient_t *cl, xReq *request)\n{ \n\txResourceReq *req = (xResourceReq *)request;\n\txGetGeometryReply\trep;\n\txresource_t *drawable;\n\n\txwindow_t *wnd;\n\txpixmap_t *pm;\n\n\trep.type\t\t\t= X_Reply;\n\trep.depth\t\t\t= 24;\n\trep.sequenceNumber\t= cl->requestnum;\n\trep.length\t\t\t= 0;\n\trep.root\t\t\t= 0;\n\trep.x\t\t\t\t= 0;\n\trep.y\t\t\t\t= 0;\n\trep.width\t\t\t= 0;\n\trep.height\t\t\t= 0;\n\trep.borderWidth\t\t= 0;\n\trep.pad1\t\t\t= 0;\n\trep.pad2\t\t\t= 0;\n\trep.pad3\t\t\t= 0;\n\n\tswitch(XS_GetResource(req->id, (void**)&drawable))\n\t{\n\tcase x_window:\n\t\twnd = (xwindow_t*)drawable;\n\t\trep.x = wnd->xpos;\n\t\trep.y = wnd->ypos;\n\t\trep.borderWidth = 0;\t//fixme\n\t\trep.width = wnd->width;\n\t\trep.height = wnd->height;\n\t\trep.root = rootwindow->res.id;\n\t\tbreak;\n\tcase x_pixmap:\n\t\tpm = (xpixmap_t*)drawable;\n\t\trep.width = pm->width;\n\t\trep.height = pm->height;\n\t\tbreak;\n\tdefault:\n\t\tX_SendError(cl, BadDrawable, req->id, X_GetGeometry, 0);\n\t\treturn;\n\t}\n\n\n\tX_SendData(cl, &rep, sizeof(rep));\n}\n\nvoid XR_CreateWindow (xclient_t *cl, xReq *request)\n{\n\txCreateWindowReq *req = (xCreateWindowReq *)request;\n\txwindow_t *parent;\n\txwindow_t *wnd;\n\tCARD32 *parameters;\n\n\tif (req->class == InputOnly && req->depth != 0)\n\t{\n\t\tX_SendError(cl, BadMatch, req->wid, X_CreateWindow, 0);\n\t\treturn;\n\t}\n\tif (XS_GetResource(req->wid, (void**)&parent) != x_none)\n\t{\n\t\tX_SendError(cl, BadIDChoice, req->wid, X_CreateWindow, 0);\n\t\treturn;\n\t}\n\n\tif (XS_GetResource(req->parent, (void**)&parent) != x_window)\n\t{\n\t\tX_SendError(cl, BadWindow, req->parent, X_CreateWindow, 0);\n\t\treturn;\n\t}\n\n\twnd = XS_CreateWindow(req->wid, cl, parent, req->x, req->y, req->width, req->height);\n\n\tif (req->depth != 0)\n\t\twnd->depth = req->depth;\n\telse\n\t\twnd->depth = parent->depth;\n\n\tif (req->class == CopyFromParent)\n\t\twnd->inputonly = parent->inputonly;\n\telse\n\t\twnd->inputonly = (req->class == InputOnly);\n\n\t//FIXME: Depth must be valid\n\t//FIXME: visual id must be valid.\n\n\tparameters = (CARD32 *)(req+1);\n\tif (req->mask & CWBackPixmap)\n\t{\n\t\twnd->backpixmap = NULL;\n\t\tif (XS_GetResource(*parameters, (void**)&wnd->backpixmap) != x_pixmap)\n\t\t{\n\t\t\tif (*parameters)\n\t\t\t\tX_SendError(cl, BadPixmap, *parameters, X_CreateWindow, 0);\n\t\t}\n\t\telse\n\t\t\twnd->backpixmap->references++;\n\t\tparameters++;\n\t}\n\tif (req->mask & CWBackPixel)//\n\t{\n\t\twnd->backpixel = *parameters;\n\t\tparameters++;\n\t}\n\tif (req->mask & CWBorderPixmap)\n\t\tparameters+=0;\n\tif (req->mask & CWBorderPixel)//\n\t{\n\t\twnd->borderpixel = *parameters;\n\t\tparameters++;\n\t}\n\tif (req->mask & CWBitGravity)//\n\t{\n\t\twnd->bitgravity = *parameters;\n\t\tparameters++;\n\t}\n\tif (req->mask & CWWinGravity)\n\t\twnd->bitgravity = *parameters++;\n\tif (req->mask & CWBackingStore)\n\t\tparameters++;\t//ignored\n\tif (req->mask & CWBackingPlanes)\n\t\tparameters+=0;\n\tif (req->mask & CWBackingPixel)\n\t\tparameters+=0;\n\tif (req->mask & CWOverrideRedirect)\n\t\twnd->overrideredirect = *parameters++;\n\telse\n\t\twnd->overrideredirect = false;\n\tif (req->mask & CWSaveUnder)\n\t\tparameters++;\n\tif (req->mask & CWEventMask)//\n\t{\n\t\txnotificationmask_t *nm;\n\t\tnm = malloc(sizeof(xnotificationmask_t));\n\t\tnm->client = cl;\n\t\tnm->next = NULL;\n\t\tnm->mask = *parameters;\n\t\twnd->notificationmask = nm;\n\t\tparameters++;\n\n\t\twnd->notificationmasks = 0;\n\t\tfor (nm = wnd->notificationmask; nm; nm = nm->next)\n\t\t\twnd->notificationmasks |= nm->mask;\n\t}\n\tif (req->mask & CWDontPropagate)\n\t\twnd->donotpropagate = *parameters++;\n\tif (req->mask & CWColormap)//\n\t{\n\t\twnd->colormap = *parameters;\n\t\tparameters++;\n\t}\n\tif (req->mask & CWCursor)\n\t\tparameters++;\n\n\t#define CWBackPixmap\t\t(1L<<0)\n#define CWBackPixel\t\t(1L<<1)\n#define CWBorderPixmap\t\t(1L<<2)\n#define CWBorderPixel           (1L<<3)\n#define CWBitGravity\t\t(1L<<4)\n#define CWWinGravity\t\t(1L<<5)\n#define CWBackingStore          (1L<<6)\n#define CWBackingPlanes\t        (1L<<7)\n#define CWBackingPixel\t        (1L<<8)\n#define CWOverrideRedirect\t(1L<<9)\n#define CWSaveUnder\t\t(1L<<10)\n#define CWEventMask\t\t(1L<<11)\n#define CWDontPropagate\t        (1L<<12)\n#define CWColormap\t\t(1L<<13)\n#define CWCursor\t        (1L<<14)\n/*\n    CARD8 depth;\n    Window wid;\n\tWindow parent;\n    INT16 x B16, y B16;\n    CARD16 width B16, height B16, borderWidth B16;  \n#if defined(__cplusplus) || defined(c_plusplus)\n    CARD16 c_class B16;\n#else\n    CARD16 class B16;\n#endif\n    VisualID visual B32;\n    CARD32 mask B32;\n*/\n\n\tif (wnd->inputonly)\n\t\treturn;\n\n\n\t{\n\t\txEvent ev;\n\n\t\tev.u.u.type\t\t\t\t\t\t= CreateNotify;\n\t\tev.u.u.detail\t\t\t\t\t= 0;\n\t\tev.u.u.sequenceNumber\t\t\t= 0;\n\t\tev.u.createNotify.parent\t\t= wnd->parent->res.id;\n\t\tev.u.createNotify.window\t\t= wnd->res.id;\n\t\tev.u.createNotify.x\t\t\t\t= wnd->xpos;\n\t\tev.u.createNotify.y\t\t\t\t= wnd->ypos;\n\t\tev.u.createNotify.width\t\t\t= wnd->width;\n\t\tev.u.createNotify.height\t\t= wnd->height; \n\t\tev.u.createNotify.borderWidth\t= req->borderWidth;\n\t\tev.u.createNotify.override\t\t= wnd->overrideredirect;\n\t\tev.u.createNotify.bpad\t\t\t= 0;\n\n\t\tX_SendNotificationMasked (&ev, wnd, SubstructureNotifyMask);\n\t}\n\n/*\t{\n\t\txEvent ev;\n\n\t\tev.u.u.type\t\t\t\t\t\t= MapRequest;\n\t\tev.u.u.detail\t\t\t\t\t= 0;\n\t\tev.u.u.sequenceNumber\t\t\t= 0;\n\t\tev.u.mapRequest.window\t\t\t= wnd->res.id;\n\t\tev.u.mapRequest.parent\t\t\t= wnd->parent->res.id;\n\n\t\tX_SendNotificationMasked(&ev, wnd, SubstructureRedirectMask);\n\t}*/\n/*\t{\n\t\txEvent ev;\n\n\t\tev.u.u.type\t\t\t\t\t\t= GraphicsExpose;\n\t\tev.u.u.detail\t\t\t\t\t= 0;\n\t\tev.u.u.sequenceNumber\t\t\t= 0;\n\t\tev.u.expose.window\t\t\t\t= wnd->res.id;\n\t\tev.u.expose.x\t\t\t\t\t= 0;\n\t\tev.u.expose.y\t\t\t\t\t= 0;\n\t\tev.u.expose.width\t\t\t\t= wnd->width;\n\t\tev.u.expose.height\t\t\t\t= wnd->height;\n\t\tev.u.expose.count\t\t\t\t= 0;\t//matching expose events after this one\n\t\tev.u.expose.pad2\t\t\t\t= 0;\n\n\t\tX_SendNotificationMasked(&ev, wnd, ExposureMask);\n\t}*/\n}\n\nvoid XR_ChangeWindowAttributes (xclient_t *cl, xReq *request)\n{\n\tCARD32 *parameters;\n\txChangeWindowAttributesReq *req = (xChangeWindowAttributesReq *)request;\n\txwindow_t *wnd;\n\n\tif (XS_GetResource(req->window, (void**)&wnd) != x_window)\n\t{\n\t\tX_SendError(cl, BadWindow, req->window, X_ChangeWindowAttributes, 0);\n\t\treturn;\n\t}\n\n\tparameters = (CARD32 *)(req+1);\n\n\tif (req->valueMask & CWBackPixmap)\n\t{\n\t\tif (wnd->backpixmap)\n\t\t\twnd->backpixmap->references--;\n\t\twnd->backpixmap = NULL;\n\t\tif (XS_GetResource(*parameters, (void**)&wnd->backpixmap) != x_pixmap)\n\t\t{\n\t\t\tif (*parameters)\n\t\t\t\tX_SendError(cl, BadPixmap, *parameters, X_ChangeWindowAttributes, 0);\n\t\t}\n\t\telse\n\t\t\twnd->backpixmap->references++;\n\t\tparameters++;\n\t}\n\n\tif (req->valueMask & CWBackPixel)\n\t{\n\t\tif (wnd->backpixmap)\n\t\t\twnd->backpixmap->references--;\n\t\twnd->backpixmap = NULL;\n\t\twnd->backpixel = *parameters++;\n\t}\n\n\tif (req->valueMask & CWBorderPixmap)\n\t{\n\t\tX_SendError(cl, BadImplementation, 0, X_ChangeWindowAttributes, 0);\n/*\t\twnd->borderpixmap = NULL;\n\t\tif (XS_GetResource(*parameters, (void**)&wnd->borderpixmap) != x_pixmap)\n\t\t{\n\t\t\tif (*parameters)\n\t\t\t\tX_SendError(cl, BadPixmap, *parameters, X_ChangeWindowAttributes, 0);\n\t\t}\n\t\telse\n\t\t\twnd->backpixmap->references++;\n*/\t\tparameters++;\n\t}\n\n\tif (req->valueMask & CWBorderPixel)\n\t\twnd->borderpixel = *parameters++;\n\t\n\tif (req->valueMask & CWBitGravity)\n\t{\n\t\twnd->bitgravity = *parameters++;\n\t}\n\n\tif (req->valueMask & CWWinGravity)\n\t{\n\t\twnd->wingravity = *parameters++;\n\t}\n\n\tif (req->valueMask & CWBackingStore)\n\t{\n//\t\tX_SendError(cl, BadImplementation, 0, X_ChangeWindowAttributes, 0);\n\t\tparameters++;\t//ignore\n\t}\n\n\tif (req->valueMask & CWBackingPlanes)\n\t{\n\t\tX_SendError(cl, BadImplementation, 0, X_ChangeWindowAttributes, 0);\n\t\tparameters++;\n\t}\n\n\tif (req->valueMask & CWBackingPixel)\n\t{\n\t\tX_SendError(cl, BadImplementation, 0, X_ChangeWindowAttributes, 0);\n\t\tparameters++;\n\t}\n\n\tif (req->valueMask & CWOverrideRedirect)\n\t{\n\t\twnd->overrideredirect = *parameters++;\n\t}\n\n\tif (req->valueMask & CWSaveUnder)\n\t{\n//\t\tX_SendError(cl, BadImplementation, 0, X_ChangeWindowAttributes, 0);\n\t\tparameters++;\n\t}\n\n\tif (req->valueMask & CWEventMask)\n\t{\n\t\txnotificationmask_t *nm;\n\n\t\tif (*parameters & (SubstructureRedirectMask | ResizeRedirectMask))\n\t\t{\t//you're only allowed one client with that one at a time.\n\t\t\tfor (nm = wnd->notificationmask; nm; nm = nm->next)\n\t\t\t{\n\t\t\t\tif (nm->mask & (*parameters))\n\t\t\t\t\tif (nm->client != cl)\n\t\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t\tnm = NULL;\n\t\tif (nm)\t//client has this one.\n\t\t\tX_SendError(cl, BadAccess, *parameters, X_ChangeWindowAttributes, CWEventMask);\n\t\telse\n\t\t{\n\t\t\tfor (nm = wnd->notificationmask; nm; nm = nm->next)\n\t\t\t{\n\t\t\t\tif (nm->client == cl)\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (!nm)\n\t\t\t{\n\t\t\t\tnm = malloc(sizeof(xnotificationmask_t));\n\t\t\t\tnm->next = wnd->notificationmask;\n\t\t\t\twnd->notificationmask = nm;\n\t\t\t\tnm->client = cl;\n\t\t\t}\n\t\t\tnm->mask = *parameters;\n\n\t\t\twnd->notificationmasks = 0;\n\t\t\tfor (nm = wnd->notificationmask; nm; nm = nm->next)\n\t\t\t\twnd->notificationmasks |= nm->mask;\n\t\t}\n\t\tparameters++;\n\t}\n\n\tif (req->valueMask & CWDontPropagate)\n\t{\n\t\twnd->donotpropagate = *parameters++;\n\t}\n\n\tif (req->valueMask & CWColormap)\n\t{\n\t\tX_SendError(cl, BadImplementation, 0, X_ChangeWindowAttributes, 0);\n\t\tparameters++;\n\t}\n\n\tif (req->valueMask & CWCursor)\n\t{\n//\t\tX_SendError(cl, BadImplementation, 0, X_ChangeWindowAttributes, 0);\n\t\tparameters++;\n\t}\n\n\txrefreshed=true;\n\n\tif (req->valueMask > CWCursor)\t//anything else is an error on some implementation's part.\n\t\tX_SendError(cl, BadImplementation, 0, X_ChangeWindowAttributes, 0);\n\n//\tXW_ClearArea(wnd, 0, 0, wnd->width, wnd->height);\n}\n\nvoid XR_ConfigureWindow (xclient_t *cl, xReq *request)\n{\n\tint newx, newy, neww, newh, sibid, newbw, stackmode;\n\txConfigureWindowReq *req = (xConfigureWindowReq *)request;\n\txwindow_t *wnd;\n\n\tCARD32 *parm;\n\n\tif (XS_GetResource(req->window, (void**)&wnd) != x_window)\n\t{\n\t\tX_SendError(cl, BadWindow, req->window, X_ConfigureWindow, 0);\n\t\treturn;\n\t}\n\n\tif (!wnd->parent)\t//root window.\n\t{\t//can't resize this one.\n\t\tX_SendError(cl, BadWindow, req->window, X_ConfigureWindow, 0);\n\t\treturn;\n\t}\n\n\tparm = (CARD32 *)(req+1);\n\n    if (req->mask & CWX)\n\t\tnewx = *parm++;\n\telse\n\t\tnewx=wnd->xpos;\n\tif (req->mask & CWY)\n\t\tnewy = *parm++;\n\telse\n\t\tnewy=wnd->ypos;\n\n\tif (req->mask & CWWidth)\n\t\tneww = *parm++;\n\telse\n\t\tneww=wnd->width;\n\n\tif (wnd->width <= 0)\n\t\twnd->width = 1;\n\n\tif (req->mask & CWHeight)\n\t\tnewh = *parm++;\n\telse\n\t\tnewh=wnd->height;\n\n\tif (req->mask & CWBorderWidth)\n\t\tnewbw = *parm++;\n\telse\n\t\tnewbw = 0;\n\tif (req->mask & CWSibling)\n\t\tsibid = *parm++;\n\telse\n\t\tsibid = None;\n\tif (req->mask & CWStackMode)\n\t\tstackmode = *parm++;\n\telse\n\t\tstackmode = Above;\n\n\tif (!wnd->overrideredirect && X_NotifcationMaskPresent(wnd, SubstructureRedirectMask, cl))\n\t{\n\t\txEvent ev;\n\n\t\tev.u.u.type\t\t\t\t\t\t\t= ConfigureRequest;\n\t\tev.u.u.detail\t\t\t\t\t\t= stackmode;\n\t\tev.u.u.sequenceNumber\t\t\t\t= 0;\n\t\tev.u.configureRequest.parent\t\t= wnd->parent->res.id;\n\t\tev.u.configureRequest.window\t\t= wnd->res.id;\n\t\tev.u.configureRequest.sibling\t\t= sibid;\n\t\tev.u.configureRequest.x\t\t\t\t= newx;\n\t\tev.u.configureRequest.y\t\t\t\t= newy;\n\t\tev.u.configureRequest.width\t\t\t= neww;\n\t\tev.u.configureRequest.height\t\t= newh;\n\t\tev.u.configureRequest.borderWidth\t= newbw;\n\t\tev.u.configureRequest.valueMask\t\t= req->mask;\n\t\tev.u.configureRequest.pad1\t\t\t= 0;\n\n\t\tX_SendNotificationMasked(&ev, wnd, SubstructureRedirectMask);\n\t}\n\telse\n\t\tX_Resize(wnd, newx, newy, neww, newh);\n}\n\nvoid XR_ReparentWindow (xclient_t *cl, xReq *request)\n{\n\tqboolean wasmapped;\n\txEvent ev;\n\txReparentWindowReq *req = (xReparentWindowReq *)request;\n\txwindow_t *wnd, *parent;\n\n\tif (XS_GetResource(req->window, (void**)&wnd) != x_window)\n\t{\n\t\tX_SendError(cl, BadWindow, req->window, X_ReparentWindow, 0);\n\t\treturn;\n\t}\n\tif (XS_GetResource(req->parent, (void**)&parent) != x_window)\n\t{\n\t\tX_SendError(cl, BadWindow, req->parent, X_ReparentWindow, 0);\n\t\treturn;\n\t}\n\n\tif (wnd->mapped)\n\t{\n\t\tXR_UnmapWindow(cl, request);\n\t\twasmapped = true;\n\t}\n\telse\n\t\twasmapped = false;\n\n\tev.u.u.type\t\t\t\t\t\t= ReparentNotify;\n\tev.u.u.detail\t\t\t\t\t= 0;\n\tev.u.reparent.override = wnd->overrideredirect;\n\tev.u.reparent.window = wnd->res.id;\n\tev.u.reparent.parent = wnd->parent->res.id;\n\tev.u.reparent.x = req->x;\n\tev.u.reparent.y = req->y;\n\n\tX_SendNotificationMasked (&ev, wnd, SubstructureNotifyMask);\n\n\tXS_SetParent(wnd, parent);\n\twnd->xpos = req->x;\n\twnd->ypos = req->y;\n\n\tX_SendNotificationMasked (&ev, wnd, SubstructureNotifyMask);\t//and again, now that we have the new parent.\n\n\tev.u.reparent.event = wnd->res.id;\n\tX_SendNotificationMasked (&ev, wnd, StructureNotifyMask);\n\n\tif (wasmapped)\n\t\tXR_MapWindow(cl, request);\n}\n\nvoid XR_DestroyWindow (xclient_t *cl, xReq *request)\n{\n\txResourceReq *req = (xResourceReq *)request;\n\txwindow_t *wnd;\n\n\tif (XS_GetResource(req->id, (void**)&wnd) != x_window)\n\t{\n\t\tX_SendError(cl, BadWindow, req->id, X_DestroyWindow, 0);\n\t\treturn;\n\t}\n\tif (!wnd->res.owner)\t//root window.\n\t\treturn;\n\tXS_DestroyResource(&wnd->res);\n}\nvoid XR_QueryTree (xclient_t *cl, xReq *request)\n{\n\tchar buffer[8192];\n\txResourceReq *req = (xResourceReq *)request;\n\txQueryTreeReply *rep = (xQueryTreeReply*)buffer;\n\n\txwindow_t *wnd;\n\n\tWindow\t*cwnd;\n\n\n\tif (XS_GetResource(req->id, (void**)&wnd) != x_window)\n\t{\n\t\tX_SendError(cl, BadWindow, req->id, X_DestroyWindow, 0);\n\t\treturn;\n\t}\n\n\t//FIXME: be careful of the count of children overflowing buffer.\n\n\trep->type\t= X_Reply;\n\trep->pad1\t= 0;\n\trep->sequenceNumber\t= cl->requestnum;\n\trep->length\t= 0;\n\trep->root\t= rootwindow->res.id;\t//we only have one root\n\tif (wnd->parent)\n\t\trep->parent\t= wnd->parent->res.id;\n\telse\n\t\trep->parent\t= 0;\n\trep->nChildren\t= 0;\n\trep->pad2\t= 0;\n\trep->pad3\t= 0;\n\trep->pad4\t= 0;\n\trep->pad5\t= 0;\n\n\tcwnd = (Window*)(rep+1);\n\n\tfor (wnd = wnd->child ; wnd ; wnd = wnd->sibling)\n\t{\n\t\t*cwnd++ = wnd->res.id;\n\t\trep->nChildren++;\n\t}\n\n\trep->length = rep->nChildren;\n\n\tX_SendData(cl, rep, sizeof(*rep)+rep->length*4);\n}\n\nvoid XR_GetWindowAttributes (xclient_t *cl, xReq *request)\n{\n\txnotificationmask_t *nm;\n\txResourceReq *req = (xResourceReq *)request;\n\txwindow_t *wnd;\n\n\txGetWindowAttributesReply rep;\n\n\tif (XS_GetResource(req->id, (void**)&wnd) != x_window)\n\t{\n\t\tX_SendError(cl, BadWindow, req->id, X_GetWindowAttributes, 0);\n\t\treturn;\n\t}\n\n\trep.type\t\t\t\t= X_Reply;\n\trep.backingStore\t\t= 2;\n\trep.sequenceNumber\t\t= cl->requestnum;\n\trep.visualID\t\t\t= 0x22;\n\trep.class\t\t\t\t= wnd->inputonly;\n\trep.bitGravity\t\t\t= wnd->bitgravity;\n\trep.winGravity\t\t\t= wnd->wingravity;\n\trep.backingBitPlanes\t= wnd->depth;\n\trep.backingPixel\t\t= wnd->backpixel;\n\trep.saveUnder\t\t\t= 1;\n\trep.mapInstalled\t\t= !!wnd->buffer;\n\trep.mapState\t\t\t= wnd->mapped*2;\n\trep.override\t\t\t= wnd->overrideredirect;\n\trep.colormap\t\t\t= wnd->colormap;\n\trep.yourEventMask\t\t= 0;\n\trep.allEventMasks\t\t= 0;\n\tfor (nm = wnd->notificationmask; nm; nm = nm->next)\n\t{\n\t\tif (nm->client == cl)\n\t\t\trep.yourEventMask = nm->mask;\n\t\trep.allEventMasks |= nm->mask;\n\t}\n\trep.doNotPropagateMask\t= wnd->donotpropagate;\n\trep.pad\t\t\t\t\t= 0;\n\n\trep.length = (sizeof(xGetWindowAttributesReply) - sizeof(xGenericReply) + 3)/4;\n\n\tX_SendData(cl, &rep, sizeof(xGetWindowAttributesReply));\n}\n\n\nstatic struct\n{\n\tKeySym keysym[8];\n} keyboardmapping[256] =\n{\n\t{{0}},\n\t{{0}},\n\t{{0}},\n\t{{0}},\n\t{{0}},\n\t{{0}},\n\t{{0}},\n\t{{0}},\n\t{{0}},\n\t{{XK_Escape, NoSymbol, XK_Escape}},\t//10\n\t{{XK_1, XK_exclam, XK_1, XK_exclam, XK_onesuperior, XK_exclamdown, XK_onesuperior}},\t//10\t//11\n\t{{XK_2, XK_quotedbl, XK_2, XK_quotedbl, XK_twosuperior}},//, XK_oneeighth, XK_twosuperior}},\t//12\n\t{{XK_3, XK_sterling, XK_3, XK_sterling, XK_threesuperior, XK_sterling, XK_threesuperior}},\t//13\n\t{{XK_4, XK_dollar, XK_4, XK_dollar}},//, XK_EuroSign, XK_onequarter, XK_EuroSign}},\t//14\n\t{{XK_5, XK_percent, XK_5, XK_percent, XK_onehalf}},//, XK_threeeighths, XK_onehalf}},\t//15\n\t{{XK_6, XK_asciicircum, XK_6, XK_asciicircum, XK_threequarters}},//, XK_fiveeighths, XK_threequarters}},\t//16\n\t{{XK_7, XK_ampersand, XK_7, XK_ampersand, XK_braceleft}},//, XK_seveneighths, XK_braceleft}},\t//17\n\t{{XK_8, XK_asterisk, XK_8, XK_asterisk, XK_bracketleft}},//, XK_trademark, XK_bracketleft}},\t//18\n\t{{XK_9, XK_parenleft, XK_9, XK_parenleft, XK_bracketright, XK_plusminus, XK_bracketright}},\t//19\n    {{XK_0, XK_parenright, XK_0, XK_parenright, XK_braceright, XK_degree, XK_braceright}},\t//10\n    {{XK_minus, XK_underscore, XK_minus, XK_underscore, XK_backslash, XK_questiondown, XK_backslash}},\t//20\n    {{XK_equal, XK_plus, XK_equal, XK_plus}},//, XK_dead_cedilla, XK_dead_ogonek, XK_dead_cedilla}},\t//21\n    {{XK_BackSpace, XK_BackSpace, XK_BackSpace, XK_BackSpace}},\t//22\n    {{XK_Tab}},//, XK_ISO_Left_Tab, XK_Tab, XK_ISO_Left_Tab}},\t//23\n    {{XK_q, XK_Q, XK_q, XK_Q, XK_at}},//, XK_Greek_OMEGA, XK_at}},\t//24\n    {{XK_w, XK_W, XK_w, XK_W}},//, XK_lstroke, XK_Lstroke, XK_lstroke}},\t//25\n    {{XK_e, XK_E, XK_e, XK_E, XK_e, XK_E, XK_e}},\t//26\n    {{XK_r, XK_R, XK_r, XK_R, XK_paragraph, XK_registered, XK_paragraph}},\t//27\n    {{XK_t, XK_T, XK_t, XK_T}},//, XK_tslash, XK_Tslash, XK_tslash}},\t//28\n    {{XK_y, XK_Y, XK_y, XK_Y}},//, XK_leftarrow, XK_yen, XK_leftarrow}},\t//29\n     {{XK_u, XK_U, XK_u, XK_U}},//, XK_downarrow, XK_uparrow, XK_downarrow}},\t//30\n     {{XK_i, XK_I, XK_i, XK_I}},//, XK_rightarrow, XK_idotless, XK_rightarrow}},\t//31\n     {{XK_o, XK_O, XK_o, XK_O, XK_oslash, XK_Oslash, XK_oslash}},\t//32\n     {{XK_p, XK_P, XK_p, XK_P, XK_thorn, XK_THORN, XK_thorn}},\t//33\n     {{XK_bracketleft, XK_braceleft, XK_bracketleft, XK_braceleft}},//, XK_dead_diaeresis, XK_dead_abovering, XK_dead_diaeresis}},\t//34\n     {{XK_bracketright, XK_braceright, XK_bracketright, XK_braceright}},//, XK_dead_tilde, XK_dead_macron, XK_dead_tilde}},\t//35\n     {{XK_Return, NoSymbol, XK_Return}},\t//36\n     {{XK_Control_L, NoSymbol, XK_Control_L}},\t//37\n     {{XK_a, XK_A, XK_a, XK_A, XK_ae, XK_AE, XK_ae}},\t//38\n     {{XK_s, XK_S, XK_s, XK_S, XK_ssharp, XK_section, XK_ssharp}},\t//39\n     {{XK_d, XK_D, XK_d, XK_D, XK_eth, XK_ETH, XK_eth}},\t//40\n     {{XK_f, XK_F, XK_f, XK_F}},//, XK_dstroke, XK_ordfeminine, XK_dstroke}},\t//41\n     {{XK_g, XK_G, XK_g, XK_G}},//, XK_eng, XK_ENG, XK_eng}},\t//42\n     {{XK_h, XK_H, XK_h, XK_H}},//, XK_hstroke, XK_Hstroke, XK_hstroke}},\t//43\n     {{XK_j, XK_J, XK_j, XK_J}},//, XK_dead_hook, XK_dead_horn, XK_dead_hook}},\t//44\n     {{XK_k, XK_K, XK_k, XK_K}},//, XK_kra, XK_ampersand, XK_kra}},\t//45\n     {{XK_l, XK_L, XK_l, XK_L}},//, XK_lstroke, XK_Lstroke, XK_lstroke}},\t//46\n     {{XK_semicolon, XK_colon, XK_semicolon, XK_colon}},//, XK_dead_acute, XK_dead_doubleacute, XK_dead_acute}},\t//47\n     {{XK_apostrophe, XK_at, XK_apostrophe, XK_at}},//, XK_dead_circumflex, XK_dead_caron, XK_dead_circumflex}},\t//48\n     {{XK_grave, XK_notsign, XK_grave, XK_notsign, XK_bar, XK_bar, XK_bar}},\t//49\n     {{XK_Shift_L, NoSymbol, XK_Shift_L}},\t//50\n     {{XK_numbersign, XK_asciitilde, XK_numbersign, XK_asciitilde}},//, XK_dead_grave, XK_dead_breve, XK_dead_grave}},\t//51\n     {{XK_z, XK_Z, XK_z, XK_Z, XK_guillemotleft, XK_less, XK_guillemotleft}},\t//52\n     {{XK_x, XK_X, XK_x, XK_X, XK_guillemotright, XK_greater, XK_guillemotright}},\t//53\n     {{XK_c, XK_C, XK_c, XK_C, XK_cent, XK_copyright, XK_cent}},\t//54\n     {{XK_v, XK_V, XK_v, XK_V}},//, XK_leftdoublequotemark, XK_leftsinglequotemark, XK_leftdoublequotemark}},\t//55\n     {{XK_b, XK_B, XK_b, XK_B}},//, XK_rightdoublequotemark, XK_rightsinglequotemark, XK_rightdoublequotemark}},\t//56\n     {{XK_n, XK_N, XK_n, XK_N, XK_n, XK_N, XK_n}},\t//57\n     {{XK_m, XK_M, XK_m, XK_M, XK_mu, XK_masculine, XK_mu}},\t//58\n     {{XK_comma, XK_less, XK_comma, XK_less}},//, XK_horizconnector, XK_multiply, XK_horizconnector}},\t//59\n     {{XK_period, XK_greater, XK_period, XK_greater, XK_periodcentered, XK_division, XK_periodcentered}},\t//60\n     {{XK_slash, XK_question, XK_slash, XK_question}},//, XK_dead_belowdot, XK_dead_abovedot, XK_dead_belowdot}},\t//61\n     {{XK_Shift_R, NoSymbol, XK_Shift_R}},\t//62\n     {{XK_KP_Multiply, XK_KP_Multiply, XK_KP_Multiply, XK_KP_Multiply, XK_KP_Multiply, XK_KP_Multiply}},//, XK_XF86ClearGrab}},\t//63\n     {{XK_Alt_L, XK_Meta_L, XK_Alt_L, XK_Meta_L}},\t//64\n     {{XK_space, NoSymbol, XK_space}},\t//65\n     {{XK_Caps_Lock, NoSymbol, XK_Caps_Lock}},\t//66\n     {{XK_F1, XK_F1, XK_F1, XK_F1, XK_F1, XK_F1}},//, XK_XF86Switch_VT_1}},\t//67\n     {{XK_F2, XK_F2, XK_F2, XK_F2, XK_F2, XK_F2}},//, XK_XF86Switch_VT_2}},\t//68\n     {{XK_F3, XK_F3, XK_F3, XK_F3, XK_F3, XK_F3}},//, XK_XF86Switch_VT_3}},\t//69\n     {{XK_F4, XK_F4, XK_F4, XK_F4, XK_F4, XK_F4}},//, XK_XF86Switch_VT_4}},\t//70\n     {{XK_F5, XK_F5, XK_F5, XK_F5, XK_F5, XK_F5}},//, XK_XF86Switch_VT_5}},\t//71\n     {{XK_F6, XK_F6, XK_F6, XK_F6, XK_F6, XK_F6}},//, XK_XF86Switch_VT_6}},\t//72\n     {{XK_F7, XK_F7, XK_F7, XK_F7, XK_F7, XK_F7}},//, XK_XF86Switch_VT_7}},\t//73\n     {{XK_F8, XK_F8, XK_F8, XK_F8, XK_F8, XK_F8}},//, XK_XF86Switch_VT_8}},\t//74\n     {{XK_F9, XK_F9, XK_F9, XK_F9, XK_F9, XK_F9}},//, XK_XF86Switch_VT_9}},\t//75\n     {{XK_F10, XK_F10, XK_F10, XK_F10, XK_F10, XK_F10}},//, XK_XF86Switch_VT_10}},\t//76\n     {{XK_Num_Lock, NoSymbol, XK_Num_Lock}},\t//77\n     {{XK_Scroll_Lock, NoSymbol, XK_Scroll_Lock}},\t//78\n     {{XK_KP_Home, XK_KP_7, XK_KP_Home, XK_KP_7}},\t//79\n     {{XK_KP_Up, XK_KP_8, XK_KP_Up, XK_KP_8}},\t//80\n     {{XK_KP_Prior, XK_KP_9, XK_KP_Prior, XK_KP_9}},\t//81\n     {{XK_KP_Subtract, XK_KP_Subtract, XK_KP_Subtract, XK_KP_Subtract, XK_KP_Subtract, XK_KP_Subtract}},//, XK_XF86Prev_VMode}},\t//82\n     {{XK_KP_Left, XK_KP_4, XK_KP_Left, XK_KP_4}},\t//83\n     {{XK_KP_Begin, XK_KP_5, XK_KP_Begin, XK_KP_5}},\t//84\n     {{XK_KP_Right, XK_KP_6, XK_KP_Right, XK_KP_6}},\t//85\n     {{XK_KP_Add, XK_KP_Add, XK_KP_Add, XK_KP_Add, XK_KP_Add, XK_KP_Add}},//, XK_XF86Next_VMode}},\t//86\n     {{XK_KP_End, XK_KP_1, XK_KP_End, XK_KP_1}},\t//87\n     {{XK_KP_Down, XK_KP_2, XK_KP_Down, XK_KP_2}},\t//88\n     {{XK_KP_Next, XK_KP_3, XK_KP_Next, XK_KP_3}},\t//89\n     {{XK_KP_Insert, XK_KP_0, XK_KP_Insert, XK_KP_0}},\t//90\n     {{XK_KP_Delete, XK_KP_Decimal, XK_KP_Delete, XK_KP_Decimal}},\t//91\n     {{0}},//XK_ISO_Level3_Shift, NoSymbol, XK_ISO_Level3_Shift}},\t//92\n     {{0}}, //93\n     {{XK_backslash, XK_bar, XK_backslash, XK_bar, XK_bar, XK_brokenbar, XK_bar}},\t//94\n     {{XK_F11, XK_F11, XK_F11, XK_F11, XK_F11, XK_F11}},//, XK_XF86Switch_VT_11}},\t//95\n     {{XK_F12, XK_F12, XK_F12, XK_F12, XK_F12, XK_F12}},//, XK_XF86Switch_VT_12}},\t//96\n     {{0}},\t//97\n     {{XK_Katakana, NoSymbol, XK_Katakana}},\t//98\n     {{XK_Hiragana, NoSymbol, XK_Hiragana}},\t//99\n    {{XK_Henkan_Mode, NoSymbol, XK_Henkan_Mode}},\t//100\n    {{XK_Hiragana_Katakana, NoSymbol, XK_Hiragana_Katakana}},\t//101\n    {{XK_Muhenkan, NoSymbol, XK_Muhenkan}},\t//102\n    {{0}}, //103\n    {{XK_KP_Enter, NoSymbol, XK_KP_Enter}},\t//104\n    {{XK_Control_R, NoSymbol, XK_Control_R}},\t//105\n    {{XK_KP_Divide, XK_KP_Divide, XK_KP_Divide, XK_KP_Divide, XK_KP_Divide, XK_KP_Divide}},//, XK_XF86Ungrab}},\t//106\n    {{XK_Print, XK_Sys_Req, XK_Print, XK_Sys_Req}},\t//107\n    {{0}},//XK_ISO_Level3_Shift, XK_Multi_key, XK_ISO_Level3_Shift, XK_Multi_key}},\t//108\n    {{XK_Linefeed, NoSymbol, XK_Linefeed}},\t//109\n    {{XK_Home, NoSymbol, XK_Home}},\t//110\n    {{XK_Up, NoSymbol, XK_Up}},\t//111\n    {{XK_Prior, NoSymbol, XK_Prior}},\t//112\n    {{XK_Left, NoSymbol, XK_Left}},\t//113\n    {{XK_Right, NoSymbol, XK_Right}},\t//114\n    {{XK_End, NoSymbol, XK_End}},\t//115\n    {{XK_Down, NoSymbol, XK_Down}},\t//116\n    {{XK_Next, NoSymbol, XK_Next}},\t//117\n    {{XK_Insert, NoSymbol, XK_Insert}},\t//118\n    {{XK_Delete, NoSymbol, XK_Delete}},\t//119\n/*\n    120\n    121         0x1008ff12 (XF86AudioMute)      0x0000 (NoSymbol)       0x1008ff12 (XF86AudioMute)\n    122         0x1008ff11 (XF86AudioLowerVolume)       0x0000 (NoSymbol)       0x1008ff11 (XF86AudioLowerVolume)\n    123         0x1008ff13 (XF86AudioRaiseVolume)       0x0000 (NoSymbol)       0x1008ff13 (XF86AudioRaiseVolume)\n    124         0x1008ff2a (XF86PowerOff)       0x0000 (NoSymbol)       0x1008ff2a (XF86PowerOff)\n    125         0xffbd (KP_Equal)       0x0000 (NoSymbol)       0xffbd (KP_Equal)\n    126         0x00b1 (plusminus)      0x0000 (NoSymbol)       0x00b1 (plusminus)\n    127         0xff13 (Pause)  0xff6b (Break)  0xff13 (Pause)  0xff6b (Break)\n    128         0x1008ff4a (XF86LaunchA)        0x0000 (NoSymbol)       0x1008ff4a (XF86LaunchA)\n    129         0xffae (KP_Decimal)     0xffae (KP_Decimal)     0xffae (KP_Decimal)     0xffae (KP_Decimal)\n    130         0xff31 (Hangul) 0x0000 (NoSymbol)       0xff31 (Hangul)\n    131         0xff34 (Hangul_Hanja)   0x0000 (NoSymbol)       0xff34 (Hangul_Hanja)\n    132\n    133         0xffeb (Super_L)        0x0000 (NoSymbol)       0xffeb (Super_L)\n    134         0xffec (Super_R)        0x0000 (NoSymbol)       0xffec (Super_R)\n    135         0xff67 (Menu)   0x0000 (NoSymbol)       0xff67 (Menu)\n    136         0xff69 (Cancel) 0x0000 (NoSymbol)       0xff69 (Cancel)\n    137         0xff66 (Redo)   0x0000 (NoSymbol)       0xff66 (Redo)\n    138         0x1005ff70 (SunProps)   0x0000 (NoSymbol)       0x1005ff70 (SunProps)\n    139         0xff65 (Undo)   0x0000 (NoSymbol)       0xff65 (Undo)\n    140         0x1005ff71 (SunFront)   0x0000 (NoSymbol)       0x1005ff71 (SunFront)\n    141         0x1008ff57 (XF86Copy)   0x0000 (NoSymbol)       0x1008ff57 (XF86Copy)\n    142         0x1008ff6b (XF86Open)   0x0000 (NoSymbol)       0x1008ff6b (XF86Open)\n    143         0x1008ff6d (XF86Paste)  0x0000 (NoSymbol)       0x1008ff6d (XF86Paste)\n    144         0xff68 (Find)   0x0000 (NoSymbol)       0xff68 (Find)\n    145         0x1008ff58 (XF86Cut)    0x0000 (NoSymbol)       0x1008ff58 (XF86Cut)\n    146         0xff6a (Help)   0x0000 (NoSymbol)       0xff6a (Help)\n    147         0x1008ff65 (XF86MenuKB) 0x0000 (NoSymbol)       0x1008ff65 (XF86MenuKB)\n    148         0x1008ff1d (XF86Calculator)     0x0000 (NoSymbol)       0x1008ff1d (XF86Calculator)\n    149\n    150         0x1008ff2f (XF86Sleep)  0x0000 (NoSymbol)       0x1008ff2f (XF86Sleep)\n    151         0x1008ff2b (XF86WakeUp) 0x0000 (NoSymbol)       0x1008ff2b (XF86WakeUp)\n    152         0x1008ff5d (XF86Explorer)       0x0000 (NoSymbol)       0x1008ff5d (XF86Explorer)\n    153         0x1008ff7b (XF86Send)   0x0000 (NoSymbol)       0x1008ff7b (XF86Send)\n    154\n    155         0x1008ff8a (XF86Xfer)   0x0000 (NoSymbol)       0x1008ff8a (XF86Xfer)\n    156         0x1008ff41 (XF86Launch1)        0x0000 (NoSymbol)       0x1008ff41 (XF86Launch1)\n    157         0x1008ff42 (XF86Launch2)        0x0000 (NoSymbol)       0x1008ff42 (XF86Launch2)\n    158         0x1008ff2e (XF86WWW)    0x0000 (NoSymbol)       0x1008ff2e (XF86WWW)\n    159         0x1008ff5a (XF86DOS)    0x0000 (NoSymbol)       0x1008ff5a (XF86DOS)\n    160         0x1008ff2d (XF86ScreenSaver)    0x0000 (NoSymbol)       0x1008ff2d (XF86ScreenSaver)\n    161         0x1008ff74 (XF86RotateWindows)  0x0000 (NoSymbol)       0x1008ff74 (XF86RotateWindows)\n    162         0x1008ff7f (XF86TaskPane)       0x0000 (NoSymbol)       0x1008ff7f (XF86TaskPane)\n    163         0x1008ff19 (XF86Mail)   0x0000 (NoSymbol)       0x1008ff19 (XF86Mail)\n    164         0x1008ff30 (XF86Favorites)      0x0000 (NoSymbol)       0x1008ff30 (XF86Favorites)\n    165         0x1008ff33 (XF86MyComputer)     0x0000 (NoSymbol)       0x1008ff33 (XF86MyComputer)\n    166         0x1008ff26 (XF86Back)   0x0000 (NoSymbol)       0x1008ff26 (XF86Back)\n    167         0x1008ff27 (XF86Forward)        0x0000 (NoSymbol)       0x1008ff27 (XF86Forward)\n    168\n    169         0x1008ff2c (XF86Eject)  0x0000 (NoSymbol)       0x1008ff2c (XF86Eject)\n    170         0x1008ff2c (XF86Eject)  0x1008ff2c (XF86Eject)  0x1008ff2c (XF86Eject)  0x1008ff2c (XF86Eject)\n    171         0x1008ff17 (XF86AudioNext)      0x0000 (NoSymbol)       0x1008ff17 (XF86AudioNext)\n    172         0x1008ff14 (XF86AudioPlay)      0x1008ff31 (XF86AudioPause)     0x1008ff14 (XF86AudioPlay)      0x1008ff31 (XF86AudioPause)\n    173         0x1008ff16 (XF86AudioPrev)      0x0000 (NoSymbol)       0x1008ff16 (XF86AudioPrev)\n    174         0x1008ff15 (XF86AudioStop)      0x1008ff2c (XF86Eject)  0x1008ff15 (XF86AudioStop)      0x1008ff2c (XF86Eject)\n    175         0x1008ff1c (XF86AudioRecord)    0x0000 (NoSymbol)       0x1008ff1c (XF86AudioRecord)\n    176         0x1008ff3e (XF86AudioRewind)    0x0000 (NoSymbol)       0x1008ff3e (XF86AudioRewind)\n    177         0x1008ff6e (XF86Phone)  0x0000 (NoSymbol)       0x1008ff6e (XF86Phone)\n    178\n    179         0x1008ff81 (XF86Tools)  0x0000 (NoSymbol)       0x1008ff81 (XF86Tools)\n    180         0x1008ff18 (XF86HomePage)       0x0000 (NoSymbol)       0x1008ff18 (XF86HomePage)\n    181         0x1008ff73 (XF86Reload) 0x0000 (NoSymbol)       0x1008ff73 (XF86Reload)\n    182         0x1008ff56 (XF86Close)  0x0000 (NoSymbol)       0x1008ff56 (XF86Close)\n    183\n    184\n    185         0x1008ff78 (XF86ScrollUp)       0x0000 (NoSymbol)       0x1008ff78 (XF86ScrollUp)\n    186         0x1008ff79 (XF86ScrollDown)     0x0000 (NoSymbol)       0x1008ff79 (XF86ScrollDown)\n    187         0x0028 (parenleft)      0x0000 (NoSymbol)       0x0028 (parenleft)\n    188         0x0029 (parenright)     0x0000 (NoSymbol)       0x0029 (parenright)\n    189         0x1008ff68 (XF86New)    0x0000 (NoSymbol)       0x1008ff68 (XF86New)\n    190         0xff66 (Redo)   0x0000 (NoSymbol)       0xff66 (Redo)\n    191         0x1008ff81 (XF86Tools)  0x0000 (NoSymbol)       0x1008ff81 (XF86Tools)\n    192         0x1008ff45 (XF86Launch5)        0x0000 (NoSymbol)       0x1008ff45 (XF86Launch5)\n    193         0x1008ff46 (XF86Launch6)        0x0000 (NoSymbol)       0x1008ff46 (XF86Launch6)\n    194         0x1008ff47 (XF86Launch7)        0x0000 (NoSymbol)       0x1008ff47 (XF86Launch7)\n    195         0x1008ff48 (XF86Launch8)        0x0000 (NoSymbol)       0x1008ff48 (XF86Launch8)\n    196         0x1008ff49 (XF86Launch9)        0x0000 (NoSymbol)       0x1008ff49 (XF86Launch9)\n    197\n    198         0x1008ffb2 (XF86AudioMicMute)   0x0000 (NoSymbol)       0x1008ffb2 (XF86AudioMicMute)\n    199         0x1008ffa9 (XF86TouchpadToggle) 0x0000 (NoSymbol)       0x1008ffa9 (XF86TouchpadToggle)\n    200         0x1008ffb0 (XF86TouchpadOn)     0x0000 (NoSymbol)       0x1008ffb0 (XF86TouchpadOn)\n    201         0x1008ffb1 (XF86TouchpadOff)    0x0000 (NoSymbol)       0x1008ffb1 (XF86TouchpadOff)\n    202\n    203         0xff7e (Mode_switch)    0x0000 (NoSymbol)       0xff7e (Mode_switch)\n    204         0x0000 (NoSymbol)       0xffe9 (Alt_L)  0x0000 (NoSymbol)       0xffe9 (Alt_L)\n    205         0x0000 (NoSymbol)       0xffe7 (Meta_L) 0x0000 (NoSymbol)       0xffe7 (Meta_L)\n    206         0x0000 (NoSymbol)       0xffeb (Super_L)        0x0000 (NoSymbol)       0xffeb (Super_L)\n    207         0x0000 (NoSymbol)       0xffed (Hyper_L)        0x0000 (NoSymbol)       0xffed (Hyper_L)\n    208         0x1008ff14 (XF86AudioPlay)      0x0000 (NoSymbol)       0x1008ff14 (XF86AudioPlay)\n    209         0x1008ff31 (XF86AudioPause)     0x0000 (NoSymbol)       0x1008ff31 (XF86AudioPause)\n    210         0x1008ff43 (XF86Launch3)        0x0000 (NoSymbol)       0x1008ff43 (XF86Launch3)\n    211         0x1008ff44 (XF86Launch4)        0x0000 (NoSymbol)       0x1008ff44 (XF86Launch4)\n    212         0x1008ff4b (XF86LaunchB)        0x0000 (NoSymbol)       0x1008ff4b (XF86LaunchB)\n    213         0x1008ffa7 (XF86Suspend)        0x0000 (NoSymbol)       0x1008ffa7 (XF86Suspend)\n    214         0x1008ff56 (XF86Close)  0x0000 (NoSymbol)       0x1008ff56 (XF86Close)\n    215         0x1008ff14 (XF86AudioPlay)      0x0000 (NoSymbol)       0x1008ff14 (XF86AudioPlay)\n    216         0x1008ff97 (XF86AudioForward)   0x0000 (NoSymbol)       0x1008ff97 (XF86AudioForward)\n    217\n    218         0xff61 (Print)  0x0000 (NoSymbol)       0xff61 (Print)\n    219\n    220         0x1008ff8f (XF86WebCam) 0x0000 (NoSymbol)       0x1008ff8f (XF86WebCam)\n    221\n    222\n    223         0x1008ff19 (XF86Mail)   0x0000 (NoSymbol)       0x1008ff19 (XF86Mail)\n    224         0x1008ff8e (XF86Messenger)      0x0000 (NoSymbol)       0x1008ff8e (XF86Messenger)\n    225         0x1008ff1b (XF86Search) 0x0000 (NoSymbol)       0x1008ff1b (XF86Search)\n    226         0x1008ff5f (XF86Go)     0x0000 (NoSymbol)       0x1008ff5f (XF86Go)\n    227         0x1008ff3c (XF86Finance)        0x0000 (NoSymbol)       0x1008ff3c (XF86Finance)\n    228         0x1008ff5e (XF86Game)   0x0000 (NoSymbol)       0x1008ff5e (XF86Game)\n    229         0x1008ff36 (XF86Shop)   0x0000 (NoSymbol)       0x1008ff36 (XF86Shop)\n    230\n    231         0xff69 (Cancel) 0x0000 (NoSymbol)       0xff69 (Cancel)\n    232         0x1008ff03 (XF86MonBrightnessDown)      0x0000 (NoSymbol)       0x1008ff03 (XF86MonBrightnessDown)\n    233         0x1008ff02 (XF86MonBrightnessUp)        0x0000 (NoSymbol)       0x1008ff02 (XF86MonBrightnessUp)\n    234         0x1008ff32 (XF86AudioMedia)     0x0000 (NoSymbol)       0x1008ff32 (XF86AudioMedia)\n    235         0x1008ff59 (XF86Display)        0x0000 (NoSymbol)       0x1008ff59 (XF86Display)\n    236         0x1008ff04 (XF86KbdLightOnOff)  0x0000 (NoSymbol)       0x1008ff04 (XF86KbdLightOnOff)\n    237         0x1008ff06 (XF86KbdBrightnessDown)      0x0000 (NoSymbol)       0x1008ff06 (XF86KbdBrightnessDown)\n    238         0x1008ff05 (XF86KbdBrightnessUp)        0x0000 (NoSymbol)       0x1008ff05 (XF86KbdBrightnessUp)\n    239         0x1008ff7b (XF86Send)   0x0000 (NoSymbol)       0x1008ff7b (XF86Send)\n    240         0x1008ff72 (XF86Reply)  0x0000 (NoSymbol)       0x1008ff72 (XF86Reply)\n    241         0x1008ff90 (XF86MailForward)    0x0000 (NoSymbol)       0x1008ff90 (XF86MailForward)\n    242         0x1008ff77 (XF86Save)   0x0000 (NoSymbol)       0x1008ff77 (XF86Save)\n    243         0x1008ff5b (XF86Documents)      0x0000 (NoSymbol)       0x1008ff5b (XF86Documents)\n    244         0x1008ff93 (XF86Battery)        0x0000 (NoSymbol)       0x1008ff93 (XF86Battery)\n    245         0x1008ff94 (XF86Bluetooth)      0x0000 (NoSymbol)       0x1008ff94 (XF86Bluetooth)\n    246         0x1008ff95 (XF86WLAN)   0x0000 (NoSymbol)       0x1008ff95 (XF86WLAN)\n    247\n    248\n    249\n    250\n    251\n  */\n};\n\nvoid XR_GetKeyboardMapping (xclient_t *cl, xReq *request)\n{//fixme: send the XK equivelents.\n\txGetKeyboardMappingReq *req = (xGetKeyboardMappingReq *)request;\n\tchar buffer[8192];\n\txGetKeyboardMappingReply *rep = (xGetKeyboardMappingReply *)buffer;\n\tint i, y, x;\n\tint *syms = ((int *)(rep+1));\n\n\trep->type\t\t\t\t= X_Reply;\n\trep->keySymsPerKeyCode\t= countof(keyboardmapping[0].keysym);\n\trep->sequenceNumber\t\t= cl->requestnum;\n\trep->length\t\t\t\t= 0;\n\trep->pad2\t\t\t\t= 0;\n\trep->pad3\t\t\t\t= 0;\n\trep->pad4\t\t\t\t= 0;\n\trep->pad5\t\t\t\t= 0;\n\trep->pad6\t\t\t\t= 0;\n\trep->pad7\t\t\t\t= 0;\n\n\tfor (i = 0; i < req->count; i++)\n\t{\n\t\ty = req->firstKeyCode+i;\n\t\tif (y >= countof(keyboardmapping))\n\t\t\tbreak;\n\t\tfor (x = 0; x < rep->keySymsPerKeyCode; x++)\n\t\t\t*syms++ = keyboardmapping[y].keysym[x];\n\t}\n\trep->length = i*rep->keySymsPerKeyCode;\n\n\tX_SendData(cl, rep, sizeof(*rep)+rep->length*4);\n}\n\nstatic struct\n{\n\tKEYCODE keysym[8];\n} modifiermapping[] =\n{\t//these are scancodes\n\t{{0x32, 0x32}},\n\t{{0x42}},\n\t{{0x24, 0x69}},\n\t{{0x40, 0xcd}},\n\t{{0x4d}},\n\t{{0}},\n\t{{0x85, 0x86, 0xce, 0xcf}},\n\t{{0x5c, 0xcb}},\n};\nvoid XR_GetModifierMapping (xclient_t *cl, xReq *request)\n{//fixme: send the XK equivelents.\n//\txReq *req = (xReq *)request;\n\tchar buffer[8192];\n\txGetModifierMappingReply *rep = (xGetModifierMappingReply *)buffer;\n\tint x, y;\n\tKEYCODE *syms = ((KEYCODE *)(rep+1));\n\n\trep->type\t\t\t\t= X_Reply;\n\trep->numKeyPerModifier\t= countof(modifiermapping[0].keysym);\n\trep->sequenceNumber\t\t= cl->requestnum;\n\trep->length\t\t\t\t= (8*rep->numKeyPerModifier * sizeof(KEYCODE) + 3)/4;\n\trep->pad2\t\t\t\t= 0;\n\trep->pad3\t\t\t\t= 0;\n\trep->pad4\t\t\t\t= 0;\n\trep->pad5\t\t\t\t= 0;\n\trep->pad6\t\t\t\t= 0;\n\n\tfor (y = 0; y < countof(modifiermapping); y++)\n\t{\n\t\tfor (x = 0; x < rep->numKeyPerModifier; x++)\n\t\t{\n\t\t\t*syms++ = modifiermapping[y].keysym[x];\n\t\t}\n\t}\n\n\tX_SendData(cl, rep, sizeof(*rep)+rep->length*4);\n}\n\nvoid XR_QueryPointer (xclient_t *cl, xReq *request)\n{\n\textern int x_mousex, x_mousey, x_mousestate;\n\txQueryPointerReply rep;\n\n\trep.type\t\t\t= X_Reply;\n\trep.sameScreen\t\t= 1;\n\trep.sequenceNumber\t= cl->requestnum;\n\trep.length\t= 0;\n\trep.root\t= rootwindow->res.id;\n\trep.child\t= rootwindow->res.id;\n\trep.rootX\t= x_mousex;\n\trep.rootY\t= x_mousey;\n\trep.winX\t= x_mousex;\n\trep.winY\t= x_mousey;\n\trep.mask\t= 0;\n\trep.pad1\t= 0;\n\trep.pad\t\t= 0;\n\n\tif ((x_mousestate) & Button1Mask)\n\t\trep.mask |= Button1MotionMask;\n\tif ((x_mousestate) & Button2Mask)\n\t\trep.mask |= Button2MotionMask;\n\tif ((x_mousestate) & Button3Mask)\n\t\trep.mask |= Button3MotionMask;\n\tif ((x_mousestate) & Button4Mask)\n\t\trep.mask |= Button4MotionMask;\n\tif ((x_mousestate) & Button5Mask)\n\t\trep.mask |= Button5MotionMask;\n\n\tX_SendData(cl, &rep, sizeof(rep));\n}\n\nvoid XR_CreateCursor (xclient_t *cl, xReq *request)\n{\n//\txCreateCursorReq *req = (xCreateCursorReq *)request;\n\n\t//\tX_SendError(cl, BadImplementation, 0, req->reqType, 0);\n}\nvoid XR_CreateGlyphCursor (xclient_t *cl, xReq *request)\n{\n//\txCreateGlyphCursorReq *req = (xCreateGlyphCursorReq *)request;\n\n//\tchar buffer[8192];\n//\txGetKeyboardMappingReply *rep = (xGetKeyboardMappingReply *)buffer;\n\n//\tX_SendError(cl, BadImplementation, 0, req->reqType, 0);\n\n//\tX_SendError(cl, BadAlloc, req->id, X_DestroyWindow, 0);\n//\tX_SendError(cl, BadFont, req->id, X_DestroyWindow, 0);\n//\tX_SendError(cl, BadValue, req->id, X_DestroyWindow, 0);\n}\nvoid XR_FreeCursor (xclient_t *cl, xReq *request)\n{\n//\tX_SendError(cl, BadImplementation, 0, req->reqType, 0);\n//\tX_SendError(cl, BadValue, req->id, X_DestroyWindow, 0);\n}\nvoid XR_RecolorCursor (xclient_t *cl, xReq *request)\n{\n}\n\nvoid XR_GrabButton (xclient_t *cl, xReq *request)\n{\n}\nvoid XR_UngrabButton (xclient_t *cl, xReq *request)\n{\n}\n\nvoid XR_ChangeGCInternal(unsigned int mask, xgcontext_t *gc, CARD32 *param)\n{\n\tif (mask & GCFunction)\n\t\tgc->function = *param++;\n\tif (mask & GCPlaneMask)\n\t\tparam++;\n\tif (mask & GCForeground)\n\t\tgc->fgcolour = *param++;\n\tif (mask & GCBackground)\n\t\tgc->bgcolour = *param++;\n\tif (mask & GCLineWidth)\n\t\tparam++;\n\tif (mask & GCLineStyle)\n\t\tparam++;\n\tif (mask & GCCapStyle)\n\t\tparam++;\n\tif (mask & GCJoinStyle)\n\t\tparam++;\n\tif (mask & GCFillStyle)\n\t\tparam++;\n\tif (mask & GCFillRule)\n\t\tparam++;\n\tif (mask & GCTile)\n\t\tparam++;\n\tif (mask & GCStipple)\n\t\tparam++;\n\tif (mask & GCTileStipXOrigin)\n\t\tparam++;\n\tif (mask & GCTileStipYOrigin)\n\t\tparam++;\n\tif (mask & GCFont)\n\t{\n\t\tvoid *font = NULL;\n\t\tif (XS_GetResource(*param++, &font) != x_font)\n\t\t\tfont = NULL;\n\t\tgc->font = font;\n\t}\n\tif (mask & GCSubwindowMode)\n\t\tparam++;\n\tif (mask & GCGraphicsExposures)\n\t\tparam++;\n\tif (mask & GCClipXOrigin)\n\t\tparam++;\n\tif (mask & GCClipYOrigin)\n\t\tparam++;\n\tif (mask & GCClipMask)\n\t\tparam++;\n\tif (mask & GCDashOffset)\n\t\tparam++;\n\tif (mask & GCDashList)\n\t\tparam++;\n\tif (mask & GCArcMode)\n\t\tparam++;\n}\nvoid XR_CopyGCInternal(unsigned int mask, xgcontext_t *dest, xgcontext_t *src)\n{\n\tint param=0;\n\tif (mask & GCFunction)\n\t\tdest->function = src->function;\n\tif (mask & GCPlaneMask)\n\t\tparam++;\n\tif (mask & GCForeground)\n\t\tdest->fgcolour = src->fgcolour;\n\tif (mask & GCBackground)\n\t\tdest->bgcolour = src->fgcolour;\n\tif (mask & GCLineWidth)\n\t\tparam++;\n\tif (mask & GCLineStyle)\n\t\tparam++;\n\tif (mask & GCCapStyle)\n\t\tparam++;\n\tif (mask & GCJoinStyle)\n\t\tparam++;\n\tif (mask & GCFillStyle)\n\t\tparam++;\n\tif (mask & GCFillRule)\n\t\tparam++;\n\tif (mask & GCTile)\n\t\tparam++;\n\tif (mask & GCStipple)\n\t\tparam++;\n\tif (mask & GCTileStipXOrigin)\n\t\tparam++;\n\tif (mask & GCTileStipYOrigin)\n\t\tparam++;\n\tif (mask & GCFont)\n\t{\n\t\tdest->font = src->font;\n\t}\n\tif (mask & GCSubwindowMode)\n\t\tparam++;\n\tif (mask & GCGraphicsExposures)\n\t\tparam++;\n\tif (mask & GCClipXOrigin)\n\t\tparam++;\n\tif (mask & GCClipYOrigin)\n\t\tparam++;\n\tif (mask & GCClipMask)\n\t\tparam++;\n\tif (mask & GCDashOffset)\n\t\tparam++;\n\tif (mask & GCDashList)\n\t\tparam++;\n\tif (mask & GCArcMode)\n\t\tparam++;\n}\nvoid XR_ChangeGC(xclient_t *cl, xReq *request)\n{\n\txChangeGCReq *req = (xChangeGCReq *)request;\n\txgcontext_t *gc;\n\n\tif (XS_GetResource(req->gc, (void**)&gc) != x_gcontext)\n\t{\n\t\tX_SendError(cl, BadGC, req->gc, X_FreeGC, 0);\n\t\treturn;\n\t}\n\n\tXR_ChangeGCInternal(req->mask, gc, (CARD32\t*)(req + 1));\n}\n\nvoid XR_CopyGC(xclient_t *cl, xReq *request)\n{\n\txCopyGCReq *req = (xCopyGCReq *)request;\n\txgcontext_t *dest, *src;\n\n\tif (XS_GetResource(req->dstGC, (void**)&dest) != x_gcontext)\n\t{\n\t\tX_SendError(cl, BadGC, req->dstGC, X_FreeGC, 0);\n\t\treturn;\n\t}\n\tif (XS_GetResource(req->srcGC, (void**)&src) != x_gcontext)\n\t{\n\t\tX_SendError(cl, BadGC, req->srcGC, X_FreeGC, 0);\n\t\treturn;\n\t}\n\n\tXR_CopyGCInternal(req->mask, dest, src);\n}\n\nvoid XR_CreateGC(xclient_t *cl, xReq *request)\n{\n\txCreateGCReq *req = (xCreateGCReq *)request;\n\txresource_t *drawable;\n\n\tif (XS_GetResource(req->gc, (void**)&drawable) != x_none)\n\t{\n//\t\tif (req->gc == cl->ridbase&&drawable->owner)\n//\t\t\tXS_DestroyResourcesOfClient(drawable->owner);\n//\t\telse\n\t\t{\n\t\t\tX_SendError(cl, BadIDChoice, req->gc, X_CreateGC, 0);\n\t\t\treturn;\n\t\t}\n\t}\n\tXS_GetResource(req->drawable, (void**)&drawable);\n\t/*if (drawable->restype != x_window && drawable->restype != x_gcontext)\n\t{\n\t\tX_SendError(cl, BadDrawable, req->drawable, X_CreateGC, 0);\n\t\treturn;\n\t}*/\n\n\tXR_ChangeGCInternal(req->mask, XS_CreateGContext(req->gc, cl, drawable), (CARD32\t*)(req + 1));\n}\n\nvoid XR_FreeGC(xclient_t *cl, xReq *request)\n{\n\txResourceReq *req = (xResourceReq *)request;\n\txresource_t *gc;\n\tif (XS_GetResource(req->id, (void**)&gc) != x_gcontext)\n\t{\n\t\tX_SendError(cl, BadGC, req->id, X_FreeGC, 0);\n\t\treturn;\n\t}\n\n\tXS_DestroyResource(gc);\n}\n\nvoid XW_ClearArea(xwindow_t *wnd, int xp, int yp, int width, int height)\n{\n\tif (!wnd->buffer)\n\t{\n\t\tif (wnd->width*wnd->height<=0)\n\t\t\twnd->buffer = malloc(1);\n\t\telse\n\t\t\twnd->buffer = malloc(wnd->width*wnd->height*4);\n\t}\n\n\tif (xp < 0)\n\t{\n\t\twidth += xp;\n\t\txp = 0;\n\t}\n\tif (xp>wnd->width)\n\t\txp = wnd->width;\n\tif (yp < 0)\n\t{\n\t\theight += yp;\n\t\txp = 0;\n\t}\n\tif (yp>wnd->height)\n\t\typ = wnd->height;\n\tif (width+xp > wnd->width)\n\t\twidth = wnd->width - xp;\n\tif (height+yp > wnd->height)\n\t\theight = wnd->height - yp;\n\n\tif (wnd->backpixmap && wnd->backpixmap->width && wnd->backpixmap->height)\n\t{\n\t\tint x, xs;\n\t\tint y, ys;\n\t\tunsigned int *out;\n\t\tunsigned int *in;\n\n\t\tout = (unsigned int *)wnd->buffer + xp +yp*wnd->width;\n\t\tin = (unsigned int *)wnd->backpixmap->data;\n\n\t\tfor (y = 0, ys = 0; y < height; y++)\n\t\t{\n\t\t\tfor (x = 0; x < width; x+=wnd->backpixmap->width)\n\t\t\t{\n\t\t\t\t//when do we stop?\n\t\t\t\txs = wnd->backpixmap->width;\n\t\t\t\tif (xs > wnd->width-x-1)\n\t\t\t\t\txs = wnd->width-x-1;\n\t\t\t\tfor (; xs > 0; xs--)\n\t\t\t\t{\n\t\t\t\t\tout[x+xs] = in[xs+ys*wnd->backpixmap->width];\n\t\t\t\t}\n\t\t\t}\n\t\t\tout += wnd->width;\n\t\t\tys++;\n\t\t\tif (ys >= wnd->backpixmap->height)\n\t\t\t\tys = 0;\n\t\t}\n\t}\n\telse\n\t{\n\t\tint x;\n\t\tint y;\n\t\tunsigned int *out;\n\n\t\tout = (unsigned int *)wnd->buffer + xp +yp*wnd->width;\n\n\t\tfor (y = yp; y < height; y++)\n\t\t{\n\t\t\tfor (x = xp; x < width; x++)\n\t\t\t{\n\t\t\t\tout[x] = wnd->backpixel;\n\t\t\t}\n\t\t\tout+=wnd->width;\n\t\t}\n\t}\n}\n\nvoid XW_CopyArea(unsigned int *dest, int dx, int dy, int dwidth, int dheight, unsigned int *source, int sx, int sy, int swidth, int sheight, int cwidth, int cheight, xgcontext_t *gc)\n{\n\tint x, y;\n\n\t//tlcap on dest\n\tif (dx < 0)\n\t{\n\t\tcwidth += dx;\n\t\tdx = 0;\n\t}\n\tif (dy < 0)\n\t{\n\t\tcheight += dy;\n\t\tdy = 0;\n\t}\n\n\t//tlcap on source\n\tif (sx < 0)\n\t{\n\t\tcwidth += sx;\n\t\tsx = 0;\n\t}\n\tif (sy < 0)\n\t{\n\t\tcheight += sy;\n\t\tsy = 0;\n\t}\n\n\t//brcap on dest\n\tif (cwidth > dwidth - dx)\n\t\tcwidth = dwidth - dx;\n\n\tif (cheight > dheight - dy)\n\t\tcheight = dheight - dy;\n\n\t//brcap on source\n\tif (cwidth > swidth - sx)\n\t\tcwidth = swidth - sx;\n\n\tif (cheight > sheight - sy)\n\t\tcheight = sheight - sy;\n\n\tif (cwidth<=0)\n\t\treturn;\n\tif (cheight<=0)\n\t\treturn;\n\n\tdest += dx+dy*dwidth;\n\tsource += sx+sy*swidth;\n\n\tfor (y = 0; y < cheight; y++)\n\t{\n\t\tfor (x = 0; x < cwidth;x++)\n\t\t{\n\t\t\tGCFunc(gc->fgcolour, dest[x], gc->function, source[x], 0xffffff);\n\t\t}\n\t\tdest += dwidth;\n\t\tsource += swidth;\n\t}\n}\n\nvoid XR_ClearArea(xclient_t *cl, xReq *request)\n{//FIXME: Should be area rather than entire window\n\txClearAreaReq *req = (xClearAreaReq *)request;\n\txwindow_t *wnd;\n\n\tif (XS_GetResource(req->window, (void**)&wnd) != x_window)\n\t{\n\t\tX_SendError(cl, BadWindow, req->window, X_ClearArea, 0);\n\t\treturn;\n\t}\n\n\tif (req->x < 0)\n\t{\n\t\tif (req->width)\n\t\t\treq->width += req->x;\n\t\treq->x = 0;\n\t}\n\tif (req->y < 0)\n\t{\n\t\tif (req->height)\n\t\t\treq->height += req->y;\n\t\treq->y = 0;\n\t}\n\n\tif (!req->width || req->width + req->x > wnd->width)\n\t{\n\t\tif (req->width)\n\t\t\treq->width = wnd->width - req->x;\n\t\treq->width = wnd->width - req->x;\n\t}\n\tif (!req->height || req->height + req->y > wnd->height)\n\t{\n\t\tif (req->height)\n\t\t\treq->height = wnd->height - req->y;\n\t\treq->height = wnd->height - req->y;\n\t}\n\n\tXW_ClearArea(wnd, req->x, req->y, req->width, req->height);\n\n\tif (req->exposures)\n\t\tXW_ExposeWindowRegionInternal(wnd, req->x, req->y, req->width, req->height);\n\n\txrefreshed=true;\n}\n\nvoid XR_CopyArea(xclient_t *cl, xReq *request)\t//from and to pixmap or drawable.\n{\n\txCopyAreaReq *req = (xCopyAreaReq *)request;\n\n\txresource_t *drawable;\n\txgcontext_t *gc;\n\n\n\tunsigned int *outbuffer;\n\tunsigned int *inbuffer;\n\tint inwidth;\n\tint inheight;\n\tint outwidth;\n\tint outheight;\n\n\tif (XS_GetResource(req->gc, (void**)&gc) == x_none)\n\t{\n\t\tX_SendError(cl, BadGC, req->gc, X_CopyArea, 0);\n\t\treturn;\n\t}\n\n\n\tswitch (XS_GetResource(req->srcDrawable, (void**)&drawable))\n\t{\n\tdefault:\n\t\tX_SendError(cl, BadDrawable, req->srcDrawable, X_CopyArea, 0);\n\t\treturn;\n\n\tcase x_window:\n\t\t{\n\t\t\txwindow_t *wnd;\n\t\t\twnd = (xwindow_t *)drawable;\n\t\t\tif (!wnd->buffer)\n\t\t\t{\n\t\t\t\twnd->buffer = malloc(wnd->width*wnd->height*4);\n\t\t\t\tXW_ClearArea(wnd, 0, 0, wnd->width, wnd->height);\n\t\t\t}\n\n\t\t\tinwidth = wnd->width;\n\t\t\tinheight = wnd->height;\n\t\t\tinbuffer = (unsigned int *)wnd->buffer;\n\t\t}\n\t\tbreak;\n\n\tcase x_pixmap:\n\t\t{\n\t\t\txpixmap_t *pm;\n\t\t\tpm = (xpixmap_t *)drawable;\n\t\t\tif (!pm->data)\n\t\t\t{\n\t\t\t\tpm->data = malloc(pm->width*pm->height*4);\n\t\t\t\tmemset(pm->data, rand(), pm->width*pm->height*4);\n\t\t\t}\n\n\t\t\tinwidth = pm->width;\n\t\t\tinheight = pm->height;\n\t\t\tinbuffer = (unsigned int *)pm->data;\n\t\t}\n\t\tbreak;\n\t}\n\n\tswitch (XS_GetResource(req->dstDrawable, (void**)&drawable))\n\t{\n\tdefault:\n\t\tX_SendError(cl, BadDrawable, req->dstDrawable, X_CopyArea, 0);\n\t\treturn;\n\n\tcase x_window:\n\t\t{\n\t\t\txwindow_t *wnd;\n\t\t\twnd = (xwindow_t *)drawable;\n\t\t\tif (!wnd->buffer)\n\t\t\t{\n\t\t\t\twnd->buffer = malloc(wnd->width*wnd->height*4);\n\t\t\t\tXW_ClearArea(wnd, 0, 0, wnd->width, wnd->height);\n\t\t\t}\n\n\t\t\toutwidth = wnd->width;\n\t\t\toutheight = wnd->height;\n\t\t\toutbuffer = (unsigned int *)wnd->buffer;\n\t\t}\n\t\tbreak;\n\n\tcase x_pixmap:\n\t\t{\n\t\t\txpixmap_t *pm;\n\t\t\tpm = (xpixmap_t *)drawable;\n\t\t\tif (!pm->data)\n\t\t\t{\n\t\t\t\tpm->data = malloc(pm->width*pm->height*4);\n\t\t\t\tmemset(pm->data, rand(), pm->width*pm->height*4);\n\t\t\t}\n\n\t\t\toutwidth = pm->width;\n\t\t\toutheight = pm->height;\n\t\t\toutbuffer = (unsigned int *)pm->data;\n\t\t}\n\t\tbreak;\n\t}\n\n\tXW_CopyArea(outbuffer, req->dstX, req->dstY, outwidth, outheight, inbuffer, req->srcX, req->srcY, inwidth, inheight, req->width, req->height, gc);\n\n\txrefreshed=true;\n}\n\nvoid XR_MapWindow(xclient_t *cl, xReq *request)\n{\n\txResourceReq *req = (xResourceReq *)request;\n\n\txwindow_t *wnd;\n\tif (XS_GetResource(req->id, (void**)&wnd) != x_window)\n\t{\n\t\tX_SendError(cl, BadWindow, req->id, X_MapWindow, 0);\n\t\treturn;\n\t}\n\n//\tif (wnd->mapped)\n//\t\treturn;\n\n\tif (!wnd->overrideredirect && X_NotifcationMaskPresent(wnd, SubstructureRedirectMask, cl))\n\t{\n\t\txEvent ev;\n\n\t\tev.u.u.type\t\t\t\t\t\t= MapRequest;\n\t\tev.u.u.detail\t\t\t\t\t= 0;\n\t\tev.u.u.sequenceNumber\t\t\t= 0;\n\t\tev.u.mapRequest.parent\t\t\t= wnd->parent->res.id;\n\t\tev.u.mapRequest.window\t\t\t= wnd->res.id;\n\n\t\tX_SendNotificationMasked(&ev, wnd, SubstructureRedirectMask);\n\n\t\treturn;\n\t}\n\n\tif (!wnd->buffer)\n\t\tXW_ClearArea(wnd, 0, 0, wnd->width, wnd->height);\n\n\twnd->mapped = true;\n\n\t{\n\t\txEvent ev;\n\n\t\tev.u.u.type\t\t\t\t\t\t= MapNotify;\n\t\tev.u.u.detail\t\t\t\t\t= 0;\n\t\tev.u.u.sequenceNumber\t\t\t= 0;\n\t\tev.u.mapNotify.event\t\t\t= wnd->res.id;\n\t\tev.u.mapNotify.window\t\t\t= wnd->res.id;\n\t\tev.u.mapNotify.override\t\t\t= wnd->overrideredirect;\n\t\tev.u.mapNotify.pad1\t\t\t\t= 0;\n\t\tev.u.mapNotify.pad2\t\t\t\t= 0;\n\t\tev.u.mapNotify.pad3\t\t\t\t= 0;\n\n\t\tX_SendNotificationMasked(&ev, wnd, StructureNotifyMask);\n\t\tX_SendNotificationMasked(&ev, wnd, SubstructureNotifyMask);\n\t}\n\n/*\t{\n\t\txEvent ev;\n\n\t\tev.u.u.type\t\t\t\t\t\t= GraphicsExpose;\n\t\tev.u.u.detail\t\t\t\t\t= 0;\n\t\tev.u.u.sequenceNumber\t\t\t= 0;\n\t\tev.u.mapNotify.window\t\t\t= wnd->res.id;\n\t\tev.u.mapNotify.override\t\t\t= false;\n\t\tev.u.mapNotify.pad1\t\t\t\t= 0;\n\t\tev.u.mapNotify.pad2\t\t\t\t= 0;\n\t\tev.u.mapNotify.pad3\t\t\t\t= 0;\n\n\t\tX_SendNotificationMasked(&ev, wnd, ExposureMask);\n\t}*/\n\n\tXW_ExposeWindowRegionInternal(wnd, 0, 0, wnd->width, wnd->height);\n\t/*\n\n\twhile(wnd->mapped)\n\t{\n\t\txEvent ev;\n\n\t\tev.u.u.type\t\t\t\t\t\t= VisibilityNotify;\n\t\tev.u.u.detail\t\t\t\t\t= 0;\n\t\tev.u.u.sequenceNumber\t\t\t= 0;\n\t\tev.u.visibility.window\t\t\t= wnd->res.id;\n\t\tev.u.visibility.state\t\t\t= 0;\n\t\tev.u.visibility.pad1\t\t\t= 0;\n\t\tev.u.visibility.pad2\t\t\t= 0;\n\t\tev.u.visibility.pad3\t\t\t= 0;\n\n\t\tX_SendNotificationMasked(&ev, wnd, VisibilityChangeMask);\n\t}\n\n\t{\n\t\txEvent ev;\n\n\t\tev.u.u.type\t\t\t\t\t\t= Expose;\n\t\tev.u.u.detail\t\t\t\t\t= 0;\n\t\tev.u.u.sequenceNumber\t\t\t= 0;\n\t\tev.u.expose.window\t\t\t\t= wnd->res.id;\n\t\tev.u.expose.x\t\t\t\t\t= 0;\n\t\tev.u.expose.y\t\t\t\t\t= 0;\n\t\tev.u.expose.width\t\t\t\t= wnd->width;\n\t\tev.u.expose.height\t\t\t\t= wnd->height;\n\t\tev.u.expose.count\t\t\t\t= false;\t//other expose events following\n\t\tev.u.expose.pad2\t\t\t\t= 0;\n\n\t\tX_SendNotificationMasked(&ev, wnd, ExposureMask);\n\t}*/\n\n\txrefreshed = true;\n}\n\nvoid XR_UnmapWindow(xclient_t *cl, xReq *request)\n{\n\txResourceReq *req = (xResourceReq *)request;\n\n\txwindow_t *wnd;\n\tif (XS_GetResource(req->id, (void**)&wnd) != x_window)\n\t{\n\t\tX_SendError(cl, BadWindow, req->id, X_UnmapWindow, 0);\n\t\treturn;\n\t}\n\n\tif (!wnd->mapped || !wnd->parent)\n\t\treturn;\n\n\twnd->mapped = false;\n\txrefreshed=true;\n\n\tXW_ExposeWindow(wnd, 0, 0, wnd->width, wnd->height);\n\n\t{\n\t\txEvent ev;\n\n\t\tev.u.u.type\t\t\t\t\t\t= UnmapNotify;\n\t\tev.u.u.detail\t\t\t\t\t= 0;\n\t\tev.u.u.sequenceNumber\t\t\t= 0;\n\t\tev.u.unmapNotify.event\t\t\t= wnd->res.id;\n\t\tev.u.unmapNotify.window\t\t\t= wnd->res.id;\n\t\tev.u.unmapNotify.fromConfigure\t= 0;\n\t\tev.u.unmapNotify.pad1\t\t\t= 0;\n\t\tev.u.unmapNotify.pad2\t\t\t= 0;\n\t\tev.u.unmapNotify.pad3\t\t\t= 0;\n\n\t\tX_SendNotificationMasked(&ev, wnd, StructureNotifyMask);\n\t\tX_SendNotificationMasked(&ev, wnd, SubstructureNotifyMask);\n\t}\n}\n\nvoid XR_MapSubwindows(xclient_t *cl, xReq *request)\n{\n\txResourceReq *req = (xResourceReq *)request;\n\txwindow_t *wnd;\n\n\tif (XS_GetResource(req->id, (void**)&wnd) != x_window)\n\t{\n\t\tX_SendError(cl, BadWindow, req->id, X_MapWindow, 0);\n\t\treturn;\n\t}\n\n\tfor (wnd = wnd->child; wnd; wnd = wnd->sibling)\n\t{\n\t\treq->id = wnd->res.id;\n\t\tXR_MapWindow(cl, request);\n\t}\n}\n\nvoid XR_CreatePixmap(xclient_t *cl, xReq *request)\n{\n\txCreatePixmapReq *req = (xCreatePixmapReq *)request;\n\n\txresource_t *res;\n\txpixmap_t *newpix;\n\t\n\tswitch(XS_GetResource(req->drawable, (void**)&res))\n\t{\n\tcase x_window:\n\tcase x_pixmap:\n\t\tbreak;\n\tdefault:\n\t\tX_SendError(cl, BadDrawable, req->drawable, X_CreatePixmap, 0);\n\t\treturn;\n\t}\n\n\t//depth must be one of the depths supported by the drawable's root window\n\tif (req->depth != 24 && req->depth != 32)\n\t{\n//\t\tX_SendError(cl, BadValue, req->depth, X_CreatePixmap, 0);\n//\t\treturn;\n\t}\n\n\tif (XS_GetResource(req->pid, (void**)&newpix) != x_none)\n\t{\n\t\tX_SendError(cl, BadIDChoice, req->pid, X_CreatePixmap, 0);\n\t}\n\tXS_CreatePixmap(req->pid, cl, req->width, req->height, req->depth);\n}\n\nvoid XR_FreePixmap(xclient_t *cl, xReq *request)\n{\n\txResourceReq *req = (xResourceReq *)request;\n\txresource_t *pm;\n\tif (XS_GetResource(req->id, (void**)&pm) != x_pixmap)\n\t{\n\t\tX_SendError(cl, BadPixmap, req->id, X_FreePixmap, 0);\n\t\treturn;\n\t}\n\n\tXS_DestroyResource(pm);\n}\n\nvoid XR_PutImage(xclient_t *cl, xReq *request)\n{\n\tunsigned char *out;\n\tunsigned char *in;\n\txPutImageReq *req = (xPutImageReq *)request;\n\txresource_t *drawable;\n\txgcontext_t *gc;\n\tint i;\n\n\tint drwidth;\n\tint drheight;\n\tint drdepth;\n\tunsigned char *drbuffer;\n\n\tif (XS_GetResource(req->drawable, (void**)&drawable) == x_none)\n\t{\n\t\tX_SendError(cl, BadDrawable, req->drawable, X_PutImage, 0);\n\t\treturn;\n\t}\n\n\tif (XS_GetResource(req->gc, (void**)&gc) == x_none)\n\t{\n\t\tX_SendError(cl, BadGC, req->gc, X_PutImage, 0);\n\t\treturn;\n\t}\n\n\n\tif (drawable->restype == x_window)\n\t{\n\t\txwindow_t *wnd;\n\t\twnd = (xwindow_t *)drawable;\n\t\tif (!wnd->buffer)\n\t\t{\n\t\t\twnd->buffer = malloc(wnd->width*wnd->height*4);\n\t\t\tXW_ClearArea(wnd, 0, 0, wnd->width, wnd->height);\n\t\t}\n\n\t\tdrwidth = wnd->width;\n\t\tdrheight = wnd->height;\n\t\tdrbuffer = wnd->buffer;\n\t\tdrdepth = wnd->depth;\n\t}\n\telse if (drawable->restype == x_pixmap)\n\t{\n\t\txpixmap_t *pm;\n\t\tpm = (xpixmap_t *)drawable;\n\t\tif (!pm->data)\n\t\t{\n\t\t\tpm->data = malloc(pm->width*pm->height*4);\n\t\t\tmemset(pm->data, rand(), pm->width*pm->height*4);\n\t\t}\n\n\t\tdrwidth = pm->width;\n\t\tdrheight = pm->height;\n\t\tdrbuffer = pm->data;\n\t\tdrdepth = pm->depth;\n\t}\n\telse\n\t{\n\t\tX_SendError(cl, BadDrawable, req->drawable, X_PutImage, 0);\n\t\treturn;\n\t}\n\n\txrefreshed = true;\n\n\tif (req->dstX < 0)\n\t{\n\t\treq->width += req->dstX;\n\t\treq->dstX = 0;\n\t}\n\tif (req->dstY < 0)\n\t{\n\t\treq->height += req->dstY;\n\t\treq->dstY = 0;\n\t}\n\n\tif (req->width > drwidth - req->dstX)\n\t\treq->width = drwidth - req->dstX;\n\n\tif (req->height > drheight - req->dstY)\n\t\treq->height = drheight - req->dstY;\n\n\tin = (qbyte *)(req+1);\n\n\t//my own bugs...\n\tif (req->leftPad != 0)\n\t{\n\t\tX_SendError(cl, BadImplementation, req->drawable, X_PutImage, req->format);\n\t\treturn;\n\t}\n\n\tif (req->format == XYBitmap)\n\t{\t//bitmaps are just a 'mask' specifying which pixels get foreground and which get background.\n\t\tint bnum;\n\t\tunsigned int *o4;\n\t\tbnum=0;\n\t\tif (req->depth == 1)\n\t\t{\n\t\t\twhile(req->height)\n\t\t\t{\n\t\t\t\tbnum += req->leftPad;\n\n\t\t\t\tout = drbuffer + (req->dstX + req->dstY*drwidth)*4;\n\t\t\t\to4 = (unsigned int*)out;\n\t\t\t\tfor (i = 0; i < req->width; i++)\n\t\t\t\t{\n\t\t\t\t\tif (in[bnum>>8]&(1<<(bnum&7)))\n\t\t\t\t\t\to4[i] = gc->fgcolour;\n\t\t\t\t\telse\n\t\t\t\t\t\to4[i] = gc->bgcolour;\n\t\t\t\t\tbnum++;\n\t\t\t\t}\n\t\t\t\tbnum += req->width;\n\n\t\t\t\treq->height--;\n\t\t\t\treq->dstY++;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t\tX_SendError(cl, BadMatch, req->drawable, X_PutImage, 0);\n\t}\n\telse if (req->depth != drdepth)\t/*depth must match*/\n\t{\n\t\tX_SendError(cl, BadMatch, req->drawable, X_PutImage, 0);\n\t}\n\telse if (req->format == ZPixmap)\t//32 bit network bandwidth (hideous)\n\t{\n\t\tif (req->leftPad != 0)\n\t\t{\n\t\t\tX_SendError(cl, BadMatch, req->drawable, X_PutImage, 0);\n\t\t\treturn;\n\t\t}\n\n\t\tif (req->depth == 1)\n\t\t{\n\t\t\tunsigned int *o4;\n\t\t\tint bnum = 0;\n\t\t\twhile(req->height)\n\t\t\t{\n\t\t\t\tout = drbuffer + (req->dstX + req->dstY*drwidth)*4;\n\t\t\t\to4 = (unsigned int*)out;\n\t\t\t\tfor (i = 0; i < req->width; i++, bnum++)\n\t\t\t\t{\n\t\t\t\t\tif (in[bnum>>8]&(1<<(bnum&7)))\n\t\t\t\t\t\to4[i] = 0xffffff;\n\t\t\t\t\telse\n\t\t\t\t\t\to4[i] = 0;\n\t\t\t\t}\n\n\t\t\t\treq->height--;\n\t\t\t\treq->dstY++;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\twhile(req->height)\n\t\t\t{\n\t\t\t\tunsigned int *i4, *o4;\n\t\t\t\tout = drbuffer + (req->dstX + req->dstY*drwidth)*4;\n\t\t\t\ti4 = (unsigned int*)in;\n\t\t\t\to4 = (unsigned int*)out;\n\t\t\t\tfor (i = 0; i < req->width; i++)\n\t\t\t\t{\n\t\t\t\t\to4[i] = i4[i];\n\t\t\t\t}\n\t/*\t\t\tfor (i = 0; i < req->width; i++)\n\t\t\t\t{\n\t\t\t\t\tout[i*4+0] = in[i*4+0];\n\t\t\t\t\tout[i*4+1] = in[i*4+1];\n\t\t\t\t\tout[i*4+2] = in[i*4+2];\n\t\t\t\t}\n\t*/\t\t\tin += req->width*4;\n\n\t\t\t\treq->height--;\n\t\t\t\treq->dstY++;\n\t\t\t}\n\t\t}\n\t}\n\telse if (req->format == XYPixmap)\n\t{\n\t\twhile(req->height)\n\t\t{\n\t\t\tunsigned int *o4;\n\t\t\tout = drbuffer + (req->dstX + req->dstY*drwidth)*4;\n\t\t\to4 = (unsigned int*)out;\n\t\t\tfor (i = 0; i < req->width; i++)\n\t\t\t{\n\t\t\t\tif (in[i>>3] & (1u<<(i&7)))\n\t\t\t\t\to4[i] = rand();\n\t\t\t\telse\n\t\t\t\t\to4[i] = rand();\n\t\t\t}\n\t\t\tin += (req->width+7)/8;\n\n\t\t\treq->height--;\n\t\t\treq->dstY++;\n\t\t}\n\t}\n\telse\n\t{\n\t\tX_SendError(cl, BadImplementation, req->drawable, X_PutImage, req->format);\n\t}\n}\nvoid XR_GetImage(xclient_t *cl, xReq *request)\n{\n\tunsigned char *out, *data;\n\tunsigned char *in;\n\txGetImageReq *req = (xGetImageReq *)request;\n\txresource_t *drawable;\n\n\txGetImageReply rep;\n\n\tint drwidth;\n\tint drheight;\n\tunsigned char *drbuffer;\n\n\tif (XS_GetResource(req->drawable, (void**)&drawable) == x_none)\n\t{\n\t\tX_SendError(cl, BadDrawable, req->drawable, X_GetImage, 0);\n\t\treturn;\n\t}\n\n\n\tif (drawable->restype == x_window)\n\t{\n\t\txwindow_t *wnd;\n\t\twnd = (xwindow_t *)drawable;\n\t\tif (!wnd->buffer)\n\t\t{\n\t\t\twnd->buffer = malloc(wnd->width*wnd->height*4);\n\t\t\tXW_ClearArea(wnd, 0, 0, wnd->width, wnd->height);\n\t\t}\n\n\t\tdrwidth = wnd->width;\n\t\tdrheight = wnd->height;\n\t\tdrbuffer = wnd->buffer;\n\n\t\trep.visual\t\t\t= 0x22;\n\t}\n\telse if (drawable->restype == x_pixmap)\n\t{\n\t\txpixmap_t *pm;\n\t\tpm = (xpixmap_t *)drawable;\n\t\tif (!pm->data)\n\t\t{\n\t\t\tpm->data = malloc(pm->width*pm->height*4);\n\t\t\tmemset(pm->data, rand(), pm->width*pm->height*4);\n\t\t}\n\n\t\tdrwidth = pm->width;\n\t\tdrheight = pm->height;\n\t\tdrbuffer = pm->data;\n\n\t\trep.visual\t\t\t= 0;\n\t}\n\telse\n\t{\n\t\tX_SendError(cl, BadDrawable, req->drawable, X_GetImage, 0);\n\t\treturn;\n\t}\n\n\tif (req->x < 0)\n\t{\n\t\tX_SendError(cl, BadValue, req->drawable, X_GetImage, 0);\n\t\treturn;\n\t}\n\tif (req->y < 0)\n\t{\n\t\tX_SendError(cl, BadValue, req->drawable, X_GetImage, 0);\n\t\treturn;\n\t}\n\n\tif (req->width > drwidth - req->x)\n\t{\n\t\tX_SendError(cl, BadValue, req->drawable, X_GetImage, 0);\n\t\treturn;\n\t}\n\n\tif (req->height > drheight - req->y)\n\t{\n\t\tX_SendError(cl, BadValue, req->drawable, X_GetImage, 0);\n\t\treturn;\n\t}\n\n\tdata = out = alloca(req->width*4*req->height);\n\tif (req->format == 2)\t//32 bit network bandwidth (hideous)\n\t{\n\t\twhile(req->height)\n\t\t{\n\t\t\tin = drbuffer + (req->x + req->y*drwidth)*4;\n#if 1\n\t\t\tmemcpy(out, in, req->width*4);\n#else\n\t\t\tfor (i = 0; i < req->width; i++)\n\t\t\t{\n\t\t\t\tout[i*4+0] = in[i*4+0];\n\t\t\t\tout[i*4+1] = in[i*4+1];\n\t\t\t\tout[i*4+2] = in[i*4+2];\n\t\t\t}\n#endif\n\t\t\tout += req->width*4;\n\n\t\t\treq->height--;\n\t\t\treq->y++;\n\t\t}\n\t}\n\telse\n\t{\n\t\tX_SendError(cl, BadImplementation, req->drawable, X_GetImage, req->format);\n\t\treturn;\n\t}\n\n\trep.type\t\t\t= X_Reply;\n\trep.sequenceNumber\t= cl->requestnum;\n\trep.length\t\t\t= (out-data+3)/4;\n\trep.depth\t\t\t= 24;\n\trep.pad3\t\t\t= 0;\n\trep.pad4\t\t\t= 0;\n\trep.pad5\t\t\t= 0;\n\trep.pad6\t\t\t= 0;\n\trep.pad7\t\t\t= 0;\n\n\tX_SendData(cl, &rep, sizeof(rep));\n\tX_SendData(cl, data, rep.length*4);\n}\n\nvoid XW_PolyLine(unsigned int *dbuffer, int dwidth, int dheight, int x1, int x2, int y1, int y2, xgcontext_t *gc)\n{\n\t//FIXME: cap to region.\n\tint len;\n\n\tint dx, dy;\n\n\tif (x1 < 0)\n\t\treturn;\n\tif (x2 < 0)\n\t\treturn;\n\tif (y1 < 0)\n\t\treturn;\n\tif (y2 < 0)\n\t\treturn;\n\n\tif (x1 >= dwidth)\n\t\tx1 = dwidth-1;\n\tif (x2 >= dwidth)\n\t\tx2 = dwidth-1;\n\n\tif (y1 >= dheight)\n\t\ty1 = dheight-1;\n\tif (y2 >= dheight)\n\t\ty2 = dheight-1;\n\n\tdx = (x2 - x1);\n\tdy = (y2 - y1);\n\tlen = (int)sqrt(dx*dx+dy*dy);\n\n\tif (!len)\n\t\treturn;\n\n\tx1<<=16;\n\ty1<<=16;\n\tdx=(dx<<16)/len;\n\tdy=(dy<<16)/len;\n\n\tfor (; len ; len--)\n\t{\n\t\tGCFunc(gc->fgcolour, dbuffer[(x1>>16)+dwidth*(y1>>16)], gc->function, dbuffer[(x1>>16)+dwidth*(y1>>16)], 0xffffff);\n\t\tx1+=dx;\n\t\ty1+=dy;\n\t}\n}\n\nvoid XR_PolyLine(xclient_t *cl, xReq *request)\n{\n\txPolyLineReq *req = (xPolyLineReq *)request;\n\n\txresource_t *drawable;\n\txgcontext_t *gc;\n\n\tint pointsleft;\n\n\tint drwidth;\n\tint drheight;\n\tunsigned char *drbuffer;\n\tINT16 start[2], end[2];\n\n\tINT16 *points;\n\tpoints = (INT16 *)(req+1);\n\n\n\n\tif (XS_GetResource(req->drawable, (void**)&drawable) == x_none)\n\t{\n\t\tX_SendError(cl, BadDrawable, req->drawable, X_PolyRectangle, 0);\n\t\treturn;\n\t}\n\n\tif (XS_GetResource(req->gc, (void**)&gc) == x_none)\n\t{\n\t\tX_SendError(cl, BadGC, req->gc, X_PolyRectangle, 0);\n\t\treturn;\n\t}\n\n\tif (drawable->restype == x_window)\n\t{\n\t\txwindow_t *wnd;\n\t\twnd = (xwindow_t *)drawable;\n\t\tif (!wnd->buffer)\n\t\t{\n\t\t\twnd->buffer = malloc(wnd->width*wnd->height*4);\n\t\t\tXW_ClearArea(wnd, 0, 0, wnd->width, wnd->height);\n\t\t}\n\n\t\tdrwidth = wnd->width;\n\t\tdrheight = wnd->height;\n\t\tdrbuffer = wnd->buffer;\n\t}\n\telse if (drawable->restype == x_pixmap)\n\t{\n\t\txpixmap_t *pm;\n\t\tpm = (xpixmap_t *)drawable;\n\t\tif (!pm->data)\n\t\t{\n\t\t\tpm->data = malloc(pm->width*pm->height*4);\n\t\t\tmemset(pm->data, rand(), pm->width*pm->height*4);\n\t\t}\n\n\t\tdrwidth = pm->width;\n\t\tdrheight = pm->height;\n\t\tdrbuffer = pm->data;\n\t}\n\telse\n\t{\n\t\tX_SendError(cl, BadDrawable, req->drawable, X_PolyRectangle, 0);\n\t\treturn;\n\t}\n\n\txrefreshed = true;\n\n\tif (req->reqType == X_PolySegment)\n\t{\n\t\tfor (pointsleft = req->length/2-3; pointsleft>0; pointsleft--)\n\t\t{\n\t\t\tXW_PolyLine((unsigned int *)drbuffer, drwidth, drheight, points[0], points[2], points[1], points[3], gc);\n\t\t\tpoints+=4;\n\t\t}\n\t}\n\telse\n\t{\n\t\tend[0] = 0;\n\t\tend[1] = 0;\n\n\t\tfor (pointsleft = req->length-3; pointsleft>0; pointsleft-=2)\n\t\t{\n\t\t\tif (req->coordMode == 1/*Previous*/)\n\t\t\t{\n\t\t\t\tstart[0] = end[0] + points[0];\n\t\t\t\tstart[1] = end[1] + points[1];\n\t\t\t\tend[0] = start[0] + points[2];\n\t\t\t\tend[1] = start[1] + points[3];\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tstart[0] = points[0];\n\t\t\t\tstart[1] = points[1];\n\t\t\t\tend[0] = points[2];\n\t\t\t\tend[1] = points[3];\n\t\t\t}\n\t\t\tXW_PolyLine((unsigned int *)drbuffer, drwidth, drheight, start[0], end[0], start[1], end[1], gc);\n\t\t\tpoints+=4;\n\t\t}\n\t}\n}\n\nvoid XR_FillPoly(xclient_t *cl, xReq *request)\n{\n\txFillPolyReq *req = (xFillPolyReq *)request;\n\tINT16 *points = (INT16*)(req+1);\n\tint numpoints = req->length-4;\n\n\txresource_t *drawable;\n\txgcontext_t *gc;\n\n\tint drwidth;\n\tint drheight;\n\tunsigned char *drbuffer;\n\tINT16 start[2], end[2];\n\n\tpoints = (INT16 *)(req+1);\n\n\t\n\n\tif (XS_GetResource(req->drawable, (void**)&drawable) == x_none)\n\t{\n\t\tX_SendError(cl, BadDrawable, req->drawable, X_PolyRectangle, 0);\n\t\treturn;\n\t}\n\n\tif (XS_GetResource(req->gc, (void**)&gc) == x_none)\n\t{\n\t\tX_SendError(cl, BadGC, req->gc, X_PolyRectangle, 0);\n\t\treturn;\n\t}\n\n\tif (drawable->restype == x_window)\n\t{\n\t\txwindow_t *wnd;\n\t\twnd = (xwindow_t *)drawable;\n\t\tif (!wnd->buffer)\n\t\t{\n\t\t\twnd->buffer = malloc(wnd->width*wnd->height*4);\n\t\t\tXW_ClearArea(wnd, 0, 0, wnd->width, wnd->height);\n\t\t}\n\n\t\tdrwidth = wnd->width;\n\t\tdrheight = wnd->height;\n\t\tdrbuffer = wnd->buffer;\n\t}\n\telse if (drawable->restype == x_pixmap)\n\t{\n\t\txpixmap_t *pm;\n\t\tpm = (xpixmap_t *)drawable;\n\t\tif (!pm->data)\n\t\t{\n\t\t\tpm->data = malloc(pm->width*pm->height*4);\n\t\t\tmemset(pm->data, rand(), pm->width*pm->height*4);\n\t\t}\n\n\t\tdrwidth = pm->width;\n\t\tdrheight = pm->height;\n\t\tdrbuffer = pm->data;\n\t}\n\telse\n\t{\n\t\tX_SendError(cl, BadDrawable, req->drawable, X_PolyRectangle, 0);\n\t\treturn;\n\t}\n\n\txrefreshed = true;\n\n\n\n\t{\n\t\tstart[0] = points[(numpoints*2)-2];\n\t\tstart[1] = points[(numpoints*2)-1];\n\t\twhile (numpoints-->0)\n\t\t{\n\t\t\tif (req->coordMode == 1/*Previous*/)\n\t\t\t{\n\t\t\t\tend[0] = start[0] + points[0];\n\t\t\t\tend[1] = start[1] + points[1];\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tend[0] = points[0];\n\t\t\t\tend[1] = points[1];\n\t\t\t}\n\t\t\tpoints+=2;\n\n(void)drbuffer, (void)drwidth, (void)drheight;\n//\t\t\tXW_PolyLine((unsigned int *)drbuffer, drwidth, drheight, start[0], start[1], end[0], end[1], gc);\n\t\t\tpoints++;\n\n\t\t\tstart[0] = end[0];\n\t\t\tstart[1] = end[1];\n\t\t}\n\t}\n}\n\nvoid XR_PolyRectangle(xclient_t *cl, xReq *request)\n{\n\tunsigned int *out;\n\txPolyRectangleReq *req = (xPolyRectangleReq *)request;\n\txresource_t *drawable;\n\txgcontext_t *gc;\n\tint i;\n\n\tshort *rect;\n\tint rectnum;\n\n\tint drwidth;\n\tint drheight;\n\tunsigned char *drbuffer;\n\n\n\n\tif (XS_GetResource(req->drawable, (void**)&drawable) == x_none)\n\t{\n\t\tX_SendError(cl, BadDrawable, req->drawable, X_PolyRectangle, 0);\n\t\treturn;\n\t}\n\n\tif (XS_GetResource(req->gc, (void**)&gc) == x_none)\n\t{\n\t\tX_SendError(cl, BadGC, req->gc, X_PolyRectangle, 0);\n\t\treturn;\n\t}\n\n\tif (drawable->restype == x_window)\n\t{\n\t\txwindow_t *wnd;\n\t\twnd = (xwindow_t *)drawable;\n\t\tif (!wnd->buffer)\n\t\t{\n\t\t\twnd->buffer = malloc(wnd->width*wnd->height*4);\n\t\t\tXW_ClearArea(wnd, 0, 0, wnd->width, wnd->height);\n\t\t}\n\n\t\tdrwidth = wnd->width;\n\t\tdrheight = wnd->height;\n\t\tdrbuffer = wnd->buffer;\n\t}\n\telse if (drawable->restype == x_pixmap)\n\t{\n\t\txpixmap_t *pm;\n\t\tpm = (xpixmap_t *)drawable;\n\t\tif (!pm->data)\n\t\t{\n\t\t\tpm->data = malloc(pm->width*pm->height*4);\n\t\t\tmemset(pm->data, rand(), pm->width*pm->height*4);\n\t\t}\n\n\t\tdrwidth = pm->width;\n\t\tdrheight = pm->height;\n\t\tdrbuffer = pm->data;\n\t}\n\telse\n\t{\n\t\tX_SendError(cl, BadDrawable, req->drawable, X_PolyRectangle, 0);\n\t\treturn;\n\t}\n\n\txrefreshed = true;\n\n\n\trect = (short *)(req+1);\n\tfor (rectnum = req->length/2 - 1; rectnum>0; rectnum--)\n\t{\n\n//\t\tCon_Printf(\"polyrect %i %i %i %i %i\\n\", req->drawable, rect[0], rect[1], rect[2], rect[3]);\n\n\t\tif (rect[2] < 0)\n\t\t{\n\t\t\trect[2] *= -1;\n\t\t}\n\t\tif (rect[3] < 0)\n\t\t{\n\t\t\trect[3] *= -1;\n\t\t}\n\n\t\tif (rect[0] < 0)\n\t\t{\n\t\t\trect[2] += rect[0];\n\t\t\trect[0] = 0;\n\t\t}\n\t\tif (rect[0] >= drwidth)\n\t\t\trect[0] = drwidth-1;\n\t\tif (rect[1] < 0)\n\t\t{\n\t\t\trect[3] += rect[1];\n\t\t\trect[1] = 0;\n\t\t}\n\t\tif (rect[1] >= drheight)\n\t\t\trect[1] = drheight-1;\n\n\t\tif (rect[0] + rect[2] > drwidth)\n\t\t\trect[2] = drwidth - rect[0];\n\t\tif (rect[1] + rect[3] > drheight)\n\t\t\trect[3] = drheight - rect[1];\n\t\tif (request->reqType == X_PolyFillRectangle)\t//fill\n\t\t{\n\t\t\twhile(rect[3])\n\t\t\t{\n\t\t\t\tout = (unsigned int *)drbuffer + (rect[0] + rect[1]*drwidth);\n\t\t\t\tfor (i = 0; i < rect[2]; i++)\n\t\t\t\t{\n\t\t\t\t\tGCFunc(gc->fgcolour, *(char *)&out[i], gc->function, *(char *)&out[i], 0xff);\n\t\t\t\t}\n\n\t\t\t\trect[3]--;\n\t\t\t\trect[1]++;\n\t\t\t}\n\t\t}\n\t\telse\t//outline\n\t\t{\n\t\t\t//top\n\t\t\tout = (unsigned int *)drbuffer + (rect[0] + rect[1]*drwidth);\n\t\t\tfor (i = 1; i < rect[2]-1; i++)\n\t\t\t{\n\t\t\t\tGCFunc(gc->fgcolour, out[i], gc->function, out[i], 0xffffff);\n\t\t\t}\n\n\t\t\t//bottom\n\t\t\tif (rect[3]-1)\n\t\t\t{\n\t\t\t\tout = (unsigned int *)drbuffer + (rect[0] + (rect[1]+rect[3]-1)*drwidth);\n\t\t\t\tfor (i = 1; i < rect[2]-1; i++)\n\t\t\t\t{\n\t\t\t\t\tGCFunc(gc->fgcolour, out[i], gc->function, out[i], 0xffffff);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t//left\n\t\t\tout = (unsigned int *)drbuffer + (rect[0] + rect[1]*drwidth);\n\t\t\tfor (i = 0; i < rect[3]; i++)\n\t\t\t{\n\t\t\t\tGCFunc(gc->fgcolour, out[i*drwidth], gc->function, out[i*drwidth], 0xffffff);\n\t\t\t}\n\n\t\t\t//right\n\t\t\tif (rect[2]-1)\n\t\t\t{\n\t\t\t\tout = (unsigned int *)drbuffer + (rect[0]+rect[2]-1 + rect[1]*drwidth);\n\t\t\t\tfor (i = 0; i < rect[3]; i++)\n\t\t\t\t{\n\t\t\t\t\tGCFunc(gc->fgcolour, out[i*drwidth], gc->function, out[i*drwidth], 0xffffff);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\trect+=4;\n\t}\n}\n\nvoid XR_PolyPoint(xclient_t *cl, xReq *request)\n{\n\txPolyPointReq *req = (xPolyPointReq *)request;\n\n\tunsigned int *out;\n\txresource_t *drawable;\n\txgcontext_t *gc;\n\n\tshort *point;\n\tint pointnum;\n\n\tint drwidth;\n\tint drheight;\n\tunsigned char *drbuffer;\n\n\tshort lastpoint[2];\n\n\tif (XS_GetResource(req->drawable, (void**)&drawable) == x_none)\n\t{\n\t\tX_SendError(cl, BadDrawable, req->drawable, X_PolyPoint, 0);\n\t\treturn;\n\t}\n\n\tif (XS_GetResource(req->gc, (void**)&gc) != x_gcontext)\n\t{\n\t\tX_SendError(cl, BadGC, req->gc, X_PolyPoint, 0);\n\t\treturn;\n\t}\n\n\tif (drawable->restype == x_window)\n\t{\n\t\txwindow_t *wnd;\n\t\twnd = (xwindow_t *)drawable;\n\t\tif (!wnd->buffer)\n\t\t{\n\t\t\twnd->buffer = malloc(wnd->width*wnd->height*4);\n\t\t\tXW_ClearArea(wnd, 0, 0, wnd->width, wnd->height);\n\t\t}\n\n\t\tdrwidth = wnd->width;\n\t\tdrheight = wnd->height;\n\t\tdrbuffer = wnd->buffer;\n\t}\n\telse if (drawable->restype == x_pixmap)\n\t{\n\t\txpixmap_t *pm;\n\t\tpm = (xpixmap_t *)drawable;\n\t\tif (!pm->data)\n\t\t{\n\t\t\tpm->data = malloc(pm->width*pm->height*4);\n\t\t\tmemset(pm->data, rand(), pm->width*pm->height*4);\n\t\t}\n\n\t\tdrwidth = pm->width;\n\t\tdrheight = pm->height;\n\t\tdrbuffer = pm->data;\n\t}\n\telse\n\t{\n\t\tX_SendError(cl, BadDrawable, req->drawable, X_PolyPoint, 0);\n\t\treturn;\n\t}\n\n\tpoint = (short*)(req+1);\n\n\tif (req->coordMode)\t//relative\n\t{\n\t\tlastpoint[0] = 0;\t//do the absolute stuff\n\t\tlastpoint[1] = 0;\n\t\tfor (pointnum = 1; pointnum>0; pointnum--)\n\t\t{\n\t\t\tlastpoint[0] += point[0];\t//do the absolute stuff\n\t\t\tlastpoint[1] += point[1];\n\n\t\t\tout = (unsigned int *)drbuffer + lastpoint[0] + lastpoint[1]*drwidth;\n\t\t\tGCFunc(gc->fgcolour, *out, gc->function, *out, 0xffffff);\n\t\t\tpoint+=2;\n\t\t}\n\n\t}\n\telse\t//absolute\n\t{\n\t\tfor (pointnum = req->length-1; pointnum>0; pointnum--)\n\t\t{\n\t\t\tif (!(point[0] < 0 || point[1] < 0 || point[0] >= drwidth || point[1] >= drheight))\n\t\t\t{\n\t\t\t\tout = (unsigned int *)drbuffer + point[0] + point[1]*drwidth;\n\t\t\t\tGCFunc(gc->fgcolour, *out, gc->function, *out, 0xffffff);\n\t\t\t}\n\n\t\t\tpoint+=2;\n\t\t}\n\t}\n}\n\nvoid Draw_CharToDrawable (int num, unsigned int *drawable, int x, int y, int width, int height, xgcontext_t *gc)\n{\n\tint\t\trow, col;\n\tunsigned int\t*source;\n\tint\t\tdrawline;\n\tint\t\ti;\n\n\tint s, e;\n\n\txfont_t *font;\n\n\tfont = gc->font;\n\tif (!font || font->res.restype != x_font)\n\t\treturn;\n\n\ts = 0;\n\te = font->charwidth;\n\tif (x<0)\n\t\ts = s - x;\n\n\tif (x > width - e)\n\t\te = width - x;\n\n\tif (s > e)\n\t\treturn;\n//\tif (y >= height-e)\n//\t\treturn;\n\n//\tif (y < -font->charheight)\n//\t\treturn;\n\n\tif (y <= 0)\n\t\tdrawable += x;\n\telse\n\t\tdrawable += (width*y) + x;\n\n\trow = num>>4;\n\tcol = num&15;\n\tsource = font->data + (row*font->rowwidth*font->charheight) + (col*font->charwidth);\n\tif (y < 0)\n\t\tsource -= font->rowwidth*y;\n\n\tdrawline = height-y;\n\tif (drawline > font->charheight)\n\t\tdrawline = font->charheight;\n\n\tif (y < 0)\n\t\t\tdrawline += y;\n\n\twhile (drawline-->=0)\n\t{\n\t\tfor (i=s ; i<e ; i++)\n\t\t\tif (((qbyte*)(&source[i]))[3] > 128 && source[i])\n//\t\t\t\tGCFunc(gc->fgcolour, drawable[i], gc->function, drawable[i], source[i]);\n\t\t\t\tdrawable[i] = source[i];\n\t\tsource += font->rowwidth;\n\t\tdrawable += width;\n\t}\n}\n\nvoid XR_PolyText(xclient_t *cl, xReq *request)\n{\n\tunsigned char *str;\n  \n\txPolyTextReq\t*req = (xPolyTextReq*)request;\n\t\n\txresource_t *drawable;\n\txgcontext_t *gc;\n\n\n\tint drwidth;\n\tint drheight;\n\tunsigned char *drbuffer;\n\n\tshort charnum;\n\tshort numchars;\n\n\tint xpos, ypos;\n\n\tif (XS_GetResource(req->drawable, (void**)&drawable) == x_none)\n\t{\n\t\tX_SendError(cl, BadDrawable, req->drawable, req->reqType, 0);\n\t\treturn;\n\t}\n\n\tif (XS_GetResource(req->gc, (void**)&gc) != x_gcontext)\n\t{\n\t\tX_SendError(cl, BadGC, req->gc, req->reqType, 0);\n\t\treturn;\n\t}\n\tif (!gc->font)\n\t{\n\t\tX_SendError(cl, BadGC, req->gc, req->reqType, 0);\n\t\treturn;\n\t}\n\n\tif (drawable->restype == x_window)\n\t{\n\t\txwindow_t *wnd;\n\t\twnd = (xwindow_t *)drawable;\n\t\tif (!wnd->buffer)\n\t\t{\n\t\t\twnd->buffer = malloc(wnd->width*wnd->height*4);\n\t\t\tXW_ClearArea(wnd, 0, 0, wnd->width, wnd->height);\n\t\t}\n\n\t\tdrwidth = wnd->width;\n\t\tdrheight = wnd->height;\n\t\tdrbuffer = wnd->buffer;\n\t}\n\telse if (drawable->restype == x_pixmap)\n\t{\n\t\txpixmap_t *pm;\n\t\tpm = (xpixmap_t *)drawable;\n\t\tif (!pm->data)\n\t\t{\n\t\t\tpm->data = malloc(pm->width*pm->height*4);\n\t\t\tmemset(pm->data, rand(), pm->width*pm->height*4);\n\t\t}\n\n\t\tdrwidth = pm->width;\n\t\tdrheight = pm->height;\n\t\tdrbuffer = pm->data;\n\t}\n\telse\n\t{\n\t\tX_SendError(cl, BadDrawable, req->drawable, req->reqType, 0);\n\t\treturn;\n\t}\n\n\txrefreshed = true;\n\n\txpos = req->x;\n\typos = req->y-gc->font->charheight/2;\n\n\tstr = (char*)(req+1);\n\n\tif (req->reqType == X_ImageText16 || req->reqType == X_ImageText8)\n\t{\n\t\twhile(1)\n\t\t{\n\t\t\tcharnum = 0;\n\t\t\tcharnum |= *str++;\n\t\t\tif (req->reqType == X_ImageText16)\n\t\t\t\tcharnum |= (*str++)<<8;\n\t\t\tif (!charnum)\n\t\t\t\treturn;\n\n\t\t\tDraw_CharToDrawable(charnum&255, (unsigned int *)drbuffer, xpos, ypos, drwidth, drheight, gc);\n\n\t\t\txpos += gc->font->charwidth;\n\t\t}\n\t}\n\telse\n\t{\n\t\tnumchars = *(short*) str;\n\t\tstr+=2;\n\t\twhile(1)\n\t\t{\n\t\t\tcharnum = 0;\n\t\t\tif (req->reqType == X_PolyText16)\n\t\t\t\tcharnum |= (*str++)<<8;\n\t\t\tcharnum |= *str++;\n\t\t\tif (!numchars--)\n\t\t\t\treturn;\n\n\t\t\tDraw_CharToDrawable(charnum, (unsigned int *)drbuffer, xpos, ypos, drwidth, drheight, gc);\n\n\t\t\txpos += gc->font->charwidth;\n\t\t}\n\t}\n}\n\nvoid XR_OpenFont(xclient_t *cl, xReq *request)\t//basically ignored. We only support one font...\n{\n\txOpenFontReq *req = (xOpenFontReq *)request;\n\tchar *name;\n\n\tname = (char *)(req+1);\n\n\tXS_CreateFont(req->fid, cl, name);\n}\n\nvoid XR_CloseFont(xclient_t *cl, xReq *request)\t//basically ignored. We only support one font...\n{\n\txResourceReq *req = (xResourceReq *)request;\n\tvoid *font = XS_GetResourceType(req->id, x_font);\n\tif (font)\n\t\tXS_DestroyResource(font);\n}\n\nvoid XR_ListFonts(xclient_t *cl, xReq *request)\t//basically ignored. We only support one font...\n{\n//\txListFontsReq *req = (xListFontsReq *)request;\n\tint buffer[256];\n\txListFontsReply *reply = (xListFontsReply *)buffer;\n\n\treply->type\t= X_Reply;\n    reply->sequenceNumber\t= cl->requestnum;\n    reply->length\t= 0;\n    reply->nFonts\t= 0;\n\n\tX_SendData(cl, reply, sizeof(xGenericReply)+reply->length*4);\n}\n\nvoid XR_QueryFont(xclient_t *cl, xReq *request)\t//basically ignored. We only support one font...\n{\n\txResourceReq *req = (xResourceReq *)request;\n\tchar buffer[8192];\n\tint i;\n\txCharInfo *ci;\n\txQueryFontReply *rep = (xQueryFontReply *)buffer;\n\txfont_t\t*font = XS_GetResourceType(req->id, x_font);\n\tif (!font)\n\t{\n\t\tX_SendError(cl, BadFont, req->id, req->reqType, 0);\n\t\treturn;\n\t}\n\n\trep->type\t\t\t= X_Reply;\n\trep->pad1\t\t\t= 0;\n\trep->sequenceNumber\t= cl->requestnum;\n\trep->length\t\t\t= 0;  /* definitely > 0, even if \"nCharInfos\" is 0 */\n\n\trep->minBounds.leftSideBearing\t= 0;\n\trep->minBounds.rightSideBearing\t= 0;\n\trep->minBounds.characterWidth\t= font->charwidth;\n\trep->minBounds.ascent\t\t\t= font->charheight/2;\n\trep->minBounds.descent\t\t\t= font->charheight/2;\n\trep->minBounds.attributes\t\t= 0;\n\n#ifndef WORD64\n\trep->walign1\t\t= 0;\n#endif\n\trep->maxBounds.leftSideBearing\t= 0;\n\trep->maxBounds.rightSideBearing\t= 0;\n\trep->maxBounds.characterWidth\t= font->charwidth;\n\trep->maxBounds.ascent\t\t\t= font->charheight/2;\n\trep->maxBounds.descent\t\t\t= font->charheight/2;\n\trep->maxBounds.attributes\t\t= 0;\n#ifndef WORD64\n\trep->walign2\t\t= 0;\n#endif\n\trep->minCharOrByte2\t= 0;\n\trep->maxCharOrByte2\t= 0;\n\trep->defaultChar\t= 0;\n\trep->nFontProps\t\t= 0;  /* followed by this many xFontProp structures */\n\trep->drawDirection\t= 0;\n\trep->minByte1\t\t= 0;\n\trep->maxByte1\t\t= 0;\n\trep->allCharsExist\t= 0;\n\trep->fontAscent\t\t= font->charheight/2;\n\trep->fontDescent\t= font->charheight/2;\n\trep->nCharInfos\t\t= 255; /* followed by this many xCharInfo structures */\n\n\trep->length = ((sizeof(xQueryFontReply) - sizeof(xGenericReply)) + rep->nFontProps*sizeof(xFontProp) + rep->nCharInfos*sizeof(xCharInfo))/4;\n\n\tci = (xCharInfo*)(rep+1);\n\tfor (i = 0; i < rep->nCharInfos; i++)\n\t{\n\t\tci[i].leftSideBearing = 0;\n\t\tci[i].rightSideBearing = 0;\n\t\tci[i].characterWidth = font->charwidth;\n\t\tci[i].ascent = font->charheight/2;\n\t\tci[i].descent = font->charheight/2;\n\t}\n\n\tX_SendData(cl, rep, sizeof(xGenericReply)+rep->length*4);\n}\n\n//esentually just throw it back at them.\nvoid XR_AllocColor(xclient_t *cl, xReq *request)\n{\n\txAllocColorReq *req = (xAllocColorReq *)request;\n\txAllocColorReply rep;\n\tunsigned char rgb[3] = {req->red>>8, req->green>>8, req->blue>>8};\n\n\trep.type\t\t\t= X_Reply;\n\trep.pad1\t\t\t= 0;\n\trep.sequenceNumber\t= cl->requestnum;\n\trep.length\t\t\t= 0;\n\trep.red\t\t\t\t= req->red;\n\trep.green\t\t\t= req->green;\n\trep.blue\t\t\t= req->blue;\n\trep.pad2\t\t\t= 0;\n\trep.pixel\t\t\t= (rgb[0]<<16) | (rgb[1]<<8) | (rgb[2]);\n\trep.pad3\t\t\t= 0;\n\trep.pad4\t\t\t= 0;\n\trep.pad5\t\t\t= 0;\n\n\tX_SendData(cl, &rep, sizeof(rep));\n}\n\nvoid XR_QueryColors(xclient_t *cl, xReq *request)\n{\n\txQueryColorsReq *req = (xQueryColorsReq *)request;\n\txQueryColorsReply rep;\n\txrgb rgb[65536];\n\tint n;\n\tint *pixel = (int*)(req+1);\n\n\trep.type\t\t\t= X_Reply;\n\trep.pad1\t\t\t= 0;\n\trep.sequenceNumber\t= cl->requestnum;\n\trep.length\t\t\t= 0;\n\trep.nColors\t\t\t= 0;\n\trep.pad2\t\t\t= 0;\n\trep.pad3\t\t\t= 0;\n\trep.pad4\t\t\t= 0;\n\trep.pad5\t\t\t= 0;\n\trep.pad6\t\t\t= 0;\n\trep.pad7\t\t\t= 0;\n\n\tfor (n = 0; n < req->length - sizeof(*req)/4; n++)\n\t{\n\t\trgb[n].red = ((pixel[n]>>16)&0xff)<<8;\n\t\trgb[n].green = ((pixel[n]>>8)&0xff)<<8;\n\t\trgb[n].blue = ((pixel[n]>>0)&0xff)<<8;\n\t\trgb[n].pad = 0;\n\t}\n\trep.nColors = n;\n\trep.length = (sizeof(xrgb)*n+3)/4;\n\n\tX_SendData(cl, &rep, sizeof(rep));\n\tX_SendData(cl, rgb, rep.length*4);\n}\n\nvoid XR_LookupColor(xclient_t *cl, xReq *request)\n{\n\ttypedef struct  {\n\t\tchar *name;\n\t\tfloat r, g, b;\n\t} colour_t;\n\tchar colourname[256];\n\tcolour_t *c, colour[] = {\n\t\t{\"black\",\t0,0,0},\n\t\t{\"grey\",\t0.5f,0.5f,0.5f},\n\t\t{\"gray\",\t0.5f,0.5f,0.5f},\t//wmaker uses this one. humour it.\n\t\t{\"gray90\",\t0.9f,0.9f,0.9f},\n\t\t{\"gray80\",\t0.8f,0.8f,0.8f},\n\t\t{\"gray70\",\t0.7f,0.7f,0.7f},\n\t\t{\"gray60\",\t0.6f,0.6f,0.6f},\n\t\t{\"gray50\",\t0.5f,0.5f,0.5f},\n\t\t{\"gray40\",\t0.4f,0.4f,0.4f},\n\t\t{\"gray30\",\t0.3f,0.3f,0.3f},\n\t\t{\"gray20\",\t0.2f,0.2f,0.2f},\n\t\t{\"gray10\",\t0.1f,0.1f,0.1f},\n\t\t{\"grey10\",\t0.1f,0.1f,0.1f},\n\t\t{\"white\",\t1,1,1},\n\t\t{\"red\",\t\t1,0,0},\n\t\t{\"green\",\t0,1,0},\n\t\t{\"blue\",\t0,0,1},\n\t\t{\"blue4\",\t0,0,0.4f},\n\t\t{NULL}\n\t};\n\n\txLookupColorReq *req = (xLookupColorReq *)request;\n\txLookupColorReply rep;\n\n\tif (req->nbytes >= sizeof(colourname))\n\t{\n\t\tX_SendError(cl, BadName, 0, X_LookupColor, 0);\n\t\treturn;\n\t}\n\tmemcpy(colourname, (char *)(req+1), req->nbytes);\n\tcolourname[req->nbytes] = '\\0';\n\n\tfor (c = colour; c->name; c++)\n\t{\n\t\tif (!strcasecmp(c->name, colourname))\n\t\t{\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tif (!c->name)\n\t{\n\t\tX_SendError(cl, BadName, 0, X_LookupColor, 0);\n\t\treturn;\n\t}\n\n\n\trep.type\t\t\t= X_Reply;\n\trep.pad1\t\t\t= 0;\n\trep.sequenceNumber\t= cl->requestnum;\n\trep.length\t\t\t= 0;\n\trep.exactRed\t\t= (unsigned short)(c->r*0xffffu);\n\trep.exactGreen\t\t= (unsigned short)(c->g*0xffffu);\n\trep.exactBlue\t\t= (unsigned short)(c->b*0xffffu);\n\trep.screenRed\t\t= rep.exactRed;\n\trep.screenGreen\t\t= rep.exactGreen;\n\trep.screenBlue\t\t= rep.exactBlue;\n\trep.pad3\t\t\t= 0;\n\trep.pad4\t\t\t= 0;\n\trep.pad5\t\t\t= 0;\n\n\tX_SendData(cl, &rep, sizeof(rep));\n}\n\n//get's keyboard status stuff.\nvoid XR_GetKeyboardControl(xclient_t *cl, xReq *request)\n{\n\txGetKeyboardControlReply rep;\n\n\trep.type\t\t\t\t= X_Reply;\n\trep.globalAutoRepeat\t= false;\n\trep.sequenceNumber\t\t= cl->requestnum;\n\trep.length\t\t\t\t= 5;\n\trep.ledMask\t\t\t\t= 0;\n\trep.keyClickPercent\t\t= 0;\n\trep.bellPercent\t\t\t= 0;\n\trep.bellPitch\t\t\t= 0;\n\trep.bellDuration\t\t= 0;\n\trep.pad\t\t\t\t\t= 0;\n\tmemset(rep.map, 0, sizeof(rep.map));\t//per key map\n\n\tX_SendData(cl, &rep, sizeof(rep));\n}\n\nvoid XR_WarpPointer(xclient_t *cl, xReq *request)\n{\n//\txWarpPointerReq *req = (xWarpPointerReq *)request;\n//\treq->m\n}\n\n#ifdef XBigReqExtensionName\nvoid XR_BigReq(xclient_t *cl, xReq *request)\n{\n\txBigReqEnableReply rep;\n\n\trep.type\t\t\t\t= X_Reply;\n\trep.pad0\t\t\t\t= 0;\n\trep.sequenceNumber\t\t= cl->requestnum;\n\trep.length\t\t\t\t= 0;\n\trep.max_request_size\t= 65535*1000;\n\trep.pad1\t\t\t\t= 0;\n\trep.pad2\t\t\t\t= 0;\n\trep.pad3\t\t\t\t= 0;\n\trep.pad4\t\t\t\t= 0;\n\trep.pad5\t\t\t\t= 0;\n\n\tX_SendData(cl, &rep, sizeof(rep));\n}\n#endif\n\n#define MAXREQUESTSIZE\t65535\n\n//cl -> cl protocol\nvoid XR_SendEvent (xclient_t *cl, xReq *request)\n{\n\tint count;\n\txSendEventReq *req = (xSendEventReq *)request;\n\txwindow_t *wnd = XS_GetResourceType(req->destination, x_window);\n\n\tif (!wnd)\n\t{\n\t\tX_SendError(cl, BadWindow, req->destination, X_SendEvent, 0);\n\t\treturn;\n\t}\n\n\tif (!req->eventMask)\t//goes to owner.\n\t{\n\t\treq->event.u.u.sequenceNumber = cl->requestnum;\n\t\tX_SendData(wnd->res.owner, &req->event, sizeof(req->event));\n\t}\n\telse\n\t{\n\t\txnotificationmask_t *nm;\n\t\tcount = 0;\n\t\twhile(!count)\n\t\t{\n\t\t\tfor (nm = wnd->notificationmask; nm; nm = nm->next)\n\t\t\t{\n\t\t\t\tif (!(nm->mask & req->eventMask))\n\t\t\t\t\tcontinue;\n\t\t\t\tcl = nm->client;\n\n\t\t\t\tif (cl->stillinitialising)\n\t\t\t\t\tcontinue;\n\n\t\t\t\tcount++;\n\n\t\t\t\tif (cl->tobedropped)\n\t\t\t\t\tcontinue;\n\t\t\t\tif (cl->outbufferlen > MAXREQUESTSIZE*4)\n\t\t\t\t{\n\t\t\t\t\tcl->tobedropped = true;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\treq->event.u.u.sequenceNumber = cl->requestnum;\n\t\t\t\tX_SendData(cl, &req->event, sizeof(xEvent));\n\t\t\t}\n\t\t\tif (req->propagate)\n\t\t\t\twnd = wnd->parent;\n\t\t\telse\n\t\t\t\tbreak;\n\t\t}\n\t}\n}\n\nvoid XR_GrabServer (xclient_t *cl, xReq *request)\n{\n\txgrabbedclient = cl;\n}\nvoid XR_UngrabServer (xclient_t *cl, xReq *request)\n{\n\txgrabbedclient = NULL;\n}\n\nxclient_t *xpointergrabclient;\nxwindow_t *xpgrabbedwindow;\nxwindow_t *xpconfinewindow;\nunsigned int xpointergrabmask;\nCARD32 xpointergrabcursor;\nvoid XR_GrabPointer (xclient_t *cl, xReq *request)\n{\n\txGrabPointerReq *req = (xGrabPointerReq *)request;\n\txGrabPointerReply reply;\n\n\treply.type\t\t\t\t= X_Reply;\n    reply.status\t\t\t= 0;\n    reply.sequenceNumber \t= cl->requestnum;\n    reply.length \t\t\t= 0;\n    reply.pad1 \t\t\t\t= 0;\n    reply.pad2\t\t\t\t= 0;\n    reply.pad3 \t\t\t\t= 0;\n    reply.pad4 \t\t\t\t= 0;\n    reply.pad5\t\t\t\t= 0;\n    reply.pad6\t\t\t\t= 0;\n\n\tif (xpointergrabclient && xpointergrabclient != cl)\n\t{\t//you can't have it.\n//\t\tif (pointerstatus == Frozen)\n//\t\t\treply.status\t\t\t= GrabFrozen;\n//\t\telse\n\t\t\treply.status\t\t\t= AlreadyGrabbed;\n\t\tX_SendData(cl, &reply, sizeof(reply));\n\t\treturn;\n\t}\n\n\txpointergrabclient = cl;\n\tXS_GetResource(req->grabWindow, (void**)&xpgrabbedwindow);\n\tXS_GetResource(req->confineTo, (void**)&xpconfinewindow);\n\txpointergrabmask = req->eventMask;\n\txpointergrabcursor = req->cursor;\n//\txpointergrabtime = req->time;\n\n\tX_EvalutateCursorOwner(NotifyGrab);\n\n\n\treply.status\t\t\t= GrabSuccess;\n\tX_SendData(cl, &reply, sizeof(reply));\n}\nvoid XR_ChangeActivePointerGrab (xclient_t *cl, xReq *request)\n{\n\txChangeActivePointerGrabReq *req = (xChangeActivePointerGrabReq *)request;\n\n\tif (xpointergrabclient != cl)\n\t{\t//its not yours to change\n\t\treturn;\n\t}\n\n\txpointergrabmask = req->eventMask;\n\txpointergrabcursor = req->cursor;\n//\txpointergrabtime = req->time;\n}\nvoid XR_UngrabPointer (xclient_t *cl, xReq *request)\n{\n\txpointergrabclient = NULL;\n\txpgrabbedwindow = NULL;\n\txpconfinewindow = NULL;\n\n\tX_EvalutateCursorOwner(NotifyUngrab);\n}\n\nvoid XR_SetClipRectangles (xclient_t *cl, xReq *request)\n{\n\txSetClipRectanglesReq *req = (xSetClipRectanglesReq*)request;\n\txgcontext_t *gc;\n\tif (XS_GetResource(req->gc, (void**)&gc) != x_gcontext)\n\t{\n\t\tX_SendError(cl, BadGC, req->gc, X_FreeGC, 0);\n\t\treturn;\n\t}\n\tCon_DPrintf(\"XR_SetClipRectangles is not implemented\\n\");\n}\n\nvoid XR_NoOperation (xclient_t *cl, xReq *request)\n{\n}\n\nvoid X_InitRequests(void)\n{\n\tint ExtentionCode = X_NoOperation+1;\n\n\tmemset(XRequests, 0, sizeof(XRequests));\n\n\tXRequests[X_QueryExtension] = XR_QueryExtension; \n\tXRequests[X_ListExtensions] = XR_ListExtensions;\n\tXRequests[X_SetCloseDownMode] = XR_SetCloseDownMode;\n\tXRequests[X_GetProperty] = XR_GetProperty; \n\tXRequests[X_ChangeProperty] = XR_ChangeProperty;\n\tXRequests[X_DeleteProperty] = XR_DeleteProperty;\n\tXRequests[X_ListProperties] = XR_ListProperties;\n\tXRequests[X_SetInputFocus] = XR_SetInputFocus;\n\tXRequests[X_GetInputFocus] = XR_GetInputFocus;\n\tXRequests[X_QueryBestSize] = XR_QueryBestSize;\n\tXRequests[X_CreateWindow] = XR_CreateWindow;\n\tXRequests[X_DestroyWindow] = XR_DestroyWindow;\n\tXRequests[X_QueryTree] = XR_QueryTree;\n\tXRequests[X_ChangeWindowAttributes] = XR_ChangeWindowAttributes;\n\tXRequests[X_GetWindowAttributes] = XR_GetWindowAttributes;\n\tXRequests[X_CreateGC] = XR_CreateGC;\n\tXRequests[X_ChangeGC] = XR_ChangeGC;\n\tXRequests[X_CopyGC] = XR_CopyGC;\n\tXRequests[X_FreeGC] = XR_FreeGC;\n\tXRequests[X_CreatePixmap] = XR_CreatePixmap;\n\tXRequests[X_FreePixmap] = XR_FreePixmap;\n\tXRequests[X_MapWindow] = XR_MapWindow;\n\tXRequests[X_MapSubwindows] = XR_MapSubwindows;\n\tXRequests[X_UnmapWindow] = XR_UnmapWindow;\n\tXRequests[X_ClearArea] = XR_ClearArea;\n\tXRequests[X_CopyArea] = XR_CopyArea;\n\tXRequests[X_InternAtom] = XR_InternAtom;\n\tXRequests[X_GetAtomName] = XR_GetAtomName;\n\tXRequests[X_PutImage] = XR_PutImage;\n\tXRequests[X_GetImage] = XR_GetImage;\n\tXRequests[X_PolyRectangle] = XR_PolyRectangle;\n\tXRequests[X_PolyFillRectangle] = XR_PolyRectangle;\n\tXRequests[X_PolyPoint] = XR_PolyPoint;\n\tXRequests[X_PolyLine] = XR_PolyLine;\n\tXRequests[X_PolySegment] = XR_PolyLine;\n\tXRequests[X_QueryPointer] = XR_QueryPointer;\n//\tXRequests[X_ChangeKeyboardMapping] = XR_ChangeKeyboardMapping;\n//\tXRequests[X_SetModifierMapping] = XR_ChangeKeyboardMapping;\n\tXRequests[X_GetKeyboardMapping] = XR_GetKeyboardMapping;\n\tXRequests[X_GetKeyboardControl] = XR_GetKeyboardControl;\n\tXRequests[X_GetModifierMapping] = XR_GetModifierMapping;\n\tXRequests[X_AllocColor] = XR_AllocColor;\n\tXRequests[X_LookupColor] = XR_LookupColor;\n\tXRequests[X_QueryColors] = XR_QueryColors;\n\tXRequests[X_GetGeometry] = XR_GetGeometry;\n\tXRequests[X_CreateCursor] = XR_CreateCursor;\n\tXRequests[X_CreateGlyphCursor] = XR_CreateGlyphCursor;\n\tXRequests[X_RecolorCursor] = XR_RecolorCursor;\n\tXRequests[X_FreeCursor] = XR_FreeCursor;\n\n\tXRequests[X_GrabButton] = XR_GrabButton;\n\tXRequests[X_UngrabButton] = XR_UngrabButton;\n\n\tXRequests[X_WarpPointer] = XR_WarpPointer;\n\tXRequests[X_ListFonts] = XR_ListFonts;\n\tXRequests[X_OpenFont] = XR_OpenFont;\n\tXRequests[X_CloseFont] = XR_CloseFont;\n\tXRequests[X_QueryFont] = XR_QueryFont;\n\tXRequests[X_PolyText8] = XR_PolyText;\n\tXRequests[X_PolyText16] = XR_PolyText;\n\tXRequests[X_ImageText8] = XR_PolyText;\n\tXRequests[X_ImageText16] = XR_PolyText;\n\n\tXRequests[X_SetClipRectangles] = XR_SetClipRectangles;\n\n\tXRequests[X_ConfigureWindow] = XR_ConfigureWindow;\n\tXRequests[X_ReparentWindow] = XR_ReparentWindow;\n\n\tXRequests[X_GrabServer] = XR_GrabServer;\n\tXRequests[X_UngrabServer] = XR_UngrabServer;\n\tXRequests[X_GrabPointer] = XR_GrabPointer;\n\tXRequests[X_ChangeActivePointerGrab] = XR_ChangeActivePointerGrab;\n\tXRequests[X_UngrabPointer] = XR_UngrabPointer;\n\n\n\tXRequests[X_SendEvent] = XR_SendEvent;\n\n\tXRequests[X_ConvertSelection] = XR_ConvertSelection;\n\tXRequests[X_GetSelectionOwner] = XR_GetSelectionOwner;\n\tXRequests[X_SetSelectionOwner] = XR_SetSelectionOwner;\n\n\tXRequests[X_GrabKey] = XR_NoOperation;\n\tXRequests[X_AllowEvents] = XR_NoOperation;\n\tXRequests[X_FillPoly] = XR_FillPoly;\n\tXRequests[X_NoOperation] = XR_NoOperation;\n\n#ifdef XBigReqExtensionName\n\tX_BigReqCode=ExtentionCode++;\n\tXRequests[X_BigReqCode] = XR_BigReq;\n#endif\n}\n\n"
  },
  {
    "path": "plugins/xsv/x_res.c",
    "content": "#include \"../plugin.h\"\n\n#include \"qux.h\"\n#include \"../../engine/qclib/hash.h\"\n\n\nhashtable_t restable;\nbucket_t *resbuckets[1357];\t//strange number to help the hashing.\n\nxwindow_t *rootwindow;\n\nxresource_t *resources;\n\nqbyte *xscreen;\nextern qboolean xscreenmodified;\nshort xscreenwidth;\nshort xscreenheight;\n\nint baseres;\n\nvoid XS_ClearParent(xwindow_t *wnd);\n\nint XS_GetResource(int id, void **data)\n{\n\txresource_t *res;\n\tif (id < 0)\n\t\treturn x_none;\n\n\tres = Hash_GetKey(&restable, id);\n\n\tif (res)\n\t{\n\t\tif (data)\n\t\t\t*data = res;\n\t\treturn res->restype;\n\t}\n\treturn x_none;\n}\nvoid *XS_GetResourceType(int id, int requiredtype)\n{\n\txresource_t *res;\n\tif (id < 0)\n\t\treturn NULL;\n\n\tres = Hash_GetKey(&restable, id);\n\n\tif (res && res->restype == requiredtype)\n\t\treturn res;\n\treturn NULL;\n}\n\nAtom XS_FindAtom(char *name)\n{\n\txresource_t *res;\n\t\n\tfor (res = resources; res; res = res->next)\n\t{\n\t\tif (res->restype == x_atom)\n\t\t{\n\t\t\tif (!strcmp(((xatom_t*)res)->atomname, name))\n\t\t\t\treturn res->id;\n\t\t}\n\t}\n\n\treturn None;\n}\n\nvoid XS_DestroyResource(xresource_t *res)\n{\n\tqboolean nofree = false;\n\tif (res->restype == x_pixmap)\n\t{\n\t\txpixmap_t *pm = (xpixmap_t *)res;\n\n\t\tif (pm->references)\n\t\t\tnofree = true;\n\t\tif (!pm->linked)\n\t\t\treturn;\n\n\t\tpm->linked = false;\n\t}\n\tif (res->restype == x_gcontext)\n\t{\n/*\t\txgcontext_t *gc = (xgcontext_t *)res;\n\n\t\tgc->references--;\n\t\tif (gc->references > 0)\n\t\t\treturn;\n*/\n\t}\n\tif (res->restype == x_window)\n\t{\n\t\textern xwindow_t *xfocusedwindow, *xpgrabbedwindow;\n\t\txwindow_t *wnd = (xwindow_t *)res;\n\t\twhile(wnd->child)\n\t\t\tXS_DestroyResource((xresource_t *)(wnd->child));\n\n\t\tif (xfocusedwindow == wnd)\n\t\t\txfocusedwindow = wnd->parent;\n\t\tif (xpgrabbedwindow == wnd)\n\t\t\txpgrabbedwindow = wnd->parent;\n\t\n\n\t\tif (wnd->mapped)\n\t\t{\n\t\t\tXW_ExposeWindow(wnd, 0, 0, wnd->width, wnd->height);\n\t\t}\n\n\n\t\t{\n\t\t\txEvent ev;\n\n\t\t\txrefreshed = true;\n\n\t\t\tev.u.u.type\t\t\t\t\t\t= DestroyNotify;\n\t\t\tev.u.u.detail\t\t\t\t\t= 0;\n\t\t\tev.u.u.sequenceNumber\t\t\t= 0;\n\t\t\tev.u.destroyNotify.event\t\t= wnd->res.id;\t//should be the window that has the recieve attribute.\n\t\t\tev.u.destroyNotify.window\t\t= wnd->res.id;\n\t\t\tX_SendNotificationMasked (&ev, wnd, StructureNotifyMask);\n\t\t\tX_SendNotificationMasked (&ev, wnd, SubstructureNotifyMask);\n\t\t}\n\n\t\tXS_ClearParent(wnd);\n\t}\n\tif (!res->prev)\n\t{\n\t\tresources = res->next;\n\t\tif (res->next)\n\t\t\tres->next->prev = NULL;\n\t}\n\telse\n\t{\n\t\tres->prev->next = res->next;\n\t\tres->next->prev = res->prev;\n\t}\n\n//\tXS_FindAtom(\"\");\n\n\tHash_RemoveKey(&restable, res->id);\n\tif (!nofree)\n\t\tfree(res);\n\n//\tXS_FindAtom(\"\");\n}\n\nvoid XS_DestroyResourcesOfClient(xclient_t *cl)\n{\n\txresource_t *res;\n\txnotificationmask_t *nm, *nm2;\n\n\tif (xgrabbedclient == cl)\n\t\txgrabbedclient = NULL;\n\tif (xpointergrabclient == cl)\n\t\txpointergrabclient = NULL;\n\n\tfor (res = resources; res;)\n\t{\n\t\tif (res->restype == x_window)\n\t\t{\t//clear the event masks\n\t\t\tnm = ((xwindow_t *)res)->notificationmask;\n\t\t\tif (nm)\n\t\t\t{\n\t\t\t\tif (nm->client == cl)\n\t\t\t\t{\n\t\t\t\t\t((xwindow_t *)res)->notificationmask = nm->next;\n\t\t\t\t\tfree(nm);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tfor (; nm->next; nm = nm->next)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (nm->next->client == cl || !cl)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tnm2 = nm->next;\n\t\t\t\t\t\t\tnm->next = nm->next->next;\n\t\t\t\t\t\t\tfree(nm2);\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (res->owner == cl)\n\t\t{\n\t\t\tXS_DestroyResource(res);\n\n\t\t\tres = resources;\t//evil!!!\n\t\t\tcontinue;\n\t\t}\n\t\tres = res->next;\n\t}\n}\n\nvoid XS_LinkResource(xresource_t *res)\n{\n\tres->next = resources;\n\tresources = res;\n\n\tres->prev = NULL;\n\tif (res->next)\n\t\tres->next->prev = res;\n\tHash_AddKey(&restable, res->id, res, malloc(sizeof(bucket_t)));\n}\n\nxatom_t *XS_CreateAtom(Atom atomid, char *name, xclient_t *owner)\n{\n\txatom_t *at;\n\tint nlen = strlen(name);\n\tat = malloc(sizeof(xatom_t)+nlen);\n\tat->res.owner = owner;\n\tat->res.restype = x_atom;\n\tat->res.id = atomid;\n\tmemcpy(at->atomname, name, nlen+1);\n\n\tXS_LinkResource(&at->res);\n\n\treturn at;\n}\n\nvoid XS_ClearParent(xwindow_t *wnd)\n{\n\txwindow_t *sib;\n\n\txwindow_t *parent = wnd->parent;\n\n\tif (!parent)\n\t\treturn;\n\n\twnd->parent = NULL;\n\n\tif (parent->child == wnd)\n\t{\n\t\tparent->child = wnd->sibling;\n\t\treturn;\n\t}\n\n\tfor (sib = parent->child; sib; sib = sib->sibling)\n\t{\n\t\tif (sib->sibling == wnd)\n\t\t{\n\t\t\tsib->sibling = wnd->sibling;\n\t\t\treturn;\n\t\t}\n\t}\n}\n\nint XS_GetProperty(xwindow_t *wnd, Atom atomid, Atom *atomtype, char *output, int maxlen, int offset, int *extrabytes, int *format)\n{\n\txproperty_t *xp;\n\tfor (xp = wnd->properties; xp; xp = xp->next)\n\t{\n\t\tif (xp->atomid == atomid)\n\t\t{\n\t\t\tif (maxlen > xp->datalen - offset)\n\t\t\t\tmaxlen = xp->datalen - offset;\n\t\t\tmemcpy(output, xp->data + offset, maxlen);\n\t\t\tif (maxlen < 0)\n\t\t\t\tmaxlen = 0;\n\t\t\t*extrabytes = xp->datalen - maxlen - offset;\n\t\t\t*format = xp->format;\n\t\t\t*atomtype = xp->type;\n\t\t\tif (*extrabytes < 0)\n\t\t\t\t*extrabytes = 0;\n\t\t\treturn maxlen;\n\t\t}\n\t}\n\t*format = 0;\n\t*extrabytes = 0;\n\t*atomtype = None;\n\treturn 0;\n}\n\nvoid XS_DeleteProperty(xwindow_t *wnd, Atom atomid)\n{\n\txproperty_t *xp, *prev = NULL;\n\tfor (xp = wnd->properties; xp; xp = xp->next)\n\t{\n\t\tif (xp->atomid == atomid)\n\t\t{\n\t\t\tif (prev)\n\t\t\t\tprev->next = xp->next;\n\t\t\telse\n\t\t\t\twnd->properties = xp->next;\n\n\t\t\tfree(xp);\n\t\t\treturn;\n\t\t}\n\t\tprev = xp;\n\t}\n}\n\nvoid XS_SetProperty(xwindow_t *wnd, Atom atomid, Atom atomtype, char *data, int datalen, int format)\n{\n\txproperty_t *prop;\n\tXS_DeleteProperty(wnd, atomid);\n\n\tprop = malloc(sizeof(xproperty_t) + datalen);\n\tprop->atomid = atomid;\n\tprop->format = format;\n\tprop->type = atomtype;\n\n\tmemcpy(prop->data, data, datalen);\n\tprop->data[datalen] = '\\0';\n\tprop->datalen = datalen;\n\n\tprop->next = wnd->properties;\n\twnd->properties = prop;\n}\n\nvoid XS_RaiseWindow(xwindow_t *wnd)\n{\n\txwindow_t *bigger;\n\tif (!wnd->parent)\n\t\treturn;\n\n\tbigger = wnd->parent->child;\n\tif (bigger == wnd)\n\t{\n\t\twnd->parent->child = wnd->sibling;\n\t\tbigger = wnd->sibling;\n\t\tif (!bigger)\n\t\t{\n\t\t\twnd->parent->child = wnd;\n\t\t\t//ah well, it was worth a try\n\t\t\treturn;\n\t\t}\n\t}\n\telse\n\t{\n\t\twhile(bigger->sibling != wnd)\n\t\t\tbigger = bigger->sibling;\n\n\t\tbigger->sibling = wnd->sibling;\t//unlink\n\t}\n\twhile(bigger->sibling)\n\t\tbigger = bigger->sibling;\n\n\tbigger->sibling = wnd;\n\twnd->sibling = NULL;\n}\nstatic qboolean XS_IsAncestor(xwindow_t *w, xwindow_t *check)\n{\n\txwindow_t *p;\n\tif (w)\n\tfor (p = w->parent; p; p = p->parent)\n\t{\n\t\tif (p == check)\n\t\t\treturn true;\n\t}\n\treturn false;\n}\nvoid XS_SetParent(xwindow_t *wnd, xwindow_t *parent)\n{\n\tif (XS_IsAncestor(parent, wnd))\n\t\tparent = wnd==rootwindow?NULL:rootwindow;\n\tif (wnd == parent)\n\t\tparent = wnd==rootwindow?NULL:rootwindow;\n\tXS_ClearParent(wnd);\n\tif (parent)\n\t{\n\t\tif (!parent->child)\n\t\t\tparent->child = wnd;\n\t\telse\n\t\t{\n\t\t\txwindow_t *sib;\n\t\t\tsib = parent->child;\n\t\t\twhile(sib->sibling)\n\t\t\t{\n\t\t\t\tsib = sib->sibling;\n\t\t\t}\n\t\t\tsib->sibling = wnd;\n\t\t\twnd->sibling = NULL;\n\t\t}\n//\t\twnd->sibling = parent->child;\n//\t\tparent->child = wnd;\n\t}\n\twnd->parent = parent;\n\n\tXS_RaiseWindow(wnd);\n}\n\nvoid X_Resize(xwindow_t *wnd, int newx, int newy, int neww, int newh)\n{\n\txEvent ev;\n\n/*\tif (wnd->xpos == newx && wnd->ypos == newy)\n\t{\n\t\tev.u.u.type\t\t\t\t\t\t\t= ResizeRequest;\n\t\tev.u.u.detail\t\t\t\t\t\t= 0;\n\t\tev.u.u.sequenceNumber\t\t\t\t= 0;\n\t\tev.u.resizeRequest.window\t\t\t= wnd->res.id;\n\t\tev.u.resizeRequest.width\t\t\t= wnd->width;\n\t\tev.u.resizeRequest.height\t\t\t= wnd->height;\n\n\t\tX_SendNotificationMasked(&ev, wnd, StructureNotifyMask);\n\t\tX_SendNotificationMasked(&ev, wnd, SubstructureNotifyMask);\n\n\t\treturn;\n\t}*/\n\n\twnd->xpos = newx;\n\twnd->ypos = newy;\n\n\tif ((wnd->width != neww || wnd->height != newh) && wnd->buffer)\n\t{\n\t\tfree(wnd->buffer);\n\t\twnd->buffer = NULL;\n\t}\n\twnd->width = neww;\n\twnd->height = newh;\n\n\tif (wnd->mapped)\n\t\txrefreshed = true;\n\n\tev.u.u.type\t\t\t\t\t\t\t= ConfigureNotify;\n\tev.u.u.detail\t\t\t\t\t\t= 0;\n\tev.u.u.sequenceNumber\t\t\t\t= 0;\n\tev.u.configureNotify.event\t\t\t= wnd->res.id;\n\tev.u.configureNotify.window\t\t\t= wnd->res.id;\n\tev.u.configureNotify.aboveSibling\t= None;\n\tev.u.configureNotify.x\t\t\t\t= wnd->xpos;\n\tev.u.configureNotify.y\t\t\t\t= wnd->ypos;\n\tev.u.configureNotify.width\t\t\t= wnd->width;\n\tev.u.configureNotify.height\t\t\t= wnd->height;\n\tev.u.configureNotify.borderWidth\t= 0;\n\tev.u.configureNotify.override\t\t= wnd->overrideredirect;\n\tev.u.configureNotify.bpad\t\t\t= 0;\n\n\tX_SendNotificationMasked(&ev, wnd, StructureNotifyMask);\n\tX_SendNotificationMasked(&ev, wnd, SubstructureNotifyMask);\n}\n\nxwindow_t *XS_CreateWindow(int wid, xclient_t *owner, xwindow_t *parent, short x, short y, short width, short height)\n{\n\txwindow_t *neww;\n\tneww = malloc(sizeof(xwindow_t));\n\tmemset(neww, 0, sizeof(xwindow_t));\n\n\tneww->res.id = wid;\n\tneww->res.owner = owner;\n\tneww->res.restype = x_window;\n\n\tif (width < 0)\n\t{\n\t\twidth*=-1;\n\t\tx-=width;\n\t}\n\tif (height < 0)\n\t{\n\t\theight*=-1;\n\t\tx-=height;\n\t}\n\n\tneww->xpos = (CARD16)x;\n\tneww->ypos = (CARD16)y;\n\tneww->width = width;\n\tneww->height = height;\n\tneww->buffer = NULL;\n\tneww->depth = 24;\n\n\tXS_SetParent(neww, parent);\n\n\tXS_LinkResource(&neww->res);\n\treturn neww;\n}\n\nxgcontext_t *XS_CreateGContext(int id, xclient_t *owner, xresource_t *drawable)\n{\n\txgcontext_t *newgc;\n\tnewgc = malloc(sizeof(xgcontext_t));\n\tmemset(newgc, 0, sizeof(xgcontext_t));\n\n\tnewgc->res.id = id;\n\tnewgc->res.owner = owner;\n\tnewgc->res.restype = x_gcontext;\n\n\tnewgc->function = GXcopy;\n//\tnewgc->planemask = ~0;\n\tnewgc->bgcolour = 1;\n\tnewgc->fgcolour = 0;\n//\tnewgc->line_width = 0;\n//\tnewgc->line_style = linesolid;\n//\tnewgc->cap_style = capbutt;\n//\tnewgc->join_style = joinmitter;\n//\tnewgc->fill_style = fillsolid;\n//\tnewgc->fill_rule = evenoddrule;\n//\tnewgc->arc_mode = arcpieslice;\n//\tnewgc->tile = somepixmap;\n//\tnewgc->stipple somepixmap;\n//\tnewgc->ts_x_origin = 0;\n//\tnewgc->ts_y_origin = 0;\n//\tnewgc->font = 0;\n//\tnewgc->subwindow_mode = clipbychildren;\n//\tnewgc->graphics_exposures = true;\n//\tnewgc->clip_x_origin = 0;\n//\tnewgc->clip_y_origin = 0;\n//\tnewgc->clip_mask = None;\n//\tnewgc->dash_offset = 0;\n//\tnewgc->dashes = 4;\n\n\tXS_LinkResource(&newgc->res);\n\n\treturn newgc;\n}\n\nxpixmap_t *XS_CreatePixmap(int id, xclient_t *owner, int width, int height, int depth)\n{\n\txpixmap_t *newpm;\n\tint i;\n\tunsigned char *c;\n\tnewpm = malloc(sizeof(xpixmap_t) + (width*height*4));\n\tmemset(newpm, 0, sizeof(xpixmap_t));\n\n\tfor(i = 0, c = (char*)(newpm+1); i < width*height*4; i++)\n\t{\n\t\tc[i] = rand();\n\t}\n\n\tnewpm->res.id = id;\n\tnewpm->res.owner = owner;\n\tnewpm->res.restype = x_pixmap;\n\n\tif (id>0)\n\t{\n\t\tnewpm->linked=true;\t//destroyed when last reference AND this are cleared\n\t\tXS_LinkResource(&newpm->res);\n\t}\n\n\tnewpm->data = (qbyte*)(newpm+1);\n\tnewpm->width = width;\n\tnewpm->height = height;\n\tnewpm->depth = depth;\n\n\treturn newpm;\n}\n\n#include <stdio.h>\nxfont_t *XS_CreateFont(int id, xclient_t *owner, char *fontname)\n{\n\txfont_t *newfont;\n\n\tFILE *f;\n\tint len;\n\tint width;\n\tint height;\n\tint i;\n\n\tunsigned int *out;\n\tunsigned char *in, *insrc;\n\n\tf = fopen(\"xfont.raw\", \"rb\");\n\tif (f)\n\t{\n\t\tfseek(f, 0, SEEK_END);\n\t\tlen = ftell(f);\n\t\tfseek(f, 0, SEEK_SET);\n\n\t\tif (len == 256*256*3)\n\t\t{\n\t\t\twidth = height = 256;\n\t\t}\n\t\telse if (len == 128*128*3)\n\t\t\twidth = height = 128;\n\t\telse\n\t\t{\t//urm, no idea.\n\t\t\tfclose(f);\n\t\t\treturn NULL;\n\t\t}\n\t}\n\telse\n\t{\n\t\twidth = height = 256;\n\t\tlen = width*height*3;\n\t}\n\n\tnewfont = malloc(sizeof(xfont_t) + (width*height*4));\n\tmemset(newfont, 0, sizeof(xfont_t));\n\n\tnewfont->res.id = id;\n\tnewfont->res.owner = owner;\n\tnewfont->res.restype = x_font;\n\n\tXS_LinkResource(&newfont->res);\n\n\tnewfont->rowwidth = width;\n\tnewfont->rowheight = height;\n\tnewfont->charwidth = width/16;\n\tnewfont->charheight = height/16;\n\tnewfont->depth = 32;\n\n\tin = insrc = malloc(len);\n\tif (f)\n\t{\n\t\tfread(in, len, 1, f);\n\t\tfclose(f);\n\t}\n\telse\n\t\tmemset(in, 0, len);\n\tout = newfont->data;\n\n\tfor (i = 0; i < width*height; i++)\n\t{\n\t\t*out = (in[0]) + (in[1]<<8) + (in[2]<<16) + (255u<<24);\n\t\tout++;\n\t\tin+=3;\n\t}\n\n\tfree(insrc);\n\n\treturn newfont;\n}\n\nint XS_NewResource (void)\n{\n\treturn baseres++;\n}\nvoid XS_CreateInitialResources(void)\n{\n\tstatic xpixmap_t pm;\n\tstatic unsigned int rawpm[8*9] = {\n\t\t0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000,\n\t\t0x000000, 0x000000, 0xffffff, 0x000000, 0xffffff, 0x000000, 0x000000, 0x000000,\n\t\t0x000000, 0xffffff, 0x000000, 0x000000, 0x000000, 0xffffff, 0x000000, 0x000000,\n\t\t0xffffff, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0xffffff, 0x000000,\n\t\t0xffffff, 0x000000, 0xffffff, 0xffffff, 0xffffff, 0x000000, 0xffffff, 0x000000,\n\t\t0xffffff, 0x000000, 0x000000, 0xffffff, 0x000000, 0x000000, 0xffffff, 0x000000,\n\t\t0x000000, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0x000000, 0x000000,\n\t\t0x000000, 0x000000, 0x000000, 0xffffff, 0x000000, 0x000000, 0x000000, 0x000000,\n\t\t0x000000, 0x000000, 0x000000, 0xffffff, 0x000000, 0x000000, 0x000000, 0x000000};\n\n\tif (baseres)\n\t\treturn;\n\tbaseres=1;\n\n\txscreenwidth = 640;\n\txscreenheight = 480;\n\txscreen = realloc(xscreen, xscreenwidth*4 * xscreenheight);\n\txscreenmodified = true;\n\n\tXS_DestroyResourcesOfClient(NULL);\n\n\tmemset(resbuckets, 0, sizeof(resbuckets));\n\tHash_InitTable(&restable, sizeof(resbuckets)/sizeof(resbuckets[0]), resbuckets);\n\n\tresources = NULL;\n\n\n\t/*these are the 'always created' atoms*/\n\tXS_CreateAtom(baseres++, \"PRIMARY\", NULL);\n\tXS_CreateAtom(baseres++, \"SECONDARY\", NULL);\n\tXS_CreateAtom(baseres++, \"ARC\", NULL);\n\tXS_CreateAtom(baseres++, \"ATOM\", NULL);\n\tXS_CreateAtom(baseres++, \"BITMAP\", NULL);\n\tXS_CreateAtom(baseres++, \"CARDINAL\", NULL);\n\tXS_CreateAtom(baseres++, \"COLORMAP\", NULL);\n\tXS_CreateAtom(baseres++, \"CURSOR\", NULL);\n\tXS_CreateAtom(baseres++, \"CUT_BUFFER0\", NULL);\n\tXS_CreateAtom(baseres++, \"CUT_BUFFER1\", NULL);\n\tXS_CreateAtom(baseres++, \"CUT_BUFFER2\", NULL);\n\tXS_CreateAtom(baseres++, \"CUT_BUFFER3\", NULL);\n\tXS_CreateAtom(baseres++, \"CUT_BUFFER4\", NULL);\n\tXS_CreateAtom(baseres++, \"CUT_BUFFER5\", NULL);\n\tXS_CreateAtom(baseres++, \"CUT_BUFFER6\", NULL);\n\tXS_CreateAtom(baseres++, \"CUT_BUFFER7\", NULL);\n\tXS_CreateAtom(baseres++, \"DRAWABLE\", NULL);\n\tXS_CreateAtom(baseres++, \"FONT\", NULL);\n\tXS_CreateAtom(baseres++, \"INTEGER\", NULL);\n\tXS_CreateAtom(baseres++, \"PIXMAP\", NULL);\n\tXS_CreateAtom(baseres++, \"POINT\", NULL);\n\tXS_CreateAtom(baseres++, \"RECTANGLE\", NULL);\n\tXS_CreateAtom(baseres++, \"RESOURCE_MANAGER\", NULL);\n\tXS_CreateAtom(baseres++, \"RGB_COLOR_MAP\", NULL);\n\tXS_CreateAtom(baseres++, \"RGB_BEST_MAP\", NULL);\n\tXS_CreateAtom(baseres++, \"RGB_BLUE_MAP\", NULL);\n\tXS_CreateAtom(baseres++, \"RGB_DEFAULT_MAP\", NULL);\n\tXS_CreateAtom(baseres++, \"RGB_GRAY_MAP\", NULL);\n\tXS_CreateAtom(baseres++, \"RGB_GREEN_MAP\", NULL);\n\tXS_CreateAtom(baseres++, \"RGB_RED_MAP\", NULL);\n\tXS_CreateAtom(baseres++, \"STRING\", NULL);\n\tXS_CreateAtom(baseres++, \"VISUALID\", NULL);\n\tXS_CreateAtom(baseres++, \"WINDOW\", NULL);\n\tXS_CreateAtom(baseres++, \"WM_COMMAND\", NULL);\n\tXS_CreateAtom(baseres++, \"WM_HINTS\", NULL);\n\tXS_CreateAtom(baseres++, \"WM_CLIENT_MACHINE\", NULL);\n\tXS_CreateAtom(baseres++, \"WM_ICON_NAME\", NULL);\n\tXS_CreateAtom(baseres++, \"WM_ICON_SIZE\", NULL);\n\tXS_CreateAtom(baseres++, \"WM_NAME\", NULL);\n\tXS_CreateAtom(baseres++, \"WM_NORMAL_HINTS\", NULL);\n\tXS_CreateAtom(baseres++, \"WM_SIZE_HINTS\", NULL);\n\tXS_CreateAtom(baseres++, \"WM_ZOOM_HINTS\", NULL);\n\tXS_CreateAtom(baseres++, \"MIN_SPACE\", NULL);\n\tXS_CreateAtom(baseres++, \"NORM_SPACE\", NULL);\n\tXS_CreateAtom(baseres++, \"MAX_SPACE\", NULL);\n\tXS_CreateAtom(baseres++, \"END_SPACE\", NULL);\n\tXS_CreateAtom(baseres++, \"SUPERSCRIPT_X\", NULL);\n\tXS_CreateAtom(baseres++, \"SUPERSCRIPT_Y\", NULL);\n\tXS_CreateAtom(baseres++, \"SUBSCRIPT_X\", NULL);\n\tXS_CreateAtom(baseres++, \"SUBSCRIPT_Y\", NULL);\n\tXS_CreateAtom(baseres++, \"UNDERLINE_POSITION\", NULL);\n\tXS_CreateAtom(baseres++, \"UNDERLINE_THICKNESS\", NULL);\n\tXS_CreateAtom(baseres++, \"STRIKEOUT_ASCENT\", NULL);\n\tXS_CreateAtom(baseres++, \"STRIKEOUT_DESCENT\", NULL);\n\tXS_CreateAtom(baseres++, \"ITALIC_ANGLE\", NULL);\n\tXS_CreateAtom(baseres++, \"X_HEIGHT\", NULL);\n\tXS_CreateAtom(baseres++, \"QUAD_WIDTH\", NULL);\n\tXS_CreateAtom(baseres++, \"WEIGHT\", NULL);\n\tXS_CreateAtom(baseres++, \"POINT_SIZE\", NULL);\n\tXS_CreateAtom(baseres++, \"RESOLUTION\", NULL);\n\tXS_CreateAtom(baseres++, \"COPYRIGHT\", NULL);\n\tXS_CreateAtom(baseres++, \"NOTICE\", NULL);\n\tXS_CreateAtom(baseres++, \"FONT_NAME\", NULL);\n\tXS_CreateAtom(baseres++, \"FAMILY_NAME\", NULL);\n\tXS_CreateAtom(baseres++, \"FULL_NAME\", NULL);\n\tXS_CreateAtom(baseres++, \"CAP_HEIGHT\", NULL);\n\tXS_CreateAtom(baseres++, \"WM_CLASS\", NULL);\n\tXS_CreateAtom(baseres++, \"WM_TRANSIENT_FOR\", NULL);\n\n\trootwindow = XS_CreateWindow(baseres++, NULL, NULL, 0, 0, xscreenwidth, xscreenheight);\n\trootwindow->depth = 24;\n\trootwindow->mapped = true;\n\n\n\tmemset(&pm, 0, sizeof(pm));\n\tpm.linked = true;\n\tpm.data = (qbyte *)rawpm;\n\tpm.depth = 24;\n\tpm.width = 8;\n\tpm.height = 9;\n\tpm.references = 2;\n\tpm.res.restype = x_pixmap;\n\n\trootwindow->backpixmap = &pm;\n\n\tXW_ClearArea(rootwindow, 0, 0, rootwindow->width, rootwindow->height);\n\n\txrefreshed = true;\n}\n"
  },
  {
    "path": "q3asm2/Makefile",
    "content": "# yeah, couldn't do more simple really\r\n\r\nCC=gcc\r\nCFLAGS=-O3 -Wall\r\n\r\ndefault:\tq3asm2\r\n\r\nq3asm2:\tq3asm2.c ../engine/qclib/hash.c\r\n\t$(CC) $(CFLAGS) -o $@ $^\r\n\r\nclean:\r\n\trm -f q3asm2 q3asm *~ *.o\r\n\r\n"
  },
  {
    "path": "q3asm2/opstrings.h",
    "content": "/*\r\n===========================================================================\r\nCopyright (C) 1999-2005 Id Software, Inc.\r\n\r\nThis file is part of Quake III Arena source code.\r\n\r\nQuake III Arena source code is free software; you can redistribute it\r\nand/or modify it under the terms of the GNU General Public License as\r\npublished by the Free Software Foundation; either version 2 of the License,\r\nor (at your option) any later version.\r\n\r\nQuake III Arena source code is distributed in the hope that it will be\r\nuseful, but WITHOUT ANY WARRANTY; without even the implied warranty of\r\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r\nGNU General Public License for more details.\r\n\r\nYou should have received a copy of the GNU General Public License\r\nalong with Foobar; if not, write to the Free Software\r\nFoundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\r\n===========================================================================\r\n*/\r\n{ \"BREAK\", OP_BREAK },\r\n\r\n{ \"CNSTF4\", OP_CONST },\r\n{ \"CNSTI4\", OP_CONST },\r\n{ \"CNSTP4\", OP_CONST },\r\n{ \"CNSTU4\", OP_CONST },\r\n\r\n{ \"CNSTI2\", OP_CONST },\r\n{ \"CNSTU2\", OP_CONST },\r\n\r\n{ \"CNSTI1\", OP_CONST },\r\n{ \"CNSTU1\", OP_CONST },\r\n\r\n//{ \"ARGB\", OP_ARG },\r\n//{ \"ARGF\", OP_ARG },\r\n//{ \"ARGI\", OP_ARG },\r\n//{ \"ARGP\", OP_ARG },\r\n//{ \"ARGU\", OP_ARG },\r\n\r\n{ \"ASGNB\", \tOP_BLOCK_COPY },\r\n{ \"ASGNF4\", OP_STORE4 },\r\n{ \"ASGNI4\", OP_STORE4 },\r\n{ \"ASGNP4\", OP_STORE4 },\r\n{ \"ASGNU4\", OP_STORE4 },\r\n\r\n{ \"ASGNI2\", OP_STORE2 },\r\n{ \"ASGNU2\", OP_STORE2 },\r\n\r\n{ \"ASGNI1\", OP_STORE1 },\r\n{ \"ASGNU1\", OP_STORE1 },\r\n\r\n{ \"INDIRB\", OP_IGNORE },\t// block copy deals with this\r\n{ \"INDIRF4\", OP_LOAD4 },\r\n{ \"INDIRI4\", OP_LOAD4 },\r\n{ \"INDIRP4\", OP_LOAD4 },\r\n{ \"INDIRU4\", OP_LOAD4 },\r\n\r\n{ \"INDIRI2\", OP_LOAD2 },\r\n{ \"INDIRU2\", OP_LOAD2 },\r\n\r\n{ \"INDIRI1\", OP_LOAD1 },\r\n{ \"INDIRU1\", OP_LOAD1 },\r\n\r\n{ \"CVFF4\", OP_UNDEF },\r\n{ \"CVFI4\", OP_CVFI },\r\n\r\n{ \"CVIF4\", OP_CVIF },\r\n{ \"CVII4\", OP_SEX8 },\t// will be either SEX8 or SEX16\r\n{ \"CVII1\", OP_IGNORE },\r\n{ \"CVII2\", OP_IGNORE },\r\n{ \"CVIU4\", OP_IGNORE },\r\n\r\n{ \"CVPU4\", OP_IGNORE },\r\n\r\n{ \"CVUI4\", OP_IGNORE },\r\n{ \"CVUP4\", OP_IGNORE },\r\n{ \"CVUU4\", OP_IGNORE },\r\n\r\n{ \"CVUU1\", OP_IGNORE },\r\n\r\n{ \"NEGF4\", OP_NEGF },\r\n{ \"NEGI4\", OP_NEGI },\r\n\r\n//{ \"CALLB\", OP_UNDEF },\r\n//{ \"CALLF\", OP_UNDEF },\r\n//{ \"CALLI\", OP_UNDEF },\r\n//{ \"CALLP\", OP_UNDEF },\r\n//{ \"CALLU\", OP_UNDEF },\r\n//{ \"CALLV\", OP_CALL },\r\n\r\n//{ \"RETF\", OP_UNDEF },\r\n//{ \"RETI\", OP_UNDEF },\r\n//{ \"RETP\", OP_UNDEF },\r\n//{ \"RETU\", OP_UNDEF },\r\n//{ \"RETV\", OP_UNDEF },\r\n\r\n{ \"ADDRGP4\", OP_CONST },\r\n\r\n//{ \"ADDRFP\", OP_PARM },\r\n//{ \"ADDRLP\", OP_LOCAL },\r\n\r\n{ \"ADDF4\", OP_ADDF },\r\n{ \"ADDI4\", OP_ADD },\r\n{ \"ADDP4\", OP_ADD },\r\n{ \"ADDP\", OP_ADD },\r\n{ \"ADDU4\", OP_ADD },\r\n\r\n{ \"SUBF4\", OP_SUBF },\r\n{ \"SUBI4\", OP_SUB },\r\n{ \"SUBP4\", OP_SUB },\r\n{ \"SUBU4\", OP_SUB },\r\n\r\n{ \"LSHI4\", OP_LSH },\r\n{ \"LSHU4\", OP_LSH },\r\n\r\n{ \"MODI4\", OP_MODI },\r\n{ \"MODU4\", OP_MODU },\r\n\r\n{ \"RSHI4\", OP_RSHI },\r\n{ \"RSHU4\", OP_RSHU },\r\n\r\n{ \"BANDI4\", OP_BAND },\r\n{ \"BANDU4\", OP_BAND },\r\n\r\n{ \"BCOMI4\", OP_BCOM },\r\n{ \"BCOMU4\", OP_BCOM },\r\n\r\n{ \"BORI4\", OP_BOR },\r\n{ \"BORU4\", OP_BOR },\r\n\r\n{ \"BXORI4\", OP_BXOR },\r\n{ \"BXORU4\", OP_BXOR },\r\n\r\n{ \"DIVF4\", OP_DIVF },\r\n{ \"DIVI4\", OP_DIVI },\r\n{ \"DIVU4\", OP_DIVU },\r\n\r\n{ \"MULF4\", OP_MULF },\r\n{ \"MULI4\", OP_MULI },\r\n{ \"MULU4\", OP_MULU },\r\n\r\n{ \"EQF4\", OP_EQF },\r\n{ \"EQI4\", OP_EQ },\r\n{ \"EQU4\", OP_EQ },\r\n\r\n{ \"GEF4\", OP_GEF },\r\n{ \"GEI4\", OP_GEI },\r\n{ \"GEU4\", OP_GEU },\r\n\r\n{ \"GTF4\", OP_GTF },\r\n{ \"GTI4\", OP_GTI },\r\n{ \"GTU4\", OP_GTU },\r\n\r\n{ \"LEF4\", OP_LEF },\r\n{ \"LEI4\", OP_LEI },\r\n{ \"LEU4\", OP_LEU },\r\n\r\n{ \"LTF4\", OP_LTF },\r\n{ \"LTI4\", OP_LTI },\r\n{ \"LTU4\", OP_LTU },\r\n\r\n{ \"NEF4\", OP_NEF },\r\n{ \"NEI4\", OP_NE },\r\n{ \"NEU4\", OP_NE },\r\n\r\n{ \"JUMPV\", OP_JUMP },\r\n\r\n{ \"LOADB4\", OP_UNDEF },\r\n{ \"LOADF4\", OP_UNDEF },\r\n{ \"LOADI4\", OP_UNDEF },\r\n{ \"LOADP4\", OP_UNDEF },\r\n{ \"LOADU4\", OP_UNDEF },\r\n\r\n\r\n"
  },
  {
    "path": "q3asm2/q3asm2.c",
    "content": "/*\n===========================================================================\nCopyright (C) 1999-2005 Id Software, Inc.\n\nThis file is part of the fteqw tools.\n\nFTEQW and the Quake III Arena source code are free software; you can redistribute them\nand/or modify them under the terms of the GNU General Public License as\npublished by the Free Software Foundation; either version 2 of the License,\nor (at your option) any later version.\n\nFTEQW and the Quake III Arena source code are distributed in the hope that it will be\nuseful, but WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Foobar; if not, write to the Free Software\nFoundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n===========================================================================\n*/\n\n/*\nCopyright (C) 2007 David Walton\n\nGPL.\n\nThis file is the core of an assembler/linker to generate q3-compatable qvm files. It is based on the version in the Q3 source code, but rewritten from the ground up.\n*/\n\n#include <stdio.h>\n#include <string.h>\n#include <stdlib.h>\n#include <stdarg.h>\n#include \"../engine/qclib/hash.h\"\n\n\n//from qfiles.h\n#define\tVM_MAGIC\t0x12721444\nstruct vmheader {\n\tint\t\tvmMagic;\n\n\tint\t\tinstructionCount;\n\n\tint\t\tcodeOffset;\n\tint\t\tcodeLength;\n\n\tint\t\tdataOffset;\t// data then lit are here\n\tint\t\tdataLength;\n\tint\t\tlitLength;\t// ( dataLength - litLength ) should be byteswapped on load\n\tint\t\tbssLength;\t// zero filled memory appended to datalength\n};\n//end of qfiles.h\n\n\n\ntypedef enum {\n\tOP_UNDEF, \n\n\tOP_IGNORE, \n\n\tOP_BREAK, \n\n\tOP_ENTER,\n\tOP_LEAVE,\n\tOP_CALL,\n\tOP_PUSH,\n\tOP_POP,\n\n\tOP_CONST,\n\tOP_LOCAL,\n\n\tOP_JUMP,\n\n\t//-------------------\n\n\tOP_EQ,\n\tOP_NE,\n\n\tOP_LTI,\n\tOP_LEI,\n\tOP_GTI,\n\tOP_GEI,\n\n\tOP_LTU,\n\tOP_LEU,\n\tOP_GTU,\n\tOP_GEU,\n\n\tOP_EQF,\n\tOP_NEF,\n\n\tOP_LTF,\n\tOP_LEF,\n\tOP_GTF,\n\tOP_GEF,\n\n\t//-------------------\n\n\tOP_LOAD1,\n\tOP_LOAD2,\n\tOP_LOAD4,\n\tOP_STORE1,\n\tOP_STORE2,\n\tOP_STORE4,\t\t\t\t// *(stack[top-1]) = stack[yop\n\tOP_ARG,\n\tOP_BLOCK_COPY,\n\n\t//-------------------\n\n\tOP_SEX8,\n\tOP_SEX16,\n\n\tOP_NEGI,\n\tOP_ADD,\n\tOP_SUB,\n\tOP_DIVI,\n\tOP_DIVU,\n\tOP_MODI,\n\tOP_MODU,\n\tOP_MULI,\n\tOP_MULU,\n\n\tOP_BAND,\n\tOP_BOR,\n\tOP_BXOR,\n\tOP_BCOM,\n\n\tOP_LSH,\n\tOP_RSHI,\n\tOP_RSHU,\n\n\tOP_NEGF,\n\tOP_ADDF,\n\tOP_SUBF,\n\tOP_DIVF,\n\tOP_MULF,\n\n\tOP_CVIF,\n\tOP_CVFI\n} opcode_t;\n\n\ntypedef struct {\n\tchar\t*name;\n\tint\t\topcode;\n} sourceOps_t;\n\nsourceOps_t\t\tsourceOps[] = {\n#include \"opstrings.h\"\n};\n\n\nenum segs {\n\tSEG_CODE,\n\tSEG_DATA,\n\tSEG_LIT,\n\tSEG_BSS,\n\n\tSEG_MAX,\n\tSEG_UNKNOWN\n};\n\n#define SYMBOLBUCKETS 4096\n#define LOCALSYMBOLBUCKETS 1024\n#define USEDATBLOCK 64\n#define STACKSIZE 0x10000\n\n#ifdef WIN32\n#define snprintf _snprintf\n#endif\n\nstruct usedat {\n\tstruct usedat *next;\n\tint count;\n\t\n\tchar seg[USEDATBLOCK];\n\tint offset[USEDATBLOCK];\n};\n\nstruct symbol {\n\tstruct symbol *next;\n\n\tint inseg;\t//this is where the actual value is defined\n\tint atoffset;\n\n\tstruct usedat usedat;\t//this is where it is used\n\t\t\t\t//to help with cpu cache, the first is embeded\n\t\n\tbucket_t bucket;\t//for the hash tables\n\n\tchar name[1];\t//a common C hack\n};\n\nstruct segment {\n\tvoid *data;\n\tunsigned int base;\n\tunsigned int length;\n\tunsigned int available;\t//before it needs a realloc\n\n\tint isdummy;\t//set if there's no real data assosiated with this segment\n};\n\nstruct assembler {\n\tstruct segment segs[SEG_MAX];\n\tstruct segment *curseg;\n\n\tstruct symbol *symbols;\n\n\thashtable_t symboltable;\n\tbucket_t symboltablebuckets[SYMBOLBUCKETS];\n\t\n\thashtable_t localsymboltable;\n\tbucket_t localsymboltablebuckets[LOCALSYMBOLBUCKETS];\n\n\tstruct symbol *lastsym;\t//so we can move symbols that lcc put into the wrong place.\n\t\n\tint numinstructions;\n\n\tint errorcount;\n\n\tint funclocals;\n\tint funcargs;\n\tint funcargoffset;\n\n\tint verbose;\n};\n\nstruct workload {\n\tchar output[256];\n\n\tint numsrcfiles;\n\tchar **srcfilelist;\n\n\tint verbose;\n};\n\n\n\n\n\n\n\nvoid EmitData(struct segment *seg, void *data, int bytes)\n{\n\tunsigned char *d, *s=data;\n\n\tif (seg->isdummy)\n\t{\n\t\tseg->length += bytes;\n\t\treturn;\n\t}\n\n\tif (seg->length + bytes > seg->available)\n\t{\n\t\tunsigned int newlen = (seg->length + bytes + 0x10000) & ~0xffff;\n\t\tvoid *newseg;\n\t\tnewseg = realloc(seg->data, newlen);\n\t\tif (!newseg)\n\t\t{\n\t\t\tprintf(\"out of memory\\n\");\n\t\t\tseg->length += bytes;\n\t\t\tseg->isdummy = 1;\n\t\t\treturn;\n\t\t}\n\t\tmemset((char*)newseg+seg->available, 0, newlen - seg->available);\n\t\tseg->data = newseg;\n\t\tseg->available = newlen;\n\t}\n\n\td = (unsigned char*)seg->data + seg->length;\n\tseg->length += bytes;\n\twhile(bytes-- > 0)\n\t\t*d++ = *s++;\n}\n\nvoid EmitByte(struct segment *seg, unsigned char data)\n{\n\tEmitData(seg, &data, 1);\n}\n\nvoid SkipData(struct segment *seg, int bytes)\n{\n\tseg->length += bytes;\n}\n\nvoid AlignSegment(struct segment *seg, int alignment)\n{\n\t//alignment must always be power of 2\n\talignment--;\n\tseg->length = (seg->length + alignment) & ~(alignment);\n}\n\nvoid AssembleError(struct assembler *w, char *message, ...)\n{\n\tva_list va;\n\n\tva_start(va, message);\n\tvprintf(message, va);\n\tva_end(va);\n\tw->errorcount++;\n}\n\nvoid SegmentHack(struct assembler *w, int seg)\n{\n\t//like id, I don't see any way around this\n\t//plus compatability. :/\n\t\n\tif (w->curseg != &w->segs[seg])\n\t{\n\t\tw->curseg = &w->segs[seg];\n\n\t\tif (w->lastsym)\n\t\t{\n//\t\t\tif (*w->lastsym->name != '$')\n//\t\t\t\tprintf(\"symbol %s segment-switch hack\\n\", w->lastsym->name);\n\t\t\tw->lastsym->inseg = seg;\n\t\t\tw->lastsym->atoffset = w->curseg->length;\n\t\t}\n\t\telse\n\t\t\tprintf(\"segment-switch hack\\n\");\n\t}\n}\n\nvoid DoSegmentHack(struct assembler *w, int bytes)\n{\n\tif (bytes == 4)\n\t{\n\t\tSegmentHack(w, SEG_DATA);\n\t}\n\telse if (bytes == 1)\n\t{\n\t\tSegmentHack(w, SEG_LIT);\n\t}\n\telse\n\t\tAssembleError(w, \"%ibit variables are not supported\\n\", bytes*8);\n}\n\n\nint ParseToken(char *buffer, int bufferlen, char **input)\n{\n\tunsigned char *in = (unsigned char*)*input;\n\t*buffer = 0;\n\twhile (*in <= ' ')\n\t{\n\t\tif (!*in++)\n\t\t\treturn 0;\n\t}\n\n\tif (*in == ';')\t//';' is taken as a comment\n\t\treturn 0;\n\t\n\tdo\n\t{\n\t\tif (!--bufferlen)\n\t\t{\n\t\t\tprintf(\"name too long\\n\");\n\t\t\tbreak;\n\t\t}\n\t\t*buffer++ = *in++;\n\t} while (*in > ' ');\n\t*buffer = 0;\n\t*input = (char*)in;\n\treturn 1;\n}\n\nvoid AddReference(struct usedat *s, int segnum, int segofs)\n{\n\tif (s->count == USEDATBLOCK)\n\t{\t//we're getting a lot of references for this var\n\t\tif (!s->next)\n\t\t{\n\t\t\ts->next = malloc(sizeof(*s));\n\t\t\tif (!s->next)\n\t\t\t{\n\t\t\t\tprintf(\"out of memory\\n\");\n//\t\t\t\tAssembleError(w, \"out of memory\\n\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\ts->next->next = NULL;\n\t\t\ts->next->count = 0;\n\t\t}\n\t\tAddReference(s->next, segnum, segofs);\n\t\treturn;\n\t}\n\ts->seg[s->count] = segnum;\n\ts->offset[s->count] = segofs;\n\ts->count++;\n}\n\nstruct symbol *GetSymbol(struct assembler *w, char *name)\n{\n\tstruct symbol *s;\n\thashtable_t *t;\n\tif (*name == '$')\n\t\tt = &w->localsymboltable;\n\telse\n\t\tt = &w->symboltable;\n\n\ts = Hash_Get(t, name);\n\tif (!s)\n\t{\n\t\ts = malloc(sizeof(*s) + strlen(name));\n\t\tif (!s)\n\t\t{\n\t\t\tAssembleError(w, \"out of memory\\n\");\n\t\t\treturn NULL;\n\t\t}\n\t\tmemset(s, 0, sizeof(*s));\n\t\ts->next = w->symbols;\n\t\tw->symbols = s;\n\t\tstrcpy(s->name, name);\n\t\tHash_Add(t, s->name, s, &s->bucket);\n\t\ts->inseg = SEG_UNKNOWN;\n\t\ts->atoffset = 0;\n\t}\n\n\treturn s;\n}\n\nvoid DefineSymbol(struct assembler *w, char *symname, int segnum, int segofs)\n{\n\tstruct symbol *s;\n\thashtable_t *t;\n\tif (*symname == '$')\n\t\tt = &w->localsymboltable;\n\telse\n\t\tt = &w->symboltable;\n\n\tif (!*symname)\t//bail out\n\t\t*(int*)NULL = -3;\n\t\t\n\ts = Hash_Get(t, symname);\n\tif (s)\n\t{\n\t\tif (s->inseg != SEG_UNKNOWN)\n\t\t\tAssembleError(w, \"symbol %s is already defined!\\n\", symname);\n\t}\n\telse\n\t{\n\t\ts = malloc(sizeof(*s) + strlen(symname));\n\t\tif (!s)\n\t\t{\n\t\t\tprintf (\"out of memory\\n\");\n\t\t\tw->errorcount++;\n\t\t\treturn;\n\t\t}\n\t\tmemset(s, 0, sizeof(*s));\n\t\ts->next = w->symbols;\n\t\tw->symbols = s;\n\t\tstrcpy(s->name, symname);\n\t\tHash_Add(t, s->name, s, &s->bucket);\n\t}\n\tw->lastsym = s;\n\ts->inseg = segnum;\n\ts->atoffset = segofs;\n}\n\nint CalculateExpression(struct assembler *w, char *line)\n{\n\tchar *o;\n\tchar valstr[64];\n\tint val;\n\tint segnum = w->curseg - w->segs;\n\tint segoffs = w->curseg->length;\n\tint isnum;\n\tstruct symbol *s;\n\t//this is expressed as CONST+NAME-NAME\n\t//many instructions need to add an additional base before emitting it\n\t//so set the references to the place that we expect to be\n\t\n\tval = 0;\n\t\n\twhile (*line)\n\t{\n\t\to = valstr;\n\t\tif (*line == '-' || *line == '+')\n\t\t{\n\t\t\t*o++ = *line++;\n\t\t}\n\t\twhile (*(unsigned char*)line > ' ')\n\t\t{\n\t\t\tif (*line == '+' || *line == '-')\n\t\t\t\tbreak;\n\t\t\t*o++ = *line++;\n\t\t}\n\t\t*o = '\\0';\n\n\t\tif (*valstr == '-')\n\t\t\tisnum = 1;\n\t\telse if (*valstr == '+')\n\t\t\tisnum = (valstr[1] >= '0' && valstr[1] <= '9');\n\t\telse\n\t\t\tisnum = (valstr[0] >= '0' && valstr[0] <= '9');\n\n\t\tif (isnum)\n\t\t\tval += atoi(valstr);\n\t\telse\n\t\t{\n\t\t\tif (*valstr == '-' || *valstr == '+')\n\t\t\t\ts = GetSymbol(w, valstr+1);\n\t\t\telse\n\t\t\t\ts = GetSymbol(w, valstr);\n\t\t\tif (s)\t//yeah, doesn't make sense to have two symbols in one expression\n\t\t\t\tAddReference(&s->usedat, segnum, segoffs);\t//but we do it anyway\n\t\t}\n\t}\n\n\treturn val;\n}\n\nvoid AssembleLine(struct assembler *w, char *line)\n{\n\tint i;\n\tint opcode;\n\tchar instruction[64];\n\tif (!ParseToken(instruction, sizeof(instruction), &line))\n\t\treturn;\t//nothing on this line\n\n\t//try and get our special instructions first\n\tswitch (instruction[0])\n\t{\n\t\tcase 'a':\n\t\t\tif (!strcmp(instruction+1, \"lign\"))\n\t\t\t{\t//align\n\t\t\t\t//theoretically, this should never make a difference. if it does, the segment hack stuff screwed something up.\n\t\t\t\tif (ParseToken(instruction, sizeof(instruction), &line))\n\t\t\t\t\tAlignSegment(w->curseg, atoi(instruction));\n\t\t\t\telse\n\t\t\t\t\tAssembleError(w, \"align without argument\\n\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (!strcmp(instruction+1, \"ddress\"))\n\t\t\t{\t//address\n//\t\t\t\tw->lastsym = NULL;\n\t\t\t\tif (ParseToken(instruction, sizeof(instruction), &line))\n\t\t\t\t{\n\t\t\t\t\tint expression;\n\t\t\t\t\tSegmentHack(w, SEG_DATA);\n\t\t\t\t\texpression = CalculateExpression(w, instruction);\n\t\t\t\t\tEmitData(w->curseg, &expression, 4);\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 'b':\n\t\t\tif (!strcmp(instruction+1, \"ss\"))\n\t\t\t{\t//bss\n\t\t\t\tw->curseg = &w->segs[SEG_BSS];\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (!strcmp(instruction+1, \"yte\"))\n\t\t\t{\t//byte\n\t\t\t\tunsigned int value = 0;\n\t\t\t\tunsigned int bytes = 0;\n\n\t\t\t\tif (ParseToken(instruction, sizeof(instruction), &line))\n\t\t\t\t\tbytes = atoi(instruction);\n\t\t\t\telse\n\t\t\t\t\tAssembleError(w, \"byte without count\\n\");\n\t\t\t\tif (ParseToken(instruction, sizeof(instruction), &line))\n\t\t\t\t\tvalue = atoi(instruction);\n\t\t\t\telse\n\t\t\t\t\tAssembleError(w, \"byte without value\\n\");\n\n\t\t\t\tif (bytes == 4)\n\t\t\t\t{\n\t\t\t\t\tSegmentHack(w, SEG_DATA);\n\t\t\t\t\tEmitData(w->curseg, &value, 4);\n\t\t\t\t}\n\t\t\t\telse if (bytes == 1)\n\t\t\t\t{\n\t\t\t\t\tSegmentHack(w, SEG_LIT);\n\t\t\t\t\tEmitByte(w->curseg, value);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tAssembleError(w, \"%ibit variables are not supported\\n\", bytes*8);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 'c':\n\t\t\tif (!strcmp(instruction+1, \"ode\"))\n\t\t\t{\t//code\n\t\t\t\tw->curseg = &w->segs[SEG_CODE];\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 'd':\n\t\t\tif (!strcmp(instruction+1, \"ata\"))\n\t\t\t{\t//data\n\t\t\t\tw->curseg = &w->segs[SEG_DATA];\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 'e':\n\t\t\tif (!strcmp(instruction+1, \"qu\"))\n\t\t\t{\t//equ\n\t\t\t\tchar value[32];\n\t\t\t\tif (ParseToken(instruction, sizeof(instruction), &line))\n\t\t\t\t{\n\t\t\t\t\tif (ParseToken(value, sizeof(value), &line))\n\t\t\t\t\t\tDefineSymbol(w, instruction, w->curseg - w->segs, atoi(value));\n\t\t\t\t\telse\n\n\t\t\t\t\t\tAssembleError(w, \"equ without value\\n\");\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tAssembleError(w, \"equ without name\\n\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (!strcmp(instruction+1, \"xport\"))\n\t\t\t{\t//export (ignored)\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (!strcmp(instruction+1, \"ndproc\"))\n\t\t\t{\t//endproc\n\t\t\t\tint locals = 0, args = 0;\n\t\t\t\tif (ParseToken(instruction, sizeof(instruction), &line))\n\t\t\t\t\tlocals = atoi(instruction);\n\t\t\t\tif (ParseToken(instruction, sizeof(instruction), &line))\n\t\t\t\t\targs = atoi(instruction);\n\n\t\t\t\tEmitByte(&w->segs[SEG_CODE], OP_PUSH);\t//we must return something\n\t\t\t\tw->numinstructions++;\n\n\t\t\t\t//disregard the two vars from above (matches q3asm...)\n\t\t\t\tlocals = 8 + w->funclocals + w->funcargs;\n\t\t\t\tEmitByte(&w->segs[SEG_CODE], OP_LEAVE);\n\t\t\t\tEmitData(&w->segs[SEG_CODE], &locals, 4);\n\t\t\t\tw->numinstructions++;\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 'f':\n\t\t\tif (!strcmp(instruction+1, \"ile\"))\n\t\t\t{\t//file (ignored)\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 'i':\n\t\t\tif (!strcmp(instruction+1, \"mport\"))\n\t\t\t{\t//import (ignored)\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 'l':\n\t\t\tif (!strcmp(instruction+1, \"ine\"))\n\t\t\t{\t//line (ignored)\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (!strcmp(instruction+1, \"it\"))\n\t\t\t{\t//lit\n\t\t\t\tw->curseg = &w->segs[SEG_LIT];\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 'p':\n\t\t\tif (!strcmp(instruction+1, \"roc\"))\n\t\t\t{\t//proc\n\t\t\t\tint v;\n\t\t\t\tif (!ParseToken(instruction, sizeof(instruction), &line))\n\t\t\t\t\tAssembleError(w, \"unnamed function\\n\");\n\t\t\t\tDefineSymbol(w, instruction, SEG_CODE, w->numinstructions);\n\t\t\t\tif (ParseToken(instruction, sizeof(instruction), &line))\n\t\t\t\t\tw->funclocals = (atoi(instruction) + 3) & ~3;\n\t\t\t\telse\n\t\t\t\t\tAssembleError(w, \"proc without locals\\n\");\n\t\t\t\tif (ParseToken(instruction, sizeof(instruction), &line))\n\t\t\t\t\tw->funcargs = (atoi(instruction) + 3) & ~3;\n\t\t\t\telse\n\t\t\t\t\tAssembleError(w, \"proc without args\\n\");\n\n\t\t\t\tv = 8 + w->funclocals + w->funcargs;\n\t\t\t\tif (v > 32767)\n\t\t\t\t\tAssembleError(w, \"function with an aweful lot of args\\n\");\n\n\t\t\t\tEmitByte(&w->segs[SEG_CODE], OP_ENTER);\n\t\t\t\tEmitData(&w->segs[SEG_CODE], &v, 4);\n\t\t\t\tw->numinstructions++;\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (!strcmp(instruction+1, \"op\"))\n\t\t\t{\t//pop\n\t\t\t\tEmitByte(&w->segs[SEG_CODE], OP_POP);\n\t\t\t\tw->numinstructions++;\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 's':\n\t\t\tif (!strcmp(instruction+1, \"kip\"))\n\t\t\t{\t//skip\n\t\t\t\tif (ParseToken(instruction, sizeof(instruction), &line))\n\t\t\t\t\tSkipData(w->curseg, atoi(instruction)); \n\t\t\t\telse\n\t\t\t\t\tAssembleError(w, \"skip without argument\\n\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 'L':\n\t\t\tif (!strncmp(instruction+1, \"ABEL\", 4))\n\t\t\t{\n\t\t\t\tif (!ParseToken(instruction, sizeof(instruction), &line))\n\t\t\t\t{\n\t\t\t\t\tprintf(\"label with no name\\n\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\t//some evilness here (symbols in the instruction segment are instruction indexes not byte offsets)\n\t\t\t\tif (w->curseg == &w->segs[SEG_CODE])\n\t\t\t\t\tDefineSymbol(w, instruction, w->curseg - w->segs, w->numinstructions);\n\t\t\t\telse\n\t\t\t\t\tDefineSymbol(w, instruction, w->curseg - w->segs, w->curseg->length);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 'A':\n\t\t\tif (!strncmp(instruction+1, \"RG\", 2))\n\t\t\t{\t//ARG*\n\t\t\t\tEmitByte(&w->segs[SEG_CODE], OP_ARG);\n\t\t\t\tw->numinstructions++;\n\t\t\t\tif (8 + w->funcargoffset > 255)\n\t\t\t\t\tAssembleError(w, \"too many arguments in fuction\\n\");\n\t\t\t\tEmitByte(&w->segs[SEG_CODE], 8 + w->funcargoffset);\n\t\t\t\tw->funcargoffset += 4;\t//reset by a following CALL instruction\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (!strncmp(instruction+1, \"DDRF\", 4))\n\t\t\t{\t//ADDRF*\n\t\t\t\tint exp = 0;\n\t\t\t\tw->curseg = &w->segs[SEG_CODE];\t//this differs, but not for lcc\n\t\t\t\tEmitByte(&w->segs[SEG_CODE], OP_LOCAL);\n\t\t\t\tif (ParseToken(instruction, sizeof(instruction), &line))\n\t\t\t\t\texp = CalculateExpression(w, instruction);\n\t\t\t\texp += 16 + w->funcargs + w->funclocals;\n\t\t\t\tEmitData(&w->segs[SEG_CODE], &exp, 4);\n\t\t\t\tw->numinstructions++;\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (!strncmp(instruction+1, \"DDRL\", 4))\n\t\t\t{\t//ADDRL\n\t\t\t\tint exp = 0;\n\t\t\t\tw->curseg = &w->segs[SEG_CODE];\t//this differs, but not for lcc\n\t\t\t\tEmitByte(&w->segs[SEG_CODE], OP_LOCAL);\n\t\t\t\tif (ParseToken(instruction, sizeof(instruction), &line))\n\t\t\t\t\texp = CalculateExpression(w, instruction);\n\t\t\t\texp += 8 + w->funcargs;\n\t\t\t\tEmitData(&w->segs[SEG_CODE], &exp, 4);\n\t\t\t\tw->numinstructions++;\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 'C':\n\t\t\tif (!strncmp(instruction+1, \"ALL\", 3))\n\t\t\t{\t//CALL*\n\t\t\t\tEmitByte(&w->segs[SEG_CODE], OP_CALL);\n\t\t\t\tw->numinstructions++;\n\t\t\t\tw->funcargoffset = 0;\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 'R':\n\t\t\tif (!strncmp(instruction+1, \"ET\", 2))\n\t\t\t{\n\t\t\t\tint v = 8 + w->funclocals + w->funcargs;\n\t\t\t\tEmitByte(&w->segs[SEG_CODE], OP_LEAVE);\n\t\t\t\tEmitData(&w->segs[SEG_CODE], &v, 4);\n\t\t\t\tw->numinstructions++;\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tbreak;\n\t}\n\n\t//okay, we don't know what it is yet, try generic instructions\n\t\n\tfor (i = 0; i < sizeof(sourceOps) / sizeof(sourceOps[0]); i++)\n\t{\n\t\tif (*(unsigned int*)sourceOps[i].name == *(unsigned int*)instruction && !strcmp(sourceOps[i].name+4, instruction+4))\n\t\t{\n\t\t\topcode = sourceOps[i].opcode;\n\t\t\tif (opcode == OP_IGNORE)\n\t\t\t\treturn;\n\n\t\t\tif (opcode == OP_SEX8)\n\t\t\t{\t//sex is special, apparently\n\t\t\t\t\n\t\t\t\tif (ParseToken(instruction, sizeof(instruction), &line))\n\t\t\t\t{\n\t\t\t\t\tif (*instruction == '2')\n\t\t\t\t\t\topcode = OP_SEX16;\n\t\t\t\t\telse if (*instruction != '1')\n\t\t\t\t\t\tAssembleError(w, \"bad sign extension\\n\");\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (opcode == OP_CVIF || opcode == OP_CVFI)\n\t\t\t\tline = \"\";\t//so we don't fall afoul looking for aguments (float/int conversions are always 4 anyway)\n\n\t\t\tif (ParseToken(instruction, sizeof(instruction), &line))\n\t\t\t{\n\t\t\t\tint expression;\n\t\t\t\tw->curseg = &w->segs[SEG_CODE];\t//this differs, but not for lcc\n\t\t\t\tEmitByte(&w->segs[SEG_CODE], opcode);\n\t\t\t\texpression = CalculateExpression(w, instruction);\n\n\t\t\t\tif (opcode == OP_BLOCK_COPY)\t//string initialisations are block copys too (this could still be wrong, but it matches)\n\t\t\t\t\texpression = (expression+3)&~3;\n\t\t\t\tEmitData(&w->segs[SEG_CODE], &expression, 4);\n\t\t\t}\n\t\t\telse\n\t\t\t\tEmitByte(&w->segs[SEG_CODE], opcode);\n\n\t\t\tw->numinstructions++;\n\t\t\treturn;\n\t\t}\n\t}\n\n\tAssembleError(w, \"Cannot handle %s\\n\", instruction);\n}\n\nvoid AssembleFile(struct assembler *w, int fnum, char *filename)\n{\n\tchar linebuffer[1024];\n\tFILE *f;\n\tif (w->verbose)\n\t{\n\t\tif (fnum)\n\t\t\tprintf(\"file: %i %s\\n\", fnum, filename);\n\t\telse\n\t\t\tprintf(\"file: %s\\n\", filename);\n\t}\n\n\tw->curseg = &w->segs[SEG_CODE];\n\n\tf = fopen(filename, \"rt\");\r\n\tif (!f)\r\n\t{\r\n\t\tsnprintf(linebuffer, sizeof(linebuffer)-1, \"%s.asm\", filename);\r\n\t\tf = fopen(linebuffer, \"rt\");\r\n\t}\r\n\n\tif (f)\n\t{\n\t\twhile(fgets(linebuffer, sizeof(linebuffer), f))\n\t\t\tAssembleLine(w, linebuffer);\n\t\tfclose(f);\n\t}\n\telse\r\n\t{\r\n\t\tprintf(\"couldn't open %s\\n\", filename);\n\t\tw->errorcount++;\r\n\t}\n\n\t//reset the local symbol hash, so we don't find conflicting vars\n\tmemset(w->localsymboltablebuckets, 0, sizeof(w->localsymboltablebuckets));\n}\n\n\n\nint FixupSymbolReferencesOne(struct assembler *w, struct symbol *s, struct usedat *u)\n{\n\tint i;\n\tint val;\n\tunsigned int temp;\n\tunsigned char *ptr;\n\tval = w->segs[s->inseg].base + s->atoffset;\n\tfor (i = 0; i < u->count; i++)\n\t{\n\t\tptr = (unsigned char*)w->segs[(int)u->seg[i]].data;\n\t\tptr += u->offset[i];\n\t\ttemp = (unsigned int)(ptr[0] | (ptr[1]<<8) | (ptr[2]<<16) | (ptr[3]<<24));\n\t\t*(int*)&temp += val;\n\t\tptr[0] = (temp&0x000000ff)>>0;\n\t\tptr[1] = (temp&0x0000ff00)>>8;\n\t\tptr[2] = (temp&0x00ff0000)>>16;\n\t\tptr[3] = (temp&0xff000000)>>24;\n\t}\n\tif (u->next)\n\t\ti += FixupSymbolReferencesOne(w, s, u->next);\n\t\n\treturn i;\n}\n\n\nvoid FixupSymbolReferences(struct assembler *w)\n{\t//this is our 'second pass'\n\tstruct symbol *s;\n\tint numsyms = 0;\n\tint numsymrefs = 0;\n\n\tif (w->verbose)\n\t\tprintf(\"second 'pass'\\n\");\n\n\tfor (s = w->symbols; s; s = s->next)\n\t{\n\t\tif (s->inseg == SEG_UNKNOWN)\n\t\t{\n\t\t\tAssembleError(w, \"Symbol %s was not defined\\n\", s->name);\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (w->verbose && !s->usedat.count && *s->name != '$')\t//don't ever mention the compiler generated 'static' vars\n\t\t\tprintf(\"%s was never used\\n\", s->name);\n\t\tnumsymrefs += FixupSymbolReferencesOne(w, s, &s->usedat);\n\t\tnumsyms++;\n\t}\n\n\n\ts = GetSymbol(w, \"vmMain\");\n\tif (s)\n\t{\n\t\tif (s->atoffset != 0 || s->inseg != SEG_CODE)\n\t\t\tAssembleError(w, \"vmMain *MUST* be the first symbol in the qvm\\nReorder your files\\n\");\n\t}\n\n\tif (w->verbose)\n\t{\n\t\tprintf(\"Found %i symbols\\n\", numsyms);\n\t\tprintf(\"Found %i symbol references\\n\", numsymrefs);\n\t}\n}\n\nvoid WriteMapFile(struct assembler *w, char *fname)\n{\n\tint segnum;\n\tstruct symbol *s;\n\tFILE *f;\n\tf = fopen(fname, \"wt\");\n\tif (!f)\n\t\treturn;\n//if we sorted these, we'd have better compatability with id's q3asm\n//(their linker only defines variables in the second pass, their first pass is only to get table sizes)\n//our symbols are in the order they were referenced, rather than used.\n\tfor (segnum = 0; segnum < SEG_MAX; segnum++)\n\t{\n\t\tfor (s = w->symbols; s; s = s->next)\n\t\t{\n\t\t\tif (*s->name == '$')\t//don't show locals\n\t\t\t\tcontinue;\n\t\t\tif (s->inseg != segnum)\n\t\t\t\tcontinue;\n\n\t\t\tfprintf(f, \"%i %8x %s\\n\", s->inseg, s->atoffset, s->name);\n\t\t}\n\t}\n\t\n\tfclose(f);\n}\n\n\n\n\n\nvoid InitialiseAssembler(struct assembler *w)\n{\n\tint i;\n\tmemset(w, 0, sizeof(*w));\n\n\tHash_InitTable(&w->symboltable, SYMBOLBUCKETS, w->symboltablebuckets);\n\tHash_InitTable(&w->localsymboltable, LOCALSYMBOLBUCKETS, w->localsymboltablebuckets);\n\t\n\tw->segs[SEG_BSS].isdummy = 1;\n\n\ti = 0;\n\tEmitData(&w->segs[SEG_DATA], &i, 4);\t//so NULL really is NULL\n\n}\n\nvoid WriteOutput(struct assembler *w, char *outputname)\n{\n\tFILE *f;\n\tstruct vmheader h;\n\n\tint i;\n\tfor (i = 0; i < SEG_MAX; i++)\n\t{\t//align the segments (yes, even the code segment)\n\t\tw->segs[i].length = (w->segs[i].length + 3) & ~3;\n\t}\n\n\t//add the stack to the end of the bss. I don't know if q3 even uses this\n\tDefineSymbol(w, \"_stackStart\", SEG_BSS, w->segs[SEG_BSS].length);\n\tw->segs[SEG_BSS].length += STACKSIZE;\n\tDefineSymbol(w, \"_stackEnd\", SEG_BSS, w->segs[SEG_BSS].length);\n\n\tw->segs[SEG_CODE].base = 0;\n\tw->segs[SEG_DATA].base = 0;\n\tw->segs[SEG_LIT].base = w->segs[SEG_DATA].base + w->segs[SEG_DATA].length;\n\tw->segs[SEG_BSS].base = w->segs[SEG_LIT].base + w->segs[SEG_LIT].length;\n\n\tFixupSymbolReferences(w);\n\t\n\tif (w->segs[SEG_CODE].isdummy || w->segs[SEG_DATA].isdummy || w->segs[SEG_LIT].isdummy)\n\t\tw->errorcount++;\t//one of the segments failed to allocate mem\n\t\n\tprintf(\"code bytes: %i\\n\", w->segs[SEG_CODE].length);\n\tprintf(\"data bytes: %i\\n\", w->segs[SEG_DATA].length);\n\tprintf(\" lit bytes: %i\\n\", w->segs[SEG_LIT ].length);\n\tprintf(\" bss bytes: %i\\n\", w->segs[SEG_BSS ].length);\n\tprintf(\"instruction count: %i\\n\", w->numinstructions);\n\n\tif (w->errorcount)\n\t{\n\t\tprintf(\"%i errors\\n\", w->errorcount);\n\t\treturn;\n\t}\n\t\n\n\th.vmMagic = VM_MAGIC;\n\th.instructionCount = w->numinstructions;\n\th.codeLength = w->segs[SEG_CODE].length;\n\th.dataLength = w->segs[SEG_DATA].length;\n\th.litLength = w->segs[SEG_LIT].length;\n\th.bssLength = w->segs[SEG_BSS].length;\n\n\th.codeOffset = sizeof(h);\n\th.dataOffset = h.codeOffset + h.codeLength;\n\tf = fopen(outputname, \"wb\");\n\tif (f)\n\t{\n\t\tfwrite(&h, 1, sizeof(h), f);\n\t\tfwrite(w->segs[SEG_CODE].data, 1, w->segs[SEG_CODE].length, f);\n\t\tfwrite(w->segs[SEG_DATA].data, 1, w->segs[SEG_DATA].length, f);\n\t\tfwrite(w->segs[SEG_LIT ].data, 1, w->segs[SEG_LIT ].length, f);\n\t\t//logically the bss comes here (but doesn't, cos its bss)\n\t\tfclose(f);\n\t}\n\telse\n\t\tprintf(\"error writing to file: %s\\n\", outputname);\n\n\n\tWriteMapFile(w, \"q3asm2.map\");\n}\n\n\n\n\n\n\n\n\n\n\nvoid Assemble(struct workload *w)\n{\n\tint i;\n\n\tstruct assembler a;\n\n\tInitialiseAssembler(&a);\n\ta.verbose = w->verbose;\n\n\tfor (i = 0; i < w->numsrcfiles; i++)\n\t\tAssembleFile(&a, i+1, w->srcfilelist[i]);\n\t\n\tWriteOutput(&a, w->output);\n}\n\nint ParseCommandList(struct workload *w, int numcmds, char **cmds)\n{\n\tchar **t;\r\n\r\n\tFILE *f;\r\n\tchar *buffer, *pp, *pps;\r\n\tunsigned int len, numextraargs;\r\n\tchar *suppargs[64];\r\n\n\twhile (numcmds)\n\t{\n\t\tif ((*cmds)[0] == '-')\n\t\t{\n\t\t\tif ((*cmds)[1] == 'f')\n\t\t\t{\n\t\t\t\tnumcmds--;\n\t\t\t\tcmds++;\r\n\r\n\t\t\t\tf = fopen(*cmds, \"rt\");\r\n\t\t\t\tif (!f)\r\n\t\t\t\t{\r\n\t\t\t\t\tchar blah[256];\r\n\t\t\t\t\tsnprintf(blah, sizeof(blah)-1, \"%s.q3asm\", *cmds);\r\n\t\t\t\t\tf = fopen(blah, \"rt\");\r\n\t\t\t\t}\r\n\t\t\t\tif (f)\r\n\t\t\t\t{\r\n\t\t\t\t\tfseek(f, 0, SEEK_END);\r\n\t\t\t\t\tlen = ftell(f);\r\n\t\t\t\t\tfseek(f, 0, SEEK_SET);\r\n\t\t\t\t\tbuffer = malloc((len+9));\r\n\t\t\t\t\tif (buffer)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tif (fread(buffer, 1, len, f) == len)\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tbuffer[len] = 0;\r\n\t\t\t\t\t\t\tnumextraargs = 0;\r\n\t\t\t\t\t\t\tpp = buffer;\r\n\t\t\t\t\t\t\tdo\r\n\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\tif (numextraargs == sizeof(suppargs)/sizeof(suppargs[0]))\r\n\t\t\t\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t\t\t\twhile (*pp == ' ' || *pp == '\\r' || *pp == '\\n')\r\n\t\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\t\tpp++;\r\n\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t\tif (*pp == '\\\"')\r\n\t\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\t\tpp++;\r\n\t\t\t\t\t\t\t\t\tpps = pp;\r\n\t\t\t\t\t\t\t\t\twhile (*pp && *pp != '\\\"')\r\n\t\t\t\t\t\t\t\t\t\tpp++;\r\n\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t\telse\r\n\t\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\t\tpps = pp;\r\n\t\t\t\t\t\t\t\t\twhile (*pp && !(*pp == ' ' || *pp == '\\r' || *pp == '\\n'))\r\n\t\t\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\t\t\tpp++;\r\n\t\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t\tif (*pp)\r\n\t\t\t\t\t\t\t\t\t*pp++ = 0;\r\n\t\t\t\t\t\t\t\tsuppargs[numextraargs] = pps;\r\n\t\t\t\t\t\t\t\tnumextraargs++;\r\n\t\t\t\t\t\t\t} while (*pp);\r\n\t\t\t\t\t\t\tParseCommandList(w, numextraargs, suppargs);\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\telse\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tprintf(\"failure reading source file\\n\");\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\tfree(buffer);\r\n\t\t\t\t\t}\r\n\t\t\t\t\telse\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tprintf(\"out of memory\\n\");\r\n\t\t\t\t\t}\r\n\t\t\t\t\tfclose(f);\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\tprintf(\"couldn't open \\\"%s\\\"\\n\", *cmds);\r\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if ((*cmds)[1] == 'o')\n\t\t\t{\n\t\t\t\tnumcmds--;\n\t\t\t\tcmds++;\n\r\n\t\t\t\tif (!strchr(*cmds, '.'))\r\n\t\t\t\t{\r\n\t\t\t\t\tsnprintf(w->output, sizeof(w->output)-1, \"%s.qvm\", *cmds);\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t{\n\t\t\t\t\tstrncpy(w->output, *cmds, sizeof(w->output)-1);\n\t\t\t\t\tw->output[sizeof(w->output)-1] = 0;\r\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if ((*cmds)[1] == 'v')\n\t\t\t\tw->verbose = 1;\n\t\t\telse\n\t\t\t{\n\t\t\t\tprintf(\"Unrecognised command\\n\");\n\t\t\t\treturn 1;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\t//just a src file\n\t\t\tt = realloc(w->srcfilelist, sizeof(char*)*(w->numsrcfiles+1));\n\t\t\tif (!t)\n\t\t\t{\t//if realloc fails, the old pointer is still valid\n\t\t\t\tprintf(\"out of memory\\n\");\n\t\t\t\treturn 1;\n\t\t\t}\n\t\t\tw->srcfilelist = t;\n\t\t\tw->srcfilelist[w->numsrcfiles] = malloc(strlen(*cmds)+1);\n\t\t\tif (!w->srcfilelist[w->numsrcfiles])\n\t\t\t{\n\t\t\t\tprintf(\"out of memory\\n\");\n\t\t\t\treturn 1;\n\t\t\t}\n\t\t\tstrcpy(w->srcfilelist[w->numsrcfiles], *cmds);\n\t\t\tw->numsrcfiles++;\n\t\t}\n\t\tnumcmds--;\n\t\tcmds++;\n\t}\n\n\treturn 0;\n}\n\nint main(int argc, char **argv)\n{\n\tint i;\n\tstruct workload *w;\n\n\tif (!(w = malloc(sizeof(*w))))\n\t{\n\t\tprintf(\"out of memory (REALLY EARLY!!!)\\n\");\n\t}\n\telse\n\t{\n\t\tw->numsrcfiles = 0;\n\t\tw->srcfilelist = NULL;\n\t\tstrcpy(w->output, \"q3asm2.qvm\");\t//fill in the default options\n\n\t\tif (ParseCommandList(w, argc-1, argv+1))\n\t\t{\n\t\t\tprintf(\"Syntax is: q3asm2 [-o <output>] <files>\\n\");\n\t\t\tprintf(\"       or: q3asm2 -f <listfile>\\n\");\n\t\t\tprintf(\"or any crazy recursive mixture of the two\\n\");\n\t\t}\n\t\telse\n\t\t{\n\t\t\tprintf(\"output file: %s\\n\", w->output);\n\t\t\tprintf(\"%i files\\n\", w->numsrcfiles);\n\n\n\t\t\tAssemble(w);\n\t\t}\n\n\t\tfor (i = 0; i < w->numsrcfiles; i++)\n\t\t{\n\t\t\tfree(w->srcfilelist[i]);\n\t\t}\n\t\tif (w->srcfilelist)\n\t\t\tfree(w->srcfilelist);\n\n\t\tfree(w);\n\t}\n\treturn 0;\n}\n\n"
  },
  {
    "path": "quakec/autoext/lists/extlist.txt",
    "content": "BX_COLOREDTEXT\nBX_CON_DEL\nBX_CON_EXT\nBX_CON_TEXREPLACE\nBX_CON_VAR\nBX_CON_WRITECONFIG\nBX_ENGINE\nBX_ENT_BOUNCESCALE\nBX_ENT_FRICTION\nBX_ENT_MODELFLAGS\nBX_GFX_SKYBOX\nBX_GFX_SUN\nBX_INPUTBUTTONS\nBX_MDLOVERRIDE\nBX_MULTIPLEFONTS\nBX_P_M_SUPPORT\nBX_QC_CAMERA\nBX_QC_CHECKFOV\nBX_QC_FINDFIELDCHAIN\nBX_QC_GETDATETIME\nBX_QC_INIFILE\nBX_QC_MSGREAD\nBX_QC_PROGPARMS\nBX_QC_PROTOCOL_FLAGS\nBX_QC_SOUNDTEAM\nBX_QC_SPRINTF\nBX_QC_STR\nBX_QC_VECMATH\nBX_QC_VISIBLE\nBX_SERVERPROGS\nBX_SND_MUSIC\nBX_SV_LOCKCVARCMD\nBX_SV_PARSESAY\nBX_SV_UPDATE_CLIENTCVARS\nBX_WAL_SUPPORT\nBX_WEAPONBOB\nDP_BUTTONCHAT\nDP_BUTTONUSE\nDP_CGAME\nDP_CL_LOADSKY\nDP_CON_SET\nDP_CON_SETA\nDP_CON_STARTMAP\nDP_EF_ADDITIVE\nDP_EF_BLUE\nDP_EF_FLAME\nDP_EF_FULLBRIGHT\nDP_EF_NODEPTHTEST\nDP_EF_NODRAW\nDP_EF_NOSHADOW\nDP_EF_RED\nDP_EF_STARDUST\nDP_ENT_ALPHA\nDP_ENT_COLORMOD\nDP_ENT_CUSTOMCOLORMAP\nDP_ENT_DELTACOMPRESS\nDP_ENT_EXTERIORMODELTOCLIENT\nDP_ENT_GLOW\nDP_ENT_LOWPRECISION\nDP_ENT_SCALE\nDP_ENT_VIEWMODEL\nDP_GFX_EXTERNALTEXTURES\nDP_GFX_FOG\nDP_GFX_QUAKE3MODELTAGS\nDP_GFX_SKINFILES\nDP_GFX_SKYBOX\nDP_HALFLIFE_MAP\nDP_HALFLIFE_MAP_CVAR\nDP_HALFLIFE_SPRITE\nDP_INPUTBUTTONS\nDP_LITSPRITES\nDP_LITSUPPORT\nDP_MD3_TAGSINFO\nDP_MONSTERWALK\nDP_MOVETYPEBOUNCEMISSILE\nDP_MOVETYPEFOLLOW\nDP_QC_BOTCLIENT\nDP_QC_CHANGEPITCH\nDP_QC_COPYENTITY\nDP_QC_CVAR_STRING\nDP_QC_ETOS\nDP_QC_FINDCHAIN\nDP_QC_FINDCHAINFLAGS\nDP_QC_FINDCHAINFLOAT\nDP_QC_FINDFLAGS\nDP_QC_FINDFLOAT\nDP_QC_FS_SEARCH\nDP_QC_GETLIGHT\nDP_QC_GETSURFACE\nDP_QC_GETTAGINFO\nDP_QC_MINMAXBOUND\nDP_QC_MULTIPLETEMPSTRINGS\nDP_QC_RANDOMVEC\nDP_QC_SINCOSSQRTPOW\nDP_QC_TRACE_MOVETYPE_HITMODEL\nDP_QC_TRACE_MOVETYPE_WORLDONLY\nDP_QC_TRACE_MOVETYPES\nDP_QC_TRACEBOX\nDP_QC_TRACETOSS\nDP_QC_VECTORVECTORS\nDP_QUAKE2_MODEL\nDP_QUAKE2_SPRITE\nDP_QUAKE3_MAP\nDP_QUAKE3_MODEL\nDP_REGISTERCVAR\nDP_SENSITIVITYSCALE\nDP_SND_DIRECTIONLESSATTNNONE\nDP_SND_FAKETRACKS\nDP_SND_OGGVORBIS\nDP_SND_STEREOWAV\nDP_SOLIDCORPSE\nDP_SPRITE32\nDP_SV_CLIENTCOLORS\nDP_SV_CLIENTNAME\nDP_SV_DRAWONLYTOCLIENT\nDP_SV_DRAWONLYTOTEAM\nDP_SV_DROPCLIENT\nDP_SV_EFFECT\nDP_SV_EXTERIORMODELFORCLIENT\nDP_SV_NODRAWTOCLIENT\nDP_SV_PING\nDP_SV_PLAYERPHYSICS\nDP_SV_PUNCHVECTOR\nDP_SV_ROTATINGBMODEL\nDP_SV_SETCOLOR\nDP_SV_SLOWMO\nDP_TE_BLOOD\nDP_TE_BLOODSHOWER\nDP_TE_CUSTOMFLASH\nDP_TE_EXPLOSIONRGB\nDP_TE_FLAMEJET\nDP_TE_PARTICLECUBE\nDP_TE_PARTICLERAIN\nDP_TE_PARTICLESNOW\nDP_TE_PLASMABURN\nDP_TE_QUADEFFECTS1\nDP_TE_SMALLFLASH\nDP_TE_SPARK\nDP_TE_STANDARDEFFECTBUILTINS\nDP_VIEWZOOM\nDP_WEAPONSKIN\nEXT_BITSHIFT\nEXT_CSQC\nEXT_DIMENSION_GHOST\nEXT_DIMENSION_PHYSICS\nEXT_DIMENSION_VISIBILITY\nFTE_CALLTIMEOFDAY\nFTE_FORCEINFOKEY\nFTE_HEXEN2\nFTE_MEDIA_AVI\nFTE_MEDIA_CIN\nFTE_MEDIA_ROQ\nFTE_MULTIPROGS\nFTE_MULTITHREADED\nFTE_MVD_PLAYBACK\nFTE_NPCCHAT\nFTE_PEXT_BULLETENS\nFTE_PEXT_CUSTOMTENTS\nFTE_PEXT_ENTITYDBL\nFTE_PEXT_ENTITYDBL2\nFTE_PEXT_FATNESS\nFTE_PEXT_HULLSIZE\nFTE_PEXT_LIGHTSTYLECOL\nFTE_PEXT_LIGHTUPDATES\nFTE_PEXT_MODELDBL\nFTE_PEXT_ORIGINDBL\nFTE_PEXT_Q2BSP\nFTE_PEXT_Q3BSP\nFTE_PEXT_SETVIEW\nFTE_PEXT_SPAWNSTATIC\nFTE_PEXT_TE_BULLET\nFTE_PEXT_VIEW2\nFTE_PEXT_VWEAP\nFTE_PEXT_ZLIBDL\nFTE_SOLID_LADDER\nFTE_VALID_VEC_HULL3\nFRIK_FILE\nKRIMZON_SV_PARSECLIENTCOMMAND\nNEH_CMD_PLAY2\nNEH_RESTOREGAME\nNEXUIZ_PLAYERMODEL\nNXQ_GFX_LETTERBOX\nPRYDON_CLIENTCURSOR\nQSG_FILE\nQSG_CVARSTRING\nQSG_DIMENSIONAL\nQSG_NO_CENTERPRINT\nQSG_NO_MDL\nQSG_NO_NETCODE_Q1\nQW_ENGINE\nQWE_MVD_RECORD\nTEI_AUTOANIMATION\nTEI_AUTOFLUOR\nTEI_AUTOFOGWATER\nTEI_AUTOFOOT\nTEI_AUTOLAVA\nTEI_AUTOLIGHTDAY\nTEI_AUTORAIN\nTEI_AUTOSNOW\nTEI_AUTOVANISH\nTEI_AUTOZING\nTEI_BOB2\nTEI_EF_EXTRA\nTEI_EF2_EXTRA\nTEI_EF3_EXTRA\nTEI_HUDMANAGER\nTEI_LOCALMENUS1\nTEI_LOCALMENUS2\nTEI_MAPMODELS\nTEI_MD3_MODEL\nTEI_MENU_MISC2\nTEI_MOD_PROGS\nTEI_MODPREDATOR\nTEI_MOVMAGNETIC\nTEI_MOVMAGNETICFOLLOW\nTEI_MOVRELATIVESTATIC\nTEI_SHOWLMP2\nTEI_SHOWLMP3\nTEI_STATCOUNT\nTEI_STATICFX\nTEI_STEPSIZE\nTEI_SUN\nTEI_SVSKY\nTEI_TE_ALIENEXPLOSION\nTEI_TE_CUSTOMFX\nTEI_TE_EXPLOSIONSMALL\nTEI_TE_EXPLOSIONSMALL2\nTEI_TE_EXPLOSIONSMALL3\nTEI_TE_IMPLOSIONFX\nTEI_TE_LIGHTNING4\nTEI_TE_LIGHTNINGX\nTEI_TE_PARTICLEBLAST\nTEI_TE_STATICFOG\nTEI_TE_TRAILS\nTEI_THEMEFOLDER\nTEI_TPAKLOAD\nTEI_ZONEFOGS\nTENEBRAE_GFX_DLIGHTS\nTIMESCALE\nTQ_DLIGHTS\nTQ_PLASMA\nTQ_RAILTRAIL\nTQ_RAIN\nTQ_SLOWMO\nTQ_SNOW\nTW_SV_STEPCONTROLS\nUDC_EXTEFFECT\nZQ_MOVETYPE_NOCLIP\nZQ_MOVETYPE_FLY\nZQ_MOVETYPE_NONE\nZQ_QC_CHECKBUILTIN\nZQ_QC_PARTICLE\nZQ_QC_STRINGS"
  },
  {
    "path": "quakec/autoext/src/autoext.qc",
    "content": "float resultfile;\nvoid(string s) putresultstring =\n{\n\tfputs(resultfile, s);\n};\n\nvoid(string s) FoundExtension =\n{\n\tfloat descfile;\n\tstring descfilename;\n\n\tputresultstring(\"//\");putresultstring(s);putresultstring(\"\\r\\n\");\n\n\tdescfilename = strcat(\"ext/\", s, \".qc\");\n\tdescfile = fopen(descfilename, 0);\n\n\tif (descfile>=0)\n\t{\n\t\tfor(;;)\n\t\t{\n\t\t\ts = fgets(descfile);\n\t\t\tif (s) {} else break;\n\n\t\t\tputresultstring(s);\n\t\t\tputresultstring(\"\\r\\n\");\n\t\t}\n\t\tfclose(descfile);\n\t\tputresultstring(\"\\r\\n\");\n\t}\n\telse\n\t{\n\t\tputresultstring(\"//FIXME: AutoExt: No information\\r\\n\");\n\t\tputresultstring(\"\\r\\n\");\t//and a blank line\n\t}\n};\n\nvoid(string inname) decompose =\n{\n\tfloat in;\n\tfloat out;\n\tfloat len;\n\tstring s;\n\tstring outname;\n\n\tin = fopen(inname, 0);\n\tif (in < 0)\n\t{\n\t\treturn;\n\t}\n\n\tfor(;;)\n\t{\n\t\ts = fgets(in);\n\t\tif (s) {} else break;\n\n\t\tif (s == \"\")\t//skip extra whitespace\n\t\t\tcontinue;\n\n\t\tlen = strlen(s);\n\t\ts = substring(s, 2, len-2);\n\t\toutname = strcat(\"ext/\", s, \".qc\");\n\t\tout = fopen(outname, 2);\n\t\twhile((s = fgets(in)) != \"\")\n\t\t{\n\t\t\tfputs(out, s);\n\t\t\tfputs(out, \"\\r\\n\");\n\t\t}\n\t\tfclose(out);\n\t}\n\n\tfclose(in);\n};\n\nvoid() worldspawn =\n{\n\tfloat extlist;\n\tstring s;\n\n\tif (!cvar(\"pr_checkextension\"))\n\t\terror(\"Engine doesn't support any extensions\\n\");\n\n\tif (!checkextension(\"FRIK_FILE\"))\n\t\terror(\"Unable to continue without FRIK_FILE\\n\");\n\n\n//\tdecompose(\"lists/betwix.qc\");\n//\tdecompose(\"lists/dpextensions.qc\");\n\n\textlist = fopen(\"lists/extlist.txt\", 0);\n\tresultfile = fopen(\"results.qc\", 2);\n\n\tfor(;;)\n\t{\n\t\ts = fgets(extlist);\n\t\tif (s) {} else break;\n\n\t\tif (checkextension(s))\n\t\t\tFoundExtension(s);\n\t}\n\n\tfclose(resultfile);\n\tfclose(extlist);\n\n//\tdprint(\"\\n\\n\\n\\n\\n\\n\\n\\n\");\n\terror(\"autoext compleate\\n\");\n};"
  },
  {
    "path": "quakec/autoext/src/builtins.qc",
    "content": "void(string e) error\t\t\t\t= #10;\nfloat(string s) cvar = #45;\nfloat(string s) checkextension = #99;\n\n\n//FRIK_FILE\n//IDEA: FrikaC\nfloat  (string s)                            stof      = #81;  // get numerical value from a string\nfloat  (string filename, float mode)         fopen     = #110; // opens a file inside quake/gamedir/ or quake/gamedir/data/ (mode is FILE_READ, FILE_APPEND, or FILE_WRITE), returns fhandle >= 0 if successful, or fhandle < 0 if unable to open file for any reason\nvoid   (float fhandle)                       fclose    = #111; // closes a file\nstring (float fhandle)                       fgets     = #112; // reads a line of text from the file and returns as a tempstring\nvoid   (float fhandle, string s)             fputs     = #113; // writes a line of text to the end of the file\nfloat  (string s)                            strlen    = #114; // returns how many characters are in a string\nstring (string s1, string s2)                strcat    = #115; // concatenates two strings (for example \"abc\", \"def\" would return \"abcdef\") and returns as a tempstring\nstring (string s, float start, float length) substring = #116; // returns a section of a string as a tempstring\nvector (string s)                            stov      = #117; // returns vector value from a string\nstring (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)\nvoid   (string s)                            strunzone = #119; // removes a copy of a string from the string zone (you can not use that string again or it may crash!!!)\nfloat FILE_READ   = 0;\nfloat FILE_APPEND = 1;\nfloat FILE_WRITE  = 2;"
  },
  {
    "path": "quakec/autoext/src/defs.qc",
    "content": "\n/*\n==============================================================================\n\n\t\t\tSOURCE FOR GLOBALVARS_T C STRUCTURE\n\n==============================================================================\n*/\n\n//\n// system globals\n//\nentity\t\tself;\nentity\t\tother;\nentity\t\tworld;\nfloat\t\ttime;\nfloat\t\tframetime;\n\nfloat\t\tforce_retouch;\t\t// force all entities to touch triggers\n\t\t\t\t\t\t\t\t// next frame.  this is needed because\n\t\t\t\t\t\t\t\t// non-moving things don't normally scan\n\t\t\t\t\t\t\t\t// for triggers, and when a trigger is\n\t\t\t\t\t\t\t\t// created (like a teleport trigger), it\n\t\t\t\t\t\t\t\t// needs to catch everything.\n\t\t\t\t\t\t\t\t// decremented each frame, so set to 2\n\t\t\t\t\t\t\t\t// to guarantee everything is touched\nstring\t\tmapname;\n\nfloat\t\tdeathmatch;\nfloat\t\tcoop;\nfloat\t\tteamplay;\n\nfloat\t\tserverflags;\t\t// propagated from level to level, used to\n\t\t\t\t\t\t\t\t// keep track of completed episodes\n\nfloat\t\ttotal_secrets;\nfloat\t\ttotal_monsters;\n\nfloat\t\tfound_secrets;\t\t// number of secrets found\nfloat\t\tkilled_monsters;\t// number of monsters killed\n\n\n// spawnparms are used to encode information about clients across server\n// level changes\nfloat\t\tparm1, parm2, parm3, parm4, parm5, parm6, parm7, parm8, parm9, parm10, parm11, parm12, parm13, parm14, parm15, parm16;\n\n//\n// global variables set by built in functions\n//\t\nvector\t\tv_forward, v_up, v_right;\t// set by makevectors()\n\t\n// set by traceline / tracebox\nfloat\t\ttrace_allsolid;\nfloat\t\ttrace_startsolid;\nfloat\t\ttrace_fraction;\nvector\t\ttrace_endpos;\nvector\t\ttrace_plane_normal;\nfloat\t\ttrace_plane_dist;\nentity\t\ttrace_ent;\nfloat\t\ttrace_inopen;\nfloat\t\ttrace_inwater;\n\nentity\t\tmsg_entity;\t\t\t\t// destination of single entity writes\n\n//\n// required prog functions\n//\nvoid() \t\tmain;\t\t\t\t\t\t// only for testing\n\nvoid()\t\tStartFrame;\n\nvoid() \t\tPlayerPreThink;\nvoid() \t\tPlayerPostThink;\n\nvoid()\t\tClientKill;\nvoid()\t\tClientConnect;\nvoid() \t\tPutClientInServer;\t\t// call after setting the parm1... parms\nvoid()\t\tClientDisconnect;\n\nvoid()\t\tSetNewParms;\t\t\t// called when a client first connects to\n\t\t\t\t\t\t\t\t\t// a server. sets parms so they can be\n\t\t\t\t\t\t\t\t\t// saved off for restarts\n\nvoid()\t\tSetChangeParms;\t\t\t// call to set parms for self so they can\n\t\t\t\t\t\t\t\t\t// be saved for a level transition\n\n\n//================================================\nvoid\t\tend_sys_globals;\t\t// flag for structure dumping\n//================================================\n\n/*\n==============================================================================\n\n\t\t\tSOURCE FOR ENTVARS_T C STRUCTURE\n\n==============================================================================\n*/\n\n//\n// system fields (*** = do not set in prog code, maintained by C code)\n//\n.float\t\tmodelindex;\t\t// *** model index in the precached list\n.vector\t\tabsmin, absmax;\t// *** origin + mins / maxs\n\n.float\t\tltime;\t\t\t// local time for entity\n.float\t\tmovetype;\n.float\t\tsolid;\n\n.vector\t\torigin;\t\t\t// ***\n.vector\t\toldorigin;\t\t// ***\n.vector\t\tvelocity;\n.vector\t\tangles;\n.vector\t\tavelocity;\n\n.vector\t\tpunchangle;\t\t// temp angle adjust from damage or recoil\n\n.string\t\tclassname;\t\t// spawn function\n.string\t\tmodel;\n.float\t\tframe;\n.float\t\tskin;\n.float\t\teffects;\n\n.vector\t\tmins, maxs;\t\t// bounding box extents reletive to origin\n.vector\t\tsize;\t\t\t// maxs - mins\n\n.void()\t\ttouch;\n.void()\t\tuse;\n.void()\t\tthink;\n.void()\t\tblocked;\t\t// for doors or plats, called when can't push other\n\n.float\t\tnextthink;\n.entity\t\tgroundentity;\n\n// stats\n.float\t\thealth;\n.float\t\tfrags;\n.float\t\tweapon;\t\t\t// one of the IT_SHOTGUN, etc flags\n.string\t\tweaponmodel;\n.float\t\tweaponframe;\n.float\t\tcurrentammo;\n.float\t\tammo_shells, ammo_nails, ammo_rockets, ammo_cells;\n\n.float\t\titems;\t\t\t// bit flags\n\n.float\t\ttakedamage;\n.entity\t\tchain;\n.float\t\tdeadflag;\n\n.vector\t\tview_ofs;\t\t\t// add to origin to get eye point\n\n\n.float\t\tbutton0;\t\t// fire\n.float\t\tbutton1;\t\t// use\n.float\t\tbutton2;\t\t// jump\n\n.float\t\timpulse;\t\t// weapon changes\n\n.float\t\tfixangle;\n.vector\t\tv_angle;\t\t// view / targeting angle for players\n.float\t\tidealpitch;\t\t// calculated pitch angle for lookup up slopes\n\n\n.string\t\tnetname;\n\n.entity \tenemy;\n\n.float\t\tflags;\n\n.float\t\tcolormap;\n.float\t\tteam;\n\n.float\t\tmax_health;\t\t// players maximum health is stored here\n\n.float\t\tteleport_time;\t// don't back up\n\n.float\t\tarmortype;\t\t// save this fraction of incoming damage\n.float\t\tarmorvalue;\n\n.float\t\twaterlevel;\t\t// 0 = not in, 1 = feet, 2 = wast, 3 = eyes\n.float\t\twatertype;\t\t// a contents value\n\n.float\t\tideal_yaw;\n.float\t\tyaw_speed;\n\n.entity\t\taiment;\n\n.entity \tgoalentity;\t\t// a movetarget or an enemy\n\n.float\t\tspawnflags;\n\n.string\t\ttarget;\n.string\t\ttargetname;\n\n// damage is accumulated through a frame. and sent as one single\n// message, so the super shotgun doesn't generate huge messages\n.float\t\tdmg_take;\n.float\t\tdmg_save;\n.entity\t\tdmg_inflictor;\n\n.entity\t\towner;\t\t// who launched a missile\n.vector\t\tmovedir;\t// mostly for doors, but also used for waterjump\n\n.string\t\tmessage;\t\t// trigger messages\n\n.float\t\tsounds;\t\t// either a cd track number or sound number\n\n.string\t\tnoise, noise1, noise2, noise3;\t// contains names of wavs to play\n\n//================================================\nvoid\t\tend_sys_fields;\t\t\t// flag for structure dumping\n//================================================\n\n"
  },
  {
    "path": "quakec/autoext/src/progs.src",
    "content": "../progs.dat\n\ndefs.qc\nbuiltins.qc\n\nautoext.qc"
  },
  {
    "path": "quakec/basemod/ai.qc",
    "content": "void() t_movetarget;\n/*\n\n.enemy\nWill be world if not currently angry at anyone.\n\n.movetarget\nThe next path spot to walk toward.  If .enemy, ignore .movetarget.\nWhen an enemy is killed, the monster will try to return to it's path.\n\n.huntt_ime\nSet to time + something when the player is in sight, but movement straight for\nhim is blocked.  This causes the monster to use wall following code for\nmovement direction instead of sighting on the player.\n\n.ideal_yaw\nA yaw angle of the intended direction, which will be turned towards at up\nto 45 deg / state.  If the enemy is in view and hunt_time is not active,\nthis will be the exact line towards the enemy.\n\n.pausetime\nA monster will leave it's stand state and head towards it's .movetarget when\ntime > .pausetime.\n\nwalkmove(angle, speed) primitive is all or nothing\n*/\n\n//\n// when a monster becomes angry at a player, that monster will be used\n// as the sight target the next frame so that monsters near that one\n// will wake up even if they wouldn't have noticed the player\n//\nentity\tsight_entity;\nfloat\tsight_entity_time;\n\nfloat(float v) anglemod =\n{\n\twhile (v >= 360)\n\t\tv = v - 360;\n\twhile (v < 0)\n\t\tv = v + 360;\n\treturn v;\n};\n\n/*\n==============================================================================\n\nMOVETARGET CODE\n\nThe angle of the movetarget effects standing and bowing direction, but has no effect on movement, which allways heads to the next target.\n\ntargetname\nmust be present.  The name of this movetarget.\n\ntarget\nthe next spot to move to.  If not present, stop here for good.\n\npausetime\nThe number of seconds to spend standing or bowing for path_stand or path_bow\n\n==============================================================================\n*/\n\n\nvoid() movetarget_f =\n{\n\tif (!self.targetname)\n\t\tobjerror (\"monster_movetarget: no targetname\");\n\t\t\n\tself.solid = SOLID_TRIGGER;\n\tself.touch = t_movetarget;\n\tsetsize (self, '-8 -8 -8', '8 8 8');\n\t\n};\n\n/*QUAKED path_corner (0.5 0.3 0) (-8 -8 -8) (8 8 8)\nMonsters will continue walking towards the next target corner.\n*/\nvoid() path_corner =\n{\n\tmovetarget_f ();\n};\n\n\n/*\n=============\nt_movetarget\n\nSomething has bumped into a movetarget.  If it is a monster\nmoving towards it, change the next destination and continue.\n==============\n*/\nvoid() t_movetarget =\n{\nlocal entity\ttemp;\n\n\tif (other.movetarget != self)\n\t\treturn;\n\t\n\tif (other.enemy)\n\t\treturn;\t\t// fighting, not following a path\n\n\ttemp = self;\n\tself = other;\n\tother = temp;\n\n\tif (self.classname == \"monster_ogre\")\n\t\tsound (self, CHAN_VOICE, \"ogre/ogdrag.wav\", 1, ATTN_IDLE);// play chainsaw drag sound\n\n//dprint (\"t_movetarget\\n\");\n\tself.goalentity = self.movetarget = find (world, targetname, other.target);\n\tself.ideal_yaw = vectoyaw(self.goalentity.origin - self.origin);\n\tif (!self.movetarget)\n\t{\n\t\tself.pausetime = time + 999999;\n\t\tself.th_stand ();\n\t\treturn;\n\t}\n};\n\n\n\n//============================================================================\n\n/*\n=============\nrange\n\nreturns the range catagorization of an entity reletive to self\n0\tmelee range, will become hostile even if back is turned\n1\tvisibility and infront, or visibility and show hostile\n2\tinfront and show hostile\n3\tonly triggered by damage\n=============\n*/\nfloat(entity targ) range =\n{\n\tlocal vector\tspot1, spot2;\n\tlocal float\t\tr;\t\n\n\tspot1 = self.origin + self.view_ofs;\n\tspot2 = targ.origin + targ.view_ofs;\n\t\n\tr = vlen (spot1 - spot2);\n\tif (r < 120)\n\t\treturn RANGE_MELEE;\n\tif (r < 500)\n\t\treturn RANGE_NEAR;\n\tif (r < 1000)\n\t\treturn RANGE_MID;\n\treturn RANGE_FAR;\n};\n\n/*\n=============\nvisible\n\nreturns 1 if the entity is visible to self, even if not infront ()\n=============\n*/\nBOOL (entity targ) visible =\n{\n\tlocal vector\tspot1, spot2;\n\t\n\tspot1 = self.origin + self.view_ofs;\n\tspot2 = targ.origin + targ.view_ofs;\n\ttraceline (spot1, spot2, TRUE, self);\t// see through other monsters\n\t\n\tif (trace_inopen && trace_inwater)\n\t\treturn FALSE;\t\t\t// sight line crossed contents\n\n\tif (trace_fraction == 1)\n\t\treturn TRUE;\n\treturn FALSE;\n};\n\n\n/*\n=============\ninfront\n\nreturns 1 if the entity is in front (in sight) of self\n=============\n*/\nBOOL(entity targ) infront =\n{\n\tlocal vector\tvec;\n\tlocal float\t\tdot;\n\t\n\tmakevectors (self.angles);\n\tvec = normalize (targ.origin - self.origin);\n\tdot = vec * v_forward;\n\t\n\tif ( dot > 0.3)\n\t{\n\t\treturn TRUE;\n\t}\n\treturn FALSE;\n};\n\n\n//============================================================================\n\n/*\n===========\nChangeYaw\n\nTurns towards self.ideal_yaw at self.yaw_speed\nSets the global variable current_yaw\nCalled every 0.1 sec by monsters\n============\n*/\n/*\nvoid() ChangeYaw =\n{\n\tlocal float\t\tideal, move, current_yaw;\n\n//current_yaw = self.ideal_yaw;\n// mod down the current angle\n\tcurrent_yaw = anglemod( self.angles_y );\n\tideal = self.ideal_yaw;\n\t\n\tif (current_yaw == ideal)\n\t\treturn;\n\t\n\tmove = ideal - current_yaw;\n\tif (ideal > current_yaw)\n\t{\n\t\tif (move > 180)\n\t\t\tmove = move - 360;\n\t}\n\telse\n\t{\n\t\tif (move < -180)\n\t\t\tmove = move + 360;\n\t}\n\t\t\n\tif (move > 0)\n\t{\n\t\tif (move > self.yaw_speed)\n\t\t\tmove = self.yaw_speed;\n\t}\n\telse\n\t{\n\t\tif (move < 0-self.yaw_speed )\n\t\t\tmove = 0-self.yaw_speed;\n\t}\n\n\tcurrent_yaw = anglemod (current_yaw + move);\n\n\tself.angles_y = current_yaw;\n};\n*/\n\n\n//============================================================================\n\nvoid() HuntTarget =\n{\n\tself.goalentity = self.enemy;\n\tself.think = self.th_run;\n\tself.ideal_yaw = vectoyaw(self.enemy.origin - self.origin);\n\tself.nextthink = time + 0.1;\n\tSUB_AttackFinished (1);\t// wait a while before first attack\n};\n\nvoid() SightSound =\n{\n\tlocal float\trsnd;\n\tlocal string s;\n\n\tswitch (self.classname)\n\t{\n\tcase \"monster_ogre\":\n\t\ts = \"ogre/ogwake.wav\";\n\t\tbreak;\n\tcase \"monster_knight\":\n\t\ts = \"knight/ksight.wav\";\n\t\tbreak;\n\tcase \"monster_shambler\":\n\t\ts = \"shambler/ssight.wav\";\n\t\tbreak;\n\tcase \"monster_demon1\":\n\t\ts = \"demon/sight2.wav\";\n\t\tbreak;\n\tcase \"monster_wizard\":\n\t\ts = \"wizard/wsight.wav\";\n\t\tbreak;\n\tcase \"monster_zombie\":\n\t\ts = \"zombie/z_idle.wav\";\n\t\tbreak;\n\tcase \"monster_dog\":\n\t\ts = \"dog/dsight.wav\";\n\t\tbreak;\n\tcase \"monster_hell_knight\":\n\t\ts = \"hknight/sight1.wav\";\n\t\tbreak;\n\tcase \"monster_tarbaby\":\n\t\ts = \"blob/sight1.wav\";\n\t\tbreak;\n\tcase \"monster_enforcer\":\n\t\trsnd = random();\n\t\tif (rsnd < 0.25)\n\t\t\ts = \"enforcer/sight1.wav\";\n\t\telse if (rsnd < 0.5)\n\t\t\ts = \"enforcer/sight2.wav\";\n\t\telse if (rsnd < 0.75)\n\t\t\ts = \"enforcer/sight3.wav\";\n\t\telse\n\t\t\ts = \"enforcer/sight4.wav\";\n\t\tbreak;\n\tcase \"monster_shalrath\":\n\t\ts = \"shalrath/sight.wav\";\n\t\tbreak;\n\tdefault:\n\t\ts = \"soldier/sight1.wav\";\n\t}\n\n\tsound (self, CHAN_VOICE, s, 1, ATTN_NORM);\n};\n\nvoid() FoundTarget =\n{\n\tif (self.enemy.classname == \"player\")\n\t{\t// let other monsters see this monster for a while\n\t\tsight_entity = self;\n\t\tsight_entity_time = time;\n\t}\n\t\n\tself.show_hostile = time + 1;\t\t// wake up other monsters\n\n\tSightSound ();\n\tHuntTarget ();\n};\n\n/*\n===========\nFindTarget\n\nSelf is currently not attacking anything, so try to find a target\n\nReturns TRUE if an enemy was sighted\n\nWhen a player fires a missile, the point of impact becomes a fakeplayer so\nthat monsters that see the impact will respond as if they had seen the\nplayer.\n\nTo avoid spending too much time, only a single client (or fakeclient) is\nchecked each frame.  This means multi player games will have slightly\nslower noticing monsters.\n============\n*/\nBOOL() FindTarget =\n{\n\tlocal entity\tclient;\n\tlocal float\t\tr;\n\n// if the first spawnflag bit is set, the monster will only wake up on\n// really seeing the player, not another monster getting angry\n\n// spawnflags & 3 is a big hack, because zombie crucified used the first\n// spawn flag prior to the ambush flag, and I forgot about it, so the second\n// spawn flag works as well\n\tif (sight_entity_time >= time - 0.1 && !(self.spawnflags & 3) )\n\t{\n\t\tclient = sight_entity;\n\t\tif (client.enemy == self.enemy)\n\t\t\treturn TRUE;\n\t}\n\telse\n\t{\n\t\tclient = checkclient ();\n\t\tif (!client)\n\t\t\treturn FALSE;\t// current check entity isn't in PVS\n\t}\n\n\tif (client == self.enemy)\n\t\treturn FALSE;\n\n\tif (client.flags & FL_NOTARGET)\n\t\treturn FALSE;\n\tif (client.items & IT_INVISIBILITY)\n\t\treturn FALSE;\n\n\tr = range (client);\n\tif (r == RANGE_FAR)\n\t\treturn FALSE;\n\t\t\n\tif (!visible (client))\n\t\treturn FALSE;\n\n\tif (r == RANGE_NEAR)\n\t{\n\t\tif (client.show_hostile < time && !infront (client))\n\t\t\treturn FALSE;\n\t}\n\telse if (r == RANGE_MID)\n\t{\n\t\tif ( /* client.show_hostile < time || */ !infront (client))\n\t\t\treturn FALSE;\n\t}\n\t\n//\n// got one\n//\n\tself.enemy = client;\n\tif (self.enemy.classname != \"player\")\n\t{\n\t\tself.enemy = self.enemy.enemy;\n\t\tif (self.enemy.classname != \"player\")\n\t\t{\n\t\t\tself.enemy = world;\n\t\t\treturn FALSE;\n\t\t}\n\t}\n\t\n\tFoundTarget ();\n\n\treturn TRUE;\n};\n\n\n//=============================================================================\n\nvoid(float dist) ai_forward =\n{\n\twalkmove (self.angles_y, dist);\n};\n\nvoid(float dist) ai_back =\n{\n\twalkmove ( (self.angles_y+180), dist);\n};\n\n\n/*\n=============\nai_pain\n\nstagger back a bit\n=============\n*/\nvoid(float dist) ai_pain =\n{\n\tai_back (dist);\n/*\n\tlocal float\taway;\n\t\n\taway = anglemod (vectoyaw (self.origin - self.enemy.origin) \n\t+ 180*(random()- 0.5) );\n\t\n\twalkmove (away, dist);\n*/\n};\n\n/*\n=============\nai_painforward\n\nstagger back a bit\n=============\n*/\nvoid(float dist) ai_painforward =\n{\n\twalkmove (self.ideal_yaw, dist);\n};\n\n/*\n=============\nai_walk\n\nThe monster is walking it's beat\n=============\n*/\nvoid(float dist) ai_walk =\n{\n//\tmovedist = dist;\n\t\n\t/*\n\tThere are no dragons!\n\tif (self.classname == \"monster_dragon\")\n\t{\n\t\tmovetogoal (dist);\n\t\treturn;\n\t}\n\t*/\n\n\t// check for noticing a player\n\tif (FindTarget ())\n\t\treturn;\n\n\tmovetogoal (dist);\n};\n\n\n/*\n=============\nai_stand\n\nThe monster is staying in one place for a while, with slight angle turns\n=============\n*/\nvoid() ai_stand =\n{\n\tif (FindTarget ())\n\t\treturn;\n\t\n\tif (time > self.pausetime)\n\t{\n\t\tself.th_walk ();\n\t\treturn;\n\t}\n\t\n// change angle slightly\n\n};\n\n/*\n=============\nai_turn\n\ndon't move, but turn towards ideal_yaw\n=============\n*/\nvoid() ai_turn =\n{\n\tif (FindTarget ())\n\t\treturn;\n\t\n\tChangeYaw ();\n};\n\n//=============================================================================\n\n/*\n=============\nChooseTurn\n=============\n*/\n/*\nvoid(vector dest3) ChooseTurn =\n{\n\tlocal vector\tdir, newdir;\n\t\n\tdir = self.origin - dest3;\n\n\tnewdir_x = trace_plane_normal_y;\n\tnewdir_y = 0 - trace_plane_normal_x;\n\tnewdir_z = 0;\n\t\n\tif (dir * newdir > 0)\n\t{\n\t\tdir_x = 0 - trace_plane_normal_y;\n\t\tdir_y = trace_plane_normal_x;\n\t}\n\telse\n\t{\n\t\tdir_x = trace_plane_normal_y;\n\t\tdir_y = 0 - trace_plane_normal_x;\n\t}\n\n\tdir_z = 0;\n\tself.ideal_yaw = vectoyaw(dir);\t\n};\n*/\n\n/*\n============\nFacingIdeal\n\n============\n*/\nBOOL() FacingIdeal =\n{\n\tlocal\tfloat\tdelta;\n\t\n\tdelta = anglemod(self.angles_y - self.ideal_yaw);\n\tif (delta > 45 && delta < 315)\n\t\treturn FALSE;\n\treturn TRUE;\n};\n\n\n//=============================================================================\nBOOL(float enemy_range) DogCheckAttack;\nBOOL(float enemy_range) WizardCheckAttack;\nBOOL(float enemy_range) DemonCheckAttack;\n\nBOOL(float enemy_range) CheckAnyAttack =\n{\n\n\tswitch (self.classname)\n\t{\n\tcase \"monster_army\":\n\t\treturn SoldierCheckAttack (enemy_range);\n\tcase \"monster_ogre\":\n\t\treturn OgreCheckAttack (enemy_range);\n\tcase \"monster_shambler\":\n\t\treturn ShamCheckAttack (enemy_range);\n\tcase \"monster_demon1\":\n\t\treturn DemonCheckAttack (enemy_range);\n\tcase \"monster_dog\":\n\t\treturn DogCheckAttack (enemy_range);\n\tcase \"monster_wizard\":\n\t\treturn WizardCheckAttack (enemy_range);\n\t}\n\n\treturn CheckAttack (enemy_range);\n};\n\n\n/*\n=============\nai_run_melee\n\nTurn and close until within an angle to launch a melee attack\n=============\n*/\nvoid(float enemy_yaw) ai_run_melee =\n{\n\tself.ideal_yaw = enemy_yaw;\n\tChangeYaw ();\n\n\tif (FacingIdeal())\n\t{\n\t\tself.th_melee ();\n\t\tself.attack_state = AS_STRAIGHT;\n\t}\n};\n\n\n/*\n=============\nai_run_missile\n\nTurn in place until within an angle to launch a missile attack\n=============\n*/\nvoid(float enemy_yaw) ai_run_missile =\n{\n\tself.ideal_yaw = enemy_yaw;\n\tChangeYaw ();\n\tif (FacingIdeal())\n\t{\n\t\tself.th_missile ();\n\t\tself.attack_state = AS_STRAIGHT;\n\t}\n};\n\n\n/*\n=============\nai_run_slide\n\nStrafe sideways, but stay at aproximately the same range\n=============\n*/\nvoid(float enemy_yaw, float movedist) ai_run_slide =\n{\n\tlocal float\tofs;\n\t\n\tself.ideal_yaw = enemy_yaw;\n\tChangeYaw ();\n\tif (self.lefty)\n\t\tofs = 90;\n\telse\n\t\tofs = -90;\n\t\n\tif (walkmove (self.ideal_yaw + ofs, movedist))\n\t\treturn;\n\t\t\n\tself.lefty = 1 - self.lefty;\n\t\n\twalkmove (self.ideal_yaw - ofs, movedist);\n};\n\n/*\n=============\nai_run\n\nThe monster has an enemy it is trying to kill\n=============\n*/\nvoid(float dist) ai_run =\n{\n\tlocal float enemy_vis, enemy_yaw;\n\t\n// see if the enemy is dead\n\tif (self.enemy.health <= 0)\n\t{\n\t\tself.enemy = world;\n\t// FIXME: look all around for other targets\n\t\tif (self.oldenemy.health > 0)\n\t\t{\n\t\t\tself.enemy = self.oldenemy;\n\t\t\tHuntTarget ();\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (self.movetarget)\n\t\t\t\tself.th_walk ();\n\t\t\telse\n\t\t\t\tself.th_stand ();\n\t\t\treturn;\n\t\t}\n\t}\n\n\tself.show_hostile = time + 1;\t\t// wake up other monsters\n\n// check knowledge of enemy\n\tenemy_vis = visible(self.enemy);\n\tif (enemy_vis)\n\t\tself.search_time = time + 5;\n\n// look for other coop players\n\tif (coop && self.search_time < time)\n\t{\n\t\tif (FindTarget ())\n\t\t\treturn;\n\t}\n\n\tenemy_yaw = vectoyaw(self.enemy.origin - self.origin);\n\t\n\tif (self.attack_state == AS_MISSILE)\n\t{\n\t\tai_run_missile (enemy_yaw);\n\t\treturn;\n\t}\n\tif (self.attack_state == AS_MELEE)\n\t{\n\t\tai_run_melee (enemy_yaw);\n\t\treturn;\n\t}\n\n\tif (!enemy_vis)\n\t\treturn;\n\n\tif (CheckAnyAttack (range(self.enemy)))\n\t\treturn;\t\t\t\t\t// beginning an attack\n\t\n\tif (self.attack_state == AS_SLIDING)\n\t{\n\t\tai_run_slide (enemy_yaw, dist);\n\t\treturn;\n\t}\n\t\t\n// head straight in\n\tmovetogoal (dist);\t\t// done in C code...\n};\n\n\u0000"
  },
  {
    "path": "quakec/basemod/basemod.txt",
    "content": "Incomplete -\nTriggered kills (explobox, etc)\nQ2/Q3 ents\nHL ents\n\nDone -\nCleanup warnings\nFix QW bugs\nHeal decay (over maxhealth)\nINTEGER mod\nNailgun frame seperation\nBody queue change\nSuicide timer limit\nSuicide backpack/quad/ring drop\nCondense bprint, sprint, centerprints\nte_explode sprite\nNQ/QW cross compatibility\nTrack oldbutton presses/weaponstate\nGeneric projectile spawning\nUsage of ammo_shells, etc as display\n\nIn progress -\nItem fixing for SP\nAdd monsters back\n\nTodo -\nFix ammo_shells, etc for monsters/backpacks\nSamelevel 4 (exit acts as a spawnpoint teleporter)\nEffects/decal system\nFix weird deathmatch modes, cvar checking\nRogue/hipnotic weapons/monsters/hud stuffs\nH2 ents\nCoop friendly finale\nAdd in shambler resistance in a better way\n\nStray Ideas -\nAdvanced heal decay (only decay health that the megahealth added?)\nClean up backpack pickup prints?\nDecal system based on visibility from players?\nCSQC?\nDon't use newmis/spawn projectiles in front?\nsv_gravity change?\n"
  },
  {
    "path": "quakec/basemod/boss.qc",
    "content": "/*\n==============================================================================\n\nBOSS-ONE\n\n==============================================================================\n*/\n$cd id1/models/boss1\n$origin 0 0 -15\n$base base\n$skin skin\n$scale 5\n\n$frame rise1 rise2 rise3 rise4 rise5 rise6 rise7 rise8 rise9 rise10\n$frame rise11 rise12 rise13 rise14 rise15 rise16 rise17 \n\n$frame walk1 walk2 walk3 walk4 walk5 walk6 walk7 walk8\n$frame walk9 walk10 walk11 walk12 walk13 walk14 walk15\n$frame walk16 walk17 walk18 walk19 walk20 walk21 walk22\n$frame walk23 walk24 walk25 walk26 walk27 walk28 walk29 walk30 walk31\n\n$frame death1 death2 death3 death4 death5 death6 death7 death8 death9\n\n$frame attack1 attack2 attack3 attack4 attack5 attack6 attack7 attack8\n$frame attack9 attack10 attack11 attack12 attack13 attack14 attack15\n$frame attack16 attack17 attack18 attack19 attack20 attack21 attack22\n$frame attack23\n\n$frame shocka1 shocka2 shocka3 shocka4 shocka5 shocka6 shocka7 shocka8\n$frame shocka9 shocka10 \n\n$frame shockb1 shockb2 shockb3 shockb4 shockb5 shockb6\n\n$frame shockc1 shockc2 shockc3 shockc4 shockc5 shockc6 shockc7 shockc8 \n$frame shockc9 shockc10\n\n\nvoid(vector p) boss_missile;\n\nvoid() boss_face =\n{\n\t\n// go for another player if multi player\n\tif (self.enemy.health <= 0 || random() < 0.02)\n\t{\n\t\tself.enemy = find(self.enemy, classname, \"player\");\n\t\tif (!self.enemy)\n\t\t\tself.enemy = find(self.enemy, classname, \"player\");\n\t}\n\tai_face();\n};\n\nvoid() boss_rise1\t=[\t$rise1, boss_rise2 ] {\nsound (self, CHAN_WEAPON, \"boss1/out1.wav\", 1, ATTN_NORM);\n};\nvoid() boss_rise2\t=[\t$rise2, boss_rise3 ] {\nsound (self, CHAN_VOICE, \"boss1/sight1.wav\", 1, ATTN_NORM);\n};\nvoid() boss_rise3\t=[\t$rise3, boss_rise4 ] {};\nvoid() boss_rise4\t=[\t$rise4, boss_rise5 ] {};\nvoid() boss_rise5\t=[\t$rise5, boss_rise6 ] {};\nvoid() boss_rise6\t=[\t$rise6, boss_rise7 ] {};\nvoid() boss_rise7\t=[\t$rise7, boss_rise8 ] {};\nvoid() boss_rise8\t=[\t$rise8, boss_rise9 ] {};\nvoid() boss_rise9\t=[\t$rise9, boss_rise10 ] {};\nvoid() boss_rise10\t=[\t$rise10, boss_rise11 ] {};\nvoid() boss_rise11\t=[\t$rise11, boss_rise12 ] {};\nvoid() boss_rise12\t=[\t$rise12, boss_rise13 ] {};\nvoid() boss_rise13\t=[\t$rise13, boss_rise14 ] {};\nvoid() boss_rise14\t=[\t$rise14, boss_rise15 ] {};\nvoid() boss_rise15\t=[\t$rise15, boss_rise16 ] {};\nvoid() boss_rise16\t=[\t$rise16, boss_rise17 ] {};\nvoid() boss_rise17\t=[\t$rise17, boss_missile1 ] {};\n\nvoid() boss_idle1\t=[\t$walk1, boss_idle2 ]\n{\n// look for other players\n};\nvoid() boss_idle2\t=[\t$walk2, boss_idle3 ] {boss_face();};\nvoid() boss_idle3\t=[\t$walk3, boss_idle4 ] {boss_face();};\nvoid() boss_idle4\t=[\t$walk4, boss_idle5 ] {boss_face();};\nvoid() boss_idle5\t=[\t$walk5, boss_idle6 ] {boss_face();};\nvoid() boss_idle6\t=[\t$walk6, boss_idle7 ] {boss_face();};\nvoid() boss_idle7\t=[\t$walk7, boss_idle8 ] {boss_face();};\nvoid() boss_idle8\t=[\t$walk8, boss_idle9 ] {boss_face();};\nvoid() boss_idle9\t=[\t$walk9, boss_idle10 ] {boss_face();};\nvoid() boss_idle10\t=[\t$walk10, boss_idle11 ] {boss_face();};\nvoid() boss_idle11\t=[\t$walk11, boss_idle12 ] {boss_face();};\nvoid() boss_idle12\t=[\t$walk12, boss_idle13 ] {boss_face();};\nvoid() boss_idle13\t=[\t$walk13, boss_idle14 ] {boss_face();};\nvoid() boss_idle14\t=[\t$walk14, boss_idle15 ] {boss_face();};\nvoid() boss_idle15\t=[\t$walk15, boss_idle16 ] {boss_face();};\nvoid() boss_idle16\t=[\t$walk16, boss_idle17 ] {boss_face();};\nvoid() boss_idle17\t=[\t$walk17, boss_idle18 ] {boss_face();};\nvoid() boss_idle18\t=[\t$walk18, boss_idle19 ] {boss_face();};\nvoid() boss_idle19\t=[\t$walk19, boss_idle20 ] {boss_face();};\nvoid() boss_idle20\t=[\t$walk20, boss_idle21 ] {boss_face();};\nvoid() boss_idle21\t=[\t$walk21, boss_idle22 ] {boss_face();};\nvoid() boss_idle22\t=[\t$walk22, boss_idle23 ] {boss_face();};\nvoid() boss_idle23\t=[\t$walk23, boss_idle24 ] {boss_face();};\nvoid() boss_idle24\t=[\t$walk24, boss_idle25 ] {boss_face();};\nvoid() boss_idle25\t=[\t$walk25, boss_idle26 ] {boss_face();};\nvoid() boss_idle26\t=[\t$walk26, boss_idle27 ] {boss_face();};\nvoid() boss_idle27\t=[\t$walk27, boss_idle28 ] {boss_face();};\nvoid() boss_idle28\t=[\t$walk28, boss_idle29 ] {boss_face();};\nvoid() boss_idle29\t=[\t$walk29, boss_idle30 ] {boss_face();};\nvoid() boss_idle30\t=[\t$walk30, boss_idle31 ] {boss_face();};\nvoid() boss_idle31\t=[\t$walk31, boss_idle1 ] {boss_face();};\n\nvoid() boss_missile1\t=[\t$attack1, boss_missile2 ] {boss_face();};\nvoid() boss_missile2\t=[\t$attack2, boss_missile3 ] {boss_face();};\nvoid() boss_missile3\t=[\t$attack3, boss_missile4 ] {boss_face();};\nvoid() boss_missile4\t=[\t$attack4, boss_missile5 ] {boss_face();};\nvoid() boss_missile5\t=[\t$attack5, boss_missile6 ] {boss_face();};\nvoid() boss_missile6\t=[\t$attack6, boss_missile7 ] {boss_face();};\nvoid() boss_missile7\t=[\t$attack7, boss_missile8 ] {boss_face();};\nvoid() boss_missile8\t=[\t$attack8, boss_missile9 ] {boss_face();};\nvoid() boss_missile9\t=[\t$attack9, boss_missile10 ] {boss_missile('100 100 200');};\nvoid() boss_missile10\t=[\t$attack10, boss_missile11 ] {boss_face();};\nvoid() boss_missile11\t=[\t$attack11, boss_missile12 ] {boss_face();};\nvoid() boss_missile12\t=[\t$attack12, boss_missile13 ] {boss_face();};\nvoid() boss_missile13\t=[\t$attack13, boss_missile14 ] {boss_face();};\nvoid() boss_missile14\t=[\t$attack14, boss_missile15 ] {boss_face();};\nvoid() boss_missile15\t=[\t$attack15, boss_missile16 ] {boss_face();};\nvoid() boss_missile16\t=[\t$attack16, boss_missile17 ] {boss_face();};\nvoid() boss_missile17\t=[\t$attack17, boss_missile18 ] {boss_face();};\nvoid() boss_missile18\t=[\t$attack18, boss_missile19 ] {boss_face();};\nvoid() boss_missile19\t=[\t$attack19, boss_missile20 ] {boss_face();};\nvoid() boss_missile20\t=[\t$attack20, boss_missile21 ] {boss_missile('100 -100 200');};\nvoid() boss_missile21\t=[\t$attack21, boss_missile22 ] {boss_face();};\nvoid() boss_missile22\t=[\t$attack22, boss_missile23 ] {boss_face();};\nvoid() boss_missile23\t=[\t$attack23, boss_missile1 ] {boss_face();};\n\nvoid() boss_shocka1 =[\t$shocka1, boss_shocka2 ] {};\nvoid() boss_shocka2 =[\t$shocka2, boss_shocka3 ] {};\nvoid() boss_shocka3 =[\t$shocka3, boss_shocka4 ] {};\nvoid() boss_shocka4 =[\t$shocka4, boss_shocka5 ] {};\nvoid() boss_shocka5 =[\t$shocka5, boss_shocka6 ] {};\nvoid() boss_shocka6 =[\t$shocka6, boss_shocka7 ] {};\nvoid() boss_shocka7 =[\t$shocka7, boss_shocka8 ] {};\nvoid() boss_shocka8 =[\t$shocka8, boss_shocka9 ] {};\nvoid() boss_shocka9 =[\t$shocka9, boss_shocka10 ] {};\nvoid() boss_shocka10 =[\t$shocka10, boss_missile1 ] {};\n\nvoid() boss_shockb1 =[\t$shockb1, boss_shockb2 ] {};\nvoid() boss_shockb2 =[\t$shockb2, boss_shockb3 ] {};\nvoid() boss_shockb3 =[\t$shockb3, boss_shockb4 ] {};\nvoid() boss_shockb4 =[\t$shockb4, boss_shockb5 ] {};\nvoid() boss_shockb5 =[\t$shockb5, boss_shockb6 ] {};\nvoid() boss_shockb6 =[\t$shockb6, boss_shockb7 ] {};\nvoid() boss_shockb7 =[\t$shockb1, boss_shockb8 ] {};\nvoid() boss_shockb8 =[\t$shockb2, boss_shockb9 ] {};\nvoid() boss_shockb9 =[\t$shockb3, boss_shockb10 ] {};\nvoid() boss_shockb10 =[\t$shockb4, boss_missile1 ] {};\n\nvoid() boss_shockc1 =[\t$shockc1, boss_shockc2 ] {};\nvoid() boss_shockc2 =[\t$shockc2, boss_shockc3 ] {};\nvoid() boss_shockc3 =[\t$shockc3, boss_shockc4 ] {};\nvoid() boss_shockc4 =[\t$shockc4, boss_shockc5 ] {};\nvoid() boss_shockc5 =[\t$shockc5, boss_shockc6 ] {};\nvoid() boss_shockc6 =[\t$shockc6, boss_shockc7 ] {};\nvoid() boss_shockc7 =[\t$shockc7, boss_shockc8 ] {};\nvoid() boss_shockc8 =[\t$shockc8, boss_shockc9 ] {};\nvoid() boss_shockc9 =[\t$shockc9, boss_shockc10 ] {};\nvoid() boss_shockc10 =[\t$shockc10, boss_death1 ] {};\n\nvoid() boss_death1 = [$death1, boss_death2] {\nsound (self, CHAN_VOICE, \"boss1/death.wav\", 1, ATTN_NORM);\n};\nvoid() boss_death2 = [$death2, boss_death3] {};\nvoid() boss_death3 = [$death3, boss_death4] {};\nvoid() boss_death4 = [$death4, boss_death5] {};\nvoid() boss_death5 = [$death5, boss_death6] {};\nvoid() boss_death6 = [$death6, boss_death7] {};\nvoid() boss_death7 = [$death7, boss_death8] {};\nvoid() boss_death8 = [$death8, boss_death9] {};\nvoid() boss_death9 = [$death9, boss_death10]\n{\n\tsound (self, CHAN_BODY, \"boss1/out1.wav\", 1, ATTN_NORM);\n\tTE_lavasplash(self.origin);\n};\n\nvoid() boss_death10 = [$death9, boss_death10]\n{\n\tkilled_monsters = killed_monsters + 1;\n\tWriteByte (MSG_ALL, SVC_KILLEDMONSTER);\t// FIXME: reliable broadcast\n\tSUB_UseTargets ();\n\tremove (self);\n};\n\nvoid(vector p) boss_missile =\n{\n\tlocal\tvector\toffang;\n\tlocal\tvector\torg, vec, d;\n\tlocal\tfloat\tt;\n\n\toffang = vectoangles (self.enemy.origin - self.origin);\t\n\tmakevectors (offang);\n\n\torg = self.origin + p_x*v_forward + p_y*v_right + p_z*'0 0 1';\n\t\n// lead the player on hard mode\n\tif (skill > 1)\n\t{\n\t\tt = vlen(self.enemy.origin - org) / 300;\n\t\tvec = self.enemy.velocity;\n\t\tvec_z = 0;\n\t\td = self.enemy.origin + t * vec;\t\t\n\t}\n\telse\n\t{\n\t\td = self.enemy.origin;\n\t}\n\t\n\tvec = normalize(d - org) * 300;\n\n\tPRJ_FireProjectile(self, \"progs/lavaball.mdl\", org, vec, PE_EXPLOSION, 100+random()*20, MOD_CHTHON, 5);\n\tPRJ_SetRadiusDamage(120, 160, MOD_CHTHON);\n\tnewmis.avelocity = '200 100 300';\n\n\tsound (self, CHAN_WEAPON, \"boss1/throw.wav\", 1, ATTN_NORM);\n\n// check for dead enemy\n\tif (self.enemy.health <= 0)\n\t\tboss_idle1 ();\n};\n\n\nvoid() boss_awake =\n{\n\tself.solid = SOLID_SLIDEBOX;\n\tself.movetype = MOVETYPE_STEP;\n\tself.takedamage = DAMAGE_NO;\n\t\n\tsetmodel (self, \"progs/boss.mdl\");\n\tsetsize (self, '-128 -128 -24', '128 128 256');\n\t\n\tif (skill == 0)\n\t\tself.health = 1;\n\telse\n\t\tself.health = 3;\n\n\tself.enemy = activator;\n\n\tTE_lavasplash(self.origin);\n\n\tself.yaw_speed = 20;\n\tboss_rise1 ();\n};\n\n\n/*QUAKED monster_boss (1 0 0) (-128 -128 -24) (128 128 256)\n*/\nvoid() monster_boss =\n{\n\tif (deathmatch)\n\t{\n\t\tremove(self);\n\t\treturn;\n\t}\n\tprecache_model (\"progs/boss.mdl\");\n\tprecache_model (\"progs/lavaball.mdl\");\n\n\tprecache_sound (\"weapons/rocket1i.wav\");\n\tprecache_sound (\"boss1/out1.wav\");\n\tprecache_sound (\"boss1/sight1.wav\");\n\tprecache_sound (\"misc/power.wav\");\n\tprecache_sound (\"boss1/throw.wav\");\n\tprecache_sound (\"boss1/pain.wav\");\n\tprecache_sound (\"boss1/death.wav\");\n\n\ttotal_monsters = total_monsters + 1;\n\n\tself.use = boss_awake;\n};\n\n//===========================================================================\n\nentity\tle1, le2;\nfloat\tlightning_end;\n\nvoid() lightning_fire =\n{\n\tlocal vector p1, p2;\n\t\n\tif (time >= lightning_end)\n\t{\t// done here, put the terminals back up\n\t\tself = le1;\n\t\tdoor_go_down ();\n\t\tself = le2;\n\t\tdoor_go_down ();\n\t\treturn;\n\t}\n\t\n\tp1 = (le1.mins + le1.maxs) * 0.5;\n\tp1_z = le1.absmin_z - 16;\n\t\n\tp2 = (le2.mins + le2.maxs) * 0.5;\n\tp2_z = le2.absmin_z - 16;\n\t\n\t// compensate for length of bolt\n\tp2 = p2 - normalize(p2-p1)*100;\n\n\tself.nextthink = time + 0.1;\n\tself.think = lightning_fire;\n\n\tTE_lightning3(world, p1, p2);\n};\n\nvoid() lightning_use =\n{\n\tif (lightning_end >= time + 1)\n\t\treturn;\n\n\tle1 = find( world, target, \"lightning\");\n\tle2 = find( le1, target, \"lightning\");\n\tif (!le1 || !le2)\n\t{\n\t\tdprint (\"missing lightning targets\\n\");\n\t\treturn;\n\t}\n\t\n\tif (\n\t (le1.state != STATE_TOP && le1.state != STATE_BOTTOM)\n\t|| (le2.state != STATE_TOP && le2.state != STATE_BOTTOM)\n\t|| (le1.state != le2.state) )\n\t{\n//\t\tdprint (\"not aligned\\n\");\n\t\treturn;\n\t}\n\n// don't let the electrodes go back up until the bolt is done\n\tle1.nextthink = -1;\n\tle2.nextthink = -1;\n\tlightning_end = time + 1;\n\n\tsound (self, CHAN_VOICE, \"misc/power.wav\", 1, ATTN_NORM);\n\tlightning_fire ();\t\t\n\n// advance the boss pain if down\n\tself = find (world, classname, \"monster_boss\");\n\tif (!self)\n\t\treturn;\n\tself.enemy = activator;\n\tif (le1.state == STATE_TOP && self.health > 0)\n\t{\n\t\tsound (self, CHAN_VOICE, \"boss1/pain.wav\", 1, ATTN_NORM);\n\t\tself.health = self.health - 1;\n\t\tif (self.health >= 2)\n\t\t\tboss_shocka1();\n\t\telse if (self.health == 1)\n\t\t\tboss_shockb1();\n\t\telse if (self.health == 0)\n\t\t\tboss_shockc1();\n\t}\t\n};\n\n\n/*QUAKED event_lightning (0 1 1) (-16 -16 -16) (16 16 16)\nJust for boss level.\n*/\nvoid() event_lightning =\n{\n\tif (deathmatch)\n\t{\n\t\tremove(self);\n\t\treturn;\n\t}\n\n\tself.use = lightning_use;\n};\n\n\n"
  },
  {
    "path": "quakec/basemod/buttons.qc",
    "content": "// button and multiple button\n\nvoid() button_wait;\nvoid() button_return;\n\nvoid() button_wait =\n{\n\tself.state = STATE_TOP;\n\tself.nextthink = self.ltime + self.wait;\n\tself.think = button_return;\n\tactivator = self.enemy;\n\tSUB_UseTargets();\n\tself.frame = 1;\t\t\t// use alternate textures\n};\n\nvoid() button_done =\n{\n\tself.state = STATE_BOTTOM;\n};\n\nvoid() button_return =\n{\n\tself.state = STATE_DOWN;\n\tSUB_CalcMove (self.pos1, self.speed, button_done);\n\tself.frame = 0;\t\t\t// use normal textures\n\tif (self.health)\n\t\tself.takedamage = DAMAGE_YES;\t// can be shot again\n};\n\n\nvoid() button_blocked =\n{\t// do nothing, just don't ome all the way back out\n};\n\n\nvoid() button_fire =\n{\n\tif (self.state == STATE_UP || self.state == STATE_TOP)\n\t\treturn;\n\n\tsound (self, CHAN_VOICE, self.noise, 1, ATTN_NORM);\n\n\tself.state = STATE_UP;\n\tSUB_CalcMove (self.pos2, self.speed, button_wait);\n};\n\n\nvoid() button_use =\n{\n\tself.enemy = activator;\n\tbutton_fire ();\n};\n\nvoid() button_touch =\n{\n\tif (other.classname != \"player\")\n\t\treturn;\n\tself.enemy = other;\n\tbutton_fire ();\n};\n\nvoid() button_killed =\n{\n\tself.enemy = damage_attacker;\n\tself.health = self.max_health;\n\tself.takedamage = DAMAGE_NO;\t// wil be reset upon return\n\tbutton_fire ();\n};\n\n\n/*QUAKED func_button (0 .5 .8) ?\nWhen a button is touched, it moves some distance in the direction of it's angle, triggers all of it's targets, waits some time, then returns to it's original position where it can be triggered again.\n\n\"angle\"\t\tdetermines the opening direction\n\"target\"\tall entities with a matching targetname will be used\n\"speed\"\t\toverride the default 40 speed\n\"wait\"\t\toverride the default 1 second wait (-1 = never return)\n\"lip\"\t\toverride the default 4 pixel lip remaining at end of move\n\"health\"\tif set, the button must be killed instead of touched\n\"sounds\"\n0) steam metal\n1) wooden clunk\n2) metallic click\n3) in-out\n*/\nvoid() func_button =\n{\n\tswitch (self.sounds)\n\t{\n\tcase 0:\n\t\tprecache_sound (\"buttons/airbut1.wav\");\n\t\tself.noise = \"buttons/airbut1.wav\";\n\t\tbreak;\n\tcase 1:\n\t\tprecache_sound (\"buttons/switch21.wav\");\n\t\tself.noise = \"buttons/switch21.wav\";\n\t\tbreak;\n\tcase 2:\n\t\tprecache_sound (\"buttons/switch02.wav\");\n\t\tself.noise = \"buttons/switch02.wav\";\n\t\tbreak;\n\tcase 3:\n\t\tprecache_sound (\"buttons/switch04.wav\");\n\t\tself.noise = \"buttons/switch04.wav\";\n\t\tbreak;\n\t}\n\t\n\tSetMovedir ();\n\n\tself.movetype = MOVETYPE_PUSH;\n\tself.solid = SOLID_BSP;\n\tsetmodel (self, self.model);\n\n\tself.blocked = button_blocked;\n\tself.use = button_use;\n\n\tif (self.health)\n\t{\n\t\tself.max_health = self.health;\n\t\tself.th_die = button_killed;\n\t\tself.takedamage = DAMAGE_YES;\n\t}\n\telse\n\t\tself.touch = button_touch;\n\n\tif (!self.speed)\n\t\tself.speed = 40;\n\tif (!self.wait)\n\t\tself.wait = 1;\n\tif (!self.lip)\n\t\tself.lip = 4;\n\n\tself.state = STATE_BOTTOM;\n\n\tself.pos1 = self.origin;\n\tself.pos2 = self.pos1 + self.movedir*(fabs(self.movedir*self.size) - self.lip);\n};\n\n"
  },
  {
    "path": "quakec/basemod/client.qc",
    "content": "\n// prototypes\nvoid () W_WeaponFrame;\nvoid (float weap) W_WeaponSwitch;\nvoid() player_pain;\nvoid() player_stand1;\nvoid (vector org) spawn_tfog;\nvoid (vector org, entity death_owner) spawn_tdeath;\n\nfloat   modelindex_eyes, modelindex_player;\n\n/*\n=============================================================================\n\n\t\t\t\tLEVEL CHANGING / INTERMISSION\n\n=============================================================================\n*/\n\nstring nextmap;\n\n/*QUAKED info_intermission (1 0.5 0.5) (-16 -16 -16) (16 16 16)\nThis is the camera point for the intermission.\nUse mangle instead of angle, so you can set pitch or roll as well as yaw.  'pitch roll yaw'\n*/\nvoid() info_intermission =\n{\n\tself.angles = self.mangle;      // so C can get at it\n};\n\n\n\nvoid() SetChangeParms =\n{\n\tif (self.health <= 0)\n\t{\n\t\tSetNewParms ();\n\t\treturn;\n\t}\n \n// remove items\n\tself.items = self.items - (self.items & \n\t(IT_KEY1 | IT_KEY2 | IT_INVISIBILITY | IT_INVULNERABILITY | IT_SUIT | IT_QUAD) );\n\t\n// cap super health\n\tif (self.health > 100)\n\t\tself.health = 100;\n\tif (self.health < 50)\n\t\tself.health = 50;\n\tparm1 = self.items;\n\tparm2 = self.health;\n\tparm3 = self.armorvalue;\n\tif (self.ammo_shells_real < 25)\n\t\tparm4 = 25;\n\telse\n\t\tparm4 = self.ammo_shells_real;\n\tparm5 = self.ammo_nails_real;\n\tparm6 = self.ammo_rockets_real;\n\tparm7 = self.ammo_cells_real;\n\tparm8 = self.weapon;\n\tparm9 = self.armortype;\n};\n\nvoid() SetNewParms =\n{\n\tparm1 = IT_SHOTGUN | IT_AXE;\n\tparm2 = 100;\n\tparm3 = 0;\n\tparm4 = 25;\n\tparm5 = 0;\n\tparm6 = 0;\n\tparm7 = 0;\n\tparm8 = IT_SHOTGUN;\n\tparm9 = 0;\n};\n\nvoid() DecodeLevelParms =\n{\n\tif (!deathmatch)\n\t{\n\t\tif (world.model == \"maps/start.bsp\")\n\t\t\tSetNewParms ();         // take away all stuff on starting new episode\n\t}\n\t\n\tself.items = parm1;\n\tself.health = parm2;\n\tself.armorvalue = parm3;\n\tself.ammo_shells_real = parm4;\n\tself.ammo_nails_real = parm5;\n\tself.ammo_rockets_real = parm6;\n\tself.ammo_cells_real = parm7;\n\tself.weapon = parm8;\n\tself.armortype = parm9;\n};\n\n/*\n============\nFindIntermission\n\nReturns the entity to view from\n============\n*/\nentity() FindIntermission =\n{\n\tlocal   entity spot;\n\tlocal   float cyc;\n\n// look for info_intermission first\n\tspot = find (world, classname, \"info_intermission\");\n\tif (spot)\n\t{       // pick a random one\n\t\tcyc = random() * 4;\n\t\twhile (cyc > 1)\n\t\t{\n\t\t\tspot = find (spot, classname, \"info_intermission\");\n\t\t\tif (!spot)\n\t\t\t\tspot = find (spot, classname, \"info_intermission\");\n\t\t\tcyc = cyc - 1;\n\t\t}\n\t\treturn spot;\n\t}\n\n// then look for the start position\n\tspot = find (world, classname, \"info_player_start\");\n\tif (spot)\n\t\treturn spot;\n\t\n\tobjerror (\"FindIntermission: no spot\");\n\treturn world; // remove warning\n};\n\nvoid() GotoNextMap =\n{\n\tlocal string newmap;\n\n//ZOID: 12-13-96, samelevel is overloaded, only 1 works for same level\n\n\tif (cvar(\"samelevel\") == 1)     // if samelevel is set, stay on same level\n\t\tchangelevel (mapname);\n\telse {\n\t\t// configurable map lists, see if the current map exists as a\n\t\t// serverinfo/localinfo var\n\t\tnewmap = stringserverinfokey(mapname);\n\t\tif (newmap != \"\")\n\t\t\tchangelevel (newmap);\n\t\telse\n\t\t\tchangelevel (nextmap);\n\t}\n};\n\nvoid() ExitIntermission=\n{\n// skip any text in deathmatch\n\tif (deathmatch)\n\t{\n\t\tGotoNextMap ();\n\t\treturn;\n\t}\n\t\n\tintermission_exittime = time + 1;\n\tintermission_running = intermission_running + 1;\n\n//\n// run some text if at the end of an episode\n//\n\tif (intermission_running == 2)\n\t{\n\t\tif (world.model == \"maps/e1m7.bsp\")\n\t\t{\n\t\t\tENG_SwitchTrack(2, 3);\n\t\t\tif (!cvar(\"registered\"))\n\t\t\t{\n\t\t\t\tWriteByte (MSG_ALL, SVC_FINALE);\n\t\t\t\tWriteString (MSG_ALL, \"As the corpse of the monstrous entity\\nChthon sinks back into the lava whence\\nit rose, you grip the Rune of Earth\\nMagic tightly. Now that you have\\nconquered the Dimension of the Doomed,\\nrealm of Earth Magic, you are ready to\\ncomplete your task in the other three\\nhaunted lands of Quake. Or are you? If\\nyou don't register Quake, you'll never\\nknow what awaits you in the Realm of\\nBlack Magic, the Netherworld, and the\\nElder World!\");\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tWriteByte (MSG_ALL, SVC_FINALE);\n\t\t\t\tWriteString (MSG_ALL, \"As the corpse of the monstrous entity\\nChthon sinks back into the lava whence\\nit rose, you grip the Rune of Earth\\nMagic tightly. Now that you have\\nconquered the Dimension of the Doomed,\\nrealm of Earth Magic, you are ready to\\ncomplete your task. A Rune of magic\\npower lies at the end of each haunted\\nland of Quake. Go forth, seek the\\ntotality of the four Runes!\");\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\telse if (world.model == \"maps/e2m6.bsp\")\n\t\t{\n\t\t\tENG_SwitchTrack(2, 3);\n\t\t\tWriteByte (MSG_ALL, SVC_FINALE);\n\t\t\tWriteString (MSG_ALL, \"The Rune of Black Magic throbs evilly in\\nyour hand and whispers dark thoughts\\ninto your brain. You learn the inmost\\nlore of the Hell-Mother; Shub-Niggurath!\\nYou now know that she is behind all the\\nterrible plotting which has led to so\\nmuch death and horror. But she is not\\ninviolate! Armed with this Rune, you\\nrealize that once all four Runes are\\ncombined, the gate to Shub-Niggurath's\\nPit will open, and you can face the\\nWitch-Goddess herself in her frightful\\notherworld cathedral.\");\n\t\t\treturn;\n\t\t}\n\t\telse if (world.model == \"maps/e3m6.bsp\")\n\t\t{\n\t\t\tENG_SwitchTrack(2, 3);\n\t\t\tWriteByte (MSG_ALL, SVC_FINALE);\n\t\t\tWriteString (MSG_ALL, \"The charred viscera of diabolic horrors\\nbubble viscously as you seize the Rune\\nof Hell Magic. Its heat scorches your\\nhand, and its terrible secrets blight\\nyour mind. Gathering the shreds of your\\ncourage, you shake the devil's shackles\\nfrom your soul, and become ever more\\nhard and determined to destroy the\\nhideous creatures whose mere existence\\nthreatens the souls and psyches of all\\nthe population of Earth.\");\n\t\t\treturn;\n\t\t}\n\t\telse if (world.model == \"maps/e4m7.bsp\")\n\t\t{\n\t\t\tENG_SwitchTrack(2, 3);\n\t\t\tWriteByte (MSG_ALL, SVC_FINALE);\n\t\t\tWriteString (MSG_ALL, \"Despite the awful might of the Elder\\nWorld, you have achieved the Rune of\\nElder Magic, capstone of all types of\\narcane wisdom. Beyond good and evil,\\nbeyond life and death, the Rune\\npulsates, heavy with import. Patient and\\npotent, the Elder Being Shub-Niggurath\\nweaves her dire plans to clear off all\\nlife from the Earth, and bring her own\\nfoul offspring to our world! For all the\\ndwellers in these nightmare dimensions\\nare her descendants! Once all Runes of\\nmagic power are united, the energy\\nbehind them will blast open the Gateway\\nto Shub-Niggurath, and you can travel\\nthere to foil the Hell-Mother's plots\\nin person.\");\n\t\t\treturn;\n\t\t}\n\n\t\tGotoNextMap();\n\t}\n\t\n\tif (intermission_running == 3)\n\t{\n\t\tif (!cvar(\"registered\"))\n\t\t{\t// shareware episode has been completed, go to sell screen\n\t\t\tWriteByte (MSG_ALL, SVC_SELLSCREEN);\n\t\t\treturn;\n\t\t}\n\t\t\n\t\tif ( (serverflags&15) == 15)\n\t\t{\n\t\t\tWriteByte (MSG_ALL, SVC_FINALE);\n\t\t\tWriteString (MSG_ALL, \"Now, you have all four Runes. You sense\\ntremendous invisible forces moving to\\nunseal ancient barriers. Shub-Niggurath\\nhad hoped to use the Runes Herself to\\nclear off the Earth, but now instead,\\nyou will use them to enter her home and\\nconfront her as an avatar of avenging\\nEarth-life. If you defeat her, you will\\nbe remembered forever as the savior of\\nthe planet. If she conquers, it will be\\nas if you had never been born.\");\n\t\t\treturn;\n\t\t}\n\t\t\n\t}\n\n\tGotoNextMap();\n};\n\n/*\n============\nIntermissionThink\n\nWhen the player presses attack or jump, change to the next level\n============\n*/\nvoid() IntermissionThink =\n{\n\tif (time < intermission_exittime)\n\t\treturn;\n\n\tif (!self.button0 && !self.button1 && !self.button2)\n\t\treturn;\n\t\n\tExitIntermission ();\n};\n\n/*\n============\nexecute_changelevel\n\nThe global \"nextmap\" has been set previously.\nTake the players to the intermission spot\n============\n*/\nvoid() execute_changelevel =\n{\n\tlocal entity    pos;\n\n\tintermission_running = 1;\n\t\n// enforce a wait time before allowing changelevel\n\tif (deathmatch)\n\t\tintermission_exittime = time + 5;\n\telse\n\t\tintermission_exittime = time + 2;\n\n\tpos = FindIntermission ();\n\n// play intermission music\n\tENG_SwitchTrack(3, 3);\n\n#ifdef NETQUAKE\n\tpos = FindIntermission ();\n\n\tother = find (world, classname, \"player\");\n\twhile (other != world)\n\t{\n\t\tother.view_ofs = '0 0 0';\n\t\tother.angles = other.v_angle = pos.mangle;\n\t\tother.fixangle = TRUE;\t\t// turn this way immediately\n\t\tother.nextthink = time + 0.5;\n\t\tother.takedamage = DAMAGE_NO;\n\t\tother.solid = SOLID_NOT;\n\t\tother.movetype = MOVETYPE_NONE;\n\t\tother.modelindex = 0;\n\t\tsetorigin (other, pos.origin);\n\t\tother = find (other, classname, \"player\");\n\t}\t\n\n\tWriteByte (MSG_ALL, SVC_INTERMISSION);\n#else\n\tWriteByte (MSG_ALL, SVC_INTERMISSION);\n\tWriteCoord (MSG_ALL, pos.origin_x);\n\tWriteCoord (MSG_ALL, pos.origin_y);\n\tWriteCoord (MSG_ALL, pos.origin_z);\n\tWriteAngle (MSG_ALL, pos.mangle_x);\n\tWriteAngle (MSG_ALL, pos.mangle_y);\n\tWriteAngle (MSG_ALL, pos.mangle_z);\n\t\n\tother = find (world, classname, \"player\");\n\twhile (other != world)\n\t{\n\t\tother.takedamage = DAMAGE_NO;\n\t\tother.solid = SOLID_NOT;\n\t\tother.movetype = MOVETYPE_NONE;\n\t\tother.modelindex = 0;\n\t\tother = find (other, classname, \"player\");\n\t}       \n#endif\n};\n\n\nvoid() changelevel_touch =\n{\n\tif (other.classname != \"player\")\n\t\treturn;\n\n// if \"noexit\" is set, blow up the player trying to leave\n//ZOID, 12-13-96, noexit isn't supported in QW.  Overload samelevel\n//      if ((cvar(\"noexit\") == 1) || ((cvar(\"noexit\") == 2) && (mapname != \"start\")))\n\tif (deathmatch)\n\t{\n\t\tif ((cvar(\"samelevel\") == 2) || ((cvar(\"samelevel\") == 3) && (mapname != \"start\")))\n\t\t{\n\t\t\tT_Damage (other, self, self, 50000, MOD_EXIT);\n\t\t\treturn;\n\t\t}\n\t}\n\n\tbprint2 (PRINT_HIGH, other.netname, \" exited the level\\n\");\n\t\n\tnextmap = self.map;\n\n\tSUB_UseTargets ();\n\n\tself.touch = SUB_Null;\n\n// we can't move people right now, because touch functions are called\n// in the middle of C movement code, so set a think time to do it\n\tself.think = execute_changelevel;\n\tself.nextthink = time + 0.1;\n};\n\n/*QUAKED trigger_changelevel (0.5 0.5 0.5) ? NO_INTERMISSION\nWhen the player touches this, he gets sent to the map listed in the \"map\" variable.  Unless the NO_INTERMISSION flag is set, the view will go to the info_intermission spot and display stats.\n*/\nvoid() trigger_changelevel =\n{\n\tif (!self.map)\n\t\tobjerror (\"chagnelevel trigger doesn't have map\");\n\t\n\tInitTrigger ();\n\tself.touch = changelevel_touch;\n};\n\n\n/*\n=============================================================================\n\n\t\t\t\tPLAYER GAME EDGE FUNCTIONS\n\n=============================================================================\n*/\n\nvoid() set_suicide_frame;\n\n// create a deadbody ent that is removed over time\nvoid(entity ent) CopyToDeadbody =\n{\n\tlocal entity deadbody;\n\n\tdeadbody = spawn();\n\tdeadbody.angles = ent.angles;\n\tdeadbody.model = ent.model;\n\tdeadbody.modelindex = ent.modelindex;\n\tdeadbody.frame = ent.frame;\n\tdeadbody.colormap = ent.colormap;\n\tdeadbody.movetype = ent.movetype;\n\tdeadbody.velocity = ent.velocity;\n\tdeadbody.flags = 0;\n\tsetorigin (deadbody, ent.origin);\n\tsetsize (deadbody, ent.mins, ent.maxs);\n\n\tdeadbody.think = SUB_Remove;\n\tdeadbody.nextthink = time + 30;\n};\n\n// called by ClientKill and DeadThink\nvoid() respawn =\n{\n\tif (coop)\n\t{\n\t\t// make a copy of the dead body for appearances sake\n\t\tCopyToDeadbody (self);\n\t\t// get the spawn parms as they were at level start\n\t\tsetspawnparms (self);\n\t\t// respawn\t\t\n\t\tPutClientInServer ();\n\t}\n\telse if (deathmatch)\n\t{\n\t\t// make a copy of the dead body for appearances sake\n\t\tCopyToDeadbody (self);\n\t\t// set default spawn parms\n\t\tSetNewParms ();\n\t\t// respawn\t\t\n\t\tPutClientInServer ();\n\t}\n\telse\n\t{\t// restart the entire server\n\t\tlocalcmd (\"restart\\n\");\n\t}\n};\n\n\n/*\n============\nClientKill\n\nPlayer entered the suicide command\n============\n*/\nvoid() PlayerDropStuff;\n\nvoid() ClientKill =\n{\n\tif (intermission_running)\n\t\treturn;\n\n\tif (self.suicide_time > time)\n\t\treturn;\n\n\tbprint2 (PRINT_MEDIUM, self.netname, \" suicides\\n\");\n\tPlayerDropStuff ();\n\tset_suicide_frame ();\n\tself.modelindex = modelindex_player;\n\tlogfrag (self, self);\n\tself.frags = self.frags - 2;    // extra penalty\n\trespawn ();\n};\n\n/*\n============\nSelectSpawnPoint\n\nReturns the entity to spawn at\n============\n*/\nentity() SelectSpawnPoint =\n{\n\tlocal entity spot, thing;\n\tlocal float numspots, totalspots;\n\tlocal float pcount;\n\tlocal entity spots;\n\n\tif (coop) // coop spawning\n\t{\n\t\tif (!spotspawn)\n\t\t{\n\t\t\tspotspawn = 1;\n\t\t\tspot = find (world, classname, \"info_player_start\");\n\t\t\tif (spot)\n\t\t\t\treturn spot;\n\t\t}\n\t\tlastspawn = find(lastspawn, classname, \"info_player_coop\");\n\t\tif (lastspawn)\n\t\t\treturn lastspawn;\n\t}\n\n\tif (!deathmatch) // single player spawning\n\t{\n\t\tif (serverflags)\n\t\t{\t// return with a rune to start\n\t\t\tspot = find (world, classname, \"info_player_start2\");\n\t\t\tif (spot)\n\t\t\t\treturn spot;\n\t\t}\n\t\n\t\tspot = find (world, classname, \"info_player_start\");\n\t\tif (spot)\n\t\t\treturn spot;\n\t}\n\n\t// QuakeWorld style deathmatch spawning\n\tnumspots = 0;\n\ttotalspots = 0;\n\t\n\t// choose a info_player_deathmatch point\n\t// ok, find all spots that don't have players nearby\n\tspots = world;\n\tspot = find (world, classname, \"info_player_deathmatch\");       \n\twhile (spot)\n\t{\n\t\ttotalspots = totalspots + 1;\n\n\t\tthing=findradius(spot.origin, 84);\n\t\tpcount=0;               \n\t\twhile (thing)\n\t\t{\n\t\t\tif (thing.classname == \"player\")\n\t\t\t\tpcount=pcount + 1;                      \n\t\t\tthing=thing.chain;      \n\t\t}\n\t\tif (pcount == 0) {\n\t\t\tspot.goalentity = spots;\n\t\t\tspots = spot;\n\t\t\tnumspots = numspots + 1;\n\t\t}\n\n\t\t// Get the next spot in the chain\n\t\tspot = find (spot, classname, \"info_player_deathmatch\");                \n\t}\n\ttotalspots=totalspots - 1;\n\tif (!numspots) {\n\t\t// ack, they are all full, just pick one at random\n\t\ttotalspots = rint((random() * totalspots));\n\t\tspot = find (world, classname, \"info_player_deathmatch\");       \n\t\twhile (totalspots > 0) {\n\t\t\ttotalspots = totalspots - 1;\n\t\t\tspot = find (spot, classname, \"info_player_deathmatch\");\n\t\t}\n\t\treturn spot;\n\t}\n\t\t\n\t// We now have the number of spots available on the map in numspots\n\t// Generate a random number between 1 and numspots\n\tnumspots = numspots - 1;\n\tnumspots = rint((random() * numspots));\n\n\tspot = spots;\n\twhile (numspots > 0) {\n\t\tspot = spot.goalentity;\n\t\tnumspots = numspots - 1;\n\t}\n\treturn spot;\n\n};\nvoid() DecodeLevelParms;\nvoid() PlayerDie;\n\n/*\n===========\nPutClientInServer\n\ncalled each time a player enters a new level\n============\n*/\nvoid() PutClientInServer =\n{\n\tlocal   entity spot;\n\tlocal float wtemp;\n\n\tself.classname = \"player\";\n\tself.health = 100;\n\tself.takedamage = DAMAGE_AIM;\n\tself.solid = SOLID_SLIDEBOX;\n\tself.movetype = MOVETYPE_WALK;\n\tself.show_hostile = 0;\n\tself.max_health = 100;\n\tself.flags = FL_CLIENT;\n\tself.air_finished = time + 12;\n\tself.waterdmg = 2; // initial water damage\n\tself.super_damage_finished = 0;\n\tself.radsuit_finished = 0;\n\tself.invisible_finished = 0;\n\tself.invincible_finished = 0;\n\tself.effects = 0;\n\tself.invincible_time = 0;\n\tself.suicide_time = time + 3;\n\tself.weaponstate = WS_IDLE;\n\n\tDecodeLevelParms ();\n\t\n\t// dumb hack until I get the real weapon system in\n\twtemp = self.weapon;\n\n\tif (deathmatch == 4)\n\t{\n\t\tself.ammo_shells_real = 0;\n\t\tif (numberserverinfokey(\"axe\") == 0)\n\t\t{\n\t\t\tself.ammo_nails_real = 255;\n\t\t\tself.ammo_shells_real = 255;\n\t\t\tself.ammo_rockets_real = 255;\n\t\t\tself.ammo_cells_real = 255;\n\t\t\tself.items |= IT_NAILGUN\n\t\t\t| IT_SUPER_NAILGUN\n\t\t\t| IT_SUPER_SHOTGUN\n\t\t\t| IT_ROCKET_LAUNCHER\n\t\t\t| IT_LIGHTNING;\n\t\t}\n\t\telse\n\t\t\twtemp = IT_AXE;\n\n\t\tself.items |= IT_ARMOR3 | IT_INVULNERABILITY;\n\t\tself.items = self.items - (self.items & (IT_ARMOR1 | IT_ARMOR2));\n\t\tself.armorvalue = 200;\n\t\tself.armortype = 0.8;\n\t\tself.health = 250;\n\t\tself.invincible_time = 1;\n\t\tself.invincible_finished = time + 3;\n\t}\n\n\tif (deathmatch == 5)\n\t{\n\t\tself.ammo_nails_real = 80;\n\t\tself.ammo_shells_real = 30;\n\t\tself.ammo_rockets_real = 10;\n\t\tself.ammo_cells_real = 30;\n\t\tself.items |= IT_NAILGUN\n\t\t\t| IT_SUPER_NAILGUN\n\t\t\t| IT_SUPER_SHOTGUN\n\t\t\t| IT_ROCKET_LAUNCHER\n\t\t\t| IT_GRENADE_LAUNCHER\n\t\t\t| IT_LIGHTNING\n\t\t\t| IT_ARMOR3\n\t\t\t| IT_INVULNERABILITY;\n\t\tself.items = self.items - (self.items & (IT_ARMOR1 | IT_ARMOR2));\n\t\tself.armorvalue = 200;\n\t\tself.armortype = 0.8;\n\t\tself.health = 200;\n\t\tself.invincible_time = 1;\n\t\tself.invincible_finished = time + 3;\n\t}\n\n\tself.weapon = 0;\n\tW_WeaponSwitch (wtemp);\n\n\tself.attack_finished = time;\n\tself.th_pain = player_pain;\n\tself.th_die = PlayerDie;\n\t\n\tself.deadflag = DEAD_NO;\n\t\n\tspot = SelectSpawnPoint ();\n\n\tself.origin = spot.origin + '0 0 1';\n\tself.angles = spot.angles;\n\tself.fixangle = TRUE;           // turn this way immediately\n\n// oh, this is a hack!\n\tsetmodel (self, \"progs/eyes.mdl\");\n\tmodelindex_eyes = self.modelindex;\n\n\tsetmodel (self, \"progs/player.mdl\");\n\tmodelindex_player = self.modelindex;\n\n\tsetsize (self, VEC_HULL_MIN, VEC_HULL_MAX);\n\t\n\tself.view_ofs = '0 0 22';\n\n// Mod - Xian (May.20.97)\n// Bug where player would have velocity from their last kill\n\n\tself.velocity = '0 0 0';\n\n\tplayer_stand1 ();\n\t\n\tmakevectors(self.angles);\n\tspawn_tfog (self.origin + v_forward*20);\n\n\tspawn_tdeath (self.origin, self);\n\n\t// Set Rocket Jump Modifiers\n\trj = numberserverinfokey(\"rj\");\n};\n\n\n/*\n=============================================================================\n\n\t\t\t\tQUAKED FUNCTIONS\n\n=============================================================================\n*/\n\n\n/*QUAKED info_player_start (1 0 0) (-16 -16 -24) (16 16 24)\nThe normal starting point for a level.\n*/\nvoid() info_player_start =\n{\n};\n\n\n/*QUAKED info_player_start2 (1 0 0) (-16 -16 -24) (16 16 24)\nOnly used on start map for the return point from an episode.\n*/\nvoid() info_player_start2 =\n{\n};\n\n/*QUAKED info_player_deathmatch (1 0 1) (-16 -16 -24) (16 16 24)\npotential spawning position for deathmatch games\n*/\nvoid() info_player_deathmatch =\n{\n};\n\n/*QUAKED info_player_coop (1 0 1) (-16 -16 -24) (16 16 24)\npotential spawning position for coop games\n*/\nvoid() info_player_coop =\n{\n};\n\n/*\n===============================================================================\n\nRULES\n\n===============================================================================\n*/\n\n/*\ngo to the next level for deathmatch\n*/\nvoid() NextLevel =\n{\n\tlocal entity o;\n\n\tif (nextmap != \"\")\n\t\treturn; // already done\n\n\tif (mapname == \"start\")\n\t{\n\t\tif (!cvar(\"registered\"))\n\t\t{\n\t\t\tmapname = \"e1m1\";\n\t\t}\n\t\telse if (!(serverflags & 1))\n\t\t{\n\t\t\tmapname = \"e1m1\";\n\t\t\tserverflags = serverflags | 1;\n\t\t}\n\t\telse if (!(serverflags & 2))\n\t\t{\n\t\t\tmapname = \"e2m1\";\n\t\t\tserverflags = serverflags | 2;\n\t\t}\n\t\telse if (!(serverflags & 4))\n\t\t{\n\t\t\tmapname = \"e3m1\";\n\t\t\tserverflags = serverflags | 4;\n\t\t}\n\t\telse if (!(serverflags & 8))\n\t\t{\n\t\t\tmapname = \"e4m1\";\n\t\t\tserverflags = serverflags - 7;\n\t\t}\n \n\t\to = spawn();\n\t\to.map = mapname;\n\t}\n\telse\n\t{\n\t\t// find a trigger changelevel\n\t\to = find(world, classname, \"trigger_changelevel\");\n\t\tif (!o || mapname == \"start\")\n\t\t{       // go back to same map if no trigger_changelevel\n\t\t\to = spawn();\n\t\t\to.map = mapname;\n\t\t}\n\t}\n\n\tnextmap = o.map;\n\n\tif (o.nextthink < time)\n\t{\n\t\to.think = execute_changelevel;\n\t\to.nextthink = time + 0.1;\n\t}\n};\n\n/*\n============\nCheckRules\n\nExit deathmatch games upon conditions\n============\n*/\nvoid() CheckRules =\n{       \n\tif (deathmatch && timelimit && time >= timelimit)\n\t\tNextLevel ();\n\t\n\tif (deathmatch && fraglimit && self.frags >= fraglimit)\n\t\tNextLevel ();\n};\n\n//============================================================================\n\nvoid() PlayerDeathThink =\n{\n\tlocal float             forward;\n\n\tif ((self.flags & FL_ONGROUND))\n\t{\n\t\tforward = vlen (self.velocity);\n\t\tforward = forward - 20;\n\t\tif (forward <= 0)\n\t\t\tself.velocity = '0 0 0';\n\t\telse    \n\t\t\tself.velocity = forward * normalize(self.velocity);\n\t}\n\n// wait for all buttons released\n\tif (self.deadflag == DEAD_DEAD)\n\t{\n\t\tif (self.button2 || self.button1 || self.button0)\n\t\t\treturn;\n\t\tself.deadflag = DEAD_RESPAWNABLE;\n\t\treturn;\n\t}\n\n// wait for any button down\n\tif (!self.button2 && !self.button1 && !self.button0)\n\t\treturn;\n\n\tself.button0 = 0;\n\tself.button1 = 0;\n\tself.button2 = 0;\n\trespawn();\n};\n\n\nvoid() PlayerJump =\n{\n\tif (self.flags & FL_WATERJUMP)\n\t\treturn;\n\t\n\tif (self.waterlevel >= 2)\n\t{\n// play swiming sound\n\t\tif (self.swim_flag < time)\n\t\t{\n\t\t\tself.swim_flag = time + 1;\n\t\t\tif (random() < 0.5)\n\t\t\t\tsound (self, CHAN_BODY, \"misc/water1.wav\", 1, ATTN_NORM);\n\t\t\telse\n\t\t\t\tsound (self, CHAN_BODY, \"misc/water2.wav\", 1, ATTN_NORM);\n\t\t}\n\n\t\treturn;\n\t}\n\n\tif (!(self.flags & FL_ONGROUND))\n\t\treturn;\n\n\tif ( !(self.flags & FL_JUMPRELEASED) )\n\t\treturn;         // don't pogo stick\n\n\tself.flags = self.flags - (self.flags & FL_JUMPRELEASED);       \n\tself.button2 = 0;\n\n// player jumping sound\n\tsound (self, CHAN_BODY, \"player/plyrjmp8.wav\", 1, ATTN_NORM);\n#ifdef NETQUAKE\n\tself.flags = self.flags - FL_ONGROUND;\n\tself.velocity_z = self.velocity_z + 270;\n#endif\n};\n\n\n/*\n===========\nWaterMove\n\n============\n*/\nvoid() WaterMove =\n{\n//dprint (ftos(self.waterlevel));\n\tif (self.movetype == MOVETYPE_NOCLIP)\n\t\treturn;\n\tif (self.health < 0)\n\t\treturn;\n\n\tif (self.waterlevel != 3)\n\t{\n\t\tif (self.air_finished < time)\n\t\t\tsound (self, CHAN_VOICE, \"player/gasp2.wav\", 1, ATTN_NORM);\n\t\telse if (self.air_finished < time + 9)\n\t\t\tsound (self, CHAN_VOICE, \"player/gasp1.wav\", 1, ATTN_NORM);\n\t\tself.air_finished = time + 12;\n\t\tself.waterdmg = 2;\n\t}\n\telse if (self.air_finished < time)\n\t{       // drown!\n\t\tif (self.pain_finished < time)\n\t\t{\n\t\t\tself.waterdmg = self.waterdmg + 2;\n\t\t\tif (self.waterdmg > 15)\n\t\t\t\tself.waterdmg = 10;\n\t\t\tT_Damage (self, world, world, self.waterdmg, MOD_DROWN);\n\t\t\tself.pain_finished = time + 1;\n\t\t}\n\t}\n\t\n\tif (!self.waterlevel)\n\t{\n\t\tif (self.flags & FL_INWATER)\n\t\t{       \n\t\t\t// play leave water sound\n\t\t\tsound (self, CHAN_BODY, \"misc/outwater.wav\", 1, ATTN_NORM);\n\t\t\tself.flags = self.flags - FL_INWATER;\n\t\t}\n\t\treturn;\n\t}\n\n\tif ( !(self.flags & FL_INWATER) )\n\t{      \n\t\t// player enter water sound\n\t\tswitch (self.watertype)\n\t\t{\n\t\tcase CONTENT_LAVA:\n\t\t\tsound (self, CHAN_BODY, \"player/inlava.wav\", 1, ATTN_NORM);\n\t\t\tbreak;\n\t\tcase CONTENT_WATER:\n\t\t\tsound (self, CHAN_BODY, \"player/inh2o.wav\", 1, ATTN_NORM);\n\t\t\tbreak;\n\t\tcase CONTENT_SLIME:\n\t\t\tsound (self, CHAN_BODY, \"player/slimbrn2.wav\", 1, ATTN_NORM);\n\t\t\tbreak;\n\t\t}\n\n\t\tself.flags = self.flags + FL_INWATER;\n\t\tself.dmgtime = 0;\n\t}       \n\n\tif (self.watertype == CONTENT_LAVA)\n\t{       // do damage\n\t\tif (self.dmgtime < time)\n\t\t{\n\t\t\tif (self.radsuit_finished > time)\n\t\t\t\tself.dmgtime = time + 1;\n\t\t\telse\n\t\t\t\tself.dmgtime = time + 0.2;\n\n\t\t\tT_Damage (self, world, world, 10*self.waterlevel, MOD_LAVA);\n\t\t}\n\t}\n\telse if (self.watertype == CONTENT_SLIME)\n\t{       // do damage\n\t\tif (self.dmgtime < time && self.radsuit_finished < time)\n\t\t{\n\t\t\tself.dmgtime = time + 1;\n\t\t\tT_Damage (self, world, world, 4*self.waterlevel, MOD_SLIME);\n\t\t}\n\t}\n\n};\n\nvoid() CheckWaterJump =\n{\n\tlocal vector start, end;\n\n// check for a jump-out-of-water\n\tmakevectors (self.angles);\n\tstart = self.origin;\n\tstart_z = start_z + 8; \n\tv_forward_z = 0;\n\tnormalize(v_forward);\n\tend = start + v_forward*24;\n\ttraceline (start, end, TRUE, self);\n\tif (trace_fraction < 1)\n\t{       // solid at waist\n\t\tstart_z = start_z + self.maxs_z - 8;\n\t\tend = start + v_forward*24;\n\t\tself.movedir = trace_plane_normal * -50;\n\t\ttraceline (start, end, TRUE, self);\n\t\tif (trace_fraction == 1)\n\t\t{       // open at eye level\n\t\t\tself.flags = self.flags | FL_WATERJUMP;\n\t\t\tself.velocity_z = 225;\n\t\t\tself.flags = self.flags - (self.flags & FL_JUMPRELEASED);\n\t\t\tself.teleport_time = time + 2;  // safety net\n\t\t\treturn;\n\t\t}\n\t}\n};\n\n/*\n================\nPlayerPreThink\n\nCalled every frame before physics are run\n================\n*/\nvoid() PlayerPreThink =\n{\n\tif (intermission_running)\n\t{\n\t\tIntermissionThink ();   // otherwise a button could be missed between\n\t\treturn;                                 // the think tics\n\t}\n\n// if intermission is running what is the point of this?\n//\tif (self.view_ofs == '0 0 0')\n//\t\treturn;\n\n\tmakevectors (self.v_angle);             // is this still used\n\n\tCheckRules ();\n\tWaterMove ();\n/*\n\tif (self.waterlevel == 2)\n\t\tCheckWaterJump ();\n*/\n\n\tif (self.deadflag >= DEAD_DEAD)\n\t{\n\t\tPlayerDeathThink ();\n\t\treturn;\n\t}\n\t\n\tif (self.deadflag == DEAD_DYING)\n\t\treturn; // dying, so do nothing\n\n\tif (self.button2)\n\t\tPlayerJump ();\n\telse\n\t\tself.flags = self.flags | FL_JUMPRELEASED;\n\n\tif(time > self.attack_finished && self.currentammo == 0 && self.weapon != IT_AXE)\n\t{\n\t\tW_WeaponSwitch (W_BestWeapon ());\n\t}\n};\n\t\n/*\n================\nCheckPowerups\n\nCheck for turning off powerups\n================\n*/\nvoid() CheckPowerups =\n{\n\tif (self.health <= 0)\n\t\treturn;\n\n// invisibility\n\tif (self.invisible_finished)\n\t{\n// sound and screen flash when items starts to run out\n\t\tif (self.invisible_sound < time)\n\t\t{\n\t\t\tsound (self, CHAN_AUTO, \"items/inv3.wav\", 0.5, ATTN_IDLE);\n\t\t\tself.invisible_sound = time + ((random() * 3) + 1);\n\t\t}\n\n\n\t\tif (self.invisible_finished < time + 3)\n\t\t{\n\t\t\tif (self.invisible_time == 1)\n\t\t\t{\n\t\t\t\tsprint1 (self, PRINT_HIGH, \"Ring of Shadows magic is fading\\n\");\n\t\t\t\tstuffcmd (self, \"bf\\n\");\n\t\t\t\tsound (self, CHAN_AUTO, \"items/inv2.wav\", 1, ATTN_NORM);\n\t\t\t\tself.invisible_time = time + 1;\n\t\t\t}\n\t\t\t\n\t\t\tif (self.invisible_time < time)\n\t\t\t{\n\t\t\t\tself.invisible_time = time + 1;\n\t\t\t\tstuffcmd (self, \"bf\\n\");\n\t\t\t}\n\t\t}\n\n\t\tif (self.invisible_finished < time)\n\t\t{       // just stopped\n\t\t\tself.items = self.items - IT_INVISIBILITY;\n\t\t\tself.invisible_finished = 0;\n\t\t\tself.invisible_time = 0;\n\t\t}\n\t\t\n\t// use the eyes\n\t\tself.frame = 0;\n\t\tself.modelindex = modelindex_eyes;\n\t}\n\telse\n\t\tself.modelindex = modelindex_player;    // don't use eyes\n\n// invincibility\n\tif (self.invincible_finished)\n\t{\n// sound and screen flash when items starts to run out\n\t\tif (self.invincible_finished < time + 3)\n\t\t{\n\t\t\tif (self.invincible_time == 1)\n\t\t\t{\n\t\t\t\tsprint1 (self, PRINT_HIGH, \"Protection is almost burned out\\n\");\n\t\t\t\tstuffcmd (self, \"bf\\n\");\n\t\t\t\tsound (self, CHAN_AUTO, \"items/protect2.wav\", 1, ATTN_NORM);\n\t\t\t\tself.invincible_time = time + 1;\n\t\t\t}\n\t\t\t\n\t\t\tif (self.invincible_time < time)\n\t\t\t{\n\t\t\t\tself.invincible_time = time + 1;\n\t\t\t\tstuffcmd (self, \"bf\\n\");\n\t\t\t}\n\t\t}\n\t\t\n\t\tif (self.invincible_finished < time)\n\t\t{       // just stopped\n\t\t\tself.items = self.items - IT_INVULNERABILITY;\n\t\t\tself.invincible_time = 0;\n\t\t\tself.invincible_finished = 0;\n\t\t}\n\t\tif (self.invincible_finished > time)\n\t\t\tself.effects = self.effects | ef_pent;\n\t\telse\n\t\t\tself.effects = self.effects - (self.effects & ef_pent);\n\t}\n\n// super damage\n\tif (self.super_damage_finished)\n\t{\n\n// sound and screen flash when items starts to run out\n\n\t\tif (self.super_damage_finished < time + 3)\n\t\t{\n\t\t\tif (self.super_time == 1)\n\t\t\t{\n\t\t\t\tif (deathmatch == 4)\n\t\t\t\t\tsprint1 (self, PRINT_HIGH, \"OctaPower is wearing off\\n\");\n\t\t\t\telse\n\t\t\t\t\tsprint1 (self, PRINT_HIGH, \"Quad Damage is wearing off\\n\");\n\t\t\t\tstuffcmd (self, \"bf\\n\");\n\t\t\t\tsound (self, CHAN_AUTO, \"items/damage2.wav\", 1, ATTN_NORM);\n\t\t\t\tself.super_time = time + 1;\n\t\t\t}         \n\t\t\t\n\t\t\tif (self.super_time < time)\n\t\t\t{\n\t\t\t\tself.super_time = time + 1;\n\t\t\t\tstuffcmd (self, \"bf\\n\");\n\t\t\t}\n\t\t}\n\n\t\tif (self.super_damage_finished < time)\n\t\t{       // just stopped\n\t\t\tself.items = self.items - IT_QUAD;\n\t\t\tif (deathmatch == 4)\n\t\t\t{\n\t\t\t\tself.ammo_cells_real = 255;\n\t\t\t\tW_UpdateAmmoCounts(self);\n\t\t\t\tself.armorvalue = 1;\n\t\t\t\tself.armortype = 0.8;\n\t\t\t\tself.health = 100;\n\t\t\t}\n\t\t\tself.super_damage_finished = 0;\n\t\t\tself.super_time = 0;\n\t\t}\n\t\tif (self.super_damage_finished > time)\n\t\t\tself.effects = self.effects | ef_quad;\n\t\telse\n\t\t\tself.effects = self.effects - (self.effects & ef_quad);\n\t}       \n\n// suit \n\tif (self.radsuit_finished)\n\t{\n\t\tself.air_finished = time + 12;          // don't drown\n\n// sound and screen flash when items starts to run out\n\t\tif (self.radsuit_finished < time + 3)\n\t\t{\n\t\t\tif (self.rad_time == 1)\n\t\t\t{\n\t\t\t\tsprint1 (self, PRINT_HIGH, \"Air supply in Biosuit expiring\\n\");\n\t\t\t\tstuffcmd (self, \"bf\\n\");\n\t\t\t\tsound (self, CHAN_AUTO, \"items/suit2.wav\", 1, ATTN_NORM);\n\t\t\t\tself.rad_time = time + 1;\n\t\t\t}\n\t\t\t\n\t\t\tif (self.rad_time < time)\n\t\t\t{\n\t\t\t\tself.rad_time = time + 1;\n\t\t\t\tstuffcmd (self, \"bf\\n\");\n\t\t\t}\n\t\t}\n\n\t\tif (self.radsuit_finished < time)\n\t\t{       // just stopped\n\t\t\tself.items = self.items - IT_SUIT;\n\t\t\tself.rad_time = 0;\n\t\t\tself.radsuit_finished = 0;\n\t\t}\n\t}       \n\n};\n\n\n/*\n================\nPlayerPostThink\n\nCalled every frame after physics are run\n================\n*/\nvoid() PlayerPostThink =\n{\n//dprint (\"post think\\n\");\n\tif (intermission_running)\n\t\treturn;\n//\tif (self.view_ofs == '0 0 0')\n//\t\treturn;\n\tif (self.deadflag)\n\t\treturn;\n\n// check to see if player landed and play landing sound \n\tif ((self.jump_flag < -300) && (self.flags & FL_ONGROUND) )\n\t{\n\t\tif (self.watertype == CONTENT_WATER)\n\t\t\tsound (self, CHAN_BODY, \"player/h2ojump.wav\", 1, ATTN_NORM);\n\t\telse if (self.jump_flag < -650)\n\t\t{\n\t\t\tT_Damage (self, world, world, 5, MOD_FALL); \n\t\t\tsound (self, CHAN_VOICE, \"player/land2.wav\", 1, ATTN_NORM);\n\t\t}\n\t\telse\n\t\t\tsound (self, CHAN_VOICE, \"player/land.wav\", 1, ATTN_NORM);\n\t}\n\n\tself.jump_flag = self.velocity_z;\n\n\tCheckPowerups ();\n\n\tW_WeaponFrame ();\n\n\t// decay health\n\tif (self.healdecay < time)\n\t{\n\t\tif (self.health > self.max_health)\n\t\t\tself.health = self.health - 1;\n\t\tself.healdecay = time + 1;\n\t}\n};\n\n\n/*\n===========\nClientConnect\n\ncalled when a player connects to a server\n============\n*/\nvoid() ClientConnect =\n{\n\tbprint2 (PRINT_HIGH, self.netname, \" entered the game\\n\");\n\t\n// a client connecting during an intermission can cause problems\n\tif (intermission_running)\n\t\tGotoNextMap ();\n};\n\n\n/*\n===========\nClientDisconnect\n\ncalled when a player disconnects from a server\n============\n*/\nvoid() ClientDisconnect =\n{\n\t// let everyone else know\n\tbprint4 (PRINT_HIGH, self.netname, \" left the game with \", ftos(self.frags), \" frags\\n\");\n\tsound (self, CHAN_BODY, \"player/tornoff2.wav\", 1, ATTN_NONE);\n\tset_suicide_frame ();\n};\n\n/*\n===========\nClientObituary\n\ncalled when a player dies\n============\n*/\nBOOL(entity targ, entity attacker) OnSameTeam;\n\nvoid(entity targ, entity attacker, INTEGER mod) ClientObituary =\n{\n\tif (attacker.flags & FL_CLIENT)\n\t{\n\t\tif (attacker == targ)\n\t\t{\n\t\t\tSuicideMessage(targ.netname, mod);\n\t\t\ttarg.frags = targ.frags - 1;\n\t\t\tlogfrag(targ, targ);\n\t\t\treturn;\n\t\t} // else if anything else\n\n\t\tif (OnSameTeam(targ, attacker))\n\t\t{\n\t\t\tTeamKillMessage(targ.netname, attacker.netname, mod);\n\t\t\tlogfrag(attacker, attacker);\t\t\n\t\t\tattacker.frags = attacker.frags - 1;\n\t\t\treturn;\n\t\t}\n\n\t\tif (targ.flags & FL_CLIENT)\n\t\t{\n\t\t\tKillMessage(targ.netname, attacker.netname, mod);\n\t\t\tattacker.frags = attacker.frags + 1;\n\t\t\tlogfrag(attacker, targ);\n\t\t}\n\t\treturn;\n\t} // else if attacker != player\n\n\tif (targ.flags & FL_CLIENT)\n\t{\n\t\tWorldKillMessage(targ.netname, mod);\n\t\ttarg.frags = targ.frags - 1;\n\t\tlogfrag(targ, targ);\n\t}\n};\n\n\u0000"
  },
  {
    "path": "quakec/basemod/defs.qc",
    "content": "#ifdef FTE\n// use real int if using FTE type progs\n#define INTEGER int\n#else\n#define INTEGER float\n#endif\n\n#define BOOL INTEGER\n\n\n#define FALSE ((BOOL)0)\n#define TRUE ((BOOL)1)\n\n/*\n==============================================================================\n\n\t\t\tSOURCE FOR GLOBALVARS_T C STRUCTURE\n\n==============================================================================\n*/\n\n//\n// system globals\n//\nentity          self;\nentity          other;\nentity          world;\nfloat           time;\nfloat           frametime;\n\n#ifndef NETQUAKE\nentity          newmis;                         // if this is set, the entity that just\n\t\t\t\t\t\t\t\t// run created a new missile that should\n\t\t\t\t\t\t\t\t// be simulated immediately\n#endif\n\nfloat           force_retouch;          // force all entities to touch triggers\n\t\t\t\t\t\t\t\t// next frame.  this is needed because\n\t\t\t\t\t\t\t\t// non-moving things don't normally scan\n\t\t\t\t\t\t\t\t// for triggers, and when a trigger is\n\t\t\t\t\t\t\t\t// created (like a teleport trigger), it\n\t\t\t\t\t\t\t\t// needs to catch everything.\n\t\t\t\t\t\t\t\t// decremented each frame, so set to 2\n\t\t\t\t\t\t\t\t// to guarantee everything is touched\nstring          mapname;\n\n#ifdef NETQUAKE\nfloat deathmatch;\nfloat coop;\nfloat teamplay;\n#endif\n\nfloat           serverflags;            // propagated from level to level, used to\n\t\t\t\t\t\t\t\t// keep track of completed episodes\n\nfloat           total_secrets;\nfloat           total_monsters;\n\nfloat           found_secrets;          // number of secrets found\nfloat           killed_monsters;        // number of monsters killed\n\n\n// spawnparms are used to encode information about clients across server\n// level changes\nfloat           parm1, parm2, parm3, parm4, parm5, parm6, parm7, parm8, parm9, parm10, parm11, parm12, parm13, parm14, parm15, parm16;\n\n//\n// global variables set by built in functions\n//      \nvector          v_forward, v_up, v_right;       // set by makevectors()\n\t\n// set by traceline / tracebox\nfloat           trace_allsolid;\nfloat           trace_startsolid;\nfloat           trace_fraction;\nvector          trace_endpos;\nvector          trace_plane_normal;\nfloat           trace_plane_dist;\nentity          trace_ent;\nfloat           trace_inopen;\nfloat           trace_inwater;\n\nentity          msg_entity;                             // destination of single entity writes\n\n//\n// required prog functions\n//\nvoid()          main;                                           // only for testing\n\nvoid()          StartFrame;\n\nvoid()          PlayerPreThink;\nvoid()          PlayerPostThink;\n\nvoid()          ClientKill;\nvoid()          ClientConnect;\nvoid()          PutClientInServer;              // call after setting the parm1... parms\nvoid()          ClientDisconnect;\n\nvoid()          SetNewParms;                    // called when a client first connects to\n\t\t\t\t\t\t\t\t\t// a server. sets parms so they can be\n\t\t\t\t\t\t\t\t\t// saved off for restarts\n\nvoid()          SetChangeParms;                 // call to set parms for self so they can\n\t\t\t\t\t\t\t\t\t// be saved for a level transition\n\n\n//================================================\nvoid            end_sys_globals;                // flag for structure dumping\n//================================================\n\n/*\n==============================================================================\n\n\t\t\tSOURCE FOR ENTVARS_T C STRUCTURE\n\n==============================================================================\n*/\n\n//\n// system fields (*** = do not set in prog code, maintained by C code)\n//\n.float          modelindex;             // *** model index in the precached list\n.vector         absmin, absmax; // *** origin + mins / maxs\n\n.float          ltime;                  // local time for entity\n#ifndef NETQUAKE\n.float          lastruntime;    // *** to allow entities to run out of sequence\n#endif\n\n.float          movetype;\n.float          solid;\n\n.vector         origin;                 // ***\n.vector         oldorigin;              // ***\n.vector         velocity;\n.vector         angles;\n.vector         avelocity;\n\n#ifdef NETQUAKE\n.vector punchangle;\n#endif\n\n.string         classname;              // spawn function\n.string         model;\n.float          frame;\n.float          skin;\n.float          effects;\n\n.vector         mins, maxs;             // bounding box extents reletive to origin\n.vector         size;                   // maxs - mins\n\n.void()         touch;\n.void()         use;\n.void()         think;\n.void()         blocked;                // for doors or plats, called when can't push other\n\n.float          nextthink;\n.entity         groundentity;\n\n// stats\n.float          health;\n.float          frags;\n.float          weapon;                 // one of the IT_SHOTGUN, etc flags\n.string         weaponmodel;\n.float          weaponframe;\n.float          currentammo;\n.float          ammo_shells, ammo_nails, ammo_rockets, ammo_cells;\n\n.float          items;                  // bit flags\n\n.float          takedamage;\n.entity         chain;\n.float          deadflag;\n\n.vector         view_ofs;                       // add to origin to get eye point\n\n\n.float          button0;                // fire\n.float          button1;                // use\n.float          button2;                // jump\n\n.float          impulse;                // weapon changes\n\n.float          fixangle;\n.vector         v_angle;                // view / targeting angle for players\n\n#ifdef NETQUAKE\n.float idealpitch;\n#endif\n\n.string         netname;\n\n.entity         enemy;\n\n.float          flags;\n\n.float          colormap;\n.float          team;\n\n.float          max_health;             // players maximum health is stored here\n\n.float          teleport_time;  // don't back up\n\n.float          armortype;              // save this fraction of incoming damage\n.float          armorvalue;\n\n.float          waterlevel;             // 0 = not in, 1 = feet, 2 = wast, 3 = eyes\n.float          watertype;              // a contents value\n\n.float          ideal_yaw;\n.float          yaw_speed;\n\n.entity         aiment;\n\n.entity         goalentity;             // a movetarget or an enemy\n\n.float          spawnflags;\n\n.string         target;\n.string         targetname;\n\n// damage is accumulated through a frame. and sent as one single\n// message, so the super shotgun doesn't generate huge messages\n.float          dmg_take;\n.float          dmg_save;\n.entity         dmg_inflictor;\n\n.entity         owner;          // who launched a missile\n.vector         movedir;        // mostly for doors, but also used for waterjump\n\n.string         message;                // trigger messages\n\n.float          sounds;         // either a cd track number or sound number\n\n.string         noise, noise1, noise2, noise3;  // contains names of wavs to play\n\n//================================================\nvoid            end_sys_fields;                 // flag for structure dumping\n//================================================\n\n/*\n==============================================================================\n\n\t\t\t\tVARS NOT REFERENCED BY C CODE\n\n==============================================================================\n*/\n\n\n//\n// constants\n//\n\n// edict.flags\n#define FL_FLY              1\n#define FL_SWIM             2\n#define FL_CLIENT           8 // set for all client edicts\n#define FL_INWATER         16 // for enter / leave water splash\n#define FL_MONSTER         32\n#define FL_GODMODE         64 // player cheat\n#define FL_NOTARGET       128 // player cheat\n#define FL_ITEM           256 // extra wide size for bonus items\n#define FL_ONGROUND       512 // standing on something\n#define FL_PARTIALGROUND 1024 // not all corners are valid\n#define FL_WATERJUMP     2048 // player jumping out of water\n#define FL_JUMPRELEASED  4096 // for jump debouncing\n\n// edict.movetype values\n#define MOVETYPE_NONE           0 // never moves\n#define MOVETYPE_WALK           3 // players only\n#define MOVETYPE_STEP           4 // discrete, not real time unless fall\n#define MOVETYPE_FLY            5\n#define MOVETYPE_TOSS           6\n#define MOVETYPE_PUSH           7 // no clip to world, push and crush\n#define MOVETYPE_NOCLIP         8\n#define MOVETYPE_FLYMISSILE     9 // fly with extra size against monsters\n#define MOVETYPE_BOUNCE        10 \n\n// edict.solid values\n#define SOLID_NOT      0 // no interaction with other objects\n#define SOLID_TRIGGER  1 // touch on edge, but not blocking\n#define SOLID_BBOX     2 // touch on edge, block\n#define SOLID_SLIDEBOX 3 // touch on edge, but not an onground\n#define SOLID_BSP      4 // bsp clip, touch on edge, block\n\n\n// deadflag values\n#define DEAD_NO          0\n#define DEAD_DYING       1\n#define DEAD_DEAD        2\n#define DEAD_RESPAWNABLE 3\n\n// takedamage values\n#define DAMAGE_NO  0\n#define DAMAGE_YES 1\n#define DAMAGE_AIM 2\n\n// items\nfloat   IT_AXE                                  = 4096;\nfloat   IT_SHOTGUN                              = 1;\nfloat   IT_SUPER_SHOTGUN                = 2;\nfloat   IT_NAILGUN                              = 4;\nfloat   IT_SUPER_NAILGUN                = 8;\nfloat   IT_GRENADE_LAUNCHER             = 16;\nfloat   IT_ROCKET_LAUNCHER              = 32;\nfloat   IT_LIGHTNING                    = 64;\nfloat   IT_EXTRA_WEAPON                 = 128;\n\nfloat   IT_SHELLS                               = 256;\nfloat   IT_NAILS                                = 512;\nfloat   IT_ROCKETS                              = 1024;\nfloat   IT_CELLS                                = 2048;\n\nfloat   IT_ARMOR1                               = 8192;\nfloat   IT_ARMOR2                               = 16384;\nfloat   IT_ARMOR3                               = 32768;\nfloat   IT_SUPERHEALTH                  = 65536;\n\nfloat   IT_KEY1                                 = 131072;\nfloat   IT_KEY2                                 = 262144;\n\nfloat   IT_INVISIBILITY                 = 524288;\nfloat   IT_INVULNERABILITY              = 1048576;\nfloat   IT_SUIT                                 = 2097152;\nfloat   IT_QUAD                                 = 4194304;\n\n// point content values\n\nfloat   CONTENT_EMPTY                   = -1;\nfloat   CONTENT_SOLID                   = -2;\nfloat   CONTENT_WATER                   = -3;\nfloat   CONTENT_SLIME                   = -4;\nfloat   CONTENT_LAVA                    = -5;\nfloat   CONTENT_SKY                             = -6;\n\nvector  VEC_ORIGIN = '0 0 0';\nvector  VEC_HULL_MIN = '-16 -16 -24';\nvector  VEC_HULL_MAX = '16 16 32';\n\nvector  VEC_HULL2_MIN = '-32 -32 -24';\nvector  VEC_HULL2_MAX = '32 32 64';\n\n// protocol bytes\nfloat   SVC_TEMPENTITY          = 23;\nfloat   SVC_KILLEDMONSTER       = 27;\nfloat   SVC_FOUNDSECRET         = 28;\nfloat   SVC_INTERMISSION        = 30;\nfloat   SVC_FINALE                      = 31;\nfloat   SVC_CDTRACK                     = 32;\nfloat   SVC_SELLSCREEN          = 33;\nfloat   SVC_SMALLKICK           = 34;\nfloat   SVC_BIGKICK                     = 35;\nfloat   SVC_MUZZLEFLASH         = 39;\n\nfloat   TE_SPIKE                = 0;\nfloat   TE_SUPERSPIKE   = 1;\nfloat   TE_GUNSHOT              = 2;\nfloat   TE_EXPLOSION    = 3;\nfloat   TE_TAREXPLOSION = 4;\nfloat   TE_LIGHTNING1   = 5;\nfloat   TE_LIGHTNING2   = 6;\nfloat   TE_WIZSPIKE             = 7;\nfloat   TE_KNIGHTSPIKE  = 8;\nfloat   TE_LIGHTNING3   = 9;\nfloat   TE_LAVASPLASH   = 10;\nfloat   TE_TELEPORT             = 11;\nfloat   TE_BLOOD                = 12;\nfloat   TE_LIGHTNINGBLOOD = 13;\n\n// sound channels\n// channel 0 never willingly overrides\n// other channels (1-7) allways override a playing sound on that channel\n#define CHAN_AUTO   0\n#define CHAN_WEAPON 1\n#define CHAN_VOICE  2\n#define CHAN_ITEM   3\n#define CHAN_BODY   4\n// chan_no_phs_add is defined in engine.qc\n\n#define ATTN_NONE   0.0 // no volume fall off due to range\n#define ATTN_NORM   1.0 // small volume fall off (1000 range)\n#define ATTN_IDLE   2.0 // medium volume fall off (500 range)\n#define ATTN_STATIC 3.0 // large volume fall off (333 range)\n\n// entity effects\n\n//float EF_BRIGHTFIELD  = 1;\nfloat   EF_MUZZLEFLASH  = 2;\nfloat   EF_BRIGHTLIGHT  = 4;\nfloat   EF_DIMLIGHT     = 8;\nfloat   EF_FLAG1                = 16;\nfloat   EF_FLAG2                = 32;\n// GLQuakeWorld Stuff\n// float \tEF_BLUE\t\t=\t64;\t// Blue Globe effect for Quad\n// float\tEF_RED\t\t=\t128;\t// Red Globe effect for Pentagram\n\n// messages\n#define MSG_BROADCAST 0 // unreliable to all\n#define MSG_ONE       1 // reliable to one (msg_entity)\n#define MSG_ALL       2 // reliable to all\n#define MSG_INIT      3 // write to the init string\n#define MSG_MULTICAST 4 // for multicast() call\n\n// message levels\n#define PRINT_LOW    0\n#define PRINT_MEDIUM 1\n#define PRINT_HIGH   2\n#define PRINT_CHAT   3\n\n// multicast sets\n#define MULTICAST_ALL   0 // every client\n#define MULTICAST_PHS   1 // within hearing\n#define MULTICAST_PVS   2 // within sight\n#define MULTICAST_ALL_R 3 // every client, reliable\n#define MULTICAST_PHS_R 4 // within hearing, reliable\n#define MULTICAST_PVS_R 5 // within sight, reliable\n\n\n\n\n//================================================\n\n//\n// globals\n//\n// float   movedist;\n\nstring  string_null;    // null string, nothing should be held here\n\nentity  activator;              // the entity that activated a trigger or brush\n\nentity  damage_attacker;        // set by T_Damage\nentity  damage_inflictor;       // set by T_Damage\nINTEGER damage_mod;             // set by T_Damage\n\nfloat   framecount;\n\n//\n// cvars checked each frame\n//\nfloat           timelimit;\nfloat           fraglimit;\nfloat           rj;\n\n#ifndef NETQUAKE\n// these variables are not a part of QW's system fields\nfloat deathmatch;\nfloat coop;\nfloat teamplay;\n#endif\n\nfloat skill;\n\n// coop spawning globals\nentity  lastspawn;\nfloat spotspawn;\n\nentity shub; // boss entity\n\n//================================================\n\n#define WT_MEDIEVAL 0\n#define WT_METAL    1\n#define WT_BASE     2\n\n//================================================\n\n.string killtarget; // used by anything using SUB_UseTargets (items/triggers/etc)\n\n.void(entity attacker, float damage) th_pain; // used by secret doors, monsters, players\n.void()         th_die;   // used by anything damagable (doors/buttons/players/monsters/explobox/triggermultiple)\n\n.float speed; // used with door/plats/fireball spawner, inherited\n.string map; // used with world/trigger_changelevel, inherited\n\n// used with monsters/players\n.INTEGER ammo_shells_real;\t// real shells count\n.INTEGER ammo_nails_real;\t// real nails count\n.INTEGER ammo_rockets_real;\t// real rockets count\n.INTEGER ammo_cells_real;\t// real cells count\n\n// Zoid Additions\nnoref .float\t\tmaxspeed;\t\t// Used to set Maxspeed on a player\nnoref .float\t\tgravity;\t\t// Gravity Multiplier (0 to 1.0)\n\n.float          attack_finished; // used with lots of stuff...\n.float          pain_finished; // used with monsters/players\n\n.float          invincible_finished; // only used for players but object check needed for T_Damage\n.float          super_damage_finished; // only used for players but object check needed for T_Damage\n\n//\n// misc\n//\n.float cnt; // misc flag used by trains/dropped pent and quad/monsters\n\t\n// intermission\nfloat   intermission_running;\nfloat   intermission_exittime;\n\n#ifdef NETQUAKE\nentity newmis;\n#endif\n\n// extensions\n.float alpha; // DP_ENT_ALPHA\n\n// enums\n// ai ranges\nenum\n{\n\tRANGE_MELEE,\n\tRANGE_NEAR,\n\tRANGE_MID,\n\tRANGE_FAR\n};\n\n// monster attack states\nenum\n{\n\tAS_NONE,\n\tAS_STRAIGHT,\n\tAS_SLIDING,\n\tAS_MELEE,\n\tAS_MISSILE\n};\n\n// door/plat states\nenum {\n\tSTATE_TOP,\n\tSTATE_BOTTOM,\n\tSTATE_UP,\n\tSTATE_DOWN\n};\n\n// heal defines\nenumflags {\n\tH_ROTTEN,\n\tH_MEGA\n};\n\n// weaponstate defines\nenum {\n\tWS_IDLE,\n\tWS_FIRING1,\n\tWS_FIRING2 // used with nailgun\n};\n\n// ammo type defines\nenum {\n\tAT_NONE,\n\tAT_SHELLS,\n\tAT_NAILS,\n\tAT_ROCKETS,\n\tAT_CELLS\n};\n\n// unions\n//floats (includes vectors)\n.union {\n\tstruct { // fields used with world object\n\t\tfloat worldtype; \t// world type, 0=medieval 1=metal 2=base\n\t};\n\tstruct { // fields used with triggers/doors/plats\n\t\tfloat delay;\t\t// time from activation to firing\n\t\tfloat wait;\t\t\t// time from firing to restarting\n\t\tfloat t_length;\t\t// override length to move sideways\n\t\tfloat t_width; \t\t// override length to move upwards/downwards\n\t\tvector finaldest;\t\t// used with SUB_CalcMove\n\t\tvector finalangle;\t// used with SUB_CalcMove\n\t\tfloat count;\t\t// for counting triggers\n\t\tfloat dmg;\t\t\t// damage done by door/train/trigger hurt\n\t\tvector mangle;\t\t// initial angle (doors/teleporter/intermission)\n\t\tfloat lip;\t\t\t// position adjustment (func_door/func_button)\n\t\tfloat state;\t\t// object state (doors/buttons/plats)\n\t\tvector pos1;\t\t// top position (doors/buttons/plats)\n\t\tvector pos2;\t\t// bottom position (doors/buttons/plats)\n\t\tfloat height;\t\t// height (plats/trigger_monsterjump)\n\t\tvector dest1;\t\t// passed to CalcMove (doors)\n\t\tvector dest2;\t\t// passed to CalcMove (doors)\n\t};\n\tstruct { // fields used with players\n\t\tfloat weaponframe_time;\t\t// weapon frame advance time\n\t\tfloat suicide_time;     \t// time to allow suicide after spawn\n\t\tfloat healdecay; \t\t\t// time when health will decay\n\t\tfloat show_hostile;\t\t// used to alert monsters\n\t\tfloat lightning_sound;\t\t// used for lightning sound playback\n\t\tfloat fly_sound;\t\t\t// used with trigger_push and maintaining wind sound\n\t\tfloat invincible_time;\t\t// pentagram state\n\t\tfloat invincible_sound;\t\t// pentagram sound playback\n\t\tfloat invisible_time;\t\t// ring state\n\t\tfloat invisible_sound;\t\t// ring sound playback\n\t\tfloat invisible_finished;\t// ring current duration\n\t\tfloat super_time;\t\t\t// quad state\n\t\tfloat super_sound;\t\t// quad sound playback\n\t\tfloat rad_time;\t\t\t// rad suit state\n\t\tfloat radsuit_finished;\t\t// rad suit current duration\n\t\tfloat dmgtime;\t\t\t// time slime/lava will damage\n\t\tfloat swim_flag;\t\t\t// swim sound playback\n\t\tfloat air_finished;\t\t// when time > air_finished, start drowning\n\t\tfloat waterdmg;\t\t\t// damage water will deal when drowning\n\t\tfloat jump_flag;\t\t\t// last z velocity used for falling damage\n\t};\n\tstruct { // fields used with items\n\t\tfloat healamount;\t// amount healed with health item\n\t\tfloat ammo_count;\t// ammo amount \n\t};\n\tstruct { // fields used with generic projectiles\n\t\tfloat damage_direct;\t\t// damage done with a direct hit\n\t\tfloat damage_exp; \t\t// damage done from radius damage\n\t\tfloat radius_exp;  \t\t// radius of radius damage\n\t\tfloat expire_time;\t\t// time when projectile dies\n\t\tfloat proj_think_time;\t\t// interval between thinks\n\t};\n\tstruct { // fields used with lights\n\t\tfloat light_lev;\t// not used by game, but parsed by light util\n\t\tfloat style;\t// lightstyle to use for light\n\t};\n\tstruct { // fields used with monsters\n\t\tfloat search_time;\t// search time intervals\n\t\tfloat attack_state;\t// current AI attack state\n\t\tfloat pausetime;\t\t// time to pause for monsters\n\t\tfloat hknightattack;\t// hell knight attack pattern\n\t\tfloat lefty;\t\t// ai slide move\n\t\tfloat wizardidle;\t\t// wizard idle sound timer\n\t\tfloat inpain;\t\t// zombie in pain\n\t};\n\t// fields used with ambient sounds?\n\t/*\n\tstruct {\n\t\tfloat waitmin;\n\t\tfloat waitmax;\n\t\tfloat distance;\n\t\tfloat volume;\n\t};\n\t*/\n};\n\n//integers\n.union {\n\n\tstruct { // fields used with players\n\t\tINTEGER weaponstate;\t\t// firing state of current weapon\n\t\tINTEGER walkframe;\t\t// used with walking animation\n\t\tINTEGER ammo_type;\t\t// ammo type in use\n\t};\n\tstruct { // fields used with bubbles spawned from drowning\n\t\tINTEGER bubble_count;   // keeps track of the number of bubbles\n\t\tINTEGER bubble_state;\t// associated with bubble progression\n\t};\n\tstruct { // fields used with items\n\t\tINTEGER healtype;\t// type of health with health item\n\t};\n\tstruct { // fields used with generic projectiles\n\t\tINTEGER mod_direct;\t\t// mod type for direct hit\n\t\tINTEGER mod_exp; \t\t\t// mod type for radius damage\n\t\tINTEGER proj_effect;\t\t// effect used with projectile\n\t\tINTEGER voided;\t\t\t// projectile has been voided\n\t};\n};\n\n//functions\n.union {\n\tstruct { // fields used with triggers/doors/plats\n\t\tvoid() think1;\t\t// used with SUB_CalcMove\n\t};\n\tstruct { // fields used with generic projectiles\n\t\tvoid() proj_think;\t\t// extra think function used with projectile\n\t\tfloat() proj_touch;\t\t// extra touch function used with projectile\n\t};\n\tstruct { // fields used with monsters\n\t\tvoid() th_stand;\t\t// standing animation\n\t\tvoid() th_walk;\t\t// walking animation\n\t\tvoid() th_run;\t\t// running animation\n\t\tvoid() th_missile;\t// ranged animation\n\t\tvoid() th_melee;\t\t// melee animation\n\t};\n};\n\n\n//entities\n.union {\n\tstruct { // fields used with triggers/doors/plats\n\t\tentity trigger_field;\t// used with linking (doors)\n\t};\n\n\tstruct { // fields used with monsters\n\t\tentity oldenemy;\t\t// old enemy\n\t\tentity movetarget;\t// target entity to move to\n\t};\n};\n\n//strings\n.union {\n\tstruct { // fields used with world object\n\t\tstring wad; //mute it\n\t};\n\tstruct { // fields used with triggers/doors/plats\n\t\tstring noise4;\t\t// extra sound (doors)\n\t};\n\tstruct { // fields used with items\n\t\tstring mdl;\t\t// model used with SUB_regen\n\t};\n};\n\n//mute those warnings (unions/structs do not support noref mid-struct, but a later def will propogate the noref flag)\nnoref .string wad;\nnoref .float light_lev;\n\n//===========================================================================\n\t\n\n//\n// builtin functions\n//\n\nvoid(vector ang)        makevectors             = #1;           // sets v_forward, etc globals\nvoid(entity e, vector o) setorigin      = #2;\nvoid(entity e, string m) setmodel       = #3;           // set movetype and solid first\nvoid(entity e, vector min, vector max) setsize = #4;\n// #5 was removed\n// void() break                                            = #6;\nfloat() random                                          = #7;           // returns 0 - 1\nvoid(entity e, float chan, string samp, float vol, float atten) sound = #8;\nvector(vector v) normalize                      = #9;\nvoid(string e) error                            = #10;\nvoid(string e) objerror                         = #11;\nfloat(vector v) vlen                            = #12;\nfloat(vector v) vectoyaw                        = #13;\nentity() spawn                                          = #14;\nvoid(entity e) remove                           = #15;\n\n// sets trace_* globals\n// nomonsters can be:\n// An entity will also be ignored for testing if forent == test,\n// forent->owner == test, or test->owner == forent\n// a forent of world is ignored\nvoid(vector v1, vector v2, float nomonsters, entity forent) traceline = #16;    \n\nentity() checkclient                            = #17;  // returns a client to look for\nentity(entity start, .string fld, string match) find = #18;\nstring(string s) precache_sound         = #19;\nstring(string s) precache_model         = #20;\nvoid(entity client, string s)stuffcmd = #21;\nentity(vector org, float rad) findradius = #22;\n// bprint moved to engine.qc\n// sprint moved to engine.qc\nvoid(string s) dprint                           = #25;\nstring(float f) ftos                            = #26;\nstring(vector v) vtos                           = #27;\nvoid() coredump                                         = #28;          // prints all edicts\nvoid() traceon                                          = #29;          // turns statment trace on\nvoid() traceoff                                         = #30;\nvoid(entity e) eprint                           = #31;          // prints an entire edict\nfloat(float yaw, float dist) walkmove   = #32;  // returns TRUE or FALSE\n// #33 was removed\nfloat() droptofloor= #34;  // TRUE if landed on floor\nvoid(float style, string value) lightstyle = #35;\nfloat(float v) rint                                     = #36;          // round to nearest int\nfloat(float v) floor                            = #37;          // largest integer <= v\nfloat(float v) ceil                                     = #38;          // smallest integer >= v\n// #39 was removed\nfloat(entity e) checkbottom                     = #40;          // true if self is on ground\nfloat(vector v) pointcontents           = #41;          // returns a CONTENT_*\n// #42 was removed\nfloat(float f) fabs = #43;\nvector(entity e, float speed) aim = #44;                // returns the shooting vector\n// cvar moved to engine.qc\nvoid(string s) localcmd = #46;                                  // put string into local que\nentity(entity e) nextent = #47;                                 // for looping through all ents\n// #48 was removed\nvoid() ChangeYaw = #49;                                         // turn towards self.ideal_yaw\n\t\t\t\t\t\t\t\t\t\t\t// at self.yaw_speed\n// #50 was removed\nvector(vector v) vectoangles                    = #51;\n\n//\n// direct client message generation\n//\nvoid(float to, float f) WriteByte               = #52;\nvoid(float to, float f) WriteChar               = #53;\nvoid(float to, float f) WriteShort              = #54;\nvoid(float to, float f) WriteLong               = #55;\nvoid(float to, float f) WriteCoord              = #56;\nvoid(float to, float f) WriteAngle              = #57;\nvoid(float to, string s) WriteString    = #58;\nvoid(float to, entity s) WriteEntity    = #59;\n\n// several removed\n\nvoid(float step) movetogoal                             = #67;\n\nstring(string s) precache_file          = #68;  // no effect except for -copy\nvoid(entity e) makestatic               = #69; \nvoid(string s) changelevel = #70;\n\n//#71 was removed\n\nvoid(string var, string val) cvar_set = #72;    // sets cvar.value\n\nvoid(entity client, string s) centerprint = #73;        // sprint, but in middle\n\nvoid(vector pos, string samp, float vol, float atten) ambientsound = #74;\n\nstring(string s) precache_model2        = #75;          // registered version only\nstring(string s) precache_sound2        = #76;          // registered version only\nstring(string s) precache_file2         = #77;          // registered version only\n\nvoid(entity e) setspawnparms            = #78;          // set parm1... to the\n\t\t\t\t\t\t\t\t\t\t\t\t// values at level start\n\t\t\t\t\t\t\t\t\t\t\t\t// for coop respawn\n// logfrag moved to engine.qc\n// infokey moved to engine.qc\n// stof moved to engine.qc\n\n//============================================================================\n\n//\n// subs.qc\n//\nvoid(vector tdest, float tspeed, void() func) SUB_CalcMove;\nvoid(entity ent, vector tdest, float tspeed, void() func) SUB_CalcMoveEnt;\nvoid(vector destangle, float tspeed, void() func) SUB_CalcAngleMove;\nvoid()  SUB_CalcMoveDone;\nvoid() SUB_CalcAngleMoveDone;\nvoid() SUB_Null;\nvoid() SUB_UseTargets;\nvoid() SUB_Remove;\n\n//\n//      combat.qc\n//\nvoid(entity targ, entity inflictor, entity attacker, float damage, INTEGER mod) T_Damage;\n\nfloat (entity e, float healamount, float ignore) T_Heal; // health function\n\nBOOL(entity targ, entity inflictor) CanDamage;\n\n\u0000"
  },
  {
    "path": "quakec/basemod/demon.qc",
    "content": "/*\n==============================================================================\n\nDEMON\n\n==============================================================================\n*/\n\n$cd id1/models/demon3\n$scale\t0.8\n$origin 0 0 24\n$base base\n$skin base\n\n$frame stand1 stand2 stand3 stand4 stand5 stand6 stand7 stand8 stand9\n$frame stand10 stand11 stand12 stand13\n\n$frame walk1 walk2 walk3 walk4 walk5 walk6 walk7 walk8\n\n$frame run1 run2 run3 run4 run5 run6\n\n$frame leap1 leap2 leap3 leap4 leap5 leap6 leap7 leap8 leap9 leap10\n$frame leap11 leap12\n\n$frame pain1 pain2 pain3 pain4 pain5 pain6\n\n$frame death1 death2 death3 death4 death5 death6 death7 death8 death9\n\n$frame attacka1 attacka2 attacka3 attacka4 attacka5 attacka6 attacka7 attacka8\n$frame attacka9 attacka10 attacka11 attacka12 attacka13 attacka14 attacka15\n\n//============================================================================\n\nvoid()\tDemon_JumpTouch;\nvoid(float side) Demon_Melee;\n\nvoid()\tdemon1_stand1\t=[\t$stand1,\tdemon1_stand2\t] {ai_stand();};\nvoid()\tdemon1_stand2\t=[\t$stand2,\tdemon1_stand3\t] {ai_stand();};\nvoid()\tdemon1_stand3\t=[\t$stand3,\tdemon1_stand4\t] {ai_stand();};\nvoid()\tdemon1_stand4\t=[\t$stand4,\tdemon1_stand5\t] {ai_stand();};\nvoid()\tdemon1_stand5\t=[\t$stand5,\tdemon1_stand6\t] {ai_stand();};\nvoid()\tdemon1_stand6\t=[\t$stand6,\tdemon1_stand7\t] {ai_stand();};\nvoid()\tdemon1_stand7\t=[\t$stand7,\tdemon1_stand8\t] {ai_stand();};\nvoid()\tdemon1_stand8\t=[\t$stand8,\tdemon1_stand9\t] {ai_stand();};\nvoid()\tdemon1_stand9\t=[\t$stand9,\tdemon1_stand10\t] {ai_stand();};\nvoid()\tdemon1_stand10\t=[\t$stand10,\tdemon1_stand11\t] {ai_stand();};\nvoid()\tdemon1_stand11\t=[\t$stand11,\tdemon1_stand12\t] {ai_stand();};\nvoid()\tdemon1_stand12\t=[\t$stand12,\tdemon1_stand13\t] {ai_stand();};\nvoid()\tdemon1_stand13\t=[\t$stand13,\tdemon1_stand1\t] {ai_stand();};\n\nvoid()\tdemon1_walk1\t=[\t$walk1,\t\tdemon1_walk2\t] {\nif (random() < 0.2)\n    sound (self, CHAN_VOICE, \"demon/idle1.wav\", 1, ATTN_IDLE);\nai_walk(8);\n};\nvoid()\tdemon1_walk2\t=[\t$walk2,\t\tdemon1_walk3\t] {ai_walk(6);};\nvoid()\tdemon1_walk3\t=[\t$walk3,\t\tdemon1_walk4\t] {ai_walk(6);};\nvoid()\tdemon1_walk4\t=[\t$walk4,\t\tdemon1_walk5\t] {ai_walk(7);};\nvoid()\tdemon1_walk5\t=[\t$walk5,\t\tdemon1_walk6\t] {ai_walk(4);};\nvoid()\tdemon1_walk6\t=[\t$walk6,\t\tdemon1_walk7\t] {ai_walk(6);};\nvoid()\tdemon1_walk7\t=[\t$walk7,\t\tdemon1_walk8\t] {ai_walk(10);};\nvoid()\tdemon1_walk8\t=[\t$walk8,\t\tdemon1_walk1\t] {ai_walk(10);};\n\nvoid()\tdemon1_run1\t=[\t$run1,\t\tdemon1_run2\t] {\nif (random() < 0.2)\n    sound (self, CHAN_VOICE, \"demon/idle1.wav\", 1, ATTN_IDLE);\nai_run(20);};\nvoid()\tdemon1_run2\t=[\t$run2,\t\tdemon1_run3\t] {ai_run(15);};\nvoid()\tdemon1_run3\t=[\t$run3,\t\tdemon1_run4\t] {ai_run(36);};\nvoid()\tdemon1_run4\t=[\t$run4,\t\tdemon1_run5\t] {ai_run(20);};\nvoid()\tdemon1_run5\t=[\t$run5,\t\tdemon1_run6\t] {ai_run(15);};\nvoid()\tdemon1_run6\t=[\t$run6,\t\tdemon1_run1\t] {ai_run(36);};\n\nvoid()\tdemon1_jump1\t=[\t$leap1,\t\tdemon1_jump2\t] {ai_face();};\nvoid()\tdemon1_jump2\t=[\t$leap2,\t\tdemon1_jump3\t] {ai_face();};\nvoid()\tdemon1_jump3\t=[\t$leap3,\t\tdemon1_jump4\t] {ai_face();};\nvoid()\tdemon1_jump4\t=[\t$leap4,\t\tdemon1_jump5\t]\n{\n\tai_face();\n\t\n\tself.touch = Demon_JumpTouch;\n\tmakevectors (self.angles);\n\tself.origin_z = self.origin_z + 1;\n\tself.velocity = v_forward * 600 + '0 0 250';\n\tif (self.flags & FL_ONGROUND)\n\t\tself.flags = self.flags - FL_ONGROUND;\n};\nvoid()\tdemon1_jump5\t=[\t$leap5,\t\tdemon1_jump6\t] {};\nvoid()\tdemon1_jump6\t=[\t$leap6,\t\tdemon1_jump7\t] {};\nvoid()\tdemon1_jump7\t=[\t$leap7,\t\tdemon1_jump8\t] {};\nvoid()\tdemon1_jump8\t=[ \t$leap8,\t\tdemon1_jump9\t] {};\nvoid()\tdemon1_jump9\t=[ \t$leap9,\t\tdemon1_jump10\t] {};\nvoid()\tdemon1_jump10\t=[ \t$leap10,\tdemon1_jump1\t] {\nself.nextthink = time + 3;\n// if three seconds pass, assume demon is stuck and jump again\n};\n\nvoid()\tdemon1_jump11\t=[ \t$leap11,\tdemon1_jump12\t] {};\nvoid()\tdemon1_jump12\t=[ \t$leap12,\tdemon1_run1\t] {};\n\n\nvoid()\tdemon1_atta1\t=[\t$attacka1,\t\tdemon1_atta2\t] {ai_charge(4);};\nvoid()\tdemon1_atta2\t=[\t$attacka2,\t\tdemon1_atta3\t] {ai_charge(0);};\nvoid()\tdemon1_atta3\t=[\t$attacka3,\t\tdemon1_atta4\t] {ai_charge(0);};\nvoid()\tdemon1_atta4\t=[\t$attacka4,\t\tdemon1_atta5\t] {ai_charge(1);};\nvoid()\tdemon1_atta5\t=[\t$attacka5,\t\tdemon1_atta6\t] {ai_charge(2); Demon_Melee(200);};\nvoid()\tdemon1_atta6\t=[\t$attacka6,\t\tdemon1_atta7\t] {ai_charge(1);};\nvoid()\tdemon1_atta7\t=[\t$attacka7,\t\tdemon1_atta8\t] {ai_charge(6);};\nvoid()\tdemon1_atta8\t=[\t$attacka8,\t\tdemon1_atta9\t] {ai_charge(8);};\nvoid()\tdemon1_atta9\t=[\t$attacka9,\t\tdemon1_atta10] {ai_charge(4);};\nvoid()\tdemon1_atta10\t=[\t$attacka10,\t\tdemon1_atta11] {ai_charge(2);};\nvoid()\tdemon1_atta11\t=[\t$attacka11,\t\tdemon1_atta12] {Demon_Melee(-200);};\nvoid()\tdemon1_atta12\t=[\t$attacka12,\t\tdemon1_atta13] {ai_charge(5);};\nvoid()\tdemon1_atta13\t=[\t$attacka13,\t\tdemon1_atta14] {ai_charge(8);};\nvoid()\tdemon1_atta14\t=[\t$attacka14,\t\tdemon1_atta15] {ai_charge(4);};\nvoid()\tdemon1_atta15\t=[\t$attacka15,\t\tdemon1_run1] {ai_charge(4);};\n\nvoid()\tdemon1_pain1\t=[\t$pain1,\t\tdemon1_pain2\t] {};\nvoid()\tdemon1_pain2\t=[\t$pain2,\t\tdemon1_pain3\t] {};\nvoid()\tdemon1_pain3\t=[\t$pain3,\t\tdemon1_pain4\t] {};\nvoid()\tdemon1_pain4\t=[\t$pain4,\t\tdemon1_pain5\t] {};\nvoid()\tdemon1_pain5\t=[\t$pain5,\t\tdemon1_pain6\t] {};\nvoid()\tdemon1_pain6\t=[\t$pain6,\t\tdemon1_run1\t] {};\n\nvoid(entity attacker, float damage)\tdemon1_pain =\n{\n\tif (self.touch == Demon_JumpTouch)\n\t\treturn;\n\n\tif (self.pain_finished > time)\n\t\treturn;\n\n\tself.pain_finished = time + 1;\n\tsound (self, CHAN_VOICE, \"demon/dpain1.wav\", 1, ATTN_NORM);\n\n\tif (random()*200 > damage)\n\t\treturn;\t\t// didn't flinch\n\t\t\n\tdemon1_pain1 ();\n};\n\nvoid()\tdemon1_die1\t\t=[\t$death1,\t\tdemon1_die2\t] {\nsound (self, CHAN_VOICE, \"demon/ddeath.wav\", 1, ATTN_NORM);};\nvoid()\tdemon1_die2\t\t=[\t$death2,\t\tdemon1_die3\t] {};\nvoid()\tdemon1_die3\t\t=[\t$death3,\t\tdemon1_die4\t] {};\nvoid()\tdemon1_die4\t\t=[\t$death4,\t\tdemon1_die5\t] {};\nvoid()\tdemon1_die5\t\t=[\t$death5,\t\tdemon1_die6\t] {};\nvoid()\tdemon1_die6\t\t=[\t$death6,\t\tdemon1_die7\t]\n{self.solid = SOLID_NOT;};\nvoid()\tdemon1_die7\t\t=[\t$death7,\t\tdemon1_die8\t] {};\nvoid()\tdemon1_die8\t\t=[\t$death8,\t\tdemon1_die9\t] {};\nvoid()\tdemon1_die9\t\t=[\t$death9,\t\tdemon1_die9 ] {};\n\nvoid() demon_die =\n{\n// check for gib\n\tif (self.health < -80)\n\t{\n\t\tsound (self, CHAN_VOICE, \"player/udeath.wav\", 1, ATTN_NORM);\n\t\tThrowHead (\"progs/h_demon.mdl\", self.health);\n\t\tThrowGib (\"progs/gib1.mdl\", self.health);\n\t\tThrowGib (\"progs/gib1.mdl\", self.health);\n\t\tThrowGib (\"progs/gib1.mdl\", self.health);\n\t\treturn;\n\t}\n\n// regular death\n\tdemon1_die1 ();\n};\n\n\nvoid() Demon_MeleeAttack =\n{\n\tdemon1_atta1 ();\n};\n\n\n/*QUAKED monster_demon1 (1 0 0) (-32 -32 -24) (32 32 64) Ambush\n\n*/\nvoid() monster_demon1 =\n{\n\tif (deathmatch)\n\t{\n\t\tremove(self);\n\t\treturn;\n\t}\n\tprecache_model (\"progs/demon.mdl\");\n\tprecache_model (\"progs/h_demon.mdl\");\n\n\tprecache_sound (\"demon/ddeath.wav\");\n\tprecache_sound (\"demon/dhit2.wav\");\n\tprecache_sound (\"demon/djump.wav\");\n\tprecache_sound (\"demon/dpain1.wav\");\n\tprecache_sound (\"demon/idle1.wav\");\n\tprecache_sound (\"demon/sight2.wav\");\n\n\tself.solid = SOLID_SLIDEBOX;\n\tself.movetype = MOVETYPE_STEP;\n\n\tsetmodel (self, \"progs/demon.mdl\");\n\n\tsetsize (self, VEC_HULL2_MIN, VEC_HULL2_MAX);\n\tself.health = 300;\n\n\tself.th_stand = demon1_stand1;\n\tself.th_walk = demon1_walk1;\n\tself.th_run = demon1_run1;\n\tself.th_die = demon_die;\n\tself.th_melee = Demon_MeleeAttack;\t\t// one of two attacks\n\tself.th_missile = demon1_jump1;\t\t\t// jump attack\n\tself.th_pain = demon1_pain;\n\t\t\n\twalkmonster_start();\n};\n\n\n/*\n==============================================================================\n\nDEMON\n\n==============================================================================\n*/\n\n/*\n==============\nCheckDemonMelee\n\nReturns TRUE if a melee attack would hit right now\n==============\n*/\nBOOL(float enemy_range) CheckDemonMelee =\n{\n\tif (enemy_range == RANGE_MELEE)\n\t{\t// FIXME: check canreach\n\t\tself.attack_state = AS_MELEE;\n\t\treturn TRUE;\n\t}\n\treturn FALSE;\n};\n\n/*\n==============\nCheckDemonJump\n\n==============\n*/\nBOOL()\tCheckDemonJump =\n{\n\tlocal\tvector\tdist;\n\tlocal\tfloat\td;\n\n\tif (self.origin_z + self.mins_z > self.enemy.origin_z + self.enemy.mins_z\n\t+ 0.75 * self.enemy.size_z)\n\t\treturn FALSE;\n\t\t\n\tif (self.origin_z + self.maxs_z < self.enemy.origin_z + self.enemy.mins_z\n\t+ 0.25 * self.enemy.size_z)\n\t\treturn FALSE;\n\t\t\n\tdist = self.enemy.origin - self.origin;\n\tdist_z = 0;\n\t\n\td = vlen(dist);\n\t\n\tif (d < 100)\n\t\treturn FALSE;\n\t\t\n\tif (d > 200)\n\t{\n\t\tif (random() < 0.9)\n\t\t\treturn FALSE;\n\t}\n\t\t\n\treturn TRUE;\n};\n\nBOOL(float enemy_range) DemonCheckAttack =\n{\n// if close enough for slashing, go for it\n\tif (CheckDemonMelee (enemy_range))\n\t{\n\t\tself.attack_state = AS_MELEE;\n\t\treturn TRUE;\n\t}\n\t\n\tif (CheckDemonJump ())\n\t{\n\t\tself.attack_state = AS_MISSILE;\n        sound (self, CHAN_VOICE, \"demon/djump.wav\", 1, ATTN_NORM);\n\t\treturn TRUE;\n\t}\n\t\n\treturn FALSE;\n};\n\n\n//===========================================================================\n\nvoid(float side)\tDemon_Melee =\n{\n\tlocal\tfloat\tldmg;\n\tlocal vector\tdelta;\n\t\n\tai_face ();\n\twalkmove (self.ideal_yaw, 12);\t// allow a little closing\n\n\tdelta = self.enemy.origin - self.origin;\n\n\tif (vlen(delta) > 100)\n\t\treturn;\n\tif (!CanDamage (self.enemy, self))\n\t\treturn;\n\t\t\n\tsound (self, CHAN_WEAPON, \"demon/dhit2.wav\", 1, ATTN_NORM);\n\tldmg = 10 + 5*random();\n\tT_Damage (self.enemy, self, self, ldmg, MOD_DEMON);\t\n\n\tmakevectors (self.angles);\n\tSpawnMeatSpray (self.origin + v_forward*16, side * v_right);\n};\n\n\nvoid()\tDemon_JumpTouch =\n{\n\tlocal\tfloat\tldmg;\n\n\tif (self.health <= 0)\n\t\treturn;\n\t\t\n\tif (other.takedamage)\n\t{\n\t\tif ( vlen(self.velocity) > 400 )\n\t\t{\n\t\t\tldmg = 40 + 10*random();\n\t\t\tT_Damage (other, self, self, ldmg, MOD_DEMON);\t\n\t\t}\n\t}\n\n\tif (!checkbottom(self))\n\t{\n\t\tif (self.flags & FL_ONGROUND)\n\t\t{\t// jump randomly to not get hung up\n\t\t\tself.touch = SUB_Null;\n\t\t\tself.think = demon1_jump1;\n\t\t\tself.nextthink = time + 0.1;\n\t\t}\n\t\treturn;\t// not on ground yet\n\t}\n\n\tself.touch = SUB_Null;\n\tself.think = demon1_jump11;\n\tself.nextthink = time + 0.1;\n};\n\n\u0000"
  },
  {
    "path": "quakec/basemod/dog.qc",
    "content": "/*\n==============================================================================\n\nDOG\n\n==============================================================================\n*/\n$cd id1/models/dog\n$origin 0 0 24\n$base base\n$skin skin\n\n$frame attack1 attack2 attack3 attack4 attack5 attack6 attack7 attack8\n\n$frame death1 death2 death3 death4 death5 death6 death7 death8 death9\n\n$frame deathb1 deathb2 deathb3 deathb4 deathb5 deathb6 deathb7 deathb8\n$frame deathb9\n\n$frame pain1 pain2 pain3 pain4 pain5 pain6\n\n$frame painb1 painb2 painb3 painb4 painb5 painb6 painb7 painb8 painb9 painb10\n$frame painb11 painb12 painb13 painb14 painb15 painb16\n\n$frame run1 run2 run3 run4 run5 run6 run7 run8 run9 run10 run11 run12\n\n$frame leap1 leap2 leap3 leap4 leap5 leap6 leap7 leap8 leap9\n\n$frame stand1 stand2 stand3 stand4 stand5 stand6 stand7 stand8 stand9\n\n$frame walk1 walk2 walk3 walk4 walk5 walk6 walk7 walk8\n\n\nvoid() dog_leap1;\nvoid() dog_run1;\n\n/*\n================\ndog_bite\n\n================\n*/\nvoid() dog_bite =\n{\nlocal vector\tdelta;\nlocal float \tldmg;\n\n\tif (!self.enemy)\n\t\treturn;\n\n\tai_charge(10);\n\n\tif (!CanDamage (self.enemy, self))\n\t\treturn;\n\n\tdelta = self.enemy.origin - self.origin;\n\n\tif (vlen(delta) > 100)\n\t\treturn;\n\t\t\n\tldmg = (random() + random() + random()) * 8;\n\tT_Damage (self.enemy, self, self, ldmg, MOD_DOG);\n};\n\nvoid()\tDog_JumpTouch =\n{\n\tlocal\tfloat\tldmg;\n\n\tif (self.health <= 0)\n\t\treturn;\n\t\t\n\tif (other.takedamage)\n\t{\n\t\tif ( vlen(self.velocity) > 300 )\n\t\t{\n\t\t\tldmg = 10 + 10*random();\n\t\t\tT_Damage (other, self, self, ldmg, MOD_DOG);\t\n\t\t}\n\t}\n\n\tif (!checkbottom(self))\n\t{\n\t\tif (self.flags & FL_ONGROUND)\n\t\t{\n\t\t\t// jump randomly to not get hung up\n\t\t\tself.touch = SUB_Null;\n\t\t\tself.think = dog_leap1;\n\t\t\tself.nextthink = time + 0.1;\n\t\t}\n\t\treturn;\t// not on ground yet\n\t}\n\n\tself.touch = SUB_Null;\n\tself.think = dog_run1;\n\tself.nextthink = time + 0.1;\n};\n\n\nvoid() dog_stand1\t=[\t$stand1,\tdog_stand2\t] {ai_stand();};\nvoid() dog_stand2\t=[\t$stand2,\tdog_stand3\t] {ai_stand();};\nvoid() dog_stand3\t=[\t$stand3,\tdog_stand4\t] {ai_stand();};\nvoid() dog_stand4\t=[\t$stand4,\tdog_stand5\t] {ai_stand();};\nvoid() dog_stand5\t=[\t$stand5,\tdog_stand6\t] {ai_stand();};\nvoid() dog_stand6\t=[\t$stand6,\tdog_stand7\t] {ai_stand();};\nvoid() dog_stand7\t=[\t$stand7,\tdog_stand8\t] {ai_stand();};\nvoid() dog_stand8\t=[\t$stand8,\tdog_stand9\t] {ai_stand();};\nvoid() dog_stand9\t=[\t$stand9,\tdog_stand1\t] {ai_stand();};\n\nvoid() dog_walk1\t=[\t$walk1 ,\tdog_walk2\t] {\nif (random() < 0.2)\n\tsound (self, CHAN_VOICE, \"dog/idle.wav\", 1, ATTN_IDLE);\nai_walk(8);};\nvoid() dog_walk2\t=[\t$walk2 ,\tdog_walk3\t] {ai_walk(8);};\nvoid() dog_walk3\t=[\t$walk3 ,\tdog_walk4\t] {ai_walk(8);};\nvoid() dog_walk4\t=[\t$walk4 ,\tdog_walk5\t] {ai_walk(8);};\nvoid() dog_walk5\t=[\t$walk5 ,\tdog_walk6\t] {ai_walk(8);};\nvoid() dog_walk6\t=[\t$walk6 ,\tdog_walk7\t] {ai_walk(8);};\nvoid() dog_walk7\t=[\t$walk7 ,\tdog_walk8\t] {ai_walk(8);};\nvoid() dog_walk8\t=[\t$walk8 ,\tdog_walk1\t] {ai_walk(8);};\n\nvoid() dog_run1\t\t=[\t$run1  ,\tdog_run2\t] {\nif (random() < 0.2)\n\tsound (self, CHAN_VOICE, \"dog/idle.wav\", 1, ATTN_IDLE);\nai_run(16);};\nvoid() dog_run2\t\t=[\t$run2  ,\tdog_run3\t] {ai_run(32);};\nvoid() dog_run3\t\t=[\t$run3  ,\tdog_run4\t] {ai_run(32);};\nvoid() dog_run4\t\t=[\t$run4  ,\tdog_run5\t] {ai_run(20);};\nvoid() dog_run5\t\t=[\t$run5  ,\tdog_run6\t] {ai_run(64);};\nvoid() dog_run6\t\t=[\t$run6  ,\tdog_run7\t] {ai_run(32);};\nvoid() dog_run7\t\t=[\t$run7  ,\tdog_run8\t] {ai_run(16);};\nvoid() dog_run8\t\t=[\t$run8  ,\tdog_run9\t] {ai_run(32);};\nvoid() dog_run9\t\t=[\t$run9  ,\tdog_run10\t] {ai_run(32);};\nvoid() dog_run10\t=[\t$run10  ,\tdog_run11\t] {ai_run(20);};\nvoid() dog_run11\t=[\t$run11  ,\tdog_run12\t] {ai_run(64);};\nvoid() dog_run12\t=[\t$run12  ,\tdog_run1\t] {ai_run(32);};\n\nvoid() dog_atta1\t=[\t$attack1,\tdog_atta2\t] {ai_charge(10);};\nvoid() dog_atta2\t=[\t$attack2,\tdog_atta3\t] {ai_charge(10);};\nvoid() dog_atta3\t=[\t$attack3,\tdog_atta4\t] {ai_charge(10);};\nvoid() dog_atta4\t=[\t$attack4,\tdog_atta5\t] {\nsound (self, CHAN_VOICE, \"dog/dattack1.wav\", 1, ATTN_NORM);\ndog_bite();};\nvoid() dog_atta5\t=[\t$attack5,\tdog_atta6\t] {ai_charge(10);};\nvoid() dog_atta6\t=[\t$attack6,\tdog_atta7\t] {ai_charge(10);};\nvoid() dog_atta7\t=[\t$attack7,\tdog_atta8\t] {ai_charge(10);};\nvoid() dog_atta8\t=[\t$attack8,\tdog_run1\t] {ai_charge(10);};\n\nvoid() dog_leap1\t=[\t$leap1,\t\tdog_leap2\t] {ai_face();};\nvoid() dog_leap2\t=[\t$leap2,\t\tdog_leap3\t]\n{\n\tai_face();\n\t\n\tself.touch = Dog_JumpTouch;\n\tmakevectors (self.angles);\n\tself.origin_z = self.origin_z + 1;\n\tself.velocity = v_forward * 300 + '0 0 200';\n\tif (self.flags & FL_ONGROUND)\n\t\tself.flags = self.flags - FL_ONGROUND;\n};\n\nvoid() dog_leap3\t=[\t$leap3,\t\tdog_leap4\t] {};\nvoid() dog_leap4\t=[\t$leap4,\t\tdog_leap5\t] {};\nvoid() dog_leap5\t=[\t$leap5,\t\tdog_leap6\t] {};\nvoid() dog_leap6\t=[\t$leap6,\t\tdog_leap7\t] {};\nvoid() dog_leap7\t=[\t$leap7,\t\tdog_leap8\t] {};\nvoid() dog_leap8\t=[\t$leap8,\t\tdog_leap9\t] {};\nvoid() dog_leap9\t=[\t$leap9,\t\tdog_leap9\t] {};\n\nvoid() dog_pain1\t=[\t$pain1 ,\tdog_pain2\t] {};\nvoid() dog_pain2\t=[\t$pain2 ,\tdog_pain3\t] {};\nvoid() dog_pain3\t=[\t$pain3 ,\tdog_pain4\t] {};\nvoid() dog_pain4\t=[\t$pain4 ,\tdog_pain5\t] {};\nvoid() dog_pain5\t=[\t$pain5 ,\tdog_pain6\t] {};\nvoid() dog_pain6\t=[\t$pain6 ,\tdog_run1\t] {};\n\nvoid() dog_painb1\t=[\t$painb1 ,\tdog_painb2\t] {};\nvoid() dog_painb2\t=[\t$painb2 ,\tdog_painb3\t] {};\nvoid() dog_painb3\t=[\t$painb3 ,\tdog_painb4\t] {ai_pain(4);};\nvoid() dog_painb4\t=[\t$painb4 ,\tdog_painb5\t] {ai_pain(12);};\nvoid() dog_painb5\t=[\t$painb5 ,\tdog_painb6\t] {ai_pain(12);};\nvoid() dog_painb6\t=[\t$painb6 ,\tdog_painb7\t] {ai_pain(2);};\nvoid() dog_painb7\t=[\t$painb7 ,\tdog_painb8\t] {};\nvoid() dog_painb8\t=[\t$painb8 ,\tdog_painb9\t] {ai_pain(4);};\nvoid() dog_painb9\t=[\t$painb9 ,\tdog_painb10\t] {};\nvoid() dog_painb10\t=[\t$painb10 ,\tdog_painb11\t] {ai_pain(10);};\nvoid() dog_painb11\t=[\t$painb11 ,\tdog_painb12\t] {};\nvoid() dog_painb12\t=[\t$painb12 ,\tdog_painb13\t] {};\nvoid() dog_painb13\t=[\t$painb13 ,\tdog_painb14\t] {};\nvoid() dog_painb14\t=[\t$painb14 ,\tdog_painb15\t] {};\nvoid() dog_painb15\t=[\t$painb15 ,\tdog_painb16\t] {};\nvoid() dog_painb16\t=[\t$painb16 ,\tdog_run1\t] {};\n\nvoid() dog_pain =\n{\n\tsound (self, CHAN_VOICE, \"dog/dpain1.wav\", 1, ATTN_NORM);\n\n\tif (random() > 0.5)\n\t\tdog_pain1 ();\n\telse\n\t\tdog_painb1 ();\n};\n\nvoid() dog_die1\t\t=[\t$death1,\tdog_die2\t] {};\nvoid() dog_die2\t\t=[\t$death2,\tdog_die3\t] {};\nvoid() dog_die3\t\t=[\t$death3,\tdog_die4\t] {};\nvoid() dog_die4\t\t=[\t$death4,\tdog_die5\t] {};\nvoid() dog_die5\t\t=[\t$death5,\tdog_die6\t] {};\nvoid() dog_die6\t\t=[\t$death6,\tdog_die7\t] {};\nvoid() dog_die7\t\t=[\t$death7,\tdog_die8\t] {};\nvoid() dog_die8\t\t=[\t$death8,\tdog_die9\t] {};\nvoid() dog_die9\t\t=[\t$death9,\tdog_die9\t] {};\n\nvoid() dog_dieb1\t\t=[\t$deathb1,\tdog_dieb2\t] {};\nvoid() dog_dieb2\t\t=[\t$deathb2,\tdog_dieb3\t] {};\nvoid() dog_dieb3\t\t=[\t$deathb3,\tdog_dieb4\t] {};\nvoid() dog_dieb4\t\t=[\t$deathb4,\tdog_dieb5\t] {};\nvoid() dog_dieb5\t\t=[\t$deathb5,\tdog_dieb6\t] {};\nvoid() dog_dieb6\t\t=[\t$deathb6,\tdog_dieb7\t] {};\nvoid() dog_dieb7\t\t=[\t$deathb7,\tdog_dieb8\t] {};\nvoid() dog_dieb8\t\t=[\t$deathb8,\tdog_dieb9\t] {};\nvoid() dog_dieb9\t\t=[\t$deathb9,\tdog_dieb9\t] {};\n\n\nvoid() dog_die =\n{\n// check for gib\n\tif (self.health < -35)\n\t{\n\t\tsound (self, CHAN_VOICE, \"player/udeath.wav\", 1, ATTN_NORM);\n\t\tThrowGib (\"progs/gib3.mdl\", self.health);\n\t\tThrowGib (\"progs/gib3.mdl\", self.health);\n\t\tThrowGib (\"progs/gib3.mdl\", self.health);\n\t\tThrowHead (\"progs/h_dog.mdl\", self.health);\n\t\treturn;\n\t}\n\n// regular death\n\tsound (self, CHAN_VOICE, \"dog/ddeath.wav\", 1, ATTN_NORM);\n\tself.solid = SOLID_NOT;\n\n\tif (random() > 0.5)\n\t\tdog_die1 ();\n\telse\n\t\tdog_dieb1 ();\n};\n\n//============================================================================\n\n/*\n==============\nCheckDogMelee\n\nReturns TRUE if a melee attack would hit right now\n==============\n*/\nBOOL(float enemy_range)\tCheckDogMelee =\n{\n\tif (enemy_range == RANGE_MELEE)\n\t{\t// FIXME: check canreach\n\t\tself.attack_state = AS_MELEE;\n\t\treturn TRUE;\n\t}\n\treturn FALSE;\n};\n\n/*\n==============\nCheckDogJump\n\n==============\n*/\nBOOL()\tCheckDogJump =\n{\n\tlocal\tvector\tdist;\n\tlocal\tfloat\td;\n\n\tif (self.origin_z + self.mins_z > self.enemy.origin_z + self.enemy.mins_z\n\t+ 0.75 * self.enemy.size_z)\n\t\treturn FALSE;\n\t\t\n\tif (self.origin_z + self.maxs_z < self.enemy.origin_z + self.enemy.mins_z\n\t+ 0.25 * self.enemy.size_z)\n\t\treturn FALSE;\n\t\t\n\tdist = self.enemy.origin - self.origin;\n\tdist_z = 0;\n\t\n\td = vlen(dist);\n\t\n\tif (d < 80)\n\t\treturn FALSE;\n\t\t\n\tif (d > 150)\n\t\treturn FALSE;\n\t\t\n\treturn TRUE;\n};\n\nBOOL(float enemy_range)\tDogCheckAttack =\n{\n// if close enough for slashing, go for it\n\tif (CheckDogMelee (enemy_range))\n\t{\n\t\tself.attack_state = AS_MELEE;\n\t\treturn TRUE;\n\t}\n\t\n\tif (CheckDogJump ())\n\t{\n\t\tself.attack_state = AS_MISSILE;\n\t\treturn TRUE;\n\t}\n\t\n\treturn FALSE;\n};\n\n\n//===========================================================================\n\n/*QUAKED monster_dog (1 0 0) (-32 -32 -24) (32 32 40) Ambush\n\n*/\nvoid() monster_dog =\n{\n\tif (deathmatch)\n\t{\n\t\tremove(self);\n\t\treturn;\n\t}\n\tprecache_model (\"progs/h_dog.mdl\");\n\tprecache_model (\"progs/dog.mdl\");\n\n\tprecache_sound (\"dog/dattack1.wav\");\n\tprecache_sound (\"dog/ddeath.wav\");\n\tprecache_sound (\"dog/dpain1.wav\");\n\tprecache_sound (\"dog/dsight.wav\");\n\tprecache_sound (\"dog/idle.wav\");\n\n\tself.solid = SOLID_SLIDEBOX;\n\tself.movetype = MOVETYPE_STEP;\n\n\tsetmodel (self, \"progs/dog.mdl\");\n\n\tsetsize (self, '-32 -32 -24', '32 32 40');\n\tself.health = 25;\n\n\tself.th_stand = dog_stand1;\n\tself.th_walk = dog_walk1;\n\tself.th_run = dog_run1;\n\tself.th_pain = dog_pain;\n\tself.th_die = dog_die;\n\tself.th_melee = dog_atta1;\n\tself.th_missile = dog_leap1;\n\n\twalkmonster_start();\n};\n\u0000"
  },
  {
    "path": "quakec/basemod/doors.qc",
    "content": "\nfloat DOOR_START_OPEN = 1;\nfloat DOOR_DONT_LINK = 4;\nfloat DOOR_GOLD_KEY = 8;\nfloat DOOR_SILVER_KEY = 16;\nfloat DOOR_TOGGLE = 32;\n\n/*\n\nDoors are similar to buttons, but can spawn a fat trigger field around them\nto open without a touch, and they link together to form simultanious\ndouble/quad doors.\n \nDoor.owner is the master door.  If there is only one door, it points to itself.\nIf multiple doors, all will point to a single one.\n\nDoor.enemy chains from the master door through all doors linked in the chain.\n\n*/\n\n/*\n=============================================================================\n\nTHINK FUNCTIONS\n\n=============================================================================\n*/\n\nvoid() door_go_down;\nvoid() door_go_up;\n\nvoid() door_blocked =\n{\n\tT_Damage (other, self, self.goalentity, self.dmg, MOD_SQUISH);\n\n// if a door has a negative wait, it would never come back if blocked,\n// so let it just squash the object to death real fast\n\tif (self.wait >= 0)\n\t{\n\t\tif (self.state == STATE_DOWN)\n\t\t\tdoor_go_up ();\n\t\telse\n\t\t\tdoor_go_down ();\n\t}\n};\n\n\nvoid() door_hit_top =\n{\n\tsound (self, chan_no_phs_add|CHAN_VOICE, self.noise1, 1, ATTN_NORM);\n\tself.state = STATE_TOP;\n\tif (self.spawnflags & DOOR_TOGGLE)\n\t\treturn;         // don't come down automatically\n\tself.think = door_go_down;\n\tself.nextthink = self.ltime + self.wait;\n};\n\nvoid() door_hit_bottom =\n{\n\tsound (self, chan_no_phs_add|CHAN_VOICE, self.noise1, 1, ATTN_NORM);\n\tself.state = STATE_BOTTOM;\n};\n\nvoid() door_go_down =\n{\n\tsound (self, CHAN_VOICE, self.noise2, 1, ATTN_NORM);\n\tif (self.max_health)\n\t{\n\t\tself.takedamage = DAMAGE_YES;\n\t\tself.health = self.max_health;\n\t}\n\t\n\tself.state = STATE_DOWN;\n\tSUB_CalcMove (self.pos1, self.speed, door_hit_bottom);\n};\n\nvoid() door_go_up =\n{\n\tif (self.state == STATE_UP)\n\t\treturn;         // allready going up\n\n\tif (self.state == STATE_TOP)\n\t{       // reset top wait time\n\t\tself.nextthink = self.ltime + self.wait;\n\t\treturn;\n\t}\n\t\n\tsound (self, CHAN_VOICE, self.noise2, 1, ATTN_NORM);\n\tself.state = STATE_UP;\n\tSUB_CalcMove (self.pos2, self.speed, door_hit_top);\n\n\tSUB_UseTargets();\n};\n\n\n/*\n=============================================================================\n\nACTIVATION FUNCTIONS\n\n=============================================================================\n*/\n\nvoid() door_fire =\n{\n\tlocal entity    oself;\n\tlocal entity    starte;\n\n\tif (self.owner != self)\n\t\tobjerror (\"door_fire: self.owner != self\");\n\n// play use key sound\n\n\tif (self.items)\n\t\tsound (self, CHAN_ITEM, self.noise4, 1, ATTN_NORM);\n\n\tself.message = string_null;             // no more message\n\toself = self;\n\n\tif (self.spawnflags & DOOR_TOGGLE)\n\t{\n\t\tif (self.state == STATE_UP || self.state == STATE_TOP)\n\t\t{\n\t\t\tstarte = self;\n\t\t\tdo\n\t\t\t{\n\t\t\t\tdoor_go_down ();\n\t\t\t\tself = self.enemy;\n\t\t\t} while ( (self != starte) && (self != world) );\n\t\t\tself = oself;\n\t\t\treturn;\n\t\t}\n\t}\n\t\n// trigger all paired doors\n\tstarte = self;\n\t\n\tdo\n\t{\n\t\tself.goalentity = activator;\t\t// Who fired us\n\t\tdoor_go_up ();\n\t\tself = self.enemy;\n\t} while ( (self != starte) && (self != world) );\n\tself = oself;\n};\n\n\nvoid() door_use =\n{\n\tlocal entity oself;\n\n\tself.message = \"\";                      // door message are for touch only\n\tself.owner.message = \"\";        \n\tself.enemy.message = \"\";\n\n\toself = self;\n\tself = self.owner;\n\tdoor_fire ();\n\tself = oself;\n};\n\n\nvoid() door_trigger_touch =\n{\n\tif (other.health <= 0)\n\t\treturn;\n\n\tif (time < self.attack_finished)\n\t\treturn;\n\tself.attack_finished = time + 1;\n\n\tactivator = other;\n\n\tself = self.owner;\n\tdoor_use ();\n};\n\n\nvoid() door_killed =\n{\n\tlocal entity oself;\n\t\n\toself = self;\n\tself = self.owner;\n\tself.health = self.max_health;\n\tself.takedamage = DAMAGE_NO;    // wil be reset upon return\n\tdoor_use ();\n\tself = oself;\n};\n\n\n/*\n================\ndoor_touch\n\nPrints messages and opens key doors\n================\n*/\nvoid() door_touch =\n{\n\tif (other.classname != \"player\")\n\t\treturn;\n\tif (self.owner.attack_finished > time)\n\t\treturn;\n\n\tself.owner.attack_finished = time + 2;\n\n\tif (self.owner.message != \"\")\n\t{\n\t\tcenterprint (other, self.owner.message);\n\t\tsound (other, CHAN_VOICE, \"misc/talk.wav\", 1, ATTN_NORM);\n\t}\n\t\n// key door stuff\n\tif (!self.items)\n\t\treturn;\n\n// FIXME: blink key on player's status bar\n\tif ( (self.items & other.items) != self.items )\n\t{\n\t\tif (self.owner.items == IT_KEY1)\n\t\t{\n\t\t\tswitch (world.worldtype)\n\t\t\t{\n\t\t\tcase WT_MEDIEVAL:\n\t\t\t\tcenterprint (other, \"You need the silver key\");\n\t\t\t\tbreak;\n\t\t\tcase WT_METAL:\n\t\t\t\tcenterprint (other, \"You need the silver runekey\");\n\t\t\t\tbreak;\n\t\t\tcase WT_BASE:\n\t\t\t\tcenterprint (other, \"You need the silver keycard\");\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tswitch (world.worldtype)\n\t\t\t{\n\t\t\tcase WT_MEDIEVAL:\n\t\t\t\tcenterprint (other, \"You need the gold key\");\n\t\t\t\tbreak;\n\t\t\tcase WT_METAL:\n\t\t\t\tcenterprint (other, \"You need the gold runekey\");\n\t\t\t\tbreak;\n\t\t\tcase WT_BASE:\n\t\t\t\tcenterprint (other, \"You need the gold keycard\");\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tsound (self, CHAN_VOICE, self.noise3, 1, ATTN_NORM);\n\t\treturn;\n\t}\n\n\tother.items = other.items - self.items;\n\tself.touch = SUB_Null;\n\tif (self.enemy)\n\t\tself.enemy.touch = SUB_Null;    // get paired door\n\tdoor_use ();\n};\n\n/*\n=============================================================================\n\nSPAWNING FUNCTIONS\n\n=============================================================================\n*/\n\n\nentity(vector fmins, vector fmaxs) spawn_field =\n{\n\tlocal entity    trigger;\n\tlocal   vector  t1, t2;\n\n\ttrigger = spawn();\n\ttrigger.movetype = MOVETYPE_NONE;\n\ttrigger.solid = SOLID_TRIGGER;\n\ttrigger.owner = self;\n\ttrigger.touch = door_trigger_touch;\n\n\tt1 = fmins;\n\tt2 = fmaxs;\n\tsetsize (trigger, t1 - '60 60 8', t2 + '60 60 8');\n\treturn (trigger);\n};\n\n\nBOOL (entity e1, entity e2) EntitiesTouching =\n{\n\tif (e1.mins_x > e2.maxs_x)\n\t\treturn FALSE;\n\tif (e1.mins_y > e2.maxs_y)\n\t\treturn FALSE;\n\tif (e1.mins_z > e2.maxs_z)\n\t\treturn FALSE;\n\tif (e1.maxs_x < e2.mins_x)\n\t\treturn FALSE;\n\tif (e1.maxs_y < e2.mins_y)\n\t\treturn FALSE;\n\tif (e1.maxs_z < e2.mins_z)\n\t\treturn FALSE;\n\treturn TRUE;\n};\n\n\n/*\n=============\nLinkDoors\n\n\n=============\n*/\nvoid() LinkDoors =\n{\n\tlocal entity    t, starte;\n\tlocal vector    cmins, cmaxs;\n\n\tif (self.enemy)\n\t\treturn;         // already linked by another door\n\tif (self.spawnflags & 4)\n\t{\n\t\tself.owner = self.enemy = self;\n\t\treturn;         // don't want to link this door\n\t}\n\n\tcmins = self.mins;\n\tcmaxs = self.maxs;\n\t\n\tstarte = self;\n\tt = self;\n\t\n\tdo\n\t{\n\t\tself.owner = starte;                    // master door\n\n\t\tif (self.health)\n\t\t\tstarte.health = self.health;\n\t\tif (self.targetname)\n\t\t\tstarte.targetname = self.targetname;\n\t\tif (self.message != \"\")\n\t\t\tstarte.message = self.message;\n\n\t\tt = find (t, classname, self.classname);        \n\t\tif (!t)\n\t\t{\n\t\t\tself.enemy = starte;            // make the chain a loop\n\n\t\t// shootable, fired, or key doors just needed the owner/enemy links,\n\t\t// they don't spawn a field\n\t\n\t\t\tself = self.owner;\n\n\t\t\tif (self.health)\n\t\t\t\treturn;\n\t\t\tif (self.targetname)\n\t\t\t\treturn;\n\t\t\tif (self.items)\n\t\t\t\treturn;\n\n\t\t\tself.owner.trigger_field = spawn_field(cmins, cmaxs);\n\n\t\t\treturn;\n\t\t}\n\n\t\tif (EntitiesTouching(self,t))\n\t\t{\n\t\t\tif (t.enemy)\n\t\t\t\tobjerror (\"cross connected doors\");\n\t\t\t\n\t\t\tself.enemy = t;\n\t\t\tself = t;\n\n\t\t\tif (t.mins_x < cmins_x)\n\t\t\t\tcmins_x = t.mins_x;\n\t\t\tif (t.mins_y < cmins_y)\n\t\t\t\tcmins_y = t.mins_y;\n\t\t\tif (t.mins_z < cmins_z)\n\t\t\t\tcmins_z = t.mins_z;\n\t\t\tif (t.maxs_x > cmaxs_x)\n\t\t\t\tcmaxs_x = t.maxs_x;\n\t\t\tif (t.maxs_y > cmaxs_y)\n\t\t\t\tcmaxs_y = t.maxs_y;\n\t\t\tif (t.maxs_z > cmaxs_z)\n\t\t\t\tcmaxs_z = t.maxs_z;\n\t\t}\n\t} while (1 );\n\n};\n\n\n/*QUAKED func_door (0 .5 .8) ? START_OPEN x DOOR_DONT_LINK GOLD_KEY SILVER_KEY TOGGLE\nif two doors touch, they are assumed to be connected and operate as a unit.\n\nTOGGLE causes the door to wait in both the start and end states for a trigger event.\n\nSTART_OPEN causes the door to move to its destination when spawned, and operate in reverse.  It is used to temporarily or permanently close off an area when triggered (not usefull for touch or takedamage doors).\n\nKey doors are allways wait -1.\n\n\"message\"       is printed when the door is touched if it is a trigger door and it hasn't been fired yet\n\"angle\"         determines the opening direction\n\"targetname\" if set, no touch field will be spawned and a remote button or trigger field activates the door.\n\"health\"        if set, door must be shot open\n\"speed\"         movement speed (100 default)\n\"wait\"          wait before returning (3 default, -1 = never return)\n\"lip\"           lip remaining at end of move (8 default)\n\"dmg\"           damage to inflict when blocked (2 default)\n\"sounds\"\n0)      no sound\n1)      stone\n2)      base\n3)      stone chain\n4)      screechy metal\n*/\n\nvoid() func_door =\n{\n\tswitch (world.worldtype)\n\t{\n\tcase WT_MEDIEVAL:\n\t\tprecache_sound (\"doors/medtry.wav\");\n\t\tprecache_sound (\"doors/meduse.wav\");\n\t\tself.noise3 = \"doors/medtry.wav\";\n\t\tself.noise4 = \"doors/meduse.wav\";\n\t\tbreak;\n\tcase WT_METAL:\n\t\tprecache_sound (\"doors/runetry.wav\");\n\t\tprecache_sound (\"doors/runeuse.wav\");\n\t\tself.noise3 = \"doors/runetry.wav\";\n\t\tself.noise4 = \"doors/runeuse.wav\";\n\t\tbreak;\n\tcase WT_BASE:\n\t\tprecache_sound (\"doors/basetry.wav\");\n\t\tprecache_sound (\"doors/baseuse.wav\");\n\t\tself.noise3 = \"doors/basetry.wav\";\n\t\tself.noise4 = \"doors/baseuse.wav\";\n\t\tbreak;\n\tdefault:\n\t\tdprint (\"no worldtype set!\\n\");\n\t}\n\n\tswitch (self.sounds)\n\t{\n\tcase 0:\n\t\tprecache_sound (\"misc/null.wav\");\n\t\tprecache_sound (\"misc/null.wav\");\n\t\tself.noise1 = \"misc/null.wav\";\n\t\tself.noise2 = \"misc/null.wav\";\n\t\tbreak;\n\tcase 1:\n\t\tprecache_sound (\"doors/drclos4.wav\");\n\t\tprecache_sound (\"doors/doormv1.wav\");\n\t\tself.noise1 = \"doors/drclos4.wav\";\n\t\tself.noise2 = \"doors/doormv1.wav\";\n\t\tbreak;\n\tcase 2:\n\t\tprecache_sound (\"doors/hydro1.wav\");\n\t\tprecache_sound (\"doors/hydro2.wav\");\n\t\tself.noise2 = \"doors/hydro1.wav\";\n\t\tself.noise1 = \"doors/hydro2.wav\";\n\t\tbreak;\n\tcase 3:\n\t\tprecache_sound (\"doors/stndr1.wav\");\n\t\tprecache_sound (\"doors/stndr2.wav\");\n\t\tself.noise2 = \"doors/stndr1.wav\";\n\t\tself.noise1 = \"doors/stndr2.wav\";\n\t\tbreak;\n\tcase 4:\n\t\tprecache_sound (\"doors/ddoor1.wav\");\n\t\tprecache_sound (\"doors/ddoor2.wav\");\n\t\tself.noise1 = \"doors/ddoor2.wav\";\n\t\tself.noise2 = \"doors/ddoor1.wav\";\n\t\tbreak;\n\t}\n\n\tSetMovedir ();\n\n\tself.max_health = self.health;\n\tself.solid = SOLID_BSP;\n\tself.movetype = MOVETYPE_PUSH;\n\tsetorigin (self, self.origin);  \n\tsetmodel (self, self.model);\n\tself.classname = \"door\";\n\n\tself.blocked = door_blocked;\n\tself.use = door_use;\n\t\n\tif (self.spawnflags & DOOR_SILVER_KEY)\n\t\tself.items = IT_KEY1;\n\tif (self.spawnflags & DOOR_GOLD_KEY)\n\t\tself.items = IT_KEY2;\n\t\n\tif (!self.speed)\n\t\tself.speed = 100;\n\tif (!self.wait)\n\t\tself.wait = 3;\n\tif (!self.lip)\n\t\tself.lip = 8;\n\tif (!self.dmg)\n\t\tself.dmg = 2;\n\n\tself.pos1 = self.origin;\n\tself.pos2 = self.pos1 + self.movedir*(fabs(self.movedir*self.size) - self.lip);\n\n// DOOR_START_OPEN is to allow an entity to be lighted in the closed position\n// but spawn in the open position\n\tif (self.spawnflags & DOOR_START_OPEN)\n\t{\n\t\tsetorigin (self, self.pos2);\n\t\tself.pos2 = self.pos1;\n\t\tself.pos1 = self.origin;\n\t}\n\n\tself.state = STATE_BOTTOM;\n\n\tif (self.health)\n\t{\n\t\tself.takedamage = DAMAGE_YES;\n\t\tself.th_die = door_killed;\n\t}\n\t\n\tif (self.items)\n\t\tself.wait = -1;\n\t\t\n\tself.touch = door_touch;\n\n// LinkDoors can't be done until all of the doors have been spawned, so\n// the sizes can be detected properly.\n\tself.think = LinkDoors;\n\tself.nextthink = self.ltime + 0.1;\n};\n\n/*\n=============================================================================\n\nSECRET DOORS\n\n=============================================================================\n*/\n\nvoid() fd_secret_move1;\nvoid() fd_secret_move2;\nvoid() fd_secret_move3;\nvoid() fd_secret_move4;\nvoid() fd_secret_move5;\nvoid() fd_secret_move6;\nvoid() fd_secret_done;\n\nfloat SECRET_OPEN_ONCE = 1;             // stays open\nfloat SECRET_1ST_LEFT = 2;              // 1st move is left of arrow\nfloat SECRET_1ST_DOWN = 4;              // 1st move is down from arrow\nfloat SECRET_NO_SHOOT = 8;              // only opened by trigger\nfloat SECRET_YES_SHOOT = 16;    // shootable even if targeted\n\n\nvoid () fd_secret_use =\n{\n\tlocal float temp;\n\t\n\tself.health = 10000;\n\n\t// exit if still moving around...\n\tif (self.origin != self.oldorigin)\n\t\treturn;\n\t\n\tself.message = string_null;             // no more message\n\n\tSUB_UseTargets();                               // fire all targets / killtargets\n\t\n\tif (!(self.spawnflags & SECRET_NO_SHOOT))\n\t{\n\t\tself.th_pain = SUB_Null;\n\t\tself.takedamage = DAMAGE_NO;\n\t}\n\tself.velocity = '0 0 0';\n\n\t// Make a sound, wait a little...\n\t\n\tsound(self, CHAN_VOICE, self.noise1, 1, ATTN_NORM);\n\tself.nextthink = self.ltime + 0.1;\n\n\ttemp = 1 - (self.spawnflags & SECRET_1ST_LEFT); // 1 or -1\n\tmakevectors(self.mangle);\n\t\n\tif (!self.t_width)\n\t{\n\t\tif (self.spawnflags & SECRET_1ST_DOWN)\n\t\t\tself.t_width = fabs(v_up * self.size);\n\t\telse\n\t\t\tself.t_width = fabs(v_right * self.size);\n\t}\n\t\t\n\tif (!self.t_length)\n\t\tself.t_length = fabs(v_forward * self.size);\n\n\tif (self.spawnflags & SECRET_1ST_DOWN)\n\t\tself.dest1 = self.origin - v_up * self.t_width;\n\telse\n\t\tself.dest1 = self.origin + v_right * (self.t_width * temp);\n\t\t\n\tself.dest2 = self.dest1 + v_forward * self.t_length;\n\tSUB_CalcMove(self.dest1, self.speed, fd_secret_move1);\n\tsound(self, CHAN_VOICE, self.noise2, 1, ATTN_NORM);\n};\n\n// Wait after first movement...\nvoid () fd_secret_move1 = \n{\n\tself.nextthink = self.ltime + 1.0;\n\tself.think = fd_secret_move2;\n\tsound(self, CHAN_VOICE, self.noise3, 1, ATTN_NORM);\n};\n\n// Start moving sideways w/sound...\nvoid () fd_secret_move2 =\n{\n\tsound(self, CHAN_VOICE, self.noise2, 1, ATTN_NORM);\n\tSUB_CalcMove(self.dest2, self.speed, fd_secret_move3);\n};\n\n// Wait here until time to go back...\nvoid () fd_secret_move3 =\n{\n\tsound(self, CHAN_VOICE, self.noise3, 1, ATTN_NORM);\n\tif (!(self.spawnflags & SECRET_OPEN_ONCE))\n\t{\n\t\tself.nextthink = self.ltime + self.wait;\n\t\tself.think = fd_secret_move4;\n\t}\n};\n\n// Move backward...\nvoid () fd_secret_move4 =\n{\n\tsound(self, CHAN_VOICE, self.noise2, 1, ATTN_NORM);\n\tSUB_CalcMove(self.dest1, self.speed, fd_secret_move5);          \n};\n\n// Wait 1 second...\nvoid () fd_secret_move5 = \n{\n\tself.nextthink = self.ltime + 1.0;\n\tself.think = fd_secret_move6;\n\tsound(self, CHAN_VOICE, self.noise3, 1, ATTN_NORM);\n};\n\nvoid () fd_secret_move6 =\n{\n\tsound(self, CHAN_VOICE, self.noise2, 1, ATTN_NORM);\n\tSUB_CalcMove(self.oldorigin, self.speed, fd_secret_done);\n};\n\nvoid () fd_secret_done =\n{\n\tif (!self.targetname || self.spawnflags&SECRET_YES_SHOOT)\n\t{\n\t\tself.health = 10000;\n\t\tself.takedamage = DAMAGE_YES;\n\t\tself.th_pain = fd_secret_use;   \n\t\tself.th_die = fd_secret_use;\n\t}\n\tsound(self, chan_no_phs_add|CHAN_VOICE, self.noise3, 1, ATTN_NORM);\n};\n\nvoid () secret_blocked =\n{\n\tif (time < self.attack_finished)\n\t\treturn;\n\tself.attack_finished = time + 0.5;\n\tT_Damage (other, self, self, self.dmg, MOD_SQUISH);\n};\n\n/*\n================\nsecret_touch\n\nPrints messages\n================\n*/\nvoid() secret_touch =\n{\n\tif (other.classname != \"player\")\n\t\treturn;\n\tif (self.attack_finished > time)\n\t\treturn;\n\n\tself.attack_finished = time + 2;\n\t\n\tif (self.message)\n\t{\n\t\tcenterprint (other, self.message);\n\t\tsound (other, CHAN_BODY, \"misc/talk.wav\", 1, ATTN_NORM);\n\t}\n};\n\n\n/*QUAKED func_door_secret (0 .5 .8) ? open_once 1st_left 1st_down no_shoot always_shoot\nBasic secret door. Slides back, then to the side. Angle determines direction.\nwait  = # of seconds before coming back\n1st_left = 1st move is left of arrow\n1st_down = 1st move is down from arrow\nalways_shoot = even if targeted, keep shootable\nt_width = override WIDTH to move back (or height if going down)\nt_length = override LENGTH to move sideways\n\"dmg\"           damage to inflict when blocked (2 default)\n\nIf a secret door has a targetname, it will only be opened by it's botton or trigger, not by damage.\n\"sounds\"\n1) medieval\n2) metal\n3) base\n*/\n\nvoid () func_door_secret =\n{\n\tswitch (self.sounds)\n\t{\n\tcase 0:\n\tcase 3:\n\t\tprecache_sound (\"doors/basesec1.wav\");\n\t\tprecache_sound (\"doors/basesec2.wav\");\n\t\tself.noise2 = \"doors/basesec1.wav\";\n\t\tself.noise1 = \"doors/basesec2.wav\";\n\t\tself.noise3 = \"doors/basesec2.wav\";\n\t\tbreak;\n\tcase 1:\n\t\tprecache_sound (\"doors/latch2.wav\");\n\t\tprecache_sound (\"doors/winch2.wav\");\n\t\tprecache_sound (\"doors/drclos4.wav\");\n\t\tself.noise1 = \"doors/latch2.wav\";\n\t\tself.noise2 = \"doors/winch2.wav\";\n\t\tself.noise3 = \"doors/drclos4.wav\";\n\t\tbreak;\n\tcase 2:\n\t\tprecache_sound (\"doors/airdoor1.wav\");\n\t\tprecache_sound (\"doors/airdoor2.wav\");\n\t\tself.noise2 = \"doors/airdoor1.wav\";\n\t\tself.noise1 = \"doors/airdoor2.wav\";\n\t\tself.noise3 = \"doors/airdoor2.wav\";\n\t\tbreak;\n\t}\n\n\tif (!self.dmg)\n\t\tself.dmg = 2;\n\t\t\n\t// Magic formula...\n\tself.mangle = self.angles;\n\tself.angles = '0 0 0';\n\tself.solid = SOLID_BSP;\n\tself.movetype = MOVETYPE_PUSH;\n\tself.classname = \"door\";\n\tsetmodel (self, self.model);\n\tsetorigin (self, self.origin);  \n\t\n\tself.touch = secret_touch;\n\tself.blocked = secret_blocked;\n\tself.speed = 50;\n\tself.use = fd_secret_use;\n\tif ( !self.targetname || self.spawnflags&SECRET_YES_SHOOT)\n\t{\n\t\tself.health = 10000;\n\t\tself.takedamage = DAMAGE_YES;\n\t\tself.th_pain = fd_secret_use;\n\t}\n\tself.oldorigin = self.origin;\n\tif (!self.wait)\n\t\tself.wait = 5;          // 5 seconds before closing\n}; "
  },
  {
    "path": "quakec/basemod/effects.qc",
    "content": "\n// Effects code (EFF) --\n\n// TE effects/muzzleflash/messages\n\n// modular efs\nvar float ef_red  = 0;           // used for glow on pent item\nvar float ef_pent = EF_DIMLIGHT; // used for pent effect on player\nvar float ef_blue = 0;           // used for glow on quad\nvar float ef_quad = EF_DIMLIGHT; // used for quad effect on player\n\n// base te write functions\nvoid WriteVector (float msg, vector org) =\n{\n\tWriteCoord (msg, org_x);\n\tWriteCoord (msg, org_y);\n\tWriteCoord (msg, org_z);\n};\n\n#ifdef NETQUAKE\nvoid(vector org, float effect) _EFF_PointEffect =\n{\n\tWriteByte (MSG_BROADCAST, SVC_TEMPENTITY);\n\tWriteByte (MSG_BROADCAST, effect);\n\tWriteVector (MSG_BROADCAST, org);\n};\n\nvoid(entity ent, vector org, vector org2, float effect) _EFF_BeamEffect =\n{\n\tWriteByte (MSG_BROADCAST, SVC_TEMPENTITY);\n\tWriteByte (MSG_BROADCAST, effect);\n\tWriteEntity (MSG_BROADCAST, ent);\n\tWriteVector (MSG_BROADCAST, org);\n\tWriteVector (MSG_BROADCAST, org2);\n};\n\nvoid(vector org, float damage, float effect) _EFF_CountEffect =\n{\n\tWriteByte (MSG_BROADCAST, SVC_TEMPENTITY);\n\tWriteByte (MSG_BROADCAST, effect);\n\tWriteByte (MSG_BROADCAST, damage);\n\tWriteVector (MSG_BROADCAST, org);\n};\n\n#define EFF_PointEffect(a,b,c) _EFF_PointEffect(a,b)\n#define EFF_BeamEffect(a,b,c,d,e) _EFF_BeamEffect(a,b,c,d)\n#define EFF_CountEffect(a,b,c,d) _EFF_CountEffect(a,b,c)\n#else\nvoid(vector org, float effect, float mcast) EFF_PointEffect =\n{\n\tWriteByte (MSG_MULTICAST, SVC_TEMPENTITY);\n\tWriteByte (MSG_MULTICAST, effect);\n\tWriteVector (MSG_MULTICAST, org);\n\tmulticast (org, mcast);\n};\n\nvoid(entity ent, vector org, vector org2, float effect, float mcast) EFF_BeamEffect =\n{\n\tWriteByte (MSG_MULTICAST, SVC_TEMPENTITY);\n\tWriteByte (MSG_MULTICAST, effect);\n\tWriteEntity (MSG_MULTICAST, ent);\n\tWriteVector (MSG_MULTICAST, org);\n\tWriteVector (MSG_MULTICAST, org2);\n\tmulticast (org, mcast);\n};\n\nvoid(vector org, float damage, float effect, float mcast) EFF_CountEffect =\n{\n\tWriteByte (MSG_MULTICAST, SVC_TEMPENTITY);\n\tWriteByte (MSG_MULTICAST, effect);\n\tWriteByte (MSG_MULTICAST, damage);\n\tWriteVector (MSG_MULTICAST, org);\n\tmulticast (org, mcast);\n};\n#endif\n\n/*\n================\nSpawnBlood\n================\n*/\nvoid(vector org, float damage) _SpawnBlood_TEBlood =\n{\n\tte_blood(org, '0 0 0', damage*2);\n};\n\nvoid(vector org, float damage) _SpawnBlood =\n{\n#ifdef NETQUAKE\n\tparticle (org, '0 0 0', 73, damage*2);\n#else\n\tEFF_CountEffect(org, damage, TE_BLOOD, MULTICAST_PVS);\n#endif\n};\n\n// #if defined(NETQUAKE) || defined(MONSTERS)\nvoid() s_explode6 = [5,\t\tSUB_Remove] {};\nvoid() s_explode5 = [4,\t\ts_explode6] {};\nvoid() s_explode4 = [3,\t\ts_explode5] {};\nvoid() s_explode3 = [2,\t\ts_explode4] {};\nvoid() s_explode2 = [1,\t\ts_explode3] {};\nvoid() s_explode1 = [0,\t\ts_explode2] {};\n\nvoid(vector org) CreateExplosion =\n{\n\tlocal entity s;\n\n\ts = self; // save old self\n\n\tself = spawn();\n\n\tself.movetype = MOVETYPE_NONE;\n\tself.velocity = '0 0 0';\n\tself.touch = SUB_Null;\n\tsetmodel (self, \"progs/s_explod.spr\");\n\tsetorigin (self, org);\n\tself.solid = SOLID_NOT;\n\ts_explode1 ();\n\n\tself = s; // restore old self\n};\n// #endif\n\nvoid(vector org) _TE_explosion =\n{\n\tEFF_PointEffect(org, TE_EXPLOSION, MULTICAST_PHS);\n#ifdef NETQUAKE\n\tCreateExplosion(org);\n#endif\n};\n\nvoid(vector org) _TE_gunshot =\n{\n#ifdef NETQUAKE\n\tEFF_PointEffect(org, TE_GUNSHOT, MULTICAST_PVS);\n#else\n\tEFF_CountEffect(org, 3, TE_GUNSHOT, MULTICAST_PVS);\n#endif\n};\n\nvoid(vector org) _TE_spike =\n{\n\tEFF_PointEffect(org, TE_SPIKE, MULTICAST_PHS);\n};\n\nvoid(vector org) _TE_knightspike =\n{\n\tEFF_PointEffect(org, TE_KNIGHTSPIKE, MULTICAST_PHS);\n};\n\nvoid(vector org) _TE_wizspike =\n{\n\tEFF_PointEffect(org, TE_WIZSPIKE, MULTICAST_PHS);\n};\n\nvoid(vector org) _TE_superspike =\n{\n\tEFF_PointEffect(org, TE_SUPERSPIKE, MULTICAST_PHS);\n};\n\nvoid(vector org) _TE_teleport =\n{\n\tEFF_PointEffect(org, TE_TELEPORT, MULTICAST_PHS);\n};\n\nvoid(vector org) _TE_lavasplash = \n{\n\tEFF_PointEffect(org, TE_LAVASPLASH, MULTICAST_PVS);\n};\n\nvoid(vector org) _TE_tarexplosion = \n{\n\tEFF_PointEffect(org, TE_TAREXPLOSION, MULTICAST_PHS);\n};\n\nvoid(entity ent, vector start, vector end) _TE_lightning1 =\n{\n\tEFF_BeamEffect (ent, start, end, TE_LIGHTNING1, MULTICAST_PHS);\n};\n\nvoid(entity ent, vector start, vector end) _TE_lightning2 =\n{\n\tEFF_BeamEffect (ent, start, end, TE_LIGHTNING2, MULTICAST_PHS);\n};\n\nvoid(entity ent, vector start, vector end) _TE_lightning3 =\n{\n\tEFF_BeamEffect (ent, start, end, TE_LIGHTNING3, MULTICAST_PHS);\n};\n\nvoid(vector org) TE_lightningblood =\n{\n#ifdef NETQUAKE\n\tparticle(org, '0 0 100', 225, 120);\n#else\n\tEFF_PointEffect(org, TE_LIGHTNINGBLOOD, MULTICAST_PVS);\n#endif\n};\n\n// function pointers for TE calls\nvar void(vector org, float damage) SpawnBlood = _SpawnBlood;\nvar void(vector org) TE_explosion = _TE_explosion;\nvar void(vector org) TE_gunshot = _TE_gunshot;\nvar void(vector org) TE_spike = _TE_spike;\nvar void(vector org) TE_knightspike = _TE_knightspike;\nvar void(vector org) TE_wizspike = _TE_wizspike;\nvar void(vector org) TE_superspike = _TE_superspike;\nvar void(vector org) TE_teleport = _TE_teleport;\nvar void(vector org) TE_lavasplash = _TE_lavasplash;\nvar void(vector org) TE_tarexplosion = _TE_tarexplosion;\nvar void(entity ent, vector start, vector end) TE_lightning1 = _TE_lightning1;\nvar void(entity ent, vector start, vector end) TE_lightning2 = _TE_lightning2;\nvar void(entity ent, vector start, vector end) TE_lightning3 = _TE_lightning3;\n\n// set effects function, takes function pointers and assigns them based on detected builtins\nvoid() EFF_SetEffects =\n{\n\tif (eng_support & ENG_EFRED)\n\t{\n\t\tef_pent = 128;\n\t\tef_red = 128;\n\t}\n\n\tif (eng_support & ENG_EFBLUE)\n\t{\n\t\tef_blue = 64;\n\t\tef_quad = 64;\n\t}\n\n\tif (eng_support & ENG_TEBUILTINS)\n\t{\n\t\t// use TE_ builtins instead\n\t\tTE_explosion = _te_explosion;\n\t\tTE_gunshot = _te_gunshot;\n\t\tTE_spike = _te_spike;\n\t\tTE_knightspike = _te_knightspike;\n\t\tTE_wizspike = _te_wizspike;\n\t\tTE_superspike = _te_superspike;\n\t\tTE_teleport = _te_teleport;\n\t\tTE_lavasplash = _te_lavasplash;\n\t\tTE_tarexplosion = _te_tarexplosion;\n\t\tTE_lightning1 = _te_lightning1;\n\t\tTE_lightning2 = _te_lightning2;\n\t\tTE_lightning3 = _te_lightning3;\n\t}\n\n\tif (eng_support & ENG_TEBLOOD)\n\t\tSpawnBlood = _SpawnBlood_TEBlood; // use TE_Blood builtin instead\n};\n\n// view kicks\nvoid(entity ent) VK_smallkick =\n{\n#ifdef NETQUAKE\n\tself.punchangle_x = -2;\n#else\n\tmsg_entity = ent;\n\tWriteByte (MSG_ONE, SVC_SMALLKICK);\n#endif\n}\n\nvoid(entity ent) VK_bigkick =\n{\n#ifdef NETQUAKE\n\tself.punchangle_x = -4;\n#else\n\tmsg_entity = ent;\n\tWriteByte (MSG_ONE, SVC_BIGKICK);\n#endif\n}\n\n// muzzle flash\nvoid() muzzleflash =\n{\n#ifdef NETQUAKE\n\tself.effects |= EF_MUZZLEFLASH;\n#else\n\tWriteByte (MSG_MULTICAST, SVC_MUZZLEFLASH);\n\tWriteEntity (MSG_MULTICAST, self);\n\tmulticast (self.origin, MULTICAST_PVS);\n#endif\n};\n\n// touch blood functions\nvoid(float damage) spawn_touchblood =\n{\n\tlocal vector    vel;\n\n\tvel = normalize (self.velocity);\n\tvel = normalize(vel + v_up*(random()- 0.5) + v_right*(random()- 0.5));\n\tvel = vel + 2*trace_plane_normal;\n\tvel = vel * 0.4;\n\tSpawnBlood (self.origin + vel, damage);\n}; "
  },
  {
    "path": "quakec/basemod/enforcer.qc",
    "content": "/*\n==============================================================================\n\nSOLDIER / PLAYER\n\n==============================================================================\n*/\n\n$cd id1/models/enforcer\n$origin 0 -6 24\n$base base\t\t\n$skin skin\n\n$frame stand1 stand2 stand3 stand4 stand5 stand6 stand7\n\n$frame walk1 walk2 walk3 walk4 walk5 walk6 walk7 walk8 walk9 walk10\n$frame walk11 walk12 walk13 walk14 walk15 walk16\n\n$frame run1 run2 run3 run4 run5 run6 run7 run8\n\n$frame attack1 attack2 attack3 attack4 attack5 attack6\n$frame attack7 attack8 attack9 attack10\n\n$frame death1 death2 death3 death4 death5 death6 death7 death8\n$frame death9 death10 death11 death12 death13 death14\n\n$frame fdeath1 fdeath2 fdeath3 fdeath4 fdeath5 fdeath6 fdeath7 fdeath8\n$frame fdeath9 fdeath10 fdeath11\n\n$frame paina1 paina2 paina3 paina4\n\n$frame painb1 painb2 painb3 painb4 painb5\n\n$frame painc1 painc2 painc3 painc4 painc5 painc6 painc7 painc8\n\n$frame paind1 paind2 paind3 paind4 paind5 paind6 paind7 paind8\n$frame paind9 paind10 paind11 paind12 paind13 paind14 paind15 paind16\n$frame paind17 paind18 paind19\n\nvoid() enforcer_fire =\n{\n\tlocal vector org, vec;\n\n\tmuzzleflash();\n\tmakevectors (self.angles);\n\t\n\torg = self.origin + v_forward * 30 + v_right * 8.5 + '0 0 16';\n\tvec = normalize(self.enemy.origin - self.origin) * 600;\n\n\tsound (self, CHAN_VOICE, \"enforcer/enfire.wav\", 1, ATTN_NORM);\n\tPRJ_FireProjectile(self, \"progs/laser.mdl\", org, vec,\tPE_LASER, 15, MOD_ENFORCER, 5);\n\tnewmis.effects |= EF_DIMLIGHT;\n\tnewmis.alpha = 0.5;\n};\n\n//============================================================================\n\nvoid()\tenf_stand1\t=[\t$stand1,\tenf_stand2\t] {ai_stand();};\nvoid()\tenf_stand2\t=[\t$stand2,\tenf_stand3\t] {ai_stand();};\nvoid()\tenf_stand3\t=[\t$stand3,\tenf_stand4\t] {ai_stand();};\nvoid()\tenf_stand4\t=[\t$stand4,\tenf_stand5\t] {ai_stand();};\nvoid()\tenf_stand5\t=[\t$stand5,\tenf_stand6\t] {ai_stand();};\nvoid()\tenf_stand6\t=[\t$stand6,\tenf_stand7\t] {ai_stand();};\nvoid()\tenf_stand7\t=[\t$stand7,\tenf_stand1\t] {ai_stand();};\n\nvoid()\tenf_walk1\t=[\t$walk1 ,\tenf_walk2\t] {\nif (random() < 0.2)\n\tsound (self, CHAN_VOICE, \"enforcer/idle1.wav\", 1, ATTN_IDLE);\nai_walk(2);};\nvoid()\tenf_walk2\t=[\t$walk2 ,\tenf_walk3\t] {ai_walk(4);};\nvoid()\tenf_walk3\t=[\t$walk3 ,\tenf_walk4\t] {ai_walk(4);};\nvoid()\tenf_walk4\t=[\t$walk4 ,\tenf_walk5\t] {ai_walk(3);};\nvoid()\tenf_walk5\t=[\t$walk5 ,\tenf_walk6\t] {ai_walk(1);};\nvoid()\tenf_walk6\t=[\t$walk6 ,\tenf_walk7\t] {ai_walk(2);};\nvoid()\tenf_walk7\t=[\t$walk7 ,\tenf_walk8\t] {ai_walk(2);};\nvoid()\tenf_walk8\t=[\t$walk8 ,\tenf_walk9\t] {ai_walk(1);};\nvoid()\tenf_walk9\t=[\t$walk9 ,\tenf_walk10\t] {ai_walk(2);};\nvoid()\tenf_walk10\t=[\t$walk10,\tenf_walk11\t] {ai_walk(4);};\nvoid()\tenf_walk11\t=[\t$walk11,\tenf_walk12\t] {ai_walk(4);};\nvoid()\tenf_walk12\t=[\t$walk12,\tenf_walk13\t] {ai_walk(1);};\nvoid()\tenf_walk13\t=[\t$walk13,\tenf_walk14\t] {ai_walk(2);};\nvoid()\tenf_walk14\t=[\t$walk14,\tenf_walk15\t] {ai_walk(3);};\nvoid()\tenf_walk15\t=[\t$walk15,\tenf_walk16\t] {ai_walk(4);};\nvoid()\tenf_walk16\t=[\t$walk16,\tenf_walk1\t] {ai_walk(2);};\n\nvoid()\tenf_run1\t=[\t$run1  ,\tenf_run2\t] {\nif (random() < 0.2)\n\tsound (self, CHAN_VOICE, \"enforcer/idle1.wav\", 1, ATTN_IDLE);\nai_run(18);};\nvoid()\tenf_run2\t=[\t$run2  ,\tenf_run3\t] {ai_run(14);};\nvoid()\tenf_run3\t=[\t$run3  ,\tenf_run4\t] {ai_run(7);};\nvoid()\tenf_run4\t=[\t$run4  ,\tenf_run5\t] {ai_run(12);};\nvoid()\tenf_run5\t=[\t$run5  ,\tenf_run6\t] {ai_run(14);};\nvoid()\tenf_run6\t=[\t$run6  ,\tenf_run7\t] {ai_run(14);};\nvoid()\tenf_run7\t=[\t$run7  ,\tenf_run8\t] {ai_run(7);};\nvoid()\tenf_run8\t=[\t$run8  ,\tenf_run1\t] {ai_run(11);};\n\nvoid()\tenf_atk1\t=[\t$attack1,\tenf_atk2\t] {ai_face();};\nvoid()\tenf_atk2\t=[\t$attack2,\tenf_atk3\t] {ai_face();};\nvoid()\tenf_atk3\t=[\t$attack3,\tenf_atk4\t] {ai_face();};\nvoid()\tenf_atk4\t=[\t$attack4,\tenf_atk5\t] {ai_face();};\nvoid()\tenf_atk5\t=[\t$attack5,\tenf_atk6\t] {ai_face();};\nvoid()\tenf_atk6\t=[\t$attack6,\tenf_atk7\t] {enforcer_fire();};\nvoid()\tenf_atk7\t=[\t$attack7,\tenf_atk8\t] {ai_face();};\nvoid()\tenf_atk8\t=[\t$attack8,\tenf_atk9\t] {ai_face();};\nvoid()\tenf_atk9\t=[\t$attack5,\tenf_atk10\t] {ai_face();};\nvoid()\tenf_atk10\t=[\t$attack6,\tenf_atk11\t] {enforcer_fire();};\nvoid()\tenf_atk11\t=[\t$attack7,\tenf_atk12\t] {ai_face();};\nvoid()\tenf_atk12\t=[\t$attack8,\tenf_atk13\t] {ai_face();};\nvoid()\tenf_atk13\t=[\t$attack9,\tenf_atk14\t] {ai_face();};\nvoid()\tenf_atk14\t=[\t$attack10,\tenf_run1\t] {ai_face();\nSUB_CheckRefire (enf_atk1);\n};\n\nvoid()\tenf_paina1\t=[\t$paina1,\tenf_paina2\t] {};\nvoid()\tenf_paina2\t=[\t$paina2,\tenf_paina3\t] {};\nvoid()\tenf_paina3\t=[\t$paina3,\tenf_paina4\t] {};\nvoid()\tenf_paina4\t=[\t$paina4,\tenf_run1\t] {};\n\nvoid()\tenf_painb1\t=[\t$painb1,\tenf_painb2\t] {};\nvoid()\tenf_painb2\t=[\t$painb2,\tenf_painb3\t] {};\nvoid()\tenf_painb3\t=[\t$painb3,\tenf_painb4\t] {};\nvoid()\tenf_painb4\t=[\t$painb4,\tenf_painb5\t] {};\nvoid()\tenf_painb5\t=[\t$painb5,\tenf_run1\t] {};\n\nvoid()\tenf_painc1\t=[\t$painc1,\tenf_painc2\t] {};\nvoid()\tenf_painc2\t=[\t$painc2,\tenf_painc3\t] {};\nvoid()\tenf_painc3\t=[\t$painc3,\tenf_painc4\t] {};\nvoid()\tenf_painc4\t=[\t$painc4,\tenf_painc5\t] {};\nvoid()\tenf_painc5\t=[\t$painc5,\tenf_painc6\t] {};\nvoid()\tenf_painc6\t=[\t$painc6,\tenf_painc7\t] {};\nvoid()\tenf_painc7\t=[\t$painc7,\tenf_painc8\t] {};\nvoid()\tenf_painc8\t=[\t$painc8,\tenf_run1\t] {};\n\nvoid()\tenf_paind1\t=[\t$paind1,\tenf_paind2\t] {};\nvoid()\tenf_paind2\t=[\t$paind2,\tenf_paind3\t] {};\nvoid()\tenf_paind3\t=[\t$paind3,\tenf_paind4\t] {};\nvoid()\tenf_paind4\t=[\t$paind4,\tenf_paind5\t] {ai_painforward(2);};\nvoid()\tenf_paind5\t=[\t$paind5,\tenf_paind6\t] {ai_painforward(1);};\nvoid()\tenf_paind6\t=[\t$paind6,\tenf_paind7\t] {};\nvoid()\tenf_paind7\t=[\t$paind7,\tenf_paind8\t] {};\nvoid()\tenf_paind8\t=[\t$paind8,\tenf_paind9\t] {};\nvoid()\tenf_paind9\t=[\t$paind9,\tenf_paind10\t] {};\nvoid()\tenf_paind10\t=[\t$paind10,\tenf_paind11\t] {};\nvoid()\tenf_paind11\t=[\t$paind11,\tenf_paind12\t] {ai_painforward(1);};\nvoid()\tenf_paind12\t=[\t$paind12,\tenf_paind13\t] {ai_painforward(1);};\nvoid()\tenf_paind13\t=[\t$paind13,\tenf_paind14\t] {ai_painforward(1);};\nvoid()\tenf_paind14\t=[\t$paind14,\tenf_paind15\t] {};\nvoid()\tenf_paind15\t=[\t$paind15,\tenf_paind16\t] {};\nvoid()\tenf_paind16\t=[\t$paind16,\tenf_paind17\t] {ai_pain(1);};\nvoid()\tenf_paind17\t=[\t$paind17,\tenf_paind18\t] {ai_pain(1);};\nvoid()\tenf_paind18\t=[\t$paind18,\tenf_paind19\t] {};\nvoid()\tenf_paind19\t=[\t$paind19,\tenf_run1\t] {};\n\nvoid(entity attacker, float damage)\tenf_pain =\n{\n\tlocal float r;\n\n\tif (self.pain_finished > time)\n\t\treturn;\n\n\tr = random ();\n\tif (r < 0.5)\n\t\tsound (self, CHAN_VOICE, \"enforcer/pain1.wav\", 1, ATTN_NORM);\n\telse\n\t\tsound (self, CHAN_VOICE, \"enforcer/pain2.wav\", 1, ATTN_NORM);\n\n\tself.pain_finished = time + 1;\n\tif (r < 0.2)\n\t\tenf_paina1 ();\n\telse if (r < 0.4)\n\t\tenf_painb1 ();\n\telse if (r < 0.7)\n\t\tenf_painc1 ();\n\telse\n\t\tenf_paind1 ();\n};\n\n//============================================================================\n\n\n\n\nvoid()\tenf_die1\t=[\t$death1,\tenf_die2\t] {};\nvoid()\tenf_die2\t=[\t$death2,\tenf_die3\t] {};\nvoid()\tenf_die3\t=[\t$death3,\tenf_die4\t]\n{self.solid = SOLID_NOT;DropBackpack();};\nvoid()\tenf_die4\t=[\t$death4,\tenf_die5\t] {ai_forward(14);};\nvoid()\tenf_die5\t=[\t$death5,\tenf_die6\t] {ai_forward(2);};\nvoid()\tenf_die6\t=[\t$death6,\tenf_die7\t] {};\nvoid()\tenf_die7\t=[\t$death7,\tenf_die8\t] {};\nvoid()\tenf_die8\t=[\t$death8,\tenf_die9\t] {};\nvoid()\tenf_die9\t=[\t$death9,\tenf_die10\t] {ai_forward(3);};\nvoid()\tenf_die10\t=[\t$death10,\tenf_die11\t] {ai_forward(5);};\nvoid()\tenf_die11\t=[\t$death11,\tenf_die12\t] {ai_forward(5);};\nvoid()\tenf_die12\t=[\t$death12,\tenf_die13\t] {ai_forward(5);};\nvoid()\tenf_die13\t=[\t$death13,\tenf_die14\t] {};\nvoid()\tenf_die14\t=[\t$death14,\tenf_die14\t] {};\n\nvoid()\tenf_fdie1\t=[\t$fdeath1,\tenf_fdie2\t] {\n\n};\nvoid()\tenf_fdie2\t=[\t$fdeath2,\tenf_fdie3\t] {};\nvoid()\tenf_fdie3\t=[\t$fdeath3,\tenf_fdie4\t] \n{self.solid = SOLID_NOT;DropBackpack();};\nvoid()\tenf_fdie4\t=[\t$fdeath4,\tenf_fdie5\t] {};\nvoid()\tenf_fdie5\t=[\t$fdeath5,\tenf_fdie6\t] {};\nvoid()\tenf_fdie6\t=[\t$fdeath6,\tenf_fdie7\t] {};\nvoid()\tenf_fdie7\t=[\t$fdeath7,\tenf_fdie8\t] {};\nvoid()\tenf_fdie8\t=[\t$fdeath8,\tenf_fdie9\t] {};\nvoid()\tenf_fdie9\t=[\t$fdeath9,\tenf_fdie10\t] {};\nvoid()\tenf_fdie10\t=[\t$fdeath10,\tenf_fdie11\t] {};\nvoid()\tenf_fdie11\t=[\t$fdeath11,\tenf_fdie11\t] {};\n\n\nvoid() enf_die =\n{\n// check for gib\n\tif (self.health < -35)\n\t{\n\t\tsound (self, CHAN_VOICE, \"player/udeath.wav\", 1, ATTN_NORM);\n\t\tThrowHead (\"progs/h_mega.mdl\", self.health);\n\t\tThrowGib (\"progs/gib1.mdl\", self.health);\n\t\tThrowGib (\"progs/gib2.mdl\", self.health);\n\t\tThrowGib (\"progs/gib3.mdl\", self.health);\n\t\treturn;\n\t}\n\n// regular death\n\tsound (self, CHAN_VOICE, \"enforcer/death1.wav\", 1, ATTN_NORM);\n\tif (random() > 0.5)\n\t\tenf_die1 ();\n\telse\n\t\tenf_fdie1 ();\n};\n\n\n/*QUAKED monster_enforcer (1 0 0) (-16 -16 -24) (16 16 40) Ambush\n\n*/\nvoid() monster_enforcer =\n{\n\tif (deathmatch)\n\t{\n\t\tremove(self);\n\t\treturn;\n\t}\n\n\tprecache_model2 (\"progs/enforcer.mdl\");\n\tprecache_model2 (\"progs/h_mega.mdl\");\n\tprecache_model2 (\"progs/laser.mdl\");\n\n\tprecache_sound2 (\"enforcer/death1.wav\");\n\tprecache_sound2 (\"enforcer/enfire.wav\");\n\tprecache_sound2 (\"enforcer/enfstop.wav\");\n\tprecache_sound2 (\"enforcer/idle1.wav\");\n\tprecache_sound2 (\"enforcer/pain1.wav\");\n\tprecache_sound2 (\"enforcer/pain2.wav\");\n\tprecache_sound2 (\"enforcer/sight1.wav\");\n\tprecache_sound2 (\"enforcer/sight2.wav\");\n\tprecache_sound2 (\"enforcer/sight3.wav\");\n\tprecache_sound2 (\"enforcer/sight4.wav\");\n\t\n\tself.solid = SOLID_SLIDEBOX;\n\tself.movetype = MOVETYPE_STEP;\n\n\tsetmodel (self, \"progs/enforcer.mdl\");\n\n\tsetsize (self, '-16 -16 -24', '16 16 40');\n\tself.health = 80;\n\n\tself.th_stand = enf_stand1;\n\tself.th_walk = enf_walk1;\n\tself.th_run = enf_run1;\n\tself.th_pain = enf_pain;\n\tself.th_die = enf_die;\n\tself.th_missile = enf_atk1;\n\n\tself.ammo_cells_real = 5; // drop 5 cells on death\n\n\twalkmonster_start();\n};\n"
  },
  {
    "path": "quakec/basemod/fish.qc",
    "content": "$cd id1/models/fish\n$origin 0 0 24\n$base base\t\t\n$skin skin\n\n$frame attack1 attack2 attack3 attack4 attack5 attack6 \n$frame attack7 attack8 attack9 attack10 attack11 attack12 attack13 \n$frame attack14 attack15 attack16 attack17 attack18 \n\n$frame death1 death2 death3 death4 death5 death6 death7 \n$frame death8 death9 death10 death11 death12 death13 death14 death15 \n$frame death16 death17 death18 death19 death20 death21 \n\n$frame swim1 swim2 swim3 swim4 swim5 swim6 swim7 swim8 \n$frame swim9 swim10 swim11 swim12 swim13 swim14 swim15 swim16 swim17 \n$frame swim18 \n\n$frame pain1 pain2 pain3 pain4 pain5 pain6 pain7 pain8 \n$frame pain9 \n\nvoid() swimmonster_start;\n\nvoid() f_stand1  =[      $swim1, f_stand2 ] {ai_stand();};\nvoid() f_stand2  =[      $swim2, f_stand3 ] {ai_stand();};\nvoid() f_stand3  =[      $swim3, f_stand4 ] {ai_stand();};\nvoid() f_stand4  =[      $swim4, f_stand5 ] {ai_stand();};\nvoid() f_stand5  =[      $swim5, f_stand6 ] {ai_stand();};\nvoid() f_stand6  =[      $swim6, f_stand7 ] {ai_stand();};\nvoid() f_stand7  =[      $swim7, f_stand8 ] {ai_stand();};\nvoid() f_stand8  =[      $swim8, f_stand9 ] {ai_stand();};\nvoid() f_stand9  =[      $swim9, f_stand10  ] {ai_stand();};\nvoid() f_stand10 =[      $swim10, f_stand11 ] {ai_stand();};\nvoid() f_stand11 =[      $swim11, f_stand12 ] {ai_stand();};\nvoid() f_stand12 =[      $swim12, f_stand13 ] {ai_stand();};\nvoid() f_stand13 =[      $swim13, f_stand14 ] {ai_stand();};\nvoid() f_stand14 =[      $swim14, f_stand15 ] {ai_stand();};\nvoid() f_stand15 =[      $swim15, f_stand16 ] {ai_stand();};\nvoid() f_stand16 =[      $swim16, f_stand17 ] {ai_stand();};\nvoid() f_stand17 =[      $swim17, f_stand18 ] {ai_stand();};\nvoid() f_stand18 =[      $swim18, f_stand1 ] {ai_stand();};\n\nvoid() f_walk1  =[      $swim1, f_walk2 ] {ai_walk(8);};\nvoid() f_walk2  =[      $swim2, f_walk3 ] {ai_walk(8);};\nvoid() f_walk3  =[      $swim3, f_walk4 ] {ai_walk(8);};\nvoid() f_walk4  =[      $swim4, f_walk5 ] {ai_walk(8);};\nvoid() f_walk5  =[      $swim5, f_walk6 ] {ai_walk(8);};\nvoid() f_walk6  =[      $swim6, f_walk7 ] {ai_walk(8);};\nvoid() f_walk7  =[      $swim7, f_walk8 ] {ai_walk(8);};\nvoid() f_walk8  =[      $swim8, f_walk9 ] {ai_walk(8);};\nvoid() f_walk9  =[      $swim9, f_walk10  ] {ai_walk(8);};\nvoid() f_walk10 =[      $swim10, f_walk11 ] {ai_walk(8);};\nvoid() f_walk11 =[      $swim11, f_walk12 ] {ai_walk(8);};\nvoid() f_walk12 =[      $swim12, f_walk13 ] {ai_walk(8);};\nvoid() f_walk13 =[      $swim13, f_walk14 ] {ai_walk(8);};\nvoid() f_walk14 =[      $swim14, f_walk15 ] {ai_walk(8);};\nvoid() f_walk15 =[      $swim15, f_walk16 ] {ai_walk(8);};\nvoid() f_walk16 =[      $swim16, f_walk17 ] {ai_walk(8);};\nvoid() f_walk17 =[      $swim17, f_walk18 ] {ai_walk(8);};\nvoid() f_walk18 =[      $swim18, f_walk1 ] {ai_walk(8);};\n\nvoid() f_run1  =[      $swim1, f_run2 ] {ai_run(12);\n\tif (random() < 0.5)\n\t\tsound (self, CHAN_VOICE, \"fish/idle.wav\", 1, ATTN_NORM);\n};\nvoid() f_run2  =[      $swim3, f_run3 ] {ai_run(12);};\nvoid() f_run3  =[      $swim5, f_run4 ] {ai_run(12);};\nvoid() f_run4  =[      $swim7, f_run5 ] {ai_run(12);};\nvoid() f_run5  =[      $swim9, f_run6 ] {ai_run(12);};\nvoid() f_run6  =[      $swim11, f_run7 ] {ai_run(12);};\nvoid() f_run7  =[      $swim13, f_run8 ] {ai_run(12);};\nvoid() f_run8  =[      $swim15, f_run9 ] {ai_run(12);};\nvoid() f_run9  =[      $swim17, f_run1 ] {ai_run(12);};\n\nvoid() fish_melee =\n{\n\tlocal vector\tdelta;\n\tlocal float \tldmg;\n\n\tif (!self.enemy)\n\t\treturn;\t\t// removed before stroke\n\t\t\n\tdelta = self.enemy.origin - self.origin;\n\n\tif (vlen(delta) > 60)\n\t\treturn;\n\t\t\n\tsound (self, CHAN_VOICE, \"fish/bite.wav\", 1, ATTN_NORM);\n\tldmg = (random() + random()) * 3;\n\tT_Damage (self.enemy, self, self, ldmg, MOD_FISH);\n};\n\nvoid() f_attack1        =[      $attack1,       f_attack2 ] {ai_charge(10);};\nvoid() f_attack2        =[      $attack2,       f_attack3 ] {ai_charge(10);};\nvoid() f_attack3        =[      $attack3,       f_attack4 ] {fish_melee();};\nvoid() f_attack4        =[      $attack4,       f_attack5 ] {ai_charge(10);};\nvoid() f_attack5        =[      $attack5,       f_attack6 ] {ai_charge(10);};\nvoid() f_attack6        =[      $attack6,       f_attack7 ] {ai_charge(10);};\nvoid() f_attack7        =[      $attack7,       f_attack8 ] {ai_charge(10);};\nvoid() f_attack8        =[      $attack8,       f_attack9 ] {ai_charge(10);};\nvoid() f_attack9        =[      $attack9,       f_attack10] {fish_melee();};\nvoid() f_attack10       =[      $attack10,      f_attack11] {ai_charge(10);};\nvoid() f_attack11       =[      $attack11,      f_attack12] {ai_charge(10);};\nvoid() f_attack12       =[      $attack12,      f_attack13] {ai_charge(10);};\nvoid() f_attack13       =[      $attack13,      f_attack14] {ai_charge(10);};\nvoid() f_attack14       =[      $attack14,      f_attack15] {ai_charge(10);};\nvoid() f_attack15       =[      $attack15,      f_attack16] {fish_melee();};\nvoid() f_attack16       =[      $attack16,      f_attack17] {ai_charge(10);};\nvoid() f_attack17       =[      $attack17,      f_attack18] {ai_charge(10);};\nvoid() f_attack18       =[      $attack18,      f_run1    ] {ai_charge(10);};\n\nvoid() f_death1 =[      $death1,        f_death2        ] {\nsound (self, CHAN_VOICE, \"fish/death.wav\", 1, ATTN_NORM);\n};\nvoid() f_death2 =[      $death2,        f_death3        ] {};\nvoid() f_death3 =[      $death3,        f_death4        ] {self.solid = SOLID_NOT;};\nvoid() f_death4 =[      $death4,        f_death5        ] {};\nvoid() f_death5 =[      $death5,        f_death6        ] {};\nvoid() f_death6 =[      $death6,        f_death7        ] {};\nvoid() f_death7 =[      $death7,        f_death8        ] {};\nvoid() f_death8 =[      $death8,        f_death9        ] {};\nvoid() f_death9 =[      $death9,        f_death10       ] {};\nvoid() f_death10 =[      $death10,       f_death11       ] {};\nvoid() f_death11 =[      $death11,       f_death12       ] {};\nvoid() f_death12 =[      $death12,       f_death13       ] {};\nvoid() f_death13 =[      $death13,       f_death14       ] {};\nvoid() f_death14 =[      $death14,       f_death15       ] {};\nvoid() f_death15 =[      $death15,       f_death16       ] {};\nvoid() f_death16 =[      $death16,       f_death17       ] {};\nvoid() f_death17 =[      $death17,       f_death18       ] {};\nvoid() f_death18 =[      $death18,       f_death19       ] {};\nvoid() f_death19 =[      $death19,       f_death20       ] {};\nvoid() f_death20 =[      $death20,       f_death21       ] {};\nvoid() f_death21 =[      $death21,       f_death21       ] {};\n\nvoid() f_pain1  =[      $pain1, f_pain2 ] {};\nvoid() f_pain2  =[      $pain2, f_pain3 ] {ai_pain(6);};\nvoid() f_pain3  =[      $pain3, f_pain4 ] {ai_pain(6);};\nvoid() f_pain4  =[      $pain4, f_pain5 ] {ai_pain(6);};\nvoid() f_pain5  =[      $pain5, f_pain6 ] {ai_pain(6);};\nvoid() f_pain6  =[      $pain6, f_pain7 ] {ai_pain(6);};\nvoid() f_pain7  =[      $pain7, f_pain8 ] {ai_pain(6);};\nvoid() f_pain8  =[      $pain8, f_pain9 ] {ai_pain(6);};\nvoid() f_pain9  =[      $pain9, f_run1 ] {ai_pain(6);};\n\nvoid(entity attacker, float damage)\tfish_pain =\n{\n\n// fish allways do pain frames\n\tf_pain1 ();\n};\n\n\n\n/*QUAKED monster_fish (1 0 0) (-16 -16 -24) (16 16 24) Ambush\n*/\nvoid() monster_fish =\n{\n\tif (deathmatch)\n\t{\n\t\tremove(self);\n\t\treturn;\n\t}\n\tprecache_model2 (\"progs/fish.mdl\");\n\n\tprecache_sound2 (\"fish/death.wav\");\n\tprecache_sound2 (\"fish/bite.wav\");\n\tprecache_sound2 (\"fish/idle.wav\");\n\n\tself.solid = SOLID_SLIDEBOX;\n\tself.movetype = MOVETYPE_STEP;\n\n\tsetmodel (self, \"progs/fish.mdl\");\n\n\tsetsize (self, '-16 -16 -24', '16 16 24');\n\tself.health = 25;\n\t\n\tself.th_stand = f_stand1;\n\tself.th_walk = f_walk1;\n\tself.th_run = f_run1;\n\tself.th_die = f_death1;\n\tself.th_pain = fish_pain;\n\tself.th_melee = f_attack1;\n\t\n\tswimmonster_start ();\n};\n\n"
  },
  {
    "path": "quakec/basemod/hknight.qc",
    "content": "/*\n==============================================================================\n\nKNIGHT\n\n==============================================================================\n*/\n\n$cd id1/models/knight2\n$origin 0 0 24\n$base base\n$skin skin\n\n$frame stand1 stand2 stand3 stand4 stand5 stand6 stand7 stand8 stand9\n\n$frame walk1 walk2 walk3 walk4 walk5 walk6 walk7 walk8 walk9\n$frame walk10 walk11 walk12 walk13 walk14 walk15 walk16 walk17\n$frame walk18 walk19 walk20\n\n$frame run1 run2 run3 run4 run5 run6 run7 run8\n\n$frame pain1 pain2 pain3 pain4 pain5\n\n$frame death1 death2 death3 death4 death5 death6 death7 death8\n$frame death9 death10 death11 death12\n\n$frame deathb1 deathb2 deathb3 deathb4 deathb5 deathb6 deathb7 deathb8\n$frame deathb9\n\n$frame char_a1 char_a2 char_a3 char_a4 char_a5 char_a6 char_a7 char_a8\n$frame char_a9 char_a10 char_a11 char_a12 char_a13 char_a14 char_a15 char_a16\n\n$frame magica1 magica2 magica3 magica4 magica5 magica6 magica7 magica8\n$frame magica9 magica10 magica11 magica12 magica13 magica14\n\n$frame magicb1 magicb2 magicb3 magicb4 magicb5 magicb6 magicb7 magicb8\n$frame magicb9 magicb10 magicb11 magicb12 magicb13\n\n$frame char_b1 char_b2 char_b3 char_b4 char_b5 char_b6\n\n$frame slice1 slice2 slice3 slice4 slice5 slice6 slice7 slice8 slice9 slice10\n\n$frame smash1 smash2 smash3 smash4 smash5 smash6 smash7 smash8 smash9 smash10\n$frame smash11\n\n$frame w_attack1 w_attack2 w_attack3 w_attack4 w_attack5 w_attack6 w_attack7 \n$frame w_attack8 w_attack9 w_attack10 w_attack11 w_attack12 w_attack13 w_attack14\n$frame w_attack15 w_attack16 w_attack17 w_attack18 w_attack19 w_attack20 \n$frame w_attack21 w_attack22 \n\n$frame magicc1 magicc2 magicc3 magicc4 magicc5 magicc6 magicc7 magicc8\n$frame magicc9 magicc10 magicc11\n\n\nvoid() hknight_char_a1;\nvoid() hknight_run1;\nvoid() hk_idle_sound;\n\nvoid(float offset) hknight_shot =\n{\n\tlocal\tvector\toffang;\n\tlocal\tvector\torg, vec;\n\t\n\toffang = vectoangles (self.enemy.origin - self.origin);\n\toffang_y = offang_y + offset * 6;\n\t\n\tmakevectors (offang);\n\n\torg = self.origin + self.mins + self.size*0.5 + v_forward * 20;\n\n// set missile speed\n\tvec = normalize (v_forward);\n\tvec_z = 0 - vec_z + (random() - 0.5)*0.1;\n\n\tsound (self, CHAN_WEAPON, \"hknight/attack1.wav\", 1, ATTN_NORM);\n\tPRJ_FireProjectile(self, \"progs/k_spike.mdl\", org, vec * 300, PE_KNIGHTSPIKE, 9, MOD_HKNIGHT, 6);\n};\n\nvoid() CheckForCharge =\n{\n\t// check for mad charge\n\tif (!visible(self.enemy))\n\t\treturn;\n\tif (time < self.attack_finished)\n\t\treturn;\t\n\tif ( fabs(self.origin_z - self.enemy.origin_z) > 20)\n\t\treturn;\t\t// too much height change\n\tif ( vlen (self.origin - self.enemy.origin) < 80)\n\t\treturn;\t\t// use regular attack\n\n\t// charge\t\t\n\tSUB_AttackFinished (2);\n\thknight_char_a1 ();\n};\n\nvoid() CheckContinueCharge =\n{\n\tif (time > self.attack_finished)\n\t{\n\t\tSUB_AttackFinished (3);\n\t\thknight_run1 ();\n\t\treturn;\t\t// done charging\n\t}\n\tif (random() > 0.5)\n\t\tsound (self, CHAN_WEAPON, \"knight/sword2.wav\", 1, ATTN_NORM);\n\telse\n\t\tsound (self, CHAN_WEAPON, \"knight/sword1.wav\", 1, ATTN_NORM);\n};\n\n//===========================================================================\n\nvoid()\thknight_stand1\t=[\t$stand1,\thknight_stand2\t] {ai_stand();};\nvoid()\thknight_stand2\t=[\t$stand2,\thknight_stand3\t] {ai_stand();};\nvoid()\thknight_stand3\t=[\t$stand3,\thknight_stand4\t] {ai_stand();};\nvoid()\thknight_stand4\t=[\t$stand4,\thknight_stand5\t] {ai_stand();};\nvoid()\thknight_stand5\t=[\t$stand5,\thknight_stand6\t] {ai_stand();};\nvoid()\thknight_stand6\t=[\t$stand6,\thknight_stand7\t] {ai_stand();};\nvoid()\thknight_stand7\t=[\t$stand7,\thknight_stand8\t] {ai_stand();};\nvoid()\thknight_stand8\t=[\t$stand8,\thknight_stand9\t] {ai_stand();};\nvoid()\thknight_stand9\t=[\t$stand9,\thknight_stand1\t] {ai_stand();};\n\n//===========================================================================\n\nvoid()\thknight_walk1\t=[\t$walk1,\t\thknight_walk2\t] {\nhk_idle_sound();\nai_walk(2);};\nvoid()\thknight_walk2\t=[\t$walk2,\t\thknight_walk3\t] {ai_walk(5);};\nvoid()\thknight_walk3\t=[\t$walk3,\t\thknight_walk4\t] {ai_walk(5);};\nvoid()\thknight_walk4\t=[\t$walk4,\t\thknight_walk5\t] {ai_walk(4);};\nvoid()\thknight_walk5\t=[\t$walk5,\t\thknight_walk6\t] {ai_walk(4);};\nvoid()\thknight_walk6\t=[\t$walk6,\t\thknight_walk7\t] {ai_walk(2);};\nvoid()\thknight_walk7\t=[\t$walk7,\t\thknight_walk8\t] {ai_walk(2);};\nvoid()\thknight_walk8\t=[\t$walk8,\t\thknight_walk9\t] {ai_walk(3);};\nvoid()\thknight_walk9\t=[\t$walk9,\t\thknight_walk10\t] {ai_walk(3);};\nvoid()\thknight_walk10\t=[\t$walk10,\thknight_walk11\t] {ai_walk(4);};\nvoid()\thknight_walk11\t=[\t$walk11,\thknight_walk12\t] {ai_walk(3);};\nvoid()\thknight_walk12\t=[\t$walk12,\thknight_walk13\t] {ai_walk(4);};\nvoid()\thknight_walk13\t=[\t$walk13,\thknight_walk14\t] {ai_walk(6);};\nvoid()\thknight_walk14\t=[\t$walk14,\thknight_walk15\t] {ai_walk(2);};\nvoid()\thknight_walk15\t=[\t$walk15,\thknight_walk16\t] {ai_walk(2);};\nvoid()\thknight_walk16\t=[\t$walk16,\thknight_walk17\t] {ai_walk(4);};\nvoid()\thknight_walk17\t=[\t$walk17,\thknight_walk18\t] {ai_walk(3);};\nvoid()\thknight_walk18\t=[\t$walk18,\thknight_walk19\t] {ai_walk(3);};\nvoid()\thknight_walk19\t=[\t$walk19,\thknight_walk20\t] {ai_walk(3);};\nvoid()\thknight_walk20\t=[\t$walk20,\thknight_walk1\t] {ai_walk(2);};\n\n//===========================================================================\n\nvoid()\thknight_run1\t=[\t$run1,\t\thknight_run2\t] {\nhk_idle_sound();\nai_run (20); CheckForCharge (); };\nvoid()\thknight_run2\t=[\t$run2,\t\thknight_run3\t] {ai_run(25);};\nvoid()\thknight_run3\t=[\t$run3,\t\thknight_run4\t] {ai_run(18);};\nvoid()\thknight_run4\t=[\t$run4,\t\thknight_run5\t] {ai_run(16);};\nvoid()\thknight_run5\t=[\t$run5,\t\thknight_run6\t] {ai_run(14);};\nvoid()\thknight_run6\t=[\t$run6,\t\thknight_run7\t] {ai_run(25);};\nvoid()\thknight_run7\t=[\t$run7,\t\thknight_run8\t] {ai_run(21);};\nvoid()\thknight_run8\t=[\t$run8,\t\thknight_run1\t] {ai_run(13);};\n\n//============================================================================\n\nvoid()\thknight_pain1\t=[\t$pain1,\t\thknight_pain2\t] {sound (self, CHAN_VOICE, \"hknight/pain1.wav\", 1, ATTN_NORM);};\nvoid()\thknight_pain2\t=[\t$pain2,\t\thknight_pain3\t] {};\nvoid()\thknight_pain3\t=[\t$pain3,\t\thknight_pain4\t] {};\nvoid()\thknight_pain4\t=[\t$pain4,\t\thknight_pain5\t] {};\nvoid()\thknight_pain5\t=[\t$pain5,\t\thknight_run1\t] {};\n\n//============================================================================\n\nvoid()\thknight_die1\t=[\t$death1,\thknight_die2\t] {ai_forward(10);};\nvoid()\thknight_die2\t=[\t$death2,\thknight_die3\t] {ai_forward(8);};\nvoid()\thknight_die3\t=[\t$death3,\thknight_die4\t]\n{self.solid = SOLID_NOT; ai_forward(7);};\nvoid()\thknight_die4\t=[\t$death4,\thknight_die5\t] {};\nvoid()\thknight_die5\t=[\t$death5,\thknight_die6\t] {};\nvoid()\thknight_die6\t=[\t$death6,\thknight_die7\t] {};\nvoid()\thknight_die7\t=[\t$death7,\thknight_die8\t] {};\nvoid()\thknight_die8\t=[\t$death8,\thknight_die9\t] {ai_forward(10);};\nvoid()\thknight_die9\t=[\t$death9,\thknight_die10\t] {ai_forward(11);};\nvoid()\thknight_die10\t=[\t$death10,\thknight_die11\t] {};\nvoid()\thknight_die11\t=[\t$death11,\thknight_die12\t] {};\nvoid()\thknight_die12\t=[\t$death12,\thknight_die12\t] {};\n\nvoid()\thknight_dieb1\t=[\t$deathb1,\thknight_dieb2\t] {};\nvoid()\thknight_dieb2\t=[\t$deathb2,\thknight_dieb3\t] {};\nvoid()\thknight_dieb3\t=[\t$deathb3,\thknight_dieb4\t]\n{self.solid = SOLID_NOT;};\nvoid()\thknight_dieb4\t=[\t$deathb4,\thknight_dieb5\t] {};\nvoid()\thknight_dieb5\t=[\t$deathb5,\thknight_dieb6\t] {};\nvoid()\thknight_dieb6\t=[\t$deathb6,\thknight_dieb7\t] {};\nvoid()\thknight_dieb7\t=[\t$deathb7,\thknight_dieb8\t] {};\nvoid()\thknight_dieb8\t=[\t$deathb8,\thknight_dieb9\t] {};\nvoid()\thknight_dieb9\t=[\t$deathb9,\thknight_dieb9\t] {};\n\nvoid() hknight_die =\n{\n// check for gib\n\tif (self.health < -40)\n\t{\n\t\tsound (self, CHAN_VOICE, \"player/udeath.wav\", 1, ATTN_NORM);\n\t\tThrowHead (\"progs/h_hellkn.mdl\", self.health);\n\t\tThrowGib (\"progs/gib1.mdl\", self.health);\n\t\tThrowGib (\"progs/gib2.mdl\", self.health);\n\t\tThrowGib (\"progs/gib3.mdl\", self.health);\n\t\treturn;\n\t}\n\n// regular death\n\tsound (self, CHAN_VOICE, \"hknight/death1.wav\", 1, ATTN_NORM);\n\tif (random() > 0.5)\n\t\thknight_die1 ();\n\telse\n\t\thknight_dieb1 ();\n};\n\n\n//============================================================================\n#ifdef 0\n// Not used\nvoid()\thknight_magica1 =[\t$magica1,\thknight_magica2\t] {ai_face();};\nvoid()\thknight_magica2 =[\t$magica2,\thknight_magica3\t] {ai_face();};\nvoid()\thknight_magica3 =[\t$magica3,\thknight_magica4\t] {ai_face();};\nvoid()\thknight_magica4 =[\t$magica4,\thknight_magica5\t] {ai_face();};\nvoid()\thknight_magica5 =[\t$magica5,\thknight_magica6\t] {ai_face();};\nvoid()\thknight_magica6 =[\t$magica6,\thknight_magica7\t] {ai_face();};\nvoid()\thknight_magica7 =[\t$magica7,\thknight_magica8\t] {hknight_shot(-2);};\nvoid()\thknight_magica8 =[\t$magica8,\thknight_magica9\t] {hknight_shot(-1);};\nvoid()\thknight_magica9 =[\t$magica9,\thknight_magica10] {hknight_shot(0);};\nvoid()\thknight_magica10 =[\t$magica10,\thknight_magica11] {hknight_shot(1);};\nvoid()\thknight_magica11 =[\t$magica11,\thknight_magica12] {hknight_shot(2);};\nvoid()\thknight_magica12 =[\t$magica12,\thknight_magica13] {hknight_shot(3);};\nvoid()\thknight_magica13 =[\t$magica13,\thknight_magica14] {ai_face();};\nvoid()\thknight_magica14 =[\t$magica14,\thknight_run1\t] {ai_face();};\n\n//============================================================================\n\nvoid()\thknight_magicb1 =[\t$magicb1,\thknight_magicb2\t] {ai_face();};\nvoid()\thknight_magicb2 =[\t$magicb2,\thknight_magicb3\t] {ai_face();};\nvoid()\thknight_magicb3 =[\t$magicb3,\thknight_magicb4\t] {ai_face();};\nvoid()\thknight_magicb4 =[\t$magicb4,\thknight_magicb5\t] {ai_face();};\nvoid()\thknight_magicb5 =[\t$magicb5,\thknight_magicb6\t] {ai_face();};\nvoid()\thknight_magicb6 =[\t$magicb6,\thknight_magicb7\t] {ai_face();};\nvoid()\thknight_magicb7 =[\t$magicb7,\thknight_magicb8\t] {hknight_shot(-2);};\nvoid()\thknight_magicb8 =[\t$magicb8,\thknight_magicb9\t] {hknight_shot(-1);};\nvoid()\thknight_magicb9 =[\t$magicb9,\thknight_magicb10] {hknight_shot(0);};\nvoid()\thknight_magicb10 =[\t$magicb10,\thknight_magicb11] {hknight_shot(1);};\nvoid()\thknight_magicb11 =[\t$magicb11,\thknight_magicb12] {hknight_shot(2);};\nvoid()\thknight_magicb12 =[\t$magicb12,\thknight_magicb13] {hknight_shot(3);};\nvoid()\thknight_magicb13 =[\t$magicb13,\thknight_run1] {ai_face();};\n#endif\n\n//============================================================================\n\nvoid()\thknight_magicc1 =[\t$magicc1,\thknight_magicc2\t] {ai_face();};\nvoid()\thknight_magicc2 =[\t$magicc2,\thknight_magicc3\t] {ai_face();};\nvoid()\thknight_magicc3 =[\t$magicc3,\thknight_magicc4\t] {ai_face();};\nvoid()\thknight_magicc4 =[\t$magicc4,\thknight_magicc5\t] {ai_face();};\nvoid()\thknight_magicc5 =[\t$magicc5,\thknight_magicc6\t] {ai_face();};\nvoid()\thknight_magicc6 =[\t$magicc6,\thknight_magicc7\t] {hknight_shot(-2);};\nvoid()\thknight_magicc7 =[\t$magicc7,\thknight_magicc8\t] {hknight_shot(-1);};\nvoid()\thknight_magicc8 =[\t$magicc8,\thknight_magicc9\t] {hknight_shot(0);};\nvoid()\thknight_magicc9 =[\t$magicc9,\thknight_magicc10] {hknight_shot(1);};\nvoid()\thknight_magicc10 =[\t$magicc10,\thknight_magicc11] {hknight_shot(2);};\nvoid()\thknight_magicc11 =[\t$magicc11,\thknight_run1] {hknight_shot(3);};\n\n//===========================================================================\n\nvoid()\thknight_char_a1\t=[\t$char_a1,\thknight_char_a2\t] {ai_charge(20);};\nvoid()\thknight_char_a2\t=[\t$char_a2,\thknight_char_a3\t] {ai_charge(25);};\nvoid()\thknight_char_a3\t=[\t$char_a3,\thknight_char_a4\t] {ai_charge(18);};\nvoid()\thknight_char_a4\t=[\t$char_a4,\thknight_char_a5\t] {ai_charge(16);};\nvoid()\thknight_char_a5\t=[\t$char_a5,\thknight_char_a6\t] {ai_charge(14);};\nvoid()\thknight_char_a6\t=[\t$char_a6,\thknight_char_a7\t] {ai_charge(20); ai_melee(MOD_HKNIGHT);};\nvoid()\thknight_char_a7\t=[\t$char_a7,\thknight_char_a8\t] {ai_charge(21); ai_melee(MOD_HKNIGHT);};\nvoid()\thknight_char_a8\t=[\t$char_a8,\thknight_char_a9\t] {ai_charge(13); ai_melee(MOD_HKNIGHT);};\nvoid()\thknight_char_a9\t=[\t$char_a9,\thknight_char_a10\t] {ai_charge(20); ai_melee(MOD_HKNIGHT);};\nvoid()\thknight_char_a10=[\t$char_a10,\thknight_char_a11\t] {ai_charge(20); ai_melee(MOD_HKNIGHT);};\nvoid()\thknight_char_a11=[\t$char_a11,\thknight_char_a12\t] {ai_charge(18); ai_melee(MOD_HKNIGHT);};\nvoid()\thknight_char_a12=[\t$char_a12,\thknight_char_a13\t] {ai_charge(16);};\nvoid()\thknight_char_a13=[\t$char_a13,\thknight_char_a14\t] {ai_charge(14);};\nvoid()\thknight_char_a14=[\t$char_a14,\thknight_char_a15\t] {ai_charge(25);};\nvoid()\thknight_char_a15=[\t$char_a15,\thknight_char_a16\t] {ai_charge(21);};\nvoid()\thknight_char_a16=[\t$char_a16,\thknight_run1\t] {ai_charge(13);};\n\n//===========================================================================\n\n#ifdef 0\n// Not used\nvoid()\thknight_char_b1\t=[\t$char_b1,\thknight_char_b2\t]\n{CheckContinueCharge (); ai_charge(23); ai_melee(MOD_HKNIGHT);};\nvoid()\thknight_char_b2\t=[\t$char_b2,\thknight_char_b3\t] {ai_charge(17); ai_melee(MOD_HKNIGHT);};\nvoid()\thknight_char_b3\t=[\t$char_b3,\thknight_char_b4\t] {ai_charge(12); ai_melee(MOD_HKNIGHT);};\nvoid()\thknight_char_b4\t=[\t$char_b4,\thknight_char_b5\t] {ai_charge(22); ai_melee(MOD_HKNIGHT);};\nvoid()\thknight_char_b5\t=[\t$char_b5,\thknight_char_b6\t] {ai_charge(18); ai_melee(MOD_HKNIGHT);};\nvoid()\thknight_char_b6\t=[\t$char_b6,\thknight_char_b1\t] {ai_charge(8); ai_melee(MOD_HKNIGHT);};\n#endif\n\n//===========================================================================\n\nvoid()\thknight_slice1\t=[\t$slice1,\thknight_slice2\t] {ai_charge(9);};\nvoid()\thknight_slice2\t=[\t$slice2,\thknight_slice3\t] {ai_charge(6);};\nvoid()\thknight_slice3\t=[\t$slice3,\thknight_slice4\t] {ai_charge(13);};\nvoid()\thknight_slice4\t=[\t$slice4,\thknight_slice5\t] {ai_charge(4);};\nvoid()\thknight_slice5\t=[\t$slice5,\thknight_slice6\t] {ai_charge(7); ai_melee(MOD_HKNIGHT);};\nvoid()\thknight_slice6\t=[\t$slice6,\thknight_slice7\t] {ai_charge(15); ai_melee(MOD_HKNIGHT);};\nvoid()\thknight_slice7\t=[\t$slice7,\thknight_slice8\t] {ai_charge(8); ai_melee(MOD_HKNIGHT);};\nvoid()\thknight_slice8\t=[\t$slice8,\thknight_slice9\t] {ai_charge(2); ai_melee(MOD_HKNIGHT);};\nvoid()\thknight_slice9\t=[\t$slice9,\thknight_slice10\t] {ai_melee(MOD_HKNIGHT);};\nvoid()\thknight_slice10\t=[\t$slice10,\thknight_run1\t] {ai_charge(3);};\n\n//===========================================================================\n\nvoid()\thknight_smash1\t=[\t$smash1,\thknight_smash2\t] {ai_charge(1);};\nvoid()\thknight_smash2\t=[\t$smash2,\thknight_smash3\t] {ai_charge(13);};\nvoid()\thknight_smash3\t=[\t$smash3,\thknight_smash4\t] {ai_charge(9);};\nvoid()\thknight_smash4\t=[\t$smash4,\thknight_smash5\t] {ai_charge(11);};\nvoid()\thknight_smash5\t=[\t$smash5,\thknight_smash6\t] {ai_charge(10); ai_melee(MOD_HKNIGHT);};\nvoid()\thknight_smash6\t=[\t$smash6,\thknight_smash7\t] {ai_charge(7); ai_melee(MOD_HKNIGHT);};\nvoid()\thknight_smash7\t=[\t$smash7,\thknight_smash8\t] {ai_charge(12); ai_melee(MOD_HKNIGHT);};\nvoid()\thknight_smash8\t=[\t$smash8,\thknight_smash9\t] {ai_charge(2); ai_melee(MOD_HKNIGHT);};\nvoid()\thknight_smash9\t=[\t$smash9,\thknight_smash10\t] {ai_charge(3); ai_melee(MOD_HKNIGHT);};\nvoid()\thknight_smash10\t=[\t$smash10,\thknight_smash11\t] {ai_charge(0);};\nvoid()\thknight_smash11\t=[\t$smash11,\thknight_run1\t] {ai_charge(0);};\n\n//============================================================================\n\nvoid()\thknight_watk1\t=[\t$w_attack1,\thknight_watk2\t] {ai_charge(2);};\nvoid()\thknight_watk2\t=[\t$w_attack2,\thknight_watk3\t] {ai_charge(0);};\nvoid()\thknight_watk3\t=[\t$w_attack3,\thknight_watk4\t] {ai_charge(0);};\nvoid()\thknight_watk4\t=[\t$w_attack4,\thknight_watk5\t] {ai_melee(MOD_HKNIGHT);};\nvoid()\thknight_watk5\t=[\t$w_attack5,\thknight_watk6\t] {ai_melee(MOD_HKNIGHT);};\nvoid()\thknight_watk6\t=[\t$w_attack6,\thknight_watk7\t] {ai_melee(MOD_HKNIGHT);};\nvoid()\thknight_watk7\t=[\t$w_attack7,\thknight_watk8\t] {ai_charge(1);};\nvoid()\thknight_watk8\t=[\t$w_attack8,\thknight_watk9\t] {ai_charge(4);};\nvoid()\thknight_watk9\t=[\t$w_attack9,\thknight_watk10\t] {ai_charge(5);};\nvoid()\thknight_watk10\t=[\t$w_attack10,\thknight_watk11\t] {ai_charge(3); ai_melee(MOD_HKNIGHT);};\nvoid()\thknight_watk11\t=[\t$w_attack11,\thknight_watk12\t] {ai_charge(2); ai_melee(MOD_HKNIGHT);};\nvoid()\thknight_watk12\t=[\t$w_attack12,\thknight_watk13\t] {ai_charge(2); ai_melee(MOD_HKNIGHT);};\nvoid()\thknight_watk13\t=[\t$w_attack13,\thknight_watk14\t] {ai_charge(0);};\nvoid()\thknight_watk14\t=[\t$w_attack14,\thknight_watk15\t] {ai_charge(0);};\nvoid()\thknight_watk15\t=[\t$w_attack15,\thknight_watk16\t] {ai_charge(0);};\nvoid()\thknight_watk16\t=[\t$w_attack16,\thknight_watk17\t] {ai_charge(1);};\nvoid()\thknight_watk17\t=[\t$w_attack17,\thknight_watk18\t] {ai_charge(1); ai_melee(MOD_HKNIGHT);};\nvoid()\thknight_watk18\t=[\t$w_attack18,\thknight_watk19\t] {ai_charge(3); ai_melee(MOD_HKNIGHT);};\nvoid()\thknight_watk19\t=[\t$w_attack19,\thknight_watk20\t] {ai_charge(4); ai_melee(MOD_HKNIGHT);};\nvoid()\thknight_watk20\t=[\t$w_attack20,\thknight_watk21\t] {ai_charge(6);};\nvoid()\thknight_watk21\t=[\t$w_attack21,\thknight_watk22\t] {ai_charge(7);};\nvoid()\thknight_watk22\t=[\t$w_attack22,\thknight_run1\t] {ai_charge(3);};\n\n//============================================================================\n\nvoid() hk_idle_sound =\n{\n\tif (random() < 0.2)\n\t\tsound (self, CHAN_VOICE, \"hknight/idle.wav\", 1, ATTN_NORM);\n};\n\nvoid(entity attacker, float damage)\thknight_pain =\n{\n\tif (self.pain_finished > time)\n\t\treturn;\n\n\tsound (self, CHAN_VOICE, \"hknight/pain1.wav\", 1, ATTN_NORM);\n\n\tif (time - self.pain_finished > 5)\n\t{\t// allways go into pain frame if it has been a while\n\t\thknight_pain1 ();\n\t\tself.pain_finished = time + 1;\n\t\treturn;\n\t}\n\t\n\tif ((random()*30 > damage) )\n\t\treturn;\t\t// didn't flinch\n\n\tself.pain_finished = time + 1;\n\thknight_pain1 ();\n};\n\nvoid() hknight_melee =\n{\n\tself.hknightattack += 1;\n\n\tsound (self, CHAN_WEAPON, \"hknight/slash1.wav\", 1, ATTN_NORM);\n\tswitch (self.hknightattack)\n\t{\n\tcase 1:\n\t\thknight_slice1 ();\n\t\tbreak;\n\tcase 2:\n\t\thknight_smash1 ();\n\t\tbreak;\n\tdefault:\n\t\thknight_watk1 ();\n\t\tself.hknightattack = 0;\t\t\n\t}\n};\n\n/*QUAKED monster_hell_knight (1 0 0) (-16 -16 -24) (16 16 40) Ambush\n*/\nvoid() monster_hell_knight =\n{\n\tif (deathmatch)\n\t{\n\t\tremove(self);\n\t\treturn;\n\t}\n\tprecache_model2 (\"progs/hknight.mdl\");\n\tprecache_model2 (\"progs/k_spike.mdl\");\n\tprecache_model2 (\"progs/h_hellkn.mdl\");\n\n\tprecache_sound2 (\"hknight/attack1.wav\");\n\tprecache_sound2 (\"hknight/death1.wav\");\n\tprecache_sound2 (\"hknight/pain1.wav\");\n\tprecache_sound2 (\"hknight/sight1.wav\");\n\tprecache_sound (\"hknight/hit.wav\");\t\t// used by C code, so don't sound2\n\tprecache_sound2 (\"hknight/slash1.wav\");\n\tprecache_sound2 (\"hknight/idle.wav\");\n\tprecache_sound2 (\"hknight/grunt.wav\");\n\n\tprecache_sound (\"knight/sword1.wav\");\n\tprecache_sound (\"knight/sword2.wav\");\n\t\n\tself.solid = SOLID_SLIDEBOX;\n\tself.movetype = MOVETYPE_STEP;\n\n\tsetmodel (self, \"progs/hknight.mdl\");\n\n\tsetsize (self, '-16 -16 -24', '16 16 40');\n\tself.health = 250;\n\n\tself.th_stand = hknight_stand1;\n\tself.th_walk = hknight_walk1;\n\tself.th_run = hknight_run1;\n\tself.th_melee = hknight_melee;\n\tself.th_missile = hknight_magicc1;\n\tself.th_pain = hknight_pain;\n\tself.th_die = hknight_die;\n\n\tself.hknightattack = floor(random() * 2.99);\n\t\n\twalkmonster_start ();\n};\n"
  },
  {
    "path": "quakec/basemod/items.qc",
    "content": "/* ALL LIGHTS SHOULD BE 0 1 0 IN COLOR ALL OTHER ITEMS SHOULD\nBE .8 .3 .4 IN COLOR */\n\nvoid (float weap) W_WeaponSwitch;\nvoid (entity ent) W_UpdateAmmoCounts;\n\nvoid() SUB_regen =\n{\n\tself.model = self.mdl;          // restore original model\n\tself.solid = SOLID_TRIGGER;     // allow it to be touched again\n\tsound (self, CHAN_VOICE, \"items/itembk2.wav\", 1, ATTN_NORM);    // play respawn sound\n\tsetorigin (self, self.origin);\n};\n\n\n\n/*QUAKED noclass (0 0 0) (-8 -8 -8) (8 8 8)\nprints a warning message when spawned\n*/\nvoid() noclass =\n{\n\tdprint (\"noclass spawned at\");\n\tdprint (vtos(self.origin));\n\tdprint (\"\\n\");\n\tremove (self);\n};\n\n// names of weapons function\nstring(float weap) I_WeaponName =\n{\n\tswitch (weap)\n\t{\n\tcase IT_AXE:\n\t\treturn \"Axe\";\n\tcase IT_SHOTGUN:\n\t\treturn \"Shotgun\";\n\tcase IT_SUPER_SHOTGUN:\n\t\treturn \"Double-barrelled Shotgun\";\n\tcase IT_NAILGUN:\n\t\treturn \"Nailgun\";\n\tcase IT_SUPER_NAILGUN:\n\t\treturn \"Super Nailgun\";\n\tcase IT_GRENADE_LAUNCHER:\n\t\treturn \"Grenade Launcher\";\n\tcase IT_ROCKET_LAUNCHER:\n\t\treturn \"Rocket Launcher\";\n\tcase IT_LIGHTNING:\n\t\treturn \"Thunderbolt\";\n\t}\n\n\treturn \"\";\n}\n\nvoid() q_touch;\n\nvoid() q_touch =\n{\n\tlocal string s, t;\n\n\tif (other.classname != \"player\")\n\t\treturn;\n\tif (other.health <= 0)\n\t\treturn;\n\n\tself.mdl = self.model;\n\n\tsound (other, CHAN_VOICE, self.noise, 1, ATTN_NORM);\n\tstuffcmd (other, \"bf\\n\");\n\tself.solid = SOLID_NOT;\n\tother.items = other.items | IT_QUAD;\n\tself.model = string_null;\n\t\tif (deathmatch == 4)\n\t\t{\n\t\t\tother.armortype = 0;\n\t\t\tother.armorvalue = 0;\n\t\t\tother.ammo_cells_real = 0;\n\t\t\tW_UpdateAmmoCounts(other);\n\t\t}\n\n// do the apropriate action\n\tother.super_time = 1;\n\tother.super_damage_finished = self.cnt;\n\n\ts=ftos(rint(other.super_damage_finished - time));\n\tt = \" recovered a Quad with \";\n\tif (deathmatch == 4)\n\t\tt = \" recovered an OctaPower with \";\n\n\tbprint4 (PRINT_LOW, other.netname, t, s, \" seconds remaining!\\n\");\n\n\tactivator = other;\n\tSUB_UseTargets();                               // fire all targets / killtargets\n};\n\n\nvoid(float timeleft) DropQuad =\n{\n\tlocal entity    item;\n\n\titem = spawn();\n\titem.origin = self.origin;\n\t\n\titem.velocity_z = 300;\n\titem.velocity_x = -100 + (random() * 200);\n\titem.velocity_y = -100 + (random() * 200);\n\t\n\titem.flags = FL_ITEM;\n\titem.solid = SOLID_TRIGGER;\n\titem.movetype = MOVETYPE_TOSS;\n\titem.noise = \"items/damage.wav\";\n\tsetmodel (item, \"progs/quaddama.mdl\");\n\tsetsize (item, '-16 -16 -24', '16 16 32');\n\titem.cnt = time + timeleft;\n\titem.touch = q_touch;\n\titem.nextthink = time + timeleft;    // remove it with the time left on it\n\titem.think = SUB_Remove;\n};\n\n\nvoid() r_touch;\n\nvoid() r_touch =\n{\nlocal string    s;\n\n\tif (other.classname != \"player\")\n\t\treturn;\n\tif (other.health <= 0)\n\t\treturn;\n\n\tself.mdl = self.model;\n\n\tsound (other, CHAN_VOICE, self.noise, 1, ATTN_NORM);\n\tstuffcmd (other, \"bf\\n\");\n\tself.solid = SOLID_NOT;\n\tother.items = other.items | IT_INVISIBILITY;\n\tself.model = string_null;\n\n// do the apropriate action\n\tother.invisible_time = 1;\n\tother.invisible_finished = self.cnt;\n\ts=ftos(rint(other.invisible_finished - time));\n\tbprint4 (PRINT_LOW, other.netname, \" recovered a Ring with \", s, \" seconds remaining!\\n\");\n      \n\n\tactivator = other;\n\tSUB_UseTargets();                               // fire all targets / killtargets\n};\n\n\nvoid(float timeleft) DropRing =\n{\n\tlocal entity    item;\n\n\titem = spawn();\n\titem.origin = self.origin;\n\t\n\titem.velocity_z = 300;\n\titem.velocity_x = -100 + (random() * 200);\n\titem.velocity_y = -100 + (random() * 200);\n\t\n\titem.flags = FL_ITEM;\n\titem.solid = SOLID_TRIGGER;\n\titem.movetype = MOVETYPE_TOSS;\n\titem.noise = \"items/inv1.wav\";\n\tsetmodel (item, \"progs/invisibl.mdl\");\n\tsetsize (item, '-16 -16 -24', '16 16 32');\n\titem.cnt = time + timeleft;\n\titem.touch = r_touch;\n\titem.nextthink = time + timeleft;    // remove after 30 seconds\n\titem.think = SUB_Remove;\n};\n\n/*\n============\nPlaceItem\n\nplants the object on the floor\n============\n*/\nvoid() PlaceItem =\n{\n\tlocal float     oldz;\n\n\tself.mdl = self.model;          // so it can be restored on respawn\n\tself.flags = FL_ITEM;           // make extra wide\n\tself.solid = SOLID_TRIGGER;\n\tself.movetype = MOVETYPE_TOSS;  \n\tself.velocity = '0 0 0';\n\tself.origin_z = self.origin_z + 6;\n\toldz = self.origin_z;\n\tif (!droptofloor())\n\t{\n\t\tdprint (\"Bonus item fell out of level at \");\n\t\tdprint (vtos(self.origin));\n\t\tdprint (\"\\n\");\n\t\tremove(self);\n\t\treturn;\n\t}\n};\n\n/*\n============\nStartItem\n\nSets the clipping size and plants the object on the floor\n============\n*/\nvoid() StartItem =\n{\n\tself.nextthink = time + 0.2;    // items start after other solids\n\tself.think = PlaceItem;\n};\n\n/*\n=========================================================================\n\nHEALTH BOX\n\n=========================================================================\n*/\n//\n// T_Heal: add health to an entity, limiting health to max_health\n// \"ignore\" will ignore max_health limit\n//\nfloat (entity e, float hamount, float ignore) T_Heal =\n{\n\tif (e.health <= 0)\n\t\treturn 0;\n\tif ((!ignore) && (e.health >= other.max_health))\n\t\treturn 0;\n\thamount = ceil(hamount);\n\n\te.health = e.health + hamount;\n\tif ((!ignore) && (e.health >= other.max_health))\n\t\te.health = other.max_health;\n\t\n\t\t\n\tif (e.health > e.max_health + 150)\n\t\te.health = e.max_health + 150;\n\n\te.healdecay = time + 5;\n\treturn 1;\n};\n\n/*QUAKED item_health (.3 .3 1) (0 0 0) (32 32 32) rotten megahealth\nHealth box. Normally gives 25 points.\nRotten box heals 5-10 points,\nmegahealth will add 100 health, then \nrot you down to your maximum health limit, \none point per second.\n*/\n\nvoid() health_touch;\n\nvoid() item_health =\n{       \n\tself.touch = health_touch;\n\n\tif (self.spawnflags & H_ROTTEN)\n\t{\n\t\tprecache_model(\"maps/b_bh10.bsp\");\n\n\t\tprecache_sound(\"items/r_item1.wav\");\n\t\tsetmodel(self, \"maps/b_bh10.bsp\");\n\t\tself.noise = \"items/r_item1.wav\";\n\t\tself.healamount = 15;\n\t\tself.healtype = 0;\n\t}\n\telse\n\tif (self.spawnflags & H_MEGA)\n\t{\n\t\tprecache_model(\"maps/b_bh100.bsp\");\n\t\tprecache_sound(\"items/r_item2.wav\");\n\t\tsetmodel(self, \"maps/b_bh100.bsp\");\n\t\tself.noise = \"items/r_item2.wav\";\n\t\tself.healamount = 100;\n\t\tself.healtype = 2;\n\t}\n\telse\n\t{\n\t\tprecache_model(\"maps/b_bh25.bsp\");\n\t\tprecache_sound(\"items/health1.wav\");\n\t\tsetmodel(self, \"maps/b_bh25.bsp\");\n\t\tself.noise = \"items/health1.wav\";\n\t\tself.healamount = 25;\n\t\tself.healtype = 1;\n\t}\n\tsetsize (self, '0 0 0', '32 32 56');\n\tStartItem ();\n};\n\n\nvoid() health_touch =\n{\n\tlocal   string  s;\n\t\n\tif (deathmatch == 4)\n\t\tif (other.invincible_time > time)\n\t\t\treturn;\n\n\tif (other.classname != \"player\")\n\t\treturn;\n\t\n\tif (self.healtype == H_MEGA) // Megahealth?  Ignore max_health...\n\t{\n\t\tif (other.health >= other.max_health + 150)\n\t\t\treturn;\n\t\tif (!T_Heal(other, self.healamount, 1))\n\t\t\treturn;\n\t}\n\telse\n\t{\n\t\tif (!T_Heal(other, self.healamount, 0))\n\t\t\treturn;\n\t}\n\t\n\ts = ftos(self.healamount);\n\tsprint3(other, PRINT_LOW, \"You receive \", s, \" health\\n\");\n\t\n// health touch sound\n\tsound(other, CHAN_ITEM, self.noise, 1, ATTN_NORM);\n\n\tstuffcmd (other, \"bf\\n\");\n\t\n\tself.model = string_null;\n\tself.solid = SOLID_NOT;\n\n\t// Megahealth = rot down the player's super health\n\tif (deathmatch) // TODO: are these rules right?\n\t{\n\t\tif (deathmatch != 2)            // deathmatch 2 is the silly old rules\n\t\t{\n\t\t\tif (self.healtype == H_MEGA)\n\t\t\t\tself.nextthink = time + 45; // should I account for health healed instead?\n\t\t\telse\n\t\t\t\tself.nextthink = time + 20;\n\t\t\tself.think = SUB_regen;\n\t\t}\n\t}\n\t\n\tactivator = other;\n\tSUB_UseTargets();                               // fire all targets / killtargets\n};      \n\n/*\n===============================================================================\n\nARMOR\n\n===============================================================================\n*/\n\nvoid() armor_touch;\n\nvoid() armor_touch =\n{\n\tlocal   float   type, value, bit;\n\t\n\tif (other.health <= 0)\n\t\treturn;\n\tif (other.classname != \"player\")\n\t\treturn;\n\n\tif (deathmatch == 4)\n\n\t\tif (other.invincible_time > 0)\n\t\t\treturn;\n\n\tswitch (self.classname)\n\t{\n\tcase \"item_armor1\":\n\t\ttype = 0.3;\n\t\tvalue = 100;\n\t\tbit = IT_ARMOR1;\n\t\tbreak;\n\tcase \"item_armor2\":\n\t\ttype = 0.6;\n\t\tvalue = 150;\n\t\tbit = IT_ARMOR2;\n\t\tbreak;\n\tcase \"item_armorInv\":\n\t\ttype = 0.8;\n\t\tvalue = 200;\n\t\tbit = IT_ARMOR3;\n\t\tbreak;\n\t}\n\n\tif (other.armortype*other.armorvalue >= type*value)\n\t\treturn;\n\t\t\n\tother.armortype = type;\n\tother.armorvalue = value;\n\tother.items = other.items - (other.items & (IT_ARMOR1 | IT_ARMOR2 | IT_ARMOR3)) + bit;\n\n\tself.solid = SOLID_NOT;\n\tself.model = string_null;\n\tif (deathmatch && deathmatch != 2)\n\t{\n\t\tself.nextthink = time + 20;\n\t\tself.think = SUB_regen;\n\t}\n\n\tsprint1(other, PRINT_LOW, \"You got armor\\n\");\n// armor touch sound\n\tsound(other, CHAN_ITEM, \"items/armor1.wav\", 1, ATTN_NORM);\n\tstuffcmd (other, \"bf\\n\");\n\t\n\tactivator = other;\n\tSUB_UseTargets();                               // fire all targets / killtargets\n};\n\n\n/*QUAKED item_armor1 (0 .5 .8) (-16 -16 0) (16 16 32)\n*/\n\nvoid() item_armor1 =\n{\n\tself.touch = armor_touch;\n\tprecache_model (\"progs/armor.mdl\");\n\tsetmodel (self, \"progs/armor.mdl\");\n\tself.skin = 0;\n\tsetsize (self, '-16 -16 0', '16 16 56');\n\tStartItem ();\n};\n\n/*QUAKED item_armor2 (0 .5 .8) (-16 -16 0) (16 16 32)\n*/\n\nvoid() item_armor2 =\n{\n\tself.touch = armor_touch;\n\tprecache_model (\"progs/armor.mdl\");\n\tsetmodel (self, \"progs/armor.mdl\");\n\tself.skin = 1;\n\tsetsize (self, '-16 -16 0', '16 16 56');\n\tStartItem ();\n};\n\n/*QUAKED item_armorInv (0 .5 .8) (-16 -16 0) (16 16 32)\n*/\n\nvoid() item_armorInv =\n{\n\tself.touch = armor_touch;\n\tprecache_model (\"progs/armor.mdl\");\n\tsetmodel (self, \"progs/armor.mdl\");\n\tself.skin = 2;\n\tsetsize (self, '-16 -16 0', '16 16 56');\n\tStartItem ();\n};\n\n/*\n===============================================================================\n\nWEAPONS\n\n===============================================================================\n*/\n\nvoid() bound_other_ammo =\n{\n\tif (other.ammo_shells_real > 100)\n\t\tother.ammo_shells_real = 100;\n\tif (other.ammo_nails_real > 200)\n\t\tother.ammo_nails_real = 200;\n\tif (other.ammo_rockets_real > 100)\n\t\tother.ammo_rockets_real = 100;               \n\tif (other.ammo_cells_real > 100)\n\t\tother.ammo_cells_real = 100;         \n};\n\n\nfloat(float w) RankForWeapon =\n{\n\tif (w == IT_LIGHTNING)\t\n\t\treturn 1;\n\tif (w == IT_ROCKET_LAUNCHER)\n\t\treturn 2;\n\tif (w == IT_SUPER_NAILGUN)\n\t\treturn 3;\n\tif (w == IT_GRENADE_LAUNCHER)\n\t\treturn 4;\n\tif (w == IT_SUPER_SHOTGUN)\n\t\treturn 5;\n\tif (w == IT_NAILGUN)\n\t\treturn 6;\n\treturn 7;\n};\n\nfloat (float w) WeaponCode =\n{\n\tif (w == IT_SUPER_SHOTGUN)\n\t\treturn 3;\n\tif (w == IT_NAILGUN)\n\t\treturn 4;\n\tif (w == IT_SUPER_NAILGUN)\n\t\treturn 5;\n\tif (w == IT_GRENADE_LAUNCHER)\n\t\treturn 6;\n\tif (w == IT_ROCKET_LAUNCHER)\n\t\treturn 7;\n\tif (w == IT_LIGHTNING)\n\t\treturn 8;\n\treturn 1;\n};\n\n/*\n=============\nDeathmatch_Weapon\n\nDeathmatch weapon change rules for picking up a weapon\n\n.float          ammo_shells, ammo_nails, ammo_rockets, ammo_cells;\n=============\n*/\nvoid(float old, float new) Deathmatch_Weapon =\n{\n\tlocal float or, nr;\n\n// change self.weapon if desired\n\tor = RankForWeapon (self.weapon);\n\tnr = RankForWeapon (new);\n\n\tif ( nr == IT_LIGHTNING && self.waterlevel > 1 )\n\t\treturn;\n\n\tif ( nr < or )\n\t\tW_WeaponSwitch (new);\n};\n\n/*\n=============\nweapon_touch\n=============\n*/\nfloat() W_BestWeapon;\n\nvoid() weapon_touch =\n{\n\tlocal   float   new, old;\n\tlocal   float   leave;\n\tlocal   entity  stemp;\n\n\t// For client weapon_switch\n\tlocal   float   w_switch;\n\n\tif (!(other.flags & FL_CLIENT))\n\t\treturn;\n\n\tw_switch = numberclientinfokey(other,\"w_switch\");\n\tif (w_switch == 0)\n\t\tw_switch = 8;\n\t\n\tif (deathmatch == 2 || deathmatch == 3 || deathmatch == 5)\n\t\tleave = 1;\n\telse\n\t\tleave = 0;\n\n\t// check if we have the weapon already\n\tif (leave && (other.items & self.weapon))\n\t\treturn;\n\tnew = self.weapon;\n\n\t// add ammo here\n\tswitch (new)\n\t{\n\tcase IT_SUPER_SHOTGUN:\t\t\n\t\tother.ammo_shells_real += 5;\n\t\tbreak;\n\tcase IT_NAILGUN:\n\tcase IT_SUPER_NAILGUN:\n\t\tother.ammo_nails_real += 30;\n\t\tbreak;\n\tcase IT_GRENADE_LAUNCHER:\n\tcase IT_ROCKET_LAUNCHER:\n\t\tother.ammo_rockets_real += 5;\n\t\tbreak;\n\tcase IT_LIGHTNING:\n\t\tother.ammo_cells_real += 15;\n\t\tbreak;\n\t}\n\t\n\tsprint3 (other, PRINT_LOW, \"You got the \", I_WeaponName(new), \"\\n\");\n// weapon touch sound\n\tsound (other, CHAN_ITEM, \"weapons/pkup.wav\", 1, ATTN_NORM);\n\tstuffcmd (other, \"bf\\n\");\n\n\tbound_other_ammo ();\n\tW_UpdateAmmoCounts(other);\n\n// change to the weapon\n\told = other.items;\n\tother.items = other.items | new;\n\t\n\tstemp = self;\n\tself = other;\n\n\tif ( WeaponCode(new) <= w_switch )\n\t{\n\t\tif (self.flags & FL_INWATER)\n\t\t{\n\t\t\tif (new != IT_LIGHTNING)\n\t\t\t{\n\t\t\t\tDeathmatch_Weapon (old, new);\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{                \n\t\t\tDeathmatch_Weapon (old, new);\n\t\t}\n\t}\n\n\tself = stemp;\n\n\tif (leave)\n\t\treturn;\n\n\tif (deathmatch != 3 || deathmatch != 5)\n\t{\n\t// remove it in single player, or setup for respawning in deathmatch\n\t\tself.model = string_null;\n\t\tself.solid = SOLID_NOT;\n\t\tif (deathmatch && deathmatch != 2)\n\t\t{\n\t\t\tself.nextthink = time + 30;\n\t\t\tself.think = SUB_regen;\n\t\t}\n\t}\n\tactivator = other;\n\tSUB_UseTargets();                               // fire all targets / killtargets\n};\n\n\n/*QUAKED weapon_supershotgun (0 .5 .8) (-16 -16 0) (16 16 32)\n*/\n\nvoid() weapon_supershotgun =\n{\n\tif (deathmatch <= 3)\n\t{\n\t\tprecache_model (\"progs/g_shot.mdl\");\n\t\tsetmodel (self, \"progs/g_shot.mdl\");\n\t\tself.weapon = IT_SUPER_SHOTGUN;\n\t\tself.touch = weapon_touch;\n\t\tsetsize (self, '-16 -16 0', '16 16 56');\n\t\tStartItem ();\n\t}\n\telse\n\t\tremove(self);\n};\n\n/*QUAKED weapon_nailgun (0 .5 .8) (-16 -16 0) (16 16 32)\n*/\n\nvoid() weapon_nailgun =\n{\n\tif (deathmatch <= 3)\n\t{\n\t\tprecache_model (\"progs/g_nail.mdl\");\n\t\tsetmodel (self, \"progs/g_nail.mdl\");\n\t\tself.weapon = IT_NAILGUN;\n\t\tself.touch = weapon_touch;\n\t\tsetsize (self, '-16 -16 0', '16 16 56');\n\t\tStartItem ();\n\t}\n\telse\n\t\tremove(self);\n};\n\n/*QUAKED weapon_supernailgun (0 .5 .8) (-16 -16 0) (16 16 32)\n*/\n\nvoid() weapon_supernailgun =\n{\n\tif (deathmatch <= 3)\n\t{\n\t\tprecache_model (\"progs/g_nail2.mdl\");\n\t\tsetmodel (self, \"progs/g_nail2.mdl\");\n\t\tself.weapon = IT_SUPER_NAILGUN;\n\t\tself.touch = weapon_touch;\n\t\tsetsize (self, '-16 -16 0', '16 16 56');\n\t\tStartItem ();\n\t}\n\telse\n\t\tremove(self);\n};\n\n/*QUAKED weapon_grenadelauncher (0 .5 .8) (-16 -16 0) (16 16 32)\n*/\n\nvoid() weapon_grenadelauncher =\n{\n\tif (deathmatch <= 3)\n\t{\n\t\tprecache_model (\"progs/g_rock.mdl\");\n\t\tsetmodel (self, \"progs/g_rock.mdl\");\n\t\tself.weapon = IT_GRENADE_LAUNCHER;\n\t\tself.touch = weapon_touch;\n\t\tsetsize (self, '-16 -16 0', '16 16 56');\n\t\tStartItem ();\n\t}\n\telse\n\t\tremove(self);\n};\n\n/*QUAKED weapon_rocketlauncher (0 .5 .8) (-16 -16 0) (16 16 32)\n*/\n\nvoid() weapon_rocketlauncher =\n{\n\tif (deathmatch <= 3)\n\t{\n\t\tprecache_model (\"progs/g_rock2.mdl\");\n\t\tsetmodel (self, \"progs/g_rock2.mdl\");\n\t\tself.weapon = IT_ROCKET_LAUNCHER;\n\t\tself.touch = weapon_touch;\n\t\tsetsize (self, '-16 -16 0', '16 16 56');\n\t\tStartItem ();\n\t}\n\telse\n\t\tremove(self);\n};\n\n\n/*QUAKED weapon_lightning (0 .5 .8) (-16 -16 0) (16 16 32)\n*/\n\nvoid() weapon_lightning =\n{\n\tif (deathmatch <= 3)\n\t{\n\t\tprecache_model (\"progs/g_light.mdl\");\n\t\tsetmodel (self, \"progs/g_light.mdl\");\n\t\tself.weapon = IT_LIGHTNING;\n\t\tself.touch = weapon_touch;\n\t\tsetsize (self, '-16 -16 0', '16 16 56');\n\t\tStartItem ();\n\t}\n\telse\n\t\tremove(self);\n};\n\n\n/*\n===============================================================================\n\nAMMO\n\n===============================================================================\n*/\n\nvoid() ammo_touch =\n{\nlocal entity    stemp;\nlocal float             best;\n\n\tif (other.classname != \"player\")\n\t\treturn;\n\tif (other.health <= 0)\n\t\treturn;\n\n// if the player was using his best weapon, change up to the new one if better          \n\tstemp = self;\n\tself = other;\n\tbest = W_BestWeapon();\n\tself = stemp;\n\n\n\tswitch (self.weapon)\n\t{\n\tcase 1: // shotgun\n\t\tif (other.ammo_shells_real >= 100)\n\t\t\treturn;\n\t\tother.ammo_shells_real = other.ammo_shells_real + self.ammo_count;\n\t\tbreak;\n\tcase 2: // spikes\n\t\tif (other.ammo_nails_real >= 200)\n\t\t\treturn;\n\t\tother.ammo_nails_real = other.ammo_nails_real + self.ammo_count;\n\t\tbreak;\n\tcase 3: // rockets\n\t\tif (other.ammo_rockets_real >= 100)\n\t\t\treturn;\n\t\tother.ammo_rockets_real = other.ammo_rockets_real + self.ammo_count;\n\t\tbreak;\n\tcase 4: // cells\n\t\tif (other.ammo_cells_real >= 100)\n\t\t\treturn;\n\t\tother.ammo_cells_real = other.ammo_cells_real + self.ammo_count;\n\t\tbreak;\n\t}\n\n\tbound_other_ammo ();\n\t\n\tsprint3 (other, PRINT_LOW, \"You got the \", self.netname, \"\\n\");\n// ammo touch sound\n\tsound (other, CHAN_ITEM, \"weapons/lock4.wav\", 1, ATTN_NORM);\n\tstuffcmd (other, \"bf\\n\");\n\n// change to a better weapon if appropriate\n\n\tstemp = self;\n\tself = other;\n\n\tif ( self.weapon == best )\n\t\tW_WeaponSwitch (W_BestWeapon ());\n\telse\n\t\tW_UpdateAmmoCounts (self);\n\n\tself = stemp;\n\n// remove it in single player, or setup for respawning in deathmatch\n\tself.model = string_null;\n\tself.solid = SOLID_NOT;\n\tif (deathmatch)\n\t{\n\t\tif (deathmatch != 2)\n\t\t\tself.nextthink = time + 30;\n\n\t\t// Xian -- If playing in DM 3.0 mode, halve the time ammo respawns        \n\t\tif (deathmatch == 3 || deathmatch == 5)        \n\t\t\tself.nextthink = time + 15;\n\n\t\tself.think = SUB_regen;\n\t}\n\n\tactivator = other;\n\tSUB_UseTargets();                               // fire all targets / killtargets\n};\n\n\n\n\nfloat WEAPON_BIG2 = 1;\n\n/*QUAKED item_shells (0 .5 .8) (0 0 0) (32 32 32) big\n*/\n\nvoid() item_shells =\n{\n\tif (deathmatch == 4)\n\t{\n\t\tremove(self);\n\t\treturn;\n\t}\n\n\tself.touch = ammo_touch;\n\n\tif (self.spawnflags & WEAPON_BIG2)\n\t{\n\t\tprecache_model (\"maps/b_shell1.bsp\");\n\t\tsetmodel (self, \"maps/b_shell1.bsp\");\n\t\tself.ammo_count = 40;\n\t}\n\telse\n\t{\n\t\tprecache_model (\"maps/b_shell0.bsp\");\n\t\tsetmodel (self, \"maps/b_shell0.bsp\");\n\t\tself.ammo_count = 20;\n\t}\n\tself.weapon = 1;\n\tself.netname = \"shells\";\n\tsetsize (self, '0 0 0', '32 32 56');\n\tStartItem ();\n};\n\n/*QUAKED item_spikes (0 .5 .8) (0 0 0) (32 32 32) big\n*/\n\nvoid() item_spikes =\n{\n\tif (deathmatch == 4)\n\t{\n\t\tremove(self);\n\t\treturn;\n\t}\n\n\tself.touch = ammo_touch;\n\n\tif (self.spawnflags & WEAPON_BIG2)\n\t{\n\t\tprecache_model (\"maps/b_nail1.bsp\");\n\t\tsetmodel (self, \"maps/b_nail1.bsp\");\n\t\tself.ammo_count = 50;\n\t}\n\telse\n\t{\n\t\tprecache_model (\"maps/b_nail0.bsp\");\n\t\tsetmodel (self, \"maps/b_nail0.bsp\");\n\t\tself.ammo_count = 25;\n\t}\n\tself.weapon = 2;\n\tself.netname = \"nails\";\n\tsetsize (self, '0 0 0', '32 32 56');\n\tStartItem ();\n\n};\n\n/*QUAKED item_rockets (0 .5 .8) (0 0 0) (32 32 32) big\n*/\n\nvoid() item_rockets =\n{\n\tif (deathmatch == 4)\n\t{\n\t\tremove(self);\n\t\treturn;\n\t}\n\n\tself.touch = ammo_touch;\n\n\tif (self.spawnflags & WEAPON_BIG2)\n\t{\n\t\tprecache_model (\"maps/b_rock1.bsp\");\n\t\tsetmodel (self, \"maps/b_rock1.bsp\");\n\t\tself.ammo_count = 10;\n\t}\n\telse\n\t{\n\t\tprecache_model (\"maps/b_rock0.bsp\");\n\t\tsetmodel (self, \"maps/b_rock0.bsp\");\n\t\tself.ammo_count = 5;\n\t}\n\tself.weapon = 3;\n\tself.netname = \"rockets\";\n\tsetsize (self, '0 0 0', '32 32 56');\n\tStartItem ();\n\n};\n\n\n/*QUAKED item_cells (0 .5 .8) (0 0 0) (32 32 32) big\n*/\n\nvoid() item_cells =\n{\n\tif (deathmatch == 4)\n\t{\n\t\tremove(self);\n\t\treturn;\n\t}\n\n\tself.touch = ammo_touch;\n\n\tif (self.spawnflags & WEAPON_BIG2)\n\t{\n\t\tprecache_model (\"maps/b_batt1.bsp\");\n\t\tsetmodel (self, \"maps/b_batt1.bsp\");\n\t\tself.ammo_count = 12;\n\t}\n\telse\n\t{\n\t\tprecache_model (\"maps/b_batt0.bsp\");\n\t\tsetmodel (self, \"maps/b_batt0.bsp\");\n\t\tself.ammo_count = 6;\n\t}\n\tself.weapon = 4;\n\tself.netname = \"cells\";\n\tsetsize (self, '0 0 0', '32 32 56');\n\tStartItem ();\n\n};\n\n\n/*QUAKED item_weapon (0 .5 .8) (0 0 0) (32 32 32) shotgun rocket spikes big\nDO NOT USE THIS!!!! IT WILL BE REMOVED!\n*/\n\nfloat WEAPON_SHOTGUN = 1;\nfloat WEAPON_ROCKET = 2;\nfloat WEAPON_SPIKES = 4;\nfloat WEAPON_MASK = 7;\nfloat WEAPON_BIG = 8;\nvoid() item_weapon =\n{\n\tlocal float weap;\n\t\t\n\tweap = self.spawnflags & WEAPON_MASK;\n\tself.touch = ammo_touch;\n\n\tself.spawnflags = 0;\n\tif (self.spawnflags & WEAPON_BIG)\n\n\t\tself.spawnflags |= WEAPON_BIG2;\n\n\tswitch (weap)\n\t{\n\tcase WEAPON_SHOTGUN:\n\t\tself.classname = \"item_shells\";\n\t\titem_shells();\n\t\treturn;\n\tcase WEAPON_SPIKES:\n\t\tself.classname = \"item_spikes\";\n\t\titem_spikes();\n\t\treturn;\n\tcase WEAPON_ROCKET:\n\t\tself.classname = \"item_rockets\";\n\t\titem_rockets();\n\t}\n};\n\n\n/*\n===============================================================================\n\nKEYS\n\n===============================================================================\n*/\n\nvoid() key_touch =\n{\n\tif (other.classname != \"player\")\n\t\treturn;\n\tif (other.health <= 0)\n\t\treturn;\n\tif (other.items & self.items)\n\t\treturn;\n\n\tsprint3 (other, PRINT_LOW, \"You got the \", self.netname, \"\\n\");\n\n\tsound (other, CHAN_ITEM, self.noise, 1, ATTN_NORM);\n\tstuffcmd (other, \"bf\\n\");\n\tother.items = other.items | self.items;\n\n\tself.solid = SOLID_NOT;\n\tself.model = string_null;\n\n\tactivator = other;\n\tSUB_UseTargets();                               // fire all targets / killtargets\n};\n\n\nvoid() key_setsounds =\n{\n\tswitch (world.worldtype)\n\t{\n\tcase WT_MEDIEVAL:\n\t\tprecache_sound (\"misc/medkey.wav\");\n\t\tself.noise = \"misc/medkey.wav\";\n\t\tbreak;\n\tcase WT_METAL:\n\t\tprecache_sound (\"misc/runekey.wav\");\n\t\tself.noise = \"misc/runekey.wav\";\n\t\tbreak;\n\tcase WT_BASE:\n\t\tprecache_sound2 (\"misc/basekey.wav\");\n\t\tself.noise = \"misc/basekey.wav\";\n\t\tbreak;\n\t}\n};\n\n/*QUAKED item_key1 (0 .5 .8) (-16 -16 -24) (16 16 32)\nSILVER key\nIn order for keys to work\nyou MUST set your maps\nworldtype to one of the\nfollowing:\n0: medieval\n1: metal\n2: base\n*/\n\nvoid() item_key1 =\n{\n\tif (deathmatch)\n\t{\n\t\tremove(self);\n\t\treturn;\n\t}\n\n\tswitch (world.worldtype)\n\t{\n\tcase WT_MEDIEVAL:\n\t\tprecache_model (\"progs/w_s_key.mdl\");\n\t\tsetmodel (self, \"progs/w_s_key.mdl\");\n\t\tself.netname = \"silver key\";\n\t\tbreak;\n\tcase WT_METAL:\n\t\tprecache_model (\"progs/m_s_key.mdl\");\n\t\tsetmodel (self, \"progs/m_s_key.mdl\");\n\t\tself.netname = \"silver runekey\";\n\t\tbreak;\n\tcase WT_BASE:\n\t\tprecache_model2 (\"progs/b_s_key.mdl\");\n\t\tsetmodel (self, \"progs/b_s_key.mdl\");\n\t\tself.netname = \"silver keycard\";\n\t\tbreak;\n\t}\n\n\tkey_setsounds();\n\tself.touch = key_touch;\n\tself.items = IT_KEY1;\n\tsetsize (self, '-16 -16 -24', '16 16 32');\n\tStartItem ();\n};\n\n/*QUAKED item_key2 (0 .5 .8) (-16 -16 -24) (16 16 32)\nGOLD key\nIn order for keys to work\nyou MUST set your maps\nworldtype to one of the\nfollowing:\n0: medieval\n1: metal\n2: base\n*/\n\nvoid() item_key2 =\n{\n\tif (deathmatch)\n\t{\n\t\tremove(self);\n\t\treturn;\n\t}\n\n\tswitch (world.worldtype)\n\t{\n\tcase WT_MEDIEVAL:\n\t\tprecache_model (\"progs/w_g_key.mdl\");\n\t\tsetmodel (self, \"progs/w_g_key.mdl\");\n\t\tself.netname = \"gold key\";\n\t\tbreak;\n\tcase WT_METAL:\n\t\tprecache_model (\"progs/m_g_key.mdl\");\n\t\tsetmodel (self, \"progs/m_g_key.mdl\");\n\t\tself.netname = \"gold runekey\";\n\t\tbreak;\n\tcase WT_BASE:\n\t\tprecache_model2 (\"progs/b_g_key.mdl\");\n\t\tsetmodel (self, \"progs/b_g_key.mdl\");\n\t\tself.netname = \"gold keycard\";\n\t\tbreak;\n\t}\n\tkey_setsounds();\n\tself.touch = key_touch;\n\tself.items = IT_KEY2;\n\tsetsize (self, '-16 -16 -24', '16 16 32');\n\tStartItem ();\n};\n\n\n\n/*\n===============================================================================\n\nEND OF LEVEL RUNES\n\n===============================================================================\n*/\n\nvoid() sigil_touch =\n{\n\tif (other.classname != \"player\")\n\t\treturn;\n\tif (other.health <= 0)\n\t\treturn;\n\n\tcenterprint (other, \"You got the rune!\");\n\n\tsound (other, CHAN_ITEM, self.noise, 1, ATTN_NORM);\n\tstuffcmd (other, \"bf\\n\");\n\tself.solid = SOLID_NOT;\n\tself.model = string_null;\n\tserverflags = serverflags | (self.spawnflags & 15);\n\tself.classname = \"\";            // so rune doors won't find it\n\t\n\tactivator = other;\n\tSUB_UseTargets();                               // fire all targets / killtargets\n};\n\n\n/*QUAKED item_sigil (0 .5 .8) (-16 -16 -24) (16 16 32) E1 E2 E3 E4\nEnd of level sigil, pick up to end episode and return to jrstart.\n*/\n\nvoid() item_sigil =\n{\n\tif (deathmatch)\n\t{\n\t\tremove(self);\n\t\treturn;\n\t}\n\n\tif (!self.spawnflags)\n\t\tobjerror (\"no spawnflags\");\n\n\tprecache_sound (\"misc/runekey.wav\");\n\tself.noise = \"misc/runekey.wav\";\n\n\tswitch (self.spawnflags & 15)\n\t{\n\tcase 1:\n\t\tprecache_model (\"progs/end1.mdl\");\n\t\tsetmodel (self, \"progs/end1.mdl\");\n\t\tbreak;\n\tcase 2:\n\t\tprecache_model2 (\"progs/end2.mdl\");\n\t\tsetmodel (self, \"progs/end2.mdl\");\n\t\tbreak;\n\tcase 4:\n\t\tprecache_model2 (\"progs/end3.mdl\");\n\t\tsetmodel (self, \"progs/end3.mdl\");\n\t\tbreak;\n\tcase 8:\n\t\tprecache_model2 (\"progs/end4.mdl\");\n\t\tsetmodel (self, \"progs/end4.mdl\");\n\t\tbreak;\n\t}\n\t\n\tself.touch = sigil_touch;\n\tsetsize (self, '-16 -16 -24', '16 16 32');\n\tStartItem ();\n};\n\n/*\n===============================================================================\n\nPOWERUPS\n\n===============================================================================\n*/\n\nvoid() powerup_touch =\n{\n\tif (other.classname != \"player\")\n\t\treturn;\n\tif (other.health <= 0)\n\t\treturn;\n\n\tsprint3 (other, PRINT_LOW, \"You got the \",  self.netname, \"\\n\");\n\n\tself.mdl = self.model;\n\n\tif (deathmatch)\n\t{\t\n\t\tif (self.items & (IT_INVULNERABILITY | IT_INVISIBILITY))\n\t\t\tself.nextthink = time + 60*5;\n\t\telse\n\t\t\tself.nextthink = time + 60;\n\t\n\t\tself.think = SUB_regen;\n\t}\n\n\tsound (other, CHAN_VOICE, self.noise, 1, ATTN_NORM);\n\tstuffcmd (other, \"bf\\n\");\n\tself.solid = SOLID_NOT;\n\tother.items = other.items | self.items;\n\tself.model = string_null;\n\n// do the apropriate action\n\tswitch (self.items)\n\t{\n\tcase IT_SUIT:\n\t\tother.rad_time = 1;\n\t\tother.radsuit_finished = time + 30;\n\t\tbreak;\n\tcase IT_INVULNERABILITY:\n\t\tother.invincible_time = 1;\n\t\tother.invincible_finished = time + 30;\n\t\tbreak;\n\tcase IT_INVISIBILITY:\n\t\tother.invisible_time = 1;\n\t\tother.invisible_finished = time + 30;\n\t\tbreak;\n\tcase IT_QUAD:\n\t\tif (deathmatch == 4)\n\t\t{\n\t\t\tother.armortype = 0;\n\t\t\tother.armorvalue = 0;\n\t\t\tother.ammo_cells_real = 0;\n\t\t\tW_UpdateAmmoCounts(other);\n\t\t}\n\t\tother.super_time = 1;\n\t\tother.super_damage_finished = time + 30;\n\t\tbreak;\n\t}\n\n\tactivator = other;\n\tSUB_UseTargets();                               // fire all targets / killtargets\n};\n\n\n\n/*QUAKED item_artifact_invulnerability (0 .5 .8) (-16 -16 -24) (16 16 32)\nPlayer is invulnerable for 30 seconds\n*/\nvoid() item_artifact_invulnerability =\n{\n\tself.touch = powerup_touch;\n\n\tprecache_model (\"progs/invulner.mdl\");\n\tprecache_sound (\"items/protect.wav\");\n\tprecache_sound (\"items/protect2.wav\");\n\tprecache_sound (\"items/protect3.wav\");\n\tself.noise = \"items/protect.wav\";\n\tsetmodel (self, \"progs/invulner.mdl\");\n\tself.netname = \"Pentagram of Protection\";\n\tself.effects = self.effects | ef_red;\n\tself.items = IT_INVULNERABILITY;\n\tsetsize (self, '-16 -16 -24', '16 16 32');\n\tStartItem ();\n};\n\n/*QUAKED item_artifact_envirosuit (0 .5 .8) (-16 -16 -24) (16 16 32)\nPlayer takes no damage from water or slime for 30 seconds\n*/\nvoid() item_artifact_envirosuit =\n{\n\tself.touch = powerup_touch;\n\n\tprecache_model (\"progs/suit.mdl\");\n\tprecache_sound (\"items/suit.wav\");\n\tprecache_sound (\"items/suit2.wav\");\n\tself.noise = \"items/suit.wav\";\n\tsetmodel (self, \"progs/suit.mdl\");\n\tself.netname = \"Biosuit\";\n\tself.items = IT_SUIT;\n\tsetsize (self, '-16 -16 -24', '16 16 32');\n\tStartItem ();\n};\n\n\n/*QUAKED item_artifact_invisibility (0 .5 .8) (-16 -16 -24) (16 16 32)\nPlayer is invisible for 30 seconds\n*/\nvoid() item_artifact_invisibility =\n{\n\tself.touch = powerup_touch;\n\n\tprecache_model (\"progs/invisibl.mdl\");\n\tprecache_sound (\"items/inv1.wav\");\n\tprecache_sound (\"items/inv2.wav\");\n\tprecache_sound (\"items/inv3.wav\");\n\tself.noise = \"items/inv1.wav\";\n\tsetmodel (self, \"progs/invisibl.mdl\");\n\tself.netname = \"Ring of Shadows\";\n\tself.items = IT_INVISIBILITY;\n\tsetsize (self, '-16 -16 -24', '16 16 32');\n\tStartItem ();\n};\n\n\n/*QUAKED item_artifact_super_damage (0 .5 .8) (-16 -16 -24) (16 16 32)\nThe next attack from the player will do 4x damage\n*/\nvoid() item_artifact_super_damage =\n{\n\tself.touch = powerup_touch;\n\n\tprecache_model (\"progs/quaddama.mdl\");\n\tprecache_sound (\"items/damage.wav\");\n\tprecache_sound (\"items/damage2.wav\");\n\tprecache_sound (\"items/damage3.wav\");\n\tself.noise = \"items/damage.wav\";\n\tsetmodel (self, \"progs/quaddama.mdl\");\n\tif (deathmatch == 4)\n\t\tself.netname = \"OctaPower\";\t\n\telse\n\t\tself.netname = \"Quad Damage\";\n\tself.items = IT_QUAD;\n\tself.effects = self.effects | ef_blue;\n\tsetsize (self, '-16 -16 -24', '16 16 32');\n\tStartItem ();\n};\n\n\n\n/*\n===============================================================================\n\nPLAYER BACKPACKS\n\n===============================================================================\n*/\n\nvoid() BackpackTouch =\n{\n\tlocal string    s;\n\tlocal   float   best, old, new;\n\tlocal           entity  stemp;\n\tlocal   float   acount;\n\tlocal   float   b_switch;\n\n\tif (deathmatch == 4)\n\t\tif (other.invincible_time > 0)\n\t\t\treturn;\n\n\tb_switch = numberclientinfokey(other,\"b_switch\");\n\tif (b_switch == 0)\n\t\tb_switch = 8;\n\n\tif (other.classname != \"player\")\n\t\treturn;\n\tif (other.health <= 0)\n\t\treturn;\n\t\t\n\tacount = 0;\n\tif (deathmatch == 4)\n\t{       \n\t\tother.health = other.health + 10;\n\t\tsprint1 (other, PRINT_LOW, \"You get 10 additional health\\n\");\n\t\tif ((other.health > 250) && (other.health < 300))\n\t\t\tsound (other, CHAN_ITEM, \"items/protect3.wav\", 1, ATTN_NORM);\n\t\telse\n\t\t\tsound (other, CHAN_ITEM, \"weapons/lock4.wav\", 1, ATTN_NORM);\n\t\tstuffcmd (other, \"bf\\n\");\n\t\tremove(self);\n\n\t\tif (other.health >299)\n\t\t{               \n\t\t\tif (other.invincible_time != 1)\n\t\t\t{                       \n\t\t\t\tother.invincible_time = 1;\n\t\t\t\tother.invincible_finished = time + 30;\n\t\t\t\tother.items = other.items | IT_INVULNERABILITY;\n\t\t\t\t\n\t\t\t\tother.super_time = 1;\n\t\t\t\tother.super_damage_finished = time + 30;\n\t\t\t\tother.items = other.items | IT_QUAD;\n\n\t\t\t\tother.ammo_cells_real = 0;\n\t\t\t\tW_UpdateAmmoCounts(other);\n\t\n\t\t\t\tsound (other, CHAN_VOICE, \"boss1/sight1.wav\", 1, ATTN_NORM);\n\t\t\t\tstuffcmd (other, \"bf\\n\");               \n\t\t\t\tbprint2 (PRINT_HIGH, other.netname, \" attains bonus powers!!!\\n\");\n\t\t\t}\n\t\t}\n\t\tself = other;\n\t\treturn;\n\t}\n\n\tsprint1 (other, PRINT_LOW, \"You get \");\n\n\tif ((other.items & self.items) == 0)\n\t{\n\t\tacount = 1;\n\t\tsprint2 (other, PRINT_LOW, \"the \", I_WeaponName(self.items));\n\t}\n \n// if the player was using his best weapon, change up to the new one if better          \n\tstemp = self;\n\tself = other;\n\tbest = W_BestWeapon();\n\tself = stemp;\n\n// change weapons\n\tother.ammo_shells_real = other.ammo_shells_real + self.ammo_shells;\n\tother.ammo_nails_real = other.ammo_nails_real + self.ammo_nails;\n\tother.ammo_rockets_real = other.ammo_rockets_real + self.ammo_rockets;\n\tother.ammo_cells_real = other.ammo_cells_real + self.ammo_cells;\n\n\tnew = self.items;\n\tif (!new)\n\t\tnew = other.weapon;\n\told = other.items;\n\tother.items = other.items | self.items;\n\t\n\tbound_other_ammo ();\n\n\tif (self.ammo_shells)\n\t{\n\t\ts = ftos(self.ammo_shells);\n\t\tif (acount)\n\t\t\tsprint3(other, PRINT_LOW, \". \", s, \" shells\");\n\t\telse\n\t\t\tsprint2(other, PRINT_LOW, s, \" shells\");\n\t\tacount = 1;\n\t}\n\tif (self.ammo_nails)\n\t{\n\t\ts = ftos(self.ammo_nails);\n\t\tif (acount)\n\t\t\tsprint3(other, PRINT_LOW, \". \", s, \" nails\");\n\t\telse\n\t\t\tsprint2(other, PRINT_LOW, s, \" nails\");\n\t\tacount = 1;\n\t}\n\tif (self.ammo_rockets)\n\t{\n\t\ts = ftos(self.ammo_rockets);\n\t\tif (acount)\n\t\t\tsprint3(other, PRINT_LOW, \". \", s, \" rockets\");\n\t\telse\n\t\t\tsprint2(other, PRINT_LOW, s, \" rockets\");\n\t\tacount = 1;\n\t}\n\tif (self.ammo_cells)\n\t{\n\t\ts = ftos(self.ammo_cells);\n\t\tif (acount)\n\t\t\tsprint3(other, PRINT_LOW, \". \", s, \" cells\");\n\t\telse\n\t\t\tsprint2(other, PRINT_LOW, s, \" cells\");\n\t\tacount = 1;\n\t}\n\t\n\tif ( (deathmatch==3 || deathmatch == 5) & ( (WeaponCode(new)==6) || (WeaponCode(new)==7) ) & (other.ammo_rockets_real < 5) )\n\t\tother.ammo_rockets_real = 5;\n\n\tsprint1 (other, PRINT_LOW, \"\\n\");\n// backpack touch sound\n\tsound (other, CHAN_ITEM, \"weapons/lock4.wav\", 1, ATTN_NORM);\n\tstuffcmd (other, \"bf\\n\");\n\n\tW_UpdateAmmoCounts(other);\n\n\tremove(self);\n\tself = other;\n\t\n// change to the weapon\n\t\n\tif ( WeaponCode(new) <= b_switch )\n\t{\n\t\tif (self.flags & FL_INWATER)\n\t\t{\n\t\t\tif (new != IT_LIGHTNING)\n\t\t\t{\n\t\t\t\tDeathmatch_Weapon (old, new);\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{                \n\t\t\tDeathmatch_Weapon (old, new);\n\t\t}\n\t}\n};\n\n/*\n===============\nDropBackpack\n===============\n*/\nvoid() DropBackpack =\n{\n\tlocal entity    item;\n\n\tif (!(self.ammo_shells_real + self.ammo_nails_real + self.ammo_rockets_real + self.ammo_cells_real))\n\t\treturn; // nothing in it\n\n\titem = spawn();\n\titem.origin = self.origin - '0 0 24';\n\t\n\titem.items = self.weapon;\n\n\titem.ammo_shells = self.ammo_shells_real;\n\titem.ammo_nails = self.ammo_nails_real;\n\titem.ammo_rockets = self.ammo_rockets_real;\n\titem.ammo_cells = self.ammo_cells_real;\n\n\titem.velocity_z = 300;\n\titem.velocity_x = -100 + (random() * 200);\n\titem.velocity_y = -100 + (random() * 200);\n\t\n\titem.flags = FL_ITEM;\n\titem.solid = SOLID_TRIGGER;\n\titem.movetype = MOVETYPE_TOSS;\n\tsetmodel (item, \"progs/backpack.mdl\");\n\tsetsize (item, '-16 -16 0', '16 16 56');\n\titem.touch = BackpackTouch;\n\t\n\titem.nextthink = time + 120;    // remove after 2 minutes\n\titem.think = SUB_Remove;\n};\n\n\n\u0000"
  },
  {
    "path": "quakec/basemod/knight.qc",
    "content": "/*\n==============================================================================\n\nKNIGHT\n\n==============================================================================\n*/\n\n$cd id1/models/knight\n$origin 0 0 24\n$base base\n$skin badass3\n\n$frame stand1 stand2 stand3 stand4 stand5 stand6 stand7 stand8 stand9\n\n$frame runb1 runb2 runb3 runb4 runb5 runb6 runb7 runb8\n\n//frame runc1 runc2 runc3 runc4 runc5 runc6\n\n$frame runattack1 runattack2 runattack3 runattack4 runattack5\n$frame runattack6 runattack7 runattack8 runattack9 runattack10\n$frame runattack11\n\n$frame pain1 pain2 pain3\n\n$frame painb1 painb2 painb3 painb4 painb5 painb6 painb7 painb8 painb9\n$frame painb10 painb11\n\n//frame attack1 attack2 attack3 attack4 attack5 attack6 attack7\n//frame attack8 attack9 attack10 attack11\n\n$frame attackb0 attackb1 attackb2 attackb3 attackb4 attackb5\n$frame attackb6 attackb7 attackb8 attackb9 attackb10\n\n$frame walk1 walk2 walk3 walk4 walk5 walk6 walk7 walk8 walk9\n$frame walk10 walk11 walk12 walk13 walk14\n\n$frame kneel1 kneel2 kneel3 kneel4 kneel5\n\n$frame standing2 standing3 standing4 standing5\n\n$frame death1 death2 death3 death4 death5 death6 death7 death8\n$frame death9 death10\n\n$frame deathb1 deathb2 deathb3 deathb4 deathb5 deathb6 deathb7 deathb8\n$frame deathb9 deathb10 deathb11\n\nvoid()\tknight_stand1\t=[\t$stand1,\tknight_stand2\t] {ai_stand();};\nvoid()\tknight_stand2\t=[\t$stand2,\tknight_stand3\t] {ai_stand();};\nvoid()\tknight_stand3\t=[\t$stand3,\tknight_stand4\t] {ai_stand();};\nvoid()\tknight_stand4\t=[\t$stand4,\tknight_stand5\t] {ai_stand();};\nvoid()\tknight_stand5\t=[\t$stand5,\tknight_stand6\t] {ai_stand();};\nvoid()\tknight_stand6\t=[\t$stand6,\tknight_stand7\t] {ai_stand();};\nvoid()\tknight_stand7\t=[\t$stand7,\tknight_stand8\t] {ai_stand();};\nvoid()\tknight_stand8\t=[\t$stand8,\tknight_stand9\t] {ai_stand();};\nvoid()\tknight_stand9\t=[\t$stand9,\tknight_stand1\t] {ai_stand();};\n\nvoid()\tknight_walk1\t=[\t$walk1,\t\tknight_walk2\t] {\nif (random() < 0.2)\n\tsound (self, CHAN_VOICE, \"knight/idle.wav\", 1,  ATTN_IDLE);\nai_walk(3);};\nvoid()\tknight_walk2\t=[\t$walk2,\t\tknight_walk3\t] {ai_walk(2);};\nvoid()\tknight_walk3\t=[\t$walk3,\t\tknight_walk4\t] {ai_walk(3);};\nvoid()\tknight_walk4\t=[\t$walk4,\t\tknight_walk5\t] {ai_walk(4);};\nvoid()\tknight_walk5\t=[\t$walk5,\t\tknight_walk6\t] {ai_walk(3);};\nvoid()\tknight_walk6\t=[\t$walk6,\t\tknight_walk7\t] {ai_walk(3);};\nvoid()\tknight_walk7\t=[\t$walk7,\t\tknight_walk8\t] {ai_walk(3);};\nvoid()\tknight_walk8\t=[\t$walk8,\t\tknight_walk9\t] {ai_walk(4);};\nvoid()\tknight_walk9\t=[\t$walk9,\t\tknight_walk10\t] {ai_walk(3);};\nvoid()\tknight_walk10\t=[\t$walk10,\tknight_walk11\t] {ai_walk(3);};\nvoid()\tknight_walk11\t=[\t$walk11,\tknight_walk12\t] {ai_walk(2);};\nvoid()\tknight_walk12\t=[\t$walk12,\tknight_walk13\t] {ai_walk(3);};\nvoid()\tknight_walk13\t=[\t$walk13,\tknight_walk14\t] {ai_walk(4);};\nvoid()\tknight_walk14\t=[\t$walk14,\tknight_walk1\t] {ai_walk(3);};\n\n\nvoid()\tknight_run1\t=[\t$runb1,\t\tknight_run2\t] {\nif (random() < 0.2)\n\tsound (self, CHAN_VOICE, \"knight/idle.wav\", 1,  ATTN_IDLE);\nai_run(16);};\nvoid()\tknight_run2\t=[\t$runb2,\t\tknight_run3\t] {ai_run(20);};\nvoid()\tknight_run3\t=[\t$runb3,\t\tknight_run4\t] {ai_run(13);};\nvoid()\tknight_run4\t=[\t$runb4,\t\tknight_run5\t] {ai_run(7);};\nvoid()\tknight_run5\t=[\t$runb5,\t\tknight_run6\t] {ai_run(16);};\nvoid()\tknight_run6\t=[\t$runb6,\t\tknight_run7\t] {ai_run(20);};\nvoid()\tknight_run7\t=[\t$runb7,\t\tknight_run8\t] {ai_run(14);};\nvoid()\tknight_run8\t=[\t$runb8,\t\tknight_run1\t] {ai_run(6);};\n\n\nvoid()\tknight_runatk1\t=[\t$runattack1,\t\tknight_runatk2\t]\n{\nif (random() > 0.5)\n\tsound (self, CHAN_WEAPON, \"knight/sword2.wav\", 1, ATTN_NORM);\nelse\n\tsound (self, CHAN_WEAPON, \"knight/sword1.wav\", 1, ATTN_NORM);\nai_charge(20);\n};\nvoid()\tknight_runatk2\t=[\t$runattack2,\tknight_runatk3\t] {ai_charge_side();};\nvoid()\tknight_runatk3\t=[\t$runattack3,\tknight_runatk4\t] {ai_charge_side();};\nvoid()\tknight_runatk4\t=[\t$runattack4,\tknight_runatk5\t] {ai_charge_side();};\nvoid()\tknight_runatk5\t=[\t$runattack5,\tknight_runatk6\t] {ai_melee_side(MOD_KNIGHT);};\nvoid()\tknight_runatk6\t=[\t$runattack6,\tknight_runatk7\t] {ai_melee_side(MOD_KNIGHT);};\nvoid()\tknight_runatk7\t=[\t$runattack7,\tknight_runatk8\t] {ai_melee_side(MOD_KNIGHT);};\nvoid()\tknight_runatk8\t=[\t$runattack8,\tknight_runatk9\t] {ai_melee_side(MOD_KNIGHT);};\nvoid()\tknight_runatk9\t=[\t$runattack9,\tknight_runatk10\t] {ai_melee_side(MOD_KNIGHT);};\nvoid()\tknight_runatk10\t=[\t$runattack10,\tknight_runatk11\t] {ai_charge_side();};\nvoid()\tknight_runatk11\t=[\t$runattack11,\tknight_run1\t] {ai_charge(10);};\n\nvoid()\tknight_atk1\t=[\t$attackb1,\t\tknight_atk2\t]\n{\nsound (self, CHAN_WEAPON, \"knight/sword1.wav\", 1, ATTN_NORM);\nai_charge(0);};\nvoid()\tknight_atk2\t=[\t$attackb2,\t\tknight_atk3\t] {ai_charge(7);};\nvoid()\tknight_atk3\t=[\t$attackb3,\t\tknight_atk4\t] {ai_charge(4);};\nvoid()\tknight_atk4\t=[\t$attackb4,\t\tknight_atk5\t] {ai_charge(0);};\nvoid()\tknight_atk5\t=[\t$attackb5,\t\tknight_atk6\t] {ai_charge(3);};\nvoid()\tknight_atk6\t=[\t$attackb6,\t\tknight_atk7\t] {ai_charge(4); ai_melee(MOD_KNIGHT);};\nvoid()\tknight_atk7\t=[\t$attackb7,\t\tknight_atk8\t] {ai_charge(1); ai_melee(MOD_KNIGHT);};\nvoid()\tknight_atk8\t=[\t$attackb8,\t\tknight_atk9\t] {ai_charge(3); ai_melee(MOD_KNIGHT);};\nvoid()\tknight_atk9\t=[\t$attackb9,\t\tknight_atk10] {ai_charge(1);};\nvoid()\tknight_atk10=[\t$attackb10,\t\tknight_run1\t] {ai_charge(5);};\n\n//void()\tknight_atk9\t=[\t$attack9,\t\tknight_atk10\t] {};\n//void()\tknight_atk10\t=[\t$attack10,\t\tknight_atk11\t] {};\n//void()\tknight_atk11\t=[\t$attack11,\t\tknight_run1\t] {};\n\n//===========================================================================\n\nvoid()\tknight_pain1\t=[\t$pain1,\t\tknight_pain2\t] {};\nvoid()\tknight_pain2\t=[\t$pain2,\t\tknight_pain3\t] {};\nvoid()\tknight_pain3\t=[\t$pain3,\t\tknight_run1\t] {};\n\nvoid()\tknight_painb1\t=[\t$painb1,\tknight_painb2\t] {ai_painforward(0);};\nvoid()\tknight_painb2\t=[\t$painb2,\tknight_painb3\t] {ai_painforward(3);};\nvoid()\tknight_painb3\t=[\t$painb3,\tknight_painb4\t] {};\nvoid()\tknight_painb4\t=[\t$painb4,\tknight_painb5\t] {};\nvoid()\tknight_painb5\t=[\t$painb5,\tknight_painb6\t] {ai_painforward(2);};\nvoid()\tknight_painb6\t=[\t$painb6,\tknight_painb7\t] {ai_painforward(4);};\nvoid()\tknight_painb7\t=[\t$painb7,\tknight_painb8\t] {ai_painforward(2);};\nvoid()\tknight_painb8\t=[\t$painb8,\tknight_painb9\t] {ai_painforward(5);};\nvoid()\tknight_painb9\t=[\t$painb9,\tknight_painb10\t] {ai_painforward(5);};\nvoid()\tknight_painb10\t=[\t$painb10,\tknight_painb11\t] {ai_painforward(0);};\nvoid()\tknight_painb11\t=[\t$painb11,\tknight_run1\t] {};\n\nvoid(entity attacker, float damage)\tknight_pain =\n{\n\tlocal float r;\n\n\tif (self.pain_finished > time)\n\t\treturn;\n\n\tr = random();\n\t\n\tsound (self, CHAN_VOICE, \"knight/khurt.wav\", 1, ATTN_NORM);\n\tself.pain_finished = time + 1;\n\n\tif (r < 0.85)\n\t\tknight_pain1 ();\n\telse\n\t\tknight_painb1 ();\n\t\n};\n\n//===========================================================================\n\n#ifdef 0\n// Not used\nvoid()\tknight_bow1\t=[\t$kneel1,\t\tknight_bow2\t] {ai_turn();};\nvoid()\tknight_bow2\t=[\t$kneel2,\t\tknight_bow3\t] {ai_turn();};\nvoid()\tknight_bow3\t=[\t$kneel3,\t\tknight_bow4\t] {ai_turn();};\nvoid()\tknight_bow4\t=[\t$kneel4,\t\tknight_bow5\t] {ai_turn();};\n\nvoid()\tknight_bow5\t=[\t$kneel5,\t\tknight_bow5\t] {ai_turn();};\n\nvoid()\tknight_bow6\t=[\t$kneel4,\t\tknight_bow7\t] {ai_turn();};\nvoid()\tknight_bow7\t=[\t$kneel3,\t\tknight_bow8\t] {ai_turn();};\nvoid()\tknight_bow8\t=[\t$kneel2,\t\tknight_bow9\t] {ai_turn();};\nvoid()\tknight_bow9\t=[\t$kneel1,\t\tknight_bow10\t] {ai_turn();};\nvoid()\tknight_bow10\t=[\t$walk1,\t\tknight_walk1\t] {ai_turn();};\n#endif\n\nvoid()\tknight_die1\t=[\t$death1,\tknight_die2\t] {};\nvoid()\tknight_die2\t=[\t$death2,\tknight_die3\t] {};\nvoid()\tknight_die3\t=[\t$death3,\tknight_die4\t] \n{self.solid = SOLID_NOT;};\nvoid()\tknight_die4\t=[\t$death4,\tknight_die5\t] {};\nvoid()\tknight_die5\t=[\t$death5,\tknight_die6\t] {};\nvoid()\tknight_die6\t=[\t$death6,\tknight_die7\t] {};\nvoid()\tknight_die7\t=[\t$death7,\tknight_die8\t] {};\nvoid()\tknight_die8\t=[\t$death8,\tknight_die9\t] {};\nvoid()\tknight_die9\t=[\t$death9,\tknight_die10] {};\nvoid()\tknight_die10=[\t$death10,\tknight_die10] {};\n\n\nvoid()\tknight_dieb1\t=[\t$deathb1,\tknight_dieb2\t] {};\nvoid()\tknight_dieb2\t=[\t$deathb2,\tknight_dieb3\t] {};\nvoid()\tknight_dieb3\t=[\t$deathb3,\tknight_dieb4\t] \t\n{self.solid = SOLID_NOT;};\nvoid()\tknight_dieb4\t=[\t$deathb4,\tknight_dieb5\t] {};\nvoid()\tknight_dieb5\t=[\t$deathb5,\tknight_dieb6\t] {};\nvoid()\tknight_dieb6\t=[\t$deathb6,\tknight_dieb7\t] {};\nvoid()\tknight_dieb7\t=[\t$deathb7,\tknight_dieb8\t] {};\nvoid()\tknight_dieb8\t=[\t$deathb8,\tknight_dieb9\t] {};\nvoid()\tknight_dieb9\t=[\t$deathb9,\tknight_dieb10] {};\nvoid()\tknight_dieb10 = [\t$deathb10,\tknight_dieb11] {};\nvoid()\tknight_dieb11 = [\t$deathb11,\tknight_dieb11] {};\n\n\nvoid() knight_die =\n{\n// check for gib\n\tif (self.health < -40)\n\t{\n\t\tsound (self, CHAN_VOICE, \"player/udeath.wav\", 1, ATTN_NORM);\n\t\tThrowHead (\"progs/h_knight.mdl\", self.health);\n\t\tThrowGib (\"progs/gib1.mdl\", self.health);\n\t\tThrowGib (\"progs/gib2.mdl\", self.health);\n\t\tThrowGib (\"progs/gib3.mdl\", self.health);\n\t\treturn;\n\t}\n\n// regular death\n\tsound (self, CHAN_VOICE, \"knight/kdeath.wav\", 1, ATTN_NORM);\n\tif (random() < 0.5)\n\t\tknight_die1 ();\n\telse\n\t\tknight_dieb1 ();\n};\n\nvoid() knight_attack =\n{\n\tlocal float\t\tlen;\n\t\n// decide if now is a good swing time\n\tlen = vlen(self.enemy.origin+self.enemy.view_ofs - (self.origin+self.view_ofs));\n\t\n\tif (len<80)\n\t\tknight_atk1 ();\n\telse\n\t\tknight_runatk1 ();\n};\n\n/*QUAKED monster_knight (1 0 0) (-16 -16 -24) (16 16 40) Ambush\n*/\nvoid() monster_knight =\n{\n\tif (deathmatch)\n\t{\n\t\tremove(self);\n\t\treturn;\n\t}\n\tprecache_model (\"progs/knight.mdl\");\n\tprecache_model (\"progs/h_knight.mdl\");\n\n\tprecache_sound (\"knight/kdeath.wav\");\n\tprecache_sound (\"knight/khurt.wav\");\n\tprecache_sound (\"knight/ksight.wav\");\n\tprecache_sound (\"knight/sword1.wav\");\n\tprecache_sound (\"knight/sword2.wav\");\n\tprecache_sound (\"knight/idle.wav\");\n\n\tself.solid = SOLID_SLIDEBOX;\n\tself.movetype = MOVETYPE_STEP;\n\n\tsetmodel (self, \"progs/knight.mdl\");\n\n\tsetsize (self, '-16 -16 -24', '16 16 40');\n\tself.health = 75;\n\n\tself.th_stand = knight_stand1;\n\tself.th_walk = knight_walk1;\n\tself.th_run = knight_run1;\n\tself.th_melee = knight_attack;\n\tself.th_pain = knight_pain;\n\tself.th_die = knight_die;\n\t\n\twalkmonster_start ();\n};\n"
  },
  {
    "path": "quakec/basemod/misc.qc",
    "content": "\n/*QUAKED info_null (0 0.5 0) (-4 -4 -4) (4 4 4)\nUsed as a positional target for spotlights, etc.\n*/\nvar void() info_null = SUB_Remove;\n\n/*QUAKED info_notnull (0 0.5 0) (-4 -4 -4) (4 4 4)\nUsed as a positional target for lightning.\n*/\nvoid() info_notnull =\n{\n};\n\n//============================================================================\n\nfloat START_OFF = 1;\n\nvoid() light_use =\n{\n\tif (self.spawnflags & START_OFF)\n\t{\n\t\tlightstyle(self.style, \"m\");\n\t\tself.spawnflags = self.spawnflags - START_OFF;\n\t}\n\telse\n\t{\n\t\tlightstyle(self.style, \"a\");\n\t\tself.spawnflags = self.spawnflags | START_OFF;\n\t}\n};\n\n/*QUAKED light (0 1 0) (-8 -8 -8) (8 8 8) START_OFF\nNon-displayed light.\nDefault light value is 300\nDefault style is 0\nIf targeted, it will toggle between on or off.\n*/\nvoid() light =\n{\n\tif (!self.targetname)\n\t{       // inert light\n\t\tremove(self);\n\t\treturn;\n\t}\n\t\n\tif (self.style >= 32)\n\t{\n\t\tself.use = light_use;\n\t\tif (self.spawnflags & START_OFF)\n\t\t\tlightstyle(self.style, \"a\");\n\t\telse\n\t\t\tlightstyle(self.style, \"m\");\n\t}\n};\n\n/*QUAKED light_fluoro (0 1 0) (-8 -8 -8) (8 8 8) START_OFF\nNon-displayed light.\nDefault light value is 300\nDefault style is 0\nIf targeted, it will toggle between on or off.\nMakes steady fluorescent humming sound\n*/\nvoid() light_fluoro =\n{\n\tif (self.style >= 32)\n\t{\n\t\tself.use = light_use;\n\t\tif (self.spawnflags & START_OFF)\n\t\t\tlightstyle(self.style, \"a\");\n\t\telse\n\t\t\tlightstyle(self.style, \"m\");\n\t}\n\t\n\tprecache_sound (\"ambience/fl_hum1.wav\");\n\tambientsound (self.origin, \"ambience/fl_hum1.wav\", 0.5, ATTN_STATIC);\n};\n\n/*QUAKED light_fluorospark (0 1 0) (-8 -8 -8) (8 8 8)\nNon-displayed light.\nDefault light value is 300\nDefault style is 10\nMakes sparking, broken fluorescent sound\n*/\nvoid() light_fluorospark =\n{\n\tif (!self.style)\n\t\tself.style = 10;\n\n\tprecache_sound (\"ambience/buzz1.wav\");\n\tambientsound (self.origin, \"ambience/buzz1.wav\", 0.5, ATTN_STATIC);\n};\n\n/*QUAKED light_globe (0 1 0) (-8 -8 -8) (8 8 8)\nSphere globe light.\nDefault light value is 300\nDefault style is 0\n*/\nvoid() light_globe =\n{\n\tprecache_model (\"progs/s_light.spr\");\n\tsetmodel (self, \"progs/s_light.spr\");\n\tself.alpha = 0.5;\n\tmakestatic (self);\n};\n\nvoid() FireAmbient =\n{\n\tprecache_sound (\"ambience/fire1.wav\");\n// attenuate fast\n\tambientsound (self.origin, \"ambience/fire1.wav\", 0.5, ATTN_STATIC);\n};\n\n/*QUAKED light_torch_small_walltorch (0 .5 0) (-10 -10 -20) (10 10 20)\nShort wall torch\nDefault light value is 200\nDefault style is 0\n*/\nvoid() light_torch_small_walltorch =\n{\n\tprecache_model (\"progs/flame.mdl\");\n\tsetmodel (self, \"progs/flame.mdl\");\n\tFireAmbient ();\n\tmakestatic (self);\n};\n\n/*QUAKED light_flame_large_yellow (0 1 0) (-10 -10 -12) (12 12 18)\nLarge yellow flame ball\n*/\nvoid() light_flame_large_yellow =\n{\n\tprecache_model (\"progs/flame2.mdl\");\n\tsetmodel (self, \"progs/flame2.mdl\");\n\tself.frame = 1;\n\tself.alpha = 0.5;\n\tFireAmbient ();\n\tmakestatic (self);\n};\n\n/*QUAKED light_flame_small_yellow (0 1 0) (-8 -8 -8) (8 8 8) START_OFF\nSmall yellow flame ball\n*/\nvoid() light_flame_small_yellow =\n{\n\tprecache_model (\"progs/flame2.mdl\");\n\tsetmodel (self, \"progs/flame2.mdl\");\n\tself.alpha = 0.5;\n\tFireAmbient ();\n\tmakestatic (self);\n};\n\n/*QUAKED light_flame_small_white (0 1 0) (-10 -10 -40) (10 10 40) START_OFF\nSmall white flame ball\n*/\nvoid() light_flame_small_white =\n{\n\tprecache_model (\"progs/flame2.mdl\");\n\tsetmodel (self, \"progs/flame2.mdl\");\n\tself.alpha = 0.5;\n\tFireAmbient ();\n\tmakestatic (self);\n};\n\n//============================================================================\n\n\n/*QUAKED misc_fireball (0 .5 .8) (-8 -8 -8) (8 8 8)\nLava Balls\n*/\n\nvoid() fire_fly =\n{\n\tlocal vector vel;\n\n\tvel_x = (random() * 100) - 50;\n\tvel_y = (random() * 100) - 50;\n\tvel_z = self.speed + (random() * 200);\n\n\tPRJ_FireProjectile (world, \"progs/lavaball.mdl\", self.origin, vel, PE_NONE, 20, MOD_FIREBALL, 5);\n\tnewmis.movetype = MOVETYPE_BOUNCE;\n\tnewmis.alpha = 0.9;\n\t\n\tself.nextthink = time + (random() * 5) + 3;\n\tself.think = fire_fly;\n};\n\nvoid() misc_fireball =\n{\n\t\n\tprecache_model (\"progs/lavaball.mdl\");\n\tself.classname = \"fireball\";\n\tself.nextthink = time + 0.1 + (random() * 4.9);\n\tself.think = fire_fly;\n\tif (!self.speed)\n\t\tself.speed = 1000;\n};\n\n//============================================================================\n\n\nvoid() barrel_explode =\n{\n\tself.takedamage = DAMAGE_NO;\n\tself.classname = \"explo_box\";\n\t// did say self.owner, self.enemy should be set by Killed function\n\tT_RadiusDamage (self, self.enemy, 160, 200, self, MOD_EXPLOBOX);\n\tTE_explosion(self.origin + '0 0 32');\n\tremove (self);\n};\n\n\n\n/*QUAKED misc_explobox (0 .5 .8) (0 0 0) (32 32 64)\nTESTING THING\n*/\n\nvoid() misc_explobox =\n{\n\tlocal float     oldz;\n\t\n\tself.solid = SOLID_BBOX;\n\tself.movetype = MOVETYPE_NONE;\n\tprecache_model (\"maps/b_explob.bsp\");\n\tsetmodel (self, \"maps/b_explob.bsp\");\n\tsetsize (self, '0 0 0', '32 32 64');\n\tprecache_sound (\"weapons/r_exp3.wav\");\n\tself.health = 20;\n\tself.th_die = barrel_explode;\n\tself.takedamage = DAMAGE_AIM;\n\n\tself.origin_z = self.origin_z + 2;\n\toldz = self.origin_z;\n\tdroptofloor();\n\tif (oldz - self.origin_z > 250)\n\t{\n\t\tdprint (\"item fell out of level at \");\n\t\tdprint (vtos(self.origin));\n\t\tdprint (\"\\n\");\n\t\tremove(self);\n\t}\n};\n\n\n\n\n/*QUAKED misc_explobox2 (0 .5 .8) (0 0 0) (32 32 64)\nSmaller exploding box, REGISTERED ONLY\n*/\n\nvoid() misc_explobox2 =\n{\n\tlocal float     oldz;\n\t\n\tself.solid = SOLID_BBOX;\n\tself.movetype = MOVETYPE_NONE;\n\tprecache_model2 (\"maps/b_exbox2.bsp\");\n\tsetmodel (self, \"maps/b_exbox2.bsp\");\n\tsetsize (self, '0 0 0', '32 32 32');\n\tprecache_sound (\"weapons/r_exp3.wav\");\n\tself.health = 20;\n\tself.th_die = barrel_explode;\n\tself.takedamage = DAMAGE_AIM;\n\n\tself.origin_z = self.origin_z + 2;\n\toldz = self.origin_z;\n\tdroptofloor();\n\tif (oldz - self.origin_z > 250)\n\t{\n\t\tdprint (\"item fell out of level at \");\n\t\tdprint (vtos(self.origin));\n\t\tdprint (\"\\n\");\n\t\tremove(self);\n\t}\n};\n\n//============================================================================\n\nfloat SPAWNFLAG_SUPERSPIKE      = 1;\nfloat SPAWNFLAG_LASER = 2;\n\nvoid() spikeshooter_use =\n{\n\tif (self.spawnflags & SPAWNFLAG_LASER)\n\t{\n\t\tsound (self, CHAN_VOICE, \"enforcer/enfire.wav\", 1, ATTN_NORM);\n\t\tPRJ_FireProjectile(self,\n\t\t\t\"progs/laser.mdl\",\n\t\t\tself.origin,\n\t\t\tself.movedir * 600,\n\t\t\tPE_LASER,\n\t\t\t15,\n\t\t\tMOD_LASER,\n\t\t\t5);\n\t\tnewmis.effects |= EF_DIMLIGHT;\n\t\tnewmis.alpha = 0.5;\n\t}\n\telse\n\t{\n\t\tsound (self, CHAN_VOICE, \"weapons/spike2.wav\", 1, ATTN_NORM);\n\t\tif (self.spawnflags & SPAWNFLAG_SUPERSPIKE)\n\t\t{\n\t\t\tPRJ_FireProjectile(self,\n\t\t\t\t\"progs/s_spike.mdl\",\n\t\t\t\tself.origin,\n\t\t\t\tself.movedir * 500,\n\t\t\t\tPE_SUPERSPIKE,\n\t\t\t\t18,\n\t\t\t\tMOD_SUPERSPIKE,\n\t\t\t\t6);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tPRJ_FireProjectile(self,\n\t\t\t\t\"progs/spike.mdl\",\n\t\t\t\tself.origin,\n\t\t\t\tself.movedir * 500,\n\t\t\t\tPE_SPIKE,\n\t\t\t\t9,\n\t\t\t\tMOD_SPIKE,\n\t\t\t\t6);\n\t\t}\n\t}\n};\n\nvoid() shooter_think =\n{\n\tspikeshooter_use ();\n\tself.nextthink = time + self.wait;\n\tnewmis.velocity = self.movedir * 500;\n};\n\n\n/*QUAKED trap_spikeshooter (0 .5 .8) (-8 -8 -8) (8 8 8) superspike laser\nWhen triggered, fires a spike in the direction set in QuakeEd.\nLaser is only for REGISTERED.\n*/\n\nvoid() trap_spikeshooter =\n{\n\tSetMovedir ();\n\tself.use = spikeshooter_use;\n\tif (self.spawnflags & SPAWNFLAG_LASER)\n\t{\n\t\tprecache_model2 (\"progs/laser.mdl\");\n\t\t\n\t\tprecache_sound2 (\"enforcer/enfire.wav\");\n\t\tprecache_sound2 (\"enforcer/enfstop.wav\");\n\t}\n\telse\n\t\tprecache_sound (\"weapons/spike2.wav\");\n};\n\n\n/*QUAKED trap_shooter (0 .5 .8) (-8 -8 -8) (8 8 8) superspike laser\nContinuously fires spikes.\n\"wait\" time between spike (1.0 default)\n\"nextthink\" delay before firing first spike, so multiple shooters can be stagered.\n*/\nvoid() trap_shooter =\n{\n\ttrap_spikeshooter ();\n\t\n\tif (self.wait == 0)\n\t\tself.wait = 1;\n\tself.nextthink = time + self.wait;\n\tself.think = shooter_think;\n};\n\n\n\n/*\n===============================================================================\n\n\n===============================================================================\n*/\n\nvoid() make_bubbles;\nvoid() bubble_bob;\n\n/*QUAKED air_bubbles (0 .5 .8) (-8 -8 -8) (8 8 8)\n\ntesting air bubbles\n*/\n\nnoref var void() air_bubbles = SUB_Remove;\n\nvoid() make_bubbles =\n{\nlocal entity    bubble;\n\n\tbubble = spawn();\n\tsetmodel (bubble, \"progs/s_bubble.spr\");\n\tsetorigin (bubble, self.origin);\n\tbubble.movetype = MOVETYPE_NOCLIP;\n\tbubble.solid = SOLID_NOT;\n\tbubble.velocity = '0 0 15';\n\tbubble.nextthink = time + 0.5;\n\tbubble.think = bubble_bob;\n\tbubble.classname = \"bubble\";\n\tbubble.frame = 0;\n\tbubble.bubble_state = 0;\n\tbubble.alpha = 0.4;\n\tsetsize (bubble, '-8 -8 -8', '8 8 8');\n\tself.nextthink = time + random() + 0.5;\n\tself.think = make_bubbles;\n};\n\nvoid() bubble_split =\n{\nlocal entity    bubble;\n\tbubble = spawn();\n\tsetmodel (bubble, \"progs/s_bubble.spr\");\n\tsetorigin (bubble, self.origin);\n\tbubble.movetype = MOVETYPE_NOCLIP;\n\tbubble.solid = SOLID_NOT;\n\tbubble.velocity = self.velocity;\n\tbubble.nextthink = time + 0.5;\n\tbubble.think = bubble_bob;\n\tbubble.classname = \"bubble\";\n\tbubble.frame = 1;\n\tbubble.bubble_state = 10;\n\tsetsize (bubble, '-8 -8 -8', '8 8 8');\n\tself.frame = 1;\n\tself.bubble_state = 10;\n\tif (self.waterlevel != 3)\n\t\tremove (self);\n};\n\nvoid() bubble_bob =\n{\nlocal float             rnd1, rnd2, rnd3;\n\n\tself.bubble_state = self.bubble_state + 1;\n\tif (self.bubble_state == 4)\n\t\tbubble_split();\n\tif (self.bubble_state == 20)\n\t\tremove(self);\n\n\trnd1 = self.velocity_x + (-10 + (random() * 20));\n\trnd2 = self.velocity_y + (-10 + (random() * 20));\n\trnd3 = self.velocity_z + 10 + random() * 10;\n\n\tif (rnd1 > 10)\n\t\trnd1 = 5;\n\tif (rnd1 < -10)\n\t\trnd1 = -5;\n\t\t\n\tif (rnd2 > 10)\n\t\trnd2 = 5;\n\tif (rnd2 < -10)\n\t\trnd2 = -5;\n\t\t\n\tif (rnd3 < 10)\n\t\trnd3 = 15;\n\tif (rnd3 > 30)\n\t\trnd3 = 25;\n\t\n\tself.velocity_x = rnd1;\n\tself.velocity_y = rnd2;\n\tself.velocity_z = rnd3;\n\t\t\n\tself.nextthink = time + 0.5;\n\tself.think = bubble_bob;\n};\n\n/*~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>\n~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~*/\n\n/*QUAKED viewthing (0 .5 .8) (-8 -8 -8) (8 8 8)\n\nJust for the debugging level.  Don't use\n*/\n\nnoref var void() viewthing = SUB_Remove;\n\n/*\n==============================================================================\n\nSIMPLE BMODELS\n\n==============================================================================\n*/\n\nvoid() func_wall_use =\n{       // change to alternate textures\n\tself.frame = 1 - self.frame;\n};\n\n/*QUAKED func_wall (0 .5 .8) ?\nThis is just a solid wall if not inhibitted\n*/\nvoid() func_wall =\n{\n\tself.angles = '0 0 0';\n\tself.movetype = MOVETYPE_PUSH;  // so it doesn't get pushed by anything\n\tself.solid = SOLID_BSP;\n\tself.use = func_wall_use;\n\tsetmodel (self, self.model);\n};\n\n\n/*QUAKED func_illusionary (0 .5 .8) ?\nA simple entity that looks solid but lets you walk through it.\n*/\nvoid() func_illusionary =\n{\n\tself.angles = '0 0 0';\n\tself.movetype = MOVETYPE_NONE;\n\tself.solid = SOLID_NOT;\n\tsetmodel (self, self.model);\n\tmakestatic (self);\n};\n\n/*QUAKED func_episodegate (0 .5 .8) ? E1 E2 E3 E4\nThis bmodel will appear if the episode has allready been completed, so players can't reenter it.\n*/\nvoid() func_episodegate =\n{\n\tif (!(serverflags & self.spawnflags))\n\t\treturn;                 // can still enter episode\n\n\tself.angles = '0 0 0';\n\tself.movetype = MOVETYPE_PUSH;  // so it doesn't get pushed by anything\n\tself.solid = SOLID_BSP;\n\tself.use = func_wall_use;\n\tsetmodel (self, self.model);\n};\n\n/*QUAKED func_bossgate (0 .5 .8) ?\nThis bmodel appears unless players have all of the episode sigils.\n*/\nvoid() func_bossgate =\n{\n\tif ( (serverflags & 15) == 15)\n\t{\n\t\tremove(self);\n\t\treturn;         // all episodes completed\n\t}\n\tself.angles = '0 0 0';\n\tself.movetype = MOVETYPE_PUSH;  // so it doesn't get pushed by anything\n\tself.solid = SOLID_BSP;\n\tself.use = func_wall_use;\n\tsetmodel (self, self.model);\n};\n\n//============================================================================\n/*QUAKED ambient_suck_wind (0.3 0.1 0.6) (-10 -10 -8) (10 10 8)\n*/\nvoid() ambient_suck_wind =\n{\n\tprecache_sound (\"ambience/suck1.wav\");\n\tambientsound (self.origin, \"ambience/suck1.wav\", 1, ATTN_STATIC);\n};\n\n/*QUAKED ambient_drone (0.3 0.1 0.6) (-10 -10 -8) (10 10 8)\n*/\nvoid() ambient_drone =\n{\n\tprecache_sound (\"ambience/drone6.wav\");\n\tambientsound (self.origin, \"ambience/drone6.wav\", 0.5, ATTN_STATIC);\n};\n\n/*QUAKED ambient_flouro_buzz (0.3 0.1 0.6) (-10 -10 -8) (10 10 8)\n*/\nvoid() ambient_flouro_buzz =\n{\n\tprecache_sound (\"ambience/buzz1.wav\");\n\tambientsound (self.origin, \"ambience/buzz1.wav\", 1, ATTN_STATIC);\n};\n/*QUAKED ambient_drip (0.3 0.1 0.6) (-10 -10 -8) (10 10 8)\n*/\nvoid() ambient_drip =\n{\n\tprecache_sound (\"ambience/drip1.wav\");\n\tambientsound (self.origin, \"ambience/drip1.wav\", 0.5, ATTN_STATIC);\n};\n/*QUAKED ambient_comp_hum (0.3 0.1 0.6) (-10 -10 -8) (10 10 8)\n*/\nvoid() ambient_comp_hum =\n{\n\tprecache_sound (\"ambience/comp1.wav\");\n\tambientsound (self.origin, \"ambience/comp1.wav\", 1, ATTN_STATIC);\n};\n/*QUAKED ambient_thunder (0.3 0.1 0.6) (-10 -10 -8) (10 10 8)\n*/\nvoid() ambient_thunder =\n{\n\tprecache_sound (\"ambience/thunder1.wav\");\n\tambientsound (self.origin, \"ambience/thunder1.wav\", 0.5, ATTN_STATIC);\n};\n/*QUAKED ambient_light_buzz (0.3 0.1 0.6) (-10 -10 -8) (10 10 8)\n*/\nvoid() ambient_light_buzz =\n{\n\tprecache_sound (\"ambience/fl_hum1.wav\");\n\tambientsound (self.origin, \"ambience/fl_hum1.wav\", 0.5, ATTN_STATIC);\n};\n/*QUAKED ambient_swamp1 (0.3 0.1 0.6) (-10 -10 -8) (10 10 8)\n*/\nvoid() ambient_swamp1 =\n{\n\tprecache_sound (\"ambience/swamp1.wav\");\n\tambientsound (self.origin, \"ambience/swamp1.wav\", 0.5, ATTN_STATIC);\n};\n/*QUAKED ambient_swamp2 (0.3 0.1 0.6) (-10 -10 -8) (10 10 8)\n*/\nvoid() ambient_swamp2 =\n{\n\tprecache_sound (\"ambience/swamp2.wav\");\n\tambientsound (self.origin, \"ambience/swamp2.wav\", 0.5, ATTN_STATIC);\n};\n\u0000"
  },
  {
    "path": "quakec/basemod/monsters.qc",
    "content": "/* ALL MONSTERS SHOULD BE 1 0 0 IN COLOR */\n\n/*\n================\nmonster_use\n\nUsing a monster makes it angry at the current activator\n================\n*/\nvoid() monster_use =\n{\n\tif (self.enemy)\n\t\treturn;\n\tif (self.health <= 0)\n\t\treturn;\n\tif (activator.items & IT_INVISIBILITY)\n\t\treturn;\n\tif (activator.flags & FL_NOTARGET)\n\t\treturn;\n\tif (activator.flags & FL_CLIENT)\n\t\treturn;\n\t\n\t// delay reaction so if the monster is teleported, its sound is still\n\t// heard\n\tself.enemy = activator;\n\tself.nextthink = time + 0.1;\n\tself.think = FoundTarget;\n};\n\n/*\n================\nmonster_death_use\n\nWhen a mosnter dies, it fires all of its targets with the current\nenemy as activator.\n================\n*/\nvoid() monster_death_use =\n{\n\t// fall to ground\n\tif (self.flags & FL_FLY)\n\t\tself.flags = self.flags - FL_FLY;\n\tif (self.flags & FL_SWIM)\n\t\tself.flags = self.flags - FL_SWIM;\n\n\tif (!self.target)\n\t\treturn;\n\n\tactivator = self.enemy;\n\tSUB_UseTargets ();\n};\n\n//============================================================================\n\nvoid() walkmonster_start_go =\n{\n\tself.origin_z = self.origin_z + 1;\t// raise off floor a bit\n\tdroptofloor();\n\t\n\tif (!walkmove(0,0))\n\t{\n\t\tdprint (\"walkmonster in wall at: \");\n\t\tdprint (vtos(self.origin));\n\t\tdprint (\"\\n\");\n\t}\n\t\n\tself.takedamage = DAMAGE_AIM;\n\n\tself.ideal_yaw = self.angles * '0 1 0';\n\tif (!self.yaw_speed)\n\t\tself.yaw_speed = 20;\n\tself.view_ofs = '0 0 25';\n\tself.use = monster_use;\n\t\n\tself.flags = self.flags | FL_MONSTER;\n\t\n\tif (self.target)\n\t{\n\t\tself.goalentity = self.movetarget = find(world, targetname, self.target);\n\t\tself.ideal_yaw = vectoyaw(self.goalentity.origin - self.origin);\n\t\tif (!self.movetarget)\n\t\t{\n\t\t\tdprint (\"Monster can't find target at \");\n\t\t\tdprint (vtos(self.origin));\n\t\t\tdprint (\"\\n\");\n\t\t}\n// this used to be an objerror\n\t\tif (self.movetarget.classname == \"path_corner\")\n\t\t\tself.th_walk ();\n\t\telse\n\t\t\tself.pausetime = 99999999;\n\t\t\tself.th_stand ();\n\t}\n\telse\n\t{\n\t\tself.pausetime = 99999999;\n\t\tself.th_stand ();\n\t}\n\n// spread think times so they don't all happen at same time\n\tself.nextthink = time + 0.1 + random()*0.5;\n};\n\n\nvoid() walkmonster_start =\n{\n// delay drop to floor to make sure all doors have been spawned\n// spread think times so they don't all happen at same time\n\tself.nextthink = time + 0.1 + random()*0.5;\n\tself.think = walkmonster_start_go;\n\ttotal_monsters = total_monsters + 1;\n};\n\n\n\nvoid() flymonster_start_go =\n{\n\tself.takedamage = DAMAGE_AIM;\n\n\tself.ideal_yaw = self.angles * '0 1 0';\n\tif (!self.yaw_speed)\n\t\tself.yaw_speed = 10;\n\tself.view_ofs = '0 0 25';\n\tself.use = monster_use;\n\n\tself.flags = self.flags | FL_FLY | FL_MONSTER;\n\n\tif (!walkmove(0,0))\n\t{\n\t\tdprint (\"flymonster in wall at: \");\n\t\tdprint (vtos(self.origin));\n\t\tdprint (\"\\n\");\n\t}\n\n\tif (self.target)\n\t{\n\t\tself.goalentity = self.movetarget = find(world, targetname, self.target);\n\t\tif (!self.movetarget)\n\t\t{\n\t\t\tdprint (\"Monster can't find target at \");\n\t\t\tdprint (vtos(self.origin));\n\t\t\tdprint (\"\\n\");\n\t\t}\n// this used to be an objerror\n\t\tif (self.movetarget.classname == \"path_corner\")\n\t\t\tself.th_walk ();\n\t\telse\n\t\t\tself.pausetime = 99999999;\n\t\t\tself.th_stand ();\n\t}\n\telse\n\t{\n\t\tself.pausetime = 99999999;\n\t\tself.th_stand ();\n\t}\n};\n\nvoid() flymonster_start =\n{\n// spread think times so they don't all happen at same time\n\tself.nextthink = time + 0.1 + random()*0.5;\n\tself.think = flymonster_start_go;\n\ttotal_monsters = total_monsters + 1;\n};\n\n\nvoid() swimmonster_start_go =\n{\n\tif (deathmatch)\n\t{\n\t\tremove(self);\n\t\treturn;\n\t}\n\n\tself.takedamage = DAMAGE_AIM;\n//\ttotal_monsters = total_monsters + 1;\n\n\tself.ideal_yaw = self.angles * '0 1 0';\n\tif (!self.yaw_speed)\n\t\tself.yaw_speed = 10;\n\tself.view_ofs = '0 0 10';\n\tself.use = monster_use;\n\t\n\tself.flags = self.flags | FL_SWIM | FL_MONSTER;\n\n\tif (self.target)\n\t{\n\t\tself.goalentity = self.movetarget = find(world, targetname, self.target);\n\t\tif (!self.movetarget)\n\t\t{\n\t\t\tdprint (\"Monster can't find target at \");\n\t\t\tdprint (vtos(self.origin));\n\t\t\tdprint (\"\\n\");\n\t\t}\n// this used to be an objerror\n\t\tself.ideal_yaw = vectoyaw(self.goalentity.origin - self.origin);\n\t\tself.th_walk ();\n\t}\n\telse\n\t{\n\t\tself.pausetime = 99999999;\n\t\tself.th_stand ();\n\t}\n\n// spread think times so they don't all happen at same time\n\tself.nextthink = time + 0.1 + random()*0.5;\n};\n\nvoid() swimmonster_start =\n{\n// spread think times so they don't all happen at same time\n\tself.nextthink = time + 0.1 + random()*0.5;\n\tself.think = swimmonster_start_go;\n\ttotal_monsters = total_monsters + 1;\n};\n\n\n"
  },
  {
    "path": "quakec/basemod/nomonst.qc",
    "content": "\n// QC for a monsterless compile\n\nvar void() monster_ogre = SUB_Remove;\nvar void() monster_demon1 = SUB_Remove;\nvar void() monster_shambler = SUB_Remove;\nvar void() monster_knight = SUB_Remove;\nvar void() monster_army = SUB_Remove;\nvar void() monster_wizard = SUB_Remove;\nvar void() monster_dog = SUB_Remove;\nvar void() monster_zombie = SUB_Remove;\nvar void() monster_boss = SUB_Remove;\nvar void() monster_tarbaby = SUB_Remove;\nvar void() monster_hell_knight = SUB_Remove;\nvar void() monster_fish = SUB_Remove;\nvar void() monster_shalrath = SUB_Remove;\nvar void() monster_enforcer = SUB_Remove;\nvar void() monster_oldone = SUB_Remove;\nvar void() event_lightning = SUB_Remove;\nvar void() path_corner = SUB_Remove;\n"
  },
  {
    "path": "quakec/basemod/obituary.qc",
    "content": "// client death messages\nenum {\n\tMOD_NONE,\n\tMOD_AXE,\n\tMOD_SHOTGUN,\n\tMOD_SUPERSHOTGUN,\n\tMOD_SPIKE,\n\tMOD_SUPERSPIKE,\n\tMOD_GRENADE,\n\tMOD_ROCKET,\n\tMOD_ROCKETRADIUS,\n\tMOD_SHAFT,\n\tMOD_SHAFTWATER,\n\tMOD_SHAFTSLIME,\n\tMOD_SHAFTLAVA,\n\tMOD_TELEFRAG,\n\tMOD_TELEFRAGDEFLECT,\n\tMOD_SQUISH,\n\tMOD_DROWN,\n\tMOD_SLIME,\n\tMOD_LAVA,\n\tMOD_EXPLOBOX,\n\tMOD_FALL,\n\tMOD_FIREBALL,\n\tMOD_EXIT,\n\tMOD_LASER,\n\tMOD_SELFWATER,\n\tMOD_HURT,\n\tMOD_DOG,\n\tMOD_SOLDIER,\n\tMOD_ENFORCER,\n\tMOD_OGRE,\n\tMOD_WIZARD,\n\tMOD_DEMON,\n\tMOD_KNIGHT,\n\tMOD_HKNIGHT,\n\tMOD_SHALRATH,\n\tMOD_SHAMBLER,\n\tMOD_FISH,\n\tMOD_TARBABY,\n\tMOD_ZOMBIE,\n\tMOD_CHTHON\n};\n\nvoid(string targ, INTEGER mod) SuicideMessage =\n{\n\tstring s, t, u;\n\n\ts = \"\";\n\tt = \"\";\n\tu = \"\";\n\n\tswitch (mod)\n\t{\n\tcase MOD_GRENADE:\n\t\ts = targ;\n\t\tt = \" tries to put the pin back in\";\n\t\tbreak;\n\tcase MOD_SHAFTWATER:\n\t\ts = targ;\n\t\tt = \" discharges into the water.\";\n\t\tbreak;\n\tcase MOD_SHAFTSLIME:\n\t\ts = targ;\n\t\tt = \" discharges into the slime\";\n\t\tbreak;\n\tcase MOD_SHAFTLAVA:\n\t\ts = targ;\n\t\tt = \" discharges into the lava\";\n\t\tbreak;\n\tcase MOD_TELEFRAGDEFLECT:\n\t\ts = \"Satan's power deflects \";\n\t\tt = targ;\n\t\tu = \"'s telefrag\";\n\t\tbreak;\n\tcase MOD_SELFWATER:\n\t\ts = targ;\n\t\tt = \" electrocutes himself.\";\n\t\tbreak;\n\tcase MOD_EXPLOBOX:\n\t\ts = targ;\n\t\tt = \" blew himself up\";\n\t\tbreak;\n\tdefault:\n\t\ts = targ;\n\t\tt = \" becomes bored with life\";\n\t}\n\n\tbprint4(PRINT_MEDIUM, s, t, u, \"\\n\");\n}\n\nvoid(string targ, string attacker, INTEGER mod) KillMessage =\n{\n\tstring s, t, u, v, w;\n\n\ts = \"\";\n\tt = \"\";\n\tu = \"\";\n\tv = \"\";\n\tw = \"\";\n\n\tswitch (mod)\n\t{\n\tcase MOD_AXE:\n\t\ts = targ;\n\t\tt = \" was ax-murdered by \";\n\t\tu = attacker;\n\t\tbreak;\n\tcase MOD_SHOTGUN:\n\t\ts = targ;\n\t\tt = \" chewed on \";\n\t\tu = attacker;\n\t\tv = \"'s boomstick\";\n\t\tbreak;\n\tcase MOD_SUPERSHOTGUN:\n\t\ts = targ;\n\t\tt = \" ate two loads of \";\n\t\tu = attacker;\n\t\tv = \"'s buckshot\";\n\t\tbreak;\n\tcase MOD_SPIKE:\n\t\ts = targ;\n\t\tt = \" was nailed by \";\n\t\tu = attacker;\n\t\tbreak;\n\tcase MOD_SUPERSPIKE:\n\t\ts = targ;\n\t\tt = \" was punctured by \";\n\t\tu = attacker;\n\t\tbreak;\n\tcase MOD_GRENADE:\n\t\ts = targ;\n\t\tt = \" eats \";\n\t\tu = attacker;\n\t\tv = \"'s pineapple\";\n\t\tbreak;\n\tcase MOD_ROCKET:\n\tcase MOD_ROCKETRADIUS:\n\t\ts = targ;\n\t\tt = \" rides \";\n\t\tu = attacker;\n\t\tv = \"'s rocket\";\n\t\tbreak;\n\tcase MOD_SHAFT:\n\t\ts = targ;\n\t\tt = \" accepts \";\n\t\tu = attacker;\n\t\tv = \"'s shaft\";\n\t\tbreak;\n\tcase MOD_SHAFTWATER:\n\tcase MOD_SHAFTSLIME:\n\tcase MOD_SHAFTLAVA:\n\t\ts = targ;\n\t\tt = \" accepts \";\n\t\tu = attacker;\n\t\tv = \"'s discharge\";\n\t\tbreak;\n\tcase MOD_TELEFRAG:\n\t\ts = targ;\n\t\tt = \" was telefragged by \";\n\t\tu = attacker;\n\t\tbreak;\n\tcase MOD_TELEFRAGDEFLECT:\n\t\ts = targ;\n\t\tt = \" was telefragged by \";\n\t\tu = attacker;\n\t\tv = \"'s Satan's power\";\n\t\tbreak;\n\tcase MOD_SQUISH:\n\t\ts = attacker;\n\t\tt = \" squishes \";\n\t\tu = targ;\n\t\tbreak;\n\tcase MOD_EXPLOBOX:\n\t\ts = targ;\n\t\tt = \" was blown up by \";\n\t\tu = attacker;\n\t\tbreak;\n\tdefault:\n\t\ts = targ;\n\t\tt = \" was killed by \";\n\t\tu = attacker;\n\t}\n\n\tbprint6(PRINT_MEDIUM, s, t, u, v, w, \"\\n\");\n}\n\nvoid(string targ, string attacker, INTEGER mod) TeamKillMessage =\n{\n\tstring s, t, u, v, w;\n\tfloat rnum;\n\n\ts = \"\";\n\tt = \"\";\n\tu = \"\";\n\tv = \"\";\n\tw = \"\";\n\n\tswitch (mod)\n\t{\n\tcase MOD_SQUISH:\n\t\ts = attacker;\n\t\tt = \" squishes teammate \";\n\t\tu = targ;\n\t\tbreak;\n\tdefault:\n\t\trnum = random();\n\n\t\ts = attacker;\n\n\t\tif (rnum < 0.25)\n\t\t\tt = \" mows down teammate \";\n\t\telse if (rnum < 0.5)\n\t\t{\n\t\t\tt = \" checks his glasses and sees \";\n\t\t\tv = \" dead\";\n\t\t}\n\t\telse if (rnum < 0.75)\n\t\t{\n\t\t\tt = \" frags \";\n\t\t\tv = \" for the other team\";\n\t\t}\n\t\telse\n\t\t\tt = \" loses his friend \";\n\n\t\tu = targ;\n\t}\n\n\tbprint6(PRINT_MEDIUM, s, t, u, v, w, \"\\n\");\n}\n\nvoid(string targ, INTEGER mod) WorldKillMessage =\n{\n\tstring s, t, u;\n\n\ts = \"\";\n\tt = \"\";\n\tu = \"\";\n\n\tswitch (mod)\n\t{\n\tcase MOD_SPIKE:\n\tcase MOD_SUPERSPIKE:\n\t\ts = targ;\n\t\tt = \" was spiked\";\n\t\tbreak;\n\tcase MOD_SQUISH:\n\t\ts = targ;\n\t\tt = \"  was squished\";\n\t\tbreak;\n\tcase MOD_DROWN:\n\t\ts = targ;\n\n\t\tif (random() < 0.5)\n\t\t\tt = \" sleeps with the fishes\";\n\t\telse\n\t\t\tt = \" sucks it down\";\n\t\tbreak;\n\tcase MOD_SLIME:\n\t\ts = targ;\n\n\t\tif (random() < 0.5)\n\t\t\tt = \" gulped a load of slime\";\n\t\telse\n\t\t\tt = \" can't exist on slime alone\";\n\t\tbreak;\n\tcase MOD_LAVA:\n\t\ts = targ;\n\n\t\tif (random() < 0.5)\n\t\t\tt = \" turned into hot slag\";\n\t\telse\n\t\t\tt = \" visits the Volcano God\";\n\t\tbreak;\n\tcase MOD_EXPLOBOX:\n\t\ts = targ;\n\t\tt = \" blew up\";\n\t\tbreak;\n\tcase MOD_FALL:\n\t\ts = targ;\n\t\tt = \" fell to his death\";\n\t\tbreak;\n\tcase MOD_FIREBALL:\n\t\ts = targ;\n\t\tt = \" ate a lavaball\";\n\t\tbreak;\n\tcase MOD_EXIT:\n\t\ts = targ;\n\t\tt = \" tried to leave\";\n\t\tbreak;\n\tcase MOD_LASER:\n\t\ts = targ;\n\t\tt = \" was zapped\";\n\t\tbreak;\n#ifdef MONSTERS\n\tcase MOD_DOG:\n\t\ts = targ;\n\t\tt = \" was mauled by a Rottweiler\";\n\t\tbreak;\n\tcase MOD_SOLDIER:\n\t\ts = targ;\n\t\tt = \" was shot by a Grunt\";\n\t\tbreak;\n\tcase MOD_ENFORCER:\n\t\ts = targ;\n\t\tt = \" was blasted by an Enforcer\";\n\t\tbreak;\n\tcase MOD_OGRE:\n\t\ts = targ;\n\t\tt = \" was destroyed by an Ogre\";\n\t\tbreak;\n\tcase MOD_WIZARD:\n\t\ts = targ;\n\t\tt = \" was scragged by a Scrag\";\n\t\tbreak;\n\tcase MOD_DEMON:\n\t\ts = targ;\n\t\tt = \" was eviscerated by a Fiend\";\n\t\tbreak;\n\tcase MOD_KNIGHT:\n\t\ts = targ;\n\t\tt = \" was slashed by a Knight\";\n\t\tbreak;\n\tcase MOD_HKNIGHT:\n\t\ts = targ;\n\t\tt = \" was slain by a Death Knight\";\n\t\tbreak;\n\tcase MOD_SHALRATH:\n\t\ts = targ;\n\t\tt = \"  was exploded by a Vore\";\n\t\tbreak;\n\tcase MOD_SHAMBLER:\n\t\ts = targ;\n\t\tt = \" was smashed by a Shambler\";\n\t\tbreak;\n\tcase MOD_FISH:\n\t\ts = targ;\n\t\tt = \" was fed to the Rotfish\";\n\t\tbreak;\n\tcase MOD_TARBABY:\n\t\ts = targ;\n\t\tt = \" was slimed by a Spawn\";\n\t\tbreak;\n\tcase MOD_ZOMBIE:\n\t\ts = targ;\n\t\tt = \" joins the Zombies\";\n\t\tbreak;\n\tcase MOD_CHTHON:\n\t\ts = targ;\n\t\tt = \" fell to Chthon's wrath\";\n\t\tbreak;\n#endif\n\tdefault:\n\t\ts = targ;\n\t\tt = \" died\";\n\t}\n\n\tbprint4(PRINT_MEDIUM, s, t, u, \"\\n\");\n}"
  },
  {
    "path": "quakec/basemod/ogre.qc",
    "content": "/*\n==============================================================================\n\nOGRE\n\n==============================================================================\n*/\n\n$cd id1/models/ogre_c\n$origin 0 0 24\n$base base\t\t\n$skin base\n\n$frame\tstand1 stand2 stand3 stand4 stand5 stand6 stand7 stand8 stand9\n\n$frame walk1 walk2 walk3 walk4 walk5 walk6 walk7\n$frame walk8 walk9 walk10 walk11 walk12 walk13 walk14 walk15 walk16\n\n$frame run1 run2 run3 run4 run5 run6 run7 run8\n\n$frame swing1 swing2 swing3 swing4 swing5 swing6 swing7\n$frame swing8 swing9 swing10 swing11 swing12 swing13 swing14\n\n$frame smash1 smash2 smash3 smash4 smash5 smash6 smash7\n$frame smash8 smash9 smash10 smash11 smash12 smash13 smash14\n\n$frame shoot1 shoot2 shoot3 shoot4 shoot5 shoot6\n\n$frame pain1 pain2 pain3 pain4 pain5\n\n$frame painb1 painb2 painb3\n\n$frame painc1 painc2 painc3 painc4 painc5 painc6\n\n$frame paind1 paind2 paind3 paind4 paind5 paind6 paind7 paind8 paind9 paind10\n$frame paind11 paind12 paind13 paind14 paind15 paind16\n\n$frame paine1 paine2 paine3 paine4 paine5 paine6 paine7 paine8 paine9 paine10\n$frame paine11 paine12 paine13 paine14 paine15\n\n$frame death1 death2 death3 death4 death5 death6\n$frame death7 death8 death9 death10 death11 death12\n$frame death13 death14\n\n$frame bdeath1 bdeath2 bdeath3 bdeath4 bdeath5 bdeath6\n$frame bdeath7 bdeath8 bdeath9 bdeath10\n\n$frame pull1 pull2 pull3 pull4 pull5 pull6 pull7 pull8 pull9 pull10 pull11\n\n//=============================================================================\n\n/*\n================\nOgreFireGrenade\n================\n*/\nvoid() OgreFireGrenade =\n{\n\tlocal vector vel;\n\t\n\tmuzzleflash();\n\tsound (self, CHAN_WEAPON, \"weapons/grenade.wav\", 1, ATTN_NORM);\n\n\tvel = normalize(self.enemy.origin - self.origin) * 600;\n\tvel_z = 200;\n\n\tPRJ_FireProjectile(self, \"progs/grenade.mdl\", self.origin, vel, PE_EXPLOSIONGROUND, 0, 0, 2.5);\n\tPRJ_SetRadiusDamage(40, 80, MOD_OGRE);\n\tPRJ_SetBouncyProjectile();\n};\n\n\n//=============================================================================\n\n/*\n================\nchainsaw\n\nFIXME\n================\n*/\nvoid(float side) chainsaw =\n{\nlocal vector\tdelta;\nlocal float \tldmg;\n\n\tif (!self.enemy)\n\t\treturn;\n\tif (!CanDamage (self.enemy, self))\n\t\treturn;\n\n\tai_charge(10);\n\n\tdelta = self.enemy.origin - self.origin;\n\n\tif (vlen(delta) > 100)\n\t\treturn;\n\t\t\n\tldmg = (random() + random() + random()) * 4;\n\tT_Damage (self.enemy, self, self, ldmg, MOD_OGRE);\n\t\n\tif (side)\n\t{\n\t\tmakevectors (self.angles);\n\t\tif (side == 1)\n\t\t\tSpawnMeatSpray (self.origin + v_forward*16, crandom() * 100 * v_right);\n\t\telse\n\t\t\tSpawnMeatSpray (self.origin + v_forward*16, side * v_right);\n\t}\n};\n\n\nvoid() ogre_stand1\t=[\t$stand1,\togre_stand2\t] {ai_stand();};\nvoid() ogre_stand2\t=[\t$stand2,\togre_stand3\t] {ai_stand();};\nvoid() ogre_stand3\t=[\t$stand3,\togre_stand4\t] {ai_stand();};\nvoid() ogre_stand4\t=[\t$stand4,\togre_stand5\t] {ai_stand();};\nvoid() ogre_stand5\t=[\t$stand5,\togre_stand6\t] {\nif (random() < 0.2)\n\tsound (self, CHAN_VOICE, \"ogre/ogidle.wav\", 1, ATTN_IDLE);\nai_stand();\n};\nvoid() ogre_stand6\t=[\t$stand6,\togre_stand7\t] {ai_stand();};\nvoid() ogre_stand7\t=[\t$stand7,\togre_stand8\t] {ai_stand();};\nvoid() ogre_stand8\t=[\t$stand8,\togre_stand9\t] {ai_stand();};\nvoid() ogre_stand9\t=[\t$stand9,\togre_stand1\t] {ai_stand();};\n\nvoid() ogre_walk1\t=[\t$walk1,\t\togre_walk2\t] {ai_walk(3);};\nvoid() ogre_walk2\t=[\t$walk2,\t\togre_walk3\t] {ai_walk(2);};\nvoid() ogre_walk3\t=[\t$walk3,\t\togre_walk4\t] {\nai_walk(2);\nif (random() < 0.2)\n\tsound (self, CHAN_VOICE, \"ogre/ogidle.wav\", 1, ATTN_IDLE);\n};\nvoid() ogre_walk4\t=[\t$walk4,\t\togre_walk5\t] {ai_walk(2);};\nvoid() ogre_walk5\t=[\t$walk5,\t\togre_walk6\t] {ai_walk(2);};\nvoid() ogre_walk6\t=[\t$walk6,\t\togre_walk7\t] {\nai_walk(5);\nif (random() < 0.1)\n\tsound (self, CHAN_VOICE, \"ogre/ogdrag.wav\", 1, ATTN_IDLE);\n};\nvoid() ogre_walk7\t=[\t$walk7,\t\togre_walk8\t] {ai_walk(3);};\nvoid() ogre_walk8\t=[\t$walk8,\t\togre_walk9\t] {ai_walk(2);};\nvoid() ogre_walk9\t=[\t$walk9,\t\togre_walk10\t] {ai_walk(3);};\nvoid() ogre_walk10\t=[\t$walk10,\togre_walk11\t] {ai_walk(1);};\nvoid() ogre_walk11\t=[\t$walk11,\togre_walk12\t] {ai_walk(2);};\nvoid() ogre_walk12\t=[\t$walk12,\togre_walk13\t] {ai_walk(3);};\nvoid() ogre_walk13\t=[\t$walk13,\togre_walk14\t] {ai_walk(3);};\nvoid() ogre_walk14\t=[\t$walk14,\togre_walk15\t] {ai_walk(3);};\nvoid() ogre_walk15\t=[\t$walk15,\togre_walk16\t] {ai_walk(3);};\nvoid() ogre_walk16\t=[\t$walk16,\togre_walk1\t] {ai_walk(4);};\n\nvoid() ogre_run1\t=[\t$run1,\t\togre_run2\t] {ai_run(9);\nif (random() < 0.2)\n\tsound (self, CHAN_VOICE, \"ogre/ogidle2.wav\", 1, ATTN_IDLE);\n};\nvoid() ogre_run2\t=[\t$run2,\t\togre_run3\t] {ai_run(12);};\nvoid() ogre_run3\t=[\t$run3,\t\togre_run4\t] {ai_run(8);};\nvoid() ogre_run4\t=[\t$run4,\t\togre_run5\t] {ai_run(22);};\nvoid() ogre_run5\t=[\t$run5,\t\togre_run6\t] {ai_run(16);};\nvoid() ogre_run6\t=[\t$run6,\t\togre_run7\t] {ai_run(4);};\nvoid() ogre_run7\t=[\t$run7,\t\togre_run8\t] {ai_run(13);};\nvoid() ogre_run8\t=[\t$run8,\t\togre_run1\t] {ai_run(24);};\n\nvoid() ogre_swing1\t=[\t$swing1,\t\togre_swing2\t] {ai_charge(11);\nsound (self, CHAN_WEAPON, \"ogre/ogsawatk.wav\", 1, ATTN_NORM);\n};\nvoid() ogre_swing2\t=[\t$swing2,\t\togre_swing3\t] {ai_charge(1);};\nvoid() ogre_swing3\t=[\t$swing3,\t\togre_swing4\t] {ai_charge(4);};\nvoid() ogre_swing4\t=[\t$swing4,\t\togre_swing5\t] {ai_charge(13);};\nvoid() ogre_swing5\t=[\t$swing5,\t\togre_swing6\t] {ai_charge(9); chainsaw(0);self.angles_y = self.angles_y + random()*25;};\nvoid() ogre_swing6\t=[\t$swing6,\t\togre_swing7\t] {chainsaw(200);self.angles_y = self.angles_y + random()* 25;};\nvoid() ogre_swing7\t=[\t$swing7,\t\togre_swing8\t] {chainsaw(0);self.angles_y = self.angles_y + random()* 25;};\nvoid() ogre_swing8\t=[\t$swing8,\t\togre_swing9\t] {chainsaw(0);self.angles_y = self.angles_y + random()* 25;};\nvoid() ogre_swing9\t=[\t$swing9,\t\togre_swing10 ] {chainsaw(0);self.angles_y = self.angles_y + random()* 25;};\nvoid() ogre_swing10\t=[\t$swing10,\t\togre_swing11 ] {chainsaw(-200);self.angles_y = self.angles_y + random()* 25;};\nvoid() ogre_swing11\t=[\t$swing11,\t\togre_swing12 ] {chainsaw(0);self.angles_y = self.angles_y + random()* 25;};\nvoid() ogre_swing12\t=[\t$swing12,\t\togre_swing13 ] {ai_charge(3);};\nvoid() ogre_swing13\t=[\t$swing13,\t\togre_swing14 ] {ai_charge(8);};\nvoid() ogre_swing14\t=[\t$swing14,\t\togre_run1\t] {ai_charge(9);};\n\nvoid() ogre_smash1\t=[\t$smash1,\t\togre_smash2\t] {ai_charge(6);\nsound (self, CHAN_WEAPON, \"ogre/ogsawatk.wav\", 1, ATTN_NORM);\n};\nvoid() ogre_smash2\t=[\t$smash2,\t\togre_smash3\t] {ai_charge(0);};\nvoid() ogre_smash3\t=[\t$smash3,\t\togre_smash4\t] {ai_charge(0);};\nvoid() ogre_smash4\t=[\t$smash4,\t\togre_smash5\t] {ai_charge(1);};\nvoid() ogre_smash5\t=[\t$smash5,\t\togre_smash6\t] {ai_charge(4);};\nvoid() ogre_smash6\t=[\t$smash6,\t\togre_smash7\t] {ai_charge(4); chainsaw(0);};\nvoid() ogre_smash7\t=[\t$smash7,\t\togre_smash8\t] {ai_charge(4); chainsaw(0);};\nvoid() ogre_smash8\t=[\t$smash8,\t\togre_smash9\t] {ai_charge(10); chainsaw(0);};\nvoid() ogre_smash9\t=[\t$smash9,\t\togre_smash10 ] {ai_charge(13); chainsaw(0);};\nvoid() ogre_smash10\t=[\t$smash10,\t\togre_smash11 ] {chainsaw(1);};\nvoid() ogre_smash11\t=[\t$smash11,\t\togre_smash12 ] {ai_charge(2); chainsaw(0);\nself.nextthink = time + 0.1 + random()*0.1;};\t// slight variation\n//void() ogre_smash12\t=[\t$smash12,\t\togre_smash13 ] {ai_charge();};\nvoid() ogre_smash12 =[$smash12, ogre_smash13] {ai_charge(0);};\nvoid() ogre_smash13\t=[\t$smash13,\t\togre_smash14 ] {ai_charge(4);};\nvoid() ogre_smash14\t=[\t$smash14,\t\togre_run1\t] {ai_charge(12);};\n\nvoid() ogre_nail1\t=[\t$shoot1,\t\togre_nail2\t] {ai_face();};\nvoid() ogre_nail2\t=[\t$shoot2,\t\togre_nail3\t] {ai_face();};\nvoid() ogre_nail3\t=[\t$shoot2,\t\togre_nail4\t] {ai_face();};\nvoid() ogre_nail4\t=[\t$shoot3,\t\togre_nail5\t] {ai_face();OgreFireGrenade();};\nvoid() ogre_nail5\t=[\t$shoot4,\t\togre_nail6\t] {ai_face();};\nvoid() ogre_nail6\t=[\t$shoot5,\t\togre_nail7\t] {ai_face();};\nvoid() ogre_nail7\t=[\t$shoot6,\t\togre_run1\t] {ai_face();};\n\nvoid()\togre_pain1\t=[\t$pain1,\t\togre_pain2\t] {};\nvoid()\togre_pain2\t=[\t$pain2,\t\togre_pain3\t] {};\nvoid()\togre_pain3\t=[\t$pain3,\t\togre_pain4\t] {};\nvoid()\togre_pain4\t=[\t$pain4,\t\togre_pain5\t] {};\nvoid()\togre_pain5\t=[\t$pain5,\t\togre_run1\t] {};\n\n\nvoid()\togre_painb1\t=[\t$painb1,\togre_painb2\t] {};\nvoid()\togre_painb2\t=[\t$painb2,\togre_painb3\t] {};\nvoid()\togre_painb3\t=[\t$painb3,\togre_run1\t] {};\n\n\nvoid()\togre_painc1\t=[\t$painc1,\togre_painc2\t] {};\nvoid()\togre_painc2\t=[\t$painc2,\togre_painc3\t] {};\nvoid()\togre_painc3\t=[\t$painc3,\togre_painc4\t] {};\nvoid()\togre_painc4\t=[\t$painc4,\togre_painc5\t] {};\nvoid()\togre_painc5\t=[\t$painc5,\togre_painc6\t] {};\nvoid()\togre_painc6\t=[\t$painc6,\togre_run1\t] {};\n\n\nvoid()\togre_paind1\t=[\t$paind1,\togre_paind2\t] {};\nvoid()\togre_paind2\t=[\t$paind2,\togre_paind3\t] {ai_pain(10);};\nvoid()\togre_paind3\t=[\t$paind3,\togre_paind4\t] {ai_pain(9);};\nvoid()\togre_paind4\t=[\t$paind4,\togre_paind5\t] {ai_pain(4);};\nvoid()\togre_paind5\t=[\t$paind5,\togre_paind6\t] {};\nvoid()\togre_paind6\t=[\t$paind6,\togre_paind7\t] {};\nvoid()\togre_paind7\t=[\t$paind7,\togre_paind8\t] {};\nvoid()\togre_paind8\t=[\t$paind8,\togre_paind9\t] {};\nvoid()\togre_paind9\t=[\t$paind9,\togre_paind10\t] {};\nvoid()\togre_paind10=[\t$paind10,\togre_paind11\t] {};\nvoid()\togre_paind11=[\t$paind11,\togre_paind12\t] {};\nvoid()\togre_paind12=[\t$paind12,\togre_paind13\t] {};\nvoid()\togre_paind13=[\t$paind13,\togre_paind14\t] {};\nvoid()\togre_paind14=[\t$paind14,\togre_paind15\t] {};\nvoid()\togre_paind15=[\t$paind15,\togre_paind16\t] {};\nvoid()\togre_paind16=[\t$paind16,\togre_run1\t] {};\n\nvoid()\togre_paine1\t=[\t$paine1,\togre_paine2\t] {};\nvoid()\togre_paine2\t=[\t$paine2,\togre_paine3\t] {ai_pain(10);};\nvoid()\togre_paine3\t=[\t$paine3,\togre_paine4\t] {ai_pain(9);};\nvoid()\togre_paine4\t=[\t$paine4,\togre_paine5\t] {ai_pain(4);};\nvoid()\togre_paine5\t=[\t$paine5,\togre_paine6\t] {};\nvoid()\togre_paine6\t=[\t$paine6,\togre_paine7\t] {};\nvoid()\togre_paine7\t=[\t$paine7,\togre_paine8\t] {};\nvoid()\togre_paine8\t=[\t$paine8,\togre_paine9\t] {};\nvoid()\togre_paine9\t=[\t$paine9,\togre_paine10\t] {};\nvoid()\togre_paine10=[\t$paine10,\togre_paine11\t] {};\nvoid()\togre_paine11=[\t$paine11,\togre_paine12\t] {};\nvoid()\togre_paine12=[\t$paine12,\togre_paine13\t] {};\nvoid()\togre_paine13=[\t$paine13,\togre_paine14\t] {};\nvoid()\togre_paine14=[\t$paine14,\togre_paine15\t] {};\nvoid()\togre_paine15=[\t$paine15,\togre_run1\t] {};\n\n\nvoid(entity attacker, float damage)\togre_pain =\n{\n\tlocal float\tr;\n\n// don't make multiple pain sounds right after each other\n\tif (self.pain_finished > time)\n\t\treturn;\n\n\tsound (self, CHAN_VOICE, \"ogre/ogpain1.wav\", 1, ATTN_NORM);\t\t\n\n\tr = random();\n\t\n\tif (r < 0.25)\n\t{\n\t\togre_pain1 ();\n\t\tself.pain_finished = time + 1;\n\t}\n\telse if (r < 0.5)\n\t{\n\t\togre_painb1 ();\n\t\tself.pain_finished = time + 1;\n\t}\n\telse if (r < 0.75)\n\t{\n\t\togre_painc1 ();\n\t\tself.pain_finished = time + 1;\n\t}\n\telse if (r < 0.88)\n\t{\n\t\togre_paind1 ();\n\t\tself.pain_finished = time + 2;\n\t}\n\telse\n\t{\n\t\togre_paine1 ();\n\t\tself.pain_finished = time + 2;\n\t}\n};\n\nvoid()\togre_die1\t=[\t$death1,\togre_die2\t] {};\nvoid()\togre_die2\t=[\t$death2,\togre_die3\t] {};\nvoid()\togre_die3\t=[\t$death3,\togre_die4\t]\n{self.solid = SOLID_NOT;DropBackpack();};\nvoid()\togre_die4\t=[\t$death4,\togre_die5\t] {};\nvoid()\togre_die5\t=[\t$death5,\togre_die6\t] {};\nvoid()\togre_die6\t=[\t$death6,\togre_die7\t] {};\nvoid()\togre_die7\t=[\t$death7,\togre_die8\t] {};\nvoid()\togre_die8\t=[\t$death8,\togre_die9\t] {};\nvoid()\togre_die9\t=[\t$death9,\togre_die10\t] {};\nvoid()\togre_die10\t=[\t$death10,\togre_die11\t] {};\nvoid()\togre_die11\t=[\t$death11,\togre_die12\t] {};\nvoid()\togre_die12\t=[\t$death12,\togre_die13\t] {};\nvoid()\togre_die13\t=[\t$death13,\togre_die14\t] {};\nvoid()\togre_die14\t=[\t$death14,\togre_die14\t] {};\n\nvoid()\togre_bdie1\t=[\t$bdeath1,\togre_bdie2\t] {};\nvoid()\togre_bdie2\t=[\t$bdeath2,\togre_bdie3\t] {ai_forward(5);};\nvoid()\togre_bdie3\t=[\t$bdeath3,\togre_bdie4\t]\n{self.solid = SOLID_NOT;DropBackpack();};\nvoid()\togre_bdie4\t=[\t$bdeath4,\togre_bdie5\t] {ai_forward(1);};\nvoid()\togre_bdie5\t=[\t$bdeath5,\togre_bdie6\t] {ai_forward(3);};\nvoid()\togre_bdie6\t=[\t$bdeath6,\togre_bdie7\t] {ai_forward(7);};\nvoid()\togre_bdie7\t=[\t$bdeath7,\togre_bdie8\t] {ai_forward(25);};\nvoid()\togre_bdie8\t=[\t$bdeath8,\togre_bdie9\t] {};\nvoid()\togre_bdie9\t=[\t$bdeath9,\togre_bdie10\t] {};\nvoid()\togre_bdie10\t=[\t$bdeath10,\togre_bdie10\t] {};\n\nvoid() ogre_die =\n{\n// check for gib\n\tif (self.health < -80)\n\t{\n\t\tsound (self, CHAN_VOICE, \"player/udeath.wav\", 1, ATTN_NORM);\n\t\tThrowHead (\"progs/h_ogre.mdl\", self.health);\n\t\tThrowGib (\"progs/gib3.mdl\", self.health);\n\t\tThrowGib (\"progs/gib3.mdl\", self.health);\n\t\tThrowGib (\"progs/gib3.mdl\", self.health);\n\t\treturn;\n\t}\n\n\tsound (self, CHAN_VOICE, \"ogre/ogdth.wav\", 1, ATTN_NORM);\n\t\n\tif (random() < 0.5)\n\t\togre_die1 ();\n\telse\n\t\togre_bdie1 ();\n};\n\nvoid() ogre_melee =\n{\n\tif (random() > 0.5)\n\t\togre_smash1 ();\n\telse\n\t\togre_swing1 ();\n};\n\n\n/*QUAKED monster_ogre (1 0 0) (-32 -32 -24) (32 32 64) Ambush\n\n*/\nvoid() monster_ogre =\n{\n\tif (deathmatch)\n\t{\n\t\tremove(self);\n\t\treturn;\n\t}\n\tprecache_model (\"progs/ogre.mdl\");\n\tprecache_model (\"progs/h_ogre.mdl\");\n\tprecache_model (\"progs/grenade.mdl\");\n\n\tprecache_sound (\"ogre/ogdrag.wav\");\n\tprecache_sound (\"ogre/ogdth.wav\");\n\tprecache_sound (\"ogre/ogidle.wav\");\n\tprecache_sound (\"ogre/ogidle2.wav\");\n\tprecache_sound (\"ogre/ogpain1.wav\");\n\tprecache_sound (\"ogre/ogsawatk.wav\");\n\tprecache_sound (\"ogre/ogwake.wav\");\n\n\tself.solid = SOLID_SLIDEBOX;\n\tself.movetype = MOVETYPE_STEP;\n\n\tsetmodel (self, \"progs/ogre.mdl\");\n\n\tsetsize (self, VEC_HULL2_MIN, VEC_HULL2_MAX);\n\tself.health = 200;\n\n\tself.th_stand = ogre_stand1;\n\tself.th_walk = ogre_walk1;\n\tself.th_run = ogre_run1;\n\tself.th_die = ogre_die;\n\tself.th_melee = ogre_melee;\n\tself.th_missile = ogre_nail1;\n\tself.th_pain = ogre_pain;\n\n\tself.ammo_rockets_real = 2; // drop 2 rockets on death\n\t\n\twalkmonster_start();\n};\n\nvoid() monster_ogre_marksman =\n{\n\tmonster_ogre ();\n};\n\n\n\u0000"
  },
  {
    "path": "quakec/basemod/oldone.qc",
    "content": "/*\n==============================================================================\n\nOLD ONE\n\n==============================================================================\n*/\n$cd id1/models/old_one\n$origin 0 0 24\n$base base\n$skin skin\n$scale 1\n\nvoid() finale_1;\nvoid() finale_2;\nvoid() finale_3;\nvoid() finale_4;\n\n$frame old1 old2 old3 old4 old5 old6 old7 old8 old9 \n$frame old10 old11 old12 old13 old14 old15 old16 old17 old18 old19 \n$frame old20 old21 old22 old23 old24 old25 old26 old27 old28 old29 \n$frame old30 old31 old32 old33 old34 old35 old36 old37 old38 old39 \n$frame old40 old41 old42 old43 old44 old45 old46 \n\n$frame shake1 shake2 shake3 shake4 shake5 shake6 shake7 shake8\n$frame shake9 shake10 shake11 shakex shake12 shake13 shake14\n$frame shake15 shake16 shake17 shake18 shake19 shake20\n\n//void() old_stand     =[      $old1,       old_stand    ] {};\n\nvoid() old_idle1        =[      $old1,  old_idle2       ] {};\nvoid() old_idle2        =[      $old2,  old_idle3       ] {};\nvoid() old_idle3        =[      $old3,  old_idle4       ] {};\nvoid() old_idle4        =[      $old4,  old_idle5       ] {};\nvoid() old_idle5        =[      $old5,  old_idle6       ] {};\nvoid() old_idle6        =[      $old6,  old_idle7       ] {};\nvoid() old_idle7        =[      $old7,  old_idle8       ] {};\nvoid() old_idle8        =[      $old8,  old_idle9       ] {};\nvoid() old_idle9        =[      $old9,  old_idle10      ] {};\nvoid() old_idle10       =[      $old10, old_idle11      ] {};\nvoid() old_idle11       =[      $old11, old_idle12      ] {};\nvoid() old_idle12       =[      $old12, old_idle13      ] {};\nvoid() old_idle13       =[      $old13, old_idle14      ] {};\nvoid() old_idle14       =[      $old14, old_idle15      ] {};\nvoid() old_idle15       =[      $old15, old_idle16      ] {};\nvoid() old_idle16       =[      $old16, old_idle17      ] {};\nvoid() old_idle17       =[      $old17, old_idle18      ] {};\nvoid() old_idle18       =[      $old18, old_idle19      ] {};\nvoid() old_idle19       =[      $old19, old_idle20      ] {};\nvoid() old_idle20       =[      $old20, old_idle21      ] {};\nvoid() old_idle21       =[      $old21, old_idle22      ] {};\nvoid() old_idle22       =[      $old22, old_idle23      ] {};\nvoid() old_idle23       =[      $old23, old_idle24      ] {};\nvoid() old_idle24       =[      $old24, old_idle25      ] {};\nvoid() old_idle25       =[      $old25, old_idle26      ] {};\nvoid() old_idle26       =[      $old26, old_idle27      ] {};\nvoid() old_idle27       =[      $old27, old_idle28      ] {};\nvoid() old_idle28       =[      $old28, old_idle29      ] {};\nvoid() old_idle29       =[      $old29, old_idle30      ] {};\nvoid() old_idle30       =[      $old30, old_idle31      ] {};\nvoid() old_idle31       =[      $old31, old_idle32      ] {};\nvoid() old_idle32       =[      $old32, old_idle33      ] {};\nvoid() old_idle33       =[      $old33, old_idle34      ] {};\nvoid() old_idle34       =[      $old34, old_idle35      ] {};\nvoid() old_idle35       =[      $old35, old_idle36      ] {};\nvoid() old_idle36       =[      $old36, old_idle37      ] {};\nvoid() old_idle37       =[      $old37, old_idle38      ] {};\nvoid() old_idle38       =[      $old38, old_idle39      ] {};\nvoid() old_idle39       =[      $old39, old_idle40      ] {};\nvoid() old_idle40       =[      $old40, old_idle41      ] {};\nvoid() old_idle41       =[      $old41, old_idle42      ] {};\nvoid() old_idle42       =[      $old42, old_idle43      ] {};\nvoid() old_idle43       =[      $old43, old_idle44      ] {};\nvoid() old_idle44       =[      $old44, old_idle45      ] {};\nvoid() old_idle45       =[      $old45, old_idle46      ] {};\nvoid() old_idle46       =[      $old46, old_idle1       ] {};\n\n\nvoid() old_thrash1        =[ $shake1,  old_thrash2       ] {lightstyle(0, \"m\");};\nvoid() old_thrash2        =[      $shake2,  old_thrash3       ] {lightstyle(0, \"k\");};\nvoid() old_thrash3        =[      $shake3,  old_thrash4       ] {lightstyle(0, \"k\");};\nvoid() old_thrash4        =[      $shake4,  old_thrash5       ] {lightstyle(0, \"i\");};\nvoid() old_thrash5        =[      $shake5,  old_thrash6       ] {lightstyle(0, \"g\");};\nvoid() old_thrash6        =[      $shake6,  old_thrash7       ] {lightstyle(0, \"e\");};\nvoid() old_thrash7        =[      $shake7,  old_thrash8       ] {lightstyle(0, \"c\");};\nvoid() old_thrash8        =[      $shake8,  old_thrash9       ] {lightstyle(0, \"a\");};\nvoid() old_thrash9        =[      $shake9,  old_thrash10      ] {lightstyle(0, \"c\");};\nvoid() old_thrash10       =[      $shake10, old_thrash11      ] {lightstyle(0, \"e\");};\nvoid() old_thrash11       =[      $shake11, old_thrash12      ] {lightstyle(0, \"g\");};\nvoid() old_thrash12       =[      $shake12, old_thrash13      ] {lightstyle(0, \"i\");};\nvoid() old_thrash13       =[      $shake13, old_thrash14      ] {lightstyle(0, \"k\");};\nvoid() old_thrash14       =[      $shake14, old_thrash15      ] {lightstyle(0, \"m\");};\nvoid() old_thrash15       =[      $shake15, old_thrash16      ] {lightstyle(0, \"m\");\nself.cnt = self.cnt + 1;\nif (self.cnt < 3)\n\tself.think = old_thrash1;\n};\nvoid() old_thrash16       =[      $shake16, old_thrash17      ] {lightstyle(0, \"g\");};\nvoid() old_thrash17       =[      $shake17, old_thrash18      ] {lightstyle(0, \"c\");};\nvoid() old_thrash18       =[      $shake18, old_thrash19      ] {lightstyle(0, \"b\");};\nvoid() old_thrash19       =[      $shake19, old_thrash20      ] {lightstyle(0, \"a\");};\nvoid() old_thrash20       =[      $shake20, old_thrash20      ] {finale_4();};\n\n//============================================================================\n\nvoid() finale_1 =\n{\n\tlocal entity\tpos, pl;\n\tlocal entity\ttimer;\n\n\t// TODO: Coop friendly finale goes here\n\t\n\tkilled_monsters = killed_monsters + 1;\n\tWriteByte (MSG_ALL, SVC_KILLEDMONSTER);\t// FIXME: reliable broadcast\n\n\tintermission_exittime = time + 10000000;\t// never allow exit\n\tintermission_running = 1;\n\n\t// find the intermission spot\n\tpos = find (world, classname, \"info_intermission\");\n\tif (!pos)\n\t\terror (\"no info_intermission\");\n\tpl = find (world, classname, \"misc_teleporttrain\");\n\tif (!pl)\n\t\terror (\"no teleporttrain\");\n\tremove (pl);\n\n\tENG_Finale(\"\");\n\n\tpl = find (world, classname, \"player\");\n\twhile (pl != world)\n\t{\n\t\tpl.view_ofs = '0 0 0';\n\t\tpl.angles = other.v_angle = pos.mangle;\n\t\tpl.fixangle = TRUE;\t\t// turn this way immediately\n\t\tpl.map = self.map;\n\t\tpl.nextthink = time + 0.5;\n\t\tpl.takedamage = DAMAGE_NO;\n\t\tpl.solid = SOLID_NOT;\n\t\tpl.movetype = MOVETYPE_NONE;\n\t\tpl.modelindex = 0;\n\t\tsetorigin (pl, pos.origin);\n\t\tpl = find (pl, classname, \"player\");\n\t}\t\n\t\n\t// make fake versions of all players as standins, and move the real\n\t// players to the intermission spot\n\t\n\t// wait for 1 second\n\ttimer = spawn();\n\ttimer.nextthink = time + 1;\n\ttimer.think = finale_2;\t\n};\n\nvoid() finale_2 =\n{\n\tlocal vector\to;\n\n\t// start a teleport splash inside shub\n\n\to = shub.origin - '0 100 0';\n\tTE_teleport(o);\n\n\tsound (shub, CHAN_VOICE, \"misc/r_tele1.wav\", 1, ATTN_NORM);\n\t\n\tself.nextthink = time + 2;\n\tself.think = finale_3;\n};\n\nvoid() finale_3 =\n{\n\t// start shub thrashing wildly\n\tlightstyle(0, \"a\");\t\n\tshub.think = old_thrash1;\n\tsound (shub, CHAN_VOICE, \"boss2/death.wav\", 1, ATTN_NORM);\n\tremove(self);\n};\n\nvoid() finale_4 =\n{\n\t// throw tons of meat chunks\t\n\tlocal\tvector\toldo;\n\tlocal\tfloat\tx, y, z;\n\tlocal\tfloat\tr;\n\tlocal entity\tn;\n\n\tsound (self, CHAN_VOICE, \"boss2/pop2.wav\", 1, ATTN_NORM);\n\t\n\toldo = self.origin;\n\n\tz = 16;\n\twhile (z <= 144)\n\t{\n\t\tx = -64;\n\t\twhile (x <= 64)\n\t\t{\n\t\t\ty = -64;\n\t\t\twhile (y <= 64)\n\t\t\t{\n\t\t\t\tself.origin_x = oldo_x + x;\n\t\t\t\tself.origin_y = oldo_y + y;\n\t\t\t\tself.origin_z = oldo_z + z;\n\n\t\t\t\tr = random();\n\t\t\t\tif (r < 0.3)\t\t\t\t\n\t\t\t\t\tThrowGib (\"progs/gib1.mdl\", -999);\n\t\t\t\telse if (r < 0.6)\n\t\t\t\t\tThrowGib (\"progs/gib2.mdl\", -999);\n\t\t\t\telse\n\t\t\t\t\tThrowGib (\"progs/gib3.mdl\", -999);\n\t\t\t\ty = y + 32;\n\t\t\t}\n\t\t\tx = x + 32;\n\t\t}\n\t\tz = z + 96;\n\t}\n\t// start the end text\n\tENG_Finale(\"Congratulations and well done! You have\\nbeaten the hideous Shub-Niggurath, and\\nher hundreds of ugly changelings and\\nmonsters. You have proven that your\\nskill and your cunning are greater than\\nall the powers of Quake. You are the\\nmaster now. Id Software salutes you.\");\n\n// put a player model down\n\tn = spawn();\n\tsetmodel (n, \"progs/player.mdl\");\n\toldo = oldo - '32 264 0';\n\tsetorigin (n, oldo);\n\tn.angles = '0 290 0';\n\tn.frame = 1;\n\n\tremove (self);\n\n// switch cd track\n\tENG_SwitchTrack(3, 3);\n\tlightstyle(0, \"m\");\t\n};\n\n//============================================================================\n\nvoid () nopain =\n{\n\tself.health = 40000;\n};\n\n//============================================================================\n\n\n/*QUAKED monster_oldone (1 0 0) (-16 -16 -24) (16 16 32)\n*/\nvoid() monster_oldone =\n{\n\tif (deathmatch)\n\t{\n\t\tremove(self);\n\t\treturn;\n\t}\n\n\tprecache_model2 (\"progs/oldone.mdl\");\n\n\tprecache_sound2 (\"boss2/death.wav\");\n\tprecache_sound2 (\"boss2/idle.wav\");\n\tprecache_sound2 (\"boss2/sight.wav\");\n\tprecache_sound2 (\"boss2/pop2.wav\");\n\n\tself.solid = SOLID_SLIDEBOX;\n\tself.movetype = MOVETYPE_STEP;\n\t\n\tsetmodel (self, \"progs/oldone.mdl\");\n\tsetsize (self, '-160 -128 -24', '160 128 256');\n\n\tself.health = 40000;\t\t// kill by telefrag\n\tself.think = old_idle1;\n\tself.nextthink = time + 0.1;\t\n\tself.takedamage = DAMAGE_YES;\n\tself.th_pain = nopain;\n\tself.th_die = finale_1;\n\tshub = self;\n\t\n\ttotal_monsters = total_monsters + 1;\n};\n\n"
  },
  {
    "path": "quakec/basemod/plats.qc",
    "content": "\n\nvoid() plat_center_touch;\nvoid() plat_outside_touch;\nvoid() plat_trigger_use;\nvoid() plat_go_up;\nvoid() plat_go_down;\nvoid() plat_crush;\nfloat PLAT_LOW_TRIGGER = 1;\n\nvoid() plat_spawn_inside_trigger =\n{\n\tlocal entity\ttrigger;\n\tlocal vector\ttmin, tmax;\n\n//\n// middle trigger\n//\t\n\ttrigger = spawn();\n\ttrigger.touch = plat_center_touch;\n\ttrigger.movetype = MOVETYPE_NONE;\n\ttrigger.solid = SOLID_TRIGGER;\n\ttrigger.enemy = self;\n\t\n\ttmin = self.mins + '25 25 0';\n\ttmax = self.maxs - '25 25 -8';\n\ttmin_z = tmax_z - (self.pos1_z - self.pos2_z + 8);\n\tif (self.spawnflags & PLAT_LOW_TRIGGER)\n\t\ttmax_z = tmin_z + 8;\n\t\n\tif (self.size_x <= 50)\n\t{\n\t\ttmin_x = (self.mins_x + self.maxs_x) / 2;\n\t\ttmax_x = tmin_x + 1;\n\t}\n\tif (self.size_y <= 50)\n\t{\n\t\ttmin_y = (self.mins_y + self.maxs_y) / 2;\n\t\ttmax_y = tmin_y + 1;\n\t}\n\t\n\tsetsize (trigger, tmin, tmax);\n};\n\nvoid() plat_hit_top =\n{\n\tsound (self, chan_no_phs_add|CHAN_VOICE, self.noise1, 1, ATTN_NORM);\n\tself.state = STATE_TOP;\n\tself.think = plat_go_down;\n\tself.nextthink = self.ltime + 3;\n};\n\nvoid() plat_hit_bottom =\n{\n\tsound (self, chan_no_phs_add|CHAN_VOICE, self.noise1, 1, ATTN_NORM);\n\tself.state = STATE_BOTTOM;\n};\n\nvoid() plat_go_down =\n{\n\tsound (self, CHAN_VOICE, self.noise, 1, ATTN_NORM);\n\tself.state = STATE_DOWN;\n\tSUB_CalcMove (self.pos2, self.speed, plat_hit_bottom);\n};\n\nvoid() plat_go_up =\n{\n\tsound (self, CHAN_VOICE, self.noise, 1, ATTN_NORM);\n\tself.state = STATE_UP;\n\tSUB_CalcMove (self.pos1, self.speed, plat_hit_top);\n};\n\nvoid() plat_center_touch =\n{\n\tif (other.classname != \"player\")\n\t\treturn;\n\t\t\n\tif (other.health <= 0)\n\t\treturn;\n\n\tself = self.enemy;\n\tif (self.state == STATE_BOTTOM)\n\t\tplat_go_up ();\n\telse if (self.state == STATE_TOP)\n\t\tself.nextthink = self.ltime + 1;\t// delay going down\n};\n\nvoid() plat_outside_touch =\n{\n\tif (other.classname != \"player\")\n\t\treturn;\n\n\tif (other.health <= 0)\n\t\treturn;\n\t\t\n//dprint (\"plat_outside_touch\\n\");\n\tself = self.enemy;\n\tif (self.state == STATE_TOP)\n\t\tplat_go_down ();\n};\n\nvoid() plat_trigger_use =\n{\n\tif (self.think)\n\t\treturn;\t\t// allready activated\n\tplat_go_down();\n};\n\n\nvoid() plat_crush =\n{\n//dprint (\"plat_crush\\n\");\n\n\tT_Damage (other, self, self, 1, MOD_SQUISH);\n\t\n\tif (self.state == STATE_UP)\n\t\tplat_go_down ();\n\telse if (self.state == STATE_DOWN)\n\t\tplat_go_up ();\n\telse\n\t\tobjerror (\"plat_crush: bad self.state\\n\");\n};\n\nvoid() plat_use =\n{\n\tself.use = SUB_Null;\n\tif (self.state != STATE_UP)\n\t\tobjerror (\"plat_use: not in up state\");\n\tplat_go_down();\n};\n\n\n/*QUAKED func_plat (0 .5 .8) ? PLAT_LOW_TRIGGER\nspeed\tdefault 150\n\nPlats are always drawn in the extended position, so they will light correctly.\n\nIf the plat is the target of another trigger or button, it will start out disabled in the extended position until it is trigger, when it will lower and become a normal plat.\n\nIf the \"height\" key is set, that will determine the amount the plat moves, instead of being implicitly determined by the model's height.\nSet \"sounds\" to one of the following:\n1) base fast\n2) chain slow\n*/\n\n\nvoid() func_plat =\n\n{\n\n\tif (!self.t_length)\n\t\tself.t_length = 80;\n\tif (!self.t_width)\n\t\tself.t_width = 10;\n\n\tif (self.sounds == 0)\n\t\tself.sounds = 2;\n// FIX THIS TO LOAD A GENERIC PLAT SOUND\n\n\tif (self.sounds == 1)\n\t{\n\t\tprecache_sound (\"plats/plat1.wav\");\n\t\tprecache_sound (\"plats/plat2.wav\");\n\t\tself.noise = \"plats/plat1.wav\";\n\t\tself.noise1 = \"plats/plat2.wav\";\n\t}\n\telse if (self.sounds == 2)\n\t{\n\t\tprecache_sound (\"plats/medplat1.wav\");\n\t\tprecache_sound (\"plats/medplat2.wav\");\n\t\tself.noise = \"plats/medplat1.wav\";\n\t\tself.noise1 = \"plats/medplat2.wav\";\n\t}\n\n\n\tself.mangle = self.angles;\n\tself.angles = '0 0 0';\n\n\tself.classname = \"plat\";\n\tself.solid = SOLID_BSP;\n\tself.movetype = MOVETYPE_PUSH;\n\tsetorigin (self, self.origin);\t\n\tsetmodel (self, self.model);\n\tsetsize (self, self.mins , self.maxs);\n\n\tself.blocked = plat_crush;\n\tif (!self.speed)\n\t\tself.speed = 150;\n\n// pos1 is the top position, pos2 is the bottom\n\tself.pos1 = self.origin;\n\tself.pos2 = self.origin;\n\tif (self.height)\n\t\tself.pos2_z = self.origin_z - self.height;\n\telse\n\t\tself.pos2_z = self.origin_z - self.size_z + 8;\n\n\tself.use = plat_trigger_use;\n\n\tplat_spawn_inside_trigger ();\t// the \"start moving\" trigger\t\n\n\tif (self.targetname)\n\t{\n\t\tself.state = STATE_UP;\n\t\tself.use = plat_use;\n\t}\n\telse\n\t{\n\t\tsetorigin (self, self.pos2);\n\t\tself.state = STATE_BOTTOM;\n\t}\n};\n\n//============================================================================\n\nvoid() train_next;\nvoid() func_train_find;\n\nvoid() train_blocked =\n{\n\tif (time < self.attack_finished)\n\t\treturn;\n\tself.attack_finished = time + 0.5;\n\tT_Damage (other, self, self, self.dmg, MOD_SQUISH);\n};\n\nvoid() train_use =\n{\n\tif (self.think != func_train_find)\n\t\treturn;\t\t// already activated\n\ttrain_next();\n};\n\nvoid() train_wait =\n{\n\tif (self.wait)\n\t{\n\t\tself.nextthink = self.ltime + self.wait;\n\t\tsound (self, chan_no_phs_add|CHAN_VOICE, self.noise, 1, ATTN_NORM);\n\t}\n\telse\n\t\tself.nextthink = self.ltime + 0.1;\n\t\n\tself.think = train_next;\n};\n\nvoid() train_next =\n{\n\tlocal entity\ttarg;\n\n\ttarg = find (world, targetname, self.target);\n\tself.target = targ.target;\n\tif (!self.target)\n\t\tobjerror (\"train_next: no next target\");\n\tif (targ.wait)\n\t\tself.wait = targ.wait;\n\telse\n\t\tself.wait = 0;\n\tsound (self, CHAN_VOICE, self.noise1, 1, ATTN_NORM);\n\tSUB_CalcMove (targ.origin - self.mins, self.speed, train_wait);\n};\n\nvoid() func_train_find =\n\n{\n\tlocal entity\ttarg;\n\n\ttarg = find (world, targetname, self.target);\n\tself.target = targ.target;\n\tsetorigin (self, targ.origin - self.mins);\n\tif (!self.targetname)\n\t{\t// not triggered, so start immediately\n\t\tself.nextthink = self.ltime + 0.1;\n\t\tself.think = train_next;\n\t}\n};\n\n/*QUAKED func_train (0 .5 .8) ?\nTrains are moving platforms that players can ride.\nThe targets origin specifies the min point of the train at each corner.\nThe train spawns at the first target it is pointing at.\nIf the train is the target of a button or trigger, it will not begin moving until activated.\nspeed\tdefault 100\ndmg\t\tdefault\t2\nsounds\n1) ratchet metal\n\n*/\nvoid() func_train =\n{\t\n\tif (!self.speed)\n\t\tself.speed = 100;\n\tif (!self.target)\n\t\tobjerror (\"func_train without a target\");\n\tif (!self.dmg)\n\t\tself.dmg = 2;\n\n\tif (self.sounds == 0)\n\t{\n\t\tself.noise = (\"misc/null.wav\");\n\t\tprecache_sound (\"misc/null.wav\");\n\t\tself.noise1 = (\"misc/null.wav\");\n\t\tprecache_sound (\"misc/null.wav\");\n\t}\n\n\tif (self.sounds == 1)\n\t{\n\t\tself.noise = (\"plats/train2.wav\");\n\t\tprecache_sound (\"plats/train2.wav\");\n\t\tself.noise1 = (\"plats/train1.wav\");\n\t\tprecache_sound (\"plats/train1.wav\");\n\t}\n\n\tself.cnt = 1;\n\tself.solid = SOLID_BSP;\n\tself.movetype = MOVETYPE_PUSH;\n\tself.blocked = train_blocked;\n\tself.use = train_use;\n\tself.classname = \"train\";\n\n\tsetmodel (self, self.model);\n\tsetsize (self, self.mins , self.maxs);\n\tsetorigin (self, self.origin);\n\n// start trains on the second frame, to make sure their targets have had\n// a chance to spawn\n\tself.nextthink = self.ltime + 0.1;\n\tself.think = func_train_find;\n};\n\n/*QUAKED misc_teleporttrain (0 .5 .8) (-8 -8 -8) (8 8 8)\nThis is used for the final bos\n*/\nvoid() misc_teleporttrain =\n{\t\n\tif (!self.speed)\n\t\tself.speed = 100;\n\tif (!self.target)\n\t\tobjerror (\"func_train without a target\");\n\n\tself.cnt = 1;\n\tself.solid = SOLID_NOT;\n\tself.movetype = MOVETYPE_PUSH;\n\tself.blocked = train_blocked;\n\tself.use = train_use;\n\tself.avelocity = '100 200 300';\n\n\tself.noise = (\"misc/null.wav\");\n\tprecache_sound (\"misc/null.wav\");\n\tself.noise1 = (\"misc/null.wav\");\n\tprecache_sound (\"misc/null.wav\");\n\n\tprecache_model2 (\"progs/teleport.mdl\");\n\tsetmodel (self, \"progs/teleport.mdl\");\n\tsetsize (self, self.mins , self.maxs);\n\tsetorigin (self, self.origin);\n\n// start trains on the second frame, to make sure their targets have had\n// a chance to spawn\n\tself.nextthink = self.ltime + 0.1;\n\tself.think = func_train_find;\n};\n\n"
  },
  {
    "path": "quakec/basemod/player.qc",
    "content": "\nvoid() bubble_bob;\n\n/*\n==============================================================================\n\nPLAYER\n\n==============================================================================\n*/\n\n$cd /raid/quake/id1/models/player_4\n$origin 0 -6 24\n$base base              \n$skin skin\n\n//\n// running\n//\n$frame axrun1 axrun2 axrun3 axrun4 axrun5 axrun6\n\n$frame rockrun1 rockrun2 rockrun3 rockrun4 rockrun5 rockrun6\n\n//\n// standing\n//\n$frame stand1 stand2 stand3 stand4 stand5\n\n$frame axstnd1 axstnd2 axstnd3 axstnd4 axstnd5 axstnd6\n$frame axstnd7 axstnd8 axstnd9 axstnd10 axstnd11 axstnd12\n\n\n//\n// pain\n//\n$frame axpain1 axpain2 axpain3 axpain4 axpain5 axpain6\n\n$frame pain1 pain2 pain3 pain4 pain5 pain6\n\n\n//\n// death\n//\n\n$frame axdeth1 axdeth2 axdeth3 axdeth4 axdeth5 axdeth6\n$frame axdeth7 axdeth8 axdeth9\n\n$frame deatha1 deatha2 deatha3 deatha4 deatha5 deatha6 deatha7 deatha8\n$frame deatha9 deatha10 deatha11\n\n$frame deathb1 deathb2 deathb3 deathb4 deathb5 deathb6 deathb7 deathb8\n$frame deathb9\n\n$frame deathc1 deathc2 deathc3 deathc4 deathc5 deathc6 deathc7 deathc8\n$frame deathc9 deathc10 deathc11 deathc12 deathc13 deathc14 deathc15\n\n$frame deathd1 deathd2 deathd3 deathd4 deathd5 deathd6 deathd7\n$frame deathd8 deathd9\n\n$frame deathe1 deathe2 deathe3 deathe4 deathe5 deathe6 deathe7\n$frame deathe8 deathe9\n\n//\n// attacks\n//\n$frame nailatt1 nailatt2\n\n$frame light1 light2\n\n$frame rockatt1 rockatt2 rockatt3 rockatt4 rockatt5 rockatt6\n\n$frame shotatt1 shotatt2 shotatt3 shotatt4 shotatt5 shotatt6\n\n$frame axatt1 axatt2 axatt3 axatt4 axatt5 axatt6\n\n$frame axattb1 axattb2 axattb3 axattb4 axattb5 axattb6\n\n$frame axattc1 axattc2 axattc3 axattc4 axattc5 axattc6\n\n$frame axattd1 axattd2 axattd3 axattd4 axattd5 axattd6\n\n\n/*\n==============================================================================\nPLAYER\n==============================================================================\n*/\n\nvoid() player_run;\n\nvoid()  player_stand1 =[        $axstnd1,       player_stand1   ]\n{\n\tif (self.velocity_x || self.velocity_y)\n\t{\n\t\tself.walkframe=0;\n\t\tplayer_run();\n\t\treturn;\n\t}\n\n\tif (self.weapon == IT_AXE)\n\t{\n\t\tif (self.walkframe >= 12)\n\t\t\tself.walkframe = 0;\n\t\tself.frame = $axstnd1 + self.walkframe;\n\t}\n\telse\n\t{\n\t\tif (self.walkframe >= 5)\n\t\t\tself.walkframe = 0;\n\t\tself.frame = $stand1 + self.walkframe;\n\t}\n\tself.walkframe = self.walkframe + 1;    \n};\n\nvoid()  player_run =[   $rockrun1,      player_run      ]\n{\n\tif (!self.velocity_x && !self.velocity_y)\n\t{\n\t\tself.walkframe=0;\n\t\tplayer_stand1();\n\t\treturn;\n\t}\n\n\tif (self.weapon == IT_AXE)\n\t{\n\t\tif (self.walkframe >= 6)\n\t\t\tself.walkframe = 0;\n\t\tself.frame = $axrun1 + self.walkframe;\n\t}\n\telse\n\t{\n\t\tif (self.walkframe >= 6)\n\t\t\tself.walkframe = 0;\n\t\tself.frame = self.frame + self.walkframe;\n\t}\n\tself.walkframe = self.walkframe + 1;\n};\n\nvoid()  player_shot1 =  [$shotatt1, player_shot2        ] {};\nvoid()  player_shot2 =  [$shotatt2, player_shot3        ] {};\nvoid()  player_shot3 =  [$shotatt3, player_shot4        ] {};\nvoid()  player_shot4 =  [$shotatt4, player_shot5        ] {};\nvoid()  player_shot5 =  [$shotatt5, player_shot6        ] {};\nvoid()  player_shot6 =  [$shotatt6, player_run  ] {};\n\nvoid()  player_axe1 =   [$axatt1, player_axe2   ] {};\nvoid()  player_axe2 =   [$axatt2, player_axe3   ] {};\nvoid()  player_axe3 =   [$axatt3, player_axe4   ] {W_FireAxe();};\nvoid()  player_axe4 =   [$axatt4, player_run    ] {};\n\nvoid()  player_axeb1 =  [$axattb1, player_axeb2 ] {};\nvoid()  player_axeb2 =  [$axattb2, player_axeb3 ] {};\nvoid()  player_axeb3 =  [$axattb3, player_axeb4 ] {W_FireAxe();};\nvoid()  player_axeb4 =  [$axattb4, player_run   ] {};\n\nvoid()  player_axec1 =  [$axattc1, player_axec2 ] {};\nvoid()  player_axec2 =  [$axattc2, player_axec3 ] {};\nvoid()  player_axec3 =  [$axattc3, player_axec4 ] {W_FireAxe();};\nvoid()  player_axec4 =  [$axattc4, player_run   ] {};\n\nvoid()  player_axed1 =  [$axattd1, player_axed2 ] {};\nvoid()  player_axed2 =  [$axattd2, player_axed3 ] {};\nvoid()  player_axed3 =  [$axattd3, player_axed4 ] {W_FireAxe();};\nvoid()  player_axed4 =  [$axattd4, player_run   ] {};\n\n\n//============================================================================\n\nvoid() player_nail1   =[$nailatt1, player_nail2  ] \n{\n\tif (self.weaponstate == WS_IDLE || intermission_running)\n\t\t{player_run ();return;}\n};\nvoid() player_nail2   =[$nailatt2, player_nail1  ]\n{\n\tif (self.weaponstate == WS_IDLE || intermission_running)\n\t\t{player_run ();return;}\n};\n\n//============================================================================\n\nvoid() player_light1   =[$light1, player_light2  ] \n{\n\tif (self.weaponstate == WS_IDLE || intermission_running)\n\t\t{player_run ();return;}\n};\nvoid() player_light2   =[$light2, player_light1  ]\n{\n\tif (self.weaponstate == WS_IDLE || intermission_running)\n\t\t{player_run ();return;}\n};\n\n//============================================================================\n\n\nvoid() player_rocket1   =[$rockatt1, player_rocket2  ] {};\nvoid() player_rocket2   =[$rockatt2, player_rocket3  ] {};\nvoid() player_rocket3   =[$rockatt3, player_rocket4  ] {};\nvoid() player_rocket4   =[$rockatt4, player_rocket5  ] {};\nvoid() player_rocket5   =[$rockatt5, player_rocket6  ] {};\nvoid() player_rocket6   =[$rockatt6, player_run  ] {};\nvoid(float num_bubbles) DeathBubbles;\n\nvoid() PainSound =\n{\n\tlocal float rs;\n\tlocal string s;\n\n\tif (self.health < 0)\n\t\treturn;\n\n\tif (self.pain_finished > time)\n\t\treturn;\n\n\t// change pain sound depending on means of death\n\tswitch (damage_mod)\n\t{\n\tcase MOD_TELEFRAG:\n\tcase MOD_TELEFRAGDEFLECT:\n\t\tsound (self, CHAN_VOICE, \"player/teledth1.wav\", 1, ATTN_NONE);\n\t\treturn;\n\tcase MOD_DROWN:\n\t\tDeathBubbles(1);\n\t\tif (random() > 0.5)\n\t\t\tsound (self, CHAN_VOICE, \"player/drown1.wav\", 1, ATTN_NORM);\n\t\telse\n\t\t\tsound (self, CHAN_VOICE, \"player/drown2.wav\", 1, ATTN_NORM);\n\t\treturn;\n\tcase MOD_SLIME:\n\t\t// FIX ME: put in some steam here\n\t\tif (self.waterlevel == 3)\n\t\t\tDeathBubbles(1);\n\tcase MOD_LAVA:\n\t\tif (random() > 0.5)\n\t\t\tsound (self, CHAN_VOICE, \"player/lburn1.wav\", 1, ATTN_NORM);\n\t\telse\n\t\t\tsound (self, CHAN_VOICE, \"player/lburn2.wav\", 1, ATTN_NORM);\n\t\treturn;\n\tcase MOD_AXE:\n\t\tsound (self, CHAN_VOICE, \"player/axhit1.wav\", 1, ATTN_NORM);\n\t\tself.pain_finished = time + 0.5;\n\t\treturn;\n\tdefault:\n\t \trs = random();\n\n \t\ts = \"player/pain6.wav\";\n \t\tif (rs < 0.166)\n \t\t\ts = \"player/pain1.wav\";\n \t\telse if (rs < 0.333)\n \t\t\ts = \"player/pain2.wav\";\n \t\telse if (rs < 0.5)\n \t\t\ts = \"player/pain3.wav\";\n \t\telse if (rs < 0.667)\n \t\t\ts = \"player/pain4.wav\";\n \t\telse if (rs < 0.833)\n \t\t\ts = \"player/pain5.wav\";\n\n\t \tsound (self, CHAN_VOICE, s, 1, ATTN_NORM);\n\t\treturn;\n\t}\n};\n\nvoid()  player_pain1 =  [       $pain1, player_pain2    ] {PainSound();};\nvoid()  player_pain2 =  [       $pain2, player_pain3    ] {};\nvoid()  player_pain3 =  [       $pain3, player_pain4    ] {};\nvoid()  player_pain4 =  [       $pain4, player_pain5    ] {};\nvoid()  player_pain5 =  [       $pain5, player_pain6    ] {};\nvoid()  player_pain6 =  [       $pain6, player_run      ] {};\n\nvoid()  player_axpain1 =        [       $axpain1,       player_axpain2  ] {PainSound();};\nvoid()  player_axpain2 =        [       $axpain2,       player_axpain3  ] {};\nvoid()  player_axpain3 =        [       $axpain3,       player_axpain4  ] {};\nvoid()  player_axpain4 =        [       $axpain4,       player_axpain5  ] {};\nvoid()  player_axpain5 =        [       $axpain5,       player_axpain6  ] {};\nvoid()  player_axpain6 =        [       $axpain6,       player_run      ] {};\n\nvoid() player_pain =\n{\n\tif (self.weaponstate != WS_IDLE)\n\t\treturn;\n\n\tif (self.invisible_finished > time)\n\t\treturn;         // eyes don't have pain frames\n\n\tif (self.weapon == IT_AXE)\n\t\tplayer_axpain1 ();\n\telse\n\t\tplayer_pain1 ();\n};\n\nvoid() player_diea1;\nvoid() player_dieb1;\nvoid() player_diec1;\nvoid() player_died1;\nvoid() player_diee1;\nvoid() player_die_ax1;\n\nvoid() DeathBubblesSpawn =\n{\nlocal entity    bubble;\n\tif (self.owner.waterlevel != 3)\n\t{\n\t\tremove(self);\n\t\treturn;\n\t}\n\tbubble = spawn();\n\tsetmodel (bubble, \"progs/s_bubble.spr\");\n\tsetorigin (bubble, self.owner.origin + '0 0 24');\n\tbubble.movetype = MOVETYPE_NOCLIP;\n\tbubble.solid = SOLID_NOT;\n\tbubble.velocity = '0 0 15';\n\tbubble.nextthink = time + 0.5;\n\tbubble.think = bubble_bob;\n\tbubble.classname = \"bubble\";\n\tbubble.frame = 0;\n\tbubble.bubble_state = 0;\n\tsetsize (bubble, '-8 -8 -8', '8 8 8');\n\tself.nextthink = time + 0.1;\n\tself.think = DeathBubblesSpawn;\n\tself.bubble_count = self.bubble_count - 1;\n\tif (self.bubble_count <= 0)\n\t\tremove(self);\n};\n\nvoid(float num_bubbles) DeathBubbles =\n{\nlocal entity    bubble_spawner;\n\t\n\tbubble_spawner = spawn();\n\tsetorigin (bubble_spawner, self.origin);\n\tbubble_spawner.movetype = MOVETYPE_NONE;\n\tbubble_spawner.solid = SOLID_NOT;\n\tbubble_spawner.nextthink = time + 0.1;\n\tbubble_spawner.think = DeathBubblesSpawn;\n\tbubble_spawner.owner = self;\n\tbubble_spawner.bubble_count = num_bubbles;\n};\n\n\nvoid() DeathSound =\n{\nlocal float             rs;\n\n\t// water death sounds\n\tif (self.waterlevel == 3)\n\t{\n\t\tDeathBubbles(5);\n\t\tsound (self, CHAN_VOICE, \"player/h2odeath.wav\", 1, ATTN_NONE);\n\t\treturn;\n\t}\n\t\n\trs = rint ((random() * 4) + 1);\n\tif (rs == 1)\n\t\tself.noise = \"player/death1.wav\";\n\tif (rs == 2)\n\t\tself.noise = \"player/death2.wav\";\n\tif (rs == 3)\n\t\tself.noise = \"player/death3.wav\";\n\tif (rs == 4)\n\t\tself.noise = \"player/death4.wav\";\n\tif (rs == 5)\n\t\tself.noise = \"player/death5.wav\";\n\n\tsound (self, CHAN_VOICE, self.noise, 1, ATTN_NONE);\n\treturn;\n};\n\n\nvoid() PlayerDead =\n{\n\tself.nextthink = -1;\n// allow respawn after a certain time\n\tself.deadflag = DEAD_DEAD;\n};\n\nvector(float dm) VelocityForDamage =\n{\n\tlocal vector v;\n\n\tif (vlen(damage_inflictor.velocity)>0)\n\t{\n\t\tv = 0.5 * damage_inflictor.velocity;\n\t\tv = v + (25 * normalize(self.origin-damage_inflictor.origin));\n\t\tv_z = 100 + 240 * random();\n\t\tv_x = v_x + (200 * crandom());\n\t\tv_y = v_y + (200 * crandom());\n\t\t//dprint (\"Velocity gib\\n\");                \n\t}\n\telse\n\t{\n\t\tv_x = 100 * crandom();\n\t\tv_y = 100 * crandom();\n\t\tv_z = 200 + 100 * random();\n\t}\n\n\t//v_x = 100 * crandom();\n\t//v_y = 100 * crandom();\n\t//v_z = 200 + 100 * random();\n\n\tif (dm > -50)\n\t{\n\t//      dprint (\"level 1\\n\");\n\t\tv = v * 0.7;\n\t}\n\telse if (dm > -200)\n\t{\n\t//      dprint (\"level 3\\n\");\n\t\tv = v * 2;\n\t}\n\telse\n\t\tv = v * 10;\n\n\treturn v;\n};\n\nvoid(string gibname, float dm) ThrowGib =\n{\n\tlocal   entity new;\n\n\tnew = spawn();\n\tnew.origin = self.origin;\n\tsetmodel (new, gibname);\n\tsetsize (new, '0 0 0', '0 0 0');\n\tnew.velocity = VelocityForDamage (dm);\n\tnew.movetype = MOVETYPE_BOUNCE;\n\tnew.solid = SOLID_NOT;\n\tnew.avelocity_x = random()*600;\n\tnew.avelocity_y = random()*600;\n\tnew.avelocity_z = random()*600;\n\tnew.think = SUB_Remove;\n\tnew.nextthink = time + 10 + random()*10;\n\tnew.frame = 0;\n\tnew.flags = 0;\n};\n\nvoid(string gibname, float dm) ThrowHead =\n{\n\tsetmodel (self, gibname);\n\tself.frame = 0;\n\tself.nextthink = -1;\n\tself.movetype = MOVETYPE_BOUNCE;\n\tself.takedamage = DAMAGE_NO;\n\tself.solid = SOLID_NOT;\n\tself.view_ofs = '0 0 8';\n\tsetsize (self, '-16 -16 0', '16 16 56');\n\tself.velocity = VelocityForDamage (dm);\n\tself.origin_z = self.origin_z - 24;\n\tself.flags = self.flags - (self.flags & FL_ONGROUND);\n\tself.avelocity = crandom() * '0 600 0';\n};\n\n\nvoid() GibPlayer =\n{\n\tThrowHead (\"progs/h_player.mdl\", self.health);\n\tThrowGib (\"progs/gib1.mdl\", self.health);\n\tThrowGib (\"progs/gib2.mdl\", self.health);\n\tThrowGib (\"progs/gib3.mdl\", self.health);\n\n\tself.deadflag = DEAD_DEAD;\n\n\tif (damage_attacker.classname == \"teledeath\")\n\t{\n\t\tsound (self, CHAN_VOICE, \"player/teledth1.wav\", 1, ATTN_NONE);\n\t\treturn;\n\t}\n\t\t\n\tif (random() < 0.5)\n\t\tsound (self, CHAN_VOICE, \"player/gib.wav\", 1, ATTN_NONE);\n\telse\n\t\tsound (self, CHAN_VOICE, \"player/udeath.wav\", 1, ATTN_NONE);\n};\n\nvoid() PlayerDropStuff =\n{\n\tlocal\tstring\ts, t;\t\n\n\tif (numberserverinfokey(\"dq\") != 0)\n\t{\n\t\tif (self.super_damage_finished > time)\n\t\t{\n\t\t\tDropQuad (self.super_damage_finished - time);\n\t\t\ts = ftos(rint(self.super_damage_finished - time));\n\t\t\tt = \" lost a quad with \";\n\t\t\tif (deathmatch == 4)\n\t\t\t\tt = \" lost an OctaPower with \";\n\t\t\tbprint4 (PRINT_LOW, self.netname, t, s, \" seconds remaining\\n\");\n\t\t}\n\t}\n\n\tif (numberserverinfokey(\"dr\") != 0)\n\t{\n\t\tif (self.invisible_finished > time)\n\t\t{\n\t\t\ts = ftos(rint(self.invisible_finished - time));\n\t\t\tbprint4 (PRINT_LOW, self.netname, \" lost a ring with \", s, \" seconds remaining\\n\");\n\t\t\tDropRing (self.invisible_finished - time);\n\t\t}\n\t}\n\n\tDropBackpack();\n}\n\nvoid() PlayerDie =\n{\n\tlocal   float   i;\n\tself.items = self.items - (self.items & IT_INVISIBILITY);\n\n\tPlayerDropStuff();\n\n\tself.invisible_finished = 0;    // don't die as eyes\n\tself.invincible_finished = 0;\n\tself.super_damage_finished = 0;\n\tself.radsuit_finished = 0;\n\tself.modelindex = modelindex_player;    // don't use eyes\n\n\tself.weaponmodel=\"\";\n\tself.view_ofs = '0 0 -8';\n\tself.deadflag = DEAD_DYING;\n\tself.solid = SOLID_NOT;\n\tself.flags = self.flags - (self.flags & FL_ONGROUND);\n\tself.movetype = MOVETYPE_TOSS;\n\tif (self.velocity_z < 10)\n\t\tself.velocity_z = self.velocity_z + random()*300;\n\n\tif (self.health < -40)\n\t{\n\t\tGibPlayer ();\n\t\treturn;\n\t}\n\n\tDeathSound();\n\t\n\tself.angles_x = 0;\n\tself.angles_z = 0;\n\t\n\tif (self.weapon == IT_AXE)\n\t{\n\t\tplayer_die_ax1 ();\n\t\treturn;\n\t}\n\t\n\ti = random();\n\t\n\tif (i < 0.2)\n\t\tplayer_diea1();\n\telse if (i < 0.4)\n\t\tplayer_dieb1();\n\telse if (i < 0.6)\n\t\tplayer_diec1();\n\telse if (i < 0.8)\n\t\tplayer_died1();\n\telse\n\t\tplayer_diee1();\n\n};\n\nvoid() set_suicide_frame =\n{       // used by klill command and diconnect command\n\tif (self.model != \"progs/player.mdl\")\n\t\treturn; // allready gibbed\n\tself.frame = $deatha11;\n\tself.solid = SOLID_NOT;\n\tself.movetype = MOVETYPE_TOSS;\n\tself.deadflag = DEAD_DEAD;\n\tself.nextthink = -1;\n};\n\n\nvoid()  player_diea1    =       [       $deatha1,       player_diea2    ] {};\nvoid()  player_diea2    =       [       $deatha2,       player_diea3    ] {};\nvoid()  player_diea3    =       [       $deatha3,       player_diea4    ] {};\nvoid()  player_diea4    =       [       $deatha4,       player_diea5    ] {};\nvoid()  player_diea5    =       [       $deatha5,       player_diea6    ] {};\nvoid()  player_diea6    =       [       $deatha6,       player_diea7    ] {};\nvoid()  player_diea7    =       [       $deatha7,       player_diea8    ] {};\nvoid()  player_diea8    =       [       $deatha8,       player_diea9    ] {};\nvoid()  player_diea9    =       [       $deatha9,       player_diea10   ] {};\nvoid()  player_diea10   =       [       $deatha10,      player_diea11   ] {};\nvoid()  player_diea11   =       [       $deatha11,      player_diea11 ] {PlayerDead();};\n\nvoid()  player_dieb1    =       [       $deathb1,       player_dieb2    ] {};\nvoid()  player_dieb2    =       [       $deathb2,       player_dieb3    ] {};\nvoid()  player_dieb3    =       [       $deathb3,       player_dieb4    ] {};\nvoid()  player_dieb4    =       [       $deathb4,       player_dieb5    ] {};\nvoid()  player_dieb5    =       [       $deathb5,       player_dieb6    ] {};\nvoid()  player_dieb6    =       [       $deathb6,       player_dieb7    ] {};\nvoid()  player_dieb7    =       [       $deathb7,       player_dieb8    ] {};\nvoid()  player_dieb8    =       [       $deathb8,       player_dieb9    ] {};\nvoid()  player_dieb9    =       [       $deathb9,       player_dieb9    ] {PlayerDead();};\n\nvoid()  player_diec1    =       [       $deathc1,       player_diec2    ] {};\nvoid()  player_diec2    =       [       $deathc2,       player_diec3    ] {};\nvoid()  player_diec3    =       [       $deathc3,       player_diec4    ] {};\nvoid()  player_diec4    =       [       $deathc4,       player_diec5    ] {};\nvoid()  player_diec5    =       [       $deathc5,       player_diec6    ] {};\nvoid()  player_diec6    =       [       $deathc6,       player_diec7    ] {};\nvoid()  player_diec7    =       [       $deathc7,       player_diec8    ] {};\nvoid()  player_diec8    =       [       $deathc8,       player_diec9    ] {};\nvoid()  player_diec9    =       [       $deathc9,       player_diec10   ] {};\nvoid()  player_diec10   =       [       $deathc10,      player_diec11   ] {};\nvoid()  player_diec11   =       [       $deathc11,      player_diec12   ] {};\nvoid()  player_diec12   =       [       $deathc12,      player_diec13   ] {};\nvoid()  player_diec13   =       [       $deathc13,      player_diec14   ] {};\nvoid()  player_diec14   =       [       $deathc14,      player_diec15   ] {};\nvoid()  player_diec15   =       [       $deathc15,      player_diec15 ] {PlayerDead();};\n\nvoid()  player_died1    =       [       $deathd1,       player_died2    ] {};\nvoid()  player_died2    =       [       $deathd2,       player_died3    ] {};\nvoid()  player_died3    =       [       $deathd3,       player_died4    ] {};\nvoid()  player_died4    =       [       $deathd4,       player_died5    ] {};\nvoid()  player_died5    =       [       $deathd5,       player_died6    ] {};\nvoid()  player_died6    =       [       $deathd6,       player_died7    ] {};\nvoid()  player_died7    =       [       $deathd7,       player_died8    ] {};\nvoid()  player_died8    =       [       $deathd8,       player_died9    ] {};\nvoid()  player_died9    =       [       $deathd9,       player_died9    ] {PlayerDead();};\n\nvoid()  player_diee1    =       [       $deathe1,       player_diee2    ] {};\nvoid()  player_diee2    =       [       $deathe2,       player_diee3    ] {};\nvoid()  player_diee3    =       [       $deathe3,       player_diee4    ] {};\nvoid()  player_diee4    =       [       $deathe4,       player_diee5    ] {};\nvoid()  player_diee5    =       [       $deathe5,       player_diee6    ] {};\nvoid()  player_diee6    =       [       $deathe6,       player_diee7    ] {};\nvoid()  player_diee7    =       [       $deathe7,       player_diee8    ] {};\nvoid()  player_diee8    =       [       $deathe8,       player_diee9    ] {};\nvoid()  player_diee9    =       [       $deathe9,       player_diee9    ] {PlayerDead();};\n\nvoid()  player_die_ax1  =       [       $axdeth1,       player_die_ax2  ] {};\nvoid()  player_die_ax2  =       [       $axdeth2,       player_die_ax3  ] {};\nvoid()  player_die_ax3  =       [       $axdeth3,       player_die_ax4  ] {};\nvoid()  player_die_ax4  =       [       $axdeth4,       player_die_ax5  ] {};\nvoid()  player_die_ax5  =       [       $axdeth5,       player_die_ax6  ] {};\nvoid()  player_die_ax6  =       [       $axdeth6,       player_die_ax7  ] {};\nvoid()  player_die_ax7  =       [       $axdeth7,       player_die_ax8  ] {};\nvoid()  player_die_ax8  =       [       $axdeth8,       player_die_ax9  ] {};\nvoid()  player_die_ax9  =       [       $axdeth9,       player_die_ax9  ] {PlayerDead();};\n"
  },
  {
    "path": "quakec/basemod/progs.src",
    "content": "// First non-comment character must be # to use\n// \"new style\" progs.src\n#pragma PROGS_DAT qwprogs.dat\n\n// uncomment based on desired compile\n// #define NETQUAKE\n// #define FTE\n#define MONSTERS\n#define REPLACEMENTS\n\n// compile options\n#ifdef NETQUAKE\n// if netquake, use progs.dat file name\n#pragma PROGS_DAT progs.dat\n#endif\n\n#ifdef FTE\n// if FTE, use FTE style progs and use fteprogs.dat file name\n#pragma target fte\n#pragma PROGS_DAT fteprogs.dat\n#endif\n\n// includes\n#include \"defs.qc\"\n#include \"subs.qc\"\n#include \"engine.qc\"\n#include \"effects.qc\"\n#include \"obituary.qc\"\n#include \"combat.qc\"\n#include \"items.qc\"\n#include \"proj.qc\"\n#include \"weapons.qc\"\n#include \"world.qc\"\n#include \"client.qc\"\n#include \"spectate.qc\"\n#include \"player.qc\"\n#include \"doors.qc\"\n#include \"buttons.qc\"\n#include \"triggers.qc\"\n#include \"plats.qc\"\n#include \"misc.qc\"\n\n#ifdef MONSTERS\n#include \"fight.qc\"\n#include \"ai.qc\"\n#include \"monsters.qc\"\n#include \"dog.qc\"\n#include \"soldier.qc\"\n#include \"enforcer.qc\"\n#include \"ogre.qc\"\n#include \"wizard.qc\"\n#include \"demon.qc\"\n#include \"knight.qc\"\n#include \"hknight.qc\"\n#include \"shalrath.qc\"\n#include \"shambler.qc\"\n#include \"fish.qc\"\n#include \"tarbaby.qc\"\n#include \"zombie.qc\"\n#include \"boss.qc\"\n#include \"oldone.qc\"\n#else\n#include \"nomonst.qc\"\n#endif\n\n#ifdef REPLACEMENTS\n#include \"replace.qc\"\n#endif "
  },
  {
    "path": "quakec/basemod/proj.qc",
    "content": "\n// Generic projectile spawning code (PRJ) ---\n// projectile effect enumerator\nenum {\n\tPE_NONE,\n\tPE_SPIKE,\n\tPE_SUPERSPIKE,\n\tPE_WIZSPIKE,\n\tPE_KNIGHTSPIKE,\n\tPE_GUNSHOT,\n\tPE_EXPLOSION,\n\tPE_EXPLOSIONGROUND,\n\tPE_LASER,\n\tPE_ZOMBIEGIB\n};\n\n// functions used only by this QC file\nfloat() _PRJ_Bounce =\n{\n\tif (other.takedamage == DAMAGE_AIM)\n\t\treturn 0; // explode\n\n\tsound (self, CHAN_WEAPON, \"weapons/bounce.wav\", 1, ATTN_NORM);  // bounce sound\n\tif (self.velocity == '0 0 0')\n\t\tself.avelocity = '0 0 0';\n\n\treturn 1; // keep bouncing\n};\n\nfloat() _PRJ_Remove =\n{\n\tremove(self);\n\treturn 1; // stop execution within touch function\n};\n\nfloat() _PRJ_Stop =\n{\n\tif (other.takedamage)\n\t\treturn 0;\n\n\tsound (self, CHAN_WEAPON, \"zombie/z_miss.wav\", 1, ATTN_NORM); // bounce sound\n\tself.velocity = '0 0 0';\n\tself.avelocity = '0 0 0';\n\tself.proj_touch = _PRJ_Remove;\n\treturn 1; // keep the entity alive\n};\n\nvoid() _PRJ_Touch =\n{\n\tlocal entity ignore;\n\n\t// check validity of projectile\n\tif (other == self.owner)\n\t\treturn; // don't explode on owner\n\n\tif (self.voided) {\n\t\treturn;\n\t}\n\n\tif (pointcontents(self.origin) == CONTENT_SKY)\n\t{\n\t\tremove(self);\n\t\treturn;\n\t}\n\n\t// handle custom touch\n\tif (other != self) // didn't expire\n\t\tif (self.proj_touch) // is valid function\n\t\t\tif (self.proj_touch())\n\t\t\t\treturn;\n\n\t// void projectile\n\tself.voided = 1;\n\n\t// do projectile damage\n\tignore = self;\n\n\tif (other.health >= 1 && self.damage_direct)\n\t{\n\t\tT_Damage (other, self, self.owner, self.damage_direct, self.mod_direct);\n\t\tignore = other;\n\t}\n\n\tif (self.radius_exp)\n\t\tT_RadiusDamage (self, self.owner, self.damage_exp, self.radius_exp, ignore, self.mod_exp);\n\t\n\t// run projectile effect\n\tswitch (self.proj_effect)\n\t{\n\tcase PE_SPIKE:\n\t\tif (ignore != self) // hit something\n\t\t\tspawn_touchblood (self.damage_direct);\n\t\telse if (other != self) // didn't expire\n\t\t\tTE_spike(self.origin);\n\t\tbreak;\n\tcase PE_SUPERSPIKE:\n\t\tif (ignore != self) // hit something\n\t\t\tspawn_touchblood (self.damage_direct);\n\t\telse if (other != self) // didn't expire\n\t\t\tTE_superspike(self.origin);\n\t\tbreak;\n\tcase PE_WIZSPIKE:\n\t\tif (ignore != self) // hit something\n\t\t\tspawn_touchblood (self.damage_direct);\n\t\telse if (other != self) // didn't expire\n\t\t\tTE_wizspike(self.origin);\n\t\tbreak;\n\tcase PE_KNIGHTSPIKE:\n\t\tif (ignore != self) // hit something\n\t\t\tspawn_touchblood (self.damage_direct);\n\t\telse if (other != self) // didn't expire\n\t\t\tTE_knightspike(self.origin);\n\t\tbreak;\n\tcase PE_LASER:\n\t\tif (other != self)\n\t\t\tsound (self, CHAN_WEAPON, \"enforcer/enfstop.wav\", 1, ATTN_STATIC);\n\t\tself.origin = self.origin - 8 * normalize(self.velocity);\n\tcase PE_GUNSHOT:\n\t\tif (ignore != self) // hit something\n\t\t\tspawn_touchblood (self.damage_direct);\n\t\telse if (other != self) // didn't expire\n\t\t\tTE_gunshot(self.origin);\n\t\tbreak;\n\tcase PE_EXPLOSION:\n\t\tself.origin = self.origin - 8 * normalize(self.velocity);\n\tcase PE_EXPLOSIONGROUND:\n\t\tTE_explosion(self.origin);\n\t\tbreak;\n\tcase PE_ZOMBIEGIB:\n\t\tsound (self, CHAN_WEAPON, \"zombie/z_hit.wav\", 1, ATTN_NORM);\n\t\tbreak;\n\t}\n\n\tremove(self);\n};\n\nvoid() _PRJ_Expire =\n{\n\tother = self;\n\t_PRJ_Touch();\t\n};\n\nvoid() _PRJ_Think =\n{\n\tif (self.expire_time > time)\n\t{\n\t\t_PRJ_Expire();\n\t\treturn;\n\t}\n\n\tself.nextthink = time + self.proj_think_time;\n\tself.proj_think(); // projectile could remove itself here\n};\n\n// functions used by outside QC files\n// set bouncy projectile function\nvoid() PRJ_SetBouncyProjectile =\n{\n\tnewmis.proj_touch = _PRJ_Bounce;\n\tnewmis.movetype = MOVETYPE_BOUNCE;\n\tnewmis.avelocity = '300 300 300';\n};\n\n// set tossed projectile (zombie gib) function\nvoid() PRJ_SetTossedProjectile =\n{\n\tnewmis.proj_touch = _PRJ_Stop;\n\tnewmis.movetype = MOVETYPE_BOUNCE;\n\tnewmis.avelocity = '3000 1000 2000';\n};\n\n// set radius damage function\nvoid(INTEGER damg, INTEGER damgrad, INTEGER damgmod) PRJ_SetRadiusDamage =\n{\n\tnewmis.damage_exp = damg;\n\tnewmis.radius_exp = damgrad;\n\tnewmis.mod_exp = damgmod;\n};\n\n// extra think function, should always be called ONCE after the main spawn function\nvoid(void() thinkfunc, float thinktimeinit, float thinkres) PRJ_SetThink =\n{\n\tnewmis.think = _PRJ_Think;\n\tnewmis.nextthink = time + thinktimeinit;\n\tnewmis.proj_think = thinkfunc;\n\tnewmis.proj_think_time = thinkres;\n};\n\n// extra touch function\nvoid(float() touchfunc) PRJ_SetTouch =\n{\n\tnewmis.proj_touch = touchfunc;\n};\n\n// main spawning function\nvoid(entity parent, string modl, vector org, vector vel, INTEGER effect, INTEGER damg, INTEGER damgmod, float expiretime) PRJ_FireProjectile =\n{\n\tnewmis = spawn ();\n\tnewmis.owner = parent;\n\tnewmis.movetype = MOVETYPE_FLYMISSILE;\n\tnewmis.solid = SOLID_BBOX;\n//\tnewmis.classname = class;\n\tnewmis.velocity = vel;\n\n\tnewmis.damage_direct = damg;\n\tnewmis.mod_direct = damgmod;\n\tnewmis.proj_effect = effect;\n\n\tnewmis.touch = _PRJ_Touch;\n\tnewmis.expire_time = time + expiretime;\n\n\tnewmis.think = _PRJ_Expire;\n\tnewmis.nextthink = time + expiretime;\n\t\n\tnewmis.angles = vectoangles(newmis.velocity);\n\n\tsetmodel (newmis, modl);\n\tsetsize (newmis, VEC_ORIGIN, VEC_ORIGIN);             \n\tsetorigin (newmis, org);\n};\n"
  },
  {
    "path": "quakec/basemod/replace.qc",
    "content": "\n// Basemod replacement ent defines\n#define ENT_REPLACE(f,t) void(void) f = { self.classname = #t; t(); }\n#define ENT_REPLACE_FUNC(f,t,x) void(void) f = { self.classname = #t; x(); t(); }\n\n// Replacement functions\nvoid() health_setrotten = { self.spawnflags |= H_ROTTEN; };\nvoid() health_setmega = { self.spawnflags |= H_MEGA; };\nvoid() ammo_setlarge = { self.spawnflags |= WEAPON_BIG2; };\n\n// -- Quake 3 entities\n// weapons\nENT_REPLACE(weapon_gauntlet, weapon_supershotgun); // Gauntlet (not normally used)\nENT_REPLACE(weapon_machinegun, weapon_nailgun); // Machinegun (not normally used)\nENT_REPLACE(weapon_shotgun, weapon_supershotgun); // Shotgun\nENT_REPLACE(weapon_plasmagun, weapon_supernailgun); // Plasma gun\nENT_REPLACE(weapon_railgun, weapon_rocketlauncher); // Railgun\nENT_REPLACE(weapon_bfg, weapon_lightning); // BFG\n// holdables\nENT_REPLACE(holdable_medikit, item_health); // personal medikit \nENT_REPLACE(holdable_teleporter, info_null); // personal teleporter\n// armor\nENT_REPLACE(item_armor_body, item_armorInv); // 100 armor\nENT_REPLACE(item_armor_combat, item_armor2); // 50 armor\nENT_REPLACE(item_armor_shard, info_null); // +5 armor shard\n// powerups\nENT_REPLACE(item_enviro, item_artifact_envirosuit); // enviro powerup\nENT_REPLACE(item_flight, item_artifact_invisibility); // flight\nENT_REPLACE(item_haste, item_artifact_super_damage); // haste\nENT_REPLACE(item_quad, item_artifact_super_damage); // quad damage\nENT_REPLACE(item_invis, item_artifact_invisibility); // invisibility\nENT_REPLACE(item_regen, item_health); // regeneration\n// health\nENT_REPLACE(item_health_large, item_health); // +50 health\nENT_REPLACE_FUNC(item_health_mega, item_health, health_setmega); // +100 health (ignore max)\nENT_REPLACE_FUNC(item_health_small, item_health, health_setrotten); // +5 health (ignore max)\n// ammo\nENT_REPLACE(ammo_bullets, item_spikes); // Machinegun ammo\nENT_REPLACE(ammo_grenades, item_rockets); // Grenade launcher ammo\nENT_REPLACE(ammo_lightning, item_cells); // Lightning gun ammo\nENT_REPLACE_FUNC(ammo_slugs, item_rockets, ammo_setlarge); // Railgun ammo\nENT_REPLACE_FUNC(ammo_bfg, item_cells, ammo_setlarge); // BFG ammo\n// other ents\nENT_REPLACE(misc_teleporter_dest, info_teleport_destination); // teleport destination\nENT_REPLACE(target_position, info_notnull); // \"target\" entity\nENT_REPLACE(info_player_intermission, info_intermission); // intermission\n\n// -- Quake 2 entities\n// weapons\nENT_REPLACE(weapon_chaingun, weapon_supernailgun); // chaingun\nENT_REPLACE(weapon_hyperblaster, weapon_lightning); // hyperblaster\n// armor\nENT_REPLACE(item_armor_jacket, item_armor1);\nENT_REPLACE(item_power_screen, item_armor2); // power screen (not used often)\nENT_REPLACE(item_power_shield, item_armorInv); // power shield\n // powerups\nENT_REPLACE(item_bandolier, item_rockets, ammo_setlarge); // bandolier\nENT_REPLACE(item_breather, item_artifact_envirosuit); // underwater air breather\nENT_REPLACE(item_invulnerability, item_artifact_invulnerability); // invulnerable artifact\nENT_REPLACE(item_pack, item_rockets, ammo_setlarge); // ammo pack\nENT_REPLACE(item_silencer, item_artifact_invisibility); // weapon silencer\n// health\nENT_REPLACE_FUNC(item_ancient_head, item_health, health_setmega); // ancient head (+2 max health)\nENT_REPLACE_FUNC(item_adrenaline, item_health, health_setmega); // adrenaline (+1 max health)\n\n// -- Half-life entities\n// weapons\nENT_REPLACE(weapon_357, weapon_grenadelauncher); // 357 magnum\nENT_REPLACE(weapon_9mmAR, weapon_supernailgun); // mp5\nENT_REPLACE(weapon_9mmhandgun, weapon_nailgun); // handgun\nENT_REPLACE(weapon_crossbow, weapon_grenadelauncher); // crossbow\nENT_REPLACE(weapon_crowbar, info_null); // crowbar\nENT_REPLACE(weapon_egon, weapon_lightning); // ghostbusters gun\nENT_REPLACE(weapon_gauss, weapon_lightning); // tau cannon\nENT_REPLACE(weapon_handgrenade, item_rockets); // hand grenades\nENT_REPLACE(weapon_hornetgun, info_null); // hornet gun\nENT_REPLACE(weapon_rpg, weapon_rocketlauncher); // rpg\nENT_REPLACE(weapon_satchel, item_rockets); // satchel charge\nENT_REPLACE(weapon_snark, item_cells); // snarks\nENT_REPLACE(weapon_tripmine, item_rockets); // tripmines\n// armor\nENT_REPLACE(item_battery, info_null); // battery charge\n// powerups\nENT_REPLACE(item_airtank, item_artifact_envirosuit); // air tank (not used often)\nENT_REPLACE(item_antidote, item_health); // antidote (not used often)\nENT_REPLACE(item_longjump, weapon_rocketlauncher); // long jump module\nENT_REPLACE(item_suit, item_artifact_envirosuit); // HEV suit\n// health\nENT_REPLACE(item_healthkit, item_health); // floor health kit\n// ammo\nENT_REPLACE(ammo_357, item_rockets); // 357 ammo\nENT_REPLACE(ammo_9mmAR, item_spikes); // mp5 clip\nENT_REPLACE(ammo_9mmbox, item_spikes, ammo_setlarge); // 9mm box\nENT_REPLACE(ammo_9mmclip, item_spikes); // 9mm clip\nENT_REPLACE(ammo_ARgrenades, item_rockets); // mp5 grenades\nENT_REPLACE(ammo_buckshot, item_shells); // shotgun shells\nENT_REPLACE(ammo_crossbow, item_rockets); // crossbow bolts\nENT_REPLACE(ammo_gaussclip, item_cells); // cells for gauss/egon\nENT_REPLACE(ammo_rpgclip, item_rockets, ammo_setlarge); // RPG ammo\n\n// special replace\nvoid(void() func) CheckSpawn =\n{\n\t// these ents conflict with q1 field names\n\tswitch (self.classname)\n\t{\n\tcase \"ammo_shells\":\n\t\tself.classname = \"item_shells\";\n\t\titem_shells();\n\t\tbreak;\n\tcase \"ammo_nails\":\n\t\tself.classname = \"item_spikes\";\n\t\titem_spikes();\n\t\tbreak;\n\tcase \"ammo_cells\":\n\t\tself.classname = \"item_cells\";\n\t\titem_cells();\n\t\tbreak;\n\tdefault:\n\t\tif (func)\n\t\t\tfunc();\n\t\telse\n\t\t{\n\t\t\tdprint(\"Couldn't find spawn function \");\n\t\t\tdprint(self.classname);\n\t\t\tdprint(\"\\n\");\n\t\t}\n\t}\n};"
  },
  {
    "path": "quakec/basemod/shalrath.qc",
    "content": "/*\n==============================================================================\n\nSHAL-RATH\n\n==============================================================================\n*/\n$cd id1/models/shalrath\n$origin 0 0 24\n$base base\n$skin skin\n$scale 0.7\n\n$frame attack1 attack2 attack3 attack4 attack5 attack6 attack7 attack8\n$frame attack9 attack10 attack11\n\n$frame pain1 pain2 pain3 pain4 pain5 \n\n$frame death1 death2 death3 death4 death5 death6 death7\n\n$frame\twalk1 walk2 walk3 walk4 walk5 walk6 walk7 walk8 walk9 walk10\n$frame\twalk11 walk12\n\nvoid() shalrath_pain;\nvoid() ShalMissile;\nvoid() shal_stand     =[      $walk1,       shal_stand    ] {ai_stand();};\n\nvoid() shal_walk1     =[      $walk2,       shal_walk2    ] {\nif (random() < 0.2)\n\tsound (self, CHAN_VOICE, \"shalrath/idle.wav\", 1, ATTN_IDLE);\nai_walk(6);};\nvoid() shal_walk2     =[      $walk3,       shal_walk3    ] {ai_walk(4);};\nvoid() shal_walk3     =[      $walk4,       shal_walk4    ] {ai_walk(0);};\nvoid() shal_walk4     =[      $walk5,       shal_walk5    ] {ai_walk(0);};\nvoid() shal_walk5     =[      $walk6,       shal_walk6    ] {ai_walk(0);};\nvoid() shal_walk6     =[      $walk7,       shal_walk7    ] {ai_walk(0);};\nvoid() shal_walk7     =[      $walk8,       shal_walk8    ] {ai_walk(5);};\nvoid() shal_walk8     =[      $walk9,       shal_walk9    ] {ai_walk(6);};\nvoid() shal_walk9     =[      $walk10,       shal_walk10    ] {ai_walk(5);};\nvoid() shal_walk10    =[      $walk11,       shal_walk11    ] {ai_walk(0);};\nvoid() shal_walk11    =[      $walk12,       shal_walk12    ] {ai_walk(4);};\nvoid() shal_walk12    =[      $walk1,       shal_walk1    ] {ai_walk(5);};\n\nvoid() shal_run1     =[      $walk2,       shal_run2    ] {\nif (random() < 0.2)\n\tsound (self, CHAN_VOICE, \"shalrath/idle.wav\", 1, ATTN_IDLE);\nai_run(6);};\nvoid() shal_run2     =[      $walk3,       shal_run3    ] {ai_run(4);};\nvoid() shal_run3     =[      $walk4,       shal_run4    ] {ai_run(0);};\nvoid() shal_run4     =[      $walk5,       shal_run5    ] {ai_run(0);};\nvoid() shal_run5     =[      $walk6,       shal_run6    ] {ai_run(0);};\nvoid() shal_run6     =[      $walk7,       shal_run7    ] {ai_run(0);};\nvoid() shal_run7     =[      $walk8,       shal_run8    ] {ai_run(5);};\nvoid() shal_run8     =[      $walk9,       shal_run9    ] {ai_run(6);};\nvoid() shal_run9     =[      $walk10,       shal_run10    ] {ai_run(5);};\nvoid() shal_run10    =[      $walk11,       shal_run11    ] {ai_run(0);};\nvoid() shal_run11    =[      $walk12,       shal_run12    ] {ai_run(4);};\nvoid() shal_run12    =[      $walk1,       shal_run1    ] {ai_run(5);};\n\nvoid() shal_attack1     =[      $attack1,       shal_attack2    ] {\nsound (self, CHAN_VOICE, \"shalrath/attack.wav\", 1, ATTN_NORM);\nai_face();\n};\nvoid() shal_attack2     =[      $attack2,       shal_attack3    ] {ai_face();};\nvoid() shal_attack3     =[      $attack3,       shal_attack4    ] {ai_face();};\nvoid() shal_attack4     =[      $attack4,       shal_attack5    ] {ai_face();};\nvoid() shal_attack5     =[      $attack5,       shal_attack6    ] {ai_face();};\nvoid() shal_attack6     =[      $attack6,       shal_attack7    ] {ai_face();};\nvoid() shal_attack7     =[      $attack7,       shal_attack8    ] {ai_face();};\nvoid() shal_attack8     =[      $attack8,       shal_attack9    ] {ai_face();};\nvoid() shal_attack9     =[      $attack9,       shal_attack10   ] {ShalMissile();};\nvoid() shal_attack10    =[      $attack10,      shal_attack11   ] {ai_face();};\nvoid() shal_attack11    =[      $attack11,      shal_run1   ] {};\n\nvoid() shal_pain1       =[      $pain1, shal_pain2      ] {};\nvoid() shal_pain2       =[      $pain2, shal_pain3      ] {};\nvoid() shal_pain3       =[      $pain3, shal_pain4      ] {};\nvoid() shal_pain4       =[      $pain4, shal_pain5      ] {};\nvoid() shal_pain5       =[      $pain5, shal_run1      ] {};\n\nvoid() shal_death1      =[      $death1,        shal_death2     ] {};\nvoid() shal_death2      =[      $death2,        shal_death3     ] {};\nvoid() shal_death3      =[      $death3,        shal_death4     ] {};\nvoid() shal_death4      =[      $death4,        shal_death5     ] {};\nvoid() shal_death5      =[      $death5,        shal_death6     ] {};\nvoid() shal_death6      =[      $death6,        shal_death7     ] {};\nvoid() shal_death7      =[      $death7,        shal_death7    ] {};\n\n\nvoid() shalrath_pain =\n{\n\tif (self.pain_finished > time)\n\t\treturn;\n\n\tsound (self, CHAN_VOICE, \"shalrath/pain.wav\", 1, ATTN_NORM);\n\tshal_pain1();\n\tself.pain_finished = time + 3;\n};\n\nvoid() shalrath_die =\n{\n// check for gib\n\tif (self.health < -90)\n\t{\n\t\tsound (self, CHAN_VOICE, \"player/udeath.wav\", 1, ATTN_NORM);\n\t\tThrowHead (\"progs/h_shal.mdl\", self.health);\n\t\tThrowGib (\"progs/gib1.mdl\", self.health);\n\t\tThrowGib (\"progs/gib2.mdl\", self.health);\n\t\tThrowGib (\"progs/gib3.mdl\", self.health);\n\t\treturn;\n\t}\n\n\tsound (self, CHAN_VOICE, \"shalrath/death.wav\", 1, ATTN_NORM);\n\tshal_death1();\n\tself.solid = SOLID_NOT;\n\t// insert death sounds here\n};\n\n/*\n================\nShalMissile\n================\n*/\nfloat() ShalMissileTouch;\nvoid() ShalHome;\nvoid() ShalMissile =\n{\n\tlocal\tvector\tdir;\n\tlocal\tfloat\tdist, flytime;\n\n\tdir = normalize((self.enemy.origin + '0 0 10') - self.origin);\n\tdist = vlen (self.enemy.origin - self.origin);\n\tflytime = dist * 0.002;\n\tif (flytime < 0.1)\n\t\tflytime = 0.1;\n\n\tmuzzleflash();\n\tsound (self, CHAN_WEAPON, \"shalrath/attack2.wav\", 1, ATTN_NORM);\n\n\tPRJ_FireProjectile(self,\n\t\t\"progs/v_spike.mdl\",\n\t\tself.origin + '0 0 10',\n\t\tdir * 400,\n\t\tPE_EXPLOSION,\n\t\t0,\n\t\t0,\n\t\t30);\n\tPRJ_SetRadiusDamage(40, 80, MOD_SHALRATH);\n\tPRJ_SetThink(ShalHome, flytime, 0.2);\n\tPRJ_SetTouch(ShalMissileTouch);\n\tnewmis.avelocity = '300 300 300';\n\tnewmis.enemy = self.enemy;\n};\n\nvoid() ShalHome =\n{\n\tlocal vector\tdir, vtemp;\n\tvtemp = self.enemy.origin + '0 0 10';\n\tif (self.enemy.health < 1)\n\t{\n\t\tremove(self);\n\t\treturn;\n\t}\n\n\tdir = normalize(vtemp - self.origin);\n\tif (skill > 2)\n\t\tself.velocity = dir * 350;\n\telse\n\t\tself.velocity = dir * 250;\n};\n\nfloat() ShalMissileTouch =\n{\n\tif (other.classname == \"monster_zombie\")\n\t\tT_Damage (other, self, self, 110, MOD_SHALRATH);\t\n\n\treturn 0; // always explode on touch\n};\n\n//=================================================================\n\n/*QUAKED monster_shalrath (1 0 0) (-32 -32 -24) (32 32 48) Ambush\n*/\nvoid() monster_shalrath =\n{\n\tif (deathmatch)\n\t{\n\t\tremove(self);\n\t\treturn;\n\t}\n\tprecache_model2 (\"progs/shalrath.mdl\");\n\tprecache_model2 (\"progs/h_shal.mdl\");\n\tprecache_model2 (\"progs/v_spike.mdl\");\n\t\n\tprecache_sound2 (\"shalrath/attack.wav\");\n\tprecache_sound2 (\"shalrath/attack2.wav\");\n\tprecache_sound2 (\"shalrath/death.wav\");\n\tprecache_sound2 (\"shalrath/idle.wav\");\n\tprecache_sound2 (\"shalrath/pain.wav\");\n\tprecache_sound2 (\"shalrath/sight.wav\");\n\t\n\tself.solid = SOLID_SLIDEBOX;\n\tself.movetype = MOVETYPE_STEP;\n\t\n\tsetmodel (self, \"progs/shalrath.mdl\");\n\tsetsize (self, VEC_HULL2_MIN, VEC_HULL2_MAX);\n\tself.health = 400;\n\n\tself.th_stand = shal_stand;\n\tself.th_walk = shal_walk1;\n\tself.th_run = shal_run1;\n\tself.th_die = shalrath_die;\n\tself.th_pain = shalrath_pain;\n\tself.th_missile = shal_attack1;\n\n\tself.think = walkmonster_start;\n\tself.nextthink = time + 0.1 + random ()*0.1;\t\n\n};\n"
  },
  {
    "path": "quakec/basemod/shambler.qc",
    "content": "/*\n==============================================================================\n\nSHAMBLER\n\n==============================================================================\n*/\n\n$cd id1/models/shams\n$origin 0 0 24\n$base base\t\t\n$skin base\n\n$frame stand1 stand2 stand3 stand4 stand5 stand6 stand7 stand8 stand9\n$frame stand10 stand11 stand12 stand13 stand14 stand15 stand16 stand17\n\n$frame walk1 walk2 walk3 walk4 walk5 walk6 walk7 \n$frame walk8 walk9 walk10 walk11 walk12\n\n$frame\trun1 run2 run3 run4 run5 run6\n\n$frame smash1 smash2 smash3 smash4 smash5 smash6 smash7 \n$frame smash8 smash9 smash10 smash11 smash12\n\n$frame swingr1 swingr2 swingr3 swingr4 swingr5 \n$frame swingr6 swingr7 swingr8 swingr9\n\n$frame swingl1 swingl2 swingl3 swingl4 swingl5 \n$frame swingl6 swingl7 swingl8 swingl9\n\n$frame magic1 magic2 magic3 magic4 magic5 \n$frame magic6 magic7 magic8 magic9 magic10 magic11 magic12\n\n$frame pain1 pain2 pain3 pain4 pain5 pain6\n\n$frame death1 death2 death3 death4 death5 death6 \n$frame death7 death8 death9 death10 death11\n\nvoid(float chrg, float damage, float side, float crnd) ShamClaw =\n{\nlocal vector\tdelta;\nlocal float \tldmg;\n\n\tif (!self.enemy)\n\t\treturn;\n\tai_charge(chrg);\n\n\tdelta = self.enemy.origin - self.origin;\n\n\tif (vlen(delta) > 100)\n\t\treturn;\n\t\t\n\tldmg = (random() + random() + random()) * damage;\n\tT_Damage (self.enemy, self, self, ldmg, MOD_SHAMBLER);\n\tsound (self, CHAN_VOICE, \"shambler/smack.wav\", 1, ATTN_NORM);\n\n\tif (side)\n\t{\n\t\tmakevectors (self.angles);\n\t\tSpawnMeatSpray (self.origin + v_forward*16, (crandom() * crnd + side) * v_right);\n\t}\n};\n\n\nvoid() sham_stand1\t=[\t$stand1,\tsham_stand2\t] {ai_stand();};\nvoid() sham_stand2\t=[\t$stand2,\tsham_stand3\t] {ai_stand();};\nvoid() sham_stand3\t=[\t$stand3,\tsham_stand4\t] {ai_stand();};\nvoid() sham_stand4\t=[\t$stand4,\tsham_stand5\t] {ai_stand();};\nvoid() sham_stand5\t=[\t$stand5,\tsham_stand6\t] {ai_stand();};\nvoid() sham_stand6\t=[\t$stand6,\tsham_stand7\t] {ai_stand();};\nvoid() sham_stand7\t=[\t$stand7,\tsham_stand8\t] {ai_stand();};\nvoid() sham_stand8\t=[\t$stand8,\tsham_stand9\t] {ai_stand();};\nvoid() sham_stand9\t=[\t$stand9,\tsham_stand10] {ai_stand();};\nvoid() sham_stand10\t=[\t$stand10,\tsham_stand11] {ai_stand();};\nvoid() sham_stand11\t=[\t$stand11,\tsham_stand12] {ai_stand();};\nvoid() sham_stand12\t=[\t$stand12,\tsham_stand13] {ai_stand();};\nvoid() sham_stand13\t=[\t$stand13,\tsham_stand14] {ai_stand();};\nvoid() sham_stand14\t=[\t$stand14,\tsham_stand15] {ai_stand();};\nvoid() sham_stand15\t=[\t$stand15,\tsham_stand16] {ai_stand();};\nvoid() sham_stand16\t=[\t$stand16,\tsham_stand17] {ai_stand();};\nvoid() sham_stand17\t=[\t$stand17,\tsham_stand1\t] {ai_stand();};\n\nvoid() sham_walk1\t\t=[      $walk1,        sham_walk2 ] {ai_walk(10);};\nvoid() sham_walk2       =[      $walk2,        sham_walk3 ] {ai_walk(9);};\nvoid() sham_walk3       =[      $walk3,        sham_walk4 ] {ai_walk(9);};\nvoid() sham_walk4       =[      $walk4,        sham_walk5 ] {ai_walk(5);};\nvoid() sham_walk5       =[      $walk5,        sham_walk6 ] {ai_walk(6);};\nvoid() sham_walk6       =[      $walk6,        sham_walk7 ] {ai_walk(12);};\nvoid() sham_walk7       =[      $walk7,        sham_walk8 ] {ai_walk(8);};\nvoid() sham_walk8       =[      $walk8,        sham_walk9 ] {ai_walk(3);};\nvoid() sham_walk9       =[      $walk9,        sham_walk10] {ai_walk(13);};\nvoid() sham_walk10      =[      $walk10,       sham_walk11] {ai_walk(9);};\nvoid() sham_walk11      =[      $walk11,       sham_walk12] {ai_walk(7);};\nvoid() sham_walk12      =[      $walk12,       sham_walk1 ] {ai_walk(7);\nif (random() > 0.8)\n\tsound (self, CHAN_VOICE, \"shambler/sidle.wav\", 1, ATTN_IDLE);};\n\nvoid() sham_run1       =[      $run1,        sham_run2      ] {ai_run(20);};\nvoid() sham_run2       =[      $run2,        sham_run3      ] {ai_run(24);};\nvoid() sham_run3       =[      $run3,        sham_run4      ] {ai_run(20);};\nvoid() sham_run4       =[      $run4,        sham_run5      ] {ai_run(20);};\nvoid() sham_run5       =[      $run5,        sham_run6      ] {ai_run(24);};\nvoid() sham_run6       =[      $run6,        sham_run1      ] {ai_run(20);\nif (random() > 0.8)\n\tsound (self, CHAN_VOICE, \"shambler/sidle.wav\", 1, ATTN_IDLE);\n};\n\nvoid() sham_smash1     =[      $smash1,       sham_smash2    ] {\nsound (self, CHAN_VOICE, \"shambler/melee1.wav\", 1, ATTN_NORM);\nai_charge(2);};\nvoid() sham_smash2     =[      $smash2,       sham_smash3    ] {ai_charge(6);};\nvoid() sham_smash3     =[      $smash3,       sham_smash4    ] {ai_charge(6);};\nvoid() sham_smash4     =[      $smash4,       sham_smash5    ] {ai_charge(5);};\nvoid() sham_smash5     =[      $smash5,       sham_smash6    ] {ai_charge(4);};\nvoid() sham_smash6     =[      $smash6,       sham_smash7    ] {ai_charge(1);};\nvoid() sham_smash7     =[      $smash7,       sham_smash8    ] {ai_charge(0);};\nvoid() sham_smash8     =[      $smash8,       sham_smash9    ] {ai_charge(0);};\nvoid() sham_smash9     =[      $smash9,       sham_smash10   ] {ai_charge(0);};\nvoid() sham_smash10    =[      $smash10,      sham_smash11   ] {ShamClaw(0, 40, 0, 100);};\nvoid() sham_smash11    =[      $smash11,      sham_smash12   ] {ai_charge(5);};\nvoid() sham_smash12    =[      $smash12,      sham_run1\t   ] {ai_charge(4);};\n\nvoid() sham_swingr1;\n\nvoid() sham_swingl1\t=[      $swingl1,      sham_swingl2   ] {\nsound (self, CHAN_VOICE, \"shambler/melee2.wav\", 1, ATTN_NORM);\nai_charge(5);};\nvoid() sham_swingl2 =[      $swingl2,      sham_swingl3   ] {ai_charge(3);};\nvoid() sham_swingl3 =[      $swingl3,      sham_swingl4   ] {ai_charge(7);};\nvoid() sham_swingl4 =[      $swingl4,      sham_swingl5   ] {ai_charge(3);};\nvoid() sham_swingl5 =[      $swingl5,      sham_swingl6   ] {ai_charge(7);};\nvoid() sham_swingl6 =[      $swingl6,      sham_swingl7   ] {ai_charge(9);};\nvoid() sham_swingl7 =[      $swingl7,      sham_swingl8   ] {ai_charge(5); ShamClaw(10, 20, 250, 0);};\nvoid() sham_swingl8 =[      $swingl8,      sham_swingl9   ] {ai_charge(4);};\nvoid() sham_swingl9 =[      $swingl9,      sham_run1  ] {\nai_charge(8);\nif (random()<0.5)\n\tself.think = sham_swingr1;\n};\n\nvoid() sham_swingr1\t=[      $swingr1,      sham_swingr2   ] {\nsound (self, CHAN_VOICE, \"shambler/melee1.wav\", 1, ATTN_NORM);\nai_charge(1);};\nvoid() sham_swingr2\t=[      $swingr2,      sham_swingr3   ] {ai_charge(8);};\nvoid() sham_swingr3 =[      $swingr3,      sham_swingr4   ] {ai_charge(14);};\nvoid() sham_swingr4 =[      $swingr4,      sham_swingr5   ] {ai_charge(7);};\nvoid() sham_swingr5 =[      $swingr5,      sham_swingr6   ] {ai_charge(3);};\nvoid() sham_swingr6 =[      $swingr6,      sham_swingr7   ] {ai_charge(6);};\nvoid() sham_swingr7 =[      $swingr7,      sham_swingr8   ] {ai_charge(6); ShamClaw(10, 20, -250, 0);};\nvoid() sham_swingr8 =[      $swingr8,      sham_swingr9   ] {ai_charge(3);};\nvoid() sham_swingr9 =[      $swingr9,      sham_run1  ] {ai_charge(1);\nai_charge(10);\nif (random()<0.5)\n\tself.think = sham_swingl1;\n};\n\nvoid() sham_melee =\n{\n\tlocal float chance;\n\t\n\tchance = random();\n\tif (chance > 0.6 || self.health == 600)\n\t\tsham_smash1 ();\n\telse if (chance > 0.3)\n\t\tsham_swingr1 ();\n\telse\n\t\tsham_swingl1 ();\n};\n\n\n//============================================================================\n\nvoid() CastLightning =\n{\n\tlocal\tvector\torg, dir;\n\t\n\tmuzzleflash();\n\tai_face ();\n\n\torg = self.origin + '0 0 40';\n\n\tdir = self.enemy.origin + '0 0 16' - org;\n\tdir = normalize (dir);\n\n\ttraceline (org, self.origin + dir*600, TRUE, self);\n\n\tTE_lightning1(self, org, trace_endpos);\n\n\tLightningDamage (org, trace_endpos, self, 10, MOD_SHAMBLER);\n};\n\nvoid() sham_magic1     =[      $magic1,       sham_magic2    ] {ai_face();\n\tsound (self, CHAN_WEAPON, \"shambler/sattck1.wav\", 1, ATTN_NORM);\n};\nvoid() sham_magic2     =[      $magic2,       sham_magic3    ] {ai_face();};\nvoid() sham_magic3     =[      $magic3,       sham_magic4    ] {ai_face();self.nextthink = time + 0.2;\nlocal entity o;\n\nmuzzleflash();\nai_face();\n// TODO: Spawn as an effect\nself.owner = spawn();\no = self.owner;\nsetmodel (o, \"progs/s_light.mdl\");\nsetorigin (o, self.origin);\no.angles = self.angles;\no.nextthink = time + 0.7;\no.think = SUB_Remove;\n};\nvoid() sham_magic4     =[      $magic4,       sham_magic5    ]\n{\nself.effects = self.effects | EF_MUZZLEFLASH;\nself.owner.frame = 1;\n};\nvoid() sham_magic5     =[      $magic5,       sham_magic6    ]\n{\nself.effects = self.effects | EF_MUZZLEFLASH;\nself.owner.frame = 2;\n};\nvoid() sham_magic6     =[      $magic6,       sham_magic9    ]\n{\nremove (self.owner);\nCastLightning();\nsound (self, CHAN_WEAPON, \"shambler/sboom.wav\", 1, ATTN_NORM);\n};\nvoid() sham_magic9     =[      $magic9,       sham_magic10   ]\n{CastLightning();};\nvoid() sham_magic10    =[      $magic10,      sham_magic11   ]\n{CastLightning();};\nvoid() sham_magic11    =[      $magic11,      sham_magic12   ]\n{\nif (skill > 2)\n\tCastLightning();\n};\nvoid() sham_magic12    =[      $magic12,      sham_run1\t   ] {};\n\n\n\nvoid() sham_pain1       =[      $pain1, sham_pain2      ] {};\nvoid() sham_pain2       =[      $pain2, sham_pain3      ] {};\nvoid() sham_pain3       =[      $pain3, sham_pain4      ] {};\nvoid() sham_pain4       =[      $pain4, sham_pain5      ] {};\nvoid() sham_pain5       =[      $pain5, sham_pain6      ] {};\nvoid() sham_pain6       =[      $pain6, sham_run1      ] {};\n\nvoid(entity attacker, float damage)\tsham_pain =\n{\n\tsound (self, CHAN_VOICE, \"shambler/shurt2.wav\", 1, ATTN_NORM);\n\n\tif (self.health <= 0)\n\t\treturn;\t\t// allready dying, don't go into pain frame\n\n\tif (random()*400 > damage)\n\t\treturn;\t\t// didn't flinch\n\n\tif (self.pain_finished > time)\n\t\treturn;\n\tself.pain_finished = time + 2;\n\t\t\n\tsham_pain1 ();\n};\n\n\n//============================================================================\n\nvoid() sham_death1      =[      $death1,       sham_death2     ] {};\nvoid() sham_death2      =[      $death2,       sham_death3     ] {};\nvoid() sham_death3      =[      $death3,       sham_death4     ] {self.solid = SOLID_NOT;};\nvoid() sham_death4      =[      $death4,       sham_death5     ] {};\nvoid() sham_death5      =[      $death5,       sham_death6     ] {};\nvoid() sham_death6      =[      $death6,       sham_death7     ] {};\nvoid() sham_death7      =[      $death7,       sham_death8     ] {};\nvoid() sham_death8      =[      $death8,       sham_death9     ] {};\nvoid() sham_death9      =[      $death9,       sham_death10    ] {};\nvoid() sham_death10     =[      $death10,      sham_death11    ] {};\nvoid() sham_death11     =[      $death11,      sham_death11    ] {};\n\nvoid() sham_die =\n{\n// check for gib\n\tif (self.health < -60)\n\t{\n\t\tsound (self, CHAN_VOICE, \"player/udeath.wav\", 1, ATTN_NORM);\n\t\tThrowHead (\"progs/h_shams.mdl\", self.health);\n\t\tThrowGib (\"progs/gib1.mdl\", self.health);\n\t\tThrowGib (\"progs/gib2.mdl\", self.health);\n\t\tThrowGib (\"progs/gib3.mdl\", self.health);\n\t\treturn;\n\t}\n\n// regular death\n\tsound (self, CHAN_VOICE, \"shambler/sdeath.wav\", 1, ATTN_NORM);\n\tsham_death1 ();\n};\n\n//============================================================================\n\n\n/*QUAKED monster_shambler (1 0 0) (-32 -32 -24) (32 32 64) Ambush\n*/\nvoid() monster_shambler =\n{\n\tif (deathmatch)\n\t{\n\t\tremove(self);\n\t\treturn;\n\t}\n\tprecache_model (\"progs/shambler.mdl\");\n\tprecache_model (\"progs/s_light.mdl\");\n\tprecache_model (\"progs/h_shams.mdl\");\n\tprecache_model (\"progs/bolt.mdl\");\n\t\n\tprecache_sound (\"shambler/sattck1.wav\");\n\tprecache_sound (\"shambler/sboom.wav\");\n\tprecache_sound (\"shambler/sdeath.wav\");\n\tprecache_sound (\"shambler/shurt2.wav\");\n\tprecache_sound (\"shambler/sidle.wav\");\n\tprecache_sound (\"shambler/ssight.wav\");\n\tprecache_sound (\"shambler/melee1.wav\");\n\tprecache_sound (\"shambler/melee2.wav\");\n\tprecache_sound (\"shambler/smack.wav\");\n\n\tself.solid = SOLID_SLIDEBOX;\n\tself.movetype = MOVETYPE_STEP;\n\tsetmodel (self, \"progs/shambler.mdl\");\n\n\tsetsize (self, VEC_HULL2_MIN, VEC_HULL2_MAX);\n\tself.health = 600;\n\n\tself.th_stand = sham_stand1;\n\tself.th_walk = sham_walk1;\n\tself.th_run = sham_run1;\n\tself.th_die = sham_die;\n\tself.th_melee = sham_melee;\n\tself.th_missile = sham_magic1;\n\tself.th_pain = sham_pain;\n\t\n\twalkmonster_start();\n};\n"
  },
  {
    "path": "quakec/basemod/soldier.qc",
    "content": "/*\n==============================================================================\n\nSOLDIER / PLAYER\n\n==============================================================================\n*/\n\n$cd id1/models/soldier3\n$origin 0 -6 24\n$base base\t\t\n$skin skin\n\n$frame stand1 stand2 stand3 stand4 stand5 stand6 stand7 stand8\n\n$frame death1 death2 death3 death4 death5 death6 death7 death8\n$frame death9 death10\n\n$frame deathc1 deathc2 deathc3 deathc4 deathc5 deathc6 deathc7 deathc8\n$frame deathc9 deathc10 deathc11\n\n$frame load1 load2 load3 load4 load5 load6 load7 load8 load9 load10 load11\n\n$frame pain1 pain2 pain3 pain4 pain5 pain6\n\n$frame painb1 painb2 painb3 painb4 painb5 painb6 painb7 painb8 painb9 painb10\n$frame painb11 painb12 painb13 painb14\n\n$frame painc1 painc2 painc3 painc4 painc5 painc6 painc7 painc8 painc9 painc10\n$frame painc11 painc12 painc13\n\n$frame run1 run2 run3 run4 run5 run6 run7 run8\n\n$frame shoot1 shoot2 shoot3 shoot4 shoot5 shoot6 shoot7 shoot8 shoot9\n\n$frame prowl_1 prowl_2 prowl_3 prowl_4 prowl_5 prowl_6 prowl_7 prowl_8\n$frame prowl_9 prowl_10 prowl_11 prowl_12 prowl_13 prowl_14 prowl_15 prowl_16\n$frame prowl_17 prowl_18 prowl_19 prowl_20 prowl_21 prowl_22 prowl_23 prowl_24\n\n/*\n==============================================================================\nSOLDIER CODE\n==============================================================================\n*/\n\nvoid() army_fire;\n\nvoid()\tarmy_stand1\t=[\t$stand1,\tarmy_stand2\t] {ai_stand();};\nvoid()\tarmy_stand2\t=[\t$stand2,\tarmy_stand3\t] {ai_stand();};\nvoid()\tarmy_stand3\t=[\t$stand3,\tarmy_stand4\t] {ai_stand();};\nvoid()\tarmy_stand4\t=[\t$stand4,\tarmy_stand5\t] {ai_stand();};\nvoid()\tarmy_stand5\t=[\t$stand5,\tarmy_stand6\t] {ai_stand();};\nvoid()\tarmy_stand6\t=[\t$stand6,\tarmy_stand7\t] {ai_stand();};\nvoid()\tarmy_stand7\t=[\t$stand7,\tarmy_stand8\t] {ai_stand();};\nvoid()\tarmy_stand8\t=[\t$stand8,\tarmy_stand1\t] {ai_stand();};\n\nvoid()\tarmy_walk1\t=[\t$prowl_1,\tarmy_walk2\t] {\nif (random() < 0.2)\n\tsound (self, CHAN_VOICE, \"soldier/idle.wav\", 1, ATTN_IDLE);\nai_walk(1);};\nvoid()\tarmy_walk2\t=[\t$prowl_2,\tarmy_walk3\t] {ai_walk(1);};\nvoid()\tarmy_walk3\t=[\t$prowl_3,\tarmy_walk4\t] {ai_walk(1);};\nvoid()\tarmy_walk4\t=[\t$prowl_4,\tarmy_walk5\t] {ai_walk(1);};\nvoid()\tarmy_walk5\t=[\t$prowl_5,\tarmy_walk6\t] {ai_walk(2);};\nvoid()\tarmy_walk6\t=[\t$prowl_6,\tarmy_walk7\t] {ai_walk(3);};\nvoid()\tarmy_walk7\t=[\t$prowl_7,\tarmy_walk8\t] {ai_walk(4);};\nvoid()\tarmy_walk8\t=[\t$prowl_8,\tarmy_walk9\t] {ai_walk(4);};\nvoid()\tarmy_walk9\t=[\t$prowl_9,\tarmy_walk10\t] {ai_walk(2);};\nvoid()\tarmy_walk10\t=[\t$prowl_10,\tarmy_walk11\t] {ai_walk(2);};\nvoid()\tarmy_walk11\t=[\t$prowl_11,\tarmy_walk12\t] {ai_walk(2);};\nvoid()\tarmy_walk12\t=[\t$prowl_12,\tarmy_walk13\t] {ai_walk(1);};\nvoid()\tarmy_walk13\t=[\t$prowl_13,\tarmy_walk14\t] {ai_walk(0);};\nvoid()\tarmy_walk14\t=[\t$prowl_14,\tarmy_walk15\t] {ai_walk(1);};\nvoid()\tarmy_walk15\t=[\t$prowl_15,\tarmy_walk16\t] {ai_walk(1);};\nvoid()\tarmy_walk16\t=[\t$prowl_16,\tarmy_walk17\t] {ai_walk(1);};\nvoid()\tarmy_walk17\t=[\t$prowl_17,\tarmy_walk18\t] {ai_walk(3);};\nvoid()\tarmy_walk18\t=[\t$prowl_18,\tarmy_walk19\t] {ai_walk(3);};\nvoid()\tarmy_walk19\t=[\t$prowl_19,\tarmy_walk20\t] {ai_walk(3);};\nvoid()\tarmy_walk20\t=[\t$prowl_20,\tarmy_walk21\t] {ai_walk(3);};\nvoid()\tarmy_walk21\t=[\t$prowl_21,\tarmy_walk22\t] {ai_walk(2);};\nvoid()\tarmy_walk22\t=[\t$prowl_22,\tarmy_walk23\t] {ai_walk(1);};\nvoid()\tarmy_walk23\t=[\t$prowl_23,\tarmy_walk24\t] {ai_walk(1);};\nvoid()\tarmy_walk24\t=[\t$prowl_24,\tarmy_walk1\t] {ai_walk(1);};\n\nvoid()\tarmy_run1\t=[\t$run1,\t\tarmy_run2\t] {\nif (random() < 0.2)\n\tsound (self, CHAN_VOICE, \"soldier/idle.wav\", 1, ATTN_IDLE);\nai_run(11);};\nvoid()\tarmy_run2\t=[\t$run2,\t\tarmy_run3\t] {ai_run(15);};\nvoid()\tarmy_run3\t=[\t$run3,\t\tarmy_run4\t] {ai_run(10);};\nvoid()\tarmy_run4\t=[\t$run4,\t\tarmy_run5\t] {ai_run(10);};\nvoid()\tarmy_run5\t=[\t$run5,\t\tarmy_run6\t] {ai_run(8);};\nvoid()\tarmy_run6\t=[\t$run6,\t\tarmy_run7\t] {ai_run(15);};\nvoid()\tarmy_run7\t=[\t$run7,\t\tarmy_run8\t] {ai_run(10);};\nvoid()\tarmy_run8\t=[\t$run8,\t\tarmy_run1\t] {ai_run(8);};\n\nvoid()\tarmy_atk1\t=[\t$shoot1,\tarmy_atk2\t] {ai_face();};\nvoid()\tarmy_atk2\t=[\t$shoot2,\tarmy_atk3\t] {ai_face();};\nvoid()\tarmy_atk3\t=[\t$shoot3,\tarmy_atk4\t] {ai_face();};\nvoid()\tarmy_atk4\t=[\t$shoot4,\tarmy_atk5\t] {ai_face();};\nvoid()\tarmy_atk5\t=[\t$shoot5,\tarmy_atk6\t] {ai_face(); army_fire(); muzzleflash();};\nvoid()\tarmy_atk6\t=[\t$shoot6,\tarmy_atk7\t] {ai_face();};\nvoid()\tarmy_atk7\t=[\t$shoot7,\tarmy_atk8\t] {ai_face();SUB_CheckRefire (army_atk1);};\nvoid()\tarmy_atk8\t=[\t$shoot8,\tarmy_atk9\t] {ai_face();};\nvoid()\tarmy_atk9\t=[\t$shoot9,\tarmy_run1\t] {ai_face();};\n\n\nvoid()\tarmy_pain1\t=[\t$pain1,\t\tarmy_pain2\t] {};\nvoid()\tarmy_pain2\t=[\t$pain2,\t\tarmy_pain3\t] {};\nvoid()\tarmy_pain3\t=[\t$pain3,\t\tarmy_pain4\t] {};\nvoid()\tarmy_pain4\t=[\t$pain4,\t\tarmy_pain5\t] {};\nvoid()\tarmy_pain5\t=[\t$pain5,\t\tarmy_pain6\t] {};\nvoid()\tarmy_pain6\t=[\t$pain6,\t\tarmy_run1\t] {ai_pain(1);};\n\nvoid()\tarmy_painb1\t=[\t$painb1,\tarmy_painb2\t] {};\nvoid()\tarmy_painb2\t=[\t$painb2,\tarmy_painb3\t] {ai_painforward(13);};\nvoid()\tarmy_painb3\t=[\t$painb3,\tarmy_painb4\t] {ai_painforward(9);};\nvoid()\tarmy_painb4\t=[\t$painb4,\tarmy_painb5\t] {};\nvoid()\tarmy_painb5\t=[\t$painb5,\tarmy_painb6\t] {};\nvoid()\tarmy_painb6\t=[\t$painb6,\tarmy_painb7\t] {};\nvoid()\tarmy_painb7\t=[\t$painb7,\tarmy_painb8\t] {};\nvoid()\tarmy_painb8\t=[\t$painb8,\tarmy_painb9\t] {};\nvoid()\tarmy_painb9\t=[\t$painb9,\tarmy_painb10] {};\nvoid()\tarmy_painb10=[\t$painb10,\tarmy_painb11] {};\nvoid()\tarmy_painb11=[\t$painb11,\tarmy_painb12] {};\nvoid()\tarmy_painb12=[\t$painb12,\tarmy_painb13] {ai_pain(2);};\nvoid()\tarmy_painb13=[\t$painb13,\tarmy_painb14] {};\nvoid()\tarmy_painb14=[\t$painb14,\tarmy_run1\t] {};\n\nvoid()\tarmy_painc1\t=[\t$painc1,\tarmy_painc2\t] {};\nvoid()\tarmy_painc2\t=[\t$painc2,\tarmy_painc3\t] {ai_pain(1);};\nvoid()\tarmy_painc3\t=[\t$painc3,\tarmy_painc4\t] {};\nvoid()\tarmy_painc4\t=[\t$painc4,\tarmy_painc5\t] {};\nvoid()\tarmy_painc5\t=[\t$painc5,\tarmy_painc6\t] {ai_painforward(1);};\nvoid()\tarmy_painc6\t=[\t$painc6,\tarmy_painc7\t] {ai_painforward(1);};\nvoid()\tarmy_painc7\t=[\t$painc7,\tarmy_painc8\t] {};\nvoid()\tarmy_painc8\t=[\t$painc8,\tarmy_painc9\t] {ai_pain(1);};\nvoid()\tarmy_painc9\t=[\t$painc9,\tarmy_painc10] {ai_painforward(4);};\nvoid()\tarmy_painc10=[\t$painc10,\tarmy_painc11] {ai_painforward(3);};\nvoid()\tarmy_painc11=[\t$painc11,\tarmy_painc12] {ai_painforward(6);};\nvoid()\tarmy_painc12=[\t$painc12,\tarmy_painc13] {ai_painforward(8);};\nvoid()\tarmy_painc13=[\t$painc13,\tarmy_run1] {};\n\nvoid(entity attacker, float damage)\tarmy_pain =\n{\n\tlocal float r;\n\t\n\tif (self.pain_finished > time)\n\t\treturn;\n\n\tr = random();\n\n\tif (r < 0.2)\n\t{\n\t\tself.pain_finished = time + 0.6;\n\t\tarmy_pain1 ();\n\t\tsound (self, CHAN_VOICE, \"soldier/pain1.wav\", 1, ATTN_NORM);\n\t}\n\telse if (r < 0.6)\n\t{\n\t\tself.pain_finished = time + 1.1;\n\t\tarmy_painb1 ();\n\t\tsound (self, CHAN_VOICE, \"soldier/pain2.wav\", 1, ATTN_NORM);\n\t}\n\telse\n\t{\n\t\tself.pain_finished = time + 1.1;\n\t\tarmy_painc1 ();\n\t\tsound (self, CHAN_VOICE, \"soldier/pain2.wav\", 1, ATTN_NORM);\n\t}\n};\n\n\nvoid() army_fire =\n{\n\tlocal\tvector\tdir;\n\tlocal\tentity\ten;\n\t\n\tai_face();\n\t\n\tsound (self, CHAN_WEAPON, \"soldier/sattck1.wav\", 1, ATTN_NORM);\t\n\n// fire somewhat behind the player, so a dodging player is harder to hit\n\ten = self.enemy;\n\t\n\tdir = en.origin - en.velocity*0.2;\n\tdir = normalize (dir - self.origin);\n\t\n\tFireBullets (4, dir, '0.1 0.1 0', MOD_SOLDIER);\n};\n\n\n\nvoid()\tarmy_die1\t=[\t$death1,\tarmy_die2\t] {};\nvoid()\tarmy_die2\t=[\t$death2,\tarmy_die3\t] {};\nvoid()\tarmy_die3\t=[\t$death3,\tarmy_die4\t] {self.solid = SOLID_NOT;DropBackpack();};\nvoid()\tarmy_die4\t=[\t$death4,\tarmy_die5\t] {};\nvoid()\tarmy_die5\t=[\t$death5,\tarmy_die6\t] {};\nvoid()\tarmy_die6\t=[\t$death6,\tarmy_die7\t] {};\nvoid()\tarmy_die7\t=[\t$death7,\tarmy_die8\t] {};\nvoid()\tarmy_die8\t=[\t$death8,\tarmy_die9\t] {};\nvoid()\tarmy_die9\t=[\t$death9,\tarmy_die10\t] {};\nvoid()\tarmy_die10\t=[\t$death10,\tarmy_die10\t] {};\n\nvoid()\tarmy_cdie1\t=[\t$deathc1,\tarmy_cdie2\t] {};\nvoid()\tarmy_cdie2\t=[\t$deathc2,\tarmy_cdie3\t] {ai_back(5);};\nvoid()\tarmy_cdie3\t=[\t$deathc3,\tarmy_cdie4\t] {self.solid = SOLID_NOT;DropBackpack();ai_back(4);};\nvoid()\tarmy_cdie4\t=[\t$deathc4,\tarmy_cdie5\t] {ai_back(13);};\nvoid()\tarmy_cdie5\t=[\t$deathc5,\tarmy_cdie6\t] {ai_back(3);};\nvoid()\tarmy_cdie6\t=[\t$deathc6,\tarmy_cdie7\t] {ai_back(4);};\nvoid()\tarmy_cdie7\t=[\t$deathc7,\tarmy_cdie8\t] {};\nvoid()\tarmy_cdie8\t=[\t$deathc8,\tarmy_cdie9\t] {};\nvoid()\tarmy_cdie9\t=[\t$deathc9,\tarmy_cdie10\t] {};\nvoid()\tarmy_cdie10\t=[\t$deathc10,\tarmy_cdie11\t] {};\nvoid()\tarmy_cdie11\t=[\t$deathc11,\tarmy_cdie11\t] {};\n\n\nvoid() army_die =\n{\n// check for gib\n\tif (self.health < -35)\n\t{\n\t\tsound (self, CHAN_VOICE, \"player/udeath.wav\", 1, ATTN_NORM);\n\t\tThrowHead (\"progs/h_guard.mdl\", self.health);\n\t\tThrowGib (\"progs/gib1.mdl\", self.health);\n\t\tThrowGib (\"progs/gib2.mdl\", self.health);\n\t\tThrowGib (\"progs/gib3.mdl\", self.health);\n\t\treturn;\n\t}\n\n// regular death\n\tsound (self, CHAN_VOICE, \"soldier/death1.wav\", 1, ATTN_NORM);\n\tif (random() < 0.5)\n\t\tarmy_die1 ();\n\telse\n\t\tarmy_cdie1 ();\n};\n\n\n/*QUAKED monster_army (1 0 0) (-16 -16 -24) (16 16 40) Ambush\n*/\nvoid() monster_army =\n{\t\n\tif (deathmatch)\n\t{\n\t\tremove(self);\n\t\treturn;\n\t}\n\tprecache_model (\"progs/soldier.mdl\");\n\tprecache_model (\"progs/h_guard.mdl\");\n\tprecache_model (\"progs/gib1.mdl\");\n\tprecache_model (\"progs/gib2.mdl\");\n\tprecache_model (\"progs/gib3.mdl\");\n\n\tprecache_sound (\"soldier/death1.wav\");\n\tprecache_sound (\"soldier/idle.wav\");\n\tprecache_sound (\"soldier/pain1.wav\");\n\tprecache_sound (\"soldier/pain2.wav\");\n\tprecache_sound (\"soldier/sattck1.wav\");\n\tprecache_sound (\"soldier/sight1.wav\");\n\n\tprecache_sound (\"player/udeath.wav\");\t\t// gib death\n\n\tself.solid = SOLID_SLIDEBOX;\n\tself.movetype = MOVETYPE_STEP;\n\n\tsetmodel (self, \"progs/soldier.mdl\");\n\n\tsetsize (self, '-16 -16 -24', '16 16 40');\n\tself.health = 30;\n\n\tself.th_stand = army_stand1;\n\tself.th_walk = army_walk1;\n\tself.th_run = army_run1;\n\tself.th_missile = army_atk1;\n\tself.th_pain = army_pain;\n\tself.th_die = army_die;\n\n\tself.ammo_shells_real = 5; // drop 5 shells on death\n\n\twalkmonster_start ();\n};\n"
  },
  {
    "path": "quakec/basemod/spectate.qc",
    "content": "// Spectator functions\n// Added Aug11'97 by Zoid <zoid@idsoftware.com>\n//\n// These functions are called from the server if they exist.\n// Note that Spectators only have one think since they movement code doesn't\n// track them much.  Impulse commands work as usual, but don't call\n// the regular ImpulseCommand handler in weapons.qc since Spectators don't\n// have any weapons and things can explode.\n//\n//   --- Zoid.\n\n#ifndef NETQUAKE\n\n/*\n===========\nSpectatorConnect\n\ncalled when a spectator connects to a server\n============\n*/\nvoid() SpectatorConnect =\n{\n\tbprint3 (PRINT_MEDIUM, \"Spectator \", self.netname, \" entered the game\\n\");\n\n\tself.goalentity = world; // used for impulse 1 below\n};\n\n/*\n===========\nSpectatorDisconnect\n\ncalled when a spectator disconnects from a server\n============\n*/\nvoid() SpectatorDisconnect =\n{\n\tbprint3 (PRINT_MEDIUM, \"Spectator \", self.netname, \" left the game\\n\");\n};\n\n/*\n================\nSpectatorImpulseCommand\n\nCalled by SpectatorThink if the spectator entered an impulse\n================\n*/\nvoid() SpectatorImpulseCommand =\n{\n\tif (self.impulse == 1) {\n\t\t// teleport the spectator to the next spawn point\n\t\t// note that if the spectator is tracking, this doesn't do\n\t\t// much\n\t\tself.goalentity = find(self.goalentity, classname, \"info_player_deathmatch\");\n\t\tif (self.goalentity == world)\n\t\t\tself.goalentity = find(self.goalentity, classname, \"info_player_deathmatch\");\n\t\tif (self.goalentity != world) {\n\t\t\tsetorigin(self, self.goalentity.origin);\n\t\t\tself.angles = self.goalentity.angles;\n\t\t\tself.fixangle = TRUE;           // turn this way immediately\n\t\t}\n\t}\n\n\tself.impulse = 0;\n};\n\n/*\n================\nSpectatorThink\n\nCalled every frame after physics are run\n================\n*/\nvoid() SpectatorThink =\n{\n\t// self.origin, etc contains spectator position, so you could\n\t// do some neat stuff here\n\n\tif (self.impulse)\n\t\tSpectatorImpulseCommand();\n};\n\n#endif\n\n"
  },
  {
    "path": "quakec/basemod/sprites.qc",
    "content": "\n// these are the only sprites still in the game...\n\n$spritename s_explod\n$type vp_parallel\n$load /raid/quake/id1/gfx/sprites/explod03.lbm\n$frame\t24\t24\t56\t56\n$frame\t120\t24\t56\t56\n$frame\t216\t24\t56\t56\n$frame\t24\t88\t56\t56\n$frame\t120\t88\t56\t56\n$frame\t216\t88\t56\t56\n\n\n$spritename s_bubble\n$type vp_parallel\n$load /raid/quake/id1/gfx/sprites/bubble.lbm\n$frame\t16\t16\t16\t16\n$frame\t40\t16\t16\t16\n\n\n$spritename s_light\n$type vp_parallel\n$load /raid/quake/id1/gfx/sprites/light.lbm\n$frame\t104\t32\t32\t32\n\n"
  },
  {
    "path": "quakec/basemod/tarbaby.qc",
    "content": "/*\n==============================================================================\n\nBLOB\n\n==============================================================================\n*/\n\n$cd id1/models/tarbaby\n$origin 0 0 24\n$base base\t\t\n\n$skin skin\n\n$frame walk1 walk2 walk3 walk4  walk5 walk6 walk7 walk8 walk9 walk10\n$frame walk11 walk12 walk13 walk14 walk15 walk16 walk17 walk18 walk19\n$frame walk20 walk21 walk22 walk23 walk24 walk25\n\n$frame run1 run2 run3 run4 run5 run6  run7 run8 run9 run10 run11 run12 run13\n$frame run14 run15 run16 run17 run18 run19 run20 run21 run22 run23\n$frame run24 run25\n\n$frame jump1 jump2 jump3 jump4 jump5 jump6\n\n$frame fly1 fly2 fly3 fly4\n\n$frame exp\n\nvoid()\ttbaby_stand1\t=[\t$walk1,\t\ttbaby_stand1\t] {ai_stand();};\n\nvoid()\ttbaby_hang1\t\t=[\t$walk1,\t\ttbaby_hang1\t] {ai_stand();};\n\nvoid()\ttbaby_walk1\t=[\t$walk1,\t\ttbaby_walk2\t] {ai_turn();};\nvoid()\ttbaby_walk2\t=[\t$walk2,\t\ttbaby_walk3\t] {ai_turn();};\nvoid()\ttbaby_walk3\t=[\t$walk3,\t\ttbaby_walk4\t] {ai_turn();};\nvoid()\ttbaby_walk4\t=[\t$walk4,\t\ttbaby_walk5\t] {ai_turn();};\nvoid()\ttbaby_walk5\t=[\t$walk5,\t\ttbaby_walk6\t] {ai_turn();};\nvoid()\ttbaby_walk6\t=[\t$walk6,\t\ttbaby_walk7\t] {ai_turn();};\nvoid()\ttbaby_walk7\t=[\t$walk7,\t\ttbaby_walk8\t] {ai_turn();};\nvoid()\ttbaby_walk8\t=[\t$walk8,\t\ttbaby_walk9\t] {ai_turn();};\nvoid()\ttbaby_walk9\t=[\t$walk9,\t\ttbaby_walk10\t] {ai_turn();};\nvoid()\ttbaby_walk10\t=[\t$walk10,\t\ttbaby_walk11\t] {ai_turn();};\nvoid()\ttbaby_walk11\t=[\t$walk11,\t\ttbaby_walk12\t] {ai_walk(2);};\nvoid()\ttbaby_walk12\t=[\t$walk12,\t\ttbaby_walk13\t] {ai_walk(2);};\nvoid()\ttbaby_walk13\t=[\t$walk13,\t\ttbaby_walk14\t] {ai_walk(2);};\nvoid()\ttbaby_walk14\t=[\t$walk14,\t\ttbaby_walk15\t] {ai_walk(2);};\nvoid()\ttbaby_walk15\t=[\t$walk15,\t\ttbaby_walk16\t] {ai_walk(2);};\nvoid()\ttbaby_walk16\t=[\t$walk16,\t\ttbaby_walk17\t] {ai_walk(2);};\nvoid()\ttbaby_walk17\t=[\t$walk17,\t\ttbaby_walk18\t] {ai_walk(2);};\nvoid()\ttbaby_walk18\t=[\t$walk18,\t\ttbaby_walk19\t] {ai_walk(2);};\nvoid()\ttbaby_walk19\t=[\t$walk19,\t\ttbaby_walk20\t] {ai_walk(2);};\nvoid()\ttbaby_walk20\t=[\t$walk20,\t\ttbaby_walk21\t] {ai_walk(2);};\nvoid()\ttbaby_walk21\t=[\t$walk21,\t\ttbaby_walk22\t] {ai_walk(2);};\nvoid()\ttbaby_walk22\t=[\t$walk22,\t\ttbaby_walk23\t] {ai_walk(2);};\nvoid()\ttbaby_walk23\t=[\t$walk23,\t\ttbaby_walk24\t] {ai_walk(2);};\nvoid()\ttbaby_walk24\t=[\t$walk24,\t\ttbaby_walk25\t] {ai_walk(2);};\nvoid()\ttbaby_walk25\t=[\t$walk25,\t\ttbaby_walk1\t] {ai_walk(2);};\n\nvoid()\ttbaby_run1\t=[\t$run1,\t\ttbaby_run2\t] {ai_face();};\nvoid()\ttbaby_run2\t=[\t$run2,\t\ttbaby_run3\t] {ai_face();};\nvoid()\ttbaby_run3\t=[\t$run3,\t\ttbaby_run4\t] {ai_face();};\nvoid()\ttbaby_run4\t=[\t$run4,\t\ttbaby_run5\t] {ai_face();};\nvoid()\ttbaby_run5\t=[\t$run5,\t\ttbaby_run6\t] {ai_face();};\nvoid()\ttbaby_run6\t=[\t$run6,\t\ttbaby_run7\t] {ai_face();};\nvoid()\ttbaby_run7\t=[\t$run7,\t\ttbaby_run8\t] {ai_face();};\nvoid()\ttbaby_run8\t=[\t$run8,\t\ttbaby_run9\t] {ai_face();};\nvoid()\ttbaby_run9\t=[\t$run9,\t\ttbaby_run10\t] {ai_face();};\nvoid()\ttbaby_run10\t=[\t$run10,\t\ttbaby_run11\t] {ai_face();};\nvoid()\ttbaby_run11\t=[\t$run11,\t\ttbaby_run12\t] {ai_run(2);};\nvoid()\ttbaby_run12\t=[\t$run12,\t\ttbaby_run13\t] {ai_run(2);};\nvoid()\ttbaby_run13\t=[\t$run13,\t\ttbaby_run14\t] {ai_run(2);};\nvoid()\ttbaby_run14\t=[\t$run14,\t\ttbaby_run15\t] {ai_run(2);};\nvoid()\ttbaby_run15\t=[\t$run15,\t\ttbaby_run16\t] {ai_run(2);};\nvoid()\ttbaby_run16\t=[\t$run16,\t\ttbaby_run17\t] {ai_run(2);};\nvoid()\ttbaby_run17\t=[\t$run17,\t\ttbaby_run18\t] {ai_run(2);};\nvoid()\ttbaby_run18\t=[\t$run18,\t\ttbaby_run19\t] {ai_run(2);};\nvoid()\ttbaby_run19\t=[\t$run19,\t\ttbaby_run20\t] {ai_run(2);};\nvoid()\ttbaby_run20\t=[\t$run20,\t\ttbaby_run21\t] {ai_run(2);};\nvoid()\ttbaby_run21\t=[\t$run21,\t\ttbaby_run22\t] {ai_run(2);};\nvoid()\ttbaby_run22\t=[\t$run22,\t\ttbaby_run23\t] {ai_run(2);};\nvoid()\ttbaby_run23\t=[\t$run23,\t\ttbaby_run24\t] {ai_run(2);};\nvoid()\ttbaby_run24\t=[\t$run24,\t\ttbaby_run25\t] {ai_run(2);};\nvoid()\ttbaby_run25\t=[\t$run25,\t\ttbaby_run1\t] {ai_run(2);};\n\n\n//============================================================================\n\n\nvoid()\ttbaby_jump1;\n\nvoid()\tTar_JumpTouch =\n{\n\tlocal\tfloat\tldmg;\n\n\tif (other.takedamage && other.classname != self.classname)\n\t{\n\t\tif ( vlen(self.velocity) > 400 )\n\t\t{\n\t\t\tldmg = 10 + 10*random();\n\t\t\tT_Damage (other, self, self, ldmg, MOD_TARBABY);\t\n\t\t\tsound (self, CHAN_WEAPON, \"blob/hit1.wav\", 1, ATTN_NORM);\n\t\t}\n\t}\n\telse\n\t\tsound (self, CHAN_WEAPON, \"blob/land1.wav\", 1, ATTN_NORM);\n\n\n\tif (!checkbottom(self))\n\t{\n\t\tif (self.flags & FL_ONGROUND)\n\t\t{\t// jump randomly to not get hung up\n\t\t\tself.touch = SUB_Null;\n\t\t\tself.think = tbaby_run1;\n\t\t\tself.movetype = MOVETYPE_STEP;\n\t\t\tself.nextthink = time + 0.1;\n\t\t}\n\t\treturn;\t// not on ground yet\n\t}\n\n\tself.touch = SUB_Null;\n\tself.think = tbaby_jump1;\n\tself.nextthink = time + 0.1;\n};\n\nvoid()\ttbaby_jump5;\n\nvoid()\ttbaby_fly1\t\t=[\t$fly1,\ttbaby_fly2\t] {};\nvoid()\ttbaby_fly2\t\t=[\t$fly2,\ttbaby_fly3\t] {};\nvoid()\ttbaby_fly3\t\t=[\t$fly3,\ttbaby_fly4\t] {};\nvoid()\ttbaby_fly4\t\t=[\t$fly4,\ttbaby_fly1\t] {\nself.cnt = self.cnt + 1;\nif (self.cnt == 4)\n{\n//dprint (\"spawn hop\\n\");\ntbaby_jump5 ();\n}\n};\n\nvoid()\ttbaby_jump1\t\t=[\t$jump1,\ttbaby_jump2\t\t] {ai_face();};\nvoid()\ttbaby_jump2\t\t=[\t$jump2,\ttbaby_jump3\t\t] {ai_face();};\nvoid()\ttbaby_jump3\t\t=[\t$jump3,\ttbaby_jump4\t\t] {ai_face();};\nvoid()\ttbaby_jump4\t\t=[\t$jump4,\ttbaby_jump5\t\t] {ai_face();};\nvoid()\ttbaby_jump5\t\t=[\t$jump5,\ttbaby_jump6\t\t]\n{\t\n\tself.movetype = MOVETYPE_BOUNCE;\n\tself.touch = Tar_JumpTouch;\n\tmakevectors (self.angles);\n\tself.origin_z = self.origin_z + 1;\n\tself.velocity = v_forward * 600 + '0 0 200';\n\tself.velocity_z = self.velocity_z + random()*150;\n\tif (self.flags & FL_ONGROUND)\n\t\tself.flags = self.flags - FL_ONGROUND;\n\tself.cnt = 0;\n};\nvoid()\ttbaby_jump6\t=[\t$jump6,tbaby_fly1\t] {};\n\n\n\n//=============================================================================\n\nvoid()\ttbaby_die1\t=[\t$exp,\t\ttbaby_die2\t] {\nself.takedamage = DAMAGE_NO;\n};\nvoid()\ttbaby_die2\t=[\t$exp,\t\ttbaby_run1\t] \n{\n\tT_RadiusDamage (self, self, 120, 160, world, MOD_TARBABY);\n\n\tsound (self, CHAN_VOICE, \"blob/death1.wav\", 1, ATTN_NORM);\n\tself.origin = self.origin - 8*normalize(self.velocity);\n\n\tTE_tarexplosion(self.origin);\n\tCreateExplosion(self.origin);\n\tremove(self);\n};\n\n//=============================================================================\n\n\n/*QUAKED monster_tarbaby (1 0 0) (-16 -16 -24) (16 16 24) Ambush\n*/\nvoid() monster_tarbaby =\n{\n\tif (deathmatch)\n\t{\n\t\tremove(self);\n\t\treturn;\n\t}\n\tprecache_model2 (\"progs/tarbaby.mdl\");\n\n\tprecache_sound2 (\"blob/death1.wav\");\n\tprecache_sound2 (\"blob/hit1.wav\");\n\tprecache_sound2 (\"blob/land1.wav\");\n\tprecache_sound2 (\"blob/sight1.wav\");\n\t\n\tself.solid = SOLID_SLIDEBOX;\n\tself.movetype = MOVETYPE_STEP;\n\n\tsetmodel (self, \"progs/tarbaby.mdl\");\n\n\tsetsize (self, '-16 -16 -24', '16 16 40');\n\tself.health = 80;\n\n\tself.th_stand = tbaby_stand1;\n\tself.th_walk = tbaby_walk1;\n\tself.th_run = tbaby_run1;\n\tself.th_missile = tbaby_jump1;\n\tself.th_melee = tbaby_jump1;\n\tself.th_die = tbaby_die1;\n\t\n\twalkmonster_start ();\n};\n\n"
  },
  {
    "path": "quakec/basemod/triggers.qc",
    "content": "void() trigger_reactivate =\n{\n\tself.solid = SOLID_TRIGGER;\n};\n\n//=============================================================================\n\nfloat\tSPAWNFLAG_NOMESSAGE = 1;\nfloat\tSPAWNFLAG_NOTOUCH = 1;\n\n// the wait time has passed, so set back up for another activation\nvoid() multi_wait =\n{\n\tif (self.max_health)\n\t{\n\t\tself.health = self.max_health;\n\t\tself.takedamage = DAMAGE_YES;\n\t\tself.solid = SOLID_BBOX;\n\t}\n};\n\n\n// the trigger was just touched/killed/used\n// self.enemy should be set to the activator so it can be held through a delay\n// so wait for the delay time before firing\nvoid() multi_trigger =\n{\n\tif (self.nextthink > time)\n\t{\n\t\treturn;\t\t// allready been triggered\n\t}\n\n\tif (self.classname == \"trigger_secret\")\n\t{\n\t\tif (self.enemy.classname != \"player\")\n\t\t\treturn;\n\t\tfound_secrets = found_secrets + 1;\n\t\tWriteByte (MSG_ALL, SVC_FOUNDSECRET);\n\t}\n\n\tif (self.noise)\n\t\tsound (self, CHAN_VOICE, self.noise, 1, ATTN_NORM);\n\n// don't trigger again until reset\n\tself.takedamage = DAMAGE_NO;\n\n\tactivator = self.enemy;\n\t\n\tSUB_UseTargets();\n\n\tif (self.wait > 0)\t\n\t{\n\t\tself.think = multi_wait;\n\t\tself.nextthink = time + self.wait;\n\t}\n\telse\n\t{\t// we can't just remove (self) here, because this is a touch function\n\t\t// called wheil C code is looping through area links...\n\t\tself.touch = SUB_Null;\n\t\tself.nextthink = time + 0.1;\n\t\tself.think = SUB_Remove;\n\t}\n};\n\nvoid() multi_killed =\n{\n\tself.enemy = damage_attacker;\n\tmulti_trigger();\n};\n\nvoid() multi_use =\n{\n\tself.enemy = activator;\n\tmulti_trigger();\n};\n\nvoid() multi_touch =\n{\n\tif (other.classname != \"player\")\n\t\treturn;\n\t\n// if the trigger has an angles field, check player's facing direction\n\tif (self.movedir != '0 0 0')\n\t{\n\t\tmakevectors (other.angles);\n\t\tif (v_forward * self.movedir < 0)\n\t\t\treturn;\t\t// not facing the right way\n\t}\n\t\n\tself.enemy = other;\n\tmulti_trigger ();\n};\n\n/*QUAKED trigger_multiple (.5 .5 .5) ? notouch\nVariable sized repeatable trigger.  Must be targeted at one or more entities.  If \"health\" is set, the trigger must be killed to activate each time.\nIf \"delay\" is set, the trigger waits some time after activating before firing.\n\"wait\" : Seconds between triggerings. (.2 default)\nIf notouch is set, the trigger is only fired by other entities, not by touching.\nNOTOUCH has been obsoleted by trigger_relay!\nsounds\n1)\tsecret\n2)\tbeep beep\n3)\tlarge switch\n4)\nset \"message\" to text string\n*/\nvoid() trigger_multiple =\n{\n\tswitch (self.sounds)\n\t{\n\tcase 1:\n\t\tprecache_sound (\"misc/secret.wav\");\n\t\tself.noise = \"misc/secret.wav\";\n\t\tbreak;\n\tcase 2:\n\t\tprecache_sound (\"misc/talk.wav\");\n\t\tself.noise = \"misc/talk.wav\";\n\t\tbreak;\n\tcase 3:\n\t\tprecache_sound (\"misc/trigger1.wav\");\n\t\tself.noise = \"misc/trigger1.wav\";\n\t\tbreak;\n\t}\n\t\n\tif (!self.wait)\n\t\tself.wait = 0.2;\n\tself.use = multi_use;\n\n\tInitTrigger ();\n\n\tif (self.health)\n\t{\n\t\tif (self.spawnflags & SPAWNFLAG_NOTOUCH)\n\t\t\tobjerror (\"health and notouch don't make sense\\n\");\n\t\tself.max_health = self.health;\n\t\tself.th_die = multi_killed;\n\t\tself.takedamage = DAMAGE_YES;\n\t\tself.solid = SOLID_BBOX;\n\t\tsetorigin (self, self.origin);\t// make sure it links into the world\n\t}\n\telse\n\t{\n\t\tif ( !(self.spawnflags & SPAWNFLAG_NOTOUCH) )\n\t\t{\n\t\t\tself.touch = multi_touch;\n\t\t}\n\t}\n};\n\n\n/*QUAKED trigger_once (.5 .5 .5) ? notouch\nVariable sized trigger. Triggers once, then removes itself.  You must set the key \"target\" to the name of another object in the level that has a matching\n\"targetname\".  If \"health\" is set, the trigger must be killed to activate.\nIf notouch is set, the trigger is only fired by other entities, not by touching.\nif \"killtarget\" is set, any objects that have a matching \"target\" will be removed when the trigger is fired.\nif \"angle\" is set, the trigger will only fire when someone is facing the direction of the angle.  Use \"360\" for an angle of 0.\nsounds\n1)\tsecret\n2)\tbeep beep\n3)\tlarge switch\n4)\nset \"message\" to text string\n*/\nvoid() trigger_once =\n{\n\tself.wait = -1;\n\ttrigger_multiple();\n};\n\n//=============================================================================\n\n/*QUAKED trigger_relay (.5 .5 .5) (-8 -8 -8) (8 8 8)\nThis fixed size trigger cannot be touched, it can only be fired by other events.  It can contain killtargets, targets, delays, and messages.\n*/\nvoid() trigger_relay =\n{\n\tself.use = SUB_UseTargets;\n};\n\n\n//=============================================================================\n\n/*QUAKED trigger_secret (.5 .5 .5) ?\nsecret counter trigger\nsounds\n1)\tsecret\n2)\tbeep beep\n3)\n4)\nset \"message\" to text string\n*/\nvoid() trigger_secret =\n{\n\ttotal_secrets = total_secrets + 1;\n\tself.wait = -1;\n\tif (!self.message)\n\t\tself.message = \"You found a secret area!\";\n\tif (!self.sounds)\n\t\tself.sounds = 1;\n\t\n\tif (self.sounds == 1)\n\t{\n\t\tprecache_sound (\"misc/secret.wav\");\n\t\tself.noise = \"misc/secret.wav\";\n\t}\n\telse if (self.sounds == 2)\n\t{\n\t\tprecache_sound (\"misc/talk.wav\");\n\t\tself.noise = \"misc/talk.wav\";\n\t}\n\n\ttrigger_multiple ();\n};\n\n//=============================================================================\n\n\nvoid() counter_use =\n{\n\tself.count = self.count - 1;\n\tif (self.count < 0)\n\t\treturn;\n\t\n\tif (self.count != 0)\n\t{\n\t\tif (activator.classname == \"player\"\n\t\t&& (self.spawnflags & SPAWNFLAG_NOMESSAGE) == 0)\n\t\t{\n\t\t\tif (self.count >= 4)\n\t\t\t\tcenterprint (activator, \"There are more to go...\");\n\t\t\telse if (self.count == 3)\n\t\t\t\tcenterprint (activator, \"Only 3 more to go...\");\n\t\t\telse if (self.count == 2)\n\t\t\t\tcenterprint (activator, \"Only 2 more to go...\");\n\t\t\telse\n\t\t\t\tcenterprint (activator, \"Only 1 more to go...\");\n\t\t}\n\t\treturn;\n\t}\n\t\n\tif (activator.classname == \"player\"\n\t&& (self.spawnflags & SPAWNFLAG_NOMESSAGE) == 0)\n\t\tcenterprint(activator, \"Sequence completed!\");\n\tself.enemy = activator;\n\tmulti_trigger ();\n};\n\n/*QUAKED trigger_counter (.5 .5 .5) ? nomessage\nActs as an intermediary for an action that takes multiple inputs.\n\nIf nomessage is not set, t will print \"1 more.. \" etc when triggered and \"sequence complete\" when finished.\n\nAfter the counter has been triggered \"count\" times (default 2), it will fire all of it's targets and remove itself.\n*/\nvoid() trigger_counter =\n{\n\tself.wait = -1;\n\tif (!self.count)\n\t\tself.count = 2;\n\n\tself.use = counter_use;\n};\n\n\n/*\n==============================================================================\n\nTELEPORT TRIGGERS\n\n==============================================================================\n*/\n\nfloat\tPLAYER_ONLY\t= 1;\nfloat\tSILENT = 2;\n\nvoid() play_teleport =\n{\n\tlocal\tfloat v;\n\tlocal\tstring tmpstr;\n\n\tv = random();\n\tif (v < 0.2)\n\t\ttmpstr = \"misc/r_tele1.wav\";\n\telse if (v < 0.4)\n\t\ttmpstr = \"misc/r_tele2.wav\";\n\telse if (v < 0.6)\n\t\ttmpstr = \"misc/r_tele3.wav\";\n\telse if (v < 0.8)\n\t\ttmpstr = \"misc/r_tele4.wav\";\n\telse\n\t\ttmpstr = \"misc/r_tele5.wav\";\n\n\tsound (self, CHAN_VOICE, tmpstr, 1, ATTN_NORM);\n\tremove (self);\n};\n\nvoid(vector org) spawn_tfog =\n{\n\tlocal entity s;\n\ts = spawn ();\n\ts.origin = org;\n\ts.nextthink = time + 0.2;\n\ts.think = play_teleport;\n\n\tTE_teleport(org);\n};\n\n\nvoid() tdeath_touch =\n{\n\tif (other == self.owner)\n\t\treturn;\n\n// frag anyone who teleports in on top of an invincible player\n\tif (other.classname == \"player\")\n\t{\n\t\tif (other.invincible_finished > time &&\n\t\t\tself.owner.invincible_finished > time) {\n\t\t\tother.invincible_finished = 0;\n\t\t\tself.owner.invincible_finished = 0;\n\t\t\tT_Damage (other, self, self.owner, 50000, MOD_TELEFRAGDEFLECT);\n\t\t\tT_Damage (self.owner, self, other, 50000, MOD_TELEFRAGDEFLECT);\n\t\t\treturn;\n\t\t}\n\t\t\t\n\t\tif (other.invincible_finished > time)\n\t\t{\n\t\t\tT_Damage (self.owner, self, self.owner, 50000, MOD_TELEFRAGDEFLECT);\n\t\t\treturn;\n\t\t}\n\t\t\n\t}\n\n\tif (other.health)\n\t{\n\t\tT_Damage (other, self, self.owner, 50000, MOD_TELEFRAG);\n\t}\n};\n\n\nvoid(vector org, entity death_owner) spawn_tdeath =\n{\nlocal entity\tdeath;\n\n\tdeath = spawn();\n\tdeath.classname = \"teledeath\";\n\tdeath.movetype = MOVETYPE_NONE;\n\tdeath.solid = SOLID_TRIGGER;\n\tdeath.angles = '0 0 0';\n\tsetsize (death, death_owner.mins - '1 1 1', death_owner.maxs + '1 1 1');\n\tsetorigin (death, org);\n\tdeath.touch = tdeath_touch;\n\tdeath.nextthink = time + 0.2;\n\tdeath.think = SUB_Remove;\n\tdeath.owner = death_owner;\n\t\n\tforce_retouch = 2;\t\t// make sure even still objects get hit\n};\n\nvoid() teleport_touch =\n{\nlocal entity\tt;\nlocal vector\torg;\n\n\tif (self.targetname)\n\t{\n\t\tif (self.nextthink < time)\n\t\t{\n\t\t\treturn;\t\t// not fired yet\n\t\t}\n\t}\n\n\tif (self.spawnflags & PLAYER_ONLY)\n\t{\n\t\tif (other.classname != \"player\")\n\t\t\treturn;\n\t}\n\n// only teleport living creatures\n\tif (other.health <= 0 || other.solid != SOLID_SLIDEBOX)\n\t\treturn;\n\n\tSUB_UseTargets ();\n\n// put a tfog where the player was\n\tspawn_tfog (other.origin);\n\n\tt = find (world, targetname, self.target);\n\tif (!t)\n\t\tobjerror (\"couldn't find target\");\n\t\t\n// spawn a tfog flash in front of the destination\n\tmakevectors (t.mangle);\n\torg = t.origin + 32 * v_forward;\n\n\tspawn_tfog (org);\n\tspawn_tdeath(t.origin, other);\n\n// move the player and lock him down for a little while\n\tif (!other.health)\n\t{\n\t\tother.origin = t.origin;\n\t\tother.velocity = (v_forward * other.velocity_x) + (v_forward * other.velocity_y);\n\t\treturn;\n\t}\n\n\tsetorigin (other, t.origin);\n\tother.angles = t.mangle;\n\tif (other.classname == \"player\")\n\t{\n\t\tother.fixangle = 1;\t\t// turn this way immediately\n\t\tother.teleport_time = time + 0.7;\n\t\tif (other.flags & FL_ONGROUND)\n\t\t\tother.flags = other.flags - FL_ONGROUND;\n\t\tother.velocity = v_forward * 300;\n\t}\n\tother.flags = other.flags - other.flags & FL_ONGROUND;\n};\n\n/*QUAKED info_teleport_destination (.5 .5 .5) (-8 -8 -8) (8 8 32)\nThis is the destination marker for a teleporter.  It should have a \"targetname\" field with the same value as a teleporter's \"target\" field.\n*/\nvoid() info_teleport_destination =\n{\n// this does nothing, just serves as a target spot\n\tself.mangle = self.angles;\n\tself.angles = '0 0 0';\n\tself.model = \"\";\n\tself.origin = self.origin + '0 0 27';\n\tif (!self.targetname)\n\t\tobjerror (\"no targetname\");\n};\n\nvoid() teleport_use =\n{\n\tself.nextthink = time + 0.2;\n\tforce_retouch = 2;\t\t// make sure even still objects get hit\n\tself.think = SUB_Null;\n};\n\n/*QUAKED trigger_teleport (.5 .5 .5) ? PLAYER_ONLY SILENT\nAny object touching this will be transported to the corresponding info_teleport_destination entity. You must set the \"target\" field, and create an object with a \"targetname\" field that matches.\n\nIf the trigger_teleport has a targetname, it will only teleport entities when it has been fired.\n*/\nvoid() trigger_teleport =\n{\n\tlocal vector o;\n\n\tInitTrigger ();\n\tself.touch = teleport_touch;\n\t// find the destination \n\tif (!self.target)\n\t\tobjerror (\"no target\");\n\tself.use = teleport_use;\n\n\tif (!(self.spawnflags & SILENT))\n\t{\n\t\tprecache_sound (\"ambience/hum1.wav\");\n\t\to = (self.mins + self.maxs)*0.5;\n\t\tambientsound (o, \"ambience/hum1.wav\",0.5 , ATTN_STATIC);\n\t}\n};\n\n/*\n==============================================================================\n\ntrigger_setskill\n\n==============================================================================\n*/\n\n/*QUAKED trigger_setskill (.5 .5 .5) ?\nsets skill level to the value of \"message\".\nOnly used on start map.\n*/\nvoid() trigger_setskill =\n{\n\tremove (self);\n};\n\n\n/*\n==============================================================================\n\nONLY REGISTERED TRIGGERS\n\n==============================================================================\n*/\n\nvoid() trigger_onlyregistered_touch =\n{\n\tif (other.classname != \"player\")\n\t\treturn;\n\tif (self.attack_finished > time)\n\t\treturn;\n\n\tself.attack_finished = time + 2;\n\tif (cvar(\"registered\"))\n\t{\n\t\tself.message = \"\";\n\t\tSUB_UseTargets ();\n\t\tremove (self);\n\t}\n\telse\n\t{\n\t\tif (self.message != \"\")\n\t\t{\n\t\t\tcenterprint (other, self.message);\n\t\t\tsound (other, CHAN_BODY, \"misc/talk.wav\", 1, ATTN_NORM);\n\t\t}\n\t}\n};\n\n/*QUAKED trigger_onlyregistered (.5 .5 .5) ?\nOnly fires if playing the registered version, otherwise prints the message\n*/\nvoid() trigger_onlyregistered =\n{\n\tprecache_sound (\"misc/talk.wav\");\n\tInitTrigger ();\n\tself.touch = trigger_onlyregistered_touch;\n};\n\n//============================================================================\n\nvoid() hurt_on =\n{\n\tself.solid = SOLID_TRIGGER;\n\tself.nextthink = -1;\n};\n\nvoid() hurt_touch =\n{\n\tif (other.takedamage)\n\t{\n\t\tself.solid = SOLID_NOT;\n\t\tT_Damage (other, self, self, self.dmg, MOD_HURT);\n\t\tself.think = hurt_on;\n\t\tself.nextthink = time + 1;\n\t}\n\n\treturn;\n};\n\n/*QUAKED trigger_hurt (.5 .5 .5) ?\nAny object touching this will be hurt\nset dmg to damage amount\ndefalt dmg = 5\n*/\nvoid() trigger_hurt =\n{\n\tInitTrigger ();\n\tself.touch = hurt_touch;\n\tif (!self.dmg)\n\t\tself.dmg = 5;\n};\n\n//============================================================================\n\nfloat PUSH_ONCE = 1;\n\nvoid() trigger_push_touch =\n{\n\tif (other.classname == \"grenade\")\n\t\tother.velocity = self.speed * self.movedir * 10;\n\telse if (other.health > 0)\n\t{\n\t\tother.velocity = self.speed * self.movedir * 10;\n\t\tif (other.classname == \"player\")\n\t\t{\n\t\t\tif (other.fly_sound < time)\n\t\t\t{\n\t\t\t\tother.fly_sound = time + 1.5;\n\t\t\t\tsound (other, CHAN_AUTO, \"ambience/windfly.wav\", 1, ATTN_NORM);\n\t\t\t}\n\t\t}\n\t}\n\tif (self.spawnflags & PUSH_ONCE)\n\t\tremove(self);\n};\n\n\n/*QUAKED trigger_push (.5 .5 .5) ? PUSH_ONCE\nPushes the player\n*/\nvoid() trigger_push =\n{\n\tInitTrigger ();\n\tprecache_sound (\"ambience/windfly.wav\");\n\tself.touch = trigger_push_touch;\n\tif (!self.speed)\n\t\tself.speed = 1000;\n};\n\n//============================================================================\n\nvoid() trigger_monsterjump_touch =\n{\n\tif ( other.flags & (FL_MONSTER | FL_FLY | FL_SWIM) != FL_MONSTER )\n\t\treturn;\n\n// set XY even if not on ground, so the jump will clear lips\n\tother.velocity_x = self.movedir_x * self.speed;\n\tother.velocity_y = self.movedir_y * self.speed;\n\t\n\tif ( !(other.flags & FL_ONGROUND) )\n\t\treturn;\n\t\n\tother.flags = other.flags - FL_ONGROUND;\n\n\tother.velocity_z = self.height;\n};\n\n/*QUAKED trigger_monsterjump (.5 .5 .5) ?\nWalking monsters that touch this will jump in the direction of the trigger's angle\n\"speed\" default to 200, the speed thrown forward\n\"height\" default to 200, the speed thrown upwards\n*/\nvoid() trigger_monsterjump =\n{\n\tif (deathmatch)\n\t{\n\t\tremove(self);\n\t\treturn;\n\t}\n\n\tif (!self.speed)\n\t\tself.speed = 200;\n\tif (!self.height)\n\t\tself.height = 200;\n\tif (self.angles == '0 0 0')\n\t\tself.angles = '0 360 0';\n\tInitTrigger ();\n\tself.touch = trigger_monsterjump_touch;\n};\n\n"
  },
  {
    "path": "quakec/basemod/weapons.qc",
    "content": "/*\n*/\nvoid (entity targ, entity inflictor, entity attacker, float damage, INTEGER mod) T_Damage;\nvoid () player_run;\nvoid(entity inflictor, entity attacker, float damage, float radius, entity ignore, INTEGER mod) T_RadiusDamage;\nvoid(vector org, float damage) SpawnBlood;\nvoid() SuperDamageSound;\n\n\n// called by worldspawn\nvoid() W_Precache =\n{\n\tprecache_sound (\"weapons/r_exp3.wav\");  // new rocket explosion\n\tprecache_sound (\"weapons/rocket1i.wav\");        // spike gun\n\tprecache_sound (\"weapons/sgun1.wav\");\n\tprecache_sound (\"weapons/guncock.wav\"); // player shotgun\n\tprecache_sound (\"weapons/ric1.wav\");    // ricochet (used in c code)\n\tprecache_sound (\"weapons/ric2.wav\");    // ricochet (used in c code)\n\tprecache_sound (\"weapons/ric3.wav\");    // ricochet (used in c code)\n\tprecache_sound (\"weapons/spike2.wav\");  // super spikes\n\tprecache_sound (\"weapons/tink1.wav\");   // spikes tink (used in c code)\n\tprecache_sound (\"weapons/grenade.wav\"); // grenade launcher\n\tprecache_sound (\"weapons/bounce.wav\");          // grenade bounce\n\tprecache_sound (\"weapons/shotgn2.wav\"); // super shotgun\n};\n\n#define crandom() (2*(random()-0.5))\n\n/*\nAmmo update functions\n*/\nvoid(entity ent) W_UpdateAmmoCounts =\n{\n\t// update current ammo\n\tswitch (ent.ammo_type)\n\t{\n\tcase AT_SHELLS:\n\t\tent.currentammo = ent.ammo_shells_real;\n\t\tbreak;\n\tcase AT_NAILS:\n\t\tent.currentammo = ent.ammo_nails_real;\n\t\tbreak;\n\tcase AT_ROCKETS:\n\t\tent.currentammo = ent.ammo_rockets_real;\n\t\tbreak;\n\tcase AT_CELLS:\n\t\tent.currentammo = ent.ammo_cells_real;\n\t\tbreak;\n\tdefault:\n\t\tent.currentammo = 0;\n\t}\n\t\n\t// update ammo display (FTE progs converts to floats here)\n\tent.ammo_shells = ent.ammo_shells_real;\n\tent.ammo_nails = ent.ammo_nails_real;\n\tent.ammo_rockets = ent.ammo_rockets_real;\n\tent.ammo_cells = ent.ammo_cells_real;\n};\n\n/*\n================\nW_FireAxe\n================\n*/\nvoid() W_FireAxe =\n{\n\tlocal   vector  source;\n\tlocal   vector  org;\n\n\tmakevectors (self.v_angle);\n\tsource = self.origin + '0 0 16';\n\ttraceline (source, source + v_forward*64, FALSE, self);\n\tif (trace_fraction == 1.0)\n\t\treturn;\n\t\n\torg = trace_endpos - v_forward*4;\n\n\tif (trace_ent.takedamage)\n\t{\n\t\tSpawnBlood (org, 20);\n\t\tif (deathmatch > 3)\n\t\t\tT_Damage (trace_ent, self, self, 75, MOD_AXE);\n\t\telse\n\t\t\tT_Damage (trace_ent, self, self, 20, MOD_AXE);\n\t}\n\telse\n\t{       // hit wall\n\t\tsound (self, CHAN_WEAPON, \"player/axhit2.wav\", 1, ATTN_NORM);\n\n\t\tTE_gunshot(org);\n\t}\n};\n\n\n//============================================================================\n\n/*\n================\nSpawnMeatSpray\n================\n*/\nvoid(vector org, vector vel) SpawnMeatSpray =\n{\n\tlocal   entity missile;\n\n\tmissile = spawn ();\n\tmissile.owner = self;\n\tmissile.movetype = MOVETYPE_BOUNCE;\n\tmissile.solid = SOLID_NOT;\n\n\tmakevectors (self.angles);\n\n\tmissile.velocity = vel;\n\tmissile.velocity_z = missile.velocity_z + 250 + 50*random();\n\n\tmissile.avelocity = '3000 1000 2000';\n\t\n// set missile duration\n\tmissile.nextthink = time + 1;\n\tmissile.think = SUB_Remove;\n\n\tsetmodel (missile, \"progs/zom_gib.mdl\");\n\tsetsize (missile, '0 0 0', '0 0 0');            \n\tsetorigin (missile, org);\n};\n\n/*\n==============================================================================\n\nMULTI-DAMAGE\n\nCollects multiple small damages into a single damage\n\n==============================================================================\n*/\n\nentity  multi_ent;\nfloat   multi_damage;\nINTEGER multi_mod;\n\nvector  blood_org;\nfloat   blood_count;\n\nvector  puff_org;\nfloat   puff_count;\n\nvoid() ClearMultiDamage =\n{\n\tmulti_ent = world;\n\tmulti_damage = 0;\n\tblood_count = 0;\n\tpuff_count = 0;\n\tmulti_mod = MOD_NONE;\n};\n\nvoid() ApplyMultiDamage =\n{\n\tif (!multi_ent)\n\t\treturn;\n\tT_Damage (multi_ent, self, self, multi_damage, multi_mod);\n};\n\nvoid(entity hit, float damage, INTEGER mod) AddMultiDamage =\n{\n\tif (!hit)\n\t\treturn;\n\t\n\tif (hit != multi_ent || mod != multi_mod)\n\t{\n\t\tApplyMultiDamage ();\n\t\tmulti_damage = damage;\n\t\tmulti_ent = hit;\n\t}\n\telse\n\t\tmulti_damage = multi_damage + damage;\n};\n\nvoid() Multi_Finish =\n{\n\tif (puff_count)\n\t\tTE_gunshot(puff_org);\n\n\tif (blood_count)\n\t\tSpawnBlood(blood_org, blood_count);\n};\n\n/*\n==============================================================================\nBULLETS\n==============================================================================\n*/\n\n/*\n================\nTraceAttack\n================\n*/\nvoid(float damage, vector dir, INTEGER mod) TraceAttack =\n{\n\tlocal   vector  vel, org;\n\t\n\tvel = normalize(dir + v_up*crandom() + v_right*crandom());\n\tvel = vel + 2*trace_plane_normal;\n\tvel = vel * 200;\n\n\torg = trace_endpos - dir*4;\n\n\tif (trace_ent.takedamage)\n\t{\n\t\tblood_count = blood_count + 1;\n\t\tblood_org = org;\n\t\tAddMultiDamage (trace_ent, damage, mod);\n\t}\n\telse\n\t{\n\t\tpuff_count = puff_count + 1;\n\t}\n};\n\n/*\n================\nFireBullets\n\nUsed by shotgun, super shotgun, and enemy soldier firing\nGo to the trouble of combining multiple pellets into a single damage call.\n================\n*/\nvoid(float shotcount, vector dir, vector spread, INTEGER mod) FireBullets =\n{\n\tlocal   vector direction;\n\tlocal   vector  src;\n\t\n\tmakevectors(self.v_angle);\n\n\tsrc = self.origin + v_forward*10;\n\tsrc_z = self.absmin_z + self.size_z * 0.7;\n\n\tClearMultiDamage ();\n\n\ttraceline (src, src + dir*2048, FALSE, self);\n\tpuff_org = trace_endpos - dir*4;\n\n\twhile (shotcount > 0)\n\t{\n\t\tdirection = dir + crandom()*spread_x*v_right + crandom()*spread_y*v_up;\n\t\ttraceline (src, src + direction*2048, FALSE, self);\n\t\tif (trace_fraction != 1.0)\n\t\t\tTraceAttack (4, direction, mod);\n\n\t\tshotcount = shotcount - 1;\n\t}\n\tApplyMultiDamage ();\n\tMulti_Finish ();\n};\n\n/*\n================\nW_FireShotgun\n================\n*/\nvoid() W_FireShotgun =\n{\n\tlocal vector dir;\n\n\tsound (self, CHAN_WEAPON, \"weapons/guncock.wav\", 1, ATTN_NORM); \n\n\tVK_smallkick(self);\n\t\n\tif (deathmatch != 4)\n\t{\n\t\tself.ammo_shells_real -= 1;\n\t\tW_UpdateAmmoCounts(self);\n\t}\n\n\tdir = aim (self, 100000);\n\tFireBullets (6, dir, '0.04 0.04 0', MOD_SHOTGUN);\n};\n\n\n/*\n================\nW_FireSuperShotgun\n================\n*/\nvoid() W_FireSuperShotgun =\n{\n\tlocal vector dir;\n\n\tif (self.currentammo == 1)\n\t{\n\t\tW_FireShotgun ();\n\t\treturn;\n\t}\n\t\t\n\tsound (self ,CHAN_WEAPON, \"weapons/shotgn2.wav\", 1, ATTN_NORM); \n\n\tVK_bigkick(self);\n\t\n\tif (deathmatch != 4)\n\t{\n\t\tself.ammo_shells_real -= 2;\n\t\tW_UpdateAmmoCounts(self);\n\t}\n\n\tdir = aim (self, 100000);\n\tFireBullets (14, dir, '0.14 0.08 0', MOD_SUPERSHOTGUN);\n};\n\n\n/*\n==============================================================================\n\nROCKETS\n\n==============================================================================\n*/\n\n/*\n================\nW_FireRocket\n================\n*/\nvoid() W_FireRocket =\n{\n\tif (deathmatch != 4)\n\t{\n\t\tself.ammo_rockets_real -= 1;\n\t\tW_UpdateAmmoCounts(self);\n\t}\n\t\n\tsound (self, CHAN_WEAPON, \"weapons/sgun1.wav\", 1, ATTN_NORM);\n\n\tVK_smallkick(self);\n\tPRJ_FireProjectile(self, \n\t\t\"progs/missile.mdl\", \n\t\tself.origin + v_forward*8 + '0 0 16', \n\t\taim(self, 1000) * 1000,\n\t\tPE_EXPLOSION, \n\t\t100+random()*20, \n\t\tMOD_ROCKET, \n\t\t5);\n\tPRJ_SetRadiusDamage(120, 160, MOD_ROCKETRADIUS);\n};\n\n/*\n===============================================================================\nLIGHTNING\n===============================================================================\n*/\n\nvoid(entity from, float damage, INTEGER lmod) LightningHit =\n{\n\tTE_lightningblood(trace_endpos);\n\n\tT_Damage (trace_ent, from, from, damage, lmod);\n};\n\n/*\n=================\nLightningDamage\n=================\n*/\nvoid(vector p1, vector p2, entity from, float damage, INTEGER lmod) LightningDamage =\n{\n\tlocal entity            e1, e2;\n\tlocal vector            f;\n\t\n\tf = p2 - p1;\n\tf = normalize(f);\n\tf_x = 0 - f_y;\n\tf_y = f_x;\n\tf_z = 0;\n\tf = f*16;\n\n\te1 = e2 = world;\n\n\ttraceline (p1, p2, FALSE, self);\n\n\tif (trace_ent.takedamage)\n\t\tLightningHit (from, damage, lmod);\n\te1 = trace_ent;\n\n\ttraceline (p1 + f, p2 + f, FALSE, self);\n\tif (trace_ent != e1 && trace_ent.takedamage)\n\t\tLightningHit (from, damage, lmod);\n\te2 = trace_ent;\n\n\ttraceline (p1 - f, p2 - f, FALSE, self);\n\tif (trace_ent != e1 && trace_ent != e2 && trace_ent.takedamage)\n\t\tLightningHit (from, damage, lmod);\n};\n\n\nvoid() W_FireLightning =\n{\n\tlocal   vector          org;\n\tlocal   float           cells;\n\tlocal   INTEGER\t\texpmod;\n\n\tif (self.ammo_cells_real < 1)\n\t{\n\t\tW_WeaponSwitch (W_BestWeapon ());\n\t\treturn;\n\t}\n\n// explode if under water\n\tif (self.waterlevel > 1)\n\t{\n\t\tif (deathmatch > 3)\n\t\t{\n\t\t\tif (random() <= 0.5)\n\t\t\t{\n\t\t\t\tT_Damage (self, self, self.owner, 4000, MOD_SELFWATER);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\tcells = self.ammo_cells_real;\n\t\tself.ammo_cells_real = 0;\n\t\tW_WeaponSwitch (W_BestWeapon ());\n\t\texpmod = MOD_SHAFTWATER;\n\t\tif (self.watertype == CONTENT_SLIME)\n\t\t\texpmod = MOD_SHAFTSLIME;\n\t\telse if (self.watertype == CONTENT_LAVA)\n\t\t\texpmod = MOD_SHAFTLAVA;\n\t\tT_RadiusDamage (self, self, 35*cells, 40+35*cells, world, expmod);\n\t\treturn;\n\t}\n\n\tif (self.lightning_sound < time)\n\t{\n\t\tsound (self, CHAN_WEAPON, \"weapons/lhit.wav\", 1, ATTN_NORM);\n\t\tself.lightning_sound = time + 0.6;\n\t}\n\tVK_smallkick(self);\n\n\tif (deathmatch != 4)\n\t{\n\t\tself.ammo_cells_real -= 1;\n\t\tW_UpdateAmmoCounts(self);\n\t}\n\n\torg = self.origin + '0 0 16';\n\t\n\ttraceline (org, org + v_forward*600, TRUE, self);\n\n\tTE_lightning2(self, org, trace_endpos);\n\n\tLightningDamage (self.origin, trace_endpos + v_forward*4, self, 30, MOD_SHAFT);\n};\n\n/*\n================\nW_FireGrenade\n================\n*/\nvoid() W_FireGrenade =\n{\n\tlocal vector vel;\n\n\tif (deathmatch != 4)\n\t{\n\t\tself.ammo_rockets_real -= 1;\n\t\tW_UpdateAmmoCounts(self);\n\t}\n\t\n\tsound (self, CHAN_WEAPON, \"weapons/grenade.wav\", 1, ATTN_NORM);\n\n\tif (self.v_angle_x)\n\t\tvel = v_forward*600 + v_up * 200 + crandom()*v_right*10 + crandom()*v_up*10;\n\telse\n\t{\n\t\tvel = aim(self, 10000) * 600;\n\t\tvel_z = 200;\n\t}\n\n\tVK_smallkick(self);\n\tPRJ_FireProjectile(self, \"progs/grenade.mdl\", self.origin, vel, PE_EXPLOSIONGROUND, 0, 0, 2.5);\n\tPRJ_SetRadiusDamage(120, 160, MOD_GRENADE);\n\tPRJ_SetBouncyProjectile();\n\n\tif (deathmatch == 4)\n\t{\n\t\tself.attack_finished = time + 1.1;\n\t\tT_Damage (self, self, self.owner, 10, MOD_GRENADE);\n\t}\n};\n\n\n//=============================================================================\nvoid(float ox) W_FireSpikes =\n{\n\tif (self.ammo_nails_real < 1)\n\t{\n\t\tW_WeaponSwitch (W_BestWeapon ());\n\t\treturn;\n\t}\n\n\tsound (self, CHAN_WEAPON, \"weapons/rocket1i.wav\", 1, ATTN_NORM);\n\tif (deathmatch != 4)\n\t{\n\t\tself.ammo_nails_real -= 1;\n\t\tW_UpdateAmmoCounts(self);\n\t}\n\n\tVK_smallkick(self);\n\tPRJ_FireProjectile(self,\n\t\t\"progs/spike.mdl\",\n\t\tself.origin + '0 0 16' + v_right*ox,\n\t\taim(self, 1000) * 1000,\n\t\tPE_SPIKE,\n\t\t9,\n\t\tMOD_SPIKE,\n\t\t6);\n};\n\nvoid() W_FireSuperSpikes =\n{\n\tif (self.ammo_nails_real < 2)\n\t{\n\t\tW_FireSpikes(0);\n\t\treturn;\n\t}\n\t\n\tsound (self, CHAN_WEAPON, \"weapons/spike2.wav\", 1, ATTN_NORM);\n\tif (deathmatch != 4)\n\t{\n\t\tself.ammo_nails_real -= 2;\n\t\tW_UpdateAmmoCounts(self);\n\t}\n\n\tVK_smallkick(self);\n\tPRJ_FireProjectile(self,\n\t\t\"progs/s_spike.mdl\",\n\t\tself.origin + '0 0 16',\n\t\taim(self, 1000) * 1000,\n\t\tPE_SUPERSPIKE,\n\t\t18,\n\t\tMOD_SUPERSPIKE,\n\t\t6);\n};\n\n/*\n===============================================================================\n\nPLAYER WEAPON USE\n\n===============================================================================\n*/\n// different from W_CheckNoAmmo due to SSG/SNG being able to fire 1 shot instead of 2...\nBOOL(float wep) W_HasAmmo =\n{\n\tswitch (wep)\n\t{\n\tcase IT_SHOTGUN:\n\t\treturn self.ammo_shells_real >= 1;\n\tcase IT_SUPER_SHOTGUN:\n\t\treturn self.ammo_shells_real >= 2;\n\tcase IT_NAILGUN:\n\t\treturn self.ammo_nails_real >= 1;\n\tcase IT_SUPER_NAILGUN:\n\t\treturn self.ammo_nails_real >= 2;\n\tcase IT_GRENADE_LAUNCHER:\n\tcase IT_ROCKET_LAUNCHER:\n\t\treturn self.ammo_rockets_real >= 1;\n\tcase IT_LIGHTNING:\n\t\treturn self.ammo_cells_real >= 1;\n\t}\n\n\treturn TRUE;\n};\n\nvoid() W_UpdateWeapon =\n{\n\tplayer_run ();          // get out of any weapon firing states\n\n\tself.items = self.items - ( self.items & (IT_SHELLS | IT_NAILS | IT_ROCKETS | IT_CELLS) );\n\n\tswitch (self.weapon)\n\t{\n\tcase IT_AXE:\n\t\tself.weaponmodel = \"progs/v_axe.mdl\";\n\t\tself.ammo_type = AT_NONE;\n\t\tbreak;\n\tcase IT_SHOTGUN:\n\t\tself.weaponmodel = \"progs/v_shot.mdl\";\n\t\tself.items = self.items | IT_SHELLS;\n\t\tself.ammo_type = AT_SHELLS;\n\t\tbreak;\n\tcase IT_SUPER_SHOTGUN:\n\t\tself.weaponmodel = \"progs/v_shot2.mdl\";\n\t\tself.items = self.items | IT_SHELLS;\n\t\tself.ammo_type = AT_SHELLS;\n\t\tbreak;\n\tcase IT_NAILGUN:\n\t\tself.weaponmodel = \"progs/v_nail.mdl\";\n\t\tself.items = self.items | IT_NAILS;\n\t\tself.ammo_type = AT_NAILS;\n\t\tbreak;\n\tcase IT_SUPER_NAILGUN:\n\t\tself.weaponmodel = \"progs/v_nail2.mdl\";\n\t\tself.items = self.items | IT_NAILS;\n\t\tself.ammo_type = AT_NAILS;\n\t\tbreak;\n\tcase IT_GRENADE_LAUNCHER:\n\t\tself.weaponmodel = \"progs/v_rock.mdl\";\n\t\tself.items = self.items | IT_ROCKETS;\n\t\tself.ammo_type = AT_ROCKETS;\n\t\tbreak;\n\tcase IT_ROCKET_LAUNCHER:\n\t\tself.weaponmodel = \"progs/v_rock2.mdl\";\n\t\tself.items = self.items | IT_ROCKETS;\n\t\tself.ammo_type = AT_ROCKETS;\n\t\tbreak;\n\tcase IT_LIGHTNING:\n\t\tself.weaponmodel = \"progs/v_light.mdl\";\n\t\tself.items = self.items | IT_CELLS;\n\t\tself.ammo_type = AT_CELLS;\n\t\tbreak;\n\tdefault:\n\t\tself.weaponmodel = \"\";\n\t}\n\n\tself.weaponframe = 0;\n};\n\nvoid(float weap) W_WeaponSwitch =\n{\n\t// skip weapon model/ammo_type update if this isn't a new weapon\n\tif (self.weapon != weap)\n\t{\n\t\tself.weapon = weap;\n\t\tW_UpdateWeapon();\n\t}\n\n\t// always update ammo count\n\tW_UpdateAmmoCounts(self);\n};\n\nfloat() W_BestWeapon =\n{\n\tfloat fl;\n\n\tif (self.waterlevel <= 1)\n\t\tfl = IT_LIGHTNING;\n\telse\n\t\tfl = IT_SUPER_NAILGUN;\n\n\twhile (1)\n\t{\n\t\tif ( (self.items & fl) && W_HasAmmo(fl) )\n\t\t\treturn fl;\n\n\t\t// best weapon order\n\t\tswitch (fl)\n\t\t{\n\t\tcase IT_LIGHTNING:\n\t\t\tfl = IT_SUPER_NAILGUN;\n\t\t\tbreak;\n\t\tcase IT_SUPER_NAILGUN:\n\t\t\tfl = IT_SUPER_SHOTGUN;\n\t\t\tbreak;\n\t\tcase IT_SUPER_SHOTGUN:\n\t\t\tfl = IT_NAILGUN;\n\t\t\tbreak;\n\t\tcase IT_NAILGUN:\n\t\t\tfl = IT_SHOTGUN;\n\t\t\tbreak;\n\t\tcase IT_SHOTGUN:\n\t\tdefault:\n\t\t\treturn IT_AXE; // so we don't get an infinite loop with certain engines\n\t\t}\n\t}\n};\n\nBOOL() W_CheckNoAmmo =\n{\n\tif (self.currentammo > 0)\n\t\treturn TRUE;\n\n\tif (self.weapon == IT_AXE)\n\t\treturn TRUE;\n\t\n\tW_WeaponSwitch (W_BestWeapon ());\n\t\n// drop the weapon down\n\treturn FALSE;\n};\n\n/*\n============\nW_Attack\n\nAn attack impulse can be triggered now\n============\n*/\nvoid()\tplayer_axe1;\nvoid()\tplayer_axeb1;\nvoid()\tplayer_axec1;\nvoid()\tplayer_axed1;\nvoid()\tplayer_shot1;\nvoid()\tplayer_nail1;\nvoid()\tplayer_nail2;\nvoid()\tplayer_light1;\nvoid()\tplayer_light2;\nvoid()\tplayer_rocket1;\nvoid()\tmuzzleflash;\n\nvoid() W_Attack =\n{\n\tfloat r;\n\n\tif (!W_CheckNoAmmo ())\n\t\treturn;\n\n\tmakevectors (self.v_angle);                 // calculate forward angle for velocity\n\tself.show_hostile = time + 1;   // wake monsters up\n\n\tif (self.weaponstate == WS_IDLE) // start delay\n\t\tself.delay = time + 0.1;\n\n\t// animations are dealt with here\n\tswitch (self.weapon) \n\t{\n\tcase IT_AXE:\n\t\tr = random();\n\t\tif (r < 0.25)\n\t\t{\n\t\t\tself.weaponframe = 1;\n\t\t\tplayer_axe1 ();\n\t\t}\n\t\telse if (r < 0.5)\n\t\t{\n\t\t\tself.weaponframe = 5;\n\t\t\tplayer_axeb1 ();\n\t\t}\n\t\telse if (r < 0.75)\n\t\t{\n\t\t\tself.weaponframe = 1;\n\t\t\tplayer_axec1 ();\n\t\t}\n\t\telse\n\t\t{\n\t\t\tself.weaponframe = 5;\n\t\t\tplayer_axed1 ();\n\t\t}\n\t\tbreak;\n\tcase IT_NAILGUN:\n\tcase IT_SUPER_NAILGUN:\n\t\tmuzzleflash();\n\t\tif (self.weaponframe == 0)\n\t\t\tself.weaponframe = 1;\n\n\t\tif (self.weaponframe & 1)\n\t\t\tplayer_nail1();\n\t\telse\n\t\t\tplayer_nail2();\n\t\tbreak;\t\t\n\tcase IT_GRENADE_LAUNCHER:\n\tcase IT_ROCKET_LAUNCHER:\n\t\tself.weaponframe = 1;\n\t\tmuzzleflash();\n\t\tplayer_rocket1();\n\t\tbreak;\n\tcase IT_LIGHTNING:\n\t\tmuzzleflash();\n\t\tif (self.weaponframe == 0)\n\t\t{\n\t\t\tsound (self, CHAN_AUTO, \"weapons/lstart.wav\", 1, ATTN_NORM);\n\t\t\tself.weaponframe = 1;\n\t\t}\n\n\t\tif (self.weaponframe & 1)\n\t\t\tplayer_light1();\n\t\telse\n\t\t\tplayer_light2();\n\t\tbreak;\n\tdefault:\n\t\tmuzzleflash();\n\t\tself.weaponframe = 1;\n\t\tplayer_shot1();\n\t}\n\n\tSuperDamageSound();\n\n\t// firing is done here (r is used for round time instead of a temp here)\n\tswitch (self.weapon)\n\t{\n\tcase IT_AXE:\n\t\t// frame handles most of this so skip most of it\n\t\tsound (self, CHAN_WEAPON, \"weapons/ax1.wav\", 1, ATTN_NORM);\n\t\tr = 0.5;\n\t\tbreak;\n\tcase IT_SHOTGUN:\n\t\tW_FireShotgun ();\n\t\tr = 0.5;\n\t\tbreak;\n\tcase IT_SUPER_SHOTGUN:\n\t\tW_FireSuperShotgun ();\n\t\tr = 0.7;\n\t\tbreak;\n\tcase IT_NAILGUN:\n\t\tif (self.weaponstate == WS_FIRING1)\n\t\t{\n\t\t\tW_FireSpikes(-4);\n\t\t\tself.weaponstate = WS_FIRING2;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tW_FireSpikes(4);\n\t\t\tself.weaponstate = WS_FIRING1;\n\t\t}\n\n\t\tr = 0.1;\n\t\tbreak;\n\tcase IT_SUPER_NAILGUN:\n\t\tW_FireSuperSpikes();\n\t\tr = 0.1;\n\t\tbreak;\n\tcase IT_GRENADE_LAUNCHER:\n\t\tW_FireGrenade();\n\t\tr = 0.6;\n\t\tbreak;\n\tcase IT_ROCKET_LAUNCHER:\n\t\tW_FireRocket();\n\t\tr = 0.8;\n\t\tbreak;\n\tcase IT_LIGHTNING:\n\t\tW_FireLightning();\n\t\tr = 0.1;\n\t\tbreak;\n\t}\n\n\tif (self.weaponstate == WS_IDLE)\n\t\tself.weaponstate = WS_FIRING1;\n\n\t// advance attack time\n\tif (self.attack_finished <= time)\n\t\tself.attack_finished = self.attack_finished + r;\n};\n\n/*\n============\nW_ChangeWeapon\n\n============\n*/\nvoid() W_ChangeWeapon =\n{\n\tlocal   float   fl;\n\t\n\tswitch (self.impulse)\n\t{\n\tcase 1:\n\t\tfl = IT_AXE;\n\t\tbreak;\n\tcase 2:\n\t\tfl = IT_SHOTGUN;\n\t\tbreak;\n\tcase 3:\n\t\tfl = IT_SUPER_SHOTGUN;\n\t\tbreak;\n\tcase 4:\n\t\tfl = IT_NAILGUN;\n\t\tbreak;\n\tcase 5:\n\t\tfl = IT_SUPER_NAILGUN;\n\t\tbreak;\n\tcase 6:\n\t\tfl = IT_GRENADE_LAUNCHER;\n\t\tbreak;\n\tcase 7:\n\t\tfl = IT_ROCKET_LAUNCHER;\n\t\tbreak;\n\tcase 8:\n\t\tfl = IT_LIGHTNING;\n\t\tbreak;\n\t}\n\t\n\t\n\tif (!(self.items & fl))\n\t{       // don't have the weapon or the ammo\n\t\tsprint1 (self, PRINT_HIGH, \"no weapon.\\n\");\n\t\treturn;\n\t}\n\t\n\tif (!W_HasAmmo(fl))\n\t{       // don't have the ammo\n\t\tsprint1 (self, PRINT_HIGH, \"not enough ammo.\\n\");\n\t\treturn;\n\t}\n\n//\n// set weapon, set ammo\n//\n\tW_WeaponSwitch (fl);\n};\n\n/*\n============\nCheatCommand\n============\n*/\nvoid() CheatCommand =\n{\n      if (deathmatch || coop)\n\t\treturn;\n\n\tself.ammo_rockets_real = 100;\n\tself.ammo_nails_real = 200;\n\tself.ammo_shells_real = 100;\n\tself.ammo_cells_real = 100;\n\tself.items |= IT_AXE |\n\t\tIT_SHOTGUN |\n\t\tIT_SUPER_SHOTGUN |\n\t\tIT_NAILGUN |\n\t\tIT_SUPER_NAILGUN |\n\t\tIT_GRENADE_LAUNCHER |\n\t\tIT_ROCKET_LAUNCHER |\n\t\tIT_LIGHTNING |\n\t\tIT_KEY1 | IT_KEY2;\n\n\tW_WeaponSwitch (IT_ROCKET_LAUNCHER);\n};\n\n/*\n============\nCycleWeaponCommand\n\nGo to the next weapon with ammo\n============\n*/\nvoid() CycleWeaponCommand =\n{\n\tlocal float w;\n\tw = self.weapon;\n\n\twhile (1)\n\t{\n\t\tswitch (w)\n\t\t{\n\t\tcase IT_LIGHTNING:\n\t\t\tw = IT_AXE;\n\t\t\tbreak;\n\t\tcase IT_AXE:\n\t\t\tw = IT_SHOTGUN;\n\t\t\tbreak;\n\t\tcase IT_SHOTGUN:\n\t\t\tw = IT_SUPER_SHOTGUN;\n\t\t\tbreak;\n\t\tcase IT_SUPER_SHOTGUN:\n\t\t\tw = IT_NAILGUN;\n\t\t\tbreak;\n\t\tcase IT_NAILGUN:\n\t\t\tw = IT_SUPER_NAILGUN;\n\t\t\tbreak;\n\t\tcase IT_SUPER_NAILGUN:\n\t\t\tw = IT_GRENADE_LAUNCHER;\n\t\t\tbreak;\n\t\tcase IT_GRENADE_LAUNCHER:\n\t\t\tw = IT_ROCKET_LAUNCHER;\n\t\t\tbreak;\n\t\tcase IT_ROCKET_LAUNCHER:\n\t\t\tw = IT_LIGHTNING;\n\t\t\tbreak;\n\t\t}\n\t\n\t\tif ( (self.items & w) && W_HasAmmo(w) )\n\t\t{\n\t\t\tW_WeaponSwitch (w);\n\t\t\treturn;\n\t\t}\n\t}\n\n};\n\n\n/*\n============\nCycleWeaponReverseCommand\n\nGo to the prev weapon with ammo\n============\n*/\nvoid() CycleWeaponReverseCommand =\n{\n\tlocal float w;\n\tw = self.weapon;\n\n\twhile (1)\n\t{\n\t\tswitch (w)\n\t\t{\n\t\tcase IT_LIGHTNING:\n\t\t\tw = IT_ROCKET_LAUNCHER;\n\t\t\tbreak;\n\t\tcase IT_ROCKET_LAUNCHER:\n\t\t\tw = IT_GRENADE_LAUNCHER;\n\t\t\tbreak;\n\t\tcase IT_GRENADE_LAUNCHER:\n\t\t\tw = IT_SUPER_NAILGUN;\n\t\t\tbreak;\n\t\tcase IT_SUPER_NAILGUN:\n\t\t\tw = IT_NAILGUN;\n\t\t\tbreak;\n\t\tcase IT_NAILGUN:\n\t\t\tw = IT_SUPER_SHOTGUN;\n\t\t\tbreak;\n\t\tcase IT_SUPER_SHOTGUN:\n\t\t\tw = IT_SHOTGUN;\n\t\t\tbreak;\n\t\tcase IT_SHOTGUN:\n\t\t\tw = IT_AXE;\n\t\t\tbreak;\n\t\tcase IT_AXE:\n\t\t\tw = IT_LIGHTNING;\n\t\t\tbreak;\n\t\t}\n\t\n\t\tif ( (self.items & w) && W_HasAmmo(w) )\n\t\t{\n\t\t\tW_WeaponSwitch (w);\n\t\t\treturn;\n\t\t}\n\t}\n\n};\n\n\n/*\n============\nServerflagsCommand\n\nJust for development\n============\n*/\nvoid() ServerflagsCommand =\n{\n      if (deathmatch || coop)\n\t\treturn;\n\n\tserverflags = serverflags * 2 + 1;\n};\n\n\n/*\n============\nImpulseCommands\n\n============\n*/\nvoid() ImpulseCommands =\n{\n\tswitch (self.impulse) {\n\tcase 1 .. 8:\n\t\tW_ChangeWeapon ();\n\t\tbreak;\n\tcase 9:\n\t\tCheatCommand ();\n\t\tbreak;\n\tcase 10:\n\t\tCycleWeaponCommand ();\n\t\tbreak;\n\tcase 11:\n\t\tServerflagsCommand ();\n\t\tbreak;\n\tcase 12:\n\t\tCycleWeaponReverseCommand ();\n\t\tbreak;\n\t}\n\n\tself.impulse = 0;\n};\n\n/*\n============\nW_HandlePlayerFrame\n\nHandle player weapon model\n============\n*/\nvoid() W_HandlePlayerFrame =\n{\n\tif (!self.weaponframe)\n\t\treturn;\n\n\tif (self.weaponframe_time >= time)\n\t\treturn;\n\n\tswitch (self.weapon)\n\t{\n\tcase IT_AXE:\n\t\t// axe frames can start at 1 or 5\n\t\tself.weaponframe_time = time + 0.1;\n\t\tself.weaponframe = self.weaponframe + 1;\n\t\tif (self.weaponframe == 5)\n\t\t\tself.weaponframe = 0;\n\t\telse if (self.weaponframe > 8)\n\t\t\tself.weaponframe = 0;\n\t\t\n\t\treturn;\n\tcase IT_NAILGUN:\n\tcase IT_SUPER_NAILGUN:\n\t\t// cycle until fire button is released\n\t\tif (self.weaponstate != WS_IDLE)\n \t\t{\n\t\t\tself.weaponframe_time = time + 0.1;\n  \t\t\tself.weaponframe = self.weaponframe + 1;\n  \t\t\tif (self.weaponframe > 8)\n  \t\t\t\tself.weaponframe = 1;\n  \t\t}\n  \t\telse\n  \t\t\tself.weaponframe = 0;\n\n\t\treturn;\n\tcase IT_LIGHTNING:\n\t\t// cycle until fire button is released\n\t\tif (self.weaponstate != WS_IDLE)\n\t\t{\n\t\t\tself.weaponframe_time = time + 0.1;\n\t\t\tself.weaponframe = self.weaponframe + 1;\n\t\t\tif (self.weaponframe > 4)\n\t\t\t\tself.weaponframe = 1;\n\t\t}\n\t\telse\n\t\t\tself.weaponframe = 0;\n\t\treturn;\n\tdefault:\n\t\tself.weaponframe = self.weaponframe + 1;\n\t\tself.weaponframe_time = time + 0.1;\n\t\tif (self.weaponframe > 6)\n\t\t\tself.weaponframe = 0;\n\t}\n};\n\n/*\n============\nW_WeaponFrame\n\nCalled every frame so impulse events can be handled as well as possible\n============\n*/\nvoid() W_WeaponFrame =\n{\n\tlocal INTEGER scount;\n\n\tW_HandlePlayerFrame();\n\n\tif (time < self.attack_finished)\n\t\treturn;\n\n\tif (self.impulse)\n\t\tImpulseCommands ();\n\t\n// check for attack\n\tif (self.button0)\n\t{\n\t\tscount = 0;\n\t\t// play catchup but don't allow more than 4 shots per frame\n\t\twhile (self.attack_finished <= time)\n\t\t{\n\t\t\tif (scount >= 4)\n\t\t\t{\n\t\t\t\tself.attack_finished = time;\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tW_Attack();\n\t\t\tscount++;\n\t\t}\n\t}\n\telse\n\t{\n\t\tself.attack_finished = time;\n\t\tself.weaponstate = WS_IDLE;\n\t}\n};\n\n/*\n========\nSuperDamageSound\n\nPlays sound if needed\n========\n*/\nvoid() SuperDamageSound =\n{\n\tif (self.super_damage_finished > time)\n\t{\n\t\tif (self.super_sound < time)\n\t\t{\n\t\t\tself.super_sound = time + 1;\n\t\t\tsound (self, CHAN_BODY, \"items/damage3.wav\", 1, ATTN_NORM);\n\t\t}\n\t}\n\treturn;\n};\n\n/*\nvoid() testfunction =\n{\n\tlocal vector v;\n\tlocal float a, b, c;\n\n\ta = 2;\n\tb = 4;\n\tc = 6;\n\ta *= 2;\n\tb *= 2;\n\tc *= 2;\n\tv = '2 4 6';\n\tv *= 2;\n\tif (!a && !b && !c)\n\t\treturn;\n\n\tif (!v)\n\t\treturn;\n\n\tv_x = 23;\n\n\tif (self.health && self.ammo_shells && self.ammo_cells)\n\t\treturn;\n};\n*/\n\u0000"
  },
  {
    "path": "quakec/basemod/wizard.qc",
    "content": "/*\n==============================================================================\n\nWIZARD\n\n==============================================================================\n*/\n\n$cd id1/models/a_wizard\n$origin 0 0 24\n$base wizbase\t\n$skin wizbase\n\n$frame hover1 hover2 hover3 hover4 hover5 hover6 hover7 hover8\n$frame hover9 hover10 hover11 hover12 hover13 hover14 hover15\n\n$frame fly1 fly2 fly3 fly4 fly5 fly6 fly7 fly8 fly9 fly10\n$frame fly11 fly12 fly13 fly14\n\n$frame magatt1 magatt2 magatt3 magatt4 magatt5 magatt6 magatt7\n$frame magatt8 magatt9 magatt10 magatt11 magatt12 magatt13\n\n$frame pain1 pain2 pain3 pain4\n\n$frame death1 death2 death3 death4 death5 death6 death7 death8\n\n/*\n==============================================================================\n\nWIZARD\n\nIf the player moves behind cover before the missile is launched, launch it\nat the last visible spot with no velocity leading, in hopes that the player\nwill duck back out and catch it.\n==============================================================================\n*/\n\nvoid() wiz_run1;\nvoid() wiz_side1;\n\n/*\n=================\nWizardCheckAttack\n=================\n*/\nBOOL(float enemy_range) WizardCheckAttack =\n{\n\tlocal vector\tspot1, spot2;\t\n\tlocal entity\ttarg;\n\tlocal float\t\tchance;\n\n\tif (time < self.attack_finished)\n\t\treturn FALSE;\n\n\tif (enemy_range == RANGE_FAR)\n\t{\n\t\tif (self.attack_state != AS_STRAIGHT)\n\t\t{\n\t\t\tself.attack_state = AS_STRAIGHT;\n\t\t\twiz_run1 ();\n\t\t}\n\t\treturn FALSE;\n\t}\n\t\t\n\ttarg = self.enemy;\n\t\n// see if any entities are in the way of the shot\n\tspot1 = self.origin + self.view_ofs;\n\tspot2 = targ.origin + targ.view_ofs;\n\n\ttraceline (spot1, spot2, FALSE, self);\n\n\tif (trace_ent != targ)\n\t{\t// don't have a clear shot, so move to a side\n\t\tif (self.attack_state != AS_STRAIGHT)\n\t\t{\n\t\t\tself.attack_state = AS_STRAIGHT;\n\t\t\twiz_run1 ();\n\t\t}\n\t\treturn FALSE;\n\t}\n\t\t\t\n\tif (enemy_range == RANGE_MELEE)\n\t\tchance = 0.9;\n\telse if (enemy_range == RANGE_NEAR)\n\t\tchance = 0.6;\n\telse if (enemy_range == RANGE_MID)\n\t\tchance = 0.2;\n\telse\n\t\tchance = 0;\n\n\tif (random () < chance)\n\t{\n\t\tself.attack_state = AS_MISSILE;\n\t\treturn TRUE;\n\t}\n\n\tif (enemy_range == RANGE_MID)\n\t{\n\t\tif (self.attack_state != AS_STRAIGHT)\n\t\t{\n\t\t\tself.attack_state = AS_STRAIGHT;\n\t\t\twiz_run1 ();\n\t\t}\n\t}\n\telse\n\t{\n\t\tif (self.attack_state != AS_SLIDING)\n\t\t{\n\t\t\tself.attack_state = AS_SLIDING;\n\t\t\twiz_side1 ();\n\t\t}\n\t}\n\t\n\treturn FALSE;\n};\n\n/*\n=================\nWizardAttackFinished\n=================\n*/\nvoid() WizardAttackFinished =\n{\n\tif (range(self.enemy) >= RANGE_MID || !visible(self.enemy))\n\t{\n\t\tself.attack_state = AS_STRAIGHT;\n\t\tself.think = wiz_run1;\n\t}\n\telse\n\t{\n\t\tself.attack_state = AS_SLIDING;\n\t\tself.think = wiz_side1;\n\t}\n};\n\n/*\n==============================================================================\n\nFAST ATTACKS\n\n==============================================================================\n*/\n\nvoid(float side) Wiz_FastFire =\n{\n\tlocal vector org, vec;\n\n\tmuzzleflash();\n\tsound (self, CHAN_WEAPON, \"wizard/wattack.wav\", 1, ATTN_NORM);\n\n\tmakevectors (self.angles);\t\n\torg = self.origin + '0 0 30' + v_forward*14 + v_right*side*14;\n\tvec = normalize(self.enemy.origin - v_right*side*13 - org) * 600;\n\n\tPRJ_FireProjectile (self, \"progs/w_spike.mdl\", org, vec, PE_WIZSPIKE, 9, MOD_WIZARD, 6);\n};\n\nvoid() Wiz_idlesound =\n{\n\tlocal float wr;\n\n\tif (self.wizardidle < time)\n\t{\n\t\twr = random();\n\n\t \tif (wr > 0.9) \n\t \t\tsound (self, CHAN_VOICE, \"wizard/widle1.wav\", 1,  ATTN_IDLE);\n\t \telse if (wr < 0.3)\n\t \t\tsound (self, CHAN_VOICE, \"wizard/widle2.wav\", 1, ATTN_IDLE);\n\n\t \tself.wizardidle = time + 2;\n\t}\n\n\treturn;\n};\n\nvoid()\twiz_stand1\t=[\t$hover1,\t\twiz_stand2\t] {ai_stand();};\nvoid()\twiz_stand2\t=[\t$hover2,\t\twiz_stand3\t] {ai_stand();};\nvoid()\twiz_stand3\t=[\t$hover3,\t\twiz_stand4\t] {ai_stand();};\nvoid()\twiz_stand4\t=[\t$hover4,\t\twiz_stand5\t] {ai_stand();};\nvoid()\twiz_stand5\t=[\t$hover5,\t\twiz_stand6\t] {ai_stand();};\nvoid()\twiz_stand6\t=[\t$hover6,\t\twiz_stand7\t] {ai_stand();};\nvoid()\twiz_stand7\t=[\t$hover7,\t\twiz_stand8\t] {ai_stand();};\nvoid()\twiz_stand8\t=[\t$hover8,\t\twiz_stand1\t] {ai_stand();};\n\nvoid()\twiz_walk1\t=[\t$hover1,\t\twiz_walk2\t] {ai_walk(8);\nWiz_idlesound();};\nvoid()\twiz_walk2\t=[\t$hover2,\t\twiz_walk3\t] {ai_walk(8);};\nvoid()\twiz_walk3\t=[\t$hover3,\t\twiz_walk4\t] {ai_walk(8);};\nvoid()\twiz_walk4\t=[\t$hover4,\t\twiz_walk5\t] {ai_walk(8);};\nvoid()\twiz_walk5\t=[\t$hover5,\t\twiz_walk6\t] {ai_walk(8);};\nvoid()\twiz_walk6\t=[\t$hover6,\t\twiz_walk7\t] {ai_walk(8);};\nvoid()\twiz_walk7\t=[\t$hover7,\t\twiz_walk8\t] {ai_walk(8);};\nvoid()\twiz_walk8\t=[\t$hover8,\t\twiz_walk1\t] {ai_walk(8);};\n\nvoid()\twiz_side1\t=[\t$hover1,\t\twiz_side2\t] {ai_run(8);\nWiz_idlesound();};\nvoid()\twiz_side2\t=[\t$hover2,\t\twiz_side3\t] {ai_run(8);};\nvoid()\twiz_side3\t=[\t$hover3,\t\twiz_side4\t] {ai_run(8);};\nvoid()\twiz_side4\t=[\t$hover4,\t\twiz_side5\t] {ai_run(8);};\nvoid()\twiz_side5\t=[\t$hover5,\t\twiz_side6\t] {ai_run(8);};\nvoid()\twiz_side6\t=[\t$hover6,\t\twiz_side7\t] {ai_run(8);};\nvoid()\twiz_side7\t=[\t$hover7,\t\twiz_side8\t] {ai_run(8);};\nvoid()\twiz_side8\t=[\t$hover8,\t\twiz_side1\t] {ai_run(8);};\n\nvoid()\twiz_run1\t=[\t$fly1,\t\twiz_run2\t] {ai_run(16);\nWiz_idlesound();\n};\nvoid()\twiz_run2\t=[\t$fly2,\t\twiz_run3\t] {ai_run(16);};\nvoid()\twiz_run3\t=[\t$fly3,\t\twiz_run4\t] {ai_run(16);};\nvoid()\twiz_run4\t=[\t$fly4,\t\twiz_run5\t] {ai_run(16);};\nvoid()\twiz_run5\t=[\t$fly5,\t\twiz_run6\t] {ai_run(16);};\nvoid()\twiz_run6\t=[\t$fly6,\t\twiz_run7\t] {ai_run(16);};\nvoid()\twiz_run7\t=[\t$fly7,\t\twiz_run8\t] {ai_run(16);};\nvoid()\twiz_run8\t=[\t$fly8,\t\twiz_run9\t] {ai_run(16);};\nvoid()\twiz_run9\t=[\t$fly9,\t\twiz_run10\t] {ai_run(16);};\nvoid()\twiz_run10\t=[\t$fly10,\t\twiz_run11\t] {ai_run(16);};\nvoid()\twiz_run11\t=[\t$fly11,\t\twiz_run12\t] {ai_run(16);};\nvoid()\twiz_run12\t=[\t$fly12,\t\twiz_run13\t] {ai_run(16);};\nvoid()\twiz_run13\t=[\t$fly13,\t\twiz_run14\t] {ai_run(16);};\nvoid()\twiz_run14\t=[\t$fly14,\t\twiz_run1\t] {ai_run(16);};\n\nvoid()\twiz_fast1\t=[\t$magatt1,\t\twiz_fast2\t] {ai_face();};\nvoid()\twiz_fast2\t=[\t$magatt2,\t\twiz_fast3\t] {ai_face();};\nvoid()\twiz_fast3\t=[\t$magatt3,\t\twiz_fast4\t] {ai_face();};\nvoid()\twiz_fast4\t=[\t$magatt4,\t\twiz_fast5\t] {ai_face();Wiz_FastFire(-1);};\nvoid()\twiz_fast5\t=[\t$magatt5,\t\twiz_fast6\t] {ai_face();};\nvoid()\twiz_fast6\t=[\t$magatt6,\t\twiz_fast7\t] {ai_face();};\nvoid()\twiz_fast7\t=[\t$magatt5,\t\twiz_fast8\t] {ai_face();};\nvoid()\twiz_fast8\t=[\t$magatt4,\t\twiz_fast9\t] {ai_face();};\nvoid()\twiz_fast9\t=[\t$magatt3,\t\twiz_fast10\t] {ai_face();Wiz_FastFire(1);};\nvoid()\twiz_fast10\t=[\t$magatt2,\t\twiz_run1\t] {ai_face();SUB_AttackFinished(2);WizardAttackFinished ();};\n\nvoid()\twiz_pain1\t=[\t$pain1,\t\twiz_pain2\t] {};\nvoid()\twiz_pain2\t=[\t$pain2,\t\twiz_pain3\t] {};\nvoid()\twiz_pain3\t=[\t$pain3,\t\twiz_pain4\t] {};\nvoid()\twiz_pain4\t=[\t$pain4,\t\twiz_run1\t] {};\n\nvoid()\twiz_death1\t=[\t$death1,\t\twiz_death2\t] {\n\nself.velocity_x = -200 + 400*random();\nself.velocity_y = -200 + 400*random();\nself.velocity_z = 100 + 100*random();\nself.flags = self.flags - (self.flags & FL_ONGROUND);\nsound (self, CHAN_VOICE, \"wizard/wdeath.wav\", 1, ATTN_NORM);\n};\nvoid()\twiz_death2\t=[\t$death2,\t\twiz_death3\t] {};\nvoid()\twiz_death3\t=[\t$death3,\t\twiz_death4\t]{self.solid = SOLID_NOT;};\nvoid()\twiz_death4\t=[\t$death4,\t\twiz_death5\t] {};\nvoid()\twiz_death5\t=[\t$death5,\t\twiz_death6\t] {};\nvoid()\twiz_death6\t=[\t$death6,\t\twiz_death7\t] {};\nvoid()\twiz_death7\t=[\t$death7,\t\twiz_death8\t] {};\nvoid()\twiz_death8\t=[\t$death8,\t\twiz_death8\t] {};\n\nvoid() wiz_die =\n{\n// check for gib\n\tif (self.health < -40)\n\t{\n\t\tsound (self, CHAN_VOICE, \"player/udeath.wav\", 1, ATTN_NORM);\n\t\tThrowHead (\"progs/h_wizard.mdl\", self.health);\n\t\tThrowGib (\"progs/gib2.mdl\", self.health);\n\t\tThrowGib (\"progs/gib2.mdl\", self.health);\n\t\tThrowGib (\"progs/gib2.mdl\", self.health);\n\t\treturn;\n\t}\n\n\twiz_death1 ();\n};\n\n\nvoid(entity attacker, float damage) Wiz_Pain =\n{\n\tsound (self, CHAN_VOICE, \"wizard/wpain.wav\", 1, ATTN_NORM);\n\tif (random()*70 > damage)\n\t\treturn;\t\t// didn't flinch\n\n\twiz_pain1 ();\n};\n\n/*QUAKED monster_wizard (1 0 0) (-16 -16 -24) (16 16 40) Ambush\n*/\nvoid() monster_wizard =\n{\n\tif (deathmatch)\n\t{\n\t\tremove(self);\n\t\treturn;\n\t}\n\tprecache_model (\"progs/wizard.mdl\");\n\tprecache_model (\"progs/h_wizard.mdl\");\n\tprecache_model (\"progs/w_spike.mdl\");\n\n\tprecache_sound (\"wizard/hit.wav\");\t\t// used by c code\n\tprecache_sound (\"wizard/wattack.wav\");\n\tprecache_sound (\"wizard/wdeath.wav\");\n\tprecache_sound (\"wizard/widle1.wav\");\n\tprecache_sound (\"wizard/widle2.wav\");\n\tprecache_sound (\"wizard/wpain.wav\");\n\tprecache_sound (\"wizard/wsight.wav\");\n\n\tself.solid = SOLID_SLIDEBOX;\n\tself.movetype = MOVETYPE_STEP;\n\n\tsetmodel (self, \"progs/wizard.mdl\");\n\n\tsetsize (self, '-16 -16 -24', '16 16 40');\n\tself.health = 80;\n\n\tself.th_stand = wiz_stand1;\n\tself.th_walk = wiz_walk1;\n\tself.th_run = wiz_run1;\n\tself.th_missile = wiz_fast1;\n\tself.th_pain = Wiz_Pain;\n\tself.th_die = wiz_die;\n\t\t\n\tflymonster_start ();\n};\n\u0000"
  },
  {
    "path": "quakec/basemod/world.qc",
    "content": "\nvoid() main =\n{\n// these are just commands the the prog compiler to copy these files\n\n\tprecache_file (\"progs.dat\");\n\tprecache_file (\"gfx.wad\");\n\tprecache_file (\"quake.rc\");\n\tprecache_file (\"default.cfg\");\n\n\tprecache_file (\"end1.bin\");\n\tprecache_file2 (\"end2.bin\");\n\n\tprecache_file (\"demo1.dem\");\n\tprecache_file (\"demo2.dem\");\n\tprecache_file (\"demo3.dem\");\n\n//\n// these are all of the lumps from the cached.ls files\n//\n\tprecache_file (\"gfx/palette.lmp\");\n\tprecache_file (\"gfx/colormap.lmp\");\n\n\tprecache_file2 (\"gfx/pop.lmp\");\n\n\tprecache_file (\"gfx/complete.lmp\");\n\tprecache_file (\"gfx/inter.lmp\");\n\n\tprecache_file (\"gfx/ranking.lmp\");\n\tprecache_file (\"gfx/vidmodes.lmp\");\n\tprecache_file (\"gfx/finale.lmp\");\n\tprecache_file (\"gfx/conback.lmp\");\n\tprecache_file (\"gfx/qplaque.lmp\");\n\n\tprecache_file (\"gfx/menudot1.lmp\");\n\tprecache_file (\"gfx/menudot2.lmp\");\n\tprecache_file (\"gfx/menudot3.lmp\");\n\tprecache_file (\"gfx/menudot4.lmp\");\n\tprecache_file (\"gfx/menudot5.lmp\");\n\tprecache_file (\"gfx/menudot6.lmp\");\n\n\tprecache_file (\"gfx/menuplyr.lmp\");\n\tprecache_file (\"gfx/bigbox.lmp\");\n\tprecache_file (\"gfx/dim_modm.lmp\");\n\tprecache_file (\"gfx/dim_drct.lmp\");\n\tprecache_file (\"gfx/dim_ipx.lmp\");\n\tprecache_file (\"gfx/dim_tcp.lmp\");\n\tprecache_file (\"gfx/dim_mult.lmp\");\n\tprecache_file (\"gfx/mainmenu.lmp\");\n\t\n\tprecache_file (\"gfx/box_tl.lmp\");\n\tprecache_file (\"gfx/box_tm.lmp\");\n\tprecache_file (\"gfx/box_tr.lmp\");\n\t\n\tprecache_file (\"gfx/box_ml.lmp\");\n\tprecache_file (\"gfx/box_mm.lmp\");\n\tprecache_file (\"gfx/box_mm2.lmp\");\n\tprecache_file (\"gfx/box_mr.lmp\");\n\t\n\tprecache_file (\"gfx/box_bl.lmp\");\n\tprecache_file (\"gfx/box_bm.lmp\");\n\tprecache_file (\"gfx/box_br.lmp\");\n\t\n\tprecache_file (\"gfx/sp_menu.lmp\");\n\tprecache_file (\"gfx/ttl_sgl.lmp\");\n\tprecache_file (\"gfx/ttl_main.lmp\");\n\tprecache_file (\"gfx/ttl_cstm.lmp\");\n\t\n\tprecache_file (\"gfx/mp_menu.lmp\");\n\t\n\tprecache_file (\"gfx/netmen1.lmp\");\n\tprecache_file (\"gfx/netmen2.lmp\");\n\tprecache_file (\"gfx/netmen3.lmp\");\n\tprecache_file (\"gfx/netmen4.lmp\");\n\tprecache_file (\"gfx/netmen5.lmp\");\n\t\n\tprecache_file (\"gfx/sell.lmp\");\n\t\n\tprecache_file (\"gfx/help0.lmp\");\n\tprecache_file (\"gfx/help1.lmp\");\n\tprecache_file (\"gfx/help2.lmp\");\n\tprecache_file (\"gfx/help3.lmp\");\n\tprecache_file (\"gfx/help4.lmp\");\n\tprecache_file (\"gfx/help5.lmp\");\n\n\tprecache_file (\"gfx/pause.lmp\");\n\tprecache_file (\"gfx/loading.lmp\");\n\n\tprecache_file (\"gfx/p_option.lmp\");\n\tprecache_file (\"gfx/p_load.lmp\");\n\tprecache_file (\"gfx/p_save.lmp\");\n\tprecache_file (\"gfx/p_multi.lmp\");\n\n// sounds loaded by C code\n\tprecache_sound (\"misc/menu1.wav\");\n\tprecache_sound (\"misc/menu2.wav\");\n\tprecache_sound (\"misc/menu3.wav\");\n\n\tprecache_sound (\"ambience/water1.wav\");\n\tprecache_sound (\"ambience/wind2.wav\");\n\n// shareware\n\tprecache_file (\"maps/start.bsp\");\n\n\tprecache_file (\"maps/e1m1.bsp\");\n\tprecache_file (\"maps/e1m2.bsp\");\n\tprecache_file (\"maps/e1m3.bsp\");\n\tprecache_file (\"maps/e1m4.bsp\");\n\tprecache_file (\"maps/e1m5.bsp\");\n\tprecache_file (\"maps/e1m6.bsp\");\n\tprecache_file (\"maps/e1m7.bsp\");\n\tprecache_file (\"maps/e1m8.bsp\");\n\n// registered\n\tprecache_file2 (\"gfx/pop.lmp\");\n\n\tprecache_file2 (\"maps/e2m1.bsp\");\n\tprecache_file2 (\"maps/e2m2.bsp\");\n\tprecache_file2 (\"maps/e2m3.bsp\");\n\tprecache_file2 (\"maps/e2m4.bsp\");\n\tprecache_file2 (\"maps/e2m5.bsp\");\n\tprecache_file2 (\"maps/e2m6.bsp\");\n\tprecache_file2 (\"maps/e2m7.bsp\");\n\n\tprecache_file2 (\"maps/e3m1.bsp\");\n\tprecache_file2 (\"maps/e3m2.bsp\");\n\tprecache_file2 (\"maps/e3m3.bsp\");\n\tprecache_file2 (\"maps/e3m4.bsp\");\n\tprecache_file2 (\"maps/e3m5.bsp\");\n\tprecache_file2 (\"maps/e3m6.bsp\");\n\tprecache_file2 (\"maps/e3m7.bsp\");\n\n\tprecache_file2 (\"maps/e4m1.bsp\");\n\tprecache_file2 (\"maps/e4m2.bsp\");\n\tprecache_file2 (\"maps/e4m3.bsp\");\n\tprecache_file2 (\"maps/e4m4.bsp\");\n\tprecache_file2 (\"maps/e4m5.bsp\");\n\tprecache_file2 (\"maps/e4m6.bsp\");\n\tprecache_file2 (\"maps/e4m7.bsp\");\n\tprecache_file2 (\"maps/e4m8.bsp\");\n\n\tprecache_file2 (\"maps/end.bsp\");\n\n\tprecache_file2 (\"maps/dm1.bsp\");\n\tprecache_file2 (\"maps/dm2.bsp\");\n\tprecache_file2 (\"maps/dm3.bsp\");\n\tprecache_file2 (\"maps/dm4.bsp\");\n\tprecache_file2 (\"maps/dm5.bsp\");\n\tprecache_file2 (\"maps/dm6.bsp\");\n};\n\n//=======================\n/*QUAKED worldspawn (0 0 0) ?\nOnly used for the world entity.\nSet message to the level name.\nSet sounds to the cd track to play.\n\nWorld Types:\n0: medieval\n1: metal\n2: base\n*/\n//=======================\nvoid() worldspawn =\n{\n\tlastspawn = world;\n\tspotspawn = 0;\n\n\tENG_Check();\n\n#ifndef NETQUAKE\n// custom map attributes\n\tif (self.model == \"maps/e1m8.bsp\")\n\t\tcvar_set (\"sv_gravity\", \"100\");\n\telse\n\t\tcvar_set (\"sv_gravity\", \"800\");\n#endif\n\n// the area based ambient sounds MUST be the first precache_sounds\n\n// player precaches     \n\tW_Precache ();                  // get weapon precaches\n\n// sounds used from C physics code\n\tprecache_sound (\"demon/dland2.wav\");            // landing thud\n\tprecache_sound (\"misc/h2ohit1.wav\");            // landing splash\n\n// setup precaches allways needed\n\tprecache_sound (\"items/itembk2.wav\");           // item respawn sound\n\tprecache_sound (\"player/plyrjmp8.wav\");         // player jump\n\tprecache_sound (\"player/land.wav\");                     // player landing\n\tprecache_sound (\"player/land2.wav\");            // player hurt landing\n\tprecache_sound (\"player/drown1.wav\");           // drowning pain\n\tprecache_sound (\"player/drown2.wav\");           // drowning pain\n\tprecache_sound (\"player/gasp1.wav\");            // gasping for air\n\tprecache_sound (\"player/gasp2.wav\");            // taking breath\n\tprecache_sound (\"player/h2odeath.wav\");         // drowning death\n\n\tprecache_sound (\"misc/talk.wav\");                       // talk\n\tprecache_sound (\"player/teledth1.wav\");         // telefrag\n\tprecache_sound (\"misc/r_tele1.wav\");            // teleport sounds\n\tprecache_sound (\"misc/r_tele2.wav\");\n\tprecache_sound (\"misc/r_tele3.wav\");\n\tprecache_sound (\"misc/r_tele4.wav\");\n\tprecache_sound (\"misc/r_tele5.wav\");\n\tprecache_sound (\"weapons/lock4.wav\");           // ammo pick up\n\tprecache_sound (\"weapons/pkup.wav\");            // weapon up\n\tprecache_sound (\"items/armor1.wav\");            // armor up\n\tprecache_sound (\"weapons/lhit.wav\");            //lightning\n\tprecache_sound (\"weapons/lstart.wav\");          //lightning start\n\tprecache_sound (\"items/damage3.wav\");\n\n\tprecache_sound (\"misc/power.wav\");                      //lightning for boss\n\n// player gib sounds\n\tprecache_sound (\"player/gib.wav\");                      // player gib sound\n\tprecache_sound (\"player/udeath.wav\");           // player gib sound\n\tprecache_sound (\"player/tornoff2.wav\");         // gib sound\n\n// player pain sounds\n\n\tprecache_sound (\"player/pain1.wav\");\n\tprecache_sound (\"player/pain2.wav\");\n\tprecache_sound (\"player/pain3.wav\");\n\tprecache_sound (\"player/pain4.wav\");\n\tprecache_sound (\"player/pain5.wav\");\n\tprecache_sound (\"player/pain6.wav\");\n\n// player death sounds\n\tprecache_sound (\"player/death1.wav\");\n\tprecache_sound (\"player/death2.wav\");\n\tprecache_sound (\"player/death3.wav\");\n\tprecache_sound (\"player/death4.wav\");\n\tprecache_sound (\"player/death5.wav\");\n\n\tprecache_sound (\"boss1/sight1.wav\");\n\n// ax sounds    \n\tprecache_sound (\"weapons/ax1.wav\");                     // ax swoosh\n\tprecache_sound (\"player/axhit1.wav\");           // ax hit meat\n\tprecache_sound (\"player/axhit2.wav\");           // ax hit world\n\n\tprecache_sound (\"player/h2ojump.wav\");          // player jumping into water\n\tprecache_sound (\"player/slimbrn2.wav\");         // player enter slime\n\tprecache_sound (\"player/inh2o.wav\");            // player enter water\n\tprecache_sound (\"player/inlava.wav\");           // player enter lava\n\tprecache_sound (\"misc/outwater.wav\");           // leaving water sound\n\n\tprecache_sound (\"player/lburn1.wav\");           // lava burn\n\tprecache_sound (\"player/lburn2.wav\");           // lava burn\n\n\tprecache_sound (\"misc/water1.wav\");                     // swimming\n\tprecache_sound (\"misc/water2.wav\");                     // swimming\n\n// Invulnerability sounds\n\tprecache_sound (\"items/protect.wav\");\n\tprecache_sound (\"items/protect2.wav\");\n\tprecache_sound (\"items/protect3.wav\");\n\n\n\tprecache_model (\"progs/player.mdl\");\n\tprecache_model (\"progs/eyes.mdl\");\n\tprecache_model (\"progs/h_player.mdl\");\n\tprecache_model (\"progs/gib1.mdl\");\n\tprecache_model (\"progs/gib2.mdl\");\n\tprecache_model (\"progs/gib3.mdl\");\n\n\tprecache_model (\"progs/s_bubble.spr\");  // drowning bubbles\n\tprecache_model (\"progs/s_explod.spr\");  // sprite explosion\n\n\tprecache_model (\"progs/v_axe.mdl\");\n\tprecache_model (\"progs/v_shot.mdl\");\n\tprecache_model (\"progs/v_nail.mdl\");\n\tprecache_model (\"progs/v_rock.mdl\");\n\tprecache_model (\"progs/v_shot2.mdl\");\n\tprecache_model (\"progs/v_nail2.mdl\");\n\tprecache_model (\"progs/v_rock2.mdl\");\n\n\tprecache_model (\"progs/bolt.mdl\");              // for lightning gun\n\tprecache_model (\"progs/bolt2.mdl\");             // for lightning gun\n\tprecache_model (\"progs/bolt3.mdl\");             // for boss shock\n\tprecache_model (\"progs/lavaball.mdl\");  // for testing\n\t\n\tprecache_model (\"progs/missile.mdl\");\n\tprecache_model (\"progs/grenade.mdl\");\n\tprecache_model (\"progs/spike.mdl\");\n\tprecache_model (\"progs/s_spike.mdl\");\n\n\tprecache_model (\"progs/backpack.mdl\");\n\n\tprecache_model (\"progs/zom_gib.mdl\");\n\n\tprecache_model (\"progs/v_light.mdl\");\n\t\n\n//\n// Setup light animation tables. 'a' is total darkness, 'z' is maxbright.\n//\n\n\t// 0 normal\n\tlightstyle(0, \"m\");\n\t\n\t// 1 FLICKER (first variety)\n\tlightstyle(1, \"mmnmmommommnonmmonqnmmo\");\n\t\n\t// 2 SLOW STRONG PULSE\n\tlightstyle(2, \"abcdefghijklmnopqrstuvwxyzyxwvutsrqponmlkjihgfedcba\");\n\t\n\t// 3 CANDLE (first variety)\n\tlightstyle(3, \"mmmmmaaaaammmmmaaaaaabcdefgabcdefg\");\n\t\n\t// 4 FAST STROBE\n\tlightstyle(4, \"mamamamamama\");\n\t\n\t// 5 GENTLE PULSE 1\n\tlightstyle(5,\"jklmnopqrstuvwxyzyxwvutsrqponmlkj\");\n\t\n\t// 6 FLICKER (second variety)\n\tlightstyle(6, \"nmonqnmomnmomomno\");\n\t\n\t// 7 CANDLE (second variety)\n\tlightstyle(7, \"mmmaaaabcdefgmmmmaaaammmaamm\");\n\t\n\t// 8 CANDLE (third variety)\n\tlightstyle(8, \"mmmaaammmaaammmabcdefaaaammmmabcdefmmmaaaa\");\n\t\n\t// 9 SLOW STROBE (fourth variety)\n\tlightstyle(9, \"aaaaaaaazzzzzzzz\");\n\t\n\t// 10 FLUORESCENT FLICKER\n\tlightstyle(10, \"mmamammmmammamamaaamammma\");\n\n\t// 11 SLOW PULSE NOT FADE TO BLACK\n\tlightstyle(11, \"abcdefghijklmnopqrrqponmlkjihgfedcba\");\n\t\n\t// styles 32-62 are assigned by the light program for switchable lights\n\n\t// 63 testing\n\tlightstyle(63, \"a\");\n};\n\nvoid() StartFrame =\n{\n\ttimelimit = cvar(\"timelimit\") * 60;\n\tfraglimit = cvar(\"fraglimit\");\n#ifdef NETQUAKE\n\tdeathmatch = cvar(\"deathmatch\");\n\tcoop = cvar(\"coop\");\n\tteamplay = cvar(\"teamplay\");\n#endif\n\tskill = cvar(\"skill\");\n\t\n\tframecount = framecount + 1;\n};\n"
  },
  {
    "path": "quakec/basemod/zombie.qc",
    "content": "/*\n==============================================================================\n\nZOMBIE\n\n==============================================================================\n*/\n$cd id1/models/zombie\n\n$origin\t0 0 24\n\n$base base\n$skin skin\n\n$frame stand1 stand2 stand3 stand4 stand5 stand6 stand7 stand8\n$frame stand9 stand10 stand11 stand12 stand13 stand14 stand15\n\n$frame walk1 walk2 walk3 walk4 walk5 walk6 walk7 walk8 walk9 walk10 walk11\n$frame walk12 walk13 walk14 walk15 walk16 walk17 walk18 walk19\n\n$frame run1 run2 run3 run4 run5 run6 run7 run8 run9 run10 run11 run12\n$frame run13 run14 run15 run16 run17 run18\n\n$frame atta1 atta2 atta3 atta4 atta5 atta6 atta7 atta8 atta9 atta10 atta11\n$frame atta12 atta13\n\n$frame attb1 attb2 attb3 attb4 attb5 attb6 attb7 attb8 attb9 attb10 attb11\n$frame attb12 attb13 attb14\n\n$frame attc1 attc2 attc3 attc4 attc5 attc6 attc7 attc8 attc9 attc10 attc11\n$frame attc12\n\n$frame paina1 paina2 paina3 paina4 paina5 paina6 paina7 paina8 paina9 paina10\n$frame paina11 paina12\n\n$frame painb1 painb2 painb3 painb4 painb5 painb6 painb7 painb8 painb9 painb10\n$frame painb11 painb12 painb13 painb14 painb15 painb16 painb17 painb18 painb19\n$frame painb20 painb21 painb22 painb23 painb24 painb25 painb26 painb27 painb28\n\n$frame painc1 painc2 painc3 painc4 painc5 painc6 painc7 painc8 painc9 painc10\n$frame painc11 painc12 painc13 painc14 painc15 painc16 painc17 painc18\n\n$frame paind1 paind2 paind3 paind4 paind5 paind6 paind7 paind8 paind9 paind10\n$frame paind11 paind12 paind13\n\n$frame paine1 paine2 paine3 paine4 paine5 paine6 paine7 paine8 paine9 paine10\n$frame paine11 paine12 paine13 paine14 paine15 paine16 paine17 paine18 paine19\n$frame paine20 paine21 paine22 paine23 paine24 paine25 paine26 paine27 paine28\n$frame paine29 paine30\n\n$frame cruc_1 cruc_2 cruc_3 cruc_4 cruc_5 cruc_6\n\nfloat\tSPAWN_CRUCIFIED\t= 1;\n\n//=============================================================================\n\nvoid() zombie_stand1\t=[\t$stand1,\t\tzombie_stand2\t] {ai_stand();};\nvoid() zombie_stand2\t=[\t$stand2,\t\tzombie_stand3\t] {ai_stand();};\nvoid() zombie_stand3\t=[\t$stand3,\t\tzombie_stand4\t] {ai_stand();};\nvoid() zombie_stand4\t=[\t$stand4,\t\tzombie_stand5\t] {ai_stand();};\nvoid() zombie_stand5\t=[\t$stand5,\t\tzombie_stand6\t] {ai_stand();};\nvoid() zombie_stand6\t=[\t$stand6,\t\tzombie_stand7\t] {ai_stand();};\nvoid() zombie_stand7\t=[\t$stand7,\t\tzombie_stand8\t] {ai_stand();};\nvoid() zombie_stand8\t=[\t$stand8,\t\tzombie_stand9\t] {ai_stand();};\nvoid() zombie_stand9\t=[\t$stand9,\t\tzombie_stand10\t] {ai_stand();};\nvoid() zombie_stand10\t=[\t$stand10,\t\tzombie_stand11\t] {ai_stand();};\nvoid() zombie_stand11\t=[\t$stand11,\t\tzombie_stand12\t] {ai_stand();};\nvoid() zombie_stand12\t=[\t$stand12,\t\tzombie_stand13\t] {ai_stand();};\nvoid() zombie_stand13\t=[\t$stand13,\t\tzombie_stand14\t] {ai_stand();};\nvoid() zombie_stand14\t=[\t$stand14,\t\tzombie_stand15\t] {ai_stand();};\nvoid() zombie_stand15\t=[\t$stand15,\t\tzombie_stand1\t] {ai_stand();};\n\nvoid() zombie_cruc1\t=\t[\t$cruc_1,\t\tzombie_cruc2\t] {\nif (random() < 0.1)\n\tsound (self, CHAN_VOICE, \"zombie/idle_w2.wav\", 1, ATTN_STATIC);};\nvoid() zombie_cruc2\t=\t[\t$cruc_2,\t\tzombie_cruc3\t] {self.nextthink = time + 0.1 + random()*0.1;};\nvoid() zombie_cruc3\t=\t[\t$cruc_3,\t\tzombie_cruc4\t] {self.nextthink = time + 0.1 + random()*0.1;};\nvoid() zombie_cruc4\t=\t[\t$cruc_4,\t\tzombie_cruc5\t] {self.nextthink = time + 0.1 + random()*0.1;};\nvoid() zombie_cruc5\t=\t[\t$cruc_5,\t\tzombie_cruc6\t] {self.nextthink = time + 0.1 + random()*0.1;};\nvoid() zombie_cruc6\t=\t[\t$cruc_6,\t\tzombie_cruc1\t] {self.nextthink = time + 0.1 + random()*0.1;};\n\nvoid() zombie_walk1\t\t=[\t$walk1,\t\tzombie_walk2\t] {ai_walk(0);};\nvoid() zombie_walk2\t\t=[\t$walk2,\t\tzombie_walk3\t] {ai_walk(2);};\nvoid() zombie_walk3\t\t=[\t$walk3,\t\tzombie_walk4\t] {ai_walk(3);};\nvoid() zombie_walk4\t\t=[\t$walk4,\t\tzombie_walk5\t] {ai_walk(2);};\nvoid() zombie_walk5\t\t=[\t$walk5,\t\tzombie_walk6\t] {ai_walk(1);};\nvoid() zombie_walk6\t\t=[\t$walk6,\t\tzombie_walk7\t] {ai_walk(0);};\nvoid() zombie_walk7\t\t=[\t$walk7,\t\tzombie_walk8\t] {ai_walk(0);};\nvoid() zombie_walk8\t\t=[\t$walk8,\t\tzombie_walk9\t] {ai_walk(0);};\nvoid() zombie_walk9\t\t=[\t$walk9,\t\tzombie_walk10\t] {ai_walk(0);};\nvoid() zombie_walk10\t=[\t$walk10,\tzombie_walk11\t] {ai_walk(0);};\nvoid() zombie_walk11\t=[\t$walk11,\tzombie_walk12\t] {ai_walk(2);};\nvoid() zombie_walk12\t=[\t$walk12,\tzombie_walk13\t] {ai_walk(2);};\nvoid() zombie_walk13\t=[\t$walk13,\tzombie_walk14\t] {ai_walk(1);};\nvoid() zombie_walk14\t=[\t$walk14,\tzombie_walk15\t] {ai_walk(0);};\nvoid() zombie_walk15\t=[\t$walk15,\tzombie_walk16\t] {ai_walk(0);};\nvoid() zombie_walk16\t=[\t$walk16,\tzombie_walk17\t] {ai_walk(0);};\nvoid() zombie_walk17\t=[\t$walk17,\tzombie_walk18\t] {ai_walk(0);};\nvoid() zombie_walk18\t=[\t$walk18,\tzombie_walk19\t] {ai_walk(0);};\nvoid() zombie_walk19\t=[\t$walk19,\tzombie_walk1\t] {\nai_walk(0);\nif (random() < 0.2)\n\tsound (self, CHAN_VOICE, \"zombie/z_idle.wav\", 1, ATTN_IDLE);};\n\nvoid() zombie_run1\t\t=[\t$run1,\t\tzombie_run2\t] {ai_run(1);self.inpain = 0;};\nvoid() zombie_run2\t\t=[\t$run2,\t\tzombie_run3\t] {ai_run(1);};\nvoid() zombie_run3\t\t=[\t$run3,\t\tzombie_run4\t] {ai_run(0);};\nvoid() zombie_run4\t\t=[\t$run4,\t\tzombie_run5\t] {ai_run(1);};\nvoid() zombie_run5\t\t=[\t$run5,\t\tzombie_run6\t] {ai_run(2);};\nvoid() zombie_run6\t\t=[\t$run6,\t\tzombie_run7\t] {ai_run(3);};\nvoid() zombie_run7\t\t=[\t$run7,\t\tzombie_run8\t] {ai_run(4);};\nvoid() zombie_run8\t\t=[\t$run8,\t\tzombie_run9\t] {ai_run(4);};\nvoid() zombie_run9\t\t=[\t$run9,\t\tzombie_run10\t] {ai_run(2);};\nvoid() zombie_run10\t\t=[\t$run10,\t\tzombie_run11\t] {ai_run(0);};\nvoid() zombie_run11\t\t=[\t$run11,\t\tzombie_run12\t] {ai_run(0);};\nvoid() zombie_run12\t\t=[\t$run12,\t\tzombie_run13\t] {ai_run(0);};\nvoid() zombie_run13\t\t=[\t$run13,\t\tzombie_run14\t] {ai_run(2);};\nvoid() zombie_run14\t\t=[\t$run14,\t\tzombie_run15\t] {ai_run(4);};\nvoid() zombie_run15\t\t=[\t$run15,\t\tzombie_run16\t] {ai_run(6);};\nvoid() zombie_run16\t\t=[\t$run16,\t\tzombie_run17\t] {ai_run(7);};\nvoid() zombie_run17\t\t=[\t$run17,\t\tzombie_run18\t] {ai_run(3);};\nvoid() zombie_run18\t\t=[\t$run18,\t\tzombie_run1\t] {\nai_run(8);\nif (random() < 0.2)\n\tsound (self, CHAN_VOICE, \"zombie/z_idle.wav\", 1, ATTN_IDLE);\nif (random() > 0.8)\n\tsound (self, CHAN_VOICE, \"zombie/z_idle1.wav\", 1, ATTN_IDLE);\n};\n\n/*\n=============================================================================\n\nATTACKS\n\n=============================================================================\n*/\n\n/*\n================\nZombieFireGrenade\n================\n*/\nvoid(vector st) ZombieFireGrenade =\n{\n\tlocal\tvector\torg, vel;\n\n\tsound (self, CHAN_WEAPON, \"zombie/z_shot1.wav\", 1, ATTN_NORM);\n\n\torg = self.origin + st_x * v_forward + st_y * v_right + (st_z - 24) * v_up;\n\tvel = normalize(self.enemy.origin - org) * 600;\n\tvel_z = 200;\n\tPRJ_FireProjectile(self, \"progs/zom_gib.mdl\", org, vel, PE_ZOMBIEGIB, 10, MOD_ZOMBIE, 2.5);\n\tPRJ_SetTossedProjectile();\n};\n\n\nvoid() zombie_atta1\t\t=[\t$atta1,\t\tzombie_atta2\t] {ai_face();};\nvoid() zombie_atta2\t\t=[\t$atta2,\t\tzombie_atta3\t] {ai_face();};\nvoid() zombie_atta3\t\t=[\t$atta3,\t\tzombie_atta4\t] {ai_face();};\nvoid() zombie_atta4\t\t=[\t$atta4,\t\tzombie_atta5\t] {ai_face();};\nvoid() zombie_atta5\t\t=[\t$atta5,\t\tzombie_atta6\t] {ai_face();};\nvoid() zombie_atta6\t\t=[\t$atta6,\t\tzombie_atta7\t] {ai_face();};\nvoid() zombie_atta7\t\t=[\t$atta7,\t\tzombie_atta8\t] {ai_face();};\nvoid() zombie_atta8\t\t=[\t$atta8,\t\tzombie_atta9\t] {ai_face();};\nvoid() zombie_atta9\t\t=[\t$atta9,\t\tzombie_atta10\t] {ai_face();};\nvoid() zombie_atta10\t=[\t$atta10,\tzombie_atta11\t] {ai_face();};\nvoid() zombie_atta11\t=[\t$atta11,\tzombie_atta12\t] {ai_face();};\nvoid() zombie_atta12\t=[\t$atta12,\tzombie_atta13\t] {ai_face();};\nvoid() zombie_atta13\t=[\t$atta13,\tzombie_run1\t] {ai_face();ZombieFireGrenade('-10 -22 30');};\n\nvoid() zombie_attb1\t\t=[\t$attb1,\t\tzombie_attb2\t] {ai_face();};\nvoid() zombie_attb2\t\t=[\t$attb2,\t\tzombie_attb3\t] {ai_face();};\nvoid() zombie_attb3\t\t=[\t$attb3,\t\tzombie_attb4\t] {ai_face();};\nvoid() zombie_attb4\t\t=[\t$attb4,\t\tzombie_attb5\t] {ai_face();};\nvoid() zombie_attb5\t\t=[\t$attb5,\t\tzombie_attb6\t] {ai_face();};\nvoid() zombie_attb6\t\t=[\t$attb6,\t\tzombie_attb7\t] {ai_face();};\nvoid() zombie_attb7\t\t=[\t$attb7,\t\tzombie_attb8\t] {ai_face();};\nvoid() zombie_attb8\t\t=[\t$attb8,\t\tzombie_attb9\t] {ai_face();};\nvoid() zombie_attb9\t\t=[\t$attb9,\t\tzombie_attb10\t] {ai_face();};\nvoid() zombie_attb10\t=[\t$attb10,\tzombie_attb11\t] {ai_face();};\nvoid() zombie_attb11\t=[\t$attb11,\tzombie_attb12\t] {ai_face();};\nvoid() zombie_attb12\t=[\t$attb12,\tzombie_attb13\t] {ai_face();};\nvoid() zombie_attb13\t=[\t$attb13,\tzombie_attb14\t] {ai_face();};\nvoid() zombie_attb14\t=[\t$attb13,\tzombie_run1\t] {ai_face();ZombieFireGrenade('-10 -24 29');};\n\nvoid() zombie_attc1\t\t=[\t$attc1,\t\tzombie_attc2\t] {ai_face();};\nvoid() zombie_attc2\t\t=[\t$attc2,\t\tzombie_attc3\t] {ai_face();};\nvoid() zombie_attc3\t\t=[\t$attc3,\t\tzombie_attc4\t] {ai_face();};\nvoid() zombie_attc4\t\t=[\t$attc4,\t\tzombie_attc5\t] {ai_face();};\nvoid() zombie_attc5\t\t=[\t$attc5,\t\tzombie_attc6\t] {ai_face();};\nvoid() zombie_attc6\t\t=[\t$attc6,\t\tzombie_attc7\t] {ai_face();};\nvoid() zombie_attc7\t\t=[\t$attc7,\t\tzombie_attc8\t] {ai_face();};\nvoid() zombie_attc8\t\t=[\t$attc8,\t\tzombie_attc9\t] {ai_face();};\nvoid() zombie_attc9\t\t=[\t$attc9,\t\tzombie_attc10\t] {ai_face();};\nvoid() zombie_attc10\t=[\t$attc10,\tzombie_attc11\t] {ai_face();};\nvoid() zombie_attc11\t=[\t$attc11,\tzombie_attc12\t] {ai_face();};\nvoid() zombie_attc12\t=[\t$attc12,\tzombie_run1\t\t] {ai_face();ZombieFireGrenade('-12 -19 29');};\n\nvoid() zombie_missile =\n{\n\tlocal float\tr;\n\t\n\tr = random();\n\t\n\tif (r < 0.3)\n\t\tzombie_atta1 ();\n\telse if (r < 0.6)\n\t\tzombie_attb1 ();\n\telse\n\t\tzombie_attc1 ();\n};\n\n\n/*\n=============================================================================\n\nPAIN\n\n=============================================================================\n*/\n\nvoid() zombie_paina1\t=[\t$paina1,\tzombie_paina2\t] {sound (self, CHAN_VOICE, \"zombie/z_pain.wav\", 1, ATTN_NORM);};\nvoid() zombie_paina2\t=[\t$paina2,\tzombie_paina3\t] {ai_painforward(3);};\nvoid() zombie_paina3\t=[\t$paina3,\tzombie_paina4\t] {ai_painforward(1);};\nvoid() zombie_paina4\t=[\t$paina4,\tzombie_paina5\t] {ai_pain(1);};\nvoid() zombie_paina5\t=[\t$paina5,\tzombie_paina6\t] {ai_pain(3);};\nvoid() zombie_paina6\t=[\t$paina6,\tzombie_paina7\t] {ai_pain(1);};\nvoid() zombie_paina7\t=[\t$paina7,\tzombie_paina8\t] {};\nvoid() zombie_paina8\t=[\t$paina8,\tzombie_paina9\t] {};\nvoid() zombie_paina9\t=[\t$paina9,\tzombie_paina10\t] {};\nvoid() zombie_paina10\t=[\t$paina10,\tzombie_paina11\t] {};\nvoid() zombie_paina11\t=[\t$paina11,\tzombie_paina12\t] {};\nvoid() zombie_paina12\t=[\t$paina12,\tzombie_run1\t\t] {};\n\nvoid() zombie_painb1\t=[\t$painb1,\tzombie_painb2\t] {sound (self, CHAN_VOICE, \"zombie/z_pain1.wav\", 1, ATTN_NORM);};\nvoid() zombie_painb2\t=[\t$painb2,\tzombie_painb3\t] {ai_pain(2);};\nvoid() zombie_painb3\t=[\t$painb3,\tzombie_painb4\t] {ai_pain(8);};\nvoid() zombie_painb4\t=[\t$painb4,\tzombie_painb5\t] {ai_pain(6);};\nvoid() zombie_painb5\t=[\t$painb5,\tzombie_painb6\t] {ai_pain(2);};\nvoid() zombie_painb6\t=[\t$painb6,\tzombie_painb7\t] {};\nvoid() zombie_painb7\t=[\t$painb7,\tzombie_painb8\t] {};\nvoid() zombie_painb8\t=[\t$painb8,\tzombie_painb9\t] {};\nvoid() zombie_painb9\t=[\t$painb9,\tzombie_painb10\t] {sound (self, CHAN_BODY, \"zombie/z_fall.wav\", 1, ATTN_NORM);};\nvoid() zombie_painb10\t=[\t$painb10,\tzombie_painb11\t] {};\nvoid() zombie_painb11\t=[\t$painb11,\tzombie_painb12\t] {};\nvoid() zombie_painb12\t=[\t$painb12,\tzombie_painb13\t] {};\nvoid() zombie_painb13\t=[\t$painb13,\tzombie_painb14\t] {};\nvoid() zombie_painb14\t=[\t$painb14,\tzombie_painb15\t] {};\nvoid() zombie_painb15\t=[\t$painb15,\tzombie_painb16\t] {};\nvoid() zombie_painb16\t=[\t$painb16,\tzombie_painb17\t] {};\nvoid() zombie_painb17\t=[\t$painb17,\tzombie_painb18\t] {};\nvoid() zombie_painb18\t=[\t$painb18,\tzombie_painb19\t] {};\nvoid() zombie_painb19\t=[\t$painb19,\tzombie_painb20\t] {};\nvoid() zombie_painb20\t=[\t$painb20,\tzombie_painb21\t] {};\nvoid() zombie_painb21\t=[\t$painb21,\tzombie_painb22\t] {};\nvoid() zombie_painb22\t=[\t$painb22,\tzombie_painb23\t] {};\nvoid() zombie_painb23\t=[\t$painb23,\tzombie_painb24\t] {};\nvoid() zombie_painb24\t=[\t$painb24,\tzombie_painb25\t] {};\nvoid() zombie_painb25\t=[\t$painb25,\tzombie_painb26\t] {ai_painforward(1);};\nvoid() zombie_painb26\t=[\t$painb26,\tzombie_painb27\t] {};\nvoid() zombie_painb27\t=[\t$painb27,\tzombie_painb28\t] {};\nvoid() zombie_painb28\t=[\t$painb28,\tzombie_run1\t\t] {};\n\nvoid() zombie_painc1\t=[\t$painc1,\tzombie_painc2\t] {sound (self, CHAN_VOICE, \"zombie/z_pain1.wav\", 1, ATTN_NORM);};\nvoid() zombie_painc2\t=[\t$painc2,\tzombie_painc3\t] {};\nvoid() zombie_painc3\t=[\t$painc3,\tzombie_painc4\t] {ai_pain(3);};\nvoid() zombie_painc4\t=[\t$painc4,\tzombie_painc5\t] {ai_pain(1);};\nvoid() zombie_painc5\t=[\t$painc5,\tzombie_painc6\t] {};\nvoid() zombie_painc6\t=[\t$painc6,\tzombie_painc7\t] {};\nvoid() zombie_painc7\t=[\t$painc7,\tzombie_painc8\t] {};\nvoid() zombie_painc8\t=[\t$painc8,\tzombie_painc9\t] {};\nvoid() zombie_painc9\t=[\t$painc9,\tzombie_painc10\t] {};\nvoid() zombie_painc10\t=[\t$painc10,\tzombie_painc11\t] {};\nvoid() zombie_painc11\t=[\t$painc11,\tzombie_painc12\t] {ai_painforward(1);};\nvoid() zombie_painc12\t=[\t$painc12,\tzombie_painc13\t] {ai_painforward(1);};\nvoid() zombie_painc13\t=[\t$painc13,\tzombie_painc14\t] {};\nvoid() zombie_painc14\t=[\t$painc14,\tzombie_painc15\t] {};\nvoid() zombie_painc15\t=[\t$painc15,\tzombie_painc16\t] {};\nvoid() zombie_painc16\t=[\t$painc16,\tzombie_painc17\t] {};\nvoid() zombie_painc17\t=[\t$painc17,\tzombie_painc18\t] {};\nvoid() zombie_painc18\t=[\t$painc18,\tzombie_run1\t] {};\n\nvoid() zombie_paind1\t=[\t$paind1,\tzombie_paind2\t] {sound (self, CHAN_VOICE, \"zombie/z_pain.wav\", 1, ATTN_NORM);};\nvoid() zombie_paind2\t=[\t$paind2,\tzombie_paind3\t] {};\nvoid() zombie_paind3\t=[\t$paind3,\tzombie_paind4\t] {};\nvoid() zombie_paind4\t=[\t$paind4,\tzombie_paind5\t] {};\nvoid() zombie_paind5\t=[\t$paind5,\tzombie_paind6\t] {};\nvoid() zombie_paind6\t=[\t$paind6,\tzombie_paind7\t] {};\nvoid() zombie_paind7\t=[\t$paind7,\tzombie_paind8\t] {};\nvoid() zombie_paind8\t=[\t$paind8,\tzombie_paind9\t] {};\nvoid() zombie_paind9\t=[\t$paind9,\tzombie_paind10\t] {ai_pain(1);};\nvoid() zombie_paind10\t=[\t$paind10,\tzombie_paind11\t] {};\nvoid() zombie_paind11\t=[\t$paind11,\tzombie_paind12\t] {};\nvoid() zombie_paind12\t=[\t$paind12,\tzombie_paind13\t] {};\nvoid() zombie_paind13\t=[\t$paind13,\tzombie_run1\t] {};\n\nvoid() zombie_paine1\t=[\t$paine1,\tzombie_paine2\t] {\nsound (self, CHAN_VOICE, \"zombie/z_pain.wav\", 1, ATTN_NORM);\nself.health = 60;\n};\nvoid() zombie_paine2\t=[\t$paine2,\tzombie_paine3\t] {ai_pain(8);};\nvoid() zombie_paine3\t=[\t$paine3,\tzombie_paine4\t] {ai_pain(5);};\nvoid() zombie_paine4\t=[\t$paine4,\tzombie_paine5\t] {ai_pain(3);};\nvoid() zombie_paine5\t=[\t$paine5,\tzombie_paine6\t] {ai_pain(1);};\nvoid() zombie_paine6\t=[\t$paine6,\tzombie_paine7\t] {ai_pain(2);};\nvoid() zombie_paine7\t=[\t$paine7,\tzombie_paine8\t] {ai_pain(1);};\nvoid() zombie_paine8\t=[\t$paine8,\tzombie_paine9\t] {ai_pain(1);};\nvoid() zombie_paine9\t=[\t$paine9,\tzombie_paine10\t] {ai_pain(2);};\nvoid() zombie_paine10\t=[\t$paine10,\tzombie_paine11\t] {\nsound (self, CHAN_BODY, \"zombie/z_fall.wav\", 1, ATTN_NORM);\nself.solid = SOLID_NOT;\n};\nvoid() zombie_paine11\t=[\t$paine11,\tzombie_paine12\t] {self.nextthink = time + 5;self.health = 60;};\nvoid() zombie_paine12\t=[\t$paine12,\tzombie_paine13\t]{\n// see if ok to stand up\nself.health = 60;\nsound (self, CHAN_VOICE, \"zombie/z_idle.wav\", 1, ATTN_IDLE);\nself.solid = SOLID_SLIDEBOX;\nif (!walkmove (0, 0))\n{\n\tself.think = zombie_paine11;\n\tself.solid = SOLID_NOT;\n\treturn;\n}\n};\nvoid() zombie_paine13\t=[\t$paine13,\tzombie_paine14\t] {};\nvoid() zombie_paine14\t=[\t$paine14,\tzombie_paine15\t] {};\nvoid() zombie_paine15\t=[\t$paine15,\tzombie_paine16\t] {};\nvoid() zombie_paine16\t=[\t$paine16,\tzombie_paine17\t] {};\nvoid() zombie_paine17\t=[\t$paine17,\tzombie_paine18\t] {};\nvoid() zombie_paine18\t=[\t$paine18,\tzombie_paine19\t] {};\nvoid() zombie_paine19\t=[\t$paine19,\tzombie_paine20\t] {};\nvoid() zombie_paine20\t=[\t$paine20,\tzombie_paine21\t] {};\nvoid() zombie_paine21\t=[\t$paine21,\tzombie_paine22\t] {};\nvoid() zombie_paine22\t=[\t$paine22,\tzombie_paine23\t] {};\nvoid() zombie_paine23\t=[\t$paine23,\tzombie_paine24\t] {};\nvoid() zombie_paine24\t=[\t$paine24,\tzombie_paine25\t] {};\nvoid() zombie_paine25\t=[\t$paine25,\tzombie_paine26\t] {ai_painforward(5);};\nvoid() zombie_paine26\t=[\t$paine26,\tzombie_paine27\t] {ai_painforward(3);};\nvoid() zombie_paine27\t=[\t$paine27,\tzombie_paine28\t] {ai_painforward(1);};\nvoid() zombie_paine28\t=[\t$paine28,\tzombie_paine29\t] {ai_pain(1);};\nvoid() zombie_paine29\t=[\t$paine29,\tzombie_paine30\t] {};\nvoid() zombie_paine30\t=[\t$paine30,\tzombie_run1\t\t] {};\n\nvoid() zombie_die =\n{\n\tsound (self, CHAN_VOICE, \"zombie/z_gib.wav\", 1, ATTN_NORM);\n\tThrowHead (\"progs/h_zombie.mdl\", self.health);\n\tThrowGib (\"progs/gib1.mdl\", self.health);\n\tThrowGib (\"progs/gib2.mdl\", self.health);\n\tThrowGib (\"progs/gib3.mdl\", self.health);\n};\n\n/*\n=================\nzombie_pain\n\nZombies can only be killed (gibbed) by doing 60 hit points of damage\nin a single frame (rockets, grenades, quad shotgun, quad nailgun).\n\nA hit of 25 points or more (super shotgun, quad nailgun) will allways put it\ndown to the ground.\n\nA hit of from 10 to 40 points in one frame will cause it to go down if it\nhas been twice in two seconds, otherwise it goes into one of the four\nfast pain frames.\n\nA hit of less than 10 points of damage (winged by a shotgun) will be ignored.\n\nFIXME: don't use pain_finished because of nightmare hack\n=================\n*/\nvoid(entity attacker, float take) zombie_pain =\n{\n\tlocal float r;\n\n\tself.health = 60;\t\t// allways reset health\n\n\tif (take < 9)\n\t\treturn;\t\t\t\t// totally ignore\n\n\tif (self.inpain == 2)\n\t\treturn;\t\t\t// down on ground, so don't reset any counters\n\n// go down immediately if a big enough hit\n\tif (take >= 25)\n\t{\n\t\tself.inpain = 2;\n\t\tzombie_paine1 ();\n\t\treturn;\n\t}\n\t\n\tif (self.inpain)\n\t{\n// if hit again in next gre seconds while not in pain frames, definately drop\n\t\tself.pain_finished = time + 3;\n\t\treturn;\t\t\t// currently going through an animation, don't change\n\t}\n\t\n\tif (self.pain_finished > time)\n\t{\n// hit again, so drop down\n\t\tself.inpain = 2;\n\t\tzombie_paine1 ();\n\t\treturn;\n\t}\n\n// gp into one of the fast pain animations\t\n\tself.inpain = 1;\n\n\tr = random();\n\tif (r < 0.25)\n\t\tzombie_paina1 ();\n\telse if (r <  0.5)\n\t\tzombie_painb1 ();\n\telse if (r <  0.75)\n\t\tzombie_painc1 ();\n\telse\n\t\tzombie_paind1 ();\n};\n\n//============================================================================\n\n/*QUAKED monster_zombie (1 0 0) (-16 -16 -24) (16 16 32) Crucified ambush\n\nIf crucified, stick the bounding box 12 pixels back into a wall to look right.\n*/\nvoid() monster_zombie =\n{\n\tif (deathmatch)\n\t{\n\t\tremove(self);\n\t\treturn;\n\t}\n\n\tprecache_model (\"progs/zombie.mdl\");\n\tprecache_model (\"progs/h_zombie.mdl\");\n\tprecache_model (\"progs/zom_gib.mdl\");\n\n\tprecache_sound (\"zombie/z_idle.wav\");\n\tprecache_sound (\"zombie/z_idle1.wav\");\n\tprecache_sound (\"zombie/z_shot1.wav\");\n\tprecache_sound (\"zombie/z_gib.wav\");\n\tprecache_sound (\"zombie/z_pain.wav\");\n\tprecache_sound (\"zombie/z_pain1.wav\");\n\tprecache_sound (\"zombie/z_fall.wav\");\n\tprecache_sound (\"zombie/z_miss.wav\");\n\tprecache_sound (\"zombie/z_hit.wav\");\n\tprecache_sound (\"zombie/idle_w2.wav\");\n\n\tself.solid = SOLID_SLIDEBOX;\n\tself.movetype = MOVETYPE_STEP;\n\n\tsetmodel (self, \"progs/zombie.mdl\");\n\n\tsetsize (self, '-16 -16 -24', '16 16 40');\n\tself.health = 60;\n\n\tself.th_stand = zombie_stand1;\n\tself.th_walk = zombie_walk1;\n\tself.th_run = zombie_run1;\n\tself.th_pain = zombie_pain;\n\tself.th_die = zombie_die;\n\tself.th_missile = zombie_missile;\n\n\tif (self.spawnflags & SPAWN_CRUCIFIED)\n\t{\n\t\tself.movetype = MOVETYPE_NONE;\n\t\tzombie_cruc1 ();\n\t}\n\telse\n\t\twalkmonster_start();\n};\n"
  },
  {
    "path": "quakec/csaddon/src/brush_draw.qc",
    "content": "void(int mod, int id) DrawEngineBrushWireframe =\n{\n\tconst vector col = '1 0 0';\n\tfor(int facenum = 0;;)\n\t{\n\t\tint points = brush_getfacepoints(mod, id, ++facenum, &facepoints[0], facepoints.length);\n\t\tif (!points)\n\t\t\tbreak;\t//end of face list, I guess\n\n\t\tR_BeginPolygon(\"chop\");\n\t\tR_PolygonVertex(facepoints[0], '0 0', col, 1);\n\t\tfor (int point = 1; point < points; point++)\n\t\t{\n\t\t\tR_PolygonVertex(facepoints[point], '0 0', col, 1);\n\t\t\tR_EndPolygon();\n\t\t\tR_PolygonVertex(facepoints[point], '0 0', col, 1);\n\t\t}\n\t\tR_PolygonVertex(facepoints[0], '0 0', col, 1);\n\t\tR_EndPolygon();\n\t}\n};\nvoid(patchvert_t *cp, patchinfo_t info, vector col, float alpha) DrawQCPatchTextured;\nvoid(int mod, int id, float alpha) DrawEngineBrushFaded =\n{\t//draw one of the engine's brushes, but faded.\n\tconst vector col = '1 1 1';\n\tstatic int contents;\n\tstatic brushface_t faces[MAX_BRUSHFACES];\n\tint numfaces = brush_get(mod, id, &faces[0], faces.length, &contents);\n\tif (!numfaces)\t\n\t{\n\t\tnumfaces = patch_getmesh(mod, id, __NULL__, 0, __NULL__);\t//ask how much space we need\n\t\tif (numfaces)\n\t\t{\n\t\t\tpatchvert_t *v = memalloc(sizeof(*v)*numfaces);\n\t\t\tstatic patchinfo_t patchinfo;\n\t\t\tpatch_getmesh(mod, id, v, numfaces, &patchinfo);\t//now we can actually get it\n\t\t\tDrawQCPatchTextured(v, patchinfo, col, alpha);\n\t\t\tmemfree(v);\n\t\t}\n\t\t\n\t\treturn;\n\t}\n\n\tfor(int f = 0; f < numfaces; f++)\n\t{\n\t\tint points = brush_getfacepoints(mod, id, 1+f, &facepoints[0], facepoints.length);\n\t\tif (!points)\n\t\t\tcontinue;\n\n\t\t//this is unfortunate. the built in shaders expect to use lightmaps. we don't have those.\n\t\t//because lightmaps are special things, we end up in a real mess. so lets just make sure there's a shader now, because we can.\n\t\t//FIXME: we don't manage to pick up the size of the original wad image\n\t\t\n\t\tstring materialname = strcat(\"textures/\", faces[f].shadername);\n\t\tshaderforname(materialname,\n\t\t\tsprintf(\"{\"\n\t\t\t\t\"{\\n\"\n\t\t\t\t\t\"map \\\"%s\\\"\\n\"\n\t\t\t\t\t\"rgbgen vertex\\n\"\n\t\t\t\t\t\"alphagen vertex\\n\"\n\t\t\t\t\"}\\n\"\n\t\t\t\"}\", faces[f].shadername));\n\n\t\tvector sz = drawgetimagesize(materialname);\n\t\tR_BeginPolygon(materialname, 3);\n\t\tfor (int point = 0; point < points; point++)\n\t\t\tR_PolygonVertex(facepoints[point], [(facepoints[point] * faces[f].sdir + faces[f].sbias)/sz_x, (facepoints[point] * faces[f].tdir + faces[f].tbias)/sz_y], col, alpha);\n\t\tR_EndPolygon();\n\t}\n};\n\nvoid(patchvert_t *vert, patchinfo_t info, string shader, vector col, float alpha) DrawQCPatchWireframe =\n{\n\tint x,y,cp;\n\tR_BeginPolygon(shader);\n\tfor (y = 0, cp = 0; y < info.cpheight; y++, cp++)\n\t\tfor (x = 0; x < info.cpwidth-1; x++)\n\t\t{\n\t\t\tR_PolygonVertex(vert[cp].xyz, [vert[cp].s, vert[cp].t], col, alpha);\n\t\t\tcp++;\n\t\t\tR_PolygonVertex(vert[cp].xyz, [vert[cp].s, vert[cp].t], col, alpha);\n\t\t\tR_EndPolygon();\n\t\t}\n\tfor (x = 0; x < info.cpwidth; x++)\n\t\tfor (y = 0, cp=x; y < info.cpheight-1; y++)\n\t\t{\n\t\t\tR_PolygonVertex(vert[cp].xyz, [vert[cp].s, vert[cp].t], col, alpha);\n\t\t\tcp += info.cpwidth;\n\t\t\tR_PolygonVertex(vert[cp].xyz, [vert[cp].s, vert[cp].t], col, alpha);\n\t\t\tR_EndPolygon();\n\t\t}\n};\n\nvoid(brushface_t *faces, int numfaces, string shader, vector col, float alpha) DrawQCBrushWireframe =\n{\n\tint f;\n\tint point, points;\n\tfor(f = 0; f < numfaces;)\n\t{\n\t\tpoints = brush_calcfacepoints(++f, faces, numfaces, facepoints, MAX_FACEPOINTS);\n\t\tif (!points)\n\t\t\tcontinue;\t//should probably warn somehow about this\n\t\tR_BeginPolygon(shader);\n\t\tR_PolygonVertex(facepoints[0], '0 0', col, alpha);\n\t\tfor (point = 0; point < points-1; )\n\t\t{\n\t\t\tpoint++;\n\t\t\tR_PolygonVertex(facepoints[point], '0 0', col, alpha);\n\t\t\tR_EndPolygon();\n\t\t\tR_PolygonVertex(facepoints[point], '0 0', col, alpha);\n\t\t}\n\t\tR_PolygonVertex(facepoints[0], '0 0', col, alpha);\n\t\tR_EndPolygon();\n\t}\n};\nvoid(brushface_t *faces, int numfaces, string shader, vector col, float alpha) DrawQCBrushSolid =\n{\n\tint f;\n\tint point, points;\n\tfor(f = 0; f < numfaces;)\n\t{\n\t\tpoints = brush_calcfacepoints(++f, faces, numfaces, facepoints, MAX_FACEPOINTS);\n\t\tif (!points)\n\t\t\tcontinue;\t//should probably warn somehow about this\n\t\tR_BeginPolygon(shader);\n\t\tfor (point = 0; point < points; point++)\n\t\t\tR_PolygonVertex(facepoints[point], '0 0', col, alpha);\n\t\tR_EndPolygon();\n\t}\n};\nvoid(brushface_t *faces, int numfaces, vector col, float alpha) DrawQCBrushTextured =\n{\n\tint f;\n\tint point, points;\n\tfor(f = 0; f < numfaces; f++)\n\t{\n\t\tpoints = brush_calcfacepoints(1+f, faces, numfaces, facepoints, MAX_FACEPOINTS);\n\t\tif (points)\n\t\t{\n\t\t\t//this is unfortunate. the built in shaders expect to use lightmaps. we don't have those.\n\t\t\t//because lightmaps are special things, we end up in a real mess. so lets just make sure there's a shader now, because we can.\n\t\t\t//FIXME: we don't manage to pick up the size of the original wad image\n\t\t\tstring materialname = strcat(\"textures/\", faces[f].shadername);\n\t\t\tshaderforname(materialname,\n\t\t\t\tsprintf(\"{\"\n\t\t\t\t\t\"{\\n\"\n\t\t\t\t\t\t\"map \\\"%s\\\"\\n\"\n\t\t\t\t\t\t\"rgbgen vertex\\n\"\n\t\t\t\t\t\t\"alphagen vertex\\n\"\n\t\t\t\t\t\"}\\n\"\n\t\t\t\t\"}\", faces[f].shadername));\n\n\t\t\tvector sz = drawgetimagesize(materialname);\n\t\t\tR_BeginPolygon(materialname, 3);\n\t\t\tfor (point = 0; point < points; point++)\n\t\t\t\tR_PolygonVertex(facepoints[point], [(facepoints[point] * faces[f].sdir + faces[f].sbias)/sz_x, (facepoints[point] * faces[f].tdir + faces[f].tbias)/sz_y], col, alpha);\n\t\t\tR_EndPolygon();\n\t\t}\n\t}\n};\nvoid(patchvert_t *cp, patchinfo_t info, vector col, float alpha) DrawQCPatchTextured =\n{\n\tint x, y;\n\n\tstring materialname = strcat(\"textures/\", info.shadername);\n\tshaderforname(materialname,\n\t\tsprintf(\"{\"\n\t\t\t\"{\\n\"\n\t\t\t\t\"map \\\"%s\\\"\\n\"\n\t\t\t\t\"rgbgen vertex\\n\"\n\t\t\t\t\"alphagen vertex\\n\"\n\t\t\t\"}\\n\"\n\t\t\"}\", info.shadername));\n\n\tR_BeginPolygon(materialname);\n\tfor (y = 0; y < info.cpheight-1; y++, cp++)\n\tfor (x = 0; x < info.cpwidth-1; x++, cp++)\n\t{\n#if 1\n\t\tR_PolygonVertex(cp[0].xyz, [cp[0].s,cp[0].t], col, alpha);\n\t\tR_PolygonVertex(cp[1].xyz, [cp[1].s,cp[1].t], col, alpha);\n\t\tR_PolygonVertex(cp[1+info.cpwidth].xyz, [cp[1+info.cpwidth].s,cp[1+info.cpwidth].t], col, alpha);\n\t\tR_PolygonVertex(cp[0+info.cpwidth].xyz, [cp[0+info.cpwidth].s,cp[0+info.cpwidth].t], col, alpha);\n#else\n\t\tR_PolygonVertex(cp[0].xyz, [cp[0].s,cp[0].t], cp[0].rgb, cp[0].a);\n\t\tR_PolygonVertex(cp[1].xyz, [cp[1].s,cp[1].t], cp[1].rgb, cp[1].a);\n\t\tR_PolygonVertex(cp[1+info.cpwidth].xyz, [cp[1+info.cpwidth].s,cp[1+info.cpwidth].t], cp[1+info.cpwidth].rgb, cp[1+info.cpwidth].a);\n\t\tR_PolygonVertex(cp[0+info.cpwidth].xyz, [cp[0+info.cpwidth].s,cp[0+info.cpwidth].t], cp[0+info.cpwidth].rgb, cp[0+info.cpwidth].a);\n#endif\n\t\tR_EndPolygon();\n\t}\n};\n\nvoid(vector *p, int points, string shader, vector col, float alpha) DrawAxisExtensions =\n{\n\tR_BeginPolygon(shader);\n\tfor (int point = 0; point < points; point++)\n\t{\n\t\tR_PolygonVertex(p[point] + [ 64, 0, 0], '0 0', col, alpha);\n\t\tR_PolygonVertex(p[point] + [-64, 0, 0], '0 0', col, alpha);\n\t\tR_EndPolygon();\n\t\tR_PolygonVertex(p[point] + [0,  64, 0], '0 0', col, alpha);\n\t\tR_PolygonVertex(p[point] + [0, -64, 0], '0 0', col, alpha);\n\t\tR_EndPolygon();\n\t\tR_PolygonVertex(p[point] + [0, 0,  64], '0 0', col, alpha);\n\t\tR_PolygonVertex(p[point] + [0, 0, -64], '0 0', col, alpha);\n\t\tR_EndPolygon();\n\t}\n};\n\n\nvoid(int brushid) editor_drawbbox =\n{\n\tstatic vector bbox[2];\n\tint p = brush_getfacepoints(selectedbrushmodel, brushid, 0, bbox, bbox.length);\n\tif (p == 2)\n\t{\n\t\tR_BeginPolygon(\"chop\");\n#define line(x,y) \t\t\tR_PolygonVertex(x, '0 0', '1 0 0', 1);\tR_PolygonVertex(y, '0 0', '1 0 0', 1);\tR_EndPolygon()\n\t\tline(bbox[0], ([bbox[1][0], bbox[0][1], bbox[0][2]]));\n\t\tline(bbox[0], ([bbox[0][0], bbox[1][1], bbox[0][2]]));\n\t\tline(bbox[0], ([bbox[0][0], bbox[0][1], bbox[1][2]]));\n\t\tline(bbox[1], ([bbox[0][0], bbox[1][1], bbox[1][2]]));\n\t\tline(bbox[1], ([bbox[1][0], bbox[0][1], bbox[1][2]]));\n\t\tline(bbox[1], ([bbox[1][0], bbox[1][1], bbox[0][2]]));\n\n\t\tline(([bbox[1][0], bbox[0][1], bbox[0][2]]), ([bbox[1][0], bbox[1][1], bbox[0][2]]));\n\t\tline(([bbox[1][0], bbox[0][1], bbox[0][2]]), ([bbox[1][0], bbox[0][1], bbox[1][2]]));\n\t\tline(([bbox[0][0], bbox[1][1], bbox[0][2]]), ([bbox[0][0], bbox[1][1], bbox[1][2]]));\n\t\tline(([bbox[0][0], bbox[1][1], bbox[0][2]]), ([bbox[1][0], bbox[1][1], bbox[0][2]]));\n\t\tline(([bbox[0][0], bbox[0][1], bbox[1][2]]), ([bbox[0][0], bbox[1][1], bbox[1][2]]));\n\t\tline(([bbox[0][0], bbox[0][1], bbox[1][2]]), ([bbox[1][0], bbox[0][1], bbox[1][2]]));\n#undef line\n\t}\n};\n"
  },
  {
    "path": "quakec/csaddon/src/brush_history.qc",
    "content": "//history is implemented using a ringbuffer\ntypedef struct\n{\n\tfloat timestamp;\n\tint brushmodel;\n\tint wasdelete;\n\tint id;\n\tbrushface_t *brushdata;\t//list of faces\n\tpatchvert_t *patchdata;\t//list of control points\n\tunion\n\t{\n\t\tstruct\n\t\t{\n\t\t\tint numfaces;\n\t\t\tint contents;\n\t\t} brushinfo;\n\t\tpatchinfo_t patchinfo;\n\t};\n} history_t;\nhistory_t *historyring;\nint historyring_length;\nint history_min;\t//oldest we can go\nint history;\t\t//updated on each change\nint history_max;\t//max value for redo\n\n//when replaying history, ids get changed, which makes things fun. this updates selection state for edited brushes.\nvoid(int modelidx, int old, int new) history_remap =\n{\n\tfor (int i = history_min; i < history_max; i++)\n\t{\n\t\thistory_t *h = &historyring[i & (historyring_length-1)];\n\t\tif (h->id == old)\n\t\t\th->id = new;\n\t}\n\t\n\tint i = brush_isselected(modelidx, old);\n\tif (i)\n\t{\n\t\ti--;\n\t\tbrush_selected(modelidx, old, -1, FALSE);\n\t\tbrush_selected(modelidx, new, -1, TRUE);\n\t\tselectedbrushes[i].id = new;\n\t}\n};\n\nvoid() brush_undo =\n{\n\tif (history == history_min)\n\t\tprint(\"Nothing to undo.\\n\");\n\tdo\n\t{\n\t\tif (history <= history_min)\n\t\t\treturn;\n\t\thistory--;\n\n\t\thistory_t *h = &historyring[history & (historyring_length-1)];\n\n\t\t//we're undoing, so deletes create and creates delete. weird, yes.\n\t\tif (h->wasdelete)\n\t\t{\n\t\t\tint newid;\n\t\t\tif (h->patchdata)\n\t\t\t\tnewid = patch_create(h->brushmodel, 0, h->patchdata, h->patchinfo);\n\t\t\telse\n\t\t\t\tnewid = brush_create(h->brushmodel, h->brushdata, h->brushinfo.numfaces, h->brushinfo.contents);\n\t\t\thistory_remap(h->brushmodel, h->id, newid);\n\t\t\th->id = newid;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tbrush_delete(h->brushmodel, h->id);\n\t\t\th->id = 0;\n\t\t}\n\t\tif (history == history_min)\n\t\t\tbreak;\t//as far back as we can go, more timestamps are invalid\n\t} while (historyring[(history-1) & (historyring_length-1)].timestamp == h->timestamp);\n};\nvoid() brush_redo =\n{\n\tif (history == history_max)\n\t\tprint(\"Nothing to redo.\");\n\tdo\n\t{\n\t\tif (history >= history_max)\n\t\t\treturn;\n\n\t\thistory_t *h = &historyring[history & (historyring_length-1)];\n\n\t\t//we're redoing stuff that has previously been done. yay.\n\t\tif (h->wasdelete)\n\t\t{\n\t\t\tbrush_delete(h->brushmodel, h->id);\n\t\t\th->id = 0;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tint newid;\n\t\t\tif (h->patchdata)\n\t\t\t\tnewid = patch_create(h->brushmodel, 0, h->patchdata, h->patchinfo);\n\t\t\telse\n\t\t\t\tnewid = brush_create(h->brushmodel, h->brushdata, h->brushinfo.numfaces, h->brushinfo.contents);\n\t\t\thistory_remap(h->brushmodel, h->id, newid);\n\t\t\th->id = newid;\n\t\t}\n\t\thistory++;\n\t\tif (history == history_max)\n\t\t\treturn;\t//don't poke beyond the end...\n\t} while (historyring[history & (historyring_length-1)].timestamp == h->timestamp);\n};\n\nhistory_t *() history_allocate =\t//returns a new history entry.\n{\n\tif (!historyring)\n\t{\n\t\thistory_min = history_max = history;\n\t\thistoryring_length = 512;\n\t\thistoryring = memalloc(sizeof(*historyring)*historyring_length);\n\t}\n\thistory_t *h = &historyring[history & (historyring_length-1)];\n\t\n\thistory++;\n\thistory_max = history;\t//always break any pending redos\n\tif (history_min < history_max - historyring_length)\n\t\thistory_min = history_max - historyring_length;\n\t\n\th->timestamp = time;\n\tmemfree(h->brushdata);\n\th->brushdata = __NULL__;\n\treturn h;\n};\nvoid(history_t *h) history_deallocate =\t//cancels a newly created history entry, in case something went wrong.\n{\n\thistory--;\n\tif (h == &historyring[history & (historyring_length-1)])\n\t\thistory_max--;\n};\n//create and journal.\nint(int mod, brushface_t *faces, int numfaces, int contents, float selectnow) brush_history_create =\n{\n\thistory_t *h = history_allocate();\n\n\th->brushdata = memalloc(sizeof(brushface_t) * numfaces);\n\tmemcpy(h->brushdata, faces, sizeof(brushface_t) * numfaces);\n\th->brushinfo.numfaces = numfaces;\n\th->brushinfo.contents = contents;\n\th->brushmodel = mod;\n\th->wasdelete = FALSE;\n\n\th->id = brush_create(h->brushmodel, h->brushdata, h->brushinfo.numfaces, h->brushinfo.contents);\n\n\tif (!h->id)\n\t\thistory_deallocate(h);\n\telse if (selectnow)\n\t\tbrush_select(mod, h->id);\n\treturn h->id;\n};\n//create and journal.\nint(int mod, patchvert_t *vert, patchinfo_t info, float selectnow) patch_history_create =\n{\n\tint totalcp = info.cpwidth * info.cpheight;\n\thistory_t *h = history_allocate();\n\n\th->patchdata = memalloc(sizeof(patchvert_t) * totalcp);\n\tmemcpy(h->patchdata, vert, sizeof(patchvert_t) * totalcp);\n\th->patchinfo = info;\n\th->brushmodel = mod;\n\th->wasdelete = FALSE;\n\n\th->id = patch_create(h->brushmodel, 0, vert, info);\n\n\tif (!h->id)\n\t\thistory_deallocate(h);\n\telse if (selectnow)\n\t\tbrush_select(mod, h->id);\n\treturn h->id;\n};\n//delete and journal.\nvoid(int mod, int id) brush_history_delete =\n{\n\tint numfaces = brush_get(mod, id, __NULL__, 0, __NULL__);\n\tint numcp = patch_getcp(mod, id, __NULL__, 0, __NULL__);\n\tbrush_deselect(mod, id);\n\n\tif (!numfaces && !numcp)\n\t\treturn;\t//doesn't exist or something, some kind of error. we can't log a delete if it didn't delete anything, if only because we can't recreate it if its undone.\n\t\t\n\thistory_t *h = history_allocate();\n\n\th->brushmodel = mod;\n\th->id = id;\n\th->wasdelete = TRUE;\n\n\tif (numcp)\n\t{\n\t\th->patchdata = memalloc(sizeof(patchvert_t) * numcp);\n\t\tpatch_getcp(mod, id, h->patchdata, numcp, &h->patchinfo);\n\t}\n\telse\n\t{\n\t\th->brushdata = memalloc(sizeof(brushface_t) * numfaces);\n\t\th->brushinfo.numfaces = brush_get(mod, id, h->brushdata, numfaces, &h->brushinfo.contents);\n\t}\n\tbrush_delete(mod, id);\n};\n\nint(int mod, int id, brushface_t *faces, int numfaces, int contents) brush_history_edit =\n{\n\tint wasselected = brush_isselected(mod, id);\n\tif (wasselected)\n\t\tselectedbrushes[wasselected-1].id = -id;\t//hack so that brush_history_delete won't remove this entry.\n\tbrush_history_delete(mod, id);\n\tid = brush_history_create(mod, faces, numfaces, contents, FALSE);\n\tif (wasselected)\n\t{\n\t\tselectedbrushes[wasselected-1].id = id;\n\t\tbrush_selected(mod, id, -1, TRUE);\n\t}\n\treturn id;\n};\nint(int mod, int id, patchvert_t *vert, patchinfo_t info) patch_history_edit =\n{\n\tint wasselected = brush_isselected(mod, id);\n\tif (wasselected)\n\t\tselectedbrushes[wasselected-1].id = -id;\t//hack so that brush_history_delete won't remove this entry.\n\tbrush_history_delete(mod, id);\n\tid = patch_history_create(mod, vert, info, FALSE);\n\tif (wasselected)\n\t{\n\t\tselectedbrushes[wasselected-1].id = id;\n\t\tbrush_selected(mod, id, -1, TRUE);\n\t}\n\treturn id;\n};"
  },
  {
    "path": "quakec/csaddon/src/brush_manip.qc",
    "content": "\n//basic cube plane normals, for selection.\nstatic nosave const vector axis[6] = {\n\t'-1 0 0',\n\t'0 -1 0',\n\t'0 0 -1',\n\t'1 0 0',\n\t'0 1 0',\n\t'0 0 1'\n};\nfloat dists[6];\n\n\n//generates default quakeed-style texture mapping for the given surface.\n//this sucks for cylinders, but keeps slopes and things easy.\nvoid(brushface_t *fa) reset_texturecoords =\n{\n\t//figure out some default texture coords\t\n\tfa->sdir = '0 0 0';\n\tfa->sbias = 0;\n\tfa->tdir = '0 0 0';\n\tfa->tbias = 0;\n\tfloat a=fabs(fa->planenormal[0]),b=fabs(fa->planenormal[1]),c=fabs(fa->planenormal[2]);\n\tif (a>=b&&a>=c)\n\t\tfa->sdir[1] = 1;\n\telse\n\t\tfa->sdir[0] = 1;\n\tif (c>a&&c>b)\n\t\tfa->tdir[1] = -1;\n\telse\n\t\tfa->tdir[2] = -1;\n};\n\nint(brushface_t *fa, int famax, vector *points, int numpoints, float height) BrushFromPoints =\n{\n\tint count = 0;\n\t\n\tfa->planenormal = normalize(crossproduct(points[2] - points[0], points[1] - points[0]));\n\tfa->planedist = bt_point[0] * fa->planenormal + height;\n\tfa->shadername = autocvar_ca_newbrushtexture;\n\treset_texturecoords(fa);\n\tfa++;\n\n\tfa->planenormal = -normalize(crossproduct(points[2] - points[0], points[1] - points[0]));\n\tfa->planedist = (bt_point[0] * fa->planenormal);\n\tfa->shadername = autocvar_ca_newbrushtexture;\n\treset_texturecoords(fa);\n\tfa++;\n\tcount = 2;\n\n\tfor (int p = 0; p < numpoints; p++)\n\t{\n\t\tint n = p + 1;\n\t\tif (n == numpoints)\n\t\t\tn = 0;\n\t\tfa->planenormal = normalize(crossproduct(points[p] - bt_point[n], tmp.faces[0].planenormal));\n\t\tfa->planedist = points[p] * fa->planenormal;\n\t\tfa->shadername = autocvar_ca_newbrushtexture;\n\t\treset_texturecoords(fa);\n\t\tfa++;\n\t\tcount++;\n\t}\n\n\treturn count;\n}\n\n\nvector(vector guess) brush_snappoint =\n{\n\tif (nogrid || autocvar_ca_grid <= 0)\n\t\treturn guess;\n\tint facenum, points;\n\tfloat bestdist = autocvar_ca_grid*autocvar_ca_grid;\t//worst case value so we don't snap to grid when there's a vertex 0.001qu away from the grid.\n\tvector best = guess * (1.0/autocvar_ca_grid);\n\tbest_x = rint(best_x);\t//snap to grid\n\tbest_y = rint(best_y);\n\tbest_z = rint(best_z);\n\tbest *= autocvar_ca_grid;\n\n\t//find surfaces within 32qu of the point (via plane volume). use a tetrahedron instead if you want something more circular\n\tfor (facenum = 0; facenum < axis.length; facenum++)\n\t\tdists[facenum] = (guess * axis[facenum]) + autocvar_ca_grid;\n\tint numbrushes = brush_findinvolume(selectedbrushmodel, axis, dists, 6, brushlist, __NULL__, brushlist.length);\n\n\tfor (int brushnum = 0; brushnum < numbrushes; brushnum++)\n\t{\n\t\tfor (facenum = 0; ; )\n\t\t{\n\t\t\tpoints = brush_getfacepoints(selectedbrushmodel, brushlist[brushnum], ++facenum, facepoints, MAX_FACEPOINTS);\n\t\t\tif (!points)\n\t\t\t\tbreak;\t//end of face list, I guess\n\n\t\t\t//walk the faces we found and use the point if its nearer than our previous guess.\n\t\t\tfor (int point = 0; point < points; point++)\n\t\t\t{\n\t\t\t\tvector disp = facepoints[point] - guess;\n\t\t\t\tfloat dist = disp*disp;\n\t\t\t\tif (dist < bestdist)\n\t\t\t\t\tbest = facepoints[point];\n\t\t\t}\n\t\t}\n\t}\n\n\treturn best;\n};\n\n\n\n//move a brush so that its planes all move without any translations in positions or texcoords\nvoid brushface_translate(vector displacement)\n{\n\tint i;\n\tif (tmp.numcp)\n\t{\n\t\tfor (i = 0; i < tmp.numcp; i++)\n\t\t\ttmp.cp[i].xyz += displacement;\n\t}\n\telse\n\t{\n\t\tfor (i = 0; i < tmp.numfaces; i++)\n\t\t{\n\t\t\ttmp.faces[i].planedist += tmp.faces[i].planenormal * displacement;\n\t\t\ttmp.faces[i].sbias -= tmp.faces[i].sdir * displacement;\n\t\t\ttmp.faces[i].tbias -= tmp.faces[i].tdir * displacement;\n\t\t}\n\t}\n};\n//rotates a list of faces by the current v_* vectors, around the origin.\n//translate before+after first in order to deal with pivots.\nvoid brushface_rotate(void)\n{\n\tif (tmp.numcp)\n\t{\n\t\tfor (int i = 0; i < tmp.numcp; i++)\n\t\t{\n\t\t\tvector orig = tmp.cp[i].xyz;\n\t\t\ttmp.cp[i].xyz = [orig * v_forward,\n\t\t\t\t\torig * -v_right,\n\t\t\t\t\torig * v_up];\n\t\t\t//don't need to touch tcs\n\t\t}\n\t}\n\telse\n\t{\n\t\tbrushface_t *fa = tmp.faces;\n\t\tint numfaces = tmp.numfaces;\n\t\tfor (int i = 0; i < numfaces; i++, fa++)\n\t\t{\n\t\t\tvector orig = fa->planenormal;\n\t\t\tfa->planenormal[0] = orig * v_forward;\n\t\t\tfa->planenormal[1] = orig * -v_right;\t//quake just isn't right...\n\t\t\tfa->planenormal[2] = orig * v_up;\n\n\t\t\torig = fa->sdir;\n\t\t\tfa->sdir[0] = orig * v_forward;\n\t\t\tfa->sdir[1] = orig * -v_right;\t//quake just isn't right...\n\t\t\tfa->sdir[2] = orig * v_up;\n\n\t\t\torig = fa->tdir;\n\t\t\tfa->tdir[0] = orig * v_forward;\n\t\t\tfa->tdir[1] = orig * -v_right;\t//quake just isn't right...\n\t\t\tfa->tdir[2] = orig * v_up;\n\t\t}\n\t}\n};\n\nvector(vector dir) axialize =\n{\n\tvector a;\n\ta_x = fabs(dir_x);\n\ta_y = fabs(dir_y);\n\ta_z = fabs(dir_z);\n\tif (a_x > a_y && a_x > a_z)\n\t\treturn (dir_x > 0)?[1,0,0]:[-1,0,0];\n\tif (a_y > a_x && a_y > a_z)\n\t\treturn (dir_y > 0)?[0,1,0]:[0,-1,0];\n\treturn (dir_z > 0)?[0,0,1]:[0,0,-1];\n};\n\nvector(vector in) channelizeangle =\n{\n\tin_x = anglemod(in_x);\n\tin_y = anglemod(in_y);\n\tin_z = anglemod(in_z);\n\tif (in_x > 180)\n\t\tin_x -= 360;\n\tif (in_y > 180)\n\t\tin_y -= 360;\n\tif (in_z > 180)\n\t\tin_z -= 360;\n\n\tfloat fx = fabs(in_x);\n\tfloat fy = fabs(in_y);\n\tfloat fz = fabs(in_z);\n\tcprint(sprintf(\"%v\", in));\n\tif (fx > fy && fx > fz)\n\t\treturn [in_x,0,0];\n\tif (fy > fz)\n\t\treturn [0,in_y,0];\n\treturn [0,0,in_z];\n}\n\nvector(vector p1, vector p2, vector norm, float dist) planelinepoint =\n{\n\tfloat d1 = p1*norm - dist;\n\tfloat d2 = p2*norm - dist;\n\tfloat frac = d1 / (d2-d1);\n\n//\tfrac = bound(0, frac, 1);\n\n\treturn p2 + (p1-p2)*frac;\t//convert that frac into an actual position\n};\n\n\nint(brushface_t *faces, int numfaces, vector *points, int numpoints) isconcave =\n{\n\tint result = 0;\n\n\t//if any of the points are outside the brush, then we know that one of the planes cut straight through in a concavey kind of way\n\tfor(int f = 0; f < numfaces; f++)\n\t{\n\t\tvector n = faces[f].planenormal;\n\t\tfloat d = faces[f].planedist + EPSILON; //epsilon to cover precision issues\n\t\tfor (int p = 0; p < numpoints; p++)\n\t\t{\n\t\t\tif (points[p] * n > d)\n\t\t\t{\n\t\t\t\tresult++;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn result;\n};\n\n//returns the resulting brush id\nint(int model, int brush1, int brush2, int face1, int face2) mergebrushes =\n{\n\tint extrafaces;\n\tfloat found = FALSE;\n\tbrushface_t *fa;\n\tbrushface_t *infa;\n\tint i;\n\n\tif (brush1 == brush2)\n\t{\n\t\tprint(\"cannot merge brush with itself\\n\");\n\t\treturn 0;\n\t}\n\tif (!brush1 || !brush2)\n\t{\n\t\tprint(\"no brush targetted\\n\");\n\t\treturn 0;\n\t}\n\n\tif (patch_getcp(model, brush1, __NULL__, 0, __NULL__) || patch_getcp(model, brush2, __NULL__, 0, __NULL__))\n\t{\t//fixme: should be possible if they're reasonably adjacent with the same numbers of CPs\n\t\tprint(\"unable to merge patches\\n\");\n\t\treturn 0;\n\t}\n\n\ttmp.numfaces = brush_get(model, brush1, tmp.faces, tmp.faces.length, &tmp.contents);\n\ttmp.numcp = 0;\n\tinfa = &tmp.faces[tmp.numfaces];\n\textrafaces = brush_get(model, brush2, &tmp.faces[tmp.numfaces], tmp.faces.length-tmp.numfaces, &tmp.contents);\n\n\t//merge the two sets of planes together.\n\tfor(infa = &tmp.faces[tmp.numfaces]; extrafaces > 0; infa++, extrafaces--)\n\t{\n\t\tfor (fa = tmp.faces, i = 0; i < tmp.numfaces; i++, fa++)\n\t\t{\n//fixme: needs some tolerance / epsilon\n\t\t\tif (fa->planenormal == -infa->planenormal && fa->planedist == -infa->planedist)\n\t\t\t{\n\t\t\t\t//inverted. this is the plane we're merging over, so strip it out\n\t\t\t\tmemcpy(fa, &fa[1], sizeof(brushface_t) * ((tmp.numfaces-i)-1));\n\t\t\t\ttmp.numfaces--;\n\t\t\t\ti--;\n\t\t\t\tfa--;\n\t\t\t\tfound = TRUE;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\telse if (fa->planenormal * infa->planenormal > 0.999 && fa->planedist == infa->planedist)\n\t\t\t{\n//print(\"coplanar\\n\");\n\t\t\t\t//coplanar surfaces are considered safe to ignore. we use the selected brush's texturing info\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (i == tmp.numfaces)\n\t\t{\t//okay, this plane is important, merge it into the selected brush.\n//print(\"merge plane\\n\");\n\t\t\tmemcpy(fa, infa, sizeof(brushface_t));\n\t\t\ttmp.numfaces++;\n\t\t}\n\t}\n\tif (!found)\n\t{\n\t\tprint(\"Brushes do not share a plane\\n\");\n#if 0\n\t\t//try to find a surface to move to match to the given plane\n\t\tfloat val;\n\t\tfloat bestval = -0.707;\t//must be within 45 degrees\n\t\tint bestface = -1;\n\t\ttmp_numfaces = brush_get(model, brush1, tmp_faces, tmp_faces.length, &tmp_contents);\n\t\tbrush_get(model, brush2, &tmp_faces[tmp_numfaces], tmp_faces.length-tmp_numfaces, &tmp_contents);\n\t\tinfa = &tmp_faces[tmp_numfaces + face2-1i];\n\t\tfor (i = 0; i < tmp_numfaces; i++)\n\t\t{\n\t\t\tval = tmp_faces[i].planenormal * infa->planenormal;\n\t\t\tif (val < bestval)\n\t\t\t{\n\t\t\t\tbestval = val;\n\t\t\t\tbestface = i;\n\t\t\t}\n\t\t}\n\t\tif (bestface != -1)\n\t\t{\n\t\t\t//FIXME: determine volume and reject it if we shrink?\n\t\t\ttmp_faces[bestface].planenormal = -infa->planenormal;\n\t\t\ttmp_faces[bestface].planedist = -infa->planedist;\n\n\t\t//\tif (isconcave(tmp_faces, tmp_numfaces))\n\t\t//\t{\n\t\t//\t\tprint(\"Resulting brush would be concave\\n\");\n\t\t//\t\treturn 0;\n\t\t//\t}\n\n\t\t\tbrush_history_delete(model, brush1);\n\t\t\treturn brush_history_create(model, tmp_faces, tmp_numfaces, tmp_contents);\n\t\t}\n\t\telse\n#endif\n\t\t\treturn 0;\n\t}\n\telse\n\t{\n\t\tvector *points = memalloc(sizeof(vector)*64*64);\n\t\tint numpoints = 0, f;\n\t\tfor(f = 0; (i = brush_getfacepoints(model, brush1, ++f, points+numpoints, 64*64-numpoints)); )\n\t\t\tnumpoints += i;\n\t\tfor(f = 0; (i = brush_getfacepoints(model, brush2, ++f, points+numpoints, 64*64-numpoints)); )\n\t\t\tnumpoints += i;\n\n\t\tif (isconcave(tmp.faces, tmp.numfaces, points, numpoints))\n\t\t{\n\t\t\tprint(\"Resulting brush would be concave\\n\");\n\t\t\tmemfree(points);\n\t\t\treturn 0;\n\t\t}\n\t\tmemfree(points);\n\n\t//FIXME: verify that no planes got dropped, as this indicates that the region wasn't convex, and we probably just destroyed the entire thing.\n\t\tbrush_history_delete(model, brush1);\n\t\tbrush_history_delete(model, brush2);\n\n\t\treturn brush_history_create(model, tmp.faces, tmp.numfaces, tmp.contents, TRUE);\n\t}\n};\n\n\n\n\n/*\nvoid(vector org, vector ang) editor_brushes_simpleclone =\n{\n\tvector midpoint;\n\tif (!selectedbrush)\n\t\treturn;\n\n\ttmp.numfaces = brush_get(selectedbrushmodel, selectedbrush, tmp.faces, tmp.faces.length, &tmp.contents);\n\tif (ang != '0 0 0')\n\t{\n\t\tbrush_getfacepoints(selectedbrushmodel, selectedbrush, 0, &midpoint, 1);\n\t\tbrushface_translate(tmp.faces, tmp_numfaces, -midpoint);\n\t\tmakevectors(ang);\n\t\tbrushface_rotate(tmp_faces, tmp_numfaces);\n\t\tbrushface_translate(tmp.faces, tmp_numfaces, midpoint + org);\n\t}\n\telse\n\t\tbrushface_translate(tmp.faces, tmp_numfaces, org);\n\tbrush_history_create(selectedbrushmodel, tmp.faces, tmp.numfaces, tmp.contents, TRUE);\n};\n*/\n\nvoid() brushedit_subtract =\n{\n\tstatic int discard;\n\tstatic vector selnormals[tmp.faces.length];\n\tstatic float seldists[tmp.faces.length];\n\t\n\tfor (int sb = 0; sb < selectedbrushcount; sb++)\n\t{\n\t\tint mod = selectedbrushes[sb].model;\n\t\tint brush = selectedbrushes[sb].id;\n\t\tint planecount = brush_get(mod, brush, tmp.faces, tmp.faces.length, &tmp.contents);\n\t\tif (!planecount)\n\t\t\tcontinue; //csg can't subtract patches\n\t\tfor (int i = 0; i < planecount; i++)\n\t\t{\n\t\t\tselnormals[i] = tmp.faces[i].planenormal;\n\t\t\tseldists[i] = tmp.faces[i].planedist;\n\t\t}\n\t\tint numbrushes = brush_findinvolume(mod, selnormals, seldists, planecount, brushlist, __NULL__, brushlist.length);\n\n\t\twhile (numbrushes --> 0)\n\t\t{\n\t\t\tint br = brushlist[numbrushes];\n\t\t\t\n\t\t\tif (brush_isselected(mod, br))\n\t\t\t\tcontinue;\t//ignore other selected brushes. race conditions suck\n\n\t\t\tint counto = brush_get(mod, br, tmp.faces, tmp.faces.length, &tmp.contents);\n\t\t\tif (!counto)\n\t\t\t\tcontinue; //don't cut into patches.\n\t\t\tint counts = counto + brush_get(mod, brush, tmp.faces+counto, tmp.faces.length-counto, &discard);\n\n\t\t\tbrush_history_delete(mod, br);\n\t\t\twhile(counts --> counto)\n\t\t\t{\n\t\t\t\t//only consider the resulting brush if the new face actually contributed anything.\n\t\t\t\t//this reduces dupes.\n\t\t\t\tif (brush_calcfacepoints(1+counts, tmp.faces, counts+1, facepoints, MAX_FACEPOINTS))\n\t\t\t\t{\n\t\t\t\t\t//determine the brush defined by this plane\n\t\t\t\t\ttmp.faces[counts].planenormal *= -1;\n\t\t\t\t\ttmp.faces[counts].planedist *= -1;\n\t\t\t\t\tbrush_history_create(mod, tmp.faces, counts+1, tmp.contents, FALSE);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n};\n\nvoid() brushedit_resettextures =\n{\n\tfor (int sb = 0; sb < selectedbrushcount; sb++)\n\t{\n\t\tint model = selectedbrushes[sb].model;\n\t\tint brush = selectedbrushes[sb].id;\n\t\t__uint64 facemask = selectedbrushes[sb].facemask;\n\t\t\n\t\tint planecount = brush_get(model, brush, tmp.faces, tmp.faces.length, &tmp.contents);\t\t\n\t\tif (planecount)\n\t\t{\n\t\t\tfor (int i = 0; i < planecount; i++)\n\t\t\t\tif (facemask & (1lu << i))\n\t\t\t\t\treset_texturecoords(&tmp.faces[i]);\n\n\t\t\tbrush_history_edit(model, brush, tmp.faces, planecount, tmp.contents);\n\t\t}\n\t}\n};\n\nvoid(string newtexture) brushedit_settexture =\n{\n\tfor (int sb = 0; sb < selectedbrushcount; sb++)\n\t{\n\t\tint model = selectedbrushes[sb].model;\n\t\tint brush = selectedbrushes[sb].id;\n\t\t__uint64 facemask = selectedbrushes[sb].facemask;\n\t\t\n\t\tint planecount = brush_get(model, brush, tmp.faces, tmp.faces.length, &tmp.contents);\t\t\n\t\tif (planecount)\n\t\t{\n\t\t\tfor (int i = 0; i < planecount; i++)\n\t\t\t\tif (facemask & (1lu << i))\n\t\t\t\t\ttmp.faces[i].shadername = newtexture;\n\n\t\t\tbrush_history_edit(model, brush, tmp.faces, planecount, tmp.contents);\n\t\t}\n\t}\n};"
  },
  {
    "path": "quakec/csaddon/src/brush_selection.qc",
    "content": "var float \tautocvar_ca_brush_view = 0;\t\t//0=normal, 1=x, 2=y, 3=z\nvar float \tautocvar_ca_brush_viewsize = 1024;\t//for different views.\nvar string\tautocvar_ca_newbrushtexture = \"metal4_2\";\nvar float\tautocvar_ca_newbrushheight = 64;\nvar float\tautocvar_ca_grid = 16;\nvar float\tautocvar_ca_explicitpatch = 0;\nvar float\tautocvar_ca_cpwidth = 2;\nvar float\tautocvar_ca_cpheight = 2;\n\n\n#define EPSILON (1.0 / 32)\t//inprecision sucks.\n\n#define MAX_FACEPOINTS 64\nvector facepoints[MAX_FACEPOINTS];\t//for temp usage\n\n#define MAX_BRUSHFACES 64\nstruct\n{\n\t//brush state\n\tbrushface_t faces[MAX_BRUSHFACES];\n\tint numfaces;\n\tint contents;\n\n\t//patch state\n\tpatchvert_t *cp;\n\tint numcp, maxcp;\n\tpatchinfo_t patchinfo;\n} tmp;\n\n\n\n//int selected;\nint brushlist[2048];\n\nenum : int\n{\n\tBT_NONE,\t//selection\n\tBT_MOVE = BT_NONE,\n\tBT_ROTATE,\n\tBT_MERGE,\n\tBT_PUSHFACE,\n\tBT_CREATEBRUSH,\n\tBT_CREATEPATCH,\n\tBT_CREATEDRAG,\n\tBT_CLONEDISPLACE,\n\tBT_SLICE,\n\tBT_MOVETEXTURE,\n\tBT_VERTEXEDIT\n};\nint mousetool;\nint brushtool;\nint bt_points;\nvector bt_displace;\nvector bt_point[64];\nint nogrid;\t//+nogrid, temporarily disables grid locks\n\n\n\n\n\ntypedef struct\n{\n\tint model;\n\tint id;\n\t__uint64 facemask;\t//MAX_BRUSHFACES is 64... handy.\n} selbrush_t;\nselbrush_t *selectedbrushes;\nint selectedbrushcount;\n\nvar int selectedbrushmodel = 1;\t//by default, the worldmodel. this is tracked to know which submodel to insert new brushes into.\n\nint(int modelidx, int brushid) brush_isselected =\t//0 for faceid means 'all'\n{\n\tfor (int i = 0; i < selectedbrushcount; i++)\n\t\tif (selectedbrushes[i].id == brushid)\n\t\t\tif (selectedbrushes[i].model == modelidx)\n\t\t\t\treturn i+1;\n\treturn 0;\n};\nint(int modelidx, int brushid, int faceid) brushface_isselected =\t//0 for faceid means 'all'\n{\n\tfor (int i = 0; i < selectedbrushcount; i++)\n\t\tif (selectedbrushes[i].id == brushid)\n\t\t\tif (selectedbrushes[i].model == modelidx)\n\t\t\t\treturn !!(selectedbrushes[i].facemask&(1ull<<(faceid-1i)));\n\treturn 0;\n};\nint(int modelidx, int brushid) brush_deselect =\n{\n\tint i = brush_isselected(modelidx, brushid);\n\tif (!i)\n\t\treturn FALSE;\n\tbrush_selected(modelidx, brushid, -1, FALSE);\n\tmemcpy(&selectedbrushes[i-1], &selectedbrushes[i], sizeof(selbrush_t)*(selectedbrushcount-i));\n\tselectedbrushcount--;\n\treturn TRUE;\n};\nint(int modelidx, int brushid, int face) brushface_deselect =\n{\n\t//deselecting a single face\n\tint i = brush_isselected(modelidx, brushid);\t//get the brush selection index\n\tif (!i)\n\t\treturn FALSE;\n\ti--;\n\tface--;\n\tif (!selectedbrushes[i].facemask)\t//no faces means all..\n\t\tselectedbrushes[i].facemask = ~0ul;\n\tselectedbrushes[i].facemask &= ~(1ul<<face);\n\tif (!selectedbrushes[i].facemask)\n\t{\t//no faces left selected.\n\t\tbrush_selected(modelidx, brushid, -1, FALSE);\n\t\tmemcpy(&selectedbrushes[i], &selectedbrushes[i+1], sizeof(selbrush_t)*(selectedbrushcount-i));\n\t\tselectedbrushcount--;\n\t}\n\treturn TRUE;\n};\nvoid() brush_deselectall =\n{\n\tfor (int i = 0; i < selectedbrushcount; i++)\n\t\tbrush_selected(selectedbrushes[i].model, selectedbrushes[i].id, -1, FALSE);\n\n\tselectedbrushcount = 0;\n};\nvoid(int modelidx, int brushid) brush_select =\n{\n\tif (!brush_isselected(modelidx, brushid))\n\t{\n\t\tselbrush_t *n = memalloc(sizeof(selbrush_t) * (selectedbrushcount+1));\n\t\tmemcpy(n, selectedbrushes, sizeof(selbrush_t)*selectedbrushcount);\n\t\tmemfree(selectedbrushes);\n\t\tselectedbrushes = n;\n\t\tn[selectedbrushcount].model = modelidx;\n\t\tn[selectedbrushcount].id = brushid;\n\t\tn[selectedbrushcount].facemask = ~0ul;\n\t\tselectedbrushcount++;\n\t\tbrush_selected(modelidx, brushid, -1, TRUE);\n\t}\n\tselectedbrushmodel = modelidx;\n};\nvoid(int modelidx, int brushid, int faceid) brushface_select =\n{\n\tfaceid--;\n\tint i = brush_isselected(modelidx, brushid);\t//get the brush selection index\n\tif (!i)\n\t{\t//brush not selected yet\n\t\tselbrush_t *n = memalloc(sizeof(selbrush_t) * (selectedbrushcount+1));\n\t\tmemcpy(n, selectedbrushes, sizeof(selbrush_t)*selectedbrushcount);\n\t\tmemfree(selectedbrushes);\n\t\tselectedbrushes = n;\n\t\tn[selectedbrushcount].model = modelidx;\n\t\tn[selectedbrushcount].id = brushid;\n\t\tn[selectedbrushcount].facemask = 0;\n\t\tselectedbrushcount++;\n\t\tbrush_selected(modelidx, brushid, -1, TRUE);\n\t\ti = selectedbrushcount;\n\t}\n\tselectedbrushes[i-1].facemask |= 1ul<<faceid;\n\tselectedbrushmodel = modelidx;\n};"
  },
  {
    "path": "quakec/csaddon/src/brush_vertedit.qc",
    "content": "\ntypedef struct\n{\n\tint numverts;\n\tint numidx;\n\tvector *p;\n\tint *i;\n\t\n\tint selectedvert1;\n\tint selectedvert2;\n\t\n\tint model;\n\tint brush;\n} vertsoup_t;\nvertsoup_t vertedit;\n\nstatic patchinfo_t patchinfo;\nstatic patchvert_t *patchvert;\nstatic int maxcp = 64*64;\n\n\n//take a brush apart and generate trisoup from it.\n//note that the trisoup does not have textures. they will be reconciled when reforming the brush.\nvoid(vertsoup_t *vertedit, int model, int brush) Debrushify =\n{\n\tint *ni;\n\tint i, j, k;\n\tint points;\n\tvector p;\n\tvector *np;\n\tstatic int fi[64];\n\tvertedit->model = model;\n\tvertedit->brush = brush;\n\tvertedit->numidx = 0;\n\tvertedit->numverts = patch_getcp(vertedit->model, vertedit->brush, __NULL__, 0, __NULL__);\n\tif (vertedit->numverts)\n\t{\t//okay, this is a patch. one logical face.\n\t\tmemfree(vertedit->i);\n\t\tmemfree(vertedit->p);\n\n\t\tvertedit->p = memalloc(sizeof(*vertedit->p) * vertedit->numverts);\n\n\t\tif (!patchvert)\n\t\t\tpatchvert = memalloc(sizeof(*patchvert)*maxcp);\n\t\tvertedit->numverts = patch_getcp(vertedit->model, vertedit->brush, patchvert, maxcp, &patchinfo);\n\t\tfor (i = 0; i < vertedit->numverts; i++)\n\t\t\tvertedit->p[i] = patchvert[i].xyz;\n\n\t\t//some of the rest of the code assumes we have indexes. oh well.\n\t\tk = (patchinfo.cpwidth-1)*(patchinfo.cpheight-1);\n\t\tvertedit->numidx = k*6;\n\t\tvertedit->i = ni = memalloc(sizeof(*ni) * vertedit->numidx);\n\t\tfor (i = 0; i < k; i++, ni+=6)\n\t\t{\n\t\t\tni[0] = i;\n\t\t\tni[1] = i+1;\n\t\t\tni[2] = i+patchinfo.cpwidth;\n\n\t\t\tni[3] = i+1;\n\t\t\tni[4] = i+patchinfo.cpwidth+1;\n\t\t\tni[5] = i+patchinfo.cpwidth;\n\t\t}\n\t\treturn;\n\t}\n\n\t//brush.\n\tfor (i = 0; ; )\n\t{\n\t\tpoints = brush_getfacepoints(vertedit->model, vertedit->brush, ++i, facepoints, facepoints.length);\n\t\tif (!points)\n\t\t\tbreak;\n\t\t\n\t\t//allocate a few new indexes\n\t\tni = memalloc(sizeof(*ni) * (vertedit->numidx+3*(points-2)));\n\t\tmemcpy(ni, vertedit->i, sizeof(*ni) * vertedit->numidx);\n\t\tmemfree(vertedit->i);\n\t\tvertedit->i = ni;\n\t\tni += vertedit->numidx;\n\t\tvertedit->numidx += 3*(points-2);\n\n\t\tfor (j = 0; j < points; j++)\n\t\t{\n\t\t\tp = facepoints[j];\n\t\t\tp_x = floor(p_x + 0.5);\t//gah, bloomin inprecision.\n\t\t\tp_y = floor(p_y + 0.5);\n\t\t\tp_z = floor(p_z + 0.5);\n\t\t\tfor (k = 0; k < vertedit->numverts; k++)\n\t\t\t{\t//try to be nice and re-use verts.\n\t\t\t\tif (vertedit->p[k] == p)\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (k == vertedit->numverts)\n\t\t\t{\n\t\t\t\t//if it wasn't found, we need to allocate a new one\n\t\t\t\tnp = memalloc(sizeof(*np) * (vertedit->numverts+1));\n\t\t\t\tmemcpy(np, vertedit->p, sizeof(*np) * vertedit->numverts);\n\t\t\t\tmemfree(vertedit->p);\n\t\t\t\tvertedit->p = np;\n\t\t\t\tnp += vertedit->numverts;\n\t\t\t\tvertedit->numverts += 1;\n\t\t\t\t*np = p;\n\t\t\t}\n\t\t\tfi[j] = k;\n\t\t}\n\t\tfor (j = 2; j < points; j++)\n\t\t{\n\t\t\t*ni++ = fi[0];\n\t\t\t*ni++ = fi[j-1];\n\t\t\t*ni++ = fi[j];\n\t\t}\n\t}\n};\n\n//determines only the various points of the brush, without duplicates. doesn't care about indexes.\nvoid(void) DebrushifyLite =\n{\n\tint points, k;\n\tvector p;\n\tvector *np;\n\tint fi[64];\n\tvertedit.numidx = 0;\n\n\tvertedit->numverts = patch_getcp(vertedit->model, vertedit->brush, __NULL__, 0, __NULL__);\n\tif (vertedit->numverts)\n\t{\t//okay, this is a patch. one logical face.\n\t\tmemfree(vertedit->i);\n\t\tmemfree(vertedit->p);\n\n\t\tvertedit->i = __NULL__;\t//don't bother.\n\t\tvertedit->p = memalloc(sizeof(*vertedit->p) * vertedit->numverts);\n\n\t\tif (!patchvert)\n\t\t\tpatchvert = memalloc(sizeof(*patchvert)*maxcp);\n\n\t\tvertedit->numverts = patch_getcp(vertedit->model, vertedit->brush, patchvert, maxcp, &patchinfo);\n\n\t\tfor (int i = 0; i < vertedit->numverts; i++)\n\t\t\tvertedit->p[i] = patchvert[i].xyz;\n\t\treturn;\n\t}\n\n\tfor (int i = 0; ; )\n\t{\n\t\tpoints = brush_calcfacepoints(++i, tmp.faces, tmp.numfaces, facepoints, facepoints.length);\n\t\tif (!points)\n\t\t\tbreak;\n\t\t\n\t\tfor (int j = 0; j < points; j++)\n\t\t{\n\t\t\tp = facepoints[j];\n\t\t\tp_x = floor(p_x + 0.5);\t//gah, bloomin inprecision.\n\t\t\tp_y = floor(p_y + 0.5);\n\t\t\tp_z = floor(p_z + 0.5);\n\t\t\tfor (k = 0; k < vertedit.numverts; k++)\n\t\t\t{\t//try to be nice and re-use verts.\n\t\t\t\tif (vertedit.p[k] == p)\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (k == vertedit.numverts)\n\t\t\t{\n\t\t\t\t//if it wasn't found, we need to allocate a new one\n\t\t\t\tnp = memalloc(sizeof(*np) * (vertedit.numverts+1));\n\t\t\t\tmemcpy(np, vertedit.p, sizeof(*np) * vertedit.numverts);\n\t\t\t\tmemfree(vertedit.p);\n\t\t\t\tvertedit.p = np;\n\t\t\t\tnp += vertedit.numverts;\n\t\t\t\tvertedit.numverts += 1;\n\t\t\t\t*np = p;\n\t\t\t}\n\t\t\tfi[j] = k;\n\t\t}\n\t}\n};\n\n\n\n\nstatic float(vertsoup_t *soup, int *idx, __out vector norm, __out float dist) planenormal =\n{\n\tvector v1 = soup->p[idx[0]];\n\tvector v2 = soup->p[idx[1]];\n\tvector v3 = soup->p[idx[2]];\n\tvector d1 = v3 - v1;\n\tvector d2 = v2 - v1;\n\tvector d3 = v3 - v2;\n\tnorm = normalize(crossproduct(d1, d2));\n\tdist = norm * v1;\n\n\tif (!d1 || !d2 || !d3 || !norm)\n\t\treturn FALSE;\n\treturn TRUE;\n};\n\nvoid(vertsoup_t *soup, int drawit) Rebrushify =\n{\n\tstatic brushface_t faces[MAX_BRUSHFACES];\n\tint numfaces, f, point;\n\tstring shader = 0;\n\t\n\tvector n;\n\tfloat d;\n\tint o=0;\n\t\t\n\tf = patch_getcp(soup->model, soup->brush, patchvert, maxcp, &patchinfo);\n\tif (f)\n\t{\n\t\tfor (o = 0; o < f; o++)\n\t\t\tpatchvert[o].xyz = soup.p[o];\n\t\tif (drawit)\n\t\t{\n\t\t\tconst int maxtessverts=64*64;\n\t\t\tstatic patchinfo_t tessinfo;\n\t\t\tpatchvert_t *tessverts = memalloc(sizeof(*tessverts)*maxtessverts);\n\t\t\ttessinfo = patchinfo;\n\t\t\tif (patch_evaluate(patchvert, tessverts, maxtessverts, &tessinfo) <= maxtessverts)\n\t\t\t{\n\t\t\t\t//draw textured preview (yay for fullbright lighting being overbright)\n\t\t\t\tDrawQCPatchTextured(tessverts, tessinfo, '0.7 0.7 0.7', 0.7);\t\t//display the patch. in-place so you know what it'll actually look like. stoopid tessellation.\n\t\t\t\tDrawQCPatchWireframe(tessverts, tessinfo, \"chop\", [0.3,0,0.3], 1);\t//show a somewhat faded indication of how many quads are actually being used.\n\t\t\t}\n\t\t\tmemfree(tessverts);\n\t\t\t\n\t\t\t//draw it wireframe without depth testing\n\t\t\tDrawQCPatchWireframe(patchvert, patchinfo, \"chop\", [0,0,1], 1);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tpatch_history_edit(soup->model, soup->brush, patchvert, patchinfo); \n\t\t\t\n\t\t\tsoup->numidx = 0;\n\t\t\tsoup->numverts = 0;\n\t\t}\t\n\t\treturn;\n\t}\n\t\n\ttmp.numfaces = brush_get(soup->model, soup->brush, tmp.faces, tmp.faces.length, &tmp.contents);\n\t\n\t//if any triangle's neighbour opposes it, reform the edge across the quad to try to keep it convex.\n\tfor(point = 0; point+2 < soup->numidx; point+=3)\n\t{\n\t\tint p1 = soup->i[point+0];\n\t\tint p2 = soup->i[point+1];\n\t\tint p3 = soup->i[point+2];\n\t\tif (!planenormal(soup, &soup->i[point], n, d))\n\t\t\tcontinue;\t//degenerate\n\t\td += EPSILON;\n\t\tfor(f = point+3; f+2 < soup->numidx; f+=3)\n\t\t{\n\t\t\tint o1 = soup->i[f+0];\n\t\t\tint o2 = soup->i[f+1];\n\t\t\tint o3 = soup->i[f+2];\np1p2edge:\n\t\t\tif (o2 == p1 && o1 == p2)\n\t\t\t\to = o3;\n\t\t\telse if (o3 == p1 && o2 == p2)\n\t\t\t\to = o1;\n\t\t\telse if (o1 == p1 && o3 == p2)\n\t\t\t\to = o2;\n\t\t\telse\n\t\t\t\tgoto p2p3edge;\n\t\t\tif (soup->p[o] * n > d)\n\t\t\t{\n\t\t\t\tsoup->i[f+0] = p3;\n\t\t\t\tsoup->i[f+1] = o;\n\t\t\t\tsoup->i[f+2] = p2;\n\t\t\t\tp2 = o;\n\t\t\t}\n\t\t\tcontinue;\np2p3edge:\n\t\t\tif (o2 == p2 && o1 == p3)\n\t\t\t\to = o3;\n\t\t\telse if (o3 == p2 && o2 == p3)\n\t\t\t\to = o1;\n\t\t\telse if (o1 == p2 && o3 == p3)\n\t\t\t\to = o2;\n\t\t\telse\n\t\t\t\tgoto p3p1edge;\n\t\t\tif (soup->p[o] * n > d)\n\t\t\t{\n\t\t\t\tsoup->i[f+0] = p1;\n\t\t\t\tsoup->i[f+1] = o;\n\t\t\t\tsoup->i[f+2] = p3;\n\t\t\t\tp3 = o;\n\t\t\t}\n\t\t\tcontinue;\np3p1edge:\n\t\t\tif (o2 == p3 && o1 == p1)\n\t\t\t\to = o3;\n\t\t\telse if (o3 == p3 && o2 == p1)\n\t\t\t\to = o1;\n\t\t\telse if (o1 == p3 && o3 == p1)\n\t\t\t\to = o2;\n\t\t\telse\n\t\t\t\tcontinue;\n\t\t\tif (soup->p[o] * n > d)\n\t\t\t{\n\t\t\t\tsoup->i[f+0] = p2;\n\t\t\t\tsoup->i[f+1] = o;\n\t\t\t\tsoup->i[f+2] = p1;\n\t\t\t\tp1 = o;\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\t\n\t\tsoup->i[point+0] = p1;\n\t\tsoup->i[point+1] = p2;\n\t\tsoup->i[point+2] = p3;\n\t}\n\t\n\t//generate the plane info\n\tnumfaces = 0;\n\tfor(point = 0; point+2 < soup->numidx; point+=3)\n\t{\n\t\tif (!planenormal(soup, &soup->i[point], n, d))\n\t\t\tcontinue;\t//a degenerate triangle is one that probably got merged or something\n\n\t\tfor (f = 0; f < numfaces; f++)\n\t\t{\n\t\t\tif (faces[f].planenormal == n && faces[f].planedist == d)\n\t\t\t\tbreak;\n\t\t}\n\t\tif (f < numfaces)\n\t\t\tcontinue;\t//duplicate plane\n\t\t\t\n\t\tfor (f = 0; f < tmp.numfaces; f++)\n\t\t{\n\t\t\tif (tmp.faces[f].planenormal * n > 0.999) //stupid inprecisions\n\t\t\t{\n\t\t\t\tif (numfaces == 64)\n\t\t\t\t\treturn;\n\t\t\t\t\t\n\t\t\t\t//note that we only care about the normal, not the dist. this means you can move the entire face forward+back without loosing textures.\n\t\t\t\tfaces[numfaces].shadername = shader = tmp.faces[f].shadername;\n\t\t\t\tfaces[numfaces].planenormal = n;\n\t\t\t\tfaces[numfaces].planedist = d;\n\t\t\t\tfaces[numfaces].sdir = tmp.faces[f].sdir;\n\t\t\t\tfaces[numfaces].sbias = tmp.faces[f].sbias;\n\t\t\t\tfaces[numfaces].tdir = tmp.faces[f].tdir;\n\t\t\t\tfaces[numfaces].tbias = tmp.faces[f].tbias;\n\t\t\t\tnumfaces++;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (f < tmp.numfaces)\n\t\t\tcontinue;\t//matched a plane in the original brush\n\t\t\t\n\t\tif (numfaces == 64)\n\t\t\treturn;\n\t\t\t\n\t\t//FIXME: find aproximate faces to give corners or something\n\n\t\t//okay, it appears to be new. that's a pain.\n\t\tfaces[numfaces].shadername = 0;\n\t\tfaces[numfaces].planenormal = n;\n\t\tfaces[numfaces].planedist = d;\n\t\treset_texturecoords(&faces[numfaces]);\n\t\tnumfaces++;\n\t}\n\t\n\t//any surface without a texture/shader yet should inherit one from some other surface\n\tif (!shader)\n\t\tshader = autocvar_ca_newbrushtexture;\n\tfor(f = 0; f < numfaces; f++)\n\t{\n\t\tif (!faces[f].shadername)\n\t\t\tfaces[f].shadername = shader;\n\t}\n\n\tint internals = 0;\n\t//If we have a point outside a plane, then we know we have a concave volume.\n\t//chop the points \n\tfor(f = 0; f < numfaces; f++)\n\t{\n\t\tn = faces[f].planenormal;\n\t\td = faces[f].planedist;\n\t\tfor (point = 0; point < soup->numidx; point++)\t//would ideally use points, but I want to cover dead ones\n\t\t{\n\t\t\tif (soup->p[soup->i[point]] * n > d + EPSILON)\n\t\t\t{\n\t\t\t\tinternals++;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n//\tcprint(sprintf(\"%i internal splits, %i faces\\n\", internals, numfaces));\n\t\n\tif (numfaces <= 3)\n\t\treturn;\t//can't possibly be valid.\n\t\n\tif (drawit)\n\t{\t\n\t\t//draw textured preview (yay for block lighting)\n\t\tDrawQCBrushTextured(faces, numfaces, '0.7 0.7 0.7', 1);\n\t\t\n\t\t//draw it wireframe without depth testing\n\t\tDrawQCBrushWireframe(faces, numfaces, \"chop\", internals?[1,0,0]:[0,0,1], 1);\n\t}\n\telse\n\t{\n\t\tbrush_history_edit(soup->model, soup->brush, faces, numfaces, 1i); \n\t\t\n\t\tsoup->numidx = 0;\n\t\tsoup->numverts = 0;\n\t}\t\n};"
  },
  {
    "path": "quakec/csaddon/src/cam.qc",
    "content": "#ifdef CAMQUAKE\r\ntypedef struct\r\n{\r\n\tint numpoints;\r\n\tint type;\r\n\tvector pos[64];\r\n\tfloat totallength;\r\n\tvector color;\r\n\tint selected_point;\r\n\tfloat position;\r\n} spline_t;\r\n\r\ntypedef struct\r\n{\r\n\tfloat starttime;\r\n\tfloat stoptime;\r\n\tspline_t spline;\r\n} cam_t;\r\n\r\ntypedef struct\r\n{\r\n\tint ver;\r\n\tint position_count;\r\n\tint position_selected;\r\n\tint view_count;\r\n\tint view_selected;\r\n\tcam_t position[64];\r\n\tcam_t view[64];\r\n} camdata_t;\r\n#define VER 1i\t/*bump when the above structs change*/\r\n\r\nstatic camdata_t *camdata;\r\nstatic var float splinefile = -1;\r\nstatic var int edit_type = 0;\r\nstatic vector submenu_position;\r\nvar float autocvar_cl_demospeed = 1;\r\n\r\nvoid(int *dest, int bytes) memclr =\r\n{\r\n\tbytes = bytes>>2i;\r\n\twhile(bytes > 0i)\r\n\t{\r\n\t\tbytes = bytes - 1i;\r\n\t\tdest[bytes] = 0i;\r\n\t}\r\n};\r\n\r\nvoid() spline_init =\r\n{\r\n\tstring camfilename;\r\n\tif (isdemo())\r\n\t\tcamfilename = serverkey(\"ip\");\t//this always contains the name of the demo rather than the original ip, which is not known.\r\n\telse\r\n\t\tcamfilename = mapname;\t//otherwise just use the current map (though really we should just disable this)\r\n\tcamfilename = strcat(camfilename, \".cam\");\r\n\r\n\t/*precache the shader*/\r\n\tshaderforname(\"camsplineshader\", \"{\\n{\\nmap construction.tga\\nblendfunc blend\\nrgbgen vertex\\nalphagen vertex\\n}\\n}\\n\");\r\n\r\n\tsplinefile = fopen(camfilename, FILE_MMAP_RW, sizeof(camdata_t));\r\n\tif (splinefile < 0)\r\n\t{\r\n\t\t/*too lazy to create a file, just use it as a malloc*/\r\n\t\tsplinefile = fopen(\"\", FILE_MMAP_RW, sizeof(camdata_t));\r\n\t}\r\n\r\n\tcamdata = (camdata_t*)(fgets(splinefile));\r\n\r\n\tif (!camdata)\r\n\t\terror(\"unable to mmap spline.dat\\n\");\r\n\r\n\tif (camdata->ver != VER)\r\n\t{\r\n\t\tprint(sprintf(\"%s version is outdated, wiping.\\n\", camfilename));\r\n\t\tmemclr ((int*)camdata, sizeof(camdata_t));\r\n\t\tcamdata->ver = VER;\r\n\t}\r\n};\r\nvoid() spline_shutdown =\r\n{\r\n\tfclose(splinefile);\r\n\tsplinefile = -1;\r\n};\r\n\r\nstatic void spline_calclength(spline_t *s)\r\n{\r\n\tint i;\r\n\ts->totallength = 0;\r\n\tfor (i = 0; i < s->numpoints - 1; i+=1)\r\n\t{\r\n\t\ts->totallength = s->totallength + vlen(s->pos[i] - s->pos[i+1]);\r\n\t}\r\n};\r\n\r\nstatic vector(spline_t *s, float frac) spline_pos =\r\n{\r\n\tvector v1, v2;\r\n\tif (s->numpoints < 2)\r\n\t{\r\n\t\treturn '0 0 0';\r\n\t}\r\n\tfrac *= s->numpoints;\r\n\tif (frac < 0)\r\n\t{\r\n\t\tv1 = s->pos[0];\r\n\t\tv2 = s->pos[1];\r\n\t}\r\n\telse if (frac >= s->numpoints)\r\n\t{\r\n\t\tv1 = s->pos[s->numpoints-2];\r\n\t\tv2 = s->pos[s->numpoints-1];\r\n\t\tfrac -= s->numpoints;\r\n\t}\r\n\telse\r\n\t{\r\n\t\tv1 = s->pos[s->numpoints];\r\n\t\tv2 = s->pos[s->numpoints+1];\r\n\t\tfrac = frac - (float)(int)frac;\r\n\t}\r\n\r\n\treturn v1 * (1-frac) + v2 * frac;\r\n};\r\n\r\nstatic vector(spline_t *s, float frac) spline_dir =\r\n{\r\n\treturn normalize(spline_pos(s, frac+0.001) - spline_pos(s, frac-0.001));\r\n};\r\n\r\nstatic vector(vector pos, vector dir, vector view) beamdir =\r\n{\r\n\tvector result;\r\n\tview = normalize(view - pos);\r\n\t//crossproduct view+dir\r\n\tresult_x = dir_y*view_z - dir_z*view_y;\r\n\tresult_y = dir_z*view_x - dir_x*view_z;\r\n\tresult_z = dir_x*view_y - dir_y*view_x;\r\n\treturn result;\r\n};\r\n\r\nvoid(vector p1, vector p2, vector color1, vector color2) fakeline = {\r\n\tR_BeginPolygon(\"camsplineshader\");\r\n\tR_PolygonVertex(p1+'0 0 1', '0 0', color1,1);\r\n\tR_PolygonVertex(p1-'0 0 1', '0 1', color1,1);\r\n\tR_PolygonVertex(p2-'0 0 1', '1 1', color2,1);\r\n\tR_PolygonVertex(p2+'0 0 1', '1 0', color2,1);\r\n\tR_PolygonVertex(p2+'0 0 1', '0 0', color2,1);\r\n\tR_PolygonVertex(p2-'0 0 1', '0 1', color2,1);\r\n\tR_PolygonVertex(p1-'0 0 1', '1 1', color1,1);\r\n\tR_PolygonVertex(p1-'0 0 1', '1 0', color1,1);\r\n\tR_EndPolygon();\r\n};\r\n\r\nvoid (vector p1, vector p2, vector color1, vector color2) tube\r\n{\r\n\tlocal vector s;\r\n\tR_BeginPolygon(\"camsplineshader\");\r\n\tR_EndPolygon();\r\n};\r\n\r\nvector (vector p1, vector p2, float frac) interpolate\r\n{\r\n\treturn (p2 - p1) * frac + p1;\r\n};\r\n\r\nvoid (vector pos, float point_size, vector color) draw_point\r\n{\r\n\tlocal int i;\r\n\tlocal vector offsets[5];\r\n\tlocal vector points[8];\r\n\tlocal vector point, point1;\r\n\r\n\tif (point_size == 0)\r\n\t\treturn;\r\n\r\n\tpoint_x = -(point_size/2);\r\n\tpoint_y = -(point_size/2);\r\n\toffsets[0] = point;\r\n\tpoint_x = point_size/2;\r\n\tpoint_y = -(point_size/2);\r\n\toffsets[1] = point;\r\n\tpoint_x = -(point_size/2);\r\n\tpoint_y = (point_size/2);\r\n\toffsets[2] = point;\r\n\tpoint_x = (point_size/2);\r\n\tpoint_y = (point_size/2);\r\n\toffsets[3] = point;\r\n\r\n\tpoint1_z = -(point_size/2);\r\n\tfor (i=0; i<4; i+=1)\r\n\t\tpoints[i] = offsets[i] + point1 + pos;\r\n\r\n\tpoint1_z = (point_size/2);\r\n\tfor (i=4; i<8; i+=1)\r\n\t\tpoints[i] = offsets[i-4] + point1 + pos;\r\n\r\n\t/*\r\n\t * bottom\r\n\t * 2 3\r\n\t * 0 1\r\n\t *\r\n\t * top\r\n\t * 6 7\r\n\t * 4 5\r\n\t */\r\n\r\n\t// bottom\r\n\tR_BeginPolygon(\"camsplineshader\");\r\n\tR_PolygonVertex(points[3], '0 0', color ,1);\r\n\tR_PolygonVertex(points[2], '0 1', color ,1);\r\n\tR_PolygonVertex(points[0], '1 1', color,1);\r\n\tR_PolygonVertex(points[1], '1 0', color,1);\r\n\tR_EndPolygon();\r\n\r\n\t// top\r\n\tR_BeginPolygon(\"camsplineshader\");\r\n\tR_PolygonVertex(points[5], '0 0', color ,1);\r\n\tR_PolygonVertex(points[4], '0 1', color ,1);\r\n\tR_PolygonVertex(points[6], '1 1', color,1);\r\n\tR_PolygonVertex(points[7], '1 0', color,1);\r\n\tR_EndPolygon();\r\n\r\n\t// front\r\n\tR_BeginPolygon(\"camsplineshader\");\r\n\tR_PolygonVertex(points[4], '0 0', color ,1);\r\n\tR_PolygonVertex(points[5], '0 1', color ,1);\r\n\tR_PolygonVertex(points[1], '1 1', color,1);\r\n\tR_PolygonVertex(points[0], '1 0', color,1);\r\n\tR_EndPolygon();\r\n\r\n\t//back\r\n\tR_BeginPolygon(\"camsplineshader\");\r\n\tR_PolygonVertex(points[7], '0 0', color ,1);\r\n\tR_PolygonVertex(points[6], '0 1', color ,1);\r\n\tR_PolygonVertex(points[2], '1 1', color,1);\r\n\tR_PolygonVertex(points[3], '1 0', color,1);\r\n\tR_EndPolygon();\r\n\r\n\t//left\r\n\tR_BeginPolygon(\"camsplineshader\");\r\n\tR_PolygonVertex(points[6], '0 0', color ,1);\r\n\tR_PolygonVertex(points[4], '0 1', color ,1);\r\n\tR_PolygonVertex(points[0], '1 1', color,1);\r\n\tR_PolygonVertex(points[2], '1 0', color,1);\r\n\tR_EndPolygon();\r\n\r\n\t//right\r\n\tR_BeginPolygon(\"camsplineshader\");\r\n\tR_PolygonVertex(points[5], '0 0', color ,1);\r\n\tR_PolygonVertex(points[7], '0 1', color ,1);\r\n\tR_PolygonVertex(points[3], '1 1', color,1);\r\n\tR_PolygonVertex(points[1], '1 0', color,1);\r\n\tR_EndPolygon();\r\n};\r\n\r\nvoid (spline_t *s, vector p) spline_move\r\n{\r\n\tlocal int i;\r\n\r\n\tfor (i=0; i<s->numpoints; i+=1i)\r\n\t{\r\n\t\ts->pos[i] = s->pos[i] + p;\r\n\t}\r\n};\r\n\r\n// time has to be between 0 and 1\r\nvector(spline_t *s, float frac_time) spline_calculate_position =\r\n{\r\n\tlocal vector r, point1, point2, point3, point4;\r\n\tlocal float t1, t2, t3, t4, t5, t_scaled;\r\n\tlocal int i;\r\n\r\n\tif (s->numpoints == 0)\r\n\t{\r\n\t\treturn '0 0 0';\r\n\t}\r\n\r\n\t// point\r\n\tif (s->numpoints == 1)\r\n\t{\r\n\t\treturn s->pos[0];\r\n\t}\r\n\r\n\t// straight line\r\n\tif (s->numpoints == 2)\r\n\t{\r\n\t\tr = s->pos[1] - s->pos[0];\r\n\t\tr = r * frac_time;\r\n\t\tr = r + s->pos[0];\r\n\t\treturn r;\r\n\t}\r\n\r\n\t// cubic spline\r\n\tif (s->numpoints == 3)// || (s->numpoints > 3 && s->type == 1))\r\n\t{\r\n\t\tt1 = 1-frac_time;\r\n\t\tt2 = pow(t1, s->numpoints - 1i);\r\n\t\tt3 = pow(frac_time, s->numpoints - 1i);\r\n\r\n\t\tfor (i=0; i<s->numpoints; i+=1)\r\n\t\t{\r\n\t\t\tif (i == 0i)\r\n\t\t\t{\r\n\t\t\t\tr = s->pos[i] * t2;\r\n\t\t\t}\r\n\t\t\telse if (i == s->numpoints - 1i)\r\n\t\t\t{\r\n\t\t\t\tr = r + s->pos[i] * t3;\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tt4 = (s->numpoints-1i) * pow(frac_time, i) * pow(t1, s->numpoints - 1i - i);\r\n\t\t\t\tr = r + s->pos[i] * t4;\r\n\t\t\t}\r\n\t\t}\r\n\t\treturn r;\r\n\t}\r\n\r\n\t// catmull rom\r\n\tif (s->numpoints > 3)\r\n\t{\r\n\t\t// getting the start point -- might change if we add times to points\r\n\t\t// first point is always an entry point last point also\r\n\t\tt5 = 1 / (float)(s->numpoints - 3i);\r\n\t\tfor (i=1, t4=t5; t4<frac_time; t4 = t4 + t5)\r\n\t\t{\r\n\t\t\ti+=1;\r\n\t\t}\r\n\r\n\t\tif (i >= s->numpoints)\r\n\t\t{\r\n\t\t\tprint(\"something went wrong while getting the start point!\\n\");\r\n\t\t\treturn s->pos[0];\r\n\t\t}\r\n\r\n\r\n\t\tpoint1 = s->pos[i - 1i];\r\n\t\tpoint2 = s->pos[i];\r\n\t\tpoint3 = s->pos[i+1i];\r\n\t\tpoint4 = s->pos[i+2i];\r\n\r\n\t\t// scale the time so its 0 -> 1 between point1 and 2\r\n\t\t/*\r\n\t\t\t1\t\t2\t\t3\t\t4\t\t5\t\t6\r\n\t\t\t\t\t0\t\t1\r\n\t\t\t\t\t0\t\t\t\t\t\t1\r\n\t\t*/\r\n\r\n\t\tt_scaled = (frac_time - (t4 - t5))/t5; // hope this works :P\r\n\r\n\t\t// would be really nice if i could access vector components via [0] [1] ... here\r\n\t\t// warning RETARDED!\r\n\t\tt1 = point2_x;\r\n\t\tt2 = -0.5 * point1_x + 0.5 * point3_x;\r\n\t\tt3 = point1_x + (-2.5 * point2_x) + 2.0* point3_x +(-0.5 *point4_x);\r\n\t\tt4 = -0.5 * point1_x + 1.5 * point2_x + (-1.5 * point3_x) + 0.5 * point4_x;\r\n\t\tr_x = ((t4 * t_scaled + t3)*t_scaled + t2)*t_scaled + t1;\r\n\r\n\t\tt1 = point2_y;\r\n\t\tt2 = -0.5 * point1_y + 0.5 * point3_y;\r\n\t\tt3 = point1_y + (-2.5 * point2_y) + 2.0* point3_y +(-0.5 *point4_y);\r\n\t\tt4 = -0.5 * point1_y + 1.5 * point2_y + (-1.5 * point3_y) + 0.5 * point4_y;\r\n\t\tr_y = ((t4 * t_scaled + t3)*t_scaled + t2)*t_scaled + t1;\r\n\r\n\t\tt1 = point2_z;\r\n\t\tt2 = -0.5 * point1_z + 0.5 * point3_z;\r\n\t\tt3 = point1_z + (-2.5 * point2_z) + 2.0* point3_z +(-0.5 *point4_z);\r\n\t\tt4 = -0.5 * point1_z + 1.5 * point2_z + (-1.5 * point3_z) + 0.5 * point4_z;\r\n\t\tr_z = ((t4 * t_scaled + t3)*t_scaled + t2)*t_scaled + t1;\r\n\r\n\t\treturn r;\r\n\t}\r\n\r\n\treturn s->pos[0];\r\n\r\n};\r\n\r\nstatic void(spline_t *s) spline_draw =\r\n{\r\n\tlocal float step, frac;\r\n\tlocal vector pos, pos1, pos2, vpos;\r\n\tlocal vector bdir;\r\n\tlocal int i;\r\n\tlocal vector color, color1, color2;\r\n\tlocal float psize;\r\n\r\n\t/*example of drawing convex polygons*/\r\n\t///R_BeginPolygon(\"camsplineshader\");\r\n\r\n\tvpos = getviewprop(VF_ORIGIN);\r\n\r\n\tif (s->numpoints < 3)\r\n\t{\r\n\t\tstep = 1; // should be fine\r\n\t}\r\n\telse\r\n\t{\r\n\t\tstep = 0.01;\r\n\t}\r\n\r\n\tfor (frac=0; frac<=1; frac = frac + step)\r\n\t{\r\n\t\tpos = spline_calculate_position(s, frac);\r\n\t\tif (frac == 0)\r\n\t\t{\r\n\t\t\tcolor2 = interpolate('0.1 0.3 0.1' , '0.5 0 0', frac);\r\n\t\t\tpos2 = pos;\r\n\t\t\tcontinue;\r\n\t\t}\r\n\r\n\t\tcolor1 = interpolate('0.1 0.3 0.1' , '0.5 0 0', frac);\r\n\t\tfakeline(pos2, pos, color2, color1);\r\n\t\tpos2 = pos;\r\n\t\tcolor2 = color1;\r\n\t}\r\n\r\n\t//fakeline(pos2, s->pos[s->numpoints-1]);\r\n\r\n//\tdraw points\r\n\tfor (i=0; i<s->numpoints; i+=1)\r\n\t{\r\n\t\tpsize = 5;\r\n\t\tcolor = '0.25 0.25 0.25';\r\n\t\tif (i == s->selected_point)\r\n\t\t{\r\n\t\t\tpsize = 10;\r\n\t\t\tcolor = '1 1 1';\r\n\t\t}\r\n\t\tdraw_point(s->pos[i], psize, color);\r\n\t}\r\n\t// draw position\r\n\tpos = spline_calculate_position(s, s->position);\r\n\tdraw_point(pos, 3, '1 0 0');\r\n};\r\n\r\nvoid(float attime) spline_overrides =\r\n{\r\n\tlocal vector position, view;\r\n\tlocal cam_t *cpos;\r\n\tlocal cam_t *cview;\r\n\tlocal float stime;\r\n\tlocal int i;\r\n\r\n\tif (splinefile < 0)\r\n\t\tspline_init();\r\n\r\n\tif (camdata->position_count == 0 || camdata->position_count < 0 || camdata->position_count > 64)\r\n\t\treturn;\r\n\r\n\tif (camdata->view_count == 0 || camdata->view_count < 0 || camdata->view_count > 64)\r\n\t\treturn;\r\n\r\n\tfor (i=0i; i < camdata->position_count; i=i+1i)\r\n\t{\r\n\t\tcpos = &camdata->position[i];\r\n\t\tif (cpos->starttime <= attime && cpos->stoptime > attime)\r\n\t\t\tbreak;\r\n\t}\r\n\r\n\tif (i == camdata->position_count)\r\n\t\treturn;\r\n\r\n\tfor (i=0i; i < camdata->view_count; i=i+1i)\r\n\t{\r\n\t\tcview = &camdata->view[i];\r\n\t\tif (cview->starttime <= attime && cview->stoptime > attime)\r\n\t\t\tbreak;\r\n\t}\r\n\r\n\tif (i == camdata->view_count)\r\n\t\treturn;\r\n\t\t\r\n\tstime = (attime - cpos->starttime) / (cpos->stoptime - cpos->starttime);\r\n\tposition = spline_calculate_position(&cpos->spline, stime);\r\n\r\n\tstime = (attime - cview->starttime) / (cview->stoptime - cview->starttime);\r\n\tview = spline_calculate_position(&cview->spline, stime);\r\n\r\n\tsetviewprop(VF_ORIGIN, position);\r\n\tsetviewprop(VF_ANGLES, vectoangles(view - position));\r\n};\r\n\r\nvoid() editor_spline_addentities =\r\n{\r\n\tlocal int i;\r\n\tlocal float ctime;\r\n\t//local float stime;\r\n\r\n\tlocal vector tdir = [16 * cos(time), 16 * sin(time)];\r\n//\tdrawlame3dtext(\"charset\", '300 -500 50', tdir, '0 0 -16', \"hello jogi\\ndoes this string appear for you?\", '1 1 1', 1);\r\n//\tdrawlame3dtext(\"charset\", '300 -500 50', -tdir, '0 0 -16', \"hello jogi\\ndoes this string appear for you?\", '1 1 1', 1);\r\n\r\n\tif (splinefile < 0)\r\n\t\tspline_init();\r\n\r\n\tctime = gettime(5);\r\n\r\n\t/*add visible splines to the scene*/\r\n\tfor (i = 0; i < camdata->position_count; i+=1i)\r\n\t\tspline_draw(&camdata->position[i].spline);\r\n\tfor (i = 0; i < camdata->view_count; i+=1i)\r\n\t\tspline_draw(&camdata->view[i].spline);\r\n\r\n\t/*sort out the overrides*/\r\n//\tspline_overrides(simtime);\r\n};\r\n//float slidertestfloat1, slidertestfloat2, slidertestfloat3;\r\n//static float *curslider;\r\nstatic menu_t menu;\r\n\r\nvoid (vector *curmousepos, vector mousediff, float key) submenu =\r\n{\r\n\tlocal vector pos, cpos;\r\n\tlocal spline_t *s;\r\n\tlocal cam_t *c;\r\n\tlocal vector point, point1;\r\n\tstring str;\r\n\r\n\tpos = submenu_position;\r\n\r\n\tmenu->key = key;\r\n\tmenu->mousepos = curmousepos;\r\n\tmenu->mousediff = mousediff;\r\n\r\n\tif (edit_type == 0)\r\n\t{\r\n\t\tif (camdata.position_count > 0)\r\n\t\t{\r\n\t\t\tc = &camdata.position[camdata.position_selected];\r\n\t\t\ts = &camdata.position[camdata.position_selected].spline;\r\n\t\t}\r\n\t}\r\n\telse\r\n\t{\r\n\t\tif (camdata.view_count > 0)\r\n\t\t{\r\n\t\t\tc = &camdata.view[camdata.view_selected];\r\n\t\t\ts = &camdata.view[camdata.view_selected].spline;\r\n\t\t}\r\n\t}\r\n\r\n\tmenu->flags = MF_DS_VALUE;\r\n\tif (edit_type == 0)\r\n\t{\r\n\t\tif (camdata.position_count > 1)\r\n\t\t{\r\n\t\t\tstr = sprintf(\"%2i/%2i\", camdata.position_selected+1i, camdata.position_count);\r\n\t\t\tcpos = pos;\r\n\t\t\tcpos_x = strlen(str) * 8 +4;\r\n\t\t\tdrawrawstring(pos, str, '8 8 0', '1 0 0', 1);\r\n\t\t\tslideri_widgit(&menu, \"\", cpos, &camdata.position_selected, 0, camdata.position_count-1i);\r\n\t\t\tpos_y += 8;\r\n\t\t}\r\n\t\telse if (camdata.position_count > 0)\r\n\t\t{\r\n\t\t\tstr = sprintf(\"starttime: %f - \", camdata.position[camdata.position_selected].starttime);\r\n\t\t\tdrawrawstring(pos, str, '8 8 0', '1 1 1', 1);\r\n\t\t\tcpos = pos;\r\n\t\t\tcpos_x += strlen(str) * 8;\r\n\t\t\tsliderf_fixed_widgit(&menu, \"\", cpos, &camdata.position[camdata.position_selected].starttime);\r\n\t\t\tcpos_x += 32;\r\n\t\t\tif (button_widget(&menu, \"set\", cpos))\r\n\t\t\t\tcamdata.position[camdata.position_selected].starttime = gettime(5);\r\n\t\t\tpos_y += 8;\r\n\t\t\tstr = sprintf(\"stoptime : %f - \", camdata.position[camdata.position_selected].stoptime);\r\n\t\t\tdrawrawstring(pos, str, '8 8 0', '1 1 1', 1);\r\n\t\t\tcpos = pos;\r\n\t\t\tcpos_x += strlen(str) * 8;\r\n\t\t\tsliderf_fixed_widgit(&menu, \"\", cpos, &camdata.position[camdata.position_selected].stoptime);\r\n\t\t\tcpos_x += 32;\r\n\t\t\tif (button_widget(&menu, \"set\", cpos))\r\n\t\t\t\tcamdata.position[camdata.position_selected].stoptime = gettime(5);\r\n\t\t\tpos_y += 8;\r\n\t\t}\r\n\t}\r\n\telse\r\n\t{\r\n\t\tif (camdata.view_count > 1)\r\n\t\t{\r\n\t\t\tstr = sprintf(\"%2i/%2i\", camdata.view_selected+1i, camdata.view_count);\r\n\t\t\tcpos = pos;\r\n\t\t\tcpos_x = strlen(str) * 8 +4;\r\n\t\t\tdrawrawstring(pos, str, '8 8 0', '1 0 0', 1);\r\n\t\t\tslideri_widgit(&menu, \"\", cpos, &camdata.view_selected, 0, camdata.view_count);\r\n\t\t\tpos_y += 8;\r\n\t\t}\r\n\t\telse if (camdata.view_count > 0)\r\n\t\t{\r\n\t\t\tstr = sprintf(\"starttime: %f - \", camdata.view[camdata.view_selected].starttime);\r\n\t\t\tdrawrawstring(pos, str, '8 8 0', '1 1 1', 1);\r\n\t\t\tcpos = pos;\r\n\t\t\tcpos_x += strlen(str) * 8;\r\n\t\t\tsliderf_fixed_widgit(&menu, \"\", cpos, &camdata.view[camdata.view_selected].starttime);\r\n\t\t\tcpos_x += 32;\r\n\t\t\tif (button_widget(&menu, \"set\", cpos))\r\n\t\t\t\tcamdata.view[camdata.view_selected].starttime = gettime(5);\r\n\t\t\tpos_y += 8;\r\n\t\t\tstr = sprintf(\"stoptime : %f - \", camdata.view[camdata.view_selected].stoptime);\r\n\t\t\tdrawrawstring(pos, str, '8 8 0', '1 1 1', 1);\r\n\t\t\tcpos = pos;\r\n\t\t\tcpos_x += strlen(str) * 8;\r\n\t\t\tsliderf_fixed_widgit(&menu, \"\", cpos, &camdata.view[camdata.view_selected].stoptime);\r\n\t\t\tcpos_x += 32;\r\n\t\t\tif (button_widget(&menu, \"set\", cpos))\r\n\t\t\t\tcamdata.view[camdata.view_selected].stoptime = gettime(5);\r\n\t\t\tpos_y += 8;\r\n\t\t}\r\n\t}\r\n\tmenu->flags = 0;\r\n\r\n\tpos_y += 8;\r\n\r\n\tif (s && s->numpoints > 0)\r\n\t{\r\n\t\tmenu->flags = MF_DS_VALUE;\r\n\t\tstr = sprintf(\"selected point: %2i/%2i\", s->selected_point + 1i, s->numpoints);\r\n\t\tdrawrawstring(pos, str, '8 8 0', '1 1 1', 1);\r\n\t\tcpos = pos;\r\n\t\tcpos_x += strlen(str) * 8 + 4;\r\n\t\tslideri_widgit(&menu, \"\",  cpos, &s->selected_point, 0, s->numpoints - 1i);pos_y+=8;\r\n\t\tmenu->flags = 0;\r\n\t\tpoint = s->pos[s->selected_point];\r\n\t\tsliderf_fixed_widgit(&menu, \"x:\", pos, &point_x);pos_y+=8;\r\n\t\tsliderf_fixed_widgit(&menu, \"y:\", pos, &point_y);pos_y+=8;\r\n\t\tsliderf_fixed_widgit(&menu, \"z:\", pos, &point_z);pos_y+=8;\r\n\t\ts->pos[s->selected_point] = point;\r\n\t\tpos_y += 8;\r\n\t}\r\n\r\n\tif (c && s)\r\n\t{\r\n\t\tmenu->flags = MF_DS_VALUE;\r\n\t\tpoint1 = '0 0 0';\t// << problem with using a global value identifier i cant reuse point\r\n\t\tstr = \"move spline: \";\r\n\t\tdrawrawstring(pos, str, '8 8 0', '1 1 1', 1);\r\n\t\tpos_y += 8;\r\n\t\tsliderf_fixed_widgit(&menu, \" x:\", pos, &point1_x); pos_y += 8;\r\n\t\tsliderf_fixed_widgit(&menu, \" y:\", pos, &point1_y); pos_y += 8;\r\n\t\tsliderf_fixed_widgit(&menu, \" z:\", pos, &point1_z); pos_y += 8;\r\n\t\tspline_move(s, point1);\r\n\t\tmenu->flags = 0;\r\n\t}\r\n\r\n//\tdrawrawstring(pos, sprintf(\"isdemo: %f\", isdemo()), '8 8 0', '1 1 1', 1);\r\n}\r\n\r\nint (vector *curmousepos, vector mousediff, float key) testmenu =\r\n{\r\n\tlocal vector pos;\r\n\tlocal int reset_mouse = 0;\r\n\r\n \tpos = '0 80';\r\n\t/*\r\n\tsliderf_widgit (\"test float a\", pos, curmousepos, mousediff, &slidertestfloat1, 100, 200);pos_y += 8;\r\n\tsliderf_widgit (\"test float b\", pos, curmousepos, mousediff, &slidertestfloat2, 0, 1);pos_y += 8;\r\n\r\n\tcheckboxf_widgit (\"checkbox test\", pos, curmousepos, &slidertestfloat2, key);pos_y += 32;\r\n\tsliderf_fixed_widgit(\"test float c\", pos, curmousepos, mousediff, &slidertestfloat3, key, 1i);pos_y += 32;\r\n\t*/\r\n\r\n\t//curmousepos = '0 0 0';\r\n\r\n\treturn reset_mouse;\r\n}\r\n\r\nint (vector *curmousepos, vector mousediff) editor_spline_overlay =\r\n{\r\n\tlocal vector pos, v;\r\n//\tlocal spline_t *s;\r\n\tlocal int ri;\r\n\tlocal string str;\r\n\tlocal float minv, sec;\r\n\r\n\tif (splinefile < 0)\r\n\t\tspline_init();\r\n\r\n\t/*draw menu*/\r\n\t/*dunno if the light editor has any convienient code*/\r\n\tpos_y = 40;\r\n\tstr = sprintf(\"cam(%i)\", camdata->position_count);\r\n\tif (edit_type == 0)\r\n\t\tdrawrawstring(pos, str, '8 8 0', '1 0 0', 1);\r\n\telse\r\n\t\tdrawrawstring(pos, str, '8 8 0', '1 1 1', 1);\r\n\tv = pos;\r\n\tv_x += strlen(str) * 8;\r\n\tri = strlen(str) + 1;\r\n\tdrawrawstring(v , \"/\", '8 8 0', '1 1 1', 1);\r\n\tv_x += 8;\r\n\tstr = sprintf(\"view(%i)\", camdata->view_count);\r\n\tif (edit_type == 1)\r\n\t\tdrawrawstring(v, str, '8 8 0', '1 0 0', 1);\r\n\telse\r\n\t\tdrawrawstring(v, str, '8 8 0', '1 1 1', 1);\r\n\tri += (int)(strlen(str) + 1);\r\n\tv_x += 4 + strlen(str) * 8;\r\n\r\n\tmenu->flags = MF_DS_VALUE;\r\n\tcheckboxi_widgit(&menu, \"\", v, &edit_type);\r\n\r\n\tv_x = 0;\r\n\tv_y += 8;\r\n\tsliderf_widgit(&menu, \"demo speed\", v, &autocvar_cl_demospeed, 0, 1);\r\n\tcvar_set(\"cl_demospeed\", ftos(autocvar_cl_demospeed));\r\n\r\n\tv_y += 8;\r\n\tsec = time;\r\n\tminv = floor(sec/60);\r\n\tsec -= minv*60;\r\n\tdrawrawstring(v, sprintf(\"time: %g:%02.0f\", min, sec), '8 8 0', '1 1 1', 1);\r\n\r\n\tsubmenu_position = pos + '0 8';\r\n\r\n\tsubmenu(curmousepos, mousediff, -1);\r\n\r\n\treturn ri;\r\n};\r\n\r\nint (spline_t *s, vector point) spline_add_point = \r\n{\r\n\tif (s->numpoints + 1 >= 64)\r\n\t{\r\n\t\treturn 1;\r\n\t}\r\n\r\n\ts->pos[s->numpoints] = point;\r\n\ts->selected_point = s->numpoints;\r\n\ts->numpoints = s->numpoints + 1i; //oldskool!\r\n\treturn 0;\r\n};\r\n\r\nvoid (spline_t *s, vector point) spline_set_point =\r\n{\r\n\ts->pos[s->selected_point] = point;\r\n};\r\n\r\nvoid (int type) position_add_point =\r\n{\r\n\tvector v;\r\n\tint i;\r\n\tlocal cam_t *c;\r\n\tlocal spline_t *s;\r\n\r\n\ti = camdata->position_selected;\r\n\tc = &camdata->position[i];\r\n\ts = &c->spline;\r\n\tv = getviewprop(VF_ORIGIN);\r\n\tif (type == 0)\r\n\t{\r\n\t\tif (spline_add_point(s, v))\r\n\t\t{\r\n//\t\t\tprint(sprintf(\"point could not be added.\\n\"));\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n//\t\t\tprint(sprintf(\"added point: %i %f %f %f\\n\", s->selected_point, v_x, v_y, v_z));\r\n\t\t}\r\n\t}\r\n}\r\n\r\nvoid (int type) view_add_point =\r\n{\r\n\tvector v;\r\n\tint i;\r\n\tlocal cam_t *c;\r\n\tlocal spline_t *s;\r\n\r\n\ti = camdata->view_selected;\r\n\tc = &camdata->view[i];\r\n\ts = &c->spline;\r\n\tv = getviewprop(VF_ORIGIN);\r\n\tif (type == 0)\r\n\t{\r\n\t\tif (spline_add_point(s, v))\r\n\t\t{\r\n//\t\t\tprint(sprintf(\"point could not be added.\\n\"));\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n//\t\t\tprint(sprintf(\"added point: %i %f %f %f\\n\", s->selected_point, v_x, v_y, v_z));\r\n\t\t}\r\n\t}\r\n}\r\n\r\n\r\nvoid(void) position_add\r\n{\r\n\tif (camdata->position_count >= 64)\r\n\t{\r\n\t\treturn;\r\n\t}\r\n\r\n\tcamdata->position_selected = camdata->position_count;\r\n\tcamdata->position_count = camdata->position_count + 1i; //oldskool\r\n}\r\n\r\nvoid(void) view_add\r\n{\r\n\tif (camdata->view_count >= 64)\r\n\t{\r\n\t\treturn;\r\n\t}\r\n\tcamdata->view_selected = camdata->view_count;\r\n\tcamdata->view_count = camdata->view_count + 1i;\r\n}\r\n\r\nvoid(spline_t *s, vector change) spline_change_point_position\r\n{\r\n\tif (s->numpoints == 0)\r\n\t\treturn;\r\n\ts->pos[s->selected_point] = s->pos[s->selected_point] + change;\r\n};\r\n\r\nvoid (cam_t *c, float starttime) cam_set_start_time\r\n{\r\n\tc->starttime = starttime;\r\n\tif (starttime > c->starttime)\r\n\t\tc->starttime = starttime;\r\n}\r\n\r\nvoid (cam_t *c, float starttime) cam_set_stop_time\r\n{\r\n\tc->starttime = starttime;\r\n\tif (starttime < c->starttime)\r\n\t\tc->starttime = starttime;\r\n}\r\n\r\nvoid (spline_t *s, float change) spline_change_position\r\n{\r\n\ts->position = s->position + change;\r\n\tif (s->position < 0)\r\n\t\ts->position = 0;\r\n\telse if (s->position > 1)\r\n\t\ts->position = 1;\r\n}\r\n\r\nvoid(spline_t *s, int change) spline_change_selected_point\r\n{\r\n\tif (s->numpoints == 0)\r\n\t\treturn;\r\n\r\n\tif (change + s->selected_point < 0)\r\n\t\ts->selected_point = s->numpoints -1;\r\n\telse if (change + s->selected_point >= s->numpoints)\r\n\t\ts->selected_point = 0;\r\n\telse\r\n\t\ts->selected_point = s->selected_point + change;\r\n};\r\n\r\nfloat(float keycode, float unicode, vector *curmousepos_in, vector mousediff) editor_spline_key\r\n{\r\n\tlocal vector curmousepos = *curmousepos_in;\r\n\r\n\tif (splinefile < 0)\r\n\t\tspline_init();\r\n\r\n\tif (keycode == 'j')\r\n\t{\r\n\t\tif (edit_type == 0)\r\n\t\t{\r\n\t\t\tif (camdata->position_count == 0)\r\n\t\t\t\tposition_add();\r\n\t\t\tposition_add_point(0);\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tif (camdata->view_count == 0)\r\n\t\t\t\tview_add();\r\n\t\t\tview_add_point(0);\r\n\t\t}\r\n\t\treturn TRUE;\r\n\t}\r\n\r\n\tif (keycode == 'k')\r\n\t{\r\n\t\tif (edit_type == 0)\r\n\t\t{\r\n\t\t\tif (camdata->position_count == 0)\r\n\t\t\t\treturn FALSE;\r\n\t\t\tspline_change_selected_point(&camdata->position[camdata->position_selected].spline, -1);\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tif (camdata->view_count == 0)\r\n\t\t\t\treturn FALSE;\r\n\t\t\tspline_change_selected_point(&camdata->view[camdata->view_selected].spline, -1);\r\n\t\t}\r\n\t\treturn TRUE;\r\n\t}\r\n\r\n\tif (keycode == 'l')\r\n\t{\r\n\t\tif (camdata->position_count == 0)\r\n\t\t\treturn FALSE;\r\n\t\tspline_change_selected_point(&camdata->position[camdata->position_selected].spline, 1);\r\n\t\treturn TRUE;\r\n\t}\r\n\r\n\tif (keycode == '9')\r\n\t{\r\n\t\tif (edit_type == 0)\r\n\t\t{\r\n\t\t\tif (camdata->position_count == 0)\r\n\t\t\t\treturn FALSE;\r\n\t\t\tspline_change_point_position(&camdata->position[camdata->position_selected].spline, '0 0 -1');\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tif (camdata->view_count == 0)\r\n\t\t\t\treturn FALSE;\r\n\t\t\tspline_change_point_position(&camdata->view[camdata->view_selected].spline, '0 0 -1');\r\n\t\t}\r\n\t\treturn TRUE;\r\n\t}\r\n\r\n\tif (keycode == '0')\r\n\t{\r\n\t\tif (edit_type == 0)\r\n\t\t{\r\n\t\t\tif (camdata->position_count == 0)\r\n\t\t\t\treturn FALSE;\r\n\t\t\tspline_change_point_position(&camdata->position[camdata->position_selected].spline, '0 0 1');\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tif (camdata->view_count == 0)\r\n\t\t\t\treturn FALSE;\r\n\t\t\tspline_change_point_position(&camdata->view[camdata->view_selected].spline, '0 0 1');\r\n\t\t}\r\n\t\treturn TRUE;\r\n\t}\r\n\r\n\tif (keycode == '7')\r\n\t{\r\n\t\tif (edit_type == 0)\r\n\t\t{\r\n\t\t\tif (camdata->position_count == 0)\r\n\t\t\t\treturn FALSE;\r\n\t\t\tspline_change_point_position(&camdata->position[camdata->position_selected].spline, '0 -1 0');\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tif (camdata->view_count == 0)\r\n\t\t\t\treturn FALSE;\r\n\t\t\tspline_change_point_position(&camdata->view[camdata->view_selected].spline, '0 -1 0');\r\n\t\t}\r\n\t\treturn TRUE;\r\n\t}\r\n\r\n\tif (keycode == '8')\r\n\t{\r\n\t\tif (edit_type == 0)\r\n\t\t{\r\n\t\t\tif (camdata->position_count == 0)\r\n\t\t\t\treturn FALSE;\r\n\t\t\tspline_change_point_position(&camdata->position[camdata->position_selected].spline, '0 1 0');\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tif (camdata->view_count== 0)\r\n\t\t\t\treturn FALSE;\r\n\t\t\tspline_change_point_position(&camdata->view[camdata->view_selected].spline, '0 1 0');\r\n\t\t}\r\n\t\treturn TRUE;\r\n\t}\r\n\r\n\tif (keycode == '5')\r\n\t{\r\n\t\tif (edit_type == 0)\r\n\t\t{\r\n\t\t\tif (camdata->position_count == 0)\r\n\t\t\t\treturn FALSE;\r\n\t\t\tspline_change_point_position(&camdata->position[camdata->position_selected].spline, '-1 0 0');\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tif (camdata->view_count == 0)\r\n\t\t\t\treturn FALSE;\r\n\t\t\tspline_change_point_position(&camdata->view[camdata->view_selected].spline, '-1 0 0');\r\n\t\t}\r\n\t\treturn TRUE;\r\n\t}\r\n\r\n\tif (keycode == '6')\r\n\t{\r\n\t\tif (edit_type == 0)\r\n\t\t{\r\n\t\t\tif (camdata->position_count == 0)\r\n\t\t\t\treturn FALSE;\r\n\t\t\tspline_change_point_position(&camdata->position[camdata->position_selected].spline, '1 0 0');\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tif (camdata->view_count == 0)\r\n\t\t\t\treturn FALSE;\r\n\t\t\tspline_change_point_position(&camdata->view[camdata->view_selected].spline, '1 0 0');\r\n\t\t}\r\n\t\treturn TRUE;\r\n\t}\r\n\r\n\tif (keycode == '6')\r\n\t{\r\n\t\tif (edit_type == 0)\r\n\t\t{\r\n\t\t\tif (camdata->position_count == 0)\r\n\t\t\t\treturn FALSE;\r\n\t\t\tspline_change_point_position(&camdata->position[camdata->position_selected].spline, '1 0 0');\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tif (camdata->view_count == 0)\r\n\t\t\t\treturn FALSE;\r\n\t\t\tspline_change_point_position(&camdata->view[camdata->view_selected].spline, '1 0 0');\r\n\t\t}\r\n\t\treturn TRUE;\r\n\t}\r\n\r\n\tif (keycode == '-')\r\n\t{\r\n\t\tif (edit_type == 0)\r\n\t\t{\r\n\t\t\tif (camdata->position_count == 0)\r\n\t\t\t\treturn FALSE;\r\n\t\t\tspline_change_position(&camdata->position[camdata->position_selected].spline, -0.01);\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tif (camdata->view_count == 0)\r\n\t\t\t\treturn FALSE;\r\n\t\t\tspline_change_position(&camdata->view[camdata->view_selected].spline, -0.01);\r\n\t\t}\r\n\t\treturn TRUE;\r\n\t}\r\n\r\n\tif (keycode == '=')\r\n\t{\r\n\t\tif (edit_type == 0)\r\n\t\t{\r\n\t\t\tif (camdata->position_count == 0)\r\n\t\t\t\treturn FALSE;\r\n\t\t\tspline_change_position(&camdata->position[camdata->position_selected].spline, 0.01);\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tif (camdata->view_count == 0)\r\n\t\t\t\treturn FALSE;\r\n\t\t\tspline_change_position(&camdata->view[camdata->view_selected].spline, 0.01);\r\n\t\t}\r\n\t\treturn TRUE;\r\n\t}\r\n\r\n\tif (keycode == '1')\r\n\t{\r\n\t\tedit_type = !edit_type;\r\n\t\treturn TRUE;\r\n\t}\r\n\r\n\tif (keycode == '2')\r\n\t{\r\n\t\tif (edit_type == 0)\r\n\t\t{\r\n\t\t\tif (camdata->position_count == 0)\r\n\t\t\t\treturn FALSE;\r\n\t\t\tcam_set_start_time(&camdata->position[camdata->position_selected], gettime(5));\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tif (camdata->view_count == 0)\r\n\t\t\t\treturn FALSE;\r\n\t\t\tcam_set_start_time(&camdata->view[camdata->view_selected], gettime(5));\r\n\t\t}\r\n\t\treturn TRUE;\r\n\t}\r\n\r\n\tif (keycode == '2')\r\n\t{\r\n\t\tif (edit_type == 0)\r\n\t\t{\r\n\t\t\tif (camdata->position_count == 0)\r\n\t\t\t\treturn FALSE;\r\n\t\t\tcam_set_stop_time(&camdata->position[camdata->position_selected], gettime(5));\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tif (camdata->view_count == 0)\r\n\t\t\t\treturn FALSE;\r\n\t\t\tcam_set_stop_time(&camdata->view[camdata->view_selected], gettime(5));\r\n\t\t}\r\n\t\treturn TRUE;\r\n\t}\r\n\r\n\tif (keycode == 'n')\r\n\t{\r\n\t\tif (edit_type == 0)\r\n\t\t\tposition_add();\r\n\t\telse\r\n\t\t\tview_add();\r\n\t\treturn TRUE;\r\n\t}\r\n\r\n\tsubmenu(curmousepos_in, mousediff, keycode);\r\n\r\n\treturn FALSE;\r\n};\r\n\r\nfloat(string str) CSQC_ConsoleCommand =\r\n{\r\n\tlocal float numargs;\r\n\tnumargs = tokenize(str);\r\n\tswitch(argv(0))\r\n\t{\r\n\tcase \"examplecommandforjogi\":\r\n\t\tprint(\"Hello jogi, did this work?\\n\");\r\n\t\tprint(argv(1), \"\\n\");\r\n\t\treturn TRUE;\r\n\tcase \"crashandcatchfire\":\r\n\t\tprint(\"Yeah, you're probably going to regret that\\n\");\r\n\t\tlocalcmd(\"unbindall\\ncfg_save\\nalias quit say ahaha\\n\");\r\n\t\treturn TRUE;\r\n\t}\r\n\treturn FALSE;\r\n};\r\n\r\nvoid() Cam_Init =\r\n{\r\n\tregistercommand(\"examplecommandforjogi\");\r\n//\tregistercommand(\"crashandcatchfire\");\r\n};\r\n#endif\r\n"
  },
  {
    "path": "quakec/csaddon/src/csaddon.qc",
    "content": "//FIXME: slice tool is a bit shite and has no way to change points 1+2, only the last one can be freely changed.\r\n\r\nvar float autocvar_ca_show = 0;\r\nvar float autocvar_ca_editormode = MODE_LIGHTEDIT;\r\nstring autocvar_ca_colourtint;\r\n\r\nvar vector vidsize = '640 480 0';\r\nvector curmousepos;\r\nvector mousediff;\r\nvector originalmousepos;\r\nfloat mousedown;\r\nfloat shiftdown;\r\nfloat ctrldown;\r\nvector mousenear;\r\nvector mousefar;\r\n\r\nfloat releasedmouse;\r\n\r\n//#define WALLBROWSERS\r\n#ifdef WALLBROWSERS\r\nstring pointedshadername;\r\n#endif\r\n\r\nfloat editorrsd[editornames.length];\r\n\r\nstatic void() csaddon_savemap =\r\n{\r\n\tlocalcmd(strcat(\"mod_terrain_save \\\"\", mapname, \"\\\"\\n\"));\r\n};\r\n\r\nstruct\r\n{\r\n\tstring text;\r\n\tstring cmd;\r\n\tvoid() cb;\r\n} editorcommands[] =\r\n{\r\n\t{\"Close\",\t\t\t\"ca_show 0\\n\"},\r\n\t{\"Noclip\",\t\t\t\"noclip\\n\"},\r\n\t{\"Notarget\",\t\t\t\"notarget\\n\"},\r\n\t{\"Fullbright\",\t\t\t\"toggle r_fullbright\\n\"},\r\n\t{\"Brush Editor Bindings\",\t\"brushedit_binds\\n\"},\r\n\t{\"Save\",\t\t\t__NULL__, csaddon_savemap},\r\n};\r\nfloat(float key, float unic, vector mouse) editor_options_key =\r\n{\r\n\tif (key == K_MOUSE1)\r\n\t{\r\n\t\tint sel = (mouse_y - 16) / 8;\r\n\t\tif (sel >= 0 && sel < editorcommands.length)\r\n\t\t{\r\n\t\t\tif (editorcommands[sel].cb)\r\n\t\t\t\teditorcommands[sel].cb();\r\n\t\t\telse\r\n\t\t\t\tlocalcmd(editorcommands[sel].cmd);\r\n\t\t\treturn TRUE;\r\n\t\t}\r\n\t}\r\n\treturn FALSE;\r\n};\r\nvoid(vector mouse) editor_options_overlay =\r\n{\r\n\tvector pos = '0 16';\r\n\tint sel = (mouse_y - pos_y) / 8;\r\n\tfor (int i = 0; i < editorcommands.length; i++)\r\n\t{\r\n\t\tstring txt = editorcommands[i].text;\r\n\t\tstring cmd = editorcommands[i].cmd;\r\n\t\tif (substring(cmd, 0, 7) == \"toggle \")\r\n\t\t{\r\n\t\t\tif (cvar(substring(cmd, 7, -2)))\r\n\t\t\t\ttxt = strcat(txt, \" (ON)\");\r\n\t\t\telse\r\n\t\t\t\ttxt = strcat(txt, \" (OFF)\");\r\n\t\t}\r\n\t\tdrawstring(pos, txt, \t\t'8 8', (sel==i)?'0 0 1' :'1 1 1', 1, 0);\r\n\t\tpos_y += 8;\r\n\t}\r\n};\r\n\r\nstatic void() perf_renderscene =\r\n{\r\n\trenderscene();\r\n};\r\n\r\n/*the renderscene builtin in the parent progs is redirected to here*/\r\nvoid() wrap_renderscene =\r\n{\r\n\tfloat x;\r\n\tvector col;\r\n\tlocal float i;\r\n\t\r\n\tif (ca_checksave)\r\n\t{\r\n\t\tif (ca_checksave(autocvar_ca_show?autocvar_ca_editormode:0))\r\n\t\t{\r\n\t\t\tca_checksave = __NULL__;\r\n\t\t\tlocalcmd(\"changelevel . .\\n\");\r\n\t\t}\r\n\t}\r\n\r\n\t//don't do anything if this is a subview. this avoids getting confused.\r\n\tif (!(float)getproperty(VF_DRAWWORLD))\r\n\t{\r\n\t\trenderscene();\r\n\t\treturn;\r\n\t}\r\n\r\n\tvidsize = getproperty(VF_SCREENVSIZE);\r\n\r\n\t/*inactive? then show nothing*/\r\n\tif (!autocvar_ca_show)\r\n\t{\r\n\t\tmousefar = unproject((vidsize*0.5) + '0 0 8192');\r\n\t\tmousenear = unproject(vidsize*0.5);\r\n\r\n#ifdef CAMQUAKE\r\n\t\tif (isdemo())\r\n\t\t\tspline_overrides(gettime(5));\r\n#endif\r\n\r\n\t\trenderscene();\r\n\r\n\t\tif (autocvar_ca_colourtint != \"\")\r\n\t\t{\r\n\t\t\tlocal string shdrname = strcat(\"tint_\", autocvar_ca_colourtint);\r\n\t\t\t/*make sure the shader exist*/\r\n\t\t\tshaderforname(shdrname,\r\n\t\t\t\t\t\"{\\n\"\r\n\t\t\t\t\t\t//if we can actually use glsl...\r\n\t\t\t\t\t\t\"if $glsl\\n\"\r\n\t\t\t\t\t\t\t\"glslprogram\\n\"\r\n\t\t\t\t\t\t\t\"{\\n\"\r\n\t\t\t\t\t\t\t\t\"varying vec4 tf;\\n\"\r\n\t\t\t\t\t\t\t\t\"#ifdef VERTEX_SHADER\\n\"\r\n\t\t\t\t\t\t\t\t\t\"void main ()\\n\"\r\n\t\t\t\t\t\t\t\t\t\"{\\n\"\r\n\t\t\t\t\t\t\t\t\t\t\"gl_Position = tf = ftetransform();\\n\"\r\n\t\t\t\t\t\t\t\t\t\"}\\n\"\r\n\t\t\t\t\t\t\t\t\"#endif\\n\"\r\n\r\n\t\t\t\t\t\t\t\t\"#ifdef FRAGMENT_SHADER\\n\"\r\n\t\t\t\t\t\t\t\t\t\"uniform sampler2D s_t0;\\n\"\r\n\t\t\t\t\t\t\t\t\t\"uniform sampler3D s_t1;\\n\"\r\n\t\t\t\t\t\t\t\t\t\"void main()\\n\"\r\n\t\t\t\t\t\t\t\t\t\"{\\n\"\r\n\t\t\t\t\t\t\t\t\t\t\"vec2 fc;\\n\"\r\n\t\t\t\t\t\t\t\t\t\t\"fc = tf.xy / tf.w;\\n\"\r\n\t\t\t\t\t\t\t\t\t\t\"vec3 raw = texture2D(s_t0, (1.0 + fc) / 2.0).rgb;\\n\"\r\n\r\n\t\t\t\t\t\t\t\t\t\t//scale+bias the sample to not clamp out at the edges\r\n\t\t\t\t\t\t\t\t\t\t\"#define LUTSIZE 16.0\\n\"\r\n\t\t\t\t\t\t\t\t\t\t\"vec3 scale = vec3((LUTSIZE-1.0)/LUTSIZE);\\n\"\r\n\t\t\t\t\t\t\t\t\t\t\"vec3 bias = vec3(1.0/(2.0*LUTSIZE));\\n\"\r\n\r\n\t\t\t\t\t\t\t\t\t\t\"gl_FragColor = texture3D(s_t1, raw * scale + bias);\\n\"\r\n\t\t\t\t\t\t\t\t\t\"}\\n\"\r\n\t\t\t\t\t\t\t\t\"#endif\\n\"\r\n\t\t\t\t\t\t\t\"}\\n\"\r\n\t\t\t\t\t\t\t\"{\\n\"\r\n\t\t\t\t\t\t\t\t\"map $currentrender\\n\"\r\n\t\t\t\t\t\t\t\"}\\n\"\r\n\t\t\t\t\t\t\t\"{\\n\"\r\n\t\t\t\t\t\t\t\t//16*16*16 image\r\n\t\t\t\t\t\t\t\t\"clampmap $3d:\",autocvar_ca_colourtint,\"\\n\"\r\n\t\t\t\t\t\t\t\"}\\n\"\r\n\t\t\t\t\t\t//else (glsl not available)\r\n\t\t\t\t\t\t\"else\\n\"\r\n\t\t\t\t\t\t\t//just don't draw anything.\r\n\t\t\t\t\t\t\t\"surfaceparm nodraw\\n\"\r\n\t\t\t\t\t\t\"endif\\n\"\r\n\t\t\t\t\t\"}\\n\"\r\n\t\t\t\t);\r\n\t\t\tdrawpic(getproperty(VF_MIN), shdrname, getproperty(VF_SIZE), '1 1 1', 1);\r\n\t\t}\r\n\r\n\t\tif (releasedmouse)\r\n\t\t{\t//remove the cursor if we activated it\r\n\t\t\treleasedmouse = FALSE;\r\n\t\t\tsetcursormode(FALSE);\r\n\t\t}\r\n\t\tmousedown = 0;\r\n\t\treturn;\r\n\t}\r\n\t\r\n\tif (mousedown & 2)\r\n\t{\t//RMB re-enables mlook\r\n\t\tif (releasedmouse)\r\n\t\t{\r\n\t\t\treleasedmouse = FALSE;\r\n\t\t\tsetcursormode(FALSE);\r\n\t\t}\r\n\t}\r\n\telse\r\n\t{\r\n\t\tif (FALSE == getcursormode(FALSE))\r\n\t\t{\t//only release it again if it should be released.\r\n\t\t\tsetcursormode(TRUE);\r\n\t\t\treleasedmouse = TRUE;\r\n\t\t}\r\n\t}\r\n\r\n\t/*hide hud and crosshair*/\r\n\tsetproperty(VF_DRAWENGINESBAR, 0);\r\n\tsetproperty(VF_DRAWCROSSHAIR, 0);\r\n\r\n\tvector mn = (vector)getproperty(VF_MIN);\r\n\tvector sz = (vector)getproperty(VF_SIZE);\r\n\tif (curmousepos_x >= mn_x && curmousepos_x <= mn_x+sz_x && curmousepos_y >= mn_y && curmousepos_y <= mn_y+sz_y)\r\n\t{\r\n\t\tmousefar = unproject(curmousepos + '0 0 8192');\r\n\t\tmousenear = unproject(curmousepos);\r\n\t}\r\n\r\n\tif (autocvar_ca_editormode == MODE_LIGHTEDIT)\r\n\t\teditor_lights_addentities();\r\n\telse if (autocvar_ca_editormode == MODE_ENTSEDIT)\r\n\t\teditor_ents_addentities();\r\n#ifdef CAMQUAKE\r\n\telse if (autocvar_ca_editormode == MODE_SPLINEEDIT)\r\n\t\teditor_spline_addentities();\r\n#endif\r\n\telse if (autocvar_ca_editormode == MODE_TERRAINEDIT)\r\n\t\teditor_terrain_addentities(curmousepos);\r\n\telse if (autocvar_ca_editormode == MODE_BRUSHEDIT)\r\n\t\teditor_brushes_addentities(curmousepos);\r\n\r\n\tperf_renderscene();\r\n\r\n\tfor (i = 0, x = 0; i < editornames.length; i+=1)\r\n\t{\r\n\t\tif (editornames[i] != \"\")\r\n\t\t{\r\n\t\t\tfloat w = stringwidth(editornames[i], TRUE, '8 8 0') + 8;\r\n\t\t\tif (autocvar_ca_editormode == i)\r\n\t\t\t\tcol = '1 0 0';\r\n\t\t\telse if (curmousepos_y < 8 && curmousepos_x >= x && curmousepos_x < x+w)\r\n\t\t\t\tcol = '0 0 1';\r\n\t\t\telse\r\n\t\t\t\tcol = '1 1 1';\r\n\t\t\tdrawrawstring([x,0], editornames[i], '8 8 0', col, 1);\r\n\r\n\t\t\tx += w;\r\n\t\t}\r\n\t\teditorrsd[i] = x;\r\n\t}\r\n\r\n\tif (autocvar_ca_editormode == MODE_OPTIONS)\r\n\t\teditor_options_overlay(curmousepos);\r\n\telse if (autocvar_ca_editormode == MODE_LIGHTEDIT)\r\n\t\teditor_lights_overlay(curmousepos);\r\n#ifdef CAMQUAKE\r\n\telse if (autocvar_ca_editormode == MODE_SPLINEEDIT)\r\n\t{\r\n\t\teditor_spline_overlay(&curmousepos, mousediff);\r\n\t\toriginalmousepos = curmousepos;\r\n\t\tmousediff = '0 0 0';\r\n\t}\r\n#endif\r\n\telse if (autocvar_ca_editormode == MODE_PARTICLEEDIT)\r\n\t\teditor_particles_overlay(curmousepos);\r\n\telse if (autocvar_ca_editormode == MODE_TERRAINEDIT)\r\n\t\teditor_terrain_overlay(curmousepos);\r\n\telse if (autocvar_ca_editormode == MODE_ENTSEDIT)\r\n\t\teditor_ents_overlay(curmousepos);\r\n\telse if (autocvar_ca_editormode == MODE_BRUSHEDIT)\r\n\t\teditor_brushes_overlay(curmousepos);\r\n\r\n\tdrawcharacter(curmousepos - '4 4', '+', '8 8', '1 1 1', 1);\r\n};\r\n\r\nvar float(float,float,float,float) orig_input_event = __NULL__;\r\nfloat (float event, float parama, float paramb, float devid) CSQC_InputEvent =\r\n{\r\n\tif (event == IE_KEYDOWN || event == IE_KEYUP)\r\n\t{\r\n\t\tif (parama == K_RSHIFT || parama == K_LSHIFT)\r\n\t\t\tshiftdown = (event == IE_KEYDOWN);\r\n\t\tif (parama == K_RCTRL || parama == K_LCTRL)\r\n\t\t\tctrldown = (event == IE_KEYDOWN);\r\n\t\tif (parama == K_RALT || parama == K_LALT)\r\n\t\t\taltdown = (event == IE_KEYDOWN);\r\n\t}\r\n\tif (autocvar_ca_show)\r\n\t{\r\n\t\tif (event == IE_KEYDOWN)\r\n\t\t{\r\n\t\t\tif (parama == K_MOUSE1 && curmousepos_y < 8)\r\n\t\t\t{\r\n\t\t\t\tfloat i, nm = 0;\r\n\t\t\t\tfor (i = editorrsd.length-1; i >= 0; i--)\r\n\t\t\t\t{\r\n\t\t\t\t\tif (curmousepos_x < editorrsd[i])\r\n\t\t\t\t\t\tnm = i;\r\n\t\t\t\t\telse\r\n\t\t\t\t\t\tbreak;\r\n\t\t\t\t}\r\n\t\t\t\tcvar_set(\"ca_editormode\", ftos(nm));\r\n\t\t\t\treturn TRUE;\r\n\t\t\t}\r\n\t\t\tif (autocvar_ca_editormode == MODE_OPTIONS)\r\n\t\t\t{\r\n\t\t\t\tif (editor_options_key(parama, paramb, curmousepos))\r\n\t\t\t\t\treturn TRUE;\r\n\t\t\t}\r\n\t\t\telse if (autocvar_ca_editormode == MODE_LIGHTEDIT)\r\n\t\t\t{\r\n\t\t\t\tif (editor_lights_key(parama, paramb, curmousepos))\r\n\t\t\t\t\treturn TRUE;\r\n\t\t\t}\r\n#ifdef CAMQUAKE\r\n\t\t\telse if (autocvar_ca_editormode == MODE_SPLINEEDIT)\r\n\t\t\t{\r\n\t\t\t\tif (editor_spline_key(parama, paramb, &curmousepos, mousediff))\r\n\t\t\t\t{\r\n\t\t\t\t\toriginalmousepos = curmousepos;\r\n\t\t\t\t\tmousediff = '0 0 0';\r\n\t\t\t\t\treturn TRUE;\r\n\t\t\t\t}\r\n\t\t\t}\r\n#endif\r\n\t\t\telse if (autocvar_ca_editormode == MODE_PARTICLEEDIT)\r\n\t\t\t{\r\n\t\t\t\tif (editor_particles_key(parama, paramb, curmousepos))\r\n\t\t\t\t\treturn TRUE;\r\n\t\t\t}\r\n\t\t\telse if (autocvar_ca_editormode == MODE_TERRAINEDIT)\r\n\t\t\t{\r\n\t\t\t\tif (editor_terrain_key(parama, paramb, curmousepos))\r\n\t\t\t\t\treturn TRUE;\r\n\t\t\t}\r\n\t\t\telse if (autocvar_ca_editormode == MODE_BRUSHEDIT)\r\n\t\t\t{\r\n\t\t\t\tif (editor_brushes_key(parama, paramb, curmousepos))\r\n\t\t\t\t\treturn TRUE;\r\n\t\t\t}\r\n\t\t\telse if (autocvar_ca_editormode == MODE_ENTSEDIT)\r\n\t\t\t{\r\n\t\t\t\tif (editor_ents_key(parama, paramb, curmousepos))\r\n\t\t\t\t\treturn TRUE;\r\n\t\t\t}\r\n\t\t\tif (parama == K_MOUSE1)\r\n\t\t\t{\r\n\t\t\t\tmousedown |= 1;\r\n\t\t\t\treturn TRUE;\r\n\t\t\t}\r\n\t\t\tif (parama == K_MOUSE2)\r\n\t\t\t{\r\n\t\t\t\tmousedown |= 2;\r\n\t\t\t\treturn TRUE;\r\n\t\t\t}\r\n\t\t\tif (parama == K_MOUSE3)\r\n\t\t\t{\r\n\t\t\t\tmousedown |= 4;\r\n\t\t\t\treturn TRUE;\r\n\t\t\t}\r\n\t\t}\r\n\t\telse if (event == IE_KEYUP)\r\n\t\t{\r\n\t\t\tif (parama == K_MOUSE1)\r\n\t\t\t\tmousedown &= ~1;\r\n\t\t\tif (parama == K_MOUSE2)\r\n\t\t\t\tmousedown &= ~2;\r\n\t\t\tif (parama == K_MOUSE3)\r\n\t\t\t\tmousedown &= ~4;\r\n\t\t}\r\n\t\telse if (event == IE_MOUSEDELTA)\r\n\t\t{\r\n\t\t\tif (mousedown & 2)\r\n\t\t\t\treturn FALSE;\r\n\t\t\toriginalmousepos = curmousepos;\r\n\t\t\tcurmousepos_x += parama;\r\n\t\t\tcurmousepos_y += paramb;\r\n\t\t\tmousediff = curmousepos - originalmousepos;\r\n\t\t\tif (curmousepos_x < 0)\r\n\t\t\t\tcurmousepos_x = 0;\r\n\t\t\tif (curmousepos_y < 0)\r\n\t\t\t\tcurmousepos_y = 0;\r\n\t\t\tif (curmousepos_x > vidsize_x)\r\n\t\t\t\tcurmousepos_x = vidsize_x;\r\n\t\t\tif (curmousepos_y > vidsize_y)\r\n\t\t\t\tcurmousepos_y = vidsize_y;\r\n\t\t\treturn TRUE;\r\n\t\t}\r\n\t\telse if (event == IE_MOUSEABS)\r\n\t\t{\r\n\t\t\tcurmousepos_x = parama;\r\n\t\t\tcurmousepos_y = paramb;\r\n\t\t\tif (mousedown & 2)\r\n\t\t\t\treturn FALSE;\r\n\t\t\treturn TRUE;\r\n\t\t}\r\n\t}\r\n#ifdef WALLBROWSERS\r\n\telse if (pointedshadername != \"\")\r\n\t{\r\n/*\t\tif (parama == K_MOUSE2)\r\n\t\t{\r\n\t\t\tstrunzone(pointedshadername);\r\n\t\t\tpointedshadername = \"\";\r\n\t\t\tprint(\"release (mouse2)!\\n\");\r\n\t\t\treturn TRUE;\r\n\t\t}\r\n*/\r\n\t\tif (event == IE_KEYDOWN || event == IE_KEYUP)\r\n\t\t{\r\n\t\t\tgecko_keyevent(pointedshadername, parama, event);\r\n\t\t\tif (event == 0)\r\n\t\t\t\treturn TRUE;\r\n\t\t}\r\n\t}\r\n#endif\r\n\r\n\tif (orig_input_event)\r\n\t\treturn orig_input_event(event, parama, paramb, devid);\r\n\treturn FALSE;\r\n};\r\n\r\n\r\nvoid(float mask) wrap_addentities =\r\n{\r\n\tif (autocvar_ca_show)\r\n\t{\r\n\t\tmask = mask - (mask & MASK_VIEWMODEL);\r\n\t\tif (autocvar_ca_editormode == MODE_ENTSEDIT)\r\n\t\t\tmask = 0;\r\n\t}\r\n\taddentities(mask);\r\n};\r\n\r\nvar float autocvar_fov = 90;\r\nstatic void(float seat, vector mn, vector sz) renderscene_helper =\r\n{\r\n\tif (seat)\r\n\t{\r\n\t\tsetproperty(VF_ACTIVESEAT, seat, TRUE);\t//updates globals, true means resets view properties too (but not ents)\r\n\t\tsetproperty(VF_VIEWENTITY, player_localentnum);\r\n\t\tif (!intermission)\r\n\t\t\twrap_addentities(MASK_VIEWMODEL);\r\n\t}\r\n\tsetproperty(VF_VIEWPORT, mn, sz);\r\n\tsetproperty(VF_AFOV, autocvar_fov);\r\n\twrap_renderscene();\r\n};\r\n\r\n/*this is a fallback function, in case the main progs does not have one*/\r\nvoid(float width, float height, float do2d) CSQC_UpdateView =\r\n{\r\n\tfloat foo;\r\n\tclearscene();\r\n\tsetproperty(VF_ACTIVESEAT, 0, TRUE);\t//in_forceseat can prevent this from already being true, sadly. silly csqc mods with no splitscreen support...\r\n\tsetproperty(VF_DRAWENGINESBAR, 1);\r\n\tsetproperty(VF_DRAWCROSSHAIR, 1);\r\n\twrap_addentities(intermission?MASK_ENGINE:(MASK_VIEWMODEL|MASK_ENGINE));\r\n\r\n\tfoo = numclientseats;\r\n//\tif (autocvar_ca_show)\r\n//\t\tfoo = 0;\r\n\tswitch(foo)\r\n\t{\r\n\tcase 0:\t//just in case someone runs us in dp\r\n\tcase 1:\r\n\t\twrap_renderscene();\r\n\t\tbreak;\r\n\tcase 2:\r\n\t\trenderscene_helper(0, [0, 0], [width, height/2]);\r\n\t\trenderscene_helper(1, [0, height/2], [width, height/2]);\r\n\t\tsetviewprop(VF_LPLAYER, 0, FALSE);\r\n\t\tdrawline(1, [0, height/2], [width, height/2], '0 0 0', 1);\r\n\t\tbreak;\r\n\tcase 3:\r\n\t\trenderscene_helper(0, [0, 0], [width, height/2]);\r\n\t\trenderscene_helper(1, [0, height/2], [width/2, height/2]);\r\n\t\trenderscene_helper(2, [width/2, height/2], [width/2, height/2]);\r\n\t\tsetviewprop(VF_LPLAYER, 0, FALSE);\r\n\t\tdrawline(1, [0, height/2], [width, height/2], '0 0 0', 1);\r\n\t\tdrawline(1, [width/2, height/2], [width/2, height], '0 0 0', 1);\r\n\t\tbreak;\r\n\tdefault:\r\n\tcase 4:\r\n\t\trenderscene_helper(0, [0, 0], [width/2, height/2]);\r\n\t\trenderscene_helper(1, [width/2, 0], [width/2, height/2]);\r\n\t\trenderscene_helper(2, [0, height/2], [width/2, height/2]);\r\n\t\trenderscene_helper(3, [width/2, height/2], [width/2, height/2]);\r\n\t\tsetviewprop(VF_LPLAYER, 0, FALSE);\r\n\t\tdrawline(1, [0, height/2], [width, height/2], '0 0 0', 1);\r\n\t\tdrawline(1, [width/2, 0], [width/2, height], '0 0 0', 1);\r\n\t\tbreak;\r\n\t}\r\n};\r\n\r\nvoid() CSQC_Input_Frame =\r\n{\r\n\tif (autocvar_ca_show)\t//when we're using the UI, don't send any attack or jump stuff. these are generally annoying modifiers or so\r\n\t\tinput_buttons = 0;\r\n\t\t\r\n#ifdef WALLBROWSERS\r\n\tif ((input_buttons & 1) && pointedshadername == \"\")\r\n\t{\r\n\t\tvector t, o;\r\n\t\tt = mousefar;\r\n\t\to = mousenear;\r\n\t\tif (vlen(o - t) > 8192)\r\n\t\t\tt = o + normalize(t)*8192;\r\n\t\ttraceline(o, t, TRUE, world);\r\n\r\n\t\tif (vlen(trace_endpos - o) < 512)\r\n\t\t{\r\n\t\t\tfloat s = getsurfacenearpoint(trace_ent, trace_endpos);\r\n\t\t\tpointedshadername = getsurfacetexture(trace_ent, s);\r\n\t\t\tpointedsurfacenormal = getsurfacenormal(trace_ent, s);\r\n\t\t\tif (gecko_get_texture_extent(pointedshadername) == '0 0 0')\r\n\t\t\t\tpointedshadername = \"\";\r\n\t\t\tif (pointedshadername == \"\")\r\n\t\t\t\tpointedshadername = \"\";\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\t//fixme: no unfocus!\r\n\t\t\t\tpointedshadername = strzone(pointedshadername);\r\n\t\t\t\t//print(\"sending input to \", pointedshadername, \"\\n\");\r\n\r\n\t\t\t\tgecko_navigate(pointedshadername, \"cmd:focus\");\r\n\r\n\t\t\t\tpointedent = trace_ent;\r\n\t\t\t\tpointedsurface = s;\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\tif (pointedshadername != \"\")\r\n\t{\r\n\t\tinput_buttons &= ~1;\r\n\r\n\t\tmakevectors(input_angles);\r\n\t\tif (v_forward * pointedsurfacenormal >= 0)\r\n\t\t{\r\n\t\t\tgecko_navigate(pointedshadername, \"cmd:unfocus\");\r\n\t\t\tstrunzone(pointedshadername);\r\n\t\t\tpointedshadername = \"\";\r\n\t\t//\tprint(\"release (angle)!\\n\");\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tt = mousefar;\r\n\t\t\to = mousenear;\r\n\t\t\tif (vlen(o - t) > 8192)\r\n\t\t\t\tt = o + normalize(t)*8192;\r\n\t\t\ttraceline(o, t, TRUE, world);\r\n\r\n\t\t\t//this code is vile.\r\n\t\t\t//it expects the texture to be aligned to the surface plane with flat projection.\r\n\t\t\t//it will also break on any polysoup/patch surfaces\r\n\t\t\t//it should at least accept any weird shaped cut up triangles, so long as they're all flat on the surface.\r\n\r\n\t\t\tvector xyz1, xyz2, xyz3;\r\n\t\t\tvector st1, st2, st3;\r\n\t\t\txyz1 = getsurfacepointattribute(pointedent, pointedsurface, 0, 0);\r\n\t\t\txyz2 = getsurfacepointattribute(pointedent, pointedsurface, 1, 0);\r\n\t\t\txyz3 = getsurfacepointattribute(pointedent, pointedsurface, 2, 0);\r\n\t\t\tst1 = getsurfacepointattribute(pointedent, pointedsurface, 0, 4);\r\n\t\t\tst2 = getsurfacepointattribute(pointedent, pointedsurface, 1, 4);\r\n\t\t\tst3 = getsurfacepointattribute(pointedent, pointedsurface, 2, 4);\r\n\r\n\t\t\tfloat f1, f2, backoff;\r\n\t\t\tvector dir1 = xyz2 - xyz1;\r\n\t\t\tvector std1 = st2 - st1;\r\n\t\t\tvector dir2 = xyz3 - xyz1;\r\n\t\t\tvector std2 = st3 - st1;\r\n\t\t\tf1 = 1/vlen(dir1);\r\n\t\t\tdir1 *= f1;\r\n\t\t\tstd1 *= f1;\r\n\t\t\tbackoff = dir1 * dir2;\r\n\t\t\tstd2 = std2 - (std1 * backoff);\r\n\t\t\tdir2 = dir2 - (dir1 * backoff);\r\n\t\t\tf2 = 1/vlen(dir2);\r\n\t\t\tdir1 *= f2;\r\n\t\t\tstd1 *= f2;\r\n\t\t\t//the two directions should be perpendicular, and normalized\r\n\r\n\t\t\tfloat d1 = dir1*xyz1;\r\n\t\t\tfloat p = dir1*trace_endpos;\r\n\t\t\tfloat d2 = dir1*(xyz1+dir1);\r\n\t\t\tf1 = (p-d1) / (d2 - d1);\r\n\r\n\t\t\td1 = dir2*xyz1;\r\n\t\t\tp = dir2*trace_endpos;\r\n\t\t\td2 = dir2*(xyz1+dir2);\r\n\t\t\tf2 = (p-d1) / (d2 - d1);\r\n\r\n\t\t\tvector res = st1 + std1*f1 + std2*f2;\r\n\r\n\t\t\tres_x-= floor(res_x);\r\n\t\t\tres_y-= floor(res_y);\r\n\t\t\tgecko_mousemove(pointedshadername, res_x, res_y);\r\n\r\n//\t\t\tpointparticles(particleeffectnum(\"te_spike\"), xyz1 + dir1*f1 + dir2*f2, trace_plane_normal, 1);\r\n\t\t}\r\n\t}\r\n#endif\r\n};\r\n\r\n/*\r\nfloat(string txt, string info) CSQC_ConsoleLink =\r\n{\r\n\tprint(\"Console link pressed. Text \\\"\", txt, \"\\\"\\n\");\r\n\tprint(\"Info: \\\"\", info, \"\\\"\\n\");\r\n\r\n\treturn FALSE;\r\n};\r\n*/\r\n\r\nfloat(string cmd) CSQC_ConsoleCommand =\r\n{\r\n\ttokenize(cmd);\r\n\treturn editor_brushes_command();\r\n};\r\n\r\n/*this is a fallback function, in case the main progs does not have one*/\r\n/*float (float event, float parama, float paramb, float devid) CSQC_InputEvent =\r\n{\r\n\treturn wrap_InputEvent(event, parama, paramb);\r\n};*/\r\n\r\nvoid(float prevprogs) init =\r\n{\r\n#ifdef CAMQUAKE\r\n\tCam_Init();\r\n#else\r\n\teditornames[MODE_SPLINEEDIT] = 0;\r\n#endif\r\n\r\n\tif (prevprogs >= 0)\r\n\t{\r\n\t\t/*its easy to wrap a builtin*/\r\n\t\texternset(0, wrap_renderscene, \"renderscene\");\r\n\t\texternset(0, wrap_addentities, \"addentities\");\r\n\r\n\t\t/*wrap the parent's input event function in favour of ours*/\r\n\t\torig_input_event = (float(float, float, float, float))externvalue(0, \"CSQC_InputEvent\");\r\n\t\texternset(0, CSQC_InputEvent, \"CSQC_InputEvent\");\r\n\t}\r\n\tcsfixups();\r\n\tlocalcmd(sprintf(\"alias rtlight_editor \\\"set ca_show 1; set ca_editormode %g\\\"\\n\", MODE_LIGHTEDIT));\r\n\tlocalcmd(sprintf(\"alias camquake_editor \\\"set ca_show 1; set ca_editormode %g\\\"\\n\", MODE_SPLINEEDIT));\r\n\tlocalcmd(sprintf(\"alias terrain_editor \\\"set ca_show 1; set ca_editormode %g\\\"\\n\", MODE_TERRAINEDIT));\r\n\tlocalcmd(sprintf(\"alias r_part_editor \\\"set ca_show 1; set ca_editormode %g\\\"\\n\", MODE_PARTICLEEDIT));\r\n\tlocalcmd(sprintf(\"alias entities_editor \\\"set ca_show 1; set ca_editormode %g\\\"\\n\", MODE_ENTSEDIT));\r\n\t\r\n\t//brush editor needs some commands for easier binds.\r\n\teditor_brushes_registercommands();\r\n};\r\nvoid() initents =\r\n{\r\n\tworld_hitcontentsmaski = &world.hitcontentsmaski;\r\n}\r\n\r\nvoid() CSQC_Shutdown =\r\n{\r\n#ifdef CAMQUAKE\r\n\tspline_shutdown();\r\n#endif\r\n\tbrush_deselectall();\t//so they don't stay hidden when restarted...\r\n};\r\n\r\n"
  },
  {
    "path": "quakec/csaddon/src/csaddon.src",
    "content": "#output \"../csaddon.dat\"\r\n//pr_dumpplatform -FFTE -Fdefines -TCS -O csplat\r\n\r\n//#pragma flag enable assumeint\r\n//#pragma flag enable typeexplicit\r\n\r\n#define CSQC\r\n#define _ACCESSORS\r\n#pragma target FTE_5768\r\n//#pragma optimise 2\r\n//#pragma optimise no-filename\r\n\r\n#includelist\r\ncsplat.qc\r\ncsfixups.qc\r\n\r\neditor_lights.qc\r\neditor_terrain.qc\r\nbrush_selection.qc\r\nbrush_history.qc\r\nbrush_manip.qc\r\nbrush_draw.qc\r\nbrush_vertedit.qc\r\neditor_brushes.qc\r\neditor_ents.qc\r\ntextfield.qc\r\neditor_particles.qc\r\nmenu.qc\r\ncam.qc\r\ncsaddon.qc\r\n#endlist"
  },
  {
    "path": "quakec/csaddon/src/csfixups.qc",
    "content": "//addons cannot use certain optimisations - the engine MUST be able to find+fix their string+function indexes.\r\n#pragma optimise addon\t//disables any optimisations that would break addons.\r\n\r\n//#define CAMQUAKE\r\n\r\n//with addons, the engine only tracks one set of globals for most things.\r\n//thus we use pointers in the addon to refer to that set of globals.\r\n//this avoids having to make things shared, and having to pay for every transition.\r\n\r\nvector *ptr_trace_endpos;\r\n#define trace_endpos (*ptr_trace_endpos)\r\n\r\nvector *ptr_trace_plane_normal;\r\n#define trace_plane_normal (*ptr_trace_plane_normal)\r\n\r\nentity *ptr_trace_ent;\r\n#define trace_ent (*ptr_trace_ent)\r\n\r\nentity *ptr_self;\r\n#define self \t\t(*ptr_self)\r\n\r\nvoid() csfixups =\r\n{\r\n\tptr_trace_endpos\t\t= (vector*)externvalue(0, \"&trace_endpos\");\r\n\tptr_trace_plane_normal\t= (vector*)externvalue(0, \"&trace_plane_normal\");\r\n\tptr_trace_ent\t\t\t= (entity*)externvalue(0, \"&trace_ent\");\r\n\tptr_self\t\t\t\t= (entity*)externvalue(0, \"&self\");\r\n};\r\n\r\nint *world_hitcontentsmaski; //evilness, so we can assign to world without errors.\r\n\r\nvector mousenear;\r\nvector mousefar;\r\nfloat mousedown;\r\nfloat shiftdown;\r\nfloat ctrldown;\r\nfloat altdown;\r\nvector curmousepos;\r\n\r\n\r\n\r\nenum\r\n{\r\n\tMODE_OPTIONS,\r\n\r\n\tMODE_LIGHTEDIT,\r\n\tMODE_SPLINEEDIT,\r\n\tMODE_TERRAINEDIT,\r\n\tMODE_BRUSHEDIT,\r\n\tMODE_PARTICLEEDIT,\r\n\tMODE_ENTSEDIT,\r\n\r\n\tMODE_COUNT\r\n};\r\n\r\nvar string editornames[MODE_COUNT] = {\"OPTIONS\",\"LIGHTS\",\"SPLINES\", \"TERRAIN\", \"BRUSHES\", \"PARTICLES\", \"ENTITIES\"};\r\n\r\nvar float(float vismode) ca_checksave;\r\n\r\n\r\n#define isnull(v) (!(int)(v))\r\n#define notnull(v) ((int)(v))\r\n"
  },
  {
    "path": "quakec/csaddon/src/csplat.qc",
    "content": "/*\nThis file was generated by FTE Quake 5781, dated 2020-10-26T11:48:50.477494Z.\nThis file can be regenerated by issuing the following command:\npr_dumpplatform -o fteextensions\n(Use the -help arg for a list of available args)\n*/\n#pragma noref 1\n//#pragma flag enable logicops\n#pragma warning error Q101 /*too many parms. The vanilla qcc didn't validate properly, hence why fteqcc normally treats it as a warning.*/\n#pragma warning error Q105 /*too few parms. The vanilla qcc didn't validate properly, hence why fteqcc normally treats it as a warning.*/\n#pragma warning error Q106 /*assignment to constant/lvalue. Define them as var if you want to initialise something.*/\n#pragma warning error Q208 /*system crc unknown. Compatibility goes out of the window if you disable this.*/\n#pragma warning enable F301 /*non-utf-8 strings. Think of the foreigners! Also think of text editors that insist on screwing up your char encodings.*/\n#pragma warning enable F302 /*uninitialised locals. They usually default to 0 in qc (except in recursive functions), but its still probably a bug*/\n#if !defined(CSQC) && !defined(NQSSQC) && !defined(QWSSQC)&& !defined(MENU)\n\t#ifdef QUAKEWORLD\n\t\t#define QWSSQC\n\t#else\n\t\t#define NQSSQC\n\t#endif\n#endif\n#if !defined(SSQC) && (defined(QWSSQC) || defined(NQSSQC))\n\t#define SSQC\n#endif\n#if defined(CSQC) || defined(MENU)\n\t#define DEP_CSQC DEP\n#else\n\t#define DEP_CSQC __deprecated(\"Use CSQC for this\")\n#endif\n#ifndef DEP\n\t#define DEP __deprecated //predefine this if you want to avoid our deprecation warnings.\n#endif\n#ifndef FTEDEP\n\t#define FTEDEP(reason) //for symbols deprecated in FTE that may still be useful/required for other engines\n#endif\n#define FTE_PEXT_SETVIEW /* NQ's svc_setview works correctly even in quakeworld */\n#define DP_ENT_SCALE\n#define FTE_PEXT_LIGHTSTYLECOL\n#define DP_ENT_ALPHA\n#define FTE_PEXT_VIEW2\n#define FTE_PEXT_ACURATETIMINGS\n#define FTE_PEXT_SOUNDDBL\n#define FTE_PEXT_FATNESS\n#define DP_HALFLIFE_MAP\n#define FTE_PEXT_TE_BULLET\n#define FTE_PEXT_HULLSIZE\n#define FTE_PEXT_MODELDBL\n#define FTE_PEXT_ENTITYDBL\n#define FTE_PEXT_ENTITYDBL2\n#define FTE_PEXT_FLOATCOORDS\n#define FTE_PEXT_VWEAP\n#define FTE_PEXT_Q2BSP\n#define FTE_PEXT_Q3BSP\n#define DP_ENT_COLORMOD\n#define FTE_HEXEN2\n#define FTE_PEXT_SPAWNSTATIC\n#define FTE_PEXT_CUSTOMTENTS\n#define FTE_PEXT_256PACKETENTITIES\n#define TEI_SHOWLMP2\n#define DP_GFX_QUAKE3MODELTAGS\n#define FTE_PK3DOWNLOADS\n#define PEXT_CHUNKEDDOWNLOADS\n#define EXT_CSQC_SHARED\n#define PEXT_DPFLAGS\n#define EXT_CSQC\n#define BX_COLOREDTEXT\n#define DP_CON_SET /* The 'set' console command exists, and can be used to create/set cvars. */\n#define DP_CON_SETA /* The 'seta' console command exists, like the 'set' command, but also marks the cvar for archiving, allowing it to be written into the user's config. Use this command in your default.cfg file. */\n#define DP_CSQC_ROTATEMOVES\n#define DP_EF_ADDITIVE\n#define DP_EF_BLUE\n#define DP_EF_FULLBRIGHT\n#define DP_EF_NODEPTHTEST\n#define DP_EF_NODRAW\n#define DP_EF_NOGUNBOB\n#define DP_EF_NOSHADOW\n#define DP_EF_RED\n#define DP_ENT_CUSTOMCOLORMAP\n#define DP_ENT_EXTERIORMODELTOCLIENT\n#define DP_ENT_TRAILEFFECTNUM /* self.traileffectnum=particleeffectnum(\"myeffectname\"); can be used to attach a particle trail to the given server entity. This is equivelent to calling trailparticles each frame. */\n#define DP_ENT_VIEWMODEL\n#define DP_GECKO_SUPPORT\n#define DP_GFX_FONTS\n#define DP_GFX_SKINFILES\n#define DP_GFX_SKYBOX\n#define DP_HALFLIFE_MAP_CVAR\n#define DP_INPUTBUTTONS\n#define DP_LIGHTSTYLE_STATICVALUE\n#define DP_LITSUPPORT\n#define DP_MONSTERWALK /* MOVETYPE_WALK is valid on non-player entities. Note that only players receive acceleration etc in line with none/bounce/fly/noclip movetypes on the player, thus you will have to provide your own accelerations (incluing gravity) yourself. */\n#define DP_MOVETYPEBOUNCEMISSILE\n#define DP_MOVETYPEFOLLOW\n#define DP_QC_ASINACOSATANATAN2TAN\n#define DP_QC_CHANGEPITCH\n#define DP_QC_COPYENTITY\n#define DP_QC_CRC16\n#define DP_QC_CVAR_DEFSTRING\n#define DP_QC_CVAR_STRING\n#define DP_QC_CVAR_TYPE\n#define DP_QC_DIGEST_SHA256\n#define DP_QC_EDICT_NUM\n#define DP_QC_ENTITYDATA\n#define DP_QC_ETOS\n#define DP_QC_FINDCHAIN\n#define DP_QC_FINDCHAINFLOAT\n#define DP_QC_FINDFLAGS\n#define DP_QC_FINDCHAINFLAGS\n#define DP_QC_FINDFLOAT\n#define DP_QC_FS_SEARCH\n#define DP_QC_FS_SEARCH_PACKFILE\n#define DP_QC_GETSURFACE\n#define DP_QC_GETSURFACEPOINTATTRIBUTE\n#define DP_QC_GETTAGINFO\n#define DP_QC_MINMAXBOUND\n#define DP_QC_MULTIPLETEMPSTRINGS /* Superseded by DP_QC_UNLIMITEDTEMPSTRINGS. Functions that return a temporary string will not overwrite/destroy previous temporary strings until at least 16 strings are returned (or control returns to the engine). */\n#define DP_QC_RANDOMVEC\n#define DP_QC_RENDER_SCENE /* clearscene+addentity+setviewprop+renderscene+setmodel are available to menuqc. WARNING: DP advertises this extension without actually supporting it, FTE does actually support it. */\n#define DP_QC_SINCOSSQRTPOW\n#define DP_QC_SPRINTF /* Provides the sprintf builtin, which allows for rich formatting along the lines of C's function with the same name. Not to be confused with QC's sprint builtin. */\n#define DP_QC_STRFTIME\n#define DP_QC_STRING_CASE_FUNCTIONS\n#define DP_QC_STRINGBUFFERS\n#define DP_QC_STRINGCOLORFUNCTIONS\n#define DP_QC_STRREPLACE\n#define DP_QC_TOKENIZEBYSEPARATOR\n#define DP_QC_TRACEBOX\n#define DP_QC_TRACETOSS\n#define DP_QC_TRACE_MOVETYPE_HITMODEL\n#define DP_QC_TRACE_MOVETYPE_WORLDONLY\n#define DP_QC_TRACE_MOVETYPES\n#define DP_QC_UNLIMITEDTEMPSTRINGS /* Supersedes DP_QC_MULTIPLETEMPSTRINGS, superseded by FTE_QC_PERSISTENTTEMPSTRINGS. Specifies that all temp strings will be valid at least until the QCVM returns. */\n#define DP_QC_URI_ESCAPE\n#define DP_QC_URI_GET\n#define DP_QC_URI_POST\n#define DP_QC_VECTOANGLES_WITH_ROLL\n#define DP_QC_VECTORVECTORS\n#define DP_QC_WHICHPACK\n#define DP_QUAKE2_MODEL\n#define DP_QUAKE2_SPRITE\n#define DP_QUAKE3_MODEL\n#define DP_REGISTERCVAR\n#define DP_SND_SOUND7_WIP2\n#define DP_SND_STEREOWAV\n#define DP_SND_OGGVORBIS\n#define DP_SOLIDCORPSE\n#define DP_SPRITE32\n#define DP_SV_BOTCLIENT\n#define DP_SV_CLIENTCAMERA /* Works like svc_setview except also handles pvs. */\n#define DP_SV_CLIENTCOLORS /* Provided only for compatibility with DP. */\n#define DP_SV_CLIENTNAME /* Provided only for compatibility with DP. */\n#define DP_SV_DRAWONLYTOCLIENT\n#define DP_SV_DROPCLIENT /* Equivelent to quakeworld's stuffcmd(self,\"disconnect\\n\"); hack */\n#define DP_SV_EFFECT\n#define DP_SV_EXTERIORMODELFORCLIENT\n#define DP_SV_NODRAWTOCLIENT\n#define DP_SV_PLAYERPHYSICS /* Allows reworking parts of NQ player physics. USE AT OWN RISK - this necessitates NQ physics and is thus guarenteed to break prediction. */\n#define DP_SV_POINTSOUND\n#define DP_SV_PRECACHEANYTIME /* Specifies that the various precache builtins can be called at any time. WARNING: precaches are sent reliably while sound events, modelindexes, and particle events are not. This can mean sounds and particles might not work the first time around, or models may take a while to appear (after the reliables are received and the model is loaded from disk). Always attempt to precache a little in advance in order to reduce these issues (preferably at the start of the map...) */\n#define DP_SV_PRINT /* Says that the print builtin can be used from nqssqc (as well as just csqc), bypassing the developer cvar issues. */\n#define DP_SV_SETCOLOR\n#define DP_SV_SPAWNFUNC_PREFIX\n#define DP_SV_WRITEPICTURE\n#define DP_SV_WRITEUNTERMINATEDSTRING\n#define DP_TE_BLOOD\n#define DP_TE_CUSTOMFLASH\n#define DP_TE_EXPLOSIONRGB\n#define DP_TE_PARTICLECUBE\n#define DP_TE_PARTICLERAIN\n#define DP_TE_PARTICLESNOW\n#define DP_TE_SMALLFLASH\n#define DP_TE_SPARK\n#define DP_TE_STANDARDEFFECTBUILTINS\n#define DP_VIEWZOOM\n#define EXT_BITSHIFT\n#define EXT_DIMENSION_VISIBILITY\n#define EXT_DIMENSION_PHYSICS\n#define EXT_DIMENSION_GHOST\n#define FRIK_FILE\n#define FTE_CALLTIMEOFDAY /* Replication of mvdsv functionality (call calltimeofday to cause 'timeofday' to be called, with arguments that can be saved off to a global). Generally strftime is simpler to use. */\n#define FTE_CSQC_ALTCONSOLES /* The engine tracks multiple consoles. These may or may not be directly visible to the user. */\n#define FTE_CSQC_BASEFRAME /* Specifies that .basebone, .baseframe2, .baselerpfrac, baseframe1time, etc exist in csqc. These fields affect all bones in the entity's model with a lower index than the .basebone field, allowing you to give separate control to the legs of a skeletal model, without affecting the torso animations. */\n#define FTE_CSQC_HALFLIFE_MODELS\n#define FTE_CSQC_SERVERBROWSER /* Provides builtins to query the engine's serverbrowser servers list from ssqc. Note that these builtins are always available in menuqc. */\n#define FTE_CSQC_SKELETONOBJECTS /* Provides container objects for skeletal bone data, which can be modified on a per bone basis if needed. This allows you to dynamically generate animations (or just blend them with greater customisation) instead of being limited to a single animation or two. */\n#define FTE_CSQC_RAWIMAGES /* Provides raw rgba image access to csqc. With this, the csprogs can read textures into qc-accessible memory, modify it, and then upload it to the renderer. */\n#define FTE_CSQC_RENDERTARGETS /* VF_RT_DESTCOLOUR exists and can be used to redirect any rendering to a texture instead of the screen. */\n#define FTE_CSQC_REVERB /* Specifies that the mod can create custom reverb effects. Whether they will actually be used or not depends upon the sound driver. */\n#define FTE_CSQC_WINDOWCAPTION /* Provides csqc with the ability to change the window caption as displayed when running windowed or in the task bar when switched out. */\n#define FTE_ENT_SKIN_CONTENTS /* self.skin = CONTENTS_WATER; makes a brush entity into water. use -16 for a ladder. */\n#define FTE_ENT_UNIQUESPAWNID\n#define FTE_EXTENDEDTEXTCODES\n#define FTE_FORCESHADER /* Allows csqc to override shaders on models with an explicitly named replacement. Also allows you to define shaders with a fallback if it does not exist on disk. */\n#define FTE_FORCEINFOKEY /* Provides an easy way to change a user's userinfo from the server. */\n#define FTE_GFX_QUAKE3SHADERS /* specifies that the engine has full support for vanilla quake3 shaders */\n#define FTE_GFX_REMAPSHADER /* With the raw power of stuffcmds, the r_remapshader console command is exposed! This mystical command can be used to remap any shader to another. Remapped shaders that specify $diffuse etc in some form will inherit the textures implied by the surface. */\n#define FTE_GFX_MODELEVENTS /* Provides a query for per-animation events in model files, including from progs/foo.mdl.events files. */\n#define FTE_ISBACKBUFFERED /* Allows you to check if a client has too many reliable messages pending. */\n#define FTE_MEMALLOC /* Allows dynamically allocating memory. Use pointers to access this memory. Memory will not be saved into saved games. */\n#define FTE_MEDIA_CIN /* playfilm command supports q2 cin files. */\n#define FTE_MEDIA_ROQ /* playfilm command supports q3 roq files. */\n#define FTE_MULTIPROGS /* Multiple progs.dat files can be loaded inside the same qcvm. Insert new ones with addprogs inside the 'init' function, and use externvalue+externset to rewrite globals (and hook functions) to link them together. Note that the result is generally not very clean unless you carefully design for it beforehand. */\n#define FTE_MULTITHREADED /* Faux multithreading, allowing multiple contexts to run in sequence. */\n#define FTE_MVD_PLAYERSTATS /* In csqc, getplayerstat can be used to query any player's stats when playing back MVDs. isdemo will return 2 in this case. */\n#define FTE_PART_SCRIPT /* Specifies that the r_particledesc cvar can be used to select a list of particle effects to load from particles/foo.cfg, the format of which is documented elsewhere. */\n#define FTE_PART_NAMESPACES /* Specifies that the engine can use foo.bar to load effect foo from particle description bar. When used via ssqc, this should cause the client to download whatever effects as needed. */\n#define FTE_PART_NAMESPACE_EFFECTINFO /* Specifies that effectinfo.bar can load effects from effectinfo.txt for DP compatibility. */\n#define FTE_QC_BASEFRAME /* Specifies that .basebone and .baseframe exist in ssqc. These fields affect all bones in the entity's model with a lower index than the .basebone field, allowing you to give separate control to the legs of a skeletal model, without affecting the torso animations, from ssqc. */\n#define FTE_QC_FILE_BINARY /* Extends FRIK_FILE with binary read+write, as well as allowing seeking. Requires pointers. */\n#define FTE_QC_CHANGELEVEL_HUB /* Adds an extra argument to changelevel which is carried over to the next map in the 'spawnspot' global. Maps will be saved+reloaded until the extra argument is omitted again, purging all saved maps. Saved games will contain a copy of each preserved map. parm1-parm64 globals can be used, giving more space to transfer more player data. */\n#define FTE_QC_CHECKCOMMAND /* Provides a way to test if a console command exists, and whether its a command/alias/cvar. Does not say anything about the expected meanings of any arguments or values. */\n#define FTE_QC_CHECKPVS\n#define FTE_QC_CROSSPRODUCT\n#define FTE_QC_CUSTOMSKINS /* The engine supports the use of q3 skins, as well as the use of such skin 'files' to specify rich top+bottom colours, qw skins, geomsets, or texture composition even on non-players.. */\n#define FTE_QC_DIGEST_SHA1\n#define FTE_QC_DIGEST_SHA224\n#define FTE_QC_DIGEST_SHA384\n#define FTE_QC_DIGEST_SHA512\n#define FTE_QC_FS_SEARCH_SIZEMTIME\n#define FTE_QC_HARDWARECURSORS /* setcursormode exists in both csqc+menuqc, and accepts additional arguments to specify a cursor image to use when this module has focus. If the image exceeds hardware limits (or hardware cursors are unsupported), it will be emulated using regular draws - this at least still avoids conflicting cursors as only one will ever be used, even if console+menu+csqc are all overlayed. */\n#define FTE_QC_HASHTABLES /* Provides efficient string-based lookups. */\n#define FTE_QC_INFOKEY /* QuakeWorld's infokey builtin works, and reports at least name+topcolor+bottomcolor+ping(in ms)+ip(unmasked, but not always ipv4)+team(aka bottomcolor in nq). Does not require actual localinfo/serverinfo/userinfo, but they're _highly_ recommended to any engines with csqc */\n#define FTE_QC_INTCONV /* Provides string<>int conversions, including hex representations. */\n#define FTE_QC_MATCHCLIENTNAME\n#define FTE_QC_MULTICAST /* QuakeWorld's multicast builtin works along with MSG_MULTICAST, but also with unicast support. */\n#define FTE_QC_PAUSED\n#define FTE_QC_PERSISTENTTEMPSTRINGS /* Supersedes DP_QC_MULTIPLETEMPSTRINGS. Temp strings are garbage collected automatically, and do not expire while they're still in use. This makes strzone redundant. */\n#define FTE_QC_RAGDOLL_WIP\n#define FTE_QC_SENDPACKET /* Allows the use of out-of-band udp packets to/from other hosts. Includes the SV_ParseConnectionlessPacket event. */\n#define FTE_QC_STUFFCMDFLAGS /* Variation on regular stuffcmd that gives control over how spectators/mvds should be treated. */\n#define FTE_QC_TRACETRIGGER\n#define FTE_QUAKE2_CLIENT /* This engine is able to act as a quake2 client */\n#define FTE_QUAKE2_SERVER /* This engine is able to act as a quake2 server */\n#define FTE_QUAKE3_CLIENT /* This engine is able to act as a quake3 client */\n#define FTE_QUAKE3_SERVER /* This engine is able to act as a quake3 server */\n#define FTE_SOLID_LADDER /* Allows a simple trigger to remove effects of gravity (solid 20). obsolete. will prolly be removed at some point as it is not networked properly. Use FTE_ENT_SKIN_CONTENTS */\n#define FTE_SPLITSCREEN /* Client supports splitscreen, controlled via cl_splitscreen. Servers require allow_splitscreen 1 if splitscreen is to be used over the internet. Mods that use csqc will need to be aware for this to work properly. per-client networking may be problematic. */\n#define FTE_SQL /* Provides sql* builtins which can be used for sql database access */\n#define FTE_SQL_SQLITE /* SQL functionality is able to utilise sqlite databases */\n#define FTE_STRINGS /* Extra builtins (and additional behaviour) to make string manipulation easier */\n#define FTE_SV_POINTPARTICLES /* Specifies that particleeffectnum, pointparticles, and trailparticles exist in ssqc as well as csqc. particleeffectnum acts as a precache, allowing ssqc values to be networked up with csqc for use. Use in combination with FTE_PART_SCRIPT+FTE_PART_NAMESPACES to use custom effects. This extension is functionally identical to the DP version, but avoids any misplaced assumptions about the format of the client's particle descriptions. */\n#define FTE_SV_REENTER\n#define FTE_TE_STANDARDEFFECTBUILTINS /* Provides builtins to replace writebytes, with a QW compatible twist. */\n#define FTE_TERRAIN_MAP /* This engine supports .hmp files, as well as terrain embedded within bsp files. */\n#define FTE_RAW_MAP /* This engine supports directly loading .map files, as well as realtime editing of the various brushes. */\n#define KRIMZON_SV_PARSECLIENTCOMMAND /* SSQC's SV_ParseClientCommand function is able to handle client 'cmd' commands. The tokenizing parts also work in csqc. */\n#define NEH_CMD_PLAY2\n#define NEH_RESTOREGAME\n#define QSG_CVARSTRING\n#define QW_ENGINE\n#define QWE_MVD_RECORD /* You can use the easyrecord command to record MVD demos serverside. */\n#define TEI_MD3_MODEL\n#define TENEBRAE_GFX_DLIGHTS /* Allows ssqc to attach rtlights to entities with various special properties. */\n#define ZQ_MOVETYPE_FLY /* MOVETYPE_FLY works on players. */\n#define ZQ_MOVETYPE_NOCLIP /* MOVETYPE_NOCLIP works on players. */\n#define ZQ_MOVETYPE_NONE /* MOVETYPE_NONE works on players. */\n#define ZQ_VWEP\n#define ZQ_QC_STRINGS /* The strings-only subset of FRIK_FILE is supported. */\n\n#ifdef _ACCESSORS\naccessor strbuf : float;\naccessor searchhandle : float;\naccessor hashtable : float;\naccessor infostring : string;\naccessor filestream : float;\naccessor filestream : float;\n#else\n#define strbuf float\n#define searchhandle float\n#define hashtable float\n#define infostring string\n#define filestream float\n#endif\n\nentity self;\t/* The magic me */\n#if defined(CSQC) || defined(SSQC)\nentity other;\t/* Valid in touch functions, this is the entity that we touched. */\nentity world;\t/* The null entity. Hurrah. Readonly after map spawn time. */\nfloat time;\t/* The current game time. Stops when paused. */\n#endif\n#ifdef CSQC\nfloat cltime;\t/* A local timer that ticks relative to local time regardless of latency, packetloss, or pause. */\n#endif\n#if defined(CSQC) || defined(SSQC)\nfloat frametime;\t/* The time since the last physics/render/input frame. */\n#endif\n#ifdef CSQC\nfloat player_localentnum;\t/* This is entity number the player is seeing from/spectating, or the player themself, can change mid-map. */\nfloat player_localnum;\t/* The 0-based player index, valid for getplayerkeyvalue calls. */\nfloat maxclients;\t/* Maximum number of player slots on the server. */\nfloat clientcommandframe;\t/* This is the input-frame sequence. frames < clientcommandframe have been sent to the server. frame==clientcommandframe is still being generated and can still change. */\nfloat servercommandframe;\t/* This is the input-frame that was last acknowledged by the server. Input frames greater than this should be applied to the player's entity. */\n#endif\n#if defined(QWSSQC)\nentity newmis;\t/* A named entity that should be run soon, to reduce the effects of latency. */\n#endif\n#ifdef SSQC\nfloat force_retouch;\t/* If positive, causes all entities to check for triggers. */\n#endif\n#if defined(CSQC) || defined(SSQC)\nstring mapname;\t/* The short name of the map. */\n#endif\n#if defined(NQSSQC)\nfloat deathmatch;\nfloat coop;\nfloat teamplay;\n#endif\n#ifdef SSQC\nfloat serverflags;\nfloat total_secrets;\nfloat total_monsters;\nfloat found_secrets;\nfloat killed_monsters;\nfloat parm1;\t/* Player specific mod-defined values that are transferred from one map to the next. These are set by the qc inside calls to SetNewParms and SetChangeParms, and can then be read on a per-player basis after a call to the setspawnparms builtin. They are not otherwise valid. */\nfloat parm2;\t/* Player specific mod-defined values that are transferred from one map to the next. These are set by the qc inside calls to SetNewParms and SetChangeParms, and can then be read on a per-player basis after a call to the setspawnparms builtin. They are not otherwise valid. */\nfloat parm3;\t/* Player specific mod-defined values that are transferred from one map to the next. These are set by the qc inside calls to SetNewParms and SetChangeParms, and can then be read on a per-player basis after a call to the setspawnparms builtin. They are not otherwise valid. */\nfloat parm4;\t/* Player specific mod-defined values that are transferred from one map to the next. These are set by the qc inside calls to SetNewParms and SetChangeParms, and can then be read on a per-player basis after a call to the setspawnparms builtin. They are not otherwise valid. */\nfloat parm5;\t/* Player specific mod-defined values that are transferred from one map to the next. These are set by the qc inside calls to SetNewParms and SetChangeParms, and can then be read on a per-player basis after a call to the setspawnparms builtin. They are not otherwise valid. */\nfloat parm6;\t/* Player specific mod-defined values that are transferred from one map to the next. These are set by the qc inside calls to SetNewParms and SetChangeParms, and can then be read on a per-player basis after a call to the setspawnparms builtin. They are not otherwise valid. */\nfloat parm7;\t/* Player specific mod-defined values that are transferred from one map to the next. These are set by the qc inside calls to SetNewParms and SetChangeParms, and can then be read on a per-player basis after a call to the setspawnparms builtin. They are not otherwise valid. */\nfloat parm8;\t/* Player specific mod-defined values that are transferred from one map to the next. These are set by the qc inside calls to SetNewParms and SetChangeParms, and can then be read on a per-player basis after a call to the setspawnparms builtin. They are not otherwise valid. */\nfloat parm9;\t/* Player specific mod-defined values that are transferred from one map to the next. These are set by the qc inside calls to SetNewParms and SetChangeParms, and can then be read on a per-player basis after a call to the setspawnparms builtin. They are not otherwise valid. */\nfloat parm10;\t/* Player specific mod-defined values that are transferred from one map to the next. These are set by the qc inside calls to SetNewParms and SetChangeParms, and can then be read on a per-player basis after a call to the setspawnparms builtin. They are not otherwise valid. */\nfloat parm11;\t/* Player specific mod-defined values that are transferred from one map to the next. These are set by the qc inside calls to SetNewParms and SetChangeParms, and can then be read on a per-player basis after a call to the setspawnparms builtin. They are not otherwise valid. */\nfloat parm12;\t/* Player specific mod-defined values that are transferred from one map to the next. These are set by the qc inside calls to SetNewParms and SetChangeParms, and can then be read on a per-player basis after a call to the setspawnparms builtin. They are not otherwise valid. */\nfloat parm13;\t/* Player specific mod-defined values that are transferred from one map to the next. These are set by the qc inside calls to SetNewParms and SetChangeParms, and can then be read on a per-player basis after a call to the setspawnparms builtin. They are not otherwise valid. */\nfloat parm14;\t/* Player specific mod-defined values that are transferred from one map to the next. These are set by the qc inside calls to SetNewParms and SetChangeParms, and can then be read on a per-player basis after a call to the setspawnparms builtin. They are not otherwise valid. */\nfloat parm15;\t/* Player specific mod-defined values that are transferred from one map to the next. These are set by the qc inside calls to SetNewParms and SetChangeParms, and can then be read on a per-player basis after a call to the setspawnparms builtin. They are not otherwise valid. */\nfloat parm16;\t/* Player specific mod-defined values that are transferred from one map to the next. These are set by the qc inside calls to SetNewParms and SetChangeParms, and can then be read on a per-player basis after a call to the setspawnparms builtin. They are not otherwise valid. */\n#endif\n#ifdef CSQC\nfloat intermission;\n#endif\n#if defined(CSQC) || defined(SSQC)\nvector v_forward;\nvector v_up;\nvector v_right;\n#endif\n#ifdef CSQC\nvector view_angles;\t/* +x=DOWN */\n#endif\n#if defined(CSQC) || defined(SSQC)\nfloat trace_allsolid;\nfloat trace_startsolid;\nfloat trace_fraction;\nvector trace_endpos;\nvector trace_plane_normal;\nfloat trace_plane_dist;\nentity trace_ent;\nfloat trace_inopen;\nfloat trace_inwater;\n#endif\n#ifdef CSQC\nfloat input_timelength;\nvector input_angles;\t/* +x=DOWN */\nvector input_movevalues;\nfloat input_buttons;\nfloat input_impulse;\n#endif\n#ifdef SSQC\nentity msg_entity;\nvoid() main;\t/* This function is never called, and is effectively dead code. */\nvoid() StartFrame;\t/* Called at the start of each new physics frame. Player entities may think out of sequence so try not to depend upon explicit ordering too much. */\nvoid() PlayerPreThink;\t/* With Prediction(QW compat/FTE default): Called before the player's input commands are processed.\nNo Prediction(NQ compat): Called AFTER the player's movement intents have already been processed (ie: velocity will have already changed according to input_*, but before the actual position change. */\nvoid() PlayerPostThink;\t/* Called after the player's input commands are processed. */\nvoid() ClientKill;\t/* Called in response to 'cmd kill' (or just 'kill'). */\nvoid() ClientConnect;\t/* Called after the connecting client has finished loading and is ready to receive active entities. Note that this is NOT the first place that a client might be referred to. To determine if the client has csqc active (and kick anyone that doesn't), you can use if(infokeyf(self,INFOKEY_P_CSQCACTIVE)) {sprint(self, \"CSQC is required for this server\\n\");dropclient(self);} */\nvoid() PutClientInServer;\t/* Enginewise, this is only ever called immediately after ClientConnect and is thus a little redundant. Modwise, this is also called for respawning a player etc. */\nvoid() ClientDisconnect;\t/* Called once a client disconnects or times out. Not guarenteed to be called on map changes. */\nvoid() SetNewParms;\t/* Called without context when a new client initially connects (before ClientConnect is even called). This function is expected to only set the parm* globals so that they can be decoded properly later. You should not rely on 'self' being set. */\nvoid() SetChangeParms;\t/* Called for each client on map changes. Should copy various entity fields to the parm* globals. */\n#endif\nvoid end_sys_globals;\n#if defined(CSQC) || defined(SSQC)\n.float modelindex;\t/* This is the model precache index for the model that was set on the entity, instead of having to look up the model according to the .model field. Use setmodel to change it. */\n.vector absmin;\t/* Set by the engine when the entity is relinked (by setorigin, setsize, or setmodel). This is in world coordinates. */\n.vector absmax;\t/* Set by the engine when the entity is relinked (by setorigin, setsize, or setmodel). This is in world coordinates. */\n#endif\n#ifdef SSQC\n.float ltime;\t/* On MOVETYPE_PUSH entities, this is used as an alternative to the 'time' global, and .nextthink is synced to this instead of time. This allows time to effectively freeze if the entity is blocked, ensuring the think happens when the entity reaches the target point instead of randomly. */\n#endif\n#ifdef CSQC\n.float entnum;\t/* The entity number as its known on the server. */\n.float drawmask;\t/* Acts as a filter in the addentities call. */\n.float() predraw;\t/* Called by addentities after the filter and before the entity is actually drawn. Do your interpolation and animation in here. Should return one of the PREDRAW_* constants. */\n#endif\n#if defined(QWSSQC)\n.float lastruntime;\t/* This field used to be used to avoid running an entity multiple times in a single frame due to quakeworld's out-of-order thinks. It is no longer used by FTE due to precision issues, but may still be updated for compatibility reasons. */\n#endif\n#if defined(CSQC) || defined(SSQC)\n.float movetype;\t/* Describes how the entity moves. One of the MOVETYPE_ constants. */\n.float solid;\t/* Describes whether the entity is solid or not, and any special properties infered by that. Must be one of the SOLID_ constants */\n.vector origin;\t/* The current location of the entity in world space. Inline bsp entities (ie: ones placed by a mapper) will typically have a value of '0 0 0' in their neutral pose, as the geometry is offset from that. It is the reference point of the entity rather than the center of its geometry, for non-bsp models, this is often not a significant distinction. */\n.vector oldorigin;\t/* This is often used on players to reset the player back to where they were last frame if they somehow got stuck inside something due to fpu precision. Never change a player's oldorigin field to inside a solid, because that might cause them to become pemanently stuck. */\n.vector velocity;\t/* The direction and speed that the entity is moving in world space. */\n.vector angles;\t/* The eular angles the entity is facing in, in pitch, yaw, roll order. Due to a legacy bug, mdl/iqm/etc formats use +x=UP, bsp/spr/etc formats use +x=DOWN. */\n.vector avelocity;\t/* The amount the entity's angles change by per second. Note that this is direct eular angles, and thus the angular change is non-linear and often just looks buggy if you're changing more than one angle at a time. */\n#endif\n#ifdef CSQC\n.float pmove_flags;\n#endif\n#if defined(NQSSQC)\n.vector punchangle;\n#endif\n#if defined(CSQC) || defined(SSQC)\n.string classname;\t/* Identifies the class/type of the entity. Useful for debugging, also used for loading, but its value is not otherwise significant to the engine, this leaves the mod free to set it to whatever it wants and randomly test strings for values in whatever inefficient way it chooses fit. */\n#endif\n#ifdef CSQC\n.float renderflags;\n#endif\n#if defined(CSQC) || defined(SSQC)\n.string model;\t/* The model name that was set via setmodel, in theory. Often, this is cleared to null to prevent the engine from being seen by clients while not changing modelindex. This behaviour allows inline models to remain solid yet be invisible. */\n.float frame;\t/* The current frame the entity is meant to be displayed in. In CSQC, note the lerpfrac and frame2 fields as well. if it specifies a framegroup, the framegroup will autoanimate in ssqc, but not in csqc. */\n#endif\n#ifdef CSQC\n.float frame1time;\t/* The absolute time into the animation/framegroup specified by .frame. */\n.float frame2;\t/* The alternative frame. Visible only when lerpfrac is set to 1. */\n.float frame2time;\t/* The absolute time into the animation/framegroup specified by .frame2. */\n.float lerpfrac;\t/* If 0, use frame1 only. If 1, use frame2 only. Mix them together for values between. */\n#endif\n#if defined(CSQC) || defined(SSQC)\n.float skin;\t/* The skin index to use. on a bsp entity, setting this to 1 will switch to the 'activated' texture instead. A negative value will be understood as a replacement contents value, so setting it to CONTENTS_WATER will make a movable pool of water. */\n.float effects;\t/* Lots of random flags that change random effects. See EF_* constants. */\n.vector mins;\t/* The minimum extent of the model (ie: the bottom-left coordinate relative to the entity's origin). Change via setsize. May also be changed by setmodel. */\n.vector maxs;\t/* like mins, but in the other direction. */\n.vector size;\t/* maxs-mins. Updated when the entity is relinked (by setorigin, setsize, setmodel) */\n.void() touch;\n#endif\n#ifdef SSQC\n.void() use;\n#endif\n#if defined(CSQC) || defined(SSQC)\n.void() think;\n.void() blocked;\n.float nextthink;\t/* The time at which the entity is next scheduled to fire its think event. For MOVETYPE_PUSH entities, this is relative to that entity's ltime field, for all other entities it is relative to the time gloal. */\n#endif\n#ifdef SSQC\n.entity groundentity;\n.float health;\n.float frags;\n.float weapon;\n.string weaponmodel;\n.float weaponframe;\n.float currentammo;\n.float ammo_shells;\n.float ammo_nails;\n.float ammo_rockets;\n.float ammo_cells;\n.float items;\n.float takedamage;\n#endif\n#if defined(CSQC) || defined(SSQC)\n.entity chain;\n#endif\n#ifdef SSQC\n.float deadflag;\n.vector view_ofs;\n.float button0;\n.float button1;\n.float button2;\n.float impulse;\n.float fixangle;\t/* Forces the clientside view angles to change to the value of .angles (has some lag). If set to 1/TRUE, the server will guess whether to send a delta or an explicit angle. If 2, will always send a delta (due to lag between transmission and acknowledgement, this cannot be spammed reliably). If 3, will always send an explicit angle. */\n.vector v_angle;\t/* The angles a player is viewing. +x is DOWN (pitch, yaw, roll) */\n#endif\n#if defined(NQSSQC)\n.float idealpitch;\n#endif\n#ifdef SSQC\n.string netname;\n#endif\n#if defined(CSQC) || defined(SSQC)\n.entity enemy;\n.float flags;\n.float colormap;\n#endif\n#ifdef SSQC\n.float team;\n.float max_health;\n.float teleport_time;\t/* While active, prevents the player from using the +back command, also blocks waterjumping. */\n.float armortype;\n.float armorvalue;\n.float waterlevel;\n.float watertype;\n.float ideal_yaw;\n.float yaw_speed;\n.entity aiment;\n.entity goalentity;\n.float spawnflags;\n.string target;\n.string targetname;\n.float dmg_take;\n.float dmg_save;\n.entity dmg_inflictor;\n#endif\n#if defined(CSQC) || defined(SSQC)\n.entity owner;\n#endif\n#ifdef SSQC\n.vector movedir;\n.string message;\n.float sounds;\n.string noise;\n.string noise1;\n.string noise2;\n.string noise3;\n#endif\nvoid end_sys_fields;\n#ifdef MENU\nfloat time;\t/* The current local time. Increases while paused. */\n#endif\n#ifdef SSQC\nfloat input_timelength;\nvector input_angles;\t/* +x=DOWN */\nvector input_movevalues;\nfloat input_buttons;\nfloat input_impulse;\n#endif\n#ifdef CSQC\nvector input_cursor_screen;\nvector input_cursor_trace_start;\nvector input_cursor_trace_endpos;\nfloat input_cursor_trace_entnum;\n#endif\n#if defined(CSQC) || defined(SSQC)\nint trace_endcontents;\nint trace_surfaceflags;\nint trace_brush_id;\nint trace_brush_faceid;\nint trace_surface_id;\t/* 1-based. 0 if not known. */\nint trace_bone_id;\t/* 1-based. 0 if not known. typically needs MOVE_HITMODEL. */\nint trace_triangle_id;\t/* 1-based. 0 if not known. */\n#endif\n#ifdef CSQC\nfloat trace_networkentity;\t/* Repots which ssqc entnum was hit when a csqc traceline impacts an ssqc-based brush entity. */\nvector pmove_org;\t/* Reports the origin of the engineside player (after prediction). Does not work when the player is a csqc-owned entity. */\nvector pmove_vel;\t/* Reports the velocity of the engineside player (after prediction). Does not work when the player is a csqc-owned entity. */\nfloat pmove_onground;\t/* Reports the onground state of the engineside player (after prediction). Does not work when the player is a csqc-owned entity. */\n#endif\n#if defined(CSQC) || defined(SSQC)\nvector global_gravitydir = '0 0 -1';\t/* The direction gravity should act in if not otherwise specified per entity. */\nint serverid;\t/* The unique id of this server within the server cluster. */\n#endif\n#ifdef CSQC\n.string message;\t/* Allows the csqc to read the map description from the server. */\n#endif\n#ifdef SSQC\n.float button3;\n.float button4;\n.float button5;\n.float button6;\n.float button7;\n.float button8;\n#endif\n#if defined(NQSSQC)\n.float buttonuse;\t/* For DP compat only. */\n.float buttonchat;\t/* For DP compat only. */\n.float cursor_active;\t/* For DP compat only. */\n.float button9;\t/* For DP compat only. */\n.float button10;\t/* For DP compat only. */\n.float button11;\t/* For DP compat only. */\n.float button12;\t/* For DP compat only. */\n.float button13;\t/* For DP compat only. */\n.float button14;\t/* For DP compat only. */\n.float button15;\t/* For DP compat only. */\n.float button16;\t/* For DP compat only. */\n.vector cursor_screen;\t/* For DP compat only. */\n.vector cursor_start;\t/* For DP compat only. */\n.vector cursor_impact;\t/* For DP compat only. */\n.entity cursor_entitynumber;\t/* For DP compat only. */\n.float lastruntime;\t/* This field used to be used to avoid running an entity multiple times in a single frame due to quakeworld's out-of-order thinks. It is no longer used by FTE due to precision issues, but may still be updated for compatibility reasons. */\n#endif\n#if defined(CSQC) || defined(QWSSQC)\n.vector punchangle;\n#endif\n#if defined(CSQC) || defined(SSQC)\n.float gravity;\t/* Multiplier applied in addition to sv_gravity (not absolute units), to control the gravity affecting this entity specifically. */\n.float hull;\t/* Overrides the hull used by the entity for walkmove/movetogoal and not traceline/tracebox. */\n.entity movechain;\t/* This is a linked list of entities which will be moved whenever this entity moves, logically they are attached to this entity. */\n.void() chainmoved;\t/* Called when the entity is moved as a result of being part of another entity's .movechain */\n.void(float old, float new) contentstransition;\t/* This function is called when the entity moves between water and air. If specified, default splash sounds will be disabled allowing you to provide your own. */\n.float dimension_solid;\t/* This is the bitmask of dimensions which the entity is solid within. This is not networked, instead csqc traces impacting ssqc entities assumes the ssqc entity to have a dimension_solid of 1. */\n.float dimension_hit;\t/* This is the bitmask of dimensions which the entity will be blocked by. If other.dimension_solid & self.dimension_hit, our traces will impact and not proceed. If its false, the traces will NOT impact, allowing self to pass straight through. */\n.int hitcontentsmaski;\t/* Traces performed for this entity will impact against surfaces that match this contents mask (CONTENTBITS_* constants). */\n__deprecated(\"Does not support mos-ecific contents.\") .float dphitcontentsmask;\t/* Some crappy field that inefficiently requires translating to the native contents flags. Ditch the 'dp', do it properly. */\n.float scale;\t/* Multiplier that resizes the entity. 1 is normal sized, 2 is double sized. scale 0 is remapped to 1. In SSQC, this is limited to 1/16th precision, with a maximum just shy of 16. */\n.float fatness;\t/* How many QuakeUnits to push the entity's verticies along their normals by. */\n.float alpha;\t/* The transparency of the entity. 1 means opaque, 0.0001 means virtually invisible. 0 is remapped to 1, for compatibility. */\n.float modelflags;\t/* Used to override the flags set in the entity's model. Should be set according to the MF_ constants. Use effects|=EF_NOMODELFLAGS to ignore the model's flags completely. The traileffectnum field is more versatile. */\n#endif\n#ifdef SSQC\n.float frame1time;\t/* This controls the time into the framegroup/animation named by .frame, you should increment this value according to frametime or to distance moved, depending on the sort of animation you're attempting. You may wish to avoid incrementing this while lerpfrac is still changing, to avoid wasting parts of the animation. */\n#endif\n#if defined(CSQC) || defined(SSQC)\n.float basebone;\t/* The base* frame animations are equivelent to their non-base versions, except that they only affect bone numbers below the 'basebone' value. This means that the base* animation can affect the legs of a skeletal model independantly of the normal animation fields affecting the torso area. For more complex animation than this, use skeletal objects. */\n.float baseframe;\t/* See basebone */\n.void() customphysics;\t/* Called once each physics frame, overriding the entity's .movetype field and associated logic. You'll probably want to use tracebox to move it through the world. Be sure to call .think as appropriate. */\n.entity tag_entity;\t/* Specifies which entity this entity's origin+angles is 'attached' to. */\n.float tag_index;\t/* Specifies the tag or bone on the parent entity that we're attached to. If this is -1 then the entity is instead a q3-like camera portal, with the tag_entity saying the entity to display for. If tag_entity is world then this is a q3-like portal surface marker with a separate camera (with a tag_entity referring to the portal surface). */\n.float skeletonindex;\t/* This object serves as a container for the skeletal bone states used to override the animation data. */\n.vector colormod;\t/* Provides a colour tint for the entity (does not affect fullbrights). */\n.vector glowmod;\t/* Scaler for an entity's fullbright textures. */\n.vector gravitydir;\t/* Specifies the direction in which gravity acts. Must be normalised. '0 0 0' also means down. Use '0 0 1' if you want the player to be able to run on ceilings. */\n.vector(vector org, vector ang) camera_transform;\t/* A callback that provides portal transform information for portal surfaces attached to this entity. Also used to open up pvs in ssqc. */\n#endif\n#ifdef SSQC\n.float pmove_flags;\n#endif\n#if defined(CSQC) || defined(SSQC)\n.float geomtype;\n.float friction;\n.float erp;\n.float jointtype;\n.float mass;\n.float bouncefactor;\n.float bouncestop;\n#endif\n#if defined(CSQC) || defined(QWSSQC)\n.float idealpitch;\n#endif\n#if defined(CSQC) || defined(SSQC)\n.float pitch_speed;\n.float drawflags;\t/* Various flags that affect lighting values and scaling. Typically set to 96 in quake for proper compatibility with DP_QC_SCALE. */\n.float abslight;\t/* Allows overriding light levels. Use drawflags to state that this field should actually be used. */\n.vector color;\t/* This affects the colour of realtime lights that were enabled via the pflags field. */\n.float light_lev;\t/* This is the radius of an entity's light. This is not normally used by the engine, but is used for realtime lights (ones that are enabled with the pflags field). */\n.float style;\t/* Used by the light util to decide how an entity's light should animate. On an entity with pflags set, this also affects realtime lights. */\n.float pflags;\t/* Realtime lighting flags */\n#endif\n#ifdef SSQC\n.float maxspeed;\n.entity view2;\t/* defines a second viewpoint, typically displayed in a corner of the screen (also punches open pvs). */\n.vector movement;\t/* These are the directions that the player is currently trying to move in (ie: which +forward/+moveright/+moveup etc buttons they have held), expressed relative to that player's angles. Order is forward, right, up. */\n.float vw_index;\t/* This acts as a second modelindex, using the same frames etc. */\n__deprecated(\"Cannot be recorded in MVDs, nor work properly with splitscreen. Use CSQC instead.\") .entity nodrawtoclient;\t/* This entity will not be sent to the player named by this field. They will be invisible and not emit dlights/particles. Does not work in MVD-recorded game. */\n__deprecated(\"Cannot be recorded in MVDs, nor work properly with splitscreen. Use CSQC instead.\") .entity drawonlytoclient;\t/* This entity will be sent *only* to the player named by this field. To other players they will be invisible and not emit dlights/particles. Does not work in MVD-recorded game. */\n__deprecated(\"Redundant. Cannot be recorded in MVDs, nor work properly with splitscreen. Use CSQC instead.\") .entity viewmodelforclient;\t/* This entity will be sent only to the player named by this field, and this entity will be attached to the player's view as an additional weapon model. */\n__deprecated(\"Cannot be recorded in MVDs, nor work properly with splitscreen. Use CSQC instead.\") .entity exteriormodeltoclient;\t/* This entity will be invisible to the player named by this field, except in mirrors or mirror-like surfaces, where it will be visible as normal. It may still cast shadows as normal, and generate lights+particles, depending on client settings. Does not affect how other players see the entity. */\n.entity clientcamera;\t/* Controls which entity to use for this client's camera. */\n.float glow_size;\t/* Some outdated particle trail thing. */\n.float glow_color;\t/* Some outdated particle trail thing. */\n.float glow_trail;\t/* Some outdated particle trail thing. */\n.float traileffectnum;\t/* This should be set to the result of particleeffectnum, in order to attach a custom trail effect to an entity as it moves. */\n.float emiteffectnum;\t/* This should be set to the result of particleeffectnum, in order to continually spawn particles in the direction that this entity faces. */\n__deprecated(\"Does not work with MVDs nor splitscreen.\") .float dimension_see;\t/* This is the dimension mask (bitfield) that the client is allowed to see. Entities and events not in this dimension mask will be invisible. */\n__deprecated(\"Does not work with MVDs nor splitscreen.\") .float dimension_seen;\t/* This is the dimension mask (bitfield) that the client is visible within. Clients that cannot see this dimension mask will not see this entity. */\n__deprecated(\"Does not work with MVDs nor splitscreen.\") .float dimension_ghost;\t/* If this entity is visible only within these dimensions, it will become transparent, as if a ghost. */\n__deprecated(\"Does not work with MVDs nor splitscreen.\") .float dimension_ghost_alpha;\t/* If this entity is subject to dimension_ghost, this is the scaler for its alpha value. If 0, 0.5 will be used instead. */\n.float(entity playerent, float changedflags) SendEntity;\t/* Called by the engine whenever an entity needs to be (re)sent to a client's csprogs, either because SendFlags was set or because data was lost. Must write its data to the MSG_ENTITY buffer. Will be called at the engine's leasure. */\n.float SendFlags;\t/* Indicates that something in the entity has been changed, and that it needs to be updated to all players that can see it. The engine will clear it at some point, with the cleared bits appearing in the 'changedflags' argument of the SendEntity method. */\n__deprecated(\"Use SendFlags instead.\") .float Version;\t/* Obsolete */\n__deprecated(\"Doesn't support RGB player colours.\") .float clientcolors;\n.float viewzoom;\n.float playerclass;\n.float hasted;\n.float light_level;\t/* Used by hexen2 to indicate the light level where the player is standing. */\n.float pvsflags;\t/* Reconfigures when the entity is visible to clients */\n.float uniquespawnid;\t/* Incremented by 1 whenever the entity is respawned. Persists across remove calls, for when the two-second grace period is insufficient. */\nDEP_CSQC .float() customizeentityforclient;\t/* Called just before an entity is sent to a client (non-csqc protocol). This gives you a chance to tailor 'self' according to what 'other' should see. */\n#endif\n#ifdef CSQC\n.float frame3;\t/* Some people just don't understand how to use framegroups... */\n.float frame3time;\t/* .frame3 equivelent of frame1time. */\n.float lerpfrac3;\t/* Weight of .frame3 - .frame's weight is automatically calculated as 1-(lerpfrac+lerpfrac3+lerpfrac4), as a result these fields should NEVER add to above 1. */\n.float frame4;\n.float frame4time;\t/* .frame4 equivelent of frame1time. */\n.float lerpfrac4;\n.float forceshader;\t/* Contains a shader handle used to replace all surfaces upon the entity. */\n.float baseframe2;\t/* See basebone */\n.float baseframe1time;\t/* See basebone */\n.float baseframe2time;\t/* See basebone */\n.float baselerpfrac;\t/* See basebone */\n.float bonecontrol1;\t/* Halflife model format bone controller. On player models, this typically affects the spine's yaw. */\n.float bonecontrol2;\t/* Halflife model format bone controller. On player models, this typically affects the spine's yaw. */\n.float bonecontrol3;\t/* Halflife model format bone controller. On player models, this typically affects the spine's yaw. */\n.float bonecontrol4;\t/* Halflife model format bone controller. On player models, this typically affects the spine's yaw. */\n.float bonecontrol5;\t/* Halflife model format bone controller. This typically affects the mouth. */\n.float subblendfrac;\t/* Weird animation value specific to halflife models. On player models, this typically affects the spine's pitch, or yaw, or... */\n.float subblend2frac;\t/* Weird animation value specific to halflife models. I've no idea what this does, probably nothing for most models. */\n.float basesubblendfrac;\t/* See basebone */\n.float basesubblend2frac;\t/* See basebone */\n#endif\nvoid(float reqid, float responsecode, string resourcebody, int resourcebytes) URI_Get_Callback;\t/* Called as an eventual result of the uri_get builtin. */\n#ifdef SSQC\nvoid() SpectatorConnect;\t/* Called when a spectator joins the game. */\nvoid() SpectatorDisconnect;\t/* Called when a spectator disconnects from the game. */\nvoid() SpectatorThink;\t/* Called each frame for each spectator. */\nvoid(string cmd) SV_ParseClientCommand;\t/* Provides QC with a way to intercept 'cmd foo' commands from the client. Very handy. Self will be set to the sending client, while the 'cmd' argument can be tokenize()d and each element retrieved via argv(argno). Unrecognised cmds MUST be passed on to the clientcommand builtin. */\nvoid(string dest, string from, string cmd, string info) SV_ParseClusterEvent;\t/* Part of cluster mode. Handles cross-node events that were sent via clusterevent, on behalf of the named client. */\nfloat(string sender, string body) SV_ParseConnectionlessPacket;\t/* Provides QC with a way to communicate between servers, or with client server browsers. Sender is the sender's ip. Body is the body of the message. You'll need to add your own password/etc support as required. Self is not valid. */\nvoid(float pauseduration) SV_PausedTic;\t/* For each frame that the server is paused, this function will be called to give the gamecode a chance to unpause the server again. the pauseduration argument says how long the server has been paused for (the time global is frozen and will not increment while paused). Self is not valid. */\nfloat(float newstatus) SV_ShouldPause;\t/* Called to give the qc a change to block pause/unpause requests. Return false for the pause request to be ignored. newstatus is 1 if the user is trying to pause the game. For the duration of the call, self will be set to the player who tried to pause, or to world if it was triggered by a server-side event. */\nvoid() SV_RunClientCommand;\t/* Called each time a player movement packet was received from a client. Self is set to the player entity which should be updated, while the input_* globals specify the various properties stored within the input packet. The contents of this function should be somewaht identical to the equivelent function in CSQC, or prediction misses will occur. If you're feeling lazy, you can simply call 'runstandardplayerphysics' after modifying the inputs. */\nvoid() SV_AddDebugPolygons;\t/* Called each video frame. This is the only place where ssqc is allowed to call the R_BeginPolygon/R_PolygonVertex/R_EndPolygon builtins. This is exclusively for debugging, and will break in anything but single player as it will not be called if the engine is not running both a client and a server. */\nDEP_CSQC void() SV_PlayerPhysics;\t/* Compatibility method to tweak player input that does not reliably work with prediction (prediction WILL break). Mods that care about prediction should use SV_RunClientCommand instead. If pr_no_playerphysics is set to 1, this function will never be called, which will either fix prediction or completely break player movement depending on whether the feature was even useful. */\nvoid() EndFrame;\t/* Called after non-player entities have been run at the end of the physics frame. Player physics is performed out of order and can/will still occur between EndFrame and BeginFrame. */\nstring(string addr, string uinfo, string features)  SV_CheckRejectConnection;\t/* Called to give the mod a chance to ignore connection requests based upon client protocol support or other properties. Use infoget to read the uinfo and features arguments. */\n#endif\n#ifdef CSQC\nvoid(float apilevel, string enginename, float engineversion) CSQC_Init;\t/* Called at startup. enginename and engineversion are arbitary hints and can take any form. enginename should be consistant between revisions, but this cannot truely be relied upon. */\nvoid() CSQC_WorldLoaded;\t/* Called after the server's model+sound precaches have been executed. Gives a chance for the qc to read the entity lump from the bsp (via getentitytoken). */\nvoid() CSQC_Shutdown;\t/* Specifies that the csqc is going down. Save your persistant settings here. */\nvoid(float vwidth, float vheight, float notmenu) CSQC_UpdateView;\t/* Called every single video frame. The CSQC is responsible for rendering the entire screen. */\nvoid(float vwidth, float vheight, float notmenu) CSQC_UpdateViewLoading;\t/* Alternative to CSQC_UpdateView, called when the engine thinks there should be a loading screen. If present, will inhibit the engine's normal loading screen, deferring to qc to draw it. */\nvoid(vector viewsize, float scoresshown) CSQC_DrawHud;\t/* Part of simple csqc, called after drawing the 3d view whenever CSQC_UpdateView is not defined. */\nvoid(vector viewsize, float scoresshown) CSQC_DrawScores;\t/* Part of simple csqc, called after CSQC_DrawHud whenever CSQC_UpdateView is not defined, and when there are no menus/console active. */\nvoid(string msg) CSQC_Parse_StuffCmd;\t/* Gives the CSQC a chance to intercept stuffcmds. Use the tokenize builtin to parse the message. Unrecognised commands would normally be localcmded, but its probably better to drop unrecognised stuffcmds completely. */\nfloat(string msg) CSQC_Parse_CenterPrint;\t/* Gives the CSQC a chance to intercept centerprints. Return true if you wish the engine to otherwise ignore the centerprint. */\nfloat(float save, float take, vector inflictororg) CSQC_Parse_Damage;\t/* Called as a result of player.dmg_save or player.dmg_take being set on the server.\nReturn true to completely inhibit the engine's colour shift and damage rolls, allowing you to do your own thing.\nYou can use punch_roll += (normalize(inflictororg-player.origin)*v_right)*(take+save)*autocvar_v_kickroll; as a modifier for the roll angle should the player be hit from the side, and slowly fade it away over time. */\nvoid(string printmsg, float printlvl) CSQC_Parse_Print;\t/* Gives the CSQC a chance to intercept sprint/bprint builtin calls. CSQC should filter by the client's current msg setting and then pass the message on to the print command, or handle them itself. */\nvoid() CSQC_Parse_Event;\t/* Called when the client receives an SVC_CGAMEPACKET. The csqc should read the data or call the error builtin if it does not recognise the message. */\nfloat(float evtype, float scanx, float chary, float devid) CSQC_InputEvent;\t/* Called whenever a key is pressed, the mouse is moved, etc. evtype will be one of the IE_* constants. The other arguments vary depending on the evtype. Key presses are not guarenteed to have both scan and unichar values set at the same time. */\n__used void() CSQC_Input_Frame;\t/* Called just before each time clientcommandframe is updated. You can edit the input_* globals in order to apply your own player inputs within csqc, which may allow you a convienient way to pass certain info to ssqc. */\nvoid(string rendererdescription) CSQC_RendererRestarted;\t/* Called by the engine after the video was restarted. This serves to notify the CSQC that any render targets that it may have cached were purged, and will need to be regenerated. */\nfloat(string cmd) CSQC_ConsoleCommand;\t/* Called if the user uses any console command registed via registercommand. */\nfloat(string text, string info) CSQC_ConsoleLink;\t/* Called if the user clicks a ^[text\\infokey\\infovalue^] link. Use infoget to read/check each supported key. Return true if you wish the engine to not attempt to handle the link itself.\nWARNING: link text can potentially come from other players, so be careful about what you allow to be changed. */\nvoid(float entnum) CSQC_Ent_Spawn;\t/* Clumsily defined function for compat with DP. Should call spawn, set that ent's entnum field, and return the entity inside the 'self' global which will then be used for fllowing Ent_Updates. MUST NOT PARSE ANY NETWORK DATA (which makes it kinda useless). */\nvoid(float isnew) CSQC_Ent_Update;\t/* Parses the data sent by ssqc's various SendEntity functions (must use the exact same reads as the ssqc used writes - to debug this rule more easily, you may wish to use sv_csqcdebug). 'self' provides context between frames, and self.entnum should normally report which ssqc entity . Be aware that interpolation will need to happen separately. */\nvoid() CSQC_Ent_Remove;\nfloat(float entnum, float channel, string soundname, float vol, float attenuation, vector pos, float pitchmod, float flags) CSQC_Event_Sound;\nfloat() CSQC_Parse_TempEntity;\t/* Please don't use this. Use CSQC_Parse_Event and multicasts instead.\nThe use of serverside protocol translation to handle QW vs NQ protocols mean that you're likely to end up reading slightly different data. Which is bad.\nReturn true to say that you fully handled the tempentity. Return false to have the client attempt to rewind the network stream and parse the message itself. */\n#endif\n#if defined(CSQC) || defined(MENU)\nvoid(string cmdtext) GameCommand;\n#endif\nstring(string uri, string method, string postdata, __in string requestheaders, __inout string responseheaders) Cef_GeneratePage;\t/* Provides an entrypoint to generate pages for the CEF plugin from within QC. Headers are \n-separated key/value pairs (use tokenizebyseparator). */\n#ifdef SSQC\nstring(string uri, string method, string postdata, __in string requestheaders, __inout string responseheaders) HTTP_GeneratePage;\t/* Provides an entrypoint to generate pages for pages requested over http (sv_port_tcp+net_enable_http). Headers are \n-separated key/value pairs (use tokenizebyseparator). Return __NULL__ to let the engine handle it, an empty string for a 404, and any other text for a regular 200 response. */\n#endif\n#if defined(CSQC) || defined(SSQC)\nvoid(float prevprogs) init;\t/* Part of FTE_MULTIPROGS. Called as soon as a progs is loaded, called at a time when entities are not valid. This is the only time when it is safe to call addprogs without field assignment. As it is also called as part of addprogs, this also gives you a chance to hook functions in modules that are already loaded (via externget+externget). */\nvoid() initents;\t/* Part of FTE_MULTIPROGS. Called after fields have been finalized. This is the first point at which it is safe to call spawn(), and is called before any entity fields have been parsed. You can use this entrypoint to send notifications to other modules. */\n#endif\n#ifdef MENU\nvoid() m_init;\nvoid() m_shutdown;\nvoid(vector screensize) m_draw;\t/* Provides the menuqc with a chance to draw. Will be called even if the menu does not have focus, so be sure to avoid that. COMPAT: screensize is not provided in DP. */\nvoid(vector screensize, float opaque) m_drawloading;\t/* Additional drawing function to draw loading screens. If opaque is set, then this function must ensure that the entire screen is overdrawn (even if just by a black drawfill). */\nvoid(string rendererdescription) Menu_RendererRestarted;\t/* Called by the engine after the video was restarted. This serves to notify the MenuQC that any render targets that it may have cached were purged, and will need to be regenerated. */\nfloat(float evtype, float scanx, float chary, float devid) Menu_InputEvent;\t/* If present, this is called instead of m_keydown and m_keyup\nCalled whenever a key is pressed, the mouse is moved, etc. evtype will be one of the IE_* constants. The other arguments vary depending on the evtype. Key presses are not guarenteed to have both scan and unichar values set at the same time. */\n__deprecated(\"Use Menu_InputEvent\") void(float scan, float chr) m_keydown;\n__deprecated(\"Use Menu_InputEvent\") void(float scan, float chr) m_keyup;\nvoid(float wantmode) m_toggle;\nfloat(string cmd) m_consolecommand;\n#endif\n#ifdef SSQC\nfloat parm17, parm18, parm19, parm20, parm21, parm22, parm23, parm24, parm25, parm26, parm27, parm28, parm29, parm30, parm31, parm32;\t/* Additional spawn parms, following the same parmN theme. */\nfloat parm33, parm34, parm35, parm36, parm37, parm38, parm39, parm40, parm41, parm42, parm43, parm44, parm45, parm46, parm47, parm48;\nfloat parm49, parm50, parm51, parm52, parm53, parm54, parm55, parm56, parm57, parm58, parm59, parm60, parm61, parm62, parm63, parm64;\nstring parm_string;\t/* Like the regular parmN globals, but preserves string contents. */\nstring startspot;\t/* Receives the value of the second argument to changelevel from the previous map. */\nvar float dimension_send;\t/* Used by multicast functionality. Multicasts (and related builtins that multicast internally) will only be sent to players where (player.dimension_see & dimension_send) is non-zero. */\n//var float dimension_default = 255;\n/* Default dimension bitmask */\n__unused var string __fullspawndata;\t/* Set by the engine before calls to spawn functions, and is most easily parsed with the tokenize builtin. This allows you to handle halflife's multiple-fields-with-the-same-name (or target-specific fields). */\n#endif\n#if defined(CSQC) || defined(SSQC)\n__used var float physics_mode = 2;\t/* 0: original csqc - physics are not run\n1: DP-compat. Thinks occur, but not true movetypes.\n2: movetypes occur just as they do in ssqc. */\n#endif\n#ifdef CSQC\nfloat gamespeed;\t/* Set by the engine, this is the value of the sv_gamespeed cvar */\nfloat numclientseats;\t/* This is the number of splitscreen clients currently running on this client. */\n#endif\n#if defined(CSQC) || defined(MENU)\nvar vector drawfontscale = '1 1 0';\t/* Specifies a scaler for all text rendering. There are other ways to implement this. */\nfloat drawfont;\t/* Allows you to choose exactly which font is to be used to draw text. Fonts can be registered/allocated with the loadfont builtin. */\nconst float FONT_DEFAULT = 0;\n#endif\n#ifdef CSQC\nfloat(vector angles, float isdelta) CSQC_Parse_SetAngles;\nvoid(float playernum) CSQC_PlayerInfoChanged;\nvoid() CSQC_ServerInfoChanged;\nDEP(\"use CSQC_Event_Sound\") float(float channel, string soundname, vector pos, float vol, float attenuation, float flags) CSQC_ServerSound;\nvoid(int entidx, string newentdata) CSQC_MapEntityEdited;\nfloat clframetime;\nfloat servertime;\nfloat serverprevtime;\nfloat serverdeltatime;\nfloat deathmatch;\nfloat coop;\nint trace_surfaceflagsi;\nstring trace_surfacename;\nint trace_endcontentsi;\nstring trace_dphittexturename;\nDEP(\"Does not support mod-specific contents.\") float trace_dpstartcontents;\nDEP(\"Does not support mod-specific contents.\") float trace_dphitcontents;\nDEP(\"Does not support mod-specific surface flags\") float trace_dphitq3surfaceflags;\nDEP(\"Does not support all mod-specific surface flags.\") float trace_surfaceflagsf;\nDEP(\"Does not support all mod-specific contents.\") float trace_endcontentsf;\nfloat intermission_time;\nvector pmove_mins;\nvector pmove_maxs;\nfloat pmove_jump_held;\nfloat pmove_waterjumptime;\nfloat input_lightlevel;\nfloat input_weapon;\nfloat input_servertime;\nfloat input_clienttime;\nfloat input_cursor_entitynumber;\nfloat dimension_default;\nfloat autocvar_vid_conwidth;\nfloat autocvar_vid_conheight;\nfloat cycle_wrapped;\n#endif\nconst float TRUE = 1;\nconst float FALSE = 0;\t/* File not found... */\nconst float M_PI = 3.14159;\t/* Mathematica Pi constant. */\n#if defined(CSQC) || defined(SSQC)\nconst float MOVETYPE_NONE = 0;\nconst float MOVETYPE_WALK = 3;\nconst float MOVETYPE_STEP = 4;\nconst float MOVETYPE_FLY = 5;\nconst float MOVETYPE_TOSS = 6;\nconst float MOVETYPE_PUSH = 7;\nconst float MOVETYPE_NOCLIP = 8;\nconst float MOVETYPE_FLYMISSILE = 9;\nconst float MOVETYPE_BOUNCE = 10;\nconst float MOVETYPE_BOUNCEMISSILE = 11;\nconst float MOVETYPE_FOLLOW = 12;\nconst float MOVETYPE_6DOF = 30;\t/* A glorified MOVETYPE_FLY. Players using this movetype will get some flightsim-like physics, with fully independant rotations (order-dependant transforms). */\nconst float MOVETYPE_WALLWALK = 31;\t/* Players using this movetype will be able to orient themselves to walls, and then run up them. */\nconst float MOVETYPE_PHYSICS = 32;\t/* Enable the use of ODE physics upon this entity. */\nconst float SOLID_NOT = 0;\nconst float SOLID_TRIGGER = 1;\nconst float SOLID_BBOX = 2;\nconst float SOLID_SLIDEBOX = 3;\nconst float SOLID_BSP = 4;\t/* Does not collide against other SOLID_BSP entities. Normally paired with MOVETYPE_PUSH. */\nconst float SOLID_CORPSE = 5;\t/* Non-solid to SOLID_SLIDEBOX or other SOLID_CORPSE entities. For hitscan weapons to hit corpses, change the player's .hitcontentsmaski value to include CONTENTBIT_CORPSE, perform the traceline, then revert the player's .solid value. */\n__deprecated(\"Obsoleted by .skin=CONTENTS_LADDER\") const float SOLID_LADDER = 20;\t/* Obsolete and may be removed at some point. Use skin=CONTENT_LADDER and solid_bsp or solid_trigger instead. */\nconst float SOLID_PORTAL = 21;\t/* CSG subtraction volume combined with entity transformations on impact. */\nconst float SOLID_BSPTRIGGER = 22;\t/* For complex-shaped trigger volumes, instead of being a pure aabb. */\nconst float SOLID_PHYSICS_BOX = 32;\nconst float SOLID_PHYSICS_SPHERE = 33;\nconst float SOLID_PHYSICS_CAPSULE = 34;\nconst float SOLID_PHYSICS_TRIMESH = 35;\nconst float SOLID_PHYSICS_CYLINDER = 36;\nconst float GEOMTYPE_NONE = -1;\nconst float GEOMTYPE_SOLID = 0;\nconst float GEOMTYPE_BOX = 1;\nconst float GEOMTYPE_SPHERE = 2;\nconst float GEOMTYPE_CAPSULE = 3;\nconst float GEOMTYPE_TRIMESH = 4;\nconst float GEOMTYPE_CYLINDER = 5;\nconst float GEOMTYPE_CAPSULE_X = 6;\nconst float GEOMTYPE_CAPSULE_Y = 7;\nconst float GEOMTYPE_CAPSULE_Z = 8;\nconst float GEOMTYPE_CYLINDER_X = 9;\nconst float GEOMTYPE_CYLINDER_Y = 10;\nconst float GEOMTYPE_CYLINDER_Z = 11;\nconst float JOINTTYPE_FIXED = -1;\nconst float JOINTTYPE_POINT = 1;\nconst float JOINTTYPE_HINGE = 2;\nconst float JOINTTYPE_SLIDER = 3;\nconst float JOINTTYPE_UNIVERSAL = 4;\nconst float JOINTTYPE_HINGE2 = 5;\n#endif\n#ifdef CSQC\nconst float GE_MAXENTS = -1;\t/* Valid for getentity, ignores the entity argument. Returns the maximum number of entities which may be valid, to avoid having to poll 65k when only 100 are used. */\nconst float GE_ACTIVE = 0;\t/* Valid for getentity. Returns whether this entity is known to the client or not. */\nconst float GE_ORIGIN = 1;\t/* Valid for getentity. Returns the interpolated .origin. */\nconst float GE_FORWARD = 2;\t/* Valid for getentity. Returns the interpolated forward vector. */\nconst float GE_RIGHT = 3;\t/* Valid for getentity. Returns the entity's right vector. */\nconst float GE_UP = 4;\t/* Valid for getentity. Returns the entity's up vector. */\nconst float GE_SCALE = 5;\t/* Valid for getentity. Returns the entity .scale. */\nconst float GE_ORIGINANDVECTORS = 6;\t/* Valid for getentity. Returns interpolated .origin, but also sets v_forward, v_right, and v_up accordingly. Use vectoangles(v_forward,v_up) to determine the angles. */\nconst float GE_ALPHA = 7;\t/* Valid for getentity. Returns the entity alpha. */\nconst float GE_COLORMOD = 8;\t/* Valid for getentity. Returns the colormod vector. */\nconst float GE_PANTSCOLOR = 9;\t/* Valid for getentity. Returns the entity's lower color (from .colormap), as a palette range value. */\nconst float GE_SHIRTCOLOR = 10;\t/* Valid for getentity. Returns the entity's lower color (from .colormap), as a palette range value. */\nconst float GE_SKIN = 11;\t/* Valid for getentity. Returns the entity's .skin index. */\nconst float GE_MINS = 12;\t/* Valid for getentity. Guesses the entity's .min vector. */\nconst float GE_MAXS = 13;\t/* Valid for getentity. Guesses the entity's .max vector. */\nconst float GE_ABSMIN = 14;\t/* Valid for getentity. Guesses the entity's .absmin vector. */\nconst float GE_ABSMAX = 15;\t/* Valid for getentity. Guesses the entity's .absmax vector. */\nconst float GE_MODELINDEX = 200;\t/* Valid for getentity. Guesses the entity's .modelindex float. */\nconst float GE_MODELINDEX2 = 201;\t/* Valid for getentity. Guesses the entity's .vw_index float. */\nconst float GE_EFFECTS = 202;\t/* Valid for getentity. Guesses the entity's .effects float. */\nconst float GE_FRAME = 203;\t/* Valid for getentity. Guesses the entity's .frame float. */\nconst float GE_ANGLES = 204;\t/* Valid for getentity. Guesses the entity's .angles vector. */\nconst float GE_FATNESS = 205;\t/* Valid for getentity. Guesses the entity's .fatness float. */\nconst float GE_DRAWFLAGS = 206;\t/* Valid for getentity. Guesses the entity's .drawflags float. */\nconst float GE_ABSLIGHT = 207;\t/* Valid for getentity. Guesses the entity's .abslight float. */\nconst float GE_GLOWMOD = 208;\t/* Valid for getentity. Guesses the entity's .glowmod vector. */\nconst float GE_GLOWSIZE = 209;\t/* Valid for getentity. Guesses the entity's .glowsize float. */\nconst float GE_GLOWCOLOUR = 210;\t/* Valid for getentity. Guesses the entity's .glowcolor float. */\nconst float GE_RTSTYLE = 211;\t/* Valid for getentity. Guesses the entity's .style float. */\nconst float GE_RTPFLAGS = 212;\t/* Valid for getentity. Guesses the entity's .pflags float. */\nconst float GE_RTCOLOUR = 213;\t/* Valid for getentity. Guesses the entity's .color vector. */\nconst float GE_RTRADIUS = 214;\t/* Valid for getentity. Guesses the entity's .light_lev float. */\nconst float GE_TAGENTITY = 215;\t/* Valid for getentity. Guesses the entity's .tag_entity float. */\nconst float GE_TAGINDEX = 216;\t/* Valid for getentity. Guesses the entity's .tag_index float. */\nconst float GE_GRAVITYDIR = 217;\t/* Valid for getentity. Guesses the entity's .gravitydir vector. */\nconst float GE_TRAILEFFECTNUM = 218;\t/* Valid for getentity. Guesses the entity's .traileffectnum float. */\n#endif\n#ifdef SSQC\nconst float DAMAGE_NO = 0;\nconst float DAMAGE_YES = 1;\nconst float DAMAGE_AIM = 2;\n#endif\n#if defined(CSQC) || defined(SSQC)\nconst float CONTENT_EMPTY = -1;\nconst float CONTENT_SOLID = -2;\nconst float CONTENT_WATER = -3;\nconst float CONTENT_SLIME = -4;\nconst float CONTENT_LAVA = -5;\nconst float CONTENT_SKY = -6;\nconst float CONTENT_LADDER = -16;\t/* If this value is assigned to a solid_bsp's .skin field, the entity will become a ladder volume. */\nconst int CONTENTBIT_NONE = 0x00000000i;\nconst int CONTENTBIT_SOLID = 0x00000001i;\nconst int CONTENTBIT_LAVA = 0x00000008i;\nconst int CONTENTBIT_SLIME = 0x00000010i;\nconst int CONTENTBIT_WATER = 0x00000020i;\nconst int CONTENTBIT_FTELADDER = 0x00004000i;\t/* Content bit used for .skin=CONTENT_LADDER entities. */\nconst int CONTENTBIT_PLAYERCLIP = 0x00010000i;\nconst int CONTENTBIT_MONSTERCLIP = 0x00020000i;\nconst int CONTENTBIT_BODY = 0x02000000i;\t/* Content bit that indicates collisions against SOLID_BBOX/SOLID_SLIDEBOX entities. */\nconst int CONTENTBIT_CORPSE = 0x04000000i;\t/* Content bit that indicates collisions against SOLID_CORPSE entities. */\nconst int CONTENTBIT_Q2LADDER = 0x20000000i;\t/* Content bit specific to q2bsp (conflicts with q3bsp contents so use with caution). */\nconst int CONTENTBIT_SKY = 0x80000000i;\t/* Content bit somewhat specific to q1bsp (aliases to NODROP in q3bsp), but you should probably check surfaceflags&SURF_SKY as well for q2+q3bsp too. */\nconst int CONTENTBITS_POINTSOLID = CONTENTBIT_SOLID|0x00000002i|CONTENTBIT_BODY;\t/* Bits that traceline would normally consider solid */\nconst int CONTENTBITS_BOXSOLID = CONTENTBIT_SOLID|0x00000002i|CONTENTBIT_BODY|CONTENTBIT_PLAYERCLIP;\t/* Bits that tracebox would normally consider solid */\nconst int CONTENTBITS_FLUID = CONTENTBIT_WATER|CONTENTBIT_SLIME|CONTENTBIT_LAVA|CONTENTBIT_SKY;\nconst float SPA_POSITION = 0;\t/* These SPA_* constants are to specify which attribute is returned by the getsurfacepointattribute builtin */\nconst float SPA_S_AXIS = 1;\nconst float SPA_T_AXIS = 2;\nconst float SPA_R_AXIS = 3;\t/* aka: SPA_NORMAL */\nconst float SPA_TEXCOORDS0 = 4;\nconst float SPA_LIGHTMAP0_TEXCOORDS = 5;\nconst float SPA_LIGHTMAP0_COLOR = 6;\nconst float CHAN_AUTO = 0;\t/* The automatic channel, play as many sounds on this channel as you want, and they'll all play, however the other channels will replace each other. */\nconst float CHAN_WEAPON = 1;\nconst float CHAN_VOICE = 2;\nconst float CHAN_ITEM = 3;\nconst float CHAN_BODY = 4;\n#endif\n#if defined(QWSSQC)\nconst float CHANF_RELIABLE = 8;\t/* Only valid if the flags argument is not specified. The sound will be sent reliably, which is important if it is intended to replace looping sounds on doors etc. */\n#endif\n#ifdef SSQC\nconst float SOUNDFLAG_RELIABLE = 1;\t/* The sound will be sent reliably, and without regard to phs. */\n#endif\n#ifdef CSQC\nconst float SOUNDFLAG_ABSVOLUME = 16;\t/* The sample's volume is not scaled by the volume cvar. Use with caution */\n#endif\n#if defined(CSQC) || defined(SSQC)\nconst float SOUNDFLAG_FORCELOOP = 2;\t/* The sound will restart once it reaches the end of the sample. */\n#endif\n#ifdef CSQC\nconst float SOUNDFLAG_NOSPACIALISE = 4;\t/* The different audio channels are played at the same volume regardless of which way the player is facing, without needing to use 0 attenuation. */\n#endif\n#if defined(CSQC) || defined(SSQC)\nconst float SOUNDFLAG_NOREVERB = 32;\t/* Disables the use of underwater/reverb effects on this sound effect. */\nconst float SOUNDFLAG_FOLLOW = 64;\t/* The sound's origin will updated to follow the emitting entity. */\n#endif\n#ifdef SSQC\nconst float SOUNDFLAG_UNICAST = 256;\t/* The sound will be sent only by the player specified by msg_entity. Spectators and related splitscreen players will also hear the sound. */\nconst float SOUNDFLAG_SENDVELOCITY = 512;\t/* The entity's current velocity will be sent to the client, only useful if doppler is enabled. */\n#endif\n#if defined(CSQC) || defined(SSQC)\nconst float ATTN_NONE = 0;\t/* Sounds with this attenuation can be heard throughout the map */\nconst float ATTN_NORM = 1;\t/* Standard attenuation */\nconst float ATTN_IDLE = 2;\t/* Extra attenuation so that sounds don't travel too far. */\nconst float ATTN_STATIC = 3;\t/* Even more attenuation to avoid torches drowing out everything else throughout the map. */\n#endif\n#ifdef SSQC\nconst float SVC_CGAMEPACKET = 83;\t/* Direct ssqc->csqc message. Must only be multicast. The data triggers a CSQC_Parse_Event call in the csqc for the csqc to read the contents. The server *may* insert length information for clients connected via proxies which are not able to cope with custom csqc payloads. This should only ever be used in conjunction with the MSG_MULTICAST destination. */\n#endif\n#if defined(CSQC) || defined(SSQC)\nconst float TE_SPIKE = 0;\nconst float TE_SUPERSPIKE = 1;\n#endif\n#if defined(CSQC) || defined(QWSSQC)\nconst float TE_GUNSHOT = 2;\nconst float TE_EXPLOSION = 3;\n#endif\n#if defined(NQSSQC)\nconst float TE_GUNSHOT = 2;\nconst float TE_EXPLOSION = 3;\n#endif\n#if defined(CSQC) || defined(SSQC)\nconst float TE_TAREXPLOSION = 4;\nconst float TE_LIGHTNING1 = 5;\nconst float TE_LIGHTNING2 = 6;\nconst float TE_WIZSPIKE = 7;\nconst float TE_KNIGHTSPIKE = 8;\nconst float TE_LIGHTNING3 = 9;\nconst float TE_LAVASPLASH = 10;\nconst float TE_TELEPORT = 11;\n#endif\n#if defined(CSQC) || defined(QWSSQC)\nconst float TE_BLOOD = 12;\n#endif\n#if defined(NQSSQC)\nconst float TE_EXPLOSION2 = 12;\n#endif\n#if defined(CSQC) || defined(QWSSQC)\nconst float TE_LIGHTNINGBLOOD = 13;\n#endif\n#if defined(NQSSQC)\nconst float TE_BEAM = 13;\nconst float MSG_BROADCAST = 0;\t/* The byte(s) will be unreliably sent to all players. MSG_ constants are valid arguments to the Write* builtin family. */\nconst float MSG_ONE = 1;\t/* The byte(s) will be reliably sent to the player specified in the msg_entity global. WARNING: in quakeworld servers without network preparsing enabled, this can result in illegible server messages (due to individual reliable messages being split between multiple backbuffers/packets). NQ has larger reliable buffers which avoids this issue, but still kicks the client. */\nconst float MSG_ALL = 2;\t/* The byte(s) will be reliably sent to all players. */\n#endif\n#if defined(QWSSQC)\n__deprecated(\"Use MSG_MULTICAST+multicast(MULTICAST_*)\") const float MSG_BROADCAST;\t/* The byte(s) will be unreliably sent to all players. MSG_ constants are valid arguments to the Write* builtin family. */\n__deprecated(\"Use MSG_MULTICAST+multicast(MULTICAST_ONE_R)\") const float MSG_ONE = 1;\t/* The byte(s) will be reliably sent to the player specified in the msg_entity global. WARNING: in quakeworld servers without network preparsing enabled, this can result in illegible server messages (due to individual reliable messages being split between multiple backbuffers/packets). NQ has larger reliable buffers which avoids this issue, but still kicks the client. */\n__deprecated(\"Use MSG_MULTICAST+multicast(MULTICAST_ALL)\") const float MSG_ALL = 2;\t/* The byte(s) will be reliably sent to all players. */\n#endif\n#ifdef SSQC\nconst float MSG_INIT = 3;\t/* The byte(s) will be written into the signon buffer. Clients will see these messages when they connect later. This buffer is only flushed on map changes, so spamming it _WILL_ result in overflows. */\nconst float MSG_MULTICAST = 4;\t/* The byte(s) will be written into the multicast buffer for more selective sending. Messages sent this way will never be split across packets, and using this for csqc-only messages will not break protocol translation. */\nconst float MSG_ENTITY = 5;\t/* The byte(s) will be written into the entity buffer. This is a special value used only inside 'SendEntity' functions. */\nconst float MULTICAST_ALL = 0;\t/* The multicast message is unreliably sent to all players. MULTICAST_ constants are valid arguments for the multicast builtin, which ignores the specified origin when given this constant. */\nconst float MULTICAST_PHS = 1;\t/* The multicast message is unreliably sent to only players that can potentially hear the specified origin. Its quite loose. */\nconst float MULTICAST_PVS = 2;\t/* The multicast message is unreliably sent to only players that can potentially see the specified origin. */\nconst float MULTICAST_ONE = 6;\t/* The multicast message is unreliably sent to the player (AND ALL TRACKING SPECTATORS) specified in the msg_entity global. The specified origin is ignored. */\nconst float MULTICAST_ONE_NOSPECS = 9;\t/* The multicast message is unreliably sent to the player specified in the msg_entity global. The specified origin is ignored. */\nconst float MULTICAST_ALL_R = 3;\t/* The multicast message is reliably sent to all players. The specified origin is ignored. */\nconst float MULTICAST_PHS_R = 4;\t/* The multicast message is reliably sent to only players that can potentially hear the specified origin. Players might still not receive it if they are out of range. */\nconst float MULTICAST_PVS_R = 5;\t/* The multicast message is reliably sent to only players that can potentially see the specified origin. Players might still not receive it if they cannot see the event. */\nconst float MULTICAST_ONE_R = 7;\t/* The multicast message is reliably sent to the player (AND ALL TRACKING SPECTATORS) specified in the msg_entity global. The specified origin is ignored */\nconst float MULTICAST_ONE_R_NOSPECS = 10;\t/* The multicast message is reliably sent to the player specified in the msg_entity global. The specified origin is ignored */\n#endif\n#if defined(QWSSQC)\nconst float PRINT_LOW = 0;\nconst float PRINT_MEDIUM = 1;\nconst float PRINT_HIGH = 2;\nconst float PRINT_CHAT = 3;\n#endif\n#ifdef SSQC\nconst float PVSF_NORMALPVS = 0;\t/* Filter first by PVS, then filter this entity using tracelines if sv_cullentities is enabled. */\nconst float PVSF_NOTRACECHECK = 1;\t/* Filter strictly by PVS. */\nconst float PVSF_USEPHS = 2;\t/* Send if we're close enough to be able to hear this entity. */\nconst float PVSF_IGNOREPVS = 3;\t/* Ignores pvs. This entity is visible whereever you are on the map. Updates will be sent regardless of pvs or phs */\nconst float PVSF_NOREMOVE = 128;\t/* Once visible to a client, this entity will remain visible. This can be useful for csqc and corpses. While this flag is set, no CSQC_Remove events will be sent for the entity, but this does NOT mean that it will still receive further updates while outside of the pvs. */\nconst string INFOKEY_P_IP = \"ip\";\t/* The apparent ip address of the client. This may be a proxy's ip address. */\nconst string INFOKEY_P_REALIP = \"realip\";\t/* If sv_getrealip is set, this gives the ip as determine using that algorithm. */\nconst string INFOKEY_P_CSQCACTIVE = \"csqcactive\";\t/* Client has csqc enabled. CSQC ents etc will be sent to this player. */\nconst string INFOKEY_P_SVPING = \"svping\";\nconst string INFOKEY_P_GUID = \"guid\";\t/* Some hash string which should be reasonably unique to this player's quake installation. */\nconst string INFOKEY_P_CHALLENGE = \"challenge\";\nconst string INFOKEY_P_USERID = \"*userid\";\nconst string INFOKEY_P_DOWNLOADPCT = \"download\";\t/* The client's download percentage for the current file. Additional files are not known. */\nconst string INFOKEY_P_TRUSTLEVEL = \"trustlevel\";\nconst string INFOKEY_P_PROTOCOL = \"protocol\";\t/* The network protocol the client is using to connect to the server. */\nconst string INFOKEY_P_VIP = \"*VIP\";\t/* 1 if the player has the VIP 'penalty'. */\nconst string INFOKEY_P_ISMUTED = \"*ismuted\";\t/* 1 if the player has the 'mute' penalty and is not allowed to use the say/say_team commands. */\nconst string INFOKEY_P_ISDEAF = \"*isdeaf\";\t/* 1 if the player has the 'deaf' penalty and cannot see other people's say/say_team commands. */\nconst string INFOKEY_P_ISCRIPPLED = \"*ismuted\";\t/* 1 if the player has the cripple penalty, and their movement values are ignored (.movement is locked to 0). */\nconst string INFOKEY_P_ISCUFFED = \"*ismuted\";\t/* 1 if the player has the cuff penalty, and is unable to attack or use impulses(.button0 and .impulse fields are locked to 0). */\nconst string INFOKEY_P_ISLAGGED = \"*ismuted\";\t/* 1 if the player has the fakelag penalty and has an extra 200ms of lag. */\n#endif\n#if defined(CSQC) || defined(SSQC)\nconst string INFOKEY_P_PING = \"ping\";\t/* The player's ping time, in milliseconds. */\nconst string INFOKEY_P_NAME = \"name\";\t/* The player's name. */\nconst string INFOKEY_P_SPECTATOR = \"*spectator\";\t/* Whether the player is a spectator or not. */\nconst string INFOKEY_P_TOPCOLOR = \"topcolor\";\t/* The player's upper/shirt colour (palette index). */\nconst string INFOKEY_P_BOTTOMCOLOR = \"bottomcolor\";\t/* The player's lower/pants/trouser colour (palette index). */\n#endif\n#ifdef CSQC\nconst string INFOKEY_P_TOPCOLOR_RGB = \"topcolor_rgb\";\t/* The player's upper/shirt colour as an rgb value in a format usable with stov. */\nconst string INFOKEY_P_BOTTOMCOLOR_RGB = \"bottomcolor_rgb\";\t/* The player's lower/pants/trouser colour as an rgb value in a format usable with stov. */\nconst string INFOKEY_P_MUTED = \"ignored\";\t/* 0: we can see the result of the player's say/say_team commands.   1: we see no say/say_team messages from this player. Use the ignore command to toggle this value. */\nconst string INFOKEY_P_VOIP_MUTED = \"vignored\";\t/* 0: we can hear this player when they speak (assuming voip is generally enabled). 1: we ignore everything this player says. Use cl_voip_mute to change the values. */\nconst string INFOKEY_P_ENTERTIME = \"entertime\";\t/* Reads the timestamp at which the player entered the game, in terms of csqc's time global. */\nconst string INFOKEY_P_FRAGS = \"frags\";\t/* Reads a player's frag count. */\nconst string INFOKEY_P_PACKETLOSS = \"pl\";\t/* Reads a player's packetloss, as a percentage. */\nconst string INFOKEY_P_VOIPSPEAKING = \"voipspeaking\";\t/* Boolean value that says whether the given player is currently sending voice information. */\nconst string INFOKEY_P_VOIPLOUDNESS = \"voiploudness\";\t/* Only valid for the local player. Gives a value between 0 and 1 to indicate to the user how loud their mic is. */\nconst string SERVERKEY_IP = \"ip\";\t/* The address of the server we connected to. */\nconst string SERVERKEY_SERVERNAME = \"servername\";\t/* The hostname that was last passed to the connect command. */\nconst string SERVERKEY_CONSTATE = \"constate\";\t/* The current connection state. Will be set to one of: disconnected (menu-only mode), active (gamestate received and loaded), connecting(connecting, downloading, or precaching content, aka: loading screen). */\nconst string SERVERKEY_TRANSFERRING = \"transferring\";\t/* Set to the hostname of the server that we are attempting to connect or transfer to. */\nconst string SERVERKEY_LOADSTATE = \"loadstate\";\t/* loadstage, loading image name, current step, max steps\nStages are: 1=connecting, 2=serverside, 3=clientside\nKey will be empty if we are not loading. */\nconst string SERVERKEY_PAUSESTATE = \"pausestate\";\t/* 1 if the server claimed to be paused. 0 otherwise */\nconst string SERVERKEY_DLSTATE = \"dlstate\";\t/* The progress of any current downloads. Empty string if no download is active, otherwise a tokenizable string containing this info:\nfiles-remaining, total-size, unknown-sizes-flag, file-localname, file-remotename, file-percent, file-rate, file-received-bytes, file-total-bytes\nIf the current file info is omitted, then we are waiting for a download to start. */\nconst string SERVERKEY_PROTOCOL = \"protocol\";\t/* The protocol we are connected to the server with. */\nconst string SERVERKEY_MAXPLAYERS = \"maxplayers\";\t/* The number of player/spectator slots allocated on the server. */\n#endif\n#ifdef SSQC\nconst float STUFFCMD_IGNOREINDEMO = 1;\t/* This stuffcmd will NOT be written to mvds/qtv. */\nconst float STUFFCMD_DEMOONLY = 2;\t/* This stuffcmd will ONLY be written into mvds/qtv streams. */\nconst float STUFFCMD_BROADCAST = 4;\t/* The stuffcmd will be broadcast server-wide (according to the mvd filters). */\nconst float STUFFCMD_UNRELIABLE = 8;\t/* The stuffcmd might not arrive. It might also get there faster than ones sent over the reliable channel. */\n#endif\n#if defined(CSQC) || defined(SSQC)\nconst float FL_FLY = 1;\nconst float FL_SWIM = 2;\nconst float FL_CLIENT = 8;\nconst float FL_INWATER = 16;\nconst float FL_MONSTER = 32;\n#endif\n#ifdef SSQC\nconst float FL_GODMODE = 64;\nconst float FL_NOTARGET = 128;\n#endif\n#if defined(CSQC) || defined(SSQC)\nconst float FL_ITEM = 256;\nconst float FL_ONGROUND = 512;\nconst float FL_PARTIALGROUND = 1024;\nconst float FL_WATERJUMP = 2048;\n#endif\n#if defined(CSQC) || defined(NQSSQC)\nconst float FL_JUMPRELEASED = 4096;\n#endif\n#if defined(CSQC) || defined(SSQC)\nconst float FL_FINDABLE_NONSOLID = 16384;\t/* Allows this entity to be found with findradius */\n#endif\n#ifdef SSQC\nconst float FL_LAGGEDMOVE = 65536;\t/* Enables anti-lag on rockets etc. */\n#endif\n#if defined(CSQC) || defined(SSQC)\nconst float MOVE_NORMAL = 0;\nconst float MOVE_NOMONSTERS = 1;\t/* The trace will ignore all non-solid_bsp entities. */\nconst float MOVE_MISSILE = 2;\t/* The trace will use a bbox size of +/- 15 against entities with FL_MONSTER set. */\nconst float MOVE_WORLDONLY = 3;\t/* The trace will ignore everything but the worldmodel. This is useful for to prevent the q3bsp pvs+culling issues that come with spectator modes leaving the world . */\nconst float MOVE_HITMODEL = 4;\t/* Traces will impact the actual mesh of the model instead of merely their bounding box. Should generally only be used for tracelines. Note that this flag is unreliable as an object can animate through projectiles. The bounding box MUST be set to completely encompass the entity or those extra areas will be non-solid (leaving a hole for things to go through). */\nconst float MOVE_TRIGGERS = 16;\t/* This trace type will impact only triggers. It will ignore non-solid entities. */\nconst float MOVE_EVERYTHING = 32;\t/* This type of trace will hit solids and triggers alike. Even non-solid entities. */\n#endif\n#ifdef SSQC\nconst float MOVE_LAGGED = 64;\t/* Will use antilag based upon the player's latency. Traces will be performed against old positions for entities instead of their current origin. */\n#endif\n#if defined(CSQC) || defined(SSQC)\nconst float MOVE_ENTCHAIN = 128;\t/* Returns a list of entities impacted via the trace_ent.chain field */\nconst float MOVE_OTHERONLY = 256;\t/* Traces that use this trace type will collide against *only* the entity specified via the 'other' global, and will ignore all owner/solid_not/dimension etc rules, they will still adhere to contents and bsp/bbox rules though. */\n#endif\nconst float CVAR_TYPEFLAG_EXISTS = 1;\t/* Cvar name actually exists. */\nconst float CVAR_TYPEFLAG_SAVED = 2;\t/* Cvar is flaged for archival (might need cfg_save to actually save). */\nconst float CVAR_TYPEFLAG_PRIVATE = 4;\t/* QC is not allowed to read. */\nconst float CVAR_TYPEFLAG_ENGINE = 8;\t/* Cvar was created by the engine itself (not user/mod created). */\nconst float CVAR_TYPEFLAG_HASDESCRIPTION = 16;\t/* cvar_description will return something (hopefully) useful. */\nconst float CVAR_TYPEFLAG_READONLY = 32;\t/* cvar may not be changed by qc. */\nconst float RESTYPE_MODEL = 0;\t/* RESTYPE_* constants are used as arguments with the resourcestatus builtin. */\nconst float RESTYPE_SOUND = 1;\t/* precache_sound */\nconst float RESTYPE_PARTICLE = 2;\t/* particleeffectnum */\n#if defined(CSQC) || defined(MENU)\nconst float RESTYPE_PIC = 3;\t/* precache_pic. Status results are an amalgomation of the textures used by the named shader. */\nconst float RESTYPE_SKIN = 4;\t/* setcustomskin */\nconst float RESTYPE_TEXTURE = 5;\t/* Individual textures within shaders. These are not directly usable, but may be named as part of a skin file, or a shader. */\n#endif\nconst float RESSTATE_NOTKNOWN = 0;\t/* RESSTATE_* constants are return values from the resourcestatus builtin. The engine doesn't know about the resource if it is in this state. This means you will need to precache it. Attempting to use it anyway may result in warnings, errors, or silently succeed, depending on engine version and resource type. */\nconst float RESSTATE_NOTLOADED = 1;\t/* The resource was precached, but has been flushed and there has not been an attempt to reload it. If you use the resource normally, chances are it'll be loaded but at the cost of a stall. */\nconst float RESSTATE_LOADING = 2;\t/* Resources in this this state are queued for loading, and will be loaded at the engine's convienience. If you attempt to query the resource now, the engine will stall until the result is available. sounds in this state may be delayed, while models/pics/shaders may be invisible. */\nconst float RESSTATE_FAILED = 3;\t/* Resources in this state are unusable/could not be loaded. You will get placeholders or dummy results. Queries will not stall the engine. The engine may display placeholder content. */\nconst float RESSTATE_LOADED = 4;\t/* Resources in this state are finally usable, everything will work okay. Hurrah. Queries will not stall the engine. */\n#if defined(CSQC) || defined(SSQC)\nconst float EF_BRIGHTFIELD = 1;\n#endif\n#if defined(CSQC) || defined(NQSSQC)\nconst float EF_MUZZLEFLASH = 2;\n#endif\n#if defined(CSQC) || defined(SSQC)\nconst float EF_BRIGHTLIGHT = 4;\nconst float EF_DIMLIGHT = 8;\n#endif\n#if defined(QWSSQC)\nconst float EF_FLAG1 = 16;\nconst float EF_FLAG2 = 32;\n#endif\n#if defined(CSQC) || defined(NQSSQC)\nconst float EF_NODRAW = 16;\t/* Disables drawing of the model. Does NOT work on QW players. */\n#endif\n#if defined(CSQC) || defined(SSQC)\nconst float EF_ADDITIVE = 32;\t/* The entity will be drawn with an additive blend. This is NOT supported on players in any quakeworld engine. */\nconst float EF_BLUE = 64;\t/* A blue glow */\nconst float EF_RED = 128;\t/* A red glow */\nconst float EF_GREEN = 262144;\t/* A green glow */\nconst float EF_FULLBRIGHT = 512;\t/* This entity will ignore lighting */\nconst float EF_NOSHADOW = 4096;\t/* This entity will not cast shadows */\nconst float EF_NODEPTHTEST = 8192;\t/* This entity will be drawn over the top of other things that are closer. */\n#endif\n#ifdef SSQC\nconst float EF_NOMODELFLAGS = 8388608;\t/* Surpresses the normal flags specified in the model. */\n#endif\n#if defined(CSQC) || defined(SSQC)\nconst float MF_ROCKET = 1;\nconst float MF_GRENADE = 2;\nconst float MF_GIB = 4;\t/* Regular blood trail */\nconst float MF_ROTATE = 8;\nconst float MF_TRACER = 16;\t/* AKA: green scrag trail */\nconst float MF_ZOMGIB = 32;\t/* Dark blood trail */\nconst float MF_TRACER2 = 64;\t/* AKA: hellknight projectile trail */\nconst float MF_TRACER3 = 128;\t/* AKA: purple vore trail */\n#endif\n#ifdef SSQC\nDEP_CSQC const float SL_ORG_TL = 20;\t/* Used with showpic etc, specifies that the x+y values are relative to the top-left of the screen */\nDEP_CSQC const float SL_ORG_TR = 21;\nDEP_CSQC const float SL_ORG_BL = 22;\nDEP_CSQC const float SL_ORG_BR = 23;\nDEP_CSQC const float SL_ORG_MM = 24;\nDEP_CSQC const float SL_ORG_TM = 25;\nDEP_CSQC const float SL_ORG_BM = 26;\nDEP_CSQC const float SL_ORG_ML = 27;\nDEP_CSQC const float SL_ORG_MR = 28;\n#endif\n#if defined(CSQC) || defined(SSQC)\nconst float PFLAGS_NOSHADOW = 1;\t/* Associated RT lights attached will not cast shadows, making them significantly faster to draw. */\nconst float PFLAGS_CORONA = 2;\t/* Enables support of coronas on the associated rtlights. */\n#endif\n#ifdef SSQC\nconst float PFLAGS_FULLDYNAMIC = 128;\t/* When set in self.pflags, enables fully-customised dynamic lights. Custom rtlight information is not otherwise used. */\n#endif\nconst float EV_STRING = 1;\nconst float EV_FLOAT = 2;\nconst float EV_VECTOR = 3;\nconst float EV_ENTITY = 4;\nconst float EV_FIELD = 5;\nconst float EV_FUNCTION = 6;\nconst float EV_POINTER = 7;\nconst float EV_INTEGER = 8;\nconst float EV_UINT = 9;\nconst float EV_INT64 = 10;\nconst float EV_UINT64 = 11;\nconst float EV_DOUBLE = 12;\nhashtable gamestate;\t/* Special hash table index for hash_add and hash_get. Entries in this table will persist over map changes (and doesn't need to be created/deleted). */\nconst float HASH_REPLACE = 256;\t/* Used with hash_add. Attempts to remove the old value instead of adding two values for a single key. */\nconst float HASH_ADD = 512;\t/* Used with hash_add. The new entry will be inserted in addition to the existing entry. */\n#ifdef CSQC\nconst float STAT_HEALTH = 0;\t/* Player's health. */\nconst float STAT_WEAPONMODELI = 2;\t/* This is the modelindex of the current viewmodel (renamed from the original name 'STAT_WEAPON' due to confusions). */\nconst float STAT_AMMO = 3;\t/* player.currentammo */\nconst float STAT_ARMOR = 4;\nconst float STAT_WEAPONFRAME = 5;\nconst float STAT_SHELLS = 6;\nconst float STAT_NAILS = 7;\nconst float STAT_ROCKETS = 8;\nconst float STAT_CELLS = 9;\nconst float STAT_ACTIVEWEAPON = 10;\t/* player.weapon */\nconst float STAT_TOTALSECRETS = 11;\nconst float STAT_TOTALMONSTERS = 12;\nconst float STAT_FOUNDSECRETS = 13;\nconst float STAT_KILLEDMONSTERS = 14;\nconst float STAT_ITEMS = 15;\t/* self.items | (self.items2<<23). In order to decode this stat properly, you need to use getstatbits(STAT_ITEMS,0,23) to read self.items, and getstatbits(STAT_ITEMS,23,11) to read self.items2 or getstatbits(STAT_ITEMS,28,4) to read the visible part of serverflags, whichever is applicable. */\nconst float STAT_VIEWHEIGHT = 16;\t/* player.view_ofs_z */\nconst float STAT_VIEW2 = 20;\t/* This stat contains the number of the entity in the server's .view2 field. */\nconst float STAT_VIEWZOOM = 21;\t/* Scales fov and sensitiity. Part of DP_VIEWZOOM. */\n#endif\n#if defined(CSQC) || defined(SSQC)\nconst float STAT_USER = 32;\t/* Custom user stats start here (lower values are reserved for engine use). */\n#endif\n#if defined(CSQC) || defined(MENU)\nconst float PRECACHE_PIC_FROMWAD = 1;\t/* Attempt to load it from the legacy gfx.wad file (usually its better to just use a gfx/ prefix instead). */\nconst float PRECACHE_PIC_NOCLAMP = 4;\t/* Texture coords for the pic will not be clamped nor padded nor atlased. */\nconst float PRECACHE_PIC_DOWNLOAD = 256;\t/* If no image could be loaded then attempt to download one from the server. This flag can cause the function to block until completion. (Slow!) */\nconst float PRECACHE_PIC_TEST = 512;\t/* The precache will block until the image is fully loaded, returning a null string on failure. (Slow!) */\nconst float VF_MIN = 1;\t/* The top-left of the 3d viewport in screenspace. The VF_ values are used via the setviewprop/getviewprop builtins. */\nconst float VF_MIN_X = 2;\nconst float VF_MIN_Y = 3;\nconst float VF_SIZE = 4;\t/* The width+height of the 3d viewport in screenspace. */\nconst float VF_SIZE_X = 5;\nconst float VF_SIZE_Y = 6;\nconst float VF_VIEWPORT = 7;\t/* vector+vector. Two argument shortcut for VF_MIN and VF_SIZE */\nconst float VF_FOV = 8;\t/* sets both fovx and fovy. consider using afov instead. */\nconst float VF_FOVX = 9;\t/* horizontal field of view. does not consider aspect at all. */\nconst float VF_FOVY = 10;\t/* vertical field of view. does not consider aspect at all. */\nconst float VF_ORIGIN = 11;\t/* The origin of the view. Not of the player. */\nconst float VF_ORIGIN_X = 12;\nconst float VF_ORIGIN_Y = 13;\nconst float VF_ORIGIN_Z = 14;\nconst float VF_ANGLES = 15;\t/* The angles the view will be drawn at. Not the angle the client reports to the server. */\nconst float VF_ANGLES_X = 16;\nconst float VF_ANGLES_Y = 17;\nconst float VF_ANGLES_Z = 18;\n#endif\n#ifdef CSQC\nconst float VF_DRAWWORLD = 19;\t/* boolean. If set to 1, the engine will draw the world and static/persistant rtlights. If 0, the world will be skipped and everything will be fullbright. */\nconst float VF_DRAWENGINESBAR = 20;\t/* boolean. If set to 1, the sbar will be drawn, and viewsize will be honoured automatically. */\nconst float VF_DRAWCROSSHAIR = 21;\t/* boolean. If set to 1, the engine will draw its default crosshair. */\n#endif\n#if defined(CSQC) || defined(MENU)\nconst float VF_MINDIST = 23;\t/* The distance of the near clip plane from the view position. Should generally not be <=0, as this would introduce NANs. */\nconst float VF_MAXDIST = 24;\t/* The distance of the far clip plane from the view position. If 0, will be considered infinite. */\n#endif\n#ifdef CSQC\nconst float VF_CL_VIEWANGLES = 33;\nconst float VF_CL_VIEWANGLES_X = 34;\nconst float VF_CL_VIEWANGLES_Y = 35;\nconst float VF_CL_VIEWANGLES_Z = 36;\n#endif\n#if defined(CSQC) || defined(MENU)\nconst float VF_PERSPECTIVE = 200;\t/* 1: regular rendering. Fov specifies the angle. 0: isometric-style. Fov specifies the number of Quake Units each side of the viewport, and mindist restrictions are removed, pvs culling should be disabled. */\n#endif\n#ifdef CSQC\n#define VF_LPLAYER VF_ACTIVESEAT\nconst float VF_ACTIVESEAT = 202;\t/* The 'seat' number, used when running splitscreen. */\n#endif\n#if defined(CSQC) || defined(MENU)\nconst float VF_AFOV = 203;\t/* Aproximate fov. Matches the 'fov' cvar. The engine handles the aspect ratio for you. */\nconst float VF_SCREENVSIZE = 204;\t/* Provides a reliable way to retrieve the current virtual screen size (even if the screen is automatically scaled to retain aspect). */\nconst float VF_SCREENPSIZE = 205;\t/* Provides a reliable way to retrieve the current physical screen size (cvars need vid_restart for them to take effect). */\n#endif\n#ifdef CSQC\nconst float VF_VIEWENTITY = 206;\t/* Changes the RF_EXTERNALMODEL flag on entities to match the new selection, and removes entities flaged with RF_VIEWENTITY. Requires cunning use of .entnum and typically requires calling addentities(MASK_VIEWMODEL) too. */\n#endif\n#if defined(CSQC) || defined(MENU)\nconst float VF_RT_DESTCOLOUR = 212;\t/* The texture name to write colour info into, this includes both 3d and 2d drawing.\nAdditional arguments are: format (IMGFMT_*), sizexy.\nWritten to by both 3d and 2d rendering.\nNote that any rendertarget textures may be destroyed on video mode changes or so. Shaders can name render targets by prefixing texture names with '$rt:', or $sourcecolour. */\nconst float VF_RT_DESTCOLOUR1 = 213;\t/* Like VF_RT_DESTCOLOUR, for multiple render targets. */\nconst float VF_RT_DESTCOLOUR2 = 214;\t/* Like VF_RT_DESTCOLOUR, for multiple render targets. */\nconst float VF_RT_DESTCOLOUR3 = 215;\t/* Like VF_RT_DESTCOLOUR, for multiple render targets. */\nconst float VF_RT_SOURCECOLOUR = 209;\t/* The texture name to use with shaders that specify a $sourcecolour map. */\nconst float VF_RT_DEPTH = 210;\t/* The texture name to use as a depth buffer. Also used for shaders that specify $sourcedepth. 1-based. Additional arguments are: format (IMGFMT_D*), sizexy. */\nconst float VF_RT_RIPPLE = 211;\t/* The texture name to use as a ripplemap (target for shaders with 'sort ripple'). Also used for shaders that specify $ripplemap. 1-based. Additional arguments are: format, sizexy. */\nconst float VF_ENVMAP = 220;\t/* The cubemap name to use as a fallback for $reflectcube, if a shader was unable to load one. Note that this doesn't automatically change shader permutations or anything. */\nconst float VF_USERDATA = 221;\t/* Pointer (and byte size) to an array of vec4s. This data is then globally visible to all glsl via the w_user uniform. */\n#endif\n#ifdef CSQC\nconst float VF_SKYROOM_CAMERA = 222;\t/* Controls the camera position of the skyroom (which will be drawn underneath transparent sky surfaces). This should move slightly with the real camera, but not so much that the skycamera enters walls. Requires a skyshader with a blend mode on the first pass (or no passes). */\n#endif\n#if defined(CSQC) || defined(MENU)\nconst float VF_PROJECTIONOFFSET = 224;\t/* vec2 horizontal+vertical offset for the projection matrix, for weird off-centre rendering. */\nconst float DRAWFLAG_NORMAL = 0;\t/* Args for drawpic/drawfill/beginpolygon. Not to be confused with the hexen2-compatibility feature. */\nconst float DRAWFLAG_ADD = 1;\t/* Forces additive blending, overriding any shader settings. */\nconst float DRAWFLAG_MODULATE = 2;\t/* Forces alpha blending, overriding any shader settings. */\nconst float DRAWFLAG_2D = 4;\t/* For use with beginpolygon. The polygon will be drawn to the 2d screen, instead of being added to the 3d scene. */\nconst float DRAWFLAG_TWOSIDED = 1024;\t/* For use with beginpolygon. The polygon will be two-sided without any backface culling. */\nconst float DRAWFLAG_LINES = 2048;\t/* For use with beginpolygon. The surface verticies should be interpreted as a line loop, instead of a triangle fan. */\nconst float IMGFMT_R8G8B8A8 = 1;\t/* Typical 32bit rgba pixel format. */\nconst float IMGFMT_R16G16B16A16F = 2;\t/* Half-Float pixel format. Requires gl3 support. */\nconst float IMGFMT_R32G32B32A32F = 3;\t/* Regular Float pixel format. Requires gl3 support. */\nconst float IMGFMT_D16 = 4;\t/* 16-bit depth pixel format. Must not be used with VF_RT_DESTCOLOUR*. */\nconst float IMGFMT_D24 = 5;\t/* 24-bit depth pixel format. Must not be used with VF_RT_DESTCOLOUR*. */\nconst float IMGFMT_D32 = 6;\t/* 32-bit depth pixel format. Must not be used with VF_RT_DESTCOLOUR*. */\nconst float IMGFMT_R8 = 7;\t/* Single channel red-only 8bit pixel format. */\nconst float IMGFMT_R16F = 8;\t/* Single channel red-only Half-Float pixel format. Requires gl3 support. */\nconst float IMGFMT_R32F = 9;\t/* Single channel red-only Float pixel format. Requires gl3 support. */\nconst float IMGFMT_A2B10G10R10 = 10;\t/* Packed 32-bit packed 10-bit colour pixel format. Requires gl3 support. */\nconst float IMGFMT_R5G6B5 = 11;\t/* Packed 16-bit colour pixel format. */\nconst float IMGFMT_R4G4B4A4 = 12;\t/* Packed 16-bit colour pixel format, with alpha */\nconst float IMGFMT_R8G8 = 13;\t/* 16-bit two-channel pixel format. */\nconst float IMGFMT_R32G32B32F = 14;\t/* A pixel format that matches QC's vector type. */\n#endif\n#ifdef CSQC\nconst float RF_VIEWMODEL = 1;\t/* Specifies that the entity is a view model, and that its origin is relative to the current view position. These entities are also subject to viewweapon bob. */\nconst float RF_EXTERNALMODEL = 2;\t/* Specifies that this entity should be displayed in mirrors (and may still cast shadows), but will not otherwise be visible. */\n#endif\n#if defined(CSQC) || defined(MENU)\nconst float RF_DEPTHHACK = 4;\t/* Hacks the depth values such that the entity uses depth values as if it were closer to the screen. This is useful when combined with viewmodels to avoid weapons poking in to walls. */\nconst float RF_ADDITIVE = 8;\t/* Shaders from this entity will temporarily be hacked to use an additive blend mode instead of their normal blend mode. */\n#endif\n#ifdef CSQC\nconst float RF_USEAXIS = 16;\t/* The entity will be oriented according to the current v_forward+v_right+v_up vector values instead of the entity's .angles field. */\nconst float RF_NOSHADOW = 32;\t/* This entity will not cast shadows. Often useful on view models. */\nconst float RF_FRAMETIMESARESTARTTIMES = 64;\t/* Specifies that the frame1time, frame2time field are timestamps (denoting the start of the animation) rather than time into the animation. */\n#endif\n#if defined(CSQC) || defined(MENU)\nconst float IE_KEYDOWN = 0;\t/* Specifies that a key was pressed. Second argument is the scan code. Third argument is the unicode (printable) char value. Fourth argument denotes which keyboard(or mouse, if its a mouse 'scan' key) the event came from. Note that some systems may completely separate scan codes and unicode values, with a 0 value for the unspecified argument. */\nconst float IE_KEYUP = 1;\t/* Specifies that a key was released. Arguments are the same as IE_KEYDOWN. On some systems, this may be fired instantly after IE_KEYDOWN was fired. */\nconst float IE_MOUSEDELTA = 2;\t/* Specifies that a mouse was moved (touch screens and tablets typically give IE_MOUSEABS events instead, use in_windowed_mouse 0 to test code to cope with either). Second argument is the X displacement, third argument is the Y displacement. Fourth argument is which mouse or touch event triggered the event. */\nconst float IE_MOUSEABS = 3;\t/* Specifies that a mouse cursor or touch event was moved to a specific location relative to the virtual screen space. Second argument is the new X position, third argument is the new Y position. Fourth argument is which mouse or touch event triggered the event. */\nconst float IE_ACCELEROMETER = 4;\nconst float IE_FOCUS = 5;\t/* Specifies that input focus was given. parama says mouse focus, paramb says keyboard focus. If either are -1, then it is unchanged. */\nconst float IE_JOYAXIS = 6;\t/* Specifies that what value a joystick/controller axis currently specifies. x=axis, y=value. Will be called multiple times, once for each axis of each active controller. */\nconst float IE_GYROSCOPE = 7;\nconst float GGDI_GAMEDIR = 0;\t/* Used with getgamedirinfo to query the mod's public gamedir. There is often other info that cannot be expressed with just a gamedir name, resulting in dupes or other weirdness. */\nconst float GGDI_DESCRIPTION = 1;\t/* The human-readable title of the mod. Empty when no data is known (ie: the gamedir just contains some maps). */\nconst float GGDI_OVERRIDES = 2;\t/* A list of settings overrides. */\nconst float GGDI_LOADCOMMAND = 3;\t/* The console command needed to actually load the mod. */\nconst float GGDI_ICON = 4;\t/* The mod's Icon path, ready for drawpic. */\nconst float GGDI_GAMEDIRLIST = 5;\t/* A semi-colon delimited list of gamedirs that the mod's content can be loaded through. */\n#endif\n#ifdef SSQC\nconst float CLIENTTYPE_DISCONNECTED = 0;\t/* Return value from clienttype() builtin. This entity is a player slot that is currently empty. */\nconst float CLIENTTYPE_REAL = 1;\t/* This is a real player, and not a bot. */\nconst float CLIENTTYPE_BOT = 2;\t/* This player slot does not correlate to a real player, any messages sent to this client will be ignored. */\nconst float CLIENTTYPE_NOTACLIENT = 3;\t/* This entity is not even a player slot. This is typically an error condition. */\n#endif\nconst float FILE_READ = 0;\t/* The file may be read via fgets to read a single line at a time. */\nconst float FILE_APPEND = 1;\t/* Like FILE_WRITE, but writing starts at the end of the file. */\nconst float FILE_WRITE = 2;\t/* fputs will be used to write to the file. */\n#if defined(CSQC) || defined(SSQC)\nconst float FILE_READNL = 4;\t/* Like FILE_READ, except newlines are not special. fgets reads the entire file into a tempstring. */\nconst float FILE_MMAP_READ = 5;\t/* The file will be loaded into memory. fgets returns a pointer to the first byte (and will always return the same value for this file). Cast this to your datatype. */\nconst float FILE_MMAP_RW = 6;\t/* Like FILE_MMAP_READ, except any changes to the data will be written back to disk once the file is closed. */\n#endif\n#ifdef CSQC\nconst float MASK_ENGINE = 1;\t/* Valid as an argument for addentities. If specified, all non-csqc entities will be added to the scene. */\nconst float MASK_VIEWMODEL = 2;\t/* Valid as an argument for addentities. If specified, the regular engine viewmodel will be added to the scene. */\nconst float PREDRAW_AUTOADD = 0;\t/* Valid as a return value from the predraw function. Returning this will cause the engine to automatically invoke addentity(self) for you. */\nconst float PREDRAW_NEXT = 1;\t/* Valid as a return value from the predraw function. Returning this will simply move on to the next entity without the autoadd behaviour, so can be used for particle/invisible/special entites, or entities that were explicitly drawn with addentity. */\nconst float LFIELD_ORIGIN = 0;\nconst float LFIELD_COLOUR = 1;\nconst float LFIELD_RADIUS = 2;\nconst float LFIELD_FLAGS = 3;\nconst float LFIELD_STYLE = 4;\nconst float LFIELD_ANGLES = 5;\nconst float LFIELD_FOV = 6;\nconst float LFIELD_CORONA = 7;\nconst float LFIELD_CORONASCALE = 8;\nconst float LFIELD_CUBEMAPNAME = 9;\nconst float LFIELD_AMBIENTSCALE = 10;\nconst float LFIELD_DIFFUSESCALE = 11;\nconst float LFIELD_SPECULARSCALE = 12;\nconst float LFIELD_ROTATION = 13;\nconst float LFIELD_DIETIME = 14;\nconst float LFIELD_RGBDECAY = 15;\nconst float LFIELD_RADIUSDECAY = 16;\nconst float LFIELD_STYLESTRING = 17;\nconst float LFIELD_NEARCLIP = 18;\nconst float LFLAG_NORMALMODE = 1;\nconst float LFLAG_REALTIMEMODE = 2;\nconst float LFLAG_LIGHTMAP = 4;\nconst float LFLAG_FLASHBLEND = 8;\nconst float LFLAG_NOSHADOWS = 256;\nconst float LFLAG_SHADOWMAP = 512;\nconst float LFLAG_CREPUSCULAR = 1024;\nconst float LFLAG_ORTHOSUN = 2048;\nconst float TEREDIT_RELOAD = 0;\nconst float TEREDIT_SAVE = 1;\nconst float TEREDIT_SETHOLE = 2;\nconst float TEREDIT_HEIGHT_SET = 3;\nconst float TEREDIT_HEIGHT_SMOOTH = 4;\nconst float TEREDIT_HEIGHT_SPREAD = 5;\nconst float TEREDIT_HEIGHT_RAISE = 6;\nconst float TEREDIT_HEIGHT_FLATTEN = 18;\nconst float TEREDIT_HEIGHT_LOWER = 7;\nconst float TEREDIT_TEX_KILL = 8;\nconst float TEREDIT_TEX_GET = 9;\nconst float TEREDIT_TEX_BLEND = 10;\nconst float TEREDIT_TEX_UNIFY = 11;\nconst float TEREDIT_TEX_NOISE = 12;\nconst float TEREDIT_TEX_BLUR = 13;\nconst float TEREDIT_TEX_REPLACE = 19;\nconst float TEREDIT_TEX_SETMASK = 25;\nconst float TEREDIT_WATER_SET = 14;\nconst float TEREDIT_MESH_ADD = 15;\nconst float TEREDIT_MESH_KILL = 16;\nconst float TEREDIT_TINT = 17;\nconst float TEREDIT_RESET_SECT = 20;\nconst float TEREDIT_RELOAD_SECT = 21;\nconst float TEREDIT_ENT_GET = 26;\nconst float TEREDIT_ENT_SET = 27;\nconst float TEREDIT_ENT_ADD = 28;\nconst float TEREDIT_ENT_COUNT = 29;\n#endif\n#if defined(CSQC) || defined(MENU)\nconst float SLIST_HOSTCACHEVIEWCOUNT = 0;\nconst float SLIST_HOSTCACHETOTALCOUNT = 1;\nconst float SLIST_MASTERQUERYCOUNT = 2;\nconst float SLIST_MASTERREPLYCOUNT = 3;\nconst float SLIST_SERVERQUERYCOUNT = 4;\nconst float SLIST_SERVERREPLYCOUNT = 5;\nconst float SLIST_SORTFIELD = 6;\nconst float SLIST_SORTDESCENDING = 7;\nconst float SLIST_TEST_CONTAINS = 0;\nconst float SLIST_TEST_NOTCONTAIN = 1;\nconst float SLIST_TEST_LESSEQUAL = 2;\nconst float SLIST_TEST_LESS = 3;\nconst float SLIST_TEST_EQUAL = 4;\nconst float SLIST_TEST_GREATER = 5;\nconst float SLIST_TEST_GREATEREQUAL = 6;\nconst float SLIST_TEST_NOTEQUAL = 7;\nconst float SLIST_TEST_STARTSWITH = 8;\nconst float SLIST_TEST_NOTSTARTSWITH = 9;\n#endif\n#ifdef MENU\nfloat(string ext) checkextension = #1; /*\n\t\tChecks if the named extension is supported by the running engine. */\n\nvoid(string err,...) error = #2; /*\n\t\tFatal error that will trigger a crash-to-console that users will actually notice. */\n\nvoid(string err,...) objerror = #3; /*\n\t\tFor some reason this has been redefined as non-fatal, and as it won't force the user to look at the console it'll generally be ignored completely so really what's the point? Other than as a convoluted way to remove(self) that is. */\n\nvoid(string text,...) print = #4; /* Part of DP_SV_PRINT\n\t\tHello, world. Shoves junk on the console. Hopefully people will bother to read it, maybe. */\n\nDEP void(string text,...) bprint = #5;\nDEP void(float clientnum, string text,...) msprint = #6;\nvoid(string text,...) cprint = #7; /*\n\t\tTries to show the given message in the centre of the screen, assuming that its not obscured by menus. Oh hey look, you're calling it in menuqc! */\n\nvector(vector) normalize = #8;\nfloat(vector) vlen = #9;\nfloat(vector) vectoyaw = #10;\nvector(vector) vectoangles = #11;\nfloat() random = #12;\nvoid(string,...) localcmd = #13;\nfloat(string name) cvar = #14;\nvoid(string name, string value) cvar_set = #15;\nvoid(string text, ...) dprint = #16;\nstring(float) ftos = #17;\nfloat(float) fabs = #18;\nstring(vector) vtos = #19;\nstring(entity) etos = #20; /* Part of DP_QC_ETOS*/\nfloat(string) stof = #21; /* Part of FRIK_FILE, FTE_QC_INFOKEY, FTE_STRINGS, QW_ENGINE, ZQ_QC_STRINGS*/\nentity() spawn = #22;\nvoid(entity) remove = #23;\nentity(entity start, .string field, string match) find = #24;\nentity(entity start, .__variant field, __variant match) findfloat = #25; /* Part of DP_QC_FINDFLOAT*/\nentity(.string field, string match) findchain = #26; /* Part of DP_QC_FINDCHAIN*/\nentity(.__variant field, __variant match) findchainfloat = #27; /* Part of DP_QC_FINDCHAINFLOAT*/\nstring(string file) precache_file = #28; /*\n\t\tAttempts to download the named file from the current server, if it isn't found locally. Not very useful as menuqc is normally meant to work before joining servers too. */\n\nstring(string sample) precache_sound = #29;\nvoid() coredump = #30; /*\n\t\tTakes a dump, writing the qcvm's state to disk. There are normally easier ways to debug, but I suppose this one still beats print spam. */\n\nvoid() traceon = #31; /*\n\t\tEnables single-stepping. Its generally easier to just set a breakpoint. */\n\nvoid() traceoff = #32; /*\n\t\tDisables single-stepping. Which sucks if you started said singlestepping outside of qc. */\n\nvoid(entity) eprint = #33;\nfloat(float) rint = #34;\nfloat(float) floor = #35;\nfloat(float) ceil = #36;\nentity(entity) nextent = #37;\nfloat(float) sin = #38; /* Part of DP_QC_SINCOSSQRTPOW*/\nfloat(float) cos = #39; /* Part of DP_QC_SINCOSSQRTPOW*/\nfloat(float) sqrt = #40; /* Part of DP_QC_SINCOSSQRTPOW*/\nvector() randomvector = #41;\nfloat(string name, string value, float flags) registercvar = #42; /* Part of DP_REGISTERCVAR\n\t\tCreates the cvar if it didn't already exist. This presents issues for setting those cvars via startup configs of course, and autocvars are easier but I suppose they don't get any flags (which are ignored anyway, of course). */\n\nfloat(float,...) min = #43; /* Part of DP_QC_MINMAXBOUND*/\nfloat(float,...) max = #44; /* Part of DP_QC_MINMAXBOUND*/\nfloat(float min,float value,float max) bound = #45; /* Part of DP_QC_MINMAXBOUND*/\nfloat(float,float) pow = #46; /* Part of DP_QC_SINCOSSQRTPOW*/\nvoid(entity src, entity dst) copyentity = #47; /* Part of DP_QC_COPYENTITY\n\t\tCopies all entity fields from one entity into another (forgetting any that were previously set on the destination). */\n\nfilestream(string filename, float mode) fopen = #48; /* Part of FRIK_FILE*/\nvoid(filestream fhandle) fclose = #49; /* Part of FRIK_FILE*/\nstring(filestream fhandle) fgets = #50; /* Part of FRIK_FILE*/\nvoid(filestream fhandle, string s) fputs = #51; /* Part of FRIK_FILE*/\nfloat(string) strlen = #52; /* Part of FRIK_FILE, FTE_STRINGS, ZQ_QC_STRINGS*/\nstring(string, optional string, optional string, optional string, optional string, optional string, optional string, optional string) strcat = #53; /* Part of FRIK_FILE, FTE_STRINGS, ZQ_QC_STRINGS*/\nstring(string s, float start, float length) substring = #54; /* Part of FRIK_FILE, FTE_STRINGS, ZQ_QC_STRINGS*/\nvector(string) stov = #55; /* Part of FRIK_FILE, FTE_STRINGS, ZQ_QC_STRINGS*/\nFTEDEP(\"Redundant\") string(string) strzone = #56; /* Part of FRIK_FILE, FTE_STRINGS, ZQ_QC_STRINGS\n\t\tExists in FTE for compat only, no different from strcat. */\n\nFTEDEP(\"Redundant\") void(string) strunzone = #57; /* Part of FRIK_FILE, FTE_STRINGS, ZQ_QC_STRINGS\n\t\tExists in FTE for compat only, does nothing. */\n\nfloat(string) tokenize = #58; /* Part of KRIMZON_SV_PARSECLIENTCOMMAND\n\t\tSplits up the given string into its different components (what constitutes a token separator is not well defined and has been hacked about with over the years so have fun with that), returning the number of tokens that were found. Call argv(0 through ret-1) to retrieve each individual token. Take care to not use this recursively. */\n\nstring(float) argv = #59; /* Part of KRIMZON_SV_PARSECLIENTCOMMAND\n\t\tReturns one of the tokens found via tokenize (and equivelent builtins). */\n\nfloat() isserver = #60; /*\n\t\tReturns true if the local engine is running a server, and thus cvars and localcmds are shared with said server. */\n\nfloat() clientcount = #61; /*\n\t\tReturns the maximum number of players on the server. Useless if its a remote server, so its a kinda useless builtin really. */\n\nfloat() clientstate = #62; /*\n\t\tTells you whether the client is actually connected to anything. 0 for a dedicated server (but dedicated servers don't normally run menuqc anyway), 2 if connecting or connected to a server (but not necessarily spawned+active), 1 for sitting around idle without trying to connect to anything yet. */\n\nvoid(string map) changelevel = #64; /*\n\t\tNot really any different from a localcmd, but with proper string escapes. */\n\nvoid(string sample, optional float channel, optional float volume) localsound = #65; /*\n\t\tPlays a sound, locally. precaching is optional, but recommended. */\n\nvector() getmousepos = #66; /*\n\t\tObsolete. Return values depend upon the current cursor mode. Implement Menu_InputEvent instead, so you can handle deltas as-is or absolutes if that's all the OS can provide. */\n\nfloat(optional float timetype) gettime = #67;\nvoid(string s) loadfromdata = #68; /*\n\t\tReads a set of entities from the given string. This string should have the same format as a .ent file or a saved game. Entities will be spawned as required. If you need to see the entities that were created, you should use parseentitydata instead. */\n\nvoid(string s) loadfromfile = #69; /*\n\t\tReads a set of entities from the named file. This file should have the same format as a .ent file or a saved game. Entities will be spawned as required. If you need to see the entities that were created, you should use parseentitydata instead. */\n\nfloat(float val, float m) mod = #70;\nstring(string name) cvar_string = #71; /* Part of DP_QC_CVAR_STRING\n\t\tReturns the value of a cvar, as a string. */\n\nvoid() crash = #72; /*\n\t\tDemonstrates that no program is bug free. */\n\nvoid() stackdump = #73; /*\n\t\tPrints out the QC's stack, for console-based error reports. */\n\nsearchhandle(string pattern, enumflags:float{SB_CASEINSENSITIVE=1<<0,SB_FULLPACKAGEPATH=1<<1,SB_ALLOWDUPES=1<<2,SB_FORCESEARCH=1<<3,SB_MULTISEARCH=1<<4} flags, float quiet, optional string package) search_begin = #74; /* Part of DP_QC_FS_SEARCH, DP_QC_FS_SEARCH_PACKFILE*/\nvoid(searchhandle handle) search_end = #75; /* Part of DP_QC_FS_SEARCH, DP_QC_FS_SEARCH_PACKFILE*/\nfloat(searchhandle handle) search_getsize = #76; /* Part of DP_QC_FS_SEARCH, DP_QC_FS_SEARCH_PACKFILE*/\nstring(searchhandle handle, float num) search_getfilename = #77; /* Part of DP_QC_FS_SEARCH, DP_QC_FS_SEARCH_PACKFILE*/\nfloat(entity) etof = #79;\nentity(float) ftoe = #80;\nfloat(string) validstring = #81; /*\n\t\tReturns true if str isn't null. In case 'if [not](str)' was configured to test for empty instead of null. */\n\nDEP float(string str) altstr_count = #82; /*\n\t\tReports how many single-quotes there were in the string, divided by 2. */\n\nDEP string(string str) altstr_prepare = #83; /*\n\t\tAdds markup to escape only single-quotes. Does not add any. */\n\nDEP string(string str, float num) altstr_get = #84; /*\n\t\tGets the Nth single-quoted token in the input. */\n\nDEP string(string str, float num, string setval) altstr_set = #85; /*\n\t\tChanges the Nth single-quoted token. The setval argument must not contain any single-quotes (use altstr_prepare to ensure this). */\n\nentity(entity start, .float field, float match) findflags = #87; /* Part of DP_QC_FINDFLAGS*/\nentity(.float field, float match) findchainflags = #88; /* Part of DP_QC_FINDCHAINFLAGS*/\nstring(string name) cvar_defstring = #89; /* Part of DP_QC_CVAR_DEFSTRING*/\nvoid(entity ent, string mname) setmodel = #90; /*\n\t\tMenuqc-specific version. */\n\nvoid(string mname) precache_model = #91; /*\n\t\tMenuqc-specific version. */\n\nvoid(entity ent, vector neworg) setorigin = #92; /*\n\t\tMenuqc-specific version. */\n\n#endif\n#if defined(CSQC) || defined(SSQC)\nvoid(vector vang) makevectors = #1; /*\n\t\tTakes an angle vector (pitch,yaw,roll) (+x=DOWN). Writes its results into v_forward, v_right, v_up vectors. */\n\nvoid(entity e, vector o) setorigin = #2; /*\n\t\tChanges e's origin to be equal to o. Also relinks collision state (as well as setting absmin+absmax), which is required after changing .solid */\n\nvoid(entity e, string m) setmodel = #3; /*\n\t\tLooks up m in the model precache list, and sets both e.model and e.modelindex to match. BSP models will set e.mins and e.maxs accordingly, other models depend upon the value of sv_gameplayfix_setmodelrealbox - for compatibility you should always call setsize after all pickups or non-bsp models. Also relinks collision state. */\n\nvoid(entity e, vector min, vector max) setsize = #4; /*\n\t\tSets the e's mins and maxs fields. Also relinks collision state, which sets absmin and absmax too. */\n\n#endif\n#ifdef SSQC\nvoid() breakpoint = #6; /*\n\t\tTrigger a debugging event. FTE will break into the qc debugger. Other engines may crash with a debug execption. */\n\n#endif\n#if defined(CSQC) || defined(SSQC)\nfloat() random = #7; /*\n\t\tReturns a random value between 0 and 1. Be warned, this builtin can return 1 in most engines, which can break arrays. */\n\nvoid(entity e, float chan, string samp, float vol, float atten, optional float speedpct, optional float flags, optional float timeofs) sound = #8; /*\n\t\tStarts a sound centered upon the given entity.\n\t\tchan is the entity sound channel to use, channel 0 will allow you to mix many samples at once, others will replace the old sample\n\t\t'samp' must have been precached first\n\t\tif specified, 'speedpct' should normally be around 100 (or =0), 200 for double speed or 50 for half speed.\n\t\tIf flags is specified, the reliable flag in the channels argument is used for additional channels. Flags should be made from SOUNDFLAG_* constants\n\t\ttimeofs should be negative in order to provide a delay before the sound actually starts. */\n\nvector(vector v) normalize = #9; /*\n\t\tShorten or lengthen a direction vector such that it is only one quake unit long. */\n\nvoid(string e) error = #10; /*\n\t\tEnds the game with an easily readable error message. */\n\nvoid(string e) objerror = #11; /*\n\t\tDisplays a non-fatal easily readable error message concerning the self entity, including a field dump. self will be removed! */\n\nfloat(vector v) vlen = #12; /*\n\t\tReturns the square root of the dotproduct of a vector with itself. Or in other words the length of a distance vector, in quake units. */\n\nfloat(vector v, optional entity reference) vectoyaw = #13; /*\n\t\tGiven a direction vector, returns the yaw angle in which that direction vector points. If an entity is passed, the yaw angle will be relative to that entity's gravity direction. */\n\nentity() spawn = #14; /*\n\t\tAdds a brand new entity into the world! Hurrah, you're now a parent! */\n\nvoid(entity e) remove = #15; /*\n\t\tDestroys the given entity and clears some limited fields (including model, modelindex, solid, classname). Any references to the entity following the call are an error. After half a second the entity will be reused, in the interim you can unfortunatly still read its fields to see if the reference is no longer valid. */\n\n#endif\nvoid(entity e) removeinstant = #0:removeinstant; /*\n\t\tSame thing as the regular remove builtin, but bypasses the half-second rule. The entity slot may be reused instantly. Be CERTAIN that you have no lingering references, because if they're followed they will end up poking an entirely different type of entity! So only use this where you're sure its safe. */\n\n#if defined(CSQC) || defined(SSQC)\nvoid(vector v1, vector v2, float flags, entity ent) traceline = #16; /*\n\t\tTraces a thin line through the world from v1 towards v2.\n\t\tWill not collide with ent, ent.owner, or any entity who's owner field refers to ent.\n\t\tThe passed entity will also be used to determine whether to use a capsule trace, the contents that the trace should impact, and a couple of other extra fields that define the trace.\n\t\tThere are no side effects beyond the trace_* globals being written.\n\t\tflags&MOVE_NOMONSTERS will not impact on non-bsp entities.\n\t\tflags&MOVE_MISSILE will impact with increased size.\n\t\tflags&MOVE_HITMODEL will impact upon model meshes, instead of their bounding boxes.\n\t\tflags&MOVE_TRIGGERS will also stop on triggers\n\t\tflags&MOVE_EVERYTHING will stop if it hits anything, even non-solid entities.\n\t\tflags&MOVE_LAGGED will backdate entity positions for the purposes of this builtin according to the indicated player ent's latency, to provide lag compensation. */\n\n#endif\n#ifdef SSQC\nentity() checkclient = #17; /*\n\t\tReturns one of the player entities. The returned player will change periodically. */\n\n#endif\n#if defined(CSQC) || defined(SSQC)\nentity(entity start, .string fld, string match) find = #18; /*\n\t\tScan for the next entity with a given field set to the given 'match' value. start should be either world, or the previous entity that was found. Returns world on failure/if there are no more.\n\t\tIf you have many many entities then you may find that hashtables will give more performance (but requires extra upkeep). */\n\n#endif\nentity*(.__variant fld, __variant match, int type=EV_STRING, __out int count) find_list = #0:find_list; /*\n\t\tScan for the next entity with a given field set to the given 'match' value. start should be either world, or the previous entity that was found. Returns world on failure/if there are no more.\n\t\tIf you have many many entities then you may find that hashtables will give more performance (but requires extra upkeep). */\n\n#if defined(CSQC) || defined(SSQC)\nstring(string s) precache_sound = #19; /*\n\t\tPrecaches a sound, making it known to clients and loading it from disk. This builtin (strongly) should be called during spawn functions. This builtin must be called for the sound before the sound builtin is called, or it might not even be heard. */\n\nstring(string s) precache_model = #20; /*\n\t\tPrecaches a model, making it known to clients and loading it from disk if it has a .bsp extension. This builtin (strongly) should be called during spawn functions. This must be called for each model name before setmodel may use that model name.\n\t\tModelindicies precached in SSQC will always be positive. CSQC precaches will be negative if they are not also on the server. */\n\n#endif\n#ifdef SSQC\nvoid(entity client, string s, optional string s2, optional string s3, optional string s4, optional string s5, optional string s6, optional string s7) stuffcmd = #21; /*\n\t\tSends a console command (or cvar) to the client, where it will be executed. Different clients support different commands. Do NOT forget the final \\n.\n\t\tThis builtin is generally considered evil. */\n\nvoid(entity client, float flags, string s, optional string s2, optional string s3, optional string s4, optional string s5, optional string s6) stuffcmdflags = #0:stuffcmdflags; /* Part of FTE_QC_STUFFCMDFLAGS\n\t\tSends a console command (or cvar) to the client, where it will be executed. Different clients support different commands. Do NOT forget the final \\n.\n\t\tThis (just as evil) variant allows specifying some flags too. See the STUFFCMD_* constants. */\n\n#endif\n#if defined(CSQC) || defined(SSQC)\nentity(vector org, float rad, optional .entity chainfield) findradius = #22; /*\n\t\tFinds all entities within a distance of the 'org' specified. One entity is returned directly, while other entities are returned via that entity's .chain field. Use findradius_list for an updated alternative without reenterancy issues. */\n\nentity*(vector org, float rad, __out int foundcount, int sort=0) findradius_list = #0:findradius_list; /*\n\t\tFinds all entities linked with a bbox within a distance of the 'org' specified, returning the list as a temp-array (world signifies the end). Unlike findradius, sv_gameplayfix_blowupfallenzombies is ignored (use FL_FINDABLE_NONSOLID instead), while sv_gameplayfix_findradiusdistancetobox and dpcompat_findradiusarealinks are force-enabled. The resulting buffer will automatically be cleaned up by the engine and does not need to be freed. */\n\n#endif\n#if defined(NQSSQC)\nvoid(string s, optional string s2, optional string s3, optional string s4, optional string s5, optional string s6, optional string s7, optional string s8) bprint = #23; /*\n\t\tNQ: Concatenates all arguments, and prints the messsage on the console of all connected clients. */\n\n#endif\n#if defined(QWSSQC)\nvoid(float msglvl, string s, optional string s2, optional string s3, optional string s4, optional string s5, optional string s6, optional string s7) bprint = #23; /*\n\t\tQW: Concatenates all string arguments, and prints the messsage on the console of only all clients who's 'msg' infokey is set lower or equal to the supplied 'msglvl' argument. */\n\n#endif\n#if defined(NQSSQC)\nvoid(entity client, string s, optional string s2, optional string s3, optional string s4, optional string s5, optional string s6, optional string s7) sprint = #24; /*\n\t\tNQ: Concatenates all string arguments, and prints the messsage on the named client's console */\n\n#endif\n#if defined(QWSSQC)\nvoid(entity client, float msglvl, string s, optional string s2, optional string s3, optional string s4, optional string s5, optional string s6) sprint = #24; /*\n\t\tQW: Concatenates all string arguments, and prints the messsage on the named client's console, but only if that client's 'msg' infokey is set lower or equal to the supplied 'msglvl' argument. */\n\n#endif\n#if defined(CSQC) || defined(NQSSQC)\nvoid(string s, ...) dprint = #25; /*\n\t\tNQ: Prints the given message on the server's console, but only if the developer cvar is set. Arguments will be concatenated into a single message. */\n\n#endif\n#if defined(CSQC) || defined(QWSSQC)\nvoid(string s, ...) dprint = #25; /*\n\t\tQW: Unconditionally prints the given message on the server's console.  Arguments will be concatenated into a single message. */\n\n#endif\n#if defined(CSQC) || defined(SSQC)\nstring(float val) ftos = #26; /*\n\t\tReturns a tempstring containing a representation of the given float. Precision depends upon engine. */\n\nstring(vector val) vtos = #27; /*\n\t\tReturns a tempstring containing a representation of the given vector. Precision depends upon engine. */\n\nvoid() coredump = #28; /*\n\t\tWrites out a coredump. This contains stack, globals, and field info for all ents. This can be handy for debugging. */\n\nvoid() traceon = #29; /*\n\t\tEnables tracing. This may be spammy, slow, and stuff. Set debugger 1 in order to use fte's qc debugger. */\n\nvoid() traceoff = #30; /*\n\t\tDisables tracing again. */\n\nvoid(entity e) eprint = #31; /*\n\t\tDebugging builtin that prints all fields of the given entity to the console. */\n\nfloat(float yaw, float dist, optional float settraceglobals) walkmove = #32; /*\n\t\tAttempt to walk the entity at a given angle for a given distance.\n\t\tif settraceglobals is set, the trace_* globals will be set, showing the results of the movement.\n\t\tThis function will trigger touch events. */\n\nfloat() droptofloor = #34; /*\n\t\tInstantly moves the entity downwards until it hits the ground. If the entity is in solid or would need to drop more than 'pr_droptofloorunits' quake units, its position will be considered invalid and the builtin will abort, returning FALSE, otherwise TRUE. */\n\nvoid(float lightstyle, string stylestring, optional vector rgb) lightstyle = #35; /*\n\t\tSpecifies an auto-animating string that specifies the light intensity for entities using that lightstyle.\n\t\ta is off, z is fully lit. Should be lower case only.\n\t\trgb will recolour all lights using that lightstyle. */\n\nfloat(float) rint = #36; /*\n\t\tRounds the given float up or down to the closest integeral value. X.5 rounds away from 0 */\n\nfloat(float) floor = #37; /*\n\t\tRounds the given float downwards, even when negative. */\n\nfloat(float) ceil = #38; /*\n\t\tRounds the given float upwards, even when negative. */\n\nfloat(entity ent) checkbottom = #40; /*\n\t\tExpensive checks to ensure that the entity is actually sitting on something solid, returns true if it is. */\n\nfloat(vector pos) pointcontents = #41; /*\n\t\tChecks the given point to see what is there. Returns one of the SOLID_* constants. Just because a spot is empty does not mean that the player can stand there due to the size of the player - use tracebox for such tests. */\n\nfloat(float) fabs = #43; /*\n\t\tRemoves the sign of the float, making it positive if it is negative. */\n\n#endif\n#ifdef SSQC\nvector(entity player, float missilespeed) aim = #44; /*\n\t\tReturns a tweaked copy of the v_forward vector (must be set! ie: makevectors(player.v_angle) ). This is important for keyboard users (that don't want to have to look up/down the whole time), as well as joystick users (who's aim is otherwise annoyingly imprecise). Only the upwards component of the result will differ from the value of v_forward. The builtin will select the enemy closest to the crosshair within the angle of acos(sv_aim). */\n\n#endif\n#if defined(CSQC) || defined(SSQC)\nfloat(string) cvar = #45; /*\n\t\tReturns the numeric value of the named cvar */\n\nvoid(string, ...) localcmd = #46; /*\n\t\tAdds the string to the console command queue. Commands will not be executed immediately, but rather at the start of the following frame. */\n\nentity(entity) nextent = #47; /*\n\t\tReturns the following entity. Skips over removed entities. Returns world when passed the last valid entity. */\n\nvoid(vector pos, vector dir, float colour, float count) particle = #48; /*\n\t\tSpawn 'count' particles around 'pos' moving in the direction 'dir', with a palette colour index between 'colour' and 'colour+8'. */\n\n#define ChangeYaw changeyaw\nvoid() changeyaw = #49; /*\n\t\tChanges the self.angles_y field towards self.ideal_yaw by up to self.yaw_speed. */\n\nvector(vector fwd, optional vector up) vectoangles = #51; /*\n\t\tReturns the angles (+x=UP) required to orient an entity to look in the given direction. The 'up' argument is required if you wish to set a roll angle, otherwise it will be limited to just monster-style turning. */\n\n#endif\n#ifdef SSQC\nvoid(float to, float val) WriteByte = #52; /*\n\t\tWrites a single byte into a network message buffer. Typically you will find a more correct alternative to writing arbitary data. 'to' should be one of the MSG_* constants. MSG_ONE must have msg_entity set first. */\n\nvoid(float to, float val) WriteChar = #53; /*\n\t\tWrites a signed value between -128 and 127. */\n\nvoid(float to, float val) WriteShort = #54; /*\n\t\tWrites a signed value between -32768 and 32767. As an exception, values up to 65535 will not trigger warnings (but readshort will read the result as negative!) */\n\nvoid(float to, float val) WriteLong = #55; /*\n\t\tWrites a signed 32bit integer. Note that the input argument being of float type limits the resulting integer to a mere 24 consecutive bits of validity. Use WriteInt if you want to write an entire 32bit int without data loss. */\n\nvoid(float to, float val) WriteCoord = #56; /*\n\t\tWrites a single value suitable for a map coordinate axis. The precision is not strictly specified but is assumed to be of at least 13.3 fixed-point precision (ie: +/-4k with 1/8th precision). */\n\nvoid(float to, float val) WriteAngle = #57; /*\n\t\tWrites a single value suitable for an angle axis. The precision is not strictly specified but is assumed to be 8bit, giving 256 notches instead of the assumed 360 range passed in. */\n\nvoid(float to, string val) WriteString = #58; /*\n\t\tWrites a variable-length null terminated string. There are length limits. The codepage is not translated, so be sure that client+server agree on whether utf-8 is being used or not (or just stick to ascii+markup). */\n\nvoid(float to, entity val) WriteEntity = #59; /*\n\t\tWrites the index of the specified entity (the network data size is not specified). This can be read clientside using the readentitynum builtin, with caveats. */\n\n#endif\n#if defined(CSQC) || defined(SSQC)\nfloat(float angle) sin = #60; /* Part of DP_QC_SINCOSSQRTPOW\n\t\tForgive me father, for I have trigonometry homework. */\n\nfloat(float angle) cos = #61; /* Part of DP_QC_SINCOSSQRTPOW*/\nfloat(float value) sqrt = #62; /* Part of DP_QC_SINCOSSQRTPOW*/\n#endif\n#ifdef SSQC\nfloat(float a, float n) modulo = #0:modulo;\n#endif\n#if defined(CSQC) || defined(SSQC)\nvoid(entity ent) changepitch = #63; /* Part of DP_QC_CHANGEPITCH*/\nvoid(entity ent, entity ignore) tracetoss = #64;\nstring(entity ent) etos = #65; /* Part of DP_QC_ETOS*/\nvoid(float step) movetogoal = #67; /*\n\t\tRuns lots and lots of fancy logic in order to try to step the entity the specified distance towards its goalentity. */\n\nstring(string s) precache_file = #68; /*\n\t\tThis builtin does nothing. It was used only as a hint for pak generation. */\n\nvoid(entity e) makestatic = #69; /*\n\t\tSends a copy of the entity's renderable fields to all clients, and REMOVES the entity, preventing further changes. This means it will be unmutable and non-solid. */\n\n#endif\n#ifdef SSQC\nvoid(string mapname, optional string newmapstartspot) changelevel = #70; /*\n\t\tAttempts to change the map to the named map. If 'newmapstartspot' is specified, the state of the current map will be preserved, and the argument will be passed to the next map in the 'startspot' global, and the next map will be loaded from archived state if it was previously visited. If not specified, all archived map states will be purged. */\n\n#endif\n#if defined(CSQC) || defined(SSQC)\nvoid(string cvarname, string valuetoset) cvar_set = #72; /*\n\t\tInstantly sets a cvar to the given string value. Warning: the resulting string includes apostrophies surrounding the result. You may wish to use sprintf instead. */\n\n#endif\n#ifdef SSQC\nvoid(entity ent, string text, optional string text2, optional string text3, optional string text4, optional string text5, optional string text6, optional string text7) centerprint = #73;\n#endif\n#if defined(CSQC) || defined(SSQC)\nvoid (vector pos, string samp, float vol, float atten) ambientsound = #74;\nstring(string str) precache_model2 = #75;\nstring(string str) precache_sound2 = #76;\nstring(string str) precache_file2 = #77;\n#endif\n#ifdef SSQC\nvoid(entity player) setspawnparms = #78;\nvoid(entity killer, entity killee) logfrag = #79; /* Part of QW_ENGINE*/\n#endif\n#if defined(CSQC) || defined(SSQC)\nstring(entity e, string key) infokey = #80; /* Part of FTE_QC_INFOKEY, QW_ENGINE\n\t\tIf e is world, returns the field 'key' from either the serverinfo or the localinfo. If e is a player, returns the value of 'key' from the player's userinfo string. There are a few special exceptions, like 'ip' which is not technically part of the userinfo. */\n\n#endif\n#ifdef SSQC\nfloat(entity e, string key) infokeyf = #0:infokeyf; /*\n\t\tIdentical to regular infokey, except returns a float. */\n\n#endif\n#if defined(CSQC) || defined(SSQC)\nfloat(string) stof = #81; /* Part of FRIK_FILE, FTE_QC_INFOKEY, FTE_STRINGS, QW_ENGINE, ZQ_QC_STRINGS*/\n#endif\n#ifdef SSQC\n#define unicast(pl,reli) do{msg_entity = pl; multicast('0 0 0', reli?MULITCAST_ONE_R:MULTICAST_ONE);}while(0)\nvoid(vector where, float set) multicast = #82; /* Part of FTE_QC_MULTICAST\n\t\tOnce the MSG_MULTICAST network message buffer has been filled with data, this builtin is used to dispatch it to the given target, filtering by pvs for reduced network bandwidth. */\n\nDEP void(entity to, string str) redirectcmd = #101; /* Part of ??MVDSV_BUILTINS\n\t\tExecutes a single console command, and sends the text generated by it to the specified player. The command will be executed at the end of the frame once QC is no longer running - you may wish to pre/postfix it with 'echo'. */\n\n#endif\n#if defined(CSQC) || defined(SSQC)\nstring(float style, optional __out vector rgb) getlightstyle = #0:getlightstyle; /*\n\t\tObtains the light style string for the given style. */\n\nvector(float style) getlightstylergb = #0:getlightstylergb; /*\n\t\tObtains the current rgb value of the specified light style. In csqc, this is correct with regard to the current frame, while ssqc gives no guarentees about time and ignores client cvars. Note: use getlight if you want the actual light value at a point. */\n\n#endif\n#ifdef SSQC\nvoid(float style, float val, optional vector rgb) lightstylestatic = #5; /*\n\t\tSets the lightstyle to an explicit numerical level. From Hexen2. */\n\n#endif\n#if defined(CSQC) || defined(SSQC)\nvoid(vector start, vector mins, vector maxs, vector end, float nomonsters, entity ent) tracebox = #90; /* Part of DP_QC_TRACEBOX\n\t\tExactly like traceline, but a box instead of a uselessly thin point. Acceptable sizes are limited by bsp format, q1bsp has strict acceptable size values. */\n\nvector() randomvec = #91; /* Part of DP_QC_RANDOMVEC\n\t\tReturns a vector with random values. Each axis is independantly a value between -1 and 1 inclusive. */\n\nvector(vector org) getlight = #92;\nfloat(string cvarname, string defaultvalue) registercvar = #93; /* Part of DP_REGISTERCVAR\n\t\tCreates a new cvar on the fly. If it does not already exist, it will be given the specified value. If it does exist, this is a no-op.\n\t\tThis builtin has the limitation that it does not apply to configs or commandlines. Such configs will need to use the set or seta command causing this builtin to be a noop.\n\t\tIn engines that support it, you will generally find the autocvar feature easier and more efficient to use. */\n\nfloat(float a, float b, ...) min = #94; /* Part of DP_QC_MINMAXBOUND\n\t\tReturns the lowest value of its arguments. */\n\nfloat(float a, float b, ...) max = #95; /* Part of DP_QC_MINMAXBOUND\n\t\tReturns the highest value of its arguments. */\n\nfloat(float minimum, float val, float maximum) bound = #96; /* Part of DP_QC_MINMAXBOUND\n\t\tReturns val, unless minimum is higher, or maximum is less. */\n\nfloat(float value, float exp) pow = #97; /* Part of DP_QC_SINCOSSQRTPOW*/\n#endif\nfloat(float v, optional float base) logarithm = #0:logarithm; /*\n\t\tDetermines the logarithm of the input value according to the specified base. This can be used to calculate how much something was shifted by. */\n\n#if defined(CSQC) || defined(SSQC)\n#define findentity findfloat\nentity(entity start, .__variant fld, __variant match) findfloat = #98; /* Part of DP_QC_FINDFLOAT\n\t\tEquivelent to the find builtin, but instead of comparing strings contents, this builtin compares the raw values. This builtin requires multiple calls in order to scan all entities - set start to the previous call's return value.\n\t\tworld is returned when there are no more entities. */\n\nfloat(string extname) checkextension = #99; /*\n\t\tChecks for an extension by its name (eg: checkextension(\"FRIK_FILE\") says that its okay to go ahead and use strcat).\n\t\tUse cvar(\"pr_checkextension\") to see if this builtin exists. */\n\n#endif\nfloat(__variant funcref) checkbuiltin = #0:checkbuiltin; /*\n\t\tChecks to see if the specified builtin is supported/mapped. This is intended as a way to check for #0 functions, allowing for simple single-builtin functions. Warning, if two different engines map different builtins to the same number, then this function will not tell you which will be called, only that it won't crash (the exception being #0, which are remapped as available). */\n\n#ifdef SSQC\nfloat(string builtinname) builtin_find = #100; /*\n\t\tLooks to see if the named builtin is valid, and returns the builtin number it exists at. */\n\n#endif\n#if defined(CSQC) || defined(SSQC)\nfloat(float value) anglemod = #102;\n#endif\n#ifdef SSQC\nDEP_CSQC void(string slot, string picname, float x, float y, float zone, optional entity player) showpic = #104; /* Part of TEI_SHOWLMP2*/\nDEP_CSQC void(string slot, optional entity player) hidepic = #105; /* Part of TEI_SHOWLMP2*/\nDEP_CSQC void(string slot, float x, float y, float zone, optional entity player) movepic = #106; /* Part of TEI_SHOWLMP2*/\nDEP_CSQC void(string slot, string picname, optional entity player) changepic = #107; /* Part of TEI_SHOWLMP2*/\n#endif\n#if defined(CSQC) || defined(SSQC)\nfilestream(string filename, float mode, optional float mmapminsize) fopen = #110; /* Part of FRIK_FILE\n\t\tOpens a file, typically prefixed with \"data/\", for either read or write access. */\n\nvoid(filestream fhandle) fclose = #111; /* Part of FRIK_FILE*/\nstring(filestream fhandle) fgets = #112; /* Part of FRIK_FILE\n\t\tReads a single line out of the file. The new line character is not returned as part of the string. Returns the null string on EOF (use if not(string) to easily test for this, which distinguishes it from the empty string which is returned if the line being read is blank */\n\nvoid(filestream fhandle, string s, optional string s2, optional string s3, optional string s4, optional string s5, optional string s6, optional string s7) fputs = #113; /* Part of FRIK_FILE\n\t\tWrites the given string(s) into the file. For compatibility with fgets, you should ensure that the string is terminated with a \\n - this will not otherwise be done for you. It is up to the engine whether dos or unix line endings are actually written. */\n\n#endif\nint(filestream fhandle, void *ptr, int size) fread = #0:fread; /* Part of FTE_QC_FILE_BINARY\n\t\tReads binary data out of the file. Returns truncated lengths if the read exceeds the length of the file. */\n\nint(filestream fhandle, void *ptr, int size) fwrite = #0:fwrite; /* Part of FTE_QC_FILE_BINARY\n\t\tWrites binary data out of the file. */\n\n#define ftell fseek //c compat\nint(filestream fhandle, optional int newoffset) fseek = #0:fseek; /* Part of FTE_QC_FILE_BINARY\n\t\tChanges the current position of the file, if specified. Returns prior position, in bytes. */\n\nint(filestream fhandle, optional int newsize) fsize = #0:fsize; /* Part of FTE_QC_FILE_BINARY\n\t\tReports the total size of the file, in bytes. Can also be used to truncate/extend the file */\n\n#if defined(CSQC) || defined(SSQC)\nfloat(string s) strlen = #114; /* Part of FRIK_FILE, FTE_STRINGS, ZQ_QC_STRINGS*/\nstring(string s1, optional string s2, optional string s3, optional string s4, optional string s5, optional string s6, optional string s7, optional string s8) strcat = #115; /* Part of FRIK_FILE, FTE_STRINGS, ZQ_QC_STRINGS*/\nstring(string s, float start, float length) substring = #116; /* Part of FRIK_FILE, FTE_STRINGS, ZQ_QC_STRINGS*/\nvector(string s) stov = #117; /* Part of FRIK_FILE, FTE_STRINGS, ZQ_QC_STRINGS*/\nFTEDEP(\"Redundant\") string(string s, ...) strzone = #118; /* Part of FRIK_FILE, FTE_STRINGS, ZQ_QC_STRINGS\n\t\tCreate a semi-permanent copy of a string that only becomes invalid once strunzone is called on the string (instead of when the engine assumes your string has left scope). This builtin has become redundant in FTEQW due to the FTE_QC_PERSISTENTTEMPSTRINGS extension and is now functionally identical to strcat for compatibility with old engines+mods. */\n\nFTEDEP(\"Redundant\") void(string s) strunzone = #119; /* Part of FRIK_FILE, FTE_STRINGS, ZQ_QC_STRINGS\n\t\tDestroys a string that was allocated by strunzone. Further references to the string MAY crash the game. In FTE, this function became redundant and now does nothing. */\n\n#endif\nvoid*(int bytes) createbuffer = #0:createbuffer; /*\n\t\tReturns a temporary buffer that can be written to / read from. The buffer will be garbage collected and thus cannot be explicitly freed. Tempstrings and buffer references must not be stored into the buffer as the garbage collector will not scan these. */\n\n#ifdef SSQC\nvoid(string cvar, float val) cvar_setf = #176;\n#endif\n#if defined(CSQC) || defined(SSQC)\nvoid(string soundname, optional float channel, optional float volume) localsound = #177; /*\n\t\tPlays a sound... locally... probably best not to call this from ssqc. Also disables reverb. */\n\n#endif\n#ifdef SSQC\nfloat(string soundname, float queryonly) getsoundindex = #0:getsoundindex; /*\n\t\tProvides a way to query if a sound is already precached or not. The return value can also be checked for <=255 to see if it'll work over any network protocol. The sound index can also be used for writebyte hacks, but this is discouraged - use SOUNDFLAG_UNICAST instead. */\n\n#endif\n#if defined(CSQC) || defined(SSQC)\nfloat(string modelname, optional float queryonly) getmodelindex = #200; /*\n\t\tActs as an alternative to precache_model(foo);setmodel(bar, foo); return bar.modelindex;\n\t\tIf queryonly is set and the model was not previously precached, the builtin will return 0 without needlessly precaching the model. */\n\n__variant(float prnum, string funcname, ...) externcall = #201; /* Part of FTE_MULTIPROGS\n\t\tDirectly call a function in a different/same progs by its name.\n\t\tprnum=0 is the 'default' or 'main' progs.\n\t\tprnum=-1 means current progs.\n\t\tprnum=-2 will scan through the active progs and will use the first it finds. */\n\nfloat(string progsname) addprogs = #202; /* Part of FTE_MULTIPROGS\n\t\tLoads an additional .dat file into the current qcvm. The returned handle can be used with any of the externcall/externset/externvalue builtins.\n\t\tThere are cvars that allow progs to be loaded automatically. */\n\n__variant(float prnum, string varname) externvalue = #203; /* Part of FTE_MULTIPROGS\n\t\tReads a global in the named progs by the name of that global.\n\t\tprnum=0 is the 'default' or 'main' progs.\n\t\tprnum=-1 means current progs.\n\t\tprnum=-2 will scan through the active progs and will use the first it finds. */\n\nvoid(float prnum, __variant newval, string varname) externset = #204; /* Part of FTE_MULTIPROGS\n\t\tSets a global in the named progs by name.\n\t\tprnum=0 is the 'default' or 'main' progs.\n\t\tprnum=-1 means current progs.\n\t\tprnum=-2 will scan through the active progs and will use the first it finds. */\n\nvoid(entity portal, float state) openportal = #207; /*\n\t\tOpens or closes the portals associated with a door or some such on q2 or q3 maps. On Q2BSPs, the entity should be the 'func_areaportal' entity - its style field will say which portal to open. On Q3BSPs, the entity is the door itself, the portal will be determined by the two areas found from a preceding setorigin call. */\n\n#endif\n#ifdef SSQC\nfloat(float attributes, string effectname, ...) RegisterTempEnt = #208; /* Part of FTE_PEXT_CUSTOMTENTS*/\nvoid(float type, vector pos, ...) CustomTempEnt = #209; /* Part of FTE_PEXT_CUSTOMTENTS*/\nfloat(optional float sleeptime) fork = #210; /* Part of FTE_MULTITHREADED\n\t\tWhen called, this builtin simply returns. Twice.\n\t\tThe current 'thread' will return instantly with a return value of 0. The new 'thread' will return after sleeptime seconds with a return value of 1. See documentation for the 'sleep' builtin for limitations/requirements concerning the new thread. Note that QC should probably call abort in the new thread, as otherwise the function will return to the calling qc function twice also. */\n\n#endif\nvoid(optional __variant ret) abort = #211; /* Part of FTE_MULTITHREADED\n\t\tQC execution is aborted. Parent QC functions on the stack will be skipped, effectively this forces all QC functions to 'return ret' until execution returns to the engine. If ret is ommited, it is assumed to be 0. */\n\n#ifdef SSQC\nvoid(float sleeptime) sleep = #212; /* Part of FTE_MULTITHREADED\n\t\tSuspends the current QC execution thread for 'sleeptime' seconds.\n\t\tOther QC functions can and will be executed in the interim, including changing globals and field state (but not simultaneously).\n\t\tThe self and other globals will be restored when the thread wakes up (or set to world if they were removed since the thread started sleeping). Locals will be preserved, but will not be protected from remove calls.\n\t\tIf the engine is expecting the QC to return a value (even in the parent/root function), the value 0 shall be used instead of waiting for the qc to resume. */\n\nvoid(entity player, string key, string value) forceinfokey = #213; /* Part of FTE_FORCEINFOKEY\n\t\tDirectly changes a user's info without pinging off the client. Also allows explicitly setting * keys, including *spectator. Does not affect the user's config or other servers. */\n\nvoid(entity player, string key, void *data, int size) forceinfokeyblob = #0:forceinfokeyblob; /*\n\t\tDirectly changes a user's info without pinging off the client. Also allows explicitly setting * keys, including *spectator. Does not affect the user's config or other servers. */\n\n#endif\n#if defined(CSQC) || defined(SSQC)\nvoid(vector org, vector dmin, vector dmax, float colour, float effect, float count) particle2 = #215; /* Part of FTE_HEXEN2*/\nvoid(vector org, vector box, float colour, float effect, float count) particle3 = #216; /* Part of FTE_HEXEN2*/\nvoid(vector org, float radius, float colour, float effect, float count) particle4 = #217; /* Part of FTE_HEXEN2*/\nfloat(float number, float quantity) bitshift = #218; /* Part of EXT_BITSHIFT*/\nvoid(vector pos) te_lightningblood = #219; /* Part of FTE_TE_STANDARDEFFECTBUILTINS*/\n#endif\nfloat(string s1, string sub, optional float startidx) strstrofs = #221; /* Part of FTE_STRINGS\n\t\tReturns the 0-based offset of sub within the s1 string, or -1 if sub is not in s1.\n\t\tIf startidx is set, this builtin will ignore matches before that 0-based offset. */\n\nfloat(string str, float index) str2chr = #222; /* Part of FTE_STRINGS\n\t\tRetrieves the character value at offset 'index'. */\n\nstring(float chr, ...) chr2str = #223; /* Part of FTE_STRINGS\n\t\tThe input floats are considered character values, and are concatenated. */\n\nstring(float ccase, float redalpha, float redchars, string str, ...) strconv = #224; /* Part of FTE_STRINGS\n\t\tConverts quake chars in the input string amongst different representations.\n\t\tccase specifies the new case for letters.\n\t\t 0: not changed.\n\t\t 1: forced to lower case.\n\t\t 2: forced to upper case.\n\t\tredalpha and redchars switch between colour ranges.\n\t\t 0: no change.\n\t\t 1: Forced white.\n\t\t 2: Forced red.\n\t\t 3: Forced gold(low) (numbers only).\n\t\t 4: Forced gold (high) (numbers only).\n\t\t 5+6: Forced to white and red alternately.\n\t\tYou should not use this builtin in combination with UTF-8. */\n\nstring(float pad, string str1, ...) strpad = #225; /* Part of FTE_STRINGS\n\t\tPads the string with spaces, to ensure its a specific length (so long as a fixed-width font is used, anyway). If pad is negative, the spaces are added on the left. If positive the padding is on the right. */\n\ninfostring(infostring old, string key, string value) infoadd = #226; /* Part of FTE_STRINGS\n\t\tReturns a new tempstring infostring with the named value changed (or added if it was previously unspecified). Key and value may not contain the \\ character. */\n\nstring(infostring info, string key) infoget = #227; /* Part of FTE_STRINGS\n\t\tReads a named value from an infostring. The returned value is a tempstring */\n\n#define strcmp strncmp\nfloat(string s1, string s2, optional float len, optional float s1ofs, optional float s2ofs) strncmp = #228; /* Part of FTE_STRINGS\n\t\tCompares up to 'len' chars in the two strings. s1ofs allows you to treat s2 as a substring to compare against, or should be 0.\n\t\tReturns 0 if the two strings are equal, a negative value if s1 appears numerically lower, and positive if s1 appears numerically higher. */\n\nfloat(string s1, string s2) strcasecmp = #229; /* Part of FTE_STRINGS\n\t\tCompares the two strings without case sensitivity.\n\t\tReturns 0 if they are equal. The sign of the return value may be significant, but should not be depended upon. */\n\nfloat(string s1, string s2, float len, optional float s1ofs, optional float s2ofs) strncasecmp = #230; /* Part of FTE_STRINGS\n\t\tCompares up to 'len' chars in the two strings without case sensitivity. s1ofs allows you to treat s2 as a substring to compare against, or should be 0.\n\t\tReturns 0 if they are equal. The sign of the return value may be significant, but should not be depended upon. */\n\nstring(string s) strtrim = #0:strtrim; /*\n\t\tTrims the whitespace from the start+end of the string. */\n\n#if defined(CSQC) || defined(SSQC)\n__deprecated(\"Use strftime.\") void() calltimeofday = #231; /* Part of FTE_CALLTIMEOFDAY\n\t\tAsks the engine to instantly call the qc's 'timeofday' function, before returning. For compatibility with mvdsv.\n\t\ttimeofday should have the prototype: void(float secs, float mins, float hour, float day, float mon, float year, string strvalue)\n\t\tThe strftime builtin is more versatile and less weird. */\n\n#endif\n#ifdef SSQC\nvoid(float num, float type, .__variant fld) clientstat = #232; /*\n\t\tSpecifies what data to use in order to send various stats, in a client-specific way.\n\t\t'num' should be a value between 32 and 127, other values are reserved.\n\t\t'type' must be set to one of the EV_* constants, one of EV_FLOAT, EV_STRING, EV_INTEGER, EV_ENTITY.\n\t\tfld must be a reference to the field used, each player will be sent only their own copy of these fields. */\n\nvoid(float num, float type, string name) globalstat = #233; /*\n\t\tSpecifies what data to use in order to send various stats, in a non-client-specific way. num and type are as in clientstat, name however, is the name of the global to read in the form of a string (pass \"foo\"). */\n\nvoid(float num, float type, __variant *address) pointerstat = #0:pointerstat; /*\n\t\tSpecifies what data to use in order to send various stats, in a non-client-specific way. num and type are as in clientstat, address however, is the address of the variable you would like to use (pass &foo). */\n\nfloat(entity player) isbackbuffered = #234; /* Part of FTE_ISBACKBUFFERED\n\t\tReturns if the given player's network buffer will take multiple network frames in order to clear. If this builtin returns non-zero, you should delay or reduce the amount of reliable (and also unreliable) data that you are sending to that client. */\n\n#endif\n#if defined(CSQC) || defined(SSQC)\nvoid(vector angle) rotatevectorsbyangle = #235; /*\n\t\trotates the v_forward,v_right,v_up matrix by the specified angles. */\n\nvoid(vector fwd, vector right, vector up) rotatevectorsbyvectors = #236;\nfloat(float mdlindex, string skinname) skinforname = #237;\n#endif\n#if defined(CSQC) || defined(MENU)\nfloat(string shadername, optional string defaultshader, ...) shaderforname = #238; /* Part of FTE_FORCESHADER\n\t\tCaches the named shader and returns a handle to it.\n\t\tIf the shader could not be loaded from disk (missing file or ruleset_allow_shaders 0), it will be created from the 'defaultshader' string if specified, or a 'skin shader' default will be used.\n\t\tdefaultshader if not empty should include the outer {} that you would ordinarily find in a shader. */\n\n#endif\n#ifdef CSQC\nvoid(string shadername, string replacement, float timeoffset) remapshader = #0:remapshader; /*\n\t\tAll surfaces drawn with the specified shader will instead be drawn using the specified replacement shader. Shaders can be remapped to something else later by using the same source shadername. This is mostly useful for worldmodel surfaces (eg showing which team is currently winning). Entities should generally use setcustomskin or forceshader instead. Remaps will be forgotten on vid_reload, but can be reapplied via CSQC_RendererRestarted. */\n\n#endif\n#if defined(CSQC) || defined(SSQC)\nvoid(vector org, optional float count) te_bloodqw = #239; /* Part of FTE_TE_STANDARDEFFECTBUILTINS*/\n#endif\n#ifdef SSQC\nvoid(entity ent) te_muzzleflash = #0:te_muzzleflash;\n#endif\n#if defined(CSQC) || defined(SSQC)\nfloat(vector viewpos, entity entity) checkpvs = #240; /* Part of FTE_QC_CHECKPVS*/\n#endif\n#ifdef SSQC\nentity(string match, optional float matchnum) matchclientname = #241; /* Part of FTE_QC_MATCHCLIENTNAME*/\n#endif\nfloat(string destaddress, string content) sendpacket = #242; /* Part of FTE_QC_SENDPACKET\n\t\tSends a UDP packet to the specified destination. Note that the payload will be prefixed with four 255 bytes as a sort of security feature. */\n\n#ifdef CSQC\nvector(entity ent, float tagnum) rotatevectorsbytag = #244;\n#endif\n#if defined(CSQC) || defined(SSQC)\nfloat(float dividend, float divisor) mod = #245;\n#endif\n#ifdef SSQC\nfloat(optional string host, optional string user, optional string pass, optional string defaultdb, optional string driver) sqlconnect = #250; /* Part of FTE_SQL*/\nvoid(float serveridx) sqldisconnect = #251; /* Part of FTE_SQL*/\nfloat(float serveridx, void(float serveridx, float queryidx, float rows, float columns, float eof, float firstrow) callback, float querytype, string query) sqlopenquery = #252; /* Part of FTE_SQL*/\nvoid(float serveridx, float queryidx) sqlclosequery = #253; /* Part of FTE_SQL*/\nstring(float serveridx, float queryidx, float row, float column) sqlreadfield = #254; /* Part of FTE_SQL*/\nstring(float serveridx, optional float queryidx) sqlerror = #255; /* Part of FTE_SQL*/\nstring(float serveridx, string data) sqlescape = #256; /* Part of FTE_SQL*/\nstring(float serveridx) sqlversion = #257; /* Part of FTE_SQL*/\nfloat(float serveridx, float queryidx, float row, float column) sqlreadfloat = #258; /* Part of FTE_SQL*/\nint(float serveridx, float queryidx, float row, float column, __variant *ptr, int maxsize) sqlreadblob = #0:sqlreadblob;\nstring(float serveridx, __variant *ptr, int maxsize) sqlescapeblob = #0:sqlescapeblob;\n#endif\n#if defined(CSQC) || defined(SSQC)\nint(string) stoi = #259; /* Part of FTE_QC_INTCONV\n\t\tConverts the given string into a true integer. Base 8, 10, or 16 is determined based upon the format of the string. */\n\nstring(int) itos = #260; /* Part of FTE_QC_INTCONV\n\t\tConverts the passed true integer into a base10 string. */\n\nint(string) stoh = #261; /* Part of FTE_QC_INTCONV\n\t\tReads a base-16 string (with or without 0x prefix) as an integer. Bugs out if given a base 8 or base 10 string. :P */\n\nstring(int) htos = #262; /* Part of FTE_QC_INTCONV\n\t\tFormats an integer as a base16 string, with leading 0s and no prefix. Always returns 8 characters. */\n\n#endif\nint(float) ftoi = #0:ftoi; /* Part of FTE_QC_INTCONV\n\t\tConverts the given float into a true integer without depending on extended qcvm instructions. */\n\nfloat(int, optional float shift, float mask=24) itof = #0:itof; /* Part of FTE_QC_INTCONV\n\t\tConverts the given true integer into a float without depending on extended qcvm instructions. If shift and mask are specified then only specific parts of the integer will be cast to float. */\n\n#if defined(CSQC) || defined(SSQC)\nfloat(float modlindex, optional float useabstransforms) skel_create = #263; /* Part of FTE_CSQC_SKELETONOBJECTS\n\t\tAllocates a new uninitiaised skeletal object, with enough bone info to animate the given model.\n\t\teg: self.skeletonobject = skel_create(self.modelindex); */\n\nfloat(float skel, entity ent, float modelindex, float retainfrac, float firstbone, float lastbone, optional float addfrac) skel_build = #264; /* Part of FTE_CSQC_SKELETONOBJECTS\n\t\tAnimation data (according to the entity's frame info) is pulled from the specified model and blended into the specified skeletal object.\n\t\tIf retainfrac is set to 0 on the first call and 1 on the others, you can blend multiple animations together according to the addfrac value. The final weight should be 1. Other values will result in scaling and/or other weirdness. You can use firstbone and lastbone to update only part of the skeletal object, to allow legs to animate separately from torso, use 0 for both arguments to specify all, as bones are 1-based. */\n\ntypedef struct\n{\n\tint sourcemodelindex; /*frame data will be imported from this model, bones must be compatible*/\n\tint reserved;\n\tint firstbone;\n\tint lastbone;\n\tfloat prescale;\t/*0 destroys existing data, 1 retains it*/\n\tfloat scale[4];\t/*you'll need to do lerpfrac manually*/\n\tint animation[4];\n\tfloat animationtime[4];\n\t/*halflife models*/\n\tfloat subblend[2];\n\tfloat controllers[5];\n} skelblend_t;\nfloat(float skel, int numblends, skelblend_t *weights, int structsize) skel_build_ptr = #0:skel_build_ptr; /*\n\t\tLike skel_build, but slightly simpler. */\n\nfloat(float skel) skel_get_numbones = #265; /* Part of FTE_CSQC_SKELETONOBJECTS\n\t\tRetrives the number of bones in the model. The valid range is 1<=bone<=numbones. */\n\nstring(float skel, float bonenum) skel_get_bonename = #266; /* Part of FTE_CSQC_SKELETONOBJECTS\n\t\tRetrieves the name of the specified bone. Mostly only for debugging. */\n\nfloat(float skel, float bonenum) skel_get_boneparent = #267; /* Part of FTE_CSQC_SKELETONOBJECTS\n\t\tRetrieves which bone this bone's position is relative to. Bone 0 refers to the entity's position rather than an actual bone */\n\nfloat(float skel, string tagname) skel_find_bone = #268; /* Part of FTE_CSQC_SKELETONOBJECTS\n\t\tFinds a bone by its name, from the model that was used to create the skeletal object. */\n\nvector(float skel, float bonenum) skel_get_bonerel = #269; /* Part of FTE_CSQC_SKELETONOBJECTS\n\t\tGets the bone position and orientation relative to the bone's parent. Return value is the offset, and v_forward, v_right, v_up contain the orientation. */\n\nvector(float skel, float bonenum) skel_get_boneabs = #270; /* Part of FTE_CSQC_SKELETONOBJECTS\n\t\tGets the bone position and orientation relative to the entity. Return value is the offset, and v_forward, v_right, v_up contain the orientation.\n\t\tUse gettaginfo for world coord+orientation. */\n\nvoid(float skel, float bonenum, vector org, optional vector fwd, optional vector right, optional vector up) skel_set_bone = #271; /* Part of FTE_CSQC_SKELETONOBJECTS\n\t\tSets a bone position relative to its parent. If the orientation arguments are not specified, v_forward+v_right+v_up are used instead. */\n\nvoid(float skel, float bonenum, vector org, optional vector fwd, optional vector right, optional vector up) skel_premul_bone = #272; /* Part of FTE_CSQC_SKELETONOBJECTS\n\t\tTransforms a single bone by a matrix. You can use makevectors to generate a rotation matrix from an angle. */\n\nvoid(float skel, float startbone, float endbone, vector org, optional vector fwd, optional vector right, optional vector up) skel_premul_bones = #273; /* Part of FTE_CSQC_SKELETONOBJECTS\n\t\tTransforms an entire consecutive range of bones by a matrix. You can use makevectors to generate a rotation matrix from an angle, but you'll probably want to divide the angle by the number of bones. */\n\nvoid(float skel, float bonenum, vector org, optional vector fwd, optional vector right, optional vector up) skel_postmul_bone = #0:skel_postmul_bone; /*\n\t\tTransforms a single bone by a matrix. You can use makevectors to generate a rotation matrix from an angle. */\n\nvoid(float skeldst, float skelsrc, float startbone, float entbone) skel_copybones = #274; /* Part of FTE_CSQC_SKELETONOBJECTS\n\t\tCopy bone data from one skeleton directly into another. */\n\nvoid(float skel) skel_delete = #275; /* Part of FTE_CSQC_SKELETONOBJECTS\n\t\tDeletes a skeletal object. The actual delete is delayed, allowing the skeletal object to be deleted in an entity's predraw function yet still be valid by the time the addentity+renderscene builtins need it. Also uninstanciates any ragdoll currently in effect on the skeletal object. */\n\nfloat(float modidx, string framename) frameforname = #276; /* Part of FTE_CSQC_SKELETONOBJECTS\n\t\tLooks up a framegroup from a model by name, avoiding the need for hardcoding. Returns -1 on error. */\n\nfloat(float modidx, float framenum) frameduration = #277; /* Part of FTE_CSQC_SKELETONOBJECTS\n\t\tRetrieves the duration (in seconds) of the specified framegroup. */\n\nvoid(float modidx, float framenum, __inout float basetime, float targettime, void(float timestamp, int code, string data) callback) processmodelevents = #0:processmodelevents; /* Part of FTE_GFX_MODELEVENTS\n\t\tCalls a callback for each event that has been reached. Basetime is set to targettime. */\n\nfloat(float modidx, float framenum, __inout float basetime, float targettime, __out int code, __out string data) getnextmodelevent = #0:getnextmodelevent; /*\n\t\tReports the next event within a model's animation. Returns a boolean if an event was found between basetime and targettime. Writes to basetime,code,data arguments (if an event was found, basetime is set to the event's time, otherwise to targettime).\n\t\tWARNING: this builtin cannot deal with multiple events with the same timestamp (only the first will be reported). */\n\nfloat(float modidx, float framenum, int eventidx, __out float timestamp, __out int code, __out string data) getmodeleventidx = #0:getmodeleventidx; /*\n\t\tReports an indexed event within a model's animation. Writes to timestamp,code,data arguments on success. Returns false if the animation/event/model was out of range/invalid. Does not consider looping animations (retry from index 0 if it fails and you know that its a looping animation). This builtin is more annoying to use than getnextmodelevent, but can be made to deal with multiple events with the exact same timestamp. */\n\n#endif\n#define dotproduct(v1,v2) ((vector)(v1)*(vector)(v2))\nvector(vector v1, vector v2) crossproduct = #0:crossproduct; /* Part of FTE_QC_CROSSPRODUCT\n\t\tSmall helper function to calculate the crossproduct of two vectors. */\n\n#if defined(CSQC) || defined(SSQC)\nfloat(entity pusher, vector move, vector amove) pushmove = #0:pushmove;\n__variant(float action, optional vector pos, optional float radius, optional float quant, ...) terrain_edit = #278; /* Part of FTE_TERRAIN_MAP\n\t\tRealtime terrain editing. Actions are the TEREDIT_ constants. */\n\ntypedef struct\n{\n\tstring\tshadername;\n\tvector\tplanenormal;\n\tfloat\tplanedist;\n\tvector\tsdir;\n\tfloat\tsbias;\n\tvector\ttdir;\n\tfloat\ttbias;\n} brushface_t;\nint(float modelidx, int brushid, brushface_t *out_faces, int maxfaces, int *out_contents) brush_get = #0:brush_get; /* Part of FTE_RAW_MAP\n\t\tQueries a brush's information. You must pre-allocate the face array for the builtin to write to. Return value is the number of faces retrieved, 0 on error. */\n\nint(float modelidx, brushface_t *in_faces, int numfaces, int contents, optional int brushid) brush_create = #0:brush_create; /* Part of FTE_RAW_MAP\n\t\tInserts a new brush into the model. Return value is the new brush's id. */\n\nvoid(float modelidx, int brushid) brush_delete = #0:brush_delete; /* Part of FTE_RAW_MAP\n\t\tDestroys the specified brush. */\n\nfloat(float modelid, int brushid, int faceid, float selectedstate) brush_selected = #0:brush_selected; /* Part of FTE_RAW_MAP\n\t\tAllows you to easily set transient visual properties of a brush. returns old value. selectedstate=-1 changes nothing (called for its return value). */\n\nint(float modelid, int brushid, int faceid, vector *points, int maxpoints) brush_getfacepoints = #0:brush_getfacepoints; /* Part of FTE_RAW_MAP\n\t\tReturns the list of verticies surrounding the given face. If face is 0, returns the center of the brush (if space for 1 point) or the mins+maxs (if space for 2 points). */\n\nint(int faceid, brushface_t *in_faces, int numfaces, vector *points, int maxpoints) brush_calcfacepoints = #0:brush_calcfacepoints; /* Part of FTE_RAW_MAP\n\t\tDetermines the points of the specified face, if the specified brush were to actually be created. */\n\nint(float modelid, vector *planes, float *dists, int numplanes, int *out_brushes, int *out_faces, int maxresults) brush_findinvolume = #0:brush_findinvolume; /* Part of FTE_RAW_MAP\n\t\tAllows you to easily obtain a list of brushes+faces within the given bounding region. If out_faces is not null, the same brush might be listed twice. */\n\ntypedef struct\n{\n\tstring shadername;\n\tint contents;\n\tint cpwidth;\n\tint cpheight;\n\tint tesswidth;\n\tint tessheight;\n\tvector texinfo;/*scalex,y,rot*/\n} patchinfo_t;\ntypedef struct\n{\n\tvector xyz;\n\tvector rgb; float a;\n\tfloat s, t;\n} patchvert_t;\n#define patch_delete(modelidx,patchidx) brush_delete(modelidx,patchidx)\nint(float modelidx, int patchid, patchvert_t *out_controlverts, int maxcp, patchinfo_t *out_info) patch_getcp = #0:patch_getcp; /*\n\t\tQueries a patch's information. You must pre-allocate the face array for the builtin to write to. Return value is the total number of control verts that were retrieved, 0 on error. */\n\nint(float modelidx, int patchid, patchvert_t *out_verts, int maxverts, patchinfo_t *out_info) patch_getmesh = #0:patch_getmesh; /*\n\t\tQueries a patch's information. You must pre-allocate the face array for the builtin to write to. Return value is the total number of control verts that were retrieved, 0 on error. */\n\nint(float modelidx, int oldpatchid, patchvert_t *in_controlverts, patchinfo_t in_info) patch_create = #0:patch_create; /*\n\t\tInserts a new patch into the model. Return value is the new patch's id. */\nint(patchvert_t *in_controlverts, patchvert_t *out_renderverts, int maxout, patchinfo_t *inout_info) patch_evaluate = #0;\n\ntypedef struct\n{\n\tvector dest;\n\tint linkflags;\n} nodeslist_t;\nvoid(entity ent, vector dest, int denylinkflags, void(entity ent, vector dest, int numnodes, nodeslist_t *nodelist) callback) route_calculate = #0:route_calculate; /*\n\t\tBegin calculating a route. The callback function will be called once the route has finished being calculated. The route must be memfreed once it is no longer needed. The route must be followed in reverse order (ie: the first node that must be reached is at index numnodes-1). If no route is available then the callback will be called with no nodes. */\n\nvoid(optional entity ent, optional vector neworigin) touchtriggers = #279; /*\n\t\tTriggers a touch events between self and every SOLID_TRIGGER entity that it is in contact with. This should typically just be the triggers touch functions. Also optionally updates the origin of the moved entity. */\n\n#endif\n#ifdef SSQC\nvoid(float buf, float fl) WriteFloat = #280; /*\n\t\tWrites a full 32bit float without any data conversions at all, for full precision. */\n\nvoid(float buf, __double dbl) WriteDouble = #0:WriteDouble; /*\n\t\tWrites a full 64bit double-precision float without any data conversions at all, for excessive precision. */\n\nvoid(float buf, int fl) WriteInt = #0:WriteInt; /*\n\t\tWrites all 4 bytes of a 32bit integer without truncating to a float first before converting back to an int (unlike WriteLong does, but otherwise equivelent). */\n\nvoid(float buf, __int64 fl) WriteInt64 = #0:WriteInt64; /*\n\t\tWrites all 8 bytes of a 64bit integer. */\n\n#endif\n#if defined(CSQC) || defined(SSQC)\nfloat(entity skelent, string dollcmd, float animskel) skel_ragupdate = #281; /*\n\t\tUpdates the skeletal object attached to the entity according to its origin and other properties.\n\t\tif animskel is non-zero, the ragdoll will animate towards the bone state in the animskel skeletal object, otherwise they will pick up the model's base pose which may not give nice results.\n\t\tIf dollcmd is not set, the ragdoll will update (this should be done each frame).\n\t\tIf the doll is updated without having a valid doll, the model's default .doll will be instanciated.\n\t\tcommands:\n\t\t doll foo.doll : sets up the entity to use the named doll file\n\t\t dollstring TEXT : uses the doll file directly embedded within qc, with that extra prefix.\n\t\t cleardoll : uninstanciates the doll without destroying the skeletal object.\n\t\t animate 0.5 : specifies the strength of the ragdoll as a whole \n\t\t animatebody somebody 0.5 : specifies the strength of the ragdoll on a specific body (0 will disable ragdoll animations on that body).\n\t\t enablejoint somejoint 1 : enables (or disables) a joint. Disabling joints will allow the doll to shatter. */\n\nfloat*(float skel) skel_mmap = #282; /*\n\t\tMap the bones in VM memory. They can then be accessed via pointers. Each bone is 12 floats, the four vectors interleaved (sadly). */\n\nvoid(entity ent, float bonenum, vector org, optional vector angorfwd, optional vector right, optional vector up) skel_set_bone_world = #283; /*\n\t\tSets the world position of a bone within the given entity's attached skeletal object. The world position is dependant upon the owning entity's position. If no orientation argument is specified, v_forward+v_right+v_up are used for the orientation instead. If 1 is specified, it is understood as angles. If 3 are specified, they are the forawrd/right/up vectors to use. */\n\nstring(float modidx, float framenum) frametoname = #284;\nstring(float modidx, float skin) skintoname = #285;\nfloat(float resourcetype, float tryload, string resourcename) resourcestatus = #286; /*\n\t\tresourcetype must be one of the RESTYPE_ constants. Returns one of the RESSTATE_ constants. Tryload 0 is a query only. Tryload 1 will attempt to reload the content if it was flushed. */\n\n#endif\nhashtable(float tabsize, optional float defaulttype) hash_createtab = #287; /* Part of FTE_QC_HASHTABLES\n\t\tCreates a hash table object.\n\t\tThe tabsize argument is a performance hint and should generally be set to something similar to the number of entries expected, typically a power of two assumption. Too high simply wastes memory, too low results in extra string compares but no actual bugs.\n\t\tdefaulttype must be one of the EV_* values, if specified.\n\t\tThe hash table with index 0 is a game-persistant table and will NEVER be returned by this builtin (except as an error return). */\n\nvoid(hashtable table) hash_destroytab = #288; /* Part of FTE_QC_HASHTABLES\n\t\tDestroys a hash table object. */\n\nvoid(hashtable table, string name, __variant value, optional float typeandflags) hash_add = #289; /* Part of FTE_QC_HASHTABLES\n\t\tAdds the given key with the given value to the table.\n\t\tIf flags&HASH_REPLACE, the old value will be removed, otherwise if flags&HASH_ADD then a duplicate entry will be added with a second value (can be obtained via hash_get's index argument).\n\t\tThe type argument describes how the value should be stored in saved games, as well as providing constraints with the hash_get function. While you can claim that all variables are just vectors, being more precise can result in less issues with tempstrings or saved games - be sure to be explicit with EV_STRING where appropriate because tempstrings may be reclaimed before the get (especially with saved games or table 0). */\n\n__variant(hashtable table, string name, optional __variant deflt, optional float requiretype, optional float index) hash_get = #290; /* Part of FTE_QC_HASHTABLES\n\t\tLooks up the specified key name in the hash table. Returns deflt if the key was not found.\n\t\tIf requiretype is specified then the function will only consider entries of the matching type (allowing you to store both flags+strings under a single name without getting confused).\n\t\tIf index is specified then the function will ignore the first N entries with the same key (applicable only with entries added using HASH_ADD, not HASH_REPLACE), allowing you to store multiple entries. Keep querying higher indexes starting from 0 until it returns the deflt value.\n\t\tYou will usually need to cast the result of this function to a real datatype. */\n\n__variant(hashtable table, string name) hash_delete = #291; /* Part of FTE_QC_HASHTABLES\n\t\tremoves the named key. returns the value of the object that was destroyed, or 0 on error. */\n\nstring(hashtable table, float idx) hash_getkey = #292; /* Part of FTE_QC_HASHTABLES\n\t\tgets some random key name. add+delete can change return values of this, so don't blindly increment the key index if you're removing all. */\n\nfloat(string name) checkcommand = #294; /* Part of FTE_QC_CHECKCOMMAND\n\t\tChecks to see if the supplied name is a valid command, cvar, or alias. Returns 0 if it does not exist. */\n\nstring(string s) argescape = #295; /*\n\t\tMarks up a string so that it can be reliably tokenized as a single argument later. */\n\n#ifdef SSQC\nvoid(string dest, string from, string cmd, string info) clusterevent = #0:clusterevent; /*\n\t\tOnly functions in mapcluster mode. Sends an event to whichever server the named player is on. The destination server can then dispatch the event to the client or handle it itself via the SV_ParseClusterEvent entrypoint. If dest is empty, the event is broadcast to ALL servers. If the named player can't be found, the event will be returned to this server with the cmd prefixed with 'error:'. */\n\nstring(entity player, optional string newnode) clustertransfer = #0:clustertransfer; /*\n\t\tOnly functions in mapcluster mode. Initiate transfer of the player to a different node. Can take some time. If dest is specified, returns null on error. Otherwise returns the current/new target node (or null if not transferring). */\n\n#endif\n#if defined(CSQC) || defined(SSQC)\nfloat(float mdlidx) modelframecount = #0:modelframecount; /*\n\t\tRetrieves the number of frames in the specified model. */\n\n#endif\n#if defined(CSQC) || defined(MENU)\nvoid() clearscene = #300; /*\n\t\tForgets all rentities, polygons, and temporary dlights. Resets all view properties to their default values. */\n\n#endif\n#ifdef CSQC\nvoid(float mask) addentities = #301; /*\n\t\tWalks through all entities effectively doing this:\n\t\t if (ent.drawmask&mask){ if (!ent.predaw()) addentity(ent); }\n\t\tIf mask&MASK_DELTA, non-csqc entities, particles, and related effects will also be added to the rentity list.\n\t\t If mask&MASK_STDVIEWMODEL then the default view model will also be added. */\n\n#endif\n#if defined(CSQC) || defined(MENU)\nvoid(entity ent) addentity = #302; /*\n\t\tCopies the entity fields into a new rentity for later rendering via addscene. */\n\n#endif\n#ifdef CSQC\nvoid(entity ent) removeentity = #0:removeentity; /*\n\t\tUndoes all addentities added to the scene from the given entity, without removing ALL entities (useful for splitscreen/etc, readd modified versions as desired). */\n\ntypedef float vec2[2];\ntypedef float vec3[3];\ntypedef float vec4[4];\ntypedef struct trisoup_simple_vert_s {vec3 xyz;vec2 st;vec4 rgba;} trisoup_simple_vert_t;\nvoid(string texturename, int flags, struct trisoup_simple_vert_s *verts, int *indexes, int numindexes) addtrisoup_simple = #0:addtrisoup_simple; /*\n\t\tAdds the specified trisoup into the scene as additional geometry. This permits caching geometry to reduce builtin spam. Indexes are a triangle list (so eg quads will need 6 indicies to form two triangles). NOTE: this is not going to be a speedup over polygons if you're still generating lots of new data every frame. */\n\n#endif\n#if defined(CSQC) || defined(MENU)\n#define setviewprop setproperty\nfloat(float property, ...) setproperty = #303; /*\n\t\tAllows you to override default view properties like viewport, fov, and whether the engine hud will be drawn. Different VF_ values have slightly different arguments, some are vectors, some floats. */\n\nvoid() renderscene = #304; /*\n\t\tDraws all entities, polygons, and particles on the rentity list (which were added via addentities or addentity), using the various view properties set via setproperty. There is no ordering dependancy.\n\t\tThe scene must generally be cleared again before more entities are added, as entities will persist even over to the next frame.\n\t\tYou may call this builtin multiple times per frame, but should only be called from CSQC_UpdateView. */\n\n#endif\n#ifdef CSQC\nfloat(vector org, float radius, vector lightcolours, optional float style, optional string cubemapname, optional float pflags) dynamiclight_add = #305; /*\n\t\tAdds a temporary dlight, ready to be drawn via addscene. Cubemap orientation will be read from v_forward/v_right/v_up. */\n\n#endif\nvoid(string texturename, optional float flags, optional float is2d) R_BeginPolygon = #306; /*\n\t\tSpecifies the shader to use for the following polygons, along with optional flags.\n\t\tIf is2d, the polygon will be drawn as soon as the EndPolygon call is made, rather than waiting for renderscene. This allows complex 2d effects. */\n\nvoid(vector org, vector texcoords, vector rgb, float alpha) R_PolygonVertex = #307; /*\n\t\tSpecifies a polygon vertex with its various properties. */\n\nvoid() R_EndPolygon = #308; /*\n\t\tEnds the current polygon. At least 3 verticies must have been specified. You do not need to call beginpolygon again if you wish to draw another polygon with the same shader. */\n\n#ifdef CSQC\nvoid(float radius, vector texcoordbias) R_EndPolygonRibbon = #0:R_EndPolygonRibbon; /*\n\t\tEnds the current primitive and duplicates each vertex sideways into a ribbon. The texcoordbias will be added to each duplicated vertex allowing for regular 2d textures. At least 2 verticies must have been specified. You do not need to call beginpolygon again if you wish to draw another polygon with the same shader. */\n\n#endif\n#if defined(CSQC) || defined(MENU)\n#define getviewprop getproperty\n__variant(float property) getproperty = #309; /*\n\t\tRetrieve a currently-set (typically view) property, allowing you to read the current viewport or other things. Due to cheat protection, certain values may be unretrievable. */\n\n#endif\n#ifdef CSQC\nvector (vector v) unproject = #310; /*\n\t\tTransform a 2d screen-space point (with depth) into a 3d world-space point, according the various origin+angle+fov etc settings set via setproperty. */\n\nvector (vector v) project = #311; /*\n\t\tTransform a 3d world-space point into a 2d screen-space point, according the various origin+angle+fov etc settings set via setproperty. */\n\n#endif\n#if defined(CSQC) || defined(MENU)\nfloat(vector pos, vector size, float alignflags, string text) drawtextfield = #0:drawtextfield; /*\n\t\tDraws a multi-line block of text, including word wrapping and alignment. alignflags bits are RTLB, typically 3. Returns the total number of lines. */\n\n#endif\n#ifdef CSQC\nvoid(float width, vector pos1, vector pos2, vector rgb, float alpha, optional float drawflag) drawline = #315; /*\n\t\tDraws a 2d line between the two 2d points. */\n\nfloat(string name) iscachedpic = #316; /*\n\t\tChecks to see if the image is currently loaded. Engines might lie, or cache between maps. */\n\nstring(string name, optional float flags) precache_pic = #317; /*\n\t\tForces the engine to load the named image. Flags are a bitmask of the PRECACHE_PIC_* flags. */\n\n#endif\n#if defined(CSQC) || defined(MENU)\nvoid(string imagename, int width, int height, void *pixeldata, optional int datasize, optional int format) r_uploadimage = #0:r_uploadimage; /* Part of FTE_CSQC_RAWIMAGES\n\t\tUpdates a texture with the specified rgba data (uploading it to the gpu). Will be created if needed. If datasize is specified then the image is decoded (eg .ktx or .dds data) instead of being raw R8G8B8A data. You'll typically want shaderforname to also generate a shader to use the texture. */\n\nint*(string filename, __out int width, __out int height) r_readimage = #0:r_readimage; /* Part of FTE_CSQC_RAWIMAGES\n\t\tReads and decodes an image from disk, providing raw R8G8B8A8 pixel data. Should not be used for dds or ktx etc formats. Returns __NULL__ if the image could not be read for any reason. Use memfree to free the data once you're done with it. */\n\n#endif\n#ifdef CSQC\n#define draw_getimagesize drawgetimagesize\nvector(string picname) drawgetimagesize = #318; /*\n\t\tReturns the dimensions of the named image. Images specified with .lmp should give the original .lmp's dimensions even if texture replacements use a different resolution. WARNING: this function may be slow if used without or directly after its initial precache_pic. */\n\nvoid(string name) freepic = #319; /*\n\t\tTells the engine that the image is no longer needed. The image will appear to be new the next time its needed. */\n\nstring(string modelname, int frame, float frametime) spriteframe = #0:spriteframe; /*\n\t\tObtains a suitable shader name to draw a sprite's shader via drawpic/R_BeginPolygon/etc, instead of needing to create a scene. */\n\nfloat(vector position, float character, vector size, vector rgb, float alpha, optional float drawflag) drawcharacter = #320; /*\n\t\tDraw the given quake character at the given position.\n\t\tIf flag&4, the function will consider the char to be a unicode char instead (or display as a ? if outside the 32-127 range).\n\t\tsize should normally be something like '8 8 0'.\n\t\trgb should normally be '1 1 1'\n\t\talpha normally 1.\n\t\tSoftware engines may assume the named defaults.\n\t\tNote that ALL text may be rescaled on the X axis due to variable width fonts. The X axis may even be ignored completely. */\n\nfloat(vector position, string text, vector size, vector rgb, float alpha, optional float drawflag) drawrawstring = #321; /*\n\t\tDraws the specified string without using any markup at all, even in engines that support it.\n\t\tIf UTF-8 is globally enabled in the engine, then that encoding is used (without additional markup), otherwise it is raw quake chars.\n\t\tSoftware engines may assume a size of '8 8 0', rgb='1 1 1', alpha=1, flag&3=0, but it is not an error to draw out of the screen. */\n\nfloat(vector position, string pic, vector size, vector rgb, float alpha, optional float drawflag) drawpic = #322; /*\n\t\tDraws an shader within the given 2d screen box. Software engines may omit support for rgb+alpha, but must support rescaling, and must clip to the screen without crashing. */\n\nfloat(vector position, vector size, vector rgb, float alpha, optional float drawflag) drawfill = #323; /*\n\t\tDraws a solid block over the given 2d box, with given colour, alpha, and blend mode (specified via flags).\n\t\tflags&3=0 simple blend.\n\t\tflags&3=1 additive blend */\n\nvoid(float x, float y, float width, float height) drawsetcliparea = #324; /*\n\t\tSpecifies a 2d clipping region (aka: scissor test). 2d draw calls will all be clipped to this 2d box, the area outside will not be modified by any 2d draw call (even 2d polygons). */\n\nvoid(void) drawresetcliparea = #325; /*\n\t\tReverts the scissor/clip area to the whole screen. */\n\nfloat(vector position, string text, vector size, vector rgb, float alpha, float drawflag) drawstring = #326; /*\n\t\tDraws a string, interpreting markup and recolouring as appropriate. */\n\nfloat(string text, float usecolours, optional vector fontsize) stringwidth = #327; /*\n\t\tCalculates the width of the screen in virtual pixels. If usecolours is 1, markup that does not affect the string width will be ignored. Will always be decoded as UTF-8 if UTF-8 is globally enabled.\n\t\tIf the char size is not specified, '8 8 0' will be assumed. */\n\nvoid(vector pos, vector sz, string pic, vector srcpos, vector srcsz, vector rgb, float alpha, optional float drawflag) drawsubpic = #328; /*\n\t\tDraws a rescaled subsection of an image to the screen. */\n\n#endif\n#if defined(CSQC) || defined(MENU)\nvoid(vector pivot, vector mins, vector maxs, string pic, vector rgb, float alpha, float angle) drawrotpic = #0:drawrotpic; /*\n\t\tDraws an image rotating at the pivot. To rotate in the center, use mins+maxs of half the size with mins negated. Angle is in degrees. */\n\nvoid(vector pivot, vector mins, vector maxs, string pic, vector txmin, vector txsize, vector rgb, vector alphaandangles) drawrotsubpic = #0:drawrotsubpic; /*\n\t\tOvercomplicated draw function for over complicated people. Positions follow drawrotpic, while texture coords follow drawsubpic. Due to argument count limitations in builtins, the alpha value and angles are combined into separate fields of a vector (tip: use fteqcc's [alpha, angle] feature. */\n\n#endif\n#ifdef CSQC\n#define getstati_punf(stnum) (float)(__variant)getstati(stnum)\nint(float stnum) getstati = #330; /*\n\t\tRetrieves the full precision of a stat registered as EV_INTEGER. */\n\n#define getstatbits getstatf\nfloat(float stnum, optional float firstbit, optional float bitcount) getstatf = #331; /*\n\t\tRetrieves the numerical value of the given EV_FLOAT stat. If firstbit and bitcount are specified, then this builtin acts as getstati combined with itof, and which should be used for STAT_ITEMS (but not other stats). */\n\nstring(float stnum) getstats = #332; /*\n\t\tRetrieves the value of the given EV_STRING stat, as a tempstring.\n\t\tOlder engines may use 4 consecutive integer stats, with a limit of 15 chars (yes, really. 15.), but FTE Quake uses a separate namespace for string stats and has a much higher length limit. */\n\n__variant(float playernum, float statnum, float stattype) getplayerstat = #0:getplayerstat; /*\n\t\tRetrieves a specific player's stat, matching the type specified on the server. This builtin is primarily intended for mvd playback where ALL players are known. Return value matches the specified EV_ stattype. For EV_ENTITY, world will be returned if the entity is not in the pvs, use type-punning with EV_INTEGER to get the entity number if you just want to see if its set. STAT_ITEMS should be queried as an EV_INTEGER on account of runes and items2 being packed into the upper bits. */\n\nvoid(entity e, float mdlindex) setmodelindex = #333; /*\n\t\tSets a model by precache index instead of by name. Otherwise identical to setmodel. */\n\nstring(float mdlindex) modelnameforindex = #334; /*\n\t\tRetrieves the name of the model based upon a precache index. This can be used to reduce csqc network traffic by enabling model matching. */\n\n#endif\n#if defined(CSQC) || defined(SSQC)\nfloat(string effectname) particleeffectnum = #335; /* Part of DP_ENT_TRAILEFFECTNUM, FTE_SV_POINTPARTICLES\n\t\tPrecaches the named particle effect. If your effect name is of the form 'foo.bar' then particles/foo.cfg will be loaded by the client if foo.bar was not already defined.\n\t\tDifferent engines will have different particle systems, this specifies the QC API only. */\n\nvoid(float effectnum, entity ent, vector start, vector end) trailparticles = #336; /* Part of FTE_SV_POINTPARTICLES\n\t\tDraws the given effect between the two named points. If ent is not world, distances will be cached in the entity in order to avoid framerate dependancies. The entity is not otherwise used. */\n\nvoid(float effectnum, vector origin, optional vector dir, optional float count) pointparticles = #337; /* Part of FTE_SV_POINTPARTICLES\n\t\tSpawn a load of particles from the given effect at the given point traveling or aiming along the direction specified. The number of particles are scaled by the count argument.\n\t\tFor regular particles, the dir vector is multiplied by the 'veladd' property (while orgadd will push the particles along it). Decals will use it as a hint to align to the correct surface. In both cases, it should normally be a unit vector, but other lengths will still work. If it has length 0 then FTE will assume downwards. */\n\n#endif\n#ifdef CSQC\nvoid(string s, ...) cprint = #338; /*\n\t\tPrint into the center of the screen just as ssqc's centerprint would appear. */\n\n#endif\n#if defined(CSQC) || defined(SSQC)\nvoid(string s, ...) print = #339; /* Part of DP_SV_PRINT\n\t\tUnconditionally print on the local system's console, even in ssqc (doesn't care about the value of the developer cvar). */\n\n#endif\n#ifdef CSQC\nstring(float keynum) keynumtostring = #340; /*\n\t\tReturns a hunam-readable name for the given keycode, as a tempstring. */\n\n#endif\n#ifdef MENU\nDEP string(float keynum) keynumtostring_csqc = #340; /*\n\t\tReturns a hunam-readable name for the given keycode, as a tempstring. */\n\n#endif\n#ifdef CSQC\nfloat(string keyname) stringtokeynum = #341; /*\n\t\tLooks up the key name in the same way that the bind command would, returning the keycode for that key. */\n\n#endif\n#ifdef MENU\nDEP float(string keyname) stringtokeynum_csqc = #341; /*\n\t\tLooks up the key name in the same way that the bind command would, returning the keycode for that key. */\n\n#endif\n#if defined(CSQC) || defined(MENU)\nstring(float keynum) getkeybind = #342; /*\n\t\tReturns the current binding for the given key (returning only the command executed when no modifiers are pressed). */\n\nvoid(float usecursor, optional string cursorimage, optional vector hotspot, optional float scale) setcursormode = #343; /*\n\t\tPass TRUE if you want the engine to release the mouse cursor (absolute input events + touchscreen mode). Pass FALSE if you want the engine to grab the cursor (relative input events + standard looking). If the image name is specified, the engine will use that image for a cursor (use an empty string to clear it again), in a way that will not conflict with the console. Images specified this way will be hardware accelerated, if supported by the platform/port. */\n\nfloat(float effective) getcursormode = #0:getcursormode; /*\n\t\tReports the cursor mode this module previously attempted to use. If 'effective' is true, reports the cursor mode currently active (if was overriden by a different module which has precidence, for instance, or if there is only a touchscreen and no mouse). */\n\n#endif\n#ifdef CSQC\nvector() getmousepos = #344; /*\n\t\tNasty convoluted DP extension. Typically returns deltas instead of positions. Use CSQC_InputEvent instead for such things in csqc mods. */\n\n#endif\n#if defined(CSQC) || defined(MENU)\nvoid(vector newpos) setmousepos = #0:setmousepos; /*\n\t\tWarps the mouse cursor to the given location. Should normally only be done following setcursormode(TRUE,...). The warp MAY be visible through *_InputEvent, but normally be seen as an IE_ABSMOUSE event anyway. Not all systems support cursor warping (or even cursors), so this is a hint only and you should not depend upon it. */\n\n#endif\n#ifdef CSQC\nfloat(float inputsequencenum) getinputstate = #345; /*\n\t\tLooks up an input frame from the log, setting the input_* globals accordingly.\n\t\tThe sequence number range used for prediction should normally be servercommandframe < sequence <= clientcommandframe.\n\t\tThe sequence equal to clientcommandframe will change between input frames. */\n\nvoid(float sens) setsensitivityscaler = #346; /*\n\t\tTemporarily scales the player's mouse sensitivity based upon something like zoom, avoiding potential cvar saving and thus corruption. */\n\n#endif\n#if defined(CSQC) || defined(SSQC)\nvoid(entity ent) runstandardplayerphysics = #347; /*\n\t\tPerform the engine's standard player movement prediction upon the given entity using the input_* globals to describe movement. */\n\n#endif\n#ifdef CSQC\nstring(float playernum, string keyname) getplayerkeyvalue = #348; /*\n\t\tLook up a player's userinfo, to discover things like their name, topcolor, bottomcolor, skin, team, *ver.\n\t\tAlso includes scoreboard info like frags, ping, pl, userid, entertime, as well as voipspeaking and voiploudness. */\n\nfloat(float playernum, string keyname, optional float assumevalue) getplayerkeyfloat = #0:getplayerkeyfloat; /*\n\t\tCheaper version of getplayerkeyvalue that avoids the need for so many tempstrings. */\n\nint(float playernum, string keyname, optional void *outptr, int size) getplayerkeyblob = #0:getplayerkeyblob; /*\n\t\tObtains a copy of the full data blob. Will write up to size bytes but return the full size. Does not null terminate (but memalloc(ret+1) will, if you want to cast the buffer to a string), and the blob may contain embedded nulls. Ignores all special keys, returning only what is actually there. */\n\n#endif\n#if defined(CSQC) || defined(MENU)\nvoid(float seat, string keyname, string newvalue) setlocaluserinfo = #0:setlocaluserinfo; /*\n\t\tChange a userinfo key for the specified local player seat, equivelent to the setinfo console command. The server will normally forward the setting to other clients. */\n\nstring(float seat, string keyname) getlocaluserinfo = #0:getlocaluserinfo; /*\n\t\tReads a local userinfo key for the specified local player seat. This is not quite the same as getplayerkeyvalue, due to latency and possible serverside filtering. */\n\nvoid(float seat, string keyname, void *outptr, int size) setlocaluserinfoblob = #0:setlocaluserinfoblob; /*\n\t\tSets the userinfo key to a blob that may contain nulls etc. Keys with a leading underscore will be visible to only the server (for user-specific binary settings). */\n\nint(float seat, string keyname, void *outptr, int maxsize) getlocaluserinfoblob = #0:getlocaluserinfoblob; /*\n\t\tObtains a copy of the full data blob. Will write up to size bytes but return the full size. Does not null terminate (but memalloc(ret+1) will, if you want to cast the buffer to a string), and the blob may contain embedded nulls. Ignores all special keys, returning only what is actually there. */\n\n#endif\n#ifdef SSQC\nint(string keyname, optional void *outptr, int size) getlocalinfo = #0:getlocalinfo; /*\n\t\tObtains a copy of a data blob (with spaces) from the server's private localinfo. Will write up to size bytes and return the actual size. Does not null terminate (but memalloc(ret+1) will, if you want to cast the buffer to a string), and the blob may contain embedded nulls. Ignores all special keys, returning only what is actually there. */\n\nvoid(string keyname, optional void *outptr, int size) setlocalinfo = #0:setlocalinfo; /*\n\t\tChanges the server's private localinfo. This data will be available for the following map, and will *usually* reload with saved games. */\n\n#endif\n#if defined(CSQC) || defined(MENU)\nfloat() isdemo = #349; /*\n\t\tReturns if the client is currently playing a demo or not. Returns 2 when playing an mvd (where other player's stats can be queried, or the pov can be changed freely). */\n\n#endif\n#ifdef CSQC\nfloat() isserver = #350; /*\n\t\tReturns non-zero whenever the local console can directly affect the server (ie: listen servers or single-player). Compat note: DP returns 0 for single-player. */\n\nvoid(vector origin, vector forward, vector right, vector up, optional float reverbtype) SetListener = #351; /*\n\t\tSets the position of the view, as far as the audio subsystem is concerned. This should be called once per CSQC_UpdateView as it will otherwise revert to default. For reverbtype, see setup_reverb or treat as 'underwater'. */\n\ntypedef struct {\n\tfloat flDensity;\n\tfloat flDiffusion;\n\tfloat flGain;\n\tfloat flGainHF;\n\tfloat flGainLF;\n\tfloat flDecayTime;\n\tfloat flDecayHFRatio;\n\tfloat flDecayLFRatio;\n\tfloat flReflectionsGain;\n\tfloat flReflectionsDelay;\n\tvector flReflectionsPan;\n\tfloat flLateReverbGain;\n\tfloat flLateReverbDelay;\n\tvector flLateReverbPan;\n\tfloat flEchoTime;\n\tfloat flEchoDepth;\n\tfloat flModulationTime;\n\tfloat flModulationDepth;\n\tfloat flAirAbsorptionGainHF;\n\tfloat flHFReference;\n\tfloat flLFReference;\n\tfloat flRoomRolloffFactor;\n\tint   iDecayHFLimit;\n} reverbinfo_t;\nvoid(float reverbslot, reverbinfo_t *reverbinfo, int sizeofreverinfo_t) setup_reverb = #0:setup_reverb; /* Part of FTE_CSQC_REVERB\n\t\tReconfigures a reverb slot for weird effects. Slot 0 is reserved for no effects. Slot 1 is reserved for underwater effects. Reserved slots will be reinitialised on snd_restart, but can otherwise be changed. These reverb slots can be activated with SetListener. Note that reverb will currently only work when using OpenAL. */\n\n#endif\nvoid(string cmdname) registercommand = #352; /*\n\t\tRegister the given console command, for easy console use.\n\t\tConsole commands that are later used will invoke CSQC_ConsoleCommand/m_consolecommand/ConsoleCmd according to module. */\n\nfloat(entity ent) wasfreed = #353; /*\n\t\tQuickly check to see if the entity is currently free. This function is only valid during the two-second non-reuse window, after that it may give bad results. Try one second to make it more robust. */\n\n#if defined(CSQC) || defined(SSQC)\nstring(string key) serverkey = #354; /*\n\t\tLook up a key in the server's public serverinfo string. If the key contains binary data then it will be truncated at the first null. */\n\nfloat(string key, optional float assumevalue) serverkeyfloat = #0:serverkeyfloat; /*\n\t\tVersion of serverkey that returns the value as a float (which avoids tempstrings). */\n\nint(string key, optional void *ptr, int maxsize) serverkeyblob = #0:serverkeyblob; /*\n\t\tVersion of serverkey that returns data as a blob (ie: binary data that may contain nulls). Returns the full blob size, even if truncated (pass maxsize=0 to query required storage). */\n\n#endif\n#ifdef SSQC\nvoid(string key, void *ptr, optional int size) setserverkey = #0:setserverkey; /*\n\t\tChanges the server's serverinfo. */\n\n#endif\n#ifdef CSQC\nstring(optional string resetstring) getentitytoken = #355; /*\n\t\tGrab the next token in the map's entity lump.\n\t\tIf resetstring is not specified, the next token will be returned with no other sideeffects.\n\t\tIf empty, will reset from the map before returning the first token, probably {.\n\t\tIf not empty, will tokenize from that string instead.\n\t\tAlways returns tempstrings. */\n\n#endif\n#if defined(CSQC) || defined(MENU)\nfloat(string s) findfont = #356; /* Part of DP_GFX_FONTS\n\t\tLooks up a named font slot. Matches the actual font name as a last resort. */\n\nfloat(string fontname, string fontmaps, string sizes, float slot, optional float fix_scale, optional float fix_voffset) loadfont = #357; /* Part of DP_GFX_FONTS\n\t\ttoo convoluted for me to even try to explain correct usage. Try drawfont = loadfont(\"\", \"cour\", \"16\", -1, 0, 0); to switch to the courier font (optimised for 16 virtual pixels high) ('cour' requires mscorefonts installed in linux). Additionally you can add \"outline=1\" as an extra token in the sizes string, to have more readable outlined fonts. */\n\n#endif\n#ifdef CSQC\nvoid(string evname, string evargs, ...) sendevent = #359; /*\n\t\tInvoke CSEv_evname_evargs in ssqc. evargs must be a string of initials refering to the types of the arguments to pass. v=vector, e=entity(.entnum field is sent), f=float, i=int. 6 arguments max - you can get more if you pack your floats into vectors. */\n\nfloat() readbyte = #360; /*\n\t\tReads an unsigned 8-bit value, pair with WriteByte. */\n\nfloat() readchar = #361; /*\n\t\tReads a signed 8-bit value. Paired with WriteChar. */\n\nfloat() readshort = #362; /*\n\t\tReads a signed 16-bit value. Paired with WriteShort. */\n\nfloat() readlong = #363; /*\n\t\tReads a signed 32-bit value. Paired with WriteLong or WriteInt. */\n\nfloat() readcoord = #364; /*\n\t\tReads a value matching the unspecified precision written ONLY by WriteCoord. */\n\nfloat() readangle = #365; /*\n\t\tReads a value matching the unspecified precision written ONLY by WriteAngle. */\n\nstring() readstring = #366; /*\n\t\tReads a null-terminated string. */\n\nfloat() readfloat = #367; /*\n\t\tReads a float without any truncation nor conversions. Data MUST have originally been written with WriteFloat. */\n\n__double() readdouble = #0:readdouble; /*\n\t\tReads a double-precision float without any truncation nor conversions. Data MUST have originally been written with WriteDouble. */\n\nint() readint = #0:readint; /*\n\t\tReads a 32bit int without any conversions to float, otherwise interchangable with readlong. */\n\n__int64() readint64 = #0:readint64; /*\n\t\tReads a 64bit int. Paired with WriteInt64. */\n\nfloat() readentitynum = #368; /*\n\t\tReads the serverside index of an entity, paired with WriteEntity. There may be nothing else known about the entity yet, so the result typically needs to be saved as-is and re-looked up each frame. This can be done via getentity(NUM, GE_*) for non-csqc ents, or findentity(world,entnum,NUM) - both of which can fail due to latency. */\n\nfloat(string modelname, float(float isnew) updatecallback, float flags) deltalisten = #371; /*\n\t\tSpecifies a per-modelindex callback to listen for engine-networking entity updates. Such entities are automatically interpolated by the engine (unless flags specifies not to).\n\t\tThe various standard entity fields will be overwritten each frame before the updatecallback function is called. */\n\nfloat(vector org, float radius, vector rgb) dynamiclight_spawnstatic = #0:dynamiclight_spawnstatic; /*\n\t\tCreates a static persistent light at the given position with the specified colour. Additional properties must be set via dynamiclight_set. */\n\n__variant(float lno, float fld) dynamiclight_get = #372; /*\n\t\tRetrieves a property from the given dynamic/rt light. Return type depends upon the light field requested. */\n\nvoid(float lno, float fld, __variant value) dynamiclight_set = #373; /*\n\t\tChanges a property on the given dynamic/rt light. Value type depends upon the light field to be changed. */\n\nstring(float efnum, float body) particleeffectquery = #374; /*\n\t\tRetrieves either the name or the body of the effect with the given number. The effect body is regenerated from internal state, and can be changed before being reapplied via the localcmd builtin. */\n\nvoid(string shadername, vector origin, vector up, vector side, vector rgb, float alpha) adddecal = #375; /*\n\t\tAdds a temporary clipped decal shader to the scene, centered at the given point with given orientation. Will be drawn by the next renderscene call, and freed by the next clearscene call. */\n\n#endif\n#if defined(CSQC) || defined(MENU)\nvoid(entity e, string skinfilename, optional string skindata) setcustomskin = #376; /* Part of FTE_QC_CUSTOMSKINS\n\t\tSets an entity's skin overrides to a new skin object. Releases the entities old skin (refcounted). */\n\n#endif\n#ifdef CSQC\nfloat(string skinfilename, optional string skindata) loadcustomskin = #377; /*\n\t\tCreates a new skin object and returns it. These are custom per-entity surface->shader lookups. The skinfilename/data should be in .skin format:\n\t\tsurfacename,shadername - makes the named surface use the named shader (legacy format for compat with q3)\n\t\treplace \"surfacename\" \"shadername\" - non-legacy equivalent.\n\t\tqwskin \"foo\" - use an unmodified quakeworld player skin (including crop+repalette rules)\n\t\tq1lower 0xff0000 - specify an override for the entity's lower colour, in this case to red\n\t\tq1upper 0x0000ff - specify an override for the entity's lower colour, in this case to blue\n\t\tcompose \"surfacename\" \"shader\" \"imagename@x,y:w,h$s,t,s2,t2?r,g,b,a\" - compose a skin texture from multiple images.\n\t\t  The texture is determined to be sufficient to hold the first named image, additional images can be named as extra tokens on the same line.\n\t\t  Use a + at the end of the line to continue reading image tokens from the next line also, the named shader must use 'map $diffuse' to read the composed texture (compatible with the defaultskin shader). Must be matched with a releasecustomskin call later, and is pointless without applycustomskin. */\n\nvoid(entity e, float skinobj) applycustomskin = #378; /*\n\t\tUpdates the entity's custom skin (refcounted). */\n\nvoid(float skinobj) releasecustomskin = #379; /*\n\t\tLets the engine know that the skin will no longer be needed. Thanks to refcounting any ents with the skin already applied will retain their skin until later changed. It is valid to destroy a skin just after applying it to an ent in the same function that it was created in, as the skin will only be destroyed once its refcount rops to 0. */\n\n#endif\n__variant*(int size) memalloc = #384; /* Part of FTE_MEMALLOC\n\t\tAllocate an arbitary block of memory */\n\nvoid(__variant *ptr) memfree = #385; /* Part of FTE_MEMALLOC\n\t\tFrees a block of memory that was allocated with memfree */\n\nvoid(__variant *dst, __variant *src, int size) memcpy = #386; /* Part of FTE_MEMALLOC\n\t\tCopys memory from one location to another */\n\nvoid(__variant *dst, int val, int size) memfill8 = #387; /* Part of FTE_MEMALLOC\n\t\tSets an entire block of memory to a specified value. Pretty much always 0. */\n\n__variant(__variant *dst, float ofs) memgetval = #388; /*\n\t\tLooks up the 32bit value stored at a pointer-with-offset. */\n\nvoid(__variant *dst, float ofs, __variant val) memsetval = #389; /*\n\t\tChanges the 32bit value stored at the specified pointer-with-offset. */\n\n__variant*(__variant *base, float ofs) memptradd = #390; /*\n\t\tPerform some pointer maths. Woo. */\n\nfloat(string s) memstrsize = #0:memstrsize; /*\n\t\tstrlen, except ignores utf-8 */\n\n#if defined(CSQC) || defined(MENU)\nstring(string conname, string field, optional string newvalue) con_getset = #391; /* Part of FTE_CSQC_ALTCONSOLES\n\t\tReads or sets a property from a console object. The old value is returned. Iterrate through consoles with the 'next' field. Valid properties: \ttitle, name, next, unseen, markup, forceutf8, close, clear, hidden, linecount */\n\nvoid(string conname, string messagefmt, ...) con_printf = #392; /* Part of FTE_CSQC_ALTCONSOLES\n\t\tPrints onto a named console. */\n\nvoid(string conname, vector pos, vector size, float fontsize) con_draw = #393; /* Part of FTE_CSQC_ALTCONSOLES\n\t\tDraws the named console. */\n\nfloat(string conname, float inevtype, float parama, float paramb, float paramc) con_input = #394; /* Part of FTE_CSQC_ALTCONSOLES\n\t\tForwards input events to the named console. Mouse updates should be absolute only. */\n\nvoid(string newcaption) setwindowcaption = #0:setwindowcaption; /* Part of FTE_CSQC_WINDOWCAPTION\n\t\tReplaces the title of the game window, as seen when task switching or just running in windowed mode. */\n\nfloat() cvars_haveunsaved = #0:cvars_haveunsaved; /*\n\t\tReturns true if any archived cvar has an unsaved value. */\n\n#endif\nfloat(entity e, float nowreadonly) entityprotection = #0:entityprotection; /*\n\t\tChanges the protection on the specified entity to protect it from further edits from QC. The return value is the previous setting. Note that this can be used to unprotect the world, but doing so long term is not advised as you will no longer be able to detect invalid entity references. Also, world is not networked, so results might not be seen by clients (or in other words, world.avelocity_y=64 is a bad idea). */\n\n#ifdef CSQC\nstring(vector pos) getlocationname = #0:getlocationname; /*\n\t\tLooks up the specified position in the current map's .loc file and reports the nearest marked name. */\n\n#endif\n#ifdef MENU\nvoid(int cliptype) clipboard_get = #0:clipboard_get; /*\n\t\tAttempts to query the system clipboard. Any pasted text will be returned via Menu_InputEvent */\n\n#endif\n#if defined(CSQC) || defined(MENU)\nvoid(int cliptype, string text) clipboard_set = #0:clipboard_set; /*\n\t\tChanges the system clipboard to the specified text. */\n\n#endif\n#if defined(CSQC) || defined(SSQC)\nentity(entity from, optional entity to) copyentity = #400; /* Part of DP_QC_COPYENTITY\n\t\tCopies all fields from one entity to another. */\n\n#endif\n#ifdef SSQC\n__deprecated(\"No RGB support.\") void(entity ent, float colours) setcolors = #401; /*\n\t\tChanges a player's colours. The bits 0-3 are the lower/trouser colour, bits 4-7 are the upper/shirt colours. */\n\n#endif\n#if defined(CSQC) || defined(SSQC)\nentity(.string field, string match, optional .entity chainfield) findchain = #402; /* Part of DP_QC_FINDCHAIN*/\nentity(.float fld, float match, optional .entity chainfield) findchainfloat = #403; /* Part of DP_QC_FINDCHAINFLOAT*/\nvoid(vector org, string modelname, float startframe, float endframe, float framerate) effect = #404; /* Part of DP_SV_EFFECT\n\t\tSpawns a self-animating sprite */\n\nvoid(vector org, vector dir, float count) te_blood = #405; /* Part of DP_TE_BLOOD*/\nvoid(vector mincorner, vector maxcorner, float explosionspeed, float howmany) te_bloodshower = #406; /* Part of _DP_TE_BLOODSHOWER*/\nvoid(vector org, vector color) te_explosionrgb = #407; /* Part of DP_TE_EXPLOSIONRGB*/\nvoid(vector mincorner, vector maxcorner, vector vel, float howmany, float color, float gravityflag, float randomveljitter) te_particlecube = #408; /* Part of DP_TE_PARTICLECUBE*/\nvoid(vector mincorner, vector maxcorner, vector vel, float howmany, float color) te_particlerain = #409; /* Part of DP_TE_PARTICLERAIN*/\nvoid(vector mincorner, vector maxcorner, vector vel, float howmany, float color) te_particlesnow = #410; /* Part of DP_TE_PARTICLESNOW*/\nvoid(vector org, vector vel, float howmany) te_spark = #411; /* Part of DP_TE_SPARK*/\nvoid(vector org) te_gunshotquad = #412; /* Part of _DP_TE_QUADEFFECTS1*/\nvoid(vector org) te_spikequad = #413; /* Part of _DP_TE_QUADEFFECTS1*/\nvoid(vector org) te_superspikequad = #414; /* Part of _DP_TE_QUADEFFECTS1*/\nvoid(vector org) te_explosionquad = #415; /* Part of _DP_TE_QUADEFFECTS1*/\nvoid(vector org) te_smallflash = #416; /* Part of DP_TE_SMALLFLASH*/\nvoid(vector org, float radius, float lifetime, vector color) te_customflash = #417; /* Part of DP_TE_CUSTOMFLASH*/\nvoid(vector org, optional float count) te_gunshot = #418; /* Part of DP_TE_STANDARDEFFECTBUILTINS, FTE_TE_STANDARDEFFECTBUILTINS*/\nvoid(vector org) te_spike = #419; /* Part of DP_TE_STANDARDEFFECTBUILTINS, FTE_TE_STANDARDEFFECTBUILTINS*/\nvoid(vector org) te_superspike = #420; /* Part of DP_TE_STANDARDEFFECTBUILTINS, FTE_TE_STANDARDEFFECTBUILTINS*/\nvoid(vector org) te_explosion = #421; /* Part of DP_TE_STANDARDEFFECTBUILTINS, FTE_TE_STANDARDEFFECTBUILTINS*/\nvoid(vector org) te_tarexplosion = #422; /* Part of DP_TE_STANDARDEFFECTBUILTINS, FTE_TE_STANDARDEFFECTBUILTINS*/\nvoid(vector org) te_wizspike = #423; /* Part of DP_TE_STANDARDEFFECTBUILTINS, FTE_TE_STANDARDEFFECTBUILTINS*/\nvoid(vector org) te_knightspike = #424; /* Part of DP_TE_STANDARDEFFECTBUILTINS, FTE_TE_STANDARDEFFECTBUILTINS*/\nvoid(vector org) te_lavasplash = #425; /* Part of DP_TE_STANDARDEFFECTBUILTINS, FTE_TE_STANDARDEFFECTBUILTINS*/\nvoid(vector org) te_teleport = #426; /* Part of DP_TE_STANDARDEFFECTBUILTINS, FTE_TE_STANDARDEFFECTBUILTINS*/\nvoid(vector org, float color, float colorlength) te_explosion2 = #427; /* Part of DP_TE_STANDARDEFFECTBUILTINS*/\nvoid(entity own, vector start, vector end) te_lightning1 = #428; /* Part of DP_TE_STANDARDEFFECTBUILTINS, FTE_TE_STANDARDEFFECTBUILTINS*/\nvoid(entity own, vector start, vector end) te_lightning2 = #429; /* Part of DP_TE_STANDARDEFFECTBUILTINS, FTE_TE_STANDARDEFFECTBUILTINS*/\nvoid(entity own, vector start, vector end) te_lightning3 = #430; /* Part of DP_TE_STANDARDEFFECTBUILTINS, FTE_TE_STANDARDEFFECTBUILTINS*/\nvoid(entity own, vector start, vector end) te_beam = #431; /* Part of DP_TE_STANDARDEFFECTBUILTINS*/\nvoid(vector dir) vectorvectors = #432; /* Part of DP_QC_VECTORVECTORS*/\nvoid(vector org) te_plasmaburn = #433; /* Part of _DP_TE_PLASMABURN*/\nfloat(entity e, float s) getsurfacenumpoints = #434; /* Part of DP_QC_GETSURFACE*/\nvector(entity e, float s, float n) getsurfacepoint = #435; /* Part of DP_QC_GETSURFACE*/\nvector(entity e, float s) getsurfacenormal = #436; /* Part of DP_QC_GETSURFACE*/\nstring(entity e, float s) getsurfacetexture = #437; /* Part of DP_QC_GETSURFACE*/\nfloat(entity e, vector p) getsurfacenearpoint = #438; /* Part of DP_QC_GETSURFACE*/\nvector(entity e, float s, vector p) getsurfaceclippedpoint = #439; /* Part of DP_QC_GETSURFACE*/\n#endif\n#ifdef MENU\nstrbuf() buf_create = #440; /* Part of DP_QC_STRINGBUFFERS*/\nvoid(strbuf bufhandle) buf_del = #441; /* Part of DP_QC_STRINGBUFFERS*/\nfloat(strbuf bufhandle) buf_getsize = #442; /* Part of DP_QC_STRINGBUFFERS*/\nvoid(strbuf bufhandle_from, float bufhandle_to) buf_copy = #443; /* Part of DP_QC_STRINGBUFFERS*/\nvoid(strbuf bufhandle, float sortprefixlen, float backward) buf_sort = #444; /* Part of DP_QC_STRINGBUFFERS*/\nstring(strbuf bufhandle, string glue) buf_implode = #445; /* Part of DP_QC_STRINGBUFFERS*/\nstring(strbuf bufhandle, float string_index) bufstr_get = #446; /* Part of DP_QC_STRINGBUFFERS*/\nvoid(strbuf bufhandle, float string_index, string str) bufstr_set = #447; /* Part of DP_QC_STRINGBUFFERS*/\nfloat(strbuf bufhandle, string str, float ordered) bufstr_add = #448; /* Part of DP_QC_STRINGBUFFERS*/\nvoid(strbuf bufhandle, float string_index) bufstr_free = #449; /* Part of DP_QC_STRINGBUFFERS*/\nfloat(string name) iscachedpic = #451;\nstring(string name, optional float flags) precache_pic = #452;\nfloat(vector position, float character, vector scale, vector rgb, float alpha, optional float flag) drawcharacter = #454;\nfloat(vector position, string text, vector scale, vector rgb, float alpha, optional float flag) drawrawstring = #455;\nfloat(vector position, string pic, vector size, vector rgb, float alpha, optional float flag) drawpic = #456;\nfloat(vector position, vector size, vector rgb, float alpha, optional float flag) drawfill = #457;\nvoid(float x, float y, float width, float height) drawsetcliparea = #458;\nvoid(void) drawresetcliparea = #459;\nvector(string picname) drawgetimagesize = #460;\nvoid(float width, vector pos1, vector pos2) drawline = #466;\nfloat(vector position, string text, vector scale, vector rgb, float alpha, float flag) drawstring = #467;\nfloat(string text, float usecolours, optional vector fontsize) stringwidth = #468;\nvoid(vector pos, vector sz, string pic, vector srcpos, vector srcsz, vector rgb, float alpha, float flag) drawsubpic = #469;\n#endif\n#ifdef SSQC\nvoid(entity e, string s) clientcommand = #440; /* Part of KRIMZON_SV_PARSECLIENTCOMMAND*/\n#endif\n#if defined(CSQC) || defined(SSQC)\nfloat(string s) tokenize = #441; /* Part of KRIMZON_SV_PARSECLIENTCOMMAND*/\nstring(float n) argv = #442; /* Part of KRIMZON_SV_PARSECLIENTCOMMAND*/\nvoid(entity e, entity tagentity, string tagname) setattachment = #443; /* Part of DP_GFX_QUAKE3MODELTAGS*/\nsearchhandle(string pattern, enumflags:float{SB_CASEINSENSITIVE=1<<0,SB_FULLPACKAGEPATH=1<<1,SB_ALLOWDUPES=1<<2,SB_FORCESEARCH=1<<3,SB_MULTISEARCH=1<<4} flags, float quiet, optional string filterpackage) search_begin = #444; /* Part of DP_QC_FS_SEARCH, DP_QC_FS_SEARCH_PACKFILE\n\t\tinitiate a filesystem scan based upon filenames. Be sure to call search_end on the returned handle. SB_FULLPACKAGEPATH interprets the filterpackage arg as a full package path to avoid gamedir ambiguity, equivelent to whichpack's WP_FULLPACKAGEPATH flag. SB_ALLOWDUPES allows returning multiple entries with the same name (but different package, useful with search_fopen). SB_FORCESEARCH requires use of the filterpackage and SB_FULLPACKAGEPATH flag, initiating searches from gamedirs/packages which are not currently active. */\n\nvoid(searchhandle handle) search_end = #445; /* Part of DP_QC_FS_SEARCH, DP_QC_FS_SEARCH_PACKFILE*/\nfloat(searchhandle handle) search_getsize = #446; /* Part of DP_QC_FS_SEARCH, DP_QC_FS_SEARCH_PACKFILE\n\t\tRetrieves the number of files that were found. */\n\nstring(searchhandle handle, float num) search_getfilename = #447; /* Part of DP_QC_FS_SEARCH, DP_QC_FS_SEARCH_PACKFILE\n\t\tRetrieves name of one of the files that was found by the initial search. */\n\n#endif\nfloat(searchhandle handle, float num) search_getfilesize = #0:search_getfilesize; /* Part of FTE_QC_FS_SEARCH_SIZEMTIME\n\t\tRetrieves the size of one of the files that was found by the initial search. */\n\nstring(searchhandle handle, float num) search_getfilemtime = #0:search_getfilemtime; /* Part of FTE_QC_FS_SEARCH_SIZEMTIME\n\t\tRetrieves modification time of one of the files. */\n\nstring(searchhandle handle, float num) search_getpackagename = #0:search_getpackagename; /*\n\t\tRetrieves the name of the package containing the file. Search with SB_FULLPACKAGEPATH to see gamedir/package info */\n\nfilestream(searchhandle handle, float num) search_fopen = #0:search_fopen; /*\n\t\tOpens the file directly, without getting confused about entries from other packages. Read access only. */\n\n#if defined(CSQC) || defined(SSQC)\nstring(string cvarname) cvar_string = #448; /* Part of DP_QC_CVAR_STRING*/\nentity(entity start, .float fld, float match) findflags = #449; /* Part of DP_QC_FINDFLAGS*/\nentity(.float fld, float match, optional .entity chainfield) findchainflags = #450; /* Part of DP_QC_FINDCHAINFLAGS*/\nfloat(entity ent, string tagname) gettagindex = #451; /* Part of DP_QC_GETTAGINFO*/\nvector(entity ent, float tagindex) gettaginfo = #452; /* Part of DP_QC_GETTAGINFO\n\t\tObtains the current worldspace position+orientation of the bone or tag from the given entity. The return value is the world coord, v_forward, v_right, v_up are also set according to the bone/tag's orientation. */\n\n#endif\n#ifdef SSQC\nvoid(entity player) dropclient = #453; /* Part of DP_SV_DROPCLIENT*/\nentity() spawnclient = #454; /* Part of DP_SV_BOTCLIENT*/\nfloat(entity client) clienttype = #455; /* Part of DP_SV_BOTCLIENT*/\nvoid(float target, string str) WriteUnterminatedString = #456; /* Part of DP_SV_WRITEUNTERMINATEDSTRING*/\n#endif\n#if defined(CSQC) || defined(SSQC)\nvoid(vector org, vector vel, float howmany) te_flamejet = #457; /* Part of _DP_TE_FLAMEJET*/\nentity(float entnum) edict_num = #459; /* Part of DP_QC_EDICT_NUM*/\nstrbuf() buf_create = #460; /* Part of DP_QC_STRINGBUFFERS*/\nvoid(strbuf bufhandle) buf_del = #461; /* Part of DP_QC_STRINGBUFFERS*/\nfloat(strbuf bufhandle) buf_getsize = #462; /* Part of DP_QC_STRINGBUFFERS*/\nvoid(strbuf bufhandle_from, strbuf bufhandle_to) buf_copy = #463; /* Part of DP_QC_STRINGBUFFERS*/\nvoid(strbuf bufhandle, float sortprefixlen, float backward) buf_sort = #464; /* Part of DP_QC_STRINGBUFFERS*/\nstring(strbuf bufhandle, string glue) buf_implode = #465; /* Part of DP_QC_STRINGBUFFERS*/\nstring(strbuf bufhandle, float string_index) bufstr_get = #466; /* Part of DP_QC_STRINGBUFFERS*/\nvoid(strbuf bufhandle, float string_index, string str) bufstr_set = #467; /* Part of DP_QC_STRINGBUFFERS*/\nfloat(strbuf bufhandle, string str, float ordered) bufstr_add = #468; /* Part of DP_QC_STRINGBUFFERS*/\nvoid(strbuf bufhandle, float string_index) bufstr_free = #469; /* Part of DP_QC_STRINGBUFFERS*/\n#endif\nfloat(float s) asin = #471; /* Part of DP_QC_ASINACOSATANATAN2TAN*/\nfloat(float c) acos = #472; /* Part of DP_QC_ASINACOSATANATAN2TAN*/\nfloat(float t) atan = #473; /* Part of DP_QC_ASINACOSATANATAN2TAN*/\nfloat(float c, float s) atan2 = #474; /* Part of DP_QC_ASINACOSATANATAN2TAN*/\nfloat(float a) tan = #475; /* Part of DP_QC_ASINACOSATANATAN2TAN\n\t\tForgive me father, for I have a sunbed and I'm not afraid to use it. */\n\nfloat(string s) strlennocol = #476; /* Part of DP_QC_STRINGCOLORFUNCTIONS\n\t\tReturns the number of characters in the string after any colour codes or other markup has been parsed. */\n\nstring(string s) strdecolorize = #477; /* Part of DP_QC_STRINGCOLORFUNCTIONS\n\t\tFlattens any markup/colours, removing them from the string. */\n\nstring(float uselocaltime, string format, ...) strftime = #478; /* Part of DP_QC_STRFTIME*/\nfloat(string s, string separator1, ...) tokenizebyseparator = #479; /* Part of DP_QC_TOKENIZEBYSEPARATOR\n\t\tSplits up the string using only the specified delimiters/separators. Multiple delimiters can be given, they are each considered equivelent (though should start with the longest if you want to do weird subseparator stuff).\n\t\tThe resulting tokens can be queried via argv (and argv_start|end_index builtins, if you want to determine which of the separators was present between two tokens).\n\t\tNote that while an input string containing JUST a separator will return 2, a string with no delimiter will return 1, while (in FTE) an empty string will ALWAYS return 0. */\n\nstring(string s) strtolower = #480; /* Part of DP_QC_STRING_CASE_FUNCTIONS*/\nstring(string s) strtoupper = #481; /* Part of DP_QC_STRING_CASE_FUNCTIONS*/\n#if defined(CSQC) || defined(SSQC)\nstring(string s) cvar_defstring = #482; /* Part of DP_QC_CVAR_DEFSTRING*/\nvoid(vector origin, string sample, float volume, float attenuation) pointsound = #483; /* Part of DP_SV_POINTSOUND*/\n#endif\nstring(string search, string replace, string subject) strreplace = #484; /* Part of DP_QC_STRREPLACE*/\nstring(string search, string replace, string subject) strireplace = #485; /* Part of DP_QC_STRREPLACE*/\n#if defined(CSQC) || defined(SSQC)\nvector(entity e, float s, float n, float a) getsurfacepointattribute = #486; /* Part of DP_QC_GETSURFACEPOINTATTRIBUTE*/\n#endif\n#if defined(CSQC) || defined(MENU)\nfloat(string name, optional string initialURI) gecko_create = #487; /* Part of DP_GECKO_SUPPORT\n\t\tCreate a new 'browser tab' shader with the specified name that can then be drawn via drawpic (shader should not already exist - including from map/model textures or disk). In order to function correctly, this builtin depends upon external plugins being available. Use gecko_navigate to navigate it to a page of your choosing. */\n\nvoid(string name) gecko_destroy = #488; /* Part of DP_GECKO_SUPPORT\n\t\tDestroy a shader. */\n\nvoid(string name, string URI) gecko_navigate = #489; /* Part of DP_GECKO_SUPPORT\n\t\tSends a command to the media decoder attached to the specified shader. In the case of a browser decoder, this changes the url that the browser displays. 'cmd:[un]focus' will tell the decoder that it has focus. */\n\nfloat(string name, float key, float eventtype, optional float charcode) gecko_keyevent = #490; /* Part of DP_GECKO_SUPPORT\n\t\tSend a key event to a media decoder. This applies only to interactive decoders like browsers. */\n\nvoid(string name, float x, float y) gecko_mousemove = #491; /* Part of DP_GECKO_SUPPORT\n\t\tSets a media decoder shader's mouse position. Values should be 0-1. */\n\nvoid(string name, float w, float h) gecko_resize = #492; /* Part of DP_GECKO_SUPPORT\n\t\tRequest to resize a media decoder. */\n\nvector(string name) gecko_get_texture_extent = #493; /* Part of DP_GECKO_SUPPORT\n\t\tRetrieves a media decoder current image pixel sizes. */\n\nstring(string shadname, string propname) gecko_getproperty = #0:gecko_getproperty; /*\n\t\tQueries the media decoder (especially browser ones) for decoder-specific properties. The cef plugin recognises url, title, status. */\n\n#endif\n#ifdef CSQC\nfloat(string file, string id) cin_open = #0:cin_open;\nvoid(string id) cin_close = #0:cin_close;\nvoid(string id, float newstate) cin_setstate = #0:cin_setstate;\nfloat(string id) cin_getstate = #0:cin_getstate;\nvoid(string file) cin_restart = #0:cin_restart;\n#endif\n__deprecated(\"Use digest_hex\") float(float caseinsensitive, string s, ...) crc16 = #494; /* Part of DP_QC_CRC16*/\nfloat(string name) cvar_type = #495; /* Part of DP_QC_CVAR_TYPE*/\nfloat() numentityfields = #496; /* Part of DP_QC_ENTITYDATA\n\t\tGives the number of named entity fields. Note that this is not the size of an entity, but rather just the number of unique names (ie: vectors use 4 names rather than 3). */\n\nfloat(string fieldname) findentityfield = #0:findentityfield; /*\n\t\tFind a field index by name. */\n\ntypedef .__variant field_t;\nfield_t(float fieldnum) entityfieldref = #0:entityfieldref; /*\n\t\tReturns a field value that can be directly used to read entity fields. Be sure to validate the type with entityfieldtype before using. */\n\nstring(float fieldnum) entityfieldname = #497; /* Part of DP_QC_ENTITYDATA\n\t\tRetrieves the name of the given entity field. */\n\nfloat(float fieldnum) entityfieldtype = #498; /* Part of DP_QC_ENTITYDATA\n\t\tProvides information about the type of the field specified by the field num. Returns one of the EV_ values. */\n\nstring(float fieldnum, entity ent) getentityfieldstring = #499; /* Part of DP_QC_ENTITYDATA*/\nfloat(float fieldnum, entity ent, string s) putentityfieldstring = #500; /* Part of DP_QC_ENTITYDATA*/\n#ifdef SSQC\nvoid(float to, string s, float sz) WritePicture = #501; /* Part of DP_SV_WRITEPICTURE\n\t\tEncodes the named image across the network as-is adhering to some size limit. In FTE, this simply writes the string and is equivelent to writestring and sz is ignored. WritePicture should be paired with ReadPicture in csqc. */\n\n#endif\n#ifdef CSQC\nstring() ReadPicture = #501; /*\n\t\tReads a picture that was written by ReadPicture, and returns a name that can be used in drawpic and other 2d drawing functions. In FTE, this acts as a readstring-with-downloadcheck - the image will appear normally once it has been downloaded, but its size may be incorrect until then. */\n\nvoid(float effectindex, entity own, vector org_from, vector org_to, vector dir_from, vector dir_to, float countmultiplier, optional float flags) boxparticles = #502;\n#endif\nstring(string filename, optional enumflags:float{WP_REFERENCEPACKAGE,WP_FULLPACKAGEPATH} flags) whichpack = #503; /* Part of DP_QC_WHICHPACK\n\t\tReturns the pak file name that contains the file specified. progs/player.mdl will generally return something like 'pak0.pak'. If WP_REFERENCE, clients will automatically be told that the returned package should be pre-downloaded and used, even if allow_download_refpackages is not set. */\n\n#ifdef CSQC\n__variant(float entnum, float fieldnum) getentity = #504; /*\n\t\tLooks up fields from non-csqc-visible entities. The entity will need to be within the player's pvs. fieldnum should be one of the GE_ constants. */\n\n#endif\nstring(string in) uri_escape = #510; /* Part of DP_QC_URI_ESCAPE\n\t\tUses percent-encoding to encode any bytes in the input string which are not ascii alphanumeric, period, hyphen, or underscore. All other bytes will expand to eg '%20' for a single space char. This encoding scheme is compatible with http and other uris. */\n\nstring(string in) uri_unescape = #511; /* Part of DP_QC_URI_ESCAPE\n\t\tUndo any percent-encoding in the input string, hopefully resulting in the same original sequence of bytes (and thus chars too). */\n\nfloat(entity ent) num_for_edict = #512;\n#define uri_post uri_get\nfloat(string uril, float id, optional string postmimetype, optional string postdata) uri_get = #513; /* Part of DP_QC_URI_GET, DP_QC_URI_POST\n\t\turi_get() gets content from an URL and calls a callback \"uri_get_callback\" with it set as string; an unique ID of the transfer is returned\n\t\treturns 1 on success, and then calls the callback with the ID, 0 or the HTTP status code, and the received data in a string\n\t\tFor a POST request, you will typically want the postmimetype set to application/x-www-form-urlencoded.\n\t\tFor a GET request, omit the mime+data entirely.\n\t\tConsult your webserver/php/etc documentation for best-practise. */\n\nfloat(string str) tokenize_console = #514; /*\n\t\tTokenize a string exactly as the console's tokenizer would do so. The regular tokenize builtin became bastardized for convienient string parsing, which resulted in a large disparity that can be exploited to bypass checks implemented in a naive SV_ParseClientCommand function, therefore you can use this builtin to make sure it exactly matches. */\n\nfloat(float idx) argv_start_index = #515; /*\n\t\tReturns the character index that the tokenized arg started at. */\n\nfloat(float idx) argv_end_index = #516; /*\n\t\tReturns the character index that the tokenized arg stopped at. */\n\nvoid(strbuf strbuf, string pattern, string antipattern) buf_cvarlist = #517;\nstring(string cvarname) cvar_description = #518; /*\n\t\tRetrieves the description of a cvar, which might be useful for tooltips or help files. This may still not be useful. */\n\n#if defined(CSQC) || defined(SSQC)\nfloat(optional float timetype) gettime = #519;\n#endif\n#ifdef CSQC\nDEP string(float keynum) keynumtostring_omgwtf = #520;\n__deprecated(\"Does not support modifiers\") string(string command, optional float bindmap) findkeysforcommand = #521; /*\n\t\tReturns a list of keycodes that perform the given console command in a format that can only be parsed via tokenize (NOT tokenize_console). This only and always returns two values - if only one key is actually bound, -1 will be returned. The bindmap argument is listed for compatibility with dp-specific defs, but is ignored in FTE. */\n\nstring(string command, optional float bindmap) findkeysforcommandex = #0:findkeysforcommandex; /*\n\t\tReturns a list of key bindings in keyname format instead of keynums. Use tokenize to parse. This list may contain modifiers. May return large numbers of keys. */\n\n#endif\n#if defined(CSQC) || defined(SSQC)\nvoid(string s) loadfromdata = #529; /*\n\t\tReads a set of entities from the given string. This string should have the same format as a .ent file or a saved game. Entities will be spawned as required. If you need to see the entities that were created, you should use parseentitydata instead. */\n\nvoid(string s) loadfromfile = #530; /*\n\t\tReads a set of entities from the named file. This file should have the same format as a .ent file or a saved game. Entities will be spawned as required. If you need to see the entities that were created, you should use parseentitydata instead. */\n\nvoid(float pause) setpause = #531; /*\n\t\tSets whether the server should or should not be paused. This does not affect auto-paused things like when the console is down. */\n\n#endif\n#ifdef SSQC\nfloat(string mname) precache_vwep_model = #532; /* Part of ZQ_VWEP*/\n#endif\nfloat(float v, optional float base) log = #532; /* Part of ??MVDSV_BUILTINS\n\t\tDetermines the logarithm of the input value according to the specified base. This can be used to calculate how much something was shifted by. */\n\n#ifdef CSQC\nfloat(entity e, float channel, string newsample, float volume, float attenuation, float pitchpct, float flags, float timeoffset) soundupdate = #0:soundupdate; /*\n\t\tChanges the properties of the current sound being played on the given entity channel. newsample may be empty, and will be ignored in this case. timeoffset is relative to the current position (subtract the result of getsoundtime for absolute positions). Negative volume can be used to stop the sound. Return value is a fractional value based upon the number of audio devices that could be updated - test against TRUE rather than non-zero. */\n\nfloat(entity e, float channel) getsoundtime = #533; /*\n\t\tReturns the current playback time of the sample on the given entity's channel. Beware CHAN_AUTO (in csqc, channels are not limited by network protocol). */\n\nfloat(entity e, float channel) getchannellevel = #0:getchannellevel; /* */\n\n#endif\n#if defined(CSQC) || defined(MENU)\nfloat(string sample) soundlength = #534; /*\n\t\tProvides a way to query the duration of a sound sample, allowing you to set up a timer to chain samples. */\n\n#endif\nfloat(string filename, strbuf bufhandle) buf_loadfile = #535; /*\n\t\tAppends the named file into a string buffer (which must have been created in advance). The return value merely says whether the file was readable. */\n\nfloat(filestream filehandle, strbuf bufhandle, optional float startpos, optional float numstrings) buf_writefile = #536; /*\n\t\tWrites the contents of a string buffer onto the end of the supplied filehandle (you must have already used fopen). Additional optional arguments permit you to constrain the writes to a subsection of the stringbuffer. */\n\nfloat(float bufhandle, string match, float matchrule, float startpos, float step) bufstr_find = #537; /*\n\t\tLooks for the first occurence of the specified string in the buffer, returning its index or -1 on failure. */\n\n#ifdef SSQC\nfloat(optional float forcestate) physics_supported = #0:physics_supported; /*\n\t\tQueries whether rigid body physics is enabled or not. CSQC and SSQC may report different values. If the force argument is specified then the engine will try to activate or release physics (returning the new state, which may fail if plugins or dlls are missing). Note that restarting the physics engine is likely to result in hitches when collision trees get generated. The state may change if a plugin is disabled mid-map. */\n\n#endif\n#if defined(CSQC) || defined(SSQC)\nvoid(entity e, float physics_enabled) physics_enable = #540; /*\n\t\tEnable or disable the physics attached to a MOVETYPE_PHYSICS entity. Entities which have been disabled in this way will stop taking so much cpu time. */\n\nvoid(entity e, vector force, vector relative_ofs) physics_addforce = #541; /*\n\t\tApply some impulse directional force upon a MOVETYPE_PHYSICS entity. */\n\nvoid(entity e, vector torque) physics_addtorque = #542; /*\n\t\tApply some impulse rotational force upon a MOVETYPE_PHYSICS entity. */\n\n#endif\n#ifdef MENU\nvoid(float dest) setkeydest = #601;\nfloat() getkeydest = #602;\n#endif\n#if defined(CSQC) || defined(MENU)\nvoid(float trg) setmousetarget = #603;\nfloat() getmousetarget = #604;\n#endif\nvoid(.../*, string funcname*/) callfunction = #605; /*\n\t\tInvokes the named function. The function name is always passed as the last parameter and must always be present. The others are passed to the named function as-is */\n\nvoid(filestream fh, entity e) writetofile = #606; /*\n\t\tWrites an entity's fields to the named frik_file file handle. */\n\nfloat(string s) isfunction = #607; /*\n\t\tReturns true if the named function exists and can be called with the callfunction builtin. */\n\n#if defined(CSQC) || defined(MENU)\nvector(float vidmode, optional float forfullscreen) getresolution = #608; /*\n\t\tSupposed to query the driver for supported video modes. FTE does not query drivers in this way, nor would it trust drivers anyway. */\n\n#endif\n#ifdef CSQC\nDEP string(float keynum) keynumtostring_menu = #609;\n#endif\n#ifdef MENU\nstring(float keynum) keynumtostring = #609; /*\n\t\tConverts a qscancode key number into a mostly-human-readable name, matching the bind command. */\n\nstring(string command, optional float bindmap) findkeysforcommand = #610;\n#endif\n#if defined(CSQC) || defined(MENU)\nfloat(float type) gethostcachevalue = #611; /* Part of FTE_CSQC_SERVERBROWSER*/\nstring(float type, float hostnr) gethostcachestring = #612; /* Part of FTE_CSQC_SERVERBROWSER*/\n#endif\nfloat(entity e, string s, optional float offset) parseentitydata = #613; /*\n\t\tReads a single entity's fields into an already-spawned entity. s should contain field pairs like in a saved game: {\"foo1\" \"bar\" \"foo2\" \"5\"}. Returns <=0 on failure, otherwise returns the offset in the string that was read to. */\n\nstring(entity e) generateentitydata = #0:generateentitydata; /*\n\t\tDumps the entities fields into a string which can later be parsed with parseentitydata. */\n\n#ifdef MENU\nfloat(string key) stringtokeynum = #614; /*\n\t\tReturns the qscancode of a key from its name. Names are identical to the bind command. ctrl/shift/alt modifiers are ignored. */\n\n#endif\n#ifdef CSQC\nfloat(string key) stringtokeynum_menu = #614;\n#endif\n#if defined(CSQC) || defined(MENU)\nvoid() resethostcachemasks = #615; /* Part of FTE_CSQC_SERVERBROWSER*/\nvoid(float mask, float fld, string str, float op) sethostcachemaskstring = #616; /* Part of FTE_CSQC_SERVERBROWSER*/\nvoid(float mask, float fld, float num, float op) sethostcachemasknumber = #617; /* Part of FTE_CSQC_SERVERBROWSER*/\nvoid() resorthostcache = #618; /* Part of FTE_CSQC_SERVERBROWSER*/\nvoid(float fld, float descending) sethostcachesort = #619; /* Part of FTE_CSQC_SERVERBROWSER*/\nvoid(optional float dopurge) refreshhostcache = #620; /* Part of FTE_CSQC_SERVERBROWSER*/\nfloat(float fld, float hostnr) gethostcachenumber = #621; /* Part of FTE_CSQC_SERVERBROWSER*/\nfloat(string key) gethostcacheindexforkey = #622; /* Part of FTE_CSQC_SERVERBROWSER*/\nvoid(string key) addwantedhostcachekey = #623; /* Part of FTE_CSQC_SERVERBROWSER*/\nstring() getextresponse = #624; /* Part of FTE_CSQC_SERVERBROWSER*/\n#endif\nstring(string dnsname, optional float defport) netaddress_resolve = #625;\n#if defined(CSQC) || defined(MENU)\nstring(float n, float prop) getgamedirinfo = #626;\nstring(int n, int prop) getpackagemanagerinfo = #0:getpackagemanagerinfo; /*\n\t\tQueries information about a package from the engine's package manager subsystem. Actions can be taken via the pkg console command. */\n\n#endif\nstring(string fmt, ...) sprintf = #627; /* Part of DP_QC_SPRINTF\n\t\t'prints' to a formatted temp-string. Mostly acts as in C, however %d assumes floats (fteqcc has arg checking. Use it.).\n\t\ttype conversions: l=arg is an int, h=arg is a float, and will work as a prefix for any float or int representation.\n\t\tfloat representations: d=decimal, e,E=exponent-notation, f,F=floating-point notation, g,G=terse float, c=char code, x,X=hex\n\t\tother representations: i=int, s=string, S=quoted and marked-up string, v=vector, p=pointer\n\t\tso %ld will accept an int arg, while %hi will expect a float arg.\n\t\tentities, fields, and functions will generally need to be printed as ints with %i. */\n\n#if defined(CSQC) || defined(SSQC)\nfloat(entity e, float s) getsurfacenumtriangles = #628;\nvector(entity e, float s, float n) getsurfacetriangle = #629;\n#endif\n#if defined(CSQC) || defined(MENU)\nfloat(float key, string bind, optional float bindmap, optional float modifier) setkeybind = #630;\nvector() getbindmaps = #631;\nfloat(vector bm) setbindmaps = #632;\n#endif\nstring(string digest, string data, ...) digest_hex = #639;\nstring(string digest, void *data, int length) digest_ptr = #0:digest_ptr; /*\n\t\tCalculates the digest of a single contiguous block of memory (including nulls) using the specified hash function. */\n\nfloat(string src, string dst) fcopy = #650; /*\n\t\tEquivelent to fopen+fread+fwrite+fclose from QC (ie: reads from $gamedir/data/ or $gamedir, but always writes to $gamedir/data/ ) */\n\nfloat(string src, string dst) frename = #651; /*\n\t\tRenames the file, returning 0 on success. Both paths are relative to the data/ subdir. */\n\nfloat(string fname) fremove = #652; /*\n\t\tDeletes the named file - path is relative to data/ subdir, like fopen's FILE_WRITE. Returns 0 on success. */\n\nfloat(string fname) fexists = #653; /*\n\t\tReturns true if it exists inside the default writable path. Use whichpack for greater portability. */\n\nfloat(string path) rmtree = #654; /*\n\t\tDangerous, but sandboxed to data/ */\n\n#if defined(CSQC) || defined(MENU)\nconst float K_TAB = 9;\nconst float K_ENTER = 13;\nconst float K_ESCAPE = 27;\nconst float K_SPACE = 32;\nconst float K_BACKSPACE = 127;\nconst float K_UPARROW = 128;\nconst float K_DOWNARROW = 129;\nconst float K_LEFTARROW = 130;\nconst float K_RIGHTARROW = 131;\nconst float K_LALT = 132;\nconst float K_RALT = -245;\nconst float K_LCTRL = 133;\nconst float K_RCTRL = -246;\nconst float K_LSHIFT = 134;\nconst float K_RSHIFT = -247;\nconst float K_F1 = 135;\nconst float K_F2 = 136;\nconst float K_F3 = 137;\nconst float K_F4 = 138;\nconst float K_F5 = 139;\nconst float K_F6 = 140;\nconst float K_F7 = 141;\nconst float K_F8 = 142;\nconst float K_F9 = 143;\nconst float K_F10 = 144;\nconst float K_F11 = 145;\nconst float K_F12 = 146;\nconst float K_INS = 147;\nconst float K_DEL = 148;\nconst float K_PGDN = 149;\nconst float K_PGUP = 150;\nconst float K_HOME = 151;\nconst float K_END = 152;\nconst float K_KP_HOME = 164;\nconst float K_KP_UPARROW = 165;\nconst float K_KP_PGUP = 166;\nconst float K_KP_LEFTARROW = 161;\nconst float K_KP_5 = 162;\nconst float K_KP_RIGHTARROW = 163;\nconst float K_KP_END = 158;\nconst float K_KP_DOWNARROW = 159;\nconst float K_KP_PGDN = 160;\nconst float K_KP_ENTER = 172;\nconst float K_KP_INS = 157;\nconst float K_KP_DEL = 167;\nconst float K_KP_SLASH = 168;\nconst float K_KP_MINUS = 170;\nconst float K_KP_PLUS = 171;\nconst float K_KP_NUMLOCK = 154;\nconst float K_KP_STAR = 169;\nconst float K_KP_EQUALS = 173;\nconst float K_MOUSE1 = 512;\nconst float K_MOUSE2 = 513;\nconst float K_MOUSE3 = 514;\nconst float K_MOUSE4 = 517;\nconst float K_MOUSE5 = 518;\nconst float K_MOUSE6 = 519;\nconst float K_MOUSE7 = 520;\nconst float K_MOUSE8 = 521;\nconst float K_MOUSE9 = 522;\nconst float K_MOUSE10 = 523;\nconst float K_MWHEELUP = 515;\nconst float K_MWHEELDOWN = 516;\nconst float K_LWIN = -239;\nconst float K_RWIN = -240;\nconst float K_APP = -241;\nconst float K_SEARCH = -242;\nconst float K_POWER = -130;\nconst float K_VOLUP = -243;\nconst float K_VOLDOWN = -244;\nconst float K_JOY1 = 768;\nconst float K_JOY2 = 769;\nconst float K_JOY3 = 770;\nconst float K_JOY4 = 771;\nconst float K_AUX1 = 784;\nconst float K_AUX2 = 785;\nconst float K_AUX3 = 786;\nconst float K_AUX4 = 787;\nconst float K_AUX5 = 788;\nconst float K_AUX6 = 789;\nconst float K_AUX7 = 790;\nconst float K_AUX8 = 791;\nconst float K_AUX9 = 792;\nconst float K_AUX10 = 793;\nconst float K_AUX11 = 794;\nconst float K_AUX12 = 795;\nconst float K_AUX13 = 796;\nconst float K_AUX14 = 797;\nconst float K_AUX15 = 798;\nconst float K_AUX16 = 799;\nconst float K_AUX17 = 800;\nconst float K_AUX18 = 801;\nconst float K_AUX19 = 802;\nconst float K_AUX20 = 803;\nconst float K_AUX21 = 804;\nconst float K_AUX22 = 805;\nconst float K_AUX23 = 806;\nconst float K_AUX24 = 807;\nconst float K_AUX25 = 808;\nconst float K_AUX26 = 809;\nconst float K_AUX27 = 810;\nconst float K_AUX28 = 811;\nconst float K_AUX29 = 812;\nconst float K_AUX30 = 813;\nconst float K_AUX31 = 814;\nconst float K_AUX32 = 815;\nconst float K_PAUSE = 153;\nconst float K_PRINTSCREEN = 174;\nconst float K_CAPSLOCK = 155;\nconst float K_SCROLLLOCK = 156;\nconst float K_SEMICOLON = 59;\nconst float K_PLUS = 43;\nconst float K_MINUS = 45;\nconst float K_TILDE = 126;\nconst float K_BACKQUOTE = 96;\nconst float K_BACKSLASH = 92;\nconst float K_GP_A = 826;\nconst float K_GP_B = 827;\nconst float K_GP_X = 828;\nconst float K_GP_Y = 829;\nconst float K_GP_LSHOULDER = 824;\nconst float K_GP_RSHOULDER = 825;\nconst float K_GP_LTRIGGER = 830;\nconst float K_GP_RTRIGGER = 831;\nconst float K_GP_BACK = 821;\nconst float K_GP_START = 820;\nconst float K_GP_LTHUMB = 822;\nconst float K_GP_RTHUMB = 823;\nconst float K_GP_DPAD_UP = 816;\nconst float K_GP_DPAD_DOWN = 817;\nconst float K_GP_DPAD_LEFT = 818;\nconst float K_GP_DPAD_RIGHT = 819;\nconst float K_GP_GUIDE = -202;\nconst float K_GP_UNKNOWN = -255;\nconst float K_GP_LTHUMB_UP = 832;\nconst float K_GP_LTHUMB_DOWN = 833;\nconst float K_GP_LTHUMB_LEFT = 834;\nconst float K_GP_LTHUMB_RIGHT = 835;\nconst float K_GP_RTHUMB_UP = 836;\nconst float K_GP_RTHUMB_DOWN = 837;\nconst float K_GP_RTHUMB_LEFT = 838;\nconst float K_GP_RTHUMB_RIGHT = 839;\n#endif\n#ifdef _ACCESSORS\naccessor strbuf : float\n{\n\tinline get float asfloat[float idx] = {return stof(bufstr_get(this, idx));};\n\tinline set float asfloat[float idx] = {bufstr_set(this, idx, ftos(value));};\n\tget string[float] = bufstr_get;\n\tset string[float] = bufstr_set;\n\tget float length = buf_getsize;\n};\naccessor searchhandle : float\n{\n\tget string[float] = search_getfilename;\n\tget float length = search_getsize;\n};\naccessor hashtable : float\n{\n\tinline get vector v[string key] = {return hash_get(this, key, '0 0 0', EV_VECTOR);};\n\tinline set vector v[string key] = {hash_add(this, key, value, HASH_REPLACE|EV_VECTOR);};\n\tinline get string s[string key] = {return hash_get(this, key, \"\", EV_STRING);};\n\tinline set string s[string key] = {hash_add(this, key, value, HASH_REPLACE|EV_STRING);};\n\tinline get float f[string key] = {return hash_get(this, key, 0.0, EV_FLOAT);};\n\tinline set float f[string key] = {hash_add(this, key, value, HASH_REPLACE|EV_FLOAT);};\n\tinline get __variant[string key] = {return hash_get(this, key, __NULL__);};\n\tinline set __variant[string key] = {hash_add(this, key, value, HASH_REPLACE);};\n};\naccessor infostring : string\n{\n\tget string[string] = infoget;\n\tinline set& string[string fld] = {this = infoadd(this, fld, value);};\n};\naccessor filestream : float\n{\n\tget string = fgets;\n\tinline set string = {fputs(this,value);};\n};\n#endif\n#undef DEP_CSQC\n#undef FTEDEP\n#undef DEP\n#pragma noref 0\n"
  },
  {
    "path": "quakec/csaddon/src/editor_brushes.qc",
    "content": "/*traceline/tracebox returns trace_brush_id and trace_brush_faceid\r\n\r\n\r\nmod should track selected brush list instead of polling.\r\nto move 50 brushes, mod needs to get+delete+transform+create\r\nbrush ids are ints. this allows different clients to use different ranges without float problems.\r\n*/\r\n\r\n\r\n\r\n\r\n\r\n//when we're drawing sideviews, we normally need them wireframe in order to actually see anything\r\nstatic void(vector sizes) drawwireframeview =\r\n{\r\n\t//figure out the planes that we care about.\r\n\tvector org = getviewprop(VF_ORIGIN);\r\n\tstatic vector vaxis[6];\r\n\tvaxis[0] = v_right;\r\n\tvaxis[1] = v_up;\r\n\tvaxis[2] = v_forward;\r\n\tfor (int i = 0; i < 3; i++)\r\n\t\tdists[i] = (org*vaxis[i]) + sizes[i];\r\n\tfor (int i = 0; i < 3; i++)\r\n\t{\r\n\t\tvaxis[i+3] = -vaxis[i];\r\n\t\tdists[i+3] = (org*vaxis[i+3]) + sizes[i];\r\n\t}\r\n\r\n\tint numbrushes = brush_findinvolume(selectedbrushmodel, vaxis, dists, dists.length, &brushlist[0], __NULL__, brushlist.length);\r\n\t\r\n\t//this can be a bit slow with lots of brushes. would be nice to batch them a bit better.\r\n\twhile(numbrushes --> 0)\r\n\t\tDrawEngineBrushWireframe(selectedbrushmodel, brushlist[numbrushes]);\r\n\tsetviewprop(VF_DRAWWORLD, FALSE);\r\n};\r\n\r\nstatic void() editor_brushes_drawselected =\r\n{\r\n\tfloat intensity = (sin(gettime(5)*4)+1)*0.05;\r\n\tint facenum, point, points;\r\n\tvector col;\r\n\r\n\tfor (int sb = 0; sb < selectedbrushcount; sb++)\r\n\t{\r\n\t\tint model = selectedbrushes[sb].model;\r\n\t\tint brush = selectedbrushes[sb].id;\r\n\t\t\r\n\t\t//draw a faded version of all the brushes.\r\n\t\tDrawEngineBrushFaded(model, brush, 0.5);\r\n\t}\r\n\t\r\n\t//draw all selected brush faces.\r\n\tfor (int sb = 0; sb < selectedbrushcount; sb++)\r\n\t{\r\n\t\tint model = selectedbrushes[sb].model;\r\n\t\tint brush = selectedbrushes[sb].id;\r\n\t\t__uint64 facemask = selectedbrushes[sb].facemask;\r\n\t\tfor(facenum = 0;; facenum++)\r\n\t\t{\r\n\t\t\tpoints = brush_getfacepoints(model, brush, 1+facenum, facepoints, MAX_FACEPOINTS);\r\n\t\t\tif (!points)\r\n\t\t\t\tbreak;\t//end of face list, I guess\r\n\r\n\t\t\tif (facemask & (1ul<<facenum))\r\n\t\t\t\tcol = [0,intensity,0];\r\n\t\t\telse\r\n\t\t\t\tcol = [intensity,0,0];\r\n\r\n\t\t\tR_BeginPolygon(\"terrainedit\");\r\n\t\t\tfor (point = 0; point < points; point++)\r\n\t\t\t\tR_PolygonVertex(facepoints[point], '0 0', col, 1);\r\n\t\t\tR_EndPolygon();\r\n\t\t}\r\n\t}\r\n\t\r\n\tfor (int sb = 0; sb < selectedbrushcount; sb++)\r\n\t{\r\n\t\tint model = selectedbrushes[sb].model;\r\n\t\tint brush = selectedbrushes[sb].id;\r\n\t\t\r\n\t\t//now draw wireframe\r\n\t\tDrawEngineBrushWireframe(model, brush);\r\n\t}\r\n\t\r\n//\ttmp.numfaces = brush_get(selectedbrushmodel, selectedbrush, tmp.faces, tmp.faces.length, &tmp.contents);\r\n//\tDebrushifyLite(tmp.faces, tmp.numfaces);\r\n\tDrawAxisExtensions(vertedit.p, vertedit.numverts, \"terrainedit\", '0 0 0.3', 1);\r\n};\r\n\r\nvoid(vector mousepos) editor_brushes_addentities =\r\n{\r\n\tvector col = '0 0 0';\r\n\tint points, point;\r\n\tint facenum;\r\n\tfloat intensity = (sin(gettime(5)*4)+1)*0.05;\r\n\tvector displace = 0;\r\n\tstatic vector mid;\r\n\t\r\n\tautocvar_ca_brush_view = fabs(autocvar_ca_brush_view) % 4f;\r\n\tif (autocvar_ca_brush_view)\r\n\t{\r\n\t\tvector vmn = (vector)getproperty(VF_MIN);\r\n\t\tvector vsz = (vector)getproperty(VF_SIZE);\r\n\t\tvector ang = vectoangles([autocvar_ca_brush_view==1,autocvar_ca_brush_view==2,autocvar_ca_brush_view==3]);\r\n\t\tvector fov;\r\n\t\tif (vsz_x > vsz_y)\r\n\t\t\tfov = [autocvar_ca_brush_viewsize, autocvar_ca_brush_viewsize * (vsz_y/vsz_x), 8192];\r\n\t\telse\r\n\t\t\tfov = [autocvar_ca_brush_viewsize * (vsz_x/vsz_y), autocvar_ca_brush_viewsize, 8192];\r\n\t\tmakevectors(ang);\r\n\t\tdrawfill(vmn, vsz, '0 0 0', 1, 0);\r\n\t\tsetviewprop(VF_VIEWENTITY, 0);\r\n\t\tsetviewprop(VF_PERSPECTIVE, FALSE);\r\n\t\tsetviewprop(VF_ANGLES, ang);\r\n\t\tsetviewprop(VF_MAXDIST, fov_z*0.5);\r\n\t\tsetviewprop(VF_MINDIST, -fov_z*0.5);\t//I'm not entirely sure where the near clip plane should be. oh well.\r\n\t\tif (vsz_x > vsz_y)\r\n\t\t\tsetviewprop(VF_FOV, fov);\r\n\t\telse\r\n\t\t\tsetviewprop(VF_FOV, fov);\r\n\r\n\t\tif (curmousepos_x >= vmn_x && curmousepos_x <= vmn_x+vsz_x && curmousepos_y >= vmn_y && curmousepos_y <= vmn_y+vsz_y)\r\n\t\t{\r\n\t\t\tmousefar = unproject(curmousepos + '0 0 1');\r\n\t\t\tmousenear = unproject(curmousepos);\r\n\t\t}\r\n\r\n\t\tdrawwireframeview(fov*0.5);\r\n\t}\r\n\telse\r\n\t\tmakevectors(input_angles);\r\n\t\r\n\tif ((mousetool == BT_VERTEXEDIT || mousetool == BT_CREATEDRAG || brushtool == BT_PUSHFACE || brushtool == BT_CLONEDISPLACE || brushtool == BT_MOVE || brushtool == BT_MOVETEXTURE) && bt_points)\r\n\t{\r\n\t\tvector fwd = v_forward;\r\n\t\tvector rgt = v_right;\r\n\t\tvector up = v_up;\r\n\t\tvector farpos;\r\n\r\n\t\tfwd = axialize(fwd);\r\n\t\trgt -= (rgt * fwd) * rgt;\r\n\t\tup -= (up * fwd) * up;\r\n\t\trgt = axialize(rgt);\r\n\t\tup = axialize(up);\r\n\r\n\t\tfarpos = normalize(mousefar-mousenear) + mousenear;\r\n\t\tfarpos = planelinepoint(mousenear, farpos, v_forward, bt_point[0] * v_forward); //find where the cursor impacts the screen grid (moved along the view vector to match where the drag was last frame)\r\n\t\tdisplace = farpos - bt_point[0];\r\n\t\tif (mousetool != BT_VERTEXEDIT && mousetool != BT_CREATEDRAG)\r\n\t\t\tdisplace = brush_snappoint(displace);\r\n\t\tif (altdown)\r\n\t\t\tbt_displace_x = displace * fwd;\r\n\t\telse\r\n\t\t{\r\n\t\t\tbt_displace_y = displace * rgt;\r\n\t\t\tbt_displace_z = displace * up;\r\n\t\t}\r\n\t\tdisplace = bt_displace_x * fwd + bt_displace_y * rgt + bt_displace_z * up;\r\n\t\t\r\n\t\tif (mousetool == BT_VERTEXEDIT)\r\n\t\t\tdisplace = brush_snappoint(displace+bt_point[0]);\r\n\t}\r\n\telse if (brushtool == BT_ROTATE)\r\n\t{\r\n\t\tte_lightning2(world, bt_point[0], bt_point[0]+-bt_displace);\r\n\t\tcol = vectoangles(bt_point[0]+bt_displace) - vectoangles(bt_point[0]);\r\n\t\tcol = channelizeangle([col_x*-1,col_y,col_z]);\r\n\t}\r\n\t\r\n\tif (vertedit.numidx && mousetool != BT_VERTEXEDIT)\r\n\t{\r\n\t\tvertedit.numidx = 0;\r\n\t\tvertedit.numverts = 0;\r\n\t}\r\n\r\n\t//make sure this shader was generated already.\r\n\tshaderforname(\"terrainedit\",\r\n\t\t\"{\"\r\n\t\t\t\"{\\n\"\r\n\t\t\t\t\"map terrainedit\\n\"\r\n\t\t\t\t\"blendfunc add\\n\"\r\n\t\t\t\t\"rgbgen vertex\\n\"\r\n\t\t\t\t\"alphagen vertex\\n\"\r\n\t\t\t\"}\\n\"\r\n\t\t\"}\");\r\n\r\n\t//make sure this shader was generated already.\r\n\tshaderforname(\"chop\",\r\n\t\t\"{\"\r\n\t\t\t\"cull disable\\n\"\r\n\t\t\t\"{\\n\"\r\n\t\t\t\t\"map terrainedit\\n\"\r\n\t\t\t\t\"blendfunc add\\n\"\r\n\t\t\t\t\"rgbgen vertex\\n\"\r\n\t\t\t\t\"alphagen vertex\\n\"\r\n\t\t\t\t\"sort nearest\\n\"\r\n\t\t\t\t\"nodepthtest\\n\"\r\n\t\t\t\"}\\n\"\r\n\t\t\"}\");\r\n\r\n\tif (mousetool == BT_VERTEXEDIT && vertedit.numidx)\r\n\t{\r\n#if 0\r\n\t\t//draw it solid WITH depth testing\r\n\t\tR_BeginPolygon(\"terrainedit\");\r\n\t\tfor(point = 0; point+2 < vertedit.numidx; point+=3)\r\n\t\t{\r\n\t\t\tcol = '0 0 0.1';\r\n\t\t\tR_PolygonVertex(vertedit.p[vertedit.i[point+0]], '0 0', col, 1);\r\n\t\t\tR_PolygonVertex(vertedit.p[vertedit.i[point+1]], '0 0', col, 1);\r\n\t\t\tR_PolygonVertex(vertedit.p[vertedit.i[point+2]], '0 0', col, 1);\r\n\t\t\tR_EndPolygon();\r\n\t\t}\r\n#endif\r\n#if 1\r\n\t\t//draw the wires (no depth)\r\n\t\tR_BeginPolygon(\"chop\");\r\n\t\tfor(point = 0; point+2 < vertedit.numidx; point+=3)\r\n\t\t{\r\n\t\t\tcol = '0 0.1 0';\r\n\t\t\tR_PolygonVertex(vertedit.p[vertedit.i[point+0]], '0 0', col, 1);\r\n\t\t\tR_PolygonVertex(vertedit.p[vertedit.i[point+1]], '0 0', col, 1);\r\n\t\t\tR_EndPolygon();\r\n\t\t\tR_PolygonVertex(vertedit.p[vertedit.i[point+1]], '0 0', col, 1);\r\n\t\t\tR_PolygonVertex(vertedit.p[vertedit.i[point+2]], '0 0', col, 1);\r\n\t\t\tR_EndPolygon();\r\n\t\t\tR_PolygonVertex(vertedit.p[vertedit.i[point+2]], '0 0', col, 1);\r\n\t\t\tR_PolygonVertex(vertedit.p[vertedit.i[point+0]], '0 0', col, 1);\r\n\t\t\tR_EndPolygon();\r\n\t\t}\r\n#endif\r\n\r\n\t\tDrawAxisExtensions(vertedit.p, vertedit.numverts, \"terrainedit\", '1 0.3 0.3', 1);\r\n\r\n\t\tif (mousedown)\r\n\t\t{\r\n\t\t\tif (bt_points == 1 && vertedit.selectedvert1 != -1)\r\n\t\t\t{\r\n\t\t\t\tif (vertedit.selectedvert2 != -1)\r\n\t\t\t\t\tvertedit.p[vertedit.selectedvert2] = displace + bt_point[2];\r\n\t\t\t\tvertedit.p[vertedit.selectedvert1] = displace + bt_point[1];\r\n\t\t\t}\r\n\t\t}\r\n\t\telse\r\n\t\t\tbt_points = 0;\r\n\r\n\t\tRebrushify(&vertedit, TRUE);\r\n\t}\r\n\telse if (brushtool == BT_CREATEBRUSH || brushtool == BT_CREATEPATCH)\r\n\t{\r\n\t\teditor_brushes_drawselected();\r\n\t\t\r\n\t\tif (bt_points > 2)\r\n\t\t{\r\n\t\t\ttmp.numfaces = BrushFromPoints(tmp.faces, tmp.faces.length, bt_point, bt_points, autocvar_ca_newbrushheight);\r\n\t\t\ttmp.numcp = 0;\r\n\t\t\t\r\n\t\t\tDrawQCBrushWireframe(tmp.faces, tmp.numfaces, \"chop\", '1 0 0', 1);\r\n\t\t}\r\n\t}\r\n\telse if (mousetool == BT_CREATEDRAG)\r\n\t{\r\n\t\tif (bt_points)\r\n\t\t{\r\n\t\t\tdisplace -= axialize(v_forward)*autocvar_ca_grid;\r\n\r\n\t\t\tmid = bt_point[0];\r\n\t\t\tdisplace = brush_snappoint(mid + displace);\r\n\t\t\tmid = brush_snappoint(mid);\r\n\t\t\tdisplace = displace - mid;\r\n\r\n\t\t\ttmp.faces[0].planenormal = '1 0 0';\r\n\t\t\ttmp.faces[1].planenormal = '-1 0 0';\r\n\t\t\ttmp.faces[2].planenormal = '0 1 0';\r\n\t\t\ttmp.faces[3].planenormal = '0 -1 0';\r\n\t\t\ttmp.faces[4].planenormal = '0 0 1';\r\n\t\t\ttmp.faces[5].planenormal = '0 0 -1';\r\n\t\t\ttmp.faces[0].planedist = mid[0];\r\n\t\t\ttmp.faces[1].planedist = -mid[0];\r\n\t\t\ttmp.faces[2].planedist = mid[1];\r\n\t\t\ttmp.faces[3].planedist = -mid[1];\r\n\t\t\ttmp.faces[4].planedist = mid[2];\r\n\t\t\ttmp.faces[5].planedist = -mid[2];\r\n\t\t\ttmp.numfaces = 6;\r\n\t\t\ttmp.contents = 1;\r\n\t\t\ttmp.numcp = 0;\r\n\t\t\t\r\n\t\t\ttmp.faces[0 + (displace_x<0.0)].planedist += fabs(displace_x);\r\n\t\t\ttmp.faces[2 + (displace_y<0.0)].planedist += fabs(displace_y);\r\n\t\t\ttmp.faces[4 + (displace_z<0.0)].planedist += fabs(displace_z);\r\n\t\t\t\r\n\t\t\tfor (facenum = 0; facenum < tmp.numfaces; facenum++)\r\n\t\t\t{\r\n\t\t\t\ttmp.faces[facenum].shadername = autocvar_ca_newbrushtexture;\r\n\t\t\t\treset_texturecoords(&tmp.faces[facenum]);\r\n\t\t\t}\r\n\r\n\t\t\tDrawQCBrushWireframe(tmp.faces, tmp.numfaces, \"chop\", '1 0 0', 1);\r\n\r\n\t\t\tif (!mousedown)\r\n\t\t\t{\r\n\t\t\t\tbrush_history_create(selectedbrushmodel, tmp.faces, tmp.numfaces, tmp.contents, TRUE);\r\n\t\t\t\tbt_points = 0;\r\n\t\t\t\tmousetool = BT_NONE;\r\n\t\t\t}\r\n\t\t}\r\n\t\telse\r\n\t\t\tmousedown = FALSE;\r\n\t}\r\n\telse if (brushtool == BT_SLICE)\r\n\t{\r\n\t\teditor_brushes_drawselected();\r\n\t\tif (bt_points)\r\n\t\t{\r\n\t\t\tfor (int sb = 0; sb < selectedbrushcount; sb++)\r\n\t\t\t{\r\n\t\t\t\tint model = selectedbrushes[sb].model;\r\n\t\t\t\tint brush = selectedbrushes[sb].id;\r\n\t\t\r\n\t\t\t\ttmp.numfaces = brush_get(model, brush, tmp.faces, tmp.faces.length, &tmp.contents);\r\n\t\t\t\ttmp.numcp = 0;\r\n\t\t\t\ttmp.faces[tmp.numfaces].planenormal = normalize(crossproduct(bt_point[2] - bt_point[0], bt_point[1] - bt_point[0]));\r\n\t\t\t\ttmp.faces[tmp.numfaces].planedist = bt_point[0] * tmp.faces[tmp.numfaces].planenormal;\r\n\r\n\t\t\t\t//draw it wireframe\r\n\t\t\t\tDrawQCBrushWireframe(tmp.faces, tmp.numfaces+1, \"chop\", '1 0 0', 1);\r\n\r\n\t\t\t\t//flip the split\r\n\t\t\t\ttmp.faces[tmp.numfaces].planenormal *= -1;\r\n\t\t\t\ttmp.faces[tmp.numfaces].planedist *= -1;\r\n\r\n\t\t\t\t//draw the other side wireframe\r\n\t\t\t\tDrawQCBrushWireframe(tmp.faces, tmp.numfaces+1, \"chop\", '0 1 0', 1);\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\telse if ((mousetool == BT_PUSHFACE || mousetool == BT_MOVETEXTURE || mousetool == BT_MOVE || mousetool == BT_CLONEDISPLACE || mousetool == BT_ROTATE) && bt_points)\r\n\t{\r\n\t\tint oldselectedbrushcount = selectedbrushcount;\r\n\t\tselbrush_t *oldselectedbrushes = memalloc(sizeof(selbrush_t)*selectedbrushcount);\r\n\t\tmemcpy(oldselectedbrushes, selectedbrushes, selectedbrushcount*sizeof(selbrush_t));\r\n\t\tfor (int sb = 0; sb < oldselectedbrushcount; sb++)\r\n\t\t{\r\n\t\t\tint model = oldselectedbrushes[sb].model;\r\n\t\t\tint brush = oldselectedbrushes[sb].id;\r\n\t\t\t__uint64 facemask = oldselectedbrushes[sb].facemask;\r\n\t\t\tif (!brush)\r\n\t\t\t\tcontinue;\r\n\r\n\t\t\ttmp.numfaces = brush_get(model, brush, tmp.faces, tmp.faces.length, &tmp.contents);\r\n\t\t\ttmp.numcp = patch_getcp(model, brush, tmp.cp, tmp.maxcp, &tmp.patchinfo);\r\n\r\n\t\t\tif (mousetool == BT_PUSHFACE)\r\n\t\t\t{\r\n\t\t\t\tfor (facenum = 0; facenum < tmp.numfaces; facenum++)\r\n\t\t\t\t{\r\n\t\t\t\t\tif (facemask & (1ul<<facenum))\r\n\t\t\t\t\t\ttmp.faces[facenum].planedist += tmp.faces[facenum].planenormal * displace;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\telse if (mousetool == BT_MOVETEXTURE)\r\n\t\t\t{\r\n\t\t\t\tfor (facenum = 0; facenum < tmp.numfaces; facenum++)\r\n\t\t\t\t{\r\n\t\t\t\t\tif (facemask & (1ul<<facenum))\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\ttmp.faces[facenum].sbias -= tmp.faces[facenum].sdir * displace;\r\n\t\t\t\t\t\ttmp.faces[facenum].tbias -= tmp.faces[facenum].tdir * displace;\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\telse if (mousetool == BT_MOVE || mousetool == BT_CLONEDISPLACE)\r\n\t\t\t\tbrushface_translate(displace);\t\t\r\n\t\t\telse if (mousetool == BT_ROTATE)\r\n\t\t\t{\r\n\t\t\t\t//find the brush's middle (based on its bbox)\r\n\t\t\t\tbrush_getfacepoints(model, brush, 0, &mid, 1);\r\n\r\n\t\t\t\tmakevectors(col);\r\n\r\n\t\t\t\t//move it so its pivot is at the origin\r\n\t\t\t\tbrushface_translate(-mid);\r\n\t\t\t\t//rotate it (by v_forward etc)\r\n\t\t\t\tbrushface_rotate();\r\n\t\t\t\t//reposition it around its pivot again\r\n\t\t\t\tbrushface_translate(mid);\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t\tcontinue;\r\n\r\n\t\t\tif (0)//mousetool == BT_MOVETEXTURE)\r\n\t\t\t{\r\n\t\t\t\tDrawQCBrushTextured(tmp.faces, tmp.numfaces, '1 1 1', 0.3);\t//faded to give an idea, without stopping you from seeing what you're aligning it to.\r\n\r\n\t\t\t\t/*points = brush_calcfacepoints(model, tmp.faces, tmp.numfaces, facepoints, MAX_FACEPOINTS);\r\n\t\t\t\tif (points)\r\n\t\t\t\t{\r\n\t\t\t\t\t//this is unfortunate. the built in shaders expect to use lightmaps. we don't have those.\r\n\t\t\t\t\t//because lightmaps are special things, we end up in a real mess. so lets just make sure there's a shader now, because we can.\r\n\t\t\t\t\tshaderforname(tmp.faces[face-1].shadername,\r\n\t\t\t\t\t\tsprintf(\"{\"\r\n\t\t\t\t\t\t\t\"{\\n\"\r\n\t\t\t\t\t\t\t\t\"map \\\"%s.lmp\\\"\\n\"\r\n\t\t\t\t\t\t\t\t\"rgbgen vertex\\n\"\r\n\t\t\t\t\t\t\t\t\"alphagen vertex\\n\"\r\n\t\t\t\t\t\t\t\"}\\n\"\r\n\t\t\t\t\t\t\"}\", tmp.faces[face-1].shadername));\r\n\r\n\t\t\t\t\tcol = '0.7 0.7 0.7'; //fullbright is typically TOO bright. overbrights? meh!\r\n\t\t\t\t\tvector sz = drawgetimagesize(tmp.faces[face-1].shadername);\r\n\t\t\t\t\tR_BeginPolygon(tmp.faces[face-1].shadername);\r\n\t\t\t\t\tfor (point = 0; point < points; point++)\r\n\t\t\t\t\t\tR_PolygonVertex(facepoints[point] + tmp.faces[face-1].planenormal*0.1, [(facepoints[point] * tmp.faces[face-1].sdir + tmp.faces[face-1].sbias)/sz_x, (facepoints[point] * tmp.faces[face-1].tdir + tmp.faces[face-1].tbias)/sz_y], col, 1);\r\n\t\t\t\t\tR_EndPolygon();\r\n\t\t\t\t}*/\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\t//draw it wireframe WITH depth testing\r\n\t\t\t\tif (tmp.numcp)\r\n\t\t\t\t\tDrawQCPatchWireframe(tmp.cp, tmp.patchinfo, \"terrainedit\", '0 0 1', 1);\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\t//draw it wireframe\r\n\t\t\t\t\tfor(facenum = 0; facenum < tmp.numfaces;)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tpoints = brush_calcfacepoints(++facenum, tmp.faces, tmp.numfaces, facepoints, MAX_FACEPOINTS);\r\n\t\t\t\t\t\tif (!points)\r\n\t\t\t\t\t\t\tcontinue;\t//should probably warn somehow about this\r\n\t\t\t\t\t\t//should we use two colour channels? one depth one not?\r\n\t\t\t\t\t\tif (facemask & (1ul<<facenum))\r\n\t\t\t\t\t\t\tcol = '1 0 0';\r\n\t\t\t\t\t\telse\r\n\t\t\t\t\t\t\tcol = '0 0.5 0';\r\n\t\t\t\t\t\tR_BeginPolygon(\"chop\");\r\n\t\t\t\t\t\tR_PolygonVertex(facepoints[0], '0 0', col, 1);\r\n\t\t\t\t\t\tfor (point = 0; point < points-1; )\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tpoint++;\r\n\t\t\t\t\t\t\tR_PolygonVertex(facepoints[point], '0 0', col, 1);\r\n\t\t\t\t\t\t\tR_EndPolygon();\r\n\t\t\t\t\t\t\tR_PolygonVertex(facepoints[point], '0 0', col, 1);\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\tR_PolygonVertex(facepoints[0], '0 0', col, 1);\r\n\t\t\t\t\t\tR_EndPolygon();\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\tDrawQCBrushTextured(tmp.faces, tmp.numfaces, '1 1 1', 0.3);\t//faded to give an idea, without stopping you from seeing what you're aligning it to.\r\n\t\t\t\t\tDrawQCBrushWireframe(tmp.faces, tmp.numfaces, \"terrainedit\", '0 0 1', 1);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\tDebrushifyLite();\r\n\t\t\tDrawAxisExtensions(vertedit.p, vertedit.numverts, \"terrainedit\", '0 0 0.3', 1);\r\n\r\n\t\t\tif (!mousedown)\r\n\t\t\t{\r\n\t\t\t\tif (mousetool == BT_CLONEDISPLACE)\t//doesn't affect the original brush.\r\n\t\t\t\t{\r\n\t\t\t\t\tif (displace*displace > 1)\r\n\t\t\t\t\t\tbrush_history_create(model, tmp.faces, tmp.numfaces, tmp.contents, TRUE);\r\n\t\t\t\t}\r\n\t\t\t\telse if (tmp.numcp)\r\n\t\t\t\t\tpatch_history_edit(model, brush, tmp.cp, tmp.patchinfo);\r\n\t\t\t\telse\r\n\t\t\t\t\tbrush_history_edit(model, brush, tmp.faces, tmp.numfaces, tmp.contents);\r\n\t\t\t\tbt_points = 0;\r\n\t\t\t\tmousetool = BT_NONE;\r\n\t\t\t}\r\n\t\t}\r\n\t\tmemfree(oldselectedbrushes);\r\n\t}\r\n\telse if (selectedbrushcount)\r\n\t{\r\n\t\t/*if (brushtool == BT_PUSHFACE)\r\n\t\t{\t//selected face (not brush) follows cursor when we're not actively dragging a face\r\n\t\t\tfor(facenum = 0;;)\r\n\t\t\t{\r\n\t\t\t\tpoints = brush_getfacepoints(selectedbrushmodel, selectedbrush, ++facenum, facepoints, MAX_FACEPOINTS);\r\n\t\t\t\tif (!points)\r\n\t\t\t\t\tbreak;\t//end of face list, I guess\r\n\r\n\t\t\t\tmid = 0;\r\n\t\t\t\tfor (point = 0; point < points; point++)\r\n\t\t\t\t\tmid += facepoints[point];\t//FIXME: find the centroid for greater accuracy\r\n\t\t\t\tmid = project(mid * (1.0/points));\r\n\t\t\t\tdist = (mousepos_x - mid_x) * (mousepos_x - mid_x) + (mousepos_y - mid_y) * (mousepos_y - mid_y);\r\n\t\t\t\tif (facenum == 1 || dist < bestdist)\r\n\t\t\t\t{\r\n\t\t\t\t\tbestdist = dist;\r\n\t\t\t\t\tselectedbrushface = facenum;\r\n\t\t\t\t}\t\r\n\t\t\t}\r\n\t\t}*/\r\n\t\r\n\t\teditor_brushes_drawselected();\r\n\t}\r\n\r\n//\teditor_drawbbox(selectedbrush);\r\n\r\n\tvector t = mousefar;\r\n\tvector o = mousenear;\r\n\tif (vlen(o - t) > 8192 && !autocvar_ca_brush_view)\r\n\t\tt = o + normalize(t)*8192;\r\n\t*world_hitcontentsmaski = ~0;\t//I want to be able to select water...\r\n\ttraceline(o, t, TRUE, world);\r\n\t*world_hitcontentsmaski = 0;\t//don't break stuff\r\n\r\n#if 0\r\n\t//find all the brushes within 32qu of the mouse cursor's impact point\r\n\t//you can tweak this to find the nearest brush vertex efficiently (or snap it to a grid or so).\r\n\t//you can then slice through a brush by generating a plane between three points found this way and inserting it into the brush, clipping away the extra part.\r\n\t//(remember, the engine will automatically discard any degenerate planes)\r\n\tcol = '0 0 1';\r\n\r\n\t//generate the volume\r\n\tfor (facenum = 0; facenum < 6; facenum++)\r\n\t\tdists[facenum] = (trace_endpos * axis[facenum]) + 32;\r\n\r\n\tint brushnum, numbrushes = brush_findinvolume(selectedbrushmodel, axis, dists, dists.length, &brushlist[0], __NULL__, brushlist.length);\r\n\tfor (brushnum = 0; brushnum < numbrushes; brushnum++)\r\n\t{\r\n\t\tfor (facenum = 0; ; )\r\n\t\t{\r\n\t\t\tpoints = brush_getfacepoints(selectedbrushmodel, brushlist[brushnum], ++facenum, facepoints, MAX_FACEPOINTS);\r\n\t\t\tif (!points)\r\n\t\t\t\tbreak;\t//end of face list, I guess\r\n\r\n\t\t\tR_BeginPolygon(\"terrainedit\");\r\n\t\t\tfor (point = 0; point < points; point++)\r\n\t\t\t\tR_PolygonVertex(facepoints[point], '0 0', col, 1);\r\n\t\t\tR_EndPolygon();\r\n\t\t}\r\n\t}\r\n#endif\r\n\r\n\t//draw a line/triangle to show the selection.\r\n\tint showpoints = bt_points;\r\n\tif (brushtool == BT_SLICE && bt_points)\r\n\t{\r\n//\t\tbt_point[showpoints++] = brush_snappoint(trace_endpos);\r\n\t\tshowpoints = 3;\r\n\t}\r\n\tif (brushtool == BT_CREATEBRUSH || brushtool == BT_CREATEPATCH)\r\n\t\tbt_point[showpoints++] = brush_snappoint(trace_endpos);\r\n\r\n\t//FIXME: if slicing, draw the intersection\r\n\r\n\tif (showpoints > 1)\r\n\t{\r\n\t\tcol = '0 0 1';\r\n\r\n\t\tR_BeginPolygon(\"chop\");\r\n\t\tfor (point = 0; point < showpoints; point++)\r\n\t\t\tR_PolygonVertex(bt_point[point], '0 0', col, 1);\r\n\t\tR_EndPolygon();\r\n\t}\r\n\r\n\t//FIXME: if creating, draw a wireframe brush.\r\n};\r\n\r\nvoid(vector mousepos) editor_brushes_overlay =\r\n{\r\n\tint point;\r\n\tvector mid;\r\n\tfloat dist, bestdist;\r\n\tif (vertedit.numidx)\r\n\t{\r\n\t\tif (!mousedown)\r\n\t\t{\r\n\t\t\tfor(vertedit.selectedvert1 = -1, vertedit.selectedvert2 = -1, point = 0, bestdist = 16*16; point < vertedit.numverts; point++)\r\n\t\t\t{\r\n\t\t\t\tmid = project(vertedit.p[point]);\r\n\t\t\t\tdist = (mousepos_x - mid_x) * (mousepos_x - mid_x) + (mousepos_y - mid_y) * (mousepos_y - mid_y);\r\n\t\t\t\tif (dist < bestdist && mid_z > 0)\r\n\t\t\t\t{\r\n\t\t\t\t\tbestdist = dist;\r\n\t\t\t\t\tvertedit.selectedvert1 = point;\r\n\t\t\t\t}\t\r\n\t\t\t}\r\n\t\t\tfor(point = 0; point < vertedit.numidx; point+=3)\r\n\t\t\t{\r\n\t\t\t\tmid = project(0.5*(vertedit.p[vertedit.i[point+0]] + vertedit.p[vertedit.i[point+1]]));\r\n\t\t\t\tdist = (mousepos_x - mid_x) * (mousepos_x - mid_x) + (mousepos_y - mid_y) * (mousepos_y - mid_y);\r\n\t\t\t\tif (dist < bestdist && mid_z > 0)\r\n\t\t\t\t{\r\n\t\t\t\t\tbestdist = dist;\r\n\t\t\t\t\tvertedit.selectedvert1 = vertedit.i[point+0];\r\n\t\t\t\t\tvertedit.selectedvert2 = vertedit.i[point+1];\r\n\t\t\t\t}\r\n\t\t\t\t\r\n\t\t\t\tmid = project(0.5*(vertedit.p[vertedit.i[point+1]] + vertedit.p[vertedit.i[point+2]]));\r\n\t\t\t\tdist = (mousepos_x - mid_x) * (mousepos_x - mid_x) + (mousepos_y - mid_y) * (mousepos_y - mid_y);\r\n\t\t\t\tif (dist < bestdist && mid_z > 0)\r\n\t\t\t\t{\r\n\t\t\t\t\tbestdist = dist;\r\n\t\t\t\t\tvertedit.selectedvert1 = vertedit.i[point+1];\r\n\t\t\t\t\tvertedit.selectedvert2 = vertedit.i[point+2];\r\n\t\t\t\t}\r\n\t\t\t\t\r\n\t\t\t\tmid = project(0.5*(vertedit.p[vertedit.i[point+2]] + vertedit.p[vertedit.i[point+0]]));\r\n\t\t\t\tdist = (mousepos_x - mid_x) * (mousepos_x - mid_x) + (mousepos_y - mid_y) * (mousepos_y - mid_y);\r\n\t\t\t\tif (dist < bestdist && mid_z > 0)\r\n\t\t\t\t{\r\n\t\t\t\t\tbestdist = dist;\r\n\t\t\t\t\tvertedit.selectedvert1 = vertedit.i[point+2];\r\n\t\t\t\t\tvertedit.selectedvert2 = vertedit.i[point+0];\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t\tif (vertedit.selectedvert1 != -1)\r\n\t\t{\r\n\t\t\tmid = project(vertedit.p[vertedit.selectedvert1]);\r\n\t\t\tif (mid_z >= 0)\r\n\t\t\t\tdrawfill([mid_x,mid_y] - '2 2', [4,4], [0.2,0.2,1], 1, 0);\r\n\t\t}\r\n\t\tif (vertedit.selectedvert2 != -1)\r\n\t\t{\r\n\t\t\tmid = project(vertedit.p[vertedit.selectedvert2]);\r\n\t\t\tif (mid_z >= 0)\r\n\t\t\t\tdrawfill([mid_x,mid_y] - '2 2', [4,4], [0.2,0.2,1], 1, 0);\r\n\t\t}\r\n\t\t\r\n\t\tdrawrawstring('0 32 0', \"Vertex Editor\", '8 8 0', '1 1 1', 1);\r\n\t\treturn;\r\n\t}\r\n\r\n\tswitch(brushtool)\r\n\t{\r\n\tcase BT_CLONEDISPLACE:\r\n\t\tdrawrawstring('0 32 0', \"Clone\", '8 8 0', '1 1 1', 1);\r\n\t\tbreak;\r\n\tcase BT_CREATEDRAG:\r\n\t\tdrawrawstring('0 32 0', \"Drag+Create\", '8 8 0', '1 1 1', 1);\r\n\t\tbreak;\r\n\tcase BT_CREATEPATCH:\r\n\t\tif (autocvar_ca_explicitpatch)\r\n\t\t\tdrawrawstring('0 32 0', sprintf(\"Create Explicit %g*%g Patch\", autocvar_ca_cpwidth, autocvar_ca_cpheight), '8 8 0', '1 1 1', 1);\r\n\t\telse\r\n\t\t\tdrawrawstring('0 32 0', sprintf(\"Create Auto %g*%g Patch\", autocvar_ca_cpwidth, autocvar_ca_cpheight), '8 8 0', '1 1 1', 1);\r\n\t\tbreak;\r\n\tcase BT_CREATEBRUSH:\r\n\t\tdrawrawstring('0 32 0', \"Paint+Create\", '8 8 0', '1 1 1', 1);\r\n\t\tbreak;\r\n\tcase BT_SLICE:\r\n\t\tdrawrawstring('0 32 0', \"Slice Tool\", '8 8 0', '1 1 1', 1);\r\n\t\tif (!selectedbrushcount)\r\n\t\t\tbrushtool = BT_NONE;\r\n\t\tbreak;\r\n\tcase BT_PUSHFACE:\r\n\t\tdrawrawstring('0 32 0', \"Push Face\", '8 8 0', '1 1 1', 1);\r\n\t\tbreak;\r\n\tcase BT_MOVE:\r\n\t\tdrawrawstring('0 32 0', \"Move Brush\", '8 8 0', '1 1 1', 1);\r\n\t\tbreak;\r\n\tcase BT_ROTATE:\r\n\t\tdrawrawstring('0 32 0', \"Rotate Brush\", '8 8 0', '1 1 1', 1);\r\n\t\tbreak;\r\n\tcase BT_MOVETEXTURE:\r\n\t\tdrawrawstring('0 32 0', \"Move Texture\", '8 8 0', '1 1 1', 1);\r\n\t\tbreak;\r\n\tcase BT_VERTEXEDIT:\r\n\t\tdrawrawstring('0 32 0', \"Vertex Editor\", '8 8 0', '1 1 1', 1);\r\n\t\tbreak;\r\n\t}\r\n};\r\n\r\n#define brusheditormodes\r\n//#append brusheditormodes brusheditormode(\"move\", BT_MOVE)\r\n#append brusheditormodes brusheditormode(\"clone\", BT_CLONEDISPLACE)\r\n#append brusheditormodes brusheditormode(\"draw\", BT_CREATEDRAG)\r\n#append brusheditormodes brusheditormode(\"rotate\", BT_ROTATE)\r\n#append brusheditormodes brusheditormode(\"pushface\", BT_PUSHFACE)\r\n#append brusheditormodes brusheditormode(\"scrolltex\", BT_MOVETEXTURE)\r\n#append brusheditormodes brusheditormode(\"vertex\", BT_VERTEXEDIT)\r\n\r\nvoid() editor_brushes_registercommands =\r\n{\r\n\ttmp.maxcp = 64*64;\r\n\ttmp.cp = memalloc(sizeof(*tmp.cp)*tmp.maxcp);\r\n#define brusheditormode(n,v) registercommand(\"+brushedit_\"n);registercommand(\"-brushedit_\"n);\r\nbrusheditormodes\r\n#undef brusheditormode\r\n\tregistercommand(\"brushedit_undo\");\r\n\tregistercommand(\"brushedit_redo\");\r\n\tregistercommand(\"brushedit_delete\");\r\n\tregistercommand(\"brushedit_create\");\r\n\tregistercommand(\"brushedit_createpatch\");\r\n\tregistercommand(\"brushedit_slice\");\r\n\tregistercommand(\"brushedit_matchface\");\r\n\tregistercommand(\"brushedit_resettexcoords\");\r\n\tregistercommand(\"brushedit_settexture\");\r\n\tregistercommand(\"brushedit_scrolltex\");\r\n\tregistercommand(\"brushedit_subtract\");\r\n\tregistercommand(\"brushedit_binds\");\r\n\tregistercommand(\"brushedit_binds_default\");\r\n\tregistercommand(\"+brushedit_nogrid\");\r\n\tregistercommand(\"-brushedit_nogrid\");\r\n};\r\n\r\nfloat() editor_brushes_command =\r\n{\r\n\tswitch(argv(0))\r\n\t{\r\n#define brusheditormode(n,v) case \"+brushedit_\"n: brushtool = v; bt_points = 0; break;  case \"-brushedit_\"n: if (brushtool == v) brushtool = BT_NONE; break;\r\nbrusheditormodes\r\n#undef brusheditormode\r\n\r\n\tcase \"brushedit_undo\":\r\n\t\tbrush_undo();\r\n\t\tbreak;\r\n\tcase \"brushedit_redo\":\r\n\t\tbrush_undo();\r\n\t\tbreak;\r\n\tcase \"brushedit_create\":\r\n\t\tbrushtool = BT_CREATEBRUSH;\r\n\t\tbt_points = 0;\r\n\t\tbreak;\r\n\tcase \"brushedit_createpatch\":\r\n\t\tbrushtool = BT_CREATEPATCH;\r\n\t\tbt_points = 0;\r\n\t\tbreak;\r\n\tcase \"brushedit_slice\":\r\n\t\tbrushtool = BT_SLICE;\r\n\t\tbt_points = 0;\r\n\t\tbreak;\r\n\tcase \"brushedit_scrolltex\":\r\n\t\tbrushtool = BT_MOVETEXTURE;\r\n\t\tbt_points = 0;\r\n\t\tbreak;\r\n\t\t\r\n\tcase \"+brushedit_nogrid\": nogrid = TRUE; break;\r\n\tcase \"-brushedit_nogrid\": nogrid = FALSE; break;\r\n\r\n/*\tcase \"brushedit_matchface\":\r\n\t\tbrushtool = BT_NONE;\r\n\t\tbt_points = 0;\r\n\t\t\r\n\t\t\r\n\t\t{\r\n\t\t\tvector t = mousefar;\r\n\t\t\tvector o = mousenear;\r\n\t\t\tint i;\r\n\t\t\tif (vlen(o - t) > 8192 && !autocvar_ca_brush_view)\r\n\t\t\t\tt = o + normalize(t)*8192;\r\n\t\t\r\n\t\t\ttraceline(o, t, TRUE, world);\r\n\t\t\t\r\n\t\t\ti = mergebrushes(selectedbrushmodel, selectedbrush, trace_brush_id, selectedbrushface, trace_brush_faceid);\r\n\t\t\tif (i)\r\n\t\t\t{\r\n\t\t\t\tbrush_selected(selectedbrushmodel, selectedbrush, -1, FALSE);\r\n\t\t\t\tselectedbrush = i;\r\n\t\t\t\tselectedbrushface = 0;\r\n\t\t\t\tbrush_selected(selectedbrushmodel, selectedbrush, selectedbrushface, TRUE);\r\n\t\t\t}\r\n\t\t}\r\n\t\tbreak;\r\n*/\r\n\tcase \"brushedit_resettexcoords\":\r\n\t\tbrushedit_resettextures();\r\n\t\tbreak;\r\n\tcase \"brushedit_settexture\":\r\n\t\tbrushedit_settexture(argv(1));\r\n\t\tbreak;\r\n\tcase \"brushedit_subtract\":\r\n\t\tbrushedit_subtract();\r\n\t\tbreak;\r\n\tcase \"brushedit_delete\":\r\n\t\twhile(selectedbrushcount)\r\n\t\t{\r\n\t\t\tbrush_history_delete(selectedbrushes[0].model, selectedbrushes[0].id);\r\n\t\t}\r\n\t\tbreak;\r\n\tcase \"brushedit_binds_default\":\r\n\t\tlocalcmd(\"echo Setting default brusheditor bindings\\n\");\r\n\t\tlocalcmd(\"bind ctrl+z\t\tbrushedit_undo\\n\");\r\n\t\tlocalcmd(\"bind ctrl+y\t\tbrushedit_redo\\n\");\r\n\t\tlocalcmd(\"bind ctrl+i\t\tbrushedit_create\\n\");\r\n\t\tlocalcmd(\"bind ctrl+p\t\tbrushedit_createpatch\\n\");\r\n\t\tlocalcmd(\"bind ctrl+s\t\tbrushedit_slice\\n\");\r\n\t\tlocalcmd(\"bind ctrl+m\t\tbrushedit_matchface\\n\");\r\n\t\tlocalcmd(\"bind ctrl+del\t\tbrushedit_delete\\n\");\r\n\t\tlocalcmd(\"bind ctrl+backspace\tbrushedit_subtract\\n\");\r\n\t\tlocalcmd(\"bind ctrl+c\t\t+brushedit_clone\\n\");\r\n\t\tlocalcmd(\"bind ctrl+d\t\t+brushedit_draw\\n\");\r\n\t\tlocalcmd(\"bind ctrl+r\t\t+brushedit_rotate\\n\");\r\n\t\tlocalcmd(\"bind ctrl+f\t\t+brushedit_pushface\\n\");\r\n\t\tlocalcmd(\"bind ctrl+t\t\tbrushedit_scrolltex\\n\");\r\n\t\tlocalcmd(\"bind ctrl+v\t\t+brushedit_vertex\\n\");\r\n\t\tbreak;\r\n\tcase \"brushedit_binds\":\r\n\t\tlocalcmd(\"conmenu \\\"\\\"\\n\");\r\n\t\tfloat foo = 0;\r\n\t\tlocalcmd(sprintf(\"menutext 0 %g \\\"Set Default Bindings\\\" \\\"brushedit_binds_default\\\"\\n\", foo+=8));\r\n\t\tfoo+=8;\r\n\t\tlocalcmd(sprintf(\"menubind 0 %g \\\"brushedit_create\\\" \\\"Creation Tool\\\"\\n\", foo+=8));\r\n\t\tlocalcmd(sprintf(\"menubind 0 %g \\\"brushedit_slice\\\" \\\"Slice Tool\\\"\\n\", foo+=8));\r\n\t\tlocalcmd(sprintf(\"menubind 0 %g \\\"brushedit_delete\\\" \\\"Delete\\\"\\n\", foo+=8));\r\n\t\tlocalcmd(sprintf(\"menubind 0 %g \\\"brushedit_subtract\\\" \\\"Subtract\\\"\\n\", foo+=8));\r\n\t\tlocalcmd(sprintf(\"menubind 0 %g \\\"brushedit_undo\\\" \\\"Undo\\\"\\n\", foo+=8));\r\n\t\tlocalcmd(sprintf(\"menubind 0 %g \\\"brushedit_redo\\\" \\\"Redo\\\"\\n\", foo+=8));\r\n\t\tlocalcmd(sprintf(\"menubind 0 %g \\\"brushedit_nogrid\\\" \\\"Disable Grid\\\"\\n\", foo+=8));\r\n\t\t\r\n#define brusheditormode(n,v) localcmd(sprintf(\"menubind 0 %g \\\"+brushedit_\"n\"\\\" \\\"\"n\"\\\"\\n\", foo+=8));\r\nbrusheditormodes\r\n#undef brusheditormode\r\n\t\tbreak;\r\n\tdefault:\r\n\t\treturn FALSE;\r\n\t}\r\n\r\n\t//just in case.\r\n\tcvar_set(\"ca_show\", \"1\");\r\n\tcvar_set(\"ca_editormode\", ftos(MODE_BRUSHEDIT));\r\n\treturn TRUE;\r\n};\r\n\r\nvoid(entity e) editor_brush_set_entity\r\n{\r\n//\tfaesf = e;\r\n};\r\n\r\nfloat(float key, float unic, vector mousepos) editor_brushes_key =\r\n{\r\n\tbrushface_t *fa;\r\n\tvector t = mousefar;\r\n\tvector o = mousenear;\r\n\tint i;\r\n\r\n\tif (vlen(o - t) > 8192 && !autocvar_ca_brush_view)\r\n\t\tt = o + normalize(t)*8192;\r\n\tif (key == K_ESCAPE)\r\n\t{\r\n\t\tbt_points = 0;\r\n\t\tvertedit.numidx = 0;\r\n\t\tvertedit.numverts = 0;\r\n\t\tif (brushtool)\r\n\t\t\tbrushtool = BT_NONE;\r\n\t\telse\r\n\t\t\tbrush_deselectall();\r\n\t\treturn TRUE;\r\n\t}\r\n\tif (key == K_MOUSE1)\r\n\t{\r\n\t\tif (vertedit.numidx)\r\n\t\t{\r\n\t\t\tif (vertedit.selectedvert1 != -1)\r\n\t\t\t{\r\n\t\t\t\tmousedown |= 1;\r\n\t\t\t\tmousetool = BT_VERTEXEDIT;\r\n\t\t\t\tif (vertedit.selectedvert2 != -1)\r\n\t\t\t\t{\r\n\t\t\t\t\tbt_point[0] = 0.5 * (vertedit.p[vertedit.selectedvert1] + vertedit.p[vertedit.selectedvert2]);\r\n\t\t\t\t\tbt_point[1] = vertedit.p[vertedit.selectedvert1] - bt_point[0];\r\n\t\t\t\t\tbt_point[2] = vertedit.p[vertedit.selectedvert2] - bt_point[0];\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t\tbt_point[0] = vertedit.p[vertedit.selectedvert1];\r\n\t\t\t\t\tbt_point[1] = vertedit.p[vertedit.selectedvert1] - bt_point[0];\r\n\t\t\t\tbt_points = 1;\r\n\t\t\t\tbt_displace = '0 0 0';\r\n\t\t\t}\r\n\t\t\treturn TRUE;\r\n\t\t}\r\n\t\t\r\n\t\t*world_hitcontentsmaski = ~0;\t//I want to be able to select water...\r\n\t\ttraceline(o, t, TRUE, world);\r\n\t\t*world_hitcontentsmaski = 0;\t//don't break stuff.\r\n\t\tfloat tracemodel = trace_ent.modelindex;\r\n\r\n\t\t/*if (brushtool == BT_CREATEDRAG || (brushtool == BT_PUSHFACE && selectedbrushface))\r\n\t\t{\r\n\t\t\ttrace_brush_faceid = selectedbrushface;\r\n\t\t\ttrace_brush_id = selectedbrush;\r\n\t\t\ttracemodel = selectedbrushmodel;\r\n\t\t}\r\n\t\telse*/ if (brushtool != BT_PUSHFACE && brushtool != BT_MOVETEXTURE)\r\n\t\t\ttrace_brush_faceid = 0;\r\n\t\t\t\r\n\t\tif (brushtool == BT_SLICE || brushtool == BT_CREATEBRUSH || brushtool == BT_CREATEPATCH)\r\n\t\t{\r\n\t\t\tif (brushtool == BT_SLICE && bt_points > 2)\r\n\t\t\t\tbt_points = 2;\r\n\t\t\tif (brushtool == BT_CREATEPATCH && bt_points > 3)\r\n\t\t\t\tbt_points = 3;\r\n\r\n//FIXME: BT_CREATE is planar. so keep the points planar too.\r\n//FIXME: need a way to move points already placed\r\n//FIXME: create needs to ensure verts are clockwise and convex.\r\n\t\t\tif (bt_points < bt_point.length)\r\n\t\t\t{\r\n\t\t\t\tbt_point[bt_points] = brush_snappoint(trace_endpos);\r\n\t\t\t\tbt_points++;\r\n\t\t\t}\r\n\r\n\t\t\tif (brushtool == BT_SLICE && bt_points == 1)\r\n\t\t\t{\t//slice makes assumptions about the brush, so that you don't have to move to the other side of it first.\r\n\t\t\t\t//it ALWAYS draws 3 points if any are defined, so make sure 2+3 have valid locations once point 1 is defined.\r\n\t\t\t\tint majoraxis;\r\n\t\t\t\ttraceline(o, t, TRUE, world);\r\n\r\n\t\t\t\tstatic vector bbox[2];\r\n\t\t\t\tbrush_getfacepoints(tracemodel, trace_brush_id, 0, bbox, bbox.length);\r\n\t\r\n\t\t\t\tt[0] = fabs(trace_plane_normal[0]);\r\n\t\t\t\tt[1] = fabs(trace_plane_normal[1]);\r\n\t\t\t\tt[2] = fabs(trace_plane_normal[2]);\r\n\t\t\t\tif (t[2] > t[0] && t[2] > t[1]) \r\n\t\t\t\t\tmajoraxis = 2;\r\n\t\t\t\telse if (t[1] > t[0])\r\n\t\t\t\t\tmajoraxis = 1;\r\n\t\t\t\telse\r\n\t\t\t\t\tmajoraxis = 0;\r\n\r\n\t\t\t\tbt_point[1] = bt_point[0];\r\n\t\t\t\tbt_point[1][majoraxis] = bbox[trace_plane_normal[majoraxis]<0][majoraxis];\r\n\t\t\t\tmajoraxis = !majoraxis;\r\n\t\t\t\tbt_point[2] = bt_point[0];\r\n\t\t\t\tbt_point[2][majoraxis] = bbox[trace_plane_normal[majoraxis]<0][majoraxis];\r\n\t\t\t}\r\n\t\t}\r\n\t\t//FIXME: selecting a brush by face should select either the front or the back. ideally depending on which one is closest to its respective face center, I suppose.\r\n\t\telse\r\n\t\t{\r\n\t\t\tif (shiftdown)\r\n\t\t\t{\t//simple selection toggle of the clicked brush\r\n\t\t\t\tif (!tracemodel || !trace_brush_id)\r\n\t\t\t\t{\r\n\t\t\t\t}\r\n\t\t\t\telse if (brushface_isselected(tracemodel, trace_brush_id, trace_brush_faceid))\r\n\t\t\t\t\tbrushface_deselect(tracemodel, trace_brush_id, trace_brush_faceid);\r\n\t\t\t\telse\r\n\t\t\t\t\tbrushface_select(tracemodel, trace_brush_id, trace_brush_faceid);\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\t//deselect everything but the targetted brush.\r\n\t\t\t\tif (!tracemodel || !trace_brush_id)\r\n\t\t\t\t\tbrush_deselectall();\r\n\t\t\t\telse if (!brush_isselected(tracemodel, trace_brush_id))\r\n\t\t\t\t{\r\n\t\t\t\t\tbrush_deselectall();\r\n\t\t\t\t\tbrushface_select(tracemodel, trace_brush_id, trace_brush_faceid);\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t{\t//its already selected, start doing whatever the current mouse action is meant to be\r\n\t\t\t\t\tmousedown |= 1;\r\n\t\t\t\t\tmousetool = brushtool;\r\n\t\t\t\t\tvertedit.selectedvert1 = -1;\r\n\t\t\t\t\tvertedit.selectedvert2 = -1;\r\n\t\t\t\t\tbt_point[0] = brush_snappoint(trace_endpos);\r\n\t\t\t\t\tbt_points = 1;\r\n\t\t\t\t\tbt_displace = '0 0 0';\r\n\t\t\t\t}\r\n\t\t\t\t\r\n\t\t\t\tif (brushtool == BT_VERTEXEDIT && !vertedit.numidx && trace_brush_id)\r\n\t\t\t\t{\r\n\t\t\t\t\tmousedown = FALSE;\r\n\t\t\t\t\tif (brush_isselected(tracemodel, trace_brush_id))\r\n\t\t\t\t\t\tDebrushify(&vertedit, tracemodel, trace_brush_id);\r\n\t\t\t\t}\r\n\t\t\t}\r\n/*\r\n\t\t\t\r\n\t\t\t if (trace_brush_id != selectedbrush || selectedbrushface != trace_brush_faceid || selectedbrushmodel != tracemodel)\r\n\t\t\t{\r\n\t\t\t\tbrush_selected(selectedbrushmodel, selectedbrush, -1, FALSE);\r\n\t\t\t\tif (!shiftdown)\r\n\t\t\t\t{\r\n\t\t\t\t\tif (!brush_isselected(tracemodel, trace_brush_id))\r\n\t\t\t\t\t\tbrush_deselectall();\r\n\t\t\t\t}\r\n\t\t\t\telse if (brush_deselect(tracemodel, trace_brush_id))\r\n\t\t\t\t\treturn TRUE;\r\n\t\t\t\tbrush_select(tracemodel, trace_brush_id);\r\n\t\t\t\tselectedbrush = trace_brush_id;\r\n\t\t\t\tselectedbrushface = trace_brush_faceid;\r\n\t\t\t\tselectedbrushmodel = tracemodel;\r\n\t\t\t\tif (trace_brush_faceid)\r\n\t\t\t\t\tbrush_selected(selectedbrushmodel, selectedbrush, selectedbrushface, TRUE);\r\n\t\t\t\t\r\n\t\t\t\tvertedit.numidx = 0;\r\n\t\t\t\tvertedit.numverts = 0;\r\n\t\t\t\t\r\n\t\t\t\tif (selectedbrushface && (brushtool == BT_PUSHFACE || brushtool == BT_MOVETEXTURE))\r\n\t\t\t\t{\r\n\t\t\t\t\tmousedown |= 1;\r\n\t\t\t\t\tmousetool = brushtool;\r\n\t\t\t\t\tvertedit.selectedvert1 = -1;\r\n\t\t\t\t\tvertedit.selectedvert2 = -1;\r\n\t\t\t\t\tbt_point[0] = brush_snappoint(trace_endpos);\r\n\t\t\t\t\tbt_points = 1;\r\n\t\t\t\t\tbt_displace = '0 0 0';\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\telse if (selectedbrush == trace_brush_id && selectedbrushface == trace_brush_faceid && selectedbrushmodel == tracemodel)\r\n\t\t\t{\r\n\t\t\t\tmousedown = TRUE;\r\n\t\t\t\tmousetool = brushtool;\r\n\t\t\t\tvertedit.selectedvert1 = -1;\r\n\t\t\t\tvertedit.selectedvert2 = -1;\r\n\t\t\t\tbt_point[0] = brush_snappoint(trace_endpos);\r\n\t\t\t\tbt_points = 1;\r\n\t\t\t\tbt_displace = '0 0 0';\r\n\t\t\t}\r\n*/\r\n\t\t}\r\n\t\treturn TRUE;\r\n\t}\r\n\r\n\tif (key == K_ENTER)\r\n\t{\r\n\t\tif (vertedit.numidx)\r\n\t\t{\r\n\t\t\tRebrushify(&vertedit, FALSE);\r\n\t\t\tmousetool = BT_NONE;\r\n\t\t\tbt_points = 0;\r\n\t\t\tvertedit.numidx = 0;\r\n\t\t\tvertedit.numverts = 0;\r\n\t\t\tmousedown = FALSE;\r\n\t\t\treturn TRUE;\r\n\t\t}\r\n\t\telse if (brushtool == BT_CREATEPATCH)\r\n\t\t{\r\n\t\t\tif (bt_points == 4)\r\n\t\t\t{\r\n\t\t\t\tint x,y;\r\n\t\t\t\tpatchvert_t *vert;\r\n\t\t\t\tpatchvert_t *cp;\r\n\t\t\t\tpatchinfo_t patchinfo = {autocvar_ca_newbrushtexture, CONTENTBIT_SOLID, max(2,autocvar_ca_cpwidth), max(2,autocvar_ca_cpheight), 0, 0, '0 0 0'};\r\n\t\t\t\ttmp.numfaces = 0;\r\n\t\t\t\ttmp.numcp = 0;\r\n\t\t\t\tbt_points = 0;\r\n\r\n\t\t\t\tif (!autocvar_ca_explicitpatch)\r\n\t\t\t\t{\t//must be odd sizes. cos grr.\r\n\t\t\t\t\tpatchinfo.cpwidth|=1;\r\n\t\t\t\t\tpatchinfo.cpheight|=1;\r\n\t\t\t\t\tpatchinfo.tesswidth = patchinfo.tessheight = -1; //proper q3 patch, engine decides according to smoothness (which means nothing quite fits).\r\n\t\t\t\t}\r\n\t\t\t\tif (patchinfo.cpwidth*patchinfo.cpheight>64*64)\r\n\t\t\t\t{\r\n\t\t\t\t\tcprint(sprintf(\"%i*%i patch dimensions too large\", patchinfo.cpwidth, patchinfo.cpheight));\r\n\t\t\t\t\treturn TRUE;\r\n\t\t\t\t}\r\n\r\n\t\t\t\t//populate the CPs, averaging from the 4 specified coords.\r\n\t\t\t\tcp = vert = memalloc(sizeof(*vert) * patchinfo.cpwidth*patchinfo.cpheight);\r\n\t\t\t\tfor (y = 0; y < patchinfo.cpheight; y++)\r\n\t\t\t\tfor (x = 0; x < patchinfo.cpwidth; x++)\r\n\t\t\t\t{\r\n\t\t\t\t\tcp->s = x/(patchinfo.cpwidth-1);\r\n\t\t\t\t\tcp->t = y/(patchinfo.cpheight-1);\r\n\t\t\t\t\tvector top = bt_point[0]+(bt_point[1]-bt_point[0])*cp->s;\r\n\t\t\t\t\tvector bot = bt_point[3]+(bt_point[2]-bt_point[3])*cp->s;\r\n\t\t\t\t\tcp->xyz = top+(bot-top)*cp->t;\r\n\t\t\t\t\tcp->rgb = '1 1 1'; cp->a = 1;\r\n\t\t\t\t\tcp++;\r\n\t\t\t\t}\r\n\r\n\t\t\t\tbrush_deselectall();\r\n\t\t\t\tpatch_history_create(selectedbrushmodel, vert, patchinfo, TRUE);\r\n\t\t\t\tmemfree(vert);\r\n\t\t\t}\r\n\t\t\treturn TRUE;\r\n\t\t}\r\n\t\telse if (brushtool == BT_CREATEBRUSH)\r\n\t\t{\r\n\t\t\tif (bt_points >= 3)\r\n\t\t\t{\r\n\t\t\t\ttmp.numfaces = BrushFromPoints(tmp.faces, tmp.faces.length, bt_point, bt_points, autocvar_ca_newbrushheight);\r\n\t\t\t\ttmp.numcp = 0;\r\n\t\t\t\tbt_points = 0;\r\n\t\t\t\tbrush_deselectall();\r\n\t\t\t\tbrush_history_create(selectedbrushmodel, tmp.faces, tmp.numfaces, CONTENTBIT_SOLID, TRUE);\r\n\t\t\t}\r\n\t\t\treturn TRUE;\r\n\t\t}\r\n\t\telse if (brushtool == BT_SLICE)\r\n\t\t{\t//save off the selected brushes so we can butcher the list, and select ONLY the sliced brushes on one side of the slice plane.\r\n\t\t\tint oldselectedbrushcount = selectedbrushcount;\r\n\t\t\tselbrush_t *oldselectedbrushes = memalloc(sizeof(selbrush_t)*selectedbrushcount);\r\n\t\t\tmemcpy(oldselectedbrushes, selectedbrushes, selectedbrushcount*sizeof(selbrush_t));\r\n\t\t\tbrush_deselectall();\r\n\t\t\tfor (int sb = 0; sb < oldselectedbrushcount; sb++)\r\n\t\t\t{\r\n\t\t\t\tint model = oldselectedbrushes[sb].model;\r\n\t\t\t\tint brush = oldselectedbrushes[sb].id;\r\n\t\t\t\t//get the current faces\r\n\t\t\t\ttmp.numfaces = brush_get(model, brush, tmp.faces, tmp.faces.length-1, &tmp.contents);\r\n\t\t\t\tif (!tmp.numfaces)\r\n\t\t\t\t\tcontinue;\t//can't slice patches\r\n\r\n\t\t\t\t//generate a new face plane\r\n\t\t\t\tfa = &tmp.faces[tmp.numfaces];\r\n\t\t\t\tfa->planenormal = normalize(crossproduct(bt_point[2] - bt_point[0], bt_point[1] - bt_point[0]));\r\n\t\t\t\tfa->planedist = bt_point[0] * fa->planenormal;\r\n\t\t\t\tfa->shadername = tmp.faces[0].shadername;\t//find a neighbour?\r\n\r\n\t\t\t\t//make sure its not coplanar with some other surface. such slices are silly\r\n\t\t\t\tfor (i = 0; i < tmp.numfaces; i++)\r\n\t\t\t\t{\r\n\t\t\t\t\tif (tmp.faces[i].planenormal == fa->planenormal && tmp.faces[i].planedist == fa->planedist)\r\n\t\t\t\t\t\tbreak;\r\n\t\t\t\t}\r\n\t\t\t\tif (i < tmp.numfaces)\r\n\t\t\t\t\tcontinue;\r\n\t\t\t\ttmp.numfaces++;\r\n\r\n\t\t\t\treset_texturecoords(fa);\t\t\r\n\r\n\t\t\t\t//delete the old one and insert the new\r\n\t\t\t\tbrush_history_edit(model, brush, tmp.faces, tmp.numfaces, tmp.contents);\r\n\r\n\t\t\t\t//and insert another new one too, because inserting a plane like this generates two fragments and I'm too lazy to work out which is the front and which is the back.\r\n\t\t\t\tfa->planenormal *= -1;\r\n\t\t\t\tfa->planedist *= -1;\r\n\t\t\t\tbrush_history_create(model, tmp.faces, tmp.numfaces, tmp.contents, TRUE);\r\n\t\t\t}\r\n\t\t\tmemfree(oldselectedbrushes);\r\n\t\t\tbt_points = 0;\r\n\t\t\treturn TRUE;\r\n\t\t}\r\n\t\telse return FALSE;\r\n\t}\r\n\r\n\treturn FALSE;\r\n};\r\n"
  },
  {
    "path": "quakec/csaddon/src/editor_ents.qc",
    "content": "static int selectedent;\r\nstatic entity tempent;\r\nstatic string editkey;\r\nstatic int editfieldtype;\t//0 = not editing. 1 = editing the fieldname. 2 = editing the value\r\n\r\ntypedef struct\r\n{\r\n\t//quick access for rendering.\r\n\tfloat modelindex;\r\n\tfloat isbsp;\r\n\tfloat alpha;\r\n\tvector colourmod;\r\n\tvector org;\r\n\tvector ang;\r\n\tvector mins;\r\n\tvector maxs;\r\n\tfloat scale;\r\n\t\r\n\ttrisoup_simple_vert_t bboxverts[8];\r\n\r\n\t//full data\r\n\thashtable fields;\r\n} entedit_t;\r\n\r\nint bbox_line_idxs[] =\r\n{\r\n\t0,2, 2,3, 3,1, 1,0,\r\n\t4,6, 6,7, 7,5, 5,4,\r\n\t0,4, 3,7, 2,6, 1,5\r\n};\r\n\r\nstatic entedit_t *editents;\r\nstatic int numents;\r\n\r\nstruct\r\n{\t//FIXME: should probably parse quakeed comments or something.\r\n\t//FIXME: bbox colours.\r\n\tstring classn;\r\n\tstring model;\r\n\tvector colour;\r\n\tvector mins, maxs;\r\n\tfloat spawnflags_match;\r\n\tfloat spawnflags_mask;\r\n} entclasses[] =\r\n{\r\n\t{\"info_player_start\", \t\t\t\"progs/player.mdl\",\t'0 1 0',\t'-16 -16 -24',\t'16 16 32'},\r\n\t{\"info_player_start\", \t\t\t\"progs/player.mdl\",\t'1 0 0',\t'-16 -16 -24',\t'16 16 32'},\r\n\t{\"info_player_start2\",\t\t\t\"progs/player.mdl\",\t'1 0 0',\t'-16 -16 -24',\t'16 16 32'},\r\n\t{\"info_player_deathmatch\",\t\t\"progs/player.mdl\",\t'1 0 1',\t'-16 -16 -24',\t'16 16 32'},\r\n\t{\"info_player_coop\", \t\t\t\"progs/player.mdl\",\t'1 0 1',\t'-16 -16 -24',\t'16 16 32'},\r\n\r\n\t{\"item_armor1\", \t\t\t\"progs/armor.mdl\",\t'0 0.5 0.8',\t'-16 -16 0',\t'16 16 56'},\r\n\t{\"item_armor2\", \t\t\t\"progs/armor.mdl\",\t'0 0.5 0.8',\t'-16 -16 0',\t'16 16 56'},\r\n\t{\"item_armorInv\", \t\t\t\"progs/armor.mdl\",\t'0 0.5 0.8',\t'-16 -16 0',\t'16 16 56'},\r\n\r\n\t{\"weapon_supershotgun\", \t\t\"progs/g_shot.mdl\",\t'0 0.5 0.8',\t'-16 -16 0',\t'16 16 56'},\r\n\t{\"weapon_nailgun\", \t\t\t\"progs/g_nail.mdl\",\t'0 0.5 0.8',\t'-16 -16 0',\t'16 16 56'},\r\n\t{\"weapon_supernailgun\", \t\t\"progs/g_nail2.mdl\",\t'0 0.5 0.8',\t'-16 -16 0',\t'16 16 56'},\r\n\t{\"weapon_grenadelauncher\",\t\t\"progs/g_rock.mdl\",\t'0 0.5 0.8',\t'-16 -16 0',\t'16 16 56'},\r\n\t{\"weapon_rocketlauncher\", \t\t\"progs/g_rock2.mdl\",\t'0 0.5 0.8',\t'-16 -16 0',\t'16 16 56'},\r\n\t{\"weapon_lightning\", \t\t\t\"progs/g_light.mdl\",\t'0 0.5 0.8',\t'-16 -16 0',\t'16 16 56'},\r\n\r\n\t{\"item_key1\", \t\t\t\t\"progs/w_s_key.mdl\",\t'0 0.5 0.8',\t'-16 -16 -24',\t'16 16 32'},\r\n\t{\"item_key2\", \t\t\t\t\"progs/w_g_key.mdl\",\t'0 0.5 0.8',\t'-16 -16 -24',\t'16 16 32'},\r\n\t{\"item_sigil\", \t\t\t\t\"progs/end1.mdl\",\t'0 0.5 0.8',\t'-16 -16 -24',\t'16 16 32'},\r\n\r\n\t{\"item_artifact_invulnerability\", \t\"progs/invulner.mdl\",\t'0 0.5 0.8',\t'-16 -16 -24',\t'16 16 32'},\r\n\t{\"item_artifact_envirosuit\", \t\t\"progs/suit.mdl\",\t'0 0.5 0.8',\t'-16 -16 -24',\t'16 16 32'},\r\n\t{\"item_artifact_invisibility\", \t\t\"progs/invisibl.mdl\",\t'0 0.5 0.8',\t'-16 -16 -24',\t'16 16 32'},\r\n\t{\"item_artifact_super_damage\", \t\t\"progs/quaddama.mdl\",\t'0 0.5 0.8',\t'-16 -16 -24',\t'16 16 32'},\r\n\r\n\r\n\r\n//\t{\"func_button\",\t\t\t\t\"*\",\t\t\t'0 .5 .8', \t?\r\n//\t{\"trigger_changelevel\",\t\t\t\"*\",\t\t\t'0.5 0.5 0.5'\t? //NO_INTERMISSION\r\n//\t{\"func_door\",\t\t\t\t\"*\",\t\t\t'0 .5 .8,'\t\t? //START_OPEN x DOOR_DONT_LINK GOLD_KEY SILVER_KEY TOGGLE\r\n//\t{\"func_door_secret\",\t\t\t\"*\",\t\t\t'0 .5 .8',\t\t? //open_once 1st_left 1st_down no_shoot always_shoot\r\n//\t{\"trigger_portaldespawn\",\t\t\"*\",\t\t\t'.5 .5 .5',\t?\r\n//\t{\"func_wall\",\t\t\t\t\"*\",\t\t\t'0 .5 .8', \t?\r\n//\t{\"func_illusionary\",\t\t\t\"*\",\t\t\t'0 .5 .8',\t\t?\r\n//\t{\"func_episodegate\",\t\t\t\"*\",\t\t\t'0 .5 .8',\t\t? //E1 E2 E3 E4\r\n//\t{\"func_bossgate\",\t\t\t\"*\",\t\t\t'0 .5 .8',\t\t?\r\n//\t{\"func_plat\",\t\t\t\t\"*\",\t\t\t'0 .5 .8'\t\t? PLAT_LOW_TRIGGER\r\n//\t{\"func_train\",\t\t\t\t\"*\",\t\t\t'0 .5 .8'\t\t?\r\n//\t{\"trigger_multiple\",\t\t\t\"*\",\t\t\t'.5 .5 .5' \t? notouch\r\n//\t{\"trigger_once\",\t\t\t\"*\",\t\t\t'.5 .5 .5' \t? notouch\r\n//\t{\"trigger_secret\",\t\t\t\"*\",\t\t\t'.5 .5 .5' \t?\r\n//\t{\"trigger_counter\",\t\t\t\"*\",\t\t\t'.5 .5 .5' \t? nomessage\r\n//\t{\"trigger_teleport\",\t\t\t\"*\",\t\t\t'.5 .5 .5' \t? PLAYER_ONLY SILENT\r\n//\t{\"trigger_setskill\",\t\t\t\"*\",\t\t\t'.5 .5 .5' \t?\r\n//\t{\"trigger_onlyregistered\",\t\t\"*\",\t\t\t'.5 .5 .5' \t?\r\n//\t{\"trigger_hurt\",\t\t\t\"*\",\t\t\t'.5 .5 .5' \t?\r\n//\t{\"trigger_push\",\t\t\t\"*\",\t\t\t'.5 .5 .5' \t? PUSH_ONCE\r\n//\t{\"trigger_monsterjump\",\t\t\t\"*\",\t\t\t'.5 .5 .5', \t?\r\n//\t{\"trigger_motionsickness\",\t\t\"*\",\t\t\t'.5 .5 .5', \t?\r\n//\t{\"worldspawn\",\t\t\t\t\"*\",\t\t\t'0 0 0) ?\r\n\r\n\t{\"path_corner\",\t\t\t\t0,\t\t\t'0.5 0.3 0', \t'-8 -8 -8',\t'8 8 8'},\r\n\t{\"event_lightning\",\t\t\t0,\t\t\t'0 1 1', \t'-16 -16 -16',\t'16 16 16'},\r\n\t{\"info_intermission\",\t\t\t0,\t\t\t'1 0.5 0.5', \t'-16 -16 -16',\t'16 16 16'},\r\n\t{\"misc_portalspawn\",\t\t\t0,\t\t\t'.5 .5 .5', \t'-8 -8 -8',\t'8 8 8'},\r\n\t{\"noclass\",\t\t\t\t0,\t\t\t'0 0 0', \t'-8 -8 -8',\t'8 8 8'},\r\n\t{\"item_health\",\t\t\t\t\"maps/b_bh25.bsp\",\t'.3 .3 1', \t'0 0 0',\t'32 32 32'}, //rotten megahealth\r\n\t{\"item_shells\",\t\t\t\t\"maps/b_shell0.bsp\",\t'0 .5 .8', \t'0 0 0',\t'32 32 32'}, //big\r\n\t{\"item_spikes\",\t\t\t\t\"maps/b_nail0.bsp\",\t'0 .5 .8', \t'0 0 0',\t'32 32 32'}, //big\r\n\t{\"item_rockets\",\t\t\t\"maps/b_rock0.bsp\",\t'0 .5 .8', \t'0 0 0',\t'32 32 32'}, //big\r\n\t{\"item_cells\",\t\t\t\t\"maps/b_batt0.bsp\",\t'0 .5 .8', \t'0 0 0',\t'32 32 32'}, //big\r\n\t{\"item_weapon\",\t\t\t\t0,\t\t\t'0 .5 .8', \t'0 0 0',\t'32 32 32'}, //shotgun rocket spikes big\r\n\t{\"info_null\",\t\t\t\t0,\t\t\t'0 0.5 0', \t'-4 -4 -4',\t'4 4 4'},\r\n\t{\"info_notnull\",\t\t\t0,\t\t\t'0 0.5 0', \t'-4 -4 -4',\t'4 4 4'},\r\n\t{\"light\",\t\t\t\t0,\t\t\t'0 1 0', \t'-8 -8 -8',\t'8 8 8'}, //START_OFF\r\n\t{\"light_fluoro\",\t\t\t0,\t\t\t'0 1 0', \t'-8 -8 -8',\t'8 8 8'},//START_OFF\r\n\t{\"light_fluorospark\",\t\t\t0,\t\t\t'0 1 0', \t'-8 -8 -8',\t'8 8 8'},\r\n\t{\"light_globe\",\t\t\t\t0,\t\t\t'0 1 0', \t'-8 -8 -8',\t'8 8 8'},\r\n\t{\"light_torch_small_walltorch\",\t\t0,\t\t\t'0 .5 0', \t'-10 -10 -20',\t'10 10 20'},\r\n\t{\"light_flame_large_yellow\",\t\t0,\t\t\t'0 1 0', \t'-10 -10 -12',\t'12 12 18'},\r\n\t{\"light_flame_small_yellow\",\t\t0,\t\t\t'0 1 0', \t'-8 -8 -8',\t'8 8 8'}, //START_OFF\r\n\t{\"light_flame_small_white\",\t\t0,\t\t\t'0 1 0', \t'-10 -10 -40',\t'10 10 40'}, //START_OFF\r\n\t{\"misc_fireball\",\t\t\t0,\t\t\t'0 .5 .8', \t'-8 -8 -8',\t'8 8 8'},\r\n\t{\"misc_explobox\",\t\t\t0,\t\t\t'0 .5 .8', \t'0 0 0',\t'32 32 64'},\r\n\t{\"misc_explobox2\",\t\t\t0,\t\t\t'0 .5 .8', \t'0 0 0',\t'32 32 64'},\r\n\t{\"trap_spikeshooter\",\t\t\t0,\t\t\t'0 .5 .8', \t'-8 -8 -8',\t'8 8 8'}, //superspike laser\r\n\t{\"trap_shooter\",\t\t\t0,\t\t\t'0 .5 .8', \t'-8 -8 -8',\t'8 8 8'}, //superspike laser\r\n\t{\"air_bubbles\",\t\t\t\t0,\t\t\t'0 .5 .8', \t'-8 -8 -8',\t'8 8 8'},\r\n\t{\"viewthing\",\t\t\t\t0,\t\t\t'0 .5 .8', \t'-8 -8 -8',\t'8 8 8'},\r\n\t{\"ambient_suck_wind\",\t\t\t0,\t\t\t'0.3 0.1 0.6', \t'-10 -10 -8',\t'10 10 8'},\r\n\t{\"ambient_drone\",\t\t\t0,\t\t\t'0.3 0.1 0.6', \t'-10 -10 -8',\t'10 10 8'},\r\n\t{\"ambient_flouro_buzz\",\t\t\t0,\t\t\t'0.3 0.1 0.6', \t'-10 -10 -8',\t'10 10 8'},\r\n\t{\"ambient_drip\",\t\t\t0,\t\t\t'0.3 0.1 0.6', \t'-10 -10 -8',\t'10 10 8'},\r\n\t{\"ambient_comp_hum\",\t\t\t0,\t\t\t'0.3 0.1 0.6', \t'-10 -10 -8',\t'10 10 8'},\r\n\t{\"ambient_thunder\",\t\t\t0,\t\t\t'0.3 0.1 0.6', \t'-10 -10 -8',\t'10 10 8'},\r\n\t{\"ambient_light_buzz\",\t\t\t0,\t\t\t'0.3 0.1 0.6', \t'-10 -10 -8',\t'10 10 8'},\r\n\t{\"ambient_swamp1\",\t\t\t0,\t\t\t'0.3 0.1 0.6', \t'-10 -10 -8',\t'10 10 8'},\r\n\t{\"ambient_swamp2\",\t\t\t0,\t\t\t'0.3 0.1 0.6', \t'-10 -10 -8',\t'10 10 8'},\r\n\t{\"misc_noisemaker\",\t\t\t0,\t\t\t'1 0.5 0', \t'-10 -10 -10',\t'10 10 10'},\r\n\t{\"misc_teleporttrain\",\t\t\t0,\t\t\t'0 .5 .8', \t'-8 -8 -8',\t'8 8 8'},\r\n\t{\"trigger_relay\",\t\t\t0,\t\t\t'.5 .5 .5', \t'-8 -8 -8', \t'8 8 8'},\r\n\t{\"info_teleport_destination\",\t\t0,\t\t\t'.5 .5 .5', \t'-8 -8 -8', \t'8 8 32'},\r\n\r\n\t{\"monster_boss\",\t\t\t\"progs/boss.mdl\",\t'1 0 0', \t'-128 -128 -24','128 128 256'},\r\n\t{\"monster_hell_knight\",\t\t\t\"progs/hknight.mdl\",\t'1 0 0', \t'-16 -16 -24',\t'16 16 40'}, //Ambush\r\n\t{\"monster_demon1\",\t\t\t\"progs/demon.mdl\",\t'1 0 0', \t'-32 -32 -24',\t'32 32 64'}, //Ambush\r\n\t{\"monster_dog\",\t\t\t\t\"progs/dog.mdl\",\t'1 0 0', \t'-32 -32 -24',\t'32 32 40'}, //Ambush\r\n\t{\"monster_enforcer\",\t\t\t\"progs/enforcer.mdl\",\t'1 0 0', \t'-16 -16 -24',\t'16 16 40'}, //Ambush\r\n\t{\"monster_fish\",\t\t\t\"progs/fish.mdl\",\t'1 0 0', \t'-16 -16 -24',\t'16 16 24'}, //Ambush\r\n\t{\"monster_ogre\",\t\t\t\"progs/ogre.mdl\",\t'1 0 0', \t'-32 -32 -24', \t'32 32 64'}, //Ambush\r\n\t{\"monster_oldone\",\t\t\t\"progs/oldone.mdl\",\t'1 0 0', \t'-16 -16 -24', \t'16 16 32'},\r\n\t{\"monster_knight\",\t\t\t\"progs/knight.mdl\",\t'1 0 0', \t'-16 -16 -24',\t'16 16 40'}, //Ambush\r\n\t{\"monster_shalrath\",\t\t\t\"progs/shalrath.mdl\",\t'1 0 0', \t'-32 -32 -24',\t'32 32 48'}, //Ambush\r\n\t{\"monster_shambler\",\t\t\t\"progs/shambler.mdl\",\t'1 0 0', \t'-32 -32 -24',\t'32 32 64'}, //Ambush\r\n\t{\"monster_army\",\t\t\t\"progs/soldier.mdl\",\t'1 0 0', \t'-16 -16 -24',\t'16 16 40'}, //Ambush\r\n\t{\"monster_tarbaby\",\t\t\t\"progs/tarbaby.mdl\",\t'1 0 0', \t'-16 -16 -24',\t'16 16 24'}, //Ambush\r\n\t{\"monster_wizard\",\t\t\t\"progs/wizard.mdl\",\t'1 0 0', \t'-16 -16 -24', \t'16 16 40'}, // Ambush\r\n\t{\"monster_zombie\",\t\t\t\"progs/zombie.mdl\",\t'1 0 0', \t'-16 -16 -24', \t'16 16 32'} //Crucified ambush\r\n};\r\n\r\nentedit_t*() editor_ents_new =\r\n{\r\n\tlocal int nent;\r\n\tlocal entedit_t *newents;\r\n\tnent = terrain_edit(TEREDIT_ENT_ADD, \"\");\r\n\tif (nent >= numents)\r\n\t{\r\n\t\t//extend the list\r\n\t\tnewents = memalloc(sizeof(entedit_t)*(nent+1));\r\n\t\tmemcpy((__variant*)newents, (__variant*)editents, sizeof(entedit_t)*numents);\r\n\t\tmemfree((__variant*)editents);\r\n\t\teditents = newents;\r\n\t\tnuments = nent+1;\r\n\t}\r\n\r\n\teditents[nent].fields = hash_createtab(12, EV_STRING);\r\n\r\n\treturn &editents[nent];\r\n};\r\n\r\nvoid(float num) editor_ents_delete =\r\n{\r\n\t//num 0 *could* be deleted, but we really don't want to allow that...\r\n\tif (num > 0 && num < numents)\r\n\t{\r\n\t\thash_destroytab(editents[num].fields);\r\n\t\teditents[num].fields = 0i;\r\n\t\tterrain_edit(TEREDIT_ENT_SET, num, __NULL__);\r\n\t}\r\n};\r\n\r\nstring(entedit_t *ent) reforment =\r\n{\r\n\tstring n = \"\";\r\n\tfor (int i = 0; ; i++)\r\n\t{\r\n\t\tstring key, value;\r\n\t\tkey = hash_getkey(ent->fields, i);\r\n\t\tif isnull(key)\r\n\t\t\tbreak;\r\n\t\tif (key == \"{\")\r\n\t\t\tcontinue;\r\n\t\tvalue = ent->fields[key];\r\n\t\t//inject markup into the value so that it doesn't get too corrupted\r\n\t\tvalue = strreplace(\"\\\\\", \"\\\\\\\\\", value);\r\n\t\tvalue = strreplace(\"\\\"\", \"\\\\\\\"\", value);\r\n\t\tvalue = strreplace(\"\\n\", \"\\\\n\", value);\r\n\t\t//these are more optional\r\n\t\tvalue = strreplace(\"\\t\", \"\\\\t\", value);\r\n\t\tvalue = strreplace(\"\\r\", \"\\\\r\", value);\r\n\t\tn = strcat(n, key, \" \\\"\", value, \"\\\"\\n\");\r\n\t}\r\n\treturn n;\r\n};\r\n\r\nvoid(entedit_t *ent) editor_ents_edited =\r\n{\r\n\tstring n = reforment(ent);\r\n\tterrain_edit(TEREDIT_ENT_SET, ent-editents, n);\r\n}\r\n\r\ninline float(string model) modelindexforname =\r\n{\r\n//\tprint(\"precaching \\\"\");\r\n//\tprint(model);\r\n//\tprint(\"\\\"\\n\");\r\n\tprecache_model(model);\r\n\tsetmodel(tempent, model);\r\n\treturn tempent.modelindex;\r\n};\r\nvoid(entedit_t *nent) editor_ents_updated =\r\n{\r\n\tfloat ang;\r\n\tint i;\r\n\r\n\tnent->alpha = stof(nent->fields[\"alpha\"]);\r\n\tnent->scale = stof(nent->fields[\"scale\"]);\r\n\tnent->modelindex = modelindexforname(nent->fields[\"model\"]);\r\n\tnent->mins = '-8 -8 -8';\r\n\tnent->maxs = '8 8 8';\r\n\tnent->org = stov(nent->fields[\"origin\"]);\r\n\tnent->isbsp = FALSE;\r\n\tnent->colourmod = '1 1 1';\r\n\r\n\tang = stof(nent->fields[\"angle\"]);\r\n\tif (ang == -1)\r\n\t\tnent->ang = [90, 0, 0];\r\n\telse if (ang == -2)\r\n\t\tnent->ang = [-90, 0, 0];\r\n\telse\r\n\t\tnent->ang = [0, ang, 0];\r\n\r\n\tif (!nent->modelindex)\r\n\t{\r\n\t\tstring classn;\r\n\t\tclassn = nent->fields[\"classname\"];\r\n\t\tfor (i = 0; i < entclasses.length; i++)\r\n\t\t{\r\n\t\t\tif (classn == entclasses[i].classn)\r\n\t\t\t{\r\n\t\t\t\tnent->modelindex = modelindexforname(entclasses[i].model);\r\n\t\t\t\tnent->alpha = 0.7;\r\n\t\t\t\tnent->colourmod = entclasses[i].colour;\r\n\t\t\t\tnent->mins = entclasses[i].mins;\r\n\t\t\t\tnent->maxs = entclasses[i].maxs;\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\telse\r\n\t\tnent->isbsp = TRUE;\r\n\r\n\tif (nent->isbsp)\r\n\t\tnent->ang = '0 0 0';\t//bsp entities should not be displayed rotated, because that just messes everything up.\r\n\r\n\tfor (i = 0; i < 8; i++)\r\n\t{\r\n\t\tnent->bboxverts[i].st = (vec2){0,0};\r\n\t\tnent->bboxverts[i].rgba = (vec4){nent->colourmod[0], nent->colourmod[1], nent->colourmod[2], nent->alpha?nent->alpha:1};\r\n\t\tnent->bboxverts[i].xyz[0] = nent->org[0] + ((i&1i)?nent->maxs[0]:nent->mins[0]);\r\n\t\tnent->bboxverts[i].xyz[1] = nent->org[1] + (((i&2i)?nent->maxs[1]:nent->mins[1]));\r\n\t\tnent->bboxverts[i].xyz[2] = nent->org[2] + ((i&4)?nent->maxs[2]:nent->mins[2]);\r\n\t}\r\n\t\t/*void(string shadername, vector min, vector max, vector col) editor_ents_drawbbox =\r\n{\r\n\tif (min == max)\r\n\t\treturn;\r\n\tR_BeginPolygon(shadername);\r\n#define line(x,y) \t\t\tR_PolygonVertex(x, '0 0', col, 1);\tR_PolygonVertex(y, '0 0', col, 1);\tR_EndPolygon()\r\n\tline(min, ([max[0], min[1], min[2]]));\r\n\tline(min, ([min[0], max[1], min[2]]));\r\n\tline(min, ([min[0], min[1], max[2]]));\r\n\tline(max, ([min[0], max[1], max[2]]));\r\n\tline(max, ([max[0], min[1], max[2]]));\r\n\tline(max, ([max[0], max[1], min[2]]));\r\n\r\n\tline(([max[0], min[1], min[2]]), ([max[0], max[1], min[2]]));\r\n\tline(([max[0], min[1], min[2]]), ([max[0], min[1], max[2]]));\r\n\tline(([min[0], max[1], min[2]]), ([min[0], max[1], max[2]]));\r\n\tline(([min[0], max[1], min[2]]), ([max[0], max[1], min[2]]));\r\n\tline(([min[0], min[1], max[2]]), ([min[0], max[1], max[2]]));\r\n\tline(([min[0], min[1], max[2]]), ([max[0], min[1], max[2]]));\r\n#undef line\r\n};*/\r\n};\r\n\r\nentedit_t*(entedit_t *o) editor_ents_clone =\r\n{\r\n\tint i;\r\n\tentedit_t *n = editor_ents_new();\r\n\r\n\tfor (i = 0; ; i++)\r\n\t{\r\n\t\tstring key, value;\r\n\t\tkey = hash_getkey(o->fields, i);\r\n\t\tif isnull(key)\r\n\t\t\tbreak;\r\n\t\tvalue = o->fields[key];\r\n\t\tn->fields[key] = value;\r\n\t}\r\n\r\n\teditor_ents_updated(n);\r\n\treturn n;\r\n};\r\n\r\nvoid() editor_ents_reload =\r\n{\r\n\tlocal entedit_t *nent;\r\n\tlocal string field, value;\r\n\r\n\tint id;\r\n\tint f, fcount;\r\n\r\n\tnuments = terrain_edit(TEREDIT_ENT_COUNT);\r\n\teditents = memalloc(sizeof(entedit_t)*numents);\r\n\tfor (id = 0; id < numents; id++)\r\n\t{\r\n\t\tfield = terrain_edit(TEREDIT_ENT_GET, id);\r\n\t\tnent = &editents[id];\r\n\t\tif (nent->fields)\r\n\t\t\thash_destroytab(nent->fields);\r\n\t\tnent->fields = __NULL__;\r\n\t\tif (field == __NULL__)\t//empty entity slot.\r\n\t\t\tcontinue;\r\n\t\tnent->fields = hash_createtab(12, EV_STRING);\r\n\t\tfcount = tokenize(field);\r\n\t\tfor (f = 0; f < fcount; f+=2)\r\n\t\t{\r\n\t\t\tfield = argv(f);\r\n\t\t\tvalue = argv(f+1);\r\n\t\t\tnent->fields[field] = value;\r\n\t\t}\r\n\t\teditor_ents_updated(nent);\r\n\t}\r\n};\r\n\r\n//called when another client has edited an entity.\r\nvoid(int idx, string new) CSQC_MapEntityEdited =\r\n{\r\n\tif (idx >= numents)\r\n\t{\r\n\t\tif not (new)\r\n\t\t\treturn; //deleting an ent that doesn't already exist? :o\r\n\t}\r\n\tlocal entedit_t *ent = &editents[idx];\r\n\r\n\tif (ent->fields)\r\n\t\thash_destroytab(ent->fields);\r\n\tent->fields = hash_createtab(12, EV_STRING);\r\n\tint fcount = tokenize(new);\r\n\tfor (int f = 0; f < fcount; f+=2)\r\n\t{\r\n\t\tstring field = argv(f);\r\n\t\tstring value = argv(f+1);\r\n\t\tent->fields[field] = value;\r\n\t}\r\n\r\n\teditor_ents_updated(ent);\r\n//\teditor_ents_edited(ent);\r\n};\r\n\r\nvoid() editor_ents_addentities =\r\n{\r\n\tint e;\r\n\tstring shadername;\r\n\tif (!tempent)\r\n\t{\r\n\t\ttempent = spawn();\r\n\t\teditor_ents_reload();\r\n\t}\r\n\r\n\t//make sure this shader was generated already.\r\n\tshaderforname(\"entbox\",\r\n\t\t\"{\"\r\n\t\t\t\"cull disable\\n\"\r\n\t\t\t\"{\\n\"\r\n\t\t\t\t\"map $whiteimage\\n\"\r\n\t\t\t\t\"blendfunc add\\n\"\r\n\t\t\t\t\"rgbgen vertex\\n\"\r\n\t\t\t\t\"alphagen vertex\\n\"\r\n\t\t\t\"}\\n\"\r\n\t\t\"}\");\r\n\t//make sure this shader was generated already.\r\n\tshaderforname(\"entboxsel\",\r\n\t\t\"{\"\r\n\t\t\t\"cull disable\\n\"\r\n\t\t\t\"{\\n\"\r\n\t\t\t\t\"map $whiteimage\\n\"\r\n\t\t\t\t\"blendfunc add\\n\"\r\n\t\t\t\t\"rgbgen vertex\\n\"\r\n\t\t\t\t\"alphagen vertex\\n\"\r\n\t\t\t\t\"sort nearest\\n\"\r\n\t\t\t\t\"nodepthtest\\n\"\r\n\t\t\t\"}\\n\"\r\n\t\t\"}\");\r\n\r\n\tfor (e = 1; e < numents; e++)\r\n\t{\r\n\t\tif (e == selectedent)\r\n\t\t{\r\n\t\t\tif ((gettime(0)*5f) & 1)\r\n\t\t\t\tcontinue;\r\n\t\t\ttempent.effects |= EF_NODEPTHTEST;\r\n\t\t\tshadername = \"entboxsel\";\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\ttempent.effects &= ~EF_NODEPTHTEST;\r\n\t\t\tshadername = \"entbox\";\r\n\t\t}\r\n\r\n\t\tif (editents[e].modelindex)\r\n\t\t{\r\n\t\t\ttempent.modelindex = editents[e].modelindex;\r\n\t\t\ttempent.alpha = editents[e].alpha;\r\n\t\t\ttempent.scale = editents[e].scale;\r\n\t\t\ttempent.angles = editents[e].ang;\r\n\t\t\ttempent.colormod = editents[e].colourmod;\r\n\r\n\t\t\tsetorigin(tempent, editents[e].org);\r\n\t\t\taddentity(tempent);\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\taddtrisoup_simple(\"entbox\", 0x800, &editents[e].bboxverts[0], bbox_line_idxs, bbox_line_idxs.length);\r\n\t\t}\r\n\t}\r\n};\r\n\r\nfloat(float key, float unic, vector mousepos) editor_ents_key =\r\n{\r\n\tvector t = mousefar;\r\n\tvector o = mousenear;\r\n\tentedit_t *ent;\r\n\r\n\tif (key == K_MOUSE1)\r\n\t{\r\n\t\tfloat bestfrac = 1;\r\n\t\tint bestent = selectedent, e;\r\n\t\t\r\n\t\t//FIXME: we need some click+drag moving like the brush editor has\r\n\t\t\r\n\t\tif (mousepos_x < 128)\r\n\t\t{\r\n\t\t\teditfieldtype = isnull(editkey)?1:2;\r\n\t\t\treturn TRUE;\r\n\t\t}\r\n\t\teditfieldtype = 0;\r\n\r\n\t\tif (vlen(o - t) > 8192)\r\n\t\t\tt = o + normalize(t)*8192;\r\n\r\n\t\tother = tempent;\r\n\t\tfor (e = 1; e < numents; e++)\r\n\t\t{\r\n\t\t\tif (editents[e].isbsp)\r\n\t\t\t\ttempent.solid = SOLID_BSP;\r\n\t\t\telse\r\n\t\t\t\ttempent.solid = SOLID_BBOX;\r\n\t\t\ttempent.modelindex = editents[e].modelindex;\r\n\t\t\ttempent.alpha = editents[e].alpha;\r\n\t\t\ttempent.scale = editents[e].scale;\r\n\t\t\ttempent.angles = editents[e].ang;\r\n\t\t\ttempent.colormod = editents[e].colourmod;\r\n\t\t\ttempent.mins = editents[e].mins;\r\n\t\t\ttempent.maxs = editents[e].maxs;\r\n\t\t\tsetorigin(tempent, editents[e].org);\r\n\r\n\t\t\ttraceline(o, t, 256, tempent);\r\n\r\n\t\t\tif (bestfrac > trace_fraction)\r\n\t\t\t{\r\n\t\t\t\tbestfrac = trace_fraction;\r\n\t\t\t\tbestent = e;\r\n\t\t\t}\r\n\t\t}\r\n\t\ttempent.modelindex = 0;\r\n\r\n\t\tselectedent = bestent;\r\n\t}\r\n\telse if (editfieldtype == 1)\r\n\t{\r\n\t\tif (selectedent >= numents)\r\n\t\t\treturn FALSE;\r\n\r\n\t\tif (key == K_ESCAPE)\r\n\t\t{\r\n\t\t\teditkey = \"\";\r\n\t\t\teditfieldtype = 0;\r\n\t\t}\r\n\t\telse if (key == K_BACKSPACE || key == K_DEL)\r\n\t\t{\r\n\t\t\tif (!editkey)\r\n\t\t\t\teditfieldtype = 0;\r\n\t\t\telse\r\n\t\t\t\teditkey = substring(editkey, 0, -2);\r\n\t\t}\r\n\t\telse if (key == K_ENTER && !shiftdown)\r\n\t\t{\r\n\t\t\tif (editkey != \"\")\r\n\t\t\t{\r\n\t\t\t\teditents[selectedent].fields[editkey] = \"\";\r\n\t\t\t\teditfieldtype = 2;\r\n\t\t\t}\r\n\t\t}\r\n\t\telse if (unic)\r\n\t\t\teditkey = strcat(editkey, chr2str(unic));\r\n\t\telse\r\n\t\t\treturn FALSE;\r\n\t}\r\n\telse if (editfieldtype == 2)\r\n\t{\r\n\t\tif (selectedent >= numents)\r\n\t\t\treturn FALSE;\r\n\t\tent = &editents[selectedent];\r\n\t\tstring value = ent->fields[editkey];\r\n\r\n\t\tif (key == K_ESCAPE)\r\n\t\t{\r\n\t\t\teditkey = \"\";\r\n\t\t\teditfieldtype = 0;\r\n\t\t}\r\n\t\telse if (key == K_BACKSPACE || key == K_DEL)\r\n\t\t{\r\n\t\t\tif (!value)\r\n\t\t\t{\r\n\t\t\t\thash_delete(ent->fields, editkey);\r\n\t\t\t\teditfieldtype = 0;\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t\tvalue = substring(value, 0, -2);\r\n\t\t}\r\n\t\telse if (key == K_ENTER && !shiftdown)\r\n\t\t\teditfieldtype = 0;\r\n\t\telse if (key == K_ENTER)\r\n\t\t\tvalue = strcat(value, chr2str('\\n'));\r\n\t\telse if (unic)\r\n\t\t\tvalue = strcat(value, chr2str(unic));\r\n\t\telse\r\n\t\t\treturn FALSE;\r\n\r\n\t\tif (editfieldtype)\r\n\t\t{\r\n\t\t\tent->fields[editkey] = value;\r\n\t\t\teditor_ents_edited(ent);\r\n\t\t\teditor_ents_updated(ent);\r\n\t\t}\r\n\t}\r\n\telse if (key == K_ESCAPE && selectedent)\r\n\t\tselectedent = 0;\r\n\telse if (key == K_BACKSPACE || key == K_DEL)\r\n\t\teditor_ents_delete(selectedent);\r\n\telse if (unic == 'm' || unic == 'M' || unic == 'i' || unic == 'I')\r\n\t{\r\n\t\ttraceline(o, t, TRUE, world);\r\n\t\t\r\n\t\tent = &editents[selectedent];\r\n\t\tif (unic == 'i' || unic == 'I')\r\n\t\t\tent = editor_ents_clone(ent);\r\n\r\n\t\t//figure out how far along the plane normal to push the entity in order to ensure that its mins/maxs is on the floor/slope/ceiling/wall/etc\r\n\t\t//yay dotproducts\r\n\t\tfloat ext = \ttrace_plane_normal * [\r\n\t\t\t\t\t(trace_plane_normal[0] < 0)?ent->mins[0]:ent->maxs[0],\r\n\t\t\t\t\t(trace_plane_normal[1] < 0)?ent->mins[1]:ent->maxs[1],\r\n\t\t\t\t\t(trace_plane_normal[2] < 0)?ent->mins[2]:ent->maxs[2]\r\n\t\t\t\t\t];\r\n\t\t//update the all important origin\r\n\t\tstring str = sprintf(\"%v\", trace_endpos + trace_plane_normal * ext);\r\n\t\tent->fields[\"origin\"] = str;\r\n\t\t//and fix up the quick-access stuff\r\n\t\tent->org = stov(ent->fields[\"origin\"]);\r\n\r\n\t\teditor_ents_edited(ent);\r\n\t}\r\n\telse\r\n\t\treturn FALSE;\r\n\treturn TRUE;\r\n};\r\n\r\nvoid(vector mousepos) editor_ents_overlay =\r\n{\r\n\tint i;\r\n\tvector pos;\r\n\tvector col;\r\n\tfloat foundedit = FALSE;\r\n\tfloat pickedit;\r\n\tif (selectedent >= numents)\r\n\t\treturn;\r\n\tentedit_t *ent = &editents[selectedent];\r\n\t\r\n\tpos = '128 0 0';\r\n\tpos_y = vidsize_y - 32;\r\n\tdrawfill('0 16 0', pos, '0 0 0', 0.3);\r\n\tpos = '0 32 0';\r\n\t\r\n\tpickedit = !editfieldtype && mousepos_x < 128;\r\n\tif (pickedit)\r\n\t\teditkey = 0;\r\n\r\n\tfor (i = 0; ; i++)\r\n\t{\r\n\t\tstring key, value;\r\n\t\tkey = hash_getkey(ent->fields, i);\r\n\t\tif isnull(key)\r\n\t\t\tbreak;\r\n\t\tvalue = ent->fields[key];\r\n\t\tcol = '1 1 1';\r\n\t\tif (pickedit && mousepos_y >= pos_y && mousepos_y < pos_y + 8 && mousepos_x < 128)\r\n\t\t{\r\n\t\t\tcol_y = 0;\r\n\t\t\teditkey = key;\r\n\t\t}\r\n\t\tif (key == editkey && editfieldtype)\r\n\t\t{\r\n\t\t\tcol = '0 0 1';\r\n\t\t\tfoundedit = TRUE;\r\n\t\t}\r\n\t\t\r\n\t\t//provide some markup so that its actually readable.\r\n\t\tvalue = strreplace(\"^\", \"^^\", value);\r\n\t\tvalue = strreplace(\"\\\\\", \"^5\\\\\\\\^7\", value);\r\n\t\tvalue = strreplace(\"\\\"\", \"^5\\\\\\\"^7\", value);\r\n\t\tvalue = strreplace(\"\\n\", \"^5\\\\n^7\", value);\r\n\t\tvalue = strreplace(\"\\t\", \"^5\\\\t^7\", value);\r\n\t\tvalue = strreplace(\"\\r\", \"^5\\\\r^7\", value);\r\n\t\t\t\t\r\n\t\tdrawstring(pos, sprintf(\"%s: %s\", key, value), '8 8 0', col, 1, 0);\r\n\t\tpos_y += 8;\r\n\t}\r\n\tif (editfieldtype && !foundedit)\r\n\t{\r\n\t\tdrawrawstring(pos, sprintf(\"%s: <UNDEFINED>\", editkey==\"\"?\"<UNMNAMED>\":editkey), '8 8 0', '0 0 1', 1);\r\n\t\tpos_y += 8;\r\n\t}\r\n};\r\n"
  },
  {
    "path": "quakec/csaddon/src/editor_lights.qc",
    "content": "/*FTE has some special light editing builtins, I don't ever expect them to be standard or anything, but they're handy for this*/\r\n\r\n/*you probably want to change this if you're running hexen2*/\r\nvar string autocvar_cg_editor_lightmodel = \"progs/s_light.spr\";\r\n\r\nvar float autocvar_r_editlights_import_ambient = 0;\r\nvar float autocvar_r_editlights_import_diffuse = 1;\r\nvar float autocvar_r_editlights_import_specular = 1;\r\n\r\nstatic float selectedlight;\r\nstatic float editfield;\r\nstatic string editvalue;\r\nstatic entity tempent;\r\nstatic float helphidden;\r\nvoid() editor_lights_addentities =\r\n{\r\n\tfloat l;\r\n\tif (!tempent)\r\n\t\ttempent = spawn();\r\n\r\n\tl = (float)dynamiclight_get(-1, -1);\r\n\tprecache_model(autocvar_cg_editor_lightmodel); /*just to silence it*/\r\n\tsetmodel(tempent, autocvar_cg_editor_lightmodel);\r\n\twhile(l > 0)\r\n\t{\r\n\t\tl = l-1.0;\r\n\t\tif (l == selectedlight)\r\n\t\t{\r\n\t\t\tif (gettime(0)*5f & 1)\r\n\t\t\t\tcontinue;\r\n\t\t\ttempent.effects |= 8192f;\r\n\t\t}\r\n\t\telse\r\n\t\t\ttempent.effects &= ~8192f;\r\n\t\tif (!(float)dynamiclight_get(l, LFIELD_RADIUS))\r\n\t\t\tcontinue;\r\n\t\tsetorigin(tempent, dynamiclight_get(l, LFIELD_ORIGIN));\r\n\t\taddentity(tempent);\r\n\t}\r\n};\r\n\r\n#define NUMLFIELDS 15\r\n#define NUMCMDS 7\r\nstatic var string fldname[NUMLFIELDS+1+NUMCMDS] = {\t\"bad\",\r\n\t\"  num\",\r\n\t\"  org\",\r\n\t\"  rgb\",\r\n\t\"  rad\",\r\n\t\"  flg\",\r\n\t\"  sty\",\r\n\t\"  ang\",\r\n\t\"  fov\",\r\n\t\"  cmp\",\r\n\t \" cor\",\r\n\t \" csc\",\r\n\t \" amb\",\r\n\t \" dif\",\r\n\t \" spc\",\r\n\t \" avl\",\r\n\r\n\t\"Save\",\r\n\t\"World Lights\",\r\n\t\"Dynamic Lights\",\r\n\t\"Toggle Help\",\r\n\t\"Wipe All\",\r\n\t\"Import\",\r\n\t\"Reload\"\r\n};\r\nstatic string(int fld, float foredit) readfield =\r\n{\r\n\tswitch(fld)\r\n\t{\r\n\tcase 1:\r\n\t\tif (foredit)\r\n\t\t\treturn ftos(selectedlight);\r\n\t\treturn strcat(ftos(selectedlight), \" / \", ftos(dynamiclight_get(-1f, -1f)));\r\n\tcase 2:\r\n\t\treturn sprintf(\"%v\", (vector)dynamiclight_get(selectedlight, LFIELD_ORIGIN));\r\n\tcase 3:\r\n\t\treturn sprintf(\"%v\", (vector)dynamiclight_get(selectedlight, LFIELD_COLOUR));\r\n\tcase 4:\r\n\t\treturn ftos(dynamiclight_get(selectedlight, LFIELD_RADIUS));\r\n\tcase 5:\r\n\t\tfloat fl = (float)dynamiclight_get(selectedlight, LFIELD_FLAGS);\r\n\t\tstring ret;\r\n\t\tret = strcat(ret, (fl & LFLAG_NORMALMODE)?\"d\":\"\");\r\n\t\tret = strcat(ret, (fl & LFLAG_REALTIMEMODE)?\"w\":\"\");\r\n\t\tret = strcat(ret, (fl & LFLAG_LIGHTMAP)?\"l\":\"\");\r\n\t\tret = strcat(ret, (fl & LFLAG_FLASHBLEND)?\"f\":\"\");\r\n\t\tret = strcat(ret, (fl & LFLAG_NOSHADOWS)?\"c\":\"\");\r\n\t\tret = strcat(ret, (fl & LFLAG_SHADOWMAP)?\"s\":\"\");\r\n\t\tret = strcat(ret, (fl & LFLAG_CREPUSCULAR)?\"r\":\"\");\r\n\t\tret = strcat(ret, (fl & LFLAG_ORTHOSUN)?\"o\":\"\");\r\n\t\treturn ret;\r\n\tcase 6:\r\n\t\treturn ftos(dynamiclight_get(selectedlight, LFIELD_STYLE));\r\n\tcase 7:\r\n\t\treturn sprintf(\"%v\", (vector)dynamiclight_get(selectedlight, LFIELD_ANGLES));\r\n\tcase 8:\r\n\t\treturn ftos(dynamiclight_get(selectedlight, LFIELD_FOV));\r\n\tcase 9:\r\n\t\treturn (string)dynamiclight_get(selectedlight, LFIELD_CUBEMAPNAME);\r\n\tcase 10:\r\n\t\treturn ftos(dynamiclight_get(selectedlight, LFIELD_CORONA));\r\n\tcase 11:\r\n\t\treturn ftos(dynamiclight_get(selectedlight, LFIELD_CORONASCALE));\r\n\tcase 12:\r\n\t\treturn ftos(dynamiclight_get(selectedlight, LFIELD_AMBIENTSCALE));\r\n\tcase 13:\r\n\t\treturn ftos(dynamiclight_get(selectedlight, LFIELD_DIFFUSESCALE));\r\n\tcase 14:\r\n\t\treturn ftos(dynamiclight_get(selectedlight, LFIELD_SPECULARSCALE));\r\n\tcase 15:\r\n\t\treturn sprintf(\"%v\", (vector)dynamiclight_get(selectedlight, LFIELD_ROTATION));\r\n\tdefault:\r\n\t\treturn \"\";\r\n\t}\r\n};\r\n\r\nstatic void(float fld, string newval) writefield =\r\n{\r\n\tswitch(fld)\r\n\t{\r\n\tcase 1:\r\n\t\tselectedlight = stof(newval);\r\n\t\treturn;\r\n\tcase 2:\r\n\t\tdynamiclight_set(selectedlight, LFIELD_ORIGIN, stov(newval));\r\n\t\treturn;\r\n\tcase 3:\r\n\t\tdynamiclight_set(selectedlight, LFIELD_COLOUR, stov(newval));\r\n\t\treturn;\r\n\tcase 4:\r\n\t\tdynamiclight_set(selectedlight, LFIELD_RADIUS, stof(newval));\r\n\t\treturn;\r\n\tcase 5:\r\n\t\tvar float fl = 0;\r\n\t\tif (strstrofs(newval, \"d\")>=0) fl |= LFLAG_NORMALMODE;\r\n\t\tif (strstrofs(newval, \"w\")>=0) fl |= LFLAG_REALTIMEMODE;\r\n\t\tif (strstrofs(newval, \"l\")>=0) fl |= LFLAG_LIGHTMAP;\r\n\t\tif (strstrofs(newval, \"f\")>=0) fl |= LFLAG_FLASHBLEND;\r\n\t\tif (strstrofs(newval, \"c\")>=0) fl |= LFLAG_NOSHADOWS;\r\n\t\tif (strstrofs(newval, \"s\")>=0) fl |= LFLAG_SHADOWMAP;\r\n\t\tif (strstrofs(newval, \"r\")>=0) fl |= LFLAG_CREPUSCULAR;\r\n\t\tif (strstrofs(newval, \"o\")>=0) fl |= LFLAG_ORTHOSUN;\r\n\t\tdynamiclight_set(selectedlight, LFIELD_FLAGS, (float)fl);\r\n\t\treturn;\r\n\tcase 6:\r\n\t\tdynamiclight_set(selectedlight, LFIELD_STYLE, stof(newval));\r\n\t\treturn;\r\n\tcase 7:\r\n\t\tdynamiclight_set(selectedlight, LFIELD_ANGLES, stov(newval));\r\n\t\treturn;\r\n\tcase 8:\r\n\t\tdynamiclight_set(selectedlight, LFIELD_FOV, stof(newval));\r\n\t\treturn;\r\n\tcase 9:\r\n\t\tdynamiclight_set(selectedlight, LFIELD_CUBEMAPNAME, newval);\r\n\t\treturn;\r\n\tcase 10:\r\n\t\tdynamiclight_set(selectedlight, LFIELD_CORONA, stof(newval));\r\n\t\treturn;\r\n\tcase 11:\r\n\t\tdynamiclight_set(selectedlight, LFIELD_CORONASCALE, stof(newval));\r\n\t\treturn;\r\n\tcase 12:\r\n\t\tdynamiclight_set(selectedlight, LFIELD_AMBIENTSCALE, stof(newval));\r\n\t\treturn;\r\n\tcase 13:\r\n\t\tdynamiclight_set(selectedlight, LFIELD_DIFFUSESCALE, stof(newval));\r\n\t\treturn;\r\n\tcase 14:\r\n\t\tdynamiclight_set(selectedlight, LFIELD_SPECULARSCALE, stof(newval));\r\n\t\treturn;\r\n\tcase 15:\r\n\t\tdynamiclight_set(selectedlight, LFIELD_ROTATION, stov(newval));\r\n\t\treturn;\r\n\tdefault:\r\n\t\treturn;\r\n\t}\r\n};\r\n\r\nvoid(vector m) editor_lights_overlay =\r\n{\r\n\tint i;\r\n\tstring s;\r\n\tvector col;\r\n\r\n\tm_y = floor((m_y - 32f) / 8f);\r\n\r\n\r\n\tfldname[NUMLFIELDS+2] = strcat(\"realtime world: \", cvar(\"r_shadow_realtime_world\")?\"on\":\"off\");\r\n\tfldname[NUMLFIELDS+3] = strcat(\"realtime dlight: \", cvar(\"r_shadow_realtime_dlight\")?\"on\":\"off\");\r\n\r\n\tfor (i = 1; i <= NUMLFIELDS; i++)\r\n\t{\r\n\t\tif (editfield == i)\r\n\t\t\ts = editvalue;\r\n\t\telse\r\n\t\t\ts = readfield(i, 0);\r\n\t\ts = strcat(ftos(i), \" \", fldname[i], \": \", s);\r\n\r\n\t\tif (editfield == i)\r\n\t\t\tcol = '1 0 0';\r\n\t\telse if (m_y == i && m_x < 64)\r\n\t\t\tcol = '0 0 1';\r\n\t\telse\r\n\t\t\tcol = '1 1 1';\r\n\t\t\t\r\n\t\tdrawrawstring('0 32 0' + '0 8 0' * (float)i, s, '8 8 0', col, 1);\r\n\t}\r\n\r\n\tfor (i = NUMLFIELDS+1f; i <= NUMLFIELDS+NUMCMDS; i+=1)\r\n\t{\r\n\t\ts = strcat(ftos(i), \" \", fldname[i]);\r\n\r\n\t\tif (editfield == i)\r\n\t\t\tcol = '1 0 0';\r\n\t\telse if (m_y == i && m_x < 64)\r\n\t\t\tcol = '0 0 1';\r\n\t\telse\r\n\t\t\tcol = '1 1 1';\r\n\t\t\t\r\n\t\tdrawrawstring('0 32 0' + '0 8 0' * (float)i, s, '8 8 0', col, 1);\r\n\t}\r\n\ti+=1i;\r\n\r\n\tif (helphidden)\r\n\t\treturn;\r\n\r\n\tif (editfield == 5)\r\n\t{\r\n\t\tdrawrawstring('0 32 0' + '0 8 0' * (float)i, \"d: dynamic mode\\n\", '8 8 0', '1 1 1', 1); i+=1i;\r\n\t\tdrawrawstring('0 32 0' + '0 8 0' * (float)i, \"w: realtime world lights mode\\n\", '8 8 0', '1 1 1', 1); i+=1i;\r\n\t\tdrawrawstring('0 32 0' + '0 8 0' * (float)i, \"l: lightmap hack (not valid above index 32)\\n\", '8 8 0', '1 1 1', 1); i+=1i;\r\n\t\tdrawrawstring('0 32 0' + '0 8 0' * (float)i, \"f: flashblend coronas\\n\", '8 8 0', '1 1 1', 1); i+=1i;\r\n\t\tdrawrawstring('0 32 0' + '0 8 0' * (float)i, \"c: does not cast shadows\\n\", '8 8 0', '1 1 1', 1); i+=1i;\r\n\t\tdrawrawstring('0 32 0' + '0 8 0' * (float)i, \"s: shadowmapped light\\n\", '8 8 0', '1 1 1', 1); i+=1i;\r\n\t\tdrawrawstring('0 32 0' + '0 8 0' * (float)i, \"r: crepuscular rays\\n\", '8 8 0', '1 1 1', 1); i+=1i;\r\n\t}\r\n\telse\r\n\t{\r\n\t\tdrawrawstring('0 32 0' + '0 8 0' * (float)i, \"+/- change selected light\\n\", '8 8 0', '1 1 1', 1); i+=1i;\r\n\t\tdrawrawstring('0 32 0' + '0 8 0' * (float)i, \"mouse also selects lights\\n\", '8 8 0', '1 1 1', 1);\ti+=1i;\r\n\t\tdrawrawstring('0 32 0' + '0 8 0' * (float)i, \"m moves the light to the cursor\\n\", '8 8 0', '1 1 1', 1); i+=1i;\r\n\t\tdrawrawstring('0 32 0' + '0 8 0' * (float)i, \"i inserts new light\\n\", '8 8 0', '1 1 1', 1); i+=1i;\r\n\t\tdrawrawstring('0 32 0' + '0 8 0' * (float)i, \"@ sets the light's radius to the cursor\\n\", '8 8 0', '1 1 1', 1); i+=1i;\r\n\t\tdrawrawstring('0 32 0' + '0 8 0' * (float)i, \"# changes the light's shadow flag\\n\", '8 8 0', '1 1 1', 1); i+=1i;\r\n\t\tdrawrawstring('0 32 0' + '0 8 0' * (float)i, \"p points the light to aim at the cursor\\n\", '8 8 0', '1 1 1', 1); i+=1i;\r\n\t\tdrawrawstring('0 32 0' + '0 8 0' * (float)i, \"[ and ] move the light towards/away from indicated plane\\n\", '8 8 0', '1 1 1', 1); i+=1i;\r\n\t\tdrawrawstring('0 32 0' + '0 8 0' * (float)i, \"don't forget to save\\n\", '8 8 0', '1 1 1', 1); i+=1i;\r\n\t}\r\n};\r\n\r\nstatic void(vector fwd, vector vorg) selectbestlight =\r\n{\r\n\tfloat l, b=selectedlight, d, bd;\r\n\tvector ldir;\r\n\tl = (float)dynamiclight_get(-1f, -1f);\r\n\tbd = 0;\r\n\twhile(l > 0)\r\n\t{\r\n\t\tl-=1;\r\n\t\tldir = (vector)dynamiclight_get(l, LFIELD_ORIGIN);\r\n\t\tldir = normalize(ldir - vorg);\r\n\t\td = fwd*ldir;\r\n\t\tif (d > bd)\r\n\t\t{\r\n\t\t\tbd = d;\r\n\t\t\tb = l;\r\n\t\t}\r\n\t}\r\n\tselectedlight = b;\r\n};\r\n\r\nfloat(float keyc, float unic, vector m) editor_lights_key =\r\n{\r\n\tvector t = unproject(m + '0 0 8192');\r\n\tvector o = unproject(m);\r\n\tstring ns;\r\n\tif (keyc == 512 || (!editfield && unic == 13 && m_x < 64))\r\n\t{\r\n\t\tif (editfield)\r\n\t\t{\r\n\t\t\twritefield(editfield, editvalue);\r\n\t\t\tstrunzone(editvalue);\r\n\t\t\teditfield = 0;\r\n\t\t}\r\n\t\teditfield = floor((m_y - 32f) / 8f);\r\n\t\tif (editfield <= 0 || editfield > NUMLFIELDS+NUMCMDS || m_x >= 64)\r\n\t\t{\r\n\t\t\teditfield = 0;\r\n\r\n\t\t\tselectbestlight(t - o, o);\r\n\t\t}\r\n\t\telse if (editfield > NUMLFIELDS)\r\n\t\t{\r\n\t\t\tswitch(editfield - (NUMLFIELDS+1))\r\n\t\t\t{\r\n\t\t\tcase 0:\r\n\t\t\t\tlocalcmd(\"r_editlights_save\\n\");\r\n\t\t\t\tbreak;\r\n\t\t\tcase 1:\r\n\t\t\t\tlocalcmd(\"toggle r_shadow_realtime_world\\n\");\r\n\t\t\t\tbreak;\r\n\t\t\tcase 2:\r\n\t\t\t\tlocalcmd(\"toggle r_shadow_realtime_dlight\\n\");\r\n\t\t\t\tbreak;\r\n\t\t\tcase 3:\r\n\t\t\t\thelphidden = !helphidden;\r\n\t\t\t\tbreak;\r\n\t\t\tcase 4:\r\n\t\t\t\tlocalcmd(\"r_editlights_reload none\\n\");\r\n\t\t\t\tbreak;\r\n\t\t\tcase 5:\r\n\t\t\t\tlocalcmd(\"r_editlights_reload bsp\\n\");\r\n\t\t\t\tbreak;\r\n\t\t\tcase 6:\r\n\t\t\t\tlocalcmd(\"r_editlights_reload\\n\");\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\t\t\teditfield = 0;\r\n\t\t}\r\n\t\telse\r\n\t\t\teditvalue = strzone(readfield(editfield, 1));\r\n\t}\r\n\telse if (editfield)\r\n\t{\r\n\t\tns = strcat(editvalue);\r\n\t\tif (keyc == 10 || keyc == 13)\r\n\t\t{\r\n\t\t\twritefield(editfield, ns);\r\n\t\t\teditfield = 0;\r\n\t\t}\r\n\t\telse if (keyc == 127)\r\n\t\t{\r\n\t\t\tif (ns != \"\")\r\n\t\t\t\tns = substring(ns, 0, -2);\r\n\t\t}\r\n\t\telse if (keyc == 8)\r\n\t\t{\r\n\t\t\tns = \"\";\r\n\t\t}\r\n\t\telse\r\n\t\t\tns = strcat(ns, chr2str(unic));\r\n\r\n\t\tns = strzone(ns);\r\n\t\tstrunzone(editvalue);\r\n\t\teditvalue = ns;\r\n\r\n\t\twritefield(editfield, ns);\r\n\t}\r\n\telse if (unic >= '0' && unic <= '9')\r\n\t{\r\n\t\teditfield = unic - '0';\r\n\t\teditvalue = strzone(readfield(editfield, 1));\r\n\t}\r\n\telse if (unic == '=')\r\n\t\tselectedlight++;\r\n\telse if (unic == '-')\r\n\t\tselectedlight-=1;\r\n\telse if (unic == 'n' || unic == 'N')\r\n\t\tlocalcmd(\"noclip\\n\");\r\n//\telse if (unic == 's' || unic == 'S')\r\n//\t{\r\n//\t\tselectbestlight(t - o, o);\r\n//\t}\r\n\telse if (unic == 'm' || unic == 'M')\r\n\t{\r\n\t\ttraceline(o, t, TRUE, world);\r\n\t\tdynamiclight_set(selectedlight, LFIELD_ORIGIN, trace_endpos + trace_plane_normal*4);\r\n\t}\r\n\telse if (unic == 'p' || unic == 'P')\r\n\t{\r\n\t\ttraceline(o, t, TRUE, world);\r\n\t\tvector ang = vectoangles((trace_endpos + trace_plane_normal*4f) - (vector)dynamiclight_get(selectedlight, LFIELD_ORIGIN));\r\n\t\tang_x *= -1f;\r\n\t\tdynamiclight_set(selectedlight, LFIELD_ANGLES, ang);\r\n\r\n\t\t/*if we're pointing the light at something, it should probably have a fov*/\r\n\t\tif (!(float)dynamiclight_get(selectedlight, LFIELD_FOV))\r\n\t\t\tdynamiclight_set(selectedlight, LFIELD_FOV, 90f);\r\n\t}\r\n\telse if (unic == 'i' || unic == 'I')\r\n\t{\r\n\t\ttraceline(o, t, TRUE, world);\r\n\t\tfloat oldl = selectedlight;\r\n\t\tselectedlight = dynamiclight_spawnstatic(trace_endpos + trace_plane_normal*4f, 300, '1 1 1');\r\n\r\n\t\tif ((float)dynamiclight_get(oldl, LFIELD_RADIUS) != 0f && oldl != selectedlight)\r\n\t\t{\r\n\t\t\t//dupe the current light\r\n\t\t\tdynamiclight_set(selectedlight, LFIELD_RADIUS,\t\tdynamiclight_get(oldl, LFIELD_RADIUS));\r\n\t\t\tdynamiclight_set(selectedlight, LFIELD_COLOUR,\t\tdynamiclight_get(oldl, LFIELD_COLOUR));\r\n\t\t\tdynamiclight_set(selectedlight, LFIELD_FOV,\t\tdynamiclight_get(oldl, LFIELD_FOV));\r\n\t\t\tdynamiclight_set(selectedlight, LFIELD_STYLE,\t\tdynamiclight_get(oldl, LFIELD_STYLE));\r\n\t\t\tdynamiclight_set(selectedlight, LFIELD_ANGLES,\t\tdynamiclight_get(oldl, LFIELD_ANGLES));\r\n\t\t\tdynamiclight_set(selectedlight, LFIELD_FLAGS,\t\tdynamiclight_get(oldl, LFIELD_FLAGS));\r\n\t\t\tdynamiclight_set(selectedlight, LFIELD_AMBIENTSCALE,\tdynamiclight_get(oldl, LFIELD_AMBIENTSCALE));\r\n\t\t\tdynamiclight_set(selectedlight, LFIELD_DIFFUSESCALE,\tdynamiclight_get(oldl, LFIELD_DIFFUSESCALE));\r\n\t\t\tdynamiclight_set(selectedlight, LFIELD_SPECULARSCALE,\tdynamiclight_get(oldl, LFIELD_SPECULARSCALE));\r\n\t\t\tdynamiclight_set(selectedlight, LFIELD_CUBEMAPNAME,\tdynamiclight_get(oldl, LFIELD_CUBEMAPNAME));\r\n\t\t\tdynamiclight_set(selectedlight, LFIELD_CORONA,\t\tdynamiclight_get(oldl, LFIELD_CORONA));\r\n\t\t\tdynamiclight_set(selectedlight, LFIELD_CORONASCALE,\tdynamiclight_get(oldl, LFIELD_CORONASCALE));\r\n\t\t\tdynamiclight_set(selectedlight, LFIELD_ROTATION,\tdynamiclight_get(oldl, LFIELD_ROTATION));\r\n\t\t}else\r\n\t\t{\r\n\t\t\t/*reset the light's properties*/\r\n\t\t\tdynamiclight_set(selectedlight, LFIELD_RADIUS, 300);\r\n\t\t\tdynamiclight_set(selectedlight, LFIELD_COLOUR, '1 1 1');\r\n\t\t\tdynamiclight_set(selectedlight, LFIELD_FOV, 0);\r\n\t\t\tdynamiclight_set(selectedlight, LFIELD_STYLE, 0);\r\n\t\t\tdynamiclight_set(selectedlight, LFIELD_ANGLES, '0 0 0');\r\n\t\t\tdynamiclight_set(selectedlight, LFIELD_FLAGS, LFLAG_REALTIMEMODE);\r\n\t\t\tdynamiclight_set(selectedlight, LFIELD_AMBIENTSCALE, autocvar_r_editlights_import_ambient);\r\n\t\t\tdynamiclight_set(selectedlight, LFIELD_DIFFUSESCALE, autocvar_r_editlights_import_diffuse);\r\n\t\t\tdynamiclight_set(selectedlight, LFIELD_SPECULARSCALE, autocvar_r_editlights_import_specular);\r\n\t\t\tdynamiclight_set(selectedlight, LFIELD_CUBEMAPNAME,\t\"\");\r\n\t\t\tdynamiclight_set(selectedlight, LFIELD_CORONA,\t\t0);\r\n\t\t\tdynamiclight_set(selectedlight, LFIELD_CORONASCALE,\t1);\r\n\t\t\tdynamiclight_set(selectedlight, LFIELD_ROTATION,\t'0 0 0');\r\n\t\t}\r\n\t}\r\n\telse if (unic == '[')\r\n\t{\r\n\t\ttraceline(o, t, TRUE, world);\r\n\t\tdynamiclight_set(selectedlight, LFIELD_ORIGIN, (vector)dynamiclight_get(selectedlight, LFIELD_ORIGIN) - trace_plane_normal);\r\n\t}\r\n\telse if (unic == ']')\r\n\t{\r\n\t\ttraceline(o, t, TRUE, world);\r\n\t\tdynamiclight_set(selectedlight, LFIELD_ORIGIN, (vector)dynamiclight_get(selectedlight, LFIELD_ORIGIN) + trace_plane_normal);\r\n\t}\r\n\telse if (unic == '\\'' || unic == '@')\r\n\t{\r\n\t\ttraceline(o, t, TRUE, world);\r\n\t\tdynamiclight_set(selectedlight, LFIELD_RADIUS, 1.5*vlen(trace_endpos - (vector)dynamiclight_get(selectedlight, LFIELD_ORIGIN)));\r\n\t}\r\n\telse if (unic == '#')\r\n\t{\r\n\t\tfloat fl;\r\n\t\tfl = (float)dynamiclight_get(selectedlight, LFIELD_FLAGS);\r\n\t\tif (fl & LFLAG_NOSHADOWS)\r\n\t\t\tfl -= LFLAG_NOSHADOWS;\r\n\t\telse\r\n\t\t\tfl += LFLAG_NOSHADOWS;\r\n\t\tdynamiclight_set(selectedlight, LFIELD_FLAGS, (float)fl);\r\n\t}\r\n\telse if (unic == '{')\r\n\t\tdynamiclight_set(selectedlight, LFIELD_RADIUS, dynamiclight_get(selectedlight, LFIELD_RADIUS) - 15);\r\n\telse if (unic == '}')\r\n\t\tdynamiclight_set(selectedlight, LFIELD_RADIUS, dynamiclight_get(selectedlight, LFIELD_RADIUS) + 15);\r\n\telse if (keyc == 127)\r\n\t\tdynamiclight_set(selectedlight, LFIELD_RADIUS, 0);\r\n\telse\r\n\t\treturn FALSE;\r\n\treturn TRUE;\r\n};\r\n"
  },
  {
    "path": "quakec/csaddon/src/editor_particles.qc",
    "content": "\r\nstatic float cureffect;\r\nstatic float curmodified;\r\nconst textfield_linedesc_t fields[100] =\r\n{\r\n\t{\"shader\", \"shader <shadername>\\\\n{\\\\nshaderbody\\\\n}\"},\r\n\t{\"texture\", \"texture <shadername>\"},\r\n\t{\"tcoords\", \"tcoords <s1> <t1> <s2> <t2> [coordscale] [randcount] [randsinc]\\ncoordscale is an optional divisor for more readable numbers.\\nrandcount is a the number of images used in particlefont image.\\nrandsinc is the amount to add to s coords to display each sequential randomized image.\"},\r\n\t{\"rotationstart\", \"rotationstart <minangle> [maxangle]\\nan angle of 45 will give you a particle aligned to screen coords\"},\r\n\t{\"rotationspeed\", \"rotationspeed <min> <max>\\nspecifies a range of rotational speeds. You probably want to set the min value as negative.\"},\r\n\t{\"beamtexstep\",\t\"beamtexstep <documentme>\"},\r\n\t{\"scale\",\t\"scale <min> [max]\\nValues are 4 times higher than quake units\"},\r\n\t{\"scalerand\",\t\"use scale instead\"},\r\n\t{\"scalefactor\",\t\"scalefactor <factor>\\nHow much the particle shrinks with distance.\\n1=true scaling\"},\r\n\t{\"scaledelta\",\t\"scaledelta <changepersecond>\\nHow much the particle's scale changes per second\"},\r\n\t{\"step\",\t\"step <dist>\\nworld distance between each particle within a particle trail.\"},\r\n\t{\"count\",\t\"count <multiplier>\\nProvides a multiplier value independant from the particle() builtin or other mechanisms\"},\r\n\t{\"alpha\",\t\"alpha <val>\\nInitial opacity of particle. 0 is invisible, 1 is fully visible.\"},\r\n\t{\"alphachange\",\t\"depricated. use alphadelta\"},\r\n\t{\"alphadelta\",\t\"alphadelta <alphapersecond>\\n How much the alpha value changes per second\"},\r\n\t{\"die\",\t\"die <min> [max]\\nHow long the particle lives for\"},\r\n\t{\"diesubrand\",\t\"depricated. use die\"},\r\n\t{\"randomvel\",\t\"<documentme>\"},\r\n\t{\"veladd\",\t\"<documentme>\"},\r\n\t{\"orgadd\",\t\"<documentme>\"},\r\n\t{\"friction\",\t\"<documentme>\"},\r\n\t{\"gravity\",\t\"gravity <grav>\\nParticle's downwards velocity will be increased by this much per second\"},\r\n\t{\"clipbounce\",\t\"<documentme>\"},\r\n\t{\"assoc\",\t\"assoc <othereffectname>\\nWhen the effect is spawned, the associated effect will also be spawned. Can chain.\"},\r\n\t{\"inwater\",\t\"inwater <othereffectname>\\nIf the particle effect is spawned underwater, the named effect will be used instead.\"},\r\n\t{\"model\",\t\"model <modelname> <firstframe> <lastframe> <framerate> <alpha>\\nSpawns a the sprite/model when the effect is spawned.\\nCan be specified multiple times and a random model line will be used.\\nModels with only 1 frame will animate skins instead.\"},\r\n\t{\"colorindex\",\t\"colourindex <min> <max>\\nIf set, the particle's colour will be set to this palette index instead of the particle's rgb field.\"},\r\n\t{\"colorrand\",\t\"depricated\"},\r\n\t{\"citracer\",\t\"citracer\\nFlag that syncronizes colorindex-based palette indexes with spawn ordering ala quake.\"},\r\n\t{\"red\",\t\"depricated, use rgb.\"},\r\n\t{\"green\",\t\"depricated, use rgb.\"},\r\n\t{\"blue\",\t\"depricated, use rgb.\"},\r\n\t{\"rgb\",\t\"rgb <red> <green> <blue>\\nInitial colour of particle, in a scale of 0 to 255.\"},\r\n\t{\"reddelta\",\t\"depricated, use rgbdelta.\"},\r\n\t{\"greendelta\",\t\"depricated, use rgbdelta.\"},\r\n\t{\"bluedelta\",\t\"depricated, use rgbdelta.\"},\r\n\t{\"rgbdelta\",\t\"rgbdelta <red> <green> <blue>\\nSpecifies the change in particle colours over a second of time. The effective range is 0 to 255, but larger values might be needed for short-lived effects.\"},\r\n\t{\"rgbdeltatime\",\t\"rgbdeltatime <age>\\nOnce the particle reaches this age, its rgb values will stop changing.\"},\r\n\t{\"redrand\",\t\"depricated\"},\r\n\t{\"greenrand\",\t\"depricated\"},\r\n\t{\"bluerand\",\t\"depricated\"},\r\n\t{\"rgbrand\",\t\"rgbrand <red> <green> <blue>\\nThis rgb value will be multiplied by a random value between 0 and 1 and added to the particle's initial rgb value. All three channels channels are scaled independantly.\"},\r\n\t{\"rgbrandsync\",\t\"rgbrandsync <red> <green> <blue>\\nThis rgb value will be multiplied by a random value between 0 and 1 and added to the particle's initial rgb value. All three channels will be scaled the same.\"},\r\n\t{\"redrandsync\",\t\"depricated\"},\r\n\t{\"greenrandsync\",\t\"depricated\"},\r\n\t{\"bluerandsync\",\t\"depricated\"},\r\n\t{\"stains\",\t\"stains <radius>\\nSpecifies a multiplier for the radius of a stain if the particle impacts upon a wall\"},\r\n\t{\"blend\",\t\"blend <blendmode>\\nOne of add, subtract, blendcolour, or blendalpha. Particles with additive blend modes will render fastest due to no depth sorting requirement.\"},\r\n\t{\"spawnmode\",\t\"<documentme>\"},\r\n\t{\"type\",\t\"type <rendertype>\\nOne of:\\nbeam - quads generated between particles\\nspark - a single line drawn in direction of motion\\nsparkfan - triangle fan oriented in direction of motion\\ntexturedspark - quads extruded along line of motion\\ndecal - motionless effect that attaches to surfaces around the center of the effect\\nnormal - simple screen-facing quad\"},\r\n\t{\"isbeam\",\t\"depricated, use type.\"},\r\n\t{\"spawntime\",\t\"<documentme>\"},\r\n\t{\"spawnchance\",\t\"<documentme>\"},\r\n\t{\"cliptype\",\t\"<documentme>\"},\r\n\t{\"clipcount\",\t\"<documentme>\"},\r\n\t{\"emit\",\t\"<documentme>\"},\r\n\t{\"emitinterval\",\t\"<documentme>\"},\r\n\t{\"emitintervalrand\",\t\"<documentme>\"},\r\n\t{\"emitstart\",\t\"<documentme>\"},\r\n\t{\"areaspread\",\t\"<documentme>\"},\r\n\t{\"areaspreadvert\",\t\"<documentme>\"},\r\n\t{\"offsetspread\",\t\"<documentme>\"},\r\n\t{\"offsetspreadvert\",\t\"<documentme>\"},\r\n\t{\"spawnorg\",\t\"<documentme>\"},\r\n\t{\"spawnvel\",\t\"<documentme>\"},\r\n\t{\"spawnparm1\",\t\"<documentme>\"},\r\n\t{\"spawnparm2\",\t\"<documentme>\"},\r\n\t{\"up\",\t\"<documentme>\"},\r\n\t{\"rampmode\",\t\"<documentme>\"},\r\n\t{\"rampindexlist\",\t\"<documentme>\"},\r\n\t{\"rampindex\",\t\"<documentme>\"},\r\n\t{\"ramp\",\t\"<documentme>\"},\r\n\t{\"perframe\",\t\"<documentme>\"},\r\n\t{\"averageout\",\t\"<documentme>\"},\r\n\t{\"nostate\",\t\"<documentme>\"},\r\n\t{\"nospreadfirst\",\t\"<documentme>\"},\r\n\t{\"nospreadlast\",\t\"<documentme>\"},\r\n\t{\"lightradius\",\t\"<documentme>\"},\r\n\t{\"lightradiusfade\",\t\"<documentme>\"},\r\n\t{\"lightradiusrgb\",\t\"<documentme>\"},\r\n\t{\"lightradiusrgbfade\",\t\"<documentme>\"},\r\n\t{\"lighttime\",\t\"<documentme>\"},\r\n\t{__NULL__}\r\n};\r\n\r\nstrip static textfield_t tf_particle;\r\n\r\n#define MAXPARTICLETYPES 200\r\ntypedef struct\r\n{\r\n\tstring efname;\r\n\tint start;\r\n\tint end;\r\n} particledesc_t;\r\nstrip particledesc_t pdesc[MAXPARTICLETYPES];\r\nint numptypes;\r\nstring particlesfile;\r\n\r\nstatic entity ptrailent;\r\n\r\nstatic void(string descname) saveparticle;\r\n\r\nstatic void(float newef) selecteffect =\r\n{\r\n\tif (newef == cureffect)\r\n\t\treturn;\r\n\r\n\tif (curmodified)\r\n\t\tsaveparticle(cvar_string(\"ca_particleset\"));\r\n\tcurmodified = FALSE;\r\n\r\n\tcureffect = floor(newef);\r\n\tif (cureffect < 0)\r\n\t\tcureffect = 0;\r\n\r\n\ttextfield_fill(&tf_particle, substring(particlesfile, pdesc[cureffect].start, pdesc[cureffect].end - pdesc[cureffect].start));\r\n\r\n\tif (ptrailent)\r\n\t\tremove(ptrailent);\r\n\tptrailent = world;\r\n};\r\n\r\n\r\n\r\nstatic int(string src, int *start) skipblock =\r\n{\r\n\tstring line;\r\n\tint ls = *start, le;\r\n\tint level = 0;\r\n\t/*parse a string from the start of one { to its closing }*/\r\n\r\n\twhile(1)\r\n\t{\r\n\t\tle = strstrofs(particlesfile, \"\\n\", ls);\r\n\t\tif (le < 0)\r\n\t\t\tbreak;\r\n\r\n\t\tline = substring(particlesfile, ls, le - ls);\r\n\t\ttokenize_console(line);\r\n\t\tline = argv(0);\r\n\t\tif (line == \"{\")\r\n\t\t{\r\n\t\t\tif (!level)\r\n\t\t\t\t*start = le+1;\r\n\t\t\tlevel+=1;\r\n\t\t}\r\n\t\telse if (line == \"}\")\r\n\t\t{\r\n\t\t\tif (level == 1)\r\n\t\t\t\treturn ls;\r\n\t\t\t--level;\r\n\t\t}\r\n\t\telse if (!level)\r\n\t\t\t\treturn le+1;\r\n\r\n\t\tls = le+1;\r\n\t}\r\n\t\r\n\treturn ls;\r\n};\r\n\r\nstatic void() updateloadedparticles =\r\n{\r\n\tstring line;\r\n\tint ls, le;\r\n\tint ce = cureffect;\r\n\twhile(numptypes>0)\r\n\t{\r\n\t\t--numptypes;\r\n\t\tstrunzone(pdesc[numptypes].efname);\r\n\t}\r\n\r\n\tls = 0;\r\n\twhile(1)\r\n\t{\r\n\t\tle = strstrofs(particlesfile, \"\\n\", ls);\r\n\t\tif (le < 0)\r\n\t\t\tbreak;\r\n//\t\tprint(\"line: \", substring(particlesfile, ls, le - ls), \"\\n\");\r\n\t\tif (le - ls > 7)\r\n\t\t\tif (substring(particlesfile, ls, 6) == \"r_part\")\r\n\t\t\t{\r\n\t\t\t\tline = substring(particlesfile, ls, le - ls);\r\n\t\t\t\ttokenize_console(line);\r\n\t\t\t\tif (argv(0) == \"r_part\")\r\n\t\t\t\t{\r\n\t\t\t\t\tpdesc[numptypes].efname = strzone(argv(1));\r\n\t\t\t\t\tpdesc[numptypes].start = le+1;\r\n\t\t\t\t\tle = skipblock(particlesfile, &pdesc[numptypes].start);\r\n\t\t\t\t\tpdesc[numptypes].end = le;\r\n\r\n//\t\t\t\t\tprint(\"particle: \", pdesc[numptypes].efname, \"\\n\", substring(particlesfile, pdesc[numptypes].start, pdesc[numptypes].end - pdesc[numptypes].start), \"\\n\");\r\n\t\t\t\t\tnumptypes+=1;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\tls = le+1;\r\n\t}\r\n\t\r\n\tcureffect = -1;\r\n\tselecteffect(ce);\r\n};\r\n\r\nstatic void(string descname) loadparticles =\r\n{\r\n\tfilestream f;\r\n\r\n\t/*kill old string*/\r\n\tif notnull(particlesfile)\r\n\t\tstrunzone(particlesfile);\r\n\tparticlesfile = (string)0;\r\n\tcureffect = 0;\r\n\r\n/*load new string*/\r\n\tif (descname != \"\")\r\n\t{\r\n\t\tf = fopen(strcat(\"particles/\", descname, \".cfg\"), FILE_READNL);\r\n\t\tif ((float)f >= 0)\r\n\t\t{\r\n\t\t\tparticlesfile = strzone(fgets(f));\r\n\t\t\tfclose(f);\r\n\t\t}\r\n\t}\r\n\r\n\t/*and find out where the particles are*/\r\n\tupdateloadedparticles();\r\n\r\n\tlocalcmd(particlesfile);\r\n\tlocalcmd(\"\\n\");\r\n};\r\n\r\nstatic void(string descname) saveparticle =\r\n{\r\n\tfilestream f;\r\n\tstring newfile;\r\n\r\n\t/*only do this if there's something to save*/\r\n\tif (cureffect < 0)\r\n\t\treturn;\r\n\t\t\r\n\tnewfile = textfield_strcat(&tf_particle);\r\n\tnewfile = strcat(substring(particlesfile, 0, pdesc[cureffect].start), newfile, substring(particlesfile, pdesc[cureffect].end, -1));\r\n\r\n\tstrunzone(particlesfile);\r\n\tparticlesfile = \"\";\r\n\r\n\tparticlesfile = strzone(newfile);\r\n\r\n\tif (descname != \"\")\r\n\t{\r\n\t\tf = fopen(strcat(\"particles/\", descname, \".cfg\"), FILE_WRITE);\r\n\t\tif ((float)f >= 0)\r\n\t\t{\r\n\t\t\tfputs(f, particlesfile);\r\n\t\t\tfclose(f);\r\n\t\t}\r\n\t}\r\n\r\n\t/*and recalculate where the particles are*/\r\n\tupdateloadedparticles();\r\n};\r\n\r\nstatic void() applyparticles =\r\n{\r\n\tif (tf_particle.dirty)\r\n\t{\r\n\t\tstring sln;\r\n\t\tint i;\r\n\t\tif (cureffect >= numptypes)\r\n\t\t\treturn;\r\n\r\n\t\tlocalcmd(\"r_part \\\"\");\r\n\t\tlocalcmd(pdesc[cureffect].efname);\r\n\t\tlocalcmd(\"\\\"\\n{\\n\");\r\n\t\ttextfield_localcmd(&tf_particle);\r\n\t\tlocalcmd(\"}\\n\");\r\n\r\n\t\tsln = strcat(\"+\", pdesc[cureffect].efname);\r\n\t\tfor (i = cureffect + 1; i < numptypes; i+=1)\r\n\t\t{\r\n\t\t\tif (pdesc[i].efname == sln)\r\n\t\t\t{\r\n\t\t\t\tlocalcmd(\"r_part \\\"\");\r\n\t\t\t\tlocalcmd(pdesc[i].efname);\r\n\t\t\t\tlocalcmd(\"\\\"\\n{\\n\");\r\n\t\t\t\tlocalcmd(substring(particlesfile, pdesc[i].start, pdesc[i].end - pdesc[i].start));\r\n\t\t\t\tlocalcmd(\"}\\n\");\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\ttf_particle.dirty = FALSE;\r\n\r\n\t\tcurmodified = TRUE;\r\n\t}\r\n};\r\n\r\nfloat(float keyc, float unic, vector m) editor_particles_key =\r\n{\r\n\tif (m_y > 16 && m_y < 24 && m_x < 128)\r\n\t{\r\n\t\tstring oval = strcat(cvar_string(\"ca_particleset\"));\r\n\t\tif (keyc == 127)\r\n\t\t\tcvar_set(\"ca_particleset\", substring(oval, 0, -2));\r\n\t\telse if (unic)\r\n\t\t\tcvar_set(\"ca_particleset\", strcat(oval, chr2str(unic)));\r\n\t\telse\r\n\t\t\treturn FALSE;\r\n\r\n\t\tapplyparticles();\r\n\t\tif (curmodified)\r\n\t\t\tsaveparticle(oval);\r\n\t\tcurmodified = FALSE;\r\n\r\n\t\toval = cvar_string(\"ca_particleset\");\r\n\t\tloadparticles(oval);\r\n\t\treturn TRUE;\r\n\t}\r\n\r\n\tif (m_y < 32)\r\n\t\treturn FALSE;\r\n\r\n\t/*middle mouse*/\r\n\tif (keyc == 514)\r\n\t{\r\n\t\tmousedown |= 4;\r\n\r\n\t\tapplyparticles();\r\n\t}\r\n\telse if (m_x < 128)\r\n\t{\r\n\t\tif (keyc == 512)\r\n\t\t{\r\n\t\t\tfloat ef, y;\r\n\t\t\tef = cureffect - 10;\r\n\t\t\tif (ef < 0)\r\n\t\t\t\tef = 0;\r\n\t\t\ty = 32;\r\n\r\n\t\t\tselecteffect(ef + (m_y - y)/8);\r\n\t\t}\r\n\t\treturn TRUE;\r\n\t}\r\n\telse\r\n\t{\r\n\t\treturn textfield_key(&tf_particle, keyc, unic, m - '128 32 0');\r\n\t}\r\n\treturn FALSE;\r\n};\r\n\r\nvoid(vector mousepos) editor_particles_overlay =\r\n{\r\n\tstring n;\r\n\tfloat ef;\r\n\tvector y;\r\n\tvector col;\r\n\tstring setname = cvar_string(\"ca_particleset\");\r\n\r\n\tif (!numptypes)\r\n\t{\r\n\t\tloadparticles(setname);\r\n\t}\r\n\r\n\tif (mousedown & 4 && cureffect >= 0 && cureffect < numptypes)\r\n\t{\r\n\t\tvector t = unproject(mousepos + '0 0 8192');\r\n\t\tvector o = unproject(mousepos);\r\n\t\ttraceline(o, t, TRUE, world);\r\n\r\n\t\ttrace_endpos += trace_plane_normal * 4;\r\n\t\tif (!ptrailent)\r\n\t\t{\r\n\t\t\tptrailent = spawn();\r\n\t\t\tptrailent.origin = trace_endpos;\r\n\r\n\t\t\tpointparticles(particleeffectnum(pdesc[cureffect].efname), trace_endpos, '0 0 -1', 1);\r\n\t\t}\r\n\t\t\r\n\t\t//fixme: should ONLY be done if we started dragging.\r\n\t\ttrailparticles(particleeffectnum(pdesc[cureffect].efname), ptrailent, ptrailent.origin, trace_endpos);\r\n\t\tptrailent.origin = trace_endpos;\r\n\t}\r\n\telse\tif (ptrailent)\r\n\t{\r\n\t\tremove(ptrailent);\r\n\t\tptrailent = world;\r\n\t}\r\n\r\n\tif (mousepos_y > 16 && mousepos_y < 24 && mousepos_x < 128)\r\n\t\tdrawstring('0 16 0', strcat(setname, ((time*4)&1)?\"^1\\x0b\":\"\"), '8 8 0', '1 1 1', 1, 0);\r\n\telse\r\n\t\tdrawstring('0 16 0', ((setname==\"\")?\"NO SET LOADED\":setname), '8 8 0', '1 1 1', 1, 0);\r\n\r\n\tef = cureffect - 10;\r\n\tif (ef < 0)\r\n\t\tef = 0;\r\n\ty = '0 32 0';\r\n\tfor (; ; ef+=1)\r\n\t{\r\n\t\tif (ef >= numptypes)\r\n\t\t\tbreak;\r\n\t\tn = pdesc[ef].efname;\r\n\r\n\t\tcol = '1 1 1';\r\n\t\tif (ef == cureffect)\r\n\t\t\tcol = '1 0 0';\r\n\t\tdrawrawstring(y, n, '8 8 0', col, 1, 0);\r\n\t\ty_y += 8;\r\n\t}\r\n\r\n\ttextfield_draw('128 32 0', &tf_particle, &fields[0]);\r\n};\r\n"
  },
  {
    "path": "quakec/csaddon/src/editor_terrain.qc",
    "content": "/*\r\na)\r\nb) Make a version of Mix Paint that just draws the texture onto tiles and replaces any other texture there instead of mixing, call it Tex Paint Single\r\nb2) Rename Mix Paint to Tex Paint Mix\r\nc) Add 2 buttons to incr/decrease Percentage setting\r\nd) Add option to switch from circular selection reticle to square\r\ne) Make reticle follow mouse properly (I can see it moving on the sonar map up above, but it's definitely not where I'm pointing the mouse)\r\nf) Make Tex Kill choose the circular or square reticle you selected (suggestion D), right now it only uses square\r\ng) Possibly an option to manually shrink the world bounds from the outside in (for eliminating unwanted space)\r\n*/\r\n\r\nenum\r\n{\r\n\tter_reload,\t\t//reload the entire thing\r\n\tter_save,\t\t\t//save the entire heightmap\r\n\tter_sethole,\t\t//punch a hole in the terrain\r\n\tter_height_set,\t\t//set heights to a specific value\r\n\tter_height_smooth,\t//smooth the heights slightly (non-destructive)\r\n\tter_height_spread,\t//smooth the heights slightly (leaves center as-is)\r\n\tter_height_flatten,\t//smooth the heights slightly (non-destructive), such that the middle becomes flatter faster than the edges\r\n\tter_height_raise,\t//raise the terrain in a bell (negative value to lower)\r\n\tter_height_lower,\t//lower the terrain in a bell (negative value to raise)\r\n\tter_tex_kill,\t\t//set section texture\r\n\tter_tex_get,\t\t//get section texture\r\n\tter_tex_paint,\t\t//paint a specific texture with gracefulish blending.\r\n\tter_tex_paint_single,\t//paint a texture with 100% opacity and no attenuation (other than radius)\r\n\tter_mixconcentrate,\t//figure out which is the strongest mixed texture and make it stronger\r\n\tter_mixnoise,\t\t//add random noise to the affected samples\r\n\tter_mixblur,\t\t//blur the texture mixture\r\n\tter_water_set,\t\t//lower the terrain in a bell (negative value to raise)\r\n\tter_mesh_add,\t\t//add a mesh\r\n\tter_mesh_kill,\t\t//remove meshes within the radius\r\n\tter_tint,\t\t\t//paints new colour modifiers/tints\r\n\tter_reset,\t\t\t//destroy's the entire section completely, resetting it to default.\r\n\tter_reloadsect,\t\t//reload a section, reverting changes.\r\n\tter_blank,\r\n\tter_radius,\r\n\tter_quant,\r\n\tter_strength,\r\n\tter_mesh,\r\n\tter_tintval,\r\n\tter_tex,\r\n\tter_roundpegsquarehole,\r\n\tter_count\r\n};\r\nstatic var float eradius = 256;\r\nstatic var float equant = 8;\r\nstatic var float epercent = 40;\r\nstatic var float squaretool = 0;\r\nstatic string tex[8];\r\nstatic var string tint[8] = {\"1 1 1\", \"1.2 0.9 0.9\", \"0 1 0\"};\r\nstatic string meshname;\r\nstatic var float curtool = ter_blank;\r\nstatic var float lasttool = ter_blank;\r\nstatic int painttex;\r\nstatic float mautorepeattime;\r\nstatic entity tempent;\r\nstatic var searchhandle texturesearch = (searchhandle)-1;\r\nstatic float texturesearchfirst;\r\nstatic string texturesearchhighlighted;\r\n\r\nfloat autocvar_mod_terrain_networked;\r\n\r\nvector vidsize;\r\nfloat mousedown;\r\n\r\nstatic string toolname[ter_count] =\r\n{\r\n\t\"reload\",\r\n\t\"save\",\r\n\t\"holes\",\r\n\t\"height set\",\r\n\t\"height smooth\",\r\n\t\"height spread\",\r\n\t\"height flatten\",\r\n\t\"height raise\",\r\n\t\"height lower\",\r\n\t\"tex kill\",\r\n\t\"tex get\",\r\n\t\"tex paint blend\",\r\n\t\"tex paint single\",\r\n\t\"tex concentrate\",\r\n\t\"tex noise\",\r\n\t\"tex blur\",\r\n\t\"water set\",\r\n\t\"mesh add\",\r\n\t\"mesh kill\",\r\n\t\"tex tint\",\r\n\t\"revert to default\",\r\n\t\"reload single section\",\r\n\t\"\",\r\n\t\"rad\",\r\n\t\"quant\",\r\n\t\"str\",\r\n\t\"mesh\",\r\n\t\"tex\"\r\n};\r\n\r\n#define terrain_edit terrain_editfoo\r\n\r\n__variant(float action, ...) terrain_edit = #278;\r\n\r\nvoid(vector m, float repeated) editor_do =\r\n{\r\n\tvector t = mousefar;\r\n\tvector o = mousenear;\r\n\tif (vlen(o - t) > 8192)\r\n\t\tt = o + normalize(t)*8192;\r\n\ttraceline(o, t, TRUE, world);\r\n\r\n\tself = world;\r\n\r\n\tswitch(curtool)\r\n\t{\r\n\tcase ter_reload:\r\n\t\tif (repeated)\t//don't autorepeat that...\r\n\t\t\treturn;\r\n\t\tif (autocvar_mod_terrain_networked && !isserver())\r\n\t\t\tsendevent(\"teredit\", \"f\", TEREDIT_RELOAD);\r\n\t\telse if (!(float)terrain_edit(TEREDIT_RELOAD))\r\n\t\t\tprint(\"Unable to reload terrain.\\n\");\r\n\t\telse\r\n\t\t\tprint(\"Terrain changes discarded.\\n\");\r\n\t\tbreak;\r\n\tcase ter_save:\r\n\t\tif (repeated)\t//don't autorepeat that...\r\n\t\t\treturn;\r\n\t\tif (autocvar_mod_terrain_networked && !isserver())\r\n\t\t\tsendevent(\"teredit\", \"f\", TEREDIT_SAVE);\r\n\t\telse\r\n\t\t\tprint(sprintf(\"Saved %g chunks\\n\", (float)terrain_edit(TEREDIT_SAVE)));\r\n\t\tbreak;\r\n\tcase ter_sethole:\t//use view center instead of targetted - you cannot target that which is not there\r\n\t\tif (autocvar_mod_terrain_networked && !isserver())\r\n\t\t\tsendevent(\"teredit\", \"fvff\", TEREDIT_SETHOLE, trace_endpos, eradius, equant);\r\n\t\telse\r\n\t\t\tterrain_edit(TEREDIT_SETHOLE, trace_endpos, eradius, equant);\r\n\t\tbreak;\r\n\tcase ter_height_smooth:\r\n\t\tif (autocvar_mod_terrain_networked && !isserver())\r\n\t\t\tsendevent(\"teredit\", \"fvff\", TEREDIT_HEIGHT_SMOOTH, trace_endpos, eradius, epercent/100.0);\r\n\t\telse\r\n\t\t\tterrain_edit(TEREDIT_HEIGHT_SMOOTH, trace_endpos, eradius, epercent/100.0);\r\n\t\tbreak;\r\n\tcase ter_height_spread:\r\n\t\tif (autocvar_mod_terrain_networked && !isserver())\r\n\t\t\tsendevent(\"teredit\", \"fvff\", TEREDIT_HEIGHT_SPREAD, trace_endpos, eradius, epercent/100.0);\r\n\t\telse\r\n\t\t\tterrain_edit(TEREDIT_HEIGHT_SPREAD, trace_endpos, eradius, epercent/100.0);\r\n\t\tbreak;\r\n\tcase ter_height_flatten:\r\n\t\tif (autocvar_mod_terrain_networked && !isserver())\r\n\t\t\tsendevent(\"teredit\", \"fvff\", TEREDIT_HEIGHT_FLATTEN, trace_endpos, eradius, epercent/100.0);\r\n\t\telse\r\n\t\t\tterrain_edit(TEREDIT_HEIGHT_FLATTEN, trace_endpos, eradius, epercent/100.0);\r\n\t\tbreak;\r\n\tcase ter_water_set:\r\n\t\tif (autocvar_mod_terrain_networked && !isserver())\r\n\t\t\tsendevent(\"teredit\", \"fvff\", TEREDIT_WATER_SET, trace_endpos, eradius, equant);\r\n\t\telse\r\n\t\t\tterrain_edit(TEREDIT_WATER_SET, trace_endpos, eradius, equant);\r\n\t\tbreak;\r\n\tcase ter_height_set:\r\n\t\tif (autocvar_mod_terrain_networked && !isserver())\r\n\t\t\tsendevent(\"teredit\", \"fvff\", TEREDIT_HEIGHT_SET, trace_endpos, eradius, equant);\r\n\t\telse\r\n\t\t\tterrain_edit(TEREDIT_HEIGHT_SET, trace_endpos, eradius, equant);\r\n\t\tbreak;\r\n\tcase ter_height_raise:\r\n\t\tif (autocvar_mod_terrain_networked && !isserver())\r\n\t\t\tsendevent(\"teredit\", \"fvff\", TEREDIT_HEIGHT_RAISE, trace_endpos, eradius, equant);\r\n\t\telse\r\n\t\t\tterrain_edit(TEREDIT_HEIGHT_RAISE, trace_endpos, eradius, equant);\r\n\t\tbreak;\r\n\tcase ter_height_lower:\r\n\t\tif (autocvar_mod_terrain_networked && !isserver())\r\n\t\t\tsendevent(\"teredit\", \"fvff\", TEREDIT_HEIGHT_LOWER, trace_endpos, eradius, equant);\r\n\t\telse\r\n\t\t\tterrain_edit(TEREDIT_HEIGHT_LOWER, trace_endpos, eradius, equant);\r\n\t\tbreak;\r\n\tcase ter_tex_get:\r\n\t\tstrunzone(tex[0]);\r\n\t\tstrunzone(tex[1]);\r\n\t\tstrunzone(tex[2]);\r\n\t\tstrunzone(tex[3]);\r\n\t\ttex[0] = strzone(terrain_edit(TEREDIT_TEX_GET, trace_endpos, 0, 0));\r\n\t\ttex[1] = strzone(terrain_edit(TEREDIT_TEX_GET, trace_endpos, 0, 1));\r\n\t\ttex[2] = strzone(terrain_edit(TEREDIT_TEX_GET, trace_endpos, 0, 2));\r\n\t\ttex[3] = strzone(terrain_edit(TEREDIT_TEX_GET, trace_endpos, 0, 3));\r\n\t\tbreak;\r\n//\tcase ter_mixset:\r\n//\t\tterrain_edit(curtool, trace_endpos, eradius, equant, emix);\r\n//\t\tbreak;\r\n\tcase ter_tex_paint:\r\n\t\tif (autocvar_mod_terrain_networked && !isserver())\r\n\t\t\tsendevent(\"teredit\", \"fvffs\", TEREDIT_TEX_BLEND, trace_endpos, eradius, epercent/100.0, tex[painttex]);\r\n\t\telse\r\n\t\t\tterrain_edit(TEREDIT_TEX_BLEND, trace_endpos, eradius, epercent/100.0, tex[painttex]);\r\n\t\tbreak;\r\n\tcase ter_tex_paint_single:\r\n\t\tif (autocvar_mod_terrain_networked && !isserver())\r\n\t\t\tsendevent(\"teredit\", \"fvfs\", TEREDIT_TEX_REPLACE, trace_endpos, eradius, tex[painttex]);\r\n\t\telse\r\n\t\t\tterrain_edit(TEREDIT_TEX_REPLACE, trace_endpos, eradius, tex[painttex]);\r\n\t\tbreak;\r\n\tcase ter_tex_kill:\r\n\t\tif (autocvar_mod_terrain_networked && !isserver())\r\n\t\t\tsendevent(\"teredit\", \"fvffs\", TEREDIT_TEX_KILL, trace_endpos, eradius, equant, tex[painttex]);\r\n\t\telse\r\n\t\t\tterrain_edit(TEREDIT_TEX_KILL, trace_endpos, eradius, equant, tex[painttex]);\r\n\t\tbreak;\r\n\tcase ter_reset:\r\n\t\tif (autocvar_mod_terrain_networked && !isserver())\r\n\t\t\tsendevent(\"teredit\", \"fvf\", TEREDIT_RESET_SECT, trace_endpos, eradius);\r\n\t\telse\r\n\t\t\tterrain_edit(TEREDIT_RESET_SECT, trace_endpos, eradius);\r\n\t\tbreak;\r\n\tcase ter_reloadsect:\r\n\t\tif (autocvar_mod_terrain_networked && !isserver())\r\n\t\t\tsendevent(\"teredit\", \"fvf\", TEREDIT_RELOAD_SECT, trace_endpos, eradius);\r\n\t\telse\r\n\t\t\tterrain_edit(TEREDIT_RELOAD_SECT, trace_endpos, eradius);\r\n\t\tbreak;\r\n\tcase ter_mixconcentrate:\r\n\t\tif (autocvar_mod_terrain_networked && !isserver())\r\n\t\t\tsendevent(\"teredit\", \"fvff\", TEREDIT_TEX_UNIFY, trace_endpos, eradius, equant);\r\n\t\telse\r\n\t\t\tterrain_edit(TEREDIT_TEX_UNIFY, trace_endpos, eradius, equant);\r\n\t\tbreak;\r\n\tcase ter_mixnoise:\r\n\t\tif (autocvar_mod_terrain_networked && !isserver())\r\n\t\t\tsendevent(\"teredit\", \"fvff\", TEREDIT_TEX_NOISE, trace_endpos, eradius, equant);\r\n\t\telse\r\n\t\t\tterrain_edit(TEREDIT_TEX_NOISE, trace_endpos, eradius, equant);\r\n\t\tbreak;\r\n\tcase ter_mixblur:\r\n\t\tif (autocvar_mod_terrain_networked && !isserver())\r\n\t\t\tsendevent(\"teredit\", \"fvff\", TEREDIT_TEX_BLUR, trace_endpos, eradius, equant);\r\n\t\telse\r\n\t\t\tterrain_edit(TEREDIT_TEX_BLUR, trace_endpos, eradius, equant);\r\n\t\tbreak;\r\n\r\n\tcase ter_tint:\r\n\t\tif (autocvar_mod_terrain_networked && !isserver())\r\n\t\t\tsendevent(\"teredit\", \"fvffvf\", TEREDIT_TINT, trace_endpos, eradius, epercent/100.0, stov(tint[painttex]), 1);\r\n\t\telse\r\n\t\t\tterrain_edit(TEREDIT_TINT, trace_endpos, eradius, epercent/100.0, stov(tint[painttex]), 1);\r\n\t\tbreak;\r\n\r\n\tcase ter_mesh_add:\r\n\t\tif (repeated)\t//don't autorepeat this one.\r\n\t\t\tbreak;\r\n\t\tterrain_edit(TEREDIT_MESH_ADD, tempent);\r\n\t\tbreak;\r\n\tcase ter_mesh_kill:\r\n\t\tif (autocvar_mod_terrain_networked && !isserver())\r\n\t\t\tsendevent(\"teredit\", \"fvf\", TEREDIT_MESH_KILL, trace_endpos, eradius);\r\n\t\telse\r\n\t\t\tterrain_edit(TEREDIT_MESH_KILL, trace_endpos, eradius);\r\n\t\tbreak;\r\n\t}\r\n};\r\n\r\nfloat(float keyc, float unic, vector m) editor_terrain_key =\r\n{\r\n\tfloat nt;\r\n\tif (curtool >= ter_radius && curtool <= ter_tex)\r\n\t{\r\n\t\tstring txt = \"\";\r\n\t\tnt = curtool;\r\n\t\tif (curtool == ter_tex)\r\n\t\t{\r\n\t\t\tif (keyc == 512 && m_x > 128)\r\n\t\t\t{\r\n\t\t\t\ttxt = texturesearchhighlighted;\r\n\t\t\t\tnt = ter_tex_paint;\r\n\t\t\t}\r\n\t\t\tif (keyc == 515)\r\n\t\t\t\ttexturesearchfirst += floor((vidsize_x)/128) - 1;\r\n\t\t\tif (keyc == 516)\r\n\t\t\t\ttexturesearchfirst -= floor((vidsize_x)/128) - 1;\r\n\t\t}\r\n\r\n\t\tif (curtool == ter_radius)\r\n\t\t\ttxt = itos((int)fabs(eradius));\r\n\t\tif (curtool == ter_quant)\r\n\t\t\ttxt = itos((int)equant);\r\n\t\tif (curtool == ter_strength)\r\n\t\t\ttxt = itos((int)epercent);\r\n\r\n\t\tif (curtool == ter_mesh)\r\n\t\t\ttxt = meshname;\r\n\t\tif (curtool == ter_tintval)\r\n\t\t\ttxt = tint[painttex];\r\n\t\tif (curtool == ter_tex)\r\n\t\t\ttxt = tex[painttex];\r\n\t\t\t\r\n\t\tif (keyc == 10 || keyc == 13)\r\n\t\t\tnt = lasttool;\r\n\t\telse if (keyc == 127)\r\n\t\t\ttxt = substring(txt, 0, -2);\r\n\t\telse if (keyc == 8)\r\n\t\t\ttxt = \"\";\r\n\t\telse if (unic)\r\n\t\t\ttxt = strcat(txt, chr2str(unic));\r\n\r\n\t\tif (curtool == ter_radius)\r\n\t\t{\r\n\t\t\teradius = fabs(stof(txt));\r\n\t\t\tif (squaretool)\r\n\t\t\t\teradius *= -1;\r\n\t\t}\r\n\t\tif (curtool == ter_quant)\r\n\t\t\tequant = stof(txt);\r\n\t\tif (curtool == ter_strength)\r\n\t\t\tepercent = stof(txt);\r\n\t\tif (curtool == ter_mesh)\r\n\t\t{\r\n\t\t\ttxt = strzone(txt);\r\n\t\t\tstrunzone(meshname);\r\n\t\t\tmeshname = txt;\r\n\t\t}\r\n\t\tif (curtool == ter_tex)\r\n\t\t{\r\n\t\t\ttxt = strzone(txt);\r\n\t\t\tstrunzone(tex[painttex]);\r\n\t\t\ttex[painttex] = txt;\r\n\t\t}\r\n\t\tif (curtool == ter_tintval)\r\n\t\t{\r\n\t\t\ttxt = strzone(txt);\r\n\t\t\tstrunzone(tint[painttex]);\r\n\t\t\ttint[painttex] = txt;\r\n\t\t}\r\n\r\n\t\tif (curtool != nt)\r\n\t\t{\r\n\t\t\tlasttool = curtool;\r\n\t\t\tcurtool = nt;\r\n\t\t}\r\n\t}\r\n\telse if (keyc == 13 || (keyc == 512 && m_x < 128))\r\n\t{\r\n\t\tif (m_x < 128)\r\n\t\t{\r\n\t\t\tnt = floor((m_y-16) / 8);\r\n\t\t\tif (nt != curtool)\r\n\t\t\t{\r\n\t\t\t\tif (nt == ter_roundpegsquarehole)\r\n\t\t\t\t{\r\n\t\t\t\t\tsquaretool = !squaretool;\r\n\t\t\t\t\teradius = fabs(eradius);\r\n\t\t\t\t\tif (squaretool) eradius *= -1;\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\tlasttool = curtool;\r\n\t\t\t\t\tcurtool = nt;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t\telse if (keyc == 13)\r\n\t\t{\r\n\t\t\teditor_do(m, FALSE);\r\n\t\t}\r\n\t}\r\n\telse if (unic == '+' || unic == '=')\r\n\t\teradius += squaretool?-16:16;\r\n\telse if (unic == '-')\r\n\t{\r\n\t\teradius = fabs(eradius) - 16;\r\n\t\tif (eradius < 0)\r\n\t\t\teradius = 0;\r\n\t\tif (squaretool) eradius *= -1;\r\n\t}\r\n\telse if (curtool == ter_mesh_add && tempent)\r\n\t{\r\n\t\tif (unic == '[')\r\n\t\t\ttempent.angles_y -= 12.5;\r\n\t\telse if (unic == ']')\r\n\t\t\ttempent.angles_y += 12.5;\r\n\t\telse if (unic == '{')\r\n\t\t\ttempent.angles_x -= 12.5;\r\n\t\telse if (unic == '}')\r\n\t\t\ttempent.angles_x += 12.5;\r\n\t\telse if (unic == '(')\r\n\t\t\ttempent.angles_z -= 12.5;\r\n\t\telse if (unic == ')')\r\n\t\t\ttempent.angles_z += 12.5;\r\n\t\telse if (unic == '@')\r\n\t\t{\r\n\t\t\ttempent.angles_x = gettime(5)*360*360;\r\n\t\t\ttempent.angles_y = gettime(5)*360;\r\n\t\t\ttempent.angles_z = gettime(5)*360*360*360;\r\n\t\t}\r\n\t\telse if (keyc == 127 || keyc == 8)\r\n\t\t{\r\n\t\t\ttempent.angles_x = 0;\r\n\t\t\ttempent.angles_y = 0;\r\n\t\t\ttempent.angles_z = 0;\r\n\t\t}\r\n\t\telse\r\n\t\t\treturn FALSE;\r\n\t}\r\n\telse if (unic == '(')\r\n\t\tepercent -= 10;\r\n\telse if (unic == ')')\r\n\t\tepercent += 10;\r\n\telse if (unic == '[')\r\n\t\tequant -= 1;\r\n\telse if (unic == ']')\r\n\t\tequant += 1;\r\n\telse if (unic == '{')\r\n\t\tpainttex = (painttex + 7) & 7;\r\n\telse if (unic == '}')\r\n\t\tpainttex = (painttex + 1) & 7;\r\n\telse if (unic == '1')\r\n\t\tpainttex = 0;\r\n\telse if (unic == '2')\r\n\t\tpainttex = 1;\r\n\telse if (unic == '3')\r\n\t\tpainttex = 2;\r\n\telse if (unic == '4')\r\n\t\tpainttex = 3;\r\n\telse if (unic == '5')\r\n\t\tpainttex = 4;\r\n\telse if (unic == '6')\r\n\t\tpainttex = 5;\r\n\telse if (unic == '7')\r\n\t\tpainttex = 6;\r\n\telse if (unic == '8')\r\n\t\tpainttex = 7;\r\n\telse\r\n\t\treturn FALSE;\r\n\r\n\tif (curtool == ter_save || curtool == ter_reload)\r\n\t{\r\n\t\teditor_do('0 0 0', FALSE);\r\n\t\tcurtool = -1;\r\n\t}\r\n\treturn TRUE;\r\n};\r\n\r\nvoid(vector mousepos) editor_terrain_addentities =\r\n{\r\n\tfloat s,c;\r\n\tfloat r;\r\n\tvector tx, p, col;\r\n\tfloat a;\r\n\t\r\n\tif (curtool == ter_tex_paint || curtool == ter_tex_paint_single || curtool == ter_tex)\r\n\t\tterrain_edit(TEREDIT_TEX_SETMASK, tex[painttex]);\r\n\t\r\n\tif (mousepos_x < 128)\r\n\t\treturn;\r\n\r\n\tvector t = mousefar;\r\n\tvector o = mousenear;\r\n\tif (vlen(o - t) > 8192)\r\n\t\tt = o + normalize(t)*8192;\r\n\ttraceline(o, t, TRUE, world);\r\n\r\n\tif (curtool == ter_mesh_add || curtool == ter_mesh)\r\n\t{\r\n\t\tif (!tempent)\r\n\t\t\ttempent = spawn();\r\n\r\n\t\tprecache_model(meshname); /*just to silence it*/\r\n\t\tsetmodel(tempent, meshname);\r\n\t\tsetorigin(tempent, trace_endpos);\r\n\t\ttempent.scale = eradius/256;\r\n\t\taddentity(tempent);\r\n\t}\r\n\telse\r\n\t{\r\n\t\tshaderforname(\"terrainedit\",\r\n\t\t\"{\"\r\n\t\t\t\"{\\n\"\r\n\t\t\t\t\"map terrainedit\\n\"\r\n\t\t\t\t\"blendfunc add\\n\"\r\n\t\t\t\t\"rgbgen vertex\\n\"\r\n\t\t\t\t\"alphagen vertex\\n\"\r\n\t\t\t\"}\\n\"\r\n\t\t\"}\");\r\n\r\n\t\tr = eradius;//sqrt(eradius*eradius/2);\r\n\r\n\t\ts = sin(gettime(5)) * r;\r\n\t\tc = cos(gettime(5)) * r;\r\n\r\n\t\tcol = [(sin(gettime(5))+1.5)*0.1,  0,  0];\r\n\r\n\t\tt = trace_endpos;\r\n\t\tR_BeginPolygon(\"terrainedit\");\r\n\t\ttx_z = 0;\r\n\t\tfor (a = 0; a < 3.14*2; a += 3.14*2/8)\r\n\t\t{\r\n\t\t\ttx_x = sin(a);\r\n\t\t\ttx_y = cos(a);\r\n\r\n\t\t\t//-1 -1\r\n\t\t\tp_x = t_x + tx_x*c - tx_y*s;\r\n\t\t\tp_y = t_y + tx_y*c + tx_x*s;\r\n\t\t\tp_z = t_z + 5;\r\n\t\t\ttx = (tx*0.5)+'0.5 0.5 0';\r\n\t\t\tR_PolygonVertex(p, tx, col, 1);\r\n\t\t}\r\n\t\tR_EndPolygon();\r\n\t}\r\n};\r\n\r\nvoid(vector mousepos) editor_terrain_overlay =\r\n{\r\n\tfloat i;\r\n\tvector pos;\r\n\tvector colour;\r\n\t\r\n//\tif (curtool == ter_tex_paint || curtool == ter_tex_paint_single || curtool == ter_tex)\r\n\t\tterrain_edit(TEREDIT_TEX_SETMASK, 0);\r\n\r\n\tfloat ctime = gettime(5);\r\n\tif (mautorepeattime)\r\n\t{\r\n\t\tif (mousedown != 1)\r\n\t\t\tmautorepeattime = 0;\r\n\t\telse if (mautorepeattime < ctime && ter_mesh_add)\r\n\t\t{\r\n\t\t\tmautorepeattime = ctime + 0.05;\r\n\t\t\teditor_do(mousepos, TRUE);\r\n\t\t}\r\n\t}\r\n\telse if (mousedown == 1)\r\n\t{\r\n\t\tmautorepeattime = ctime + 0.5;\r\n\t\teditor_do(mousepos, FALSE);\r\n\t}\r\n\r\n\tpos = '128 0 0';\r\n\tpos_y = vidsize_y - 32;\r\n\tdrawfill('0 16 0', pos, '0 0 0', 0.3);\r\n\r\n\tpos = '0 16 0';\r\n\tfor (i = 0; i < ter_count; i+=1)\r\n\t{\r\n\t\tif (curtool == i)\r\n\t\t\tcolour = '1 0 0';\r\n\t\telse if (mousepos_x < 128 && mousepos_y >= pos_y && mousepos_y < pos_y + 8)\r\n\t\t\tcolour = '0 0 1';\r\n\t\telse\r\n\t\t\tcolour = '1 1 1';\r\n\r\n\t\tif (i == ter_radius)\r\n\t\t\tdrawstring(pos, sprintf(\"radius: %g\", fabs(eradius)), '8 8 0', colour, 1, 0);\r\n\t\telse if (i == ter_quant)\r\n\t\t\tdrawstring(pos, sprintf(\"quantity: %g\", equant), '8 8 0', colour, 1, 0);\r\n\t\telse if (i == ter_strength)\r\n\t\t\tdrawstring(pos, sprintf(\"percent: %g%%\", epercent), '8 8 0', colour, 1, 0);\r\n\t\telse if (i == ter_mesh)\r\n\t\t\tdrawstring(pos, sprintf(\"mesh: %s\", meshname), '8 8 0', colour, 1, 0);\r\n\t\telse if (i == ter_roundpegsquarehole)\r\n\t\t\tdrawstring(pos, sprintf(\"shape: %s\", (squaretool?\"square peg\":\"round\")), '8 8 0', colour, 1, 0);\r\n\t\telse if (i == ter_tex)\r\n\t\t{\r\n\t\t\tif (curtool == ter_tex_get)\r\n\t\t\t{\r\n\t\t\t\tdrawstring(pos, sprintf(\"Tex0: %s\", tex[0]), '8 8 0', colour, 1, 0);\tpos_y += 8;\r\n\t\t\t\tdrawstring(pos, sprintf(\"Tex1: %s\", tex[1]), '8 8 0', colour, 1, 0);\tpos_y += 8;\r\n\t\t\t\tdrawstring(pos, sprintf(\"Tex2: %s\", tex[2]), '8 8 0', colour, 1, 0);\tpos_y += 8;\r\n\t\t\t\tdrawstring(pos, sprintf(\"Tex3: %s\", tex[3]), '8 8 0', colour, 1, 0);\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t\tdrawstring(pos, sprintf(\"Tex%1i: %s\", painttex, tex[painttex]), '8 8 0', colour, 1, 0);\r\n\t\t\tif (tex[painttex] != \"\")\r\n\t\t\t\tif (whichpack(strcat(\"textures/\", tex[painttex], \".tga\")) != \"\")\r\n\t\t\t\t\tdrawpic(pos + '0 8 0', tex[painttex], '128 128 0', '1 1 1', 1);\r\n\t\t\r\n\t\t}\r\n\t\telse if (i == ter_tintval)\r\n\t\t\tdrawstring(pos, sprintf(\"Tint%1i: %s\", painttex, tint[painttex]), '8 8 0', colour, 1, 0);\r\n\t\telse\r\n\t\t\tdrawstring(pos, toolname[i], '8 8 0', colour, 1, 0);\r\n\t\tpos_y += 8;\r\n\t}\r\n\r\n\tif notnull(texturesearchhighlighted)\r\n\t{\r\n\t\tstrunzone(texturesearchhighlighted);\r\n\t\ttexturesearchhighlighted = __NULL__;\r\n\t}\r\n\tif (curtool == ter_tex)\r\n\t{\r\n\t\tif ((float)texturesearch < 0)\r\n\t\t\ttexturesearch = search_begin(\"textures/*\", FALSE, TRUE);\r\n\t\tif (texturesearchfirst > search_getsize(texturesearch)-4)\r\n\t\t\ttexturesearchfirst = search_getsize(texturesearch)-4;\r\n\t\tif (texturesearchfirst < 0)\r\n\t\t\ttexturesearchfirst = 0;\r\n\t\ti = texturesearchfirst;\r\n\t\tpos = '128 16';\r\n\t\tlocal float x;\r\n\t\tfor (pos_y = 8; pos_y < vidsize_y; pos_y+=128+8)\r\n\t\tfor (x = 0; x < floor(vidsize_x / 128)-1; )\r\n\t\t{\r\n\t\t\tstring fname = search_getfilename(texturesearch, i);\r\n\t\t\ti+=1;\r\n\t\t\tif (fname == \"\")\r\n\t\t\t\tbreak;\r\n\t\t\tif (substring(fname, -1, 1) == \"/\")\r\n\t\t\t\tcontinue;\r\n\t\t\tdrawpic(pos + [x*128, 8], fname, '128 128', '1 1 1', 1, 0);\r\n\t\t\tif (substring(fname, 0, 9) == \"textures/\")\r\n\t\t\t\tfname = substring(fname, 9, -1);\r\n\t\t\tstring ext = substring(fname, -4, 4);\r\n\t\t\tif (ext == \".png\" || ext == \".tga\" || ext == \".jpg\")\r\n\t\t\t\tfname = substring(fname, 0, -5);\r\n\t\t\tdrawstring(pos + [x*128, 0], fname, '8 8 0', '1 1 1', 1, 0);\r\n\t\t\tx+=1;\r\n\r\n\t\t\tif (mousepos_x > pos_x+x*128 && mousepos_y > pos_y && mousepos_x < pos_x+(x+1)*128 && mousepos_y < pos_y+(128+8))\r\n\t\t\t{\r\n\t\t\t\tif notnull(texturesearchhighlighted)\r\n\t\t\t\t\tstrunzone(texturesearchhighlighted);\r\n\t\t\t\ttexturesearchhighlighted = strzone(fname);\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n};\r\n"
  },
  {
    "path": "quakec/csaddon/src/menu.qc",
    "content": "#define MF_DS_VALUE\t\t(1i << 0i)\r\n\r\ntypedef struct\r\n{\r\n\tfloat *currentf;\r\n\tint *currenti;\r\n\tfloat key;\r\n\tvector *mousepos;\r\n\tvector mousediff;\r\n\tint flags;\r\n} menu_t;\r\n\r\nint (vector topleft, vector bottomright, vector cursor) cursor_in_widget\r\n{\r\n\tif (cursor_x <= bottomright_x && cursor_x >= topleft_x)\r\n\t\tif (cursor_y <= bottomright_y && cursor_y >= topleft_y)\r\n\t\t\treturn 1;\r\n\treturn 0;\r\n}\r\n\r\nvoid(menu_t *menu, string fieldname, vector pos, float *value, float minv, float maxv) sliderf_widgit =\r\n{\r\n\tlocal vector rpos, spos, col;\r\n\tlocal float f;\r\n\tlocal int render_only;\r\n\tlocal vector mousepos = *menu->mousepos;\r\n\tlocal vector mp = *menu->mousepos;\r\n\tlocal vector mousediff = menu->mousediff;\r\n\tlocal float key = menu->key;\r\n\r\n\trender_only = 0;\r\n\r\n\trpos = pos;\r\n\tspos = rpos;\r\n\tspos_x = pos_x + strlen(fieldname) * 8 + 4;\r\n\t/*if they're dragging us, update*/\r\n\tif (menu->currentf == value)\r\n\t{\r\n\t\tif (mousedown == 1)\r\n\t\t{\r\n\t\t\t*value = *value + mousediff_x/10;\r\n\t\t\t*value = bound(minv, *value, maxv);\r\n\t\t\tf = *value;\r\n\t\t\tmp_y = spos_y + 4;\r\n\t\t\tmp_x = spos_x + 9 * 8 * (f - minv) / (maxv - minv) + 4;\r\n\t\t\t*menu->mousepos = mp;\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tmenu->currentf = (float*)__NULL__;\r\n\t\t}\r\n\t}\r\n\telse\r\n\t\tif (mousedown == 1)\r\n\t\t\trender_only = 1;\r\n\r\n\tif (key == -1 || render_only == 1 )\r\n\t{\r\n\t\tif (mousepos_y >= pos_y && mousepos_y < pos_y + 8 && render_only == 0)\r\n\t\t{\r\n\t\t\tcol_x = (sin(cltime*3.14)+1) / 2;\r\n\t\t\tcol_y = col_x;\r\n\t\t\tcol_z = 1;\r\n\t\t}\r\n\t\telse\r\n\t\t\tcol = '1 1 1';\r\n\r\n\t\tf = *value;\r\n\t\tif (f < minv)\r\n\t\t\tf = minv;\r\n\t\tif (f > maxv)\r\n\t\t\tf = maxv;\r\n\t\tdrawstring(pos, fieldname, '8 8 0', col, 1, 0);\r\n\t\tdrawstring(spos, \"^Ue080^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue082\", '8 8 0', '1 1 1', 1, 0);\r\n\t\tif (!(menu->flags & MF_DS_VALUE))\r\n\t\t\tdrawstring(spos + '88 0', ftos(f), '8 8 0', col, 1, 0);\r\n\t\tspos_x += 4 + 9*8*(f - minv) / (maxv - minv);\r\n\t\tdrawstring(spos, \"^Ue083\", '8 8 0', '1 1 1', 1, 0);\r\n\t}\r\n\telse \tif (mousepos_y >= pos_y && mousepos_y < pos_y + 8)\r\n\t{\r\n\t\tif (key == 512)\r\n\t\t{\r\n\t\t\tf = *value;\r\n\t\t\tmenu->currentf = value;\r\n\t\t\tmp_x = spos_x + 9 * 8 * (f - minv) / (maxv - minv) + 4;\r\n\t\t\t*menu->mousepos = mp;\r\n\t\t}\r\n\t\telse if (key == 515)\r\n\t\t\t*value = bound(minv, *value + (maxv - minv) / 10, maxv);\r\n\t\telse if (key == 516)\r\n\t\t\t*value = bound(minv, *value - (maxv - minv) / 10, maxv);\r\n\t}\r\n};\r\n\r\nvoid(menu_t *menu, string fieldname, vector pos, float *value) sliderf_fixed_widgit =\r\n{\r\n\tlocal vector rpos, spos, epos, col;\r\n\tlocal float f;\r\n\tlocal int render_only = 0;\r\n\tlocal vector mousepos = *menu->mousepos;\r\n\tlocal vector mp = *menu->mousepos;\r\n\tlocal vector mousediff = menu->mousediff;\r\n\tlocal vector *mpp = menu->mousepos;\r\n\tlocal float key = menu->key;\r\n\r\n\trpos = pos;\r\n\tspos = rpos;\r\n\tspos_x = pos_x + strlen(fieldname) * 8 + 4;\r\n\tepos = spos + '20 8';\r\n\tif (!(menu->flags & MF_DS_VALUE))\r\n\t\tepos_x += strlen(sprintf(\"%f\", *value)) * 8;\r\n\t/*if they're dragging us, update*/\r\n\tif (menu->currentf == value)\r\n\t{\r\n\t\tif (mousedown == 1)\r\n\t\t{\r\n\t\t\t*value = *value + mousediff_x/10;\r\n\t\t\tf = *value;\r\n\t\t\tmp_y = spos_y + 4;\r\n\t\t\tmp_x = spos_x + 4;\r\n\t\t\t*menu->mousepos = mp;\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tmenu->currentf = (float*)__NULL__;\r\n\t\t}\r\n\t}\r\n\telse\r\n\t\tif (mousedown == 1)\r\n\t\t\trender_only = 1;\r\n\r\n\tif (key == -1 || render_only == 1 )\r\n\t{\r\n\t\tif (cursor_in_widget(pos, epos, mousepos) && render_only == 0)\r\n\t\t{\r\n\t\t\tcol_x = (sin(cltime*3.14)+1) / 2;\r\n\t\t\tcol_y = col_x;\r\n\t\t\tcol_z = 1;\r\n\t\t}\r\n\t\telse\r\n\t\t\tcol = '1 1 1';\r\n\r\n\t\tf = *value;\r\n\t\tdrawstring(pos, fieldname, '8 8 0', col, 1, 0);\r\n\t\t\r\n\t\tdrawstring(spos, \"^Ue080^Ue082\", '8 8 0', '1 1 1', 1, 0);\r\n\t\tif (!(menu->flags & MF_DS_VALUE))\r\n\t\t\tdrawstring(spos + '20 0', ftos(f), '8 8 0', col, 1, 0);\r\n\t\tdrawstring(spos + '4 0', \"^Ue083\", '8 8 0', '1 1 1', 1, 0);\r\n\t}\r\n\telse \tif (cursor_in_widget(pos, epos, mousepos))\r\n\t{\r\n\t\tif (key == 512)\r\n\t\t{\r\n\t\t\tf = *value;\r\n\t\t\tmenu->currentf = value;\r\n\t\t\tmp_x = spos_x + 4;\r\n\t\t\t*mpp = mp;\r\n\t\t}\r\n\t\telse if (key == 515)\r\n\t\t\t*value = *value + 0.1;\r\n\t\telse if (key == 516)\r\n\t\t\t*value = *value - 0.1;\r\n\t}\r\n};\r\n\r\nvoid(menu_t *menu, string fieldname, vector pos, int *value, int minv, int maxv) slideri_widgit =\r\n{\r\n\tlocal vector rpos, spos, epos, col;\r\n\tlocal int f;\r\n\tlocal int render_only;\r\n\tlocal vector mousepos = *menu->mousepos;\r\n\tlocal vector mp = *menu->mousepos;\r\n\tlocal vector mousediff = menu->mousediff;\r\n\tlocal float key;\r\n\r\n\tkey = menu->key;\r\n\r\n\trender_only = 0;\r\n\r\n\trpos = pos;\r\n\tspos = rpos;\r\n\tspos_x = pos_x + strlen(fieldname) * 8 + 4;\r\n\tepos = spos + '88 8';\r\n\tif (!(menu->flags & MF_DS_VALUE))\r\n\t\tepos_x = strlen(sprintf(\"%i\", *value)) * 8;\r\n\t/*if they're dragging us, update*/\r\n\tif (menu->currenti == value)\r\n\t{\r\n\t\tif (mousedown == 1)\r\n\t\t{\r\n\t\t\t*value = *value + mousediff_x/10;\r\n\t\t\t*value = bound(minv, *value, maxv);\r\n\t\t\tf = *value;\r\n\t\t\tmp_y = spos_y + 4;\r\n\t\t\tmp_x = spos_x + 9 * 8 * (f - minv) / (maxv - minv) + 4;\r\n\t\t\t*menu->mousepos = mp;\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tmenu->currenti = (int*)__NULL__;\r\n\t\t}\r\n\t}\r\n\telse\r\n\t\tif (mousedown == 1)\r\n\t\t\trender_only = 1;\r\n\r\n\tif (key == -1 || render_only == 1 )\r\n\t{\r\n\t\tif (mousepos_y >= pos_y && mousepos_y < pos_y + 8 && render_only == 0)\r\n\t\t{\r\n\t\t\tcol_x = (sin(cltime*3.14)+1) / 2;\r\n\t\t\tcol_y = col_x;\r\n\t\t\tcol_z = 1;\r\n\t\t}\r\n\t\telse\r\n\t\t\tcol = '1 1 1';\r\n\r\n\t\tf = *value;\r\n\t\tif (f < minv)\r\n\t\t\tf = minv;\r\n\t\tif (f > maxv)\r\n\t\t\tf = maxv;\r\n\t\tdrawstring(pos, fieldname, '8 8 0', col, 1, 0);\r\n\t\tdrawstring(spos, \"^Ue080^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue082\", '8 8 0', '1 1 1', 1, 0);\r\n\t\tif (!(menu->flags & MF_DS_VALUE))\r\n\t\t\tdrawstring(spos + '88 0', ftos(f), '8 8 0', col, 1, 0);\r\n\t\tspos_x += 9*8*(f - minv) / (maxv - minv);\r\n\t\tdrawstring(spos, \"^Ue083\", '8 8 0', '1 1 1', 1, 0);\r\n\t}\r\n\telse \tif (mousepos_y >= pos_y && mousepos_y < pos_y + 8)\r\n\t{\r\n\t\tif (key == 512)\r\n\t\t{\r\n\t\t\tf = *value;\r\n\t\t\tmenu->currenti = value;\r\n\t\t\tmp_x = spos_x + 9 * 8 * (f - minv) / (maxv - minv) + 4;\r\n\t\t\t*menu->mousepos = mp;\r\n\t\t}\r\n\t\telse if (key == 515)\r\n\t\t\t*value = bound(minv, *value + 1i, maxv);\r\n\t\telse if (key == 516)\r\n\t\t\t*value = bound(minv, *value - 1i, maxv);\r\n\t}\r\n};\r\n\r\n\r\nvoid(string fieldname, vector pos, vector *mousepos_in, float *value, float key) checkboxf_widgit =\r\n{\r\n\tlocal vector rpos, col;\r\n\tlocal int render_only;\r\n\tlocal vector mousepos = *mousepos_in;\r\n\trpos = pos + '132 0 0';\r\n\r\n\trender_only = 0;\r\n\t// fix this\r\n\tif (mousedown == 1)\r\n\t\trender_only = 1;\r\n\r\n\tif (key == -1 || render_only == 1)\r\n\t{\r\n\t\tif (mousepos_y >= pos_y && mousepos_y < pos_y + 8 && render_only == 0)\r\n\t\t{\r\n\t\t\tcol_x = (sin(cltime*3.14)+1) / 2;\r\n\t\t\tcol_y = col_x;\r\n\t\t\tcol_z = 1;\r\n\t\t}\r\n\t\telse\r\n\t\t\tcol = '1 1 1';\r\n\r\n\t\tdrawstring(pos, fieldname, '8 8 0', col, 1, 0);\r\n\t\trpos_x -= 4;\r\n\t\tdrawstring(rpos, \"^Ue080^Ue082\", '8 8 0', '1 1 1', 1, 0);\r\n\t\tdrawstring(rpos + '88 0', ((*value) ?\"TRUE\":\"FALSE\"), '8 8 0', col, 1, 0);\r\n\t\trpos_x += 4;\r\n\t\tif (*value)\r\n\t\t\tdrawstring(rpos, \"^Ue083\", '8 8 0', '1 1 1', 1, 0);\r\n\t}\r\n\telse \tif (mousepos_y >= pos_y && mousepos_y < pos_y + 8)\r\n\t{\r\n\t\tif (key == 512)\r\n\t\t\t*value = !*value;\r\n\t\telse if (key == 515)\r\n\t\t\t*value = !*value;\r\n\t\telse if (key == 516)\r\n\t\t\t*value = !*value;\r\n\t}\r\n};\r\n\r\nvoid(menu_t *menu, string fieldname, vector pos, int *value) checkboxi_widgit =\r\n{\r\n\tlocal vector epos, spos, col;\r\n\tlocal int render_only = 0;\r\n\tlocal vector mousepos = *menu->mousepos;\r\n\tlocal vector mousediff = menu->mousediff;\r\n\tlocal float key = menu->key;\r\n\r\n\tspos = pos;\r\n\tspos_x += strlen(fieldname) * 8 + 4;\r\n\tepos = spos;\r\n\tepos_x += 16;\r\n\tepos_y += 8;\r\n\tif (!(menu->flags & MF_DS_VALUE))\r\n\t\tepos_x += strlen((*value) ? \"TRUE\":\"FALSE\") *8;\r\n\r\n\trender_only = 0;\r\n\r\n\tif (key == -1)\r\n\t{\r\n\t\tif (cursor_in_widget(pos, epos, mousepos) && render_only == 0)\r\n\t\t{\r\n\t\t\tcol_x = (sin(cltime*3.14)+1) / 2;\r\n\t\t\tcol_y = col_x;\r\n\t\t\tcol_z = 1;\r\n\t\t}\r\n\t\telse\r\n\t\t\tcol = '1 1 1';\r\n\r\n\t\tdrawstring(pos, fieldname, '8 8 0', col, 1, 0);\r\n\t\tdrawstring(spos, \"^Ue080^Ue082\", '8 8 0', '1 1 1', 1, 0);\r\n\t\tif (!(menu->flags & MF_DS_VALUE))\r\n\t\t\tdrawstring(spos + '88 0', ((*value) ?\"TRUE\":\"FALSE\"), '8 8 0', col, 1, 0);\r\n\t\tif (*value)\r\n\t\t\tdrawstring(spos + '4 0', \"^Ue083\", '8 8 0', '1 1 1', 1, 0);\r\n\t}\r\n\r\n\tif (cursor_in_widget(pos, epos, mousepos) && key != -1)\r\n\t{\r\n\t\tif (key == 512)\r\n\t\t\t*value = !*value;\r\n\t\telse if (key == 515)\r\n\t\t\t*value = !*value;\r\n\t\telse if (key == 516)\r\n\t\t\t*value = !*value;\r\n\t}\r\n};\r\n\r\nint (menu_t *menu, string fieldname, vector pos) button_widget =\r\n{\r\n\tlocal vector epos, spos, col;\r\n\tlocal int render_only = 0;\r\n\tlocal vector mousepos = *menu->mousepos;\r\n\tlocal vector mousediff = menu->mousediff;\r\n\tlocal float key = menu->key;\r\n\r\n\tspos = pos;\r\n\tspos_x += strlen(fieldname) * 8;\r\n\tepos = spos;\r\n\tepos_y += 8;\r\n\r\n\trender_only = 0;\r\n\r\n\tif (key == -1)\r\n\t{\r\n\t\tif (cursor_in_widget(pos, epos, mousepos) && render_only == 0)\r\n\t\t{\r\n\t\t\tcol_x = (sin(cltime*3.14)+1) / 2;\r\n\t\t\tcol_y = col_x;\r\n\t\t\tcol_z = 1;\r\n\t\t}\r\n\t\telse\r\n\t\t\tcol = '1 1 1';\r\n\r\n\t\tdrawstring(pos, fieldname, '8 8 0', col, 1, 0);\r\n\t\treturn 0;\r\n\t}\r\n\r\n\tif (cursor_in_widget(pos, epos, mousepos) && key != -1)\r\n\t{\r\n\t\tif (key == 512)\r\n\t\t\treturn 1;\r\n\t//\telse if (key == 515)\r\n\t//\telse if (key == 516)\r\n\t}\r\n\r\n\treturn 0;\r\n};\r\n\r\nvoid(string shader, vector org, vector s, vector t, string text, vector col, float alph) drawlame3dtext =\r\n{\r\n\t/*the shader must be a 16*16 grid of 256 chars*/\r\n\tvector st = draw_getimagesize(shader);\r\n\tvector pos = org;\r\n\tfloat chr;\r\n\tfloat idx = 0;\r\n\tvector tc0,tc1,tc2,tc3;\r\n\r\n\t//with GL_LINEAR sampling, the sample value is interpolated from the nearby two pixels\r\n\t//this means that we must move the texture coord inwards by half of a pixel to avoid any bleed through into neighbouring chars\r\n\t//(with nearest sampling, this isn't needed)\r\n\tif (st != '0 0 0')\r\n\t{\r\n\t\tst_x = 0.5 / st_x;\r\n\t\tst_y = 0.5 / st_y;\r\n\t}\r\n\r\n\t//precompute the st offset for each vertex\r\n\ttc0 = [(0.0/16) + st_x, (0.0/16) + st_y];\r\n\ttc1 = [(1.0/16) - st_x, (0.0/16) + st_y];\r\n\ttc2 = [(1.0/16) - st_x, (1.0/16) - st_y];\r\n\ttc3 = [(0.0/16) + st_x, (1.0/16) - st_y];\r\n\r\n\t/*begin looks up the shader and is thus potentially expensive, fte requires it only once per batch of polygons.*/\r\n\tR_BeginPolygon(shader);\r\n\r\n\twhile((chr = text[idx]))\r\n\t{\r\n\t\tidx+=1;\r\n\r\n\t\t/*handle new lines*/\r\n\t\tif (chr == '\\n')\r\n\t\t{\r\n\t\t\torg += t;\r\n\t\t\tpos = org;\r\n\t\t\tcontinue;\r\n\t\t}\r\n\r\n\t\t/*skip spaces*/\r\n\t\tif (chr != ' ')\r\n\t\t{\r\n\t\t\t/*its a grid, so the top-left texturecoord should be easy to calculate*/\r\n\t\t\t/*weirdly though, qc has no (direct) modulo functionality, not even as a builtin, so this code is ugly*/\r\n\t\t\tst_x = (chr & 15) / 16;\r\n\t\t\tst_y = floor(chr/16) / 16;\r\n\r\n\t\t\t/*plot the poly's point and emit the quad*/\r\n\t\t\tR_PolygonVertex(pos    , st + tc0, col, alph);\r\n\t\t\tR_PolygonVertex(pos + s, st + tc1, col, alph);\r\n\t\t\tR_PolygonVertex(pos+s+t, st + tc2, col, alph);\r\n\t\t\tR_PolygonVertex(pos + t, st + tc3, col, alph);\r\n\t\t\tR_EndPolygon();\r\n\t\t}\r\n\t\tpos += s;\r\n\t}\r\n};\r\n"
  },
  {
    "path": "quakec/csaddon/src/textfield.qc",
    "content": "/*reusable code to draw/edit/etc some generic text object*/\r\n/*consult the brief blurb above each function to see what it does*/\r\n\r\n/*this file deals with textfield_t pointers. allocate your textfield somewhere yourself (probably as a global, this is QC after all), before invoking this code with a pointer to it*/\r\n#define TEXTFIELD_LINES 200i\r\ntypedef struct\r\n{\r\n\tstring lines[TEXTFIELD_LINES];\r\n\tint cursorline;\r\n\tint cursorcol;\r\n\tint linecount;\r\n\tint dirty;\r\n} textfield_t;\r\n\r\n/*this is a text description thing*/\r\ntypedef struct\r\n{\r\n\tstring name;\r\n\tstring desc;\r\n} textfield_linedesc_t;\r\n\r\n/*destroys the contents of a text field, releasing strings to the system (and clears ready for reuse)*/\r\nvoid(textfield_t *fld) textfield_clean =\r\n{\r\n\twhile(fld->linecount)\r\n\t{\r\n\t\tfld->linecount = fld->linecount - 1i;\r\n\t\tstrunzone(fld->lines[fld->linecount]);\r\n\t\tfld->lines[fld->linecount] = (string)__NULL__;\r\n\t}\r\n\tfld->cursorline = 0i;\r\n\tfld->cursorcol = 0i;\r\n\tfld->dirty = TRUE; //because its changed\r\n};\r\n\r\n/*fill a text field with data from a string. new lines are supported, but leading tabs and spaces will be stripped*/\r\nvoid(textfield_t *fld, string text) textfield_fill =\r\n{\r\n\tstring line;\r\n\tfloat start, end, term;\r\n\r\n\twhile(fld->linecount)\r\n\t{\r\n\t\tfld->linecount = fld->linecount - 1i;\r\n\t\tstrunzone(fld->lines[fld->linecount]);\r\n\t\tfld->lines[fld->linecount] = (string)__NULL__;\r\n\t}\r\n\r\n\tfld->cursorline = -1i;\r\n\tfld->cursorcol = -1i;\r\n\tstart = 0;\r\n\tfor(;;)\r\n\t{\r\n\t\tend = strstrofs(text, \"\\n\", start);\r\n\t\tif (end < 0)\r\n\t\t\tbreak;\r\n\t\twhile(text[start] == '\\t')\r\n\t\t\tstart+=1;\r\nterm = end;\r\nif (term) if (text[term-1] == '\\r') term-=1;\r\n\t\tline = substring(text, start, term - start);\r\n\t\tend += 1;\r\n\r\n\t\tif (fld->linecount == TEXTFIELD_LINES)\r\n\t\t\tbreak;\r\n\t\tfld->lines[fld->linecount] = strzone(line);\r\n\t\tfld->linecount = fld->linecount + 1i;\r\n\r\n\t\tstart = end;\r\n\t}\r\n\tfld->dirty = TRUE;\r\n};\r\n\r\n\r\n/*draws the text field on the screen at a given position.\r\nthere's no size argument, so make sure data doesn't cause the display to overflow intended area (personally I'm just going to put it at the bottom+right of the screen)*/\r\nvoid(vector pos, textfield_t *fld, textfield_linedesc_t *fields) textfield_draw =\r\n{\r\n\tstring t;\r\n\tint l;\r\n\tint nummatches = 0;\r\n\tint lastmatch = 0;\r\n\tfor (l = 0i; l < fld->linecount; l+=1i, pos_y += 8)\r\n\t{\r\n\t\tif (l == fld->cursorline && ((cltime * 4) & 1))\r\n\t\t{\r\n\t\t\tt = substring(fld->lines[l], 0, fld->cursorcol);\r\n\t\t\tdrawrawstring(pos, t, '8 8 0', '1 1 1', 1, 0);\r\n\t\t\tdrawrawstring(pos + (0+fld->cursorcol) * '8 0 0', \"\\x0b\", '8 8 0', '1 0 0', 1, 0);\r\n\t\t\tt = substring(fld->lines[l], fld->cursorcol+1, -1);\r\n\t\t\tdrawrawstring(pos + (1+fld->cursorcol) * '8 0 0', t, '8 8 0', '1 1 1', 1, 0);\r\n\t\t}\r\n\t\telse\r\n\t\t\tdrawrawstring(pos, fld->lines[l], '8 8 0', '1 1 1', 1, 0);\r\n\t}\r\n\r\n\tif (fld->cursorline < 0)\r\n\t\treturn;\r\n\r\n\tpos_y += 8;\r\n\tpos_x += 64;\r\n\tstring cmd = fld->lines[fld->cursorline];\r\n\tfloat llen = strstrofs(cmd, \" \");\r\n\tif (llen < 0f)\r\n\t\tllen = strlen(cmd);\r\n\tcmd = substring(cmd, 0, llen);\r\n\r\n\t/*show command completion box*/\r\n\tfor (l = 0; fields[l].name; l+=1)\r\n\t{\r\n\t\tif (!strncmp(fields[l].name, cmd, llen))\r\n\t\t{\r\n\t\t\tnummatches+=1;\r\n\t\t\tlastmatch = l;\r\n\t\t\t/*if its an exact match, then stop here*/\r\n\t\t\tif (cmd == fields[l].name)\r\n\t\t\t{\r\n\t\t\t\tnummatches = 1;\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\tif (nummatches == 1 && strlen(fields[lastmatch].name) == llen)\r\n\t{\r\n\t\tdrawrawstring(pos, fields[lastmatch].desc, '8 8 0', '0 1 1', 1, 0);\r\n\t}\r\n\telse\r\n\t{\r\n\t\t/*show command completion box*/\r\n\t\tfor (l = 0; fields[l].name; l+=1)\r\n\t\t{\r\n\t\t\tif (!strncmp(fields[l].name, cmd, llen))\r\n\t\t\t{\r\n\t\t\t\tdrawrawstring(pos, fields[l].name, '8 8 0', '0 1 1', 1, 0);\r\n\t\t\t\tpos_y += 8;\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n};\r\n\r\n/*send the entire text field into the localcmd builtin, newlines and all*/\r\nvoid(textfield_t *fld) textfield_localcmd =\r\n{\r\n\tint l;\r\n\tfor (l = 0i; l < fld->linecount; l+=1i)\r\n\t{\r\n\t\tlocalcmd(fld->lines[l]);\r\n\t\tlocalcmd(\"\\n\");\r\n\t}\r\n};\r\n\r\n/*returns the current contents of the text field as a single tempstring (strcat, woo)*/\r\nstring(textfield_t *fld) textfield_strcat =\r\n{\r\n\tint l;\r\n\tstring res = \"\";\r\n\tfor (l = 0i; l < fld->linecount; l+=1i)\r\n\t{\r\n\t\tres = strcat(res, fld->lines[l], \"\\n\");\r\n\t}\r\n\treturn res;\r\n};\r\n\r\n/*if the user clicks+presses+etc upon the text field, do the magic here\r\nthe caller will need to track selected wigit themselves.*/\r\nfloat(textfield_t *fld, float keyc, float unic, vector m) textfield_key\r\n{\r\n\tint i;\r\n\tstring l,r;\r\n\t/*mouse*/\r\n\tif (keyc == 512)\r\n\t{\r\n\t\tfld->cursorline = floor(m_y / 8);\r\n\t\tfld->cursorcol = floor(m_x / 8);\r\n\t\tif (fld->cursorline > fld->linecount && fld->linecount)\r\n\t\t\tfld->cursorline = -1i;\r\n\t\treturn TRUE;\r\n\t}\r\n\telse if (fld->cursorline == -1i)\r\n\t{\r\n\t\t/*if its not active, do nothing*/\r\n\t\treturn FALSE;\r\n\t}\r\n\t/*escape*/\r\n\telse if (keyc == 27)\r\n\t{\r\n\t\tfld->cursorline = -1i;\r\n\t\treturn TRUE;\r\n\t}\r\n\t/*backspace*/\r\n\telse if (keyc == 127)\r\n\t{\r\n\t\tif (fld->cursorline >= fld->linecount)\r\n\t\t{\r\n\t\t\t/*we're beyond the bounds of the buffer*/\r\n\t\t\tif (fld->cursorline)\r\n\t\t\t\tfld->cursorline = fld->cursorline - 1i;\r\n\t\t}\r\n\t\telse if (fld->cursorcol)\r\n\t\t{\r\n\t\t\t/*simple delete*/\r\n\t\t\tl = substring(fld->lines[fld->cursorline], 0, fld->cursorcol-1);\r\n\t\t\tr = substring(fld->lines[fld->cursorline], fld->cursorcol, -1);\r\n\t\t\tstrunzone(fld->lines[fld->cursorline]);\r\n\t\t\tfld->lines[fld->cursorline] = strzone(strcat(l, r));\r\n\t\t\tfld->cursorcol = fld->cursorcol - 1i;\r\n\t\t}\r\n\t\telse if (fld->cursorline)\r\n\t\t{\r\n\t\t\t/*at the start of the line, merge the line with thee previous*/\r\n\t\t\tl = fld->lines[(fld->cursorline - 1i)];\r\n\t\t\tr = fld->lines[fld->cursorline];\r\n\t\t\tfld->cursorcol = strlen(l);\r\n\t\t\tl = strcat(l, r);\r\n\t\t\tstrunzone(fld->lines[fld->cursorline - 1i]);\r\n\t\t\tfld->lines[fld->cursorline - 1i] = strzone(l);\r\n\t\t\tfor (i = fld->cursorline; i < fld->linecount - 1i; i+=1)\r\n\t\t\t{\r\n\t\t\t\tfld->lines[i] = fld->lines[i + 1i];\r\n\t\t\t}\r\n\t\t\tfld->cursorline = fld->cursorline - 1i;\r\n\t\t\tfld->linecount = fld->linecount - 1i;\r\n\t\t}\r\n\t}\r\n\t/*delete*/\r\n\telse if (keyc == 8)\r\n\t{\r\n\t\tif (fld->cursorline >= fld->linecount)\r\n\t\t{\r\n\t\t\t/*we're beyond the bounds of the buffer*/\r\n\t\t\tif (fld->cursorline)\r\n\t\t\t\tfld->cursorline = fld->cursorline - 1i;\r\n\t\t}\r\n\t\telse if (fld->cursorcol >= strlen(fld->lines[fld->cursorline]))\r\n\t\t{\r\n\t\t\t/*we're at the end of the line, merge next line in to this one*/\r\n\t\t\tl = fld->lines[(fld->cursorline)];\r\n\t\t\tr = fld->lines[fld->cursorline + 1i];\r\n\t\t\tfld->cursorcol = strlen(l);\r\n\t\t\tl = strcat(l, r);\r\n\t\t\tstrunzone(fld->lines[fld->cursorline]);\r\n\t\t\tfld->lines[fld->cursorline] = strzone(l);\r\n\t\t\tfor (i = fld->cursorline + 1i; i < fld->linecount - 1i; i+=1)\r\n\t\t\t{\r\n\t\t\t\tfld->lines[i] = fld->lines[i + 1i];\r\n\t\t\t}\r\n\t\t\tfld->linecount = fld->linecount - 1i;\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\t/*simple delete*/\r\n\t\t\tl = substring(fld->lines[fld->cursorline], 0, fld->cursorcol);\r\n\t\t\tr = substring(fld->lines[fld->cursorline], fld->cursorcol + 1, -1);\r\n\t\t\tstrunzone(fld->lines[fld->cursorline]);\r\n\t\t\tfld->lines[fld->cursorline] = strzone(strcat(l, r));\r\n\t\t\tfld->cursorcol = fld->cursorcol - 1i;\r\n\t\t}\r\n\t}\r\n\t/*home*/\r\n\telse if (keyc == 151)\r\n\t{\r\n\t\tfld->cursorcol = 0;\r\n\t}\r\n\t/*end*/\r\n\telse if (keyc == 152)\r\n\t{\r\n\t\tfld->cursorcol = strlen(fld->lines[fld->cursorline]);\r\n\t}\r\n\t/*leftarrow*/\r\n\telse if (keyc == 130)\r\n\t{\r\n\t\tif (fld->cursorcol)\r\n\t\t\tfld->cursorcol = fld->cursorcol - 1i;\r\n\t}\r\n\t/*rightarrow*/\r\n\telse if (keyc == 131)\r\n\t{\r\n\t\tfld->cursorcol = fld->cursorcol + 1i;\r\n\t}\r\n\t/*uparrow*/\r\n\telse if (keyc == 128)\r\n\t{\r\n\t\tif (fld->cursorline)\r\n\t\t\tfld->cursorline = fld->cursorline - 1i;\r\n\t}\r\n\t/*downarrow*/\r\n\telse if (keyc == 129)\r\n\t{\r\n\t\tif (fld->cursorline < fld->linecount)\r\n\t\t\tfld->cursorline = fld->cursorline + 1i;\r\n\t}\r\n\t/*return*/\r\n\telse if (keyc == 13)\r\n\t{\r\n\t\tif (fld->linecount < TEXTFIELD_LINES)\r\n\t\t{\r\n\t\t\t/*shift trailing lines down*/\r\n\t\t\tfld->linecount = fld->linecount + 1i;\r\n\t\t\tfor (i = fld->linecount - 1i; i > fld->cursorline; i-=1)\r\n\t\t\t{\r\n\t\t\t\tfld->lines[i] = fld->lines[i - 1i];\r\n\t\t\t}\r\n\t\t\t/*set the new line to the second half of the current line*/\r\n\t\t\tfld->lines[fld->cursorline + 1i] = strzone(substring(fld->lines[fld->cursorline], fld->cursorcol, -1));\r\n\t\t\t/*update the old line to be the first half only*/\r\n\t\t\tr = strzone(substring(fld->lines[fld->cursorline], 0, fld->cursorcol));\r\n\t\t\tstrunzone(fld->lines[fld->cursorline]);\r\n\t\t\tfld->lines[fld->cursorline] = r;\r\n\t\t\t/*and set the cursor properly*/\r\n\t\t\tfld->cursorline = fld->cursorline + 1i;\r\n\t\t\tfld->cursorcol = 0;\r\n\t\t}\r\n\t}\r\n\t/*printable char*/\r\n\telse if (unic)\r\n\t{\r\n\t\tif (fld->cursorline >= fld->linecount)\r\n\t\t{\r\n\t\t\tfld->cursorline = fld->linecount;\r\n\t\t\tfld->linecount += 1i;\r\n\t\t\tfld->lines[fld->cursorline] = strzone(chr2str(unic));\r\n\t\t\tfld->cursorcol = 1i;\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tl = substring(fld->lines[fld->cursorline], 0, fld->cursorcol);\r\n\t\t\tr = substring(fld->lines[fld->cursorline], fld->cursorcol, -1);\r\n\t\t\tstrunzone(fld->lines[fld->cursorline]);\r\n\t\t\tfld->lines[fld->cursorline] = strzone(strcat(l, chr2str(unic), r));\r\n\t\t\tfld->cursorcol = fld->cursorcol+1i;\r\n\t\t}\r\n\r\n\t}\r\n\telse\r\n\t\treturn FALSE;\r\n\tfld->dirty = TRUE;\r\n\treturn TRUE;\r\n}\r\n"
  },
  {
    "path": "quakec/csqctest/src/common/classes.qc",
    "content": "#ifndef _CLASSES_QC_\r\n\t#define _CLASSES_QC_\r\n\r\n\r\n#define eclasses\teclass(CLASS_ROCKET, ParseRocketClass)\teclass(CLASS_GIB, ParseGibbing)\teclass(CLASS_PLAYER, ParsePlayer) eclass(CLASS_EXPLOSION, ParseExplosion) eclass(CLASS_NAIL, ParseNailClass)\r\n\r\n#ifdef CSQC\r\n#define eclass(eid,func) void(float isnew) func;\r\n\t\teclasses\r\n#undef eclass\r\n#endif\r\n\r\n\r\n\tenum {\r\n#define eclass(eid,func) eid,\r\n\t\teclasses CLASS_MAX\r\n#undef eclass\r\n\t\t};\r\n\r\n\tenum {\r\n\t\tGIB_BAD,\r\n\t\tGIB_PLAYER,\r\n\t\tGIB_SOLDIER,\r\n\t\tGIB_ZOMBIE,\r\n\t\tGIB_DEMON,\r\n\t\tGIB_OGRE,\r\n\t\tGIB_DOG,\r\n\t\tGIB_WIZARD,\r\n\t\tGIB_SHAMBLER,\r\n\t\tGIB_KNIGHT,\r\n\t\tGIB_HELLKNIGHT,\r\n\t\tGIB_FISH,\r\n\t\tGIB_ENFORCER,\r\n\t\tGIB_SHALRATH,\r\n\t\tGIB_MAX\r\n\t};\r\n#else\r\n\t#ifdef CSQC\r\n\t\tnonstatic var void(float isnew) ParseEntityClass[ ] = {\r\n#define eclass(eid,func) func ,\r\n\t\teclasses\r\n#undef eclass\r\n__NULL__\r\n\t\t};\r\n\t#endif\r\n#endif\r\n"
  },
  {
    "path": "quakec/csqctest/src/common/econstants.qc",
    "content": "float\tFALSE\t\t\t\t\t= 0;\r\nfloat \tTRUE\t\t\t\t\t= 1;\r\nfloat\tfalse = FALSE;\r\nfloat\ttrue = TRUE;\r\n\r\n// edict.flags\r\nfloat\tFL_FLY\t\t\t\t\t= 1;\r\nfloat\tFL_SWIM\t\t\t\t\t= 2;\r\nfloat\tFL_CLIENT\t\t\t\t= 8;\t// set for all client edicts\r\nfloat\tFL_INWATER\t\t\t\t= 16;\t// for enter / leave water splash\r\nfloat\tFL_MONSTER\t\t\t\t= 32;\r\nfloat\tFL_GODMODE\t\t\t\t= 64;\t// player cheat\r\nfloat\tFL_NOTARGET\t\t\t\t= 128;\t// player cheat\r\nfloat\tFL_ITEM\t\t\t\t\t= 256;\t// extra wide size for bonus items\r\nfloat\tFL_ONGROUND\t\t\t\t= 512;\t// standing on something\r\nfloat\tFL_PARTIALGROUND\t\t= 1024;\t// not all corners are valid\r\nfloat\tFL_WATERJUMP\t\t\t= 2048;\t// player jumping out of water\r\nfloat\tFL_JUMPRELEASED\t\t\t= 4096;\t// for jump debouncing\r\n\r\n// edict.solid values\r\nfloat\tSOLID_TRIGGER\t\t\t= 1;\t// touch on edge, but not blocking\r\nfloat\tSOLID_BBOX\t\t\t\t= 2;\t// touch on edge, block\r\nfloat\tSOLID_SLIDEBOX\t\t\t= 3;\t// touch on edge, but not an onground\r\nfloat\tSOLID_BSP\t\t\t\t= 4;\t// bsp clip, touch on edge, block\r\n\r\n// edict.movetype values\r\nfloat\tMOVETYPE_NONE\t\t\t= 0;\t// never moves\r\n//float\tMOVETYPE_ANGLENOCLIP\t= 1;\r\n//float\tMOVETYPE_ANGLECLIP\t\t= 2;\r\nfloat\tMOVETYPE_WALK\t\t\t= 3;\t// players only\r\nfloat\tMOVETYPE_STEP\t\t\t= 4;\t// discrete, not real time unless fall\r\nfloat\tMOVETYPE_FLY\t\t\t= 5;\r\nfloat\tMOVETYPE_TOSS\t\t\t= 6;\t// gravity\r\nfloat\tMOVETYPE_PUSH\t\t\t= 7;\t// no clip to world, push and crush\r\nfloat\tMOVETYPE_NOCLIP\t\t\t= 8;\r\nfloat\tMOVETYPE_FLYMISSILE\t\t= 9;\t// fly with extra size against monsters\r\nfloat\tMOVETYPE_BOUNCE\t\t\t= 10;\r\nfloat\tMOVETYPE_BOUNCEMISSILE\t= 11;\t// bounce with extra size\r\n\r\nfloat\tSOLID_NOT=0;\r\n\r\nvector\tVEC_ORIGIN = '0 0 0';\r\nvector\tVEC_HULL_MIN = '-16 -16 -24';\r\nvector\tVEC_HULL_MAX = '16 16 32';\r\n\r\nvector\tVEC_HULL2_MIN = '-32 -32 -24';\r\nvector\tVEC_HULL2_MAX = '32 32 64';\r\n\r\n// items\r\nfloat\tIT_AXE\t\t\t\t\t= 4096;\r\nfloat\tIT_SHOTGUN\t\t\t\t= 1;\r\nfloat\tIT_SUPER_SHOTGUN\t\t= 2;\r\nfloat\tIT_NAILGUN\t\t\t\t= 4;\r\nfloat\tIT_SUPER_NAILGUN\t\t= 8;\r\nfloat\tIT_GRENADE_LAUNCHER\t\t= 16;\r\nfloat\tIT_ROCKET_LAUNCHER\t\t= 32;\r\nfloat\tIT_LIGHTNING\t\t\t= 64;\r\nfloat\tIT_EXTRA_WEAPON\t\t\t= 128;\r\n\r\nfloat\tIT_SHELLS\t\t\t\t= 256;\r\nfloat\tIT_NAILS\t\t\t\t= 512;\r\nfloat\tIT_ROCKETS\t\t\t\t= 1024;\r\nfloat\tIT_CELLS\t\t\t\t= 2048;\r\n\r\nfloat\tIT_ARMOR1\t\t\t\t= 8192;\r\nfloat\tIT_ARMOR2\t\t\t\t= 16384;\r\nfloat\tIT_ARMOR3\t\t\t\t= 32768;\r\nfloat\tIT_SUPERHEALTH\t\t\t= 65536;\r\n\r\nfloat\tIT_KEY1\t\t\t\t\t= 131072;\r\nfloat\tIT_KEY2\t\t\t\t\t= 262144;\r\n\r\nfloat\tIT_INVISIBILITY\t\t\t= 524288;\r\nfloat\tIT_INVULNERABILITY\t\t= 1048576;\r\nfloat\tIT_SUIT\t\t\t\t\t= 2097152;\r\nfloat\tIT_QUAD\t\t\t\t\t= 4194304;\r\n\r\nfloat IT2_24 = 1;\r\nfloat IT2_25 = 2;\r\nfloat IT2_26 = 4;\r\nfloat IT2_27 = 8;\r\nfloat IT2_28 = 16;\r\nfloat IT2_RUNE1 = 32;\r\nfloat IT2_RUNE2 = 64;\r\nfloat IT2_RUNE3 = 128;\r\nfloat IT2_RUNE4 = 256;\r\n"
  },
  {
    "path": "quakec/csqctest/src/common/makeallstatic.qc",
    "content": "#ifdef FTEQCC\r\n//\t#define DOTHESTATICTHING\r\n#endif\r\n\r\n#ifdef DOTHESTATICTHING\r\n\t#pragma defaultstatic 1\r\n#else\r\n#ifndef FTEQCC\r\n\t#define static\r\n\t#define nonstatic\r\n#endif\r\n#endif"
  },
  {
    "path": "quakec/csqctest/src/common/mconstants.qc",
    "content": "OBIT_DIED\r\nOBIT_FELL\r\nOBIT_LAVA_BIG\r\nOBIT_LAVA_SMALL1\r\nOBIT_LAVA_SMALL2\r\nOBIT_SLIME1\r\nOBIT_SLIME2\r\nOBIT_DROWN1\r\nOBIT_DROWN2\r\nOBIT_TRIEDTOLEAVE\r\nOBIT_FIREBALL\r\nOBIT_SPIKED\r\nOBIT_SQUISHED\r\nOBIT_BLEWUP\r\nOBIT_MONST_ZOMBIE\r\nOBIT_MONST_SCRAG\r\nOBIT_MONST_TARBABY\r\nOBIT_MONST_SHAMBLER\r\nOBIT_MONST_SHALRATH\r\nOBIT_MONST_OLDONE\r\nOBIT_MONST_OGRE\r\nOBIT_MONST_KNIGHT\r\nOBIT_MONST_HELLKNIGHT\r\nOBIT_MONST_FISH\r\nOBIT_MONST_ENFORCER\r\nOBIT_MONST_DRAGON\r\nOBIT_MONST_DOG\r\nOBIT_MONST_FIEND\r\nOBIT_MONST_ARMY\r\nOBIT_WEAP_DISCHARGE\r\nOBIT_WEAP_SHAFT\r\nOBIT_WEAP_ROCKETGIB\r\nOBIT_WEAP_ROCKETRIDE\r\nOBIT_WEAP_GRENADEGIB\r\nOBIT_WEAP_GRENADEEAT\r\nOBIT_WEAP_PUNCTURED\r\nOBIT_WEAP_NAILED\r\nOBIT_WEAP_BUCKSHOT\r\nOBIT_WEAP_BOOMSTICK\r\nOBIT_WEAP_AXE\r\nOBIT_TEAMKILL1\r\nOBIT_TEAMKILL2\r\nOBIT_TEAMKILL3\r\nOBIT_TEAMKILL4\r\nOBIT_SELF_BORED\r\nOBIT_SELF_REPIN\r\nOBIT_SELF_DISCHARGE\r\nOBIT_TELEFRAG\r\nOBIT_TELEFRAG_REFLECT"
  },
  {
    "path": "quakec/csqctest/src/common/pmove.qc",
    "content": "/*\r\n\r\nWARNING: This entire file is pretty much GPLed.\r\nIf you want to release your csqc mod free from the GPL, do not define OWNPLAYERPHYSICS, and remove this file from your progs.src\r\n\r\n*/\r\n\r\nenumflags\r\n{\r\n\tPMF_JUMP_HELD,\r\n\tPMF_RESERVED,\r\n\tPMF_ONGROUND\r\n};\r\n\r\n#ifdef OWNPLAYERPHYSICS\r\n\r\n/*\r\nbe very careful about the fields/globals that are read/written in this code.\r\nUsing any that are changed elsewhere can and will result in prediction errors.\r\nAny fields that are expected to persist need to be added to csqc code to revert them.\r\nAny fields that are read need to be the same between csqc and ssqc code somehow. Changing such fields will result in brief errors.\r\n*/\r\n\r\n\r\n#define movevars_stepheight 22\r\n#define movevars_friction 4\r\n#define movevars_gravity 800\r\n#define movevars_accelerate 10\r\n#define movevars_stopspeed 100\r\n#define movevars_maxspeed 320\r\n#define movevars_maxairspeed 30\r\n#define movevars_jumpspeed 270\r\n#define movevars_watersinkspeed 60\r\n\r\n.float pmove_flags;\r\n\r\n#ifdef HAVE_DOTGRAVITY\r\n.float gravity;\r\n#endif\r\n.entity groundentity;\r\nstatic vector groundnormal;\r\n\r\nstatic void(entity tother) dotouch =\r\n{\r\n\tentity oself;\r\n\tif (tother.touch == __NULL__)\r\n\t\treturn;\r\n\r\n\toself = self;\r\n\t\r\n\tother = self;\r\n\tself = tother;\r\n\r\n\tself.touch();\r\n\r\n\tself = oself;\r\n};\r\n\r\n//this function 'bounces' off any surfaces that were hit\r\nvoid(vector surfnorm) PMove_Rebound =\r\n{\r\n\tfloat v;\r\n\tv = self.velocity*surfnorm;\r\n\tself.velocity = self.velocity - surfnorm*(v);\r\n\r\n\tif (surfnorm_z > 0.7)\r\n\t{\t//if we hit a ground plane then we're now on the ground.\r\n\t\tself.pmove_flags |= PMF_ONGROUND;\r\n\t\tself.groundentity = trace_ent;\r\n\t}\r\n};\r\n\r\nvoid(void) PMove_Move =\t//move forwards (preferably on the level) (does step ups)\r\n{\r\n\tvector dest;\r\n\tvector saved_plane_normal;\r\n\tfloat stepped;\r\n\tfloat movetime;\r\n\tfloat attempts;\r\n\r\n\t//we need to bounce off surfaces (in order to slide along them), so we need at 2 attempts\r\n\tfor (attempts = 3, movetime = input_timelength; movetime>0 && attempts; attempts--)\r\n\t{\r\n\t\tdest = self.origin + self.velocity*movetime;\r\n\t\ttracebox(self.origin, self.mins, self.maxs, dest, false, self);\t//try going straight there\r\n\t\tself.origin = trace_endpos;\r\n\r\n\t\tif (trace_fraction < 1)\r\n\t\t{\r\n\t\t\tsaved_plane_normal = trace_plane_normal;\r\n\r\n\t\t\tmovetime -= movetime * trace_fraction;\r\n\r\n\t\t\tif (movetime)\r\n\t\t\t{\r\n\t\t\t\t//step up if we can\r\n\t\t\t\ttrace_endpos = self.origin;\r\n\t\t\t\ttrace_endpos_z += movevars_stepheight;\r\n\t\t\t\ttracebox(self.origin, self.mins, self.maxs, trace_endpos, false, self);\r\n\t\t\t\tstepped = trace_endpos_z - self.origin_z;\r\n\r\n\t\t\t\tdest = trace_endpos + self.velocity*movetime;\r\n\t\t\t\tdest_z = trace_endpos_z;\r\n\t\t\t\t//move forwards\r\n\t\t\t\ttracebox(trace_endpos, self.mins, self.maxs, dest, false, self);\r\n\r\n\t\t\t\t//if we got anywhere, make this raised-step move count\r\n\t\t\t\tif (trace_fraction != 0)\r\n\t\t\t\t{\r\n\t\t\t\t\tmovetime -= movetime * trace_fraction;\r\n\t\t\t\t\tself.pmove_flags &= ~PMF_ONGROUND;\r\n\t\t\t\t\tif (trace_fraction < 1)\r\n\t\t\t\t\t\tPMove_Rebound(trace_plane_normal);\r\n\t\t\t\r\n\t\t\t\t\t//move down\r\n\t\t\t\t\tdest = trace_endpos;\r\n\t\t\t\t\tdest_z -= stepped+1;\r\n\t\t\t\t\ttracebox(trace_endpos, self.mins, self.maxs, dest, false, self);\r\n\t\t\t\t\tif (trace_fraction < 1)\r\n\t\t\t\t\t\tPMove_Rebound(trace_plane_normal);\r\n\r\n\t\t\t\t\tself.origin = trace_endpos;\r\n\r\n\t\t\t\t\tself.groundentity = trace_ent;\r\n\t\t\t\t\tcontinue;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\t\r\n\t\t\t//stepping failed, just bounce off\r\n\t\t\tPMove_Rebound(saved_plane_normal);\r\n\r\n\t\t\tdotouch(trace_ent);\r\n\t\t}\r\n\t\telse\r\n\t\t\tbreak;\r\n\t}\r\n};\r\nvoid() PMove_ApplyFriction =\r\n{\r\n\tfloat newspeed, oldspeed;\r\n\toldspeed = vlen(self.velocity);\r\n\tif (oldspeed < 1)\r\n\t{\r\n\t\tself.velocity = '0 0 0';\r\n\t\treturn;\r\n\t}\r\n\t\r\n\t//calculate what their new speed should be\r\n\tnewspeed = oldspeed - oldspeed*movevars_friction*input_timelength;\r\n\r\n\t//and slow them\r\n\tif (newspeed < 0)\r\n\t\tnewspeed = 0;\r\n\tself.velocity = self.velocity * (newspeed/oldspeed);\r\n};\r\n\r\nvoid(vector wishdir, float wishspeed, float accel) PMove_Accelerate =\r\n{\r\n\tfloat addspeed, accelspeed;\r\n\tfloat curspeed;\r\n\tcurspeed = self.velocity*wishdir;\t//This is wrong, but allows us to replicate many of Quake's old physics bugs... yay...\r\n\taddspeed = wishspeed - (curspeed);\r\n\tif (addspeed <= 0)\r\n\t\treturn;\r\n\taccelspeed = accel*input_timelength*wishspeed;\r\n\tif (accelspeed > addspeed)\r\n\t\taccelspeed = addspeed;\r\n\r\n\tself.velocity = self.velocity + accelspeed*wishdir;\r\n};\r\n\r\nvoid() PMove_InAirAccelerate =\r\n{\r\n\tvector hforward;\r\n\tvector hright;\r\n\tvector desireddir;\r\n\tfloat desiredspeed;\r\n\r\n\thforward = v_forward;\r\n\thforward_z = 0;\r\n\thforward = normalize(hforward);\r\n\thright = v_right;\r\n\thright_z = 0;\r\n\thright = normalize(hright);\r\n\r\n\tdesireddir = hforward*input_movevalues_x + hright*input_movevalues_y;\r\n\tdesiredspeed = vlen(desireddir);\r\n\tdesireddir = normalize(desireddir);\r\n\r\n\tif (desiredspeed > movevars_maxspeed)\r\n\t\tdesiredspeed = movevars_maxspeed;\r\n\r\n\tif (self.pmove_flags & PMF_ONGROUND)\r\n\t{\r\n\t\tif (input_buttons & 2)\r\n\t\t{\r\n\t\t\tif (!(self.pmove_flags & PMF_JUMP_HELD))\r\n\t\t\t{\r\n\t\t\t\t//make sure we get at least jumpspeed upwards from the ground plane by clamping it first.\r\n\t\t\t\tif (self.velocity*groundnormal < 0)\r\n\t\t\t\t\tself.velocity = self.velocity - groundnormal*(self.velocity*groundnormal);\r\n\t\t\t\tself.velocity_z += movevars_jumpspeed;\r\n\t\t\t\tself.pmove_flags &~= PMF_ONGROUND;\r\n\t\t\t\tself.pmove_flags |= PMF_JUMP_HELD;\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\tif (self.pmove_flags & PMF_ONGROUND)\r\n\t{\r\n\t\tPMove_ApplyFriction();\r\n\t\tPMove_Accelerate(desireddir, desiredspeed, movevars_accelerate);\r\n\t}\r\n\telse\r\n\t{\r\n\t\t//there's no friction in air...\r\n\t\tif (desiredspeed > movevars_maxairspeed)\r\n\t\t\tdesiredspeed = movevars_maxairspeed;\r\n\t\tPMove_Accelerate(desireddir, desiredspeed, movevars_accelerate);\r\n\r\n#ifdef HAVE_DOTGRAVITY\r\n\t\tif (self.gravity)\r\n\t\t\tself.velocity_z -= self.gravity * movevars_gravity * input_timelength;\r\n\t\telse\r\n#endif\r\n\t\t\tself.velocity_z -= movevars_gravity * input_timelength;\r\n\t}\r\n};\r\n\r\nvoid(float scale) PMove_NoclipAccelerate =\r\n{\r\n\tvector desireddir;\r\n\tfloat desiredspeed;\r\n\r\n\tdesireddir = v_forward*input_movevalues_x + v_right*input_movevalues_y+v_up*input_movevalues_z;\r\n\tif (input_buttons & 2)\r\n\t\tdesireddir_z = max(movevars_maxspeed, desireddir_z);\t//should be water:100, slime:80, lava:50, but lets just bake smartjump in here instead (we don't have the client's cl_upspeed value in ssqc though).\r\n\telse if (desireddir == '0 0 0' && scale<1)\r\n\t\tdesireddir_z = -movevars_watersinkspeed;\r\n\tdesiredspeed = vlen(desireddir)*scale;\r\n\tdesireddir = normalize(desireddir);\r\n\r\n\tPMove_ApplyFriction();\r\n\tPMove_Accelerate(desireddir, desiredspeed, movevars_accelerate);\r\n};\r\n\r\nvoid() PMove_Categorise =\r\n{\r\n\t//if we're moving up, we're not on the ground\r\n\tif (0)//self.velocity_z > 0)\r\n\t{\r\n\t\tself.pmove_flags &= ~PMF_ONGROUND;\r\n\t}\r\n\telse\r\n\t{\r\n\t\t//don't know, maybe we are, maybe we're not\r\n\t\ttracebox(self.origin, self.mins, self.maxs, self.origin-'0 0 1', false, self);\r\n\t\tgroundnormal = trace_plane_normal;\r\n\t\tif (trace_fraction == 1 || trace_plane_normal_z < 0.7)\r\n\t\t{\r\n\t\t\tself.pmove_flags &= ~PMF_ONGROUND;\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tself.pmove_flags |= PMF_ONGROUND;\r\n\t\t\tself.groundentity = trace_ent;\r\n\t\t}\r\n\t}\r\n\r\n\tself.waterlevel = 0;\r\n\tfloat c = pointcontents(self.origin + [0,0,1]);\r\n\tself.watertype = c;\r\n\tif (c <= CONTENT_WATER)\r\n\t{\r\n\t\tself.waterlevel = 1;\r\n\t\tc = pointcontents(self.origin + [0,0,(self.maxs_z+self.mins_z)*0.5]);\r\n\t\tif (c <= CONTENT_WATER)\r\n\t\t{\r\n\t\t\tself.watertype = c;\r\n\t\t\tself.waterlevel = 2;\r\n\r\n\t\t\t//don't bother checking eyes. who cares.\r\n\t\t}\r\n\t}\r\n};\r\n\r\nfloat roundcoordf(float f)\r\n{\t//round this coord like the networking will... ish...\r\n\t//coord rounding is often client-specific. which makes life fun.\r\n\treturn f;//rint(f*8)/8;\r\n}\r\nvector roundcoordv(vector v)\r\n{\r\n\treturn [\r\n\t\troundcoordf(v_x),\r\n\t\troundcoordf(v_y),\r\n\t\troundcoordf(v_z)\r\n\t];\r\n}\r\nvector truncv(vector v)\r\n{\r\n\treturn v & v;\r\n}\r\nstatic void Pmove_Nudge(void)\r\n{\r\n\tself.velocity = truncv(self.velocity);\r\n\tvector test, org = roundcoordv(self.origin);\r\n\r\n\tstatic float offsets[] = {0, -1./8, 1./8, -2./8, 2./8};\r\n\tfor (float z = 0; z < offsets.length; z++)\t\r\n\t{\r\n\t\ttest.z = org.z + offsets[z];\r\n\t\tfor (float y = 0; y < offsets.length; y++)\t\r\n\t\t{\r\n\t\t\ttest.y = org.y + offsets[y];\r\n\t\t\tfor (float x = 0; x < offsets.length; x++)\t\r\n\t\t\t{\r\n\t\t\t\ttest.x = org.x + offsets[x];\r\n\t\t\t\ttracebox(test, self.mins, self.maxs, test, false, self);\r\n\t\t\t\tif (!trace_startsolid)\r\n\t\t\t\t{\t//okay, that'll do\r\n\t\t\t\t\tself.origin = test;\r\n\t\t\t\t\treturn;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\tself.origin = org;\r\n};\r\n\r\n#ifdef SSQC\r\n//small hack to make sure our cvars are actually stored as serverinfo (we then access them via serverkey for consistency between ssqc+csqc, safe in the knowledge that they'll be networked for us)\r\n__accumulate void() worldspawn =\r\n{\r\n\tif (autocvar(test_qwphys, \"0\") != serverkey(\"test_qwphys\"))\r\n\t\tlocalcmd(sprintf(\"setfl test_qwphys %S s\\n\", autocvar(test_qwphys, \"0\")));\r\n\tif (autocvar(test_autobunny, \"0\") != serverkey(\"test_autobunny\"))\r\n\t\tlocalcmd(sprintf(\"setfl test_autobunny %S s\\n\", autocvar(test_autobunny, \"0\")));\r\n};\r\n#endif\r\n\r\nvoid(entity ent) PMove =\r\n{\r\n\tif (stof(serverkey(\"test_qwphys\")) && checkbuiltin(runstandardplayerphysics))\r\n\t{\r\n\t\trunstandardplayerphysics(ent);\r\n\t\treturn;\r\n\t}\r\n\r\n\tself = ent;\r\n\tmakevectors(input_angles);\r\n\r\n\tPmove_Nudge();\r\n\r\n\tif (!(input_buttons & 2) || serverkey(\"test_autobunny\"))\r\n\t\tself.pmove_flags &= ~PMF_JUMP_HELD;\r\n\r\n\tPMove_Categorise();\r\n\r\n//\tself.movetype = MOVETYPE_WALK;\r\n\r\n\tif (input_timelength >= 0)\r\n\t{\r\n\t\tswitch(self.movetype)\r\n\t\t{\r\n\t\tcase MOVETYPE_WALK:\r\n\t\t\tif (self.waterlevel >= 2)\r\n\t\t\t{\r\n\t\t\t\tPMove_NoclipAccelerate(0.7);\r\n\t\t\t\tself.velocity = self.velocity - 0.8*self.waterlevel*input_timelength*self.velocity;\t//water friction.\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t\tPMove_InAirAccelerate();\r\n\t\t\tPMove_Move();\r\n\t\t\tbreak;\r\n\t\tcase MOVETYPE_FLY:\r\n\t\t\tPMove_NoclipAccelerate(1.0);\r\n\t\t\tPMove_Move();\r\n\t\t\tbreak;\r\n\t\tcase MOVETYPE_NOCLIP:\r\n\t\t\tPMove_NoclipAccelerate(1.0);\r\n\t\t\tself.origin += self.velocity*input_timelength;\r\n\t\t\tbreak;\r\n\t\tcase MOVETYPE_NONE:\r\n\t\t\tbreak;\r\n\t\t}\r\n\r\n\t\tdotouch(self.groundentity);\r\n\t\ttouchtriggers(self);\r\n\t}\r\n\telse print(sprintf(\"timelength %g\\n\", input_timelength));\r\n\r\n\tif (self.pmove_flags & PMF_ONGROUND)\r\n\t\tself.flags |= FL_ONGROUND;\r\n\telse\r\n\t\tself.flags &= ~FL_ONGROUND;\r\n};\r\n\r\n#endif\r\n"
  },
  {
    "path": "quakec/csqctest/src/cs/constants.qc",
    "content": "//FIXME: most of this should be in common/constants.qc\r\n\r\n//\r\n// constants\r\n//\r\n\r\n// point content values\r\n\r\nfloat\tCONTENT_EMPTY\t\t\t= -1;\r\nfloat\tCONTENT_SOLID\t\t\t= -2;\r\nfloat\tCONTENT_WATER\t\t\t= -3;\r\nfloat\tCONTENT_SLIME\t\t\t= -4;\r\nfloat\tCONTENT_LAVA\t\t\t= -5;\r\nfloat\tCONTENT_SKY\t\t\t\t= -6;\r\n\r\n// sound channels\r\n// channel 0 never willingly overrides\r\n// other channels (1-7) allways override a playing sound on that channel\r\nfloat\tCHAN_AUTO\t\t= 0;\r\nfloat\tCHAN_WEAPON\t\t= 1;\r\nfloat\tCHAN_VOICE\t\t= 2;\r\nfloat\tCHAN_ITEM\t\t= 3;\r\nfloat\tCHAN_BODY\t\t= 4;\r\n\r\nfloat\tATTN_NONE\t\t= 0;\r\nfloat\tATTN_NORM\t\t= 1;\r\nfloat\tATTN_IDLE\t\t= 2;\r\nfloat\tATTN_STATIC\t\t= 3;\r\n\r\n\r\n// entity effects\r\n\r\nfloat\tEF_BRIGHTFIELD\t= 1;\r\nfloat\tEF_MUZZLEFLASH \t= 2;\r\nfloat\tEF_BRIGHTLIGHT \t= 4;\r\nfloat\tEF_DIMLIGHT \t= 8;\r\n\r\n\r\n\r\n\r\nfloat\tMASK_ENGINE\t\t= 1;\t//this is special. Any entities known by the engine but not the csqc will be added.\r\n//you can add any other masks below, remember, use bits.\r\nfloat MASK_ENGINEVIEWMODEL = 2;\t//asks the engine to add the viewmodels too\r\nfloat\tMASK_NORMAL\t\t= 4;\r\n\r\n\r\n\r\nfloat INPUT_KEYDOWN = 0;\r\nfloat INPUT_KEYUP = 1;\r\nfloat INPUT_MOUSEMOVE = 2;\r\n\r\n\r\n\r\n//stats 0-31 are filled by the engine.\r\n//they are for the 'fixed function' stuff, that works without requiring csqc.\r\n//these stats need to be standardized amoung engines, so you're not allowed to mod them, so nur.\r\n#define STAT_HEALTH 0\r\n#define STAT_WEAPONMODEL 2\t//weapon model index, as evalutated by server code.\r\n#define STAT_AMMO 3\r\n#define STAT_ARMOR\t 4\r\n#define STAT_WEAPONFRAME\t 5\r\n#define STAT_SHELLS 6\r\n#define STAT_NAILS 7\r\n#define STAT_ROCKETS 8\r\n#define STAT_CELLS 9\r\n#define STAT_ACTIVEWEAPON 10\t//the one shown on the hud\r\n#define STAT_TOTALSECRETS 11\r\n#define STAT_TOTALMONSTERS 12\r\n#define STAT_FOUNDSECRETS 13\r\n#define STAT_KILLEDMONSTERS 14\r\n#define STAT_ITEMS 15\r\n#define STAT_VIEWHEIGHT 16\r\n#define STAT_VIEWZOOM 21\t//DP extension\r\n//stats 32 onwards are filled by the csqc.\r\n\r\n\r\n#define FL_ONGROUND 1\r\n\r\n\r\n\r\nenum\r\n{\r\n\tVF_MIN = 1,\r\n\tVF_MIN_X = 2,\r\n\tVF_MIN_Y = 3,\r\n\tVF_SIZE = 4,\r\n\tVF_SIZE_X = 5,\r\n\tVF_SIZE_Y = 6,\r\n\tVF_VIEWPORT = 7,\r\n\tVF_FOV = 8,\r\n\tVF_FOVX = 9,\r\n\tVF_FOVY = 10,\r\n\tVF_ORIGIN = 11,\r\n\tVF_ORIGIN_X = 12,\r\n\tVF_ORIGIN_Y = 13,\r\n\tVF_ORIGIN_Z = 14,\r\n\tVF_ANGLES = 15,\r\n\tVF_ANGLES_X = 16,\r\n\tVF_ANGLES_Y = 17,\r\n\tVF_ANGLES_Z = 18,\r\n\tVF_DRAWWORLD = 19,\r\n\tVF_DRAWENGINESBAR = 20,\r\n\tVF_DRAWCROSSHAIR = 21\r\n};\r\n\r\nenumflags {\r\n\tRF_VIEWMODEL,\r\n\tRF_EXTERNALMODEL,\r\n\tRF_DEPTHHACK,\r\n\tRF_ADDATIVE,\r\n\tRF_USEAXIS\r\n};\r\n\r\n"
  },
  {
    "path": "quakec/csqctest/src/cs/defs.qc",
    "content": "float chasecam;\t//chasecam active\r\nentity player_local;\t//handle to the local player entity\r\nfloat isdp;\t//if we're running under DP\r\nstring levelname;\r\n\r\n//q3playerm\r\n.entity headent;\r\n.entity torsoent;\r\n.entity legsent;\r\n.entity weaponent;\r\n.float modelnum;\r\n.float animnum;\r\n.float framechangetime;\r\n\r\n//player\r\n.float lerptime;\r\n.void() removefunc;\r\n.vector lastorg;\r\n.vector lastvel;\r\n.float haddied;\r\n.float modelstyle;\r\n.string oldskin;\r\n\r\n//the genders you can have.\r\n//we're generous with three.\r\nenum {\r\n\tGENDER_NEUTER,\r\n\tGENDER_MALE,\r\n\tGENDER_FEMALE\r\n};\r\n\r\n//\r\n.float ideal_yaw;\r\n.float yaw_speed;\r\n.float tag_index;\r\n\r\n\r\n.float sveffects;\t//lower 4 is weapon, next is nopred, upper 3 is powerups\r\n#define SVE_INVIS 128\r\n#define SVE_GOD 64\r\n#define SVE_QUAD 32\r\n#define SVE_MOVETYPE 16\r\n#define SVE_WEAPONSMASK 15\r\n\r\n.float removetime;\r\n.float gibbable;\r\n\r\n//q3playerm\r\nfloat(string newmodelskin) Anim_SetModel;\r\nvoid() Anim_Draw;\r\nvoid() Anim_UnsetModel;\r\nentity() Anim_DupModel;\r\nfloat(string fname) Anim_ReadAnimationFile;\r\nfloat() Anim_GetGender;\r\nfloat(string skinname) Anim_GetHeadModelIndex;\r\nfloat(string skinname) Anim_GetHeadSkinNumber;\r\nvector(string skinname) Anim_GetHeadOffset;\r\n\r\n//prediction\r\nvoid(entity ent) Pred_PlayerUpdated;\r\nvoid(entity ent) Pred_UpdateLocalMovement;\r\nvector vieworg;\r\n\r\n//menu\r\nvoid(void(float, float, float, float) fnc) Menu_Activate;\r\nvoid() Menu_Main;\r\nvoid() Menu_Think;\r\nvar void(float event, float button, float mousex, float mousey) MenuEventFunc;\r\n\r\nvoid() Movetype_Bounce;\r\n\r\nDEFCVAR_FLOAT(cg_editor, 0)\r\nDEFCVAR_FLOAT(cg_hudtype, 2)\r\nDEFCVAR_FLOAT(cg_noerror, 0)\r\nDEFCVAR_FLOAT(cg_nostep, 0)\r\nDEFCVAR_FLOAT(cg_nopred, 0)\r\nDEFCVAR_FLOAT(cg_thirdPerson, 0)\r\nDEFCVAR_FLOAT(cg_thirdPersonAngle, 0)\r\nDEFCVAR_FLOAT(cg_thirdPersonRange, 96)\r\nDEFCVAR_FLOAT(cg_thirdPersonHeight, 24)\r\nDEFCVAR_FLOAT(cg_noselfsounds, 0)\r\nDEFCVAR_FLOAT(cg_noselfjumpsound, 0)\r\nDEFCVAR_FLOAT(v_viewheight, 0)\r\nDEFCVAR_STRING(cg_forceskin, \"\")"
  },
  {
    "path": "quakec/csqctest/src/cs/editor_lights.qc",
    "content": "/*NOTE: csaddon is more up to date, with more stuff! use that instead!*/\r\n/*FTE has some special light editing builtins, I don't ever expect them to be standard or anything, but they're handy for this*/\r\n/*If you want to edit lights for hexen2, edit this cvar, use models/s_light.spr or something.*/\r\nDEFCVAR_STRING(cg_editor_lightmodel, \"progs/s_light.spr\")\r\n\r\nstatic float selectedlight;\r\nstatic float editfield;\r\nstatic string editvalue;\r\nstatic entity tempent;\r\nvoid() editor_lights_add =\r\n{\r\n\t__using dynamiclight_get;\r\n\r\n\tfloat l;\r\n\tif (!tempent)\r\n\t\ttempent = spawn();\r\n\r\n\tl = dynamiclight_get(-1, -1);\r\n\tsetmodel(tempent, CVARS(cg_editor_lightmodel));\r\n\twhile(l > 0)\r\n\t{\r\n\t\tl = l-1;\r\n\t\tif (l == selectedlight)\r\n\t\t{\r\n\t\t\tif (time*5 & 1)\r\n\t\t\t\tcontinue;\r\n\t\t\ttempent.effects |= 8192;\r\n\t\t}\r\n\t\telse\r\n\t\t\ttempent.effects (-) 8192;\r\n\t\tif (!(float)dynamiclight_get(l, LFIELD_RADIUS))\r\n\t\t\tcontinue;\r\n\t\tsetorigin(tempent, (vector)dynamiclight_get(l, LFIELD_ORIGIN));\r\n\t\taddentity(tempent);\r\n\t}\r\n};\r\n\r\nstatic string fldname[8] = {\r\n\t\"bad\",\r\n\t\"num\",\r\n\t\"org\",\r\n\t\"rgb\",\r\n\t\"rad\",\r\n\t\"flg\",\r\n\t\"sty\",\r\n\t\"???\"\r\n};\r\nstatic string(float fld, float foredit) readfield =\r\n{\r\n\t__using dynamiclight_get;\r\n\r\n\tswitch(fld)\r\n\t{\r\n\tcase 1:\r\n\t\tif (foredit)\r\n\t\t\treturn ftos(selectedlight);\r\n\t\treturn strcat(ftos(selectedlight), \" / \", ftos(dynamiclight_get(-1, -1)));\r\n\tcase 2:\r\n\t\treturn vtos((vector)dynamiclight_get(selectedlight, LFIELD_ORIGIN));\r\n\tcase 3:\r\n\t\treturn vtos((vector)dynamiclight_get(selectedlight, LFIELD_COLOUR));\r\n\tcase 4:\r\n\t\treturn ftos(dynamiclight_get(selectedlight, LFIELD_RADIUS));\r\n\tcase 5:\r\n\t\tfloat fl = dynamiclight_get(selectedlight, LFIELD_FLAGS);\r\n\t\tif (foredit)\r\n\t\t\treturn ftos(fl);\r\n\t\treturn strcat(\t((fl & 1)?\" alwaysvisible\":\"\"),\r\n\t\t\t\t\t((fl & 2)?\" realtime\":\"\"),\r\n\t\t\t\t\t((fl & 65536)?\" no-shadows\":\"\")\r\n\t\t\t\t );\r\n\tcase 6:\r\n\t\treturn ftos(dynamiclight_get(selectedlight, LFIELD_STYLE));\r\n\tdefault:\r\n\t\treturn \"\";\r\n\t}\r\n};\r\n\r\nstatic void(float fld, string newval) writefield =\r\n{\r\n\tswitch(fld)\r\n\t{\r\n\tcase 1:\r\n\t\tselectedlight = stof(newval);\r\n\t\treturn;\r\n\tcase 2:\r\n\t\tdynamiclight_set(selectedlight, LFIELD_ORIGIN, stov(newval));\r\n\t\treturn;\r\n\tcase 3:\r\n\t\tdynamiclight_set(selectedlight, LFIELD_COLOUR, stov(newval));\r\n\t\treturn;\r\n\tcase 4:\r\n\t\tdynamiclight_set(selectedlight, LFIELD_RADIUS, stof(newval));\r\n\t\treturn;\r\n\tcase 5:\r\n\t\tdynamiclight_set(selectedlight, LFIELD_FLAGS, stof(newval));\r\n\t\treturn;\r\n\tcase 6:\r\n\t\tdynamiclight_set(selectedlight, LFIELD_STYLE, stof(newval));\r\n\t\treturn;\r\n\tdefault:\r\n\t\treturn;\r\n\t}\r\n};\r\n\r\nvoid() editor_lights_overlay =\r\n{\r\n\tfloat i;\r\n\tstring s;\r\n//void(vector position, string text, vector scale, vector rgb, float alpha, ...) drawrawstring\r\n\tdrawrawstring('0 0 0', \"LIGHTS EDITOR\", '8 8 0', '1 1 1', 1);\r\n\tfor (i = 1; i < 8; i++)\r\n\t{\r\n\t\tif (editfield == i)\r\n\t\t\ts = editvalue;\r\n\t\telse\r\n\t\t\ts = readfield(i, 0);\r\n\t\ts = strcat(ftos(i), \" \", fldname[i], \": \", s);\r\n\t\tdrawrawstring('0 32 0' + '0 8 0' * i, s, '8 8 0', ((editfield == i)?'1 0 0':'1 1 1'), 1);\r\n\t}\r\n};\r\n\r\nstatic void(vector fwd, vector vorg) selectbestlight =\r\n{\r\n\tfloat l, b=selectedlight, d, bd = 0;\r\n\tvector ldir;\r\n\tl = (float)dynamiclight_get(-1, -1);\r\n\twhile(l > 0)\r\n\t{\r\n\t\tl--;\r\n\t\tldir = (vector)dynamiclight_get(l, LFIELD_ORIGIN);\r\n\t\tldir = normalize(ldir - vorg);\r\n\t\td = fwd*ldir;\r\n\t\tif (d > bd)\r\n\t\t{\r\n\t\t\tbd = d;\r\n\t\t\tb = l;\r\n\t\t}\r\n\t}\r\n\tselectedlight = b;\r\n};\r\n\r\nfloat(float keyc, float unic) editor_lights_key =\r\n{\r\n\tvector v, o;\r\n\tstring ns;\r\n\tif (editfield)\r\n\t{\r\n\t\tns = strcat(editvalue);\r\n\t\tif (keyc == 10 || keyc == 13)\r\n\t\t{\r\n\t\t\twritefield(editfield, ns);\r\n\t\t\teditfield = 0;\r\n\t\t}\r\n\t\telse if (keyc == 127)\r\n\t\t{\r\n\t\t\tif (ns != \"\")\r\n\t\t\t\tns = substring(ns, 0, -2);\r\n\t\t}\r\n\t\telse if (keyc == 8)\r\n\t\t{\r\n\t\t\tns = \"\";\r\n\t\t}\r\n\t\telse\r\n\t\t\tns = strcat(ns, chr2str(unic));\r\n\r\n\t\tns = strzone(ns);\r\n\t\tstrunzone(editvalue);\r\n\t\teditvalue = ns;\r\n\r\n\t\twritefield(editfield, ns);\r\n\t}\r\n\telse if (keyc >= '0' && keyc <= '9')\r\n\t{\r\n\t\teditfield = keyc - '0';\r\n\t\teditvalue = strzone(readfield(editfield, 1));\r\n\t}\r\n\telse if (keyc == '=')\r\n\t\tselectedlight++;\r\n\telse if (keyc == '-')\r\n\t\tselectedlight--;\r\n\telse if (keyc == 'n')\r\n\t\tlocalcmd(\"noclip\\n\");\r\n\telse if (keyc == 's')\r\n\t{\r\n\t\tv = (vector)getviewprop(15);\r\n\t\to = (vector)getviewprop(11);\r\n\t\tmakevectors(v);\r\n\t\tselectbestlight(v_forward, o);\r\n\t}\r\n\telse if (keyc == 'm')\r\n\t{\r\n\t\tv = (vector)getviewprop(15);\r\n\t\to = (vector)getviewprop(11);\r\n\t\tmakevectors(v);\r\n\t\ttraceline(o, o + v_forward*8192, true, world);\r\n\t\tdynamiclight_set(selectedlight, LFIELD_ORIGIN, trace_endpos + trace_plane_normal*4);\r\n\t}\r\n\telse if (keyc == 'i')\r\n\t{\r\n\t\tfor (selectedlight = 32; ; selectedlight++)\r\n\t\t{\r\n\t\t\tif (!(float)dynamiclight_get(selectedlight, LFIELD_RADIUS))\r\n\t\t\t{\r\n\t\t\t\tdynamiclight_set(selectedlight, LFIELD_RADIUS, 300);\r\n\t\t\t\tv = (vector)getviewprop(15);\r\n\t\t\t\to = (vector)getviewprop(11);\r\n\t\t\t\tmakevectors(v);\r\n\t\t\t\ttraceline(o, o + v_forward*8192, true, world);\r\n\t\t\t\tdynamiclight_set(selectedlight, LFIELD_ORIGIN, trace_endpos + trace_plane_normal*4);\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\telse if (keyc == '[')\r\n\t{\r\n\t\tv = (vector)getviewprop(15);\r\n\t\to = (vector)getviewprop(11);\r\n\t\tmakevectors(v);\r\n\t\ttraceline(o, o + v_forward*8192, true, world);\r\n\t\tdynamiclight_set(selectedlight, LFIELD_ORIGIN, (vector)dynamiclight_get(selectedlight, LFIELD_ORIGIN) - trace_plane_normal);\r\n\t}\r\n\telse if (keyc == ']')\r\n\t{\r\n\t\tv = (vector)getviewprop(15);\r\n\t\to = (vector)getviewprop(11);\r\n\t\tmakevectors(v);\r\n\t\ttraceline(o, o + v_forward*8192, true, world);\r\n\t\tdynamiclight_set(selectedlight, LFIELD_ORIGIN, (vector)dynamiclight_get(selectedlight, LFIELD_ORIGIN) + trace_plane_normal);\r\n\t}\r\n\telse if (keyc == 127)\r\n\t\tdynamiclight_set(selectedlight, LFIELD_RADIUS, 0);\r\n\telse\r\n\t\treturn false;\r\n\treturn true;\r\n};\r\n"
  },
  {
    "path": "quakec/csqctest/src/cs/editor_terrain.qc",
    "content": "enum\r\n{\r\n\tter_reload = 0,\r\n\tter_save = 1,\r\n\tter_set = 2,\r\n\tter_smooth = 3,\r\n\tter_raise = 4,\r\n\tter_lower = 5\r\n};\r\n\r\n\r\n\r\nfloat(float keyc, float unic) editor_terrain_key =\r\n{\r\n\tvector v, o;\r\n\t__using terrain_edit;\r\n\tif (keyc == '[')\r\n\t{\r\n\t\tv = (vector)getviewprop(VF_ANGLES);\r\n\t\to = (vector)getviewprop(VF_ORIGIN);\r\n\t\tmakevectors(v);\r\n\t\ttraceline(o, o + v_forward*8192, true, world);\r\n\r\n\t\tterrain_edit(ter_lower, trace_endpos, 256, 8);\r\n\t}\r\n\telse if (keyc == '=')\r\n\t{\r\n\t\tv = (vector)getviewprop(VF_ANGLES);\r\n\t\to = (vector)getviewprop(VF_ORIGIN);\r\n\t\tmakevectors(v);\r\n\t\ttraceline(o, o + v_forward*8192, true, world);\r\n\r\n\t\tterrain_edit(ter_set, trace_endpos, 256, 8);\r\n\t}\r\n\r\n\telse if (keyc == 13)\r\n\t{\r\n\t\tv = (vector)getviewprop(VF_ANGLES);\r\n\t\to = (vector)getviewprop(VF_ORIGIN);\r\n\t\tmakevectors(v);\r\n\t\ttraceline(o, o + v_forward*8192, true, world);\r\n\r\n\t\tterrain_edit(ter_smooth, trace_endpos, 256, 8);\r\n\t}\r\n\telse if (keyc == ']')\r\n\t{\r\n\t\tv = (vector)getviewprop(VF_ANGLES);\r\n\t\to = (vector)getviewprop(VF_ORIGIN);\r\n\t\tmakevectors(v);\r\n\t\ttraceline(o, o + v_forward*8192, true, world);\r\n\r\n\t\tterrain_edit(ter_raise, trace_endpos, 256, 8);\r\n\t}\r\n\telse return false;\r\n\r\n\treturn true;\r\n};\r\n"
  },
  {
    "path": "quakec/csqctest/src/cs/entrypoints.qc",
    "content": "#ifdef WORKINDP\r\n\t#define show_scoreboard sb_showscores\r\n#endif\r\n\r\n#define RSES_NOLERP 1\r\n#define RSES_NOROTATE 2\r\n#define RSES_NOTRAILS 4\r\n#define RSES_NOLIGHTS 8\r\n\r\nfloat show_scoreboard;\r\nstring dbgstr;\r\nvector mousepos;\r\n\r\nvoid()\tCSQC_Shutdown = {};\r\n\r\n//check engine extensions, and give suitable warning\r\nvoid() checkengineversion =\r\n{\r\n\tif (checkextension(\"FTE_STRINGS\"))\r\n\t{\r\n\t\tisdp = substring(\" \", 0, -1) == \"\";\r\n\t\tif (isdp)\r\n\t\t\tprint(\"broken FTE_STRINGS support\\n\");\r\n\t}\r\n\telse\r\n\t\tprint(\"no FTE_STRINGS support, this mod might crash... sorry\\n\");\r\n};\r\n\r\n\r\nfloat(float isnew) RefreshOther =\r\n{\r\n\tself.drawmask = MASK_ENGINE;\r\n\tsetmodelindex(self, self.modelindex);\r\n\r\n\tif (self.entnum <= maxclients)\r\n\t\tRefreshPlayer(isnew);\r\n\telse if (self.colormap)\r\n\t\tself.modelindex = 0;\t//don't show dead bodies, we do dying ourselves.\r\n\r\n\treturn PREDRAW_AUTOADD;\r\n};\r\n\r\n\r\n\r\nfloat(string centertext) centerprinted =\r\n{\r\n//warning!\r\n//centertext is a temporary string!\r\n//warning!\r\n\r\n\treturn false;\t//we don't want to handle it, let the engine do it.\r\n};\r\n\r\n.float classnum;\r\n\r\nvoid(float isnew) CSQC_Ent_Update =\r\n{\r\n\tlocal float classtype;\r\n\tlocal var void(float isnew) fnc;\r\n\r\n\tclasstype = readbyte();\r\n\r\n//remove if the class changed.\r\n\tif (self.classnum != classtype)\r\n\t{\r\n\t\tif (self.removefunc)\r\n\t\t\tself.removefunc();\r\n\t\tisnew = true;\r\n\t\tself.classnum = classtype;\r\n\t}\r\n\r\n\tfnc = ParseEntityClass[classtype];\r\n\tif (!fnc)\r\n\t\terror(\"Unrecognised entity class: Your csqc needs updating\");\r\n\tfnc(isnew);\r\n\r\n//\tif (isnew)\r\n//\tprint(\"csqc \", self.model, \" updated at \", vtos(self.origin), \"\\n\");\r\n};\r\n\r\nvoid() regcommands =\r\n{\r\n\tregistercommand(\"tetris\");\r\n\tregistercommand(\"skinchooser\");\r\n\tregistercommand(\"randomskin\");\r\n\tregistercommand(\"test\");\r\n\tregistercommand(\"+showscores\");\r\n\tregistercommand(\"-showscores\");\r\n\tregistercommand(\"+showteamscores\");\r\n\tregistercommand(\"-showteamscores\");\r\n\r\n\tregistercommand(\"osgk\");\r\n\tregistercommand(\"osgk_command\");\r\n\tregistercommand(\"osgk_resize\");\r\n\tregistercommand(\"osgk_mousemove\");\r\n\tregistercommand(\"osgk_keypress\");\r\n\t\r\n\tregistercommand(\"test_viewinfo\");\r\n};\r\n\r\nfloat(string str) CSQC_ConsoleCommand =\r\n{\r\n\tlocal float args;\r\n\targs = tokenize(str);\r\n\tswitch(argv(0))\r\n\t{\r\n\tcase \"test\":\r\n\t\tsendevent(\"testevent\", \"fsev\", 64, \"hello world\", player_local, '73 72 71');\r\n\t\tbreak;\r\n\tcase \"tetris\":\r\n\t\tMenu_Tetris();\r\n\t\tbreak;\r\n\tcase \"skinchooser\":\r\n\t\tMenu_SkinChooser();\r\n\t\tbreak;\r\n\tcase \"randomskin\":\r\n\t\tSelectRandomSkin();\r\n\t\tbreak;\r\n\tcase \"+showscores\":\r\n\t\tshow_scoreboard = 1;\r\n\t\treturn false;\r\n\tcase \"+showteamscores\":\r\n\t\tshow_scoreboard = 2;\r\n\t\treturn false;\r\n\tcase \"-showscores\":\r\n\tcase \"-showteamscores\":\r\n\t\tshow_scoreboard = 0;\r\n\t\treturn false;\r\n\tcase \"osgk\":\r\n\t\tMenu_OSGK(argv(1));\r\n\t\tbreak;\r\n\tcase \"osgk_command\":\r\n\t\tgecko_navigate(argv(1), argv(2));\r\n\t\tbreak;\r\n\tcase \"osgk_keypress\":\r\n\t\tgecko_keyevent(argv(1), stof(argv(2)), 2);\r\n\t\tbreak;\r\n\tcase \"osgk_resize\":\r\n\t\tgecko_resize(argv(1), stof(argv(2)), stof(argv(3)));\r\n\t\tbreak;\r\n\tcase \"osgk_mousemove\":\r\n\t\tgecko_mousemove(argv(1), stof(argv(2)), stof(argv(3)));\r\n\t\tbreak;\r\n\tcase \"test_viewinfo\":\r\n\t\tif (player_local)\r\n\t\t{\r\n\t\t\tprint(sprintf(\"View origin is at %v\\n\", vieworg));\r\n\t\t\tprint(sprintf(\"local player entity %i is num %g on server, at %v\\n\", player_local, player_local.entnum, player_local.origin));\r\n\t\t}\r\n\t\telse\r\n\t\t\tprint(\"Local player entity is not known. Engine is providing the default view origin.\\n\");\r\n\t\tbreak;\r\n\tdefault:\r\n\t\treturn false;\r\n\t}\r\n\treturn true;\r\n};\r\n\r\nvoid(string line) CSQC_Parse_StuffCmd =\r\n{\r\n\tprint(sprintf(\"stufftext: %S\\n\", substring(line, 0, -2)));\r\n\tlocalcmd(line);\r\n};\r\nfloat(string msg) CSQC_Parse_CenterPrint =\r\n{\r\n\tprint(sprintf(\"centerprint: %S\\n\", msg));\r\n\tcprint(msg);\r\n\treturn false;\r\n};\r\n\r\nvoid(string msg, float type) CSQC_Parse_Print =\r\n{\r\n//\tprint(\"print: \");\r\n\tprint(msg);\r\n\r\n/*\tif (dbgstr)\r\n\t\tstrunzone(dbgstr);\r\n\tdbgstr = strzone(msg);\r\n*/\r\n};\r\n\r\nvoid() CSQC_Ent_Remove =\t//the ent disappeared on the server.\r\n{\r\n\tif (self.removefunc)\r\n\t\tself.removefunc();\r\n\tremove(self);\r\n};\r\n\r\nentity viewentity;\r\n//note that a better way of animating weapon firing would be as a response to an event, or as a clientside predicted thing.\r\nvoid(entity ve) DoThatViewModelThing =\r\n{\r\n\tfloat newframe, newmodel;\r\n\r\n\tnewframe = getstatf(STAT_WEAPONFRAME);\r\n\tnewmodel = getstatf(STAT_WEAPONMODEL);\r\n\r\n\tif (newmodel != ve.modelindex)\r\n\t{\t//changed entirly.\r\n\t\tve.modelindex = newmodel;\r\n\t\tve.frame2 = ve.frame = newframe;\r\n\t\tve.lerptime = time;\r\n\t}\r\n\telse if (newframe != ve.frame)\r\n\t{\r\n\t\tve.frame2 = ve.frame;\r\n\t\tve.frame = newframe;\r\n\t\tve.lerptime = time;\r\n\t}\r\n\tve.lerpfrac = 1-(time-ve.lerptime)*10;\r\n\r\n\tve.origin = '0 0 0';\r\n\tve.angles = '0 0 0';\r\n};\r\n\r\nvoid(float apilevel, string enginename, float engineversion) CSQC_Init =\r\n{\r\n\t__using renderflags;\r\n\r\n\tcheckengineversion();\r\n\r\n\tprecache_model(\"progs/eyes.mdl\");\r\n\tprecache_model(\"progs/player.mdl\");\r\n\tprecache_model(\"progs/missile.mdl\");\r\n\tprecache_sound(\"zombie/z_gib.wav\");\r\n\tprecache_sound(\"player/udeath.wav\");\r\n\tprecache_sound(\"weapons/r_exp3.wav\");\r\n\r\n\tviewentity = spawn();\r\n\tviewentity.renderflags = RF_VIEWMODEL|RF_DEPTHHACK;\r\n\r\n\tregcommands();\r\n\r\n\tif (checkextension(\"EXT_CSQC_1\"))\r\n\t{\r\n//\t\tdeltalisten(\"*\", RefreshOther, 0);\t//catch-all\r\n\t\tdeltalisten(\"progs/player.mdl\", RefreshPlayer, RSES_NOLERP|RSES_NOROTATE);\t//animate players/player-corpses\r\n\t}\r\n};\r\n\r\nvoid(entity ent) CSQC_DrawViewModel =\r\n{\r\n\t__using shaderforname, checkbuiltin, forceshader;\r\n\r\n\tif (player_local.sveffects & SVE_INVIS)\r\n\t{\r\n\t\tif (!checkbuiltin(shaderforname))\r\n\t\t\treturn;\r\n\t\tent.forceshader = shaderforname(\"powerups/invisibility\");\r\n\t}\r\n\telse\r\n\t\tent.forceshader = 0;\r\n\r\n\tent.fatness = 0;\r\n\tDoThatViewModelThing(ent);\r\n\r\n#ifdef WORKINDP\r\n\t//DP doesn't support RF_VIEWMODEL\r\n\t//so hack around that\r\n\tent.renderflags = RF_DEPTHHACK;\t//nope, not a view model, that just breaks things\r\n\tent.origin = vieworg;\r\n\tent.angles = input_angles;\r\n\tent.angles_x *= autocvar(r_meshpitch, -1);\r\n#endif\r\n\r\n\taddentity(ent);\r\n\tif ((player_local.sveffects & SVE_QUAD) && checkbuiltin(shaderforname))\r\n\t{\r\n\t\tent.fatness = -2;\r\n\t\tent.forceshader = shaderforname(\"powerups/quad\");\r\n\t\taddentity(ent);\r\n\t}\r\n\tif ((player_local.sveffects & SVE_GOD) && checkbuiltin(shaderforname))\r\n\t{\r\n\t\tent.fatness = -2.8;\r\n\t\tent.forceshader = shaderforname(\"powerups/regen\");\r\n\t\taddentity(ent);\r\n\t}\r\n}\r\n\r\n//a bit of fun\r\nvoid() CSQC_Input_Frame =\r\n{\r\n/*\t//swap jump/attack\r\n\tif (input_buttons == 1)\r\n\t\tinput_buttons = 2;\r\n\telse if (input_buttons == 2)\r\n\t\tinput_buttons = 1;*/\r\n\r\n\tif (input_impulse)\r\n\t\tinput_buttons |= 32|64|128;\r\n\r\n\tif (autocvar(test_pmove,0) & 1)\r\n\t\tinput_buttons |= 32;\t//print server pmoves\r\n\tif (autocvar(test_pmove,0) & 2)\r\n\t\tinput_buttons |= 64;\t//print client pmoves\r\n\tif (autocvar(test_pmove,0) & 4)\r\n\t\tinput_buttons |= 128;\t//print client partials\r\n};\r\n\r\nvoid() CSQC_Delta_Remove =\r\n{\r\n\tif (self == player_local)\r\n\t\tplayer_local = world;\r\n\r\n//\tprint(\"delt \", ftos(self.modelindex), \"(\", self.model, \") removed\\n\");\r\n\tif (self.removefunc)\r\n\t\tself.removefunc();\r\n\tremove(self);\r\n};\r\n\r\n\r\nfloat (float event, float parama, float paramb, float paramc) CSQC_InputEvent =\r\n{\r\n\tif (event == IE_KEYDOWN)\r\n\t{\r\n\t\tswitch(CVARF(cg_editor))\r\n\t\t{\r\n\t\tcase 1:\r\n\t\t\tif (editor_lights_key(parama, paramb))\r\n\t\t\t\treturn true;\r\n\t\t\tbreak;\r\n\t\tcase 2:\r\n\t\t\tif (editor_terrain_key(parama, paramb))\r\n\t\t\t\treturn true;\r\n\t\t\tbreak;\r\n\t\t}\r\n\t}\r\n\telse if (event == IE_MOUSEABS)\r\n\t\tmousepos = [parama, paramb];\r\n\telse if (event == IE_MOUSEDELTA)\r\n\t{\t//move it...\r\n\t\tmousepos += [parama, paramb];\r\n\r\n\t\t//bound it.\r\n\t\tvector ssize = getviewprop(VF_SCREENVSIZE);\r\n\t\tmousepos_x = bound(0, mousepos_x, ssize_x);\r\n\t\tmousepos_y = bound(0, mousepos_y, ssize_y);\r\n\t}\r\n\r\n\treturn Menu_InputEvent(event, parama, paramb);\r\n};\r\n\r\nvoid(float width, float height, float do2d) CSQC_UpdateView =\r\n{\r\n\tfloat hudtype = CVARF(cg_hudtype);\r\n\r\n\tchasecam = CVARF(cg_thirdPerson);\r\n\tif (getstatf(STAT_HEALTH) <= 0)\r\n\t\tchasecam = 1;\r\n\r\n\tclearscene();\r\n\r\n\t//force fullscreen views (ignore viewsize).\r\n\tsetviewprop(VF_MIN, '0 0 0');\r\n\tsetviewprop(VF_SIZE_X, width);\r\n\tsetviewprop(VF_SIZE_Y, height);\r\n#if 0\r\n\tsetviewprop(VF_MIN_X, width/2);\r\n\tsetviewprop(VF_MIN_Y, height/2);\r\n\tsetviewprop(VF_SIZE_X, width/2);\r\n\tsetviewprop(VF_SIZE_Y, height/2);\r\n#endif\r\n\r\n\tif (hudtype != 1)\r\n\t{\r\n\t\tsetviewprop(VF_DRAWENGINESBAR, 0);\r\n\t}\r\n\telse\r\n\t{\r\n\t\tsetviewprop(VF_DRAWENGINESBAR, 1);\r\n\t}\r\n\tsetviewprop(VF_DRAWCROSSHAIR, !player_local || !checkbuiltin(project));\r\n\r\n\taddentities(MASK_NORMAL|MASK_ENGINE);\r\n\r\n\tif (player_local)\r\n\t{\r\n\t\tsetviewprop(VF_ORIGIN, vieworg);\r\n\t\tsetviewprop(VF_ANGLES, view_angles);\r\n\r\n\t\tmakevectors(view_angles);\r\n\t\tSetListener(vieworg, v_forward, v_right, v_up);\r\n\r\n\t\tif (!chasecam)\r\n\t\t{\r\n\t\t\tCSQC_DrawViewModel(viewentity);\r\n\t\t}\r\n\t}\r\n\telse\r\n\t{\r\n\t\t//engine didn't tell us about our player entity. that's not right...\r\n\t\taddentities(MASK_ENGINEVIEWMODEL);\r\n\t}\r\n\r\n\tswitch(CVARF(cg_editor))\r\n\t{\r\n\tcase 1:\r\n\t\t/*add light ents*/\r\n\t\teditor_lights_add();\r\n\t\thudtype = 0;\r\n\t\tbreak;\r\n\tcase 2:\r\n\t\tbreak;\r\n\t}\r\n\r\n\trenderscene();\r\n\r\n\tif (do2d)\r\n\t{\r\n\t\tswitch(CVARF(cg_editor))\r\n\t\t{\r\n\t\tcase 1:\r\n\t\t\t/*add light ents*/\r\n\t\t\teditor_lights_overlay();\r\n\t\t\thudtype = 0;\r\n\t\t\tbreak;\r\n\t\tcase 2:\r\n\t\t\tbreak;\r\n\t\tdefault:\r\n\t\t\tif (autocvar(cg_imageview, \"\") != \"\")\t//eg: set cg_imageview \"https://www.quaddicted.com/reviews/screenshots/dmd.jpg\"\r\n\t\t\t\tdrawpic([0,0,0], autocvar(cg_imageview, \"\"), [width,height], [1,1,1],1, 0);\r\n\t\t\telse if (hudtype)\r\n\t\t\t\tHud_Draw(hudtype, show_scoreboard, width, height);\r\n\t\t\t//quicky `project` test\r\n\t\t\tif (player_local)\r\n\t\t\t{\r\n\t\t\t\tif (checkbuiltin(project))\r\n\t\t\t\t{\r\n\t\t\t\t\tsetviewprop(VF_DRAWCROSSHAIR, 0);\r\n\t\t\t\t\tvector shotorg = player_local.origin+'0 0 16';\t//quake is weird.\r\n\t\t\t\t\ttraceline(shotorg, shotorg + v_forward*8192, FALSE, player_local);\r\n\t\t\t\t\tdrawcharacter(project(trace_endpos)-'4 4', '+', '8 8', '1 1 1',1);\r\n\t\t\t\t}\r\n\t\t\r\n\t\t\t\tif (autocvar(cg_unprojecttest, FALSE))\r\n\t\t\t\t{\r\n\t\t\t\t\tstatic float nexttime;\r\n\t\t\t\t\tnexttime += clframetime;\r\n\t\t\t\t\tdrawcharacter(mousepos-'4 4', '+', '8 8', '1 1 1',1);\r\n\t\t\t\t\tif (nexttime > 1)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tvector near = unproject([mousepos_x,mousepos_y,0]);\r\n\t\t\t\t\t\tvector far = unproject([mousepos_x,mousepos_y,1]);\r\n\t\t\t\t\t\ttraceline(near, far, FALSE, player_local);\r\n\t\t\t\t\t\tte_explosion(trace_endpos);\r\n\t\t\t\t\t\tnexttime-=1;\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\t\r\n\t\t\tbreak;\r\n\t\t}\r\n\r\n\t\tMenu_Think();\r\n\t}\r\n\r\n\tif (dbgstr)\r\n\t{\r\n//\t\tdrawrawstring('16 64 0', dbgstr, '16 16 16', '1 1 1', 0.5);\r\n//\t\tdrawstring('16 80 0', dbgstr, '16 16 16', 0.5);\r\n\t}\r\n};\r\n\r\nvoid(float vwidth, float vheight, float notmenu) CSQC_UpdateViewLoading =\r\n{\r\n\tdrawfill([0,0],[vwidth,vheight], [0,0,0],1, 0);\r\n\r\n\tconst vector tsize = '24 24';\r\n\tstring text = \"CSQCTest is Loading!\\nPlease wait...\";\r\n\tvector pos = ([vwidth,vheight]-[8,stringwidth(text,TRUE,tsize)])*0.5;\r\n\tdrawstring(pos, text, tsize, '1 1 1',1,0);\r\n};\r\n"
  },
  {
    "path": "quakec/csqctest/src/cs/fun/osgk.qc",
    "content": "string osgkname;\r\n\r\nvoid(float event, float button, float mousex, float mousey) OSGKMenuEvent =\r\n{\r\n\tif (event == ME_DRAW)\r\n\t{\r\n\t\t//nothing.\r\n\t\treturn;\r\n\t}\r\n\r\n\tif (event == ME_MOUSEMOVE)\r\n\t{\r\n\t\tgecko_mousemove(osgkname, mousex/640, mousey/480);\r\n\t\treturn;\r\n\t}\r\n\tif (event == ME_KEYDOWN)\r\n\t{\r\n\t\tif (button == K_ESCAPE)\r\n\t\t{\r\n\t\t\tMenu_Main();\r\n\t\t\treturn;\r\n\t\t}\r\n\t\tgecko_keyevent(osgkname, button, 2);\r\n\t}\r\n/*\tif (event == ME_KEYUP)\r\n\t{\r\n\t\tgecko_keyevent(osgkname, button, 1);\r\n\t}\r\n*/};\r\n\r\n\r\nnonstatic void(string posgkname) Menu_OSGK\r\n{\r\n\tif (osgkname)\r\n\t\tstrunzone(osgkname);\r\n\tosgkname = strzone(posgkname);\r\n\tMenu_Activate(OSGKMenuEvent);\r\n};\r\n"
  },
  {
    "path": "quakec/csqctest/src/cs/fun/skinchooser.qc",
    "content": "static var float c_modellist = -1;\r\nstatic var float c_skinlist = -1;\r\n\r\nstatic float c_modelnum;\r\nstatic float c_skinnum;\r\n\r\nstatic string(float num) ModelForNum =\r\n{\r\n\tstring str;\r\n\tfloat slashpos;\r\n\tstr = search_getfilename(c_modellist, num);\r\n\tstr = substring(str, 15, -1);   // models/players/\r\n\tslashpos = strstrofs(str, \"/\");\r\n\treturn substring(str, 0, slashpos);\r\n}\r\nstatic string(float num) SkinForNum =\r\n{\r\n\tstring str;\r\n\tfloat slashpos;\r\n\tstr = search_getfilename(c_skinlist, num);\r\n\tstr = substring(str, 15, -1);   // models/players/\r\n\tslashpos = strstrofs(str, \"/\");\r\n\treturn substring(str, slashpos+7, -6);\t// /upper_   .skin-1\r\n}\r\n\r\nstatic void() CheckSkinNumber =\r\n{\r\n\tif (c_skinnum < 0)\r\n\t\tc_skinnum = search_getsize(c_skinlist)-1;\r\n\telse if (c_skinnum >= search_getsize(c_skinlist))\r\n\t\tc_skinnum = 0;\r\n};\r\n\r\nstatic void() FindPlayerSkins =\r\n{\r\n\tstring str;\r\n\r\n\tif (c_modelnum < 0)\r\n\t\tc_modelnum = search_getsize(c_modellist)-1;\r\n\telse if (c_modelnum >= search_getsize(c_modellist))\r\n\t\tc_modelnum = 0;\r\n\r\n\tstr = ModelForNum(c_modelnum);\r\n\r\n\tif (c_skinlist >= 0)\r\n\t\tsearch_end(c_skinlist);\r\n\tc_skinlist = -1;\r\n\tif (str != \"\")\r\n\t\tc_skinlist = search_begin(strcat(\"models/players/\", str, \"/upper_*.skin\"), true, true);\r\n\r\n\tif (c_skinlist < 0)\r\n\t{\r\n\t\tprint(\"You have no skins available\\n\");\r\n\t\treturn;\r\n\t}\r\n\r\n\tfor (c_skinnum = search_getsize(c_skinlist)-1; c_skinnum>0; c_skinnum--)\r\n\t{\r\n\t\tif (SkinForNum(c_skinnum) == \"default\")\r\n\t\t\tbreak;\t//we found the default, use it as default. :p\r\n\t}\r\n};\r\n\r\nstatic void() FindPlayerModels =\r\n{\r\n\tif (c_modellist >= 0)\r\n\t\tsearch_end(c_modellist);\r\n\tc_modellist = search_begin(\"models/players/*/animation.cfg\", true, true);\r\n\tc_modelnum = 0;\r\n\tc_skinnum = 0;\r\n\r\n\tFindPlayerSkins();\r\n};\r\n\r\nstatic void() DrawSkinChooser =\r\n{\r\n\tstring modname;\r\n\tstring skinname;\r\n\tif (c_skinlist < 0)\r\n\t\treturn;\r\n\tif (c_modellist < 0)\r\n\t\treturn;\r\n\r\n\tmodname = ModelForNum(c_modelnum);\r\n\tskinname = SkinForNum(c_skinnum);\r\n\tcprint(modname, \"/\", skinname);\r\n\r\n\tchasecam = true;\r\n}\r\n\r\nstatic void(float event, float button, float mousex, float mousey) SkinChooserMenuEvent =\r\n{\r\n\tstring str;\r\n\tif (event == ME_DRAW)\r\n\t{\r\n\t\tDrawSkinChooser();\r\n\t\treturn;\r\n\t}\r\n\r\n\tif (event == ME_MOUSEMOVE)\r\n\t{\r\n\t\treturn;\r\n\t}\r\n\tif (event == ME_KEYDOWN)\r\n\t{\r\n\t\tif (button == K_ESCAPE)\r\n\t\t{\r\n\t\t\tcprint(\"\");\r\n\t\t\tMenu_Main();\r\n\r\n\t\t\tif (c_skinlist >= 0)\r\n\t\t\t\tsearch_end(c_skinlist);\r\n\t\t\tif (c_modellist >= 0)\r\n\t\t\t\tsearch_end(c_modellist);\r\n\t\t\treturn;\r\n\t\t}\r\n\t\tif (button == K_ENTER)\r\n\t\t{\r\n\t\t\tstr = SkinForNum(c_skinnum);\r\n\t\t\tstr = strcat(ModelForNum(c_modelnum), \"/\", str);\r\n\t\t\tlocalcmd(\"skin \");\r\n\t\t\tlocalcmd(str);\r\n\t\t\tlocalcmd(\"\\n\");\r\n\t\t}\r\n\t\tif (button == K_LEFTARROW)\r\n\t\t{\r\n\t\t\tc_skinnum--;\r\n\t\t\tCheckSkinNumber();\r\n\t\t}\r\n\t\tif (button == K_RIGHTARROW)\r\n\t\t{\r\n\t\t\tc_skinnum++;\r\n\t\t\tCheckSkinNumber();\r\n\t\t}\r\n\t\tif (button == K_UPARROW)\r\n\t\t{\r\n\t\t\tc_modelnum--;\r\n\t\t\tFindPlayerSkins();\r\n\t\t}\r\n\t\tif (button == K_DOWNARROW)\r\n\t\t{\r\n\t\t\tc_modelnum++;\r\n\t\t\tFindPlayerSkins();\r\n\t\t}\r\n\t}\r\n};\r\n\r\n\r\nnonstatic void() Menu_SkinChooser\r\n{\r\n\tc_modellist = -1;\r\n\tc_skinlist = -1;\r\n\tFindPlayerModels();\r\n\r\n\tMenu_Activate(SkinChooserMenuEvent);\r\n};\r\n\r\nnonstatic void() SelectRandomSkin =\r\n{\r\n\tlocal string str;\r\n\r\n\tFindPlayerModels();\r\n\tc_modelnum = floor(random()*search_getsize(c_modellist));\r\n\tFindPlayerSkins();\r\n\tc_skinnum = floor(random()*search_getsize(c_skinlist));\r\n\r\n\tstr = SkinForNum(c_skinnum);\r\n\tstr = strcat(ModelForNum(c_modelnum), \"/\", str);\r\n\tlocalcmd(\"skin \");\r\n\tlocalcmd(str);\r\n\tlocalcmd(\"\\n\");\r\n};\r\n"
  },
  {
    "path": "quakec/csqctest/src/cs/fun/tetris.qc",
    "content": "/*\r\nQuake tetris, origionally created by FrikaC\r\n\r\nPorted to csqc by Spike, primarily for testing in a fun way.\r\n*/\r\n\r\nfloat tetris_on, tet_time, tet_autodown, tet_old_keys, tet_keys;\r\nvector piece_pos;\r\nfloat piece_type, next_piece, tet_score, tet_lines;\r\n\r\nvar float tet_high_score = 0;\r\n\r\nfloat TET_LINES = 20;\r\nfloat TET_WIDTH = 10;\r\n//character values\r\nfloat TET_BORDER = 132;\r\nfloat TET_BLOCKS = 132; // +1 = first color, +2, +3;\r\nfloat TET_SPACE = 32; // blankness\r\n\r\n\r\n\r\nfloat TETKEY_UP = 1;\r\nfloat TETKEY_DOWN = 2;\r\nfloat TETKEY_LEFT = 4;\r\nfloat TETKEY_RIGHT = 8;\r\nfloat TETKEY_ROTLEFT = 16;\r\nfloat TETKEY_ROTRIGHT = 32;\r\nfloat KEYS_IMMEDIATE = 60;//32 + 16 + 8 + 4;\r\n\r\nfloat PIECES = 7;\r\n\r\nfloat line[20];\r\n\r\n\r\nstring printline;\r\n\r\n/*\r\n*********************************\r\n\r\nLibrary Functions\r\n\r\n*********************************\r\n*/\r\nvoid (string snd) tetsnd =\r\n{\r\n\tlocalcmd(\"play \");\r\n\tlocalcmd(snd);\r\n\tlocalcmd(\"\\n\");\r\n\r\n};\r\n\r\nfloat () Tetris_Level =\r\n{\r\n\treturn floor(tet_lines / 20) + 1;\r\n};\r\n\r\n#define SetLine(l,v) line[l] = v\r\n#define GetLine(l) line[l]\r\n\r\nfloat(float x, float dat) GetXBlock =\r\n{\r\n\tif (x == 0)\r\n\t\treturn dat & 3;\r\n\telse if (x == 1)\r\n\t\treturn (dat & 12) / 4;\r\n\telse if (x == 2)\r\n\t\treturn (dat & 48) / 16;\r\n\telse if (x == 3)\r\n\t\treturn (dat & 192) / 64;\r\n\telse if (x == 4)\r\n\t\treturn (dat & 768) / 256;\r\n\telse if (x == 5)\r\n\t\treturn (dat & 3072) / 1024;\r\n\telse if (x == 6)\r\n\t\treturn (dat & 12288) / 4096;\r\n\telse if (x == 7)\r\n\t\treturn (dat & 49152) / 16384;\r\n\telse if (x == 8)\r\n\t\treturn (dat & 196608) / 65536;\r\n\telse if (x == 9)\r\n\t\treturn (dat & 786432) / 262144;\r\n\telse\r\n\t\treturn 0;\r\n};\r\n\r\nfloat(float x, float dat, float new) SetXBlock =\r\n{\r\n\tif (x == 0)\r\n\t\treturn (dat - (dat & 3)) | new;\r\n\telse if (x == 1)\r\n\t\treturn (dat - (dat & 12)) | (new*4);\r\n\telse if (x == 2)\r\n\t\treturn (dat - (dat & 48)) | (new*16);\r\n\telse if (x == 3)\r\n\t\treturn (dat - (dat & 192)) | (new*64);\r\n\telse if (x == 4)\r\n\t\treturn (dat - (dat & 768)) | (new*256);\r\n\telse if (x == 5)\r\n\t\treturn (dat - (dat & 3072)) | (new*1024);\r\n\telse if (x == 6)\r\n\t\treturn (dat - (dat & 12288)) | (new*4096);\r\n\telse if (x == 7)\r\n\t\treturn (dat - (dat & 49152)) | (new*16384);\r\n\telse if (x == 8)\r\n\t\treturn (dat - (dat & 196608)) | (new*65536);\r\n\telse if (x == 9)\r\n\t\treturn (dat - (dat & 786432)) | (new*262144);\r\n\telse\r\n\t\treturn dat;\r\n};\r\n\r\n\r\nfloat(float x, float y) GetSquare =\r\n{\r\n\treturn GetXBlock(x,  GetLine(y));\r\n};\r\n\r\nvoid (float x, float y, float val) SetSquare =\r\n{\r\n\tfloat dat;\r\n\t\r\n\tdat = GetLine(y);\r\n\tdat  = SetXBlock(x, dat, val & 3);\r\n\tSetLine(y, dat);\r\n};\r\n\r\n\r\n\r\nvector(float pc) PieceShape =\r\n{\r\n// third value is width?\r\n\r\n/*\r\n1 =\r\n## \r\n## \r\n*/\r\n\tif (pc == 1)\r\n\t\treturn '5 5 2'; // 1 * 4 + 1 * 16\r\n/*\r\n2 =\r\n\r\n####\r\n*/\r\n\telse if (pc == 2)\r\n\t\treturn '85 0 4';\r\n\r\n/*\r\n3 =\r\n\r\n###\r\n#\r\n*/\r\n\telse if (pc == 3)\r\n\t\treturn '21 1 3';\r\n/*\r\n4 =\r\n\r\n#\r\n###\r\n*/\r\n\telse if (pc == 4)\r\n\t\treturn '1 21 3';\r\n/*\r\n5 =\r\n##\r\n ##\r\n*/\r\n\telse if (pc == 5)\r\n\t\treturn '5 20 3';\r\n\r\n/*\r\n6 =\r\n ##\r\n##\r\n*/\r\n\telse if (pc == 6)\r\n\t\treturn '20 5 3';\r\n\r\n/*\r\n7 =\r\n #\r\n###\r\n*/\r\n\telse if (pc == 7)\r\n\t\treturn '4 21 3';\r\n\r\n\r\n\telse\r\n\t\treturn '0 0 0';\r\n\r\n}\r\n\r\n// do x 1..4 and y 1..4 in case of rotation\r\nfloat(float x, float y, float rot, float pc) PieceMetric =\r\n{\r\n\tfloat t;\r\n\tvector piece_dat;\r\n\tfloat wid;\r\n\tpiece_dat = PieceShape(pc);\r\n\twid = piece_dat_z;\r\n\t\r\n\t// return bits of a piece\r\n\tif (rot == 1) // 90 degrees\r\n\t{\r\n\t\tt = y;\r\n\t\ty = x;\r\n\t\tx = wid - t;\r\n\t}\r\n//\telse if (rot == 2)//180\r\n//\t{\r\n//\t\tx = wid-1 - x;\r\n//\t\ty = 3 - y;\r\n//\t}\r\n//\telse if (rot == 3) // 270\r\n//\t{\r\n//\t\tt = y;\r\n//\t\ty = wid-1 - x;\r\n//\t\tx = t;\r\n//\t}\r\nprint(ftos(x));\r\nprint(\", \");\r\nprint(ftos(y));\r\nprint(\"\\n\");\r\n\tif (x < 0 || y < 0 || x >= 4 || y >= 4)\r\n\t\treturn 0;\r\n\tif (y == 0)\r\n\t\treturn GetXBlock(x, piece_dat_x); // first row\r\n\telse if (y == 1)\r\n\t\treturn GetXBlock(x, piece_dat_y); // second row\r\n\telse\r\n\t\treturn 0; // illegal parms\r\n};\r\n/*\r\n*********************************\r\n\r\nDraw\r\n\r\n*********************************\r\n*/\r\n\r\n\r\n/* some prydon gate functions to make life easier.... \r\n\r\nsomewhat modified because we don't need all the fanciness Prydon Gate is capable of\r\n\r\n*/\r\n\r\nvoid(float c1, float c2, float c3, float c4, float c5, float c6) p6 =\r\n{\r\n\tprintline = strcat(printline, chr2str(c1, c2, c3, c4, c5, c6));\r\n};\r\n\r\nfloat(float num, float dig) pnum =\r\n{\r\n\tlocal string str;\r\n\tstr = ftos(num);\r\n\tprintline = strcat(printline, str);\r\n\tnum = strlen(str);\r\n\twhile(num < dig)\r\n\t{\r\n\t\tprintline = strcat(printline, \" \");\r\n\t\tnum++;\r\n\t}\r\n\treturn 6;\r\n};\r\n\r\nvoid (float ln) DrawLine =\r\n{\r\n\tfloat x, d;\r\n\tprintline = strcat(printline, chr2str(TET_BORDER));\r\n\t\r\n\tfor (x = 0; x < TET_WIDTH; x = x + 1)\r\n\t{\r\n\t\td = GetSquare(x, ln);\r\n\t\tif (d)\r\n\t\t\tprintline = strcat(printline, chr2str(TET_BLOCKS + d));\r\n\t\telse\r\n\t\t\tprintline = strcat(printline, chr2str(TET_SPACE));\r\n\t}\r\n\tprintline = strcat(printline, chr2str(TET_BORDER));\r\n}\r\n\r\nvoid (float pc, float ln) DrawPiece =\r\n{\r\n\tfloat x, d, piece_ln, color;\r\n\tvector piece_dat;\r\n\tcolor = pc & 3;\r\n\tif (color == 0) // 4\r\n\t\tcolor = 1;\r\n\tprintline = strcat(printline, chr2str(TET_SPACE));\r\n\t\r\n\tpiece_dat = PieceShape(pc);\r\n\tif (ln == 1)\r\n\t\tpiece_ln = piece_dat_x;\r\n\telse\r\n\t\tpiece_ln = piece_dat_y;\r\n\tfor (x = 0; x < 4; x = x + 1)\r\n\t{\r\n\t\td = GetXBlock(x, piece_ln) * color;\r\n\t\tif (d)\r\n\t\t\tprintline = strcat(printline, chr2str(TET_BLOCKS + d));\r\n\t\telse\r\n\t\t\tprintline = strcat(printline, chr2str(TET_SPACE));\r\n\t}\r\n\tprintline = strcat(printline, chr2str(TET_SPACE));\r\n}\r\nvoid() Draw_Tetris =\r\n{\r\n\tfloat i;\r\n\r\n\t// decoration\r\n\tfor (i = 0; i < (TET_WIDTH + 2); i = i + 1)\r\n\t\tprintline = strcat(printline, chr2str(TET_BORDER));\r\n\tp6(' ', ' ', ' ', ' ', ' ', ' ');\r\n\tprintline = strcat(printline, \"\\n\");\r\n\tfor (i = 0; i < TET_LINES; i = i + 1)\r\n\t{\r\n\t\tif (tetris_on == 2 && i == 11)\r\n\t\t{\r\n\t\t\tp6(TET_BORDER, 'G', 'A', 'M', 'E', ' ');\r\n\t\t\tp6('O', 'V', 'E', 'R', ' ', TET_BORDER);\r\n\t\t}\r\n\t\telse\r\n\t\t\tDrawLine(i);\r\n\t\tif (i == 1)\r\n\t\t\tp6(' ', 'N', 'E', 'X', 'T', ' ');\r\n\t\telse if (i == 3)\r\n\t\t\tDrawPiece(next_piece, 1);\r\n\t\telse if (i == 4)\r\n\t\t\tDrawPiece(next_piece, 2);\r\n\t\telse if (i == 6)\r\n\t\t\tp6(' ', 'L', 'I', 'N', 'E', 'S');\r\n\t\telse if (i == 7)\r\n\t\t\tpnum(tet_lines, 6);\r\n\t\telse if (i == 9)\r\n\t\t\tp6(' ', 'S', 'C', 'O', 'R', 'E');\r\n\t\telse if (i == 10)\r\n\t\t\tpnum(tet_score, 6);\r\n\t\telse if (i == 12)\r\n\t\t\tp6(' ', 'H', 'I', 'G', 'H', ' ');\r\n\t\telse if (i == 13)\r\n\t\t\tp6(' ', 'S', 'C', 'O', 'R', 'E');\r\n\t\telse if (i == 14)\r\n\t\t\tpnum(tet_high_score, 6);\r\n\t\telse if (i == 16)\r\n\t\t\tp6(' ', 'L', 'E', 'V', 'E', 'L');\r\n\t\telse if (i == 17)\r\n\t\t\tpnum(Tetris_Level(), 6);\r\n\t\telse\r\n\t\t\tp6(' ', ' ', ' ', ' ', ' ', ' ');\r\n\t\tprintline = strcat(printline, \"\\n\");\r\n\t}\r\n\t// decoration\r\n\r\n\tfor (i = 0; i < (TET_WIDTH + 2); i = i + 1)\r\n\t\tprintline = strcat(printline, chr2str(TET_BORDER));\r\n\tp6(' ', ' ', ' ', ' ', ' ', ' ');\r\n\r\n\r\n\tprintline = strcat(printline, \"\\n\");\r\n\r\n\tcprint(printline);\r\n\tprintline = \"\";\r\n}\r\n/*\r\n*********************************\r\n\r\nGame Functions\r\n\r\n*********************************\r\n*/\r\n\r\n// reset the game\r\nvoid() ResetTetris =\r\n{\r\n\tfloat i;\r\n\t\r\n\tfor (i=0; i<TET_LINES; i = i + 1)\r\n\t\tSetLine(i, 0);\r\n\tpiece_pos = '0 0 0';\r\n\tpiece_type = 0;\r\n\tnext_piece = tet_lines = tet_score = 0;\r\n\t\r\n};\r\n\r\n/*\r\n*********************************\r\n\r\nGame Mechanics\r\n\r\n*********************************\r\n*/\r\nfloat() RandomPiece =\r\n{\r\n\treturn floor(random() * PIECES) + 1;\r\n};\r\n\r\nvoid(float n) TetAddScore =\r\n{\r\n\ttet_score = tet_score + n * Tetris_Level();\r\n\tif (tet_score > tet_high_score)\r\n\t\ttet_high_score = tet_score; \r\n};\r\nfloat CheckMetrics(float piece, float orgx, float orgy, float rot) = \r\n{\r\n\t// check to see if the piece, if moved to the locations will overlap\r\n\t\r\n\tfloat x, y;\r\n\r\n\torgx = orgx;\r\n\torgy = orgy;\r\n\t\r\n\tfor (y = 0; y < 4; y = y + 1)\t\r\n\t{\r\n\t\tfor (x = 0; x < 4; x = x + 1)\r\n\t\t{\r\n\t\t\tif (PieceMetric(x, y, rot, piece))\r\n\t\t\t{\r\n\t\t\t\tif (GetSquare(x + orgx, y + orgy))\r\n\t\t\t\t\treturn FALSE; // uhoh, gonna hit something.\r\n\t\t\t\tif (x+orgx<0 || x+orgx >= TET_WIDTH || y+orgy<0 || y+orgy>= TET_LINES)\r\n\t\t\t\t\treturn FALSE; // ouside the level\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\treturn TRUE;\r\n}\r\n\r\nvoid ClearPiece(float piece, float orgx, float orgy, float rot) =\r\n{\r\n\t\r\n\tfloat x, y;\r\n\torgx = orgx;\r\n\torgy = orgy;\r\n\t\r\n\tfor (y = 0; y < 4; y = y + 1)\t\r\n\t{\r\n\t\tfor (x = 0; x < 4; x = x + 1)\r\n\t\t{\r\n\t\t\tif (PieceMetric(x, y, rot, piece))\r\n\t\t\t{\r\n\t\t\t\tSetSquare(x + orgx, y + orgy, 0);\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n}\r\nvoid CementPiece(float piece, float orgx, float orgy, float rot) =\r\n{\r\n\tfloat color;\r\n\tfloat x, y;\r\n\r\n\torgx = orgx;\r\n\torgy = orgy;\r\n\r\n\tcolor = piece & 3;\r\n\tif (color == 0) // 4\r\n\t\tcolor = 1;\r\n\t\t\r\n\tfor (y = 0; y < 4; y = y + 1)\t\r\n\t{\r\n\t\tfor (x = 0; x < 4; x = x + 1)\r\n\t\t{\r\n\t\t\tif (PieceMetric(x, y, rot, piece))\r\n\t\t\t{\r\n\t\t\t\tSetSquare(x + orgx, y + orgy, color);\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n}\r\n\r\nfloat LINE_LOW = 349525;\r\nfloat LINE_HIGH = 699050; // above number times 2\r\n\r\nvoid() CompletedLines =\r\n{\r\n\tfloat y, cleared, ln;\r\n\r\n\tcleared = 0;\r\n\ty = TET_LINES-1;\r\n\twhile(y >= 0)\r\n\t{\r\n\t\tln = GetLine(y);\r\n\t\tif (((ln & LINE_LOW) | ((ln & LINE_HIGH)/2)) == LINE_LOW)\r\n\t\t\tcleared = cleared + 1;\r\n\t\telse\r\n\t\t\ty = y - 1;\r\n\t\tln = GetLine(y - cleared);\r\n\t\tSetLine(y, ln);\r\n\t}\r\n\tif (cleared == 4)\r\n\t\ttetsnd(\"tetris\");\r\n\telse if (cleared)\r\n\t\ttetsnd(\"tetline\");\r\n\telse\r\n\t\ttetsnd(\"tetland\");\r\n\ttet_lines = tet_lines + cleared;\r\n\tTetAddScore(cleared * cleared * 10);\r\n};\r\n\r\nvoid(float keys) HandleGame =\r\n{\r\n\tvector check_pos, piece_data;\r\n\tfloat brand_new, nudge, i;\r\n\tbrand_new = 0;\r\n\tnudge = 0;\r\n\tif ((tet_time > time) && !(keys & KEYS_IMMEDIATE))\r\n\t\treturn;\r\n\ttet_time = time + 0.1;\r\n\t// first off, we need to see if we need a new piece\r\n\r\n\tif (tetris_on==2)\r\n\t\treturn;\r\n\r\n\tif (piece_type == 0)\r\n\t{\r\n\t\tpiece_pos = '5 1 0'; // that's about middle top, we count from 1 ARGH\r\n\t\tif (next_piece)\r\n\t\t\tpiece_type = next_piece;\r\n\t\telse\r\n\t\t\tpiece_type = RandomPiece();\r\n\t\tnext_piece =  RandomPiece();\r\n\t\tkeys = 0; // no movement first frame\r\n\t\ttet_autodown = time + 1;\r\n\t\tbrand_new = 1;\r\n\t}\r\n\telse\r\n\t\tClearPiece(piece_type, piece_pos_x, piece_pos_y, piece_pos_z);\r\n\t\r\n\t// next we need to check the piece metrics against what's on the level\r\n\t// based on the key order\r\n\r\n\tcheck_pos = piece_pos;\r\n\r\n\tif (keys & TETKEY_RIGHT)\r\n\t{\r\n\t\tcheck_pos_x = check_pos_x + 1;\r\n\t\ttetsnd(\"tetmove\");\r\n\t}\r\n\telse if (keys & TETKEY_LEFT)\r\n\t{\r\n\t\tcheck_pos_x = check_pos_x - 1;\r\n\t\ttetsnd(\"tetmove\");\r\n\t}\r\n\telse if (keys & TETKEY_ROTRIGHT)\r\n\t{\r\n\t\tcheck_pos_z = check_pos_z + 1;\r\n\t\t// nudge stuff\r\n\t\tpiece_data = PieceShape(piece_type);\r\n\t\tnudge = piece_data_z - 2;\r\n\t\ttetsnd(\"tetrot\");\r\n\t}\r\n\telse if (keys & TETKEY_ROTLEFT)\r\n\t{\r\n\t\tcheck_pos_z = check_pos_z - 1;\r\n\t\t// nudge stuff\r\n\t\tpiece_data = PieceShape(piece_type);\r\n\t\tnudge = piece_data_z - 2;\r\n\t\ttetsnd(\"tetrot\");\r\n\t}\r\n\t// bounds check\r\n\tif (check_pos_z > 3)\r\n\t\tcheck_pos_z = 0;\r\n\telse if (check_pos_z < 0)\r\n\t\tcheck_pos_z = 3;\r\n\t\r\n\t// reality check\r\n\t// more nudge stuff\r\n\tif (CheckMetrics(piece_type, check_pos_x, check_pos_y, check_pos_z))\r\n\t\tpiece_pos = check_pos;\r\n\telse if (brand_new)\r\n\t{\r\n\t\ttetris_on = 2;\r\n\t\treturn;\r\n\t}\r\n\telse if (nudge)\r\n\t{\r\n\t\tfor (i = nudge; i >= (0-nudge); i = i - 1)\r\n\t\t{\r\n\t\t\tif (CheckMetrics(piece_type, check_pos_x + i, check_pos_y, check_pos_z))\r\n\t\t\t{\r\n\t\t\t\tpiece_pos = check_pos + '1 0 0' * i;\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\tcheck_pos = piece_pos;\r\n\tif (keys & TETKEY_DOWN)\r\n\t\tcheck_pos_y = check_pos_y + 1;\r\n\telse if (tet_autodown < time)\r\n\t{\r\n\t\tcheck_pos_y = check_pos_y + 1;\r\n\t\ttet_autodown = time + 1 / Tetris_Level();\r\n\t}\r\n\tif (CheckMetrics(piece_type, check_pos_x, check_pos_y, check_pos_z))\r\n\t\tpiece_pos = check_pos;\r\n\telse\r\n\t{\r\n\t\tCementPiece(piece_type, piece_pos_x, piece_pos_y, piece_pos_z);\r\n\t\tTetAddScore(1);\r\n\t\tCompletedLines();\r\n\t\tpiece_type = 0;\r\n\t\treturn;\r\n\t}\r\n\tCementPiece(piece_type, piece_pos_x, piece_pos_y, piece_pos_z);\r\n};\r\n\r\n/*\r\n*********************************\r\n\r\nImportant Linking Into Quake stuff\r\n\r\n*********************************\r\n*/\r\n\r\nvoid(float event, float button, float mousex, float mousey) TetrisMenuEvent =\r\n{\r\n\tlocal float key = 0;\r\n\r\n\tif (event == ME_DRAW)\r\n\t{\t\t\t\r\n\t\tHandleGame(tet_keys);\r\n\t\tDraw_Tetris();\r\n\t\treturn;\r\n\t}\r\n\r\n\tif (event == ME_MOUSEMOVE)\t//0 is a mouse move\r\n\t\treturn;\r\n\r\n\tif (button == K_ESCAPE && event == ME_KEYDOWN)\r\n\t{\r\n\t\tcprint(\"\");\r\n\t\tMenu_Main();\r\n\t\treturn;\r\n\t}\r\n\tswitch(button)\r\n\t{\r\n\tcase K_LEFTARROW:\tkey = TETKEY_LEFT;\t\tbreak;\r\n\tcase K_RIGHTARROW:\tkey = TETKEY_RIGHT; \tbreak;\r\n\tcase K_UPARROW:\t\tkey = TETKEY_ROTRIGHT;\tbreak;\r\n\tcase K_SPACE:\t\tkey = TETKEY_DOWN;\t\tbreak;\r\n\tcase K_DOWNARROW:\tkey = TETKEY_DOWN;\t\tbreak;\r\n\tdefault:\r\n\t\treturn;\t//we didn't recognise it.\r\n\t}\r\n\r\n\tif (event == ME_KEYDOWN)\r\n\t\ttet_keys = tet_keys | key;\r\n\tif (event == ME_KEYUP)\r\n\t\ttet_keys -= tet_keys&key;\r\n\r\n\tHandleGame(tet_keys);\r\n\r\n\ttet_keys -= tet_keys & KEYS_IMMEDIATE;\r\n\ttet_old_keys = tet_keys;\r\n};\r\n\r\nnonstatic void() Menu_Tetris =\r\n{\r\n\ttetris_on = 1;\r\n\tResetTetris();\r\n\tMenu_Activate(TetrisMenuEvent);\r\n\tMenuEventFunc = TetrisMenuEvent;\r\n};\r\n"
  },
  {
    "path": "quakec/csqctest/src/cs/hlpm.qc",
    "content": "#ifdef HLPMODELS\r\n\r\n.float oldframe;\r\n\r\n#pragma noref 1\r\n//FTE_CSQC_BASEFRAME\r\n.float basebone;\r\n.float baseframe;\r\n.float baseframe2;\r\n.float baseframe1time;\r\n.float baseframe2time;\r\n.float subblendfrac;\r\n.float baselerpfrac;\r\n\r\n//FTE_CSQC_HALFLIFE_MODELS\r\n.float bonecontrol1, bonecontrol2, bonecontrol3, bonecontrol4;\r\n\r\n#pragma noref 0\r\n\r\n.float starttime;\r\n\r\nstatic void(string framename) ForceToAnim =\r\n{\r\n\tfloat a, s;\r\n\tfloat newframe;\r\n\r\n\tself.basebone = gettagindex(self, \"Bip01 Spine\");\t//use this bone to split base/normal frames\r\n\r\n\tnewframe = frameforname(self.modelindex, framename);\r\n\tself.frame = newframe;\r\n\tself.frame2 = newframe;\r\n\r\n\tnewframe = frameforname(self.modelindex, \"run2\");\r\n\tself.baseframe = newframe;\r\n\tself.baseframe2 = newframe;\r\n\tself.baselerpfrac = 0;\r\n\r\n\tself.baseframe1time = time;\r\n\r\n\tif (self.velocity_x == 0 && self.velocity_y == 0)\r\n\t{\r\n\t\ta = 0;\r\n\t\ts = 0;\r\n\t}\r\n\telse\r\n\t{\r\n\t\ta = self.angles_y - vectoyaw(self.velocity);\r\n\t\ts = vlen(self.velocity);\r\n\t\tif (s < 100)\r\n\t\t\ta *= s/100;\r\n\t}\r\n\ts /= 400;\r\n\tif (a < -180)\r\n\t\ta += 360;\r\n\tif (a > 180)\r\n\t\ta -= 360;\r\n\r\n\tif (a > 120)\r\n\t\ta = 120;\r\n\tif (a < -120)\r\n\t\ta = -120;\r\n\tself.bonecontrol1 = self.bonecontrol2 = self.bonecontrol3 = self.bonecontrol4 = (a)/150;///4;\r\n\tself.angles_y -= a;\r\n\r\n\tself.baseframe1time += (time - self.starttime)*s;\r\n\tself.starttime = time;\r\n\r\n\tself.subblendfrac = self.angles_x*(1/15);\r\n\tself.angles_x = 0;\r\n\r\n\t//halflife players were made for bigger maps.\r\n\tself.scale = 52/72.0;\r\n\tself.origin_z += 12;\r\n};\r\n\r\nvoid() updatesequences =\r\n{\r\n\tif (self.frame == playerframe::nailatt1 || self.frame == playerframe::nailatt2 ||\r\n\t    self.frame == playerframe::light1 || self.frame == playerframe::light2)\r\n\t\tForceToAnim (\"ref_shoot_onehanded\");\t//these ones loop\r\n//\telse if (random() < 0.005 && !(self.velocity_x || self.velocity_y) && (chasecam || self != player_local))\r\n//\t{\t//randomly taunt, when standing still, when not first-person (making the sounds is just confusing when first person)\r\n//\t\tsexedsound(self, \"taunt.wav\");\r\n//\t\tForceToAnim (TORSO_GESTURE);\r\n//\t}\r\n\telse if ((self.frame >= playerframe::axrun1 && self.frame <= playerframe::axrun6) ||\r\n\t\t (self.frame >= playerframe::axstnd1 && self.frame <= playerframe::axstnd12) ||\r\n\t\t (self.frame >= playerframe::axpain1 && self.frame <= playerframe::axpain6))\r\n\t\tForceToAnim (\"ref_aim_crowbar\");\r\n\telse\r\n\t\tForceToAnim (\"ref_aim_onehanded\");\r\n}\r\n\r\nvoid() HLPM_Draw =\r\n{\r\n\tsetmodel(self, self.model);\r\n\tupdatesequences();\r\n};\r\n\r\nfloat(string skinname) HLPM_SetModel =\r\n{\r\n\tstring mname = strcat(\"models/player/\", skinname, \"/\", skinname, \".mdl\");\r\n\r\n\t//check to see if it exists (this is too slow for my liking really)\r\n\tif (whichpack(mname) == \"\")\r\n\t\treturn false;\r\n\r\n\tsetmodel(self, mname);\r\n\r\n\tself.oldframe = -1;\r\n\tself.scale = 20/72.0;\r\n\r\n\treturn true;\r\n};\r\n\r\nvoid() HLPM_UnsetModel =\r\n{\r\n\tself.scale = 0;\r\n};\r\n\r\nentity() HLPM_DupModel =\r\n{\r\n\treturn spawn();\r\n};\r\n#endif\r\n"
  },
  {
    "path": "quakec/csqctest/src/cs/hud.qc",
    "content": "\r\nstring number[10] = {\r\n\t\"num_0\",\r\n\t\"num_1\",\r\n\t\"num_2\",\r\n\t\"num_3\",\r\n\t\"num_4\",\r\n\t\"num_5\",\r\n\t\"num_6\",\r\n\t\"num_7\",\r\n\t\"num_8\",\r\n\t\"num_9\"\r\n};\r\nstring anumber[10] = {\r\n\t\"anum_0\",\r\n\t\"anum_1\",\r\n\t\"anum_2\",\r\n\t\"anum_3\",\r\n\t\"anum_4\",\r\n\t\"anum_5\",\r\n\t\"anum_6\",\r\n\t\"anum_7\",\r\n\t\"anum_8\",\r\n\t\"anum_9\"\r\n};\r\nvoid() Hud_Init\r\n{\r\n\tfloat i;\r\n\tprecache_pic(\"sbar\", true);\r\n\tfor (i = 0; i < 10; i++)\r\n\t{\r\n\t\tprecache_pic(number[i], true);\r\n\t\tprecache_pic(anumber[i], true);\r\n\t}\r\n};\r\n\r\nvector screensize;\r\nfloat stat_items, stat_items2;\r\n\r\nvoid(vector pos, float value, float threshhold) Hud_DrawLargeValue =\r\n{\r\n\tfloat c;\r\n\tfloat len;\r\n\tstring s;\r\n\tif (value < 0)\r\n\t\tvalue = 0;\t//hrm\r\n\tif (value>999)\r\n\t\tvalue = 999;\r\n\r\n\ts = ftos(floor(value));\r\n\tlen = strlen(s);\r\n\r\n\tpos_x += 24 * (3-len);\r\n\t\r\n\r\n\tif (value <= threshhold)\r\n\t{\t//use alternate (red) numbers\r\n\r\n\t\twhile(len>0)\r\n\t\t{\r\n\t\t\tlen--;\r\n\t\t\tc = str2chr(s, len);\r\n\r\n\t\t\tdrawpic(pos+len * '24 0 0', anumber[c-'0'], '24 24 0', '1 1 1', 1, 0);\r\n\t\t}\r\n\t}\r\n\telse\r\n\t{\t//use normal numbers\r\n\r\n\t\twhile(len>0)\r\n\t\t{\r\n\t\t\tlen--;\r\n\t\t\tc = str2chr(s, len);\r\n\r\n\t\t\tdrawpic(pos+len * '24 0 0', number[c-'0'], '24 24 0', '1 1 1', 1, 0);\r\n\t\t}\r\n\t}\r\n};\r\n\r\nvoid(float type, vector pos, float drawback) Hud_DrawAmmoCount =\r\n{\r\n\tstring s;\r\n\tfloat value;\r\n\tfloat len;\r\n\tfloat c;\r\n\tvalue = getstati(STAT_SHELLS+type);\r\n\tif (value < 0)\r\n\t\tvalue = 0;\t//hrm\r\n\tif (value>999)\r\n\t\tvalue = 999;\r\n\r\n\ts = ftos(floor(value));\r\n\tlen = strlen(s);\r\n\r\n\tpos_x += 8 * (3-len);\r\n\twhile(len>0)\r\n\t{\r\n\t\tlen--;\r\n\t\tc = str2chr(s, len);\r\n\t\tdrawcharacter(pos+len * '8 0 0', (18-'0') + c, '8 8 0', '1 1 1', 1, 0);\r\n\t}\r\n};\r\n\r\nstring weaponnames[7] = {\r\n\t\"inv_shotgun\",\r\n\t\"inv_sshotgun\",\r\n\t\"inv_nailgun\",\r\n\t\"inv_sshotgun\",\r\n\t\"inv_rlaunch\",\r\n\t\"inv_srlaunch\",\r\n\t\"inv_lightng\"\r\n};\r\nvoid(float num, vector pos) Hud_DrawWeapon\r\n{\r\n\tdrawpic(pos, weaponnames[num], '24 16 0', '1 1 1', 1, 0);\r\n};\r\nvoid(float num, vector pos) Hud_DrawWeaponWide\t//for LG\r\n{\r\n\tdrawpic(pos, weaponnames[num], '48 16 0', '1 1 1', 1, 0);\r\n};\r\n\r\nstring(float f, float chars, string lead) FormatFloat\r\n{\r\n\tstring s = ftos(f);\r\n\tif (f < 10 && chars >= 3)\r\n\t\ts = strcat(lead, lead, s);\r\n\telse if (f < 10 && chars == 2)\r\n\t\ts = strcat(lead, s);\r\n\telse if (f < 100 && chars >= 3)\r\n\t\ts = strcat(lead, s);\r\n\treturn s;\r\n};\r\n\r\nvoid(vector pos) Hud_CoopScores_SBar =\r\n{\r\n\tstring s;\r\n\tfloat secs;\r\n\tfloat mins;\r\n\t//When you press tab in single player/coop, you get some replacement info over the sbar\r\n\tvector sbar = screensize_y * ' 0 1 0' - '0 24 0';\r\n\tdrawpic(sbar, \"scorebar\", '320 24 0', '1 1 1', 0.333, 0);\r\n\r\n\ts = strcat(\"Monsters:\", FormatFloat(getstatf(STAT_KILLEDMONSTERS), 3, \" \"), \"/\", FormatFloat(getstatf(STAT_TOTALMONSTERS), 3, \" \"));\r\n\tdrawstring(sbar + '8 4', s, '8 8 0', '1 1 1', 1, 0);\r\n\ts = strcat(\"Secrets :\", FormatFloat(getstatf(STAT_FOUNDSECRETS), 3, \" \"), \"/\", FormatFloat(getstatf(STAT_TOTALSECRETS), 3, \" \"));\r\n\tdrawstring(sbar + '8 12', s, '8 8 0', '1 1 1', 1, 0);\r\n\r\n\tmins = floor(time/60);\r\n\tsecs = floor(time - mins*60);\r\n\ts = strcat(\"Time :\", FormatFloat(mins, 3, \" \"), \":\", FormatFloat(secs, 2, \"0\"));\r\n\tdrawstring(sbar + '184 4', s, '8 8 0', '1 1 1', 1, 0);\r\n\r\n\tdrawstring(sbar + '232 12' - strlen(levelname)*'4 0', levelname, '8 8 0', '1 1 1', 1, 0);\r\n};\r\n\r\nvoid Hud_DrawSBar(vector pos)\r\n{\r\n\tdrawpic(pos, \"sbar\", '320 24 0', '1 1 1', 0.333, 0);\r\n\r\n\tif (stat_items & IT_INVULNERABILITY)\r\n\t{\r\n\t\tdrawpic(pos, \"disc\", '24 24 0', '1 1 1', 1, 0);\r\n\t\tHud_DrawLargeValue(pos+'24 0 0', 999, 25);\r\n\t}\r\n\telse\r\n\t{\r\n\t\tHud_DrawLargeValue(pos+'24 0 0', getstatf(STAT_ARMOR), 25);\r\n\t\tif (stat_items & IT_ARMOR3)\r\n\t\t\tdrawpic(pos, \"sb_armor3\", '24 24 0', '1 1 1', 1, 0);\r\n\t\telse if (stat_items & IT_ARMOR2)\r\n\t\t\tdrawpic(pos, \"sb_armor2\", '24 24 0', '1 1 1', 1, 0);\r\n\t\telse if (stat_items & IT_ARMOR1)\r\n\t\t\tdrawpic(pos, \"sb_armor1\", '24 24 0', '1 1 1', 1, 0);\r\n\t}\r\n\r\n\tHud_DrawLargeValue(pos+'136 0 0', getstatf(STAT_HEALTH), 25);\r\n\r\n\tif (stat_items & IT_SHELLS)\r\n\t\tdrawpic(pos+'224 0 0', \"sb_shells\", '24 24 0', '1 1 1', 1, 0);\r\n\telse if (stat_items & IT_NAILS)\r\n\t\tdrawpic(pos+'224 0 0', \"sb_nails\", '24 24 0', '1 1 1', 1, 0);\r\n\telse if (stat_items & IT_ROCKETS)\r\n\t\tdrawpic(pos+'224 0 0', \"sb_rocket\", '24 24 0', '1 1 1', 1, 0);\r\n\telse if (stat_items & IT_CELLS)\r\n\t\tdrawpic(pos+'224 0 0', \"sb_cells\", '24 24 0', '1 1 1', 1, 0);\r\n\tHud_DrawLargeValue(pos+'248 0 0', getstatf(STAT_AMMO), 10);\r\n};\r\n\r\nvoid Hud_DrawIBar(vector pos)\r\n{\r\n\tdrawpic(pos, \"ibar\", '320 24 0', '1 1 1', 0.333, 0);\r\n\r\n\tif (stat_items & IT_SHOTGUN)\r\n\t\tHud_DrawWeapon(0, pos+'0 8 0');\r\n\tif (stat_items & IT_SUPER_SHOTGUN)\r\n\t\tHud_DrawWeapon(1, pos+'24 8 0');\r\n\tif (stat_items & IT_NAILGUN)\r\n\t\tHud_DrawWeapon(2, pos+'48 8 0');\r\n\tif (stat_items & IT_SUPER_NAILGUN)\r\n\t\tHud_DrawWeapon(3, pos+'72 8 0');\r\n\tif (stat_items & IT_GRENADE_LAUNCHER)\r\n\t\tHud_DrawWeapon(4, pos+'96 8 0');\r\n\tif (stat_items & IT_ROCKET_LAUNCHER)\r\n\t\tHud_DrawWeapon(5, pos+'120 8 0');\r\n\tif (stat_items & IT_LIGHTNING)\r\n\t\tHud_DrawWeaponWide(6, pos+'144 8 0');\r\n\r\n\tHud_DrawAmmoCount(0, pos + '10 0 0', false);\r\n\tHud_DrawAmmoCount(1, pos + '58 0 0', false);\r\n\tHud_DrawAmmoCount(2, pos + '106 0 0', false);\r\n\tHud_DrawAmmoCount(3, pos + '154 0 0', false);\r\n\r\n\tif (stat_items & IT_KEY1)\r\n\t\tdrawpic(pos+'192 8 0', \"sb_key1\", '16 16 0', '1 1 1', 1, 0);\r\n\tif (stat_items & IT_KEY2)\r\n\t\tdrawpic(pos+'208 8 0', \"sb_key2\", '16 16 0', '1 1 1', 1, 0);\r\n\r\n\tif (stat_items & IT_INVISIBILITY)\r\n\t\tdrawpic(pos+'224 8 0', \"sb_invis\", '16 16 0', '1 1 1', 1, 0);\r\n\tif (stat_items & IT_INVULNERABILITY)\r\n\t\tdrawpic(pos+'240 8 0', \"sb_invuln\", '16 16 0', '1 1 1', 1, 0);\r\n\tif (stat_items & IT_SUIT)\r\n\t\tdrawpic(pos+'256 8 0', \"sb_suit\", '16 16 0', '1 1 1', 1, 0);\r\n\tif (stat_items & IT_QUAD)\r\n\t\tdrawpic(pos+'272 8 0', \"sb_quad\", '16 16 0', '1 1 1', 1, 0);\r\n\r\n\tif (stat_items2 & 32)\r\n\t\tdrawpic(pos+'288 8 0', \"sb_sigil1\", '8 16 0', '1 1 1', 1, 0);\r\n\tif (stat_items2 & 64)\r\n\t\tdrawpic(pos+'296 8 0', \"sb_sigil2\", '8 16 0', '1 1 1', 1, 0);\r\n\tif (stat_items2 & 128)\r\n\t\tdrawpic(pos+'304 8 0', \"sb_sigil3\", '8 16 0', '1 1 1', 1, 0);\r\n\tif (stat_items2 & 256)\r\n\t\tdrawpic(pos+'312 8 0', \"sb_sigil4\", '8 16 0', '1 1 1', 1, 0);\r\n};\r\n\r\nstring q3numbers[10] =\r\n{\r\n\t\"gfx/2d/numbers/zero_32b.tga\",\r\n\t\"gfx/2d/numbers/one_32b.tga\",\r\n\t\"gfx/2d/numbers/two_32b.tga\",\r\n\t\"gfx/2d/numbers/three_32b.tga\",\r\n\t\"gfx/2d/numbers/four_32b.tga\",\r\n\t\"gfx/2d/numbers/five_32b.tga\",\r\n\t\"gfx/2d/numbers/six_32b.tga\",\r\n\t\"gfx/2d/numbers/seven_32b.tga\",\r\n\t\"gfx/2d/numbers/eight_32b.tga\",\r\n\t\"gfx/2d/numbers/nine_32b.tga\"\r\n};\r\nvoid Hud_DrawQ3Number(vector pos, float value, vector colours)\r\n{\r\n\tfloat c;\r\n\tfloat len;\r\n\tstring s;\r\n\tif (value < 0)\r\n\t\tvalue = 0;\t//hrm\r\n\tif (value>999)\r\n\t\tvalue = 999;\r\n\r\n\ts = ftos(floor(value));\r\n\tlen = strlen(s);\r\n\r\n\tpos_x += 24 * (3-len);\r\n\t\r\n\twhile(len>0)\r\n\t{\r\n\t\tlen--;\r\n\t\tc = str2chr(s, len);\r\n\r\n\t\tdrawpic(pos+len * '24 0 0', q3numbers[c-'0'], '24 24 0', colours, 1, 0);\r\n\t}\r\n};\r\n\r\nentity hud3ditem;\r\n\r\nvoid Hud_Draw3dScene(vector topleft, vector sz)\r\n{\r\n\tclearscene();\r\n\r\n\tsetviewprop(VF_DRAWWORLD, 0);\r\n\tsetviewprop(VF_DRAWENGINESBAR, 0);\r\n\tsetviewprop(VF_DRAWCROSSHAIR, 0);\r\n\tsetviewprop(VF_MIN, topleft);\r\n\tsetviewprop(VF_SIZE, sz);\r\n\tsetviewprop(VF_FOV, '30 30');\r\n\tsetviewprop(VF_ORIGIN, '0 0 0');\r\n\tsetviewprop(VF_ANGLES, '0 0 0');\r\n\r\n\taddentity(hud3ditem);\r\n\trenderscene();\r\n}\r\n\r\nvoid Hud_Draw3dItem(vector topleft, vector sz, string modelname, vector org, vector ang)\r\n{\r\n\tif (!hud3ditem)\r\n\t\thud3ditem = spawn();\r\n\r\n\thud3ditem.origin = org;\r\n\thud3ditem.angles = ang;\r\n\tsetmodel(hud3ditem, modelname);\r\n\r\n\tHud_Draw3dScene(topleft, sz);\r\n}\r\n\r\nvoid Hud_DrawQ3Head(vector topleft, vector sz, string skinname, vector ang)\r\n{\r\n\tfloat headmodelindex;\r\n\tvector headoffset;\r\n\tfloat headskinnumber;\r\n\r\n\tvector org;\r\n\r\n\ttokenize(skinname);\r\n\tskinname = argv(0);\r\n\r\n\theadmodelindex = Anim_GetHeadModelIndex(skinname);\r\n\theadskinnumber = Anim_GetHeadSkinNumber(skinname);\r\n\theadoffset = Anim_GetHeadOffset(skinname);\r\n\r\n\tif (!headmodelindex)\r\n\t\treturn;\t//eep.\r\n\r\n\tif (!hud3ditem )\r\n\t\thud3ditem = spawn();\r\n\r\n\tsetmodelindex(hud3ditem, headmodelindex);\r\n\thud3ditem.skin = headskinnumber;\r\n\r\n\torg_z = -0.5 * (hud3ditem.mins_z + hud3ditem.maxs_z);\r\n\torg_y = 0.5 * (hud3ditem.mins_y + hud3ditem.maxs_y);\r\n\torg_x = (hud3ditem.maxs_z - hud3ditem.mins_z) * (0.7 / /*tan(30)*/0.268);\r\n\r\n\torg += headoffset;\r\n\r\n\thud3ditem.origin = org;\r\n\thud3ditem.angles = ang;\r\n\r\n\tHud_Draw3dScene(topleft, sz);\r\n\thud3ditem.skin = 0;\r\n}\r\n\r\nvoid Hud_Q3(vector pos)\r\n{\r\n\tHud_Draw3dItem(pos+'224 0', '24 24', \"models/powerups/ammo/rocketam.md3\", '70 0 0', '0 90 0' + ('0 1 0'*20)*sin(time));\r\n\tHud_Draw3dItem(pos+'0 0', '24 24', \"models/powerups/armor/armor_red.md3\", '80 0 -10', '0 1 0'*360*(time/6));\r\n\tHud_DrawQ3Head(pos+'112 0', '24 24', cvar_string(\"cg_forceskin\"), '0 180 0'+'0 20 0'*sin(time));\r\n\r\n\tHud_DrawQ3Number(pos+'24 0 0', getstatf(STAT_ARMOR), '1 1 1');\r\n\tHud_DrawQ3Number(pos+'136 0 0', getstatf(STAT_HEALTH), '1 1 1');\r\n\tHud_DrawQ3Number(pos+'248 0 0', getstatf(STAT_AMMO), '1 1 1');\r\n}\r\n\r\nnonstatic void Hud_Draw(float hudtype, float scoreboard, float width, float height)\r\n{\r\n\tvector pos;\r\n\tscreensize_x = width;\r\n\tscreensize_y = height;\r\n\tscreensize_z = 0;\r\n\r\n\tpos_x = (screensize_x-320)/2;\r\n\tpos_y = screensize_y;\r\n\tpos_z = 0;\r\n\r\n\tstat_items = getstatbits(STAT_ITEMS, 0, 23);\r\n\tstat_items2 = getstatbits(STAT_ITEMS, 23, 9);\r\n\r\n\t//if hudtype == 0 then the engine already drew it.\r\n\tif (hudtype == 3)\r\n\t{\r\n\t\tHud_Q3(pos - '0 24 0');\r\n\t\treturn;\r\n\t}\r\n\telse if (hudtype == 2)\r\n\t{\r\n\t\tif (scoreboard)\r\n\t\t{\r\n\t\t\tHud_CoopScores_SBar(pos - '0 24 0');\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tHud_DrawSBar(pos - '0 24 0');\r\n\t\t}\r\n\t\tHud_DrawIBar(pos - '0 48 0');\r\n\t}\r\n};\r\n"
  },
  {
    "path": "quakec/csqctest/src/cs/map.qc",
    "content": ".string target;\r\n.string targetname;\r\n.vector mangle;\r\n.string message;\r\n\r\nvoid() cs_teleport_touch =\r\n{\r\n\tlocal entity t;\r\n\tt = find(world, targetname, self.target);\r\n\tif (t)\r\n\t{\r\n\t\tmakevectors(t.angles);\r\n\t\tPred_Predict_Teleport(t.origin + '0 0 27', v_forward*300, t.angles);\r\n\t}\r\n};\r\n\r\nvoid() spawn_trigger_teleport =\r\n{\r\n\tself.solid = SOLID_TRIGGER;\r\n\tsetmodel(self, self.model);\r\n\tself.model = \"\";\r\n\r\n\tself.touch = cs_teleport_touch;\r\n};\r\n\r\nvoid() spawn_info_teleport_destination =\r\n{\r\n};\r\n\r\nvoid() spawn_trigger_push =\r\n{\r\n\tself.solid = SOLID_TRIGGER;\r\n\tsetmodel(self, self.model);\r\n\tself.model = \"\";\r\n\r\n//\tself.touch = cs_teleport_touch;\r\n};\r\n\r\nvoid() spawn_worldspawn =\r\n{\r\n\tlevelname = self.message;\r\n\tremove(self);\r\n};\r\n\r\nfloat() parsenewmapentity =\r\n{\r\n\tlocal string field, value;\r\n\tlocal entity nent;\r\n\tlocal void() spawnfunc = __NULL__;\r\n\tnent = spawn();\r\n\r\n\twhile (1)\r\n\t{\r\n\t\tfield = getentitytoken();\r\n\t\tif not (field)\r\n\t\t\tbreak;\r\n\r\n\t\tif (field == \"}\")\r\n\t\t{\r\n\t\t\tif (!nent.classname)\r\n\t\t\t\tbreak;\r\n\t\t\tif (spawnfunc)\r\n\t\t\t{\r\n\t\t\t\tself = nent;\r\n\t\t\t\tspawnfunc();\r\n\t\t\t\treturn true;\r\n\t\t\t}\r\n\r\n//\t\t\tprint(\"ignoring \", nent.classname, \"\\n\");\r\n\t\t\t//I don't know what you are, go away.\r\n\t\t\tif (nent.classname!=\"\")\r\n\t\t\t\tstrunzone(nent.classname);\r\n\t\t\tif (nent.message!=\"\")\r\n\t\t\t\tstrunzone(nent.message);\r\n\t\t\tremove(nent);\r\n\t\t\treturn true;\r\n\t\t}\r\n\r\n\t\tvalue = getentitytoken();\r\n\t\tif not (value)\r\n\t\t\tbreak;\r\n\r\n//\t\tprint(\"ent field \", field, \" has value \", value, \"\\n\");\r\n\r\n\t\tif (field == \"classname\")\r\n\t\t{\r\n\t\t\tif (value == \"trigger_teleport\")\r\n\t\t\t\tspawnfunc = spawn_trigger_teleport;\r\n\t\t\tif (value == \"info_teleport_destination\")\r\n\t\t\t\tspawnfunc = spawn_info_teleport_destination;\r\n\t\t\tif (value == \"trigger_push\")\r\n\t\t\t\tspawnfunc = spawn_trigger_push;\r\n\t\t\tif (value == \"worldspawn\")\r\n\t\t\t\tspawnfunc = spawn_worldspawn;\r\n\t\t\tnent.classname = strzone(value);\r\n\t\t}\r\n\t\telse if (field == \"targetname\")\r\n\t\t\tnent.targetname = strzone(value);\r\n\t\telse if (field == \"target\")\r\n\t\t\tnent.target = strzone(value);\r\n\t\telse if (field == \"origin\")\r\n\t\t\tnent.origin = stov(value);\r\n\t\telse if (field == \"angles\")\r\n\t\t\tnent.angles = stov(value);\r\n\t\telse if (field == \"angle\")\r\n\t\t{\r\n\t\t\tnent.angles_x = 0;\r\n\t\t\tnent.angles_y = stof(value);\r\n\t\t\tnent.angles_z = 0;\r\n\t\t}\r\n\t\telse if (field == \"mangle\")\r\n\t\t\tnent.angles = stov(value);\r\n\t\telse if (field == \"model\")\r\n\t\t\tnent.model = value;\r\n\t\telse if (field == \"message\")\r\n\t\t\tnent.message = strzone(value);\r\n/*\t\telse if (field == \"light\");\r\n\t\telse if (field == \"mangle\");\r\n\t\telse if (field == \"killtarget\");\r\n\t\telse if (field == \"wad\");\r\n\t\telse if (field == \"height\");\r\n\t\telse if (field == \"lip\");\r\n\t\telse if (field == \"count\");\r\n\t\telse if (field == \"dmg\");\r\n\t\telse if (field == \"delay\");\r\n\t\telse if (field == \"worldtype\");\r\n\t\telse if (field == \"sounds\");\r\n\t\telse if (field == \"map\");\r\n\t\telse if (field == \"spawnflags\");\r\n\t\telse if (field == \"wait\");\r\n\t\telse if (field == \"style\");\r\n\t\telse if (field == \"health\");\r\n\t\telse if (field == \"speed\");\r\n\t\telse\r\n\t\t\tprint(\"ignoring field \", field, \"\\n\");\r\n*/\r\n\t}\r\n\r\n\tremove(nent);\r\n\r\n\treturn false;\r\n};\r\n\r\nvoid() CSQC_WorldLoaded =\r\n{\r\n\tlocal string tok;\r\n\r\n\twhile (1)\r\n\t{\r\n\t\ttok = getentitytoken();\r\n\r\n\t\tif (tok == \"\")\r\n\t\t\tbreak;\r\n\r\n\t\tif (tok != \"{\")\r\n\t\t{\r\n\t\t\tprint(\"bad entity data\\n\");\r\n\t\t\treturn;\r\n\t\t}\r\n\t\t\r\n\t\tif (!parsenewmapentity())\r\n\t\t{\r\n\t\t\tprint(\"bad entity data\\n\");\r\n\t\t\treturn;\r\n\t\t}\r\n\t}\r\n};\r\n"
  },
  {
    "path": "quakec/csqctest/src/cs/menu.qc",
    "content": "float inmenu;\r\n\r\nenum {\r\n\tME_KEYDOWN,\r\n\tME_KEYUP,\r\n\tME_MOUSEMOVE,\r\n\tME_DRAW = 200\r\n};\r\n\r\nnonstatic void(void(float, float, float, float) fnc) Menu_Activate =\r\n{\r\n\tinmenu = TRUE;\r\n\tMenuEventFunc = fnc;\r\n};\r\n\r\nnoref vector mousepos; //z is not set\r\nfloat (float event, float parama, float paramb) Menu_InputEvent =\r\n{\r\n\tif (!inmenu)\r\n\t\treturn false;\t//let the engine do what it wants.\r\n\r\n\tif (event == INPUT_MOUSEMOVE)\r\n\t{\r\n\t\tmousepos_x += parama;\r\n\t\tif (mousepos_x < 0)\r\n\t\t\tmousepos_x = 0;\r\n\t\tif (mousepos_x >= 640)\r\n\t\t\tmousepos_x = 640;\r\n\t\tmousepos_y += paramb;\r\n\t\tif (mousepos_y < 0)\r\n\t\t\tmousepos_y = 0;\r\n\t\tif (mousepos_y >= 480)\r\n\t\t\tmousepos_y = 480;\r\n\t}\r\n\r\n\tMenuEventFunc(event, parama, mousepos_x, mousepos_y);\r\n\r\n\treturn true;\r\n};\r\n\r\nnonstatic void() Menu_Think =\r\n{\r\n\tif (!inmenu)\r\n\t\treturn;\r\n\r\n\tMenuEventFunc(ME_MOUSEMOVE, 0, mousepos_x, mousepos_y);\r\n\r\n\tMenuEventFunc(ME_DRAW, 0, mousepos_x, mousepos_y);\r\n};\r\n\r\n//We don't have a main menu for the csqc yet, so just pop it instead, so submenus can close properly.\r\nnonstatic void() Menu_Main =\r\n{\r\n\tinmenu = FALSE;\r\n};\r\n"
  },
  {
    "path": "quakec/csqctest/src/cs/movetypes.qc",
    "content": "//Emulates the movetype using the 'predraw' function.\r\n\r\n.float starttime;\r\n\r\n#define RunThink() do {if (self.nextthink && self.nextthink <= time){float otime;otime = time;time = self.nextthink;self.think();time = otime;}} while(0)\r\n\r\nvoid(entity e1, entity e2) Impact =\r\n{\r\n\tentity oself;\r\n\tentity oother;\r\n\toself = self;\r\n\toother = other;\r\n\tif (e1.touch && e1.solid)\r\n\t{\r\n\t\tself = e1;\r\n\t\tother = e2;\r\n\t\tself.touch();\r\n\t}\r\n\tif (e2.touch && e2.solid)\r\n\t{\r\n\t\tself = e2;\r\n\t\tother = e1;\r\n\t\tself.touch();\r\n\t}\r\n\tself = oself;\r\n\tother = oother;\r\n};\r\n\r\n//returns the time that it didn't manage to move.\r\nfloat(float time_left) FlyMove =\r\n{\r\n\tfloat impacts;\r\n\tvector end;\r\n\r\n\tfor(impacts = 4; impacts; impacts--)\r\n\t{\r\n\t\tend = self.origin + self.velocity*time_left;\r\n\t\ttracebox(self.origin, self.mins, self.maxs, end, FALSE, self);\r\n\t\tif (trace_startsolid)\r\n\t\t\treturn time_left;\r\n\r\n\t\tself.origin = trace_endpos;\r\n\t\tif (trace_fraction == 1)\r\n\t\t{\r\n\t\t\treturn 0;\t//w00t\r\n\t\t}\r\n\r\n\t\tif (trace_plane_normal_z > 0.7)\r\n\t\t\tself.flags |= FL_ONGROUND;\r\n\r\n\t\tImpact(self, trace_ent);\r\n\t\tif (wasfreed(self))\r\n\t\t\treturn 0;\r\n\r\n\t\t//slide them along the wall.\r\n\t\tself.velocity = self.velocity - (trace_plane_normal*self.velocity)*trace_plane_normal;\r\n\r\n\r\n\t\ttime_left -= time_left * trace_fraction;\r\n\t}\r\n\treturn time_left;\r\n};\r\nvoid(vector end) PushSelf =\r\n{\r\n\tend += self.origin;\r\n\ttracebox(self.origin, self.mins, self.maxs, end, FALSE, self);\r\n\tif (trace_startsolid)\r\n\t\treturn;\r\n\r\n\tself.origin = trace_endpos;\r\n\tif (trace_fraction == 1)\r\n\t\treturn;\r\n\r\n\tImpact(self, trace_ent);\r\n};\r\n\r\n//MOVETYPE_BOUNCE and MOVETYPE_TOSS\r\nnonstatic void() Movetype_Bounce =\r\n{\r\n\tlocal vector dest;\r\n\tlocal float td;\r\n\r\n\tRunThink();\r\n\r\n\ttd = time - self.starttime;\r\n\tself.starttime = time;\r\n\r\n\tif (self.flags & FL_ONGROUND)\r\n\t\treturn;\r\n\r\n\tself.velocity = self.velocity - '0 0 800'*td;\r\n\tdest = self.origin + self.velocity * td;\r\n\ttracebox(self.origin, self.mins, self.maxs, dest, TRUE, self);\r\n\tif (trace_startsolid)\r\n\t{\r\n\t\ttrace_fraction = 0;\r\n\t\ttrace_endpos = self.origin;\r\n\t}\r\n\tif (trace_fraction < 1)\r\n\t{\r\n\t\tdest = trace_endpos;\r\n\t\tself.avelocity = self.avelocity*0.5;\r\n\t\t//bounce the gib off the surface.\r\n\t\tif (self.movetype == MOVETYPE_BOUNCE)\r\n\t\t\tself.velocity = self.velocity - (trace_plane_normal*self.velocity)*trace_plane_normal*1.5;\t//vectorize the bounce vector, and multiply with the bounce value\r\n\t\telse\r\n\t\t\tself.velocity = self.velocity - (trace_plane_normal*self.velocity)*trace_plane_normal;\t//vectorize the bounce vector, and multiply with the bounce value\r\n\r\n\t\tif (trace_plane_normal_z > 0.7 && self.velocity_z < 60)\r\n\t\t{\r\n\t\t\tself.velocity = '0 0 0';\r\n\t\t\tself.flags |= FL_ONGROUND;\r\n\t\t}\r\n\t}\r\n\tself.angles = self.angles + self.avelocity*td;\r\n\r\n\t//Do the trail thing, if possible.\r\n\r\n\tself.origin = dest;\r\n\tsetorigin(self, self.origin);\r\n};\r\n\r\nvoid() Movetype_Walk =\r\n{\r\n\tfloat tl, td;\r\n\tRunThink();\r\n\r\n\ttd = time - self.starttime;\r\n\tself.starttime = time;\r\n\r\n\ttl = FlyMove(td);\r\n\tif (self.flags & FL_WATERJUMP)\t//waterjumps, yay.\r\n\t\treturn;\r\n\r\n\tif (!trace_plane_normal_z)\r\n\t{\t//a vertical wall\r\n\t\tPushSelf('0 0 22');\t//so step up\r\n\t\tself.velocity_z = 0;\r\n\t\tFlyMove(tl);\t\t//continue going forwards\r\n\t\tPushSelf('0 0 -22');\t//and step down again\r\n\t}\r\n\tsetorigin(self, self.origin);\r\n};\r\n\r\nvoid() Movetype_Noclip =\r\n{\r\n\tfloat td;\r\n\tRunThink();\r\n\r\n\ttd = time - self.starttime;\r\n\tself.starttime = time;\r\n\r\n\tself.angles += self.avelocity*td;\r\n\tself.origin += self.velocity*td;\r\n};\r\n\r\nvoid() Movetype_Nomove =\r\n{\r\n\tRunThink();\r\n};"
  },
  {
    "path": "quakec/csqctest/src/cs/player.qc",
    "content": "/*\r\nthis file handles the local player, and marshalling between the different sorts of player models.\r\n\r\nRefreshPlayer: Called from the engine each time a player (ent with player.mdl) is about to be drawn\r\nParsePlayer: Called from CSQC_Ent_Parse for csqc protocol ents\r\n*/\r\n\r\nstatic void() RemovePlayer;\r\n\r\n\r\nenum\r\n{\r\n\tMF_BAD,\r\n#ifdef MD3PMODELS\r\n\tMF_QUAKE3,\r\n#endif\r\n#ifdef Q4PMODELS\r\n\tMF_QUAKE4,\r\n#endif\r\n#ifdef HLPMODELS\r\n\tMF_HLPM,\r\n#endif\r\n#ifdef XM\r\n\tMF_XM,\r\n#endif\r\n\tMF_QUAKE\r\n};\r\n\r\n#ifdef NOEXTENSIONS\t//FRIK_FILE is an extension still, unfortunatly. but hey, the model code depends upon it more\r\n#define strunzone(s)\t//so really we can get away with this for this module\r\n#define strzone(s) \"\"\r\n#endif\r\n\r\nstatic float() Player_Interpolate =\r\n{\t//do some frame interpolation.\r\n\tif (player_local == self)\r\n\t{\r\n\t\tPred_UpdateLocalMovement(self);\r\n\t}\r\n\r\n\tswitch(self.modelstyle)\r\n\t{\r\n#ifdef MD3PMODELS\r\n\tcase MF_QUAKE3:\r\n\t\tAnim_Draw();\r\n\t\tbreak;\r\n#endif\r\n#ifdef Q4PMODELS\r\n\tcase MF_QUAKE4:\r\n\t\tQ4PM_Draw();\r\n\t\treturn PREDRAW_NEXT;\r\n#endif\r\n#ifdef HLPMODELS\r\n\tcase MF_HLPM:\r\n\t\tHLPM_Draw();\r\n\t\tbreak;\r\n#endif\r\n#ifdef XM\r\n\tcase MF_XM:\r\n\t\tXM_Draw();\r\n\t\tbreak;\r\n#endif\r\n\tdefault:\r\n\t\tif (self.lerptime)\r\n\t\t\tself.lerpfrac = 1-(time-self.lerptime)*10;\r\n\r\n\t\tif (chasecam || self.entnum != player_localentnum)\r\n\t\t\tself.renderflags = 0;\r\n\t\telse\r\n\t\t\tself.renderflags = RF_EXTERNALMODEL;\r\n\r\n#ifdef POWERUP_SHELLS\r\n\t\tif (checkbuiltin(shaderforname))\r\n\t\t{\r\n\t\t\tif (self.sveffects & SVE_INVIS)\r\n\t\t\t\tself.forceshader = shaderforname(\"powerups/invisibility\");\r\n\t\t\telse\r\n\t\t\t\tself.forceshader = 0;\r\n\r\n\t\t\tself.fatness = 0;\r\n\r\n\t\t\tif (!(self.sveffects & SVE_INVIS))\r\n\t\t\t{\r\n\t\t\t\tif (self.sveffects & SVE_QUAD)\r\n\t\t\t\t{\r\n\t\t\t\t\taddentity(self);\r\n\r\n\t\t\t\t\tself.fatness = -2;\r\n\t\t\t\t\tself.forceshader = shaderforname(\"powerups/quad\");\r\n\t\t\t\t}\r\n\t\t\t\tif (self.sveffects & SVE_GOD)\r\n\t\t\t\t{\r\n\t\t\t\t\taddentity(self);\r\n\r\n\t\t\t\t\tself.fatness = -2.8;\r\n\t\t\t\t\tself.forceshader = shaderforname(\"powerups/regen\");\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\tbreak;\r\n\t\t}\r\n#endif\r\n\t\tif (self.sveffects & SVE_INVIS)\r\n\t\t\tsetmodel(self, \"progs/eyes.mdl\");\r\n\t\telse\r\n\t\t\tsetmodel(self, \"progs/player.mdl\");\r\n\t\tsetsize(self, VEC_HULL_MIN, VEC_HULL_MAX);\r\n\t\tbreak;\r\n\t}\r\n\taddentity(self);\r\n\treturn PREDRAW_NEXT;\r\n};\r\n\r\nstatic void(float g) Player_SetLocalInfoGender =\r\n{\r\n\tif (player_local == self)\r\n\t{\r\n\t\t//if it was forced, don't lie to everyone else.\r\n\t\tif (CVARS(cg_forceskin) != \"\")\r\n\t\t\treturn;\r\n\r\n\t\tswitch(g)\r\n\t\t{\r\n\t\tcase GENDER_FEMALE:\r\n\t\t\tlocalcmd(\"setinfo s f\\n\");\r\n\t\t\tbreak;\r\n\t\tcase GENDER_MALE:\r\n\t\t\tlocalcmd(\"setinfo s \\\"\\\"\\n\");\r\n\t\t\tbreak;\r\n\t\tdefault:\r\n\t\tcase GENDER_NEUTER:\r\n\t\t\tlocalcmd(\"setinfo s n\\n\");\r\n\t\t\tbreak;\r\n\t\t}\r\n\t}\r\n}\r\n\r\nstatic void() Player_SetQ1Model =\r\n{\r\n\tself.modelstyle = MF_QUAKE;\r\n\tself.drawmask = MASK_NORMAL;\t\r\n\tsetmodel(self, \"progs/player.mdl\");\r\n\tPlayer_SetLocalInfoGender(GENDER_MALE);\r\n}\r\n\r\n#ifdef MD3PMODELS\r\nstatic void(string skinname) Player_SetQ3Model =\r\n{\r\n\tif (!Anim_SetModel(skinname))\r\n\t{\r\n\t\tAnim_UnsetModel();\r\n\t\treturn;\r\n\t}\r\n\tPlayer_SetLocalInfoGender(Anim_GetGender());\r\n\tself.modelstyle = MF_QUAKE3;\r\n};\r\n#endif\r\n\r\n#ifdef Q4PMODELS\r\nstatic void(string skinname) Player_SetQ4Model =\r\n{\r\n\tif (!Q4PM_SetModel(skinname))\r\n\t{\r\n\t\tQ4PM_UnsetModel();\r\n\t\treturn;\r\n\t}\r\n\tPlayer_SetLocalInfoGender(GENDER_MALE);\r\n\tself.modelstyle = MF_QUAKE4;\r\n};\r\n#endif\r\n\r\n#ifdef HLPMODELS\r\nstatic void(string skinname) Player_SetHLModel =\r\n{\r\n\tif (!HLPM_SetModel(skinname))\r\n\t{\r\n\t\tHLPM_UnsetModel();\r\n\t\treturn;\r\n\t}\r\n\tPlayer_SetLocalInfoGender(GENDER_MALE);\r\n\tself.modelstyle = MF_HLPM;\r\n};\r\n#endif\r\n\r\nstatic void() Player_UnsetModel =\r\n{\r\n\tswitch(self.modelstyle)\r\n\t{\r\n#ifdef MD3PMODELS\r\n\tcase MF_QUAKE3:\r\n\t\tAnim_UnsetModel();\r\n\t\tbreak;\r\n#endif\r\n#ifdef Q4PMODELS\r\n\tcase MF_QUAKE4:\r\n\t\tQ4PM_UnsetModel();\r\n\t\tbreak;\r\n#endif\r\n#ifdef HLPMODELS\r\n\tcase MF_HLPM:\r\n\t\tHLPM_UnsetModel();\r\n\t\tbreak;\r\n#endif\r\n\tdefault:\r\n\t\t//nothing to do\r\n\t\tbreak;\r\n\t}\r\n\tself.modelstyle = MF_BAD;\r\n\tsetmodel(self, \"\");\r\n};\r\n\r\nstatic entity() Player_DupModel =\r\n{\r\n\tlocal entity e = world;\r\n\r\n\tswitch(self.modelstyle)\r\n\t{\r\n#ifdef MD3PMODELS\r\n\tcase MF_QUAKE3:\r\n\t\te = Anim_DupModel();\r\n\t\tbreak;\r\n#endif\r\n#ifdef Q4PMODELS\r\n\tcase MF_QUAKE4:\r\n\t\te = Q4PM_DupModel();\r\n\t\tbreak;\r\n#endif\r\n#ifdef HLPMODELS\r\n\tcase MF_HLPM:\r\n\t\te = HLPM_DupModel();\r\n\t\tbreak;\r\n#endif\r\n\tdefault:\r\n\t\te = spawn();\r\n\t\tbreak;\r\n\t}\r\n\te.modelstyle = self.modelstyle;\r\n\treturn e;\r\n};\r\n\r\n.float starttime;\r\n.float oldframe;\r\nstatic void() bodythink =\r\n{\r\n\tlocal float final;\r\n\tif (self.frame >= playerframe::axdeth1 && self.frame <= playerframe::axdeth9)\r\n\t\tfinal = playerframe::axdeth9;\r\n\telse if (self.frame >= playerframe::deatha1 && self.frame <= playerframe::deatha11)\r\n\t\tfinal = playerframe::deatha11;\r\n\telse if (self.frame >= playerframe::deathb1 && self.frame <= playerframe::deathb9)\r\n\t\tfinal = playerframe::deathb9;\r\n\telse if (self.frame >= playerframe::deathc1 && self.frame <= playerframe::deathc15)\r\n\t\tfinal = playerframe::deathc15;\r\n\telse if (self.frame >= playerframe::deathd1 && self.frame <= playerframe::deathd9)\r\n\t\tfinal = playerframe::deathd9;\r\n\telse if (self.frame >= playerframe::deathe1 && self.frame <= playerframe::deathe9)\r\n\t\tfinal = playerframe::deathe9;\r\n\telse\r\n\t\tfinal = 0;\r\n\r\n\tself.frame = self.frame+1;\r\n\r\n\tif (self.frame > final)\r\n\t\tself.frame = final;\r\n\r\n\tself.nextthink = time + 0.1;\r\n};\r\nstatic float() DeadBodyPredraw =\r\n{\r\n\tfloat ftime;\r\n\tftime = time - self.starttime;\r\n\r\n\tif (self.removetime < time)\r\n\t{\r\n\t\tPlayer_UnsetModel();\r\n\t\tremove(self);\r\n\t\treturn PREDRAW_NEXT;\r\n\t}\r\n\r\n\tMovetype_Bounce();\r\n\r\n\tPlayer_Interpolate();\r\n\tself.origin_z -= ftime*0.5;\r\n\treturn PREDRAW_AUTOADD;\r\n};\r\n\r\nstatic void() JustRessed =\r\n{\r\n\tlocal entity e;\r\n\r\n\te = Player_DupModel();\r\n\te.frame = self.oldframe;\t//and stay dead!\r\n\te.frame2 = self.oldframe;\t//and stay dead!\r\n\tsetmodel(e, \"progs/player.mdl\");\r\n\te.origin = self.lastorg;\r\n\te.velocity = self.lastvel;\r\n\te.predraw = DeadBodyPredraw;\r\n\te.removetime = time + 30;\r\n\te.lerptime = time;\r\n\r\n\te.gibbable = GIB_PLAYER;\r\n\te.solid = SOLID_TRIGGER;\r\n\r\n\te.classname = \"deadbody\";\r\n\te.movetype = MOVETYPE_TOSS;\r\n\r\n\te.starttime = time;\r\n\te.nextthink = time + 0.1;\r\n\te.think = bodythink;\r\n\te.drawmask = self.drawmask;\r\n};\r\n\r\nstatic void() RemovePlayer =\r\n{\r\n\tif (player_local == self)\r\n\t\tplayer_local = world;\r\n\t\t\r\n\tif (self.haddied)\r\n\t{\r\n\t\tJustRessed();\r\n\t\tself.haddied = false;\r\n\t}\r\n\r\n\tPlayer_UnsetModel();\r\n\r\n\tstrunzone(self.oldskin);\r\n\tself.oldskin = \"\";\r\n\r\n\tself.predraw = __NULL__;\r\n};\r\n\r\nfloat(float isnew) RefreshPlayer =\r\n{\r\n\tlocal string newskin;\r\n\r\n\tself.predraw = Player_Interpolate;\r\n\r\n\tif (isnew)\r\n\t\tself.haddied = false;\r\n\r\n\t//if its not new and they're not dead, but they were before..\r\n\tif (self.frame < playerframe::axdeth1 || self.frame > playerframe::deathe9)\r\n\t{\r\n\t\tif (self.haddied)\r\n\t\t{\r\n\t\t\tJustRessed();\r\n\t\t\tself.haddied = false;\r\n\t\t}\r\n\t\tself.solid = SOLID_BBOX;\r\n\t}\r\n\telse\r\n\t{\r\n\t\tself.haddied = true;\r\n\t\tself.solid = SOLID_NOT;\r\n\t}\r\n\r\n\tself.lastorg = self.origin;\r\n\tself.lastvel = self.velocity;\r\n\r\n\tif (isnew)\r\n\t{\r\n\t\tself.modelstyle = MF_BAD;\r\n\r\n\t\tself.oldskin = strzone(\"\");\r\n\t\tself.drawmask = MASK_NORMAL;\r\n\r\n\t\tself.removefunc = RemovePlayer;\r\n\t\tself.customphysics = (void()){};\r\n\t}\r\n\tif (self.entnum == player_localentnum)\r\n\t\tplayer_local = self;\r\n\r\n\tif (self.sveffects & SVE_INVIS)\r\n\t{\r\n\t\tif (self.modelstyle != MF_QUAKE)\r\n\t\t{\r\n\t\t\tPlayer_UnsetModel();\r\n\t\t\tPlayer_SetQ1Model();\t//every model uses eyes\r\n\t\t}\r\n\t}\r\n\telse\r\n\t{\r\n\t\tnewskin = CVARS(cg_forceskin);\r\n\t\tif (newskin == \"\")\r\n\t\t\tnewskin = getplayerkeyvalue(self.entnum-1, \"model\");\r\n\t\tif (newskin == \"\")\r\n\t\t\tnewskin = getplayerkeyvalue(self.entnum-1, \"skin\");\r\n\t\tif (newskin != self.oldskin)\r\n\t\t{\r\n\t\t\tPlayer_UnsetModel();\r\n\r\n\t\t\tstrunzone(self.oldskin);\r\n\t\t\tself.oldskin = strzone(newskin);\r\n\t\t}\r\n\r\n\t\tif (self.modelstyle == MF_BAD)\r\n\t\t{\r\n\t\t\tnewskin = self.oldskin;\r\n\r\n#ifdef MD3PMODELS\r\n\t\t\tif (self.modelstyle == MF_BAD && newskin != \"\")\r\n\t\t\t\tPlayer_SetQ3Model(newskin);\r\n#endif\r\n#ifdef Q4PMODELS\r\n\t\t\tif (self.modelstyle == MF_BAD && newskin != \"\")\r\n\t\t\t\tPlayer_SetQ4Model(newskin);\r\n#endif\r\n#ifdef HLPMODELS\r\n\t\t\tif (self.modelstyle == MF_BAD && newskin != \"\")\r\n\t\t\t\tPlayer_SetHLModel(newskin);\r\n#endif\r\n\t\t\tif (self.modelstyle == MF_BAD)\r\n\t\t\t\tPlayer_SetQ1Model();\r\n\r\n\t\t\tswitch(self.modelstyle)\r\n\t\t\t{\r\n\t\t\tcase MF_BAD:\tdprint(sprintf(\"%S: player model not known\\n\", newskin)); break;\r\n#ifdef MD3PMODELS\r\n\t\t\tcase MF_QUAKE3:\tdprint(sprintf(\"%S: quake3 player model loaded\\n\", newskin)); break;\r\n#endif\r\n#ifdef Q4PMODELS\r\n\t\t\tcase MF_QUAKE4:\tdprint(sprintf(\"%S: quake4 player model loaded\\n\", newskin)); break;\r\n#endif\r\n#ifdef HLPMODELS\r\n\t\t\tcase MF_HLPM:\tdprint(sprintf(\"%S: halflife player model loaded\\n\", newskin)); break;\r\n#endif\r\n#ifdef XM\r\n\t\t\tcase MF_XM:\tdprint(sprintf(\"%S: xonotic player model loaded\\n\", newskin)); break;\r\n#endif\r\n\t\t\tcase MF_QUAKE:\tdprint(sprintf(\"%S: quake player model loaded\\n\", newskin)); break;\r\n\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\tsetsize(self, VEC_HULL_MIN, VEC_HULL_MAX);\r\n\r\n\tself.oldframe = self.frame;\r\n\r\n\tif (player_local != self)\r\n\t{\r\n\t\treturn TRUE;\r\n\t}\r\n\tPred_PlayerUpdated(self);\r\n\treturn TRUE;\r\n};\r\n\r\n//this is sent after the server has run our movement command.\r\nnonstatic void(float isnew) ParsePlayer =\r\n{\r\n\tlocal float f;\r\n\r\n\tf = readbyte();\r\n\r\n\tif (f != self.frame || isnew)\r\n\t{\r\n\t\tself.frame2 = self.frame;\r\n\t\tself.lerptime = time;\r\n\t\tself.frame = f;\r\n\t}\r\n\r\n\t//note: this could/should be compressed. this original code predated SendFlags.\r\n\tself.angles_x = readbyte()*(360/256);\t//not really needed for ourselves, but useful when its showing a different player.\r\n\tself.angles_y = readbyte()*(360/256);\r\n\tself.origin_x = readcoord();\r\n\tself.origin_y = readcoord();\r\n\tself.origin_z = readcoord();\r\n\tself.velocity_x = readshort()/8;\r\n\tself.velocity_y = readshort()/8;\r\n\tself.velocity_z = readshort()/8;\r\n\tself.colormap = self.entnum;\r\n\r\n\tself.sveffects = readbyte();\r\n\tif (self.sveffects & SVE_MOVETYPE)\r\n\t\tself.movetype = readbyte();\r\n\telse\r\n\t\tself.movetype = MOVETYPE_WALK;\r\n\tself.pmove_flags = (self.movetype&~63)>>6;\r\n\tself.movetype &= 63;\r\n\r\n\tRefreshPlayer(isnew);\r\n};\r\n\r\n#ifdef NOEXTENSIONS\r\n#undef strunzone\r\n#undef strzone\r\n#endif\r\n"
  },
  {
    "path": "quakec/csqctest/src/cs/playerframes.inc",
    "content": "enum playerframe\r\n{\r\n//\r\n//, running\r\n//\r\n\taxrun1, axrun2, axrun3, axrun4, axrun5, axrun6,\r\n\trockrun1, rockrun2, rockrun3, rockrun4, rockrun5, rockrun6,\r\n\r\n//\r\n//, standing\r\n//\r\n\tstand1, stand2, stand3, stand4, stand5,\r\n\taxstnd1, axstnd2, axstnd3, axstnd4, axstnd5, axstnd6, axstnd7, axstnd8, axstnd9, axstnd10, axstnd11, axstnd12,\r\n\r\n\r\n//\r\n//, pain\r\n//\r\n\taxpain1, axpain2, axpain3, axpain4, axpain5, axpain6,\r\n\tpain1, pain2, pain3, pain4, pain5, pain6,\r\n\r\n\r\n//\r\n//, death\r\n//\r\n\r\n\taxdeth1, axdeth2, axdeth3, axdeth4, axdeth5, axdeth6, axdeth7, axdeth8, axdeth9,\r\n\tdeatha1, deatha2, deatha3, deatha4, deatha5, deatha6, deatha7, deatha8,\tdeatha9, deatha10, deatha11,\r\n\tdeathb1, deathb2, deathb3, deathb4, deathb5, deathb6, deathb7, deathb8,\tdeathb9,\r\n\tdeathc1, deathc2, deathc3, deathc4, deathc5, deathc6, deathc7, deathc8,\tdeathc9, deathc10, deathc11, deathc12, deathc13, deathc14, deathc15,\r\n\tdeathd1, deathd2, deathd3, deathd4, deathd5, deathd6, deathd7, deathd8, deathd9,\r\n\tdeathe1, deathe2, deathe3, deathe4, deathe5, deathe6, deathe7, deathe8, deathe9,\r\n\r\n//\r\n//, attacks\r\n//\r\n\tnailatt1, nailatt2,\r\n\tlight1, light2,\r\n\trockatt1, rockatt2, rockatt3, rockatt4, rockatt5, rockatt6,\r\n\tshotatt1, shotatt2, shotatt3, shotatt4, shotatt5, shotatt6,\r\n\taxatt1, axatt2, axatt3, axatt4, axatt5, axatt6,\r\n\taxattb1, axattb2, axattb3, axattb4, axattb5, axattb6,\r\n\taxattc1, axattc2, axattc3, axattc4, axattc5, axattc6,\r\n\taxattd1, axattd2, axattd3, axattd4, axattd5, axattd6,\r\n};\r\n"
  },
  {
    "path": "quakec/csqctest/src/cs/prediction.qc",
    "content": "/*\r\nUpdateLocalMovement: runs the local prediction (called from player.qc - local players are drawn for mirrors etc)\r\n\tout: vieworg (view origin, with chasecam/viewheight added)\r\n\tout: ` (raw player ent origin)\r\n\r\nPlayerUpdated: updates internal state, called from player.qc any time we got an update from the server.\r\n\tin: self (the player ent origin/velocity/mins/maxs)\r\n\r\nResetPlayerPrediction: call if you broke the special pmove globals\r\n*/\r\n\r\n#define ERRORTIME 20\r\n#define STEPTIME 8\r\n\r\nvector player_org;\r\nvector player_vel;\r\nfloat player_pmflags;\r\nfloat player_sequence;\r\n\r\nfloat player_steptime;\r\nfloat player_step;\r\n\r\nvector pmove_error;\r\nfloat pmove_errortime;\r\n\r\nfloat pmove_step;\r\nfloat pmove_steptime;\r\nfloat pmove_step_oldz;\r\n\r\n.float pmove_flags;\r\n\r\nfloat pmoveframe;\r\nnonstatic void(entity ent) Pred_ResetPlayerPrediction = \r\n{\r\n//reset the pmove to lerp from the new position\r\n\tent.origin = player_org;\r\n\tent.velocity = player_vel;\r\n\tpmoveframe = player_sequence+1;\t//+1 because the recieved frame has the move already done (server side)\r\n\tent.pmove_flags = player_pmflags;\r\n\r\n\tif (pmoveframe < clientcommandframe-128)\r\n\t\tpmoveframe = clientcommandframe-128;\t//avoid an infinate loop\r\n};\r\n\r\n//called from map.qc when a teleport trigger was predicted to be touched.\r\nvoid(vector newteleorg, vector newtelevel, vector newteleang) Pred_Predict_Teleport =\r\n{\r\n\tfloat teleframe;\r\n\tteleframe = pmoveframe;\r\n\r\n\t//if this is the first client frame that shows us moving into the teleporter\r\n\tif (teleframe == clientcommandframe-1)\r\n\t{\r\n\t\t//update the client's view angles\r\n\t\tsetviewprop(33, newteleang);\r\n\t\tview_angles = newteleang;\r\n\t}\r\n\tother.origin = newteleorg;\r\n\tother.velocity = newtelevel;\r\n\tinput_angles = newteleang;\r\n};\r\n\r\nvoid(entity ent, float endframe) Pred_RunMovement;\r\n\r\nnonstatic void(entity ent) Pred_PlayerUpdated =\r\n{\r\n\tlocal float noerror;\r\n\tlocal vector o;\r\n\tlocal vector v;\r\n\tlocal float pmf;\r\n\r\n\to = ent.origin;\r\n\tv = ent.velocity;\r\n\tpmf = ent.pmove_flags;\r\n\r\n\tnoerror = CVARF(cg_noerror);\r\n\r\n\t//reset the prediction to last-known-good state\r\n\tPred_ResetPlayerPrediction(ent);\r\n\tPred_RunMovement(ent, servercommandframe+1);\r\n\r\n\tplayer_step = pmove_step;\r\n\tplayer_steptime = pmove_steptime;\r\n\r\n\t//pull out the new values\r\n\tplayer_org = o;\r\n\tplayer_vel = v;\r\n\tplayer_sequence = servercommandframe;\r\n\tplayer_pmflags = pmf;\r\n\r\n\tif (noerror)\r\n\t{\r\n\t\tpmove_error = '0 0 0';\r\n\t\tpmove_errortime = time;\r\n\r\n\t\tPred_ResetPlayerPrediction(ent);\r\n\t}\r\n\telse\r\n\t{\r\n\t\tPred_RunMovement(ent, clientcommandframe); //make sure we're up to date\r\n\t\to = ent.origin;\t//save off the old for the teleport check below.\r\n\r\n\t\t//reset it, then update to now to guage how much our previous prediction was incorrect by\r\n\t\tPred_ResetPlayerPrediction(ent);\r\n\t\tPred_RunMovement(ent, clientcommandframe);\r\n\r\n\t\tif (vlen(o - ent.origin) > 64)\r\n\t\t{//teleport\r\n\t\t\tpmove_error = '0 0 0';\r\n\t\t\tpmove_errortime = time;\r\n\t\t}\r\n\t\telse\r\n\t\t{\t//figure out the error ammount, and lerp back to it, without forgetting about any current inaccuracies.\r\n\t\t\tpmove_error = (pmove_errortime - time)*ERRORTIME * pmove_error + (o - ent.origin);\r\n\t\t\tif (vlen(pmove_error) < 1)\r\n\t\t\t\tpmove_error = '0 0 0';\r\n\t\t\tpmove_errortime = time + 1/ERRORTIME;\r\n\t\t}\r\n\t}\r\n};\r\n\r\nvoid(entity ent, float endframe) Pred_RunMovement =\r\n{\r\n\tvector oorg;\r\n\tif (servercommandframe >= player_sequence+63)\r\n\t{\r\n\t\t//we're meant to be updating the player faster than this\r\n\t\t//hopefully its just that we're throttled...\r\n\r\n\t\t//you can comment out this block and the player will continue to be predicted locally. But its best to freeze them\r\n\r\n\t\tplayer_sequence = servercommandframe-63;\r\n\r\n\t\treturn;\r\n\t}\r\n\r\n\t//rewind to our last-known-good position\r\n\tPred_ResetPlayerPrediction(ent);\r\n\r\n\tif (getstatf(STAT_HEALTH) <= 0)\r\n\t{\r\n\t\tpmoveframe = clientcommandframe;\r\n\t\t//just update the angles\r\n\t\tif (!getinputstate(pmoveframe-1))\r\n\t\t\tdprint(sprintf(\"Bad sequence %g\\n\", pmoveframe-1));\r\n\t\treturn;\t//dead, so don't run prediction. :D\r\n\t}\r\n\r\n\tif (CVARF(cg_nopred))\r\n\t{\r\n\t\tpmoveframe = clientcommandframe;\r\n\t\t//just update the angles\r\n\t\tif (!getinputstate(pmoveframe-1))\r\n\t\t\tdprint(sprintf(\"Bad sequence %g\\n\", pmoveframe-1));\r\n\t\tinput_angles = view_angles;\r\n\t\treturn;\r\n\t}\r\n\r\n//\tif (getinputstate(pmoveframe-1))\r\n//\t\tent.pmove_flags = PMF_JUMP_HELD;\r\n\twhile(pmoveframe <= endframe)\r\n\t{\r\n\t\tif (!getinputstate(pmoveframe))\r\n\t\t{\r\n\t\t\tdprint(sprintf(\"Bad sequence %g\\n\", pmoveframe));\r\n\t\t\tbreak;\r\n\t\t}\r\n\t\tif (pmoveframe==clientcommandframe)\r\n\t\t\tCSQC_Input_Frame();\t//manipulate it like we would if we actually sent it...\r\n\t\toorg = ent.origin;\r\n#ifdef OWNPLAYERPHYSICS\r\n\t\tPMove(ent);\r\n#else\r\n\t\trunstandardplayerphysics(ent);\r\n#endif\r\n\t\tif (input_buttons & ((pmoveframe == clientcommandframe)?128:64))\r\n\t\t{   //some debugging info, to match up to the client. server's sequence will have no gaps.\r\n\t\t\tprint(sprintf(\"after input_sequence:%g(%g-%g), msecs:%g, aim:%v, move:%v%s\\n\", input_sequence, servercommandframe, clientcommandframe, input_timelength*1000, input_angles, input_movevalues, (pmoveframe == clientcommandframe)?\" \\spartial\":\"\"));\r\n\t\t\tprint(sprintf(\"  origin: %v <- %v, velocity %v\\n\", ent.origin, oorg, ent.velocity));\r\n\t\t}\r\n\r\n\t\tpmoveframe++;\r\n\t}\r\n\r\n\t//add in anything that was applied after (for low packet rate protocols)\r\n\tinput_angles = view_angles;\r\n};\r\n\r\nnonstatic void(entity ent) Pred_UpdateLocalMovement =\r\n{\r\n\tlocal float viewheight;\r\n\r\n\tPred_RunMovement(ent, clientcommandframe);\r\n\r\n\tif (ent.origin_z > pmove_step_oldz+8 && ent.origin_z < pmove_step_oldz+24 && ent.velocity_z == 0)\r\n\t{\r\n\t\t//evaluate out the remaining old step\r\n\t\tif (pmove_steptime - time > 0)\r\n\t\t\tpmove_step = (pmove_steptime - time)*STEPTIME*pmove_step;\r\n\t\telse\r\n\t\t\tpmove_step = 0;\r\n\r\n\t\t//work out the new step\r\n\t\tpmove_step += (pmove_step_oldz-self.origin_z);\r\n\t\tpmove_steptime = time + 1/STEPTIME;\r\n\t}\r\n\tpmove_step_oldz = ent.origin_z;\r\n\r\n\t//allow the user to move the viewheight down 6 units so it's at +16, where projectiles come from.\r\n\tviewheight = CVARF(v_viewheight);\r\n\tif (viewheight < -7)\r\n\t\tviewheight = -7;\r\n\telse if (viewheight > 7)\r\n\t\tviewheight = 7;\r\n\r\n\tvieworg = ent.origin;\t//the default view height\r\n\tvieworg_z += getstatf(STAT_VIEWHEIGHT) + viewheight;\r\n\r\n\t//correct the view position to compensate for any errors, slowly over time, 0.1 seconds.\r\n\tif (pmove_errortime - time > 0)\r\n\t\tvieworg += (pmove_errortime - time)*ERRORTIME * pmove_error;\r\n\r\n\tif (!CVARF(cg_nostep))\r\n\t\tif (pmove_steptime - time > 0)\r\n\t\t\tvieworg_z += (pmove_steptime - time)*STEPTIME * pmove_step;\r\n\r\n\tif (chasecam)\r\n\t{\r\n\t\tview_angles_y += CVARF(cg_thirdPersonAngle);\r\n\r\n\t\tmakevectors(view_angles);\r\n\t\ttraceline(self.origin, vieworg - v_forward * CVARF(cg_thirdPersonRange)+v_up*CVARF(cg_thirdPersonHeight), TRUE, self);\r\n\t\tvieworg = trace_endpos + v_forward*8;\r\n\t}\r\n};\r\n"
  },
  {
    "path": "quakec/csqctest/src/cs/q3playerm.qc",
    "content": "//This file provides support for Q3-compatible player models.\r\n\r\n/*\r\n\tThis module provides:\r\n\t\tbool(string newmodelskin) Anim_SetModel\r\n\t\t\tThis function applies the named model (eg: \"sarge\" or \"sarge/red\") to self.\r\n\t\t\tReturns false on failure. If it fails, the entity will be untouched.\r\n\t\tvoid() Anim_Draw\r\n\t\t\tThis function adds self's segmented models to the scene.\r\n\t\t\tThis is intended to be called from inside a predraw function.\r\n\t\tvoid() Anim_UnsetModel\r\n\t\t\tThis frees everything assosiated with self, but Does not flush the animation file.\r\n\t\tentity() Anim_DupModel\r\n\t\t\tDuplicates the animation state/model from self entity to a newly spawned entity.\r\n\t\t\tThis is expected to be used for bodyqueues.\r\n\t\tfloat(string fname) Anim_ReadAnimationFile\r\n\t\t\tAn explicit call is not required, except as perhaps a precache.\r\n\t\t\tfname is eg: \"sarge\", but NOT \"sarge/red\".\r\n\t\t\tReturns -1 on failure.\r\n\t\t\tFails if list is full or animation.cfg is not found. Does not fail if a model is not found.\r\n\t\tfloat() Anim_GetGender\r\n\t\t\tReturns which gender the 'self' player model is.\r\n\t\tfloat(string skinname) Anim_GetHeadModelIndex\r\n\t\t\tReturns a modelindex assosiated with the given model/skin name.\r\n\t\t\tIntended for the hud.\r\n\t\tfloat(string skinname) Anim_GetHeadSkinNumber\r\n\t\t\tReturns a skin number for use with the modelindex returned by Anim_GetHeadModelIndex to give the correct model/skin for the given model/skin parameter.\r\n\t\t\tIntended for the hud.\r\n\t\tvector(string skinname) Anim_GetHeadOffset\r\n\t\t\tRetrieves the q3 head offset for displaying the head in a nice neat box on the hud.\r\n\r\n\tif only I were allowed to use pointers... these three head functions would be a single more efficient function\r\n\r\n*/\r\n\r\n#ifdef MD3PMODELS\r\n\r\n//these are the animation sequence names used in quake3.\r\nenum {\r\n\tBOTH_DEATH1,\r\n\tBOTH_DEAD1,\r\n\tBOTH_DEATH2,\r\n\tBOTH_DEAD2,\r\n\tBOTH_DEATH3,\r\n\tBOTH_DEAD3,\r\n\r\n\tTORSO_GESTURE,\r\n\r\n\tTORSO_ATTACK,\r\n\tTORSO_ATTACK2,\r\n\r\n\tTORSO_DROP,\r\n\tTORSO_RAISE,\r\n\r\n\tTORSO_STAND,\r\n\tTORSO_STAND2,\r\n\r\n\tLEGS_WALKCR,\r\n\tLEGS_WALK,\r\n\tLEGS_RUN,\r\n\tLEGS_BACK,\r\n\tLEGS_SWIM,\r\n\r\n\tLEGS_JUMP,\r\n\tLEGS_LAND,\r\n\r\n\tLEGS_JUMPB,\r\n\tLEGS_LANDB,\r\n\r\n\tLEGS_IDLE,\r\n\tLEGS_IDLECR,\r\n\tLEGS_TURN,\r\n\r\n\tNUMANIMS\r\n};\r\n\r\n\r\n//we use qc arrays to store all the information. this results in a lot of prog space used.\r\n#define MAXMODELS 50\t//if progs size becomes an issue, reduce this.\r\n\r\n//a size optimisation.\r\n#define anim_firstframe A\r\n#define anim_numframes B\r\n#define anim_loopingframes C\r\n#define anum_framespersecond D\r\n\r\n//a q3 animation.cfg file contains 4 items per sequence.\r\nnosave float anim_firstframe[NUMANIMS*MAXMODELS];\t//first frame in the sequence\r\nnosave float anim_numframes[NUMANIMS*MAXMODELS];\t//number of frames in this sequence.\r\nnosave float anim_loopingframes[NUMANIMS*MAXMODELS];\t//number of frames to play while looping (jumps to total-looping)\r\nnosave float anum_framespersecond[NUMANIMS*MAXMODELS];//number of frames to play per second.\r\n\r\nnosave float anim_gender[MAXMODELS];\t//one of the SEX_ enum values.\r\nnosave string anim_name[MAXMODELS];\t\t//names the (skinless) player model, so they can be cached and shared.\r\nnosave float anim_headmodel[MAXMODELS];\r\nnosave vector anim_headoffset[MAXMODELS];\r\n\r\n//note: q3 assumes male.\r\n//we assume male too, due to the player model that is typically used if we have no q3 models.\r\n#define GENDER_DEFAULT GENDER_MALE\r\n\r\n\r\n/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\r\n//This block of code is to replace builtins which may be missing in your engine of choice.\r\n//Its all generic maths/tag stuff, so consider it just noise :)\r\n//this doesn't affect how the model is built.\r\n\r\n#ifdef WORKINDP\r\nvector getlerpedtaginfo(entity ent, float tagnum)\r\n{\r\n\t//personally I consider it a bug that this function is needed.\r\n\t//this function exactly duplicates the gettaginfo builtin\r\n\t//but ensures that interpolation based on frame2 happens.\r\n\t//this matches how the renderer will lerp frames.\r\n\r\n\t//at the time of writing, this is needed for DP to be smooth.\r\n\t//fte does not support gettaginfo at all (I've been lazy due to the heirachical nature).\r\n\t//this is called by rotatevectorsbytag_GTI, so it not required in fte as FTE has a working rotatevectorsbytag instead.\r\n\r\n\tfloat frame2ness = ent.lerpfrac;\r\n\tfloat frame1ness = 1-frame2ness;\r\n\tfloat f1=ent.frame;\r\n\tfloat f2=ent.frame2;\r\n//it doesn't matter what the ent's lerpfrac currently is.\r\n\tvector f1o, f1f, f1r, f1u;\r\n\tvector f2o, f2f, f2r, f2u;\r\n\tvector v_origin;\r\n\r\n\t//make sure both frames are set, in case the builtin is fixed\r\n\tent.frame = f1;\r\n\tent.frame2 = f1;\r\n\tf1o = gettaginfo(ent, tagnum);\r\n\tf1r=v_right;f1f=v_forward;f1u=v_up;\r\n\r\n\tent.frame = f2;\r\n\tent.frame2 = f2;\r\n\tv_origin = gettaginfo(ent, tagnum);\r\n\r\n\t//restore the entity\r\n\tent.frame = f1;\r\n\tent.frame2 = f2;\r\n\r\n\t//lerp the current frame2 with the cached frame1 values\r\n\tv_forward = f1f*frame1ness+v_forward*frame2ness;\r\n\tv_right = f1r*frame1ness+v_right*frame2ness;\r\n\tv_up = f1u*frame1ness+v_up*frame2ness;\r\n\tv_origin = f1o*frame1ness+v_origin*frame2ness;\r\n\r\n\treturn v_origin;\r\n}\r\n\r\n#define rotatevectorsbytag rotatevectorsbytag_GTI\r\nvector rotatevectorsbytag_GTI(entity ent, float tagnum)\r\n{\r\n\tvector saveang=ent.angles;\r\n\tvector saveorg=ent.origin;\r\n\r\n\tvector oldx=v_forward, oldy=('0 0 0'-v_right), oldz=v_up;\r\n\tvector oldo=ent.origin;\r\n\r\n\tent.angles = '0 0 0';\r\n\tent.origin = '0 0 0';\r\n\tvector ango=getlerpedtaginfo(ent, tagnum);\r\n\tent.angles = saveang;\r\n\tent.origin = saveorg;\r\n//note: v_right is actually left, in tags.\r\n\tvector angx=v_forward, angy=v_right, angz=v_up;\r\n\r\n\tangx = normalize(angx);\r\n\tangy = normalize(angy);\r\n\tangz = normalize(angz);\r\n\r\n\t//multiply out the tag matrix with the previous matrix.\r\n\tv_forward_x = angx_x*oldx_x + angx_y*oldy_x + angx_z*oldz_x;\r\n\tv_forward_y = angx_x*oldx_y + angx_y*oldy_y + angx_z*oldz_y;\r\n\tv_forward_z = angx_x*oldx_z + angx_y*oldy_z + angx_z*oldz_z;\r\n\tv_right_x   = angy_x*oldx_x + angy_y*oldy_x + angy_z*oldz_x;\r\n\tv_right_y   = angy_x*oldx_y + angy_y*oldy_y + angy_z*oldz_y;\r\n\tv_right_z   = angy_x*oldx_z + angy_y*oldy_z + angy_z*oldz_z;\r\n\tv_up_x      = angz_x*oldx_x + angz_y*oldy_x + angz_z*oldz_x;\r\n\tv_up_y      = angz_x*oldx_y + angz_y*oldy_y + angz_z*oldz_y;\r\n\tv_up_z      = angz_x*oldx_z + angz_y*oldy_z + angz_z*oldz_z;\r\n\r\n\tv_right = '0 0 0'-v_right;\r\n\r\n\t//transform the tag's origin\r\n\tsaveorg = oldo;\r\n\tsaveorg_x += ango_x * oldx_x;\r\n\tsaveorg_y += ango_x * oldx_y;\r\n\tsaveorg_z += ango_x * oldx_z;\r\n\tsaveorg_x += ango_y * oldy_x;\r\n\tsaveorg_y += ango_y * oldy_y;\r\n\tsaveorg_z += ango_y * oldy_z;\r\n\tsaveorg_x += ango_z * oldz_x;\r\n\tsaveorg_y += ango_z * oldz_y;\r\n\tsaveorg_z += ango_z * oldz_z;\r\n\treturn saveorg;\r\n}\r\n#define rotatevectorsbyangle rotatevectorsbyangle_QC\r\nvoid rotatevectorsbyangle_QC(vector angle)\r\n{\r\n\tvector oldx=v_forward, oldy='0 0 0'-v_right, oldz=v_up;\r\n\tangle_x = -angle_x;\r\n\tmakevectors(angle);\r\n\tvector angx=v_forward, angy='0 0 0'-v_right, angz=v_up;\r\n\r\n\tv_forward_x = angx_x*oldx_x + angx_y*oldy_x + angx_z*oldz_x;\r\n\tv_forward_y = angx_x*oldx_y + angx_y*oldy_y + angx_z*oldz_y;\r\n\tv_forward_z = angx_x*oldx_z + angx_y*oldy_z + angx_z*oldz_z;\r\n\tv_right_x   = angy_x*oldx_x + angy_y*oldy_x + angy_z*oldz_x;\r\n\tv_right_y   = angy_x*oldx_y + angy_y*oldy_y + angy_z*oldz_y;\r\n\tv_right_z   = angy_x*oldx_z + angy_y*oldy_z + angy_z*oldz_z;\r\n\tv_up_x      = angz_x*oldx_x + angz_y*oldy_x + angz_z*oldz_x;\r\n\tv_up_y      = angz_x*oldx_y + angz_y*oldy_y + angz_z*oldz_y;\r\n\tv_up_z      = angz_x*oldx_z + angz_y*oldy_z + angz_z*oldz_z;\r\n\r\n\tv_right = '0 0 0'-v_right;\r\n}\r\n#define RotateVectorsByVectors RotateVectorsByVectorsDP\r\n\r\n//\r\n#define skinforname(i,n) 0\r\n#endif\r\n\r\n//end noisy maths.\r\n/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\r\n\r\n//a utility function required by sexedsound so we don't try playing missing sounds (its common for male models to be missing some)\r\nfloat(string str) fileexists =\r\n{\r\n\t//we use searches because its more likly to be cached than reading a 200kb file from the disk every time.\r\n\tlocal float search;\r\n\tsearch = search_begin(str, false, true);\r\n\tif (search < 0)\r\n\t{\r\n\t\treturn false;\r\n\t}\r\n\tsearch_end(search);\r\n\treturn true;\r\n};\r\n\r\n//plays a CHAN_VOICE sound around the given entity.\r\nvoid(entity ent, string soundname) sexedsound =\r\n{\r\n\tstring str, full;\r\n\tif (self == player_local)\r\n\t\tif (CVARF(cg_noselfsounds))\r\n\t\t\treturn;\t//option to disable sounds from local player\r\n\r\n\tstr = strcat(\"player/\", anim_name[self.modelnum], \"/\", soundname);\r\n\tfull = strcat(\"sound/\", str);\r\n\tif (fileexists(full))\r\n\t{\r\n\t\tprecache_sound(str);\r\n\t\tsound(self, CHAN_VOICE, str, 1, 1, 95 + random()*10);\r\n\t}\r\n\telse\r\n\t{\r\n\t\tstr = strcat(\"player/sarge/\", soundname);\t// :(\r\n\t\tprecache_sound(str);\r\n\t\tsound(self, CHAN_VOICE, str, 1, 1, 95 + random()*10);\r\n\t}\r\n};\r\n\r\n\r\n//our player's frames changed to some new sequence.\r\n//our animation function will animate acordingly.\r\n\r\nstatic void(float anum) ForceToAnim =\r\n{\r\n\tif (anum <= TORSO_STAND2)\r\n\t{\r\n\t\tself.torsoent.animnum = anum;\r\n\t\tself.torsoent.framechangetime = time;\r\n\t}\r\n\tif (anum <= BOTH_DEAD3 || anum >= LEGS_WALKCR)\r\n\t{\r\n\t\tself.legsent.animnum = anum;\r\n\t\tself.legsent.framechangetime = time;\r\n\t}\r\n};\r\n\r\n//this function is per-segment.\r\n//it updates the frame/frame2/lerpfrac values and loops/caps the animation.\r\n//returns true if the end of the current animation was reached.\r\nfloat(entity ent) animate =\r\n{\r\n//true if looped\r\n\tfloat anum;\r\n\tfloat ft;\r\n\tfloat fnum;\r\n\tfloat numframes;\r\n\tfloat fps;\r\n\tfloat loopingframes;\r\n\tfloat ret;\r\n\r\n\tanum = (ent.modelnum * NUMANIMS) + ent.animnum;\r\n\r\n\tft = time - ent.framechangetime;\r\n\tnumframes = anim_numframes[anum];\r\n\tfps = anum_framespersecond[anum];\r\n\r\n\tft *= fps;\r\n\tfnum = floor(ft);\r\n\tft = ft - fnum;\t//time into the frame.\r\n\tret = fnum >= numframes;\r\n\tif (ret)\r\n\t{\r\n\t\tloopingframes = anim_loopingframes[anum];\r\n\r\n\t\tif (loopingframes)\r\n\t\t{\r\n\t\t\tfnum -= numframes - loopingframes;\r\n\t\t\tfnum = fnum - floor(fnum/loopingframes)*loopingframes;\r\n\t\t\tfnum += numframes - loopingframes;\r\n\t\t}\r\n\t\telse\r\n\t\t{\t//stop at the end of it.\r\n\t\t\tfnum = numframes-1;\r\n\t\t\tft = 1;\r\n\t\t}\r\n\r\n\t\tif (numframes == 1)\r\n\t\t\tent.frame2 = ent.frame;\r\n\t}\r\n\tfnum += anim_firstframe[anum];\r\n\r\n\tif (ent.frame2 == -1)\r\n\t{\t//special flag to make it snap for the initial frame\r\n\t\tent.frame = fnum;\r\n\t\tent.frame2 = fnum;\r\n\t}\r\n\telse if (ent.frame != fnum)\r\n\t{\r\n\t\tent.frame2 = ent.frame;\r\n\t\tent.frame = fnum;\r\n\t}\r\n\tent.lerpfrac = 1-ft;\r\n\tif (ent.lerpfrac<=0)\r\n\t\tent.frame2 = ent.frame;\r\n\r\n\treturn ret;\r\n};\r\n\r\n.float wasinair;\r\n//this function checks to see if the player is jumping/flying/dead/moving, and just updates the legs to match the current events and time.\r\nvoid() LegsUpdateAnim =\r\n{\r\n\tfloat inair;\r\n\r\n\ttracebox(self.origin, self.mins, self.maxs, self.origin-'0 0 2', FALSE, self);\r\n\r\n\tif (trace_fraction == 1)\r\n\t\tinair = self.velocity_z;\r\n\telse\r\n\t\tinair = 0;\r\n\r\n\tif (self.legsent.animnum > BOTH_DEAD3)\r\n\tif ((self.wasinair!=0) != (inair!=0))\r\n\t{\r\n\t\tif (trace_fraction == 1)\t//in air\r\n\t\t{\r\n\t\t\tif (self.velocity_z > 5)\r\n\t\t\t{\r\n\t\t\t\tmakevectors(self.angles);\r\n\t\t\t\tif (v_forward * self.velocity >= 0)\r\n\t\t\t\t\tForceToAnim(LEGS_JUMP);\r\n\t\t\t\telse\r\n\t\t\t\t\tForceToAnim(LEGS_JUMPB);\r\n\t\t\t}\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tif (inair < -5 || self.wasinair < -5)\r\n\t\t\t{\r\n\t\t\t\tif (self.animnum == LEGS_JUMPB)\r\n\t\t\t\t\tForceToAnim(LEGS_LANDB);\r\n\t\t\t\telse\r\n\t\t\t\t\tForceToAnim(LEGS_LAND);\r\n\t\t\t}\r\n\t\t}\r\n\t\tself.wasinair = inair;\r\n\t}\r\n\r\n\tif (vlen(self.velocity) > 0.1)\r\n\t{\r\n\t\tif (self.legsent.animnum == LEGS_IDLE)\r\n\t\t{\r\n\t\t\tif (self.velocity * v_forward > 0)\r\n\t\t\t\tForceToAnim(LEGS_RUN);\r\n\t\t\telse\r\n\t\t\t\tForceToAnim(LEGS_BACK);\r\n\t\t}\r\n\t}\r\n\telse\r\n\t{\r\n\t\tif (self.legsent.animnum == LEGS_RUN || self.legsent.animnum == LEGS_BACK)\r\n\t\t\tForceToAnim(LEGS_IDLE);\r\n\t}\r\n\r\n\tif (animate(self.legsent))\r\n\t{\r\n\t\tif (self.legsent.animnum <= BOTH_DEAD3)\r\n\t\t{\r\n\t\t\treturn;\t//don't play the idle anim when dead.\r\n\t\t}\r\n\t\tif (self.wasinair)\r\n\t\t{\r\n\t\t\treturn;\t//don't change the legs anim whilst flying.\r\n\t\t}\r\n\r\n\t\tif (self.velocity_x || self.velocity_y)\r\n\t\t{\r\n\t\t\tif (self.velocity * v_forward > 0)\r\n\t\t\t\tForceToAnim(LEGS_RUN);\r\n\t\t\telse\r\n\t\t\t\tForceToAnim(LEGS_BACK);\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tForceToAnim(LEGS_IDLE);\r\n\t\t}\r\n\r\n\t\tanimate(self.legsent);\r\n\t}\r\n};\r\n\r\n//updates the torso's animations. basically just shoots/gestures/or otherwise just updates to the current time.\r\nvoid() TorsoUpdateAnim =\r\n{\r\n\tif (animate(self.torsoent))\r\n\t{\r\n\t\tplayerframe f = self.frame;\r\n\t\tif (f >= playerframe::axdeth1 && f <= playerframe::deathe9)\r\n\t\t\treturn;\t//dead.\r\n\r\n\t\tif (f == playerframe::nailatt1 || f == playerframe::nailatt2 ||\r\n\t\t    f == playerframe::light1 || f == playerframe::light2)\r\n\t\t\tForceToAnim(TORSO_ATTACK);\t//these ones loop\r\n\t\telse if (random() < 0.005 && !(self.velocity_x || self.velocity_y) && (chasecam || self != player_local))\r\n\t\t{\t//randomly taunt, when standing still, when not first-person (making the sounds is just confusing when first person)\r\n\t\t\tsexedsound(self, \"taunt.wav\");\r\n\t\t\tForceToAnim(TORSO_GESTURE);\r\n\t\t}\r\n\t\telse if ((f >= playerframe::axrun1 && f <= playerframe::axrun6) ||\r\n\t\t\t (f >= playerframe::axstnd1 && f <= playerframe::axstnd12) ||\r\n\t\t\t (f >= playerframe::axpain1 && f <= playerframe::axpain6))\r\n\t\t\tForceToAnim(TORSO_STAND2);\r\n\t\telse\r\n\t\t\tForceToAnim(TORSO_STAND);\r\n\t\tanimate(self.legsent);\r\n\t}\r\n};\r\n\r\n//adds a q3-shell around the model based on which powerups they have active.\r\n//and just basically acts as a wrapper to addentity.\r\nvoid(entity ent) AddModelWithEffects =\r\n{\r\n\tif (self.sveffects & SVE_INVIS)\r\n\t\tent.forceshader = shaderforname(\"powerups/invisibility\");\r\n\r\n\taddentity(ent);\r\n\tif (self.sveffects & SVE_QUAD)\r\n\t{\r\n\t\tent.fatness = -2;\r\n\t\tent.forceshader = shaderforname(\"powerups/quad\");\r\n\t\taddentity(ent);\r\n\t}\r\n\tif (self.sveffects & SVE_GOD)\r\n\t{\r\n\t\tent.fatness = -2;\r\n\t\tent.forceshader = shaderforname(\"powerups/regen\");\r\n\t\taddentity(ent);\r\n\t}\r\n\tent.fatness = 0;\r\n\tent.forceshader = 0;\r\n};\r\n\r\n//this function is called inside your base entity's predraw function (which should then return false)\r\nnonstatic void() Anim_Draw =\r\n{\r\n\tvector tf, tr, tu;\r\n\tvector ang;\r\n\tfloat move;\r\n\tstring weaponname = 0;\r\n\r\n\tself.modelindex = 0;\t//should never have been anything else.\r\n\r\n\r\n\tif (player_local == self && !chasecam)\r\n\t{\r\n\t\t//we still add the local player entity, although only to mirrors.\r\n\t\tself.legsent.renderflags = RF_USEAXIS|RF_EXTERNALMODEL;\r\n\t\tself.torsoent.renderflags = RF_USEAXIS|RF_EXTERNALMODEL;\r\n\t\tself.headent.renderflags = RF_USEAXIS|RF_EXTERNALMODEL;\r\n\t\tself.weaponent.renderflags = RF_USEAXIS|RF_EXTERNALMODEL;\r\n\t}\r\n\telse\r\n\t{\r\n\t\tself.legsent.renderflags = RF_USEAXIS;\r\n\t\tself.torsoent.renderflags = RF_USEAXIS;\r\n\t\tself.headent.renderflags = RF_USEAXIS;\r\n\t\tself.weaponent.renderflags = RF_USEAXIS;\r\n\t}\r\n\r\n\tif (!(self.frame >= playerframe::axdeth1 && self.frame <= playerframe::deathe9))\r\n\t{\r\n\t\t//if they're still alive\r\n\t\t//animate the legs a bit so they turn to the player, but not for 1-degree turns. little more realism there.\r\n\r\n\t\tang = [0,self.angles_y,0];\r\n\t\tif (self.velocity_x||self.velocity_y)\r\n\t\t{\r\n\t\t\tmakevectors(ang);\r\n\t\t\ttf = self.velocity;\r\n\t\t\ttf_z = 0;\r\n\t\t\ttf = normalize(tf);\r\n\t\t\tif (v_forward*tf > -0.01)\r\n\t\t\t\tself.legsent.ideal_yaw = vectoyaw(tf);\r\n\t\t\telse\r\n\t\t\t\tself.legsent.ideal_yaw = vectoyaw(tf)+180;\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tmove = ang_y - self.legsent.ideal_yaw;\r\n\t\t\tif (move < -180)\r\n\t\t\t\tmove += 360;\r\n\t\t\tif (move > 180)\r\n\t\t\t\tmove -= 360;\r\n\r\n\t\t\tif (move*move > 30*30)\r\n\t\t\t\tForceToAnim(LEGS_TURN);\r\n\r\n\t\t\tif (self.legsent.animnum == LEGS_TURN || self.legsent.animnum == LEGS_JUMP)\r\n\t\t\t\tself.legsent.ideal_yaw = ang_y;\r\n\t\t}\r\n\t\tother = self;\r\n\t\tself = self.legsent;\r\n\t\tself.yaw_speed = 5;//180*frametime;\r\n\r\n\t\t//clamp the legs to never be more than 90 degrees\r\n\t\tmove = other.angles_y-self.angles_y;\r\n\t\tif (move < -180)\r\n\t\t\tmove += 360;\r\n\t\tif (move > 180)\r\n\t\t\tmove -= 360;\r\n\t\t//turn the legs\r\n\t\tif (move*move > 95*95)\t//snap\r\n\t\t{\r\n\t\t\tif (move>0)\r\n\t\t\t\tmove=20;\r\n\t\t\telse\r\n\t\t\t\tmove=-20;\r\n\t\t\tself.angles_y = other.angles_y - move;\r\n\t\t\tself = other;\r\n\t\t}\r\n\t\telse\r\n\t\t\tChangeYaw();\r\n\t\tself = other;\r\n\t}\r\n\t\r\n\r\n\tif (self.frame != self.frame2)\r\n\t{\r\n\t//see if the player changed to any animations.\r\n\t\tif (self.frame >= playerframe::axdeth1 && self.frame <= playerframe::axdeth8 && self.legsent.animnum != BOTH_DEATH1)\r\n\t\t{\r\n\t\t\tsexedsound(self, \"death1.wav\");\r\n\t\t\tForceToAnim(BOTH_DEATH1);\r\n\t\t}\r\n\t\telse if (self.frame == playerframe::axdeth9 && self.frame2 != playerframe::axdeth8)\r\n\t\t\tForceToAnim(BOTH_DEAD1);\t//suddenly appeared with this frame (otherwise we just flow into it)\r\n\r\n\t\telse if (self.frame == playerframe::deatha1 && self.frame <= playerframe::deatha10 && self.legsent.animnum != BOTH_DEATH2)\r\n\t\t{\r\n\t\t\tsexedsound(self, \"death2.wav\");\r\n\t\t\tForceToAnim(BOTH_DEATH2);\r\n\t\t}\r\n\t\telse if (self.frame == playerframe::deatha11 && self.frame2 != playerframe::deatha10)\r\n\t\t\tForceToAnim(BOTH_DEAD2);\t//suddenly appeared with this frame (otherwise we just flow into it)\r\n\r\n\t\telse if (self.frame == playerframe::deathb1 && self.frame <= playerframe::deathb8 && self.legsent.animnum != BOTH_DEATH3)\r\n\t\t{\r\n\t\t\tsexedsound(self, \"death3.wav\");\r\n\t\t\tForceToAnim(BOTH_DEATH3);\r\n\t\t}\r\n\t\telse if (self.frame == playerframe::deathb9 && self.frame2 != playerframe::deathb8)\r\n\t\t\tForceToAnim(BOTH_DEAD3);\t//suddenly appeared with this frame (otherwise we just flow into it)\r\n\r\n\t\telse if (self.frame == playerframe::deathc1 && self.frame <= playerframe::deathc14 && self.legsent.animnum != BOTH_DEATH1)\r\n\t\t{\r\n\t\t\tsexedsound(self, \"death1.wav\");\r\n\t\t\tForceToAnim(BOTH_DEATH1);\r\n\t\t}\r\n\t\telse if (self.frame == playerframe::deathc15 && self.frame2 != playerframe::deathc14)\r\n\t\t\tForceToAnim(BOTH_DEAD1);\t//suddenly appeared with this frame (otherwise we just flow into it)\r\n\r\n\t\telse if (self.frame == playerframe::deathd1 && self.frame <= playerframe::deathd8 && self.legsent.animnum != BOTH_DEATH2)\r\n\t\t{\r\n\t\t\tsexedsound(self, \"death2.wav\");\r\n\t\t\tForceToAnim(BOTH_DEATH2);\r\n\t\t}\r\n\t\telse if (self.frame == playerframe::deathd9 && self.frame2 != playerframe::deathd8)\r\n\t\t\tForceToAnim(BOTH_DEAD2);\t//suddenly appeared with this frame (otherwise we just flow into it)\r\n\r\n\t\telse if (self.frame == playerframe::deathe1 && self.frame <= playerframe::deathe8 && self.legsent.animnum != BOTH_DEATH3)\r\n\t\t{\r\n\t\t\tsexedsound(self, \"death3.wav\");\r\n\t\t\tForceToAnim(BOTH_DEATH3);\r\n\t\t}\r\n\t\telse if (self.frame == playerframe::deathe9 && self.frame2 != playerframe::deathe8)\r\n\t\t\tForceToAnim(BOTH_DEAD3);\t//suddenly appeared with this frame (otherwise we just flow into it)\r\n\r\n\t\telse if ((self.frame  == playerframe::nailatt1 || self.frame  == playerframe::nailatt2) &&\r\n\t\t\t (self.frame2 != playerframe::nailatt1 && self.frame2 != playerframe::nailatt2))\r\n\t\t\tForceToAnim(TORSO_ATTACK);\r\n\t\telse if ((self.frame  == playerframe::light1 || self.frame  == playerframe::light2) &&\r\n\t\t\t (self.frame2 != playerframe::light1 && self.frame2 != playerframe::light2))\r\n\t\t\tForceToAnim(TORSO_ATTACK);\r\n\t\telse if (self.frame == playerframe::rockatt1)\r\n\t\t\tForceToAnim(TORSO_ATTACK);\r\n\t\telse if (self.frame == playerframe::shotatt1)\r\n\t\t\tForceToAnim(TORSO_ATTACK);\r\n\t\telse if (self.frame == playerframe::axatt1)\r\n\t\t\tForceToAnim(TORSO_ATTACK2);\r\n\t\telse if (self.frame == playerframe::axattb1)\r\n\t\t\tForceToAnim(TORSO_ATTACK2);\r\n\t\telse if (self.frame == playerframe::axattc1)\r\n\t\t\tForceToAnim(TORSO_ATTACK2);\r\n\t\telse if (self.frame == playerframe::axattd1)\r\n\t\t\tForceToAnim(TORSO_ATTACK2);\r\n\t\telse if (self.frame2 >= playerframe::axdeth1 && self.frame2 <= playerframe::deathe9 && !(self.frame >= playerframe::axdeth1 && self.frame <= playerframe::deathe9))\r\n\t\t{\t//respawned\r\n\t\t\tForceToAnim(LEGS_IDLE);\r\n\t\t\tForceToAnim(TORSO_STAND);\r\n\t\t}\r\n\t\tself.frame2 = self.frame;\r\n\t}\r\n\r\n\t//add a dynamic light around the player if they have any powerups.\r\n\tang = '0 0 0';\r\n\tif (self.sveffects & SVE_QUAD)\r\n\t\tang_z = 1;\r\n\tif (self.sveffects & SVE_GOD)\r\n\t\tang_x = 1;\r\n\tif (ang != '0 0 0')\r\n\t{\r\n\t\tdynamiclight_add(self.origin, 400, ang);\r\n\t}\r\n\r\n\t//if they're meant to be transparent, propogate that.\r\n\tself.legsent.alpha = self.torsoent.alpha = self.headent.alpha = self.weaponent.alpha = self.alpha;\r\n\t//LegsUpdateAnim needs its origin early, for stff\r\n\tself.legsent.origin = self.origin;\r\n\r\n\t//update the animation lerps\r\n\tmakevectors(self.legsent.angles);\r\n\tLegsUpdateAnim();\r\n\tTorsoUpdateAnim();\r\n\r\n\t//add the legs into the scene, with their lagged angles.\r\n\tAddModelWithEffects(self.legsent);\r\n\t//legs are in place, matricies are set up for them.\r\n\r\n\t//work out where to put the torso by querying the tag\r\n\tself.torsoent.origin = rotatevectorsbytag(self.legsent, self.torsoent.tag_index);//figure out the torso position\r\n\t//torso's angles are not lagged, and must always point directly at the target, so rotate by the extra angles.\r\n\tang = self.angles;\r\n\tang_y = self.angles_y - self.legsent.angles_y;\t//keep the angle on the legs\r\n\tang_x*=2;\r\n\tif (self.legsent.animnum > BOTH_DEAD3)\r\n\t\trotatevectorsbyangle(ang);\t//rotate the torso (when dead the whole thing acts as one model with no custom angles inside)\r\n\tAddModelWithEffects(self.torsoent);\r\n\t//torso is now added to the scene.\r\n\r\n\t//save the direction we're looking in for the weapon which is added after the head.\r\n\ttf = v_forward;\r\n\ttr = v_right;\r\n\ttu = v_up;\r\n\r\n\t//now work out where to put the head\r\n\tself.headent.origin = rotatevectorsbytag(self.torsoent, self.headent.tag_index);//\r\n\tang_y = sin(time)*22.5;\r\n\tang_x = cos(time*0.4532)*11.25;\r\n\tif (self.legsent.animnum > BOTH_DEAD3)\r\n\t\trotatevectorsbyangle(ang);\t//make the head around a bit\r\n\tAddModelWithEffects(self.headent);\r\n\t//head is now in place\r\n\r\n\tif (self.frame >= playerframe::axdeth1 && self.frame <= playerframe::deathe9)\r\n\t\treturn;\t//don't show the weapon in death frames.\r\n\r\n\t//and revert the matrix back to how it was before the head.\r\n\tv_forward = tf;\r\n\tv_right = tr;\r\n\tv_up = tu;\r\n\r\n\tmove = self.sveffects&15;\r\n\tswitch (move)\r\n\t{\r\n\tcase 12:\t//axe\r\n\t\tweaponname = \"models/weapons2/gauntlet/gauntlet\";\r\n\t\tbreak;\r\n\tdefault:\r\n\tcase 0:\t//shotgun\r\n\t\tweaponname = \"models/weapons2/railgun/railgun\";\t//well... the sniper's prefered weapon, at least. :p\r\n\t\tbreak;\r\n\tcase 1:\t//super shotgun\r\n\t\tweaponname = \"models/weapons2/shotgun/shotgun\";\r\n\t\tbreak;\r\n\tcase 2:\t//nailgun\r\n\t\tweaponname = \"models/weapons2/machinegun/machinegun\";\r\n\t\tbreak;\r\n\tcase 3:\t//super nailgun\r\n\t\tweaponname = \"models/weapons2/plasma/plasma\";\r\n\t\tbreak;\r\n\tcase 4: //grenade launcher\r\n\t\tweaponname = \"models/weapons2/grenadel/grenadel\";\r\n\t\tbreak;\r\n\tcase 5: //rocket launcher\r\n\t\tweaponname = \"models/weapons2/rocketl/rocketl\";\r\n\t\tbreak;\r\n\tcase 6: //lightning gun\r\n\t\tweaponname = \"models/weapons2/lightning/lightning\";\r\n\t\tbreak;\r\n\t}\r\n\r\n\tsetmodel(self.weaponent, strcat(weaponname, \".md3\"));\r\n\r\n\t//rotate by a tag on the torso\r\n\tself.weaponent.origin = rotatevectorsbytag(self.torsoent, self.weaponent.tag_index);//place the weapon in the hand\r\n\t//and add it.\r\n\tAddModelWithEffects(self.weaponent);\r\n\r\n\t//this check is a little wrong.\r\n\tif (self.torsoent.animnum == TORSO_ATTACK || self.torsoent.animnum == TORSO_ATTACK2)\r\n\t{\r\n\t\tmove = gettagindex(self.weaponent, \"tag_flash\");\r\n\t\tif (move)\r\n\t\t{\r\n\t\t\t//they're shooting something. make a muzzleflash appear at the end of their weapon.\r\n\t\t\tself.weaponent.origin = rotatevectorsbytag(self.weaponent, move);\r\n\t\t\tsetmodel(self.weaponent, strcat(weaponname, \"_flash.md3\"));\r\n\t\t\tAddModelWithEffects(self.weaponent);\r\n\t\t}\r\n\t}\r\n};\r\n\r\n//remove our attached models, restoring the player model to being a boring player.\r\nnonstatic void() Anim_UnsetModel =\r\n{\r\n\tif (self.torsoent)\r\n\t\tremove(self.torsoent);\r\n\tif (self.headent)\r\n\t\tremove(self.headent);\r\n\tif (self.legsent)\r\n\t\tremove(self.legsent);\r\n\tif (self.weaponent)\r\n\t\tremove(self.weaponent);\r\n\r\n\tself.torsoent = world;\r\n\tself.headent = world;\r\n\tself.legsent = world;\r\n\tself.weaponent = world;\r\n\tself.modelnum = -1;\r\n\r\n\tsetmodel(self, self.model);\r\n};\r\n\r\nnonstatic float() Anim_GetGender =\r\n{\r\n\tif (self.headent)\r\n\t\treturn anim_gender[self.headent.modelnum];\r\n\treturn GENDER_DEFAULT;\r\n};\r\n\r\n//Attempts to read the animation file for the named q3 player model\r\n//-1 on failure.\r\nnonstatic float(string modname) Anim_ReadAnimationFile =\r\n{\r\n\tlocal float modnum;\r\n\tlocal string str;\r\n\tlocal float file;\r\n\tlocal float sequencenum = 0;\r\n\tlocal float stupid;\r\n\r\n\tif (modname == \"\")\r\n\t\treturn -1;\r\n\r\n\tfor (modnum = 0; modnum < MAXMODELS; modnum++)\r\n\t{\r\n\t\tif (anim_name[modnum] == modname)\r\n\t\t\treturn modnum;\t//already loaded\r\n\t\tif (anim_name[modnum] == \"\")\r\n\t\t\tbreak;\t//empty slot\r\n\t}\r\n\tif (modnum == MAXMODELS)\r\n\t{\t//list is full\r\n\t\tprint(\"Too many models\\n\");\r\n\t\treturn -1;\r\n\t}\r\n\telse\r\n\t{\r\n\t\tstr = strcat(\"models/players/\", modname, \"/animation.cfg\");\r\n\r\n\t\tfile = fopen(str, FILE_READ);\r\n\t\tif (file < 0)\r\n\t\t{\r\n\t\t\tprint(sprintf(\"fopen %S failed\\n\", str));\r\n\t\t\treturn -1;\r\n\t\t}\r\n\r\n\t\tmodname= strzone(modname);\r\n\t\tanim_name[modnum] = modname;\r\n\r\n\t\t//precache them.\r\n\t\tprecache_model(strcat(\"models/players/\", modname, \"/upper.md3\"));\r\n\t\tprecache_model(strcat(\"models/players/\", modname, \"/lower.md3\"));\r\n\t\tprecache_model(strcat(\"models/players/\", modname, \"/head.md3\"));\r\n\r\n\t\tanim_headmodel[modnum] = getmodelindex(strcat(\"models/players/\", modname, \"/head.md3\"));\r\n\r\n\t\t//default general values\r\n\t\tanim_gender[modnum] = GENDER_DEFAULT;\r\n\t\tanim_headoffset[modnum] = '0 0 0';\r\n\r\n\t\tfor (;;)\r\n\t\t{\r\n\t\t\tstr = fgets(file);\r\n\t\t\tif not (str) break;\r\n\r\n\t\t\ttokenize(str);\r\n\t\t\tstr = argv(0);\r\n\t\t\tif (str == \"\")\r\n\t\t\t\tcontinue;\r\n\t\t\telse if (str == \"sex\")\r\n\t\t\t{\r\n\t\t\t\tstr = argv(1);\r\n\t\t\t\tif (str == \"m\" || str == \"M\")\r\n\t\t\t\t\tanim_gender[modnum] = GENDER_MALE;\r\n\t\t\t\telse if (str == \"f\" || str == \"F\")\r\n\t\t\t\t\tanim_gender[modnum] = GENDER_FEMALE;\r\n\t\t\t\telse\t//n or N\r\n\t\t\t\t\tanim_gender[modnum] = GENDER_NEUTER;\r\n\t\t\t}\r\n\t\t\telse if (str == \"headoffset\")\r\n\t\t\t{\r\n\t\t\t\tvector v;\r\n\t\t\t\tv_x = stof(argv(1));\r\n\t\t\t\tv_y = stof(argv(2));\r\n\t\t\t\tv_z = stof(argv(3));\r\n\t\t\t\tanim_headoffset[modnum] = v;\r\n\t\t\t}\r\n\t\t\telse if (str == \"footsteps\")\r\n\t\t\t{\r\n\t\t\t\t//we don't play footsteps anyway\r\n\t\t\t}\r\n\t\t\telse if (str2chr(str,0) >= '0' && str2chr(str,0) <= '9')\r\n\t\t\t{\r\n\t\t\t\tif (sequencenum == NUMANIMS)\r\n\t\t\t\t\tprint(\"animation file \\\"\" \"models/players/\", modname, \"/animation.cfg\" \"\\\" contains too many animations\\n\");\r\n\t\t\t\telse\r\n\t\t\t\t{\t\r\n\t\t\t\t\tif (sequencenum == LEGS_WALKCR)\t//for some stupid reason, the leg frames start where the torso leaves\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tstupid = stof(str);\r\n\t\t\t\t\t\tstupid = stupid - anim_firstframe[(modnum * NUMANIMS) + TORSO_GESTURE];\r\n\t\t\t\t\t}\r\n\t\t\t\t\telse stupid = 0;\r\n\t\t\t\t\tanim_firstframe[(modnum * NUMANIMS) + sequencenum] = stof(str) - stupid;\r\n\t\t\t\t\tanim_numframes[(modnum * NUMANIMS) + sequencenum] = stof(argv(1));\r\n\t\t\t\t\tanim_loopingframes[(modnum * NUMANIMS) + sequencenum] = stof(argv(2));\r\n\t\t\t\t\tanum_framespersecond[(modnum * NUMANIMS) + sequencenum] = stof(argv(3));\r\n\t\t\t\t\tsequencenum++;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t\tprint(\"animation.cfg contains unrecognised token \", str, \"\\n\");\r\n\t\t}\r\n\r\n\t\tfclose(file);\r\n\r\n\t\tif (sequencenum != NUMANIMS)\r\n\t\t\tprint(\"animation.cfg is incompleate\\n\");\r\n\t}\r\n\treturn modnum;\r\n}\r\n\r\n//attempts to apply a player model/skin to the given entity.\r\n//this may load the configuration.cfg\r\n//skinname is of the form: ranger/default\r\nnonstatic float(string skinname) Anim_SetModel =\r\n{\r\n\tlocal string lowermodelname;\r\n\tlocal string uppermodelname;\r\n\tlocal string headmodelname;\r\n\r\n\tlocal string lowerskinname;\r\n\tlocal string upperskinname;\r\n\tlocal string headskinname;\r\n\r\n\tlocal float lowermodnum;\r\n\tlocal float uppermodnum;\r\n\tlocal float headmodnum;\r\n\r\n\tlocal float slashpos;\r\n\r\n\ttokenize(skinname);\r\n\tlowermodelname = argv(2);\r\n\tuppermodelname = argv(1);\r\n\theadmodelname = argv(0);\r\n\r\n\tslashpos = strstrofs(lowermodelname, \"/\");\r\n\tlowerskinname = substring(lowermodelname, slashpos+1, -1);\r\n\tlowermodelname = substring(lowermodelname, 0, slashpos);\r\n\r\n\tslashpos = strstrofs(uppermodelname, \"/\");\r\n\tupperskinname = substring(uppermodelname, slashpos+1, -1);\r\n\tuppermodelname = substring(uppermodelname, 0, slashpos);\r\n\r\n\tslashpos = strstrofs(headmodelname, \"/\");\r\n\theadskinname = substring(headmodelname, slashpos+1, -1);\r\n\theadmodelname = substring(headmodelname, 0, slashpos);\r\n\r\n\t//seeing as we support loading each part from a different player model (well, q3 does)\r\n\t//we load it three times.\r\n\tlowermodnum = Anim_ReadAnimationFile(lowermodelname);\r\n\tuppermodnum = Anim_ReadAnimationFile(uppermodelname);\r\n\theadmodnum = Anim_ReadAnimationFile(headmodelname);\r\n\r\n\t//all failed\r\n\tif (lowermodnum < 0 && uppermodnum < 0 && headmodnum < 0)\r\n\t\treturn false;\t//failed to load the animation.\r\n\r\n\t//make sure that all three parts are valid or something.\r\n\tif (headmodnum < 0)\r\n\t{\r\n\t\tif (lowermodnum < 0)\r\n\t\t{\r\n\t\t\theadmodnum = uppermodnum;\r\n\t\t\theadmodelname = uppermodelname;\r\n\t\t\theadskinname = upperskinname;\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\theadmodnum = lowermodnum;\r\n\t\t\theadmodelname = lowermodelname;\r\n\t\t\theadskinname = lowerskinname;\r\n\t\t}\r\n\t}\r\n\tif (lowermodnum < 0)\r\n\t{\r\n\t\tlowermodnum = headmodnum;\r\n\t\tlowermodelname = headmodelname;\r\n\t\tlowerskinname = headskinname;\r\n\t}\r\n\tif (uppermodnum < 0)\r\n\t{\r\n\t\tuppermodnum = headmodnum;\r\n\t\tuppermodelname = headmodelname;\r\n\t\tupperskinname = headskinname;\r\n\t}\r\n\r\n\t//spawn the attaching ents\r\n\tif (!self.torsoent)\r\n\t\tself.torsoent = spawn();\r\n\tif (!self.headent)\r\n\t\tself.headent = spawn();\r\n\tif (!self.legsent)\r\n\t\tself.legsent = spawn();\r\n\tif (!self.weaponent)\r\n\t\tself.weaponent = spawn();\r\n\r\n\t//give them the correct model\r\n\tsetmodel(self.legsent, strcat(\"models/players/\", anim_name[lowermodnum], \"/lower.md3\"));\r\n\tsetmodel(self.torsoent, strcat(\"models/players/\", anim_name[uppermodnum], \"/upper.md3\"));\r\n\tsetmodel(self.headent, strcat(\"models/players/\", anim_name[headmodnum], \"/head.md3\"));\r\n\r\n//\tprecache_model(AXEMODELNAME);\r\n//\tprecache_model(WEAPONMODELNAME);\r\n\r\n\r\n\tself.legsent.owner = self;\r\n\tself.headent.owner = self;\r\n\tself.torsoent.owner = self;\r\n\tself.weaponent.owner = self;\r\n\r\n\tself.drawmask = MASK_NORMAL;\t//general view.\r\n\r\n\t//find the tags so we don't do it every single frame\r\n\tself.torsoent.tag_index = gettagindex(self.legsent, \"tag_torso\");\r\n\tself.headent.tag_index = gettagindex(self.torsoent, \"tag_head\");\r\n\tself.weaponent.tag_index = gettagindex(self.torsoent, \"tag_weapon\");\r\n\r\n\tself.modelnum = headmodnum;\r\n\tself.legsent.modelnum = lowermodnum;\r\n\tself.headent.modelnum = headmodnum;\r\n\tself.torsoent.modelnum = uppermodnum;\r\n\tself.weaponent.modelnum = lowermodnum;\r\n\r\n\tself.legsent.colormap = self.colormap;\r\n\tself.headent.colormap = self.colormap;\r\n\tself.torsoent.colormap = self.colormap;\r\n\tself.weaponent.colormap = self.colormap;\r\n\r\n\tif (!lowerskinname)\r\n\t\tlowerskinname = \"default\";\r\n\tif (!upperskinname)\r\n\t\tupperskinname = \"default\";\r\n\tif (!headskinname)\r\n\t\theadskinname = \"default\";\r\n\r\n\t//find which skin number to use for the given skin file\r\n\tif (stof(lowerskinname))\r\n\t\tself.legsent.skin = stof(lowerskinname);\r\n\telse\r\n\t\tself.legsent.skin = skinforname(self.legsent.modelindex, strcat(\"models/players/\", anim_name[lowermodnum], \"/lower_\", lowerskinname, \".skin\"));\r\n\tif (stof(upperskinname))\r\n\t\tself.torsoent.skin = stof(upperskinname);\r\n\telse\r\n\t\tself.torsoent.skin = skinforname(self.torsoent.modelindex, strcat(\"models/players/\", anim_name[uppermodnum], \"/upper_\", upperskinname, \".skin\"));\r\n\tif (stof(headskinname))\r\n\t\tself.headent.skin = stof(headskinname);\r\n\telse\r\n\t\tself.headent.skin = skinforname(self.headent.modelindex, strcat(\"models/players/\", anim_name[headmodnum], \"/head_\", headskinname, \".skin\"));\r\n\r\n\t//setup the initial sequences.\r\n\tForceToAnim(LEGS_IDLE);\r\n\tForceToAnim(TORSO_STAND);\r\n\r\n\t//so it takes effect fully and immediatly... well, the first time its drawn, anyway.\r\n\tself.legsent.framechangetime = -50;\r\n\tself.torsoent.framechangetime = -50;\r\n\tself.legsent.frame2 = -1;\r\n\tself.torsoent.frame2 = -1;\r\n\r\n\tself.frame2 = -1;\r\n\tself.lerpfrac = 0;\r\n\r\n\treturn true;\r\n};\r\n\r\nentity(entity src) CloneModel =\r\n{\r\n\tlocal entity dest;\r\n\tdest = spawn();\r\n\r\n\tdest.modelnum = src.modelnum;\r\n\tdest.animnum = src.animnum;\r\n\tdest.colormap = src.colormap;\r\n\tdest.tag_index = src.tag_index;\r\n\tdest.skin = src.skin;\r\n\tdest.predraw = src.predraw;\r\n\tdest.frame = src.frame;\r\n\tdest.frame2 = src.frame2;\r\n\tdest.model = src.model;\r\n\tdest.modelindex = src.modelindex;\r\n\tdest.drawmask = src.drawmask;\r\n\tdest.origin = src.origin;\r\n\tdest.angles = src.angles;\r\n\tdest.mins = src.mins;\r\n\tdest.maxs = src.maxs;\r\n\t\r\n\treturn dest;\r\n};\r\n\r\nnonstatic entity() Anim_DupModel =\r\n{\r\n\tlocal entity o, n;\r\n\to = self;\r\n\tn = self = CloneModel(o);\r\n\r\n\tself.legsent = CloneModel(o.legsent);\r\n\tself.headent = CloneModel(o.headent);\r\n\tself.torsoent = CloneModel(o.torsoent);\r\n\tself.weaponent = CloneModel(o.weaponent);\r\n\tself.velocity = o.velocity;\r\n\r\n\r\n\tself = o;\r\n\treturn n;\r\n};\r\n\r\nnonstatic float(string skinname) Anim_GetHeadModelIndex =\r\n{\r\n\tfloat slashpos;\r\n\tstring modelname;\r\n\tfloat modnum;\r\n\r\n\ttokenize(skinname);\r\n\tmodelname = argv(0);\r\n\tif (modelname == \"\")\r\n\t\treturn 0;\t//an invalid modelindex.\r\n\r\n\tslashpos = strstrofs(modelname, \"/\");\r\n\tmodelname = substring(modelname, 0, slashpos);\r\n\r\n\t//seeing as we support loading each part from a different player model (well, q3 does)\r\n\t//we load it three times.\r\n\tmodnum = Anim_ReadAnimationFile(modelname);\r\n\tif (modnum < 0)\r\n\t\treturn 0;\r\n\r\n\treturn anim_headmodel[modnum];\r\n};\r\n\r\nnonstatic float(string skinname) Anim_GetHeadSkinNumber =\r\n{\r\n\tfloat slashpos;\r\n\tstring modelname;\r\n\tfloat modnum;\r\n\r\n\ttokenize(skinname);\r\n\tmodelname = argv(0);\r\n\tif (modelname == \"\")\r\n\t\treturn 0;\t//0 = default\r\n\r\n\tslashpos = strstrofs(modelname, \"/\");\r\n\tskinname = substring(modelname, slashpos+1, -1);\r\n\tmodelname = substring(modelname, 0, slashpos);\r\n\r\n\tif (stof(skinname))\r\n\t\treturn stof(skinname);\r\n\r\n\t//seeing as we support loading each part from a different player model (well, q3 does)\r\n\t//we load it three times.\r\n\tmodnum = Anim_ReadAnimationFile(modelname);\r\n\tif (modnum < 0)\r\n\t\treturn 0;\r\n\r\n\tif (!skinname)\r\n\t\tskinname = \"default\";\r\n\r\n\treturn skinforname(anim_headmodel[modnum], strcat(\"models/players/\", modelname, \"/head_\", skinname, \".skin\"));\r\n};\r\n\r\nnonstatic vector(string skinname) Anim_GetHeadOffset =\r\n{\r\n\tfloat slashpos;\r\n\tstring modelname;\r\n\tfloat modnum;\r\n\r\n\ttokenize(skinname);\r\n\tmodelname = argv(0);\r\n\tif (modelname == \"\")\r\n\t\treturn '0 0 0';\t//an invalid modelindex.\r\n\r\n\tslashpos = strstrofs(modelname, \"/\");\r\n\tmodelname = substring(modelname, 0, slashpos);\r\n\r\n\t//seeing as we support loading each part from a different player model (well, q3 does)\r\n\t//we load it three times.\r\n\tmodnum = Anim_ReadAnimationFile(modelname);\r\n\tif (modnum < 0)\r\n\t\treturn '0 0 0';\r\n\treturn anim_headoffset[modnum];\r\n}\r\n\r\n\r\n\r\nstatic float(float channel, string soundname, vector pos, float vol, float attenuation, float flags) ServerSoundStartRequest =\r\n{\t//the server started a sound on an entity that the csqc has control over.\r\n\tif (!self.headent)\r\n\t{\r\n\t\treturn false;\r\n\t}\r\n\r\n\tif (soundname == \"player/plyrjmp8.wav\")\r\n\t{\r\n\t\tif (self == player_local)\r\n\t\t\tif (CVARF(cg_noselfjumpsound))\r\n\t\t\t\treturn true;\t//option to disable jump noise for local player.\r\n\t\tsexedsound(self, \"jump1.wav\");\r\n\t\treturn true;\r\n\t}\r\n\tif (soundname == \"player/gasp1.wav\")\r\n\t{\r\n\t\tsexedsound(self, \"gasp.wav\");\r\n\t\treturn true;\r\n\t}\r\n\tif (soundname == \"player/gasp2.wav\")\r\n\t{\r\n\t\tsexedsound(self, \"gasp.wav\");\r\n\t\treturn true;\r\n\t}\r\n\tif (soundname == \"player/land.wav\")\r\n\t{\r\n\t\tsexedsound(self, \"fall1.wav\");\r\n\t\treturn true;\r\n\t}\r\n\tif (soundname == \"player/land2.wav\")\r\n\t{\r\n\t\tsexedsound(self, \"fall1.wav\");\r\n\t\treturn true;\r\n\t}\r\n\r\n\tif (soundname == \"player/pain1.wav\")\r\n\t{\r\n\t\tsexedsound(self, \"pain25_1.wav\");\r\n\t\treturn true;\r\n\t}\r\n\tif (soundname == \"player/pain2.wav\")\r\n\t{\r\n\t\tsexedsound(self, \"pain50_1.wav\");\r\n\t\treturn true;\r\n\t}\r\n\tif (soundname == \"player/pain3.wav\")\r\n\t{\r\n\t\tsexedsound(self, \"pain75_1.wav\");\r\n\t\treturn true;\r\n\t}\r\n\tif (soundname == \"player/pain4.wav\")\r\n\t{\r\n\t\tsexedsound(self, \"pain100_1.wav\");\r\n\t\treturn true;\r\n\t}\r\n\tif (soundname == \"player/pain5.wav\")\r\n\t{\r\n\t\tsexedsound(self, \"pain75_1.wav\");\r\n\t\treturn true;\r\n\t}\r\n\tif (soundname == \"player/pain6.wav\")\r\n\t{\r\n\t\tsexedsound(self, \"pain100_1.wav\");\r\n\t\treturn true;\r\n\t}\r\n\r\n\tif (soundname == \"player/h2odeath.wav\")\r\n\t{\r\n\t\tsexedsound(self, \"drown.wav\");\r\n\t\treturn true;\r\n\t}\r\n\tif (soundname == \"player/death1.wav\")\t//normal deaths come from the player animations.\r\n\t\treturn true;\r\n\tif (soundname == \"player/death2.wav\")\r\n\t\treturn true;\r\n\tif (soundname == \"player/death3.wav\")\r\n\t\treturn true;\r\n\tif (soundname == \"player/death4.wav\")\r\n\t\treturn true;\r\n\tif (soundname == \"player/death5.wav\")\r\n\t\treturn true;\r\n\r\n\treturn false;\r\n};\r\n\r\n//version that breaks when the ent is not necessarily known to us, called only when 'self' is a known CSQC entity.\r\nfloat(float channel, string soundname, vector org, float vol, float attenuation, float flags) CSQC_ServerSound =\r\n{\r\n\treturn ServerSoundStartRequest(channel, soundname, org, vol, attenuation, flags);\r\n}\r\n//version that lets the qc handle any ent fixups.\r\nfloat(float sventnum, float channel, string soundname, float vol, float att, vector org, float pitchmod, float flags) CSQC_Event_Sound =\r\n{\r\n\tif (autocvar(developer, 0))\r\n\t\tprint(sprintf(\"CSQC_Event_Sound(%S): chan:%g\\n\", soundname, channel));\r\n\r\n\tself = findfloat(world, entnum, sventnum);\r\n\tif (self)\r\n\t\treturn ServerSoundStartRequest(channel, soundname, org, vol, att, flags);\r\n\treturn false;\r\n};\r\n#endif\r\n"
  },
  {
    "path": "quakec/csqctest/src/cs/q4player.qc",
    "content": "enum\r\n{\r\nANIM_FORWARD,\r\nANIM_BACK,\r\nANIM_LEFT,\r\nANIM_RIGHT,\r\nANIM_IDLE,\r\nANIM_MAX\r\n};\r\n\r\nfloat anim[ANIM_MAX];\r\n\r\n.vector lastorg;\r\n\r\n.float aweight[ANIM_MAX];\r\n.float atime[ANIM_MAX];\r\n\r\n.float rangle;\r\n\r\n/*pick an angle for the legs to run in such that the movement is in 0, 90, 180, -90 angles from movement direction. attempt to clamp past a full circle*/\r\nfloat() anglequadrant =\r\n{\r\n\tfloat ideal, diff;\r\n\r\n\tif (!self.velocity_x && !self.velocity_y)\r\n\t\tideal = self.angles_y;\r\n\telse\r\n\t\tideal = vectoyaw(self.velocity);\r\n\tdiff = ideal - self.angles_y;\r\n\tif (diff > 180)\r\n\t\tdiff -= 360;\r\n\tif (diff < -180)\r\n\t\tdiff += 360;\r\n\r\n\tif (diff < -180)\r\n\t\treturn self.angles_y + 180;\r\n\telse if (diff > 180)\r\n\t\treturn self.angles_y - 180;\r\n\telse if (diff < -150)\r\n\t\treturn ideal + 180;\r\n\telse if (diff > 150)\r\n\t\treturn ideal - 180;\r\n\telse if (diff > 75)\r\n\t\treturn ideal - 90;\r\n\telse if (diff < -75)\r\n\t\treturn ideal + 90;\r\n\telse\r\n\t\treturn ideal;\r\n};\r\n\r\nvoid(float ideal, float speed) mychangeyaw =\r\n{\r\n\tfloat move;\r\n\tmove = ideal - self.angles_y;\r\n\tspeed *= frametime;\r\n\tif (move > 180)\r\n\t\tmove -= 360;\r\n\tif (move < -180)\r\n\t\tmove += 360;\r\n\tif (move >= 0)\r\n\t{\r\n\t\tif (move > speed)\r\n\t\t\tmove = speed;\r\n\t}\r\n\telse\r\n\t{\r\n\t\tif (move < -speed)\r\n\t\t\tmove = -speed;\r\n\t}\r\n\tself.angles_y = self.angles_y + move;\r\n};\r\n\r\nvoid() Q4PM_Draw =\r\n{\r\n\tfloat fw, rt;\r\n\tfloat tweight;\r\n\tfloat i;\r\n\tfloat ideal;\r\n\tvector aimang;\r\n\r\n\tvector move = self.origin - self.lastorg;\r\n\tself.lastorg = self.origin;\r\n\r\n\tif (!self.skeletonindex)\r\n\t{\r\n\t\tself.skeletonindex = skel_create(self.modelindex, FALSE);\r\n\t\tif (!self.skeletonindex)\r\n\t\t\treturn;\r\n\t}\r\n\r\n\tideal = anglequadrant();\r\n\taimang = [0, self.angles_y, self.angles_x*-3];\r\n\r\n\tself.angles = [0, self.rangle, 0];\r\n\tmychangeyaw(ideal, 360);\r\n\tself.rangle = self.angles_y;\r\n\r\n\tmakevectors(self.angles);\r\n\t\r\n\tfw = (move * v_forward)/(320);\r\n\trt = (move * v_right)/(320);\r\n\r\n\taimang_y -= self.angles_y;\r\n\taimang_x = 0;\r\n\tif (aimang_y > 180)\r\n\t\taimang_y -= 360;\r\n\tif (aimang_y < -180)\r\n\t\taimang_y += 360;\r\n\tmakevectors(aimang*0.333);\r\n\r\n\tif (fw*fw > 0)\r\n\t{\r\n\t\tif (fw > 0)\r\n\t\t\tself.aweight[ANIM_FORWARD] += fw*32;\r\n\t\telse\r\n\t\t\tself.aweight[ANIM_BACK] -= fw*32;\r\n\t}\r\n\tif (fw > 0)\r\n\t\tself.atime[ANIM_FORWARD] += fw;\r\n\telse\r\n\t\tself.atime[ANIM_BACK] -= fw;\r\n\tif (rt*rt > 0)\r\n\t{\r\n\t\tif (rt > 0)\r\n\t\t\tself.aweight[ANIM_RIGHT] += rt*32;\r\n\t\telse\r\n\t\t\tself.aweight[ANIM_LEFT] -= rt*32;\r\n\t}\r\n\tif (rt > 0)\r\n\t\tself.atime[ANIM_RIGHT] += rt;\r\n\telse\r\n\t\tself.atime[ANIM_LEFT] -= rt;\r\n\tif (move_x == 0 && move_y == 0)\r\n\t\tself.aweight[ANIM_IDLE] += frametime*4;\r\n\tself.atime[ANIM_IDLE] += frametime;\r\n\r\n\ttweight = self.aweight[ANIM_FORWARD]+self.aweight[ANIM_BACK]+self.aweight[ANIM_LEFT]+self.aweight[ANIM_RIGHT]+self.aweight[ANIM_IDLE];\r\n\tif (tweight > 1)\r\n\t{\r\n\t\ttweight = 1/tweight;\r\n\t\tself.aweight[ANIM_FORWARD] *= tweight;\r\n\t\tself.aweight[ANIM_BACK] *= tweight;\r\n\t\tself.aweight[ANIM_LEFT] *= tweight;\r\n\t\tself.aweight[ANIM_RIGHT] *= tweight;\r\n\t\tself.aweight[ANIM_IDLE] *= tweight;\r\n\t}\r\n\telse\r\n\t{\r\n\t\tself.aweight[ANIM_IDLE] += 1-tweight;\r\n\t\tself.atime[ANIM_IDLE] += frametime;\r\n\t}\r\n\r\n\tself.origin_z -= 24;//self.mins_z;\r\n\tsetmodel(self, self.model);\r\n\tfor (i = 0; i < ANIM_MAX; i++)\r\n\t{\r\n\t\tself.frame = 0;\r\n\t\tself.frame2 = 0;\r\n\t\tself.lerpfrac = 0;\r\n\t\tself.frame1time = self.atime[i];\r\n\t\tself.frame2time = self.atime[i];\r\n\t\tskel_build(self.skeletonindex, self, anim[i], ((i==0)?0:1), 0, -1, self.aweight[i]);\r\n\t}\r\n\r\n\tskel_premul_bone(self.skeletonindex, 18, '0 0 0', v_forward, v_right, v_up);\r\n\tskel_premul_bone(self.skeletonindex, 19, '0 0 0', v_forward, v_right, v_up);\r\n\tskel_premul_bone(self.skeletonindex, 20, '0 0 0', v_forward, v_right, v_up);\r\n\r\n\t//obliterate the bone movement, so that it doesn't move forwards.\r\n\tskel_get_bonerel(self.skeletonindex, 1);\r\n\tskel_set_bone(self.skeletonindex, 1, '0 0 0', v_forward*(64/72), v_right*(64/72), v_up*(64/72));\r\n\r\n\tself.renderflags = 0;\r\n\taddentity(self);\r\n\r\n\tself.origin = self.lastorg;\r\n};\r\n\r\nfloat(string skinname) Q4PM_SetModel =\r\n{\r\n\tstring mname = strcat(\"models/characters/player/\", skinname, \".md5mesh\");\r\n\r\n\t//check to see if it exists\r\n\tif not (whichpack(mname))\r\n\t{\r\nprint(sprintf(\"File %s doesn't exist\\n\", mname));\r\n\t\treturn false;\r\n\t}\r\n\r\n\tanim[ANIM_IDLE]    = getmodelindex(\"models/characters/player/idle.md5anim\");\r\n\tanim[ANIM_FORWARD] = getmodelindex(\"models/characters/player/run.md5anim\");\r\n\tanim[ANIM_BACK]    = getmodelindex(\"models/characters/player/run_backwards.md5anim\");\r\n\tanim[ANIM_RIGHT]   = getmodelindex(\"models/characters/player/strafe_right.md5anim\");\r\n\tanim[ANIM_LEFT]    = getmodelindex(\"models/characters/player/strafe_left.md5anim\");\r\n\r\n\tsetmodel(self, mname);\r\n\r\n\tself.rangle = self.angles_y;\r\n\r\n\treturn true;\r\n};\r\n\r\nvoid() Q4PM_UnsetModel =\r\n{\r\n\tif (self.skeletonindex)\r\n\t\tskel_delete(self.skeletonindex);\r\n\tself.skeletonindex = 0;\r\n};\r\n\r\nentity() Q4PM_DupModel\r\n{\r\n\treturn spawn();\r\n};\r\n"
  },
  {
    "path": "quakec/csqctest/src/cs/tempent.qc",
    "content": "float() CSQC_Parse_TempEntity =\n{\n\tvector org;\n\tfloat te = readbyte();\nprint(sprintf(\"CSQC_Parse_TempEntity(%g)\\n\", te));\n\tif (te == 130)\n\t{\n\t\torg_x = readcoord();\n\t\torg_y = readcoord();\n\t\torg_z = readcoord();\n\t\tte_gunshot(org, 1);\n\t}\n\telse\n\t\treturn FALSE;\n\treturn TRUE;\n};\n"
  },
  {
    "path": "quakec/csqctest/src/cs/test.qc",
    "content": ""
  },
  {
    "path": "quakec/csqctest/src/cs/weapons.qc",
    "content": "\r\n#define crandom() (random()*2 - 1)\r\n.float starttime;\r\n.vector source;\r\nvoid() SUB_Remove =\r\n{\r\n\tremove(self);\r\n};\r\n\r\n//Moves the entity to where it should be\r\nfloat() RocketProject =\r\n{\r\n\tlocal vector oldorg;\r\n\toldorg = self.origin;\r\n\tself.origin = self.source + self.velocity * (time - self.starttime);\r\n\r\n\t//Do the trail thing, if possible.\r\n\ttrailparticles(particleeffectnum(\"tr_rocket\"), self, self.origin, oldorg);\r\n\r\n\tdynamiclight_add(self.origin, 400, '0 1 0');\r\n\treturn PREDRAW_AUTOADD;\r\n};\r\n\r\nnonstatic void(float isnew) ParseRocketClass =\r\n{\r\n\tself.starttime = time;\r\n\tself.source_x = readcoord();\r\n\tself.source_y = readcoord();\r\n\tself.source_z = readcoord();\r\n\tself.angles_x = (readshort()*360)/65535;\r\n\tself.angles_y = (readshort()*360)/65535;\r\n\tself.angles_z = random()*360;\r\n\r\n\tself.angles_x*=autocvar(r_meshpitch,-1);\r\n\tmakevectors(self.angles);\r\n\tself.angles_x*=autocvar(r_meshpitch,-1);\r\n\tself.velocity = v_forward*1000;\r\n\tself.origin = self.source;\r\n\r\n\tsetmodel(self, \"progs/missile.mdl\");\r\n\tself.drawmask = MASK_NORMAL;\t//general view.\r\n\tself.predraw = RocketProject;\r\n};\r\n\r\nnonstatic float() NailProject =\r\n{\r\n\tlocal vector oldorg;\r\n\toldorg = self.origin;\r\n\tself.origin = self.source + self.velocity * (time - self.starttime);\r\n\r\n\t//Do the trail thing, if possible.\r\n\ttrailparticles(particleeffectnum(\"tr_nail\"), self, self.origin, oldorg);\r\n\r\n//\tadddynamiclight(self.origin, 100, '1 0 0');\r\n\treturn PREDRAW_AUTOADD;\r\n};\r\n\r\nnonstatic void(float isnew) ParseNailClass =\r\n{\r\n\tlocal float speed;\r\n\tself.starttime = time;\r\n\tself.source_x = readcoord();\r\n\tself.source_y = readcoord();\r\n\tself.source_z = readcoord();\r\n\tself.modelindex = readbyte();\r\n\tspeed = readshort();\r\n\tself.angles_x = (readshort()*360)/65535;\r\n\tself.angles_y = (readshort()*360)/65535;\r\n\tself.angles_z = 0;\t//don't rotate nails.\r\n\r\n\tself.angles_x*=autocvar(r_meshpitch,-1);\r\n\tmakevectors(self.angles);\r\n\tself.angles_x*=autocvar(r_meshpitch,-1);\r\n\tself.velocity = v_forward*speed;\r\n\tself.origin = self.source;\r\n\r\n\tsetmodelindex(self, self.modelindex);\r\n\tself.drawmask = MASK_NORMAL;\t//general view.\r\n\tself.predraw = NailProject;\r\n};\r\n\r\nvector(float dm) VelocityForDamage =\r\n{\r\n\tlocal vector v;\r\n\r\n\tv_x = 100 * crandom();\r\n\tv_y = 100 * crandom();\r\n\tv_z = 200 + 100 * random();\r\n\r\n\tdm*=-1;\r\n\r\n\tif (dm > -50)\r\n\t{\r\n//\t\tprint (\"level 1\\n\");\r\n\t\tv = v * 0.7;\r\n\t}\r\n\telse if (dm > -200)\r\n\t{\r\n//\t\tprint (\"level 2\\n\");\r\n\t\tv = v * 2;\r\n\t}\r\n\telse\r\n\t{\r\n//\t\tprint (\"level 3\\n\");\r\n\t\tv = v * 10;\r\n\t}\r\n\r\n\treturn v;\r\n};\r\n\r\nfloat() GibProject =\r\n{\r\n\tlocal float td;\r\n\tlocal vector oldorg;\r\n\toldorg = self.origin;\r\n\r\n\ttd = time - self.starttime;\r\n\tif (self.nextthink < time)\r\n\t{\r\n\t\tif (self.alpha <= td)\r\n\t\t{\r\n\t\t\tremove(self);\r\n\t\t\treturn PREDRAW_NEXT;\r\n\t\t}\r\n\t\tself.alpha -= td;\r\n\t}\r\n\r\n\tMovetype_Bounce();\r\n\ttrailparticles(particleeffectnum(\"tr_blood\"), self, self.origin, oldorg);\r\n\treturn PREDRAW_AUTOADD;\r\n};\r\n\r\nvoid(string gibname, float dm, vector org) ThrowGib =\r\n{\r\n\tlocal\tentity new;\r\n\r\n\tnew = spawn();\r\n\tnew.origin = org;\r\n\tsetmodel (new, gibname);\r\n\tsetsize (new, '0 0 0', '0 0 0');\r\n\tnew.velocity = VelocityForDamage (dm);\r\n\tnew.movetype = MOVETYPE_BOUNCE;\r\n\tnew.solid = SOLID_NOT;\r\n\tnew.avelocity_x = random()*600;\r\n\tnew.avelocity_y = random()*600;\r\n\tnew.avelocity_z = random()*600;\r\n\tnew.think = SUB_Remove;\r\n\tnew.nextthink = time + 10 + random()*10;\r\n\tnew.frame = 0;\r\n\tnew.flags = 0;\r\n\r\n\tnew.alpha = 1;\r\n\tnew.drawmask = MASK_NORMAL;\r\n\r\n\tnew.predraw = GibProject;\r\n\tnew.starttime = time;\r\n};\r\n\r\nvoid TossGibs(vector org, float type, float dm)\r\n{\r\n\tlocal float giblevel;\r\n\r\n\tswitch(type)\r\n\t{\r\n\tcase GIB_ZOMBIE:\r\n\t\tsound (self, CHAN_VOICE, \"zombie/z_gib.wav\", 1, ATTN_NORM);\r\n\t\tbreak;\r\n\tdefault:\r\n\t\tsound (self, CHAN_VOICE, \"player/udeath.wav\", 1, ATTN_NORM);\r\n\t\tbreak;\r\n\t}\r\n\r\n\tgiblevel = cvar(\"cg_giblevel\");\r\n\tif (giblevel > 20)\r\n\t\tgiblevel = 20;\r\n\r\n\twhile(giblevel > 0)\r\n\t{\r\n\t\tgiblevel--;\r\n\t\tswitch(type)\r\n\t\t{\r\n\t\tcase GIB_PLAYER:\r\n\t\t\tThrowGib(\"progs/h_player.mdl\", dm, org);\r\n\t\t\tThrowGib(\"progs/gib1.mdl\", dm, org);\r\n\t\t\tThrowGib(\"progs/gib2.mdl\", dm, org);\r\n\t\t\tThrowGib(\"progs/gib3.mdl\", dm, org);\r\n\t\t\tbreak;\r\n\t\tcase GIB_SOLDIER:\t//CHUNKY KIBBLES!!!\r\n\t\t\tThrowGib(\"progs/h_guard.mdl\", dm, org);\r\n\t\t\tThrowGib(\"progs/gib1.mdl\", dm, org);\r\n\t\t\tThrowGib(\"progs/gib2.mdl\", dm, org);\r\n\t\t\tThrowGib(\"progs/gib3.mdl\", dm, org);\r\n\t\t\tbreak;\r\n\t\tcase GIB_DOG:\r\n\t\t\tThrowGib(\"progs/h_guard.mdl\", dm, org);\r\n\t\t\tThrowGib(\"progs/gib3.mdl\", dm, org);\r\n\t\t\tThrowGib(\"progs/gib3.mdl\", dm, org);\r\n\t\t\tThrowGib(\"progs/gib3.mdl\", dm, org);\r\n\t\t\tbreak;\r\n\t\tcase GIB_DEMON:\r\n\t\t\tThrowGib(\"progs/h_demon.mdl\", dm, org);\r\n\t\t\tThrowGib(\"progs/gib1.mdl\", dm, org);\r\n\t\t\tThrowGib(\"progs/gib2.mdl\", dm, org);\r\n\t\t\tThrowGib(\"progs/gib3.mdl\", dm, org);\r\n\t\t\tbreak;\r\n\t\tcase GIB_OGRE:\r\n\t\t\tThrowGib(\"progs/h_ogre.mdl\", dm, org);\r\n\t\t\tThrowGib(\"progs/gib3.mdl\", dm, org);\r\n\t\t\tThrowGib(\"progs/gib3.mdl\", dm, org);\r\n\t\t\tThrowGib(\"progs/gib3.mdl\", dm, org);\r\n\t\t\tbreak;\r\n\t\tcase GIB_WIZARD:\r\n\t\t\tThrowGib(\"progs/h_wizard.mdl\", dm, org);\r\n\t\t\tThrowGib(\"progs/gib2.mdl\", dm, org);\r\n\t\t\tThrowGib(\"progs/gib2.mdl\", dm, org);\r\n\t\t\tThrowGib(\"progs/gib2.mdl\", dm, org);\r\n\t\t\tbreak;\r\n\t\tcase GIB_SHAMBLER:\r\n\t\t\tThrowGib(\"progs/h_shams.mdl\", dm, org);\r\n\t\t\tThrowGib(\"progs/gib1.mdl\", dm, org);\r\n\t\t\tThrowGib(\"progs/gib2.mdl\", dm, org);\r\n\t\t\tThrowGib(\"progs/gib3.mdl\", dm, org);\r\n\t\t\tbreak;\r\n\t\tcase GIB_ZOMBIE:\r\n\t\t\tThrowGib(\"progs/h_zombie.mdl\", dm, org);\r\n\t\t\tThrowGib(\"progs/gib1.mdl\", dm, org);\r\n\t\t\tThrowGib(\"progs/gib2.mdl\", dm, org);\r\n\t\t\tThrowGib(\"progs/gib3.mdl\", dm, org);\r\n\t\t\tbreak;\r\n\t\tcase GIB_KNIGHT:\r\n\t\t\tThrowGib(\"progs/h_knight.mdl\", dm, org);\r\n\t\t\tThrowGib(\"progs/gib1.mdl\", dm, org);\r\n\t\t\tThrowGib(\"progs/gib2.mdl\", dm, org);\r\n\t\t\tThrowGib(\"progs/gib3.mdl\", dm, org);\r\n\t\t\tbreak;\r\n\r\n\t\tcase GIB_HELLKNIGHT:\r\n\t\t\tThrowGib(\"progs/h_hellkn.mdl\", dm, org);\r\n\t\t\tThrowGib(\"progs/gib1.mdl\", dm, org);\r\n\t\t\tThrowGib(\"progs/gib2.mdl\", dm, org);\r\n\t\t\tThrowGib(\"progs/gib3.mdl\", dm, org);\r\n\t\t\tbreak;\r\n\t\tcase GIB_FISH:\r\n\t\t\tThrowGib(\"progs/gib1.mdl\", dm, org);\r\n\t\t\tThrowGib(\"progs/gib2.mdl\", dm, org);\r\n\t\t\tThrowGib(\"progs/gib3.mdl\", dm, org);\r\n\t\t\tbreak;\r\n\t\tcase GIB_ENFORCER:\r\n\t\t\tThrowGib(\"progs/h_mega.mdl\", dm, org);\r\n\t\t\tThrowGib(\"progs/gib1.mdl\", dm, org);\r\n\t\t\tThrowGib(\"progs/gib2.mdl\", dm, org);\r\n\t\t\tThrowGib(\"progs/gib3.mdl\", dm, org);\r\n\t\t\tbreak;\r\n\t\tcase GIB_SHALRATH:\r\n\t\t\tThrowGib(\"progs/h_shal.mdl\", dm, org);\r\n\t\t\tThrowGib(\"progs/gib1.mdl\", dm, org);\r\n\t\t\tThrowGib(\"progs/gib2.mdl\", dm, org);\r\n\t\t\tThrowGib(\"progs/gib3.mdl\", dm, org);\r\n\t\t\tbreak;\r\n\t\tdefault:\r\n\t\t\tThrowGib(\"progs/gib1.mdl\", dm, org);\r\n\t\t\tThrowGib(\"progs/gib2.mdl\", dm, org);\r\n\t\t\tThrowGib(\"progs/gib3.mdl\", dm, org);\r\n\t\t\tbreak;\r\n\t\t}\r\n\t}\r\n};\r\n\r\nnonstatic void(float isnew) ParseGibbing =\r\n{\r\n\tfloat type;\r\n\tvector org;\r\n\tfloat dm;\r\n\r\n\ttype = readbyte();\r\n\tdm = readbyte();\r\n\torg_x = readcoord();\r\n\torg_y = readcoord();\r\n\torg_z = readcoord();\r\n\r\n\tif (!isnew)\t//doesn't count if it's not new.\r\n\t\treturn;\r\n\r\n\tself.origin = org;\r\n\tTossGibs(org, type, dm);\r\n}\r\n\r\nnonstatic void(float isnew) ParseExplosion =\r\n{\r\n\tlocal vector org;\r\n\tlocal entity e;\r\n\torg_x = readcoord();\r\n\torg_y = readcoord();\r\n\torg_z = readcoord();\r\n\tself.origin = org;\r\n\tif (isnew)\r\n\t{\r\n\t\tsound (self, CHAN_WEAPON, \"weapons/r_exp3.wav\", 1, ATTN_NORM);\r\n#ifdef WORKINDP\r\n\t\tpointparticles(particleeffectnum(\"te_explosion\"), org, '0 0 0', 1);\r\n#else\r\n\t\tpointparticles(particleeffectnum(\"te_explosion\"), org);\r\n#endif\r\n\r\n\t\te = world;//findradius(org, 120);\r\n\t\twhile (e)\r\n\t\t{\r\n\t\t\t//a bit of fun - allow the local client to gib csqc bodies\r\n\t\t\tif (e.gibbable)\r\n\t\t\t{\r\n\t\t\t\tTossGibs(e.origin+'0 0 16', e.gibbable, 20);\r\n\t\t\t\te.gibbable = GIB_BAD;\r\n\t\t\t\te.removetime = 0;\r\n\t\t\t}\r\n\t\t\te = e.chain;\r\n\t\t}\r\n\t}\r\n\r\n\tself.drawmask = 0;\t//don't draw it any more.\r\n};\r\n"
  },
  {
    "path": "quakec/csqctest/src/csprogs.src",
    "content": "../csprogs.dat\r\n\r\n#define CSQC\r\noptsall.qc\r\n#if defined(QSS) || defined(DP)\r\ncsqc_api.qc\r\n#else\r\nfteextensions.qc\r\n#endif\r\ncs/defs.qc\r\n\r\ncommon/classes.qc\r\n\r\ncommon/econstants.qc\r\ncs/constants.qc\r\ncs/playerframes.inc\r\n\r\ncommon/pmove.qc\r\n\r\ncommon/makeallstatic.qc\r\n\r\ncs/editor_lights.qc\r\ncs/editor_terrain.qc\r\n\r\ncs/prediction.qc\r\ncs/q3playerm.qc\r\ncs/q4player.qc\r\ncs/hlpm.qc\r\ncs/player.qc\r\ncs/hud.qc\r\n\r\ncs/movetypes.qc\r\n\r\ncs/map.qc\r\ncs/menu.qc\r\ncs/fun/tetris.qc\r\ncs/fun/osgk.qc\r\n\r\ncs/weapons.qc\r\ncs/tempent.qc\r\n\r\ncommon/classes.qc\r\n\r\ncs/test.qc\r\n\r\ncs/fun/skinchooser.qc\r\ncs/entrypoints.qc\r\n"
  },
  {
    "path": "quakec/csqctest/src/optsall.qc",
    "content": "//which engines do we want to support?\r\n#define FTE\r\n//#define QSS\r\n//#define DP\r\n\r\n//#define OWNPLAYERPHYSICS\t//run our own prediction code, instead of the engine-supplied default\r\n\r\n#define AUTOCVAR\r\n\r\n#define MD3PMODELS\t//support Q3 segmented player models\r\n#define HLPMODELS\t\t//support HalfLife skeletal models\r\n#define POWERUP_SHELLS\t//show shells around players for powerups\r\n#define Q4PMODELS\r\n//#define NOEXTENSIONS\r\n\r\n#ifdef NOEXTENSIONS\r\n#undef HLPMODELS\r\n#undef MD3PMODELS\r\n#endif\r\n\r\n#ifdef DP\r\n\t#define WORKINDP\t//do various workarounds for DP. don't use any opcode extensions\r\n#elifdef QSS\r\n//\t#pragma TARGET QSS\t//FTE also supports this.\r\n\t#define NOT_DP\t\t//mute deprecation warnings about DP, we don't care.\r\n#else\r\n\t#pragma TARGET FTE\t//neither of the other engines are targetted, go crazy with it.\r\n\t#define NOT_QSS\t\t//mute deprecation warnings about QSS, we don't care.\r\n\t#define NOT_DP\t\t//mute deprecation warnings about DP, we don't care.\r\n#endif\r\n\r\n//just mute engine-dependancy deprecation warnings for now.\r\n#define NOT_QSS\r\n#define NOT_DP\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n#ifdef AUTOCVAR\r\n#define DEFCVAR_FLOAT(n,d) var float autocvar_##n = d;\r\n#define DEFCVAR_STRING(n,d) var string autocvar_##n = d;\r\n#define CVARF(n) autocvar_##n\r\n#define CVARS(n) autocvar_##n\r\n#else\r\n#define DEFCVAR_FLOAT(n,d)\r\n#define DEFCVAR_STRING(n,d)\r\n#define CVARF(n) cvar(#n)\r\n#define CVARS(n) cvar_string(#n)\r\n#endif\r\n"
  },
  {
    "path": "quakec/csqctest/src/optsmenu.qc",
    "content": "#define MENU"
  },
  {
    "path": "quakec/csqctest/src/progs.src",
    "content": "#pragma sourcefile csprogs.src\r\n#pragma sourcefile ssqc.src\r\n"
  },
  {
    "path": "quakec/csqctest/src/ss/ai.qc",
    "content": "void() movetarget_f;\r\nvoid() t_movetarget;\r\nvoid() knight_walk1;\r\n//void() knight_bow6;\r\n//void() knight_bow1;\r\nvoid(entity etemp, entity stemp, entity stemp, float dmg) T_Damage;\r\n/*\r\n\r\n.enemy\r\nWill be world if not currently angry at anyone.\r\n\r\n.movetarget\r\nThe next path spot to walk toward.  If .enemy, ignore .movetarget.\r\nWhen an enemy is killed, the monster will try to return to it's path.\r\n\r\n.huntt_ime\r\nSet to time + something when the player is in sight, but movement straight for\r\nhim is blocked.  This causes the monster to use wall following code for\r\nmovement direction instead of sighting on the player.\r\n\r\n.ideal_yaw\r\nA yaw angle of the intended direction, which will be turned towards at up\r\nto 45 deg / state.  If the enemy is in view and hunt_time is not active,\r\nthis will be the exact line towards the enemy.\r\n\r\n.pausetime\r\nA monster will leave it's stand state and head towards it's .movetarget when\r\ntime > .pausetime.\r\n\r\nwalkmove(angle, speed) primitive is all or nothing\r\n*/\r\n\r\n\r\n//\r\n// globals\r\n//\r\n\r\n//\r\n// when a monster becomes angry at a player, that monster will be used\r\n// as the sight target the next frame so that monsters near that one\r\n// will wake up even if they wouldn't have noticed the player\r\n//\r\nentity\tsight_entity;\r\nfloat\tsight_entity_time;\r\n\r\n/*float(float v) anglemod =\r\n{\r\n\twhile (v >= 360)\r\n\t\tv = v - 360;\r\n\twhile (v < 0)\r\n\t\tv = v + 360;\r\n\treturn v;\r\n};*/\r\n\r\n/*\r\n==============================================================================\r\n\r\nMOVETARGET CODE\r\n\r\nThe angle of the movetarget effects standing and bowing direction, but has no effect on movement, which allways heads to the next target.\r\n\r\ntargetname\r\nmust be present.  The name of this movetarget.\r\n\r\ntarget\r\nthe next spot to move to.  If not present, stop here for good.\r\n\r\npausetime\r\nThe number of seconds to spend standing or bowing for path_stand or path_bow\r\n\r\n==============================================================================\r\n*/\r\n\r\n\r\nvoid() movetarget_f =\r\n{\r\n\tif (!self.targetname)\r\n\t\tobjerror (\"monster_movetarget: no targetname\");\r\n\t\t\r\n\tself.solid = SOLID_TRIGGER;\r\n\tself.touch = t_movetarget;\r\n\tsetsize (self, '-8 -8 -8', '8 8 8');\r\n\t\r\n};\r\n\r\n/*QUAKED path_corner (0.5 0.3 0) (-8 -8 -8) (8 8 8)\r\nMonsters will continue walking towards the next target corner.\r\n*/\r\nvoid() path_corner =\r\n{\r\n\tmovetarget_f ();\r\n};\r\n\r\n\r\n/*\r\n=============\r\nt_movetarget\r\n\r\nSomething has bumped into a movetarget.  If it is a monster\r\nmoving towards it, change the next destination and continue.\r\n==============\r\n*/\r\nvoid() t_movetarget =\r\n{\r\nlocal entity\ttemp;\r\n\r\n\tif (other.movetarget != self)\r\n\t\treturn;\r\n\t\r\n\tif (other.enemy)\r\n\t\treturn;\t\t// fighting, not following a path\r\n\r\n\ttemp = self;\r\n\tself = other;\r\n\tother = temp;\r\n\r\n\tif (self.classname == \"monster_ogre\")\r\n\t\tsound (self, CHAN_VOICE, \"ogre/ogdrag.wav\", 1, ATTN_IDLE);// play chainsaw drag sound\r\n\r\n//dprint (\"t_movetarget\\n\");\r\n\tself.goalentity = self.movetarget = find (world, targetname, other.target);\r\n\tself.ideal_yaw = vectoyaw(self.goalentity.origin - self.origin);\r\n\tif (!self.movetarget)\r\n\t{\r\n\t\tself.pausetime = time + 999999;\r\n\t\tself.th_stand ();\r\n\t\treturn;\r\n\t}\r\n};\r\n\r\n\r\n\r\n//============================================================================\r\n\r\n/*\r\n=============\r\nrange\r\n\r\nreturns the range catagorization of an entity reletive to self\r\n0\tmelee range, will become hostile even if back is turned\r\n1\tvisibility and infront, or visibility and show hostile\r\n2\tinfront and show hostile\r\n3\tonly triggered by damage\r\n=============\r\n*/\r\nfloat(entity targ) range =\r\n{\r\nlocal vector\tspot1, spot2;\r\nlocal float\t\tr;\t\r\n\tspot1 = self.origin + self.view_ofs;\r\n\tspot2 = targ.origin + targ.view_ofs;\r\n\t\r\n\tr = vlen (spot1 - spot2);\r\n\tif (r < 120)\r\n\t\treturn RANGE_MELEE;\r\n\tif (r < 500)\r\n\t\treturn RANGE_NEAR;\r\n\tif (r < 1000)\r\n\t\treturn RANGE_MID;\r\n\treturn RANGE_FAR;\r\n};\r\n\r\n/*\r\n=============\r\nvisible\r\n\r\nreturns 1 if the entity is visible to self, even if not infront ()\r\n=============\r\n*/\r\nfloat (entity targ) visible =\r\n{\r\n\tlocal vector\tspot1, spot2;\r\n\t\r\n\tspot1 = self.origin + self.view_ofs;\r\n\tspot2 = targ.origin + targ.view_ofs;\r\n\ttraceline (spot1, spot2, TRUE, self);\t// see through other monsters\r\n\t\r\n\tif (trace_inopen && trace_inwater)\r\n\t\treturn FALSE;\t\t\t// sight line crossed contents\r\n\r\n\tif (trace_fraction == 1)\r\n\t\treturn TRUE;\r\n\treturn FALSE;\r\n};\r\n\r\n\r\n/*\r\n=============\r\ninfront\r\n\r\nreturns 1 if the entity is in front (in sight) of self\r\n=============\r\n*/\r\nfloat(entity targ) infront =\r\n{\r\n\tlocal vector\tvec;\r\n\tlocal float\t\tdot;\r\n\t\r\n\tmakevectors (self.angles);\r\n\tvec = normalize (targ.origin - self.origin);\r\n\tdot = vec * v_forward;\r\n\t\r\n\tif ( dot > 0.3)\r\n\t{\r\n\t\treturn TRUE;\r\n\t}\r\n\treturn FALSE;\r\n};\r\n\r\n\r\n//============================================================================\r\n\r\n/*\r\n===========\r\nChangeYaw\r\n\r\nTurns towards self.ideal_yaw at self.yaw_speed\r\nSets the global variable current_yaw\r\nCalled every 0.1 sec by monsters\r\n============\r\n*/\r\n/*\r\n\r\nvoid() ChangeYaw =\r\n{\r\n\tlocal float\t\tideal, move;\r\n\r\n//current_yaw = self.ideal_yaw;\r\n// mod down the current angle\r\n\tcurrent_yaw = anglemod( self.angles_y );\r\n\tideal = self.ideal_yaw;\r\n\t\r\n\tif (current_yaw == ideal)\r\n\t\treturn;\r\n\t\r\n\tmove = ideal - current_yaw;\r\n\tif (ideal > current_yaw)\r\n\t{\r\n\t\tif (move > 180)\r\n\t\t\tmove = move - 360;\r\n\t}\r\n\telse\r\n\t{\r\n\t\tif (move < -180)\r\n\t\t\tmove = move + 360;\r\n\t}\r\n\t\t\r\n\tif (move > 0)\r\n\t{\r\n\t\tif (move > self.yaw_speed)\r\n\t\t\tmove = self.yaw_speed;\r\n\t}\r\n\telse\r\n\t{\r\n\t\tif (move < 0-self.yaw_speed )\r\n\t\t\tmove = 0-self.yaw_speed;\r\n\t}\r\n\r\n\tcurrent_yaw = anglemod (current_yaw + move);\r\n\r\n\tself.angles_y = current_yaw;\r\n};\r\n\r\n*/\r\n\r\n\r\n//============================================================================\r\n\r\nvoid() HuntTarget =\r\n{\r\n\tself.goalentity = self.enemy;\r\n\tself.think = self.th_run;\r\n\tself.ideal_yaw = vectoyaw(self.enemy.origin - self.origin);\r\n\tself.nextthink = time + 0.1;\r\n\tSUB_AttackFinished (1);\t// wait a while before first attack\r\n};\r\n\r\nvoid() SightSound =\r\n{\r\nlocal float\trsnd;\r\n\r\n\tif (self.classname == \"monster_ogre\")\t\r\n\t\tsound (self, CHAN_VOICE, \"ogre/ogwake.wav\", 1, ATTN_NORM);\r\n\telse if (self.classname == \"monster_knight\")\r\n\t\tsound (self, CHAN_VOICE, \"knight/ksight.wav\", 1, ATTN_NORM);\r\n\telse if (self.classname == \"monster_shambler\")\r\n\t\tsound (self, CHAN_VOICE, \"shambler/ssight.wav\", 1, ATTN_NORM);\r\n\telse if (self.classname == \"monster_demon1\")\r\n\t\tsound (self, CHAN_VOICE, \"demon/sight2.wav\", 1, ATTN_NORM);\r\n\telse if (self.classname == \"monster_wizard\")\r\n\t\tsound (self, CHAN_VOICE, \"wizard/wsight.wav\", 1, ATTN_NORM);\r\n\telse if (self.classname == \"monster_zombie\")\r\n\t\tsound (self, CHAN_VOICE, \"zombie/z_idle.wav\", 1, ATTN_NORM);\r\n\telse if (self.classname == \"monster_dog\")\r\n\t\tsound (self, CHAN_VOICE, \"dog/dsight.wav\", 1, ATTN_NORM);\r\n\telse if (self.classname == \"monster_hell_knight\")\r\n\t\tsound (self, CHAN_VOICE, \"hknight/sight1.wav\", 1, ATTN_NORM);\r\n\telse if (self.classname == \"monster_tarbaby\")\r\n\t\tsound (self, CHAN_VOICE, \"blob/sight1.wav\", 1, ATTN_NORM);\r\n\telse if (self.classname == \"monster_enforcer\")\r\n\t{\r\n\t\trsnd = rint(random() * 3);\t\t\t\r\n\t\tif (rsnd == 1)\r\n\t\t\tsound (self, CHAN_VOICE, \"enforcer/sight1.wav\", 1, ATTN_NORM);\r\n\t\telse if (rsnd == 2)\r\n\t\t\tsound (self, CHAN_VOICE, \"enforcer/sight2.wav\", 1, ATTN_NORM);\r\n\t\telse if (rsnd == 0)\r\n\t\t\tsound (self, CHAN_VOICE, \"enforcer/sight3.wav\", 1, ATTN_NORM);\r\n\t\telse\r\n\t\t\tsound (self, CHAN_VOICE, \"enforcer/sight4.wav\", 1, ATTN_NORM);\r\n\t}\r\n\telse if (self.classname == \"monster_army\")\r\n\t\tsound (self, CHAN_VOICE, \"soldier/sight1.wav\", 1, ATTN_NORM);\r\n\telse if (self.classname == \"monster_shalrath\")\r\n\t\tsound (self, CHAN_VOICE, \"shalrath/sight.wav\", 1, ATTN_NORM);\r\n};\r\n\r\nvoid() FoundTarget =\r\n{\r\n\tif (self.enemy.classname == \"player\")\r\n\t{\t// let other monsters see this monster for a while\r\n\t\tsight_entity = self;\r\n\t\tsight_entity_time = time;\r\n\t}\r\n\t\r\n\tself.show_hostile = time + 1;\t\t// wake up other monsters\r\n\r\n\tSightSound ();\r\n\tHuntTarget ();\r\n};\r\n\r\n/*\r\n===========\r\nFindTarget\r\n\r\nSelf is currently not attacking anything, so try to find a target\r\n\r\nReturns TRUE if an enemy was sighted\r\n\r\nWhen a player fires a missile, the point of impact becomes a fakeplayer so\r\nthat monsters that see the impact will respond as if they had seen the\r\nplayer.\r\n\r\nTo avoid spending too much time, only a single client (or fakeclient) is\r\nchecked each frame.  This means multi player games will have slightly\r\nslower noticing monsters.\r\n============\r\n*/\r\nfloat() FindTarget =\r\n{\r\n\tlocal entity\tclient;\r\n\tlocal float\t\tr;\r\n\r\n// if the first spawnflag bit is set, the monster will only wake up on\r\n// really seeing the player, not another monster getting angry\r\n\r\n// spawnflags & 3 is a big hack, because zombie crucified used the first\r\n// spawn flag prior to the ambush flag, and I forgot about it, so the second\r\n// spawn flag works as well\r\n\tif (sight_entity_time >= time - 0.1 && !(self.spawnflags & 3) )\r\n\t{\r\n\t\tclient = sight_entity;\r\n\t\tif (client.enemy == self.enemy)\r\n\t\t\treturn FALSE;\r\n\t}\r\n\telse\r\n\t{\r\n\t\tclient = checkclient ();\r\n\t\tif (!client)\r\n\t\t\treturn FALSE;\t// current check entity isn't in PVS\r\n\t}\r\n\r\n\tif (client == self.enemy)\r\n\t\treturn FALSE;\r\n\r\n\tif (client.flags & FL_NOTARGET)\r\n\t\treturn FALSE;\r\n\tif (client.items & IT_INVISIBILITY)\r\n\t\treturn FALSE;\r\n\r\n\tr = range (client);\r\n\tif (r == RANGE_FAR)\r\n\t\treturn FALSE;\r\n\t\t\r\n\tif (!visible (client))\r\n\t\treturn FALSE;\r\n\r\n\tif (r == RANGE_NEAR)\r\n\t{\r\n\t\tif (client.show_hostile < time && !infront (client))\r\n\t\t\treturn FALSE;\r\n\t}\r\n\telse if (r == RANGE_MID)\r\n\t{\r\n\t\tif ( /* client.show_hostile < time || */ !infront (client))\r\n\t\t\treturn FALSE;\r\n\t}\r\n\t\r\n//\r\n// got one\r\n//\r\n\tself.enemy = client;\r\n\tif (self.enemy.classname != \"player\")\r\n\t{\r\n\t\tself.enemy = self.enemy.enemy;\r\n\t\tif (self.enemy.classname != \"player\")\r\n\t\t{\r\n\t\t\tself.enemy = world;\r\n\t\t\treturn FALSE;\r\n\t\t}\r\n\t}\r\n\t\r\n\tFoundTarget ();\r\n\r\n\treturn TRUE;\r\n};\r\n\r\n\r\n//=============================================================================\r\n\r\nvoid(float dist) ai_forward =\r\n{\r\n\twalkmove (self.angles_y, dist);\r\n};\r\n\r\nvoid(float dist) ai_back =\r\n{\r\n\twalkmove ( (self.angles_y+180), dist);\r\n};\r\n\r\n\r\n/*\r\n=============\r\nai_pain\r\n\r\nstagger back a bit\r\n=============\r\n*/\r\nvoid(float dist) ai_pain =\r\n{\r\n\tself.SendFlags = FULLSEND;\t//if nothing else happened, we animated.\r\n\r\n\tai_back (dist);\r\n/*\r\n\tlocal float\taway;\r\n\t\r\n\taway = anglemod (vectoyaw (self.origin - self.enemy.origin) \r\n\t+ 180*(random()- 0.5) );\r\n\t\r\n\twalkmove (away, dist);\r\n*/\r\n};\r\n\r\n/*\r\n=============\r\nai_painforward\r\n\r\nstagger back a bit\r\n=============\r\n*/\r\nvoid(float dist) ai_painforward =\r\n{\r\n\tself.SendFlags = FULLSEND;\t//if nothing else happened, we animated.\r\n\r\n\twalkmove (self.ideal_yaw, dist);\r\n};\r\n\r\n/*\r\n=============\r\nai_walk\r\n\r\nThe monster is walking it's beat\r\n=============\r\n*/\r\nvoid(float dist) ai_walk =\r\n{\t\r\n\tself.SendFlags = FULLSEND;\t//if nothing else happened, we animated.\r\n\r\n\tmovedist = dist;\r\n\t\r\n\tif (self.classname == \"monster_dragon\")\r\n\t{\r\n\t\tmovetogoal (dist);\r\n\t\treturn;\r\n\t}\r\n\t// check for noticing a player\r\n\tif (FindTarget ())\r\n\t\treturn;\r\n\r\n\tmovetogoal (dist);\r\n};\r\n\r\n\r\n/*\r\n=============\r\nai_stand\r\n\r\nThe monster is staying in one place for a while, with slight angle turns\r\n=============\r\n*/\r\nvoid() ai_stand =\r\n{\r\n\tself.SendFlags = FULLSEND;\t//if nothing else happened, we animated.\r\n\r\n\tif (FindTarget ())\r\n\t\treturn;\r\n\t\r\n\tif (time > self.pausetime)\r\n\t{\r\n\t\tself.th_walk ();\r\n\t\treturn;\r\n\t}\r\n\t\r\n// change angle slightly\r\n\r\n};\r\n\r\n/*\r\n=============\r\nai_turn\r\n\r\ndon't move, but turn towards ideal_yaw\r\n=============\r\n*/\r\nvoid() ai_turn =\r\n{\r\n\tif (FindTarget ())\r\n\t\treturn;\r\n\t\r\n\tChangeYaw ();\r\n};\r\n\r\n//=============================================================================\r\n\r\n/*\r\n=============\r\nChooseTurn\r\n=============\r\n*/\r\nvoid(vector dest3) ChooseTurn =\r\n{\r\n\tlocal vector\tdir, newdir;\r\n\t\r\n\tdir = self.origin - dest3;\r\n\r\n\tnewdir_x = trace_plane_normal_y;\r\n\tnewdir_y = 0 - trace_plane_normal_x;\r\n\tnewdir_z = 0;\r\n\t\r\n\tif (dir * newdir > 0)\r\n\t{\r\n\t\tdir_x = 0 - trace_plane_normal_y;\r\n\t\tdir_y = trace_plane_normal_x;\r\n\t}\r\n\telse\r\n\t{\r\n\t\tdir_x = trace_plane_normal_y;\r\n\t\tdir_y = 0 - trace_plane_normal_x;\r\n\t}\r\n\r\n\tdir_z = 0;\r\n\tself.ideal_yaw = vectoyaw(dir);\t\r\n};\r\n\r\n/*\r\n============\r\nFacingIdeal\r\n\r\n============\r\n*/\r\nfloat() FacingIdeal =\r\n{\r\n\tlocal\tfloat\tdelta;\r\n\t\r\n\tdelta = anglemod(self.angles_y - self.ideal_yaw);\r\n\tif (delta > 45 && delta < 315)\r\n\t\treturn FALSE;\r\n\treturn TRUE;\r\n};\r\n\r\n\r\n//=============================================================================\r\n\r\nfloat()\tWizardCheckAttack;\r\nfloat()\tDogCheckAttack;\r\n\r\nfloat() CheckAnyAttack =\r\n{\r\n\tif (!enemy_vis)\r\n\t\treturn FALSE;\r\n\tif (self.classname == \"monster_army\")\r\n\t\treturn SoldierCheckAttack ();\r\n\tif (self.classname == \"monster_ogre\")\r\n\t\treturn OgreCheckAttack ();\r\n\tif (self.classname == \"monster_shambler\")\r\n\t\treturn ShamCheckAttack ();\r\n\tif (self.classname == \"monster_demon1\")\r\n\t\treturn DemonCheckAttack ();\r\n\tif (self.classname == \"monster_dog\")\r\n\t\treturn DogCheckAttack ();\r\n\tif (self.classname == \"monster_wizard\")\r\n\t\treturn WizardCheckAttack ();\r\n\treturn CheckAttack ();\r\n};\r\n\r\n\r\n/*\r\n=============\r\nai_run_melee\r\n\r\nTurn and close until within an angle to launch a melee attack\r\n=============\r\n*/\r\nvoid() ai_run_melee =\r\n{\r\n\tself.ideal_yaw = enemy_yaw;\r\n\tChangeYaw ();\r\n\r\n\tif (FacingIdeal())\r\n\t{\r\n\t\tself.th_melee ();\r\n\t\tself.attack_state = AS_STRAIGHT;\r\n\t}\r\n};\r\n\r\n\r\n/*\r\n=============\r\nai_run_missile\r\n\r\nTurn in place until within an angle to launch a missile attack\r\n=============\r\n*/\r\nvoid() ai_run_missile =\r\n{\r\n\tself.ideal_yaw = enemy_yaw;\r\n\tChangeYaw ();\r\n\tif (FacingIdeal())\r\n\t{\r\n\t\tself.th_missile ();\r\n\t\tself.attack_state = AS_STRAIGHT;\r\n\t}\r\n};\r\n\r\n\r\n/*\r\n=============\r\nai_run_slide\r\n\r\nStrafe sideways, but stay at aproximately the same range\r\n=============\r\n*/\r\nvoid() ai_run_slide =\r\n{\r\n\tlocal float\tofs;\r\n\t\r\n\tself.ideal_yaw = enemy_yaw;\r\n\tChangeYaw ();\r\n\tif (self.lefty)\r\n\t\tofs = 90;\r\n\telse\r\n\t\tofs = -90;\r\n\t\r\n\tif (walkmove (self.ideal_yaw + ofs, movedist))\r\n\t\treturn;\r\n\t\t\r\n\tself.lefty = 1 - self.lefty;\r\n\t\r\n\twalkmove (self.ideal_yaw - ofs, movedist);\r\n};\r\n\r\n\r\n/*\r\n=============\r\nai_run\r\n\r\nThe monster has an enemy it is trying to kill\r\n=============\r\n*/\r\nvoid(float dist) ai_run =\r\n{\r\n\tself.SendFlags = FULLSEND;\t//if nothing else happened, we animated.\r\n\r\n\tmovedist = dist;\r\n// see if the enemy is dead\r\n\tif (self.enemy.health <= 0)\r\n\t{\r\n\t\tself.enemy = world;\r\n\t// FIXME: look all around for other targets\r\n\t\tif (self.oldenemy.health > 0)\r\n\t\t{\r\n\t\t\tself.enemy = self.oldenemy;\r\n\t\t\tHuntTarget ();\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tif (self.movetarget)\r\n\t\t\t\tself.th_walk ();\r\n\t\t\telse\r\n\t\t\t\tself.th_stand ();\r\n\t\t\treturn;\r\n\t\t}\r\n\t}\r\n\r\n\tself.show_hostile = time + 1;\t\t// wake up other monsters\r\n\r\n// check knowledge of enemy\r\n\tenemy_vis = visible(self.enemy);\r\n\tif (enemy_vis)\r\n\t\tself.search_time = time + 5;\r\n\r\n// look for other coop players\r\n\tif (coop && self.search_time < time)\r\n\t{\r\n\t\tif (FindTarget ())\r\n\t\t\treturn;\r\n\t}\r\n\r\n\tenemy_infront = infront(self.enemy);\r\n\tenemy_range = range(self.enemy);\r\n\tenemy_yaw = vectoyaw(self.enemy.origin - self.origin);\r\n\t\r\n\tif (self.attack_state == AS_MISSILE)\r\n\t{\r\n//dprint (\"ai_run_missile\\n\");\r\n\t\tai_run_missile ();\r\n\t\treturn;\r\n\t}\r\n\tif (self.attack_state == AS_MELEE)\r\n\t{\r\n//dprint (\"ai_run_melee\\n\");\r\n\t\tai_run_melee ();\r\n\t\treturn;\r\n\t}\r\n\r\n\tif (CheckAnyAttack ())\r\n\t\treturn;\t\t\t\t\t// beginning an attack\r\n\t\t\r\n\tif (self.attack_state == AS_SLIDING)\r\n\t{\r\n\t\tai_run_slide ();\r\n\t\treturn;\r\n\t}\r\n\t\t\r\n// head straight in\r\n\tmovetogoal (dist);\t\t// done in C code...\r\n};\r\n\r\n"
  },
  {
    "path": "quakec/csqctest/src/ss/boss.qc",
    "content": "/*\n==============================================================================\n\nBOSS-ONE\n\n==============================================================================\n*/\n$cd id1/models/boss1\n$origin 0 0 -15\n$base base\n$skin skin\n$scale 5\n\n$frame rise1 rise2 rise3 rise4 rise5 rise6 rise7 rise8 rise9 rise10\n$frame rise11 rise12 rise13 rise14 rise15 rise16 rise17 \n\n$frame walk1 walk2 walk3 walk4 walk5 walk6 walk7 walk8\n$frame walk9 walk10 walk11 walk12 walk13 walk14 walk15\n$frame walk16 walk17 walk18 walk19 walk20 walk21 walk22\n$frame walk23 walk24 walk25 walk26 walk27 walk28 walk29 walk30 walk31\n\n$frame death1 death2 death3 death4 death5 death6 death7 death8 death9\n\n$frame attack1 attack2 attack3 attack4 attack5 attack6 attack7 attack8\n$frame attack9 attack10 attack11 attack12 attack13 attack14 attack15\n$frame attack16 attack17 attack18 attack19 attack20 attack21 attack22\n$frame attack23\n\n$frame shocka1 shocka2 shocka3 shocka4 shocka5 shocka6 shocka7 shocka8\n$frame shocka9 shocka10 \n\n$frame shockb1 shockb2 shockb3 shockb4 shockb5 shockb6\n\n$frame shockc1 shockc2 shockc3 shockc4 shockc5 shockc6 shockc7 shockc8 \n$frame shockc9 shockc10\n\n\nvoid(vector p) boss_missile;\n\nvoid() boss_face =\n{\n\t\n// go for another player if multi player\n\tif (self.enemy.health <= 0 || random() < 0.02)\n\t{\n\t\tself.enemy = find(self.enemy, classname, \"player\");\n\t\tif (!self.enemy)\n\t\t\tself.enemy = find(self.enemy, classname, \"player\");\n\t}\n\tai_face();\n};\n\nvoid() boss_rise1\t=[\t$rise1, boss_rise2 ] {\nsound (self, CHAN_WEAPON, \"boss1/out1.wav\", 1, ATTN_NORM);\n};\nvoid() boss_rise2\t=[\t$rise2, boss_rise3 ] {\nsound (self, CHAN_VOICE, \"boss1/sight1.wav\", 1, ATTN_NORM);\n};\nvoid() boss_rise3\t=[\t$rise3, boss_rise4 ] {};\nvoid() boss_rise4\t=[\t$rise4, boss_rise5 ] {};\nvoid() boss_rise5\t=[\t$rise5, boss_rise6 ] {};\nvoid() boss_rise6\t=[\t$rise6, boss_rise7 ] {};\nvoid() boss_rise7\t=[\t$rise7, boss_rise8 ] {};\nvoid() boss_rise8\t=[\t$rise8, boss_rise9 ] {};\nvoid() boss_rise9\t=[\t$rise9, boss_rise10 ] {};\nvoid() boss_rise10\t=[\t$rise10, boss_rise11 ] {};\nvoid() boss_rise11\t=[\t$rise11, boss_rise12 ] {};\nvoid() boss_rise12\t=[\t$rise12, boss_rise13 ] {};\nvoid() boss_rise13\t=[\t$rise13, boss_rise14 ] {};\nvoid() boss_rise14\t=[\t$rise14, boss_rise15 ] {};\nvoid() boss_rise15\t=[\t$rise15, boss_rise16 ] {};\nvoid() boss_rise16\t=[\t$rise16, boss_rise17 ] {};\nvoid() boss_rise17\t=[\t$rise17, boss_missile1 ] {};\n\nvoid() boss_idle1\t=[\t$walk1, boss_idle2 ]\n{\n// look for other players\n};\nvoid() boss_idle2\t=[\t$walk2, boss_idle3 ] {boss_face();};\nvoid() boss_idle3\t=[\t$walk3, boss_idle4 ] {boss_face();};\nvoid() boss_idle4\t=[\t$walk4, boss_idle5 ] {boss_face();};\nvoid() boss_idle5\t=[\t$walk5, boss_idle6 ] {boss_face();};\nvoid() boss_idle6\t=[\t$walk6, boss_idle7 ] {boss_face();};\nvoid() boss_idle7\t=[\t$walk7, boss_idle8 ] {boss_face();};\nvoid() boss_idle8\t=[\t$walk8, boss_idle9 ] {boss_face();};\nvoid() boss_idle9\t=[\t$walk9, boss_idle10 ] {boss_face();};\nvoid() boss_idle10\t=[\t$walk10, boss_idle11 ] {boss_face();};\nvoid() boss_idle11\t=[\t$walk11, boss_idle12 ] {boss_face();};\nvoid() boss_idle12\t=[\t$walk12, boss_idle13 ] {boss_face();};\nvoid() boss_idle13\t=[\t$walk13, boss_idle14 ] {boss_face();};\nvoid() boss_idle14\t=[\t$walk14, boss_idle15 ] {boss_face();};\nvoid() boss_idle15\t=[\t$walk15, boss_idle16 ] {boss_face();};\nvoid() boss_idle16\t=[\t$walk16, boss_idle17 ] {boss_face();};\nvoid() boss_idle17\t=[\t$walk17, boss_idle18 ] {boss_face();};\nvoid() boss_idle18\t=[\t$walk18, boss_idle19 ] {boss_face();};\nvoid() boss_idle19\t=[\t$walk19, boss_idle20 ] {boss_face();};\nvoid() boss_idle20\t=[\t$walk20, boss_idle21 ] {boss_face();};\nvoid() boss_idle21\t=[\t$walk21, boss_idle22 ] {boss_face();};\nvoid() boss_idle22\t=[\t$walk22, boss_idle23 ] {boss_face();};\nvoid() boss_idle23\t=[\t$walk23, boss_idle24 ] {boss_face();};\nvoid() boss_idle24\t=[\t$walk24, boss_idle25 ] {boss_face();};\nvoid() boss_idle25\t=[\t$walk25, boss_idle26 ] {boss_face();};\nvoid() boss_idle26\t=[\t$walk26, boss_idle27 ] {boss_face();};\nvoid() boss_idle27\t=[\t$walk27, boss_idle28 ] {boss_face();};\nvoid() boss_idle28\t=[\t$walk28, boss_idle29 ] {boss_face();};\nvoid() boss_idle29\t=[\t$walk29, boss_idle30 ] {boss_face();};\nvoid() boss_idle30\t=[\t$walk30, boss_idle31 ] {boss_face();};\nvoid() boss_idle31\t=[\t$walk31, boss_idle1 ] {boss_face();};\n\nvoid() boss_missile1\t=[\t$attack1, boss_missile2 ] {boss_face();};\nvoid() boss_missile2\t=[\t$attack2, boss_missile3 ] {boss_face();};\nvoid() boss_missile3\t=[\t$attack3, boss_missile4 ] {boss_face();};\nvoid() boss_missile4\t=[\t$attack4, boss_missile5 ] {boss_face();};\nvoid() boss_missile5\t=[\t$attack5, boss_missile6 ] {boss_face();};\nvoid() boss_missile6\t=[\t$attack6, boss_missile7 ] {boss_face();};\nvoid() boss_missile7\t=[\t$attack7, boss_missile8 ] {boss_face();};\nvoid() boss_missile8\t=[\t$attack8, boss_missile9 ] {boss_face();};\nvoid() boss_missile9\t=[\t$attack9, boss_missile10 ] {boss_missile('100 100 200');};\nvoid() boss_missile10\t=[\t$attack10, boss_missile11 ] {boss_face();};\nvoid() boss_missile11\t=[\t$attack11, boss_missile12 ] {boss_face();};\nvoid() boss_missile12\t=[\t$attack12, boss_missile13 ] {boss_face();};\nvoid() boss_missile13\t=[\t$attack13, boss_missile14 ] {boss_face();};\nvoid() boss_missile14\t=[\t$attack14, boss_missile15 ] {boss_face();};\nvoid() boss_missile15\t=[\t$attack15, boss_missile16 ] {boss_face();};\nvoid() boss_missile16\t=[\t$attack16, boss_missile17 ] {boss_face();};\nvoid() boss_missile17\t=[\t$attack17, boss_missile18 ] {boss_face();};\nvoid() boss_missile18\t=[\t$attack18, boss_missile19 ] {boss_face();};\nvoid() boss_missile19\t=[\t$attack19, boss_missile20 ] {boss_face();};\nvoid() boss_missile20\t=[\t$attack20, boss_missile21 ] {boss_missile('100 -100 200');};\nvoid() boss_missile21\t=[\t$attack21, boss_missile22 ] {boss_face();};\nvoid() boss_missile22\t=[\t$attack22, boss_missile23 ] {boss_face();};\nvoid() boss_missile23\t=[\t$attack23, boss_missile1 ] {boss_face();};\n\nvoid() boss_shocka1 =[\t$shocka1, boss_shocka2 ] {};\nvoid() boss_shocka2 =[\t$shocka2, boss_shocka3 ] {};\nvoid() boss_shocka3 =[\t$shocka3, boss_shocka4 ] {};\nvoid() boss_shocka4 =[\t$shocka4, boss_shocka5 ] {};\nvoid() boss_shocka5 =[\t$shocka5, boss_shocka6 ] {};\nvoid() boss_shocka6 =[\t$shocka6, boss_shocka7 ] {};\nvoid() boss_shocka7 =[\t$shocka7, boss_shocka8 ] {};\nvoid() boss_shocka8 =[\t$shocka8, boss_shocka9 ] {};\nvoid() boss_shocka9 =[\t$shocka9, boss_shocka10 ] {};\nvoid() boss_shocka10 =[\t$shocka10, boss_missile1 ] {};\n\nvoid() boss_shockb1 =[\t$shockb1, boss_shockb2 ] {};\nvoid() boss_shockb2 =[\t$shockb2, boss_shockb3 ] {};\nvoid() boss_shockb3 =[\t$shockb3, boss_shockb4 ] {};\nvoid() boss_shockb4 =[\t$shockb4, boss_shockb5 ] {};\nvoid() boss_shockb5 =[\t$shockb5, boss_shockb6 ] {};\nvoid() boss_shockb6 =[\t$shockb6, boss_shockb7 ] {};\nvoid() boss_shockb7 =[\t$shockb1, boss_shockb8 ] {};\nvoid() boss_shockb8 =[\t$shockb2, boss_shockb9 ] {};\nvoid() boss_shockb9 =[\t$shockb3, boss_shockb10 ] {};\nvoid() boss_shockb10 =[\t$shockb4, boss_missile1 ] {};\n\nvoid() boss_shockc1 =[\t$shockc1, boss_shockc2 ] {};\nvoid() boss_shockc2 =[\t$shockc2, boss_shockc3 ] {};\nvoid() boss_shockc3 =[\t$shockc3, boss_shockc4 ] {};\nvoid() boss_shockc4 =[\t$shockc4, boss_shockc5 ] {};\nvoid() boss_shockc5 =[\t$shockc5, boss_shockc6 ] {};\nvoid() boss_shockc6 =[\t$shockc6, boss_shockc7 ] {};\nvoid() boss_shockc7 =[\t$shockc7, boss_shockc8 ] {};\nvoid() boss_shockc8 =[\t$shockc8, boss_shockc9 ] {};\nvoid() boss_shockc9 =[\t$shockc9, boss_shockc10 ] {};\nvoid() boss_shockc10 =[\t$shockc10, boss_death1 ] {};\n\nvoid() boss_death1 = [$death1, boss_death2] {\nsound (self, CHAN_VOICE, \"boss1/death.wav\", 1, ATTN_NORM);\n};\nvoid() boss_death2 = [$death2, boss_death3] {};\nvoid() boss_death3 = [$death3, boss_death4] {};\nvoid() boss_death4 = [$death4, boss_death5] {};\nvoid() boss_death5 = [$death5, boss_death6] {};\nvoid() boss_death6 = [$death6, boss_death7] {};\nvoid() boss_death7 = [$death7, boss_death8] {};\nvoid() boss_death8 = [$death8, boss_death9] {};\nvoid() boss_death9 = [$death9, boss_death10]\n{\n\tsound (self, CHAN_BODY, \"boss1/out1.wav\", 1, ATTN_NORM);\n\tWriteByte (MSG_BROADCAST, SVC_TEMPENTITY);\n\tWriteByte (MSG_BROADCAST, TE_LAVASPLASH);\n\tWriteCoord (MSG_BROADCAST, self.origin_x);\n\tWriteCoord (MSG_BROADCAST, self.origin_y);\n\tWriteCoord (MSG_BROADCAST, self.origin_z);\n};\n\nvoid() boss_death10 = [$death9, boss_death10]\n{\n\tkilled_monsters = killed_monsters + 1;\n\tWriteByte (MSG_ALL, SVC_KILLEDMONSTER);\t// FIXME: reliable broadcast\n\tSUB_UseTargets ();\n\tremove (self);\n};\n\nvoid(vector p) boss_missile =\n{\n\tlocal\tvector\toffang;\n\tlocal\tvector\torg, vec, d;\n\tlocal\tfloat\tt;\n\n\toffang = vectoangles (self.enemy.origin - self.origin);\t\n\tmakevectors (offang);\n\n\torg = self.origin + p_x*v_forward + p_y*v_right + p_z*'0 0 1';\n\t\n// lead the player on hard mode\n\tif (skill > 1)\n\t{\n\t\tt = vlen(self.enemy.origin - org) / 300;\n\t\tvec = self.enemy.velocity;\n\t\tvec_z = 0;\n\t\td = self.enemy.origin + t * vec;\t\t\n\t}\n\telse\n\t{\n\t\td = self.enemy.origin;\n\t}\n\t\n\tvec = normalize (d - org);\n\n\tlaunch_spike (org, vec);\n\tsetmodel (newmis, \"progs/lavaball.mdl\");\n\tnewmis.avelocity = '200 100 300';\n\tsetsize (newmis, VEC_ORIGIN, VEC_ORIGIN);\t\t\n\tnewmis.velocity = vec*300;\n\tnewmis.touch = T_MissileTouch; // rocket explosion\n\tsound (self, CHAN_WEAPON, \"boss1/throw.wav\", 1, ATTN_NORM);\n\n// check for dead enemy\n\tif (self.enemy.health <= 0)\n\t\tboss_idle1 ();\n};\n\n\nvoid() boss_awake =\n{\n\tself.solid = SOLID_SLIDEBOX;\n\tself.movetype = MOVETYPE_STEP;\n\tself.takedamage = DAMAGE_NO;\n\t\n\tsetmodel (self, \"progs/boss.mdl\");\n\tsetsize (self, '-128 -128 -24', '128 128 256');\n\t\n\tif (skill == 0)\n\t\tself.health = 1;\n\telse\n\t\tself.health = 3;\n\n\tself.enemy = activator;\n\n\tWriteByte (MSG_BROADCAST, SVC_TEMPENTITY);\n\tWriteByte (MSG_BROADCAST, TE_LAVASPLASH);\n\tWriteCoord (MSG_BROADCAST, self.origin_x);\n\tWriteCoord (MSG_BROADCAST, self.origin_y);\n\tWriteCoord (MSG_BROADCAST, self.origin_z);\n\n\tself.yaw_speed = 20;\n\tboss_rise1 ();\n};\n\n\n/*QUAKED monster_boss (1 0 0) (-128 -128 -24) (128 128 256)\n*/\nvoid() monster_boss =\n{\n\tif (deathmatch)\n\t{\n\t\tremove(self);\n\t\treturn;\n\t}\n\tprecache_model (\"progs/boss.mdl\");\n\tprecache_model (\"progs/lavaball.mdl\");\n\n\tprecache_sound (\"weapons/rocket1i.wav\");\n\tprecache_sound (\"boss1/out1.wav\");\n\tprecache_sound (\"boss1/sight1.wav\");\n\tprecache_sound (\"misc/power.wav\");\n\tprecache_sound (\"boss1/throw.wav\");\n\tprecache_sound (\"boss1/pain.wav\");\n\tprecache_sound (\"boss1/death.wav\");\n\n\ttotal_monsters = total_monsters + 1;\n\n\tself.use = boss_awake;\n};\n\n//===========================================================================\n\nentity\tle1, le2;\nfloat\tlightning_end;\n\nvoid() lightning_fire =\n{\n\tlocal vector p1, p2;\n\t\n\tif (time >= lightning_end)\n\t{\t// done here, put the terminals back up\n\t\tself = le1;\n\t\tdoor_go_down ();\n\t\tself = le2;\n\t\tdoor_go_down ();\n\t\treturn;\n\t}\n\t\n\tp1 = (le1.mins + le1.maxs) * 0.5;\n\tp1_z = le1.absmin_z - 16;\n\t\n\tp2 = (le2.mins + le2.maxs) * 0.5;\n\tp2_z = le2.absmin_z - 16;\n\t\n\t// compensate for length of bolt\n\tp2 = p2 - normalize(p2-p1)*100;\n\n\tself.nextthink = time + 0.1;\n\tself.think = lightning_fire;\n\n\tWriteByte (MSG_ALL, SVC_TEMPENTITY);\n\tWriteByte (MSG_ALL, TE_LIGHTNING3);\n\tWriteEntity (MSG_ALL, world);\n\tWriteCoord (MSG_ALL, p1_x);\n\tWriteCoord (MSG_ALL, p1_y);\n\tWriteCoord (MSG_ALL, p1_z);\n\tWriteCoord (MSG_ALL, p2_x);\n\tWriteCoord (MSG_ALL, p2_y);\n\tWriteCoord (MSG_ALL, p2_z);\n};\n\nvoid() lightning_use =\n{\n\tif (lightning_end >= time + 1)\n\t\treturn;\n\n\tle1 = find( world, target, \"lightning\");\n\tle2 = find( le1, target, \"lightning\");\n\tif (!le1 || !le2)\n\t{\n\t\tdprint (\"missing lightning targets\\n\");\n\t\treturn;\n\t}\n\t\n\tif (\n\t (le1.state != STATE_TOP && le1.state != STATE_BOTTOM)\n\t|| (le2.state != STATE_TOP && le2.state != STATE_BOTTOM)\n\t|| (le1.state != le2.state) )\n\t{\n//\t\tdprint (\"not aligned\\n\");\n\t\treturn;\n\t}\n\n// don't let the electrodes go back up until the bolt is done\n\tle1.nextthink = -1;\n\tle2.nextthink = -1;\n\tlightning_end = time + 1;\n\n\tsound (self, CHAN_VOICE, \"misc/power.wav\", 1, ATTN_NORM);\n\tlightning_fire ();\t\t\n\n// advance the boss pain if down\n\tself = find (world, classname, \"monster_boss\");\n\tif (!self)\n\t\treturn;\n\tself.enemy = activator;\n\tif (le1.state == STATE_TOP && self.health > 0)\n\t{\n\t\tsound (self, CHAN_VOICE, \"boss1/pain.wav\", 1, ATTN_NORM);\n\t\tself.health = self.health - 1;\n\t\tif (self.health >= 2)\n\t\t\tboss_shocka1();\n\t\telse if (self.health == 1)\n\t\t\tboss_shockb1();\n\t\telse if (self.health == 0)\n\t\t\tboss_shockc1();\n\t}\t\n};\n\n\n/*QUAKED event_lightning (0 1 1) (-16 -16 -16) (16 16 16)\nJust for boss level.\n*/\nvoid() event_lightning =\n{\n\tself.use = lightning_use;\n};\n\n\n"
  },
  {
    "path": "quakec/csqctest/src/ss/buttons.qc",
    "content": "// button and multiple button\r\n\r\nvoid() button_wait;\r\nvoid() button_return;\r\n\r\nvoid() button_wait =\r\n{\r\n\tself.state = STATE_TOP;\r\n\tself.nextthink = self.ltime + self.wait;\r\n\tself.think = button_return;\r\n\tactivator = self.enemy;\r\n\tSUB_UseTargets();\r\n\tself.frame = 1;\t\t\t// use alternate textures\r\n};\r\n\r\nvoid() button_done =\r\n{\r\n\tself.state = STATE_BOTTOM;\r\n};\r\n\r\nvoid() button_return =\r\n{\r\n\tself.state = STATE_DOWN;\r\n\tSUB_CalcMove (self.pos1, self.speed, button_done);\r\n\tself.frame = 0;\t\t\t// use normal textures\r\n\tif (self.health)\r\n\t\tself.takedamage = DAMAGE_YES;\t// can be shot again\r\n};\r\n\r\n\r\nvoid() button_blocked =\r\n{\t// do nothing, just don't ome all the way back out\r\n};\r\n\r\n\r\nvoid() button_fire =\r\n{\r\n\tif (self.state == STATE_UP || self.state == STATE_TOP)\r\n\t\treturn;\r\n\r\n\tsound (self, CHAN_VOICE, self.noise, 1, ATTN_NORM);\r\n\r\n\tself.state = STATE_UP;\r\n\tSUB_CalcMove (self.pos2, self.speed, button_wait);\r\n};\r\n\r\n\r\nvoid() button_use =\r\n{\r\n\tself.enemy = activator;\r\n\tbutton_fire ();\r\n};\r\n\r\nvoid() button_touch =\r\n{\r\n\tif (other.classname != \"player\")\r\n\t\treturn;\r\n\tself.enemy = other;\r\n\tbutton_fire ();\r\n};\r\n\r\nvoid() button_killed =\r\n{\r\n\tself.enemy = damage_attacker;\r\n\tself.health = self.max_health;\r\n\tself.takedamage = DAMAGE_NO;\t// wil be reset upon return\r\n\tbutton_fire ();\r\n};\r\n\r\n\r\n/*QUAKED func_button (0 .5 .8) ?\r\nWhen a button is touched, it moves some distance in the direction of it's angle, triggers all of it's targets, waits some time, then returns to it's original position where it can be triggered again.\r\n\r\n\"angle\"\t\tdetermines the opening direction\r\n\"target\"\tall entities with a matching targetname will be used\r\n\"speed\"\t\toverride the default 40 speed\r\n\"wait\"\t\toverride the default 1 second wait (-1 = never return)\r\n\"lip\"\t\toverride the default 4 pixel lip remaining at end of move\r\n\"health\"\tif set, the button must be killed instead of touched\r\n\"sounds\"\r\n0) steam metal\r\n1) wooden clunk\r\n2) metallic click\r\n3) in-out\r\n*/\r\nvoid() func_button =\r\n{\r\n\tif (self.sounds == 0)\r\n\t{\r\n\t\tprecache_sound (\"buttons/airbut1.wav\");\r\n\t\tself.noise = \"buttons/airbut1.wav\";\r\n\t}\r\n\tif (self.sounds == 1)\r\n\t{\r\n\t\tprecache_sound (\"buttons/switch21.wav\");\r\n\t\tself.noise = \"buttons/switch21.wav\";\r\n\t}\r\n\tif (self.sounds == 2)\r\n\t{\r\n\t\tprecache_sound (\"buttons/switch02.wav\");\r\n\t\tself.noise = \"buttons/switch02.wav\";\r\n\t}\r\n\tif (self.sounds == 3)\r\n\t{\r\n\t\tprecache_sound (\"buttons/switch04.wav\");\r\n\t\tself.noise = \"buttons/switch04.wav\";\r\n\t}\r\n\t\r\n\tSetMovedir ();\r\n\r\n\tself.movetype = MOVETYPE_PUSH;\r\n\tself.solid = SOLID_BSP;\r\n\tsetmodel (self, self.model);\r\n\r\n\tself.blocked = button_blocked;\r\n\tself.use = button_use;\r\n\r\n\tif (self.health)\r\n\t{\r\n\t\tself.max_health = self.health;\r\n\t\tself.th_die = button_killed;\r\n\t\tself.takedamage = DAMAGE_YES;\r\n\t}\r\n\telse\r\n\t\tself.touch = button_touch;\r\n\r\n\tif (!self.speed)\r\n\t\tself.speed = 40;\r\n\tif (!self.wait)\r\n\t\tself.wait = 1;\r\n\tif (!self.lip)\r\n\t\tself.lip = 4;\r\n\r\n\tself.state = STATE_BOTTOM;\r\n\r\n\tself.pos1 = self.origin;\r\n\tself.pos2 = self.pos1 + self.movedir*(fabs(self.movedir*self.size) - self.lip);\r\n};\r\n\r\n"
  },
  {
    "path": "quakec/csqctest/src/ss/client.qc",
    "content": "\r\n// prototypes\r\nvoid () W_WeaponFrame;\r\nvoid() W_SetCurrentAmmo;\r\nvoid(entity attacker, float damage) player_pain;\r\nvoid() player_stand1;\r\nvoid (vector org) spawn_tfog;\r\nvoid (vector org, entity death_owner) spawn_tdeath;\r\n\r\nfloat\tmodelindex_eyes, modelindex_player;\r\n\r\n.float usingcsqc;\r\n\r\n/*\r\n=============================================================================\r\n\r\n\t\t\t\tLEVEL CHANGING / INTERMISSION\r\n\r\n=============================================================================\r\n*/\r\n\r\nfloat\tintermission_running;\r\nfloat\tintermission_exittime;\r\n\r\n/*QUAKED info_intermission (1 0.5 0.5) (-16 -16 -16) (16 16 16)\r\nThis is the camera point for the intermission.\r\nUse mangle instead of angle, so you can set pitch or roll as well as yaw.  'pitch roll yaw'\r\n*/\r\nvoid() info_intermission =\r\n{\r\n};\r\n\r\n\r\n\r\nvoid() SetChangeParms =\r\n{\r\n\tif (self.health <= 0)\r\n\t{\r\n\t\tSetNewParms ();\r\n\t\treturn;\r\n\t}\r\n\r\n// remove items\r\n\tself.items = self.items - (self.items & \r\n\t(IT_KEY1 | IT_KEY2 | IT_INVISIBILITY | IT_INVULNERABILITY | IT_SUIT | IT_QUAD) );\r\n\t\r\n// cap super health\r\n\tif (self.health > 100)\r\n\t\tself.health = 100;\r\n\tif (self.health < 50)\r\n\t\tself.health = 50;\r\n\tparm1 = self.items;\r\n\tparm2 = self.health;\r\n\tparm3 = self.armorvalue;\r\n\tif (self.ammo_shells < 25)\r\n\t\tparm4 = 25;\r\n\telse\r\n\t\tparm4 = self.ammo_shells;\r\n\tparm5 = self.ammo_nails;\r\n\tparm6 = self.ammo_rockets;\r\n\tparm7 = self.ammo_cells;\r\n\tparm8 = self.weapon;\r\n\tparm9 = self.armortype * 100;\r\n};\r\n\r\nvoid() SetNewParms =\r\n{\r\n\tparm1 = IT_SHOTGUN | IT_AXE;\r\n\tparm2 = 100;\r\n\tparm3 = 0;\r\n\tparm4 = 25;\r\n\tparm5 = 0;\r\n\tparm6 = 0;\r\n\tparm7 = 0;\r\n\tparm8 = 1;\r\n\tparm9 = 0;\r\n};\r\n\r\nvoid() DecodeLevelParms =\r\n{\r\n\tif (serverflags)\r\n\t{\r\n\t\tif (world.model == \"maps/start.bsp\")\r\n\t\t\tSetNewParms ();\t\t// take away all stuff on starting new episode\r\n\t}\r\n\t\r\n\tself.items = parm1;\r\n\tself.health = parm2;\r\n\tself.armorvalue = parm3;\r\n\tself.ammo_shells = parm4;\r\n\tself.ammo_nails = parm5;\r\n\tself.ammo_rockets = parm6;\r\n\tself.ammo_cells = parm7;\r\n\tself.weapon = parm8;\r\n\tself.armortype = parm9 * 0.01;\r\n};\r\n\r\n/*\r\n============\r\nFindIntermission\r\n\r\nReturns the entity to view from\r\n============\r\n*/\r\nentity() FindIntermission =\r\n{\r\n\tlocal\tentity spot;\r\n\tlocal\tfloat cyc;\r\n\r\n// look for info_intermission first\r\n\tspot = find (world, classname, \"info_intermission\");\r\n\tif (spot)\r\n\t{\t// pick a random one\r\n\t\tcyc = random() * 4;\r\n\t\twhile (cyc > 1)\r\n\t\t{\r\n\t\t\tspot = find (spot, classname, \"info_intermission\");\r\n\t\t\tif (!spot)\r\n\t\t\t\tspot = find (spot, classname, \"info_intermission\");\r\n\t\t\tcyc = cyc - 1;\r\n\t\t}\r\n\t\treturn spot;\r\n\t}\r\n\r\n// then look for the start position\r\n\tspot = find (world, classname, \"info_player_start\");\r\n\tif (spot)\r\n\t\treturn spot;\r\n\t\r\n// testinfo_player_start is only found in regioned levels\r\n\tspot = find (world, classname, \"testplayerstart\");\r\n\tif (spot)\r\n\t\treturn spot;\r\n\t\r\n\tobjerror (\"FindIntermission: no spot\");\r\n\treturn world;\r\n};\r\n\r\n\r\nstring nextmap;\r\nvoid() GotoNextMap =\r\n{\r\n\tif (cvar(\"samelevel\"))\t// if samelevel is set, stay on same level\r\n\t\tchangelevel (mapname);\r\n\telse\r\n\t\tchangelevel (nextmap);\r\n};\r\n\r\n\r\nvoid() ExitIntermission =\r\n{\r\n// skip any text in deathmatch\r\n\tif (deathmatch)\r\n\t{\r\n\t\tGotoNextMap ();\r\n\t\treturn;\r\n\t}\r\n\t\r\n\tintermission_exittime = time + 1;\r\n\tintermission_running = intermission_running + 1;\r\n\r\n//\r\n// run some text if at the end of an episode\r\n//\r\n\tif (intermission_running == 2)\r\n\t{\r\n\t\tif (world.model == \"maps/e1m7.bsp\")\r\n\t\t{\r\n\t\t\tWriteByte (MSG_ALL, SVC_CDTRACK);\r\n\t\t\tWriteByte (MSG_ALL, 2);\r\n\t\t\tWriteByte (MSG_ALL, 3);\r\n\t\t\tif (!cvar(\"registered\"))\r\n\t\t\t{\r\n\t\t\t\tWriteByte (MSG_ALL, SVC_FINALE);\r\n\t\t\t\tWriteString (MSG_ALL, \"As the corpse of the monstrous entity\\nChthon sinks back into the lava whence\\nit rose, you grip the Rune of Earth\\nMagic tightly. Now that you have\\nconquered the Dimension of the Doomed,\\nrealm of Earth Magic, you are ready to\\ncomplete your task in the other three\\nhaunted lands of Quake. Or are you? If\\nyou don't register Quake, you'll never\\nknow what awaits you in the Realm of\\nBlack Magic, the Netherworld, and the\\nElder World!\");\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tWriteByte (MSG_ALL, SVC_FINALE);\r\n\t\t\t\tWriteString (MSG_ALL, \"As the corpse of the monstrous entity\\nChthon sinks back into the lava whence\\nit rose, you grip the Rune of Earth\\nMagic tightly. Now that you have\\nconquered the Dimension of the Doomed,\\nrealm of Earth Magic, you are ready to\\ncomplete your task. A Rune of magic\\npower lies at the end of each haunted\\nland of Quake. Go forth, seek the\\ntotality of the four Runes!\");\r\n\t\t\t}\r\n\t\t\treturn;\r\n\t\t}\r\n\t\telse if (world.model == \"maps/e2m6.bsp\")\r\n\t\t{\r\n\t\t\tWriteByte (MSG_ALL, SVC_CDTRACK);\r\n\t\t\tWriteByte (MSG_ALL, 2);\r\n\t\t\tWriteByte (MSG_ALL, 3);\r\n\r\n\t\t\tWriteByte (MSG_ALL, SVC_FINALE);\r\n\t\t\tWriteString (MSG_ALL, \"The Rune of Black Magic throbs evilly in\\nyour hand and whispers dark thoughts\\ninto your brain. You learn the inmost\\nlore of the Hell-Mother; Shub-Niggurath!\\nYou now know that she is behind all the\\nterrible plotting which has led to so\\nmuch death and horror. But she is not\\ninviolate! Armed with this Rune, you\\nrealize that once all four Runes are\\ncombined, the gate to Shub-Niggurath's\\nPit will open, and you can face the\\nWitch-Goddess herself in her frightful\\notherworld cathedral.\");\r\n\t\t\treturn;\r\n\t\t}\r\n\t\telse if (world.model == \"maps/e3m6.bsp\")\r\n\t\t{\r\n\t\t\tWriteByte (MSG_ALL, SVC_CDTRACK);\r\n\t\t\tWriteByte (MSG_ALL, 2);\r\n\t\t\tWriteByte (MSG_ALL, 3);\r\n\r\n\t\t\tWriteByte (MSG_ALL, SVC_FINALE);\r\n\t\t\tWriteString (MSG_ALL, \"The charred viscera of diabolic horrors\\nbubble viscously as you seize the Rune\\nof Hell Magic. Its heat scorches your\\nhand, and its terrible secrets blight\\nyour mind. Gathering the shreds of your\\ncourage, you shake the devil's shackles\\nfrom your soul, and become ever more\\nhard and determined to destroy the\\nhideous creatures whose mere existence\\nthreatens the souls and psyches of all\\nthe population of Earth.\");\r\n\t\t\treturn;\r\n\t\t}\r\n\t\telse if (world.model == \"maps/e4m7.bsp\")\r\n\t\t{\r\n\t\t\tWriteByte (MSG_ALL, SVC_CDTRACK);\r\n\t\t\tWriteByte (MSG_ALL, 2);\r\n\t\t\tWriteByte (MSG_ALL, 3);\r\n\r\n\t\t\tWriteByte (MSG_ALL, SVC_FINALE);\r\n\t\t\tWriteString (MSG_ALL, \"Despite the awful might of the Elder\\nWorld, you have achieved the Rune of\\nElder Magic, capstone of all types of\\narcane wisdom. Beyond good and evil,\\nbeyond life and death, the Rune\\npulsates, heavy with import. Patient and\\npotent, the Elder Being Shub-Niggurath\\nweaves her dire plans to clear off all\\nlife from the Earth, and bring her own\\nfoul offspring to our world! For all the\\ndwellers in these nightmare dimensions\\nare her descendants! Once all Runes of\\nmagic power are united, the energy\\nbehind them will blast open the Gateway\\nto Shub-Niggurath, and you can travel\\nthere to foil the Hell-Mother's plots\\nin person.\");\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\tGotoNextMap();\r\n\t}\r\n\t\r\n\tif (intermission_running == 3)\r\n\t{\r\n\t\tif (!cvar(\"registered\"))\r\n\t\t{\t// shareware episode has been completed, go to sell screen\r\n\t\t\tWriteByte (MSG_ALL, SVC_SELLSCREEN);\r\n\t\t\treturn;\r\n\t\t}\r\n\t\t\r\n\t\tif ( (serverflags&15) == 15)\r\n\t\t{\r\n\t\t\tWriteByte (MSG_ALL, SVC_FINALE);\r\n\t\t\tWriteString (MSG_ALL, \"Now, you have all four Runes. You sense\\ntremendous invisible forces moving to\\nunseal ancient barriers. Shub-Niggurath\\nhad hoped to use the Runes Herself to\\nclear off the Earth, but now instead,\\nyou will use them to enter her home and\\nconfront her as an avatar of avenging\\nEarth-life. If you defeat her, you will\\nbe remembered forever as the savior of\\nthe planet. If she conquers, it will be\\nas if you had never been born.\");\r\n\t\t\treturn;\r\n\t\t}\r\n\t\t\r\n\t}\r\n\r\n\tGotoNextMap();\r\n};\r\n\r\n/*\r\n============\r\nIntermissionThink\r\n\r\nWhen the player presses attack or jump, change to the next level\r\n============\r\n*/\r\nvoid() IntermissionThink =\r\n{\r\n\tif (time < intermission_exittime)\r\n\t\treturn;\r\n\r\n\tif (!self.button0 && !self.button1 && !self.button2)\r\n\t\treturn;\r\n\t\r\n\tExitIntermission ();\r\n};\r\n\r\nvoid() execute_changelevel =\r\n{\r\n\tlocal entity\tpos;\r\n\r\n\tintermission_running = 1;\r\n\t\r\n// enforce a wait time before allowing changelevel\r\n\tif (deathmatch)\r\n\t\tintermission_exittime = time + 5;\r\n\telse\r\n\t\tintermission_exittime = time + 2;\r\n\r\n\tWriteByte (MSG_ALL, SVC_CDTRACK);\r\n\tWriteByte (MSG_ALL, 3);\r\n\tWriteByte (MSG_ALL, 3);\r\n\t\r\n\tpos = FindIntermission ();\r\n\r\n\tother = find (world, classname, \"player\");\r\n\twhile (other != world)\r\n\t{\r\n\t\tother.view_ofs = '0 0 0';\r\n\t\tother.angles = other.v_angle = pos.mangle;\r\n\t\tother.fixangle = TRUE;\t\t// turn this way immediately\r\n\t\tother.nextthink = time + 0.5;\r\n\t\tother.takedamage = DAMAGE_NO;\r\n\t\tother.solid = SOLID_NOT;\r\n\t\tother.movetype = MOVETYPE_NONE;\r\n\t\tother.modelindex = 0;\r\n\t\tsetorigin (other, pos.origin);\r\n\t\tother = find (other, classname, \"player\");\r\n\t}\t\r\n\r\n\tWriteByte (MSG_ALL, SVC_INTERMISSION);\r\n};\r\n\r\n\r\nvoid() changelevel_touch =\r\n{\r\n\tif (other.classname != \"player\")\r\n\t\treturn;\r\n\r\n\tif ((cvar(\"noexit\") == 1) || ((cvar(\"noexit\") == 2) && (mapname != \"start\")))\r\n\t{\r\n\t\tT_Damage (other, self, self, 50000);\r\n\t\treturn;\r\n\t}\r\n\r\n\tif (coop || deathmatch)\r\n\t{\r\n\t\tbprint (other.netname);\r\n\t\tbprint (\" exited the level\\n\");\r\n\t}\r\n\t\r\n\tnextmap = self.map;\r\n\r\n\tSUB_UseTargets ();\r\n\r\n\tif ( (self.spawnflags & 1) && (deathmatch == 0) )\r\n\t{\t// NO_INTERMISSION\r\n\t\tGotoNextMap();\r\n\t\treturn;\r\n\t}\r\n\t\r\n\tself.touch = SUB_Null;\r\n\r\n// we can't move people right now, because touch functions are called\r\n// in the middle of C movement code, so set a think time to do it\r\n\tself.think = execute_changelevel;\r\n\tself.nextthink = time + 0.1;\r\n};\r\n\r\n/*QUAKED trigger_changelevel (0.5 0.5 0.5) ? NO_INTERMISSION\r\nWhen the player touches this, he gets sent to the map listed in the \"map\" variable.  Unless the NO_INTERMISSION flag is set, the view will go to the info_intermission spot and display stats.\r\n*/\r\nvoid() trigger_changelevel =\r\n{\r\n\tif (!self.map)\r\n\t\tobjerror (\"chagnelevel trigger doesn't have map\");\r\n\t\r\n\tInitTrigger ();\r\n\tself.touch = changelevel_touch;\r\n};\r\n\r\n\r\n/*\r\n=============================================================================\r\n\r\n\t\t\t\tPLAYER GAME EDGE FUNCTIONS\r\n\r\n=============================================================================\r\n*/\r\n\r\nvoid() set_suicide_frame;\r\n\r\n// called by ClientKill and DeadThink\r\nvoid() respawn =\r\n{\r\n\tif (coop)\r\n\t{\r\n\t\t// make a copy for apearances sake\r\n\t\tCopyToBodyQue(self);\r\n\t\t// get the spawn parms as they were at level start\r\n\t\tsetspawnparms (self);\r\n\t\t// respawn\t\t\r\n\t\tPutClientInServer ();\r\n\t}\r\n\telse if (deathmatch)\r\n\t{\r\n\t\t// make a copy for apearances sake\r\n\t\tCopyToBodyQue(self);\r\n\t\t// set default spawn parms\r\n\t\tSetNewParms ();\r\n\t\t// respawn\t\t\r\n\t\tPutClientInServer ();\r\n\t}\r\n\telse\r\n\t{\t// restart the entire server\r\n\t\tlocalcmd (\"restart\\n\");\r\n\t}\r\n};\r\n\r\n\r\n/*\r\n============\r\nClientKill\r\n\r\nPlayer entered the suicide command\r\n============\r\n*/\r\nvoid() ClientKill =\r\n{\r\n\tbprint (self.netname);\r\n\tbprint (\" suicides\\n\");\r\n\r\n\tself.frags = self.frags - 2;\t// extra penalty\r\n\tself.modelindex = modelindex_player;\r\n\r\n\tself.health = 0;\r\n\tself.takedamage = DAMAGE_NO;\r\n\tself.touch = SUB_Null;\r\n\tself.th_die ();\r\n};\r\n\r\n/*float(vector v) CheckSpawnPoint =\r\n{\r\n\treturn FALSE;\r\n};*/\r\n\r\n/*\r\n============\r\nSelectSpawnPoint\r\n\r\nReturns the entity to spawn at\r\n============\r\n*/\r\nentity() SelectSpawnPoint =\r\n{\r\n\tlocal\tentity spot;\r\n\tlocal\tentity thing;\r\n\tlocal\tfloat  pcount;\r\n\t\r\n// testinfo_player_start is only found in regioned levels\r\n\tspot = find (world, classname, \"testplayerstart\");\r\n\tif (spot)\r\n\t\treturn spot;\r\n\t\t\r\n// choose a info_player_deathmatch point\r\n\tif (coop)\r\n\t{\r\n\t\tlastspawn = find(lastspawn, classname, \"info_player_coop\");\r\n\t\tif (lastspawn == world)\r\n\t\t\tlastspawn = find (lastspawn, classname, \"info_player_start\");\r\n\t\tif (lastspawn != world)\r\n\t\t\treturn lastspawn;\r\n\t}\r\n\telse if (deathmatch)\r\n\t{\r\n\t\tspot = lastspawn;\r\n\t\twhile (1)\r\n\t\t{\r\n\t\t\tspot = find(spot, classname, \"info_player_deathmatch\");\r\n\t\t\tif (spot != world)\r\n\t\t\t{\r\n\t\t\t\tif (spot == lastspawn)\r\n\t\t\t\t\treturn lastspawn;\r\n\t\t\t\tpcount = 0;\r\n\t\t\t\tthing = findradius(spot.origin, 32);\r\n\t\t\t\twhile(thing)\r\n\t\t\t\t{\r\n\t\t\t\t\tif (thing.classname == \"player\")\r\n\t\t\t\t\t\tpcount = pcount + 1;\r\n\t\t\t\t\tthing = thing.chain;\r\n\t\t\t\t}\r\n\t\t\t\tif (pcount == 0)\r\n\t\t\t\t{\r\n\t\t\t\t\tlastspawn = spot;\r\n\t\t\t\t\treturn spot;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\tif (serverflags)\r\n\t{\t// return with a rune to start\r\n\t\tspot = find (world, classname, \"info_player_start2\");\r\n\t\tif (spot)\r\n\t\t\treturn spot;\r\n\t}\r\n\t\r\n\tspot = find (world, classname, \"info_player_start\");\r\n\tif (!spot)\r\n\t\terror (\"PutClientInServer: no info_player_start on level\");\r\n\t\r\n\treturn spot;\r\n};\r\n\r\nfloat(entity to, float fl) SendPlayer =\r\n{\r\n\tfloat ef;\r\n\tfloat f;\r\n\tfloat mt;\r\n\r\n//encode the current weapon in the lower 4 bits.\r\n\tfor (f = 1, ef = 0; ef < 16; ef=ef+1,f*=2)\r\n\t{\r\n\t\tif (self.weapon == f)\r\n\t\t\tbreak;\r\n\t}\r\n//and put effects in the upper 3\r\n\tif (self.items & IT_INVISIBILITY)\r\n\t\tef |= 128;\r\n\tif (self.items & IT_INVULNERABILITY || self.flags & FL_GODMODE)\r\n\t\tef |= 64;\r\n\tif (self.items & IT_QUAD)\r\n\t\tef |= 32;\r\n//and the final bit takes an indication to switch off prediction.\r\n\tmt = self.movetype | (self.pmove_flags<<6);\r\n\tif (mt != MOVETYPE_WALK)\r\n\t\tef |= 16;\r\n\r\n\r\n\tWriteByte(MSG_ENTITY, CLASS_PLAYER);\r\n\tWriteByte(MSG_ENTITY, self.frame);\t//for clientside animation selection, in a way compatable with not having csqc.\r\n\tWriteChar(MSG_ENTITY, self.angles_x*(256/360));\r\n\tWriteChar(MSG_ENTITY, self.angles_y*(256/360));\r\n\tWriteCoord(MSG_ENTITY, self.origin_x);\r\n\tWriteCoord(MSG_ENTITY, self.origin_y);\r\n\tWriteCoord(MSG_ENTITY, self.origin_z);\r\n\tWriteShort(MSG_ENTITY, self.velocity_x*8);\r\n\tWriteShort(MSG_ENTITY, self.velocity_y*8);\r\n\tWriteShort(MSG_ENTITY, self.velocity_z*8);\r\n\tWriteByte(MSG_ENTITY, ef);\r\n\tif (ef & 16)\r\n\t\tWriteByte(MSG_ENTITY, mt);\r\n\treturn TRUE;\r\n};\r\n\r\n\r\n/*\r\n===========\r\nPutClientInServer\r\n\r\ncalled each time a player is spawned\r\n============\r\n*/\r\nvoid() DecodeLevelParms;\r\nvoid() PlayerDie;\r\n\r\nvoid() PutClientInServer =\r\n{\r\n\tlocal\tentity spot;\r\n\r\n\tspot = SelectSpawnPoint ();\r\n\r\n\tself.classname = \"player\";\r\n\tself.health = 100;\r\n\tself.takedamage = DAMAGE_AIM;\r\n\tself.solid = SOLID_SLIDEBOX;\r\n\tself.movetype = MOVETYPE_WALK;\r\n\tself.show_hostile = 0;\r\n\tself.max_health = 100;\r\n\tself.flags = FL_CLIENT;\r\n\tself.air_finished = time + 12;\r\n\tself.dmg = 2;   \t\t// initial water damage\r\n\tself.super_damage_finished = 0;\r\n\tself.radsuit_finished = 0;\r\n\tself.invisible_finished = 0;\r\n\tself.invincible_finished = 0;\r\n\tself.effects = 0;\r\n\tself.invincible_time = 0;\r\n\r\n\tself.SendEntity = SendPlayer;\r\n\tself.SendFlags = FULLSEND;\r\n\r\n\tDecodeLevelParms ();\r\n\t\r\n\tW_SetCurrentAmmo ();\r\n\r\n\tself.attack_finished = time;\r\n\tself.th_pain = player_pain;\r\n\tself.th_die = PlayerDie;\r\n\t\r\n\tself.deadflag = DEAD_NO;\r\n// paustime is set by teleporters to keep the player from moving a while\r\n\tself.pausetime = 0;\r\n\t\r\n//\tspot = SelectSpawnPoint ();\r\n\r\n\tself.origin = spot.origin + '0 0 1';\r\n\tself.angles = spot.angles;\r\n\tself.fixangle = TRUE;\t\t// turn this way immediately\r\n\r\n// oh, this is a hack!\r\n\tsetmodel (self, \"progs/eyes.mdl\");\r\n\tmodelindex_eyes = self.modelindex;\r\n\r\n\tsetmodel (self, \"progs/player.mdl\");\r\n\tmodelindex_player = self.modelindex;\r\n\r\n\tsetsize (self, VEC_HULL_MIN, VEC_HULL_MAX);\r\n\t\r\n\tself.view_ofs = '0 0 22';\r\n\r\n\tplayer_stand1 ();\r\n\t\r\n\tif (deathmatch || coop)\r\n\t{\r\n\t\tmakevectors(self.angles);\r\n\t\tspawn_tfog (self.origin + v_forward*20);\r\n\t}\r\n\r\n\tspawn_tdeath (self.origin, self);\r\n};\r\n\r\n\r\n/*\r\n=============================================================================\r\n\r\n\t\t\t\tQUAKED FUNCTIONS\r\n\r\n=============================================================================\r\n*/\r\n\r\n\r\n/*QUAKED info_player_start (1 0 0) (-16 -16 -24) (16 16 24)\r\nThe normal starting point for a level.\r\n*/\r\nvoid() info_player_start =\r\n{\r\n};\r\n\r\n\r\n/*QUAKED info_player_start2 (1 0 0) (-16 -16 -24) (16 16 24)\r\nOnly used on start map for the return point from an episode.\r\n*/\r\nvoid() info_player_start2 =\r\n{\r\n};\r\n\r\n\r\n/*\r\nsaved out by quaked in region mode\r\n*/\r\nvoid() testplayerstart =\r\n{\r\n};\r\n\r\n/*QUAKED info_player_deathmatch (1 0 1) (-16 -16 -24) (16 16 24)\r\npotential spawning position for deathmatch games\r\n*/\r\nvoid() info_player_deathmatch =\r\n{\r\n};\r\n\r\n/*QUAKED info_player_coop (1 0 1) (-16 -16 -24) (16 16 24)\r\npotential spawning position for coop games\r\n*/\r\nvoid() info_player_coop =\r\n{\r\n};\r\n\r\n/*\r\n===============================================================================\r\n\r\nRULES\r\n\r\n===============================================================================\r\n*/\r\n\r\n/*\r\ngo to the next level for deathmatch\r\nonly called if a time or frag limit has expired\r\n*/\r\nvoid() NextLevel =\r\n{\r\n\tlocal entity o;\r\n\r\n\tif (mapname == \"start\")\r\n\t{\r\n\t\tif (!cvar(\"registered\"))\r\n\t\t{\r\n\t\t\tmapname = \"e1m1\";\r\n\t\t}\r\n\t\telse if (!(serverflags & 1))\r\n\t\t{\r\n\t\t\tmapname = \"e1m1\";\r\n\t\t\tserverflags = serverflags | 1;\r\n\t\t}\r\n\t\telse if (!(serverflags & 2))\r\n\t\t{\r\n\t\t\tmapname = \"e2m1\";\r\n\t\t\tserverflags = serverflags | 2;\r\n\t\t}\r\n\t\telse if (!(serverflags & 4))\r\n\t\t{\r\n\t\t\tmapname = \"e3m1\";\r\n\t\t\tserverflags = serverflags | 4;\r\n\t\t}\r\n\t\telse if (!(serverflags & 8))\r\n\t\t{\r\n\t\t\tmapname = \"e4m1\";\r\n\t\t\tserverflags = serverflags - 7;\r\n\t\t}\r\n\r\n\t\to = spawn();\r\n\t\to.map = mapname;\r\n\t}\r\n\telse\r\n\t{\r\n\t\t// find a trigger changelevel\r\n\t\to = find(world, classname, \"trigger_changelevel\");\r\n\r\n\t\t// go back to start if no trigger_changelevel\r\n\t\tif (!o)\r\n\t\t{\r\n\t\t\tmapname = \"start\";\r\n\t\t\to = spawn();\r\n\t\t\to.map = mapname;\r\n\t\t}\r\n\t}\r\n\r\n\tnextmap = o.map;\r\n\tgameover = TRUE;\r\n\t\r\n\tif (o.nextthink < time)\r\n\t{\r\n\t\to.think = execute_changelevel;\r\n\t\to.nextthink = time + 0.1;\r\n\t}\r\n};\r\n\r\n/*\r\n============\r\nCheckRules\r\n\r\nExit deathmatch games upon conditions\r\n============\r\n*/\r\nvoid() CheckRules =\r\n{\r\n\tlocal\tfloat\t\ttimelimit;\r\n\tlocal\tfloat\t\tfraglimit;\r\n\t\r\n\tif (gameover)\t// someone else quit the game already\r\n\t\treturn;\r\n\t\t\r\n\ttimelimit = cvar(\"timelimit\") * 60;\r\n\tfraglimit = cvar(\"fraglimit\");\r\n\t\r\n\tif (timelimit && time >= timelimit)\r\n\t{\r\n\t\tNextLevel ();\r\n\t\treturn;\r\n\t}\r\n\t\r\n\tif (fraglimit && self.frags >= fraglimit)\r\n\t{\r\n\t\tNextLevel ();\r\n\t\treturn;\r\n\t}\t\r\n};\r\n\r\n//============================================================================\r\n\r\nvoid() PlayerDeathThink =\r\n{\r\n\tlocal float\t\tforward;\r\n\r\n\tif ((self.flags & FL_ONGROUND))\r\n\t{\r\n\t\tforward = vlen (self.velocity);\r\n\t\tforward = forward - 20;\r\n\t\tif (forward <= 0)\r\n\t\t\tself.velocity = '0 0 0';\r\n\t\telse\t\r\n\t\t\tself.velocity = forward * normalize(self.velocity);\r\n\t}\r\n\r\n// wait for all buttons released\r\n\tif (self.deadflag == DEAD_DEAD)\r\n\t{\r\n\t\tif (self.button2 || self.button1 || self.button0)\r\n\t\t\treturn;\r\n\t\tself.deadflag = DEAD_RESPAWNABLE;\r\n\t\treturn;\r\n\t}\r\n\r\n// wait for any button down\r\n\tif (!self.button2 && !self.button1 && !self.button0)\r\n\t\treturn;\r\n\r\n\tself.button0 = 0;\r\n\tself.button1 = 0;\r\n\tself.button2 = 0;\r\n\trespawn();\r\n};\r\n\r\n\r\nvoid() PlayerJump =\r\n{\r\n\r\n\tif (self.flags & FL_WATERJUMP)\r\n\t\treturn;\r\n\t\r\n\tif (self.waterlevel >= 2)\r\n\t{\r\n//pmove code predicts this\r\n/*\r\n\t\tif (self.watertype == CONTENT_WATER)\r\n\t\t\tself.velocity_z = 100;\r\n\t\telse if (self.watertype == CONTENT_SLIME)\r\n\t\t\tself.velocity_z = 80;\r\n\t\telse\r\n\t\t\tself.velocity_z = 50;\r\n*/\r\n\r\n// play swiming sound\r\n\t\tif (self.swim_flag < time)\r\n\t\t{\r\n\t\t\tself.swim_flag = time + 1;\r\n\t\t\tif (random() < 0.5)\r\n\t\t\t\tsound (self, CHAN_BODY, \"misc/water1.wav\", 1, ATTN_NORM);\r\n\t\t\telse\r\n\t\t\t\tsound (self, CHAN_BODY, \"misc/water2.wav\", 1, ATTN_NORM);\r\n\t\t}\r\n\r\n\t\treturn;\r\n\t}\r\n\r\n\tif (!(self.flags & FL_ONGROUND))\r\n\t\treturn;\r\n\r\n\tif ( !(self.flags & FL_JUMPRELEASED) )\r\n\t\treturn;\t\t// don't pogo stick\r\n\r\n\tself.flags = self.flags - (self.flags & FL_JUMPRELEASED);\r\n\r\n\tself.flags = self.flags - FL_ONGROUND;\t// don't stairwalk\r\n\t\r\n\tself.button2 = 0;\r\n// player jumping sound\r\n\tsound (self, 64/*CHAN_BODY*/, \"player/plyrjmp8.wav\", 1, ATTN_NORM, 0, 0, 0);\r\n\r\n//pmove code predicts this\r\n//\tself.velocity_z = self.velocity_z + 270;\r\n};\r\n\r\n\r\n/*\r\n===========\r\nWaterMove\r\n\r\n============\r\n*/\r\n.float\tdmgtime;\r\n\r\nvoid() WaterMove =\r\n{\r\n//dprint (ftos(self.waterlevel));\r\n\tif (self.movetype == MOVETYPE_NOCLIP)\r\n\t\treturn;\r\n\tif (self.health < 0)\r\n\t\treturn;\r\n\r\n\tif (self.waterlevel != 3)\r\n\t{\r\n\t\tif (self.air_finished < time)\r\n\t\t\tsound (self, CHAN_VOICE, \"player/gasp2.wav\", 1, ATTN_NORM);\r\n\t\telse if (self.air_finished < time + 9)\r\n\t\t\tsound (self, CHAN_VOICE, \"player/gasp1.wav\", 1, ATTN_NORM);\r\n\t\tself.air_finished = time + 12;\r\n\t\tself.dmg = 2;\r\n\t}\r\n\telse if (self.air_finished < time)\r\n\t{\t// drown!\r\n\t\tif (self.pain_finished < time)\r\n\t\t{\r\n\t\t\tself.dmg = self.dmg + 2;\r\n\t\t\tif (self.dmg > 15)\r\n\t\t\t\tself.dmg = 10;\r\n\t\t\tT_Damage (self, world, world, self.dmg);\r\n\t\t\tself.pain_finished = time + 1;\r\n\t\t}\r\n\t}\r\n\t\r\n\tif (!self.waterlevel)\r\n\t{\r\n\t\tif (self.flags & FL_INWATER)\r\n\t\t{\t\r\n\t\t\t// play leave water sound\r\n\t\t\tsound (self, CHAN_BODY, \"misc/outwater.wav\", 1, ATTN_NORM);\r\n\t\t\tself.flags = self.flags - FL_INWATER;\r\n\t\t}\r\n\t\treturn;\r\n\t}\r\n\r\n\tif (self.watertype == CONTENT_LAVA)\r\n\t{\t// do damage\r\n\t\tif (self.dmgtime < time)\r\n\t\t{\r\n\t\t\tif (self.radsuit_finished > time)\r\n\t\t\t\tself.dmgtime = time + 1;\r\n\t\t\telse\r\n\t\t\t\tself.dmgtime = time + 0.2;\r\n\r\n\t\t\tT_Damage (self, world, world, 10*self.waterlevel);\r\n\t\t}\r\n\t}\r\n\telse if (self.watertype == CONTENT_SLIME)\r\n\t{\t// do damage\r\n\t\tif (self.dmgtime < time && self.radsuit_finished < time)\r\n\t\t{\r\n\t\t\tself.dmgtime = time + 1;\r\n\t\t\tT_Damage (self, world, world, 4*self.waterlevel);\r\n\t\t}\r\n\t}\r\n\t\r\n\tif ( !(self.flags & FL_INWATER) )\r\n\t{\t\r\n\r\n// player enter water sound\r\n\r\n\t\tif (self.watertype == CONTENT_LAVA)\r\n\t\t\tsound (self, CHAN_BODY, \"player/inlava.wav\", 1, ATTN_NORM);\r\n\t\tif (self.watertype == CONTENT_WATER)\r\n\t\t\tsound (self, CHAN_BODY, \"player/inh2o.wav\", 1, ATTN_NORM);\r\n\t\tif (self.watertype == CONTENT_SLIME)\r\n\t\t\tsound (self, CHAN_BODY, \"player/slimbrn2.wav\", 1, ATTN_NORM);\r\n\r\n\t\tself.flags = self.flags + FL_INWATER;\r\n\t\tself.dmgtime = 0;\r\n\t}\r\n\t\r\n//\tif (! (self.flags & FL_WATERJUMP) )\r\n//\t\tself.velocity = self.velocity - 0.8*self.waterlevel*frametime*self.velocity;\r\n};\r\n\r\n/*void() CheckWaterJump =\r\n{\r\n\tlocal vector start, end;\r\n\r\n// check for a jump-out-of-water\r\n\tmakevectors (self.angles);\r\n\tstart = self.origin;\r\n\tstart_z = start_z + 8; \r\n\tv_forward_z = 0;\r\n\tnormalize(v_forward);\r\n\tend = start + v_forward*24;\r\n\ttraceline (start, end, TRUE, self);\r\n\tif (trace_fraction < 1)\r\n\t{\t// solid at waist\r\n\t\tstart_z = start_z + self.maxs_z - 8;\r\n\t\tend = start + v_forward*24;\r\n\t\tself.movedir = trace_plane_normal * -50;\r\n\t\ttraceline (start, end, TRUE, self);\r\n\t\tif (trace_fraction == 1)\r\n\t\t{\t// open at eye level\r\n\t\t\tself.flags = self.flags | FL_WATERJUMP;\r\n\t\t\tself.velocity_z = 225;\r\n\t\t\tself.flags = self.flags - (self.flags & FL_JUMPRELEASED);\r\n\t\t\tself.teleport_time = time + 2;\t// safety net\r\n\t\t\treturn;\r\n\t\t}\r\n\t}\r\n};*/\r\n\r\n\r\n/*\r\n================\r\nPlayerPreThink\r\n\r\nCalled every frame before physics are run\r\n================\r\n*/\r\nvoid() PlayerPreThink =\r\n{\r\n\tif (intermission_running)\r\n\t{\r\n\t\tIntermissionThink ();\t// otherwise a button could be missed between\r\n\t\treturn;\t\t\t\t\t// the think tics\r\n\t}\r\n\r\n\tif (self.view_ofs == '0 0 0')\r\n\t\treturn;\t\t// intermission or finale\r\n\r\n\tself.SendFlags = FULLSEND;\r\n\r\n\tmakevectors (self.v_angle);\t\t// is this still used\r\n\r\n\tCheckRules ();\r\n\tWaterMove ();\r\n\r\n//\tif (self.waterlevel == 2)\r\n//\t\tCheckWaterJump ();\r\n\r\n\tif (self.deadflag >= DEAD_DEAD)\r\n\t{\r\n\t\tPlayerDeathThink ();\r\n\t\treturn;\r\n\t}\r\n\t\r\n\tif (self.deadflag == DEAD_DYING)\r\n\t\treturn;\t// dying, so do nothing\r\n\r\n\tif (self.button2)\r\n\t{\r\n\t\tPlayerJump ();\r\n\t}\r\n\telse\r\n\t\tself.flags = self.flags | FL_JUMPRELEASED;\r\n\r\n// teleporters can force a non-moving pause time\t\r\n\tif (time < self.pausetime)\r\n\t\tself.velocity = '0 0 0';\r\n\r\n\tif(time > self.attack_finished && self.currentammo == 0 && self.weapon != IT_AXE)\r\n\t{\r\n\t\tself.weapon = W_BestWeapon ();\r\n\t\tW_SetCurrentAmmo ();\r\n\t}\r\n};\r\n\t\r\n/*\r\n================\r\nCheckPowerups\r\n\r\nCheck for turning off powerups\r\n================\r\n*/\r\nvoid() CheckPowerups =\r\n{\r\n\tif (self.health <= 0)\r\n\t\treturn;\r\n\r\n// invisibility\r\n\tif (self.invisible_finished)\r\n\t{\r\n// sound and screen flash when items starts to run out\r\n\t\tif (self.invisible_sound < time)\r\n\t\t{\r\n\t\t\tsound (self, CHAN_AUTO, \"items/inv3.wav\", 0.5, ATTN_IDLE);\r\n\t\t\tself.invisible_sound = time + ((random() * 3) + 1);\r\n\t\t}\r\n\r\n\r\n\t\tif (self.invisible_finished < time + 3)\r\n\t\t{\r\n\t\t\tif (self.invisible_time == 1)\r\n\t\t\t{\r\n\t\t\t\tsprint (self, \"Ring of Shadows magic is fading\\n\");\r\n\t\t\t\tstuffcmd (self, \"bf\\n\");\r\n\t\t\t\tsound (self, CHAN_AUTO, \"items/inv2.wav\", 1, ATTN_NORM);\r\n\t\t\t\tself.invisible_time = time + 1;\r\n\t\t\t}\r\n\t\t\t\r\n\t\t\tif (self.invisible_time < time)\r\n\t\t\t{\r\n\t\t\t\tself.invisible_time = time + 1;\r\n\t\t\t\tstuffcmd (self, \"bf\\n\");\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tif (self.invisible_finished < time)\r\n\t\t{\t// just stopped\r\n\t\t\tself.items = self.items - IT_INVISIBILITY;\r\n\t\t\tself.invisible_finished = 0;\r\n\t\t\tself.invisible_time = 0;\r\n\t\t}\r\n\t\t\r\n\t// use the eyes\r\n\t\tself.modelindex = modelindex_eyes;\r\n\t}\r\n\telse\r\n\t\tself.modelindex = modelindex_player;\t// don't use eyes\r\n\r\n// invincibility\r\n\tif (self.invincible_finished)\r\n\t{\r\n// sound and screen flash when items starts to run out\r\n\t\tif (self.invincible_finished < time + 3)\r\n\t\t{\r\n\t\t\tif (self.invincible_time == 1)\r\n\t\t\t{\r\n\t\t\t\tsprint (self, \"Protection is almost burned out\\n\");\r\n\t\t\t\tstuffcmd (self, \"bf\\n\");\r\n\t\t\t\tsound (self, CHAN_AUTO, \"items/protect2.wav\", 1, ATTN_NORM);\r\n\t\t\t\tself.invincible_time = time + 1;\r\n\t\t\t}\r\n\t\t\t\r\n\t\t\tif (self.invincible_time < time)\r\n\t\t\t{\r\n\t\t\t\tself.invincible_time = time + 1;\r\n\t\t\t\tstuffcmd (self, \"bf\\n\");\r\n\t\t\t}\r\n\t\t}\r\n\t\t\r\n\t\tif (self.invincible_finished < time)\r\n\t\t{\t// just stopped\r\n\t\t\tself.items = self.items - IT_INVULNERABILITY;\r\n\t\t\tself.invincible_time = 0;\r\n\t\t\tself.invincible_finished = 0;\r\n\t\t}\r\n\t\tif (self.invincible_finished > time)\r\n\t\t\tself.effects = self.effects | EF_DIMLIGHT|EF_RED;\r\n\t\telse\r\n\t\t\tself.effects = self.effects - (self.effects & (EF_DIMLIGHT|EF_RED));\r\n\t}\r\n\r\n// super damage\r\n\tif (self.super_damage_finished)\r\n\t{\r\n\r\n// sound and screen flash when items starts to run out\r\n\r\n\t\tif (self.super_damage_finished < time + 3)\r\n\t\t{\r\n\t\t\tif (self.super_time == 1)\r\n\t\t\t{\r\n\t\t\t\tsprint (self, \"Quad Damage is wearing off\\n\");\r\n\t\t\t\tstuffcmd (self, \"bf\\n\");\r\n\t\t\t\tsound (self, CHAN_AUTO, \"items/damage2.wav\", 1, ATTN_NORM);\r\n\t\t\t\tself.super_time = time + 1;\r\n\t\t\t}\t  \r\n\t\t\t\r\n\t\t\tif (self.super_time < time)\r\n\t\t\t{\r\n\t\t\t\tself.super_time = time + 1;\r\n\t\t\t\tstuffcmd (self, \"bf\\n\");\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tif (self.super_damage_finished < time)\r\n\t\t{\t// just stopped\r\n\t\t\tself.items = self.items - IT_QUAD;\r\n\t\t\tself.super_damage_finished = 0;\r\n\t\t\tself.super_time = 0;\r\n\t\t}\r\n\t\tif (self.super_damage_finished > time)\r\n\t\t\tself.effects = self.effects | EF_DIMLIGHT | EF_BLUE;\r\n\t\telse\r\n\t\t\tself.effects = self.effects - (self.effects & (EF_DIMLIGHT | EF_BLUE));\r\n\t}\t\r\n\r\n// suit\t\r\n\tif (self.radsuit_finished)\r\n\t{\r\n\t\tself.air_finished = time + 12;\t\t// don't drown\r\n\r\n// sound and screen flash when items starts to run out\r\n\t\tif (self.radsuit_finished < time + 3)\r\n\t\t{\r\n\t\t\tif (self.rad_time == 1)\r\n\t\t\t{\r\n\t\t\t\tsprint (self, \"Air supply in Biosuit expiring\\n\");\r\n\t\t\t\tstuffcmd (self, \"bf\\n\");\r\n\t\t\t\tsound (self, CHAN_AUTO, \"items/suit2.wav\", 1, ATTN_NORM);\r\n\t\t\t\tself.rad_time = time + 1;\r\n\t\t\t}\r\n\t\t\t\r\n\t\t\tif (self.rad_time < time)\r\n\t\t\t{\r\n\t\t\t\tself.rad_time = time + 1;\r\n\t\t\t\tstuffcmd (self, \"bf\\n\");\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tif (self.radsuit_finished < time)\r\n\t\t{\t// just stopped\r\n\t\t\tself.items = self.items - IT_SUIT;\r\n\t\t\tself.rad_time = 0;\r\n\t\t\tself.radsuit_finished = 0;\r\n\t\t}\r\n\t}\t\r\n\r\n};\r\n\r\n\r\n/*\r\n================\r\nPlayerPostThink\r\n\r\nCalled every frame after physics are run\r\n================\r\n*/\r\nvoid() PlayerPostThink =\r\n{\r\n\tif (self.view_ofs == '0 0 0')\r\n\t\treturn;\t\t// intermission or finale\r\n\tif (self.deadflag)\r\n\t\treturn;\r\n\t\t\r\n// do weapon stuff\r\n\r\n\tW_WeaponFrame ();\r\n\r\n// check to see if player landed and play landing sound\t\r\n\tif ((self.jump_flag < -300) && (self.flags & FL_ONGROUND) && (self.health > 0))\r\n\t{\r\n\t\tif (self.watertype == CONTENT_WATER)\r\n\t\t\tsound (self, CHAN_BODY, \"player/h2ojump.wav\", 1, ATTN_NORM);\r\n\t\telse if (self.jump_flag < -650)\r\n\t\t{\r\n\t\t\tself.deathtype = \"falling\";\r\n\t\t\tT_Damage (self, world, world, 5); \r\n\t\t\tsound (self, CHAN_VOICE, \"player/land2.wav\", 1, ATTN_NORM);\r\n\t\t}\r\n\t\telse\r\n\t\t\tsound (self, CHAN_VOICE, \"player/land.wav\", 1, ATTN_NORM);\r\n\r\n\t\tself.jump_flag = 0;\r\n\t}\r\n\r\n\tif (!(self.flags & FL_ONGROUND))\r\n\t\tself.jump_flag = self.velocity_z;\r\n\r\n\tCheckPowerups ();\r\n};\r\n\r\n\r\n/*\r\n===========\r\nClientConnect\r\n\r\ncalled when a player connects to a server\r\n============\r\n*/\r\nvoid() ClientConnect =\r\n{\r\n\tbprint (self.netname);\r\n\tbprint (\" entered the game\\n\");\r\n\t\r\n// a client connecting during an intermission can cause problems\r\n\tif (intermission_running)\r\n\t\tExitIntermission ();\r\n\r\n\t__using dimension_see;\t//csqc support checks are safe. just mute the splitscreen/mvd warnings.\r\n\tself.usingcsqc = stof(infokey(self, INFOKEY_P_CSQCACTIVE));\r\n\tif (self.usingcsqc)\r\n\t{\r\n\t\tself.dimension_see = DIMENSION_HASCSQC;\r\n\t\tsprint(self, \"Welcome to csqctest!\\n\");\r\n\t}\r\n\telse\r\n\t{\r\n\t\tself.dimension_see = DIMENSION_NOCSQC;\r\n\r\n\t\tbprint(self.netname);\r\n\t\tbprint(\" is not using csqc!\\n\");\r\n\t\tsprint(self, \"You are not using csqc.\\nThis could be due to version differences or only a partially installed mod.\\nIf you are using FTEQW, please type \\sallow_download_csprogs 1\\s in the console, and then reconnect.\\n\\nNote that certain incompatabilities may exist if you do not.\\n\");\r\n\t}\r\n};\r\n\r\n\r\n/*\r\n===========\r\nClientDisconnect\r\n\r\ncalled when a player disconnects from a server\r\n============\r\n*/\r\nvoid() ClientDisconnect =\r\n{\r\n\tif (gameover)\r\n\t\treturn;\r\n\t// if the level end trigger has been activated, just return\r\n\t// since they aren't *really* leaving\r\n\r\n\t// let everyone else know\r\n\tbprint (self.netname);\r\n\tbprint (\" left the game with \");\r\n\tbprint (ftos(self.frags));\r\n\tbprint (\" frags\\n\");\r\n\tsound (self, CHAN_BODY, \"player/tornoff2.wav\", 1, ATTN_NONE);\r\n\tset_suicide_frame ();\r\n\r\n\tself.SendEntity = __NULL__;\r\n};\r\n\r\nenum {\r\n\tGENDER_MALE,\r\n\tGENDER_FEMALE,\r\n\tGENDER_NEUTER\r\n};\r\nfloat(entity ent) GetGender =\r\n{\r\n\tlocal string s;\r\n\tif (checkbuiltin(infokey))\r\n\t{\r\n\t\ts = infokey(ent, \"s\");\r\n\t\tif (s == \"\" || s == \"m\" || s == \"male\" || s == \"yesplease\")\r\n\t\t\treturn GENDER_MALE;\r\n\t\tif (s == \"f\" || s == \"female\" || s == \"onthebeach\")\r\n\t\t\treturn GENDER_FEMALE;\r\n\t\treturn GENDER_NEUTER;\r\n\t}\r\n\telse\r\n\t\treturn GENDER_NEUTER;\r\n};\r\n\r\n/*\r\n===========\r\nClientObituary\r\n\r\ncalled when a player dies\r\n============\r\n*/\r\nvoid(entity targ, entity attacker) ClientObituary =\r\n{\r\n\tlocal\tfloat rnum, g;\r\n\tlocal\tstring deathstring, deathstring2;\r\n\trnum = random();\r\n\r\n\tif (targ.classname == \"player\")\r\n\t{\r\n\t\tif (attacker.classname == \"teledeath\")\r\n\t\t{\r\n\t\t\tbprint (targ.netname);\r\n\t\t\tbprint (\" was telefragged by \");\r\n\t\t\tbprint (attacker.owner.netname);\r\n\t\t\tbprint (\"\\n\");\r\n\r\n\t\t\tattacker.owner.frags = attacker.owner.frags + 1;\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\tif (attacker.classname == \"teledeath2\")\r\n\t\t{\r\n\t\t\tbprint (\"Satan's power deflects \");\r\n\t\t\tbprint (targ.netname);\r\n\t\t\tbprint (\"'s telefrag\\n\");\r\n\r\n\t\t\ttarg.frags = targ.frags - 1;\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\tif (attacker.classname == \"player\")\r\n\t\t{\r\n\t\t\tif (targ == attacker)\r\n\t\t\t{\r\n\t\t\t\t// killed self\r\n\t\t\t\tattacker.frags = attacker.frags - 1;\r\n\t\t\t\tbprint (targ.netname);\r\n\t\t\t\t\r\n\t\t\t\tif (targ.weapon == 64 && targ.waterlevel > 1)\r\n\t\t\t\t{\r\n\t\t\t\t\tbprint (\" discharges into the water.\\n\");\r\n\t\t\t\t\treturn;\r\n\t\t\t\t}\r\n\t\t\t\tif (targ.weapon == IT_GRENADE_LAUNCHER)\r\n\t\t\t\t\tbprint (\" tries to put the pin back in\\n\");\r\n\t\t\t\telse if (rnum < 0.1)\r\n\t\t\t\t{\r\n\t\t\t\t\tg = GetGender(attacker);\r\n\t\t\t\t\tif (g == GENDER_NEUTER)\r\n\t\t\t\t\t\tdeathstring = \" has a brain the size of a planet\\n\";\r\n\t\t\t\t\telse if (g == GENDER_FEMALE)\r\n\t\t\t\t\t\tdeathstring = \" smeared her makeup\\n\";\r\n\t\t\t\t\telse\r\n\t\t\t\t\t\tdeathstring = \" smeared the walls\\n\";\r\n\t\t\t\t\tbprint (deathstring);\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t\tbprint (\" becomes bored with life\\n\");\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\t\t\telse if ( (teamplay == 2) && (targ.team > 0)&&(targ.team == attacker.team) )\r\n\t\t\t{\r\n\t\t\t\tif (rnum < 0.25)\r\n\t\t\t\t\tdeathstring = \" mows down a teammate\\n\";\r\n\t\t\t\telse if (rnum < 0.50)\r\n\t\t\t\t{\r\n\t\t\t\t\tg = GetGender(attacker);\r\n\t\t\t\t\tif (g == GENDER_NEUTER)\r\n\t\t\t\t\t\tdeathstring = \" checks its glasses\\n\";\r\n\t\t\t\t\telse if (g == GENDER_FEMALE)\r\n\t\t\t\t\t\tdeathstring = \" checks her glasses\\n\";\r\n\t\t\t\t\telse\r\n\t\t\t\t\t\tdeathstring = \" checks his glasses\\n\";\r\n\t\t\t\t}\r\n\t\t\t\telse if (rnum < 0.75)\r\n\t\t\t\t\tdeathstring = \" gets a frag for the other team\\n\";\r\n\t\t\t\telse\r\n\t\t\t\t\tdeathstring = \" loses another friend\\n\";\r\n\t\t\t\tbprint (attacker.netname);\r\n\t\t\t\tbprint (deathstring);\r\n\t\t\t\tattacker.frags = attacker.frags - 1;\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tattacker.frags = attacker.frags + 1;\r\n\r\n\t\t\t\trnum = attacker.weapon;\r\n\t\t\t\tif (rnum == IT_AXE)\r\n\t\t\t\t{\r\n\t\t\t\t\tdeathstring = \" was ax-murdered by \";\r\n\t\t\t\t\tdeathstring2 = \"\\n\";\r\n\t\t\t\t}\r\n\t\t\t\telse if (rnum == IT_SHOTGUN)\r\n\t\t\t\t{\r\n\t\t\t\t\tdeathstring = \" chewed on \";\r\n\t\t\t\t\tdeathstring2 = \"'s boomstick\\n\";\r\n\t\t\t\t}\r\n\t\t\t\telse if (rnum == IT_SUPER_SHOTGUN)\r\n\t\t\t\t{\r\n\t\t\t\t\tdeathstring = \" ate 2 loads of \";\r\n\t\t\t\t\tdeathstring2 = \"'s buckshot\\n\";\r\n\t\t\t\t}\r\n\t\t\t\telse if (rnum == IT_NAILGUN)\r\n\t\t\t\t{\r\n\t\t\t\t\tdeathstring = \" was nailed by \";\r\n\t\t\t\t\tdeathstring2 = \"\\n\";\r\n\t\t\t\t}\r\n\t\t\t\telse if (rnum == IT_SUPER_NAILGUN)\r\n\t\t\t\t{\r\n\t\t\t\t\tdeathstring = \" was punctured by \";\r\n\t\t\t\t\tdeathstring2 = \"\\n\";\r\n\t\t\t\t}\r\n\t\t\t\telse if (rnum == IT_GRENADE_LAUNCHER)\r\n\t\t\t\t{\r\n\t\t\t\t\tdeathstring = \" eats \";\r\n\t\t\t\t\tdeathstring2 = \"'s pineapple\\n\";\r\n\t\t\t\t\tif (targ.health < -40)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tdeathstring = \" was gibbed by \";\r\n\t\t\t\t\t\tdeathstring2 = \"'s grenade\\n\";\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t\telse if (rnum == IT_ROCKET_LAUNCHER)\r\n\t\t\t\t{\r\n\t\t\t\t\tdeathstring = \" rides \";\r\n\t\t\t\t\tdeathstring2 = \"'s rocket\\n\";\r\n\t\t\t\t\tif (targ.health < -40)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tdeathstring = \" was gibbed by \";\r\n\t\t\t\t\t\tdeathstring2 = \"'s rocket\\n\" ;\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t\telse if (rnum == IT_LIGHTNING)\r\n\t\t\t\t{\r\n\t\t\t\t\tdeathstring = \" accepts \";\r\n\t\t\t\t\tif (attacker.waterlevel > 1)\r\n\t\t\t\t\t\tdeathstring2 = \"'s discharge\\n\";\r\n\t\t\t\t\telse\r\n\t\t\t\t\t\tdeathstring2 = \"'s shaft\\n\";\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\tdeathstring = \" was killed by \";\r\n\t\t\t\t\tdeathstring2 = \"\\n\";\r\n\t\t\t\t}\r\n\t\t\t\tbprint (targ.netname);\r\n\t\t\t\tbprint (deathstring);\r\n\t\t\t\tbprint (attacker.netname);\r\n\t\t\t\tbprint (deathstring2);\r\n\t\t\t}\r\n\t\t\treturn;\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\ttarg.frags = targ.frags - 1;\r\n\t\t\tbprint (targ.netname);\r\n\r\n\t\t\t// killed by a montser?\r\n\t\t\tif (attacker.flags & FL_MONSTER)\r\n\t\t\t{\r\n\t\t\t\tif (attacker.classname == \"monster_army\")\r\n\t\t\t\t\tbprint (\" was shot by a Grunt\\n\");\r\n\t\t\t\tif (attacker.classname == \"monster_demon1\")\r\n\t\t\t\t\tbprint (\" was eviscerated by a Fiend\\n\");\r\n\t\t\t\tif (attacker.classname == \"monster_dog\")\r\n\t\t\t\t\tbprint (\" was mauled by a Rottweiler\\n\");\r\n\t\t\t\tif (attacker.classname == \"monster_dragon\")\r\n\t\t\t\t\tbprint (\" was fried by a Dragon\\n\");\r\n\t\t\t\tif (attacker.classname == \"monster_enforcer\")\r\n\t\t\t\t\tbprint (\" was blasted by an Enforcer\\n\");\r\n\t\t\t\tif (attacker.classname == \"monster_fish\")\r\n\t\t\t\t\tbprint (\" was fed to the Rotfish\\n\");\r\n\t\t\t\tif (attacker.classname == \"monster_hell_knight\")\r\n\t\t\t\t\tbprint (\" was slain by a Death Knight\\n\");\r\n\t\t\t\tif (attacker.classname == \"monster_knight\")\r\n\t\t\t\t\tbprint (\" was slashed by a Knight\\n\");\r\n\t\t\t\tif (attacker.classname == \"monster_ogre\")\r\n\t\t\t\t\tbprint (\" was destroyed by an Ogre\\n\");\r\n\t\t\t\tif (attacker.classname == \"monster_oldone\")\r\n\t\t\t\t\tbprint (\" became one with Shub-Niggurath\\n\");\r\n\t\t\t\tif (attacker.classname == \"monster_shalrath\")\r\n\t\t\t\t\tbprint (\" was exploded by a Vore\\n\");\r\n\t\t\t\tif (attacker.classname == \"monster_shambler\")\r\n\t\t\t\t\tbprint (\" was smashed by a Shambler\\n\");\r\n\t\t\t\tif (attacker.classname == \"monster_tarbaby\")\r\n\t\t\t\t\tbprint (\" was slimed by a Spawn\\n\");\r\n\t\t\t\tif (attacker.classname == \"monster_vomit\")\r\n\t\t\t\t\tbprint (\" was vomited on by a Vomitus\\n\");\r\n\t\t\t\tif (attacker.classname == \"monster_wizard\")\r\n\t\t\t\t\tbprint (\" was scragged by a Scrag\\n\");\r\n\t\t\t\tif (attacker.classname == \"monster_zombie\")\r\n\t\t\t\t\tbprint (\" joins the Zombies\\n\");\r\n\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\t// tricks and traps\r\n\t\t\tif (attacker.classname == \"explo_box\")\r\n\t\t\t{\r\n\t\t\t\tbprint (\" blew up\\n\");\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\t\t\tif (attacker.solid == SOLID_BSP && attacker != world)\r\n\t\t\t{\t\r\n\t\t\t\tbprint (\" was squished\\n\");\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\t\t\tif (attacker.classname == \"trap_shooter\" || attacker.classname == \"trap_spikeshooter\")\r\n\t\t\t{\r\n\t\t\t\tbprint (\" was spiked\\n\");\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\t\t\tif (attacker.classname == \"fireball\")\r\n\t\t\t{\r\n\t\t\t\tbprint (\" ate a lavaball\\n\");\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\t\t\tif (attacker.classname == \"trigger_changelevel\")\r\n\t\t\t{\r\n\t\t\t\tbprint (\" tried to leave\\n\");\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\t// in-water deaths\r\n\t\t\trnum = targ.watertype;\r\n\t\t\tif (rnum == -3)\r\n\t\t\t{\r\n\t\t\t\tif (random() < 0.5)\r\n\t\t\t\t\tbprint (\" sleeps with the fishes\\n\");\r\n\t\t\t\telse\r\n\t\t\t\t\tbprint (\" sucks it down\\n\");\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\t\t\telse if (rnum == -4)\r\n\t\t\t{\r\n\t\t\t\tif (random() < 0.5)\r\n\t\t\t\t\tbprint (\" gulped a load of slime\\n\");\r\n\t\t\t\telse\r\n\t\t\t\t\tbprint (\" can't exist on slime alone\\n\");\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\t\t\telse if (rnum == -5)\r\n\t\t\t{\r\n\t\t\t\tif (targ.health < -15)\r\n\t\t\t\t{\r\n\t\t\t\t\tbprint (\" burst into flames\\n\");\r\n\t\t\t\t\treturn;\r\n\t\t\t\t}\r\n\t\t\t\tif (random() < 0.5)\r\n\t\t\t\t\tbprint (\" turned into hot slag\\n\");\r\n\t\t\t\telse\r\n\t\t\t\t\tbprint (\" visits the Volcano God\\n\");\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\t// fell to their death?\r\n\t\t\tif (targ.deathtype == \"falling\")\r\n\t\t\t{\r\n\t\t\t\ttarg.deathtype = \"\";\r\n\t\t\t\tg = GetGender(targ);\r\n\t\t\t\tif (g == GENDER_NEUTER)\r\n\t\t\t\t\tdeathstring = \" fell to its death\\n\";\r\n\t\t\t\telse if (g == GENDER_FEMALE)\r\n\t\t\t\t\tdeathstring = \" fell to her death\\n\";\r\n\t\t\t\telse\r\n\t\t\t\t\tdeathstring = \" fell to his death\\n\";\r\n\t\t\t\tbprint (deathstring);\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\t// hell if I know; he's just dead!!!\r\n\t\t\tbprint (\" died\\n\");\r\n\t\t}\r\n\t}\r\n};\r\n\r\nvoid() SV_RunClientCommand =\r\n{\r\n\tvector oorg = self.origin;\r\n\t//should match the one used by csqc.\r\n#ifdef OWNPLAYERPHYSICS\r\n\tPMove(self);\r\n#else\r\n\trunstandardplayerphysics(self);\r\n#endif\r\n\r\n\tif (input_buttons&32)\r\n\t{\t//some debugging info, to match up to the client. server's sequence will have no gaps.\r\n\t\tsprint(self, sprintf(\"\\safter \\sinput_sequence:%g, msecs:%g, aim:%v, move:%v\\n\", input_sequence, input_timelength*1000, input_angles, input_movevalues));\r\n\t\tsprint(self, sprintf(\"  origin: %v <- %v, velocity %v\\n\", self.origin, oorg, self.velocity));\r\n\t}\r\n};\r\n\r\nvoid(string cmd) SV_ParseClientCommand =\r\n{\r\n\ttokenize(cmd);\r\n\tswitch(argv(0))\r\n\t{\r\n\tcase \"give\":\t//we have our own additions/behaviours.\r\n\t\tGiveCommand_f();\r\n\t\tbreak;\r\n\r\n\t//case \"god\":\r\n\t//case \"noclip\":\r\n\t//case \"fly\":\r\n\t//case \"setpos\":\r\n\t//case \"notarget\":\r\n\t//case \"spiderpig\":\r\n\t//case \"6dof\":\r\n\tdefault:\t//fallback. let the engine handle it.\r\n\t\tclientcommand(self, cmd);\r\n\t\tbreak;\r\n\t}\r\n};\r\n\r\n"
  },
  {
    "path": "quakec/csqctest/src/ss/combat.qc",
    "content": "\r\nvoid() T_MissileTouch;\r\nvoid() info_player_start;\r\nvoid(entity targ, entity attacker) ClientObituary;\r\n\r\nvoid() monster_death_use;\r\n\r\n//============================================================================\r\n\r\n/*\r\n============\r\nCanDamage\r\n\r\nReturns true if the inflictor can directly damage the target.  Used for\r\nexplosions and melee attacks.\r\n============\r\n*/\r\nfloat(entity targ, entity inflictor) CanDamage =\r\n{\r\n// bmodels need special checking because their origin is 0,0,0\r\n\tif (targ.movetype == MOVETYPE_PUSH)\r\n\t{\r\n\t\ttraceline(inflictor.origin, 0.5 * (targ.absmin + targ.absmax), TRUE, self);\r\n\t\tif (trace_fraction == 1)\r\n\t\t\treturn TRUE;\r\n\t\tif (trace_ent == targ)\r\n\t\t\treturn TRUE;\r\n\t\treturn FALSE;\r\n\t}\r\n\t\r\n\ttraceline(inflictor.origin, targ.origin, TRUE, self);\r\n\tif (trace_fraction == 1)\r\n\t\treturn TRUE;\r\n\ttraceline(inflictor.origin, targ.origin + '15 15 0', TRUE, self);\r\n\tif (trace_fraction == 1)\r\n\t\treturn TRUE;\r\n\ttraceline(inflictor.origin, targ.origin + '-15 -15 0', TRUE, self);\r\n\tif (trace_fraction == 1)\r\n\t\treturn TRUE;\r\n\ttraceline(inflictor.origin, targ.origin + '-15 15 0', TRUE, self);\r\n\tif (trace_fraction == 1)\r\n\t\treturn TRUE;\r\n\ttraceline(inflictor.origin, targ.origin + '15 -15 0', TRUE, self);\r\n\tif (trace_fraction == 1)\r\n\t\treturn TRUE;\r\n\r\n\treturn FALSE;\r\n};\r\n\r\n\r\n/*\r\n============\r\nKilled\r\n============\r\n*/\r\nvoid(entity targ, entity attacker) Killed =\r\n{\r\n\tlocal entity oself;\r\n\r\n\toself = self;\r\n\tself = targ;\r\n\t\r\n\tif (self.health < -99)\r\n\t\tself.health = -99;\t\t// don't let sbar look bad if a player\r\n\r\n\tif (self.movetype == MOVETYPE_PUSH || self.movetype == MOVETYPE_NONE)\r\n\t{\t// doors, triggers, etc\r\n\t\tself.th_die ();\r\n\t\tself = oself;\r\n\t\treturn;\r\n\t}\r\n\r\n\tself.enemy = attacker;\r\n\r\n// bump the monster counter\r\n\tif (self.flags & FL_MONSTER)\r\n\t{\r\n\t\tkilled_monsters = killed_monsters + 1;\r\n\t\tWriteByte (MSG_ALL, SVC_KILLEDMONSTER);\r\n\t}\r\n\r\n\tClientObituary(self, attacker);\r\n\t\r\n\tself.takedamage = DAMAGE_NO;\r\n\tself.touch = SUB_Null;\r\n\r\n\tmonster_death_use();\r\n\tself.th_die ();\r\n\t\r\n\tself = oself;\r\n};\r\n\r\n\r\n/*\r\n============\r\nT_Damage\r\n\r\nThe damage is coming from inflictor, but get mad at attacker\r\nThis should be the only function that ever reduces health.\r\n============\r\n*/\r\nvoid(entity targ, entity inflictor, entity attacker, float damage) T_Damage=\r\n{\r\n\tlocal\tvector\tdir;\r\n\tlocal\tentity\toldself;\r\n\tlocal\tfloat\tsave;\r\n\tlocal\tfloat\ttake;\r\n\r\n\tif (!targ.takedamage)\r\n\t\treturn;\r\n\r\n// used by buttons and triggers to set activator for target firing\r\n\tdamage_attacker = attacker;\r\n\r\n// check for quad damage powerup on the attacker\r\n\tif (attacker.super_damage_finished > time)\r\n\t\tdamage = damage * 4;\r\n\r\n// save damage based on the target's armor level\r\n\r\n\tsave = ceil(targ.armortype*damage);\r\n\tif (save >= targ.armorvalue)\r\n\t{\r\n\t\tsave = targ.armorvalue;\r\n\t\ttarg.armortype = 0;\t// lost all armor\r\n\t\ttarg.items = targ.items - (targ.items & (IT_ARMOR1 | IT_ARMOR2 | IT_ARMOR3));\r\n\t}\r\n\t\r\n\ttarg.armorvalue = targ.armorvalue - save;\r\n\ttake = ceil(damage-save);\r\n\r\n// add to the damage total for clients, which will be sent as a single\r\n// message at the end of the frame\r\n// FIXME: remove after combining shotgun blasts?\r\n\tif (targ.flags & FL_CLIENT)\r\n\t{\r\n\t\ttarg.dmg_take = targ.dmg_take + take;\r\n\t\ttarg.dmg_save = targ.dmg_save + save;\r\n\t\ttarg.dmg_inflictor = inflictor;\r\n\t}\r\n\r\n// figure momentum add\r\n\tif ( (inflictor != world) && (targ.movetype == MOVETYPE_WALK) )\r\n\t{\r\n\t\tdir = targ.origin - (inflictor.absmin + inflictor.absmax) * 0.5;\r\n\t\tdir = normalize(dir);\r\n\t\ttarg.velocity = targ.velocity + dir*damage*8;\r\n\t}\r\n\r\n// check for godmode or invincibility\r\n\tif (targ.flags & FL_GODMODE)\r\n\t\treturn;\r\n\tif (targ.invincible_finished >= time)\r\n\t{\r\n\t\tif (self.invincible_sound < time)\r\n\t\t{\r\n\t\t\tsound (targ, CHAN_ITEM, \"items/protect3.wav\", 1, ATTN_NORM);\r\n\t\t\tself.invincible_sound = time + 2;\r\n\t\t}\r\n\t\treturn;\r\n\t}\r\n\r\n// team play damage avoidance\r\n\tif ( (teamplay == 1) && (targ.team > 0)&&(targ.team == attacker.team) )\r\n\t\treturn;\r\n\t\t\r\n// do the damage\r\n\ttarg.health = targ.health - take;\r\n\t\t\t\r\n\tif (targ.health <= 0)\r\n\t{\r\n\t\tKilled (targ, attacker);\r\n\t\treturn;\r\n\t}\r\n\r\n// react to the damage\r\n\toldself = self;\r\n\tself = targ;\r\n\r\n\tif ( (self.flags & FL_MONSTER) && attacker != world)\r\n\t{\r\n\t// get mad unless of the same class (except for soldiers)\r\n\t\tif (self != attacker && attacker != self.enemy)\r\n\t\t{\r\n\t\t\tif ( (self.classname != attacker.classname) \r\n\t\t\t|| (self.classname == \"monster_army\" ) )\r\n\t\t\t{\r\n\t\t\t\tif (self.enemy.classname == \"player\")\r\n\t\t\t\t\tself.oldenemy = self.enemy;\r\n\t\t\t\tself.enemy = attacker;\r\n\t\t\t\tFoundTarget ();\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\tif (self.th_pain)\r\n\t{\r\n\t\tself.th_pain (attacker, take);\r\n\t// nightmare mode monsters don't go into pain frames often\r\n\t\tif (skill == 3)\r\n\t\t\tself.pain_finished = time + 5;\t\t\r\n\t}\r\n\r\n\tself = oldself;\r\n};\r\n\r\n/*\r\n============\r\nT_RadiusDamage\r\n============\r\n*/\r\nvoid(entity inflictor, entity attacker, float damage, entity ignore) T_RadiusDamage =\r\n{\r\n\tlocal\tfloat \tpoints;\r\n\tlocal\tentity\thead;\r\n\tlocal\tvector\torg;\r\n\r\n\thead = findradius(inflictor.origin, damage+40);\r\n\t\r\n\twhile (head)\r\n\t{\r\n\t\tif (head != ignore)\r\n\t\t{\r\n\t\t\tif (head.takedamage)\r\n\t\t\t{\r\n\t\t\t\torg = head.origin + (head.mins + head.maxs)*0.5;\r\n\t\t\t\tpoints = 0.5*vlen (inflictor.origin - org);\r\n\t\t\t\tif (points < 0)\r\n\t\t\t\t\tpoints = 0;\r\n\t\t\t\tpoints = damage - points;\r\n\t\t\t\tif (head == attacker)\r\n\t\t\t\t\tpoints = points * 0.5;\r\n\t\t\t\tif (points > 0)\r\n\t\t\t\t{\r\n\t\t\t\t\tif (CanDamage (head, inflictor))\r\n\t\t\t\t\t{\t// shambler takes half damage from all explosions\r\n\t\t\t\t\t\tif (head.classname == \"monster_shambler\")\t\t\t\t\t\t\r\n\t\t\t\t\t\t\tT_Damage (head, inflictor, attacker, points*0.5);\r\n\t\t\t\t\t\telse\r\n\t\t\t\t\t\t\tT_Damage (head, inflictor, attacker, points);\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t\thead = head.chain;\r\n\t}\r\n};\r\n\r\n/*\r\n============\r\nT_BeamDamage\r\n============\r\n*/\r\n/*void(entity attacker, float damage) T_BeamDamage =\r\n{\r\n\tlocal\tfloat \tpoints;\r\n\tlocal\tentity\thead;\r\n\t\r\n\thead = findradius(attacker.origin, damage+40);\r\n\t\r\n\twhile (head)\r\n\t{\r\n\t\tif (head.takedamage)\r\n\t\t{\r\n\t\t\tpoints = 0.5*vlen (attacker.origin - head.origin);\r\n\t\t\tif (points < 0)\r\n\t\t\t\tpoints = 0;\r\n\t\t\tpoints = damage - points;\r\n\t\t\tif (head == attacker)\r\n\t\t\t\tpoints = points * 0.5;\r\n\t\t\tif (points > 0)\r\n\t\t\t{\r\n\t\t\t\tif (CanDamage (head, attacker))\r\n\t\t\t\t{\r\n\t\t\t\t\tif (head.classname == \"monster_shambler\")\t\t\t\t\t\t\r\n\t\t\t\t\t\tT_Damage (head, attacker, attacker, points*0.5);\r\n\t\t\t\t\telse\r\n\t\t\t\t\t\tT_Damage (head, attacker, attacker, points);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t\thead = head.chain;\r\n\t}\r\n};*/\r\n\r\n"
  },
  {
    "path": "quakec/csqctest/src/ss/defs.qc",
    "content": "\r\n/*\r\n==============================================================================\r\n\r\n\t\t\tSOURCE FOR GLOBALVARS_T C STRUCTURE\r\n\r\n==============================================================================\r\n*/\r\n\r\n//\r\n// system globals\r\n//\r\nentity\t\tself;\r\nentity\t\tother;\r\nentity\t\tworld;\r\nfloat\t\ttime;\r\nfloat\t\tframetime;\r\n\r\nfloat\t\tforce_retouch;\t\t// force all entities to touch triggers\r\n\t\t\t\t\t\t\t\t// next frame.  this is needed because\r\n\t\t\t\t\t\t\t\t// non-moving things don't normally scan\r\n\t\t\t\t\t\t\t\t// for triggers, and when a trigger is\r\n\t\t\t\t\t\t\t\t// created (like a teleport trigger), it\r\n\t\t\t\t\t\t\t\t// needs to catch everything.\r\n\t\t\t\t\t\t\t\t// decremented each frame, so set to 2\r\n\t\t\t\t\t\t\t\t// to guarantee everything is touched\r\nstring\t\tmapname;\r\n\r\nfloat\t\tdeathmatch;\r\nfloat\t\tcoop;\r\nfloat\t\tteamplay;\r\n\r\nfloat\t\tserverflags;\t\t// propagated from level to level, used to\r\n\t\t\t\t\t\t\t\t// keep track of completed episodes\r\n\r\nfloat\t\ttotal_secrets;\r\nfloat\t\ttotal_monsters;\r\n\r\nfloat\t\tfound_secrets;\t\t// number of secrets found\r\nfloat\t\tkilled_monsters;\t// number of monsters killed\r\n\r\n\r\n// spawnparms are used to encode information about clients across server\r\n// level changes\r\nfloat\t\tparm1, parm2, parm3, parm4, parm5, parm6, parm7, parm8, parm9, parm10, parm11, parm12, parm13, parm14, parm15, parm16;\r\n\r\n//\r\n// global variables set by built in functions\r\n//\t\r\nvector\t\tv_forward, v_up, v_right;\t// set by makevectors()\r\n\t\r\n// set by traceline / tracebox\r\nfloat\t\ttrace_allsolid;\r\nfloat\t\ttrace_startsolid;\r\nfloat\t\ttrace_fraction;\r\nvector\t\ttrace_endpos;\r\nvector\t\ttrace_plane_normal;\r\nfloat\t\ttrace_plane_dist;\r\nentity\t\ttrace_ent;\r\nfloat\t\ttrace_inopen;\r\nfloat\t\ttrace_inwater;\r\n\r\nentity\t\tmsg_entity;\t\t\t\t// destination of single entity writes\r\n\r\n//\r\n// required prog functions\r\n//\r\nvoid() \t\tmain;\t\t\t\t\t\t// only for testing\r\n\r\nvoid()\t\tStartFrame;\r\n\r\nvoid() \t\tPlayerPreThink;\r\nvoid() \t\tPlayerPostThink;\r\n\r\nvoid()\t\tClientKill;\r\nvoid()\t\tClientConnect;\r\nvoid() \t\tPutClientInServer;\t\t// call after setting the parm1... parms\r\nvoid()\t\tClientDisconnect;\r\n\r\nvoid()\t\tSetNewParms;\t\t\t// called when a client first connects to\r\n\t\t\t\t\t\t\t\t\t// a server. sets parms so they can be\r\n\t\t\t\t\t\t\t\t\t// saved off for restarts\r\n\r\nvoid()\t\tSetChangeParms;\t\t\t// call to set parms for self so they can\r\n\t\t\t\t\t\t\t\t\t// be saved for a level transition\r\n\r\n\r\n//================================================\r\nvoid\t\tend_sys_globals;\t\t// flag for structure dumping\r\n//================================================\r\n\r\n/*\r\n==============================================================================\r\n\r\n\t\t\tSOURCE FOR ENTVARS_T C STRUCTURE\r\n\r\n==============================================================================\r\n*/\r\n\r\n//\r\n// system fields (*** = do not set in prog code, maintained by C code)\r\n//\r\n.float\t\tmodelindex;\t\t// *** model index in the precached list\r\n.vector\t\tabsmin, absmax;\t// *** origin + mins / maxs\r\n\r\n.float\t\tltime;\t\t\t// local time for entity\r\n.float\t\tmovetype;\r\n.float\t\tsolid;\r\n\r\n.vector\t\torigin;\t\t\t// ***\r\n.vector\t\toldorigin;\t\t// ***\r\n.vector\t\tvelocity;\r\n.vector\t\tangles;\r\n.vector\t\tavelocity;\r\n\r\n.vector\t\tpunchangle;\t\t// temp angle adjust from damage or recoil\r\n\r\n.string\t\tclassname;\t\t// spawn function\r\n.string\t\tmodel;\r\n.float\t\tframe;\r\n.float\t\tskin;\r\n.float\t\teffects;\r\n\r\n.vector\t\tmins, maxs;\t\t// bounding box extents reletive to origin\r\n.vector\t\tsize;\t\t\t// maxs - mins\r\n\r\n.void()\t\ttouch;\r\n.void()\t\tuse;\r\n.void()\t\tthink;\r\n.void()\t\tblocked;\t\t// for doors or plats, called when can't push other\r\n\r\n.float\t\tnextthink;\r\n.entity\t\tgroundentity;\r\n\r\n// stats\r\n.float\t\thealth;\r\n.float\t\tfrags;\r\n.float\t\tweapon;\t\t\t// one of the IT_SHOTGUN, etc flags\r\n.string\t\tweaponmodel;\r\n.float\t\tweaponframe;\r\n.float\t\tcurrentammo;\r\n.float\t\tammo_shells, ammo_nails, ammo_rockets, ammo_cells;\r\n\r\n.float\t\titems;\t\t\t// bit flags\r\n\r\n.float\t\ttakedamage;\r\n.entity\t\tchain;\r\n.float\t\tdeadflag;\r\n\r\n.vector\t\tview_ofs;\t\t\t// add to origin to get eye point\r\n\r\n\r\n.float\t\tbutton0;\t\t// fire\r\n.float\t\tbutton1;\t\t// use\r\n.float\t\tbutton2;\t\t// jump\r\n\r\n.float\t\timpulse;\t\t// weapon changes\r\n\r\n.float\t\tfixangle;\r\n.vector\t\tv_angle;\t\t// view / targeting angle for players\r\n.float\t\tidealpitch;\t\t// calculated pitch angle for lookup up slopes\r\n\r\n\r\n.string\t\tnetname;\r\n\r\n.entity \tenemy;\r\n\r\n.float\t\tflags;\r\n\r\n.float\t\tcolormap;\r\n.float\t\tteam;\r\n\r\n.float\t\tmax_health;\t\t// players maximum health is stored here\r\n\r\n.float\t\tteleport_time;\t// don't back up\r\n\r\n.float\t\tarmortype;\t\t// save this fraction of incoming damage\r\n.float\t\tarmorvalue;\r\n\r\n.float\t\twaterlevel;\t\t// 0 = not in, 1 = feet, 2 = wast, 3 = eyes\r\n.float\t\twatertype;\t\t// a contents value\r\n\r\n.float\t\tideal_yaw;\r\n.float\t\tyaw_speed;\r\n\r\n.entity\t\taiment;\r\n\r\n.entity \tgoalentity;\t\t// a movetarget or an enemy\r\n\r\n.float\t\tspawnflags;\r\n\r\n.string\t\ttarget;\r\n.string\t\ttargetname;\r\n\r\n// damage is accumulated through a frame. and sent as one single\r\n// message, so the super shotgun doesn't generate huge messages\r\n.float\t\tdmg_take;\r\n.float\t\tdmg_save;\r\n.entity\t\tdmg_inflictor;\r\n\r\n.entity\t\towner;\t\t// who launched a missile\r\n.vector\t\tmovedir;\t// mostly for doors, but also used for waterjump\r\n\r\n.string\t\tmessage;\t\t// trigger messages\r\n\r\n.float\t\tsounds;\t\t// either a cd track number or sound number\r\n\r\n.string\t\tnoise, noise1, noise2, noise3;\t// contains names of wavs to play\r\n\r\n//================================================\r\nvoid\t\tend_sys_fields;\t\t\t// flag for structure dumping\r\n//================================================\r\n\r\n/*\r\n==============================================================================\r\n\r\n\t\t\t\tVARS NOT REFERENCED BY C CODE\r\n\r\n==============================================================================\r\n*/\r\n\r\n\r\n//\r\n// constants\r\n//\r\n\r\n\r\n// range values\r\nfloat\tRANGE_MELEE\t\t\t\t= 0;\r\nfloat\tRANGE_NEAR\t\t\t\t= 1;\r\nfloat\tRANGE_MID\t\t\t\t= 2;\r\nfloat\tRANGE_FAR\t\t\t\t= 3;\r\n\r\n// deadflag values\r\n\r\nfloat\tDEAD_NO\t\t\t\t\t= 0;\r\nfloat\tDEAD_DYING\t\t\t\t= 1;\r\nfloat\tDEAD_DEAD\t\t\t\t= 2;\r\nfloat\tDEAD_RESPAWNABLE\t\t= 3;\r\n\r\n// takedamage values\r\n\r\nfloat\tDAMAGE_NO\t\t\t\t= 0;\r\nfloat\tDAMAGE_YES\t\t\t\t= 1;\r\nfloat\tDAMAGE_AIM\t\t\t\t= 2;\r\n\r\n// point content values\r\n\r\nfloat\tCONTENT_EMPTY\t\t\t= -1;\r\nfloat\tCONTENT_SOLID\t\t\t= -2;\r\nfloat\tCONTENT_WATER\t\t\t= -3;\r\nfloat\tCONTENT_SLIME\t\t\t= -4;\r\nfloat\tCONTENT_LAVA\t\t\t= -5;\r\nfloat\tCONTENT_SKY\t\t\t\t= -6;\r\n\r\nfloat\tSTATE_TOP\t\t= 0;\r\nfloat\tSTATE_BOTTOM\t= 1;\r\nfloat\tSTATE_UP\t\t= 2;\r\nfloat\tSTATE_DOWN\t\t= 3;\r\n\r\n// protocol bytes\r\nfloat\tSVC_TEMPENTITY\t\t= 23;\r\nfloat\tSVC_KILLEDMONSTER\t= 27;\r\nfloat\tSVC_FOUNDSECRET\t\t= 28;\r\nfloat\tSVC_INTERMISSION\t= 30;\r\nfloat\tSVC_FINALE\t\t\t= 31;\r\nfloat\tSVC_CDTRACK\t\t\t= 32;\r\nfloat\tSVC_SELLSCREEN\t\t= 33;\r\n\r\n\r\nfloat\tTE_SPIKE\t\t= 0;\r\nfloat\tTE_SUPERSPIKE\t= 1;\r\nfloat\tTE_GUNSHOT\t\t= 2;\r\nfloat\tTE_EXPLOSION\t= 3;\r\nfloat\tTE_TAREXPLOSION\t= 4;\r\nfloat\tTE_LIGHTNING1\t= 5;\r\nfloat\tTE_LIGHTNING2\t= 6;\r\nfloat\tTE_WIZSPIKE\t\t= 7;\r\nfloat\tTE_KNIGHTSPIKE\t= 8;\r\nfloat\tTE_LIGHTNING3\t= 9;\r\nfloat\tTE_LAVASPLASH\t= 10;\r\nfloat\tTE_TELEPORT\t\t= 11;\r\n\r\n// sound channels\r\n// channel 0 never willingly overrides\r\n// other channels (1-7) allways override a playing sound on that channel\r\nfloat\tCHAN_AUTO\t\t= 0;\r\nfloat\tCHAN_WEAPON\t\t= 1;\r\nfloat\tCHAN_VOICE\t\t= 2;\r\nfloat\tCHAN_ITEM\t\t= 3;\r\nfloat\tCHAN_BODY\t\t= 4;\r\n\r\nfloat\tATTN_NONE\t\t= 0;\r\nfloat\tATTN_NORM\t\t= 1;\r\nfloat\tATTN_IDLE\t\t= 2;\r\nfloat\tATTN_STATIC\t\t= 3;\r\n\r\n// update types\r\n\r\nfloat\tUPDATE_GENERAL\t= 0;\r\nfloat\tUPDATE_STATIC\t= 1;\r\nfloat\tUPDATE_BINARY\t= 2;\r\nfloat\tUPDATE_TEMP\t\t= 3;\r\n\r\n// entity effects\r\n\r\nfloat\tEF_BRIGHTFIELD\t= 1;\r\nfloat\tEF_MUZZLEFLASH \t= 2;\r\nfloat\tEF_BRIGHTLIGHT \t= 4;\r\nfloat\tEF_DIMLIGHT \t= 8;\r\n\r\n\r\n// messages\r\nfloat\tMSG_BROADCAST\t= 0;\t\t// unreliable to all\r\nfloat\tMSG_ONE\t\t\t= 1;\t\t// reliable to one (msg_entity)\r\nfloat\tMSG_ALL\t\t\t= 2;\t\t// reliable to all\r\nfloat\tMSG_INIT\t\t= 3;\t\t// write to the init string\r\n\r\n//================================================\r\n\r\n//\r\n// globals\r\n//\r\nfloat\tmovedist;\r\nfloat\tgameover;\t\t// set when a rule exits\r\n\r\nstring\tstring_null;\t// null string, nothing should be held here\r\n\r\nentity\tnewmis;\t\t\t// launch_spike sets this after spawning it\r\n\r\nentity\tactivator;\t\t// the entity that activated a trigger or brush\r\n\r\nentity\tdamage_attacker;\t// set by T_Damage\r\nfloat\tframecount;\r\n\r\nfloat\t\tskill;\r\n\r\n//================================================\r\n\r\n//\r\n// world fields (FIXME: make globals)\r\n//\r\nnoref .string\t\twad;\r\n.string \tmap;\r\n.float\t\tworldtype;\t// 0=medieval 1=metal 2=base\r\n\r\n//================================================\r\n\r\n.string\t\tkilltarget;\r\n\r\n//\r\n// quakeed fields\r\n//\r\nnoref .float\t\tlight_lev;\t\t// not used by game, but parsed by light util\r\n.float\t\tstyle;\r\n\r\n\r\n//\r\n// monster ai\r\n//\r\n.void()\t\tth_stand;\r\n.void()\t\tth_walk;\r\n.void()\t\tth_run;\r\n.void()\t\tth_missile;\r\n.void()\t\tth_melee;\r\n.void(entity attacker, float damage)\t\tth_pain;\r\n.void()\t\tth_die;\r\n\r\n.entity\t\toldenemy;\t\t// mad at this player before taking damage\r\n\r\n.float\t\tspeed;\r\n\r\n.float\tlefty;\r\n\r\n.float\tsearch_time;\r\n.float\tattack_state;\r\n\r\nfloat\tAS_STRAIGHT\t\t= 1;\r\nfloat\tAS_SLIDING\t\t= 2;\r\nfloat\tAS_MELEE\t\t= 3;\r\nfloat\tAS_MISSILE\t\t= 4;\r\n\r\n//\r\n// player only fields\r\n//\r\n.float\t\twalkframe;\r\n\r\n.float \t\tattack_finished;\r\n.float\t\tpain_finished;\r\n\r\n.float\t\tinvincible_finished;\r\n.float\t\tinvisible_finished;\r\n.float\t\tsuper_damage_finished;\r\n.float\t\tradsuit_finished;\r\n\r\n.float\t\tinvincible_time, invincible_sound;\r\n.float\t\tinvisible_time, invisible_sound;\r\n.float\t\tsuper_time, super_sound;\r\n.float\t\trad_time;\r\n.float\t\tfly_sound;\r\n\r\n.float\t\taxhitme;\r\n\r\n.float\t\tshow_hostile;\t// set to time+0.2 whenever a client fires a\r\n\t\t\t\t\t\t\t// weapon or takes damage.  Used to alert\r\n\t\t\t\t\t\t\t// monsters that otherwise would let the player go\r\n.float\t\tjump_flag;\t\t// player jump flag\r\n.float\t\tswim_flag;\t\t// player swimming sound flag\r\n.float\t\tair_finished;\t// when time > air_finished, start drowning\r\n.float\t\tbubble_count;\t// keeps track of the number of bubbles\r\n.string\t\tdeathtype;\t\t// keeps track of how the player died\r\n\r\n//\r\n// object stuff\r\n//\r\n.string\t\tmdl;\r\n.vector\t\tmangle;\t\t\t// angle at start\r\n\r\n.vector\t\toldorigin;\t\t// only used by secret door\r\n\r\n.float\t\tt_length, t_width;\r\n\r\n\r\n//\r\n// doors, etc\r\n//\r\n.vector\t\tdest1, dest2;\r\n.float\t\twait;\t\t\t// time from firing to restarting\r\n.float\t\tdelay;\t\t\t// time from activation to firing\r\n.entity\t\ttrigger_field;\t// door's trigger entity\r\n.string\t\tnoise4;\r\n\r\n//\r\n// monsters\r\n//\r\n.float \t\tpausetime;\r\n.entity \tmovetarget;\r\n\r\n\r\n//\r\n// doors\r\n//\r\n.float\t\taflag;\r\n.float\t\tdmg;\t\t\t// damage done by door when hit\r\n\t\r\n//\r\n// misc\r\n//\r\n.float\t\tcnt; \t\t\t// misc flag\r\n\t\r\n//\r\n// subs\r\n//\r\n.void()\t\tthink1;\r\n.vector\t\tfinaldest, finalangle;\r\n\r\n//\r\n// triggers\r\n//\r\n.float\t\tcount;\t\t\t// for counting triggers\r\n\r\n\r\n//\r\n// plats / doors / buttons\r\n//\r\n.float\t\tlip;\r\n.float\t\tstate;\r\n.vector\t\tpos1, pos2;\t\t// top and bottom positions\r\n.float\t\theight;\r\n\r\n//\r\n// sounds\r\n//\r\n.float\t\twaitmin;\r\n\r\n\r\n\r\n//============================================================================\r\n\r\n//\r\n// subs.qc\r\n//\r\nvoid(vector tdest, float tspeed, void() func) SUB_CalcMove;\r\nvoid(entity ent, vector tdest, float tspeed, void() func) SUB_CalcMoveEnt;\r\nvoid(vector destangle, float tspeed, void() func) SUB_CalcAngleMove;\r\nvoid()  SUB_CalcMoveDone;\r\nvoid() SUB_CalcAngleMoveDone;\r\nvoid() SUB_Null;\r\nvoid() SUB_UseTargets;\r\nvoid() SUB_Remove;\r\n\r\n//\r\n//\tcombat.qc\r\n//\r\nvoid(entity targ, entity inflictor, entity attacker, float damage) T_Damage;\r\n\r\n\r\nfloat (entity e, float healamount, float ignore) T_Heal; // health function\r\n\r\nfloat(entity targ, entity inflictor) CanDamage;\r\n\r\n\r\nfloat serverusingcsqc;\r\nfloat isdp;\r\n\r\n#define FULLSEND 0xffffff\r\n\r\n#ifdef QWSSQC\r\n#define sprint(pl,...) sprint(pl, PRINT_HIGH, __VA_ARGS__)\r\n#define bprint(...) bprint(PRINT_HIGH, __VA_ARGS__)\r\n#endif\r\n\r\n#define DIMENSION_NOCSQC\t1\r\n#define DIMENSION_HASCSQC\t2\r\n#define DIMENSION_DEFAULT\t255\r\n\r\n"
  },
  {
    "path": "quakec/csqctest/src/ss/demon.qc",
    "content": "/*\r\n==============================================================================\r\n\r\nDEMON\r\n\r\n==============================================================================\r\n*/\r\n\r\n$cd id1/models/demon3\r\n$scale\t0.8\r\n$origin 0 0 24\r\n$base base\r\n$skin base\r\n\r\n$frame stand1 stand2 stand3 stand4 stand5 stand6 stand7 stand8 stand9\r\n$frame stand10 stand11 stand12 stand13\r\n\r\n$frame walk1 walk2 walk3 walk4 walk5 walk6 walk7 walk8\r\n\r\n$frame run1 run2 run3 run4 run5 run6\r\n\r\n$frame leap1 leap2 leap3 leap4 leap5 leap6 leap7 leap8 leap9 leap10\r\n$frame leap11 leap12\r\n\r\n$frame pain1 pain2 pain3 pain4 pain5 pain6\r\n\r\n$frame death1 death2 death3 death4 death5 death6 death7 death8 death9\r\n\r\n$frame attacka1 attacka2 attacka3 attacka4 attacka5 attacka6 attacka7 attacka8\r\n$frame attacka9 attacka10 attacka11 attacka12 attacka13 attacka14 attacka15\r\n\r\n//============================================================================\r\n\r\nvoid()\tDemon_JumpTouch;\r\n\r\nvoid()\tdemon1_stand1\t=[\t$stand1,\tdemon1_stand2\t] {ai_stand();};\r\nvoid()\tdemon1_stand2\t=[\t$stand2,\tdemon1_stand3\t] {ai_stand();};\r\nvoid()\tdemon1_stand3\t=[\t$stand3,\tdemon1_stand4\t] {ai_stand();};\r\nvoid()\tdemon1_stand4\t=[\t$stand4,\tdemon1_stand5\t] {ai_stand();};\r\nvoid()\tdemon1_stand5\t=[\t$stand5,\tdemon1_stand6\t] {ai_stand();};\r\nvoid()\tdemon1_stand6\t=[\t$stand6,\tdemon1_stand7\t] {ai_stand();};\r\nvoid()\tdemon1_stand7\t=[\t$stand7,\tdemon1_stand8\t] {ai_stand();};\r\nvoid()\tdemon1_stand8\t=[\t$stand8,\tdemon1_stand9\t] {ai_stand();};\r\nvoid()\tdemon1_stand9\t=[\t$stand9,\tdemon1_stand10\t] {ai_stand();};\r\nvoid()\tdemon1_stand10\t=[\t$stand10,\tdemon1_stand11\t] {ai_stand();};\r\nvoid()\tdemon1_stand11\t=[\t$stand11,\tdemon1_stand12\t] {ai_stand();};\r\nvoid()\tdemon1_stand12\t=[\t$stand12,\tdemon1_stand13\t] {ai_stand();};\r\nvoid()\tdemon1_stand13\t=[\t$stand13,\tdemon1_stand1\t] {ai_stand();};\r\n\r\nvoid()\tdemon1_walk1\t=[\t$walk1,\t\tdemon1_walk2\t] {\r\nif (random() < 0.2)\r\n    sound (self, CHAN_VOICE, \"demon/idle1.wav\", 1, ATTN_IDLE);\r\nai_walk(8);\r\n};\r\nvoid()\tdemon1_walk2\t=[\t$walk2,\t\tdemon1_walk3\t] {ai_walk(6);};\r\nvoid()\tdemon1_walk3\t=[\t$walk3,\t\tdemon1_walk4\t] {ai_walk(6);};\r\nvoid()\tdemon1_walk4\t=[\t$walk4,\t\tdemon1_walk5\t] {ai_walk(7);};\r\nvoid()\tdemon1_walk5\t=[\t$walk5,\t\tdemon1_walk6\t] {ai_walk(4);};\r\nvoid()\tdemon1_walk6\t=[\t$walk6,\t\tdemon1_walk7\t] {ai_walk(6);};\r\nvoid()\tdemon1_walk7\t=[\t$walk7,\t\tdemon1_walk8\t] {ai_walk(10);};\r\nvoid()\tdemon1_walk8\t=[\t$walk8,\t\tdemon1_walk1\t] {ai_walk(10);};\r\n\r\nvoid()\tdemon1_run1\t=[\t$run1,\t\tdemon1_run2\t] {\r\nif (random() < 0.2)\r\n    sound (self, CHAN_VOICE, \"demon/idle1.wav\", 1, ATTN_IDLE);\r\nai_run(20);};\r\nvoid()\tdemon1_run2\t=[\t$run2,\t\tdemon1_run3\t] {ai_run(15);};\r\nvoid()\tdemon1_run3\t=[\t$run3,\t\tdemon1_run4\t] {ai_run(36);};\r\nvoid()\tdemon1_run4\t=[\t$run4,\t\tdemon1_run5\t] {ai_run(20);};\r\nvoid()\tdemon1_run5\t=[\t$run5,\t\tdemon1_run6\t] {ai_run(15);};\r\nvoid()\tdemon1_run6\t=[\t$run6,\t\tdemon1_run1\t] {ai_run(36);};\r\n\r\nvoid()\tdemon1_jump1\t=[\t$leap1,\t\tdemon1_jump2\t] {ai_face();};\r\nvoid()\tdemon1_jump2\t=[\t$leap2,\t\tdemon1_jump3\t] {ai_face();};\r\nvoid()\tdemon1_jump3\t=[\t$leap3,\t\tdemon1_jump4\t] {ai_face();};\r\nvoid()\tdemon1_jump4\t=[\t$leap4,\t\tdemon1_jump5\t]\r\n{\r\n\tai_face();\r\n\t\r\n\tself.touch = Demon_JumpTouch;\r\n\tmakevectors (self.angles);\r\n\tself.origin_z = self.origin_z + 1;\r\n\tself.velocity = v_forward * 600 + '0 0 250';\r\n\tif (self.flags & FL_ONGROUND)\r\n\t\tself.flags = self.flags - FL_ONGROUND;\r\n};\r\nvoid()\tdemon1_jump5\t=[\t$leap5,\t\tdemon1_jump6\t] {};\r\nvoid()\tdemon1_jump6\t=[\t$leap6,\t\tdemon1_jump7\t] {};\r\nvoid()\tdemon1_jump7\t=[\t$leap7,\t\tdemon1_jump8\t] {};\r\nvoid()\tdemon1_jump8\t=[ \t$leap8,\t\tdemon1_jump9\t] {};\r\nvoid()\tdemon1_jump9\t=[ \t$leap9,\t\tdemon1_jump10\t] {};\r\nvoid()\tdemon1_jump10\t=[ \t$leap10,\tdemon1_jump1\t] {\r\nself.nextthink = time + 3;\r\n// if three seconds pass, assume demon is stuck and jump again\r\n};\r\n\r\nvoid()\tdemon1_jump11\t=[ \t$leap11,\tdemon1_jump12\t] {};\r\nvoid()\tdemon1_jump12\t=[ \t$leap12,\tdemon1_run1\t] {};\r\n\r\n\r\nvoid()\tdemon1_atta1\t=[\t$attacka1,\t\tdemon1_atta2\t] {ai_charge(4);};\r\nvoid()\tdemon1_atta2\t=[\t$attacka2,\t\tdemon1_atta3\t] {ai_charge(0);};\r\nvoid()\tdemon1_atta3\t=[\t$attacka3,\t\tdemon1_atta4\t] {ai_charge(0);};\r\nvoid()\tdemon1_atta4\t=[\t$attacka4,\t\tdemon1_atta5\t] {ai_charge(1);};\r\nvoid()\tdemon1_atta5\t=[\t$attacka5,\t\tdemon1_atta6\t] {ai_charge(2); Demon_Melee(200);};\r\nvoid()\tdemon1_atta6\t=[\t$attacka6,\t\tdemon1_atta7\t] {ai_charge(1);};\r\nvoid()\tdemon1_atta7\t=[\t$attacka7,\t\tdemon1_atta8\t] {ai_charge(6);};\r\nvoid()\tdemon1_atta8\t=[\t$attacka8,\t\tdemon1_atta9\t] {ai_charge(8);};\r\nvoid()\tdemon1_atta9\t=[\t$attacka9,\t\tdemon1_atta10] {ai_charge(4);};\r\nvoid()\tdemon1_atta10\t=[\t$attacka10,\t\tdemon1_atta11] {ai_charge(2);};\r\nvoid()\tdemon1_atta11\t=[\t$attacka11,\t\tdemon1_atta12] {Demon_Melee(-200);};\r\nvoid()\tdemon1_atta12\t=[\t$attacka12,\t\tdemon1_atta13] {ai_charge(5);};\r\nvoid()\tdemon1_atta13\t=[\t$attacka13,\t\tdemon1_atta14] {ai_charge(8);};\r\nvoid()\tdemon1_atta14\t=[\t$attacka14,\t\tdemon1_atta15] {ai_charge(4);};\r\nvoid()\tdemon1_atta15\t=[\t$attacka15,\t\tdemon1_run1] {ai_charge(4);};\r\n\r\nvoid()\tdemon1_pain1\t=[\t$pain1,\t\tdemon1_pain2\t] {};\r\nvoid()\tdemon1_pain2\t=[\t$pain2,\t\tdemon1_pain3\t] {};\r\nvoid()\tdemon1_pain3\t=[\t$pain3,\t\tdemon1_pain4\t] {};\r\nvoid()\tdemon1_pain4\t=[\t$pain4,\t\tdemon1_pain5\t] {};\r\nvoid()\tdemon1_pain5\t=[\t$pain5,\t\tdemon1_pain6\t] {};\r\nvoid()\tdemon1_pain6\t=[\t$pain6,\t\tdemon1_run1\t] {};\r\n\r\nvoid(entity attacker, float damage)\tdemon1_pain =\r\n{\r\n\tif (self.touch == Demon_JumpTouch)\r\n\t\treturn;\r\n\r\n\tif (self.pain_finished > time)\r\n\t\treturn;\r\n\r\n\tself.pain_finished = time + 1;\r\n    sound (self, CHAN_VOICE, \"demon/dpain1.wav\", 1, ATTN_NORM);\r\n\r\n\tif (random()*200 > damage)\r\n\t\treturn;\t\t// didn't flinch\r\n\t\t\r\n\tdemon1_pain1 ();\r\n};\r\n\r\nvoid()\tdemon1_die1\t\t=[\t$death1,\t\tdemon1_die2\t] {\r\nsound (self, CHAN_VOICE, \"demon/ddeath.wav\", 1, ATTN_NORM);};\r\nvoid()\tdemon1_die2\t\t=[\t$death2,\t\tdemon1_die3\t] {};\r\nvoid()\tdemon1_die3\t\t=[\t$death3,\t\tdemon1_die4\t] {};\r\nvoid()\tdemon1_die4\t\t=[\t$death4,\t\tdemon1_die5\t] {};\r\nvoid()\tdemon1_die5\t\t=[\t$death5,\t\tdemon1_die6\t] {};\r\nvoid()\tdemon1_die6\t\t=[\t$death6,\t\tdemon1_die7\t]\r\n{self.solid = SOLID_NOT;};\r\nvoid()\tdemon1_die7\t\t=[\t$death7,\t\tdemon1_die8\t] {};\r\nvoid()\tdemon1_die8\t\t=[\t$death8,\t\tdemon1_die9\t] {};\r\nvoid()\tdemon1_die9\t\t=[\t$death9,\t\tdemon1_die9 ] {};\r\n\r\nvoid() demon_die =\r\n{\r\n// check for gib\r\n\tif (self.health < -80)\r\n\t{\r\n\t\tThrowCSQCGibs(GIB_DEMON);\r\n\t\treturn;\r\n\t}\r\n\r\n// regular death\r\n\tdemon1_die1 ();\r\n};\r\n\r\n\r\nvoid() Demon_MeleeAttack =\r\n{\r\n\tdemon1_atta1 ();\r\n};\r\n\r\n\r\n/*QUAKED monster_demon1 (1 0 0) (-32 -32 -24) (32 32 64) Ambush\r\n\r\n*/\r\nvoid() monster_demon1 =\r\n{\r\n\tif (deathmatch)\r\n\t{\r\n\t\tremove(self);\r\n\t\treturn;\r\n\t}\r\n\tprecache_model (\"progs/demon.mdl\");\r\n\tprecache_model (\"progs/h_demon.mdl\");\r\n\r\n\tprecache_sound (\"demon/ddeath.wav\");\r\n\tprecache_sound (\"demon/dhit2.wav\");\r\n\tprecache_sound (\"demon/djump.wav\");\r\n\tprecache_sound (\"demon/dpain1.wav\");\r\n\tprecache_sound (\"demon/idle1.wav\");\r\n\tprecache_sound (\"demon/sight2.wav\");\r\n\r\n\tself.solid = SOLID_SLIDEBOX;\r\n\tself.movetype = MOVETYPE_STEP;\r\n\r\n\tsetmodel (self, \"progs/demon.mdl\");\r\n\r\n\tsetsize (self, VEC_HULL2_MIN, VEC_HULL2_MAX);\r\n\tself.health = 300;\r\n\r\n\tself.th_stand = demon1_stand1;\r\n\tself.th_walk = demon1_walk1;\r\n\tself.th_run = demon1_run1;\r\n\tself.th_die = demon_die;\r\n\tself.th_melee = Demon_MeleeAttack;\t\t// one of two attacks\r\n\tself.th_missile = demon1_jump1;\t\t\t// jump attack\r\n\tself.th_pain = demon1_pain;\r\n\t\t\r\n\twalkmonster_start();\r\n};\r\n\r\n\r\n/*\r\n==============================================================================\r\n\r\nDEMON\r\n\r\n==============================================================================\r\n*/\r\n\r\n/*\r\n==============\r\nCheckDemonMelee\r\n\r\nReturns TRUE if a melee attack would hit right now\r\n==============\r\n*/\r\nfloat()\tCheckDemonMelee =\r\n{\r\n\tif (enemy_range == RANGE_MELEE)\r\n\t{\t// FIXME: check canreach\r\n\t\tself.attack_state = AS_MELEE;\r\n\t\treturn TRUE;\r\n\t}\r\n\treturn FALSE;\r\n};\r\n\r\n/*\r\n==============\r\nCheckDemonJump\r\n\r\n==============\r\n*/\r\nfloat()\tCheckDemonJump =\r\n{\r\n\tlocal\tvector\tdist;\r\n\tlocal\tfloat\td;\r\n\r\n\tif (self.origin_z + self.mins_z > self.enemy.origin_z + self.enemy.mins_z\r\n\t+ 0.75 * self.enemy.size_z)\r\n\t\treturn FALSE;\r\n\t\t\r\n\tif (self.origin_z + self.maxs_z < self.enemy.origin_z + self.enemy.mins_z\r\n\t+ 0.25 * self.enemy.size_z)\r\n\t\treturn FALSE;\r\n\t\t\r\n\tdist = self.enemy.origin - self.origin;\r\n\tdist_z = 0;\r\n\t\r\n\td = vlen(dist);\r\n\t\r\n\tif (d < 100)\r\n\t\treturn FALSE;\r\n\t\t\r\n\tif (d > 200)\r\n\t{\r\n\t\tif (random() < 0.9)\r\n\t\t\treturn FALSE;\r\n\t}\r\n\t\t\r\n\treturn TRUE;\r\n};\r\n\r\nfloat()\tDemonCheckAttack =\r\n{\t\r\n// if close enough for slashing, go for it\r\n\tif (CheckDemonMelee ())\r\n\t{\r\n\t\tself.attack_state = AS_MELEE;\r\n\t\treturn TRUE;\r\n\t}\r\n\t\r\n\tif (CheckDemonJump ())\r\n\t{\r\n\t\tself.attack_state = AS_MISSILE;\r\n        sound (self, CHAN_VOICE, \"demon/djump.wav\", 1, ATTN_NORM);\r\n\t\treturn TRUE;\r\n\t}\r\n\t\r\n\treturn FALSE;\r\n};\r\n\r\n\r\n//===========================================================================\r\n\r\nvoid(float side)\tDemon_Melee =\r\n{\r\n\tlocal\tfloat\tldmg;\r\n\tlocal vector\tdelta;\r\n\t\r\n\tai_face ();\r\n\twalkmove (self.ideal_yaw, 12);\t// allow a little closing\r\n\r\n\tdelta = self.enemy.origin - self.origin;\r\n\r\n\tif (vlen(delta) > 100)\r\n\t\treturn;\r\n\tif (!CanDamage (self.enemy, self))\r\n\t\treturn;\r\n\t\t\r\n    sound (self, CHAN_WEAPON, \"demon/dhit2.wav\", 1, ATTN_NORM);\r\n\tldmg = 10 + 5*random();\r\n\tT_Damage (self.enemy, self, self, ldmg);\t\r\n\r\n\tmakevectors (self.angles);\r\n\tSpawnMeatSpray (self.origin + v_forward*16, side * v_right);\r\n};\r\n\r\n\r\nvoid()\tDemon_JumpTouch =\r\n{\r\n\tlocal\tfloat\tldmg;\r\n\r\n\tif (self.health <= 0)\r\n\t\treturn;\r\n\t\t\r\n\tif (other.takedamage)\r\n\t{\r\n\t\tif ( vlen(self.velocity) > 400 )\r\n\t\t{\r\n\t\t\tldmg = 40 + 10*random();\r\n\t\t\tT_Damage (other, self, self, ldmg);\t\r\n\t\t}\r\n\t}\r\n\r\n\tif (!checkbottom(self))\r\n\t{\r\n\t\tif (self.flags & FL_ONGROUND)\r\n\t\t{\t// jump randomly to not get hung up\r\n//dprint (\"popjump\\n\");\r\n\tself.touch = SUB_Null;\r\n\tself.think = demon1_jump1;\r\n\tself.nextthink = time + 0.1;\r\n\r\n//\t\t\tself.velocity_x = (random() - 0.5) * 600;\r\n//\t\t\tself.velocity_y = (random() - 0.5) * 600;\r\n//\t\t\tself.velocity_z = 200;\r\n//\t\t\tself.flags = self.flags - FL_ONGROUND;\r\n\t\t}\r\n\t\treturn;\t// not on ground yet\r\n\t}\r\n\r\n\tself.touch = SUB_Null;\r\n\tself.think = demon1_jump11;\r\n\tself.nextthink = time + 0.1;\r\n};\r\n\r\n"
  },
  {
    "path": "quakec/csqctest/src/ss/dog.qc",
    "content": "/*\r\n==============================================================================\r\n\r\nDOG\r\n\r\n==============================================================================\r\n*/\r\n$cd id1/models/dog\r\n$origin 0 0 24\r\n$base base\r\n$skin skin\r\n\r\n$frame attack1 attack2 attack3 attack4 attack5 attack6 attack7 attack8\r\n\r\n$frame death1 death2 death3 death4 death5 death6 death7 death8 death9\r\n\r\n$frame deathb1 deathb2 deathb3 deathb4 deathb5 deathb6 deathb7 deathb8\r\n$frame deathb9\r\n\r\n$frame pain1 pain2 pain3 pain4 pain5 pain6\r\n\r\n$frame painb1 painb2 painb3 painb4 painb5 painb6 painb7 painb8 painb9 painb10\r\n$frame painb11 painb12 painb13 painb14 painb15 painb16\r\n\r\n$frame run1 run2 run3 run4 run5 run6 run7 run8 run9 run10 run11 run12\r\n\r\n$frame leap1 leap2 leap3 leap4 leap5 leap6 leap7 leap8 leap9\r\n\r\n$frame stand1 stand2 stand3 stand4 stand5 stand6 stand7 stand8 stand9\r\n\r\n$frame walk1 walk2 walk3 walk4 walk5 walk6 walk7 walk8\r\n\r\n\r\nvoid() dog_leap1;\r\nvoid() dog_run1;\r\n\r\n/*\r\n================\r\ndog_bite\r\n\r\n================\r\n*/\r\nvoid() dog_bite =\r\n{\r\nlocal vector\tdelta;\r\nlocal float \tldmg;\r\n\r\n\tif (!self.enemy)\r\n\t\treturn;\r\n\r\n\tai_charge(10);\r\n\r\n\tif (!CanDamage (self.enemy, self))\r\n\t\treturn;\r\n\r\n\tdelta = self.enemy.origin - self.origin;\r\n\r\n\tif (vlen(delta) > 100)\r\n\t\treturn;\r\n\t\t\r\n\tldmg = (random()*3) * 8;\r\n\tT_Damage (self.enemy, self, self, ldmg);\r\n};\r\n\r\nvoid()\tDog_JumpTouch =\r\n{\r\n\tlocal\tfloat\tldmg;\r\n\r\n\tif (self.health <= 0)\r\n\t\treturn;\r\n\t\t\r\n\tif (other.takedamage)\r\n\t{\r\n\t\tif ( vlen(self.velocity) > 300 )\r\n\t\t{\r\n\t\t\tldmg = 10 + 10*random();\r\n\t\t\tT_Damage (other, self, self, ldmg);\t\r\n\t\t}\r\n\t}\r\n\r\n\tif (!checkbottom(self))\r\n\t{\r\n\t\tif (self.flags & FL_ONGROUND)\r\n\t\t{\t// jump randomly to not get hung up\r\n//dprint (\"popjump\\n\");\r\n\tself.touch = SUB_Null;\r\n\tself.think = dog_leap1;\r\n\tself.nextthink = time + 0.1;\r\n\r\n//\t\t\tself.velocity_x = (random() - 0.5) * 600;\r\n//\t\t\tself.velocity_y = (random() - 0.5) * 600;\r\n//\t\t\tself.velocity_z = 200;\r\n//\t\t\tself.flags = self.flags - FL_ONGROUND;\r\n\t\t}\r\n\t\treturn;\t// not on ground yet\r\n\t}\r\n\r\n\tself.touch = SUB_Null;\r\n\tself.think = dog_run1;\r\n\tself.nextthink = time + 0.1;\r\n};\r\n\r\n\r\nvoid() dog_stand1\t=[\t$stand1,\tdog_stand2\t] {ai_stand();};\r\nvoid() dog_stand2\t=[\t$stand2,\tdog_stand3\t] {ai_stand();};\r\nvoid() dog_stand3\t=[\t$stand3,\tdog_stand4\t] {ai_stand();};\r\nvoid() dog_stand4\t=[\t$stand4,\tdog_stand5\t] {ai_stand();};\r\nvoid() dog_stand5\t=[\t$stand5,\tdog_stand6\t] {ai_stand();};\r\nvoid() dog_stand6\t=[\t$stand6,\tdog_stand7\t] {ai_stand();};\r\nvoid() dog_stand7\t=[\t$stand7,\tdog_stand8\t] {ai_stand();};\r\nvoid() dog_stand8\t=[\t$stand8,\tdog_stand9\t] {ai_stand();};\r\nvoid() dog_stand9\t=[\t$stand9,\tdog_stand1\t] {ai_stand();};\r\n\r\nvoid() dog_walk1\t=[\t$walk1 ,\tdog_walk2\t] {\r\nif (random() < 0.2)\r\n\tsound (self, CHAN_VOICE, \"dog/idle.wav\", 1, ATTN_IDLE);\r\nai_walk(8);};\r\nvoid() dog_walk2\t=[\t$walk2 ,\tdog_walk3\t] {ai_walk(8);};\r\nvoid() dog_walk3\t=[\t$walk3 ,\tdog_walk4\t] {ai_walk(8);};\r\nvoid() dog_walk4\t=[\t$walk4 ,\tdog_walk5\t] {ai_walk(8);};\r\nvoid() dog_walk5\t=[\t$walk5 ,\tdog_walk6\t] {ai_walk(8);};\r\nvoid() dog_walk6\t=[\t$walk6 ,\tdog_walk7\t] {ai_walk(8);};\r\nvoid() dog_walk7\t=[\t$walk7 ,\tdog_walk8\t] {ai_walk(8);};\r\nvoid() dog_walk8\t=[\t$walk8 ,\tdog_walk1\t] {ai_walk(8);};\r\n\r\nvoid() dog_run1\t\t=[\t$run1  ,\tdog_run2\t] {\r\nif (random() < 0.2)\r\n\tsound (self, CHAN_VOICE, \"dog/idle.wav\", 1, ATTN_IDLE);\r\nai_run(16);};\r\nvoid() dog_run2\t\t=[\t$run2  ,\tdog_run3\t] {ai_run(32);};\r\nvoid() dog_run3\t\t=[\t$run3  ,\tdog_run4\t] {ai_run(32);};\r\nvoid() dog_run4\t\t=[\t$run4  ,\tdog_run5\t] {ai_run(20);};\r\nvoid() dog_run5\t\t=[\t$run5  ,\tdog_run6\t] {ai_run(64);};\r\nvoid() dog_run6\t\t=[\t$run6  ,\tdog_run7\t] {ai_run(32);};\r\nvoid() dog_run7\t\t=[\t$run7  ,\tdog_run8\t] {ai_run(16);};\r\nvoid() dog_run8\t\t=[\t$run8  ,\tdog_run9\t] {ai_run(32);};\r\nvoid() dog_run9\t\t=[\t$run9  ,\tdog_run10\t] {ai_run(32);};\r\nvoid() dog_run10\t=[\t$run10  ,\tdog_run11\t] {ai_run(20);};\r\nvoid() dog_run11\t=[\t$run11  ,\tdog_run12\t] {ai_run(64);};\r\nvoid() dog_run12\t=[\t$run12  ,\tdog_run1\t] {ai_run(32);};\r\n\r\nvoid() dog_atta1\t=[\t$attack1,\tdog_atta2\t] {ai_charge(10);};\r\nvoid() dog_atta2\t=[\t$attack2,\tdog_atta3\t] {ai_charge(10);};\r\nvoid() dog_atta3\t=[\t$attack3,\tdog_atta4\t] {ai_charge(10);};\r\nvoid() dog_atta4\t=[\t$attack4,\tdog_atta5\t] {\r\nsound (self, CHAN_VOICE, \"dog/dattack1.wav\", 1, ATTN_NORM);\r\ndog_bite();};\r\nvoid() dog_atta5\t=[\t$attack5,\tdog_atta6\t] {ai_charge(10);};\r\nvoid() dog_atta6\t=[\t$attack6,\tdog_atta7\t] {ai_charge(10);};\r\nvoid() dog_atta7\t=[\t$attack7,\tdog_atta8\t] {ai_charge(10);};\r\nvoid() dog_atta8\t=[\t$attack8,\tdog_run1\t] {ai_charge(10);};\r\n\r\nvoid() dog_leap1\t=[\t$leap1,\t\tdog_leap2\t] {ai_face();};\r\nvoid() dog_leap2\t=[\t$leap2,\t\tdog_leap3\t]\r\n{\r\n\tai_face();\r\n\t\r\n\tself.touch = Dog_JumpTouch;\r\n\tmakevectors (self.angles);\r\n\tself.origin_z = self.origin_z + 1;\r\n\tself.velocity = v_forward * 300 + '0 0 200';\r\n\tif (self.flags & FL_ONGROUND)\r\n\t\tself.flags = self.flags - FL_ONGROUND;\r\n};\r\n\r\nvoid() dog_leap3\t=[\t$leap3,\t\tdog_leap4\t] {};\r\nvoid() dog_leap4\t=[\t$leap4,\t\tdog_leap5\t] {};\r\nvoid() dog_leap5\t=[\t$leap5,\t\tdog_leap6\t] {};\r\nvoid() dog_leap6\t=[\t$leap6,\t\tdog_leap7\t] {};\r\nvoid() dog_leap7\t=[\t$leap7,\t\tdog_leap8\t] {};\r\nvoid() dog_leap8\t=[\t$leap8,\t\tdog_leap9\t] {};\r\nvoid() dog_leap9\t=[\t$leap9,\t\tdog_leap9\t] {};\r\n\r\nvoid() dog_pain1\t=[\t$pain1 ,\tdog_pain2\t] {};\r\nvoid() dog_pain2\t=[\t$pain2 ,\tdog_pain3\t] {};\r\nvoid() dog_pain3\t=[\t$pain3 ,\tdog_pain4\t] {};\r\nvoid() dog_pain4\t=[\t$pain4 ,\tdog_pain5\t] {};\r\nvoid() dog_pain5\t=[\t$pain5 ,\tdog_pain6\t] {};\r\nvoid() dog_pain6\t=[\t$pain6 ,\tdog_run1\t] {};\r\n\r\nvoid() dog_painb1\t=[\t$painb1 ,\tdog_painb2\t] {};\r\nvoid() dog_painb2\t=[\t$painb2 ,\tdog_painb3\t] {};\r\nvoid() dog_painb3\t=[\t$painb3 ,\tdog_painb4\t] {ai_pain(4);};\r\nvoid() dog_painb4\t=[\t$painb4 ,\tdog_painb5\t] {ai_pain(12);};\r\nvoid() dog_painb5\t=[\t$painb5 ,\tdog_painb6\t] {ai_pain(12);};\r\nvoid() dog_painb6\t=[\t$painb6 ,\tdog_painb7\t] {ai_pain(2);};\r\nvoid() dog_painb7\t=[\t$painb7 ,\tdog_painb8\t] {};\r\nvoid() dog_painb8\t=[\t$painb8 ,\tdog_painb9\t] {ai_pain(4);};\r\nvoid() dog_painb9\t=[\t$painb9 ,\tdog_painb10\t] {};\r\nvoid() dog_painb10\t=[\t$painb10 ,\tdog_painb11\t] {ai_pain(10);};\r\nvoid() dog_painb11\t=[\t$painb11 ,\tdog_painb12\t] {};\r\nvoid() dog_painb12\t=[\t$painb12 ,\tdog_painb13\t] {};\r\nvoid() dog_painb13\t=[\t$painb13 ,\tdog_painb14\t] {};\r\nvoid() dog_painb14\t=[\t$painb14 ,\tdog_painb15\t] {};\r\nvoid() dog_painb15\t=[\t$painb15 ,\tdog_painb16\t] {};\r\nvoid() dog_painb16\t=[\t$painb16 ,\tdog_run1\t] {};\r\n\r\nvoid(entity attacker, float damage) dog_pain =\r\n{\r\n\tsound (self, CHAN_VOICE, \"dog/dpain1.wav\", 1, ATTN_NORM);\r\n\r\n\tif (random() > 0.5)\r\n\t\tdog_pain1 ();\r\n\telse\r\n\t\tdog_painb1 ();\r\n};\r\n\r\nvoid() dog_die1\t\t=[\t$death1,\tdog_die2\t] {};\r\nvoid() dog_die2\t\t=[\t$death2,\tdog_die3\t] {};\r\nvoid() dog_die3\t\t=[\t$death3,\tdog_die4\t] {};\r\nvoid() dog_die4\t\t=[\t$death4,\tdog_die5\t] {};\r\nvoid() dog_die5\t\t=[\t$death5,\tdog_die6\t] {};\r\nvoid() dog_die6\t\t=[\t$death6,\tdog_die7\t] {};\r\nvoid() dog_die7\t\t=[\t$death7,\tdog_die8\t] {};\r\nvoid() dog_die8\t\t=[\t$death8,\tdog_die9\t] {};\r\nvoid() dog_die9\t\t=[\t$death9,\tdog_die9\t] {};\r\n\r\nvoid() dog_dieb1\t\t=[\t$deathb1,\tdog_dieb2\t] {};\r\nvoid() dog_dieb2\t\t=[\t$deathb2,\tdog_dieb3\t] {};\r\nvoid() dog_dieb3\t\t=[\t$deathb3,\tdog_dieb4\t] {};\r\nvoid() dog_dieb4\t\t=[\t$deathb4,\tdog_dieb5\t] {};\r\nvoid() dog_dieb5\t\t=[\t$deathb5,\tdog_dieb6\t] {};\r\nvoid() dog_dieb6\t\t=[\t$deathb6,\tdog_dieb7\t] {};\r\nvoid() dog_dieb7\t\t=[\t$deathb7,\tdog_dieb8\t] {};\r\nvoid() dog_dieb8\t\t=[\t$deathb8,\tdog_dieb9\t] {};\r\nvoid() dog_dieb9\t\t=[\t$deathb9,\tdog_dieb9\t] {};\r\n\r\n\r\nvoid() dog_die =\r\n{\r\n// check for gib\r\n\tif (self.health < -35)\r\n\t{\r\n\t\tThrowCSQCGibs(GIB_DOG);\r\n\t\treturn;\r\n\t}\r\n\r\n// regular death\r\n\tsound (self, CHAN_VOICE, \"dog/ddeath.wav\", 1, ATTN_NORM);\r\n\tself.solid = SOLID_NOT;\r\n\r\n\tif (random() > 0.5)\r\n\t\tdog_die1 ();\r\n\telse\r\n\t\tdog_dieb1 ();\r\n};\r\n\r\n//============================================================================\r\n\r\n/*\r\n==============\r\nCheckDogMelee\r\n\r\nReturns TRUE if a melee attack would hit right now\r\n==============\r\n*/\r\nfloat()\tCheckDogMelee =\r\n{\r\n\tif (enemy_range == RANGE_MELEE)\r\n\t{\t// FIXME: check canreach\r\n\t\tself.attack_state = AS_MELEE;\r\n\t\treturn TRUE;\r\n\t}\r\n\treturn FALSE;\r\n};\r\n\r\n/*\r\n==============\r\nCheckDogJump\r\n\r\n==============\r\n*/\r\nfloat()\tCheckDogJump =\r\n{\r\n\tlocal\tvector\tdist;\r\n\tlocal\tfloat\td;\r\n\r\n\tif (self.origin_z + self.mins_z > self.enemy.origin_z + self.enemy.mins_z\r\n\t+ 0.75 * self.enemy.size_z)\r\n\t\treturn FALSE;\r\n\t\t\r\n\tif (self.origin_z + self.maxs_z < self.enemy.origin_z + self.enemy.mins_z\r\n\t+ 0.25 * self.enemy.size_z)\r\n\t\treturn FALSE;\r\n\t\t\r\n\tdist = self.enemy.origin - self.origin;\r\n\tdist_z = 0;\r\n\t\r\n\td = vlen(dist);\r\n\t\r\n\tif (d < 80)\r\n\t\treturn FALSE;\r\n\t\t\r\n\tif (d > 150)\r\n\t\treturn FALSE;\r\n\t\t\r\n\treturn TRUE;\r\n};\r\n\r\nfloat()\tDogCheckAttack =\r\n{\r\n\r\n// if close enough for slashing, go for it\r\n\tif (CheckDogMelee ())\r\n\t{\r\n\t\tself.attack_state = AS_MELEE;\r\n\t\treturn TRUE;\r\n\t}\r\n\t\r\n\tif (CheckDogJump ())\r\n\t{\r\n\t\tself.attack_state = AS_MISSILE;\r\n\t\treturn TRUE;\r\n\t}\r\n\t\r\n\treturn FALSE;\r\n};\r\n\r\n\r\n//===========================================================================\r\n\r\n/*QUAKED monster_dog (1 0 0) (-32 -32 -24) (32 32 40) Ambush\r\n\r\n*/\r\nvoid() monster_dog =\r\n{\r\n\tif (deathmatch)\r\n\t{\r\n\t\tremove(self);\r\n\t\treturn;\r\n\t}\r\n\tprecache_model (\"progs/h_dog.mdl\");\r\n\tprecache_model (\"progs/dog.mdl\");\r\n\r\n\tprecache_sound (\"dog/dattack1.wav\");\r\n\tprecache_sound (\"dog/ddeath.wav\");\r\n\tprecache_sound (\"dog/dpain1.wav\");\r\n\tprecache_sound (\"dog/dsight.wav\");\r\n\tprecache_sound (\"dog/idle.wav\");\r\n\r\n\tself.solid = SOLID_SLIDEBOX;\r\n\tself.movetype = MOVETYPE_STEP;\r\n\r\n\tsetmodel (self, \"progs/dog.mdl\");\r\n\r\n\tsetsize (self, '-32 -32 -24', '32 32 40');\r\n\tself.health = 25;\r\n\r\n\tself.th_stand = dog_stand1;\r\n\tself.th_walk = dog_walk1;\r\n\tself.th_run = dog_run1;\r\n\tself.th_pain = dog_pain;\r\n\tself.th_die = dog_die;\r\n\tself.th_melee = dog_atta1;\r\n\tself.th_missile = dog_leap1;\r\n\r\n\twalkmonster_start();\r\n};\r\n"
  },
  {
    "path": "quakec/csqctest/src/ss/doors.qc",
    "content": "\r\nfloat DOOR_START_OPEN = 1;\r\nfloat DOOR_DONT_LINK = 4;\r\nfloat DOOR_GOLD_KEY = 8;\r\nfloat DOOR_SILVER_KEY = 16;\r\nfloat DOOR_TOGGLE = 32;\r\n\r\n/*\r\n\r\nDoors are similar to buttons, but can spawn a fat trigger field around them\r\nto open without a touch, and they link together to form simultanious\r\ndouble/quad doors.\r\n \r\nDoor.owner is the master door.  If there is only one door, it points to itself.\r\nIf multiple doors, all will point to a single one.\r\n\r\nDoor.enemy chains from the master door through all doors linked in the chain.\r\n\r\n*/\r\n\r\n/*\r\n=============================================================================\r\n\r\nTHINK FUNCTIONS\r\n\r\n=============================================================================\r\n*/\r\n\r\nvoid() door_go_down;\r\nvoid() door_go_up;\r\n\r\nvoid() door_blocked =\r\n{\r\n\tT_Damage (other, self, self, self.dmg);\r\n\t\r\n// if a door has a negative wait, it would never come back if blocked,\r\n// so let it just squash the object to death real fast\r\n\tif (self.wait >= 0)\r\n\t{\r\n\t\tif (self.state == STATE_DOWN)\r\n\t\t\tdoor_go_up ();\r\n\t\telse\r\n\t\t\tdoor_go_down ();\r\n\t}\r\n};\r\n\r\n\r\nvoid() door_hit_top =\r\n{\r\n\tsound (self, CHAN_VOICE, self.noise1, 1, ATTN_NORM);\r\n\tself.state = STATE_TOP;\r\n\tif (self.spawnflags & DOOR_TOGGLE)\r\n\t\treturn;\t\t// don't come down automatically\r\n\tself.think = door_go_down;\r\n\tself.nextthink = self.ltime + self.wait;\r\n};\r\n\r\nvoid() door_hit_bottom =\r\n{\r\n\tsound (self, CHAN_VOICE, self.noise1, 1, ATTN_NORM);\r\n\tself.state = STATE_BOTTOM;\r\n};\r\n\r\nvoid() door_go_down =\r\n{\r\n\tsound (self, CHAN_VOICE, self.noise2, 1, ATTN_NORM);\r\n\tif (self.max_health)\r\n\t{\r\n\t\tself.takedamage = DAMAGE_YES;\r\n\t\tself.health = self.max_health;\r\n\t}\r\n\t\r\n\tself.state = STATE_DOWN;\r\n\tSUB_CalcMove (self.pos1, self.speed, door_hit_bottom);\r\n};\r\n\r\nvoid() door_go_up =\r\n{\r\n\tif (self.state == STATE_UP)\r\n\t\treturn;\t\t// allready going up\r\n\r\n\tif (self.state == STATE_TOP)\r\n\t{\t// reset top wait time\r\n\t\tself.nextthink = self.ltime + self.wait;\r\n\t\treturn;\r\n\t}\r\n\t\r\n\tsound (self, CHAN_VOICE, self.noise2, 1, ATTN_NORM);\r\n\tself.state = STATE_UP;\r\n\tSUB_CalcMove (self.pos2, self.speed, door_hit_top);\r\n\r\n\tSUB_UseTargets();\r\n};\r\n\r\n\r\n/*\r\n=============================================================================\r\n\r\nACTIVATION FUNCTIONS\r\n\r\n=============================================================================\r\n*/\r\n\r\nvoid() door_fire =\r\n{\r\n\tlocal entity \toself;\r\n\tlocal entity\tstarte;\r\n\r\n\tif (self.owner != self)\r\n\t\tobjerror (\"door_fire: self.owner != self\");\r\n\r\n// play use key sound\r\n\r\n\tif (self.items)\r\n\t\tsound (self, CHAN_VOICE, self.noise4, 1, ATTN_NORM);\r\n\r\n\tself.message = string_null;\t\t// no more message\r\n\toself = self;\r\n\r\n\tif (self.spawnflags & DOOR_TOGGLE)\r\n\t{\r\n\t\tif (self.state == STATE_UP || self.state == STATE_TOP)\r\n\t\t{\r\n\t\t\tstarte = self;\r\n\t\t\tdo\r\n\t\t\t{\r\n\t\t\t\tdoor_go_down ();\r\n\t\t\t\tself = self.enemy;\r\n\t\t\t} while ( (self != starte) && (self != world) );\r\n\t\t\tself = oself;\r\n\t\t\treturn;\r\n\t\t}\r\n\t}\r\n\t\r\n// trigger all paired doors\r\n\tstarte = self;\r\n\tdo\r\n\t{\r\n\t\tdoor_go_up ();\r\n\t\tself = self.enemy;\r\n\t} while ( (self != starte) && (self != world) );\r\n\tself = oself;\r\n};\r\n\r\n\r\nvoid() door_use =\r\n{\r\n\tlocal entity oself;\r\n\r\n\tself.message = \"\";\t\t\t// door message are for touch only\r\n\tself.owner.message = \"\";\t\r\n\tself.enemy.message = \"\";\r\n\toself = self;\r\n\tself = self.owner;\r\n\tdoor_fire ();\r\n\tself = oself;\r\n};\r\n\r\n\r\nvoid() door_trigger_touch =\r\n{\r\n\tif (other.health <= 0)\r\n\t\treturn;\r\n\r\n\tif (time < self.attack_finished)\r\n\t\treturn;\r\n\tself.attack_finished = time + 1;\r\n\r\n\tactivator = other;\r\n\r\n\tself = self.owner;\r\n\tdoor_use ();\r\n};\r\n\r\n\r\nvoid() door_killed =\r\n{\r\n\tlocal entity oself;\r\n\t\r\n\toself = self;\r\n\tself = self.owner;\r\n\tself.health = self.max_health;\r\n\tself.takedamage = DAMAGE_NO;\t// wil be reset upon return\r\n\tdoor_use ();\r\n\tself = oself;\r\n};\r\n\r\n\r\n/*\r\n================\r\ndoor_touch\r\n\r\nPrints messages and opens key doors\r\n================\r\n*/\r\nvoid() door_touch =\r\n{\r\n\tif (other.classname != \"player\")\r\n\t\treturn;\r\n\tif (self.owner.attack_finished > time)\r\n\t\treturn;\r\n\r\n\tself.owner.attack_finished = time + 2;\r\n\r\n\tif (self.owner.message != \"\")\r\n\t{\r\n\t\tcenterprint (other, self.owner.message);\r\n\t\tsound (other, CHAN_VOICE, \"misc/talk.wav\", 1, ATTN_NORM);\r\n\t}\r\n\t\r\n// key door stuff\r\n\tif (!self.items)\r\n\t\treturn;\r\n\r\n// FIXME: blink key on player's status bar\r\n\tif ( (self.items & other.items) != self.items )\r\n\t{\r\n\t\tif (self.owner.items == IT_KEY1)\r\n\t\t{\r\n\t\t\tif (world.worldtype == 2)\r\n\t\t\t{\r\n\t\t\t\tcenterprint (other, \"You need the silver keycard\");\r\n\t\t\t\tsound (self, CHAN_VOICE, self.noise3, 1, ATTN_NORM);\r\n\t\t\t}\r\n\t\t\telse if (world.worldtype == 1)\r\n\t\t\t{\r\n\t\t\t\tcenterprint (other, \"You need the silver runekey\");\r\n\t\t\t\tsound (self, CHAN_VOICE, self.noise3, 1, ATTN_NORM);\r\n\t\t\t}\r\n\t\t\telse if (world.worldtype == 0)\r\n\t\t\t{\r\n\t\t\t\tcenterprint (other, \"You need the silver key\");\r\n\t\t\t\tsound (self, CHAN_VOICE, self.noise3, 1, ATTN_NORM);\r\n\t\t\t}\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tif (world.worldtype == 2)\r\n\t\t\t{\r\n\t\t\t\tcenterprint (other, \"You need the gold keycard\");\r\n\t\t\t\tsound (self, CHAN_VOICE, self.noise3, 1, ATTN_NORM);\r\n\t\t\t}\r\n\t\t\telse if (world.worldtype == 1)\r\n\t\t\t{\r\n\t\t\t\tcenterprint (other, \"You need the gold runekey\");\r\n\t\t\t\tsound (self, CHAN_VOICE, self.noise3, 1, ATTN_NORM);\t\t\t\r\n\t\t\t}\r\n\t\t\telse if (world.worldtype == 0)\r\n\t\t\t{\r\n\t\t\t\tcenterprint (other, \"You need the gold key\");\r\n\t\t\t\tsound (self, CHAN_VOICE, self.noise3, 1, ATTN_NORM);\r\n\t\t\t}\r\n\t\t}\r\n\t\treturn;\r\n\t}\r\n\r\n\tother.items = other.items - self.items;\r\n\tself.touch = SUB_Null;\r\n\tif (self.enemy)\r\n\t\tself.enemy.touch = SUB_Null;\t// get paired door\r\n\tdoor_use ();\r\n};\r\n\r\n/*\r\n=============================================================================\r\n\r\nSPAWNING FUNCTIONS\r\n\r\n=============================================================================\r\n*/\r\n\r\n\r\nentity(vector fmins, vector fmaxs) spawn_field =\r\n{\r\n\tlocal entity\ttrigger;\r\n\tlocal\tvector\tt1, t2;\r\n\r\n\ttrigger = spawn();\r\n\ttrigger.movetype = MOVETYPE_NONE;\r\n\ttrigger.solid = SOLID_TRIGGER;\r\n\ttrigger.owner = self;\r\n\ttrigger.touch = door_trigger_touch;\r\n\r\n\tt1 = fmins;\r\n\tt2 = fmaxs;\r\n\tsetsize (trigger, t1 - '60 60 8', t2 + '60 60 8');\r\n\treturn (trigger);\r\n};\r\n\r\n\r\nfloat (entity e1, entity e2) EntitiesTouching =\r\n{\r\n\tif (e1.mins_x > e2.maxs_x)\r\n\t\treturn FALSE;\r\n\tif (e1.mins_y > e2.maxs_y)\r\n\t\treturn FALSE;\r\n\tif (e1.mins_z > e2.maxs_z)\r\n\t\treturn FALSE;\r\n\tif (e1.maxs_x < e2.mins_x)\r\n\t\treturn FALSE;\r\n\tif (e1.maxs_y < e2.mins_y)\r\n\t\treturn FALSE;\r\n\tif (e1.maxs_z < e2.mins_z)\r\n\t\treturn FALSE;\r\n\treturn TRUE;\r\n};\r\n\r\n\r\n/*\r\n=============\r\nLinkDoors\r\n\r\n\r\n=============\r\n*/\r\nvoid() LinkDoors =\r\n{\r\n\tlocal entity\tt, starte;\r\n\tlocal vector\tcmins, cmaxs;\r\n\r\n\tif (self.enemy)\r\n\t\treturn;\t\t// already linked by another door\r\n\tif (self.spawnflags & 4)\r\n\t{\r\n\t\tself.owner = self.enemy = self;\r\n\t\treturn;\t\t// don't want to link this door\r\n\t}\r\n\r\n\tcmins = self.mins;\r\n\tcmaxs = self.maxs;\r\n\t\r\n\tstarte = self;\r\n\tt = self;\r\n\t\r\n\tdo\r\n\t{\r\n\t\tself.owner = starte;\t\t\t// master door\r\n\r\n\t\tif (self.health)\r\n\t\t\tstarte.health = self.health;\r\n\t\tif (self.targetname!=\"\")\r\n\t\t\tstarte.targetname = self.targetname;\r\n\t\tif (self.message != \"\")\r\n\t\t\tstarte.message = self.message;\r\n\r\n\t\tt = find (t, classname, self.classname);\t\r\n\t\tif (!t)\r\n\t\t{\r\n\t\t\tself.enemy = starte;\t\t// make the chain a loop\r\n\r\n\t\t// shootable, fired, or key doors just needed the owner/enemy links,\r\n\t\t// they don't spawn a field\r\n\t\r\n\t\t\tself = self.owner;\r\n\r\n\t\t\tif (self.health)\r\n\t\t\t\treturn;\r\n\t\t\tif (self.targetname!=\"\")\r\n\t\t\t\treturn;\r\n\t\t\tif (self.items)\r\n\t\t\t\treturn;\r\n\r\n\t\t\tself.owner.trigger_field = spawn_field(cmins, cmaxs);\r\n\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\tif (EntitiesTouching(self,t))\r\n\t\t{\r\n\t\t\tif (t.enemy)\r\n\t\t\t\tobjerror (\"cross connected doors\");\r\n\t\t\t\r\n\t\t\tself.enemy = t;\r\n\t\t\tself = t;\r\n\r\n\t\t\tif (t.mins_x < cmins_x)\r\n\t\t\t\tcmins_x = t.mins_x;\r\n\t\t\tif (t.mins_y < cmins_y)\r\n\t\t\t\tcmins_y = t.mins_y;\r\n\t\t\tif (t.mins_z < cmins_z)\r\n\t\t\t\tcmins_z = t.mins_z;\r\n\t\t\tif (t.maxs_x > cmaxs_x)\r\n\t\t\t\tcmaxs_x = t.maxs_x;\r\n\t\t\tif (t.maxs_y > cmaxs_y)\r\n\t\t\t\tcmaxs_y = t.maxs_y;\r\n\t\t\tif (t.maxs_z > cmaxs_z)\r\n\t\t\t\tcmaxs_z = t.maxs_z;\r\n\t\t}\r\n\t} while (1 );\r\n\r\n};\r\n\r\n\r\n/*QUAKED func_door (0 .5 .8) ? START_OPEN x DOOR_DONT_LINK GOLD_KEY SILVER_KEY TOGGLE\r\nif two doors touch, they are assumed to be connected and operate as a unit.\r\n\r\nTOGGLE causes the door to wait in both the start and end states for a trigger event.\r\n\r\nSTART_OPEN causes the door to move to its destination when spawned, and operate in reverse.  It is used to temporarily or permanently close off an area when triggered (not usefull for touch or takedamage doors).\r\n\r\nKey doors are allways wait -1.\r\n\r\n\"message\"\tis printed when the door is touched if it is a trigger door and it hasn't been fired yet\r\n\"angle\"\t\tdetermines the opening direction\r\n\"targetname\" if set, no touch field will be spawned and a remote button or trigger field activates the door.\r\n\"health\"\tif set, door must be shot open\r\n\"speed\"\t\tmovement speed (100 default)\r\n\"wait\"\t\twait before returning (3 default, -1 = never return)\r\n\"lip\"\t\tlip remaining at end of move (8 default)\r\n\"dmg\"\t\tdamage to inflict when blocked (2 default)\r\n\"sounds\"\r\n0)\tno sound\r\n1)\tstone\r\n2)\tbase\r\n3)\tstone chain\r\n4)\tscreechy metal\r\n*/\r\n\r\nvoid() func_door =\r\n\r\n{\r\n\r\n\tif (world.worldtype == 0)\r\n\t{\r\n\t\tprecache_sound (\"doors/medtry.wav\");\r\n\t\tprecache_sound (\"doors/meduse.wav\");\r\n\t\tself.noise3 = \"doors/medtry.wav\";\r\n\t\tself.noise4 = \"doors/meduse.wav\";\r\n\t}\r\n\telse if (world.worldtype == 1)\r\n\t{\r\n\t\tprecache_sound (\"doors/runetry.wav\");\r\n\t\tprecache_sound (\"doors/runeuse.wav\");\r\n\t\tself.noise3 = \"doors/runetry.wav\";\r\n\t\tself.noise4 = \"doors/runeuse.wav\";\r\n\t}\r\n\telse if (world.worldtype == 2)\r\n\t{\r\n\t\tprecache_sound (\"doors/basetry.wav\");\r\n\t\tprecache_sound (\"doors/baseuse.wav\");\r\n\t\tself.noise3 = \"doors/basetry.wav\";\r\n\t\tself.noise4 = \"doors/baseuse.wav\";\r\n\t}\r\n\telse\r\n\t{\r\n\t\tdprint (\"no worldtype set!\\n\");\r\n\t}\r\n\tif (self.sounds == 0)\r\n\t{\r\n\t\tprecache_sound (\"misc/null.wav\");\r\n\t\tprecache_sound (\"misc/null.wav\");\r\n\t\tself.noise1 = \"misc/null.wav\";\r\n\t\tself.noise2 = \"misc/null.wav\";\r\n\t}\r\n\tif (self.sounds == 1)\r\n\t{\r\n\t\tprecache_sound (\"doors/drclos4.wav\");\r\n\t\tprecache_sound (\"doors/doormv1.wav\");\r\n\t\tself.noise1 = \"doors/drclos4.wav\";\r\n\t\tself.noise2 = \"doors/doormv1.wav\";\r\n\t}\r\n\tif (self.sounds == 2)\r\n\t{\r\n\t\tprecache_sound (\"doors/hydro1.wav\");\r\n\t\tprecache_sound (\"doors/hydro2.wav\");\r\n\t\tself.noise2 = \"doors/hydro1.wav\";\r\n\t\tself.noise1 = \"doors/hydro2.wav\";\r\n\t}\r\n\tif (self.sounds == 3)\r\n\t{\r\n\t\tprecache_sound (\"doors/stndr1.wav\");\r\n\t\tprecache_sound (\"doors/stndr2.wav\");\r\n\t\tself.noise2 = \"doors/stndr1.wav\";\r\n\t\tself.noise1 = \"doors/stndr2.wav\";\r\n\t}\r\n\tif (self.sounds == 4)\r\n\t{\r\n\t\tprecache_sound (\"doors/ddoor1.wav\");\r\n\t\tprecache_sound (\"doors/ddoor2.wav\");\r\n\t\tself.noise1 = \"doors/ddoor2.wav\";\r\n\t\tself.noise2 = \"doors/ddoor1.wav\";\r\n\t}\r\n\r\n\r\n\tSetMovedir ();\r\n\r\n\tself.max_health = self.health;\r\n\tself.solid = SOLID_BSP;\r\n\tself.movetype = MOVETYPE_PUSH;\r\n\tsetorigin (self, self.origin);\t\r\n\tsetmodel (self, self.model);\r\n\tself.classname = \"door\";\r\n\r\n\tself.blocked = door_blocked;\r\n\tself.use = door_use;\r\n\t\r\n\tif (self.spawnflags & DOOR_SILVER_KEY)\r\n\t\tself.items = IT_KEY1;\r\n\tif (self.spawnflags & DOOR_GOLD_KEY)\r\n\t\tself.items = IT_KEY2;\r\n\t\r\n\tif (!self.speed)\r\n\t\tself.speed = 100;\r\n\tif (!self.wait)\r\n\t\tself.wait = 3;\r\n\tif (!self.lip)\r\n\t\tself.lip = 8;\r\n\tif (!self.dmg)\r\n\t\tself.dmg = 2;\r\n\r\n\tself.pos1 = self.origin;\r\n\tself.pos2 = self.pos1 + self.movedir*(fabs(self.movedir*self.size) - self.lip);\r\n\r\n// DOOR_START_OPEN is to allow an entity to be lighted in the closed position\r\n// but spawn in the open position\r\n\tif (self.spawnflags & DOOR_START_OPEN)\r\n\t{\r\n\t\tsetorigin (self, self.pos2);\r\n\t\tself.pos2 = self.pos1;\r\n\t\tself.pos1 = self.origin;\r\n\t}\r\n\r\n\tself.state = STATE_BOTTOM;\r\n\r\n\tif (self.health)\r\n\t{\r\n\t\tself.takedamage = DAMAGE_YES;\r\n\t\tself.th_die = door_killed;\r\n\t}\r\n\t\r\n\tif (self.items)\r\n\t\tself.wait = -1;\r\n\t\t\r\n\tself.touch = door_touch;\r\n\r\n// LinkDoors can't be done until all of the doors have been spawned, so\r\n// the sizes can be detected properly.\r\n\tself.think = LinkDoors;\r\n\tself.nextthink = self.ltime + 0.1;\r\n};\r\n\r\n/*\r\n=============================================================================\r\n\r\nSECRET DOORS\r\n\r\n=============================================================================\r\n*/\r\n\r\nvoid() fd_secret_move1;\r\nvoid() fd_secret_move2;\r\nvoid() fd_secret_move3;\r\nvoid() fd_secret_move4;\r\nvoid() fd_secret_move5;\r\nvoid() fd_secret_move6;\r\nvoid() fd_secret_done;\r\n\r\nfloat SECRET_OPEN_ONCE = 1;\t\t// stays open\r\nfloat SECRET_1ST_LEFT = 2;\t\t// 1st move is left of arrow\r\nfloat SECRET_1ST_DOWN = 4;\t\t// 1st move is down from arrow\r\nfloat SECRET_NO_SHOOT = 8;\t\t// only opened by trigger\r\nfloat SECRET_YES_SHOOT = 16;\t// shootable even if targeted\r\n\r\n\r\nvoid () fd_secret_use =\r\n{\r\n\tlocal float temp;\r\n\t\r\n\tself.health = 10000;\r\n\r\n\t// exit if still moving around...\r\n\tif (self.origin != self.oldorigin)\r\n\t\treturn;\r\n\t\r\n\tself.message = string_null;\t\t// no more message\r\n\r\n\tSUB_UseTargets();\t\t\t\t// fire all targets / killtargets\r\n\t\r\n\tif (!(self.spawnflags & SECRET_NO_SHOOT))\r\n\t{\r\n\t\tself.th_pain = __NULL__;\r\n\t\tself.takedamage = DAMAGE_NO;\r\n\t}\r\n\tself.velocity = '0 0 0';\r\n\r\n\t// Make a sound, wait a little...\r\n\t\r\n\tsound(self, CHAN_VOICE, self.noise1, 1, ATTN_NORM);\r\n\tself.nextthink = self.ltime + 0.1;\r\n\r\n\ttemp = 1 - (self.spawnflags & SECRET_1ST_LEFT);\t// 1 or -1\r\n\tmakevectors(self.mangle);\r\n\t\r\n\tif (!self.t_width)\r\n\t{\r\n\t\tif (self.spawnflags & SECRET_1ST_DOWN)\r\n\t\t\tself. t_width = fabs(v_up * self.size);\r\n\t\telse\r\n\t\t\tself. t_width = fabs(v_right * self.size);\r\n\t}\r\n\t\t\r\n\tif (!self.t_length)\r\n\t\tself. t_length = fabs(v_forward * self.size);\r\n\r\n\tif (self.spawnflags & SECRET_1ST_DOWN)\r\n\t\tself.dest1 = self.origin - v_up * self.t_width;\r\n\telse\r\n\t\tself.dest1 = self.origin + v_right * (self.t_width * temp);\r\n\t\t\r\n\tself.dest2 = self.dest1 + v_forward * self.t_length;\r\n\tSUB_CalcMove(self.dest1, self.speed, fd_secret_move1);\r\n\tsound(self, CHAN_VOICE, self.noise2, 1, ATTN_NORM);\r\n};\r\n\r\n// Wait after first movement...\r\nvoid () fd_secret_move1 = \r\n{\r\n\tself.nextthink = self.ltime + 1.0;\r\n\tself.think = fd_secret_move2;\r\n\tsound(self, CHAN_VOICE, self.noise3, 1, ATTN_NORM);\r\n};\r\n\r\n// Start moving sideways w/sound...\r\nvoid () fd_secret_move2 =\r\n{\r\n\tsound(self, CHAN_VOICE, self.noise2, 1, ATTN_NORM);\r\n\tSUB_CalcMove(self.dest2, self.speed, fd_secret_move3);\r\n};\r\n\r\n// Wait here until time to go back...\r\nvoid () fd_secret_move3 =\r\n{\r\n\tsound(self, CHAN_VOICE, self.noise3, 1, ATTN_NORM);\r\n\tif (!(self.spawnflags & SECRET_OPEN_ONCE))\r\n\t{\r\n\t\tself.nextthink = self.ltime + self.wait;\r\n\t\tself.think = fd_secret_move4;\r\n\t}\r\n};\r\n\r\n// Move backward...\r\nvoid () fd_secret_move4 =\r\n{\r\n\tsound(self, CHAN_VOICE, self.noise2, 1, ATTN_NORM);\r\n\tSUB_CalcMove(self.dest1, self.speed, fd_secret_move5);\t\t\r\n};\r\n\r\n// Wait 1 second...\r\nvoid () fd_secret_move5 = \r\n{\r\n\tself.nextthink = self.ltime + 1.0;\r\n\tself.think = fd_secret_move6;\r\n\tsound(self, CHAN_VOICE, self.noise3, 1, ATTN_NORM);\r\n};\r\n\r\nvoid () fd_secret_move6 =\r\n{\r\n\tsound(self, CHAN_VOICE, self.noise2, 1, ATTN_NORM);\r\n\tSUB_CalcMove(self.oldorigin, self.speed, fd_secret_done);\r\n};\r\n\r\nvoid () fd_secret_done =\r\n{\r\n\tif (!self.targetname || self.spawnflags&SECRET_YES_SHOOT)\r\n\t{\r\n\t\tself.health = 10000;\r\n\t\tself.takedamage = DAMAGE_YES;\r\n\t\tself.th_pain = (void(entity attacker, float damage))fd_secret_use;\t\r\n\t}\r\n\tsound(self, CHAN_VOICE, self.noise3, 1, ATTN_NORM);\r\n};\r\n\r\nvoid () secret_blocked =\r\n{\r\n\tif (time < self.attack_finished)\r\n\t\treturn;\r\n\tself.attack_finished = time + 0.5;\r\n\tT_Damage (other, self, self, self.dmg);\r\n};\r\n\r\n/*\r\n================\r\nsecret_touch\r\n\r\nPrints messages\r\n================\r\n*/\r\nvoid() secret_touch =\r\n{\r\n\tif (other.classname != \"player\")\r\n\t\treturn;\r\n\tif (self.attack_finished > time)\r\n\t\treturn;\r\n\r\n\tself.attack_finished = time + 2;\r\n\t\r\n\tif (self.message!=\"\")\r\n\t{\r\n\t\tcenterprint (other, self.message);\r\n\t\tsound (other, CHAN_BODY, \"misc/talk.wav\", 1, ATTN_NORM);\r\n\t}\r\n};\r\n\r\n\r\n/*QUAKED func_door_secret (0 .5 .8) ? open_once 1st_left 1st_down no_shoot always_shoot\r\nBasic secret door. Slides back, then to the side. Angle determines direction.\r\nwait  = # of seconds before coming back\r\n1st_left = 1st move is left of arrow\r\n1st_down = 1st move is down from arrow\r\nalways_shoot = even if targeted, keep shootable\r\nt_width = override WIDTH to move back (or height if going down)\r\nt_length = override LENGTH to move sideways\r\n\"dmg\"\t\tdamage to inflict when blocked (2 default)\r\n\r\nIf a secret door has a targetname, it will only be opened by it's botton or trigger, not by damage.\r\n\"sounds\"\r\n1) medieval\r\n2) metal\r\n3) base\r\n*/\r\n\r\nvoid () func_door_secret =\r\n{\r\n\tif (self.sounds == 0)\r\n\t\tself.sounds = 3;\r\n\tif (self.sounds == 1)\r\n\t{\r\n\t\tprecache_sound (\"doors/latch2.wav\");\r\n\t\tprecache_sound (\"doors/winch2.wav\");\r\n\t\tprecache_sound (\"doors/drclos4.wav\");\r\n\t\tself.noise1 = \"doors/latch2.wav\";\r\n\t\tself.noise2 = \"doors/winch2.wav\";\r\n\t\tself.noise3 = \"doors/drclos4.wav\";\r\n\t}\r\n\tif (self.sounds == 2)\r\n\t{\r\n\t\tprecache_sound (\"doors/airdoor1.wav\");\r\n\t\tprecache_sound (\"doors/airdoor2.wav\");\r\n\t\tself.noise2 = \"doors/airdoor1.wav\";\r\n\t\tself.noise1 = \"doors/airdoor2.wav\";\r\n\t\tself.noise3 = \"doors/airdoor2.wav\";\r\n\t}\r\n\tif (self.sounds == 3)\r\n\t{\r\n\t\tprecache_sound (\"doors/basesec1.wav\");\r\n\t\tprecache_sound (\"doors/basesec2.wav\");\r\n\t\tself.noise2 = \"doors/basesec1.wav\";\r\n\t\tself.noise1 = \"doors/basesec2.wav\";\r\n\t\tself.noise3 = \"doors/basesec2.wav\";\r\n\t}\r\n\r\n\tif (!self.dmg)\r\n\t\tself.dmg = 2;\r\n\t\t\r\n\t// Magic formula...\r\n\tself.mangle = self.angles;\r\n\tself.angles = '0 0 0';\r\n\tself.solid = SOLID_BSP;\r\n\tself.movetype = MOVETYPE_PUSH;\r\n\tself.classname = \"door\";\r\n\tsetmodel (self, self.model);\r\n\tsetorigin (self, self.origin);\t\r\n\t\r\n\tself.touch = secret_touch;\r\n\tself.blocked = secret_blocked;\r\n\tself.speed = 50;\r\n\tself.use = fd_secret_use;\r\n\tif ( !self.targetname || self.spawnflags&SECRET_YES_SHOOT)\r\n\t{\r\n\t\tself.health = 10000;\r\n\t\tself.takedamage = DAMAGE_YES;\r\n\t\tself.th_pain = (void(entity attacker, float damage))fd_secret_use;\r\n\t\tself.th_die = fd_secret_use;\r\n\t}\r\n\tself.oldorigin = self.origin;\r\n\tif (!self.wait)\r\n\t\tself.wait = 5;\t\t// 5 seconds before closing\r\n};\r\n"
  },
  {
    "path": "quakec/csqctest/src/ss/enforcer.qc",
    "content": "/*\r\n==============================================================================\r\n\r\nSOLDIER / PLAYER\r\n\r\n==============================================================================\r\n*/\r\n\r\n$cd id1/models/enforcer\r\n$origin 0 -6 24\r\n$base base\t\t\r\n$skin skin\r\n\r\n$frame stand1 stand2 stand3 stand4 stand5 stand6 stand7\r\n\r\n$frame walk1 walk2 walk3 walk4 walk5 walk6 walk7 walk8 walk9 walk10\r\n$frame walk11 walk12 walk13 walk14 walk15 walk16\r\n\r\n$frame run1 run2 run3 run4 run5 run6 run7 run8\r\n\r\n$frame attack1 attack2 attack3 attack4 attack5 attack6\r\n$frame attack7 attack8 attack9 attack10\r\n\r\n$frame death1 death2 death3 death4 death5 death6 death7 death8\r\n$frame death9 death10 death11 death12 death13 death14\r\n\r\n$frame fdeath1 fdeath2 fdeath3 fdeath4 fdeath5 fdeath6 fdeath7 fdeath8\r\n$frame fdeath9 fdeath10 fdeath11\r\n\r\n$frame paina1 paina2 paina3 paina4\r\n\r\n$frame painb1 painb2 painb3 painb4 painb5\r\n\r\n$frame painc1 painc2 painc3 painc4 painc5 painc6 painc7 painc8\r\n\r\n$frame paind1 paind2 paind3 paind4 paind5 paind6 paind7 paind8\r\n$frame paind9 paind10 paind11 paind12 paind13 paind14 paind15 paind16\r\n$frame paind17 paind18 paind19\r\n\r\n\r\nvoid() Laser_Touch =\r\n{\r\n\tlocal vector org;\r\n\t\r\n\tif (other == self.owner)\r\n\t\treturn;\t\t// don't explode on owner\r\n\r\n\tif (pointcontents(self.origin) == CONTENT_SKY)\r\n\t{\r\n\t\tremove(self);\r\n\t\treturn;\r\n\t}\r\n\t\r\n\tsound (self, CHAN_WEAPON, \"enforcer/enfstop.wav\", 1, ATTN_STATIC);\r\n\torg = self.origin - 8*normalize(self.velocity);\r\n\r\n\tif (other.health)\r\n\t{\r\n\t\tSpawnBlood (org, self.velocity*0.2, 15);\r\n\t\tT_Damage (other, self, self.owner, 15);\r\n\t}\r\n\telse\r\n\t{\r\n\t\tWriteByte (MSG_BROADCAST, SVC_TEMPENTITY);\r\n\t\tWriteByte (MSG_BROADCAST, TE_GUNSHOT);\r\n#ifdef QWSSQC\r\n\t\tWriteByte (MSG_BROADCAST, 1);\t//count\r\n#endif\r\n\t\tWriteCoord (MSG_BROADCAST, org_x);\r\n\t\tWriteCoord (MSG_BROADCAST, org_y);\r\n\t\tWriteCoord (MSG_BROADCAST, org_z);\r\n\t}\r\n\t\r\n\tremove(self);\t\r\n};\r\n\r\nvoid(vector org, vector vec) LaunchLaser =\r\n{\r\n\tif (self.classname == \"monster_enforcer\")\r\n\t\tsound (self, CHAN_WEAPON, \"enforcer/enfire.wav\", 1, ATTN_NORM);\r\n\r\n\tvec = normalize(vec);\r\n\t\r\n\tentity mis = spawn();\r\n\tmis.owner = self;\r\n\tmis.movetype = MOVETYPE_FLY;\r\n\tmis.solid = SOLID_BBOX;\r\n\tmis.effects = EF_DIMLIGHT;\r\n\r\n\tsetmodel (mis, \"progs/laser.mdl\");\r\n\tsetsize (mis, '0 0 0', '0 0 0');\t\t\r\n\r\n\tsetorigin (mis, org);\r\n\r\n\tmis.velocity = vec * 600;\r\n\tmis.angles = vectoangles(mis.velocity);\r\n\r\n\tmis.nextthink = time + 5;\r\n\tmis.think = SUB_Remove;\r\n\tmis.touch = Laser_Touch;\r\n};\r\n\r\n\r\n\r\nvoid() enforcer_fire =\r\n{\r\n\tlocal vector org;\r\n\r\n\tself.effects = self.effects | EF_MUZZLEFLASH;\r\n\tmakevectors (self.angles);\r\n\t\r\n\torg = self.origin + v_forward * 30 + v_right * 8.5 + '0 0 16';\r\n\r\n\tLaunchLaser(org, self.enemy.origin - self.origin);\r\n};\r\n\r\n//============================================================================\r\n\r\nvoid()\tenf_stand1\t=[\t$stand1,\tenf_stand2\t] {ai_stand();};\r\nvoid()\tenf_stand2\t=[\t$stand2,\tenf_stand3\t] {ai_stand();};\r\nvoid()\tenf_stand3\t=[\t$stand3,\tenf_stand4\t] {ai_stand();};\r\nvoid()\tenf_stand4\t=[\t$stand4,\tenf_stand5\t] {ai_stand();};\r\nvoid()\tenf_stand5\t=[\t$stand5,\tenf_stand6\t] {ai_stand();};\r\nvoid()\tenf_stand6\t=[\t$stand6,\tenf_stand7\t] {ai_stand();};\r\nvoid()\tenf_stand7\t=[\t$stand7,\tenf_stand1\t] {ai_stand();};\r\n\r\nvoid()\tenf_walk1\t=[\t$walk1 ,\tenf_walk2\t] {\r\nif (random() < 0.2)\r\n\tsound (self, CHAN_VOICE, \"enforcer/idle1.wav\", 1, ATTN_IDLE);\r\nai_walk(2);};\r\nvoid()\tenf_walk2\t=[\t$walk2 ,\tenf_walk3\t] {ai_walk(4);};\r\nvoid()\tenf_walk3\t=[\t$walk3 ,\tenf_walk4\t] {ai_walk(4);};\r\nvoid()\tenf_walk4\t=[\t$walk4 ,\tenf_walk5\t] {ai_walk(3);};\r\nvoid()\tenf_walk5\t=[\t$walk5 ,\tenf_walk6\t] {ai_walk(1);};\r\nvoid()\tenf_walk6\t=[\t$walk6 ,\tenf_walk7\t] {ai_walk(2);};\r\nvoid()\tenf_walk7\t=[\t$walk7 ,\tenf_walk8\t] {ai_walk(2);};\r\nvoid()\tenf_walk8\t=[\t$walk8 ,\tenf_walk9\t] {ai_walk(1);};\r\nvoid()\tenf_walk9\t=[\t$walk9 ,\tenf_walk10\t] {ai_walk(2);};\r\nvoid()\tenf_walk10\t=[\t$walk10,\tenf_walk11\t] {ai_walk(4);};\r\nvoid()\tenf_walk11\t=[\t$walk11,\tenf_walk12\t] {ai_walk(4);};\r\nvoid()\tenf_walk12\t=[\t$walk12,\tenf_walk13\t] {ai_walk(1);};\r\nvoid()\tenf_walk13\t=[\t$walk13,\tenf_walk14\t] {ai_walk(2);};\r\nvoid()\tenf_walk14\t=[\t$walk14,\tenf_walk15\t] {ai_walk(3);};\r\nvoid()\tenf_walk15\t=[\t$walk15,\tenf_walk16\t] {ai_walk(4);};\r\nvoid()\tenf_walk16\t=[\t$walk16,\tenf_walk1\t] {ai_walk(2);};\r\n\r\nvoid()\tenf_run1\t=[\t$run1  ,\tenf_run2\t] {\r\nif (random() < 0.2)\r\n\tsound (self, CHAN_VOICE, \"enforcer/idle1.wav\", 1, ATTN_IDLE);\r\nai_run(18);};\r\nvoid()\tenf_run2\t=[\t$run2  ,\tenf_run3\t] {ai_run(14);};\r\nvoid()\tenf_run3\t=[\t$run3  ,\tenf_run4\t] {ai_run(7);};\r\nvoid()\tenf_run4\t=[\t$run4  ,\tenf_run5\t] {ai_run(12);};\r\nvoid()\tenf_run5\t=[\t$run5  ,\tenf_run6\t] {ai_run(14);};\r\nvoid()\tenf_run6\t=[\t$run6  ,\tenf_run7\t] {ai_run(14);};\r\nvoid()\tenf_run7\t=[\t$run7  ,\tenf_run8\t] {ai_run(7);};\r\nvoid()\tenf_run8\t=[\t$run8  ,\tenf_run1\t] {ai_run(11);};\r\n\r\nvoid()\tenf_atk1\t=[\t$attack1,\tenf_atk2\t] {ai_face();};\r\nvoid()\tenf_atk2\t=[\t$attack2,\tenf_atk3\t] {ai_face();};\r\nvoid()\tenf_atk3\t=[\t$attack3,\tenf_atk4\t] {ai_face();};\r\nvoid()\tenf_atk4\t=[\t$attack4,\tenf_atk5\t] {ai_face();};\r\nvoid()\tenf_atk5\t=[\t$attack5,\tenf_atk6\t] {ai_face();};\r\nvoid()\tenf_atk6\t=[\t$attack6,\tenf_atk7\t] {enforcer_fire();};\r\nvoid()\tenf_atk7\t=[\t$attack7,\tenf_atk8\t] {ai_face();};\r\nvoid()\tenf_atk8\t=[\t$attack8,\tenf_atk9\t] {ai_face();};\r\nvoid()\tenf_atk9\t=[\t$attack5,\tenf_atk10\t] {ai_face();};\r\nvoid()\tenf_atk10\t=[\t$attack6,\tenf_atk11\t] {enforcer_fire();};\r\nvoid()\tenf_atk11\t=[\t$attack7,\tenf_atk12\t] {ai_face();};\r\nvoid()\tenf_atk12\t=[\t$attack8,\tenf_atk13\t] {ai_face();};\r\nvoid()\tenf_atk13\t=[\t$attack9,\tenf_atk14\t] {ai_face();};\r\nvoid()\tenf_atk14\t=[\t$attack10,\tenf_run1\t] {ai_face();\r\nSUB_CheckRefire (enf_atk1);\r\n};\r\n\r\nvoid()\tenf_paina1\t=[\t$paina1,\tenf_paina2\t] {};\r\nvoid()\tenf_paina2\t=[\t$paina2,\tenf_paina3\t] {};\r\nvoid()\tenf_paina3\t=[\t$paina3,\tenf_paina4\t] {};\r\nvoid()\tenf_paina4\t=[\t$paina4,\tenf_run1\t] {};\r\n\r\nvoid()\tenf_painb1\t=[\t$painb1,\tenf_painb2\t] {};\r\nvoid()\tenf_painb2\t=[\t$painb2,\tenf_painb3\t] {};\r\nvoid()\tenf_painb3\t=[\t$painb3,\tenf_painb4\t] {};\r\nvoid()\tenf_painb4\t=[\t$painb4,\tenf_painb5\t] {};\r\nvoid()\tenf_painb5\t=[\t$painb5,\tenf_run1\t] {};\r\n\r\nvoid()\tenf_painc1\t=[\t$painc1,\tenf_painc2\t] {};\r\nvoid()\tenf_painc2\t=[\t$painc2,\tenf_painc3\t] {};\r\nvoid()\tenf_painc3\t=[\t$painc3,\tenf_painc4\t] {};\r\nvoid()\tenf_painc4\t=[\t$painc4,\tenf_painc5\t] {};\r\nvoid()\tenf_painc5\t=[\t$painc5,\tenf_painc6\t] {};\r\nvoid()\tenf_painc6\t=[\t$painc6,\tenf_painc7\t] {};\r\nvoid()\tenf_painc7\t=[\t$painc7,\tenf_painc8\t] {};\r\nvoid()\tenf_painc8\t=[\t$painc8,\tenf_run1\t] {};\r\n\r\nvoid()\tenf_paind1\t=[\t$paind1,\tenf_paind2\t] {};\r\nvoid()\tenf_paind2\t=[\t$paind2,\tenf_paind3\t] {};\r\nvoid()\tenf_paind3\t=[\t$paind3,\tenf_paind4\t] {};\r\nvoid()\tenf_paind4\t=[\t$paind4,\tenf_paind5\t] {ai_painforward(2);};\r\nvoid()\tenf_paind5\t=[\t$paind5,\tenf_paind6\t] {ai_painforward(1);};\r\nvoid()\tenf_paind6\t=[\t$paind6,\tenf_paind7\t] {};\r\nvoid()\tenf_paind7\t=[\t$paind7,\tenf_paind8\t] {};\r\nvoid()\tenf_paind8\t=[\t$paind8,\tenf_paind9\t] {};\r\nvoid()\tenf_paind9\t=[\t$paind9,\tenf_paind10\t] {};\r\nvoid()\tenf_paind10\t=[\t$paind10,\tenf_paind11\t] {};\r\nvoid()\tenf_paind11\t=[\t$paind11,\tenf_paind12\t] {ai_painforward(1);};\r\nvoid()\tenf_paind12\t=[\t$paind12,\tenf_paind13\t] {ai_painforward(1);};\r\nvoid()\tenf_paind13\t=[\t$paind13,\tenf_paind14\t] {ai_painforward(1);};\r\nvoid()\tenf_paind14\t=[\t$paind14,\tenf_paind15\t] {};\r\nvoid()\tenf_paind15\t=[\t$paind15,\tenf_paind16\t] {};\r\nvoid()\tenf_paind16\t=[\t$paind16,\tenf_paind17\t] {ai_pain(1);};\r\nvoid()\tenf_paind17\t=[\t$paind17,\tenf_paind18\t] {ai_pain(1);};\r\nvoid()\tenf_paind18\t=[\t$paind18,\tenf_paind19\t] {};\r\nvoid()\tenf_paind19\t=[\t$paind19,\tenf_run1\t] {};\r\n\r\nvoid(entity attacker, float damage)\tenf_pain =\r\n{\r\n\tlocal float r;\r\n\r\n\tr = random ();\r\n\tif (self.pain_finished > time)\r\n\t\treturn;\r\n\r\n\t\r\n\tif (r < 0.5)\r\n\t\tsound (self, CHAN_VOICE, \"enforcer/pain1.wav\", 1, ATTN_NORM);\r\n\telse\r\n\t\tsound (self, CHAN_VOICE, \"enforcer/pain2.wav\", 1, ATTN_NORM);\r\n\r\n\tif (r < 0.2)\r\n\t{\r\n\t\tself.pain_finished = time + 1;\r\n\t\tenf_paina1 ();\r\n\t}\r\n\telse if (r < 0.4)\r\n\t{\r\n\t\tself.pain_finished = time + 1;\r\n\t\tenf_painb1 ();\r\n\t}\r\n\telse if (r < 0.7)\r\n\t{\r\n\t\tself.pain_finished = time + 1;\r\n\t\tenf_painc1 ();\r\n\t}\r\n\telse\r\n\t{\r\n\t\tself.pain_finished = time + 2;\r\n\t\tenf_paind1 ();\r\n\t}\r\n};\r\n\r\n//============================================================================\r\n\r\n\r\n\r\n\r\nvoid()\tenf_die1\t=[\t$death1,\tenf_die2\t] {};\r\nvoid()\tenf_die2\t=[\t$death2,\tenf_die3\t] {};\r\nvoid()\tenf_die3\t=[\t$death3,\tenf_die4\t]\r\n{self.solid = SOLID_NOT;self.ammo_cells = 5;DropBackpack();};\r\nvoid()\tenf_die4\t=[\t$death4,\tenf_die5\t] {ai_forward(14);};\r\nvoid()\tenf_die5\t=[\t$death5,\tenf_die6\t] {ai_forward(2);};\r\nvoid()\tenf_die6\t=[\t$death6,\tenf_die7\t] {};\r\nvoid()\tenf_die7\t=[\t$death7,\tenf_die8\t] {};\r\nvoid()\tenf_die8\t=[\t$death8,\tenf_die9\t] {};\r\nvoid()\tenf_die9\t=[\t$death9,\tenf_die10\t] {ai_forward(3);};\r\nvoid()\tenf_die10\t=[\t$death10,\tenf_die11\t] {ai_forward(5);};\r\nvoid()\tenf_die11\t=[\t$death11,\tenf_die12\t] {ai_forward(5);};\r\nvoid()\tenf_die12\t=[\t$death12,\tenf_die13\t] {ai_forward(5);};\r\nvoid()\tenf_die13\t=[\t$death13,\tenf_die14\t] {};\r\nvoid()\tenf_die14\t=[\t$death14,\tenf_die14\t] {};\r\n\r\nvoid()\tenf_fdie1\t=[\t$fdeath1,\tenf_fdie2\t] {\r\n\r\n};\r\nvoid()\tenf_fdie2\t=[\t$fdeath2,\tenf_fdie3\t] {};\r\nvoid()\tenf_fdie3\t=[\t$fdeath3,\tenf_fdie4\t] \r\n{self.solid = SOLID_NOT;self.ammo_cells = 5;DropBackpack();};\r\nvoid()\tenf_fdie4\t=[\t$fdeath4,\tenf_fdie5\t] {};\r\nvoid()\tenf_fdie5\t=[\t$fdeath5,\tenf_fdie6\t] {};\r\nvoid()\tenf_fdie6\t=[\t$fdeath6,\tenf_fdie7\t] {};\r\nvoid()\tenf_fdie7\t=[\t$fdeath7,\tenf_fdie8\t] {};\r\nvoid()\tenf_fdie8\t=[\t$fdeath8,\tenf_fdie9\t] {};\r\nvoid()\tenf_fdie9\t=[\t$fdeath9,\tenf_fdie10\t] {};\r\nvoid()\tenf_fdie10\t=[\t$fdeath10,\tenf_fdie11\t] {};\r\nvoid()\tenf_fdie11\t=[\t$fdeath11,\tenf_fdie11\t] {};\r\n\r\n\r\nvoid() enf_die =\r\n{\r\n// check for gib\r\n\tif (self.health < -35)\r\n\t{\r\n\t\tThrowCSQCGibs(GIB_ENFORCER);\r\n\t\treturn;\r\n\t}\r\n\r\n// regular death\r\n\tsound (self, CHAN_VOICE, \"enforcer/death1.wav\", 1, ATTN_NORM);\r\n\tif (random() > 0.5)\r\n\t\tenf_die1 ();\r\n\telse\r\n\t\tenf_fdie1 ();\r\n};\r\n\r\n\r\n/*QUAKED monster_enforcer (1 0 0) (-16 -16 -24) (16 16 40) Ambush\r\n\r\n*/\r\nvoid() monster_enforcer =\r\n{\r\n\tif (deathmatch)\r\n\t{\r\n\t\tremove(self);\r\n\t\treturn;\r\n\t}\r\n\tprecache_model2 (\"progs/enforcer.mdl\");\r\n\tprecache_model2 (\"progs/h_mega.mdl\");\r\n\tprecache_model2 (\"progs/laser.mdl\");\r\n\r\n\tprecache_sound2 (\"enforcer/death1.wav\");\r\n\tprecache_sound2 (\"enforcer/enfire.wav\");\r\n\tprecache_sound2 (\"enforcer/enfstop.wav\");\r\n\tprecache_sound2 (\"enforcer/idle1.wav\");\r\n\tprecache_sound2 (\"enforcer/pain1.wav\");\r\n\tprecache_sound2 (\"enforcer/pain2.wav\");\r\n\tprecache_sound2 (\"enforcer/sight1.wav\");\r\n\tprecache_sound2 (\"enforcer/sight2.wav\");\r\n\tprecache_sound2 (\"enforcer/sight3.wav\");\r\n\tprecache_sound2 (\"enforcer/sight4.wav\");\r\n\t\r\n\tself.solid = SOLID_SLIDEBOX;\r\n\tself.movetype = MOVETYPE_STEP;\r\n\r\n\tsetmodel (self, \"progs/enforcer.mdl\");\r\n\r\n\tsetsize (self, '-16 -16 -24', '16 16 40');\r\n\tself.health = 80;\r\n\r\n\tself.th_stand = enf_stand1;\r\n\tself.th_walk = enf_walk1;\r\n\tself.th_run = enf_run1;\r\n\tself.th_pain = enf_pain;\r\n\tself.th_die = enf_die;\r\n\tself.th_missile = enf_atk1;\r\n\r\n\twalkmonster_start();\r\n};\r\n"
  },
  {
    "path": "quakec/csqctest/src/ss/fight.qc",
    "content": "\r\n/*\r\n\r\nA monster is in fight mode if it thinks it can effectively attack its\r\nenemy.\r\n\r\nWhen it decides it can't attack, it goes into hunt mode.\r\n\r\n*/\r\n\r\nfloat(float v) anglemod;\r\n\r\nvoid() knight_atk1;\r\nvoid() knight_runatk1;\r\nvoid() ogre_smash1;\r\nvoid() ogre_swing1;\r\n\r\nvoid() sham_smash1;\r\nvoid() sham_swingr1;\r\nvoid() sham_swingl1;\r\n\r\nfloat()\tDemonCheckAttack;\r\nvoid(float side)\tDemon_Melee;\r\n\r\n//void(vector dest) ChooseTurn;\r\n\r\nvoid() ai_face;\r\n\r\n\r\nfloat\tenemy_vis, enemy_infront, enemy_range;\r\nfloat\tenemy_yaw;\r\n\r\n\r\nvoid() knight_attack =\r\n{\r\n\tlocal float\t\tlen;\r\n\t\r\n// decide if now is a good swing time\r\n\tlen = vlen(self.enemy.origin+self.enemy.view_ofs - (self.origin+self.view_ofs));\r\n\t\r\n\tif (len<80)\r\n\t\tknight_atk1 ();\r\n\telse\r\n\t\tknight_runatk1 ();\r\n};\r\n\r\n//=============================================================================\r\n\r\n/*\r\n===========\r\nCheckAttack\r\n\r\nThe player is in view, so decide to move or launch an attack\r\nReturns FALSE if movement should continue\r\n============\r\n*/\r\nfloat() CheckAttack =\r\n{\r\n\tlocal vector\tspot1, spot2;\t\r\n\tlocal entity\ttarg;\r\n\tlocal float\t\tchance;\r\n\r\n\ttarg = self.enemy;\r\n\t\r\n// see if any entities are in the way of the shot\r\n\tspot1 = self.origin + self.view_ofs;\r\n\tspot2 = targ.origin + targ.view_ofs;\r\n\r\n\ttraceline (spot1, spot2, FALSE, self);\r\n\r\n\tif (trace_ent != targ)\r\n\t\treturn FALSE;\t\t// don't have a clear shot\r\n\t\t\t\r\n\tif (trace_inopen && trace_inwater)\r\n\t\treturn FALSE;\t\t\t// sight line crossed contents\r\n\r\n\tif (enemy_range == RANGE_MELEE)\r\n\t{\t// melee attack\r\n\t\tif (self.th_melee)\r\n\t\t{\r\n\t\t\tif (self.classname == \"monster_knight\")\r\n\t\t\t\tknight_attack ();\r\n\t\t\telse\r\n\t\t\t\tself.th_melee ();\r\n\t\t\treturn TRUE;\r\n\t\t}\r\n\t}\r\n\t\r\n// missile attack\r\n\tif (!self.th_missile)\r\n\t\treturn FALSE;\r\n\t\t\r\n\tif (time < self.attack_finished)\r\n\t\treturn FALSE;\r\n\t\t\r\n\tif (enemy_range == RANGE_FAR)\r\n\t\treturn FALSE;\r\n\t\t\r\n\tif (enemy_range == RANGE_MELEE)\r\n\t{\r\n\t\tchance = 0.9;\r\n\t\tself.attack_finished = 0;\r\n\t}\r\n\telse if (enemy_range == RANGE_NEAR)\r\n\t{\r\n\t\tif (self.th_melee)\r\n\t\t\tchance = 0.2;\r\n\t\telse\r\n\t\t\tchance = 0.4;\r\n\t}\r\n\telse if (enemy_range == RANGE_MID)\r\n\t{\r\n\t\tif (self.th_melee)\r\n\t\t\tchance = 0.05;\r\n\t\telse\r\n\t\t\tchance = 0.1;\r\n\t}\r\n\telse\r\n\t\tchance = 0;\r\n\r\n\tif (random () < chance)\r\n\t{\r\n\t\tself.th_missile ();\r\n\t\tSUB_AttackFinished (2*random());\r\n\t\treturn TRUE;\r\n\t}\r\n\r\n\treturn FALSE;\r\n};\r\n\r\n\r\n/*\r\n=============\r\nai_face\r\n\r\nStay facing the enemy\r\n=============\r\n*/\r\nvoid() ai_face =\r\n{\r\n\tself.ideal_yaw = vectoyaw(self.enemy.origin - self.origin);\r\n\tChangeYaw ();\r\n};\r\n\r\n/*\r\n=============\r\nai_charge\r\n\r\nThe monster is in a melee attack, so get as close as possible to .enemy\r\n=============\r\n*/\r\nfloat (entity targ) visible;\r\nfloat(entity targ) infront;\r\nfloat(entity targ) range;\r\n\r\nvoid(float d) ai_charge =\r\n{\r\n\tai_face ();\t\r\n\tmovetogoal (d);\t\t// done in C code...\r\n};\r\n\r\nvoid() ai_charge_side =\r\n{\r\n\tlocal\tvector\tdtemp;\r\n\tlocal\tfloat\theading;\r\n\t\r\n// aim to the left of the enemy for a flyby\r\n\r\n\tself.ideal_yaw = vectoyaw(self.enemy.origin - self.origin);\r\n\tChangeYaw ();\r\n\r\n\tmakevectors (self.angles);\r\n\tdtemp = self.enemy.origin - 30*v_right;\r\n\theading = vectoyaw(dtemp - self.origin);\r\n\t\r\n\twalkmove(heading, 20);\r\n};\r\n\r\n\r\n/*\r\n=============\r\nai_melee\r\n\r\n=============\r\n*/\r\nvoid() ai_melee =\r\n{\r\n\tlocal vector\tdelta;\r\n\tlocal float \tldmg;\r\n\r\n\tif (!self.enemy)\r\n\t\treturn;\t\t// removed before stroke\r\n\t\t\r\n\tdelta = self.enemy.origin - self.origin;\r\n\r\n\tif (vlen(delta) > 60)\r\n\t\treturn;\r\n\t\t\r\n\tldmg = (random() *3) * 3;\r\n\tT_Damage (self.enemy, self, self, ldmg);\r\n};\r\n\r\n\r\nvoid() ai_melee_side =\r\n{\r\n\tlocal vector\tdelta;\r\n\tlocal float \tldmg;\r\n\r\n\tif (!self.enemy)\r\n\t\treturn;\t\t// removed before stroke\r\n\t\t\r\n\tai_charge_side();\r\n\t\r\n\tdelta = self.enemy.origin - self.origin;\r\n\r\n\tif (vlen(delta) > 60)\r\n\t\treturn;\r\n\tif (!CanDamage (self.enemy, self))\r\n\t\treturn;\r\n\tldmg = (random()*3) * 3;\r\n\tT_Damage (self.enemy, self, self, ldmg);\r\n};\r\n\r\n\r\n//=============================================================================\r\n\r\n/*\r\n===========\r\nSoldierCheckAttack\r\n\r\nThe player is in view, so decide to move or launch an attack\r\nReturns FALSE if movement should continue\r\n============\r\n*/\r\nfloat() SoldierCheckAttack =\r\n{\r\n\tlocal vector\tspot1, spot2;\t\r\n\tlocal entity\ttarg;\r\n\tlocal float\t\tchance;\r\n\r\n\ttarg = self.enemy;\r\n\t\r\n// see if any entities are in the way of the shot\r\n\tspot1 = self.origin + self.view_ofs;\r\n\tspot2 = targ.origin + targ.view_ofs;\r\n\r\n\ttraceline (spot1, spot2, FALSE, self);\r\n\r\n\tif (trace_inopen && trace_inwater)\r\n\t\treturn FALSE;\t\t\t// sight line crossed contents\r\n\r\n\tif (trace_ent != targ)\r\n\t\treturn FALSE;\t// don't have a clear shot\r\n\t\t\t\r\n\t\r\n// missile attack\r\n\tif (time < self.attack_finished)\r\n\t\treturn FALSE;\r\n\t\t\r\n\tif (enemy_range == RANGE_FAR)\r\n\t\treturn FALSE;\r\n\t\t\r\n\tif (enemy_range == RANGE_MELEE)\r\n\t\tchance = 0.9;\r\n\telse if (enemy_range == RANGE_NEAR)\r\n\t\tchance = 0.4;\r\n\telse if (enemy_range == RANGE_MID)\r\n\t\tchance = 0.05;\r\n\telse\r\n\t\tchance = 0;\r\n\r\n\tif (random () < chance)\r\n\t{\r\n\t\tself.th_missile ();\r\n\t\tSUB_AttackFinished (1 + random());\r\n\t\tif (random() < 0.3)\r\n\t\t\tself.lefty = !self.lefty;\r\n\r\n\t\treturn TRUE;\r\n\t}\r\n\r\n\treturn FALSE;\r\n};\r\n//=============================================================================\r\n\r\n/*\r\n===========\r\nShamCheckAttack\r\n\r\nThe player is in view, so decide to move or launch an attack\r\nReturns FALSE if movement should continue\r\n============\r\n*/\r\nfloat() ShamCheckAttack =\r\n{\r\n\tlocal vector\tspot1, spot2;\t\r\n\tlocal entity\ttarg;\r\n\r\n\tif (enemy_range == RANGE_MELEE)\r\n\t{\r\n\t\tif (CanDamage (self.enemy, self))\r\n\t\t{\r\n\t\t\tself.attack_state = AS_MELEE;\r\n\t\t\treturn TRUE;\r\n\t\t}\r\n\t}\r\n\r\n\tif (time < self.attack_finished)\r\n\t\treturn FALSE;\r\n\t\r\n\tif (!enemy_vis)\r\n\t\treturn FALSE;\r\n\t\t\r\n\ttarg = self.enemy;\r\n\t\r\n// see if any entities are in the way of the shot\r\n\tspot1 = self.origin + self.view_ofs;\r\n\tspot2 = targ.origin + targ.view_ofs;\r\n\r\n\tif (vlen(spot1 - spot2) > 600)\r\n\t\treturn FALSE;\r\n\r\n\ttraceline (spot1, spot2, FALSE, self);\r\n\r\n\tif (trace_inopen && trace_inwater)\r\n\t\treturn FALSE;\t\t\t// sight line crossed contents\r\n\r\n\tif (trace_ent != targ)\r\n\t{\r\n\t\treturn FALSE;\t// don't have a clear shot\r\n\t}\r\n\t\t\t\r\n// missile attack\r\n\tif (enemy_range == RANGE_FAR)\r\n\t\treturn FALSE;\r\n\t\t\r\n\tself.attack_state = AS_MISSILE;\r\n\tSUB_AttackFinished (2 + 2*random());\r\n\treturn TRUE;\r\n};\r\n\r\n//============================================================================\r\n\r\n/*\r\n===========\r\nOgreCheckAttack\r\n\r\nThe player is in view, so decide to move or launch an attack\r\nReturns FALSE if movement should continue\r\n============\r\n*/\r\nfloat() OgreCheckAttack =\r\n{\r\n\tlocal vector\tspot1, spot2;\t\r\n\tlocal entity\ttarg;\r\n\tlocal float\t\tchance;\r\n\r\n\tif (enemy_range == RANGE_MELEE)\r\n\t{\r\n\t\tif (CanDamage (self.enemy, self))\r\n\t\t{\r\n\t\t\tself.attack_state = AS_MELEE;\r\n\t\t\treturn TRUE;\r\n\t\t}\r\n\t}\r\n\r\n\tif (time < self.attack_finished)\r\n\t\treturn FALSE;\r\n\t\r\n\tif (!enemy_vis)\r\n\t\treturn FALSE;\r\n\t\t\r\n\ttarg = self.enemy;\r\n\t\r\n// see if any entities are in the way of the shot\r\n\tspot1 = self.origin + self.view_ofs;\r\n\tspot2 = targ.origin + targ.view_ofs;\r\n\r\n\ttraceline (spot1, spot2, FALSE, self);\r\n\r\n\tif (trace_inopen && trace_inwater)\r\n\t\treturn FALSE;\t\t\t// sight line crossed contents\r\n\r\n\tif (trace_ent != targ)\r\n\t{\r\n\t\treturn FALSE;\t// don't have a clear shot\r\n\t}\r\n\t\t\t\r\n// missile attack\r\n\tif (time < self.attack_finished)\r\n\t\treturn FALSE;\r\n\t\t\r\n\tif (enemy_range == RANGE_FAR)\r\n\t\treturn FALSE;\r\n\t\t\r\n\telse if (enemy_range == RANGE_NEAR)\r\n\t\tchance = 0.10;\r\n\telse if (enemy_range == RANGE_MID)\r\n\t\tchance = 0.05;\r\n\telse\r\n\t\tchance = 0;\r\n\r\n\tself.attack_state = AS_MISSILE;\r\n\tSUB_AttackFinished (1 + 2*random());\r\n\treturn TRUE;\r\n};\r\n\r\n"
  },
  {
    "path": "quakec/csqctest/src/ss/fish.qc",
    "content": "$cd id1/models/fish\r\n$origin 0 0 24\r\n$base base\t\t\r\n$skin skin\r\n\r\n$frame attack1 attack2 attack3 attack4 attack5 attack6 \r\n$frame attack7 attack8 attack9 attack10 attack11 attack12 attack13 \r\n$frame attack14 attack15 attack16 attack17 attack18 \r\n\r\n$frame death1 death2 death3 death4 death5 death6 death7 \r\n$frame death8 death9 death10 death11 death12 death13 death14 death15 \r\n$frame death16 death17 death18 death19 death20 death21 \r\n\r\n$frame swim1 swim2 swim3 swim4 swim5 swim6 swim7 swim8 \r\n$frame swim9 swim10 swim11 swim12 swim13 swim14 swim15 swim16 swim17 \r\n$frame swim18 \r\n\r\n$frame pain1 pain2 pain3 pain4 pain5 pain6 pain7 pain8 \r\n$frame pain9 \r\n\r\nvoid() swimmonster_start;\r\n\r\nvoid() f_stand1  =[      $swim1, f_stand2 ] {ai_stand();};\r\nvoid() f_stand2  =[      $swim2, f_stand3 ] {ai_stand();};\r\nvoid() f_stand3  =[      $swim3, f_stand4 ] {ai_stand();};\r\nvoid() f_stand4  =[      $swim4, f_stand5 ] {ai_stand();};\r\nvoid() f_stand5  =[      $swim5, f_stand6 ] {ai_stand();};\r\nvoid() f_stand6  =[      $swim6, f_stand7 ] {ai_stand();};\r\nvoid() f_stand7  =[      $swim7, f_stand8 ] {ai_stand();};\r\nvoid() f_stand8  =[      $swim8, f_stand9 ] {ai_stand();};\r\nvoid() f_stand9  =[      $swim9, f_stand10  ] {ai_stand();};\r\nvoid() f_stand10 =[      $swim10, f_stand11 ] {ai_stand();};\r\nvoid() f_stand11 =[      $swim11, f_stand12 ] {ai_stand();};\r\nvoid() f_stand12 =[      $swim12, f_stand13 ] {ai_stand();};\r\nvoid() f_stand13 =[      $swim13, f_stand14 ] {ai_stand();};\r\nvoid() f_stand14 =[      $swim14, f_stand15 ] {ai_stand();};\r\nvoid() f_stand15 =[      $swim15, f_stand16 ] {ai_stand();};\r\nvoid() f_stand16 =[      $swim16, f_stand17 ] {ai_stand();};\r\nvoid() f_stand17 =[      $swim17, f_stand18 ] {ai_stand();};\r\nvoid() f_stand18 =[      $swim18, f_stand1 ] {ai_stand();};\r\n\r\nvoid() f_walk1  =[      $swim1, f_walk2 ] {ai_walk(8);};\r\nvoid() f_walk2  =[      $swim2, f_walk3 ] {ai_walk(8);};\r\nvoid() f_walk3  =[      $swim3, f_walk4 ] {ai_walk(8);};\r\nvoid() f_walk4  =[      $swim4, f_walk5 ] {ai_walk(8);};\r\nvoid() f_walk5  =[      $swim5, f_walk6 ] {ai_walk(8);};\r\nvoid() f_walk6  =[      $swim6, f_walk7 ] {ai_walk(8);};\r\nvoid() f_walk7  =[      $swim7, f_walk8 ] {ai_walk(8);};\r\nvoid() f_walk8  =[      $swim8, f_walk9 ] {ai_walk(8);};\r\nvoid() f_walk9  =[      $swim9, f_walk10  ] {ai_walk(8);};\r\nvoid() f_walk10 =[      $swim10, f_walk11 ] {ai_walk(8);};\r\nvoid() f_walk11 =[      $swim11, f_walk12 ] {ai_walk(8);};\r\nvoid() f_walk12 =[      $swim12, f_walk13 ] {ai_walk(8);};\r\nvoid() f_walk13 =[      $swim13, f_walk14 ] {ai_walk(8);};\r\nvoid() f_walk14 =[      $swim14, f_walk15 ] {ai_walk(8);};\r\nvoid() f_walk15 =[      $swim15, f_walk16 ] {ai_walk(8);};\r\nvoid() f_walk16 =[      $swim16, f_walk17 ] {ai_walk(8);};\r\nvoid() f_walk17 =[      $swim17, f_walk18 ] {ai_walk(8);};\r\nvoid() f_walk18 =[      $swim18, f_walk1 ] {ai_walk(8);};\r\n\r\nvoid() f_run1  =[      $swim1, f_run2 ] {ai_run(12);\r\n\tif (random() < 0.5)\r\n\t\tsound (self, CHAN_VOICE, \"fish/idle.wav\", 1, ATTN_NORM);\r\n};\r\nvoid() f_run2  =[      $swim3, f_run3 ] {ai_run(12);};\r\nvoid() f_run3  =[      $swim5, f_run4 ] {ai_run(12);};\r\nvoid() f_run4  =[      $swim7, f_run5 ] {ai_run(12);};\r\nvoid() f_run5  =[      $swim9, f_run6 ] {ai_run(12);};\r\nvoid() f_run6  =[      $swim11, f_run7 ] {ai_run(12);};\r\nvoid() f_run7  =[      $swim13, f_run8 ] {ai_run(12);};\r\nvoid() f_run8  =[      $swim15, f_run9 ] {ai_run(12);};\r\nvoid() f_run9  =[      $swim17, f_run1 ] {ai_run(12);};\r\n\r\nvoid() fish_melee =\r\n{\r\n\tlocal vector\tdelta;\r\n\tlocal float \tldmg;\r\n\r\n\tif (!self.enemy)\r\n\t\treturn;\t\t// removed before stroke\r\n\t\t\r\n\tdelta = self.enemy.origin - self.origin;\r\n\r\n\tif (vlen(delta) > 60)\r\n\t\treturn;\r\n\t\t\r\n\tsound (self, CHAN_VOICE, \"fish/bite.wav\", 1, ATTN_NORM);\r\n\tldmg = (random()*2) * 3;\r\n\tT_Damage (self.enemy, self, self, ldmg);\r\n};\r\n\r\nvoid() f_attack1        =[      $attack1,       f_attack2 ] {ai_charge(10);};\r\nvoid() f_attack2        =[      $attack2,       f_attack3 ] {ai_charge(10);};\r\nvoid() f_attack3        =[      $attack3,       f_attack4 ] {fish_melee();};\r\nvoid() f_attack4        =[      $attack4,       f_attack5 ] {ai_charge(10);};\r\nvoid() f_attack5        =[      $attack5,       f_attack6 ] {ai_charge(10);};\r\nvoid() f_attack6        =[      $attack6,       f_attack7 ] {ai_charge(10);};\r\nvoid() f_attack7        =[      $attack7,       f_attack8 ] {ai_charge(10);};\r\nvoid() f_attack8        =[      $attack8,       f_attack9 ] {ai_charge(10);};\r\nvoid() f_attack9        =[      $attack9,       f_attack10] {fish_melee();};\r\nvoid() f_attack10       =[      $attack10,      f_attack11] {ai_charge(10);};\r\nvoid() f_attack11       =[      $attack11,      f_attack12] {ai_charge(10);};\r\nvoid() f_attack12       =[      $attack12,      f_attack13] {ai_charge(10);};\r\nvoid() f_attack13       =[      $attack13,      f_attack14] {ai_charge(10);};\r\nvoid() f_attack14       =[      $attack14,      f_attack15] {ai_charge(10);};\r\nvoid() f_attack15       =[      $attack15,      f_attack16] {fish_melee();};\r\nvoid() f_attack16       =[      $attack16,      f_attack17] {ai_charge(10);};\r\nvoid() f_attack17       =[      $attack17,      f_attack18] {ai_charge(10);};\r\nvoid() f_attack18       =[      $attack18,      f_run1    ] {ai_charge(10);};\r\n\r\nvoid() f_death1 =[      $death1,        f_death2        ] {\r\nsound (self, CHAN_VOICE, \"fish/death.wav\", 1, ATTN_NORM);\r\n};\r\nvoid() f_death2 =[      $death2,        f_death3        ] {};\r\nvoid() f_death3 =[      $death3,        f_death4        ] {};\r\nvoid() f_death4 =[      $death4,        f_death5        ] {};\r\nvoid() f_death5 =[      $death5,        f_death6        ] {};\r\nvoid() f_death6 =[      $death6,        f_death7        ] {};\r\nvoid() f_death7 =[      $death7,        f_death8        ] {};\r\nvoid() f_death8 =[      $death8,        f_death9        ] {};\r\nvoid() f_death9 =[      $death9,        f_death10       ] {};\r\nvoid() f_death10 =[      $death10,       f_death11       ] {};\r\nvoid() f_death11 =[      $death11,       f_death12       ] {};\r\nvoid() f_death12 =[      $death12,       f_death13       ] {};\r\nvoid() f_death13 =[      $death13,       f_death14       ] {};\r\nvoid() f_death14 =[      $death14,       f_death15       ] {};\r\nvoid() f_death15 =[      $death15,       f_death16       ] {};\r\nvoid() f_death16 =[      $death16,       f_death17       ] {};\r\nvoid() f_death17 =[      $death17,       f_death18       ] {};\r\nvoid() f_death18 =[      $death18,       f_death19       ] {};\r\nvoid() f_death19 =[      $death19,       f_death20       ] {};\r\nvoid() f_death20 =[      $death20,       f_death21       ] {};\r\nvoid() f_death21 =[      $death21,       f_death21       ] {self.solid = SOLID_NOT;};\r\n\r\nvoid() f_pain1  =[      $pain1, f_pain2 ] {};\r\nvoid() f_pain2  =[      $pain2, f_pain3 ] {ai_pain(6);};\r\nvoid() f_pain3  =[      $pain3, f_pain4 ] {ai_pain(6);};\r\nvoid() f_pain4  =[      $pain4, f_pain5 ] {ai_pain(6);};\r\nvoid() f_pain5  =[      $pain5, f_pain6 ] {ai_pain(6);};\r\nvoid() f_pain6  =[      $pain6, f_pain7 ] {ai_pain(6);};\r\nvoid() f_pain7  =[      $pain7, f_pain8 ] {ai_pain(6);};\r\nvoid() f_pain8  =[      $pain8, f_pain9 ] {ai_pain(6);};\r\nvoid() f_pain9  =[      $pain9, f_run1 ] {ai_pain(6);};\r\n\r\nvoid(entity attacker, float damage)\tfish_pain =\r\n{\r\n\r\n// fish allways do pain frames\r\n\tf_pain1 ();\r\n};\r\n\r\n\r\n\r\n/*QUAKED monster_fish (1 0 0) (-16 -16 -24) (16 16 24) Ambush\r\n*/\r\nvoid() monster_fish =\r\n{\r\n\tif (deathmatch)\r\n\t{\r\n\t\tremove(self);\r\n\t\treturn;\r\n\t}\r\n\tprecache_model2 (\"progs/fish.mdl\");\r\n\r\n\tprecache_sound2 (\"fish/death.wav\");\r\n\tprecache_sound2 (\"fish/bite.wav\");\r\n\tprecache_sound2 (\"fish/idle.wav\");\r\n\r\n\tself.solid = SOLID_SLIDEBOX;\r\n\tself.movetype = MOVETYPE_STEP;\r\n\r\n\tsetmodel (self, \"progs/fish.mdl\");\r\n\r\n\tsetsize (self, '-16 -16 -24', '16 16 24');\r\n\tself.health = 25;\r\n\t\r\n\tself.th_stand = f_stand1;\r\n\tself.th_walk = f_walk1;\r\n\tself.th_run = f_run1;\r\n\tself.th_die = f_death1;\r\n\tself.th_pain = fish_pain;\r\n\tself.th_melee = f_attack1;\r\n\t\r\n\tswimmonster_start ();\r\n};\r\n\r\n"
  },
  {
    "path": "quakec/csqctest/src/ss/flag.qc",
    "content": "/*QUAKED item_deathball (.3 .3 1) (0 0 0) (32 32 32)\n*/\nvoid() deathball_touch;\n\nvoid() item_deathball =\n{\t\n\tself.touch = deathball_touch;\n};"
  },
  {
    "path": "quakec/csqctest/src/ss/hknight.qc",
    "content": "/*\r\n==============================================================================\r\n\r\nKNIGHT\r\n\r\n==============================================================================\r\n*/\r\n\r\n$cd id1/models/knight2\r\n$origin 0 0 24\r\n$base base\r\n$skin skin\r\n\r\n$frame stand1 stand2 stand3 stand4 stand5 stand6 stand7 stand8 stand9\r\n\r\n$frame walk1 walk2 walk3 walk4 walk5 walk6 walk7 walk8 walk9\r\n$frame walk10 walk11 walk12 walk13 walk14 walk15 walk16 walk17\r\n$frame walk18 walk19 walk20\r\n\r\n$frame run1 run2 run3 run4 run5 run6 run7 run8\r\n\r\n$frame pain1 pain2 pain3 pain4 pain5\r\n\r\n$frame death1 death2 death3 death4 death5 death6 death7 death8\r\n$frame death9 death10 death11 death12\r\n\r\n$frame deathb1 deathb2 deathb3 deathb4 deathb5 deathb6 deathb7 deathb8\r\n$frame deathb9\r\n\r\n$frame char_a1 char_a2 char_a3 char_a4 char_a5 char_a6 char_a7 char_a8\r\n$frame char_a9 char_a10 char_a11 char_a12 char_a13 char_a14 char_a15 char_a16\r\n\r\n$frame magica1 magica2 magica3 magica4 magica5 magica6 magica7 magica8\r\n$frame magica9 magica10 magica11 magica12 magica13 magica14\r\n\r\n$frame magicb1 magicb2 magicb3 magicb4 magicb5 magicb6 magicb7 magicb8\r\n$frame magicb9 magicb10 magicb11 magicb12 magicb13\r\n\r\n$frame char_b1 char_b2 char_b3 char_b4 char_b5 char_b6\r\n\r\n$frame slice1 slice2 slice3 slice4 slice5 slice6 slice7 slice8 slice9 slice10\r\n\r\n$frame smash1 smash2 smash3 smash4 smash5 smash6 smash7 smash8 smash9 smash10\r\n$frame smash11\r\n\r\n$frame w_attack1 w_attack2 w_attack3 w_attack4 w_attack5 w_attack6 w_attack7 \r\n$frame w_attack8 w_attack9 w_attack10 w_attack11 w_attack12 w_attack13 w_attack14\r\n$frame w_attack15 w_attack16 w_attack17 w_attack18 w_attack19 w_attack20 \r\n$frame w_attack21 w_attack22 \r\n\r\n$frame magicc1 magicc2 magicc3 magicc4 magicc5 magicc6 magicc7 magicc8\r\n$frame magicc9 magicc10 magicc11\r\n\r\n\r\nvoid() hknight_char_a1;\r\nvoid() hknight_run1;\r\nvoid() hk_idle_sound;\r\n\r\nvoid(float offset) hknight_shot =\r\n{\r\n\tlocal\tvector\toffang;\r\n\tlocal\tvector\torg, vec;\r\n\t\r\n\toffang = vectoangles (self.enemy.origin - self.origin);\r\n\toffang_y = offang_y + offset * 6;\r\n\t\r\n\tmakevectors (offang);\r\n\r\n\torg = self.origin + self.mins + self.size*0.5 + v_forward * 20;\r\n\r\n// set missile speed\r\n\tvec = normalize (v_forward);\r\n\tvec_z = 0 - vec_z + (random() - 0.5)*0.1;\r\n\t\r\n\tlaunch_spike (org, vec);\r\n\tnewmis.classname = \"knightspike\";\r\n\tsetmodel (newmis, \"progs/k_spike.mdl\");\r\n\tsetsize (newmis, VEC_ORIGIN, VEC_ORIGIN);\t\t\r\n\tnewmis.velocity = vec*300;\r\n\tsound (self, CHAN_WEAPON, \"hknight/attack1.wav\", 1, ATTN_NORM);\r\n};\r\n\r\nvoid() CheckForCharge =\r\n{\r\n// check for mad charge\r\nif (!enemy_vis)\r\n\treturn;\r\nif (time < self.attack_finished)\r\n\treturn;\t\r\nif ( fabs(self.origin_z - self.enemy.origin_z) > 20)\r\n\treturn;\t\t// too much height change\r\nif ( vlen (self.origin - self.enemy.origin) < 80)\r\n\treturn;\t\t// use regular attack\r\n\r\n// charge\t\t\r\n\tSUB_AttackFinished (2);\r\n\thknight_char_a1 ();\r\n\r\n};\r\n\r\nvoid() CheckContinueCharge =\r\n{\r\n\tif (time > self.attack_finished)\r\n\t{\r\n\t\tSUB_AttackFinished (3);\r\n\t\thknight_run1 ();\r\n\t\treturn;\t\t// done charging\r\n\t}\r\n\tif (random() > 0.5)\r\n\t\tsound (self, CHAN_WEAPON, \"knight/sword2.wav\", 1, ATTN_NORM);\r\n\telse\r\n\t\tsound (self, CHAN_WEAPON, \"knight/sword1.wav\", 1, ATTN_NORM);\r\n};\r\n\r\n//===========================================================================\r\n\r\nvoid()\thknight_stand1\t=[\t$stand1,\thknight_stand2\t] {ai_stand();};\r\nvoid()\thknight_stand2\t=[\t$stand2,\thknight_stand3\t] {ai_stand();};\r\nvoid()\thknight_stand3\t=[\t$stand3,\thknight_stand4\t] {ai_stand();};\r\nvoid()\thknight_stand4\t=[\t$stand4,\thknight_stand5\t] {ai_stand();};\r\nvoid()\thknight_stand5\t=[\t$stand5,\thknight_stand6\t] {ai_stand();};\r\nvoid()\thknight_stand6\t=[\t$stand6,\thknight_stand7\t] {ai_stand();};\r\nvoid()\thknight_stand7\t=[\t$stand7,\thknight_stand8\t] {ai_stand();};\r\nvoid()\thknight_stand8\t=[\t$stand8,\thknight_stand9\t] {ai_stand();};\r\nvoid()\thknight_stand9\t=[\t$stand9,\thknight_stand1\t] {ai_stand();};\r\n\r\n//===========================================================================\r\n\r\nvoid()\thknight_walk1\t=[\t$walk1,\t\thknight_walk2\t] {\r\nhk_idle_sound();\r\nai_walk(2);};\r\nvoid()\thknight_walk2\t=[\t$walk2,\t\thknight_walk3\t] {ai_walk(5);};\r\nvoid()\thknight_walk3\t=[\t$walk3,\t\thknight_walk4\t] {ai_walk(5);};\r\nvoid()\thknight_walk4\t=[\t$walk4,\t\thknight_walk5\t] {ai_walk(4);};\r\nvoid()\thknight_walk5\t=[\t$walk5,\t\thknight_walk6\t] {ai_walk(4);};\r\nvoid()\thknight_walk6\t=[\t$walk6,\t\thknight_walk7\t] {ai_walk(2);};\r\nvoid()\thknight_walk7\t=[\t$walk7,\t\thknight_walk8\t] {ai_walk(2);};\r\nvoid()\thknight_walk8\t=[\t$walk8,\t\thknight_walk9\t] {ai_walk(3);};\r\nvoid()\thknight_walk9\t=[\t$walk9,\t\thknight_walk10\t] {ai_walk(3);};\r\nvoid()\thknight_walk10\t=[\t$walk10,\thknight_walk11\t] {ai_walk(4);};\r\nvoid()\thknight_walk11\t=[\t$walk11,\thknight_walk12\t] {ai_walk(3);};\r\nvoid()\thknight_walk12\t=[\t$walk12,\thknight_walk13\t] {ai_walk(4);};\r\nvoid()\thknight_walk13\t=[\t$walk13,\thknight_walk14\t] {ai_walk(6);};\r\nvoid()\thknight_walk14\t=[\t$walk14,\thknight_walk15\t] {ai_walk(2);};\r\nvoid()\thknight_walk15\t=[\t$walk15,\thknight_walk16\t] {ai_walk(2);};\r\nvoid()\thknight_walk16\t=[\t$walk16,\thknight_walk17\t] {ai_walk(4);};\r\nvoid()\thknight_walk17\t=[\t$walk17,\thknight_walk18\t] {ai_walk(3);};\r\nvoid()\thknight_walk18\t=[\t$walk18,\thknight_walk19\t] {ai_walk(3);};\r\nvoid()\thknight_walk19\t=[\t$walk19,\thknight_walk20\t] {ai_walk(3);};\r\nvoid()\thknight_walk20\t=[\t$walk20,\thknight_walk1\t] {ai_walk(2);};\r\n\r\n//===========================================================================\r\n\r\nvoid()\thknight_run1\t=[\t$run1,\t\thknight_run2\t] {\r\nhk_idle_sound();\r\nai_run (20); CheckForCharge (); };\r\nvoid()\thknight_run2\t=[\t$run2,\t\thknight_run3\t] {ai_run(25);};\r\nvoid()\thknight_run3\t=[\t$run3,\t\thknight_run4\t] {ai_run(18);};\r\nvoid()\thknight_run4\t=[\t$run4,\t\thknight_run5\t] {ai_run(16);};\r\nvoid()\thknight_run5\t=[\t$run5,\t\thknight_run6\t] {ai_run(14);};\r\nvoid()\thknight_run6\t=[\t$run6,\t\thknight_run7\t] {ai_run(25);};\r\nvoid()\thknight_run7\t=[\t$run7,\t\thknight_run8\t] {ai_run(21);};\r\nvoid()\thknight_run8\t=[\t$run8,\t\thknight_run1\t] {ai_run(13);};\r\n\r\n//============================================================================\r\n\r\nvoid()\thknight_pain1\t=[\t$pain1,\t\thknight_pain2\t] {sound (self, CHAN_VOICE, \"hknight/pain1.wav\", 1, ATTN_NORM);};\r\nvoid()\thknight_pain2\t=[\t$pain2,\t\thknight_pain3\t] {};\r\nvoid()\thknight_pain3\t=[\t$pain3,\t\thknight_pain4\t] {};\r\nvoid()\thknight_pain4\t=[\t$pain4,\t\thknight_pain5\t] {};\r\nvoid()\thknight_pain5\t=[\t$pain5,\t\thknight_run1\t] {};\r\n\r\n//============================================================================\r\n\r\nvoid()\thknight_die1\t=[\t$death1,\thknight_die2\t] {ai_forward(10);};\r\nvoid()\thknight_die2\t=[\t$death2,\thknight_die3\t] {ai_forward(8);};\r\nvoid()\thknight_die3\t=[\t$death3,\thknight_die4\t]\r\n{self.solid = SOLID_NOT; ai_forward(7);};\r\nvoid()\thknight_die4\t=[\t$death4,\thknight_die5\t] {};\r\nvoid()\thknight_die5\t=[\t$death5,\thknight_die6\t] {};\r\nvoid()\thknight_die6\t=[\t$death6,\thknight_die7\t] {};\r\nvoid()\thknight_die7\t=[\t$death7,\thknight_die8\t] {};\r\nvoid()\thknight_die8\t=[\t$death8,\thknight_die9\t] {ai_forward(10);};\r\nvoid()\thknight_die9\t=[\t$death9,\thknight_die10\t] {ai_forward(11);};\r\nvoid()\thknight_die10\t=[\t$death10,\thknight_die11\t] {};\r\nvoid()\thknight_die11\t=[\t$death11,\thknight_die12\t] {};\r\nvoid()\thknight_die12\t=[\t$death12,\thknight_die12\t] {};\r\n\r\nvoid()\thknight_dieb1\t=[\t$deathb1,\thknight_dieb2\t] {};\r\nvoid()\thknight_dieb2\t=[\t$deathb2,\thknight_dieb3\t] {};\r\nvoid()\thknight_dieb3\t=[\t$deathb3,\thknight_dieb4\t]\r\n{self.solid = SOLID_NOT;};\r\nvoid()\thknight_dieb4\t=[\t$deathb4,\thknight_dieb5\t] {};\r\nvoid()\thknight_dieb5\t=[\t$deathb5,\thknight_dieb6\t] {};\r\nvoid()\thknight_dieb6\t=[\t$deathb6,\thknight_dieb7\t] {};\r\nvoid()\thknight_dieb7\t=[\t$deathb7,\thknight_dieb8\t] {};\r\nvoid()\thknight_dieb8\t=[\t$deathb8,\thknight_dieb9\t] {};\r\nvoid()\thknight_dieb9\t=[\t$deathb9,\thknight_dieb9\t] {};\r\n\r\nvoid() hknight_die =\r\n{\r\n// check for gib\r\n\tif (self.health < -40)\r\n\t{\r\n\t\tThrowCSQCGibs(GIB_HELLKNIGHT);\r\n\t\treturn;\r\n\t}\r\n\r\n// regular death\r\n\tsound (self, CHAN_VOICE, \"hknight/death1.wav\", 1, ATTN_NORM);\r\n\tif (random() > 0.5)\r\n\t\thknight_die1 ();\r\n\telse\r\n\t\thknight_dieb1 ();\r\n};\r\n\r\n\r\n//============================================================================\r\n\r\nvoid()\thknight_magica1 =[\t$magica1,\thknight_magica2\t] {ai_face();};\r\nvoid()\thknight_magica2 =[\t$magica2,\thknight_magica3\t] {ai_face();};\r\nvoid()\thknight_magica3 =[\t$magica3,\thknight_magica4\t] {ai_face();};\r\nvoid()\thknight_magica4 =[\t$magica4,\thknight_magica5\t] {ai_face();};\r\nvoid()\thknight_magica5 =[\t$magica5,\thknight_magica6\t] {ai_face();};\r\nvoid()\thknight_magica6 =[\t$magica6,\thknight_magica7\t] {ai_face();};\r\nvoid()\thknight_magica7 =[\t$magica7,\thknight_magica8\t] {hknight_shot(-2);};\r\nvoid()\thknight_magica8 =[\t$magica8,\thknight_magica9\t] {hknight_shot(-1);};\r\nvoid()\thknight_magica9 =[\t$magica9,\thknight_magica10] {hknight_shot(0);};\r\nvoid()\thknight_magica10 =[\t$magica10,\thknight_magica11] {hknight_shot(1);};\r\nvoid()\thknight_magica11 =[\t$magica11,\thknight_magica12] {hknight_shot(2);};\r\nvoid()\thknight_magica12 =[\t$magica12,\thknight_magica13] {hknight_shot(3);};\r\nvoid()\thknight_magica13 =[\t$magica13,\thknight_magica14] {ai_face();};\r\nvoid()\thknight_magica14 =[\t$magica14,\thknight_run1\t] {ai_face();};\r\n\r\n//============================================================================\r\n\r\nvoid()\thknight_magicb1 =[\t$magicb1,\thknight_magicb2\t] {ai_face();};\r\nvoid()\thknight_magicb2 =[\t$magicb2,\thknight_magicb3\t] {ai_face();};\r\nvoid()\thknight_magicb3 =[\t$magicb3,\thknight_magicb4\t] {ai_face();};\r\nvoid()\thknight_magicb4 =[\t$magicb4,\thknight_magicb5\t] {ai_face();};\r\nvoid()\thknight_magicb5 =[\t$magicb5,\thknight_magicb6\t] {ai_face();};\r\nvoid()\thknight_magicb6 =[\t$magicb6,\thknight_magicb7\t] {ai_face();};\r\nvoid()\thknight_magicb7 =[\t$magicb7,\thknight_magicb8\t] {hknight_shot(-2);};\r\nvoid()\thknight_magicb8 =[\t$magicb8,\thknight_magicb9\t] {hknight_shot(-1);};\r\nvoid()\thknight_magicb9 =[\t$magicb9,\thknight_magicb10] {hknight_shot(0);};\r\nvoid()\thknight_magicb10 =[\t$magicb10,\thknight_magicb11] {hknight_shot(1);};\r\nvoid()\thknight_magicb11 =[\t$magicb11,\thknight_magicb12] {hknight_shot(2);};\r\nvoid()\thknight_magicb12 =[\t$magicb12,\thknight_magicb13] {hknight_shot(3);};\r\nvoid()\thknight_magicb13 =[\t$magicb13,\thknight_run1] {ai_face();};\r\n\r\n//============================================================================\r\n\r\nvoid()\thknight_magicc1 =[\t$magicc1,\thknight_magicc2\t] {ai_face();};\r\nvoid()\thknight_magicc2 =[\t$magicc2,\thknight_magicc3\t] {ai_face();};\r\nvoid()\thknight_magicc3 =[\t$magicc3,\thknight_magicc4\t] {ai_face();};\r\nvoid()\thknight_magicc4 =[\t$magicc4,\thknight_magicc5\t] {ai_face();};\r\nvoid()\thknight_magicc5 =[\t$magicc5,\thknight_magicc6\t] {ai_face();};\r\nvoid()\thknight_magicc6 =[\t$magicc6,\thknight_magicc7\t] {hknight_shot(-2);};\r\nvoid()\thknight_magicc7 =[\t$magicc7,\thknight_magicc8\t] {hknight_shot(-1);};\r\nvoid()\thknight_magicc8 =[\t$magicc8,\thknight_magicc9\t] {hknight_shot(0);};\r\nvoid()\thknight_magicc9 =[\t$magicc9,\thknight_magicc10] {hknight_shot(1);};\r\nvoid()\thknight_magicc10 =[\t$magicc10,\thknight_magicc11] {hknight_shot(2);};\r\nvoid()\thknight_magicc11 =[\t$magicc11,\thknight_run1] {hknight_shot(3);};\r\n\r\n//===========================================================================\r\n\r\nvoid()\thknight_char_a1\t=[\t$char_a1,\thknight_char_a2\t] {ai_charge(20);};\r\nvoid()\thknight_char_a2\t=[\t$char_a2,\thknight_char_a3\t] {ai_charge(25);};\r\nvoid()\thknight_char_a3\t=[\t$char_a3,\thknight_char_a4\t] {ai_charge(18);};\r\nvoid()\thknight_char_a4\t=[\t$char_a4,\thknight_char_a5\t] {ai_charge(16);};\r\nvoid()\thknight_char_a5\t=[\t$char_a5,\thknight_char_a6\t] {ai_charge(14);};\r\nvoid()\thknight_char_a6\t=[\t$char_a6,\thknight_char_a7\t] {ai_charge(20); ai_melee();};\r\nvoid()\thknight_char_a7\t=[\t$char_a7,\thknight_char_a8\t] {ai_charge(21); ai_melee();};\r\nvoid()\thknight_char_a8\t=[\t$char_a8,\thknight_char_a9\t] {ai_charge(13); ai_melee();};\r\nvoid()\thknight_char_a9\t=[\t$char_a9,\thknight_char_a10\t] {ai_charge(20); ai_melee();};\r\nvoid()\thknight_char_a10=[\t$char_a10,\thknight_char_a11\t] {ai_charge(20); ai_melee();};\r\nvoid()\thknight_char_a11=[\t$char_a11,\thknight_char_a12\t] {ai_charge(18); ai_melee();};\r\nvoid()\thknight_char_a12=[\t$char_a12,\thknight_char_a13\t] {ai_charge(16);};\r\nvoid()\thknight_char_a13=[\t$char_a13,\thknight_char_a14\t] {ai_charge(14);};\r\nvoid()\thknight_char_a14=[\t$char_a14,\thknight_char_a15\t] {ai_charge(25);};\r\nvoid()\thknight_char_a15=[\t$char_a15,\thknight_char_a16\t] {ai_charge(21);};\r\nvoid()\thknight_char_a16=[\t$char_a16,\thknight_run1\t] {ai_charge(13);};\r\n\r\n//===========================================================================\r\n\r\nvoid()\thknight_char_b1\t=[\t$char_b1,\thknight_char_b2\t]\r\n{CheckContinueCharge (); ai_charge(23); ai_melee();};\r\nvoid()\thknight_char_b2\t=[\t$char_b2,\thknight_char_b3\t] {ai_charge(17); ai_melee();};\r\nvoid()\thknight_char_b3\t=[\t$char_b3,\thknight_char_b4\t] {ai_charge(12); ai_melee();};\r\nvoid()\thknight_char_b4\t=[\t$char_b4,\thknight_char_b5\t] {ai_charge(22); ai_melee();};\r\nvoid()\thknight_char_b5\t=[\t$char_b5,\thknight_char_b6\t] {ai_charge(18); ai_melee();};\r\nvoid()\thknight_char_b6\t=[\t$char_b6,\thknight_char_b1\t] {ai_charge(8); ai_melee();};\r\n\r\n//===========================================================================\r\n\r\nvoid()\thknight_slice1\t=[\t$slice1,\thknight_slice2\t] {ai_charge(9);};\r\nvoid()\thknight_slice2\t=[\t$slice2,\thknight_slice3\t] {ai_charge(6);};\r\nvoid()\thknight_slice3\t=[\t$slice3,\thknight_slice4\t] {ai_charge(13);};\r\nvoid()\thknight_slice4\t=[\t$slice4,\thknight_slice5\t] {ai_charge(4);};\r\nvoid()\thknight_slice5\t=[\t$slice5,\thknight_slice6\t] {ai_charge(7); ai_melee();};\r\nvoid()\thknight_slice6\t=[\t$slice6,\thknight_slice7\t] {ai_charge(15); ai_melee();};\r\nvoid()\thknight_slice7\t=[\t$slice7,\thknight_slice8\t] {ai_charge(8); ai_melee();};\r\nvoid()\thknight_slice8\t=[\t$slice8,\thknight_slice9\t] {ai_charge(2); ai_melee();};\r\nvoid()\thknight_slice9\t=[\t$slice9,\thknight_slice10\t] {ai_melee();};\r\nvoid()\thknight_slice10\t=[\t$slice10,\thknight_run1\t] {ai_charge(3);};\r\n\r\n//===========================================================================\r\n\r\nvoid()\thknight_smash1\t=[\t$smash1,\thknight_smash2\t] {ai_charge(1);};\r\nvoid()\thknight_smash2\t=[\t$smash2,\thknight_smash3\t] {ai_charge(13);};\r\nvoid()\thknight_smash3\t=[\t$smash3,\thknight_smash4\t] {ai_charge(9);};\r\nvoid()\thknight_smash4\t=[\t$smash4,\thknight_smash5\t] {ai_charge(11);};\r\nvoid()\thknight_smash5\t=[\t$smash5,\thknight_smash6\t] {ai_charge(10); ai_melee();};\r\nvoid()\thknight_smash6\t=[\t$smash6,\thknight_smash7\t] {ai_charge(7); ai_melee();};\r\nvoid()\thknight_smash7\t=[\t$smash7,\thknight_smash8\t] {ai_charge(12); ai_melee();};\r\nvoid()\thknight_smash8\t=[\t$smash8,\thknight_smash9\t] {ai_charge(2); ai_melee();};\r\nvoid()\thknight_smash9\t=[\t$smash9,\thknight_smash10\t] {ai_charge(3); ai_melee();};\r\nvoid()\thknight_smash10\t=[\t$smash10,\thknight_smash11\t] {ai_charge(0);};\r\nvoid()\thknight_smash11\t=[\t$smash11,\thknight_run1\t] {ai_charge(0);};\r\n\r\n//============================================================================\r\n\r\nvoid()\thknight_watk1\t=[\t$w_attack1,\thknight_watk2\t] {ai_charge(2);};\r\nvoid()\thknight_watk2\t=[\t$w_attack2,\thknight_watk3\t] {ai_charge(0);};\r\nvoid()\thknight_watk3\t=[\t$w_attack3,\thknight_watk4\t] {ai_charge(0);};\r\nvoid()\thknight_watk4\t=[\t$w_attack4,\thknight_watk5\t] {ai_melee();};\r\nvoid()\thknight_watk5\t=[\t$w_attack5,\thknight_watk6\t] {ai_melee();};\r\nvoid()\thknight_watk6\t=[\t$w_attack6,\thknight_watk7\t] {ai_melee();};\r\nvoid()\thknight_watk7\t=[\t$w_attack7,\thknight_watk8\t] {ai_charge(1);};\r\nvoid()\thknight_watk8\t=[\t$w_attack8,\thknight_watk9\t] {ai_charge(4);};\r\nvoid()\thknight_watk9\t=[\t$w_attack9,\thknight_watk10\t] {ai_charge(5);};\r\nvoid()\thknight_watk10\t=[\t$w_attack10,\thknight_watk11\t] {ai_charge(3); ai_melee();};\r\nvoid()\thknight_watk11\t=[\t$w_attack11,\thknight_watk12\t] {ai_charge(2); ai_melee();};\r\nvoid()\thknight_watk12\t=[\t$w_attack12,\thknight_watk13\t] {ai_charge(2); ai_melee();};\r\nvoid()\thknight_watk13\t=[\t$w_attack13,\thknight_watk14\t] {ai_charge(0);};\r\nvoid()\thknight_watk14\t=[\t$w_attack14,\thknight_watk15\t] {ai_charge(0);};\r\nvoid()\thknight_watk15\t=[\t$w_attack15,\thknight_watk16\t] {ai_charge(0);};\r\nvoid()\thknight_watk16\t=[\t$w_attack16,\thknight_watk17\t] {ai_charge(1);};\r\nvoid()\thknight_watk17\t=[\t$w_attack17,\thknight_watk18\t] {ai_charge(1); ai_melee();};\r\nvoid()\thknight_watk18\t=[\t$w_attack18,\thknight_watk19\t] {ai_charge(3); ai_melee();};\r\nvoid()\thknight_watk19\t=[\t$w_attack19,\thknight_watk20\t] {ai_charge(4); ai_melee();};\r\nvoid()\thknight_watk20\t=[\t$w_attack20,\thknight_watk21\t] {ai_charge(6);};\r\nvoid()\thknight_watk21\t=[\t$w_attack21,\thknight_watk22\t] {ai_charge(7);};\r\nvoid()\thknight_watk22\t=[\t$w_attack22,\thknight_run1\t] {ai_charge(3);};\r\n\r\n//============================================================================\r\n\r\nvoid() hk_idle_sound =\r\n{\r\n\tif (random() < 0.2)\r\n\t\tsound (self, CHAN_VOICE, \"hknight/idle.wav\", 1, ATTN_NORM);\r\n};\r\n\r\nvoid(entity attacker, float damage)\thknight_pain =\r\n{\r\n\tif (self.pain_finished > time)\r\n\t\treturn;\r\n\r\n\tsound (self, CHAN_VOICE, \"hknight/pain1.wav\", 1, ATTN_NORM);\r\n\r\n\tif (time - self.pain_finished > 5)\r\n\t{\t// allways go into pain frame if it has been a while\r\n\t\thknight_pain1 ();\r\n\t\tself.pain_finished = time + 1;\r\n\t\treturn;\r\n\t}\r\n\t\r\n\tif ((random()*30 > damage) )\r\n\t\treturn;\t\t// didn't flinch\r\n\r\n\tself.pain_finished = time + 1;\r\n\thknight_pain1 ();\r\n};\r\n\r\nfloat\thknight_type;\r\n\r\nvoid() hknight_melee =\r\n{\r\n\thknight_type = hknight_type + 1;\r\n\r\n\tsound (self, CHAN_WEAPON, \"hknight/slash1.wav\", 1, ATTN_NORM);\r\n\tif (hknight_type == 1)\r\n\t\thknight_slice1 ();\r\n\telse if (hknight_type == 2)\r\n\t\thknight_smash1 ();\r\n\telse if (hknight_type == 3)\r\n\t{\r\n\t\thknight_watk1 ();\r\n\t\thknight_type = 0;\r\n\t}\r\n};\r\n\r\n/*QUAKED monster_hell_knight (1 0 0) (-16 -16 -24) (16 16 40) Ambush\r\n*/\r\nvoid() monster_hell_knight =\r\n{\r\n\tif (deathmatch)\r\n\t{\r\n\t\tremove(self);\r\n\t\treturn;\r\n\t}\r\n\tprecache_model2 (\"progs/hknight.mdl\");\r\n\tprecache_model2 (\"progs/k_spike.mdl\");\r\n\tprecache_model2 (\"progs/h_hellkn.mdl\");\r\n\r\n\t\r\n\tprecache_sound2 (\"hknight/attack1.wav\");\r\n\tprecache_sound2 (\"hknight/death1.wav\");\r\n\tprecache_sound2 (\"hknight/pain1.wav\");\r\n\tprecache_sound2 (\"hknight/sight1.wav\");\r\n\tprecache_sound (\"hknight/hit.wav\");\t\t// used by C code, so don't sound2\r\n\tprecache_sound2 (\"hknight/slash1.wav\");\r\n\tprecache_sound2 (\"hknight/idle.wav\");\r\n\tprecache_sound2 (\"hknight/grunt.wav\");\r\n\r\n\tprecache_sound (\"knight/sword1.wav\");\r\n\tprecache_sound (\"knight/sword2.wav\");\r\n\t\r\n\tself.solid = SOLID_SLIDEBOX;\r\n\tself.movetype = MOVETYPE_STEP;\r\n\r\n\tsetmodel (self, \"progs/hknight.mdl\");\r\n\r\n\tsetsize (self, '-16 -16 -24', '16 16 40');\r\n\tself.health = 250;\r\n\r\n\tself.th_stand = hknight_stand1;\r\n\tself.th_walk = hknight_walk1;\r\n\tself.th_run = hknight_run1;\r\n\tself.th_melee = hknight_melee;\r\n\tself.th_missile = hknight_magicc1;\r\n\tself.th_pain = hknight_pain;\r\n\tself.th_die = hknight_die;\r\n\t\r\n\twalkmonster_start ();\r\n};\r\n"
  },
  {
    "path": "quakec/csqctest/src/ss/items.qc",
    "content": "void() W_SetCurrentAmmo;\r\n/* ALL LIGHTS SHOULD BE 0 1 0 IN COLOR ALL OTHER ITEMS SHOULD\r\nBE .8 .3 .4 IN COLOR */\r\n\r\n\r\nvoid() SUB_regen =\r\n{\r\n\tself.model = self.mdl;\t\t// restore original model\r\n\tself.solid = SOLID_TRIGGER;\t// allow it to be touched again\r\n\tsound (self, CHAN_VOICE, \"items/itembk2.wav\", 1, ATTN_NORM);\t// play respawn sound\r\n\tsetorigin (self, self.origin);\r\n};\r\n\r\n\r\n\r\n/*QUAKED noclass (0 0 0) (-8 -8 -8) (8 8 8)\r\nprints a warning message when spawned\r\n*/\r\nvoid() noclass =\r\n{\r\n\tdprint (\"noclass spawned at\");\r\n\tdprint (vtos(self.origin));\r\n\tdprint (\"\\n\");\r\n\tremove (self);\r\n};\r\n\r\n\r\n\r\n/*\r\n============\r\nPlaceItem\r\n\r\nplants the object on the floor\r\n============\r\n*/\r\nvoid() PlaceItem =\r\n{\r\n\tlocal float\toldz;\r\n\r\n\tself.mdl = self.model;\t\t// so it can be restored on respawn\r\n\tself.flags = FL_ITEM;\t\t// make extra wide\r\n\tself.solid = SOLID_TRIGGER;\r\n\tself.movetype = MOVETYPE_TOSS;\t\r\n\tself.velocity = '0 0 0';\r\n\tself.origin_z = self.origin_z + 6;\r\n\toldz = self.origin_z;\r\n\tif (!droptofloor())\r\n\t{\r\n\t\tdprint (\"Bonus item fell out of level at \");\r\n\t\tdprint (vtos(self.origin));\r\n\t\tdprint (\"\\n\");\r\n\t\tremove(self);\r\n\t\treturn;\r\n\t}\r\n};\r\n\r\n/*\r\n============\r\nStartItem\r\n\r\nSets the clipping size and plants the object on the floor\r\n============\r\n*/\r\nvoid() StartItem =\r\n{\r\n\tself.nextthink = time + 0.2;\t// items start after other solids\r\n\tself.think = PlaceItem;\r\n};\r\n\r\n/*\r\n=========================================================================\r\n\r\nHEALTH BOX\r\n\r\n=========================================================================\r\n*/\r\n//\r\n// T_Heal: add health to an entity, limiting health to max_health\r\n// \"ignore\" will ignore max_health limit\r\n//\r\nfloat (entity e, float healamount, float ignore) T_Heal =\r\n{\r\n\tif (e.health <= 0)\r\n\t\treturn 0;\r\n\tif ((!ignore) && (e.health >= other.max_health))\r\n\t\treturn 0;\r\n\thealamount = ceil(healamount);\r\n\r\n\te.health = e.health + healamount;\r\n\tif ((!ignore) && (e.health >= other.max_health))\r\n\t\te.health = other.max_health;\r\n\t\t\r\n\tif (e.health > 250)\r\n\t\te.health = 250;\r\n\treturn 1;\r\n};\r\n\r\n/*QUAKED item_health (.3 .3 1) (0 0 0) (32 32 32) rotten megahealth\r\nHealth box. Normally gives 25 points.\r\nRotten box heals 5-10 points,\r\nmegahealth will add 100 health, then \r\nrot you down to your maximum health limit, \r\none point per second.\r\n*/\r\n\r\nfloat\tH_ROTTEN = 1;\r\nfloat\tH_MEGA = 2;\r\n.float\thealamount, healtype;\r\nvoid() health_touch;\r\nvoid() item_megahealth_rot;\r\n\r\nvoid() item_health =\r\n{\t\r\n\tself.touch = health_touch;\r\n\r\n\tif (self.spawnflags & H_ROTTEN)\r\n\t{\r\n\t\tprecache_model(\"maps/b_bh10.bsp\");\r\n\r\n\t\tprecache_sound(\"items/r_item1.wav\");\r\n\t\tsetmodel(self, \"maps/b_bh10.bsp\");\r\n\t\tself.noise = \"items/r_item1.wav\";\r\n\t\tself.healamount = 15;\r\n\t\tself.healtype = 0;\r\n\t}\r\n\telse\r\n\tif (self.spawnflags & H_MEGA)\r\n\t{\r\n\t\tprecache_model(\"maps/b_bh100.bsp\");\r\n\t\tprecache_sound(\"items/r_item2.wav\");\r\n\t\tsetmodel(self, \"maps/b_bh100.bsp\");\r\n\t\tself.noise = \"items/r_item2.wav\";\r\n\t\tself.healamount = 100;\r\n\t\tself.healtype = 2;\r\n\t}\r\n\telse\r\n\t{\r\n\t\tprecache_model(\"maps/b_bh25.bsp\");\r\n\t\tprecache_sound(\"items/health1.wav\");\r\n\t\tsetmodel(self, \"maps/b_bh25.bsp\");\r\n\t\tself.noise = \"items/health1.wav\";\r\n\t\tself.healamount = 25;\r\n\t\tself.healtype = 1;\r\n\t}\r\n\tsetsize (self, '0 0 0', '32 32 56');\r\n\tStartItem ();\r\n};\r\n\r\n\r\nvoid() health_touch =\r\n{\r\n\tlocal\tstring\ts;\r\n\t\r\n\tif (other.classname != \"player\")\r\n\t\treturn;\r\n\t\r\n\tif (self.healtype == 2) // Megahealth?  Ignore max_health...\r\n\t{\r\n\t\tif (other.health >= 250)\r\n\t\t\treturn;\r\n\t\tif (!T_Heal(other, self.healamount, 1))\r\n\t\t\treturn;\r\n\t}\r\n\telse\r\n\t{\r\n\t\tif (!T_Heal(other, self.healamount, 0))\r\n\t\t\treturn;\r\n\t}\r\n\t\r\n\tsprint(other, \"You receive \");\r\n\ts = ftos(self.healamount);\r\n\tsprint(other, s);\r\n\tsprint(other, \" health\\n\");\r\n\t\r\n// health touch sound\r\n\tsound(other, CHAN_ITEM, self.noise, 1, ATTN_NORM);\r\n\r\n\tstuffcmd (other, \"bf\\n\");\r\n\t\r\n\tself.model = string_null;\r\n\tself.solid = SOLID_NOT;\r\n\r\n\t// Megahealth = rot down the player's super health\r\n\tif (self.healtype == 2)\r\n\t{\r\n\t\tother.items = other.items | IT_SUPERHEALTH;\r\n\t\tself.nextthink = time + 5;\r\n\t\tself.think = item_megahealth_rot;\r\n\t\tself.owner = other;\r\n\t}\r\n\telse\r\n\t{\r\n\t\tif (deathmatch != 2)\t\t// deathmatch 2 is the silly old rules\r\n\t\t{\r\n\t\t\tif (deathmatch)\r\n\t\t\t\tself.nextthink = time + 20;\r\n\t\t\tself.think = SUB_regen;\r\n\t\t}\r\n\t}\r\n\t\r\n\tactivator = other;\r\n\tSUB_UseTargets();\t\t\t\t// fire all targets / killtargets\r\n};\t\r\n\r\nvoid() item_megahealth_rot =\r\n{\r\n\tother = self.owner;\r\n\t\r\n\tif (other.health > other.max_health)\r\n\t{\r\n\t\tother.health = other.health - 1;\r\n\t\tself.nextthink = time + 1;\r\n\t\treturn;\r\n\t}\r\n\r\n// it is possible for a player to die and respawn between rots, so don't\r\n// just blindly subtract the flag off\r\n\tother.items = other.items - (other.items & IT_SUPERHEALTH);\r\n\t\r\n\tif (deathmatch == 1)\t// deathmatch 2 is silly old rules\r\n\t{\r\n\t\tself.nextthink = time + 20;\r\n\t\tself.think = SUB_regen;\r\n\t}\r\n};\r\n\r\n/*\r\n===============================================================================\r\n\r\nARMOR\r\n\r\n===============================================================================\r\n*/\r\n\r\nvoid() armor_touch;\r\n\r\nvoid() armor_touch =\r\n{\r\n\tlocal\tfloat\ttype, value, bit;\r\n\t\r\n\tif (other.health <= 0)\r\n\t\treturn;\r\n\tif (other.classname != \"player\")\r\n\t\treturn;\r\n\r\n\tif (self.classname == \"item_armor1\")\r\n\t{\r\n\t\ttype = 0.3;\r\n\t\tvalue = 100;\r\n\t\tbit = IT_ARMOR1;\r\n\t}\r\n\telse if (self.classname == \"item_armor2\")\r\n\t{\r\n\t\ttype = 0.6;\r\n\t\tvalue = 150;\r\n\t\tbit = IT_ARMOR2;\r\n\t}\r\n\telse if (self.classname == \"item_armorInv\")\r\n\t{\r\n\t\ttype = 0.8;\r\n\t\tvalue = 200;\r\n\t\tbit = IT_ARMOR3;\r\n\t}\r\n\telse\r\n\t\treturn;\r\n\tif (other.armortype*other.armorvalue >= type*value)\r\n\t\treturn;\r\n\t\t\r\n\tother.armortype = type;\r\n\tother.armorvalue = value;\r\n\tother.items = other.items - (other.items & (IT_ARMOR1 | IT_ARMOR2 | IT_ARMOR3)) + bit;\r\n\r\n\tself.solid = SOLID_NOT;\r\n\tself.model = string_null;\r\n\tif (deathmatch == 1)\r\n\t\tself.nextthink = time + 20;\r\n\tself.think = SUB_regen;\r\n\r\n\tsprint(other, \"You got armor\\n\");\r\n// armor touch sound\r\n\tsound(other, CHAN_ITEM, \"items/armor1.wav\", 1, ATTN_NORM);\r\n\tstuffcmd (other, \"bf\\n\");\r\n\t\r\n\tactivator = other;\r\n\tSUB_UseTargets();\t\t\t\t// fire all targets / killtargets\r\n};\r\n\r\n\r\n/*QUAKED item_armor1 (0 .5 .8) (-16 -16 0) (16 16 32)\r\n*/\r\n\r\nvoid() item_armor1 =\r\n{\r\n\tself.touch = armor_touch;\r\n\tprecache_model (\"progs/armor.mdl\");\r\n\tsetmodel (self, \"progs/armor.mdl\");\r\n\tself.skin = 0;\r\n\tsetsize (self, '-16 -16 0', '16 16 56');\r\n\tStartItem ();\r\n};\r\n\r\n/*QUAKED item_armor2 (0 .5 .8) (-16 -16 0) (16 16 32)\r\n*/\r\n\r\nvoid() item_armor2 =\r\n{\r\n\tself.touch = armor_touch;\r\n\tprecache_model (\"progs/armor.mdl\");\r\n\tsetmodel (self, \"progs/armor.mdl\");\r\n\tself.skin = 1;\r\n\tsetsize (self, '-16 -16 0', '16 16 56');\r\n\tStartItem ();\r\n};\r\n\r\n/*QUAKED item_armorInv (0 .5 .8) (-16 -16 0) (16 16 32)\r\n*/\r\n\r\nvoid() item_armorInv =\r\n{\r\n\tself.touch = armor_touch;\r\n\tprecache_model (\"progs/armor.mdl\");\r\n\tsetmodel (self, \"progs/armor.mdl\");\r\n\tself.skin = 2;\r\n\tsetsize (self, '-16 -16 0', '16 16 56');\r\n\tStartItem ();\r\n};\r\n\r\n/*\r\n===============================================================================\r\n\r\nWEAPONS\r\n\r\n===============================================================================\r\n*/\r\n\r\nvoid() bound_other_ammo =\r\n{\r\n\tif (other.ammo_shells > 100)\r\n\t\tother.ammo_shells = 100;\r\n\tif (other.ammo_nails > 200)\r\n\t\tother.ammo_nails = 200;\r\n\tif (other.ammo_rockets > 100)\r\n\t\tother.ammo_rockets = 100;\t\t\r\n\tif (other.ammo_cells > 100)\r\n\t\tother.ammo_cells = 100;\t\t\r\n};\r\n\r\n\r\nfloat(float w) RankForWeapon =\r\n{\r\n\tif (w == IT_LIGHTNING)\r\n\t\treturn 1;\r\n\tif (w == IT_ROCKET_LAUNCHER)\r\n\t\treturn 2;\r\n\tif (w == IT_SUPER_NAILGUN)\r\n\t\treturn 3;\r\n\tif (w == IT_GRENADE_LAUNCHER)\r\n\t\treturn 4;\r\n\tif (w == IT_SUPER_SHOTGUN)\r\n\t\treturn 5;\r\n\tif (w == IT_NAILGUN)\r\n\t\treturn 6;\r\n\treturn 7;\r\n};\r\n\r\n/*\r\n=============\r\nDeathmatch_Weapon\r\n\r\nDeathmatch weapon change rules for picking up a weapon\r\n\r\n.float\t\tammo_shells, ammo_nails, ammo_rockets, ammo_cells;\r\n=============\r\n*/\r\nvoid(float old, float new) Deathmatch_Weapon =\r\n{\r\n\tlocal float or, nr;\r\n\r\n// change self.weapon if desired\r\n\tor = RankForWeapon (self.weapon);\r\n\tnr = RankForWeapon (new);\r\n\tif ( nr < or )\r\n\t\tself.weapon = new;\r\n};\r\n\r\n/*\r\n=============\r\nweapon_touch\r\n=============\r\n*/\r\nfloat() W_BestWeapon;\r\n\r\nvoid() weapon_touch =\r\n{\r\n\tlocal\tfloat\thadammo, best, new, old;\r\n\tlocal\tentity\tstemp;\r\n\tlocal\tfloat\tleave;\r\n\r\n\tif (!(other.flags & FL_CLIENT))\r\n\t\treturn;\r\n\r\n// if the player was using his best weapon, change up to the new one if better\t\t\r\n\tstemp = self;\r\n\tself = other;\r\n\tbest = W_BestWeapon();\r\n\tself = stemp;\r\n\r\n\tif (deathmatch == 2 || coop)\r\n\t\tleave = 1;\r\n\telse\r\n\t\tleave = 0;\r\n\t\r\n\tif (self.classname == \"weapon_nailgun\")\r\n\t{\r\n\t\tif (leave && (other.items & IT_NAILGUN) )\r\n\t\t\treturn;\r\n\t\thadammo = other.ammo_nails;\t\t\t\r\n\t\tnew = IT_NAILGUN;\r\n\t\tother.ammo_nails = other.ammo_nails + 30;\r\n\t}\r\n\telse if (self.classname == \"weapon_supernailgun\")\r\n\t{\r\n\t\tif (leave && (other.items & IT_SUPER_NAILGUN) )\r\n\t\t\treturn;\r\n\t\thadammo = other.ammo_rockets;\t\t\t\r\n\t\tnew = IT_SUPER_NAILGUN;\r\n\t\tother.ammo_nails = other.ammo_nails + 30;\r\n\t}\r\n\telse if (self.classname == \"weapon_supershotgun\")\r\n\t{\r\n\t\tif (leave && (other.items & IT_SUPER_SHOTGUN) )\r\n\t\t\treturn;\r\n\t\thadammo = other.ammo_rockets;\t\t\t\r\n\t\tnew = IT_SUPER_SHOTGUN;\r\n\t\tother.ammo_shells = other.ammo_shells + 5;\r\n\t}\r\n\telse if (self.classname == \"weapon_rocketlauncher\")\r\n\t{\r\n\t\tif (leave && (other.items & IT_ROCKET_LAUNCHER) )\r\n\t\t\treturn;\r\n\t\thadammo = other.ammo_rockets;\t\t\t\r\n\t\tnew = IT_ROCKET_LAUNCHER;\r\n\t\tother.ammo_rockets = other.ammo_rockets + 5;\r\n\t}\r\n\telse if (self.classname == \"weapon_grenadelauncher\")\r\n\t{\r\n\t\tif (leave && (other.items & IT_GRENADE_LAUNCHER) )\r\n\t\t\treturn;\r\n\t\thadammo = other.ammo_rockets;\t\t\t\r\n\t\tnew = IT_GRENADE_LAUNCHER;\r\n\t\tother.ammo_rockets = other.ammo_rockets + 5;\r\n\t}\r\n\telse if (self.classname == \"weapon_lightning\")\r\n\t{\r\n\t\tif (leave && (other.items & IT_LIGHTNING) )\r\n\t\t\treturn;\r\n\t\thadammo = other.ammo_rockets;\t\t\t\r\n\t\tnew = IT_LIGHTNING;\r\n\t\tother.ammo_cells = other.ammo_cells + 15;\r\n\t}\r\n\telse\r\n\t{\r\n\t\tobjerror (\"weapon_touch: unknown classname\");\r\n\t\tnew = 0;\r\n\t}\r\n\r\n\tsprint (other, \"You got the \");\r\n\tsprint (other, self.netname);\r\n\tsprint (other, \"\\n\");\r\n// weapon touch sound\r\n\tsound (other, CHAN_ITEM, \"weapons/pkup.wav\", 1, ATTN_NORM);\r\n\tstuffcmd (other, \"bf\\n\");\r\n\r\n\tbound_other_ammo ();\r\n\r\n// change to the weapon\r\n\told = other.items;\r\n\tother.items = other.items | new;\r\n\t\r\n\tstemp = self;\r\n\tself = other;\r\n\r\n\tif (!deathmatch)\r\n\t\tself.weapon = new;\r\n\telse\r\n\t\tDeathmatch_Weapon (old, new);\r\n\r\n\tW_SetCurrentAmmo();\r\n\r\n\tself = stemp;\r\n\r\n\tif (leave)\r\n\t\treturn;\r\n\r\n// remove it in single player, or setup for respawning in deathmatch\r\n\tself.model = string_null;\r\n\tself.solid = SOLID_NOT;\r\n\tif (deathmatch == 1)\r\n\t\tself.nextthink = time + 30;\r\n\tself.think = SUB_regen;\r\n\t\r\n\tactivator = other;\r\n\tSUB_UseTargets();\t\t\t\t// fire all targets / killtargets\r\n};\r\n\r\n\r\n/*QUAKED weapon_supershotgun (0 .5 .8) (-16 -16 0) (16 16 32)\r\n*/\r\n\r\nvoid() weapon_supershotgun =\r\n{\r\n\tprecache_model (\"progs/g_shot.mdl\");\r\n\tsetmodel (self, \"progs/g_shot.mdl\");\r\n\tself.weapon = IT_SUPER_SHOTGUN;\r\n\tself.netname = \"Double-barrelled Shotgun\";\r\n\tself.touch = weapon_touch;\r\n\tsetsize (self, '-16 -16 0', '16 16 56');\r\n\tStartItem ();\r\n};\r\n\r\n/*QUAKED weapon_nailgun (0 .5 .8) (-16 -16 0) (16 16 32)\r\n*/\r\n\r\nvoid() weapon_nailgun =\r\n{\r\n\tprecache_model (\"progs/g_nail.mdl\");\r\n\tsetmodel (self, \"progs/g_nail.mdl\");\r\n\tself.weapon = IT_NAILGUN;\r\n\tself.netname = \"nailgun\";\r\n\tself.touch = weapon_touch;\r\n\tsetsize (self, '-16 -16 0', '16 16 56');\r\n\tStartItem ();\r\n};\r\n\r\n/*QUAKED weapon_supernailgun (0 .5 .8) (-16 -16 0) (16 16 32)\r\n*/\r\n\r\nvoid() weapon_supernailgun =\r\n{\r\n\tprecache_model (\"progs/g_nail2.mdl\");\r\n\tsetmodel (self, \"progs/g_nail2.mdl\");\r\n\tself.weapon = IT_SUPER_NAILGUN;\r\n\tself.netname = \"Super Nailgun\";\r\n\tself.touch = weapon_touch;\r\n\tsetsize (self, '-16 -16 0', '16 16 56');\r\n\tStartItem ();\r\n};\r\n\r\n/*QUAKED weapon_grenadelauncher (0 .5 .8) (-16 -16 0) (16 16 32)\r\n*/\r\n\r\nvoid() weapon_grenadelauncher =\r\n{\r\n\tprecache_model (\"progs/g_rock.mdl\");\r\n\tsetmodel (self, \"progs/g_rock.mdl\");\r\n\tself.weapon = 3;\r\n\tself.netname = \"Grenade Launcher\";\r\n\tself.touch = weapon_touch;\r\n\tsetsize (self, '-16 -16 0', '16 16 56');\r\n\tStartItem ();\r\n};\r\n\r\n/*QUAKED weapon_rocketlauncher (0 .5 .8) (-16 -16 0) (16 16 32)\r\n*/\r\n\r\nvoid() weapon_rocketlauncher =\r\n{\r\n\tprecache_model (\"progs/g_rock2.mdl\");\r\n\tsetmodel (self, \"progs/g_rock2.mdl\");\r\n\tself.weapon = 3;\r\n\tself.netname = \"Rocket Launcher\";\r\n\tself.touch = weapon_touch;\r\n\tsetsize (self, '-16 -16 0', '16 16 56');\r\n\tStartItem ();\r\n};\r\n\r\n\r\n/*QUAKED weapon_lightning (0 .5 .8) (-16 -16 0) (16 16 32)\r\n*/\r\n\r\nvoid() weapon_lightning =\r\n{\r\n\tprecache_model (\"progs/g_light.mdl\");\r\n\tsetmodel (self, \"progs/g_light.mdl\");\r\n\tself.weapon = 3;\r\n\tself.netname = \"Thunderbolt\";\r\n\tself.touch = weapon_touch;\r\n\tsetsize (self, '-16 -16 0', '16 16 56');\r\n\tStartItem ();\r\n};\r\n\r\n\r\n/*\r\n===============================================================================\r\n\r\nAMMO\r\n\r\n===============================================================================\r\n*/\r\n\r\nvoid() ammo_touch =\r\n{\r\nlocal entity\tstemp;\r\nlocal float\t\tbest;\r\n\r\n\tif (other.classname != \"player\")\r\n\t\treturn;\r\n\tif (other.health <= 0)\r\n\t\treturn;\r\n\r\n// if the player was using his best weapon, change up to the new one if better\t\t\r\n\tstemp = self;\r\n\tself = other;\r\n\tbest = W_BestWeapon();\r\n\tself = stemp;\r\n\r\n\r\n// shotgun\r\n\tif (self.weapon == 1)\r\n\t{\r\n\t\tif (other.ammo_shells >= 100)\r\n\t\t\treturn;\r\n\t\tother.ammo_shells = other.ammo_shells + self.aflag;\r\n\t}\r\n\r\n// spikes\r\n\tif (self.weapon == 2)\r\n\t{\r\n\t\tif (other.ammo_nails >= 200)\r\n\t\t\treturn;\r\n\t\tother.ammo_nails = other.ammo_nails + self.aflag;\r\n\t}\r\n\r\n//\trockets\r\n\tif (self.weapon == 3)\r\n\t{\r\n\t\tif (other.ammo_rockets >= 100)\r\n\t\t\treturn;\r\n\t\tother.ammo_rockets = other.ammo_rockets + self.aflag;\r\n\t}\r\n\r\n//\tcells\r\n\tif (self.weapon == 4)\r\n\t{\r\n\t\tif (other.ammo_cells >= 100)\r\n\t\t\treturn;\r\n\t\tother.ammo_cells = other.ammo_cells + self.aflag;\r\n\t}\r\n\r\n\tbound_other_ammo ();\r\n\t\r\n\tsprint (other, \"You got the \");\r\n\tsprint (other, self.netname);\r\n\tsprint (other, \"\\n\");\r\n// ammo touch sound\r\n\tsound (other, CHAN_ITEM, \"weapons/lock4.wav\", 1, ATTN_NORM);\r\n\tstuffcmd (other, \"bf\\n\");\r\n\r\n// change to a better weapon if appropriate\r\n\r\n\tif ( other.weapon == best )\r\n\t{\r\n\t\tstemp = self;\r\n\t\tself = other;\r\n\t\tself.weapon = W_BestWeapon();\r\n\t\tW_SetCurrentAmmo ();\r\n\t\tself = stemp;\r\n\t}\r\n\r\n// if changed current ammo, update it\r\n\tstemp = self;\r\n\tself = other;\r\n\tW_SetCurrentAmmo();\r\n\tself = stemp;\r\n\r\n// remove it in single player, or setup for respawning in deathmatch\r\n\tself.model = string_null;\r\n\tself.solid = SOLID_NOT;\r\n\tif (deathmatch == 1)\r\n\t\tself.nextthink = time + 30;\r\n\tself.think = SUB_regen;\r\n\r\n\tactivator = other;\r\n\tSUB_UseTargets();\t\t\t\t// fire all targets / killtargets\r\n};\r\n\r\n\r\n\r\n\r\nfloat WEAPON_BIG2 = 1;\r\n\r\n/*QUAKED item_shells (0 .5 .8) (0 0 0) (32 32 32) big\r\n*/\r\n\r\nvoid() item_shells =\r\n{\r\n\tself.touch = ammo_touch;\r\n\r\n\tif (self.spawnflags & WEAPON_BIG2)\r\n\t{\r\n\t\tprecache_model (\"maps/b_shell1.bsp\");\r\n\t\tsetmodel (self, \"maps/b_shell1.bsp\");\r\n\t\tself.aflag = 40;\r\n\t}\r\n\telse\r\n\t{\r\n\t\tprecache_model (\"maps/b_shell0.bsp\");\r\n\t\tsetmodel (self, \"maps/b_shell0.bsp\");\r\n\t\tself.aflag = 20;\r\n\t}\r\n\tself.weapon = 1;\r\n\tself.netname = \"shells\";\r\n\tsetsize (self, '0 0 0', '32 32 56');\r\n\tStartItem ();\r\n};\r\n\r\n/*QUAKED item_spikes (0 .5 .8) (0 0 0) (32 32 32) big\r\n*/\r\n\r\nvoid() item_spikes =\r\n{\r\n\tself.touch = ammo_touch;\r\n\r\n\tif (self.spawnflags & WEAPON_BIG2)\r\n\t{\r\n\t\tprecache_model (\"maps/b_nail1.bsp\");\r\n\t\tsetmodel (self, \"maps/b_nail1.bsp\");\r\n\t\tself.aflag = 50;\r\n\t}\r\n\telse\r\n\t{\r\n\t\tprecache_model (\"maps/b_nail0.bsp\");\r\n\t\tsetmodel (self, \"maps/b_nail0.bsp\");\r\n\t\tself.aflag = 25;\r\n\t}\r\n\tself.weapon = 2;\r\n\tself.netname = \"nails\";\r\n\tsetsize (self, '0 0 0', '32 32 56');\r\n\tStartItem ();\r\n};\r\n\r\n/*QUAKED item_rockets (0 .5 .8) (0 0 0) (32 32 32) big\r\n*/\r\n\r\nvoid() item_rockets =\r\n{\r\n\tself.touch = ammo_touch;\r\n\r\n\tif (self.spawnflags & WEAPON_BIG2)\r\n\t{\r\n\t\tprecache_model (\"maps/b_rock1.bsp\");\r\n\t\tsetmodel (self, \"maps/b_rock1.bsp\");\r\n\t\tself.aflag = 10;\r\n\t}\r\n\telse\r\n\t{\r\n\t\tprecache_model (\"maps/b_rock0.bsp\");\r\n\t\tsetmodel (self, \"maps/b_rock0.bsp\");\r\n\t\tself.aflag = 5;\r\n\t}\r\n\tself.weapon = 3;\r\n\tself.netname = \"rockets\";\r\n\tsetsize (self, '0 0 0', '32 32 56');\r\n\tStartItem ();\r\n};\r\n\r\n\r\n/*QUAKED item_cells (0 .5 .8) (0 0 0) (32 32 32) big\r\n*/\r\n\r\nvoid() item_cells =\r\n{\r\n\tself.touch = ammo_touch;\r\n\r\n\tif (self.spawnflags & WEAPON_BIG2)\r\n\t{\r\n\t\tprecache_model (\"maps/b_batt1.bsp\");\r\n\t\tsetmodel (self, \"maps/b_batt1.bsp\");\r\n\t\tself.aflag = 12;\r\n\t}\r\n\telse\r\n\t{\r\n\t\tprecache_model (\"maps/b_batt0.bsp\");\r\n\t\tsetmodel (self, \"maps/b_batt0.bsp\");\r\n\t\tself.aflag = 6;\r\n\t}\r\n\tself.weapon = 4;\r\n\tself.netname = \"cells\";\r\n\tsetsize (self, '0 0 0', '32 32 56');\r\n\tStartItem ();\r\n};\r\n\r\n\r\n/*QUAKED item_weapon (0 .5 .8) (0 0 0) (32 32 32) shotgun rocket spikes big\r\nDO NOT USE THIS!!!! IT WILL BE REMOVED!\r\n*/\r\n\r\nfloat WEAPON_SHOTGUN = 1;\r\nfloat WEAPON_ROCKET = 2;\r\nfloat WEAPON_SPIKES = 4;\r\nfloat WEAPON_BIG = 8;\r\nvoid() item_weapon =\r\n{\r\n\tself.touch = ammo_touch;\r\n\r\n\tif (self.spawnflags & WEAPON_SHOTGUN)\r\n\t{\r\n\t\tif (self.spawnflags & WEAPON_BIG)\r\n\t\t{\r\n\t\t\tprecache_model (\"maps/b_shell1.bsp\");\r\n\t\t\tsetmodel (self, \"maps/b_shell1.bsp\");\r\n\t\t\tself.aflag = 40;\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tprecache_model (\"maps/b_shell0.bsp\");\r\n\t\t\tsetmodel (self, \"maps/b_shell0.bsp\");\r\n\t\t\tself.aflag = 20;\r\n\t\t}\r\n\t\tself.weapon = 1;\r\n\t\tself.netname = \"shells\";\r\n\t}\r\n\r\n\tif (self.spawnflags & WEAPON_SPIKES)\r\n\t{\r\n\t\tif (self.spawnflags & WEAPON_BIG)\r\n\t\t{\r\n\t\t\tprecache_model (\"maps/b_nail1.bsp\");\r\n\t\t\tsetmodel (self, \"maps/b_nail1.bsp\");\r\n\t\t\tself.aflag = 40;\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tprecache_model (\"maps/b_nail0.bsp\");\r\n\t\t\tsetmodel (self, \"maps/b_nail0.bsp\");\r\n\t\t\tself.aflag = 20;\r\n\t\t}\r\n\t\tself.weapon = 2;\r\n\t\tself.netname = \"spikes\";\r\n\t}\r\n\r\n\tif (self.spawnflags & WEAPON_ROCKET)\r\n\t{\r\n\t\tif (self.spawnflags & WEAPON_BIG)\r\n\t\t{\r\n\t\t\tprecache_model (\"maps/b_rock1.bsp\");\r\n\t\t\tsetmodel (self, \"maps/b_rock1.bsp\");\r\n\t\t\tself.aflag = 10;\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tprecache_model (\"maps/b_rock0.bsp\");\r\n\t\t\tsetmodel (self, \"maps/b_rock0.bsp\");\r\n\t\t\tself.aflag = 5;\r\n\t\t}\r\n\t\tself.weapon = 3;\r\n\t\tself.netname = \"rockets\";\r\n\t}\r\n\t\r\n\tsetsize (self, '0 0 0', '32 32 56');\r\n\tStartItem ();\r\n};\r\n\r\n\r\n/*\r\n===============================================================================\r\n\r\nKEYS\r\n\r\n===============================================================================\r\n*/\r\n\r\nvoid() key_touch =\r\n{\r\n\tif (other.classname != \"player\")\r\n\t\treturn;\r\n\tif (other.health <= 0)\r\n\t\treturn;\r\n\tif (other.items & self.items)\r\n\t\treturn;\r\n\r\n\tsprint (other, \"You got the \");\r\n\tsprint (other, self.netname);\r\n\tsprint (other,\"\\n\");\r\n\r\n\tsound (other, CHAN_ITEM, self.noise, 1, ATTN_NORM);\r\n\tstuffcmd (other, \"bf\\n\");\r\n\tother.items = other.items | self.items;\r\n\r\n\tif (!coop)\r\n\t{\t\r\n\t\tself.solid = SOLID_NOT;\r\n\t\tself.model = string_null;\r\n\t}\r\n\r\n\tactivator = other;\r\n\tSUB_UseTargets();\t\t\t\t// fire all targets / killtargets\r\n};\r\n\r\n\r\nvoid() key_setsounds =\r\n{\r\n\tif (world.worldtype == 0)\r\n\t{\r\n\t\tprecache_sound (\"misc/medkey.wav\");\r\n\t\tself.noise = \"misc/medkey.wav\";\r\n\t}\r\n\tif (world.worldtype == 1)\r\n\t{\r\n\t\tprecache_sound (\"misc/runekey.wav\");\r\n\t\tself.noise = \"misc/runekey.wav\";\r\n\t}\r\n\tif (world.worldtype == 2)\r\n\t{\r\n\t\tprecache_sound2 (\"misc/basekey.wav\");\r\n\t\tself.noise = \"misc/basekey.wav\";\r\n\t}\r\n};\r\n\r\n/*QUAKED item_key1 (0 .5 .8) (-16 -16 -24) (16 16 32)\r\nSILVER key\r\nIn order for keys to work\r\nyou MUST set your maps\r\nworldtype to one of the\r\nfollowing:\r\n0: medieval\r\n1: metal\r\n2: base\r\n*/\r\n\r\nvoid() item_key1 =\r\n{\r\n\tif (world.worldtype == 0)\r\n\t{\r\n\t\tprecache_model (\"progs/w_s_key.mdl\");\r\n\t\tsetmodel (self, \"progs/w_s_key.mdl\");\r\n\t\tself.netname = \"silver key\";\r\n\t}\r\n\telse if (world.worldtype == 1)\r\n\t{\r\n\t\tprecache_model (\"progs/m_s_key.mdl\");\r\n\t\tsetmodel (self, \"progs/m_s_key.mdl\");\r\n\t\tself.netname = \"silver runekey\";\r\n\t}\r\n\telse if (world.worldtype == 2)\r\n\t{\r\n\t\tprecache_model2 (\"progs/b_s_key.mdl\");\r\n\t\tsetmodel (self, \"progs/b_s_key.mdl\");\r\n\t\tself.netname = \"silver keycard\";\r\n\t}\r\n\tkey_setsounds();\r\n\tself.touch = key_touch;\r\n\tself.items = IT_KEY1;\r\n\tsetsize (self, '-16 -16 -24', '16 16 32');\r\n\tStartItem ();\r\n};\r\n\r\n/*QUAKED item_key2 (0 .5 .8) (-16 -16 -24) (16 16 32)\r\nGOLD key\r\nIn order for keys to work\r\nyou MUST set your maps\r\nworldtype to one of the\r\nfollowing:\r\n0: medieval\r\n1: metal\r\n2: base\r\n*/\r\n\r\nvoid() item_key2 =\r\n{\r\n\tif (world.worldtype == 0)\r\n\t{\r\n\t\tprecache_model (\"progs/w_g_key.mdl\");\r\n\t\tsetmodel (self, \"progs/w_g_key.mdl\");\r\n\t\tself.netname = \"gold key\";\r\n\t}\r\n\tif (world.worldtype == 1)\r\n\t{\r\n\t\tprecache_model (\"progs/m_g_key.mdl\");\r\n\t\tsetmodel (self, \"progs/m_g_key.mdl\");\r\n\t\tself.netname = \"gold runekey\";\r\n\t}\r\n\tif (world.worldtype == 2)\r\n\t{\r\n\t\tprecache_model2 (\"progs/b_g_key.mdl\");\r\n\t\tsetmodel (self, \"progs/b_g_key.mdl\");\r\n\t\tself.netname = \"gold keycard\";\r\n\t}\r\n\tkey_setsounds();\r\n\tself.touch = key_touch;\r\n\tself.items = IT_KEY2;\r\n\tsetsize (self, '-16 -16 -24', '16 16 32');\r\n\tStartItem ();\r\n};\r\n\r\n\r\n\r\n/*\r\n===============================================================================\r\n\r\nEND OF LEVEL RUNES\r\n\r\n===============================================================================\r\n*/\r\n\r\nvoid() sigil_touch =\r\n{\r\n\tif (other.classname != \"player\")\r\n\t\treturn;\r\n\tif (other.health <= 0)\r\n\t\treturn;\r\n\r\n\tcenterprint (other, \"You got the rune!\");\r\n\r\n\tsound (other, CHAN_ITEM, self.noise, 1, ATTN_NORM);\r\n\tstuffcmd (other, \"bf\\n\");\r\n\tself.solid = SOLID_NOT;\r\n\tself.model = string_null;\r\n\tserverflags = serverflags | (self.spawnflags & 15);\r\n\tself.classname = \"\";\t\t// so rune doors won't find it\r\n\t\r\n\tactivator = other;\r\n\tSUB_UseTargets();\t\t\t\t// fire all targets / killtargets\r\n};\r\n\r\n\r\n/*QUAKED item_sigil (0 .5 .8) (-16 -16 -24) (16 16 32) E1 E2 E3 E4\r\nEnd of level sigil, pick up to end episode and return to jrstart.\r\n*/\r\n\r\nvoid() item_sigil =\r\n{\r\n\tif (!self.spawnflags)\r\n\t\tobjerror (\"no spawnflags\");\r\n\r\n\tprecache_sound (\"misc/runekey.wav\");\r\n\tself.noise = \"misc/runekey.wav\";\r\n\r\n\tif (self.spawnflags & 1)\r\n\t{\r\n\t\tprecache_model (\"progs/end1.mdl\");\r\n\t\tsetmodel (self, \"progs/end1.mdl\");\r\n\t}\r\n\tif (self.spawnflags & 2)\r\n\t{\r\n\t\tprecache_model2 (\"progs/end2.mdl\");\r\n\t\tsetmodel (self, \"progs/end2.mdl\");\r\n\t}\r\n\tif (self.spawnflags & 4)\r\n\t{\r\n\t\tprecache_model2 (\"progs/end3.mdl\");\r\n\t\tsetmodel (self, \"progs/end3.mdl\");\r\n\t}\r\n\tif (self.spawnflags & 8)\r\n\t{\r\n\t\tprecache_model2 (\"progs/end4.mdl\");\r\n\t\tsetmodel (self, \"progs/end4.mdl\");\r\n\t}\r\n\t\r\n\tself.touch = sigil_touch;\r\n\tsetsize (self, '-16 -16 -24', '16 16 32');\r\n\tStartItem ();\r\n};\r\n\r\n/*\r\n===============================================================================\r\n\r\nPOWERUPS\r\n\r\n===============================================================================\r\n*/\r\n\r\nvoid() powerup_touch;\r\n\r\n\r\nvoid() powerup_touch =\r\n{\r\n\tif (other.classname != \"player\")\r\n\t\treturn;\r\n\tif (other.health <= 0)\r\n\t\treturn;\r\n\r\n\tsprint (other, \"You got the \");\r\n\tsprint (other, self.netname);\r\n\tsprint (other,\"\\n\");\r\n\r\n\tif (deathmatch)\r\n\t{\r\n\t\tself.mdl = self.model;\r\n\t\t\r\n\t\tif ((self.classname == \"item_artifact_invulnerability\") ||\r\n\t\t\t(self.classname == \"item_artifact_invisibility\"))\r\n\t\t\tself.nextthink = time + 60*5;\r\n\t\telse\r\n\t\t\tself.nextthink = time + 60;\r\n\t\t\r\n\t\tself.think = SUB_regen;\r\n\t}\t\r\n\r\n\tsound (other, CHAN_VOICE, self.noise, 1, ATTN_NORM);\r\n\tstuffcmd (other, \"bf\\n\");\r\n\tself.solid = SOLID_NOT;\r\n\tother.items = other.items | self.items;\r\n\tself.model = string_null;\r\n\r\n// do the apropriate action\r\n\tif (self.classname == \"item_artifact_envirosuit\")\r\n\t{\r\n\t\tother.rad_time = 1;\r\n\t\tother.radsuit_finished = time + 30;\r\n\t}\r\n\t\r\n\tif (self.classname == \"item_artifact_invulnerability\")\r\n\t{\r\n\t\tother.invincible_time = 1;\r\n\t\tother.invincible_finished = time + 30;\r\n\t}\r\n\t\r\n\tif (self.classname == \"item_artifact_invisibility\")\r\n\t{\r\n\t\tother.invisible_time = 1;\r\n\t\tother.invisible_finished = time + 30;\r\n\t}\r\n\r\n\tif (self.classname == \"item_artifact_super_damage\")\r\n\t{\r\n\t\tother.super_time = 1;\r\n\t\tother.super_damage_finished = time + 30;\r\n\t}\t\r\n\r\n\tactivator = other;\r\n\tSUB_UseTargets();\t\t\t\t// fire all targets / killtargets\r\n};\r\n\r\n\r\n\r\n/*QUAKED item_artifact_invulnerability (0 .5 .8) (-16 -16 -24) (16 16 32)\r\nPlayer is invulnerable for 30 seconds\r\n*/\r\nvoid() item_artifact_invulnerability =\r\n{\r\n\tself.touch = powerup_touch;\r\n\r\n\tprecache_model (\"progs/invulner.mdl\");\r\n\tprecache_sound (\"items/protect.wav\");\r\n\tprecache_sound (\"items/protect2.wav\");\r\n\tprecache_sound (\"items/protect3.wav\");\r\n\tself.noise = \"items/protect.wav\";\r\n\tsetmodel (self, \"progs/invulner.mdl\");\r\n\tself.netname = \"Pentagram of Protection\";\r\n\tself.items = IT_INVULNERABILITY;\r\n\tself.effects = EF_RED;\r\n\tsetsize (self, '-16 -16 -24', '16 16 32');\r\n\tStartItem ();\r\n};\r\n\r\n/*QUAKED item_artifact_envirosuit (0 .5 .8) (-16 -16 -24) (16 16 32)\r\nPlayer takes no damage from water or slime for 30 seconds\r\n*/\r\nvoid() item_artifact_envirosuit =\r\n{\r\n\tself.touch = powerup_touch;\r\n\r\n\tprecache_model (\"progs/suit.mdl\");\r\n\tprecache_sound (\"items/suit.wav\");\r\n\tprecache_sound (\"items/suit2.wav\");\r\n\tself.noise = \"items/suit.wav\";\r\n\tsetmodel (self, \"progs/suit.mdl\");\r\n\tself.netname = \"Biosuit\";\r\n\tself.items = IT_SUIT;\r\n\tsetsize (self, '-16 -16 -24', '16 16 32');\r\n\tStartItem ();\r\n};\r\n\r\n\r\n/*QUAKED item_artifact_invisibility (0 .5 .8) (-16 -16 -24) (16 16 32)\r\nPlayer is invisible for 30 seconds\r\n*/\r\nvoid() item_artifact_invisibility =\r\n{\r\n\tself.touch = powerup_touch;\r\n\r\n\tprecache_model (\"progs/invisibl.mdl\");\r\n\tprecache_sound (\"items/inv1.wav\");\r\n\tprecache_sound (\"items/inv2.wav\");\r\n\tprecache_sound (\"items/inv3.wav\");\r\n\tself.noise = \"items/inv1.wav\";\r\n\tsetmodel (self, \"progs/invisibl.mdl\");\r\n\tself.netname = \"Ring of Shadows\";\r\n\tself.items = IT_INVISIBILITY;\r\n\tsetsize (self, '-16 -16 -24', '16 16 32');\r\n\tStartItem ();\r\n};\r\n\r\n\r\n/*QUAKED item_artifact_super_damage (0 .5 .8) (-16 -16 -24) (16 16 32)\r\nThe next attack from the player will do 4x damage\r\n*/\r\nvoid() item_artifact_super_damage =\r\n{\r\n\tself.touch = powerup_touch;\r\n\r\n\tprecache_model (\"progs/quaddama.mdl\");\r\n\tprecache_sound (\"items/damage.wav\");\r\n\tprecache_sound (\"items/damage2.wav\");\r\n\tprecache_sound (\"items/damage3.wav\");\r\n\tself.noise = \"items/damage.wav\";\r\n\tsetmodel (self, \"progs/quaddama.mdl\");\r\n\tself.netname = \"Quad Damage\";\r\n\tself.items = IT_QUAD;\r\n\tself.effects = EF_BLUE;\r\n\tsetsize (self, '-16 -16 -24', '16 16 32');\r\n\tStartItem ();\r\n};\r\n\r\n\r\n\r\n/*\r\n===============================================================================\r\n\r\nPLAYER BACKPACKS\r\n\r\n===============================================================================\r\n*/\r\n\r\nvoid() BackpackTouch =\r\n{\r\n\tlocal string\ts;\r\n\tlocal\tfloat\tbest, old, new;\r\n\tlocal\t\tentity\tstemp;\r\n\tlocal\tfloat\tacount;\r\n\t\r\n\tif (other.classname != \"player\")\r\n\t\treturn;\r\n\tif (other.health <= 0)\r\n\t\treturn;\r\n\r\n\tacount = 0;\r\n\tsprint (other, \"You get \");\r\n\r\n\tif (self.items)\r\n\t\tif ((other.items & self.items) == 0)\r\n\t\t{\r\n\t\t\tacount = 1;\r\n\t\t\tsprint (other, \"the \");\r\n\t\t\tsprint (other, self.netname);\r\n\t\t}\r\n\r\n// if the player was using his best weapon, change up to the new one if better\t\t\r\n\tstemp = self;\r\n\tself = other;\r\n\tbest = W_BestWeapon();\r\n\tself = stemp;\r\n\r\n// change weapons\r\n\tother.ammo_shells = other.ammo_shells + self.ammo_shells;\r\n\tother.ammo_nails = other.ammo_nails + self.ammo_nails;\r\n\tother.ammo_rockets = other.ammo_rockets + self.ammo_rockets;\r\n\tother.ammo_cells = other.ammo_cells + self.ammo_cells;\r\n\r\n\tnew = self.items;\r\n\tif (!new)\r\n\t\tnew = other.weapon;\r\n\told = other.items;\r\n\tother.items = other.items | new;\r\n\t\r\n\tbound_other_ammo ();\r\n\r\n\tif (self.ammo_shells)\r\n\t{\r\n\t\tif (acount)\r\n\t\t\tsprint(other, \", \");\r\n\t\tacount = 1;\r\n\t\ts = ftos(self.ammo_shells);\r\n\t\tsprint (other, s);\r\n\t\tsprint (other, \" shells\");\r\n\t}\r\n\tif (self.ammo_nails)\r\n\t{\r\n\t\tif (acount)\r\n\t\t\tsprint(other, \", \");\r\n\t\tacount = 1;\r\n\t\ts = ftos(self.ammo_nails);\r\n\t\tsprint (other, s);\r\n\t\tsprint (other, \" nails\");\r\n\t}\r\n\tif (self.ammo_rockets)\r\n\t{\r\n\t\tif (acount)\r\n\t\t\tsprint(other, \", \");\r\n\t\tacount = 1;\r\n\t\ts = ftos(self.ammo_rockets);\r\n\t\tsprint (other, s);\r\n\t\tsprint (other, \" rockets\");\r\n\t}\r\n\tif (self.ammo_cells)\r\n\t{\r\n\t\tif (acount)\r\n\t\t\tsprint(other, \", \");\r\n\t\tacount = 1;\r\n\t\ts = ftos(self.ammo_cells);\r\n\t\tsprint (other, s);\r\n\t\tsprint (other, \" cells\");\r\n\t}\r\n\t\r\n\tsprint (other, \"\\n\");\r\n// backpack touch sound\r\n\tsound (other, CHAN_ITEM, \"weapons/lock4.wav\", 1, ATTN_NORM);\r\n\tstuffcmd (other, \"bf\\n\");\r\n\r\n// remove the backpack, change self to the player\r\n\tremove(self);\r\n\tself = other;\r\n\r\n// change to the weapon\r\n\tif (!deathmatch)\r\n\t\tself.weapon = new;\r\n\telse\r\n\t\tDeathmatch_Weapon (old, new);\r\n\r\n\tW_SetCurrentAmmo ();\r\n};\r\n\r\n/*\r\n===============\r\nDropBackpack\r\n===============\r\n*/\r\nvoid() DropBackpack =\r\n{\r\n\tlocal entity\titem;\r\n\r\n\tif (!(self.ammo_shells + self.ammo_nails + self.ammo_rockets + self.ammo_cells))\r\n\t\treturn;\t// nothing in it\r\n\r\n\titem = spawn();\r\n\titem.origin = self.origin - '0 0 24';\r\n\t\r\n\titem.items = self.weapon;\r\n\tif (item.items == IT_AXE)\r\n\t\titem.netname = \"Axe\";\r\n\telse if (item.items == IT_SHOTGUN)\r\n\t\titem.netname = \"Shotgun\";\r\n\telse if (item.items == IT_SUPER_SHOTGUN)\r\n\t\titem.netname = \"Double-barrelled Shotgun\";\r\n\telse if (item.items == IT_NAILGUN)\r\n\t\titem.netname = \"Nailgun\";\r\n\telse if (item.items == IT_SUPER_NAILGUN)\r\n\t\titem.netname = \"Super Nailgun\";\r\n\telse if (item.items == IT_GRENADE_LAUNCHER)\r\n\t\titem.netname = \"Grenade Launcher\";\r\n\telse if (item.items == IT_ROCKET_LAUNCHER)\r\n\t\titem.netname = \"Rocket Launcher\";\r\n\telse if (item.items == IT_LIGHTNING)\r\n\t\titem.netname = \"Thunderbolt\";\r\n\telse\r\n\t\titem.netname = \"\";\r\n\r\n\titem.ammo_shells = self.ammo_shells;\r\n\titem.ammo_nails = self.ammo_nails;\r\n\titem.ammo_rockets = self.ammo_rockets;\r\n\titem.ammo_cells = self.ammo_cells;\r\n\r\n\titem.velocity_z = 300;\r\n\titem.velocity_x = -100 + (random() * 200);\r\n\titem.velocity_y = -100 + (random() * 200);\r\n\t\r\n\titem.flags = FL_ITEM;\r\n\titem.solid = SOLID_TRIGGER;\r\n\titem.movetype = MOVETYPE_TOSS;\r\n\tsetmodel (item, \"progs/backpack.mdl\");\r\n\tsetsize (item, '-16 -16 0', '16 16 56');\r\n\titem.touch = BackpackTouch;\r\n\t\r\n\titem.nextthink = time + 120;\t// remove after 2 minutes\r\n\titem.think = SUB_Remove;\r\n};\r\n"
  },
  {
    "path": "quakec/csqctest/src/ss/knight.qc",
    "content": "/*\r\n==============================================================================\r\n\r\nKNIGHT\r\n\r\n==============================================================================\r\n*/\r\n\r\n$cd id1/models/knight\r\n$origin 0 0 24\r\n$base base\r\n$skin badass3\r\n\r\n$frame stand1 stand2 stand3 stand4 stand5 stand6 stand7 stand8 stand9\r\n\r\n$frame runb1 runb2 runb3 runb4 runb5 runb6 runb7 runb8\r\n\r\n//frame runc1 runc2 runc3 runc4 runc5 runc6\r\n\r\n$frame runattack1 runattack2 runattack3 runattack4 runattack5\r\n$frame runattack6 runattack7 runattack8 runattack9 runattack10\r\n$frame runattack11\r\n\r\n$frame pain1 pain2 pain3\r\n\r\n$frame painb1 painb2 painb3 painb4 painb5 painb6 painb7 painb8 painb9\r\n$frame painb10 painb11\r\n\r\n//frame attack1 attack2 attack3 attack4 attack5 attack6 attack7\r\n//frame attack8 attack9 attack10 attack11\r\n\r\n$frame attackb1 attackb1_dupe attackb2 attackb3 attackb4 attackb5\r\n$frame attackb6 attackb7 attackb8 attackb9 attackb10\r\n\r\n$frame walk1 walk2 walk3 walk4 walk5 walk6 walk7 walk8 walk9\r\n$frame walk10 walk11 walk12 walk13 walk14\r\n\r\n$frame kneel1 kneel2 kneel3 kneel4 kneel5\r\n\r\n$frame standing2 standing3 standing4 standing5\r\n\r\n$frame death1 death2 death3 death4 death5 death6 death7 death8\r\n$frame death9 death10\r\n\r\n$frame deathb1 deathb2 deathb3 deathb4 deathb5 deathb6 deathb7 deathb8\r\n$frame deathb9 deathb10 deathb11\r\n\r\nvoid()\tknight_stand1\t=[\t$stand1,\tknight_stand2\t] {ai_stand();};\r\nvoid()\tknight_stand2\t=[\t$stand2,\tknight_stand3\t] {ai_stand();};\r\nvoid()\tknight_stand3\t=[\t$stand3,\tknight_stand4\t] {ai_stand();};\r\nvoid()\tknight_stand4\t=[\t$stand4,\tknight_stand5\t] {ai_stand();};\r\nvoid()\tknight_stand5\t=[\t$stand5,\tknight_stand6\t] {ai_stand();};\r\nvoid()\tknight_stand6\t=[\t$stand6,\tknight_stand7\t] {ai_stand();};\r\nvoid()\tknight_stand7\t=[\t$stand7,\tknight_stand8\t] {ai_stand();};\r\nvoid()\tknight_stand8\t=[\t$stand8,\tknight_stand9\t] {ai_stand();};\r\nvoid()\tknight_stand9\t=[\t$stand9,\tknight_stand1\t] {ai_stand();};\r\n\r\nvoid()\tknight_walk1\t=[\t$walk1,\t\tknight_walk2\t] {\r\nif (random() < 0.2)\r\n\tsound (self, CHAN_VOICE, \"knight/idle.wav\", 1,  ATTN_IDLE);\r\nai_walk(3);};\r\nvoid()\tknight_walk2\t=[\t$walk2,\t\tknight_walk3\t] {ai_walk(2);};\r\nvoid()\tknight_walk3\t=[\t$walk3,\t\tknight_walk4\t] {ai_walk(3);};\r\nvoid()\tknight_walk4\t=[\t$walk4,\t\tknight_walk5\t] {ai_walk(4);};\r\nvoid()\tknight_walk5\t=[\t$walk5,\t\tknight_walk6\t] {ai_walk(3);};\r\nvoid()\tknight_walk6\t=[\t$walk6,\t\tknight_walk7\t] {ai_walk(3);};\r\nvoid()\tknight_walk7\t=[\t$walk7,\t\tknight_walk8\t] {ai_walk(3);};\r\nvoid()\tknight_walk8\t=[\t$walk8,\t\tknight_walk9\t] {ai_walk(4);};\r\nvoid()\tknight_walk9\t=[\t$walk9,\t\tknight_walk10\t] {ai_walk(3);};\r\nvoid()\tknight_walk10\t=[\t$walk10,\tknight_walk11\t] {ai_walk(3);};\r\nvoid()\tknight_walk11\t=[\t$walk11,\tknight_walk12\t] {ai_walk(2);};\r\nvoid()\tknight_walk12\t=[\t$walk12,\tknight_walk13\t] {ai_walk(3);};\r\nvoid()\tknight_walk13\t=[\t$walk13,\tknight_walk14\t] {ai_walk(4);};\r\nvoid()\tknight_walk14\t=[\t$walk14,\tknight_walk1\t] {ai_walk(3);};\r\n\r\n\r\nvoid()\tknight_run1\t=[\t$runb1,\t\tknight_run2\t] {\r\nif (random() < 0.2)\r\n\tsound (self, CHAN_VOICE, \"knight/idle.wav\", 1,  ATTN_IDLE);\r\nai_run(16);};\r\nvoid()\tknight_run2\t=[\t$runb2,\t\tknight_run3\t] {ai_run(20);};\r\nvoid()\tknight_run3\t=[\t$runb3,\t\tknight_run4\t] {ai_run(13);};\r\nvoid()\tknight_run4\t=[\t$runb4,\t\tknight_run5\t] {ai_run(7);};\r\nvoid()\tknight_run5\t=[\t$runb5,\t\tknight_run6\t] {ai_run(16);};\r\nvoid()\tknight_run6\t=[\t$runb6,\t\tknight_run7\t] {ai_run(20);};\r\nvoid()\tknight_run7\t=[\t$runb7,\t\tknight_run8\t] {ai_run(14);};\r\nvoid()\tknight_run8\t=[\t$runb8,\t\tknight_run1\t] {ai_run(6);};\r\n\r\n\r\nvoid()\tknight_runatk1\t=[\t$runattack1,\t\tknight_runatk2\t]\r\n{\r\nif (random() > 0.5)\r\n\tsound (self, CHAN_WEAPON, \"knight/sword2.wav\", 1, ATTN_NORM);\r\nelse\r\n\tsound (self, CHAN_WEAPON, \"knight/sword1.wav\", 1, ATTN_NORM);\r\nai_charge(20);\r\n};\r\nvoid()\tknight_runatk2\t=[\t$runattack2,\tknight_runatk3\t] {ai_charge_side();};\r\nvoid()\tknight_runatk3\t=[\t$runattack3,\tknight_runatk4\t] {ai_charge_side();};\r\nvoid()\tknight_runatk4\t=[\t$runattack4,\tknight_runatk5\t] {ai_charge_side();};\r\nvoid()\tknight_runatk5\t=[\t$runattack5,\tknight_runatk6\t] {ai_melee_side();};\r\nvoid()\tknight_runatk6\t=[\t$runattack6,\tknight_runatk7\t] {ai_melee_side();};\r\nvoid()\tknight_runatk7\t=[\t$runattack7,\tknight_runatk8\t] {ai_melee_side();};\r\nvoid()\tknight_runatk8\t=[\t$runattack8,\tknight_runatk9\t] {ai_melee_side();};\r\nvoid()\tknight_runatk9\t=[\t$runattack9,\tknight_runatk10\t] {ai_melee_side();};\r\nvoid()\tknight_runatk10\t=[\t$runattack10,\tknight_runatk11\t] {ai_charge_side();};\r\nvoid()\tknight_runatk11\t=[\t$runattack11,\tknight_run1\t] {ai_charge(10);};\r\n\r\nvoid()\tknight_atk1\t=[\t$attackb1,\t\tknight_atk2\t]\r\n{\r\nsound (self, CHAN_WEAPON, \"knight/sword1.wav\", 1, ATTN_NORM);\r\nai_charge(0);};\r\nvoid()\tknight_atk2\t=[\t$attackb2,\t\tknight_atk3\t] {ai_charge(7);};\r\nvoid()\tknight_atk3\t=[\t$attackb3,\t\tknight_atk4\t] {ai_charge(4);};\r\nvoid()\tknight_atk4\t=[\t$attackb4,\t\tknight_atk5\t] {ai_charge(0);};\r\nvoid()\tknight_atk5\t=[\t$attackb5,\t\tknight_atk6\t] {ai_charge(3);};\r\nvoid()\tknight_atk6\t=[\t$attackb6,\t\tknight_atk7\t] {ai_charge(4); ai_melee();};\r\nvoid()\tknight_atk7\t=[\t$attackb7,\t\tknight_atk8\t] {ai_charge(1); ai_melee();};\r\nvoid()\tknight_atk8\t=[\t$attackb8,\t\tknight_atk9\t] {ai_charge(3);\r\nai_melee();};\r\nvoid()\tknight_atk9\t=[\t$attackb9,\t\tknight_atk10] {ai_charge(1);};\r\nvoid()\tknight_atk10=[\t$attackb10,\t\tknight_run1\t] {ai_charge(5);};\r\n\r\n//void()\tknight_atk9\t=[\t$attack9,\t\tknight_atk10\t] {};\r\n//void()\tknight_atk10\t=[\t$attack10,\t\tknight_atk11\t] {};\r\n//void()\tknight_atk11\t=[\t$attack11,\t\tknight_run1\t] {};\r\n\r\n//===========================================================================\r\n\r\nvoid()\tknight_pain1\t=[\t$pain1,\t\tknight_pain2\t] {};\r\nvoid()\tknight_pain2\t=[\t$pain2,\t\tknight_pain3\t] {};\r\nvoid()\tknight_pain3\t=[\t$pain3,\t\tknight_run1\t] {};\r\n\r\nvoid()\tknight_painb1\t=[\t$painb1,\tknight_painb2\t] {ai_painforward(0);};\r\nvoid()\tknight_painb2\t=[\t$painb2,\tknight_painb3\t] {ai_painforward(3);};\r\nvoid()\tknight_painb3\t=[\t$painb3,\tknight_painb4\t] {};\r\nvoid()\tknight_painb4\t=[\t$painb4,\tknight_painb5\t] {};\r\nvoid()\tknight_painb5\t=[\t$painb5,\tknight_painb6\t] {ai_painforward(2);};\r\nvoid()\tknight_painb6\t=[\t$painb6,\tknight_painb7\t] {ai_painforward(4);};\r\nvoid()\tknight_painb7\t=[\t$painb7,\tknight_painb8\t] {ai_painforward(2);};\r\nvoid()\tknight_painb8\t=[\t$painb8,\tknight_painb9\t] {ai_painforward(5);};\r\nvoid()\tknight_painb9\t=[\t$painb9,\tknight_painb10\t] {ai_painforward(5);};\r\nvoid()\tknight_painb10\t=[\t$painb10,\tknight_painb11\t] {ai_painforward(0);};\r\nvoid()\tknight_painb11\t=[\t$painb11,\tknight_run1\t] {};\r\n\r\nvoid(entity attacker, float damage)\tknight_pain =\r\n{\r\n\tlocal float r;\r\n\r\n\tif (self.pain_finished > time)\r\n\t\treturn;\r\n\r\n\tr = random();\r\n\t\r\n\tsound (self, CHAN_VOICE, \"knight/khurt.wav\", 1, ATTN_NORM);\r\n\tif (r < 0.85)\r\n\t{\r\n\t\tknight_pain1 ();\r\n\t\tself.pain_finished = time + 1;\r\n\t}\r\n\telse\r\n\t{\r\n\t\tknight_painb1 ();\r\n\t\tself.pain_finished = time + 1;\r\n\t}\r\n\t\r\n};\r\n\r\n//===========================================================================\r\n\r\nvoid()\tknight_bow1\t=[\t$kneel1,\t\tknight_bow2\t] {ai_turn();};\r\nvoid()\tknight_bow2\t=[\t$kneel2,\t\tknight_bow3\t] {ai_turn();};\r\nvoid()\tknight_bow3\t=[\t$kneel3,\t\tknight_bow4\t] {ai_turn();};\r\nvoid()\tknight_bow4\t=[\t$kneel4,\t\tknight_bow5\t] {ai_turn();};\r\n\r\nvoid()\tknight_bow5\t=[\t$kneel5,\t\tknight_bow5\t] {ai_turn();};\r\n\r\nvoid()\tknight_bow6\t=[\t$kneel4,\t\tknight_bow7\t] {ai_turn();};\r\nvoid()\tknight_bow7\t=[\t$kneel3,\t\tknight_bow8\t] {ai_turn();};\r\nvoid()\tknight_bow8\t=[\t$kneel2,\t\tknight_bow9\t] {ai_turn();};\r\nvoid()\tknight_bow9\t=[\t$kneel1,\t\tknight_bow10\t] {ai_turn();};\r\nvoid()\tknight_bow10\t=[\t$walk1,\t\tknight_walk1\t] {ai_turn();};\r\n\r\n\r\n\r\nvoid()\tknight_die1\t=[\t$death1,\tknight_die2\t] {};\r\nvoid()\tknight_die2\t=[\t$death2,\tknight_die3\t] {};\r\nvoid()\tknight_die3\t=[\t$death3,\tknight_die4\t] \r\n{self.solid = SOLID_NOT;};\r\nvoid()\tknight_die4\t=[\t$death4,\tknight_die5\t] {};\r\nvoid()\tknight_die5\t=[\t$death5,\tknight_die6\t] {};\r\nvoid()\tknight_die6\t=[\t$death6,\tknight_die7\t] {};\r\nvoid()\tknight_die7\t=[\t$death7,\tknight_die8\t] {};\r\nvoid()\tknight_die8\t=[\t$death8,\tknight_die9\t] {};\r\nvoid()\tknight_die9\t=[\t$death9,\tknight_die10] {};\r\nvoid()\tknight_die10=[\t$death10,\tknight_die10] {};\r\n\r\n\r\nvoid()\tknight_dieb1\t=[\t$deathb1,\tknight_dieb2\t] {};\r\nvoid()\tknight_dieb2\t=[\t$deathb2,\tknight_dieb3\t] {};\r\nvoid()\tknight_dieb3\t=[\t$deathb3,\tknight_dieb4\t] \t\r\n{self.solid = SOLID_NOT;};\r\nvoid()\tknight_dieb4\t=[\t$deathb4,\tknight_dieb5\t] {};\r\nvoid()\tknight_dieb5\t=[\t$deathb5,\tknight_dieb6\t] {};\r\nvoid()\tknight_dieb6\t=[\t$deathb6,\tknight_dieb7\t] {};\r\nvoid()\tknight_dieb7\t=[\t$deathb7,\tknight_dieb8\t] {};\r\nvoid()\tknight_dieb8\t=[\t$deathb8,\tknight_dieb9\t] {};\r\nvoid()\tknight_dieb9\t=[\t$deathb9,\tknight_dieb10] {};\r\nvoid()\tknight_dieb10 = [\t$deathb10,\tknight_dieb11] {};\r\nvoid()\tknight_dieb11 = [\t$deathb11,\tknight_dieb11] {};\r\n\r\n\r\nvoid() knight_die =\r\n{\r\n// check for gib\r\n\tif (self.health < -40)\r\n\t{\r\n\t\tThrowCSQCGibs(GIB_KNIGHT);\r\n\t\treturn;\r\n\t}\r\n\r\n// regular death\r\n\tsound (self, CHAN_VOICE, \"knight/kdeath.wav\", 1, ATTN_NORM);\r\n\tif (random() < 0.5)\r\n\t\tknight_die1 ();\r\n\telse\r\n\t\tknight_dieb1 ();\r\n};\r\n\r\n\r\n/*QUAKED monster_knight (1 0 0) (-16 -16 -24) (16 16 40) Ambush\r\n*/\r\nvoid() monster_knight =\r\n{\r\n\tif (deathmatch)\r\n\t{\r\n\t\tremove(self);\r\n\t\treturn;\r\n\t}\r\n\tprecache_model (\"progs/knight.mdl\");\r\n\tprecache_model (\"progs/h_knight.mdl\");\r\n\r\n\tprecache_sound (\"knight/kdeath.wav\");\r\n\tprecache_sound (\"knight/khurt.wav\");\r\n\tprecache_sound (\"knight/ksight.wav\");\r\n\tprecache_sound (\"knight/sword1.wav\");\r\n\tprecache_sound (\"knight/sword2.wav\");\r\n\tprecache_sound (\"knight/idle.wav\");\r\n\r\n\tself.solid = SOLID_SLIDEBOX;\r\n\tself.movetype = MOVETYPE_STEP;\r\n\r\n\tsetmodel (self, \"progs/knight.mdl\");\r\n\r\n\tsetsize (self, '-16 -16 -24', '16 16 40');\r\n\tself.health = 75;\r\n\r\n\tself.th_stand = knight_stand1;\r\n\tself.th_walk = knight_walk1;\r\n\tself.th_run = knight_run1;\r\n\tself.th_melee = knight_atk1;\r\n\tself.th_pain = knight_pain;\r\n\tself.th_die = knight_die;\r\n\t\r\n\twalkmonster_start ();\r\n};\r\n"
  },
  {
    "path": "quakec/csqctest/src/ss/misc.qc",
    "content": "\r\n/*QUAKED info_null (0 0.5 0) (-4 -4 -4) (4 4 4)\r\nUsed as a positional target for spotlights, etc.\r\n*/\r\nvoid() info_null =\r\n{\r\n\tremove(self);\r\n};\r\n\r\n/*QUAKED info_notnull (0 0.5 0) (-4 -4 -4) (4 4 4)\r\nUsed as a positional target for lightning.\r\n*/\r\nvoid() info_notnull =\r\n{\r\n};\r\n\r\n//============================================================================\r\n\r\nfloat START_OFF = 1;\r\n\r\nvoid() light_use =\r\n{\r\n\tif (self.spawnflags & START_OFF)\r\n\t{\r\n\t\tlightstyle(self.style, \"m\");\r\n\t\tself.spawnflags = self.spawnflags - START_OFF;\r\n\t}\r\n\telse\r\n\t{\r\n\t\tlightstyle(self.style, \"a\");\r\n\t\tself.spawnflags = self.spawnflags + START_OFF;\r\n\t}\r\n};\r\n\r\n/*QUAKED light (0 1 0) (-8 -8 -8) (8 8 8) START_OFF\r\nNon-displayed light.\r\nDefault light value is 300\r\nDefault style is 0\r\nIf targeted, it will toggle between on or off.\r\n*/\r\nvoid() light =\r\n{\r\n\tif (!self.targetname)\r\n\t{\t// inert light\r\n\t\tremove(self);\r\n\t\treturn;\r\n\t}\r\n\t\r\n\tif (self.style >= 32)\r\n\t{\r\n\t\tself.use = light_use;\r\n\t\tif (self.spawnflags & START_OFF)\r\n\t\t\tlightstyle(self.style, \"a\");\r\n\t\telse\r\n\t\t\tlightstyle(self.style, \"m\");\r\n\t}\r\n};\r\n\r\n/*QUAKED light_fluoro (0 1 0) (-8 -8 -8) (8 8 8) START_OFF\r\nNon-displayed light.\r\nDefault light value is 300\r\nDefault style is 0\r\nIf targeted, it will toggle between on or off.\r\nMakes steady fluorescent humming sound\r\n*/\r\nvoid() light_fluoro =\r\n{\r\n\tif (self.style >= 32)\r\n\t{\r\n\t\tself.use = light_use;\r\n\t\tif (self.spawnflags & START_OFF)\r\n\t\t\tlightstyle(self.style, \"a\");\r\n\t\telse\r\n\t\t\tlightstyle(self.style, \"m\");\r\n\t}\r\n\t\r\n\tprecache_sound (\"ambience/fl_hum1.wav\");\r\n\tambientsound (self.origin, \"ambience/fl_hum1.wav\", 0.5, ATTN_STATIC);\r\n};\r\n\r\n/*QUAKED light_fluorospark (0 1 0) (-8 -8 -8) (8 8 8)\r\nNon-displayed light.\r\nDefault light value is 300\r\nDefault style is 10\r\nMakes sparking, broken fluorescent sound\r\n*/\r\nvoid() light_fluorospark =\r\n{\r\n\tif (!self.style)\r\n\t\tself.style = 10;\r\n\r\n\tprecache_sound (\"ambience/buzz1.wav\");\r\n\tambientsound (self.origin, \"ambience/buzz1.wav\", 0.5, ATTN_STATIC);\r\n};\r\n\r\n/*QUAKED light_globe (0 1 0) (-8 -8 -8) (8 8 8)\r\nSphere globe light.\r\nDefault light value is 300\r\nDefault style is 0\r\n*/\r\nvoid() light_globe =\r\n{\r\n\tprecache_model (\"progs/s_light.spr\");\r\n\tsetmodel (self, \"progs/s_light.spr\");\r\n\tmakestatic (self);\r\n};\r\n\r\nvoid() FireAmbient =\r\n{\r\n\tprecache_sound (\"ambience/fire1.wav\");\r\n// attenuate fast\r\n\tambientsound (self.origin, \"ambience/fire1.wav\", 0.5, ATTN_STATIC);\r\n};\r\n\r\n/*QUAKED light_torch_small_walltorch (0 .5 0) (-10 -10 -20) (10 10 20)\r\nShort wall torch\r\nDefault light value is 200\r\nDefault style is 0\r\n*/\r\nvoid() light_torch_small_walltorch =\r\n{\r\n\tprecache_model (\"progs/flame.mdl\");\r\n\tsetmodel (self, \"progs/flame.mdl\");\r\n\tFireAmbient ();\r\n\tmakestatic (self);\r\n};\r\n\r\n/*QUAKED light_flame_large_yellow (0 1 0) (-10 -10 -12) (12 12 18)\r\nLarge yellow flame ball\r\n*/\r\nvoid() light_flame_large_yellow =\r\n{\r\n\tprecache_model (\"progs/flame2.mdl\");\r\n\tsetmodel (self, \"progs/flame2.mdl\");\r\n\tself.frame = 1;\r\n\tFireAmbient ();\r\n\tmakestatic (self);\r\n};\r\n\r\n/*QUAKED light_flame_small_yellow (0 1 0) (-8 -8 -8) (8 8 8) START_OFF\r\nSmall yellow flame ball\r\n*/\r\nvoid() light_flame_small_yellow =\r\n{\r\n\tprecache_model (\"progs/flame2.mdl\");\r\n\tsetmodel (self, \"progs/flame2.mdl\");\r\n\tFireAmbient ();\r\n\tmakestatic (self);\r\n};\r\n\r\n/*QUAKED light_flame_small_white (0 1 0) (-10 -10 -40) (10 10 40) START_OFF\r\nSmall white flame ball\r\n*/\r\nvoid() light_flame_small_white =\r\n{\r\n\tprecache_model (\"progs/flame2.mdl\");\r\n\tsetmodel (self, \"progs/flame2.mdl\");\r\n\tFireAmbient ();\r\n\tmakestatic (self);\r\n};\r\n\r\n//============================================================================\r\n\r\n\r\n/*QUAKED misc_fireball (0 .5 .8) (-8 -8 -8) (8 8 8)\r\nLava Balls\r\n*/\r\n\r\nvoid() fire_fly;\r\nvoid() fire_touch;\r\nvoid() misc_fireball =\r\n{\r\n\t\r\n\tprecache_model (\"progs/lavaball.mdl\");\r\n\tself.classname = \"fireball\";\r\n\tself.nextthink = time + (random() * 5);\r\n\tself.think = fire_fly;\r\n\tif (!self.speed)\r\n\t\tself.speed = 1000;\r\n};\r\n\r\nvoid() fire_fly =\r\n{\r\nlocal entity\tfireball;\r\n\r\n\tfireball = spawn();\r\n\tfireball.solid = SOLID_TRIGGER;\r\n\tfireball.movetype = MOVETYPE_TOSS;\r\n\tfireball.velocity = '0 0 1000';\r\n\tfireball.velocity_x = (random() * 100) - 50;\r\n\tfireball.velocity_y = (random() * 100) - 50;\r\n\tfireball.velocity_z = self.speed + (random() * 200);\r\n\tfireball.classname = \"fireball\";\r\n\tsetmodel (fireball, \"progs/lavaball.mdl\");\r\n\tsetsize (fireball, '0 0 0', '0 0 0');\r\n\tsetorigin (fireball, self.origin);\r\n\tfireball.nextthink = time + 5;\r\n\tfireball.think = SUB_Remove;\r\n\tfireball.touch = fire_touch;\r\n\t\r\n\tself.nextthink = time + (random() * 5) + 3;\r\n\tself.think = fire_fly;\r\n};\r\n\r\n\r\nvoid() fire_touch =\r\n{\r\n\tT_Damage (other, self, self, 20);\r\n\tremove(self);\r\n};\r\n\r\n//============================================================================\r\n\r\n\r\nvoid() barrel_explode =\r\n{\r\n\tself.takedamage = DAMAGE_NO;\r\n\tself.classname = \"explo_box\";\r\n\t// did say self.owner\r\n\tT_RadiusDamage (self, self, 160, world);\r\n\tsound (self, CHAN_VOICE, \"weapons/r_exp3.wav\", 1, ATTN_NORM);\r\n\tparticle (self.origin, '0 0 0', 75, 255);\r\n\r\n\tself.origin_z = self.origin_z + 32;\r\n\tBecomeExplosion ();\r\n};\r\n\r\n\r\n\r\n/*QUAKED misc_explobox (0 .5 .8) (0 0 0) (32 32 64)\r\nTESTING THING\r\n*/\r\n\r\nvoid() misc_explobox =\r\n{\r\n\tlocal float\toldz;\r\n\t\r\n\tself.solid = SOLID_BBOX;\r\n\tself.movetype = MOVETYPE_NONE;\r\n\tprecache_model (\"maps/b_explob.bsp\");\r\n\tsetmodel (self, \"maps/b_explob.bsp\");\r\n\tprecache_sound (\"weapons/r_exp3.wav\");\r\n\tself.health = 20;\r\n\tself.th_die = barrel_explode;\r\n\tself.takedamage = DAMAGE_AIM;\r\n\r\n\tself.origin_z = self.origin_z + 2;\r\n\toldz = self.origin_z;\r\n\tdroptofloor();\r\n\tif (oldz - self.origin_z > 250)\r\n\t{\r\n\t\tdprint (\"item fell out of level at \");\r\n\t\tdprint (vtos(self.origin));\r\n\t\tdprint (\"\\n\");\r\n\t\tremove(self);\r\n\t}\r\n};\r\n\r\n\r\n\r\n\r\n/*QUAKED misc_explobox2 (0 .5 .8) (0 0 0) (32 32 64)\r\nSmaller exploding box, REGISTERED ONLY\r\n*/\r\n\r\nvoid() misc_explobox2 =\r\n{\r\n\tlocal float\toldz;\r\n\t\r\n\tself.solid = SOLID_BBOX;\r\n\tself.movetype = MOVETYPE_NONE;\r\n\tprecache_model2 (\"maps/b_exbox2.bsp\");\r\n\tsetmodel (self, \"maps/b_exbox2.bsp\");\r\n\tprecache_sound (\"weapons/r_exp3.wav\");\r\n\tself.health = 20;\r\n\tself.th_die = barrel_explode;\r\n\tself.takedamage = DAMAGE_AIM;\r\n\r\n\tself.origin_z = self.origin_z + 2;\r\n\toldz = self.origin_z;\r\n\tdroptofloor();\r\n\tif (oldz - self.origin_z > 250)\r\n\t{\r\n\t\tdprint (\"item fell out of level at \");\r\n\t\tdprint (vtos(self.origin));\r\n\t\tdprint (\"\\n\");\r\n\t\tremove(self);\r\n\t}\r\n};\r\n\r\n//============================================================================\r\n\r\nfloat SPAWNFLAG_SUPERSPIKE\t= 1;\r\nfloat SPAWNFLAG_LASER = 2;\r\n\r\nvoid(vector org, vector vec) LaunchLaser;\r\n\r\nvoid() spikeshooter_use =\r\n{\r\n\tif (self.spawnflags & SPAWNFLAG_LASER)\r\n\t{\r\n\t\tsound (self, CHAN_VOICE, \"enforcer/enfire.wav\", 1, ATTN_NORM);\r\n\t\tLaunchLaser (self.origin, self.movedir);\r\n\t}\r\n\telse\r\n\t{\r\n\t\tsound (self, CHAN_VOICE, \"weapons/spike2.wav\", 1, ATTN_NORM);\r\n\t\tlaunch_spike (self.origin, self.movedir);\r\n\t\tnewmis.velocity = self.movedir * 500;\r\n\t\tif (self.spawnflags & SPAWNFLAG_SUPERSPIKE)\r\n\t\t\tnewmis.touch = superspike_touch;\r\n\t}\r\n};\r\n\r\nvoid() shooter_think =\r\n{\r\n\tspikeshooter_use ();\r\n\tself.nextthink = time + self.wait;\r\n\tnewmis.velocity = self.movedir * 500;\r\n};\r\n\r\n\r\n/*QUAKED trap_spikeshooter (0 .5 .8) (-8 -8 -8) (8 8 8) superspike laser\r\nWhen triggered, fires a spike in the direction set in QuakeEd.\r\nLaser is only for REGISTERED.\r\n*/\r\n\r\nvoid() trap_spikeshooter =\r\n{\r\n\tSetMovedir ();\r\n\tself.use = spikeshooter_use;\r\n\tif (self.spawnflags & SPAWNFLAG_LASER)\r\n\t{\r\n\t\tprecache_model2 (\"progs/laser.mdl\");\r\n\t\t\r\n\t\tprecache_sound2 (\"enforcer/enfire.wav\");\r\n\t\tprecache_sound2 (\"enforcer/enfstop.wav\");\r\n\t}\r\n\telse\r\n\t\tprecache_sound (\"weapons/spike2.wav\");\r\n};\r\n\r\n\r\n/*QUAKED trap_shooter (0 .5 .8) (-8 -8 -8) (8 8 8) superspike laser\r\nContinuously fires spikes.\r\n\"wait\" time between spike (1.0 default)\r\n\"nextthink\" delay before firing first spike, so multiple shooters can be stagered.\r\n*/\r\nvoid() trap_shooter =\r\n{\r\n\ttrap_spikeshooter ();\r\n\t\r\n\tif (self.wait == 0)\r\n\t\tself.wait = 1;\r\n\tself.nextthink = self.nextthink + self.wait + self.ltime;\r\n\tself.think = shooter_think;\r\n};\r\n\r\n\r\n\r\n/*\r\n===============================================================================\r\n\r\n\r\n===============================================================================\r\n*/\r\n\r\n\r\nvoid() make_bubbles;\r\nvoid() bubble_remove;\r\nvoid() bubble_bob;\r\n\r\n/*QUAKED air_bubbles (0 .5 .8) (-8 -8 -8) (8 8 8)\r\n\r\ntesting air bubbles\r\n*/\r\n\r\nvoid() air_bubbles =\r\n\r\n{\r\n\tif (deathmatch)\r\n\t{\r\n\t\tremove (self);\r\n\t\treturn;\r\n\t}\r\n\tprecache_model (\"progs/s_bubble.spr\");\r\n\tself.nextthink = time + 1;\r\n\tself.think = make_bubbles;\r\n};\r\n\r\nvoid() make_bubbles =\r\n{\r\nlocal entity\tbubble;\r\n\r\n\tbubble = spawn();\r\n\tsetmodel (bubble, \"progs/s_bubble.spr\");\r\n\tsetorigin (bubble, self.origin);\r\n\tbubble.movetype = MOVETYPE_NOCLIP;\r\n\tbubble.solid = SOLID_NOT;\r\n\tbubble.velocity = '0 0 15';\r\n\tbubble.nextthink = time + 0.5;\r\n\tbubble.think = bubble_bob;\r\n\tbubble.touch = bubble_remove;\r\n\tbubble.classname = \"bubble\";\r\n\tbubble.frame = 0;\r\n\tbubble.cnt = 0;\r\n\tsetsize (bubble, '-8 -8 -8', '8 8 8');\r\n\tself.nextthink = time + random() + 0.5;\r\n\tself.think = make_bubbles;\r\n};\r\n\r\nvoid() bubble_split =\r\n{\r\nlocal entity\tbubble;\r\n\tbubble = spawn();\r\n\tsetmodel (bubble, \"progs/s_bubble.spr\");\r\n\tsetorigin (bubble, self.origin);\r\n\tbubble.movetype = MOVETYPE_NOCLIP;\r\n\tbubble.solid = SOLID_NOT;\r\n\tbubble.velocity = self.velocity;\r\n\tbubble.nextthink = time + 0.5;\r\n\tbubble.think = bubble_bob;\r\n\tbubble.touch = bubble_remove;\r\n\tbubble.classname = \"bubble\";\r\n\tbubble.frame = 1;\r\n\tbubble.cnt = 10;\r\n\tsetsize (bubble, '-8 -8 -8', '8 8 8');\r\n\tself.frame = 1;\r\n\tself.cnt = 10;\r\n\tif (self.waterlevel != 3)\r\n\t\tremove (self);\r\n};\r\n\r\nvoid() bubble_remove =\r\n{\r\n\tif (other.classname == self.classname)\r\n\t{\r\n//\t\tdprint (\"bump\");\r\n\t\treturn;\r\n\t}\r\n\tremove(self);\r\n};\r\n\r\nvoid() bubble_bob =\r\n{\r\nlocal float\t\trnd1, rnd2, rnd3;\r\n\r\n\tself.cnt = self.cnt + 1;\r\n\tif (self.cnt == 4)\r\n\t\tbubble_split();\r\n\tif (self.cnt == 20)\r\n\t\tremove(self);\r\n\r\n\trnd1 = self.velocity_x + (-10 + (random() * 20));\r\n\trnd2 = self.velocity_y + (-10 + (random() * 20));\r\n\trnd3 = self.velocity_z + 10 + random() * 10;\r\n\r\n\tif (rnd1 > 10)\r\n\t\trnd1 = 5;\r\n\tif (rnd1 < -10)\r\n\t\trnd1 = -5;\r\n\t\t\r\n\tif (rnd2 > 10)\r\n\t\trnd2 = 5;\r\n\tif (rnd2 < -10)\r\n\t\trnd2 = -5;\r\n\t\t\r\n\tif (rnd3 < 10)\r\n\t\trnd3 = 15;\r\n\tif (rnd3 > 30)\r\n\t\trnd3 = 25;\r\n\t\r\n\tself.velocity_x = rnd1;\r\n\tself.velocity_y = rnd2;\r\n\tself.velocity_z = rnd3;\r\n\t\t\r\n\tself.nextthink = time + 0.5;\r\n\tself.think = bubble_bob;\r\n};\r\n\r\n/*~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>\r\n~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~*/\r\n\r\n/*QUAKED viewthing (0 .5 .8) (-8 -8 -8) (8 8 8)\r\n\r\nJust for the debugging level.  Don't use\r\n*/\r\n\r\nvoid() viewthing =\r\n\r\n{\r\n\tself.movetype = MOVETYPE_NONE;\r\n\tself.solid = SOLID_NOT;\r\n\tprecache_model (\"progs/player.mdl\");\r\n\tsetmodel (self, \"progs/player.mdl\");\r\n};\r\n\r\n\r\n/*\r\n==============================================================================\r\n\r\nSIMPLE BMODELS\r\n\r\n==============================================================================\r\n*/\r\n\r\nvoid() func_wall_use =\r\n{\t// change to alternate textures\r\n\tself.frame = 1 - self.frame;\r\n};\r\n\r\n/*QUAKED func_wall (0 .5 .8) ?\r\nThis is just a solid wall if not inhibitted\r\n*/\r\nvoid() func_wall =\r\n{\r\n\tself.angles = '0 0 0';\r\n\tself.movetype = MOVETYPE_PUSH;\t// so it doesn't get pushed by anything\r\n\tself.solid = SOLID_BSP;\r\n\tself.use = func_wall_use;\r\n\tsetmodel (self, self.model);\r\n};\r\n\r\n\r\n/*QUAKED func_illusionary (0 .5 .8) ?\r\nA simple entity that looks solid but lets you walk through it.\r\n*/\r\nvoid() func_illusionary =\r\n\r\n{\r\n\tself.angles = '0 0 0';\r\n\tself.movetype = MOVETYPE_NONE;\r\n\tself.solid = SOLID_NOT;\r\n\tsetmodel (self, self.model);\r\n\tmakestatic (self);\r\n};\r\n\r\n/*QUAKED func_episodegate (0 .5 .8) ? E1 E2 E3 E4\r\nThis bmodel will appear if the episode has allready been completed, so players can't reenter it.\r\n*/\r\nvoid() func_episodegate =\r\n\r\n{\r\n\tif (!(serverflags & self.spawnflags))\r\n\t\treturn;\t\t\t// can still enter episode\r\n\r\n\tself.angles = '0 0 0';\r\n\tself.movetype = MOVETYPE_PUSH;\t// so it doesn't get pushed by anything\r\n\tself.solid = SOLID_BSP;\r\n\tself.use = func_wall_use;\r\n\tsetmodel (self, self.model);\r\n};\r\n\r\n/*QUAKED func_bossgate (0 .5 .8) ?\r\nThis bmodel appears unless players have all of the episode sigils.\r\n*/\r\nvoid() func_bossgate =\r\n\r\n{\r\n\tif ( (serverflags & 15) == 15)\r\n\t\treturn;\t\t// all episodes completed\r\n\tself.angles = '0 0 0';\r\n\tself.movetype = MOVETYPE_PUSH;\t// so it doesn't get pushed by anything\r\n\tself.solid = SOLID_BSP;\r\n\tself.use = func_wall_use;\r\n\tsetmodel (self, self.model);\r\n};\r\n\r\n//============================================================================\r\n/*QUAKED ambient_suck_wind (0.3 0.1 0.6) (-10 -10 -8) (10 10 8)\r\n*/\r\nvoid() ambient_suck_wind =\r\n{\r\n\tprecache_sound (\"ambience/suck1.wav\");\r\n\tambientsound (self.origin, \"ambience/suck1.wav\", 1, ATTN_STATIC);\r\n};\r\n\r\n/*QUAKED ambient_drone (0.3 0.1 0.6) (-10 -10 -8) (10 10 8)\r\n*/\r\nvoid() ambient_drone =\r\n{\r\n\tprecache_sound (\"ambience/drone6.wav\");\r\n\tambientsound (self.origin, \"ambience/drone6.wav\", 0.5, ATTN_STATIC);\r\n};\r\n\r\n/*QUAKED ambient_flouro_buzz (0.3 0.1 0.6) (-10 -10 -8) (10 10 8)\r\n*/\r\nvoid() ambient_flouro_buzz =\r\n{\r\n\tprecache_sound (\"ambience/buzz1.wav\");\r\n\tambientsound (self.origin, \"ambience/buzz1.wav\", 1, ATTN_STATIC);\r\n};\r\n/*QUAKED ambient_drip (0.3 0.1 0.6) (-10 -10 -8) (10 10 8)\r\n*/\r\nvoid() ambient_drip =\r\n{\r\n\tprecache_sound (\"ambience/drip1.wav\");\r\n\tambientsound (self.origin, \"ambience/drip1.wav\", 0.5, ATTN_STATIC);\r\n};\r\n/*QUAKED ambient_comp_hum (0.3 0.1 0.6) (-10 -10 -8) (10 10 8)\r\n*/\r\nvoid() ambient_comp_hum =\r\n{\r\n\tprecache_sound (\"ambience/comp1.wav\");\r\n\tambientsound (self.origin, \"ambience/comp1.wav\", 1, ATTN_STATIC);\r\n};\r\n/*QUAKED ambient_thunder (0.3 0.1 0.6) (-10 -10 -8) (10 10 8)\r\n*/\r\nvoid() ambient_thunder =\r\n{\r\n\tprecache_sound (\"ambience/thunder1.wav\");\r\n\tambientsound (self.origin, \"ambience/thunder1.wav\", 0.5, ATTN_STATIC);\r\n};\r\n/*QUAKED ambient_light_buzz (0.3 0.1 0.6) (-10 -10 -8) (10 10 8)\r\n*/\r\nvoid() ambient_light_buzz =\r\n{\r\n\tprecache_sound (\"ambience/fl_hum1.wav\");\r\n\tambientsound (self.origin, \"ambience/fl_hum1.wav\", 0.5, ATTN_STATIC);\r\n};\r\n/*QUAKED ambient_swamp1 (0.3 0.1 0.6) (-10 -10 -8) (10 10 8)\r\n*/\r\nvoid() ambient_swamp1 =\r\n{\r\n\tprecache_sound (\"ambience/swamp1.wav\");\r\n\tambientsound (self.origin, \"ambience/swamp1.wav\", 0.5, ATTN_STATIC);\r\n};\r\n/*QUAKED ambient_swamp2 (0.3 0.1 0.6) (-10 -10 -8) (10 10 8)\r\n*/\r\nvoid() ambient_swamp2 =\r\n{\r\n\tprecache_sound (\"ambience/swamp2.wav\");\r\n\tambientsound (self.origin, \"ambience/swamp2.wav\", 0.5, ATTN_STATIC);\r\n};\r\n\r\n//============================================================================\r\n/*\r\nvoid() noise_think =\r\n{\r\n\tself.nextthink = time + 0.5;\r\n\tsound (self, 1, \"enforcer/enfire.wav\", 1, ATTN_NORM);\r\n\tsound (self, 2, \"enforcer/enfstop.wav\", 1, ATTN_NORM);\r\n\tsound (self, 3, \"enforcer/sight1.wav\", 1, ATTN_NORM);\r\n\tsound (self, 4, \"enforcer/sight2.wav\", 1, ATTN_NORM);\r\n\tsound (self, 5, \"enforcer/sight3.wav\", 1, ATTN_NORM);\r\n\tsound (self, 6, \"enforcer/sight4.wav\", 1, ATTN_NORM);\r\n\tsound (self, 7, \"enforcer/pain1.wav\", 1, ATTN_NORM);\r\n};\r\n*/\r\n/*QUAKED misc_noisemaker (1 0.5 0) (-10 -10 -10) (10 10 10)\r\n\r\nFor optimzation testing, starts a lot of sounds.\r\n*/\r\n/*\r\nvoid() misc_noisemaker =\r\n\r\n{\r\n\tprecache_sound2 (\"enforcer/enfire.wav\");\r\n\tprecache_sound2 (\"enforcer/enfstop.wav\");\r\n\tprecache_sound2 (\"enforcer/sight1.wav\");\r\n\tprecache_sound2 (\"enforcer/sight2.wav\");\r\n\tprecache_sound2 (\"enforcer/sight3.wav\");\r\n\tprecache_sound2 (\"enforcer/sight4.wav\");\r\n\tprecache_sound2 (\"enforcer/pain1.wav\");\r\n\tprecache_sound2 (\"enforcer/pain2.wav\");\r\n\tprecache_sound2 (\"enforcer/death1.wav\");\r\n\tprecache_sound2 (\"enforcer/idle1.wav\");\r\n\r\n\tself.nextthink = time + 0.1 + random();\r\n\tself.think = noise_think;\r\n};*/\r\n"
  },
  {
    "path": "quakec/csqctest/src/ss/monsters.qc",
    "content": "/* ALL MONSTERS SHOULD BE 1 0 0 IN COLOR */\r\n\r\n// name =[framenum,\tnexttime, nextthink] {code}\r\n// expands to:\r\n// name ()\r\n// {\r\n//\t\tself.frame=framenum;\r\n//\t\tself.nextthink = time + nexttime;\r\n//\t\tself.think = nextthink\r\n//\t\t<code>\r\n// };\r\n\r\n\r\n/*\r\n================\r\nmonster_use\r\n\r\nUsing a monster makes it angry at the current activator\r\n================\r\n*/\r\nvoid() monster_use =\r\n{\r\n\tif (self.enemy)\r\n\t\treturn;\r\n\tif (self.health <= 0)\r\n\t\treturn;\r\n\tif (activator.items & IT_INVISIBILITY)\r\n\t\treturn;\r\n\tif (activator.flags & FL_NOTARGET)\r\n\t\treturn;\r\n\tif (activator.classname != \"player\")\r\n\t\treturn;\r\n\t\r\n// delay reaction so if the monster is teleported, its sound is still\r\n// heard\r\n\tself.enemy = activator;\r\n\tself.nextthink = time + 0.1;\r\n\tself.think = FoundTarget;\r\n};\r\n\r\n/*\r\n================\r\nmonster_death_use\r\n\r\nWhen a mosnter dies, it fires all of its targets with the current\r\nenemy as activator.\r\n================\r\n*/\r\nvoid() monster_death_use =\r\n{\r\n\r\n// fall to ground\r\n\tif (self.flags & FL_FLY)\r\n\t\tself.flags = self.flags - FL_FLY;\r\n\tif (self.flags & FL_SWIM)\r\n\t\tself.flags = self.flags - FL_SWIM;\r\n\r\n\tif (!self.target)\r\n\t\treturn;\r\n\r\n\tactivator = self.enemy;\r\n\tSUB_UseTargets ();\r\n};\r\n\r\n\r\n//============================================================================\r\n\r\nvoid() walkmonster_start_go =\r\n{\r\n\tself.origin_z = self.origin_z + 1;\t// raise off floor a bit\r\n\tdroptofloor();\r\n\t\r\n\tif (!walkmove(0,0))\r\n\t{\r\n\t\tdprint (\"walkmonster in wall at: \");\r\n\t\tdprint (vtos(self.origin));\r\n\t\tdprint (\"\\n\");\r\n\t}\r\n\t\r\n\tself.takedamage = DAMAGE_AIM;\r\n\r\n\tself.ideal_yaw = self.angles * '0 1 0';\r\n\tif (!self.yaw_speed)\r\n\t\tself.yaw_speed = 20;\r\n\tself.view_ofs = '0 0 25';\r\n\tself.use = monster_use;\r\n\t\r\n\tself.flags = self.flags | FL_MONSTER;\r\n\t\r\n\tif (self.target!=\"\")\r\n\t{\r\n\t\tself.goalentity = self.movetarget = find(world, targetname, self.target);\r\n\t\tself.ideal_yaw = vectoyaw(self.goalentity.origin - self.origin);\r\n\t\tif (!self.movetarget)\r\n\t\t{\r\n\t\t\tdprint (\"Monster can't find target at \");\r\n\t\t\tdprint (vtos(self.origin));\r\n\t\t\tdprint (\"\\n\");\r\n\t\t}\r\n// this used to be an objerror\r\n\t\tif (self.movetarget.classname == \"path_corner\")\r\n\t\t\tself.th_walk ();\r\n\t\telse\r\n\t\t\tself.pausetime = 99999999;\r\n\t\t\tself.th_stand ();\r\n\t}\r\n\telse\r\n\t{\r\n\t\tself.pausetime = 99999999;\r\n\t\tself.th_stand ();\r\n\t}\r\n\r\n// spread think times so they don't all happen at same time\r\n\tself.nextthink = self.nextthink + random()*0.5;\r\n};\r\n\r\n\r\nvoid() walkmonster_start =\r\n{\r\n// delay drop to floor to make sure all doors have been spawned\r\n// spread think times so they don't all happen at same time\r\n\tself.nextthink = self.nextthink + random()*0.5;\r\n\tself.think = walkmonster_start_go;\r\n\ttotal_monsters = total_monsters + 1;\r\n};\r\n\r\n\r\n\r\nvoid() flymonster_start_go =\r\n{\r\n\tself.takedamage = DAMAGE_AIM;\r\n\r\n\tself.ideal_yaw = self.angles * '0 1 0';\r\n\tif (!self.yaw_speed)\r\n\t\tself.yaw_speed = 10;\r\n\tself.view_ofs = '0 0 25';\r\n\tself.use = monster_use;\r\n\r\n\tself.flags = self.flags | FL_FLY;\r\n\tself.flags = self.flags | FL_MONSTER;\r\n\r\n\tif (!walkmove(0,0))\r\n\t{\r\n\t\tdprint (\"flymonster in wall at: \");\r\n\t\tdprint (vtos(self.origin));\r\n\t\tdprint (\"\\n\");\r\n\t}\r\n\r\n\tif (self.target!=\"\")\r\n\t{\r\n\t\tself.goalentity = self.movetarget = find(world, targetname, self.target);\r\n\t\tif (!self.movetarget)\r\n\t\t{\r\n\t\t\tdprint (\"Monster can't find target at \");\r\n\t\t\tdprint (vtos(self.origin));\r\n\t\t\tdprint (\"\\n\");\r\n\t\t}\r\n// this used to be an objerror\r\n\t\tif (self.movetarget.classname == \"path_corner\")\r\n\t\t\tself.th_walk ();\r\n\t\telse\r\n\t\t\tself.pausetime = 99999999;\r\n\t\t\tself.th_stand ();\r\n\t}\r\n\telse\r\n\t{\r\n\t\tself.pausetime = 99999999;\r\n\t\tself.th_stand ();\r\n\t}\r\n};\r\n\r\nvoid() flymonster_start =\r\n{\r\n// spread think times so they don't all happen at same time\r\n\tself.nextthink = self.nextthink + random()*0.5;\r\n\tself.think = flymonster_start_go;\r\n\ttotal_monsters = total_monsters + 1;\r\n};\r\n\r\n\r\nvoid() swimmonster_start_go =\r\n{\r\n\tif (deathmatch)\r\n\t{\r\n\t\tremove(self);\r\n\t\treturn;\r\n\t}\r\n\r\n\tself.takedamage = DAMAGE_AIM;\r\n\ttotal_monsters = total_monsters + 1;\r\n\r\n\tself.ideal_yaw = self.angles * '0 1 0';\r\n\tif (!self.yaw_speed)\r\n\t\tself.yaw_speed = 10;\r\n\tself.view_ofs = '0 0 10';\r\n\tself.use = monster_use;\r\n\t\r\n\tself.flags = self.flags | FL_SWIM;\r\n\tself.flags = self.flags | FL_MONSTER;\r\n\r\n\tif (self.target !=\"\")\r\n\t{\r\n\t\tself.goalentity = self.movetarget = find(world, targetname, self.target);\r\n\t\tif (!self.movetarget)\r\n\t\t{\r\n\t\t\tdprint (\"Monster can't find target at \");\r\n\t\t\tdprint (vtos(self.origin));\r\n\t\t\tdprint (\"\\n\");\r\n\t\t}\r\n// this used to be an objerror\r\n\t\tself.ideal_yaw = vectoyaw(self.goalentity.origin - self.origin);\r\n\t\tself.th_walk ();\r\n\t}\r\n\telse\r\n\t{\r\n\t\tself.pausetime = 99999999;\r\n\t\tself.th_stand ();\r\n\t}\r\n\r\n// spread think times so they don't all happen at same time\r\n\tself.nextthink = self.nextthink + random()*0.5;\r\n};\r\n\r\nvoid() swimmonster_start =\r\n{\r\n// spread think times so they don't all happen at same time\r\n\tself.nextthink = self.nextthink + random()*0.5;\r\n\tself.think = swimmonster_start_go;\r\n\ttotal_monsters = total_monsters + 1;\r\n};\r\n\r\n\r\n"
  },
  {
    "path": "quakec/csqctest/src/ss/ogre.qc",
    "content": "/*\r\n==============================================================================\r\n\r\nOGRE\r\n\r\n==============================================================================\r\n*/\r\n\r\n$cd id1/models/ogre_c\r\n$origin 0 0 24\r\n$base base\t\t\r\n$skin base\r\n\r\n$frame\tstand1 stand2 stand3 stand4 stand5 stand6 stand7 stand8 stand9\r\n\r\n$frame walk1 walk2 walk3 walk4 walk5 walk6 walk7\r\n$frame walk8 walk9 walk10 walk11 walk12 walk13 walk14 walk15 walk16\r\n\r\n$frame run1 run2 run3 run4 run5 run6 run7 run8\r\n\r\n$frame swing1 swing2 swing3 swing4 swing5 swing6 swing7\r\n$frame swing8 swing9 swing10 swing11 swing12 swing13 swing14\r\n\r\n$frame smash1 smash2 smash3 smash4 smash5 smash6 smash7\r\n$frame smash8 smash9 smash10 smash11 smash12 smash13 smash14\r\n\r\n$frame shoot1 shoot2 shoot3 shoot4 shoot5 shoot6\r\n\r\n$frame pain1 pain2 pain3 pain4 pain5\r\n\r\n$frame painb1 painb2 painb3\r\n\r\n$frame painc1 painc2 painc3 painc4 painc5 painc6\r\n\r\n$frame paind1 paind2 paind3 paind4 paind5 paind6 paind7 paind8 paind9 paind10\r\n$frame paind11 paind12 paind13 paind14 paind15 paind16\r\n\r\n$frame paine1 paine2 paine3 paine4 paine5 paine6 paine7 paine8 paine9 paine10\r\n$frame paine11 paine12 paine13 paine14 paine15\r\n\r\n$frame death1 death2 death3 death4 death5 death6\r\n$frame death7 death8 death9 death10 death11 death12\r\n$frame death13 death14\r\n\r\n$frame bdeath1 bdeath2 bdeath3 bdeath4 bdeath5 bdeath6\r\n$frame bdeath7 bdeath8 bdeath9 bdeath10\r\n\r\n$frame pull1 pull2 pull3 pull4 pull5 pull6 pull7 pull8 pull9 pull10 pull11\r\n\r\n//=============================================================================\r\n\r\n\r\nvoid() OgreGrenadeExplode =\r\n{\r\n\tT_RadiusDamage (self, self.owner, 40, world);\r\n#if 1\r\n\tBecomeExplosion();\r\n#else\r\n\tsound (self, CHAN_VOICE, \"weapons/r_exp3.wav\", 1, ATTN_NORM);\r\n\r\n\tWriteByte (MSG_BROADCAST, SVC_TEMPENTITY);\r\n\tWriteByte (MSG_BROADCAST, TE_EXPLOSION);\r\n\tWriteCoord (MSG_BROADCAST, self.origin_x);\r\n\tWriteCoord (MSG_BROADCAST, self.origin_y);\r\n\tWriteCoord (MSG_BROADCAST, self.origin_z);\r\n\r\n\tself.velocity = '0 0 0';\r\n\tself.touch = SUB_Null;\r\n\tsetmodel (self, \"progs/s_explod.spr\");\r\n\tself.solid = SOLID_NOT;\r\n\ts_explode1 ();\r\n#endif\r\n};\r\n\r\nvoid() OgreGrenadeTouch =\r\n{\r\n\tif (other == self.owner)\r\n\t\treturn;\t\t// don't explode on owner\r\n\tif (other.takedamage == DAMAGE_AIM)\r\n\t{\r\n\t\tOgreGrenadeExplode();\r\n\t\treturn;\r\n\t}\r\n\tsound (self, CHAN_VOICE, \"weapons/bounce.wav\", 1, ATTN_NORM);\t// bounce sound\r\n\tif (self.velocity == '0 0 0')\r\n\t\tself.avelocity = '0 0 0';\r\n};\r\n\r\n/*\r\n================\r\nOgreFireGrenade\r\n================\r\n*/\r\nvoid() OgreFireGrenade =\r\n{\r\n\tlocal\tentity missile;\r\n\t\r\n\tself.effects = self.effects | EF_MUZZLEFLASH;\r\n\r\n\tsound (self, CHAN_WEAPON, \"weapons/grenade.wav\", 1, ATTN_NORM);\r\n\r\n\tmissile = spawn ();\r\n\tmissile.owner = self;\r\n\tmissile.movetype = MOVETYPE_BOUNCE;\r\n\tmissile.solid = SOLID_BBOX;\r\n\t\t\r\n// set missile speed\t\r\n\r\n\tmakevectors (self.angles);\r\n\r\n\tmissile.velocity = normalize(self.enemy.origin - self.origin);\r\n\tmissile.velocity = missile.velocity * 600;\r\n\tmissile.velocity_z = 200;\r\n\r\n\tmissile.avelocity = '300 300 300';\r\n\r\n\tmissile.angles = vectoangles(missile.velocity);\r\n\t\r\n\tmissile.touch = OgreGrenadeTouch;\r\n\t\r\n// set missile duration\r\n\tmissile.nextthink = time + 2.5;\r\n\tmissile.think = OgreGrenadeExplode;\r\n\r\n\tsetmodel (missile, \"progs/grenade.mdl\");\r\n\tsetsize (missile, '0 0 0', '0 0 0');\t\t\r\n\tsetorigin (missile, self.origin);\r\n};\r\n\r\n\r\n//=============================================================================\r\n\r\n/*\r\n================\r\nchainsaw\r\n\r\nFIXME\r\n================\r\n*/\r\nvoid(float side) chainsaw =\r\n{\r\nlocal vector\tdelta;\r\nlocal float \tldmg;\r\n\r\n\tif (!self.enemy)\r\n\t\treturn;\r\n\tif (!CanDamage (self.enemy, self))\r\n\t\treturn;\r\n\r\n\tai_charge(10);\r\n\r\n\tdelta = self.enemy.origin - self.origin;\r\n\r\n\tif (vlen(delta) > 100)\r\n\t\treturn;\r\n\t\t\r\n\tldmg = (random()*3) * 4;\r\n\tT_Damage (self.enemy, self, self, ldmg);\r\n\t\r\n\tif (side)\r\n\t{\r\n\t\tmakevectors (self.angles);\r\n\t\tif (side == 1)\r\n\t\t\tSpawnMeatSpray (self.origin + v_forward*16, crandom() * 100 * v_right);\r\n\t\telse\r\n\t\t\tSpawnMeatSpray (self.origin + v_forward*16, side * v_right);\r\n\t}\r\n};\r\n\r\n\r\nvoid() ogre_stand1\t=[\t$stand1,\togre_stand2\t] {ai_stand();};\r\nvoid() ogre_stand2\t=[\t$stand2,\togre_stand3\t] {ai_stand();};\r\nvoid() ogre_stand3\t=[\t$stand3,\togre_stand4\t] {ai_stand();};\r\nvoid() ogre_stand4\t=[\t$stand4,\togre_stand5\t] {ai_stand();};\r\nvoid() ogre_stand5\t=[\t$stand5,\togre_stand6\t] {\r\nif (random() < 0.2)\r\n\tsound (self, CHAN_VOICE, \"ogre/ogidle.wav\", 1, ATTN_IDLE);\r\nai_stand();\r\n};\r\nvoid() ogre_stand6\t=[\t$stand6,\togre_stand7\t] {ai_stand();};\r\nvoid() ogre_stand7\t=[\t$stand7,\togre_stand8\t] {ai_stand();};\r\nvoid() ogre_stand8\t=[\t$stand8,\togre_stand9\t] {ai_stand();};\r\nvoid() ogre_stand9\t=[\t$stand9,\togre_stand1\t] {ai_stand();};\r\n\r\nvoid() ogre_walk1\t=[\t$walk1,\t\togre_walk2\t] {ai_walk(3);};\r\nvoid() ogre_walk2\t=[\t$walk2,\t\togre_walk3\t] {ai_walk(2);};\r\nvoid() ogre_walk3\t=[\t$walk3,\t\togre_walk4\t] {\r\nai_walk(2);\r\nif (random() < 0.2)\r\n\tsound (self, CHAN_VOICE, \"ogre/ogidle.wav\", 1, ATTN_IDLE);\r\n};\r\nvoid() ogre_walk4\t=[\t$walk4,\t\togre_walk5\t] {ai_walk(2);};\r\nvoid() ogre_walk5\t=[\t$walk5,\t\togre_walk6\t] {ai_walk(2);};\r\nvoid() ogre_walk6\t=[\t$walk6,\t\togre_walk7\t] {\r\nai_walk(5);\r\nif (random() < 0.1)\r\n\tsound (self, CHAN_VOICE, \"ogre/ogdrag.wav\", 1, ATTN_IDLE);\r\n};\r\nvoid() ogre_walk7\t=[\t$walk7,\t\togre_walk8\t] {ai_walk(3);};\r\nvoid() ogre_walk8\t=[\t$walk8,\t\togre_walk9\t] {ai_walk(2);};\r\nvoid() ogre_walk9\t=[\t$walk9,\t\togre_walk10\t] {ai_walk(3);};\r\nvoid() ogre_walk10\t=[\t$walk10,\togre_walk11\t] {ai_walk(1);};\r\nvoid() ogre_walk11\t=[\t$walk11,\togre_walk12\t] {ai_walk(2);};\r\nvoid() ogre_walk12\t=[\t$walk12,\togre_walk13\t] {ai_walk(3);};\r\nvoid() ogre_walk13\t=[\t$walk13,\togre_walk14\t] {ai_walk(3);};\r\nvoid() ogre_walk14\t=[\t$walk14,\togre_walk15\t] {ai_walk(3);};\r\nvoid() ogre_walk15\t=[\t$walk15,\togre_walk16\t] {ai_walk(3);};\r\nvoid() ogre_walk16\t=[\t$walk16,\togre_walk1\t] {ai_walk(4);};\r\n\r\nvoid() ogre_run1\t=[\t$run1,\t\togre_run2\t] {ai_run(9);\r\nif (random() < 0.2)\r\n\tsound (self, CHAN_VOICE, \"ogre/ogidle2.wav\", 1, ATTN_IDLE);\r\n};\r\nvoid() ogre_run2\t=[\t$run2,\t\togre_run3\t] {ai_run(12);};\r\nvoid() ogre_run3\t=[\t$run3,\t\togre_run4\t] {ai_run(8);};\r\nvoid() ogre_run4\t=[\t$run4,\t\togre_run5\t] {ai_run(22);};\r\nvoid() ogre_run5\t=[\t$run5,\t\togre_run6\t] {ai_run(16);};\r\nvoid() ogre_run6\t=[\t$run6,\t\togre_run7\t] {ai_run(4);};\r\nvoid() ogre_run7\t=[\t$run7,\t\togre_run8\t] {ai_run(13);};\r\nvoid() ogre_run8\t=[\t$run8,\t\togre_run1\t] {ai_run(24);};\r\n\r\nvoid() ogre_swing1\t=[\t$swing1,\t\togre_swing2\t] {ai_charge(11);\r\nsound (self, CHAN_WEAPON, \"ogre/ogsawatk.wav\", 1, ATTN_NORM);\r\n};\r\nvoid() ogre_swing2\t=[\t$swing2,\t\togre_swing3\t] {ai_charge(1);};\r\nvoid() ogre_swing3\t=[\t$swing3,\t\togre_swing4\t] {ai_charge(4);};\r\nvoid() ogre_swing4\t=[\t$swing4,\t\togre_swing5\t] {ai_charge(13);};\r\nvoid() ogre_swing5\t=[\t$swing5,\t\togre_swing6\t] {ai_charge(9); chainsaw(0);self.angles_y = self.angles_y + random()*25;};\r\nvoid() ogre_swing6\t=[\t$swing6,\t\togre_swing7\t] {chainsaw(200);self.angles_y = self.angles_y + random()* 25;};\r\nvoid() ogre_swing7\t=[\t$swing7,\t\togre_swing8\t] {chainsaw(0);self.angles_y = self.angles_y + random()* 25;};\r\nvoid() ogre_swing8\t=[\t$swing8,\t\togre_swing9\t] {chainsaw(0);self.angles_y = self.angles_y + random()* 25;};\r\nvoid() ogre_swing9\t=[\t$swing9,\t\togre_swing10 ] {chainsaw(0);self.angles_y = self.angles_y + random()* 25;};\r\nvoid() ogre_swing10\t=[\t$swing10,\t\togre_swing11 ] {chainsaw(-200);self.angles_y = self.angles_y + random()* 25;};\r\nvoid() ogre_swing11\t=[\t$swing11,\t\togre_swing12 ] {chainsaw(0);self.angles_y = self.angles_y + random()* 25;};\r\nvoid() ogre_swing12\t=[\t$swing12,\t\togre_swing13 ] {ai_charge(3);};\r\nvoid() ogre_swing13\t=[\t$swing13,\t\togre_swing14 ] {ai_charge(8);};\r\nvoid() ogre_swing14\t=[\t$swing14,\t\togre_run1\t] {ai_charge(9);};\r\n\r\nvoid() ogre_smash1\t=[\t$smash1,\t\togre_smash2\t] {ai_charge(6);\r\nsound (self, CHAN_WEAPON, \"ogre/ogsawatk.wav\", 1, ATTN_NORM);\r\n};\r\nvoid() ogre_smash2\t=[\t$smash2,\t\togre_smash3\t] {ai_charge(0);};\r\nvoid() ogre_smash3\t=[\t$smash3,\t\togre_smash4\t] {ai_charge(0);};\r\nvoid() ogre_smash4\t=[\t$smash4,\t\togre_smash5\t] {ai_charge(1);};\r\nvoid() ogre_smash5\t=[\t$smash5,\t\togre_smash6\t] {ai_charge(4);};\r\nvoid() ogre_smash6\t=[\t$smash6,\t\togre_smash7\t] {ai_charge(4); chainsaw(0);};\r\nvoid() ogre_smash7\t=[\t$smash7,\t\togre_smash8\t] {ai_charge(4); chainsaw(0);};\r\nvoid() ogre_smash8\t=[\t$smash8,\t\togre_smash9\t] {ai_charge(10); chainsaw(0);};\r\nvoid() ogre_smash9\t=[\t$smash9,\t\togre_smash10 ] {ai_charge(13); chainsaw(0);};\r\nvoid() ogre_smash10\t=[\t$smash10,\t\togre_smash11 ] {chainsaw(1);};\r\nvoid() ogre_smash11\t=[\t$smash11,\t\togre_smash12 ] {ai_charge(2); chainsaw(0);\r\nself.nextthink = self.nextthink + random()*0.2;};\t// slight variation\r\nvoid() ogre_smash12\t=[\t$smash12,\t\togre_smash13 ] {ai_charge(0);};\r\nvoid() ogre_smash13\t=[\t$smash13,\t\togre_smash14 ] {ai_charge(4);};\r\nvoid() ogre_smash14\t=[\t$smash14,\t\togre_run1\t] {ai_charge(12);};\r\n\r\nvoid() ogre_nail1\t=[\t$shoot1,\t\togre_nail2\t] {ai_face();};\r\nvoid() ogre_nail2\t=[\t$shoot2,\t\togre_nail3\t] {ai_face();};\r\nvoid() ogre_nail3\t=[\t$shoot2,\t\togre_nail4\t] {ai_face();};\r\nvoid() ogre_nail4\t=[\t$shoot3,\t\togre_nail5\t] {ai_face();OgreFireGrenade();};\r\nvoid() ogre_nail5\t=[\t$shoot4,\t\togre_nail6\t] {ai_face();};\r\nvoid() ogre_nail6\t=[\t$shoot5,\t\togre_nail7\t] {ai_face();};\r\nvoid() ogre_nail7\t=[\t$shoot6,\t\togre_run1\t] {ai_face();};\r\n\r\nvoid()\togre_pain1\t=[\t$pain1,\t\togre_pain2\t] {};\r\nvoid()\togre_pain2\t=[\t$pain2,\t\togre_pain3\t] {};\r\nvoid()\togre_pain3\t=[\t$pain3,\t\togre_pain4\t] {};\r\nvoid()\togre_pain4\t=[\t$pain4,\t\togre_pain5\t] {};\r\nvoid()\togre_pain5\t=[\t$pain5,\t\togre_run1\t] {};\r\n\r\n\r\nvoid()\togre_painb1\t=[\t$painb1,\togre_painb2\t] {};\r\nvoid()\togre_painb2\t=[\t$painb2,\togre_painb3\t] {};\r\nvoid()\togre_painb3\t=[\t$painb3,\togre_run1\t] {};\r\n\r\n\r\nvoid()\togre_painc1\t=[\t$painc1,\togre_painc2\t] {};\r\nvoid()\togre_painc2\t=[\t$painc2,\togre_painc3\t] {};\r\nvoid()\togre_painc3\t=[\t$painc3,\togre_painc4\t] {};\r\nvoid()\togre_painc4\t=[\t$painc4,\togre_painc5\t] {};\r\nvoid()\togre_painc5\t=[\t$painc5,\togre_painc6\t] {};\r\nvoid()\togre_painc6\t=[\t$painc6,\togre_run1\t] {};\r\n\r\n\r\nvoid()\togre_paind1\t=[\t$paind1,\togre_paind2\t] {};\r\nvoid()\togre_paind2\t=[\t$paind2,\togre_paind3\t] {ai_pain(10);};\r\nvoid()\togre_paind3\t=[\t$paind3,\togre_paind4\t] {ai_pain(9);};\r\nvoid()\togre_paind4\t=[\t$paind4,\togre_paind5\t] {ai_pain(4);};\r\nvoid()\togre_paind5\t=[\t$paind5,\togre_paind6\t] {};\r\nvoid()\togre_paind6\t=[\t$paind6,\togre_paind7\t] {};\r\nvoid()\togre_paind7\t=[\t$paind7,\togre_paind8\t] {};\r\nvoid()\togre_paind8\t=[\t$paind8,\togre_paind9\t] {};\r\nvoid()\togre_paind9\t=[\t$paind9,\togre_paind10\t] {};\r\nvoid()\togre_paind10=[\t$paind10,\togre_paind11\t] {};\r\nvoid()\togre_paind11=[\t$paind11,\togre_paind12\t] {};\r\nvoid()\togre_paind12=[\t$paind12,\togre_paind13\t] {};\r\nvoid()\togre_paind13=[\t$paind13,\togre_paind14\t] {};\r\nvoid()\togre_paind14=[\t$paind14,\togre_paind15\t] {};\r\nvoid()\togre_paind15=[\t$paind15,\togre_paind16\t] {};\r\nvoid()\togre_paind16=[\t$paind16,\togre_run1\t] {};\r\n\r\nvoid()\togre_paine1\t=[\t$paine1,\togre_paine2\t] {};\r\nvoid()\togre_paine2\t=[\t$paine2,\togre_paine3\t] {ai_pain(10);};\r\nvoid()\togre_paine3\t=[\t$paine3,\togre_paine4\t] {ai_pain(9);};\r\nvoid()\togre_paine4\t=[\t$paine4,\togre_paine5\t] {ai_pain(4);};\r\nvoid()\togre_paine5\t=[\t$paine5,\togre_paine6\t] {};\r\nvoid()\togre_paine6\t=[\t$paine6,\togre_paine7\t] {};\r\nvoid()\togre_paine7\t=[\t$paine7,\togre_paine8\t] {};\r\nvoid()\togre_paine8\t=[\t$paine8,\togre_paine9\t] {};\r\nvoid()\togre_paine9\t=[\t$paine9,\togre_paine10\t] {};\r\nvoid()\togre_paine10=[\t$paine10,\togre_paine11\t] {};\r\nvoid()\togre_paine11=[\t$paine11,\togre_paine12\t] {};\r\nvoid()\togre_paine12=[\t$paine12,\togre_paine13\t] {};\r\nvoid()\togre_paine13=[\t$paine13,\togre_paine14\t] {};\r\nvoid()\togre_paine14=[\t$paine14,\togre_paine15\t] {};\r\nvoid()\togre_paine15=[\t$paine15,\togre_run1\t] {};\r\n\r\n\r\nvoid(entity attacker, float damage)\togre_pain =\r\n{\r\n\tlocal float\tr;\r\n\r\n// don't make multiple pain sounds right after each other\r\n\tif (self.pain_finished > time)\r\n\t\treturn;\r\n\r\n\tsound (self, CHAN_VOICE, \"ogre/ogpain1.wav\", 1, ATTN_NORM);\t\t\r\n\r\n\tr = random();\r\n\t\r\n\tif (r < 0.25)\r\n\t{\r\n\t\togre_pain1 ();\r\n\t\tself.pain_finished = time + 1;\r\n\t}\r\n\telse if (r < 0.5)\r\n\t{\r\n\t\togre_painb1 ();\r\n\t\tself.pain_finished = time + 1;\r\n\t}\r\n\telse if (r < 0.75)\r\n\t{\r\n\t\togre_painc1 ();\r\n\t\tself.pain_finished = time + 1;\r\n\t}\r\n\telse if (r < 0.88)\r\n\t{\r\n\t\togre_paind1 ();\r\n\t\tself.pain_finished = time + 2;\r\n\t}\r\n\telse\r\n\t{\r\n\t\togre_paine1 ();\r\n\t\tself.pain_finished = time + 2;\r\n\t}\r\n};\r\n\r\nvoid()\togre_die1\t=[\t$death1,\togre_die2\t] {};\r\nvoid()\togre_die2\t=[\t$death2,\togre_die3\t] {};\r\nvoid()\togre_die3\t=[\t$death3,\togre_die4\t]\r\n{self.solid = SOLID_NOT;\r\nself.ammo_rockets = 2;DropBackpack();};\r\nvoid()\togre_die4\t=[\t$death4,\togre_die5\t] {};\r\nvoid()\togre_die5\t=[\t$death5,\togre_die6\t] {};\r\nvoid()\togre_die6\t=[\t$death6,\togre_die7\t] {};\r\nvoid()\togre_die7\t=[\t$death7,\togre_die8\t] {};\r\nvoid()\togre_die8\t=[\t$death8,\togre_die9\t] {};\r\nvoid()\togre_die9\t=[\t$death9,\togre_die10\t] {};\r\nvoid()\togre_die10\t=[\t$death10,\togre_die11\t] {};\r\nvoid()\togre_die11\t=[\t$death11,\togre_die12\t] {};\r\nvoid()\togre_die12\t=[\t$death12,\togre_die13\t] {};\r\nvoid()\togre_die13\t=[\t$death13,\togre_die14\t] {};\r\nvoid()\togre_die14\t=[\t$death14,\togre_die14\t] {};\r\n\r\nvoid()\togre_bdie1\t=[\t$bdeath1,\togre_bdie2\t] {};\r\nvoid()\togre_bdie2\t=[\t$bdeath2,\togre_bdie3\t] {ai_forward(5);};\r\nvoid()\togre_bdie3\t=[\t$bdeath3,\togre_bdie4\t]\r\n{self.solid = SOLID_NOT;\r\nself.ammo_rockets = 2;DropBackpack();};\r\nvoid()\togre_bdie4\t=[\t$bdeath4,\togre_bdie5\t] {ai_forward(1);};\r\nvoid()\togre_bdie5\t=[\t$bdeath5,\togre_bdie6\t] {ai_forward(3);};\r\nvoid()\togre_bdie6\t=[\t$bdeath6,\togre_bdie7\t] {ai_forward(7);};\r\nvoid()\togre_bdie7\t=[\t$bdeath7,\togre_bdie8\t] {ai_forward(25);};\r\nvoid()\togre_bdie8\t=[\t$bdeath8,\togre_bdie9\t] {};\r\nvoid()\togre_bdie9\t=[\t$bdeath9,\togre_bdie10\t] {};\r\nvoid()\togre_bdie10\t=[\t$bdeath10,\togre_bdie10\t] {};\r\n\r\nvoid() ogre_die =\r\n{\r\n// check for gib\r\n\tif (self.health < -80)\r\n\t{\r\n\t\tThrowCSQCGibs(GIB_OGRE);\r\n\t\treturn;\r\n\t}\r\n\r\n\tsound (self, CHAN_VOICE, \"ogre/ogdth.wav\", 1, ATTN_NORM);\r\n\t\r\n\tif (random() < 0.5)\r\n\t\togre_die1 ();\r\n\telse\r\n\t\togre_bdie1 ();\r\n};\r\n\r\nvoid() ogre_melee =\r\n{\r\n\tif (random() > 0.5)\r\n\t\togre_smash1 ();\r\n\telse\r\n\t\togre_swing1 ();\r\n};\r\n\r\n\r\n/*QUAKED monster_ogre (1 0 0) (-32 -32 -24) (32 32 64) Ambush\r\n\r\n*/\r\nvoid() monster_ogre =\r\n{\r\n\tif (deathmatch)\r\n\t{\r\n\t\tremove(self);\r\n\t\treturn;\r\n\t}\r\n\tprecache_model (\"progs/ogre.mdl\");\r\n\tprecache_model (\"progs/h_ogre.mdl\");\r\n\tprecache_model (\"progs/grenade.mdl\");\r\n\r\n\tprecache_sound (\"ogre/ogdrag.wav\");\r\n\tprecache_sound (\"ogre/ogdth.wav\");\r\n\tprecache_sound (\"ogre/ogidle.wav\");\r\n\tprecache_sound (\"ogre/ogidle2.wav\");\r\n\tprecache_sound (\"ogre/ogpain1.wav\");\r\n\tprecache_sound (\"ogre/ogsawatk.wav\");\r\n\tprecache_sound (\"ogre/ogwake.wav\");\r\n\r\n\tself.solid = SOLID_SLIDEBOX;\r\n\tself.movetype = MOVETYPE_STEP;\r\n\r\n\tsetmodel (self, \"progs/ogre.mdl\");\r\n\r\n\tsetsize (self, VEC_HULL2_MIN, VEC_HULL2_MAX);\r\n\tself.health = 200;\r\n\r\n\tself.th_stand = ogre_stand1;\r\n\tself.th_walk = ogre_walk1;\r\n\tself.th_run = ogre_run1;\r\n\tself.th_die = ogre_die;\r\n\tself.th_melee = ogre_melee;\r\n\tself.th_missile = ogre_nail1;\r\n\tself.th_pain = ogre_pain;\r\n\t\r\n\twalkmonster_start();\r\n};\r\n\r\nvoid() monster_ogre_marksman =\r\n{\r\n\tmonster_ogre ();\r\n};\r\n\r\n\r\n"
  },
  {
    "path": "quakec/csqctest/src/ss/oldone.qc",
    "content": "/*\r\n==============================================================================\r\n\r\nOLD ONE\r\n\r\n==============================================================================\r\n*/\r\n$cd id1/models/old_one\r\n$origin 0 0 24\r\n$base base\r\n$skin skin\r\n$scale 1\r\n\r\nvoid() finale_1;\r\nvoid() finale_2;\r\nvoid() finale_3;\r\nvoid() finale_4;\r\n\r\n\r\nentity\tshub;\r\n\r\n$frame old1 old2 old3 old4 old5 old6 old7 old8 old9 \r\n$frame old10 old11 old12 old13 old14 old15 old16 old17 old18 old19 \r\n$frame old20 old21 old22 old23 old24 old25 old26 old27 old28 old29 \r\n$frame old30 old31 old32 old33 old34 old35 old36 old37 old38 old39 \r\n$frame old40 old41 old42 old43 old44 old45 old46 \r\n\r\n$frame shake1 shake2 shake3 shake4 shake5 shake6 shake7 shake8\r\n$frame shake9 shake10 shake11 shake12 shake12_dupe shake13 shake14\r\n$frame shake15 shake16 shake17 shake18 shake19 shake20\r\n\r\n//void() old_stand     =[      $old1,       old_stand    ] {};\r\n\r\nvoid() old_idle1        =[      $old1,  old_idle2       ] {};\r\nvoid() old_idle2        =[      $old2,  old_idle3       ] {};\r\nvoid() old_idle3        =[      $old3,  old_idle4       ] {};\r\nvoid() old_idle4        =[      $old4,  old_idle5       ] {};\r\nvoid() old_idle5        =[      $old5,  old_idle6       ] {};\r\nvoid() old_idle6        =[      $old6,  old_idle7       ] {};\r\nvoid() old_idle7        =[      $old7,  old_idle8       ] {};\r\nvoid() old_idle8        =[      $old8,  old_idle9       ] {};\r\nvoid() old_idle9        =[      $old9,  old_idle10      ] {};\r\nvoid() old_idle10       =[      $old10, old_idle11      ] {};\r\nvoid() old_idle11       =[      $old11, old_idle12      ] {};\r\nvoid() old_idle12       =[      $old12, old_idle13      ] {};\r\nvoid() old_idle13       =[      $old13, old_idle14      ] {};\r\nvoid() old_idle14       =[      $old14, old_idle15      ] {};\r\nvoid() old_idle15       =[      $old15, old_idle16      ] {};\r\nvoid() old_idle16       =[      $old16, old_idle17      ] {};\r\nvoid() old_idle17       =[      $old17, old_idle18      ] {};\r\nvoid() old_idle18       =[      $old18, old_idle19      ] {};\r\nvoid() old_idle19       =[      $old19, old_idle20      ] {};\r\nvoid() old_idle20       =[      $old20, old_idle21      ] {};\r\nvoid() old_idle21       =[      $old21, old_idle22      ] {};\r\nvoid() old_idle22       =[      $old22, old_idle23      ] {};\r\nvoid() old_idle23       =[      $old23, old_idle24      ] {};\r\nvoid() old_idle24       =[      $old24, old_idle25      ] {};\r\nvoid() old_idle25       =[      $old25, old_idle26      ] {};\r\nvoid() old_idle26       =[      $old26, old_idle27      ] {};\r\nvoid() old_idle27       =[      $old27, old_idle28      ] {};\r\nvoid() old_idle28       =[      $old28, old_idle29      ] {};\r\nvoid() old_idle29       =[      $old29, old_idle30      ] {};\r\nvoid() old_idle30       =[      $old30, old_idle31      ] {};\r\nvoid() old_idle31       =[      $old31, old_idle32      ] {};\r\nvoid() old_idle32       =[      $old32, old_idle33      ] {};\r\nvoid() old_idle33       =[      $old33, old_idle34      ] {};\r\nvoid() old_idle34       =[      $old34, old_idle35      ] {};\r\nvoid() old_idle35       =[      $old35, old_idle36      ] {};\r\nvoid() old_idle36       =[      $old36, old_idle37      ] {};\r\nvoid() old_idle37       =[      $old37, old_idle38      ] {};\r\nvoid() old_idle38       =[      $old38, old_idle39      ] {};\r\nvoid() old_idle39       =[      $old39, old_idle40      ] {};\r\nvoid() old_idle40       =[      $old40, old_idle41      ] {};\r\nvoid() old_idle41       =[      $old41, old_idle42      ] {};\r\nvoid() old_idle42       =[      $old42, old_idle43      ] {};\r\nvoid() old_idle43       =[      $old43, old_idle44      ] {};\r\nvoid() old_idle44       =[      $old44, old_idle45      ] {};\r\nvoid() old_idle45       =[      $old45, old_idle46      ] {};\r\nvoid() old_idle46       =[      $old46, old_idle1       ] {};\r\n\r\n\r\nvoid() old_thrash1        =[ $shake1,  old_thrash2       ] {lightstyle(0, \"m\");};\r\nvoid() old_thrash2        =[      $shake2,  old_thrash3       ] {lightstyle(0, \"k\");};\r\nvoid() old_thrash3        =[      $shake3,  old_thrash4       ] {lightstyle(0, \"k\");};\r\nvoid() old_thrash4        =[      $shake4,  old_thrash5       ] {lightstyle(0, \"i\");};\r\nvoid() old_thrash5        =[      $shake5,  old_thrash6       ] {lightstyle(0, \"g\");};\r\nvoid() old_thrash6        =[      $shake6,  old_thrash7       ] {lightstyle(0, \"e\");};\r\nvoid() old_thrash7        =[      $shake7,  old_thrash8       ] {lightstyle(0, \"c\");};\r\nvoid() old_thrash8        =[      $shake8,  old_thrash9       ] {lightstyle(0, \"a\");};\r\nvoid() old_thrash9        =[      $shake9,  old_thrash10      ] {lightstyle(0, \"c\");};\r\nvoid() old_thrash10       =[      $shake10, old_thrash11      ] {lightstyle(0, \"e\");};\r\nvoid() old_thrash11       =[      $shake11, old_thrash12      ] {lightstyle(0, \"g\");};\r\nvoid() old_thrash12       =[      $shake12, old_thrash13      ] {lightstyle(0, \"i\");};\r\nvoid() old_thrash13       =[      $shake13, old_thrash14      ] {lightstyle(0, \"k\");};\r\nvoid() old_thrash14       =[      $shake14, old_thrash15      ] {lightstyle(0, \"m\");};\r\nvoid() old_thrash15       =[      $shake15, old_thrash16      ] {lightstyle(0, \"m\");\r\nself.cnt = self.cnt + 1;\r\nif (self.cnt != 3)\r\n\tself.think = old_thrash1;\r\n};\r\nvoid() old_thrash16       =[      $shake16, old_thrash17      ] {lightstyle(0, \"g\");};\r\nvoid() old_thrash17       =[      $shake17, old_thrash18      ] {lightstyle(0, \"c\");};\r\nvoid() old_thrash18       =[      $shake18, old_thrash19      ] {lightstyle(0, \"b\");};\r\nvoid() old_thrash19       =[      $shake19, old_thrash20      ] {lightstyle(0, \"a\");};\r\nvoid() old_thrash20       =[      $shake20, old_thrash20      ] {finale_4();};\r\n\r\n//============================================================================\r\n\r\nvoid() finale_1 =\r\n{\r\n\tlocal entity\tpos, pl;\r\n\tlocal entity\ttimer;\r\n\r\n\tintermission_exittime = time + 10000000;\t// never allow exit\r\n\tintermission_running = 1;\r\n\r\n\t// find the intermission spot\r\n\tpos = find (world, classname, \"info_intermission\");\r\n\tif (!pos)\r\n\t\terror (\"no info_intermission\");\r\n\tpl = find (world, classname, \"misc_teleporttrain\");\r\n\tif (!pl)\r\n\t\terror (\"no teleporttrain\");\r\n\tremove (pl);\r\n\r\n\tWriteByte (MSG_ALL, SVC_FINALE);\r\n\tWriteString (MSG_ALL, \"\");\r\n\r\n\tpl = find (world, classname, \"player\");\r\n\twhile (pl != world)\r\n\t{\r\n\t\tpl.view_ofs = '0 0 0';\r\n\t\tpl.angles = other.v_angle = pos.mangle;\r\n\t\tpl.fixangle = TRUE;\t\t// turn this way immediately\r\n\t\tpl.map = self.map;\r\n\t\tpl.nextthink = time + 0.5;\r\n\t\tpl.takedamage = DAMAGE_NO;\r\n\t\tpl.solid = SOLID_NOT;\r\n\t\tpl.movetype = MOVETYPE_NONE;\r\n\t\tpl.modelindex = 0;\r\n\t\tsetorigin (pl, pos.origin);\r\n\t\tpl = find (pl, classname, \"player\");\r\n\t}\t\r\n\t\r\n\t// make fake versions of all players as standins, and move the real\r\n\t// players to the intermission spot\r\n\t\r\n\t// wait for 1 second\r\n\ttimer = spawn();\r\n\ttimer.nextthink = time + 1;\r\n\ttimer.think = finale_2;\t\r\n};\r\n\r\nvoid() finale_2 =\r\n{\r\n\tlocal vector\to;\r\n\r\n\t// start a teleport splash inside shub\r\n\r\n\to = shub.origin - '0 100 0';\r\n\tWriteByte (MSG_BROADCAST, SVC_TEMPENTITY);\r\n\tWriteByte (MSG_BROADCAST, TE_TELEPORT);\r\n\tWriteCoord (MSG_BROADCAST, o_x);\r\n\tWriteCoord (MSG_BROADCAST, o_y);\r\n\tWriteCoord (MSG_BROADCAST, o_z);\r\n\r\n\tsound (shub, CHAN_VOICE, \"misc/r_tele1.wav\", 1, ATTN_NORM);\r\n\t\r\n\tself.nextthink = time + 2;\r\n\tself.think = finale_3;\r\n};\r\n\r\nvoid() finale_3 =\r\n{\r\n\t// start shub thrashing wildly\r\n\tshub.think = old_thrash1;\r\n\tsound (shub, CHAN_VOICE, \"boss2/death.wav\", 1, ATTN_NORM);\r\n\tlightstyle(0, \"abcdefghijklmlkjihgfedcb\");\t\r\n};\r\n\r\nvoid() finale_4 =\r\n{\r\n\t// throw tons of meat chunks\t\r\n\tlocal\tvector\toldo;\r\n\tlocal\tfloat\tx, y, z;\r\n\tlocal\tfloat\tr;\r\n\tlocal entity\tn;\r\n\r\n\tsound (self, CHAN_VOICE, \"boss2/pop2.wav\", 1, ATTN_NORM);\r\n\t\r\n\toldo = self.origin;\r\n\r\n\tz = 16;\r\n\twhile (z <= 144)\r\n\t{\r\n\t\tx = -64;\r\n\t\twhile (x <= 64)\r\n\t\t{\r\n\t\t\ty = -64;\r\n\t\t\twhile (y <= 64)\r\n\t\t\t{\r\n\t\t\t\tself.origin_x = oldo_x + x;\r\n\t\t\t\tself.origin_y = oldo_y + y;\r\n\t\t\t\tself.origin_z = oldo_z + z;\r\n\r\n\t\t\t\tr = random();\r\n\t\t\t\tif (r < 0.3)\t\t\t\t\r\n\t\t\t\t\tThrowGib (\"progs/gib1.mdl\", -999);\r\n\t\t\t\telse if (r < 0.6)\r\n\t\t\t\t\tThrowGib (\"progs/gib2.mdl\", -999);\r\n\t\t\t\telse\r\n\t\t\t\t\tThrowGib (\"progs/gib3.mdl\", -999);\r\n\t\t\t\ty = y + 32;\r\n\t\t\t}\r\n\t\t\tx = x + 32;\r\n\t\t}\r\n\t\tz = z + 96;\r\n\t}\r\n\t// start the end text\r\n\tWriteByte (MSG_ALL, SVC_FINALE);\r\n\tWriteString (MSG_ALL, \"Congratulations and well done! You have\\nbeaten the hideous Shub-Niggurath, and\\nher hundreds of ugly changelings and\\nmonsters. You have proven that your\\nskill and your cunning are greater than\\nall the powers of Quake. You are the\\nmaster now. Id Software salutes you.\");\r\n\r\n// put a player model down\r\n\tn = spawn();\r\n\tsetmodel (n, \"progs/player.mdl\");\r\n\toldo = oldo - '32 264 0';\r\n\tsetorigin (n, oldo);\r\n\tn.angles = '0 290 0';\r\n\tn.frame = 1;\r\n\r\n\tremove (self);\r\n\r\n// switch cd track\r\n\tWriteByte (MSG_ALL, SVC_CDTRACK);\r\n\tWriteByte (MSG_ALL, 3);\r\n\tWriteByte (MSG_ALL, 3);\r\n\tlightstyle(0, \"m\");\t\r\n};\r\n\r\n//============================================================================\r\n\r\nvoid (entity attacker, float damage) nopain =\r\n{\r\n\tself.health = 40000;\r\n};\r\n\r\n//============================================================================\r\n\r\n\r\n/*QUAKED monster_oldone (1 0 0) (-16 -16 -24) (16 16 32)\r\n*/\r\nvoid() monster_oldone =\r\n{\r\n\tif (deathmatch)\r\n\t{\r\n\t\tremove(self);\r\n\t\treturn;\r\n\t}\r\n\r\n\tprecache_model2 (\"progs/oldone.mdl\");\r\n\r\n\tprecache_sound2 (\"boss2/death.wav\");\r\n\tprecache_sound2 (\"boss2/idle.wav\");\r\n\tprecache_sound2 (\"boss2/sight.wav\");\r\n\tprecache_sound2 (\"boss2/pop2.wav\");\r\n\r\n\tself.solid = SOLID_SLIDEBOX;\r\n\tself.movetype = MOVETYPE_STEP;\r\n\t\r\n\tsetmodel (self, \"progs/oldone.mdl\");\r\n\tsetsize (self, '-160 -128 -24', '160 128 256');\r\n\r\n\tself.health = 40000;\t\t// kill by telefrag\r\n\tself.think = old_idle1;\r\n\tself.nextthink = time + 0.1;\t\r\n\tself.takedamage = DAMAGE_YES;\r\n\tself.th_pain = nopain;\r\n\tself.th_die = finale_1;\r\n\tshub = self;\r\n\t\r\n\ttotal_monsters = total_monsters + 1;\r\n};\r\n\r\n"
  },
  {
    "path": "quakec/csqctest/src/ss/plats.qc",
    "content": "\r\n\r\nvoid() plat_center_touch;\r\nvoid() plat_outside_touch;\r\nvoid() plat_trigger_use;\r\nvoid() plat_go_up;\r\nvoid() plat_go_down;\r\nvoid() plat_crush;\r\nfloat PLAT_LOW_TRIGGER = 1;\r\n\r\nvoid() plat_spawn_inside_trigger =\r\n{\r\n\tlocal entity\ttrigger;\r\n\tlocal vector\ttmin, tmax;\r\n\r\n//\r\n// middle trigger\r\n//\t\r\n\ttrigger = spawn();\r\n\ttrigger.touch = plat_center_touch;\r\n\ttrigger.movetype = MOVETYPE_NONE;\r\n\ttrigger.solid = SOLID_TRIGGER;\r\n\ttrigger.enemy = self;\r\n\t\r\n\ttmin = self.mins + '25 25 0';\r\n\ttmax = self.maxs - '25 25 -8';\r\n\ttmin_z = tmax_z - (self.pos1_z - self.pos2_z + 8);\r\n\tif (self.spawnflags & PLAT_LOW_TRIGGER)\r\n\t\ttmax_z = tmin_z + 8;\r\n\t\r\n\tif (self.size_x <= 50)\r\n\t{\r\n\t\ttmin_x = (self.mins_x + self.maxs_x) / 2;\r\n\t\ttmax_x = tmin_x + 1;\r\n\t}\r\n\tif (self.size_y <= 50)\r\n\t{\r\n\t\ttmin_y = (self.mins_y + self.maxs_y) / 2;\r\n\t\ttmax_y = tmin_y + 1;\r\n\t}\r\n\t\r\n\tsetsize (trigger, tmin, tmax);\r\n};\r\n\r\nvoid() plat_hit_top =\r\n{\r\n\tsound (self, CHAN_VOICE, self.noise1, 1, ATTN_NORM);\r\n\tself.state = STATE_TOP;\r\n\tself.think = plat_go_down;\r\n\tself.nextthink = self.ltime + 3;\r\n};\r\n\r\nvoid() plat_hit_bottom =\r\n{\r\n\tsound (self, CHAN_VOICE, self.noise1, 1, ATTN_NORM);\r\n\tself.state = STATE_BOTTOM;\r\n};\r\n\r\nvoid() plat_go_down =\r\n{\r\n\tsound (self, CHAN_VOICE, self.noise, 1, ATTN_NORM);\r\n\tself.state = STATE_DOWN;\r\n\tSUB_CalcMove (self.pos2, self.speed, plat_hit_bottom);\r\n};\r\n\r\nvoid() plat_go_up =\r\n{\r\n\tsound (self, CHAN_VOICE, self.noise, 1, ATTN_NORM);\r\n\tself.state = STATE_UP;\r\n\tSUB_CalcMove (self.pos1, self.speed, plat_hit_top);\r\n};\r\n\r\nvoid() plat_center_touch =\r\n{\r\n\tif (other.classname != \"player\")\r\n\t\treturn;\r\n\t\t\r\n\tif (other.health <= 0)\r\n\t\treturn;\r\n\r\n\tself = self.enemy;\r\n\tif (self.state == STATE_BOTTOM)\r\n\t\tplat_go_up ();\r\n\telse if (self.state == STATE_TOP)\r\n\t\tself.nextthink = self.ltime + 1;\t// delay going down\r\n};\r\n\r\nvoid() plat_outside_touch =\r\n{\r\n\tif (other.classname != \"player\")\r\n\t\treturn;\r\n\r\n\tif (other.health <= 0)\r\n\t\treturn;\r\n\t\t\r\n//dprint (\"plat_outside_touch\\n\");\r\n\tself = self.enemy;\r\n\tif (self.state == STATE_TOP)\r\n\t\tplat_go_down ();\r\n};\r\n\r\nvoid() plat_trigger_use =\r\n{\r\n\tif (self.think)\r\n\t\treturn;\t\t// allready activated\r\n\tplat_go_down();\r\n};\r\n\r\n\r\nvoid() plat_crush =\r\n{\r\n//dprint (\"plat_crush\\n\");\r\n\r\n\tT_Damage (other, self, self, 1);\r\n\t\r\n\tif (self.state == STATE_UP)\r\n\t\tplat_go_down ();\r\n\telse if (self.state == STATE_DOWN)\r\n\t\tplat_go_up ();\r\n\telse\r\n\t\tobjerror (\"plat_crush: bad self.state\\n\");\r\n};\r\n\r\nvoid() plat_use =\r\n{\r\n\tself.use = SUB_Null;\r\n\tif (self.state != STATE_UP)\r\n\t\tobjerror (\"plat_use: not in up state\");\r\n\tplat_go_down();\r\n};\r\n\r\n\r\n/*QUAKED func_plat (0 .5 .8) ? PLAT_LOW_TRIGGER\r\nspeed\tdefault 150\r\n\r\nPlats are always drawn in the extended position, so they will light correctly.\r\n\r\nIf the plat is the target of another trigger or button, it will start out disabled in the extended position until it is trigger, when it will lower and become a normal plat.\r\n\r\nIf the \"height\" key is set, that will determine the amount the plat moves, instead of being implicitly determined by the model's height.\r\nSet \"sounds\" to one of the following:\r\n1) base fast\r\n2) chain slow\r\n*/\r\n\r\n\r\nvoid() func_plat =\r\n{\r\n\tif (!self.t_length)\r\n\t\tself.t_length = 80;\r\n\tif (!self.t_width)\r\n\t\tself.t_width = 10;\r\n\r\n\tif (self.sounds == 0)\r\n\t\tself.sounds = 2;\r\n// FIX THIS TO LOAD A GENERIC PLAT SOUND\r\n\r\n\tif (self.sounds == 1)\r\n\t{\r\n\t\tprecache_sound (\"plats/plat1.wav\");\r\n\t\tprecache_sound (\"plats/plat2.wav\");\r\n\t\tself.noise = \"plats/plat1.wav\";\r\n\t\tself.noise1 = \"plats/plat2.wav\";\r\n\t}\r\n\r\n\tif (self.sounds == 2)\r\n\t{\r\n\t\tprecache_sound (\"plats/medplat1.wav\");\r\n\t\tprecache_sound (\"plats/medplat2.wav\");\r\n\t\tself.noise = \"plats/medplat1.wav\";\r\n\t\tself.noise1 = \"plats/medplat2.wav\";\r\n\t}\r\n\r\n\r\n\tself.mangle = self.angles;\r\n\tself.angles = '0 0 0';\r\n\r\n\tself.classname = \"plat\";\r\n\tself.solid = SOLID_BSP;\r\n\tself.movetype = MOVETYPE_PUSH;\r\n\tsetorigin (self, self.origin);\t\r\n\tsetmodel (self, self.model);\r\n\tsetsize (self, self.mins , self.maxs);\r\n\r\n\tself.blocked = plat_crush;\r\n\tif (!self.speed)\r\n\t\tself.speed = 150;\r\n\r\n// pos1 is the top position, pos2 is the bottom\r\n\tself.pos1 = self.origin;\r\n\tself.pos2 = self.origin;\r\n\tif (self.height)\r\n\t\tself.pos2_z = self.origin_z - self.height;\r\n\telse\r\n\t\tself.pos2_z = self.origin_z - self.size_z + 8;\r\n\r\n\tself.use = plat_trigger_use;\r\n\r\n\tplat_spawn_inside_trigger ();\t// the \"start moving\" trigger\t\r\n\r\n\tif (self.targetname!=\"\")\r\n\t{\r\n\t\tself.state = STATE_UP;\r\n\t\tself.use = plat_use;\r\n\t}\r\n\telse\r\n\t{\r\n\t\tsetorigin (self, self.pos2);\r\n\t\tself.state = STATE_BOTTOM;\r\n\t}\r\n};\r\n\r\n//============================================================================\r\n\r\nvoid() train_next;\r\nvoid() func_train_find;\r\n\r\nvoid() train_blocked =\r\n{\r\n\tif (time < self.attack_finished)\r\n\t\treturn;\r\n\tself.attack_finished = time + 0.5;\r\n\tT_Damage (other, self, self, self.dmg);\r\n};\r\nvoid() train_use =\r\n{\r\n\tif (self.think != func_train_find)\r\n\t\treturn;\t\t// already activated\r\n\ttrain_next();\r\n};\r\n\r\nvoid() train_wait =\r\n{\r\n\tif (self.wait)\r\n\t{\r\n\t\tself.nextthink = self.ltime + self.wait;\r\n\t\tsound (self, CHAN_VOICE, self.noise, 1, ATTN_NORM);\r\n\t}\r\n\telse\r\n\t\tself.nextthink = self.ltime + 0.1;\r\n\t\r\n\tself.think = train_next;\r\n};\r\n\r\nvoid() train_next =\r\n{\r\n\tlocal entity\ttarg;\r\n\r\n\ttarg = find (world, targetname, self.target);\r\n\tself.target = targ.target;\r\n\tif (!self.target)\r\n\t\tobjerror (\"train_next: no next target\");\r\n\tif (targ.wait)\r\n\t\tself.wait = targ.wait;\r\n\telse\r\n\t\tself.wait = 0;\r\n\tsound (self, CHAN_VOICE, self.noise1, 1, ATTN_NORM);\r\n\tSUB_CalcMove (targ.origin - self.mins, self.speed, train_wait);\r\n};\r\n\r\nvoid() func_train_find =\r\n\r\n{\r\n\tlocal entity\ttarg;\r\n\r\n\ttarg = find (world, targetname, self.target);\r\n\tself.target = targ.target;\r\n\tsetorigin (self, targ.origin - self.mins);\r\n\tif (!self.targetname)\r\n\t{\t// not triggered, so start immediately\r\n\t\tself.nextthink = self.ltime + 0.1;\r\n\t\tself.think = train_next;\r\n\t}\r\n};\r\n\r\n/*QUAKED func_train (0 .5 .8) ?\r\nTrains are moving platforms that players can ride.\r\nThe targets origin specifies the min point of the train at each corner.\r\nThe train spawns at the first target it is pointing at.\r\nIf the train is the target of a button or trigger, it will not begin moving until activated.\r\nspeed\tdefault 100\r\ndmg\t\tdefault\t2\r\nsounds\r\n1) ratchet metal\r\n\r\n*/\r\nvoid() func_train =\r\n{\t\r\n\tif (!self.speed)\r\n\t\tself.speed = 100;\r\n\tif (!self.target)\r\n\t\tobjerror (\"func_train without a target\");\r\n\tif (!self.dmg)\r\n\t\tself.dmg = 2;\r\n\r\n\tif (self.sounds == 0)\r\n\t{\r\n\t\tself.noise = (\"misc/null.wav\");\r\n\t\tprecache_sound (\"misc/null.wav\");\r\n\t\tself.noise1 = (\"misc/null.wav\");\r\n\t\tprecache_sound (\"misc/null.wav\");\r\n\t}\r\n\r\n\tif (self.sounds == 1)\r\n\t{\r\n\t\tself.noise = (\"plats/train2.wav\");\r\n\t\tprecache_sound (\"plats/train2.wav\");\r\n\t\tself.noise1 = (\"plats/train1.wav\");\r\n\t\tprecache_sound (\"plats/train1.wav\");\r\n\t}\r\n\r\n\tself.cnt = 1;\r\n\tself.solid = SOLID_BSP;\r\n\tself.movetype = MOVETYPE_PUSH;\r\n\tself.blocked = train_blocked;\r\n\tself.use = train_use;\r\n\tself.classname = \"train\";\r\n\r\n\tsetmodel (self, self.model);\r\n\tsetsize (self, self.mins , self.maxs);\r\n\tsetorigin (self, self.origin);\r\n\r\n// start trains on the second frame, to make sure their targets have had\r\n// a chance to spawn\r\n\tself.nextthink = self.ltime + 0.1;\r\n\tself.think = func_train_find;\r\n};\r\n\r\n/*QUAKED misc_teleporttrain (0 .5 .8) (-8 -8 -8) (8 8 8)\r\nThis is used for the final bos\r\n*/\r\nvoid() misc_teleporttrain =\r\n{\t\r\n\tif (!self.speed)\r\n\t\tself.speed = 100;\r\n\tif (!self.target)\r\n\t\tobjerror (\"func_train without a target\");\r\n\r\n\tself.cnt = 1;\r\n\tself.solid = SOLID_NOT;\r\n\tself.movetype = MOVETYPE_PUSH;\r\n\tself.blocked = train_blocked;\r\n\tself.use = train_use;\r\n\tself.avelocity = '100 200 300';\r\n\r\n\tself.noise = (\"misc/null.wav\");\r\n\tprecache_sound (\"misc/null.wav\");\r\n\tself.noise1 = (\"misc/null.wav\");\r\n\tprecache_sound (\"misc/null.wav\");\r\n\r\n\tprecache_model2 (\"progs/teleport.mdl\");\r\n\tsetmodel (self, \"progs/teleport.mdl\");\r\n\tsetsize (self, self.mins , self.maxs);\r\n\tsetorigin (self, self.origin);\r\n\r\n// start trains on the second frame, to make sure their targets have had\r\n// a chance to spawn\r\n\tself.nextthink = self.ltime + 0.1;\r\n\tself.think = func_train_find;\r\n};\r\n\r\n"
  },
  {
    "path": "quakec/csqctest/src/ss/player.qc",
    "content": "\r\nvoid() bubble_bob;\r\n\r\n/*\r\n==============================================================================\r\n\r\nPLAYER\r\n\r\n==============================================================================\r\n*/\r\n\r\n$cd id1/models/player_4\r\n$origin 0 -6 24\r\n$base base\t\t\r\n$skin skin\r\n\r\n//\r\n// running\r\n//\r\n$frame axrun1 axrun2 axrun3 axrun4 axrun5 axrun6\r\n\r\n$frame rockrun1 rockrun2 rockrun3 rockrun4 rockrun5 rockrun6\r\n\r\n//\r\n// standing\r\n//\r\n$frame stand1 stand2 stand3 stand4 stand5\r\n\r\n$frame axstnd1 axstnd2 axstnd3 axstnd4 axstnd5 axstnd6\r\n$frame axstnd7 axstnd8 axstnd9 axstnd10 axstnd11 axstnd12\r\n\r\n\r\n//\r\n// pain\r\n//\r\n$frame axpain1 axpain2 axpain3 axpain4 axpain5 axpain6\r\n\r\n$frame pain1 pain2 pain3 pain4 pain5 pain6\r\n\r\n\r\n//\r\n// death\r\n//\r\n\r\n$frame axdeth1 axdeth2 axdeth3 axdeth4 axdeth5 axdeth6\r\n$frame axdeth7 axdeth8 axdeth9\r\n\r\n$frame deatha1 deatha2 deatha3 deatha4 deatha5 deatha6 deatha7 deatha8\r\n$frame deatha9 deatha10 deatha11\r\n\r\n$frame deathb1 deathb2 deathb3 deathb4 deathb5 deathb6 deathb7 deathb8\r\n$frame deathb9\r\n\r\n$frame deathc1 deathc2 deathc3 deathc4 deathc5 deathc6 deathc7 deathc8\r\n$frame deathc9 deathc10 deathc11 deathc12 deathc13 deathc14 deathc15\r\n\r\n$frame deathd1 deathd2 deathd3 deathd4 deathd5 deathd6 deathd7\r\n$frame deathd8 deathd9\r\n\r\n$frame deathe1 deathe2 deathe3 deathe4 deathe5 deathe6 deathe7\r\n$frame deathe8 deathe9\r\n\r\n//\r\n// attacks\r\n//\r\n$frame nailatt1 nailatt2\r\n\r\n$frame light1 light2\r\n\r\n$frame rockatt1 rockatt2 rockatt3 rockatt4 rockatt5 rockatt6\r\n\r\n$frame shotatt1 shotatt2 shotatt3 shotatt4 shotatt5 shotatt6\r\n\r\n$frame axatt1 axatt2 axatt3 axatt4 axatt5 axatt6\r\n\r\n$frame axattb1 axattb2 axattb3 axattb4 axattb5 axattb6\r\n\r\n$frame axattc1 axattc2 axattc3 axattc4 axattc5 axattc6\r\n\r\n$frame axattd1 axattd2 axattd3 axattd4 axattd5 axattd6\r\n\r\n\r\n/*\r\n==============================================================================\r\nPLAYER\r\n==============================================================================\r\n*/\r\n\r\nvoid() player_run;\r\n\r\nvoid()\tplayer_stand1 =[\t$axstnd1,\tplayer_stand1\t]\r\n{\r\n\tself.weaponframe=0;\r\n\tif (self.velocity_x || self.velocity_y)\r\n\t{\r\n\t\tself.walkframe=0;\r\n\t\tplayer_run();\r\n\t\treturn;\r\n\t}\r\n\r\n\tif (self.weapon == IT_AXE)\r\n\t{\r\n\t\tif (self.walkframe >= 12)\r\n\t\t\tself.walkframe = 0;\r\n\t\tself.frame = $axstnd1 + self.walkframe;\r\n\t}\r\n\telse\r\n\t{\r\n\t\tif (self.walkframe >= 5)\r\n\t\t\tself.walkframe = 0;\r\n\t\tself.frame = $stand1 + self.walkframe;\r\n\t}\r\n\tself.walkframe = self.walkframe + 1;\t\r\n};\r\n\r\nvoid()\tplayer_run =[\t$rockrun1,\tplayer_run\t]\r\n{\r\n\tself.weaponframe=0;\r\n\tif (!self.velocity_x && !self.velocity_y)\r\n\t{\r\n\t\tself.walkframe=0;\r\n\t\tplayer_stand1();\r\n\t\treturn;\r\n\t}\r\n\r\n\tif (self.weapon == IT_AXE)\r\n\t{\r\n\t\tif (self.walkframe == 6)\r\n\t\t\tself.walkframe = 0;\r\n\t\tself.frame = $axrun1 + self.walkframe;\r\n\t}\r\n\telse\r\n\t{\r\n\t\tif (self.walkframe == 6)\r\n\t\t\tself.walkframe = 0;\r\n\t\tself.frame = self.frame + self.walkframe;\r\n\t}\r\n\tself.walkframe = self.walkframe + 1;\r\n};\r\n\r\n\r\nvoid()\tplayer_shot1 =\t[$shotatt1, player_shot2\t] {self.weaponframe=1;\r\nself.effects = self.effects | EF_MUZZLEFLASH;};\r\nvoid()\tplayer_shot2 =\t[$shotatt2, player_shot3\t] {self.weaponframe=2;};\r\nvoid()\tplayer_shot3 =\t[$shotatt3, player_shot4\t] {self.weaponframe=3;};\r\nvoid()\tplayer_shot4 =\t[$shotatt4, player_shot5\t] {self.weaponframe=4;};\r\nvoid()\tplayer_shot5 =\t[$shotatt5, player_shot6\t] {self.weaponframe=5;};\r\nvoid()\tplayer_shot6 =\t[$shotatt6, player_run\t] {self.weaponframe=6;};\r\n\r\nvoid()\tplayer_axe1 =\t[$axatt1, player_axe2\t] {self.weaponframe=1;};\r\nvoid()\tplayer_axe2 =\t[$axatt2, player_axe3\t] {self.weaponframe=2;};\r\nvoid()\tplayer_axe3 =\t[$axatt3, player_axe4\t] {self.weaponframe=3;W_FireAxe();};\r\nvoid()\tplayer_axe4 =\t[$axatt4, player_run\t] {self.weaponframe=4;};\r\n\r\nvoid()\tplayer_axeb1 =\t[$axattb1, player_axeb2\t] {self.weaponframe=5;};\r\nvoid()\tplayer_axeb2 =\t[$axattb2, player_axeb3\t] {self.weaponframe=6;};\r\nvoid()\tplayer_axeb3 =\t[$axattb3, player_axeb4\t] {self.weaponframe=7;W_FireAxe();};\r\nvoid()\tplayer_axeb4 =\t[$axattb4, player_run\t] {self.weaponframe=8;};\r\n\r\nvoid()\tplayer_axec1 =\t[$axattc1, player_axec2\t] {self.weaponframe=1;};\r\nvoid()\tplayer_axec2 =\t[$axattc2, player_axec3\t] {self.weaponframe=2;};\r\nvoid()\tplayer_axec3 =\t[$axattc3, player_axec4\t] {self.weaponframe=3;W_FireAxe();};\r\nvoid()\tplayer_axec4 =\t[$axattc4, player_run\t] {self.weaponframe=4;};\r\n\r\nvoid()\tplayer_axed1 =\t[$axattd1, player_axed2\t] {self.weaponframe=5;};\r\nvoid()\tplayer_axed2 =\t[$axattd2, player_axed3\t] {self.weaponframe=6;};\r\nvoid()\tplayer_axed3 =\t[$axattd3, player_axed4\t] {self.weaponframe=7;W_FireAxe();};\r\nvoid()\tplayer_axed4 =\t[$axattd4, player_run\t] {self.weaponframe=8;};\r\n\r\n\r\n//============================================================================\r\n\r\nvoid() player_nail1   =[$nailatt1, player_nail2  ] \r\n{\r\n\tself.effects = self.effects | EF_MUZZLEFLASH;\r\n\r\n\tif (!self.button0)\r\n\t\t{player_run ();return;}\r\n\tself.weaponframe = self.weaponframe + 1;\r\n\tif (self.weaponframe == 9)\r\n\t\tself.weaponframe = 1;\r\n\tSuperDamageSound();\r\n\tW_FireSpikes (4);\r\n\tself.attack_finished = time + 0.2;\r\n};\r\nvoid() player_nail2   =[$nailatt2, player_nail1  ]\r\n{\r\n\tself.effects = self.effects | EF_MUZZLEFLASH;\r\n\r\n\tif (!self.button0)\r\n\t\t{player_run ();return;}\r\n\tself.weaponframe = self.weaponframe + 1;\r\n\tif (self.weaponframe == 9)\r\n\t\tself.weaponframe = 1;\r\n\tSuperDamageSound();\r\n\tW_FireSpikes (-4);\r\n\tself.attack_finished = time + 0.2;\r\n};\r\n\r\n//============================================================================\r\n\r\nvoid() player_light1   =[$light1, player_light2  ] \r\n{\r\n\tself.effects = self.effects | EF_MUZZLEFLASH;\r\n\r\n\tif (!self.button0)\r\n\t\t{player_run ();return;}\r\n\tself.weaponframe = self.weaponframe + 1;\r\n\tif (self.weaponframe == 5)\r\n\t\tself.weaponframe = 1;\r\n\tSuperDamageSound();\r\n\tW_FireLightning();\r\n\tself.attack_finished = time + 0.2;\r\n};\r\nvoid() player_light2   =[$light2, player_light1  ]\r\n{\r\n\tself.effects = self.effects | EF_MUZZLEFLASH;\r\n\r\n\tif (!self.button0)\r\n\t\t{player_run ();return;}\r\n\tself.weaponframe = self.weaponframe + 1;\r\n\tif (self.weaponframe == 5)\r\n\t\tself.weaponframe = 1;\r\n\tSuperDamageSound();\r\n\tW_FireLightning();\r\n\tself.attack_finished = time + 0.2;\r\n};\r\n\r\n//============================================================================\r\n\r\n\r\nvoid() player_rocket1   =[$rockatt1, player_rocket2  ] {self.weaponframe=1;\r\nself.effects = self.effects | EF_MUZZLEFLASH;};\r\nvoid() player_rocket2   =[$rockatt2, player_rocket3  ] {self.weaponframe=2;};\r\nvoid() player_rocket3   =[$rockatt3, player_rocket4  ] {self.weaponframe=3;};\r\nvoid() player_rocket4   =[$rockatt4, player_rocket5  ] {self.weaponframe=4;};\r\nvoid() player_rocket5   =[$rockatt5, player_rocket6  ] {self.weaponframe=5;};\r\nvoid() player_rocket6   =[$rockatt6, player_run  ] {self.weaponframe=6;};\r\nvoid(float num_bubbles) DeathBubbles;\r\n\r\nvoid() PainSound =\r\n{\r\nlocal float\t\trs;\r\n\r\n\tif (self.health < 0)\r\n\t\treturn;\r\n\r\n\tif (damage_attacker.classname == \"teledeath\")\r\n\t{\r\n\t\tsound (self, CHAN_VOICE, \"player/teledth1.wav\", 1, ATTN_NONE);\r\n\t\treturn;\r\n\t}\r\n\r\n// water pain sounds\r\n\tif (self.watertype == CONTENT_WATER && self.waterlevel == 3)\r\n\t{\r\n\t\tDeathBubbles(1);\r\n\t\tif (random() > 0.5)\r\n\t\t\tsound (self, CHAN_VOICE, \"player/drown1.wav\", 1, ATTN_NORM);\r\n\t\telse\r\n\t\t\tsound (self, CHAN_VOICE, \"player/drown2.wav\", 1, ATTN_NORM);\r\n\t\treturn;\r\n\t}\r\n\r\n// slime pain sounds\r\n\tif (self.watertype == CONTENT_SLIME)\r\n\t{\r\n// FIX ME\tput in some steam here\r\n\t\tif (random() > 0.5)\r\n\t\t\tsound (self, CHAN_VOICE, \"player/lburn1.wav\", 1, ATTN_NORM);\r\n\t\telse\r\n\t\t\tsound (self, CHAN_VOICE, \"player/lburn2.wav\", 1, ATTN_NORM);\r\n\t\treturn;\r\n\t}\r\n\r\n\tif (self.watertype == CONTENT_LAVA)\r\n\t{\r\n\t\tif (random() > 0.5)\r\n\t\t\tsound (self, CHAN_VOICE, \"player/lburn1.wav\", 1, ATTN_NORM);\r\n\t\telse\r\n\t\t\tsound (self, CHAN_VOICE, \"player/lburn2.wav\", 1, ATTN_NORM);\r\n\t\treturn;\r\n\t}\r\n\r\n\tif (self.pain_finished > time)\r\n\t{\r\n\t\tself.axhitme = 0;\r\n\t\treturn;\r\n\t}\r\n\tself.pain_finished = time + 0.5;\r\n\r\n// don't make multiple pain sounds right after each other\r\n\r\n// ax pain sound\r\n\tif (self.axhitme == 1)\r\n\t{\r\n\t\tself.axhitme = 0;\r\n\t\tsound (self, CHAN_VOICE, \"player/axhit1.wav\", 1, ATTN_NORM);\r\n\t\treturn;\r\n\t}\r\n\t\r\n\r\n\trs = rint((random() * 5) + 1);\r\n\r\n\tself.noise = \"\";\r\n\tif (rs == 1)\r\n\t\tself.noise = \"player/pain1.wav\";\r\n\telse if (rs == 2)\r\n\t\tself.noise = \"player/pain2.wav\";\r\n\telse if (rs == 3)\r\n\t\tself.noise = \"player/pain3.wav\";\r\n\telse if (rs == 4)\r\n\t\tself.noise = \"player/pain4.wav\";\r\n\telse if (rs == 5)\r\n\t\tself.noise = \"player/pain5.wav\";\r\n\telse\r\n\t\tself.noise = \"player/pain6.wav\";\r\n\r\n\tsound (self, CHAN_VOICE, self.noise, 1, ATTN_NORM);\r\n\treturn;\r\n};\r\n\r\nvoid()\tplayer_pain1 =\t[\t$pain1,\tplayer_pain2\t] {PainSound();self.weaponframe=0;};\r\nvoid()\tplayer_pain2 =\t[\t$pain2,\tplayer_pain3\t] {};\r\nvoid()\tplayer_pain3 =\t[\t$pain3,\tplayer_pain4\t] {};\r\nvoid()\tplayer_pain4 =\t[\t$pain4,\tplayer_pain5\t] {};\r\nvoid()\tplayer_pain5 =\t[\t$pain5,\tplayer_pain6\t] {};\r\nvoid()\tplayer_pain6 =\t[\t$pain6,\tplayer_run\t] {};\r\n\r\nvoid()\tplayer_axpain1 =\t[\t$axpain1,\tplayer_axpain2\t] {PainSound();self.weaponframe=0;};\r\nvoid()\tplayer_axpain2 =\t[\t$axpain2,\tplayer_axpain3\t] {};\r\nvoid()\tplayer_axpain3 =\t[\t$axpain3,\tplayer_axpain4\t] {};\r\nvoid()\tplayer_axpain4 =\t[\t$axpain4,\tplayer_axpain5\t] {};\r\nvoid()\tplayer_axpain5 =\t[\t$axpain5,\tplayer_axpain6\t] {};\r\nvoid()\tplayer_axpain6 =\t[\t$axpain6,\tplayer_run\t] {};\r\n\r\nvoid(entity attacker, float damage) player_pain =\r\n{\r\n\tif (self.weaponframe)\r\n\t\treturn;\r\n\r\n\tif (self.invisible_finished > time)\r\n\t\treturn;\t\t// eyes don't have pain frames\r\n\r\n\tif (self.weapon == IT_AXE)\r\n\t\tplayer_axpain1 ();\r\n\telse\r\n\t\tplayer_pain1 ();\r\n};\r\n\r\nvoid() player_diea1;\r\nvoid() player_dieb1;\r\nvoid() player_diec1;\r\nvoid() player_died1;\r\nvoid() player_diee1;\r\nvoid() player_die_ax1;\r\n\r\nvoid() DeathBubblesSpawn =\r\n{\r\nlocal entity\tbubble;\r\n\tif (self.owner.waterlevel != 3)\r\n\t\treturn;\r\n\tbubble = spawn();\r\n\tsetmodel (bubble, \"progs/s_bubble.spr\");\r\n\tsetorigin (bubble, self.owner.origin + '0 0 24');\r\n\tbubble.movetype = MOVETYPE_NOCLIP;\r\n\tbubble.solid = SOLID_NOT;\r\n\tbubble.velocity = '0 0 15';\r\n\tbubble.nextthink = time + 0.5;\r\n\tbubble.think = bubble_bob;\r\n\tbubble.classname = \"bubble\";\r\n\tbubble.frame = 0;\r\n\tbubble.cnt = 0;\r\n\tsetsize (bubble, '-8 -8 -8', '8 8 8');\r\n\tself.nextthink = time + 0.1;\r\n\tself.think = DeathBubblesSpawn;\r\n\tself.air_finished = self.air_finished + 1;\r\n\tif (self.air_finished >= self.bubble_count)\r\n\t\tremove(self);\r\n};\r\n\r\nvoid(float num_bubbles) DeathBubbles =\r\n{\r\nlocal entity\tbubble_spawner;\r\n\t\r\n\tbubble_spawner = spawn();\r\n\tsetorigin (bubble_spawner, self.origin);\r\n\tbubble_spawner.movetype = MOVETYPE_NONE;\r\n\tbubble_spawner.solid = SOLID_NOT;\r\n\tbubble_spawner.nextthink = time + 0.1;\r\n\tbubble_spawner.think = DeathBubblesSpawn;\r\n\tbubble_spawner.air_finished = 0;\r\n\tbubble_spawner.owner = self;\r\n\tbubble_spawner.bubble_count = num_bubbles;\r\n\treturn;\r\n};\r\n\r\n\r\nvoid() DeathSound =\r\n{\r\nlocal float\t\trs;\r\n\r\n\t// water death sounds\r\n\tif (self.waterlevel == 3)\r\n\t{\r\n\t\tDeathBubbles(20);\r\n\t\tsound (self, CHAN_VOICE, \"player/h2odeath.wav\", 1, ATTN_NONE);\r\n\t\treturn;\r\n\t}\r\n\t\r\n\trs = rint ((random() * 4) + 1);\r\n\tif (rs == 1)\r\n\t\tself.noise = \"player/death1.wav\";\r\n\tif (rs == 2)\r\n\t\tself.noise = \"player/death2.wav\";\r\n\tif (rs == 3)\r\n\t\tself.noise = \"player/death3.wav\";\r\n\tif (rs == 4)\r\n\t\tself.noise = \"player/death4.wav\";\r\n\tif (rs == 5)\r\n\t\tself.noise = \"player/death5.wav\";\r\n\r\n\tsound (self, CHAN_VOICE, self.noise, 1, ATTN_NONE);\r\n\treturn;\r\n};\r\n\r\n\r\nvoid() PlayerDead =\r\n{\r\n\tself.nextthink = -1;\r\n// allow respawn after a certain time\r\n\tself.deadflag = DEAD_DEAD;\r\n};\r\n\r\nvector(float dm) VelocityForDamage =\r\n{\r\n\tlocal vector v;\r\n\r\n\tv_x = 100 * crandom();\r\n\tv_y = 100 * crandom();\r\n\tv_z = 200 + 100 * random();\r\n\r\n\tif (dm > -50)\r\n\t{\r\n//\t\tdprint (\"level 1\\n\");\r\n\t\tv = v * 0.7;\r\n\t}\r\n\telse if (dm > -200)\r\n\t{\r\n//\t\tdprint (\"level 3\\n\");\r\n\t\tv = v * 2;\r\n\t}\r\n\telse\r\n\t\tv = v * 10;\r\n\r\n\treturn v;\r\n};\r\n\r\nfloat(entity ent, float fl) NotInCSQC =\r\n{\r\n\treturn FALSE;\r\n};\r\n\r\nvoid(string gibname, float dm) ThrowGib =\r\n{\r\n\tlocal\tentity new;\r\n\r\n\tnew = spawn();\r\n\tnew.origin = self.origin;\r\n\tsetmodel (new, gibname);\r\n\tsetsize (new, '0 0 0', '0 0 0');\r\n\tnew.velocity = VelocityForDamage (dm);\r\n\tnew.movetype = MOVETYPE_BOUNCE;\r\n\tnew.solid = SOLID_NOT;\r\n\tnew.avelocity_x = random()*600;\r\n\tnew.avelocity_y = random()*600;\r\n\tnew.avelocity_z = random()*600;\r\n\tnew.think = SUB_Remove;\r\n\tnew.ltime = time;\r\n\tnew.nextthink = time + 10 + random()*10;\r\n\tnew.frame = 0;\r\n\tnew.flags = 0;\r\n\r\n\tself.SendEntity = NotInCSQC;\r\n};\r\n\r\n//fixme: remove\r\nvoid(string gibname, float dm) ThrowHead =\r\n{\r\n\tsetmodel (self, gibname);\r\n\tself.frame = 0;\r\n\tself.nextthink = -1;\r\n\tself.movetype = MOVETYPE_BOUNCE;\r\n\tself.takedamage = DAMAGE_NO;\r\n\tself.solid = SOLID_NOT;\r\n\tself.view_ofs = '0 0 8';\r\n\tsetsize (self, '-16 -16 0', '16 16 56');\r\n\tself.velocity = VelocityForDamage (dm);\r\n\tself.origin_z = self.origin_z - 24;\r\n\tself.flags = self.flags - (self.flags & FL_ONGROUND);\r\n\tself.avelocity = crandom() * '0 600 0';\r\n};\r\n\r\nfloat(entity ent, float fl) WriteGibs =\r\n{\r\n\tWriteByte(MSG_ENTITY, CLASS_GIB);\r\n\tWriteByte(MSG_ENTITY, self.cnt);\r\n\tWriteByte(MSG_ENTITY, -self.health);\r\n\tWriteCoord(MSG_ENTITY, self.origin_x);\r\n\tWriteCoord(MSG_ENTITY, self.origin_y);\r\n\tWriteCoord(MSG_ENTITY, self.origin_z);\r\n\treturn TRUE;\r\n};\r\n\r\nvoid(float type) ThrowCSQCGibs =\r\n{\r\n\tself.cnt = type;\r\n\tself.SendFlags = FULLSEND;\r\n\tself.SendEntity = WriteGibs;\r\n\tif (type != GIB_PLAYER)\r\n\t{\t//schedule removal after a second.\r\n\t\tself.think = SUB_Remove;\r\n\t\tself.nextthink = time+1;\r\n\t}\t\r\n\r\n\tswitch(type)\r\n\t{\r\n\tcase GIB_PLAYER:\r\n\t\tThrowGib (\"progs/h_player.mdl\", self.health);\r\n\t\tbreak;\r\n\tcase GIB_SOLDIER:\r\n\t\tThrowGib (\"progs/h_guard.mdl\", self.health);\r\n\t\tbreak;\r\n\t}\r\n\tThrowGib (\"progs/gib1.mdl\", self.health);\r\n\tThrowGib (\"progs/gib2.mdl\", self.health);\r\n\tThrowGib (\"progs/gib3.mdl\", self.health);\r\n};\r\n\r\n\r\nvoid() GibPlayer =\r\n{\r\n\tThrowCSQCGibs(0);\r\n\r\n\tself.deadflag = DEAD_DEAD;\r\n\r\n\tif (damage_attacker.classname == \"teledeath\")\r\n\t{\r\n\t\tsound (self, CHAN_VOICE, \"player/teledth1.wav\", 1, ATTN_NONE);\r\n\t\treturn;\r\n\t}\r\n\r\n\tif (damage_attacker.classname == \"teledeath2\")\r\n\t{\r\n\t\tsound (self, CHAN_VOICE, \"player/teledth1.wav\", 1, ATTN_NONE);\r\n\t\treturn;\r\n\t}\r\n\t\t\r\n\tif (random() < 0.5)\r\n\t\tsound (self, CHAN_VOICE, \"player/gib.wav\", 1, ATTN_NONE);\r\n\telse\r\n\t\tsound (self, CHAN_VOICE, \"player/udeath.wav\", 1, ATTN_NONE);\r\n};\r\n\r\nvoid() PlayerDie =\r\n{\r\n\tlocal\tfloat\ti;\r\n\t\r\n\tself.items = self.items - (self.items & IT_INVISIBILITY);\r\n\tself.invisible_finished = 0;\t// don't die as eyes\r\n\tself.invincible_finished = 0;\r\n\tself.super_damage_finished = 0;\r\n\tself.radsuit_finished = 0;\r\n\tself.modelindex = modelindex_player;\t// don't use eyes\r\n\r\n\tif (deathmatch || coop)\r\n\t\tDropBackpack();\r\n\r\n\tself.weaponmodel=\"\";\r\n\tself.view_ofs = '0 0 -8';\r\n\tself.deadflag = DEAD_DYING;\r\n\tself.solid = SOLID_NOT;\r\n\tself.flags = self.flags - (self.flags & FL_ONGROUND);\r\n\tself.movetype = MOVETYPE_TOSS;\r\n\tif (self.velocity_z < 10)\r\n\t\tself.velocity_z = self.velocity_z + random()*300;\r\n\r\n\tif (self.health < -40)\r\n\t{\r\n\t\tGibPlayer ();\r\n\t\tself.modelindex = 0;\r\n\t\treturn;\r\n\t}\r\n\r\n\tDeathSound();\r\n\t\r\n\tself.angles_x = 0;\r\n\tself.angles_z = 0;\r\n\t\r\n\tif (self.weapon == IT_AXE)\r\n\t{\r\n\t\tplayer_die_ax1 ();\r\n\t}\r\n\telse\r\n\t{\r\n\t\ti = 1 + floor(random()*6);\r\n\t\r\n\t\tif (i == 1)\r\n\t\t\tplayer_diea1();\r\n\t\telse if (i == 2)\r\n\t\t\tplayer_dieb1();\r\n\t\telse if (i == 3)\r\n\t\t\tplayer_diec1();\r\n\t\telse if (i == 4)\r\n\t\t\tplayer_died1();\r\n\t\telse\r\n\t\t\tplayer_diee1();\r\n\t}\r\n};\r\n\r\nvoid() set_suicide_frame =\r\n{\t// used by kill command and diconnect command\r\n\tif (self.model != \"progs/player.mdl\")\r\n\t\treturn;\t// already gibbed\r\n\tself.frame = $deatha11;\r\n\tself.solid = SOLID_NOT;\r\n\tself.movetype = MOVETYPE_TOSS;\r\n\tself.deadflag = DEAD_DEAD;\r\n\tself.nextthink = -1;\r\n};\r\n\r\n\r\nvoid()\tplayer_diea1\t=\t[\t$deatha1,\tplayer_diea2\t] {};\r\nvoid()\tplayer_diea2\t=\t[\t$deatha2,\tplayer_diea3\t] {};\r\nvoid()\tplayer_diea3\t=\t[\t$deatha3,\tplayer_diea4\t] {};\r\nvoid()\tplayer_diea4\t=\t[\t$deatha4,\tplayer_diea5\t] {};\r\nvoid()\tplayer_diea5\t=\t[\t$deatha5,\tplayer_diea6\t] {};\r\nvoid()\tplayer_diea6\t=\t[\t$deatha6,\tplayer_diea7\t] {};\r\nvoid()\tplayer_diea7\t=\t[\t$deatha7,\tplayer_diea8\t] {};\r\nvoid()\tplayer_diea8\t=\t[\t$deatha8,\tplayer_diea9\t] {};\r\nvoid()\tplayer_diea9\t=\t[\t$deatha9,\tplayer_diea10\t] {};\r\nvoid()\tplayer_diea10\t=\t[\t$deatha10,\tplayer_diea11\t] {};\r\nvoid()\tplayer_diea11\t=\t[\t$deatha11,\tplayer_diea11 ] {PlayerDead();};\r\n\r\nvoid()\tplayer_dieb1\t=\t[\t$deathb1,\tplayer_dieb2\t] {};\r\nvoid()\tplayer_dieb2\t=\t[\t$deathb2,\tplayer_dieb3\t] {};\r\nvoid()\tplayer_dieb3\t=\t[\t$deathb3,\tplayer_dieb4\t] {};\r\nvoid()\tplayer_dieb4\t=\t[\t$deathb4,\tplayer_dieb5\t] {};\r\nvoid()\tplayer_dieb5\t=\t[\t$deathb5,\tplayer_dieb6\t] {};\r\nvoid()\tplayer_dieb6\t=\t[\t$deathb6,\tplayer_dieb7\t] {};\r\nvoid()\tplayer_dieb7\t=\t[\t$deathb7,\tplayer_dieb8\t] {};\r\nvoid()\tplayer_dieb8\t=\t[\t$deathb8,\tplayer_dieb9\t] {};\r\nvoid()\tplayer_dieb9\t=\t[\t$deathb9,\tplayer_dieb9\t] {PlayerDead();};\r\n\r\nvoid()\tplayer_diec1\t=\t[\t$deathc1,\tplayer_diec2\t] {};\r\nvoid()\tplayer_diec2\t=\t[\t$deathc2,\tplayer_diec3\t] {};\r\nvoid()\tplayer_diec3\t=\t[\t$deathc3,\tplayer_diec4\t] {};\r\nvoid()\tplayer_diec4\t=\t[\t$deathc4,\tplayer_diec5\t] {};\r\nvoid()\tplayer_diec5\t=\t[\t$deathc5,\tplayer_diec6\t] {};\r\nvoid()\tplayer_diec6\t=\t[\t$deathc6,\tplayer_diec7\t] {};\r\nvoid()\tplayer_diec7\t=\t[\t$deathc7,\tplayer_diec8\t] {};\r\nvoid()\tplayer_diec8\t=\t[\t$deathc8,\tplayer_diec9\t] {};\r\nvoid()\tplayer_diec9\t=\t[\t$deathc9,\tplayer_diec10\t] {};\r\nvoid()\tplayer_diec10\t=\t[\t$deathc10,\tplayer_diec11\t] {};\r\nvoid()\tplayer_diec11\t=\t[\t$deathc11,\tplayer_diec12\t] {};\r\nvoid()\tplayer_diec12\t=\t[\t$deathc12,\tplayer_diec13\t] {};\r\nvoid()\tplayer_diec13\t=\t[\t$deathc13,\tplayer_diec14\t] {};\r\nvoid()\tplayer_diec14\t=\t[\t$deathc14,\tplayer_diec15\t] {};\r\nvoid()\tplayer_diec15\t=\t[\t$deathc15,\tplayer_diec15 ] {PlayerDead();};\r\n\r\nvoid()\tplayer_died1\t=\t[\t$deathd1,\tplayer_died2\t] {};\r\nvoid()\tplayer_died2\t=\t[\t$deathd2,\tplayer_died3\t] {};\r\nvoid()\tplayer_died3\t=\t[\t$deathd3,\tplayer_died4\t] {};\r\nvoid()\tplayer_died4\t=\t[\t$deathd4,\tplayer_died5\t] {};\r\nvoid()\tplayer_died5\t=\t[\t$deathd5,\tplayer_died6\t] {};\r\nvoid()\tplayer_died6\t=\t[\t$deathd6,\tplayer_died7\t] {};\r\nvoid()\tplayer_died7\t=\t[\t$deathd7,\tplayer_died8\t] {};\r\nvoid()\tplayer_died8\t=\t[\t$deathd8,\tplayer_died9\t] {};\r\nvoid()\tplayer_died9\t=\t[\t$deathd9,\tplayer_died9\t] {PlayerDead();};\r\n\r\nvoid()\tplayer_diee1\t=\t[\t$deathe1,\tplayer_diee2\t] {};\r\nvoid()\tplayer_diee2\t=\t[\t$deathe2,\tplayer_diee3\t] {};\r\nvoid()\tplayer_diee3\t=\t[\t$deathe3,\tplayer_diee4\t] {};\r\nvoid()\tplayer_diee4\t=\t[\t$deathe4,\tplayer_diee5\t] {};\r\nvoid()\tplayer_diee5\t=\t[\t$deathe5,\tplayer_diee6\t] {};\r\nvoid()\tplayer_diee6\t=\t[\t$deathe6,\tplayer_diee7\t] {};\r\nvoid()\tplayer_diee7\t=\t[\t$deathe7,\tplayer_diee8\t] {};\r\nvoid()\tplayer_diee8\t=\t[\t$deathe8,\tplayer_diee9\t] {};\r\nvoid()\tplayer_diee9\t=\t[\t$deathe9,\tplayer_diee9\t] {PlayerDead();};\r\n\r\nvoid()\tplayer_die_ax1\t=\t[\t$axdeth1,\tplayer_die_ax2\t] {};\r\nvoid()\tplayer_die_ax2\t=\t[\t$axdeth2,\tplayer_die_ax3\t] {};\r\nvoid()\tplayer_die_ax3\t=\t[\t$axdeth3,\tplayer_die_ax4\t] {};\r\nvoid()\tplayer_die_ax4\t=\t[\t$axdeth4,\tplayer_die_ax5\t] {};\r\nvoid()\tplayer_die_ax5\t=\t[\t$axdeth5,\tplayer_die_ax6\t] {};\r\nvoid()\tplayer_die_ax6\t=\t[\t$axdeth6,\tplayer_die_ax7\t] {};\r\nvoid()\tplayer_die_ax7\t=\t[\t$axdeth7,\tplayer_die_ax8\t] {};\r\nvoid()\tplayer_die_ax8\t=\t[\t$axdeth8,\tplayer_die_ax9\t] {};\r\nvoid()\tplayer_die_ax9\t=\t[\t$axdeth9,\tplayer_die_ax9\t] {PlayerDead();};\r\n"
  },
  {
    "path": "quakec/csqctest/src/ss/shalrath.qc",
    "content": "/*\r\n==============================================================================\r\n\r\nSHAL-RATH\r\n\r\n==============================================================================\r\n*/\r\n$cd id1/models/shalrath\r\n$origin 0 0 24\r\n$base base\r\n$skin skin\r\n$scale 0.7\r\n\r\n$frame attack1 attack2 attack3 attack4 attack5 attack6 attack7 attack8\r\n$frame attack9 attack10 attack11\r\n\r\n$frame pain1 pain2 pain3 pain4 pain5 \r\n\r\n$frame death1 death2 death3 death4 death5 death6 death7\r\n\r\n$frame\twalk1 walk2 walk3 walk4 walk5 walk6 walk7 walk8 walk9 walk10\r\n$frame\twalk11 walk12\r\n\r\nvoid(entity attacker, float damage) shalrath_pain;\r\nvoid() ShalMissile;\r\nvoid() shal_stand     =[      $walk1,       shal_stand    ] {ai_stand();};\r\n\r\nvoid() shal_walk1     =[      $walk2,       shal_walk2    ] {\r\nif (random() < 0.2)\r\n\tsound (self, CHAN_VOICE, \"shalrath/idle.wav\", 1, ATTN_IDLE);\r\nai_walk(6);};\r\nvoid() shal_walk2     =[      $walk3,       shal_walk3    ] {ai_walk(4);};\r\nvoid() shal_walk3     =[      $walk4,       shal_walk4    ] {ai_walk(0);};\r\nvoid() shal_walk4     =[      $walk5,       shal_walk5    ] {ai_walk(0);};\r\nvoid() shal_walk5     =[      $walk6,       shal_walk6    ] {ai_walk(0);};\r\nvoid() shal_walk6     =[      $walk7,       shal_walk7    ] {ai_walk(0);};\r\nvoid() shal_walk7     =[      $walk8,       shal_walk8    ] {ai_walk(5);};\r\nvoid() shal_walk8     =[      $walk9,       shal_walk9    ] {ai_walk(6);};\r\nvoid() shal_walk9     =[      $walk10,       shal_walk10    ] {ai_walk(5);};\r\nvoid() shal_walk10    =[      $walk11,       shal_walk11    ] {ai_walk(0);};\r\nvoid() shal_walk11    =[      $walk12,       shal_walk12    ] {ai_walk(4);};\r\nvoid() shal_walk12    =[      $walk1,       shal_walk1    ] {ai_walk(5);};\r\n\r\nvoid() shal_run1     =[      $walk2,       shal_run2    ] {\r\nif (random() < 0.2)\r\n\tsound (self, CHAN_VOICE, \"shalrath/idle.wav\", 1, ATTN_IDLE);\r\nai_run(6);};\r\nvoid() shal_run2     =[      $walk3,       shal_run3    ] {ai_run(4);};\r\nvoid() shal_run3     =[      $walk4,       shal_run4    ] {ai_run(0);};\r\nvoid() shal_run4     =[      $walk5,       shal_run5    ] {ai_run(0);};\r\nvoid() shal_run5     =[      $walk6,       shal_run6    ] {ai_run(0);};\r\nvoid() shal_run6     =[      $walk7,       shal_run7    ] {ai_run(0);};\r\nvoid() shal_run7     =[      $walk8,       shal_run8    ] {ai_run(5);};\r\nvoid() shal_run8     =[      $walk9,       shal_run9    ] {ai_run(6);};\r\nvoid() shal_run9     =[      $walk10,       shal_run10    ] {ai_run(5);};\r\nvoid() shal_run10    =[      $walk11,       shal_run11    ] {ai_run(0);};\r\nvoid() shal_run11    =[      $walk12,       shal_run12    ] {ai_run(4);};\r\nvoid() shal_run12    =[      $walk1,       shal_run1    ] {ai_run(5);};\r\n\r\nvoid() shal_attack1     =[      $attack1,       shal_attack2    ] {\r\nsound (self, CHAN_VOICE, \"shalrath/attack.wav\", 1, ATTN_NORM);\r\nai_face();\r\n};\r\nvoid() shal_attack2     =[      $attack2,       shal_attack3    ] {ai_face();};\r\nvoid() shal_attack3     =[      $attack3,       shal_attack4    ] {ai_face();};\r\nvoid() shal_attack4     =[      $attack4,       shal_attack5    ] {ai_face();};\r\nvoid() shal_attack5     =[      $attack5,       shal_attack6    ] {ai_face();};\r\nvoid() shal_attack6     =[      $attack6,       shal_attack7    ] {ai_face();};\r\nvoid() shal_attack7     =[      $attack7,       shal_attack8    ] {ai_face();};\r\nvoid() shal_attack8     =[      $attack8,       shal_attack9    ] {ai_face();};\r\nvoid() shal_attack9     =[      $attack9,       shal_attack10   ] {ShalMissile();};\r\nvoid() shal_attack10    =[      $attack10,      shal_attack11   ] {ai_face();};\r\nvoid() shal_attack11    =[      $attack11,      shal_run1   ] {};\r\n\r\nvoid() shal_pain1       =[      $pain1, shal_pain2      ] {};\r\nvoid() shal_pain2       =[      $pain2, shal_pain3      ] {};\r\nvoid() shal_pain3       =[      $pain3, shal_pain4      ] {};\r\nvoid() shal_pain4       =[      $pain4, shal_pain5      ] {};\r\nvoid() shal_pain5       =[      $pain5, shal_run1      ] {};\r\n\r\nvoid() shal_death1      =[      $death1,        shal_death2     ] {};\r\nvoid() shal_death2      =[      $death2,        shal_death3     ] {};\r\nvoid() shal_death3      =[      $death3,        shal_death4     ] {};\r\nvoid() shal_death4      =[      $death4,        shal_death5     ] {};\r\nvoid() shal_death5      =[      $death5,        shal_death6     ] {};\r\nvoid() shal_death6      =[      $death6,        shal_death7     ] {};\r\nvoid() shal_death7      =[      $death7,        shal_death7    ] {};\r\n\r\n\r\nvoid(entity attacker, float damage) shalrath_pain =\r\n{\r\n\tif (self.pain_finished > time)\r\n\t\treturn;\r\n\r\n\tsound (self, CHAN_VOICE, \"shalrath/pain.wav\", 1, ATTN_NORM);\r\n\tshal_pain1();\r\n\tself.pain_finished = time + 3;\r\n};\r\n\r\nvoid() shalrath_die =\r\n{\r\n// check for gib\r\n\tif (self.health < -90)\r\n\t{\r\n\t\tThrowCSQCGibs(GIB_SHALRATH);\r\n\t\treturn;\r\n\t}\r\n\r\n\tsound (self, CHAN_VOICE, \"shalrath/death.wav\", 1, ATTN_NORM);\r\n\tshal_death1();\r\n\tself.solid = SOLID_NOT;\r\n\t// insert death sounds here\r\n};\r\n\r\n/*\r\n================\r\nShalMissile\r\n================\r\n*/\r\nvoid() ShalMissileTouch;\r\nvoid() ShalHome;\r\nvoid() ShalMissile =\r\n{\r\n\tlocal\tentity \tmissile;\r\n\tlocal\tvector\tdir;\r\n\tlocal\tfloat\tdist, flytime;\r\n\r\n\tdir = normalize((self.enemy.origin + '0 0 10') - self.origin);\r\n\tdist = vlen (self.enemy.origin - self.origin);\r\n\tflytime = dist * 0.002;\r\n\tif (flytime < 0.1)\r\n\t\tflytime = 0.1;\r\n\r\n\tself.effects = self.effects | EF_MUZZLEFLASH;\r\n\tsound (self, CHAN_WEAPON, \"shalrath/attack2.wav\", 1, ATTN_NORM);\r\n\r\n\tmissile = spawn ();\r\n\tmissile.owner = self;\r\n\r\n\tmissile.solid = SOLID_BBOX;\r\n\tmissile.movetype = MOVETYPE_FLYMISSILE;\r\n\tsetmodel (missile, \"progs/v_spike.mdl\");\r\n\r\n\tsetsize (missile, '0 0 0', '0 0 0');\t\t\r\n\r\n\tmissile.origin = self.origin + '0 0 10';\r\n\tmissile.velocity = dir * 400;\r\n\tmissile.avelocity = '300 300 300';\r\n\tmissile.nextthink = flytime + time;\r\n\tmissile.think = ShalHome;\r\n\tmissile.enemy = self.enemy;\r\n\tmissile.touch = ShalMissileTouch;\r\n};\r\n\r\nvoid() ShalHome =\r\n{\r\n\tlocal vector\tdir, vtemp;\r\n\tvtemp = self.enemy.origin + '0 0 10';\r\n\tif (self.enemy.health < 1)\r\n\t{\r\n\t\tremove(self);\r\n\t\treturn;\r\n\t}\r\n\tdir = normalize(vtemp - self.origin);\r\n\tif (skill == 3)\r\n\t\tself.velocity = dir * 350;\r\n\telse\r\n\t\tself.velocity = dir * 250;\r\n\tself.nextthink = time + 0.2;\r\n\tself.think = ShalHome;\t\r\n};\r\n\r\nvoid() ShalMissileTouch =\r\n{\r\n\tif (other == self.owner)\r\n\t\treturn;\t\t// don't explode on owner\r\n\r\n\tif (other.classname == \"monster_zombie\")\r\n\t\tT_Damage (other, self, self, 110);\t\r\n\tT_RadiusDamage (self, self.owner, 40, world);\r\n\r\n#if 1\r\n\tBecomeExplosion();\r\n#else\r\n\tsound (self, CHAN_WEAPON, \"weapons/r_exp3.wav\", 1, ATTN_NORM);\r\n\r\n\tWriteByte (MSG_BROADCAST, SVC_TEMPENTITY);\r\n\tWriteByte (MSG_BROADCAST, TE_EXPLOSION);\r\n\tWriteCoord (MSG_BROADCAST, self.origin_x);\r\n\tWriteCoord (MSG_BROADCAST, self.origin_y);\r\n\tWriteCoord (MSG_BROADCAST, self.origin_z);\r\n\r\n\tself.velocity = '0 0 0';\r\n\tself.touch = SUB_Null;\r\n\tsetmodel (self, \"progs/s_explod.spr\");\r\n\tself.solid = SOLID_NOT;\r\n\ts_explode1 ();\r\n#endif\r\n};\r\n\r\n//=================================================================\r\n\r\n/*QUAKED monster_shalrath (1 0 0) (-32 -32 -24) (32 32 48) Ambush\r\n*/\r\nvoid() monster_shalrath =\r\n{\r\n\tif (deathmatch)\r\n\t{\r\n\t\tremove(self);\r\n\t\treturn;\r\n\t}\r\n\tprecache_model2 (\"progs/shalrath.mdl\");\r\n\tprecache_model2 (\"progs/h_shal.mdl\");\r\n\tprecache_model2 (\"progs/v_spike.mdl\");\r\n\t\r\n\tprecache_sound2 (\"shalrath/attack.wav\");\r\n\tprecache_sound2 (\"shalrath/attack2.wav\");\r\n\tprecache_sound2 (\"shalrath/death.wav\");\r\n\tprecache_sound2 (\"shalrath/idle.wav\");\r\n\tprecache_sound2 (\"shalrath/pain.wav\");\r\n\tprecache_sound2 (\"shalrath/sight.wav\");\r\n\t\r\n\tself.solid = SOLID_SLIDEBOX;\r\n\tself.movetype = MOVETYPE_STEP;\r\n\t\r\n\tsetmodel (self, \"progs/shalrath.mdl\");\r\n\tsetsize (self, VEC_HULL2_MIN, VEC_HULL2_MAX);\r\n\tself.health = 400;\r\n\r\n\tself.th_stand = shal_stand;\r\n\tself.th_walk = shal_walk1;\r\n\tself.th_run = shal_run1;\r\n\tself.th_die = shalrath_die;\r\n\tself.th_pain = shalrath_pain;\r\n\tself.th_missile = shal_attack1;\r\n\r\n\tself.think = walkmonster_start;\r\n\tself.nextthink = time + 0.1 + random ()*0.1;\t\r\n\r\n};\r\n"
  },
  {
    "path": "quakec/csqctest/src/ss/shambler.qc",
    "content": "/*\r\n==============================================================================\r\n\r\nSHAMBLER\r\n\r\n==============================================================================\r\n*/\r\n\r\n$cd id1/models/shams\r\n$origin 0 0 24\r\n$base base\t\t\r\n$skin base\r\n\r\n$frame stand1 stand2 stand3 stand4 stand5 stand6 stand7 stand8 stand9\r\n$frame stand10 stand11 stand12 stand13 stand14 stand15 stand16 stand17\r\n\r\n$frame walk1 walk2 walk3 walk4 walk5 walk6 walk7 \r\n$frame walk8 walk9 walk10 walk11 walk12\r\n\r\n$frame\trun1 run2 run3 run4 run5 run6\r\n\r\n$frame smash1 smash2 smash3 smash4 smash5 smash6 smash7 \r\n$frame smash8 smash9 smash10 smash11 smash12\r\n\r\n$frame swingr1 swingr2 swingr3 swingr4 swingr5 \r\n$frame swingr6 swingr7 swingr8 swingr9\r\n\r\n$frame swingl1 swingl2 swingl3 swingl4 swingl5 \r\n$frame swingl6 swingl7 swingl8 swingl9\r\n\r\n$frame magic1 magic2 magic3 magic4 magic5 \r\n$frame magic6 magic7 magic8 magic9 magic10 magic11 magic12\r\n\r\n$frame pain1 pain2 pain3 pain4 pain5 pain6\r\n\r\n$frame death1 death2 death3 death4 death5 death6 \r\n$frame death7 death8 death9 death10 death11\r\n\r\nvoid() sham_stand1\t=[\t$stand1,\tsham_stand2\t] {ai_stand();};\r\nvoid() sham_stand2\t=[\t$stand2,\tsham_stand3\t] {ai_stand();};\r\nvoid() sham_stand3\t=[\t$stand3,\tsham_stand4\t] {ai_stand();};\r\nvoid() sham_stand4\t=[\t$stand4,\tsham_stand5\t] {ai_stand();};\r\nvoid() sham_stand5\t=[\t$stand5,\tsham_stand6\t] {ai_stand();};\r\nvoid() sham_stand6\t=[\t$stand6,\tsham_stand7\t] {ai_stand();};\r\nvoid() sham_stand7\t=[\t$stand7,\tsham_stand8\t] {ai_stand();};\r\nvoid() sham_stand8\t=[\t$stand8,\tsham_stand9\t] {ai_stand();};\r\nvoid() sham_stand9\t=[\t$stand9,\tsham_stand10] {ai_stand();};\r\nvoid() sham_stand10\t=[\t$stand10,\tsham_stand11] {ai_stand();};\r\nvoid() sham_stand11\t=[\t$stand11,\tsham_stand12] {ai_stand();};\r\nvoid() sham_stand12\t=[\t$stand12,\tsham_stand13] {ai_stand();};\r\nvoid() sham_stand13\t=[\t$stand13,\tsham_stand14] {ai_stand();};\r\nvoid() sham_stand14\t=[\t$stand14,\tsham_stand15] {ai_stand();};\r\nvoid() sham_stand15\t=[\t$stand15,\tsham_stand16] {ai_stand();};\r\nvoid() sham_stand16\t=[\t$stand16,\tsham_stand17] {ai_stand();};\r\nvoid() sham_stand17\t=[\t$stand17,\tsham_stand1\t] {ai_stand();};\r\n\r\nvoid() sham_walk1\t\t=[      $walk1,        sham_walk2 ] {ai_walk(10);};\r\nvoid() sham_walk2       =[      $walk2,        sham_walk3 ] {ai_walk(9);};\r\nvoid() sham_walk3       =[      $walk3,        sham_walk4 ] {ai_walk(9);};\r\nvoid() sham_walk4       =[      $walk4,        sham_walk5 ] {ai_walk(5);};\r\nvoid() sham_walk5       =[      $walk5,        sham_walk6 ] {ai_walk(6);};\r\nvoid() sham_walk6       =[      $walk6,        sham_walk7 ] {ai_walk(12);};\r\nvoid() sham_walk7       =[      $walk7,        sham_walk8 ] {ai_walk(8);};\r\nvoid() sham_walk8       =[      $walk8,        sham_walk9 ] {ai_walk(3);};\r\nvoid() sham_walk9       =[      $walk9,        sham_walk10] {ai_walk(13);};\r\nvoid() sham_walk10      =[      $walk10,       sham_walk11] {ai_walk(9);};\r\nvoid() sham_walk11      =[      $walk11,       sham_walk12] {ai_walk(7);};\r\nvoid() sham_walk12      =[      $walk12,       sham_walk1 ] {ai_walk(7);\r\nif (random() > 0.8)\r\n\tsound (self, CHAN_VOICE, \"shambler/sidle.wav\", 1, ATTN_IDLE);};\r\n\r\nvoid() sham_run1       =[      $run1,        sham_run2      ] {ai_run(20);};\r\nvoid() sham_run2       =[      $run2,        sham_run3      ] {ai_run(24);};\r\nvoid() sham_run3       =[      $run3,        sham_run4      ] {ai_run(20);};\r\nvoid() sham_run4       =[      $run4,        sham_run5      ] {ai_run(20);};\r\nvoid() sham_run5       =[      $run5,        sham_run6      ] {ai_run(24);};\r\nvoid() sham_run6       =[      $run6,        sham_run1      ] {ai_run(20);\r\nif (random() > 0.8)\r\n\tsound (self, CHAN_VOICE, \"shambler/sidle.wav\", 1, ATTN_IDLE);\r\n};\r\n\r\nvoid() sham_smash1     =[      $smash1,       sham_smash2    ] {\r\nsound (self, CHAN_VOICE, \"shambler/melee1.wav\", 1, ATTN_NORM);\r\nai_charge(2);};\r\nvoid() sham_smash2     =[      $smash2,       sham_smash3    ] {ai_charge(6);};\r\nvoid() sham_smash3     =[      $smash3,       sham_smash4    ] {ai_charge(6);};\r\nvoid() sham_smash4     =[      $smash4,       sham_smash5    ] {ai_charge(5);};\r\nvoid() sham_smash5     =[      $smash5,       sham_smash6    ] {ai_charge(4);};\r\nvoid() sham_smash6     =[      $smash6,       sham_smash7    ] {ai_charge(1);};\r\nvoid() sham_smash7     =[      $smash7,       sham_smash8    ] {ai_charge(0);};\r\nvoid() sham_smash8     =[      $smash8,       sham_smash9    ] {ai_charge(0);};\r\nvoid() sham_smash9     =[      $smash9,       sham_smash10   ] {ai_charge(0);};\r\nvoid() sham_smash10    =[      $smash10,      sham_smash11   ] {\r\nlocal vector\tdelta;\r\nlocal float \tldmg;\r\n\r\n\tif (!self.enemy)\r\n\t\treturn;\r\n\tai_charge(0);\r\n\r\n\tdelta = self.enemy.origin - self.origin;\r\n\r\n\tif (vlen(delta) > 100)\r\n\t\treturn;\r\n\tif (!CanDamage (self.enemy, self))\r\n\t\treturn;\r\n\t\t\r\n\tldmg = (random()*3) * 40;\r\n\tT_Damage (self.enemy, self, self, ldmg);\r\n\tsound (self, CHAN_VOICE, \"shambler/smack.wav\", 1, ATTN_NORM);\r\n\r\n\tSpawnMeatSpray (self.origin + v_forward*16, crandom() * 100 * v_right);\r\n\tSpawnMeatSpray (self.origin + v_forward*16, crandom() * 100 * v_right);\r\n};\r\nvoid() sham_smash11    =[      $smash11,      sham_smash12   ] {ai_charge(5);};\r\nvoid() sham_smash12    =[      $smash12,      sham_run1\t   ] {ai_charge(4);};\r\n\r\nvoid() sham_swingr1;\r\n\r\nvoid(float side) ShamClaw =\r\n{\r\nlocal vector\tdelta;\r\nlocal float \tldmg;\r\n\r\n\tif (!self.enemy)\r\n\t\treturn;\r\n\tai_charge(10);\r\n\r\n\tdelta = self.enemy.origin - self.origin;\r\n\r\n\tif (vlen(delta) > 100)\r\n\t\treturn;\r\n\t\t\r\n\tldmg = (random()*3) * 20;\r\n\tT_Damage (self.enemy, self, self, ldmg);\r\n\tsound (self, CHAN_VOICE, \"shambler/smack.wav\", 1, ATTN_NORM);\r\n\r\n\tif (side)\r\n\t{\r\n\t\tmakevectors (self.angles);\r\n\t\tSpawnMeatSpray (self.origin + v_forward*16, side * v_right);\r\n\t}\r\n};\r\n\r\nvoid() sham_swingl1\t=[      $swingl1,      sham_swingl2   ] {\r\nsound (self, CHAN_VOICE, \"shambler/melee2.wav\", 1, ATTN_NORM);\r\nai_charge(5);};\r\nvoid() sham_swingl2 =[      $swingl2,      sham_swingl3   ] {ai_charge(3);};\r\nvoid() sham_swingl3 =[      $swingl3,      sham_swingl4   ] {ai_charge(7);};\r\nvoid() sham_swingl4 =[      $swingl4,      sham_swingl5   ] {ai_charge(3);};\r\nvoid() sham_swingl5 =[      $swingl5,      sham_swingl6   ] {ai_charge(7);};\r\nvoid() sham_swingl6 =[      $swingl6,      sham_swingl7   ] {ai_charge(9);};\r\nvoid() sham_swingl7 =[      $swingl7,      sham_swingl8   ] {ai_charge(5); ShamClaw(250);};\r\nvoid() sham_swingl8 =[      $swingl8,      sham_swingl9   ] {ai_charge(4);};\r\nvoid() sham_swingl9 =[      $swingl9,      sham_run1  ] {\r\nai_charge(8);\r\nif (random()<0.5)\r\n\tself.think = sham_swingr1;\r\n};\r\n\r\nvoid() sham_swingr1\t=[      $swingr1,      sham_swingr2   ] {\r\nsound (self, CHAN_VOICE, \"shambler/melee1.wav\", 1, ATTN_NORM);\r\nai_charge(1);};\r\nvoid() sham_swingr2\t=[      $swingr2,      sham_swingr3   ] {ai_charge(8);};\r\nvoid() sham_swingr3 =[      $swingr3,      sham_swingr4   ] {ai_charge(14);};\r\nvoid() sham_swingr4 =[      $swingr4,      sham_swingr5   ] {ai_charge(7);};\r\nvoid() sham_swingr5 =[      $swingr5,      sham_swingr6   ] {ai_charge(3);};\r\nvoid() sham_swingr6 =[      $swingr6,      sham_swingr7   ] {ai_charge(6);};\r\nvoid() sham_swingr7 =[      $swingr7,      sham_swingr8   ] {ai_charge(6); ShamClaw(-250);};\r\nvoid() sham_swingr8 =[      $swingr8,      sham_swingr9   ] {ai_charge(3);};\r\nvoid() sham_swingr9 =[      $swingr9,      sham_run1  ] {ai_charge(1);\r\nai_charge(10);\r\nif (random()<0.5)\r\n\tself.think = sham_swingl1;\r\n};\r\n\r\nvoid() sham_melee =\r\n{\r\n\tlocal float chance;\r\n\t\r\n\tchance = random();\r\n\tif (chance > 0.6 || self.health == 600)\r\n\t\tsham_smash1 ();\r\n\telse if (chance > 0.3)\r\n\t\tsham_swingr1 ();\r\n\telse\r\n\t\tsham_swingl1 ();\r\n};\r\n\r\n\r\n//============================================================================\r\n\r\nvoid() CastLightning =\r\n{\r\n\tlocal\tvector\torg, dir;\r\n\t\r\n\tself.effects = self.effects | EF_MUZZLEFLASH;\r\n\r\n\tai_face ();\r\n\r\n\torg = self.origin + '0 0 40';\r\n\r\n\tdir = self.enemy.origin + '0 0 16' - org;\r\n\tdir = normalize (dir);\r\n\r\n\ttraceline (org, self.origin + dir*600, TRUE, self);\r\n\r\n\tWriteByte (MSG_BROADCAST, SVC_TEMPENTITY);\r\n\tWriteByte (MSG_BROADCAST, TE_LIGHTNING1);\r\n\tWriteEntity (MSG_BROADCAST, self);\r\n\tWriteCoord (MSG_BROADCAST, org_x);\r\n\tWriteCoord (MSG_BROADCAST, org_y);\r\n\tWriteCoord (MSG_BROADCAST, org_z);\r\n\tWriteCoord (MSG_BROADCAST, trace_endpos_x);\r\n\tWriteCoord (MSG_BROADCAST, trace_endpos_y);\r\n\tWriteCoord (MSG_BROADCAST, trace_endpos_z);\r\n\r\n\tLightningDamage (org, trace_endpos, self, 10);\r\n};\r\n\r\nvoid() sham_magic1     =[      $magic1,       sham_magic2    ] {ai_face();\r\n\tsound (self, CHAN_WEAPON, \"shambler/sattck1.wav\", 1, ATTN_NORM);\r\n};\r\nvoid() sham_magic2     =[      $magic2,       sham_magic3    ] {ai_face();};\r\nvoid() sham_magic3     =[      $magic3,       sham_magic4    ] {ai_face();self.nextthink = self.nextthink + 0.2;\r\nlocal entity o;\r\n\r\nself.effects = self.effects | EF_MUZZLEFLASH;\r\nai_face();\r\nself.owner = spawn();\r\no = self.owner;\r\nsetmodel (o, \"progs/s_light.mdl\");\r\nsetorigin (o, self.origin);\r\no.angles = self.angles;\r\no.nextthink = time + 0.7;\r\no.think = SUB_Remove;\r\n};\r\nvoid() sham_magic4     =[      $magic4,       sham_magic5    ]\r\n{\r\nself.effects = self.effects | EF_MUZZLEFLASH;\r\nself.owner.frame = 1;\r\n};\r\nvoid() sham_magic5     =[      $magic5,       sham_magic6    ]\r\n{\r\nself.effects = self.effects | EF_MUZZLEFLASH;\r\nself.owner.frame = 2;\r\n};\r\nvoid() sham_magic6     =[      $magic6,       sham_magic9    ]\r\n{\r\nremove (self.owner);\r\nCastLightning();\r\nsound (self, CHAN_WEAPON, \"shambler/sboom.wav\", 1, ATTN_NORM);\r\n};\r\nvoid() sham_magic9     =[      $magic9,       sham_magic10   ]\r\n{CastLightning();};\r\nvoid() sham_magic10    =[      $magic10,      sham_magic11   ]\r\n{CastLightning();};\r\nvoid() sham_magic11    =[      $magic11,      sham_magic12   ]\r\n{\r\nif (skill == 3)\r\n\tCastLightning();\r\n};\r\nvoid() sham_magic12    =[      $magic12,      sham_run1\t   ] {};\r\n\r\n\r\n\r\nvoid() sham_pain1       =[      $pain1, sham_pain2      ] {};\r\nvoid() sham_pain2       =[      $pain2, sham_pain3      ] {};\r\nvoid() sham_pain3       =[      $pain3, sham_pain4      ] {};\r\nvoid() sham_pain4       =[      $pain4, sham_pain5      ] {};\r\nvoid() sham_pain5       =[      $pain5, sham_pain6      ] {};\r\nvoid() sham_pain6       =[      $pain6, sham_run1      ] {};\r\n\r\nvoid(entity attacker, float damage)\tsham_pain =\r\n{\r\n\tsound (self, CHAN_VOICE, \"shambler/shurt2.wav\", 1, ATTN_NORM);\r\n\r\n\tif (self.health <= 0)\r\n\t\treturn;\t\t// allready dying, don't go into pain frame\r\n\r\n\tif (random()*400 > damage)\r\n\t\treturn;\t\t// didn't flinch\r\n\r\n\tif (self.pain_finished > time)\r\n\t\treturn;\r\n\tself.pain_finished = time + 2;\r\n\t\t\r\n\tsham_pain1 ();\r\n};\r\n\r\n\r\n//============================================================================\r\n\r\nvoid() sham_death1      =[      $death1,       sham_death2     ] {};\r\nvoid() sham_death2      =[      $death2,       sham_death3     ] {};\r\nvoid() sham_death3      =[      $death3,       sham_death4     ] {self.solid = SOLID_NOT;};\r\nvoid() sham_death4      =[      $death4,       sham_death5     ] {};\r\nvoid() sham_death5      =[      $death5,       sham_death6     ] {};\r\nvoid() sham_death6      =[      $death6,       sham_death7     ] {};\r\nvoid() sham_death7      =[      $death7,       sham_death8     ] {};\r\nvoid() sham_death8      =[      $death8,       sham_death9     ] {};\r\nvoid() sham_death9      =[      $death9,       sham_death10    ] {};\r\nvoid() sham_death10     =[      $death10,      sham_death11    ] {};\r\nvoid() sham_death11     =[      $death11,      sham_death11    ] {};\r\n\r\nvoid() sham_die =\r\n{\r\n// check for gib\r\n\tif (self.health < -60)\r\n\t{\r\n\t\tThrowCSQCGibs(GIB_SHAMBLER);\r\n\t\treturn;\r\n\t}\r\n\r\n// regular death\r\n\tsound (self, CHAN_VOICE, \"shambler/sdeath.wav\", 1, ATTN_NORM);\r\n\tsham_death1 ();\r\n};\r\n\r\n//============================================================================\r\n\r\n\r\n/*QUAKED monster_shambler (1 0 0) (-32 -32 -24) (32 32 64) Ambush\r\n*/\r\nvoid() monster_shambler =\r\n{\r\n\tif (deathmatch)\r\n\t{\r\n\t\tremove(self);\r\n\t\treturn;\r\n\t}\r\n\tprecache_model (\"progs/shambler.mdl\");\r\n\tprecache_model (\"progs/s_light.mdl\");\r\n\tprecache_model (\"progs/h_shams.mdl\");\r\n\tprecache_model (\"progs/bolt.mdl\");\r\n\t\r\n\tprecache_sound (\"shambler/sattck1.wav\");\r\n\tprecache_sound (\"shambler/sboom.wav\");\r\n\tprecache_sound (\"shambler/sdeath.wav\");\r\n\tprecache_sound (\"shambler/shurt2.wav\");\r\n\tprecache_sound (\"shambler/sidle.wav\");\r\n\tprecache_sound (\"shambler/ssight.wav\");\r\n\tprecache_sound (\"shambler/melee1.wav\");\r\n\tprecache_sound (\"shambler/melee2.wav\");\r\n\tprecache_sound (\"shambler/smack.wav\");\r\n\r\n\tself.solid = SOLID_SLIDEBOX;\r\n\tself.movetype = MOVETYPE_STEP;\r\n\tsetmodel (self, \"progs/shambler.mdl\");\r\n\r\n\tsetsize (self, VEC_HULL2_MIN, VEC_HULL2_MAX);\r\n\tself.health = 600;\r\n\r\n\tself.th_stand = sham_stand1;\r\n\tself.th_walk = sham_walk1;\r\n\tself.th_run = sham_run1;\r\n\tself.th_die = sham_die;\r\n\tself.th_melee = sham_melee;\r\n\tself.th_missile = sham_magic1;\r\n\tself.th_pain = sham_pain;\r\n\t\r\n\twalkmonster_start();\r\n};\r\n"
  },
  {
    "path": "quakec/csqctest/src/ss/soldier.qc",
    "content": "/*\r\n==============================================================================\r\n\r\nSOLDIER / PLAYER\r\n\r\n==============================================================================\r\n*/\r\n\r\n$cd id1/models/soldier3\r\n$origin 0 -6 24\r\n$base base\t\t\r\n$skin skin\r\n\r\n$frame stand1 stand2 stand3 stand4 stand5 stand6 stand7 stand8\r\n\r\n$frame death1 death2 death3 death4 death5 death6 death7 death8\r\n$frame death9 death10\r\n\r\n$frame deathc1 deathc2 deathc3 deathc4 deathc5 deathc6 deathc7 deathc8\r\n$frame deathc9 deathc10 deathc11\r\n\r\n$frame load1 load2 load3 load4 load5 load6 load7 load8 load9 load10 load11\r\n\r\n$frame pain1 pain2 pain3 pain4 pain5 pain6\r\n\r\n$frame painb1 painb2 painb3 painb4 painb5 painb6 painb7 painb8 painb9 painb10\r\n$frame painb11 painb12 painb13 painb14\r\n\r\n$frame painc1 painc2 painc3 painc4 painc5 painc6 painc7 painc8 painc9 painc10\r\n$frame painc11 painc12 painc13\r\n\r\n$frame run1 run2 run3 run4 run5 run6 run7 run8\r\n\r\n$frame shoot1 shoot2 shoot3 shoot4 shoot5 shoot6 shoot7 shoot8 shoot9\r\n\r\n$frame prowl_1 prowl_2 prowl_3 prowl_4 prowl_5 prowl_6 prowl_7 prowl_8\r\n$frame prowl_9 prowl_10 prowl_11 prowl_12 prowl_13 prowl_14 prowl_15 prowl_16\r\n$frame prowl_17 prowl_18 prowl_19 prowl_20 prowl_21 prowl_22 prowl_23 prowl_24\r\n\r\n#include \"../cs/playerframes.inc\"\r\nfloat(float f) mapsoldieframetoplayer =\r\n{\r\n\tswitch(f)\r\n\t{\r\n\tcase $stand1 .. $stand8 :\r\n\t\treturn playerframe::stand1;\r\n\tcase $death1 .. $deathc11 :\r\n\t\treturn playerframe::deatha1;\r\n\tcase $load1 .. $load11 :\r\n\t\treturn 0;\t//never used!\r\n\tcase $death1 .. $deathc11 :\r\n\t\treturn playerframe::deatha1;\r\n\tcase $pain1 .. $painc13 :\r\n\t\treturn playerframe::pain1;\r\n\tcase $prowl_1 .. $prowl_24 :\r\n\tcase $run1 .. $run8 :\r\n\t\treturn playerframe::rockrun1;\r\n\tcase $shoot1 .. $shoot9 :\r\n\t\treturn playerframe::shotatt1;\r\n\t}\r\n\treturn f;\r\n};\r\n\r\nfloat(entity to, float fl) SendSoldier =\r\n{\r\n\tWriteByte(MSG_ENTITY, CLASS_PLAYER);\r\n\tWriteByte(MSG_ENTITY, mapsoldieframetoplayer (self.frame));\t//for clientside animation selection, in a way compatable with not having csqc.\r\n\tWriteByte(MSG_ENTITY, self.angles_x*(256/360));\r\n\tWriteByte(MSG_ENTITY, self.angles_y*(256/360));\r\n\tWriteCoord(MSG_ENTITY, self.origin_x);\r\n\tWriteCoord(MSG_ENTITY, self.origin_y);\r\n\tWriteCoord(MSG_ENTITY, self.origin_z);\r\n\tWriteShort(MSG_ENTITY, self.velocity_x*64);\r\n\tWriteShort(MSG_ENTITY, self.velocity_y*64);\r\n\tWriteShort(MSG_ENTITY, self.velocity_z*64);\r\n\tWriteByte(MSG_ENTITY, 0);\r\n\r\n\treturn TRUE;\r\n};\r\n\r\n/*\r\n==============================================================================\r\nSOLDIER CODE\r\n==============================================================================\r\n*/\r\n\r\nvoid() army_fire;\r\n\r\nvoid()\tarmy_stand1\t=[\t$stand1,\tarmy_stand2\t] {ai_stand();};\r\nvoid()\tarmy_stand2\t=[\t$stand2,\tarmy_stand3\t] {ai_stand();};\r\nvoid()\tarmy_stand3\t=[\t$stand3,\tarmy_stand4\t] {ai_stand();};\r\nvoid()\tarmy_stand4\t=[\t$stand4,\tarmy_stand5\t] {ai_stand();};\r\nvoid()\tarmy_stand5\t=[\t$stand5,\tarmy_stand6\t] {ai_stand();};\r\nvoid()\tarmy_stand6\t=[\t$stand6,\tarmy_stand7\t] {ai_stand();};\r\nvoid()\tarmy_stand7\t=[\t$stand7,\tarmy_stand8\t] {ai_stand();};\r\nvoid()\tarmy_stand8\t=[\t$stand8,\tarmy_stand1\t] {ai_stand();};\r\n\r\nvoid()\tarmy_walk1\t=[\t$prowl_1,\tarmy_walk2\t] {\r\nif (random() < 0.2)\r\n\tsound (self, CHAN_VOICE, \"soldier/idle.wav\", 1, ATTN_IDLE);\r\nai_walk(1);};\r\nvoid()\tarmy_walk2\t=[\t$prowl_2,\tarmy_walk3\t] {ai_walk(1);};\r\nvoid()\tarmy_walk3\t=[\t$prowl_3,\tarmy_walk4\t] {ai_walk(1);};\r\nvoid()\tarmy_walk4\t=[\t$prowl_4,\tarmy_walk5\t] {ai_walk(1);};\r\nvoid()\tarmy_walk5\t=[\t$prowl_5,\tarmy_walk6\t] {ai_walk(2);};\r\nvoid()\tarmy_walk6\t=[\t$prowl_6,\tarmy_walk7\t] {ai_walk(3);};\r\nvoid()\tarmy_walk7\t=[\t$prowl_7,\tarmy_walk8\t] {ai_walk(4);};\r\nvoid()\tarmy_walk8\t=[\t$prowl_8,\tarmy_walk9\t] {ai_walk(4);};\r\nvoid()\tarmy_walk9\t=[\t$prowl_9,\tarmy_walk10\t] {ai_walk(2);};\r\nvoid()\tarmy_walk10\t=[\t$prowl_10,\tarmy_walk11\t] {ai_walk(2);};\r\nvoid()\tarmy_walk11\t=[\t$prowl_11,\tarmy_walk12\t] {ai_walk(2);};\r\nvoid()\tarmy_walk12\t=[\t$prowl_12,\tarmy_walk13\t] {ai_walk(1);};\r\nvoid()\tarmy_walk13\t=[\t$prowl_13,\tarmy_walk14\t] {ai_walk(0);};\r\nvoid()\tarmy_walk14\t=[\t$prowl_14,\tarmy_walk15\t] {ai_walk(1);};\r\nvoid()\tarmy_walk15\t=[\t$prowl_15,\tarmy_walk16\t] {ai_walk(1);};\r\nvoid()\tarmy_walk16\t=[\t$prowl_16,\tarmy_walk17\t] {ai_walk(1);};\r\nvoid()\tarmy_walk17\t=[\t$prowl_17,\tarmy_walk18\t] {ai_walk(3);};\r\nvoid()\tarmy_walk18\t=[\t$prowl_18,\tarmy_walk19\t] {ai_walk(3);};\r\nvoid()\tarmy_walk19\t=[\t$prowl_19,\tarmy_walk20\t] {ai_walk(3);};\r\nvoid()\tarmy_walk20\t=[\t$prowl_20,\tarmy_walk21\t] {ai_walk(3);};\r\nvoid()\tarmy_walk21\t=[\t$prowl_21,\tarmy_walk22\t] {ai_walk(2);};\r\nvoid()\tarmy_walk22\t=[\t$prowl_22,\tarmy_walk23\t] {ai_walk(1);};\r\nvoid()\tarmy_walk23\t=[\t$prowl_23,\tarmy_walk24\t] {ai_walk(1);};\r\nvoid()\tarmy_walk24\t=[\t$prowl_24,\tarmy_walk1\t] {ai_walk(1);};\r\n\r\nvoid()\tarmy_run1\t=[\t$run1,\t\tarmy_run2\t] {\r\nif (random() < 0.2)\r\n\tsound (self, CHAN_VOICE, \"soldier/idle.wav\", 1, ATTN_IDLE);\r\nai_run(11);};\r\nvoid()\tarmy_run2\t=[\t$run2,\t\tarmy_run3\t] {ai_run(15);};\r\nvoid()\tarmy_run3\t=[\t$run3,\t\tarmy_run4\t] {ai_run(10);};\r\nvoid()\tarmy_run4\t=[\t$run4,\t\tarmy_run5\t] {ai_run(10);};\r\nvoid()\tarmy_run5\t=[\t$run5,\t\tarmy_run6\t] {ai_run(8);};\r\nvoid()\tarmy_run6\t=[\t$run6,\t\tarmy_run7\t] {ai_run(15);};\r\nvoid()\tarmy_run7\t=[\t$run7,\t\tarmy_run8\t] {ai_run(10);};\r\nvoid()\tarmy_run8\t=[\t$run8,\t\tarmy_run1\t] {ai_run(8);};\r\n\r\nvoid()\tarmy_atk1\t=[\t$shoot1,\tarmy_atk2\t] {ai_face();};\r\nvoid()\tarmy_atk2\t=[\t$shoot2,\tarmy_atk3\t] {ai_face();};\r\nvoid()\tarmy_atk3\t=[\t$shoot3,\tarmy_atk4\t] {ai_face();};\r\nvoid()\tarmy_atk4\t=[\t$shoot4,\tarmy_atk5\t] {ai_face();};\r\nvoid()\tarmy_atk5\t=[\t$shoot5,\tarmy_atk6\t] {ai_face();army_fire();\r\nself.effects = self.effects | EF_MUZZLEFLASH;};\r\nvoid()\tarmy_atk6\t=[\t$shoot6,\tarmy_atk7\t] {ai_face();};\r\nvoid()\tarmy_atk7\t=[\t$shoot7,\tarmy_atk8\t] {ai_face();SUB_CheckRefire (army_atk1);};\r\nvoid()\tarmy_atk8\t=[\t$shoot8,\tarmy_atk9\t] {ai_face();};\r\nvoid()\tarmy_atk9\t=[\t$shoot9,\tarmy_run1\t] {ai_face();};\r\n\r\n\r\nvoid()\tarmy_pain1\t=[\t$pain1,\t\tarmy_pain2\t] {};\r\nvoid()\tarmy_pain2\t=[\t$pain2,\t\tarmy_pain3\t] {};\r\nvoid()\tarmy_pain3\t=[\t$pain3,\t\tarmy_pain4\t] {};\r\nvoid()\tarmy_pain4\t=[\t$pain4,\t\tarmy_pain5\t] {};\r\nvoid()\tarmy_pain5\t=[\t$pain5,\t\tarmy_pain6\t] {};\r\nvoid()\tarmy_pain6\t=[\t$pain6,\t\tarmy_run1\t] {ai_pain(1);};\r\n\r\nvoid()\tarmy_painb1\t=[\t$painb1,\tarmy_painb2\t] {};\r\nvoid()\tarmy_painb2\t=[\t$painb2,\tarmy_painb3\t] {ai_painforward(13);};\r\nvoid()\tarmy_painb3\t=[\t$painb3,\tarmy_painb4\t] {ai_painforward(9);};\r\nvoid()\tarmy_painb4\t=[\t$painb4,\tarmy_painb5\t] {};\r\nvoid()\tarmy_painb5\t=[\t$painb5,\tarmy_painb6\t] {};\r\nvoid()\tarmy_painb6\t=[\t$painb6,\tarmy_painb7\t] {};\r\nvoid()\tarmy_painb7\t=[\t$painb7,\tarmy_painb8\t] {};\r\nvoid()\tarmy_painb8\t=[\t$painb8,\tarmy_painb9\t] {};\r\nvoid()\tarmy_painb9\t=[\t$painb9,\tarmy_painb10] {};\r\nvoid()\tarmy_painb10=[\t$painb10,\tarmy_painb11] {};\r\nvoid()\tarmy_painb11=[\t$painb11,\tarmy_painb12] {};\r\nvoid()\tarmy_painb12=[\t$painb12,\tarmy_painb13] {ai_pain(2);};\r\nvoid()\tarmy_painb13=[\t$painb13,\tarmy_painb14] {};\r\nvoid()\tarmy_painb14=[\t$painb14,\tarmy_run1\t] {};\r\n\r\nvoid()\tarmy_painc1\t=[\t$painc1,\tarmy_painc2\t] {};\r\nvoid()\tarmy_painc2\t=[\t$painc2,\tarmy_painc3\t] {ai_pain(1);};\r\nvoid()\tarmy_painc3\t=[\t$painc3,\tarmy_painc4\t] {};\r\nvoid()\tarmy_painc4\t=[\t$painc4,\tarmy_painc5\t] {};\r\nvoid()\tarmy_painc5\t=[\t$painc5,\tarmy_painc6\t] {ai_painforward(1);};\r\nvoid()\tarmy_painc6\t=[\t$painc6,\tarmy_painc7\t] {ai_painforward(1);};\r\nvoid()\tarmy_painc7\t=[\t$painc7,\tarmy_painc8\t] {};\r\nvoid()\tarmy_painc8\t=[\t$painc8,\tarmy_painc9\t] {ai_pain(1);};\r\nvoid()\tarmy_painc9\t=[\t$painc9,\tarmy_painc10] {ai_painforward(4);};\r\nvoid()\tarmy_painc10=[\t$painc10,\tarmy_painc11] {ai_painforward(3);};\r\nvoid()\tarmy_painc11=[\t$painc11,\tarmy_painc12] {ai_painforward(6);};\r\nvoid()\tarmy_painc12=[\t$painc12,\tarmy_painc13] {ai_painforward(8);};\r\nvoid()\tarmy_painc13=[\t$painc13,\tarmy_run1] {};\r\n\r\nvoid(entity attacker, float damage)\tarmy_pain =\r\n{\r\n\tlocal float r;\r\n\t\r\n\tif (self.pain_finished > time)\r\n\t\treturn;\r\n\r\n\tr = random();\r\n\r\n\tif (r < 0.2)\r\n\t{\r\n\t\tself.pain_finished = time + 0.6;\r\n\t\tarmy_pain1 ();\r\n\t\tsound (self, CHAN_VOICE, \"soldier/pain1.wav\", 1, ATTN_NORM);\r\n\t}\r\n\telse if (r < 0.6)\r\n\t{\r\n\t\tself.pain_finished = time + 1.1;\r\n\t\tarmy_painb1 ();\r\n\t\tsound (self, CHAN_VOICE, \"soldier/pain2.wav\", 1, ATTN_NORM);\r\n\t}\r\n\telse\r\n\t{\r\n\t\tself.pain_finished = time + 1.1;\r\n\t\tarmy_painc1 ();\r\n\t\tsound (self, CHAN_VOICE, \"soldier/pain2.wav\", 1, ATTN_NORM);\r\n\t}\r\n};\r\n\r\n\r\nvoid() army_fire =\r\n{\r\n\tlocal\tvector\tdir;\r\n\tlocal\tentity\ten;\r\n\t\r\n\tai_face();\r\n\t\r\n\tsound (self, CHAN_WEAPON, \"soldier/sattck1.wav\", 1, ATTN_NORM);\t\r\n\r\n// fire somewhat behind the player, so a dodging player is harder to hit\r\n\ten = self.enemy;\r\n\t\r\n\tdir = en.origin - en.velocity*0.2;\r\n\tdir = normalize (dir - self.origin);\r\n\t\r\n\tFireBullets (4, dir, '0.1 0.1 0');\r\n};\r\n\r\n\r\n\r\nvoid()\tarmy_die1\t=[\t$death1,\tarmy_die2\t] {};\r\nvoid()\tarmy_die2\t=[\t$death2,\tarmy_die3\t] {};\r\nvoid()\tarmy_die3\t=[\t$death3,\tarmy_die4\t]\r\n{self.solid = SOLID_NOT;self.ammo_shells = 5;DropBackpack();};\r\nvoid()\tarmy_die4\t=[\t$death4,\tarmy_die5\t] {};\r\nvoid()\tarmy_die5\t=[\t$death5,\tarmy_die6\t] {};\r\nvoid()\tarmy_die6\t=[\t$death6,\tarmy_die7\t] {};\r\nvoid()\tarmy_die7\t=[\t$death7,\tarmy_die8\t] {};\r\nvoid()\tarmy_die8\t=[\t$death8,\tarmy_die9\t] {};\r\nvoid()\tarmy_die9\t=[\t$death9,\tarmy_die10\t] {};\r\nvoid()\tarmy_die10\t=[\t$death10,\tarmy_die10\t] {};\r\n\r\nvoid()\tarmy_cdie1\t=[\t$deathc1,\tarmy_cdie2\t] {};\r\nvoid()\tarmy_cdie2\t=[\t$deathc2,\tarmy_cdie3\t] {ai_back(5);};\r\nvoid()\tarmy_cdie3\t=[\t$deathc3,\tarmy_cdie4\t]\r\n{self.solid = SOLID_NOT;self.ammo_shells = 5;DropBackpack();ai_back(4);};\r\nvoid()\tarmy_cdie4\t=[\t$deathc4,\tarmy_cdie5\t] {ai_back(13);};\r\nvoid()\tarmy_cdie5\t=[\t$deathc5,\tarmy_cdie6\t] {ai_back(3);};\r\nvoid()\tarmy_cdie6\t=[\t$deathc6,\tarmy_cdie7\t] {ai_back(4);};\r\nvoid()\tarmy_cdie7\t=[\t$deathc7,\tarmy_cdie8\t] {};\r\nvoid()\tarmy_cdie8\t=[\t$deathc8,\tarmy_cdie9\t] {};\r\nvoid()\tarmy_cdie9\t=[\t$deathc9,\tarmy_cdie10\t] {};\r\nvoid()\tarmy_cdie10\t=[\t$deathc10,\tarmy_cdie11\t] {};\r\nvoid()\tarmy_cdie11\t=[\t$deathc11,\tarmy_cdie11\t] {};\r\n\r\n\r\nvoid() army_die =\r\n{\r\n// check for gib\r\n\tif (self.health < -35)\r\n\t{\r\n\t\tThrowCSQCGibs(GIB_SOLDIER);\r\n\t\treturn;\r\n\t}\r\n\r\n// regular death\r\n\tsound (self, CHAN_VOICE, \"soldier/death1.wav\", 1, ATTN_NORM);\r\n\tif (random() < 0.5)\r\n\t\tarmy_die1 ();\r\n\telse\r\n\t\tarmy_cdie1 ();\r\n};\r\n\r\n\r\n/*QUAKED monster_army (1 0 0) (-16 -16 -24) (16 16 40) Ambush\r\n*/\r\nvoid() monster_army =\r\n{\t\r\n\tif (deathmatch)\r\n\t{\r\n\t\tremove(self);\r\n\t\treturn;\r\n\t}\r\n\tprecache_model (\"progs/soldier.mdl\");\r\n\tprecache_model (\"progs/h_guard.mdl\");\r\n\tprecache_model (\"progs/gib1.mdl\");\r\n\tprecache_model (\"progs/gib2.mdl\");\r\n\tprecache_model (\"progs/gib3.mdl\");\r\n\r\n\tprecache_sound (\"soldier/death1.wav\");\r\n\tprecache_sound (\"soldier/idle.wav\");\r\n\tprecache_sound (\"soldier/pain1.wav\");\r\n\tprecache_sound (\"soldier/pain2.wav\");\r\n\tprecache_sound (\"soldier/sattck1.wav\");\r\n\tprecache_sound (\"soldier/sight1.wav\");\r\n\r\n\tprecache_sound (\"player/udeath.wav\");\t\t// gib death\r\n\r\n\r\n\tself.solid = SOLID_SLIDEBOX;\r\n\tself.movetype = MOVETYPE_STEP;\r\n\r\n\tsetmodel (self, \"progs/soldier.mdl\");\r\n\r\n\tsetsize (self, '-16 -16 -24', '16 16 40');\r\n\tself.health = 30;\r\n\r\n\tself.th_stand = army_stand1;\r\n\tself.th_walk = army_walk1;\r\n\tself.th_run = army_run1;\r\n\tself.th_missile = army_atk1;\r\n\tself.th_pain = army_pain;\r\n\tself.th_die = army_die;\r\n\r\n\tself.SendEntity = SendSoldier;\r\n\r\n\twalkmonster_start ();\r\n};\r\n"
  },
  {
    "path": "quakec/csqctest/src/ss/subs.qc",
    "content": "\r\n\r\nvoid() SUB_Null = {};\r\n\r\nvoid() SUB_Remove = {remove(self);};\r\n\r\n\r\n/*\r\nQuakeEd only writes a single float for angles (bad idea), so up and down are\r\njust constant angles.\r\n*/\r\nvoid() SetMovedir =\r\n{\r\n\tif (self.angles == '0 -1 0')\r\n\t\tself.movedir = '0 0 1';\r\n\telse if (self.angles == '0 -2 0')\r\n\t\tself.movedir = '0 0 -1';\r\n\telse\r\n\t{\r\n\t\tmakevectors (self.angles);\r\n\t\tself.movedir = v_forward;\r\n\t}\r\n\t\r\n\tself.angles = '0 0 0';\r\n};\r\n\r\n/*\r\n================\r\nInitTrigger\r\n================\r\n*/\r\nvoid() InitTrigger =\r\n{\r\n// trigger angles are used for one-way touches.  An angle of 0 is assumed\r\n// to mean no restrictions, so use a yaw of 360 instead.\r\n\tif (self.angles != '0 0 0')\r\n\t\tSetMovedir ();\r\n\tself.solid = SOLID_TRIGGER;\r\n\tsetmodel (self, self.model);\t// set size and link into world\r\n\tself.movetype = MOVETYPE_NONE;\r\n\tself.modelindex = 0;\r\n\tself.model = \"\";\r\n};\r\n\r\n/*\r\n=============\r\nSUB_CalcMove\r\n\r\ncalculate self.velocity and self.nextthink to reach dest from\r\nself.origin traveling at speed\r\n===============\r\n*/\r\nvoid(entity ent, vector tdest, float tspeed, void() func) SUB_CalcMoveEnt =\r\n{\r\nlocal entity\tstemp;\r\n\tstemp = self;\r\n\tself = ent;\r\n\r\n\tSUB_CalcMove (tdest, tspeed, func);\r\n\tself = stemp;\r\n};\r\n\r\nvoid(vector tdest, float tspeed, void() func) SUB_CalcMove =\r\n{\r\nlocal vector\tvdestdelta;\r\nlocal float\t\tlen, traveltime;\r\n\r\n\tif (!tspeed)\r\n\t\tobjerror(\"No speed is defined!\");\r\n\r\n\tself.think1 = func;\r\n\tself.finaldest = tdest;\r\n\tself.think = SUB_CalcMoveDone;\r\n\r\n\tif (tdest == self.origin)\r\n\t{\r\n\t\tself.velocity = '0 0 0';\r\n\t\tself.nextthink = self.ltime + 0.1;\r\n\t\treturn;\r\n\t}\r\n\t\t\r\n// set destdelta to the vector needed to move\r\n\tvdestdelta = tdest - self.origin;\r\n\t\r\n// calculate length of vector\r\n\tlen = vlen (vdestdelta);\r\n\t\r\n// divide by speed to get time to reach dest\r\n\ttraveltime = len / tspeed;\r\n\r\n\tif (traveltime < 0.1)\r\n\t{\r\n\t\tself.velocity = '0 0 0';\r\n\t\tself.nextthink = self.ltime + 0.1;\r\n\t\treturn;\r\n\t}\r\n\t\r\n// set nextthink to trigger a think when dest is reached\r\n\tself.nextthink = self.ltime + traveltime;\r\n\r\n// scale the destdelta vector by the time spent traveling to get velocity\r\n\tself.velocity = vdestdelta * (1/traveltime);\t// qcc won't take vec/float\t\r\n};\r\n\r\n/*\r\n============\r\nAfter moving, set origin to exact final destination\r\n============\r\n*/\r\nvoid()  SUB_CalcMoveDone =\r\n{\r\n\tsetorigin(self, self.finaldest);\r\n\tself.velocity = '0 0 0';\r\n\tself.nextthink = -1;\r\n\tif (self.think1)\r\n\t\tself.think1();\r\n};\r\n\r\n\r\n/*\r\n=============\r\nSUB_CalcAngleMove\r\n\r\ncalculate self.avelocity and self.nextthink to reach destangle from\r\nself.angles rotating \r\n\r\nThe calling function should make sure self.think is valid\r\n===============\r\n*/\r\nvoid(entity ent, vector destangle, float tspeed, void() func) SUB_CalcAngleMoveEnt =\r\n{\r\nlocal entity\t\tstemp;\r\n\tstemp = self;\r\n\tself = ent;\r\n\tSUB_CalcAngleMove (destangle, tspeed, func);\r\n\tself = stemp;\r\n};\r\n\r\nvoid(vector destangle, float tspeed, void() func) SUB_CalcAngleMove =\r\n{\r\nlocal vector\tdestdelta;\r\nlocal float\t\tlen, traveltime;\r\n\r\n\tif (!tspeed)\r\n\t\tobjerror(\"No speed is defined!\");\r\n\t\t\r\n// set destdelta to the vector needed to move\r\n\tdestdelta = destangle - self.angles;\r\n\t\r\n// calculate length of vector\r\n\tlen = vlen (destdelta);\r\n\t\r\n// divide by speed to get time to reach dest\r\n\ttraveltime = len / tspeed;\r\n\r\n// set nextthink to trigger a think when dest is reached\r\n\tself.nextthink = self.ltime + traveltime;\r\n\r\n// scale the destdelta vector by the time spent traveling to get velocity\r\n\tself.avelocity = destdelta * (1 / traveltime);\r\n\t\r\n\tself.think1 = func;\r\n\tself.finalangle = destangle;\r\n\tself.think = SUB_CalcAngleMoveDone;\r\n};\r\n\r\n/*\r\n============\r\nAfter rotating, set angle to exact final angle\r\n============\r\n*/\r\nvoid() SUB_CalcAngleMoveDone =\r\n{\r\n\tself.angles = self.finalangle;\r\n\tself.avelocity = '0 0 0';\r\n\tself.nextthink = -1;\r\n\tif (self.think1)\r\n\t\tself.think1();\r\n};\r\n\r\n\r\n//=============================================================================\r\n\r\nvoid() DelayThink =\r\n{\r\n\tactivator = self.enemy;\r\n\tSUB_UseTargets ();\r\n\tremove(self);\r\n};\r\n\r\n/*\r\n==============================\r\nSUB_UseTargets\r\n\r\nthe global \"activator\" should be set to the entity that initiated the firing.\r\n\r\nIf self.delay is set, a DelayedUse entity will be created that will actually\r\ndo the SUB_UseTargets after that many seconds have passed.\r\n\r\nCenterprints any self.message to the activator.\r\n\r\nRemoves all entities with a targetname that match self.killtarget,\r\nand removes them, so some events can remove other triggers.\r\n\r\nSearch for (string)targetname in all entities that\r\nmatch (string)self.target and call their .use function\r\n\r\n==============================\r\n*/\r\nvoid() SUB_UseTargets =\r\n{\r\n\tlocal entity t, stemp, otemp, act;\r\n\r\n//\r\n// check for a delay\r\n//\r\n\tif (self.delay)\r\n\t{\r\n\t// create a temp object to fire at a later time\r\n\t\tt = spawn();\r\n\t\tt.classname = \"DelayedUse\";\r\n\t\tt.nextthink = time + self.delay;\r\n\t\tt.think = DelayThink;\r\n\t\tt.enemy = activator;\r\n\t\tt.message = self.message;\r\n\t\tt.killtarget = self.killtarget;\r\n\t\tt.target = self.target;\r\n\t\treturn;\r\n\t}\r\n\t\r\n\t\r\n//\r\n// print the message\r\n//\r\n\tif (activator.classname == \"player\" && self.message != \"\")\r\n\t{\r\n\t\tcenterprint (activator, self.message);\r\n\t\tif (!self.noise)\r\n\t\t\tsound (activator, CHAN_VOICE, \"misc/talk.wav\", 1, ATTN_NORM);\r\n\t}\r\n\r\n//\r\n// kill the killtagets\r\n//\r\n\tif (self.killtarget != \"\")\r\n\t{\r\n\t\tt = world;\r\n\t\tdo\r\n\t\t{\r\n\t\t\tt = find (t, targetname, self.killtarget);\r\n\t\t\tif (!t)\r\n\t\t\t\treturn;\r\n\t\t\tremove (t);\r\n\t\t} while ( 1 );\r\n\t}\r\n\t\r\n//\r\n// fire targets\r\n//\r\n\tif (self.target != \"\")\r\n\t{\r\n\t\tact = activator;\r\n\t\tt = world;\r\n\t\tdo\r\n\t\t{\r\n\t\t\tt = find (t, targetname, self.target);\r\n\t\t\tif (!t)\r\n\t\t\t{\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\t\t\tstemp = self;\r\n\t\t\totemp = other;\r\n\t\t\tself = t;\r\n\t\t\tother = stemp;\r\n\t\t\tif (self.use != SUB_Null)\r\n\t\t\t{\r\n\t\t\t\tif (self.use)\r\n\t\t\t\t\tself.use ();\r\n\t\t\t}\r\n\t\t\tself = stemp;\r\n\t\t\tother = otemp;\r\n\t\t\tactivator = act;\r\n\t\t} while ( 1 );\r\n\t}\r\n\t\r\n\r\n};\r\n\r\n\r\n/*\r\n\r\nin nightmare mode, all attack_finished times become 0\r\nsome monsters refire twice automatically\r\n\r\n*/\r\n\r\nvoid(float normal) SUB_AttackFinished =\r\n{\r\n\tself.cnt = 0;\t\t// refire count for nightmare\r\n\tif (skill != 3)\r\n\t\tself.attack_finished = time + normal;\r\n};\r\n\r\nfloat (entity targ) visible;\r\n\r\nvoid (void() thinkst) SUB_CheckRefire =\r\n{\r\n\tif (skill != 3)\r\n\t\treturn;\r\n\tif (self.cnt == 1)\r\n\t\treturn;\r\n\tif (!visible (self.enemy))\r\n\t\treturn;\r\n\tself.cnt = 1;\r\n\tself.think = thinkst;\r\n};\r\n"
  },
  {
    "path": "quakec/csqctest/src/ss/tarbaby.qc",
    "content": "/*\n==============================================================================\n\nBLOB\n\n==============================================================================\n*/\n\n$cd id1/models/tarbaby\n$origin 0 0 24\n$base base\t\t\n\n$skin skin\n\n$frame walk1 walk2 walk3 walk4  walk5 walk6 walk7 walk8 walk9 walk10\n$frame walk11 walk12 walk13 walk14 walk15 walk16 walk17 walk18 walk19\n$frame walk20 walk21 walk22 walk23 walk24 walk25\n\n$frame run1 run2 run3 run4 run5 run6  run7 run8 run9 run10 run11 run12 run13\n$frame run14 run15 run16 run17 run18 run19 run20 run21 run22 run23\n$frame run24 run25\n\n$frame jump1 jump2 jump3 jump4 jump5 jump6\n\n$frame fly1 fly2 fly3 fly4\n\n$frame exp\n\nvoid()\ttbaby_stand1\t=[\t$walk1,\t\ttbaby_stand1\t] {ai_stand();};\n\nvoid()\ttbaby_hang1\t\t=[\t$walk1,\t\ttbaby_hang1\t] {ai_stand();};\n\nvoid()\ttbaby_walk1\t=[\t$walk1,\t\ttbaby_walk2\t] {ai_turn();};\nvoid()\ttbaby_walk2\t=[\t$walk2,\t\ttbaby_walk3\t] {ai_turn();};\nvoid()\ttbaby_walk3\t=[\t$walk3,\t\ttbaby_walk4\t] {ai_turn();};\nvoid()\ttbaby_walk4\t=[\t$walk4,\t\ttbaby_walk5\t] {ai_turn();};\nvoid()\ttbaby_walk5\t=[\t$walk5,\t\ttbaby_walk6\t] {ai_turn();};\nvoid()\ttbaby_walk6\t=[\t$walk6,\t\ttbaby_walk7\t] {ai_turn();};\nvoid()\ttbaby_walk7\t=[\t$walk7,\t\ttbaby_walk8\t] {ai_turn();};\nvoid()\ttbaby_walk8\t=[\t$walk8,\t\ttbaby_walk9\t] {ai_turn();};\nvoid()\ttbaby_walk9\t=[\t$walk9,\t\ttbaby_walk10\t] {ai_turn();};\nvoid()\ttbaby_walk10\t=[\t$walk10,\t\ttbaby_walk11\t] {ai_turn();};\nvoid()\ttbaby_walk11\t=[\t$walk11,\t\ttbaby_walk12\t] {ai_walk(2);};\nvoid()\ttbaby_walk12\t=[\t$walk12,\t\ttbaby_walk13\t] {ai_walk(2);};\nvoid()\ttbaby_walk13\t=[\t$walk13,\t\ttbaby_walk14\t] {ai_walk(2);};\nvoid()\ttbaby_walk14\t=[\t$walk14,\t\ttbaby_walk15\t] {ai_walk(2);};\nvoid()\ttbaby_walk15\t=[\t$walk15,\t\ttbaby_walk16\t] {ai_walk(2);};\nvoid()\ttbaby_walk16\t=[\t$walk16,\t\ttbaby_walk17\t] {ai_walk(2);};\nvoid()\ttbaby_walk17\t=[\t$walk17,\t\ttbaby_walk18\t] {ai_walk(2);};\nvoid()\ttbaby_walk18\t=[\t$walk18,\t\ttbaby_walk19\t] {ai_walk(2);};\nvoid()\ttbaby_walk19\t=[\t$walk19,\t\ttbaby_walk20\t] {ai_walk(2);};\nvoid()\ttbaby_walk20\t=[\t$walk20,\t\ttbaby_walk21\t] {ai_walk(2);};\nvoid()\ttbaby_walk21\t=[\t$walk21,\t\ttbaby_walk22\t] {ai_walk(2);};\nvoid()\ttbaby_walk22\t=[\t$walk22,\t\ttbaby_walk23\t] {ai_walk(2);};\nvoid()\ttbaby_walk23\t=[\t$walk23,\t\ttbaby_walk24\t] {ai_walk(2);};\nvoid()\ttbaby_walk24\t=[\t$walk24,\t\ttbaby_walk25\t] {ai_walk(2);};\nvoid()\ttbaby_walk25\t=[\t$walk25,\t\ttbaby_walk1\t] {ai_walk(2);};\n\nvoid()\ttbaby_run1\t=[\t$run1,\t\ttbaby_run2\t] {ai_face();};\nvoid()\ttbaby_run2\t=[\t$run2,\t\ttbaby_run3\t] {ai_face();};\nvoid()\ttbaby_run3\t=[\t$run3,\t\ttbaby_run4\t] {ai_face();};\nvoid()\ttbaby_run4\t=[\t$run4,\t\ttbaby_run5\t] {ai_face();};\nvoid()\ttbaby_run5\t=[\t$run5,\t\ttbaby_run6\t] {ai_face();};\nvoid()\ttbaby_run6\t=[\t$run6,\t\ttbaby_run7\t] {ai_face();};\nvoid()\ttbaby_run7\t=[\t$run7,\t\ttbaby_run8\t] {ai_face();};\nvoid()\ttbaby_run8\t=[\t$run8,\t\ttbaby_run9\t] {ai_face();};\nvoid()\ttbaby_run9\t=[\t$run9,\t\ttbaby_run10\t] {ai_face();};\nvoid()\ttbaby_run10\t=[\t$run10,\t\ttbaby_run11\t] {ai_face();};\nvoid()\ttbaby_run11\t=[\t$run11,\t\ttbaby_run12\t] {ai_run(2);};\nvoid()\ttbaby_run12\t=[\t$run12,\t\ttbaby_run13\t] {ai_run(2);};\nvoid()\ttbaby_run13\t=[\t$run13,\t\ttbaby_run14\t] {ai_run(2);};\nvoid()\ttbaby_run14\t=[\t$run14,\t\ttbaby_run15\t] {ai_run(2);};\nvoid()\ttbaby_run15\t=[\t$run15,\t\ttbaby_run16\t] {ai_run(2);};\nvoid()\ttbaby_run16\t=[\t$run16,\t\ttbaby_run17\t] {ai_run(2);};\nvoid()\ttbaby_run17\t=[\t$run17,\t\ttbaby_run18\t] {ai_run(2);};\nvoid()\ttbaby_run18\t=[\t$run18,\t\ttbaby_run19\t] {ai_run(2);};\nvoid()\ttbaby_run19\t=[\t$run19,\t\ttbaby_run20\t] {ai_run(2);};\nvoid()\ttbaby_run20\t=[\t$run20,\t\ttbaby_run21\t] {ai_run(2);};\nvoid()\ttbaby_run21\t=[\t$run21,\t\ttbaby_run22\t] {ai_run(2);};\nvoid()\ttbaby_run22\t=[\t$run22,\t\ttbaby_run23\t] {ai_run(2);};\nvoid()\ttbaby_run23\t=[\t$run23,\t\ttbaby_run24\t] {ai_run(2);};\nvoid()\ttbaby_run24\t=[\t$run24,\t\ttbaby_run25\t] {ai_run(2);};\nvoid()\ttbaby_run25\t=[\t$run25,\t\ttbaby_run1\t] {ai_run(2);};\n\n\n//============================================================================\n\n\nvoid()\ttbaby_jump1;\n\nvoid()\tTar_JumpTouch =\n{\n\tlocal\tfloat\tldmg;\n\n\tif (other.takedamage && other.classname != self.classname)\n\t{\n\t\tif ( vlen(self.velocity) > 400 )\n\t\t{\n\t\t\tldmg = 10 + 10*random();\n\t\t\tT_Damage (other, self, self, ldmg);\t\n\t\t\tsound (self, CHAN_WEAPON, \"blob/hit1.wav\", 1, ATTN_NORM);\n\t\t}\n\t}\n\telse\n\t\tsound (self, CHAN_WEAPON, \"blob/land1.wav\", 1, ATTN_NORM);\n\n\n\tif (!checkbottom(self))\n\t{\n\t\tif (self.flags & FL_ONGROUND)\n\t\t{\t// jump randomly to not get hung up\n//dprint (\"popjump\\n\");\n\tself.touch = SUB_Null;\n\tself.think = tbaby_run1;\n\tself.movetype = MOVETYPE_STEP;\n\tself.nextthink = time + 0.1;\n\n//\t\t\tself.velocity_x = (random() - 0.5) * 600;\n//\t\t\tself.velocity_y = (random() - 0.5) * 600;\n//\t\t\tself.velocity_z = 200;\n//\t\t\tself.flags = self.flags - FL_ONGROUND;\n\t\t}\n\t\treturn;\t// not on ground yet\n\t}\n\n\tself.touch = SUB_Null;\n\tself.think = tbaby_jump1;\n\tself.nextthink = time + 0.1;\n};\n\nvoid()\ttbaby_jump5;\n\nvoid()\ttbaby_fly1\t\t=[\t$fly1,\ttbaby_fly2\t] {};\nvoid()\ttbaby_fly2\t\t=[\t$fly2,\ttbaby_fly3\t] {};\nvoid()\ttbaby_fly3\t\t=[\t$fly3,\ttbaby_fly4\t] {};\nvoid()\ttbaby_fly4\t\t=[\t$fly4,\ttbaby_fly1\t] {\nself.cnt = self.cnt + 1;\nif (self.cnt == 4)\n{\n//dprint (\"spawn hop\\n\");\ntbaby_jump5 ();\n}\n};\n\nvoid()\ttbaby_jump1\t\t=[\t$jump1,\ttbaby_jump2\t\t] {ai_face();};\nvoid()\ttbaby_jump2\t\t=[\t$jump2,\ttbaby_jump3\t\t] {ai_face();};\nvoid()\ttbaby_jump3\t\t=[\t$jump3,\ttbaby_jump4\t\t] {ai_face();};\nvoid()\ttbaby_jump4\t\t=[\t$jump4,\ttbaby_jump5\t\t] {ai_face();};\nvoid()\ttbaby_jump5\t\t=[\t$jump5,\ttbaby_jump6\t\t]\n{\t\n\tself.movetype = MOVETYPE_BOUNCE;\n\tself.touch = Tar_JumpTouch;\n\tmakevectors (self.angles);\n\tself.origin_z = self.origin_z + 1;\n\tself.velocity = v_forward * 600 + '0 0 200';\n\tself.velocity_z = self.velocity_z + random()*150;\n\tif (self.flags & FL_ONGROUND)\n\t\tself.flags = self.flags - FL_ONGROUND;\n\tself.cnt = 0;\n};\nvoid()\ttbaby_jump6\t=[\t$jump6,tbaby_fly1\t] {};\n\n\n\n//=============================================================================\n\nvoid()\ttbaby_die1\t=[\t$exp,\t\ttbaby_die2\t] {\nself.takedamage = DAMAGE_NO;\n};\nvoid()\ttbaby_die2\t=[\t$exp,\t\ttbaby_run1\t] \n{\n\tT_RadiusDamage (self, self, 120, world);\n\n\tsound (self, CHAN_VOICE, \"blob/death1.wav\", 1, ATTN_NORM);\n\tself.origin = self.origin - 8*normalize(self.velocity);\n\n\tWriteByte (MSG_BROADCAST, SVC_TEMPENTITY);\n\tWriteByte (MSG_BROADCAST, TE_TAREXPLOSION);\n\tWriteCoord (MSG_BROADCAST, self.origin_x);\n\tWriteCoord (MSG_BROADCAST, self.origin_y);\n\tWriteCoord (MSG_BROADCAST, self.origin_z);\n\t\n\tBecomeExplosion ();\n};\n\n//=============================================================================\n\n\n/*QUAKED monster_tarbaby (1 0 0) (-16 -16 -24) (16 16 24) Ambush\n*/\nvoid() monster_tarbaby =\n{\n\tif (deathmatch)\n\t{\n\t\tremove(self);\n\t\treturn;\n\t}\n\tprecache_model2 (\"progs/tarbaby.mdl\");\n\n\tprecache_sound2 (\"blob/death1.wav\");\n\tprecache_sound2 (\"blob/hit1.wav\");\n\tprecache_sound2 (\"blob/land1.wav\");\n\tprecache_sound2 (\"blob/sight1.wav\");\n\t\n\tself.solid = SOLID_SLIDEBOX;\n\tself.movetype = MOVETYPE_STEP;\n\n\tsetmodel (self, \"progs/tarbaby.mdl\");\n\n\tsetsize (self, '-16 -16 -24', '16 16 40');\n\tself.health = 80;\n\n\tself.th_stand = tbaby_stand1;\n\tself.th_walk = tbaby_walk1;\n\tself.th_run = tbaby_run1;\n\tself.th_missile = tbaby_jump1;\n\tself.th_melee = tbaby_jump1;\n\tself.th_die = tbaby_die1;\n\t\n\twalkmonster_start ();\n};\n\n"
  },
  {
    "path": "quakec/csqctest/src/ss/triggers.qc",
    "content": "\r\nentity s;\r\n\r\n\r\nvoid() trigger_reactivate =\r\n{\r\n\tself.solid = SOLID_TRIGGER;\r\n};\r\n\r\n//=============================================================================\r\n\r\nfloat\tSPAWNFLAG_NOMESSAGE = 1;\r\nfloat\tSPAWNFLAG_NOTOUCH = 1;\r\n\r\n// the wait time has passed, so set back up for another activation\r\nvoid() multi_wait =\r\n{\r\n\tif (self.max_health)\r\n\t{\r\n\t\tself.health = self.max_health;\r\n\t\tself.takedamage = DAMAGE_YES;\r\n\t\tself.solid = SOLID_BBOX;\r\n\t}\r\n};\r\n\r\n\r\n// the trigger was just touched/killed/used\r\n// self.enemy should be set to the activator so it can be held through a delay\r\n// so wait for the delay time before firing\r\nvoid() multi_trigger =\r\n{\r\n\tif (self.nextthink > time)\r\n\t{\r\n\t\treturn;\t\t// allready been triggered\r\n\t}\r\n\r\n\tif (self.classname == \"trigger_secret\")\r\n\t{\r\n\t\tif (self.enemy.classname != \"player\")\r\n\t\t\treturn;\r\n\t\tfound_secrets = found_secrets + 1;\r\n\t\tWriteByte (MSG_ALL, SVC_FOUNDSECRET);\r\n\t}\r\n\r\n\tif (self.noise!=\"\")\r\n\t\tsound (self, CHAN_VOICE, self.noise, 1, ATTN_NORM);\r\n\r\n// don't trigger again until reset\r\n\tself.takedamage = DAMAGE_NO;\r\n\r\n\tactivator = self.enemy;\r\n\t\r\n\tSUB_UseTargets();\r\n\r\n\tif (self.wait > 0)\t\r\n\t{\r\n\t\tself.think = multi_wait;\r\n\t\tself.nextthink = time + self.wait;\r\n\t}\r\n\telse\r\n\t{\t// we can't just remove (self) here, because this is a touch function\r\n\t\t// called wheil C code is looping through area links...\r\n\t\tself.touch = SUB_Null;\r\n\t\tself.nextthink = time + 0.1;\r\n\t\tself.think = SUB_Remove;\r\n\t}\r\n};\r\n\r\nvoid() multi_killed =\r\n{\r\n\tself.enemy = damage_attacker;\r\n\tmulti_trigger();\r\n};\r\n\r\nvoid() multi_use =\r\n{\r\n\tself.enemy = activator;\r\n\tmulti_trigger();\r\n};\r\n\r\nvoid() multi_touch =\r\n{\r\n\tif (other.classname != \"player\")\r\n\t\treturn;\r\n\t\r\n// if the trigger has an angles field, check player's facing direction\r\n\tif (self.movedir != '0 0 0')\r\n\t{\r\n\t\tmakevectors (other.angles);\r\n\t\tif (v_forward * self.movedir < 0)\r\n\t\t\treturn;\t\t// not facing the right way\r\n\t}\r\n\t\r\n\tself.enemy = other;\r\n\tmulti_trigger ();\r\n};\r\n\r\n/*QUAKED trigger_multiple (.5 .5 .5) ? notouch\r\nVariable sized repeatable trigger.  Must be targeted at one or more entities.  If \"health\" is set, the trigger must be killed to activate each time.\r\nIf \"delay\" is set, the trigger waits some time after activating before firing.\r\n\"wait\" : Seconds between triggerings. (.2 default)\r\nIf notouch is set, the trigger is only fired by other entities, not by touching.\r\nNOTOUCH has been obsoleted by trigger_relay!\r\nsounds\r\n1)\tsecret\r\n2)\tbeep beep\r\n3)\tlarge switch\r\n4)\r\nset \"message\" to text string\r\n*/\r\nvoid() trigger_multiple =\r\n{\r\n\tif (self.sounds == 1)\r\n\t{\r\n\t\tprecache_sound (\"misc/secret.wav\");\r\n\t\tself.noise = \"misc/secret.wav\";\r\n\t}\r\n\telse if (self.sounds == 2)\r\n\t{\r\n\t\tprecache_sound (\"misc/talk.wav\");\r\n\t\tself.noise = \"misc/talk.wav\";\r\n\t}\r\n\telse if (self.sounds == 3)\r\n\t{\r\n\t\tprecache_sound (\"misc/trigger1.wav\");\r\n\t\tself.noise = \"misc/trigger1.wav\";\r\n\t}\r\n\t\r\n\tif (!self.wait)\r\n\t\tself.wait = 0.2;\r\n\tself.use = multi_use;\r\n\r\n\tInitTrigger ();\r\n\r\n\tif (self.health)\r\n\t{\r\n\t\tif (self.spawnflags & SPAWNFLAG_NOTOUCH)\r\n\t\t\tobjerror (\"health and notouch don't make sense\\n\");\r\n\t\tself.max_health = self.health;\r\n\t\tself.th_die = multi_killed;\r\n\t\tself.takedamage = DAMAGE_YES;\r\n\t\tself.solid = SOLID_BBOX;\r\n\t\tsetorigin (self, self.origin);\t// make sure it links into the world\r\n\t}\r\n\telse\r\n\t{\r\n\t\tif ( !(self.spawnflags & SPAWNFLAG_NOTOUCH) )\r\n\t\t{\r\n\t\t\tself.touch = multi_touch;\r\n\t\t}\r\n\t}\r\n};\r\n\r\n\r\n/*QUAKED trigger_once (.5 .5 .5) ? notouch\r\nVariable sized trigger. Triggers once, then removes itself.  You must set the key \"target\" to the name of another object in the level that has a matching\r\n\"targetname\".  If \"health\" is set, the trigger must be killed to activate.\r\nIf notouch is set, the trigger is only fired by other entities, not by touching.\r\nif \"killtarget\" is set, any objects that have a matching \"target\" will be removed when the trigger is fired.\r\nif \"angle\" is set, the trigger will only fire when someone is facing the direction of the angle.  Use \"360\" for an angle of 0.\r\nsounds\r\n1)\tsecret\r\n2)\tbeep beep\r\n3)\tlarge switch\r\n4)\r\nset \"message\" to text string\r\n*/\r\nvoid() trigger_once =\r\n{\r\n\tself.wait = -1;\r\n\ttrigger_multiple();\r\n};\r\n\r\n//=============================================================================\r\n\r\n/*QUAKED trigger_relay (.5 .5 .5) (-8 -8 -8) (8 8 8)\r\nThis fixed size trigger cannot be touched, it can only be fired by other events.  It can contain killtargets, targets, delays, and messages.\r\n*/\r\nvoid() trigger_relay =\r\n{\r\n\tself.use = SUB_UseTargets;\r\n};\r\n\r\n\r\n//=============================================================================\r\n\r\n/*QUAKED trigger_secret (.5 .5 .5) ?\r\nsecret counter trigger\r\nsounds\r\n1)\tsecret\r\n2)\tbeep beep\r\n3)\r\n4)\r\nset \"message\" to text string\r\n*/\r\nvoid() trigger_secret =\r\n{\r\n\ttotal_secrets = total_secrets + 1;\r\n\tself.wait = -1;\r\n\tif (!self.message)\r\n\t\tself.message = \"You found a secret area!\";\r\n\tif (!self.sounds)\r\n\t\tself.sounds = 1;\r\n\t\r\n\tif (self.sounds == 1)\r\n\t{\r\n\t\tprecache_sound (\"misc/secret.wav\");\r\n\t\tself.noise = \"misc/secret.wav\";\r\n\t}\r\n\telse if (self.sounds == 2)\r\n\t{\r\n\t\tprecache_sound (\"misc/talk.wav\");\r\n\t\tself.noise = \"misc/talk.wav\";\r\n\t}\r\n\r\n\ttrigger_multiple ();\r\n};\r\n\r\n//=============================================================================\r\n\r\n\r\nvoid() counter_use =\r\n{\r\n\tself.count = self.count - 1;\r\n\tif (self.count < 0)\r\n\t\treturn;\r\n\t\r\n\tif (self.count != 0)\r\n\t{\r\n\t\tif (activator.classname == \"player\"\r\n\t\t&& (self.spawnflags & SPAWNFLAG_NOMESSAGE) == 0)\r\n\t\t{\r\n\t\t\tif (self.count >= 4)\r\n\t\t\t\tcenterprint (activator, \"There are more to go...\");\r\n\t\t\telse if (self.count == 3)\r\n\t\t\t\tcenterprint (activator, \"Only 3 more to go...\");\r\n\t\t\telse if (self.count == 2)\r\n\t\t\t\tcenterprint (activator, \"Only 2 more to go...\");\r\n\t\t\telse\r\n\t\t\t\tcenterprint (activator, \"Only 1 more to go...\");\r\n\t\t}\r\n\t\treturn;\r\n\t}\r\n\t\r\n\tif (activator.classname == \"player\"\r\n\t&& (self.spawnflags & SPAWNFLAG_NOMESSAGE) == 0)\r\n\t\tcenterprint(activator, \"Sequence completed!\");\r\n\tself.enemy = activator;\r\n\tmulti_trigger ();\r\n};\r\n\r\n/*QUAKED trigger_counter (.5 .5 .5) ? nomessage\r\nActs as an intermediary for an action that takes multiple inputs.\r\n\r\nIf nomessage is not set, t will print \"1 more.. \" etc when triggered and \"sequence complete\" when finished.\r\n\r\nAfter the counter has been triggered \"count\" times (default 2), it will fire all of it's targets and remove itself.\r\n*/\r\nvoid() trigger_counter =\r\n{\r\n\tself.wait = -1;\r\n\tif (!self.count)\r\n\t\tself.count = 2;\r\n\r\n\tself.use = counter_use;\r\n};\r\n\r\n\r\n/*\r\n==============================================================================\r\n\r\nTELEPORT TRIGGERS\r\n\r\n==============================================================================\r\n*/\r\n\r\nfloat\tPLAYER_ONLY\t= 1;\r\nfloat\tSILENT = 2;\r\n\r\nvoid() play_teleport =\r\n{\r\n\tlocal\tfloat v;\r\n\tlocal\tstring tmpstr;\r\n\r\n\tv = random() * 5;\r\n\tif (v < 1)\r\n\t\ttmpstr = \"misc/r_tele1.wav\";\r\n\telse if (v < 2)\r\n\t\ttmpstr = \"misc/r_tele2.wav\";\r\n\telse if (v < 3)\r\n\t\ttmpstr = \"misc/r_tele3.wav\";\r\n\telse if (v < 4)\r\n\t\ttmpstr = \"misc/r_tele4.wav\";\r\n\telse\r\n\t\ttmpstr = \"misc/r_tele5.wav\";\r\n\r\n\tsound (self, CHAN_VOICE, tmpstr, 1, ATTN_NORM);\r\n\tremove (self);\r\n};\r\n\r\nvoid(vector org) spawn_tfog =\r\n{\r\n\ts = spawn ();\r\n\ts.origin = org;\r\n\ts.nextthink = time + 0.2;\r\n\ts.think = play_teleport;\r\n\r\n\tWriteByte (MSG_BROADCAST, SVC_TEMPENTITY);\r\n\tWriteByte (MSG_BROADCAST, TE_TELEPORT);\r\n\tWriteCoord (MSG_BROADCAST, org_x);\r\n\tWriteCoord (MSG_BROADCAST, org_y);\r\n\tWriteCoord (MSG_BROADCAST, org_z);\r\n};\r\n\r\n\r\nvoid() tdeath_touch =\r\n{\r\n\tif (other == self.owner)\r\n\t\treturn;\r\n\r\n// frag anyone who teleports in on top of an invincible player\r\n\tif (other.classname == \"player\")\r\n\t{\r\n\t\tif (other.invincible_finished > time)\r\n\t\t\tself.classname = \"teledeath2\";\r\n\t\tif (self.owner.classname != \"player\")\r\n\t\t{\t// other monsters explode themselves\r\n\t\t\tT_Damage (self.owner, self, self, 50000);\r\n\t\t\treturn;\r\n\t\t}\r\n\t\t\r\n\t}\r\n\r\n\tif (other.health)\r\n\t{\r\n\t\tT_Damage (other, self, self, 50000);\r\n\t}\r\n};\r\n\r\n\r\nvoid(vector org, entity death_owner) spawn_tdeath =\r\n{\r\nlocal entity\tdeath;\r\n\r\n\tdeath = spawn();\r\n\tdeath.classname = \"teledeath\";\r\n\tdeath.movetype = MOVETYPE_NONE;\r\n\tdeath.solid = SOLID_TRIGGER;\r\n\tdeath.angles = '0 0 0';\r\n\tsetsize (death, death_owner.mins - '1 1 1', death_owner.maxs + '1 1 1');\r\n\tsetorigin (death, org);\r\n\tdeath.touch = tdeath_touch;\r\n\tdeath.nextthink = time + 0.2;\r\n\tdeath.think = SUB_Remove;\r\n\tdeath.owner = death_owner;\r\n\t\r\n\tforce_retouch = 2;\t\t// make sure even still objects get hit\r\n};\r\n\r\nvoid() teleport_touch =\r\n{\r\nlocal entity\tt;\r\nlocal vector\torg;\r\n\r\n\tif (self.targetname!=\"\")\r\n\t{\r\n\t\tif (self.nextthink < time)\r\n\t\t{\r\n\t\t\treturn;\t\t// not fired yet\r\n\t\t}\r\n\t}\r\n\r\n\tif (self.spawnflags & PLAYER_ONLY)\r\n\t{\r\n\t\tif (other.classname != \"player\")\r\n\t\t\treturn;\r\n\t}\r\n\r\n// only teleport living creatures\r\n\tif (other.health <= 0 || other.solid != SOLID_SLIDEBOX)\r\n\t\treturn;\r\n\r\n\tSUB_UseTargets ();\r\n\r\n// put a tfog where the player was\r\n\tspawn_tfog (other.origin);\r\n\r\n\tt = find (world, targetname, self.target);\r\n\tif (!t)\r\n\t\tobjerror (\"couldn't find target\");\r\n\t\t\r\n// spawn a tfog flash in front of the destination\r\n\tmakevectors (t.mangle);\r\n\torg = t.origin + 32 * v_forward;\r\n\r\n\tspawn_tfog (org);\r\n\tspawn_tdeath(t.origin, other);\r\n\r\n// move the player and lock him down for a little while\r\n\tif (!other.health)\r\n\t{\r\n\t\tother.origin = t.origin;\r\n\t\tother.velocity = (v_forward * other.velocity_x) + (v_forward * other.velocity_y);\r\n\t\treturn;\r\n\t}\r\n\r\n\tsetorigin (other, t.origin);\r\n\tother.angles = t.mangle;\r\n\tif (other.classname == \"player\")\r\n\t{\r\n\t\t// turn this way immediately\r\n\t\t//note that csqc should have predicted this already\r\n\t\tif (!other.usingcsqc)\r\n\t\t\tother.fixangle = 1;\r\n\r\n\t\tother.teleport_time = time + 0.7;\r\n\t\tif (other.flags & FL_ONGROUND)\r\n\t\t\tother.flags = other.flags - FL_ONGROUND;\r\n\t\tother.velocity = v_forward * 300;\r\n\t}\r\n\tother.flags = other.flags - other.flags & FL_ONGROUND;\r\n};\r\n\r\n/*QUAKED info_teleport_destination (.5 .5 .5) (-8 -8 -8) (8 8 32)\r\nThis is the destination marker for a teleporter.  It should have a \"targetname\" field with the same value as a teleporter's \"target\" field.\r\n*/\r\nvoid() info_teleport_destination =\r\n{\r\n// this does nothing, just serves as a target spot\r\n\tself.mangle = self.angles;\r\n\tself.angles = '0 0 0';\r\n\tself.model = \"\";\r\n\tself.origin = self.origin + '0 0 27';\r\n\tif (!self.targetname)\r\n\t\tobjerror (\"no targetname\");\r\n};\r\n\r\nvoid() teleport_use =\r\n{\r\n\tself.nextthink = time + 0.2;\r\n\tforce_retouch = 2;\t\t// make sure even still objects get hit\r\n\tself.think = SUB_Null;\r\n};\r\n\r\n/*QUAKED trigger_teleport (.5 .5 .5) ? PLAYER_ONLY SILENT\r\nAny object touching this will be transported to the corresponding info_teleport_destination entity. You must set the \"target\" field, and create an object with a \"targetname\" field that matches.\r\n\r\nIf the trigger_teleport has a targetname, it will only teleport entities when it has been fired.\r\n*/\r\nvoid() trigger_teleport =\r\n{\r\n\tlocal vector o;\r\n\r\n\tInitTrigger ();\r\n\tself.touch = teleport_touch;\r\n\t// find the destination \r\n\tif (!self.target)\r\n\t\tobjerror (\"no target\");\r\n\tself.use = teleport_use;\r\n\r\n\tif (!(self.spawnflags & SILENT))\r\n\t{\r\n\t\tprecache_sound (\"ambience/hum1.wav\");\r\n\t\to = (self.mins + self.maxs)*0.5;\r\n\t\tambientsound (o, \"ambience/hum1.wav\",0.5 , ATTN_STATIC);\r\n\t}\r\n};\r\n\r\n/*\r\n==============================================================================\r\n\r\ntrigger_setskill\r\n\r\n==============================================================================\r\n*/\r\n\r\nvoid() trigger_skill_touch =\r\n{\r\n\tif (other.classname != \"player\")\r\n\t\treturn;\r\n\t\t\r\n\tcvar_set (\"skill\", self.message);\r\n};\r\n\r\n/*QUAKED trigger_setskill (.5 .5 .5) ?\r\nsets skill level to the value of \"message\".\r\nOnly used on start map.\r\n*/\r\nvoid() trigger_setskill =\r\n{\r\n\tInitTrigger ();\r\n\tself.touch = trigger_skill_touch;\r\n};\r\n\r\n\r\n/*\r\n==============================================================================\r\n\r\nONLY REGISTERED TRIGGERS\r\n\r\n==============================================================================\r\n*/\r\n\r\nvoid() trigger_onlyregistered_touch =\r\n{\r\n\tif (other.classname != \"player\")\r\n\t\treturn;\r\n\tif (self.attack_finished > time)\r\n\t\treturn;\r\n\r\n\tself.attack_finished = time + 2;\r\n\tif (cvar(\"registered\"))\r\n\t{\r\n\t\tself.message = \"\";\r\n\t\tSUB_UseTargets ();\r\n\t\tremove (self);\r\n\t}\r\n\telse\r\n\t{\r\n\t\tif (self.message != \"\")\r\n\t\t{\r\n\t\t\tcenterprint (other, self.message);\r\n\t\t\tsound (other, CHAN_BODY, \"misc/talk.wav\", 1, ATTN_NORM);\r\n\t\t}\r\n\t}\r\n};\r\n\r\n/*QUAKED trigger_onlyregistered (.5 .5 .5) ?\r\nOnly fires if playing the registered version, otherwise prints the message\r\n*/\r\nvoid() trigger_onlyregistered =\r\n{\r\n\tprecache_sound (\"misc/talk.wav\");\r\n\tInitTrigger ();\r\n\tself.touch = trigger_onlyregistered_touch;\r\n};\r\n\r\n//============================================================================\r\n\r\nvoid() hurt_on =\r\n{\r\n\tself.solid = SOLID_TRIGGER;\r\n\tself.nextthink = -1;\r\n};\r\n\r\nvoid() hurt_touch =\r\n{\r\n\tif (other.takedamage)\r\n\t{\r\n\t\tself.solid = SOLID_NOT;\r\n\t\tT_Damage (other, self, self, self.dmg);\r\n\t\tself.think = hurt_on;\r\n\t\tself.nextthink = time + 1;\r\n\t}\r\n\r\n\treturn;\r\n};\r\n\r\n/*QUAKED trigger_hurt (.5 .5 .5) ?\r\nAny object touching this will be hurt\r\nset dmg to damage amount\r\ndefalt dmg = 5\r\n*/\r\nvoid() trigger_hurt =\r\n{\r\n\tInitTrigger ();\r\n\tself.touch = hurt_touch;\r\n\tif (!self.dmg)\r\n\t\tself.dmg = 5;\r\n};\r\n\r\n//============================================================================\r\n\r\nfloat PUSH_ONCE = 1;\r\n\r\nvoid() target_position = {};\r\n//function provided by mercury (of oztf)\r\nvoid () trigger_push_findtarget =\r\n{\r\n\tlocal float flighttime;\r\n\tlocal float dist;\r\n\tlocal float grav;\r\n\tlocal vector org;\r\n\r\n\tself.enemy = find (world, targetname, self.target);\r\n\tif (!self.enemy)\r\n\t{\r\n\t\tdprint(\"trigger_push: target \");\r\n\t\tdprint(self.target);\r\n\t\tdprint(\" not found\\n\");\r\n\t\tremove (self);\r\n\t\treturn;\r\n\t}\r\n\torg = (self.absmin + self.absmax) * 0.5;\r\n\tgrav = cvar (\"sv_gravity\");\r\n\tflighttime = sqrt ((self.enemy.origin_z - org_z) / (0.5 * grav));\r\n\tif (!flighttime)\r\n\t{\r\n\t\tdprint(\"trigger_push: target \");\r\n\t\tdprint(self.target);\r\n\t\tdprint(\" too close\\n\");\r\n\t\tremove (self);\r\n\t\treturn;\r\n\t}\r\n\tself.movedir = self.enemy.origin - org;\r\n\tself.movedir_z = 0;\r\n\tdist = vlen (self.movedir);\r\n\tself.movedir = normalize (self.movedir) * (dist / flighttime);\r\n\tself.movedir_z = flighttime * grav;\r\n};\r\n\r\nvoid() trigger_push_touch =\r\n{\r\n\tif (other.classname == \"grenade\")\r\n\t\tother.velocity = self.movedir;\r\n\telse if (other.health > 0)\r\n\t{\r\n\t\tother.velocity = self.movedir;\r\n\t\tif (other.classname == \"player\")\r\n\t\t{\r\n\t\t\tif (other.fly_sound < time)\r\n\t\t\t{\r\n\t\t\t\tother.fly_sound = time + 1.5;\r\n\t\t\t\tsound (other, CHAN_AUTO, \"ambience/windfly.wav\", 1, ATTN_NORM);\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\tif (self.spawnflags & PUSH_ONCE)\r\n\t\tremove(self);\r\n};\r\n\r\n\r\n/*QUAKED trigger_push (.5 .5 .5) ? PUSH_ONCE\r\nPushes the player\r\n*/\r\nvoid() trigger_push =\r\n{\r\nprint(\"Found trigger_push\\n\");\r\n\tInitTrigger ();\r\n\tprecache_sound (\"ambience/windfly.wav\");\r\n\tself.touch = trigger_push_touch;\r\n\tif (!self.speed)\r\n\t\tself.speed = 1000;\r\n\r\n\tself.movedir = self.movedir * self.speed * 10;\r\n\r\n\tif (self.target)\r\n\t{\r\n//\t\tif (sqrt)\r\n\t\t{\r\n\t\t\tself.think = trigger_push_findtarget;\r\n\t\t\tself.nextthink = time + 0.2;\r\n\t\t}\r\n//\t\telse\r\n//\t\t\tremove(self);\r\n\t}\r\n};\r\n\r\n//============================================================================\r\n\r\nvoid() trigger_monsterjump_touch =\r\n{\r\n\tif ( other.flags & (FL_MONSTER | FL_FLY | FL_SWIM) != FL_MONSTER )\r\n\t\treturn;\r\n\r\n// set XY even if not on ground, so the jump will clear lips\r\n\tother.velocity_x = self.movedir_x * self.speed;\r\n\tother.velocity_y = self.movedir_y * self.speed;\r\n\t\r\n\tif ( !(other.flags & FL_ONGROUND) )\r\n\t\treturn;\r\n\t\r\n\tother.flags = other.flags - FL_ONGROUND;\r\n\r\n\tother.velocity_z = self.height;\r\n};\r\n\r\n/*QUAKED trigger_monsterjump (.5 .5 .5) ?\r\nWalking monsters that touch this will jump in the direction of the trigger's angle\r\n\"speed\" default to 200, the speed thrown forward\r\n\"height\" default to 200, the speed thrown upwards\r\n*/\r\nvoid() trigger_monsterjump =\r\n{\r\n\tif (!self.speed)\r\n\t\tself.speed = 200;\r\n\tif (!self.height)\r\n\t\tself.height = 200;\r\n\tif (self.angles == '0 0 0')\r\n\t\tself.angles = '0 360 0';\r\n\tInitTrigger ();\r\n\tself.touch = trigger_monsterjump_touch;\r\n};\r\n\r\n"
  },
  {
    "path": "quakec/csqctest/src/ss/weapons.qc",
    "content": "/*\r\n*/\r\nvoid (entity targ, entity inflictor, entity attacker, float damage) T_Damage;\r\nvoid () player_run;\r\nvoid(entity bomb, entity attacker, float rad, entity ignore) T_RadiusDamage;\r\nvoid(vector org, vector vel, float damage) SpawnBlood;\r\nvoid() SuperDamageSound;\r\n\r\n\r\n// called by worldspawn\r\nvoid() W_Precache =\r\n{\r\n\tprecache_sound (\"weapons/r_exp3.wav\");\t// new rocket explosion\r\n\tprecache_sound (\"weapons/rocket1i.wav\");\t// spike gun\r\n\tprecache_sound (\"weapons/sgun1.wav\");\r\n\tprecache_sound (\"weapons/guncock.wav\");\t// player shotgun\r\n\tprecache_sound (\"weapons/ric1.wav\");\t// ricochet (used in c code)\r\n\tprecache_sound (\"weapons/ric2.wav\");\t// ricochet (used in c code)\r\n\tprecache_sound (\"weapons/ric3.wav\");\t// ricochet (used in c code)\r\n\tprecache_sound (\"weapons/spike2.wav\");\t// super spikes\r\n\tprecache_sound (\"weapons/tink1.wav\");\t// spikes tink (used in c code)\r\n\tprecache_sound (\"weapons/grenade.wav\");\t// grenade launcher\r\n\tprecache_sound (\"weapons/bounce.wav\");\t\t// grenade bounce\r\n\tprecache_sound (\"weapons/shotgn2.wav\");\t// super shotgun\r\n};\r\n\r\nfloat() crandom =\r\n{\r\n\treturn 2*(random() - 0.5);\r\n};\r\n\r\n/*\r\n================\r\nW_FireAxe\r\n================\r\n*/\r\nvoid() W_FireAxe =\r\n{\r\n\tlocal\tvector\tsource;\r\n\tlocal\tvector\torg;\r\n\r\n\tmakevectors (self.v_angle);\r\n\tsource = self.origin + '0 0 16';\r\n\ttraceline (source, source + v_forward*64, FALSE, self);\r\n\tif (trace_fraction == 1.0)\r\n\t\treturn;\r\n\r\n\torg = trace_endpos - v_forward*4;\r\n\r\n\tif (trace_ent.takedamage)\r\n\t{\r\n\t\ttrace_ent.axhitme = 1;\r\n\t\tSpawnBlood (org, '0 0 0', 20);\r\n\t\tT_Damage (trace_ent, self, self, 20);\r\n\t}\r\n\telse\r\n\t{\t// hit wall\r\n\t\tsound (self, CHAN_WEAPON, \"player/axhit2.wav\", 1, ATTN_NORM);\r\n\r\n\t\tif (autocvar(test_buggyaxehit, 0) || isdp)\r\n\t\t{\t//for compat with dp. :(\r\n\t\t\tWriteByte (MSG_BROADCAST, SVC_TEMPENTITY);\r\n\t\t\tWriteByte (MSG_BROADCAST, 0x80|TE_GUNSHOT);\r\n\t\t\tWriteCoord (MSG_BROADCAST, org_x);\r\n\t\t\tWriteCoord (MSG_BROADCAST, org_y);\r\n\t\t\tWriteCoord (MSG_BROADCAST, org_z);\r\n\t\t\t\r\n\t\t\tWriteByte (MSG_BROADCAST, 8);\r\n\t\t\tWriteString (MSG_BROADCAST, \"overlong\\n\");\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tWriteByte (MSG_MULTICAST, SVC_TEMPENTITY);\r\n\t\t\tWriteByte (MSG_MULTICAST, 0x80|TE_GUNSHOT);\r\n\t\t\tWriteCoord (MSG_MULTICAST, org_x);\r\n\t\t\tWriteCoord (MSG_MULTICAST, org_y);\r\n\t\t\tWriteCoord (MSG_MULTICAST, org_z);\r\n\r\n\t\t\tWriteByte (MSG_MULTICAST, 8);\r\n\t\t\tWriteString (MSG_MULTICAST, \"overlong\\n\");\r\n\t\t\tmulticast(org, MULTICAST_PVS);\r\n\t\t}\r\n\t}\r\n};\r\n\r\n\r\n//============================================================================\r\n\r\n\r\nvector() wall_velocity =\r\n{\r\n\tlocal vector\tvel;\r\n\r\n\tvel = normalize (self.velocity);\r\n\tvel = normalize(vel + v_up*(random()- 0.5) + v_right*(random()- 0.5));\r\n\tvel = vel + 2*trace_plane_normal;\r\n\tvel = vel * 200;\r\n\r\n\treturn vel;\r\n};\r\n\r\n\r\n/*\r\n================\r\nSpawnMeatSpray\r\n================\r\n*/\r\nvoid(vector org, vector vel) SpawnMeatSpray =\r\n{\r\n\tlocal\tentity missile;\r\n\r\n\tmissile = spawn ();\r\n\tmissile.owner = self;\r\n\tmissile.movetype = MOVETYPE_BOUNCE;\r\n\tmissile.solid = SOLID_NOT;\r\n\r\n\tmakevectors (self.angles);\r\n\r\n\tmissile.velocity = vel;\r\n\tmissile.velocity_z = missile.velocity_z + 250 + 50*random();\r\n\r\n\tmissile.avelocity = '3000 1000 2000';\r\n\r\n// set missile duration\r\n\tmissile.nextthink = time + 1;\r\n\tmissile.think = SUB_Remove;\r\n\r\n\tsetmodel (missile, \"progs/zom_gib.mdl\");\r\n\tsetsize (missile, '0 0 0', '0 0 0');\r\n\tsetorigin (missile, org);\r\n};\r\n\r\n/*\r\n================\r\nSpawnBlood\r\n================\r\n*/\r\nvoid(vector org, vector vel, float damage) SpawnBlood =\r\n{\r\n\tparticle (org, vel*0.1, 73, damage*2);\r\n};\r\n\r\n/*\r\n================\r\nspawn_touchblood\r\n================\r\n*/\r\nvoid(float damage) spawn_touchblood =\r\n{\r\n\tlocal vector\tvel;\r\n\r\n\tvel = wall_velocity () * 0.2;\r\n\tSpawnBlood (self.origin + vel*0.01, vel, damage);\r\n};\r\n\r\n\r\n/*\r\n================\r\nSpawnChunk\r\n================\r\n*/\r\n/*void(vector org, vector vel) SpawnChunk =\r\n{\r\n\tparticle (org, vel*0.02, 0, 10);\r\n};*/\r\n\r\n/*\r\n==============================================================================\r\n\r\nMULTI-DAMAGE\r\n\r\nCollects multiple small damages into a single damage\r\n\r\n==============================================================================\r\n*/\r\n\r\nentity\tmulti_ent;\r\nfloat\tmulti_damage;\r\n\r\nvoid() ClearMultiDamage =\r\n{\r\n\tmulti_ent = world;\r\n\tmulti_damage = 0;\r\n};\r\n\r\nvoid() ApplyMultiDamage =\r\n{\r\n\tif (!multi_ent)\r\n\t\treturn;\r\n\tT_Damage (multi_ent, self, self, multi_damage);\r\n};\r\n\r\nvoid(entity hit, float damage) AddMultiDamage =\r\n{\r\n\tif (!hit)\r\n\t\treturn;\r\n\r\n\tif (hit != multi_ent)\r\n\t{\r\n\t\tApplyMultiDamage ();\r\n\t\tmulti_damage = damage;\r\n\t\tmulti_ent = hit;\r\n\t}\r\n\telse\r\n\t\tmulti_damage = multi_damage + damage;\r\n};\r\n\r\n/*\r\n==============================================================================\r\n\r\nBULLETS\r\n\r\n==============================================================================\r\n*/\r\n\r\n/*\r\n================\r\nTraceAttack\r\n================\r\n*/\r\nvoid(float damage, vector dir) TraceAttack =\r\n{\r\n\tlocal\tvector\tvel, org;\r\n\r\n\tvel = normalize(dir + v_up*crandom() + v_right*crandom());\r\n\tvel = vel + 2*trace_plane_normal;\r\n\tvel = vel * 200;\r\n\r\n\torg = trace_endpos - dir*4;\r\n\r\n\tif (trace_ent.takedamage)\r\n\t{\r\n\t\tSpawnBlood (org, vel*0.2, damage);\r\n\t\tAddMultiDamage (trace_ent, damage);\r\n\t}\r\n\telse\r\n\t{\r\n\t\tif (checkbuiltin(te_gunshot))\r\n\t\t\tte_gunshot(org);\r\n\t\telse\r\n\t\t{\r\n\t\t\tWriteByte (MSG_BROADCAST, SVC_TEMPENTITY);\r\n\t\t\tWriteByte (MSG_BROADCAST, TE_GUNSHOT);\r\n#ifdef QWSSQC\r\n\t\t\tWriteByte (MSG_BROADCAST, 1);\t//count\r\n#endif\r\n\t\t\tWriteCoord (MSG_BROADCAST, org_x);\r\n\t\t\tWriteCoord (MSG_BROADCAST, org_y);\r\n\t\t\tWriteCoord (MSG_BROADCAST, org_z);\r\n\t\t}\r\n\t}\r\n};\r\n\r\n/*\r\n================\r\nFireBullets\r\n\r\nUsed by shotgun, super shotgun, and enemy soldier firing\r\nGo to the trouble of combining multiple pellets into a single damage call.\r\n================\r\n*/\r\nvoid(float shotcount, vector dir, vector spread) FireBullets =\r\n{\r\n\tlocal\tvector direction;\r\n\tlocal\tvector\tsrc;\r\n\r\n\tmakevectors(self.v_angle);\r\n\r\n\tsrc = self.origin + v_forward*10;\r\n\tsrc_z = self.absmin_z + self.size_z * 0.7;\r\n\r\n\tClearMultiDamage ();\r\n\twhile (shotcount > 0)\r\n\t{\r\n\t\tdirection = dir + crandom()*spread_x*v_right + crandom()*spread_y*v_up;\r\n\r\n\t\ttraceline (src, src + direction*2048, FALSE, self);\r\n\t\tif (trace_fraction != 1.0)\r\n\t\t\tTraceAttack (4, direction);\r\n\r\n\t\tshotcount = shotcount - 1;\r\n\t}\r\n\tApplyMultiDamage ();\r\n};\r\n\r\n/*\r\n================\r\nW_FireShotgun\r\n================\r\n*/\r\nvoid() W_FireShotgun =\r\n{\r\n\tlocal vector dir;\r\n\r\n\tsound (self, CHAN_WEAPON, \"weapons/guncock.wav\", 1, ATTN_NORM);\r\n\r\n\tself.punchangle_x = -2;\r\n\r\n\tself.currentammo = self.ammo_shells = self.ammo_shells - 1;\r\n\tdir = aim (self, 100000);\r\n\tFireBullets (6, dir, '0.04 0.04 0');\r\n};\r\n\r\n\r\n/*\r\n================\r\nW_FireSuperShotgun\r\n================\r\n*/\r\nvoid() W_FireSuperShotgun =\r\n{\r\n\tlocal vector dir;\r\n\r\n\tif (self.currentammo == 1)\r\n\t{\r\n\t\tW_FireShotgun ();\r\n\t\treturn;\r\n\t}\r\n\r\n\tsound (self ,CHAN_WEAPON, \"weapons/shotgn2.wav\", 1, ATTN_NORM);\r\n\r\n\tself.punchangle_x = -4;\r\n\r\n\tself.currentammo = self.ammo_shells = self.ammo_shells - 2;\r\n\tdir = aim (self, 100000);\r\n\tFireBullets (14, dir, '0.14 0.08 0');\r\n};\r\n\r\n\r\n/*\r\n==============================================================================\r\n\r\nROCKETS\r\n\r\n==============================================================================\r\n*/\r\n\r\n.vector source;\r\n.float starttime;\r\n\r\nfloat(entity toplayer, float fl) SendNail =\r\n{\r\n\tvector v;\r\n\tv = self.source + self.velocity*(time-self.starttime);\r\n\tWriteByte(MSG_ENTITY, CLASS_NAIL);\r\n\tWriteCoord(MSG_ENTITY, v_x);\r\n\tWriteCoord(MSG_ENTITY, v_y);\r\n\tWriteCoord(MSG_ENTITY, v_z);\r\n\tWriteByte(MSG_ENTITY, self.modelindex);\t//note: some engines support >256 modelindexes. so long as it's precached in worldspawn, it'll not be an issue.\r\n\tWriteShort(MSG_ENTITY, vlen(self.velocity));\r\n\tWriteShort(MSG_ENTITY, self.angles_x*65535/360);\t//we might get better precision if we were to writecoord some point ahead that it should pass through, but this is smaller and accurate enough.\r\n\tWriteShort(MSG_ENTITY, self.angles_y*65535/360);\r\n\r\n\treturn TRUE;\r\n};\r\n\r\nfloat(entity toplayer, float fl) SendRocket =\r\n{\r\n\tvector v;\r\n\tv = self.source + self.velocity*(time-self.starttime);\r\n\tWriteByte(MSG_ENTITY, CLASS_ROCKET);\r\n\tWriteCoord(MSG_ENTITY, v_x);\r\n\tWriteCoord(MSG_ENTITY, v_y);\r\n\tWriteCoord(MSG_ENTITY, v_z);\r\n\tWriteShort(MSG_ENTITY, self.angles_x*65535/360);\t//we might get better precision if we were to writecoord some point ahead that it should pass through, but this is smaller and accurate enough.\r\n\tWriteShort(MSG_ENTITY, self.angles_y*65535/360);\r\n\r\n\treturn TRUE;\r\n};\r\n\r\n\r\nfloat(entity toplayer, float fl) SendExplosion =\r\n{\r\n\tvector v;\r\n\tv = self.origin;\r\n\tWriteByte(MSG_ENTITY, CLASS_EXPLOSION);\r\n\tWriteCoord(MSG_ENTITY, v_x);\r\n\tWriteCoord(MSG_ENTITY, v_y);\r\n\tWriteCoord(MSG_ENTITY, v_z);\r\n\r\n\treturn TRUE;\r\n};\r\n\r\n/*this is for shits and giggles*/\r\nvoid() makevolcano =\r\n{\r\n\tif (checkbuiltin(terrain_edit) && autocvar(test_volcano, 0, \"Rocket explosions create a volcano shape on terrain models.\"))\r\n\t{\r\n\t\tterrain_edit(4, self.origin, 512, 64/3);\r\n\t\tterrain_edit(5, self.origin, 256, 32/3);\r\n\t}\r\n};\r\nvoid()\ts_explode1\t= {self.think = s_explode1; makevolcano(); self.frame += 0.5; self.nextthink = time + 0.05; if (self.frame >= 6) {remove(self); return;}};\r\n\r\nvoid() BecomeExplosion =\r\n{\r\n\t__using dimension_seen, dimension_send;\t//okay for this purpose...\r\n\r\n\tself.movetype = MOVETYPE_NONE;\r\n\tself.velocity = '0 0 0';\r\n\tself.touch = SUB_Null;\r\n\tself.solid = SOLID_NOT;\r\n\r\n\tself.dimension_seen = DIMENSION_NOCSQC;\r\n\tdimension_send = DIMENSION_NOCSQC;\r\n\r\n\tif (checkbuiltin(te_explosion))\r\n\t\tte_explosion(self.origin);\r\n\telse\r\n\t{\r\n\t\tWriteByte (MSG_BROADCAST, SVC_TEMPENTITY);\r\n\t\tWriteByte (MSG_BROADCAST, TE_EXPLOSION);\r\n\t\tWriteCoord (MSG_BROADCAST, self.origin_x);\r\n\t\tWriteCoord (MSG_BROADCAST, self.origin_y);\r\n\t\tWriteCoord (MSG_BROADCAST, self.origin_z);\r\n\t}\r\n\r\n\tsound (self, CHAN_WEAPON, \"weapons/r_exp3.wav\", 1, ATTN_NORM);\r\n\tself.dimension_seen = DIMENSION_DEFAULT;\r\n\tdimension_send = DIMENSION_DEFAULT;\r\n\r\n\tsetmodel (self, \"progs/s_explod.spr\");\r\n\ts_explode1 ();\r\n\r\n//set up the fields required for transmitting the event.\r\n\tself.source = self.origin;\r\n\tself.SendEntity = SendExplosion;\r\n\tself.starttime = time;\r\n\tself.SendFlags = FULLSEND;\r\n};\r\n\r\nvoid() T_MissileTouch =\r\n{\r\n\tlocal float\tdamg;\r\n\r\n\tif (other == self.owner)\r\n\t\treturn;\t\t// don't explode on owner\r\n\r\n\tif (pointcontents(self.origin) == CONTENT_SKY)\r\n\t{\r\n\t\tremove(self);\r\n\t\treturn;\r\n\t}\r\n\r\n\tdamg = 100 + random()*20;\r\n\r\n\tif (other.health)\r\n\t{\r\n\t\tif (other.classname == \"monster_shambler\")\r\n\t\t\tdamg = damg * 0.5;\t// mostly immune\r\n\t\tT_Damage (other, self, self.owner, damg );\r\n\t}\r\n\r\n\t// don't do radius damage to the other, because all the damage\r\n\t// was done in the impact\r\n\tT_RadiusDamage (self, self.owner, 120, other);\r\n\r\n//\tsound (self, CHAN_WEAPON, \"weapons/r_exp3.wav\", 1, ATTN_NORM);\r\n\tself.origin = self.origin - 8*normalize(self.velocity);\r\n\r\n\tBecomeExplosion ();\r\n};\r\n\r\n/*\r\n================\r\nW_FireRocket\r\n================\r\n*/\r\nvoid() W_FireRocket =\r\n{\r\n\tlocal\tentity missile;\r\n\r\n\tself.currentammo = self.ammo_rockets = self.ammo_rockets - 1;\r\n\r\n\tsound (self, CHAN_WEAPON, \"weapons/sgun1.wav\", 1, ATTN_NORM);\r\n\r\n\tself.punchangle_x = -2;\r\n\r\n\tmissile = spawn ();\r\n\tmissile.owner = self;\r\n\tmissile.movetype = MOVETYPE_FLYMISSILE;\r\n\tmissile.solid = SOLID_BBOX;\r\n\tmissile.classname = \"missile\";\r\n\r\n// set missile speed\r\n\r\n\tmakevectors (self.v_angle);\r\n\tmissile.velocity = aim(self, 1000);\r\n\tmissile.velocity = missile.velocity * 1000;\r\n\tmissile.angles = vectoangles(missile.velocity);\r\n\r\n\tmissile.touch = T_MissileTouch;\r\n\r\n// set missile duration\r\n\tmissile.nextthink = time + 5;\r\n\tmissile.think = SUB_Remove;\r\n\r\n\tsetmodel (missile, \"progs/missile.mdl\");\r\n\tsetsize (missile, '0 0 0', '0 0 0');\r\n\tsetorigin (missile, self.origin + v_forward*8 + '0 0 16');\r\n\r\n\tmissile.source = missile.origin;\r\n\tmissile.SendEntity = SendRocket;\r\n\tmissile.starttime = time;\r\n\tmissile.SendFlags = FULLSEND;\r\n};\r\n\r\n/*\r\n===============================================================================\r\n\r\nLIGHTNING\r\n\r\n===============================================================================\r\n*/\r\n\r\n/*\r\n=================\r\nLightningDamage\r\n=================\r\n*/\r\nvoid(vector p1, vector p2, entity from, float damage) LightningDamage =\r\n{\r\n\tlocal entity\t\te1, e2;\r\n\tlocal vector\t\tf;\r\n\r\n\tf = p2 - p1;\r\n\tnormalize (f);\r\n\tf_x = 0 - f_y;\r\n\tf_y = f_x;\r\n\tf_z = 0;\r\n\tf = f*16;\r\n\r\n\te1 = e2 = world;\r\n\r\n\ttraceline (p1, p2, FALSE, self);\r\n\tif (trace_ent.takedamage)\r\n\t{\r\n\t\tparticle (trace_endpos, '0 0 100', 225, damage*4);\r\n\t\tT_Damage (trace_ent, from, from, damage);\r\n\t\tif (self.classname == \"player\")\r\n\t\t{\r\n\t\t\tif (other.classname == \"player\")\r\n\t\t\t\ttrace_ent.velocity_z = trace_ent.velocity_z + 400;\r\n\t\t}\r\n\t}\r\n\te1 = trace_ent;\r\n\r\n\ttraceline (p1 + f, p2 + f, FALSE, self);\r\n\tif (trace_ent != e1 && trace_ent.takedamage)\r\n\t{\r\n\t\tparticle (trace_endpos, '0 0 100', 225, damage*4);\r\n\t\tT_Damage (trace_ent, from, from, damage);\r\n\t}\r\n\te2 = trace_ent;\r\n\r\n\ttraceline (p1 - f, p2 - f, FALSE, self);\r\n\tif (trace_ent != e1 && trace_ent != e2 && trace_ent.takedamage)\r\n\t{\r\n\t\tparticle (trace_endpos, '0 0 100', 225, damage*4);\r\n\t\tT_Damage (trace_ent, from, from, damage);\r\n\t}\r\n};\r\n\r\n\r\nvoid() W_FireLightning =\r\n{\r\n\tlocal\tvector\t\torg;\r\n\tlocal\tfloat\t\tcells;\r\n\r\n\tif (self.ammo_cells < 1)\r\n\t{\r\n\t\tself.weapon = W_BestWeapon ();\r\n\t\tW_SetCurrentAmmo ();\r\n\t\treturn;\r\n\t}\r\n\r\n// explode if under water\r\n\tif (self.waterlevel > 1)\r\n\t{\r\n\t\tcells = self.ammo_cells;\r\n\t\tself.ammo_cells = 0;\r\n\t\tW_SetCurrentAmmo ();\r\n\t\tT_RadiusDamage (self, self, 35*cells, world);\r\n\t\treturn;\r\n\t}\r\n\r\n\tif (self.t_width < time)\r\n\t{\r\n\t\tsound (self, CHAN_WEAPON, \"weapons/lhit.wav\", 1, ATTN_NORM);\r\n\t\tself.t_width = time + 0.6;\r\n\t}\r\n\tself.punchangle_x = -2;\r\n\r\n\tself.currentammo = self.ammo_cells = self.ammo_cells - 1;\r\n\r\n\torg = self.origin + '0 0 16';\r\n\r\n\ttraceline (org, org + v_forward*600, TRUE, self);\r\n\r\n\tWriteByte (MSG_BROADCAST, SVC_TEMPENTITY);\r\n\tWriteByte (MSG_BROADCAST, TE_LIGHTNING2);\r\n\tWriteEntity (MSG_BROADCAST, self);\r\n\tWriteCoord (MSG_BROADCAST, org_x);\r\n\tWriteCoord (MSG_BROADCAST, org_y);\r\n\tWriteCoord (MSG_BROADCAST, org_z);\r\n\tWriteCoord (MSG_BROADCAST, trace_endpos_x);\r\n\tWriteCoord (MSG_BROADCAST, trace_endpos_y);\r\n\tWriteCoord (MSG_BROADCAST, trace_endpos_z);\r\n\r\n\tLightningDamage (self.origin, trace_endpos + v_forward*4, self, 30);\r\n};\r\n\r\n\r\n//=============================================================================\r\n\r\n\r\nvoid() GrenadeExplode =\r\n{\r\n\tT_RadiusDamage (self, self.owner, 120, world);\r\n\r\n\tWriteByte (MSG_BROADCAST, SVC_TEMPENTITY);\r\n\tWriteByte (MSG_BROADCAST, TE_EXPLOSION);\r\n\tWriteCoord (MSG_BROADCAST, self.origin_x);\r\n\tWriteCoord (MSG_BROADCAST, self.origin_y);\r\n\tWriteCoord (MSG_BROADCAST, self.origin_z);\r\n\r\n\tBecomeExplosion ();\r\n};\r\n\r\nvoid() GrenadeTouch =\r\n{\r\n\tif (other == self.owner)\r\n\t\treturn;\t\t// don't explode on owner\r\n\tif (other.takedamage == DAMAGE_AIM)\r\n\t{\r\n\t\tGrenadeExplode();\r\n\t\treturn;\r\n\t}\r\n\tsound (self, CHAN_WEAPON, \"weapons/bounce.wav\", 1, ATTN_NORM);\t// bounce sound\r\n\tif (self.velocity == '0 0 0')\r\n\t\tself.avelocity = '0 0 0';\r\n};\r\n\r\n/*\r\n================\r\nW_FireGrenade\r\n================\r\n*/\r\nvoid() W_FireGrenade =\r\n{\r\n\tlocal\tentity missile;\r\n\r\n\tself.currentammo = self.ammo_rockets = self.ammo_rockets - 1;\r\n\r\n\tsound (self, CHAN_WEAPON, \"weapons/grenade.wav\", 1, ATTN_NORM);\r\n\r\n\tself.punchangle_x = -2;\r\n\r\n\tmissile = spawn ();\r\n\tmissile.owner = self;\r\n\tmissile.movetype = MOVETYPE_BOUNCE;\r\n\tmissile.solid = SOLID_BBOX;\r\n\tmissile.classname = \"grenade\";\r\n\r\n// set missile speed\r\n\r\n\tmakevectors (self.v_angle);\r\n\r\n\tif (self.v_angle_x)\r\n\t\tmissile.velocity = v_forward*600 + v_up * 200 + crandom()*v_right*10 + crandom()*v_up*10;\r\n\telse\r\n\t{\r\n\t\tmissile.velocity = aim(self, 10000);\r\n\t\tmissile.velocity = missile.velocity * 600;\r\n\t\tmissile.velocity_z = 200;\r\n\t}\r\n\r\n\tmissile.avelocity = '300 300 300';\r\n\r\n\tmissile.angles = vectoangles(missile.velocity);\r\n\r\n\tmissile.touch = GrenadeTouch;\r\n\r\n// set missile duration\r\n\tmissile.nextthink = time + 2.5;\r\n\tmissile.think = GrenadeExplode;\r\n\r\n\tsetmodel (missile, \"progs/grenade.mdl\");\r\n\tsetsize (missile, '0 0 0', '0 0 0');\r\n\tsetorigin (missile, self.origin);\r\n};\r\n\r\n\r\n//=============================================================================\r\n\r\nvoid() spike_touch;\r\nvoid() superspike_touch;\r\n\r\n\r\n/*\r\n===============\r\nlaunch_spike\r\n\r\nUsed for both the player and the ogre\r\n===============\r\n*/\r\nvoid(vector org, vector dir) launch_spike =\r\n{\r\n\tnewmis = spawn ();\r\n\tnewmis.owner = self;\r\n\tnewmis.movetype = MOVETYPE_FLYMISSILE;\r\n\tnewmis.solid = SOLID_BBOX;\r\n\r\n\tnewmis.angles = vectoangles(dir);\r\n\r\n\tnewmis.touch = spike_touch;\r\n\tnewmis.classname = \"spike\";\r\n\tnewmis.think = SUB_Remove;\r\n\tnewmis.nextthink = time + 6;\r\n\tsetmodel (newmis, \"progs/spike.mdl\");\r\n\tsetsize (newmis, VEC_ORIGIN, VEC_ORIGIN);\r\n\tsetorigin (newmis, org);\r\n\r\n\tnewmis.velocity = dir * 1000;\r\n\r\n\tnewmis.SendEntity = SendNail;\r\n\tnewmis.SendFlags = FULLSEND;\r\n\tnewmis.source = newmis.origin;\r\n\tnewmis.starttime = time;\r\n};\r\n\r\nvoid() W_FireSuperSpikes =\r\n{\r\n\tlocal vector\tdir;\r\n\r\n\tsound (self, CHAN_WEAPON, \"weapons/spike2.wav\", 1, ATTN_NORM);\r\n\tself.attack_finished = time + 0.2;\r\n\tself.currentammo = self.ammo_nails = self.ammo_nails - 2;\r\n\tdir = aim (self, 1000);\r\n\tlaunch_spike (self.origin + '0 0 16', dir);\r\n\tnewmis.touch = superspike_touch;\r\n\tsetmodel (newmis, \"progs/s_spike.mdl\");\r\n\tsetsize (newmis, VEC_ORIGIN, VEC_ORIGIN);\r\n\tself.punchangle_x = -2;\r\n};\r\n\r\nvoid(float ox) W_FireSpikes =\r\n{\r\n\tlocal vector\tdir;\r\n\r\n\tmakevectors (self.v_angle);\r\n\r\n\tif (self.ammo_nails >= 2 && self.weapon == IT_SUPER_NAILGUN)\r\n\t{\r\n\t\tW_FireSuperSpikes ();\r\n\t\treturn;\r\n\t}\r\n\r\n\tif (self.ammo_nails < 1)\r\n\t{\r\n\t\tself.weapon = W_BestWeapon ();\r\n\t\tW_SetCurrentAmmo ();\r\n\t\treturn;\r\n\t}\r\n\r\n\tsound (self, CHAN_WEAPON, \"weapons/rocket1i.wav\", 1, ATTN_NORM);\r\n\tself.attack_finished = time + 0.2;\r\n\tself.currentammo = self.ammo_nails = self.ammo_nails - 1;\r\n\tdir = aim (self, 1000);\r\n\tlaunch_spike (self.origin + '0 0 16' + v_right*ox, dir);\r\n\r\n\tself.punchangle_x = -2;\r\n};\r\n\r\n\r\n\r\nvoid() spike_touch =\r\n{\r\n\tif (other == self.owner)\r\n\t\treturn;\r\n\r\n\tif (other.solid == SOLID_TRIGGER)\r\n\t\treturn;\t// trigger field, do nothing\r\n\r\n\tif (pointcontents(self.origin) == CONTENT_SKY)\r\n\t{\r\n\t\tremove(self);\r\n\t\treturn;\r\n\t}\r\n\r\n// hit something that bleeds\r\n\tif (other.takedamage)\r\n\t{\r\n\t\tspawn_touchblood (9);\r\n\t\tT_Damage (other, self, self.owner, 9);\r\n\t}\r\n\telse\r\n\t{\r\n\t\tWriteByte (MSG_BROADCAST, SVC_TEMPENTITY);\r\n\r\n\t\tif (self.classname == \"wizspike\")\r\n\t\t\tWriteByte (MSG_BROADCAST, TE_WIZSPIKE);\r\n\t\telse if (self.classname == \"knightspike\")\r\n\t\t\tWriteByte (MSG_BROADCAST, TE_KNIGHTSPIKE);\r\n\t\telse\r\n\t\t\tWriteByte (MSG_BROADCAST, TE_SPIKE);\r\n\t\tWriteCoord (MSG_BROADCAST, self.origin_x);\r\n\t\tWriteCoord (MSG_BROADCAST, self.origin_y);\r\n\t\tWriteCoord (MSG_BROADCAST, self.origin_z);\r\n\t}\r\n\r\n\tremove(self);\r\n\r\n};\r\n\r\nvoid() superspike_touch =\r\n{\r\n\tif (other == self.owner)\r\n\t\treturn;\r\n\r\n\tif (other.solid == SOLID_TRIGGER)\r\n\t\treturn;\t// trigger field, do nothing\r\n\r\n\tif (pointcontents(self.origin) == CONTENT_SKY)\r\n\t{\r\n\t\tremove(self);\r\n\t\treturn;\r\n\t}\r\n\r\n// hit something that bleeds\r\n\tif (other.takedamage)\r\n\t{\r\n\t\tspawn_touchblood (18);\r\n\t\tT_Damage (other, self, self.owner, 18);\r\n\t}\r\n\telse\r\n\t{\r\n\t\tWriteByte (MSG_BROADCAST, SVC_TEMPENTITY);\r\n\t\tWriteByte (MSG_BROADCAST, TE_SUPERSPIKE);\r\n\t\tWriteCoord (MSG_BROADCAST, self.origin_x);\r\n\t\tWriteCoord (MSG_BROADCAST, self.origin_y);\r\n\t\tWriteCoord (MSG_BROADCAST, self.origin_z);\r\n\t}\r\n\r\n\tremove(self);\r\n\r\n};\r\n\r\n\r\n/*\r\n===============================================================================\r\n\r\nPLAYER WEAPON USE\r\n\r\n===============================================================================\r\n*/\r\n\r\nvoid() W_SetCurrentAmmo =\r\n{\r\n\tplayer_run ();\t\t// get out of any weapon firing states\r\n\r\n\tself.items = self.items - ( self.items & (IT_SHELLS | IT_NAILS | IT_ROCKETS | IT_CELLS) );\r\n\r\n\tif (self.weapon == IT_AXE)\r\n\t{\r\n\t\tself.currentammo = 0;\r\n\t\tself.weaponmodel = \"progs/v_axe.mdl\";\r\n\t\tself.weaponframe = 0;\r\n\t}\r\n\telse if (self.weapon == IT_SHOTGUN)\r\n\t{\r\n\t\tself.currentammo = self.ammo_shells;\r\n\t\tself.weaponmodel = \"progs/v_shot.mdl\";\r\n\t\tself.weaponframe = 0;\r\n\t\tself.items = self.items | IT_SHELLS;\r\n\t}\r\n\telse if (self.weapon == IT_SUPER_SHOTGUN)\r\n\t{\r\n\t\tself.currentammo = self.ammo_shells;\r\n\t\tself.weaponmodel = \"progs/v_shot2.mdl\";\r\n\t\tself.weaponframe = 0;\r\n\t\tself.items = self.items | IT_SHELLS;\r\n\t}\r\n\telse if (self.weapon == IT_NAILGUN)\r\n\t{\r\n\t\tself.currentammo = self.ammo_nails;\r\n\t\tself.weaponmodel = \"progs/v_nail.mdl\";\r\n\t\tself.weaponframe = 0;\r\n\t\tself.items = self.items | IT_NAILS;\r\n\t}\r\n\telse if (self.weapon == IT_SUPER_NAILGUN)\r\n\t{\r\n\t\tself.currentammo = self.ammo_nails;\r\n\t\tself.weaponmodel = \"progs/v_nail2.mdl\";\r\n\t\tself.weaponframe = 0;\r\n\t\tself.items = self.items | IT_NAILS;\r\n\t}\r\n\telse if (self.weapon == IT_GRENADE_LAUNCHER)\r\n\t{\r\n\t\tself.currentammo = self.ammo_rockets;\r\n\t\tself.weaponmodel = \"progs/v_rock.mdl\";\r\n\t\tself.weaponframe = 0;\r\n\t\tself.items = self.items | IT_ROCKETS;\r\n\t}\r\n\telse if (self.weapon == IT_ROCKET_LAUNCHER)\r\n\t{\r\n\t\tself.currentammo = self.ammo_rockets;\r\n\t\tself.weaponmodel = \"progs/v_rock2.mdl\";\r\n\t\tself.weaponframe = 0;\r\n\t\tself.items = self.items | IT_ROCKETS;\r\n\t}\r\n\telse if (self.weapon == IT_LIGHTNING)\r\n\t{\r\n\t\tself.currentammo = self.ammo_cells;\r\n\t\tself.weaponmodel = \"progs/v_light.mdl\";\r\n\t\tself.weaponframe = 0;\r\n\t\tself.items = self.items | IT_CELLS;\r\n\t}\r\n\telse\r\n\t{\r\n\t\tself.currentammo = 0;\r\n\t\tself.weaponmodel = \"\";\r\n\t\tself.weaponframe = 0;\r\n\t}\r\n};\r\n\r\nfloat() W_BestWeapon =\r\n{\r\n\tlocal\tfloat\tit;\r\n\r\n\tit = self.items;\r\n\r\n\tif (self.waterlevel <= 1 && self.ammo_cells >= 1 && (it & IT_LIGHTNING) )\r\n\t\t\treturn IT_LIGHTNING;\r\n\tif(self.ammo_nails >= 2 && (it & IT_SUPER_NAILGUN) )\r\n\t\treturn IT_SUPER_NAILGUN;\r\n\tif(self.ammo_shells >= 2 && (it & IT_SUPER_SHOTGUN) )\r\n\t\treturn IT_SUPER_SHOTGUN;\r\n\tif(self.ammo_nails >= 1 && (it & IT_NAILGUN) )\r\n\t\treturn IT_NAILGUN;\r\n\tif(self.ammo_shells >= 1 && (it & IT_SHOTGUN) )\r\n\t\treturn IT_SHOTGUN;\r\n\treturn IT_AXE;\r\n};\r\n\r\nfloat() W_CheckNoAmmo =\r\n{\r\n\tif (self.currentammo > 0)\r\n\t\treturn TRUE;\r\n\r\n\tif (self.weapon == IT_AXE)\r\n\t\treturn TRUE;\r\n\r\n\tself.weapon = W_BestWeapon ();\r\n\r\n\tW_SetCurrentAmmo ();\r\n\r\n// drop the weapon down\r\n\treturn FALSE;\r\n};\r\n\r\n/*\r\n============\r\nW_Attack\r\n\r\nAn attack impulse can be triggered now\r\n============\r\n*/\r\nvoid()\tplayer_axe1;\r\nvoid()\tplayer_axeb1;\r\nvoid()\tplayer_axec1;\r\nvoid()\tplayer_axed1;\r\nvoid()\tplayer_shot1;\r\nvoid()\tplayer_nail1;\r\nvoid()\tplayer_light1;\r\nvoid()\tplayer_rocket1;\r\n\r\nvoid() W_Attack =\r\n{\r\n\tlocal\tfloat\tr;\r\n\r\n\tif (!W_CheckNoAmmo ())\r\n\t\treturn;\r\n\r\n\tmakevectors\t(self.v_angle);\t\t\t// calculate forward angle for velocity\r\n\tself.show_hostile = time + 1;\t// wake monsters up\r\n\r\n\tif (self.weapon == IT_AXE)\r\n\t{\r\n\t\tsound (self, CHAN_WEAPON, \"weapons/ax1.wav\", 1, ATTN_NORM);\r\n\t\tr = random();\r\n\t\tif (r < 0.25)\r\n\t\t\tplayer_axe1 ();\r\n\t\telse if (r<0.5)\r\n\t\t\tplayer_axeb1 ();\r\n\t\telse if (r<0.75)\r\n\t\t\tplayer_axec1 ();\r\n\t\telse\r\n\t\t\tplayer_axed1 ();\r\n\t\tself.attack_finished = time + 0.5;\r\n\t}\r\n\telse if (self.weapon == IT_SHOTGUN)\r\n\t{\r\n\t\tplayer_shot1 ();\r\n\t\tW_FireShotgun ();\r\n\t\tself.attack_finished = time + 0.5;\r\n\t}\r\n\telse if (self.weapon == IT_SUPER_SHOTGUN)\r\n\t{\r\n\t\tplayer_shot1 ();\r\n\t\tW_FireSuperShotgun ();\r\n\t\tself.attack_finished = time + 0.7;\r\n\t}\r\n\telse if (self.weapon == IT_NAILGUN)\r\n\t{\r\n\t\tplayer_nail1 ();\r\n\t}\r\n\telse if (self.weapon == IT_SUPER_NAILGUN)\r\n\t{\r\n\t\tplayer_nail1 ();\r\n\t}\r\n\telse if (self.weapon == IT_GRENADE_LAUNCHER)\r\n\t{\r\n\t\tplayer_rocket1();\r\n\t\tW_FireGrenade();\r\n\t\tself.attack_finished = time + 0.6;\r\n\t}\r\n\telse if (self.weapon == IT_ROCKET_LAUNCHER)\r\n\t{\r\n\t\tplayer_rocket1();\r\n\t\tW_FireRocket();\r\n\t\tself.attack_finished = time + 0.8;\r\n\t}\r\n\telse if (self.weapon == IT_LIGHTNING)\r\n\t{\r\n\t\tplayer_light1();\r\n\t\tself.attack_finished = time + 0.1;\r\n\t\tsound (self, CHAN_AUTO, \"weapons/lstart.wav\", 1, ATTN_NORM);\r\n\t}\r\n};\r\n\r\n/*\r\n============\r\nW_ChangeWeapon\r\n\r\n============\r\n*/\r\nvoid() W_ChangeWeapon =\r\n{\r\n\tlocal\tfloat\tit, am, fl;\r\n\r\n\tit = self.items;\r\n\tam = 0;\r\n\r\n\tif (self.impulse == 1)\r\n\t{\r\n\t\tfl = IT_AXE;\r\n\t}\r\n\telse if (self.impulse == 2)\r\n\t{\r\n\t\tfl = IT_SHOTGUN;\r\n\t\tif (self.ammo_shells < 1)\r\n\t\t\tam = 1;\r\n\t}\r\n\telse if (self.impulse == 3)\r\n\t{\r\n\t\tfl = IT_SUPER_SHOTGUN;\r\n\t\tif (self.ammo_shells < 2)\r\n\t\t\tam = 1;\r\n\t}\r\n\telse if (self.impulse == 4)\r\n\t{\r\n\t\tfl = IT_NAILGUN;\r\n\t\tif (self.ammo_nails < 1)\r\n\t\t\tam = 1;\r\n\t}\r\n\telse if (self.impulse == 5)\r\n\t{\r\n\t\tfl = IT_SUPER_NAILGUN;\r\n\t\tif (self.ammo_nails < 2)\r\n\t\t\tam = 1;\r\n\t}\r\n\telse if (self.impulse == 6)\r\n\t{\r\n\t\tfl = IT_GRENADE_LAUNCHER;\r\n\t\tif (self.ammo_rockets < 1)\r\n\t\t\tam = 1;\r\n\t}\r\n\telse if (self.impulse == 7)\r\n\t{\r\n\t\tfl = IT_ROCKET_LAUNCHER;\r\n\t\tif (self.ammo_rockets < 1)\r\n\t\t\tam = 1;\r\n\t}\r\n\telse if (self.impulse == 8)\r\n\t{\r\n\t\tfl = IT_LIGHTNING;\r\n\t\tif (self.ammo_cells < 1)\r\n\t\t\tam = 1;\r\n\t}\r\n\telse\r\n\t\tfl = 0;\r\n\r\n\tself.impulse = 0;\r\n\r\n\tif (!(self.items & fl))\r\n\t{\t// don't have the weapon or the ammo\r\n\t\tsprint (self, \"no weapon.\\n\");\r\n\t\treturn;\r\n\t}\r\n\r\n\tif (am)\r\n\t{\t// don't have the ammo\r\n\t\tsprint (self, \"not enough ammo.\\n\");\r\n\t\treturn;\r\n\t}\r\n\r\n//\r\n// set weapon, set ammo\r\n//\r\n\tself.weapon = fl;\r\n\tW_SetCurrentAmmo ();\r\n};\r\n\r\n/*\r\n============\r\nCheatCommand\r\n============\r\n*/\r\nvoid() CheatCommand =\r\n{\r\n\tif (deathmatch || coop)\r\n\t\treturn;\r\n\r\n\tself.ammo_rockets = 100;\r\n\tself.ammo_nails = 200;\r\n\tself.ammo_shells = 100;\r\n\tself.items = self.items |\r\n\t\tIT_AXE |\r\n\t\tIT_SHOTGUN |\r\n\t\tIT_SUPER_SHOTGUN |\r\n\t\tIT_NAILGUN |\r\n\t\tIT_SUPER_NAILGUN |\r\n\t\tIT_GRENADE_LAUNCHER |\r\n\t\tIT_ROCKET_LAUNCHER |\r\n\t\tIT_KEY1 | IT_KEY2;\r\n\r\n\tself.ammo_cells = 200;\r\n\tself.items = self.items | IT_LIGHTNING;\r\n\r\n\tself.weapon = IT_ROCKET_LAUNCHER;\r\n\tself.impulse = 0;\r\n\tW_SetCurrentAmmo ();\r\n};\r\n\r\nvoid() GiveCommand_f =\r\n{\r\n\tif (deathmatch || coop)\r\n\t\treturn;\r\n\r\n\tswitch(argv(1))\r\n\t{\r\n\tcase \"1\": self.items |= IT_AXE; break;\t\t//not sure why you'd need this, but oh well.\r\n\tcase \"2\": self.items |= IT_SHOTGUN; break;\t//not sure why you'd need this, but oh well.\r\n\tcase \"3\": self.items |= IT_SUPER_SHOTGUN; break;\r\n\tcase \"4\": self.items |= IT_NAILGUN; break;\r\n\tcase \"5\": self.items |= IT_SUPER_NAILGUN; break;\r\n\tcase \"6\": self.items |= IT_GRENADE_LAUNCHER; break;\r\n\tcase \"7\": self.items |= IT_ROCKET_LAUNCHER; break;\r\n\tcase \"8\": self.items |= IT_LIGHTNING; break;\r\n\tcase \"s\": self.ammo_shells = stof(argv(2)); break;\r\n\tcase \"n\": self.ammo_nails = stof(argv(2)); break;\r\n\tcase \"r\": self.ammo_rockets = stof(argv(2)); break;\r\n\tcase \"c\": self.ammo_cells = stof(argv(2)); break;\r\n\tcase \"h\": self.health = stof(argv(2)); break;\r\n\tcase \"a\":\r\n\t\tself.armorvalue = stof(argv(2));\r\n\t\tif (self.armorvalue < 1)\r\n\t\t{\t//remove the armour type if they no longer have any.\r\n\t\t\tself.armortype = 0;\r\n\t\t\tself.items &= ~(IT_ARMOR1|IT_ARMOR2|IT_ARMOR3);\r\n\t\t}\r\n\t\telse if (self.armorvalue > 150 && self.armortype < 0.8)\r\n\t\t{\t//prmote to red if its too high for yellow...\r\n\t\t\tself.armortype = 0.8;\r\n\t\t\tself.items = (self.items & ~(IT_ARMOR1|IT_ARMOR2|IT_ARMOR3)) | IT_ARMOR3;\r\n\t\t}\r\n\t\telse if (self.armorvalue > 100 && self.armortype < 0.6)\r\n\t\t{\t//promote to yellow if its too high for green...\r\n\t\t\tself.armortype = 0.6;\r\n\t\t\tself.items = (self.items & ~(IT_ARMOR1|IT_ARMOR2|IT_ARMOR3)) | IT_ARMOR2;\r\n\t\t}\r\n\t\telse if (self.armortype < 0.3)\r\n\t\t{\t//make sure we have at least green armour...\r\n\t\t\tself.armortype = 0.3;\r\n\t\t\tself.items = (self.items & ~(IT_ARMOR1|IT_ARMOR2|IT_ARMOR3)) | IT_ARMOR1;\r\n\t\t}\r\n\t\tbreak;\r\n\tdefault:\r\n\t\tsprint(self, \"Item \\\"\", argv(1), \"\\\" not understood\\n\");\r\n\t\tbreak;\r\n\t}\r\n\tW_SetCurrentAmmo ();\r\n};\r\n\r\n/*\r\n============\r\nCycleWeaponCommand\r\n\r\nGo to the next weapon with ammo\r\n============\r\n*/\r\nvoid() CycleWeaponCommand =\r\n{\r\n\tlocal\tfloat\tit, am;\r\n\r\n\tit = self.items;\r\n\tself.impulse = 0;\r\n\r\n\twhile (1)\r\n\t{\r\n\t\tam = 0;\r\n\r\n\t\tif (self.weapon == IT_LIGHTNING)\r\n\t\t{\r\n\t\t\tself.weapon = IT_AXE;\r\n\t\t}\r\n\t\telse if (self.weapon == IT_AXE)\r\n\t\t{\r\n\t\t\tself.weapon = IT_SHOTGUN;\r\n\t\t\tif (self.ammo_shells < 1)\r\n\t\t\t\tam = 1;\r\n\t\t}\r\n\t\telse if (self.weapon == IT_SHOTGUN)\r\n\t\t{\r\n\t\t\tself.weapon = IT_SUPER_SHOTGUN;\r\n\t\t\tif (self.ammo_shells < 2)\r\n\t\t\t\tam = 1;\r\n\t\t}\r\n\t\telse if (self.weapon == IT_SUPER_SHOTGUN)\r\n\t\t{\r\n\t\t\tself.weapon = IT_NAILGUN;\r\n\t\t\tif (self.ammo_nails < 1)\r\n\t\t\t\tam = 1;\r\n\t\t}\r\n\t\telse if (self.weapon == IT_NAILGUN)\r\n\t\t{\r\n\t\t\tself.weapon = IT_SUPER_NAILGUN;\r\n\t\t\tif (self.ammo_nails < 2)\r\n\t\t\t\tam = 1;\r\n\t\t}\r\n\t\telse if (self.weapon == IT_SUPER_NAILGUN)\r\n\t\t{\r\n\t\t\tself.weapon = IT_GRENADE_LAUNCHER;\r\n\t\t\tif (self.ammo_rockets < 1)\r\n\t\t\t\tam = 1;\r\n\t\t}\r\n\t\telse if (self.weapon == IT_GRENADE_LAUNCHER)\r\n\t\t{\r\n\t\t\tself.weapon = IT_ROCKET_LAUNCHER;\r\n\t\t\tif (self.ammo_rockets < 1)\r\n\t\t\t\tam = 1;\r\n\t\t}\r\n\t\telse if (self.weapon == IT_ROCKET_LAUNCHER)\r\n\t\t{\r\n\t\t\tself.weapon = IT_LIGHTNING;\r\n\t\t\tif (self.ammo_cells < 1)\r\n\t\t\t\tam = 1;\r\n\t\t}\r\n\r\n\t\tif ( (it & self.weapon) && am == 0)\r\n\t\t{\r\n\t\t\tW_SetCurrentAmmo ();\r\n\t\t\treturn;\r\n\t\t}\r\n\t}\r\n\r\n};\r\n\r\n/*\r\n============\r\nCycleWeaponReverseCommand\r\n\r\nGo to the prev weapon with ammo\r\n============\r\n*/\r\nvoid() CycleWeaponReverseCommand =\r\n{\r\n\tlocal\tfloat\tit, am;\r\n\r\n\tit = self.items;\r\n\tself.impulse = 0;\r\n\r\n\twhile (1)\r\n\t{\r\n\t\tam = 0;\r\n\r\n\t\tif (self.weapon == IT_LIGHTNING)\r\n\t\t{\r\n\t\t\tself.weapon = IT_ROCKET_LAUNCHER;\r\n\t\t\tif (self.ammo_rockets < 1)\r\n\t\t\t\tam = 1;\r\n\t\t}\r\n\t\telse if (self.weapon == IT_ROCKET_LAUNCHER)\r\n\t\t{\r\n\t\t\tself.weapon = IT_GRENADE_LAUNCHER;\r\n\t\t\tif (self.ammo_rockets < 1)\r\n\t\t\t\tam = 1;\r\n\t\t}\r\n\t\telse if (self.weapon == IT_GRENADE_LAUNCHER)\r\n\t\t{\r\n\t\t\tself.weapon = IT_SUPER_NAILGUN;\r\n\t\t\tif (self.ammo_nails < 2)\r\n\t\t\t\tam = 1;\r\n\t\t}\r\n\t\telse if (self.weapon == IT_SUPER_NAILGUN)\r\n\t\t{\r\n\t\t\tself.weapon = IT_NAILGUN;\r\n\t\t\tif (self.ammo_nails < 1)\r\n\t\t\t\tam = 1;\r\n\t\t}\r\n\t\telse if (self.weapon == IT_NAILGUN)\r\n\t\t{\r\n\t\t\tself.weapon = IT_SUPER_SHOTGUN;\r\n\t\t\tif (self.ammo_shells < 2)\r\n\t\t\t\tam = 1;\r\n\t\t}\r\n\t\telse if (self.weapon == IT_SUPER_SHOTGUN)\r\n\t\t{\r\n\t\t\tself.weapon = IT_SHOTGUN;\r\n\t\t\tif (self.ammo_shells < 1)\r\n\t\t\t\tam = 1;\r\n\t\t}\r\n\t\telse if (self.weapon == IT_SHOTGUN)\r\n\t\t{\r\n\t\t\tself.weapon = IT_AXE;\r\n\t\t}\r\n\t\telse if (self.weapon == IT_AXE)\r\n\t\t{\r\n\t\t\tself.weapon = IT_LIGHTNING;\r\n\t\t\tif (self.ammo_cells < 1)\r\n\t\t\t\tam = 1;\r\n\t\t}\r\n\r\n\t\tif ( (it & self.weapon) && am == 0)\r\n\t\t{\r\n\t\t\tW_SetCurrentAmmo ();\r\n\t\t\treturn;\r\n\t\t}\r\n\t}\r\n\r\n};\r\n\r\n/*\r\n============\r\nServerflagsCommand\r\n\r\nJust for development\r\n============\r\n*/\r\nvoid() ServerflagsCommand =\r\n{\r\n\tserverflags = serverflags * 2 + 1;\r\n};\r\n\r\nvoid() QuadCheat =\r\n{\r\n\tif (deathmatch || coop)\r\n\t\treturn;\r\n\tself.super_time = 1;\r\n\tself.super_damage_finished = time + 30;\r\n\tself.items = self.items | IT_QUAD;\r\n\tdprint (\"quad cheat\\n\");\r\n};\r\n\r\n/*\r\n============\r\nImpulseCommands\r\n\r\n============\r\n*/\r\nvoid() ImpulseCommands =\r\n{\r\n\tif (self.impulse >= 1 && self.impulse <= 8)\r\n\t\tW_ChangeWeapon ();\r\n\r\n\tif (self.impulse == 9)\r\n\t\tCheatCommand ();\r\n\tif (self.impulse == 10)\r\n\t\tCycleWeaponCommand ();\r\n\tif (self.impulse == 11)\r\n\t\tServerflagsCommand ();\r\n\tif (self.impulse == 12)\r\n\t\tCycleWeaponReverseCommand ();\r\n\r\n\tif (self.impulse == 255)\r\n\t\tQuadCheat ();\r\n\r\n\tself.impulse = 0;\r\n};\r\n\r\n/*\r\n============\r\nW_WeaponFrame\r\n\r\nCalled every frame so impulse events can be handled as well as possible\r\n============\r\n*/\r\nvoid() W_WeaponFrame =\r\n{\r\n\tif (time < self.attack_finished)\r\n\t\treturn;\r\n\r\n\tImpulseCommands ();\r\n\r\n// check for attack\r\n\tif (self.button0)\r\n\t{\r\n\t\tSuperDamageSound ();\r\n\t\tW_Attack ();\r\n\t}\r\n};\r\n\r\n/*\r\n========\r\nSuperDamageSound\r\n\r\nPlays sound if needed\r\n========\r\n*/\r\nvoid() SuperDamageSound =\r\n{\r\n\tif (self.super_damage_finished > time)\r\n\t{\r\n\t\tif (self.super_sound < time)\r\n\t\t{\r\n\t\t\tself.super_sound = time + 1;\r\n\t\t\tsound (self, CHAN_BODY, \"items/damage3.wav\", 1, ATTN_NORM);\r\n\t\t}\r\n\t}\r\n\treturn;\r\n};\r\n\r\n\r\n"
  },
  {
    "path": "quakec/csqctest/src/ss/wizard.qc",
    "content": "/*\r\n==============================================================================\r\n\r\nWIZARD\r\n\r\n==============================================================================\r\n*/\r\n\r\n$cd id1/models/a_wizard\r\n$origin 0 0 24\r\n$base wizbase\t\r\n$skin wizbase\r\n\r\n$frame hover1 hover2 hover3 hover4 hover5 hover6 hover7 hover8\r\n$frame hover9 hover10 hover11 hover12 hover13 hover14 hover15\r\n\r\n$frame fly1 fly2 fly3 fly4 fly5 fly6 fly7 fly8 fly9 fly10\r\n$frame fly11 fly12 fly13 fly14\r\n\r\n$frame magatt1 magatt2 magatt3 magatt4 magatt5 magatt6 magatt7\r\n$frame magatt8 magatt9 magatt10 magatt11 magatt12 magatt13\r\n\r\n$frame pain1 pain2 pain3 pain4\r\n\r\n$frame death1 death2 death3 death4 death5 death6 death7 death8\r\n\r\n/*\r\n==============================================================================\r\n\r\nWIZARD\r\n\r\nIf the player moves behind cover before the missile is launched, launch it\r\nat the last visible spot with no velocity leading, in hopes that the player\r\nwill duck back out and catch it.\r\n==============================================================================\r\n*/\r\n\r\n/*\r\n=============\r\nLaunchMissile\r\n\r\nSets the given entities velocity and angles so that it will hit self.enemy\r\nif self.enemy maintains it's current velocity\r\n0.1 is moderately accurate, 0.0 is totally accurate\r\n=============\r\n*/\r\n/*\r\nvoid(entity missile, float mspeed, float accuracy) LaunchMissile =\r\n{\r\n\tlocal\tvector\tvec, move;\r\n\tlocal\tfloat\tfly;\r\n\r\n\tmakevectors (self.angles);\r\n\t\t\r\n// set missile speed\r\n\tvec = self.enemy.origin + self.enemy.mins + self.enemy.size * 0.7 - missile.origin;\r\n\r\n// calc aproximate time for missile to reach vec\r\n\tfly = vlen (vec) / mspeed;\r\n\t\r\n// get the entities xy velocity\r\n\tmove = self.enemy.velocity;\r\n\tmove_z = 0;\r\n\r\n// project the target forward in time\r\n\tvec = vec + move * fly;\r\n\t\r\n\tvec = normalize(vec);\r\n\tvec = vec + accuracy*v_up*(random()- 0.5) + accuracy*v_right*(random()- 0.5);\r\n\t\r\n\tmissile.velocity = vec * mspeed;\r\n\r\n\tmissile.angles = '0 0 0';\r\n\tmissile.angles_y = vectoyaw(missile.velocity);\r\n\r\n// set missile duration\r\n\tmissile.nextthink = time + 5;\r\n\tmissile.think = SUB_Remove;\t\r\n};\r\n*/\r\n\r\nvoid() wiz_run1;\r\nvoid() wiz_side1;\r\n\r\n/*\r\n=================\r\nWizardCheckAttack\r\n=================\r\n*/\r\nfloat()\tWizardCheckAttack =\r\n{\r\n\tlocal vector\tspot1, spot2;\t\r\n\tlocal entity\ttarg;\r\n\tlocal float\t\tchance;\r\n\r\n\tif (time < self.attack_finished)\r\n\t\treturn FALSE;\r\n\tif (!enemy_vis)\r\n\t\treturn FALSE;\r\n\r\n\tif (enemy_range == RANGE_FAR)\r\n\t{\r\n\t\tif (self.attack_state != AS_STRAIGHT)\r\n\t\t{\r\n\t\t\tself.attack_state = AS_STRAIGHT;\r\n\t\t\twiz_run1 ();\r\n\t\t}\r\n\t\treturn FALSE;\r\n\t}\r\n\t\t\r\n\ttarg = self.enemy;\r\n\t\r\n// see if any entities are in the way of the shot\r\n\tspot1 = self.origin + self.view_ofs;\r\n\tspot2 = targ.origin + targ.view_ofs;\r\n\r\n\ttraceline (spot1, spot2, FALSE, self);\r\n\r\n\tif (trace_ent != targ)\r\n\t{\t// don't have a clear shot, so move to a side\r\n\t\tif (self.attack_state != AS_STRAIGHT)\r\n\t\t{\r\n\t\t\tself.attack_state = AS_STRAIGHT;\r\n\t\t\twiz_run1 ();\r\n\t\t}\r\n\t\treturn FALSE;\r\n\t}\r\n\t\t\t\r\n\tif (enemy_range == RANGE_MELEE)\r\n\t\tchance = 0.9;\r\n\telse if (enemy_range == RANGE_NEAR)\r\n\t\tchance = 0.6;\r\n\telse if (enemy_range == RANGE_MID)\r\n\t\tchance = 0.2;\r\n\telse\r\n\t\tchance = 0;\r\n\r\n\tif (random () < chance)\r\n\t{\r\n\t\tself.attack_state = AS_MISSILE;\r\n\t\treturn TRUE;\r\n\t}\r\n\r\n\tif (enemy_range == RANGE_MID)\r\n\t{\r\n\t\tif (self.attack_state != AS_STRAIGHT)\r\n\t\t{\r\n\t\t\tself.attack_state = AS_STRAIGHT;\r\n\t\t\twiz_run1 ();\r\n\t\t}\r\n\t}\r\n\telse\r\n\t{\r\n\t\tif (self.attack_state != AS_SLIDING)\r\n\t\t{\r\n\t\t\tself.attack_state = AS_SLIDING;\r\n\t\t\twiz_side1 ();\r\n\t\t}\r\n\t}\r\n\t\r\n\treturn FALSE;\r\n};\r\n\r\n/*\r\n=================\r\nWizardAttackFinished\r\n=================\r\n*/\r\nvoid()\tWizardAttackFinished =\r\n{\r\n\tif (enemy_range >= RANGE_MID || !enemy_vis)\r\n\t{\r\n\t\tself.attack_state = AS_STRAIGHT;\r\n\t\tself.think = wiz_run1;\r\n\t}\r\n\telse\r\n\t{\r\n\t\tself.attack_state = AS_SLIDING;\r\n\t\tself.think = wiz_side1;\r\n\t}\r\n};\r\n\r\n/*\r\n==============================================================================\r\n\r\nFAST ATTACKS\r\n\r\n==============================================================================\r\n*/\r\n\r\nvoid() Wiz_FastFire =\r\n{\r\n\tlocal vector\t\tvec;\r\n\tlocal vector\t\tdst;\r\n\r\n\tif (self.owner.health > 0)\r\n\t{\r\n\t\tself.owner.effects = self.owner.effects | EF_MUZZLEFLASH;\r\n\r\n\t\tmakevectors (self.enemy.angles);\t\r\n\t\tdst = self.enemy.origin - 13*self.movedir;\r\n\t\r\n\t\tvec = normalize(dst - self.origin);\r\n\t\tsound (self, CHAN_WEAPON, \"wizard/wattack.wav\", 1, ATTN_NORM);\r\n\t\tlaunch_spike (self.origin, vec);\r\n\t\tnewmis.velocity = vec*600;\r\n\t\tnewmis.owner = self.owner;\r\n\t\tnewmis.classname = \"wizspike\";\r\n\t\tsetmodel (newmis, \"progs/w_spike.mdl\");\r\n\t\tsetsize (newmis, VEC_ORIGIN, VEC_ORIGIN);\t\t\r\n\t}\r\n\r\n\tremove (self);\r\n};\r\n\r\n\r\nvoid() Wiz_StartFast =\r\n{\r\n\tlocal\tentity\tmissile;\r\n\t\r\n\tsound (self, CHAN_WEAPON, \"wizard/wattack.wav\", 1, ATTN_NORM);\r\n\tself.v_angle = self.angles;\r\n\tmakevectors (self.angles);\r\n\r\n\tmissile = spawn ();\r\n\tmissile.owner = self;\r\n\tmissile.nextthink = time + 0.6;\r\n\tsetsize (missile, '0 0 0', '0 0 0');\t\t\r\n\tsetorigin (missile, self.origin + '0 0 30' + v_forward*14 + v_right*14);\r\n\tmissile.enemy = self.enemy;\r\n\tmissile.nextthink = time + 0.8;\r\n\tmissile.think = Wiz_FastFire;\r\n\tmissile.movedir = v_right;\r\n\r\n\tmissile = spawn ();\r\n\tmissile.owner = self;\r\n\tmissile.nextthink = time + 1;\r\n\tsetsize (missile, '0 0 0', '0 0 0');\t\t\r\n\tsetorigin (missile, self.origin + '0 0 30' + v_forward*14 + v_right* -14);\r\n\tmissile.enemy = self.enemy;\r\n\tmissile.nextthink = time + 0.3;\r\n\tmissile.think = Wiz_FastFire;\r\n\tmissile.movedir = VEC_ORIGIN - v_right;\r\n};\r\n\r\n\r\n\r\nvoid() Wiz_idlesound =\r\n{\r\nlocal float wr;\r\n\twr = random() * 5;\r\n\r\n\tif (self.waitmin < time)\r\n\t{\r\n\t \tself.waitmin = time + 2;\r\n\t \tif (wr > 4.5) \r\n\t \t\tsound (self, CHAN_VOICE, \"wizard/widle1.wav\", 1,  ATTN_IDLE);\r\n\t \tif (wr < 1.5)\r\n\t \t\tsound (self, CHAN_VOICE, \"wizard/widle2.wav\", 1, ATTN_IDLE);\r\n\t}\r\n\treturn;\r\n};\r\n\r\nvoid()\twiz_stand1\t=[\t$hover1,\t\twiz_stand2\t] {ai_stand();};\r\nvoid()\twiz_stand2\t=[\t$hover2,\t\twiz_stand3\t] {ai_stand();};\r\nvoid()\twiz_stand3\t=[\t$hover3,\t\twiz_stand4\t] {ai_stand();};\r\nvoid()\twiz_stand4\t=[\t$hover4,\t\twiz_stand5\t] {ai_stand();};\r\nvoid()\twiz_stand5\t=[\t$hover5,\t\twiz_stand6\t] {ai_stand();};\r\nvoid()\twiz_stand6\t=[\t$hover6,\t\twiz_stand7\t] {ai_stand();};\r\nvoid()\twiz_stand7\t=[\t$hover7,\t\twiz_stand8\t] {ai_stand();};\r\nvoid()\twiz_stand8\t=[\t$hover8,\t\twiz_stand1\t] {ai_stand();};\r\n\r\nvoid()\twiz_walk1\t=[\t$hover1,\t\twiz_walk2\t] {ai_walk(8);\r\nWiz_idlesound();};\r\nvoid()\twiz_walk2\t=[\t$hover2,\t\twiz_walk3\t] {ai_walk(8);};\r\nvoid()\twiz_walk3\t=[\t$hover3,\t\twiz_walk4\t] {ai_walk(8);};\r\nvoid()\twiz_walk4\t=[\t$hover4,\t\twiz_walk5\t] {ai_walk(8);};\r\nvoid()\twiz_walk5\t=[\t$hover5,\t\twiz_walk6\t] {ai_walk(8);};\r\nvoid()\twiz_walk6\t=[\t$hover6,\t\twiz_walk7\t] {ai_walk(8);};\r\nvoid()\twiz_walk7\t=[\t$hover7,\t\twiz_walk8\t] {ai_walk(8);};\r\nvoid()\twiz_walk8\t=[\t$hover8,\t\twiz_walk1\t] {ai_walk(8);};\r\n\r\nvoid()\twiz_side1\t=[\t$hover1,\t\twiz_side2\t] {ai_run(8);\r\nWiz_idlesound();};\r\nvoid()\twiz_side2\t=[\t$hover2,\t\twiz_side3\t] {ai_run(8);};\r\nvoid()\twiz_side3\t=[\t$hover3,\t\twiz_side4\t] {ai_run(8);};\r\nvoid()\twiz_side4\t=[\t$hover4,\t\twiz_side5\t] {ai_run(8);};\r\nvoid()\twiz_side5\t=[\t$hover5,\t\twiz_side6\t] {ai_run(8);};\r\nvoid()\twiz_side6\t=[\t$hover6,\t\twiz_side7\t] {ai_run(8);};\r\nvoid()\twiz_side7\t=[\t$hover7,\t\twiz_side8\t] {ai_run(8);};\r\nvoid()\twiz_side8\t=[\t$hover8,\t\twiz_side1\t] {ai_run(8);};\r\n\r\nvoid()\twiz_run1\t=[\t$fly1,\t\twiz_run2\t] {ai_run(16);\r\nWiz_idlesound();\r\n};\r\nvoid()\twiz_run2\t=[\t$fly2,\t\twiz_run3\t] {ai_run(16);};\r\nvoid()\twiz_run3\t=[\t$fly3,\t\twiz_run4\t] {ai_run(16);};\r\nvoid()\twiz_run4\t=[\t$fly4,\t\twiz_run5\t] {ai_run(16);};\r\nvoid()\twiz_run5\t=[\t$fly5,\t\twiz_run6\t] {ai_run(16);};\r\nvoid()\twiz_run6\t=[\t$fly6,\t\twiz_run7\t] {ai_run(16);};\r\nvoid()\twiz_run7\t=[\t$fly7,\t\twiz_run8\t] {ai_run(16);};\r\nvoid()\twiz_run8\t=[\t$fly8,\t\twiz_run9\t] {ai_run(16);};\r\nvoid()\twiz_run9\t=[\t$fly9,\t\twiz_run10\t] {ai_run(16);};\r\nvoid()\twiz_run10\t=[\t$fly10,\t\twiz_run11\t] {ai_run(16);};\r\nvoid()\twiz_run11\t=[\t$fly11,\t\twiz_run12\t] {ai_run(16);};\r\nvoid()\twiz_run12\t=[\t$fly12,\t\twiz_run13\t] {ai_run(16);};\r\nvoid()\twiz_run13\t=[\t$fly13,\t\twiz_run14\t] {ai_run(16);};\r\nvoid()\twiz_run14\t=[\t$fly14,\t\twiz_run1\t] {ai_run(16);};\r\n\r\nvoid()\twiz_fast1\t=[\t$magatt1,\t\twiz_fast2\t] {ai_face();Wiz_StartFast();};\r\nvoid()\twiz_fast2\t=[\t$magatt2,\t\twiz_fast3\t] {ai_face();};\r\nvoid()\twiz_fast3\t=[\t$magatt3,\t\twiz_fast4\t] {ai_face();};\r\nvoid()\twiz_fast4\t=[\t$magatt4,\t\twiz_fast5\t] {ai_face();};\r\nvoid()\twiz_fast5\t=[\t$magatt5,\t\twiz_fast6\t] {ai_face();};\r\nvoid()\twiz_fast6\t=[\t$magatt6,\t\twiz_fast7\t] {ai_face();};\r\nvoid()\twiz_fast7\t=[\t$magatt5,\t\twiz_fast8\t] {ai_face();};\r\nvoid()\twiz_fast8\t=[\t$magatt4,\t\twiz_fast9\t] {ai_face();};\r\nvoid()\twiz_fast9\t=[\t$magatt3,\t\twiz_fast10\t] {ai_face();};\r\nvoid()\twiz_fast10\t=[\t$magatt2,\t\twiz_run1\t] {ai_face();SUB_AttackFinished(2);WizardAttackFinished ();};\r\n\r\nvoid()\twiz_pain1\t=[\t$pain1,\t\twiz_pain2\t] {};\r\nvoid()\twiz_pain2\t=[\t$pain2,\t\twiz_pain3\t] {};\r\nvoid()\twiz_pain3\t=[\t$pain3,\t\twiz_pain4\t] {};\r\nvoid()\twiz_pain4\t=[\t$pain4,\t\twiz_run1\t] {};\r\n\r\nvoid()\twiz_death1\t=[\t$death1,\t\twiz_death2\t] {\r\n\r\nself.velocity_x = -200 + 400*random();\r\nself.velocity_y = -200 + 400*random();\r\nself.velocity_z = 100 + 100*random();\r\nself.flags = self.flags - (self.flags & FL_ONGROUND);\r\nsound (self, CHAN_VOICE, \"wizard/wdeath.wav\", 1, ATTN_NORM);\r\n};\r\nvoid()\twiz_death2\t=[\t$death2,\t\twiz_death3\t] {};\r\nvoid()\twiz_death3\t=[\t$death3,\t\twiz_death4\t]{self.solid = SOLID_NOT;};\r\nvoid()\twiz_death4\t=[\t$death4,\t\twiz_death5\t] {};\r\nvoid()\twiz_death5\t=[\t$death5,\t\twiz_death6\t] {};\r\nvoid()\twiz_death6\t=[\t$death6,\t\twiz_death7\t] {};\r\nvoid()\twiz_death7\t=[\t$death7,\t\twiz_death8\t] {};\r\nvoid()\twiz_death8\t=[\t$death8,\t\twiz_death8\t] {};\r\n\r\nvoid() wiz_die =\r\n{\r\n// check for gib\r\n\tif (self.health < -40)\r\n\t{\r\n\t\tThrowCSQCGibs(GIB_WIZARD);\r\n\t\treturn;\r\n\t}\r\n\r\n\twiz_death1 ();\r\n};\r\n\r\n\r\nvoid(entity attacker, float damage) Wiz_Pain =\r\n{\r\n\tsound (self, CHAN_VOICE, \"wizard/wpain.wav\", 1, ATTN_NORM);\r\n\tif (random()*70 > damage)\r\n\t\treturn;\t\t// didn't flinch\r\n\r\n\twiz_pain1 ();\r\n};\r\n\r\n\r\nvoid() Wiz_Missile =\r\n{\r\n\twiz_fast1();\r\n};\r\n\r\n/*QUAKED monster_wizard (1 0 0) (-16 -16 -24) (16 16 40) Ambush\r\n*/\r\nvoid() monster_wizard =\r\n{\r\n\tif (deathmatch)\r\n\t{\r\n\t\tremove(self);\r\n\t\treturn;\r\n\t}\r\n\tprecache_model (\"progs/wizard.mdl\");\r\n\tprecache_model (\"progs/h_wizard.mdl\");\r\n\tprecache_model (\"progs/w_spike.mdl\");\r\n\r\n\tprecache_sound (\"wizard/hit.wav\");\t\t// used by c code\r\n\tprecache_sound (\"wizard/wattack.wav\");\r\n\tprecache_sound (\"wizard/wdeath.wav\");\r\n\tprecache_sound (\"wizard/widle1.wav\");\r\n\tprecache_sound (\"wizard/widle2.wav\");\r\n\tprecache_sound (\"wizard/wpain.wav\");\r\n\tprecache_sound (\"wizard/wsight.wav\");\r\n\r\n\tself.solid = SOLID_SLIDEBOX;\r\n\tself.movetype = MOVETYPE_STEP;\r\n\r\n\tsetmodel (self, \"progs/wizard.mdl\");\r\n\r\n\tsetsize (self, '-16 -16 -24', '16 16 40');\r\n\tself.health = 80;\r\n\r\n\tself.th_stand = wiz_stand1;\r\n\tself.th_walk = wiz_walk1;\r\n\tself.th_run = wiz_run1;\r\n\tself.th_missile = Wiz_Missile;\r\n\tself.th_pain = Wiz_Pain;\r\n\tself.th_die = wiz_die;\r\n\t\t\r\n\tflymonster_start ();\r\n};\r\n"
  },
  {
    "path": "quakec/csqctest/src/ss/world.qc",
    "content": "void() InitBodyQue;\r\n\r\nvoid() main =\r\n{\r\n\tdprint (\"main function\\n\");\r\n\t\r\n// these are just commands the the prog compiler to copy these files\r\n\r\n\tprecache_file (\"progs.dat\");\r\n\tprecache_file (\"gfx.wad\");\r\n\tprecache_file (\"quake.rc\");\r\n\tprecache_file (\"default.cfg\");\r\n\r\n\tprecache_file (\"end1.bin\");\r\n\tprecache_file2 (\"end2.bin\");\r\n\r\n\tprecache_file (\"demo1.dem\");\r\n\tprecache_file (\"demo2.dem\");\r\n\tprecache_file (\"demo3.dem\");\r\n\r\n//\r\n// these are all of the lumps from the cached.ls files\r\n//\r\n\tprecache_file (\"gfx/palette.lmp\");\r\n\tprecache_file (\"gfx/colormap.lmp\");\r\n\r\n\tprecache_file2 (\"gfx/pop.lmp\");\r\n\r\n\tprecache_file (\"gfx/complete.lmp\");\r\n\tprecache_file (\"gfx/inter.lmp\");\r\n\r\n\tprecache_file (\"gfx/ranking.lmp\");\r\n\tprecache_file (\"gfx/vidmodes.lmp\");\r\n\tprecache_file (\"gfx/finale.lmp\");\r\n\tprecache_file (\"gfx/conback.lmp\");\r\n\tprecache_file (\"gfx/qplaque.lmp\");\r\n\r\n\tprecache_file (\"gfx/menudot1.lmp\");\r\n\tprecache_file (\"gfx/menudot2.lmp\");\r\n\tprecache_file (\"gfx/menudot3.lmp\");\r\n\tprecache_file (\"gfx/menudot4.lmp\");\r\n\tprecache_file (\"gfx/menudot5.lmp\");\r\n\tprecache_file (\"gfx/menudot6.lmp\");\r\n\r\n\tprecache_file (\"gfx/menuplyr.lmp\");\r\n\tprecache_file (\"gfx/bigbox.lmp\");\r\n\tprecache_file (\"gfx/dim_modm.lmp\");\r\n\tprecache_file (\"gfx/dim_drct.lmp\");\r\n\tprecache_file (\"gfx/dim_ipx.lmp\");\r\n\tprecache_file (\"gfx/dim_tcp.lmp\");\r\n\tprecache_file (\"gfx/dim_mult.lmp\");\r\n\tprecache_file (\"gfx/mainmenu.lmp\");\r\n\t\r\n\tprecache_file (\"gfx/box_tl.lmp\");\r\n\tprecache_file (\"gfx/box_tm.lmp\");\r\n\tprecache_file (\"gfx/box_tr.lmp\");\r\n\t\r\n\tprecache_file (\"gfx/box_ml.lmp\");\r\n\tprecache_file (\"gfx/box_mm.lmp\");\r\n\tprecache_file (\"gfx/box_mm2.lmp\");\r\n\tprecache_file (\"gfx/box_mr.lmp\");\r\n\t\r\n\tprecache_file (\"gfx/box_bl.lmp\");\r\n\tprecache_file (\"gfx/box_bm.lmp\");\r\n\tprecache_file (\"gfx/box_br.lmp\");\r\n\t\r\n\tprecache_file (\"gfx/sp_menu.lmp\");\r\n\tprecache_file (\"gfx/ttl_sgl.lmp\");\r\n\tprecache_file (\"gfx/ttl_main.lmp\");\r\n\tprecache_file (\"gfx/ttl_cstm.lmp\");\r\n\t\r\n\tprecache_file (\"gfx/mp_menu.lmp\");\r\n\t\r\n\tprecache_file (\"gfx/netmen1.lmp\");\r\n\tprecache_file (\"gfx/netmen2.lmp\");\r\n\tprecache_file (\"gfx/netmen3.lmp\");\r\n\tprecache_file (\"gfx/netmen4.lmp\");\r\n\tprecache_file (\"gfx/netmen5.lmp\");\r\n\t\r\n\tprecache_file (\"gfx/sell.lmp\");\r\n\t\r\n\tprecache_file (\"gfx/help0.lmp\");\r\n\tprecache_file (\"gfx/help1.lmp\");\r\n\tprecache_file (\"gfx/help2.lmp\");\r\n\tprecache_file (\"gfx/help3.lmp\");\r\n\tprecache_file (\"gfx/help4.lmp\");\r\n\tprecache_file (\"gfx/help5.lmp\");\r\n\r\n\tprecache_file (\"gfx/pause.lmp\");\r\n\tprecache_file (\"gfx/loading.lmp\");\r\n\r\n\tprecache_file (\"gfx/p_option.lmp\");\r\n\tprecache_file (\"gfx/p_load.lmp\");\r\n\tprecache_file (\"gfx/p_save.lmp\");\r\n\tprecache_file (\"gfx/p_multi.lmp\");\r\n\r\n// sounds loaded by C code\r\n\tprecache_sound (\"misc/menu1.wav\");\r\n\tprecache_sound (\"misc/menu2.wav\");\r\n\tprecache_sound (\"misc/menu3.wav\");\r\n\r\n\tprecache_sound (\"ambience/water1.wav\");\r\n\tprecache_sound (\"ambience/wind2.wav\");\r\n\r\n// shareware\r\n\tprecache_file (\"maps/start.bsp\");\r\n\r\n\tprecache_file (\"maps/e1m1.bsp\");\r\n\tprecache_file (\"maps/e1m2.bsp\");\r\n\tprecache_file (\"maps/e1m3.bsp\");\r\n\tprecache_file (\"maps/e1m4.bsp\");\r\n\tprecache_file (\"maps/e1m5.bsp\");\r\n\tprecache_file (\"maps/e1m6.bsp\");\r\n\tprecache_file (\"maps/e1m7.bsp\");\r\n\tprecache_file (\"maps/e1m8.bsp\");\r\n\r\n// registered\r\n\tprecache_file2 (\"gfx/pop.lmp\");\r\n\r\n\tprecache_file2 (\"maps/e2m1.bsp\");\r\n\tprecache_file2 (\"maps/e2m2.bsp\");\r\n\tprecache_file2 (\"maps/e2m3.bsp\");\r\n\tprecache_file2 (\"maps/e2m4.bsp\");\r\n\tprecache_file2 (\"maps/e2m5.bsp\");\r\n\tprecache_file2 (\"maps/e2m6.bsp\");\r\n\tprecache_file2 (\"maps/e2m7.bsp\");\r\n\r\n\tprecache_file2 (\"maps/e3m1.bsp\");\r\n\tprecache_file2 (\"maps/e3m2.bsp\");\r\n\tprecache_file2 (\"maps/e3m3.bsp\");\r\n\tprecache_file2 (\"maps/e3m4.bsp\");\r\n\tprecache_file2 (\"maps/e3m5.bsp\");\r\n\tprecache_file2 (\"maps/e3m6.bsp\");\r\n\tprecache_file2 (\"maps/e3m7.bsp\");\r\n\r\n\tprecache_file2 (\"maps/e4m1.bsp\");\r\n\tprecache_file2 (\"maps/e4m2.bsp\");\r\n\tprecache_file2 (\"maps/e4m3.bsp\");\r\n\tprecache_file2 (\"maps/e4m4.bsp\");\r\n\tprecache_file2 (\"maps/e4m5.bsp\");\r\n\tprecache_file2 (\"maps/e4m6.bsp\");\r\n\tprecache_file2 (\"maps/e4m7.bsp\");\r\n\tprecache_file2 (\"maps/e4m8.bsp\");\r\n\r\n\tprecache_file2 (\"maps/end.bsp\");\r\n\r\n\tprecache_file2 (\"maps/dm1.bsp\");\r\n\tprecache_file2 (\"maps/dm2.bsp\");\r\n\tprecache_file2 (\"maps/dm3.bsp\");\r\n\tprecache_file2 (\"maps/dm4.bsp\");\r\n\tprecache_file2 (\"maps/dm5.bsp\");\r\n\tprecache_file2 (\"maps/dm6.bsp\");\r\n};\r\n\r\n\r\nentity\tlastspawn;\r\n\r\n//=======================\r\n/*QUAKED worldspawn (0 0 0) ?\r\nOnly used for the world entity.\r\nSet message to the level name.\r\nSet sounds to the cd track to play.\r\n\r\nWorld Types:\r\n0: medieval\r\n1: metal\r\n2: base\r\n*/\r\n//=======================\r\nvoid() worldspawn =\r\n{\r\n\tlastspawn = world;\r\n\tInitBodyQue();\r\n\r\n// custom map attributes\r\n\tif (self.model == \"maps/e1m8.bsp\")\r\n\t\tcvar_set (\"sv_gravity\", \"100\");\r\n\telse\r\n\t\tcvar_set (\"sv_gravity\", \"800\");\r\n\r\n// the area based ambient sounds MUST be the first precache_sounds\r\n\r\n// player precaches\t\r\n\tW_Precache ();\t\t\t// get weapon precaches\r\n\r\n// sounds used from C physics code\r\n\tprecache_sound (\"demon/dland2.wav\");\t\t// landing thud\r\n\tprecache_sound (\"misc/h2ohit1.wav\");\t\t// landing splash\r\n\r\n// setup precaches allways needed\r\n\tprecache_sound (\"items/itembk2.wav\");\t\t// item respawn sound\r\n\tprecache_sound (\"player/plyrjmp8.wav\");\t\t// player jump\r\n\tprecache_sound (\"player/land.wav\");\t\t\t// player landing\r\n\tprecache_sound (\"player/land2.wav\");\t\t// player hurt landing\r\n\tprecache_sound (\"player/drown1.wav\");\t\t// drowning pain\r\n\tprecache_sound (\"player/drown2.wav\");\t\t// drowning pain\r\n\tprecache_sound (\"player/gasp1.wav\");\t\t// gasping for air\r\n\tprecache_sound (\"player/gasp2.wav\");\t\t// taking breath\r\n\tprecache_sound (\"player/h2odeath.wav\");\t\t// drowning death\r\n\r\n\tprecache_sound (\"misc/talk.wav\");\t\t\t// talk\r\n\tprecache_sound (\"player/teledth1.wav\");\t\t// telefrag\r\n\tprecache_sound (\"misc/r_tele1.wav\");\t\t// teleport sounds\r\n\tprecache_sound (\"misc/r_tele2.wav\");\r\n\tprecache_sound (\"misc/r_tele3.wav\");\r\n\tprecache_sound (\"misc/r_tele4.wav\");\r\n\tprecache_sound (\"misc/r_tele5.wav\");\r\n\tprecache_sound (\"weapons/lock4.wav\");\t\t// ammo pick up\r\n\tprecache_sound (\"weapons/pkup.wav\");\t\t// weapon up\r\n\tprecache_sound (\"items/armor1.wav\");\t\t// armor up\r\n\tprecache_sound (\"weapons/lhit.wav\");\t\t//lightning\r\n\tprecache_sound (\"weapons/lstart.wav\");\t\t//lightning start\r\n\tprecache_sound (\"items/damage3.wav\");\r\n\r\n\tprecache_sound (\"misc/power.wav\");\t\t\t//lightning for boss\r\n\r\n// player gib sounds\r\n\tprecache_sound (\"player/gib.wav\");\t\t\t// player gib sound\r\n\tprecache_sound (\"player/udeath.wav\");\t\t// player gib sound\r\n\tprecache_sound (\"player/tornoff2.wav\");\t\t// gib sound\r\n\r\n// player pain sounds\r\n\r\n\tprecache_sound (\"player/pain1.wav\");\r\n\tprecache_sound (\"player/pain2.wav\");\r\n\tprecache_sound (\"player/pain3.wav\");\r\n\tprecache_sound (\"player/pain4.wav\");\r\n\tprecache_sound (\"player/pain5.wav\");\r\n\tprecache_sound (\"player/pain6.wav\");\r\n\r\n// player death sounds\r\n\tprecache_sound (\"player/death1.wav\");\r\n\tprecache_sound (\"player/death2.wav\");\r\n\tprecache_sound (\"player/death3.wav\");\r\n\tprecache_sound (\"player/death4.wav\");\r\n\tprecache_sound (\"player/death5.wav\");\r\n\r\n// ax sounds\t\r\n\tprecache_sound (\"weapons/ax1.wav\");\t\t\t// ax swoosh\r\n\tprecache_sound (\"player/axhit1.wav\");\t\t// ax hit meat\r\n\tprecache_sound (\"player/axhit2.wav\");\t\t// ax hit world\r\n\r\n\tprecache_sound (\"player/h2ojump.wav\");\t\t// player jumping into water\r\n\tprecache_sound (\"player/slimbrn2.wav\");\t\t// player enter slime\r\n\tprecache_sound (\"player/inh2o.wav\");\t\t// player enter water\r\n\tprecache_sound (\"player/inlava.wav\");\t\t// player enter lava\r\n\tprecache_sound (\"misc/outwater.wav\");\t\t// leaving water sound\r\n\r\n\tprecache_sound (\"player/lburn1.wav\");\t\t// lava burn\r\n\tprecache_sound (\"player/lburn2.wav\");\t\t// lava burn\r\n\r\n\tprecache_sound (\"misc/water1.wav\");\t\t\t// swimming\r\n\tprecache_sound (\"misc/water2.wav\");\t\t\t// swimming\r\n\r\n\tprecache_model (\"progs/player.mdl\");\r\n\tprecache_model (\"progs/eyes.mdl\");\r\n\tprecache_model (\"progs/h_player.mdl\");\r\n\tprecache_model (\"progs/gib1.mdl\");\r\n\tprecache_model (\"progs/gib2.mdl\");\r\n\tprecache_model (\"progs/gib3.mdl\");\r\n\r\n\tprecache_model (\"progs/s_bubble.spr\");\t// drowning bubbles\r\n\tprecache_model (\"progs/s_explod.spr\");\t// sprite explosion\r\n\r\n\tprecache_model (\"progs/v_axe.mdl\");\r\n\tprecache_model (\"progs/v_shot.mdl\");\r\n\tprecache_model (\"progs/v_nail.mdl\");\r\n\tprecache_model (\"progs/v_rock.mdl\");\r\n\tprecache_model (\"progs/v_shot2.mdl\");\r\n\tprecache_model (\"progs/v_nail2.mdl\");\r\n\tprecache_model (\"progs/v_rock2.mdl\");\r\n\r\n\tprecache_model (\"progs/bolt.mdl\");\t\t// for lightning gun\r\n\tprecache_model (\"progs/bolt2.mdl\");\t\t// for lightning gun\r\n\tprecache_model (\"progs/bolt3.mdl\");\t\t// for boss shock\r\n\tprecache_model (\"progs/lavaball.mdl\");\t// for testing\r\n\t\r\n\tprecache_model (\"progs/missile.mdl\");\r\n\tprecache_model (\"progs/grenade.mdl\");\r\n\tprecache_model (\"progs/spike.mdl\");\r\n\tprecache_model (\"progs/s_spike.mdl\");\r\n\r\n\tprecache_model (\"progs/backpack.mdl\");\r\n\r\n\tprecache_model (\"progs/zom_gib.mdl\");\r\n\r\n\tprecache_model (\"progs/v_light.mdl\");\r\n\t\r\n\r\n//\r\n// Setup light animation tables. 'a' is total darkness, 'z' is maxbright.\r\n//\r\n\r\n\t// 0 normal\r\n\tlightstyle(0, \"m\");\r\n\t\r\n\t// 1 FLICKER (first variety)\r\n\tlightstyle(1, \"mmnmmommommnonmmonqnmmo\");\r\n\t\r\n\t// 2 SLOW STRONG PULSE\r\n\tlightstyle(2, \"abcdefghijklmnopqrstuvwxyzyxwvutsrqponmlkjihgfedcba\");\r\n\t\r\n\t// 3 CANDLE (first variety)\r\n\tlightstyle(3, \"mmmmmaaaaammmmmaaaaaabcdefgabcdefg\");\r\n\t\r\n\t// 4 FAST STROBE\r\n\tlightstyle(4, \"mamamamamama\");\r\n\t\r\n\t// 5 GENTLE PULSE 1\r\n\tlightstyle(5,\"jklmnopqrstuvwxyzyxwvutsrqponmlkj\");\r\n\t\r\n\t// 6 FLICKER (second variety)\r\n\tlightstyle(6, \"nmonqnmomnmomomno\");\r\n\t\r\n\t// 7 CANDLE (second variety)\r\n\tlightstyle(7, \"mmmaaaabcdefgmmmmaaaammmaamm\");\r\n\t\r\n\t// 8 CANDLE (third variety)\r\n\tlightstyle(8, \"mmmaaammmaaammmabcdefaaaammmmabcdefmmmaaaa\");\r\n\t\r\n\t// 9 SLOW STROBE (fourth variety)\r\n\tlightstyle(9, \"aaaaaaaazzzzzzzz\");\r\n\t\r\n\t// 10 FLUORESCENT FLICKER\r\n\tlightstyle(10, \"mmamammmmammamamaaamammma\");\r\n\r\n\t// 11 SLOW PULSE NOT FADE TO BLACK\r\n\tlightstyle(11, \"abcdefghijklmnopqrrqponmlkjihgfedcba\");\r\n\t\r\n\t// styles 32-62 are assigned by the light program for switchable lights\r\n\r\n\t// 63 testing\r\n\tlightstyle(63, \"a\");\r\n\r\n\tserverusingcsqc = checkextension(\"EXT_CSQC\") || checkextension(\"EXT_CSQC_1\");\r\n\tif (!serverusingcsqc)\r\n\t{\r\n\t\tif (checkbuiltin(print))\r\n\t\t\tprint(\"Server does not support csqc\\n\");\r\n\t\telse\r\n\t\t{\r\n\t\t\tcvar_set(\"developer\", \"1\");\r\n\t\t\tdprint(\"Server is not using csqc\\n\");\r\n\t\t}\r\n\t}\r\n};\r\n\r\nvoid() StartFrame =\r\n{\r\n\tteamplay = cvar(\"teamplay\");\r\n\tskill = cvar(\"skill\");\r\n\tframecount = framecount + 1;\r\n};\r\n\r\n\r\n\r\nfloat(entity ent, float fl) BodyqueCSQC =\r\n{\r\n\treturn FALSE;\r\n};\r\n\r\nentity  bodyque_head;\r\n\r\nvoid() InitBodyQue =\r\n{\r\n\tbodyque_head = spawn();\r\n\tbodyque_head.classname = \"bodyque\";\r\n\tbodyque_head.owner = spawn();\r\n\tbodyque_head.owner.classname = \"bodyque\";\r\n\tbodyque_head.owner.owner = spawn();\r\n\tbodyque_head.owner.owner.classname = \"bodyque\";\r\n\tbodyque_head.owner.owner.owner = spawn();\r\n\tbodyque_head.owner.owner.owner.classname = \"bodyque\";\r\n\tbodyque_head.owner.owner.owner.owner = bodyque_head;\r\n};\r\n\r\n\r\n// make a body que entry for the given ent so the ent can be\r\n// respawned elsewhere\r\nvoid(entity ent) CopyToBodyQue =\r\n{\r\n\tbodyque_head.angles = ent.angles;\r\n\tbodyque_head.model = ent.model;\r\n\tbodyque_head.modelindex = ent.modelindex;\r\n\tbodyque_head.frame = ent.frame;\r\n\tbodyque_head.colormap = ent.colormap;\r\n\tbodyque_head.movetype = ent.movetype;\r\n\tbodyque_head.velocity = ent.velocity;\r\n\tbodyque_head.flags = 0;\r\n\tbodyque_head.SendEntity = BodyqueCSQC;\r\n\tsetorigin (bodyque_head, ent.origin);\r\n\tsetsize (bodyque_head, ent.mins, ent.maxs);\r\n\tbodyque_head = bodyque_head.owner;\r\n};\r\n\r\n\r\n"
  },
  {
    "path": "quakec/csqctest/src/ss/zombie.qc",
    "content": "/*\r\n==============================================================================\r\n\r\nZOMBIE\r\n\r\n==============================================================================\r\n*/\r\n$cd id1/models/zombie\r\n\r\n$origin\t0 0 24\r\n\r\n$base base\r\n$skin skin\r\n\r\n$frame stand1 stand2 stand3 stand4 stand5 stand6 stand7 stand8\r\n$frame stand9 stand10 stand11 stand12 stand13 stand14 stand15\r\n\r\n$frame walk1 walk2 walk3 walk4 walk5 walk6 walk7 walk8 walk9 walk10 walk11\r\n$frame walk12 walk13 walk14 walk15 walk16 walk17 walk18 walk19\r\n\r\n$frame run1 run2 run3 run4 run5 run6 run7 run8 run9 run10 run11 run12\r\n$frame run13 run14 run15 run16 run17 run18\r\n\r\n$frame atta1 atta2 atta3 atta4 atta5 atta6 atta7 atta8 atta9 atta10 atta11\r\n$frame atta12 atta13\r\n\r\n$frame attb1 attb2 attb3 attb4 attb5 attb6 attb7 attb8 attb9 attb10 attb11\r\n$frame attb12 attb13 attb14\r\n\r\n$frame attc1 attc2 attc3 attc4 attc5 attc6 attc7 attc8 attc9 attc10 attc11\r\n$frame attc12\r\n\r\n$frame paina1 paina2 paina3 paina4 paina5 paina6 paina7 paina8 paina9 paina10\r\n$frame paina11 paina12\r\n\r\n$frame painb1 painb2 painb3 painb4 painb5 painb6 painb7 painb8 painb9 painb10\r\n$frame painb11 painb12 painb13 painb14 painb15 painb16 painb17 painb18 painb19\r\n$frame painb20 painb21 painb22 painb23 painb24 painb25 painb26 painb27 painb28\r\n\r\n$frame painc1 painc2 painc3 painc4 painc5 painc6 painc7 painc8 painc9 painc10\r\n$frame painc11 painc12 painc13 painc14 painc15 painc16 painc17 painc18\r\n\r\n$frame paind1 paind2 paind3 paind4 paind5 paind6 paind7 paind8 paind9 paind10\r\n$frame paind11 paind12 paind13\r\n\r\n$frame paine1 paine2 paine3 paine4 paine5 paine6 paine7 paine8 paine9 paine10\r\n$frame paine11 paine12 paine13 paine14 paine15 paine16 paine17 paine18 paine19\r\n$frame paine20 paine21 paine22 paine23 paine24 paine25 paine26 paine27 paine28\r\n$frame paine29 paine30\r\n\r\n$frame cruc_1 cruc_2 cruc_3 cruc_4 cruc_5 cruc_6\r\n\r\nfloat\tSPAWN_CRUCIFIED\t= 1;\r\n\r\n//=============================================================================\r\n\r\n.float inpain;\r\n\r\nvoid() zombie_stand1\t=[\t$stand1,\t\tzombie_stand2\t] {ai_stand();};\r\nvoid() zombie_stand2\t=[\t$stand2,\t\tzombie_stand3\t] {ai_stand();};\r\nvoid() zombie_stand3\t=[\t$stand3,\t\tzombie_stand4\t] {ai_stand();};\r\nvoid() zombie_stand4\t=[\t$stand4,\t\tzombie_stand5\t] {ai_stand();};\r\nvoid() zombie_stand5\t=[\t$stand5,\t\tzombie_stand6\t] {ai_stand();};\r\nvoid() zombie_stand6\t=[\t$stand6,\t\tzombie_stand7\t] {ai_stand();};\r\nvoid() zombie_stand7\t=[\t$stand7,\t\tzombie_stand8\t] {ai_stand();};\r\nvoid() zombie_stand8\t=[\t$stand8,\t\tzombie_stand9\t] {ai_stand();};\r\nvoid() zombie_stand9\t=[\t$stand9,\t\tzombie_stand10\t] {ai_stand();};\r\nvoid() zombie_stand10\t=[\t$stand10,\t\tzombie_stand11\t] {ai_stand();};\r\nvoid() zombie_stand11\t=[\t$stand11,\t\tzombie_stand12\t] {ai_stand();};\r\nvoid() zombie_stand12\t=[\t$stand12,\t\tzombie_stand13\t] {ai_stand();};\r\nvoid() zombie_stand13\t=[\t$stand13,\t\tzombie_stand14\t] {ai_stand();};\r\nvoid() zombie_stand14\t=[\t$stand14,\t\tzombie_stand15\t] {ai_stand();};\r\nvoid() zombie_stand15\t=[\t$stand15,\t\tzombie_stand1\t] {ai_stand();};\r\n\r\nvoid() zombie_cruc1\t=\t[\t$cruc_1,\t\tzombie_cruc2\t] {\r\nif (random() < 0.1)\r\n\tsound (self, CHAN_VOICE, \"zombie/idle_w2.wav\", 1, ATTN_STATIC);};\r\nvoid() zombie_cruc2\t=\t[\t$cruc_2,\t\tzombie_cruc3\t] {self.nextthink = time + 0.1 + random()*0.1;};\r\nvoid() zombie_cruc3\t=\t[\t$cruc_3,\t\tzombie_cruc4\t] {self.nextthink = time + 0.1 + random()*0.1;};\r\nvoid() zombie_cruc4\t=\t[\t$cruc_4,\t\tzombie_cruc5\t] {self.nextthink = time + 0.1 + random()*0.1;};\r\nvoid() zombie_cruc5\t=\t[\t$cruc_5,\t\tzombie_cruc6\t] {self.nextthink = time + 0.1 + random()*0.1;};\r\nvoid() zombie_cruc6\t=\t[\t$cruc_6,\t\tzombie_cruc1\t] {self.nextthink = time + 0.1 + random()*0.1;};\r\n\r\nvoid() zombie_walk1\t\t=[\t$walk1,\t\tzombie_walk2\t] {ai_walk(0);};\r\nvoid() zombie_walk2\t\t=[\t$walk2,\t\tzombie_walk3\t] {ai_walk(2);};\r\nvoid() zombie_walk3\t\t=[\t$walk3,\t\tzombie_walk4\t] {ai_walk(3);};\r\nvoid() zombie_walk4\t\t=[\t$walk4,\t\tzombie_walk5\t] {ai_walk(2);};\r\nvoid() zombie_walk5\t\t=[\t$walk5,\t\tzombie_walk6\t] {ai_walk(1);};\r\nvoid() zombie_walk6\t\t=[\t$walk6,\t\tzombie_walk7\t] {ai_walk(0);};\r\nvoid() zombie_walk7\t\t=[\t$walk7,\t\tzombie_walk8\t] {ai_walk(0);};\r\nvoid() zombie_walk8\t\t=[\t$walk8,\t\tzombie_walk9\t] {ai_walk(0);};\r\nvoid() zombie_walk9\t\t=[\t$walk9,\t\tzombie_walk10\t] {ai_walk(0);};\r\nvoid() zombie_walk10\t=[\t$walk10,\tzombie_walk11\t] {ai_walk(0);};\r\nvoid() zombie_walk11\t=[\t$walk11,\tzombie_walk12\t] {ai_walk(2);};\r\nvoid() zombie_walk12\t=[\t$walk12,\tzombie_walk13\t] {ai_walk(2);};\r\nvoid() zombie_walk13\t=[\t$walk13,\tzombie_walk14\t] {ai_walk(1);};\r\nvoid() zombie_walk14\t=[\t$walk14,\tzombie_walk15\t] {ai_walk(0);};\r\nvoid() zombie_walk15\t=[\t$walk15,\tzombie_walk16\t] {ai_walk(0);};\r\nvoid() zombie_walk16\t=[\t$walk16,\tzombie_walk17\t] {ai_walk(0);};\r\nvoid() zombie_walk17\t=[\t$walk17,\tzombie_walk18\t] {ai_walk(0);};\r\nvoid() zombie_walk18\t=[\t$walk18,\tzombie_walk19\t] {ai_walk(0);};\r\nvoid() zombie_walk19\t=[\t$walk19,\tzombie_walk1\t] {\r\nai_walk(0);\r\nif (random() < 0.2)\r\n\tsound (self, CHAN_VOICE, \"zombie/z_idle.wav\", 1, ATTN_IDLE);};\r\n\r\nvoid() zombie_run1\t\t=[\t$run1,\t\tzombie_run2\t] {ai_run(1);self.inpain = 0;};\r\nvoid() zombie_run2\t\t=[\t$run2,\t\tzombie_run3\t] {ai_run(1);};\r\nvoid() zombie_run3\t\t=[\t$run3,\t\tzombie_run4\t] {ai_run(0);};\r\nvoid() zombie_run4\t\t=[\t$run4,\t\tzombie_run5\t] {ai_run(1);};\r\nvoid() zombie_run5\t\t=[\t$run5,\t\tzombie_run6\t] {ai_run(2);};\r\nvoid() zombie_run6\t\t=[\t$run6,\t\tzombie_run7\t] {ai_run(3);};\r\nvoid() zombie_run7\t\t=[\t$run7,\t\tzombie_run8\t] {ai_run(4);};\r\nvoid() zombie_run8\t\t=[\t$run8,\t\tzombie_run9\t] {ai_run(4);};\r\nvoid() zombie_run9\t\t=[\t$run9,\t\tzombie_run10\t] {ai_run(2);};\r\nvoid() zombie_run10\t\t=[\t$run10,\t\tzombie_run11\t] {ai_run(0);};\r\nvoid() zombie_run11\t\t=[\t$run11,\t\tzombie_run12\t] {ai_run(0);};\r\nvoid() zombie_run12\t\t=[\t$run12,\t\tzombie_run13\t] {ai_run(0);};\r\nvoid() zombie_run13\t\t=[\t$run13,\t\tzombie_run14\t] {ai_run(2);};\r\nvoid() zombie_run14\t\t=[\t$run14,\t\tzombie_run15\t] {ai_run(4);};\r\nvoid() zombie_run15\t\t=[\t$run15,\t\tzombie_run16\t] {ai_run(6);};\r\nvoid() zombie_run16\t\t=[\t$run16,\t\tzombie_run17\t] {ai_run(7);};\r\nvoid() zombie_run17\t\t=[\t$run17,\t\tzombie_run18\t] {ai_run(3);};\r\nvoid() zombie_run18\t\t=[\t$run18,\t\tzombie_run1\t] {\r\nai_run(8);\r\nif (random() < 0.2)\r\n\tsound (self, CHAN_VOICE, \"zombie/z_idle.wav\", 1, ATTN_IDLE);\r\nif (random() > 0.8)\r\n\tsound (self, CHAN_VOICE, \"zombie/z_idle1.wav\", 1, ATTN_IDLE);\r\n};\r\n\r\n/*\r\n=============================================================================\r\n\r\nATTACKS\r\n\r\n=============================================================================\r\n*/\r\n\r\nvoid() ZombieGrenadeTouch =\r\n{\r\n\tif (other == self.owner)\r\n\t\treturn;\t\t// don't explode on owner\r\n\tif (other.takedamage)\r\n\t{\r\n\t\tT_Damage (other, self, self.owner, 10 );\r\n\t\tsound (self, CHAN_WEAPON, \"zombie/z_hit.wav\", 1, ATTN_NORM);\r\n\t\tremove (self);\r\n\t\treturn;\r\n\t}\r\n\tsound (self, CHAN_WEAPON, \"zombie/z_miss.wav\", 1, ATTN_NORM);\t// bounce sound\r\n\tself.velocity = '0 0 0';\r\n\tself.avelocity = '0 0 0';\r\n\tself.touch = SUB_Remove;\r\n};\r\n\r\n/*\r\n================\r\nZombieFireGrenade\r\n================\r\n*/\r\nvoid(vector st) ZombieFireGrenade =\r\n{\r\n\tlocal\tentity missile;\r\n\tlocal\tvector\torg;\r\n\r\n\tsound (self, CHAN_WEAPON, \"zombie/z_shot1.wav\", 1, ATTN_NORM);\r\n\r\n\tmissile = spawn ();\r\n\tmissile.owner = self;\r\n\tmissile.movetype = MOVETYPE_BOUNCE;\r\n\tmissile.solid = SOLID_BBOX;\r\n\r\n// calc org\r\n\torg = self.origin + st_x * v_forward + st_y * v_right + (st_z - 24) * v_up;\r\n\t\r\n// set missile speed\t\r\n\r\n\tmakevectors (self.angles);\r\n\r\n\tmissile.velocity = normalize(self.enemy.origin - org);\r\n\tmissile.velocity = missile.velocity * 600;\r\n\tmissile.velocity_z = 200;\r\n\r\n\tmissile.avelocity = '3000 1000 2000';\r\n\r\n\tmissile.touch = ZombieGrenadeTouch;\r\n\t\r\n// set missile duration\r\n\tmissile.nextthink = time + 2.5;\r\n\tmissile.think = SUB_Remove;\r\n\r\n\tsetmodel (missile, \"progs/zom_gib.mdl\");\r\n\tsetsize (missile, '0 0 0', '0 0 0');\t\t\r\n\tsetorigin (missile, org);\r\n};\r\n\r\n\r\nvoid() zombie_atta1\t\t=[\t$atta1,\t\tzombie_atta2\t] {ai_face();};\r\nvoid() zombie_atta2\t\t=[\t$atta2,\t\tzombie_atta3\t] {ai_face();};\r\nvoid() zombie_atta3\t\t=[\t$atta3,\t\tzombie_atta4\t] {ai_face();};\r\nvoid() zombie_atta4\t\t=[\t$atta4,\t\tzombie_atta5\t] {ai_face();};\r\nvoid() zombie_atta5\t\t=[\t$atta5,\t\tzombie_atta6\t] {ai_face();};\r\nvoid() zombie_atta6\t\t=[\t$atta6,\t\tzombie_atta7\t] {ai_face();};\r\nvoid() zombie_atta7\t\t=[\t$atta7,\t\tzombie_atta8\t] {ai_face();};\r\nvoid() zombie_atta8\t\t=[\t$atta8,\t\tzombie_atta9\t] {ai_face();};\r\nvoid() zombie_atta9\t\t=[\t$atta9,\t\tzombie_atta10\t] {ai_face();};\r\nvoid() zombie_atta10\t=[\t$atta10,\tzombie_atta11\t] {ai_face();};\r\nvoid() zombie_atta11\t=[\t$atta11,\tzombie_atta12\t] {ai_face();};\r\nvoid() zombie_atta12\t=[\t$atta12,\tzombie_atta13\t] {ai_face();};\r\nvoid() zombie_atta13\t=[\t$atta13,\tzombie_run1\t] {ai_face();ZombieFireGrenade('-10 -22 30');};\r\n\r\nvoid() zombie_attb1\t\t=[\t$attb1,\t\tzombie_attb2\t] {ai_face();};\r\nvoid() zombie_attb2\t\t=[\t$attb2,\t\tzombie_attb3\t] {ai_face();};\r\nvoid() zombie_attb3\t\t=[\t$attb3,\t\tzombie_attb4\t] {ai_face();};\r\nvoid() zombie_attb4\t\t=[\t$attb4,\t\tzombie_attb5\t] {ai_face();};\r\nvoid() zombie_attb5\t\t=[\t$attb5,\t\tzombie_attb6\t] {ai_face();};\r\nvoid() zombie_attb6\t\t=[\t$attb6,\t\tzombie_attb7\t] {ai_face();};\r\nvoid() zombie_attb7\t\t=[\t$attb7,\t\tzombie_attb8\t] {ai_face();};\r\nvoid() zombie_attb8\t\t=[\t$attb8,\t\tzombie_attb9\t] {ai_face();};\r\nvoid() zombie_attb9\t\t=[\t$attb9,\t\tzombie_attb10\t] {ai_face();};\r\nvoid() zombie_attb10\t=[\t$attb10,\tzombie_attb11\t] {ai_face();};\r\nvoid() zombie_attb11\t=[\t$attb11,\tzombie_attb12\t] {ai_face();};\r\nvoid() zombie_attb12\t=[\t$attb12,\tzombie_attb13\t] {ai_face();};\r\nvoid() zombie_attb13\t=[\t$attb13,\tzombie_attb14\t] {ai_face();};\r\nvoid() zombie_attb14\t=[\t$attb13,\tzombie_run1\t] {ai_face();ZombieFireGrenade('-10 -24 29');};\r\n\r\nvoid() zombie_attc1\t\t=[\t$attc1,\t\tzombie_attc2\t] {ai_face();};\r\nvoid() zombie_attc2\t\t=[\t$attc2,\t\tzombie_attc3\t] {ai_face();};\r\nvoid() zombie_attc3\t\t=[\t$attc3,\t\tzombie_attc4\t] {ai_face();};\r\nvoid() zombie_attc4\t\t=[\t$attc4,\t\tzombie_attc5\t] {ai_face();};\r\nvoid() zombie_attc5\t\t=[\t$attc5,\t\tzombie_attc6\t] {ai_face();};\r\nvoid() zombie_attc6\t\t=[\t$attc6,\t\tzombie_attc7\t] {ai_face();};\r\nvoid() zombie_attc7\t\t=[\t$attc7,\t\tzombie_attc8\t] {ai_face();};\r\nvoid() zombie_attc8\t\t=[\t$attc8,\t\tzombie_attc9\t] {ai_face();};\r\nvoid() zombie_attc9\t\t=[\t$attc9,\t\tzombie_attc10\t] {ai_face();};\r\nvoid() zombie_attc10\t=[\t$attc10,\tzombie_attc11\t] {ai_face();};\r\nvoid() zombie_attc11\t=[\t$attc11,\tzombie_attc12\t] {ai_face();};\r\nvoid() zombie_attc12\t=[\t$attc12,\tzombie_run1\t\t] {ai_face();ZombieFireGrenade('-12 -19 29');};\r\n\r\nvoid() zombie_missile =\r\n{\r\n\tlocal float\tr;\r\n\t\r\n\tr = random();\r\n\t\r\n\tif (r < 0.3)\r\n\t\tzombie_atta1 ();\r\n\telse if (r < 0.6)\r\n\t\tzombie_attb1 ();\r\n\telse\r\n\t\tzombie_attc1 ();\r\n};\r\n\r\n\r\n/*\r\n=============================================================================\r\n\r\nPAIN\r\n\r\n=============================================================================\r\n*/\r\n\r\nvoid() zombie_paina1\t=[\t$paina1,\tzombie_paina2\t] {sound (self, CHAN_VOICE, \"zombie/z_pain.wav\", 1, ATTN_NORM);};\r\nvoid() zombie_paina2\t=[\t$paina2,\tzombie_paina3\t] {ai_painforward(3);};\r\nvoid() zombie_paina3\t=[\t$paina3,\tzombie_paina4\t] {ai_painforward(1);};\r\nvoid() zombie_paina4\t=[\t$paina4,\tzombie_paina5\t] {ai_pain(1);};\r\nvoid() zombie_paina5\t=[\t$paina5,\tzombie_paina6\t] {ai_pain(3);};\r\nvoid() zombie_paina6\t=[\t$paina6,\tzombie_paina7\t] {ai_pain(1);};\r\nvoid() zombie_paina7\t=[\t$paina7,\tzombie_paina8\t] {};\r\nvoid() zombie_paina8\t=[\t$paina8,\tzombie_paina9\t] {};\r\nvoid() zombie_paina9\t=[\t$paina9,\tzombie_paina10\t] {};\r\nvoid() zombie_paina10\t=[\t$paina10,\tzombie_paina11\t] {};\r\nvoid() zombie_paina11\t=[\t$paina11,\tzombie_paina12\t] {};\r\nvoid() zombie_paina12\t=[\t$paina12,\tzombie_run1\t\t] {};\r\n\r\nvoid() zombie_painb1\t=[\t$painb1,\tzombie_painb2\t] {sound (self, CHAN_VOICE, \"zombie/z_pain1.wav\", 1, ATTN_NORM);};\r\nvoid() zombie_painb2\t=[\t$painb2,\tzombie_painb3\t] {ai_pain(2);};\r\nvoid() zombie_painb3\t=[\t$painb3,\tzombie_painb4\t] {ai_pain(8);};\r\nvoid() zombie_painb4\t=[\t$painb4,\tzombie_painb5\t] {ai_pain(6);};\r\nvoid() zombie_painb5\t=[\t$painb5,\tzombie_painb6\t] {ai_pain(2);};\r\nvoid() zombie_painb6\t=[\t$painb6,\tzombie_painb7\t] {};\r\nvoid() zombie_painb7\t=[\t$painb7,\tzombie_painb8\t] {};\r\nvoid() zombie_painb8\t=[\t$painb8,\tzombie_painb9\t] {};\r\nvoid() zombie_painb9\t=[\t$painb9,\tzombie_painb10\t] {sound (self, CHAN_BODY, \"zombie/z_fall.wav\", 1, ATTN_NORM);};\r\nvoid() zombie_painb10\t=[\t$painb10,\tzombie_painb11\t] {};\r\nvoid() zombie_painb11\t=[\t$painb11,\tzombie_painb12\t] {};\r\nvoid() zombie_painb12\t=[\t$painb12,\tzombie_painb13\t] {};\r\nvoid() zombie_painb13\t=[\t$painb13,\tzombie_painb14\t] {};\r\nvoid() zombie_painb14\t=[\t$painb14,\tzombie_painb15\t] {};\r\nvoid() zombie_painb15\t=[\t$painb15,\tzombie_painb16\t] {};\r\nvoid() zombie_painb16\t=[\t$painb16,\tzombie_painb17\t] {};\r\nvoid() zombie_painb17\t=[\t$painb17,\tzombie_painb18\t] {};\r\nvoid() zombie_painb18\t=[\t$painb18,\tzombie_painb19\t] {};\r\nvoid() zombie_painb19\t=[\t$painb19,\tzombie_painb20\t] {};\r\nvoid() zombie_painb20\t=[\t$painb20,\tzombie_painb21\t] {};\r\nvoid() zombie_painb21\t=[\t$painb21,\tzombie_painb22\t] {};\r\nvoid() zombie_painb22\t=[\t$painb22,\tzombie_painb23\t] {};\r\nvoid() zombie_painb23\t=[\t$painb23,\tzombie_painb24\t] {};\r\nvoid() zombie_painb24\t=[\t$painb24,\tzombie_painb25\t] {};\r\nvoid() zombie_painb25\t=[\t$painb25,\tzombie_painb26\t] {ai_painforward(1);};\r\nvoid() zombie_painb26\t=[\t$painb26,\tzombie_painb27\t] {};\r\nvoid() zombie_painb27\t=[\t$painb27,\tzombie_painb28\t] {};\r\nvoid() zombie_painb28\t=[\t$painb28,\tzombie_run1\t\t] {};\r\n\r\nvoid() zombie_painc1\t=[\t$painc1,\tzombie_painc2\t] {sound (self, CHAN_VOICE, \"zombie/z_pain1.wav\", 1, ATTN_NORM);};\r\nvoid() zombie_painc2\t=[\t$painc2,\tzombie_painc3\t] {};\r\nvoid() zombie_painc3\t=[\t$painc3,\tzombie_painc4\t] {ai_pain(3);};\r\nvoid() zombie_painc4\t=[\t$painc4,\tzombie_painc5\t] {ai_pain(1);};\r\nvoid() zombie_painc5\t=[\t$painc5,\tzombie_painc6\t] {};\r\nvoid() zombie_painc6\t=[\t$painc6,\tzombie_painc7\t] {};\r\nvoid() zombie_painc7\t=[\t$painc7,\tzombie_painc8\t] {};\r\nvoid() zombie_painc8\t=[\t$painc8,\tzombie_painc9\t] {};\r\nvoid() zombie_painc9\t=[\t$painc9,\tzombie_painc10\t] {};\r\nvoid() zombie_painc10\t=[\t$painc10,\tzombie_painc11\t] {};\r\nvoid() zombie_painc11\t=[\t$painc11,\tzombie_painc12\t] {ai_painforward(1);};\r\nvoid() zombie_painc12\t=[\t$painc12,\tzombie_painc13\t] {ai_painforward(1);};\r\nvoid() zombie_painc13\t=[\t$painc13,\tzombie_painc14\t] {};\r\nvoid() zombie_painc14\t=[\t$painc14,\tzombie_painc15\t] {};\r\nvoid() zombie_painc15\t=[\t$painc15,\tzombie_painc16\t] {};\r\nvoid() zombie_painc16\t=[\t$painc16,\tzombie_painc17\t] {};\r\nvoid() zombie_painc17\t=[\t$painc17,\tzombie_painc18\t] {};\r\nvoid() zombie_painc18\t=[\t$painc18,\tzombie_run1\t] {};\r\n\r\nvoid() zombie_paind1\t=[\t$paind1,\tzombie_paind2\t] {sound (self, CHAN_VOICE, \"zombie/z_pain.wav\", 1, ATTN_NORM);};\r\nvoid() zombie_paind2\t=[\t$paind2,\tzombie_paind3\t] {};\r\nvoid() zombie_paind3\t=[\t$paind3,\tzombie_paind4\t] {};\r\nvoid() zombie_paind4\t=[\t$paind4,\tzombie_paind5\t] {};\r\nvoid() zombie_paind5\t=[\t$paind5,\tzombie_paind6\t] {};\r\nvoid() zombie_paind6\t=[\t$paind6,\tzombie_paind7\t] {};\r\nvoid() zombie_paind7\t=[\t$paind7,\tzombie_paind8\t] {};\r\nvoid() zombie_paind8\t=[\t$paind8,\tzombie_paind9\t] {};\r\nvoid() zombie_paind9\t=[\t$paind9,\tzombie_paind10\t] {ai_pain(1);};\r\nvoid() zombie_paind10\t=[\t$paind10,\tzombie_paind11\t] {};\r\nvoid() zombie_paind11\t=[\t$paind11,\tzombie_paind12\t] {};\r\nvoid() zombie_paind12\t=[\t$paind12,\tzombie_paind13\t] {};\r\nvoid() zombie_paind13\t=[\t$paind13,\tzombie_run1\t] {};\r\n\r\nvoid() zombie_paine1\t=[\t$paine1,\tzombie_paine2\t] {\r\nsound (self, CHAN_VOICE, \"zombie/z_pain.wav\", 1, ATTN_NORM);\r\nself.health = 60;\r\n};\r\nvoid() zombie_paine2\t=[\t$paine2,\tzombie_paine3\t] {ai_pain(8);};\r\nvoid() zombie_paine3\t=[\t$paine3,\tzombie_paine4\t] {ai_pain(5);};\r\nvoid() zombie_paine4\t=[\t$paine4,\tzombie_paine5\t] {ai_pain(3);};\r\nvoid() zombie_paine5\t=[\t$paine5,\tzombie_paine6\t] {ai_pain(1);};\r\nvoid() zombie_paine6\t=[\t$paine6,\tzombie_paine7\t] {ai_pain(2);};\r\nvoid() zombie_paine7\t=[\t$paine7,\tzombie_paine8\t] {ai_pain(1);};\r\nvoid() zombie_paine8\t=[\t$paine8,\tzombie_paine9\t] {ai_pain(1);};\r\nvoid() zombie_paine9\t=[\t$paine9,\tzombie_paine10\t] {ai_pain(2);};\r\nvoid() zombie_paine10\t=[\t$paine10,\tzombie_paine11\t] {\r\nsound (self, CHAN_BODY, \"zombie/z_fall.wav\", 1, ATTN_NORM);\r\nself.solid = SOLID_NOT;\r\n};\r\nvoid() zombie_paine11\t=[\t$paine11,\tzombie_paine12\t] {self.nextthink = self.nextthink + 5;self.health = 60;};\r\nvoid() zombie_paine12\t=[\t$paine12,\tzombie_paine13\t]{\r\n// see if ok to stand up\r\nself.health = 60;\r\nsound (self, CHAN_VOICE, \"zombie/z_idle.wav\", 1, ATTN_IDLE);\r\nself.solid = SOLID_SLIDEBOX;\r\nif (!walkmove (0, 0))\r\n{\r\n\tself.think = zombie_paine11;\r\n\tself.solid = SOLID_NOT;\r\n\treturn;\r\n}\r\n};\r\nvoid() zombie_paine13\t=[\t$paine13,\tzombie_paine14\t] {};\r\nvoid() zombie_paine14\t=[\t$paine14,\tzombie_paine15\t] {};\r\nvoid() zombie_paine15\t=[\t$paine15,\tzombie_paine16\t] {};\r\nvoid() zombie_paine16\t=[\t$paine16,\tzombie_paine17\t] {};\r\nvoid() zombie_paine17\t=[\t$paine17,\tzombie_paine18\t] {};\r\nvoid() zombie_paine18\t=[\t$paine18,\tzombie_paine19\t] {};\r\nvoid() zombie_paine19\t=[\t$paine19,\tzombie_paine20\t] {};\r\nvoid() zombie_paine20\t=[\t$paine20,\tzombie_paine21\t] {};\r\nvoid() zombie_paine21\t=[\t$paine21,\tzombie_paine22\t] {};\r\nvoid() zombie_paine22\t=[\t$paine22,\tzombie_paine23\t] {};\r\nvoid() zombie_paine23\t=[\t$paine23,\tzombie_paine24\t] {};\r\nvoid() zombie_paine24\t=[\t$paine24,\tzombie_paine25\t] {};\r\nvoid() zombie_paine25\t=[\t$paine25,\tzombie_paine26\t] {ai_painforward(5);};\r\nvoid() zombie_paine26\t=[\t$paine26,\tzombie_paine27\t] {ai_painforward(3);};\r\nvoid() zombie_paine27\t=[\t$paine27,\tzombie_paine28\t] {ai_painforward(1);};\r\nvoid() zombie_paine28\t=[\t$paine28,\tzombie_paine29\t] {ai_pain(1);};\r\nvoid() zombie_paine29\t=[\t$paine29,\tzombie_paine30\t] {};\r\nvoid() zombie_paine30\t=[\t$paine30,\tzombie_run1\t\t] {};\r\n\r\nvoid() zombie_die =\r\n{\r\n\tThrowCSQCGibs(GIB_ZOMBIE);\r\n};\r\n\r\n/*\r\n=================\r\nzombie_pain\r\n\r\nZombies can only be killed (gibbed) by doing 60 hit points of damage\r\nin a single frame (rockets, grenades, quad shotgun, quad nailgun).\r\n\r\nA hit of 25 points or more (super shotgun, quad nailgun) will allways put it\r\ndown to the ground.\r\n\r\nA hit of from 10 to 40 points in one frame will cause it to go down if it\r\nhas been twice in two seconds, otherwise it goes into one of the four\r\nfast pain frames.\r\n\r\nA hit of less than 10 points of damage (winged by a shotgun) will be ignored.\r\n\r\nFIXME: don't use pain_finished because of nightmare hack\r\n=================\r\n*/\r\nvoid(entity attacker, float take) zombie_pain =\r\n{\r\n\tlocal float r;\r\n\r\n\tself.health = 60;\t\t// allways reset health\r\n\r\n\tif (take < 9)\r\n\t\treturn;\t\t\t\t// totally ignore\r\n\r\n\tif (self.inpain == 2)\r\n\t\treturn;\t\t\t// down on ground, so don't reset any counters\r\n\r\n// go down immediately if a big enough hit\r\n\tif (take >= 25)\r\n\t{\r\n\t\tself.inpain = 2;\r\n\t\tzombie_paine1 ();\r\n\t\treturn;\r\n\t}\r\n\t\r\n\tif (self.inpain)\r\n\t{\r\n// if hit again in next gre seconds while not in pain frames, definately drop\r\n\t\tself.pain_finished = time + 3;\r\n\t\treturn;\t\t\t// currently going through an animation, don't change\r\n\t}\r\n\t\r\n\tif (self.pain_finished > time)\r\n\t{\r\n// hit again, so drop down\r\n\t\tself.inpain = 2;\r\n\t\tzombie_paine1 ();\r\n\t\treturn;\r\n\t}\r\n\r\n// gp into one of the fast pain animations\t\r\n\tself.inpain = 1;\r\n\r\n\tr = random();\r\n\tif (r < 0.25)\r\n\t\tzombie_paina1 ();\r\n\telse if (r <  0.5)\r\n\t\tzombie_painb1 ();\r\n\telse if (r <  0.75)\r\n\t\tzombie_painc1 ();\r\n\telse\r\n\t\tzombie_paind1 ();\r\n};\r\n\r\n//============================================================================\r\n\r\n/*QUAKED monster_zombie (1 0 0) (-16 -16 -24) (16 16 32) Crucified ambush\r\n\r\nIf crucified, stick the bounding box 12 pixels back into a wall to look right.\r\n*/\r\nvoid() monster_zombie =\r\n{\r\n\tif (deathmatch)\r\n\t{\r\n\t\tremove(self);\r\n\t\treturn;\r\n\t}\r\n\r\n\tprecache_model (\"progs/zombie.mdl\");\r\n\tprecache_model (\"progs/h_zombie.mdl\");\r\n\tprecache_model (\"progs/zom_gib.mdl\");\r\n\r\n\tprecache_sound (\"zombie/z_idle.wav\");\r\n\tprecache_sound (\"zombie/z_idle1.wav\");\r\n\tprecache_sound (\"zombie/z_shot1.wav\");\r\n\tprecache_sound (\"zombie/z_gib.wav\");\r\n\tprecache_sound (\"zombie/z_pain.wav\");\r\n\tprecache_sound (\"zombie/z_pain1.wav\");\r\n\tprecache_sound (\"zombie/z_fall.wav\");\r\n\tprecache_sound (\"zombie/z_miss.wav\");\r\n\tprecache_sound (\"zombie/z_hit.wav\");\r\n\tprecache_sound (\"zombie/idle_w2.wav\");\r\n\r\n\tself.solid = SOLID_SLIDEBOX;\r\n\tself.movetype = MOVETYPE_STEP;\r\n\r\n\tsetmodel (self, \"progs/zombie.mdl\");\r\n\r\n\tsetsize (self, '-16 -16 -24', '16 16 40');\r\n\tself.health = 60;\r\n\r\n\tself.th_stand = zombie_stand1;\r\n\tself.th_walk = zombie_walk1;\r\n\tself.th_run = zombie_run1;\r\n\tself.th_pain = zombie_pain;\r\n\tself.th_die = zombie_die;\r\n\tself.th_missile = zombie_missile;\r\n\r\n\tif (self.spawnflags & SPAWN_CRUCIFIED)\r\n\t{\r\n\t\tself.movetype = MOVETYPE_NONE;\r\n\t\tzombie_cruc1 ();\r\n\t}\r\n\telse\r\n\t\twalkmonster_start();\r\n};\r\n"
  },
  {
    "path": "quakec/csqctest/src/ssqc.src",
    "content": "../progs.dat\r\n\r\n//#define QWSSQC\r\n#define SSQC\r\noptsall.qc\r\n#if defined(QSS) || defined(DP)\r\nssqc_api.qc\r\n#else\r\nfteextensions.qc\r\n#endif\r\n\r\nss/defs.qc\r\ncommon/econstants.qc\r\ncommon/classes.qc\r\n\r\ncommon/pmove.qc\r\n\r\nss/subs.qc\r\nss/fight.qc\r\nss/ai.qc\r\nss/combat.qc\r\nss/items.qc\r\nss/weapons.qc\r\nss/world.qc\r\n\r\n//common/phys.qc\r\n//common/playerphysics.qc\r\n//common/sv_user.qc\r\n\r\nss/client.qc\r\nss/player.qc\r\nss/monsters.qc\r\nss/doors.qc\r\nss/buttons.qc\r\nss/triggers.qc\r\nss/plats.qc\r\nss/misc.qc\r\n\r\nss/ogre.qc\r\nss/demon.qc\r\nss/shambler.qc\r\nss/knight.qc\r\nss/soldier.qc\r\nss/wizard.qc\r\nss/dog.qc\r\nss/zombie.qc\r\nss/boss.qc\r\n\r\nss/tarbaby.qc\t\t// registered\r\nss/hknight.qc\t\t// registered\r\nss/fish.qc\t\t\t// registered\r\nss/shalrath.qc\t\t// registered\r\nss/enforcer.qc\t\t// registered\r\nss/oldone.qc\t\t// registered\r\n"
  },
  {
    "path": "quakec/dpsymbols.src",
    "content": "//NOTE: This file exists purely for generation of the genericdefs/*_api.qc files distributed in ftetools.zip.\n//It is consumed by FTE's build scripts and exists to normalize DP's misnamed defs to match FTE's, and some other fixups/omissions with DP defs.\n//Patches welcome...\n\n#pragma noref 1\n\n#ifdef TEST\n#include \"fteextensions.qc\"\n#endif\n\n//attempt to normalize things.\n#define ReadAngle readangle\n#define ReadByte readbyte\n#define ReadChar readchar\n#define ReadCoord readcoord\n#define ReadFloat readfloat\n#define ReadLong readlong\n#define ReadShort readshort\n#define ReadString readstring\n#define draw_getimagesize drawgetimagesize\n#define addstat clientstat\n\n#define skel_mul_bone skel_premul_bone\n#define skel_mul_bones skel_premul_bones\n\n#define setsensitivityscale setsensitivityscaler\n#define centerprint cprint\n#define cs_project project\n#define cs_unproject unproject\n#define ChangeYaw changeyaw\n\n#define entitybyindex edict_num\n#define adddynamiclight2 dynamiclight_add\n\n#define FIELD_ENTITY \t\tEV_ENTITY\n#define FIELD_FLOAT\t\t\tEV_FLOAT\n#define FIELD_FUNCTION\t\tEV_FUNCTION\n#define FIELD_STRING\t\tEV_STRING\n#define FIELD_VECTOR\t\tEV_VECTOR\n#define STAT_MONSTERS\t\tSTAT_KILLEDMONSTERS\n#define STAT_SECRETS\t\tSTAT_FOUNDSECRETS\n#define STAT_WEAPONMODEL\tSTAT_WEAPONMODELI\n\n#define E_ABSMAX\t\t\tGE_ABSMAX\n#define E_ABSMIN\t\t\tGE_ABSMIN\n#define E_ACTIVE\t\t\tGE_ACTIVE\n#define E_ALPHA\t\t\tGE_ALPHA\n#define E_COLORMOD\t\tGE_COLORMOD\n#define E_FORWARD\t\t\tGE_FORWARD\n#define E_MAXS\t\t\tGE_MAXS\n#define E_MINS\t\t\t\tGE_MINS\n#define E_ORIGIN\t\t\tGE_ORIGIN\n#define E_ORIGINANDVECTORS\tGE_ORIGINANDVECTORS\n#define E_PANTSCOLOR\t\tGE_PANTSCOLOR\n#define E_RIGHT\t\t\tGE_RIGHT\n#define E_SCALE\t\t\tGE_SCALE\n#define E_SHIRTCOLOR\t\tGE_SHIRTCOLOR\n#define E_SKIN\t\t\t\tGE_SKIN\n#define E_UP\t\t\t\tGE_UP\n\n#define VF_FOV_X\t\t\tVF_FOVX\n#define VF_FOV_Y\t\t\tVF_FOVX\n\n\n#define PI\t\t\t\t\tM_PI\n#define MASK_NORMAL\t\tMASK_ENGINE\t\n#define MASK_ENGINEVIEWMODELS\tMASK_VIEWMODEL\n#define false\t\t\t\tFALSE\n#define true\t\t\t\tTRUE\n\n\n\n\n#ifdef SSQC\n\t#include \"dpdefs/progsdefs.qc\"\n\t#include \"dpdefs/dpextensions.qc\"\n\n\t.float SendFlags;\n\t.float gravity;\n\tfloat MSG_ENTITY=5;\n#endif\n#ifdef CSQC\n\t#define drawstring drawrawstring\n\t#include \"dpdefs/csprogsdefs.qc\"\n\t#undef drawstring\n\tvector(vector position, string text, vector scale, vector rgb, float alpha, float flag) drawstring = #326;\n\n\t//not actually defined for some fucked up reason, but work none the less. These are the ones that I use - there'll be others. \n\tconst float IE_KEYDOWN = 0;\n\tconst float IE_KEYUP = 1;\n#endif\n#ifdef MENU\n\t#define cmd localcmd\n\t#include \"dpdefs/menudefs.qc\"\n\tconst float GGDI_GAMEDIR = GETGAMEDIRINFO_NAME;\n\tconst float GGDI_DESCRIPTION = GETGAMEDIRINFO_DESCRIPTION;\n\n\t//not actually defined for some fucked up reason, but work none the less. These are the ones that I use - there'll be others.\n\tfloat(float s) asin = #471;\n\tfloat(float c) acos = #472;\n\tfloat(float t) atan = #473;\n\tfloat(float c, float s) atan2 = #474;\n\tfloat(float a) tan = #475;\n\tstring(string filename) whichpack = #503;\n#endif\n\n\nfloat(__variant) checkbuiltin = #0;\t//not really present in DP, but #0 is technically just an OP_DONE so returns 0 so its actually okay in the end. its important for this to not generate extra warnings.\n\n"
  },
  {
    "path": "quakec/fallout2/ai.qc",
    "content": "void() movetarget_f;\nvoid() t_movetarget;\nvoid() knight_walk1;\nvoid() knight_bow6;\nvoid() knight_bow1;\nvoid(entity etemp, entity stemp, entity stemp, float dmg) T_Damage;\n/*\n\n.enemy\nWill be world if not currently angry at anyone.\n\n.movetarget\nThe next path spot to walk toward.  If .enemy, ignore .movetarget.\nWhen an enemy is killed, the monster will try to return to it's path.\n\n.huntt_ime\nSet to time + something when the player is in sight, but movement straight for\nhim is blocked.  This causes the monster to use wall following code for\nmovement direction instead of sighting on the player.\n\n.ideal_yaw\nA yaw angle of the intended direction, which will be turned towards at up\nto 45 deg / state.  If the enemy is in view and hunt_time is not active,\nthis will be the exact line towards the enemy.\n\n.pausetime\nA monster will leave it's stand state and head towards it's .movetarget when\ntime > .pausetime.\n\nwalkmove(angle, speed) primitive is all or nothing\n*/\n\n\n//\n// globals\n//\n//float\tcurrent_yaw;\n\n//\n// when a monster becomes angry at a player, that monster will be used\n// as the sight target the next frame so that monsters near that one\n// will wake up even if they wouldn't have noticed the player\n//\nentity\tsight_entity;\nfloat\tsight_entity_time;\n\nfloat(float v) anglemod =\n{\n\twhile (v >= 360)\n\t\tv = v - 360;\n\twhile (v < 0)\n\t\tv = v + 360;\n\treturn v;\n};\n\n/*\n==============================================================================\n\nMOVETARGET CODE\n\nThe angle of the movetarget effects standing and bowing direction, but has no effect on movement, which allways heads to the next target.\n\ntargetname\nmust be present.  The name of this movetarget.\n\ntarget\nthe next spot to move to.  If not present, stop here for good.\n\npausetime\nThe number of seconds to spend standing or bowing for path_stand or path_bow\n\n==============================================================================\n*/\n\n\nvoid() movetarget_f =\n{\n\tif (!self.targetname)\n\t\tobjerror (\"monster_movetarget: no targetname\");\n\t\t\n\tself.solid = SOLID_TRIGGER;\n\tself.touch = t_movetarget;\n\tsetsize (self, '-8 -8 -8', '8 8 8');\n\t\n};\n\nvoid() path_corner =\n{\n\tmovetarget_f ();\n};\n\n\n/*\n=============\nt_movetarget\n\nSomething has bumped into a movetarget.  If it is a monster\nmoving towards it, change the next destination and continue.\n==============\n*/\nvoid() t_movetarget =\n{\nlocal entity\ttemp;\n\n\tif (other.movetarget != self)\n\t\treturn;\n\t\n\tif (other.enemy)\n\t\treturn;\t\t// fighting, not following a path\n\n\ttemp = self;\n\tself = other;\n\tother = temp;\n\n\tif (self.classname == \"monster_ogre\")\n\t\tsound (self, CHAN_VOICE, \"ogre/ogdrag.wav\", 1, ATTN_IDLE);// play chainsaw drag sound\n\n//dprint (\"t_movetarget\\n\");\n\tself.goalentity = self.movetarget = find (world, targetname, other.target);\n\tself.ideal_yaw = vectoyaw(self.goalentity.origin - self.origin);\n\tif (!self.movetarget)\n\t{\n\t\tself.pausetime = time + 999999;\n\t\tself.th_stand ();\n\t\treturn;\n\t}\n};\n\n\n\n//============================================================================\n\n/*\n=============\nrange\n\nreturns the range catagorization of an entity reletive to self\n0\tmelee range, will become hostile even if back is turned\n1\tvisibility and infront, or visibility and show hostile\n2\tinfront and show hostile\n3\tonly triggered by damage\n=============\n*/\nfloat(entity targ) range =\n{\nlocal vector\tspot1, spot2;\nlocal float\t\tr;\t\n\tspot1 = self.origin + self.view_ofs;\n\tspot2 = targ.origin + targ.view_ofs;\n\t\n\tr = vlen (spot1 - spot2);\n\tif (r < 120)\n\t\treturn RANGE_MELEE;\n\tif (r < 500)\n\t\treturn RANGE_NEAR;\n\tif (r < 1000)\n\t\treturn RANGE_MID;\n\treturn RANGE_FAR;\n};\n\n/*\n=============\nvisible\n\nreturns 1 if the entity is visible to self, even if not infront ()\n=============\n*/\nfloat (entity targ) visible =\n{\n\tlocal vector\tspot1, spot2;\n\t\n\tspot1 = self.origin + self.view_ofs;\n\tspot2 = targ.origin + targ.view_ofs;\n\ttraceline (spot1, spot2, TRUE, self);\t// see through other monsters\n\t\n\tif (trace_inopen && trace_inwater)\n\t\treturn FALSE;\t\t\t// sight line crossed contents\n\n\tif (trace_fraction == 1)\n\t\treturn TRUE;\n\treturn FALSE;\n};\n\n\n/*\n=============\ninfront\n\nreturns 1 if the entity is in front (in sight) of self\n=============\n*/\nfloat(entity targ) infront =\n{\n\tlocal vector\tvec;\n\tlocal float\t\tdot;\n\t\n\tmakevectors (self.angles);\n\tvec = normalize (targ.origin - self.origin);\n\tdot = vec * v_forward;\n\t\n\tif ( dot > 0.3)\n\t{\n\t\treturn TRUE;\n\t}\n\treturn FALSE;\n};\n\n\n//============================================================================\n\n/*\n===========\nChangeYaw\n\nTurns towards self.ideal_yaw at self.yaw_speed\nSets the global variable current_yaw\nCalled every 0.1 sec by monsters\n============\n*/\n/*\n\nvoid() ChangeYaw =\n{\n\tlocal float\t\tideal, move;\n\n//current_yaw = self.ideal_yaw;\n// mod down the current angle\n\tcurrent_yaw = anglemod( self.angles_y );\n\tideal = self.ideal_yaw;\n\t\n\tif (current_yaw == ideal)\n\t\treturn;\n\t\n\tmove = ideal - current_yaw;\n\tif (ideal > current_yaw)\n\t{\n\t\tif (move > 180)\n\t\t\tmove = move - 360;\n\t}\n\telse\n\t{\n\t\tif (move < -180)\n\t\t\tmove = move + 360;\n\t}\n\t\t\n\tif (move > 0)\n\t{\n\t\tif (move > self.yaw_speed)\n\t\t\tmove = self.yaw_speed;\n\t}\n\telse\n\t{\n\t\tif (move < 0-self.yaw_speed )\n\t\t\tmove = 0-self.yaw_speed;\n\t}\n\n\tcurrent_yaw = anglemod (current_yaw + move);\n\n\tself.angles_y = current_yaw;\n};\n\n*/\n\n\n//============================================================================\n\nvoid() HuntTarget =\n{\n\tself.goalentity = self.enemy;\n\tself.think = self.th_run;\n\tself.ideal_yaw = vectoyaw(self.enemy.origin - self.origin);\n\tself.nextthink = time + 0.1;\n\tSUB_AttackFinished (1);\t// wait a while before first attack\n};\n\nvoid() SightSound =\n{\nlocal float\trsnd;\n\n\tif (self.classname == \"monster_ogre\")\t\n\t\tsound (self, CHAN_VOICE, \"ogre/ogwake.wav\", 1, ATTN_NORM);\n\telse if (self.classname == \"monster_knight\")\n\t\tsound (self, CHAN_VOICE, \"knight/ksight.wav\", 1, ATTN_NORM);\n\telse if (self.classname == \"monster_shambler\")\n\t\tsound (self, CHAN_VOICE, \"shambler/ssight.wav\", 1, ATTN_NORM);\n\telse if (self.classname == \"monster_demon1\")\n\t\tsound (self, CHAN_VOICE, \"demon/sight2.wav\", 1, ATTN_NORM);\n\telse if (self.classname == \"monster_wizard\")\n\t\tsound (self, CHAN_VOICE, \"wizard/wsight.wav\", 1, ATTN_NORM);\n\telse if (self.classname == \"monster_zombie\")\n\t\tsound (self, CHAN_VOICE, \"zombie/z_idle.wav\", 1, ATTN_NORM);\n\telse if (self.classname == \"monster_dog\")\n\t\tsound (self, CHAN_VOICE, \"dog/dsight.wav\", 1, ATTN_NORM);\n\telse if (self.classname == \"monster_hell_knight\")\n\t\tsound (self, CHAN_VOICE, \"hknight/sight1.wav\", 1, ATTN_NORM);\n\telse if (self.classname == \"monster_tarbaby\")\n\t\tsound (self, CHAN_VOICE, \"blob/sight1.wav\", 1, ATTN_NORM);\n\telse if (self.classname == \"monster_vomit\")\n\t\tsound (self, CHAN_VOICE, \"vomitus/v_sight1.wav\", 1, ATTN_NORM);\n\telse if (self.classname == \"monster_enforcer\")\n\t{\n\t\trsnd = rint(random() * 3);\t\t\t\n\t\tif (rsnd == 1)\n\t\t\tsound (self, CHAN_VOICE, \"enforcer/sight1.wav\", 1, ATTN_NORM);\n\t\telse if (rsnd == 2)\n\t\t\tsound (self, CHAN_VOICE, \"enforcer/sight2.wav\", 1, ATTN_NORM);\n\t\telse if (rsnd == 0)\n\t\t\tsound (self, CHAN_VOICE, \"enforcer/sight3.wav\", 1, ATTN_NORM);\n\t\telse\n\t\t\tsound (self, CHAN_VOICE, \"enforcer/sight4.wav\", 1, ATTN_NORM);\n\t}\n\telse if (self.classname == \"monster_army\")\n\t\tsound (self, CHAN_VOICE, \"soldier/sight1.wav\", 1, ATTN_NORM);\n\telse if (self.classname == \"monster_shalrath\")\n\t\tsound (self, CHAN_VOICE, \"shalrath/sight.wav\", 1, ATTN_NORM);\n};\n\nvoid() FoundTarget =\n{\n\tif (self.enemy.classname == \"player\")\n\t{\t// let other monsters see this monster for a while\n\t\tsight_entity = self;\n\t\tsight_entity_time = time;\n\t}\n\t\n\tself.show_hostile = time + 1;\t\t// wake up other monsters\n\n\tSightSound ();\n\tHuntTarget ();\n};\n\n/*\n===========\nFindTarget\n\nSelf is currently not attacking anything, so try to find a target\n\nReturns TRUE if an enemy was sighted\n\nWhen a player fires a missile, the point of impact becomes a fakeplayer so\nthat monsters that see the impact will respond as if they had seen the\nplayer.\n\nTo avoid spending too much time, only a single client (or fakeclient) is\nchecked each frame.  This means multi player games will have slightly\nslower noticing monsters.\n============\n*/\nfloat() FindTarget =\n{\n\tlocal entity\tclient;\n\tlocal float\t\tr;\n\n// if the first spawnflag bit is set, the monster will only wake up on\n// really seeing the player, not another monster getting angry\n\n// spawnflags & 3 is a big hack, because zombie crucified used the first\n// spawn flag prior to the ambush flag, and I forgot about it, so the second\n// spawn flag works as well\n\n\tif (sight_entity_time >= time - 0.1 && !(self.spawnflags & 3) )\n\t{\n\t\tclient = sight_entity;\n\t\tif (client.enemy == self.enemy)\n\t\t\treturn FALSE;\n\t}\n\telse\n\t{\n\t\tclient = checkclient ();\n\t\tif (!client)\n\t\t\treturn FALSE;\t// current check entity isn't in PVS\n\t}\n\n\tif (client == self.enemy)\n\t\treturn FALSE;\n\n\tif (client.flags & FL_NOTARGET)\n\t\treturn FALSE;\n\tif (client.items & IT_INVISIBILITY)\n\t\treturn FALSE;\n\n\tr = range (client);\n\tif (r == RANGE_FAR)\n\t\treturn FALSE;\n\t\t\n\tif (!visible (client))\n\t\treturn FALSE;\n\n\tif (client.sneak != 0)//sneaking players are invisible\n\t\treturn FALSE;\n\n\n\tif (r == RANGE_NEAR)\n\t{\n\t\tif (client.show_hostile < time && !infront (client))\n\t\t\treturn FALSE;\n\t}\n\telse if (r == RANGE_MID)\n\t{\n\t\tif ( /* client.show_hostile < time || */ !infront (client))\n\t\t\treturn FALSE;\n\t}\n\t\n//\n// got one\n//\n\tself.enemy = client;\n\tif (self.enemy.classname != \"player\")\n\t{\n\t\tself.enemy = self.enemy.enemy;\n\t\tif (self.enemy.classname != \"player\")\n\t\t{\n\t\t\tself.enemy = world;\n\t\t\treturn FALSE;\n\t\t}\n\t}\n\t\n\tFoundTarget ();\n\n\treturn TRUE;\n};\n\n\n//=============================================================================\n\nvoid(float dist) ai_forward =\n{\n\twalkmove (self.angles_y, dist);\n};\n\nvoid(float dist) ai_back =\n{\n\twalkmove ( (self.angles_y+180), dist);\n};\n\n\n/*\n=============\nai_pain\n\nstagger back a bit\n=============\n*/\nvoid(float dist) ai_pain =\n{\n\tai_back (dist);\n/*\n\tlocal float\taway;\n\t\n\taway = anglemod (vectoyaw (self.origin - self.enemy.origin) \n\t+ 180*(random()- 0.5) );\n\t\n\twalkmove (away, dist);\n*/\n};\n\n/*\n=============\nai_painforward\n\nstagger back a bit\n=============\n*/\nvoid(float dist) ai_painforward =\n{\n\twalkmove (self.ideal_yaw, dist);\n};\n\n/*\n=============\nai_walk\n\nThe monster is walking it's beat\n=============\n*/\nvoid(float dist) ai_walk =\n{\n\tmovedist = dist;\n\t\n\tif (self.classname == \"monster_dragon\")\n\t{\n\t\tmovetogoal (dist);\n\t\treturn;\n\t}\n\t// check for noticing a player\n\tif (FindTarget ())\n\t\treturn;\n\n\tmovetogoal (dist);\n};\n\n\n/*\n=============\nai_stand\n\nThe monster is staying in one place for a while, with slight angle turns\n=============\n*/\nvoid() ai_stand =\n{\n\tif (FindTarget ())\n\t\treturn;\n\t\n\tif (time > self.pausetime)\n\t{\n\t\tself.th_walk ();\n\t\treturn;\n\t}\n\t\n// change angle slightly\n\n};\n\n/*\n=============\nai_turn\n\ndon't move, but turn towards ideal_yaw\n=============\n*/\nvoid() ai_turn =\n{\n\tif (FindTarget ())\n\t\treturn;\n\t\n\tChangeYaw ();\n};\n\n//=============================================================================\n\n/*\n=============\nChooseTurn\n=============\n*/\nvoid(vector dest3) ChooseTurn =\n{\n\tlocal vector\tdir, newdir;\n\t\n\tdir = self.origin - dest3;\n\n\tnewdir_x = trace_plane_normal_y;\n\tnewdir_y = 0 - trace_plane_normal_x;\n\tnewdir_z = 0;\n\t\n\tif (dir * newdir > 0)\n\t{\n\t\tdir_x = 0 - trace_plane_normal_y;\n\t\tdir_y = trace_plane_normal_x;\n\t}\n\telse\n\t{\n\t\tdir_x = trace_plane_normal_y;\n\t\tdir_y = 0 - trace_plane_normal_x;\n\t}\n\n\tdir_z = 0;\n\tself.ideal_yaw = vectoyaw(dir);\t\n};\n\n/*\n============\nFacingIdeal\n\n============\n*/\nfloat() FacingIdeal =\n{\n\tlocal\tfloat\tdelta;\n\t\n\tdelta = anglemod(self.angles_y - self.ideal_yaw);\n\tif (delta > 45 && delta < 315)\n\t\treturn FALSE;\n\treturn TRUE;\n};\n\n\n//=============================================================================\n\nfloat()\tWizardCheckAttack;\nfloat()\tDogCheckAttack;\n\nfloat() CheckAnyAttack =\n{\n\tif (!enemy_vis)\n\t\treturn FALSE;\n\tif (self.classname == \"monster_army\")\n\t\treturn SoldierCheckAttack ();\n\tif (self.classname == \"monster_ogre\")\n\t\treturn OgreCheckAttack ();\n\tif (self.classname == \"monster_shambler\")\n\t\treturn ShamCheckAttack ();\n\tif (self.classname == \"monster_demon1\")\n\t\treturn DemonCheckAttack ();\n\tif (self.classname == \"monster_dog\")\n\t\treturn DogCheckAttack ();\n\tif (self.classname == \"monster_wizard\")\n\t\treturn WizardCheckAttack ();\n\treturn CheckAttack ();\n};\n\n\n/*\n=============\nai_run_melee\n\nTurn and close until within an angle to launch a melee attack\n=============\n*/\nvoid() ai_run_melee =\n{\n\tself.ideal_yaw = enemy_yaw;\n\tChangeYaw ();\n\n\tif (FacingIdeal())\n\t{\n\t\tself.th_melee ();\n\t\tself.attack_state = AS_STRAIGHT;\n\t}\n};\n\n\n/*\n=============\nai_run_missile\n\nTurn in place until within an angle to launch a missile attack\n=============\n*/\nvoid() ai_run_missile =\n{\n\tself.ideal_yaw = enemy_yaw;\n\tChangeYaw ();\n\tif (FacingIdeal())\n\t{\n\t\tself.th_missile ();\n\t\tself.attack_state = AS_STRAIGHT;\n\t}\n};\n\n\n/*\n=============\nai_run_slide\n\nStrafe sideways, but stay at aproximately the same range\n=============\n*/\nvoid() ai_run_slide =\n{\n\tlocal float\tofs;\n\t\n\tself.ideal_yaw = enemy_yaw;\n\tChangeYaw ();\n\tif (self.lefty)\n\t\tofs = 90;\n\telse\n\t\tofs = -90;\n\t\n\tif (walkmove (self.ideal_yaw + ofs, movedist))\n\t\treturn;\n\t\t\n\tself.lefty = 1 - self.lefty;\n\t\n\twalkmove (self.ideal_yaw - ofs, movedist);\n};\n\n\n/*\n=============\nai_run\n\nThe monster has an enemy it is trying to kill\n=============\n*/\nvoid(float dist) ai_run =\n{\n\tmovedist = dist;\n// see if the enemy is dead\n\tif (self.enemy.health <= 0)\n\t{\n\t\tself.enemy = world;\n\t// FIXME: look all around for other targets\n\t\tif (self.oldenemy.health > 0)\n\t\t{\n\t\t\tself.enemy = self.oldenemy;\n\t\t\tHuntTarget ();\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (self.movetarget)\n\t\t\t\tself.th_walk ();\n\t\t\telse\n\t\t\t\tself.th_stand ();\n\t\t\treturn;\n\t\t}\n\t}\n\n\tself.show_hostile = time + 1;\t\t// wake up other monsters\n\n// check knowledge of enemy\n\tenemy_vis = visible(self.enemy);\n\tif (enemy_vis)\n\t\tself.search_time = time + 5;\n\n// look for other coop players\n\tif (self.search_time < time)\n\t{\n\t\tif (FindTarget ())\n\t\t\treturn;\n\t}\n\n\tenemy_infront = infront(self.enemy);\n\tenemy_range = range(self.enemy);\n\tenemy_yaw = vectoyaw(self.enemy.origin - self.origin);\n\t\n\tif (self.attack_state == AS_MISSILE)\n\t{\n//dprint (\"ai_run_missile\\n\");\n\t\tai_run_missile ();\n\t\treturn;\n\t}\n\tif (self.attack_state == AS_MELEE)\n\t{\n//dprint (\"ai_run_melee\\n\");\n\t\tai_run_melee ();\n\t\treturn;\n\t}\n\n\tif (CheckAnyAttack ())\n\t\treturn;\t\t\t\t\t// beginning an attack\n\t\t\n\tif (self.attack_state == AS_SLIDING)\n\t{\n\t\tai_run_slide ();\n\t\treturn;\n\t}\n\t\t\n// head straight in\n\tmovetogoal (dist);\t\t// done in C code...\n};\n\n"
  },
  {
    "path": "quakec/fallout2/boss.qc",
    "content": "/*\n==============================================================================\n\nBOSS-ONE\n\n==============================================================================\n*/\n$cd id1/models/boss1\n$origin 0 0 -15\n$base base\n$skin skin\n$scale 5\n\n$frame rise1 rise2 rise3 rise4 rise5 rise6 rise7 rise8 rise9 rise10\n$frame rise11 rise12 rise13 rise14 rise15 rise16 rise17 \n\n$frame walk1 walk2 walk3 walk4 walk5 walk6 walk7 walk8\n$frame walk9 walk10 walk11 walk12 walk13 walk14 walk15\n$frame walk16 walk17 walk18 walk19 walk20 walk21 walk22\n$frame walk23 walk24 walk25 walk26 walk27 walk28 walk29 walk30 walk31\n\n$frame death1 death2 death3 death4 death5 death6 death7 death8 death9\n\n$frame attack1 attack2 attack3 attack4 attack5 attack6 attack7 attack8\n$frame attack9 attack10 attack11 attack12 attack13 attack14 attack15\n$frame attack16 attack17 attack18 attack19 attack20 attack21 attack22\n$frame attack23\n\n$frame shocka1 shocka2 shocka3 shocka4 shocka5 shocka6 shocka7 shocka8\n$frame shocka9 shocka10 \n\n$frame shockb1 shockb2 shockb3 shockb4 shockb5 shockb6\n\n$frame shockc1 shockc2 shockc3 shockc4 shockc5 shockc6 shockc7 shockc8 \n$frame shockc9 shockc10\n\n\nvoid(vector p) boss_missile;\n\nvoid() boss_face =\n{\n\t\n// go for another player if multi player\n\tif (self.enemy.health <= 0 || random() < 0.02 || self.enemy.class == 0 || self.enemy.team == 0)\n\t{\n\t\tself.enemy = find(self.enemy, classname, \"player\");\n\t\tif (!self.enemy)\n\t\t\tself.enemy = find(self.enemy, classname, \"player\");\n\t}\n\tai_face();\n};\n\nvoid() boss_rise1\t=[\t$rise1, boss_rise2 ] {\nsound (self, CHAN_WEAPON, \"boss1/out1.wav\", 1, ATTN_NORM);\n};\nvoid() boss_rise2\t=[\t$rise2, boss_rise3 ] {\nsound (self, CHAN_VOICE, \"boss1/sight1.wav\", 1, ATTN_NORM);\n};\nvoid() boss_rise3\t=[\t$rise3, boss_rise4 ] {};\nvoid() boss_rise4\t=[\t$rise4, boss_rise5 ] {};\nvoid() boss_rise5\t=[\t$rise5, boss_rise6 ] {};\nvoid() boss_rise6\t=[\t$rise6, boss_rise7 ] {};\nvoid() boss_rise7\t=[\t$rise7, boss_rise8 ] {};\nvoid() boss_rise8\t=[\t$rise8, boss_rise9 ] {};\nvoid() boss_rise9\t=[\t$rise9, boss_rise10 ] {};\nvoid() boss_rise10\t=[\t$rise10, boss_rise11 ] {};\nvoid() boss_rise11\t=[\t$rise11, boss_rise12 ] {};\nvoid() boss_rise12\t=[\t$rise12, boss_rise13 ] {};\nvoid() boss_rise13\t=[\t$rise13, boss_rise14 ] {};\nvoid() boss_rise14\t=[\t$rise14, boss_rise15 ] {};\nvoid() boss_rise15\t=[\t$rise15, boss_rise16 ] {};\nvoid() boss_rise16\t=[\t$rise16, boss_rise17 ] {};\nvoid() boss_rise17\t=[\t$rise17, boss_missile1 ] {};\n\nvoid() boss_idle1\t=[\t$walk1, boss_idle2 ]\n{\n\tif (self.enemy)\n\t\tboss_missile1();\n\n// look for other players\n};\nvoid() boss_idle2\t=[\t$walk2, boss_idle3 ] {boss_face();};\nvoid() boss_idle3\t=[\t$walk3, boss_idle4 ] {boss_face();};\nvoid() boss_idle4\t=[\t$walk4, boss_idle5 ] {boss_face();};\nvoid() boss_idle5\t=[\t$walk5, boss_idle6 ] {boss_face();};\nvoid() boss_idle6\t=[\t$walk6, boss_idle7 ] {boss_face();};\nvoid() boss_idle7\t=[\t$walk7, boss_idle8 ] {boss_face();};\nvoid() boss_idle8\t=[\t$walk8, boss_idle9 ] {boss_face();};\nvoid() boss_idle9\t=[\t$walk9, boss_idle10 ] {boss_face();};\nvoid() boss_idle10\t=[\t$walk10, boss_idle11 ] {boss_face();};\nvoid() boss_idle11\t=[\t$walk11, boss_idle12 ] {boss_face();};\nvoid() boss_idle12\t=[\t$walk12, boss_idle13 ] {boss_face();};\nvoid() boss_idle13\t=[\t$walk13, boss_idle14 ] {boss_face();};\nvoid() boss_idle14\t=[\t$walk14, boss_idle15 ] {boss_face();};\nvoid() boss_idle15\t=[\t$walk15, boss_idle16 ] {boss_face();};\nvoid() boss_idle16\t=[\t$walk16, boss_idle17 ] {boss_face();};\nvoid() boss_idle17\t=[\t$walk17, boss_idle18 ] {boss_face();};\nvoid() boss_idle18\t=[\t$walk18, boss_idle19 ] {boss_face();};\nvoid() boss_idle19\t=[\t$walk19, boss_idle20 ] {boss_face();};\nvoid() boss_idle20\t=[\t$walk20, boss_idle21 ] {boss_face();};\nvoid() boss_idle21\t=[\t$walk21, boss_idle22 ] {boss_face();};\nvoid() boss_idle22\t=[\t$walk22, boss_idle23 ] {boss_face();};\nvoid() boss_idle23\t=[\t$walk23, boss_idle24 ] {boss_face();};\nvoid() boss_idle24\t=[\t$walk24, boss_idle25 ] {boss_face();};\nvoid() boss_idle25\t=[\t$walk25, boss_idle26 ] {boss_face();};\nvoid() boss_idle26\t=[\t$walk26, boss_idle27 ] {boss_face();};\nvoid() boss_idle27\t=[\t$walk27, boss_idle28 ] {boss_face();};\nvoid() boss_idle28\t=[\t$walk28, boss_idle29 ] {boss_face();};\nvoid() boss_idle29\t=[\t$walk29, boss_idle30 ] {boss_face();};\nvoid() boss_idle30\t=[\t$walk30, boss_idle31 ] {boss_face();};\nvoid() boss_idle31\t=[\t$walk31, boss_idle1 ] {boss_face();};\n\nvoid() boss_missile1\t=[\t$attack1, boss_missile2 ] {boss_face();};\nvoid() boss_missile2\t=[\t$attack2, boss_missile3 ] {boss_face();};\nvoid() boss_missile3\t=[\t$attack3, boss_missile4 ] {boss_face();};\nvoid() boss_missile4\t=[\t$attack4, boss_missile5 ] {boss_face();};\nvoid() boss_missile5\t=[\t$attack5, boss_missile6 ] {boss_face();};\nvoid() boss_missile6\t=[\t$attack6, boss_missile7 ] {boss_face();};\nvoid() boss_missile7\t=[\t$attack7, boss_missile8 ] {boss_face();};\nvoid() boss_missile8\t=[\t$attack8, boss_missile9 ] {boss_face();};\nvoid() boss_missile9\t=[\t$attack9, boss_missile10 ] {boss_missile('100 100 200');};\nvoid() boss_missile10\t=[\t$attack10, boss_missile11 ] {boss_face();};\nvoid() boss_missile11\t=[\t$attack11, boss_missile12 ] {boss_face();};\nvoid() boss_missile12\t=[\t$attack12, boss_missile13 ] {boss_face();};\nvoid() boss_missile13\t=[\t$attack13, boss_missile14 ] {boss_face();};\nvoid() boss_missile14\t=[\t$attack14, boss_missile15 ] {boss_face();};\nvoid() boss_missile15\t=[\t$attack15, boss_missile16 ] {boss_face();};\nvoid() boss_missile16\t=[\t$attack16, boss_missile17 ] {boss_face();};\nvoid() boss_missile17\t=[\t$attack17, boss_missile18 ] {boss_face();};\nvoid() boss_missile18\t=[\t$attack18, boss_missile19 ] {boss_face();};\nvoid() boss_missile19\t=[\t$attack19, boss_missile20 ] {boss_face();};\nvoid() boss_missile20\t=[\t$attack20, boss_missile21 ] {boss_missile('100 -100 200');};\nvoid() boss_missile21\t=[\t$attack21, boss_missile22 ] {boss_face();};\nvoid() boss_missile22\t=[\t$attack22, boss_missile23 ] {boss_face();};\nvoid() boss_missile23\t=[\t$attack23, boss_missile1 ] {boss_face();};\n\nvoid() boss_shocka1 =[\t$shocka1, boss_shocka2 ] {};\nvoid() boss_shocka2 =[\t$shocka2, boss_shocka3 ] {};\nvoid() boss_shocka3 =[\t$shocka3, boss_shocka4 ] {};\nvoid() boss_shocka4 =[\t$shocka4, boss_shocka5 ] {};\nvoid() boss_shocka5 =[\t$shocka5, boss_shocka6 ] {};\nvoid() boss_shocka6 =[\t$shocka6, boss_shocka7 ] {};\nvoid() boss_shocka7 =[\t$shocka7, boss_shocka8 ] {};\nvoid() boss_shocka8 =[\t$shocka8, boss_shocka9 ] {};\nvoid() boss_shocka9 =[\t$shocka9, boss_shocka10 ] {};\nvoid() boss_shocka10 =[\t$shocka10, boss_missile1 ] {};\n\nvoid() boss_shockb1 =[\t$shockb1, boss_shockb2 ] {};\nvoid() boss_shockb2 =[\t$shockb2, boss_shockb3 ] {};\nvoid() boss_shockb3 =[\t$shockb3, boss_shockb4 ] {};\nvoid() boss_shockb4 =[\t$shockb4, boss_shockb5 ] {};\nvoid() boss_shockb5 =[\t$shockb5, boss_shockb6 ] {};\nvoid() boss_shockb6 =[\t$shockb6, boss_shockb7 ] {};\nvoid() boss_shockb7 =[\t$shockb1, boss_shockb8 ] {};\nvoid() boss_shockb8 =[\t$shockb2, boss_shockb9 ] {};\nvoid() boss_shockb9 =[\t$shockb3, boss_shockb10 ] {};\nvoid() boss_shockb10 =[\t$shockb4, boss_missile1 ] {};\n\nvoid() boss_shockc1 =[\t$shockc1, boss_shockc2 ] {};\nvoid() boss_shockc2 =[\t$shockc2, boss_shockc3 ] {};\nvoid() boss_shockc3 =[\t$shockc3, boss_shockc4 ] {};\nvoid() boss_shockc4 =[\t$shockc4, boss_shockc5 ] {};\nvoid() boss_shockc5 =[\t$shockc5, boss_shockc6 ] {};\nvoid() boss_shockc6 =[\t$shockc6, boss_shockc7 ] {};\nvoid() boss_shockc7 =[\t$shockc7, boss_shockc8 ] {};\nvoid() boss_shockc8 =[\t$shockc8, boss_shockc9 ] {};\nvoid() boss_shockc9 =[\t$shockc9, boss_shockc10 ] {};\nvoid() boss_shockc10 =[\t$shockc10, boss_death1 ] {};\n\nvoid() boss_death1 = [$death1, boss_death2] {\nsound (self, CHAN_VOICE, \"boss1/death.wav\", 1, ATTN_NORM);\n};\nvoid() boss_death2 = [$death2, boss_death3] {};\nvoid() boss_death3 = [$death3, boss_death4] {};\nvoid() boss_death4 = [$death4, boss_death5] {};\nvoid() boss_death5 = [$death5, boss_death6] {};\nvoid() boss_death6 = [$death6, boss_death7] {};\nvoid() boss_death7 = [$death7, boss_death8] {};\nvoid() boss_death8 = [$death8, boss_death9] {};\nvoid() boss_death9 = [$death9, boss_death10]\n{\n\tsound (self, CHAN_BODY, \"boss1/out1.wav\", 1, ATTN_NORM);\n\tWriteByte (MSG_BROADCAST, SVC_TEMPENTITY);\n\tWriteByte (MSG_BROADCAST, TE_LAVASPLASH);\n\tWriteCoord (MSG_BROADCAST, self.origin_x);\n\tWriteCoord (MSG_BROADCAST, self.origin_y);\n\tWriteCoord (MSG_BROADCAST, self.origin_z);\n};\n\nvoid() boss_death10 = [$death9, boss_death10]\n{\n\tkilled_monsters = killed_monsters + 1;\n\tWriteByte (MSG_ALL, SVC_KILLEDMONSTER);\t// FIXME: reliable broadcast\n\tSUB_UseTargets ();\n\tremove (self);\n};\n\nvoid () LavaExplode =\n{\n\tsound (self, CHAN_VOICE, \"ambience/gunfire7.wav\", 1, ATTN_NONE);\n\tself.origin = (self.origin + '0 0 16');\n\tWriteByte (MSG_BROADCAST, SVC_TEMPENTITY);\n\tWriteByte (MSG_BROADCAST, TE_EXPLOSION);\n\tWriteCoord (MSG_BROADCAST, self.origin_x);\n\tWriteCoord (MSG_BROADCAST, self.origin_y);\n\tWriteCoord (MSG_BROADCAST, self.origin_z);\n\tExplosion (2);\n\tT_RadiusDamage (self, self.owner, 60, world, \"\");\n\tremove (self);\n};\n\nvoid(vector p) boss_missile =\n{\n\tlocal\tvector\toffang;\n\tlocal\tvector\torg, vec, d;\n\tlocal\tfloat\tt;\n\n\toffang = vectoangles (self.enemy.origin - self.origin);\t\n\tmakevectors (offang);\n\n\torg = self.origin + p_x*v_forward + p_y*v_right + p_z*'0 0 1';\n\t\n\n\tt = vlen(self.enemy.origin - org) / 1200;\n\tvec = self.enemy.velocity;\n\tvec_z = 0;\n\td = self.enemy.origin + t * vec;\t\t\n\n\t\n\tvec = normalize (d - org);\n\n\tlaunch_spike (org, vec);\n\tsetmodel (newmis, \"progs/lavaball.mdl\");\n\tnewmis.avelocity = '200 100 300';\n\tsetsize (newmis, VEC_ORIGIN, VEC_ORIGIN);\t\t\n\tnewmis.velocity = vec*300;\n\tnewmis.touch = LavaExplode; // rocket explosion\n\tsound (self, CHAN_WEAPON, \"boss1/throw.wav\", 1, ATTN_NORM);\n\n// check for dead enemy\n\tif (self.enemy.health <= 0)\n\t\tboss_idle1 ();\n};\n\n\nvoid() boss_awake =\n{\n\tself.solid = SOLID_SLIDEBOX;\n\tself.movetype = MOVETYPE_STEP;\n\tself.takedamage = DAMAGE_YES;\n\tself.health = 8000;\t\n\tself.th_die = boss_death1;\n\n\tsetmodel (self, \"progs/boss.mdl\");\n\tsetsize (self, '-128 -128 -24', '128 128 256');\n\t\n\tWriteByte (MSG_BROADCAST, SVC_TEMPENTITY);\n\tWriteByte (MSG_BROADCAST, TE_LAVASPLASH);\n\tWriteCoord (MSG_BROADCAST, self.origin_x);\n\tWriteCoord (MSG_BROADCAST, self.origin_y);\n\tWriteCoord (MSG_BROADCAST, self.origin_z);\n\n\tself.yaw_speed = 20;\n\tboss_rise1 ();\n};\n\nvoid() monster_boss =\n{\n\tprecache_model (\"progs/boss.mdl\");\n\tprecache_model (\"progs/lavaball.mdl\");\n\n\tprecache_sound (\"weapons/rocket1i.wav\");\n\tprecache_sound (\"boss1/out1.wav\");\n\tprecache_sound (\"boss1/sight1.wav\");\n\tprecache_sound (\"misc/power.wav\");\n\tprecache_sound (\"boss1/throw.wav\");\n\tprecache_sound (\"boss1/pain.wav\");\n\tprecache_sound (\"boss1/death.wav\");\n\n\ttotal_monsters = total_monsters + 1;\n\n\tself.think = boss_awake;\n\tself.nextthink = time + 1;\n};\n\n//===========================================================================\n\nentity\tle1, le2;\nfloat\tlightning_end;\n\nvoid() lightning_fire =\n{\n\tlocal vector p1, p2;\n\t\n\tif (time >= lightning_end)\n\t{\t// done here, put the terminals back up\n\t\tself = le1;\n\t\tdoor_go_down ();\n\t\tself = le2;\n\t\tdoor_go_down ();\n\t\treturn;\n\t}\n\t\n\tp1 = (le1.mins + le1.maxs) * 0.5;\n\tp1_z = le1.absmin_z - 16;\n\t\n\tp2 = (le2.mins + le2.maxs) * 0.5;\n\tp2_z = le2.absmin_z - 16;\n\t\n\t// compensate for length of bolt\n\tp2 = p2 - normalize(p2-p1)*100;\n\n\tself.nextthink = time + 0.1;\n\tself.think = lightning_fire;\n\n\tWriteByte (MSG_ALL, SVC_TEMPENTITY);\n\tWriteByte (MSG_ALL, TE_LIGHTNING3);\n\tWriteEntity (MSG_ALL, world);\n\tWriteCoord (MSG_ALL, p1_x);\n\tWriteCoord (MSG_ALL, p1_y);\n\tWriteCoord (MSG_ALL, p1_z);\n\tWriteCoord (MSG_ALL, p2_x);\n\tWriteCoord (MSG_ALL, p2_y);\n\tWriteCoord (MSG_ALL, p2_z);\n};\n\nvoid() lightning_use =\n{\n\tif (lightning_end >= time + 1)\n\t\treturn;\n\n\tle1 = find( world, target, \"lightning\");\n\tle2 = find( le1, target, \"lightning\");\n\tif (!le1 || !le2)\n\t{\n\t\tdprint (\"missing lightning targets\\n\");\n\t\treturn;\n\t}\n\t\n\tif (\n\t (le1.state != STATE_TOP && le1.state != STATE_BOTTOM)\n\t|| (le2.state != STATE_TOP && le2.state != STATE_BOTTOM)\n\t|| (le1.state != le2.state) )\n\t{\n//\t\tdprint (\"not aligned\\n\");\n\t\treturn;\n\t}\n\n// don't let the electrodes go back up until the bolt is done\n\tle1.nextthink = -1;\n\tle2.nextthink = -1;\n\tlightning_end = time + 1;\n\n\tsound (self, CHAN_VOICE, \"misc/power.wav\", 1, ATTN_NORM);\n\tlightning_fire ();\t\t\n\n// advance the boss pain if down\n\tself = find (world, classname, \"monster_boss\");\n\tif (!self)\n\t\treturn;\n\tself.enemy = activator;\n\tif (le1.state == STATE_TOP && self.health > 0)\n\t{\n\t\tsound (self, CHAN_VOICE, \"boss1/pain.wav\", 1, ATTN_NORM);\n\t\tself.health = self.health - 1;\n\t\tif (self.health >= 2)\n\t\t\tboss_shocka1();\n\t\telse if (self.health == 1)\n\t\t\tboss_shockb1();\n\t\telse if (self.health == 0)\n\t\t\tboss_shockc1();\n\t}\t\n};\n\n\n"
  },
  {
    "path": "quakec/fallout2/buttons.qc",
    "content": "// button and multiple button\n\nvoid() button_wait;\nvoid() button_return;\n\nvoid() button_wait =\n{\n\tself.state = STATE_TOP;\n\tself.nextthink = self.ltime + self.wait;\n\tself.think = button_return;\n\tactivator = self.enemy;\n\tSUB_UseTargets();\n\tself.frame = 1;\t\t\t// use alternate textures\n};\n\nvoid() button_done =\n{\n\tself.state = STATE_BOTTOM;\n};\n\nvoid() button_return =\n{\n\tself.state = STATE_DOWN;\n\tSUB_CalcMove (self.pos1, self.speed, button_done);\n\tself.frame = 0;\t\t\t// use normal textures\n\tif (self.health)\n\t\tself.takedamage = DAMAGE_YES;\t// can be shot again\n};\n\n\nvoid() button_blocked =\n{\t// do nothing, just don't ome all the way back out\n};\n\n\nvoid() button_fire =\n{\n\tif (self.state == STATE_UP || self.state == STATE_TOP)\n\t\treturn;\n\n\tsound (self, CHAN_VOICE, self.noise, 1, ATTN_NORM);\n\n\tself.state = STATE_UP;\n\tSUB_CalcMove (self.pos2, self.speed, button_wait);\n};\n\n\nvoid() button_use =\n{\n\tself.enemy = activator;\n\tbutton_fire ();\n};\n\nvoid() button_touch =\n{\n\tif (!triggercantouch(self, other))\n\t\treturn;\n\n\tif (other.classname != \"player\")\n\t\treturn;\n\tself.enemy = other;\n\tbutton_fire ();\n};\n\nvoid() button_killed =\n{\n\tself.enemy = damage_attacker;\n\tself.health = self.max_health;\n\tself.takedamage = DAMAGE_NO;\t// wil be reset upon return\n\tbutton_fire ();\n};\n\n\n/*QUAKED func_button (0 .5 .8) ?\nWhen a button is touched, it moves some distance in the direction of it's angle, triggers all of it's targets, waits some time, then returns to it's original position where it can be triggered again.\n\n\"angle\"\t\tdetermines the opening direction\n\"target\"\tall entities with a matching targetname will be used\n\"speed\"\t\toverride the default 40 speed\n\"wait\"\t\toverride the default 1 second wait (-1 = never return)\n\"lip\"\t\toverride the default 4 pixel lip remaining at end of move\n\"health\"\tif set, the button must be killed instead of touched\n\"sounds\"\n0) steam metal\n1) wooden clunk\n2) metallic click\n3) in-out\n*/\nvoid() func_button =\n{\n\tif (self.sounds == 0)\n\t{\n\t\tprecache_sound (\"buttons/airbut1.wav\");\n\t\tself.noise = \"buttons/airbut1.wav\";\n\t}\n\tif (self.sounds == 1)\n\t{\n\t\tprecache_sound (\"buttons/switch21.wav\");\n\t\tself.noise = \"buttons/switch21.wav\";\n\t}\n\tif (self.sounds == 2)\n\t{\n\t\tprecache_sound (\"buttons/switch02.wav\");\n\t\tself.noise = \"buttons/switch02.wav\";\n\t}\n\tif (self.sounds == 3)\n\t{\n\t\tprecache_sound (\"buttons/switch04.wav\");\n\t\tself.noise = \"buttons/switch04.wav\";\n\t}\n\t\n\tSetMovedir ();\n\n\tself.movetype = MOVETYPE_PUSH;\n\tself.solid = SOLID_BSP;\n\tsetmodel (self, self.model);\n\n\tself.blocked = button_blocked;\n\tself.use = button_use;\n\n\tif (self.health)\n\t{\n\t\tself.max_health = self.health;\n\t\tself.th_die = button_killed;\n\t\tself.takedamage = DAMAGE_YES;\n\t}\n\telse\n\t\tself.touch = button_touch;\n\n\tif (!self.speed)\n\t\tself.speed = 40;\n\tif (!self.wait)\n\t\tself.wait = 1;\n\tif (!self.lip)\n\t\tself.lip = 4;\n\n\tself.state = STATE_BOTTOM;\n\n\tself.pos1 = self.origin;\n\tself.pos2 = self.pos1 + self.movedir*(fabs(self.movedir*self.size) - self.lip);\n};\n\n"
  },
  {
    "path": "quakec/fallout2/client.qc",
    "content": "\n// prototypes\nvoid () W_WeaponFrame;\nvoid() W_SetCurrentAmmo;\nvoid() player_pain;\nvoid() player_stand1;\nvoid (vector org) spawn_tfog;\nvoid (vector org, entity death_owner) spawn_tdeath;\nvoid(float slot) WeaponAmmo;\nvoid() Identify;\nvoid(entity to, float iid, float amount) AddStackable;\nvoid(entity to, float iid, float amount) AddNonStackable;\n\nfloat modelindex_dead, modelindex_sneak, modelindex_prone, modelindex_eyes, modelindex_player, modelindex_gone;\n\n/*\n=============================================================================\n\n\t\t\t\tLEVEL CHANGING / INTERMISSION\n\n=============================================================================\n*/\n\nstring nextmap;\n\nfloat   intermission_running;\nfloat   intermission_exittime;\n\n/*QUAKED info_intermission (1 0.5 0.5) (-16 -16 -16) (16 16 16)\nThis is the camera point for the intermission.\nUse mangle instead of angle, so you can set pitch or roll as well as yaw.  'pitch roll yaw'\n*/\nvoid() info_intermission =\n{\n\tself.angles = self.mangle;      // so C can get at it\n};\n\n\n\nvoid() DecodeLevelParms;\n\nvoid() SetChangeParms =\n{\n\tif (!self.current_slot)\t//changing without spawning?\n\t\tDecodeLevelParms();\t//make sure we have our inventory!\n\n/*\n\tif (self.health <= 0)\n\t{\n\t\tSetNewParms ();\n\t\treturn;\n\t}\n */\n\n// remove items\n\tself.items = self.items - (self.items & \n\t(IT_KEY1 | IT_KEY2 | IT_INVISIBILITY | IT_INVULNERABILITY | IT_SUIT | IT_QUAD) );\n\t\n//\tparm1 = self.slot1;\n//\tparm2 = self.slot2;\n//\tparm3 = self.armor;\n//\tparm4 = self.protect;\n//\tparm5 = self.equipment;\n//\tparm6 = self.chem;\n//\tparm7 = self.chemcount;\n\tparm8 = self.ammo_shells;\n\tparm9 = self.trait;\n\tparm10 = self.perk;\n//\tparm11 = self.bandages;\n//\tparm12 = self.scraps;\n\t//parm13 = self.class;\n\tparm14 = self.team;\n//\tparm15 = self.grenadetype;\n\n\n\n\n\tparm16 = self.current_slot;\n\tparm17 = self.islot1;\n\tparm18 = self.islot2;\n\tparm19 = self.islot3;\n\tparm20 = self.islot4;\n\tparm21 = self.islot5;\n\tparm22 = self.islot6;\n\tparm23 = self.islot7;\n\tparm24 = self.islot8;\n\tparm25 = self.islot9;\n\tparm26 = self.islot10;\n\tparm27 = self.islot11;\n\tparm28 = self.islot12;\n\tparm29 = self.islot13;\n\tparm30 = self.islot14;\n\tparm31 = self.islot15;\n\tparm32 = self.islot16;\n};\n\nvoid() SetNewParms =\n{\t//remember, don't use self!\n//\tparm1 = IID_WP_USP;\n//\tparm2 = IID_WP_KNIFE;\n//\tparm3 = IID_ARM_SHIRT;\n//\tparm4 = 0;\n//\tparm5 = 0;\n//\tparm6 = 0;\n// \tparm7 = 0;\n\tparm8 = 8;\t//starting cash\n \tparm9 = 0;\n\tparm10 = 0;\n//\tparm11 = 0;\n//\tparm12 = 0;\n\t//parm13 = 0;\n\tparm14 = 0;\n//\tparm15 = 0;\n\n\n//16 is the current_slot\n//17 to 32 are inventory slots.\n//the assignments were moved into PutClientInServer\n//on account of changing maps before spawning was resulting in no inventory.\n\tparm16 = 0;\n\tparm17 = 0;\n\tparm18 = 0;\n\tparm19 = 0;\n\tparm20 = 0;\n\tparm21 = 0;\n\tparm22 = 0;\n\tparm23 = 0;\n\tparm24 = 0;\n\tparm25 = 0;\n\tparm26 = 0;\n\tparm27 = 0;\n\tparm28 = 0;\n\tparm29 = 0;\n\tparm30 = 0;\n\tparm31 = 0;\n\tparm32 = 0;\n};\n\nvoid() DecodeLevelParms =\n{\n\tsetspawnparms(self);\n\n//\tself.slot1 = parm1;\n//\tself.slot2 = parm2;\n//\tself.armor = parm3;\n//\tself.protect = parm4;\n//\tself.equipment = parm5;\n//\tself.chem = parm6;\n//\tself.chemcount = parm7;\n\tself.ammo_shells = parm8;\n\tself.trait = parm9;\n\tself.perk = parm10;\n//\tself.bandages = parm11;\n//\tself.scraps = parm12;\n//\tself.class = parm13;\n\tself.oldteam = parm14;\n//\tself.grenadetype = parm15;\n\n\n\tself.current_slot = parm16;\n\tself.islot1 = parm17;\n\tself.islot2 = parm18;\n\tself.islot3 = parm19;\n\tself.islot4 = parm20;\n\tself.islot5 = parm21;\n\tself.islot6 = parm22;\n\tself.islot7 = parm23;\n\tself.islot8 = parm24;\n\tself.islot9 = parm25;\n\tself.islot10= parm26;\n\tself.islot11= parm27;\n\tself.islot12 = parm28;\n\tself.islot13 = parm29;\n\tself.islot14 = parm30;\n\tself.islot15 = parm31;\n\tself.islot16 = parm32;\n};\n\n/*\n============\nFindIntermission\n\nReturns the entity to view from\n============\n*/\nentity() FindIntermission =\n{\n\tlocal   entity spot;\n\tlocal   float cyc;\n\n// look for info_intermission first\n\tspot = find (world, classname, \"info_intermission\");\n\tif (spot)\n\t{       // pick a random one\n\t\tcyc = random() * 4;\n\t\twhile (cyc > 1)\n\t\t{\n\t\t\tspot = find (spot, classname, \"info_intermission\");\n\t\t\tif (!spot)\n\t\t\t\tspot = find (spot, classname, \"info_intermission\");\n\t\t\tcyc = cyc - 1;\n\t\t}\n\t\treturn spot;\n\t}\n\n// then look for the start position\n\tspot = find (world, classname, \"info_player_start\");\n\tif (spot)\n\t\treturn spot;\n\t\n\tobjerror (\"FindIntermission: no spot\");\n\treturn world;\n};\n\n\nvoid() GotoNextMap =\n{\n\tlocal string newmap;\n\n//ZOID: 12-13-96, samelevel is overloaded, only 1 works for same level\n\n\tif (cvar(\"samelevel\") == 1)     // if samelevel is set, stay on same level\n\t\tchangelevel (mapname);\n\telse {\n\t\t// configurable map lists, see if the current map exists as a\n\t\t// serverinfo/localinfo var\n\t\tnewmap = infokey(world, mapname);\n\t\tif (newmap != \"\")\n\t\t\tchangelevel (newmap);\n\t\telse\n\t\t\tchangelevel (nextmap);\n\t}\n};\n\n\n\n/*\n============\nIntermissionThink\n\nWhen the player presses attack or jump, change to the next level\n============\n*/\nvoid() IntermissionThink =\n{\n\tif (time < intermission_exittime)\n\t\treturn;\n\n\tif (!self.button0 && !self.button1 && !self.button2)\n\t\treturn;\n\t\n\tGotoNextMap ();\n};\n\n/*\n============\nexecute_changelevel\n\nThe global \"nextmap\" has been set previously.\nTake the players to the intermission spot\n============\n*/\nvoid() execute_changelevel =\n{\n\tlocal entity    pos;\n\n\tintermission_running = 1;\n\t\n// enforce a wait time before allowing changelevel\n\tintermission_exittime = time + 5;\n\n\tpos = FindIntermission ();\n\n// play intermission music\n\tWriteByte (MSG_ALL, SVC_CDTRACK);\n\tWriteByte (MSG_ALL, 3);\n\n\tWriteByte (MSG_ALL, SVC_INTERMISSION);\n\tWriteCoord (MSG_ALL, pos.origin_x);\n\tWriteCoord (MSG_ALL, pos.origin_y);\n\tWriteCoord (MSG_ALL, pos.origin_z);\n\tWriteAngle (MSG_ALL, pos.mangle_x);\n\tWriteAngle (MSG_ALL, pos.mangle_y);\n\tWriteAngle (MSG_ALL, pos.mangle_z);\n\t\n\tother = find (world, classname, \"player\");\n\twhile (other != world)\n\t{\n\t\tother.takedamage = DAMAGE_NO;\n\t\tother.solid = SOLID_NOT;\n\t\tother.movetype = MOVETYPE_NONE;\n\t\tother.modelindex = 0;\n\t\tother = find (other, classname, \"player\");\n\t}       \n\n};\n\n\nvoid() changelevel_touch =\n{\n\tif (other.classname != \"player\")\n\t\treturn;\n\n// if \"noexit\" is set, blow up the player trying to leave\n//ZOID, 12-13-96, noexit isn't supported in QW.  Overload samelevel\n//      if ((cvar(\"noexit\") == 1) || ((cvar(\"noexit\") == 2) && (mapname != \"start\")))\n\tif ((cvar(\"samelevel\") == 2) || ((cvar(\"samelevel\") == 3) && (mapname != \"start\")))\n\t{\n\t\tT_Damage (other, self, self, 50000);\n\t\treturn;\n\t}\n\n\tbprint (PRINT_HIGH, other.netname);\n\tbprint (PRINT_HIGH,\" exited the level\\n\");\n\t\n\tnextmap = self.map;\n\n\tSUB_UseTargets ();\n\n\tself.touch = SUB_Null;\n\n// we can't move people right now, because touch functions are called\n// in the middle of C movement code, so set a think time to do it\n\tself.think = execute_changelevel;\n\tself.nextthink = time + 0.1;\n};\n\n/*QUAKED trigger_changelevel (0.5 0.5 0.5) ? NO_INTERMISSION\nWhen the player touches this, he gets sent to the map listed in the \"map\" variable.  Unless the NO_INTERMISSION flag is set, the view will go to the info_intermission spot and display stats.\n*/\nvoid() trigger_changelevel =\n{\n\tif (!self.map)\n\t\tobjerror (\"chagnelevel trigger doesn't have map\");\n\t\n\tInitTrigger ();\n\tself.touch = changelevel_touch;\n};\n\n\n/*\n=============================================================================\n\n\t\t\t\tPLAYER GAME EDGE FUNCTIONS\n\n=============================================================================\n*/\n\nvoid() set_suicide_frame;\n\n// called by ClientKill and DeadThink\nvoid() respawn =\n{\n\t// make a copy of the dead body for appearances sake\n\tCopyToBodyQue (self);\n\t// set default spawn parms\n\tSetNewParms ();\n\t// respawn              \n\tPutClientInServer ();\n};\n\n\n/*\n============\nClientKill\n\nPlayer entered the suicide command\n============\n*/\nvoid() ClientKill =\n{\n\tbprint (PRINT_MEDIUM, self.netname);\n\tbprint (PRINT_MEDIUM, \" suicides\\n\");\n\tset_suicide_frame ();\n\tself.modelindex = modelindex_player;\n\tlogfrag (self, self);\n\tself.frags = self.frags - 2;    // extra penalty\n\trespawn ();\n};\n\nfloat(vector v) CheckSpawnPoint =\n{\n\treturn FALSE;\n};\n\n/*\n============\nSelectSpawnPoint\n\nReturns the entity to spawn at\n============\n*/\nentity() SelectSpawnPoint =\n{\n\tlocal   entity spot, thing;\n\tlocal   float   numspots, totalspots;\n\tlocal   float   pcount;\n\tlocal entity spots;\n\tlocal string ent1;\n\n\tnumspots = 0;\n\ttotalspots = 0;\n\n// testinfo_player_start is only found in regioned levels\n\n\tspot = find (world, classname, \"testplayerstart\");\n\tif (spot)\n\t\treturn spot;\n\t\n\tif (coop == 1)\n\t\tent1 = \"info_player_coop\";\n\telse\n\t\t\tent1 = \"spawn3\";\n\n\tif (ent1 == \"spawn3\")\n\t{\n\t\tif (self.team == 1)\n\t\t\tent1 = \"spawn1\";       \n\t\tif (self.team == 2)\n\t\t\tent1 = \"spawn2\";\n\t}    \n   \n// choose a info_player_deathmatch point\n\n// ok, find all spots that don't have players nearby\n\n\tspots = world;\n\tspot = find (world, classname, ent1);       \n\twhile (spot)\n\t{\n\t\ttotalspots = totalspots + 1;\n\n\t\tthing=findradius(spot.origin, 40);\n\t\tpcount=0;               \n\t\twhile (thing)\n\t\t{\n\t\t\tif (thing.classname == \"player\")\n\t\t\t\tpcount=pcount + 1;                      \n\t\t\tthing=thing.chain;      \n\t\t}\n\t\tif (pcount == 0)\n\t\t{\n\t\t\tspot.goalentity = spots;\n\t\t\tspots = spot;\n\t\t\tnumspots = numspots + 1;\n\t\t}\n\n\t\t// Get the next spot in the chain\n\t\tspot = find (spot, classname, ent1);                \n\t}\n\n\n\ttotalspots=totalspots - 1;\n\t\tif (!numspots)//full, find random spot\n\t\t{\n\t\t\ttotalspots = rint((random() * totalspots));\n\t\t\tspot = find (world, classname, ent1);       \n\t\t\twhile (totalspots > 0)\n\t\t\t{\n\t\t\t\ttotalspots = totalspots - 1;\n\t\t\t\tspot = find (spot, classname, ent1);\n\t\t\t}\n\t\treturn spot;\n\t}\n\t\t\n// We now have the number of spots available on the map in numspots\n\n\t// Generate a random number between 1 and numspots\n\n\tnumspots = numspots - 1;\n\t\n\tnumspots = rint((random() * numspots ) );\n\n\tspot = spots;\n\twhile (numspots > 0)\n\t{\n\t\tspot = spot.goalentity;\n\t\tnumspots = numspots - 1;\n\t}\n\treturn spot;\n\n};\n\n\nvoid() DecodeLevelParms;\nvoid() PlayerDie;\n\n/*\n===========\nPutClientInServer\n\ncalled each time a player enters a new level\n============\n*/\nvoid() PutClientInServer =\n{\n\tlocal entity spot;\n\tlocal float x;\n\tif (self.class == 0 || self.team == 0)\n\t{\n\t\tself.deadflag = DEAD_NO;\n\t\tself.health = 200;\n\t\tself.max_health = 200;\n\t\tsetsize (self, VEC_HULL_MIN, VEC_HULL_MAX);\n\t\tself.view_ofs = '0 0 22';\n\t\tself.takedamage = DAMAGE_NO;\n\t\tself.solid = SOLID_NOT;\n\t\tspot = SelectSpawnPoint ();\n\t\tself.origin = spot.origin + '0 0 1';\n\t\tself.angles = spot.angles;\n\t\tself.fixangle = TRUE;           // turn this way immediately\n\t\tself.movetype = MOVETYPE_WALK;\n\t\tself.flags = FL_CLIENT;\n\t\tself.classname = \"player\";\n\t\tself.currentmenu = \"none\";\n\t\tself.air_finished = time + 999;\n\t\tself.active = 1;\n\t\tplayer_stand1 ();\n\t\treturn;\n\t}\n\tself.classname = \"player\";\n\tself.health = 100;\n\tself.takedamage = DAMAGE_AIM;\n\tself.movetype = MOVETYPE_WALK;\n\tself.show_hostile = 0;\n\tself.max_health = 100;\n\tself.health = 100;\n\tself.flags = FL_CLIENT;\n\tself.air_finished = time + 12;\n\tself.dmg = 2;                   // initial water damage\n\tself.super_damage_finished = 0;\n\tself.radsuit_finished = 0;\n\tself.invisible_finished = 0;\n\tself.currentmenu = \"none\";\n\tself.invincible_finished = 0;\n\tself.effects = 0;\n\tself.invincible_time = 0;\n\tself.active = 1;\n\tself.solid = SOLID_NOT;\n\tself.materialize = 20;\n\n\tDecodeLevelParms ();\n\n\tif (self.current_slot == 0)\n\t{\n\t\tself.current_slot = 1;\n\t\tself.islot1 = SlotVal(IID_WP_USP, 12);\n\t\tself.islot2 = 0;\n\t\tself.islot3 = SlotVal(IID_ARM_SHIRT, 1);\n\t\tself.islot4 = SlotVal(IID_CHEM_STIMPACK, 3);\n\t\tself.islot5 = SlotVal(IID_AM_10MM, 24);\n\t\tself.islot6 = 0;\n\t\tself.islot7 = 0;\n\t\tself.islot8 = 0;\n\t\tself.islot9 = 0;\n\t\tself.islot10 = 0;\n\t\tself.islot11 = 0;\n\t\tself.islot12 = 0;\n\t\tself.islot13 = 0;\n\t\tself.islot14 = 0;\n\t\tself.islot15 = 0;\n\t\tself.islot16 = 0;\n\n//now give them special items for thier class.\n//you're fairly screwed if you change class.\n\t\tif (self.class == 1)\n\t\t{\n\t\t\tx = SlotOfItem(self, IID_CHEM_MEDICALBAG);\n\t\t\tif (x == 0)\n\t\t\t\tAddNonStackable(self, IID_CHEM_MEDICALBAG, 1);\n\t\t}\n\t\tif (self.class == 2)\n\t\t{\n\t\t\tx = SlotOfItem(self, IID_EQUIP_STEALTHBOY);\n\t\t\tif (x == 0)\n\t\t\t\tAddNonStackable(self, IID_EQUIP_STEALTHBOY, 1);\n\t\t}\n\t\tif (self.class == 4)\n\t\t{\n\t\t\tx = SlotOfItem(self, IID_WP_TOOLKIT);\n\t\t\tif (x == 0)\n\t\t\t\tAddNonStackable(self, IID_WP_TOOLKIT, 1);\n\t\t}\n\t}\n\n\tself.attack_finished = time;\n\tself.th_pain = player_pain;\n\tself.th_die = PlayerDie;\n\n\tself.deadflag = DEAD_NO;\n// paustime is set by teleporters to keep the player from moving a while\n\tself.pausetime = 0;\n\t\n\tspot = SelectSpawnPoint ();\n\n\tself.origin = spot.origin + '0 0 1';\n\tself.angles = spot.angles;\n\tself.fixangle = TRUE;           // turn this way immediately\n\n\tif (self.class == 1)\n\t\tself.max_health = 80;\n\tif (self.class == 2)\n\t\tself.max_health = 80;\n\tif (self.class == 3)\n\t\tself.max_health = 100;\n\tif (self.class == 4)\n\t\tself.max_health = 60;\n\tself.equipment_slot = 0;\n\n\tself.health = self.max_health;\n\n// oh, this is a hack!\n\tsetmodel (self, \"progs/eyes.mdl\");\n\tmodelindex_eyes = self.modelindex;\n\n\tsetmodel (self, \"progs/lay.mdl\");\n\tmodelindex_prone = self.modelindex;\n\n\tsetmodel (self, \"progs/sneak.mdl\");\n\tmodelindex_sneak = self.modelindex;\n\n\tsetmodel (self, \"progs/dead.mdl\");\n\tmodelindex_dead = self.modelindex;\n\n\tsetmodel (self, \"\");\n\tmodelindex_gone = self.modelindex;\n\n\tsetmodel (self, \"progs/guy.mdl\");\n\tmodelindex_player = self.modelindex;\n\n\tsetsize (self, VEC_HULL_MIN, VEC_HULL_MAX);\n\t\n\tself.view_ofs = '0 0 22';\n\tself.view2 = world;\n\n// Mod - Xian (May.20.97)\n// Bug where player would have velocity from their last kill\n\n\tself.velocity = '0 0 0';\n\n\tplayer_stand1 ();\n\t\n\tmakevectors(self.angles);\n\tspawn_tfog (self.origin + v_forward*20);\n\n\tspawn_tdeath (self.origin, self);\n\n\t// Set Rocket Jump Modifiers\n\tif (stof(infokey(world, \"rj\")) != 0)\n\t{                \n\t\trj = stof(infokey(world, \"rj\"));\n\t}\n\n\tif (deathmatch == 4)\n\t{\n\t\tself.ammo_shells = 0;\n\t\tif (stof(infokey(world, \"axe\")) == 0)\n\t\t{\n\t\t\tself.ammo_nails = 255;\n\t\t\tself.ammo_shells = 255;\n\t\t\tself.ammo_rockets = 255;\n\t\t\tself.ammo_cells = 255;\n\t\t\tself.items = self.items | IT_NAILGUN;\n\t\t\tself.items = self.items | IT_SUPER_NAILGUN;\n\t\t\tself.items = self.items | IT_SUPER_SHOTGUN;\n\t\t\tself.items = self.items | IT_ROCKET_LAUNCHER;\n//\t\tself.items = self.items | IT_GRENADE_LAUNCHER;\n\t\t\tself.items = self.items | IT_LIGHTNING;\n\t\t}\n\t\tself.items = self.items - (self.items & (IT_ARMOR1 | IT_ARMOR2 | IT_ARMOR3)) + IT_ARMOR3;\n\t\tself.armorvalue = 200;\n\t\tself.armortype = 0.8;\n\t\tself.health = 250;\n\t\tself.items = self.items | IT_INVULNERABILITY;\n\t\tself.invincible_time = 1;\n\t\tself.invincible_finished = time + 3;\n\t}\n\n\tif (deathmatch == 5)\n\t{\n\t\tself.ammo_nails = 80;\n\t\tself.ammo_shells = 30;\n\t\tself.ammo_rockets = 10;\n\t\tself.ammo_cells = 30;\n\t\tself.items = self.items | IT_NAILGUN;\n\t\tself.items = self.items | IT_SUPER_NAILGUN;\n\t\tself.items = self.items | IT_SUPER_SHOTGUN;\n\t\tself.items = self.items | IT_ROCKET_LAUNCHER;\n\t\tself.items = self.items | IT_GRENADE_LAUNCHER;\n\t\tself.items = self.items | IT_LIGHTNING;\n\t\tself.items = self.items - (self.items & (IT_ARMOR1 | IT_ARMOR2 | IT_ARMOR3)) + IT_ARMOR3;\n\t\tself.armorvalue = 200;\n\t\tself.armortype = 0.8;\n\t\tself.health = 200;\n\t\tself.items = self.items | IT_INVULNERABILITY;\n\t\tself.invincible_time = 1;\n\t\tself.invincible_finished = time + 3;\n\t}\n\n\tW_SetCurrentAmmo();\n};\n\n\n/*\n=============================================================================\n\n\t\t\t\tQUAKED FUNCTIONS\n\n=============================================================================\n*/\n\n\n/*QUAKED info_player_start (1 0 0) (-16 -16 -24) (16 16 24)\nThe normal starting point for a level.\n*/\nvoid() info_player_start =\n{\n};\n\n\n/*QUAKED info_player_start2 (1 0 0) (-16 -16 -24) (16 16 24)\nOnly used on start map for the return point from an episode.\n*/\nvoid() info_player_start2 =\n{\n/*\n\tlocal entity start;\n\tlocal float x;\n\n\tx = 10;\n\n\twhile (x > 0)\n\t{\n\t\tbprint(2, \"hi\\n\");\n\t\tstart = spawn();\n\t\tsetsize(start, '-16 -16 -24', '16 16 32');\n\t\tsetorigin(start, self.origin + '0 0 4');\n\t\tstart.solid = SOLID_BBOX;\n\t\tstart.health = 0;\n\t\tstart.movetype = MOVETYPE_BOUNCE;\n\t\tstart.velocity = '500 100 9000';\n\t\tsetmodel (start, \"progs/guy.mdl\");\n\t\tstart.avelocity = '3000 1000 2000';\n\t\tstart.frame = 1;\n\t\t//start.think = become_startpoint;\n\t\t//start.nextthink = time + 2;\n\n\t\tx = x - 1;\n\t}\n*/\n\n};\n\n/*QUAKED info_player_deathmatch (1 0 1) (-16 -16 -24) (16 16 24)\npotential spawning position for deathmatch games\n*/\nvoid() info_player_deathmatch =\n{\n};\n\n/*QUAKED info_player_coop (1 0 1) (-16 -16 -24) (16 16 24)\npotential spawning position for coop games\n*/\nvoid() info_player_coop =\n{\n};\n\n/*\n===============================================================================\n\nRULES\n\n===============================================================================\n*/\n\n/*\ngo to the next level for deathmatch\n*/\nvoid() NextLevel =\n{\n\tlocal entity o;\n\n\tif (nextmap != \"\")\n\t\treturn; // already done\n\n\tif (mapname == \"start\")\n\t{\n\t\tif (!cvar(\"registered\"))\n\t\t{\n\t\t\tmapname = \"e1m1\";\n\t\t}\n\t\telse if (!(serverflags & 1))\n\t\t{\n\t\t\tmapname = \"e1m1\";\n\t\t\tserverflags = serverflags | 1;\n\t\t}\n\t\telse if (!(serverflags & 2))\n\t\t{\n\t\t\tmapname = \"e2m1\";\n\t\t\tserverflags = serverflags | 2;\n\t\t}\n\t\telse if (!(serverflags & 4))\n\t\t{\n\t\t\tmapname = \"e3m1\";\n\t\t\tserverflags = serverflags | 4;\n\t\t}\n\t\telse if (!(serverflags & 8))\n\t\t{\n\t\t\tmapname = \"e4m1\";\n\t\t\tserverflags = serverflags - 7;\n\t\t}\n \n\t\to = spawn();\n\t\to.map = mapname;\n\t}\n\telse\n\t{\n\t\t// find a trigger changelevel\n\t\to = find(world, classname, \"trigger_changelevel\");\n\t\tif (!o || mapname == \"start\")\n\t\t{       // go back to same map if no trigger_changelevel\n\t\t\to = spawn();\n\t\t\to.map = mapname;\n\t\t}\n\t}\n\n\tnextmap = o.map;\n\n\tif (o.nextthink < time)\n\t{\n\t\to.think = execute_changelevel;\n\t\to.nextthink = time + 0.1;\n\t}\n};\n\n/*\n============\nCheckRules\n\nExit deathmatch games upon conditions\n============\n*/\nvoid() CheckRules =\n{       \n\tif (timelimit && time >= timelimit)\n\t\tNextLevel ();\n\t\n\tif (fraglimit && self.frags >= fraglimit)\n\t\tNextLevel ();\n};\n\n\nvoid () LocateSpectatorTarget =\n{\n\tlocal float playercnt;\n\tlocal float total;\n\tlocal float player;\n\tlocal entity ke;\n\tlocal entity de;\n\n\tif (self.button0)\n\t{\n\t\tbprint(PRINT_MEDIUM, self.classname);\n\t\tbprint(PRINT_MEDIUM, \" is dying...\\n\");\n\t\treturn;\n\t}\n\n\tif (self.ghost == 1)\n\t{\n\t\tke = find (world, classname, \"player\");\n\t\tde = ke;\n\t\ttotal = -1;\n\t\tplayer = 0;\n\t\tplayercnt = 0;\n\t\twhile (ke)\n\t\t{\n\t\t\tif (ke.classname == \"player\" && ke.ghost == 0 && ke.health > 0)\n\t\t\t{\n\t\t\t\ttotal = total + 1;\n\t\t\t}\n\t\t\tke = find (ke, classname, \"player\");\n\t\t}\n\t\twhile (de)\n\t\t{\n\t\t\tif (de.classname == \"player\" && de.ghost == 0 && de.health > 0)\n\t\t\t{\n\t\t\t\tif (player == self.ghostcnt)\n\t\t\t\t{\n\t\t\t\t\tmakevectors (de.angles);\n\t\t\t\t\tself.view2 = de;\n\t\t\t\t\tself.ghostcnt = self.ghostcnt + 1;\n\t\t\t\t\tself.cnt = 0;\n\t\t\t\t\tsprint(self, 2, \"now following \");\n\t\t\t\t\tsprint(self, 2, de.netname);\n\t\t\t\t\tsprint(self, 2, \".\\n\");\n\n\t\t\t\t\tif (self.ghostcnt > total)\n\t\t\t\t\t\tself.ghostcnt = 0;\n\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tplayer = player + 1;\n\t\t\t\tif (self.ghostcnt > total)\n\t\t\t\t{\n\t\t\t\t\tself.ghostcnt = 0;\n\t\t\t\t}\n\t\t\t}\n\t\t\tde = find (de, classname, \"player\");\n\t\t}\n\t\tif (player == 0)\n\t\t{\n\t\t\tself.view2 = world;\n\t\t\tcenterprint (self, \"No players found...\\n\");\n\t\t\tif (!deathmatch)\n\t\t\t\tlocalcmd(\"restart\\n\");\n\t\t\treturn;\n\t\t}\n\t}\n};\n\n//============================================================================\n\nvoid() PlayerDeathThink =\n{\n\tlocal float             forward;\n\n\tself.view_ofs = '0 0 -12';\n\tself.modelindex = modelindex_dead;\n\tself.solid = SOLID_NOT;\n\n\tif ((self.flags & FL_ONGROUND))\n\t{\n\t\tforward = vlen (self.velocity);\n\t\tforward = forward - 20;\n\t\tif (forward <= 0)\n\t\t\tself.velocity = '0 0 0';\n\t\telse    \n\t\t\tself.velocity = forward * normalize(self.velocity);\n\t}\n\n// wait for all buttons released\n\tif (self.deadflag == DEAD_DEAD)\n\t{\n\t\tif (self.button2 || self.button1 || self.button0)\n\t\t\treturn;\n\t\tself.deadflag = DEAD_RESPAWNABLE;\n\t\treturn;\n\t}\n\n// wait for any button down\n\tif (!self.button2 && !self.button1 && !self.button0)\n\t\treturn;\n\n\tif (self.dtime < time)\n\t{\n\t\tLocateSpectatorTarget();\n\t\tself.dtime = time + 1;\n\t}\n\n\tself.button0 = 0;\n\tself.button1 = 0;\n\tself.button2 = 0;\n\t//respawn();\n};\n\n\nvoid() PlayerJump =\n{\n\tif (self.flags & FL_WATERJUMP)\n\t\treturn;\n\t\n\tif (self.waterlevel >= 2)\n\t{\n// play swiming sound\n\t\tif (self.swim_flag < time)\n\t\t{\n\t\t\tself.swim_flag = time + 1;\n\t\t\tif (random() < 0.5)\n\t\t\t\tsound (self, CHAN_BODY, \"misc/water1.wav\", 1, ATTN_NORM);\n\t\t\telse\n\t\t\t\tsound (self, CHAN_BODY, \"misc/water2.wav\", 1, ATTN_NORM);\n\t\t}\n\n\t\treturn;\n\t}\n\n\tif (!(self.flags & FL_ONGROUND))\n\t\treturn;\n\n\tif ( !(self.flags & FL_JUMPRELEASED) )\n\t\treturn;         // don't pogo stick\n\n\tself.flags = self.flags - (self.flags & FL_JUMPRELEASED);       \n\tself.button2 = 0;\n\n// player jumping sound\n\tsound (self, CHAN_BODY, \"player/ax1.wav\", 1, ATTN_NORM);\n};\n\n\n/*\n===========\nWaterMove\n\n============\n*/\n.float  dmgtime;\n\nvoid() WaterMove =\n{\n//dprint (ftos(self.waterlevel));\n\tif (self.movetype == MOVETYPE_NOCLIP)\n\t\treturn;\n\tif (self.health < 0)\n\t\treturn;\n\n\tif (self.waterlevel != 3)\n\t{\n\t\tif (self.air_finished < time)\n\t\t\tsound (self, CHAN_VOICE, \"player/gasp2.wav\", 1, ATTN_NORM);\n\t\telse if (self.air_finished < time + 9)\n\t\t\tsound (self, CHAN_VOICE, \"player/gasp1.wav\", 1, ATTN_NORM);\n\t\tself.air_finished = time + 12;\n\t\tself.dmg = 2;\n\t}\n\telse if (self.air_finished < time)\n\t{       // drown!\n\t\tif (self.pain_finished < time)\n\t\t{\n\t\t\tself.dmg = self.dmg + 2;\n\t\t\tif (self.dmg > 15)\n\t\t\t\tself.dmg = 10;\n\t\t\tT_Damage (self, world, world, self.dmg);\n\t\t\tself.pain_finished = time + 1;\n\t\t}\n\t}\n\t\n\tif (!self.waterlevel)\n\t{\n\t\tif (self.flags & FL_INWATER)\n\t\t{       \n\t\t\t// play leave water sound\n\t\t\tsound (self, CHAN_BODY, \"misc/outwater.wav\", 1, ATTN_NORM);\n\t\t\tself.flags = self.flags - FL_INWATER;\n\t\t}\n\t\treturn;\n\t}\n\n\tif (self.watertype == CONTENT_LAVA)\n\t{       // do damage\n\t\tif (self.dmgtime < time)\n\t\t{\n\t\t\tif (self.radsuit_finished > time)\n\t\t\t\tself.dmgtime = time + 1;\n\t\t\telse\n\t\t\t\tself.dmgtime = time + 0.2;\n\n\t\t\tT_Damage (self, world, world, 10*self.waterlevel);\n\t\t}\n\t}\n\telse if (self.watertype == CONTENT_SLIME)\n\t{       // do damage\n\t\tif (self.dmgtime < time && self.radsuit_finished < time)\n\t\t{\n\t\t\tself.dmgtime = time + 1;\n\t\t\tT_Damage (self, world, world, 4*self.waterlevel);\n\t\t}\n\t}\n\t\n\tif ( !(self.flags & FL_INWATER) )\n\t{       \n\n// player enter water sound\n\n\t\tif (self.watertype == CONTENT_LAVA)\n\t\t\tsound (self, CHAN_BODY, \"player/inlava.wav\", 1, ATTN_NORM);\n\t\tif (self.watertype == CONTENT_WATER)\n\t\t\tsound (self, CHAN_BODY, \"player/inh2o.wav\", 1, ATTN_NORM);\n\t\tif (self.watertype == CONTENT_SLIME)\n\t\t\tsound (self, CHAN_BODY, \"player/slimbrn2.wav\", 1, ATTN_NORM);\n\n\t\tself.flags = self.flags + FL_INWATER;\n\t\tself.dmgtime = 0;\n\t}       \n};\n\nvoid() CheckWaterJump =\n{\n\tlocal vector start, end;\n\n// check for a jump-out-of-water\n\tmakevectors (self.angles);\n\tstart = self.origin;\n\tstart_z = start_z + 8; \n\tv_forward_z = 0;\n\tnormalize(v_forward);\n\tend = start + v_forward*24;\n\ttraceline (start, end, TRUE, self);\n\tif (trace_fraction < 1)\n\t{       // solid at waist\n\t\tstart_z = start_z + self.maxs_z - 8;\n\t\tend = start + v_forward*24;\n\t\tself.movedir = trace_plane_normal * -50;\n\t\ttraceline (start, end, TRUE, self);\n\t\tif (trace_fraction == 1)\n\t\t{       // open at eye level\n\t\t\tself.flags = self.flags | FL_WATERJUMP;\n\t\t\tself.velocity_z = 225;\n\t\t\tself.flags = self.flags - (self.flags & FL_JUMPRELEASED);\n\t\t\tself.teleport_time = time + 2;  // safety net\n\t\t\treturn;\n\t\t}\n\t}\n};\n\nvoid() ArmorCheck =\n{\n\tlocal float type;\n\n\tlocal float aid;\n\n\t//Armor\n\taid = ToIID(self.islot3);\n\tif (aid == IID_ARM_SHIRT)\n\t\ttype = 0.20;\n\tif (aid == IID_ARM_LEATHER)\n\t\ttype = 0.30;\n\tif (aid == IID_ARM_KEVLAR)\n\t\ttype = 0.35;\n\tif (aid == IID_ARM_METAL)\n\t\ttype = 0.35;\n\tif (aid == IID_ARM_COMBAT)\n\t\ttype = 0.40;\n\tif (aid == IID_ARM_BROTHERHOOD)\n\t\ttype = 0.45;\n\tif (aid == IID_ARM_FORCE)\n\t\ttype = 0.10;\n\tif (aid == IID_ARM_LPOWER)\n\t\ttype = 0.50;\n\n\tif (aid <= IID_ARM_KEVLAR)\n\t\tself.items = (self.items - (self.items & ((IT_ARMOR1 | IT_ARMOR2) | IT_ARMOR3)) + IT_ARMOR1);\n\telse if (aid <= IID_ARM_BROTHERHOOD)\n\t\tself.items = (self.items - (self.items & ((IT_ARMOR1 | IT_ARMOR2) | IT_ARMOR3)) + IT_ARMOR1);\n\telse\n\t\tself.items = (self.items - (self.items & ((IT_ARMOR1 | IT_ARMOR2) | IT_ARMOR3)) + IT_ARMOR1);\n\n\tif (aid == 1)\n\t\tself.armornoise = \"misc/thud.wav\";\n\tif (aid == 2)\n\t\tself.armornoise = \"misc/thud.wav\";\n\tif (aid == 3)\n\t\tself.armornoise = \"misc/thud.wav\";\n\tif (aid == 4)\n\t\tself.armornoise = \"weapons/ric1.wav\";\n\tif (aid == 5)\n\t\tself.armornoise = \"misc/thud.wav\";\n\tif (aid == 6)\n\t\tself.armornoise = \"misc/thud.wav\";\n\tif (aid == 7)\n\t\tself.armornoise = \"misc/laserdef.wav\";\n\tif (aid == 8)\n\t\tself.armornoise = \"weapons/ric1.wav\";\n\n\tself.armorvalue = type * 100;\n\tself.armortype = type;\n};\n\nvoid () WeightControl =\n{\n\t//for each inventory item, add up it's weight.\n\tlocal float wt;\n\twt = wt + GetItemsWeight(self.islot1);\n\twt = wt + GetItemsWeight(self.islot2);\n\twt = wt + GetItemsWeight(self.islot3);\n\twt = wt + GetItemsWeight(self.islot4);\n\twt = wt + GetItemsWeight(self.islot5);\n\twt = wt + GetItemsWeight(self.islot6);\n\twt = wt + GetItemsWeight(self.islot7);\n\twt = wt + GetItemsWeight(self.islot8);\n\twt = wt + GetItemsWeight(self.islot9);\n\twt = wt + GetItemsWeight(self.islot10);\n\twt = wt + GetItemsWeight(self.islot11);\n\twt = wt + GetItemsWeight(self.islot12);\n\twt = wt + GetItemsWeight(self.islot13);\n\twt = wt + GetItemsWeight(self.islot14);\n\twt = wt + GetItemsWeight(self.islot15);\n\twt = wt + GetItemsWeight(self.islot16);\n\n\tself.weight = wt;\n\n\tif (self.class == 1)\n\t\tself.max_weight = 50;\n\tif (self.class == 2)\n\t\tself.max_weight = 50;\n\tif (self.class == 3)\n\t\tself.max_weight = 60;\n\tif (self.class == 4)\n\t\tself.max_weight = 40;\n};\n\n\nvoid () PositionControl =\n{\n\tlocal float exp;\n\n\tif (self.position == 0)\n\t\tself.view_ofs = '0 0 22';\n\tif (self.position == 1)\n\t\tself.view_ofs = '0 0 5';\n\tif (self.position == 2)\n\t\tself.view_ofs = '0 0 -10';\n\n\tif (self.position == 0)\n\t\tsetsize (self, '-16 -16 -24', '16 16 32');\n\tif (self.position == 1)\n\t\tsetsize (self, '-16 -16 -24', '16 16 16');\n\tif (self.position == 2)\n\t\tsetsize (self, '-16 -16 -24', '16 16 0');\n\n\n\tif (self.currentmenu != \"none\")\n\t{\n\t\tself.maxspeed = 0;\n\t\treturn;\n\t}\n\n\tif (self.position == 0)\n\t\tself.maxspeed = 200;\n\telse if (self.position == 1)\n\t\tself.maxspeed = 100;\n\telse //if (self.position == 2)\n\t\tself.maxspeed = 50;\n\n\tif (self.weight > self.max_weight) //this is the penalty for being overweight.\n\t{\n\t\texp = (self.weight/self.max_weight);\n\t\texp = (exp+2)*(exp+2)+(exp*exp*exp*8);\t//is this enough?\n\t\tself.maxspeed = self.maxspeed / exp;\n\t}\n\n\tif (self.equipment_slot)\n\t{\n\t\tif ((ToIID(ItemInSlot(self, self.equipment_slot))) == IID_EQUIP_SPRINTKIT)\n\t\t\tself.maxspeed = self.maxspeed*2;\n\t\tif ((ToIID(ItemInSlot(self, self.equipment_slot))) == IID_EQUIP_STEALTHBOY)\n\t\t\tself.maxspeed = self.maxspeed/2;\n\t}\n};\n\n.float clientcolors;\n.float gravity;\n.vector movement;\n/*\n================\nPlayerPreThink\n\nCalled every frame before physics are run\n================\n*/\nvoid() PlayerPreThink =\n{\n\tlocal   float   r;\n\n\tif (intermission_running)\n\t{\n\t\tIntermissionThink ();   // otherwise a button could be missed between\n\t\treturn;                                 // the think tics\n\t}\n\n\tif (self.view_ofs == '0 0 0')\n\t\treturn;         // intermission or finale\n\n\tmakevectors (self.v_angle);             // is this still used\n\n\tif (self.deadflag >= DEAD_DEAD)\n\t{\n\t\tPlayerDeathThink ();\n\t\treturn;\n\t}\n\n\n\tif (self.team == 0)\n\t\tself.clientcolors = 0;\n\telse if (self.team == 1)\n\t\tself.clientcolors = 13 + 13*16;\n\telse\n\t\tself.clientcolors = 4 + 4*16;\n\tif (self.team == 0 && self.currentmenu == \"none\")\n\t{\n\t\tif (coop)\n\t\t\tself.team = 1;\n\t\telse\n\t\t{\n\t\t\tself.currentmenu = \"select_team\";\n\t\t\tDisplayMenu ();\n\t\t}\n\t}\n\telse if (self.class == 0 && self.currentmenu == \"none\")\n\t{\n\t\tself.currentmenu = \"select_skill\";\n\t\tDisplayMenu ();\n\t}\n\n\n\n\n\n\n\tif (self.cycle1 < time)\n\t{\n\t\tif (self.currentmenu != \"none\")\n\t\t\tDisplayMenu();\n\n\t\t//if (class == 0 || self.team == 0)\n\t\t//\treturn;\n\n\t\tIdentify();\n\t\tPositionControl();\n\t\tWeightControl();\n\t\tArmorCheck();\n\t\tCrosshair();\n\n\t\tif (self.health > self.max_health)\n\t\t\tself.health = self.max_health;\n\n\n\t\tif (self.flash < time)\n\t\t{\n\t\t\tif (self.ragetime > time)\n\t\t\t\tstuffcmd(self, \"v_cshift 75 0 0 75\\n\");\n\t\t\telse if (self.sneak > 0)\n\t\t\t\tstuffcmd(self, \"v_cshift 0 75 0 75\\n\");\n\t\t\telse\n\t\t\t\tstuffcmd(self, \"v_cshift 0 0 0 0\\n\");\n\t\t}\n\t\tself.cycle1 = time + 0.5;\n\t}\n\n\tif (self.materialize > 0)\n\t{\n\t\tself.materialize = self.materialize - 1;\n\n\t\tif (self.materialize <= 0)\n\t\t{\n\t\t\tif (walkmove(0, 0))\n\t\t\t{\n\t\t\t\tself.solid = SOLID_SLIDEBOX;\n\t\t\t\t//bprint(2, \"turning solid\\n\");\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tself.materialize = 50;\n\t\t\t\t//bprint(2, \"stuck in object, returning non-solid\\n\");\n\t\t\t}\n\t\t}\n\t}\n\n\tif (self.class == 1)\n\t\tself.recoil = self.recoil - 0.16;\n\tif (self.class == 2)\n\t\tself.recoil = self.recoil - 0.14;\n\tif (self.class == 3)\n\t\tself.recoil = self.recoil - 0.12;\n\tif (self.class == 4)\n\t\tself.recoil = self.recoil - 0.19;\n\n\tif (self.recoil <= 0)\n\t\tself.recoil = 0;\n\n      self.deathtype = \"\";\n\n\n\n\tif (self.cycle2 < time)\n\t{\n\t\tif (self.equipment == 8)\n\t\t\tr = 30;\n\t\telse\n\t\t\tr = 20;\n\n\t\tif (self.sneak == 0)\n\t\t{\n\t\t\tif (self.ammo_cells < r)\n\t\t\t\tself.ammo_cells = self.ammo_cells + 1;\n\n\t\t\tif (self.ammo_cells > r)\n\t\t\t\tself.ammo_cells = r;\n\t\t}\n\t\tif (self.sneak >= 1)\n\t\t{\n\t\t\tself.ammo_cells = self.ammo_cells - 1;\n\n\t\t\tif (self.ammo_cells <= 0)\n\t\t\t{\n\t\t\t\tsprint(self, 2, \"uncloaked.\\n\");\n\t\t\t\tself.sneak = 0;\n\t\t\t}\n\t\t}\n\n\t\tif (self.regen > 0 && !self.deadflag)\t//don't come back to life.\n\t\t{\n\t\t\tself.health = self.health + 3;\n\t\t\tself.regen = self.regen - 1;\n\t\t}\n\n\t\tif (self.flash < time)\n\t\t{\n\t\t\tstuffcmd(self, \"v_idlescale 0\\n\");\n\t\t}\n\t\tself.cycle2 = time + 1;\n\t}\n\n\n\tCheckRules ();\n\tWaterMove ();\n\n\n\n\tif (self.deadflag == DEAD_DYING)\n\t\treturn; // dying, so do nothing\n\n\tif (self.button2)\n\t{\n\t\tPlayerJump ();\n\t}\n\telse\n\t\tself.flags = self.flags | FL_JUMPRELEASED;\n\n// teleporters can force a non-moving pause time        \n\tif (time < self.pausetime)\n\t\tself.velocity = '0 0 0';\n\n\tif (!self.gravity)\n\t{\t//climbing gear\n\t\tif (self.velocity != '0 0 0' || self.button2)\n\t\t\tself.gravity = 1;\n\t}\n\tif (self.equipment_slot)\n\t{\n\t\tif ((ToIID(ItemInSlot(self, self.equipment_slot))) == IID_EQUIP_CLIMBINGGEAR)\n\t\t{\n\t\t\ttraceline ((self.origin - '0 0 24'), ((self.origin + (v_forward * EF_FLAG2)) - '0 0 24'), FALSE, self);\n\t\t\tif (trace_fraction < 1 && trace_fraction > 0)\n\t\t\t{\n\t\t\t\tself.gravity = 0;\n\t\t\t\tself.maxspeed = 50;\n\t\t\t\tself.velocity = v_forward*40 * self.movement_x;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tself.gravity = 1;\n\t\t\t}\n\t\t\tself.attack_finished = time + 0.1;\n\t\t}\n\t\tif ((ToIID(ItemInSlot(self, self.equipment_slot))) == IID_EQUIP_HOVERBOOTS && self.button2)\n\t\t{\n\t\t\tif (self.attack_finished < time)\n\t\t\t{\n\t\t\t\tself.velocity = self.velocity + v_up*800*0.4;\n\t\t\t\tself.attack_finished = time + 0.4;\n\t\t\t}\n\t\t}\n\t}\n};\n\t\n/*\n================\nCheckPowerups\n\nCheck for turning off powerups\n================\n*/\nvoid() CheckPowerups =\n{\n\tif (self.health <= 0)\n\t\treturn;\n\n// invisibility\n\tif (self.equipment_slot)\n\t\tif ((ToIID(ItemInSlot(self, self.equipment_slot))) == IID_EQUIP_STEALTHBOY)\n\t\t\tgoto beinvis;\n\n\tif (self.invisible_finished)\n\t{\n// sound and screen flash when items starts to run out\n\t\tif (self.invisible_sound < time)\n\t\t{\n\t\t\tsound (self, CHAN_AUTO, \"items/inv3.wav\", 0.5, ATTN_IDLE);\n\t\t\tself.invisible_sound = time + ((random() * 3) + 1);\n\t\t}\n\n\n\t\tif (self.invisible_finished < time + 3)\n\t\t{\n\t\t\tif (self.invisible_time == 1)\n\t\t\t{\n\t\t\t\tsprint (self, PRINT_HIGH, \"Ring of Shadows magic is fading\\n\");\n\t\t\t\tstuffcmd (self, \"bf\\n\");\n\t\t\t\tsound (self, CHAN_AUTO, \"items/inv2.wav\", 1, ATTN_NORM);\n\t\t\t\tself.invisible_time = time + 1;\n\t\t\t}\n\t\t\t\n\t\t\tif (self.invisible_time < time)\n\t\t\t{\n\t\t\t\tself.invisible_time = time + 1;\n\t\t\t\tstuffcmd (self, \"bf\\n\");\n\t\t\t}\n\t\t}\n\n\t\tif (self.invisible_finished < time)\n\t\t{       // just stopped\n\t\t\tself.invisible_finished = 0;\n\t\t\tself.invisible_time = 0;\n\t\t}\n\t\t\n:beinvis\n\t\tself.items = self.items | IT_INVISIBILITY;\n\t// use the eyes\n\t\tself.frame = 0;\n\t\tself.modelindex = modelindex_eyes;\n\t}\n\telse\n\t{\n\t\tself.items = self.items - (self.items & IT_INVISIBILITY);\n\t\tif (self.position <= 1)\n\t\t\tself.modelindex = modelindex_player;\n\t\tif (self.position == 2)\n\t\t\tself.modelindex = modelindex_prone;\n\t\tif (self.sneak == 1 || self.sneak == 3 && coop == 0)\n\t\t\tself.modelindex = modelindex_gone;\n\t\tif (self.sneak == 1 || self.sneak == 3 && coop == 1)\n\t\t\tself.modelindex = modelindex_sneak;\n\t\tif (self.health <= 0)\n\t\t\tself.modelindex = modelindex_dead;\n\t\tif (self.ghost == 1 || self.class == 0 || self.team == 0)\n\t\t\tself.modelindex = modelindex_gone;\n\t}\n\n\n// invincibility\n\tif (self.invincible_finished)\n\t{\n// sound and screen flash when items starts to run out\n\t\tif (self.invincible_finished < time + 3)\n\t\t{\n\t\t\tif (self.invincible_time == 1)\n\t\t\t{\n\t\t\t\tsprint (self, PRINT_HIGH, \"Protection is almost burned out\\n\");\n\t\t\t\tstuffcmd (self, \"bf\\n\");\n\t\t\t\tsound (self, CHAN_AUTO, \"items/protect2.wav\", 1, ATTN_NORM);\n\t\t\t\tself.invincible_time = time + 1;\n\t\t\t}\n\t\t\t\n\t\t\tif (self.invincible_time < time)\n\t\t\t{\n\t\t\t\tself.invincible_time = time + 1;\n\t\t\t\tstuffcmd (self, \"bf\\n\");\n\t\t\t}\n\t\t}\n\t\t\n\t\tif (self.invincible_finished < time)\n\t\t{       // just stopped\n\t\t\tself.items = self.items - IT_INVULNERABILITY;\n\t\t\tself.invincible_time = 0;\n\t\t\tself.invincible_finished = 0;\n\t\t}\n\t\tif (self.invincible_finished > time)\n\t\t{\n\t\t\tself.effects = self.effects | EF_DIMLIGHT;\n\t\t\tself.effects = self.effects | EF_RED;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tself.effects = self.effects - (self.effects & EF_DIMLIGHT);\n\t\t\tself.effects = self.effects - (self.effects & EF_RED);\n\t\t}\n\t}\n\n// super damage\n\tif (self.super_damage_finished)\n\t{\n\n// sound and screen flash when items starts to run out\n\n\t\tif (self.super_damage_finished < time + 3)\n\t\t{\n\t\t\tif (self.super_time == 1)\n\t\t\t{\n\t\t\t\tif (deathmatch == 4)\n\t\t\t\t\tsprint (self, PRINT_HIGH, \"OctaPower is wearing off\\n\");\n\t\t\t\telse\n\t\t\t\t\tsprint (self, PRINT_HIGH, \"Quad Damage is wearing off\\n\");\n\t\t\t\tstuffcmd (self, \"bf\\n\");\n\t\t\t\tsound (self, CHAN_AUTO, \"items/damage2.wav\", 1, ATTN_NORM);\n\t\t\t\tself.super_time = time + 1;\n\t\t\t}         \n\t\t\t\n\t\t\tif (self.super_time < time)\n\t\t\t{\n\t\t\t\tself.super_time = time + 1;\n\t\t\t\tstuffcmd (self, \"bf\\n\");\n\t\t\t}\n\t\t}\n\n\t\tif (self.super_damage_finished < time)\n\t\t{       // just stopped\n\t\t\tself.items = self.items - IT_QUAD;\n\t\t\tif (deathmatch == 4)\n\t\t\t{\n\t\t\t\tself.ammo_cells = 255;\n\t\t\t\tself.armorvalue = 1;\n\t\t\t\tself.armortype = 0.8;\n\t\t\t\tself.health = 100;\n\t\t\t}\n\t\t\tself.super_damage_finished = 0;\n\t\t\tself.super_time = 0;\n\t\t}\n\t\tif (self.super_damage_finished > time)\n\t\t{\n\t\t\tself.effects = self.effects | EF_DIMLIGHT;\n\t\t\tself.effects = self.effects | EF_BLUE;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tself.effects = self.effects - (self.effects & EF_DIMLIGHT);\n\t\t\tself.effects = self.effects - (self.effects & EF_BLUE);\n\t\t}\n\t}       \n\n// suit \n\tif (self.radsuit_finished)\n\t{\n\t\tself.air_finished = time + 12;          // don't drown\n\n// sound and screen flash when items starts to run out\n\t\tif (self.radsuit_finished < time + 3)\n\t\t{\n\t\t\tif (self.rad_time == 1)\n\t\t\t{\n\t\t\t\tsprint (self, PRINT_HIGH, \"Air supply in Biosuit expiring\\n\");\n\t\t\t\tstuffcmd (self, \"bf\\n\");\n\t\t\t\tsound (self, CHAN_AUTO, \"items/suit2.wav\", 1, ATTN_NORM);\n\t\t\t\tself.rad_time = time + 1;\n\t\t\t}\n\t\t\t\n\t\t\tif (self.rad_time < time)\n\t\t\t{\n\t\t\t\tself.rad_time = time + 1;\n\t\t\t\tstuffcmd (self, \"bf\\n\");\n\t\t\t}\n\t\t}\n\n\t\tif (self.radsuit_finished < time)\n\t\t{       // just stopped\n\t\t\tself.items = self.items - IT_SUIT;\n\t\t\tself.rad_time = 0;\n\t\t\tself.radsuit_finished = 0;\n\t\t}\n\t}       \n\n};\n\n\nvoid() Footstep;\n\n/*\n================\nPlayerPostThink\n\nCalled every frame after physics are run\n================\n*/\nvoid() PlayerPostThink =\n{\n//dprint (\"post think\\n\");\n\tif (self.view_ofs == '0 0 0')\n\t\treturn;         // intermission or finale\n\tif (self.deadflag)\n\t\treturn;\n\n// check to see if player landed and play landing sound \n\tif ((self.jump_flag < -50) && (self.flags & FL_ONGROUND) )\n\t{\n\t\tif (self.watertype == CONTENT_WATER)\n\t\t\tsound (self, CHAN_BODY, \"player/h2ojump.wav\", 1, ATTN_NORM);\n\t\telse if (self.jump_flag < -650)\n\t\t{\n\t\t\tself.deathtype = \"falling\";\n\t\t\tT_Damage (self, world, world, 5); \n\t\t\tsound (self, CHAN_VOICE, \"player/land2.wav\", 1, ATTN_NORM);\n\t\t}\n\t\telse\n\t\t\tFootstep();\n\t}\n\n\tself.jump_flag = self.velocity_z;\n\n\tif (self.rtime > time)\n\t\tself.weaponframe = 3;\n\n\tCheckPowerups ();\n\n\tW_WeaponFrame ();\n\n};\n\n\n/*\n===========\nClientConnect\n\ncalled when a player connects to a server\n============\n*/\nvoid() ClientConnect =\n{\n\tbprint (PRINT_HIGH, self.netname);\n\tbprint (PRINT_HIGH, \" entered the game\\n\");\n\t\n\n\tstuffcmd(self, \"v_damagecshift 1\\n\");\n\tstuffcmd(self, \"alias duck impulse 200\\n\");\n\tstuffcmd(self, \"alias prone impulse 201\\n\");\n\tstuffcmd(self, \"alias reload impulse 50\\n\");\n\tstuffcmd(self, \"alias chem impulse 51\\n\");\n\tstuffcmd(self, \"alias buy impulse 52\\n\");\n\tstuffcmd(self, \"alias special impulse 53\\n\");\n\tstuffcmd(self, \"alias exit impulse 54\\n\");\n\tstuffcmd(self, \"alias drop impulse 55\\n\");\n\tstuffcmd(self, \"alias info impulse 56\\n\");\n\tstuffcmd(self, \"alias equip impulse 57\\n\");\n\n\tstuffcmd(self, \"alias stimpack \\\"cmd invuse superstims+stimpack+bandages\\\"\\n\");\n\tstuffcmd(self, \"alias bandages \\\"cmd invuse bandages+superstims+stimpack\\\"\\n\");\n\n\tstuffcmd(self, \"alias toolkit \\\"cmd invswap toolkit 2;impulse 2\\\"\\n\");\n\n\tstuffcmd(self, \"bind c equip\\n\");\n\tstuffcmd(self, \"bind g drop\\n\");\n\tstuffcmd(self, \"bind e exit\\n\");\n\tstuffcmd(self, \"bind z special\\n\");\n\tstuffcmd(self, \"bind r reload\\n\");\n\tstuffcmd(self, \"bind x stimpack\\n\");\n\tstuffcmd(self, \"bind b buy\\n\");\n\tstuffcmd(self, \"bind q info\\n\");\n\tstuffcmd(self, \"bind shift prone\\n\");\n\tstuffcmd(self, \"bind ctrl duck\\n\");\n\n\tstuffcmd(self, \"exec fallout.cfg\\n\");\n\n// a client connecting during an intermission can cause problems\n\tif (intermission_running)\n\t\tGotoNextMap ();\n};\n\n\n/*\n===========\nClientDisconnect\n\ncalled when a player disconnects from a server\n============\n*/\nvoid() ClientDisconnect =\n{\n\t// let everyone else know\n\tbprint (PRINT_HIGH, self.netname);\n\t\tbprint (PRINT_HIGH, \" left the game with \");\n\t\tbprint (PRINT_HIGH, ftos(self.frags));\n\t\tbprint (PRINT_HIGH, \" frags\\n\");\n\tsound (self, CHAN_BODY, \"player/tornoff2.wav\", 1, ATTN_NONE);\n\tset_suicide_frame ();\n};\n\n/*\n===========\nClientObituary\n\ncalled when a player dies\n============\n*/\n\nvoid(entity targ, entity attacker) ClientObituary =\n{\n\tlocal   float rnum;\n\tlocal   string deathstring, deathstring2;\n\tlocal   string  attackerteam, targteam;\n\n\trnum = random();\n\t//ZOID 12-13-96: self.team doesn't work in QW.  Use keys\n\tattackerteam = infokey(attacker, \"team\");\n\ttargteam = infokey(targ, \"team\");\n\n\tif (targ.classname == \"player\")\n\t{\n\t\tif (coop == 1 && no_connect < 4)\n\t\t\tno_connect = no_connect + 1;\n\n\t\tif (no_connect > 4)\n\t\t\tno_connect = 4;\n\t}\n\n\tif (attacker.classname == \"player\")\n\t{\n\t\tif (coop == 1 && no_connect < 4)\n\t\t\tno_connect = no_connect + 1;\n\n\t\tif (attacker == targ)\n\t\t\tattacker.kills = attacker.kills - 1;\n\t\telse if (attacker.team == targ.team)\n\t\t\tattacker.kills = attacker.kills - 1;\n\t\telse if (targ.classname == \"player\")\n\t\t\tattacker.kills = attacker.kills + 1;\n\t\telse if (targ.classname == \"monster\")\n\t\t\tattacker.kills = attacker.kills + 1;\n\t}\n\n\tif (targ == attacker && targ.deathtype == \"suicide\")\n\t{\n\t\tbprint(2, attacker.netname);\n\t\tbprint(2, \" takes the easy way out\\n\");\n\t\treturn;\n\t}\n\n\tif (targ == attacker && targ.deathtype == \"bleed\")\n\t{\n\t\tbprint(2, attacker.netname);\n\t\tbprint(2, \" bled to death\\n\");\n\t\treturn;\n\t}\n\n\tif (targ != attacker && (targ.classname == \"monster\" || targ.classname == \"player\") && (attacker.classname == \"monster\" || attacker.classname == \"player\"))\n\t{\n\t\t\tbprint (PRINT_MEDIUM, \"[ \");\n\n\t\tif (attacker.critical == 3) //headshot\n\t\t\tbprint (PRINT_MEDIUM, \"X \");\n\t\tif (attacker.critical == 777) //through the wall\n\t\t\tbprint (PRINT_MEDIUM, \"% \");\n\t\tif (attacker.critical == 778) //wall headshot\n\t\t\tbprint (PRINT_MEDIUM, \"& \");\n\t\tif (attacker.velocity_z != 0)\n\t\t\tbprint (PRINT_MEDIUM, \"@ \");\n//\t\tif (attacker.current_slot == 1 && attacker.mag1 == 0)\n//\t\t\tbprint (PRINT_MEDIUM, \"! \");\n\n\t\t\tbprint (PRINT_MEDIUM, \"] \");\n\n\t\tbprint(2, attacker.netname);\n\t\tbprint(2, \" >>> \");\n\t\tbprint(2, targ.netname);\n\t\tbprint(2, \"\\n\");\n\t\treturn;\n\t}\n\n\tif (targ.classname == \"player\")\n\t{\n\n\t\tif (deathmatch > 3)\t\n\t\t{\n\t\t\tif (targ.deathtype == \"selfwater\")\n\t\t\t{\n\t\t\t\tbprint (PRINT_MEDIUM, targ.netname);\n\t\t\t\tbprint (PRINT_MEDIUM,\" electrocutes himself.\\n \");\n\t\t\t\ttarg.frags = targ.frags - 1;\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\tif (attacker.classname == \"teledeath\")\n\t\t{\n\t\t\tbprint (PRINT_MEDIUM,targ.netname);\n\t\t\tbprint (PRINT_MEDIUM,\" was telefragged by \");\n\t\t\tbprint (PRINT_MEDIUM,attacker.owner.netname);\n\t\t\tbprint (PRINT_MEDIUM,\"\\n\");\n\t\t\tlogfrag (attacker.owner, targ);\n\n\t\t\tattacker.owner.frags = attacker.owner.frags + 1;\n\t\t\treturn;\n\t\t}\n\n\t\tif (attacker.classname == \"teledeath2\")\n\t\t{\n\t\t\tbprint (PRINT_MEDIUM,\"Satan's power deflects \");\n\t\t\tbprint (PRINT_MEDIUM,targ.netname);\n\t\t\tbprint (PRINT_MEDIUM,\"'s telefrag\\n\");\n\n\t\t\ttarg.frags = targ.frags - 1;\n\t\t\tlogfrag (targ, targ);\n\t\t\treturn;\n\t\t}\n\n\t\t// double 666 telefrag (can happen often in deathmatch 4)\n\t\tif (attacker.classname == \"teledeath3\") \n\t\t{\n\t\t\tbprint (PRINT_MEDIUM,targ.netname);\n\t\t\tbprint (PRINT_MEDIUM,\" was telefragged by \");\n\t\t\tbprint (PRINT_MEDIUM,attacker.owner.netname);\n\t\t\tbprint (PRINT_MEDIUM, \"'s Satan's power\\n\");\n\t\t\ttarg.frags = targ.frags - 1;\n\t\t\tlogfrag (targ, targ);\n\t\t\treturn;\n\t\t}\n\t\n\n\t\tif (targ.deathtype == \"squish\")\n\t\t{\n\t\t\tif (teamplay && targteam == attackerteam && attackerteam != \"\" && targ != attacker)\n\t\t\t{\n\t\t\t\tlogfrag (attacker, attacker);\n\t\t\t\tattacker.frags = attacker.frags - 1; \n\t\t\t\tbprint (PRINT_MEDIUM,attacker.netname);\n\t\t\t\tbprint (PRINT_MEDIUM,\" squished a teammate\\n\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\telse if (attacker.classname == \"player\" && attacker != targ)\n\t\t\t{\n\t\t\t\tbprint (PRINT_MEDIUM, attacker.netname);\n\t\t\t\tbprint (PRINT_MEDIUM,\" squishes \");\n\t\t\t\tbprint (PRINT_MEDIUM,targ.netname);\n\t\t\t\tbprint (PRINT_MEDIUM,\"\\n\");\n\t\t\t\tlogfrag (attacker, targ);\n\t\t\t\tattacker.frags = attacker.frags + 1;\n\t\t\t\treturn;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tlogfrag (targ, targ);\n\t\t\t\ttarg.frags = targ.frags - 1;            // killed self\n\t\t\t\tbprint (PRINT_MEDIUM,targ.netname);\n\t\t\t\tbprint (PRINT_MEDIUM,\" was squished\\n\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\tif (attacker.classname == \"player\")\n\t\t{\n\t\t\tif (targ == attacker)\n\t\t\t{\n\t\t\t\t// killed self\n\t\t\t\tlogfrag (attacker, attacker);\n\t\t\t\tattacker.frags = attacker.frags - 1;\n\t\t\t\tbprint (PRINT_MEDIUM,targ.netname);\n\t\t\t\tif (targ.deathtype == \"grenade\")\n\t\t\t\t\tbprint (PRINT_MEDIUM,\" tries to put the pin back in\\n\");\n\t\t\t\telse if (targ.deathtype == \"rocket\")\n\t\t\t\t\tbprint (PRINT_MEDIUM,\" becomes bored with life\\n\");\n\t\t\t\telse if (targ.weapon == 64 && targ.waterlevel > 1)\n\t\t\t\t{\n\t\t\t\t\tif (targ.watertype == CONTENT_SLIME)\n\t\t\t\t\t\tbprint (PRINT_MEDIUM,\" discharges into the slime\\n\");\n\t\t\t\t\telse if (targ.watertype == CONTENT_LAVA)\n\t\t\t\t\t\tbprint (PRINT_MEDIUM,\" discharges into the lava\\n\");\n\t\t\t\t\telse\n\t\t\t\t\t\tbprint (PRINT_MEDIUM,\" discharges into the water.\\n\");\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tbprint (PRINT_MEDIUM,\" becomes bored with life\\n\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\telse if ( (teamplay == 2) && (targteam == attackerteam) &&\n\t\t\t\t(attackerteam != \"\") )\n\t\t\t{\n\t\t\t\tif (rnum < 0.25)\n\t\t\t\t\tdeathstring = \" mows down a teammate\\n\";\n\t\t\t\telse if (rnum < 0.50)\n\t\t\t\t\tdeathstring = \" checks his glasses\\n\";\n\t\t\t\telse if (rnum < 0.75)\n\t\t\t\t\tdeathstring = \" gets a frag for the other team\\n\";\n\t\t\t\telse\n\t\t\t\t\tdeathstring = \" loses another friend\\n\";\n\t\t\t\tbprint (PRINT_MEDIUM, attacker.netname);\n\t\t\t\tbprint (PRINT_MEDIUM, deathstring);\n\t\t\t\tattacker.frags = attacker.frags - 1;\n\t\t\t\t//ZOID 12-13-96:  killing a teammate logs as suicide\n\t\t\t\tlogfrag (attacker, attacker);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tlogfrag (attacker, targ);\n\t\t\t\tattacker.frags = attacker.frags + 1;\n\n\t\t\t\trnum = attacker.weapon;\n\t\t\t\tif (targ.deathtype == \"nail\")\n\t\t\t\t{\n\t\t\t\t\tdeathstring = \" was nailed by \";\n\t\t\t\t\tdeathstring2 = \"\\n\";\n\t\t\t\t}\n\t\t\t\telse if (targ.deathtype == \"supernail\")\n\t\t\t\t{\n\t\t\t\t\tdeathstring = \" was punctured by \";\n\t\t\t\t\tdeathstring2 = \"\\n\";\n\t\t\t\t}\n\t\t\t\telse if (targ.deathtype == \"grenade\")\n\t\t\t\t{\n\t\t\t\t\tdeathstring = \" eats \";\n\t\t\t\t\tdeathstring2 = \"'s pineapple\\n\";\n\t\t\t\t\tif (targ.health < -40)\n\t\t\t\t\t{\n\t\t\t\t\t\tdeathstring = \" was gibbed by \";\n\t\t\t\t\t\tdeathstring2 = \"'s grenade\\n\";\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse if (targ.deathtype == \"rocket\")\n\t\t\t\t{\n\t\t\t\t\tif (attacker.super_damage_finished > 0 && targ.health < -40)\n\t\t\t\t\t{\n\t\t\t\t\t\trnum = random();\n\t\t\t\t\t\tif (rnum < 0.3)\n\t\t\t\t\t\t\tdeathstring = \" was brutalized by \";\n\t\t\t\t\t\telse if (rnum < 0.6)\n\t\t\t\t\t\t\tdeathstring = \" was smeared by \";\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tbprint (PRINT_MEDIUM, attacker.netname);\n\t\t\t\t\t\t\tbprint (PRINT_MEDIUM, \" rips \");\n\t\t\t\t\t\t\tbprint (PRINT_MEDIUM, targ.netname);\n\t\t\t\t\t\t\tbprint (PRINT_MEDIUM, \" a new one\\n\");\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tdeathstring2 = \"'s quad rocket\\n\";\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tdeathstring = \" rides \";\n\t\t\t\t\t\tdeathstring2 = \"'s rocket\\n\";\n\t\t\t\t\t\tif (targ.health < -40)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tdeathstring = \" was gibbed by \";\n\t\t\t\t\t\t\tdeathstring2 = \"'s rocket\\n\" ;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse if (rnum == IT_AXE)\n\t\t\t\t{\n\t\t\t\t\tdeathstring = \" was ax-murdered by \";\n\t\t\t\t\tdeathstring2 = \"\\n\";\n\t\t\t\t}\n\t\t\t\telse if (rnum == IT_SHOTGUN)\n\t\t\t\t{\n\t\t\t\t\tdeathstring = \" chewed on \";\n\t\t\t\t\tdeathstring2 = \"'s boomstick\\n\";\n\t\t\t\t}\n\t\t\t\telse if (rnum == IT_SUPER_SHOTGUN)\n\t\t\t\t{\n\t\t\t\t\tdeathstring = \" ate 2 loads of \";\n\t\t\t\t\tdeathstring2 = \"'s buckshot\\n\";\n\t\t\t\t}\n\t\t\t\telse if (rnum == IT_LIGHTNING)\n\t\t\t\t{\n\t\t\t\t\tdeathstring = \" accepts \";\n\t\t\t\t\tif (attacker.waterlevel > 1)\n\t\t\t\t\t\tdeathstring2 = \"'s discharge\\n\";\n\t\t\t\t\telse\n\t\t\t\t\t\tdeathstring2 = \"'s shaft\\n\";\n\t\t\t\t}\n\t\t\t\tbprint (PRINT_MEDIUM,targ.netname);\n\t\t\t\tbprint (PRINT_MEDIUM,deathstring);\n\t\t\t\tbprint (PRINT_MEDIUM,attacker.netname);\n\t\t\t\tbprint (PRINT_MEDIUM,deathstring2);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tlogfrag (targ, targ);\n\t\t\ttarg.frags = targ.frags - 1;            // killed self\n\t\t\trnum = targ.watertype;\n\n\t\t\tbprint (PRINT_MEDIUM,targ.netname);\n\t\t\tif (rnum == -3)\n\t\t\t{\n\t\t\t\tif (random() < 0.5)\n\t\t\t\t\tbprint (PRINT_MEDIUM,\" sleeps with the fishes\\n\");\n\t\t\t\telse\n\t\t\t\t\tbprint (PRINT_MEDIUM,\" sucks it down\\n\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\telse if (rnum == -4)\n\t\t\t{\n\t\t\t\tif (random() < 0.5)\n\t\t\t\t\tbprint (PRINT_MEDIUM,\" gulped a load of slime\\n\");\n\t\t\t\telse\n\t\t\t\t\tbprint (PRINT_MEDIUM,\" can't exist on slime alone\\n\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\telse if (rnum == -5)\n\t\t\t{\n\t\t\t\tif (targ.health < -15)\n\t\t\t\t{\n\t\t\t\t\tbprint (PRINT_MEDIUM,\" burst into flames\\n\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (random() < 0.5)\n\t\t\t\t\tbprint (PRINT_MEDIUM,\" turned into hot slag\\n\");\n\t\t\t\telse\n\t\t\t\t\tbprint (PRINT_MEDIUM,\" visits the Volcano God\\n\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (attacker.classname == \"explo_box\")\n\t\t\t{\n\t\t\t\tbprint (PRINT_MEDIUM,\" blew up\\n\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (targ.deathtype == \"falling\")\n\t\t\t{\n\t\t\t\tbprint (PRINT_MEDIUM,\" fell to his death\\n\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (targ.deathtype == \"nail\" || targ.deathtype == \"supernail\")\n\t\t\t{\n\t\t\t\tbprint (PRINT_MEDIUM,\" was spiked\\n\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (targ.deathtype == \"laser\")\n\t\t\t{\n\t\t\t\tbprint (PRINT_MEDIUM,\" was zapped\\n\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (attacker.classname == \"fireball\")\n\t\t\t{\n\t\t\t\tbprint (PRINT_MEDIUM,\" ate a lavaball\\n\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (attacker.classname == \"trigger_changelevel\")\n\t\t\t{\n\t\t\t\tbprint (PRINT_MEDIUM,\" tried to leave\\n\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tbprint (PRINT_MEDIUM,\" died\\n\");\n\t\t}\n\t}\n};\n\n\n\nvoid() Identify =\n{\n\tlocal string c1, c2, c3, c5;\n\tlocal float tmp;\n\n\tmakevectors (self.v_angle);\n\ttraceline (self.origin, (self.origin + (v_forward * 2000)), 32, self);\n\tif (trace_ent.classname == \"player\" && self.currentmenu == \"none\" && self.team == trace_ent.team)\n\t{\n\t\tif (trace_ent.class == 2)\n\t\t\tc1 = \"^7\\nmedic\\n\";\n\t\telse if (trace_ent.class == 3)\n\t\t\tc1 = \"^7\\nassassin\\n\";\n\t\telse if (trace_ent.class == 4)\n\t\t\tc1 = \"^7\\nsoldier\\n\";\n\t\telse if (trace_ent.class == 6)\n\t\t\tc1 = \"^7\\nscientist\\n\";\n\t\telse\n\t\t\tc1 = \"^7\\n\";\n\n\t\tif (trace_ent.health <= 0)\n\t\t\tc2 = \"^1dead^7\";\n\t\tc2 = ftos (trace_ent.health);\n\t\tc3 = GetItemName (ToIID(ItemInSlot(trace_ent, trace_ent.current_slot)));\n\n\t\tif (trace_ent.islot3 == 0)\n\t\t\tc5 = \"no armor\";\n\t\telse\n\t\t\tc5 = GetItemName(ToIID(trace_ent.islot3));\n\n\t\tcenterprint (self, trace_ent.netname, c1, c2, \"\\n\", c3, \"\\n\", c5);\n\t}\n\tif (trace_ent.classname == \"robowolf\" && self.currentmenu == \"none\")\n\t{\n\t\tc1 = \"robo-fang\\n\";\n\t\tc2 = \"robot construct\\nowned by \";\n\t\tc3 = trace_ent.track.netname;\n\t\tif (trace_ent.team == self.team)\n\t\t\tc5 = \"\\nfriendly\\n\";\n\t\telse\n\t\t\tc5 = \"\\nhostile\\n\";\n\n\t\tcenterprint (self, trace_ent.netname, c1, c2, c3, c5, \"\", \"\");\n\t}\n\tif (trace_ent.classname == \"station\" && self.currentmenu == \"none\")\n\t{\n\t\tc1 = trace_ent.netname;\n\t\tif (self.armortype == 0)\n\t\t\tc2 = \"uncompleted\";\n\t\telse if (trace_ent.health >= trace_ent.max_health)\n\t\t\tc2 = \"online\";\n\t\telse if (trace_ent.health >= trace_ent.max_health*0.75)\n\t\t\tc2 = \"banged up\";\n\t\telse if (trace_ent.health >= trace_ent.max_health*0.50)\n\t\t\tc2 = \"damaged\";\n\t\telse\n\t\t\tc2 = \"almost destroyed\";\n\n\t\tc3 = trace_ent.track.netname;\n\n\t\tcenterprint (self, c1, \"\\nstationary bot\", \"\\n\", c2, \"\\n\", \"owned by \", c3);\n\t}\n\tif (trace_ent.classname == \"monster\" && self.currentmenu == \"none\")\n\t{\n\t\tc1 = \"hostile\";\n\n\t\ttmp = ToIID(trace_ent.islot3);\n\t\tif (tmp == 0)\n\t\t\tc2 = \"natural armor\";\n\t\telse if (tmp == IID_ARM_SHIRT)\n\t\t\tc2 = \"lightly armored\";\n\t\telse\n\t\t\tc2 = GetItemName(ToIID(trace_ent.islot3));\n\n\t\tif (trace_ent.weapon == 1)\n\t\t\tc3 = \"rifle\";\n\t\tif (trace_ent.weapon == 2)\n\t\t\tc3 = \"pistol\";\n\t\tif (trace_ent.weapon == 3)\n\t\t\tc3 = \"shotgun\";\n\t\tif (trace_ent.weapon == 4)\n\t\t\tc3 = \"smg\";\n\t\tif (trace_ent.weapon >= 5)\n\t\t\tc3 = \"assault rifle\";\n\n\t\tcenterprint (self, trace_ent.netname, \"\\n\", c1, \"\\n\", c2, \"\\n\", c3);\n\t}\n};\n"
  },
  {
    "path": "quakec/fallout2/cmds.qc",
    "content": "void(entity e, string s) clientcommand = #440\nfloat(string s) tokenize = #441;\nstring(float n) argv = #442;\n\nstring(string s, float start, float length) substring = #116;\nfloat(string str, string sub) strstrofs = #221;\n\nfloat(string desc) itemtoslot =\n{\n\tlocal float slot;\n\tlocal float coma;\n\n\twhile((coma = strstrofs(desc, \"+\")) > 0)\n\t{\n\t\tslot = itemtoslot(substring(desc, 0, coma));\n\t\tif (slot)\n\t\t\treturn slot;\n\t\tdesc = substring(desc, coma+1, -1);\n\t}\n\n\tslot = stof(desc);\n\tif (slot >= 1 && slot <= MAXSLOTS)\n\t\treturn slot;\n\n\tif (desc == \"current\")\n\t\treturn self.current_slot;\n\tslot = ItemIDOfName(desc);\n\tif (!slot)\n\t{\n\t\tsprint(self, PRINT_HIGH, \"Not an item name\\n\");\n\t\treturn 0;\n\t}\n\telse\n\t{\n\t\tslot = SlotOfItem(self, slot);\n\t\tif (slot)\n\t\t\treturn slot;\n\t\treturn 0;\n\t}\n};\n\nfloat(float slotno) DecreaseDestroySlot =\n{\n\tlocal float it;\n\tlocal float iid;\n\tlocal float num;\n\tit = ItemInSlot(self, slotno);\n\tif (ToStatus(it) <= 1)\n\t{\n\t\tSetItemSlot(self, slotno, 0);\n\t\treturn true;\n\t}\n\telse\n\t{\n\t\tiid = ToIID(it);\n\t\tnum = ToStatus(it);\n\t\tnum = num - 1;\n\t\tit = SlotVal(iid, num);\n\t\tSetItemSlot(self, slotno, it);\n\t\treturn false;\n\t}\n};\n\nvoid(string arg1) Cmd_InvUse =\n{\n\tlocal float it, iid;\n\tlocal float slotno;\n\tslotno = itemtoslot(arg1);\n\tif (!slotno)\n\t{\n\t\tsprint(self, PRINT_MEDIUM, \"Can't use - you don't have one\\n\");\n\t\treturn;\n\t}\n\n\n\tit = ItemInSlot(self, slotno);\n\tiid = ToIID(it);\n\n\tif (iid == 0)\n\t\treturn;\n\n\tif (WeaponAmmoType(iid))\n\t{\n\t\t//weapons are reloaded\n\t\tReloadWeapon(slotno);\n\t\treturn;\n\t}\n\n\tif (iid == IID_CHEM_STIMPACK ||\n\t    iid == IID_CHEM_MEDICALBAG ||\n\t    iid == IID_CHEM_SUPERSTIM)\n\t{\n\t\tif (UseHealingChem(iid))\n\t\t\tDecreaseDestroySlot(slotno);\n\t\treturn;\n\t}\n\n\tif (iid == IID_CHEM_ADRENALINE ||\n\t    iid == IID_CHEM_PSYCHO ||\n\t    iid == IID_CHEM_BESERK)\n\t{\n\t\tif (UseBoostingChem(iid))\n\t\t\tDecreaseDestroySlot(slotno);\n\t\treturn;\n\t}\n\n\tif (iid == IID_BUILD_MRAMMO ||\n\t    iid == IID_BUILD_SHIELDGEN ||\n\t    iid == IID_BUILD_AUTODOC ||\n\t    iid == IID_BUILD_ROBOFANG ||\n\t    iid == IID_BUILD_TTURRET || \n\t    iid == IID_BUILD_RTURRET ||\n\t    iid == IID_BUILD_GTURRET)\n\t{\n\t\tif (spawn_station(iid))\n\t\t\tDecreaseDestroySlot(slotno);\n\t\treturn;\n\t}\n\n\tif (iid == IID_EQUIP_CLIMBINGGEAR)\n\t{\n\t\tif (self.equipment_slot == slotno)\n\t\t{\n\t\t\tself.equipment_slot = 0;\n\t\t\tsprint(self, PRINT_HIGH, \"Climbing gear deactivated\\n\");\n\t\t}\n\t\telse\n\t\t{\n\t\t\tself.equipment_slot = slotno;\n\t\t\tsprint(self, PRINT_HIGH, \"Climbing gear activated\\n\");\n\t\t}\n\t\treturn;\n\t}\n\tif (iid == IID_EQUIP_SPRINTKIT)\n\t{\n\t\tif (self.equipment_slot == slotno)\n\t\t{\n\t\t\tself.equipment_slot = 0;\n\t\t\tsprint(self, PRINT_HIGH, \"What a downer\\n\");\n\t\t}\n\t\telse\n\t\t{\n\t\t\tself.equipment_slot = slotno;\n\t\t\tsprint(self, PRINT_HIGH, \"SUGAR RUSH!!!\\n\");\n\t\t}\n\t\treturn;\n\t}\n\tif (iid == IID_EQUIP_STEALTHBOY)\n\t{\n\t\tif (self.equipment_slot == slotno)\n\t\t{\n\t\t\tself.equipment_slot = 0;\n\t\t\tsprint(self, PRINT_HIGH, \"stealthboy deactivated\\n\");\n\t\t}\n\t\telse\n\t\t{\n\t\t\tself.equipment_slot = slotno;\n\t\t\tsprint(self, PRINT_HIGH, \"stealthboy activated, fading into the shadows\\n\");\n\t\t}\n\t\treturn;\n\t}\n\tif (iid == IID_EQUIP_HOVERBOOTS)\n\t{\n\t\tif (self.equipment_slot == slotno)\n\t\t{\n\t\t\tself.equipment_slot = 0;\n\t\t\tsprint(self, PRINT_HIGH, \"Hoverboots deactivated\\n\");\n\t\t}\n\t\telse\n\t\t{\n\t\t\tself.equipment_slot = slotno;\n\t\t\tsprint(self, PRINT_HIGH, \"Hoverboots activated\\n\");\n\t\t}\n\t\treturn;\n\t}\n\n\tsprint(self, PRINT_HIGH, \"Don't know how to 'use' item\\n\");\n};\n\nvoid(string arg1) Cmd_InvDrop =\n{\n\tlocal float it, iid;\n\tlocal float slotno;\n\tslotno = itemtoslot(arg1);\n\tif (!slotno)\n\t{\n\t\tsprint(self, PRINT_MEDIUM, \"Can't drop - you don't have one\\n\");\n\t\treturn;\n\t}\n\n\n\tit = ItemInSlot(self, slotno);\n\tiid = ToIID(it);\n\n\tif (iid == 0)\n\t\treturn;\n\n\tDropFromSlot(slotno, true, false);\n};\n\nvoid(string arg1, string arg2) Cmd_InvSwap =\n{\n\tlocal float old1, old2;\n\tlocal float slotno1;\n\tlocal float slotno2;\n\tslotno1 = itemtoslot(arg1);\n\tif (!slotno1)\n\t{\n\t\tsprint(self, PRINT_HIGH, \"No item\\n\");\n\t\treturn;\n\t}\n\tslotno2 = itemtoslot(arg2);\n\tif (!slotno2)\n\t{\n\t\tsprint(self, PRINT_HIGH, \"No item\\n\");\n\t\treturn;\n\t}\n\n\told1 = ItemInSlot(self, slotno1);\n\told2 = ItemInSlot(self, slotno2);\n\tif (old1 == old2)\n\t\treturn;\n\n\tif (!FitsInSlot(slotno1, ToIID(old2)))\n\t{\n\t\tsprint(self, PRINT_HIGH, \"Not allowed to exchange items\\n\");\n\t\treturn;\n\t}\n\tif (!FitsInSlot(slotno2, ToIID(old1)))\n\t{\n\t\tsprint(self, PRINT_HIGH, \"Not allowed to exchange items\\n\");\n\t\treturn;\n\t}\n\tSetItemSlot(self, slotno1, old2);\n\tSetItemSlot(self, slotno2, old1);\n\n\tif (slotno1 == self.current_slot || slotno2 == self.current_slot)\n\t\tW_SetCurrentAmmo();\n\n//swap the equipment_slot over too.\n\tif (self.equipment_slot == slotno1)\n\t\tself.equipment_slot = slotno2;\n\telse if (self.equipment_slot == slotno2)\n\t\tself.equipment_slot = slotno1;\n\n\tself.rtime = time + 2;\t//we don't check this here though - cost to get into inventory.\n};\n\nvoid(string arg1, float iid, float num) Cmd_InvGive =\n{\n\tlocal float slotno;\n\tslotno = itemtoslot(arg1);\n\tif (!slotno)\n\t{\n\t\tsprint(self, PRINT_MEDIUM, \"Can't give to that slot\\n\");\n\t\treturn;\n\t}\n\n\tif (!FitsInSlot(slotno, iid))\n\t{\n\t\tsprint(self, PRINT_MEDIUM, \"Can't give that item in that slot\\n\");\n\t\treturn;\n\t}\n\n\tif (num <= 0)\n\t\tnum = 1;\n\tSetItemSlot(self, slotno, SlotVal(iid, num));\n\n\tif (slotno == self.current_slot)\n\t\tW_SetCurrentAmmo();\n};\n\nvoid(string line) SV_ParseClientCommand =\n{\n\tlocal string cmd;\n\ttokenize(line);\n\n\tcmd = argv(0);\n\tif (cmd == \"invuse\")\n\t{\n\t\tif (self.deadflag || self.current_slot==0)\n\t\t\treturn;\n\t\tCmd_InvUse(argv(1));\n\t}\n\telse if (cmd == \"invdrop\")\n\t{\n\t\tif (self.deadflag || self.current_slot==0)\n\t\t\treturn;\n\t\tCmd_InvDrop(argv(1));\n\t}\n\telse if (cmd == \"invswap\")\n\t{\n\t\tif (self.deadflag || self.current_slot==0)\n\t\t\treturn;\n\t\tCmd_InvSwap(argv(1), argv(2));\n\t}\n\telse if (cmd == \"invgive\")\n\t{\n\t\tif (self.deadflag || self.current_slot==0)\n\t\t\treturn;\n\t\tCmd_InvGive(argv(1), stof(argv(2)), stof(argv(3)));\n\t}\n\telse if (cmd == \"god\")\n\t{\n\t\tsprint(self, PRINT_HIGH, \"sorry, but I'm an athiest\\n\");\n\t}\n\telse\n\t\tclientcommand(self, line);\n};"
  },
  {
    "path": "quakec/fallout2/combat.qc",
    "content": "\nvoid() T_MissileTouch;\nvoid() info_player_start;\nvoid(entity targ, entity attacker) ClientObituary;\nvoid(entity inflictor, entity attacker, float damage, entity ignore, string dtype) T_RadiusDamage;\n\nvoid() monster_death_use;\n\n//============================================================================\n\n/*\n============\nCanDamage\n\nReturns true if the inflictor can directly damage the target.  Used for\nexplosions and melee attacks.\n============\n*/\nfloat(entity targ, entity inflictor) CanDamage =\n{\n// bmodels need special checking because their origin is 0,0,0\n\tif (targ.movetype == MOVETYPE_PUSH)\n\t{\n\t\ttraceline(inflictor.origin, 0.5 * (targ.absmin + targ.absmax), TRUE, self);\n\t\tif (trace_fraction == 1)\n\t\t\treturn TRUE;\n\t\tif (trace_ent == targ)\n\t\t\treturn TRUE;\n\t\treturn FALSE;\n\t}\n\t\n\ttraceline(inflictor.origin, targ.origin, TRUE, self);\n\tif (trace_fraction == 1)\n\t\treturn TRUE;\n\ttraceline(inflictor.origin, targ.origin + '15 15 0', TRUE, self);\n\tif (trace_fraction == 1)\n\t\treturn TRUE;\n\ttraceline(inflictor.origin, targ.origin + '-15 -15 0', TRUE, self);\n\tif (trace_fraction == 1)\n\t\treturn TRUE;\n\ttraceline(inflictor.origin, targ.origin + '-15 15 0', TRUE, self);\n\tif (trace_fraction == 1)\n\t\treturn TRUE;\n\ttraceline(inflictor.origin, targ.origin + '15 -15 0', TRUE, self);\n\tif (trace_fraction == 1)\n\t\treturn TRUE;\n\n\treturn FALSE;\n};\n\n\n/*\n============\nKilled\n============\n*/\nvoid(entity targ, entity attacker) Killed =\n{\n\tlocal entity oself;\n\n\toself = self;\n\tself = targ;\n\t\n\tif (self.health < -99)\n\t\tself.health = -99;              // don't let sbar look bad if a player\n\n\tif (self.movetype == MOVETYPE_PUSH || self.movetype == MOVETYPE_NONE)\n\t{       // doors, triggers, etc\n\t\tself.th_die ();\n\t\tself = oself;\n\t\treturn;\n\t}\n\n\tself.enemy = attacker;\n\n\ttarg.grab = 0;\n\ttarg.equipment_slot = 0;         //turn off stealth-boys, climbing gear, etc\n\n// bump the monster counter\n\tif (self.flags & FL_MONSTER)\n\t{\n\t\tkilled_monsters = killed_monsters + 1;\n\t\tWriteByte (MSG_ALL, SVC_KILLEDMONSTER);\n\t}\n\n\tClientObituary(self, attacker);\n\t\n\tif (self.classname == \"player\")  //so dead players can spectate\n\t{\n\t\tself.ghost = 1;\n\t\tself.flags = self.flags | FL_FINDABLE_NONSOLID;\t//so tracelines can find them\n\t}\n\n\tself.takedamage = DAMAGE_NO;\n\tself.touch = SUB_Null;\n\tself.effects = 0;\n\n\tmonster_death_use();\n\n\tself.th_die ();\n\t\n\tself = oself;\n};\n\n\n/*\n============\nT_Damage\n\nThe damage is coming from inflictor, but get mad at attacker\nThis should be the only function that ever reduces health.\n============\n*/\n\nvoid(entity targ, entity inflictor, entity attacker, float damage) T_Damage =\n{\n\tlocal   entity  oldself;\n\tlocal   float   save;\n\tlocal   float   take, severity, helm;\n\tlocal   string  attackerteam, targteam;\n\n\n\tif (!targ.takedamage)\n\t\treturn;\n\n// used by buttons and triggers to set activator for target firing\n\tdamage_attacker = attacker;\n\n\n// check for quad damage powerup on the attacker\n\tif (attacker.super_damage_finished > time && inflictor.classname != \"door\")\n\tif (deathmatch == 4)\n\t\tdamage = damage * 8;\n\telse\n\t\tdamage = damage * 4;\n\n\tif (attacker.critical == 3)//attacker scored a headshot/critical\n\t{\n\t\tif (attacker.critical == 3)\n\t\t{\n\t\t\tseverity = 0 + random()*20;\n\t\t\tif (attacker.perk == 7)\n\t\t\t\tseverity = severity + 4;\n\n\t\t\tif (attacker.class == 3)\n\t\t\t\tseverity = severity + 2;\n\n\t\t\tif (severity >= 19)\n\t\t\t\tdamage = (damage * 5);\n\t\t\telse if (severity >= 14)\n\t\t\t\tdamage = (damage * 4);\n\t\t\telse\n\t\t\t\tdamage = (damage * 3);\n\n\t\t\thelm = targ.armortype;\n\t\t\tif (targ.helmet == 0)\n\t\t\t{\n\t\t\t\tsound (targ, CHAN_BODY, \"player/headshot.wav\", 1, ATTN_NORM);\n\t\t\t\thelm = 0;\n\t\t\t}\n\t\t\tif (targ.helmet == AS_STRAIGHT)\n\t\t\t{\n\t\t\t\tsound (targ, CHAN_BODY, \"weapons/helmet.wav\", 1, ATTN_NORM);\n\t\t\t\thelm = 0.30;\n\t\t\t}\n\t\t\tif (targ.helmet == AS_SLIDING)\n\t\t\t{\n\t\t\t\tsound (targ, CHAN_BODY, \"weapons/helmet.wav\", 1, ATTN_NORM);\n\t\t\t\thelm = 0.45;\n\t\t\t}\n\n\t\t\tdamage = (damage - (damage * helm));\n\n\t\t\tmakevectors (targ.v_angle);\n\t\t}\n\t}\n\n\tif (damage <= 0)\n\t{\n\t\tdamage = 0;\n\t\tsound (targ, CHAN_BODY, targ.armornoise, 1, ATTN_NORM);\n\t\treturn;\n\t}\n\n\t\tsound (targ, CHAN_BODY, targ.armornoise, 1, ATTN_NORM);\n\n// save damage based on the target's armor level\n\n\tsave = ceil(targ.armortype*damage);\n\tif (save >= targ.armorvalue)\n\t{\n\t\tsave = targ.armorvalue;\n/*\t\ttarg.armortype = 0;     // lost all armor\n\t\ttarg.items = targ.items - (targ.items & (IT_ARMOR1 | IT_ARMOR2 | IT_ARMOR3));\n*/\n\t}\n\n\t\n\ttake = ceil(damage-save);\n\n// add to the damage total for clients, which will be sent as a single\n// message at the end of the frame\n// FIXME: remove after combining shotgun blasts?\n\tif (targ.flags & FL_CLIENT)\n\t{\n\t\ttarg.dmg_take = targ.dmg_take + take;\n\t\ttarg.dmg_save = targ.dmg_save + save;\n\t\ttarg.dmg_inflictor = inflictor;\n\t}\n\n\tdamage_inflictor = inflictor;        \n\n/*\n// figure momentum add\n\tif ( (inflictor != world) && (targ.movetype == MOVETYPE_WALK) )\n\t{\n\t\tdir = targ.origin - (inflictor.absmin + inflictor.absmax) * 0.5;\n\t\tdir = normalize(dir);\n\t\t// Set kickback for smaller weapons\n//Zoid -- use normal NQ kickback\n//\t\t// Read: only if it's not yourself doing the damage\n//\t\tif ( (damage < 60) & ((attacker.classname == \"player\") & (targ.classname == \"player\")) & ( attacker.netname != targ.netname)) \n//\t\t\ttarg.velocity = targ.velocity + dir * damage * 11;\n//\t\telse                        \n\t\t// Otherwise, these rules apply to rockets and grenades                        \n\t\t// for blast velocity\n\t\t\ttarg.velocity = targ.velocity + dir * damage * 8;\n\t\t\n\t\t// Rocket Jump modifiers\n\t\tif ( (rj > 1) & ((attacker.classname == \"player\") & (targ.classname == \"player\")) & ( attacker.netname == targ.netname)) \n\t\t\ttarg.velocity = targ.velocity + dir * damage * rj;\n\n\t}*/\n\n\n\n// check for godmode or invincibility\n\tif (targ.flags & FL_GODMODE)\n\t\treturn;\n\tif (targ.invincible_finished >= time)\n\t{\n\t\tif (self.invincible_sound < time)\n\t\t{\n\t\t\tsound (targ, CHAN_ITEM, \"items/protect3.wav\", 1, ATTN_NORM);\n\t\t\tself.invincible_sound = time + 2;\n\t\t}\n\t\treturn;\n\t}\n\n// team play damage avoidance\n//ZOID 12-13-96: self.team doesn't work in QW.  Use keys\n\tattackerteam = infokey(attacker, \"team\");\n\ttargteam = infokey(targ, \"team\");\n\n\tif ((teamplay == 1) && (targteam == attackerteam) &&\n\t\t(attacker.classname == \"player\") && (attackerteam != \"\") &&\n\t\tinflictor.classname !=\"door\")\n\t\treturn;\n\n\tif ((teamplay == 3) && (targteam == attackerteam) &&\n\t\t(attacker.classname == \"player\") && (attackerteam != \"\") &&\n\t\t(targ != attacker)&& inflictor.classname !=\"door\")\n\t\treturn;\n\t\t\n\n// do the damage\n\t//different sorts of armour simply subtract different ammounts\n      //this makes armor like the force armor good against many small rounds\n      //(SMG, shotguns, swords, etc) but useless against big damage (grenades)\n      //power armor, which has the best of both worlds, is also the heaviest :p\n\n\tswitch(ToIID(targ.islot3))\n\t{\n\tcase IID_ARM_SHIRT:\n\t\ttake -= 1;\n\t\tbreak;\n\tcase IID_ARM_LEATHER:\n\t\ttake -= 2;\n\t\tbreak;\n\tcase IID_ARM_KEVLAR:\n\t\ttake -= 3;\n\t\tbreak;\n\tcase IID_ARM_METAL:\n\t\ttake -= 4;\n\t\tbreak;\n\tcase IID_ARM_COMBAT:\n\t\ttake -= 5;\n\t\tbreak;\n\tcase IID_ARM_BROTHERHOOD:\n\t\ttake -= 6;\n\t\tbreak;\n\tcase IID_ARM_FORCE:\n\t\ttake -= 7;\n\t\tbreak;\n\tcase IID_ARM_LPOWER:\n\t\ttake -= 8;\n\t\tbreak;\n\tdefault:\n\t\tbreak;\n\t}\n\n\n\tif (take <= 0)\n\t{\n\t\ttake = 0;\n\t\tsound (targ, CHAN_BODY, targ.armornoise, 1, ATTN_NORM);\n\t\treturn;\n\t}\n\n\n\ttarg.health = targ.health - take;\n\n\tif (targ.health <= 0)\n\t{\n\t\tKilled (targ, attacker);\n\t\treturn;\n\t}\n\n// react to the damage\n\toldself = self;\n\tself = targ;\n\n/*SERVER\n\tif ( (self.flags & FL_MONSTER) && attacker != world)\n\t{\n\t// get mad unless of the same class (except for soldiers)\n\t\tif (self != attacker && attacker != self.enemy)\n\t\t{\n\t\t\tif ( (self.classname != attacker.classname) \n\t\t\t|| (self.classname == \"monster\" ) )\n\t\t\t{\n\t\t\t\tif (self.enemy.classname == \"player\")\n\t\t\t\t\tself.oldenemy = self.enemy;\n\t\t\t\tself.enemy = attacker;\n\t\t\t\tFoundTarget ();\n\t\t\t}\n\t\t}\n\t}\n*/\n\tif (self.th_pain)\n\t{\n\t\tself.th_pain (attacker, take);\n\t}\n\n\tself = oldself;\n};\n\n/*\n============\nX_Damage\n\nThe other damage function.\n\n============\n*/\n\nvoid(entity targ, entity inflictor, entity attacker, float damage) X_Damage =\n{\n\tlocal   entity  oldself;\n\tlocal   float   save;\n\tlocal   float   take, severity, helm;\n\tlocal   string  attackerteam, targteam;\n\n\n\tif (!targ.takedamage)\n\t\treturn;\n\n// used by buttons and triggers to set activator for target firing\n\tdamage_attacker = attacker;\n\n\n// check for quad damage powerup on the attacker\n\tif (attacker.super_damage_finished > time && inflictor.classname != \"door\")\n\tif (deathmatch == 4)\n\t\tdamage = damage * 8;\n\telse\n\t\tdamage = damage * 4;\n\n\tif (attacker.critical == 3)//attacker scored a headshot/critical\n\t{\n\t\tif (attacker.critical == 3)\n\t\t{\n\t\t\tseverity = 0 + random()*20;\n\t\t\tif (attacker.perk == 7)\n\t\t\t\tseverity = severity + 4;\n\n\t\t\tif (attacker.class == 3)\n\t\t\t\tseverity = severity + 2;\n\n\t\t\tif (severity >= 19)\n\t\t\t\tdamage = (damage * 3.0);\n\t\t\telse if (severity >= 14)\n\t\t\t\tdamage = (damage * 2.5);\n\t\t\telse\n\t\t\t\tdamage = (damage * 2.0);\n\n\t\t\thelm = targ.armortype;\n\t\t\tif (targ.helmet == 0)\n\t\t\t{\n\t\t\t\tsound (targ, CHAN_BODY, \"player/headshot.wav\", 1, ATTN_NORM);\n\t\t\t\thelm = 0;\n\t\t\t}\n\t\t\tif (targ.helmet == AS_STRAIGHT)\n\t\t\t{\n\t\t\t\tsound (targ, CHAN_BODY, \"weapons/helmet.wav\", 1, ATTN_NORM);\n\t\t\t\thelm = 0.10;\n\t\t\t}\n\t\t\tif (targ.helmet == AS_SLIDING)\n\t\t\t{\n\t\t\t\tsound (targ, CHAN_BODY, \"weapons/helmet.wav\", 1, ATTN_NORM);\n\t\t\t\thelm = 0.15;\n\t\t\t}\n\n\t\t\tdamage = (damage - (damage * helm));\n\n\t\t\tmakevectors (targ.v_angle);\n\t\t}\n\t}\n\n\tif (random()*4<=1)\n\t\tsound (targ, CHAN_BODY, targ.armornoise, 1, ATTN_NORM);\n\n// add to the damage total for clients, which will be sent as a single\n// message at the end of the frame\n// FIXME: remove after combining shotgun blasts?\n\tif (targ.flags & FL_CLIENT)\n\t{\n\t\ttarg.dmg_take = targ.dmg_take + take;\n\t\ttarg.dmg_save = targ.dmg_save + save;\n\t\ttarg.dmg_inflictor = inflictor;\n\t}\n\n\tdamage_inflictor = inflictor;        \n\n/*\n// figure momentum add\n\tif ( (inflictor != world) && (targ.movetype == MOVETYPE_WALK) )\n\t{\n\t\tdir = targ.origin - (inflictor.absmin + inflictor.absmax) * 0.5;\n\t\tdir = normalize(dir);\n\t\t// Set kickback for smaller weapons\n//Zoid -- use normal NQ kickback\n//\t\t// Read: only if it's not yourself doing the damage\n//\t\tif ( (damage < 60) & ((attacker.classname == \"player\") & (targ.classname == \"player\")) & ( attacker.netname != targ.netname)) \n//\t\t\ttarg.velocity = targ.velocity + dir * damage * 11;\n//\t\telse                        \n\t\t// Otherwise, these rules apply to rockets and grenades                        \n\t\t// for blast velocity\n\t\t\ttarg.velocity = targ.velocity + dir * damage * 8;\n\t\t\n\t\t// Rocket Jump modifiers\n\t\tif ( (rj > 1) & ((attacker.classname == \"player\") & (targ.classname == \"player\")) & ( attacker.netname == targ.netname)) \n\t\t\ttarg.velocity = targ.velocity + dir * damage * rj;\n\n\t}*/\n\n\n\n// check for godmode or invincibility\n\tif (targ.flags & FL_GODMODE)\n\t\treturn;\n\n\tif (targ.invincible_finished >= time)\n\t{\n\t\tif (self.invincible_sound < time)\n\t\t{\n\t\t\tsound (targ, CHAN_ITEM, \"items/protect3.wav\", 1, ATTN_NORM);\n\t\t\tself.invincible_sound = time + 2;\n\t\t}\n\t\treturn;\n\t}\n\n// team play damage avoidance\n//ZOID 12-13-96: self.team doesn't work in QW.  Use keys\n\tattackerteam = infokey(attacker, \"team\");\n\ttargteam = infokey(targ, \"team\");\n\n\tif ((teamplay == 1) && (targteam == attackerteam) &&\n\t\t(attacker.classname == \"player\") && (attackerteam != \"\") &&\n\t\tinflictor.classname !=\"door\")\n\t\treturn;\n\n\tif ((teamplay == 3) && (targteam == attackerteam) &&\n\t\t(attacker.classname == \"player\") && (attackerteam != \"\") &&\n\t\t(targ != attacker)&& inflictor.classname !=\"door\")\n\t\treturn;\n\t\t\n\n// do the damage\n\t//different sorts of armour simply subtract different ammounts\n      //this makes armor like the force armor good against many small rounds\n      //(SMG, shotguns, swords, etc) but useless against big damage (grenades)\n      //power armor, which has the best of both worlds, is also the heaviest :p\n\n\tswitch(ToIID(targ.islot3))\n\t{\n\t\tcase IID_ARM_BROTHERHOOD:\n\t\t\ttake -= 1;\n\t\t\tbreak;\n\t\tcase IID_ARM_FORCE:\n\t\t\ttake -= 2;\n\t\t\tbreak;\n\t\tcase IID_ARM_LPOWER:\n\t\t\ttake -= 3;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tbreak;\n\t}\n\n\n\tif (take <= 0)\n\t{\n\t\ttake = 0;\n\t\tsound (targ, CHAN_BODY, targ.armornoise, 1, ATTN_NORM);\n\t\treturn;\n\t}\n\n\n\ttarg.health = targ.health - take;\n\n\tif (targ.health <= 0)\n\t{\n\t\tKilled (targ, attacker);\n\t\treturn;\n\t}\n\n// react to the damage\n\toldself = self;\n\tself = targ;\n\n/*SERVER\n\tif ( (self.flags & FL_MONSTER) && attacker != world)\n\t{\n\t// get mad unless of the same class (except for soldiers)\n\t\tif (self != attacker && attacker != self.enemy)\n\t\t{\n\t\t\tif ( (self.classname != attacker.classname) \n\t\t\t|| (self.classname == \"monster\" ) )\n\t\t\t{\n\t\t\t\tif (self.enemy.classname == \"player\")\n\t\t\t\t\tself.oldenemy = self.enemy;\n\t\t\t\tself.enemy = attacker;\n\t\t\t\tFoundTarget ();\n\t\t\t}\n\t\t}\n\t}\n*/\n\tif (self.th_pain)\n\t{\n\t\tself.th_pain (attacker, take);\n\t}\n\n\tself = oldself;\n};\n\n/*\n============\nT_RadiusDamage\n============\n*/\nvoid(entity inflictor, entity attacker, float damage, entity ignore, string dtype) T_RadiusDamage =\n{\n\tlocal   float   points;\n\tlocal   entity  head;\n\tlocal   vector  org;\n\n\thead = findradius(inflictor.origin, damage+40);\n\t\n\twhile (head)\n\t{\n\t\t//bprint (PRINT_HIGH, head.classname);\n\t\t//bprint (PRINT_HIGH, \" | \");\n\t\t//bprint (PRINT_HIGH, head.netname);\n\t\t//bprint (PRINT_HIGH, \"\\n\");\n\t\n\t\tif (head != ignore)\n\t\t{\n\t\t\tif (head.takedamage)\n\t\t\t{\n\t\t\t\torg = head.origin + (head.mins + head.maxs)*0.5;\n\t\t\t\tpoints = 0.5*vlen (inflictor.origin - org);\n\t\t\t\tif (points < 0)\n\t\t\t\t\tpoints = 0;\n\t\t\t\tpoints = damage - points;\n\t\t\t\t\n\t\t\t\tif (head == attacker)\n\t\t\t\t\tpoints = points * 0.5;\n\t\t\t\tif (points > 0)\n\t\t\t\t{\n\t\t\t\t\tif (CanDamage (head, inflictor))\n\t\t\t\t\t{\n\t\t\t\t\t\thead.deathtype = dtype;\n\t\t\t\t\t\tT_Damage (head, inflictor, attacker, points);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\thead = head.chain;\n\t}\n};\n\n/*\n============\nT_BeamDamage\n============\n*/\nvoid(entity attacker, float damage) T_BeamDamage =\n{\n\tlocal   float   points;\n\tlocal   entity  head;\n\t\n\thead = findradius(attacker.origin, damage+40);\n\t\n\twhile (head)\n\t{\n\t\tif (head.takedamage)\n\t\t{\n\t\t\tpoints = 0.5*vlen (attacker.origin - head.origin);\n\t\t\tif (points < 0)\n\t\t\t\tpoints = 0;\n\t\t\tpoints = damage - points;\n\t\t\tif (head == attacker)\n\t\t\t\tpoints = points * 0.5;\n\t\t\tif (points > 0)\n\t\t\t{\n\t\t\t\tif (CanDamage (head, attacker))\n\t\t\t\t\tT_Damage (head, attacker, attacker, points);\n\t\t\t}\n\t\t}\n\t\thead = head.chain;\n\t}\n};\n\n"
  },
  {
    "path": "quakec/fallout2/csprogs.src",
    "content": "../csprogs.dat\n\ncsqc/system.qc\ncsqc/stdbuiltins.qc\ncsqc/builtins.qc\ncsqc/stdconstants.qc\ncsqc/constants.qc\ncsqc/invent.qc\ncsqc/main.qc"
  },
  {
    "path": "quakec/fallout2/csqc/builtins.qc",
    "content": "\n//csqc only builtins.\n\nvoid() clearscene\t\t\t= #300; // (EXT_CSQC)\nvoid(float mask) addentities\t\t= #301; //  (EXT_CSQC)\nvoid(entity ent) addentity\t\t= #302; //  (EXT_CSQC)\nfloat(float property, ...) setviewprop\t= #303; //  (EXT_CSQC)\nvoid() renderscene\t\t\t= #304; //  (EXT_CSQC)\n\nvoid(vector org, float radius, vector lightcolours) adddynamiclight\t\t= #305; //  (EXT_CSQC)\n\n//void(string texturename) R_BeginPolygon\t= #306; //  (EXT_CSQC_???)\n//void(vector org, vector texcoords, vector rgb, float alpha) R_PolygonVertex\t= #307; //  (EXT_CSQC_???)\n//void() R_EndPolygon \t\t\t= #308; //  (EXT_CSQC_???)\n\n//maths stuff that uses the current view settings.\nvector (vector v) unproject \t\t= #310; //  (EXT_CSQC)\nvector (vector v) project \t\t= #311; //  (EXT_CSQC)\n\n//2d (immediate) operations\nvoid(float width, vector pos1, vector pos2) drawline = #315; //  (EXT_CSQC)\nfloat(string name) iscachedpic \t\t\t= #316; //  (EXT_CSQC)\nstring(string name, float trywad) precache_pic \t= #317; //  (EXT_CSQC)\nvector(string picname) draw_getimagesize \t= #318; //  (EXT_CSQC)\nvoid(string name) freepic \t\t\t= #319; //  (EXT_CSQC)\nfloat(vector position, float character, vector scale, vector rgb, float alpha, ...) drawcharacter\t= #320; //  (EXT_CSQC, [EXT_CSQC_???])\nfloat(vector position, string text, vector scale, vector rgb, float alpha, ...) drawstring\t\t= #321; //  (EXT_CSQC, [EXT_CSQC_???])\nfloat(vector position, string pic, vector size, vector rgb, float alpha, ...) drawpic\t\t\t= #322; //  (EXT_CSQC, [EXT_CSQC_???])\nfloat(vector position, vector size, vector rgb, float alpha, ...) drawfill\t\t\t\t= #323; //  (EXT_CSQC, [EXT_CSQC_???])\nvoid(float x, float y, float width, float height) drawsetcliparea \t\t\t\t\t= #324; //  (EXT_CSQC_???)\nvoid(void) drawresetcliparea \t\t\t= #325; //  (EXT_CSQC_???)\n\nfloat(float stnum) getstatf\t\t\t\t\t\t\t= #330; //  (EXT_CSQC)\nfloat(float stnum) getstati\t\t\t\t\t\t\t= #331; //  (EXT_CSQC)\nfloat(float stnum, float first, float count) getstatbits\t\t\t= #331; //  (EXT_CSQC)\nstring(float firststnum) getstats\t\t\t\t\t\t= #332; //  (EXT_CSQC)\nvoid(entity e, float mdlindex) setmodelindex\t\t\t\t\t= #333; //  (EXT_CSQC)\nstring(float mdlindex) modelnameforindex \t\t\t\t\t= #334; //  (EXT_CSQC)\n\nfloat(string effectname) particleeffectforname\t\t\t\t\t= #335; //  (EXT_CSQC)\nvoid(float effectnum, entity ent, vector start, vector end) trailparticles\t= #336; //  (EXT_CSQC),\nvoid(float effectnum, vector origin, ...) pointparticles\t\t\t= #337; //  (EXT_CSQC)\n\nvoid(string s, ...) cprint\t\t= #338; //  (EXT_CSQC)\nvoid(string s, ...) print\t\t= #339; //  (EXT_CSQC)\n\nstring(float keynum) keynumtostring\t= #340; //  (EXT_CSQC)\nfloat(string keyname) stringtokeynum\t= #341; //  (EXT_CSQC)\nstring(float keynum) getkeybind\t\t= #342; //  (EXT_CSQC)\n\nfloat(float framenum) getinputstate\t= #345; //  (EXT_CSQC)\nvoid(float sens) setsensitivityscaler\t= #346; //  (EXT_CSQC)\n\nvoid() runstandardplayerphysics\t\t= #347; //  (EXT_CSQC)\n\nstring(float playernum, string keyname) getplayerkeyvalue\t\t\t= #348; //  (EXT_CSQC)\n\nfloat() isdemo\t\t\t\t= #349; //  (EXT_CSQC)\nfloat() isserver\t\t\t= #350; //  (EXT_CSQC)\n\nvoid(vector origin, vector forward, vector right, vector up) SetListener\t= #351; //  (EXT_CSQC)\nvoid(string cmdname) registercommand\t\t\t\t\t\t= #352; //  (EXT_CSQC)\nfloat(entity ent) wasfreed\t\t\t\t\t\t\t= #353; //  (EXT_CSQC) (should be availabe on server too)\n\nstring(string keyname) getserverinfo\t\t\t= #354; //  (EXT_CSQC)\n\n//note that 'ReadEntity' is pretty hard to implement reliably. Modders should use a combination of ReadShort, and findfloat, and remember that it might not be known clientside (pvs culled or other reason)\nfloat() readbyte\t\t\t= #360; //  (EXT_CSQC)\nfloat() readchar\t\t\t= #361; //  (EXT_CSQC)\nfloat() readshort\t\t\t= #362; //  (EXT_CSQC)\nfloat() readlong\t\t\t= #363; //  (EXT_CSQC)\nfloat() readcoord\t\t\t= #364; //  (EXT_CSQC)\nfloat() readangle\t\t\t= #365; //  (EXT_CSQC)\nstring() readstring\t\t\t= #366; //  (EXT_CSQC)\nstring() readfloat\t\t\t= #367; //  (EXT_CSQC)"
  },
  {
    "path": "quakec/fallout2/csqc/constants.qc",
    "content": "//FIXME: most of this should be in common/constants.qc\n\n//\n// constants\n//\n\n// point content values\n\nfloat\tCONTENT_EMPTY\t\t\t= -1;\nfloat\tCONTENT_SOLID\t\t\t= -2;\nfloat\tCONTENT_WATER\t\t\t= -3;\nfloat\tCONTENT_SLIME\t\t\t= -4;\nfloat\tCONTENT_LAVA\t\t\t= -5;\nfloat\tCONTENT_SKY\t\t\t\t= -6;\n\n// sound channels\n// channel 0 never willingly overrides\n// other channels (1-7) allways override a playing sound on that channel\nfloat\tCHAN_AUTO\t\t= 0;\nfloat\tCHAN_WEAPON\t\t= 1;\nfloat\tCHAN_VOICE\t\t= 2;\nfloat\tCHAN_ITEM\t\t= 3;\nfloat\tCHAN_BODY\t\t= 4;\n\nfloat\tATTN_NONE\t\t= 0;\nfloat\tATTN_NORM\t\t= 1;\nfloat\tATTN_IDLE\t\t= 2;\nfloat\tATTN_STATIC\t\t= 3;\n\n\n// entity effects\n\nfloat\tEF_BRIGHTFIELD\t= 1;\nfloat\tEF_MUZZLEFLASH \t= 2;\nfloat\tEF_BRIGHTLIGHT \t= 4;\nfloat\tEF_DIMLIGHT \t= 8;\n\n\n\n\nfloat\tMASK_ENGINE\t\t= 1;\t//this is special. Any entities known by the engine but not the csqc will be added.\n//you can add any other masks below, remember, use bits.\nfloat\tMASK_NORMAL\t\t= 2;\n\n\n\nfloat INPUT_KEYDOWN = 0;\nfloat INPUT_KEYUP = 1;\nfloat INPUT_MOUSEMOVE = 2;\n\n\n\n//stats 0-31 are filled by the engine.\n//they are for the 'fixed function' stuff, that works without requiring csqc.\n//these stats need to be standardized amoung engines, so you're not allowed to mod them, so nur.\n#define STAT_HEALTH 0\n#define STAT_WEAPONMODEL 2\t//weapon model index, as evalutated by server code.\n#define STAT_AMMO 3\n#define STAT_ARMOR\t 4\n#define STAT_WEAPONFRAME\t 5\n#define STAT_SHELLS 6\n#define STAT_NAILS 7\n#define STAT_ROCKETS 8\n#define STAT_CELLS 9\n#define STAT_ACTIVEWEAPON 10\t//the one shown on the hud\n#define STAT_ITEMS 15\n//stats 32 onwards are filled by the csqc.\n\n\n#define FL_ONGROUND 1\n\n\n\nenum\n{\n\tVF_MIN = 1,\n\tVF_MIN_X = 2,\n\tVF_MIN_Y = 3,\n\tVF_SIZE = 4,\n\tVF_SIZE_X = 5,\n\tVF_SIZE_Y = 6,\n\tVF_VIEWPORT = 7,\n\tVF_FOV = 8,\n\tVF_FOVX = 9,\n\tVF_FOVY = 10,\n\tVF_ORIGIN = 11,\n\tVF_ORIGIN_X = 12,\n\tVF_ORIGIN_Y = 13,\n\tVF_ORIGIN_Z = 14,\n\tVF_ANGLES = 15,\n\tVF_ANGLES_X = 16,\n\tVF_ANGLES_Y = 17,\n\tVF_ANGLES_Z = 18,\n\tVF_DRAWWORLD = 19,\n\tVF_DRAWENGINESBAR = 20,\n\tVF_DRAWCROSSHAIR = 21,\n\tVF_PERSPECTIVE = 200\n};\n\nenumflags {\n\tRF_VIEWMODEL,\n\tRF_EXTERNALMODEL,\n\tRF_DEPTHHACK,\n\tRF_ADDATIVE,\n\tRF_USEAXIS\n};\n\n"
  },
  {
    "path": "quakec/fallout2/csqc/invent.qc",
    "content": "void bprint(float plev, string st, string s2){}\nfloat PRINT_MEDIUM = 1;\n#include \"../inventory.qc\"\n\nfloat show_inventory;\n\nvector mousepos;\n\nfloat showcontextmenu;\nvector contextpos;\n\nfloat slotnum;\t//the current slot under the mousecursor\n\nfloat sliderpos;\n\nfloat downslotnum;\t//the slot number when the mousecursor when down\n\nvector toppos = '0 32 0';\n\nfloat k_mouse1;\nfloat k_mwheelup;\nfloat k_mwheeldown;\nfloat mouseisdown;\n\n#define IMGSIZEF 64\n#define IMGSIZEV ('1 1 0'*IMGSIZEF)\n#define CURSORSIZE 8\n\nvoid(vector pos, float slotno) SlotImage =\n{\n\tlocal float it;\n\tlocal string itname;\n\n\tif (slotno == downslotnum && slotnum)\n\t\tslotno = slotnum;\n\telse if (slotno == slotnum && downslotnum)\n\t\tslotno = downslotnum;\n\n\tit = getstati(31+slotno);\n\n\titname = GetItemImage(ToIID(it));\n\tdrawpic(pos, strcat(\"gui/\", itname), IMGSIZEV, '1 1 1', 1);\n\n\tit = ToStatus(it);\n\tif (it <= 1)\n\t\treturn;\n\titname = ftos(it);\n\tit = strlen(itname);\n\tdrawstring(pos + IMGSIZEV - '0 8 0' - '8 0 0'*it, itname, '8 8 8', '1 1 1', 1);\n};\n\nvoid() Invent_Draw =\n{\n\tlocal float i;\n\tlocal float it;\n\tlocal string itname;\n\tlocal float op;\n\t\n\tlocal float height;\n\theight = cvar(\"vid_conheight\");\n\t//how much space have we got for the slider?\n\n\n\tlocal float slotofs;\n\tslotofs = sliderpos*(height - (IMGSIZEF*3)) + (IMGSIZEF*3);\n\n\tfor (i = 3; i < MAXSLOTS; i++)\n\t{\n\t\tSlotImage(((i*IMGSIZEF-slotofs) * '0 1 0'), i+1);\n\n//\t\titname = GetItemName(ToIID(it));\n//\t\tdrawstring(toppos + ((i*8-slotofs) * '0 1 0'), strcat(itname, \" (\", ftos(ToStatus(it)), \")\"), '8 8 0', '1 1 1', 1);\n\t}\n\n\tdrawstring('112 248 0', \"   HAND 1   \", '8 8 8', '1 1 1', 1);\n\tSlotImage('112 256 0', 1);\n\tdrawstring('112 248 0'+('1 0 0'*IMGSIZEF), \"   HAND 2   \", '8 8 8', '1 1 1', 1);\n\tSlotImage('112 256 0'+('1 0 0'*IMGSIZEF), 2);\n\tdrawstring('112 248 0'+('2 0 0'*IMGSIZEF), \"   ARMOUR   \", '8 8 8', '1 1 1', 1);\n\tSlotImage('112 256 0'+('2 0 0'*IMGSIZEF), 3);\n\n\ti = getstati(62);\n\tif (i == 1)\n\t\titname = \"team: rangers\";\n\telse if (i == 2)\n\t\titname = \"team: raiders\";\n\telse\n\t\titname = \"team: none\";\n\tdrawstring('112 264 0'+('0 1 0'*IMGSIZEF), itname, '8 8 8', '1 1 1', 1);\n\ti = getstati(63);\n\tif (i == 1)\n\t\titname = \"class: medic\";\n\telse if (i == 2)\n\t\titname = \"class: assasin\";\n\telse if (i == 3)\n\t\titname = \"class: soldier\";\n\telse if (i == 4)\n\t\titname = \"class: scientist\";\n\telse\n\t\titname = \"class: none\";\n\tdrawstring('112 272 0'+('0 1 0'*IMGSIZEF), itname, '8 8 8', '1 1 1', 1);\n\n\n\n\tdrawpic(('1 0 0'*IMGSIZEF), \"gui/scrollup.jpg\", '16 16 0', '1 1 1', 1);\n\tfor (i = 0; i < height; i+=16)\n\t\tdrawpic('1 0 0'*IMGSIZEF+'0 16 0'+i*'0 1 0', \"gui/scrollbar.jpg\", '16 16 0', '1 1 1', 1);\n\tdrawpic(('1 0 0'*IMGSIZEF)+(height-16)*'0 1 0', \"gui/scrolldown.jpg\", '16 16 0', '1 1 1', 1);\n\n\tdrawpic(('1 0 0'*IMGSIZEF)+'0 16 0'+sliderpos*(height-48)*'0 1 0', \"gui/scrollbox.jpg\", '16 16 0', '1 1 1', 1);\n\n\n\tif (showcontextmenu)\n\t{\n\t\top = floor((mousepos_y - contextpos_y)/8);\n\t\tdrawfill(contextpos - '8 8 0', '88 24 0'+'16 16 0', '0 0 0', 0.7);\n\t\tdrawstring(contextpos + (0 * '0 8 0'), \"Use/Reload\", '8 8 0', '1 1 0' + (op!=0)*'0 0 1', 1);\n\t\tdrawstring(contextpos + (1 * '0 8 0'), \"Put In Hand\", '8 8 0', '1 1 0' + (op!=1)*'0 0 1', 1);\n\t\tdrawstring(contextpos + (2 * '0 8 0'), \"Drop\", '8 8 0', '1 1 0' + (op!=2)*'0 0 1', 1);\n\t}\n\telse\n\t{\n\t\tif (mousepos_x >= IMGSIZEF)\n\t\t{\n\t\t\tslotnum = 0;\n\t\t\tif (mousepos_x >= 112 && mousepos_x <= 112+3*IMGSIZEF)\n\t\t\t\tif (mousepos_y >= 256 && mousepos_y <= 256+IMGSIZEF)\n\t\t\t\t\tslotnum = floor((mousepos_x - 112)/IMGSIZEF) + 1;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tslotnum = floor((mousepos_y + slotofs)/IMGSIZEF) + 1;\n\t\t}\n//\t\t\tslotnum = floor((mousepos_y - toppos_y)/8) + 1;\n\t}\n\n\tdrawstring('128 0 0', ftos(slotnum), '8 8 0', '1 1 1', 1);\n\n\tit = getstati(31+slotnum);\n\titname = GetItemName(ToIID(it));\n\tdrawstring('128 32 0', itname, '8 8 0', '1 1 1', 1);\n\titname = GetItemDesc(ToIID(it));\n\tdrawstring('128 48 0', itname, '8 8 0', '1 1 1', 1);\n\n\n//\tdrawfill(mousepos, '8 8 0', '0 0 0', 0.7);\n//\tdrawstring(mousepos, \"^\", '8 8 0', '1 1 1', 1);\n\tdrawpic(mousepos, \"gui/cursor.jpg\", '1 1 0'*CURSORSIZE, '1 1 1', 1);\n};\n\nvoid() CalcScrollPos =\n{\n\tsliderpos = (mousepos_y-24)/(cvar(\"vid_conheight\")-48);\n\tif (sliderpos < 0)\n\t\tsliderpos = 0;\n\tif (sliderpos > 1)\n\t\tsliderpos = 1;\n};\n\nfloat(float eventtype, float param1, float param2) CSQC_InputEvent =\n{\n\tlocal float op;\n\tif (eventtype == 0)\t//key down\n\t{\n\t\tif (param1 == 'i')\n\t\t{\n\t\t\tshow_inventory = !show_inventory;\n\t\t}\n\t\telse if (!show_inventory)\n\t\t\treturn false;\n\t\telse if (param1 == k_mouse1)\n\t\t{\n\t\t\tif (mousepos_x >= IMGSIZEF && mousepos_x <= IMGSIZEF+16)\n\t\t\t{\n\t\t\t\tmouseisdown = true;\n\t\t\t\tCalcScrollPos();\n\t\t\t}\n\t\t\telse\n\t\t\t\tdownslotnum = slotnum;\n\t\t}\n\t\telse if (param1 == k_mwheelup)\n\t\t{\n\t\t\tsliderpos = sliderpos - 0.05;\n\t\t\tif (sliderpos < 0)\n\t\t\t\tsliderpos = 0;\n\t\t}\n\t\telse if (param1 == k_mwheeldown)\n\t\t{\n\t\t\tsliderpos = sliderpos + 0.05;\n\t\t\tif (sliderpos > 1)\n\t\t\t\tsliderpos = 1;\n\t\t}\n\t\telse\n\t\t\treturn false;\n\t\treturn true;\n\t}\n\tif (eventtype == 1 && show_inventory)\t//key up\n\t{\n\t\tif (param1 == k_mouse1)\n\t\t{\t\t\n\t\t\tif (showcontextmenu)\n\t\t\t{\n\t\t\t\top = floor((mousepos_y - contextpos_y)/8);\n\n\t\t\t\tif (op == 0)\n\t\t\t\t\tlocalcmd(strcat(\"cmd invuse \", ftos(slotnum), \"\\n\"));\n\t\t\t\telse if (op == 1)\n\t\t\t\t\tlocalcmd(strcat(\"cmd invswap 1 \", ftos(slotnum), \"\\nimpulse 1\\n\"));\n\t\t\t\telse if (op == 2)\n\t\t\t\t\tlocalcmd(strcat(\"cmd invdrop \", ftos(slotnum), \"\\n\"));\n\n\n\t\t\t\tshowcontextmenu = false;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (mouseisdown)\n\t\t\t\t{\t//within the scrollbar\n\t\t\t\t\tmouseisdown = false;\n\t\t\t\t}\n\n\t\t\t\telse if (downslotnum == slotnum)\t//mouse didn't move away\n\t\t\t\t{\n\t\t\t\t\tif (slotnum >= 1 && slotnum <= MAXSLOTS)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (getstati(32+slotnum-1) != 0)\t//if there's actually an item there\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tshowcontextmenu = true;\t//show the context menu\n\t\t\t\t\t\t\tcontextpos = mousepos;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tshow_inventory = false;\t//they clicked outside, fools!\n\t\t\t\t\t\tshowcontextmenu = false;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t//they dragged\n\t\t\t\t\tshowcontextmenu = false;\n\t\t\t\t\tlocalcmd(strcat(\"cmd invswap \", ftos(downslotnum), \" \", ftos(slotnum), \"\\nimpulse 1\\n\"));\n\t\t\t\t}\n\t\t\t}\n\t\t\tmouseisdown = false;\n\t\t\tdownslotnum = 0;\n\t\t}\n\t\telse\n\t\t\treturn false;\n\t\treturn true;\n\t}\n\tif (eventtype == 2 && show_inventory)\t//mouse\n\t{\n\t\tmousepos_x += param1;\n\t\tmousepos_y += param2;\n\n\t\tif (mousepos_x < 0)\n\t\t\tmousepos_x = 0;\n\t\tif (mousepos_y < 0)\n\t\t\tmousepos_y = 0;\n\n\t\tif (mouseisdown)\n\t\t\tCalcScrollPos();\n\n\t\treturn true;\n\t}\n\treturn false;\n};\n\nvoid() FigureOutButtons =\n{\n\tk_mouse1 = stringtokeynum(\"K_MOUSE1\");\n\tk_mwheelup = stringtokeynum(\"K_MWHEELUP\");\n\tk_mwheeldown = stringtokeynum(\"K_MWHEELDOWN\");\n};\n"
  },
  {
    "path": "quakec/fallout2/csqc/main.qc",
    "content": "entity viewentity;\nfloat viewmodelindex;\n\n.float lerptime;\n\nvoid() DoThatViewModelThing =\n{\n\tfloat newframe, newmodel;\n\n\tnewframe = getstati(STAT_WEAPONFRAME);\n\tnewmodel = getstati(STAT_WEAPONMODEL);\n\n\tif (newmodel != viewmodelindex)\n\t{\t//changed entirly.\n\t\tviewmodelindex = newmodel;\n\t\tself.frame2 = self.frame = newframe;\n\t\tself.lerptime = time;\n\t}\n\telse if (newframe != self.frame)\n\t{\n\t\tself.frame2 = self.frame;\n\t\tself.frame = newframe;\n\t\tself.lerptime = time;\n\t}\n\tself.lerpfrac = 1-(time-self.lerptime)*10;\n\n\tif (self.lerpfrac < 0) self.lerpfrac = 0;\n\n\n\tself.modelindex = viewmodelindex;\n};\n\nvoid() CSQC_Init =\n{\n\tviewentity = spawn();\n\tviewentity.predraw = DoThatViewModelThing;\n\tviewentity.drawmask = MASK_NORMAL;\n\tviewentity.renderflags = RF_VIEWMODEL | RF_DEPTHHACK;\n\n\n\tFigureOutButtons();\n};\n\n\n\nvoid(float do2d) CSQC_UpdateView =\n{\n\tfloat width,height;\n\tfloat usehud;\n\twidth = cvar(\"vid_conwidth\");\n\theight = cvar(\"vid_conheight\");\n\n\tclearscene();\n\n\tif (0)//!cvar(\"cg_usehud\"))\n\t{\n\t\tsetviewprop(VF_DRAWENGINESBAR, 0);\n\t\tusehud = true;\n\t}\n\telse\n\t{\n\t\tsetviewprop(VF_DRAWENGINESBAR, 1);\n\t}\n\n\tsetviewprop(VF_DRAWCROSSHAIR, 1);\n\n\t//force fullscreen views.\n\tsetviewprop(VF_MIN, '0 0 0');\n\tsetviewprop(VF_SIZE_X, width);\n\tsetviewprop(VF_SIZE_Y, height);\n\n\taddentities(MASK_ENGINE | MASK_NORMAL);\n\n\trenderscene();\n\n//\tif (usehud)\n//\t\tHud_Draw();\n\n\tif (show_inventory)\n\t\tInvent_Draw();\n};"
  },
  {
    "path": "quakec/fallout2/csqc/stdbuiltins.qc",
    "content": "//common builtins.\n\nvoid(vector ang)\tmakevectors\t\t= #1;\t\t// sets v_forward, etc globals\nvoid(entity e, vector o) setorigin\t= #2;\nvoid(entity e, string m) setmodel\t= #3;\t\t// set movetype and solid first\nvoid(entity e, vector min, vector max) setsize = #4;\n\nvoid() break\t\t\t\t\t\t= #6;\nfloat() random\t\t\t\t\t\t= #7;\t\t// returns 0 - 1\nvoid(entity e, float chan, string samp, float vol, float atten) sound = #8;\nvector(vector v) normalize\t\t\t= #9;\nvoid(string e) error\t\t\t\t= #10;\nvoid(string e) objerror\t\t\t\t= #11;\nfloat(vector v) vlen\t\t\t\t= #12;\nfloat(vector v) vectoyaw\t\t\t= #13;\nentity() spawn\t\t\t\t\t\t= #14;\nvoid(entity e) remove\t\t\t\t= #15;\n\n// sets trace_* globals\n// nomonsters can be:\n// An entity will also be ignored for testing if forent == test,\n// forent->owner == test, or test->owner == forent\n// a forent of world is ignored\nvoid(vector v1, vector v2, float nomonsters, entity forent) traceline = #16;\n\n\nentity(entity start, .string fld, string match) find = #18;\nstring(string s) precache_sound\t\t= #19;\nstring(string s) precache_model\t\t= #20;\nvoid(entity client, string s)stuffcmd = #21;\nentity(vector org, float rad) findradius = #22;\n\nvoid(string s, ...) dprint\t\t\t\t= #25;\nstring(float f) ftos\t\t\t\t= #26;\nstring(vector v) vtos\t\t\t\t= #27;\nvoid() coredump\t\t\t\t\t\t= #28;\t\t// prints all edicts\nvoid() traceon\t\t\t\t\t\t= #29;\t\t// turns statment trace on\nvoid() traceoff\t\t\t\t\t\t= #30;\nvoid(entity e) eprint\t\t\t\t= #31;\t\t// prints an entire edict\n\nfloat() droptofloor= #34;\t// TRUE if landed on floor\n\nfloat(float v) rint\t\t\t\t\t= #36;\t\t// round to nearest int\nfloat(float v) floor\t\t\t\t= #37;\t\t// largest integer <= v\nfloat(float v) ceil\t\t\t\t\t= #38;\t\t// smallest integer >= v\n\nfloat(vector v) pointcontents\t\t= #41;\t\t// returns a CONTENT_*\n\nfloat(float f) fabs = #43;\n\nfloat(string s) cvar = #45;\t\t\t\t\t\t// return cvar.value\nvoid(string s, ...) localcmd = #46;\t\t\t\t\t// put string into local que\nentity(entity e) nextent = #47;\t\t\t\t\t// for looping through all ents\nvoid(vector o, vector d, float color, float count) particle = #48;// start a particle effect\nvoid() ChangeYaw = #49;\t\t\t\t\t\t// turn towards self.ideal_yaw\n\t\t\t\t\t\t\t\t\t\t\t// at self.yaw_speed\n\nvector(vector v) vectoangles\t\t\t= #51;\n\n\n\n\nvoid(string var, string val) cvar_set = #72;\t// sets cvar.value\n\n\n\n//string(float modelindex) name_for_modelindex;\n//float(string modelname) modelindex_for_name;\n\nstring(string s) precache_file          = #68;  // no effect except for -copy\nstring(string s) precache_model2        = #75;          // registered version only\nstring(string s) precache_sound2        = #76;          // registered version only\nstring(string s) precache_file2         = #77;          // registered version only\n\n\n\nfloat(string s) stof\t= #81;\n\n\nvoid(vector v1, vector min, vector max, vector v2, float nomonsters, entity forent) tracebox = #90;\nvoid(string name, string value) registercvar = #93;\n\nentity(entity start, .entity fld, entity match) findentity = #98;\nentity(entity start, .float fld, float match) findfloat = #98;\n\n\n\nfloat(string filename, float mode) fopen = #110;\nvoid(float fhandle) fclose = #111;\nstring(float fhandle) fgets = #112;\nvoid(float fhandle, string s) fputs = #113;\nfloat(string s) strlen = #114;\nstring(...) strcat = #115;\nstring(string s, float start, float length) substring = #116;\nvector(string s) stov = #117;\nstring(string s) strzone = #118;\nvoid(string s) strunzone = #119;\nfloat FILE_READ = 0;\nfloat FILE_WRITE = 1;\nfloat FILE_APPEND = 2;\n\n\n\n\n\n\n\n\n\n\n\nfloat(string s) tokenize = #441;\nstring(float argnum) argv = #442;\n\n\n//DP_QC_SINCOSSQRTPOW\n//idea: id Software, LordHavoc\n//darkplaces implementation: id Software, LordHavoc\n//builtin definitions:\nfloat(float val) sin = #60;\nfloat(float val) cos = #61;\nfloat(float val) sqrt = #62;\nfloat(float a, float b) pow = #97;\n//description:\n//useful math functions, sine of val, cosine of val, square root of val, and raise a to power b, respectively.\n\n\n\n\n//FTE_STRINGS\nfloat(string str, string match) strstrofs = #221;\nfloat(string str, float ofs) str2chr = #222;\nstring(float c, ...) chr2str = #223;\nstring(float ccase, float redalpha, float redchars, string str) strconv = #224;\nstring(string info, string key, string value) infoadd = #226;\nstring(string info, string key) infoget = #227;\nfloat(string s1, string s2, float len) strncmp = #228;\nfloat(string s1, string s2) strcasecmp = #229;\nfloat(string s1, string s2, float len) strncasecmp = #230;\n\n\nfloat(entity ent, string tagname) gettagindex = #451;\nvector(entity ent, float tagnum) RotateVectorsByTag = #234;\nvoid(vector angle) RotateVectorsByAngle = #235;\nvoid(vector fwd, vector right, vector up) RotateVectorsByVectors = #236;\n\nfloat(float modelindex, string skinname) skinforname = #237;\nfloat(string skinname) shaderforname = #238;\n"
  },
  {
    "path": "quakec/fallout2/csqc/stdconstants.qc",
    "content": "float\tFALSE\t\t\t\t\t= 0;\nfloat \tTRUE\t\t\t\t\t= 1;\nnoref float\tfalse = FALSE;\nfloat\ttrue = TRUE;\n\n// edict.flags\nfloat\tFL_FLY\t\t\t\t\t= 1;\nfloat\tFL_SWIM\t\t\t\t\t= 2;\nfloat\tFL_CLIENT\t\t\t\t= 8;\t// set for all client edicts\nfloat\tFL_INWATER\t\t\t\t= 16;\t// for enter / leave water splash\nfloat\tFL_MONSTER\t\t\t\t= 32;\nfloat\tFL_GODMODE\t\t\t\t= 64;\t// player cheat\nfloat\tFL_NOTARGET\t\t\t\t= 128;\t// player cheat\nfloat\tFL_ITEM\t\t\t\t\t= 256;\t// extra wide size for bonus items\nfloat\tFL_ONGROUND\t\t\t\t= 512;\t// standing on something\nfloat\tFL_PARTIALGROUND\t\t= 1024;\t// not all corners are valid\nfloat\tFL_WATERJUMP\t\t\t= 2048;\t// player jumping out of water\nfloat\tFL_JUMPRELEASED\t\t\t= 4096;\t// for jump debouncing\n\n// edict.solid values\nfloat\tSOLID_TRIGGER\t\t\t= 1;\t// touch on edge, but not blocking\nfloat\tSOLID_BBOX\t\t\t\t= 2;\t// touch on edge, block\nfloat\tSOLID_SLIDEBOX\t\t\t= 3;\t// touch on edge, but not an onground\nfloat\tSOLID_BSP\t\t\t\t= 4;\t// bsp clip, touch on edge, block\n\n// edict.movetype values\nfloat\tMOVETYPE_NONE\t\t\t= 0;\t// never moves\n//float\tMOVETYPE_ANGLENOCLIP\t= 1;\n//float\tMOVETYPE_ANGLECLIP\t\t= 2;\nfloat\tMOVETYPE_WALK\t\t\t= 3;\t// players only\nfloat\tMOVETYPE_STEP\t\t\t= 4;\t// discrete, not real time unless fall\nfloat\tMOVETYPE_FLY\t\t\t= 5;\nfloat\tMOVETYPE_TOSS\t\t\t= 6;\t// gravity\nfloat\tMOVETYPE_PUSH\t\t\t= 7;\t// no clip to world, push and crush\nfloat\tMOVETYPE_NOCLIP\t\t\t= 8;\nfloat\tMOVETYPE_FLYMISSILE\t\t= 9;\t// fly with extra size against monsters\nfloat\tMOVETYPE_BOUNCE\t\t\t= 10;\nfloat\tMOVETYPE_BOUNCEMISSILE\t= 11;\t// bounce with extra size\n\nfloat\tSOLID_NOT=0;\n\nvector\tVEC_ORIGIN = '0 0 0';\nvector\tVEC_HULL_MIN = '-16 -16 -24';\nvector\tVEC_HULL_MAX = '16 16 32';\n\nvector\tVEC_HULL2_MIN = '-32 -32 -24';\nvector\tVEC_HULL2_MAX = '32 32 64';\n\n// items\nfloat\tIT_AXE\t\t\t\t\t= 4096;\nfloat\tIT_SHOTGUN\t\t\t\t= 1;\nfloat\tIT_SUPER_SHOTGUN\t\t= 2;\nfloat\tIT_NAILGUN\t\t\t\t= 4;\nfloat\tIT_SUPER_NAILGUN\t\t= 8;\nfloat\tIT_GRENADE_LAUNCHER\t\t= 16;\nfloat\tIT_ROCKET_LAUNCHER\t\t= 32;\nfloat\tIT_LIGHTNING\t\t\t= 64;\nfloat\tIT_EXTRA_WEAPON\t\t\t= 128;\n\nfloat\tIT_SHELLS\t\t\t\t= 256;\nfloat\tIT_NAILS\t\t\t\t= 512;\nfloat\tIT_ROCKETS\t\t\t\t= 1024;\nfloat\tIT_CELLS\t\t\t\t= 2048;\n\nfloat\tIT_ARMOR1\t\t\t\t= 8192;\nfloat\tIT_ARMOR2\t\t\t\t= 16384;\nfloat\tIT_ARMOR3\t\t\t\t= 32768;\nfloat\tIT_SUPERHEALTH\t\t\t= 65536;\n\nfloat\tIT_KEY1\t\t\t\t\t= 131072;\nfloat\tIT_KEY2\t\t\t\t\t= 262144;\n\nfloat\tIT_INVISIBILITY\t\t\t= 524288;\nfloat\tIT_INVULNERABILITY\t\t= 1048576;\nfloat\tIT_SUIT\t\t\t\t\t= 2097152;\nfloat\tIT_QUAD\t\t\t\t\t= 4194304;"
  },
  {
    "path": "quakec/fallout2/csqc/system.qc",
    "content": "//Don't modify this file\n\n#define CSQC\t//so code sections can be compiled for either progs\n/*\n==============================================================================\n\n\t\t\tSOURCE FOR GLOBALVARS_T C STRUCTURE\n\n==============================================================================\n*/\n\n//\n// system globals\n//\nentity\t\tself;\nentity\t\tother;\nentity\t\tworld;\nfloat\t\ttime;\nfloat\t\tframetime;\n\nfloat \t\tplayer_localentnum;\t//the entnum\nfloat \t\tplayer_localnum;\t//the playernum\nfloat\t\tmaxclients;\t//a constant filled in by the engine. gah, portability eh?\n\nfloat\t\tclientcommandframe;\t//player movement\nfloat\t\tservercommandframe;\t//clientframe echoed off the server\n\nstring\t\tmapname;\t//current brief map name\nfloat\t\tintermission;\t//non-zero if the server sent an svc_intermission.\n\n//\n// global variables set by built in functions\n//\nvector\t\tv_forward, v_up, v_right;\t// set by makevectors()\n\n// set by traceline / tracebox\nfloat\t\ttrace_allsolid;\nfloat\t\ttrace_startsolid;\nfloat\t\ttrace_fraction;\nvector\t\ttrace_endpos;\nvector\t\ttrace_plane_normal;\nfloat\t\ttrace_plane_dist;\nentity\t\ttrace_ent;\nfloat\t\ttrace_inopen;\nfloat\t\ttrace_inwater;\n\n//these fields are read and set by the default player physics\nvector\t\tpmove_org;\nvector\t\tpmove_vel;\nvector\t\tpmove_mins;\nvector\t\tpmove_maxs;\n//retrieved from the current movement commands (read by player physics)\nfloat\t\tinput_timelength;\nvector\t\tinput_angles;\nvector\t\tinput_movevalues;\t//forwards, right, up.\nfloat\t\tinput_buttons;\t\t//attack, use, jump (default physics only uses jump)\n\nfloat\t\tmovevar_gravity;\nfloat\t\tmovevar_stopspeed;\nfloat\t\tmovevar_maxspeed;\nfloat\t\tmovevar_spectatormaxspeed;\t//used by NOCLIP movetypes.\nfloat\t\tmovevar_accelerate;\nfloat\t\tmovevar_airaccelerate;\nfloat\t\tmovevar_wateraccelerate;\nfloat\t\tmovevar_friction;\nfloat\t\tmovevar_waterfriction;\nfloat\t\tmovevar_entgravity;\t//the local player's gravity field. Is a multiple (1 is the normal value)\n\n//================================================\nvoid\t\tend_sys_globals;\t\t// flag for structure dumping\n//================================================\n\n/*\n==============================================================================\n\n\t\t\tSOURCE FOR ENTVARS_T C STRUCTURE\n\n==============================================================================\n*/\n\n//\n// system fields (*** = do not set in prog code, maintained by C code)\n//\n.float\t\tmodelindex;\t\t// *** model index in the precached list\n.vector\t\tabsmin, absmax;\t// *** origin + mins / maxs\n\n.float\t\tentnum;\t// *** the ent number as on the server\n.float\t\tdrawmask;\n.void()\t\tpredraw;\n\n.float\t\tmovetype;\n.float\t\tsolid;\n\n.vector\t\torigin;\t\t\t// ***\n.vector\t\toldorigin;\t\t// ***\n.vector\t\tvelocity;\n.vector\t\tangles;\n.vector\t\tavelocity;\n\n.string\t\tclassname;\t\t// spawn function\n\n.float\t\trenderflags;\n.string\t\tmodel;\n.float\t\tframe;\n.float\t\tframe2;\n.float\t\tlerpfrac;\t//the ammount of frame2 to use.\n.float\t\tskin;\n\n.vector\t\tmins, maxs;\t\t// bounding box extents reletive to origin\n.vector\t\tsize;\t\t\t// maxs - mins\n\n.void()\t\ttouch;\n.void()\t\tuse;\n.void()\t\tthink;\n.void()\t\tblocked;\t\t// for doors or plats, called when can't push other\n\n.float\t\tnextthink;\n\n.entity\t\tchain;\n\n.entity \tenemy;\n\n.float\t\tflags;\n\n.float\t\tcolormap;\n\n.entity\t\towner;\t\t// who launched a missile\n\n//================================================\nvoid\t\tend_sys_fields;\t\t\t// flag for structure dumping\n//================================================\n"
  },
  {
    "path": "quakec/fallout2/defs.qc",
    "content": "\n/*\n==============================================================================\n\n\t\t\tSOURCE FOR GLOBALVARS_T C STRUCTURE\n\n==============================================================================\n*/\n\n//\n// system globals\n//\nentity          self;\nentity          other;\nentity          world;\nfloat           time;\nfloat           frametime;\n\nentity          newmis;                         // if this is set, the entity that just\n\t\t\t\t\t\t\t\t// run created a new missile that should\n\t\t\t\t\t\t\t\t// be simulated immediately\n\n\nfloat           force_retouch;          // force all entities to touch triggers\n\t\t\t\t\t\t\t\t// next frame.  this is needed because\n\t\t\t\t\t\t\t\t// non-moving things don't normally scan\n\t\t\t\t\t\t\t\t// for triggers, and when a trigger is\n\t\t\t\t\t\t\t\t// created (like a teleport trigger), it\n\t\t\t\t\t\t\t\t// needs to catch everything.\n\t\t\t\t\t\t\t\t// decremented each frame, so set to 2\n\t\t\t\t\t\t\t\t// to guarantee everything is touched\nstring          mapname;\n\nfloat           serverflags;            // propagated from level to level, used to\n\t\t\t\t\t\t\t\t// keep track of completed episodes\n\nfloat           total_secrets;\nfloat           total_monsters;\n\nfloat           found_secrets;          // number of secrets found\nfloat           killed_monsters;        // number of monsters killed\n\n\n// spawnparms are used to encode information about clients across server\n// level changes\nfloat           parm1, parm2, parm3, parm4, parm5, parm6, parm7, parm8, parm9, parm10, parm11, parm12, parm13, parm14, parm15, parm16;\n\n//\n// global variables set by built in functions\n//      \nvector          v_forward, v_up, v_right;       // set by makevectors()\n\t\n// set by traceline / tracebox\nfloat           trace_allsolid;\nfloat           trace_startsolid;\nfloat           trace_fraction;\nvector          trace_endpos;\nvector          trace_plane_normal;\nfloat           trace_plane_dist;\nentity          trace_ent;\nfloat           trace_inopen;\nfloat           trace_inwater;\n\nentity          msg_entity;                             // destination of single entity writes\n\n//\n// required prog functions\n//\nvoid()          main;                                           // only for testing\n\nvoid()          StartFrame;\n\nvoid()          PlayerPreThink;\nvoid()          PlayerPostThink;\n\nvoid()          ClientKill;\nvoid()          ClientConnect;\nvoid()          PutClientInServer;              // call after setting the parm1... parms\nvoid()          ClientDisconnect;\n\nvoid()          SetNewParms;                    // called when a client first connects to\n\t\t\t\t\t\t\t\t\t// a server. sets parms so they can be\n\t\t\t\t\t\t\t\t\t// saved off for restarts\n\nvoid()          SetChangeParms;                 // call to set parms for self so they can\n\t\t\t\t\t\t\t\t\t// be saved for a level transition\n\n\n//================================================\nvoid            end_sys_globals;                // flag for structure dumping\n//================================================\n\n/*\n==============================================================================\n\n\t\t\tSOURCE FOR ENTVARS_T C STRUCTURE\n\n==============================================================================\n*/\n\n//\n// system fields (*** = do not set in prog code, maintained by C code)\n//\n.float          modelindex;             // *** model index in the precached list\n.vector         absmin, absmax; // *** origin + mins / maxs\n\n.float          ltime;                  // local time for entity\n.float          lastruntime;    // *** to allow entities to run out of sequence\n\n.float          movetype;\n.float          solid;\n\n.vector         origin;                 // ***\n.vector         oldorigin;              // ***\n.vector         velocity;\n.vector         angles;\n.vector         avelocity;\n\n.string         classname;              // spawn function\n.string         model;\n.float          frame;\n.float          skin;\n.float          effects;\n\n.vector         mins, maxs;             // bounding box extents reletive to origin\n.vector         size;                   // maxs - mins\n\n.void()         touch;\n.void()         use;\n.void()         think;\n.void()         blocked;                // for doors or plats, called when can't push other\n\n.float          nextthink;\n.entity         groundentity;\n\n\n\n// stats\n.float          health;\n.float          frags;\n.float          weapon;                 // one of the IT_SHOTGUN, etc flags\n.string         weaponmodel;\n.float          weaponframe;\n.float          currentammo;\n.float          ammo_shells, ammo_nails, ammo_rockets, ammo_cells;\n\n.float          items;                  // bit flags\n\n.float          takedamage;\n.entity         chain;\n.float          deadflag;\n\n.vector         view_ofs;                       // add to origin to get eye point\n\n\n.float          button0;                // fire\n.float          button1;                // use\n.float          button2;                // jump\n\n.float          impulse;                // weapon changes\n\n.float          fixangle;\n.vector         v_angle;                // view / targeting angle for players\n\n.string         netname;\n\n.entity         enemy;\n\n.float          flags;\n\n.float          colormap;\n.float          team;\n\n.float          max_health;             // players maximum health is stored here\n\n.float          teleport_time;  // don't back up\n\n.float          armortype;              // save this fraction of incoming damage\n.float          armorvalue;\n\n.float          waterlevel;             // 0 = not in, 1 = feet, 2 = wast, 3 = eyes\n.float          watertype;              // a contents value\n\n.float          ideal_yaw;\n.float          yaw_speed;\n\n.entity         aiment;\n\n.entity         goalentity;             // a movetarget or an enemy\n\n.float          spawnflags;\n\n.string         target;\n.string         targetname;\n\n// damage is accumulated through a frame. and sent as one single\n// message, so the super shotgun doesn't generate huge messages\n.float          dmg_take;\n.float          dmg_save;\n.entity         dmg_inflictor;\n\n.entity         owner;          // who launched a missile\n.vector         movedir;        // mostly for doors, but also used for waterjump\n\n.string         message;                // trigger messages\n\n.float          sounds;         // either a cd track number or sound number\n\n.string         noise, noise1, noise2, noise3;  // contains names of wavs to play\n\n//================================================\nvoid            end_sys_fields;                 // flag for structure dumping\n//================================================\n\n\n#define team team_no\n.float team_no;\n\n\nfloat           parm17, parm18, parm18, parm19, parm20, parm21, parm22, parm23, parm24, parm25, parm26, parm27, parm28, parm29, parm30, parm31, parm32;\n\n\n\n/*\n==============================================================================\n\n\t\t\t\tVARS NOT REFERENCED BY C CODE\n\n==============================================================================\n*/\n\n\n//\n// constants\n//\n\nfloat   FALSE                                   = 0;\nfloat   false                                   = 0;\nfloat   TRUE                                    = 1;\nfloat   true                                    = 1;\n\n// edict.flags\nfloat   FL_FLY                                  = 1;\nfloat   FL_SWIM                                 = 2;\nfloat   FL_CLIENT                               = 8;    // set for all client edicts\nfloat   FL_INWATER                              = 16;   // for enter / leave water splash\nfloat   FL_MONSTER                              = 32;\nfloat   FL_GODMODE                              = 64;   // player cheat\nfloat   FL_NOTARGET                             = 128;  // player cheat\nfloat   FL_ITEM                                 = 256;  // extra wide size for bonus items\nfloat   FL_ONGROUND                             = 512;  // standing on something\nfloat   FL_PARTIALGROUND                = 1024; // not all corners are valid\nfloat   FL_WATERJUMP                    = 2048; // player jumping out of water\nfloat   FL_JUMPRELEASED                 = 4096; // for jump debouncing\n\n// edict.movetype values\nfloat   MOVETYPE_NONE                   = 0;    // never moves\n//float MOVETYPE_ANGLENOCLIP    = 1;\n//float MOVETYPE_ANGLECLIP              = 2;\nfloat   MOVETYPE_WALK                   = 3;    // players only\nfloat   MOVETYPE_STEP                   = 4;    // discrete, not real time unless fall\nfloat   MOVETYPE_FLY                    = 5;\nfloat   MOVETYPE_TOSS                   = 6;    // gravity\nfloat   MOVETYPE_PUSH                   = 7;    // no clip to world, push and crush\nfloat   MOVETYPE_NOCLIP                 = 8;\nfloat   MOVETYPE_FLYMISSILE             = 9;    // fly with extra size against monsters\nfloat   MOVETYPE_BOUNCE                 = 10;\nfloat   MOVETYPE_BOUNCEMISSILE  = 11;   // bounce with extra size\n\n// edict.solid values\nfloat   SOLID_NOT                               = 0;    // no interaction with other objects\nfloat   SOLID_TRIGGER                   = 1;    // touch on edge, but not blocking\nfloat   SOLID_BBOX                              = 2;    // touch on edge, block\nfloat   SOLID_SLIDEBOX                  = 3;    // touch on edge, but not an onground\nfloat   SOLID_BSP                               = 4;    // bsp clip, touch on edge, block\n\n// range values\nfloat   RANGE_MELEE                             = 0;\nfloat   RANGE_NEAR                              = 1;\nfloat   RANGE_MID                               = 2;\nfloat   RANGE_FAR                               = 3;\n\n// deadflag values\n\nfloat   DEAD_NO                                 = 0;\nfloat   DEAD_DYING                              = 1;\nfloat   DEAD_DEAD                               = 2;\nfloat   DEAD_RESPAWNABLE                = 3;\n\n// takedamage values\n\nfloat   DAMAGE_NO                               = 0;\nfloat   DAMAGE_YES                              = 1;\nfloat   DAMAGE_AIM                              = 2;\n\n// items\nfloat   IT_AXE                                  = 4096;\nfloat   IT_SHOTGUN                              = 1;\nfloat   IT_SUPER_SHOTGUN                = 2;\nfloat   IT_NAILGUN                              = 4;\nfloat   IT_SUPER_NAILGUN                = 8;\nfloat   IT_GRENADE_LAUNCHER             = 16;\nfloat   IT_ROCKET_LAUNCHER              = 32;\nfloat   IT_LIGHTNING                    = 64;\nfloat   IT_EXTRA_WEAPON                 = 128;\n\nfloat   IT_SHELLS                               = 256;\nfloat   IT_NAILS                                = 512;\nfloat   IT_ROCKETS                              = 1024;\nfloat   IT_CELLS                                = 2048;\n\nfloat   IT_ARMOR1                               = 8192;\nfloat   IT_ARMOR2                               = 16384;\nfloat   IT_ARMOR3                               = 32768;\nfloat   IT_SUPERHEALTH                  = 65536;\n\nfloat   IT_KEY1                                 = 131072;\nfloat   IT_KEY2                                 = 262144;\n\nfloat   IT_INVISIBILITY                 = 524288;\nfloat   IT_INVULNERABILITY              = 1048576;\nfloat   IT_SUIT                                 = 2097152;\nfloat   IT_QUAD                                 = 4194304;\n\n// point content values\n\nfloat   CONTENT_EMPTY                   = -1;\nfloat   CONTENT_SOLID                   = -2;\nfloat   CONTENT_WATER                   = -3;\nfloat   CONTENT_SLIME                   = -4;\nfloat   CONTENT_LAVA                    = -5;\nfloat   CONTENT_SKY                             = -6;\n\nfloat   STATE_TOP               = 0;\nfloat   STATE_BOTTOM    = 1;\nfloat   STATE_UP                = 2;\nfloat   STATE_DOWN              = 3;\n\nvector  VEC_ORIGIN = '0 0 0';\nvector  VEC_HULL_MIN = '-16 -16 -24';\nvector  VEC_HULL_MAX = '16 16 32';\n\nvector  VEC_HULL2_MIN = '-32 -32 -24';\nvector  VEC_HULL2_MAX = '32 32 64';\n\n// protocol bytes\nfloat   SVC_TEMPENTITY          = 23;\nfloat   SVC_KILLEDMONSTER       = 27;\nfloat   SVC_FOUNDSECRET         = 28;\nfloat   SVC_INTERMISSION        = 30;\nfloat   SVC_FINALE                      = 31;\nfloat   SVC_CDTRACK                     = 32;\nfloat   SVC_SELLSCREEN          = 33;\nfloat   SVC_SMALLKICK           = 34;\nfloat   SVC_BIGKICK                     = 35;\nfloat   SVC_MUZZLEFLASH         = 39;\n\n\nfloat   TE_SPIKE                = 0;\nfloat   TE_SUPERSPIKE   = 1;\nfloat   TE_GUNSHOT              = 2;\nfloat   TE_EXPLOSION    = 3;\nfloat   TE_TAREXPLOSION = 4;\nfloat   TE_LIGHTNING1   = 5;\nfloat   TE_LIGHTNING2   = 6;\nfloat   TE_WIZSPIKE             = 7;\nfloat   TE_KNIGHTSPIKE  = 8;\nfloat   TE_LIGHTNING3   = 9;\nfloat   TE_LAVASPLASH   = 10;\nfloat   TE_TELEPORT             = 11;\nfloat   TE_BLOOD                = 12;\nfloat   TE_LIGHTNINGBLOOD = 13;\n\nfloat IDLE1    = 3;\nfloat IDLE2    = 4;\nfloat IDLE3    = 5;\nfloat IDLE4    = 6;\nfloat IDLE5    = 7;\nfloat IDLE6    = 8;\nfloat IDLE7    = 9;\nfloat IDLE8    = 10;\nfloat IDLE9    = 11;\nfloat IDLE10    = 12;\nfloat IDLE1A    = 13;\nfloat IDLE2A    = 14;\nfloat IDLE3A    = 15;\nfloat IDLE4A    = 16;\nfloat IDLE5A    = 17;\nfloat IDLE6A    = 18;\nfloat IDLE7A    = 19;\nfloat IDLE8A    = 20;\nfloat IDLE9A    = 21;\nfloat IDLE10A    = 22;\n\nfloat DRAW1    = 23;\nfloat DRAW2    = 24;\nfloat DRAW3    = 25;\nfloat DRAW4    = 26;\n\n// sound channels\n// channel 0 never willingly overrides\n// other channels (1-7) allways override a playing sound on that channel\nfloat   CHAN_AUTO               = 0;\nfloat   CHAN_WEAPON             = 1;\nfloat   CHAN_VOICE              = 2;\nfloat   CHAN_ITEM               = 3;\nfloat   CHAN_BODY               = 4;\nfloat   CHAN_NO_PHS_ADD = 8;    // ie: CHAN_BODY+CHAN_NO_PHS_ADD\n\nfloat   ATTN_NONE               = 0;\nfloat   ATTN_NORM               = 1;\nfloat   ATTN_IDLE               = 2;\nfloat   ATTN_STATIC             = 3;\n\n// update types\n\nfloat   UPDATE_GENERAL  = 0;\nfloat   UPDATE_STATIC   = 1;\nfloat   UPDATE_BINARY   = 2;\nfloat   UPDATE_TEMP             = 3;\n\n// entity effects\n\n//float EF_BRIGHTFIELD  = 1;\n//float EF_MUZZLEFLASH  = 2;\nfloat   EF_BRIGHTLIGHT  = 4;\nfloat   EF_DIMLIGHT     = 8;\nfloat   EF_FLAG1                = 16;\nfloat   EF_FLAG2                = 32;\n// GLQuakeWorld Stuff\nfloat \tEF_BLUE\t\t=\t64;\t// Blue Globe effect for Quad\nfloat\tEF_RED\t\t=\t128;\t// Red Globe effect for Pentagram\n// messages\nfloat   MSG_BROADCAST   = 0;            // unreliable to all\nfloat   MSG_ONE                 = 1;            // reliable to one (msg_entity)\nfloat   MSG_ALL                 = 2;            // reliable to all\nfloat   MSG_INIT                = 3;            // write to the init string\nfloat   MSG_MULTICAST   = 4;            // for multicast() call\n\n// message levels\nfloat   PRINT_LOW               = 0;            // pickup messages\nfloat   PRINT_MEDIUM    \t= 1;            // death messages\nfloat   PRINT_HIGH              = 2;            // critical messages\nfloat   PRINT_CHAT              = 3;            // also goes to chat console\n\n// multicast sets\nfloat   MULTICAST_ALL   = 0;            // every client\nfloat   MULTICAST_PHS   = 1;            // within hearing\nfloat   MULTICAST_PVS   = 2;            // within sight\nfloat   MULTICAST_ALL_R = 3;            // every client, reliable\nfloat   MULTICAST_PHS_R = 4;            // within hearing, reliable\nfloat   MULTICAST_PVS_R = 5;            // within sight, reliable\n\n\n#define FL_FINDABLE_NONSOLID\t16384\t//a cpqwsv feature\n\n\n//================================================\n\n//\n// globals\n//\nfloat   movedist;\n\nstring  string_null;    // null string, nothing should be held here\n//float   empty_float;\n\nentity  activator;              // the entity that activated a trigger or brush\n\nentity  damage_attacker;        // set by T_Damage\nentity  damage_inflictor;\nfloat   framecount;\n\n//\n// cvars checked each frame\n//\nfloat           teamplay;\nfloat           timelimit;\nfloat           fraglimit;\nfloat           deathmatch;\nfloat           coop;\nvar float           rj                      =       1;\nfloat           no_connect;\nfloat           round_end;\nfloat           red_kill;\nfloat           blue_kill;\nfloat           teamthatwon;\nfloat           team_blue;\nfloat           team_red;\nfloat           bombed;\nfloat           zombies;\nfloat           hostages;\nfloat           dead_hostage;\nfloat           team1win;\nfloat           team2win;\nfloat           respawnvar;\nfloat           mode;\nfloat           ff;\nfloat           headshot;\nfloat           z_skill;\nfloat           msgcount;\nfloat           max_zombies;\nfloat           gtimer;\nfloat           total_players;\nfloat           endgame_timer;\n\n//================================================\n\n//\n// world fields (FIXME: make globals)\n//\nnoref .string         wad;\n.string         map;\n.float          worldtype;      // 0=medieval 1=metal 2=base\n\n//================================================\n\n.string         killtarget;\n\n//\n// quakeed fields\n//\nnoref .float          light_lev;              // not used by game, but parsed by light util\n.float          style;\n\n\n//\n// monster ai\n//\n.void()         th_stand;\n.void()         th_walk;\n.void()         th_run;\n.void()         th_missile;\n.void()         th_melee;\n.void(entity attacker, float damage)            th_pain;\n.void()         th_die;\n\n.entity         oldenemy;               // mad at this player before taking damage\n\n.float          speed;\n\n.float  lefty;\n\n.float  search_time;\n.float  attack_state;\n\nfloat   AS_STRAIGHT             = 1;\nfloat   AS_SLIDING              = 2;\nfloat   AS_MELEE                = 3;\nfloat   AS_MISSILE              = 4;\n\n//\n// player only fields\n//\n.float          voided;\n.float          walkframe;\n\n// Zoid Additions\n.float\t\tmaxspeed;\t\t// Used to set Maxspeed on a player\n//.float\t\tgravity;\t\t// Gravity Multiplier (0 to 1.0)\n\n\n\n.float          attack_finished;\n.float          pain_finished;\n\n.float          invincible_finished;\n.float          invisible_finished;\n.float          super_damage_finished;\n.float          radsuit_finished;\n\n.float          invincible_time, invincible_sound;\n.float          invisible_time, invisible_sound;\n.float          super_time, super_sound;\n.float          rad_time;\n.float          fly_sound;\n.float          axhitme;\n\n.float          show_hostile;   // set to time+0.2 whenever a client fires a\n\t\t\t\t\t\t\t// weapon or takes damage.  Used to alert\n\t\t\t\t\t\t\t// monsters that otherwise would let the player go\n.float          jump_flag;              // player jump flag\n.float          swim_flag;              // player swimming sound flag\n.float          air_finished;   // when time > air_finished, start drowning\n.float          bubble_count;   // keeps track of the number of bubbles\n.string         deathtype;              // keeps track of how the player died\n\nfloat   POS_STAND               = 0;\nfloat   POS_DUCK                = 0;\nfloat   POS_PRONE               = 0;\n\nfloat          blue_gadget;\nfloat          red_gadget;\nfloat          blue_weapon;\nfloat          red_weapon;\nfloat          blue_armor;\nfloat          red_armor;\n\n.float          ghost;\n.float          ghostcnt;\n.float          dtime;\n.float          active;\n.float          class;\n//.float          vote1;\n//.float          vote2;\n//.float          vote3;\n//.float          vote4;\n//.float          vote5;\n//.float          vote6;\n//.float          vote7;\n//.float          override;\n.entity         track;\n//.float          recharge;\n.float          processed;\n.float          current_slot;\n.float          attack;\n.float          position;\n.float          recoil;\n.float          critical;\n.float          helmet;\n.float          rtime;\n.float          regen;\n.float          rage;\n.float          ragetime;\n.float          action_points;\n.float          pcamera;\n.float          pcamera2;\n.float          equipment_slot;\n//.float          select;\n//.float          grenade_hold;\n.float          grab;\n//.float          vehicle;\n\n.float          weight;\n.float          max_weight;\n.float          hostage;\n\n.float          sneak;\n.float          rescued;\n.float          scale;\n.float          bandages;\n.float          scraps;\n.float          cycle1;\n.float          cycle2;\n//.float          c4;\n.float          trait;\n.float          protect;\n.float          perk;\n.float          equipment;\n.float          buildtype;\n//.float          ctimer;\n.float          flash;\n.float          oldteam;\n.float          dead;\n.float          grenadetoggle;\n.float          kills;\n.float          materialize;\n.float          vote_time;\n.float          vote_on;\n.float          vote_type;\n.float          vote_num;\n.float          vote1;\n.float          vote2;\n.float          vote3;\n.float          vote4;\n.float          vote5;\n.float          vote6;\n.float          vote7;\n.float          vote8;\n.float          spamdelay;\n\n.string        armornoise;\n.string        ammotype1;\n.string        ammotype2;\n//.string        deathsound;\n.string        currentmenu;\n\n.entity         friend;\n.entity         view2;\n.vector         oldorg;\n.vector         home;\n\n//\n// object stuff\n//\n.string         mdl;\n.vector         mangle;                 // angle at start\n\n.vector         oldorigin;              // only used by secret door\n\n.float          t_length, t_width;\n\n\n//\n// doors, etc\n//\n//.vector         dest;\n.vector         dest1, dest2;\n.float          wait;                   // time from firing to restarting\n.float          delay;                  // time from activation to firing\n.entity         trigger_field;  // door's trigger entity\n.string         noise4;\n\n//\n// monsters\n//\n.float          pausetime;\n.entity         movetarget;\n\n\n//\n// doors\n//\n.float          aflag;\n.float          dmg;                    // damage done by door when hit\n\t\n//\n// misc\n//\n.float          cnt;                    // misc flag\n\t\n//\n// subs\n//\n.void()         think1;\n.vector         finaldest, finalangle;\n\n//\n// triggers\n//\n.float          count;                  // for counting triggers\n\n\n//\n// plats / doors / buttons\n//\n.float          lip;\n.float          state;\n.vector         pos1, pos2;             // top and bottom positions\n.float          height;\n\n//\n// sounds\n//\n.float          waitmin;\n//.float          waitmax;\n//.float          distance;\n//.float          volume;\n\n\n\n\n//===========================================================================\n\t\n\n//\n// builtin functions\n//\n\nvoid(vector ang)        makevectors             = #1;           // sets v_forward, etc globals\nvoid(entity e, vector o) setorigin      = #2;\nvoid(entity e, string m) setmodel       = #3;           // set movetype and solid first\nvoid(entity e, vector min, vector max) setsize = #4;\n// #5 was removed\nvoid() break                                            = #6;\nfloat() random                                          = #7;           // returns 0 - 1\nvoid(entity e, float chan, string samp, float vol, float atten) sound = #8;\nvector(vector v) normalize                      = #9;\nvoid(string e) error                            = #10;\nvoid(string e) objerror                         = #11;\nfloat(vector v) vlen                            = #12;\nfloat(vector v) vectoyaw                        = #13;\nentity() spawn                                          = #14;\nvoid(entity e) remove                           = #15;\n\n// sets trace_* globals\n// nomonsters can be:\n// An entity will also be ignored for testing if forent == test,\n// forent->owner == test, or test->owner == forent\n// a forent of world is ignored\nvoid(vector v1, vector v2, float nomonsters, entity forent) traceline = #16;    \n\nentity() checkclient                            = #17;  // returns a client to look for\nentity(entity start, .string fld, string match) find = #18;\nstring(string s) precache_sound         = #19;\nstring(string s) precache_model         = #20;\nvoid(entity client, string s)stuffcmd = #21;\nentity(vector org, float rad) findradius = #22;\nvoid(float level, string s, ...) bprint                              = #23;\nvoid(entity client, float level, string s, ...) sprint = #24;\nvoid(string s) dprint                           = #25;\nstring(float f) ftos                            = #26;\nstring(vector v) vtos                           = #27;\nvoid() coredump                                         = #28;          // prints all edicts\nvoid() traceon                                          = #29;          // turns statment trace on\nvoid() traceoff                                         = #30;\nvoid(entity e) eprint                           = #31;          // prints an entire edict\nfloat(float yaw, float dist) walkmove   = #32;  // returns TRUE or FALSE\n// #33 was removed\nfloat() droptofloor= #34;  // TRUE if landed on floor\nvoid(float style, string value) lightstyle = #35;\nfloat(float v) rint                                     = #36;          // round to nearest int\nfloat(float v) floor                            = #37;          // largest integer <= v\nfloat(float v) ceil                                     = #38;          // smallest integer >= v\n// #39 was removed\nfloat(entity e) checkbottom                     = #40;          // true if self is on ground\nfloat(vector v) pointcontents           = #41;          // returns a CONTENT_*\n// #42 was removed\nfloat(float f) fabs = #43;\nvector(entity e, float speed) aim = #44;                // returns the shooting vector\nfloat(string s) cvar = #45;                                             // return cvar.value\nvoid(string s) localcmd = #46;                                  // put string into local que\nentity(entity e) nextent = #47;                                 // for looping through all ents\n// #48 was removed\nvoid() ChangeYaw = #49;                                         // turn towards self.ideal_yaw\n\t\t\t\t\t\t\t\t\t\t\t// at self.yaw_speed\n// #50 was removed\nvector(vector v) vectoangles                    = #51;\n\n//\n// direct client message generation\n//\nvoid(float to, float f) WriteByte               = #52;\nvoid(float to, float f) WriteChar               = #53;\nvoid(float to, float f) WriteShort              = #54;\nvoid(float to, float f) WriteLong               = #55;\nvoid(float to, float f) WriteCoord              = #56;\nvoid(float to, float f) WriteAngle              = #57;\nvoid(float to, string s) WriteString    = #58;\nvoid(float to, entity s) WriteEntity    = #59;\n\n// several removed\n\nvoid(float step) movetogoal                             = #67;\n\nstring(string s) precache_file          = #68;  // no effect except for -copy\nvoid(entity e) makestatic               = #69;\nvoid(string s) changelevel = #70;\n\n//#71 was removed\n\nvoid(string var, string val) cvar_set = #72;    // sets cvar.value\n\nvoid(entity client, string s, ...) centerprint = #73;        // sprint, but in middle\n\nvoid(vector pos, string samp, float vol, float atten) ambientsound = #74;\n\nstring(string s) precache_model2        = #75;          // registered version only\nstring(string s) precache_sound2        = #76;          // registered version only\nstring(string s) precache_file2         = #77;          // registered version only\n\nvoid(entity e) setspawnparms            = #78;          // set parm1... to the\n\t\t\t\t\t\t\t\t\t\t\t\t// values at level start\n\t\t\t\t\t\t\t\t\t\t\t\t// for coop respawn\nvoid(entity killer, entity killee) logfrag = #79;       // add to stats\n\nstring(entity e, string key) infokey    = #80;  // get a key value (world = serverinfo)\nfloat(string s) stof                            = #81;          // convert string to float\nvoid(vector where, float set) multicast = #82;  // sends the temp message to a set\n\t\t\t\t\t\t\t\t\t\t\t\t// of clients, possibly in PVS or PHS\n\n\nstring(...) strcat = #115;\n\n//============================================================================\n\n//\n// subs.qc\n//\nvoid(vector tdest, float tspeed, void() func) SUB_CalcMove;\nvoid(entity ent, vector tdest, float tspeed, void() func) SUB_CalcMoveEnt;\nvoid(vector destangle, float tspeed, void() func) SUB_CalcAngleMove;\nvoid()  SUB_CalcMoveDone;\nvoid() SUB_CalcAngleMoveDone;\nvoid() SUB_Null;\nvoid() SUB_UseTargets;\nvoid() SUB_Remove;\n\n//\n//      combat.qc\n//\nvoid(entity targ, entity inflictor, entity attacker, float damage) T_Damage;\n\n\nfloat (entity e, float healamount, float ignore) T_Heal; // health function\n\nfloat(entity targ, entity inflictor) CanDamage;\n\n\n\n\n\n"
  },
  {
    "path": "quakec/fallout2/demon.qc",
    "content": "/*\n==============================================================================\n\nDEMON\n\n==============================================================================\n*/\n\n$cd id1/models/demon3\n$scale\t0.8\n$origin 0 0 24\n$base base\n$skin base\n\n$frame stand1 stand2 stand3 stand4 stand5 stand6 stand7 stand8 stand9\n$frame stand10 stand11 stand12 stand13\n\n$frame walk1 walk2 walk3 walk4 walk5 walk6 walk7 walk8\n\n$frame run1 run2 run3 run4 run5 run6\n\n$frame leap1 leap2 leap3 leap4 leap5 leap6 leap7 leap8 leap9 leap10\n$frame leap11 leap12\n\n$frame pain1 pain2 pain3 pain4 pain5 pain6\n\n$frame death1 death2 death3 death4 death5 death6 death7 death8 death9\n\n$frame attacka1 attacka2 attacka3 attacka4 attacka5 attacka6 attacka7 attacka8\n$frame attacka9 attacka10 attacka11 attacka12 attacka13 attacka14 attacka15\n\n//============================================================================\n\nvoid(float side)\tDemon_Melee;\n\nvoid()\tDemon_JumpTouch;\n\nvoid()\tdemon1_stand1\t=[\t$stand1,\tdemon1_stand2\t] {ai_stand();};\nvoid()\tdemon1_stand2\t=[\t$stand2,\tdemon1_stand3\t] {ai_stand();};\nvoid()\tdemon1_stand3\t=[\t$stand3,\tdemon1_stand4\t] {ai_stand();};\nvoid()\tdemon1_stand4\t=[\t$stand4,\tdemon1_stand5\t] {ai_stand();};\nvoid()\tdemon1_stand5\t=[\t$stand5,\tdemon1_stand6\t] {ai_stand();};\nvoid()\tdemon1_stand6\t=[\t$stand6,\tdemon1_stand7\t] {ai_stand();};\nvoid()\tdemon1_stand7\t=[\t$stand7,\tdemon1_stand8\t] {ai_stand();};\nvoid()\tdemon1_stand8\t=[\t$stand8,\tdemon1_stand9\t] {ai_stand();};\nvoid()\tdemon1_stand9\t=[\t$stand9,\tdemon1_stand10\t] {ai_stand();};\nvoid()\tdemon1_stand10\t=[\t$stand10,\tdemon1_stand11\t] {ai_stand();};\nvoid()\tdemon1_stand11\t=[\t$stand11,\tdemon1_stand12\t] {ai_stand();};\nvoid()\tdemon1_stand12\t=[\t$stand12,\tdemon1_stand13\t] {ai_stand();};\nvoid()\tdemon1_stand13\t=[\t$stand13,\tdemon1_stand1\t] {ai_stand();};\n\nvoid()\tdemon1_walk1\t=[\t$walk1,\t\tdemon1_walk2\t] {\nif (random() < 0.2)\n    sound (self, CHAN_VOICE, \"demon/idle1.wav\", 1, ATTN_IDLE);\nai_walk(8);\n};\nvoid()\tdemon1_walk2\t=[\t$walk2,\t\tdemon1_walk3\t] {ai_walk(6);};\nvoid()\tdemon1_walk3\t=[\t$walk3,\t\tdemon1_walk4\t] {ai_walk(6);};\nvoid()\tdemon1_walk4\t=[\t$walk4,\t\tdemon1_walk5\t] {ai_walk(7);};\nvoid()\tdemon1_walk5\t=[\t$walk5,\t\tdemon1_walk6\t] {ai_walk(4);};\nvoid()\tdemon1_walk6\t=[\t$walk6,\t\tdemon1_walk7\t] {ai_walk(6);};\nvoid()\tdemon1_walk7\t=[\t$walk7,\t\tdemon1_walk8\t] {ai_walk(10);};\nvoid()\tdemon1_walk8\t=[\t$walk8,\t\tdemon1_walk1\t] {ai_walk(10);};\n\nvoid()\tdemon1_run1\t=[\t$run1,\t\tdemon1_run2\t] {\nif (random() < 0.2)\n    sound (self, CHAN_VOICE, \"demon/idle1.wav\", 1, ATTN_IDLE);\nai_run(20);};\nvoid()\tdemon1_run2\t=[\t$run2,\t\tdemon1_run3\t] {ai_run(15);};\nvoid()\tdemon1_run3\t=[\t$run3,\t\tdemon1_run4\t] {ai_run(36);};\nvoid()\tdemon1_run4\t=[\t$run4,\t\tdemon1_run5\t] {ai_run(20);};\nvoid()\tdemon1_run5\t=[\t$run5,\t\tdemon1_run6\t] {ai_run(15);};\nvoid()\tdemon1_run6\t=[\t$run6,\t\tdemon1_run1\t] {ai_run(36);};\n\nvoid()\tdemon1_jump1\t=[\t$leap1,\t\tdemon1_jump2\t] {\nsound (self, CHAN_VOICE, \"demon/djump.wav\", 1, ATTN_NORM);\nai_face();};\nvoid()\tdemon1_jump2\t=[\t$leap2,\t\tdemon1_jump3\t] {ai_face();};\nvoid()\tdemon1_jump3\t=[\t$leap3,\t\tdemon1_jump4\t] {ai_face();};\nvoid()\tdemon1_jump4\t=[\t$leap4,\t\tdemon1_jump5\t]\n{\n\tai_face();\n\t\n\tself.touch = Demon_JumpTouch;\n\tmakevectors (self.angles);\n\tself.origin_z = self.origin_z + 1;\n\tself.velocity = v_forward * 600 + '0 0 250';\n\tif (self.flags & FL_ONGROUND)\n\t\tself.flags = self.flags - FL_ONGROUND;\n};\nvoid()\tdemon1_jump5\t=[\t$leap5,\t\tdemon1_jump6\t] {};\nvoid()\tdemon1_jump6\t=[\t$leap6,\t\tdemon1_jump7\t] {};\nvoid()\tdemon1_jump7\t=[\t$leap7,\t\tdemon1_jump8\t] {};\nvoid()\tdemon1_jump8\t=[ \t$leap8,\t\tdemon1_jump9\t] {};\nvoid()\tdemon1_jump9\t=[ \t$leap9,\t\tdemon1_jump10\t] {};\nvoid()\tdemon1_jump10\t=[ \t$leap10,\tdemon1_jump1\t] {\nself.nextthink = time + 3;\n// if three seconds pass, assume demon is stuck and jump again\n};\n\nvoid()\tdemon1_jump11\t=[ \t$leap11,\tdemon1_jump12\t] {};\nvoid()\tdemon1_jump12\t=[ \t$leap12,\tdemon1_run1\t] {};\n\nvoid() ClawSound =\n{\n\tlocal float r;\n\n\tr = random();\n\tif (r <= 0.33)\n\t\tsound (self, CHAN_VOICE, \"demon/attack1.wav\", 1, ATTN_NORM);\n\telse if (r <= 0.67)\n\t\tsound (self, CHAN_VOICE, \"demon/attack2.wav\", 1, ATTN_NORM);\n\telse\n\t\tsound (self, CHAN_VOICE, \"demon/attack3.wav\", 1, ATTN_NORM);\n};\n\nvoid()\tdemon1_atta1\t=[\t$attacka1,\t\tdemon1_atta2\t] {\nClawSound();\nai_charge(4);};\nvoid()\tdemon1_atta2\t=[\t$attacka2,\t\tdemon1_atta3\t] {ai_charge(0);};\nvoid()\tdemon1_atta3\t=[\t$attacka3,\t\tdemon1_atta4\t] {ai_charge(0);};\nvoid()\tdemon1_atta4\t=[\t$attacka4,\t\tdemon1_atta5\t] {ai_charge(1);};\nvoid()\tdemon1_atta5\t=[\t$attacka5,\t\tdemon1_atta6\t] {ai_charge(2); Demon_Melee(200);};\nvoid()\tdemon1_atta6\t=[\t$attacka6,\t\tdemon1_atta7\t] {ai_charge(1);};\nvoid()\tdemon1_atta7\t=[\t$attacka7,\t\tdemon1_atta8\t] {ai_charge(6);};\nvoid()\tdemon1_atta8\t=[\t$attacka8,\t\tdemon1_atta9\t] {ai_charge(8);};\nvoid()\tdemon1_atta9\t=[\t$attacka9,\t\tdemon1_atta10] {ai_charge(4);};\nvoid()\tdemon1_atta10\t=[\t$attacka10,\t\tdemon1_atta11] {\nClawSound();\nai_charge(2);};\nvoid()\tdemon1_atta11\t=[\t$attacka11,\t\tdemon1_atta12] {Demon_Melee(-200);};\nvoid()\tdemon1_atta12\t=[\t$attacka12,\t\tdemon1_atta13] {ai_charge(5);};\nvoid()\tdemon1_atta13\t=[\t$attacka13,\t\tdemon1_atta14] {ai_charge(8);};\nvoid()\tdemon1_atta14\t=[\t$attacka14,\t\tdemon1_atta15] {ai_charge(4);};\nvoid()\tdemon1_atta15\t=[\t$attacka15,\t\tdemon1_run1] {ai_charge(4);};\n\nvoid()\tdemon1_pain1\t=[\t$pain1,\t\tdemon1_pain2\t] {};\nvoid()\tdemon1_pain2\t=[\t$pain2,\t\tdemon1_pain3\t] {};\nvoid()\tdemon1_pain3\t=[\t$pain3,\t\tdemon1_pain4\t] {};\nvoid()\tdemon1_pain4\t=[\t$pain4,\t\tdemon1_pain5\t] {};\nvoid()\tdemon1_pain5\t=[\t$pain5,\t\tdemon1_pain6\t] {};\nvoid()\tdemon1_pain6\t=[\t$pain6,\t\tdemon1_run1\t] {};\n\nvoid(entity attacker, float damage)\tdemon1_pain =\n{\n\tif (self.touch == Demon_JumpTouch)\n\t\treturn;\n\n\tif (self.pain_finished > time)\n\t\treturn;\n\n\tself.pain_finished = time + 10;\n    sound (self, CHAN_VOICE, \"demon/dpain1.wav\", 1, ATTN_NORM);\n\n\tif (random()*200 > damage)\n\t\treturn;\t\t// didn't flinch\n\t\t\n\tdemon1_pain1 ();\n};\n\nvoid()\tdemon1_die1\t\t=[\t$death1,\t\tdemon1_die2\t] {\nsound (self, CHAN_VOICE, \"demon/death1.wav\", 1, ATTN_NORM);};\nvoid()\tdemon1_die2\t\t=[\t$death2,\t\tdemon1_die3\t] {};\nvoid()\tdemon1_die3\t\t=[\t$death3,\t\tdemon1_die4\t] {};\nvoid()\tdemon1_die4\t\t=[\t$death4,\t\tdemon1_die5\t] {};\nvoid()\tdemon1_die5\t\t=[\t$death5,\t\tdemon1_die6\t] {};\nvoid()\tdemon1_die6\t\t=[\t$death6,\t\tdemon1_die7\t]\n{self.solid = SOLID_NOT;};\nvoid()\tdemon1_die7\t\t=[\t$death7,\t\tdemon1_die8\t] {};\nvoid()\tdemon1_die8\t\t=[\t$death8,\t\tdemon1_die9\t] {};\nvoid()\tdemon1_die9\t\t=[\t$death9,\t\tdemon1_die9 ] {};\n\nvoid() demon_die =\n{\n// check for gib\n\tif (self.health < -80)\n\t{\n\t\tsound (self, CHAN_VOICE, \"demon/death2.wav\", 1, ATTN_NORM);\n\t\tThrowHead (\"progs/h_demon.mdl\", self.health);\n\t\tThrowGib (\"progs/gib1.mdl\", self.health);\n\t\tThrowGib (\"progs/gib1.mdl\", self.health);\n\t\tThrowGib (\"progs/gib1.mdl\", self.health);\n\t\treturn;\n\t}\n\n// regular death\n\tdemon1_die1 ();\n};\n\n\nvoid() Demon_MeleeAttack =\n{\n\tdemon1_atta1 ();\n};\n\nvoid() monster_demon1 =\n{\n\tprecache_model (\"progs/demon.mdl\");\n\tprecache_model (\"progs/h_demon.mdl\");\n\n\tprecache_sound (\"demon/dhit2.wav\");\n\tprecache_sound (\"demon/djump.wav\");\n\tprecache_sound (\"demon/dpain1.wav\");\n\tprecache_sound (\"demon/idle1.wav\");\n\tprecache_sound (\"demon/sight2.wav\");\n\tprecache_sound (\"demon/death1.wav\");\n\tprecache_sound (\"demon/death2.wav\");\n\tprecache_sound (\"demon/attack1.wav\");\n\tprecache_sound (\"demon/attack2.wav\");\n\tprecache_sound (\"demon/attack3.wav\");\n\n\tself.solid = SOLID_SLIDEBOX;\n\tself.movetype = MOVETYPE_STEP;\n\n\tsetmodel (self, \"progs/demon.mdl\");\n\n\tsetsize (self, VEC_HULL2_MIN, VEC_HULL2_MAX);\n\tself.health = 1200;\n\tself.team = 3;\n\tself.classname = \"monster\";\n\tself.netname = \"deathclaw\";\n\tself.th_stand = demon1_stand1;\n\tself.th_walk = demon1_walk1;\n\tself.th_run = demon1_run1;\n\tself.th_die = demon_die;\n\tself.th_melee = Demon_MeleeAttack;\t\t// one of two attacks\n\tself.th_missile = demon1_jump1;\t\t\t// jump attack\n\tself.th_pain = demon1_pain;\n\t\t\n\twalkmonster_start();\n};\n\n\n/*\n==============================================================================\n\nDEMON\n\n==============================================================================\n*/\n\n/*\n==============\nCheckDemonMelee\n\nReturns TRUE if a melee attack would hit right now\n==============\n*/\nfloat()\tCheckDemonMelee =\n{\n\tif (enemy_range == RANGE_MELEE)\n\t{\t// FIXME: check canreach\n\t\tself.attack_state = AS_MELEE;\n\t\treturn TRUE;\n\t}\n\treturn FALSE;\n};\n\n/*\n==============\nCheckDemonJump\n\n==============\n*/\nfloat()\tCheckDemonJump =\n{\n\tlocal\tvector\tdist;\n\tlocal\tfloat\td;\n\n\tif (self.origin_z + self.mins_z > self.enemy.origin_z + self.enemy.mins_z\n\t+ 0.75 * self.enemy.size_z)\n\t\treturn FALSE;\n\t\t\n\tif (self.origin_z + self.maxs_z < self.enemy.origin_z + self.enemy.mins_z\n\t+ 0.25 * self.enemy.size_z)\n\t\treturn FALSE;\n\t\t\n\tdist = self.enemy.origin - self.origin;\n\tdist_z = 0;\n\t\n\td = vlen(dist);\n\t\n\tif (d < 100)\n\t\treturn FALSE;\n\t\t\n\tif (d > 200)\n\t{\n\t\tif (random() < 0.9)\n\t\t\treturn FALSE;\n\t}\n\t\t\n\treturn TRUE;\n};\n\nfloat()\tDemonCheckAttack =\n{\n// if close enough for slashing, go for it\n\tif (CheckDemonMelee ())\n\t{\n\t\tself.attack_state = AS_MELEE;\n\t\treturn TRUE;\n\t}\n\t\n\tif (CheckDemonJump ())\n\t{\n\t\tself.attack_state = AS_MISSILE;\n\t\treturn TRUE;\n\t}\n\t\n\treturn FALSE;\n};\n\n\n//===========================================================================\n\nvoid(float side)\tDemon_Melee =\n{\n\tlocal\tfloat\tldmg;\n\tlocal vector\tdelta;\n\t\n\tai_face ();\n\twalkmove (self.ideal_yaw, 12);\t// allow a little closing\n\n\tdelta = self.enemy.origin - self.origin;\n\n\tif (vlen(delta) > 100)\n\t\treturn;\n\tif (!CanDamage (self.enemy, self))\n\t\treturn;\n\t\t\n    sound (self, CHAN_WEAPON, \"demon/dhit2.wav\", 1, ATTN_NORM);\n\tldmg = 10 + 5*random();\n\tT_Damage (self.enemy, self, self, ldmg);\t\n\n\tmakevectors (self.angles);\n\tSpawnMeatSpray (self.origin + v_forward*16, side * v_right);\n};\n\n\nvoid()\tDemon_JumpTouch =\n{\n\tlocal\tfloat\tldmg;\n\n\tif (self.health <= 0)\n\t\treturn;\n\t\t\n\tif (other.takedamage)\n\t{\n\t\tif ( vlen(self.velocity) > 400 )\n\t\t{\n\t\t\tldmg = 40 + 10*random();\n\t\t\tldmg = ldmg / 4;\n\t\t\tT_Damage (other, self, self, ldmg);\n\t\t\tT_Damage (other, self, self, ldmg);\t\n\t\t\tT_Damage (other, self, self, ldmg);\t\n\t\t\tT_Damage (other, self, self, ldmg);\t\n\n\t\t}\n\t}\n\n\tif (!checkbottom(self))\n\t{\n\t\tif (self.flags & FL_ONGROUND)\n\t\t{\t// jump randomly to not get hung up\n//dprint (\"popjump\\n\");\n\tself.touch = SUB_Null;\n\tself.think = demon1_jump1;\n\tself.nextthink = time + 0.1;\n\n//\t\t\tself.velocity_x = (random() - 0.5) * 600;\n//\t\t\tself.velocity_y = (random() - 0.5) * 600;\n//\t\t\tself.velocity_z = 200;\n//\t\t\tself.flags = self.flags - FL_ONGROUND;\n\t\t}\n\t\treturn;\t// not on ground yet\n\t}\n\n\tself.touch = SUB_Null;\n\tself.think = demon1_jump11;\n\tself.nextthink = time + 0.1;\n};\n\n"
  },
  {
    "path": "quakec/fallout2/dog.qc",
    "content": "/*\n==============================================================================\n\nDOG\n\n==============================================================================\n*/\n$cd id1/models/dog\n$origin 0 0 24\n$base base\n$skin skin\n\n$frame attack1 attack2 attack3 attack4 attack5 attack6 attack7 attack8\n\n$frame death1 death2 death3 death4 death5 death6 death7 death8 death9\n\n$frame deathb1 deathb2 deathb3 deathb4 deathb5 deathb6 deathb7 deathb8\n$frame deathb9\n\n$frame pain1 pain2 pain3 pain4 pain5 pain6\n\n$frame painb1 painb2 painb3 painb4 painb5 painb6 painb7 painb8 painb9 painb10\n$frame painb11 painb12 painb13 painb14 painb15 painb16\n\n$frame run1 run2 run3 run4 run5 run6 run7 run8 run9 run10 run11 run12\n\n$frame leap1 leap2 leap3 leap4 leap5 leap6 leap7 leap8 leap9\n\n$frame stand1 stand2 stand3 stand4 stand5 stand6 stand7 stand8 stand9\n\n$frame walk1 walk2 walk3 walk4 walk5 walk6 walk7 walk8\n\n\nvoid() dog_leap1;\nvoid() dog_run1;\n\n/*\n================\ndog_bite\n\n================\n*/\nvoid() Sniff;\n\nvoid() dog_bite =\n{\nlocal vector\tdelta;\nlocal float \tldmg;\n\n\tif (!self.enemy)\n\t\treturn;\n\n\tself.goalentity = self.enemy;\n\n\tai_charge(10);\n\n\tif (!CanDamage (self.enemy, self))\n\t\treturn;\n\n\tdelta = self.enemy.origin - self.origin;\n\n\tif (vlen(delta) > (100*self.scale))\n\t\treturn;\n\t\t\n\tldmg = 8+random()*8;\n\n\tif (self.classname == \"robofang\")\n\t\tldmg = 4+random()*4;\n\n\tT_Damage (self.enemy, self, self, ldmg);\n\tT_Damage (self.enemy, self, self, ldmg);\n};\n\nvoid()\tDog_JumpTouch =\n{\n\tlocal\tfloat\tldmg;\n\n\tif (self.health <= 0)\n\t\treturn;\n\t\t\n\tif (other.takedamage)\n\t{\n\t\tif ( vlen(self.velocity) > 300 )\n\t\t{\n\t\t\tldmg = 10 + 10*random();\n\t\t\tT_Damage (other, self, self, ldmg);\t\n\t\t}\n\t}\n\n\tif (!checkbottom(self))\n\t{\n\t\tif (self.flags & FL_ONGROUND)\n\t\t{\t// jump randomly to not get hung up\n//dprint (\"popjump\\n\");\n\tself.touch = SUB_Null;\n\tself.think = dog_leap1;\n\tself.nextthink = time + 0.1;\n\n//\t\t\tself.velocity_x = (random() - 0.5) * 600;\n//\t\t\tself.velocity_y = (random() - 0.5) * 600;\n//\t\t\tself.velocity_z = 200;\n//\t\t\tself.flags = self.flags - FL_ONGROUND;\n\t\t}\n\t\treturn;\t// not on ground yet\n\t}\n\n\tself.touch = SUB_Null;\n\tself.think = dog_run1;\n\tself.nextthink = time + 0.1;\n};\n\nvoid() BackDown =\n{\n\tif (self.enemy == world && self.classname == \"robofang\")\n\t{\n\t\tself.goalentity = self.owner;\n\t\tself.th_walk();\n\t}\n/*\n\tif (self.enemy.team == self.team)\n\t{\n\t\tself.enemy = world;\n\t\tself.goalentity = self.owner;\n\t\tself.th_walk();\n\t}*/\n};\n\n\n\nvoid() dog_stand1\t=[\t$stand1,\tdog_stand2\t] {ai_stand();};\nvoid() dog_stand2\t=[\t$stand2,\tdog_stand3\t] {ai_stand();};\nvoid() dog_stand3\t=[\t$stand3,\tdog_stand4\t] {ai_stand();\n\n};\nvoid() dog_stand4\t=[\t$stand4,\tdog_stand5\t] {ai_stand();};\nvoid() dog_stand5\t=[\t$stand5,\tdog_stand6\t] {ai_stand();};\nvoid() dog_stand6\t=[\t$stand6,\tdog_stand7\t] {ai_stand();};\nvoid() dog_stand7\t=[\t$stand7,\tdog_stand8\t] {ai_stand();};\nvoid() dog_stand8\t=[\t$stand8,\tdog_stand9\t] {ai_stand();};\nvoid() dog_stand9\t=[\t$stand9,\tdog_stand1\t] {ai_stand();\n\n\tSniff();\n\tBackDown();\n};\n\nvoid() dog_walk1\t=[\t$walk1 ,\tdog_walk2\t] {\nif (random() < 0.1)\n\tsound (self, CHAN_VOICE, \"dog/idle.wav\", 1, ATTN_IDLE);\nai_walk(8);};\nvoid() dog_walk2\t=[\t$walk2 ,\tdog_walk3\t] {ai_walk(8);};\nvoid() dog_walk3\t=[\t$walk3 ,\tdog_walk4\t] {ai_walk(8);};\nvoid() dog_walk4\t=[\t$walk4 ,\tdog_walk5\t] {ai_walk(8);};\nvoid() dog_walk5\t=[\t$walk5 ,\tdog_walk6\t] {ai_walk(8);\n\n\tSniff();\n\tBackDown();\n};\nvoid() dog_walk6\t=[\t$walk6 ,\tdog_walk7\t] {ai_walk(8);};\nvoid() dog_walk7\t=[\t$walk7 ,\tdog_walk8\t] {ai_walk(8);};\nvoid() dog_walk8\t=[\t$walk8 ,\tdog_walk1\t] {ai_walk(8);};\n\nvoid() Sniff =\n{\n\tlocal entity te;\n\tlocal float x;\n\n\tif (self.classname != \"robofang\")\n\t\treturn;\n\n\t\tx = 20;\n\t\tte = findradius (self.origin, 500);\n\t\twhile (te)\n\t\t{\n\t\t\tif (te.classname == \"monster\" && te.enemy == self.owner)\n\t\t\t{\n\t\t\t\tif (self.rtime < time)\n\t\t\t\t\tsound (self, CHAN_VOICE, \"dog/dsight.wav\", 1, ATTN_NORM);\n\t\t\t\tself.rtime = time + 5;\n\t\t\t\tself.enemy = te;\n\t\t\t\tFoundTarget();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tte = te.chain;\n\t\t}\n};\n\nvoid() dog_run1\t\t=[\t$run1  ,\tdog_run2\t] {\n\n\n\tSniff();\n\tBackDown();\n\nif (random() < 0.2)\n\tsound (self, CHAN_VOICE, \"dog/idle.wav\", 1, ATTN_IDLE);\nai_run(16);};\nvoid() dog_run2\t\t=[\t$run2  ,\tdog_run3\t] {ai_run(32);};\nvoid() dog_run3\t\t=[\t$run3  ,\tdog_run4\t] {ai_run(32);};\nvoid() dog_run4\t\t=[\t$run4  ,\tdog_run5\t] {ai_run(20);};\nvoid() dog_run5\t\t=[\t$run5  ,\tdog_run6\t] {ai_run(64);};\nvoid() dog_run6\t\t=[\t$run6  ,\tdog_run7\t] {\n\nif (self.enemy == self.owner)\n\tself.enemy = self.enemy.enemy;\n\nai_run(32);};\nvoid() dog_run7\t\t=[\t$run7  ,\tdog_run8\t] {ai_run(16);};\nvoid() dog_run8\t\t=[\t$run8  ,\tdog_run9\t] {ai_run(32);};\nvoid() dog_run9\t\t=[\t$run9  ,\tdog_run10\t] {ai_run(32);};\nvoid() dog_run10\t=[\t$run10  ,\tdog_run11\t] {ai_run(20);};\nvoid() dog_run11\t=[\t$run11  ,\tdog_run12\t] {ai_run(64);};\nvoid() dog_run12\t=[\t$run12  ,\tdog_run1\t] {ai_run(32);};\n\nvoid() dog_atta1\t=[\t$attack1,\tdog_atta2\t] {\n\nai_charge(10);};\nvoid() dog_atta2\t=[\t$attack2,\tdog_atta3\t] {ai_charge(10);};\nvoid() dog_atta3\t=[\t$attack3,\tdog_atta4\t] {ai_charge(10);};\nvoid() dog_atta4\t=[\t$attack4,\tdog_atta5\t] {\nsound (self, CHAN_VOICE, \"dog/dattack1.wav\", 1, ATTN_NORM);\ndog_bite();};\nvoid() dog_atta5\t=[\t$attack5,\tdog_atta6\t] {ai_charge(10);};\nvoid() dog_atta6\t=[\t$attack6,\tdog_atta7\t] {ai_charge(10);};\nvoid() dog_atta7\t=[\t$attack7,\tdog_atta8\t] {ai_charge(10);};\nvoid() dog_atta8\t=[\t$attack8,\tdog_run1\t] {ai_charge(10);};\n\nvoid() dog_leap1\t=[\t$leap1,\t\tdog_leap2\t] {ai_face();};\nvoid() dog_leap2\t=[\t$leap2,\t\tdog_leap3\t]\n{\n\tai_face();\n\t\n\tself.touch = Dog_JumpTouch;\n\tmakevectors (self.angles);\n\tself.origin_z = self.origin_z + 1;\n\tself.velocity = v_forward * 300 + '0 0 200';\n\tif (self.flags & FL_ONGROUND)\n\t\tself.flags = self.flags - FL_ONGROUND;\n};\n\nvoid() dog_leap3\t=[\t$leap3,\t\tdog_leap4\t] {};\nvoid() dog_leap4\t=[\t$leap4,\t\tdog_leap5\t] {};\nvoid() dog_leap5\t=[\t$leap5,\t\tdog_leap6\t] {};\nvoid() dog_leap6\t=[\t$leap6,\t\tdog_leap7\t] {};\nvoid() dog_leap7\t=[\t$leap7,\t\tdog_leap8\t] {};\nvoid() dog_leap8\t=[\t$leap8,\t\tdog_leap9\t] {};\nvoid() dog_leap9\t=[\t$leap9,\t\tdog_leap9\t] {};\n\nvoid() dog_pain1\t=[\t$pain1 ,\tdog_pain2\t] {};\nvoid() dog_pain2\t=[\t$pain2 ,\tdog_pain3\t] {};\nvoid() dog_pain3\t=[\t$pain3 ,\tdog_pain4\t] {};\nvoid() dog_pain4\t=[\t$pain4 ,\tdog_pain5\t] {};\nvoid() dog_pain5\t=[\t$pain5 ,\tdog_pain6\t] {};\nvoid() dog_pain6\t=[\t$pain6 ,\tdog_run1\t] {};\n\nvoid() dog_painb1\t=[\t$painb1 ,\tdog_painb2\t] {};\nvoid() dog_painb2\t=[\t$painb2 ,\tdog_painb3\t] {};\nvoid() dog_painb3\t=[\t$painb3 ,\tdog_painb4\t] {ai_pain(4);};\nvoid() dog_painb4\t=[\t$painb4 ,\tdog_painb5\t] {ai_pain(12);};\nvoid() dog_painb5\t=[\t$painb5 ,\tdog_painb6\t] {ai_pain(12);};\nvoid() dog_painb6\t=[\t$painb6 ,\tdog_painb7\t] {ai_pain(2);};\nvoid() dog_painb7\t=[\t$painb7 ,\tdog_painb8\t] {};\nvoid() dog_painb8\t=[\t$painb8 ,\tdog_painb9\t] {ai_pain(4);};\nvoid() dog_painb9\t=[\t$painb9 ,\tdog_painb10\t] {};\nvoid() dog_painb10\t=[\t$painb10 ,\tdog_painb11\t] {ai_pain(10);};\nvoid() dog_painb11\t=[\t$painb11 ,\tdog_painb12\t] {};\nvoid() dog_painb12\t=[\t$painb12 ,\tdog_painb13\t] {};\nvoid() dog_painb13\t=[\t$painb13 ,\tdog_painb14\t] {};\nvoid() dog_painb14\t=[\t$painb14 ,\tdog_painb15\t] {};\nvoid() dog_painb15\t=[\t$painb15 ,\tdog_painb16\t] {};\nvoid() dog_painb16\t=[\t$painb16 ,\tdog_run1\t] {};\n\nvoid() dog_pain =\n{\n\tsound (self, CHAN_VOICE, \"dog/dpain1.wav\", 1, ATTN_NORM);\n\n\tif (random() > 0.5)\n\t\tdog_pain1 ();\n\telse\n\t\tdog_painb1 ();\n};\n\nvoid() dog_die1\t\t=[\t$death1,\tdog_die2\t] {};\nvoid() dog_die2\t\t=[\t$death2,\tdog_die3\t] {};\nvoid() dog_die3\t\t=[\t$death3,\tdog_die4\t] {};\nvoid() dog_die4\t\t=[\t$death4,\tdog_die5\t] {};\nvoid() dog_die5\t\t=[\t$death5,\tdog_die6\t] {};\nvoid() dog_die6\t\t=[\t$death6,\tdog_die7\t] {};\nvoid() dog_die7\t\t=[\t$death7,\tdog_die8\t] {};\nvoid() dog_die8\t\t=[\t$death8,\tdog_die9\t] {\nself.solid = SOLID_NOT;\n};\nvoid() dog_die9\t\t=[\t$death9,\tdog_die9\t] {};\n\nvoid() dog_dieb1\t\t=[\t$deathb1,\tdog_dieb2\t] {};\nvoid() dog_dieb2\t\t=[\t$deathb2,\tdog_dieb3\t] {};\nvoid() dog_dieb3\t\t=[\t$deathb3,\tdog_dieb4\t] {};\nvoid() dog_dieb4\t\t=[\t$deathb4,\tdog_dieb5\t] {};\nvoid() dog_dieb5\t\t=[\t$deathb5,\tdog_dieb6\t] {};\nvoid() dog_dieb6\t\t=[\t$deathb6,\tdog_dieb7\t] {};\nvoid() dog_dieb7\t\t=[\t$deathb7,\tdog_dieb8\t] {};\nvoid() dog_dieb8\t\t=[\t$deathb8,\tdog_dieb9\t] {\nself.solid = SOLID_NOT;\n};\nvoid() dog_dieb9\t\t=[\t$deathb9,\tdog_dieb9\t] {};\n\nvoid () woof_pain =\n{\n\tif (self.frame == 26)\n\t{\n\t\tself.frame = 27;\n\t\tself.health = 180;\n\t\tself.think = dog_die1;\n\t\tself.nextthink = time + 0.12;\n\t}\n\telse\n\t{\n\t\tThrowGib2 (\"progs/zom_gib.mdl\", -35);\n\t\tself.frame = 26;\n\t\tself.health = 180;\n\t\tself.think = dog_dieb1;\n\t\tself.nextthink = time + 0.12;\n\t}\n\n\tself.attack = self.attack + 1;\n\n\tif (self.attack >= 4)\n\t{\n\t\tif (random()*4 <= 2)\n\t\t\tsound (self, CHAN_VOICE, \"zombie/z_gib.wav\", 1, ATTN_NORM);\n\t\telse\n\t\t\tsound (self, CHAN_VOICE, \"dog/death2.wav\", 1, ATTN_NORM);\n\n\t\tThrowHead (\"progs/h_dog.mdl\", -30);\n\t\tThrowGib (\"progs/gib1.mdl\", -30);\n\t\tThrowGib (\"progs/gib2.mdl\", -30);\n\t\tThrowGib (\"progs/gib3.mdl\", -30);\n\t\tThrowGib2 (\"progs/zom_gib.mdl\", -45);\n\t\tThrowGib2 (\"progs/zom_gib.mdl\", -45);\n\t}\n};\n\nvoid (vector stuff) spawn_live_dog =\n{\n\tlocal entity dog;\n\n\tdog = spawn ();\n\tdog.origin = stuff;\n\tdog.enemy = world;\n\tdog.attack_finished = time + 10;\n\tdog.solid = SOLID_SLIDEBOX;\n\tdog.movetype = MOVETYPE_STEP;\n\tdog.takedamage = DAMAGE_YES;\n\tsetmodel (dog, \"progs/dog.mdl\");\n\tsetsize (dog, VEC_HULL_MIN, '16 16 40');\n\tdog.classname = \"body\";\n\tdog.netname = \"dead\";\n\tdog.health = 180;\n\tdog.max_health = dog.health;\n\tdog.th_pain = woof_pain;\n\tdog.think = woof_pain;\n\tdog.nextthink = time + 0.05;\n};\n\nvoid () dog_die =\n{\n\tif (self.enemy.classname == \"player\")\n\t\tself.enemy.frags = self.enemy.frags + 1;\n\n\tif (self.health < -35)\n\t{\n\t\tself.solid = SOLID_NOT;\n\t\tsound (self, CHAN_VOICE, \"player/udeath.wav\", 1, ATTN_NORM);\n\t\tdog_die1 ();\n\t}\n\telse\n\t{\n\t\tsound (self, CHAN_VOICE, \"dog/ddeath.wav\", 1, ATTN_NORM);\n\t\tself.solid = SOLID_NOT;\n\t\tspawn_live_dog(self.origin);\n\t\tremove(self);\n\t}\n};\n\n//============================================================================\n\n/*\n==============\nCheckDogMelee\n\nReturns TRUE if a melee attack would hit right now\n==============\n*/\nfloat()\tCheckDogMelee =\n{\n\tif (enemy_range == RANGE_MELEE)\n\t{\t// FIXME: check canreach\n\t\tself.attack_state = AS_MELEE;\n\t\treturn TRUE;\n\t}\n\treturn FALSE;\n};\n\n/*\n==============\nCheckDogJump\n\n==============\n*/\nfloat()\tCheckDogJump =\n{\n\tlocal\tvector\tdist;\n\tlocal\tfloat\td;\n\n\tif (self.origin_z + self.mins_z > self.enemy.origin_z + self.enemy.mins_z\n\t+ 0.75 * self.enemy.size_z)\n\t\treturn FALSE;\n\t\t\n\tif (self.origin_z + self.maxs_z < self.enemy.origin_z + self.enemy.mins_z\n\t+ 0.25 * self.enemy.size_z)\n\t\treturn FALSE;\n\t\t\n\tdist = self.enemy.origin - self.origin;\n\tdist_z = 0;\n\t\n\td = vlen(dist);\n\t\n\tif (d < 80)\n\t\treturn FALSE;\n\t\t\n\tif (d > 150)\n\t\treturn FALSE;\n\t\t\n\treturn TRUE;\n};\n\nfloat()\tDogCheckAttack =\n{\n// if close enough for slashing, go for it\n\tif (CheckDogMelee ())\n\t{\n\t\tself.attack_state = AS_MELEE;\n\t\treturn TRUE;\n\t}\n\t\n\tif (CheckDogJump ())\n\t{\n\t\tself.attack_state = AS_MISSILE;\n\t\treturn TRUE;\n\t}\n\t\n\treturn FALSE;\n};\n\n\n//===========================================================================\n\nvoid() monster_dog =\n{\n\tprecache_model (\"progs/h_dog.mdl\");\n\tprecache_model (\"progs/dog.mdl\");\n\n\tprecache_sound (\"dog/dattack1.wav\");\n\tprecache_sound (\"dog/ddeath.wav\");\n\tprecache_sound (\"dog/death2.wav\");\n\tprecache_sound (\"dog/dpain1.wav\");\n\tprecache_sound (\"dog/dsight.wav\");\n\tprecache_sound (\"dog/idle.wav\");\n\n\tself.solid = SOLID_SLIDEBOX;\n\tself.movetype = MOVETYPE_STEP;\n\n\tsetmodel (self, \"progs/dog.mdl\");\n\n\tsetsize (self, '-24 -24 -24', '24 24 24');\n\tself.health = 75;\n\tself.scale = 1;\n\tself.team = 3;\n\tself.takedamage = DAMAGE_YES;\n\tself.classname = \"monster\";\n\tself.netname = \"radwolf\";\n\tself.th_stand = dog_stand1;\n\tself.th_walk = dog_walk1;\n\tself.th_run = dog_run1;\n\tself.th_pain = dog_pain;\n\tself.th_die = dog_die;\n\tself.th_melee = dog_atta1;\n\n\twalkmonster_start();\n};\n\nvoid (vector org) spawn_dog =\n{\n\tlocal entity dog, oself;\n\tlocal entity te;\n\n\tmakevectors (self.v_angle);\n\torg = ((org + (v_forward * 96)) + (v_up * 72));\n\tself.impulse = 0;\n\tte = findradius (org, 80);\n\twhile (te)\n\t{\n\t\tif (te.classname == \"player\" && te.health > 0)\n\t\t\treturn;\n\n\t\tte = te.chain;\n\t}\n\n\tdog = spawn();\n\toself = self;\n\tself = dog;\n\tself.scale = 1;\n\tself.solid = SOLID_SLIDEBOX;\n\tself.movetype = MOVETYPE_STEP;\n\n\tsetmodel (self, \"progs/dog.mdl\");\n\tsetorigin(self, org);\n\tsetsize (self, '-24 -24 -24', '24 24 24');\n\tself.health = 150;\n\tself.helmet = 1;\n\tself.armortype = 0.3;\n\tself.takedamage = DAMAGE_YES;\n\tself.classname = \"robofang\";\n\tself.netname = \"robofang\";\n\tself.th_stand = dog_stand1;\n\tself.th_walk = dog_walk1;\n\tself.th_run = dog_run1;\n\tself.th_pain = dog_pain;\n\tself.th_die = station_die;\n\tself.th_melee = dog_atta1;\n\tself.think = self.th_walk;\n\tself.owner = oself;\n\tself.track = oself;\n\tself.nextthink = time + 0.1;\n\twalkmonster_start();\n\n\tself = oself;\n};\n\n"
  },
  {
    "path": "quakec/fallout2/doors.qc",
    "content": "float DOOR_START_OPEN    = 1;\nfloat DOOR_DONT_LINK    = 4;\nfloat DOOR_GOLD_KEY    = 8;\nfloat DOOR_SILVER_KEY    = 16;\nfloat DOOR_TOGGLE    = 32;\nvoid () door_go_down;\nvoid () door_go_up;\n\nvoid () door_blocked =\n{\n\tT_Damage (other, self, self, self.dmg);\n\tif ((self.wait >= MULTICAST_ALL))\n\t{\n\t\tif ((self.state == STATE_DOWN))\n\t\t{\n\t\t\tdoor_go_up ();\n\t\t}\n\t\telse\n\t\t{\n\t\t\tdoor_go_down ();\n\t\t}\n\t}\n};\n\nvoid () door_hit_top =\n{\n\tsound (self, (CHAN_NO_PHS_ADD + CHAN_VOICE), self.noise1, DOOR_START_OPEN, ATTN_NORM);\n\tself.state = STATE_TOP;\n\tif ((self.spawnflags & DOOR_TOGGLE))\n\t{\n\t\treturn;\n\t}\n\tif (self.owner.rtime == 0)\n\t{\n\t\tself.think = door_go_down;\n\t\tself.nextthink = (self.ltime + self.wait);\n\t}\n};\n\nvoid () door_hit_bottom =\n{\n\tsound (self, (CHAN_NO_PHS_ADD + CHAN_VOICE), self.noise1, DOOR_START_OPEN, ATTN_NORM);\n\tself.state = STATE_BOTTOM;\n};\n\nvoid () door_go_down =\n{\n\tsound (self, CHAN_VOICE, self.noise2, DOOR_START_OPEN, ATTN_NORM);\n\tif (self.max_health)\n\t{\n\t\tself.takedamage = DAMAGE_YES;\n\t\tself.health = self.max_health;\n\t}\n\tself.state = STATE_DOWN;\n\tSUB_CalcMove (self.pos1, self.speed, door_hit_bottom);\n};\n\nvoid () door_go_up =\n{\n\tif (self.state == STATE_UP)\n\t\treturn;\t\t// allready going up\n\n\tif (self.state == STATE_TOP)\n\t{\t// reset top wait time\n\t\tself.nextthink = self.ltime + self.wait;\n\t\treturn;\n\t}\n\t\n\tsound (self, CHAN_VOICE, self.noise2, 1, ATTN_NORM);\n\tself.state = STATE_UP;\n\tSUB_CalcMove (self.pos2, self.speed, door_hit_top);\n\tSUB_UseTargets();\n};\n\nvoid () door_fire =\n{\n\tlocal entity oself;\n\tlocal entity starte;\n\n\tif ((self.owner != self))\n\t{\n\t\tobjerror (\"door_fire: self.owner != self\");\n\t}\n\tif (self.items)\n\t{\n\t\tsound (self, CHAN_VOICE, self.noise4, DOOR_START_OPEN, ATTN_NORM);\n\t}\n\tself.message = string_null;\n\toself = self;\n\tif ((self.spawnflags & DOOR_TOGGLE))\n\t{\n\t\tif (((self.state == STATE_UP) || (self.state == STATE_TOP)))\n\t\t{\n\t\t\tstarte = self;\n\t\t\tdo\n\t\t\t{\n\t\t\t\tdoor_go_down ();\n\t\t\t\tself = self.enemy;\n\n\t\t\t} while (((self != starte) && (self != world)));\n\t\t\tself = oself;\n\t\t\treturn;\n\t\t}\n\t}\n\tstarte = self;\n\tdo\n\t{\n\t\tdoor_go_up ();\n\t\tself = self.enemy;\n\n\t} while (((self != starte) && (self != world)));\n\tself = oself;\n};\n\nvoid () door_use =\n{\n\tlocal entity oself;\n\n\tself.message = \"\";\n\tself.owner.message = \"\";\n\tself.enemy.message = \"\";\n\toself = self;\n\tself = self.owner;\n\tdoor_fire ();\n\tself = oself;\n};\n\nvoid () door_trigger_touch =\n{\n\tif (!triggercantouch(self, other))\n\t\treturn;\n\n\tif ((other.health <= MULTICAST_ALL))\n\t{\n\t\treturn;\n\t}\n\tif ((time < self.attack_finished))\n\t{\n\t\treturn;\n\t}\n\tself.attack_finished = (time + DOOR_START_OPEN);\n\tactivator = other;\n\tself = self.owner;\n\tdoor_use ();\n};\n\nvoid () door_killed =\n{\n\tlocal entity oself;\n\n\toself = self;\n\tself = self.owner;\n\tself.health = self.max_health;\n\tself.takedamage = DAMAGE_NO;\n\tdoor_use ();\n\tself = oself;\n};\n\n\nvoid () OpenDoorBeep =\n{\n\tlocal float r;\n\n\tr = range (self.enemy);\n\n\tif (r != RANGE_MELEE)\n\t{\n\t\tself.think = OpenDoorBeep;\n\t\tself.nextthink = time + 0.5;\n\t\treturn;\n\t}\n\n\tif (random()*3<1)\n\t\tsound (self, CHAN_BODY, \"misc/build1.wav\", 1, ATTN_NORM);\n\telse if (random()*3<2)\n\t\tsound (self, CHAN_BODY, \"misc/build2.wav\", 1, ATTN_NORM);\n\telse\n\t\tsound (self, CHAN_BODY, \"misc/build3.wav\", 1, ATTN_NORM);\n\n\tself.think = OpenDoorBeep;\n\tself.nextthink = time + 0.5;\n\tself.owner.owner.rtime = self.owner.owner.rtime + 1;\n\tself.frame = floor ((self.owner.owner.rtime / 12) * 7);\n\n\tif (self.owner.owner.rtime >= 12)\n\t{\n\t\tsound (self, CHAN_BODY, \"misc/basekey.wav\", 1, ATTN_NORM);\n\t\tself.think = SUB_Remove;\n\t\tself.nextthink = time + 1;\n\t\treturn;\n\t}\n};\n\nvoid (entity portal, entity toucher) SpawnOpenDoor =\n{\n\tlocal entity open;\n\n\topen = spawn();\n\tsetorigin (open, toucher.origin + '0 0 48');\n\tsetmodel (open, \"progs/hbar.spr\");\n\tsetsize (open, VEC_ORIGIN, VEC_ORIGIN);\n\topen.think = OpenDoorBeep;\n\topen.nextthink = time + 1;\n\topen.enemy = toucher;\n\topen.owner = portal;\n};\n\n\nvoid () door_touch =\n{\n\tif (!triggercantouch(self, other))\n\t\treturn;\n\n\tif (other.classname != \"player\")\n\t\treturn;\n\n\tif (self.owner.attack_finished > time)\n\t\treturn;\n\n\tif (self.state != STATE_BOTTOM)\n\t\treturn;\n\n\tif (self.size_y > 64 && self.size_x > 64)\n\t\treturn;\n\n\tself.owner.attack_finished = (time + WEAPON_ROCKET);\n\tif ((self.owner.message != \"\"))\n\t{\n\t\tcenterprint (other, self.owner.message);\n\t\tsound (other, CHAN_VOICE, \"misc/talk.wav\", DOOR_START_OPEN, ATTN_NORM);\n\t}\n\tif (!self.items)\n\t\treturn;\n\n\tif (self.owner.rtime > 0 && self.owner.rtime < 12)\n\t\treturn;\n\n\tif ((self.size_z <= 160) && self.owner.rtime == 0 && self.items & other.items != self.items)\n\t{\n\t\tif (self.size_y <= 128 && self.size_x <= 128)\n\t\t{\n\t\tif (other.class == 2 || other.class == 4) //assassins as well as scientists\n\t\t{                                         //can open doors for more freedom\n\t\t\tSpawnOpenDoor(self, other);\n\t\t\treturn;\n\t\t}\n\n\t\tif (self.owner.items == IT_KEY1)\n\t\t\tsound (self, CHAN_VOICE, self.noise3, 1, ATTN_NORM);\n\n\t\tif (self.owner.items == IT_KEY2)\n\t\t\tsound (self, CHAN_VOICE, self.noise3, 1, ATTN_NORM);\n\n\t\treturn;\n\t\t}\n\t}\n\tif (self.owner.rtime >= 12)\n\t{\n\t\tdoor_use ();\n\t\treturn;\n\t}\n\n\tdoor_use ();\n};\n\nentity (vector fmins, vector fmaxs) spawn_field =\n{\n\tlocal entity trigger;\n\tlocal vector t1;\n\tlocal vector t2;\n\n\ttrigger = spawn ();\n\ttrigger.team = self.team;\n\ttrigger.movetype = MOVETYPE_NONE;\n\ttrigger.solid = SOLID_TRIGGER;\n\ttrigger.owner = self;\n\ttrigger.touch = door_trigger_touch;\n\tt1 = fmins;\n\tt2 = fmaxs;\n\tsetsize (trigger, (t1 - '60 60 8'), (t2 + '60 60 8'));\n\treturn (trigger);\n};\n\nfloat (entity e1, entity e2) EntitiesTouching =\n{\n\tif ((e1.mins_x > e2.maxs_x))\n\t{\n\t\treturn (FALSE);\n\t}\n\tif ((e1.mins_y > e2.maxs_y))\n\t{\n\t\treturn (FALSE);\n\t}\n\tif ((e1.mins_z > e2.maxs_z))\n\t{\n\t\treturn (FALSE);\n\t}\n\tif ((e1.maxs_x < e2.mins_x))\n\t{\n\t\treturn (FALSE);\n\t}\n\tif ((e1.maxs_y < e2.mins_y))\n\t{\n\t\treturn (FALSE);\n\t}\n\tif ((e1.maxs_z < e2.mins_z))\n\t{\n\t\treturn (FALSE);\n\t}\n\treturn (TRUE);\n};\n\nvoid () LinkDoors =\n{\n\tlocal entity t;\n\tlocal entity starte;\n\tlocal vector cmins;\n\tlocal vector cmaxs;\n\n\tif (self.enemy)\n\t{\n\t\treturn;\n\t}\n\tif ((self.spawnflags & DOOR_DONT_LINK))\n\t{\n\t\tself.enemy = self;\n\t\tself.owner = self;\n\t\treturn;\n\t}\n\tcmins = self.mins;\n\tcmaxs = self.maxs;\n\tstarte = self;\n\tt = self;\n\tdo\n\t{\n\t\tself.owner = starte;\n\t\tif (self.health)\n\t\t{\n\t\t\tstarte.health = self.health;\n\t\t}\n\t\tif (self.targetname)\n\t\t{\n\t\t\tstarte.targetname = self.targetname;\n\t\t}\n\t\tif ((self.message != \"\"))\n\t\t{\n\t\t\tstarte.message = self.message;\n\t\t}\n\t\tt = find (t, classname, self.classname);\n\t\tif (!t)\n\t\t{\n\t\t\tself.enemy = starte;\n\t\t\tself = self.owner;\n\t\t\tif (self.health)\n\t\t\t{\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (self.targetname)\n\t\t\t{\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (self.items)\n\t\t\t{\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tself.owner.trigger_field = spawn_field (cmins, cmaxs);\n\t\t\treturn;\n\t\t}\n\t\tif (EntitiesTouching (self, t))\n\t\t{\n\t\t\tif (t.enemy)\n\t\t\t{\n\t\t\t\tobjerror (\"cross connected doors\");\n\t\t\t}\n\t\t\tself.enemy = t;\n\t\t\tself = t;\n\t\t\tif ((t.mins_x < cmins_x))\n\t\t\t{\n\t\t\t\tcmins_x = t.mins_x;\n\t\t\t}\n\t\t\tif ((t.mins_y < cmins_y))\n\t\t\t{\n\t\t\t\tcmins_y = t.mins_y;\n\t\t\t}\n\t\t\tif ((t.mins_z < cmins_z))\n\t\t\t{\n\t\t\t\tcmins_z = t.mins_z;\n\t\t\t}\n\t\t\tif ((t.maxs_x > cmaxs_x))\n\t\t\t{\n\t\t\t\tcmaxs_x = t.maxs_x;\n\t\t\t}\n\t\t\tif ((t.maxs_y > cmaxs_y))\n\t\t\t{\n\t\t\t\tcmaxs_y = t.maxs_y;\n\t\t\t}\n\t\t\tif ((t.maxs_z > cmaxs_z))\n\t\t\t{\n\t\t\t\tcmaxs_z = t.maxs_z;\n\t\t\t}\n\t\t}\n\n\t} while (DOOR_START_OPEN);\n};\n\nvoid () func_door =\n{\n\tlocal float r;\n\n\tif ((world.worldtype == MULTICAST_ALL))\n\t{\n\t\tprecache_sound (\"doors/medtry.wav\");\n\t\tprecache_sound (\"doors/meduse.wav\");\n\t\tself.noise3 = \"doors/medtry.wav\";\n\t\tself.noise4 = \"doors/meduse.wav\";\n\t}\n\telse\n\t{\n\t\tif ((world.worldtype == DOOR_START_OPEN))\n\t\t{\n\t\t\tprecache_sound (\"doors/runetry.wav\");\n\t\t\tprecache_sound (\"doors/runeuse.wav\");\n\t\t\tself.noise3 = \"doors/runetry.wav\";\n\t\t\tself.noise4 = \"doors/runeuse.wav\";\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif ((world.worldtype == WEAPON_ROCKET))\n\t\t\t{\n\t\t\t\tprecache_sound (\"doors/basetry.wav\");\n\t\t\t\tprecache_sound (\"doors/baseuse.wav\");\n\t\t\t\tself.noise3 = \"doors/basetry.wav\";\n\t\t\t\tself.noise4 = \"doors/baseuse.wav\";\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tdprint (\"no worldtype set!\\n\");\n\t\t\t}\n\t\t}\n\t}\n\tif ((self.sounds == MULTICAST_ALL))\n\t{\n\t\tprecache_sound (\"misc/null.wav\");\n\t\tprecache_sound (\"misc/null.wav\");\n\t\tself.noise1 = \"misc/null.wav\";\n\t\tself.noise2 = \"misc/null.wav\";\n\t}\n\tif ((self.sounds == DOOR_START_OPEN))\n\t{\n\t\tprecache_sound (\"doors/drclos4.wav\");\n\t\tprecache_sound (\"doors/doormv1.wav\");\n\t\tself.noise1 = \"doors/drclos4.wav\";\n\t\tself.noise2 = \"doors/doormv1.wav\";\n\t}\n\tif ((self.sounds == WEAPON_ROCKET))\n\t{\n\t\tprecache_sound (\"doors/hydro1.wav\");\n\t\tprecache_sound (\"doors/hydro2.wav\");\n\t\tself.noise2 = \"doors/hydro1.wav\";\n\t\tself.noise1 = \"doors/hydro2.wav\";\n\t}\n\tif ((self.sounds == AS_MELEE))\n\t{\n\t\tprecache_sound (\"doors/stndr1.wav\");\n\t\tprecache_sound (\"doors/stndr2.wav\");\n\t\tself.noise2 = \"doors/stndr1.wav\";\n\t\tself.noise1 = \"doors/stndr2.wav\";\n\t}\n\tif ((self.sounds == DOOR_DONT_LINK))\n\t{\n\t\tprecache_sound (\"doors/ddoor1.wav\");\n\t\tprecache_sound (\"doors/ddoor2.wav\");\n\t\tself.noise1 = \"doors/ddoor2.wav\";\n\t\tself.noise2 = \"doors/ddoor1.wav\";\n\t}\n\tSetMovedir ();\n\tself.max_health = self.health;\n\tself.solid = SOLID_BSP;\n\tself.movetype = MOVETYPE_PUSH;\n\tsetorigin (self, self.origin);\n\tsetmodel (self, self.model);\n\tself.classname = \"door\";\n\tself.blocked = door_blocked;\n\tself.use = door_use;\n\n\tif (coop == 0)\n\t{\n\t\tif ((self.spawnflags & DOOR_SILVER_KEY))\n\t\t\tself.items = IT_KEY1;\n\t\tif ((self.spawnflags & DOOR_GOLD_KEY))\n\t\t\tself.items = IT_KEY2;\n\t}\n\n\tif (coop == 1)\n\t{\n\t\tr = random()*10;\n\n\t\tif (r <= 7)\n\t\t\tself.items = IT_KEY1;\n\t\telse\n\t\t\tself.items = IT_KEY2;\n\t}\n\tif (!self.speed)\n\t{\n\t\tself.speed = 100;\n\t}\n\tif (!self.wait)\n\t{\n\t\tself.wait = AS_MELEE;\n\t}\n\tif (!self.lip)\n\t{\n\t\tself.lip = DOOR_GOLD_KEY;\n\t}\n\tif (!self.dmg)\n\t{\n\t\tself.dmg = WEAPON_ROCKET;\n\t}\n\tself.pos1 = self.origin;\n\tself.pos2 = (self.pos1 + (self.movedir * (fabs ((self.movedir * self.size)) - self.lip)));\n\tif ((self.spawnflags & DOOR_START_OPEN))\n\t{\n\t\tsetorigin (self, self.pos2);\n\t\tself.pos2 = self.pos1;\n\t\tself.pos1 = self.origin;\n\t}\n\tself.state = STATE_BOTTOM;\n\tif (self.health)\n\t{\n\t\tself.takedamage = DAMAGE_YES;\n\t\tself.th_die = door_killed;\n\t}\n\tself.touch = door_touch;\n\tself.think = LinkDoors;\n\tself.nextthink = (self.ltime + 0.1);\n};\nvoid () fd_secret_move1;\nvoid () fd_secret_move2;\nvoid () fd_secret_move3;\nvoid () fd_secret_move4;\nvoid () fd_secret_move5;\nvoid () fd_secret_move6;\nvoid () fd_secret_done;\nfloat SECRET_OPEN_ONCE    = 1;\nfloat SECRET_1ST_LEFT    = 2;\nfloat SECRET_1ST_DOWN    = 4;\nfloat SECRET_NO_SHOOT    = 8;\nfloat SECRET_YES_SHOOT    = 16;\n\nvoid () fd_secret_use =\n{\n\tlocal float temp;\n\n\tself.health = 10000;\n\tif ((self.origin != self.oldorigin))\n\t{\n\t\treturn;\n\t}\n\tself.message = string_null;\n\tSUB_UseTargets ();\n\tif (!(self.spawnflags & SECRET_NO_SHOOT))\n\t{\n\t\tself.th_pain = SUB_Null;\n\t\tself.takedamage = DAMAGE_NO;\n\t}\n\tself.velocity = VEC_ORIGIN;\n\tsound (self, CHAN_VOICE, self.noise1, SECRET_OPEN_ONCE, ATTN_NORM);\n\tself.nextthink = (self.ltime + 0.1);\n\ttemp = (SECRET_OPEN_ONCE - (self.spawnflags & SECRET_1ST_LEFT));\n\tmakevectors (self.mangle);\n\tif (!self.t_width)\n\t{\n\t\tif ((self.spawnflags & SECRET_1ST_DOWN))\n\t\t{\n\t\t\tself.t_width = fabs ((v_up * self.size));\n\t\t}\n\t\telse\n\t\t{\n\t\t\tself.t_width = fabs ((v_right * self.size));\n\t\t}\n\t}\n\tif (!self.t_length)\n\t{\n\t\tself.t_length = fabs ((v_forward * self.size));\n\t}\n\tif ((self.spawnflags & SECRET_1ST_DOWN))\n\t{\n\t\tself.dest1 = (self.origin - (v_up * self.t_width));\n\t}\n\telse\n\t{\n\t\tself.dest1 = (self.origin + (v_right * (self.t_width * temp)));\n\t}\n\tself.dest2 = (self.dest1 + (v_forward * self.t_length));\n\tSUB_CalcMove (self.dest1, self.speed, fd_secret_move1);\n\tsound (self, CHAN_VOICE, self.noise2, SECRET_OPEN_ONCE, ATTN_NORM);\n};\n\nvoid () fd_secret_move1 =\n{\n\tself.nextthink = (self.ltime + SECRET_OPEN_ONCE);\n\tself.think = fd_secret_move2;\n\tsound (self, CHAN_VOICE, self.noise3, SECRET_OPEN_ONCE, ATTN_NORM);\n};\n\nvoid () fd_secret_move2 =\n{\n\tsound (self, CHAN_VOICE, self.noise2, SECRET_OPEN_ONCE, ATTN_NORM);\n\tSUB_CalcMove (self.dest2, self.speed, fd_secret_move3);\n};\n\nvoid () fd_secret_move3 =\n{\n\tsound (self, CHAN_VOICE, self.noise3, SECRET_OPEN_ONCE, ATTN_NORM);\n\tif (!(self.spawnflags & SECRET_OPEN_ONCE))\n\t{\n\t\tself.nextthink = (self.ltime + self.wait);\n\t\tself.think = fd_secret_move4;\n\t}\n};\n\nvoid () fd_secret_move4 =\n{\n\tsound (self, CHAN_VOICE, self.noise2, SECRET_OPEN_ONCE, ATTN_NORM);\n\tSUB_CalcMove (self.dest1, self.speed, fd_secret_move5);\n};\n\nvoid () fd_secret_move5 =\n{\n\tself.nextthink = (self.ltime + SECRET_OPEN_ONCE);\n\tself.think = fd_secret_move6;\n\tsound (self, CHAN_VOICE, self.noise3, SECRET_OPEN_ONCE, ATTN_NORM);\n};\n\nvoid () fd_secret_move6 =\n{\n\tsound (self, CHAN_VOICE, self.noise2, SECRET_OPEN_ONCE, ATTN_NORM);\n\tSUB_CalcMove (self.oldorigin, self.speed, fd_secret_done);\n};\n\nvoid () fd_secret_done =\n{\n\tif ((!self.targetname || (self.spawnflags & SECRET_YES_SHOOT)))\n\t{\n\t\tself.health = 10000;\n\t\tself.takedamage = DAMAGE_YES;\n\t\tself.th_pain = fd_secret_use;\n\t\tself.th_die = fd_secret_use;\n\t}\n\tsound (self, (CHAN_NO_PHS_ADD + CHAN_VOICE), self.noise3, SECRET_OPEN_ONCE, ATTN_NORM);\n};\n\nvoid () secret_blocked =\n{\n\tif ((time < self.attack_finished))\n\t{\n\t\treturn;\n\t}\n\tself.attack_finished = (time + 0.5);\n\tT_Damage (other, self, self, self.dmg);\n};\n\nvoid () secret_touch =\n{\n\tif (!triggercantouch(self, other))\n\t\treturn;\n\tif ((other.classname != \"player\"))\n\t{\n\t\treturn;\n\t}\n\tif ((self.attack_finished > time))\n\t{\n\t\treturn;\n\t}\n\tself.attack_finished = (time + SECRET_1ST_LEFT);\n\tif (self.message)\n\t{\n\t\tcenterprint (other, self.message);\n\t\tsound (other, CHAN_BODY, \"misc/talk.wav\", SECRET_OPEN_ONCE, ATTN_NORM);\n\t}\n};\n\nvoid () func_door_secret =\n{\n\tif ((self.sounds == MULTICAST_ALL))\n\t{\n\t\tself.sounds = AS_MELEE;\n\t}\n\tif ((self.sounds == SECRET_OPEN_ONCE))\n\t{\n\t\tprecache_sound (\"doors/latch2.wav\");\n\t\tprecache_sound (\"doors/winch2.wav\");\n\t\tprecache_sound (\"doors/drclos4.wav\");\n\t\tself.noise1 = \"doors/latch2.wav\";\n\t\tself.noise2 = \"doors/winch2.wav\";\n\t\tself.noise3 = \"doors/drclos4.wav\";\n\t}\n\tif ((self.sounds == SECRET_1ST_LEFT))\n\t{\n\t\tprecache_sound (\"doors/airdoor1.wav\");\n\t\tprecache_sound (\"doors/airdoor2.wav\");\n\t\tself.noise2 = \"doors/airdoor1.wav\";\n\t\tself.noise1 = \"doors/airdoor2.wav\";\n\t\tself.noise3 = \"doors/airdoor2.wav\";\n\t}\n\tif ((self.sounds == AS_MELEE))\n\t{\n\t\tprecache_sound (\"doors/basesec1.wav\");\n\t\tprecache_sound (\"doors/basesec2.wav\");\n\t\tself.noise2 = \"doors/basesec1.wav\";\n\t\tself.noise1 = \"doors/basesec2.wav\";\n\t\tself.noise3 = \"doors/basesec2.wav\";\n\t}\n\tif (!self.dmg)\n\t{\n\t\tself.dmg = SECRET_1ST_LEFT;\n\t}\n\tself.mangle = self.angles;\n\tself.angles = VEC_ORIGIN;\n\tself.solid = SOLID_BSP;\n\tself.movetype = MOVETYPE_PUSH;\n\tself.classname = \"door\";\n\tsetmodel (self, self.model);\n\tsetorigin (self, self.origin);\n\tself.touch = secret_touch;\n\tself.blocked = secret_blocked;\n\tself.speed = 50;\n\tself.use = fd_secret_use;\n\tif ((!self.targetname || (self.spawnflags & SECRET_YES_SHOOT)))\n\t{\n\t\tself.health = 10000;\n\t\tself.takedamage = DAMAGE_YES;\n\t\tself.th_pain = fd_secret_use;\n\t}\n\tself.oldorigin = self.origin;\n\tif (!self.wait)\n\t{\n\t\tself.wait = MULTICAST_PVS_R;\n\t}\n};\n"
  },
  {
    "path": "quakec/fallout2/enforcer.qc",
    "content": "/*\n==============================================================================\n\nSOLDIER / PLAYER\n\n==============================================================================\n*/\n\n#define mag1 currentammo\n#define maxmag1 cnt\n\n$cd id1/models/enforcer\n$origin 0 -6 24\n$base base\t\t\n$skin skin\n\n$frame stand1 stand2 stand3 stand4 stand5 stand6 stand7\n\n$frame walk1 walk2 walk3 walk4 walk5 walk6 walk7 walk8 walk9 walk10\n$frame walk11 walk12 walk13 walk14 walk15 walk16\n\n$frame run1 run2 run3 run4 run5 run6 run7 run8\n\n$frame attack1 attack2 attack3 attack4 attack5 attack6\n$frame attack7 attack8 attack9 attack10\n\n$frame death1 death2 death3 death4 death5 death6 death7 death8\n$frame death9 death10 death11 death12 death13 death14\n\n$frame fdeath1 fdeath2 fdeath3 fdeath4 fdeath5 fdeath6 fdeath7 fdeath8\n$frame fdeath9 fdeath10 fdeath11\n\n$frame paina1 paina2 paina3 paina4\n\n$frame painb1 painb2 painb3 painb4 painb5\n\n$frame painc1 painc2 painc3 painc4 painc5 painc6 painc7 painc8\n\n$frame paind1 paind2 paind3 paind4 paind5 paind6 paind7 paind8\n$frame paind9 paind10 paind11 paind12 paind13 paind14 paind15 paind16\n$frame paind17 paind18 paind19\n\n\nvoid () enforcer_assault_rifle;\nvoid () enf_reload1;\n\nvoid() enforcer_fire =\n{\n\tlocal vector org;\n\n\tmakevectors (self.angles);\n\t\n\torg = self.origin + v_forward * 30 + v_right * 8.5 + '0 0 16';\n\n};\n\nvoid (float tmp, float dam) enforcer_single =\n{\n\tlocal vector src;\n\tlocal vector dir;\n\tlocal vector direction;\n\tlocal entity en;\n\tlocal vector org;\n\n\tif (self.enemy.sneak == 1)\n\t\ttmp = tmp * 2;\n\n\tif (self.mag1 == 0)\n\t{\n\t\tsound (self, CHAN_WEAPON, \"weapons/reload.wav\", PLAT_LOW_TRIGGER, ATTN_NORM);\n\t\tenf_reload1();\n\t\tself.mag1 = self.maxmag1;\n\t\treturn;\n\t}\n\n\tself.mag1 = self.mag1 - 1;\n\n\tmakevectors (self.angles);\n\n\tsrc = self.origin + v_forward*10;\n\tsrc_z = self.absmin_z + self.size_z * 0.7;\n\n\ten = self.enemy;\n\t\n\tdir = en.origin - en.velocity*0.2;\n\tdir = normalize (dir - self.origin);\n\n\tdirection = dir;\n\n\ttraceline (src, src + direction*4000 + v_right*crandom()*tmp + v_up*crandom()*tmp, FALSE, self);\n\n\tif (trace_fraction == PLAT_LOW_TRIGGER)\n\t\treturn;\n\n\tif (trace_ent.takedamage)\n\t{\n\t\tSpawnBlood (org, PLAT_LOW_TRIGGER);\n\t\tdam = dam + random()*dam;\n\t\tdam = dam * (1 - (trace_fraction));\n\t\tT_Damage (trace_ent, self, self, dam);\n\t}\n\telse\n\t{\n\t\tWriteByte (MSG_MULTICAST, SVC_TEMPENTITY);\n\t\tWriteByte (MSG_MULTICAST, TE_SPIKE);\n\t\tWriteCoord (MSG_MULTICAST, trace_endpos_x);\n\t\tWriteCoord (MSG_MULTICAST, trace_endpos_y);\n\t\tWriteCoord (MSG_MULTICAST, trace_endpos_z);\n\t\tmulticast (trace_endpos, MULTICAST_PVS);\n\t}\n};\n\n//============================================================================\n\nvoid()\tenf_stand1\t=[\t$stand1,\tenf_stand2\t] {ai_stand();};\nvoid()\tenf_stand2\t=[\t$stand2,\tenf_stand3\t] {ai_stand();};\nvoid()\tenf_stand3\t=[\t$stand3,\tenf_stand4\t] {ai_stand();};\nvoid()\tenf_stand4\t=[\t$stand4,\tenf_stand5\t] {ai_stand();};\nvoid()\tenf_stand5\t=[\t$stand5,\tenf_stand6\t] {ai_stand();};\nvoid()\tenf_stand6\t=[\t$stand6,\tenf_stand7\t] {ai_stand();};\nvoid()\tenf_stand7\t=[\t$stand7,\tenf_stand1\t] {ai_stand();};\n\nvoid()\tenf_walk1\t=[\t$walk1 ,\tenf_walk2\t] {\nif (random() < 0.2)\n\tsound (self, CHAN_VOICE, \"enforcer/idle1.wav\", 1, ATTN_IDLE);\nai_walk(2);};\nvoid()\tenf_walk2\t=[\t$walk2 ,\tenf_walk3\t] {ai_walk(4);};\nvoid()\tenf_walk3\t=[\t$walk3 ,\tenf_walk4\t] {ai_walk(4);};\nvoid()\tenf_walk4\t=[\t$walk4 ,\tenf_walk5\t] {ai_walk(3);};\nvoid()\tenf_walk5\t=[\t$walk5 ,\tenf_walk6\t] {ai_walk(1);};\nvoid()\tenf_walk6\t=[\t$walk6 ,\tenf_walk7\t] {ai_walk(2);};\nvoid()\tenf_walk7\t=[\t$walk7 ,\tenf_walk8\t] {ai_walk(2);};\nvoid()\tenf_walk8\t=[\t$walk8 ,\tenf_walk9\t] {ai_walk(1);};\nvoid()\tenf_walk9\t=[\t$walk9 ,\tenf_walk10\t] {ai_walk(2);};\nvoid()\tenf_walk10\t=[\t$walk10,\tenf_walk11\t] {ai_walk(4);};\nvoid()\tenf_walk11\t=[\t$walk11,\tenf_walk12\t] {ai_walk(4);};\nvoid()\tenf_walk12\t=[\t$walk12,\tenf_walk13\t] {ai_walk(1);};\nvoid()\tenf_walk13\t=[\t$walk13,\tenf_walk14\t] {ai_walk(2);};\nvoid()\tenf_walk14\t=[\t$walk14,\tenf_walk15\t] {ai_walk(3);};\nvoid()\tenf_walk15\t=[\t$walk15,\tenf_walk16\t] {ai_walk(4);};\nvoid()\tenf_walk16\t=[\t$walk16,\tenf_walk1\t] {ai_walk(2);};\n\nvoid()\tenf_run1\t=[\t$run1  ,\tenf_run2\t] {ai_run(18);};\nvoid()\tenf_run2\t=[\t$run2  ,\tenf_run3\t] {ai_run(14);};\nvoid()\tenf_run3\t=[\t$run3  ,\tenf_run4\t] {ai_run(7);};\nvoid()\tenf_run4\t=[\t$run4  ,\tenf_run5\t] {ai_run(12);};\nvoid()\tenf_run5\t=[\t$run5  ,\tenf_run6\t] {ai_run(14);};\nvoid()\tenf_run6\t=[\t$run6  ,\tenf_run7\t] {ai_run(14);};\nvoid()\tenf_run7\t=[\t$run7  ,\tenf_run8\t] {ai_run(7);};\nvoid()\tenf_run8\t=[\t$run8  ,\tenf_run1\t] {ai_run(11);};\n\n\nvoid()\tenf_atk1\t=[\t$attack1,\tenf_atk2\t] {ai_face();};\nvoid()\tenf_atk2\t=[\t$attack2,\tenf_atk3\t] {ai_face();};\nvoid()\tenf_atk3\t=[\t$attack3,\tenf_atk4\t] {ai_face();};\nvoid()\tenf_atk4\t=[\t$attack4,\tenf_atk5\t] {ai_face();};\nvoid()\tenf_atk5\t=[\t$attack5,\tenf_atk6\t] {enforcer_assault_rifle();};\nvoid()\tenf_atk6\t=[\t$attack6,\tenf_atk7\t] {ai_face();};\nvoid()\tenf_atk7\t=[\t$attack4,\tenf_atk8\t] {ai_face();};\nvoid()\tenf_atk8\t=[\t$attack3,\tenf_atk9\t] {ai_face();};\nvoid()\tenf_atk9\t=[\t$attack2,\tenf_atk10\t] {ai_face();};\nvoid()\tenf_atk10\t=[\t$attack1,\tenf_run1\t] {ai_face();};\n\nvoid()\tenf_burst1\t=[\t$attack5,\tenf_burst2\t] {enforcer_single(400, 16);};\nvoid()\tenf_burst2\t=[\t$attack6,\tenf_burst3\t] {enforcer_single(400, 16);ai_face();};\nvoid()\tenf_burst3\t=[\t$attack5,\tenf_burst4\t] {enforcer_single(400, 16);};\nvoid()\tenf_burst4\t=[\t$attack6,\tenf_burst5\t] {enforcer_single(450, 16);ai_face();};\nvoid()\tenf_burst5\t=[\t$attack5,\tenf_burst6\t] {enforcer_single(450, 16);};\nvoid()\tenf_burst6\t=[\t$attack6,\tenf_burst7\t] {enforcer_single(500, 16);ai_face();};\nvoid()\tenf_burst7\t=[\t$attack5,\tenf_burst8\t] {enforcer_single(550, 16);};\nvoid()\tenf_burst8\t=[\t$attack4,\tenf_burst9\t] {enforcer_single(600, 16);ai_face();};\nvoid()\tenf_burst9\t=[\t$attack3,\tenf_burst10\t] {ai_face();};\nvoid()\tenf_burst10\t=[\t$attack2,\tenf_run1\t] {ai_face();};\n\n\n\nvoid()\tenf_reload1\t=[\t$attack10,\tenf_reload2\t] {};\nvoid()\tenf_reload2\t=[\t$attack9,\tenf_reload3\t] {};\nvoid()\tenf_reload3\t=[\t$attack8,\tenf_reload4\t] {};\nvoid()\tenf_reload4\t=[\t$attack7,\tenf_reload5\t] {};\nvoid()\tenf_reload5\t=[\t$attack8,\tenf_reload6\t] {};\nvoid()\tenf_reload6\t=[\t$attack9,\tenf_reload7\t] {};\nvoid()\tenf_reload7\t=[\t$attack10,\tenf_run1\t] {};\n\nvoid()\tenf_paina1\t=[\t$paina1,\tenf_paina2\t] {};\nvoid()\tenf_paina2\t=[\t$paina2,\tenf_paina3\t] {};\nvoid()\tenf_paina3\t=[\t$paina3,\tenf_paina4\t] {};\nvoid()\tenf_paina4\t=[\t$paina4,\tenf_run1\t] {};\n\nvoid()\tenf_painb1\t=[\t$painb1,\tenf_painb2\t] {};\nvoid()\tenf_painb2\t=[\t$painb2,\tenf_painb3\t] {};\nvoid()\tenf_painb3\t=[\t$painb3,\tenf_painb4\t] {};\nvoid()\tenf_painb4\t=[\t$painb4,\tenf_painb5\t] {};\nvoid()\tenf_painb5\t=[\t$painb5,\tenf_run1\t] {};\n\nvoid()\tenf_painc1\t=[\t$painc1,\tenf_painc2\t] {};\nvoid()\tenf_painc2\t=[\t$painc2,\tenf_painc3\t] {};\nvoid()\tenf_painc3\t=[\t$painc3,\tenf_painc4\t] {};\nvoid()\tenf_painc4\t=[\t$painc4,\tenf_painc5\t] {};\nvoid()\tenf_painc5\t=[\t$painc5,\tenf_painc6\t] {};\nvoid()\tenf_painc6\t=[\t$painc6,\tenf_painc7\t] {};\nvoid()\tenf_painc7\t=[\t$painc7,\tenf_painc8\t] {};\nvoid()\tenf_painc8\t=[\t$painc8,\tenf_run1\t] {};\n\nvoid()\tenf_paind1\t=[\t$paind1,\tenf_paind2\t] {};\nvoid()\tenf_paind2\t=[\t$paind2,\tenf_paind3\t] {};\nvoid()\tenf_paind3\t=[\t$paind3,\tenf_paind4\t] {};\nvoid()\tenf_paind4\t=[\t$paind4,\tenf_paind5\t] {ai_painforward(2);};\nvoid()\tenf_paind5\t=[\t$paind5,\tenf_paind6\t] {ai_painforward(1);};\nvoid()\tenf_paind6\t=[\t$paind6,\tenf_paind7\t] {};\nvoid()\tenf_paind7\t=[\t$paind7,\tenf_paind8\t] {};\nvoid()\tenf_paind8\t=[\t$paind8,\tenf_paind9\t] {};\nvoid()\tenf_paind9\t=[\t$paind9,\tenf_paind10\t] {};\nvoid()\tenf_paind10\t=[\t$paind10,\tenf_paind11\t] {};\nvoid()\tenf_paind11\t=[\t$paind11,\tenf_paind12\t] {ai_painforward(1);};\nvoid()\tenf_paind12\t=[\t$paind12,\tenf_paind13\t] {ai_painforward(1);};\nvoid()\tenf_paind13\t=[\t$paind13,\tenf_paind14\t] {ai_painforward(1);};\nvoid()\tenf_paind14\t=[\t$paind14,\tenf_paind15\t] {};\nvoid()\tenf_paind15\t=[\t$paind15,\tenf_paind16\t] {};\nvoid()\tenf_paind16\t=[\t$paind16,\tenf_paind17\t] {ai_pain(1);};\nvoid()\tenf_paind17\t=[\t$paind17,\tenf_paind18\t] {ai_pain(1);};\nvoid()\tenf_paind18\t=[\t$paind18,\tenf_paind19\t] {};\nvoid()\tenf_paind19\t=[\t$paind19,\tenf_run1\t] {};\n\nvoid(entity attacker, float damage)\tenf_pain =\n{\n\tlocal float r;\n\n\tr = random ();\n\tif (self.pain_finished > time)\n\t{\n\t\tsound (self, CHAN_BODY, \"misc/thud.wav\", 1, ATTN_NORM);\n\t\treturn;\n\t}\n\n\tif (r < 0.5)\n\t\tsound (self, CHAN_VOICE, \"enforcer/pain1.wav\", 1, ATTN_NORM);\n\telse\n\t\tsound (self, CHAN_VOICE, \"enforcer/pain2.wav\", 1, ATTN_NORM);\n\n\tif (r < 0.1)\n\t{\n\t\tself.pain_finished = time + 3;\n\t\tenf_paina1 ();\n\t}\n\telse if (r < 0.2)\n\t{\n\t\tself.pain_finished = time + 3;\n\t\tenf_painb1 ();\n\t}\n\telse if (r < 0.3)\n\t{\n\t\tself.pain_finished = time + 3;\n\t\tenf_painc1 ();\n\t}\n\telse\n\t{\n\t\tself.pain_finished = time + 3;\n\t}\n};\n\n//============================================================================\n\n\n\n\nvoid()\tenf_die1\t=[\t$death1,\tenf_die2\t] {};\nvoid()\tenf_die2\t=[\t$death2,\tenf_die3\t] {};\nvoid()\tenf_die3\t=[\t$death3,\tenf_die4\t]\n{self.solid = SOLID_NOT;self.ammo_cells = 5;DropBackpack();};\nvoid()\tenf_die4\t=[\t$death4,\tenf_die5\t] {ai_forward(14);};\nvoid()\tenf_die5\t=[\t$death5,\tenf_die6\t] {ai_forward(2);};\nvoid()\tenf_die6\t=[\t$death6,\tenf_die7\t] {};\nvoid()\tenf_die7\t=[\t$death7,\tenf_die8\t] {};\nvoid()\tenf_die8\t=[\t$death8,\tenf_die9\t] {};\nvoid()\tenf_die9\t=[\t$death9,\tenf_die10\t] {ai_forward(3);};\nvoid()\tenf_die10\t=[\t$death10,\tenf_die11\t] {ai_forward(5);};\nvoid()\tenf_die11\t=[\t$death11,\tenf_die12\t] {ai_forward(5);};\nvoid()\tenf_die12\t=[\t$death12,\tenf_die13\t] {ai_forward(5);};\nvoid()\tenf_die13\t=[\t$death13,\tenf_die14\t] {};\nvoid()\tenf_die14\t=[\t$death14,\tenf_die14\t] {};\n\nvoid()\tenf_fdie1\t=[\t$fdeath1,\tenf_fdie2\t] {\n\n};\nvoid()\tenf_fdie2\t=[\t$fdeath2,\tenf_fdie3\t] {};\nvoid()\tenf_fdie3\t=[\t$fdeath3,\tenf_fdie4\t] \n{self.solid = SOLID_NOT;self.ammo_cells = 5;DropBackpack();};\nvoid()\tenf_fdie4\t=[\t$fdeath4,\tenf_fdie5\t] {};\nvoid()\tenf_fdie5\t=[\t$fdeath5,\tenf_fdie6\t] {};\nvoid()\tenf_fdie6\t=[\t$fdeath6,\tenf_fdie7\t] {};\nvoid()\tenf_fdie7\t=[\t$fdeath7,\tenf_fdie8\t] {};\nvoid()\tenf_fdie8\t=[\t$fdeath8,\tenf_fdie9\t] {};\nvoid()\tenf_fdie9\t=[\t$fdeath9,\tenf_fdie10\t] {};\nvoid()\tenf_fdie10\t=[\t$fdeath10,\tenf_fdie11\t] {};\nvoid()\tenf_fdie11\t=[\t$fdeath11,\tenf_fdie11\t] {};\n\n\nvoid() enf_die =\n{\n// check for gib\n\tif (self.health < -35)\n\t{\n\t\tsound (self, CHAN_VOICE, \"player/udeath.wav\", 1, ATTN_NORM);\n\t\tif (random() > 0.5)\n\t\t\tenf_die1 ();\n\t\telse\n\t\t\tenf_fdie1 ();\n\t\treturn;\n\t}\n\n// regular death\n\tsound (self, CHAN_VOICE, \"enforcer/death1.wav\", 1, ATTN_NORM);\n\tif (random() > 0.5)\n\t\tenf_die1 ();\n\telse\n\t\tenf_fdie1 ();\n};\n\nvoid () enforcer_assault_rifle =\n{\n\tlocal float r;\n\n\tr = range (self.enemy);\n\n\tif (r == RANGE_FAR || r == RANGE_MID)//single shot at range\n\t{\n\t\tif (self.weapon == 5)\n\t\t\tsound (self, CHAN_WEAPON, \"weapons/ak112.wav\", PLAT_LOW_TRIGGER, ATTN_NORM);\n\t\tif (self.weapon == 6)\n\t\t\tsound (self, CHAN_WEAPON, \"weapons/ak47.wav\", PLAT_LOW_TRIGGER, ATTN_NORM);\n\t\tenforcer_single(200, 18);\n\t}\n\tif (r == RANGE_NEAR || r == RANGE_MELEE)//open up when close\n\t{\n\t\tif (self.weapon == 5)\n\t\t\tsound (self, CHAN_WEAPON, \"weapons/auto.wav\", PLAT_LOW_TRIGGER, ATTN_NORM);\n\t\tif (self.weapon == 6)\n\t\t\tsound (self, CHAN_WEAPON, \"weapons/auto2.wav\", PLAT_LOW_TRIGGER, ATTN_NORM);\n\t\tenf_burst1();\n\t}\n};\n\nvoid() monster_enforcer =\n{\n\t\tprecache_model2 (\"progs/enforcer.mdl\");\n\t\tprecache_model2 (\"progs/h_mega.mdl\");\n\t\tprecache_model2 (\"progs/laser.mdl\");\n\n\t\tprecache_sound2 (\"enforcer/death1.wav\");\n\t\tprecache_sound2 (\"enforcer/enfire.wav\");\n\t\tprecache_sound2 (\"enforcer/enfstop.wav\");\n\t\tprecache_sound2 (\"enforcer/idle1.wav\");\n\t\tprecache_sound2 (\"enforcer/pain1.wav\");\n\t\tprecache_sound2 (\"enforcer/pain2.wav\");\n\t\tprecache_sound2 (\"enforcer/sight1.wav\");\n\t\tprecache_sound2 (\"enforcer/sight2.wav\");\n\t\tprecache_sound2 (\"enforcer/sight3.wav\");\n\t\tprecache_sound2 (\"enforcer/sight4.wav\");\n\t\n\t\tself.solid = SOLID_SLIDEBOX;\n\t\tself.movetype = MOVETYPE_STEP;\n\t\tself.team = 3;\n\n\t\tsetmodel (self, \"progs/enforcer.mdl\");\n\t\tself.classname = \"monster\";\n\t\tself.netname = \"enforcer\";\n\n\t\tsetsize (self, '-12 -12 -24', '12 12 32');\n\t\tself.health = 100;\n            self.islot3 = SlotVal(IID_ARM_COMBAT, 1);\n\t\tself.armortype = 0.5;\n\t\tself.helmet = 1;\n\t\tself.th_stand = enf_stand1;\n\t\tself.th_walk = enf_walk1;\n\t\tself.th_run = enf_run1;\n\t\tself.th_pain = enf_pain;\n\t\tself.th_die = enf_die;\n\t\tself.th_missile = enf_atk1;\n\n\t\tself.weapon = 6;\n\t\tself.th_missile = enf_atk1;\n\t\tself.mag1 = 24;\n\t\tself.maxmag1 = self.mag1;\n\n\t\twalkmonster_start();\n\t\treturn;\n};\n\n#undef mag1\n#undef maxmag1\n"
  },
  {
    "path": "quakec/fallout2/fight.qc",
    "content": "\n/*\n\nA monster is in fight mode if it thinks it can effectively attack its\nenemy.\n\nWhen it decides it can't attack, it goes into hunt mode.\n\n*/\n\nvoid() knight_atk1;\nvoid() knight_runatk1;\nfloat() DemonCheckAttack;\n\nfloat(float v) anglemod;\n\nvoid(vector dest) ChooseTurn;\n\nvoid() ai_face;\n\n\nfloat\tenemy_vis, enemy_infront, enemy_range;\nfloat\tenemy_yaw;\n\n\nvoid() knight_attack =\n{\n\tlocal float\t\tlen;\n\t\n// decide if now is a good swing time\n\tlen = vlen(self.enemy.origin+self.enemy.view_ofs - (self.origin+self.view_ofs));\n\t\n\tif (len<80)\n\t\tknight_atk1 ();\n\telse\n\t\tknight_runatk1 ();\n};\n\n//=============================================================================\n\n/*\n===========\nCheckAttack\n\nThe player is in view, so decide to move or launch an attack\nReturns FALSE if movement should continue\n============\n*/\nfloat() CheckAttack =\n{\n\tlocal vector\tspot1, spot2;\t\n\tlocal entity\ttarg;\n\tlocal float\t\tchance;\n\n\ttarg = self.enemy;\n\t\n// see if any entities are in the way of the shot\n\tspot1 = self.origin + self.view_ofs;\n\tspot2 = targ.origin + targ.view_ofs;\n\n\ttraceline (spot1, spot2, FALSE, self);\n\n\tif (trace_ent != targ)\n\t\treturn FALSE;\t\t// don't have a clear shot\n\t\t\t\n\tif (trace_inopen && trace_inwater)\n\t\treturn FALSE;\t\t\t// sight line crossed contents\n\n\tif (enemy_range == RANGE_MELEE)\n\t{\t// melee attack\n\t\tif (self.th_melee)\n\t\t{\n\t\t\tif (self.classname == \"monster_knight\")\n\t\t\t\tknight_attack ();\n\t\t\telse\n\t\t\t\tself.th_melee ();\n\t\t\treturn TRUE;\n\t\t}\n\t}\n\t\n// missile attack\n\tif (!self.th_missile)\n\t\treturn FALSE;\n\t\t\n\tif (time < self.attack_finished)\n\t\treturn FALSE;\n\t\t\n\tif (enemy_range == RANGE_FAR)\n\t\treturn FALSE;\n\t\t\n\tif (enemy_range == RANGE_MELEE)\n\t{\n\t\tchance = 0.9;\n\t\tself.attack_finished = 0;\n\t}\n\telse if (enemy_range == RANGE_NEAR)\n\t{\n\t\tif (self.th_melee)\n\t\t\tchance = 0.2;\n\t\telse\n\t\t\tchance = 0.4;\n\t}\n\telse if (enemy_range == RANGE_MID)\n\t{\n\t\tif (self.th_melee)\n\t\t\tchance = 0.05;\n\t\telse\n\t\t\tchance = 0.1;\n\t}\n\telse\n\t\tchance = 0;\n\n\tif (random () < chance)\n\t{\n\t\tself.th_missile ();\n\t\tSUB_AttackFinished (2*random());\n\t\treturn TRUE;\n\t}\n\n\treturn FALSE;\n};\n\n\n/*\n=============\nai_face\n\nStay facing the enemy\n=============\n*/\nvoid() ai_face =\n{\n\tself.ideal_yaw = vectoyaw(self.enemy.origin - self.origin);\n\tChangeYaw ();\n};\n\n/*\n=============\nai_charge\n\nThe monster is in a melee attack, so get as close as possible to .enemy\n=============\n*/\nfloat (entity targ) visible;\nfloat(entity targ) infront;\nfloat(entity targ) range;\n\nvoid(float d) ai_charge =\n{\n\tai_face ();\t\n\tmovetogoal (d);\t\t// done in C code...\n};\n\nvoid() ai_charge_side =\n{\n\tlocal\tvector\tdtemp;\n\tlocal\tfloat\theading;\n\t\n// aim to the left of the enemy for a flyby\n\n\tself.ideal_yaw = vectoyaw(self.enemy.origin - self.origin);\n\tChangeYaw ();\n\n\tmakevectors (self.angles);\n\tdtemp = self.enemy.origin - 30*v_right;\n\theading = vectoyaw(dtemp - self.origin);\n\t\n\twalkmove(heading, 20);\n};\n\n\n/*\n=============\nai_melee\n\n=============\n*/\nvoid() ai_melee =\n{\n\tlocal vector\tdelta;\n\tlocal float \tldmg;\n\n\tif (!self.enemy)\n\t\treturn;\t\t// removed before stroke\n\t\t\n\tdelta = self.enemy.origin - self.origin;\n\n\tif (vlen(delta) > 60)\n\t\treturn;\n\t\t\n\tldmg = 6+random()*6;\n\tT_Damage (self.enemy, self, self, ldmg);\n};\n\n\nvoid() ai_melee_side =\n{\n\tlocal vector\tdelta;\n\tlocal float \tldmg;\n\n\tif (!self.enemy)\n\t\treturn;\t\t// removed before stroke\n\t\t\n\tai_charge_side();\n\t\n\tdelta = self.enemy.origin - self.origin;\n\n\tif (vlen(delta) > 60)\n\t\treturn;\n\tif (!CanDamage (self.enemy, self))\n\t\treturn;\n\tldmg = (random() + random() + random()) * 3;\n\tT_Damage (self.enemy, self, self, ldmg);\n};\n\n\n//=============================================================================\n\n/*\n===========\nSoldierCheckAttack\n\nThe player is in view, so decide to move or launch an attack\nReturns FALSE if movement should continue\n============\n*/\nfloat() SoldierCheckAttack =\n{\n\tlocal vector\tspot1, spot2;\t\n\tlocal entity\ttarg;\n\tlocal float\t\tchance;\n\n\ttarg = self.enemy;\n\t\n// see if any entities are in the way of the shot\n\tspot1 = self.origin + self.view_ofs;\n\tspot2 = targ.origin + targ.view_ofs;\n\n\ttraceline (spot1, spot2, FALSE, self);\n\n\tif (trace_inopen && trace_inwater)\n\t\treturn FALSE;\t\t\t// sight line crossed contents\n\n\tif (trace_ent != targ)\n\t\treturn FALSE;\t// don't have a clear shot\n\t\t\t\n\t\n// missile attack\n\tif (time < self.attack_finished)\n\t\treturn FALSE;\n\t\t\n\tif (enemy_range == RANGE_FAR)\n\t\treturn FALSE;\n\t\t\n\tif (enemy_range == RANGE_MELEE)\n\t\tchance = 0.9;\n\telse if (enemy_range == RANGE_NEAR)\n\t\tchance = 0.4;\n\telse if (enemy_range == RANGE_MID)\n\t\tchance = 0.05;\n\telse\n\t\tchance = 0;\n\n\tif (random () < chance)\n\t{\n\t\tself.th_missile ();\n\t\tSUB_AttackFinished (1 + random());\n\t\tif (random() < 0.3)\n\t\t\tself.lefty = !self.lefty;\n\n\t\treturn TRUE;\n\t}\n\n\treturn FALSE;\n};\n//=============================================================================\n\n/*\n===========\nShamCheckAttack\n\nThe player is in view, so decide to move or launch an attack\nReturns FALSE if movement should continue\n============\n*/\nfloat() ShamCheckAttack =\n{\n\tlocal vector\tspot1, spot2;\t\n\tlocal entity\ttarg;\n\n\tif (enemy_range == RANGE_MELEE)\n\t{\n\t\tif (CanDamage (self.enemy, self))\n\t\t{\n\t\t\tself.attack_state = AS_MELEE;\n\t\t\treturn TRUE;\n\t\t}\n\t}\n\n\tif (time < self.attack_finished)\n\t\treturn FALSE;\n\t\n\tif (!enemy_vis)\n\t\treturn FALSE;\n\t\t\n\ttarg = self.enemy;\n\t\n// see if any entities are in the way of the shot\n\tspot1 = self.origin + self.view_ofs;\n\tspot2 = targ.origin + targ.view_ofs;\n\n\tif (vlen(spot1 - spot2) > 600)\n\t\treturn FALSE;\n\n\ttraceline (spot1, spot2, FALSE, self);\n\n\tif (trace_inopen && trace_inwater)\n\t\treturn FALSE;\t\t\t// sight line crossed contents\n\n\tif (trace_ent != targ)\n\t{\n\t\treturn FALSE;\t// don't have a clear shot\n\t}\n\t\t\t\n// missile attack\n\tif (enemy_range == RANGE_FAR)\n\t\treturn FALSE;\n\t\t\n\tself.attack_state = AS_MISSILE;\n\tSUB_AttackFinished (2 + 2*random());\n\treturn TRUE;\n};\n\n//============================================================================\n\n/*\n===========\nOgreCheckAttack\n\nThe player is in view, so decide to move or launch an attack\nReturns FALSE if movement should continue\n============\n*/\nfloat() OgreCheckAttack =\n{\n\tlocal vector\tspot1, spot2;\t\n\tlocal entity\ttarg;\n\tlocal float\t\tchance;\n\n\tif (enemy_range == RANGE_MELEE)\n\t{\n\t\tif (CanDamage (self.enemy, self))\n\t\t{\n\t\t\tself.attack_state = AS_MELEE;\n\t\t\treturn TRUE;\n\t\t}\n\t}\n\n\tif (time < self.attack_finished)\n\t\treturn FALSE;\n\t\n\tif (!enemy_vis)\n\t\treturn FALSE;\n\t\t\n\ttarg = self.enemy;\n\t\n// see if any entities are in the way of the shot\n\tspot1 = self.origin + self.view_ofs;\n\tspot2 = targ.origin + targ.view_ofs;\n\n\ttraceline (spot1, spot2, FALSE, self);\n\n\tif (trace_inopen && trace_inwater)\n\t\treturn FALSE;\t\t\t// sight line crossed contents\n\n\tif (trace_ent != targ)\n\t{\n\t\treturn FALSE;\t// don't have a clear shot\n\t}\n\t\t\t\n// missile attack\n\tif (time < self.attack_finished)\n\t\treturn FALSE;\n\t\t\n\tif (enemy_range == RANGE_FAR)\n\t\treturn FALSE;\n\t\t\n\telse if (enemy_range == RANGE_NEAR)\n\t\tchance = 0.10;\n\telse if (enemy_range == RANGE_MID)\n\t\tchance = 0.05;\n\telse\n\t\tchance = 0;\n\n\tself.attack_state = AS_MISSILE;\n\tSUB_AttackFinished (1 + 2*random());\n\treturn TRUE;\n};\n\n"
  },
  {
    "path": "quakec/fallout2/fish.qc",
    "content": "$cd id1/models/fish\n$origin 0 0 24\n$base base\t\t\n$skin skin\n\n$frame attack1 attack2 attack3 attack4 attack5 attack6 \n$frame attack7 attack8 attack9 attack10 attack11 attack12 attack13 \n$frame attack14 attack15 attack16 attack17 attack18 \n\n$frame death1 death2 death3 death4 death5 death6 death7 \n$frame death8 death9 death10 death11 death12 death13 death14 death15 \n$frame death16 death17 death18 death19 death20 death21 \n\n$frame swim1 swim2 swim3 swim4 swim5 swim6 swim7 swim8 \n$frame swim9 swim10 swim11 swim12 swim13 swim14 swim15 swim16 swim17 \n$frame swim18 \n\n$frame pain1 pain2 pain3 pain4 pain5 pain6 pain7 pain8 \n$frame pain9 \n\nvoid() swimmonster_start;\n\nvoid() f_stand1  =[      $swim1, f_stand2 ] {ai_stand();};\nvoid() f_stand2  =[      $swim2, f_stand3 ] {ai_stand();};\nvoid() f_stand3  =[      $swim3, f_stand4 ] {ai_stand();};\nvoid() f_stand4  =[      $swim4, f_stand5 ] {ai_stand();};\nvoid() f_stand5  =[      $swim5, f_stand6 ] {ai_stand();};\nvoid() f_stand6  =[      $swim6, f_stand7 ] {ai_stand();};\nvoid() f_stand7  =[      $swim7, f_stand8 ] {ai_stand();};\nvoid() f_stand8  =[      $swim8, f_stand9 ] {ai_stand();};\nvoid() f_stand9  =[      $swim9, f_stand10  ] {ai_stand();};\nvoid() f_stand10 =[      $swim10, f_stand11 ] {ai_stand();};\nvoid() f_stand11 =[      $swim11, f_stand12 ] {ai_stand();};\nvoid() f_stand12 =[      $swim12, f_stand13 ] {ai_stand();};\nvoid() f_stand13 =[      $swim13, f_stand14 ] {ai_stand();};\nvoid() f_stand14 =[      $swim14, f_stand15 ] {ai_stand();};\nvoid() f_stand15 =[      $swim15, f_stand16 ] {ai_stand();};\nvoid() f_stand16 =[      $swim16, f_stand17 ] {ai_stand();};\nvoid() f_stand17 =[      $swim17, f_stand18 ] {ai_stand();};\nvoid() f_stand18 =[      $swim18, f_stand1 ] {ai_stand();};\n\nvoid() f_walk1  =[      $swim1, f_walk2 ] {ai_walk(8);};\nvoid() f_walk2  =[      $swim2, f_walk3 ] {ai_walk(8);};\nvoid() f_walk3  =[      $swim3, f_walk4 ] {ai_walk(8);};\nvoid() f_walk4  =[      $swim4, f_walk5 ] {ai_walk(8);};\nvoid() f_walk5  =[      $swim5, f_walk6 ] {ai_walk(8);};\nvoid() f_walk6  =[      $swim6, f_walk7 ] {ai_walk(8);};\nvoid() f_walk7  =[      $swim7, f_walk8 ] {ai_walk(8);};\nvoid() f_walk8  =[      $swim8, f_walk9 ] {ai_walk(8);};\nvoid() f_walk9  =[      $swim9, f_walk10  ] {ai_walk(8);};\nvoid() f_walk10 =[      $swim10, f_walk11 ] {ai_walk(8);};\nvoid() f_walk11 =[      $swim11, f_walk12 ] {ai_walk(8);};\nvoid() f_walk12 =[      $swim12, f_walk13 ] {ai_walk(8);};\nvoid() f_walk13 =[      $swim13, f_walk14 ] {ai_walk(8);};\nvoid() f_walk14 =[      $swim14, f_walk15 ] {ai_walk(8);};\nvoid() f_walk15 =[      $swim15, f_walk16 ] {ai_walk(8);};\nvoid() f_walk16 =[      $swim16, f_walk17 ] {ai_walk(8);};\nvoid() f_walk17 =[      $swim17, f_walk18 ] {ai_walk(8);};\nvoid() f_walk18 =[      $swim18, f_walk1 ] {ai_walk(8);};\n\nvoid() f_run1  =[      $swim1, f_run2 ] {ai_run(12);\n\tif (random() < 0.5)\n\t\tsound (self, CHAN_VOICE, \"fish/idle.wav\", 1, ATTN_NORM);\n};\nvoid() f_run2  =[      $swim3, f_run3 ] {ai_run(12);};\nvoid() f_run3  =[      $swim5, f_run4 ] {ai_run(12);};\nvoid() f_run4  =[      $swim7, f_run5 ] {ai_run(12);};\nvoid() f_run5  =[      $swim9, f_run6 ] {ai_run(12);};\nvoid() f_run6  =[      $swim11, f_run7 ] {ai_run(12);};\nvoid() f_run7  =[      $swim13, f_run8 ] {ai_run(12);};\nvoid() f_run8  =[      $swim15, f_run9 ] {ai_run(12);};\nvoid() f_run9  =[      $swim17, f_run1 ] {ai_run(12);};\n\nvoid() fish_melee =\n{\n\tlocal vector\tdelta;\n\tlocal float \tldmg;\n\n\tif (!self.enemy)\n\t\treturn;\t\t// removed before stroke\n\t\t\n\tdelta = self.enemy.origin - self.origin;\n\n\tif (vlen(delta) > 60)\n\t\treturn;\n\t\t\n\tsound (self, CHAN_VOICE, \"fish/bite.wav\", 1, ATTN_NORM);\n\tldmg = (random() + random()) * 3;\n\tT_Damage (self.enemy, self, self, ldmg);\n};\n\nvoid() f_attack1        =[      $attack1,       f_attack2 ] {ai_charge(10);};\nvoid() f_attack2        =[      $attack2,       f_attack3 ] {ai_charge(10);};\nvoid() f_attack3        =[      $attack3,       f_attack4 ] {fish_melee();};\nvoid() f_attack4        =[      $attack4,       f_attack5 ] {ai_charge(10);};\nvoid() f_attack5        =[      $attack5,       f_attack6 ] {ai_charge(10);};\nvoid() f_attack6        =[      $attack6,       f_attack7 ] {ai_charge(10);};\nvoid() f_attack7        =[      $attack7,       f_attack8 ] {ai_charge(10);};\nvoid() f_attack8        =[      $attack8,       f_attack9 ] {ai_charge(10);};\nvoid() f_attack9        =[      $attack9,       f_attack10] {fish_melee();};\nvoid() f_attack10       =[      $attack10,      f_attack11] {ai_charge(10);};\nvoid() f_attack11       =[      $attack11,      f_attack12] {ai_charge(10);};\nvoid() f_attack12       =[      $attack12,      f_attack13] {ai_charge(10);};\nvoid() f_attack13       =[      $attack13,      f_attack14] {ai_charge(10);};\nvoid() f_attack14       =[      $attack14,      f_attack15] {ai_charge(10);};\nvoid() f_attack15       =[      $attack15,      f_attack16] {fish_melee();};\nvoid() f_attack16       =[      $attack16,      f_attack17] {ai_charge(10);};\nvoid() f_attack17       =[      $attack17,      f_attack18] {ai_charge(10);};\nvoid() f_attack18       =[      $attack18,      f_run1    ] {ai_charge(10);};\n\nvoid() f_death1 =[      $death1,        f_death2        ] {\nsound (self, CHAN_VOICE, \"fish/death.wav\", 1, ATTN_NORM);\n};\nvoid() f_death2 =[      $death2,        f_death3        ] {};\nvoid() f_death3 =[      $death3,        f_death4        ] {};\nvoid() f_death4 =[      $death4,        f_death5        ] {};\nvoid() f_death5 =[      $death5,        f_death6        ] {};\nvoid() f_death6 =[      $death6,        f_death7        ] {};\nvoid() f_death7 =[      $death7,        f_death8        ] {};\nvoid() f_death8 =[      $death8,        f_death9        ] {};\nvoid() f_death9 =[      $death9,        f_death10       ] {};\nvoid() f_death10 =[      $death10,       f_death11       ] {};\nvoid() f_death11 =[      $death11,       f_death12       ] {};\nvoid() f_death12 =[      $death12,       f_death13       ] {};\nvoid() f_death13 =[      $death13,       f_death14       ] {};\nvoid() f_death14 =[      $death14,       f_death15       ] {};\nvoid() f_death15 =[      $death15,       f_death16       ] {};\nvoid() f_death16 =[      $death16,       f_death17       ] {};\nvoid() f_death17 =[      $death17,       f_death18       ] {};\nvoid() f_death18 =[      $death18,       f_death19       ] {};\nvoid() f_death19 =[      $death19,       f_death20       ] {};\nvoid() f_death20 =[      $death20,       f_death21       ] {};\nvoid() f_death21 =[      $death21,       f_death21       ] {self.solid = SOLID_NOT;};\n\nvoid() f_pain1  =[      $pain1, f_pain2 ] {};\nvoid() f_pain2  =[      $pain2, f_pain3 ] {ai_pain(6);};\nvoid() f_pain3  =[      $pain3, f_pain4 ] {ai_pain(6);};\nvoid() f_pain4  =[      $pain4, f_pain5 ] {ai_pain(6);};\nvoid() f_pain5  =[      $pain5, f_pain6 ] {ai_pain(6);};\nvoid() f_pain6  =[      $pain6, f_pain7 ] {ai_pain(6);};\nvoid() f_pain7  =[      $pain7, f_pain8 ] {ai_pain(6);};\nvoid() f_pain8  =[      $pain8, f_pain9 ] {ai_pain(6);};\nvoid() f_pain9  =[      $pain9, f_run1 ] {ai_pain(6);};\n\nvoid(entity attacker, float damage)\tfish_pain =\n{\n\n// fish allways do pain frames\n\tf_pain1 ();\n};\n\nvoid() monster_fish =\n{\n\tprecache_model2 (\"progs/fish.mdl\");\n\n\tprecache_sound2 (\"fish/death.wav\");\n\tprecache_sound2 (\"fish/bite.wav\");\n\tprecache_sound2 (\"fish/idle.wav\");\n\n\tself.solid = SOLID_SLIDEBOX;\n\tself.movetype = MOVETYPE_STEP;\n\n\tsetmodel (self, \"progs/fish.mdl\");\n\n\tsetsize (self, '-16 -16 -24', '16 16 24');\n\tself.health = 50;\n\tself.helmet = 1;\n\tself.corporation = 3;\n\tself.classname = \"monster\";\n\tself.netname = \"pirannah\";\n\n\tself.th_stand = f_stand1;\n\tself.th_walk = f_walk1;\n\tself.th_run = f_run1;\n\tself.th_die = f_death1;\n\tself.th_pain = fish_pain;\n\tself.th_melee = f_attack1;\n\t\n\tswimmonster_start ();\n};\n\n"
  },
  {
    "path": "quakec/fallout2/hknight.qc",
    "content": "/*\n==============================================================================\n\nKNIGHT\n\n==============================================================================\n*/\n\n$cd id1/models/knight2\n$origin 0 0 24\n$base base\n$skin skin\n\n$frame stand1 stand2 stand3 stand4 stand5 stand6 stand7 stand8 stand9\n\n$frame walk1 walk2 walk3 walk4 walk5 walk6 walk7 walk8 walk9\n$frame walk10 walk11 walk12 walk13 walk14 walk15 walk16 walk17\n$frame walk18 walk19 walk20\n\n$frame run1 run2 run3 run4 run5 run6 run7 run8\n\n$frame pain1 pain2 pain3 pain4 pain5\n\n$frame death1 death2 death3 death4 death5 death6 death7 death8\n$frame death9 death10 death11 death12\n\n$frame deathb1 deathb2 deathb3 deathb4 deathb5 deathb6 deathb7 deathb8\n$frame deathb9\n\n$frame char_a1 char_a2 char_a3 char_a4 char_a5 char_a6 char_a7 char_a8\n$frame char_a9 char_a10 char_a11 char_a12 char_a13 char_a14 char_a15 char_a16\n\n$frame magica1 magica2 magica3 magica4 magica5 magica6 magica7 magica8\n$frame magica9 magica10 magica11 magica12 magica13 magica14\n\n$frame magicb1 magicb2 magicb3 magicb4 magicb5 magicb6 magicb7 magicb8\n$frame magicb9 magicb10 magicb11 magicb12 magicb13\n\n$frame char_b1 char_b2 char_b3 char_b4 char_b5 char_b6\n\n$frame slice1 slice2 slice3 slice4 slice5 slice6 slice7 slice8 slice9 slice10\n\n$frame smash1 smash2 smash3 smash4 smash5 smash6 smash7 smash8 smash9 smash10\n$frame smash11\n\n$frame w_attack1 w_attack2 w_attack3 w_attack4 w_attack5 w_attack6 w_attack7 \n$frame w_attack8 w_attack9 w_attack10 w_attack11 w_attack12 w_attack13 w_attack14\n$frame w_attack15 w_attack16 w_attack17 w_attack18 w_attack19 w_attack20 \n$frame w_attack21 w_attack22 \n\n$frame magicc1 magicc2 magicc3 magicc4 magicc5 magicc6 magicc7 magicc8\n$frame magicc9 magicc10 magicc11\n\n\nvoid() hknight_char_a1;\nvoid() hknight_run1;\nvoid() hk_idle_sound;\n\nvoid(float offset) hknight_shot =\n{\n\tlocal\tvector\toffang;\n\tlocal\tvector\torg, vec;\n\t\n\toffang = vectoangles (self.enemy.origin - self.origin);\n\toffang_y = offang_y + offset * 6;\n\t\n\tmakevectors (offang);\n\n\torg = self.origin + self.mins + self.size*0.5 + v_forward * 20;\n\n// set missile speed\n\tvec = normalize (v_forward);\n\tvec_z = 0 - vec_z + (random() - 0.5)*0.1;\n\t\n\tlaunch_spike (org, vec);\n\tnewmis.classname = \"knightspike\";\n\tsetmodel (newmis, \"progs/k_spike.mdl\");\n\tsetsize (newmis, VEC_ORIGIN, VEC_ORIGIN);\t\t\n\tnewmis.velocity = vec*300;\n\tsound (self, CHAN_WEAPON, \"hknight/attack1.wav\", 1, ATTN_NORM);\n};\n\nvoid() CheckForCharge =\n{\n// check for mad charge\nif (!enemy_vis)\n\treturn;\nif (time < self.attack_finished)\n\treturn;\t\nif ( fabs(self.origin_z - self.enemy.origin_z) > 20)\n\treturn;\t\t// too much height change\nif ( vlen (self.origin - self.enemy.origin) < 80)\n\treturn;\t\t// use regular attack\n\n// charge\t\t\n\tSUB_AttackFinished (2);\n\thknight_char_a1 ();\n\n};\n\nvoid() CheckContinueCharge =\n{\n\tif (time > self.attack_finished)\n\t{\n\t\tSUB_AttackFinished (3);\n\t\thknight_run1 ();\n\t\treturn;\t\t// done charging\n\t}\n\tif (random() > 0.5)\n\t\tsound (self, CHAN_WEAPON, \"knight/sword2.wav\", 1, ATTN_NORM);\n\telse\n\t\tsound (self, CHAN_WEAPON, \"knight/sword1.wav\", 1, ATTN_NORM);\n};\n\n//===========================================================================\n\nvoid()\thknight_stand1\t=[\t$stand1,\thknight_stand2\t] {ai_stand();};\nvoid()\thknight_stand2\t=[\t$stand2,\thknight_stand3\t] {ai_stand();};\nvoid()\thknight_stand3\t=[\t$stand3,\thknight_stand4\t] {ai_stand();};\nvoid()\thknight_stand4\t=[\t$stand4,\thknight_stand5\t] {ai_stand();};\nvoid()\thknight_stand5\t=[\t$stand5,\thknight_stand6\t] {ai_stand();};\nvoid()\thknight_stand6\t=[\t$stand6,\thknight_stand7\t] {ai_stand();};\nvoid()\thknight_stand7\t=[\t$stand7,\thknight_stand8\t] {ai_stand();};\nvoid()\thknight_stand8\t=[\t$stand8,\thknight_stand9\t] {ai_stand();};\nvoid()\thknight_stand9\t=[\t$stand9,\thknight_stand1\t] {ai_stand();};\n\n//===========================================================================\n\nvoid()\thknight_walk1\t=[\t$walk1,\t\thknight_walk2\t] {\nhk_idle_sound();\nai_walk(2);};\nvoid()\thknight_walk2\t=[\t$walk2,\t\thknight_walk3\t] {ai_walk(5);};\nvoid()\thknight_walk3\t=[\t$walk3,\t\thknight_walk4\t] {ai_walk(5);};\nvoid()\thknight_walk4\t=[\t$walk4,\t\thknight_walk5\t] {ai_walk(4);};\nvoid()\thknight_walk5\t=[\t$walk5,\t\thknight_walk6\t] {ai_walk(4);};\nvoid()\thknight_walk6\t=[\t$walk6,\t\thknight_walk7\t] {ai_walk(2);};\nvoid()\thknight_walk7\t=[\t$walk7,\t\thknight_walk8\t] {ai_walk(2);};\nvoid()\thknight_walk8\t=[\t$walk8,\t\thknight_walk9\t] {ai_walk(3);};\nvoid()\thknight_walk9\t=[\t$walk9,\t\thknight_walk10\t] {ai_walk(3);};\nvoid()\thknight_walk10\t=[\t$walk10,\thknight_walk11\t] {ai_walk(4);};\nvoid()\thknight_walk11\t=[\t$walk11,\thknight_walk12\t] {ai_walk(3);};\nvoid()\thknight_walk12\t=[\t$walk12,\thknight_walk13\t] {ai_walk(4);};\nvoid()\thknight_walk13\t=[\t$walk13,\thknight_walk14\t] {ai_walk(6);};\nvoid()\thknight_walk14\t=[\t$walk14,\thknight_walk15\t] {ai_walk(2);};\nvoid()\thknight_walk15\t=[\t$walk15,\thknight_walk16\t] {ai_walk(2);};\nvoid()\thknight_walk16\t=[\t$walk16,\thknight_walk17\t] {ai_walk(4);};\nvoid()\thknight_walk17\t=[\t$walk17,\thknight_walk18\t] {ai_walk(3);};\nvoid()\thknight_walk18\t=[\t$walk18,\thknight_walk19\t] {ai_walk(3);};\nvoid()\thknight_walk19\t=[\t$walk19,\thknight_walk20\t] {ai_walk(3);};\nvoid()\thknight_walk20\t=[\t$walk20,\thknight_walk1\t] {ai_walk(2);};\n\n//===========================================================================\n\nvoid()\thknight_run1\t=[\t$run1,\t\thknight_run2\t] {\nhk_idle_sound();\nai_run (20); CheckForCharge (); };\nvoid()\thknight_run2\t=[\t$run2,\t\thknight_run3\t] {ai_run(25);};\nvoid()\thknight_run3\t=[\t$run3,\t\thknight_run4\t] {ai_run(18);};\nvoid()\thknight_run4\t=[\t$run4,\t\thknight_run5\t] {ai_run(16);};\nvoid()\thknight_run5\t=[\t$run5,\t\thknight_run6\t] {ai_run(14);};\nvoid()\thknight_run6\t=[\t$run6,\t\thknight_run7\t] {ai_run(25);};\nvoid()\thknight_run7\t=[\t$run7,\t\thknight_run8\t] {ai_run(21);};\nvoid()\thknight_run8\t=[\t$run8,\t\thknight_run1\t] {ai_run(13);};\n\n//============================================================================\n\nvoid()\thknight_pain1\t=[\t$pain1,\t\thknight_pain2\t] {sound (self, CHAN_VOICE, \"hknight/pain1.wav\", 1, ATTN_NORM);};\nvoid()\thknight_pain2\t=[\t$pain2,\t\thknight_pain3\t] {};\nvoid()\thknight_pain3\t=[\t$pain3,\t\thknight_pain4\t] {};\nvoid()\thknight_pain4\t=[\t$pain4,\t\thknight_pain5\t] {};\nvoid()\thknight_pain5\t=[\t$pain5,\t\thknight_run1\t] {};\n\n//============================================================================\n\nvoid()\thknight_die1\t=[\t$death1,\thknight_die2\t] {ai_forward(10);};\nvoid()\thknight_die2\t=[\t$death2,\thknight_die3\t] {ai_forward(8);};\nvoid()\thknight_die3\t=[\t$death3,\thknight_die4\t]\n{self.solid = SOLID_NOT; ai_forward(7);};\nvoid()\thknight_die4\t=[\t$death4,\thknight_die5\t] {};\nvoid()\thknight_die5\t=[\t$death5,\thknight_die6\t] {};\nvoid()\thknight_die6\t=[\t$death6,\thknight_die7\t] {};\nvoid()\thknight_die7\t=[\t$death7,\thknight_die8\t] {};\nvoid()\thknight_die8\t=[\t$death8,\thknight_die9\t] {ai_forward(10);};\nvoid()\thknight_die9\t=[\t$death9,\thknight_die10\t] {ai_forward(11);};\nvoid()\thknight_die10\t=[\t$death10,\thknight_die11\t] {};\nvoid()\thknight_die11\t=[\t$death11,\thknight_die12\t] {};\nvoid()\thknight_die12\t=[\t$death12,\thknight_die12\t] {};\n\nvoid()\thknight_dieb1\t=[\t$deathb1,\thknight_dieb2\t] {};\nvoid()\thknight_dieb2\t=[\t$deathb2,\thknight_dieb3\t] {};\nvoid()\thknight_dieb3\t=[\t$deathb3,\thknight_dieb4\t]\n{self.solid = SOLID_NOT;};\nvoid()\thknight_dieb4\t=[\t$deathb4,\thknight_dieb5\t] {};\nvoid()\thknight_dieb5\t=[\t$deathb5,\thknight_dieb6\t] {};\nvoid()\thknight_dieb6\t=[\t$deathb6,\thknight_dieb7\t] {};\nvoid()\thknight_dieb7\t=[\t$deathb7,\thknight_dieb8\t] {};\nvoid()\thknight_dieb8\t=[\t$deathb8,\thknight_dieb9\t] {};\nvoid()\thknight_dieb9\t=[\t$deathb9,\thknight_dieb9\t] {};\n\nvoid() hknight_die =\n{\n// check for gib\n\tif (self.health < -40)\n\t{\n\t\tsound (self, CHAN_VOICE, \"player/udeath.wav\", 1, ATTN_NORM);\n\t\tThrowHead (\"progs/h_hellkn.mdl\", self.health);\n\t\tThrowGib (\"progs/gib1.mdl\", self.health);\n\t\tThrowGib (\"progs/gib2.mdl\", self.health);\n\t\tThrowGib (\"progs/gib3.mdl\", self.health);\n\t\treturn;\n\t}\n\n// regular death\n\tsound (self, CHAN_VOICE, \"hknight/death1.wav\", 1, ATTN_NORM);\n\tif (random() > 0.5)\n\t\thknight_die1 ();\n\telse\n\t\thknight_dieb1 ();\n};\n\n\n//============================================================================\n\nvoid()\thknight_magica1 =[\t$magica1,\thknight_magica2\t] {ai_face();};\nvoid()\thknight_magica2 =[\t$magica2,\thknight_magica3\t] {ai_face();};\nvoid()\thknight_magica3 =[\t$magica3,\thknight_magica4\t] {ai_face();};\nvoid()\thknight_magica4 =[\t$magica4,\thknight_magica5\t] {ai_face();};\nvoid()\thknight_magica5 =[\t$magica5,\thknight_magica6\t] {ai_face();};\nvoid()\thknight_magica6 =[\t$magica6,\thknight_magica7\t] {ai_face();};\nvoid()\thknight_magica7 =[\t$magica7,\thknight_magica8\t] {hknight_shot(-2);};\nvoid()\thknight_magica8 =[\t$magica8,\thknight_magica9\t] {hknight_shot(-1);};\nvoid()\thknight_magica9 =[\t$magica9,\thknight_magica10] {hknight_shot(0);};\nvoid()\thknight_magica10 =[\t$magica10,\thknight_magica11] {hknight_shot(1);};\nvoid()\thknight_magica11 =[\t$magica11,\thknight_magica12] {hknight_shot(2);};\nvoid()\thknight_magica12 =[\t$magica12,\thknight_magica13] {hknight_shot(3);};\nvoid()\thknight_magica13 =[\t$magica13,\thknight_magica14] {ai_face();};\nvoid()\thknight_magica14 =[\t$magica14,\thknight_run1\t] {ai_face();};\n\n//============================================================================\n\nvoid()\thknight_magicb1 =[\t$magicb1,\thknight_magicb2\t] {ai_face();};\nvoid()\thknight_magicb2 =[\t$magicb2,\thknight_magicb3\t] {ai_face();};\nvoid()\thknight_magicb3 =[\t$magicb3,\thknight_magicb4\t] {ai_face();};\nvoid()\thknight_magicb4 =[\t$magicb4,\thknight_magicb5\t] {ai_face();};\nvoid()\thknight_magicb5 =[\t$magicb5,\thknight_magicb6\t] {ai_face();};\nvoid()\thknight_magicb6 =[\t$magicb6,\thknight_magicb7\t] {ai_face();};\nvoid()\thknight_magicb7 =[\t$magicb7,\thknight_magicb8\t] {hknight_shot(-2);};\nvoid()\thknight_magicb8 =[\t$magicb8,\thknight_magicb9\t] {hknight_shot(-1);};\nvoid()\thknight_magicb9 =[\t$magicb9,\thknight_magicb10] {hknight_shot(0);};\nvoid()\thknight_magicb10 =[\t$magicb10,\thknight_magicb11] {hknight_shot(1);};\nvoid()\thknight_magicb11 =[\t$magicb11,\thknight_magicb12] {hknight_shot(2);};\nvoid()\thknight_magicb12 =[\t$magicb12,\thknight_magicb13] {hknight_shot(3);};\nvoid()\thknight_magicb13 =[\t$magicb13,\thknight_run1] {ai_face();};\n\n//============================================================================\n\nvoid()\thknight_magicc1 =[\t$magicc1,\thknight_magicc2\t] {ai_face();};\nvoid()\thknight_magicc2 =[\t$magicc2,\thknight_magicc3\t] {ai_face();};\nvoid()\thknight_magicc3 =[\t$magicc3,\thknight_magicc4\t] {ai_face();};\nvoid()\thknight_magicc4 =[\t$magicc4,\thknight_magicc5\t] {ai_face();};\nvoid()\thknight_magicc5 =[\t$magicc5,\thknight_magicc6\t] {ai_face();};\nvoid()\thknight_magicc6 =[\t$magicc6,\thknight_magicc7\t] {hknight_shot(-2);};\nvoid()\thknight_magicc7 =[\t$magicc7,\thknight_magicc8\t] {hknight_shot(-1);};\nvoid()\thknight_magicc8 =[\t$magicc8,\thknight_magicc9\t] {hknight_shot(0);};\nvoid()\thknight_magicc9 =[\t$magicc9,\thknight_magicc10] {hknight_shot(1);};\nvoid()\thknight_magicc10 =[\t$magicc10,\thknight_magicc11] {hknight_shot(2);};\nvoid()\thknight_magicc11 =[\t$magicc11,\thknight_run1] {hknight_shot(3);};\n\n//===========================================================================\n\nvoid()\thknight_char_a1\t=[\t$char_a1,\thknight_char_a2\t] {ai_charge(20);};\nvoid()\thknight_char_a2\t=[\t$char_a2,\thknight_char_a3\t] {ai_charge(25);};\nvoid()\thknight_char_a3\t=[\t$char_a3,\thknight_char_a4\t] {ai_charge(18);};\nvoid()\thknight_char_a4\t=[\t$char_a4,\thknight_char_a5\t] {ai_charge(16);};\nvoid()\thknight_char_a5\t=[\t$char_a5,\thknight_char_a6\t] {ai_charge(14);};\nvoid()\thknight_char_a6\t=[\t$char_a6,\thknight_char_a7\t] {ai_charge(20); ai_melee();};\nvoid()\thknight_char_a7\t=[\t$char_a7,\thknight_char_a8\t] {ai_charge(21); ai_melee();};\nvoid()\thknight_char_a8\t=[\t$char_a8,\thknight_char_a9\t] {ai_charge(13); ai_melee();};\nvoid()\thknight_char_a9\t=[\t$char_a9,\thknight_char_a10\t] {ai_charge(20); ai_melee();};\nvoid()\thknight_char_a10=[\t$char_a10,\thknight_char_a11\t] {ai_charge(20); ai_melee();};\nvoid()\thknight_char_a11=[\t$char_a11,\thknight_char_a12\t] {ai_charge(18); ai_melee();};\nvoid()\thknight_char_a12=[\t$char_a12,\thknight_char_a13\t] {ai_charge(16);};\nvoid()\thknight_char_a13=[\t$char_a13,\thknight_char_a14\t] {ai_charge(14);};\nvoid()\thknight_char_a14=[\t$char_a14,\thknight_char_a15\t] {ai_charge(25);};\nvoid()\thknight_char_a15=[\t$char_a15,\thknight_char_a16\t] {ai_charge(21);};\nvoid()\thknight_char_a16=[\t$char_a16,\thknight_run1\t] {ai_charge(13);};\n\n//===========================================================================\n\nvoid()\thknight_char_b1\t=[\t$char_b1,\thknight_char_b2\t]\n{CheckContinueCharge (); ai_charge(23); ai_melee();};\nvoid()\thknight_char_b2\t=[\t$char_b2,\thknight_char_b3\t] {ai_charge(17); ai_melee();};\nvoid()\thknight_char_b3\t=[\t$char_b3,\thknight_char_b4\t] {ai_charge(12); ai_melee();};\nvoid()\thknight_char_b4\t=[\t$char_b4,\thknight_char_b5\t] {ai_charge(22); ai_melee();};\nvoid()\thknight_char_b5\t=[\t$char_b5,\thknight_char_b6\t] {ai_charge(18); ai_melee();};\nvoid()\thknight_char_b6\t=[\t$char_b6,\thknight_char_b1\t] {ai_charge(8); ai_melee();};\n\n//===========================================================================\n\nvoid()\thknight_slice1\t=[\t$slice1,\thknight_slice2\t] {ai_charge(9);};\nvoid()\thknight_slice2\t=[\t$slice2,\thknight_slice3\t] {ai_charge(6);};\nvoid()\thknight_slice3\t=[\t$slice3,\thknight_slice4\t] {ai_charge(13);};\nvoid()\thknight_slice4\t=[\t$slice4,\thknight_slice5\t] {ai_charge(4);};\nvoid()\thknight_slice5\t=[\t$slice5,\thknight_slice6\t] {ai_charge(7); ai_melee();};\nvoid()\thknight_slice6\t=[\t$slice6,\thknight_slice7\t] {ai_charge(15); ai_melee();};\nvoid()\thknight_slice7\t=[\t$slice7,\thknight_slice8\t] {ai_charge(8); ai_melee();};\nvoid()\thknight_slice8\t=[\t$slice8,\thknight_slice9\t] {ai_charge(2); ai_melee();};\nvoid()\thknight_slice9\t=[\t$slice9,\thknight_slice10\t] {ai_melee();};\nvoid()\thknight_slice10\t=[\t$slice10,\thknight_run1\t] {ai_charge(3);};\n\n//===========================================================================\n\nvoid()\thknight_smash1\t=[\t$smash1,\thknight_smash2\t] {ai_charge(1);};\nvoid()\thknight_smash2\t=[\t$smash2,\thknight_smash3\t] {ai_charge(13);};\nvoid()\thknight_smash3\t=[\t$smash3,\thknight_smash4\t] {ai_charge(9);};\nvoid()\thknight_smash4\t=[\t$smash4,\thknight_smash5\t] {ai_charge(11);};\nvoid()\thknight_smash5\t=[\t$smash5,\thknight_smash6\t] {ai_charge(10); ai_melee();};\nvoid()\thknight_smash6\t=[\t$smash6,\thknight_smash7\t] {ai_charge(7); ai_melee();};\nvoid()\thknight_smash7\t=[\t$smash7,\thknight_smash8\t] {ai_charge(12); ai_melee();};\nvoid()\thknight_smash8\t=[\t$smash8,\thknight_smash9\t] {ai_charge(2); ai_melee();};\nvoid()\thknight_smash9\t=[\t$smash9,\thknight_smash10\t] {ai_charge(3); ai_melee();};\nvoid()\thknight_smash10\t=[\t$smash10,\thknight_smash11\t] {ai_charge(0);};\nvoid()\thknight_smash11\t=[\t$smash11,\thknight_run1\t] {ai_charge(0);};\n\n//============================================================================\n\nvoid()\thknight_watk1\t=[\t$w_attack1,\thknight_watk2\t] {ai_charge(2);};\nvoid()\thknight_watk2\t=[\t$w_attack2,\thknight_watk3\t] {ai_charge(0);};\nvoid()\thknight_watk3\t=[\t$w_attack3,\thknight_watk4\t] {ai_charge(0);};\nvoid()\thknight_watk4\t=[\t$w_attack4,\thknight_watk5\t] {ai_melee();};\nvoid()\thknight_watk5\t=[\t$w_attack5,\thknight_watk6\t] {ai_melee();};\nvoid()\thknight_watk6\t=[\t$w_attack6,\thknight_watk7\t] {ai_melee();};\nvoid()\thknight_watk7\t=[\t$w_attack7,\thknight_watk8\t] {ai_charge(1);};\nvoid()\thknight_watk8\t=[\t$w_attack8,\thknight_watk9\t] {ai_charge(4);};\nvoid()\thknight_watk9\t=[\t$w_attack9,\thknight_watk10\t] {ai_charge(5);};\nvoid()\thknight_watk10\t=[\t$w_attack10,\thknight_watk11\t] {ai_charge(3); ai_melee();};\nvoid()\thknight_watk11\t=[\t$w_attack11,\thknight_watk12\t] {ai_charge(2); ai_melee();};\nvoid()\thknight_watk12\t=[\t$w_attack12,\thknight_watk13\t] {ai_charge(2); ai_melee();};\nvoid()\thknight_watk13\t=[\t$w_attack13,\thknight_watk14\t] {ai_charge(0);};\nvoid()\thknight_watk14\t=[\t$w_attack14,\thknight_watk15\t] {ai_charge(0);};\nvoid()\thknight_watk15\t=[\t$w_attack15,\thknight_watk16\t] {ai_charge(0);};\nvoid()\thknight_watk16\t=[\t$w_attack16,\thknight_watk17\t] {ai_charge(1);};\nvoid()\thknight_watk17\t=[\t$w_attack17,\thknight_watk18\t] {ai_charge(1); ai_melee();};\nvoid()\thknight_watk18\t=[\t$w_attack18,\thknight_watk19\t] {ai_charge(3); ai_melee();};\nvoid()\thknight_watk19\t=[\t$w_attack19,\thknight_watk20\t] {ai_charge(4); ai_melee();};\nvoid()\thknight_watk20\t=[\t$w_attack20,\thknight_watk21\t] {ai_charge(6);};\nvoid()\thknight_watk21\t=[\t$w_attack21,\thknight_watk22\t] {ai_charge(7);};\nvoid()\thknight_watk22\t=[\t$w_attack22,\thknight_run1\t] {ai_charge(3);};\n\n//============================================================================\n\nvoid() hk_idle_sound =\n{\n\tif (random() < 0.2)\n\t\tsound (self, CHAN_VOICE, \"hknight/idle.wav\", 1, ATTN_NORM);\n};\n\nvoid(entity attacker, float damage)\thknight_pain =\n{\n\tif (self.pain_finished > time)\n\t\treturn;\n\n\tsound (self, CHAN_VOICE, \"hknight/pain1.wav\", 1, ATTN_NORM);\n\n\tif (time - self.pain_finished > 5)\n\t{\t// allways go into pain frame if it has been a while\n\t\thknight_pain1 ();\n\t\tself.pain_finished = time + 1;\n\t\treturn;\n\t}\n\t\n\tif ((random()*30 > damage) )\n\t\treturn;\t\t// didn't flinch\n\n\tself.pain_finished = time + 1;\n\thknight_pain1 ();\n};\n\nfloat\thknight_type;\n\nvoid() hknight_melee =\n{\n\thknight_type = hknight_type + 1;\n\n\tsound (self, CHAN_WEAPON, \"hknight/slash1.wav\", 1, ATTN_NORM);\n\tif (hknight_type == 1)\n\t\thknight_slice1 ();\n\telse if (hknight_type == 2)\n\t\thknight_smash1 ();\n\telse if (hknight_type == 3)\n\t{\n\t\thknight_watk1 ();\n\t\thknight_type = 0;\n\t}\n};\n\nvoid() monster_hell_knight =\n{\n\n\tprecache_model2 (\"progs/hknight.mdl\");\n\tprecache_model2 (\"progs/k_spike.mdl\");\n\tprecache_model2 (\"progs/h_hellkn.mdl\");\n\n\t\n\tprecache_sound2 (\"hknight/attack1.wav\");\n\tprecache_sound2 (\"hknight/death1.wav\");\n\tprecache_sound2 (\"hknight/pain1.wav\");\n\tprecache_sound2 (\"hknight/sight1.wav\");\n\tprecache_sound (\"hknight/hit.wav\");\t\t// used by C code, so don't sound2\n\tprecache_sound2 (\"hknight/slash1.wav\");\n\tprecache_sound2 (\"hknight/idle.wav\");\n\tprecache_sound2 (\"hknight/grunt.wav\");\n\n\tprecache_sound (\"knight/sword1.wav\");\n\tprecache_sound (\"knight/sword2.wav\");\n\t\n\tself.solid = SOLID_SLIDEBOX;\n\tself.movetype = MOVETYPE_STEP;\n\n\tsetmodel (self, \"progs/hknight.mdl\");\n\n\tsetsize (self, '-16 -16 -24', '16 16 40');\n\tself.health = 125;\n\tself.islot3 = 10; //hell-knight armor\n\tself.armortype = 0.30;\n\tself.helmet = 1;\n\tself.team = 3;\n\tself.classname = \"monster\";\n\tself.netname = \"reaver\";\n\tself.th_stand = hknight_stand1;\n\tself.th_walk = hknight_walk1;\n\tself.th_run = hknight_run1;\n\tself.th_melee = hknight_melee;\n\tself.th_missile = hknight_magicc1;\n\tself.th_pain = hknight_pain;\n\tself.th_die = hknight_die;\n\tself.armornoise = \"weapons/ric1.wav\";\n\n\twalkmonster_start ();\n};\n"
  },
  {
    "path": "quakec/fallout2/hos.qc",
    "content": "void (float xx) hos_forward;\n\nvoid () hos_stand1 = [ 11, hos_stand2 ]\n{\n};\n\nvoid () hos_stand2 = [ 12, hos_stand3 ]\n{\n};\n\nvoid () hos_stand3 = [ 13, hos_stand4 ]\n{\n};\n\nvoid () hos_stand4 = [ 14, hos_stand5 ]\n{\n};\n\nvoid () hos_stand5 = [ 15, hos_stand1 ]\n{\n};\n\nvoid () hos_run1 = [ 0, hos_run2 ]\n{\n\thos_forward (MULTICAST_PVS_R);\n};\n\nvoid () hos_run2 = [ 1, hos_run3 ]\n{\n\thos_forward (AS_MELEE);\n};\n\nvoid () hos_run3 = [ 2, hos_run4 ]\n{\n\thos_forward (MULTICAST_PVS_R);\n};\n\nvoid () hos_run4 = [ 3, hos_run5 ]\n{\n\thos_forward (AS_MELEE);\n};\n\nvoid () hos_run5 = [ 4, hos_run6 ]\n{\n\thos_forward (MULTICAST_PVS_R);\n};\n\nvoid () hos_run6 = [ 5, hos_run1 ]\n{\n\thos_forward (AS_MELEE);\n};\n\nvoid () hos_walk1 = [ 16, hos_walk2 ]\n{\n\thos_forward (MULTICAST_PVS_R);\n};\n\nvoid () hos_walk2 = [ 17, hos_walk3 ]\n{\n\thos_forward (AS_MELEE);\n};\n\nvoid () hos_walk3 = [ 18, hos_walk4 ]\n{\n\thos_forward (MULTICAST_PVS_R);\n};\n\nvoid () hos_walk4 = [ 19, hos_walk5 ]\n{\n\thos_forward (AS_MELEE);\n};\n\nvoid () hos_walk5 = [ 20, hos_walk6 ]\n{\n\thos_forward (MULTICAST_PVS_R);\n};\n\nvoid () hos_walk6 = [ 21, hos_walk7 ]\n{\n\thos_forward (AS_MELEE);\n};\n\nvoid () hos_walk7 = [ 22, hos_walk8 ]\n{\n\thos_forward (MULTICAST_PVS_R);\n};\n\nvoid () hos_walk8 = [ 23, hos_walk9 ]\n{\n\thos_forward (AS_MELEE);\n};\n\nvoid () hos_walk9 = [ 24, hos_walk10 ]\n{\n\thos_forward (MULTICAST_PVS_R);\n};\n\nvoid () hos_walk10 = [ 25, hos_walk1 ]\n{\n\thos_forward (AS_MELEE);\n};\n\nvoid () hos_deatha1 = [ 6, hos_deatha2 ]\n{\n};\n\nvoid () hos_deatha2 = [ 7, hos_deatha3 ]\n{\n};\n\nvoid () hos_deatha3 = [ 8, hos_deatha4 ]\n{\n};\n\nvoid () hos_deatha4 = [ 9, hos_deatha5 ]\n{\n};\n\nvoid () hos_deatha5 = [ 10, hos_deatha5 ]\n{\n};\n\nvoid (float xx) hos_forward =\n{\n\tlocal float rng;\n\tlocal entity te;\n\tlocal string qqq;\n\tlocal float qq;\n\tlocal float dir1;\n\tlocal float dir2;\n\n\tself.movetype = MOVETYPE_STEP;\n\tif ((self.cnt == MULTICAST_ALL))\n\t{\n\t\treturn;\n\t}\n\tself.goalentity = self.friend;\n\tif ((vlen ((self.origin - self.goalentity.origin)) > 96))\n\t{\n\t\tmovetogoal ((SECRET_1ST_DOWN * xx));\n\t\tself.angles = vectoangles ((self.goalentity.origin - self.origin));\n\t\ttraceline (self.origin, ((self.origin + (v_right * SVC_BIGKICK)) + (v_forward * 90)), FALSE, self);\n\t\tdir1 = trace_fraction;\n\t\ttraceline (self.origin, ((self.origin + (v_right * 50)) + (v_forward * 90)), FALSE, self);\n\t\tdir1 = (dir1 + trace_fraction);\n\t\ttraceline (self.origin, ((self.origin + (v_right * 70)) + (v_forward * 90)), FALSE, self);\n\t\tdir1 = (dir1 + trace_fraction);\n\t\ttraceline (self.origin, ((self.origin + (v_right * 90)) + (v_forward * 90)), FALSE, self);\n\t\tdir1 = (dir1 + trace_fraction);\n\t\ttraceline (self.origin, ((self.origin + (v_right * 110)) + (v_forward * 90)), FALSE, self);\n\t\tdir1 = (dir1 + trace_fraction);\n\t\ttraceline (self.origin, ((self.origin - (v_right * SVC_BIGKICK)) + (v_forward * 90)), FALSE, self);\n\t\tdir2 = trace_fraction;\n\t\ttraceline (self.origin, ((self.origin - (v_right * 50)) + (v_forward * 90)), FALSE, self);\n\t\tdir2 = (dir2 + trace_fraction);\n\t\ttraceline (self.origin, ((self.origin - (v_right * 70)) + (v_forward * 90)), FALSE, self);\n\t\tdir2 = (dir2 + trace_fraction);\n\t\ttraceline (self.origin, ((self.origin - (v_right * 90)) + (v_forward * 90)), FALSE, self);\n\t\tdir2 = (dir2 + trace_fraction);\n\t\ttraceline (self.origin, ((self.origin - (v_right * 110)) + (v_forward * 90)), FALSE, self);\n\t\tdir2 = (dir2 + trace_fraction);\n\t\tif ((self.oldorg == self.origin))\n\t\t{\n\t\t\tif ((dir1 > dir2))\n\t\t\t{\n\t\t\t\tself.angles_y = (self.angles_y - ((AS_MELEE * random ()) * SVC_INTERMISSION));\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif ((dir2 > dir1))\n\t\t\t\t{\n\t\t\t\t\tself.angles_y = (self.angles_y + ((AS_MELEE * random ()) * SVC_INTERMISSION));\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tself.oldorg = (self.oldorg + '0 0 1');\n\t\t\t\t}\n\t\t\t}\n\t\t\twalkmove (self.angles_y, (SECRET_1ST_DOWN * xx));\n\t\t}\n\t}\n\tif ((vlen ((self.origin - self.goalentity.origin)) < IT_LIGHTNING))\n\t{\n\t\tself.angles = vectoangles ((self.goalentity.origin - self.origin));\n\t\twalkmove (self.angles_y, (-7 * xx));\n\t}\n\tif ((self.friend.team == PLAT_LOW_TRIGGER))\n\t{\n\t\tte = find (world, classname, \"buyzone1\");\n\t}\n\tif ((self.friend.team == SILENT))\n\t{\n\t\tte = find (world, classname, \"buyzone2\");\n\t}\n\twhile (te)\n\t{\n\t\trng = range (te);\n\t\tif (((rng <= RANGE_NEAR) && (self.rescued == MULTICAST_ALL)))\n\t\t{\n\t\t\tsound (self, CHAN_BODY, \"misc/rescued.wav\", PLAT_LOW_TRIGGER, ATTN_NONE);\n\t\t\tte = find (world, classname, \"hostage\");\n\t\t\twhile (te)\n\t\t\t{\n\t\t\t\tif (((te.health > MULTICAST_ALL) && (te.rescued == MULTICAST_ALL)))\n\t\t\t\t{\n\t\t\t\t\tqq = (qq + PLAT_LOW_TRIGGER);\n\t\t\t\t}\n\t\t\t\tte = find (te, classname, \"hostage\");\n\t\t\t}\n\t\t\tself.rescued = PLAT_LOW_TRIGGER;\n\t\t\tqqq = ftos ((qq - PLAT_LOW_TRIGGER));\n\t\t\tbprint (SILENT, qqq);\n\t\t\tbprint (SILENT, \" hostages left\\n\");\n\t\t\tsetorigin (self, (self.origin - '0 0 512'));\n\t\t}\n\t\tte = find (te, classname, \"buyzone\");\n\t}\n\tself.oldorg = self.origin;\n};\n"
  },
  {
    "path": "quakec/fallout2/inventory.qc",
    "content": "float IID_NONE = 0;\n\n#define IsMelee(iid) (iid == IID_NONE || (iid >= IID_WP_TOOLKIT && iid <= IID_WP_POWERAXE))\n#define IsGrenade(iid) (iid >= IID_GREN_FRAG && iid <= IID_GREN_FLASH)\n#define IsRanged(iid) (iid >= IID_WP_USP && iid <= IID_WP_ROCKETLAUNCHER)\n\n//weapons with ammo, things with a toggle state\n#define NotStackable(iid) (IsRanged(iid))\n\n//the ammoless weapons\nfloat IID_WP_TOOLKIT = 400;\nfloat IID_WP_KNIFE = 401;\nfloat IID_WP_AXE = 402;\nfloat IID_WP_VIBROBLADE = 403;\nfloat IID_WP_POWERAXE = 404;\n\n//the ammoed weapons\nfloat IID_WP_USP = 405;\nfloat IID_WP_DEAGLE = 406;\nfloat IID_WP_NEEDLER = 407;\nfloat IID_WP_ALIENBLASTER = 408;\nfloat IID_WP_PIPERIFLE = 409;\nfloat IID_WP_WINCHESTER = 410;\nfloat IID_WP_MOSSBERG = 411;\nfloat IID_WP_JACKHAMMER = 412;\nfloat IID_WP_MP9 = 413;\nfloat IID_WP_MP7 = 414;\nfloat IID_WP_RANGEMASTER = 415;\nfloat IID_WP_AK112 = 416;\nfloat IID_WP_AK74 = 417;\nfloat IID_WP_DKS1 = 418;\nfloat IID_WP_MOONLIGHT = 419;\nfloat IID_WP_SA80 = 420;\nfloat IID_WP_GAUSERIFLE = 421;      //2mm EC\nfloat IID_WP_PULSERIFLE = 422;\nfloat IID_WP_FNFAL = 423;\t\t//.308 AP\nfloat IID_WP_ROCKETLAUNCHER = 430;\n\n\n//and ammo for those guns\nfloat IID_AM_NEEDLER = 507;\nfloat IID_AM_10MM = 508; //10mm ammo\";\nfloat IID_AM_556MM = 509; //\"5.56mm ammo\";\nfloat IID_AM_5MMHIGHVEL = 510; //\"5mm high-velocity ammo\";\nfloat IID_AM_12GAUGESHELLS = 511; //\"12-guage shotgun shells\";\nfloat IID_AM_ENERGYCELL = 512; //\"small energy cell\";\nfloat IID_AM_2MMEC = 513; //12mmec ammo\";\nfloat IID_AM_762MM = 514; //\"7.62mm ammo\";\nfloat IID_AM_44MAGNUM = 515; //\".44 magnum ammo\";\nfloat IID_AM_45ACP = 516; //\".45 ACP ammo\";\nfloat IID_AM_ROCKET = 517; //\"explosive rocket\"\n\n//grenade items\nfloat IID_GREN_FRAG = 101;\nfloat IID_GREN_EMP = 102;\nfloat IID_GREN_SMOKE = 103;\nfloat IID_GREN_FLASH = 104;\n\n//armour\n#define IsArmor(iid) (iid >= IID_ARM_SHIRT && iid <= IID_ARM_LPOWER)\nfloat IID_ARM_SHIRT = 201;\nfloat IID_ARM_LEATHER = 202;\nfloat IID_ARM_KEVLAR = 203;\nfloat IID_ARM_METAL = 204;\nfloat IID_ARM_COMBAT = 205;\nfloat IID_ARM_BROTHERHOOD = 206;\nfloat IID_ARM_FORCE = 207;\nfloat IID_ARM_LPOWER = 208;\n\n//stims\n#define IsChem(iid) (iid >= IID_CHEM_STIMPACK && iid <= IID_CHEM_BESERK)\nfloat IID_CHEM_STIMPACK = 301;\nfloat IID_CHEM_MEDICALBAG = 302;\nfloat IID_CHEM_SUPERSTIM = 303;\nfloat IID_CHEM_ADRENALINE = 304;\nfloat IID_CHEM_PSYCHO = 305;\nfloat IID_CHEM_BESERK = 306;\n\nfloat IID_BUILD_MRAMMO = 350;\nfloat IID_BUILD_SHIELDGEN = 351;\nfloat IID_BUILD_AUTODOC = 352;\nfloat IID_BUILD_ROBOFANG = 353;\nfloat IID_BUILD_TTURRET = 354;\nfloat IID_BUILD_TELEPAD = 355;\nfloat IID_BUILD_RTURRET = 356;\nfloat IID_BUILD_GTURRET = 357;\n\nfloat IID_EQUIP_CLIMBINGGEAR = 375;\nfloat IID_EQUIP_STEALTHBOY = 376;\nfloat IID_EQUIP_SPRINTKIT = 377;\nfloat IID_EQUIP_HOVERBOOTS = 378;\n\n#define IsShootable(iid) (IsMelee(iid) || IsRanged(iid) || IsGrenade(iid))\n#define ToIID(it) floor(it/512)\n#define ToStatus(it) (it&511)\n#define SlotVal(iid,st) ((iid*512) | (st&511))\n\nfloat(float slotno, float iid) FitsInSlot;\n\n//slot1 and slot2 are the two hand slots\n//slot3 is the armour slot.\n//the other slots are for misilaneous things.\n\n#define MAXSLOTS 16\n\n#ifndef CSQC\n.float          islot1;\n.float          islot2;\n.float          islot3;\n.float          islot4;\n.float          islot5;\n.float          islot6;\n.float          islot7;\n.float          islot8;\n.float          islot9;\n.float          islot10;\n.float          islot11;\n.float          islot12;\n.float          islot13;\n.float          islot14;\n.float          islot15;\n.float          islot16;\n\nvoid(float stnum, float sttype, .float fieldname) clientstatf = #232;\nvoid() SetupStats =\n{\n\t//2 is ev_float\n\t//32 is the first stat we are allowed to use\n\tclientstatf(32, 2, islot1);\n\tclientstatf(33, 2, islot2);\n\tclientstatf(34, 2, islot3);\n\tclientstatf(35, 2, islot4);\n\tclientstatf(36, 2, islot5);\n\tclientstatf(37, 2, islot6);\n\tclientstatf(38, 2, islot7);\n\tclientstatf(39, 2, islot8);\n\tclientstatf(40, 2, islot9);\n\tclientstatf(41, 2, islot10);\n\tclientstatf(42, 2, islot11);\n\tclientstatf(43, 2, islot12);\n\tclientstatf(44, 2, islot13);\n\tclientstatf(45, 2, islot14);\n\tclientstatf(46, 2, islot15);\n\tclientstatf(47, 2, islot16);\n\n\tclientstatf(61, 2, current_slot);\n\tclientstatf(62, 2, team);\n\tclientstatf(63, 2, class);\n};\n\nfloat(entity e, float slotno) ItemInSlot =\n{\n\tif (slotno == 1)\n\t\treturn e.islot1;\n\tif (slotno == 2)\n\t\treturn e.islot2;\n\tif (slotno == 3)\n\t\treturn e.islot3;\n\tif (slotno == 4)\n\t\treturn e.islot4;\n\tif (slotno == 5)\n\t\treturn e.islot5;\n\tif (slotno == 6)\n\t\treturn e.islot6;\n\tif (slotno == 7)\n\t\treturn e.islot7;\n\tif (slotno == 8)\n\t\treturn e.islot8;\n\tif (slotno == 9)\n\t\treturn e.islot9;\n\tif (slotno == 10)\n\t\treturn e.islot10;\n\tif (slotno == 11)\n\t\treturn e.islot11;\n\tif (slotno == 12)\n\t\treturn e.islot12;\n\tif (slotno == 13)\n\t\treturn e.islot13;\n\tif (slotno == 14)\n\t\treturn e.islot14;\n\tif (slotno == 15)\n\t\treturn e.islot15;\n\tif (slotno == 16)\n\t\treturn e.islot16;\n\n\treturn 0;\n};\n\nvoid(entity e, float slotno, float item) SetItemSlot =\n{\n\tif (slotno == 1)\n\t\te.islot1 = item;\n\telse if (slotno == 2)\n\t\te.islot2 = item;\n\telse if (slotno == 3)\n\t\te.islot3 = item;\n\telse if (slotno == 4)\n\t\te.islot4 = item;\n\telse if (slotno == 5)\n\t\te.islot5 = item;\n\telse if (slotno == 6)\n\t\te.islot6 = item;\n\telse if (slotno == 7)\n\t\te.islot7 = item;\n\telse if (slotno == 8)\n\t\te.islot8 = item;\n\telse if (slotno == 9)\n\t\te.islot9 = item;\n\telse if (slotno == 10)\n\t\te.islot10 = item;\n\telse if (slotno == 11)\n\t\te.islot11 = item;\n\telse if (slotno == 12)\n\t\te.islot12 = item;\n\telse if (slotno == 13)\n\t\te.islot13 = item;\n\telse if (slotno == 14)\n\t\te.islot14 = item;\n\telse if (slotno == 15)\n\t\te.islot15 = item;\n\telse if (slotno == 16)\n\t\te.islot16 = item;\n};\n\nfloat(entity e, float iid) SlotOfItem =\n{\n\tif (ToIID(e.islot1) == iid)\n\t\treturn 1;\n\tif (ToIID(e.islot2) == iid)\n\t\treturn 2;\n\tif (ToIID(e.islot3) == iid)\n\t\treturn 3;\n\tif (ToIID(e.islot4) == iid)\n\t\treturn 4;\n\tif (ToIID(e.islot5) == iid)\n\t\treturn 5;\n\tif (ToIID(e.islot6) == iid)\n\t\treturn 6;\n\tif (ToIID(e.islot7) == iid)\n\t\treturn 7;\n\tif (ToIID(e.islot8) == iid)\n\t\treturn 8;\n\tif (ToIID(e.islot9) == iid)\n\t\treturn 9;\n\tif (ToIID(e.islot10) == iid)\n\t\treturn 10;\n\tif (ToIID(e.islot11) == iid)\n\t\treturn 11;\n\tif (ToIID(e.islot12) == iid)\n\t\treturn 12;\n\tif (ToIID(e.islot13) == iid)\n\t\treturn 13;\n\tif (ToIID(e.islot14) == iid)\n\t\treturn 14;\n\tif (ToIID(e.islot15) == iid)\n\t\treturn 15;\n\tif (ToIID(e.islot16) == iid)\n\t\treturn 16;\n\n\treturn 0;\n};\n\nfloat(entity e, float iid) TotalQuantity =\n{\t//iid must be stackable.\n\tlocal float ret;\n\tif (ToIID(e.islot1) == iid)\n\t\tret += ToStatus(e.islot1);\n\tif (ToIID(e.islot2) == iid)\n\t\tret += ToStatus(e.islot2);\n\tif (ToIID(e.islot3) == iid)\n\t\tret += ToStatus(e.islot3);\n\tif (ToIID(e.islot4) == iid)\n\t\tret += ToStatus(e.islot4);\n\tif (ToIID(e.islot5) == iid)\n\t\tret += ToStatus(e.islot5);\n\tif (ToIID(e.islot6) == iid)\n\t\tret += ToStatus(e.islot6);\n\tif (ToIID(e.islot7) == iid)\n\t\tret += ToStatus(e.islot7);\n\tif (ToIID(e.islot8) == iid)\n\t\tret += ToStatus(e.islot8);\n\tif (ToIID(e.islot9) == iid)\n\t\tret += ToStatus(e.islot9);\n\tif (ToIID(e.islot10) == iid)\n\t\tret += ToStatus(e.islot10);\n\tif (ToIID(e.islot11) == iid)\n\t\tret += ToStatus(e.islot11);\n\tif (ToIID(e.islot12) == iid)\n\t\tret += ToStatus(e.islot12);\n\tif (ToIID(e.islot13) == iid)\n\t\tret += ToStatus(e.islot13);\n\tif (ToIID(e.islot14) == iid)\n\t\tret += ToStatus(e.islot14);\n\tif (ToIID(e.islot15) == iid)\n\t\tret += ToStatus(e.islot15);\n\tif (ToIID(e.islot16) == iid)\n\t\tret += ToStatus(e.islot16);\n\n\treturn ret;\n};\n\nfloat(entity e) FindEmptySlot =\n{\n\tif (ToIID(e.islot3) == IID_NONE)\n\t\treturn 3;\n\tif (ToIID(e.islot4) == IID_NONE)\n\t\treturn 4;\n\tif (ToIID(e.islot5) == IID_NONE)\n\t\treturn 5;\n\tif (ToIID(e.islot6) == IID_NONE)\n\t\treturn 6;\n\tif (ToIID(e.islot7) == IID_NONE)\n\t\treturn 7;\n\tif (ToIID(e.islot8) == IID_NONE)\n\t\treturn 8;\n\tif (ToIID(e.islot9) == IID_NONE)\n\t\treturn 9;\n\tif (ToIID(e.islot10) == IID_NONE)\n\t\treturn 10;\n\tif (ToIID(e.islot11) == IID_NONE)\n\t\treturn 11;\n\tif (ToIID(e.islot12) == IID_NONE)\n\t\treturn 12;\n\tif (ToIID(e.islot13) == IID_NONE)\n\t\treturn 13;\n\tif (ToIID(e.islot14) == IID_NONE)\n\t\treturn 14;\n\tif (ToIID(e.islot15) == IID_NONE)\n\t\treturn 15;\n\tif (ToIID(e.islot16) == IID_NONE)\n\t\treturn 16;\n\n\treturn 0;\n};\n\nfloat(entity e, float iid) FindSuitableEmptySlot =\n{\n\tif (ToIID(e.islot1) == IID_NONE)\n\t\tif (FitsInSlot(1, iid))\n\t\t\treturn 1;\n\tif (ToIID(e.islot2) == IID_NONE)\n\t\tif (FitsInSlot(2, iid))\n\t\t\treturn 2;\n\tif (ToIID(e.islot3) == IID_NONE)\n\t\tif (FitsInSlot(3, iid))\n\t\t\treturn 3;\n\tif (ToIID(e.islot4) == IID_NONE)\n\t\tif (FitsInSlot(4, iid))\n\t\t\treturn 4;\n\tif (ToIID(e.islot5) == IID_NONE)\n\t\tif (FitsInSlot(5, iid))\n\t\t\treturn 5;\n\tif (ToIID(e.islot6) == IID_NONE)\n\t\tif (FitsInSlot(6, iid))\n\t\t\treturn 6;\n\tif (ToIID(e.islot7) == IID_NONE)\n\t\tif (FitsInSlot(7, iid))\n\t\t\treturn 7;\n\tif (ToIID(e.islot8) == IID_NONE)\n\t\tif (FitsInSlot(8, iid))\n\t\t\treturn 8;\n\tif (ToIID(e.islot9) == IID_NONE)\n\t\tif (FitsInSlot(9, iid))\n\t\t\treturn 9;\n\tif (ToIID(e.islot10) == IID_NONE)\n\t\tif (FitsInSlot(10, iid))\n\t\t\treturn 10;\n\tif (ToIID(e.islot11) == IID_NONE)\n\t\tif (FitsInSlot(11, iid))\n\t\t\treturn 11;\n\tif (ToIID(e.islot12) == IID_NONE)\n\t\tif (FitsInSlot(12, iid))\n\t\t\treturn 12;\n\tif (ToIID(e.islot13) == IID_NONE)\n\t\tif (FitsInSlot(13, iid))\n\t\t\treturn 13;\n\tif (ToIID(e.islot14) == IID_NONE)\n\t\tif (FitsInSlot(14, iid))\n\t\t\treturn 14;\n\tif (ToIID(e.islot15) == IID_NONE)\n\t\tif (FitsInSlot(15, iid))\n\t\t\treturn 15;\n\tif (ToIID(e.islot16) == IID_NONE)\n\t\tif (FitsInSlot(16, iid))\n\t\t\treturn 16;\n\n\treturn 0;\n};\n\ntypedef .float slot_t;\nslot_t(float slot) SlotField =\n{\n\tif (slot == 1)\n\t\treturn islot1;\n\tif (slot == 2)\n\t\treturn islot2;\n\tif (slot == 3)\n\t\treturn islot3;\n\tif (slot == 4)\n\t\treturn islot4;\n\tif (slot == 5)\n\t\treturn islot5;\n\tif (slot == 6)\n\t\treturn islot6;\n\tif (slot == 7)\n\t\treturn islot7;\n\tif (slot == 8)\n\t\treturn islot8;\n\tif (slot == 9)\n\t\treturn islot9;\n\tif (slot == 10)\n\t\treturn islot10;\n\tif (slot == 11)\n\t\treturn islot11;\n\tif (slot == 12)\n\t\treturn islot12;\n\tif (slot == 13)\n\t\treturn islot13;\n\tif (slot == 14)\n\t\treturn islot14;\n\tif (slot == 15)\n\t\treturn islot15;\n\tif (slot == 16)\n\t\treturn islot16;\n\n\n\tbprint(PRINT_MEDIUM, \"ERROR: Invalid slot number (\", ftos(slot), \")\\n\");\n\treturn islot1;\n};\n#endif\n\n\nstring(float iid) GetItemVModel =\n{\n\tif (iid == IID_NONE)\n\t\treturn \"progs/v_fist.mdl\";\n\tif (iid == IID_WP_TOOLKIT)\n\t\treturn \"progs/v_span.mdl\";\n\tif (iid == IID_WP_KNIFE)\n\t\treturn \"progs/v_knife.mdl\";\n\tif (iid == IID_WP_AXE)\n\t\treturn \"progs/v_axe.mdl\";\n\tif (iid == IID_WP_VIBROBLADE)\n\t\treturn \"progs/v_knife.mdl\";\n\tif (iid == IID_WP_POWERAXE)\n\t\treturn \"progs/v_axe.mdl\";\n\tif (iid == IID_WP_USP)\n\t\treturn \"progs/v_1911.mdl\";\n\n\tif (iid == IID_WP_DEAGLE)\n\t\treturn \"progs/v_deagle.mdl\";\n\tif (iid == IID_WP_NEEDLER)\n\t\treturn \"progs/v_1911.mdl\";\n\tif (iid == IID_WP_ALIENBLASTER)\n\t\treturn \"progs/v_alien.mdl\";\n\tif (iid == IID_WP_PIPERIFLE)\n\t\treturn \"progs/v_piperifle.mdl\";\n\tif (iid == IID_WP_WINCHESTER)\n\t\treturn \"progs/v_double.mdl\";\n\tif (iid == IID_WP_MOSSBERG)\n\t\treturn \"progs/v_shotgun.mdl\";\n\n\tif (iid == IID_WP_JACKHAMMER)\n\t\treturn \"progs/v_jackhammer.mdl\";\n\tif (iid == IID_WP_MP9)\n\t\treturn \"progs/v_mp9.mdl\";\n\tif (iid == IID_WP_MP7)\n\t\treturn \"progs/v_smg.mdl\";\n\tif (iid == IID_WP_RANGEMASTER)\n \t\treturn \"progs/v_rangem.mdl\";\n\tif (iid == IID_WP_AK112)\n\t\treturn \"progs/v_ak47.mdl\";\n\tif (iid == IID_WP_AK74)\n\t\treturn \"progs/v_ak47.mdl\";\n\tif (iid == IID_WP_DKS1)\n\t\treturn \"progs/v_srifle.mdl\";\n\tif (iid == IID_WP_MOONLIGHT)\n\t\treturn \"progs/v_night.mdl\";\n\tif (iid == IID_WP_FNFAL)\n \t\treturn \"progs/v_rangem.mdl\";\n\n\n\tif (iid == IID_WP_SA80)\n\t\treturn \"progs/v_sa80.mdl\";\n\tif (iid == IID_WP_GAUSERIFLE)\n\t\treturn \"progs/v_gauss.mdl\";\n\tif (iid == IID_WP_PULSERIFLE)\n\t\treturn \"progs/v_carbine.mdl\";\n\n\tif (iid == IID_WP_ROCKETLAUNCHER)\n\t\treturn \"progs/v_rocket.mdl\";\n\n\tif (iid == IID_GREN_FRAG)\n\t\treturn \"progs/v_handgren.mdl\";\n\tif (iid == IID_GREN_EMP)\n\t\treturn \"progs/v_handgren.mdl\";\n\tif (iid == IID_GREN_SMOKE)\n\t\treturn \"progs/v_handgren.mdl\";\n\tif (iid == IID_GREN_FLASH)\n\t\treturn \"progs/v_handgren.mdl\";\n\n\tbprint(PRINT_MEDIUM, ftos(iid), \" without a vmodel!\\n\");\n\treturn \"\";\n};\n\nstring(float iid) GetItemWModel =\n{\n\tif (iid == IID_WP_KNIFE)\n\t\treturn \"progs/w_knife.mdl\";\n\tif (iid == IID_WP_AXE)\n\t\treturn \"progs/w_knife.mdl\";\n\tif (iid == IID_WP_VIBROBLADE)\n\t\treturn \"progs/w_axe.mdl\";\n\tif (iid == IID_WP_POWERAXE)\n\t\treturn \"progs/w_axe.mdl\";\n\n\tif (iid == IID_WP_USP)\n\t\treturn \"progs/w_1911.mdl\";\n\tif (iid == IID_WP_DEAGLE)\n\t\treturn \"progs/w_deagle.mdl\";\n\tif (iid == IID_WP_NEEDLER)\n\t\treturn \"progs/w_1911.mdl\";\n\tif (iid == IID_WP_ALIENBLASTER)\n\t\treturn \"progs/w_alien.mdl\";\n\tif (iid == IID_WP_PIPERIFLE)\n\t\treturn \"progs/w_pipe.mdl\";\n\tif (iid == IID_WP_WINCHESTER)\n\t\treturn \"progs/w_shotgun.mdl\";\n\tif (iid == IID_WP_MOSSBERG)\n\t\treturn \"progs/w_pipe.mdl\";\n\tif (iid == IID_WP_JACKHAMMER)\n\t\treturn \"progs/w_jackhammer.mdl\";\n\n\tif (iid == IID_WP_MP9)\n\t\treturn \"progs/w_mp9.mdl\";\n\tif (iid == IID_WP_MP7)\n\t\treturn \"progs/w_mp7.mdl\";\n\tif (iid == IID_WP_RANGEMASTER)\n\t\treturn \"progs/w_rangem.mdl\";\n\tif (iid == IID_WP_AK112)\n\t\treturn \"progs/w_ak47.mdl\";\n\tif (iid == IID_WP_AK74)\n\t\treturn \"progs/w_ak47.mdl\";\n\tif (iid == IID_WP_DKS1)\n\t\treturn \"progs/w_srifle.mdl\";\n\tif (iid == IID_WP_MOONLIGHT)\n\t\treturn \"progs/w_night.mdl\";\n\tif (iid == IID_WP_SA80)\n\t\treturn \"progs/w_sa80.mdl\";\n\tif (iid == IID_WP_FNFAL)\n\t\treturn \"progs/w_rangem.mdl\";\n\n\tif (iid == IID_WP_GAUSERIFLE)\n\t\treturn \"progs/w_gauss.mdl\";\n\tif (iid == IID_WP_PULSERIFLE)\n\t\treturn \"progs/w_carbine.mdl\";\n\tif (iid == IID_WP_ROCKETLAUNCHER)\n\t\treturn \"progs/w_rocket.mdl\";\n\n\tif (iid == IID_GREN_FRAG)\n\t\treturn \"progs/grenade2.mdl\";\n\tif (iid == IID_GREN_EMP)\n\t\treturn \"progs/grenade2.mdl\";\n\tif (iid == IID_GREN_SMOKE)\n\t\treturn \"progs/grenade2.mdl\";\n\tif (iid == IID_GREN_FLASH)\n\t\treturn \"progs/grenade2.mdl\";\n\n\tif (iid == IID_CHEM_STIMPACK)\n\t\treturn \"maps/b_bh10.bsp\";\n\tif (iid == IID_CHEM_MEDICALBAG)\n\t\treturn \"maps/b_bh25.bsp\";\n\tif (iid == IID_CHEM_SUPERSTIM)\n\t\treturn \"maps/b_bh100.bsp\";\n\n//fixme\n\tif (iid == IID_CHEM_ADRENALINE)\n\t\treturn \"maps/b_bh10.bsp\";\n\tif (iid == IID_CHEM_PSYCHO)\n\t\treturn \"maps/b_bh25.bsp\";\n\tif (iid == IID_CHEM_BESERK)\n\t\treturn \"maps/b_bh100.bsp\";\n\n\n\treturn \"progs/s_light.spr\";\t//no model. :/\n};\n\nfloat(float iid) WeaponAmmoType =\n{\n\tif (iid == IID_WP_USP)\n\t\treturn IID_AM_10MM;\n\tif (iid == IID_WP_DEAGLE)\n\t\treturn IID_AM_44MAGNUM;\n\tif (iid == IID_WP_NEEDLER)\n\t\treturn IID_AM_NEEDLER;\n\tif (iid == IID_WP_ALIENBLASTER)\n\t\treturn IID_AM_ENERGYCELL;\n\tif (iid == IID_WP_PIPERIFLE)\n\t\treturn IID_AM_10MM;\n\tif (iid == IID_WP_WINCHESTER)\n\t\treturn IID_AM_12GAUGESHELLS;\n\tif (iid == IID_WP_MOSSBERG)\n\t\treturn IID_AM_12GAUGESHELLS;\n\tif (iid == IID_WP_JACKHAMMER)\n\t\treturn IID_AM_12GAUGESHELLS;\n\tif (iid == IID_WP_MP9)\n\t\treturn IID_AM_10MM;\n\tif (iid == IID_WP_MP7)\n\t\treturn IID_AM_10MM;\n\tif (iid == IID_WP_RANGEMASTER)\n\t\treturn IID_AM_556MM;\n\tif (iid == IID_WP_AK112)\n\t\treturn IID_AM_5MMHIGHVEL;\n\tif (iid == IID_WP_AK74)\n\t\treturn IID_AM_5MMHIGHVEL;\n\tif (iid == IID_WP_DKS1)\n\t\treturn IID_AM_762MM;\n\tif (iid == IID_WP_MOONLIGHT)\n\t\treturn IID_AM_556MM;\n\tif (iid == IID_WP_SA80)\n\t\treturn IID_AM_556MM;\n\tif (iid == IID_WP_FNFAL)\n\t\treturn IID_AM_762MM;\n\n\tif (iid == IID_WP_GAUSERIFLE)\n\t\treturn IID_AM_2MMEC;\n\tif (iid == IID_WP_PULSERIFLE)\n\t\treturn IID_AM_ENERGYCELL;\n\tif (iid == IID_WP_ROCKETLAUNCHER)\n\t\treturn IID_AM_ROCKET;\n\n\treturn IID_NONE;\n};\n\nfloat(float iid) WeaponMagQuant =\n{\n\tif (iid == IID_WP_USP)\n\t\treturn 12;\n\tif (iid == IID_WP_DEAGLE)\n\t\treturn 7;\n\tif (iid == IID_WP_NEEDLER)\n\t\treturn 15;\n\tif (iid == IID_WP_ALIENBLASTER)\n\t\treturn 6;\n\tif (iid == IID_WP_PIPERIFLE)\n\t\treturn 1;\n\tif (iid == IID_WP_WINCHESTER)\n\t\treturn 2;\n\tif (iid == IID_WP_MOSSBERG)\n\t\treturn 6;\n\tif (iid == IID_WP_JACKHAMMER)\n\t\treturn 10;\n\tif (iid == IID_WP_MP9)\n\t\treturn 30;\n\tif (iid == IID_WP_MP7)\n\t\treturn 30;\n\tif (iid == IID_WP_RANGEMASTER)\n\t\treturn 10;\n\tif (iid == IID_WP_AK112)\n\t\treturn 24;\n\tif (iid == IID_WP_AK74)\n\t\treturn 30;\n\tif (iid == IID_WP_DKS1)\n\t\treturn 8;\n\tif (iid == IID_WP_MOONLIGHT)\n\t\treturn 30;\n\tif (iid == IID_WP_SA80)\n\t\treturn 30;\n\tif (iid == IID_WP_GAUSERIFLE)\n\t\treturn 10;\n\tif (iid == IID_WP_PULSERIFLE)\n\t\treturn 40;\n\tif (iid == IID_WP_FNFAL)\n\t\treturn 20;\n\tif (iid == IID_WP_ROCKETLAUNCHER)\n\t\treturn 1;\n\n\treturn 0;\n};\n\nfloat(float iid) GetItemWeight =\n{\n\tif (iid == IID_NONE)\n\t\treturn 0;\n\tif (iid == IID_WP_KNIFE)\n\t\treturn 1;\n\tif (iid == IID_WP_AXE)\n\t\treturn 2;\n\tif (iid == IID_WP_VIBROBLADE)\n\t\treturn 8;\n\tif (iid == IID_WP_POWERAXE)\n\t\treturn 6;\n\tif (iid == IID_WP_USP)\n\t\treturn 1;\n\tif (iid == IID_WP_DEAGLE)\n\t\treturn 2;\n\tif (iid == IID_WP_NEEDLER)\n\t\treturn 2;\n\tif (iid == IID_WP_ALIENBLASTER)\n\t\treturn 2;\n\tif (iid == IID_WP_PIPERIFLE)\n\t\treturn 3;\n\tif (iid == IID_WP_WINCHESTER)\n\t\treturn 4;\n\tif (iid == IID_WP_MOSSBERG)\n\t\treturn 5;\n\tif (iid == IID_WP_JACKHAMMER)\n\t\treturn 6;\n\tif (iid == IID_WP_MP9)\n\t\treturn 3;\n\tif (iid == IID_WP_MP7)\n\t\treturn 3;\n\tif (iid == IID_WP_RANGEMASTER)\n\t\treturn 5;\n\tif (iid == IID_WP_AK112)\n\t\treturn 5;\n\tif (iid == IID_WP_AK74)\n\t\treturn 5;\n\tif (iid == IID_WP_DKS1)\n\t\treturn 7;\n\tif (iid == IID_WP_MOONLIGHT)\n\t\treturn 5;\n\tif (iid == IID_WP_SA80)\n\t\treturn 5;\n\tif (iid == IID_WP_GAUSERIFLE)\n\t\treturn 9;\n\tif (iid == IID_WP_PULSERIFLE)\n\t\treturn 12;\n\tif (iid == IID_WP_FNFAL)\n\t\treturn 9;\n\tif (iid == IID_WP_ROCKETLAUNCHER)\n\t\treturn 10;\n\n\n\tif (iid == IID_ARM_SHIRT)\n\t\treturn 3;\n\tif (iid == IID_ARM_LEATHER)\n\t\treturn 5;\n\tif (iid == IID_ARM_KEVLAR)\n\t\treturn 9;\n\tif (iid == IID_ARM_METAL)\n\t\treturn 15;\n\tif (iid == IID_ARM_COMBAT)\n\t\treturn 12;\n\tif (iid == IID_ARM_BROTHERHOOD)\n\t\treturn 17;\n\tif (iid == IID_ARM_FORCE)\n\t\treturn 6;\n\tif (iid == IID_ARM_LPOWER)\n\t\treturn 20;\n\n\tif (iid == IID_BUILD_MRAMMO)\n\t\treturn 2;\n\tif (iid == IID_BUILD_SHIELDGEN)\n\t\treturn 4;\n\tif (iid == IID_BUILD_AUTODOC)\n\t\treturn 3;\n\tif (iid == IID_BUILD_ROBOFANG)\n\t\treturn 2;\n\tif (iid == IID_BUILD_TTURRET)\n\t\treturn 3;\n\tif (iid == IID_BUILD_GTURRET)\n\t\treturn 3;\n\tif (iid == IID_BUILD_RTURRET)\n\t\treturn 3;\n\n\n//\tbprint(PRINT_MEDIUM, ftos(iid), \" without a weight!\\n\");\n\treturn 0;\n};\n\nfloat(float item) GetItemsWeight =\n{\n\tlocal float iid;\n\tiid = ToIID(item);\n\tif (NotStackable(iid))\n\t\treturn GetItemWeight(iid);\n\telse\n\t\treturn GetItemWeight(iid) * ToStatus(item);\n};\n\n\nstring(float iid) GetItemName =\n{\n\tif (iid == IID_NONE)\n\t\treturn \"nothing\";\n\n\n\tif (iid == IID_WP_TOOLKIT)\n\t\treturn \"toolkit\";\n\tif (iid == IID_WP_KNIFE)\n\t\treturn \"knife\";\n\tif (iid == IID_WP_AXE)\n\t\treturn \"axe\";\n\tif (iid == IID_WP_VIBROBLADE)\n\t\treturn \"ripper\";\n\tif (iid == IID_WP_POWERAXE)\n\t\treturn \"poweraxe\";\n\n\tif (iid == IID_WP_USP)\n\t\treturn \"1911 (10mm)\";\n\tif (iid == IID_WP_DEAGLE)\n\t\treturn \"desert eagle (.44mag)\";\n\tif (iid == IID_WP_NEEDLER)\n\t\treturn \"needler\";\n\tif (iid == IID_WP_ALIENBLASTER)\n\t\treturn \"alien blaster (energy)\";\n\tif (iid == IID_WP_PIPERIFLE)\n\t\treturn \"pipe rifle (10mm)\";\n\tif (iid == IID_WP_WINCHESTER)\n\t\treturn \"winchester (12g)\";\n\tif (iid == IID_WP_MOSSBERG)\n\t\treturn \"mossberg (12g)\";\n\tif (iid == IID_WP_JACKHAMMER)\n\t\treturn \"jackhammer (12g)\";\n\tif (iid == IID_WP_MP9)\n\t\treturn \"mp9 (10mm)\";\n\tif (iid == IID_WP_MP7)\n\t\treturn \"grease gun (10mm)\";\n\tif (iid == IID_WP_RANGEMASTER)\n\t\treturn \"rangemaster (5.56mm)\";\n\tif (iid == IID_WP_AK112)\n\t\treturn \"ak-112 (5mm-hv)\";\n\tif (iid == IID_WP_AK74)\n\t\treturn \"ak-74 (5mm-hv)\";\n\tif (iid == IID_WP_DKS1)\n\t\treturn \"dks-1 (7.62mm)\";\n\tif (iid == IID_WP_MOONLIGHT)\n\t\treturn \"moonlight (5.56mm)\";\n\tif (iid == IID_WP_SA80)\n\t\treturn \"sa-80 (5.56mm)\";\n\tif (iid == IID_WP_GAUSERIFLE)\n\t\treturn \"gauss rifle (2mm EC)\";\n\tif (iid == IID_WP_PULSERIFLE)\n\t\treturn \"laser carbine (energy)\";\n\tif (iid == IID_WP_FNFAL)\n\t\treturn \"fn-fal (7.62mm)\";\n\tif (iid == IID_WP_ROCKETLAUNCHER)\n\t\treturn \"rocket launcher\";\n\n\n\n\tif (iid == IID_AM_NEEDLER)\n\t\treturn \"needler cartidge\";\n\tif (iid == IID_AM_2MMEC)\n\t\treturn \"2mm EC ammo\";\n\tif (iid == IID_AM_10MM)\n\t\treturn \"10mm FMJ\";\n\tif (iid == IID_AM_556MM)\n\t\treturn \"5.56mm FMJ\";\n\tif (iid == IID_AM_5MMHIGHVEL)\n\t\treturn \"5mm JHP\";\n\tif (iid == IID_AM_12GAUGESHELLS)\n\t\treturn \"12-guage shotgun shells\";\n\tif (iid == IID_AM_ENERGYCELL)\n\t\treturn \"small energy cell\";\n\tif (iid == IID_AM_762MM)\n\t\treturn \"7.62mm AP\";\n\tif (iid == IID_AM_44MAGNUM)\n\t\treturn \".44 magnum ammo\";\n\tif (iid == IID_AM_45ACP)\n\t\treturn \".45 ACP ammo\";\n\tif (iid == IID_AM_ROCKET)\n\t\treturn \"explosive rocket\";\n\n\n\tif (iid == IID_GREN_FRAG)\n\t\treturn \"frag grenade\";\n\tif (iid == IID_GREN_EMP)\n\t\treturn \"emp grenade\";\n\tif (iid == IID_GREN_SMOKE)\n\t\treturn \"smoke grenade\";\n\tif (iid == IID_GREN_FLASH)\n\t\treturn \"flashbang\";\n\n\n\tif (iid == IID_ARM_SHIRT)\n\t\treturn \"bulletproof shirt\";\n\tif (iid == IID_ARM_LEATHER)\n\t\treturn \"leather armor\";\n\tif (iid == IID_ARM_KEVLAR)\n\t\treturn \"kevlar armor\";\n\tif (iid == IID_ARM_METAL)\n\t\treturn \"metal armor\";\n\tif (iid == IID_ARM_COMBAT)\n\t\treturn \"combat armor\";\n\tif (iid == IID_ARM_BROTHERHOOD)\n\t\treturn \"brotherhood armor\";\n\tif (iid == IID_ARM_FORCE)\n\t\treturn \"force armor\";\n\tif (iid == IID_ARM_LPOWER)\n\t\treturn \"light power armor\";\n\n\n\tif (iid == IID_CHEM_STIMPACK)\n\t\treturn \"stimpack\";\n\tif (iid == IID_CHEM_MEDICALBAG)\n\t\treturn \"medical bag\";\n\tif (iid == IID_CHEM_SUPERSTIM)\n\t\treturn \"superstim\";\n\tif (iid == IID_CHEM_ADRENALINE)\n\t\treturn \"adrenaline\";\n\tif (iid == IID_CHEM_PSYCHO)\n\t\treturn \"psycho\";\n\tif (iid == IID_CHEM_BESERK)\n\t\treturn \"beserk\";\n\n\n\tif (iid == IID_BUILD_MRAMMO)\n\t\treturn \"mr. ammo\";\n\tif (iid == IID_BUILD_SHIELDGEN)\n\t\treturn \"shield-gen\";\n\tif (iid == IID_BUILD_AUTODOC)\n\t\treturn \"auto-doc\";\n\tif (iid == IID_BUILD_ROBOFANG)\n\t\treturn \"robofang\";\n\tif (iid == IID_BUILD_TTURRET)\n\t\treturn \"tesla turret\";\n\tif (iid == IID_BUILD_RTURRET)\n\t\treturn \"rocket turret\";\n\tif (iid == IID_BUILD_GTURRET)\n\t\treturn \"machine-gun turret\";\n\n\tif (iid == IID_EQUIP_CLIMBINGGEAR)\n\t\treturn \"climbing gear\";\n\tif (iid == IID_EQUIP_STEALTHBOY)\n\t\treturn \"stealthboy\";\n\tif (iid == IID_EQUIP_SPRINTKIT)\n\t\treturn \"lucozade sport!\";\n\tif (iid == IID_EQUIP_HOVERBOOTS)\n\t\treturn \"hover boots\";\n\n\tbprint(PRINT_MEDIUM, ftos(iid), \" without a name!\\n\");\n\treturn strcat(\"unknown\", ftos(iid));\n};\n\nstring(float iid) GetItemDesc =\n{\n\tif (iid == IID_NONE)\n\t\treturn \"\";\n\n\n\tif (iid == IID_WP_TOOLKIT)\n\t\treturn \"a wattz(tm) brand toolkit consisting of many handy instruments, including a wrench. this kit is a repairman's dream, and allows those with the proper knowledge to perform technical tasks.\";\n\tif (iid == IID_WP_AK74)\n\t\treturn \"the ak74 is the predecessor of the legendary ak-47. it is still the same old killing machine, except that it uses the 5.45mm high-velocity round that was common in the latest assault rifles just before the war.\";\n\tif (iid == IID_BUILD_MRAMMO)\n\t\treturn \"a military prototype, the mr. ammo was designed to allow soldiers an extra source of ammunition in remote areas, as the handy little machine is capable of churning out ammo from things such as rocks, debris, and metal scraps.\";\n\tif (iid == IID_EQUIP_CLIMBINGGEAR)\n\t\treturn \"FIXME:\\nActivate, and then run at and up a wall. Don't look too far away from the wall!\";\n\n\tbprint(PRINT_MEDIUM, ftos(iid), \" without a desc!\\n\");\n\treturn strcat(\"<no description>\", ftos(iid));\n};\n\nstring(float iid) GetItemImage =\n{\n\tif (iid == IID_NONE)\n\t\treturn \"blank.jpg\";\n\n\n\tif (iid == IID_WP_TOOLKIT)\n\t\treturn \"toolkit.jpg\";\n\tif (iid == IID_WP_KNIFE)\n\t\treturn \"knife.jpg\";\n\tif (iid == IID_WP_AXE)\n\t\treturn \"axe.jpg\";\n\tif (iid == IID_WP_VIBROBLADE)\n\t\treturn \"ripper.jpg\";\n\tif (iid == IID_WP_POWERAXE)\n\t\treturn \"poweraxe.jpg\";\n\n\tif (iid == IID_WP_USP)\n\t\treturn \"usp.jpg\";\n\tif (iid == IID_WP_DEAGLE)\n\t\treturn \"deagle.jpg\";\n\tif (iid == IID_WP_NEEDLER)\n\t\treturn \"needler.jpg\";\n\tif (iid == IID_WP_ALIENBLASTER)\n\t\treturn \"blaster.jpg\";\n\tif (iid == IID_WP_PIPERIFLE)\n\t\treturn \"prifle.jpg\";\n\tif (iid == IID_WP_WINCHESTER)\n\t\treturn \"double.jpg\";\n\tif (iid == IID_WP_MOSSBERG)\n\t\treturn \"mossberg.jpg\";\n\tif (iid == IID_WP_JACKHAMMER)\n\t\treturn \"jackhammer.jpg\";\n\tif (iid == IID_WP_MP9)\n\t\treturn \"mp9.jpg\";\n\tif (iid == IID_WP_MP7)\n\t\treturn \"ggun.jpg\";\n\tif (iid == IID_WP_RANGEMASTER)\n\t\treturn \"rangem.jpg\";\n\tif (iid == IID_WP_AK112)\n\t\treturn \"ak112.jpg\";\n\tif (iid == IID_WP_AK74)\n\t\treturn \"ak74.jpg\";\n\tif (iid == IID_WP_DKS1)\n\t\treturn \"dks1.jpg\";\n\tif (iid == IID_WP_MOONLIGHT)\n\t\treturn \"moonlight.jpg\";\n\tif (iid == IID_WP_SA80)\n\t\treturn \"sa80.jpg\";\n\tif (iid == IID_WP_GAUSERIFLE)\n\t\treturn \"grifle.jpg\";\n\tif (iid == IID_WP_PULSERIFLE)\n\t\treturn \"lcarbine.jpg\";\n\tif (iid == IID_WP_FNFAL)\n\t\treturn \"fnfal.jpg\";\n\tif (iid == IID_WP_ROCKETLAUNCHER)\n\t\treturn \"rpg.jpg\";\n\n\n\tif (iid == IID_AM_NEEDLER)\n\t\treturn \"needles.jpg\";\n\tif (iid == IID_AM_2MMEC)\n\t\treturn \"2mm.jpg\";\n\tif (iid == IID_AM_10MM)\n\t\treturn \"10mm.jpg\";\n\tif (iid == IID_AM_556MM)\n\t\treturn \"556mm.jpg\";\n\tif (iid == IID_AM_5MMHIGHVEL)\n\t\treturn \"5mmhv.jpg\";\n\tif (iid == IID_AM_12GAUGESHELLS)\n\t\treturn \"12gauge.jpg\";\n\tif (iid == IID_AM_ENERGYCELL)\n\t\treturn \"cell.jpg\";\n\tif (iid == IID_AM_762MM)\n\t\treturn \"762mm.jpg\";\n\tif (iid == IID_AM_44MAGNUM)\n\t\treturn \"44mag.jpg\";\n\tif (iid == IID_AM_45ACP)\n\t\treturn \"45acp.jpg\";\n\tif (iid == IID_AM_ROCKET)\n\t\treturn \"rocket.jpg\";\n\n\n\tif (iid == IID_GREN_FRAG)\n\t\treturn \"frag.jpg\";\n\tif (iid == IID_GREN_EMP)\n\t\treturn \"emp.jpg\";\n\tif (iid == IID_GREN_SMOKE)\n\t\treturn \"smoke.jpg\";\n\tif (iid == IID_GREN_FLASH)\n\t\treturn \"flash.jpg\";\n\n\n\tif (iid == IID_ARM_SHIRT)\n\t\treturn \"shirt.jpg\";\n\tif (iid == IID_ARM_LEATHER)\n\t\treturn \"leather.jpg\";\n\tif (iid == IID_ARM_KEVLAR)\n\t\treturn \"kevlar.jpg\";\n\tif (iid == IID_ARM_METAL)\n\t\treturn \"metal.jpg\";\n\tif (iid == IID_ARM_COMBAT)\n\t\treturn \"combat.jpg\";\n\tif (iid == IID_ARM_BROTHERHOOD)\n\t\treturn \"brotherhood.jpg\";\n\tif (iid == IID_ARM_FORCE)\n\t\treturn \"force.jpg\";\n\tif (iid == IID_ARM_LPOWER)\n\t\treturn \"power.jpg\";\n\n\n\tif (iid == IID_CHEM_STIMPACK)\n\t\treturn \"stimpack.jpg\";\n\tif (iid == IID_CHEM_MEDICALBAG)\n\t\treturn \"medbag.jpg\";\n\tif (iid == IID_CHEM_SUPERSTIM)\n\t\treturn \"superstim.jpg\";\n\tif (iid == IID_CHEM_ADRENALINE)\n\t\treturn \"adrenaline.jpg\";\n\tif (iid == IID_CHEM_PSYCHO)\n\t\treturn \"psycho.jpg\";\n\tif (iid == IID_CHEM_BESERK)\n\t\treturn \"beserk.jpg\";\n\n\n\tif (iid == IID_BUILD_MRAMMO)\n\t\treturn \"mrammo.jpg\";\n\tif (iid == IID_BUILD_SHIELDGEN)\n\t\treturn \"shieldgen.jpg\";\n\tif (iid == IID_BUILD_AUTODOC)\n\t\treturn \"autodoc.jpg\";\n\tif (iid == IID_BUILD_ROBOFANG)\n\t\treturn \"robofang.jpg\";\n\tif (iid == IID_BUILD_TTURRET)\n\t\treturn \"tesla.jpg\";\n\tif (iid == IID_BUILD_RTURRET)\n\t\treturn \"rturret.jpg\";\n\tif (iid == IID_BUILD_GTURRET)\n\t\treturn \"gturret.jpg\";\n\n\tif (iid == IID_EQUIP_CLIMBINGGEAR)\n\t\treturn \"robofang.jpg\";\n\tif (iid == IID_EQUIP_STEALTHBOY)\n\t\treturn \"stealthboy.jpg\";\n\tif (iid == IID_EQUIP_SPRINTKIT)\n\t\treturn \"sprintkit.jpg\";\n\n\tbprint(PRINT_MEDIUM, ftos(iid), \" without a name!\\n\");\n\treturn strcat(\"unknown.jpg\", ftos(iid));\n};\n\n\nfloat(string itname) ItemIDOfName = \n{\n\tif (itname == \"nothing\")\n\t\treturn IID_NONE;\n\tif (itname == \"toolkit\")\n\t\treturn IID_WP_TOOLKIT;\n\tif (itname == \"knife\")\n\t\treturn IID_WP_KNIFE;\n\tif (itname == \"axe\")\n\t\treturn IID_WP_AXE;\n\tif (itname == \"ripper\")\n\t\treturn IID_WP_VIBROBLADE;\n\tif (itname == \"poweraxe\")\n\t\treturn IID_WP_POWERAXE;\n\tif (itname == \"1911\")\n\t\treturn IID_WP_USP;\n\tif (itname == \"desert eagle\")\n\t\treturn IID_WP_DEAGLE;\n\tif (itname == \"deagle\")\n\t\treturn IID_WP_DEAGLE;\n\tif (itname == \"needler\")\n\t\treturn IID_WP_NEEDLER;\n\tif (itname == \"alien blaster\")\n\t\treturn IID_WP_ALIENBLASTER;\n\tif (itname == \"blaster\")\n\t\treturn IID_WP_ALIENBLASTER;\n\tif (itname == \"pipe rifle\")\n\t\treturn IID_WP_PIPERIFLE;\n\tif (itname == \"winchester\")\n\t\treturn IID_WP_WINCHESTER;\n\tif (itname == \"mossberg\")\n\t\treturn IID_WP_MOSSBERG;\n\tif (itname == \"jackhammer\")\n\t\treturn IID_WP_JACKHAMMER;\n\tif (itname == \"mp9\")\n\t\treturn IID_WP_MP9;\n\tif (itname == \"grease gun\")\n\t\treturn IID_WP_MP7;\n\tif (itname == \"rangemaster\")\n\t\treturn IID_WP_RANGEMASTER;\n\tif (itname == \"fnfal\")\n\t\treturn IID_WP_FNFAL;\n\tif (itname == \"ak-112\")\n\t\treturn IID_WP_AK112;\n\tif (itname == \"ak-74\")\n\t\treturn IID_WP_AK74;\n\tif (itname == \"dks-1\")\n\t\treturn IID_WP_DKS1;\n\tif (itname == \"moonlight\")\n\t\treturn IID_WP_MOONLIGHT;\n\tif (itname == \"sa-80\")\n\t\treturn IID_WP_SA80;\n\tif (itname == \"gauss rifle\")\n\t\treturn IID_WP_GAUSERIFLE;\n\tif (itname == \"laser carbine\")\n\t\treturn IID_WP_PULSERIFLE;\n\tif (itname == \"rocket\")\n\t\treturn IID_WP_ROCKETLAUNCHER;\n\tif (itname == \"frag grenade\")\n\t\treturn IID_GREN_FRAG;\n\tif (itname == \"emp grenade\")\n\t\treturn IID_GREN_EMP;\n\tif (itname == \"smoke grenade\")\n\t\treturn IID_GREN_SMOKE;\n\tif (itname == \"flashbang\")\n\t\treturn IID_GREN_FLASH;\n\tif (itname == \"bulletproof shirt\")\n\t\treturn IID_ARM_SHIRT;\n\tif (itname == \"leather armor\")\n\t\treturn IID_ARM_LEATHER;\n\tif (itname == \"kevlar armor\")\n\t\treturn IID_ARM_KEVLAR;\n\tif (itname == \"metal armor\")\n\t\treturn IID_ARM_METAL;\n\tif (itname == \"combat armor\")\n\t\treturn IID_ARM_COMBAT;\n\tif (itname == \"brotherhood armor\")\n\t\treturn IID_ARM_BROTHERHOOD;\n\tif (itname == \"force armor\")\n\t\treturn IID_ARM_FORCE;\n\tif (itname == \"light power armor\")\n\t\treturn IID_ARM_LPOWER;\n\tif (itname == \"stimpacks\")\n\t\treturn IID_CHEM_STIMPACK;\n\tif (itname == \"stimpack\")\n\t\treturn IID_CHEM_STIMPACK;\n\tif (itname == \"medical bag\")\n\t\treturn IID_CHEM_MEDICALBAG;\n\tif (itname == \"bandages\")\n\t\treturn IID_CHEM_MEDICALBAG;\n\tif (itname == \"superstims\")\n\t\treturn IID_CHEM_SUPERSTIM;\n\tif (itname == \"adrenaline\")\n\t\treturn IID_CHEM_ADRENALINE;\n\tif (itname == \"psycho\")\n\t\treturn IID_CHEM_PSYCHO;\n\tif (itname == \"beserk\")\n\t\treturn IID_CHEM_BESERK;\n\tif (itname == \"mr.ammo\")\n\t\treturn IID_BUILD_MRAMMO;\n\tif (itname == \"mrammo\")\n\t\treturn IID_BUILD_MRAMMO;\n\tif (itname == \"shield-gen\")\n\t\treturn IID_BUILD_SHIELDGEN;\n\tif (itname == \"shieldgen\")\n\t\treturn IID_BUILD_SHIELDGEN;\n\tif (itname == \"auto-doc\")\n\t\treturn IID_BUILD_AUTODOC;\n\tif (itname == \"autodoc\")\n\t\treturn IID_BUILD_AUTODOC;\n\tif (itname == \"robofang\")\n\t\treturn IID_BUILD_ROBOFANG;\n\tif (itname == \"doggie\")\n\t\treturn IID_BUILD_ROBOFANG;\n\tif (itname == \"climbinggear\")\n\t\treturn IID_EQUIP_CLIMBINGGEAR;\n\tif (itname == \"gear\")\n\t\treturn IID_EQUIP_CLIMBINGGEAR;\n\tif (itname == \"stealthboy\")\n\t\treturn IID_EQUIP_STEALTHBOY;\n\n\tif (itname == \"sprintkit\")\n\t\treturn IID_EQUIP_SPRINTKIT;\n\n\tif (itname == \"tesla\")\n\t\treturn IID_BUILD_TTURRET;\n\n\treturn 0;\n};\n\n\n\nfloat(float slotno, float iid) FitsInSlot =\n{\n\tif (!iid)\t//nothing can be put in every slot.\n\t\treturn true;\n\tif (slotno == 1 || slotno == 2)\n\t\treturn IsShootable(iid);\n\tif (slotno == 3)\n\t\treturn IsArmor(iid);\n\treturn true;\n};"
  },
  {
    "path": "quakec/fallout2/items.qc",
    "content": "void() W_SetCurrentAmmo;\n/* ALL LIGHTS SHOULD BE 0 1 0 IN COLOR ALL OTHER ITEMS SHOULD\nBE .8 .3 .4 IN COLOR */\n\nvoid(entity to, float iid, float count) AddStackable;\n\nvoid() SUB_regen =\n{\n\tself.model = self.mdl;          // restore original model\n\tself.solid = SOLID_TRIGGER;     // allow it to be touched again\n\tsound (self, CHAN_VOICE, \"items/itembk2.wav\", 1, ATTN_NORM);    // play respawn sound\n\tsetorigin (self, self.origin);\n};\n\n\n\n/*QUAKED noclass (0 0 0) (-8 -8 -8) (8 8 8)\nprints a warning message when spawned\n*/\nvoid() noclass =\n{\n\tdprint (\"noclass spawned at\");\n\tdprint (vtos(self.origin));\n\tdprint (\"\\n\");\n\tremove (self);\n};\n\nvoid() q_touch;\n\nvoid() q_touch =\n{\nlocal string    s;\n\n\tif (other.classname != \"player\")\n\t\treturn;\n\tif (other.health <= 0)\n\t\treturn;\n\n\tself.mdl = self.model;\n\n\tsound (other, CHAN_VOICE, self.noise, 1, ATTN_NORM);\n\tstuffcmd (other, \"bf\\n\");\n\tself.solid = SOLID_NOT;\n\tother.items = other.items | IT_QUAD;\n\tself.model = string_null;\n\t\tif (deathmatch == 4)\n\t\t{\n\t\t\tother.armortype = 0;\n\t\t\tother.armorvalue = 0 * 0.01;\n\t\t\tother.ammo_cells = 0;\n\t\t}\n\n// do the apropriate action\n\tother.super_time = 1;\n\tother.super_damage_finished = self.cnt;\n\n\ts=ftos(rint(other.super_damage_finished - time));\n\n\tbprint (PRINT_LOW, other.netname);\n\tif (deathmatch == 4)\n\t\tbprint (PRINT_LOW, \" recovered an OctaPower with \");\n\telse \n\t\tbprint (PRINT_LOW, \" recovered a Quad with \");\n\tbprint (PRINT_LOW, s);\n\tbprint (PRINT_LOW, \" seconds remaining!\\n\");\n\n\tactivator = other;\n\tSUB_UseTargets();                               // fire all targets / killtargets\n};\n\n\nvoid(float timeleft) DropQuad =\n{\n\tlocal entity    item;\n\n\titem = spawn();\n\titem.origin = self.origin;\n\t\n\titem.velocity_z = 300;\n\titem.velocity_x = -100 + (random() * 200);\n\titem.velocity_y = -100 + (random() * 200);\n\t\n\titem.flags = FL_ITEM;\n\titem.solid = SOLID_TRIGGER;\n\titem.movetype = MOVETYPE_TOSS;\n\titem.noise = \"items/damage.wav\";\n\tsetmodel (item, \"progs/quaddama.mdl\");\n\tsetsize (item, '-16 -16 -24', '16 16 32');\n\titem.cnt = time + timeleft;\n\titem.touch = q_touch;\n\titem.nextthink = time + timeleft;    // remove it with the time left on it\n\titem.think = SUB_Remove;\n};\n\n\nvoid() r_touch;\n\nvoid() r_touch =\n{\nlocal string    s;\n\n\tif (other.classname != \"player\")\n\t\treturn;\n\tif (other.health <= 0)\n\t\treturn;\n\n\tself.mdl = self.model;\n\n\tsound (other, CHAN_VOICE, self.noise, 1, ATTN_NORM);\n\tstuffcmd (other, \"bf\\n\");\n\tself.solid = SOLID_NOT;\n\tother.items = other.items | IT_INVISIBILITY;\n\tself.model = string_null;\n\n// do the apropriate action\n\tother.invisible_time = 1;\n\tother.invisible_finished = self.cnt;\n\ts=ftos(rint(other.invisible_finished - time));\n\tbprint (PRINT_LOW, other.netname);\n\tbprint (PRINT_LOW, \" recovered a Ring with \");\n\tbprint (PRINT_LOW, s);\n\tbprint (PRINT_LOW, \" seconds remaining!\\n\");\n      \n\n\tactivator = other;\n\tSUB_UseTargets();                               // fire all targets / killtargets\n};\n\n\nvoid(float timeleft) DropRing =\n{\n\tlocal entity    item;\n\n\titem = spawn();\n\titem.origin = self.origin;\n\t\n\titem.velocity_z = 300;\n\titem.velocity_x = -100 + (random() * 200);\n\titem.velocity_y = -100 + (random() * 200);\n\t\n\titem.flags = FL_ITEM;\n\titem.solid = SOLID_TRIGGER;\n\titem.movetype = MOVETYPE_TOSS;\n\titem.noise = \"items/inv1.wav\";\n\tsetmodel (item, \"progs/invisibl.mdl\");\n\tsetsize (item, '-16 -16 -24', '16 16 32');\n\titem.cnt = time + timeleft;\n\titem.touch = r_touch;\n\titem.nextthink = time + timeleft;    // remove after 30 seconds\n\titem.think = SUB_Remove;\n};\n\n/*\n============\nPlaceItem\n\nplants the object on the floor\n============\n*/\nvoid() PlaceItem =\n{\n\tlocal float     oldz;\n\n\tself.mdl = self.model;          // so it can be restored on respawn\n\tself.flags = FL_ITEM;           // make extra wide\n\tself.solid = SOLID_TRIGGER;\n\tself.movetype = MOVETYPE_TOSS;  \n\tself.velocity = '0 0 0';\n\tself.origin_z = self.origin_z + 6;\n\toldz = self.origin_z;\n\tif (!droptofloor())\n\t{\n\t\tdprint (\"Bonus item fell out of level at \");\n\t\tdprint (vtos(self.origin));\n\t\tdprint (\"\\n\");\n\t\tremove(self);\n\t\treturn;\n\t}\n};\n\n/*\n============\nStartItem\n\nSets the clipping size and plants the object on the floor\n============\n*/\nvoid() StartItem =\n{\n\tself.nextthink = time + 0.2;    // items start after other solids\n\tself.think = PlaceItem;\n};\n\n/*\n=========================================================================\n\nHEALTH BOX\n\n=========================================================================\n*/\n//\n// T_Heal: add health to an entity, limiting health to max_health\n// \"ignore\" will ignore max_health limit\n//\nfloat (entity e, float healamount, float ignore) T_Heal =\n{\n\tif (e.health <= 0)\n\t\treturn 0;\n\tif ((!ignore) && (e.health >= other.max_health))\n\t\treturn 0;\n\thealamount = ceil(healamount);\n\n\te.health = e.health + healamount;\n\tif ((!ignore) && (e.health >= other.max_health))\n\t\te.health = other.max_health;\n\t\t\n\tif (e.health > 250)\n\t\te.health = 250;\n\treturn 1;\n};\n\n/*QUAKED item_health (.3 .3 1) (0 0 0) (32 32 32) rotten megahealth\nHealth box. Normally gives 25 points.\nRotten box heals 5-10 points,\nmegahealth will add 100 health, then \nrot you down to your maximum health limit, \none point per second.\n*/\n\nfloat   H_ROTTEN = 1;\nfloat   H_MEGA = 2;\n.float  healamount, healtype;\nvoid() health_touch;\nvoid() item_megahealth_rot;\n\nvoid() item_health =\n{       \n\tself.touch = health_touch;\n\n\tif (self.spawnflags & H_ROTTEN)\n\t{\n\t\tprecache_model(\"maps/b_bh10.bsp\");\n\n\t\tprecache_sound(\"items/r_item1.wav\");\n\t\tsetmodel(self, \"maps/b_bh10.bsp\");\n\t\tself.noise = \"items/r_item1.wav\";\n\t\tself.healamount = 15;\n\t\tself.healtype = 0;\n\t}\n\telse\n\tif (self.spawnflags & H_MEGA)\n\t{\n\t\tprecache_model(\"maps/b_bh100.bsp\");\n\t\tprecache_sound(\"items/r_item2.wav\");\n\t\tsetmodel(self, \"maps/b_bh100.bsp\");\n\t\tself.noise = \"items/r_item2.wav\";\n\t\tself.healamount = 100;\n\t\tself.healtype = 2;\n\t}\n\telse\n\t{\n\t\tprecache_model(\"maps/b_bh25.bsp\");\n\t\tprecache_sound(\"items/health1.wav\");\n\t\tsetmodel(self, \"maps/b_bh25.bsp\");\n\t\tself.noise = \"items/health1.wav\";\n\t\tself.healamount = 25;\n\t\tself.healtype = 1;\n\t}\n\tsetsize (self, '0 0 0', '32 32 56');\n\tStartItem ();\n};\n\n\nfloat (entity to, float iid, float quant) TryGiveStackable =\n{\n\tlocal float slot;\n\tlocal float item;\n\tslot = SlotOfItem(to, iid);\n\tif (slot > 2)\n\t{\n\t\titem = ItemInSlot(to, slot);\n\t\tif (ToStatus(item) + quant > 500)\t//500 is our maxstack value here\n\t\t{\n\t\t\tquant = quant + ToStatus(item) - 500;\n\t\t\tSetItemSlot(to, slot, SlotVal(iid, 500));\n\t\t\tslot = 0;\n\t\t\treturn false;\n\t\t}\n\t\telse\n\t\t\tquant = quant + ToStatus(item);\n\t}\n\tif (quant == 0)\n\t\treturn true;\n\tif (slot == 0)\n\t{\n\t\tslot = FindSuitableEmptySlot(to, iid);\n\t\tif (slot == 0)\n\t\t\treturn false;\n\t}\n\n\tSetItemSlot(to, slot, SlotVal(iid, quant));\n\n\treturn true;\n};\n\nvoid () health_touch =\n{\n\tif (!triggercantouch(self, other))\n\t\treturn;\n\n\tif (other.classname != \"player\" || other.ghost != 0)\n\t\treturn;\n\n\tif (other.classname == \"player\")\n\t{\n\t\tif (self.healtype == H_MEGA)\n\t\t\treturn;\n\n\t\tif (other.ghost != 0)\n\t\t\treturn;\n\n\t\tif (!TryGiveStackable(other, IID_CHEM_STIMPACK, 1))\n\t\t{\n\t\t\tif (self.spamdelay < time)\n\t\t\t\tsprint(other, 2, \"full inventory.\\n\");\n\t\t\tself.spamdelay = time + 1;\n\t\t\treturn;\n\t\t}\n\t\tsprint (other, PRINT_HIGH, \"picked up a stimpack.\\n\");\n\n\t\tif (random()*4 <= 2)\n\t\t\tsound (other, CHAN_ITEM, \"misc/item1.wav\", 1, ATTN_NORM);\n\t\telse\n\t\t\tsound (other, CHAN_ITEM, \"misc/item2.wav\", 1, ATTN_NORM);\n\n\t\tself.model = string_null;\n\t\tself.solid = SOLID_NOT;\n\t\treturn;\n\t}\n\n\tactivator = other;\n\tSUB_UseTargets ();\n};\n\nvoid() item_megahealth_rot =\n{\n\tother = self.owner;\n\t\n\tif (other.health > other.max_health)\n\t{\n\t\tother.health = other.health - 1;\n\t\tself.nextthink = time + 1;\n\t\treturn;\n\t}\n\n// it is possible for a player to die and respawn between rots, so don't\n// just blindly subtract the flag off\n\tother.items = other.items - (other.items & IT_SUPERHEALTH);\n\t\n\tif (deathmatch != 2)    // deathmatch 2 is silly old rules\n\t{\n\t\tself.nextthink = time + 20;\n\t\tself.think = SUB_regen;\n\t}\n};\n\n/*\n===============================================================================\n\nARMOR\n\n===============================================================================\n*/\n\nvoid() armor_touch;\n\nvoid() armor_touch =\n{\n\tlocal   float   type, value, bit;\n\n\tif (!triggercantouch(self, other))\n\t\treturn;\n\t\n\tif (other.health <= 0)\n\t\treturn;\n\tif (other.classname != \"player\")\n\t\treturn;\n\n\tif (deathmatch == 4)\n\t\tif (other.invincible_time > 0)\n\t\t\treturn;\n\n\tif (self.classname == \"item_armor1\")\n\t{\n\t\ttype = 0.3;\n\t\tvalue = 100;\n\t\tbit = IT_ARMOR1;\n\t}\n\tif (self.classname == \"item_armor2\")\n\t{\n\t\ttype = 0.6;\n\t\tvalue = 150;\n\t\tbit = IT_ARMOR2;\n\t}\n\tif (self.classname == \"item_armorInv\")\n\t{\n\t\ttype = 0.8;\n\t\tvalue = 200;\n\t\tbit = IT_ARMOR3;\n\t}\n\tif (other.armortype*other.armorvalue >= type*value)\n\t\treturn;\n\t\t\n\tother.armortype = type;\n\tother.armorvalue = value;\n\tother.items = other.items - (other.items & (IT_ARMOR1 | IT_ARMOR2 | IT_ARMOR3)) + bit;\n\n\tself.solid = SOLID_NOT;\n\tself.model = string_null;\n\tif (deathmatch != 2)\n\t\tself.nextthink = time + 20;\n\tself.think = SUB_regen;\n\n\tsprint(other, PRINT_LOW, \"You got armor\\n\");\n// armor touch sound\n\tsound(other, CHAN_ITEM, \"items/armor1.wav\", 1, ATTN_NORM);\n\tstuffcmd (other, \"bf\\n\");\n\t\n\tactivator = other;\n\tSUB_UseTargets();                               // fire all targets / killtargets\n};\n\n\n/*QUAKED item_armor1 (0 .5 .8) (-16 -16 0) (16 16 32)\n*/\n\nvoid() item_armor1_finish =\n{\n\tSUB_UseTargets ();\n\n\n\tself.touch = SUB_Null;\n\tself.solid = SOLID_BBOX;\n\tsetmodel (self, \"progs/enforcer.mdl\");\n\tself.skin = 0;\n\tsetsize (self, '-16 -16 -24', '16 16 32');\n\tsetorigin(self, self.origin + '0 0 24');\n\tself.classname = \"merchant\";\n};\nvoid() item_armor1 =\n{\n\tprecache_model (\"progs/enforcer.mdl\");\n\tself.think = item_armor1_finish;\n\tself.nextthink = time + 0.1;\n};\n\n/*QUAKED item_armor2 (0 .5 .8) (-16 -16 0) (16 16 32)\n*/\n\nvoid() item_armor2 =\n{\n\tself.touch = armor_touch;\n\tprecache_model (\"progs/armor.mdl\");\n\tsetmodel (self, \"progs/armor.mdl\");\n\tself.skin = 1;\n\tsetsize (self, '-16 -16 0', '16 16 56');\n\tStartItem ();\n};\n\n/*QUAKED item_armorInv (0 .5 .8) (-16 -16 0) (16 16 32)\n*/\n\nvoid() item_armorInv =\n{\n\tself.touch = armor_touch;\n\tprecache_model (\"progs/armor.mdl\");\n\tsetmodel (self, \"progs/armor.mdl\");\n\tself.skin = 2;\n\tsetsize (self, '-16 -16 0', '16 16 56');\n\tStartItem ();\n};\n\n/*\n===============================================================================\n\nWEAPONS\n\n===============================================================================\n*/\n\nvoid() bound_other_ammo =\n{\n\tif (other.ammo_shells > 100)\n\t\tother.ammo_shells = 100;\n\tif (other.ammo_nails > 200)\n\t\tother.ammo_nails = 200;\n\tif (other.ammo_rockets > 100)\n\t\tother.ammo_rockets = 100;               \n\tif (other.ammo_cells > 100)\n\t\tother.ammo_cells = 100;         \n};\n\n\nfloat(float w) RankForWeapon =\n{\n\tif (w == IT_LIGHTNING)\n\t\treturn 1;\n\tif (w == IT_ROCKET_LAUNCHER)\n\t\treturn 2;\n\tif (w == IT_SUPER_NAILGUN)\n\t\treturn 3;\n\tif (w == IT_GRENADE_LAUNCHER)\n\t\treturn 4;\n\tif (w == IT_SUPER_SHOTGUN)\n\t\treturn 5;\n\tif (w == IT_NAILGUN)\n\t\treturn 6;\n\treturn 7;\n};\n\nfloat (float w) WeaponCode =\n{\n\tif (w == IT_SUPER_SHOTGUN)\n\t\treturn 3;\n\tif (w == IT_NAILGUN)\n\t\treturn 4;\n\tif (w == IT_SUPER_NAILGUN)\n\t\treturn 5;\n\tif (w == IT_GRENADE_LAUNCHER)\n\t\treturn 6;\n\tif (w == IT_ROCKET_LAUNCHER)\n\t\treturn 7;\n\tif (w == IT_LIGHTNING)\n\t\treturn 8;\n\treturn 1;\n};\n\n/*\n=============\nDeathmatch_Weapon\n\nDeathmatch weapon change rules for picking up a weapon\n\n.float          ammo_shells, ammo_nails, ammo_rockets, ammo_cells;\n=============\n*/\nvoid(float old, float new) Deathmatch_Weapon =\n{\n\tlocal float or, nr;\n\n// change self.weapon if desired\n\tor = RankForWeapon (self.weapon);\n\tnr = RankForWeapon (new);\n\tif ( nr < or )\n\t\tself.weapon = new;\n};\n\n/*\n=============\nweapon_touch\n=============\n*/\nfloat() W_BestWeapon;\n\nvoid() weapon_touch =\n{\n\tlocal   float   hadammo, best, new, old;\n\tlocal   entity  stemp;\n\tlocal   float   leave;\n\n\t// For client weapon_switch\n\tlocal   float   w_switch;\n\n\tif (!triggercantouch(self, other))\n\t\treturn;\n\n\tif (!(other.flags & FL_CLIENT))\n\t\treturn;\n\n\tif ((stof(infokey(other,\"w_switch\"))) == 0)\n\t\tw_switch = 8;\n\telse\n\t\tw_switch = stof(infokey(other,\"w_switch\"));\n\t\n// if the player was using his best weapon, change up to the new one if better          \n\tstemp = self;\n\tself = other;\n\tbest = W_BestWeapon();\n\tself = stemp;\n\n\tif (deathmatch == 2 || deathmatch == 3 || deathmatch == 5)\n\t\tleave = 1;\n\telse\n\t\tleave = 0;\n\t\n\tif (self.classname == \"weapon_nailgun\")\n\t{\n\t\tif (leave && (other.items & IT_NAILGUN) )\n\t\t\treturn;\n\t\thadammo = other.ammo_nails;                     \n\t\tnew = IT_NAILGUN;\n\t\tother.ammo_nails = other.ammo_nails + 30;\n\t}\n\telse if (self.classname == \"weapon_supernailgun\")\n\t{\n\t\tif (leave && (other.items & IT_SUPER_NAILGUN) )\n\t\t\treturn;\n\t\thadammo = other.ammo_rockets;                   \n\t\tnew = IT_SUPER_NAILGUN;\n\t\tother.ammo_nails = other.ammo_nails + 30;\n\t}\n\telse if (self.classname == \"weapon_supershotgun\")\n\t{\n\t\tif (leave && (other.items & IT_SUPER_SHOTGUN) )\n\t\t\treturn;\n\t\thadammo = other.ammo_rockets;                   \n\t\tnew = IT_SUPER_SHOTGUN;\n\t\tother.ammo_shells = other.ammo_shells + 5;\n\t}\n\telse if (self.classname == \"weapon_rocketlauncher\")\n\t{\n\t\tif (leave && (other.items & IT_ROCKET_LAUNCHER) )\n\t\t\treturn;\n\t\thadammo = other.ammo_rockets;                   \n\t\tnew = IT_ROCKET_LAUNCHER;\n\t\tother.ammo_rockets = other.ammo_rockets + 5;\n\t}\n\telse if (self.classname == \"weapon_grenadelauncher\")\n\t{\n\t\tif (leave && (other.items & IT_GRENADE_LAUNCHER) )\n\t\t\treturn;\n\t\thadammo = other.ammo_rockets;                   \n\t\tnew = IT_GRENADE_LAUNCHER;\n\t\tother.ammo_rockets = other.ammo_rockets + 5;\n\t}\n\telse if (self.classname == \"weapon_lightning\")\n\t{\n\t\tif (leave && (other.items & IT_LIGHTNING))\n\t\t\treturn;\n\t\thadammo = other.ammo_rockets;                   \n\t\tnew = IT_LIGHTNING;\n\t\tother.ammo_cells = other.ammo_cells + 15;\n\t}\n\telse\n\t\tobjerror (\"weapon_touch: unknown classname\");\n\n\tsprint (other, PRINT_LOW, \"You got the \");\n\tsprint (other, PRINT_LOW, self.netname);\n\tsprint (other, PRINT_LOW, \"\\n\");\n// weapon touch sound\n\tsound (other, CHAN_ITEM, \"weapons/pkup.wav\", 1, ATTN_NORM);\n\tstuffcmd (other, \"bf\\n\");\n\n\tbound_other_ammo ();\n\n// change to the weapon\n\told = other.items;\n\tother.items = other.items | new;\n\t\n\tstemp = self;\n\tself = other;\n\n\tif ( WeaponCode(new) <= w_switch )\n\t{\n\t\tif (self.flags & FL_INWATER)\n\t\t{\n\t\t\tif (new != IT_LIGHTNING)\n\t\t\t{\n\t\t\t\tDeathmatch_Weapon (old, new);\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{                \n\t\t\tDeathmatch_Weapon (old, new);\n\t\t}\n\t}\n\n\tW_SetCurrentAmmo();\n\n\tself = stemp;\n\n\tif (leave)\n\t\treturn;\n\n\tif (deathmatch!=3 || deathmatch !=5)\n\t{\n\t// remove it in single player, or setup for respawning in deathmatch\n\t\tself.model = string_null;\n\t\tself.solid = SOLID_NOT;\n\t\tif (deathmatch != 2)\n\t\t\tself.nextthink = time + 30;\n\t\tself.think = SUB_regen;\n\t}\n\tactivator = other;\n\tSUB_UseTargets();                               // fire all targets / killtargets\n};\n\n\n/*QUAKED weapon_supershotgun (0 .5 .8) (-16 -16 0) (16 16 32)\n*/\n\nvoid() weapon_supershotgun =\n{\nif (deathmatch <= 3)\n{\n\tprecache_model (\"progs/g_shot.mdl\");\n\tsetmodel (self, \"progs/g_shot.mdl\");\n\tself.weapon = IT_SUPER_SHOTGUN;\n\tself.netname = \"Double-barrelled Shotgun\";\n\tself.touch = weapon_touch;\n\tsetsize (self, '-16 -16 0', '16 16 56');\n\tStartItem ();\n}\n};\n\n/*QUAKED weapon_nailgun (0 .5 .8) (-16 -16 0) (16 16 32)\n*/\n\nvoid() weapon_nailgun =\n{\nif (deathmatch <= 3)\n{\n\tprecache_model (\"progs/g_nail.mdl\");\n\tsetmodel (self, \"progs/g_nail.mdl\");\n\tself.weapon = IT_NAILGUN;\n\tself.netname = \"nailgun\";\n\tself.touch = weapon_touch;\n\tsetsize (self, '-16 -16 0', '16 16 56');\n\tStartItem ();\n}\n};\n\n/*QUAKED weapon_supernailgun (0 .5 .8) (-16 -16 0) (16 16 32)\n*/\n\nvoid() weapon_supernailgun =\n{\nif (deathmatch <= 3)\n{\n\tprecache_model (\"progs/g_nail2.mdl\");\n\tsetmodel (self, \"progs/g_nail2.mdl\");\n\tself.weapon = IT_SUPER_NAILGUN;\n\tself.netname = \"Super Nailgun\";\n\tself.touch = weapon_touch;\n\tsetsize (self, '-16 -16 0', '16 16 56');\n\tStartItem ();\n}\n};\n\n/*QUAKED weapon_grenadelauncher (0 .5 .8) (-16 -16 0) (16 16 32)\n*/\n\nvoid() weapon_grenadelauncher =\n{\nif (deathmatch <= 3)\n{\n\tprecache_model (\"progs/g_rock.mdl\");\n\tsetmodel (self, \"progs/g_rock.mdl\");\n\tself.weapon = 3;\n\tself.netname = \"Grenade Launcher\";\n\tself.touch = weapon_touch;\n\tsetsize (self, '-16 -16 0', '16 16 56');\n\tStartItem ();\n}\n};\n\n/*QUAKED weapon_rocketlauncher (0 .5 .8) (-16 -16 0) (16 16 32)\n*/\n\nvoid() weapon_rocketlauncher =\n{\nif (deathmatch <= 3)\n{\n\tprecache_model (\"progs/g_rock2.mdl\");\n\tsetmodel (self, \"progs/g_rock2.mdl\");\n\tself.weapon = 3;\n\tself.netname = \"Rocket Launcher\";\n\tself.touch = weapon_touch;\n\tsetsize (self, '-16 -16 0', '16 16 56');\n\tStartItem ();\n}\n};\n\n\n/*QUAKED weapon_lightning (0 .5 .8) (-16 -16 0) (16 16 32)\n*/\n\nvoid() weapon_lightning =\n{\nif (deathmatch <= 3)\n{\n\tprecache_model (\"progs/g_light.mdl\");\n\tsetmodel (self, \"progs/g_light.mdl\");\n\tself.weapon = 3;\n\tself.netname = \"Thunderbolt\";\n\tself.touch = weapon_touch;\n\tsetsize (self, '-16 -16 0', '16 16 56');\n\tStartItem ();\n}\n};\n\n\n/*\n===============================================================================\n\nAMMO\n\n===============================================================================\n*/\n\nfloat() GetRandomAmmo =\n{\n\tlocal float r;\n\n\tr = random()*20;\n\n\tif (r <= 2)\n\t\treturn IID_AM_NEEDLER;\n\telse if (r <= 6)\n\t\treturn IID_AM_10MM;\n\telse if (r <= 8)\n\t\treturn IID_AM_556MM;\n\telse if (r <= 10)\n\t\treturn IID_AM_5MMHIGHVEL;\n\telse if (r <= 13)\n\t\treturn IID_AM_12GAUGESHELLS;\n\telse if (r <= 14)\n\t\treturn IID_AM_ENERGYCELL;\n\telse if (r <= 15)\n\t\treturn IID_AM_2MMEC;\n\telse if (r <= 16)\n\t\treturn IID_AM_762MM;\n\telse if (r <= 18)\n\t\treturn IID_AM_44MAGNUM;\n\telse\n\t\treturn IID_AM_10MM;\n\n};\n\nfloat(float ammotype) GetAmmoCount =\n{\n\tlocal float ammocount;\n\n\tif (ammotype == IID_AM_NEEDLER)\n\t\tammocount = 2 + random()*7;\n\tif (ammotype == IID_AM_10MM)\n\t\tammocount = 5 + random()*10;\n\tif (ammotype == IID_AM_556MM)\n\t\tammocount = 3 + random()*8;\n\tif (ammotype == IID_AM_5MMHIGHVEL)\n\t\tammocount = 5 + random()*10;\n\tif (ammotype == IID_AM_12GAUGESHELLS)\n\t\tammocount = 2 + random()*6;\n\tif (ammotype == IID_AM_ENERGYCELL)\n\t\tammocount = 2 + random()*6;\n\tif (ammotype == IID_AM_2MMEC)\n\t\tammocount = 1 + random()*2;\n\tif (ammotype == IID_AM_762MM)\n\t\tammocount = 2 + random()*4;\n\tif (ammotype == IID_AM_44MAGNUM)\n\t\tammocount = 2 + random()*4;\n\tif (ammotype == IID_AM_45ACP)\n\t\tammocount = 5 + random()*10;\n\n\treturn ammocount;\n};\n\nvoid() ammo_touch =\n{\n\tlocal entity stemp;\n\tlocal string ammoname, ammocountftos;\n\tlocal float best, ammotype, ammocount;\n\n\tif (!triggercantouch(self, other))\n\t\treturn;\n\n\tif (other.classname != \"player\")\n\t\treturn;\n\tif (other.health <= 0)\n\t\treturn;\n\n// if the player was using his best weapon, change up to the new one if better          \n\tstemp = self;\n\tself = other;\n\tbest = W_BestWeapon();\n\tself = stemp;\n\n\tsprint (other, 2, \"a pre-war military ammo cache.\\n\");\n\tsprint (other, 2, \"found \");\n\n\tammotype = GetRandomAmmo();\n\n\tammocount = GetAmmoCount(ammotype);\n\tammocount = ceil(ammocount);\n\n\tif (coop == 1 && total_players == 1)\n\t\tammocount = ammocount * 2;\n\n\tammoname = GetItemName(ammotype);\n\tammocountftos = ftos(ammocount);\n\n\n// shotgun\n\tif (self.weapon == 1)\n\t{\n\t\tsprint (other, 2, ammoname);\n\t\tsprint (other, 2, \"(\");\n\t\tsprint (other, 2, ammocountftos);\n\t\tsprint (other, 2, \") \");\n\n\t\tTryGiveStackable(other, ammotype, ammocount);\n\n\t\tif (random()*20 <= 10)\n\t\t{\n\t\t\tother.ammo_shells = other.ammo_shells + 2;\n\t\t\t\tif (coop == 1 && total_players == 1)\n\t\t\t\t\tother.ammo_shells = other.ammo_shells + 2;\n\n\t\t\tsprint (other, 2, \" and bottle caps.\\n\");\n\t\t}\n\t\telse\n\t\t\tsprint (other, 2, \"\\n\");\n\n\t}\n\n// spikes\n\tif (self.weapon == 2)\n\t{\n\t\tsprint (other, 2, ammoname);\n\t\tsprint (other, 2, \"(\");\n\t\tsprint (other, 2, ammocountftos);\n\t\tsprint (other, 2, \") \");\n\n\t\tTryGiveStackable(other, ammotype, ammocount);\n\n\t\tif (random()*20 <= 10)\n\t\t{\n\t\t\tother.ammo_shells = other.ammo_shells + 2;\n\t\t\tsprint (other, 2, \" and bottle caps.\\n\");\n\t\t}\n\t\telse\n\t\t\tsprint (other, 2, \"\\n\");\n\t}\n\n//      rockets\n\tif (self.weapon == 3)\n\t{\n\t\tsprint (other, 2, ammoname);\n\t\tsprint (other, 2, \"(\");\n\t\tsprint (other, 2, ammocountftos);\n\t\tsprint (other, 2, \") \");\n\n\t\tTryGiveStackable(other, ammotype, ammocount);\n\n\t\tif (random()*20 <= 10)\n\t\t{\n\t\t\tother.ammo_shells = other.ammo_shells + 2;\n\t\t\tsprint (other, 2, \" and bottle caps.\\n\");\n\t\t}\n\t\telse\n\t\t\tsprint (other, 2, \"\\n\");\n\n\t}\n\n//      cells\n\tif (self.weapon == 4)\n\t{\n\t\tsprint (other, 2, ammoname);\n\t\tsprint (other, 2, \"(\");\n\t\tsprint (other, 2, ammocountftos);\n\t\tsprint (other, 2, \") \");\n\n\t\tTryGiveStackable(other, ammotype, ammocount);\n\n\t\tif (random()*20 <= 10)\n\t\t{\n\t\t\tother.ammo_shells = other.ammo_shells + 2;\n\t\t\tsprint (other, 2, \" and bottle caps.\\n\");\n\t\t}\n\t\telse\n\t\t\tsprint (other, 2, \"\\n\");\n\t}\n\n\tbound_other_ammo ();\n\t\n\n\n// change to a better weapon if appropriate\n/*\n\tif ( other.weapon == best )\n\t{\n\t\tstemp = self;\n\t\tself = other;\n\t\tself.weapon = W_BestWeapon();\n\t\tW_SetCurrentAmmo ();\n\t\tself = stemp;\n\t}*/\n\n// if changed current ammo, update it\n\tstemp = self;\n\tself = other;\n\tself = stemp;\n\n// remove it in single player, or setup for respawning in deathmatch\n\tself.model = string_null;\n\tself.solid = SOLID_NOT;\n\tif (coop != 1)\n\t\tself.nextthink = time + 30;\n\n// Xian -- If playing in DM 3.0 mode, halve the time ammo respawns        \n\n\tself.think = SUB_regen;\n\n\tactivator = other;\n\tSUB_UseTargets();                               // fire all targets / killtargets\n};\n\n\n\n\nfloat WEAPON_BIG2 = 1;\n\n/*QUAKED item_shells (0 .5 .8) (0 0 0) (32 32 32) big\n*/\n\nvoid() item_shells =\n{\n\tif (deathmatch == 4)\n\t\treturn;\n\n\tif (coop == 1 && random()*4 <= 2)\n\t\treturn;\n\n\tself.touch = ammo_touch;\n\n\tif (self.spawnflags & WEAPON_BIG2)\n\t{\n\t\tprecache_model (\"maps/b_shell1.bsp\");\n\t\tsetmodel (self, \"maps/b_shell1.bsp\");\n\t\tself.aflag = 40;\n\t}\n\telse\n\t{\n\t\tprecache_model (\"maps/b_shell0.bsp\");\n\t\tsetmodel (self, \"maps/b_shell0.bsp\");\n\t\tself.aflag = 20;\n\t}\n\tself.weapon = 1;\n\tself.netname = \"shells\";\n\tsetsize (self, '0 0 0', '32 32 56');\n\tStartItem ();\n};\n\n/*QUAKED item_spikes (0 .5 .8) (0 0 0) (32 32 32) big\n*/\n\nvoid() item_spikes =\n{\n\tif (deathmatch == 4)\n\t\treturn;\n\n\tif (coop == 1 && random()*4 <= 1)\n\t\treturn;\n\n\tself.touch = ammo_touch;\n\n\tif (self.spawnflags & WEAPON_BIG2)\n\t{\n\t\tprecache_model (\"maps/b_nail1.bsp\");\n\t\tsetmodel (self, \"maps/b_nail1.bsp\");\n\t\tself.aflag = 50;\n\t}\n\telse\n\t{\n\t\tprecache_model (\"maps/b_nail0.bsp\");\n\t\tsetmodel (self, \"maps/b_nail0.bsp\");\n\t\tself.aflag = 25;\n\t}\n\tself.weapon = 2;\n\tself.netname = \"nails\";\n\tsetsize (self, '0 0 0', '32 32 56');\n\tStartItem ();\n\n};\n\n/*QUAKED item_rockets (0 .5 .8) (0 0 0) (32 32 32) big\n*/\n\nvoid() item_rockets =\n{\n\tif (deathmatch == 4)\n\t\treturn;\n\n\tif (coop == 1 && random()*4 <= 3)\n\t\treturn;\n\n\tself.touch = ammo_touch;\n\n\tif (self.spawnflags & WEAPON_BIG2)\n\t{\n\t\tprecache_model (\"maps/b_rock1.bsp\");\n\t\tsetmodel (self, \"maps/b_rock1.bsp\");\n\t\tself.aflag = 10;\n\t}\n\telse\n\t{\n\t\tprecache_model (\"maps/b_rock0.bsp\");\n\t\tsetmodel (self, \"maps/b_rock0.bsp\");\n\t\tself.aflag = 5;\n\t}\n\tself.weapon = 3;\n\tself.netname = \"rockets\";\n\tsetsize (self, '0 0 0', '32 32 56');\n\tStartItem ();\n\n};\n\n\n/*QUAKED item_cells (0 .5 .8) (0 0 0) (32 32 32) big\n*/\n\nvoid() item_cells =\n{\n\tif (deathmatch == 4)\n\t\treturn;\n\n\tif (coop == 1 && random()*4 <= 2)\n\t\treturn;\n\n\tself.touch = ammo_touch;\n\n\tif (self.spawnflags & WEAPON_BIG2)\n\t{\n\t\tprecache_model (\"maps/b_batt1.bsp\");\n\t\tsetmodel (self, \"maps/b_batt1.bsp\");\n\t\tself.aflag = 12;\n\t}\n\telse\n\t{\n\t\tprecache_model (\"maps/b_batt0.bsp\");\n\t\tsetmodel (self, \"maps/b_batt0.bsp\");\n\t\tself.aflag = 6;\n\t}\n\tself.weapon = 4;\n\tself.netname = \"cells\";\n\tsetsize (self, '0 0 0', '32 32 56');\n\tStartItem ();\n\n};\n\n\n/*QUAKED item_weapon (0 .5 .8) (0 0 0) (32 32 32) shotgun rocket spikes big\nDO NOT USE THIS!!!! IT WILL BE REMOVED!\n*/\n\nfloat WEAPON_SHOTGUN = 1;\nfloat WEAPON_ROCKET = 2;\nfloat WEAPON_SPIKES = 4;\nfloat WEAPON_BIG = 8;\nvoid() item_weapon =\n{\n\tif (coop == 1 && random()*4 <= 3)\n\t\treturn;\n\n\tself.touch = ammo_touch;\n\n\tprecache_model (\"maps/b_shell0.bsp\");\n\tsetmodel (self, \"maps/b_shell0.bsp\");\n\n\t\n\tsetsize (self, '0 0 0', '32 32 56');\n\tStartItem ();\n};\n\n\n/*\n===============================================================================\n\nKEYS\n\n===============================================================================\n*/\n\nvoid() key_touch =\n{\n\tif (!triggercantouch(self, other))\n\t\treturn;\n\n\tif (other.classname != \"player\")\n\t\treturn;\n\tif (other.health <= 0)\n\t\treturn;\n\tif (other.items & self.items)\n\t\treturn;\n\n\tsprint (other, PRINT_LOW, \"You got the \");\n\tsprint (other, PRINT_LOW, self.netname);\n\tsprint (other,PRINT_LOW, \"\\n\");\n\n\tsound (other, CHAN_ITEM, self.noise, 1, ATTN_NORM);\n\tstuffcmd (other, \"bf\\n\");\n\tother.items = other.items | self.items;\n\n\tself.solid = SOLID_NOT;\n\tself.model = string_null;\n\n\tactivator = other;\n\tSUB_UseTargets();                               // fire all targets / killtargets\n};\n\n\nvoid() key_setsounds =\n{\n\tif (world.worldtype == 0)\n\t{\n\t\tprecache_sound (\"misc/medkey.wav\");\n\t\tself.noise = \"misc/medkey.wav\";\n\t}\n\tif (world.worldtype == 1)\n\t{\n\t\tprecache_sound (\"misc/runekey.wav\");\n\t\tself.noise = \"misc/runekey.wav\";\n\t}\n\tif (world.worldtype == 2)\n\t{\n\t\tprecache_sound2 (\"misc/basekey.wav\");\n\t\tself.noise = \"misc/basekey.wav\";\n\t}\n};\n\n/*QUAKED item_key1 (0 .5 .8) (-16 -16 -24) (16 16 32)\nSILVER key\nIn order for keys to work\nyou MUST set your maps\nworldtype to one of the\nfollowing:\n0: medieval\n1: metal\n2: base\n*/\n\nvoid() item_key1 =\n{\n\tif (world.worldtype == 0)\n\t{\n\t\tprecache_model (\"progs/w_s_key.mdl\");\n\t\tsetmodel (self, \"progs/w_s_key.mdl\");\n\t\tself.netname = \"silver key\";\n\t}\n\telse if (world.worldtype == 1)\n\t{\n\t\tprecache_model (\"progs/m_s_key.mdl\");\n\t\tsetmodel (self, \"progs/m_s_key.mdl\");\n\t\tself.netname = \"silver runekey\";\n\t}\n\telse if (world.worldtype == 2)\n\t{\n\t\tprecache_model2 (\"progs/b_s_key.mdl\");\n\t\tsetmodel (self, \"progs/b_s_key.mdl\");\n\t\tself.netname = \"silver keycard\";\n\t}\n\tkey_setsounds();\n\tself.touch = key_touch;\n\tself.items = IT_KEY1;\n\tsetsize (self, '-16 -16 -24', '16 16 32');\n\tStartItem ();\n};\n\n/*QUAKED item_key2 (0 .5 .8) (-16 -16 -24) (16 16 32)\nGOLD key\nIn order for keys to work\nyou MUST set your maps\nworldtype to one of the\nfollowing:\n0: medieval\n1: metal\n2: base\n*/\n\nvoid() item_key2 =\n{\n\tif (world.worldtype == 0)\n\t{\n\t\tprecache_model (\"progs/w_g_key.mdl\");\n\t\tsetmodel (self, \"progs/w_g_key.mdl\");\n\t\tself.netname = \"gold key\";\n\t}\n\tif (world.worldtype == 1)\n\t{\n\t\tprecache_model (\"progs/m_g_key.mdl\");\n\t\tsetmodel (self, \"progs/m_g_key.mdl\");\n\t\tself.netname = \"gold runekey\";\n\t}\n\tif (world.worldtype == 2)\n\t{\n\t\tprecache_model2 (\"progs/b_g_key.mdl\");\n\t\tsetmodel (self, \"progs/b_g_key.mdl\");\n\t\tself.netname = \"gold keycard\";\n\t}\n\tkey_setsounds();\n\tself.touch = key_touch;\n\tself.items = IT_KEY2;\n\tsetsize (self, '-16 -16 -24', '16 16 32');\n\tStartItem ();\n};\n\n\n\n/*\n===============================================================================\n\nEND OF LEVEL RUNES\n\n===============================================================================\n*/\n\nvoid() sigil_touch =\n{\n\tif (!triggercantouch(self, other))\n\t\treturn;\n\n\tif (other.classname != \"player\")\n\t\treturn;\n\tif (other.health <= 0)\n\t\treturn;\n\n\tcenterprint (other, \"You got the rune!\");\n\n\tsound (other, CHAN_ITEM, self.noise, 1, ATTN_NORM);\n\tstuffcmd (other, \"bf\\n\");\n\tself.solid = SOLID_NOT;\n\tself.model = string_null;\n\tserverflags = serverflags | (self.spawnflags & 15);\n\tself.classname = \"\";            // so rune doors won't find it\n\t\n\tactivator = other;\n\tSUB_UseTargets();                               // fire all targets / killtargets\n};\n\n\n/*QUAKED item_sigil (0 .5 .8) (-16 -16 -24) (16 16 32) E1 E2 E3 E4\nEnd of level sigil, pick up to end episode and return to jrstart.\n*/\n\nvoid() item_sigil =\n{\n\tif (!self.spawnflags)\n\t\tobjerror (\"no spawnflags\");\n\n\tprecache_sound (\"misc/runekey.wav\");\n\tself.noise = \"misc/runekey.wav\";\n\n\tif (self.spawnflags & 1)\n\t{\n\t\tprecache_model (\"progs/end1.mdl\");\n\t\tsetmodel (self, \"progs/end1.mdl\");\n\t}\n\tif (self.spawnflags & 2)\n\t{\n\t\tprecache_model2 (\"progs/end2.mdl\");\n\t\tsetmodel (self, \"progs/end2.mdl\");\n\t}\n\tif (self.spawnflags & 4)\n\t{\n\t\tprecache_model2 (\"progs/end3.mdl\");\n\t\tsetmodel (self, \"progs/end3.mdl\");\n\t}\n\tif (self.spawnflags & 8)\n\t{\n\t\tprecache_model2 (\"progs/end4.mdl\");\n\t\tsetmodel (self, \"progs/end4.mdl\");\n\t}\n\t\n\tself.touch = sigil_touch;\n\tsetsize (self, '-16 -16 -24', '16 16 32');\n\tStartItem ();\n};\n\n/*\n===============================================================================\n\nPOWERUPS\n\n===============================================================================\n*/\n\nvoid() powerup_touch;\n\n\nvoid() powerup_touch =\n{\n\tif (!triggercantouch(self, other))\n\t\treturn;\n\n\tif (other.classname != \"player\")\n\t\treturn;\n\tif (other.health <= 0)\n\t\treturn;\n\n\tsprint (other, PRINT_LOW, \"You got the \");\n\tsprint (other,PRINT_LOW,  self.netname);\n\tsprint (other,PRINT_LOW, \"\\n\");\n\n\tself.mdl = self.model;\n\t\n\tif ((self.classname == \"item_artifact_invulnerability\") ||\n\t\t(self.classname == \"item_artifact_invisibility\"))\n\t\tself.nextthink = time + 60*5;\n\telse\n\t\tself.nextthink = time + 60;\n\t\n\tself.think = SUB_regen;\n\n\tsound (other, CHAN_VOICE, self.noise, 1, ATTN_NORM);\n\tstuffcmd (other, \"bf\\n\");\n\tself.solid = SOLID_NOT;\n\tother.items = other.items | self.items;\n\tself.model = string_null;\n\n// do the apropriate action\n\tif (self.classname == \"item_artifact_envirosuit\")\n\t{\n\t\tother.rad_time = 1;\n\t\tother.radsuit_finished = time + 30;\n\t}\n\t\n\tif (self.classname == \"item_artifact_invulnerability\")\n\t{\n\t\tother.invincible_time = 1;\n\t\tother.invincible_finished = time + 30;\n\t}\n\t\n\tif (self.classname == \"item_artifact_invisibility\")\n\t{\n\t\tother.invisible_time = 1;\n\t\tother.invisible_finished = time + 30;\n\t}\n\n\tif (self.classname == \"item_artifact_super_damage\")\n\t{\n\t\tif (deathmatch == 4)\n\t\t{\n\t\t\tother.armortype = 0;\n\t\t\tother.armorvalue = 0 * 0.01;\n\t\t\tother.ammo_cells = 0;\n\t\t}\n\t\tother.super_time = 1;\n\t\tother.super_damage_finished = time + 30;\n\t}       \n\n\tactivator = other;\n\tSUB_UseTargets();                               // fire all targets / killtargets\n};\n\n\n\n/*QUAKED item_artifact_invulnerability (0 .5 .8) (-16 -16 -24) (16 16 32)\nPlayer is invulnerable for 30 seconds\n*/\nvoid() item_artifact_invulnerability =\n{\n\tself.touch = powerup_touch;\n\n\tprecache_model (\"progs/invulner.mdl\");\n\tprecache_sound (\"items/protect.wav\");\n\tprecache_sound (\"items/protect2.wav\");\n\tprecache_sound (\"items/protect3.wav\");\n\tself.noise = \"items/protect.wav\";\n\tsetmodel (self, \"progs/invulner.mdl\");\n\tself.netname = \"Pentagram of Protection\";\n\tself.effects = self.effects | EF_RED;\n\tself.items = IT_INVULNERABILITY;\n\tsetsize (self, '-16 -16 -24', '16 16 32');\n\tStartItem ();\n};\n\n/*QUAKED item_artifact_envirosuit (0 .5 .8) (-16 -16 -24) (16 16 32)\nPlayer takes no damage from water or slime for 30 seconds\n*/\nvoid() item_artifact_envirosuit =\n{\n\tself.touch = powerup_touch;\n\n\tprecache_model (\"progs/suit.mdl\");\n\tprecache_sound (\"items/suit.wav\");\n\tprecache_sound (\"items/suit2.wav\");\n\tself.noise = \"items/suit.wav\";\n\tsetmodel (self, \"progs/suit.mdl\");\n\tself.netname = \"Biosuit\";\n\tself.items = IT_SUIT;\n\tsetsize (self, '-16 -16 -24', '16 16 32');\n\tStartItem ();\n};\n\n\n/*QUAKED item_artifact_invisibility (0 .5 .8) (-16 -16 -24) (16 16 32)\nPlayer is invisible for 30 seconds\n*/\nvoid() item_artifact_invisibility =\n{\n\tself.touch = powerup_touch;\n\n\tprecache_model (\"progs/invisibl.mdl\");\n\tprecache_sound (\"items/inv1.wav\");\n\tprecache_sound (\"items/inv2.wav\");\n\tprecache_sound (\"items/inv3.wav\");\n\tself.noise = \"items/inv1.wav\";\n\tsetmodel (self, \"progs/invisibl.mdl\");\n\tself.netname = \"Ring of Shadows\";\n\tself.items = IT_INVISIBILITY;\n\tsetsize (self, '-16 -16 -24', '16 16 32');\n\tStartItem ();\n};\n\n\n/*QUAKED item_artifact_super_damage (0 .5 .8) (-16 -16 -24) (16 16 32)\nThe next attack from the player will do 4x damage\n*/\nvoid() item_artifact_super_damage =\n{\n\tself.touch = powerup_touch;\n\n\tprecache_model (\"progs/quaddama.mdl\");\n\tprecache_sound (\"items/damage.wav\");\n\tprecache_sound (\"items/damage2.wav\");\n\tprecache_sound (\"items/damage3.wav\");\n\tself.noise = \"items/damage.wav\";\n\tsetmodel (self, \"progs/quaddama.mdl\");\n\tif (deathmatch == 4)\n\t\tself.netname = \"OctaPower\";\t\n\telse\n\t\tself.netname = \"Quad Damage\";\n\tself.items = IT_QUAD;\n\tself.effects = self.effects | EF_BLUE;\n\tsetsize (self, '-16 -16 -24', '16 16 32');\n\tStartItem ();\n};\n\n\n\n/*\n===============================================================================\n\nPLAYER BACKPACKS\n\n===============================================================================\n*/\n\nvoid() BackpackTouch =\n{\n\tlocal float ammotype, ammocount;\n\tlocal string ammoname;\n\n\tif (other.classname != \"player\")\n\t\treturn;\n\n\tsprint(other, 2, \"loot: found\");\n\n\tif (self.weapon == 1)\n\t{\n\t\tTryGiveStackable(other, IID_AM_10MM, floor(9+random()*9));\n\t\tsprint(other, 2, \" 10mm ammo\");\n\t}\n\telse if (self.weapon == 2)\n\t{\n\t\tTryGiveStackable(other, IID_AM_556MM, floor(5+random()*5));\n\t\tsprint(other, 2, \" 5mm ammo\");\n\t}\n\telse if (self.weapon == 3)\n\t{\n\t\tTryGiveStackable(other, IID_AM_12GAUGESHELLS, floor(3+random()*3));\n\t\tsprint(other, 2, \" 12 gauge ammo\");\n\t}\n\telse if (self.weapon == 4)\n\t{\n\t\tTryGiveStackable(other, IID_AM_10MM, floor(9+random()*9));\n\t\tsprint(other, 2, \" 10mm ammo\");\n\t}\n\telse\n\t{\n\t\tammotype = GetRandomAmmo();\n\t\tammocount = GetAmmoCount(ammotype);\n\t\tammoname = GetItemName(ammotype);\n\t\tsprint(other, 2, \" \");\n\t\tsprint(other, 2, ammoname);\n\n\t}\n\n\n\tif (random()*4 <= 2)\n\t{\n\t\tself.ammo_shells = self.ammo_shells + 3;\n\t\tsprint(other, 2, \" and some caps.\\n\");\n\t\tif (total_players == 1)\n\t\t\tself.ammo_shells = self.ammo_shells + 3;\n\t}\n\telse\n\t\tsprint(other, 2, \".\\n\");\n\n\tremove(self);\n\treturn;\n};\n\n/*\n===============\nDropBackpack\n===============\n*/\nvoid() DropBackpack =\n{\n\tlocal entity    item;\n\n\titem = spawn();\n\titem.origin = self.origin - '0 0 24';\n\t\n\titem.weapon = self.weapon;\n\n\titem.velocity_z = 300;\n\titem.velocity_x = -100 + (random() * 200);\n\titem.velocity_y = -100 + (random() * 200);\n\t\n\titem.flags = FL_ITEM;\n\titem.solid = SOLID_TRIGGER;\n\titem.movetype = MOVETYPE_TOSS;\n\tsetmodel (item, \"progs/backpack.mdl\");\n\tsetsize (item, '-16 -16 0', '16 16 56');\n\titem.touch = BackpackTouch;\n\t\n\titem.nextthink = time + 120;    // remove after 2 minutes\n\titem.think = SUB_Remove;\n};\n\n\n"
  },
  {
    "path": "quakec/fallout2/knight.qc",
    "content": "/*\n==============================================================================\n\nKNIGHT\n\n==============================================================================\n*/\n\n$cd id1/models/knight\n$origin 0 0 24\n$base base\n$skin badass3\n\n$frame stand1 stand2 stand3 stand4 stand5 stand6 stand7 stand8 stand9\n\n$frame runb1 runb2 runb3 runb4 runb5 runb6 runb7 runb8\n\n//frame runc1 runc2 runc3 runc4 runc5 runc6\n\n$frame runattack1 runattack2 runattack3 runattack4 runattack5\n$frame runattack6 runattack7 runattack8 runattack9 runattack10\n$frame runattack11\n\n$frame pain1 pain2 pain3\n\n$frame painb1 painb2 painb3 painb4 painb5 painb6 painb7 painb8 painb9\n$frame painb10 painb11\n\n//frame attack1 attack2 attack3 attack4 attack5 attack6 attack7\n//frame attack8 attack9 attack10 attack11\n\n$frame attackb1 attackb2 attackb3 attackb4 attackb5\n$frame attackb6 attackb7 attackb8 attackb9 attackb10\n\n$frame walk1 walk2 walk3 walk4 walk5 walk6 walk7 walk8 walk9\n$frame walk10 walk11 walk12 walk13 walk14\n\n$frame kneel1 kneel2 kneel3 kneel4 kneel5\n\n$frame standing2 standing3 standing4 standing5\n\n$frame death1 death2 death3 death4 death5 death6 death7 death8\n$frame death9 death10\n\n$frame deathb1 deathb2 deathb3 deathb4 deathb5 deathb6 deathb7 deathb8\n$frame deathb9 deathb10 deathb11\n\nvoid()\tknight_stand1\t=[\t$stand1,\tknight_stand2\t] {ai_stand();};\nvoid()\tknight_stand2\t=[\t$stand2,\tknight_stand3\t] {ai_stand();};\nvoid()\tknight_stand3\t=[\t$stand3,\tknight_stand4\t] {ai_stand();};\nvoid()\tknight_stand4\t=[\t$stand4,\tknight_stand5\t] {ai_stand();};\nvoid()\tknight_stand5\t=[\t$stand5,\tknight_stand6\t] {ai_stand();};\nvoid()\tknight_stand6\t=[\t$stand6,\tknight_stand7\t] {ai_stand();};\nvoid()\tknight_stand7\t=[\t$stand7,\tknight_stand8\t] {ai_stand();};\nvoid()\tknight_stand8\t=[\t$stand8,\tknight_stand9\t] {ai_stand();};\nvoid()\tknight_stand9\t=[\t$stand9,\tknight_stand1\t] {ai_stand();};\n\nvoid()\tknight_walk1\t=[\t$walk1,\t\tknight_walk2\t] {\nif (random() < 0.2)\n\tsound (self, CHAN_VOICE, \"knight/idle.wav\", 1,  ATTN_IDLE);\nai_walk(3);};\nvoid()\tknight_walk2\t=[\t$walk2,\t\tknight_walk3\t] {ai_walk(2);};\nvoid()\tknight_walk3\t=[\t$walk3,\t\tknight_walk4\t] {ai_walk(3);};\nvoid()\tknight_walk4\t=[\t$walk4,\t\tknight_walk5\t] {ai_walk(4);};\nvoid()\tknight_walk5\t=[\t$walk5,\t\tknight_walk6\t] {ai_walk(3);};\nvoid()\tknight_walk6\t=[\t$walk6,\t\tknight_walk7\t] {ai_walk(3);};\nvoid()\tknight_walk7\t=[\t$walk7,\t\tknight_walk8\t] {ai_walk(3);};\nvoid()\tknight_walk8\t=[\t$walk8,\t\tknight_walk9\t] {ai_walk(4);};\nvoid()\tknight_walk9\t=[\t$walk9,\t\tknight_walk10\t] {ai_walk(3);};\nvoid()\tknight_walk10\t=[\t$walk10,\tknight_walk11\t] {ai_walk(3);};\nvoid()\tknight_walk11\t=[\t$walk11,\tknight_walk12\t] {ai_walk(2);};\nvoid()\tknight_walk12\t=[\t$walk12,\tknight_walk13\t] {ai_walk(3);};\nvoid()\tknight_walk13\t=[\t$walk13,\tknight_walk14\t] {ai_walk(4);};\nvoid()\tknight_walk14\t=[\t$walk14,\tknight_walk1\t] {ai_walk(3);};\n\n\nvoid()\tknight_run1\t=[\t$runb1,\t\tknight_run2\t] {\nif (random() < 0.2)\n\tsound (self, CHAN_VOICE, \"knight/idle.wav\", 1,  ATTN_IDLE);\nai_run(16);};\nvoid()\tknight_run2\t=[\t$runb2,\t\tknight_run3\t] {ai_run(20);};\nvoid()\tknight_run3\t=[\t$runb3,\t\tknight_run4\t] {ai_run(13);};\nvoid()\tknight_run4\t=[\t$runb4,\t\tknight_run5\t] {ai_run(7);};\nvoid()\tknight_run5\t=[\t$runb5,\t\tknight_run6\t] {ai_run(16);};\nvoid()\tknight_run6\t=[\t$runb6,\t\tknight_run7\t] {ai_run(20);};\nvoid()\tknight_run7\t=[\t$runb7,\t\tknight_run8\t] {ai_run(14);};\nvoid()\tknight_run8\t=[\t$runb8,\t\tknight_run1\t] {ai_run(6);};\n\n\nvoid()\tknight_runatk1\t=[\t$runattack1,\t\tknight_runatk2\t]\n{\nif (random() > 0.5)\n\tsound (self, CHAN_WEAPON, \"knight/sword2.wav\", 1, ATTN_NORM);\nelse\n\tsound (self, CHAN_WEAPON, \"knight/sword1.wav\", 1, ATTN_NORM);\nai_charge(20);\n};\nvoid()\tknight_runatk2\t=[\t$runattack2,\tknight_runatk3\t] {ai_charge_side();};\nvoid()\tknight_runatk3\t=[\t$runattack3,\tknight_runatk4\t] {ai_charge_side();};\nvoid()\tknight_runatk4\t=[\t$runattack4,\tknight_runatk5\t] {ai_charge_side();};\nvoid()\tknight_runatk5\t=[\t$runattack5,\tknight_runatk6\t] {ai_melee_side();};\nvoid()\tknight_runatk6\t=[\t$runattack6,\tknight_runatk7\t] {ai_melee_side();};\nvoid()\tknight_runatk7\t=[\t$runattack7,\tknight_runatk8\t] {ai_melee_side();};\nvoid()\tknight_runatk8\t=[\t$runattack8,\tknight_runatk9\t] {ai_melee_side();};\nvoid()\tknight_runatk9\t=[\t$runattack9,\tknight_runatk10\t] {ai_melee_side();};\nvoid()\tknight_runatk10\t=[\t$runattack10,\tknight_runatk11\t] {ai_charge_side();};\nvoid()\tknight_runatk11\t=[\t$runattack11,\tknight_run1\t] {ai_charge(10);};\n\nvoid()\tknight_atk1\t=[\t$attackb1,\t\tknight_atk2\t]\n{\nsound (self, CHAN_WEAPON, \"knight/sword1.wav\", 1, ATTN_NORM);\nai_charge(0);};\nvoid()\tknight_atk2\t=[\t$attackb2,\t\tknight_atk3\t] {ai_charge(7);};\nvoid()\tknight_atk3\t=[\t$attackb3,\t\tknight_atk4\t] {ai_charge(4);};\nvoid()\tknight_atk4\t=[\t$attackb4,\t\tknight_atk5\t] {ai_charge(0);};\nvoid()\tknight_atk5\t=[\t$attackb5,\t\tknight_atk6\t] {ai_charge(3);};\nvoid()\tknight_atk6\t=[\t$attackb6,\t\tknight_atk7\t] {ai_charge(4); ai_melee();};\nvoid()\tknight_atk7\t=[\t$attackb7,\t\tknight_atk8\t] {ai_charge(1); ai_melee();};\nvoid()\tknight_atk8\t=[\t$attackb8,\t\tknight_atk9\t] {ai_charge(3);\nai_melee();};\nvoid()\tknight_atk9\t=[\t$attackb9,\t\tknight_atk10] {ai_charge(1);};\nvoid()\tknight_atk10=[\t$attackb10,\t\tknight_run1\t] {ai_charge(5);};\n\n//void()\tknight_atk9\t=[\t$attack9,\t\tknight_atk10\t] {};\n//void()\tknight_atk10\t=[\t$attack10,\t\tknight_atk11\t] {};\n//void()\tknight_atk11\t=[\t$attack11,\t\tknight_run1\t] {};\n\n//===========================================================================\n\nvoid()\tknight_pain1\t=[\t$pain1,\t\tknight_pain2\t] {};\nvoid()\tknight_pain2\t=[\t$pain2,\t\tknight_pain3\t] {};\nvoid()\tknight_pain3\t=[\t$pain3,\t\tknight_run1\t] {};\n\nvoid()\tknight_painb1\t=[\t$painb1,\tknight_painb2\t] {ai_painforward(0);};\nvoid()\tknight_painb2\t=[\t$painb2,\tknight_painb3\t] {ai_painforward(3);};\nvoid()\tknight_painb3\t=[\t$painb3,\tknight_painb4\t] {};\nvoid()\tknight_painb4\t=[\t$painb4,\tknight_painb5\t] {};\nvoid()\tknight_painb5\t=[\t$painb5,\tknight_painb6\t] {ai_painforward(2);};\nvoid()\tknight_painb6\t=[\t$painb6,\tknight_painb7\t] {ai_painforward(4);};\nvoid()\tknight_painb7\t=[\t$painb7,\tknight_painb8\t] {ai_painforward(2);};\nvoid()\tknight_painb8\t=[\t$painb8,\tknight_painb9\t] {ai_painforward(5);};\nvoid()\tknight_painb9\t=[\t$painb9,\tknight_painb10\t] {ai_painforward(5);};\nvoid()\tknight_painb10\t=[\t$painb10,\tknight_painb11\t] {ai_painforward(0);};\nvoid()\tknight_painb11\t=[\t$painb11,\tknight_run1\t] {};\n\nvoid(entity attacker, float damage)\tknight_pain =\n{\n\tlocal float r;\n\n\tif (self.pain_finished > time)\n\t\treturn;\n\n\tr = random();\n\t\n\tsound (self, CHAN_VOICE, \"knight/khurt.wav\", 1, ATTN_NORM);\n\tif (r < 0.85)\n\t{\n\t\tknight_pain1 ();\n\t\tself.pain_finished = time + 1;\n\t}\n\telse\n\t{\n\t\tknight_painb1 ();\n\t\tself.pain_finished = time + 1;\n\t}\n\t\n};\n\n//===========================================================================\n\nvoid()\tknight_bow1\t=[\t$kneel1,\t\tknight_bow2\t] {ai_turn();};\nvoid()\tknight_bow2\t=[\t$kneel2,\t\tknight_bow3\t] {ai_turn();};\nvoid()\tknight_bow3\t=[\t$kneel3,\t\tknight_bow4\t] {ai_turn();};\nvoid()\tknight_bow4\t=[\t$kneel4,\t\tknight_bow5\t] {ai_turn();};\n\nvoid()\tknight_bow5\t=[\t$kneel5,\t\tknight_bow5\t] {ai_turn();};\n\nvoid()\tknight_bow6\t=[\t$kneel4,\t\tknight_bow7\t] {ai_turn();};\nvoid()\tknight_bow7\t=[\t$kneel3,\t\tknight_bow8\t] {ai_turn();};\nvoid()\tknight_bow8\t=[\t$kneel2,\t\tknight_bow9\t] {ai_turn();};\nvoid()\tknight_bow9\t=[\t$kneel1,\t\tknight_bow10\t] {ai_turn();};\nvoid()\tknight_bow10\t=[\t$walk1,\t\tknight_walk1\t] {ai_turn();};\n\n\n\nvoid()\tknight_die1\t=[\t$death1,\tknight_die2\t] {};\nvoid()\tknight_die2\t=[\t$death2,\tknight_die3\t] {};\nvoid()\tknight_die3\t=[\t$death3,\tknight_die4\t] \n{self.solid = SOLID_NOT;};\nvoid()\tknight_die4\t=[\t$death4,\tknight_die5\t] {};\nvoid()\tknight_die5\t=[\t$death5,\tknight_die6\t] {};\nvoid()\tknight_die6\t=[\t$death6,\tknight_die7\t] {};\nvoid()\tknight_die7\t=[\t$death7,\tknight_die8\t] {};\nvoid()\tknight_die8\t=[\t$death8,\tknight_die9\t] {};\nvoid()\tknight_die9\t=[\t$death9,\tknight_die10] {};\nvoid()\tknight_die10=[\t$death10,\tknight_die10] {};\n\n\nvoid()\tknight_dieb1\t=[\t$deathb1,\tknight_dieb2\t] {};\nvoid()\tknight_dieb2\t=[\t$deathb2,\tknight_dieb3\t] {};\nvoid()\tknight_dieb3\t=[\t$deathb3,\tknight_dieb4\t] \t\n{self.solid = SOLID_NOT;};\nvoid()\tknight_dieb4\t=[\t$deathb4,\tknight_dieb5\t] {};\nvoid()\tknight_dieb5\t=[\t$deathb5,\tknight_dieb6\t] {};\nvoid()\tknight_dieb6\t=[\t$deathb6,\tknight_dieb7\t] {};\nvoid()\tknight_dieb7\t=[\t$deathb7,\tknight_dieb8\t] {};\nvoid()\tknight_dieb8\t=[\t$deathb8,\tknight_dieb9\t] {};\nvoid()\tknight_dieb9\t=[\t$deathb9,\tknight_dieb10\t] {};\nvoid()\tknight_dieb10 \t=[\t$deathb10,\tknight_dieb11\t] {};\nvoid()\tknight_dieb11 \t=[\t$deathb11,\tknight_dieb11\t] {};\n\n\nvoid() knight_die =\n{\n// check for gib\n\tif (self.health < -40)\n\t{\n\t\tsound (self, CHAN_VOICE, \"player/udeath.wav\", 1, ATTN_NORM);\n\t\tThrowHead (\"progs/h_knight.mdl\", self.health);\n\t\tThrowGib (\"progs/gib1.mdl\", self.health);\n\t\tThrowGib (\"progs/gib2.mdl\", self.health);\n\t\tThrowGib (\"progs/gib3.mdl\", self.health);\n\t\treturn;\n\t}\n\n// regular death\n\tsound (self, CHAN_VOICE, \"knight/kdeath.wav\", 1, ATTN_NORM);\n\tif (random() < 0.5)\n\t\tknight_die1 ();\n\telse\n\t\tknight_dieb1 ();\n};\n\nvoid() monster_knight =\n{\n\tif (deathmatch)\n\t{\n\t\tremove(self);\n\t\treturn;\n\t}\n\tprecache_model (\"progs/knight.mdl\");\n\tprecache_model (\"progs/h_knight.mdl\");\n\n\tprecache_sound (\"knight/kdeath.wav\");\n\tprecache_sound (\"knight/khurt.wav\");\n\tprecache_sound (\"knight/ksight.wav\");\n\tprecache_sound (\"knight/sword1.wav\");\n\tprecache_sound (\"knight/sword2.wav\");\n\tprecache_sound (\"knight/idle.wav\");\n\n\tself.solid = SOLID_SLIDEBOX;\n\tself.movetype = MOVETYPE_STEP;\n\n\tsetmodel (self, \"progs/knight.mdl\");\n\n\tsetsize (self, '-16 -16 -24', '16 16 40');\n\tself.health = 75;\n\n\tself.th_stand = knight_stand1;\n\tself.th_walk = knight_walk1;\n\tself.th_run = knight_run1;\n\tself.th_melee = knight_atk1;\n\tself.th_pain = knight_pain;\n\tself.th_die = knight_die;\n\tself.armornoise = \"weapons/ric1.wav\";\n\n\twalkmonster_start ();\n};\n"
  },
  {
    "path": "quakec/fallout2/menus.qc",
    "content": "/*\n\n\treturn (\"\nweaponry\\n\\n\n1 melee          \\n\n2 thrown         \\n\n3 pistols&smgs   \\n\n4 shotguns       \\n\n5 rifles         \\n\n6 heavy guns     \\n\ne leave          \n*/\n\n/*\n1 1911a1     .45   01       5$\\n\n2 d. eagle   .44   02       7$\\n\n3 mk23 socom .45   02       9$\\n\n4 h&k mp7    4mm   03      14$\\n\n5 h&k mp5    9mm   03      17$\\n\n6 alien blaster    02      21$\\n\n\n1 pipe rifle .44   02       4$\\n\n2 winchester 12g   03       8$\\n\n3 mossberg   12g   04      14$\\n\n4 citykiller 12g   05      35$\\n\n\n1 rangemaster 7mm  03      11$\\n\n2 ak-112      5mm  04      21$\\n\n3 remington  .308  05      24$\\n\n4 ak-74       5mm  04      27$\\n\n5 moonlight  .223  05      36$\\n\n6 sa-80       5mm  05      23$\\n\n7 plasma rifle     07      41$\\n\n8 gauss rifle 2mm  08      51$\\n\n*/\n\nstring () ShopString =\n{\n\treturn (\"--- S H O P -------\\n\"\n\t\t\"\\n\"\n\t\t\"1 traits         \\n\"\n\t\t\"2 perks          \\n\"\n\t\t\"3 body armour    \\n\"\n\t\t\"4 protection     \\n\"\n\t\t\"5 weapons        \\n\"\n\t\t\"6 equipment      \\n\"\n\t\t\"7 chems          \\n\"\n\t\t\"8 special        \\n\"\n\t\t\"e leave          \\n\");\n};\n\nstring () WeaponString =\n{\n\treturn (\"weaponry\\n\"\n\t\t\"\\n\"\n\t\t\"1 melee          \\n\"\n\t\t\"2 thrown         \\n\"\n\t\t\"3 pistols&smgs   \\n\"\n\t\t\"4 shotguns       \\n\"\n\t\t\"5 rifles         \\n\"\n\t\t\"6 heavy guns     \\n\"\n\t\t\"e leave          \\n\");\n};\n\nstring () TraitString =\n{\n\treturn (\"traits\\n\"\n\t\t\"\\n\"\n\t\t\"1 one handed     \\n\"\n\t\t\"2 small frame    \\n\"\n\t\t\"3 bruiser        \\n\"\n\t\t\"4 heavy handed   \\n\"\n\t\t\"5 bloody mess    \\n\"\n\t\t\"6 bad luck       \\n\"\n\t\t\"e leave          \\n\");\n};\n\nstring () ThrownString =\n{\n\treturn (\"grenades\\n\"\n\t\t\"\tGRENADE | COST\t\t  \\n\"\n\t\t\"\\n\"\n\t\t\"1 smoke grenade             3$\\n\"\n\t\t\"2 frag grenade              4$\\n\"\n\t\t\"3 emp grenade               5$\\n\"\n\t\t\"4 flashbang                 7$\\n\"\n\t\t\"e exit                        \\n\");\n};\n\nstring () BuildString =\n{\n\treturn (\"BUILD A STRUCTURE\\n\"\n\t\t\"      NAME | UPGRADE |   SCRAPS\\n\"\n\t\t\"\\n\"\n\t\t\"1 Mr. Ammo                  4\\n\"\n\t\t\"2 Barricade                 6\\n\"\n\t\t\"3 AutoDoc(tm)              10\\n\"\n\t\t\"4 Robo-Fang                11\\n\"\n\t\t\"e exit                       \\n\");\n};\n\nstring () HelmetString =\n{\n\treturn (\"helmets\\n\"\n\t\t\"protect you from headshots so\\n\"\n\t\t\"make sure you use a decent one\\n\"\n\t\t\"\\n\"\n\t\t\"                  ABS% WT DEFLECT PRC\\n\"\n\t\t\"1 combat helm   -10% 01    5%    05\\n\"\n\t\t\"2 combat helm 2  +0% 02    5%    20\\n\"\n\t\t\"3 heavy-duty    +20% 03    5%    20\\n\"\n\t\t\"4 ceramic helm  -20% ...\");\n};\n\nstring () ArmorString =\n{\n\treturn (\"body armour           wt  abs  prc\\n\"\n\t\t\"\\n\"\n\t\t\"1 light kevlar      03 1/20% 03$\\n\"\n\t\t\"2 leather armor     05 2/30% 08$\\n\"\n\t\t\"3 kevlar armor      09 3/35% 10$\\n\"\n\t\t\"4 metal armor       15 5/35% 12$\\n\"\n\t\t\"5 combat armor      12 4/40% 25$\\n\"\n\t\t\"6 brotherhood armor 17 5/45% 35$\\n\"\n\t\t\"7 force armor       06 7/10% 45$\\n\"\n\t\t\"8 metal armor mkii  20 8/50% 55$\\n\");\n};\n\nstring () PerkString =\n{\n\treturn (\"perks\\n\"\n\t\t\"  ABILITY | FRAGS NEEDED \\n\"\n\t\t\"\\n\"\n\t\t\"1 bonus movement       2\\n\"\n\t\t\"2 strong back          2\\n\"\n\t\t\"3 quick pockets        2\\n\"\n\t\t\"4 awareness            2\\n\"\n\t\t\"5 silent running       3\\n\"\n\t\t\"6 better criticals     3\\n\"\n\t\t\"7 bonus ranged damage  3\\n\"\n\t\t\"8 divine favor         3\\n\"\n\t\t\"9 slayer               3\\n\"\n\t\t\"0 sharpshooter         4\\n\");\n};\n\nstring () ProtectString =\n{\n\treturn (\"++ high-tech protective devices ++ \\n\"\n\t\t\"\\n\"\n\t\t\"  HARDWARE | SHIELDS VS |  PRICE   \\n\"\n\t\t\"1 energy amulet  |damage: 7% 15\\n\"\n\t\t\"2 force field    |front: 15% 20\\n\"\n\t\t\"3 safety ring    |absorb: 3  35\\n\"\n\t\t\"4 smokescreen    |obscures   40\\n\"\n\t\t\"5 sentient cube  |regenerate 45\\n\");\n};\n\n\n/*return (\"++ high-tech protective devices ++ \\n\\n\n  HARDWARE | SHIELDS VS |  PRICE   \\n\n1 energy amulet  |damage: 7% 15\\n\n2 force field    |front: 15% 20\\n\n3 safety ring    |absorb: 3  35\\n\n4 smokescreen    |obscures   40\\n\n5 sentient cube  |regenerate 45\\n\");*/\n\n\nstring () OtherString =\n{\n\treturn\n\t\t\"++ miscellaneous items ++\\n\"\n\t\t\"  ITEM   |   CLASS   |   PRICE     \\n\"\n\t\t\"\\n\"\n\t\t\"\\n\"\n\t\t\"1 (25) bandages for medic      2$\\n\"\n\t\t\"3 (20) mr.ammo                20$\\n\"\n\t\t\"4 (20) auto-doc               20$\\n\"\n\t\t\"5 (20) shield-gen             20$\\n\"\n\t\t\"6 (20) tesla-turret           20$\\n\"\n\t\t;\n};\n\n\nstring () MeleeString =\n{\n\treturn (\"MELEE WEAPONS\\n\"\n\t\t\"WEAPON | TYPE | WT | PRICE   \\n\"\n\t\t\"\\n\"\n\t\t\"1 knife        melee 01 01$\\n\"\n\t\t\"2 hand axe     melee 08 03$\\n\"\n\t\t\"3 vibroblade   melee 04 10$\\n\"\n\t\t\"4 power axe    melee 07 15$\\n\"\n\t\t\"e exit                     \\n\");\n};\n\n/*\n6 h&k mp5   9mmP   03      17$\\n\n7 h&k mp7  4.60mm  03      14$\\n\n8 fn p90   5.57mm  03      22$\\n\n9 h&k mp10  10mm   03      24$\\n\n0 thompson  .45    03      20$\\n\n\n\n6 h&k mp5   9mmP   03      17$\\n7 h&k mp7  4.60mm  03      14$\\n8 fn p90   5.57mm  03      22$\\n9 h&k mp10  10mm   03      24$\\n0 thompson  .45    03      20$\\n\n*/\n\nstring () PistolString =\n{\n\treturn (\"Pistols and Submachineguns\\n\"\n\t\t\"    WEAPON | CAL | WEIGHT | PRICE \\n\"\n\t\t\"\\n\"\n\t\t\"1 mk23 socom .45   01       5$\\n\"\n\t\t\"2 d. eagle   .44   02       7$\\n\"\n\t\t\"3 needler pistol   02       9$\\n\"\n\t\t\"4 h&k mp7    4mm   03      14$\\n\"\n\t\t\"5 grease gun 9mm   03      17$\\n\"\n\t\t\"6 alien blaster    02      21$\\n\");\n};\n\nstring () ShotgunString =\n{\n\treturn (\"shotguns\\n\"\n\t\t\"    WEAPON | TYPE | WEIGHT | PRICE \\n\"\n\t\t\"\\n\"\n\t\t\"1 pipe rifle .44   02       4$\\n\"\n\t\t\"2 winchester 12g   03       8$\\n\"\n\t\t\"3 mossberg   12g   04      14$\\n\"\n\t\t\"4 citykiller 12g   05      35$\\n\");\n};\n\n/*\n6 dks-1      .338   bolt 08 32$\\n\n7 moonlight  .223   auto 06 54$\\n\n8 xl70e3      5mm   auto 08 27$\\n\n9 fn-fal     .308   auto 04 51$\\n\n0 sa-80      .223   auto 07 45$\\n\n\n*/\nstring () RifleString =\n{\n\treturn (\"rifles\\n\"\n\t\t\"   RIFLE | TYPE | WEIGHT | PRICE \\n\"\n\t\t\"\\n\"\n\t\t\"1 rangemaster 7mm  03      11$\\n\"\n\t\t\"2 ak-112      5mm  04      21$\\n\"\n\t\t\"3 remington  .308  05      24$\\n\"\n\t\t\"4 ak-74       5mm  04      27$\\n\"\n\t\t\"5 moonlight  .223  05      36$\\n\"\n\t\t\"6 sa-80       5mm  05      32$\\n\"\n\t\t\"7 fn-fal     .308  09      25$\\n\");\n};\n\nstring () HeavyString =\n{\n\treturn (\"other weaponry\\n\"\n\t\t\"  WEAPON | TYPE | WEIGHT | PRICE \\n\"\n\t\t\"\\n\"\n\t\t\"1 rocket launcher  10      30$\\n\"\n\t\t\"2 gauss rifle      07      32$\\n\"\n\t\t\"3 laser carbine    12      45$\\n\");\n};\n\nstring () ChemString =\n{\n\treturn (\"drugs\\n     DRUG  |   EFFECTS   |  PRICE\\n\"\n\t\t\"\\n\"\n\t\t\"1 stimpack   heals 05+20     3$\\n\"\n\t\t\"2 medkit+    heals 10+50     5$\\n\"\n\t\t\"3 superstim* heals 20+60    12$\\n\"\n\t\t\"4 adrenaline*+speed/jump    10$\\n\"\n\t\t\"5 jet*       aim better     18$\\n\"\n\t\t\"e exit                         \\n\"\n\t\t\"\\n\"\n\t\t\"+ requires medic                 \\n\"\n\t\t\"* requires advanced medic        \\n\");\n};\n\nstring () ChemString2 =\n{\n\treturn (\"chems\\n     DRUG  |   EFFECTS   |  PRICE\\n\"\n\t\t\"\\n\"\n\t\t\"1 adrenaline +60 speed/jump  3$\\n\"\n\t\t\"2 stimpack   heals 40        5$\\n\"\n\t\t\"3 psycho+    +60 hp/no pain 11$\\n\"\n\t\t\"4 medkit*    heals 20+50    12$\\n\"\n\t\t\"5 berserk*   adren+psycho   12$\\n\"\n\t\t\"6 jet*       aim better     15$\\n\"\n\t\t\"e exit                         \\n\"\n\t\t\"\\n\"\n\t\t\"+ requires shaman                \\n\"\n\t\t\"* requires advanced shaman       \\n\");\n};\n\nstring () EnergyWeaponsString =\n{\n\treturn (\"high-tech weaponry\\n\"\n\t\t\"  WEAPON | TYPE | WEIGHT | PRICE \\n\"\n\t\t\"\\n\"\n\t\t\"1 [*] flash gun         semi 03 21$\\n\"\n\t\t\"2 [&] plasma rifle      semi 08 34$\\n\"\n\t\t\"3 [*] laser rifle       semi 11 40$\\n\"\n\t\t\"4 [*] laser carbine     auto 06 57$\\n\"\n\t\t\"5 [?] alien blaster     semi 02 72$\\n\");\n};\n\n//6 bozar                    14 81$\\n7 firestorm                12 97$\n\nstring () HeavyGunsString =\n{\n\treturn (\"heavy guns\\n\"\n\t\t\"    WEAPON | TYPE | WEIGHT | PRICE \\n\"\n\t\t\"\\n\"\n\t\t\"1 light support weapon     15 55$\\n\"\n\t\t\"2 rocket launcher          11 75$\\n\"\n\t\t\"3 50oc flamethrower        16 35$\\n\"\n\t\t\"4 steyr amr .50 flechette  17 72$\\n\"\n\t\t\"5 m72 gauss rifle 2mm      12 81$\\n\"\n\t\t\"6 bozar                    14 81$\\n\"\n\t\t\"7 firestorm                12 97$\");\n};\n\nstring () de_dust =\n{\n\treturn (\"             DE_DUST          \\n\"\n\t\t\"        (BOMB/DEFUSE MAP)     \\n\"\n\t\t\"\\n\"\n\t\t\"rangers have obtained two ufos\\n\"\n\t\t\"that have crash landed in the \\n\"\n\t\t\"desert. raiders must blow them\\n\"\n\t\t\"up with c4 before its too late\\n\"\n\t\t\"\\n\"\n\t\t\"(activate electronic tools and\\n\"\n\t\t\"c4 by pressing 4)         ...\");\n};\n\n/*\n4 motion sensor                 20c\\n\n5 extra magazines               20c\\n\n6 electronic tools mark ii      30c\\n\n7 climbing gear                 30c\\n\n8 remote camera                 40c\\n\n9 cooling module                50c\\n\n0 laser defense field           50c\\n\n*/\n\n\n\nstring () EquipmentString =\n{\n\treturn (\"+ special equipment +\\n\"\n\t\t\"press your c key to activate!\\n\"\n\t\t\"\\n\"\n\t\t\"1 medic's bag                   15c\\n\"\n\t\t\"2 security alarm                15c\\n\"\n\t\t\"3 remote camera                 15c\\n\"\n\t\t\"4 belt pouch                    15c\\n\"\n\t\t\"5 backpack                      15c\\n\"\n\t\t\"6 toolkit mark ii               15c\\n\"\n\t\t\"7 climbing gear                 15c\\n\"\n\t\t\"8 enhanced battery              15c\\n\"\n\t\t\"9 stealth boy                   45c\\n\"\n\t\t\"\\n\");\n};\n"
  },
  {
    "path": "quakec/fallout2/misc.qc",
    "content": "\n/*QUAKED info_null (0 0.5 0) (-4 -4 -4) (4 4 4)\nUsed as a positional target for spotlights, etc.\n*/\nvoid() info_null =\n{\n\tremove(self);\n};\n\n/*QUAKED info_notnull (0 0.5 0) (-4 -4 -4) (4 4 4)\nUsed as a positional target for lightning.\n*/\nvoid() info_notnull =\n{\n};\n\n//============================================================================\n\nfloat START_OFF = 1;\n\nvoid() light_use =\n{\n\tif (self.spawnflags & START_OFF)\n\t{\n\t\tlightstyle(self.style, \"m\");\n\t\tself.spawnflags = self.spawnflags - START_OFF;\n\t}\n\telse\n\t{\n\t\tlightstyle(self.style, \"a\");\n\t\tself.spawnflags = self.spawnflags + START_OFF;\n\t}\n};\n\n/*QUAKED light (0 1 0) (-8 -8 -8) (8 8 8) START_OFF\nNon-displayed light.\nDefault light value is 300\nDefault style is 0\nIf targeted, it will toggle between on or off.\n*/\nvoid() light =\n{\n\tif (!self.targetname)\n\t{       // inert light\n\t\tremove(self);\n\t\treturn;\n\t}\n\t\n\tif (self.style >= 32)\n\t{\n\t\tself.use = light_use;\n\t\tif (self.spawnflags & START_OFF)\n\t\t\tlightstyle(self.style, \"a\");\n\t\telse\n\t\t\tlightstyle(self.style, \"m\");\n\t}\n};\n\n/*QUAKED light_fluoro (0 1 0) (-8 -8 -8) (8 8 8) START_OFF\nNon-displayed light.\nDefault light value is 300\nDefault style is 0\nIf targeted, it will toggle between on or off.\nMakes steady fluorescent humming sound\n*/\nvoid() light_fluoro =\n{\n\tif (self.style >= 32)\n\t{\n\t\tself.use = light_use;\n\t\tif (self.spawnflags & START_OFF)\n\t\t\tlightstyle(self.style, \"a\");\n\t\telse\n\t\t\tlightstyle(self.style, \"m\");\n\t}\n\t\n\tprecache_sound (\"ambience/fl_hum1.wav\");\n\tambientsound (self.origin, \"ambience/fl_hum1.wav\", 0.5, ATTN_STATIC);\n};\n\n/*QUAKED light_fluorospark (0 1 0) (-8 -8 -8) (8 8 8)\nNon-displayed light.\nDefault light value is 300\nDefault style is 10\nMakes sparking, broken fluorescent sound\n*/\nvoid() light_fluorospark =\n{\n\tif (!self.style)\n\t\tself.style = 10;\n\n\tprecache_sound (\"ambience/buzz1.wav\");\n\tambientsound (self.origin, \"ambience/buzz1.wav\", 0.5, ATTN_STATIC);\n};\n\n/*QUAKED light_globe (0 1 0) (-8 -8 -8) (8 8 8)\nSphere globe light.\nDefault light value is 300\nDefault style is 0\n*/\nvoid() light_globe =\n{\n\tprecache_model (\"progs/s_light.spr\");\n\tsetmodel (self, \"progs/s_light.spr\");\n\tmakestatic (self);\n};\n\nvoid() FireAmbient =\n{\n\tprecache_sound (\"ambience/fire1.wav\");\n// attenuate fast\n\tambientsound (self.origin, \"ambience/fire1.wav\", 0.5, ATTN_STATIC);\n};\n\n/*QUAKED light_torch_small_walltorch (0 .5 0) (-10 -10 -20) (10 10 20)\nShort wall torch\nDefault light value is 200\nDefault style is 0\n*/\nvoid() light_torch_small_walltorch =\n{\n\tprecache_model (\"progs/flame.mdl\");\n\tsetmodel (self, \"progs/flame.mdl\");\n\tFireAmbient ();\n\tmakestatic (self);\n};\n\n/*QUAKED light_flame_large_yellow (0 1 0) (-10 -10 -12) (12 12 18)\nLarge yellow flame ball\n*/\nvoid() light_flame_large_yellow =\n{\n\tprecache_model (\"progs/flame2.mdl\");\n\tsetmodel (self, \"progs/flame2.mdl\");\n\tself.frame = 1;\n\tFireAmbient ();\n\tmakestatic (self);\n};\n\n/*QUAKED light_flame_small_yellow (0 1 0) (-8 -8 -8) (8 8 8) START_OFF\nSmall yellow flame ball\n*/\nvoid() light_flame_small_yellow =\n{\n\tprecache_model (\"progs/flame2.mdl\");\n\tsetmodel (self, \"progs/flame2.mdl\");\n\tFireAmbient ();\n\tmakestatic (self);\n};\n\n/*QUAKED light_flame_small_white (0 1 0) (-10 -10 -40) (10 10 40) START_OFF\nSmall white flame ball\n*/\nvoid() light_flame_small_white =\n{\n\tprecache_model (\"progs/flame2.mdl\");\n\tsetmodel (self, \"progs/flame2.mdl\");\n\tFireAmbient ();\n\tmakestatic (self);\n};\n\n//============================================================================\n\n\n/*QUAKED misc_fireball (0 .5 .8) (-8 -8 -8) (8 8 8)\nLava Balls\n*/\n\nvoid() fire_fly;\nvoid() fire_touch;\nvoid() misc_fireball =\n{\n\t\n\tprecache_model (\"progs/lavaball.mdl\");\n\tself.classname = \"fireball\";\n\tself.nextthink = time + (random() * 5);\n\tself.think = fire_fly;\n\tif (!self.speed)\n\t\tself.speed = 1000;\n};\n\nvoid() fire_fly =\n{\nlocal entity    fireball;\n\n\tfireball = spawn();\n\tfireball.solid = SOLID_TRIGGER;\n\tfireball.movetype = MOVETYPE_TOSS;\n\tfireball.velocity = '0 0 1000';\n\tfireball.velocity_x = (random() * 100) - 50;\n\tfireball.velocity_y = (random() * 100) - 50;\n\tfireball.velocity_z = self.speed + (random() * 200);\n\tfireball.classname = \"fireball\";\n\tsetmodel (fireball, \"progs/lavaball.mdl\");\n\tsetsize (fireball, '0 0 0', '0 0 0');\n\tsetorigin (fireball, self.origin);\n\tfireball.nextthink = time + 5;\n\tfireball.think = SUB_Remove;\n\tfireball.touch = fire_touch;\n\t\n\tself.nextthink = time + (random() * 5) + 3;\n\tself.think = fire_fly;\n};\n\n\nvoid() fire_touch =\n{\n\tT_Damage (other, self, self, 20);\n\tremove(self);\n};\n\n//============================================================================\n\n\nvoid() barrel_explode =\n{\n\tself.takedamage = DAMAGE_NO;\n\tself.classname = \"explo_box\";\n\t// did say self.owner\n\tT_RadiusDamage (self, self, 160, world, \"\");\n\tWriteByte (MSG_MULTICAST, SVC_TEMPENTITY);\n\tWriteByte (MSG_MULTICAST, TE_EXPLOSION);\n\tWriteCoord (MSG_MULTICAST, self.origin_x);\n\tWriteCoord (MSG_MULTICAST, self.origin_y);\n\tWriteCoord (MSG_MULTICAST, self.origin_z+32);\n\tmulticast (self.origin, MULTICAST_PHS);\n\tremove (self);\n};\n\n\n\n/*QUAKED misc_explobox (0 .5 .8) (0 0 0) (32 32 64)\nTESTING THING\n*/\n\nvoid() misc_explobox =\n{\n\tlocal float     oldz;\n\t\n\tself.solid = SOLID_BBOX;\n\tself.movetype = MOVETYPE_NONE;\n\tprecache_model (\"maps/b_explob.bsp\");\n\tsetmodel (self, \"maps/b_explob.bsp\");\n\tsetsize (self, '0 0 0', '32 32 64');\n\tprecache_sound (\"weapons/r_exp3.wav\");\n\tself.health = 20;\n\tself.th_die = barrel_explode;\n\tself.takedamage = DAMAGE_AIM;\n\n\tself.origin_z = self.origin_z + 2;\n\toldz = self.origin_z;\n\tdroptofloor();\n\tif (oldz - self.origin_z > 250)\n\t{\n\t\tdprint (\"item fell out of level at \");\n\t\tdprint (vtos(self.origin));\n\t\tdprint (\"\\n\");\n\t\tremove(self);\n\t}\n};\n\n\nvoid () hostage_die =\n{\n\tself.enemy.frags = (self.enemy.frags - START_OFF);\n\tself.enemy.ammo_shells = (self.enemy.ammo_shells - MULTICAST_PVS_R);\n\tif ((self.enemy.team == START_OFF))\n\t{\n\t\tblue_kill = (blue_kill + START_OFF);\n\t}\n\tif ((self.enemy.team == SILENT))\n\t{\n\t\tred_kill = (red_kill + START_OFF);\n\t}\n\tif ((self.enemy.frags <= MULTICAST_ALL))\n\t{\n\t\tself.enemy.frags = MULTICAST_ALL;\n\t}\n\tif ((self.enemy.ammo_shells <= MULTICAST_ALL))\n\t{\n\t\tself.enemy.ammo_shells = MULTICAST_ALL;\n\t}\n\tself.frame = START_OFF;\n\tif ((self.model == \"progs/hosfem.mdl\"))\n\t{\n\t\tsound (self, CHAN_VOICE, \"misc/hosdie2.wav\", START_OFF, ATTN_NORM);\n\t}\n\telse\n\t{\n\t\tsound (self, CHAN_VOICE, \"misc/hosdie1.wav\", START_OFF, ATTN_NORM);\n\t}\n\tsound (self, CHAN_BODY, \"misc/hosdown.wav\", START_OFF, ATTN_NONE);\n\thos_deatha1 ();\n\tsetsize (self, VEC_ORIGIN, VEC_ORIGIN);\n};\n\nvoid () hostage_pain =\n{\n\tif (((random () * SECRET_NO_SHOOT) < SECRET_1ST_DOWN))\n\t{\n\t\tsound (self, CHAN_VOICE, \"player/paina.wav\", START_OFF, ATTN_NORM);\n\t}\n\telse\n\t{\n\t\tsound (self, CHAN_VOICE, \"player/painb.wav\", START_OFF, ATTN_NORM);\n\t}\n\tif (((random () * SECRET_NO_SHOOT) < SECRET_1ST_DOWN))\n\t{\n\t\tsound (self, CHAN_BODY, \"player/hit1.wav\", START_OFF, ATTN_NORM);\n\t}\n\thos_run1 ();\n};\n\nvoid () hostage_think =\n{\n\tself.nextthink = (time + MULTICAST_PVS_R);\n\tself.think = hostage_think;\n\tif ((self.health <= MULTICAST_ALL))\n\t{\n\t\treturn;\n\t}\n\tif ((self.cnt == MULTICAST_ALL))\n\t{\n\t\thos_stand1 ();\n\t}\n\tif ((self.cnt == START_OFF))\n\t{\n\t\thos_run1 ();\n\t}\n};\n\nvoid () sci_think =\n{\n\tself.think = sci_think;\n\tself.nextthink = (time + 15);\n\n\tif (self.health <= 0)\n\t\treturn;\n};\n\nvoid () hostage_spawn =\n{\n\tlocal entity te;\n\tlocal float qq;\n\n\tprecache_model (\"progs/hosfem.mdl\");\n\tprecache_model (\"progs/hosguy.mdl\");\n\tprecache_sound (\"misc/hosdie1.wav\");\n\tprecache_sound (\"misc/hosdie2.wav\");\n\tprecache_sound (\"misc/hosdown.wav\");\n\tprecache_sound (\"misc/rescued.wav\");\n\tif (((random () * SECRET_NO_SHOOT) <= SILENT))\n\t{\n\t\treturn;\n\t}\n\tif (((random () * SECRET_NO_SHOOT) <= SECRET_NO_SHOOT))\n\t{\n\t\tte = find (world, classname, \"hostage\");\n\t\twhile (te)\n\t\t{\n\t\t\tqq = (qq + START_OFF);\n\t\t\tte = find (te, classname, \"hostage\");\n\t\t}\n\t\tif ((qq >= MULTICAST_PVS_R))\n\t\t{\n\t\t\tremove (self);\n\t\t\treturn;\n\t\t}\n\t}\n\thos_stand1 ();\n\tself.movetype = MOVETYPE_STEP;\n\tself.velocity = VEC_ORIGIN;\n\tself.touch = SUB_Null;\n\tself.classname = \"hostage\";\n\tself.takedamage = DAMAGE_AIM;\n\tsetsize (self, VEC_HULL_MIN, VEC_HULL_MAX);\n\tself.home = self.origin;\n\tif (((random () * SECRET_NO_SHOOT) <= SECRET_1ST_DOWN))\n\t{\n\t\tsetmodel (self, \"progs/hosfem.mdl\");\n\t}\n\telse\n\t{\n\t\tsetmodel (self, \"progs/hosguy.mdl\");\n\t}\n\tself.health = 70;\n\tself.think = hostage_think;\n\tself.nextthink = (time + START_OFF);\n\tself.solid = SOLID_BBOX;\n\tself.th_die = hostage_die;\n\tself.th_pain = hostage_pain;\n\tself.rescued = MULTICAST_ALL;\n\tself.angles_y = floor ((random () * 360));\n\tself.netname = \"citizen\";\n};\n\nvoid () scientist =\n{\n\tprecache_model (\"progs/hosguy.mdl\");\n\tprecache_sound (\"misc/hosdie1.wav\");\n\tprecache_sound (\"misc/hosdie2.wav\");\n\thos_stand1 ();\n\tself.movetype = MOVETYPE_STEP;\n\tself.velocity = VEC_ORIGIN;\n\tself.touch = SUB_Null;\n\tself.classname = \"scientist\";\n\tself.takedamage = DAMAGE_AIM;\n\tsetsize (self, VEC_HULL_MIN, VEC_HULL_MAX);\n\tsetmodel (self, \"progs/hosguy.mdl\");\n\tself.health = 70;\n\tself.think = hostage_think;\n\tself.nextthink = (time + START_OFF);\n\tself.team = START_OFF;\n\tself.solid = SOLID_BBOX;\n\tself.th_die = hostage_die;\n\tself.th_pain = hostage_pain;\n\tself.rescued = MULTICAST_ALL;\n\tself.netname = \"scientist\";\n};\n\n\n/*QUAKED misc_explobox2 (0 .5 .8) (0 0 0) (32 32 64)\nSmaller exploding box, REGISTERED ONLY\n*/\n\nvoid() misc_explobox2 =\n{\n\tlocal float     oldz;\n\t\n\tself.solid = SOLID_BBOX;\n\tself.movetype = MOVETYPE_NONE;\n\tprecache_model2 (\"maps/b_exbox2.bsp\");\n\tsetmodel (self, \"maps/b_exbox2.bsp\");\n\tsetsize (self, '0 0 0', '32 32 32');\n\tprecache_sound (\"weapons/r_exp3.wav\");\n\tself.health = 20;\n\tself.th_die = barrel_explode;\n\tself.takedamage = DAMAGE_AIM;\n\n\tself.origin_z = self.origin_z + 2;\n\toldz = self.origin_z;\n\tdroptofloor();\n\tif (oldz - self.origin_z > 250)\n\t{\n\t\tdprint (\"item fell out of level at \");\n\t\tdprint (vtos(self.origin));\n\t\tdprint (\"\\n\");\n\t\tremove(self);\n\t}\n};\n\n//============================================================================\n\nfloat SPAWNFLAG_SUPERSPIKE      = 1;\nfloat SPAWNFLAG_LASER = 2;\n\nvoid() Laser_Touch =\n{\n\tlocal vector org;\n\t\n\tif (other == self.owner)\n\t\treturn;         // don't explode on owner\n\n\tif (pointcontents(self.origin) == CONTENT_SKY)\n\t{\n\t\tremove(self);\n\t\treturn;\n\t}\n\t\n\tsound (self, CHAN_WEAPON, \"enforcer/enfstop.wav\", 1, ATTN_STATIC);\n\torg = self.origin - 8*normalize(self.velocity);\n\n\tif (other.health)\n\t{\n\t\tSpawnBlood (org, 15);\n\t\tother.deathtype = \"laser\";\n\t\tT_Damage (other, self, self.owner, 15);\n\t}\n\telse\n\t{\n\t\tWriteByte (MSG_MULTICAST, SVC_TEMPENTITY);\n\t\tWriteByte (MSG_MULTICAST, TE_GUNSHOT);\n\t\tWriteByte (MSG_MULTICAST, 5);\n\t\tWriteCoord (MSG_MULTICAST, org_x);\n\t\tWriteCoord (MSG_MULTICAST, org_y);\n\t\tWriteCoord (MSG_MULTICAST, org_z);\n\t\tmulticast (org, MULTICAST_PVS);\n\t}\n\t\n\tremove(self);   \n};\n\nvoid(vector org, vector vec) LaunchLaser =\n{\n\tif (self.classname == \"monster_enforcer\")\n\t\tsound (self, CHAN_WEAPON, \"enforcer/enfire.wav\", 1, ATTN_NORM);\n\n\tvec = normalize(vec);\n\t\n\tnewmis = spawn();\n\tnewmis.owner = self;\n\tnewmis.movetype = MOVETYPE_FLY;\n\tnewmis.solid = SOLID_BBOX;\n\tnewmis.effects = EF_DIMLIGHT;\n\n\tsetmodel (newmis, \"progs/laser.mdl\");\n\tsetsize (newmis, '0 0 0', '0 0 0');             \n\n\tsetorigin (newmis, org);\n\n\tnewmis.velocity = vec * 600;\n\tnewmis.angles = vectoangles(newmis.velocity);\n\n\tnewmis.nextthink = time + 5;\n\tnewmis.think = SUB_Remove;\n\tnewmis.touch = Laser_Touch;\n};\n\nvoid() spikeshooter_use =\n{\n\tif (self.spawnflags & SPAWNFLAG_LASER)\n\t{\n\t\tsound (self, CHAN_VOICE, \"enforcer/enfire.wav\", 1, ATTN_NORM);\n\t\tLaunchLaser (self.origin, self.movedir);\n\t}\n\telse\n\t{\n\t\tsound (self, CHAN_VOICE, \"weapons/spike2.wav\", 1, ATTN_NORM);\n\t\tlaunch_spike (self.origin, self.movedir);\n\t\tnewmis.velocity = self.movedir * 500;\n\t\tif (self.spawnflags & SPAWNFLAG_SUPERSPIKE)\n\t\t\tnewmis.touch = superspike_touch;\n\t}\n};\n\nvoid() shooter_think =\n{\n\tspikeshooter_use ();\n\tself.nextthink = time + self.wait;\n\tnewmis.velocity = self.movedir * 500;\n};\n\n\n/*QUAKED trap_spikeshooter (0 .5 .8) (-8 -8 -8) (8 8 8) superspike laser\nWhen triggered, fires a spike in the direction set in QuakeEd.\nLaser is only for REGISTERED.\n*/\n\nvoid() trap_spikeshooter =\n{\n\tSetMovedir ();\n\tself.use = spikeshooter_use;\n\tif (self.spawnflags & SPAWNFLAG_LASER)\n\t{\n\t\tprecache_model2 (\"progs/laser.mdl\");\n\t\t\n\t\tprecache_sound2 (\"enforcer/enfire.wav\");\n\t\tprecache_sound2 (\"enforcer/enfstop.wav\");\n\t}\n\telse\n\t\tprecache_sound (\"weapons/spike2.wav\");\n};\n\n\n/*QUAKED trap_shooter (0 .5 .8) (-8 -8 -8) (8 8 8) superspike laser\nContinuously fires spikes.\n\"wait\" time between spike (1.0 default)\n\"nextthink\" delay before firing first spike, so multiple shooters can be stagered.\n*/\nvoid() trap_shooter =\n{\n\ttrap_spikeshooter ();\n\t\n\tif (self.wait == 0)\n\t\tself.wait = 1;\n\tself.nextthink = self.nextthink + self.wait + self.ltime;\n\tself.think = shooter_think;\n};\n\n\n\n/*\n===============================================================================\n\n\n===============================================================================\n*/\n\n\nvoid() make_bubbles;\nvoid() bubble_remove;\nvoid() bubble_bob;\n\n/*QUAKED air_bubbles (0 .5 .8) (-8 -8 -8) (8 8 8)\n\ntesting air bubbles\n*/\n\nvoid() air_bubbles =\n{\n\tremove (self);\n};\n\nvoid() make_bubbles =\n{\nlocal entity    bubble;\n\n\tbubble = spawn();\n\tsetmodel (bubble, \"progs/s_bubble.spr\");\n\tsetorigin (bubble, self.origin);\n\tbubble.movetype = MOVETYPE_NOCLIP;\n\tbubble.solid = SOLID_NOT;\n\tbubble.velocity = '0 0 15';\n\tbubble.nextthink = time + 0.5;\n\tbubble.think = bubble_bob;\n\tbubble.touch = bubble_remove;\n\tbubble.classname = \"bubble\";\n\tbubble.frame = 0;\n\tbubble.cnt = 0;\n\tsetsize (bubble, '-8 -8 -8', '8 8 8');\n\tself.nextthink = time + random() + 0.5;\n\tself.think = make_bubbles;\n};\n\nvoid() bubble_split =\n{\nlocal entity    bubble;\n\tbubble = spawn();\n\tsetmodel (bubble, \"progs/s_bubble.spr\");\n\tsetorigin (bubble, self.origin);\n\tbubble.movetype = MOVETYPE_NOCLIP;\n\tbubble.solid = SOLID_NOT;\n\tbubble.velocity = self.velocity;\n\tbubble.nextthink = time + 0.5;\n\tbubble.think = bubble_bob;\n\tbubble.touch = bubble_remove;\n\tbubble.classname = \"bubble\";\n\tbubble.frame = 1;\n\tbubble.cnt = 10;\n\tsetsize (bubble, '-8 -8 -8', '8 8 8');\n\tself.frame = 1;\n\tself.cnt = 10;\n\tif (self.waterlevel != 3)\n\t\tremove (self);\n};\n\nvoid() bubble_remove =\n{\n\tif (other.classname == self.classname)\n\t{\n//              dprint (\"bump\");\n\t\treturn;\n\t}\n\tremove(self);\n};\n\nvoid() bubble_bob =\n{\nlocal float             rnd1, rnd2, rnd3;\n\n\tself.cnt = self.cnt + 1;\n\tif (self.cnt == 4)\n\t\tbubble_split();\n\tif (self.cnt == 20)\n\t\tremove(self);\n\n\trnd1 = self.velocity_x + (-10 + (random() * 20));\n\trnd2 = self.velocity_y + (-10 + (random() * 20));\n\trnd3 = self.velocity_z + 10 + random() * 10;\n\n\tif (rnd1 > 10)\n\t\trnd1 = 5;\n\tif (rnd1 < -10)\n\t\trnd1 = -5;\n\t\t\n\tif (rnd2 > 10)\n\t\trnd2 = 5;\n\tif (rnd2 < -10)\n\t\trnd2 = -5;\n\t\t\n\tif (rnd3 < 10)\n\t\trnd3 = 15;\n\tif (rnd3 > 30)\n\t\trnd3 = 25;\n\t\n\tself.velocity_x = rnd1;\n\tself.velocity_y = rnd2;\n\tself.velocity_z = rnd3;\n\t\t\n\tself.nextthink = time + 0.5;\n\tself.think = bubble_bob;\n};\n\n/*~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>\n~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~*/\n\n/*QUAKED viewthing (0 .5 .8) (-8 -8 -8) (8 8 8)\n\nJust for the debugging level.  Don't use\n*/\n\nvoid() viewthing =\n\n{\n\tself.movetype = MOVETYPE_NONE;\n\tself.solid = SOLID_NOT;\n\tprecache_model (\"progs/player.mdl\");\n\tsetmodel (self, \"progs/player.mdl\");\n};\n\n\n/*\n==============================================================================\n\nSIMPLE BMODELS\n\n==============================================================================\n*/\n\nvoid() func_wall_use =\n{       // change to alternate textures\n\tself.frame = 1 - self.frame;\n};\n\n/*QUAKED func_wall (0 .5 .8) ?\nThis is just a solid wall if not inhibitted\n*/\nvoid() func_wall =\n{\n\tself.angles = '0 0 0';\n\tself.movetype = MOVETYPE_PUSH;  // so it doesn't get pushed by anything\n\tself.solid = SOLID_BSP;\n\tself.use = func_wall_use;\n\tsetmodel (self, self.model);\n};\n\n\n/*QUAKED func_illusionary (0 .5 .8) ?\nA simple entity that looks solid but lets you walk through it.\n*/\nvoid() func_illusionary =\n\n{\n\tself.angles = '0 0 0';\n\tself.movetype = MOVETYPE_NONE;\n\tself.solid = SOLID_NOT;\n\tsetmodel (self, self.model);\n\tmakestatic (self);\n};\n\n/*QUAKED func_episodegate (0 .5 .8) ? E1 E2 E3 E4\nThis bmodel will appear if the episode has allready been completed, so players can't reenter it.\n*/\nvoid() func_episodegate =\n\n{\n\tif (!(serverflags & self.spawnflags))\n\t\treturn;                 // can still enter episode\n\n\tself.angles = '0 0 0';\n\tself.movetype = MOVETYPE_PUSH;  // so it doesn't get pushed by anything\n\tself.solid = SOLID_BSP;\n\tself.use = func_wall_use;\n\tsetmodel (self, self.model);\n};\n\n/*QUAKED func_bossgate (0 .5 .8) ?\nThis bmodel appears unless players have all of the episode sigils.\n*/\nvoid() func_bossgate =\n\n{\n\tif ( (serverflags & 15) == 15)\n\t\treturn;         // all episodes completed\n\tself.angles = '0 0 0';\n\tself.movetype = MOVETYPE_PUSH;  // so it doesn't get pushed by anything\n\tself.solid = SOLID_BSP;\n\tself.use = func_wall_use;\n\tsetmodel (self, self.model);\n};\n\n//============================================================================\n/*QUAKED ambient_suck_wind (0.3 0.1 0.6) (-10 -10 -8) (10 10 8)\n*/\nvoid() ambient_suck_wind =\n{\n\tprecache_sound (\"ambience/suck1.wav\");\n\tambientsound (self.origin, \"ambience/suck1.wav\", 1, ATTN_STATIC);\n};\n\n/*QUAKED ambient_drone (0.3 0.1 0.6) (-10 -10 -8) (10 10 8)\n*/\nvoid() ambient_drone =\n{\n\tprecache_sound (\"ambience/drone6.wav\");\n\tambientsound (self.origin, \"ambience/drone6.wav\", 0.5, ATTN_STATIC);\n};\n\n/*QUAKED ambient_flouro_buzz (0.3 0.1 0.6) (-10 -10 -8) (10 10 8)\n*/\nvoid() ambient_flouro_buzz =\n{\n\tprecache_sound (\"ambience/buzz1.wav\");\n\tambientsound (self.origin, \"ambience/buzz1.wav\", 1, ATTN_STATIC);\n};\n/*QUAKED ambient_drip (0.3 0.1 0.6) (-10 -10 -8) (10 10 8)\n*/\nvoid() ambient_drip =\n{\n\tprecache_sound (\"ambience/drip1.wav\");\n\tambientsound (self.origin, \"ambience/drip1.wav\", 0.5, ATTN_STATIC);\n};\n/*QUAKED ambient_comp_hum (0.3 0.1 0.6) (-10 -10 -8) (10 10 8)\n*/\nvoid() ambient_comp_hum =\n{\n\tprecache_sound (\"ambience/comp1.wav\");\n\tambientsound (self.origin, \"ambience/comp1.wav\", 1, ATTN_STATIC);\n};\n/*QUAKED ambient_thunder (0.3 0.1 0.6) (-10 -10 -8) (10 10 8)\n*/\nvoid() ambient_thunder =\n{\n\tprecache_sound (\"ambience/thunder1.wav\");\n\tambientsound (self.origin, \"ambience/thunder1.wav\", 0.5, ATTN_STATIC);\n};\n/*QUAKED ambient_light_buzz (0.3 0.1 0.6) (-10 -10 -8) (10 10 8)\n*/\nvoid() ambient_light_buzz =\n{\n\tprecache_sound (\"ambience/fl_hum1.wav\");\n\tambientsound (self.origin, \"ambience/fl_hum1.wav\", 0.5, ATTN_STATIC);\n};\n/*QUAKED ambient_swamp1 (0.3 0.1 0.6) (-10 -10 -8) (10 10 8)\n*/\nvoid() ambient_swamp1 =\n{\n\tprecache_sound (\"ambience/swamp1.wav\");\n\tambientsound (self.origin, \"ambience/swamp1.wav\", 0.5, ATTN_STATIC);\n};\n/*QUAKED ambient_swamp2 (0.3 0.1 0.6) (-10 -10 -8) (10 10 8)\n*/\nvoid() ambient_swamp2 =\n{\n\tprecache_sound (\"ambience/swamp2.wav\");\n\tambientsound (self.origin, \"ambience/swamp2.wav\", 0.5, ATTN_STATIC);\n};\n\n//============================================================================\n\nvoid() noise_think =\n{\n\tself.nextthink = time + 0.5;\n\tsound (self, 1, \"enforcer/enfire.wav\", 1, ATTN_NORM);\n\tsound (self, 2, \"enforcer/enfstop.wav\", 1, ATTN_NORM);\n\tsound (self, 3, \"enforcer/sight1.wav\", 1, ATTN_NORM);\n\tsound (self, 4, \"enforcer/sight2.wav\", 1, ATTN_NORM);\n\tsound (self, 5, \"enforcer/sight3.wav\", 1, ATTN_NORM);\n\tsound (self, 6, \"enforcer/sight4.wav\", 1, ATTN_NORM);\n\tsound (self, 7, \"enforcer/pain1.wav\", 1, ATTN_NORM);\n};\n\n/*QUAKED misc_noisemaker (1 0.5 0) (-10 -10 -10) (10 10 10)\n\nFor optimzation testing, starts a lot of sounds.\n*/\n\nvoid() misc_noisemaker =\n\n{\n\tprecache_sound2 (\"enforcer/enfire.wav\");\n\tprecache_sound2 (\"enforcer/enfstop.wav\");\n\tprecache_sound2 (\"enforcer/sight1.wav\");\n\tprecache_sound2 (\"enforcer/sight2.wav\");\n\tprecache_sound2 (\"enforcer/sight3.wav\");\n\tprecache_sound2 (\"enforcer/sight4.wav\");\n\tprecache_sound2 (\"enforcer/pain1.wav\");\n\tprecache_sound2 (\"enforcer/pain2.wav\");\n\tprecache_sound2 (\"enforcer/death1.wav\");\n\tprecache_sound2 (\"enforcer/idle1.wav\");\n\n\tself.nextthink = time + 0.1 + random();\n\tself.think = noise_think;\n};\n"
  },
  {
    "path": "quakec/fallout2/mod_buy.qc",
    "content": "\nvoid(entity to, float iid, float amount) AddNonStackable =\n{\n\tlocal float slot;\n\n\tslot = SlotOfItem(to, iid);\n\tif (!slot)\n\t\tslot = FindSuitableEmptySlot(to, iid);\n\tif (slot)\n\t\tSetItemSlot(to, slot, SlotVal(iid, amount));\n};\n\nvoid(entity to, float iid, float amount) AddStackable =\n{\n\tlocal float slot;\n\n\tslot = FindSuitableEmptySlot(to, iid);\n\tif (slot)\n\t\tSetItemSlot(to, slot, SlotVal(iid, amount + ToStatus(ItemInSlot(to, slot))));\n}\n\nvoid(float cost, float iid) BuyStackable =\n{\n\tlocal string z;\n\n\tif (self.ammo_shells < cost)\n\t{\n\t\tself.currentmenu = \"none\";\n\t\tsprint(self, PRINT_HIGH, \"not enough money.\\n\");\n\t\treturn;\n\t}\n\n\tsound (self, CHAN_BODY, \"misc/item1.wav\", 1, ATTN_NORM);\n\tself.ammo_shells = self.ammo_shells - cost;\n\n\tsprint(self, PRINT_HIGH, \"you bought a \");\n\n\tz = GetItemName(iid);\n\tsprint(self, PRINT_HIGH, z);\n\tsprint(self, PRINT_HIGH, \"\\n\");\n\n\t//put grenade in inventory\n\t//finds existing grenades, otherwise, empty slot\n\n\tTryGiveStackable(self, iid, 1);\n};\n\nvoid() BuyBandages =\n{\n\tlocal string y;\n\n\tif (self.ammo_shells < 2)\n\t{\n\t\tself.currentmenu = \"none\";\n\t\tsprint(self, PRINT_HIGH, \"not enough money.\\n\");\n\t\treturn;\n\t}\n\tif (self.class != 2)\n\t{\n\t\tself.currentmenu = \"none\";\n\t\tsprint(self, PRINT_HIGH, \"not a medic.\\n\");\n\t\treturn;\n\t}\n\tif (self.bandages >= 50)\n\t{\n\t\tself.currentmenu = \"none\";\n\t\tsprint(self, PRINT_HIGH, \"too many bandages.\\n\");\n\t\treturn;\n\t}\n\n\tsound (self, CHAN_BODY, \"misc/item1.wav\", 1, ATTN_NORM);\n\tself.bandages = self.bandages + 25;\n\tself.ammo_shells = self.ammo_shells - 2;\n\tif (self.bandages > 50)\n\t\tself.bandages = 50;\n\ty = ftos(self.bandages);\n\tsprint(self, PRINT_HIGH, \"you now have \");\n\tsprint(self, PRINT_HIGH, y);\n\tsprint(self, PRINT_HIGH, \"/50 bandages.\\n\");\t\n};\n\nvoid(float cost, float type) BuyChem =\n{\n\tlocal string chemname;\n\tlocal float max;\n\tlocal float alreadygot;\n\n\tif (self.ammo_shells < cost)\n\t{\n\t\tself.currentmenu = \"none\";\n\t\tsprint(self, PRINT_HIGH, \"not enough money.\\n\");\n\t\treturn;\n\t}\n\tif (type != IID_CHEM_STIMPACK && self.class != 1)\n\t{\n\t\tself.currentmenu = \"none\";\n\t\tsprint(self, PRINT_HIGH, \"not a medic.\\n\");\n\t\treturn;\n\t}\n\tif (self.equipment == 1)\n\t\tmax = 4;\n\telse\n\t\tmax = 2;\n\n\tif (type == IID_CHEM_MEDICALBAG)\n\t\tmax = 50;\n\n\talreadygot = TotalQuantity(self, type);\n\tif (alreadygot >= max)\n\t{\n\t\tself.currentmenu = \"none\";\n\t\tsprint(self, PRINT_HIGH, \"you can't hold any more chems.\\n\");\n\t\treturn;\n\t}\n\n\tsound (self, CHAN_BODY, \"misc/item1.wav\", 1, ATTN_NORM);\n\tself.ammo_shells = self.ammo_shells - cost;\n\tchemname = GetItemName(type);\n\tsprint(self, PRINT_HIGH, chemname);\n\tsprint(self, PRINT_HIGH, \" purchased.\\n\");\n\tTryGiveStackable(self, type, 1);\n};\n\nvoid(string type) ChangeAmmo =\n{\n\n\tif (self.ammo_shells < 1)\n\t{\n\t\tself.currentmenu = \"none\";\n\t\tsprint(self, PRINT_HIGH, \"not enough money.\\n\");\n\t\treturn;\n\t}\n\n\t\n\tsound (self, CHAN_BODY, \"misc/item1.wav\", 1, ATTN_NORM);\n\n\tif (self.current_slot == 1)\n\t\tself.ammotype1 = type;\n\tif (self.current_slot == 2)\n\t\tself.ammotype2 = type;\n};\n\nvoid() SetWeaponModel;\n\nvoid (float wt, float cost, float wid) BuyWeapon =\n{\n\tlocal string itname;\n\tlocal float ammotype, ammocount;\n\tlocal float slotnum;\n\tlocal float curweap;\n\n\tif (self.ammo_shells < cost)\n\t{\n\t\tself.currentmenu = \"none\";\n\t\tsound (self, CHAN_BODY, \"misc/menu3.wav\", 1, ATTN_IDLE);\n\t\tsprint(self, PRINT_HIGH, \"not enough money.\\n\");\n\t\treturn;\n\t}\n\n\tsound (self, CHAN_BODY, \"misc/item1.wav\", 1, ATTN_NORM);\n\tself.ammo_shells = self.ammo_shells - cost;\n\n\tsprint(self, PRINT_HIGH, \"you bought \");\n\n\titname = GetItemName(wid);\n\tsprint(self, PRINT_HIGH, itname);\n\tsprint(self, PRINT_HIGH, \".\\n\");\n\n\t//put new weapon in current slot\n\t//put old weapon in empty slot (but not armor slot!)\n      //otherwise, drop it.\n\n\tcurweap = ItemInSlot(self, self.current_slot);\n\tslotnum = FindSuitableEmptySlot(self, ToIID(curweap));\n\n\tif (slotnum == 0)//no more room\n\t\tDropFromSlot (self.current_slot, 1, 0);\n\telse       //found a place to stick old weapon\n\t\tSetItemSlot(self, slotnum, curweap);\n\n\tammocount = WeaponMagQuant(wid);//load weapon with full ammo (may be changed)\n\tSetItemSlot(self, self.current_slot, SlotVal(wid, ammocount));\n\n\n\tammotype = WeaponAmmoType(wid);//load weapon with full ammo (may be changed)\n\tTryGiveStackable(self, ammotype, ammocount*3);\n\n\tSetWeaponModel();\n};\n\n\nvoid (float cost, float item) BuyPerk =\n{\n\tif (self.frags < cost)\n\t{\n\t\tsprint (self, PRINT_HIGH, \"not enough kills\\n\");\n\t\tself.currentmenu = \"none\";\n\t\treturn;\n\t}\n\tsprint (self, PRINT_HIGH, \"ok, ability gained.\\n\");\n\tself.perk = item;\n\tself.currentmenu = \"none\";\n\treturn;\n};\n\n/*\nvoid (float wt, float cost, float item) BuyArmor =\n{\n\n\tlocal float curr;\n\tlocal float lbs;\n\tlocal string x;\n\n\tlbs = weightx ();\n\tif (((item >= TE_LIGHTNING2) && (self.protect >= SPAWNFLAG_SUPERSPIKE)))\n\t{\n\t\tsprint (self, PRINT_HIGH, \"remove your hardware before buying\\nany kind of advanced armor!\\ntoo much interference otherwise.\");\n\t\tself.currentmenu = \"none\";\n\t\treturn;\n\t}\n\tif ((cost > self.ammo_shells))\n\t{\n\t\tsprint (self, PRINT_HIGH, \"not enough money.\\n\");\n\t\tself.currentmenu = \"none\";\n\t\treturn;\n\t}\n\tif ((((lbs + wt) - GetItemsWeight(self.islot3)) > self.max_weight))\n\t{\n\t\tsprint (self, PRINT_HIGH, \"you can't carry that much.\\n\");\n\t\tself.currentmenu = \"none\";\n\t\treturn;\n\t}\n\tsound (self, CHAN_BODY, \"misc/item2.wav\", 1, ATTN_NORM);\n\tself.ammo_shells = (self.ammo_shells - cost);\n//\tself.armor_weight = wt;\n\tself.armor = item;\n\tx = GetArmorName();\n\tsprint (self, PRINT_HIGH, x);\n\tsprint (self, PRINT_HIGH, \" purchased.\\n\");\n\n\treturn;\n\n};*/\n\nvoid (float wt, float cost, float item) BuyArmor =\n{\n\tlocal string z;\n\tlocal float x;\n\n\tif (self.ammo_shells < cost)\n\t{\n\t\tself.currentmenu = \"none\";\n\t\tsound (self, CHAN_BODY, \"misc/menu3.wav\", 1, ATTN_IDLE);\n\t\tsprint(self, PRINT_HIGH, \"not enough money.\\n\");\n\t\treturn;\n\t}\n\n\tsound (self, CHAN_BODY, \"misc/item1.wav\", 1, ATTN_NORM);\n\tself.ammo_shells = self.ammo_shells - cost;\n\n\tsprint(self, PRINT_HIGH, \"you bought \");\n\n\tz = GetItemName(item);\n\tsprint(self, PRINT_HIGH, z);\n\tsprint(self, PRINT_HIGH, \".\\n\");\n\n\t//put new armor in armor slot\n\t//put old armor in inventory\n\n\tx = FindEmptySlot(self);\n\n\tif (x == 0)\n\t\tsprint(self, 2, \"no more room. giving armor to merchant.\\n\");\n\n\tif (x != 0) //found a place to stick old armor\n\t\tSetItemSlot(self, x, SlotVal(self.islot3, 1));\n\n\t\tSetItemSlot(self, 3, SlotVal(item, 1));\n};\n\nvoid (float cost, float item) BuyEquipment =\n{\n\tlocal float lbs;\n\tlocal string x;\n\n\tlbs = weightx ();\n\tif ((cost > self.ammo_shells))\n\t{\n\t\tsprint (self, PRINT_HIGH, \"not enough money.\\n\");\n\t\tself.currentmenu = \"none\";\n\t\treturn;\n\t}\n\tsound (self, CHAN_BODY, \"items/item1.wav\", 1, ATTN_NORM);\n\tself.equipment = item;\n\tx = GetEquipmentName();\n\tsprint (self, PRINT_HIGH, x);\n\tsprint (self, PRINT_HIGH, \" purchased.\\n\");\n\tself.ammo_shells = (self.ammo_shells - cost);\n\treturn;\n};\n\nvoid (float cost, float item) BuyProtect =\n{\n\tlocal string x;\n\n/*\n\tif ((self.armor >= TE_LIGHTNING2))\n\t{\n\t\tsprint (self, PRINT_HIGH, \"too many electronics in\\nyour currently worn armor!\");\n\t\tself.currentmenu = \"none\";\n\t\treturn;\n\t}\n*/\n\tif ((cost > self.ammo_shells))\n\t{\n\t\tsprint (self, PRINT_HIGH, \"not enough money.\\n\");\n\t\tself.currentmenu = \"none\";\n\t\treturn;\n\t}\n\tsound (self, CHAN_BODY, \"items/item1.wav\", 1, ATTN_NORM);\n\tself.ammo_shells = (self.ammo_shells - cost);\n\tself.protect = item;\n\tx = GetProtectName();\n\tsprint (self, PRINT_HIGH, x);\n\tsprint (self, PRINT_HIGH, \" purchased.\\n\");\n\n\treturn;\n};\n\n\nfloat (float input) overweight =\n{\n\tlocal float tmp;\n\tlocal float max;\n\n\ttmp = input + self.weight;\n\tmax = self.max_weight;\n\tif (self.class == TE_LIGHTNING2)\n\t{\n\t\tmax = max + SPAWNFLAG_LASER;\n\t}\n\tif (self.perk == TE_LAVASPLASH)\n\t{\n\t\treturn (FALSE);\n\t}\n\tif (tmp > max)\n\t{\n\t\treturn (TRUE);\n\t}\n\telse\n\t{\n\t\treturn (FALSE);\n\t}\n};\n\nvoid () W_GetClass =\n{\n\tif ((self.currentmenu == \"select_skill\"))\n\t{\n\t\tsound (self, CHAN_WEAPON, \"buttons/switch02.wav\", TRUE, ATTN_NORM);\n\t\tif (self.impulse == 1)\n\t\t{\n\t\t\tself.currentmenu = \"none\";\n\t\t\tself.max_health = 80;\n\t\t\tself.class = 1;\n\t\t\tself.currentmenu = \"confirm_skill\";\n\t\t\tcenterprint (self, \"your class will be\\n\\nMedic - OK?\\n1 Yes     \\n2 No      \\n\");\n\t\t\tself.ghost = 0;\n\t\t\treturn;\n\t\t}\n\t\tif ((self.impulse == 2))\n\t\t{\n\t\t\tself.currentmenu = \"none\";\n\t\t\tself.max_health = 70;\n\t\t\tself.class = 2;\n\t\t\tself.currentmenu = \"confirm_skill\";\n\t\t\tcenterprint (self, \"your class will be\\n\\nAssassin - OK?\\n1 Yes     \\n2 No      \\n\");\n\t\t\tself.ghost = 0;\n\t\t\treturn;\n\t\t}\n\t\tif (self.impulse == 3)\n\t\t{\n\t\t\tself.currentmenu = \"none\";\n\t\t\tself.max_health = 100;\n\t\t\tself.class = 3;\n\t\t\tself.currentmenu = \"confirm_skill\";\n\t\t\tcenterprint (self, \"your class will be\\n\\nSoldiier - OK?\\n1 Yes     \\n2 No      \\n\");\n\t\t\tself.ghost = 0;\n\t\t\treturn;\n\t\t}\n\t\tif (self.impulse == 4)\n\t\t{\n\t\t\tself.max_health = 80;\n\t\t\tself.currentmenu = \"none\";\n\t\t\tself.class = 4;\n\t\t\tself.currentmenu = \"confirm_skill\";\n\t\t\tcenterprint (self, \"your class will be\\n\\nScientist - OK?\\n1 Yes     \\n2 No      \\n\");\n\t\t\tself.ghost = 0;\n\t\t\treturn;\n\t\t}\n\t}\n\tif (self.impulse > 4)\n\t\treturn;\n\n};\n\n\nvoid() W_PlayerMenu =\n{\n\tif (self.currentmenu == \"none\")\n\t\treturn;\n\n\tif (self.currentmenu == \"shop_list\")\n\t{\n\t\tif (self.impulse == 1)\n\t\t\tself.currentmenu = \"shop_trait\";\n\t\tif (self.impulse == 2)\n\t\t\tself.currentmenu = \"shop_perk\";\n\t\tif (self.impulse == 3)\n\t\t\tself.currentmenu = \"shop_armor\";\n\t\tif (self.impulse == 4)\n\t\t\tself.currentmenu = \"shop_protect\";\n\t\tif (self.impulse == 5)\n\t\t\tself.currentmenu = \"shop_weapons\";\n\t\tif (self.impulse == 6)\n\t\t\tself.currentmenu = \"shop_equipment\";\n\t\tif (self.impulse == 7)\n\t\t\tself.currentmenu = \"shop_chems\";\n\t\tif (self.impulse == 8)\n\t\t\tself.currentmenu = \"shop_other\";\n\n\t\tDisplayMenu();\n\t\treturn;\n\t}\n\n\tif (self.currentmenu == \"shop_trait\")\n\t{\n\t\tif (self.impulse == 1)\n\t\t\tself.currentmenu = \"shop_trait\";\n\t\tif (self.impulse == 2)\n\t\t\tself.currentmenu = \"shop_perk\";\n\t\tif (self.impulse == 3)\n\t\t\tself.currentmenu = \"shop_armor\";\n\t\tif (self.impulse == 4)\n\t\t\tself.currentmenu = \"shop_protect\";\n\t\tif (self.impulse == 5)\n\t\t\tself.currentmenu = \"shop_weapons\";\n\t\tif (self.impulse == 6)\n\t\t\tself.currentmenu = \"shop_equipment\";\n\t\tif (self.impulse == 7)\n\t\t\tself.currentmenu = \"shop_chems\";\n\t\tif (self.impulse == 8)\n\t\t\tself.currentmenu = \"shop_other\";\n\n\t\tDisplayMenu();\n\t\treturn;\n\t}\n\tif (self.currentmenu == \"shop_perk\")\n\t{\n\t\tif (self.impulse == 1)\n                  BuyPerk(1, 1);\n\t\tif (self.impulse == 2)\n                  BuyPerk(1, 2);\n\t\tif (self.impulse == 3)\n                  BuyPerk(1, 3);\n\t\tif (self.impulse == 4)\n                  BuyPerk(2, 4);\n\t\tif (self.impulse == 5)\n                  BuyPerk(2, 5);\n\t\tif (self.impulse == 6)\n                  BuyPerk(2, 6);\n\t\tif (self.impulse == 7)\n                  BuyPerk(2, 7);\n\t\tif (self.impulse == 8)\n                  BuyPerk(3, 8);\n\t\tif (self.impulse == 9)\n                  BuyPerk(4, 9);\n\n\t\t\treturn;\n\t}\n\tif (self.currentmenu == \"shop_armor\")\n\t{\n\t\tif (self.impulse == 1)\n                  BuyArmor(3, 03, IID_ARM_SHIRT);         //weight, cost, item\n\t\tif (self.impulse == 2)\n                  BuyArmor(7, 05, IID_ARM_LEATHER);         //weight, cost, item\n\t\tif (self.impulse == 3)\n                  BuyArmor(9, 10, IID_ARM_KEVLAR);         //weight, cost, item\n\t\tif (self.impulse == 4)\n                  BuyArmor(15, 12, IID_ARM_METAL);         //weight, cost, item\n\t\tif (self.impulse == 5)\n                  BuyArmor(12, 25, IID_ARM_COMBAT);         //weight, cost, item\n\t\tif (self.impulse == 6)\n                  BuyArmor(17, 35, IID_ARM_BROTHERHOOD);   //weight, cost, item\n\t\tif (self.impulse == 7)\n                  BuyArmor(5, 45, IID_ARM_FORCE);         //weight, cost, item\n\t\tif (self.impulse == 8)\n                  BuyArmor(20, 55, IID_ARM_LPOWER);         //weight, cost, item\n\n\t\t\treturn;\n\t}\n\tif (self.currentmenu == \"shop_protect\")\n\t{\n\t\tif (self.impulse == 1)\n                  BuyProtect(20, 1);\n\t\tif (self.impulse == 2)\n                  BuyProtect(20, 2);\n\t\tif (self.impulse == 3)\n                  BuyProtect(30, 3);\n\t\tif (self.impulse == 4)\n                  BuyProtect(30, 4);\n\t\tif (self.impulse == 5)\n                  BuyProtect(40, 5);\n\n\t\t\treturn;\n\t}\n\tif (self.currentmenu == \"shop_weapons\")\n\t{\n\t\tif (self.impulse == 1)\n\t\t\tself.currentmenu = \"shop_melee\";\n\t\tif (self.impulse == 2)\n\t\t\tself.currentmenu = \"shop_thrown\";\n\t\tif (self.impulse == 3)\n\t\t\tself.currentmenu = \"shop_pistols\";\n\t\tif (self.impulse == 4)\n\t\t\tself.currentmenu = \"shop_shotguns\";\n\t\tif (self.impulse == 5)\n\t\t\tself.currentmenu = \"shop_rifles\";\n\t\tif (self.impulse == 6)\n\t\t\tself.currentmenu = \"shop_heavy\";\n\n\t\tDisplayMenu();\n\t\treturn;\n\t}\n\tif (self.currentmenu == \"shop_melee\")\n\t{\n\t\tif (self.impulse == 1)\n                  BuyWeapon(1, 1, IID_WP_KNIFE);         //weight, cost, item\n\t\tif (self.impulse == 2)\n                  BuyWeapon(6, 3, IID_WP_AXE);         //weight, cost, item\n\t\tif (self.impulse == 3)\n                  BuyWeapon(3, 10, IID_WP_VIBROBLADE);         //weight, cost, item\n\t\tif (self.impulse == 4)\n                  BuyWeapon(10, 15, IID_WP_POWERAXE);         //weight, cost, item\n\n\t\t\treturn;\n\t}\n\tif (self.currentmenu == \"shop_thrown\")\n\t{\n\t\tif (self.impulse == 1)\n                  BuyStackable(3, IID_GREN_SMOKE);         //weight, cost, item\n\t\tif (self.impulse == 2)\n                  BuyStackable(4, IID_GREN_FRAG);         //weight, cost, item\n\t\tif (self.impulse == 3)\n                  BuyStackable(5, IID_GREN_EMP);         //weight, cost, item\n\t\tif (self.impulse == 4)\n                  BuyStackable(6, IID_GREN_FLASH);         //weight, cost, item\n\n\t\t\treturn;\n\t}\n\tif (self.currentmenu == \"shop_pistols\")\n\t{\n\t\tif (self.impulse == 1)\n                  BuyWeapon(1, 5, IID_WP_USP);         //weight, cost, item\n\t\tif (self.impulse == 2)\n                  BuyWeapon(2, 7, IID_WP_DEAGLE);         //weight, cost, item\n\t\tif (self.impulse == 3)\n                  BuyWeapon(2, 9, IID_WP_NEEDLER);         //weight, cost, item\n\t\tif (self.impulse == 4)\n                  BuyWeapon(3, 14, IID_WP_MP9);         //weight, cost, item\n\t\tif (self.impulse == 5)\n                  BuyWeapon(3, 17, IID_WP_MP7);         //weight, cost, item\n\t\tif (self.impulse == 6)\n                  BuyWeapon(2, 21, IID_WP_ALIENBLASTER);         //weight, cost, item\n\n\t\t\treturn;\n\t}\n\tif (self.currentmenu == \"shop_shotguns\")\n\t{\n\t\tif (self.impulse == 1)\n                  BuyWeapon(3, 4, IID_WP_PIPERIFLE);         //weight, cost, item\n\t\tif (self.impulse == 2)\n                  BuyWeapon(4, 8, IID_WP_WINCHESTER);         //weight, cost, item\n\t\tif (self.impulse == 3)\n                  BuyWeapon(5, 12, IID_WP_MOSSBERG);         //weight, cost, item\n\t\tif (self.impulse == 4)\n                  BuyWeapon(7, 34, IID_WP_JACKHAMMER);         //weight, cost, item\n\n\t\t\treturn;\n\t}\n\tif (self.currentmenu == \"shop_rifles\")\n\t{\n\t\tif (self.impulse == 1)\n                  BuyWeapon(3, 11, IID_WP_RANGEMASTER);    //weight, cost, item\n\t\tif (self.impulse == 2)\n                  BuyWeapon(4, 21, IID_WP_AK112);          //weight, cost, item\n\t\tif (self.impulse == 3)\n                  BuyWeapon(5, 25, IID_WP_AK74);           //weight, cost, item\n\t\tif (self.impulse == 4)\n                  BuyWeapon(6, 28, IID_WP_DKS1);           //weight, cost, item\n\t\tif (self.impulse == 5)\n                  BuyWeapon(5, 30, IID_WP_MOONLIGHT);      //weight, cost, item\n\t\tif (self.impulse == 6)\n                  BuyWeapon(4, 32, IID_WP_SA80);           //weight, cost, item\n\t\tif (self.impulse == 7)\n                  BuyWeapon(9, 25, IID_WP_FNFAL);          //weight, cost, item\n\n\t\t\treturn;\n\t}\n\tif (self.currentmenu == \"shop_heavy\")\n\t{\n\t\tif (self.impulse == 1)\n                  BuyWeapon(10, 30, IID_WP_ROCKETLAUNCHER);//weight, cost, item\n\t\tif (self.impulse == 2)\n                  BuyWeapon(7, 32, IID_WP_GAUSERIFLE);     //weight, cost, item\n\t\tif (self.impulse == 3)\n                  BuyWeapon(12, 45, IID_WP_PULSERIFLE);    //weight, cost, item\n\t}\n\n\tif (self.currentmenu == \"shop_equipment\")\n\t{\n\t\tif (self.impulse == 1)\n                  BuyEquipment(20, 1);         //cost, item\n\t\tif (self.impulse == 2)\n                  BuyEquipment(20, 2);         //cost, item\n\t\tif (self.impulse == 3)\n                  BuyEquipment(20, 3);         //cost, item\n\t\tif (self.impulse == 4)\n                  BuyEquipment(20, 4);         //cost, item\n\t\tif (self.impulse == 5)\n                  BuyEquipment(20, 5);         //cost, item\n\t\tif (self.impulse == 6)\n                  BuyEquipment(20, 6);         //cost, item\n\t\tif (self.impulse == 7)\n                  BuyEquipment(20, 7);         //cost, item\n\t\tif (self.impulse == 8)\n                  BuyEquipment(20, 8);         //cost, item\n\t\tif (self.impulse == 9)\n                  BuyEquipment(20, 9);         //cost, item\n\t\tif (self.impulse == 10)\n                  BuyEquipment(20, 10);         //cost, item\n\n\t\t\treturn;\n\t}\n\n\tif (self.currentmenu == \"shop_chems\")\n\t{\n\t\tif (self.impulse == 1)\n                  BuyChem(3, IID_CHEM_STIMPACK);         //cost, item\n\t\tif (self.impulse == 2)\n                  BuyChem(5, IID_CHEM_MEDICALBAG);         //cost, item\n\t\tif (self.impulse == 3)\n                  BuyChem(10, IID_CHEM_SUPERSTIM);         //cost, item\n\t\tif (self.impulse == 4)\n                  BuyChem(12, IID_CHEM_ADRENALINE);         //cost, item\n\t\tif (self.impulse == 5)\n                  BuyChem(18, IID_CHEM_PSYCHO);         //cost, item\n\t\tif (self.impulse == 6)\n                  BuyChem(21, IID_CHEM_BESERK);         //cost, item\n\t\treturn;\n\t}\n\n\tif (self.currentmenu == \"shop_other\")\n\t{\n\t\tif (self.impulse == 1)\n\t\t\tBuyChem(5, IID_CHEM_MEDICALBAG);\n\t\tif (self.impulse == 3)\n\t\t\tBuyStackable(20, IID_BUILD_MRAMMO);\n\t\tif (self.impulse == 4)\n\t\t\tBuyStackable(20, IID_BUILD_AUTODOC);\n\t\tif (self.impulse == 5)\n\t\t\tBuyStackable(20, IID_BUILD_SHIELDGEN);\n\t\tif (self.impulse == 6)\n\t\t\tBuyStackable(20, IID_BUILD_TTURRET);\n\t\treturn;\n\t}\n\n\n\tif (self.currentmenu == \"select_team\")\n\t{\n\t\tif (self.impulse == 1)\n\t\t{\n\t\t\tbprint(2, self.netname);\n\t\t\tbprint(2, \" has joined the rangers.\\n\");\n\t\t\tself.currentmenu = \"confirm_team\";\n\t\t\tDisplayMenu();\n\t\t\tself.team = 1;\n\t\t\treturn;\n\t\t}\n\t\tif (self.impulse == 2)\n\t\t{\n\t\t\tbprint(2, self.netname);\n\t\t\tbprint(2, \" has joined the raiders.\\n\");\n\t\t\tself.currentmenu = \"confirm_team\";\n\t\t\tDisplayMenu();\n\t\t\tself.team = 2;\n\t\t\treturn;\n\t\t}\n\n\t}\n\n\tif (self.currentmenu == \"select_skill\")\n\t{\n\t\tsound (self, CHAN_WEAPON, \"buttons/switch02.wav\", TRUE, ATTN_NORM);\n\n\t\tif (self.impulse == 1)\n\t\t{\n\t\t\tself.currentmenu = \"none\";\n\t\t\tself.max_health = 80;\n\t\t\tself.class = 1;\n\t\t\tself.currentmenu = \"confirm_skill\";\n\t\t\tcenterprint (self, \"your class will be\\n\\nMedic - OK?\\n1 Yes     \\n2 No      \\n\");\n\t\t\tself.ghost = 0;\n\t\t\treturn;\n\t\t}\n\t\tif ((self.impulse == 2))\n\t\t{\n\t\t\tself.currentmenu = \"none\";\n\t\t\tself.max_health = 70;\n\t\t\tself.class = 2;\n\t\t\tself.currentmenu = \"confirm_skill\";\n\t\t\tcenterprint (self, \"your class will be\\n\\nAssassin - OK?\\n1 Yes     \\n2 No      \\n\");\n\t\t\tself.ghost = 0;\n\t\t\treturn;\n\t\t}\n\t\tif (self.impulse == 3)\n\t\t{\n\t\t\tself.currentmenu = \"none\";\n\t\t\tself.max_health = 100;\n\t\t\tself.class = 3;\n\t\t\tself.currentmenu = \"confirm_skill\";\n\t\t\tcenterprint (self, \"your class will be\\n\\nSoldiier - OK?\\n1 Yes     \\n2 No      \\n\");\n\t\t\tself.ghost = 0;\n\t\t\treturn;\n\t\t}\n\t\tif (self.impulse == 4)\n\t\t{\n\t\t\tself.max_health = 80;\n\t\t\tself.currentmenu = \"none\";\n\t\t\tself.class = 4;\n\t\t\tself.currentmenu = \"confirm_skill\";\n\t\t\tcenterprint (self, \"your class will be\\n\\nScientist - OK?\\n1 Yes     \\n2 No      \\n\");\n\t\t\tself.ghost = 0;\n\t\t\treturn;\n\t\t}\n\t}\n\n\n\t\tif (self.currentmenu == \"confirm_team\")\n\t\t{\n\t\t\tif (self.impulse == 1)\n\t\t\t{\n\t\t\t\tsound (self, CHAN_WEAPON, \"buttons/switch02.wav\", TRUE, ATTN_NORM);\n\t\t\t\tself.currentmenu = \"select_skill\";\n\t\t\t\tDisplayMenu();\n\t\t\t\tself.impulse = 0;\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (self.impulse == 2)\n\t\t\t{\n\t\t\t\tsound (self, CHAN_WEAPON, \"buttons/switch02.wav\", TRUE, ATTN_NORM);\n\t\t\t\tself.currentmenu = \"select_team\";\n\t\t\t\tDisplayMenu();\n\t\t\t\tself.impulse = 0;\n\t\t\t\tself.team = 0;\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tif (self.currentmenu == \"confirm_skill\")\n\t\t{\n\t\t\tif (self.impulse == 1)\n\t\t\t{\n\t\t\t\tsound (self, CHAN_WEAPON, \"buttons/switch02.wav\", TRUE, ATTN_NORM);\n\t\t\t\tself.currentmenu = \"none\";\n\t\t\t\tcenterprint(self, \"\");\n\t\t\t\tPutClientInServer();\n\t\t\t\tself.impulse = 0;\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (self.impulse == 2)\n\t\t\t{\n\t\t\t\tsound (self, CHAN_WEAPON, \"buttons/switch02.wav\", TRUE, ATTN_NORM);\n\t\t\t\tself.currentmenu = \"select_skill\";\n\t\t\t\tDisplayMenu();\n\t\t\t\tself.impulse = 0;\n\t\t\t\tself.class = 0;\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n};"
  },
  {
    "path": "quakec/fallout2/mod_menus.qc",
    "content": "/*\n\n\treturn (\"\nweaponry\\n\\n\n1 melee          \\n\n2 thrown         \\n\n3 pistols&smgs   \\n\n4 shotguns       \\n\n5 rifles         \\n\n6 heavy guns     \\n\ne leave          \n*/\n\n/*\n1 1911a1     .45   01       5$\\n\n2 d. eagle   .44   02       7$\\n\n3 mk23 socom .45   02       9$\\n\n4 h&k mp7    4mm   03      14$\\n\n5 h&k mp5    9mm   03      17$\\n\n6 alien blaster    02      21$\\n\n\n1 pipe rifle .44   02       4$\\n\n2 winchester 12g   03       8$\\n\n3 mossberg   12g   04      14$\\n\n4 citykiller 12g   05      35$\\n\n\n1 rangemaster 7mm  03      11$\\n\n2 ak-112      5mm  04      21$\\n\n3 remington  .308  05      24$\\n\n4 ak-74       5mm  04      27$\\n\n5 moonlight  .223  05      36$\\n\n6 sa-80       5mm  05      23$\\n\n7 plasma rifle     07      41$\\n\n8 gauss rifle 2mm  08      51$\\n\n*/\n\nstring () ShopString =\n{\n\treturn (\"--- S H O P -------\\n\"\n\t\t\"\\n\"\n\t\t\"1 traits         \\n\"\n\t\t\"2 perks          \\n\"\n\t\t\"3 body armour    \\n\"\n\t\t\"4 protection     \\n\"\n\t\t\"5 weapons        \\n\"\n\t\t\"6 equipment      \\n\"\n\t\t\"7 chems          \\n\"\n\t\t\"e leave          \\n\");\n};\n\nstring () WeaponString =\n{\n\treturn (\"weaponry\\n\"\n\t\t\"\\n\"\n\t\t\"1 melee          \\n\"\n\t\t\"2 thrown         \\n\"\n\t\t\"3 pistols&smgs   \\n\"\n\t\t\"4 shotguns       \\n\"\n\t\t\"5 rifles         \\n\"\n\t\t\"e leave          \\n\");\n};\n\nstring () TraitString =\n{\n\treturn (\"traits\\n\"\n\t\t\"\\n\"\n\t\t\"1 one handed     \\n\"\n\t\t\"2 small frame    \\n\"\n\t\t\"3 bruiser        \\n\"\n\t\t\"4 heavy handed   \\n\"\n\t\t\"5 bloody mess    \\n\"\n\t\t\"6 bad luck       \\n\"\n\t\t\"e leave          \\n\");\n};\n\nstring () MiscString =\n{\n\treturn (\"grenades\\n\"\n\t\t\"\tGRENADE | COST\t\t  \\n\"\n\t\t\"\\n\"\n\t\t\"1 plasma grenade            11\\n\"\n\t\t\"2 frag grenade               5\\n\"\n\t\t\"3 emp grenade                3\\n\"\n\t\t\"4 smoke grenade              2\\n\"\n\t\t\"5 flash bang mark ii         2\\n\"\n\t\t\"e exit                        \\n\");\n};\n\nstring () BuildString =\n{\n\treturn (\"BUILD A STRUCTURE\\n\"\n\t\t\"      NAME | UPGRADE | COST\t  \\n\"\n\t\t\"\\n\"\n\t\t\"1 supply depot   ammo/hp     4\\n\"\n\t\t\"2 arms lab     weapons +10%  6\\n\"\n\t\t\"3 armory        armour +10% 10\\n\"\n\t\t\"4 proto-lab  gadgets/hrdwr  15\\n\"\n\t\t\"e exit                        \\n\");\n};\n\nstring () HelmetString =\n{\n\treturn (\"helmets\\nprotect you from headshots so\\n\"\n\t\t\"make sure you use a decent one\\n\"\n\t\t\"\\n\"\n\t\t\"                  ABS% WT DEFLECT PRC\\n\"\n\t\t\"1 combat helm   -10% 01    5%    05\\n\"\n\t\t\"2 combat helm 2  +0% 02    5%    20\\n\"\n\t\t\"3 heavy-duty    +20% 03    5%    20\\n\"\n\t\t\"4 ceramic helm  -20% ...\");\n};\n\nstring () ArmorString1 =\n{\n\treturn (\"body armour\\n\"\n\t\t\"                      wt  abs  prc\\n\"\n\t\t\"\\n\"\n\t\t\"1 vault suit        03 1/20% 03$\\n\"\n\t\t\"2 leather armor     07 2/30% 05$\\n\"\n\t\t\"3 kevlar armor      10 3/35% 08$\\n\"\n\t\t\"4 combat armor      13 4/40% 11$\\n\"\n\t\t\"5 brotherhood armor 17 5/45% 15$\");\n};\n\nstring () ArmorString2 =\n{\n\treturn (\"body armour\\n\"\n\t\t\"                         WT ABS SPEC PRC\\n\"\n\t\t\"\\n\"\n\t\t\"1 battle gear          08 57% NONE  50\\n\"\n\t\t\"2 battle gear ii       09 62% !EXP  60\\n\"\n\t\t\"3 high-tech suit       09 42% +ALL  60\\n\"\n\t\t\"4 carbon-fiber mesh    09 56% !ENR  70\\n\"\n\t\t\"5 pseudo-chitin suit   07...\");\n};\n\nstring () ArmorString3 =\n{\n\treturn (\"special body armour\\n\"\n\t\t\"                      WT ABS SPEC PRC\\n\"\n\t\t\"1 vsn force mail    07 %40 DFLC 95c\\n\"\n\t\t\"2 super chitin      08 %35 DFLC 95c\\n\"\n\t\t\"3 suit of mota      14 %70 HEAL 95c\\n\"\n\t\t\"0 previous                         \\n\"\n\t\t\"e exit                           ...\");\n};\n\nstring () PlusString =\n{\n\treturn (\"perks\\n\"\n\t\t\"  ABILITY | FRAGS NEEDED \\n\"\n\t\t\"\\n\"\n\t\t\"1 bonus movement       8\\n\"\n\t\t\"2 weapons handling    12\\n\"\n\t\t\"3 quick pockets       14\\n\"\n\t\t\"4 awareness           15\\n\"\n\t\t\"5 silent running      17\\n\"\n\t\t\"6 better criticals    23\\n\"\n\t\t\"7 bonus ranged damage 26\\n\"\n\t\t\"8 strong back        ...\");\n};\n\nstring () PlusString2 =\n{\n\treturn (\"perks\\n\"\n\t\t\"  ABILITY | FRAGS NEEDED \\n\"\n\t\t\"\\n\"\n\t\t\"1 bonus movement       8\\n\"\n\t\t\"2 weapons handling    12\\n\"\n\t\t\"3 quick pockets       14\\n\"\n\t\t\"4 awareness           15\\n\"\n\t\t\"5 silent running      17\\n\"\n\t\t\"6 better criticals    23\\n\"\n\t\t\"7 bonus ranged damage 26\\n\"\n\t\t\"8 strong back        ...\");\n};\n/*\n5 energy shield  |front :25% 25\n6 protect ring   |damage:15% 65\n7 dark force     |deflct:15% 75\n8 efreeti module |blasts 40% 85\n9 sentient cube  |regenerate 95\n0 vampire        |space warp 95\n*/\n\n\nstring () HardwareString =\n{\n\treturn (\"+ protective devices +\\n\\n  HARDWARE | SHIELDS VS |  PRICE   \\n1 pro cloak      |damage:07% 15\\n2 emp shielding  |emp:  100% 25\\n3 force shield   |front: 25% 20\\n4 force field    |damage:10% 25\\n5 energy shield  |energy:23% 255 energy shield  |front :25% 256 protect ring   |damage:15% 657 dark force     |deflct:15% 758 efreeti module |blasts 40% 859 sentient cube  |regenerate 950 vampire        |space warp 95\");\n};\n\nstring () MeleeString =\n{\n\treturn (\"melee weapons\\n    WEAPON | TYPE | WEIGHT | PRICE \\n\\n1 knife              melee 01 01$\\n2 combat knife       melee 03 03$\\n3 ripper             melee 03 07$\\n4 power fist         melee 04 11$\\ne exit                           \\n\");\n};\n\n/*\n6 h&k mp5   9mmP   03      17$\\n\n7 h&k mp7  4.60mm  03      14$\\n\n8 fn p90   5.57mm  03      22$\\n\n9 h&k mp10  10mm   03      24$\\n\n0 thompson  .45    03      20$\\n\n\n\n6 h&k mp5   9mmP   03      17$\\n7 h&k mp7  4.60mm  03      14$\\n8 fn p90   5.57mm  03      22$\\n9 h&k mp10  10mm   03      24$\\n0 thompson  .45    03      20$\\n\n*/\n\nstring () SmallArmString1 =\n{\n\treturn (\"Pistols and Submachineguns\\n    WEAPON | CAL | WEIGHT | PRICE \\n\\n1 1911a1     .45   01       5$\\n2 d. eagle   .44   02       7$\\n3 mk23 socom .45   02       9$\\n4 h&k mp7    4mm   03      14$\\n5 h&k mp5    9mm   03      17$\\n6 alien blaster    02      21$\\n\");\n};\n\nstring () SmallArmString2 =\n{\n\treturn (\"shotguns\\n    WEAPON | TYPE | WEIGHT | PRICE \\n\\n1 pipe rifle .44   02       4$\\n2 winchester 12g   03       8$\\n3 mossberg   12g   04      14$\\n4 citykiller 12g   05      35$\\n\");\n};\n\n/*\n6 dks-1      .338   bolt 08 32$\\n\n7 moonlight  .223   auto 06 54$\\n\n8 xl70e3      5mm   auto 08 27$\\n\n9 fn-fal     .308   auto 04 51$\\n\n0 sa-80      .223   auto 07 45$\\n\n\n*/\nstring () SmallArmString3 =\n{\n\treturn (\"rifles\\n   RIFLE | TYPE | WEIGHT | PRICE \\n\\n1 rangemaster 7mm  03      11$\\n2 ak-112      5mm  04      21$\\n3 remington  .308  05      24$\\n4 ak-74       5mm  04      27$\\n5 moonlight  .223  05      36$\\n6 sa-80       5mm  05      23$\\n7 plasma rifle     07      41$\\n8 gauss rifle 2mm  08      51$\\n\");\n};\n\nstring () DrugString1 =\n{\n\treturn (\"drugs\\n     DRUG  |   EFFECTS   |  PRICE\\n\"\n\t\t\"\\n\"\"1 stimpack   heals 40        3$\\n\"\n\t\t\"2 medkit+    heals 20+50     5$\\n\"\n\t\t\"3 superstim* heals 40+60    12$\\n\"\n\t\t\"e exit                         \\n\"\n\t\t\"\\n\"\n\t\t\"\\n\"\n\t\t\"+ requires medic                 \\n\"\n\t\t\"* requires medic with 7+ kills\");\n};\n\nstring () DrugString2 =\n{\n\treturn (\n\t\t\"drugs\\n     DRUG  |   EFFECTS   |  PRICE\\n\"\n\t\t\"\\n\"\n\t\t\"1 stimpack   heals 40        3$\\n\"\n\t\t\"2 adrenaline +60 speed/jump  7$\\n\"\n\t\t\"3 psycho+    +60 hp/no pain 15$\\n\"\n\t\t\"4 medkit*    heals 20+50    12$\\n\"\n\t\t\"5 berserk*   adren+psycho   21$\\n\"\n\t\t\"e exit                         \\n\");\n};\n\nstring () EnergyWeaponsString =\n{\n\treturn (\"high-tech weaponry\\n  WEAPON | TYPE | WEIGHT | PRICE \\n\\n1 [*] flash gun         semi 03 21$\\n2 [&] plasma rifle      semi 08 34$\\n3 [*] laser rifle       semi 11 40$\\n4 [*] laser carbine     auto 06 57$\\n5 [?] alien blaster     semi 02 72$\\n\");\n};\n\n//6 bozar                    14 81$\\n7 firestorm                12 97$\n\nstring () HeavyGunsString =\n{\n\treturn (\"heavy guns\\n    WEAPON | TYPE | WEIGHT | PRICE \\n\\n1 light support weapon     15 55$\\n2 rocket launcher          11 75$\\n3 50oc flamethrower        16 35$\\n4 steyr amr .50 flechette  17 72$\\n5 m72 gauss rifle 2mm      12 81$\\n6 bozar                    14 81$\\n7 firestorm                12 97$\");\n};\n\nstring () de_dust =\n{\n\treturn (\"             DE_DUST          \\n        (BOMB/DEFUSE MAP)     \\n\\nrangers have obtained two ufos\\nthat have crash landed in the \\ndesert. raiders must blow them\\nup with c4 before its too late\\n\\n(activate electronic tools and\\nc4 by pressing 4)         ...\");\n};\n\n/*\n4 motion sensor                 20c\\n\n5 extra magazines               20c\\n\n6 electronic tools mark ii      30c\\n\n7 climbing gear                 30c\\n\n8 remote camera                 40c\\n\n9 cooling module                50c\\n\n0 laser defense field           50c\\n\n*/\n\n\n\nstring () GadgetString =\n{\n\treturn (\"+ special equipment +\\npress your c key to activate!\\n\\n1 stealth boy                   20c\\n2 displacer cloak               20c\\n3 security alarm                20c\\n4 motion sensor                 20c\\n5 extra magazines               20c\\n6 electronic tools mark ii      30c\\n7 climbing gear                 30c\\n8 remote camera                 40c\\n9 cooling module                50c\\n0 laser defense field           50c\\n\");\n};\n"
  },
  {
    "path": "quakec/fallout2/mod_other.qc",
    "content": "\n\n\n/*\n########################################################################\nThis QC file houses all of the ugly code that no one wants to deal with.\n########################################################################\n*/\n\n/*drain_energy    -  Makes energy weapons overheat*/\n/*GetWeaponName   -  Tells the player what weapons he is carrying*/\n/*GetWeaponWeight -  Calculates the total weight for weapons*/\n\n\nvoid (vector org, entity guy, float input, float cost) spawn_station =\n{\n\tlocal entity dog;\n\tlocal entity te;\n\n\tif ((self.ammo_shells < cost))\n\t{\n\t\tsprint (self, 2, \"not enough money.\\n\");\n\t\treturn;\n\t}\n\tself = guy;\n\tte = find (world, classname, \"station\");\n\twhile (te)\n\t{\n                if (((te.track == self) && (te.skin == input)))\n\t\t{\n\t\t\tsprint (self, 2, \"already have one.\\n\");\n\t\t\treturn;\n\t\t}\n\t\tte = find (te, classname, \"station\");\n\t}\n\tmakevectors (self.v_angle);\n\torg = ((org + (v_forward * IT_LIGHTNING)) + (v_up * EF_FLAG2));\n\tif ((pointcontents ((org + '0 20 0')) != CONTENT_EMPTY))\n\t{\n\t\tsprint (self, 2, \"can't build there.\\n\");\n\t\treturn;\n\t}\n\tif ((pointcontents ((org + '0 -20 0')) != CONTENT_EMPTY))\n\t{\n\t\tsprint (self, 2, \"can't build there.\\n\");\n\t\treturn;\n\t}\n\tif ((pointcontents ((org + '20 0 0')) != CONTENT_EMPTY))\n\t{\n\t\tsprint (self, 2, \"can't build there.\\n\");\n\t\treturn;\n\t}\n\tif ((pointcontents ((org + '-20 0 0')) != CONTENT_EMPTY))\n\t{\n\t\tsprint (self, 2, \"can't build there.\\n\");\n\t\treturn;\n\t}\n\tif ((pointcontents ((org + '0 0 50')) != CONTENT_EMPTY))\n\t{\n\t\tsprint (self, 2, \"can't build there.\\n\");\n\t\treturn;\n\t}\n\tif ((pointcontents ((org + '0 0 -10')) != CONTENT_EMPTY))\n\t{\n\t\tsprint (self, 2, \"can't build there.\\n\");\n\t\treturn;\n\t}\n\tself.impulse = 0;\n\tte = findradius (org, 40);\n\twhile (te)\n\t{\n\t\tif (te.classname == \"spawn1\")\n\t\t{\n\t\t\tsprint (self, 2, \"can't build at spawn.\\n\");\n\t\t\treturn;\n\t\t}\n\t\tif (te.classname == \"spawn2\")\n\t\t{\n\t\t\tsprint (self, 2, \"can't build at spawn.\\n\");\n\t\t\treturn;\n\t\t}\n\t\tif (te.classname == \"ghoul\")\n\t\t{\n\t\t\tsprint (self, 2, \"somethings in the way.\\n\");\n\t\t\treturn;\n\t\t}\n\t\tif (((te.classname == \"player\") && (te.health > 0)))\n\t\t{\n\t\t\tsprint (self, 2, \"can't build on players.\\n\");\n\t\t\treturn;\n\t\t}\n\t\tif (((te.classname == \"station\") && (te.health > 0)))\n\t\t{\n\t\t\tsprint (self, 2, \"can't build on other stations.\\n\");\n\t\t\treturn;\n\t\t}\n\t\tte = te.chain;\n\t}\n\tself.ammo_shells = (self.ammo_shells - cost);\n\tdog = spawn ();\n\tdog.team = self.team;\n\tdog.track = self;\n\tself = dog;\n\t//spawn_dot (dog);\n\tself.origin = org;\n\tself.takedamage = DAMAGE_YES;\n\tself.solid = SOLID_SLIDEBOX;\n\tself.movetype = MOVETYPE_STEP;\n\tsetsize (self, '-16 -16 0', '16 16 40');\n\tself.health = SVC_INTERMISSION;\n\tself.max_health = (300 + (input * 50));\n\tself.th_die = station_die;\n\tsetmodel (self, \"progs/station.mdl\");\n\tself.classname = \"station\";\n\tself.think = station_think;\n\tself.helmet = 2;\n\tself.skin = input;\n\tif ((self.skin == 0))\n\t{\n\t\tself.netname = \"depot\";\n\t}\n\tif ((self.skin == 1))\n\t{\n\t\tself.netname = \"armslab\";\n\t}\n\tif ((self.skin == 2))\n\t{\n\t\tself.netname = \"armory\";\n\t}\n\tif ((self.skin == 3))\n\t{\n\t\tself.netname = \"protolab\";\n\t}\n\tself.frame = WEAPON_SPIKES;\n};\n\nvoid (float dat) drain_energy =\n{\n\tself.ammo_cells = (self.ammo_cells - dat);\n\tif ((self.ammo_cells <= 0))\n\t{\n\t\tself.recharge = 1;\n\t}\n\tif ((dat != WEAPON_SPIKES))\n\t{\n\t\tif ((self.recharge == 1))\n\t\t{\n\t\t\tcenterprint (self, \"\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\nH   TEMP    C                 \\n!COOLING!                 \\n\");\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif ((self.ammo_cells >= 200))\n\t\t\t{\n\t\t\t\treturn;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif ((self.ammo_cells >= (200 * 0.9)))\n\t\t\t\t{\n\t\t\t\t\tcenterprint (self, \"\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\nH   TEMP    C                 \\n                 \\n\");\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tif ((self.ammo_cells >= (200 * 0.8)))\n\t\t\t\t\t{\n\t\t\t\t\t\tcenterprint (self, \"\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\nH   TEMP    C                 \\n                 \\n\");\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tif ((self.ammo_cells >= (200 * 0.7)))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcenterprint (self, \"\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\nH   TEMP    C                 \\n                 \\n\");\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif ((self.ammo_cells >= (200 * 0.6)))\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tcenterprint (self, \"\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\nH   TEMP    C                 \\n                 \\n\");\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tif ((self.ammo_cells >= (200 * 0.5)))\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tcenterprint (self, \"\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\nH   TEMP    C                 \\n                 \\n\");\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tif ((self.ammo_cells >= (200 * 0.4)))\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tcenterprint (self, \"\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\nH   TEMP    C                 \\n                 \\n\");\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tif ((self.ammo_cells >= (200 * 0.3)))\n\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\tcenterprint (self, \"\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\nH   TEMP    C                 \\n                 \\n\");\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\tif ((self.ammo_cells >= (200 * 0.2)))\n\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\tcenterprint (self, \"\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\nH   TEMP    C                 \\n                 \\n\");\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\tif ((self.ammo_cells <= (200 * 0.1)))\n\t\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\t\tcenterprint (self, \"\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\nH   TEMP    C                 \\n                 \\n\");\n\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\t\tcenterprint (self, \"\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\nH   TEMP    C                 \\n                 \\n\");\n\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n};\n\t\t\t\n\nvoid (entity guy, float slot) GetWeaponWeight =\n{\n\n\treturn;\n};"
  },
  {
    "path": "quakec/fallout2/modbuy.qc",
    "content": "\n\n\nvoid(float cost, float type) BuyGrenade =\n{\n\tlocal string z;\n\tlocal float x, y;\n\n\tif (self.ammo_shells < cost)\n\t{\n\t\tself.currentmenu = \"none\";\n\t\tsprint(self, PRINT_HIGH, \"not enough money.\\n\");\n\t\treturn;\n\t}\n\n\tsound (self, CHAN_BODY, \"misc/item1.wav\", 1, ATTN_NORM);\n\tself.ammo_shells = self.ammo_shells - cost;\n\n\tsprint(self, PRINT_HIGH, \"you bought a \");\n\n\tif (type == 1)\n\t\ty = IID_GREN_SMOKE;\n\tif (type == 2)\n\t\ty = IID_GREN_FRAG;\n\tif (type == 3)\n\t\ty = IID_GREN_EMP;\n\n\tz = GetItemName(y);\n\tsprint(self, PRINT_HIGH, z);\n\tsprint(self, PRINT_HIGH, \"\\n\");\n\n\t//put grenade in inventory\n\t//finds existing grenades, otherwise, empty slot\n\n\tx = SlotOfItem(self, y);\n\n\tSlotVal(IID_GREN_SMOKE, 500)\n\n\tif (x != 0) //found existing grenades\n\t\tSetItemSlot(self, x, SlotVal(y, 1));\n\tif (x == 0)\n\t\tx = FindEmptySlot(self, y);\n\n\t\tSetItemSlot(self, x, SlotVal(y, 1));\n};\n\n\nvoid() BuyBandages =\n{\n\tlocal string y;\n\n\tif (self.ammo_shells < 2)\n\t{\n\t\tself.currentmenu = \"none\";\n\t\tsprint(self, PRINT_HIGH, \"not enough money.\\n\");\n\t\treturn;\n\t}\n\tif (self.class != 2)\n\t{\n\t\tself.currentmenu = \"none\";\n\t\tsprint(self, PRINT_HIGH, \"not a medic.\\n\");\n\t\treturn;\n\t}\n\tif (self.bandages >= 50)\n\t{\n\t\tself.currentmenu = \"none\";\n\t\tsprint(self, PRINT_HIGH, \"too many bandages.\\n\");\n\t\treturn;\n\t}\n\n\tsound (self, CHAN_BODY, \"misc/item1.wav\", 1, ATTN_NORM);\n\tself.bandages = self.bandages + 25;\n\tself.ammo_shells = self.ammo_shells - 2;\n\tif (self.bandages > 50)\n\t\tself.bandages = 50;\n\ty = ftos(self.bandages);\n\tsprint(self, PRINT_HIGH, \"you now have \");\n\tsprint(self, PRINT_HIGH, y);\n\tsprint(self, PRINT_HIGH, \"/50 bandages.\\n\");\t\n};\n\nvoid(float cost, float type) BuyChem =\n{\n\tlocal string x;\n\tlocal float y;\n\n\tif (self.ammo_shells < cost)\n\t{\n\t\tself.currentmenu = \"none\";\n\t\tsprint(self, PRINT_HIGH, \"not enough money.\\n\");\n\t\treturn;\n\t}\n\tif (type >= 2 && self.class != 1)\n\t{\n\t\tself.currentmenu = \"none\";\n\t\tsprint(self, PRINT_HIGH, \"not a medic.\\n\");\n\t\treturn;\n\t}\n\tif (self.equipment == 1)\n\t\ty = 4;\n\telse\n\t\ty = 2;\n\n\tif (self.chemcount >= y)\n\t{\n\t\tself.currentmenu = \"none\";\n\t\tsprint(self, PRINT_HIGH, \"you can't hold any more chems.\\n\");\n\t\treturn;\n\t}\n\n\tsound (self, CHAN_BODY, \"misc/item1.wav\", 1, ATTN_NORM);\n\tself.chemcount = y;\n\tself.chem = type;\n\tself.ammo_shells = self.ammo_shells - cost;\n\tx = GetChemName();\n\tsprint (self, PRINT_HIGH, x);\n\tsprint(self, PRINT_HIGH, \" purchased.\\n\");\n};\n\nvoid(string type) ChangeAmmo =\n{\n\n\tif (self.ammo_shells < 1)\n\t{\n\t\tself.currentmenu = \"none\";\n\t\tsprint(self, PRINT_HIGH, \"not enough money.\\n\");\n\t\treturn;\n\t}\n\n\t\n\tsound (self, CHAN_BODY, \"misc/item1.wav\", 1, ATTN_NORM);\n\n\tif (self.current_slot == 1)\n\t\tself.ammotype1 = type;\n\tif (self.current_slot == 2)\n\t\tself.ammotype2 = type;\n};\n\nvoid() BuyScraps =\n{\n\tlocal string y;\n\n\tif (self.ammo_shells < 5)\n\t{\n\t\tself.currentmenu = \"none\";\n\t\tsprint(self, PRINT_HIGH, \"not enough money.\\n\");\n\t\treturn;\n\t}\n\tif (self.class != 6)\n\t{\n\t\tself.currentmenu = \"none\";\n\t\tsprint(self, PRINT_HIGH, \"not a scientist.\\n\");\n\t\treturn;\n\t}\n\tif (self.scraps >= 15)\n\t{\n\t\tself.currentmenu = \"none\";\n\t\tsprint(self, PRINT_HIGH, \"too many metal scraps.\\n\");\n\t\treturn;\n\t}\n\n\tsound (self, CHAN_BODY, \"items/armor1.wav\", 1, ATTN_NORM);\n\tself.scraps = self.scraps + 5;\n\tself.ammo_shells = self.ammo_shells - 5;\n\tif (self.scraps > 15)\n\t\tself.scraps = 15;\n\ty = ftos(self.scraps);\n\tsprint(self, PRINT_HIGH, \"you now have \");\n\tsprint(self, PRINT_HIGH, y);\n\tsprint(self, PRINT_HIGH, \"/15 scraps.\\n\");\n\n};\n\n\nvoid (float wt, float cost, float item) BuyWeapon =\n{\n\tsprint (self, PRINT_HIGH, \"The shop is not open today.\\n\");\n\tself.currentmenu = \"none\";\n\tSetWeaponModel ();\n\treturn;\n\n/*\n\tlocal float lbs;\n\tlocal string qq;\n\tlocal float curr;\n\tlocal float c3;\n\tlocal string c1;\n\tlocal string c2;\n\n\tlbs = weightx ();\n\n\tif ((self.current_slot == 1))\n\t{\n\t\tcurr = self.slot1_weight;\n\n\t\tif (self.slot2 == 0)\n\t\t\tself.slot2 = self.slot1;\n\t\tif (self.slot1 != 0)\n\t\t{\n\t\t\tDropWeapon (self.slot1, 1, 0);\n\t\t\tself.slot1 = 0;\n\t\t}\n\t\tGetWeaponModel ();\n\t}\n\tif ((self.current_slot == 2))\n\t{\n\t\tcurr = self.slot2_weight;\n\n\t\tif (self.slot1 == 0)\n\t\t\tself.slot1 = self.slot2;\n\t\tif (self.slot2 != 0)\n\t\t{\n\t\t\tDropWeapon (self.slot2, 1, 0);\n\t\t\tself.slot2 = 0;\n\t\t}\n\n\t\tGetWeaponModel ();\n\t}\n\tif ((lbs + wt - curr) > self.max_weight)\n\t{\n\t\tsprint (self, PRINT_HIGH, \"you can't carry that much.\\n\");\n\t\tself.currentmenu = \"none\";\n\t\tGetWeaponModel ();\n\t\treturn;\n\t}\n\tif (cost > self.ammo_shells)\n\t{\n\t\tsprint (self, PRINT_HIGH, \"not enough money.\\n\");\n\t\tself.currentmenu = \"none\";\n\t\tGetWeaponModel ();\n\t\treturn;\n\t}\n\tself.ammo_shells = self.ammo_shells - cost;\n\tif ((self.current_slot == 1))\n\t{\n\t\tGetWeaponWeight (self, self.current_slot);\n\t\tstuffcmd (self, \"impulse 1\\n\");\n\t\tself.slot1 = item;\n\t\tself.items = (self.items | IT_SUPER_NAILGUN);\n\t\tGetWeaponModel ();\n\t}\n\tif (self.current_slot == 2)\n\t{\n\t\tGetWeaponWeight (self, self.current_slot);\n\t\tstuffcmd (self, \"impulse 2\\n\");\n\t\tself.slot2 = item;\n\t\tself.items = (self.items | IT_GRENADE_LAUNCHER);\n\t\tGetWeaponModel ();\n\t}\n\tqq = GetWeaponName (self, item);\n\tsprint (self, PRINT_HIGH, qq);\n\tsprint (self, PRINT_HIGH, \" purchased.\\n\");\n\tsound (self, CHAN_BODY, \"misc/item2.wav\", 1, ATTN_NORM);\n\tself.currentmenu = \"none\";\n\tWeaponAmmo (SPAWNFLAG_SUPERSPIKE);\n\tWeaponAmmo (SPAWNFLAG_LASER);\n\tGetWeaponWeight (self, 1);\n\tGetWeaponWeight (self, 2);\n\tGetWeaponModel ();\n\treturn;\n*/\n};\n\nvoid (float cost, float item) BuyPerk =\n{\n\tif (self.frags < cost)\n\t{\n\t\tsprint (self, PRINT_HIGH, \"not enough kills\\n\");\n\t\tself.currentmenu = \"none\";\n\t\treturn;\n\t}\n\tsprint (self, PRINT_HIGH, \"ok, ability gained.\\n\");\n\tself.perk = item;\n\tself.currentmenu = \"none\";\n\treturn;\n};\n\nvoid (float wt, float cost, float item) BuyArmor =\n{\n\tlocal float curr;\n\tlocal float lbs;\n\tlocal string x;\n\n\tlbs = weightx ();\n\tif (((item >= TE_LIGHTNING2) && (self.protect >= SPAWNFLAG_SUPERSPIKE)))\n\t{\n\t\tsprint (self, PRINT_HIGH, \"remove your hardware before buying\\nany kind of advanced armor!\\ntoo much interference otherwise.\");\n\t\tself.currentmenu = \"none\";\n\t\treturn;\n\t}\n\tif ((cost > self.ammo_shells))\n\t{\n\t\tsprint (self, PRINT_HIGH, \"not enough money.\\n\");\n\t\tself.currentmenu = \"none\";\n\t\treturn;\n\t}\n\tif ((((lbs + wt) - self.armor_weight) > self.max_weight))\n\t{\n\t\tsprint (self, PRINT_HIGH, \"you can't carry that much.\\n\");\n\t\tself.currentmenu = \"none\";\n\t\treturn;\n\t}\n\tsound (self, CHAN_BODY, \"misc/item2.wav\", 1, ATTN_NORM);\n\tself.ammo_shells = (self.ammo_shells - cost);\n\tself.armor_weight = wt;\n\tself.armor = item;\n\tx = GetArmorName();\n\tsprint (self, PRINT_HIGH, x);\n\tsprint (self, PRINT_HIGH, \" purchased.\\n\");\n\n\treturn;\n};\n\nvoid (float cost, float item) BuyEquipment =\n{\n\tlocal float lbs;\n\tlocal string x;\n\n\tlbs = weightx ();\n\tif ((cost > self.ammo_shells))\n\t{\n\t\tsprint (self, PRINT_HIGH, \"not enough money.\\n\");\n\t\tself.currentmenu = \"none\";\n\t\treturn;\n\t}\n\tsound (self, CHAN_BODY, \"items/item1.wav\", 1, ATTN_NORM);\n\tself.equipment = item;\n\tx = GetEquipmentName();\n\tsprint (self, PRINT_HIGH, x);\n\tsprint (self, PRINT_HIGH, \" purchased.\\n\");\n\tself.ammo_shells = (self.ammo_shells - cost);\n\treturn;\n};\n\nvoid (float cost, float item) BuyProtect =\n{\n\tlocal string x;\n\n\tif ((self.armor >= TE_LIGHTNING2))\n\t{\n\t\tsprint (self, PRINT_HIGH, \"too many electronics in\\nyour currently worn armor!\");\n\t\tself.currentmenu = \"none\";\n\t\treturn;\n\t}\n\tif ((cost > self.ammo_shells))\n\t{\n\t\tsprint (self, PRINT_HIGH, \"not enough money.\\n\");\n\t\tself.currentmenu = \"none\";\n\t\treturn;\n\t}\n\tsound (self, CHAN_BODY, \"items/item1.wav\", 1, ATTN_NORM);\n\tself.ammo_shells = (self.ammo_shells - cost);\n\tself.protect = item;\n\tx = GetProtectName();\n\tsprint (self, PRINT_HIGH, x);\n\tsprint (self, PRINT_HIGH, \" purchased.\\n\");\n\n\treturn;\n};\n\n\nfloat (float input) overweight =\n{\n\tlocal float tmp;\n\tlocal float max;\n\n\ttmp = ((((input) + self.armor_weight) + self.slot1_weight) + self.slot2_weight);\n\tmax = self.max_weight;\n\tif ((self.class == TE_LIGHTNING2))\n\t{\n\t\tmax = (max + SPAWNFLAG_LASER);\n\t}\n\tif ((self.perk == TE_LAVASPLASH))\n\t{\n\t\treturn (FALSE);\n\t}\n\tif ((tmp > max))\n\t{\n\t\treturn (TRUE);\n\t}\n\telse\n\t{\n\t\treturn (FALSE);\n\t}\n};\n\nfloat () weightx =\n{\n\tlocal float tmp;\n\n\ttmp = self.slot1_weight + self.slot2_weight + self.armor_weight;\n\treturn tmp;\n};\n\nvoid () W_GetClass =\n{\n\tif ((self.currentmenu == \"select_skill\"))\n\t{\n\t\tsound (self, CHAN_WEAPON, \"buttons/switch02.wav\", TRUE, ATTN_NORM);\n\t\tif (self.impulse == 1)\n\t\t{\n\t\t\tself.currentmenu = \"none\";\n\t\t\tself.max_health = 80;\n\t\t\tself.class = 1;\n\t\t\tself.currentmenu = \"confirm_skill\";\n\t\t\tcenterprint (self, \"your class will be\\n\\nMedic - OK?\\n1 Yes     \\n2 No      \\n\");\n\t\t\tself.ghost = 0;\n\t\t\treturn;\n\t\t}\n\t\tif ((self.impulse == 2))\n\t\t{\n\t\t\tself.currentmenu = \"none\";\n\t\t\tself.max_health = 70;\n\t\t\tself.class = 2;\n\t\t\tself.currentmenu = \"confirm_skill\";\n\t\t\tcenterprint (self, \"your class will be\\n\\nAssassin - OK?\\n1 Yes     \\n2 No      \\n\");\n\t\t\tself.ghost = 0;\n\t\t\treturn;\n\t\t}\n\t\tif (self.impulse == 3)\n\t\t{\n\t\t\tself.currentmenu = \"none\";\n\t\t\tself.max_health = 100;\n\t\t\tself.class = 3;\n\t\t\tself.currentmenu = \"confirm_skill\";\n\t\t\tcenterprint (self, \"your class will be\\n\\nSoldiier - OK?\\n1 Yes     \\n2 No      \\n\");\n\t\t\tself.ghost = 0;\n\t\t\treturn;\n\t\t}\n\t\tif (self.impulse == 4)\n\t\t{\n\t\t\tself.max_health = 80;\n\t\t\tself.currentmenu = \"none\";\n\t\t\tself.class = 4;\n\t\t\tself.currentmenu = \"confirm_skill\";\n\t\t\tcenterprint (self, \"your class will be\\n\\nScientist - OK?\\n1 Yes     \\n2 No      \\n\");\n\t\t\tself.ghost = 0;\n\t\t\treturn;\n\t\t}\n\t}\n\tif (self.impulse > 4)\n\t\treturn;\n\n};\n\n\nvoid() W_PlayerMenu =\n{\n\tif (self.currentmenu == \"none\")\n\t\treturn;\n\n\tif (self.currentmenu == \"shop_list\")\n\t{\n\t\tif (self.impulse == 1)\n\t\t\tself.currentmenu = \"shop_trait\";\n\t\tif (self.impulse == 2)\n\t\t\tself.currentmenu = \"shop_perk\";\n\t\tif (self.impulse == 3)\n\t\t\tself.currentmenu = \"shop_armor\";\n\t\tif (self.impulse == 4)\n\t\t\tself.currentmenu = \"shop_protect\";\n\t\tif (self.impulse == 5)\n\t\t\tself.currentmenu = \"shop_weapons\";\n\t\tif (self.impulse == 6)\n\t\t\tself.currentmenu = \"shop_equipment\";\n\t\tif (self.impulse == 7)\n\t\t\tself.currentmenu = \"shop_chems\";\n\t\tif (self.impulse == 8)\n\t\t\tself.currentmenu = \"shop_other\";\n\n\t\tDisplayMenu();\n\t\treturn;\n\t}\n\n\tif (self.currentmenu == \"shop_trait\")\n\t{\n\t\tif (self.impulse == 1)\n\t\t\tself.currentmenu = \"shop_trait\";\n\t\tif (self.impulse == 2)\n\t\t\tself.currentmenu = \"shop_perk\";\n\t\tif (self.impulse == 3)\n\t\t\tself.currentmenu = \"shop_armor\";\n\t\tif (self.impulse == 4)\n\t\t\tself.currentmenu = \"shop_protect\";\n\t\tif (self.impulse == 5)\n\t\t\tself.currentmenu = \"shop_weapons\";\n\t\tif (self.impulse == 6)\n\t\t\tself.currentmenu = \"shop_equipment\";\n\t\tif (self.impulse == 7)\n\t\t\tself.currentmenu = \"shop_chems\";\n\t\tif (self.impulse == 8)\n\t\t\tself.currentmenu = \"shop_other\";\n\n\t\tDisplayMenu();\n\t\treturn;\n\t}\n\tif (self.currentmenu == \"shop_perk\")\n\t{\n\t\tif (self.impulse == 1)\n                  BuyPerk(1, 1);\n\t\tif (self.impulse == 2)\n                  BuyPerk(1, 2);\n\t\tif (self.impulse == 3)\n                  BuyPerk(1, 3);\n\t\tif (self.impulse == 4)\n                  BuyPerk(2, 4);\n\t\tif (self.impulse == 5)\n                  BuyPerk(2, 5);\n\t\tif (self.impulse == 6)\n                  BuyPerk(2, 6);\n\t\tif (self.impulse == 7)\n                  BuyPerk(2, 7);\n\t\tif (self.impulse == 8)\n                  BuyPerk(3, 8);\n\t\tif (self.impulse == 9)\n                  BuyPerk(4, 9);\n\n\t\t\treturn;\n\t}\n\tif (self.currentmenu == \"shop_armor\")\n\t{\n\t\tif (self.impulse == 1)\n                  BuyArmor(3, 03, 1);         //weight, cost, item\n\t\tif (self.impulse == 2)\n                  BuyArmor(7, 05, 2);         //weight, cost, item\n\t\tif (self.impulse == 3)\n                  BuyArmor(9, 10, 3);         //weight, cost, item\n\t\tif (self.impulse == 4)\n                  BuyArmor(15, 12, 4);         //weight, cost, item\n\t\tif (self.impulse == 5)\n                  BuyArmor(12, 25, 5);         //weight, cost, item\n\t\tif (self.impulse == 6)\n                  BuyArmor(17, 35, 6);         //weight, cost, item\n\t\tif (self.impulse == 7)\n                  BuyArmor(5, 45, 7);         //weight, cost, item\n\t\tif (self.impulse == 8)\n                  BuyArmor(20, 55, 8);         //weight, cost, item\n\n\t\t\treturn;\n\t}\n\tif (self.currentmenu == \"shop_protect\")\n\t{\n\t\tif (self.impulse == 1)\n                  BuyProtect(20, 1);\n\t\tif (self.impulse == 2)\n                  BuyProtect(20, 2);\n\t\tif (self.impulse == 3)\n                  BuyProtect(30, 3);\n\t\tif (self.impulse == 4)\n                  BuyProtect(30, 4);\n\t\tif (self.impulse == 5)\n                  BuyProtect(40, 5);\n\n\t\t\treturn;\n\t}\n\tif (self.currentmenu == \"shop_weapons\")\n\t{\n\t\tif (self.impulse == 1)\n\t\t\tself.currentmenu = \"shop_melee\";\n\t\tif (self.impulse == 2)\n\t\t\tself.currentmenu = \"shop_thrown\";\n\t\tif (self.impulse == 3)\n\t\t\tself.currentmenu = \"shop_pistols\";\n\t\tif (self.impulse == 4)\n\t\t\tself.currentmenu = \"shop_shotguns\";\n\t\tif (self.impulse == 5)\n\t\t\tself.currentmenu = \"shop_rifles\";\n\n\t\tDisplayMenu();\n\t\treturn;\n\t}\n\tif (self.currentmenu == \"shop_melee\")\n\t{\n\t\tif (self.impulse == 1)\n                  BuyWeapon(1, 1, 1);         //weight, cost, item\n\t\tif (self.impulse == 2)\n                  BuyWeapon(6, 3, 2);         //weight, cost, item\n\t\tif (self.impulse == 3)\n                  BuyWeapon(3, 10, 3);         //weight, cost, item\n\t\tif (self.impulse == 4)\n                  BuyWeapon(10, 15, 4);         //weight, cost, item\n\n\t\t\treturn;\n\t}\n\tif (self.currentmenu == \"shop_thrown\")\n\t{\n\t\tif (self.impulse == 1)\n                  BuyGrenade(3, 1);         //weight, cost, item\n\t\tif (self.impulse == 2)\n                  BuyGrenade(4, 2);         //weight, cost, item\n\t\tif (self.impulse == 3)\n                  BuyGrenade(5, 3);         //weight, cost, item\n\t\tif (self.impulse == 4)\n                  BuyGrenade(6, 4);         //weight, cost, item\n\n\t\t\treturn;\n\t}\n\tif (self.currentmenu == \"shop_pistols\")\n\t{\n\t\tif (self.impulse == 1)\n                  BuyWeapon(1, 5, 5);         //weight, cost, item\n\t\tif (self.impulse == 2)\n                  BuyWeapon(2, 7, 6);         //weight, cost, item\n\t\tif (self.impulse == 3)\n                  BuyWeapon(2, 9, 7);         //weight, cost, item\n\t\tif (self.impulse == 4)\n                  BuyWeapon(2, 21, 8);         //weight, cost, item\n\t\tif (self.impulse == 5)\n                  BuyWeapon(3, 14, 13);         //weight, cost, item\n\t\tif (self.impulse == 6)\n                  BuyWeapon(3, 17, 14);         //weight, cost, item\n\n\t\t\treturn;\n\t}\n\tif (self.currentmenu == \"shop_shotguns\")\n\t{\n\t\tif (self.impulse == 1)\n                  BuyWeapon(3, 3, 9);         //weight, cost, item\n\t\tif (self.impulse == 2)\n                  BuyWeapon(4, 7, 10);         //weight, cost, item\n\t\tif (self.impulse == 3)\n                  BuyWeapon(5, 12, 11);         //weight, cost, item\n\t\tif (self.impulse == 4)\n                  BuyWeapon(7, 34, 12);         //weight, cost, item\n\n\t\t\treturn;\n\t}\n\tif (self.currentmenu == \"shop_rifles\")\n\t{\n\t\tif (self.impulse == 1)\n                  BuyWeapon(3, 11, 15);         //weight, cost, item\n\t\tif (self.impulse == 2)\n                  BuyWeapon(4, 21, 16);         //weight, cost, item\n\t\tif (self.impulse == 3)\n                  BuyWeapon(5, 25, 17);         //weight, cost, item\n\t\tif (self.impulse == 4)\n                  BuyWeapon(6, 28, 18);         //weight, cost, item\n\t\tif (self.impulse == 4)\n                  BuyWeapon(5, 30, 19);         //weight, cost, item\n\t\tif (self.impulse == 4)\n                  BuyWeapon(4, 32, 20);         //weight, cost, item\n\t\tif (self.impulse == 4)\n                  BuyWeapon(7, 40, 21);         //weight, cost, item\n\t\tif (self.impulse == 4)\n                  BuyWeapon(10, 45, 22);         //weight, cost, item\n\n\t\t\treturn;\n\t}\n\n\tif (self.currentmenu == \"shop_equipment\")\n\t{\n\t\tif (self.impulse == 1)\n                  BuyEquipment(20, 1);         //cost, item\n\t\tif (self.impulse == 2)\n                  BuyEquipment(20, 2);         //cost, item\n\t\tif (self.impulse == 3)\n                  BuyEquipment(20, 3);         //cost, item\n\t\tif (self.impulse == 4)\n                  BuyEquipment(20, 4);         //cost, item\n\t\tif (self.impulse == 5)\n                  BuyEquipment(20, 5);         //cost, item\n\t\tif (self.impulse == 6)\n                  BuyEquipment(20, 6);         //cost, item\n\t\tif (self.impulse == 7)\n                  BuyEquipment(20, 7);         //cost, item\n\t\tif (self.impulse == 8)\n                  BuyEquipment(20, 8);         //cost, item\n\t\tif (self.impulse == 9)\n                  BuyEquipment(20, 9);         //cost, item\n\t\tif (self.impulse == 10)\n                  BuyEquipment(20, 10);         //cost, item\n\n\t\t\treturn;\n\t}\n\n\tif (self.currentmenu == \"shop_chems\")\n\t{\n\t\tif (self.impulse == 1)\n                  BuyChem(3, 1);         //cost, item\n\t\tif (self.impulse == 2)\n                  BuyChem(5, 2);         //cost, item\n\t\tif (self.impulse == 3)\n                  BuyChem(10, 3);         //cost, item\n\t\tif (self.impulse == 4)\n                  BuyChem(12, 4);         //cost, item\n\t\tif (self.impulse == 5)\n                  BuyChem(18, 5);         //cost, item\n\t\tif (self.impulse == 6)\n                  BuyChem(21, 6);         //cost, item\n\t}\n\n\tif (self.currentmenu == \"shop_other\")\n\t\treturn;\n\n\n\tif (self.currentmenu == \"select_team\")\n\t{\n\t\tif (self.impulse == 1)\n\t\t{\n\t\t\tbprint(2, self.netname);\n\t\t\tbprint(2, \" has joined the rangers.\\n\");\n\t\t\tself.currentmenu = \"confirm_team\";\n\t\t\tDisplayMenu();\n\t\t\tself.team = 1;\n\t\t\treturn;\n\t\t}\n\t\tif (self.impulse == 2)\n\t\t{\n\t\t\tbprint(2, self.netname);\n\t\t\tbprint(2, \" has joined the raiders.\\n\");\n\t\t\tself.currentmenu = \"confirm_team\";\n\t\t\tDisplayMenu();\n\t\t\tself.team = 2;\n\t\t\treturn;\n\t\t}\n\n\t}\n\n\tif (self.currentmenu == \"select_skill\")\n\t{\n\t\tsound (self, CHAN_WEAPON, \"buttons/switch02.wav\", TRUE, ATTN_NORM);\n\n\t\tif (self.impulse == 1)\n\t\t{\n\t\t\tself.currentmenu = \"none\";\n\t\t\tself.max_health = 80;\n\t\t\tself.class = 1;\n\t\t\tself.currentmenu = \"confirm_skill\";\n\t\t\tcenterprint (self, \"your class will be\\n\\nMedic - OK?\\n1 Yes     \\n2 No      \\n\");\n\t\t\tself.ghost = 0;\n\t\t\treturn;\n\t\t}\n\t\tif ((self.impulse == 2))\n\t\t{\n\t\t\tself.currentmenu = \"none\";\n\t\t\tself.max_health = 70;\n\t\t\tself.class = 2;\n\t\t\tself.currentmenu = \"confirm_skill\";\n\t\t\tcenterprint (self, \"your class will be\\n\\nAssassin - OK?\\n1 Yes     \\n2 No      \\n\");\n\t\t\tself.ghost = 0;\n\t\t\treturn;\n\t\t}\n\t\tif (self.impulse == 3)\n\t\t{\n\t\t\tself.currentmenu = \"none\";\n\t\t\tself.max_health = 100;\n\t\t\tself.class = 3;\n\t\t\tself.currentmenu = \"confirm_skill\";\n\t\t\tcenterprint (self, \"your class will be\\n\\nSoldiier - OK?\\n1 Yes     \\n2 No      \\n\");\n\t\t\tself.ghost = 0;\n\t\t\treturn;\n\t\t}\n\t\tif (self.impulse == 4)\n\t\t{\n\t\t\tself.max_health = 80;\n\t\t\tself.currentmenu = \"none\";\n\t\t\tself.class = 4;\n\t\t\tself.currentmenu = \"confirm_skill\";\n\t\t\tcenterprint (self, \"your class will be\\n\\nScientist - OK?\\n1 Yes     \\n2 No      \\n\");\n\t\t\tself.ghost = 0;\n\t\t\treturn;\n\t\t}\n\t}\n\n\n\t\tif (self.currentmenu == \"confirm_team\")\n\t\t{\n\t\t\tif (self.impulse == 1)\n\t\t\t{\n\t\t\t\tsound (self, CHAN_WEAPON, \"buttons/switch02.wav\", TRUE, ATTN_NORM);\n\t\t\t\tself.currentmenu = \"select_skill\";\n\t\t\t\tDisplayMenu();\n\t\t\t\tself.impulse = 0;\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (self.impulse == 2)\n\t\t\t{\n\t\t\t\tsound (self, CHAN_WEAPON, \"buttons/switch02.wav\", TRUE, ATTN_NORM);\n\t\t\t\tself.currentmenu = \"select_team\";\n\t\t\t\tDisplayMenu();\n\t\t\t\tself.impulse = 0;\n\t\t\t\tself.team = 0;\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tif (self.currentmenu == \"confirm_skill\")\n\t\t{\n\t\t\tif (self.impulse == 1)\n\t\t\t{\n\t\t\t\tsound (self, CHAN_WEAPON, \"buttons/switch02.wav\", TRUE, ATTN_NORM);\n\t\t\t\tself.currentmenu = \"none\";\n\t\t\t\tPutClientInServer();\n\t\t\t\tself.impulse = 0;\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (self.impulse == 2)\n\t\t\t{\n\t\t\t\tsound (self, CHAN_WEAPON, \"buttons/switch02.wav\", TRUE, ATTN_NORM);\n\t\t\t\tself.currentmenu = \"select_skill\";\n\t\t\t\tDisplayMenu();\n\t\t\t\tself.impulse = 0;\n\t\t\t\tself.class = 0;\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n};"
  },
  {
    "path": "quakec/fallout2/models.qc",
    "content": "\n/*\n===============================================================================\n\nWORLD WEAPONS\n\n===============================================================================\n*/\n\n$modelname g_shot\n$cd /raid/quake/id1/models/g_shot\n$origin 0 0 -24\n$flags 8\t\t// client side rotate\n$base base\n$skin skin\n$frame shot1\n\n\n$modelname g_nail\n$cd /raid/quake/id1/models/g_nail\n$flags 8\t\t// client side rotate\n$origin 0 0 -24\n$base base\n$skin skin\n$frame shot1\n\n\n$modelname g_nail2\n$cd /raid/quake/id1/models/g_nail2\n$flags 8\t\t// client side rotate\n$origin 0 0 -24\n$base base\n$skin skin\n$frame shot2\n\n\n$modelname g_rock\n$cd /raid/quake/id1/models/g_rock\n$flags 8\t\t// client side rotate\n$origin 0 0 -24\n$base base\n$skin skin\n$frame shot1\n\n\n$modelname g_rock2\n$cd /raid/quake/id1/models/g_rock2\n$flags 8\t\t// client side rotate\n$origin 0 0 -24\n$base base\n$skin skin\n$frame shot1\n\n$modelname g_light\n$cd /raid/quake/id1/models/g_light\n$flags 8\t\t// client side rotate\n$origin 0 0 -24\n$base base\n$skin skin\n$frame shot1\n\n/*\n===============================================================================\n\nVIEW WEAPONS\n\n===============================================================================\n*/\n\n$modelname v_axe\n$cd /raid/quake/id1/models/v_axe\n$origin 0 5 54\n$base base\n$skin skin\n$frame frame1 frame2 frame3 frame4 frame5 frame6 frame7 frame8 frame9\n\n\n$modelname v_shot\n$cd /raid/quake/id1/models/v_shot\n$origin 0 0 54\n$base base\n$skin skin\n$frame shot1 shot2 shot3 shot4 shot5 shot6 shot7\n\n\n$modelname v_shot2\n$cd /raid/quake/id1/models/v_shot2\n$origin 0 0 56\n$base base\n$skin skin\n$frame shot1 shot2 shot3 shot4 shot5 shot6 shot7\n\n\n$modelname v_rock2\n$cd /raid/quake/id1/models/v_rock2\n$origin 0 0 54\n$base base\n$skin skin\n$frame shot1 shot2 shot3 shot4 shot5 shot6 shot6\n\n\n$modelname v_rock\n$cd /raid/quake/id1/models/v_rock\n$origin 0 0 54\n$base base\n$skin skin\n$frame shot1 shot2 shot3 shot4 shot5 shot6 shot7\n\n\n$modelname v_nail2\n$cd /raid/quake/id1/models/v_nail2\n$origin 0 0 54\n$base base\n$skin skin\n$frame shot1 shot2 shot3 shot4 shot5 shot6 shot7 shot8 shot9\n\n\n$modelname v_nail\n$cd /raid/quake/id1/models/v_nail\n$origin 0 0 54\n$base base\n$skin skin\n$frame shot1 shot2 shot3 shot4 shot5 shot6 shot7 shot8 shot9\n\n$modelname v_light\n$cd /raid/quake/id1/models/v_light\n$origin 0 0 54\n$base base\n$skin skin\n$frame shot1 shot2 shot3 shot4 shot5\n\n\n/*\n===============================================================================\n\nITEMS\n\n===============================================================================\n*/\n\n$modelname w_g_key\n$cd /raid/quake/id1/models/w_g_key\n$flags 8\t\t// client side rotate\n$base base\n$skin skin\n$frame frame1\n\n$modelname w_s_key\n$cd /raid/quake/id1/models/w_s_key\n$flags 8\t\t// client side rotate\n$base base\n$skin skin\n$frame frame1\n\n$modelname m_g_key\n$cd /raid/quake/id1/models/m_g_key\n$flags 8\t\t// client side rotate\n$base base\n$skin skin\n$frame frame1\n\n$modelname m_s_key\n$cd /raid/quake/id1/models/m_s_key\n$flags 8\t\t// client side rotate\n$base base\n$skin skin\n$frame frame1\n\n$modelname b_g_key\n$cd /raid/quake/id1/models/b_g_key\n$flags 8\t\t// client side rotate\n$base base\n$skin skin\n$frame frame1\n\n$modelname b_s_key\n$cd /raid/quake/id1/models/b_s_key\n$flags 8\t\t// client side rotate\n$base base\n$skin skin\n$frame frame1\n\n\n$modelname quaddama\n$cd /raid/quake/id1/models/quaddama\n$flags 8\t\t// client side rotate\n$base base\n$skin skin\n$frame frame1\n\n$modelname invisibl\n$cd /raid/quake/id1/models/invisibl\n$flags 8\t\t// client side rotate\n$base base\n$skin skin\n$frame frame1\n\n$modelname invulner\n$flags 8\t\t// client side rotate\n$cd /raid/quake/id1/models/invulner\n$base base\n$skin skin\n$frame frame1\n\n//modelname jetpack\n//cd /raid/quake/id1/models/jetpack\n//flags 8\t\t// client side rotate\n//base base\n//skin skin\n//frame frame1\n\n$modelname cube\n$cd /raid/quake/id1/models/cube\n$flags 8\t\t// client side rotate\n$base base\n$skin skin\n$frame frame1\n\n$modelname suit\n$cd /raid/quake/id1/models/suit\n$flags 8\t\t// client side rotate\n$base base\n$skin skin\n$frame frame1\n\n$modelname boots\n$cd /raid/quake/id1/models/boots\n$flags 8\t\t// client side rotate\n$base base\n$skin skin\n$frame frame1\n\n$modelname end1\n$cd /raid/quake/id1/models/end1\n$flags 8\t\t// client side rotate\n$base base\n$skin skin\n$frame frame1\n\n$modelname end2\n$cd /raid/quake/id1/models/end2\n$flags 8\t\t// client side rotate\n$base base\n$skin skin\n$frame frame1\n\n$modelname end3\n$cd /raid/quake/id1/models/end3\n$flags 8\t\t// client side rotate\n$base base\n$skin skin\n$frame frame1\n\n$modelname end4\n$cd /raid/quake/id1/models/end4\n$flags 8\t\t// client side rotate\n$base base\n$skin skin\n$frame frame1\n\n\n/*\n===============================================================================\n\nGIBS\n\n===============================================================================\n*/\n\n$modelname gib1\n$cd /raid/quake/id1/models/gib1\n$flags 4\t\t// EF_GIB\n$origin 0 0 0\n$base base\n$skin skin\n$frame frame1\n\n\n// torso\n$modelname gib2\n$cd /raid/quake/id1/models/gib2\n$flags 4\t\t// EF_GIB\n$origin 0 0 0\n$base base\n$skin skin\n$frame frame1\n\n$modelname gib3\n$cd /raid/quake/id1/models/gib3\n$flags 4\t\t// EF_GIB\n$origin 0 0 0\n$base base\n$skin skin\n$frame frame1\n\n\n// heads\n\n$modelname h_player\n$cd /raid/quake/id1/models/h_player\n$flags 4\t\t// EF_GIB\n$origin 0 0 0\n$base base\n$skin skin\n$frame frame1\n\n$modelname h_dog\n$cd /raid/quake/id1/models/h_dog\n$flags 4\t\t// EF_GIB\n$origin 0 0 0\n$base base\n$skin skin\n$frame frame1\n\n$modelname h_mega\n$cd /raid/quake/id1/models/h_mega\n$flags 4\t\t// EF_GIB\n$origin 0 0 0\n$base base\n$skin skin\n$frame frame1\n\n$modelname h_guard\n$cd /raid/quake/id1/models/h_guard\n$flags 4\t\t// EF_GIB\n$origin 0 0 0\n$base base\n$skin skin\n$frame frame1\n\n$modelname h_wizard\n$cd /raid/quake/id1/models/h_wizard\n$flags 4\t\t// EF_GIB\n$origin 0 0 0\n$base base\n$skin skin\n$frame frame1\n\n$modelname h_knight\n$cd /raid/quake/id1/models/h_knight\n$flags 4\t\t// EF_GIB\n$origin 0 0 0\n$base base\n$skin skin\n$frame frame1\n\n$modelname h_hellkn\n$cd /raid/quake/id1/models/h_hellkn\n$flags 4\t\t// EF_GIB\n$origin 0 0 0\n$base base\n$skin skin\n$frame frame1\n\n$modelname h_zombie\n$cd /raid/quake/id1/models/h_zombie\n$flags 4\t\t// EF_GIB\n$origin 0 0 0\n$base base\n$skin skin\n$frame frame1\n\n$modelname h_shams\n$cd /raid/quake/id1/models/h_shams\n$flags 4\t\t// EF_GIB\n$origin 0 0 0\n$base base\n$skin skin\n$frame frame1\n\n$modelname h_shal\n$cd /raid/quake/id1/models/h_shal\n$flags 4\t\t// EF_GIB\n$origin 0 0 0\n$base base\n$skin skin\n$frame frame1\n\n$modelname h_ogre\n$cd /raid/quake/id1/models/h_ogre\n$flags 4\t\t// EF_GIB\n$origin 0 0 0\n$base base\n$skin skin\n$frame frame1\n\n$modelname h_demon\n$cd /raid/quake/id1/models/h_demon\n$flags 4\t\t// EF_GIB\n$origin 0 0 0\n$base base\n$skin skin\n$frame frame1\n\n/*\n===============================================================================\n\nMISC\n\n===============================================================================\n*/\n\n$modelname armor\n$cd /raid/quake/id1/models/armor\n$flags 8\t\t// client side rotate\n$origin 0 0 -8\n$base base\n$skin skin\n$skin skin2\n$skin skin3\n$frame armor\n\n$modelname s_light\t\t\t// shambler lightning ready\n$cd /raid/quake/id1/models/s_light\n$origin 0 0 24\n$base base\n$skin skin\n$frame\tframe1 frame2 frame3\n\n$modelname bolt3\t\t\t// lightning towar bolts\n$cd /raid/quake/id1/models/bolt2\n$origin 0 0 0\n$base base\n$scale 4\n$skin skin\n$frame\tlight\n\n$modelname bolt2\n$cd /raid/quake/id1/models/bolt2\n$origin 0 0 0\n$base base\n$skin skin\n$frame\tlight\n\n$modelname bolt\n$cd /raid/quake/id1/models/bolt\n$origin 0 0 0\n$base light\n$skin light\n$frame\tlight\n\n$modelname laser\n$cd /raid/quake/id1/models/laser\n$base base\n$skin skin\n$scale 2\n$frame frame1\n\n$modelname flame\t\t// with torch\n$cd /raid/quake/id1/models/flame\n$origin 0 0 12\n$base base\n$skin skin\n$framegroupstart\n$frame flame1\t0.1\n$frame flame2\t0.1\n$frame flame3\t0.1\n$frame flame4\t0.1\n$frame flame5\t0.1\n$frame flame6\t0.1\n$framegroupend\n\n$modelname flame2\t\t// standing flame, no torch\n$cd /raid/quake/id1/models/flame2\n$origin 0 0 12\n$base base\n$skin skin\n$framegroupstart\n$frame flame1\t0.1\n$frame flame2\t0.1\n$frame flame3\t0.1\n$frame flame4\t0.1\n$frame flame5\t0.1\n$frame flame6\t0.1\n$framegroupend\n$framegroupstart\n$frame flameb1\n$frame flameb2\n$frame flameb3\n$frame flameb4\n$frame flameb5\n$frame flameb6\n$frame flameb7\n$frame flameb8\n$frame flameb9\n$frame flameb10\n$frame flameb11\n$framegroupend\n\n$modelname zom_gib\n$cd /raid/quake/id1/models/zom_gib\n$flags 32\t\t// EF_ZOMGIB\n$base base\n$skin skin\n$frame frame1\n\n$modelname eyes\n$cd /raid/quake/id1/models/eyes\n$origin 0 0 -24\n$base base\n$skin skin\n$frame frame1\n\n$modelname spike\n$cd /raid/quake/id1/models/spike\n$origin 0 0 0\n$base spike\n$skin skin\n$frame spike\n\n$modelname s_spike\n$cd /raid/quake/id1/models/s_spike\n$origin 0 0 0\n$base spike\n$skin skin\n$frame spike\n\n$modelname v_spike\n$cd /raid/quake/id1/models/v_spike\n$flags 128\t\t// EF_TRACER3\n$origin 0 0 0\n$base base\n$skin skin\n$frame frame1\n\n$modelname w_spike\n$cd /raid/quake/id1/models/w_spike\n$flags 16\t\t// EF_TRACER\n$origin 0 0 0\n$base base\n$skin skin\n$framegroupstart\n$frame frame1\t0.1\n$frame frame2\t0.1\n$frame frame3\t0.1\n$frame frame4\t0.1\n$framegroupend\n\n$modelname k_spike\n$cd /raid/quake/id1/models/k_spike\n$flags 64\t\t// EF_TRACER2\n$origin 0 0 0\n$base base\n$skin skin\n$frame frame1\n\n$modelname backpack\n$cd /raid/quake/id1/models/backpack\n$flags 8\t\t// EF_ROTATE\n$origin 0 0 0\n$base base\n$skin skin\n$frame frame1\n\n$modelname grenade\n$cd /raid/quake/id1/models/grenade2\n$flags 2\t\t// EF_GRENADE\n$origin 0 0 0\n$base base\n$skin skin\n$frame grenade\n\n$modelname missile\n$cd /raid/quake/id1/models/missile\n$flags\t1\t\t\t// EF_ROCKET\n$origin 0 0 0\n$base base\n$skin skin\n$frame missile\n\n$modelname lavaball\n$cd /raid/quake/id1/models/lavaball\n$flags\t1\t\t\t// EF_ROCKET\n$origin 0 0 0\n$base base\n$skin skin\n$frame frame1\n\n$modelname teleport\n$cd /raid/quake/id1/models/teleport\n$origin 0 0 24\n$base base\n$skin skin\n$frame frame1\n\n"
  },
  {
    "path": "quakec/fallout2/monsters.qc",
    "content": "/* ALL MONSTERS SHOULD BE 1 0 0 IN COLOR */\n\n// name =[framenum,\tnexttime, nextthink] {code}\n// expands to:\n// name ()\n// {\n//\t\tself.frame=framenum;\n//\t\tself.nextthink = time + nexttime;\n//\t\tself.think = nextthink\n//\t\t<code>\n// };\n\n\n/*\n================\nmonster_use\n\nUsing a monster makes it angry at the current activator\n================\n*/\nvoid() monster_use =\n{\n\tif (self.enemy)\n\t\treturn;\n\tif (self.health <= 0)\n\t\treturn;\n\tif (activator.items & IT_INVISIBILITY)\n\t\treturn;\n\tif (activator.flags & FL_NOTARGET)\n\t\treturn;\n\tif (activator.classname != \"player\")\n\t\treturn;\n\t\n// delay reaction so if the monster is teleported, its sound is still\n// heard\n\tself.enemy = activator;\n\tself.nextthink = time + 0.1;\n\tself.think = FoundTarget;\n};\n\n/*\n================\nmonster_death_use\n\nWhen a mosnter dies, it fires all of its targets with the current\nenemy as activator.\n================\n*/\nvoid() monster_death_use =\n{\n// fall to ground\n\tif (self.flags & FL_FLY)\n\t\tself.flags = self.flags - FL_FLY;\n\tif (self.flags & FL_SWIM)\n\t\tself.flags = self.flags - FL_SWIM;\n\n\tif (!self.target)\n\t\treturn;\n\n\tactivator = self.enemy;\n\tSUB_UseTargets ();\n};\n\n\n//============================================================================\n\nvoid() walkmonster_start_go =\n{\n\tself.origin_z = self.origin_z + 1;\t// raise off floor a bit\n\tdroptofloor();\n\t\n\tif (!walkmove(0,0))\n\t{\n\t\tdprint (\"walkmonster in wall at: \");\n\t\tdprint (vtos(self.origin));\n\t\tdprint (\"\\n\");\n\t}\n\t\n\tself.takedamage = DAMAGE_AIM;\n\n\tself.ideal_yaw = self.angles * '0 1 0';\n\tif (!self.yaw_speed)\n\t\tself.yaw_speed = 20;\n\tself.view_ofs = '0 0 25';\n\tself.use = monster_use;\n\t\n\tself.flags = self.flags | FL_MONSTER;\n\t\n\tif (self.target)\n\t{\n\t\tself.goalentity = self.movetarget = find(world, targetname, self.target);\n\t\tself.ideal_yaw = vectoyaw(self.goalentity.origin - self.origin);\n\t\tif (!self.movetarget)\n\t\t{\n\t\t\tdprint (\"Monster can't find target at \");\n\t\t\tdprint (vtos(self.origin));\n\t\t\tdprint (\"\\n\");\n\t\t}\n// this used to be an objerror\n\t\tif (self.movetarget.classname == \"path_corner\")\n\t\t\tself.th_walk ();\n\t\telse\n\t\t\tself.pausetime = 99999999;\n\t\t\tself.th_stand ();\n\t}\n\telse\n\t{\n\t\tself.pausetime = 99999999;\n\t\tself.th_stand ();\n\t}\n\n// spread think times so they don't all happen at same time\n\tself.nextthink = self.nextthink + random()*0.5;\n};\n\n\nvoid() walkmonster_start =\n{\n// delay drop to floor to make sure all doors have been spawned\n// spread think times so they don't all happen at same time\n\tself.nextthink = self.nextthink + random()*0.5;\n\tself.think = walkmonster_start_go;\n\ttotal_monsters = total_monsters + 1;\n};\n\n\n\nvoid() flymonster_start_go =\n{\n\tself.takedamage = DAMAGE_AIM;\n\n\tself.ideal_yaw = self.angles * '0 1 0';\n\tif (!self.yaw_speed)\n\t\tself.yaw_speed = 10;\n\tself.view_ofs = '0 0 25';\n\tself.use = monster_use;\n\n\tself.flags = self.flags | FL_FLY;\n\tself.flags = self.flags | FL_MONSTER;\n\n\tif (!walkmove(0,0))\n\t{\n\t\tdprint (\"flymonster in wall at: \");\n\t\tdprint (vtos(self.origin));\n\t\tdprint (\"\\n\");\n\t}\n\n\tif (self.target)\n\t{\n\t\tself.goalentity = self.movetarget = find(world, targetname, self.target);\n\t\tif (!self.movetarget)\n\t\t{\n\t\t\tdprint (\"Monster can't find target at \");\n\t\t\tdprint (vtos(self.origin));\n\t\t\tdprint (\"\\n\");\n\t\t}\n// this used to be an objerror\n\t\tif (self.movetarget.classname == \"path_corner\")\n\t\t\tself.th_walk ();\n\t\telse\n\t\t\tself.pausetime = 99999999;\n\t\t\tself.th_stand ();\n\t}\n\telse\n\t{\n\t\tself.pausetime = 99999999;\n\t\tself.th_stand ();\n\t}\n};\n\nvoid() flymonster_start =\n{\n// spread think times so they don't all happen at same time\n\tself.nextthink = self.nextthink + random()*0.5;\n\tself.think = flymonster_start_go;\n\ttotal_monsters = total_monsters + 1;\n};\n\n\nvoid() swimmonster_start_go =\n{\n\tif (deathmatch)\n\t{\n\t\tremove(self);\n\t\treturn;\n\t}\n\n\tself.takedamage = DAMAGE_AIM;\n\ttotal_monsters = total_monsters + 1;\n\n\tself.ideal_yaw = self.angles * '0 1 0';\n\tif (!self.yaw_speed)\n\t\tself.yaw_speed = 10;\n\tself.view_ofs = '0 0 10';\n\tself.use = monster_use;\n\t\n\tself.flags = self.flags | FL_SWIM;\n\tself.flags = self.flags | FL_MONSTER;\n\n\tif (self.target)\n\t{\n\t\tself.goalentity = self.movetarget = find(world, targetname, self.target);\n\t\tif (!self.movetarget)\n\t\t{\n\t\t\tdprint (\"Monster can't find target at \");\n\t\t\tdprint (vtos(self.origin));\n\t\t\tdprint (\"\\n\");\n\t\t}\n// this used to be an objerror\n\t\tself.ideal_yaw = vectoyaw(self.goalentity.origin - self.origin);\n\t\tself.th_walk ();\n\t}\n\telse\n\t{\n\t\tself.pausetime = 99999999;\n\t\tself.th_stand ();\n\t}\n\n// spread think times so they don't all happen at same time\n\tself.nextthink = self.nextthink + random()*0.5;\n};\n\nvoid() swimmonster_start =\n{\n// spread think times so they don't all happen at same time\n\tself.nextthink = self.nextthink + random()*0.5;\n\tself.think = swimmonster_start_go;\n\ttotal_monsters = total_monsters + 1;\n};\n\n\n"
  },
  {
    "path": "quakec/fallout2/name.qc",
    "content": "\n\n\n\nstring () GetEquipmentName =\n{\n\tlocal string name;\n\tlocal float slot;\n\n\tslot = self.equipment;\n\n\tif (slot == 0)\n\t\tname = \"none\";\n\telse if (slot == 1)\n\t\tname = \"medic's bag\";\n\telse if (slot == 2)\n\t\tname = \"security alarm\";\n\telse if (slot == 3)\n\t\tname = \"remote camera\";\n\telse if (slot == 4)\n\t\tname = \"belt pouch\";\n\telse if (slot == 5)\n\t\tname = \"backpack\";\n\telse if (slot == 6)\n\t\tname = \"toolkit mark ii\";\n\telse if (slot == 7)\n\t\tname = \"climbing gear\";\n\telse if (slot == 8)\n\t\tname = \"enhanced battery\";\n\telse if (slot == 9)\n\t\tname = \"stealth-boy\";\n\n\treturn name;\n};\n\nstring () GetProtectName =\n{\n\tlocal string name;\n\tlocal float slot;\n\n\tslot = self.protect;\n\n\tif (slot == 0)\n\t\tname = \"none\";\n\tif (slot == 1)\n\t\tname = \"\";\n\telse if (slot == 2)\n\t\tname = \"sacred talisman\";\n\telse if (slot == 3)\n\t\tname = \"forcefield ring\";\n\telse if (slot == 4)\n\t\tname = \"\";\n\telse if (slot == 5)\n\t\tname = \"\";\n\n\treturn name;\n};\n\nstring () GetPerkName =\n{\n\tlocal string name;\n\tlocal float slot;\n\n\tslot = self.perk;\n\n\tif (slot == 0)\n\t\tname = \"none\";\n\tif (slot == 1)\n\t\tname = \"medic's bag\";\n\telse if (slot == 2)\n\t\tname = \"security alarm\";\n\telse if (slot == 3)\n\t\tname = \"remote camera\";\n\telse if (slot == 4)\n\t\tname = \"belt pouch\";\n\telse if (slot == 5)\n\t\tname = \"backpack\";\n\telse if (slot == 6)\n\t\tname = \"toolkit mark ii\";\n\telse if (slot == 7)\n\t\tname = \"climbing gear\";\n\telse if (slot == 8)\n\t\tname = \"enhanced battery\";\n\telse if (slot == 9)\n\t\tname = \"stealth-boy\";\n\treturn name;\n};\n\nstring () GetTraitName =\n{\n\tlocal string name;\n\tlocal float slot;\n\n\tslot = self.trait;\n\n\tif (slot == 0)\n\t\tname = \"none\";\n\telse if (slot == 1)\n\t\tname = \"medic's bag\";\n\telse if (slot == 2)\n\t\tname = \"security alarm\";\n\telse if (slot == 3)\n\t\tname = \"remote camera\";\n\telse if (slot == 4)\n\t\tname = \"belt pouch\";\n\telse if (slot == 5)\n\t\tname = \"backpack\";\n\telse if (slot == 6)\n\t\tname = \"toolkit mark ii\";\n\telse if (slot == 7)\n\t\tname = \"climbing gear\";\n\telse if (slot == 8)\n\t\tname = \"enhanced battery\";\n\telse if (slot == 9)\n\t\tname = \"stealth-boy\";\n\n\treturn name;\n};"
  },
  {
    "path": "quakec/fallout2/ogre.qc",
    "content": "\nvoid () OgreGrenadeExplode =\n{\n\tT_RadiusDamage (self, self.owner, (IDLE2A + (random () * TE_BLOOD)), world, \"\");\n\tsound (self, CHAN_VOICE, \"weapons/r_exp3.wav\", PLAT_LOW_TRIGGER, ATTN_NORM);\n\tWriteByte (MSG_BROADCAST, SVC_TEMPENTITY);\n\tWriteByte (MSG_BROADCAST, TE_EXPLOSION);\n\tWriteCoord (MSG_BROADCAST, self.origin_x);\n\tWriteCoord (MSG_BROADCAST, self.origin_y);\n\tWriteCoord (MSG_BROADCAST, self.origin_z);\n\tself.velocity = VEC_ORIGIN;\n\tself.touch = SUB_Null;\n\tself.solid = SOLID_NOT;\n\tremove (self);\n};\n\nvoid () LobAGrenade =\n{\n\tnewmis = spawn ();\n\tnewmis.owner = self;\n\tnewmis.movetype = MOVETYPE_BOUNCE;\n\tnewmis.solid = SOLID_BBOX;\n\tnewmis.classname = \"grenade\";\n\n\tnewmis.skin = 0;\n\tmakevectors (self.angles);\n\n\tnewmis.velocity = v_forward * 800;\n\tnewmis.velocity_z = (newmis.velocity_z + 200);\n\tnewmis.angles = vectoangles (newmis.velocity);\n\tnewmis.avelocity_x = (random () * 300);\n\tnewmis.avelocity_y = (random () * 300);\n\tnewmis.avelocity_z = (random () * 300);\n\tnewmis.touch = HandGrenBounce;\n\tnewmis.nextthink = (time + 2.5);\n\n//\tif (iid == IID_GREN_FRAG)\n//\t\tnewmis.think = FragExplode;\n//\telse if (iid == IID_GREN_EMP)\n//\t\tnewmis.think = EMPExplode;\n//\telse if (iid == IID_GREN_SMOKE)\n\n\tnewmis.think = SmokeThink;\n\tif (random() < 0.5)\n\t\tnewmis.think = FlashExplode;\n\n\tnewmis.frame = 1;\n\tsetmodel (newmis, \"progs/handgren.mdl\");\n\tsetsize (newmis, VEC_ORIGIN, VEC_ORIGIN);\n\tsetorigin (newmis, ((self.origin + (v_right * TE_BLOOD)) + (v_up * TE_BLOOD)));\n};\n\n\n\n\nvoid (float side) chainsaw =\n{\n\tlocal vector delta;\n\tlocal float ldmg;\n\n\tif (!self.enemy)\n\t{\n\t\treturn;\n\t}\n\tif (!CanDamage (self.enemy, self))\n\t{\n\t\treturn;\n\t}\n\tai_charge (TE_LAVASPLASH);\n\tdelta = (self.enemy.origin - self.origin);\n\tif ((vlen (delta) > 100))\n\t{\n\t\treturn;\n\t}\n\tldmg = (TE_LIGHTNING3 + (random () * IDLE6A));\n\tT_Damage (self.enemy, self, self, ldmg);\n\tif (side)\n\t{\n\t\tmakevectors (self.angles);\n\t\tif ((side == PLAT_LOW_TRIGGER))\n\t\t{\n\t\t\tSpawnMeatSpray ((self.origin + (v_forward * SECRET_YES_SHOOT)), ((crandom () * 100) * v_right));\n\t\t}\n\t\telse\n\t\t{\n\t\t\tSpawnMeatSpray ((self.origin + (v_forward * SECRET_YES_SHOOT)), (side * v_right));\n\t\t}\n\t}\n};\n\nvoid () ogre_stand1 = [ 0, ogre_stand2 ]\n{\n\tai_stand ();\n};\n\nvoid () ogre_stand2 = [ 1, ogre_stand3 ]\n{\n\tai_stand ();\n};\n\nvoid () ogre_stand3 = [ 2, ogre_stand4 ]\n{\n\tai_stand ();\n};\n\nvoid () ogre_stand4 = [ 3, ogre_stand5 ]\n{\n\tai_stand ();\n};\n\nvoid () ogre_stand5 = [ 4, ogre_stand6 ]\n{\n\tif ((random () < 0.2))\n\t{\n\t\tsound (self, CHAN_VOICE, \"ogre/ogidle.wav\", PLAT_LOW_TRIGGER, ATTN_IDLE);\n\t}\n\tai_stand ();\n};\n\nvoid () ogre_stand6 = [ 5, ogre_stand7 ]\n{\n\tai_stand ();\n};\n\nvoid () ogre_stand7 = [ 6, ogre_stand8 ]\n{\n\tai_stand ();\n};\n\nvoid () ogre_stand8 = [ 7, ogre_stand9 ]\n{\n\tai_stand ();\n};\n\nvoid () ogre_stand9 = [ 8, ogre_stand1 ]\n{\n\tai_stand ();\n};\n\nvoid () ogre_walk1 = [ 9, ogre_walk2 ]\n{\n\tai_walk (AS_MELEE);\n};\n\nvoid () ogre_walk2 = [ 10, ogre_walk3 ]\n{\n\tai_walk (SILENT);\n};\n\nvoid () ogre_walk3 = [ 11, ogre_walk4 ]\n{\n\tai_walk (SILENT);\n\tif ((random () < 0.2))\n\t{\n\t\tsound (self, CHAN_VOICE, \"ogre/ogidle.wav\", PLAT_LOW_TRIGGER, ATTN_IDLE);\n\t}\n};\n\nvoid () ogre_walk4 = [ 12, ogre_walk5 ]\n{\n\tai_walk (SILENT);\n};\n\nvoid () ogre_walk5 = [ 13, ogre_walk6 ]\n{\n\tai_walk (SILENT);\n};\n\nvoid () ogre_walk6 = [ 14, ogre_walk7 ]\n{\n\tai_walk (MULTICAST_PVS_R);\n\tif ((random () < 0.1))\n\t{\n\t\tsound (self, CHAN_VOICE, \"ogre/ogdrag.wav\", PLAT_LOW_TRIGGER, ATTN_IDLE);\n\t}\n};\n\nvoid () ogre_walk7 = [ 15, ogre_walk8 ]\n{\n\tai_walk (AS_MELEE);\n};\n\nvoid () ogre_walk8 = [ 16, ogre_walk9 ]\n{\n\tai_walk (SILENT);\n};\n\nvoid () ogre_walk9 = [ 17, ogre_walk10 ]\n{\n\tai_walk (AS_MELEE);\n};\n\nvoid () ogre_walk10 = [ 18, ogre_walk11 ]\n{\n\tai_walk (PLAT_LOW_TRIGGER);\n};\n\nvoid () ogre_walk11 = [ 19, ogre_walk12 ]\n{\n\tai_walk (SILENT);\n};\n\nvoid () ogre_walk12 = [ 20, ogre_walk13 ]\n{\n\tai_walk (AS_MELEE);\n};\n\nvoid () ogre_walk13 = [ 21, ogre_walk14 ]\n{\n\tai_walk (AS_MELEE);\n};\n\nvoid () ogre_walk14 = [ 22, ogre_walk15 ]\n{\n\tai_walk (AS_MELEE);\n};\n\nvoid () ogre_walk15 = [ 23, ogre_walk16 ]\n{\n\tai_walk (AS_MELEE);\n};\n\nvoid () ogre_walk16 = [ 24, ogre_walk1 ]\n{\n\tai_walk (SECRET_1ST_DOWN);\n};\n\nvoid () ogre_run1 = [ 25, ogre_run2 ]\n{\n\tai_run (TE_LIGHTNING3+2);\n\tif ((random () < 0.2))\n\t{\n\t\tsound (self, CHAN_VOICE, \"ogre/ogidle2.wav\", PLAT_LOW_TRIGGER, ATTN_IDLE);\n\t}\n};\n\nvoid () ogre_run2 = [ 26, ogre_run3 ]\n{\n\tai_run (TE_BLOOD+2);\n};\n\nvoid () ogre_run3 = [ 27, ogre_run4 ]\n{\n\tai_run (SECRET_NO_SHOOT+2);\n};\n\nvoid () ogre_run4 = [ 28, ogre_run5 ]\n{\n\tai_run (IDLE10A);\n};\n\nvoid () ogre_run5 = [ 29, ogre_run6 ]\n{\n\tai_run (SECRET_YES_SHOOT+2);\n};\n\nvoid () ogre_run6 = [ 30, ogre_run7 ]\n{\n\tai_run (SECRET_1ST_DOWN+2);\n};\n\nvoid () ogre_run7 = [ 31, ogre_run8 ]\n{\n\tai_run (TE_LIGHTNINGBLOOD+2);\n};\n\nvoid () ogre_run8 = [ 32, ogre_run1 ]\n{\n\tai_run (DRAW2+2);\n};\n\nvoid () ogre_swing1 = [ 33, ogre_swing2 ]\n{\n\tai_charge (TE_TELEPORT);\n\tsound (self, CHAN_WEAPON, \"ogre/ogsawatk.wav\", PLAT_LOW_TRIGGER, ATTN_NORM);\n};\n\nvoid () ogre_swing2 = [ 34, ogre_swing3 ]\n{\n\tai_charge (PLAT_LOW_TRIGGER);\n};\n\nvoid () ogre_swing3 = [ 35, ogre_swing4 ]\n{\n\tai_charge (SECRET_1ST_DOWN);\n};\n\nvoid () ogre_swing4 = [ 36, ogre_swing5 ]\n{\n\tai_charge (TE_LIGHTNINGBLOOD);\n};\n\nvoid () ogre_swing5 = [ 37, ogre_swing6 ]\n{\n\tai_charge (TE_LIGHTNING3);\n\tchainsaw (MULTICAST_ALL);\n\tself.angles_y = (self.angles_y + (random () * DRAW3));\n};\n\nvoid () ogre_swing6 = [ 38, ogre_swing7 ]\n{\n};\n\nvoid () ogre_swing7 = [ 39, ogre_swing8 ]\n{\n};\n\nvoid () ogre_swing8 = [ 40, ogre_swing9 ]\n{\n\tchainsaw (MULTICAST_ALL);\n\tself.angles_y = (self.angles_y + (random () * DRAW3));\n};\n\nvoid () ogre_swing9 = [ 41, ogre_swing10 ]\n{\n};\n\nvoid () ogre_swing10 = [ 42, ogre_swing11 ]\n{\n};\n\nvoid () ogre_swing11 = [ 43, ogre_swing12 ]\n{\n};\n\nvoid () ogre_swing12 = [ 44, ogre_swing13 ]\n{\n\tai_charge (AS_MELEE);\n};\n\nvoid () ogre_swing13 = [ 45, ogre_swing14 ]\n{\n\tai_charge (SECRET_NO_SHOOT);\n};\n\nvoid () ogre_swing14 = [ 46, ogre_run1 ]\n{\n\tai_charge (TE_LIGHTNING3);\n};\n\nvoid () ogre_smash1 = [ 47, ogre_smash2 ]\n{\n\tai_charge (TE_LIGHTNING2);\n\tsound (self, CHAN_WEAPON, \"ogre/ogsawatk.wav\", PLAT_LOW_TRIGGER, ATTN_NORM);\n};\n\nvoid () ogre_smash2 = [ 48, ogre_smash3 ]\n{\n\tai_charge (MULTICAST_ALL);\n};\n\nvoid () ogre_smash3 = [ 49, ogre_smash4 ]\n{\n\tai_charge (MULTICAST_ALL);\n};\n\nvoid () ogre_smash4 = [ 50, ogre_smash5 ]\n{\n\tai_charge (PLAT_LOW_TRIGGER);\n};\n\nvoid () ogre_smash5 = [ 51, ogre_smash6 ]\n{\n\tai_charge (SECRET_1ST_DOWN);\n};\n\nvoid () ogre_smash6 = [ 52, ogre_smash7 ]\n{\n\tai_charge (SECRET_1ST_DOWN);\n\tchainsaw (MULTICAST_ALL);\n};\n\nvoid () ogre_smash7 = [ 53, ogre_smash8 ]\n{\n\tai_charge (SECRET_1ST_DOWN);\n\tchainsaw (MULTICAST_ALL);\n};\n\nvoid () ogre_smash8 = [ 54, ogre_smash9 ]\n{\n\tai_charge (TE_LAVASPLASH);\n\tchainsaw (MULTICAST_ALL);\n};\n\nvoid () ogre_smash9 = [ 55, ogre_smash10 ]\n{\n\tai_charge (TE_LIGHTNINGBLOOD);\n\tchainsaw (MULTICAST_ALL);\n};\n\nvoid () ogre_smash10 = [ 56, ogre_smash11 ]\n{\n\tchainsaw (PLAT_LOW_TRIGGER);\n};\n\nvoid () ogre_smash11 = [ 57, ogre_smash12 ]\n{\n\tai_charge (SILENT);\n\tchainsaw (MULTICAST_ALL);\n\tself.nextthink = (self.nextthink + (random () * 0.2));\n};\n\nvoid () ogre_smash12 = [ 58, ogre_smash13 ]\n{\n\tai_charge (0);\n};\n\nvoid () ogre_smash13 = [ 59, ogre_smash14 ]\n{\n\tai_charge (SECRET_1ST_DOWN);\n};\n\nvoid () ogre_smash14 = [ 60, ogre_run1 ]\n{\n\tai_charge (TE_BLOOD);\n};\n\nvoid () ogre_nail1 = [ 61, ogre_nail2 ]\n{\n\tai_face ();\n};\n\nvoid () ogre_nail2 = [ 62, ogre_nail3 ]\n{\n\tai_face ();\n};\n\nvoid () ogre_nail3 = [ 62, ogre_nail4 ]\n{\n\tai_face ();\n};\n\nvoid () ogre_nail4 = [ 63, ogre_nail5 ]\n{\n\tai_face ();\n};\n\nvoid () ogre_nail5 = [ 64, ogre_nail6 ]\n{\n\tai_face ();\n};\n\nvoid () ogre_nail6 = [ 65, ogre_nail7 ]\n{\n\tai_face ();\n\tLobAGrenade();\n};\n\nvoid () ogre_nail7 = [ 66, ogre_run1 ]\n{\n\tai_face ();\n};\n\nvoid () ogre_naila = [ 61, ogre_nailb ]\n{\n\tai_face ();\n};\n\nvoid () ogre_nailb = [ 62, ogre_nailc ]\n{\n\tai_face ();\n};\n\nvoid () ogre_nailc = [ 62, ogre_naild ]\n{\n\tai_face ();\n};\n\nvoid () ogre_naild = [ 63, ogre_naile ]\n{\n\tai_face ();\n};\n\nvoid () ogre_naile = [ 64, ogre_nailf ]\n{\n\tai_face ();\n\tLobAGrenade();\n};\n\nvoid () ogre_nailf = [ 65, ogre_nailg ]\n{\n\tai_face ();\n};\n\nvoid () ogre_nailg = [ 66, ogre_run1 ]\n{\n\tai_face ();\n};\n\nvoid () ogre_pain1 = [ 67, ogre_pain2 ]\n{\n};\n\nvoid () ogre_pain2 = [ 68, ogre_pain3 ]\n{\n};\n\nvoid () ogre_pain3 = [ 69, ogre_pain4 ]\n{\n};\n\nvoid () ogre_pain4 = [ 70, ogre_pain5 ]\n{\n};\n\nvoid () ogre_pain5 = [ 71, ogre_run1 ]\n{\n};\n\nvoid () ogre_painb1 = [ 72, ogre_painb2 ]\n{\n};\n\nvoid () ogre_painb2 = [ 73, ogre_painb3 ]\n{\n};\n\nvoid () ogre_painb3 = [ 74, ogre_run1 ]\n{\n};\n\nvoid () ogre_painc1 = [ 75, ogre_painc2 ]\n{\n};\n\nvoid () ogre_painc2 = [ 76, ogre_painc3 ]\n{\n};\n\nvoid () ogre_painc3 = [ 77, ogre_painc4 ]\n{\n};\n\nvoid () ogre_painc4 = [ 78, ogre_painc5 ]\n{\n};\n\nvoid () ogre_painc5 = [ 79, ogre_painc6 ]\n{\n};\n\nvoid () ogre_painc6 = [ 80, ogre_run1 ]\n{\n};\n\nvoid () ogre_paind1 = [ 81, ogre_paind2 ]\n{\n};\n\nvoid () ogre_paind2 = [ 82, ogre_paind3 ]\n{\n\tai_pain (TE_LAVASPLASH);\n};\n\nvoid () ogre_paind3 = [ 83, ogre_paind4 ]\n{\n\tai_pain (TE_LIGHTNING3);\n};\n\nvoid () ogre_paind4 = [ 84, ogre_paind5 ]\n{\n\tai_pain (SECRET_1ST_DOWN);\n};\n\nvoid () ogre_paind5 = [ 85, ogre_paind6 ]\n{\n};\n\nvoid () ogre_paind6 = [ 86, ogre_paind7 ]\n{\n};\n\nvoid () ogre_paind7 = [ 87, ogre_paind8 ]\n{\n};\n\nvoid () ogre_paind8 = [ 88, ogre_paind9 ]\n{\n};\n\nvoid () ogre_paind9 = [ 89, ogre_paind10 ]\n{\n};\n\nvoid () ogre_paind10 = [ 90, ogre_paind11 ]\n{\n};\n\nvoid () ogre_paind11 = [ 91, ogre_paind12 ]\n{\n};\n\nvoid () ogre_paind12 = [ 92, ogre_paind13 ]\n{\n};\n\nvoid () ogre_paind13 = [ 93, ogre_paind14 ]\n{\n};\n\nvoid () ogre_paind14 = [ 94, ogre_paind15 ]\n{\n};\n\nvoid () ogre_paind15 = [ 95, ogre_paind16 ]\n{\n};\n\nvoid () ogre_paind16 = [ 96, ogre_run1 ]\n{\n};\n\nvoid () ogre_paine1 = [ 97, ogre_paine2 ]\n{\n};\n\nvoid () ogre_paine2 = [ 98, ogre_paine3 ]\n{\n\tai_pain (TE_LAVASPLASH);\n};\n\nvoid () ogre_paine3 = [ 99, ogre_paine4 ]\n{\n\tai_pain (TE_LIGHTNING3);\n};\n\nvoid () ogre_paine4 = [ 100, ogre_paine5 ]\n{\n\tai_pain (SECRET_1ST_DOWN);\n};\n\nvoid () ogre_paine5 = [ 101, ogre_paine6 ]\n{\n};\n\nvoid () ogre_paine6 = [ 102, ogre_paine7 ]\n{\n};\n\nvoid () ogre_paine7 = [ 103, ogre_paine8 ]\n{\n};\n\nvoid () ogre_paine8 = [ 104, ogre_paine9 ]\n{\n};\n\nvoid () ogre_paine9 = [ 105, ogre_paine10 ]\n{\n};\n\nvoid () ogre_paine10 = [ 106, ogre_paine11 ]\n{\n};\n\nvoid () ogre_paine11 = [ 107, ogre_paine12 ]\n{\n};\n\nvoid () ogre_paine12 = [ 108, ogre_paine13 ]\n{\n};\n\nvoid () ogre_paine13 = [ 109, ogre_paine14 ]\n{\n};\n\nvoid () ogre_paine14 = [ 110, ogre_paine15 ]\n{\n};\n\nvoid () ogre_paine15 = [ 111, ogre_run1 ]\n{\n};\n\nvoid (entity attacker, float damage) ogre_pain =\n{\n\tif (self.pain_finished > time)\n\t\treturn;\n\n\tif (random()*8 <= 1)\n\t{\n\t\togre_paind1();\n\t\tself.pain_finished = time + 7;\n\t\tsound (self, CHAN_VOICE, \"ogre/ogpain1.wav\", PLAT_LOW_TRIGGER, ATTN_NORM);\n\t\treturn;\n\t}\n};\n\nvoid () ogre_die1 = [ 112, ogre_die2 ]\n{\n\tself.solid = SOLID_NOT;\n};\n\nvoid () ogre_die2 = [ 113, ogre_die3 ]\n{\n};\n\nvoid () ogre_die3 = [ 114, ogre_die4 ]\n{\n\tDropBackpack ();\n\tDropBackpack ();\n\tDropBackpack ();\n};\n\nvoid () ogre_die4 = [ 115, ogre_die5 ]\n{\n};\n\nvoid () ogre_die5 = [ 116, ogre_die6 ]\n{\n};\n\nvoid () ogre_die6 = [ 117, ogre_die7 ]\n{\n};\n\nvoid () ogre_die7 = [ 118, ogre_die8 ]\n{\n};\n\nvoid () ogre_die8 = [ 119, ogre_die9 ]\n{\n};\n\nvoid () ogre_die9 = [ 120, ogre_die10 ]\n{\n};\n\nvoid () ogre_die10 = [ 121, ogre_die11 ]\n{\n};\n\nvoid () ogre_die11 = [ 122, ogre_die12 ]\n{\n};\n\nvoid () ogre_die12 = [ 123, ogre_die13 ]\n{\n};\n\nvoid () ogre_die13 = [ 124, ogre_die14 ]\n{\n};\n\nvoid () ogre_die14 = [ 125, ogre_die14 ]\n{\n};\n\nvoid () ogre_bdie1 = [ 126, ogre_bdie2 ]\n{\n};\n\nvoid () ogre_bdie2 = [ 127, ogre_bdie3 ]\n{\n\tai_forward (MULTICAST_PVS_R);\n};\n\nvoid () ogre_bdie3 = [ 128, ogre_bdie4 ]\n{\n\tself.solid = SOLID_NOT;\n\tself.ammo_rockets = SILENT;\n\tDropBackpack ();\n};\n\nvoid () ogre_bdie4 = [ 129, ogre_bdie5 ]\n{\n\tai_forward (PLAT_LOW_TRIGGER);\n};\n\nvoid () ogre_bdie5 = [ 130, ogre_bdie6 ]\n{\n\tai_forward (AS_MELEE);\n};\n\nvoid () ogre_bdie6 = [ 131, ogre_bdie7 ]\n{\n\tai_forward (TE_WIZSPIKE);\n};\n\nvoid () ogre_bdie7 = [ 132, ogre_bdie8 ]\n{\n\tai_forward (DRAW3);\n};\n\nvoid () ogre_bdie8 = [ 133, ogre_bdie9 ]\n{\n};\n\nvoid () ogre_bdie9 = [ 134, ogre_bdie10 ]\n{\n};\n\nvoid () ogre_bdie10 = [ 135, ogre_bdie10 ]\n{\n};\n\n\nvoid () mutant_pain =\n{\n\tif (self.frame == 69)\n\t{\n\t\tself.frame = 70;\n\t\tself.health = 180;\n\t\tself.think = army_die1;\n\t\tself.nextthink = time + 0.12;\n\t\tself.think = ogre_die1;\n\t}\n\telse\n\t{\n\t\tThrowGib (\"progs/zom_gib.mdl\", -40);\n\t\tself.frame = 69;\n\t\tself.health = 180;\n\t\tself.think = ogre_die1;\n\t\tself.nextthink = time + 0.12;\n\t}\n\n\tself.attack = self.attack + 1;\n\n\tif (self.attack == 8)\n\t{\n\t\tif (random()*4 <= 2)\n\t\t\tsound (self, CHAN_VOICE, \"player/agdie1.wav\", 1, ATTN_NORM);\n\t\telse\n\t\t\tsound (self, CHAN_VOICE, \"player/udeath.wav\", 1, ATTN_NORM);\n\t}\n\tif (self.attack >= 16)\n\t{\n\t\tself.solid = SOLID_NOT;\n\n\t\tself.think = ogre_bdie1;\n\t\tThrowGib (\"progs/zom_gib.mdl\", -35);\n\t\tThrowGib (\"progs/zom_gib.mdl\", -35);\n\t\tThrowGib (\"progs/gib1.mdl\", -35);\n\t}\n};\n\n\nvoid (vector stuff, vector ang) spawn_live_ogre =\n{\n\tlocal entity ogre;\n\n\togre = spawn ();\n\togre.origin = stuff;\n\togre.enemy = world;\n\togre.attack_finished = time + 10;\n\togre.solid = SOLID_SLIDEBOX;\n\togre.movetype = MOVETYPE_STEP;\n\togre.takedamage = DAMAGE_YES;\n\tsetmodel (ogre, \"progs/ogre.mdl\");\n\tsetsize (ogre, '-20 -20 -24', '20 20 36');\n\togre.classname = \"body\";\n\togre.netname = \"dead\";\n\togre.health = 180;\n\togre.angles = ang;\n\togre.max_health = ogre.health;\n\togre.th_pain = mutant_pain;\n\togre.think = mutant_pain;\n\togre.nextthink = time + 0.05;\n};\n\nvoid () ogre_die =\n{\n\tif (self.enemy.classname == \"player\")\n\t\tself.enemy.frags = self.enemy.frags + 1;\n\n\tif (self.health <= -35)\n\t{\n\t\tself.solid = SOLID_NOT;\n\t\tsound (self, CHAN_VOICE, \"player/udeath.wav\", 1, ATTN_NORM);\n\t\tThrowGib (\"progs/zom_gib.mdl\", -30);\n\t\tThrowGib (\"progs/zom_gib.mdl\", -35);\n\t\togre_bdie1 ();\n\t}\n\telse\n\t{\n\t\tself.solid = SOLID_NOT;\n\t\tspawn_live_ogre(self.origin, self.angles);\n\t\tremove(self);\n\t}\n\n\tsound (self, CHAN_VOICE, \"ogre/ogpain1.wav\", 1, ATTN_NORM);\n};\n\nvoid () ogre_melee =\n{\n\tif ((random () > 0.5))\n\t{\n\t\togre_smash1 ();\n\t}\n\telse\n\t{\n\t\togre_swing1 ();\n\t}\n};\n\nvoid () monster_ogre =\n{\n\tprecache_model (\"progs/ogre.mdl\");\n\tprecache_model (\"progs/h_ogre.mdl\");\n\tprecache_model (\"progs/grenade.mdl\");\n\tprecache_sound (\"ogre/ogdrag.wav\");\n\tprecache_sound (\"ogre/ogdth.wav\");\n\tprecache_sound (\"ogre/ogidle.wav\");\n\tprecache_sound (\"ogre/ogidle2.wav\");\n\tprecache_sound (\"ogre/ogpain1.wav\");\n\tprecache_sound (\"ogre/ogsawatk.wav\");\n\tprecache_sound (\"ogre/ogwake.wav\");\n\tself.solid = SOLID_SLIDEBOX;\n\tself.movetype = MOVETYPE_STEP;\n\tsetmodel (self, \"progs/ogre.mdl\");\n\tself.netname = \"mutant\";\n\tself.classname = \"monster\";\n\tsetsize (self, '-24 -24 -24', '24 24 48');\n\tself.health = 280;\n\tself.team = 3;\n\tself.armorvalue = 0;\n\tself.armornoise = \"misc/thud.wav\";\n\tself.th_stand = ogre_stand1;\n\tself.th_walk = ogre_walk1;\n\tself.th_run = ogre_run1;\n\tself.th_die = ogre_die;\n\tself.th_melee = ogre_melee;\n\tself.th_missile = ogre_nail1;\n\tself.th_pain = ogre_pain;\n\tself.armortype = 0;\n\twalkmonster_start ();\n};\n\n\nvoid () monster_ogre_marksman =\n{\n\tmonster_ogre ();\n};\n"
  },
  {
    "path": "quakec/fallout2/plats.qc",
    "content": "\n\nvoid() plat_center_touch;\n//void() plat_outside_touch;\nvoid() plat_trigger_use;\nvoid() plat_go_up;\nvoid() plat_go_down;\nvoid() plat_crush;\nfloat PLAT_LOW_TRIGGER = 1;\n\nvoid() plat_spawn_inside_trigger =\n{\n\tlocal entity\ttrigger;\n\tlocal vector\ttmin, tmax;\n\n//\n// middle trigger\n//\t\n\ttrigger = spawn();\n\ttrigger.touch = plat_center_touch;\n\ttrigger.team = self.team;\n\ttrigger.movetype = MOVETYPE_NONE;\n\ttrigger.solid = SOLID_TRIGGER;\n\ttrigger.enemy = self;\n\t\n\ttmin = self.mins + '25 25 0';\n\ttmax = self.maxs - '25 25 -8';\n\ttmin_z = tmax_z - (self.pos1_z - self.pos2_z + 8);\n\tif (self.spawnflags & PLAT_LOW_TRIGGER)\n\t\ttmax_z = tmin_z + 8;\n\t\n\tif (self.size_x <= 50)\n\t{\n\t\ttmin_x = (self.mins_x + self.maxs_x) / 2;\n\t\ttmax_x = tmin_x + 1;\n\t}\n\tif (self.size_y <= 50)\n\t{\n\t\ttmin_y = (self.mins_y + self.maxs_y) / 2;\n\t\ttmax_y = tmin_y + 1;\n\t}\n\t\n\tsetsize (trigger, tmin, tmax);\n};\n\nvoid() plat_hit_top =\n{\n\tsound (self, CHAN_NO_PHS_ADD+CHAN_VOICE, self.noise1, 1, ATTN_NORM);\n\tself.state = STATE_TOP;\n\tself.think = plat_go_down;\n\tself.nextthink = self.ltime + 3;\n};\n\nvoid() plat_hit_bottom =\n{\n\tsound (self, CHAN_NO_PHS_ADD+CHAN_VOICE, self.noise1, 1, ATTN_NORM);\n\tself.state = STATE_BOTTOM;\n};\n\nvoid() plat_go_down =\n{\n\tsound (self, CHAN_VOICE, self.noise, 1, ATTN_NORM);\n\tself.state = STATE_DOWN;\n\tSUB_CalcMove (self.pos2, self.speed, plat_hit_bottom);\n};\n\nvoid() plat_go_up =\n{\n\tsound (self, CHAN_VOICE, self.noise, 1, ATTN_NORM);\n\tself.state = STATE_UP;\n\tSUB_CalcMove (self.pos1, self.speed, plat_hit_top);\n};\n\nvoid() plat_center_touch =\n{\n\tif (!triggercantouch(self, other))\n\t\treturn;\n\n\tif (other.classname != \"player\")\n\t\treturn;\n\t\t\n\tif (other.health <= 0)\n\t\treturn;\n\n\tself = self.enemy;\n\tif (self.state == STATE_BOTTOM)\n\t\tplat_go_up ();\n\telse if (self.state == STATE_TOP)\n\t\tself.nextthink = self.ltime + 1;\t// delay going down\n};\n/*\nvoid() plat_outside_touch =\n{\n\tif (!triggercantouch(self, other))\n\t\treturn;\n\n\tif (other.classname != \"player\")\n\t\treturn;\n\n\tif (other.health <= 0)\n\t\treturn;\n\t\t\n//dprint (\"plat_outside_touch\\n\");\n\tself = self.enemy;\n\tif (self.state == STATE_TOP)\n\t\tplat_go_down ();\n};\n*/\nvoid() plat_trigger_use =\n{\n\tif (self.think)\n\t\treturn;\t\t// allready activated\n\tplat_go_down();\n};\n\n\nvoid() plat_crush =\n{\n//dprint (\"plat_crush\\n\");\n\n\tother.deathtype = \"squish\";\n\tT_Damage (other, self, self, 1);\n\t\n\tif (self.state == STATE_UP)\n\t\tplat_go_down ();\n\telse if (self.state == STATE_DOWN)\n\t\tplat_go_up ();\n\telse\n\t\tobjerror (\"plat_crush: bad self.state\\n\");\n};\n\nvoid() plat_use =\n{\n\tself.use = SUB_Null;\n\tif (self.state != STATE_UP)\n\t\tobjerror (\"plat_use: not in up state\");\n\tplat_go_down();\n};\n\n\n/*QUAKED func_plat (0 .5 .8) ? PLAT_LOW_TRIGGER\nspeed\tdefault 150\n\nPlats are always drawn in the extended position, so they will light correctly.\n\nIf the plat is the target of another trigger or button, it will start out disabled in the extended position until it is trigger, when it will lower and become a normal plat.\n\nIf the \"height\" key is set, that will determine the amount the plat moves, instead of being implicitly determined by the model's height.\nSet \"sounds\" to one of the following:\n1) base fast\n2) chain slow\n*/\n\n\nvoid() func_plat =\n{\n\tif (!self.t_length)\n\t\tself.t_length = 80;\n\tif (!self.t_width)\n\t\tself.t_width = 10;\n\n\tif (self.sounds == 0)\n\t\tself.sounds = 2;\n// FIX THIS TO LOAD A GENERIC PLAT SOUND\n\n\tif (self.sounds == 1)\n\t{\n\t\tprecache_sound (\"plats/plat1.wav\");\n\t\tprecache_sound (\"plats/plat2.wav\");\n\t\tself.noise = \"plats/plat1.wav\";\n\t\tself.noise1 = \"plats/plat2.wav\";\n\t}\n\n\tif (self.sounds == 2)\n\t{\n\t\tprecache_sound (\"plats/medplat1.wav\");\n\t\tprecache_sound (\"plats/medplat2.wav\");\n\t\tself.noise = \"plats/medplat1.wav\";\n\t\tself.noise1 = \"plats/medplat2.wav\";\n\t}\n\n\n\tself.mangle = self.angles;\n\tself.angles = '0 0 0';\n\n\tself.classname = \"plat\";\n\tself.solid = SOLID_BSP;\n\tself.movetype = MOVETYPE_PUSH;\n\tsetorigin (self, self.origin);\t\n\tsetmodel (self, self.model);\n\tsetsize (self, self.mins , self.maxs);\n\n\tself.blocked = plat_crush;\n\tif (!self.speed)\n\t\tself.speed = 150;\n\n// pos1 is the top position, pos2 is the bottom\n\tself.pos1 = self.origin;\n\tself.pos2 = self.origin;\n\tif (self.height)\n\t\tself.pos2_z = self.origin_z - self.height;\n\telse\n\t\tself.pos2_z = self.origin_z - self.size_z + 8;\n\n\tself.use = plat_trigger_use;\n\n\tplat_spawn_inside_trigger ();\t// the \"start moving\" trigger\t\n\n\tif (self.targetname)\n\t{\n\t\tself.state = STATE_UP;\n\t\tself.use = plat_use;\n\t}\n\telse\n\t{\n\t\tsetorigin (self, self.pos2);\n\t\tself.state = STATE_BOTTOM;\n\t}\n};\n\n//============================================================================\n\nvoid() train_next;\nvoid() func_train_find;\n\nvoid() train_blocked =\n{\n\tif (time < self.attack_finished)\n\t\treturn;\n\tself.attack_finished = time + 0.5;\n\tother.deathtype = \"squish\";\n\tT_Damage (other, self, self, self.dmg);\n};\n\nvoid() train_use =\n{\n\tif (self.think != func_train_find)\n\t\treturn;\t\t// already activated\n\ttrain_next();\n};\n\nvoid() train_wait =\n{\n\tif (self.wait)\n\t{\n\t\tself.nextthink = self.ltime + self.wait;\n\t\tsound (self, CHAN_NO_PHS_ADD+CHAN_VOICE, self.noise, 1, ATTN_NORM);\n\t}\n\telse\n\t\tself.nextthink = self.ltime + 0.1;\n\t\n\tself.think = train_next;\n};\n\nvoid() train_next =\n{\n\tlocal entity\ttarg;\n\n\ttarg = find (world, targetname, self.target);\n\tself.target = targ.target;\n\tif (!self.target)\n\t\tobjerror (\"train_next: no next target\");\n\tif (targ.wait)\n\t\tself.wait = targ.wait;\n\telse\n\t\tself.wait = 0;\n\tsound (self, CHAN_VOICE, self.noise1, 1, ATTN_NORM);\n\tSUB_CalcMove (targ.origin - self.mins, self.speed, train_wait);\n};\n\nvoid() func_train_find =\n\n{\n\tlocal entity\ttarg;\n\n\ttarg = find (world, targetname, self.target);\n\tself.target = targ.target;\n\tsetorigin (self, targ.origin - self.mins);\n\tif (!self.targetname)\n\t{\t// not triggered, so start immediately\n\t\tself.nextthink = self.ltime + 0.1;\n\t\tself.think = train_next;\n\t}\n};\n\n/*QUAKED func_train (0 .5 .8) ?\nTrains are moving platforms that players can ride.\nThe targets origin specifies the min point of the train at each corner.\nThe train spawns at the first target it is pointing at.\nIf the train is the target of a button or trigger, it will not begin moving until activated.\nspeed\tdefault 100\ndmg\t\tdefault\t2\nsounds\n1) ratchet metal\n\n*/\nvoid() func_train =\n{\t\n\tif (!self.speed)\n\t\tself.speed = 100;\n\tif (!self.target)\n\t\tobjerror (\"func_train without a target\");\n\tif (!self.dmg)\n\t\tself.dmg = 2;\n\n\tif (self.sounds == 0)\n\t{\n\t\tself.noise = (\"misc/null.wav\");\n\t\tprecache_sound (\"misc/null.wav\");\n\t\tself.noise1 = (\"misc/null.wav\");\n\t\tprecache_sound (\"misc/null.wav\");\n\t}\n\n\tif (self.sounds == 1)\n\t{\n\t\tself.noise = (\"plats/train2.wav\");\n\t\tprecache_sound (\"plats/train2.wav\");\n\t\tself.noise1 = (\"plats/train1.wav\");\n\t\tprecache_sound (\"plats/train1.wav\");\n\t}\n\n\tself.cnt = 1;\n\tself.solid = SOLID_BSP;\n\tself.movetype = MOVETYPE_PUSH;\n\tself.blocked = train_blocked;\n\tself.use = train_use;\n\tself.classname = \"train\";\n\n\tsetmodel (self, self.model);\n\tsetsize (self, self.mins , self.maxs);\n\tsetorigin (self, self.origin);\n\n// start trains on the second frame, to make sure their targets have had\n// a chance to spawn\n\tself.nextthink = self.ltime + 0.1;\n\tself.think = func_train_find;\n};\n\n/*QUAKED misc_teleporttrain (0 .5 .8) (-8 -8 -8) (8 8 8)\nThis is used for the final bos\n*/\nvoid() misc_teleporttrain =\n{\t\n\tif (!self.speed)\n\t\tself.speed = 100;\n\tif (!self.target)\n\t\tobjerror (\"func_train without a target\");\n\n\tself.cnt = 1;\n\tself.solid = SOLID_NOT;\n\tself.movetype = MOVETYPE_PUSH;\n\tself.blocked = train_blocked;\n\tself.use = train_use;\n\tself.avelocity = '100 200 300';\n\n\tself.noise = (\"misc/null.wav\");\n\tprecache_sound (\"misc/null.wav\");\n\tself.noise1 = (\"misc/null.wav\");\n\tprecache_sound (\"misc/null.wav\");\n\n\tprecache_model2 (\"progs/teleport.mdl\");\n\tsetmodel (self, \"progs/teleport.mdl\");\n\tsetsize (self, self.mins , self.maxs);\n\tsetorigin (self, self.origin);\n\n// start trains on the second frame, to make sure their targets have had\n// a chance to spawn\n\tself.nextthink = self.ltime + 0.1;\n\tself.think = func_train_find;\n};\n\n"
  },
  {
    "path": "quakec/fallout2/player.qc",
    "content": "void () bubble_bob;\nvoid () make_bubbles;\nvoid () bubble_remove;\nvoid () PlayerDead;\nvoid () player_diea1;\n\nvoid () player_duck = [ 45, player_run ]\n{\n};\n\nvoid () player_lay = [ 45, player_run ]\n{\n};\n\nvoid () Footstep =\n{\n\tlocal float rand;\n\tlocal float r;\n\n\trand = random ();\n\n\tif (self.sneak != 0)\n\t\treturn;\n\n\tif (self.perk == 5)\n\t\tr = 0.5;\n\telse\n\t{\n\t\tr = 1;\n\t\tif (random()*4<=1)\n\t\t\tself.show_hostile = time + 0.1;\n\t}\n\n\tif (world.worldtype == 1 || world.worldtype == 2)\n\t{\n\t\tif ((rand < 0.25))\n\t\t\tsound (self, CHAN_ITEM, \"player/step1.wav\", r, ATTN_NORM);\n\t\telse if ((rand < 0.5))\n\t\t\tsound (self, CHAN_ITEM, \"player/step2.wav\", r, ATTN_NORM);\n\t\telse if ((rand < 0.75))\n\t\t\tsound (self, CHAN_ITEM, \"player/step3.wav\", r, ATTN_NORM);\n\t\telse\n\t\t\tsound (self, CHAN_ITEM, \"player/step4.wav\", r, ATTN_NORM);\n\t}\n\tif (world.worldtype == 0)\n\t{\n\t\tif ((rand < 0.25))\n\t\t\tsound (self, CHAN_ITEM, \"player/step1a.wav\", r, ATTN_NORM);\n\t\telse if ((rand < 0.5))\n\t\t\tsound (self, CHAN_ITEM, \"player/step2a.wav\", r, ATTN_NORM);\n\t\telse if ((rand < 0.75))\n\t\t\tsound (self, CHAN_ITEM, \"player/step3a.wav\", r, ATTN_NORM);\n\t\telse\n\t\t\tsound (self, CHAN_ITEM, \"player/step4a.wav\", r, ATTN_NORM);\n\t}\n};\n\nfloat (entity guy) holding_melee =\n{\n\tlocal float iid;\n\tiid = ToIID(ItemInSlot(guy, guy.current_slot));\n\n\tif (iid == IID_NONE ||\n\t    iid == IID_WP_KNIFE ||\n\t    iid == IID_WP_AXE ||\n\t    iid == IID_WP_VIBROBLADE ||\n\t    iid == IID_WP_POWERAXE)\n\t\treturn true;\n\treturn false;\n};\nvoid () player_crouch;\n\nvoid () player_stand1 = [ 149, player_stand1 ]\n{\n\tif (self.rtime < time && self.attack == 0)\n\t\tself.weaponframe = 0;\n\n\tif (self.position == 1)\n\t{\n\t\tplayer_crouch ();\n\t\treturn;\n\t}\n\telse\n\t{\n\t\tif (self.position == 2)\n\t\t{\n\t\t\tplayer_lay ();\n\t\t\treturn;\n\t\t}\n\t}\n\tif (self.velocity_x || self.velocity_y)\n\t{\n\t\tself.walkframe = 0;\n\t\tplayer_run ();\n\t\treturn;\n\t}\n\tif (self.walkframe >= 5)\n\t\tself.walkframe = 0;\n\n\tself.frame = 149 + self.walkframe;\n\tself.walkframe = self.walkframe + 1;\n};\n\nvoid () player_crouch = [ 45, player_run ]\n{\n\tif (self.rtime < time && self.attack == 0)\n\t\tself.weaponframe = 0;\n\n\tif (!self.velocity_x && !self.velocity_y)\n\t{\n\t\tself.frame = 45;\n\t\treturn;\n\t}\n\telse\n\t{\n\t\tif (self.position == 2)\n\t\t{\n\t\t\tplayer_lay ();\n\t\t\treturn;\n\t\t}\n\t}\n\tself.frame = (36 + self.walkframe);\n\tif ((self.walkframe >= TE_TELEPORT))\n\t{\n\t\tself.walkframe = MULTICAST_ALL;\n\t}\n\tself.walkframe = (self.walkframe + WEAPON_SHOTGUN);\n};\n\nvoid () player_climb = [ 23, player_run ]\n{\n\tself.weaponframe = 0;\n\tif (self.velocity == '0 0 0')\n\t{\n\t\tself.frame = 25;\n\t\treturn;\n\t}\n\tself.frame = (23 + self.walkframe);\n\tif ((self.walkframe >= 10))\n\t{\n\t\tself.walkframe = 0;\n\t}\n\tself.walkframe = (self.walkframe + 1);\n};\n\nvoid () player_run = [ 137, player_run ]\n{\n\tif (self.rtime < time)\n\t\tself.weaponframe = 0;\n\n\tif (self.equipment_slot)\n\t\tif ((ToIID(ItemInSlot(self, self.equipment_slot))) == IID_EQUIP_CLIMBINGGEAR)\n\t\t{\n\t\t\tplayer_climb();\n\t\t\treturn;\n\t\t}\n\n\tif ((!self.velocity_x && !self.velocity_y))\n\t{\n\t\tplayer_stand1 ();\n\t\treturn;\n\t}\n\tif ((self.position == 1))\n\t{\n\t\tplayer_crouch ();\n\t\treturn;\n\t}\n\telse\n\t{\n\t\tif ((self.position == 2))\n\t\t{\n\t\t\tplayer_lay ();\n\t\t\treturn;\n\t\t}\n\t}\n\tif (self.walkframe == 1 && self.ghost == 0 && self.position == 0 && self.velocity_z == 0)\n\t\tFootstep ();\n\n\tif (self.walkframe == 4 && self.ghost == 0 && self.position == 0 && self.velocity_z == 0)\n\t\tFootstep ();\n\n\tif (self.walkframe == 7 && self.ghost == 0 && self.position == 0 && self.velocity_z == 0)\n\t\tFootstep ();\n\n\tself.frame = (137 + self.walkframe);\n\tif (self.walkframe >= 9)\n\t\tself.walkframe = 0;\n\n\tself.walkframe = (self.walkframe + 1);\n};\n\nvoid () player_reload1 = [ 123, player_reload2 ]\n{\n};\n\nvoid () player_reload2 = [ 124, player_reload3 ]\n{\n};\n\nvoid () player_reload3 = [ 125, player_reload4 ]\n{\n};\n\nvoid () player_reload4 = [ 126, player_reload5 ]\n{\n};\n\nvoid () player_reload5 = [ 127, player_reload6 ]\n{\n};\n\nvoid () player_reload6 = [ 128, player_reload7 ]\n{\n};\n\nvoid () player_reload7 = [ 129, player_reload8 ]\n{\n};\n\nvoid () player_reload8 = [ 130, player_reload9 ]\n{\n};\n\nvoid () player_reload9 = [ 131, player_reload10 ]\n{\n};\n\nvoid () player_reload10 = [ 132, player_reload11 ]\n{\n};\n\nvoid () player_reload11 = [ 133, player_reload12 ]\n{\n};\n\nvoid () player_reload12 = [ 134, player_reload13 ]\n{\n};\n\nvoid () player_reload13 = [ 135, player_reload14 ]\n{\n};\n\nvoid () player_reload14 = [ 136, player_run ]\n{\n};\n\nvoid () player_creload1 = [ 74, player_creload2 ]\n{\n};\n\nvoid () player_creload2 = [ 75, player_creload3 ]\n{\n};\n\nvoid () player_creload3 = [ 76, player_creload4 ]\n{\n};\n\nvoid () player_creload4 = [ 77, player_creload5 ]\n{\n};\n\nvoid () player_creload5 = [ 78, player_creload6 ]\n{\n};\n\nvoid () player_creload6 = [ 79, player_creload7 ]\n{\n};\n\nvoid () player_creload7 = [ 80, player_creload8 ]\n{\n};\n\nvoid () player_creload8 = [ 81, player_creload9 ]\n{\n};\n\nvoid () player_creload9 = [ 82, player_creload10 ]\n{\n};\n\nvoid () player_creload10 = [ 83, player_creload11 ]\n{\n};\n\nvoid () player_creload11 = [ 84, player_creload12 ]\n{\n};\n\nvoid () player_creload12 = [ 85, player_creload13 ]\n{\n};\n\nvoid () player_creload13 = [ 86, player_creload14 ]\n{\n};\n\nvoid () player_creload14 = [ 87, player_run ]\n{\n};\n\nvoid () player_use1 = [ 155, player_use2 ]\n{\n};\n\nvoid () player_use2 = [ 156, player_use3 ]\n{\n};\n\nvoid () player_use3 = [ 157, player_use4 ]\n{\n};\n\nvoid () player_use4 = [ 158, player_use5 ]\n{\n};\n\nvoid () player_use5 = [ 159, player_use6 ]\n{\n};\n\nvoid () player_use6 = [ 160, player_use7 ]\n{\n};\n\nvoid () player_use7 = [ 161, player_use8 ]\n{\n};\n\nvoid () player_use8 = [ 162, player_use9 ]\n{\n};\n\nvoid () player_use9 = [ 163, player_use10 ]\n{\n};\n\nvoid () player_use10 = [ 164, player_use11 ]\n{\n};\n\nvoid () player_use11 = [ 165, player_use12 ]\n{\n};\n\nvoid () player_use12 = [ 166, player_use13 ]\n{\n};\n\nvoid () player_use13 = [ 167, player_use14 ]\n{\n};\n\nvoid () player_use14 = [ 168, player_use15 ]\n{\n};\n\nvoid () player_use15 = [ 169, player_use16 ]\n{\n};\n\nvoid () player_use16 = [ 170, player_run ]\n{\n};\n\nvoid () player_holster1 = [ 107, player_holster2 ]\n{\n\tself.attack_finished = (time + 0.25);\n};\n\nvoid () player_holster2 = [ 109, player_holster3 ]\n{\n};\n\nvoid () player_holster3 = [ 111, player_holster4 ]\n{\n};\n\nvoid () player_holster4 = [ 112, player_holster5 ]\n{\n};\n\nvoid () player_holster5 = [ 113, player_holster6 ]\n{\n};\n\nvoid () player_holster6 = [ 114, player_holster7 ]\n{\n};\n\nvoid () player_holster7 = [ 115, player_holster8 ]\n{\n};\n\nvoid () player_holster8 = [ 116, player_holster9 ]\n{\n};\n\nvoid () player_holster9 = [ 117, player_holster10 ]\n{\n};\n\nvoid () player_holster10 = [ 119, player_holster11 ]\n{\n};\n\nvoid () player_holster11 = [ 121, player_holster12 ]\n{\n};\n\nvoid () player_holster12 = [ 122, player_run ]\n{\n};\n\nvoid () player_jump1 = [ 48, player_jump2 ]\n{\n};\n\nvoid () player_jump2 = [ 49, player_jump3 ]\n{\n};\n\nvoid () player_jump3 = [ 50, player_jump4 ]\n{\n};\n\nvoid () player_jump4 = [ 52, player_jump5 ]\n{\n};\n\nvoid () player_jump5 = [ 54, player_jump6 ]\n{\n};\n\nvoid () player_jump6 = [ 53, player_jump7 ]\n{\n};\n\nvoid () player_jump7 = [ 51, player_jump8 ]\n{\n};\n\nvoid () player_jump8 = [ 49, player_jump9 ]\n{\n};\n\nvoid () player_jump9 = [ 48, player_run ]\n{\n};\n\nvoid () player_single1_left = [ 88, player_single2_left ]\n{\n\tself.weaponframe = 4;\n\tmuzzleflash ();\n};\n\nvoid () player_single2_left = [ 89, player_run ]\n{\n\tself.weaponframe = 5;\n};\n\nvoid () player_single1s_left = [ 183, player_single2_s ]\n{\n\tself.weaponframe = 4;\n\tmuzzleflash ();\n};\n\nvoid () player_single2s_left = [ 184, player_run ]\n{\n\tself.weaponframe = 5;\n};\n\nvoid () player_singlea = [ 88, player_run ]\n{\n\tself.weaponframe = WEAPON_SHOTGUN;\n\tmuzzleflash ();\n};\n\nvoid () player_singleaz = [ 183, player_run ]\n{\n\tself.weaponframe = WEAPON_SHOTGUN;\n\tmuzzleflash ();\n};\n\nvoid () player_singlea2 = [ 89, player_run ]\n{\n\tself.weaponframe = WEAPON_SHOTGUN;\n\tmuzzleflash ();\n};\n\nvoid () player_singleb = [ 89, player_run ]\n{\n\tself.weaponframe = WEAPON_ROCKET;\n\tmuzzleflash ();\n};\n\nvoid () player_singleb2 = [ 89, player_run ]\n{\n\tself.weaponframe = WEAPON_ROCKET;\n\tmuzzleflash ();\n};\n\nvoid () player_singlebz = [ 184, player_run ]\n{\n\tself.weaponframe = WEAPON_ROCKET;\n\tmuzzleflash ();\n};\n\nvoid () player_shotty1 = [ 88, player_shotty2 ]\n{\n\tself.weaponframe = WEAPON_SHOTGUN;\n};\n\nvoid () player_shotty2 = [ 89, player_shotty3 ]\n{\n\tself.weaponframe = WEAPON_ROCKET;\n};\n\nvoid () player_shotty3 = [ 90, player_shotty4 ]\n{\n\tself.weaponframe = AS_MELEE;\n};\n\nvoid () player_shotty4 = [ 91, player_run ]\n{\n\tself.weaponframe = WEAPON_ROCKET;\n};\n\nvoid () player_pull1 = [ 155, player_pull2 ]\n{\n\tself.weaponframe = WEAPON_SHOTGUN;\n};\n\nvoid () player_pull2 = [ 156, player_pull3 ]\n{\n\tself.weaponframe = WEAPON_ROCKET;\n};\n\nvoid () player_pull3 = [ 157, player_pull4 ]\n{\n\tself.weaponframe = AS_MELEE;\n};\n\nvoid () player_pull4 = [ 158, player_pull5 ]\n{\n\tself.weaponframe = WEAPON_SPIKES;\n};\n\nvoid () player_pull5 = [ 157, player_pull6 ]\n{\n\tself.weaponframe = MULTICAST_PVS_R;\n};\n\nvoid () player_pull6 = [ 156, player_pull7 ]\n{\n\tself.weaponframe = TE_LIGHTNING2;\n\tsound (self, CHAN_WEAPON, \"weapons/gpull.wav\", WEAPON_SHOTGUN, ATTN_IDLE);\n};\n\nvoid () player_pull7 = [ 155, player_pull8 ]\n{\n\tself.weaponframe = TE_WIZSPIKE;\n};\n\nvoid () player_pull8 = [ 155, player_pull9 ]\n{\n\tself.weaponframe = 8;\n\tself.grenadetoggle = 1;\n};\n\nvoid () player_pull9 = [ 155, player_pull10 ]\n{\n\tself.weaponframe = 9;\n};\n\nvoid () player_pull10 = [ 155, player_pull11 ]\n{\n\tself.weaponframe = 10;\n};\n\nvoid () player_pull11 = [ 155, player_run ]\n{\n\tself.weaponframe = 11;\n};\n\nvoid () player_throw1 = [ 155, player_throw2 ]\n{\n\tself.attack_finished = time + 1;\n\n\tself.weaponframe = 12;\n\tif (((random () * WEAPON_BIG) <= WEAPON_SPIKES))\n\t\tsound (self, CHAN_VOICE, \"radio/grenade.wav\", 0.7, ATTN_NORM);\n\telse\n\t\tsound (self, CHAN_VOICE, \"radio/lookout.wav\", 0.7, ATTN_NORM);\n};\n\nvoid () player_throw2 = [ 156, player_throw3 ]\n{\n\tself.weaponframe = TE_LIGHTNINGBLOOD;\n\tFireHandGrenade ();\n\tself.attack_finished = time + 1;\n};\n\nvoid () player_throw3 = [ 157, player_throw4 ]\n{\n\tself.weaponframe = IDLE2A;\n\tself.attack_finished = time + 1;\n};\n\nvoid () player_throw4 = [ 158, player_throw5 ]\n{\n\tself.weaponframe = IDLE3A;\n\tself.attack_finished = time + 1;\n};\n\nvoid () player_throw5 = [ 157, player_throw6 ]\n{\n\tself.weaponframe = EF_FLAG1;\n\tself.attack_finished = time + 1;\n};\n\nvoid () player_throw6 = [ 156, player_throw7 ]\n{\n\tself.weaponframe = IDLE5A;\n\tself.attack_finished = time + 1;\n};\n\nvoid () player_throw7 = [ 155, player_throw8 ]\n{\n\tself.weaponframe = IDLE6A;\n\tself.attack_finished = time + 1;\t\n};\n\nvoid () player_throw8 = [ 159, player_throw9 ]\n{\n\tself.weaponframe = IDLE7A;\n\tself.attack_finished = time + 1;\n};\n\nvoid () player_throw9 = [ 160, player_throw10 ]\n{\n\tself.weaponframe = IDLE8A;\n\tself.attack_finished = time + 1;\n};\n\nvoid () player_throw10 = [ 161, player_throw11 ]\n{\n\tself.weaponframe = IDLE9A;\n\tself.attack_finished = time + 1;\n};\n\nvoid () player_throw11 = [ 162, player_run ]\n{\n\tself.weaponframe = IDLE10A;\n\tself.attack_finished = self.nextthink;\n\tW_SetCurrentAmmo();\n};\n\nvoid () player_shotty1b = [ 183, player_shotty2b ]\n{\n\tself.weaponframe = WEAPON_SHOTGUN;\n};\n\nvoid () player_shotty2b = [ 183, player_shotty3b ]\n{\n\tself.weaponframe = WEAPON_ROCKET;\n};\n\nvoid () player_shotty3b = [ 184, player_shotty4b ]\n{\n\tself.weaponframe = AS_MELEE;\n};\n\nvoid () player_shotty4b = [ 184, player_run ]\n{\n\tself.weaponframe = WEAPON_ROCKET;\n};\n\nvoid () player_knife1 = [ 155, player_knife2 ]\n{\n\tself.weaponframe = WEAPON_SHOTGUN;\n};\n\nvoid () player_knife2 = [ 156, player_knife3 ]\n{\n\tself.weaponframe = WEAPON_ROCKET;\n\tW_FireMelee ();\n};\n\nvoid () player_knife3 = [ 157, player_run ]\n{\n\tself.weaponframe = AS_MELEE;\n};\n\nvoid () player_knifea = [ 155, player_knifeb ]\n{\n\tself.weaponframe = WEAPON_SPIKES;\n};\n\nvoid () player_knifeb = [ 156, player_knifec ]\n{\n\tself.weaponframe = MULTICAST_PVS_R;\n\tW_FireMelee ();\n};\n\nvoid () player_knifec = [ 157, player_run ]\n{\n\tself.weaponframe = TE_LIGHTNING2;\n};\n\nvoid () player_nail1 = [ 88, player_nail2 ]\n{\n\tmuzzleflash ();\n\tif (((!self.button0 || intermission_running) || self.impulse))\n\t{\n\t\tplayer_run ();\n\t\treturn;\n\t}\n\tself.weaponframe = (self.weaponframe + WEAPON_SHOTGUN);\n\tif ((self.weaponframe == AS_MELEE))\n\t{\n\t\tself.weaponframe = WEAPON_SHOTGUN;\n\t}\n};\n\nvoid () player_nail2 = [ 89, player_nail1 ]\n{\n\tmuzzleflash ();\n\tif (((!self.button0 || intermission_running) || self.impulse))\n\t{\n\t\tplayer_run ();\n\t\treturn;\n\t}\n\tself.weaponframe = (self.weaponframe + WEAPON_SHOTGUN);\n\tif ((self.weaponframe == AS_MELEE))\n\t{\n\t\tself.weaponframe = WEAPON_SHOTGUN;\n\t}\n};\n\nvoid () player_auto1 = [ 88, player_auto2 ]\n{\n\tmuzzleflash ();\n\tif (((!self.button0 || intermission_running) || self.impulse))\n\t{\n\t\tplayer_run ();\n\t\treturn;\n\t}\n\tself.weaponframe = (self.weaponframe + WEAPON_SHOTGUN);\n\tif ((self.weaponframe == WEAPON_ROCKET))\n\t{\n\t\tself.weaponframe = WEAPON_SHOTGUN;\n\t}\n};\n\nvoid () player_auto2 = [ 89, player_auto1 ]\n{\n\tmuzzleflash ();\n\tif (((!self.button0 || intermission_running) || self.impulse))\n\t{\n\t\tplayer_run ();\n\t\treturn;\n\t}\n\tself.weaponframe = (self.weaponframe + WEAPON_SHOTGUN);\n\tif ((self.weaponframe == WEAPON_ROCKET))\n\t{\n\t\tself.weaponframe = WEAPON_SHOTGUN;\n\t}\n};\n\nvoid () player_auto3 = [ 88, player_auto4 ]\n{\n\tmuzzleflash ();\n\tif (((!self.button0 || intermission_running) || self.impulse))\n\t{\n\t\tplayer_run ();\n\t\treturn;\n\t}\n\tself.weaponframe = WEAPON_SHOTGUN;\n};\n\nvoid () player_auto4 = [ 89, player_auto3 ]\n{\n\tmuzzleflash ();\n\tif (((!self.button0 || intermission_running) || self.impulse))\n\t{\n\t\tplayer_run ();\n\t\treturn;\n\t}\n\tself.weaponframe = MULTICAST_ALL;\n};\nvoid (float num_bubbles) DeathBubbles;\n\nvoid () PainSound =\n{\n\tif ((self.equipment == 8))\n\t\treturn;\n\n\tif ((self.health <= 0))\n\t\treturn;\n\n\tif (self.air_finished < time)\n\t{\n\t\tsound (self, CHAN_VOICE, \"player/drown2.wav\", 1, ATTN_NORM);\n\t\treturn;\n\t}\n\n\tif ((damage_attacker.classname == \"teledeath\"))\n\t{\n\t\tsound (self, CHAN_VOICE, \"player/teledth1.wav\", WEAPON_SHOTGUN, ATTN_NONE);\n\t\treturn;\n\t}\n\tif (((self.watertype == CONTENT_WATER) && (self.waterlevel == AS_MELEE)))\n\t{\n\t\tDeathBubbles (WEAPON_SHOTGUN);\n\t\tsound (self, CHAN_VOICE, \"player/pain1.wav\", WEAPON_SHOTGUN, ATTN_NORM);\n\t\treturn;\n\t}\n\tif ((self.watertype == CONTENT_SLIME))\n\t{\n\t\tif ((random () > 0.5))\n\t\t\tsound (self, CHAN_VOICE, \"player/slimbrn2.wav\", WEAPON_SHOTGUN, ATTN_NORM);\n\t\telse\n\t\t\tsound (self, CHAN_VOICE, \"player/lburn2.wav\", WEAPON_SHOTGUN, ATTN_NORM);\n\t\treturn;\n\t}\n\tif ((self.watertype == CONTENT_LAVA))\n\t{\n\t\tif ((random () > 0.5))\n\t\t\tsound (self, CHAN_VOICE, \"player/lburn1.wav\", WEAPON_SHOTGUN, ATTN_NORM);\n\t\telse\n\t\t\tsound (self, CHAN_VOICE, \"player/lburn2.wav\", WEAPON_SHOTGUN, ATTN_NORM);\n\t\treturn;\n\t}\n\tif ((self.pain_finished > time))\n\t{\n\t\tself.axhitme = MULTICAST_ALL;\n\t\treturn;\n\t}\n\tself.pain_finished = (time + 1.5);\n\tif ((self.axhitme == WEAPON_SHOTGUN))\n\t{\n\t\tself.axhitme = 0;\n\t\tsound (self, CHAN_VOICE, \"player/pain1.wav\", WEAPON_SHOTGUN, ATTN_NORM);\n\t\treturn;\n\t}\n\tself.noise = \"player/pain2.wav\";\n\tsound (self, CHAN_VOICE, self.noise, WEAPON_SHOTGUN, ATTN_NORM);\n\treturn;\n};\n\nvoid () player_pain1 = [ 14, player_pain2 ]\n{\n};\n\nvoid () player_pain2 = [ 15, player_pain3 ]\n{\n};\n\nvoid () player_pain3 = [ 16, player_pain4 ]\n{\n};\n\nvoid () player_pain4 = [ 17, player_pain5 ]\n{\n};\n\nvoid () player_pain5 = [ 18, player_pain6 ]\n{\n};\n\nvoid () player_pain6 = [ 19, player_run ]\n{\n};\n\nvoid () player_pain =\n{\n\tif (self.pain_finished > time)\n\t{\n\t\tsound (self, CHAN_VOICE, self.armornoise, 1, ATTN_NORM);\n\t\treturn;\n\t}\n\n\tself.pain_finished = time + 0.5;\n\n\tif (self.weaponframe)\n\t\treturn;\n\n\tif (random () * WEAPON_BIG < WEAPON_SPIKES)\n\t\tsound (self, CHAN_VOICE, \"player/paina.wav\", WEAPON_SHOTGUN, ATTN_NORM);\n\telse\n\t\tsound (self, CHAN_VOICE, \"player/painb.wav\", WEAPON_SHOTGUN, ATTN_NORM);\n\tif (random () * WEAPON_BIG < TE_LIGHTNING2)\n\t\tsound (self, CHAN_BODY, \"player/hit1.wav\", WEAPON_SHOTGUN, ATTN_NORM);\n\tif (self.invisible_finished > time)\n\t\treturn;\n\n\tself.maxspeed = (self.maxspeed * 0.6);\n\tplayer_pain1 ();\n};\nvoid () player_dieb1;\nvoid () player_diec1;\n\nvoid () DeathBubblesSpawn =\n{\n\tlocal entity bubble;\n\n\tif ((self.owner.waterlevel != AS_MELEE))\n\t{\n\t\treturn;\n\t}\n\tbubble = spawn ();\n\tsetmodel (bubble, \"progs/s_bubble.spr\");\n\tsetorigin (bubble, (self.owner.origin + '0 0 24'));\n\tbubble.movetype = MOVETYPE_NOCLIP;\n\tbubble.solid = SOLID_NOT;\n\tbubble.velocity = '0 0 15';\n\tbubble.nextthink = (time + 0.5);\n\tbubble.think = bubble_bob;\n\tbubble.classname = \"bubble\";\n\tbubble.frame = MULTICAST_ALL;\n\tbubble.cnt = MULTICAST_ALL;\n\tsetsize (bubble, '-8 -8 -8', '8 8 8');\n\tself.nextthink = (time + 0.1);\n\tself.think = DeathBubblesSpawn;\n\tself.air_finished = (self.air_finished + 1);\n\tif ((self.air_finished >= self.bubble_count))\n\t{\n\t\tremove (self);\n\t}\n};\n\nvoid (float num_bubbles) DeathBubbles =\n{\n\tlocal entity bubble_spawner;\n\n\tbubble_spawner = spawn ();\n\tsetorigin (bubble_spawner, self.origin);\n\tbubble_spawner.movetype = MOVETYPE_NONE;\n\tbubble_spawner.solid = SOLID_NOT;\n\tbubble_spawner.nextthink = (time + 0.1);\n\tbubble_spawner.think = DeathBubblesSpawn;\n\tbubble_spawner.air_finished = MULTICAST_ALL;\n\tbubble_spawner.owner = self;\n\tbubble_spawner.bubble_count = num_bubbles;\n\treturn;\n};\n\nvoid () DeathSound =\n{\n\tlocal float r;\n\n\tif ((self.equipment == 8))\n\t{\n\t\treturn;\n\t}\n\tif ((self.waterlevel == AS_MELEE))\n\t{\n\t\tsound (self, CHAN_VOICE, \"player/drown2.wav\", WEAPON_SHOTGUN, ATTN_NONE);\n\t\treturn;\n\t}\n\tr = random ();\n\tself.noise = \"player/agdie4.wav\";\n\tsound (self, CHAN_VOICE, self.noise, 0.9, ATTN_NORM);\n\treturn;\n};\n\nvoid () PlayerDead =\n{\n\tsetmodel(self, \"progs/dead.mdl\");\n\tsetsize(self, '-16 -16 -24', '16 16 0');\n\tself.nextthink = CONTENT_EMPTY;\n\tself.deadflag = DEAD_DEAD;\n};\n\nvector(float dm) VelocityForDamage =\n{\n\tlocal vector v;\n\n\tv_x = 100 * crandom();\n\tv_y = 100 * crandom();\n\tv_z = 200 + 100 * random();\n\n\tif (dm > -50)\n\t{\n//\t\tdprint (\"level 1\\n\");\n\t\tv = v * 0.7;\n\t}\n\telse if (dm > -200)\n\t{\n//\t\tdprint (\"level 3\\n\");\n\t\tv = v * 2;\n\t}\n\telse\n\t\tv = v * 10;\n\n\treturn v;\n};\n\nvoid(string gibname, float dm) ThrowGib =\n{\n\tlocal\tentity new;\n\n\tnew = spawn();\n\tnew.origin = self.origin;\n\tsetmodel (new, gibname);\n\tsetsize (new, '0 0 0', '0 0 0');\n\tnew.velocity = VelocityForDamage (dm);\n\tnew.movetype = MOVETYPE_BOUNCE;\n\tnew.solid = SOLID_NOT;\n//\tnew.avelocity_x = random()*600;\n\tnew.avelocity_y = random()*600;\n//\tnew.avelocity_z = random()*600;\n\tnew.think = SUB_Remove;\n\tnew.ltime = time;\n\tnew.nextthink = time + 10 + random()*10;\n\tnew.frame = 0;\n\tnew.flags = 0;\n};\n\nvoid(string gibname, float dm) ThrowHead =\n{\n\tsetmodel (self, gibname);\n\tself.frame = 0;\n\tself.nextthink = -1;\n\tself.movetype = MOVETYPE_BOUNCE;\n\tself.takedamage = DAMAGE_NO;\n\tself.solid = SOLID_NOT;\n\tself.view_ofs = '0 0 8';\n\tsetsize (self, '-16 -16 0', '16 16 56');\n\tself.velocity = VelocityForDamage (dm);\n\tself.origin_z = self.origin_z - 24;\n\tself.flags = self.flags - (self.flags & FL_ONGROUND);\n\tself.avelocity = crandom() * '0 600 0';\n};\n\nvoid (string gibname, float dm) ThrowGib2 =\n{\n\tlocal\tentity new;\n\n\tnew = spawn();\n\tnew.origin = self.origin;\n\tsetmodel (new, gibname);\n\tsetsize (new, '0 0 0', '0 0 0');\n\tnew.velocity = VelocityForDamage (dm);\n\tnew.velocity_z = new.velocity_z + 40;\n\tnew.movetype = MOVETYPE_BOUNCE;\n\tnew.solid = SOLID_NOT;\n//\tnew.avelocity_x = random()*600;\n\tnew.avelocity_y = random()*600;\n//\tnew.avelocity_z = random()*600;\n\tnew.think = SUB_Remove;\n\tnew.ltime = time;\n\tnew.nextthink = time + 10 + random()*10;\n\tnew.frame = 0;\n\tnew.flags = 0;\n};\n\n\nvoid () SmokeBob2 =\n{\n\tlocal float rnd1;\n\tlocal float rnd2;\n\tlocal float rnd3;\n\n\tself.cnt = (self.cnt + WEAPON_SHOTGUN);\n\tif ((self.cnt >= (WEAPON_ROCKET + (random () * AS_MELEE))))\n\t{\n\t\tremove (self);\n\t}\n\trnd1 = (self.velocity_x + (-10 + (random () * IDLE8A)));\n\trnd2 = (self.velocity_y + (-10 + (random () * IDLE8A)));\n\trnd3 = ((self.velocity_z + TE_LAVASPLASH) + (random () * TE_LAVASPLASH));\n\tif ((rnd1 > MULTICAST_PVS_R))\n\t{\n\t\trnd1 = MULTICAST_PVS_R;\n\t}\n\tif ((rnd1 < CONTENT_LAVA))\n\t{\n\t\trnd1 = CONTENT_LAVA;\n\t}\n\tif ((rnd2 > MULTICAST_PVS_R))\n\t{\n\t\trnd2 = MULTICAST_PVS_R;\n\t}\n\tif ((rnd2 < CONTENT_LAVA))\n\t{\n\t\trnd2 = CONTENT_LAVA;\n\t}\n\tif ((rnd3 < TE_LAVASPLASH))\n\t{\n\t\trnd3 = IDLE3A;\n\t}\n\tif ((rnd3 > SVC_INTERMISSION))\n\t{\n\t\trnd3 = DRAW3;\n\t}\n\tself.velocity_x = rnd1;\n\tself.velocity_y = rnd2;\n\tself.velocity_z = rnd3;\n\tself.nextthink = (time + 0.5);\n\tself.think = SmokeBob2;\n};\n\nvoid (string gibname, float dm) ThrowPlayerHead =\n{\n\tsetmodel (self, gibname);\n\tself.frame = MULTICAST_ALL;\n\tif (self.team == 1)\n\t\tself.skin = 0;\n\tif (self.team == 2)\n\t\tself.skin = 1;\n\tself.nextthink = (time + WEAPON_SHOTGUN);\n\tself.think = SUB_Null;\n\tself.movetype = MOVETYPE_BOUNCE;\n\tself.takedamage = DAMAGE_NO;\n\tself.view_ofs = '0 0 8';\n\tsetsize (self, '-8 -8 -8', '8 8 8');\n\tself.velocity = VelocityForDamage (dm);\n\tself.origin_z = (self.origin_z + DRAW2);\n\tself.flags = (self.flags - (self.flags & FL_ONGROUND));\n\tself.avelocity = (crandom () * '0 600 0');\n};\n\nvoid () GibPlayer =\n{\n\tself.solid = SOLID_NOT;\n\tself.deadflag = DEAD_DYING;\n\tsetmodel (self, \"\");\n\tself.skin = self.team;\n\tThrowGib (\"progs/gib1.mdl\", self.health);\n\tThrowGib (\"progs/gib1.mdl\", self.health);\n\tThrowGib (\"progs/gib3.mdl\", self.health);\n\tSpawnMeatSpray (self.origin, (self.origin + VEC_ORIGIN));\n\tSpawnMeatSpray (self.origin, (self.origin + VEC_ORIGIN));\n\tSpawnMeatSpray (self.origin, (self.origin + VEC_ORIGIN));\n\tself.deadflag = DEAD_DEAD;\n};\n\nvoid () PlayerDie =\n{\n\tlocal float i;\n\tlocal float r;\n\n\t//self.solid = SOLID_NOT;\n\tself.dead = self.dead + 1;\n\tif ((self.deathtype == \"fall\"))\n\t{\n\t\tsound (self, CHAN_VOICE, \"player/agdie4.wav\", WEAPON_SHOTGUN, ATTN_NONE);\n\t\tself.deathtype = \"\";\n\t}\n\tif ((self.waterlevel == AS_MELEE))\n\t{\n\t\tDeathBubbles (IDLE8A);\n\t\tsound (self, CHAN_VOICE, \"player/drown2.wav\", WEAPON_SHOTGUN, ATTN_NONE);\n\t}\n\tr = random ();\n\tself.noise = \"player/agdie2.wav\";\n\n\tsound (self, CHAN_VOICE, self.noise, 1, ATTN_NORM);\n\tself.items = (self.items - (self.items & IT_INVISIBILITY));\n\tself.invisible_finished = MULTICAST_ALL;\n\tself.invincible_finished = MULTICAST_ALL;\n\tself.super_damage_finished = MULTICAST_ALL;\n\tself.radsuit_finished = MULTICAST_ALL;\n\tself.modelindex = modelindex_player;\n\tself.weaponmodel = \"\";\n\tself.view_ofs = '0 0 -8';\n\tself.deadflag = DEAD_DYING;\n\tself.flags = (self.flags - (self.flags & FL_ONGROUND));\n\tself.movetype = MOVETYPE_TOSS;\n\tif ((self.velocity_z < TE_LAVASPLASH))\n\t{\n\t\tself.velocity_z = (self.velocity_z + (random () * 300));\n\t}\n\tself.angles_x = 0;\n\tself.angles_y = 0;\n\tself.angles_z = 0;\n\ti = (WEAPON_SHOTGUN + floor ((random () * WEAPON_ROCKET)));\n\tself.angles_z = MULTICAST_ALL;\n\tif (self.position == 1 || self.position == 2)\n\t\tplayer_diec1 ();\n\telse\n\t{\n\t\tif (self.health <= -40)\n\t\t\tplayer_dieb1 ();\n\t\telse\n\t\t\tplayer_diea1 ();\n\t}\n};\n\nvoid () set_suicide_frame =\n{\n\tself.frame = TE_LAVASPLASH;\n\tself.solid = SOLID_NOT;\n\tself.movetype = MOVETYPE_TOSS;\n\tself.deadflag = DEAD_DEAD;\n\tself.nextthink = CONTENT_EMPTY;\n};\n\nvoid () player_diea1 = [ 1, player_diea2 ]\n{\n};\n\nvoid () player_diea2 = [ 2, player_diea3 ]\n{\n};\n\nvoid () player_diea3 = [ 3, player_diea4 ]\n{\n};\n\nvoid () player_diea4 = [ 4, player_diea5 ]\n{\n};\n\nvoid () player_diea5 = [ 5, player_diea6 ]\n{\n};\n\nvoid () player_diea6 = [ 6, player_diea7 ]\n{\n};\n\nvoid () player_diea7 = [ 7, player_diea8 ]\n{\n};\n\nvoid () player_diea8 = [ 8, player_diea9 ]\n{\n};\n\nvoid () player_diea9 = [ 9, player_diea10 ]\n{\n};\n\nvoid () player_diea10 = [ 10, player_diea11 ]\n{\n};\n\nvoid () player_diea11 = [ 11, player_diea12 ]\n{\n};\n\nvoid () player_diea12 = [ 12, player_diea13 ]\n{\n};\n\nvoid () player_diea13 = [ 13, player_diea13 ]\n{\n\tPlayerDead ();\n};\n\nvoid () player_dieb1 = [ 94, player_dieb2 ]\n{\n};\n\nvoid () player_dieb2 = [ 95, player_dieb3 ]\n{\n};\n\nvoid () player_dieb3 = [ 96, player_dieb4 ]\n{\n};\n\nvoid () player_dieb4 = [ 97, player_dieb5 ]\n{\n};\n\nvoid () player_dieb5 = [ 98, player_dieb6 ]\n{\n};\n\nvoid () player_dieb6 = [ 99, player_dieb7 ]\n{\n};\n\nvoid () player_dieb7 = [ 100, player_dieb8 ]\n{\n};\n\nvoid () player_dieb8 = [ 101, player_dieb9 ]\n{\n};\n\nvoid () player_dieb9 = [ 102, player_dieb10 ]\n{\n};\n\nvoid () player_dieb10 = [ 103, player_dieb11 ]\n{\n};\n\nvoid () player_dieb11 = [ 104, player_dieb12 ]\n{\n};\n\nvoid () player_dieb12 = [ 105, player_dieb13 ]\n{\n};\n\nvoid () player_dieb13 = [ 106, player_dieb13 ]\n{\n\tPlayerDead ();\n};\n\nvoid () player_diec1 = [ 55, player_diec2 ]\n{\n};\n\nvoid () player_diec2 = [ 56, player_diec3 ]\n{\n};\n\nvoid () player_diec3 = [ 57, player_diec4 ]\n{\n};\n\nvoid () player_diec4 = [ 58, player_diec5 ]\n{\n};\n\nvoid () player_diec5 = [ 59, player_diec6 ]\n{\n};\n\nvoid () player_diec6 = [ 60, player_diec7 ]\n{\n};\n\nvoid () player_diec7 = [ 61, player_diec8 ]\n{\n};\n\nvoid () player_diec8 = [ 62, player_diec9 ]\n{\n};\n\nvoid () player_diec9 = [ 63, player_diec10 ]\n{\n};\n\nvoid () player_diec10 = [ 64, player_diec11 ]\n{\n};\n\nvoid () player_diec11 = [ 65, player_diec12 ]\n{\n};\n\nvoid () player_diec12 = [ 66, player_diec13 ]\n{\n};\n\nvoid () player_diec13 = [ 67, player_diec13 ]\n{\n\tPlayerDead ();\n};\n"
  },
  {
    "path": "quakec/fallout2/progs.src",
    "content": "../qwprogs.dat\n\ndefs.qc\ninventory.qc\nsubs.qc\nfight.qc\n\ntf.qc\n\nai.qc\ncombat.qc\nitems.qc\nname.qc\nmenus.qc\nweapons.qc\nworld.qc\nclient.qc\nserver.qc\nplayer.qc\nmonsters.qc\ndoors.qc\nbuttons.qc\ntriggers.qc\nplats.qc\nhos.qc\nzombie.qc\nenforcer.qc\nsoldier.qc\ndemon.qc\nboss.qc\nknight.qc\nhknight.qc\nshalrath.qc\nshambler.qc\nwizard.qc\ndog.qc\nogre.qc\nmisc.qc\nmod_buy.qc\nturrets.qc\ncmds.qc"
  },
  {
    "path": "quakec/fallout2/server.qc",
    "content": "\nvoid() monster_tarbaby = {remove(self);};\nvoid() monster_oldone = {remove(self);};\nvoid() event_lightning = {remove(self);};\n\n/*\n==============================================================================\n\nMOVETARGET CODE\n\nThe angle of the movetarget effects standing and bowing direction, but has no effect on movement, which allways heads to the next target.\n\ntargetname\nmust be present.  The name of this movetarget.\n\ntarget\nthe next spot to move to.  If not present, stop here for good.\n\npausetime\nThe number of seconds to spend standing or bowing for path_stand or path_bow\n\n==============================================================================\n*/\n\n/*\n=============\nt_movetarget\n\nSomething has bumped into a movetarget.  If it is a monster\nmoving towards it, change the next destination and continue.\n==============\n\nvoid() t_movetarget =\n{\nlocal entity    temp;\n\n\tif (other.movetarget != self)\n\t\treturn;\n\t\n\tif (other.enemy)\n\t\treturn;         // fighting, not following a path\n\n\ttemp = self;\n\tself = other;\n\tother = temp;\n\n\tif (self.classname == \"monster_ogre\")\n\t\tsound (self, CHAN_VOICE, \"ogre/ogdrag.wav\", 1, ATTN_IDLE);// play chainsaw drag sound\n\n//dprint (\"t_movetarget\\n\");\n\tself.goalentity = self.movetarget = find (world, targetname, other.target);\n\tself.ideal_yaw = vectoyaw(self.goalentity.origin - self.origin);\n\tif (!self.movetarget)\n\t{\n\t\tself.pausetime = time + 999999;\n\t\tself.th_stand ();\n\t\treturn;\n\t}\n};*/\n\n\n/*\nvoid() movetarget_f =\n{\n\tif (!self.targetname)\n\t\tobjerror (\"monster_movetarget: no targetname\");\n\t\t\n\tself.solid = SOLID_TRIGGER;\n\tself.touch = t_movetarget;\n\tsetsize (self, '-8 -8 -8', '8 8 8');\n\t\n};*/\n\n/*QUAKED path_corner (0.5 0.3 0) (-8 -8 -8) (8 8 8)\nMonsters will continue walking towards the next target corner.\n\nvoid() path_corner =\n{\n\tmovetarget_f ();\n};*/\n\n\n\n//============================================================================\n"
  },
  {
    "path": "quakec/fallout2/shalrath.qc",
    "content": "/*\n==============================================================================\n\nSHAL-RATH\n\n==============================================================================\n*/\n$cd id1/models/shalrath\n$origin 0 0 24\n$base base\n$skin skin\n$scale 0.7\n\n$frame attack1 attack2 attack3 attack4 attack5 attack6 attack7 attack8\n$frame attack9 attack10 attack11\n\n$frame pain1 pain2 pain3 pain4 pain5 \n\n$frame death1 death2 death3 death4 death5 death6 death7\n\n$frame\twalk1 walk2 walk3 walk4 walk5 walk6 walk7 walk8 walk9 walk10\n$frame\twalk11 walk12\n\nvoid() shalrath_pain;\nvoid() ShalMissile;\nvoid() shal_stand     =[      $walk1,       shal_stand    ] {ai_stand();};\n\nvoid() shal_walk1     =[      $walk2,       shal_walk2    ] {\nif (random() < 0.2)\n\tsound (self, CHAN_VOICE, \"shalrath/idle.wav\", 1, ATTN_IDLE);\nai_walk(6);};\nvoid() shal_walk2     =[      $walk3,       shal_walk3    ] {ai_walk(4);};\nvoid() shal_walk3     =[      $walk4,       shal_walk4    ] {ai_walk(0);};\nvoid() shal_walk4     =[      $walk5,       shal_walk5    ] {ai_walk(0);};\nvoid() shal_walk5     =[      $walk6,       shal_walk6    ] {ai_walk(0);};\nvoid() shal_walk6     =[      $walk7,       shal_walk7    ] {ai_walk(0);};\nvoid() shal_walk7     =[      $walk8,       shal_walk8    ] {ai_walk(5);};\nvoid() shal_walk8     =[      $walk9,       shal_walk9    ] {ai_walk(6);};\nvoid() shal_walk9     =[      $walk10,       shal_walk10    ] {ai_walk(5);};\nvoid() shal_walk10    =[      $walk11,       shal_walk11    ] {ai_walk(0);};\nvoid() shal_walk11    =[      $walk12,       shal_walk12    ] {ai_walk(4);};\nvoid() shal_walk12    =[      $walk1,       shal_walk1    ] {ai_walk(5);};\n\nvoid() shal_run1     =[      $walk2,       shal_run2    ] {\nif (random() < 0.2)\n\tsound (self, CHAN_VOICE, \"shalrath/idle.wav\", 1, ATTN_IDLE);\nai_run(6);};\nvoid() shal_run2     =[      $walk3,       shal_run3    ] {ai_run(4);};\nvoid() shal_run3     =[      $walk4,       shal_run4    ] {ai_run(0);};\nvoid() shal_run4     =[      $walk5,       shal_run5    ] {ai_run(0);};\nvoid() shal_run5     =[      $walk6,       shal_run6    ] {ai_run(0);};\nvoid() shal_run6     =[      $walk7,       shal_run7    ] {ai_run(0);};\nvoid() shal_run7     =[      $walk8,       shal_run8    ] {ai_run(5);};\nvoid() shal_run8     =[      $walk9,       shal_run9    ] {ai_run(6);};\nvoid() shal_run9     =[      $walk10,       shal_run10    ] {ai_run(5);};\nvoid() shal_run10    =[      $walk11,       shal_run11    ] {ai_run(0);};\nvoid() shal_run11    =[      $walk12,       shal_run12    ] {ai_run(4);};\nvoid() shal_run12    =[      $walk1,       shal_run1    ] {ai_run(5);};\n\nvoid() shal_attack1     =[      $attack1,       shal_attack2    ] {\nsound (self, CHAN_VOICE, \"shalrath/attack.wav\", 1, ATTN_NORM);\nai_face();\n};\nvoid() shal_attack2     =[      $attack2,       shal_attack3    ] {ai_face();};\nvoid() shal_attack3     =[      $attack3,       shal_attack4    ] {ai_face();};\nvoid() shal_attack4     =[      $attack4,       shal_attack5    ] {ai_face();};\nvoid() shal_attack5     =[      $attack5,       shal_attack6    ] {ai_face();};\nvoid() shal_attack6     =[      $attack6,       shal_attack7    ] {ai_face();};\nvoid() shal_attack7     =[      $attack7,       shal_attack8    ] {ai_face();};\nvoid() shal_attack8     =[      $attack8,       shal_attack9    ] {ai_face();};\nvoid() shal_attack9     =[      $attack9,       shal_attack10   ] {ShalMissile();};\nvoid() shal_attack10    =[      $attack10,      shal_attack11   ] {ai_face();};\nvoid() shal_attack11    =[      $attack11,      shal_run1   ] {};\n\nvoid() shal_pain1       =[      $pain1, shal_pain2      ] {};\nvoid() shal_pain2       =[      $pain2, shal_pain3      ] {};\nvoid() shal_pain3       =[      $pain3, shal_pain4      ] {};\nvoid() shal_pain4       =[      $pain4, shal_pain5      ] {};\nvoid() shal_pain5       =[      $pain5, shal_run1      ] {};\n\nvoid() shal_death1      =[      $death1,        shal_death2     ] {};\nvoid() shal_death2      =[      $death2,        shal_death3     ] {};\nvoid() shal_death3      =[      $death3,        shal_death4     ] {};\nvoid() shal_death4      =[      $death4,        shal_death5     ] {};\nvoid() shal_death5      =[      $death5,        shal_death6     ] {};\nvoid() shal_death6      =[      $death6,        shal_death7     ] {};\nvoid() shal_death7      =[      $death7,        shal_death7    ] {};\n\n\nvoid() shalrath_pain =\n{\n\tif (self.pain_finished > time)\n\t\treturn;\n\n\tsound (self, CHAN_VOICE, \"shalrath/pain.wav\", 1, ATTN_NORM);\n\tshal_pain1();\n\tself.pain_finished = time + 3;\n};\n\nvoid() shalrath_die =\n{\n// check for gib\n\tif (self.health < -90)\n\t{\n\t\tsound (self, CHAN_VOICE, \"player/udeath.wav\", 1, ATTN_NORM);\n\t\tThrowHead (\"progs/h_shal.mdl\", self.health);\n\t\tThrowGib (\"progs/gib1.mdl\", self.health);\n\t\tThrowGib (\"progs/gib2.mdl\", self.health);\n\t\tThrowGib (\"progs/gib3.mdl\", self.health);\n\t\treturn;\n\t}\n\n\tsound (self, CHAN_VOICE, \"shalrath/death.wav\", 1, ATTN_NORM);\n\tshal_death1();\n\tself.solid = SOLID_NOT;\n\t// insert death sounds here\n};\n\n/*\n================\nShalMissile\n================\n*/\nvoid() ShalMissileTouch;\nvoid() ShalHome;\nvoid() ShalMissile =\n{\n\tlocal\tentity \tmissile;\n\tlocal\tvector\tdir;\n\tlocal\tfloat\tdist, flytime;\n\n\tdir = normalize((self.enemy.origin + '0 0 10') - self.origin);\n\tdist = vlen (self.enemy.origin - self.origin);\n\tflytime = dist * 0.002;\n\tif (flytime < 0.1)\n\t\tflytime = 0.1;\n\n\tsound (self, CHAN_WEAPON, \"shalrath/attack2.wav\", 1, ATTN_NORM);\n\n\tmissile = spawn ();\n\tmissile.owner = self;\n\n\tmissile.solid = SOLID_BBOX;\n\tmissile.movetype = MOVETYPE_FLYMISSILE;\n\tsetmodel (missile, \"progs/v_spike.mdl\");\n\n\tsetsize (missile, '0 0 0', '0 0 0');\t\t\n\n\tmissile.origin = self.origin + '0 0 10';\n\tmissile.velocity = dir * 400;\n\tmissile.avelocity = '300 300 300';\n\tmissile.nextthink = flytime + time;\n\tmissile.think = ShalHome;\n\tmissile.enemy = self.enemy;\n\tmissile.touch = ShalMissileTouch;\n};\n\nvoid() ShalHome =\n{\n\tlocal vector\tdir, vtemp;\n\tvtemp = self.enemy.origin + '0 0 10';\n\tif (self.enemy.health < 1)\n\t{\n\t\tremove(self);\n\t\treturn;\n\t}\n\tdir = normalize(vtemp - self.origin);\n\tself.velocity = dir * 350;\n\tself.nextthink = time + 0.2;\n\tself.think = ShalHome;\t\n};\n\nvoid() ShalMissileTouch =\n{\n\tif (other == self.owner)\n\t\treturn;\t\t// don't explode on owner\n\n\tif (other.classname == \"monster_zombie\")\n\t\tT_Damage (other, self, self, 110);\t\n\tT_RadiusDamage (self, self.owner, 40, world, \"vore\");\n\tsound (self, CHAN_WEAPON, \"weapons/r_exp3.wav\", 1, ATTN_NORM);\n\n\tWriteByte (MSG_BROADCAST, SVC_TEMPENTITY);\n\tWriteByte (MSG_BROADCAST, TE_EXPLOSION);\n\tWriteCoord (MSG_BROADCAST, self.origin_x);\n\tWriteCoord (MSG_BROADCAST, self.origin_y);\n\tWriteCoord (MSG_BROADCAST, self.origin_z);\n\n\tself.velocity = '0 0 0';\n\tself.touch = SUB_Null;\n\tsetmodel (self, \"progs/s_explod.spr\");\n\tself.solid = SOLID_NOT;\n\tExplosion(2);\n};\n\n//=================================================================\n\nvoid() monster_shalrath =\n{\n\tprecache_model2 (\"progs/shalrath.mdl\");\n\tprecache_model2 (\"progs/h_shal.mdl\");\n\tprecache_model2 (\"progs/v_spike.mdl\");\n\t\n\tprecache_sound2 (\"shalrath/attack.wav\");\n\tprecache_sound2 (\"shalrath/attack2.wav\");\n\tprecache_sound2 (\"shalrath/death.wav\");\n\tprecache_sound2 (\"shalrath/idle.wav\");\n\tprecache_sound2 (\"shalrath/pain.wav\");\n\tprecache_sound2 (\"shalrath/sight.wav\");\n\t\n\tself.solid = SOLID_SLIDEBOX;\n\tself.movetype = MOVETYPE_STEP;\n\t\n\tsetmodel (self, \"progs/shalrath.mdl\");\n\tsetsize (self, VEC_HULL2_MIN, VEC_HULL2_MAX);\n\tself.health = 800;\n\tself.team = 3;\n\tself.islot3 = 11; //vore armor\n\tself.armortype = 0.1;\n\tself.helmet = 1;\n\tself.classname = \"monster\";\n\tself.netname = \"experiment\";\n\tself.th_stand = shal_stand;\n\tself.th_walk = shal_walk1;\n\tself.th_run = shal_run1;\n\tself.th_die = shalrath_die;\n\tself.th_pain = shalrath_pain;\n\tself.th_missile = shal_attack1;\n\n\tself.think = walkmonster_start;\n\tself.nextthink = time + 0.1 + random ()*0.1;\t\n\n};\n"
  },
  {
    "path": "quakec/fallout2/shambler.qc",
    "content": "/*\n==============================================================================\n\nSHAMBLER\n\n==============================================================================\n*/\n\n$cd id1/models/shams\n$origin 0 0 24\n$base base\t\t\n$skin base\n\n$frame stand1 stand2 stand3 stand4 stand5 stand6 stand7 stand8 stand9\n$frame stand10 stand11 stand12 stand13 stand14 stand15 stand16 stand17\n\n$frame walk1 walk2 walk3 walk4 walk5 walk6 walk7 \n$frame walk8 walk9 walk10 walk11 walk12\n\n$frame\trun1 run2 run3 run4 run5 run6\n\n$frame smash1 smash2 smash3 smash4 smash5 smash6 smash7 \n$frame smash8 smash9 smash10 smash11 smash12\n\n$frame swingr1 swingr2 swingr3 swingr4 swingr5 \n$frame swingr6 swingr7 swingr8 swingr9\n\n$frame swingl1 swingl2 swingl3 swingl4 swingl5 \n$frame swingl6 swingl7 swingl8 swingl9\n\n$frame magic1 magic2 magic3 magic4 magic5 \n$frame magic6 magic7 magic8 magic9 magic10 magic11 magic12\n\n$frame pain1 pain2 pain3 pain4 pain5 pain6\n\n$frame death1 death2 death3 death4 death5 death6 \n$frame death7 death8 death9 death10 death11\n\nvoid() sham_stand1\t=[\t$stand1,\tsham_stand2\t] {ai_stand();};\nvoid() sham_stand2\t=[\t$stand2,\tsham_stand3\t] {ai_stand();};\nvoid() sham_stand3\t=[\t$stand3,\tsham_stand4\t] {ai_stand();};\nvoid() sham_stand4\t=[\t$stand4,\tsham_stand5\t] {ai_stand();};\nvoid() sham_stand5\t=[\t$stand5,\tsham_stand6\t] {ai_stand();};\nvoid() sham_stand6\t=[\t$stand6,\tsham_stand7\t] {ai_stand();};\nvoid() sham_stand7\t=[\t$stand7,\tsham_stand8\t] {ai_stand();};\nvoid() sham_stand8\t=[\t$stand8,\tsham_stand9\t] {ai_stand();};\nvoid() sham_stand9\t=[\t$stand9,\tsham_stand10] {ai_stand();};\nvoid() sham_stand10\t=[\t$stand10,\tsham_stand11] {ai_stand();};\nvoid() sham_stand11\t=[\t$stand11,\tsham_stand12] {ai_stand();};\nvoid() sham_stand12\t=[\t$stand12,\tsham_stand13] {ai_stand();};\nvoid() sham_stand13\t=[\t$stand13,\tsham_stand14] {ai_stand();};\nvoid() sham_stand14\t=[\t$stand14,\tsham_stand15] {ai_stand();};\nvoid() sham_stand15\t=[\t$stand15,\tsham_stand16] {ai_stand();};\nvoid() sham_stand16\t=[\t$stand16,\tsham_stand17] {ai_stand();};\nvoid() sham_stand17\t=[\t$stand17,\tsham_stand1\t] {ai_stand();};\n\nvoid() sham_walk1\t\t=[      $walk1,        sham_walk2 ] {ai_walk(10);};\nvoid() sham_walk2       =[      $walk2,        sham_walk3 ] {ai_walk(9);};\nvoid() sham_walk3       =[      $walk3,        sham_walk4 ] {ai_walk(9);};\nvoid() sham_walk4       =[      $walk4,        sham_walk5 ] {ai_walk(5);};\nvoid() sham_walk5       =[      $walk5,        sham_walk6 ] {ai_walk(6);};\nvoid() sham_walk6       =[      $walk6,        sham_walk7 ] {ai_walk(12);};\nvoid() sham_walk7       =[      $walk7,        sham_walk8 ] {ai_walk(8);};\nvoid() sham_walk8       =[      $walk8,        sham_walk9 ] {ai_walk(3);};\nvoid() sham_walk9       =[      $walk9,        sham_walk10] {ai_walk(13);};\nvoid() sham_walk10      =[      $walk10,       sham_walk11] {ai_walk(9);};\nvoid() sham_walk11      =[      $walk11,       sham_walk12] {ai_walk(7);};\nvoid() sham_walk12      =[      $walk12,       sham_walk1 ] {ai_walk(7);\nif (random() > 0.8)\n\tsound (self, CHAN_VOICE, \"shambler/sidle.wav\", 1, ATTN_IDLE);};\n\nvoid() sham_run1       =[      $run1,        sham_run2      ] {ai_run(20);};\nvoid() sham_run2       =[      $run2,        sham_run3      ] {ai_run(24);};\nvoid() sham_run3       =[      $run3,        sham_run4      ] {ai_run(20);};\nvoid() sham_run4       =[      $run4,        sham_run5      ] {ai_run(20);};\nvoid() sham_run5       =[      $run5,        sham_run6      ] {ai_run(24);};\nvoid() sham_run6       =[      $run6,        sham_run1      ] {ai_run(20);\nif (random() > 0.8)\n\tsound (self, CHAN_VOICE, \"shambler/sidle.wav\", 1, ATTN_IDLE);\n};\n\nvoid() sham_smash1     =[      $smash1,       sham_smash2    ] {\nsound (self, CHAN_VOICE, \"shambler/melee1.wav\", 1, ATTN_NORM);\nai_charge(2);};\nvoid() sham_smash2     =[      $smash2,       sham_smash3    ] {ai_charge(6);};\nvoid() sham_smash3     =[      $smash3,       sham_smash4    ] {ai_charge(6);};\nvoid() sham_smash4     =[      $smash4,       sham_smash5    ] {ai_charge(5);};\nvoid() sham_smash5     =[      $smash5,       sham_smash6    ] {ai_charge(4);};\nvoid() sham_smash6     =[      $smash6,       sham_smash7    ] {ai_charge(1);};\nvoid() sham_smash7     =[      $smash7,       sham_smash8    ] {ai_charge(0);};\nvoid() sham_smash8     =[      $smash8,       sham_smash9    ] {ai_charge(0);};\nvoid() sham_smash9     =[      $smash9,       sham_smash10   ] {ai_charge(0);};\nvoid() sham_smash10    =[      $smash10,      sham_smash11   ] {\nlocal vector\tdelta;\nlocal float \tldmg;\n\n\tif (!self.enemy)\n\t\treturn;\n\tai_charge(0);\n\n\tdelta = self.enemy.origin - self.origin;\n\n\tif (vlen(delta) > 100)\n\t\treturn;\n\tif (!CanDamage (self.enemy, self))\n\t\treturn;\n\t\t\n\tldmg = (random() + random() + random()) * 40;\n\tT_Damage (self.enemy, self, self, ldmg);\n\tsound (self, CHAN_VOICE, \"shambler/smack.wav\", 1, ATTN_NORM);\n\n\tSpawnMeatSpray (self.origin + v_forward*16, crandom() * 100 * v_right);\n\tSpawnMeatSpray (self.origin + v_forward*16, crandom() * 100 * v_right);\n};\nvoid() sham_smash11    =[      $smash11,      sham_smash12   ] {ai_charge(5);};\nvoid() sham_smash12    =[      $smash12,      sham_run1\t   ] {ai_charge(4);};\n\nvoid() sham_swingr1;\n\nvoid(float side) ShamClaw =\n{\nlocal vector\tdelta;\nlocal float \tldmg;\n\n\tif (!self.enemy)\n\t\treturn;\n\tai_charge(10);\n\n\tdelta = self.enemy.origin - self.origin;\n\n\tif (vlen(delta) > 100)\n\t\treturn;\n\t\t\n\tldmg = (random() + random() + random()) * 20;\n\tT_Damage (self.enemy, self, self, ldmg);\n\tsound (self, CHAN_VOICE, \"shambler/smack.wav\", 1, ATTN_NORM);\n\n\tif (side)\n\t{\n\t\tmakevectors (self.angles);\n\t\tSpawnMeatSpray (self.origin + v_forward*16, side * v_right);\n\t}\n};\n\nvoid() sham_swingl1\t=[      $swingl1,      sham_swingl2   ] {\nsound (self, CHAN_VOICE, \"shambler/melee2.wav\", 1, ATTN_NORM);\nai_charge(5);};\nvoid() sham_swingl2 =[      $swingl2,      sham_swingl3   ] {ai_charge(3);};\nvoid() sham_swingl3 =[      $swingl3,      sham_swingl4   ] {ai_charge(7);};\nvoid() sham_swingl4 =[      $swingl4,      sham_swingl5   ] {ai_charge(3);};\nvoid() sham_swingl5 =[      $swingl5,      sham_swingl6   ] {ai_charge(7);};\nvoid() sham_swingl6 =[      $swingl6,      sham_swingl7   ] {ai_charge(9);};\nvoid() sham_swingl7 =[      $swingl7,      sham_swingl8   ] {ai_charge(5); ShamClaw(250);};\nvoid() sham_swingl8 =[      $swingl8,      sham_swingl9   ] {ai_charge(4);};\nvoid() sham_swingl9 =[      $swingl9,      sham_run1  ] {\nai_charge(8);\nif (random()<0.5)\n\tself.think = sham_swingr1;\n};\n\nvoid() sham_swingr1\t=[      $swingr1,      sham_swingr2   ] {\nsound (self, CHAN_VOICE, \"shambler/melee1.wav\", 1, ATTN_NORM);\nai_charge(1);};\nvoid() sham_swingr2\t=[      $swingr2,      sham_swingr3   ] {ai_charge(8);};\nvoid() sham_swingr3 =[      $swingr3,      sham_swingr4   ] {ai_charge(14);};\nvoid() sham_swingr4 =[      $swingr4,      sham_swingr5   ] {ai_charge(7);};\nvoid() sham_swingr5 =[      $swingr5,      sham_swingr6   ] {ai_charge(3);};\nvoid() sham_swingr6 =[      $swingr6,      sham_swingr7   ] {ai_charge(6);};\nvoid() sham_swingr7 =[      $swingr7,      sham_swingr8   ] {ai_charge(6); ShamClaw(-250);};\nvoid() sham_swingr8 =[      $swingr8,      sham_swingr9   ] {ai_charge(3);};\nvoid() sham_swingr9 =[      $swingr9,      sham_run1  ] {ai_charge(1);\nai_charge(10);\nif (random()<0.5)\n\tself.think = sham_swingl1;\n};\n\nvoid() sham_melee =\n{\n\tlocal float chance;\n\t\n\tchance = random();\n\tif (chance > 0.6 || self.health == 600)\n\t\tsham_smash1 ();\n\telse if (chance > 0.3)\n\t\tsham_swingr1 ();\n\telse\n\t\tsham_swingl1 ();\n};\n\n\n//============================================================================\n\nvoid() CastLightning =\n{\n\tlocal\tvector\torg, dir;\n\t\n\tai_face ();\n\n\torg = self.origin + '0 0 40';\n\n\tdir = self.enemy.origin + '0 0 16' - org;\n\tdir = normalize (dir);\n\n\ttraceline (org, self.origin + dir*600, TRUE, self);\n\n\tWriteByte (MSG_BROADCAST, SVC_TEMPENTITY);\n\tWriteByte (MSG_BROADCAST, TE_LIGHTNING1);\n\tWriteEntity (MSG_BROADCAST, self);\n\tWriteCoord (MSG_BROADCAST, org_x);\n\tWriteCoord (MSG_BROADCAST, org_y);\n\tWriteCoord (MSG_BROADCAST, org_z);\n\tWriteCoord (MSG_BROADCAST, trace_endpos_x);\n\tWriteCoord (MSG_BROADCAST, trace_endpos_y);\n\tWriteCoord (MSG_BROADCAST, trace_endpos_z);\n\n\tLightningDamage (org, trace_endpos, self, 10);\n};\n\nvoid() sham_magic1     =[      $magic1,       sham_magic2    ] {ai_face();\n\tsound (self, CHAN_WEAPON, \"shambler/sattck1.wav\", 1, ATTN_NORM);\n};\nvoid() sham_magic2     =[      $magic2,       sham_magic3    ] {ai_face();};\nvoid() sham_magic3     =[      $magic3,       sham_magic4    ] {ai_face();self.nextthink = self.nextthink + 0.2;\nlocal entity o;\n\nai_face();\nself.owner = spawn();\no = self.owner;\nsetmodel (o, \"progs/s_light.mdl\");\nsetorigin (o, self.origin);\no.angles = self.angles;\no.nextthink = time + 0.7;\no.think = SUB_Remove;\n};\nvoid() sham_magic4     =[      $magic4,       sham_magic5    ]\n{\nself.owner.frame = 1;\n};\nvoid() sham_magic5     =[      $magic5,       sham_magic6    ]\n{\nself.owner.frame = 2;\n};\nvoid() sham_magic6     =[      $magic6,       sham_magic9    ]\n{\nremove (self.owner);\nCastLightning();\nsound (self, CHAN_WEAPON, \"shambler/sboom.wav\", 1, ATTN_NORM);\n};\nvoid() sham_magic9     =[      $magic9,       sham_magic10   ]\n{CastLightning();};\nvoid() sham_magic10    =[      $magic10,      sham_magic11   ]\n{CastLightning();};\nvoid() sham_magic11    =[      $magic11,      sham_magic12   ]\n{\n\tCastLightning();\n};\nvoid() sham_magic12    =[      $magic12,      sham_run1\t   ] {};\n\n\n\nvoid() sham_pain1       =[      $pain1, sham_pain2      ] {};\nvoid() sham_pain2       =[      $pain2, sham_pain3      ] {};\nvoid() sham_pain3       =[      $pain3, sham_pain4      ] {};\nvoid() sham_pain4       =[      $pain4, sham_pain5      ] {};\nvoid() sham_pain5       =[      $pain5, sham_pain6      ] {};\nvoid() sham_pain6       =[      $pain6, sham_run1      ] {};\n\nvoid(entity attacker, float damage)\tsham_pain =\n{\n\tsound (self, CHAN_VOICE, \"shambler/shurt2.wav\", 1, ATTN_NORM);\n\n\tif (self.health <= 0)\n\t\treturn;\t\t// allready dying, don't go into pain frame\n\n\tif (random()*400 > damage)\n\t\treturn;\t\t// didn't flinch\n\n\tif (self.pain_finished > time)\n\t\treturn;\n\tself.pain_finished = time + 2;\n\t\t\n\tsham_pain1 ();\n};\n\n\n//============================================================================\n\nvoid() sham_death1      =[      $death1,       sham_death2     ] {};\nvoid() sham_death2      =[      $death2,       sham_death3     ] {};\nvoid() sham_death3      =[      $death3,       sham_death4     ] {self.solid = SOLID_NOT;};\nvoid() sham_death4      =[      $death4,       sham_death5     ] {};\nvoid() sham_death5      =[      $death5,       sham_death6     ] {};\nvoid() sham_death6      =[      $death6,       sham_death7     ] {};\nvoid() sham_death7      =[      $death7,       sham_death8     ] {};\nvoid() sham_death8      =[      $death8,       sham_death9     ] {};\nvoid() sham_death9      =[      $death9,       sham_death10    ] {};\nvoid() sham_death10     =[      $death10,      sham_death11    ] {};\nvoid() sham_death11     =[      $death11,      sham_death11    ] {};\n\nvoid() sham_die =\n{\n// check for gib\n\tif (self.health < -60)\n\t{\n\t\tsound (self, CHAN_VOICE, \"player/udeath.wav\", 1, ATTN_NORM);\n\t\tThrowHead (\"progs/h_shams.mdl\", self.health);\n\t\tThrowGib (\"progs/gib1.mdl\", self.health);\n\t\tThrowGib (\"progs/gib2.mdl\", self.health);\n\t\tThrowGib (\"progs/gib3.mdl\", self.health);\n\t\treturn;\n\t}\n\n// regular death\n\tsound (self, CHAN_VOICE, \"shambler/sdeath.wav\", 1, ATTN_NORM);\n\tsham_death1 ();\n};\n\n//============================================================================\n\nvoid() monster_shambler =\n{\n\tif (deathmatch)\n\t{\n\t\tremove(self);\n\t\treturn;\n\t}\n\tprecache_model (\"progs/shambler.mdl\");\n\tprecache_model (\"progs/s_light.mdl\");\n\tprecache_model (\"progs/h_shams.mdl\");\n\tprecache_model (\"progs/bolt.mdl\");\n\t\n\tprecache_sound (\"shambler/sattck1.wav\");\n\tprecache_sound (\"shambler/sboom.wav\");\n\tprecache_sound (\"shambler/sdeath.wav\");\n\tprecache_sound (\"shambler/shurt2.wav\");\n\tprecache_sound (\"shambler/sidle.wav\");\n\tprecache_sound (\"shambler/ssight.wav\");\n\tprecache_sound (\"shambler/melee1.wav\");\n\tprecache_sound (\"shambler/melee2.wav\");\n\tprecache_sound (\"shambler/smack.wav\");\n\n\tself.solid = SOLID_SLIDEBOX;\n\tself.movetype = MOVETYPE_STEP;\n\tsetmodel (self, \"progs/shambler.mdl\");\n\n\tsetsize (self, VEC_HULL2_MIN, VEC_HULL2_MAX);\n\tself.health = 600;\n\n\tself.th_stand = sham_stand1;\n\tself.th_walk = sham_walk1;\n\tself.th_run = sham_run1;\n\tself.th_die = sham_die;\n\tself.th_melee = sham_melee;\n\tself.th_missile = sham_magic1;\n\tself.th_pain = sham_pain;\n\t\n\twalkmonster_start();\n};\n"
  },
  {
    "path": "quakec/fallout2/soldier.qc",
    "content": "#define mag1 currentammo\n#define maxmag1 cnt\n\n\nvoid () army_load1;\n\n//PISTOL\nvoid (float tmp, float dam) army_fire =\n{\n\tlocal vector src;\n\tlocal vector dir;\n\tlocal vector direction;\n\tlocal entity en;\n\tlocal vector org;\n\n\tif (self.enemy.sneak == 1)\n\t\ttmp = tmp * 2;\n\n\tif (self.mag1 == 0)\n\t{\n\t\tsound (self, CHAN_WEAPON, \"misc/greload.wav\", PLAT_LOW_TRIGGER, ATTN_NORM);\n\t\tarmy_load1();\n\t\tself.mag1 = self.maxmag1;\n\t\treturn;\n\t}\n\n\tself.mag1 = self.mag1 - 1;\n\n\n\tmakevectors (self.angles);\n\n\tsound (self, CHAN_WEAPON, \"weapons/1911.wav\", PLAT_LOW_TRIGGER, ATTN_NORM);\n\n\tsrc = self.origin + v_forward*10;\n\tsrc_z = self.absmin_z + self.size_z * 0.7;\n\n\ten = self.enemy;\n\t\n\tdir = en.origin - en.velocity*0.2;\n\tdir = normalize (dir - self.origin);\n\n\tdirection = dir;\n\n\ttraceline (src, src + direction*2048 + v_right*crandom()*tmp + v_up*crandom()*tmp, FALSE, self);\n\n\tif (trace_fraction == PLAT_LOW_TRIGGER)\n\t\treturn;\n\n\tif (trace_ent.takedamage)\n\t{\n\t\tdam = 1 + random()*dam + random()*dam;\n\t\tdam = dam * (1 - (trace_fraction/2));\n\t\tSpawnBlood (org, PLAT_LOW_TRIGGER);\n\t\tT_Damage (trace_ent, self, self, dam);\n\t}\n\telse\n\t{\n\t\tWriteByte (MSG_MULTICAST, SVC_TEMPENTITY);\n\t\tWriteByte (MSG_MULTICAST, TE_SPIKE);\n\t\tWriteCoord (MSG_MULTICAST, trace_endpos_x);\n\t\tWriteCoord (MSG_MULTICAST, trace_endpos_y);\n\t\tWriteCoord (MSG_MULTICAST, trace_endpos_z);\n\t\tmulticast (trace_endpos, MULTICAST_PVS);\n\t}\n};\n\n\n//RIFLE\nvoid (float tmp, float dam) army_fire1 =\n{\n\tlocal vector src;\n\tlocal vector dir;\n\tlocal vector direction;\n\tlocal entity en;\n\tlocal vector org;\n\n\tif (self.enemy.sneak == 1)\n\t\ttmp = tmp * 2;\n\n\tif (self.mag1 == 0)\n\t{\n\t\tsound (self, CHAN_WEAPON, \"misc/greload.wav\", PLAT_LOW_TRIGGER, ATTN_NORM);\n\t\tarmy_load1();\n\t\tself.mag1 = self.maxmag1;\n\t\treturn;\n\t}\n\n\tself.mag1 = self.mag1 - 1;\n\n\tmakevectors (self.angles);\n\n\tsound (self, CHAN_WEAPON, \"weapons/rangem.wav\", PLAT_LOW_TRIGGER, ATTN_NORM);\n\n\tsrc = self.origin + v_forward*10;\n\tsrc_z = self.absmin_z + self.size_z * 0.7;\n\n\ten = self.enemy;\n\t\n\tdir = en.origin - en.velocity*0.2;\n\tdir = normalize (dir - self.origin);\n\n\tdirection = dir;\n\n\ttraceline (src, src + direction*2048 + v_right*crandom()*100 + v_up*crandom()*100, FALSE, self);\n\n\tif (trace_fraction == PLAT_LOW_TRIGGER)\n\t\treturn;\n\n\tif (trace_ent.takedamage)\n\t{\n\t\tSpawnBlood (org, PLAT_LOW_TRIGGER);\n\t\tdam = 10 + random()*dam + random()*dam;\n\t\tdam = dam * (1 - (trace_fraction/2));\n\t\tT_Damage (trace_ent, self, self, dam);\n\t}\n\telse\n\t{\n\t\tWriteByte (MSG_MULTICAST, SVC_TEMPENTITY);\n\t\tWriteByte (MSG_MULTICAST, TE_SPIKE);\n\t\tWriteCoord (MSG_MULTICAST, trace_endpos_x);\n\t\tWriteCoord (MSG_MULTICAST, trace_endpos_y);\n\t\tWriteCoord (MSG_MULTICAST, trace_endpos_z);\n\t\tmulticast (trace_endpos, MULTICAST_PVS);\n\t}\n};\n\n\n//SHOTGUN\nvoid (float tmp, float dam) army_fire2 =\n{\n\tlocal vector src;\n\tlocal vector dir;\n\tlocal vector direction;\n\tlocal entity en;\n\tlocal vector org;\n\tlocal float var_u, var_r, var_o;\n\tlocal float shot;\n\n\tif (self.enemy.sneak == 1)\n\t\ttmp = tmp * 2;\n\n\tif (self.mag1 == 0)\n\t{\n\t\tsound (self, CHAN_WEAPON, \"misc/greload.wav\", PLAT_LOW_TRIGGER, ATTN_NORM);\n\t\tarmy_load1();\n\t\tself.mag1 = self.maxmag1;\n\t\treturn;\n\t}\n\n\tself.mag1 = self.mag1 - 1;\n\n\tmakevectors (self.angles);\n\n\tsound (self, CHAN_WEAPON, \"weapons/shotgun2.wav\", PLAT_LOW_TRIGGER, ATTN_NORM);\n\n\tsrc = self.origin + v_forward*10;\n\tsrc_z = self.absmin_z + self.size_z * 0.7;\n\n\ten = self.enemy;\n\t\n\tdir = en.origin - en.velocity*0.2;\n\tdir = normalize (dir - self.origin);\n\n\tdirection = dir;\n\n\tshot = 5;\n\tvar_o = crandom()*50;\n\n\twhile (shot > 0)\n\t{\n\t\t\tif (shot == 5)\n\t\t\t{\n\t\t\t\tvar_r = 30;\n\t\t\t\tvar_u = 30;\n\t\t\t}\n\t\t\tif (shot == 4)\n\t\t\t{\n\t\t\t\tvar_r = -30;\n\t\t\t\tvar_u = 30;\n\t\t\t}\n\t\t\tif (shot == 3)\n\t\t\t{\n\t\t\t\tvar_r = 30;\n\t\t\t\tvar_u = -30;\n\t\t\t}\n\t\t\tif (shot == 2)\n\t\t\t{\n\t\t\t\tvar_r = -30;\n\t\t\t\tvar_u = -30;\n\t\t\t}\n\t\t\tif (shot == 1)\n\t\t\t{\n\t\t\t\tvar_r = 0;\n\t\t\t\tvar_u = 0;\n\t\t\t}\n\n\t\t\ttraceline (src, src + direction*2048 + v_right*(var_o+var_r) + v_up*(var_o+var_u), FALSE, self);\n\n\t\t\tshot = (shot - 1);\n\n\t\t\tif (trace_fraction == PLAT_LOW_TRIGGER)\n\t\t\t\treturn;\n\n\t\t\tif (trace_ent.takedamage)\n\t\t\t{\n\t\t\t\tSpawnBlood (org, PLAT_LOW_TRIGGER);\n\t\t\t\tdam = 1 + random()*dam + random()*dam;\n\t\t\t\tdam = dam * (1 - (trace_fraction/2));\n\t\t\t\tT_Damage (trace_ent, self, self, dam);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tWriteByte (MSG_MULTICAST, SVC_TEMPENTITY);\n\t\t\t\tWriteByte (MSG_MULTICAST, TE_SPIKE);\n\t\t\t\tWriteCoord (MSG_MULTICAST, trace_endpos_x);\n\t\t\t\tWriteCoord (MSG_MULTICAST, trace_endpos_y);\n\t\t\t\tWriteCoord (MSG_MULTICAST, trace_endpos_z);\n\t\t\t\tmulticast (trace_endpos, MULTICAST_PVS);\n\t\t\t}\n\t}\n};\n\n//SMG\nvoid (float tmp, float dam) army_fire3 =\n{\n\tlocal vector src;\n\tlocal vector dir;\n\tlocal vector direction;\n\tlocal entity en;\n\tlocal vector org;\n\n\tif (self.mag1 == 0)\n\t{\n\t\tsound (self, CHAN_WEAPON, \"misc/greload.wav\", PLAT_LOW_TRIGGER, ATTN_NORM);\n\t\tarmy_load1();\n\t\tself.mag1 = self.maxmag1;\n\t\treturn;\n\t}\n\n\tif (self.enemy.sneak == 1)\n\t\ttmp = tmp * 2;\n\n\tself.mag1 = self.mag1 - 1;\n\n\tmakevectors (self.angles);\n\n\tsrc = self.origin + v_forward*10;\n\tsrc_z = self.absmin_z + self.size_z * 0.7;\n\n\ten = self.enemy;\n\t\n\tdir = en.origin - en.velocity*0.2;\n\tdir = normalize (dir - self.origin);\n\n\tdirection = dir;\n\n\ttraceline (src, src + direction*1024 + v_right*crandom()*tmp + v_up*crandom()*tmp, FALSE, self);\n\n\tif (trace_fraction == PLAT_LOW_TRIGGER)\n\t\treturn;\n\n\tif (trace_ent.takedamage)\n\t{\n\t\tSpawnBlood (org, PLAT_LOW_TRIGGER);\n\t\tdam = 1 + random()*dam + random()*dam;\n\t\tdam = dam * (1 - (trace_fraction/2));\n\t\tT_Damage (trace_ent, self, self, dam);\n\t}\n\telse\n\t{\n\t\tWriteByte (MSG_MULTICAST, SVC_TEMPENTITY);\n\t\tWriteByte (MSG_MULTICAST, TE_SPIKE);\n\t\tWriteCoord (MSG_MULTICAST, trace_endpos_x);\n\t\tWriteCoord (MSG_MULTICAST, trace_endpos_y);\n\t\tWriteCoord (MSG_MULTICAST, trace_endpos_z);\n\t\tmulticast (trace_endpos, MULTICAST_PVS);\n\t}\n};\n\n//Assault Rifle\nvoid (float tmp, float dam) army_fire4 =\n{\n\tlocal vector src;\n\tlocal vector dir;\n\tlocal vector direction;\n\tlocal entity en;\n\tlocal vector org;\n\n\tif (self.mag1 == 0)\n\t{\n\t\tsound (self, CHAN_WEAPON, \"misc/greload.wav\", PLAT_LOW_TRIGGER, ATTN_NORM);\n\t\tarmy_load1();\n\t\tself.mag1 = self.maxmag1;\n\t\treturn;\n\t}\n\n\tif (self.enemy.sneak == 1)\n\t\ttmp = tmp * 2;\n\n\tself.mag1 = self.mag1 - 1;\n\n\tmakevectors (self.angles);\n\n\tsrc = self.origin + v_forward*10;\n\tsrc_z = self.absmin_z + self.size_z * 0.7;\n\n\ten = self.enemy;\n\t\n\tdir = en.origin - en.velocity*0.2;\n\tdir = normalize (dir - self.origin);\n\n\tdirection = dir;\n\n\ttraceline (src, src + direction*2048 + v_right*crandom()*tmp + v_up*crandom()*tmp, FALSE, self);\n\n\tif (trace_fraction == PLAT_LOW_TRIGGER)\n\t\treturn;\n\n\tif (trace_ent.takedamage)\n\t{\n\t\tSpawnBlood (org, PLAT_LOW_TRIGGER);\n\t\tdam = 1 + random()*dam + random()*dam;\n\t\tdam = dam * (1 - (trace_fraction/2));\n\t\tT_Damage (trace_ent, self, self, dam);\n\t}\n\telse\n\t{\n\t\tWriteByte (MSG_MULTICAST, SVC_TEMPENTITY);\n\t\tWriteByte (MSG_MULTICAST, TE_SPIKE);\n\t\tWriteCoord (MSG_MULTICAST, trace_endpos_x);\n\t\tWriteCoord (MSG_MULTICAST, trace_endpos_y);\n\t\tWriteCoord (MSG_MULTICAST, trace_endpos_z);\n\t\tmulticast (trace_endpos, MULTICAST_PVS);\n\t}\n};\n\n\nvoid () army_load1 = [ 29, army_load2 ]\n{\n\tai_stand ();\n};\nvoid () army_load2 = [ 29, army_load3 ]\n{\n\tai_stand ();\n};\nvoid () army_load3 = [ 30, army_load4 ]\n{\n\tai_stand ();\n};\nvoid () army_load4 = [ 30, army_load5 ]\n{\n\tai_stand ();\n};\nvoid () army_load5 = [ 31, army_load6 ]\n{\n\tai_stand ();\n};\nvoid () army_load6 = [ 31, army_load7 ]\n{\n\tai_stand ();\n};\nvoid () army_load7 = [ 32, army_load8 ]\n{\n\tai_stand ();\n};\nvoid () army_load8 = [ 32, army_load9 ]\n{\n\tai_stand ();\n};\nvoid () army_load9 = [ 33, army_load10 ]\n{\n\tai_stand ();\n};\nvoid () army_load10 = [ 33, army_load11 ]\n{\n\tai_stand ();\n};\nvoid () army_load11 = [ 34, army_load12 ]\n{\n\tai_stand ();\n};\n\nvoid () army_load12 = [ 34, army_load13 ]\n{\n\tai_stand ();\n};\nvoid () army_load13 = [ 35, army_load14 ]\n{\n\tai_stand ();\n};\nvoid () army_load14 = [ 35, army_load15 ]\n{\n\tai_stand ();\n};\nvoid () army_load15 = [ 36, army_load16 ]\n{\n\tai_stand ();\n};\nvoid () army_load16 = [ 36, army_load17 ]\n{\n\tai_stand ();\n};\nvoid () army_load17 = [ 37, army_load18 ]\n{\n\tai_stand ();\n};\nvoid () army_load18 = [ 37, army_load19 ]\n{\n\tai_stand ();\n};\nvoid () army_load19 = [ 38, army_load20 ]\n{\n\tai_stand ();\n};\nvoid () army_load20 = [ 38, army_load21 ]\n{\n\tai_stand ();\n};\nvoid () army_load21 = [ 39, army_load22 ]\n{\n\tai_stand ();\n};\nvoid () army_load22 = [ 39, army_run1 ]\n{\n\tai_stand ();\n};\n\nvoid () army_stand1 = [ 0, army_stand2 ]\n{\n\tai_stand ();\n};\n\nvoid () army_stand2 = [ 1, army_stand3 ]\n{\n\tai_stand ();\n};\n\nvoid () army_stand3 = [ 2, army_stand4 ]\n{\n\tai_stand ();\n};\n\nvoid () army_stand4 = [ 3, army_stand5 ]\n{\n\tai_stand ();\n};\n\nvoid () army_stand5 = [ 4, army_stand6 ]\n{\n\tai_stand ();\n};\n\nvoid () army_stand6 = [ 5, army_stand7 ]\n{\n\tai_stand ();\n};\n\nvoid () army_stand7 = [ 6, army_stand8 ]\n{\n\tai_stand ();\n};\n\nvoid () army_stand8 = [ 7, army_stand1 ]\n{\n\tai_stand ();\n};\n\nvoid () army_walk1 = [ 73, army_walk2 ]\n{\n\tai_walk (TE_TELEPORT);\n};\n\nvoid () army_walk2 = [ 74, army_walk3 ]\n{\n\tai_walk (15);\n};\n\nvoid () army_walk3 = [ 75, army_walk4 ]\n{\n\tai_walk (TE_LAVASPLASH);\n};\n\nvoid () army_walk4 = [ 76, army_walk5 ]\n{\n\tai_walk (TE_LAVASPLASH);\n};\n\nvoid () army_walk5 = [ 77, army_walk6 ]\n{\n\tai_walk (SECRET_NO_SHOOT);\n};\n\nvoid () army_walk6 = [ 78, army_walk7 ]\n{\n\tai_walk (15);\n};\n\nvoid () army_walk7 = [ 79, army_walk8 ]\n{\n\tai_walk (TE_LAVASPLASH);\n};\n\nvoid () army_walk8 = [ 80, army_walk1 ]\n{\n\tai_walk (SECRET_NO_SHOOT);\n};\n\nvoid () army_run1 = [ 73, army_run2 ]\n{\n\n\tai_run (TE_TELEPORT);\n};\n\nvoid () army_run2 = [ 74, army_run3 ]\n{\n\tai_run (15);\n};\n\nvoid () army_run3 = [ 75, army_run4 ]\n{\n\tif (self.active == 1)\n\t\tself.action_points = self.action_points - 1;\n\n\tai_run (TE_LAVASPLASH);\n};\n\nvoid () army_run4 = [ 76, army_run5 ]\n{\n\tai_run (TE_LAVASPLASH);\n};\n\nvoid () army_run5 = [ 77, army_run6 ]\n{\n\tai_run (SECRET_NO_SHOOT);\n};\n\nvoid () army_run6 = [ 78, army_run7 ]\n{\n\tai_run (15);\n};\n\nvoid () army_run7 = [ 79, army_run8 ]\n{\n\tai_run (TE_LAVASPLASH);\n};\n\nvoid () army_run8 = [ 80, army_run1 ]\n{\n\tai_run (SECRET_NO_SHOOT);\n};\n\nvoid () army_atk1 = [ 81, army_atk2 ]\n{\n\tai_face ();\n};\n\nvoid () army_atk2 = [ 82, army_atk3 ]\n{\n\tai_face ();\n};\n\nvoid () army_atk3 = [ 83, army_atk4 ]\n{\n\tai_face ();\n};\n\nvoid () army_atk4 = [ 84, army_atk5 ]\n{\n\tai_face ();\n};\n\nvoid () army_atk5 = [ 85, army_atk6 ]\n{\n\tai_face ();\n};\n\nvoid () army_atk6 = [ 86, army_atk7 ]\n{\n\tai_face ();\n};\n\nvoid () army_atk7 = [ 87, army_atk8 ]\n{\n\tai_face ();\n\tarmy_fire (120, 14);\n};\n\nvoid () army_atk8 = [ 88, army_atk9 ]\n{\n\tai_face ();\n};\n\nvoid () army_atk9 = [ 89, army_run1 ]\n{\n\tai_face ();\n};\n\nvoid () army_atka1 = [ 81, army_atka2 ]\n{\n\tai_face ();\n};\n\nvoid () army_atka2 = [ 82, army_atka3 ]\n{\n\tai_face ();\n};\n\nvoid () army_atka3 = [ 83, army_atka4 ]\n{\n\tai_face ();\n};\n\nvoid () army_atka4 = [ 84, army_atka5 ]\n{\n\tai_face ();\n};\n\nvoid () army_atka5 = [ 85, army_atka6 ]\n{\n\tai_face ();\n\tarmy_fire2 (200, 4);\n};\n\nvoid () army_atka6 = [ 86, army_atka7 ]\n{\n\tai_face ();\n};\n\nvoid () army_atka7 = [ 87, army_atka8 ]\n{\n\tai_face ();\n};\n\nvoid () army_atka8 = [ 88, army_atka9 ]\n{\n\tai_face ();\n};\n\nvoid () army_atka9 = [ 89, army_run1 ]\n{\n\tai_face ();\n};\n\nvoid () army_atkb1 = [ 81, army_atkb2 ]\n{\n\tai_face ();\n};\n\nvoid () army_atkb2 = [ 82, army_atkb4 ]\n{\n\tai_face ();\n};\n\nvoid () army_atkb3 = [ 83, army_atkb4 ]\n{\n\tai_face ();\n};\n\nvoid () army_atkb4 = [ 84, army_atkb5 ]\n{\n\tai_face ();\n};\n\nvoid () army_atkb5 = [ 85, army_atkb6 ]\n{\n\tlocal float r;\n\n\tai_face ();\n\tr = range (self.enemy);\n\n\tself.recoil = 0;\n\n\tif (r == RANGE_NEAR || r == RANGE_MELEE)\n\t\tself.recoil = 1;\n\n\tif (self.recoil == 1)\n\t\tsound (self, CHAN_WEAPON, \"weapons/burst.wav\", PLAT_LOW_TRIGGER, ATTN_NORM);\n\telse\n\t\tsound (self, CHAN_WEAPON, \"weapons/mp7.wav\", PLAT_LOW_TRIGGER, ATTN_NORM);\n\n\tarmy_fire3 (150, 13);\n};\n\nvoid () army_atkb6 = [ 86, army_atkb7 ]\n{\n\tai_face ();\n\tif (self.recoil == 1)\n\t{\n\t\tarmy_fire3 (170, 11);\n\t\tarmy_fire3 (190, 9);\n\t}\n};\n\nvoid () army_atkb7 = [ 87, army_atkb8 ]\n{\n\tai_face ();\n\tif (self.recoil == 1)\n\t\tarmy_fire3 (210, 11);\n};\n\nvoid () army_atkb8 = [ 88, army_atkb9 ]\n{\n\tai_face ();\n\tif (self.recoil == 1)\n\t{\n\t\tarmy_fire3 (230, 11);\n\t\tarmy_fire3 (250, 9);\n\t}\n};\n\nvoid () army_atkb9 = [ 89, army_run1 ]\n{\n\tai_face ();\n\tif (self.recoil == 1)\n\t{\n\t\tarmy_fire3 (270, 11);\n\t\tarmy_fire3 (290, 9);\n\t}\n};\n\nvoid () army_atkcs1 = [ 81, army_atkcs2 ]\n{\n\tai_face ();\n};\n\nvoid () army_atkcs2 = [ 82, army_atkcs3 ]\n{\n\tai_face ();\n};\n\nvoid () army_atkcs3 = [ 83, army_atkcs4 ]\n{\n\tai_face ();\n};\n\nvoid () army_atkcs4 = [ 84, army_atkcs5 ]\n{\n\tai_face ();\n};\n\nvoid () army_atkcs5 = [ 85, army_atkcs6 ]\n{\n\tai_face ();\n\tsound (self, CHAN_WEAPON, \"weapons/ak112.wav\", PLAT_LOW_TRIGGER, ATTN_NORM);\n\tarmy_fire4 (100, 16);\n};\n\nvoid () army_atkcs6 = [ 85, army_atkcs7 ]\n{\n\n\tai_face ();\n};\n\nvoid () army_atkcs7 = [ 87, army_atkcs8 ]\n{\n\n\tai_face ();\n};\n\nvoid () army_atkcs8 = [ 88, army_atkcs9 ]\n{\n\n\tai_face ();\n};\n\nvoid () army_atkcs9 = [ 89, army_run1 ]\n{\n\tai_face ();\n};\n\nvoid () army_atkc1 = [ 81, army_atkc2 ]\n{\n\tai_face ();\n};\n\nvoid () army_atkc2 = [ 82, army_atkc3 ]\n{\n\tai_face ();\n};\n\nvoid () army_atkc3 = [ 83, army_atkc4 ]\n{\n\tai_face ();\n};\n\nvoid () army_atkc4 = [ 84, army_atkc5 ]\n{\n\tai_face ();\n};\n\nvoid () army_assault_rifle =\n{\n\tlocal float r;\n\n\tr = range (self.enemy);\n\n\tif (r == RANGE_FAR || r == RANGE_MID)//single shot at range\n\t\tarmy_atkcs1();\n\tif (r == RANGE_NEAR || r == RANGE_MELEE)//open up when close\n\t\tarmy_atkc1();\n};\n\nvoid () army_atkc5 = [ 85, army_atkc6 ]\n{\n\tai_face ();\n\n\tsound (self, CHAN_WEAPON, \"weapons/auto.wav\", PLAT_LOW_TRIGGER, ATTN_NORM);\n\tarmy_fire4 (100, 16);\n};\n\nvoid () army_atkc6 = [ 84, army_atkc7 ]\n{\n\tai_face ();\n\tarmy_fire4 (140, 16);\n};\n\nvoid () army_atkc7 = [ 85, army_atkc8 ]\n{\n\tai_face ();\n\tarmy_fire4 (180, 16);\n};\n\nvoid () army_atkc8 = [ 84, army_atkc9 ]\n{\n\tai_face ();\n\tarmy_fire4 (220, 16);\n};\n\nvoid () army_atkc9 = [ 85, army_atkc10 ]\n{\n\tai_face ();\n\tarmy_fire4 (260, 16);\n};\n\nvoid () army_atkc10 = [ 84, army_atkc11 ]\n{\n\tai_face ();\n\tarmy_fire4 (300, 16);\n};\n\nvoid () army_atkc11 = [ 85, army_atkc12 ]\n{\n\tai_face ();\n\tarmy_fire4 (340, 16);\n};\n\nvoid () army_atkc12 = [ 84, army_atkc13 ]\n{\n\tai_face ();\n\tarmy_fire4 (380, 16);\n};\n\nvoid () army_atkc13 = [ 86, army_atkc14 ]\n{\n\tai_face ();\n};\n\nvoid () army_atkc14 = [ 87, army_atkc15 ]\n{\n\tai_face ();\n};\n\nvoid () army_atkc15 = [ 88, army_atkc16 ]\n{\n\tai_face ();\n};\n\nvoid () army_atkc16 = [ 89, army_run1 ]\n{\n\tai_face ();\n};\n\nvoid () army_atkp1 = [ 81, army_atkp2 ]\n{\n\tai_face ();\n};\n\nvoid () army_atkp2 = [ 82, army_atkp3 ]\n{\n\tai_face ();\n};\n\nvoid () army_atkp3 = [ 83, army_atkp4 ]\n{\n\tai_face ();\n};\n\nvoid () army_atkp4 = [ 84, army_atkp5 ]\n{\n\tai_face ();\n};\n\nvoid () army_atkp5 = [ 85, army_atkp6 ]\n{\n\tai_face ();\n\tarmy_fire1 (200, 15);\n};\n\nvoid () army_atkp6 = [ 86, army_atkp7 ]\n{\n\tai_face ();\n};\n\nvoid () army_atkp7 = [ 87, army_atkp8 ]\n{\n\tai_face ();\n};\n\nvoid () army_atkp8 = [ 88, army_atkp9 ]\n{\n\tai_face ();\n};\n\nvoid () army_atkp9 = [ 89, army_run1 ]\n{\n\tai_face ();\n};\n\nvoid () army_pain1 = [ 40, army_pain2 ]\n{\n};\n\nvoid () army_pain2 = [ 41, army_pain3 ]\n{\n};\n\nvoid () army_pain3 = [ 42, army_pain4 ]\n{\n};\n\nvoid () army_pain4 = [ 43, army_pain5 ]\n{\n};\n\nvoid () army_pain5 = [ 44, army_pain6 ]\n{\n};\n\nvoid () army_pain6 = [ 45, army_run1 ]\n{\n\tai_pain (PLAT_LOW_TRIGGER);\n};\n\nvoid () army_painb1 = [ 46, army_painb2 ]\n{\n};\n\nvoid () army_painb2 = [ 47, army_painb3 ]\n{\n\tai_painforward (TE_LIGHTNINGBLOOD);\n};\n\nvoid () army_painb3 = [ 48, army_painb4 ]\n{\n\tai_painforward (TE_LIGHTNING3);\n};\n\nvoid () army_painb4 = [ 49, army_painb5 ]\n{\n};\n\nvoid () army_painb5 = [ 50, army_painb6 ]\n{\n};\n\nvoid () army_painb6 = [ 51, army_painb7 ]\n{\n};\n\nvoid () army_painb7 = [ 52, army_painb8 ]\n{\n};\n\nvoid () army_painb8 = [ 53, army_painb9 ]\n{\n};\n\nvoid () army_painb9 = [ 54, army_painb10 ]\n{\n};\n\nvoid () army_painb10 = [ 55, army_painb11 ]\n{\n};\n\nvoid () army_painb11 = [ 56, army_painb12 ]\n{\n};\n\nvoid () army_painb12 = [ 57, army_painb13 ]\n{\n\tai_pain (SILENT);\n};\n\nvoid () army_painb13 = [ 58, army_painb14 ]\n{\n};\n\nvoid () army_painb14 = [ 59, army_run1 ]\n{\n};\n\nvoid () army_painc1 = [ 60, army_painc2 ]\n{\n};\n\nvoid () army_painc2 = [ 61, army_painc3 ]\n{\n\tai_pain (PLAT_LOW_TRIGGER);\n};\n\nvoid () army_painc3 = [ 62, army_painc4 ]\n{\n};\n\nvoid () army_painc4 = [ 63, army_painc5 ]\n{\n};\n\nvoid () army_painc5 = [ 64, army_painc6 ]\n{\n\tai_painforward (PLAT_LOW_TRIGGER);\n};\n\nvoid () army_painc6 = [ 65, army_painc7 ]\n{\n\tai_painforward (PLAT_LOW_TRIGGER);\n};\n\nvoid () army_painc7 = [ 66, army_painc8 ]\n{\n};\n\nvoid () army_painc8 = [ 67, army_painc9 ]\n{\n\tai_pain (PLAT_LOW_TRIGGER);\n};\n\nvoid () army_painc9 = [ 68, army_painc10 ]\n{\n\tai_painforward (SECRET_1ST_DOWN);\n};\n\nvoid () army_painc10 = [ 69, army_painc11 ]\n{\n\tai_painforward (AS_MELEE);\n};\n\nvoid () army_painc11 = [ 70, army_painc12 ]\n{\n\tai_painforward (TE_LIGHTNING2);\n};\n\nvoid () army_painc12 = [ 71, army_painc13 ]\n{\n\tai_painforward (SECRET_NO_SHOOT);\n};\n\nvoid () army_painc13 = [ 72, army_run1 ]\n{\n};\n\nvoid (entity attacker, float damage) army_pain =\n{\n\tlocal float r;\n\n\tif ((self.pain_finished > time))\n\t{\n\t\treturn;\n\t}\n\tr = random ();\n\tif ((r < 0.05))\n\t{\n\t\tself.pain_finished = (time + 0.6);\n\t\tarmy_pain1 ();\n\t\tsound (self, CHAN_VOICE, \"player/paina.wav\", PLAT_LOW_TRIGGER, ATTN_NORM);\n\t}\n\telse\n\t{\n\t\tif ((r < 0.1))\n\t\t{\n\t\t\tself.pain_finished = (time + 1.1);\n\t\t\tarmy_painb1 ();\n\t\t\tsound (self, CHAN_VOICE, \"player/painb.wav\", PLAT_LOW_TRIGGER, ATTN_NORM);\n\t\t}\n\t}\n};\n\nvoid () army_die1 = [ 8, army_die2 ]\n{\n\n\tif (random()*4 <= 2)\n\t\tsound (self, CHAN_VOICE, \"player/agdie2.wav\", 1, ATTN_NORM);\n\telse\n\t\tsound (self, CHAN_VOICE, \"player/agdie3.wav\", 1, ATTN_NORM);\n\n};\n\nvoid () army_die2 = [ 9, army_die3 ]\n{\n\tself.solid = SOLID_NOT;\n};\n\nvoid () army_die3 = [ 10, army_die4 ]\n{\n};\n\nvoid () army_die4 = [ 11, army_die5 ]\n{\n};\n\nvoid () army_die5 = [ 12, army_die6 ]\n{\n};\n\nvoid () army_die6 = [ 13, army_die7 ]\n{\n};\n\nvoid () army_die7 = [ 14, army_die8 ]\n{\n};\n\nvoid () army_die8 = [ 15, army_die9 ]\n{\n};\n\nvoid () army_die9 = [ 16, army_die10 ]\n{\n};\n\nvoid () army_die10 = [ 17, army_die10 ]\n{\n};\n\nvoid () army_cdie1 = [ 18, army_cdie2 ]\n{\n};\n\nvoid () army_cdie2 = [ 19, army_cdie3 ]\n{\n\tself.solid = SOLID_NOT;\n\tai_back (MULTICAST_PVS_R);\n};\n\nvoid () army_cdie3 = [ 20, army_cdie4 ]\n{\n\tai_back (SECRET_1ST_DOWN);\n};\n\nvoid () army_cdie4 = [ 21, army_cdie5 ]\n{\n\tai_back (TE_LIGHTNINGBLOOD);\n};\n\nvoid () army_cdie5 = [ 22, army_cdie6 ]\n{\n\tai_back (AS_MELEE);\n};\n\nvoid () army_cdie6 = [ 23, army_cdie7 ]\n{\n\tai_back (SECRET_1ST_DOWN);\n};\n\nvoid () army_cdie7 = [ 24, army_cdie8 ]\n{\n};\n\nvoid () army_cdie8 = [ 25, army_cdie9 ]\n{\n};\n\nvoid () army_cdie9 = [ 26, army_cdie10 ]\n{\n};\n\nvoid () army_cdie10 = [ 27, army_cdie11 ]\n{\n};\n\nvoid () army_cdie11 = [ 28, army_cdie11 ]\n{\n};\n\n\nvoid () grunt_pain =\n{\n\tif (self.frame == 41)\n\t{\n\t\tself.frame = 42;\n\t\tself.health = 180;\n\t\tself.think = army_die1;\n\t\tself.nextthink = time + 0.12;\n\t}\n\telse\n\t{\n\t\tThrowGib (\"progs/zom_gib.mdl\", -40);\n\t\tself.frame = 41;\n\t\tself.health = 180;\n\t\tself.think = army_cdie1;\n\t\tself.nextthink = time + 0.12;\n\t}\n\n\tself.attack = self.attack + 1;\n\n\tif (self.attack == 8)\n\t{\n\t\tif (random()*4 <= 2)\n\t\t\tsound (self, CHAN_VOICE, \"player/agdie1.wav\", 1, ATTN_NORM);\n\t\telse\n\t\t\tsound (self, CHAN_VOICE, \"player/agdie5.wav\", 1, ATTN_NORM);\n\t}\n\tif (self.attack >= 16)\n\t{\n\t\tself.solid = SOLID_NOT;\n\n\t\tarmy_cdie1();\n\t\tThrowGib (\"progs/zom_gib.mdl\", -35);\n\t\tThrowGib (\"progs/zom_gib.mdl\", -35);\n\t\tThrowGib (\"progs/gib1.mdl\", -35);\n\t}\n};\n\nvoid (vector stuff, vector ang) spawn_live_grunt =\n{\n\tlocal entity grunt;\n\n\tgrunt = spawn ();\n\tgrunt.origin = stuff;\n\tgrunt.enemy = world;\n\tgrunt.attack_finished = time + 10;\n\tgrunt.solid = SOLID_SLIDEBOX;\n\tgrunt.movetype = MOVETYPE_STEP;\n\tgrunt.takedamage = DAMAGE_YES;\n\tsetmodel (grunt, \"progs/soldier.mdl\");\n\tsetsize (grunt, VEC_HULL_MIN, '16 16 40');\n\tgrunt.classname = \"body\";\n\tgrunt.netname = \"dead\";\n\tgrunt.health = 180;\n\tgrunt.angles = ang;\n\tgrunt.max_health = grunt.health;\n\tgrunt.th_pain = grunt_pain;\n\tgrunt.think = grunt_pain;\n\tgrunt.nextthink = time + 0.05;\n};\n\nvoid () army_die =\n{\n\tDropBackpack ();\n\n\tif (self.health <= -35)\n\t{\n\t\tself.solid = SOLID_NOT;\n\t\tsound (self, CHAN_VOICE, \"player/udeath.wav\", 1, ATTN_NORM);\n\t\tarmy_die1 ();\n\t}\n\telse\n\t{\n\t\tself.solid = SOLID_NOT;\n\t\tspawn_live_grunt(self.origin, self.angles);\n\t\tremove(self);\n\t}\n};\n\nvoid () monster_army =\n{\n\tlocal float x;\n\n\tx = total_players;\n\n\tif (random()*10 <= x*0.5)\n\t{\t\n\t\tprecache_model2 (\"progs/enforcer.mdl\");\n\t\tprecache_model2 (\"progs/h_mega.mdl\");\n\t\tprecache_model2 (\"progs/laser.mdl\");\n\n\t\tprecache_sound2 (\"enforcer/death1.wav\");\n\t\tprecache_sound2 (\"enforcer/enfire.wav\");\n\t\tprecache_sound2 (\"enforcer/enfstop.wav\");\n\t\tprecache_sound2 (\"enforcer/idle1.wav\");\n\t\tprecache_sound2 (\"enforcer/pain1.wav\");\n\t\tprecache_sound2 (\"enforcer/pain2.wav\");\n\t\tprecache_sound2 (\"enforcer/sight1.wav\");\n\t\tprecache_sound2 (\"enforcer/sight2.wav\");\n\t\tprecache_sound2 (\"enforcer/sight3.wav\");\n\t\tprecache_sound2 (\"enforcer/sight4.wav\");\n\t\n\t\tself.solid = SOLID_SLIDEBOX;\n\t\tself.movetype = MOVETYPE_STEP;\n\t\tself.team = 3;\n\n\t\tsetmodel (self, \"progs/enforcer.mdl\");\n\t\tself.classname = \"monster\";\n\t\tself.netname = \"enforcer\";\n\n\t\tsetsize (self, '-16 -16 -24', '16 16 32');\n\t\tself.health = 120;\n            self.islot3 = SlotVal(IID_ARM_COMBAT, 1);\n\t\tself.armortype = 35;\n\t\tself.th_stand = enf_stand1;\n\t\tself.th_walk = enf_walk1;\n\t\tself.th_run = enf_run1;\n\t\tself.th_pain = enf_pain;\n\t\tself.th_die = enf_die;\n\t\tself.th_missile = enf_atk1;\n\t\tself.armornoise = \"misc/thud.wav\";\n\n\t\twalkmonster_start();\n\t\tif (random()*4 <= 2)\n\t\t\tself.weapon = 5;\n\t\telse\n\t\t\tself.weapon = 6;\n\t\tself.th_missile = enf_atk1;\n\t\tself.mag1 = 24;\n\t\tself.maxmag1 = self.mag1;\n\t\treturn;\n\t}\n\n\tprecache_model (\"progs/soldier.mdl\");\n\tprecache_model (\"progs/h_guard.mdl\");\n\tprecache_model (\"progs/gib1.mdl\");\n\tprecache_model (\"progs/gib2.mdl\");\n\tprecache_model (\"progs/gib3.mdl\");\n\tprecache_sound (\"soldier/death1.wav\");\n\tprecache_sound (\"soldier/idle.wav\");\n\tprecache_sound (\"soldier/pain1.wav\");\n\tprecache_sound (\"soldier/pain2.wav\");\n\tprecache_sound (\"soldier/sattck1.wav\");\n\tprecache_sound (\"soldier/sight1.wav\");\n\tprecache_sound (\"player/udeath.wav\");\n\n\tself.solid = SOLID_SLIDEBOX;\n\tself.classname = \"monster\";\n\tself.netname = \"raider\";\n\tself.movetype = MOVETYPE_STEP;\n\tsetmodel (self, \"progs/soldier.mdl\");\n\tsetsize (self, '-16 -16 -24', '16 16 28');\n\tself.health = 90;\n\tself.team = 3;\n            self.islot3 = SlotVal(IID_ARM_LEATHER, 1);\n\tself.armortype = 0.2;\n\tself.armornoise = \"misc/thud.wav\";\n\tself.th_stand = army_stand1;\n\tself.th_walk = army_walk1;\n\tself.th_run = army_run1;\n\tself.th_missile = army_atk1;\n\tself.th_pain = army_pain;\n\tself.th_die = army_die;\n\twalkmonster_start ();\n\t\n\tself.weapon = ceil(random()*4);\n\n\tif (self.weapon == 1)\n\t\tself.th_missile = army_atkp1; //pistol\n\tif (self.weapon == 2)\n\t\tself.th_missile = army_atk1; //rifle\n\tif (self.weapon == 3)\n\t\tself.th_missile = army_atka1; //shotgun\n\tif (self.weapon == 4)\n\t\tself.th_missile = army_atkb1; //smg\n\n\tif (self.weapon == 1)\n\t\tself.mag1 = 12;\n\tif (self.weapon == 2)\n\t\tself.mag1 = 10;\n\tif (self.weapon == 3)\n\t\tself.mag1 = 6;\n\tif (self.weapon == 4)\n\t\tself.mag1 = 30;\n\n\tself.maxmag1 = self.mag1;\n};\n\n#undef mag1\n#undef maxmag1\n"
  },
  {
    "path": "quakec/fallout2/spectate.qc",
    "content": "// Spectator functions\n// Added Aug11'97 by Zoid <zoid@idsoftware.com>\n//\n// These functions are called from the server if they exist.\n// Note that Spectators only have one think since they movement code doesn't\n// track them much.  Impulse commands work as usual, but don't call\n// the regular ImpulseCommand handler in weapons.qc since Spectators don't\n// have any weapons and things can explode.\n//\n//   --- Zoid.\n\n/*\n===========\nSpectatorConnect\n\ncalled when a spectator connects to a server\n============\n*/\nvoid() SpectatorConnect =\n{\n\tbprint (PRINT_MEDIUM, \"Spectator \");\n\tbprint (PRINT_MEDIUM, self.netname);\n\tbprint (PRINT_MEDIUM, \" entered the game\\n\");\n\n\tself.goalentity = world; // used for impulse 1 below\n};\n\n/*\n===========\nSpectatorDisconnect\n\ncalled when a spectator disconnects from a server\n============\n*/\nvoid() SpectatorDisconnect =\n{\n\tbprint (PRINT_MEDIUM, \"Spectator \");\n\tbprint (PRINT_MEDIUM, self.netname);\n\tbprint (PRINT_MEDIUM, \" left the game\\n\");\n};\n\n/*\n================\nSpectatorImpulseCommand\n\nCalled by SpectatorThink if the spectator entered an impulse\n================\n*/\nvoid() SpectatorImpulseCommand =\n{\n\tif (self.impulse == 1) {\n\t\t// teleport the spectator to the next spawn point\n\t\t// note that if the spectator is tracking, this doesn't do\n\t\t// much\n\t\tself.goalentity = find(self.goalentity, classname, \"info_player_deathmatch\");\n\t\tif (self.goalentity == world)\n\t\t\tself.goalentity = find(self.goalentity, classname, \"info_player_deathmatch\");\n\t\tif (self.goalentity != world) {\n\t\t\tsetorigin(self, self.goalentity.origin);\n\t\t\tself.angles = self.goalentity.angles;\n\t\t\tself.fixangle = TRUE;           // turn this way immediately\n\t\t}\n\t}\n\n\tself.impulse = 0;\n};\n\n/*\n================\nSpectatorThink\n\nCalled every frame after physics are run\n================\n*/\nvoid() SpectatorThink =\n{\n\t// self.origin, etc contains spectator position, so you could\n\t// do some neat stuff here\n\n\tif (self.impulse)\n\t\tSpectatorImpulseCommand();\n};\n\n\n"
  },
  {
    "path": "quakec/fallout2/sprites.qc",
    "content": "\n// these are the only sprites still in the game...\n\n$spritename s_explod\n$type vp_parallel\n$load /raid/quake/id1/gfx/sprites/explod03.lbm\n$frame\t24\t24\t56\t56\n$frame\t120\t24\t56\t56\n$frame\t216\t24\t56\t56\n$frame\t24\t88\t56\t56\n$frame\t120\t88\t56\t56\n$frame\t216\t88\t56\t56\n\n\n$spritename s_bubble\n$type vp_parallel\n$load /raid/quake/id1/gfx/sprites/bubble.lbm\n$frame\t16\t16\t16\t16\n$frame\t40\t16\t16\t16\n\n\n$spritename s_light\n$type vp_parallel\n$load /raid/quake/id1/gfx/sprites/light.lbm\n$frame\t104\t32\t32\t32\n\n"
  },
  {
    "path": "quakec/fallout2/subs.qc",
    "content": "\n\nvoid() SUB_Null = {};\n\nvoid() SUB_Remove = {remove(self);};\n\n\nvoid(float normal) SUB_AttackFinished =\n{\n\tself.cnt = 0;\t\t// refire count for nightmare\n\tself.attack_finished = time + normal;\n};\n\n/*\nQuakeEd only writes a single float for angles (bad idea), so up and down are\njust constant angles.\n*/\nvoid() SetMovedir =\n{\n\tif (self.angles == '0 -1 0')\n\t\tself.movedir = '0 0 1';\n\telse if (self.angles == '0 -2 0')\n\t\tself.movedir = '0 0 -1';\n\telse\n\t{\n\t\tmakevectors (self.angles);\n\t\tself.movedir = v_forward;\n\t}\n\t\n\tself.angles = '0 0 0';\n};\n\n/*\n================\nInitTrigger\n================\n*/\nvoid() InitTrigger =\n{\n// trigger angles are used for one-way touches.  An angle of 0 is assumed\n// to mean no restrictions, so use a yaw of 360 instead.\n\tif (self.angles != '0 0 0')\n\t\tSetMovedir ();\n\tself.solid = SOLID_TRIGGER;\n\tsetmodel (self, self.model);\t// set size and link into world\n\tself.movetype = MOVETYPE_NONE;\n\tself.modelindex = 0;\n\tself.model = \"\";\n};\n\n/*\n=============\nSUB_CalcMove\n\ncalculate self.velocity and self.nextthink to reach dest from\nself.origin traveling at speed\n===============\n*/\nvoid(entity ent, vector tdest, float tspeed, void() func) SUB_CalcMoveEnt =\n{\nlocal entity\tstemp;\n\tstemp = self;\n\tself = ent;\n\n\tSUB_CalcMove (tdest, tspeed, func);\n\tself = stemp;\n};\n\nvoid(vector tdest, float tspeed, void() func) SUB_CalcMove =\n{\nlocal vector\tvdestdelta;\nlocal float\t\tlen, traveltime;\n\n\tif (!tspeed)\n\t\tobjerror(\"No speed is defined!\");\n\n\tself.think1 = func;\n\tself.finaldest = tdest;\n\tself.think = SUB_CalcMoveDone;\n\n\tif (tdest == self.origin)\n\t{\n\t\tself.velocity = '0 0 0';\n\t\tself.nextthink = self.ltime + 0.1;\n\t\treturn;\n\t}\n\t\t\n// set destdelta to the vector needed to move\n\tvdestdelta = tdest - self.origin;\n\t\n// calculate length of vector\n\tlen = vlen (vdestdelta);\n\t\n// divide by speed to get time to reach dest\n\ttraveltime = len / tspeed;\n\n\tif (traveltime < 0.03)\n\t\ttraveltime = 0.03;\n\t\n// set nextthink to trigger a think when dest is reached\n\tself.nextthink = self.ltime + traveltime;\n\n// scale the destdelta vector by the time spent traveling to get velocity\n\tself.velocity = vdestdelta * (1/traveltime);\t// qcc won't take vec/float\t\n};\n\n/*\n============\nAfter moving, set origin to exact final destination\n============\n*/\nvoid()  SUB_CalcMoveDone =\n{\n\tsetorigin(self, self.finaldest);\n\tself.velocity = '0 0 0';\n\tself.nextthink = -1;\n\tif (self.think1)\n\t\tself.think1();\n};\n\n\n/*\n=============\nSUB_CalcAngleMove\n\ncalculate self.avelocity and self.nextthink to reach destangle from\nself.angles rotating \n\nThe calling function should make sure self.think is valid\n===============\n*/\nvoid(entity ent, vector destangle, float tspeed, void() func) SUB_CalcAngleMoveEnt =\n{\nlocal entity\t\tstemp;\n\tstemp = self;\n\tself = ent;\n\tSUB_CalcAngleMove (destangle, tspeed, func);\n\tself = stemp;\n};\n\nvoid(vector destangle, float tspeed, void() func) SUB_CalcAngleMove =\n{\nlocal vector\tdestdelta;\nlocal float\t\tlen, traveltime;\n\n\tif (!tspeed)\n\t\tobjerror(\"No speed is defined!\");\n\t\t\n// set destdelta to the vector needed to move\n\tdestdelta = destangle - self.angles;\n\t\n// calculate length of vector\n\tlen = vlen (destdelta);\n\t\n// divide by speed to get time to reach dest\n\ttraveltime = len / tspeed;\n\n// set nextthink to trigger a think when dest is reached\n\tself.nextthink = self.ltime + traveltime;\n\n// scale the destdelta vector by the time spent traveling to get velocity\n\tself.avelocity = destdelta * (1 / traveltime);\n\t\n\tself.think1 = func;\n\tself.finalangle = destangle;\n\tself.think = SUB_CalcAngleMoveDone;\n};\n\n/*\n============\nAfter rotating, set angle to exact final angle\n============\n*/\nvoid() SUB_CalcAngleMoveDone =\n{\n\tself.angles = self.finalangle;\n\tself.avelocity = '0 0 0';\n\tself.nextthink = -1;\n\tif (self.think1)\n\t\tself.think1();\n};\n\n\n//=============================================================================\n\nvoid() DelayThink =\n{\n\tactivator = self.enemy;\n\tSUB_UseTargets ();\n\tremove(self);\n};\n\n/*\n==============================\nSUB_UseTargets\n\nthe global \"activator\" should be set to the entity that initiated the firing.\n\nIf self.delay is set, a DelayedUse entity will be created that will actually\ndo the SUB_UseTargets after that many seconds have passed.\n\nCenterprints any self.message to the activator.\n\nRemoves all entities with a targetname that match self.killtarget,\nand removes them, so some events can remove other triggers.\n\nSearch for (string)targetname in all entities that\nmatch (string)self.target and call their .use function\n\n==============================\n*/\nvoid() SUB_UseTargets =\n{\n\tlocal entity t, stemp, otemp, act;\n\n//\n// check for a delay\n//\n\tif (self.delay)\n\t{\n\t// create a temp object to fire at a later time\n\t\tt = spawn();\n\t\tt.classname = \"DelayedUse\";\n\t\tt.nextthink = time + self.delay;\n\t\tt.think = DelayThink;\n\t\tt.enemy = activator;\n\t\tt.message = self.message;\n\t\tt.killtarget = self.killtarget;\n\t\tt.target = self.target;\n\t\treturn;\n\t}\n\t\n\t\n//\n// print the message\n//\n\tif (activator.classname == \"player\" && self.message != \"\")\n\t{\n\t\tcenterprint (activator, self.message);\n\t\tif (!self.noise)\n\t\t\tsound (activator, CHAN_VOICE, \"misc/talk.wav\", 1, ATTN_NORM);\n\t}\n\n//\n// kill the killtagets\n//\n\tif (self.killtarget)\n\t{\n\t\tt = world;\n\t\tdo\n\t\t{\n\t\t\tt = find (t, targetname, self.killtarget);\n\t\t\tif (!t)\n\t\t\t\treturn;\n\t\t\tremove (t);\n\t\t} while ( 1 );\n\t}\n\t\n//\n// fire targets\n//\n\tif (self.target)\n\t{\n\t\tact = activator;\n\t\tt = world;\n\t\tdo\n\t\t{\n\t\t\tt = find (t, targetname, self.target);\n\t\t\tif (!t)\n\t\t\t{\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tstemp = self;\n\t\t\totemp = other;\n\t\t\tself = t;\n\t\t\tother = stemp;\n\t\t\tif (self.use != SUB_Null)\n\t\t\t{\n\t\t\t\tif (self.use)\n\t\t\t\t\tself.use ();\n\t\t\t}\n\t\t\tself = stemp;\n\t\t\tother = otemp;\n\t\t\tactivator = act;\n\t\t} while ( 1 );\n\t}\n\t\n\n};\n\n"
  },
  {
    "path": "quakec/fallout2/tf.qc",
    "content": "entity tfdetect;\n\n.float items_allowed;\n.string broadcast;\n\nentity(float no) FindItem;\n\n//#define dprint(x)\n\nfloat(entity trigger, entity blame) triggercantouch =\n{\n\tif (blame.classname != \"player\")\n\t{\n//\t\tdprint(\"Not a player\\n\");\n\t\treturn false;\n\t}\n\tif (trigger.team_no)\n\t\tif (blame.team_no != trigger.team_no)\n\t\t{\n//\t\t\tdprint(\"Wrong team\\n\");\n\t\t\treturn false;\n\t\t}\n\tif (trigger.items_allowed)\n\t\tif ((FindItem(trigger.items_allowed)).owner != blame)\n\t\t{\n//\t\t\tdprint(\"missing an item\\n\");\n\t\t\treturn false;\n\t\t}\n\n\treturn true;\n};\n\n\n\n/*\nTeamFortress maps will be automatically detected by the TeamFortress patch\nif you put an entity in the map with a classname of \"info_tfdetect\".\nThis makes TeamFortress automatically turn on the FORTRESSMAP toggleflag,\nand turn on teamplay.\n\nAlso, once the TeamFortress map has been detected, the patch will look\nfor any spawnpoints dedicated to teams (see below).\nIf it finds any, it will look for the highest team number that is used.\nOnce found, it will limit anybody attempting to join a team to that \nnumber.\nAt the moment, Maps are limited to a maximum of 4 teams.\nE.g. If the highest team number used by a Team Spawnpoint is 3, then\n\t the patch will only allow players to join teams 1, 2, and 3.\n\n\nThe detection entity can also do the following:\n\t- specify a version string in the \"broadcast\" variable.\n\t  This will be compared with the version string for the TeamFortress\n\t  patch. If the don't match, a warning will be displayed to the\n\t  player that their patch and the map are incompatible.\n\t  The version string is in the format as follows:\n\t\tTeamFortress v2.1\n\t  The string is case sensitive, so make sure you've got it right.\n\t  Don't put a \\n on the end of it.\n\n\t- Set the state of the toggleflags when the map starts up. The\n\t  value of the toggleflags should be in the \"impulse\" variable.\n          The bits for the toggleflags are:\n\n    Bit 1 (1)   :   Off - ClasSkin              , On - Multiskin\n    Bit 2 (2)   :   Off - ClassPersistence Off  , On - ClassPersistence On\n    Bit 3 (4)   :   Off - CheatChecking Off     , On - CheatChecking On\n    Bit 4 (8)   :   Off - FortressMap Off       , On - FortressMap On\n    Bit 5 (16)  :   Off - RespawnDelay Off      , On - RespawnDelay (See below)\n    Bit 6 (32)  :   Off - RespawnDelay Off      , On - RespawnDelay (See below)\n    Bit 7 (64)  :   Off - AutoTeam Off          , On - AutoTeam On\n\tBit 8 (128)\t:\tOff - Individual Frags\t\t, On - Frags = TeamScore\n\n        N.B. FortressMap will be set On automatically by the the\n             Detection entity anyway, so just ignore that Bit.\n\n\t\tN.B. The RespawnDelay settings takes 2 bits. The value of both of\n\t\t\t them determines the level of respawn delay, as follows:\n\t\t\t\tBit 5  \t\tBit 6\t\tResult\n\t\t\t\t Off\t\t Off\t\tNo Respawn delays\n\t\t\t\t On\t\t\t Off\t\t5 Second respawn delay\n\t\t\t\t Off\t\t On\t\t\t10 Second respawn delay\n\t\t\t\t On\t\t\t On\t\t\t20 Second respawn delay\n\n\t- Specify a string which is then \"localcmd\"ed. This allows you\n\t  to automatically set gravity, friction, etc.\n\t  The string should be stored in the \"message\" variable.\n\n\t- Put a limit on the number of lives each player has. When a player\n\t  runs out of lives, they are stuck in observer mode for the rest\n\t  of the level.\n\t  Lives for each player depend on the setting for the team they\n\t  belong to. The number of lives players of each team be should\n\t  be specified in the following variables:\n\t  \tTeam 1\t:\t\"ammo_shells\"\n\t  \tTeam 2\t:\t\"ammo_nails\"\n\t  \tTeam 3\t:\t\"ammo_rockets\"\n\t  \tTeam 4\t:\t\"ammo_cells\"\n\t  If the value of any team is 0, then the players of that team get\n\t  infinite lives.\n\n\t- Specify any playerclass that is _not_ allowed on this map\n\t  for each particular team.\n\t  The bits of the four variables are used for this.\n\t  The variables are as follows:\n\t  \tTeam 1\t:\t\"maxammo_shells\"\n\t  \tTeam 2\t:\t\"maxammo_nails\"\n\t  \tTeam 3\t:\t\"maxammo_rockets\"\n\t  \tTeam 4\t:\t\"maxammo_cells\"\n\t  Also, the \"playerclass\" variable can be used to restrict classes\n\t  for all teams.\n\t  The bits for all the variables are as follows:\n      \tBit 1 (1)\t:   No Scout            \n        Bit 2 (2)\t:\tNo Sniper        \n        Bit 3 (4)\t:\tNo Soldier       \n        Bit 4 (8)\t:\tNo Demolitions Man\n        Bit 5 (16)\t:\tNo Combat Medic\n        Bit 6 (32)\t:\tNo Heavy Weapons Guy\n        Bit 7 (64)\t:\tNo Pyro\n        Bit 8 (128)\t:\tNo Random PlayerClass\n\t\tBit 9 (256) :\tNo Spy\n\t\tBit 10(512) :\tNo Engineer\n\t  E.g. If the \"maxammo_nails\" variable is set to 3, then players \n\t       in Team 2 will be unable to play Scouts or Snipers.\n\n\t  Finally, if you want the Team to be a special team for a fancy\n\t  map, such as the President in President-Quake, then you can \n\t  restrict the team to only play Civilian Class. Do this by setting\n\t  the team's variable to \"-1\".\n\nNotes:\n\tWhen using the \"message\" variable to set do localcmd's, you\n\tcan issue more than one command by seperating them with \\n\n\tMake sure you end it with a \\n too.\n\tE.g. The following changes the gravity and the friction.\n\t\t\"message\" \"sv_gravity 200\\nsv_friction .5\\n\"\n*/\n\n\nvoid() info_tfdetect =\n{\n\ttfdetect = self;\n\n\t//broadcast contains the version of tf that it was designed for\n\tif (self.broadcast != \"\")\n\t{\n\t\tdprint(\"map was designed for \");\n\t\tdprint(self.broadcast);\n\t\tdprint(\"\\n\");\n\t}\n\n\t//message states a message to localcmd\n\t//gravity, sv_friction, etc.\n\tlocalcmd(self.message);\n\tlocalcmd(\"\\n\");\n\n\t//ammo_shells\n\t//ammo_nails\n\t//ammo_rockets\n\t//ammo_cells\n\t//these fields specify the number of lives the players of any team have.\n\n\t//maxammo_shells\n\t//maxammo_nails\n\t//maxammo_rockets\n\t//maxammo_cells\n\t//these fields specify which teams are allowed which classes\n\n\n\t//team_broadcast - text for choose-team menu\n\t//non_team_broadcast - text for map help\n\n\t//noise1\n\t//noise2\n\t//noise3\n\t//noise4\n\t//these fields are per-team messages for choosing class.\n};\n\nstring nextmap;\nvoid() execute_changelevel;\n\n.float goal_no;\n.float group_no;\n.float goal_results;\n.float goal_activation;\n.float owned_by;\n\n.string team_broadcast;\n.string owner_team_broadcast;\n.string non_team_broadcast;\n\nvoid(float t, float o, entity ignore, string toteam, string toowners, string toenemies) teambroadcast =\n{\n\tlocal entity p;\n\twhile(1)\n\t{\n\t\tp = find(p, classname, \"player\");\n\t\tif (!p)\n\t\t\treturn;\n\n\t\tif (p != ignore)\n\t\t{\n\t\t\tif (p.team_no == t)\n\t\t\t\tsprint(p, PRINT_HIGH, toteam);\n\t\t\telse if (p.team_no == o)\n\t\t\t\tsprint(p, PRINT_HIGH, toowners);\n\t\t\telse\n\t\t\t\tsprint(p, PRINT_HIGH, toenemies);\n\t\t}\n\t}\n};\n\n\nentity(float num) FindGoal =\n{\n\tlocal entity e;\n\tdo\n\t{\n\t\te = find(e, classname, \"info_tfgoal\");\n\t\tif (e.goal_no == num)\n\t\t\treturn e;\n\t} while(e);\n\n\treturn world;\n};\nentity(float num) FindItem =\n{\n\tlocal entity e;\n\tdo\n\t{\n\t\te = find(e, classname, \"item_tfgoal\");\n\t\tif (e.goal_no == num)\n\t\t\treturn e;\n\t} while(e);\n\n\treturn world;\n};\n\nvoid(entity goal) goal_remove =\n{\n\tgoal.state = -1;\n};\n\nvoid(entity goal) goal_restore =\n{\n\tgoal.state = 0;\n};\n\nvoid(entity goal, entity ap) goal_inactivate =\n{\n\tsetmodel(goal, goal.mdl);\n\tsetorigin(goal, goal.oldorigin);\n\tself.solid = SOLID_TRIGGER;\n\tgoal.movetype = MOVETYPE_TOSS;\n//dprint(\"inactivated\\n\");\n\tgoal.owner = world;\n\tgoal.state = 0;\n\tgoal.nextthink = 0;\n}\n\nvoid() goal_timeout =\n{\n//dprint(\"goal_timeout\\n\");\n\tgoal_inactivate(self, world);\n};\n\nvoid(entity player, entity goal) TakeGoalFromPlayer;\nvoid(entity goal, entity ap) goal_activate;\nvoid() goaldropped =\n{\n//dprint(\"goaldropped\\n\");\n\tTakeGoalFromPlayer(self, self.owner);\n};\n\nvoid() GoalTrackCarrier =\n{\n\tif (self.owner.health <= 0)\n\t{\n\t\tgoal_inactivate(self, self.owner);\n\t\tself.think = goaldropped;\n\t\tself.nextthink = time + 40;\n\t}\n\telse\n\t\tself.nextthink = time + 0.1;\n};\n\nvoid(entity goal, entity player) TakeGoalFromPlayer =\n{\n//dprint(\"TakeGoalFromPlayer\\n\");\n\tif (goal.owner != player)\n\t\treturn;\n\n\tgoal_inactivate(goal, goal.owner);\n};\nvoid(entity goal, entity player) GiveGoalToPlayer =\n{\n\tif (goal.owner != world)\n\t{\n\t\tif (goal.owner == player)\n\t\t\treturn;\n\t\tTakeGoalFromPlayer(goal.owner, goal);\n\t}\n\n\tgoal.owner = player;\n\tsetmodel(goal, \"\");\n\tgoal.movetype = 12;//MOVETYPE_FOLLOW;\n//dprint(\"given \");\n//dprint(goal.netname);\n//dprint(\" to \");\n//dprint(player.classname);\n//dprint(\"\\n\");\n\n\tgoal_activate(goal, player);\n\n\tgoal.think = GoalTrackCarrier;\n\tgoal.nextthink = time+0.1;\n};\n\nvoid(entity goal, entity ap) goal_activate =\n{\n\n\tif (goal.state != 0)\n\t\treturn;\t//active already or removed.\n\n\n\tsprint(ap, PRINT_HIGH, goal.message);\n\tteambroadcast(goal.team_no, goal.owned_by, ap, goal.team_broadcast, goal.owner_team_broadcast, goal.non_team_broadcast);\n\tsound (other, CHAN_VOICE, self.noise, 1, ATTN_NORM);\n\n\tgoal.state = 1;\n\n\tif (goal.goal_results & 4)\n\t{\n\t\tnextmap = \"$host_mapname\";\n\t\texecute_changelevel();\n\t}\n\n//activated goals shouldn't affect players if (!(goal_result&2))\n//activated goals don't trigger others if goal_result&8\n\n\tif (goal.classname == \"item_tfgoal\")\n\t\tGiveGoalToPlayer(goal, ap);\n\telse if (ap != world)\n\t{\n//\t\tif (goal.items)\n//\t\t\tGiveGoalToPlayer(FindItem(goal.items), ap);\n\t\tif (goal.axhitme)\n\t\t\tTakeGoalFromPlayer(FindItem(goal.axhitme), ap);\n\t}\n/*\n\tif (goal.activate_goal_no)\n\t\tgoal_activate(FindGoal(goal.activate_goal_no));\n\tif (goal.inactivate_goal_no)\n\t\tgoal_inactivate(FindGoal(goal.inactivate_goal_no));\n\tif (goal.remove_goal_no)\n\t\tgoal_remove(FindGoal(goal.remove_goal_no));\n\tif (goal.restore_goal_no)\n\t\tgoal_restore(FindGoal(goal.restore_goal_no));\n*/\n//goal_results & 16 takes away stealth status.\n\n\tap.frags = ap.frags + goal.frags;\n\tap.health = ap.health + goal.health;\n\n\tif (goal.wait == -1)\n\t\tgoal.nextthink = 0;\t//permanent\n\telse if (goal.wait > 0)\n\t{\n\t\tgoal.nextthink = time + goal.wait;\n\t\tsetmodel(goal, \"\");\n\t}\n\telse\t//instant.\n\t{\n\t\tgoal.nextthink = 0;\n\t\tgoal_timeout();\n\t\treturn;\n\t}\n\tgoal.think = goal_timeout;\n\n\tif (goal.goal_results & 1)\n\t\tgoal_remove(goal);\n};\n\nvoid() goal_touch =\n{\n\tlocal float act;\n\tif (self.owner != world)\n\t\treturn;\n\tif (self.state != 0)\n\t\treturn;\n\tif (!(other.flags & FL_CLIENT))\n\t\treturn;\n\n\tif (!(self.goal_activation & 1))\t//you're only allowed to touch if 1\n\t\treturn;\n\n//\tdprint(self.netname);\n//\tdprint(\":\\n\");\n\n\tact = triggercantouch(self, other);\n\tif (self.classname == \"item_tfgoal\")\n\t{\n\t\tif ((self.goal_activation & 64))//64 inverts weather to act\n\t\t\tact = 1 - act;\n\t}\n\telse\n\t{\n\t\tif ((self.goal_activation & 4))\t//4 inverts weather to act\n\t\t\tact = 1 - act;\n\t}\n\n\tif (act) goal_activate(self, other);\n};\n\nvoid() StartItem;\nvoid() info_tfgoal =\n{\n\tself.oldorigin = self.origin;\n\tif (self.mdl != \"\")\n\t{\n\t\tprecache_model(self.mdl);\n\t\tsetmodel(self, self.mdl);\n\t\tsetsize(self, '-16 -16 -24', '16 16 32');\n\t}\n\tif (self.noise != \"\")\n\t\tprecache_sound(self.noise);\n\n\tself.touch = goal_touch;\n\n\tStartItem();\n};\nvoid() item_tfgoal =\n{\n\tself.wait = -1;\n\tinfo_tfgoal();\n};\n\nvoid() info_tfgoal_timer =\n{\n};\n\nvoid() i_t_g = \n{\n\tself.classname = \"info_tfgoal\";\n\tinfo_tfgoal();\n};\nvoid() i_t_t = \n{\n\tself.classname = \"info_tfgoal_timer\";\n\tinfo_tfgoal_timer();\n};\n\n\n\n\n\n\nvoid() info_player_teamspawn =\n{\n\tif (self.team_no == 1)\n\t\tself.classname = \"spawn1\";\n\telse if (self.team_no == 2)\n\t\tself.classname = \"spawn2\";\n\telse if (self.team_no == 3)\n\t\tself.classname = \"spawn3\";\n\telse if (self.team_no == 4)\n\t\tself.classname = \"spawn4\";\n\telse\n\t{\n\t\tdprint(\"info_player_teamspawn with team_no \");\n\t\tdprint(ftos(self.team_no));\n\t\tdprint(\"\\n\");\n\t\tself.classname = \"info_player_deathmatch\";\n\t}\n};\n\n\n\n\n\n\n/*\n\t- Since quake has a limit on the size of the entity data in a map,\n\t  abbreviations for some of the common entity fields were created.\n\t  The Abbreviations are as follows:\n*/\n\nvoid() i_p_t = \n{\n\tself.classname = \"info_player_teamspawn\";\n\tinfo_player_teamspawn();\n};\n\n\n/*\nvar .float g_a = goal_activation;\nvar .float g_e = goal_effects;\n\nvar .float h_i_g = has_item_from_group\"\nvar .float r_i_g = remove_item_group\"\n\nvar .float a_s = ammo_shells\"\nvar .float a_n = ammo_nails\"\nvar .float a_r = ammo_rockets\"\nvar .float a_c = ammo_cells\"\n\nvar .float rv_s_h = remove_spawngroup\"\nvar .float rs_s_h = restore_spawngroup\"\nvar .float rv_gr = remove_group_no\"\nvar .float rs_gr = restore_group_no\"\nvar .float rv_g = remove_goal_no\"\nvar .float rs_g = restore_goal_no\"\n\n\nvar .string t_s_h = team_str_home;\nvar .string t_s_m = team_str_moved;\nvar .string t_s_c = team_str_carried;\nvar .string n_s_h = non_team_str_home;\nvar .string n_s_m = non_team_str_moved;\nvar .string n_s_c = non_team_str_carried;\n\nvar .string b_b = broadcast;\nvar .string b_t = team_broadcast;\nvar .string b_n = non_team_broadcast;\nvar .string b_o = owners_team_broadcast;\nvar .string n_b = netname_broadcast;\nvar .string n_t = netname_team_broadcast;\nvar .string n_n = netname_non_team_broadcast;\nvar .string n_o = netname_owners_team_broadcast;\n\nvar .string d_t = team_drop;\nvar .string d_n = non_team_drop;\nvar .string d_n_t = netname_team_drop;\nvar .string d_n_n = netname_non_team_drop;\n*/\n\n//pointless entity\n.string map_name;\n.string alias;\n.string realteam;\n.float impulse_value;\nvoid() map_candidate =\n{\n\tremove(self);\n};\n\n\n\n\n"
  },
  {
    "path": "quakec/fallout2/triggers.qc",
    "content": "\n//entity s;\n\n\nvoid() trigger_reactivate =\n{\n\tself.solid = SOLID_TRIGGER;\n};\n\n//=============================================================================\n\nfloat\tSPAWNFLAG_NOMESSAGE = 1;\nfloat\tSPAWNFLAG_NOTOUCH = 1;\n\n// the wait time has passed, so set back up for another activation\nvoid() multi_wait =\n{\n\tif (self.max_health)\n\t{\n\t\tself.health = self.max_health;\n\t\tself.takedamage = DAMAGE_YES;\n\t\tself.solid = SOLID_BBOX;\n\t}\n};\n\n\n// the trigger was just touched/killed/used\n// self.enemy should be set to the activator so it can be held through a delay\n// so wait for the delay time before firing\nvoid() multi_trigger =\n{\n\tif (self.nextthink > time)\n\t{\n\t\treturn;\t\t// allready been triggered\n\t}\n\n\tif (self.classname == \"trigger_secret\")\n\t{\n\t\tif (self.enemy.classname != \"player\")\n\t\t\treturn;\n\t\tfound_secrets = found_secrets + 1;\n\t\tWriteByte (MSG_ALL, SVC_FOUNDSECRET);\n\t}\n\n\tif (self.noise)\n\t\tsound (self, CHAN_VOICE, self.noise, 1, ATTN_NORM);\n\n// don't trigger again until reset\n\tself.takedamage = DAMAGE_NO;\n\n\tactivator = self.enemy;\n\t\n\tSUB_UseTargets();\n\n\tif (self.wait > 0)\t\n\t{\n\t\tself.think = multi_wait;\n\t\tself.nextthink = time + self.wait;\n\t}\n\telse\n\t{\t// we can't just remove (self) here, because this is a touch function\n\t\t// called wheil C code is looping through area links...\n\t\tself.touch = SUB_Null;\n\t\tself.nextthink = time + 0.1;\n\t\tself.think = SUB_Remove;\n\t}\n};\n\nvoid() multi_killed =\n{\n\tif (!triggercantouch(self, other))\n\t\treturn;\n\n\tself.enemy = damage_attacker;\n\tmulti_trigger();\n};\n\nvoid() multi_use =\n{\n\tself.enemy = activator;\n\tmulti_trigger();\n};\n\nvoid() multi_touch =\n{\n\tif (!triggercantouch(self, other))\n\t\treturn;\n\n\tif (other.classname != \"player\")\n\t\treturn;\n\t\n// if the trigger has an angles field, check player's facing direction\n\tif (self.movedir != '0 0 0')\n\t{\n\t\tmakevectors (other.angles);\n\t\tif (v_forward * self.movedir < 0)\n\t\t\treturn;\t\t// not facing the right way\n\t}\n\t\n\tself.enemy = other;\n\tmulti_trigger ();\n};\n\n/*QUAKED trigger_multiple (.5 .5 .5) ? notouch\nVariable sized repeatable trigger.  Must be targeted at one or more entities.  If \"health\" is set, the trigger must be killed to activate each time.\nIf \"delay\" is set, the trigger waits some time after activating before firing.\n\"wait\" : Seconds between triggerings. (.2 default)\nIf notouch is set, the trigger is only fired by other entities, not by touching.\nNOTOUCH has been obsoleted by trigger_relay!\nsounds\n1)\tsecret\n2)\tbeep beep\n3)\tlarge switch\n4)\nset \"message\" to text string\n*/\nvoid() trigger_multiple =\n{\n\tif (self.sounds == 1)\n\t{\n\t\tprecache_sound (\"misc/secret.wav\");\n\t\tself.noise = \"misc/secret.wav\";\n\t}\n\telse if (self.sounds == 2)\n\t{\n\t\tprecache_sound (\"misc/talk.wav\");\n\t\tself.noise = \"misc/talk.wav\";\n\t}\n\telse if (self.sounds == 3)\n\t{\n\t\tprecache_sound (\"misc/trigger1.wav\");\n\t\tself.noise = \"misc/trigger1.wav\";\n\t}\n\t\n\tif (!self.wait)\n\t\tself.wait = 0.2;\n\tself.use = multi_use;\n\n\tInitTrigger ();\n\n\tif (self.health)\n\t{\n\t\tif (self.spawnflags & SPAWNFLAG_NOTOUCH)\n\t\t\tobjerror (\"health and notouch don't make sense\\n\");\n\t\tself.max_health = self.health;\n\t\tself.th_die = multi_killed;\n\t\tself.takedamage = DAMAGE_YES;\n\t\tself.solid = SOLID_BBOX;\n\t\tsetorigin (self, self.origin);\t// make sure it links into the world\n\t}\n\telse\n\t{\n\t\tif ( !(self.spawnflags & SPAWNFLAG_NOTOUCH) )\n\t\t{\n\t\t\tself.touch = multi_touch;\n\t\t}\n\t}\n};\n\n\n/*QUAKED trigger_once (.5 .5 .5) ? notouch\nVariable sized trigger. Triggers once, then removes itself.  You must set the key \"target\" to the name of another object in the level that has a matching\n\"targetname\".  If \"health\" is set, the trigger must be killed to activate.\nIf notouch is set, the trigger is only fired by other entities, not by touching.\nif \"killtarget\" is set, any objects that have a matching \"target\" will be removed when the trigger is fired.\nif \"angle\" is set, the trigger will only fire when someone is facing the direction of the angle.  Use \"360\" for an angle of 0.\nsounds\n1)\tsecret\n2)\tbeep beep\n3)\tlarge switch\n4)\nset \"message\" to text string\n*/\nvoid() trigger_once =\n{\n\tself.wait = -1;\n\ttrigger_multiple();\n};\n\n//=============================================================================\n\n/*QUAKED trigger_relay (.5 .5 .5) (-8 -8 -8) (8 8 8)\nThis fixed size trigger cannot be touched, it can only be fired by other events.  It can contain killtargets, targets, delays, and messages.\n*/\nvoid() trigger_relay =\n{\n\tself.use = SUB_UseTargets;\n};\n\n\n//=============================================================================\n\n/*QUAKED trigger_secret (.5 .5 .5) ?\nsecret counter trigger\nsounds\n1)\tsecret\n2)\tbeep beep\n3)\n4)\nset \"message\" to text string\n*/\nvoid() trigger_secret =\n{\n\ttotal_secrets = total_secrets + 1;\n\tself.wait = -1;\n\tif (!self.message)\n\t\tself.message = \"You found a secret area!\";\n\tif (!self.sounds)\n\t\tself.sounds = 1;\n\t\n\tif (self.sounds == 1)\n\t{\n\t\tprecache_sound (\"misc/secret.wav\");\n\t\tself.noise = \"misc/secret.wav\";\n\t}\n\telse if (self.sounds == 2)\n\t{\n\t\tprecache_sound (\"misc/talk.wav\");\n\t\tself.noise = \"misc/talk.wav\";\n\t}\n\n\ttrigger_multiple ();\n};\n\n//=============================================================================\n\n\nvoid() counter_use =\n{\n\tself.count = self.count - 1;\n\tif (self.count < 0)\n\t\treturn;\n\t\n\tif (self.count != 0)\n\t{\n\t\tif (activator.classname == \"player\"\n\t\t&& (self.spawnflags & SPAWNFLAG_NOMESSAGE) == 0)\n\t\t{\n\t\t\tif (self.count >= 4)\n\t\t\t\tcenterprint (activator, \"There are more to go...\");\n\t\t\telse if (self.count == 3)\n\t\t\t\tcenterprint (activator, \"Only 3 more to go...\");\n\t\t\telse if (self.count == 2)\n\t\t\t\tcenterprint (activator, \"Only 2 more to go...\");\n\t\t\telse\n\t\t\t\tcenterprint (activator, \"Only 1 more to go...\");\n\t\t}\n\t\treturn;\n\t}\n\t\n\tif (activator.classname == \"player\"\n\t&& (self.spawnflags & SPAWNFLAG_NOMESSAGE) == 0)\n\t\tcenterprint(activator, \"Sequence completed!\");\n\tself.enemy = activator;\n\tmulti_trigger ();\n};\n\n/*QUAKED trigger_counter (.5 .5 .5) ? nomessage\nActs as an intermediary for an action that takes multiple inputs.\n\nIf nomessage is not set, t will print \"1 more.. \" etc when triggered and \"sequence complete\" when finished.\n\nAfter the counter has been triggered \"count\" times (default 2), it will fire all of it's targets and remove itself.\n*/\nvoid() trigger_counter =\n{\n\tself.wait = -1;\n\tif (!self.count)\n\t\tself.count = 2;\n\n\tself.use = counter_use;\n};\n\n\n/*\n==============================================================================\n\nTELEPORT TRIGGERS\n\n==============================================================================\n*/\n\nfloat\tPLAYER_ONLY\t= 1;\nfloat\tSILENT = 2;\n\nvoid() play_teleport =\n{\n\tlocal\tfloat v;\n\tlocal\tstring tmpstr;\n\n\tv = random() * 5;\n\tif (v < 1)\n\t\ttmpstr = \"misc/r_tele1.wav\";\n\telse if (v < 2)\n\t\ttmpstr = \"misc/r_tele2.wav\";\n\telse if (v < 3)\n\t\ttmpstr = \"misc/r_tele3.wav\";\n\telse if (v < 4)\n\t\ttmpstr = \"misc/r_tele4.wav\";\n\telse\n\t\ttmpstr = \"misc/r_tele5.wav\";\n\n\tsound (self, CHAN_VOICE, tmpstr, 1, ATTN_NORM);\n\tremove (self);\n};\n\nvoid(vector org) spawn_tfog =\n{\n\tlocal entity s;\n\ts = spawn ();\n\ts.origin = org;\n\ts.nextthink = time + 0.2;\n\ts.think = play_teleport;\n\n\tWriteByte (MSG_MULTICAST, SVC_TEMPENTITY);\n\tWriteByte (MSG_MULTICAST, TE_TELEPORT);\n\tWriteCoord (MSG_MULTICAST, org_x);\n\tWriteCoord (MSG_MULTICAST, org_y);\n\tWriteCoord (MSG_MULTICAST, org_z);\n\tmulticast (org, MULTICAST_PHS);\n};\n\n\nvoid() tdeath_touch =\n{\n\tlocal entity other2;\n\n\tif (other == self.owner)\n\t\treturn;\n\n// frag anyone who teleports in on top of an invincible player\n\tif (other.classname == \"player\")\n\t{\n\t\tif (other.invincible_finished > time &&\n\t\t\tself.owner.invincible_finished > time) {\n\t\t\tself.classname = \"teledeath3\";\n\t\t\tother.invincible_finished = 0;\n\t\t\tself.owner.invincible_finished = 0;\n\t\t\tT_Damage (other, self, self, 50000);\n\t\t\tother2 = self.owner;\n\t\t\tself.owner = other;\n\t\t\tT_Damage (other2, self, self, 50000);\n\t\t}\n\t\t\t\n\t\tif (other.invincible_finished > time)\n\t\t{\n\t\t\tself.classname = \"teledeath2\";\n\t\t\tT_Damage (self.owner, self, self, 50000);\n\t\t\treturn;\n\t\t}\n\t\t\n\t}\n\n\tif (other.health)\n\t{\n\t\tT_Damage (other, self, self, 50000);\n\t}\n};\n\n\nvoid(vector org, entity death_owner) spawn_tdeath =\n{\nlocal entity\tdeath;\n\n\tdeath = spawn();\n\tdeath.classname = \"teledeath\";\n\tdeath.movetype = MOVETYPE_NONE;\n\tdeath.solid = SOLID_TRIGGER;\n\tdeath.angles = '0 0 0';\n\tsetsize (death, death_owner.mins - '1 1 1', death_owner.maxs + '1 1 1');\n\tsetorigin (death, org);\n\tdeath.touch = tdeath_touch;\n\tdeath.nextthink = time + 0.2;\n\tdeath.think = SUB_Remove;\n\tdeath.owner = death_owner;\n\t\n\tforce_retouch = 2;\t\t// make sure even still objects get hit\n};\n\nvoid() teleport_touch =\n{\nlocal entity\tt;\nlocal vector\torg;\n\n\tif (!triggercantouch(self, other))\n\t\treturn;\n\n\tif (self.targetname)\n\t{\n\t\tif (self.nextthink < time)\n\t\t{\n\t\t\treturn;\t\t// not fired yet\n\t\t}\n\t}\n\n\tif (self.spawnflags & PLAYER_ONLY)\n\t{\n\t\tif (other.classname != \"player\")\n\t\t\treturn;\n\t}\n\n// only teleport living creatures\n\tif (other.health <= 0 || other.solid != SOLID_SLIDEBOX)\n\t\treturn;\n\n\tSUB_UseTargets ();\n\n// put a tfog where the player was\n\tspawn_tfog (other.origin);\n\n\tt = find (world, targetname, self.target);\n\tif (!t)\n\t\tobjerror (\"couldn't find target\");\n\t\t\n// spawn a tfog flash in front of the destination\n\tmakevectors (t.mangle);\n\torg = t.origin + 32 * v_forward;\n\n\tspawn_tfog (org);\n\tspawn_tdeath(t.origin, other);\n\tif (self.classname == \"player\")\n\t{\n\t\tself.solid = SOLID_NOT;\n\t\tself.materialize = 200;\n\t}\n// move the player and lock him down for a little while\n\tif (!other.health)\n\t{\n\t\tother.origin = t.origin;\n\t\tother.velocity = (v_forward * other.velocity_x) + (v_forward * other.velocity_y);\n\t\treturn;\n\t}\n\n\tsetorigin (other, t.origin);\n\tother.angles = t.mangle;\n\tif (other.classname == \"player\")\n\t{\n\t\tother.fixangle = 1;\t\t// turn this way immediately\n\t\tother.teleport_time = time + 0.7;\n\t\tif (other.flags & FL_ONGROUND)\n\t\t\tother.flags = other.flags - FL_ONGROUND;\n\t\tother.velocity = v_forward * 300;\n\t}\n\tother.flags = other.flags - other.flags & FL_ONGROUND;\n};\n\n/*QUAKED info_teleport_destination (.5 .5 .5) (-8 -8 -8) (8 8 32)\nThis is the destination marker for a teleporter.  It should have a \"targetname\" field with the same value as a teleporter's \"target\" field.\n*/\nvoid() info_teleport_destination =\n{\n// this does nothing, just serves as a target spot\n\tself.mangle = self.angles;\n\tself.angles = '0 0 0';\n\tself.model = \"\";\n\tself.origin = self.origin + '0 0 27';\n\tif (!self.targetname)\n\t\tobjerror (\"no targetname\");\n};\n\nvoid() teleport_use =\n{\n\tself.nextthink = time + 0.2;\n\tforce_retouch = 2;\t\t// make sure even still objects get hit\n\tself.think = SUB_Null;\n};\n\n/*QUAKED trigger_teleport (.5 .5 .5) ? PLAYER_ONLY SILENT\nAny object touching this will be transported to the corresponding info_teleport_destination entity. You must set the \"target\" field, and create an object with a \"targetname\" field that matches.\n\nIf the trigger_teleport has a targetname, it will only teleport entities when it has been fired.\n*/\nvoid() trigger_teleport =\n{\n\tlocal vector o;\n\n\tInitTrigger ();\n\tself.touch = teleport_touch;\n\t// find the destination \n\tif (!self.target)\n\t\tobjerror (\"no target\");\n\tself.use = teleport_use;\n\n\tif (!(self.spawnflags & SILENT))\n\t{\n\t\tprecache_sound (\"ambience/hum1.wav\");\n\t\to = (self.mins + self.maxs)*0.5;\n\t\tambientsound (o, \"ambience/hum1.wav\",0.5 , ATTN_STATIC);\n\t}\n};\n\n/*\n==============================================================================\n\ntrigger_setskill\n\n==============================================================================\n*/\n\nvoid() trigger_skill_touch =\n{\n\tif (!triggercantouch(self, other))\n\t\treturn;\n\n\tif (other.classname != \"player\")\n\t\treturn;\n\t\t\n\tcvar_set (\"skill\", self.message);\n};\n\n/*QUAKED trigger_setskill (.5 .5 .5) ?\nsets skill level to the value of \"message\".\nOnly used on start map.\n*/\nvoid() trigger_setskill =\n{\n\tInitTrigger ();\n\tself.touch = trigger_skill_touch;\n};\n\n\n/*\n==============================================================================\n\nONLY REGISTERED TRIGGERS\n\n==============================================================================\n*/\n\nvoid() trigger_onlyregistered_touch =\n{\n\tif (!triggercantouch(self, other))\n\t\treturn;\n\tif (other.classname != \"player\")\n\t\treturn;\n\tif (self.attack_finished > time)\n\t\treturn;\n\n\tself.attack_finished = time + 2;\n\tif (cvar(\"registered\"))\n\t{\n\t\tself.message = \"\";\n\t\tSUB_UseTargets ();\n\t\tremove (self);\n\t}\n\telse\n\t{\n\t\tif (self.message != \"\")\n\t\t{\n\t\t\tcenterprint (other, self.message);\n\t\t\tsound (other, CHAN_BODY, \"misc/talk.wav\", 1, ATTN_NORM);\n\t\t}\n\t}\n};\n\n/*QUAKED trigger_onlyregistered (.5 .5 .5) ?\nOnly fires if playing the registered version, otherwise prints the message\n*/\nvoid() trigger_onlyregistered =\n{\n\tprecache_sound (\"misc/talk.wav\");\n\tInitTrigger ();\n\tself.touch = trigger_onlyregistered_touch;\n};\n\n//============================================================================\n\nvoid() hurt_on =\n{\n\tself.solid = SOLID_TRIGGER;\n\tself.nextthink = -1;\n};\n\nvoid() hurt_touch =\n{\n\tif (self.team && self.team != other.team)\n\t\treturn;\n\n\tif (other.takedamage)\n\t{\n\t\tself.solid = SOLID_NOT;\n\t\tT_Damage (other, self, self, self.dmg);\n\t\tself.think = hurt_on;\n\t\tself.nextthink = time + 1;\n\t}\n\n\treturn;\n};\n\n/*QUAKED trigger_hurt (.5 .5 .5) ?\nAny object touching this will be hurt\nset dmg to damage amount\ndefalt dmg = 5\n*/\nvoid() trigger_hurt =\n{\n\tInitTrigger ();\n\tself.touch = hurt_touch;\n\tif (!self.dmg)\n\t\tself.dmg = 5;\n};\n\n//============================================================================\n\nfloat PUSH_ONCE = 1;\n\nvoid() trigger_push_touch =\n{\n\tif (other.classname == \"grenade\")\n\t\tother.velocity = self.speed * self.movedir * 10;\n\telse if (other.health > 0)\n\t{\n\t\tif (other.classname == \"player\")\n\t\t{\n\t\t\tif (!triggercantouch(self, other))\n\t\t\t\treturn;\n\t\t\tif (other.fly_sound < time)\n\t\t\t{\n\t\t\t\tother.fly_sound = time + 1.5;\n\t\t\t\tsound (other, CHAN_AUTO, \"ambience/windfly.wav\", 1, ATTN_NORM);\n\t\t\t}\n\t\t}\n\t\tother.velocity = self.speed * self.movedir * 10;\n\t}\n\tif (self.spawnflags & PUSH_ONCE)\n\t\tremove(self);\n};\n\n\n/*QUAKED trigger_push (.5 .5 .5) ? PUSH_ONCE\nPushes the player\n*/\nvoid() trigger_push =\n{\n\tInitTrigger ();\n\tprecache_sound (\"ambience/windfly.wav\");\n\tself.touch = trigger_push_touch;\n\tif (!self.speed)\n\t\tself.speed = 1000;\n};\n\n//============================================================================\n\nvoid() trigger_monsterjump_touch =\n{\n\tif ( other.flags & (FL_MONSTER | FL_FLY | FL_SWIM) != FL_MONSTER )\n\t\treturn;\n\n// set XY even if not on ground, so the jump will clear lips\n\tother.velocity_x = self.movedir_x * self.speed;\n\tother.velocity_y = self.movedir_y * self.speed;\n\t\n\tif ( !(other.flags & FL_ONGROUND) )\n\t\treturn;\n\t\n\tother.flags = other.flags - FL_ONGROUND;\n\n\tother.velocity_z = self.height;\n};\n\n/*QUAKED trigger_monsterjump (.5 .5 .5) ?\nWalking monsters that touch this will jump in the direction of the trigger's angle\n\"speed\" default to 200, the speed thrown forward\n\"height\" default to 200, the speed thrown upwards\n*/\nvoid() trigger_monsterjump =\n{\n\tif (!self.speed)\n\t\tself.speed = 200;\n\tif (!self.height)\n\t\tself.height = 200;\n\tif (self.angles == '0 0 0')\n\t\tself.angles = '0 360 0';\n\tInitTrigger ();\n\tself.touch = trigger_monsterjump_touch;\n};\n\n"
  },
  {
    "path": "quakec/fallout2/turrets.qc",
    "content": "void() tesla_think =\n{\n\tlocal entity e;\n\tlocal vector org;\n\n\tself.nextthink = time + 0.1;\n\tself.frame = self.frame + 1;\n\n\tif (!self.aflag)\n\t{\n\t\tif (!self.enemy || self.enemy.health <= 0)\n\t\t{\n\t\t\te = findradius(self.origin, 800);\n\t\t\twhile (e != world)\n\t\t\t{\n\t\t\t\tif (e.health > 0)\n\t\t\t\t{\n\t\t\t\t\tif (e.team != self.team)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (e.solid)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (e.takedamage)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\ttraceline (self.origin, e.origin, FALSE, self);\n\t\t\t\t\t\t\t\tif (trace_ent == e)\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\te = e.chain;\n\t\t\t}\n\t\t\tself.enemy = e;\n\t\t\tif (e != world)\n\t\t\t\tsound (self, CHAN_AUTO, \"weapons/lstart.wav\", 1, ATTN_NORM);\n\t\t}\n\t\tif (self.enemy)\n\t\t{\n\t\t\tif (self.frame < 7)\n\t\t\t\tself.frame = 7;\n\n\t\t\torg = self.origin + '0 0 32';\n\n\t\t\ttraceline (org, self.enemy.origin, FALSE, self);\n\n\t\t\tif (self.t_width < time)\n\t\t\t{\n\t\t\t\tsound (self, CHAN_WEAPON, \"weapons/lhit.wav\", 1, ATTN_NORM);\n\t\t\t\tself.t_width = time + 0.6;\n\t\t\t}\n\n\t\t\t//mwa ha ha!!\n\t\t\tWriteByte (MSG_MULTICAST, SVC_TEMPENTITY);\n\t\t\tWriteByte (MSG_MULTICAST, TE_LIGHTNING2);\n\t\t\tWriteEntity (MSG_MULTICAST, self);\n\t\t\tWriteCoord (MSG_MULTICAST, org_x);\n\t\t\tWriteCoord (MSG_MULTICAST, org_y);\n\t\t\tWriteCoord (MSG_MULTICAST, org_z);\n\t\t\tWriteCoord (MSG_MULTICAST, trace_endpos_x);\n\t\t\tWriteCoord (MSG_MULTICAST, trace_endpos_y);\n\t\t\tWriteCoord (MSG_MULTICAST, trace_endpos_z);\n\t\t\tmulticast (org, MULTICAST_PHS);\n\n\t\t\tif (trace_ent != self.enemy)\n\t\t\t\tself.enemy = world;\n\t\t\ttrace_endpos = normalize(trace_endpos - org)*8 + trace_endpos;\n\t\t\tLightningDamage (self.origin, trace_endpos, self, 10);\n\n\t\t\tif (self.frame > 12)\n\t\t\t\tself.frame = 0;\n\t\t\treturn;\n\t\t}\n\t}\n\tif (self.frame >= 7)\n\t\tself.frame = 0;\n};\n\nvoid (float tmp, float dam) turret_mg_fire =\n{\n\tlocal vector src;\n\tlocal vector dir;\n\tlocal vector direction;\n\tlocal entity en;\n\tlocal vector org;\n\n\tmakevectors (self.angles);\n\n\tsound (self, CHAN_WEAPON, \"weapons/1911.wav\", PLAT_LOW_TRIGGER, ATTN_NORM);\n\n\tsrc = self.origin + v_forward*10;\n\tsrc_z = self.absmin_z + self.size_z * 0.7;\n\n\ten = self.enemy;\n\t\n\tdir = en.origin - en.velocity*0.2;\n\tdir = normalize (dir - self.origin);\n\n\tdirection = dir;\n\n\ttraceline (src, src + direction*2048 + v_right*crandom()*tmp + v_up*crandom()*tmp, FALSE, self);\n\n\tif (trace_fraction == PLAT_LOW_TRIGGER)\n\t\treturn;\n\n\tif (trace_ent.takedamage)\n\t{\n\t\tdam = 1 + random()*dam + random()*dam;\n\t\tdam = dam * (1 - (trace_fraction/2));\n\t\tSpawnBlood (org, PLAT_LOW_TRIGGER);\n\t\tT_Damage (trace_ent, self, self, dam);\n\t}\n\telse\n\t{\n\t\tWriteByte (MSG_MULTICAST, SVC_TEMPENTITY);\n\t\tWriteByte (MSG_MULTICAST, TE_SPIKE);\n\t\tWriteCoord (MSG_MULTICAST, trace_endpos_x);\n\t\tWriteCoord (MSG_MULTICAST, trace_endpos_y);\n\t\tWriteCoord (MSG_MULTICAST, trace_endpos_z);\n\t\tmulticast (trace_endpos, MULTICAST_PVS);\n\t}\n\n\tmuzzleflash();\n};\n\nvoid() GunTop_Think =\n{\n\tlocal entity e;\n\tlocal vector org;\n\tlocal float delta;\n\n\tself.nextthink = time + 0.1;\n\n\tif (self.owner.health < 1)\n\t{\n\t\tremove(self);\n\t\treturn;\n\t}\n\n\tsetorigin(self, self.owner.origin);\n\n\tif (!self.owner.aflag)\n\t{\n\t\tif (self.attack_finished < time)\n\t\tif (!self.enemy || self.enemy.health < 1)\n\t\t{\n\t\t\te = findradius(self.origin, 800);\n\t\t\twhile (e != world)\n\t\t\t{\n\t\t\t\tif (e.health > 0)\n\t\t\t\t{\n\t\t\t\t\tif (e.team != self.owner.team)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (e.solid)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (e.takedamage)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\ttraceline (self.origin+'0 0 16', e.origin, FALSE, self);\n\t\t\t\t\t\t\t\tif (trace_ent == e)\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\te = e.chain;\n\t\t\t}\n\t\t\tself.enemy = e;\n\t\t\tif (e == world)\n\t\t\t\tself.attack_finished = time + 0.3;\n\n\t\t}\n\t\tif (self.enemy)\n\t\t{\n\t\t\tself.ideal_yaw = vectoyaw(self.enemy.origin - self.origin);\n\t\t\tChangeYaw();\n\t\t\tif (self.attack_finished < time)\n\t\t\t{\n\t\t\t\tdelta = anglemod(self.angles_y - self.ideal_yaw);\n\t\t\t\tif (delta > 10 && delta < 350)\n\t\t\t\t\treturn;\n\n\t\t\t\ttraceline (self.owner.origin+'0 0 16', self.enemy.origin, FALSE, self.owner);\n\t\t\t\tif (trace_ent != self.enemy)\n\t\t\t\t{\n\t\t\t\t\tself.enemy = world;\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tif (self.owner.buildtype == IID_BUILD_RTURRET)\n\t\t\t\t{\n\t\t\t\t\tsound (self, CHAN_WEAPON, \"weapons/sgun1.wav\", 1, ATTN_NORM);\n\t\t\t\t\tself.attack_finished = time + 0.7;\n\t\t\t\t\tnewmis = spawn ();\n\t\t\t\t\tnewmis.owner = self.owner;\n\t\t\t\t\tnewmis.movetype = MOVETYPE_FLYMISSILE;\n\t\t\t\t\tnewmis.solid = SOLID_BBOX;\n\n// set newmis speed    \n\t\t\t\t\tnewmis.velocity = normalize(self.enemy.origin - self.origin)*1000;\n\t\t\t\t\tnewmis.angles = vectoangles(newmis.velocity);\n\n\t\t\t\t\tnewmis.touch = T_MissileTouch;\n\t\t\t\t\tnewmis.voided = 0;\n\n// set newmis duration\n\t\t\t\t\tnewmis.nextthink = time + 5;\n\t\t\t\t\tnewmis.think = SUB_Remove;\n\t\t\t\t\tnewmis.classname = \"rocket\";\n\n\t\t\t\t\tsetmodel (newmis, \"progs/missile.mdl\");\n\t\t\t\t\tsetsize (newmis, '0 0 0', '0 0 0');             \n\t\t\t\t\tsetorigin (newmis, self.origin + v_forward*8 + '0 0 16');\n\t\t\t\t}\n\t\t\t\tif (self.owner.buildtype == IID_BUILD_GTURRET)\n\t\t\t\t{\n\t\t\t\t\tturret_mg_fire(0.4, 3+random()*4);\n\t\t\t\t\tself.attack_finished = time + 0.05;\n\t\t\t\t\tself.nextthink = time + 0.05;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n};\n\nvoid() FinishTesla =\n{\n\tself.netname = \"tesla turret\";\n\tself.think = tesla_think;\n\tsetmodel (self, \"progs/coil.mdl\");\n\tsetsize (self, '-16 -16 -24', '16 16 32');\n\tself.nextthink = time + 0.1;\n};\nvoid() FinishGunPost =\n{\n\tlocal entity top;\n\n\ttop = spawn();\n\ttop.owner = self;\n\ttop.think = GunTop_Think;\n\ttop.nextthink = time + 0.1;\n\ttop.yaw_speed = 20;\n\tif (self.buildtype == IID_BUILD_RTURRET)\n\t{\n\t\tsetmodel(top, \"progs/turrgun.mdl\");\n\t\tsetmodel(self, \"progs/turrbase.mdl\");\n\t}\n\telse\n\t{\n\t\tsetmodel(top, \"progs/miniturr.mdl\");\n\t\tsetmodel(self, \"progs/minibase.mdl\");\n\t}\n\tsetorigin(top, self.origin);\n\ttop.angles = self.angles;\n};\nvoid() FinishTurret =\n{\n\tif (self.buildtype == IID_BUILD_TTURRET)\n\t\tFinishTesla();\n\telse\n\t\tFinishGunPost();\n};"
  },
  {
    "path": "quakec/fallout2/weapons.qc",
    "content": "/*\n*/\nvoid (entity targ, entity inflictor, entity attacker, float damage) T_Damage;\nvoid () player_run;\nvoid(entity bomb, entity attacker, float rad, entity ignore, string dtype) T_RadiusDamage;\nvoid(vector org, float damage) SpawnBlood;\nvoid() SuperDamageSound;\nvoid (float rec, float number, float dam, float var, float ran, float auto) W_FireShotgun;\nvoid (float dam, float rec, string snd, float rng, float rate) FireAssaultRifle;\nvoid (float dam, float rec, string snd, float rng, float rate) FirePistol;\nvoid (float dam, float rec, string snd, float rng, float rate) FireSMG;\nfloat() FireToolkit;\nvoid () FireAlienBlaster;\nvoid () W_PlayerMenu;\n//void () UseChem;\nvoid () Special;\nvoid () BuyMenu;\nvoid() Sneak;\n//void() Bandage;\nvoid() Shield;\nvoid () player_throw1;\nvoid() player_knife1;\nvoid() player_knifea;\nvoid() ExitScreen;\nvoid() CharacterSheet;\nvoid() UseEquipment;\nvoid (float slot, float snd, float drop) DropFromSlot;\nvoid() PositionControl;\nvoid() autofire;\nvoid() autofire_s;\nfloat(float iid) UseBoostingChem;\nfloat(float iid) UseHealingChem;\nfloat(float slotno) DecreaseDestroySlot;\nvoid () DropAmmo;\n\n#define weightx() (self.weight)\n\n\n// called by worldspawn\nvoid() W_Precache =\n{\n\tprecache_sound (\"weapons/r_exp3.wav\");  // new rocket explosion\n\tprecache_sound (\"weapons/rocket1i.wav\");        // spike gun\n\tprecache_sound (\"weapons/sgun1.wav\");\n\tprecache_sound (\"weapons/guncock.wav\"); // player shotgun\n\tprecache_sound (\"weapons/ric1.wav\");    // ricochet (used in c code)\n\tprecache_sound (\"weapons/ric2.wav\");    // ricochet (used in c code)\n\tprecache_sound (\"weapons/ric3.wav\");    // ricochet (used in c code)\n\tprecache_sound (\"weapons/ric4.wav\");    // ricochet (used in c code)\n\tprecache_sound (\"weapons/ric5.wav\");    // ricochet (used in c code)\n\tprecache_sound (\"weapons/spike2.wav\");  // super spikes\n\tprecache_sound (\"weapons/tink1.wav\");   // spikes tink (used in c code)\n\tprecache_sound (\"weapons/grenade.wav\"); // grenade launcher\n\tprecache_sound (\"weapons/bounce.wav\");          // grenade bounce\n\tprecache_sound (\"weapons/shotgn2.wav\"); // super shotgun\n};\n\nfloat() crandom =\n{\n\treturn 2*(random() - 0.5);\n};\n\n/*\n================\nW_FireMelee\n================\n*/\nvoid(float damage, float dist, float rate) FireMelee =\n{\n\tlocal   vector  source;\n\tlocal   vector  org;\n\n\tmakevectors (self.v_angle);\n\tsource = self.origin + '0 0 16';\n\ttraceline (source, source + v_forward*dist, FALSE, self);\n\tif (trace_fraction == 1.0)\n\t\treturn;\n\t\n\torg = trace_endpos - v_forward*4;\n\n\tif (trace_ent.takedamage)\n\t{\n\t\ttrace_ent.axhitme = 1;\n\t\tSpawnBlood (org, 1);\n\t\tT_Damage (trace_ent, self, self, damage+random()*damage);\n\t\tsound (self, CHAN_WEAPON, trace_ent.armornoise, 1, ATTN_NORM);\n\t}\n\telse\n\t{       // hit wall\n\t\tsound (self, CHAN_WEAPON, \"player/axhit2.wav\", 1, ATTN_NORM);\n\n\t\tWriteByte (MSG_MULTICAST, SVC_TEMPENTITY);\n\t\tWriteByte (MSG_MULTICAST, TE_SPIKE);\n\t\tWriteCoord (MSG_MULTICAST, org_x);\n\t\tWriteCoord (MSG_MULTICAST, org_y);\n\t\tWriteCoord (MSG_MULTICAST, org_z);\n\t\tmulticast (self.origin, MULTICAST_PHS);\n\t}\n};\n\nvoid() W_FireMelee =\n{\n\tlocal float iid;\n\tiid = ToIID(self.(SlotField(self.current_slot)));\n\tif (iid == IID_NONE)\n\t\tFireMelee(3, 32, 0.6);\n\telse if (iid == IID_WP_TOOLKIT)\n\t\tFireMelee(5, 32, 0.2);\n\telse if (iid == IID_WP_KNIFE)\n\t\tFireMelee(5, 32, 0.2);\n\telse if (iid == IID_WP_AXE)\n\t\tFireMelee(10, 64, 0.2);\n\telse if (iid == IID_WP_VIBROBLADE)\t\n\t\tFireMelee(25, 64, 0.2);\n\telse if (iid == IID_WP_POWERAXE)\n\t\tFireMelee(50, 96, 0.2);\n\n\telse if (iid == IID_CHEM_STIMPACK ||\n\t\t   iid == IID_CHEM_MEDICALBAG ||\n\t\t   iid == IID_CHEM_SUPERSTIM)\n\t{\n\t\tif (UseHealingChem(iid))\n\t\t\tDecreaseDestroySlot(self.current_slot);\n\t}\n\telse if (iid == IID_CHEM_ADRENALINE ||\n\t\t   iid == IID_CHEM_PSYCHO ||\n\t\t   iid == IID_CHEM_BESERK)\n\t{\n\t\tif (UseBoostingChem(iid))\n\t\t\tDecreaseDestroySlot(self.current_slot);\n\t}\n\telse\n\t\tdprint(strcat(\"W_FireMelee - \", ftos(iid), \"not implemented\\n\"));\n};\n\n\n//============================================================================\n\nvector() wall_velocity =\n{\n\tlocal vector    vel;\n\t\n\tvel = normalize (self.velocity);\n\tvel = normalize(vel + v_up*(random()- 0.5) + v_right*(random()- 0.5));\n\tvel = vel + 2*trace_plane_normal;\n\tvel = vel * 200;\n\t\n\treturn vel;\n};\n\n\n/*\n================\nSpawnMeatSpray\n================\n*/\nvoid(vector org, vector vel) SpawnMeatSpray =\n{\n\tlocal   entity missile;\n\n\tmissile = spawn ();\n\tmissile.owner = self;\n\tmissile.movetype = MOVETYPE_BOUNCE;\n\tmissile.solid = SOLID_NOT;\n\n\tmakevectors (self.angles);\n\n\tmissile.velocity = vel;\n\tmissile.velocity_z = missile.velocity_z + 250 + 50*random();\n\n\tmissile.avelocity = '3000 1000 2000';\n\t\n// set missile duration\n\tmissile.nextthink = time + 1;\n\tmissile.think = SUB_Remove;\n\n\tsetmodel (missile, \"progs/zom_gib.mdl\");\n\tsetsize (missile, '0 0 0', '0 0 0');            \n\tsetorigin (missile, org);\n};\n\n/*\n================\nSpawnBlood\n================\n*/\nvoid(vector org, float damage) SpawnBlood =\n{\n\tWriteByte (MSG_MULTICAST, SVC_TEMPENTITY);\n\tWriteByte (MSG_MULTICAST, TE_BLOOD);\n\tWriteByte (MSG_MULTICAST, 1);\n\tWriteCoord (MSG_MULTICAST, org_x);\n\tWriteCoord (MSG_MULTICAST, org_y);\n\tWriteCoord (MSG_MULTICAST, org_z);\n\tmulticast (org, MULTICAST_PVS);\n};\n\n/*\n================\nspawn_touchblood\n================\n*/\nvoid(float damage) spawn_touchblood =\n{\n\tlocal vector    vel;\n\n\tvel = wall_velocity () * 0.2;\n\tSpawnBlood (self.origin + vel*0.01, damage);\n};\n\n/*\n==============================================================================\n\nMULTI-DAMAGE\n\nCollects multiple small damages into a single damage\n\n==============================================================================\n*/\n\nentity  multi_ent;\nfloat   multi_damage;\n\nvector  blood_org;\nfloat   blood_count;\n\nvector  puff_org;\nfloat   puff_count;\n\nvoid() ClearMultiDamage =\n{\n\tmulti_ent = world;\n\tmulti_damage = 0;\n\tblood_count = 0;\n\tpuff_count = 0;\n};\n\nvoid() ApplyMultiDamage =\n{\n\tif (!multi_ent)\n\t\treturn;\n\tT_Damage (multi_ent, self, self, multi_damage);\n};\n\nvoid(entity hit, float damage) AddMultiDamage =\n{\n\tif (!hit)\n\t\treturn;\n\t\n\tif (hit != multi_ent)\n\t{\n\t\tApplyMultiDamage ();\n\t\tmulti_damage = damage;\n\t\tmulti_ent = hit;\n\t}\n\telse\n\t\tmulti_damage = multi_damage + damage;\n};\n\nvoid() Multi_Finish =\n{\n\tif (puff_count)\n\t{\n\t\tWriteByte (MSG_MULTICAST, SVC_TEMPENTITY);\n\t\tWriteByte (MSG_MULTICAST, TE_GUNSHOT);\n\t\tWriteByte (MSG_MULTICAST, puff_count);\n\t\tWriteCoord (MSG_MULTICAST, puff_org_x);\n\t\tWriteCoord (MSG_MULTICAST, puff_org_y);\n\t\tWriteCoord (MSG_MULTICAST, puff_org_z);\n\t\tmulticast (puff_org, MULTICAST_PVS);\n\t}\n\n\tif (blood_count)\n\t{\n\t\tWriteByte (MSG_MULTICAST, SVC_TEMPENTITY);\n\t\tWriteByte (MSG_MULTICAST, TE_BLOOD);\n\t\tWriteByte (MSG_MULTICAST, blood_count);\n\t\tWriteCoord (MSG_MULTICAST, blood_org_x);\n\t\tWriteCoord (MSG_MULTICAST, blood_org_y);\n\t\tWriteCoord (MSG_MULTICAST, blood_org_z);\n\t\tmulticast (puff_org, MULTICAST_PVS);\n\t}\n};\n\n/*\n==============================================================================\nBULLETS\n==============================================================================\n*/\n\n/*\n================\nTraceAttack\n================\n*/\nvoid(float damage, vector dir) TraceAttack =\n{\n\tlocal   vector  vel, org;\n\t\n\tvel = normalize(dir + v_up*crandom() + v_right*crandom());\n\tvel = vel + 2*trace_plane_normal;\n\tvel = vel * 200;\n\n\torg = trace_endpos - dir*4;\n\n\tif (trace_ent.takedamage)\n\t{\n\t\tblood_count = blood_count + 1;\n\t\tblood_org = org;\n\t\tAddMultiDamage (trace_ent, damage);\n\t}\n\telse\n\t{\n\t\tpuff_count = puff_count + 1;\n\t}\n};\n\n/*\n================\nFireBullets\n\nUsed by shotgun, super shotgun, and enemy soldier firing\nGo to the trouble of combining multiple pellets into a single damage call.\n================\n*/\n\n\n/*\n==============================================================================\n\nROCKETS\n\n==============================================================================\n*/\n\nvoid() T_MissileTouch =\n{\n\tlocal float     damg;\n\n//\tif (deathmatch == 4)\n//\t{\n//\tif ( ((other.weapon == 32) || (other.weapon == 16)))\n//\t\t{\t\n//\t\t\tif (random() < 0.1)\n//\t\t\t{\n//\t\t\t\tif (other != world)\n//\t\t\t\t{\n//\t//\t\t\t\tbprint (PRINT_HIGH, \"Got here\\n\");\n//\t\t\t\t\tother.deathtype = \"blaze\";\n//\t\t\t\t\tT_Damage (other, self, self.owner, 1000 );\n//\t\t\t\t\tT_RadiusDamage (self, self.owner, 1000, other);\n//\t\t\t\t}\n//\t\t\t}\n//\t\t}\t\n//\t}\n\n\tif (other == self.owner)\n\t\treturn;         // don't explode on owner\n\n\tif (self.voided) {\n\t\treturn;\n\t}\n\tself.voided = 1;\n\n\tif (pointcontents(self.origin) == CONTENT_SKY)\n\t{\n\t\tremove(self);\n\t\treturn;\n\t}\n\n\tdamg = 100 + random()*20;\n\t\n\tif (other.health)\n\t{\n\t\tother.deathtype = \"rocket\";\n\t\tT_Damage (other, self, self.owner, damg );\n\t}\n\n\t// don't do radius damage to the other, because all the damage\n\t// was done in the impact\n\n\n\tT_RadiusDamage (self, self.owner, 120, other, \"rocket\");\n\n//  sound (self, CHAN_WEAPON, \"weapons/r_exp3.wav\", 1, ATTN_NORM);\n\tself.origin = self.origin - 8 * normalize(self.velocity);\n\n\tWriteByte (MSG_MULTICAST, SVC_TEMPENTITY);\n\tWriteByte (MSG_MULTICAST, TE_EXPLOSION);\n\tWriteCoord (MSG_MULTICAST, self.origin_x);\n\tWriteCoord (MSG_MULTICAST, self.origin_y);\n\tWriteCoord (MSG_MULTICAST, self.origin_z);\n\tmulticast (self.origin, MULTICAST_PHS);\n\n\tremove(self);\n};\n\n\n\n/*\n================\nW_FireRocket\n================\n*/\nvoid() W_FireRocket =\n{\n\tDropAmmo ();\n\tself.attack_finished = time + 0.7;\n\t\n\tsound (self, CHAN_WEAPON, \"weapons/sgun1.wav\", 1, ATTN_NORM);\n\n\tmsg_entity = self;\n\tWriteByte (MSG_ONE, SVC_SMALLKICK);\n\n\tnewmis = spawn ();\n\tnewmis.owner = self;\n\tnewmis.movetype = MOVETYPE_FLYMISSILE;\n\tnewmis.solid = SOLID_BBOX;\n\t\t\n// set newmis speed     \n\n\tmakevectors (self.v_angle);\n\tnewmis.velocity = aim(self, 1000);\n\tnewmis.velocity = newmis.velocity * 1000;\n\tnewmis.angles = vectoangles(newmis.velocity);\n\t\n\tnewmis.touch = T_MissileTouch;\n\tnewmis.voided = 0;\n\t\n// set newmis duration\n\tnewmis.nextthink = time + 5;\n\tnewmis.think = SUB_Remove;\n\tnewmis.classname = \"rocket\";\n\n\tsetmodel (newmis, \"progs/missile.mdl\");\n\tsetsize (newmis, '0 0 0', '0 0 0');             \n\tsetorigin (newmis, self.origin + v_forward*8 + '0 0 16');\n};\n\n/*\n===============================================================================\nLIGHTNING\n===============================================================================\n*/\n\nvoid(entity from, float damage) LightningHit =\n{\n\tWriteByte (MSG_MULTICAST, SVC_TEMPENTITY);\n\tWriteByte (MSG_MULTICAST, TE_LIGHTNINGBLOOD);\n\tWriteCoord (MSG_MULTICAST, trace_endpos_x);\n\tWriteCoord (MSG_MULTICAST, trace_endpos_y);\n\tWriteCoord (MSG_MULTICAST, trace_endpos_z);\n\tmulticast (trace_endpos, MULTICAST_PVS);\n\n\tT_Damage (trace_ent, from, from, damage);\n};\n\n/*\n=================\nLightningDamage\n=================\n*/\nvoid(vector p1, vector p2, entity from, float damage) LightningDamage =\n{\n\tlocal entity            e1, e2;\n\tlocal vector            f;\n\t\n\tf = p2 - p1;\n\tnormalize (f);\n\tf_x = 0 - f_y;\n\tf_y = f_x;\n\tf_z = 0;\n\tf = f*16;\n\n\te1 = e2 = world;\n\n\ttraceline (p1, p2, FALSE, self);\n\n\tif (trace_ent.takedamage)\n\t{\n\t\tLightningHit (from, damage);\n/*\n\t\tif (self.classname == \"player\")\n\t\t{\n\t\t\tif (other.classname == \"player\")\n\t\t\t\ttrace_ent.velocity_z = trace_ent.velocity_z + 400;\n\t\t}\n*/\n\t}\n\n\te1 = trace_ent;\n\n\ttraceline (p1 + f, p2 + f, FALSE, self);\n\tif (trace_ent != e1 && trace_ent.takedamage)\n\t{\n\t\tLightningHit (from, damage);\n\t}\n\te2 = trace_ent;\n\n\ttraceline (p1 - f, p2 - f, FALSE, self);\n\tif (trace_ent != e1 && trace_ent != e2 && trace_ent.takedamage)\n\t{\n\t\tLightningHit (from, damage);\n\t}\n};\n\n\n\n\nvoid() W_FireLightning =\n{\n\tlocal   vector          org;\n\tlocal   float           cells;\n\n\tif (self.ammo_cells < 1)\n\t{\n\t\tself.weapon = W_BestWeapon ();\n\t\tW_SetCurrentAmmo ();\n\t\treturn;\n\t}\n\n// explode if under water\n\tif (self.waterlevel > 1)\n\t{\n\t\tif (deathmatch > 3)\n\t\t{\n\t\t\tif (random() <= 0.5)\n\t\t\t{\n\t\t\t\tself.deathtype = \"selfwater\";\n\t\t\t\tT_Damage (self, self, self.owner, 4000 );\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tcells = self.ammo_cells;\n\t\t\t\tself.ammo_cells = 0;\n\t\t\t\tW_SetCurrentAmmo ();\n\t\t\t\tT_RadiusDamage (self, self, 35*cells, world, \"\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tcells = self.ammo_cells;\n\t\t\tself.ammo_cells = 0;\n\t\t\tW_SetCurrentAmmo ();\n\t\t\tT_RadiusDamage (self, self, 35*cells, world,\"\");\n\t\t\treturn;\n\t\t}\n\t}\n\n\tif (self.t_width < time)\n\t{\n\t\tsound (self, CHAN_WEAPON, \"weapons/lhit.wav\", 1, ATTN_NORM);\n\t\tself.t_width = time + 0.6;\n\t}\n\tmsg_entity = self;\n\tWriteByte (MSG_ONE, SVC_SMALLKICK);\n\n\tif (deathmatch != 4)\n\t\tself.currentammo = self.ammo_cells = self.ammo_cells - 1;\n\n\torg = self.origin + '0 0 16';\n\t\n\ttraceline (org, org + v_forward*600, TRUE, self);\n\n\tWriteByte (MSG_MULTICAST, SVC_TEMPENTITY);\n\tWriteByte (MSG_MULTICAST, TE_LIGHTNING2);\n\tWriteEntity (MSG_MULTICAST, self);\n\tWriteCoord (MSG_MULTICAST, org_x);\n\tWriteCoord (MSG_MULTICAST, org_y);\n\tWriteCoord (MSG_MULTICAST, org_z);\n\tWriteCoord (MSG_MULTICAST, trace_endpos_x);\n\tWriteCoord (MSG_MULTICAST, trace_endpos_y);\n\tWriteCoord (MSG_MULTICAST, trace_endpos_z);\n\tmulticast (org, MULTICAST_PHS);\n\n\tLightningDamage (self.origin, trace_endpos + v_forward*4, self, 30);\n};\n\n\n//=============================================================================\n\n\nvoid() GrenadeExplode =\n{\n\tif (self.voided) {\n\t\treturn;\n\t}\n\tself.voided = 1;\n\n\tT_RadiusDamage (self, self.owner, 120, world, \"grenade\");\n\n\tWriteByte (MSG_MULTICAST, SVC_TEMPENTITY);\n\tWriteByte (MSG_MULTICAST, TE_EXPLOSION);\n\tWriteCoord (MSG_MULTICAST, self.origin_x);\n\tWriteCoord (MSG_MULTICAST, self.origin_y);\n\tWriteCoord (MSG_MULTICAST, self.origin_z);\n\tmulticast (self.origin, MULTICAST_PHS);\n\n\tremove (self);\n};\n\nvoid() GrenadeTouch =\n{\n\tif (other == self.owner)\n\t\treturn;         // don't explode on owner\n\tif (other.takedamage == DAMAGE_AIM)\n\t{\n\t\tGrenadeExplode();\n\t\treturn;\n\t}\n\tsound (self, CHAN_WEAPON, \"weapons/bounce.wav\", 1, ATTN_NORM);  // bounce sound\n\tif (self.velocity == '0 0 0')\n\t\tself.avelocity = '0 0 0';\n};\n\n/*\n================\nW_FireGrenade\n================\n*/\nvoid() W_FireGrenade =\n{       \n\tif (deathmatch != 4)\n\t\tself.currentammo = self.ammo_rockets = self.ammo_rockets - 1;\n\t\n\tsound (self, CHAN_WEAPON, \"weapons/grenade.wav\", 1, ATTN_NORM);\n\n\tmsg_entity = self;\n\tWriteByte (MSG_ONE, SVC_SMALLKICK);\n\n\tnewmis = spawn ();\n\tnewmis.voided=0;\n\tnewmis.owner = self;\n\tnewmis.movetype = MOVETYPE_BOUNCE;\n\tnewmis.solid = SOLID_BBOX;\n\tnewmis.classname = \"grenade\";\n\t\t\n// set newmis speed     \n\n\tmakevectors (self.v_angle);\n\n\tif (self.v_angle_x)\n\t\tnewmis.velocity = v_forward*600 + v_up * 200 + crandom()*v_right*10 + crandom()*v_up*10;\n\telse\n\t{\n\t\tnewmis.velocity = aim(self, 10000);\n\t\tnewmis.velocity = newmis.velocity * 600;\n\t\tnewmis.velocity_z = 200;\n\t}\n\n\tnewmis.avelocity = '300 300 300';\n\n\tnewmis.angles = vectoangles(newmis.velocity);\n\t\n\tnewmis.touch = GrenadeTouch;\n\t\n// set newmis duration\n\tif (deathmatch == 4)\n\t{\n\t\tnewmis.nextthink = time + 2.5;\t\t\n\t\tself.attack_finished = time + 1.1;\n//\t\tself.health = self.health - 1;\n\t\tT_Damage (self, self, self.owner, 10 );\n\t}\n\telse\n\t\tnewmis.nextthink = time + 2.5;\n\n\tnewmis.think = GrenadeExplode;\n\n\tsetmodel (newmis, \"progs/grenade.mdl\");\n\tsetsize (newmis, '0 0 0', '0 0 0');             \n\tsetorigin (newmis, self.origin);\n};\n\n\n//=============================================================================\n\nvoid() spike_touch;\nvoid() superspike_touch;\n\n\n/*\n===============\nlaunch_spike\n\nUsed for both the player and the ogre\n===============\n*/\nvoid(vector org, vector dir) launch_spike =\n{\n\tnewmis = spawn ();\n\tnewmis.voided=0;\n\tnewmis.owner = self;\n\tnewmis.movetype = MOVETYPE_FLYMISSILE;\n\tnewmis.solid = SOLID_BBOX;\n\n\tnewmis.angles = vectoangles(dir);\n\t\n\tnewmis.touch = spike_touch;\n\tnewmis.classname = \"spike\";\n\tnewmis.think = SUB_Remove;\n\tnewmis.nextthink = time + 6;\n\tsetmodel (newmis, \"progs/spike.mdl\");\n\tsetsize (newmis, VEC_ORIGIN, VEC_ORIGIN);               \n\tsetorigin (newmis, org);\n\n\tnewmis.velocity = dir * 1000;\n};\n\nvoid() W_FireSuperSpikes =\n{\n\tlocal vector    dir;\n\t\n\tsound (self, CHAN_WEAPON, \"weapons/spike2.wav\", 1, ATTN_NORM);\n\tself.attack_finished = time + 0.2;\n\tif (deathmatch != 4) \n\t\tself.currentammo = self.ammo_nails = self.ammo_nails - 2;\n\tdir = aim (self, 1000);\n\tlaunch_spike (self.origin + '0 0 16', dir);\n\tnewmis.touch = superspike_touch;\n\tsetmodel (newmis, \"progs/s_spike.mdl\");\n\tsetsize (newmis, VEC_ORIGIN, VEC_ORIGIN);               \n\tmsg_entity = self;\n\tWriteByte (MSG_ONE, SVC_SMALLKICK);\n};\n\nvoid(float ox) W_FireSpikes =\n{\n\tlocal vector    dir;\n\t\n\tmakevectors (self.v_angle);\n\t\n\tif (self.ammo_nails >= 2 && self.weapon == IT_SUPER_NAILGUN)\n\t{\n\t\tW_FireSuperSpikes ();\n\t\treturn;\n\t}\n\n\tif (self.ammo_nails < 1)\n\t{\n\t\tself.weapon = W_BestWeapon ();\n\t\tW_SetCurrentAmmo ();\n\t\treturn;\n\t}\n\n\tsound (self, CHAN_WEAPON, \"weapons/rocket1i.wav\", 1, ATTN_NORM);\n\tself.attack_finished = time + 0.2;\n\tif (deathmatch != 4)\n\t\tself.currentammo = self.ammo_nails = self.ammo_nails - 1;\n\tdir = aim (self, 1000);\n\tlaunch_spike (self.origin + '0 0 16' + v_right*ox, dir);\n\n\tmsg_entity = self;\n\tWriteByte (MSG_ONE, SVC_SMALLKICK);\n};\n\n\n\n//.float hit_z;\nvoid() spike_touch =\n{\n\tif (other == self.owner)\n\t\treturn;\n\n\tif (self.voided) {\n\t\treturn;\n\t}\n\tself.voided = 1;\n\n\tif (other.solid == SOLID_TRIGGER)\n\t\treturn; // trigger field, do nothing\n\n\tif (pointcontents(self.origin) == CONTENT_SKY)\n\t{\n\t\tremove(self);\n\t\treturn;\n\t}\n\t\n// hit something that bleeds\n\tif (other.takedamage)\n\t{\n\t\tspawn_touchblood (9);\n\t\tother.deathtype = \"nail\";\n\t\tT_Damage (other, self, self.owner, 9);\n\t}\n\telse\n\t{\n\t\tWriteByte (MSG_MULTICAST, SVC_TEMPENTITY);\n\t\tif (self.classname == \"wizspike\")\n\t\t{\n\t\t\tT_Damage (other, self, self.owner, 9+random()*18);\n\t\t\tWriteByte (MSG_MULTICAST, TE_WIZSPIKE);\n\t\t}\n\t\telse if (self.classname == \"knightspike\")\n\t\t\tWriteByte (MSG_MULTICAST, TE_KNIGHTSPIKE);\n\t\telse\n\t\t\tWriteByte (MSG_MULTICAST, TE_SPIKE);\n\t\tWriteCoord (MSG_MULTICAST, self.origin_x);\n\t\tWriteCoord (MSG_MULTICAST, self.origin_y);\n\t\tWriteCoord (MSG_MULTICAST, self.origin_z);\n\t\tmulticast (self.origin, MULTICAST_PHS);\n\t}\n\n\tremove(self);\n\n};\n\nvoid() superspike_touch =\n{\n\tif (other == self.owner)\n\t\treturn;\n\n\tif (self.voided) {\n\t\treturn;\n\t}\n\tself.voided = 1;\n\n\n\tif (other.solid == SOLID_TRIGGER)\n\t\treturn; // trigger field, do nothing\n\n\tif (pointcontents(self.origin) == CONTENT_SKY)\n\t{\n\t\tremove(self);\n\t\treturn;\n\t}\n\t\n// hit something that bleeds\n\tif (other.takedamage)\n\t{\n\t\tspawn_touchblood (18);\n\t\tother.deathtype = \"supernail\";\n\t\tT_Damage (other, self, self.owner, 18);\n\t}\n\telse\n\t{\n\t\tWriteByte (MSG_MULTICAST, SVC_TEMPENTITY);\n\t\tWriteByte (MSG_MULTICAST, TE_SUPERSPIKE);\n\t\tWriteCoord (MSG_MULTICAST, self.origin_x);\n\t\tWriteCoord (MSG_MULTICAST, self.origin_y);\n\t\tWriteCoord (MSG_MULTICAST, self.origin_z);\n\t\tmulticast (self.origin, MULTICAST_PHS);\n\t}\n\n\tremove(self);\n\n};\n\n\n/*\n===============================================================================\n\nPLAYER WEAPON USE\n\n===============================================================================\n*/\n\nvoid() SetWeaponModel =\n{\n\tself.weaponmodel = GetItemVModel(ToIID(ItemInSlot(self, self.current_slot)));\n};\n\nvoid() W_SetCurrentAmmo =\n{\n\tlocal string x;\n\n\tplayer_run ();          // get out of any weapon firing states\n\n\tself.items = self.items - ( self.items & (IT_SHELLS | IT_NAILS | IT_ROCKETS | IT_CELLS) );\n\t\n\n/*SPIKEREMOVED\n\tif (self.current_slot == 1)\n\t\tself.currentammo = ToStatus(self.islot1);\n\telse if (self.current_slot == 2)\n\t\tself.currentammo = ToStatus(self.islot2);\n\telse if (self.current_slot == 4)\n\t{\n\t\tif (self.team == 1)\n\t\t{\n\t\t\tsound (self, CHAN_WEAPON, \"misc/tools.wav\", 1, ATTN_NORM);\n\t\t\tself.items = (self.items | IT_LIGHTNING);\n\t\t\tself.currentammo = 0;\n\t\t\tself.weaponmodel = \"\";\n\t\t}\n\t\telse\n\t\t\tsound (self, CHAN_WEAPON, \"misc/menu3.wav\", 1, ATTN_NORM);\n\t}\n*/\n//\telse\n\t\tsound (self, CHAN_WEAPON, \"misc/menu3.wav\", 1, ATTN_NORM);\n\n\t\tif (self.current_slot == 1)\n\t\t\tx = GetItemName(ToIID(self.islot1));\n\t\tif (self.current_slot == 2)\n\t\t\tx = GetItemName(ToIID(self.islot2));\n/*SPIKEREMOVED\n\t\tif (self.current_slot == 3)\n\t\t\tx = \"grenade\";\n\t\tif (self.current_slot == 4)\n\t\t\tx = \"tools\";\n*/\n\n\t\tsprint(self, 2, x);\n\t\tsprint (self, PRINT_HIGH, \" selected.\\n\");\n\t\tsound (self, CHAN_WEAPON, \"misc/weapon.wav\", 1, ATTN_NORM);\n\t\tSetWeaponModel();\n\n\tself.currentammo = ToStatus(ItemInSlot(self, self.current_slot));\n};\n\nfloat() W_BestWeapon =\n{\n\tlocal   float   it;\n\t\n\tit = self.items;\n\n\tif (self.waterlevel <= 1 && self.ammo_cells >= 1 && (it & IT_LIGHTNING) )\n\t\treturn IT_LIGHTNING;\n\telse if(self.ammo_nails >= 2 && (it & IT_SUPER_NAILGUN) )\n\t\treturn IT_SUPER_NAILGUN;\n\telse if(self.ammo_shells >= 2 && (it & IT_SUPER_SHOTGUN) )\n\t\treturn IT_SUPER_SHOTGUN;\n\telse if(self.ammo_nails >= 1 && (it & IT_NAILGUN) )\n\t\treturn IT_NAILGUN;\n\telse if(self.ammo_shells >= 1 && (it & IT_SHOTGUN)  )\n\t\treturn IT_SHOTGUN;\n\t\t\n/*\n\tif(self.ammo_rockets >= 1 && (it & IT_ROCKET_LAUNCHER) )\n\t\treturn IT_ROCKET_LAUNCHER;\n\telse if(self.ammo_rockets >= 1 && (it & IT_GRENADE_LAUNCHER) )\n\t\treturn IT_GRENADE_LAUNCHER;\n\n*/\n\n\treturn IT_AXE;\n};\n\nvoid (float slotno) ReloadWeapon =\n{\n\tlocal float at;\n\tlocal float as;\n\tlocal float asv;\n\tlocal float ac;\n\tlocal float x;\n\tlocal float wid;\n\n\tlocal .float wslot;\n\n\tif (self.rtime > time || self.attack_finished > time)\n\t\treturn;\n\n\twslot = SlotField(slotno);\n\n\tif (self.wslot == 0)\n\t\treturn;\t//already empty..\n\n\twid = ToIID(self.wslot);\n\tat = WeaponAmmoType(wid);\n\tas = SlotOfItem(self, at);\n\tif (as == 0)\n\t{\n\t\tsprint(self, 2, \"no suitable ammo\\n\");\n\t\treturn;\n\t}\n\tasv = ItemInSlot(self, as);\n\tac = ToStatus(asv);\n\n\tx = WeaponMagQuant(wid);\t//get the max ammo capacity\n\n\tif (ToStatus(self.wslot) == x) //weapon full already\n\t\treturn;\n\n\tx = x - ToStatus(self.wslot);\t//take away the ammo currently in the weapon\n\tif (x > ac)\t//make sure there's enough ammo\n\t\tx = ac;\n\tac -= x;\n\n\tif (ac)\n\t\tasv = SlotVal(at, ac);\n\telse\n\t\tasv = 0;\t//totally used up.\n\n\tSetItemSlot(self, as, asv);\n\n\tself.wslot = SlotVal(wid, x + ToStatus(self.wslot));\n\n\tif (wid == IID_WP_ROCKETLAUNCHER || wid == IID_WP_PIPERIFLE || wid == IID_WP_WINCHESTER || wid == IID_WP_MOSSBERG)\n\t{\n\t\tsound (self, CHAN_WEAPON, \"weapons/shell.wav\", TRUE, ATTN_NORM);\n\t\tself.attack_finished = time + 1;\n\t\tself.rtime = time + 1;\n\t\tif (self.current_slot == slotno)\n\t\t\tself.currentammo = ToStatus(ItemInSlot(self, slotno));\n\n\t\treturn;\n\t}\n\telse\n\t\tsound (self, CHAN_WEAPON, \"weapons/reload.wav\", TRUE, ATTN_NORM);\n\n\tsprint(self, 2, \"reloading...\\n\");\n\n\tif (self.perk == 3)\n\t{\n\t\tself.attack_finished = time + 1;\n\t\tself.rtime = time + 1;\n\t}\n\telse\n\t{\n\t\tself.attack_finished = time + 2;\n\t\tself.rtime = time + 2;\n\t}\n\n\tif (self.current_slot == slotno)\n\t\tself.currentammo = ToStatus(ItemInSlot(self, slotno));\n\n\tplayer_run();\n};\n\n\nfloat() W_CheckNoAmmo =\n{\n\tlocal float at;\n\tlocal .float slotfield;\n\tslotfield = SlotField(self.current_slot);\n\n\tif (ToIID(self.slotfield) == IID_NONE)\n\t\treturn FALSE;\n\n\tif (ToStatus(self.slotfield) < 1)\n\t{\n\t\tat = WeaponAmmoType(ToIID(self.slotfield));\n\t\tif (at == 0)\n\t\t\treturn FALSE;\n\n\t\tat = SlotOfItem(self, at);\n\t\tif (at == 0)\n\t\t{\n\t\t\tself.attack_finished = (time + 0.2);\n\t\t\tstuffcmd (self, \"-attack\\n\");\n\t\t\tsound (self, CHAN_WEAPON, \"weapons/click.wav\", TRUE, ATTN_NORM);\n\t\t\treturn TRUE;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tstuffcmd (self, \"-attack\\n\");\n\t\t\tReloadWeapon (self.current_slot);\n\t\t\treturn TRUE;\n\t\t}\n\t}\n\n\treturn FALSE;\n};\n\n/*\n============\nW_Attack\n\nAn attack impulse can be triggered now\n============\n*/\n\nvoid() W_Attack =\n{\n\tlocal float weap;\n\n\tmakevectors     (self.v_angle);                 // calculate forward angle for velocity\n\tself.show_hostile = time + 1;   // wake monsters up\n\n\tif (self.rtime > time)\n\t\treturn;\n\tif (self.attack_finished > time)\n\t\treturn;\n\n\tif (W_CheckNoAmmo())\n\t\treturn;\n\n\tweap = ToIID(self.(SlotField(self.current_slot)));\n\n\n\tif (weap == IID_NONE ||\n\t    weap == IID_WP_KNIFE ||\n\t    weap == IID_WP_AXE ||\n\t    weap == IID_WP_VIBROBLADE ||\n\t    weap == IID_WP_POWERAXE)\n\t{\n\t\tself.attack_finished = time + 0.50;\n\t\tplayer_knife1 ();\n\t}\n\telse if (weap == IID_WP_USP)\n\t\tFirePistol(12, 2, \"weapons/1911.wav\", 2000, 0.25);\n\telse if (weap == IID_WP_DEAGLE)\n\t\tFirePistol(15, 2, \"weapons/deagle.wav\", 2000, 0.25);\n\telse if (weap == IID_WP_NEEDLER)\n\t\tFirePistol(10, 2, \"weapons/needler.wav\", 2000, 0.25);\n\telse if (weap == IID_WP_ALIENBLASTER)\n\t\tFireAlienBlaster();\n\telse if (weap == IID_WP_PIPERIFLE)\n\t\tFireAssaultRifle(18, 2, \"weapons/rangem.wav\", 3000, 0.1);\n\telse if (weap == IID_WP_WINCHESTER)\n\t\tW_FireShotgun (1, 5, 6, 160, 3000, 0);\n\telse if (weap == IID_WP_MOSSBERG)\n\t\tW_FireShotgun (1, 5, 6, 160, 3000, 0);\n\telse if (weap == IID_WP_JACKHAMMER)\n\t\tW_FireShotgun (1, 5, 6, 160, 3000, 1);\n\telse if (weap == IID_WP_MP9)\n\t\tFireSMG(12, 2, \"weapons/burst.wav\", 2000, 0.09);\n\telse if (weap == IID_WP_MP7)\n\t\tFireSMG(12, 2, \"weapons/mp7.wav\", 2000, 0.09);\n\telse if (weap == IID_WP_RANGEMASTER)\n\t\tFireAssaultRifle(14, 2, \"weapons/rangem.wav\", 4000, 0.5);\n\telse if (weap == IID_WP_AK112)\n\t\tFireAssaultRifle(14, 2, \"weapons/ak112.wav\", 4000, 0.09);\n\telse if (weap == IID_WP_AK74)\n\t\tFireAssaultRifle(18, 2, \"weapons/ak47.wav\", 4000, 0.1);\n\telse if (weap == IID_WP_DKS1)\n\t\tFireAssaultRifle(24, 2, \"weapons/dks-1.wav\", 8000, 0.5);\n\telse if (weap == IID_WP_MOONLIGHT)\n\t\tFireAssaultRifle(14, 2, \"weapons/m4-nw.wav\", 4000, 0.09);\n\telse if (weap == IID_WP_SA80)\n\t\tFireAssaultRifle(17, 2, \"weapons/sa80.wav\", 4000, 0.08);\n\telse if (weap == IID_WP_FNFAL)\n\t\tFireAssaultRifle(12, 3, \"weapons/fnfal.wav\", 6000, 0.12);\n\n//float IID_WP_GAUSERIFLE = 421;\n//float IID_WP_PULSERIFLE = 422;\n\n\telse if (weap == IID_GREN_FRAG)\n\t\tplayer_throw1();\n\telse if (weap == IID_GREN_EMP)\n\t\tplayer_throw1();\n\telse if (weap == IID_GREN_SMOKE)\n\t\tplayer_throw1();\n\telse if (weap == IID_GREN_FLASH)\n\t\tplayer_throw1();\n\n\telse if (weap == IID_WP_ROCKETLAUNCHER)\n\t\tW_FireRocket();\n\n\telse if (weap == IID_WP_TOOLKIT)\n\t{\n\t\tif (!FireToolkit())\n\t\t{\n\t\t\tself.attack_finished = time + 0.25;\n\t\t\tplayer_knife1 ();\n\t\t}\n\t}\n\telse if (IsChem(weap))\n\t{\n\t\tself.attack_finished = time + 0.25;\n\t\tplayer_knife1 ();\n\t}\n\telse\n\t\tcenterprint(self, \"Not implemented (\", ftos(weap), \")\");\n};\n\n/*\n============\nW_ChangeWeapon\n\n============\n*/\nvoid() W_ChangeWeapon =\n{\n\tlocal   float   it, am, fl;\n\t\n\tit = self.items;\n\tam = 0;\n\t\n\tif (self.impulse == 1)\n\t{\n\t\tfl = IT_NAILGUN;\n\t\tself.current_slot = 1;\n\t}\n\tif (self.impulse == 2)\n\t{\n\t\tfl = IT_SUPER_NAILGUN;\n\t\tself.current_slot = 2;\n\t}\n\n\tself.weapon = fl;               \n\tW_SetCurrentAmmo ();\n};\n\n/*\n============\nCheatCommand\n============\n*/\nvoid() CheatCommand =\n{\n\n};\n\n/*\n============\nCycleWeaponCommand\n\nGo to the next weapon with ammo\n============\n*/\nvoid() CycleWeaponCommand =\n{\n\tlocal   float   it, am;\n\t\n\tit = self.items;\n\tself.impulse = 0;\n\n\twhile (1)\n\t{\n\t\tam = 0;\n\n\t\tif (self.weapon == IT_LIGHTNING)\n\t\t{\n\t\t\tself.weapon = IT_AXE;\n\t\t}\n\t\telse if (self.weapon == IT_AXE)\n\t\t{\n\t\t\tself.weapon = IT_SHOTGUN;\n\t\t\tif (self.ammo_shells < 1)\n\t\t\t\tam = 1;\n\t\t}\n\t\telse if (self.weapon == IT_SHOTGUN)\n\t\t{\n\t\t\tself.weapon = IT_SUPER_SHOTGUN;\n\t\t\tif (self.ammo_shells < 2)\n\t\t\t\tam = 1;\n\t\t}               \n\t\telse if (self.weapon == IT_SUPER_SHOTGUN)\n\t\t{\n\t\t\tself.weapon = IT_NAILGUN;\n\t\t\tif (self.ammo_nails < 1)\n\t\t\t\tam = 1;\n\t\t}\n\t\telse if (self.weapon == IT_NAILGUN)\n\t\t{\n\t\t\tself.weapon = IT_SUPER_NAILGUN;\n\t\t\tif (self.ammo_nails < 2)\n\t\t\t\tam = 1;\n\t\t}\n\t\telse if (self.weapon == IT_SUPER_NAILGUN)\n\t\t{\n\t\t\tself.weapon = IT_GRENADE_LAUNCHER;\n\t\t\tif (self.ammo_rockets < 1)\n\t\t\t\tam = 1;\n\t\t}\n\t\telse if (self.weapon == IT_GRENADE_LAUNCHER)\n\t\t{\n\t\t\tself.weapon = IT_ROCKET_LAUNCHER;\n\t\t\tif (self.ammo_rockets < 1)\n\t\t\t\tam = 1;\n\t\t}\n\t\telse if (self.weapon == IT_ROCKET_LAUNCHER)\n\t\t{\n\t\t\tself.weapon = IT_LIGHTNING;\n\t\t\tif (self.ammo_cells < 1)\n\t\t\t\tam = 1;\n\t\t}\n\t\n\t\tif ( (self.items & self.weapon) && am == 0)\n\t\t{\n\t\t\tW_SetCurrentAmmo ();\n\t\t\treturn;\n\t\t}\n\t}\n\n};\n\n\n\n\nvoid () ProneOff =\n{\n\tsetsize(self, '-16 -16 -24', '16 16 32');\n\n\tif (!walkmove(0, 0))\n\t{\n\t\tsprint (self, 2, \"not enough room to stand up.\\n\");\n\t\tsetsize(self, '-16 -16 -24', '16 16 0');\n\t\treturn;\n\t}\n\n\tsprint (self, 2, \"position: stand.\\n\");\n\tself.position = 0;\n\tPositionControl();\n\tplayer_run ();\n};\n\nvoid () ProneOn =\n{\n\tif (self.velocity_z != 0)\n\t\treturn;\n\n\tif (self.position == 2)\n\t{\n\t\tProneOff();\n\t\treturn;\n\t}\n\n\tself.maxspeed = (self.maxspeed * 0.25);\n\tself.position = 2;\n\tself.view_ofs = '0 0 -10';\n\tsprint (self, 2, \"position: prone.\\n\");\n\tPositionControl();\n};\n\n\nvoid () DuckOff =\n{\n\tsetsize(self, '-16 -16 -24', '16 16 32');\n\n\tif (!walkmove(0, 0))\n\t{\n\t\tsprint (self, 2, \"not enough room to stand up.\\n\");\n\t\tsetsize(self, '-16 -16 -24', '16 16 16');\n\t\treturn;\n\t}\n\tsprint (self, 2, \"position: stand.\\n\");\n\tself.view_ofs = '0 0 22';\n\tself.position = 0;\n\tPositionControl();\n\tplayer_run ();\n};\n\n\nvoid () DuckOn =\n{\n\tif (self.velocity_z != 0)\n\t\treturn;\n\n\tif (self.position == 1)\n\t{\n\t\tDuckOff();\n\t\treturn;\n\t}\n\n\tself.maxspeed = (self.maxspeed * 0.50);\n\tself.position = 1;\n\tsprint (self, 2, \"position: duck.\\n\");\n\tPositionControl();\n};\n\n\n/*\n============\nCycleWeaponReverseCommand\n\nGo to the prev weapon with ammo\n============\n*/\nvoid() CycleWeaponReverseCommand =\n{\n\tlocal   float   it, am;\n\t\n\tit = self.items;\n\tself.impulse = 0;\n\n\twhile (1)\n\t{\n\t\tam = 0;\n\n\t\tif (self.weapon == IT_LIGHTNING)\n\t\t{\n\t\t\tself.weapon = IT_ROCKET_LAUNCHER;\n\t\t\tif (self.ammo_rockets < 1)\n\t\t\t\tam = 1;\n\t\t}\n\t\telse if (self.weapon == IT_ROCKET_LAUNCHER)\n\t\t{\n\t\t\tself.weapon = IT_GRENADE_LAUNCHER;\n\t\t\tif (self.ammo_rockets < 1)\n\t\t\t\tam = 1;\n\t\t}\n\t\telse if (self.weapon == IT_GRENADE_LAUNCHER)\n\t\t{\n\t\t\tself.weapon = IT_SUPER_NAILGUN;\n\t\t\tif (self.ammo_nails < 2)\n\t\t\t\tam = 1;\n\t\t}\n\t\telse if (self.weapon == IT_SUPER_NAILGUN)\n\t\t{\n\t\t\tself.weapon = IT_NAILGUN;\n\t\t\tif (self.ammo_nails < 1)\n\t\t\t\tam = 1;\n\t\t}\n\t\telse if (self.weapon == IT_NAILGUN)\n\t\t{\n\t\t\tself.weapon = IT_SUPER_SHOTGUN;\n\t\t\tif (self.ammo_shells < 2)\n\t\t\t\tam = 1;\n\t\t}               \n\t\telse if (self.weapon == IT_SUPER_SHOTGUN)\n\t\t{\n\t\t\tself.weapon = IT_SHOTGUN;\n\t\t\tif (self.ammo_shells < 1)\n\t\t\t\tam = 1;\n\t\t}\n\t\telse if (self.weapon == IT_SHOTGUN)\n\t\t{\n\t\t\tself.weapon = IT_AXE;\n\t\t}\n\t\telse if (self.weapon == IT_AXE)\n\t\t{\n\t\t\tself.weapon = IT_LIGHTNING;\n\t\t\tif (self.ammo_cells < 1)\n\t\t\t\tam = 1;\n\t\t}\n\t\n\t\tif ( (it & self.weapon) && am == 0)\n\t\t{\n\t\t\tW_SetCurrentAmmo ();\n\t\t\treturn;\n\t\t}\n\t}\n\n};\n\n\n\n/*\n============\nServerflagsCommand\n\nJust for development\n============\n*/\nvoid() ServerflagsCommand =\n{\n\tserverflags = serverflags * 2 + 1;\n};\n\n\n/*\n============\nImpulseCommands\n\n============\n*/\nvoid() ImpulseCommands =\n{\n\tif (self.impulse >= 1 && self.impulse <= 4 && self.currentmenu == \"none\")\n\t\tW_ChangeWeapon ();\n\n\tif (self.impulse >= 1 && self.impulse <= 10 && self.currentmenu != \"none\")\n\t\tW_PlayerMenu ();\n\n\tif (self.impulse == 11)\n\t\tServerflagsCommand ();\n\tif (self.impulse == 12)\n\t\tCycleWeaponReverseCommand ();\n\tif (self.impulse == 200)\n\t\tDuckOn ();\n\tif (self.impulse == 201)\n\t\tProneOn ();\n\n\tif (self.impulse == 50)\n\t\tReloadWeapon (self.current_slot);\n//\tif (self.impulse == 51)\n//\t\tUseChem ();\n\tif (self.impulse == 52)\n\t\tBuyMenu ();\n\tif (self.impulse == 53)\n\t\tSpecial ();\n\tif (self.impulse == 54)\n\t\tExitScreen ();\n\tif (self.impulse == 55)\n\t\tDropFromSlot (self.current_slot, 1, 0);\n\tif (self.impulse == 56)\n\t\tCharacterSheet ();\n\tif (self.impulse == 57)\n\t\tUseEquipment ();\n\n\tif (self.impulse == 255)\n\t\tself.ammo_shells = 200;\n\n\tself.impulse = 0;\n};\n\n/*\n============\nW_WeaponFrame\n\nCalled every frame so impulse events can be handled as well as possible\n============\n*/\nvoid() W_WeaponFrame =\n{\n\tif (time < self.attack_finished)\n\t\treturn;\n\n\tImpulseCommands ();\n\t\n// check for attack\n\tif (self.button0)\n\t{\n\t\tSuperDamageSound ();\n\t\tW_Attack ();\n\t}\n\tif (!self.button0)\n\t{\n\t\tif (self.attack > 0)\n\t\t\tplayer_run ();\n\n\t\tself.attack = 0;\n\t}\n};\n\n/*\n========\nSuperDamageSound\n\nPlays sound if needed\n========\n*/\nvoid() SuperDamageSound =\n{\n\tif (self.super_damage_finished > time)\n\t{\n\t\tif (self.super_sound < time)\n\t\t{\n\t\t\tself.super_sound = time + 1;\n\t\t\tsound (self, CHAN_BODY, \"items/damage3.wav\", 1, ATTN_NORM);\n\t\t}\n\t}\n\treturn;\n};\n\n\nvoid () DropAmmo =\n{\n\tlocal .float wslot;\n\n\twslot = SlotField(self.current_slot);\n\n\tself.currentammo = (ToStatus(self.wslot) )- 1;\n\tself.wslot = SlotVal(ToIID(self.wslot), self.currentammo);\n\tself.currentammo = ToStatus(self.wslot);\n};\n\nvoid() muzzleflash =\n{\n\tWriteByte (MSG_MULTICAST, SVC_MUZZLEFLASH);\n\tWriteEntity (MSG_MULTICAST, self);\n\tmulticast (self.origin, MULTICAST_PVS);\n};\n\nvoid () autofire =\n{\n\tif (self.frame == 88)\n\t\tself.frame = 89;\n\telse\n\t\tself.frame = 88;\n\n\n\tif (self.weaponframe == 1)\n\t\tself.weaponframe = 2;\n\telse\n\t\tself.weaponframe = 1;\n\n\tmuzzleflash ();\n};\n\nvoid () autofire_s =\n{\n\tif (self.frame == 88)\n\t\tself.frame = 89;\n\telse\n\t\tself.frame = 88;\n\n\tif (self.weaponframe == 1)\n\t\tself.weaponframe = 2;\n\telse\n\t\tself.weaponframe = 1;\n\n\tmuzzleflash ();\n};\n\n\nvoid () player_single1 = [ 88, player_single2 ]\n{\n\tself.weaponframe = 1;\n\tmuzzleflash ();\n};\n\nvoid () player_single2 = [ 89, player_run ]\n{\n\tself.weaponframe = 2;\n};\n\nvoid () player_single1_s = [ 183, player_single2_s ]\n{\n\tself.weaponframe = 1;\n\tmuzzleflash ();\n};\n\nvoid () player_single2_s = [ 184, player_run ]\n{\n\tself.weaponframe = 2;\n};\n\nvoid (vector org) bullet_hole =\n{\n\tlocal float r;\n\tlocal entity ric;\n\n\tWriteByte (MSG_MULTICAST, SVC_TEMPENTITY);\n\tWriteByte (MSG_MULTICAST, TE_SPIKE);\n\tWriteCoord (MSG_MULTICAST, org_x);\n\tWriteCoord (MSG_MULTICAST, org_y);\n\tWriteCoord (MSG_MULTICAST, org_z);\n\tmulticast (self.origin, MULTICAST_PHS);\n\n\tr = random();\n\tric = spawn();\n\tsetorigin(ric, org);\n\n\tif (r <= 0.20)\n\t\tsound (ric, CHAN_WEAPON, \"weapons/ric1.wav\", TRUE, ATTN_NORM);\n\telse if (r <= 0.40)\n\t\tsound (ric, CHAN_WEAPON, \"weapons/ric2.wav\", TRUE, ATTN_NORM);\n\telse if (r <= 0.60)\n\t\tsound (ric, CHAN_WEAPON, \"weapons/ric3.wav\", TRUE, ATTN_NORM);\n\telse if (r <= 0.80)\n\t\tsound (ric, CHAN_WEAPON, \"weapons/ric4.wav\", TRUE, ATTN_NORM);\n\telse\n\t\tsound (ric, CHAN_WEAPON, \"weapons/ric5.wav\", TRUE, ATTN_NORM);\n\n\tremove(ric);\n};\n\nvoid (vector test, float length, float dam) penetrate =\n{\n\tlocal vector org;\n\tlocal vector start;\n\tlocal vector end;\n\tlocal float go;\n\tlocal float tl;\n\n\n\tgo = 0;\n\ttl = 8;\n\n\tlength = 32 + dam;\n\n\twhile (tl < length)\n\t{\n\t\tmakevectors (self.v_angle);\n\t\tstart = (test + v_forward*tl);\n\n\t\tif (pointcontents (start) != CONTENT_SOLID && go == 0) //object penetrated\n\t\t{\n\t\t\t\tmakevectors (self.v_angle);\n\t\t\t\tend = (test + (v_forward * 8 * length));\n\t\t\t\ttraceline (start, end, FALSE, self);\n\t\t\t\tif (trace_fraction == 1) //nothing behind object\n\t\t\t\t\treturn;\n\n\t\t\t\tif (trace_fraction > 0)\n\t\t\t\t{\n\t\t\t\t\tgo = 1;\n\n\t\t\t\t\tif (trace_ent.takedamage)\n\t\t\t\t\t{\n                                                \n\t\t\t\t\t\tif (trace_ent.solid != SOLID_BSP)\n\t\t\t\t\t\t\tSpawnBlood (org, 1);\n\t\n\t\t\t\t\t\tT_Damage (trace_ent, self, self, dam);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tWriteByte (MSG_MULTICAST, SVC_TEMPENTITY);\n\t\t\t\t\t\tWriteByte (MSG_MULTICAST, TE_SPIKE);\n\t\t\t\t\t\tWriteCoord (MSG_MULTICAST, trace_endpos_x);\n\t\t\t\t\t\tWriteCoord (MSG_MULTICAST, trace_endpos_y);\n\t\t\t\t\t\tWriteCoord (MSG_MULTICAST, trace_endpos_z);\n\t\t\t\t\t\tmulticast (trace_endpos, MULTICAST_PHS);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t}\n\t\ttl = tl + 4;\n}\n};\n\nvoid (entity temp, vector org, float damage) SpawnWood =\n{\n\n\t\tif (random()*6 <= 3)\n\t\t\tsound (temp, CHAN_WEAPON, \"misc/woodhit.wav\", TRUE, ATTN_NORM);\n\t\telse\n\t\t\tsound (temp, CHAN_WEAPON, \"misc/woodhit2.wav\", TRUE, ATTN_NORM);\n\n\t\tWriteByte (MSG_MULTICAST, SVC_TEMPENTITY);\n\t\tWriteByte (MSG_MULTICAST, TE_SUPERSPIKE);\n\t\tWriteCoord (MSG_MULTICAST, org_x);\n\t\tWriteCoord (MSG_MULTICAST, org_y);\n\t\tWriteCoord (MSG_MULTICAST, org_z);\n\t\tmulticast (self.origin, MULTICAST_PHS);\n};\n\nvoid () EMPExplode =\n{\n\tlocal entity te;\n\n\tself.velocity = VEC_ORIGIN;\n\tWriteByte (MSG_BROADCAST, SVC_TEMPENTITY);\n\tWriteByte (MSG_BROADCAST, TE_TAREXPLOSION);\n\tWriteCoord (MSG_BROADCAST, self.origin_x);\n\tWriteCoord (MSG_BROADCAST, self.origin_y);\n\tWriteCoord (MSG_BROADCAST, self.origin_z);\n\tsound (self, CHAN_BODY, \"misc/flash.wav\", 1, ATTN_NORM);\n\tsound (self, CHAN_BODY, \"misc/flash.wav\", 0.2, ATTN_NONE);\n\tte = findradius (self.origin, 300);\n\twhile (te)\n\t{\n\t\tif ((((te.classname == \"camera\") || (te.classname == \"alarm\")) && (te.owner.pcamera == 0)))\n\t\t{\n\t\t\tte.owner.pcamera = 0;\n\t\t\tte.owner.pcamera2 = 0;\n\t\t\tte.owner.equipment_slot = 0;\n\t\t\tsprint (self.owner, 2, te.owner.netname);\n\t\t\tsprint (self.owner, 2, \"'s \");\n\t\t\tsprint (self.owner, 2, te.classname);\n\t\t\tsprint (self.owner, 2, \" was wiped out!\\n\");\n\t\t\tremove (te);\n\t\t}\n\t\tte = te.chain;\n\t}\n\tT_RadiusDamage (self, self.owner, 45+random()*45, other, \"\");\n\tremove (self);\n};\n\nvoid (vector org) CreateSmoke =\n{\n\tnewmis = spawn ();\n\tsetorigin (newmis, org);\n\tnewmis.movetype = MOVETYPE_NONE;\n\tnewmis.solid = SOLID_NOT;\n\tnewmis.velocity = VEC_ORIGIN;\n\tnewmis.nextthink = (time + SVC_BIGKICK);\n\tnewmis.think = SUB_Remove;\n\tnewmis.touch = SUB_Null;\n\tnewmis.classname = \"smoke\";\n\tnewmis.frame = 0;\n\tnewmis.cnt = 0;\n\tnewmis.avelocity_x = (random () * 100);\n\tnewmis.avelocity_y = (random () * 100);\n\tsetsize (newmis, VEC_ORIGIN, VEC_ORIGIN);\n};\n\n\nvoid () SmokeThink =\n{\n\tself.cnt = (self.cnt + 1);\n\n\tWriteByte (MSG_MULTICAST, SVC_TEMPENTITY);\n\tWriteByte (MSG_MULTICAST, TE_GUNSHOT);\n\tWriteByte (MSG_MULTICAST, 2);\n\tWriteCoord (MSG_MULTICAST, self.origin_x);\n\tWriteCoord (MSG_MULTICAST, self.origin_y);\n\tWriteCoord (MSG_MULTICAST, self.origin_z);\n\tmulticast (self.origin, MULTICAST_PVS);\n\n\tself.nextthink = (time + 0.33);\n\tif (self.cnt >= 90)\n\t\tremove (self);\n};\n\nvoid () FragExplode =\n{\n\tlocal float r;\n\n\tsound (self, CHAN_VOICE, \"ambience/gunfire7.wav\", 1, ATTN_NONE);\n\tself.origin = (self.origin + '0 0 16');\n\tWriteByte (MSG_BROADCAST, SVC_TEMPENTITY);\n\tWriteByte (MSG_BROADCAST, TE_EXPLOSION);\n\tWriteCoord (MSG_BROADCAST, self.origin_x);\n\tWriteCoord (MSG_BROADCAST, self.origin_y);\n\tWriteCoord (MSG_BROADCAST, self.origin_z);\n\tr = random ();\n\tif ((r < 0.3))\n\t{\n\t\tsound (self, CHAN_BODY, \"misc/exp1.wav\", 1, ATTN_NORM);\n\t}\n\tif ((r < 0.65))\n\t{\n\t\tsound (self, CHAN_BODY, \"misc/exp2.wav\", 1, ATTN_NORM);\n\t}\n\telse\n\t{\n\t\tsound (self, CHAN_BODY, \"misc/exp3.wav\", 1, ATTN_NORM);\n\t}\n\tT_RadiusDamage (self, self.owner, 50+random()*50, other, \"\");\n\tremove (self);\n};\n\n\nvoid () PlasmaExplode =\n{\n\tsound (self, CHAN_VOICE, \"ambience/gunfire7.wav\", 1, ATTN_NONE);\n\tself.origin = (self.origin + '0 0 16');\n\tself.velocity = VEC_ORIGIN;\n\tWriteByte (MSG_BROADCAST, SVC_TEMPENTITY);\n\tWriteByte (MSG_BROADCAST, TE_TAREXPLOSION);\n\tWriteCoord (MSG_BROADCAST, self.origin_x);\n\tWriteCoord (MSG_BROADCAST, self.origin_y);\n\tWriteCoord (MSG_BROADCAST, self.origin_z);\n\tsound (self, CHAN_BODY, \"misc/flash.wav\", 1, ATTN_NORM);\n\tT_RadiusDamage (self, self.owner, 80+random()*80, other, \"\");\n\tremove (self);\n};\n\nvoid () FlashExplode =\n{\n\tlocal entity te;\n\tlocal float dot;\n\tlocal vector vec;\n\n\tself.velocity = VEC_ORIGIN;\n\tsetmodel (self, \"progs/blast.mdl\");\n\tWriteByte (0, SVC_TEMPENTITY);\n\tWriteByte (0, WEAPON_SPIKES);\n\tWriteCoord (0, self.origin_x);\n\tWriteCoord (0, self.origin_y);\n\tWriteCoord (0, self.origin_z);\n\tsound (self, CHAN_BODY, \"misc/flash.wav\", 1, ATTN_NORM);\n\tte = findradius (self.origin, 1200);\n\twhile (te)\n\t{\n\t\tif (te.flags & FL_MONSTER && te.classname != te.owner.classname)\n\t\t\tte.attack_finished = time + 6;\n\n\t\tif ((te.classname == \"player\"))\n\t\t{\n/*\t\t\tmakevectors (te.angles);\n\t\t\tvec = normalize ((self.origin - te.origin));\n\t\t\tdot = (vec * v_forward);\n*/\n\t\t\tif (/*dot > 0.3 &&*/ CanDamage (self, te))\n\t\t\t{\n\t\t\t\tstuffcmd (te, \"v_cshift 255 255 255 255\\n\");\n\t\t\t\tstuffcmd (te, \"v_idlescale 10\\n\");\n\t\t\t\tif (self.owner.flags & FL_MONSTER)\n\t\t\t\t\tte.flash = time + 2;\n\t\t\t\telse\n\t\t\t\t\tte.flash = time + 6;\n\t\t\t}\n\t\t}\n\t\tte = te.chain;\n\t}\n\tremove (self);\n};\n\n/*\nvoid () HandGrenExplode =\n{\n\tif ((self.cnt == 0))\n\t{\n\t\tFragExplode ();\n\t}\n\telse\n\t{\n\t\tif ((self.cnt == 1))\n\t\t{\n\t\t\tEMPExplode ();\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif ((self.cnt == 2))\n\t\t\t{\n\t\t\t\tself.nextthink = (time + 0.5);\n\t\t\t\tself.think = SmokeThink;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tif ((self.cnt == AS_MELEE))\n\t\t\t\t{\n\t\t\t\t\tFlashExplode ();\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tif ((self.cnt == WEAPON_SPIKES))\n\t\t\t\t\t{\n\t\t\t\t\t\tPlasmaExplode ();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n};\n*/\n\nvoid () HandGrenBounce =\n{\n\tlocal float r;\n\n\tr = (random () * TE_LIGHTNING3);\n\n\tself.velocity = self.velocity * 0.75;\n\n\tif ((r < AS_MELEE))\n\t{\n\t\tsound (self, CHAN_VOICE, \"misc/bounce_1.wav\", 0.9, ATTN_NORM);\n\t}\n\telse\n\t{\n\t\tif ((r < TE_LIGHTNING2))\n\t\t{\n\t\t\tsound (self, CHAN_VOICE, \"misc/bounce_2.wav\", 0.9, ATTN_NORM);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tsound (self, CHAN_VOICE, \"misc/bounce_3.wav\", 0.9, ATTN_NORM);\n\t\t}\n\t}\n};\n\n\nvoid () FireHandGrenade =\n{\n\tlocal float item, iid;\n\tlocal float amcount;\n\n\titem = ItemInSlot(self, self.current_slot);\n\tiid = ToIID(item);\n\tamcount = ToStatus(item);\n\tif (iid == 0)\n\t\treturn;\n\t\n\tself.currentammo = amcount-1;\n\tif (amcount <= 1)\n\t\tSetItemSlot(self, self.current_slot, 0);\n\telse\n\t\tSetItemSlot(self, self.current_slot, SlotVal(iid, self.currentammo));\n\n\n\n\n\tmsg_entity = self;\n\tWriteByte (MSG_ONE, SVC_SMALLKICK);\n\tnewmis = spawn ();\n\tnewmis.owner = self;\n\tnewmis.movetype = MOVETYPE_BOUNCE;\n\tnewmis.solid = SOLID_BBOX;\n\tnewmis.classname = \"grenade\";\n\n\tnewmis.skin = 0;\n\tmakevectors (self.v_angle);\n\tnewmis.velocity = aim (self, 800);\n\tnewmis.velocity = (newmis.velocity * 800);\n\tnewmis.velocity_z = (newmis.velocity_z + 200);\n\tnewmis.angles = vectoangles (newmis.velocity);\n\tnewmis.avelocity_x = (random () * 300);\n\tnewmis.avelocity_y = (random () * 300);\n\tnewmis.avelocity_z = (random () * 300);\n\tnewmis.touch = HandGrenBounce;\n\tnewmis.nextthink = (time + 2.5);\n\n\tif (iid == IID_GREN_FRAG)\n\t\tnewmis.think = FragExplode;\n\telse if (iid == IID_GREN_EMP)\n\t\tnewmis.think = EMPExplode;\n\telse if (iid == IID_GREN_SMOKE)\n\t\tnewmis.think = SmokeThink;\n\telse //if (iid == IID_GREN_FLASH)\n\t\tnewmis.think = FlashExplode;\n\n\tnewmis.frame = 1;\n\tsetmodel (newmis, \"progs/handgren.mdl\");\n\tsetsize (newmis, VEC_ORIGIN, VEC_ORIGIN);\n\tsetorigin (newmis, ((self.origin + (v_right * TE_BLOOD)) + (v_up * TE_BLOOD)));\n};\n\n\n\nvoid(float slot, float magazine) GiveAmmo =\n{\n\tlocal float wi;\n\tlocal float wt;\n\tlocal float at;\n\tlocal float as;\n\n\t//give ammo to the weapon in a slot\n\twi = ItemInSlot(self, slot);\n\twt = ToIID(wi);\n\tSetItemSlot(self, slot, SlotVal(wt, magazine));\n\n\tmagazine = magazine*4;\n\tif (magazine < 30)\n\t\tmagazine = 30;\t//be generous.\n\n\tat = WeaponAmmoType(wt);\n\t//make sure we have enough ammo\n\tas = SlotOfItem(self, at);\n\tif (as == 0)\n\t\tas = SlotOfItem(self, IID_NONE);\n\tif (as == 0)\n\t\treturn;\t//no free slots, so we can't give them ammo\n\tif (ToStatus(ItemInSlot(self, as)) < magazine)\n\t\tSetItemSlot(self, as, SlotVal(at, magazine));\n};\n\n\nvoid (float dam, float rec, string snd, float rng, float rate) FirePistol =\n{\n      local float weap, tmp, zdif, xdif, ydif, is_headshot;\n      local vector dir, source, targ, org, org2, org3, adjust;\n\n\tweap = ToIID(self.(SlotField(self.current_slot)));\n\n\tstuffcmd(self, \"-attack\\n\");\n\tsound (self, CHAN_WEAPON, snd, 1, ATTN_NORM);\n\tself.attack_finished = (time + rate);\n\n\n\tif (self.position == POS_STAND)\n\t\tplayer_single1 ();\n\tif (self.position >= POS_DUCK)\n\t\tplayer_single1_s ();\n\n\tif (self.position == 0)\n\t\tadjust = '0 0 0';\n\tif (self.position == 1)\n\t\tadjust = '0 0 -16';\n\tif (self.position == 2)\n\t\tadjust = '0 0 -32';\n\n\n\tDropAmmo ();\n\tmakevectors (self.v_angle);\n\tif (self.recoil >= 15)\n\t\tself.recoil = 15;\n\n\n\ttmp = 50;\n\n\tif (self.velocity != '0 0 0')\n\t\ttmp = 400;\n\n\ttmp = tmp + (40 * self.recoil);\n\n\tif (self.attack <= 3 && self.position == 1 && self.velocity_z == 0)\n\t\ttmp = (tmp * 0.75);\n\n\tif (self.attack <= 3 && self.position == 2 && self.velocity_z == 0)\n\t\ttmp = (tmp * 0.5);\n\n\tself.attack = self.attack + 1;\n\tself.recoil = self.recoil + 4;\n\n\tsource = self.origin + '0 0 22';\n\n\ttarg = self.origin + '0 0 22' + v_right*crandom()* tmp + v_up*crandom()*tmp;\n\n\ttraceline (source+adjust, targ+adjust+v_forward*4000, FALSE, self);\n\tif (trace_fraction == 1)\n\t\treturn;\n\n\torg3 = self.origin + v_forward*32 + '0 0 22';\n\n\torg = trace_endpos - v_forward * 1;\n\torg2 = trace_endpos + (v_forward * ((trace_ent.size_y / 2) + (trace_ent.size_x / 2)));\n\tif (trace_ent.takedamage)\n\t{\n\t\torg2 = (trace_endpos + (v_forward * ((trace_ent.size_y / 2) + (trace_ent.size_x / 2))));\n\t\tzdif = org_z - trace_ent.origin_z;\n\t\tydif = org2_y - trace_ent.origin_y;\n\t\txdif = org2_x - trace_ent.origin_x;\n\n\n\t\tis_headshot = 0;\n\n\t\tif (((ydif >= CONTENT_SKY) && (ydif <= TE_LIGHTNING2)))\n\t\t\tis_headshot = 1;\n\t\tif (((xdif >= CONTENT_SKY) && (xdif <= TE_LIGHTNING2)))\n\t\t\tis_headshot = 1;\n\t\tif (self.attack <= 5 && is_headshot == 1 && zdif >= (trace_ent.size_z / 2 * 0.8))\n\t\t\tself.critical = 3;\n\n\t\tdam = (dam * (1 - trace_fraction));\n\t\tif (trace_ent.solid != SOLID_BSP)\n\t\t\tSpawnBlood (org, 1);\n\n\t\tif (trace_ent.solid == SOLID_BSP)\n\t\t\tSpawnWood (trace_ent, org, 1);\n\n\t\tdam = dam + random()*dam;\n\t\tif (weap == IID_WP_NEEDLER)\n\t\t\tX_Damage (trace_ent, self, self, dam);\n\t\telse\n\t\t\tT_Damage (trace_ent, self, self, dam);\n\n\t\tself.critical = 0;\n\t}\n\telse\n\t{\n\t\tbullet_hole (org);\n\t\tdir = vectoangles (source - targ);\n\t\treturn;\n\t}\n};\n\nvoid (float dam, float rec, string snd, float rng, float rate) FireSMG =\n{\n      local float weap, tmp, zdif, xdif, ydif, is_headshot;\n      local vector dir, source, targ, org, org2, adjust;\n\n\tweap = ToIID(self.(SlotField(self.current_slot)));\n\n\tif (weap != IID_WP_MP9)\n\t\tsound (self, CHAN_WEAPON, snd, 1, ATTN_NORM);\n\tif (weap == IID_WP_MP9 && self.attack == 0)\n\t\tsound (self, CHAN_WEAPON, snd, 1, ATTN_NORM);\n\tif (weap == IID_WP_MP9 && self.attack == 6)\n\t{\n\t\tsound (self, CHAN_WEAPON, snd, 1, ATTN_NORM);\n\t\tself.attack = 1;\n\t}\n\n\tself.attack_finished = (time + rate);\n\n\n\tif (self.position == POS_STAND)\n\t\tautofire ();\n\tif (self.position >= POS_DUCK)\n\t\tautofire_s ();\n\n\tif (self.position == 0)\n\t\tadjust = '0 0 0';\n\tif (self.position == 1)\n\t\tadjust = '0 0 -16';\n\tif (self.position == 2)\n\t\tadjust = '0 0 -32';\n\n\n\tDropAmmo ();\n\tmakevectors (self.v_angle);\n\tif (self.recoil >= 15)\n\t\tself.recoil = 15;\n\n\ttmp = 200;\n\n\tif (self.velocity != '0 0 0')\n\t\ttmp = 200;\n\n\ttmp = tmp + (40 * self.recoil);\n\n\tif (self.attack <= 3 && self.position == 1 && self.velocity_z == 0)\n\t\ttmp = (tmp * 0.75);\n\n\tif (self.attack <= 3 && self.position == 2 && self.velocity_z == 0)\n\t\ttmp = (tmp * 0.5);\n\n\tself.attack = self.attack + 1;\n\tself.recoil = self.recoil + 3;\n\n\tsource = self.origin + '0 0 22';\n\n\ttarg = self.origin + '0 0 22' + v_right*crandom()* tmp + v_up*crandom()*tmp;\n\n\ttraceline (source+adjust, targ+adjust+v_forward*4000, FALSE, self);\n\tif (trace_fraction == 1)\n\t\treturn;\n\n\torg = trace_endpos - v_forward * 2;\n\torg2 = trace_endpos + (v_forward * ((trace_ent.size_y / 2) + (trace_ent.size_x / 2)));\n\tif (trace_ent.takedamage)\n\t{\n\t\torg2 = (trace_endpos + (v_forward * ((trace_ent.size_y / 2) + (trace_ent.size_x / 2))));\n\t\tzdif = org_z - trace_ent.origin_z;\n\t\tydif = org2_y - trace_ent.origin_y;\n\t\txdif = org2_x - trace_ent.origin_x;\n\t\tis_headshot = 0;\n\t\tif (((ydif >= CONTENT_SKY) && (ydif <= TE_LIGHTNING2)))\n\t\t\tis_headshot = 1;\n\t\tif (((xdif >= CONTENT_SKY) && (xdif <= TE_LIGHTNING2)))\n\t\t\tis_headshot = 1;\n\t\tif (self.attack <= 5 && is_headshot == 1 && zdif >= (trace_ent.size_z / 2 * 0.8))\n\t\t\tself.critical = 3;\n\n\t\tdam = (dam * (1 - trace_fraction));\n\t\tif (trace_ent.solid != SOLID_BSP)\n\t\t\tSpawnBlood (org, 1);\n\n\t\tif (trace_ent.solid == SOLID_BSP)\n\t\t\tSpawnWood (trace_ent, org, 1);\n\n\t\tdam = dam + random()*dam;\n\t\tT_Damage (trace_ent, self, self, dam);\n\t\tself.critical = 0;\n\n\t\tif (trace_ent.solid == SOLID_BSP)\n\t\t\tpenetrate (org, (dam / 2), (dam / 2));\n\t}\n\telse\n\t{\n\t\tbullet_hole (org);\n\t\tdir = vectoangles (source - targ);\n\t\tpenetrate (org, (dam / 2), (dam / 2));\n\t\treturn;\n\t}\n};\n\nvoid (float dam, float rec, string snd, float rng, float rate) FireAssaultRifle =\n{\n      local float weap, hs, tmp, zdif, is_headshot, z;\n      local vector dir, source, targ, org, adjust, headshot_check;\n\n\tweap = ToIID(self.(SlotField(self.current_slot)));\n\n\tsound (self, CHAN_WEAPON, snd, 1, ATTN_NORM);\n\n\tself.attack_finished = (time + rate);\n\n\tif (self.position == 0)\n\t\tadjust = '0 0 0';\n\tif (self.position == 1)\n\t\tadjust = '0 0 -16';\n\tif (self.position == 2)\n\t\tadjust = '0 0 -32';\n\n\tDropAmmo ();\n\tmakevectors (self.v_angle);\n\tif (self.recoil >= 15)\n\t\tself.recoil = 15;\n\n\tif (self.attack == 0)\n\t{\n\t\tif (self.position == 0)\n\t\t\tplayer_single1 ();\n\t\tif (self.position == 1)\n\t\t\tplayer_single1_s ();\n\t\tif (self.position == 2)\n\t\t\tplayer_single1 ();\n\t}\n\tif (self.attack >= 1)\n\t{\n\t\tif (self.position == 0)\n\t\t\tautofire ();\n\t\tif (self.position == 1)\n\t\t\tautofire_s ();\n\t\tif (self.position == 2)\n\t\t\tplayer_single1 ();\n\t}\n\n\ttmp = 50;\n\n\tif (self.velocity_y < 0)\n\t\tz = z + (self.velocity_y*-1);\n\telse if (self.velocity_y > 0)\n\t\tz = z + (self.velocity_y);\n\n\tif (self.velocity_x < 0)\n\t\tz = z + (self.velocity_x*-1);\n\telse if (self.velocity_x > 0)\n\t\tz = z + (self.velocity_x);\n\n\ttmp = tmp + z;\n\ttmp = tmp + (40 * self.recoil);\n\n\tif (self.attack <= 3 && self.position == 1 && self.velocity_z == 0)\n\t\ttmp = (tmp * 0.75);\n\n\tif (self.attack <= 3 && self.position == 2 && self.velocity_z == 0)\n\t\ttmp = (tmp * 0.5);\n\n\tself.attack = self.attack + 1;\n\tself.recoil = self.recoil + 5;\n\n\tsource = self.origin + '0 0 22';\n\n\ttarg = self.origin + '0 0 22' + v_right*crandom()* tmp + v_up*crandom()*tmp;\n\n\ttraceline (source+adjust, targ+adjust+v_forward*4000, FALSE, self);\n\tif (trace_fraction == 1)\n\t\treturn;\n\n\torg = trace_endpos - v_forward * 2;\n\theadshot_check = trace_endpos + v_forward * (trace_ent.size_x / 2);\n\n\tif (trace_ent.takedamage)\n\t{\n\n\n\t\tis_headshot = 0;\n\t\ths = headshot_check_x - trace_ent.origin_x;\n\t\ths = hs + headshot_check_y - trace_ent.origin_y;\n\n\t\tif (hs < 0)\n\t\t\ths = hs * -1;\n\n\t\tif (hs <= 4)\n\t\t\tis_headshot = 1;\n\n\t\tzdif = trace_endpos_z - trace_ent.origin_z;\n\n\t\tif (is_headshot == 1 && zdif >= (trace_ent.size_z / 2 * 0.8))\n\t\t\tself.critical = 3;\n\n\n\n\t\tdam = (dam * (1 - trace_fraction));\n\t\tif (trace_ent.solid != SOLID_BSP && self.critical == 3)\n\t\t\tSpawnBlood (org, 1);\n\n\t\tif (trace_ent.solid == SOLID_BSP)\n\t\t\tSpawnWood (trace_ent, org, 1);\n\n\t\tdam = dam + random()*dam;\n\n\t\tif (weap != IID_WP_DKS1 && weap != IID_WP_FNFAL)\n\t\t\tT_Damage (trace_ent, self, self, dam);\n\t\telse if (weap == IID_WP_DKS1 || weap == IID_WP_FNFAL)\n\t\t\tX_Damage (trace_ent, self, self, dam);\n\t\tself.critical = 0;\n\n\t\tif (trace_ent.solid == SOLID_BSP)\n\t\t\tpenetrate (org, (dam / 2), (dam / 2));\n\t}\n\telse\n\t{\n\t\tbullet_hole (org);\n\t\tdir = vectoangles (source - targ);\n\t\tpenetrate (org, (dam / 2), (dam / 2));\n\t\treturn;\n\t}\n};\n\n\nvoid () Screenshake =\n{\n\tlocal entity te;\n\n\tte = findradius (self.origin, 700);\n\twhile (te)\n\t{\n\t\tif (((te.classname == \"player\") && (te.ghost == 0)))\n\t\t{\n\t\t\tstuffcmd (te, \"v_iyaw_level 2\\n\");\n\t\t\tstuffcmd (te, \"v_iyaw_cycle 32\\n\");\n\t\t\tstuffcmd (te, \"v_ipitch_level 2\\n\");\n\t\t\tstuffcmd (te, \"v_ipitch_cycle 32\\n\");\n\t\t\tstuffcmd (te, \"v_iroll_level 2\\n\");\n\t\t\tstuffcmd (te, \"v_iroll_cycle 32\\n\");\n\t\t\tstuffcmd (te, \"v_idlescale 1\\n\");\n\t\t}\n\t\tte = te.chain;\n\t}\n};\n\nvoid () ScreenshakeSingle =\n{\n\tstuffcmd (self, \"v_iyaw_level 2\\n\");\n\tstuffcmd (self, \"v_iyaw_cycle 32\\n\");\n\tstuffcmd (self, \"v_ipitch_level 2\\n\");\n\tstuffcmd (self, \"v_ipitch_cycle 32\\n\");\n\tstuffcmd (self, \"v_iroll_level 2\\n\");\n\tstuffcmd (self, \"v_iroll_cycle 32\\n\");\n\tstuffcmd (self, \"v_idlescale 1\\n\");\n};\n\nvoid () ExplosionFrames =\n{\n\tself.avelocity = '300 300 250';\n\tself.nextthink = (time + 0.02);\n\tself.frame = (self.frame + 1);\n\tif (self.frame == 16)\n\t\tremove (self);\n};\n\nvoid (float input) Explosion =\n{\n\tlocal float r;\n\n\tself.effects = EF_DIMLIGHT;\n\tself.touch = SUB_Null;\n\tsetmodel (self, \"progs/blast.mdl\");\n\tr = random ();\n\tif ((r < 0.3))\n\t\tsound (self, CHAN_BODY, \"misc/exp1.wav\", 1, ATTN_NORM);\n\tif ((r < 0.65))\n\t\tsound (self, CHAN_BODY, \"misc/exp2.wav\", 1, ATTN_NORM);\n\telse\n\t\tsound (self, CHAN_BODY, \"misc/exp3.wav\", 1, ATTN_NORM);\n\n\tWriteByte (MSG_BROADCAST, SVC_TEMPENTITY);\n\tWriteByte (MSG_BROADCAST, TE_EXPLOSION);\n\tWriteCoord (MSG_BROADCAST, self.origin_x);\n\tWriteCoord (MSG_BROADCAST, self.origin_y);\n\tWriteCoord (MSG_BROADCAST, self.origin_z);\n\tScreenshake ();\n\tself.frame = AS_MELEE;\n\tself.velocity = VEC_ORIGIN;\n\tself.avelocity = '300 300 250';\n\tself.think = ExplosionFrames;\n\tself.nextthink = (time + 0.02);\n};\n\n\nvoid () WeaponTouch =\n{\n\tlocal float slotnum;\n\tif (other.classname != \"player\")\n\t\treturn;\n\n\tif (other.ghost != 0)\n\t\treturn;\n\n\tif (ItemInSlot(other, other.current_slot) == 0 && FitsInSlot(other.current_slot, ToIID(self.islot1)))\n\t\tslotnum = other.current_slot;\n\tif (!slotnum)\n\t\tslotnum = FindSuitableEmptySlot(other, ToIID(self.islot1));\n\tif (!slotnum)\n\t\treturn;\t//can't get it.\n\n\tsound (other, CHAN_BODY, \"misc/item1.wav\", 1, ATTN_NORM);\n\n\tSetItemSlot(other, slotnum, self.islot1);\n\n\tsprint(other, PRINT_MEDIUM, \"You pick up the \");\n\tsprint(other, PRINT_MEDIUM, GetItemName(ToIID(self.islot1)));\n\tsprint(other, PRINT_MEDIUM, \"\\n\");\n\tremove (self);\n\n\tself = other;\n\tif (self.current_slot == slotnum)\n\t\tW_SetCurrentAmmo ();\n};\n\nvoid (float slotnum, float snd, float force) DropFromSlot =\n{\n\tlocal float weap;\n\tlocal string mdel;\n\n\tif (self.attack_finished > time && !force)\n\t\treturn;\n\n\tif (slotnum == 0)\n\t\treturn;\n\n\tweap = ItemInSlot(self, slotnum);\n\tif (weap == 0)\n\t{\n\t\tsprint(self, PRINT_HIGH, \"Can't drop nothing\\n\");\n\t\treturn;\n\t}\n\n\tif (snd == 1)\n\t\tsound (self, CHAN_WEAPON, \"weapons/lock4.wav\", 1, ATTN_NORM);\n\n\tmakevectors (self.v_angle);\n\tnewmis = spawn ();\n\tnewmis.owner = self;\n\tnewmis.classname = \"dropped_weapon\";\n\tnewmis.movetype = MOVETYPE_TOSS;\n\tnewmis.solid = SOLID_TRIGGER;\n\tnewmis.flags = FL_ITEM;\t//item makes it bigger to player touches.\n\tnewmis.velocity = aim (self, 500);\n\tnewmis.velocity = (newmis.velocity * 500);\n\tnewmis.angles_y = (random () * 360);\n\n\tmdel = GetItemWModel(ToIID(weap));\n\n\tsetmodel (newmis, mdel);\n\tsetsize (newmis, '-2 -2 0', '2 2 1');\n\tmakevectors (self.v_angle);\n\ttraceline (self.origin, ((self.origin + (v_forward * IT_LIGHTNING)) + '0 0 32'), FALSE, self);\n\ttrace_endpos = (trace_endpos - (v_forward * WEAPON_SPIKES));\n\tsetorigin (newmis, trace_endpos);\n\tnewmis.origin_z = self.origin_z;\n\tnewmis.nextthink = (time + 180);\n\tnewmis.think = SUB_Remove;\n\tnewmis.touch = WeaponTouch;\n\n\tnewmis.islot1 = weap;\n\n\tSetItemSlot(self, slotnum, 0);\n\n\tif (self.attack_finished < time+0.6)\n\t\tself.attack_finished = time+0.6;\n\n\tif (self.current_slot == slotnum)\n\t\tSetWeaponModel ();\n\n\tif (self.equipment_slot == slotnum)\n\t\tself.equipment_slot = 0;\n\t\t\n};\n\nvoid (float slot) WeaponAmmo =\n{\n\tlocal float weap, amount;\n\n\tweap = ToIID(ItemInSlot(self, slot));\n\n\tamount = WeaponMagQuant(weap);\n\tGiveAmmo (slot, amount);\n};\n\nvoid() Crosshair =\n{\n\tlocal float r;\n\tlocal string new;\n\n\tr = 32 + (self.recoil*8);\n\tif (r > 256)\n\t\tr = 256;\n\tif (r < 0)\n\t\tr = 0;\n\n\tnew = ftos(r);\t\n\tstuffcmd(self, \"crosshairsize \");\n\tstuffcmd(self, new);\n\tstuffcmd(self, \"\\n\");\n\tr = 0.75 - (self.recoil*0.03);\n\tif (r <= 0.25)\n\t\tr = 0.25;\n\n\tnew = ftos(r);\t\n\tstuffcmd(self, \"crosshairalpha \");\n\tstuffcmd(self, new);\n\tstuffcmd(self, \"\\n\");\n};\n\nfloat (entity healer, entity saved) RevivePlayer =\n{\n\tlocal entity oself;\n\toself = self;\n\n\tself = saved;\n/*\tif (!walkmove(0, 0))\n\t{\n\t\t//too close?\n\t\treturn FALSE;\n\t}\n*/\n\tsaved.deadflag = DEAD_NO;\n\tsaved.takedamage = DAMAGE_AIM;\n\tsaved.movetype = MOVETYPE_WALK;\n\tsaved.solid = SOLID_NOT;\n\n\tself = oself;\n\n\tsaved.materialize = 200;\n\tsaved.ghost = 0;\n\tsaved.health = 2;\n\tsaved.air_finished = time + 10;\n\tsaved.view_ofs = '0 0 22';\n\tself = saved;\n\tplayer_run();\n\tself = oself;\n\tstuffcmd(saved, \"impulse 1\\n\");\n\tif (healer.classname == \"player\")\n\t{\n\t\tsprint (healer, PRINT_HIGH, \"you revive \");\n\t\tsprint (healer, PRINT_HIGH, trace_ent.netname);\n\t\tsprint (healer, PRINT_HIGH, \".\\n \");\n\t}\n\tsprint (saved, PRINT_HIGH, healer.netname);\n\tsprint (saved, PRINT_HIGH, \" saves you from death.\\n\");\n\tsaved.view2 = world;\n\n\tsaved.flags (-) FL_FINDABLE_NONSOLID;\n\n\treturn TRUE;\n};\n\nfloat(float iid) UseBoostingChem =\n{\n\tlocal vector source;\n\tlocal string x;\n\tlocal float duration;\n\n\tif (self.attack_finished > time)\n\t\treturn false;\n\n\tx = GetItemName (iid);\n\tif (iid == IID_CHEM_ADRENALINE)\n\t\tduration = 30+random()*30;\n\telse if (iid == IID_CHEM_PSYCHO)\n\t\tduration = 30+random()*30;\n\telse if (iid == IID_CHEM_BESERK)\n\t\tduration = 30+random()*30;\n\telse\n\t{\n\t\tsprint (self, PRINT_HIGH, \"Not a boosting chem\\n\");\n\t\treturn false;\n\t}\n\n\tself.attack_finished = time + 1;\n\n\tmakevectors (self.v_angle);\n\tsource = self.origin + '0 0 0';\n\ttraceline (source, source + v_forward*64, FALSE, self);\n\tif (trace_ent.classname == \"player\" && trace_ent.team == self.team)\n\t{\n\t\tif (trace_ent.health <= 0)\n\t\t\treturn false;\n\n\t\tif (trace_ent.rage >= 1)\n\t\t{\n\t\t\tsprint (self, 2, trace_ent.netname);\n\t\t\tsprint (self, PRINT_HIGH, \" is already affected.\\n\");\n\t\t\treturn false;\n\t\t}\n\t\tsprint (trace_ent, 2, self.netname);\n\t\tsprint (trace_ent, PRINT_HIGH, \" used a \");\n\t\tsprint (trace_ent, PRINT_HIGH, x);\n\t\tsprint (trace_ent, PRINT_HIGH, \" on you.\\n\");\n\t\tsound (trace_ent, CHAN_BODY, \"player/berserk.wav\", 1, ATTN_NORM);\n\t\ttrace_ent.rage = iid;\n\t\ttrace_ent.ragetime = duration;\n\t\treturn true;\n\t}\n\tif (self.health < self.max_health && self.rage == 0)\n\t{\n\t\tsound (self, CHAN_BODY, \"player/berserk.wav\", 1, ATTN_NORM);\n\t\tself.rage = iid;\n\t\tself.ragetime = duration;\n\t\treturn true;\n\t}\n\treturn false;\n};\n\nfloat(float iid) UseHealingChem =\n{\n\tlocal vector source;\n\tlocal float heal;\n\tlocal string x;\n\tlocal float friendly;\n\n\n\tif (self.attack_finished > time)\n\t\treturn false;\n\n\tx = GetItemName (iid);\n\tif (iid == IID_CHEM_STIMPACK)\n\t\theal = 2*5;\n\telse if (iid == IID_CHEM_MEDICALBAG)\n\t{\n\t\theal = 3*5;\n\t\tx = \"bandage\";\n\t}\n\telse if (iid == IID_CHEM_SUPERSTIM)\n\t\theal = 4*5;\n\telse\n\t{\n\t\tsprint (self, PRINT_HIGH, \"Not a healing chem\\n\");\n\t\treturn false;\n\t}\n\t\n\n\tself.attack_finished = time + 1;\n\n\tmakevectors (self.v_angle);\n\tsource = self.origin + '0 0 0';\n\ttraceline (source, source + v_forward*64, 32, self);\n\tif (trace_ent.classname == \"player\" && trace_ent.team == self.team)\n\t{\n\t\tif (coop)\n\t\t\tfriendly = true;\t//all players are friendly in coop\n\t\tif (teamplay)\n\t\t\tfriendly = trace_ent.team == self.team;\n\t\tif (trace_ent.health <= 0 && friendly)\n\t\t{\n\t\t\treturn RevivePlayer(self, trace_ent);\n\t\t}\n\t\tif (!friendly)\n\t\t\treturn false;\n\n\t\tif (trace_ent.regen >= 1)\n\t\t{\n\t\t\tsprint (self, PRINT_HIGH, trace_ent.netname);\n\t\t\tsprint (self, PRINT_HIGH, \" is already healing.\\n\");\n\t\t\treturn false;\n\t\t}\n\t\tif (trace_ent.health >= trace_ent.max_health)\n\t\t{\n\t\t\tsprint(self, PRINT_HIGH, trace_ent.netname);\n\t\t\tsprint(self, PRINT_HIGH, \" isn't injured.\\n\");\n\t\t\treturn false;\n\t\t}\n\t\tsprint (trace_ent, PRINT_HIGH, self.netname);\n\t\tsprint (trace_ent, PRINT_HIGH, \" used a \");\n\t\tsprint (trace_ent, PRINT_HIGH, x);\n\t\tsprint (trace_ent, PRINT_HIGH, \" to heal you.\\n\");\n\n\t\ttrace_ent.regen = heal;\n\t\ttrace_ent.health = trace_ent.health + heal;\n\t\tstuffcmd (trace_ent, \"v_cshift 0 0 0 0\\n\");\n\t\tsound (self, CHAN_BODY, \"items/r_item2.wav\", 1, ATTN_NORM);\n\t\treturn true;\n\t}\n\tif (self.health < self.max_health && self.regen == 0)\n\t{\n\t\tsound (self, CHAN_BODY, \"items/r_item2.wav\", 1, ATTN_NORM);\n\t\tself.health = self.health + heal;\n\t\tself.regen = heal;\n\t\treturn true;\n\t}\n\treturn false;\n};\n\nvoid () DisplayMenu =\n{\n\tlocal string menu;\n\n\tif (self.currentmenu == \"none\")\n\t\treturn;\n\n\tif (self.currentmenu == \"shop_list\")\n\t{\n\t\tmenu = ShopString ();\n\t\tcenterprint (self, menu);\n\t}\n\tif (self.currentmenu == \"shop_trait\")\n\t{\n\t\tmenu = TraitString ();\n\t\tcenterprint (self, menu);\n\t}\n\tif (self.currentmenu == \"shop_perks\")\n\t{\n\t\tmenu = PerkString ();\n\t\tcenterprint (self, menu);\n\t}\n\tif (self.currentmenu == \"shop_armor\")\n\t{\n\t\tmenu = ArmorString ();\n\t\tcenterprint (self, menu);\n\t}\n\tif (self.currentmenu == \"shop_protect\")\n\t{\n\t\tmenu = ProtectString ();\n\t\tcenterprint (self, menu);\n\t}\n\tif (self.currentmenu == \"shop_melee\")\n\t{\n\t\tmenu = MeleeString ();\n\t\tcenterprint (self, menu);\n\t}\n\tif (self.currentmenu == \"shop_thrown\")\n\t{\n\t\tmenu = ThrownString ();\n\t\tcenterprint (self, menu);\n\t}\n\tif (self.currentmenu == \"shop_pistols\")\n\t{\n\t\tmenu = PistolString ();\n\t\tcenterprint (self, menu);\n\t}\n\tif (self.currentmenu == \"shop_shotguns\")\n\t{\n\t\tmenu = ShotgunString ();\n\t\tcenterprint (self, menu);\n\t}\n\tif (self.currentmenu == \"shop_rifles\")\n\t{\n\t\tmenu = RifleString ();\n\t\tcenterprint (self, menu);\n\t}\n\tif (self.currentmenu == \"shop_heavy\")\n\t{\n\t\tmenu = HeavyString ();\n\t\tcenterprint (self, menu);\n\t}\n\tif (self.currentmenu == \"shop_chems\")\n\t{\n\t\tmenu = ChemString ();\n\t\tcenterprint (self, menu);\n\t}\n\tif (self.currentmenu == \"shop_other\")\n\t{\n\t\tmenu = OtherString ();\n\t\tcenterprint (self, menu);\n\t}\n\tif (self.currentmenu == \"shop_equipment\")\n\t{\n\t\tmenu = EquipmentString ();\n\t\tcenterprint (self, menu);\n\t}\n\tif (self.currentmenu == \"shop_weapons\")\n\t{\n\t\tmenu = WeaponString ();\n\t\tcenterprint (self, menu);\n\t}\n\tif (((self.currentmenu == \"select_skill\") && (self.team == 1)))\n\t{\n\t\tcenterprint (self, \"CHOOSE SKILL SET\\n\\n1 Medic    \\n2 Assassin \\n3 Soldier  \\n4 Scientist\\n\");\n\t}\n\tif (((self.currentmenu == \"select_skill\") && (self.team == 2)))\n\t{\n\t\tcenterprint (self, \"CHOOSE SKILL SET\\n\\n1 Medic    \\n2 Assassin \\n3 Soldier  \\n4 Scientist\\n\");\n\t}\n\tif ((self.currentmenu == \"select_team\"))\n\t{\n\t\tif (self.class == 0)\n\t\t\tcenterprint (self, \"CHOOSE YOUR TEAM\\n\\n1 Rangers     (good)\\n2 Raiders     (evil)\\n3 Auto-Assign       \\n\");\n\t\tif (self.class >= 0 && self.oldteam == 0)\n\t\t\tcenterprint (self, \"CHOOSE YOUR TEAM\\n\\n1 Rangers     (good)\\n2 Raiders     (evil)\\n3 Auto-Assign       \\n\");\n\t\tif (self.class > 0 && self.oldteam > 0)\n\t\t\tcenterprint (self, \"CHOOSE YOUR TEAM\\n\\n1 Rangers     (good)\\n2 Raiders     (evil)\\n3 Auto-Assign       \\n4 Keep Previous     \\n\");\n\t}\n\tif (self.currentmenu == \"confirm_team\")\n\t{\n\t\tsound (self, CHAN_BODY, \"player/yourturn.wav\", 1, ATTN_NORM);\n\n\t\tif (self.team == 1)\n\t\t\tcenterprint (self, \"you will respawn as\\n\\nRanger - OK?\\n1 Yes     \\n2 No      \\n\");\n\t\tif (self.team == 2)\n\t\t\tcenterprint (self, \"you will respawn as\\n\\nRaider - OK?\\n1 Yes     \\n2 No      \\n\");\n\t}\n\tif (self.currentmenu == \"confirm_skill\")\n\t{\n\t\tsound (self, CHAN_BODY, \"player/yourturn.wav\", 1, ATTN_NORM);\n\n\t\tif (self.class == 1)\n\t\t\tcenterprint (self, \"your class will be\\n\\nMedic - OK?\\n1 Yes     \\n2 No      \\n\");\n\t\tif (self.class == 2)\n\t\t\tcenterprint (self, \"your class will be\\n\\nAssassin - OK?\\n1 Yes     \\n2 No      \\n\");\n\t\tif (self.class == 3)\n\t\t\tcenterprint (self, \"your class will be\\n\\nSoldiier - OK?\\n1 Yes     \\n2 No      \\n\");\n\t\tif (self.class == 4)\n\t\t\tcenterprint (self, \"your class will be\\n\\nScientist - OK?\\n1 Yes     \\n2 No      \\n\");\n\t}\n\n\tif (self.currentmenu == \"menu_build\")\n\t{\n\t\tmenu = BuildString ();\n\t\tcenterprint (self, menu);\n\t}\n};\n\n\nvoid () Special =\n{\n#if 0\n<<<< <<< weapons.qc\n/*\n==== ===\n\tif (self.class == 1)\n\t\tBandage ();\n>>>> >>> 1.2\n\tif (self.class == 2)\n<<< <<<< weapons.qc\n\t\tCmd_InvUse(GetItemName(IID_CHEM_MEDICALBAG));\n\tif (self.class == 3)\n\t\tCmd_InvUse(\"sneak-boy\");\n\tif (self.class == 4)\n\t\tCmd_InvUse(GetItemName(IID_GREN_FRAG));\n\tif (self.class == 6)\n\t\tCmd_InvUse(\"shield\");\n*/\n\n===== ==\n\t\tSneak ();\n\tif (self.class == 4)\n\t\tself.currentmenu = \"menu_build\";\n>>>> >>> 1.2\n#endif\n};\n\nvoid () hos_run1;\nvoid () hos_stand1;\nfloat (float iid) spawn_station;\n\nvoid () ExitScreen =\n{\n\tif (self.class == 0)\n\t\treturn;\n\n\tif (self.ghost == 1)\n\t\treturn;\n\n\tif (self.team == 0)\n\t\treturn;\n\n\tif (self.attack_finished > time)\n\t\treturn;\n\n\tif (trace_ent.classname == \"hostage\" && trace_ent.health > 0 && trace_fraction < 1)\n\t{\n\t\tif (self.team != 1)\n\t\t\treturn;\n\n\t\tif (self.currentmenu == \"menu_build\")\n\t\t\treturn;\n\n\t\tif (trace_ent.cnt == 0)\n\t\t{\n\t\t\tsprint (self, 2, \"hostage is now following you.\\n\");\n\t\t\ttrace_ent.nextthink = (time + 0.1);\n\t\t\ttrace_ent.think = hos_run1;\n\t\t\ttrace_ent.cnt = 1;\n\t\t\ttrace_ent.friend = self;\n\t\t\treturn;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (trace_ent.cnt == 1)\n\t\t\t{\n\t\t\t\tsprint (self, 2, \"hostage stopped following you.\\n\");\n\t\t\t\ttrace_ent.nextthink = (time + 0.1);\n\t\t\t\ttrace_ent.think = hos_stand1;\n\t\t\t\ttrace_ent.cnt = 0;\n\t\t\t\ttrace_ent.friend = trace_ent;\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\treturn;\n\t}\n\n\tif ((self.currentmenu != \"none\"))\n\t{\n\t\tcenterprint (self, \"\\n\");\n\t\tself.currentmenu = \"none\";\n\t}\n};\n\nvoid () Sneak =\n{\n\tlocal float w;\n\n\tw = weightx();\n\n\tif (self.sneak >= 1)\n\t{\n\t\tsound (self, CHAN_BODY, \"items/inv1.wav\", 1, ATTN_NORM);\n\t\tcenterprint (self, \" Uncloaked \\n\");\n\t\tsetmodel (self, \"progs/guy.mdl\");\n\t\tself.sneak = 0;\n\t\treturn;\n\t}\n\tif (self.ammo_cells < 20)\n\t{\n\t\tsprint (self, PRINT_HIGH, \"wait for stealth-boy to recharge.\\n\");\n\t\treturn;\n\t}\n\tif (self.sneak == 0 && self.class == 2)\n\t{\n\t\tsound (self, CHAN_BODY, \"items/inv1.wav\", 1, ATTN_NORM);\n\t\tcenterprint (self, \" cloaked and sneaking \\n(1% detection chance)\");\n\t\tself.sneak = 1;\n\t\treturn;\n\t}\n\telse if (self.sneak == 0 && self.class != 2)\n\t{\n\t\tsound (self, CHAN_BODY, \"items/inv1.wav\", 1, ATTN_NORM);\n\t\tcenterprint (self, \" cloaked \\n(15% detection chance)\");\n\t\tself.sneak = 3;\n\t\treturn;\n\t}\n\telse if (self.sneak == 0 && w > 20)\n\t{\n\t\tsound (self, CHAN_BODY, \"items/inv1.wav\", 1, ATTN_NORM);\n\t\tcenterprint (self, \" too much gear \\n\");\n\t\tsetmodel (self, \"progs/guy.mdl\");\n\t\tself.sneak = 0;\n\t\treturn;\n\t}\n};\n\nvoid () Shield =\n{\n\tif (self.class != 6)\n\t{\n\t\tcenterprint (self, \"You can't shield yourself!\\n\");\n\t\treturn;\n\t}\n\tif (self.sneak == 2)\n\t{\n\t\tcenterprint (self, \" Unshielded \\n\");\n\t\tself.sneak = 0;\n\t\tsound (self, CHAN_BODY, \"items/protect2.wav\", 1, ATTN_NORM);\n\t\treturn;\n\t}\n\tif (self.ammo_cells < 10)\n\t{\n\t\tcenterprint (self, \"wait for your shield to recharge.\\n\");\n\t\treturn;\n\t}\n\tif (self.sneak == 0)\n\t{\n\t\tcenterprint (self, \" Energy Shield \\n\");\n\t\tself.sneak = 2;\n\t\tsound (self, CHAN_BODY, \"items/protect.wav\", 1, ATTN_NORM);\n\t\treturn;\n\t}\n\n};\n\nvoid () station_die =\n{\n\tif ((self.buildtype == IID_BUILD_MRAMMO))\n\t{\n\t\tif ((self.team == 1))\n\t\t{\n\t\t\tblue_weapon = 0;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif ((self.team == 2))\n\t\t\t{\n\t\t\t\tred_weapon = 0;\n\t\t\t}\n\t\t}\n\t}\n\tif ((self.buildtype == IID_BUILD_SHIELDGEN))\n\t{\n\t\tif ((self.team == 1))\n\t\t{\n\t\t\tblue_armor = 0;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif ((self.team == 2))\n\t\t\t{\n\t\t\t\tred_armor = 0;\n\t\t\t}\n\t\t}\n\t}\n\tif ((self.buildtype == IID_BUILD_AUTODOC))\n\t{\n\t\tif ((self.team == 1))\n\t\t{\n\t\t\tblue_gadget = 0;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif ((self.team == 2))\n\t\t\t{\n\t\t\t\tred_gadget = 0;\n\t\t\t}\n\t\t}\n\t}\n\tExplosion (2);\n};\n\nvoid () station_think =\n{\n#define chemcount attack_finished\n\tlocal entity te;\n\tlocal float r;\n\n\tself.nextthink = time + 2;\n\n\tif (self.track.team != self.team)\n\t{\n\t\tstation_die ();\n\t\treturn;\n\t}\n\n\tif (self.chemcount <= 0)\n\t{\n\t\tstation_die ();\n\t\treturn;\n\t}\n\n\tif (self.buildtype == IID_BUILD_SHIELDGEN)//barricade\n\t{\n\t\tsound (self, CHAN_BODY, \"items/protect2.wav\", 1, ATTN_NORM);\n\n\t\tte = findradius (self.origin, 256);\n\t\twhile (te)\n\t\t{\n\t\t\tif (te.classname == \"player\" && te.team == self.team && !te.deadflag)\n\t\t\t{\n\t\t\t\tif (self.chemcount <= 0)\n\t\t\t\t{\n\t\t\t\t\tsound (self, CHAN_BODY, \"misc/menu2.wav\", TRUE, ATTN_NORM);\n\t\t\t\t\tsprint (te, 2, \"the shield generator is out of power.\\n\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (te.classname == \"player\")\n\t\t\t\t{\n\t\t\t\t\tte.protect = 3;\n\t\t\t\t\tself.chemcount = self.chemcount - 1;\n\t\t\t\t\tstuffcmd(te, \"v_cshift 0 100 100 100\\n\");\n\t\t\t\t}\n\t\t\t}\n\t\t\tte = te.chain;\n\t\t}\n\t}\n\tif (self.buildtype == IID_BUILD_AUTODOC)//autodoc\n\t{\n\t\tte = findradius (self.origin, 70);\n\t\twhile (te)\n\t\t{\n\t\t\tif (te.classname == \"player\" && te.team == self.team && !te.deadflag)\n\t\t\t{\n\t\t\t\tif (self.chemcount <= 0)\n\t\t\t\t{\n\t\t\t\t\tsound (self, CHAN_BODY, \"misc/item1.wav\", TRUE, ATTN_NORM);\n\t\t\t\t\tsprint (te, 2, \"the autodoc is out of medical supplies.\\n\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (te.deadflag)\n\t\t\t\t\tRevivePlayer(self, te);\n\t\t\t\telse if (te.health < te.max_health)\n\t\t\t\t{\n\t\t\t\t\tsound (self, CHAN_BODY, \"items/r_item2.wav\", 1, ATTN_NORM);\n\t\t\t\t\tsprint (te, PRINT_MEDIUM, \"the auto-doc heals you for 3 health.\\n\");\n\t\t\t\t\tte.health = te.health + 3;\n\t\t\t\t\tself.chemcount = self.chemcount - 1;\n\t\t\t\t\tif (te.health > te.max_health)\n\t\t\t\t\t\tte.health = te.max_health;\n\t\t\t\t}\n\t\t\t\tif (random() < 0.001)\n\t\t\t\t{\n\t\t\t\t\tr = random()*6;\n\t\t\t\t\tif (r < 1)\n\t\t\t\t\t\tsprint(te, PRINT_HIGH, \"the auto-doc heals you of AIDS\\n\");\n\t\t\t\t\telse if (r < 2)\n\t\t\t\t\t\tsprint(te, PRINT_HIGH, \"the auto-doc heals you of Genital Herpes\\n\");\n\t\t\t\t\telse if (r < 3)\n\t\t\t\t\t\tsprint(te, PRINT_HIGH, \"the auto-doc heals you of Gonorrhea\\n\");\n\t\t\t\t\telse if (r < 4)\n\t\t\t\t\t\tsprint(te, PRINT_HIGH, \"the auto-doc heals you of Syphillis\\n\");\n\t\t\t\t\telse if (r < 5)\n\t\t\t\t\t\tsprint(te, PRINT_HIGH, \"the auto-doc rids you of Crabs\\n\");\n\t\t\t\t\telse if (r < 6)\n\t\t\t\t\t\tsprint(te, PRINT_HIGH, \"the auto-doc heals you of Chlamydia\\n\");\n\t\t\t\t}\n\t\t\t}\n\t\t\tte = te.chain;\n\t\t}\n\t}\n\n\tif (self.buildtype == IID_BUILD_MRAMMO)//mr. ammo\n\t{\n\t\tte = findradius (self.origin, 60);\n\t\twhile (te)\n\t\t{\n\t\t\tif (te.classname == \"player\" && te.team == self.team && !te.deadflag)\n\t\t\t{\n\t\t\t\tif (self.chemcount <= 0)\n\t\t\t\t{\n\t\t\t\t\tsound (self, CHAN_BODY, \"misc/item1.wav\", TRUE, ATTN_NORM);\n\t\t\t\t\tsprint (te, 2, \"this mr.ammo is out of ammunition.\\n\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tlocal float x;\n\n\t\t\t\tx = 300;\n\n\t\t\t\tlocal float curweap;\n\t\t\t\tlocal float ammotype;\n\t\t\t\tlocal float ammocount;\n\t\t\t\tlocal float ammoslot;\n\t\t\t\tcurweap = ToIID(ItemInSlot(te, te.current_slot));\n\t\t\t\tammotype = WeaponAmmoType(curweap);\n\t\t\t\tif (ammotype)\n\t\t\t\t{\n\t\t\t\t\tammocount = TotalQuantity(te, ammotype);\n\t\t\t\t\tx = x - ammocount;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tx = 0;\n\t\t\t\tif (x > 0)\n\t\t\t\t{\n\t\t\t\t\tammocount = WeaponMagQuant(curweap);\n\t\t\t\t\tif (x > ammocount/2)\n\t\t\t\t\t\tx = ammocount/2;\n\n\t\t\t\t\tammoslot = SlotOfItem(te, ammotype);\n\t\t\t\t\tif (!ammoslot)\n\t\t\t\t\t\tammoslot = FindEmptySlot(te);\n\t\t\t\t\tif (ammoslot)\n\t\t\t\t\t\tSetItemSlot(te, ammoslot, SlotVal(ammotype, x + ToStatus(ItemInSlot(te, ammoslot))));\n\n\t\t\t\t\tr = random()*6;\n\t\t\t\t\tif (r < 1)\n\t\t\t\t\t\tsprint (te, PRINT_MEDIUM, ftos(ceil(x)), \" ammo was received from the mr.ammo.\\n\");\n\t\t\t\t\telse if (r < 2)\n\t\t\t\t\t\tsprint (te, PRINT_MEDIUM, ftos(ceil(x)), \" potatos were received from the mr.ammo.\\n\");\n\t\t\t\t\telse if (r < 3)\n\t\t\t\t\t\tsprint (te, PRINT_MEDIUM, ftos(ceil(x)), \" ammo was given by the mr.ammo.\\n\");\n\t\t\t\t\telse if (r < 4)\n\t\t\t\t\t\tsprint (te, PRINT_MEDIUM, ftos(ceil(x)), \" ammo was stolen from the mr.ammo.\\n\");\n\t\t\t\t\telse if (r < 5)\n\t\t\t\t\t\tsprint (te, PRINT_MEDIUM, ftos(ceil(x)), \" ammo was extracted from the mr.ammo.\\n\");\n\t\t\t\t\telse\n\t\t\t\t\t\tsprint (te, PRINT_MEDIUM, ftos(ceil(x)), \" ammo was given by the mr.ammo, along with an std\\n\");\n\n\t\t\t\t\tself.chemcount = self.chemcount - 1;\n\t\t\t\t}\n\t\t\t}\n\t\t\tte = te.chain;\n\t\t}\n\t}\n};\n\nvoid () Bar_Think =\n{\n\tlocal float dot1;\n\n\tif ((self.owner.health < WEAPON_SHOTGUN))\n\t{\n\t\tremove (self);\n\t\treturn;\n\t}\n\tif ((self.owner.health >= self.owner.max_health))\n\t{\n\t\tremove (self);\n\t\treturn;\n\t}\n\tself.flags = self.flags;\n\n\tif ((self.owner.position == WEAPON_SHOTGUN))\n\t{\n\t\tdot1 = WEAPON_SHOTGUN;\n\t}\n\tif ((dot1 == WEAPON_SHOTGUN))\n\t{\n\t\tself.frame = MULTICAST_ALL;\n\t\tsetmodel (self, \"\");\n\t\tself.nextthink = (time + 0.01);\n\t\treturn;\n\t}\n\tif (((self.owner.health >= WEAPON_SHOTGUN) && (dot1 == MULTICAST_ALL)))\n\t{\n\t\tself.frame = floor (((self.owner.health / self.owner.max_health) * TE_WIZSPIKE));\n\t\tsetorigin (self, (self.owner.origin + '0 0 48'));\n\t\tself.nextthink = (time + 0.01);\n\t\tsetmodel (self, \"progs/hbar.spr\");\n\t}\n};\n\nvoid (entity guy) spawn_dot =\n{\n\tlocal entity hologram;\n\n\thologram = spawn ();\n\thologram.movetype = MOVETYPE_NONE;\n\thologram.solid = SOLID_NOT;\n\thologram.owner = self;\n\tsetmodel (hologram, \"progs/hbar.spr\");\n\thologram.skin = self.skin;\n\tsetorigin (hologram, self.origin);\n\tsetsize (hologram, VEC_ORIGIN, VEC_ORIGIN);\n\thologram.angles = self.angles;\n\thologram.colormap = self.colormap;\n\thologram.cnt = MULTICAST_ALL;\n\thologram.think = Bar_Think;\n\thologram.nextthink = (time + 0.01);\n};\n\nvoid() FinishTurret;\nfloat (float iid) spawn_station =\n{\n\tlocal entity oself;\n\tlocal entity te;\n\tlocal vector org;\n\n\tif (iid == IID_BUILD_ROBOFANG)\n\t{\n\t\tte = find (world, classname, \"robofang\");\n\t\twhile (te)\n\t\t{\n\t\t\tif (te.track == self && te.buildtype == IID_BUILD_ROBOFANG)\n\t\t\t{\n\t\t\t\tmakevectors (self.v_angle);\n\t\t\t\tsetorigin(te, self.origin + v_forward*32);\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tte = find (te, classname, \"robofang\");\n\t\t}\n\t}\n\n\tte = find (world, classname, \"station\");\n\twhile (te)\n\t{\n\t\tif (te.track == self && te.buildtype == iid)\n\t\t{\n\t\t\tsprint (self, 2, \"already have one.\\n\");\n\t\t\treturn false;\n\t\t}\n\t\tte = find (te, classname, \"station\");\n\t}\n\tif (iid == IID_BUILD_ROBOFANG)\n\t{\n\t\t\tte = findradius (self.origin, 128);\n\t\t\twhile (te)\n\t\t\t{\n\t\t\t\tif (te != self && te.classname == \"player\" && te.health > 0)\n\t\t\t\t{\n\t\t\t\t\tsprint(self, PRINT_HIGH, \"not with other players nearby.\\n\");\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\tte = te.chain;\n\t\t\t}\n\t}\n\n\tmakevectors (self.v_angle);\n\torg = ((self.origin + (v_forward * IT_LIGHTNING)) + (v_up * EF_FLAG2));\n\tif ((pointcontents ((org + '0 20 0')) != CONTENT_EMPTY))\n\t{\n\t\tsprint (self, 2, \"can't build there.\\n\");\n\t\treturn false;\n\t}\n\tif ((pointcontents ((org + '0 -20 0')) != CONTENT_EMPTY))\n\t{\n\t\tsprint (self, 2, \"can't build there.\\n\");\n\t\treturn false;\n\t}\n\tif ((pointcontents ((org + '20 0 0')) != CONTENT_EMPTY))\n\t{\n\t\tsprint (self, 2, \"can't build there.\\n\");\n\t\treturn false;\n\t}\n\tif ((pointcontents ((org + '-20 0 0')) != CONTENT_EMPTY))\n\t{\n\t\tsprint (self, 2, \"can't build there.\\n\");\n\t\treturn false;\n\t}\n\tif ((pointcontents ((org + '0 0 50')) != CONTENT_EMPTY))\n\t{\n\t\tsprint (self, 2, \"can't build there.\\n\");\n\t\treturn false;\n\t}\n\tif ((pointcontents ((org + '0 0 -10')) != CONTENT_EMPTY))\n\t{\n\t\tsprint (self, 2, \"can't build there.\\n\");\n\t\treturn false;\n\t}\n\tself.impulse = 0;\n\tte = findradius (org, 40);\n\twhile (te)\n\t{\n\t\tif (te.classname == \"spawn1\")\n\t\t{\n\t\t\tsprint (self, 2, \"can't build at spawn.\\n\");\n\t\t\treturn false;\n\t\t}\n\t\tif (te.classname == \"spawn2\")\n\t\t{\n\t\t\tsprint (self, 2, \"can't build at spawn.\\n\");\n\t\t\treturn false;\n\t\t}\n\t\tif (te.classname == \"ghoul\")\n\t\t{\n\t\t\tsprint (self, 2, \"somethings in the way.\\n\");\n\t\t\treturn false;\n\t\t}\n\t\tif (((te.classname == \"player\") && (te.health > 0)))\n\t\t{\n\t\t\tsprint (self, 2, \"can't build on players.\\n\");\n\t\t\treturn false;\n\t\t}\n\t\tif (((te.classname == \"station\") && (te.health > 0)))\n\t\t{\n\t\t\tsprint (self, 2, \"can't build on other stations.\\n\");\n\t\t\treturn false;\n\t\t}\n\t\tte = te.chain;\n\t}\n\n\toself = self;\n\tself = spawn ();\n\tself.team = oself.team;\n\tself.track = oself;\n\n\tspawn_dot (self);\n\tself.origin = org;\n\tself.takedamage = DAMAGE_YES;\n\tself.solid = SOLID_SLIDEBOX;\n\tself.movetype = MOVETYPE_TOSS;\n\tsetsize (self, '-16 -16 0', '16 16 40');\n\tself.health = 30;\n\tself.max_health = 300;\n\tself.th_die = station_die;\n\tsetmodel (self, \"progs/station.mdl\");\n\tself.classname = \"station\";\n\tself.think = station_think;\n\tself.helmet = 2;\n\tself.buildtype = iid;\n\tself.aflag = true;\n\n\tself.frame = 4;\n\n\tself.chemcount = 75;\n\n\tif (self.buildtype == IID_BUILD_MRAMMO)\n\t\tself.netname = \"mr. ammo\";\n\tif (self.buildtype == IID_BUILD_SHIELDGEN)\n\t\tself.netname = \"shield generator\";\n\tif (self.buildtype == IID_BUILD_AUTODOC)\n\t\tself.netname = \"autodoc\";\n\tif (self.buildtype == IID_BUILD_ROBOFANG)\n\t{\n\t\tsetsize(self, '-24 -24 -24', '24 24 24');\n\t\tself.netname = \"robofang\";\n\t\tsetmodel (self, \"progs/dog.mdl\");\n\t}\n\tif (self.buildtype == IID_BUILD_TTURRET || self.buildtype == IID_BUILD_RTURRET || self.buildtype == IID_BUILD_GTURRET)\n\t{\n\t\tFinishTurret();\n\t}\n\n\tself = oself;\n\n\treturn true;\n};\n\nfloat() FireToolkit =\n{\n\tlocal float rr;\n\n\tmakevectors (self.v_angle);\n\ttraceline (self.origin, self.origin + v_forward*64, FALSE, self);\n\n\t//track is used for the owning player (without using the .owner field)\n\tif (trace_ent.track == self && trace_ent.classname == \"station\" && trace_ent.health > 0)\n\t{\n\t\tif (trace_ent.health >= trace_ent.max_health)\t//already built - don't whack it!\n\t\t\treturn true;\n\n\t\t//play a random build sound\n\t\trr = random ();\n\t\tif (rr < 0.3)\n\t\t\tsound (self, CHAN_WEAPON, \"misc/build1.wav\", TRUE, ATTN_NORM);\n\t\telse if (rr < 0.6)\n\t\t\tsound (self, CHAN_WEAPON, \"misc/build2.wav\", TRUE, ATTN_NORM);\n\t\telse\n\t\t\tsound (self, CHAN_WEAPON, \"misc/build3.wav\", TRUE, ATTN_NORM);\n\n\n\t\tif (!trace_ent.nextthink)\n\t\t\tif (trace_ent.health > 200)\n\t\t\t\ttrace_ent.frame = 5;\n\t\tself.attack_finished = time + 0.1 + (random () * 0.1);\n\t\ttrace_ent.health = trace_ent.health + 6;\n\t\t\n\n\t\tif (trace_ent.health >= trace_ent.max_health)\n\t\t{\t//it's built!\n\t\t\ttrace_ent.health = trace_ent.max_health;\n\t\t\tif (!trace_ent.aflag)\n\t\t\t\treturn true;\t//already complete\n\t\t\ttrace_ent.aflag = false;\n\n\t\t\ttrace_ent.nextthink = time + 1;\n\t\t\tsprint (self, PRINT_MEDIUM, \"BUILDING COMPLETED.\\n\");\n\n\n\t\t\tif (trace_ent.buildtype == IID_BUILD_ROBOFANG)\n\t\t\t{\n\t\t\t\ttrace_ent.solid = SOLID_NOT;\n\t\t\t\ttrace_ent.think = SUB_Remove;\n\t\t\t\ttrace_ent.nextthink = time;\n\t\t\t\tmakevectors (self.v_angle);\nsprint(self, PRINT_HIGH, \"Robofang appears to have malfunctioned\\n\");\n//\t\t\t\tspawn_dog (self.origin + v_forward * 32);\n\t\t\t}\n\t\t\tif (trace_ent.buildtype == IID_BUILD_AUTODOC)\n\t\t\t\ttrace_ent.frame = 1;\n\t\t\tif (trace_ent.buildtype == IID_BUILD_MRAMMO)\n\t\t\t\ttrace_ent.frame = 0;\n\t\t\tif (trace_ent.buildtype == IID_BUILD_SHIELDGEN)\n\t\t\t\ttrace_ent.frame = 2;\n\t\t}\n\t\treturn true;\n\t}\n\treturn false;\n};\n\nvoid () BuyMenu =\n{\n\tlocal float is_shop;\n\tlocal entity te;\n\tlocal string menu;\n\n\tif (self.currentmenu == \"777\")\n\t\treturn;\n\n\tis_shop = 0;\n\n\tif (coop == 0)\n\t{\n\t\tte = findradius (self.origin, 250);\n\t\twhile (te)\n\t\t{\n\t\t\tif (te.classname == \"buyzone1\" && self.team == 1)\n\t\t\t\tis_shop = 1;\n\t\t\tif (te.classname == \"buyzone2\" && self.team == 2)\n\t\t\t\tis_shop = 1;\n\n\t\t\tte = te.chain;\n\t\t}\n\t}\n\n\tif (coop == 1)\n\t{\n\n\t\tte = findradius (self.origin, 80);\n\t\twhile (te)\n\t\t{\n\t\t\tif (te.classname == \"buyzone\")\n\t\t\t\tis_shop = 1;\n\t\t\tif (te.classname == \"merchant\")\n\t\t\t\tis_shop = 1;\n\n\t\t\tte = te.chain;\n\t\t}\n\t}\n\n\tif ((is_shop == 0))\n\t{\n\t\tcenterprint (self, \"nothing but the afterglow, here.\\n(find a trader)\");\n\t\treturn;\n\t}\n\tif ((is_shop == 1))\n\t{\n\t\tmenu = ShopString ();\n\t\tcenterprint (self, menu);\n\t\tself.currentmenu = \"shop_list\";\n\n\t\tif (self.current_slot != 0 && self.current_slot != 2)\n\t\t\tself.current_slot = 1;\n\n\t\treturn;\n\t}\n};\n\n\n\nvoid () CharacterSheet =\n{\n\tlocal string x;\n\tlocal float qq;\n\tlocal float ratio;\n\tlocal float r1;\n\tlocal float r2;\n\n\tstuffcmd (self, \"toggleconsole\\n\");\n\tsprint (self, PRINT_HIGH, \"\\n\\n\\n\\n ** INFO ** \\n\");\n\tsprint (self, PRINT_HIGH, \"Class      \");\n\t\tif (self.class == 1)\n\t\t\tsprint (self, 2, \"Medic\");\n\t\tif (self.class == 2)\n\t\t\tsprint (self, 2, \"Assassin\");\n\t\tif (self.class == 3)\n\t\t\tsprint (self, 2, \"Soldier\");\n\t\tif (self.class == 4)\n\t\t\tsprint (self, 2, \"Scientist\");\n\n\tsprint (self, PRINT_HIGH, \"\\nTeam       \");\n\n\tif (self.team == 1)\n\t\tsprint (self, 2, \"Rangers\\n\");\n\tif (self.team == 2)\n\t\tsprint (self, 2, \"Raiders\\n\");\n\n\tsprint (self, PRINT_HIGH, \"Score      \");\n\tr1 = (self.dead);\n\tr2 = (self.kills);\n\tif (r1 == 0)\n\t\tr1 = 1;\n\n\tratio = (r2 / r1);\n\tx = ftos (ratio);\n\tsprint (self, 2, x);\n\tsprint (self, 2, \" (\");\n\tx = ftos (self.kills);\n\tsprint (self, 2, x);\n\tsprint (self, PRINT_HIGH, \"/\");\n\tx = ftos (self.dead);\n\tsprint (self, 2, x);\n\tsprint (self, 2, \")   \");\n\tsprint (self, PRINT_HIGH, \"\\nSpeed      \");\n\tx = ftos (self.maxspeed);\n\tsprint (self, 2, x);\n\tsprint (self, PRINT_HIGH, \"/300\\n\");\n\tsprint (self, PRINT_HIGH, \"Money      \");\n\n\tx = ftos (self.ammo_shells);\n\tsprint (self, 2, x);\n\tsprint (self, PRINT_HIGH, \"$\\n\");\n\tif (self.class == 2)\n\t{\n\t\tsprint (self, PRINT_HIGH, \"Bandages   \");\n\t\tx = ftos (self.bandages);\n\t\tsprint (self, 2, x);\n\t\tsprint (self, PRINT_HIGH, \"\\n\");\n\t}\n\tif (self.class == 6)\n\t{\n\t\tsprint (self, PRINT_HIGH, \"Scraps     \");\n\t\tx = ftos (self.scraps);\n\t\tsprint (self, 2, x);\n\t\tsprint (self, PRINT_HIGH, \"\\n\");\n\t}\n/*\n\tsprint (self, PRINT_HIGH, \"\\nArmor      \");\n\tx = GetArmorName();\n\tsprint (self, 2, x);\n\tsprint (self, 2, \"  (\");\n\tx = ftos (self.armor_weight);\n\tsprint (self, 2, x);\n\tsprint (self, 2, \")\\n\");\n*/\n\tsprint (self, PRINT_HIGH, \"Protective \");\n\tx = GetProtectName();\n\tsprint (self, 2, x);\n\tsprint (self, 2, \"\\n\");\n/*\n\tsprint (self, PRINT_HIGH, \"Chem       \");\n\tx = GetChemName();\n\tsprint (self, 2, x);\n\tx = ftos (self.chemcount);\n\tsprint (self, 2, \"[\");\n\tsprint (self, 2, x);\n\tsprint (self, 2, \"]\");\n\tsprint (self, 2, \"\\n\");\n*/\n\tsprint (self, PRINT_HIGH, \"Gadget     \");\n\n\tx = GetEquipmentName();\n\tsprint (self, 2, x);\n\tsprint (self, 2, \"\\n\");\n\tsprint (self, PRINT_HIGH, \"Perk       \");\n\tx = GetPerkName();\n\tsprint (self, 2, x);\n\tsprint (self, 2, \"\\n\");\n\tsprint (self, PRINT_HIGH, \"Trait      \");\n\tx = GetTraitName();\n\tsprint (self, 2, x);\n\tsprint (self, 2, \"\\n\");\n/*\n\tsprint (self, PRINT_HIGH, \"Weapon 1   \");\n\tx = GetWeaponName (self, self.slot1);\n\tsprint (self, 2, x);\n\tsprint (self, 2, \"  (\");\n\tx = ftos (self.slot1_weight);\n\tsprint (self, 2, x);\n\tsprint (self, 2, \") (\");\n\tsprint (self, 2, self.ammotype1);\n\tsprint (self, 2, \")\\n\");\n\n\tsprint (self, PRINT_HIGH, \"Weapon 2   \");\n\tx = GetWeaponName (self, self.slot2);\n\tsprint (self, 2, x);\n\tsprint (self, 2, \"  (\");\n\tx = ftos (self.slot2_weight);\n\tsprint (self, 2, x);\n\tsprint (self, 2, \") (\");\n\tsprint (self, 2, self.ammotype2);\n\tsprint (self, 2, \")\\n\\n\");\n*/\n\tqq = weightx ();\n\tsprint (self, PRINT_HIGH, \"Weight     \");\n\tx = ftos (qq);\n\tsprint (self, 2, x);\n\tqq = self.max_weight;\n\tsprint (self, PRINT_HIGH, \"/\");\n\tx = ftos (qq);\n\tsprint (self, 2, x);\n\tsprint (self, PRINT_HIGH, \"\\n\");\n\treturn;\n};\n\nvoid () UseEquipment =\n{\n\n\tif (self.equipment == 0)\n\t{\n\t\tcenterprint(self, \" no extra equipment \\n\");\n\t\treturn;\n\t}\n\tif (self.equipment == 1)\n\t{\n\t\tcenterprint(self, \" medic's bag \\nlets you carry more stimpacks\\n\");\n\t\treturn;\n\t}\n\tif (self.equipment == 4)\n\t{\n\t\tcenterprint(self, \" belt pouch \\ngives you room for two extra grenades\\n\");\n\t\treturn;\n\t}\n\tif (self.equipment == 5)\n\t{\n\t\tcenterprint(self, \" backpack \\nlets you carry more ammunition\\n\");\n\t\treturn;\n\t}\n\tif (self.equipment == 6)\n\t{\n\t\tcenterprint(self, \" toolkit mark ii \\nbuild, defuse and open doors faster\\n\");\n\t\treturn;\n\t}\n/*\n\tif (self.equipment == 7 && self.equipment_slot == 0)\n\t{\n\t\tsprint (self, PRINT_HIGH, \"climbing gear in place.\\n\");\n\t\tsound (self, CHAN_BODY, \"misc/item2.wav\", 1, ATTN_NORM);\n\t\tself.maxspeed = 100;\n\t\tself.velocity_z = 0;\n\t\tself.equipment_state = 1;\n\t}\n\tif (self.equipment == 7 && self.equipment_state == 1)\n\t{\n\t\tsprint (self, PRINT_HIGH, \"climbing gear retrieved.\\n\");\n\t\tsound (self, CHAN_BODY, \"misc/item2.wav\", 1, ATTN_NORM);\n\t\tself.grab = 0;\n\t\tself.equipment_state = 0;\n\t\treturn;\n\t}\n*/\n\tif (self.equipment == 8)\n\t{\n\t\tcenterprint(self, \" enhanced battery \\nallows longer cloaking\\n\");\n\t\treturn;\n\t}\n\tif (self.equipment == 9)\n\t{\n\t\tSneak();\n\t\treturn;\n\t}\n\n};\n\nvoid (vector s_aim, float dam, float tmp, float ran) W_FireBuckshotSpread1 =\n{\n\tlocal vector source;\n\tlocal vector targ;\n\tlocal vector org;\n\tlocal float zdif;\n\tlocal float ydif;\n\tlocal float xdif;\n\tlocal float is_headshot;\n\n\tmakevectors (self.v_angle);\n\n\tif (self.position == 0)\n\t\tsource = (self.origin + '0 0 20');\n\tif (self.position == 1)\n\t\tsource = (self.origin + '0 0 4');\n\tif (self.position == 2)\n\t\tsource = (self.origin + '0 0 -12');\n\n\ttarg = ((((((s_aim + ((v_right * random ()) * tmp)) - ((v_right * random ()) * tmp)) + ((v_up * random ()) * tmp)) - ((v_up * random ()) * tmp)) + (((v_up * random ()) * tmp) * 0.5)) + (v_forward * ran));\n\n\ttraceline (source, targ, FALSE, self);\n\tif ((trace_fraction == 1))\n\t{\n\t\treturn;\n\t}\n\torg = (trace_endpos - (v_forward * 2));\n\tif (trace_ent.takedamage)\n\t{\n\t\tdam = ((random () * dam) + dam);\n\t\tdam = (dam * (1 - trace_fraction));\n\n\t\tif (trace_ent.solid != SOLID_BSP)\n\t\t\tSpawnBlood (org, 1);\n\t\tif (trace_ent.solid == SOLID_BSP)\n\t\t\tSpawnWood (trace_ent, org, 1);\n\n\t\tzdif = (org_z - trace_ent.origin_z);\n\t\tydif = (org_y - trace_ent.origin_y);\n\t\txdif = (org_x - trace_ent.origin_x);\n\t\tis_headshot = 0;\n\t\tif (((ydif >= CONTENT_SLIME) && (ydif <= WEAPON_SPIKES)))\n\t\t{\n\t\t\tis_headshot = 1;\n\t\t}\n\t\tif (((xdif >= CONTENT_SLIME) && (xdif <= WEAPON_SPIKES)))\n\t\t{\n\t\t\tis_headshot = 1;\n\t\t}\n\t\tif (((is_headshot == 1) && (zdif >= ((trace_ent.size_z / 2) * 0.8))))\n\t\t{\n\t\t\tif (self.attack > 2)\n\t\t\t\tdam = (dam * 0.4);\n\n\t\t\tself.critical = 3;\n\t\t}\n\n\t\tdam = dam + random()*dam;\n\t\tT_Damage (trace_ent, self, self, dam);\n\t\tself.critical = 0;\n\t}\n\telse\n\t{\n\t\tbullet_hole (org);\n\t}\n};\n\nvoid (float rec, float number, float dam, float spread, float ran, float auto) W_FireShotgun =\n{\n\tlocal vector dir;\n\tlocal vector p_aim;\n\tlocal float var1;\n\tlocal float var2;\n\tlocal float var3;\n\tlocal float var4;\n\n\tif (self.velocity != '0 0 0')\n\t{\n\t\tvar1 = ((random () * 48) * (6 + self.recoil * 1.5));\n\t\tvar2 = ((random () * 48) * (6 + self.recoil * 1.5));\n\t\tvar3 = ((random () * 48) * (6 + self.recoil * 1.5));\n\t\tvar4 = ((random () * 48) * (6 + self.recoil * 1.5));\n\t}\n\tif (self.position == 2)\n\t{\n\t\tvar1 = ((random () * 12) * (6 + self.recoil * 1.5));\n\t\tvar2 = ((random () * 12) * (6 + self.recoil * 1.5));\n\t\tvar3 = ((random () * 12) * (6 + self.recoil * 1.5));\n\t\tvar4 = ((random () * 12) * (6 + self.recoil * 1.5));\n\t}\n\tif (self.position == 1)\n\t{\n\t\tvar1 = ((random () * IDLE3A) * (6 + self.recoil * 1.5));\n\t\tvar2 = ((random () * IDLE3A) * (6 + self.recoil * 1.5));\n\t\tvar3 = ((random () * IDLE3A) * (6 + self.recoil * 1.5));\n\t\tvar4 = ((random () * IDLE3A) * (6 + self.recoil * 1.5));\n\t}\n\tif (self.position == 0)\n\t{\n\t\tvar1 = ((random () * IDLE10A) * (6 + (self.recoil * 3)));\n\t\tvar2 = ((random () * IDLE10A) * (6 + (self.recoil * 3)));\n\t\tvar3 = ((random () * IDLE10A) * (6 + (self.recoil * 3)));\n\t\tvar4 = ((random () * IDLE10A) * (6 + (self.recoil * 3)));\n\t}\n\n\tif (self.position == 0)\n\t\tplayer_single1 ();\n\tif (self.position == 2)\n\t\tplayer_single1 ();\n\tif (self.position == 1)\n\t\tplayer_single1_s ();\n\n\tstuffcmd (self, \"+lookup\\n\");\n\tstuffcmd (self, \"wait\\n\");\n\tstuffcmd (self, \"-lookup\\n\");\n\n\tsound (self, CHAN_WEAPON, \"weapons/shotgun1.wav\", 1.5, ATTN_NORM);\n\n\t\n\tp_aim = (((((self.origin + '0 0 20') + (v_right * var1)) - (v_right * var2)) + (v_up * var3)) - (v_up * var4));\n\n\tmsg_entity = self;\n\tWriteByte (MSG_ONE, SVC_BIGKICK);\n\tDropAmmo ();\n\tself.attack = (self.attack + 1);\n\tself.recoil = (self.recoil + 8);\n\tCrosshair();\n\n\tif (self.recoil >= 30)\n\t\tself.recoil = 30;\n\n\tif (auto == 0)\n\t\tself.attack_finished = (time + 0.5);\n\n\tif (auto == 1)\n\t\tself.attack_finished = (time + 0.2);\n\n\tdir = aim (self, 10000);\n\tspread = 30;\n\tif ((number == MULTICAST_PVS_R))\n\t{\n\t\tW_FireBuckshotSpread1 (p_aim, dam, spread, ran);\n\t\tW_FireBuckshotSpread1 ((p_aim + (v_right * 80)), dam, spread, ran);\n\t\tW_FireBuckshotSpread1 ((p_aim - (v_right * 80)), dam, spread, ran);\n\t\tW_FireBuckshotSpread1 ((p_aim + (v_up * 80)), dam, spread, ran);\n\t\tW_FireBuckshotSpread1 ((p_aim - (v_up * 80)), dam, spread, ran);\n\t}\n\telse\n\t{\n\t\tif ((number == AS_MELEE))\n\t\t{\n\t\t\tW_FireBuckshotSpread1 (p_aim, dam, spread, ran);\n\t\t\tW_FireBuckshotSpread1 (p_aim, dam, spread, ran);\n\t\t\tW_FireBuckshotSpread1 (p_aim, dam, spread, ran);\n\t\t}\n\t}\n};\n\nvoid () PlasmaBolt =\n{\n\tlocal float dam;\n\tlocal float zdif;\n\tlocal float ydif;\n\tlocal float xdif;\n\tlocal float tru;\n\n\tif (other.solid == SOLID_TRIGGER)\n\t\treturn;\t//they're not really solid\n\n\tif (pointcontents (self.origin) == CONTENT_SKY)\n\t{\n\t\tremove (self);\n\t\treturn;\n\t}\n\n\tif (other.takedamage)\n\t{\n\t\tdam = 30 + (random () * 30);\n\n\t\tT_Damage (other, self, self.owner, dam);\n\t\tWriteByte (MSG_MULTICAST, SVC_TEMPENTITY);\n\t\tWriteByte (MSG_MULTICAST, TE_LIGHTNINGBLOOD);\n\t\tWriteCoord (MSG_MULTICAST, self.origin_x);\n\t\tWriteCoord (MSG_MULTICAST, self.origin_y);\n\t\tWriteCoord (MSG_MULTICAST, self.origin_z);\n\t\tmulticast (self.origin, MULTICAST_PHS);\n\t\tsound (self, CHAN_WEAPON, \"enforcer/enfstop.wav\", 1, ATTN_NORM);\n\t}\n\telse\n\t{\n\t\tWriteByte (MSG_MULTICAST, SVC_TEMPENTITY);\n\t\tWriteByte (MSG_MULTICAST, TE_LIGHTNINGBLOOD);\n\t\tWriteCoord (MSG_MULTICAST, self.origin_x);\n\t\tWriteCoord (MSG_MULTICAST, self.origin_y);\n\t\tWriteCoord (MSG_MULTICAST, self.origin_z);\n\t\tmulticast (self.origin, MULTICAST_PHS);\n\t\tsound (self, CHAN_WEAPON, \"enforcer/enfstop.wav\", 1, ATTN_NORM);\n\t}\n\tremove (self);\n};\n\n//the alien blaster\nvoid () FireAlienBlaster =\n{\n\tlocal float tmp;\n\n\tDropAmmo ();\n\tself.recoil = self.recoil + 8;\n\n\tmsg_entity = self;\n\tWriteByte (MSG_ONE, SVC_SMALLKICK);\n\n\tnewmis = spawn ();\n\tnewmis.owner = self;\n\tnewmis.movetype = MOVETYPE_FLYMISSILE;\n\tnewmis.solid = SOLID_BBOX;\n\tnewmis.effects = EF_DIMLIGHT;\n\tmakevectors (self.v_angle);\n\tnewmis.velocity = aim (self, 3000);\n\tnewmis.velocity = (newmis.velocity * 3000);\n\tnewmis.angles = vectoangles (newmis.velocity);\n\n\tnewmis.nextthink = (time + IDLE3A);\n\tnewmis.think = SUB_Remove;\n\tsetmodel (newmis, \"progs/plasma.mdl\");\n\ttmp = ((30 + self.velocity_y) + self.velocity_x);\n\n\n\t{\n\t\tnewmis.velocity = aim (self, 1700);\n\t\tnewmis.velocity = newmis.velocity * 1700;\n\t\tnewmis.angles = vectoangles (newmis.velocity);\n\t\tnewmis.nextthink = time + 1.4;\n\t\tsound (self, CHAN_WEAPON, \"weapons/blaster.wav\", WEAPON_SHOTGUN, ATTN_NORM);\n\t\tnewmis.touch = PlasmaBolt;\n\t\tsetmodel (newmis, \"progs/ray.mdl\");\n\t\ttmp = 90 + self.velocity_y + self.velocity_x;\n\t}\n\n\tsetsize (newmis, '0 0 0', '0 0 0');\n\tsetorigin (newmis, self.origin + (v_right * WEAPON_BIG) + (v_forward * WEAPON_ROCKET) + '0 0 20');\n\tnewmis.velocity = newmis.velocity + (v_right * random () * tmp) - (v_right * random () * tmp) + (v_up * random () * tmp) - (v_up * random () * tmp);\n\n\tself.attack_finished = time + 0.5;\n};\n"
  },
  {
    "path": "quakec/fallout2/wizard.qc",
    "content": "/*\n==============================================================================\n\nWIZARD\n\n==============================================================================\n*/\n\n$cd id1/models/a_wizard\n$origin 0 0 24\n$base wizbase\t\n$skin wizbase\n\n$frame hover1 hover2 hover3 hover4 hover5 hover6 hover7 hover8\n$frame hover9 hover10 hover11 hover12 hover13 hover14 hover15\n\n$frame fly1 fly2 fly3 fly4 fly5 fly6 fly7 fly8 fly9 fly10\n$frame fly11 fly12 fly13 fly14\n\n$frame magatt1 magatt2 magatt3 magatt4 magatt5 magatt6 magatt7\n$frame magatt8 magatt9 magatt10 magatt11 magatt12 magatt13\n\n$frame pain1 pain2 pain3 pain4\n\n$frame death1 death2 death3 death4 death5 death6 death7 death8\n\n/*\n==============================================================================\n\nWIZARD\n\nIf the player moves behind cover before the missile is launched, launch it\nat the last visible spot with no velocity leading, in hopes that the player\nwill duck back out and catch it.\n==============================================================================\n*/\n\n/*\n=============\nLaunchMissile\n\nSets the given entities velocity and angles so that it will hit self.enemy\nif self.enemy maintains it's current velocity\n0.1 is moderately accurate, 0.0 is totally accurate\n=============\n*/\nvoid(entity missile, float mspeed, float accuracy) LaunchMissile =\n{\n\tlocal\tvector\tvec, move;\n\tlocal\tfloat\tfly;\n\n\tmakevectors (self.angles);\n\t\t\n// set missile speed\n\tvec = self.enemy.origin + self.enemy.mins + self.enemy.size * 0.7 - missile.origin;\n\n// calc aproximate time for missile to reach vec\n\tfly = vlen (vec) / mspeed;\n\t\n// get the entities xy velocity\n\tmove = self.enemy.velocity;\n\tmove_z = 0;\n\n// project the target forward in time\n\tvec = vec + move * fly;\n\t\n\tvec = normalize(vec);\n\tvec = vec + accuracy*v_up*(random()- 0.5) + accuracy*v_right*(random()- 0.5);\n\t\n\tmissile.velocity = vec * mspeed;\n\n\tmissile.angles = '0 0 0';\n\tmissile.angles_y = vectoyaw(missile.velocity);\n\n// set missile duration\n\tmissile.nextthink = time + 5;\n\tmissile.think = SUB_Remove;\t\n};\n\n\nvoid() wiz_run1;\nvoid() wiz_side1;\n\n/*\n=================\nWizardCheckAttack\n=================\n*/\nfloat()\tWizardCheckAttack =\n{\n\tlocal vector\tspot1, spot2;\t\n\tlocal entity\ttarg;\n\tlocal float\t\tchance;\n\n\tif (time < self.attack_finished)\n\t\treturn FALSE;\n\tif (!enemy_vis)\n\t\treturn FALSE;\n\n\tif (enemy_range == RANGE_FAR)\n\t{\n\t\tif (self.attack_state != AS_STRAIGHT)\n\t\t{\n\t\t\tself.attack_state = AS_STRAIGHT;\n\t\t\twiz_run1 ();\n\t\t}\n\t\treturn FALSE;\n\t}\n\t\t\n\ttarg = self.enemy;\n\t\n// see if any entities are in the way of the shot\n\tspot1 = self.origin + self.view_ofs;\n\tspot2 = targ.origin + targ.view_ofs;\n\n\ttraceline (spot1, spot2, FALSE, self);\n\n\tif (trace_ent != targ)\n\t{\t// don't have a clear shot, so move to a side\n\t\tif (self.attack_state != AS_STRAIGHT)\n\t\t{\n\t\t\tself.attack_state = AS_STRAIGHT;\n\t\t\twiz_run1 ();\n\t\t}\n\t\treturn FALSE;\n\t}\n\t\t\t\n\tif (enemy_range == RANGE_MELEE)\n\t\tchance = 0.9;\n\telse if (enemy_range == RANGE_NEAR)\n\t\tchance = 0.6;\n\telse if (enemy_range == RANGE_MID)\n\t\tchance = 0.2;\n\telse\n\t\tchance = 0;\n\n\tif (random () < chance)\n\t{\n\t\tself.attack_state = AS_MISSILE;\n\t\treturn TRUE;\n\t}\n\n\tif (enemy_range == RANGE_MID)\n\t{\n\t\tif (self.attack_state != AS_STRAIGHT)\n\t\t{\n\t\t\tself.attack_state = AS_STRAIGHT;\n\t\t\twiz_run1 ();\n\t\t}\n\t}\n\telse\n\t{\n\t\tif (self.attack_state != AS_SLIDING)\n\t\t{\n\t\t\tself.attack_state = AS_SLIDING;\n\t\t\twiz_side1 ();\n\t\t}\n\t}\n\t\n\treturn FALSE;\n};\n\n/*\n=================\nWizardAttackFinished\n=================\n*/\nvoid()\tWizardAttackFinished =\n{\n\tif (enemy_range >= RANGE_MID || !enemy_vis)\n\t{\n\t\tself.attack_state = AS_STRAIGHT;\n\t\tself.think = wiz_run1;\n\t}\n\telse\n\t{\n\t\tself.attack_state = AS_SLIDING;\n\t\tself.think = wiz_side1;\n\t}\n};\n\n/*\n==============================================================================\n\nFAST ATTACKS\n\n==============================================================================\n*/\n\nvoid() Wiz_FastFire =\n{\n\tlocal vector\t\tvec;\n\tlocal vector\t\tdst;\n\n\tif (self.owner.health > 0)\n\t{\n\t\tmakevectors (self.enemy.angles);\t\n\t\tdst = self.enemy.origin - 13*self.movedir;\n\t\n\t\tvec = normalize(dst - self.origin);\n\t\tsound (self, CHAN_WEAPON, \"wizard/wattack.wav\", 1, ATTN_NORM);\n\t\tlaunch_spike (self.origin, vec);\n\t\tnewmis.velocity = vec*600;\n\t\tnewmis.owner = self.owner;\n\t\tnewmis.classname = \"wizspike\";\n\t\tsetmodel (newmis, \"progs/w_spike.mdl\");\n\t\tsetsize (newmis, VEC_ORIGIN, VEC_ORIGIN);\t\t\n\t}\n\n\tremove (self);\n};\n\n\nvoid() Wiz_StartFast =\n{\n\tlocal\tentity\tmissile;\n\t\n\tsound (self, CHAN_WEAPON, \"wizard/wattack.wav\", 1, ATTN_NORM);\n\tself.v_angle = self.angles;\n\tmakevectors (self.angles);\n\n\tmissile = spawn ();\n\tmissile.owner = self;\n\tmissile.nextthink = time + 0.6;\n\tsetsize (missile, '0 0 0', '0 0 0');\t\t\n\tsetorigin (missile, self.origin + '0 0 30' + v_forward*14 + v_right*14);\n\tmissile.enemy = self.enemy;\n\tmissile.nextthink = time + 0.8;\n\tmissile.think = Wiz_FastFire;\n\tmissile.movedir = v_right;\n\n\tmissile = spawn ();\n\tmissile.owner = self;\n\tmissile.nextthink = time + 1;\n\tsetsize (missile, '0 0 0', '0 0 0');\t\t\n\tsetorigin (missile, self.origin + '0 0 30' + v_forward*14 + v_right* -14);\n\tmissile.enemy = self.enemy;\n\tmissile.nextthink = time + 0.3;\n\tmissile.think = Wiz_FastFire;\n\tmissile.movedir = VEC_ORIGIN - v_right;\n};\n\n\n\nvoid() Wiz_idlesound =\n{\nlocal float wr;\n\twr = random() * 5;\n\n\tif (self.waitmin < time)\n\t{\n\t \tself.waitmin = time + 2;\n\t \tif (wr > 4.5) \n\t \t\tsound (self, CHAN_VOICE, \"wizard/widle1.wav\", 1,  ATTN_IDLE);\n\t \tif (wr < 1.5)\n\t \t\tsound (self, CHAN_VOICE, \"wizard/widle2.wav\", 1, ATTN_IDLE);\n\t}\n\treturn;\n};\n\nvoid()\twiz_stand1\t=[\t$hover1,\t\twiz_stand2\t] {ai_stand();};\nvoid()\twiz_stand2\t=[\t$hover2,\t\twiz_stand3\t] {ai_stand();};\nvoid()\twiz_stand3\t=[\t$hover3,\t\twiz_stand4\t] {ai_stand();};\nvoid()\twiz_stand4\t=[\t$hover4,\t\twiz_stand5\t] {ai_stand();};\nvoid()\twiz_stand5\t=[\t$hover5,\t\twiz_stand6\t] {ai_stand();};\nvoid()\twiz_stand6\t=[\t$hover6,\t\twiz_stand7\t] {ai_stand();};\nvoid()\twiz_stand7\t=[\t$hover7,\t\twiz_stand8\t] {ai_stand();};\nvoid()\twiz_stand8\t=[\t$hover8,\t\twiz_stand1\t] {ai_stand();};\n\nvoid()\twiz_walk1\t=[\t$hover1,\t\twiz_walk2\t] {ai_walk(8);\nWiz_idlesound();};\nvoid()\twiz_walk2\t=[\t$hover2,\t\twiz_walk3\t] {ai_walk(8);};\nvoid()\twiz_walk3\t=[\t$hover3,\t\twiz_walk4\t] {ai_walk(8);};\nvoid()\twiz_walk4\t=[\t$hover4,\t\twiz_walk5\t] {ai_walk(8);};\nvoid()\twiz_walk5\t=[\t$hover5,\t\twiz_walk6\t] {ai_walk(8);};\nvoid()\twiz_walk6\t=[\t$hover6,\t\twiz_walk7\t] {ai_walk(8);};\nvoid()\twiz_walk7\t=[\t$hover7,\t\twiz_walk8\t] {ai_walk(8);};\nvoid()\twiz_walk8\t=[\t$hover8,\t\twiz_walk1\t] {ai_walk(8);};\n\nvoid()\twiz_side1\t=[\t$hover1,\t\twiz_side2\t] {ai_run(8);\nWiz_idlesound();};\nvoid()\twiz_side2\t=[\t$hover2,\t\twiz_side3\t] {ai_run(8);};\nvoid()\twiz_side3\t=[\t$hover3,\t\twiz_side4\t] {ai_run(8);};\nvoid()\twiz_side4\t=[\t$hover4,\t\twiz_side5\t] {ai_run(8);};\nvoid()\twiz_side5\t=[\t$hover5,\t\twiz_side6\t] {ai_run(8);};\nvoid()\twiz_side6\t=[\t$hover6,\t\twiz_side7\t] {ai_run(8);};\nvoid()\twiz_side7\t=[\t$hover7,\t\twiz_side8\t] {ai_run(8);};\nvoid()\twiz_side8\t=[\t$hover8,\t\twiz_side1\t] {ai_run(8);};\n\nvoid()\twiz_run1\t=[\t$fly1,\t\twiz_run2\t] {ai_run(16);\nWiz_idlesound();\n};\nvoid()\twiz_run2\t=[\t$fly2,\t\twiz_run3\t] {ai_run(16);};\nvoid()\twiz_run3\t=[\t$fly3,\t\twiz_run4\t] {ai_run(16);};\nvoid()\twiz_run4\t=[\t$fly4,\t\twiz_run5\t] {ai_run(16);};\nvoid()\twiz_run5\t=[\t$fly5,\t\twiz_run6\t] {ai_run(16);};\nvoid()\twiz_run6\t=[\t$fly6,\t\twiz_run7\t] {ai_run(16);};\nvoid()\twiz_run7\t=[\t$fly7,\t\twiz_run8\t] {ai_run(16);};\nvoid()\twiz_run8\t=[\t$fly8,\t\twiz_run9\t] {ai_run(16);};\nvoid()\twiz_run9\t=[\t$fly9,\t\twiz_run10\t] {ai_run(16);};\nvoid()\twiz_run10\t=[\t$fly10,\t\twiz_run11\t] {ai_run(16);};\nvoid()\twiz_run11\t=[\t$fly11,\t\twiz_run12\t] {ai_run(16);};\nvoid()\twiz_run12\t=[\t$fly12,\t\twiz_run13\t] {ai_run(16);};\nvoid()\twiz_run13\t=[\t$fly13,\t\twiz_run14\t] {ai_run(16);};\nvoid()\twiz_run14\t=[\t$fly14,\t\twiz_run1\t] {ai_run(16);};\n\nvoid()\twiz_fast1\t=[\t$magatt1,\t\twiz_fast2\t] {ai_face();Wiz_StartFast();};\nvoid()\twiz_fast2\t=[\t$magatt2,\t\twiz_fast3\t] {ai_face();};\nvoid()\twiz_fast3\t=[\t$magatt3,\t\twiz_fast4\t] {ai_face();};\nvoid()\twiz_fast4\t=[\t$magatt4,\t\twiz_fast5\t] {ai_face();};\nvoid()\twiz_fast5\t=[\t$magatt5,\t\twiz_fast6\t] {ai_face();};\nvoid()\twiz_fast6\t=[\t$magatt6,\t\twiz_fast7\t] {ai_face();};\nvoid()\twiz_fast7\t=[\t$magatt5,\t\twiz_fast8\t] {ai_face();};\nvoid()\twiz_fast8\t=[\t$magatt4,\t\twiz_fast9\t] {ai_face();};\nvoid()\twiz_fast9\t=[\t$magatt3,\t\twiz_fast10\t] {ai_face();};\nvoid()\twiz_fast10\t=[\t$magatt2,\t\twiz_run1\t] {ai_face();SUB_AttackFinished(2);WizardAttackFinished ();};\n\nvoid()\twiz_pain1\t=[\t$pain1,\t\twiz_pain2\t] {};\nvoid()\twiz_pain2\t=[\t$pain2,\t\twiz_pain3\t] {};\nvoid()\twiz_pain3\t=[\t$pain3,\t\twiz_pain4\t] {};\nvoid()\twiz_pain4\t=[\t$pain4,\t\twiz_run1\t] {};\n\nvoid()\twiz_death1\t=[\t$death1,\t\twiz_death2\t] {\n\nself.velocity_x = -200 + 400*random();\nself.velocity_y = -200 + 400*random();\nself.velocity_z = 100 + 100*random();\nself.movetype = MOVETYPE_TOSS;\nsound (self, CHAN_VOICE, \"wizard/wdeath.wav\", 1, ATTN_NORM);\n};\nvoid()\twiz_death2\t=[\t$death2,\t\twiz_death3\t] {};\nvoid()\twiz_death3\t=[\t$death3,\t\twiz_death4\t]{self.solid = SOLID_NOT;};\nvoid()\twiz_death4\t=[\t$death4,\t\twiz_death5\t] {};\nvoid()\twiz_death5\t=[\t$death5,\t\twiz_death6\t] {};\nvoid()\twiz_death6\t=[\t$death6,\t\twiz_death7\t] {};\nvoid()\twiz_death7\t=[\t$death7,\t\twiz_death8\t] {};\nvoid()\twiz_death8\t=[\t$death8,\t\twiz_death8\t] {};\n\nvoid() wiz_die =\n{\n// check for gib\n\tif (self.health < -40)\n\t{\n\t\tsound (self, CHAN_VOICE, \"player/udeath.wav\", 1, ATTN_NORM);\n\t\tThrowHead (\"progs/h_wizard.mdl\", -10);\n\t\tThrowGib (\"progs/gib2.mdl\", -10);\n\t\tThrowGib (\"progs/gib2.mdl\", -10);\n\t\tThrowGib (\"progs/gib2.mdl\", -10);\n\t\tThrowGib (\"progs/gib3.mdl\", -10);\n\t\tThrowGib (\"progs/gib1.mdl\", -10);\n\t\treturn;\n\t}\n\n\twiz_death1 ();\n};\n\n\nvoid(entity attacker, float damage) Wiz_Pain =\n{\n\tsound (self, CHAN_VOICE, \"wizard/wpain.wav\", 1, ATTN_NORM);\n\tif (random()*70 > damage)\n\t\treturn;\t\t// didn't flinch\n\n\twiz_pain1 ();\n};\n\n\nvoid() Wiz_Missile =\n{\n\twiz_fast1();\n};\n\nvoid() monster_wizard =\n{\n\tprecache_model (\"progs/wizard.mdl\");\n\tprecache_model (\"progs/h_wizard.mdl\");\n\tprecache_model (\"progs/w_spike.mdl\");\n\n\tprecache_sound (\"wizard/hit.wav\");\t\t// used by c code\n\tprecache_sound (\"wizard/wattack.wav\");\n\tprecache_sound (\"wizard/wdeath.wav\");\n\tprecache_sound (\"wizard/widle1.wav\");\n\tprecache_sound (\"wizard/widle2.wav\");\n\tprecache_sound (\"wizard/wpain.wav\");\n\tprecache_sound (\"wizard/wsight.wav\");\n\n\tself.solid = SOLID_SLIDEBOX;\n\tself.movetype = MOVETYPE_STEP;\n\n\tsetmodel (self, \"progs/wizard.mdl\");\n\n\tsetsize (self, '-16 -16 -24', '16 16 40');\n\tself.health = 150;\n\tself.team = 3;\n\tself.classname = \"monster\";\n\tself.netname = \"floater\";\n\tself.th_stand = wiz_stand1;\n\tself.th_walk = wiz_walk1;\n\tself.th_run = wiz_run1;\n\tself.th_missile = Wiz_Missile;\n\tself.th_pain = Wiz_Pain;\n\tself.th_die = wiz_die;\n\tself.armornoise = \"misc/thud.wav\";\n\n\tflymonster_start ();\n};\n"
  },
  {
    "path": "quakec/fallout2/world.qc",
    "content": "\nvoid() InitBodyQue;\n\nvoid() sci_think;\nvoid() hostage_pain;\nvoid() hostage_think;\nvoid() hostage_die;\nvoid(entity stuff) spawn_zombie;\nvoid() GameControl;\nvoid() VoteBoy;\n\nvoid () create_referees =\n{\n\tnewmis = spawn ();\n\tnewmis.owner = self;\n\tnewmis.movetype = MOVETYPE_NONE;\n\tsetsize (newmis, VEC_ORIGIN, VEC_ORIGIN);\n\tnewmis.solid = SOLID_BBOX;\n\tnewmis.velocity = VEC_ORIGIN;\n\tnewmis.touch = SUB_Null;\n\tsetorigin (newmis, '0 0 -300');\n\tnewmis.nextthink = (time + WEAPON_SHOTGUN);\n\tnewmis.think = GameControl;\n\tnewmis.classname = \"referee\";\n\tnewmis = spawn ();\n\tnewmis.owner = self;\n\tnewmis.movetype = MOVETYPE_NONE;\n\tsetsize (newmis, VEC_ORIGIN, VEC_ORIGIN);\n\tnewmis.solid = SOLID_BBOX;\n\tnewmis.velocity = VEC_ORIGIN;\n\tnewmis.touch = SUB_Null;\n\tsetorigin (newmis, '0 0 -300');\n\tnewmis.nextthink = (time + MULTICAST_PVS_R);\n\tnewmis.think = VoteBoy;\n\tnewmis.classname = \"voteboy\";\n\tnewmis.vote_time = (time + MULTICAST_PVS_R);\n};\n\nvoid() main =\n{\n\tdprint (\"main function\\n\");\n\t\n// these are just commands the the prog compiler to copy these files\n\n\tprecache_file (\"progs.dat\");\n\tprecache_file (\"gfx.wad\");\n\tprecache_file (\"quake.rc\");\n\tprecache_file (\"default.cfg\");\n\n\tprecache_file (\"end1.bin\");\n\tprecache_file2 (\"end2.bin\");\n\n\tprecache_file (\"demo1.dem\");\n\tprecache_file (\"demo2.dem\");\n\tprecache_file (\"demo3.dem\");\n\n//\n// these are all of the lumps from the cached.ls files\n//\n\tprecache_file (\"gfx/palette.lmp\");\n\tprecache_file (\"gfx/colormap.lmp\");\n\n\tprecache_file2 (\"gfx/pop.lmp\");\n\n\tprecache_file (\"gfx/complete.lmp\");\n\tprecache_file (\"gfx/inter.lmp\");\n\n\tprecache_file (\"gfx/ranking.lmp\");\n\tprecache_file (\"gfx/vidmodes.lmp\");\n\tprecache_file (\"gfx/finale.lmp\");\n\tprecache_file (\"gfx/conback.lmp\");\n\tprecache_file (\"gfx/qplaque.lmp\");\n\n\tprecache_file (\"gfx/menudot1.lmp\");\n\tprecache_file (\"gfx/menudot2.lmp\");\n\tprecache_file (\"gfx/menudot3.lmp\");\n\tprecache_file (\"gfx/menudot4.lmp\");\n\tprecache_file (\"gfx/menudot5.lmp\");\n\tprecache_file (\"gfx/menudot6.lmp\");\n\n\tprecache_file (\"gfx/menuplyr.lmp\");\n\tprecache_file (\"gfx/bigbox.lmp\");\n\tprecache_file (\"gfx/dim_modm.lmp\");\n\tprecache_file (\"gfx/dim_drct.lmp\");\n\tprecache_file (\"gfx/dim_ipx.lmp\");\n\tprecache_file (\"gfx/dim_tcp.lmp\");\n\tprecache_file (\"gfx/dim_mult.lmp\");\n\tprecache_file (\"gfx/mainmenu.lmp\");\n\t\n\tprecache_file (\"gfx/box_tl.lmp\");\n\tprecache_file (\"gfx/box_tm.lmp\");\n\tprecache_file (\"gfx/box_tr.lmp\");\n\t\n\tprecache_file (\"gfx/box_ml.lmp\");\n\tprecache_file (\"gfx/box_mm.lmp\");\n\tprecache_file (\"gfx/box_mm2.lmp\");\n\tprecache_file (\"gfx/box_mr.lmp\");\n\t\n\tprecache_file (\"gfx/box_bl.lmp\");\n\tprecache_file (\"gfx/box_bm.lmp\");\n\tprecache_file (\"gfx/box_br.lmp\");\n\t\n\tprecache_file (\"gfx/sp_menu.lmp\");\n\tprecache_file (\"gfx/ttl_sgl.lmp\");\n\tprecache_file (\"gfx/ttl_main.lmp\");\n\tprecache_file (\"gfx/ttl_cstm.lmp\");\n\t\n\tprecache_file (\"gfx/mp_menu.lmp\");\n\t\n\tprecache_file (\"gfx/netmen1.lmp\");\n\tprecache_file (\"gfx/netmen2.lmp\");\n\tprecache_file (\"gfx/netmen3.lmp\");\n\tprecache_file (\"gfx/netmen4.lmp\");\n\tprecache_file (\"gfx/netmen5.lmp\");\n\t\n\tprecache_file (\"gfx/sell.lmp\");\n\t\n\tprecache_file (\"gfx/help0.lmp\");\n\tprecache_file (\"gfx/help1.lmp\");\n\tprecache_file (\"gfx/help2.lmp\");\n\tprecache_file (\"gfx/help3.lmp\");\n\tprecache_file (\"gfx/help4.lmp\");\n\tprecache_file (\"gfx/help5.lmp\");\n\n\tprecache_file (\"gfx/pause.lmp\");\n\tprecache_file (\"gfx/loading.lmp\");\n\n\tprecache_file (\"gfx/p_option.lmp\");\n\tprecache_file (\"gfx/p_load.lmp\");\n\tprecache_file (\"gfx/p_save.lmp\");\n\tprecache_file (\"gfx/p_multi.lmp\");\n\n// sounds loaded by C code\n\tprecache_sound (\"misc/menu1.wav\");\n\tprecache_sound (\"misc/menu2.wav\");\n\tprecache_sound (\"misc/menu3.wav\");\n\n\tprecache_sound (\"ambience/water1.wav\");\n\tprecache_sound (\"ambience/wind2.wav\");\n\n// shareware\n\tprecache_file (\"maps/start.bsp\");\n\n\tprecache_file (\"maps/e1m1.bsp\");\n\tprecache_file (\"maps/e1m2.bsp\");\n\tprecache_file (\"maps/e1m3.bsp\");\n\tprecache_file (\"maps/e1m4.bsp\");\n\tprecache_file (\"maps/e1m5.bsp\");\n\tprecache_file (\"maps/e1m6.bsp\");\n\tprecache_file (\"maps/e1m7.bsp\");\n\tprecache_file (\"maps/e1m8.bsp\");\n\n// registered\n\tprecache_file2 (\"gfx/pop.lmp\");\n\n\tprecache_file2 (\"maps/e2m1.bsp\");\n\tprecache_file2 (\"maps/e2m2.bsp\");\n\tprecache_file2 (\"maps/e2m3.bsp\");\n\tprecache_file2 (\"maps/e2m4.bsp\");\n\tprecache_file2 (\"maps/e2m5.bsp\");\n\tprecache_file2 (\"maps/e2m6.bsp\");\n\tprecache_file2 (\"maps/e2m7.bsp\");\n\n\tprecache_file2 (\"maps/e3m1.bsp\");\n\tprecache_file2 (\"maps/e3m2.bsp\");\n\tprecache_file2 (\"maps/e3m3.bsp\");\n\tprecache_file2 (\"maps/e3m4.bsp\");\n\tprecache_file2 (\"maps/e3m5.bsp\");\n\tprecache_file2 (\"maps/e3m6.bsp\");\n\tprecache_file2 (\"maps/e3m7.bsp\");\n\n\tprecache_file2 (\"maps/e4m1.bsp\");\n\tprecache_file2 (\"maps/e4m2.bsp\");\n\tprecache_file2 (\"maps/e4m3.bsp\");\n\tprecache_file2 (\"maps/e4m4.bsp\");\n\tprecache_file2 (\"maps/e4m5.bsp\");\n\tprecache_file2 (\"maps/e4m6.bsp\");\n\tprecache_file2 (\"maps/e4m7.bsp\");\n\tprecache_file2 (\"maps/e4m8.bsp\");\n\n\tprecache_file2 (\"maps/end.bsp\");\n\n\tprecache_file2 (\"maps/dm1.bsp\");\n\tprecache_file2 (\"maps/dm2.bsp\");\n\tprecache_file2 (\"maps/dm3.bsp\");\n\tprecache_file2 (\"maps/dm4.bsp\");\n\tprecache_file2 (\"maps/dm5.bsp\");\n\tprecache_file2 (\"maps/dm6.bsp\");\n};\n\n\nvoid () GameControl =\n{\n\tlocal float pcount, pdead;\n\tlocal entity te;\n\n\tte = find(world, classname, \"player\");\n\twhile (te)\n\t{\n\n\t\tif (te.health > 0)\n\t\t\tpcount = pcount + 1;\n\t\tif (te.health <= 0)\n\t\t\tpdead = pdead + 1;\n\n\t\tte = find(te, classname, \"player\");\n\t}\n\n\tif (pcount == 0 && pdead > 0) //everyone died\n\t{\n\t\tif (endgame_timer == 0)\n\t\t\tbprint(2, \"^4** ^bTHE PLAYERS WERE SLAIN^b **\\n\");\n\t\tif (endgame_timer == 1)\n\t\t\tbprint(2, \"^4** ^bTHIS AREA WILL RESTART^b **\\n\");\n\t\tif (endgame_timer == 7)\n\t\t\tbprint(2, \"^4** 3 **\\n\");\n\t\tif (endgame_timer == 8)\n\t\t\tbprint(2, \"^4** 2 **\\n\");\n\t\tif (endgame_timer == 9)\n\t\t\tbprint(2, \"^4** 1 **\\n\");\n\t\tif (endgame_timer == 10)\n\t\t\tlocalcmd(\"restart\\n\");\n\n\t\tsound (world, CHAN_BODY, \"misc/menu3.wav\", 1, ATTN_NONE);\n\t}\n\n\tif (pcount == 0 && pdead > 0)\n\t\tendgame_timer = endgame_timer + 1;\n\n\tself.think = GameControl;\n\tself.nextthink = time + 1;\n};\n\nvoid () GameTimer =\n{\n\tlocal entity te, ze;\n\tlocal float switched;\n\tlocal float c, z;\n\n\n\tswitched = 0;\n\n\tte = findradius(self.origin, 25000);\n\twhile (c < 300 && switched == 0)\n\t{\n\t\tif (te.classname == \"raider\" && te.enemy != world && te.processed == 0 && te.active == 0 && switched == 0)\n\t\t{\n\t\t\tte.active = 1;\n\t\t\tte.processed = 1;\n\t\t\tbprint(2, \"it is now \");\n\t\t\tbprint(2, te.netname);\n\t\t\tbprint(2, \"'s turn.\\n\");\n\t\t\tte.maxspeed = 300;\n\t\t\tswitched = 1;\n\n\t\t\tze = findradius(self.origin, 25000);\n\t\t\tz = 0;\n\t\t\twhile (z < 300)\n\t\t\t{\n\t\t\t\tif ((ze.classname == \"player\" || ze.classname == \"raider\") && ze != te)\n\t\t\t\t{\n\t\t\t\t\tze.active = 0;\n\t\t\t\t\tze.maxspeed = 0;\n\t\t\t\t}\n\t\t\t\tz = z + 1;\n\t\t\t\tze = ze.chain;\n\t\t\t}\n\t\t}\n\t\tif (te.classname == \"player\" && te.processed == 0 && te.active == 0 && switched == 0)\n\t\t{\n\t\t\tte.active = 1;\n\t\t\tte.processed = 1;\n\t\t\tbprint(2, \"it is now \");\n\t\t\tbprint(2, te.netname);\n\t\t\tbprint(2, \"'s turn.\\n\");\n\t\t\tte.maxspeed = 300;\n\t\t\tswitched = 1;\n\n\t\t\tze = findradius(self.origin, 25000);\n\t\t\tz = 0;\n\t\t\twhile (z < 300)\n\t\t\t{\n\t\t\t\tif ((ze.classname == \"player\" || ze.classname == \"raider\") && ze != te)\n\t\t\t\t{\n\t\t\t\t\tze.active = 0;\n\t\t\t\t\tze.maxspeed = 0;\n\t\t\t\t}\n\t\t\t\tz = z + 1;\n\t\t\t\tze = ze.chain;\n\t\t\t}\n\t\t}\n\t\tc = c + 1;\n\t\tte = te.chain;\n\t}\n\n\tc = 0;\n\tif (switched == 0) // no entities left, turn is over\n\t{\n\t\tbprint(2, \"round is now over.\\n\");\n\n\t\tte = findradius(self.origin, 25000);\n\t\twhile (c < 300)\n\t\t{\n\t\t\tif (te.classname == \"player\" || te.classname == \"raider\")\n\t\t\t{\n\t\t\t\tte.active = 0;\n\t\t\t\tte.processed = 0;\n\t\t\t\tte.maxspeed = 0;\n\t\t\t}\n\n\t\t\tc = c + 1;\n\t\t\tte = te.chain;\n\t\t}\n\t}\n\n\tself.think = GameTimer;\n\tself.nextthink = time + 1;\n};\n\nvoid () create_timer =\n{\n\tnewmis = spawn ();\n\tnewmis.owner = self;\n\tnewmis.movetype = MOVETYPE_NONE;\n\tsetsize (newmis, VEC_ORIGIN, VEC_ORIGIN);\n\tnewmis.solid = SOLID_BBOX;\n\tnewmis.velocity = VEC_ORIGIN;\n\tnewmis.touch = SUB_Null;\n\tsetorigin (newmis, '0 0 -300');\n\tnewmis.nextthink = (time + 1);\n\tnewmis.think = GameTimer;\n\tnewmis.classname = \"referee\";\n};\n\nentity  lastspawn;\n\n//=======================\n/*QUAKED worldspawn (0 0 0) ?\nOnly used for the world entity.\nSet message to the level name.\nSet sounds to the cd track to play.\n\nWorld Types:\n0: medieval\n1: metal\n2: base\n*/\n//=======================\nvoid() worldspawn =\n{\n\tlastspawn = world;\n\tInitBodyQue ();\n\tcoop = !cvar(\"deathmatch\");\n\tcreate_referees();\n\tSetupStats();\n\n// custom map attributes\n\n\tif (self.model == \"maps/e1m8.bsp\")\n\t\tcvar_set (\"sv_gravity\", \"100\");\n\telse\n\t\tcvar_set (\"sv_gravity\", \"800\");\n\n\n\n// the area based ambient sounds MUST be the first precache_sounds\n\n// player precaches     \n\tW_Precache ();                  // get weapon precaches\n\n// sounds used from C physics code\n\tprecache_sound (\"demon/dland2.wav\");            // landing thud\n\tprecache_sound (\"misc/h2ohit1.wav\");            // landing splash\n\n// setup precaches allways needed\n\tprecache_sound (\"items/itembk2.wav\");           // item respawn sound\n\tprecache_sound (\"player/plyrjmp8.wav\");         // player jump\n\tprecache_sound (\"player/land.wav\");                     // player landing\n\tprecache_sound (\"player/land2.wav\");            // player hurt landing\n\tprecache_sound (\"player/drown1.wav\");           // drowning pain\n\tprecache_sound (\"player/drown2.wav\");           // drowning pain\n\tprecache_sound (\"player/gasp1.wav\");            // gasping for air\n\tprecache_sound (\"player/gasp2.wav\");            // taking breath\n\tprecache_sound (\"player/h2odeath.wav\");         // drowning death\n\n\tprecache_sound (\"misc/talk.wav\");                       // talk\n\tprecache_sound (\"player/teledth1.wav\");         // telefrag\n\tprecache_sound (\"misc/r_tele1.wav\");            // teleport sounds\n\tprecache_sound (\"misc/r_tele2.wav\");\n\tprecache_sound (\"misc/r_tele3.wav\");\n\tprecache_sound (\"misc/r_tele4.wav\");\n\tprecache_sound (\"misc/r_tele5.wav\");\n\tprecache_sound (\"weapons/lock4.wav\");           // ammo pick up\n\tprecache_sound (\"weapons/pkup.wav\");            // weapon up\n\tprecache_sound (\"items/armor1.wav\");            // armor up\n\tprecache_sound (\"weapons/lhit.wav\");            //lightning\n\tprecache_sound (\"weapons/lstart.wav\");          //lightning start\n\tprecache_sound (\"items/damage3.wav\");\n\n\tprecache_sound (\"misc/power.wav\");                      //lightning for boss\n\n// player gib sounds\n\tprecache_sound (\"player/gib.wav\");                      // player gib sound\n\tprecache_sound (\"player/udeath.wav\");           // player gib sound\n\tprecache_sound (\"player/tornoff2.wav\");         // gib sound\n\n// player pain sounds\n\n\tprecache_sound (\"player/pain1.wav\");\n\tprecache_sound (\"player/pain2.wav\");\n\tprecache_sound (\"player/pain3.wav\");\n\tprecache_sound (\"player/pain4.wav\");\n\tprecache_sound (\"player/pain5.wav\");\n\tprecache_sound (\"player/pain6.wav\");\n\n// player death sounds\n\tprecache_sound (\"player/death1.wav\");\n\tprecache_sound (\"player/death2.wav\");\n\tprecache_sound (\"player/death3.wav\");\n\tprecache_sound (\"player/death4.wav\");\n\tprecache_sound (\"player/death5.wav\");\n\n\tprecache_sound (\"boss1/sight1.wav\");\n\n// ax sounds    \n\tprecache_sound (\"weapons/ax1.wav\");                     // ax swoosh\n\tprecache_sound (\"player/axhit1.wav\");           // ax hit meat\n\tprecache_sound (\"player/axhit2.wav\");           // ax hit world\n\n\tprecache_sound (\"player/h2ojump.wav\");          // player jumping into water\n\tprecache_sound (\"player/slimbrn2.wav\");         // player enter slime\n\tprecache_sound (\"player/inh2o.wav\");            // player enter water\n\tprecache_sound (\"player/inlava.wav\");           // player enter lava\n\tprecache_sound (\"misc/outwater.wav\");           // leaving water sound\n\n\tprecache_sound (\"player/lburn1.wav\");           // lava burn\n\tprecache_sound (\"player/lburn2.wav\");           // lava burn\n\n\tprecache_sound (\"misc/water1.wav\");                     // swimming\n\tprecache_sound (\"misc/water2.wav\");                     // swimming\n\n// Invulnerability sounds\n\tprecache_sound (\"items/protect.wav\");\n\tprecache_sound (\"items/protect2.wav\");\n\tprecache_sound (\"items/protect3.wav\");\n\n\tprecache_model (\"progs/player.mdl\");\n\tprecache_model (\"progs/guy.mdl\");\n\tprecache_model (\"progs/lay.mdl\");\n\tprecache_model (\"progs/eyes.mdl\");\n\tprecache_model (\"progs/h_player.mdl\");\n\tprecache_model (\"progs/gib1.mdl\");\n\tprecache_model (\"progs/gib2.mdl\");\n\tprecache_model (\"progs/gib3.mdl\");\n\n\tprecache_model (\"progs/s_bubble.spr\");  // drowning bubbles\n\tprecache_model (\"progs/s_explod.spr\");  // sprite explosion\n\n\tprecache_model (\"progs/v_axe.mdl\");\n\tprecache_model (\"progs/v_shot.mdl\");\n\tprecache_model (\"progs/v_span.mdl\");\n\tprecache_model (\"progs/v_nail.mdl\");\n\tprecache_model (\"progs/v_rock.mdl\");\n\tprecache_model (\"progs/v_shot2.mdl\");\n\tprecache_model (\"progs/v_nail2.mdl\");\n\tprecache_model (\"progs/v_rock2.mdl\");\n\tprecache_model (\"progs/v_fist.mdl\");\n\tprecache_model (\"progs/v_knife.mdl\");\n\tprecache_model (\"progs/v_1911.mdl\");\n\tprecache_model (\"progs/v_ak47.mdl\");\n\tprecache_model (\"progs/v_smg.mdl\");\n\tprecache_model (\"progs/v_rangem.mdl\");\n\tprecache_model (\"progs/v_pipe.mdl\");\n\tprecache_model (\"progs/v_shotgun.mdl\");\n\tprecache_model (\"progs/v_double.mdl\");\n\tprecache_model (\"progs/v_mp9.mdl\");\n\tprecache_model (\"progs/v_sa80.mdl\");\n\tprecache_model (\"progs/v_deagle.mdl\");\n\tprecache_model (\"progs/v_alien.mdl\");\n\tprecache_model (\"progs/v_srifle.mdl\");\n\tprecache_model (\"progs/v_night.mdl\");\n\tprecache_model (\"progs/v_piperifle.mdl\");\n\tprecache_model (\"progs/v_handgren.mdl\");\n\tprecache_model (\"progs/v_jackhammer.mdl\");\n\tprecache_model (\"progs/v_rocket.mdl\");\n\n\tprecache_model (\"progs/handgren.mdl\");\n\tprecache_model (\"progs/plasma.mdl\");\n\tprecache_model (\"progs/ray.mdl\");\n\tprecache_model (\"progs/sneak.mdl\");\n\tprecache_model (\"progs/dead.mdl\");\n\tprecache_model (\"progs/hbar.spr\");\n\tprecache_model (\"progs/blast.mdl\");\n\tprecache_model (\"progs/station.mdl\");\n\tprecache_model (\"progs/camera.mdl\");\n\tprecache_model (\"progs/tehghoul.mdl\");\n\tprecache_model (\"progs/ray.mdl\");\n\n\tprecache_model (\"progs/w_knife.mdl\");\n\tprecache_model (\"progs/w_pipe.mdl\");\n\tprecache_model (\"progs/w_shotgun.mdl\");\n\tprecache_model (\"progs/w_axe.mdl\");\n\tprecache_model (\"progs/w_1911.mdl\");\n\tprecache_model (\"progs/w_deagle.mdl\");\n\tprecache_model (\"progs/w_alien.mdl\");\n\tprecache_model (\"progs/w_mp7.mdl\");\n\tprecache_model (\"progs/w_mp9.mdl\");\n\tprecache_model (\"progs/w_jackhammer.mdl\");\n\tprecache_model (\"progs/w_rangem.mdl\");\n\tprecache_model (\"progs/w_sa80.mdl\");\n\tprecache_model (\"progs/w_ak47.mdl\");\n\tprecache_model (\"progs/w_night.mdl\");\n\tprecache_model (\"progs/w_srifle.mdl\");\n\tprecache_model (\"progs/w_gauss.mdl\");\n\tprecache_model (\"progs/w_carbine.mdl\");\n\tprecache_model (\"progs/w_laser.mdl\");\n\tprecache_model (\"progs/w_rocket.mdl\");\n\n\tprecache_model (\"progs/grenade2.mdl\");\n\tprecache_model (\"maps/b_bh10.bsp\");\n\tprecache_model (\"maps/b_bh25.bsp\");\n\tprecache_model (\"maps/b_bh100.bsp\");\n\tprecache_model (\"progs/s_light.spr\");\n\n\tprecache_sound (\"misc/thud.wav\");\n\tprecache_sound (\"player/step1.wav\");\n\tprecache_sound (\"player/step2.wav\");\n\tprecache_sound (\"player/step3.wav\");\n\tprecache_sound (\"player/step4.wav\");\n\tprecache_sound (\"player/step1a.wav\");\n\tprecache_sound (\"player/step2a.wav\");\n\tprecache_sound (\"player/step3a.wav\");\n\tprecache_sound (\"player/step4a.wav\");\n\tprecache_sound (\"dog/dattack1.wav\");\n\tprecache_sound (\"dog/ddeath.wav\");\n\tprecache_sound (\"dog/dpain1.wav\");\n\tprecache_sound (\"dog/dsight.wav\");\n\tprecache_sound (\"dog/idle.wav\");\n\tprecache_sound (\"weapons/1911.wav\");\n\tprecache_sound (\"weapons/auto.wav\");\n\tprecache_sound (\"weapons/auto2.wav\");\n\tprecache_sound (\"weapons/ak112.wav\");\n\tprecache_sound (\"weapons/ak47.wav\");\n\tprecache_sound (\"weapons/amr.wav\");\n\tprecache_sound (\"weapons/blaster.wav\");\n\tprecache_sound (\"weapons/blaster2.wav\");\n\tprecache_sound (\"weapons/blowoff.wav\");\n\tprecache_sound (\"weapons/bounce.wav\");\n\tprecache_sound (\"weapons/burst.wav\");\n\tprecache_sound (\"weapons/caws.wav\");\n\tprecache_sound (\"weapons/click.wav\");\n\tprecache_sound (\"weapons/deagle.wav\");\n\tprecache_sound (\"weapons/dks-1.wav\");\n\tprecache_sound (\"weapons/energy1.wav\");\n\tprecache_sound (\"weapons/energy2.wav\");\n\tprecache_sound (\"weapons/energy3.wav\");\n\tprecache_sound (\"weapons/energy4.wav\");\n\tprecache_sound (\"weapons/flamer.wav\");\n\tprecache_sound (\"weapons/fnfal.wav\");\n\tprecache_sound (\"weapons/g11.wav\");\n\tprecache_sound (\"weapons/gauss.wav\");\n\tprecache_sound (\"weapons/gl-1.wav\");\n\tprecache_sound (\"weapons/gpull.wav\");\n\tprecache_sound (\"weapons/helmet.wav\");\n\tprecache_sound (\"weapons/jackhammer.wav\");\n\tprecache_sound (\"weapons/laser.wav\");\n\tprecache_sound (\"weapons/m249.wav\");\n\tprecache_sound (\"weapons/m4-nw.wav\");\n\tprecache_sound (\"weapons/needler.wav\");\n\tprecache_sound (\"weapons/shotgun1.wav\");\n\tprecache_sound (\"weapons/shotgun2.wav\");\n\tprecache_sound (\"weapons/mp5.wav\");\n\tprecache_sound (\"weapons/mp7.wav\");\n\tprecache_sound (\"weapons/rangem.wav\");\n\tprecache_sound (\"weapons/ric1.wav\");\n\tprecache_sound (\"weapons/ric2.wav\");\n\tprecache_sound (\"weapons/ric3.wav\");\n\tprecache_sound (\"weapons/ric4.wav\");\n\tprecache_sound (\"weapons/ric5.wav\");\n\tprecache_sound (\"weapons/tink1.wav\");\n\tprecache_sound (\"weapons/sa80-1.wav\");\n\tprecache_sound (\"weapons/shell.wav\");\n\tprecache_sound (\"weapons/reload.wav\");\n\tprecache_sound (\"misc/build1.wav\");\n\tprecache_sound (\"misc/build2.wav\");\n\tprecache_sound (\"misc/build3.wav\");\n\tprecache_sound (\"player/agdie1.wav\");\n\tprecache_sound (\"player/agdie2.wav\");\n\tprecache_sound (\"player/agdie3.wav\");\n\tprecache_sound (\"player/agdie4.wav\");\n\tprecache_sound (\"player/agdie5.wav\");\n\tprecache_sound (\"player/berserk.wav\");\n\tprecache_sound (\"player/breathe.wav\");\n\tprecache_sound (\"player/headshot.wav\");\n\tprecache_sound (\"player/hit1.wav\");\n\tprecache_sound (\"player/paina.wav\");\n\tprecache_sound (\"player/painb.wav\");\n\tprecache_model (\"progs/bolt.mdl\");              // for lightning gun\n\tprecache_model (\"progs/bolt2.mdl\");             // for lightning gun\n\tprecache_model (\"progs/bolt3.mdl\");             // for boss shock\n\tprecache_model (\"progs/lavaball.mdl\");  // for testing\n\t\n\tprecache_model (\"progs/missile.mdl\");\n\tprecache_model (\"progs/grenade.mdl\");\n\tprecache_model (\"progs/spike.mdl\");\n\tprecache_model (\"progs/s_spike.mdl\");\n\n\tprecache_model (\"progs/backpack.mdl\");\n\n\tprecache_model (\"progs/zom_gib.mdl\");\n\n\tprecache_model (\"progs/v_light.mdl\");\n\t\n\n\n//\n// Setup light animation tables. 'a' is total darkness, 'z' is maxbright.\n//\n\n\t// 0 normal\n\tlightstyle(0, \"m\");\n\t\n\t// 1 FLICKER (first variety)\n\tlightstyle(1, \"mmnmmommommnonmmonqnmmo\");\n\t\n\t// 2 SLOW STRONG PULSE\n\tlightstyle(2, \"abcdefghijklmnopqrstuvwxyzyxwvutsrqponmlkjihgfedcba\");\n\t\n\t// 3 CANDLE (first variety)\n\tlightstyle(3, \"mmmmmaaaaammmmmaaaaaabcdefgabcdefg\");\n\t\n\t// 4 FAST STROBE\n\tlightstyle(4, \"mamamamamama\");\n\t\n\t// 5 GENTLE PULSE 1\n\tlightstyle(5,\"jklmnopqrstuvwxyzyxwvutsrqponmlkj\");\n\t\n\t// 6 FLICKER (second variety)\n\tlightstyle(6, \"nmonqnmomnmomomno\");\n\t\n\t// 7 CANDLE (second variety)\n\tlightstyle(7, \"mmmaaaabcdefgmmmmaaaammmaamm\");\n\t\n\t// 8 CANDLE (third variety)\n\tlightstyle(8, \"mmmaaammmaaammmabcdefaaaammmmabcdefmmmaaaa\");\n\t\n\t// 9 SLOW STROBE (fourth variety)\n\tlightstyle(9, \"aaaaaaaazzzzzzzz\");\n\t\n\t// 10 FLUORESCENT FLICKER\n\tlightstyle(10, \"mmamammmmammamamaaamammma\");\n\n\t// 11 SLOW PULSE NOT FADE TO BLACK\n\tlightstyle(11, \"abcdefghijklmnopqrrqponmlkjihgfedcba\");\n\t\n\t// styles 32-62 are assigned by the light program for switchable lights\n\n\t// 63 testing\n\tlightstyle(63, \"a\");\n};\n\nvoid() StartFrame =\n{\n\ttimelimit = cvar(\"timelimit\") * 60;\n\tfraglimit = cvar(\"fraglimit\");\n\tteamplay = cvar(\"teamplay\");\n\tdeathmatch = !coop;\n\t\n\tframecount = framecount + 1;\n};\n\n/*\n==============================================================================\n\nBODY QUE\n\n==============================================================================\n*/\n\nentity  bodyque_head;\n\nvoid() bodyque =\n{       // just here so spawn functions don't complain after the world\n\t// creates bodyques\n};\n\nvoid() InitBodyQue =\n{\n\tbodyque_head = spawn();\n\tbodyque_head.classname = \"bodyque\";\n\tbodyque_head.owner = spawn();\n\tbodyque_head.owner.classname = \"bodyque\";\n\tbodyque_head.owner.owner = spawn();\n\tbodyque_head.owner.owner.classname = \"bodyque\";\n\tbodyque_head.owner.owner.owner = spawn();\n\tbodyque_head.owner.owner.owner.classname = \"bodyque\";\n\tbodyque_head.owner.owner.owner.owner = bodyque_head;\n};\n\n\n// make a body que entry for the given ent so the ent can be\n// respawned elsewhere\nvoid(entity ent) CopyToBodyQue =\n{\n\tbodyque_head.angles = ent.angles;\n\tbodyque_head.model = ent.model;\n\tbodyque_head.modelindex = ent.modelindex;\n\tbodyque_head.frame = ent.frame;\n\tbodyque_head.colormap = ent.colormap;\n\tbodyque_head.movetype = ent.movetype;\n\tbodyque_head.velocity = ent.velocity;\n\tbodyque_head.flags = 0;\n\tsetorigin (bodyque_head, ent.origin);\n\tsetsize (bodyque_head, ent.mins, ent.maxs);\n\tbodyque_head = bodyque_head.owner;\n};\n\n\n"
  },
  {
    "path": "quakec/fallout2/zombie.qc",
    "content": "/*\n==============================================================================\n\nZOMBIE\n\n==============================================================================\n*/\n$cd id1/models/zombie\n\n$origin\t0 0 24\n\n$base base\n$skin skin\n\n$frame stand1 stand2 stand3 stand4 stand5 stand6 stand7 stand8\n$frame stand9 stand10 stand11 stand12 stand13 stand14 stand15\n\n$frame walk1 walk2 walk3 walk4 walk5 walk6 walk7 walk8 walk9 walk10 walk11\n$frame walk12 walk13 walk14 walk15 walk16 walk17 walk18 walk19\n\n$frame run1 run2 run3 run4 run5 run6 run7 run8 run9 run10 run11 run12\n$frame run13 run14 run15 run16 run17 run18\n\n$frame atta1 atta2 atta3 atta4 atta5 atta6 atta7 atta8 atta9 atta10 atta11\n$frame atta12 atta13\n\n$frame attb1 attb2 attb3 attb4 attb5 attb6 attb7 attb8 attb9 attb10 attb11\n$frame attb12 attb13 attb14\n\n$frame attc1 attc2 attc3 attc4 attc5 attc6 attc7 attc8 attc9 attc10 attc11\n$frame attc12\n\n$frame paina1 paina2 paina3 paina4 paina5 paina6 paina7 paina8 paina9 paina10\n$frame paina11 paina12\n\n$frame painb1 painb2 painb3 painb4 painb5 painb6 painb7 painb8 painb9 painb10\n$frame painb11 painb12 painb13 painb14 painb15 painb16 painb17 painb18 painb19\n$frame painb20 painb21 painb22 painb23 painb24 painb25 painb26 painb27 painb28\n\n$frame painc1 painc2 painc3 painc4 painc5 painc6 painc7 painc8 painc9 painc10\n$frame painc11 painc12 painc13 painc14 painc15 painc16 painc17 painc18\n\n$frame paind1 paind2 paind3 paind4 paind5 paind6 paind7 paind8 paind9 paind10\n$frame paind11 paind12 paind13\n\n$frame paine1 paine2 paine3 paine4 paine5 paine6 paine7 paine8 paine9 paine10\n$frame paine11 paine12 paine13 paine14 paine15 paine16 paine17 paine18 paine19\n$frame paine20 paine21 paine22 paine23 paine24 paine25 paine26 paine27 paine28\n$frame paine29 paine30\n\n$frame cruc_1 cruc_2 cruc_3 cruc_4 cruc_5 cruc_6\n\nfloat\tSPAWN_CRUCIFIED\t= 1;\n\n//=============================================================================\n\n.float inpain;\n\nvoid() zombie_stand1\t=[\t$stand1,\t\tzombie_stand2\t] {ai_stand();};\nvoid() zombie_stand2\t=[\t$stand2,\t\tzombie_stand3\t] {ai_stand();};\nvoid() zombie_stand3\t=[\t$stand3,\t\tzombie_stand4\t] {ai_stand();};\nvoid() zombie_stand4\t=[\t$stand4,\t\tzombie_stand5\t] {ai_stand();};\nvoid() zombie_stand5\t=[\t$stand5,\t\tzombie_stand6\t] {ai_stand();};\nvoid() zombie_stand6\t=[\t$stand6,\t\tzombie_stand7\t] {ai_stand();};\nvoid() zombie_stand7\t=[\t$stand7,\t\tzombie_stand8\t] {ai_stand();};\nvoid() zombie_stand8\t=[\t$stand8,\t\tzombie_stand9\t] {ai_stand();};\nvoid() zombie_stand9\t=[\t$stand9,\t\tzombie_stand10\t] {ai_stand();};\nvoid() zombie_stand10\t=[\t$stand10,\t\tzombie_stand11\t] {ai_stand();};\nvoid() zombie_stand11\t=[\t$stand11,\t\tzombie_stand12\t] {ai_stand();};\nvoid() zombie_stand12\t=[\t$stand12,\t\tzombie_stand13\t] {ai_stand();};\nvoid() zombie_stand13\t=[\t$stand13,\t\tzombie_stand14\t] {ai_stand();};\nvoid() zombie_stand14\t=[\t$stand14,\t\tzombie_stand15\t] {ai_stand();};\nvoid() zombie_stand15\t=[\t$stand15,\t\tzombie_stand1\t] {ai_stand();};\n\nvoid() zombie_cruc1\t=\t[\t$cruc_1,\t\tzombie_cruc2\t] {\nif (random() < 0.1)\n\tsound (self, CHAN_VOICE, \"zombie/idle_w2.wav\", 1, ATTN_STATIC);};\nvoid() zombie_cruc2\t=\t[\t$cruc_2,\t\tzombie_cruc3\t] {self.nextthink = time + 0.1 + random()*0.1;};\nvoid() zombie_cruc3\t=\t[\t$cruc_3,\t\tzombie_cruc4\t] {self.nextthink = time + 0.1 + random()*0.1;};\nvoid() zombie_cruc4\t=\t[\t$cruc_4,\t\tzombie_cruc5\t] {self.nextthink = time + 0.1 + random()*0.1;};\nvoid() zombie_cruc5\t=\t[\t$cruc_5,\t\tzombie_cruc6\t] {self.nextthink = time + 0.1 + random()*0.1;};\nvoid() zombie_cruc6\t=\t[\t$cruc_6,\t\tzombie_cruc1\t] {self.nextthink = time + 0.1 + random()*0.1;};\n\nvoid() zombie_walk1\t\t=[\t$walk1,\t\tzombie_walk2\t] {ai_walk(0);};\nvoid() zombie_walk2\t\t=[\t$walk2,\t\tzombie_walk3\t] {ai_walk(2);};\nvoid() zombie_walk3\t\t=[\t$walk3,\t\tzombie_walk4\t] {ai_walk(3);};\nvoid() zombie_walk4\t\t=[\t$walk4,\t\tzombie_walk5\t] {ai_walk(2);};\nvoid() zombie_walk5\t\t=[\t$walk5,\t\tzombie_walk6\t] {ai_walk(1);};\nvoid() zombie_walk6\t\t=[\t$walk6,\t\tzombie_walk7\t] {ai_walk(0);};\nvoid() zombie_walk7\t\t=[\t$walk7,\t\tzombie_walk8\t] {ai_walk(0);};\nvoid() zombie_walk8\t\t=[\t$walk8,\t\tzombie_walk9\t] {ai_walk(0);};\nvoid() zombie_walk9\t\t=[\t$walk9,\t\tzombie_walk10\t] {ai_walk(0);};\nvoid() zombie_walk10\t=[\t$walk10,\tzombie_walk11\t] {ai_walk(0);};\nvoid() zombie_walk11\t=[\t$walk11,\tzombie_walk12\t] {ai_walk(2);};\nvoid() zombie_walk12\t=[\t$walk12,\tzombie_walk13\t] {ai_walk(2);};\nvoid() zombie_walk13\t=[\t$walk13,\tzombie_walk14\t] {ai_walk(1);};\nvoid() zombie_walk14\t=[\t$walk14,\tzombie_walk15\t] {ai_walk(0);};\nvoid() zombie_walk15\t=[\t$walk15,\tzombie_walk16\t] {ai_walk(0);};\nvoid() zombie_walk16\t=[\t$walk16,\tzombie_walk17\t] {ai_walk(0);};\nvoid() zombie_walk17\t=[\t$walk17,\tzombie_walk18\t] {ai_walk(0);};\nvoid() zombie_walk18\t=[\t$walk18,\tzombie_walk19\t] {ai_walk(0);};\nvoid() zombie_walk19\t=[\t$walk19,\tzombie_walk1\t] {\nai_walk(0);\nif (random() < 0.2)\n\tsound (self, CHAN_VOICE, \"zombie/z_idle.wav\", 1, ATTN_IDLE);};\n\nvoid() zombie_run1\t\t=[\t$run1,\t\tzombie_run2\t] {ai_run(1);self.inpain = 0;};\nvoid() zombie_run2\t\t=[\t$run2,\t\tzombie_run3\t] {ai_run(1);};\nvoid() zombie_run3\t\t=[\t$run3,\t\tzombie_run4\t] {ai_run(0);};\nvoid() zombie_run4\t\t=[\t$run4,\t\tzombie_run5\t] {ai_run(1);};\nvoid() zombie_run5\t\t=[\t$run5,\t\tzombie_run6\t] {ai_run(2);};\nvoid() zombie_run6\t\t=[\t$run6,\t\tzombie_run7\t] {ai_run(3);};\nvoid() zombie_run7\t\t=[\t$run7,\t\tzombie_run8\t] {ai_run(4);};\nvoid() zombie_run8\t\t=[\t$run8,\t\tzombie_run9\t] {ai_run(4);};\nvoid() zombie_run9\t\t=[\t$run9,\t\tzombie_run10\t] {ai_run(2);};\nvoid() zombie_run10\t\t=[\t$run10,\t\tzombie_run11\t] {ai_run(0);};\nvoid() zombie_run11\t\t=[\t$run11,\t\tzombie_run12\t] {ai_run(0);};\nvoid() zombie_run12\t\t=[\t$run12,\t\tzombie_run13\t] {ai_run(0);};\nvoid() zombie_run13\t\t=[\t$run13,\t\tzombie_run14\t] {ai_run(2);};\nvoid() zombie_run14\t\t=[\t$run14,\t\tzombie_run15\t] {ai_run(4);};\nvoid() zombie_run15\t\t=[\t$run15,\t\tzombie_run16\t] {ai_run(6);};\nvoid() zombie_run16\t\t=[\t$run16,\t\tzombie_run17\t] {ai_run(7);};\nvoid() zombie_run17\t\t=[\t$run17,\t\tzombie_run18\t] {ai_run(3);};\nvoid() zombie_run18\t\t=[\t$run18,\t\tzombie_run1\t] {\nai_run(8);\nif (random() < 0.2)\n\tsound (self, CHAN_VOICE, \"zombie/z_idle.wav\", 1, ATTN_IDLE);\nif (random() > 0.8)\n\tsound (self, CHAN_VOICE, \"zombie/z_idle1.wav\", 1, ATTN_IDLE);\n};\n\n/*\n=============================================================================\n\nATTACKS\n\n=============================================================================\n*/\n\nvoid() ZombieGrenadeTouch =\n{\n\tif (other == self.owner)\n\t\treturn;\t\t// don't explode on owner\n\tif (other.takedamage)\n\t{\n\t\tT_Damage (other, self, self.owner, 10 );\n\t\tsound (self, CHAN_WEAPON, \"zombie/z_hit.wav\", 1, ATTN_NORM);\n\t\tremove (self);\n\t\treturn;\n\t}\n\tsound (self, CHAN_WEAPON, \"zombie/z_miss.wav\", 1, ATTN_NORM);\t// bounce sound\n\tself.velocity = '0 0 0';\n\tself.avelocity = '0 0 0';\n\tself.touch = SUB_Remove;\n};\n\n/*\n================\nZombieFireGrenade\n================\n*/\nvoid(vector st) ZombieFireGrenade =\n{\n\tlocal\tentity missile;\n\tlocal\tvector\torg;\n\n\tsound (self, CHAN_WEAPON, \"zombie/z_shot1.wav\", 1, ATTN_NORM);\n\n\tmissile = spawn ();\n\tmissile.owner = self;\n\tmissile.movetype = MOVETYPE_BOUNCE;\n\tmissile.solid = SOLID_BBOX;\n\n// calc org\n\torg = self.origin + st_x * v_forward + st_y * v_right + (st_z - 24) * v_up;\n\t\n// set missile speed\t\n\n\tmakevectors (self.angles);\n\n\tmissile.velocity = normalize(self.enemy.origin - org);\n\tmissile.velocity = missile.velocity * 600;\n\tmissile.velocity_z = 200;\n\n\tmissile.avelocity = '3000 1000 2000';\n\n\tmissile.touch = ZombieGrenadeTouch;\n\t\n// set missile duration\n\tmissile.nextthink = time + 2.5;\n\tmissile.think = SUB_Remove;\n\n\tsetmodel (missile, \"progs/zom_gib.mdl\");\n\tsetsize (missile, '0 0 0', '0 0 0');\t\t\n\tsetorigin (missile, org);\n};\n\n\nvoid() zombie_atta1\t\t=[\t$atta1,\t\tzombie_atta2\t] {ai_face();};\nvoid() zombie_atta2\t\t=[\t$atta2,\t\tzombie_atta3\t] {ai_face();};\nvoid() zombie_atta3\t\t=[\t$atta3,\t\tzombie_atta4\t] {ai_face();};\nvoid() zombie_atta4\t\t=[\t$atta4,\t\tzombie_atta5\t] {ai_face();};\nvoid() zombie_atta5\t\t=[\t$atta5,\t\tzombie_atta6\t] {ai_face();};\nvoid() zombie_atta6\t\t=[\t$atta6,\t\tzombie_atta7\t] {ai_face();};\nvoid() zombie_atta7\t\t=[\t$atta7,\t\tzombie_atta8\t] {ai_face();};\nvoid() zombie_atta8\t\t=[\t$atta8,\t\tzombie_atta9\t] {ai_face();};\nvoid() zombie_atta9\t\t=[\t$atta9,\t\tzombie_atta10\t] {ai_face();};\nvoid() zombie_atta10\t=[\t$atta10,\tzombie_atta11\t] {ai_face();};\nvoid() zombie_atta11\t=[\t$atta11,\tzombie_atta12\t] {ai_face();};\nvoid() zombie_atta12\t=[\t$atta12,\tzombie_atta13\t] {ai_face();};\nvoid() zombie_atta13\t=[\t$atta13,\tzombie_run1\t] {ai_face();ZombieFireGrenade('-10 -22 30');};\n\nvoid() zombie_attb1\t\t=[\t$attb1,\t\tzombie_attb2\t] {ai_face();};\nvoid() zombie_attb2\t\t=[\t$attb2,\t\tzombie_attb3\t] {ai_face();};\nvoid() zombie_attb3\t\t=[\t$attb3,\t\tzombie_attb4\t] {ai_face();};\nvoid() zombie_attb4\t\t=[\t$attb4,\t\tzombie_attb5\t] {ai_face();};\nvoid() zombie_attb5\t\t=[\t$attb5,\t\tzombie_attb6\t] {ai_face();};\nvoid() zombie_attb6\t\t=[\t$attb6,\t\tzombie_attb7\t] {ai_face();};\nvoid() zombie_attb7\t\t=[\t$attb7,\t\tzombie_attb8\t] {ai_face();};\nvoid() zombie_attb8\t\t=[\t$attb8,\t\tzombie_attb9\t] {ai_face();};\nvoid() zombie_attb9\t\t=[\t$attb9,\t\tzombie_attb10\t] {ai_face();};\nvoid() zombie_attb10\t=[\t$attb10,\tzombie_attb11\t] {ai_face();};\nvoid() zombie_attb11\t=[\t$attb11,\tzombie_attb12\t] {ai_face();};\nvoid() zombie_attb12\t=[\t$attb12,\tzombie_attb13\t] {ai_face();};\nvoid() zombie_attb13\t=[\t$attb13,\tzombie_attb14\t] {ai_face();};\nvoid() zombie_attb14\t=[\t$attb13,\tzombie_run1\t] {ai_face();ZombieFireGrenade('-10 -24 29');};\n\nvoid() zombie_attc1\t\t=[\t$attc1,\t\tzombie_attc2\t] {ai_face();};\nvoid() zombie_attc2\t\t=[\t$attc2,\t\tzombie_attc3\t] {ai_face();};\nvoid() zombie_attc3\t\t=[\t$attc3,\t\tzombie_attc4\t] {ai_face();};\nvoid() zombie_attc4\t\t=[\t$attc4,\t\tzombie_attc5\t] {ai_face();};\nvoid() zombie_attc5\t\t=[\t$attc5,\t\tzombie_attc6\t] {ai_face();};\nvoid() zombie_attc6\t\t=[\t$attc6,\t\tzombie_attc7\t] {ai_face();};\nvoid() zombie_attc7\t\t=[\t$attc7,\t\tzombie_attc8\t] {ai_face();};\nvoid() zombie_attc8\t\t=[\t$attc8,\t\tzombie_attc9\t] {ai_face();};\nvoid() zombie_attc9\t\t=[\t$attc9,\t\tzombie_attc10\t] {ai_face();};\nvoid() zombie_attc10\t=[\t$attc10,\tzombie_attc11\t] {ai_face();};\nvoid() zombie_attc11\t=[\t$attc11,\tzombie_attc12\t] {ai_face();};\nvoid() zombie_attc12\t=[\t$attc12,\tzombie_run1\t\t] {ai_face();ZombieFireGrenade('-12 -19 29');};\n\nvoid() zombie_missile =\n{\n\tlocal float\tr;\n\t\n\tr = random();\n\t\n\tif (r < 0.3)\n\t\tzombie_atta1 ();\n\telse if (r < 0.6)\n\t\tzombie_attb1 ();\n\telse\n\t\tzombie_attc1 ();\n};\n\n\n/*\n=============================================================================\n\nPAIN\n\n=============================================================================\n*/\n\nvoid() zombie_paina1\t=[\t$paina1,\tzombie_paina2\t] {sound (self, CHAN_VOICE, \"zombie/z_pain.wav\", 1, ATTN_NORM);};\nvoid() zombie_paina2\t=[\t$paina2,\tzombie_paina3\t] {ai_painforward(3);};\nvoid() zombie_paina3\t=[\t$paina3,\tzombie_paina4\t] {ai_painforward(1);};\nvoid() zombie_paina4\t=[\t$paina4,\tzombie_paina5\t] {ai_pain(1);};\nvoid() zombie_paina5\t=[\t$paina5,\tzombie_paina6\t] {ai_pain(3);};\nvoid() zombie_paina6\t=[\t$paina6,\tzombie_paina7\t] {ai_pain(1);};\nvoid() zombie_paina7\t=[\t$paina7,\tzombie_paina8\t] {};\nvoid() zombie_paina8\t=[\t$paina8,\tzombie_paina9\t] {};\nvoid() zombie_paina9\t=[\t$paina9,\tzombie_paina10\t] {};\nvoid() zombie_paina10\t=[\t$paina10,\tzombie_paina11\t] {};\nvoid() zombie_paina11\t=[\t$paina11,\tzombie_paina12\t] {};\nvoid() zombie_paina12\t=[\t$paina12,\tzombie_run1\t\t] {};\n\nvoid() zombie_painb1\t=[\t$painb1,\tzombie_painb2\t] {sound (self, CHAN_VOICE, \"zombie/z_pain1.wav\", 1, ATTN_NORM);};\nvoid() zombie_painb2\t=[\t$painb2,\tzombie_painb3\t] {ai_pain(2);};\nvoid() zombie_painb3\t=[\t$painb3,\tzombie_painb4\t] {ai_pain(8);};\nvoid() zombie_painb4\t=[\t$painb4,\tzombie_painb5\t] {ai_pain(6);};\nvoid() zombie_painb5\t=[\t$painb5,\tzombie_painb6\t] {ai_pain(2);};\nvoid() zombie_painb6\t=[\t$painb6,\tzombie_painb7\t] {};\nvoid() zombie_painb7\t=[\t$painb7,\tzombie_painb8\t] {};\nvoid() zombie_painb8\t=[\t$painb8,\tzombie_painb9\t] {};\nvoid() zombie_painb9\t=[\t$painb9,\tzombie_painb10\t] {sound (self, CHAN_BODY, \"zombie/z_fall.wav\", 1, ATTN_NORM);};\nvoid() zombie_painb10\t=[\t$painb10,\tzombie_painb11\t] {};\nvoid() zombie_painb11\t=[\t$painb11,\tzombie_painb12\t] {};\nvoid() zombie_painb12\t=[\t$painb12,\tzombie_painb13\t] {};\nvoid() zombie_painb13\t=[\t$painb13,\tzombie_painb14\t] {};\nvoid() zombie_painb14\t=[\t$painb14,\tzombie_painb15\t] {};\nvoid() zombie_painb15\t=[\t$painb15,\tzombie_painb16\t] {};\nvoid() zombie_painb16\t=[\t$painb16,\tzombie_painb17\t] {};\nvoid() zombie_painb17\t=[\t$painb17,\tzombie_painb18\t] {};\nvoid() zombie_painb18\t=[\t$painb18,\tzombie_painb19\t] {};\nvoid() zombie_painb19\t=[\t$painb19,\tzombie_painb20\t] {};\nvoid() zombie_painb20\t=[\t$painb20,\tzombie_painb21\t] {};\nvoid() zombie_painb21\t=[\t$painb21,\tzombie_painb22\t] {};\nvoid() zombie_painb22\t=[\t$painb22,\tzombie_painb23\t] {};\nvoid() zombie_painb23\t=[\t$painb23,\tzombie_painb24\t] {};\nvoid() zombie_painb24\t=[\t$painb24,\tzombie_painb25\t] {};\nvoid() zombie_painb25\t=[\t$painb25,\tzombie_painb26\t] {ai_painforward(1);};\nvoid() zombie_painb26\t=[\t$painb26,\tzombie_painb27\t] {};\nvoid() zombie_painb27\t=[\t$painb27,\tzombie_painb28\t] {};\nvoid() zombie_painb28\t=[\t$painb28,\tzombie_run1\t\t] {};\n\nvoid() zombie_painc1\t=[\t$painc1,\tzombie_painc2\t] {sound (self, CHAN_VOICE, \"zombie/z_pain1.wav\", 1, ATTN_NORM);};\nvoid() zombie_painc2\t=[\t$painc2,\tzombie_painc3\t] {};\nvoid() zombie_painc3\t=[\t$painc3,\tzombie_painc4\t] {ai_pain(3);};\nvoid() zombie_painc4\t=[\t$painc4,\tzombie_painc5\t] {ai_pain(1);};\nvoid() zombie_painc5\t=[\t$painc5,\tzombie_painc6\t] {};\nvoid() zombie_painc6\t=[\t$painc6,\tzombie_painc7\t] {};\nvoid() zombie_painc7\t=[\t$painc7,\tzombie_painc8\t] {};\nvoid() zombie_painc8\t=[\t$painc8,\tzombie_painc9\t] {};\nvoid() zombie_painc9\t=[\t$painc9,\tzombie_painc10\t] {};\nvoid() zombie_painc10\t=[\t$painc10,\tzombie_painc11\t] {};\nvoid() zombie_painc11\t=[\t$painc11,\tzombie_painc12\t] {ai_painforward(1);};\nvoid() zombie_painc12\t=[\t$painc12,\tzombie_painc13\t] {ai_painforward(1);};\nvoid() zombie_painc13\t=[\t$painc13,\tzombie_painc14\t] {};\nvoid() zombie_painc14\t=[\t$painc14,\tzombie_painc15\t] {};\nvoid() zombie_painc15\t=[\t$painc15,\tzombie_painc16\t] {};\nvoid() zombie_painc16\t=[\t$painc16,\tzombie_painc17\t] {};\nvoid() zombie_painc17\t=[\t$painc17,\tzombie_painc18\t] {};\nvoid() zombie_painc18\t=[\t$painc18,\tzombie_run1\t] {};\n\nvoid() zombie_paind1\t=[\t$paind1,\tzombie_paind2\t] {sound (self, CHAN_VOICE, \"zombie/z_pain.wav\", 1, ATTN_NORM);};\nvoid() zombie_paind2\t=[\t$paind2,\tzombie_paind3\t] {};\nvoid() zombie_paind3\t=[\t$paind3,\tzombie_paind4\t] {};\nvoid() zombie_paind4\t=[\t$paind4,\tzombie_paind5\t] {};\nvoid() zombie_paind5\t=[\t$paind5,\tzombie_paind6\t] {};\nvoid() zombie_paind6\t=[\t$paind6,\tzombie_paind7\t] {};\nvoid() zombie_paind7\t=[\t$paind7,\tzombie_paind8\t] {};\nvoid() zombie_paind8\t=[\t$paind8,\tzombie_paind9\t] {};\nvoid() zombie_paind9\t=[\t$paind9,\tzombie_paind10\t] {ai_pain(1);};\nvoid() zombie_paind10\t=[\t$paind10,\tzombie_paind11\t] {};\nvoid() zombie_paind11\t=[\t$paind11,\tzombie_paind12\t] {};\nvoid() zombie_paind12\t=[\t$paind12,\tzombie_paind13\t] {};\nvoid() zombie_paind13\t=[\t$paind13,\tzombie_run1\t] {};\n\nvoid() zombie_paine1\t=[\t$paine1,\tzombie_paine2\t] {\nsound (self, CHAN_VOICE, \"zombie/z_pain.wav\", 1, ATTN_NORM);\nself.health = 60;\n};\nvoid() zombie_paine2\t=[\t$paine2,\tzombie_paine3\t] {ai_pain(8);};\nvoid() zombie_paine3\t=[\t$paine3,\tzombie_paine4\t] {ai_pain(5);};\nvoid() zombie_paine4\t=[\t$paine4,\tzombie_paine5\t] {ai_pain(3);};\nvoid() zombie_paine5\t=[\t$paine5,\tzombie_paine6\t] {ai_pain(1);};\nvoid() zombie_paine6\t=[\t$paine6,\tzombie_paine7\t] {ai_pain(2);};\nvoid() zombie_paine7\t=[\t$paine7,\tzombie_paine8\t] {ai_pain(1);};\nvoid() zombie_paine8\t=[\t$paine8,\tzombie_paine9\t] {ai_pain(1);};\nvoid() zombie_paine9\t=[\t$paine9,\tzombie_paine10\t] {ai_pain(2);};\nvoid() zombie_paine10\t=[\t$paine10,\tzombie_paine11\t] {\nsound (self, CHAN_BODY, \"zombie/z_fall.wav\", 1, ATTN_NORM);\nself.solid = SOLID_NOT;\n};\nvoid() zombie_paine11\t=[\t$paine11,\tzombie_paine12\t] {self.nextthink = self.nextthink + 5;self.health = 60;};\nvoid() zombie_paine12\t=[\t$paine12,\tzombie_paine13\t]{\n// see if ok to stand up\nself.health = 60;\nsound (self, CHAN_VOICE, \"zombie/z_idle.wav\", 1, ATTN_IDLE);\nself.solid = SOLID_SLIDEBOX;\nif (!walkmove (0, 0))\n{\n\tself.think = zombie_paine11;\n\tself.solid = SOLID_NOT;\n\treturn;\n}\n};\nvoid() zombie_paine13\t=[\t$paine13,\tzombie_paine14\t] {};\nvoid() zombie_paine14\t=[\t$paine14,\tzombie_paine15\t] {};\nvoid() zombie_paine15\t=[\t$paine15,\tzombie_paine16\t] {};\nvoid() zombie_paine16\t=[\t$paine16,\tzombie_paine17\t] {};\nvoid() zombie_paine17\t=[\t$paine17,\tzombie_paine18\t] {};\nvoid() zombie_paine18\t=[\t$paine18,\tzombie_paine19\t] {};\nvoid() zombie_paine19\t=[\t$paine19,\tzombie_paine20\t] {};\nvoid() zombie_paine20\t=[\t$paine20,\tzombie_paine21\t] {};\nvoid() zombie_paine21\t=[\t$paine21,\tzombie_paine22\t] {};\nvoid() zombie_paine22\t=[\t$paine22,\tzombie_paine23\t] {};\nvoid() zombie_paine23\t=[\t$paine23,\tzombie_paine24\t] {};\nvoid() zombie_paine24\t=[\t$paine24,\tzombie_paine25\t] {};\nvoid() zombie_paine25\t=[\t$paine25,\tzombie_paine26\t] {ai_painforward(5);};\nvoid() zombie_paine26\t=[\t$paine26,\tzombie_paine27\t] {ai_painforward(3);};\nvoid() zombie_paine27\t=[\t$paine27,\tzombie_paine28\t] {ai_painforward(1);};\nvoid() zombie_paine28\t=[\t$paine28,\tzombie_paine29\t] {ai_pain(1);};\nvoid() zombie_paine29\t=[\t$paine29,\tzombie_paine30\t] {};\nvoid() zombie_paine30\t=[\t$paine30,\tzombie_run1\t\t] {};\n\nvoid() zombie_die =\n{\n\tsound (self, CHAN_VOICE, \"zombie/z_gib.wav\", 1, ATTN_NORM);\n\tThrowHead (\"progs/h_zombie.mdl\", self.health);\n\tThrowGib (\"progs/gib1.mdl\", self.health);\n\tThrowGib (\"progs/gib2.mdl\", self.health);\n\tThrowGib (\"progs/gib3.mdl\", self.health);\n};\n\n/*\n=================\nzombie_pain\n\nZombies can only be killed (gibbed) by doing 60 hit points of damage\nin a single frame (rockets, grenades, quad shotgun, quad nailgun).\n\nA hit of 25 points or more (super shotgun, quad nailgun) will allways put it\ndown to the ground.\n\nA hit of from 10 to 40 points in one frame will cause it to go down if it\nhas been twice in two seconds, otherwise it goes into one of the four\nfast pain frames.\n\nA hit of less than 10 points of damage (winged by a shotgun) will be ignored.\n\nFIXME: don't use pain_finished because of nightmare hack\n=================\n*/\nvoid(entity attacker, float take) zombie_pain =\n{\n\tlocal float r;\n\n\tself.health = 60;\t\t// allways reset health\n\n\tif (take < 9)\n\t\treturn;\t\t\t\t// totally ignore\n\n\tif (self.inpain == 2)\n\t\treturn;\t\t\t// down on ground, so don't reset any counters\n\n// go down immediately if a big enough hit\n\tif (take >= 25)\n\t{\n\t\tself.inpain = 2;\n\t\tzombie_paine1 ();\n\t\treturn;\n\t}\n\t\n\tif (self.inpain)\n\t{\n// if hit again in next gre seconds while not in pain frames, definately drop\n\t\tself.pain_finished = time + 3;\n\t\treturn;\t\t\t// currently going through an animation, don't change\n\t}\n\t\n\tif (self.pain_finished > time)\n\t{\n// hit again, so drop down\n\t\tself.inpain = 2;\n\t\tzombie_paine1 ();\n\t\treturn;\n\t}\n\n// gp into one of the fast pain animations\t\n\tself.inpain = 1;\n\n\tr = random();\n\tif (r < 0.25)\n\t\tzombie_paina1 ();\n\telse if (r <  0.5)\n\t\tzombie_painb1 ();\n\telse if (r <  0.75)\n\t\tzombie_painc1 ();\n\telse\n\t\tzombie_paind1 ();\n};\n\n//============================================================================\n\nvoid (entity stuff) spawn_zombie =\n{\n\tlocal entity zombie;\n\tlocal entity te;\n\n\tte = findradius (stuff.origin, 80);\n\twhile (te)\n\t{\n\t\tif (te.classname == \"player\" || te.classname == \"ighoul\" && te.health > 0)\n\t\t\treturn;\n\n\t\tte = te.chain;\n\t}\n\n\tzombie = spawn ();\n\tself = zombie;\n\tself.origin = stuff.origin;\n\tself.solid = SOLID_SLIDEBOX;\n\tself.movetype = MOVETYPE_STEP;\n\tsetmodel (self, \"progs/tehghoul.mdl\");\n\tsetsize (self, VEC_HULL_MIN, '16 16 40');\n\tself.health = 180;\n\tself.classname = \"ighoul\";\n\tself.netname = \"ghoul\";\n\tself.max_health = self.health;\n\tself.th_stand = zombie_stand1;\n\tself.th_walk = zombie_walk1;\n\tself.th_run = zombie_run1;\n\tself.th_pain = zombie_pain;\n\tself.th_die = zombie_die;\n\tself.th_missile = zombie_missile;\n\tself.target = stuff.target;\n\twalkmonster_start_go ();\n};\n\nvoid() monster_zombie =\n{\n\tprecache_model (\"progs/zombie.mdl\");\n\tprecache_model (\"progs/h_zombie.mdl\");\n\tprecache_model (\"progs/zom_gib.mdl\");\n\n\tprecache_sound (\"zombie/z_idle.wav\");\n\tprecache_sound (\"zombie/z_idle1.wav\");\n\tprecache_sound (\"zombie/z_shot1.wav\");\n\tprecache_sound (\"zombie/z_gib.wav\");\n\tprecache_sound (\"zombie/z_pain.wav\");\n\tprecache_sound (\"zombie/z_pain1.wav\");\n\tprecache_sound (\"zombie/z_fall.wav\");\n\tprecache_sound (\"zombie/z_miss.wav\");\n\tprecache_sound (\"zombie/z_hit.wav\");\n\tprecache_sound (\"zombie/idle_w2.wav\");\n\n\tself.solid = SOLID_SLIDEBOX;\n\tself.movetype = MOVETYPE_STEP;\n\n\tsetmodel (self, \"progs/zombie.mdl\");\n\n\tsetsize (self, '-16 -16 -24', '16 16 40');\n\tself.health = 60;\n\n\tself.th_stand = zombie_stand1;\n\tself.th_walk = zombie_walk1;\n\tself.th_run = zombie_run1;\n\tself.th_pain = zombie_pain;\n\tself.th_die = zombie_die;\n\tself.th_missile = zombie_missile;\n\n\tif (self.spawnflags & SPAWN_CRUCIFIED)\n\t{\n\t\tself.movetype = MOVETYPE_NONE;\n\t\tzombie_cruc1 ();\n\t}\n\telse\n\t\twalkmonster_start();\n};\n"
  },
  {
    "path": "quakec/menusys/README.TXT",
    "content": "This directory contains my menusys mod (with menusys qc library).\r\nThis mod is provided to serve as a base mod for menuqc or menus within csqc.\r\n\r\n\r\nLicense:\r\nThere is no explicit license attached to this code. This means you're free to do what you want with it, however...\r\n1) there is absolutely no warrenty at all. it works for me, but might not even compile for you.\r\n2) I make no assurances that small sections of code, comments, or constants, or really anything, wasn't inadvertantly copied from a GPL source. I'm not aware of anything not otherwise obvious from looking at a config or just running the game (ie: cvar/setting names). Really I'm just trying to cover my arse here.\r\n3) people that don't share their code are selfish, and deserve their inevitable HD crashes! yay backups :)\r\n\r\n\r\n\r\nQuick Start:\r\nTo add a new item onto the menus, you can open up the relavent menu/*.qc file, find an item of the same widget type, and dupe then edit that line for your new item, following the example given.\r\nThere's often a little extra logic used to center the menu vertically, so you may also need to tweak some numbers before anything is added.\r\nYou should NOT need to edit menusys/*.qc - you can if you want, but I wouldn't recommend it to beginners.\r\nTo add a new menu, open your .src file, find the concommandslist macro and add an extra line following the example given. This provides you with console command -> function mappings (as well as including the right qc file, if specified - hurrah for each menu being its own qc file). Then you can just copy+paste one of my menus and tweak it a little.\r\n\r\nSome more detailed info on menusys:\r\nThe menusys library is heirachical. It uses the concept of a 'desktop' object as the root node (in csqc, this would normally be expected to provide the draw calls for the actual game).\r\nThen top-level windows/menus are embedded upon that desktop. And then sliders and buttons etc are normally embedded in those windows/menus. Note that you could directly embed your widgets on the desktop, but this can make using the mouse awkward as there's no easy way to release the mouse so you can interact with things.\r\nMenusys also uses inheritance. Thus, you can easily create your own 'frame' object that inherits from mitem_frame, and thereby acts as a container for other widgets (for example).\r\n\r\n\r\nDP compatibility:\r\nThis mod should also work in DP, however there are some exceptions.\r\nThings not supported by DP obviously cannot be used with DP. This includes 3d models in menuqc, server browsers in csqc, etc.\r\nMy personal focus is on FTE, and thus you will likely want to research DP cvars in order to include any additional DP-only cvars that you may wish to include.\r\nI've used a variable called 'dp_workarounds' in many places in order to work around the DP bugs / incompatibilities that are obvious enough for me to notice.\r\nFeel free to provide diffs to fix anything that you notice (make sure they work in FTE too though).\r\n\r\n\r\nMenuQC:\r\nThe presence of menu.dat causes the engine's built-in menus to become inaccessible. This means that any mod wishing to provide a single extra option on the menus needs to rewrite the entire thing from scratch...\r\nAnd menuqc is somewhat mythical as a result - hopefully this mod will challenge that perception by making it slightly more accessible.\r\n\r\n\r\nCSQC:\r\nThe CSQC version of this mod provides a 'full' set of the menus equivelent to the menuqc ones. This typically means that using both becomes redundant. Thus you may well wish to remove most of the menus from csqc so that you can use only the menuqc ones. If you want in-game menus like class customisation then you will likely want those menus in csqc in order to more easily communicate with your server.\r\nAlternatively, if you set pr_csqcformenus to 1 in your default.cfg, then FTE will use your csprogs.dat instead of any menu.dat. This allows you to implement ALL your menus within a single module, which you may find to be a simplification. Note that this does not bypass cheat protections, and thus the csqc version will still generally not work on public servers. However, if you're making a stand-alone mod, this means that you can completely disregard menu.dat module (NOTE: this is only an option in FTE, not DP).\r\nIf your entire game is strictly single player, then look up my PureCSQC mod some time, which does not involve ssqc or even a server at all. Using PureCSQC as a base mod may help some headaches, however you'll need to merge the entry points yourself somehow (PureCSQC incorporates its own partial menu system more as an example of 2d input/drawing than actual menus).\r\nWhen both modules are loaded, the csqc receives escape events and thus invokes its own menu. Because the menus are linked via console commands, you can easily implement the in-game menu in csqc, and all other menus in menuqc.\r\n\r\n\r\nYour mod:\r\nIf you're creating a separate mod with its own special defaults then it is recommended to provide your own default.cfg\r\nYour default.cfg should not only contain bind commands for your default key settings, and defaults for engine cvars, but you should also use the set or seta commands for your own cvars.\r\n(note that if you use '-v -v' on fteqcc's commandline, it will generate a full autocvar listing from each module, including default values and some descriptions - just change any you wish to be flagged for archiving to use seta instead of set).\r\n\r\n\r\nKeybinds:\r\nOne common thing modders want is new keybinds.\r\nyou can either open menus/options_keys.qc and edit the array there.\r\nAlternatively, you can create a bindlist.lst file with <command> <description> quoted pairs on each line. If the command is a hyphen then its treated as a caption.\r\nIf there are too many keys, the frame menuitem thing will add a scrollbar for you, so go ahead and define a hundred different keys.\r\n\r\n\r\nA Mod Options Menu:\r\nThe easy way to add a menu is to just copy+paste an existing one, like the video menu.\r\nFirst, open up the appropriate src file and add a line like the following in the place that should be obvious:\r\ncmd(\"m_mod\",\t\tM_Options_Mod,\tmenu/options_mod.qc)\t\\\r\nThat should create the console command for you, associate that with a function, AND include your menu's qc file into the progs.\r\nNow copy menu/options_video.qc to the obvious name, and start editing it.\r\nOkay, these lines:\r\n\tmitem_exmenu m;\r\n\tm = spawn(mitem_exmenu, item_text:_(\"Video Options\"), item_flags:IF_SELECTABLE, item_command:\"m_options\");\r\n\tdesktop.add(m, RS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MIN | RS_X_MAX_PARENT_MAX|RS_Y_MAX_PARENT_MAX, '0 0', '0 0');\r\n\tdesktop.item_focuschange(m, IF_KFOCUSED);\r\n\tm.totop();\r\ncreates a fullscreen menu thing, they add it into the desktop, they give focus to it, and they make sure that it appears on top of everything else.\r\nthe spawn call is some special class stuff, with named field initialisations. The 'Video Options' thing isn't actually visible, but hey. The m_options string says which console command to issue when the menu is closed (by right-click or escape), this allows you to return to the previous menu.\r\nAnd the following lines:\r\n\tfloat h = 200 * 0.5;\r\n\tmitem_pic banner = spawn(mitem_pic, item_text:\"gfx/p_option.lmp\", item_size_y:24, item_flags:IF_CENTERALIGN);\r\n\tm.add(banner, RS_X_MIN_PARENT_MID|RS_Y_MIN_PARENT_MID | RS_X_MAX_OWN_MIN|RS_Y_MAX_PARENT_MID, [(160-160-banner.item_size_x)*0.5, -h-32], [banner.item_size_x, -h-8]);\r\nadds a static image. woo.\r\nThe RS_ constants are 'ReSize' constants. They say how to calculate the items top-left and bottom-right positions whenever the item is added or its parent is resized. The two vectors are additional offsets from the reference points that the RS flags give.\r\nIn this case, it just centers the image above some 200qu-high area in the middle of the screen.\r\nAnd these lines are fun:\r\n\tmitem_frame fr = spawn(mitem_frame, item_flags: IF_SELECTABLE, frame_hasscroll:TRUE);\r\n\tm.add(fr, RS_X_MIN_PARENT_MID|RS_Y_MIN_PARENT_MID | RS_X_MAX_PARENT_MID|RS_Y_MAX_OWN_MIN, [-160, -h], [160, h*2]);\r\nthey an invisible 'frame' that then holds the widgits that you're actually going to interact with.\r\nWhile that sounds a little useless at first, this provides two things: 1) if there are two many items, the frame_hasscroll value causes it to include a scrollbar, allowing the items to be scrolled without affecting any of the artwork attached to the menu itself (like that title image). and 2) child items are clipped to this frame.\r\nThis line:\r\n\taddmenuback(m);\r\nquite simply just creates a widgit on the menu that darkens the background (insertion order affects draw order, this should be last-ish, so that it is drawn BEFORE any of the other widgits, such that it darkens everything other than the menu.\r\nLines like this:\r\n\tfr.add(menuitemcombo_spawn(_(\"Video Width\"),\t\"vid_width\",\t\t\t\t\t'280 8', _(\r\n\t\t\t\t\t\t\"0\t\\\"Default\\\" \"\r\n\t\t\t\t\t\t\"640\t\\\"640\\\" \"\r\n\t\t\t\t\t\t\"800\t\\\"800\\\" \"\r\n\t\t\t\t\t\t\"1024\t\\\"1024\\\" \"\r\n\t\t\t\t\t\t\"1280 \\\"1280\\\" \"\r\n\t\t\t\t\t\t\"1920\t\\\"1920\\\" \"\r\n\t\t\t\t\t\t)),\tfl, [0, pos], [0, 8]); pos += 8;\r\njust adds a single widgit into the frame's area (its the same add method as before, with the same RS_ flags in the fl argument), so I only really need to explain menuitemcombo_spawn here.\r\nmenuitemcombo_spawn's first argument is the text to put next to it. The _(\"\") weirdness provides some internationalisation thing that I'm far too lazy to go into right now (ask divverent or something for tools to use this properly).\r\nThe second argument is the 'cvar' that its attached to (actually, you can subclass the parent item and handle the get+set events yourself).\r\nThe third is the size of the widget. Generally they should be wide enough for both the text and the value. And about 8 vpixels high.\r\nAnd finally, the fourth argument is the list of possible values, and the text that should be shown if that value is selected. Easy, right?\r\nAlternatively, this line:\r\n\tfr.add(menuitemslider_spawn(_(\"View Size\"),\t\"viewsize\",\t\t\t\t'50 120 10', '280 8'),\tfl, [0, pos], [0, 8]); pos += 8;\r\nspawns a 'slider' opject. Its basically the same as combos, except that the 3rd argument is the mix, max, and step-size values. Note that min+max can be inverted if you want (you should also negate the graduation in this case).\r\nAnd this line:\r\n\tfr.add(menuitemcheck_spawn(_(\"Show Framerate\"),\tdp(\"showfps\", \"show_fps\"), \t'280 8'),\tfl, [0, pos], [0, 8]); pos += 8;\r\nis how you add a checkbox. There is no 'third' argument in this case, just that fourth (okay, so it does have a third, but shush).\r\nThe 'dp' function you see there is part of how my code handles cvar differences between DP and other engines. If cvar_type reports that the first cvar exists, then it uses that. Otherwise it uses the second. Simple.\r\nUnfortunately this only detects whether a cvar exists or not, and can't handle different values very well, which is what the vid_fullscreen hack is all about. Yes, that sort of thing can be really awkward...\r\nAnd finally, in order to add a link to your new menu from the main or options menu or whereever, just open the qc file for that menu and copy one of the add+spawn lines to provide a textitem that invokes your menu's console command when clicked.\r\nEasy.\r\n\r\n\r\nServer browser:\r\nQC-based server browsers have a significant limitation - they have no access to the system clipboard. You can't copy+paste ip numbers etc.\r\nThis is why I've decided to just utilise FTE's normal server browser even with menuqc active. This isn't an option in DP unfortunately, and your mod might be sufficiently different for the engine's to not work too well. Or you might want to exclude other servers...\r\nIf you want to tie your server browser to a specific gamedir, for instance, you can find the MOD_GAMEDIR define, set it to something, and enable it.\r\nMy code uses lots of preprocessor tricks too, sorry about that. You'll get used to them eventually, and hopefully understand why I used them too!...\r\nYou'll likely need to polish it quite a lot before its truely friendly.\r\nIf you do polish it, feel free to send me a patch (assuming it's not too specific to your mod).\r\n"
  },
  {
    "path": "quakec/menusys/cs/entrypoints.qc",
    "content": "mitem_desktop thedesktop;\r\nvoid(mitem_desktop desktop) M_Pop =\r\n{\r\n\tmitem it = desktop.item_kactivechild;\r\n\tif (it)\r\n\t\tit.item_remove();\r\n};\r\nvoid(mitem_desktop desktop) M_ToggleMenu =\r\n{\r\n\tmitem it = desktop.item_kactivechild;\r\n\tif (it)\r\n\t\tit.item_remove();\r\n\telse\r\n\t\tM_Main(desktop);\r\n};\r\n\r\n\r\nfloat(string str) CSQC_ConsoleCommand =\r\n{\r\n\tlocal float args;\r\n\targs = tokenize(str);\r\n\r\n\tswitch(argv(0))\r\n\t{\r\n#define cmd(n,fnc,inc) case n: fnc(thedesktop); return TRUE;\r\n\tconcommandslist\r\n#undef cmd\r\n\tdefault:\r\n\t\treturn FALSE;\r\n\t}\r\n\treturn TRUE;\r\n};\r\n\r\nfloat(float isnew) updateplayer =\r\n{\r\n\tself.drawmask = 1;\r\n\tif (self.entnum == player_localentnum)\r\n\t\tself.renderflags = RF_EXTERNALMODEL;\r\n\telse\r\n\t\tself.renderflags = 0;\r\n\treturn TRUE;\r\n};\r\n//void(float apilevel, string enginename, float engineversion) CSQC_Init =\r\n//{\r\n//\tdeltalisten(\"progs/player.mdl\", updateplayer, 0);\r\n//};\r\n\r\nfloat (float event, float parama, float paramb, float devid) CSQC_InputEvent =\r\n{\r\n\tif (!thedesktop)\r\n\t\treturn event!=IE_KEYUP;\r\n\tif (items_keypress(thedesktop, event, parama, paramb, devid))\r\n\t\treturn TRUE;\r\n#ifdef CSQC_SIMPLE\r\n\tif (event == IE_KEYDOWN && parama == K_ESCAPE)\r\n\t{\r\n\t\tM_Main(thedesktop);\r\n\t\treturn TRUE;\r\n\t}\r\n#endif\r\n\treturn FALSE;\r\n};\r\n\r\n#ifdef CSQC_SIMPLE\r\n//simplecsqc doesn't give us access to the 3d scene.\r\n//instead we have hud+scoreboard entrypoints.\r\n//we're a menu, so we use the scoreboard/overlay entrypoint (one that's skipped when a menu is shown).\r\n//we do need to implement a real scoreboard though. :(\r\nvoid(vector virtsize, float showscores) CSQC_DrawScores =\r\n{\r\n\titems_draw(thedesktop, virtsize);\r\n};\r\n#define mydesktop mitem_desktop\r\n#else\r\n/*The desktop object will not normally draw anything, but you can get the desktop object to do the drawing by overriding its 'drawgame' method.\r\nThe primary advantage of doing the drawing this way is that the menu system can properly handle mouse positions in 3d space with multiple views. The menu system also handles splitscreen efficiently. Note that the menu system will handle clearing the scene and adding entities before this function is called.\r\nYou could instead draw the game then draw the menusystem over the top, if you're more comfortable with that.\r\n*/\r\nclass mydesktop : mitem_desktop\r\n{\r\n\tvirtual void(float seat, vector minpos, vector size) drawgame =\r\n\t{\r\n\t\tsetproperty(VF_DRAWENGINESBAR, TRUE);\r\n/*\r\n\t\tentity playerent = findfloat(world, entnum, player_localentnum);\r\n\t\tif (playerent)\r\n\t\t{\r\n\t\t\tvector vorg = playerent.origin - playerent.gravitydir*getstat(STAT_VIEWHEIGHT);\r\n\t\t\tvector vang = playerent.v_angle;\r\n\t\t\tsetproperty(VF_ORIGIN, vorg);\r\n\t\t\tsetproperty(VF_ANGLES, vang);\r\n\r\n\t\t\tmakevectors(vang);\r\n\t\t\tSetListener(vorg, v_forward, v_right, v_up, playerent.waterlevel==3);\r\n\t\t}\r\n*/\r\n\r\n\t\trenderscene();\r\n\t};\r\n};\r\n\r\nvoid(float width, float height, float do2d) CSQC_UpdateView =\r\n{\r\n\titems_draw(thedesktop, [width, height]);\r\n};\r\n//void(float width, float height, float do2d) CSQC_UpdateView_Loading = CSQC_UpdateView;\r\n#endif\t\r\n\r\n\r\n\r\n//var float autocvar_dp_workarounds_allow = TRUE;\r\n//var float autocvar_dp_workarounds_force = FALSE;\r\n\r\nvoid(float apilevel, string enginename, float engineversion) CSQC_Init =\r\n{\r\n\tdprint(sprintf(\"CSQC: Running on \\\"%s\\\" ver=%g, api=%g\\n\", enginename, engineversion, apilevel));\r\n\r\n\tFingerprintEngine();\r\n\r\n\t//make sure the engine knows what commands we want to handle\r\n#define cmd(n,fnc,inc) registercommand(n);\r\n\tconcommandslist\r\n#undef cmd\r\n\r\n\tthedesktop = spawn(mydesktop);\r\n#ifdef CSQC_SIMPLE\r\n\tHud_Init();\t//make sure the hud images are precached properly, so there's no stalls.\r\n#endif\r\n};"
  },
  {
    "path": "quakec/menusys/csprogs.src",
    "content": "#pragma progs_dat \"../csprogs.dat\"\n\n//#pragma TARGET FTE\n\n#define CSQC_SIMPLE\n#define CSQC\t\t//select the module\n#ifdef CSQC_SIMPLE\n\t#include \"qsextensions.qc\"\t\t//also sets up system defs\n\t#include \"fteextensions.qc\"\t\t//extra stuff\n#else\n\t#include \"fteextensions.qc\"\t\t//also sets up system defs\n#endif\n\n#includelist\nmenusys/mitems.qc\t\t\t//root type\nmenusys/mitems_common.qc\t\t\t//basic types\nmenusys/mitem_desktop.qc\t\t//other sort of root item\nmenusys/mitem_exmenu.qc\t\t//fullscreen/exclusive menus\nmenusys/mitem_edittext.qc\t\t//simple text editor\nmenusys/mitem_tabs.qc\t\t\t//tabs\nmenusys/mitem_colours.qc\t\t//colour picker\nmenusys/mitem_checkbox.qc\t\t//checkbox (boolean thingies)\nmenusys/mitem_slider.qc\t\t//scrollbars\nmenusys/mitem_combo.qc\t\t//multiple-choice thingies\nmenusys/mitem_bind.qc\t\t\t//key binding thingie\nmenusys/mitem_spinnymodel.qc\t//rotating 3d models, used for art/theme.\n#endlist\n\n//define the commands.\n//cmd argments are: Name, Function, Sourcefile(may be empty)\n//note that this list can be expanded in multiple places.\n#define concommandslist\t\t\t\t\t\t\t\t\t\t\t\t\t\\\n\tcmd(\"togglemenu\", \tM_ToggleMenu, \t\t)\t\t\t\t\t\t\t\\\n\tcmd(\"m_main\",\t\tM_Main,\t\t\t\tmenu/main.qc)\t\t\t\t\\\n\tcmd(\"m_pop\",\t\tM_Pop, \t\t\t\t)\t\t\t\t\t\t\t\\\n\tcmd(\"m_options\",\tM_Options, \t\t\tmenu/options.qc)\t\t\t\\\n\tcmd(\"m_keys\",\t\tM_Options_Keys, \tmenu/options_keys.qc)\t\t\\\n\tcmd(\"m_basicopts\",\tM_Options_Basic,\tmenu/options_basic.qc)\t\t\\\n\tcmd(\"m_video\",\t\tM_Options_Video, \tmenu/options_video.qc)\t\t\\\n\tcmd(\"m_effects\",\tM_Options_Effects,\tmenu/options_effects.qc)\t\\\n\tcmd(\"m_audio\",\t\tM_Options_Audio, \tmenu/options_audio.qc)\t\t\\\n\tcmd(\"m_particles\",\tM_Options_Particles,menu/options_particles.qc)\t\\\n\tcmd(\"m_hud\",\t\tM_Options_Hud,\t\tmenu/options_hud.qc)\t\t\\\n\tcmd(\"m_load\",\t\tM_Load, \t\t\tmenu/loadsave.qc)\t\t\t\\\n\tcmd(\"m_save\",\t\tM_Save, \t\t\t)\t\t\t\t\t\t\t\\\n\tcmd(\"m_quit\",\t\tM_Quit, \t\t\tmenu/quit.qc)\t\t\t\t\\\n\tcmd(\"m_newgame\",\tM_NewGame, \t\t\tmenu/newgame.qc)\t\t\t\\\n\tcmd(\"m_servers\",\tM_Servers, \t\t\tmenu/servers.qc)\t\t\t\\\n\tcmd(\"m_configs\",\tM_Configs,\t\t\tmenu/options_configs.qc)\t\\\n\tcmd(\"m_reset\",\t\tM_Reset,\t\t\t)\t\t\t\t\t\t\t\\\n\tcmd(\"m_preset\",\t\tM_Preset, \t\t\tmenu/presets.qc)\n\n//make sure all the right files are included\n#define cmd(n,fnc,inc) inc\n#includelist\n\tconcommandslist\n#endlist\n#undef cmd\n\n#include \"cs/hud.qc\"\n#include \"cs/entrypoints.qc\"\n"
  },
  {
    "path": "quakec/menusys/fteextensions.qc",
    "content": "/*\nThis file was generated by FTE Quake 5781, dated 2020-10-26T11:48:50.477494Z.\nThis file can be regenerated by issuing the following command:\npr_dumpplatform -o fteextensions\n(Use the -help arg for a list of available args)\n*/\n#pragma noref 1\n//#pragma flag enable logicops\n#pragma warning error Q101 /*too many parms. The vanilla qcc didn't validate properly, hence why fteqcc normally treats it as a warning.*/\n#pragma warning error Q105 /*too few parms. The vanilla qcc didn't validate properly, hence why fteqcc normally treats it as a warning.*/\n#pragma warning error Q106 /*assignment to constant/lvalue. Define them as var if you want to initialise something.*/\n#pragma warning error Q208 /*system crc unknown. Compatibility goes out of the window if you disable this.*/\n#pragma warning enable F301 /*non-utf-8 strings. Think of the foreigners! Also think of text editors that insist on screwing up your char encodings.*/\n#pragma warning enable F302 /*uninitialised locals. They usually default to 0 in qc (except in recursive functions), but its still probably a bug*/\n#if !defined(CSQC) && !defined(NQSSQC) && !defined(QWSSQC)&& !defined(MENU)\n\t#ifdef QUAKEWORLD\n\t\t#define QWSSQC\n\t#else\n\t\t#define NQSSQC\n\t#endif\n#endif\n#if !defined(SSQC) && (defined(QWSSQC) || defined(NQSSQC))\n\t#define SSQC\n#endif\n#if defined(CSQC) || defined(MENU)\n\t#define DEP_CSQC DEP\n#else\n\t#define DEP_CSQC __deprecated(\"Use CSQC for this\")\n#endif\n#ifndef DEP\n\t#define DEP __deprecated //predefine this if you want to avoid our deprecation warnings.\n#endif\n#ifndef FTEDEP\n\t#define FTEDEP(reason) //for symbols deprecated in FTE that may still be useful/required for other engines\n#endif\n#define FTE_PEXT_SETVIEW /* NQ's svc_setview works correctly even in quakeworld */\n#define DP_ENT_SCALE\n#define FTE_PEXT_LIGHTSTYLECOL\n#define DP_ENT_ALPHA\n#define FTE_PEXT_VIEW2\n#define FTE_PEXT_ACURATETIMINGS\n#define FTE_PEXT_SOUNDDBL\n#define FTE_PEXT_FATNESS\n#define DP_HALFLIFE_MAP\n#define FTE_PEXT_TE_BULLET\n#define FTE_PEXT_HULLSIZE\n#define FTE_PEXT_MODELDBL\n#define FTE_PEXT_ENTITYDBL\n#define FTE_PEXT_ENTITYDBL2\n#define FTE_PEXT_FLOATCOORDS\n#define FTE_PEXT_VWEAP\n#define FTE_PEXT_Q2BSP\n#define FTE_PEXT_Q3BSP\n#define DP_ENT_COLORMOD\n#define FTE_HEXEN2\n#define FTE_PEXT_SPAWNSTATIC\n#define FTE_PEXT_CUSTOMTENTS\n#define FTE_PEXT_256PACKETENTITIES\n#define TEI_SHOWLMP2\n#define DP_GFX_QUAKE3MODELTAGS\n#define FTE_PK3DOWNLOADS\n#define PEXT_CHUNKEDDOWNLOADS\n#define EXT_CSQC_SHARED\n#define PEXT_DPFLAGS\n#define EXT_CSQC\n#define BX_COLOREDTEXT\n#define DP_CON_SET /* The 'set' console command exists, and can be used to create/set cvars. */\n#define DP_CON_SETA /* The 'seta' console command exists, like the 'set' command, but also marks the cvar for archiving, allowing it to be written into the user's config. Use this command in your default.cfg file. */\n#define DP_CSQC_ROTATEMOVES\n#define DP_EF_ADDITIVE\n#define DP_EF_BLUE\n#define DP_EF_FULLBRIGHT\n#define DP_EF_NODEPTHTEST\n#define DP_EF_NODRAW\n#define DP_EF_NOGUNBOB\n#define DP_EF_NOSHADOW\n#define DP_EF_RED\n#define DP_ENT_CUSTOMCOLORMAP\n#define DP_ENT_EXTERIORMODELTOCLIENT\n#define DP_ENT_TRAILEFFECTNUM /* self.traileffectnum=particleeffectnum(\"myeffectname\"); can be used to attach a particle trail to the given server entity. This is equivelent to calling trailparticles each frame. */\n#define DP_ENT_VIEWMODEL\n#define DP_GECKO_SUPPORT\n#define DP_GFX_FONTS\n#define DP_GFX_SKINFILES\n#define DP_GFX_SKYBOX\n#define DP_HALFLIFE_MAP_CVAR\n#define DP_INPUTBUTTONS\n#define DP_LIGHTSTYLE_STATICVALUE\n#define DP_LITSUPPORT\n#define DP_MONSTERWALK /* MOVETYPE_WALK is valid on non-player entities. Note that only players receive acceleration etc in line with none/bounce/fly/noclip movetypes on the player, thus you will have to provide your own accelerations (incluing gravity) yourself. */\n#define DP_MOVETYPEBOUNCEMISSILE\n#define DP_MOVETYPEFOLLOW\n#define DP_QC_ASINACOSATANATAN2TAN\n#define DP_QC_CHANGEPITCH\n#define DP_QC_COPYENTITY\n#define DP_QC_CRC16\n#define DP_QC_CVAR_DEFSTRING\n#define DP_QC_CVAR_STRING\n#define DP_QC_CVAR_TYPE\n#define DP_QC_DIGEST_SHA256\n#define DP_QC_EDICT_NUM\n#define DP_QC_ENTITYDATA\n#define DP_QC_ETOS\n#define DP_QC_FINDCHAIN\n#define DP_QC_FINDCHAINFLOAT\n#define DP_QC_FINDFLAGS\n#define DP_QC_FINDCHAINFLAGS\n#define DP_QC_FINDFLOAT\n#define DP_QC_FS_SEARCH\n#define DP_QC_FS_SEARCH_PACKFILE\n#define DP_QC_GETSURFACE\n#define DP_QC_GETSURFACEPOINTATTRIBUTE\n#define DP_QC_GETTAGINFO\n#define DP_QC_MINMAXBOUND\n#define DP_QC_MULTIPLETEMPSTRINGS /* Superseded by DP_QC_UNLIMITEDTEMPSTRINGS. Functions that return a temporary string will not overwrite/destroy previous temporary strings until at least 16 strings are returned (or control returns to the engine). */\n#define DP_QC_RANDOMVEC\n#define DP_QC_RENDER_SCENE /* clearscene+addentity+setviewprop+renderscene+setmodel are available to menuqc. WARNING: DP advertises this extension without actually supporting it, FTE does actually support it. */\n#define DP_QC_SINCOSSQRTPOW\n#define DP_QC_SPRINTF /* Provides the sprintf builtin, which allows for rich formatting along the lines of C's function with the same name. Not to be confused with QC's sprint builtin. */\n#define DP_QC_STRFTIME\n#define DP_QC_STRING_CASE_FUNCTIONS\n#define DP_QC_STRINGBUFFERS\n#define DP_QC_STRINGCOLORFUNCTIONS\n#define DP_QC_STRREPLACE\n#define DP_QC_TOKENIZEBYSEPARATOR\n#define DP_QC_TRACEBOX\n#define DP_QC_TRACETOSS\n#define DP_QC_TRACE_MOVETYPE_HITMODEL\n#define DP_QC_TRACE_MOVETYPE_WORLDONLY\n#define DP_QC_TRACE_MOVETYPES\n#define DP_QC_UNLIMITEDTEMPSTRINGS /* Supersedes DP_QC_MULTIPLETEMPSTRINGS, superseded by FTE_QC_PERSISTENTTEMPSTRINGS. Specifies that all temp strings will be valid at least until the QCVM returns. */\n#define DP_QC_URI_ESCAPE\n#define DP_QC_URI_GET\n#define DP_QC_URI_POST\n#define DP_QC_VECTOANGLES_WITH_ROLL\n#define DP_QC_VECTORVECTORS\n#define DP_QC_WHICHPACK\n#define DP_QUAKE2_MODEL\n#define DP_QUAKE2_SPRITE\n#define DP_QUAKE3_MODEL\n#define DP_REGISTERCVAR\n#define DP_SND_SOUND7_WIP2\n#define DP_SND_STEREOWAV\n#define DP_SND_OGGVORBIS\n#define DP_SOLIDCORPSE\n#define DP_SPRITE32\n#define DP_SV_BOTCLIENT\n#define DP_SV_CLIENTCAMERA /* Works like svc_setview except also handles pvs. */\n#define DP_SV_CLIENTCOLORS /* Provided only for compatibility with DP. */\n#define DP_SV_CLIENTNAME /* Provided only for compatibility with DP. */\n#define DP_SV_DRAWONLYTOCLIENT\n#define DP_SV_DROPCLIENT /* Equivelent to quakeworld's stuffcmd(self,\"disconnect\\n\"); hack */\n#define DP_SV_EFFECT\n#define DP_SV_EXTERIORMODELFORCLIENT\n#define DP_SV_NODRAWTOCLIENT\n#define DP_SV_PLAYERPHYSICS /* Allows reworking parts of NQ player physics. USE AT OWN RISK - this necessitates NQ physics and is thus guarenteed to break prediction. */\n#define DP_SV_POINTSOUND\n#define DP_SV_PRECACHEANYTIME /* Specifies that the various precache builtins can be called at any time. WARNING: precaches are sent reliably while sound events, modelindexes, and particle events are not. This can mean sounds and particles might not work the first time around, or models may take a while to appear (after the reliables are received and the model is loaded from disk). Always attempt to precache a little in advance in order to reduce these issues (preferably at the start of the map...) */\n#define DP_SV_PRINT /* Says that the print builtin can be used from nqssqc (as well as just csqc), bypassing the developer cvar issues. */\n#define DP_SV_SETCOLOR\n#define DP_SV_SPAWNFUNC_PREFIX\n#define DP_SV_WRITEPICTURE\n#define DP_SV_WRITEUNTERMINATEDSTRING\n#define DP_TE_BLOOD\n#define DP_TE_CUSTOMFLASH\n#define DP_TE_EXPLOSIONRGB\n#define DP_TE_PARTICLECUBE\n#define DP_TE_PARTICLERAIN\n#define DP_TE_PARTICLESNOW\n#define DP_TE_SMALLFLASH\n#define DP_TE_SPARK\n#define DP_TE_STANDARDEFFECTBUILTINS\n#define DP_VIEWZOOM\n#define EXT_BITSHIFT\n#define EXT_DIMENSION_VISIBILITY\n#define EXT_DIMENSION_PHYSICS\n#define EXT_DIMENSION_GHOST\n#define FRIK_FILE\n#define FTE_CALLTIMEOFDAY /* Replication of mvdsv functionality (call calltimeofday to cause 'timeofday' to be called, with arguments that can be saved off to a global). Generally strftime is simpler to use. */\n#define FTE_CSQC_ALTCONSOLES /* The engine tracks multiple consoles. These may or may not be directly visible to the user. */\n#define FTE_CSQC_BASEFRAME /* Specifies that .basebone, .baseframe2, .baselerpfrac, baseframe1time, etc exist in csqc. These fields affect all bones in the entity's model with a lower index than the .basebone field, allowing you to give separate control to the legs of a skeletal model, without affecting the torso animations. */\n#define FTE_CSQC_HALFLIFE_MODELS\n#define FTE_CSQC_SERVERBROWSER /* Provides builtins to query the engine's serverbrowser servers list from ssqc. Note that these builtins are always available in menuqc. */\n#define FTE_CSQC_SKELETONOBJECTS /* Provides container objects for skeletal bone data, which can be modified on a per bone basis if needed. This allows you to dynamically generate animations (or just blend them with greater customisation) instead of being limited to a single animation or two. */\n#define FTE_CSQC_RAWIMAGES /* Provides raw rgba image access to csqc. With this, the csprogs can read textures into qc-accessible memory, modify it, and then upload it to the renderer. */\n#define FTE_CSQC_RENDERTARGETS /* VF_RT_DESTCOLOUR exists and can be used to redirect any rendering to a texture instead of the screen. */\n#define FTE_CSQC_REVERB /* Specifies that the mod can create custom reverb effects. Whether they will actually be used or not depends upon the sound driver. */\n#define FTE_CSQC_WINDOWCAPTION /* Provides csqc with the ability to change the window caption as displayed when running windowed or in the task bar when switched out. */\n#define FTE_ENT_SKIN_CONTENTS /* self.skin = CONTENTS_WATER; makes a brush entity into water. use -16 for a ladder. */\n#define FTE_ENT_UNIQUESPAWNID\n#define FTE_EXTENDEDTEXTCODES\n#define FTE_FORCESHADER /* Allows csqc to override shaders on models with an explicitly named replacement. Also allows you to define shaders with a fallback if it does not exist on disk. */\n#define FTE_FORCEINFOKEY /* Provides an easy way to change a user's userinfo from the server. */\n#define FTE_GFX_QUAKE3SHADERS /* specifies that the engine has full support for vanilla quake3 shaders */\n#define FTE_GFX_REMAPSHADER /* With the raw power of stuffcmds, the r_remapshader console command is exposed! This mystical command can be used to remap any shader to another. Remapped shaders that specify $diffuse etc in some form will inherit the textures implied by the surface. */\n#define FTE_GFX_MODELEVENTS /* Provides a query for per-animation events in model files, including from progs/foo.mdl.events files. */\n#define FTE_ISBACKBUFFERED /* Allows you to check if a client has too many reliable messages pending. */\n#define FTE_MEMALLOC /* Allows dynamically allocating memory. Use pointers to access this memory. Memory will not be saved into saved games. */\n#define FTE_MEDIA_CIN /* playfilm command supports q2 cin files. */\n#define FTE_MEDIA_ROQ /* playfilm command supports q3 roq files. */\n#define FTE_MULTIPROGS /* Multiple progs.dat files can be loaded inside the same qcvm. Insert new ones with addprogs inside the 'init' function, and use externvalue+externset to rewrite globals (and hook functions) to link them together. Note that the result is generally not very clean unless you carefully design for it beforehand. */\n#define FTE_MULTITHREADED /* Faux multithreading, allowing multiple contexts to run in sequence. */\n#define FTE_MVD_PLAYERSTATS /* In csqc, getplayerstat can be used to query any player's stats when playing back MVDs. isdemo will return 2 in this case. */\n#define FTE_PART_SCRIPT /* Specifies that the r_particledesc cvar can be used to select a list of particle effects to load from particles/foo.cfg, the format of which is documented elsewhere. */\n#define FTE_PART_NAMESPACES /* Specifies that the engine can use foo.bar to load effect foo from particle description bar. When used via ssqc, this should cause the client to download whatever effects as needed. */\n#define FTE_PART_NAMESPACE_EFFECTINFO /* Specifies that effectinfo.bar can load effects from effectinfo.txt for DP compatibility. */\n#define FTE_QC_BASEFRAME /* Specifies that .basebone and .baseframe exist in ssqc. These fields affect all bones in the entity's model with a lower index than the .basebone field, allowing you to give separate control to the legs of a skeletal model, without affecting the torso animations, from ssqc. */\n#define FTE_QC_FILE_BINARY /* Extends FRIK_FILE with binary read+write, as well as allowing seeking. Requires pointers. */\n#define FTE_QC_CHANGELEVEL_HUB /* Adds an extra argument to changelevel which is carried over to the next map in the 'spawnspot' global. Maps will be saved+reloaded until the extra argument is omitted again, purging all saved maps. Saved games will contain a copy of each preserved map. parm1-parm64 globals can be used, giving more space to transfer more player data. */\n#define FTE_QC_CHECKCOMMAND /* Provides a way to test if a console command exists, and whether its a command/alias/cvar. Does not say anything about the expected meanings of any arguments or values. */\n#define FTE_QC_CHECKPVS\n#define FTE_QC_CROSSPRODUCT\n#define FTE_QC_CUSTOMSKINS /* The engine supports the use of q3 skins, as well as the use of such skin 'files' to specify rich top+bottom colours, qw skins, geomsets, or texture composition even on non-players.. */\n#define FTE_QC_DIGEST_SHA1\n#define FTE_QC_DIGEST_SHA224\n#define FTE_QC_DIGEST_SHA384\n#define FTE_QC_DIGEST_SHA512\n#define FTE_QC_FS_SEARCH_SIZEMTIME\n#define FTE_QC_HARDWARECURSORS /* setcursormode exists in both csqc+menuqc, and accepts additional arguments to specify a cursor image to use when this module has focus. If the image exceeds hardware limits (or hardware cursors are unsupported), it will be emulated using regular draws - this at least still avoids conflicting cursors as only one will ever be used, even if console+menu+csqc are all overlayed. */\n#define FTE_QC_HASHTABLES /* Provides efficient string-based lookups. */\n#define FTE_QC_INFOKEY /* QuakeWorld's infokey builtin works, and reports at least name+topcolor+bottomcolor+ping(in ms)+ip(unmasked, but not always ipv4)+team(aka bottomcolor in nq). Does not require actual localinfo/serverinfo/userinfo, but they're _highly_ recommended to any engines with csqc */\n#define FTE_QC_INTCONV /* Provides string<>int conversions, including hex representations. */\n#define FTE_QC_MATCHCLIENTNAME\n#define FTE_QC_MULTICAST /* QuakeWorld's multicast builtin works along with MSG_MULTICAST, but also with unicast support. */\n#define FTE_QC_PAUSED\n#define FTE_QC_PERSISTENTTEMPSTRINGS /* Supersedes DP_QC_MULTIPLETEMPSTRINGS. Temp strings are garbage collected automatically, and do not expire while they're still in use. This makes strzone redundant. */\n#define FTE_QC_RAGDOLL_WIP\n#define FTE_QC_SENDPACKET /* Allows the use of out-of-band udp packets to/from other hosts. Includes the SV_ParseConnectionlessPacket event. */\n#define FTE_QC_STUFFCMDFLAGS /* Variation on regular stuffcmd that gives control over how spectators/mvds should be treated. */\n#define FTE_QC_TRACETRIGGER\n#define FTE_QUAKE2_CLIENT /* This engine is able to act as a quake2 client */\n#define FTE_QUAKE2_SERVER /* This engine is able to act as a quake2 server */\n#define FTE_QUAKE3_CLIENT /* This engine is able to act as a quake3 client */\n#define FTE_QUAKE3_SERVER /* This engine is able to act as a quake3 server */\n#define FTE_SOLID_LADDER /* Allows a simple trigger to remove effects of gravity (solid 20). obsolete. will prolly be removed at some point as it is not networked properly. Use FTE_ENT_SKIN_CONTENTS */\n#define FTE_SPLITSCREEN /* Client supports splitscreen, controlled via cl_splitscreen. Servers require allow_splitscreen 1 if splitscreen is to be used over the internet. Mods that use csqc will need to be aware for this to work properly. per-client networking may be problematic. */\n#define FTE_SQL /* Provides sql* builtins which can be used for sql database access */\n#define FTE_SQL_SQLITE /* SQL functionality is able to utilise sqlite databases */\n#define FTE_STRINGS /* Extra builtins (and additional behaviour) to make string manipulation easier */\n#define FTE_SV_POINTPARTICLES /* Specifies that particleeffectnum, pointparticles, and trailparticles exist in ssqc as well as csqc. particleeffectnum acts as a precache, allowing ssqc values to be networked up with csqc for use. Use in combination with FTE_PART_SCRIPT+FTE_PART_NAMESPACES to use custom effects. This extension is functionally identical to the DP version, but avoids any misplaced assumptions about the format of the client's particle descriptions. */\n#define FTE_SV_REENTER\n#define FTE_TE_STANDARDEFFECTBUILTINS /* Provides builtins to replace writebytes, with a QW compatible twist. */\n#define FTE_TERRAIN_MAP /* This engine supports .hmp files, as well as terrain embedded within bsp files. */\n#define FTE_RAW_MAP /* This engine supports directly loading .map files, as well as realtime editing of the various brushes. */\n#define KRIMZON_SV_PARSECLIENTCOMMAND /* SSQC's SV_ParseClientCommand function is able to handle client 'cmd' commands. The tokenizing parts also work in csqc. */\n#define NEH_CMD_PLAY2\n#define NEH_RESTOREGAME\n#define QSG_CVARSTRING\n#define QW_ENGINE\n#define QWE_MVD_RECORD /* You can use the easyrecord command to record MVD demos serverside. */\n#define TEI_MD3_MODEL\n#define TENEBRAE_GFX_DLIGHTS /* Allows ssqc to attach rtlights to entities with various special properties. */\n#define ZQ_MOVETYPE_FLY /* MOVETYPE_FLY works on players. */\n#define ZQ_MOVETYPE_NOCLIP /* MOVETYPE_NOCLIP works on players. */\n#define ZQ_MOVETYPE_NONE /* MOVETYPE_NONE works on players. */\n#define ZQ_VWEP\n#define ZQ_QC_STRINGS /* The strings-only subset of FRIK_FILE is supported. */\n\n#ifdef _ACCESSORS\naccessor strbuf : float;\naccessor searchhandle : float;\naccessor hashtable : float;\naccessor infostring : string;\naccessor filestream : float;\naccessor filestream : float;\n#else\n#define strbuf float\n#define searchhandle float\n#define hashtable float\n#define infostring string\n#define filestream float\n#endif\n\nentity self;\t/* The magic me */\n#if defined(CSQC) || defined(SSQC)\nentity other;\t/* Valid in touch functions, this is the entity that we touched. */\nentity world;\t/* The null entity. Hurrah. Readonly after map spawn time. */\nfloat time;\t/* The current game time. Stops when paused. */\n#endif\n#ifdef CSQC\nfloat cltime;\t/* A local timer that ticks relative to local time regardless of latency, packetloss, or pause. */\n#endif\n#if defined(CSQC) || defined(SSQC)\nfloat frametime;\t/* The time since the last physics/render/input frame. */\n#endif\n#ifdef CSQC\nfloat player_localentnum;\t/* This is entity number the player is seeing from/spectating, or the player themself, can change mid-map. */\nfloat player_localnum;\t/* The 0-based player index, valid for getplayerkeyvalue calls. */\nfloat maxclients;\t/* Maximum number of player slots on the server. */\nfloat clientcommandframe;\t/* This is the input-frame sequence. frames < clientcommandframe have been sent to the server. frame==clientcommandframe is still being generated and can still change. */\nfloat servercommandframe;\t/* This is the input-frame that was last acknowledged by the server. Input frames greater than this should be applied to the player's entity. */\n#endif\n#if defined(QWSSQC)\nentity newmis;\t/* A named entity that should be run soon, to reduce the effects of latency. */\n#endif\n#ifdef SSQC\nfloat force_retouch;\t/* If positive, causes all entities to check for triggers. */\n#endif\n#if defined(CSQC) || defined(SSQC)\nstring mapname;\t/* The short name of the map. */\n#endif\n#if defined(NQSSQC)\nfloat deathmatch;\nfloat coop;\nfloat teamplay;\n#endif\n#ifdef SSQC\nfloat serverflags;\nfloat total_secrets;\nfloat total_monsters;\nfloat found_secrets;\nfloat killed_monsters;\nfloat parm1;\t/* Player specific mod-defined values that are transferred from one map to the next. These are set by the qc inside calls to SetNewParms and SetChangeParms, and can then be read on a per-player basis after a call to the setspawnparms builtin. They are not otherwise valid. */\nfloat parm2;\t/* Player specific mod-defined values that are transferred from one map to the next. These are set by the qc inside calls to SetNewParms and SetChangeParms, and can then be read on a per-player basis after a call to the setspawnparms builtin. They are not otherwise valid. */\nfloat parm3;\t/* Player specific mod-defined values that are transferred from one map to the next. These are set by the qc inside calls to SetNewParms and SetChangeParms, and can then be read on a per-player basis after a call to the setspawnparms builtin. They are not otherwise valid. */\nfloat parm4;\t/* Player specific mod-defined values that are transferred from one map to the next. These are set by the qc inside calls to SetNewParms and SetChangeParms, and can then be read on a per-player basis after a call to the setspawnparms builtin. They are not otherwise valid. */\nfloat parm5;\t/* Player specific mod-defined values that are transferred from one map to the next. These are set by the qc inside calls to SetNewParms and SetChangeParms, and can then be read on a per-player basis after a call to the setspawnparms builtin. They are not otherwise valid. */\nfloat parm6;\t/* Player specific mod-defined values that are transferred from one map to the next. These are set by the qc inside calls to SetNewParms and SetChangeParms, and can then be read on a per-player basis after a call to the setspawnparms builtin. They are not otherwise valid. */\nfloat parm7;\t/* Player specific mod-defined values that are transferred from one map to the next. These are set by the qc inside calls to SetNewParms and SetChangeParms, and can then be read on a per-player basis after a call to the setspawnparms builtin. They are not otherwise valid. */\nfloat parm8;\t/* Player specific mod-defined values that are transferred from one map to the next. These are set by the qc inside calls to SetNewParms and SetChangeParms, and can then be read on a per-player basis after a call to the setspawnparms builtin. They are not otherwise valid. */\nfloat parm9;\t/* Player specific mod-defined values that are transferred from one map to the next. These are set by the qc inside calls to SetNewParms and SetChangeParms, and can then be read on a per-player basis after a call to the setspawnparms builtin. They are not otherwise valid. */\nfloat parm10;\t/* Player specific mod-defined values that are transferred from one map to the next. These are set by the qc inside calls to SetNewParms and SetChangeParms, and can then be read on a per-player basis after a call to the setspawnparms builtin. They are not otherwise valid. */\nfloat parm11;\t/* Player specific mod-defined values that are transferred from one map to the next. These are set by the qc inside calls to SetNewParms and SetChangeParms, and can then be read on a per-player basis after a call to the setspawnparms builtin. They are not otherwise valid. */\nfloat parm12;\t/* Player specific mod-defined values that are transferred from one map to the next. These are set by the qc inside calls to SetNewParms and SetChangeParms, and can then be read on a per-player basis after a call to the setspawnparms builtin. They are not otherwise valid. */\nfloat parm13;\t/* Player specific mod-defined values that are transferred from one map to the next. These are set by the qc inside calls to SetNewParms and SetChangeParms, and can then be read on a per-player basis after a call to the setspawnparms builtin. They are not otherwise valid. */\nfloat parm14;\t/* Player specific mod-defined values that are transferred from one map to the next. These are set by the qc inside calls to SetNewParms and SetChangeParms, and can then be read on a per-player basis after a call to the setspawnparms builtin. They are not otherwise valid. */\nfloat parm15;\t/* Player specific mod-defined values that are transferred from one map to the next. These are set by the qc inside calls to SetNewParms and SetChangeParms, and can then be read on a per-player basis after a call to the setspawnparms builtin. They are not otherwise valid. */\nfloat parm16;\t/* Player specific mod-defined values that are transferred from one map to the next. These are set by the qc inside calls to SetNewParms and SetChangeParms, and can then be read on a per-player basis after a call to the setspawnparms builtin. They are not otherwise valid. */\n#endif\n#ifdef CSQC\nfloat intermission;\n#endif\n#if defined(CSQC) || defined(SSQC)\nvector v_forward;\nvector v_up;\nvector v_right;\n#endif\n#ifdef CSQC\nvector view_angles;\t/* +x=DOWN */\n#endif\n#if defined(CSQC) || defined(SSQC)\nfloat trace_allsolid;\nfloat trace_startsolid;\nfloat trace_fraction;\nvector trace_endpos;\nvector trace_plane_normal;\nfloat trace_plane_dist;\nentity trace_ent;\nfloat trace_inopen;\nfloat trace_inwater;\n#endif\n#ifdef CSQC\nfloat input_timelength;\nvector input_angles;\t/* +x=DOWN */\nvector input_movevalues;\nfloat input_buttons;\nfloat input_impulse;\n#endif\n#ifdef SSQC\nentity msg_entity;\nvoid() main;\t/* This function is never called, and is effectively dead code. */\nvoid() StartFrame;\t/* Called at the start of each new physics frame. Player entities may think out of sequence so try not to depend upon explicit ordering too much. */\nvoid() PlayerPreThink;\t/* With Prediction(QW compat/FTE default): Called before the player's input commands are processed.\nNo Prediction(NQ compat): Called AFTER the player's movement intents have already been processed (ie: velocity will have already changed according to input_*, but before the actual position change. */\nvoid() PlayerPostThink;\t/* Called after the player's input commands are processed. */\nvoid() ClientKill;\t/* Called in response to 'cmd kill' (or just 'kill'). */\nvoid() ClientConnect;\t/* Called after the connecting client has finished loading and is ready to receive active entities. Note that this is NOT the first place that a client might be referred to. To determine if the client has csqc active (and kick anyone that doesn't), you can use if(infokeyf(self,INFOKEY_P_CSQCACTIVE)) {sprint(self, \"CSQC is required for this server\\n\");dropclient(self);} */\nvoid() PutClientInServer;\t/* Enginewise, this is only ever called immediately after ClientConnect and is thus a little redundant. Modwise, this is also called for respawning a player etc. */\nvoid() ClientDisconnect;\t/* Called once a client disconnects or times out. Not guarenteed to be called on map changes. */\nvoid() SetNewParms;\t/* Called without context when a new client initially connects (before ClientConnect is even called). This function is expected to only set the parm* globals so that they can be decoded properly later. You should not rely on 'self' being set. */\nvoid() SetChangeParms;\t/* Called for each client on map changes. Should copy various entity fields to the parm* globals. */\n#endif\nvoid end_sys_globals;\n#if defined(CSQC) || defined(SSQC)\n.float modelindex;\t/* This is the model precache index for the model that was set on the entity, instead of having to look up the model according to the .model field. Use setmodel to change it. */\n.vector absmin;\t/* Set by the engine when the entity is relinked (by setorigin, setsize, or setmodel). This is in world coordinates. */\n.vector absmax;\t/* Set by the engine when the entity is relinked (by setorigin, setsize, or setmodel). This is in world coordinates. */\n#endif\n#ifdef SSQC\n.float ltime;\t/* On MOVETYPE_PUSH entities, this is used as an alternative to the 'time' global, and .nextthink is synced to this instead of time. This allows time to effectively freeze if the entity is blocked, ensuring the think happens when the entity reaches the target point instead of randomly. */\n#endif\n#ifdef CSQC\n.float entnum;\t/* The entity number as its known on the server. */\n.float drawmask;\t/* Acts as a filter in the addentities call. */\n.float() predraw;\t/* Called by addentities after the filter and before the entity is actually drawn. Do your interpolation and animation in here. Should return one of the PREDRAW_* constants. */\n#endif\n#if defined(QWSSQC)\n.float lastruntime;\t/* This field used to be used to avoid running an entity multiple times in a single frame due to quakeworld's out-of-order thinks. It is no longer used by FTE due to precision issues, but may still be updated for compatibility reasons. */\n#endif\n#if defined(CSQC) || defined(SSQC)\n.float movetype;\t/* Describes how the entity moves. One of the MOVETYPE_ constants. */\n.float solid;\t/* Describes whether the entity is solid or not, and any special properties infered by that. Must be one of the SOLID_ constants */\n.vector origin;\t/* The current location of the entity in world space. Inline bsp entities (ie: ones placed by a mapper) will typically have a value of '0 0 0' in their neutral pose, as the geometry is offset from that. It is the reference point of the entity rather than the center of its geometry, for non-bsp models, this is often not a significant distinction. */\n.vector oldorigin;\t/* This is often used on players to reset the player back to where they were last frame if they somehow got stuck inside something due to fpu precision. Never change a player's oldorigin field to inside a solid, because that might cause them to become pemanently stuck. */\n.vector velocity;\t/* The direction and speed that the entity is moving in world space. */\n.vector angles;\t/* The eular angles the entity is facing in, in pitch, yaw, roll order. Due to a legacy bug, mdl/iqm/etc formats use +x=UP, bsp/spr/etc formats use +x=DOWN. */\n.vector avelocity;\t/* The amount the entity's angles change by per second. Note that this is direct eular angles, and thus the angular change is non-linear and often just looks buggy if you're changing more than one angle at a time. */\n#endif\n#ifdef CSQC\n.float pmove_flags;\n#endif\n#if defined(NQSSQC)\n.vector punchangle;\n#endif\n#if defined(CSQC) || defined(SSQC)\n.string classname;\t/* Identifies the class/type of the entity. Useful for debugging, also used for loading, but its value is not otherwise significant to the engine, this leaves the mod free to set it to whatever it wants and randomly test strings for values in whatever inefficient way it chooses fit. */\n#endif\n#ifdef CSQC\n.float renderflags;\n#endif\n#if defined(CSQC) || defined(SSQC)\n.string model;\t/* The model name that was set via setmodel, in theory. Often, this is cleared to null to prevent the engine from being seen by clients while not changing modelindex. This behaviour allows inline models to remain solid yet be invisible. */\n.float frame;\t/* The current frame the entity is meant to be displayed in. In CSQC, note the lerpfrac and frame2 fields as well. if it specifies a framegroup, the framegroup will autoanimate in ssqc, but not in csqc. */\n#endif\n#ifdef CSQC\n.float frame1time;\t/* The absolute time into the animation/framegroup specified by .frame. */\n.float frame2;\t/* The alternative frame. Visible only when lerpfrac is set to 1. */\n.float frame2time;\t/* The absolute time into the animation/framegroup specified by .frame2. */\n.float lerpfrac;\t/* If 0, use frame1 only. If 1, use frame2 only. Mix them together for values between. */\n#endif\n#if defined(CSQC) || defined(SSQC)\n.float skin;\t/* The skin index to use. on a bsp entity, setting this to 1 will switch to the 'activated' texture instead. A negative value will be understood as a replacement contents value, so setting it to CONTENTS_WATER will make a movable pool of water. */\n.float effects;\t/* Lots of random flags that change random effects. See EF_* constants. */\n.vector mins;\t/* The minimum extent of the model (ie: the bottom-left coordinate relative to the entity's origin). Change via setsize. May also be changed by setmodel. */\n.vector maxs;\t/* like mins, but in the other direction. */\n.vector size;\t/* maxs-mins. Updated when the entity is relinked (by setorigin, setsize, setmodel) */\n.void() touch;\n#endif\n#ifdef SSQC\n.void() use;\n#endif\n#if defined(CSQC) || defined(SSQC)\n.void() think;\n.void() blocked;\n.float nextthink;\t/* The time at which the entity is next scheduled to fire its think event. For MOVETYPE_PUSH entities, this is relative to that entity's ltime field, for all other entities it is relative to the time gloal. */\n#endif\n#ifdef SSQC\n.entity groundentity;\n.float health;\n.float frags;\n.float weapon;\n.string weaponmodel;\n.float weaponframe;\n.float currentammo;\n.float ammo_shells;\n.float ammo_nails;\n.float ammo_rockets;\n.float ammo_cells;\n.float items;\n.float takedamage;\n#endif\n#if defined(CSQC) || defined(SSQC)\n.entity chain;\n#endif\n#ifdef SSQC\n.float deadflag;\n.vector view_ofs;\n.float button0;\n.float button1;\n.float button2;\n.float impulse;\n.float fixangle;\t/* Forces the clientside view angles to change to the value of .angles (has some lag). If set to 1/TRUE, the server will guess whether to send a delta or an explicit angle. If 2, will always send a delta (due to lag between transmission and acknowledgement, this cannot be spammed reliably). If 3, will always send an explicit angle. */\n.vector v_angle;\t/* The angles a player is viewing. +x is DOWN (pitch, yaw, roll) */\n#endif\n#if defined(NQSSQC)\n.float idealpitch;\n#endif\n#ifdef SSQC\n.string netname;\n#endif\n#if defined(CSQC) || defined(SSQC)\n.entity enemy;\n.float flags;\n.float colormap;\n#endif\n#ifdef SSQC\n.float team;\n.float max_health;\n.float teleport_time;\t/* While active, prevents the player from using the +back command, also blocks waterjumping. */\n.float armortype;\n.float armorvalue;\n.float waterlevel;\n.float watertype;\n.float ideal_yaw;\n.float yaw_speed;\n.entity aiment;\n.entity goalentity;\n.float spawnflags;\n.string target;\n.string targetname;\n.float dmg_take;\n.float dmg_save;\n.entity dmg_inflictor;\n#endif\n#if defined(CSQC) || defined(SSQC)\n.entity owner;\n#endif\n#ifdef SSQC\n.vector movedir;\n.string message;\n.float sounds;\n.string noise;\n.string noise1;\n.string noise2;\n.string noise3;\n#endif\nvoid end_sys_fields;\n#ifdef MENU\nfloat time;\t/* The current local time. Increases while paused. */\n#endif\n#ifdef SSQC\nfloat input_timelength;\nvector input_angles;\t/* +x=DOWN */\nvector input_movevalues;\nfloat input_buttons;\nfloat input_impulse;\n#endif\n#ifdef CSQC\nvector input_cursor_screen;\nvector input_cursor_trace_start;\nvector input_cursor_trace_endpos;\nfloat input_cursor_trace_entnum;\n#endif\n#if defined(CSQC) || defined(SSQC)\nint trace_endcontents;\nint trace_surfaceflags;\nint trace_brush_id;\nint trace_brush_faceid;\nint trace_surface_id;\t/* 1-based. 0 if not known. */\nint trace_bone_id;\t/* 1-based. 0 if not known. typically needs MOVE_HITMODEL. */\nint trace_triangle_id;\t/* 1-based. 0 if not known. */\n#endif\n#ifdef CSQC\nfloat trace_networkentity;\t/* Repots which ssqc entnum was hit when a csqc traceline impacts an ssqc-based brush entity. */\nvector pmove_org;\t/* Reports the origin of the engineside player (after prediction). Does not work when the player is a csqc-owned entity. */\nvector pmove_vel;\t/* Reports the velocity of the engineside player (after prediction). Does not work when the player is a csqc-owned entity. */\nfloat pmove_onground;\t/* Reports the onground state of the engineside player (after prediction). Does not work when the player is a csqc-owned entity. */\n#endif\n#if defined(CSQC) || defined(SSQC)\nvector global_gravitydir = '0 0 -1';\t/* The direction gravity should act in if not otherwise specified per entity. */\nint serverid;\t/* The unique id of this server within the server cluster. */\n#endif\n#ifdef CSQC\n.string message;\t/* Allows the csqc to read the map description from the server. */\n#endif\n#ifdef SSQC\n.float button3;\n.float button4;\n.float button5;\n.float button6;\n.float button7;\n.float button8;\n#endif\n#if defined(NQSSQC)\n.float buttonuse;\t/* For DP compat only. */\n.float buttonchat;\t/* For DP compat only. */\n.float cursor_active;\t/* For DP compat only. */\n.float button9;\t/* For DP compat only. */\n.float button10;\t/* For DP compat only. */\n.float button11;\t/* For DP compat only. */\n.float button12;\t/* For DP compat only. */\n.float button13;\t/* For DP compat only. */\n.float button14;\t/* For DP compat only. */\n.float button15;\t/* For DP compat only. */\n.float button16;\t/* For DP compat only. */\n.vector cursor_screen;\t/* For DP compat only. */\n.vector cursor_start;\t/* For DP compat only. */\n.vector cursor_impact;\t/* For DP compat only. */\n.entity cursor_entitynumber;\t/* For DP compat only. */\n.float lastruntime;\t/* This field used to be used to avoid running an entity multiple times in a single frame due to quakeworld's out-of-order thinks. It is no longer used by FTE due to precision issues, but may still be updated for compatibility reasons. */\n#endif\n#if defined(CSQC) || defined(QWSSQC)\n.vector punchangle;\n#endif\n#if defined(CSQC) || defined(SSQC)\n.float gravity;\t/* Multiplier applied in addition to sv_gravity (not absolute units), to control the gravity affecting this entity specifically. */\n.float hull;\t/* Overrides the hull used by the entity for walkmove/movetogoal and not traceline/tracebox. */\n.entity movechain;\t/* This is a linked list of entities which will be moved whenever this entity moves, logically they are attached to this entity. */\n.void() chainmoved;\t/* Called when the entity is moved as a result of being part of another entity's .movechain */\n.void(float old, float new) contentstransition;\t/* This function is called when the entity moves between water and air. If specified, default splash sounds will be disabled allowing you to provide your own. */\n.float dimension_solid;\t/* This is the bitmask of dimensions which the entity is solid within. This is not networked, instead csqc traces impacting ssqc entities assumes the ssqc entity to have a dimension_solid of 1. */\n.float dimension_hit;\t/* This is the bitmask of dimensions which the entity will be blocked by. If other.dimension_solid & self.dimension_hit, our traces will impact and not proceed. If its false, the traces will NOT impact, allowing self to pass straight through. */\n.int hitcontentsmaski;\t/* Traces performed for this entity will impact against surfaces that match this contents mask (CONTENTBITS_* constants). */\n__deprecated(\"Does not support mos-ecific contents.\") .float dphitcontentsmask;\t/* Some crappy field that inefficiently requires translating to the native contents flags. Ditch the 'dp', do it properly. */\n.float scale;\t/* Multiplier that resizes the entity. 1 is normal sized, 2 is double sized. scale 0 is remapped to 1. In SSQC, this is limited to 1/16th precision, with a maximum just shy of 16. */\n.float fatness;\t/* How many QuakeUnits to push the entity's verticies along their normals by. */\n.float alpha;\t/* The transparency of the entity. 1 means opaque, 0.0001 means virtually invisible. 0 is remapped to 1, for compatibility. */\n.float modelflags;\t/* Used to override the flags set in the entity's model. Should be set according to the MF_ constants. Use effects|=EF_NOMODELFLAGS to ignore the model's flags completely. The traileffectnum field is more versatile. */\n#endif\n#ifdef SSQC\n.float frame1time;\t/* This controls the time into the framegroup/animation named by .frame, you should increment this value according to frametime or to distance moved, depending on the sort of animation you're attempting. You may wish to avoid incrementing this while lerpfrac is still changing, to avoid wasting parts of the animation. */\n#endif\n#if defined(CSQC) || defined(SSQC)\n.float basebone;\t/* The base* frame animations are equivelent to their non-base versions, except that they only affect bone numbers below the 'basebone' value. This means that the base* animation can affect the legs of a skeletal model independantly of the normal animation fields affecting the torso area. For more complex animation than this, use skeletal objects. */\n.float baseframe;\t/* See basebone */\n.void() customphysics;\t/* Called once each physics frame, overriding the entity's .movetype field and associated logic. You'll probably want to use tracebox to move it through the world. Be sure to call .think as appropriate. */\n.entity tag_entity;\t/* Specifies which entity this entity's origin+angles is 'attached' to. */\n.float tag_index;\t/* Specifies the tag or bone on the parent entity that we're attached to. If this is -1 then the entity is instead a q3-like camera portal, with the tag_entity saying the entity to display for. If tag_entity is world then this is a q3-like portal surface marker with a separate camera (with a tag_entity referring to the portal surface). */\n.float skeletonindex;\t/* This object serves as a container for the skeletal bone states used to override the animation data. */\n.vector colormod;\t/* Provides a colour tint for the entity (does not affect fullbrights). */\n.vector glowmod;\t/* Scaler for an entity's fullbright textures. */\n.vector gravitydir;\t/* Specifies the direction in which gravity acts. Must be normalised. '0 0 0' also means down. Use '0 0 1' if you want the player to be able to run on ceilings. */\n.vector(vector org, vector ang) camera_transform;\t/* A callback that provides portal transform information for portal surfaces attached to this entity. Also used to open up pvs in ssqc. */\n#endif\n#ifdef SSQC\n.float pmove_flags;\n#endif\n#if defined(CSQC) || defined(SSQC)\n.float geomtype;\n.float friction;\n.float erp;\n.float jointtype;\n.float mass;\n.float bouncefactor;\n.float bouncestop;\n#endif\n#if defined(CSQC) || defined(QWSSQC)\n.float idealpitch;\n#endif\n#if defined(CSQC) || defined(SSQC)\n.float pitch_speed;\n.float drawflags;\t/* Various flags that affect lighting values and scaling. Typically set to 96 in quake for proper compatibility with DP_QC_SCALE. */\n.float abslight;\t/* Allows overriding light levels. Use drawflags to state that this field should actually be used. */\n.vector color;\t/* This affects the colour of realtime lights that were enabled via the pflags field. */\n.float light_lev;\t/* This is the radius of an entity's light. This is not normally used by the engine, but is used for realtime lights (ones that are enabled with the pflags field). */\n.float style;\t/* Used by the light util to decide how an entity's light should animate. On an entity with pflags set, this also affects realtime lights. */\n.float pflags;\t/* Realtime lighting flags */\n#endif\n#ifdef SSQC\n.float maxspeed;\n.entity view2;\t/* defines a second viewpoint, typically displayed in a corner of the screen (also punches open pvs). */\n.vector movement;\t/* These are the directions that the player is currently trying to move in (ie: which +forward/+moveright/+moveup etc buttons they have held), expressed relative to that player's angles. Order is forward, right, up. */\n.float vw_index;\t/* This acts as a second modelindex, using the same frames etc. */\n__deprecated(\"Cannot be recorded in MVDs, nor work properly with splitscreen. Use CSQC instead.\") .entity nodrawtoclient;\t/* This entity will not be sent to the player named by this field. They will be invisible and not emit dlights/particles. Does not work in MVD-recorded game. */\n__deprecated(\"Cannot be recorded in MVDs, nor work properly with splitscreen. Use CSQC instead.\") .entity drawonlytoclient;\t/* This entity will be sent *only* to the player named by this field. To other players they will be invisible and not emit dlights/particles. Does not work in MVD-recorded game. */\n__deprecated(\"Redundant. Cannot be recorded in MVDs, nor work properly with splitscreen. Use CSQC instead.\") .entity viewmodelforclient;\t/* This entity will be sent only to the player named by this field, and this entity will be attached to the player's view as an additional weapon model. */\n__deprecated(\"Cannot be recorded in MVDs, nor work properly with splitscreen. Use CSQC instead.\") .entity exteriormodeltoclient;\t/* This entity will be invisible to the player named by this field, except in mirrors or mirror-like surfaces, where it will be visible as normal. It may still cast shadows as normal, and generate lights+particles, depending on client settings. Does not affect how other players see the entity. */\n.entity clientcamera;\t/* Controls which entity to use for this client's camera. */\n.float glow_size;\t/* Some outdated particle trail thing. */\n.float glow_color;\t/* Some outdated particle trail thing. */\n.float glow_trail;\t/* Some outdated particle trail thing. */\n.float traileffectnum;\t/* This should be set to the result of particleeffectnum, in order to attach a custom trail effect to an entity as it moves. */\n.float emiteffectnum;\t/* This should be set to the result of particleeffectnum, in order to continually spawn particles in the direction that this entity faces. */\n__deprecated(\"Does not work with MVDs nor splitscreen.\") .float dimension_see;\t/* This is the dimension mask (bitfield) that the client is allowed to see. Entities and events not in this dimension mask will be invisible. */\n__deprecated(\"Does not work with MVDs nor splitscreen.\") .float dimension_seen;\t/* This is the dimension mask (bitfield) that the client is visible within. Clients that cannot see this dimension mask will not see this entity. */\n__deprecated(\"Does not work with MVDs nor splitscreen.\") .float dimension_ghost;\t/* If this entity is visible only within these dimensions, it will become transparent, as if a ghost. */\n__deprecated(\"Does not work with MVDs nor splitscreen.\") .float dimension_ghost_alpha;\t/* If this entity is subject to dimension_ghost, this is the scaler for its alpha value. If 0, 0.5 will be used instead. */\n.float(entity playerent, float changedflags) SendEntity;\t/* Called by the engine whenever an entity needs to be (re)sent to a client's csprogs, either because SendFlags was set or because data was lost. Must write its data to the MSG_ENTITY buffer. Will be called at the engine's leasure. */\n.float SendFlags;\t/* Indicates that something in the entity has been changed, and that it needs to be updated to all players that can see it. The engine will clear it at some point, with the cleared bits appearing in the 'changedflags' argument of the SendEntity method. */\n__deprecated(\"Use SendFlags instead.\") .float Version;\t/* Obsolete */\n__deprecated(\"Doesn't support RGB player colours.\") .float clientcolors;\n.float viewzoom;\n.float playerclass;\n.float hasted;\n.float light_level;\t/* Used by hexen2 to indicate the light level where the player is standing. */\n.float pvsflags;\t/* Reconfigures when the entity is visible to clients */\n.float uniquespawnid;\t/* Incremented by 1 whenever the entity is respawned. Persists across remove calls, for when the two-second grace period is insufficient. */\nDEP_CSQC .float() customizeentityforclient;\t/* Called just before an entity is sent to a client (non-csqc protocol). This gives you a chance to tailor 'self' according to what 'other' should see. */\n#endif\n#ifdef CSQC\n.float frame3;\t/* Some people just don't understand how to use framegroups... */\n.float frame3time;\t/* .frame3 equivelent of frame1time. */\n.float lerpfrac3;\t/* Weight of .frame3 - .frame's weight is automatically calculated as 1-(lerpfrac+lerpfrac3+lerpfrac4), as a result these fields should NEVER add to above 1. */\n.float frame4;\n.float frame4time;\t/* .frame4 equivelent of frame1time. */\n.float lerpfrac4;\n.float forceshader;\t/* Contains a shader handle used to replace all surfaces upon the entity. */\n.float baseframe2;\t/* See basebone */\n.float baseframe1time;\t/* See basebone */\n.float baseframe2time;\t/* See basebone */\n.float baselerpfrac;\t/* See basebone */\n.float bonecontrol1;\t/* Halflife model format bone controller. On player models, this typically affects the spine's yaw. */\n.float bonecontrol2;\t/* Halflife model format bone controller. On player models, this typically affects the spine's yaw. */\n.float bonecontrol3;\t/* Halflife model format bone controller. On player models, this typically affects the spine's yaw. */\n.float bonecontrol4;\t/* Halflife model format bone controller. On player models, this typically affects the spine's yaw. */\n.float bonecontrol5;\t/* Halflife model format bone controller. This typically affects the mouth. */\n.float subblendfrac;\t/* Weird animation value specific to halflife models. On player models, this typically affects the spine's pitch, or yaw, or... */\n.float subblend2frac;\t/* Weird animation value specific to halflife models. I've no idea what this does, probably nothing for most models. */\n.float basesubblendfrac;\t/* See basebone */\n.float basesubblend2frac;\t/* See basebone */\n#endif\nvoid(float reqid, float responsecode, string resourcebody, int resourcebytes) URI_Get_Callback;\t/* Called as an eventual result of the uri_get builtin. */\n#ifdef SSQC\nvoid() SpectatorConnect;\t/* Called when a spectator joins the game. */\nvoid() SpectatorDisconnect;\t/* Called when a spectator disconnects from the game. */\nvoid() SpectatorThink;\t/* Called each frame for each spectator. */\nvoid(string cmd) SV_ParseClientCommand;\t/* Provides QC with a way to intercept 'cmd foo' commands from the client. Very handy. Self will be set to the sending client, while the 'cmd' argument can be tokenize()d and each element retrieved via argv(argno). Unrecognised cmds MUST be passed on to the clientcommand builtin. */\nvoid(string dest, string from, string cmd, string info) SV_ParseClusterEvent;\t/* Part of cluster mode. Handles cross-node events that were sent via clusterevent, on behalf of the named client. */\nfloat(string sender, string body) SV_ParseConnectionlessPacket;\t/* Provides QC with a way to communicate between servers, or with client server browsers. Sender is the sender's ip. Body is the body of the message. You'll need to add your own password/etc support as required. Self is not valid. */\nvoid(float pauseduration) SV_PausedTic;\t/* For each frame that the server is paused, this function will be called to give the gamecode a chance to unpause the server again. the pauseduration argument says how long the server has been paused for (the time global is frozen and will not increment while paused). Self is not valid. */\nfloat(float newstatus) SV_ShouldPause;\t/* Called to give the qc a change to block pause/unpause requests. Return false for the pause request to be ignored. newstatus is 1 if the user is trying to pause the game. For the duration of the call, self will be set to the player who tried to pause, or to world if it was triggered by a server-side event. */\nvoid() SV_RunClientCommand;\t/* Called each time a player movement packet was received from a client. Self is set to the player entity which should be updated, while the input_* globals specify the various properties stored within the input packet. The contents of this function should be somewaht identical to the equivelent function in CSQC, or prediction misses will occur. If you're feeling lazy, you can simply call 'runstandardplayerphysics' after modifying the inputs. */\nvoid() SV_AddDebugPolygons;\t/* Called each video frame. This is the only place where ssqc is allowed to call the R_BeginPolygon/R_PolygonVertex/R_EndPolygon builtins. This is exclusively for debugging, and will break in anything but single player as it will not be called if the engine is not running both a client and a server. */\nDEP_CSQC void() SV_PlayerPhysics;\t/* Compatibility method to tweak player input that does not reliably work with prediction (prediction WILL break). Mods that care about prediction should use SV_RunClientCommand instead. If pr_no_playerphysics is set to 1, this function will never be called, which will either fix prediction or completely break player movement depending on whether the feature was even useful. */\nvoid() EndFrame;\t/* Called after non-player entities have been run at the end of the physics frame. Player physics is performed out of order and can/will still occur between EndFrame and BeginFrame. */\nstring(string addr, string uinfo, string features)  SV_CheckRejectConnection;\t/* Called to give the mod a chance to ignore connection requests based upon client protocol support or other properties. Use infoget to read the uinfo and features arguments. */\n#endif\n#ifdef CSQC\nvoid(float apilevel, string enginename, float engineversion) CSQC_Init;\t/* Called at startup. enginename and engineversion are arbitary hints and can take any form. enginename should be consistant between revisions, but this cannot truely be relied upon. */\nvoid() CSQC_WorldLoaded;\t/* Called after the server's model+sound precaches have been executed. Gives a chance for the qc to read the entity lump from the bsp (via getentitytoken). */\nvoid() CSQC_Shutdown;\t/* Specifies that the csqc is going down. Save your persistant settings here. */\nvoid(float vwidth, float vheight, float notmenu) CSQC_UpdateView;\t/* Called every single video frame. The CSQC is responsible for rendering the entire screen. */\nvoid(float vwidth, float vheight, float notmenu) CSQC_UpdateViewLoading;\t/* Alternative to CSQC_UpdateView, called when the engine thinks there should be a loading screen. If present, will inhibit the engine's normal loading screen, deferring to qc to draw it. */\nvoid(vector viewsize, float scoresshown) CSQC_DrawHud;\t/* Part of simple csqc, called after drawing the 3d view whenever CSQC_UpdateView is not defined. */\nvoid(vector viewsize, float scoresshown) CSQC_DrawScores;\t/* Part of simple csqc, called after CSQC_DrawHud whenever CSQC_UpdateView is not defined, and when there are no menus/console active. */\nvoid(string msg) CSQC_Parse_StuffCmd;\t/* Gives the CSQC a chance to intercept stuffcmds. Use the tokenize builtin to parse the message. Unrecognised commands would normally be localcmded, but its probably better to drop unrecognised stuffcmds completely. */\nfloat(string msg) CSQC_Parse_CenterPrint;\t/* Gives the CSQC a chance to intercept centerprints. Return true if you wish the engine to otherwise ignore the centerprint. */\nfloat(float save, float take, vector inflictororg) CSQC_Parse_Damage;\t/* Called as a result of player.dmg_save or player.dmg_take being set on the server.\nReturn true to completely inhibit the engine's colour shift and damage rolls, allowing you to do your own thing.\nYou can use punch_roll += (normalize(inflictororg-player.origin)*v_right)*(take+save)*autocvar_v_kickroll; as a modifier for the roll angle should the player be hit from the side, and slowly fade it away over time. */\nvoid(string printmsg, float printlvl) CSQC_Parse_Print;\t/* Gives the CSQC a chance to intercept sprint/bprint builtin calls. CSQC should filter by the client's current msg setting and then pass the message on to the print command, or handle them itself. */\nvoid() CSQC_Parse_Event;\t/* Called when the client receives an SVC_CGAMEPACKET. The csqc should read the data or call the error builtin if it does not recognise the message. */\nfloat(float evtype, float scanx, float chary, float devid) CSQC_InputEvent;\t/* Called whenever a key is pressed, the mouse is moved, etc. evtype will be one of the IE_* constants. The other arguments vary depending on the evtype. Key presses are not guarenteed to have both scan and unichar values set at the same time. */\n__used void() CSQC_Input_Frame;\t/* Called just before each time clientcommandframe is updated. You can edit the input_* globals in order to apply your own player inputs within csqc, which may allow you a convienient way to pass certain info to ssqc. */\nvoid(string rendererdescription) CSQC_RendererRestarted;\t/* Called by the engine after the video was restarted. This serves to notify the CSQC that any render targets that it may have cached were purged, and will need to be regenerated. */\nfloat(string cmd) CSQC_ConsoleCommand;\t/* Called if the user uses any console command registed via registercommand. */\nfloat(string text, string info) CSQC_ConsoleLink;\t/* Called if the user clicks a ^[text\\infokey\\infovalue^] link. Use infoget to read/check each supported key. Return true if you wish the engine to not attempt to handle the link itself.\nWARNING: link text can potentially come from other players, so be careful about what you allow to be changed. */\nvoid(float entnum) CSQC_Ent_Spawn;\t/* Clumsily defined function for compat with DP. Should call spawn, set that ent's entnum field, and return the entity inside the 'self' global which will then be used for fllowing Ent_Updates. MUST NOT PARSE ANY NETWORK DATA (which makes it kinda useless). */\nvoid(float isnew) CSQC_Ent_Update;\t/* Parses the data sent by ssqc's various SendEntity functions (must use the exact same reads as the ssqc used writes - to debug this rule more easily, you may wish to use sv_csqcdebug). 'self' provides context between frames, and self.entnum should normally report which ssqc entity . Be aware that interpolation will need to happen separately. */\nvoid() CSQC_Ent_Remove;\nfloat(float entnum, float channel, string soundname, float vol, float attenuation, vector pos, float pitchmod, float flags) CSQC_Event_Sound;\nfloat() CSQC_Parse_TempEntity;\t/* Please don't use this. Use CSQC_Parse_Event and multicasts instead.\nThe use of serverside protocol translation to handle QW vs NQ protocols mean that you're likely to end up reading slightly different data. Which is bad.\nReturn true to say that you fully handled the tempentity. Return false to have the client attempt to rewind the network stream and parse the message itself. */\n#endif\n#if defined(CSQC) || defined(MENU)\nvoid(string cmdtext) GameCommand;\n#endif\nstring(string uri, string method, string postdata, __in string requestheaders, __inout string responseheaders) Cef_GeneratePage;\t/* Provides an entrypoint to generate pages for the CEF plugin from within QC. Headers are \n-separated key/value pairs (use tokenizebyseparator). */\n#ifdef SSQC\nstring(string uri, string method, string postdata, __in string requestheaders, __inout string responseheaders) HTTP_GeneratePage;\t/* Provides an entrypoint to generate pages for pages requested over http (sv_port_tcp+net_enable_http). Headers are \n-separated key/value pairs (use tokenizebyseparator). Return __NULL__ to let the engine handle it, an empty string for a 404, and any other text for a regular 200 response. */\n#endif\n#if defined(CSQC) || defined(SSQC)\nvoid(float prevprogs) init;\t/* Part of FTE_MULTIPROGS. Called as soon as a progs is loaded, called at a time when entities are not valid. This is the only time when it is safe to call addprogs without field assignment. As it is also called as part of addprogs, this also gives you a chance to hook functions in modules that are already loaded (via externget+externget). */\nvoid() initents;\t/* Part of FTE_MULTIPROGS. Called after fields have been finalized. This is the first point at which it is safe to call spawn(), and is called before any entity fields have been parsed. You can use this entrypoint to send notifications to other modules. */\n#endif\n#ifdef MENU\nvoid() m_init;\nvoid() m_shutdown;\nvoid(vector screensize) m_draw;\t/* Provides the menuqc with a chance to draw. Will be called even if the menu does not have focus, so be sure to avoid that. COMPAT: screensize is not provided in DP. */\nvoid(vector screensize, float opaque) m_drawloading;\t/* Additional drawing function to draw loading screens. If opaque is set, then this function must ensure that the entire screen is overdrawn (even if just by a black drawfill). */\nvoid(string rendererdescription) Menu_RendererRestarted;\t/* Called by the engine after the video was restarted. This serves to notify the MenuQC that any render targets that it may have cached were purged, and will need to be regenerated. */\nfloat(float evtype, float scanx, float chary, float devid) Menu_InputEvent;\t/* If present, this is called instead of m_keydown and m_keyup\nCalled whenever a key is pressed, the mouse is moved, etc. evtype will be one of the IE_* constants. The other arguments vary depending on the evtype. Key presses are not guarenteed to have both scan and unichar values set at the same time. */\n__deprecated(\"Use Menu_InputEvent\") void(float scan, float chr) m_keydown;\n__deprecated(\"Use Menu_InputEvent\") void(float scan, float chr) m_keyup;\nvoid(float wantmode) m_toggle;\nfloat(string cmd) m_consolecommand;\n#endif\n#ifdef SSQC\nfloat parm17, parm18, parm19, parm20, parm21, parm22, parm23, parm24, parm25, parm26, parm27, parm28, parm29, parm30, parm31, parm32;\t/* Additional spawn parms, following the same parmN theme. */\nfloat parm33, parm34, parm35, parm36, parm37, parm38, parm39, parm40, parm41, parm42, parm43, parm44, parm45, parm46, parm47, parm48;\nfloat parm49, parm50, parm51, parm52, parm53, parm54, parm55, parm56, parm57, parm58, parm59, parm60, parm61, parm62, parm63, parm64;\nstring parm_string;\t/* Like the regular parmN globals, but preserves string contents. */\nstring startspot;\t/* Receives the value of the second argument to changelevel from the previous map. */\nvar float dimension_send;\t/* Used by multicast functionality. Multicasts (and related builtins that multicast internally) will only be sent to players where (player.dimension_see & dimension_send) is non-zero. */\n//var float dimension_default = 255;\n/* Default dimension bitmask */\n__unused var string __fullspawndata;\t/* Set by the engine before calls to spawn functions, and is most easily parsed with the tokenize builtin. This allows you to handle halflife's multiple-fields-with-the-same-name (or target-specific fields). */\n#endif\n#if defined(CSQC) || defined(SSQC)\n__used var float physics_mode = 2;\t/* 0: original csqc - physics are not run\n1: DP-compat. Thinks occur, but not true movetypes.\n2: movetypes occur just as they do in ssqc. */\n#endif\n#ifdef CSQC\nfloat gamespeed;\t/* Set by the engine, this is the value of the sv_gamespeed cvar */\nfloat numclientseats;\t/* This is the number of splitscreen clients currently running on this client. */\n#endif\n#if defined(CSQC) || defined(MENU)\nvar vector drawfontscale = '1 1 0';\t/* Specifies a scaler for all text rendering. There are other ways to implement this. */\nfloat drawfont;\t/* Allows you to choose exactly which font is to be used to draw text. Fonts can be registered/allocated with the loadfont builtin. */\nconst float FONT_DEFAULT = 0;\n#endif\n#ifdef CSQC\nfloat(vector angles, float isdelta) CSQC_Parse_SetAngles;\nvoid(float playernum) CSQC_PlayerInfoChanged;\nvoid() CSQC_ServerInfoChanged;\nDEP(\"use CSQC_Event_Sound\") float(float channel, string soundname, vector pos, float vol, float attenuation, float flags) CSQC_ServerSound;\nvoid(int entidx, string newentdata) CSQC_MapEntityEdited;\nfloat clframetime;\nfloat servertime;\nfloat serverprevtime;\nfloat serverdeltatime;\nfloat deathmatch;\nfloat coop;\nint trace_surfaceflagsi;\nstring trace_surfacename;\nint trace_endcontentsi;\nstring trace_dphittexturename;\nDEP(\"Does not support mod-specific contents.\") float trace_dpstartcontents;\nDEP(\"Does not support mod-specific contents.\") float trace_dphitcontents;\nDEP(\"Does not support mod-specific surface flags\") float trace_dphitq3surfaceflags;\nDEP(\"Does not support all mod-specific surface flags.\") float trace_surfaceflagsf;\nDEP(\"Does not support all mod-specific contents.\") float trace_endcontentsf;\nfloat intermission_time;\nvector pmove_mins;\nvector pmove_maxs;\nfloat pmove_jump_held;\nfloat pmove_waterjumptime;\nfloat input_lightlevel;\nfloat input_weapon;\nfloat input_servertime;\nfloat input_clienttime;\nfloat input_cursor_entitynumber;\nfloat dimension_default;\nfloat autocvar_vid_conwidth;\nfloat autocvar_vid_conheight;\nfloat cycle_wrapped;\n#endif\nconst float TRUE = 1;\nconst float FALSE = 0;\t/* File not found... */\nconst float M_PI = 3.14159;\t/* Mathematica Pi constant. */\n#if defined(CSQC) || defined(SSQC)\nconst float MOVETYPE_NONE = 0;\nconst float MOVETYPE_WALK = 3;\nconst float MOVETYPE_STEP = 4;\nconst float MOVETYPE_FLY = 5;\nconst float MOVETYPE_TOSS = 6;\nconst float MOVETYPE_PUSH = 7;\nconst float MOVETYPE_NOCLIP = 8;\nconst float MOVETYPE_FLYMISSILE = 9;\nconst float MOVETYPE_BOUNCE = 10;\nconst float MOVETYPE_BOUNCEMISSILE = 11;\nconst float MOVETYPE_FOLLOW = 12;\nconst float MOVETYPE_6DOF = 30;\t/* A glorified MOVETYPE_FLY. Players using this movetype will get some flightsim-like physics, with fully independant rotations (order-dependant transforms). */\nconst float MOVETYPE_WALLWALK = 31;\t/* Players using this movetype will be able to orient themselves to walls, and then run up them. */\nconst float MOVETYPE_PHYSICS = 32;\t/* Enable the use of ODE physics upon this entity. */\nconst float SOLID_NOT = 0;\nconst float SOLID_TRIGGER = 1;\nconst float SOLID_BBOX = 2;\nconst float SOLID_SLIDEBOX = 3;\nconst float SOLID_BSP = 4;\t/* Does not collide against other SOLID_BSP entities. Normally paired with MOVETYPE_PUSH. */\nconst float SOLID_CORPSE = 5;\t/* Non-solid to SOLID_SLIDEBOX or other SOLID_CORPSE entities. For hitscan weapons to hit corpses, change the player's .hitcontentsmaski value to include CONTENTBIT_CORPSE, perform the traceline, then revert the player's .solid value. */\n__deprecated(\"Obsoleted by .skin=CONTENTS_LADDER\") const float SOLID_LADDER = 20;\t/* Obsolete and may be removed at some point. Use skin=CONTENT_LADDER and solid_bsp or solid_trigger instead. */\nconst float SOLID_PORTAL = 21;\t/* CSG subtraction volume combined with entity transformations on impact. */\nconst float SOLID_BSPTRIGGER = 22;\t/* For complex-shaped trigger volumes, instead of being a pure aabb. */\nconst float SOLID_PHYSICS_BOX = 32;\nconst float SOLID_PHYSICS_SPHERE = 33;\nconst float SOLID_PHYSICS_CAPSULE = 34;\nconst float SOLID_PHYSICS_TRIMESH = 35;\nconst float SOLID_PHYSICS_CYLINDER = 36;\nconst float GEOMTYPE_NONE = -1;\nconst float GEOMTYPE_SOLID = 0;\nconst float GEOMTYPE_BOX = 1;\nconst float GEOMTYPE_SPHERE = 2;\nconst float GEOMTYPE_CAPSULE = 3;\nconst float GEOMTYPE_TRIMESH = 4;\nconst float GEOMTYPE_CYLINDER = 5;\nconst float GEOMTYPE_CAPSULE_X = 6;\nconst float GEOMTYPE_CAPSULE_Y = 7;\nconst float GEOMTYPE_CAPSULE_Z = 8;\nconst float GEOMTYPE_CYLINDER_X = 9;\nconst float GEOMTYPE_CYLINDER_Y = 10;\nconst float GEOMTYPE_CYLINDER_Z = 11;\nconst float JOINTTYPE_FIXED = -1;\nconst float JOINTTYPE_POINT = 1;\nconst float JOINTTYPE_HINGE = 2;\nconst float JOINTTYPE_SLIDER = 3;\nconst float JOINTTYPE_UNIVERSAL = 4;\nconst float JOINTTYPE_HINGE2 = 5;\n#endif\n#ifdef CSQC\nconst float GE_MAXENTS = -1;\t/* Valid for getentity, ignores the entity argument. Returns the maximum number of entities which may be valid, to avoid having to poll 65k when only 100 are used. */\nconst float GE_ACTIVE = 0;\t/* Valid for getentity. Returns whether this entity is known to the client or not. */\nconst float GE_ORIGIN = 1;\t/* Valid for getentity. Returns the interpolated .origin. */\nconst float GE_FORWARD = 2;\t/* Valid for getentity. Returns the interpolated forward vector. */\nconst float GE_RIGHT = 3;\t/* Valid for getentity. Returns the entity's right vector. */\nconst float GE_UP = 4;\t/* Valid for getentity. Returns the entity's up vector. */\nconst float GE_SCALE = 5;\t/* Valid for getentity. Returns the entity .scale. */\nconst float GE_ORIGINANDVECTORS = 6;\t/* Valid for getentity. Returns interpolated .origin, but also sets v_forward, v_right, and v_up accordingly. Use vectoangles(v_forward,v_up) to determine the angles. */\nconst float GE_ALPHA = 7;\t/* Valid for getentity. Returns the entity alpha. */\nconst float GE_COLORMOD = 8;\t/* Valid for getentity. Returns the colormod vector. */\nconst float GE_PANTSCOLOR = 9;\t/* Valid for getentity. Returns the entity's lower color (from .colormap), as a palette range value. */\nconst float GE_SHIRTCOLOR = 10;\t/* Valid for getentity. Returns the entity's lower color (from .colormap), as a palette range value. */\nconst float GE_SKIN = 11;\t/* Valid for getentity. Returns the entity's .skin index. */\nconst float GE_MINS = 12;\t/* Valid for getentity. Guesses the entity's .min vector. */\nconst float GE_MAXS = 13;\t/* Valid for getentity. Guesses the entity's .max vector. */\nconst float GE_ABSMIN = 14;\t/* Valid for getentity. Guesses the entity's .absmin vector. */\nconst float GE_ABSMAX = 15;\t/* Valid for getentity. Guesses the entity's .absmax vector. */\nconst float GE_MODELINDEX = 200;\t/* Valid for getentity. Guesses the entity's .modelindex float. */\nconst float GE_MODELINDEX2 = 201;\t/* Valid for getentity. Guesses the entity's .vw_index float. */\nconst float GE_EFFECTS = 202;\t/* Valid for getentity. Guesses the entity's .effects float. */\nconst float GE_FRAME = 203;\t/* Valid for getentity. Guesses the entity's .frame float. */\nconst float GE_ANGLES = 204;\t/* Valid for getentity. Guesses the entity's .angles vector. */\nconst float GE_FATNESS = 205;\t/* Valid for getentity. Guesses the entity's .fatness float. */\nconst float GE_DRAWFLAGS = 206;\t/* Valid for getentity. Guesses the entity's .drawflags float. */\nconst float GE_ABSLIGHT = 207;\t/* Valid for getentity. Guesses the entity's .abslight float. */\nconst float GE_GLOWMOD = 208;\t/* Valid for getentity. Guesses the entity's .glowmod vector. */\nconst float GE_GLOWSIZE = 209;\t/* Valid for getentity. Guesses the entity's .glowsize float. */\nconst float GE_GLOWCOLOUR = 210;\t/* Valid for getentity. Guesses the entity's .glowcolor float. */\nconst float GE_RTSTYLE = 211;\t/* Valid for getentity. Guesses the entity's .style float. */\nconst float GE_RTPFLAGS = 212;\t/* Valid for getentity. Guesses the entity's .pflags float. */\nconst float GE_RTCOLOUR = 213;\t/* Valid for getentity. Guesses the entity's .color vector. */\nconst float GE_RTRADIUS = 214;\t/* Valid for getentity. Guesses the entity's .light_lev float. */\nconst float GE_TAGENTITY = 215;\t/* Valid for getentity. Guesses the entity's .tag_entity float. */\nconst float GE_TAGINDEX = 216;\t/* Valid for getentity. Guesses the entity's .tag_index float. */\nconst float GE_GRAVITYDIR = 217;\t/* Valid for getentity. Guesses the entity's .gravitydir vector. */\nconst float GE_TRAILEFFECTNUM = 218;\t/* Valid for getentity. Guesses the entity's .traileffectnum float. */\n#endif\n#ifdef SSQC\nconst float DAMAGE_NO = 0;\nconst float DAMAGE_YES = 1;\nconst float DAMAGE_AIM = 2;\n#endif\n#if defined(CSQC) || defined(SSQC)\nconst float CONTENT_EMPTY = -1;\nconst float CONTENT_SOLID = -2;\nconst float CONTENT_WATER = -3;\nconst float CONTENT_SLIME = -4;\nconst float CONTENT_LAVA = -5;\nconst float CONTENT_SKY = -6;\nconst float CONTENT_LADDER = -16;\t/* If this value is assigned to a solid_bsp's .skin field, the entity will become a ladder volume. */\nconst int CONTENTBIT_NONE = 0x00000000i;\nconst int CONTENTBIT_SOLID = 0x00000001i;\nconst int CONTENTBIT_LAVA = 0x00000008i;\nconst int CONTENTBIT_SLIME = 0x00000010i;\nconst int CONTENTBIT_WATER = 0x00000020i;\nconst int CONTENTBIT_FTELADDER = 0x00004000i;\t/* Content bit used for .skin=CONTENT_LADDER entities. */\nconst int CONTENTBIT_PLAYERCLIP = 0x00010000i;\nconst int CONTENTBIT_MONSTERCLIP = 0x00020000i;\nconst int CONTENTBIT_BODY = 0x02000000i;\t/* Content bit that indicates collisions against SOLID_BBOX/SOLID_SLIDEBOX entities. */\nconst int CONTENTBIT_CORPSE = 0x04000000i;\t/* Content bit that indicates collisions against SOLID_CORPSE entities. */\nconst int CONTENTBIT_Q2LADDER = 0x20000000i;\t/* Content bit specific to q2bsp (conflicts with q3bsp contents so use with caution). */\nconst int CONTENTBIT_SKY = 0x80000000i;\t/* Content bit somewhat specific to q1bsp (aliases to NODROP in q3bsp), but you should probably check surfaceflags&SURF_SKY as well for q2+q3bsp too. */\nconst int CONTENTBITS_POINTSOLID = CONTENTBIT_SOLID|0x00000002i|CONTENTBIT_BODY;\t/* Bits that traceline would normally consider solid */\nconst int CONTENTBITS_BOXSOLID = CONTENTBIT_SOLID|0x00000002i|CONTENTBIT_BODY|CONTENTBIT_PLAYERCLIP;\t/* Bits that tracebox would normally consider solid */\nconst int CONTENTBITS_FLUID = CONTENTBIT_WATER|CONTENTBIT_SLIME|CONTENTBIT_LAVA|CONTENTBIT_SKY;\nconst float SPA_POSITION = 0;\t/* These SPA_* constants are to specify which attribute is returned by the getsurfacepointattribute builtin */\nconst float SPA_S_AXIS = 1;\nconst float SPA_T_AXIS = 2;\nconst float SPA_R_AXIS = 3;\t/* aka: SPA_NORMAL */\nconst float SPA_TEXCOORDS0 = 4;\nconst float SPA_LIGHTMAP0_TEXCOORDS = 5;\nconst float SPA_LIGHTMAP0_COLOR = 6;\nconst float CHAN_AUTO = 0;\t/* The automatic channel, play as many sounds on this channel as you want, and they'll all play, however the other channels will replace each other. */\nconst float CHAN_WEAPON = 1;\nconst float CHAN_VOICE = 2;\nconst float CHAN_ITEM = 3;\nconst float CHAN_BODY = 4;\n#endif\n#if defined(QWSSQC)\nconst float CHANF_RELIABLE = 8;\t/* Only valid if the flags argument is not specified. The sound will be sent reliably, which is important if it is intended to replace looping sounds on doors etc. */\n#endif\n#ifdef SSQC\nconst float SOUNDFLAG_RELIABLE = 1;\t/* The sound will be sent reliably, and without regard to phs. */\n#endif\n#ifdef CSQC\nconst float SOUNDFLAG_ABSVOLUME = 16;\t/* The sample's volume is not scaled by the volume cvar. Use with caution */\n#endif\n#if defined(CSQC) || defined(SSQC)\nconst float SOUNDFLAG_FORCELOOP = 2;\t/* The sound will restart once it reaches the end of the sample. */\n#endif\n#ifdef CSQC\nconst float SOUNDFLAG_NOSPACIALISE = 4;\t/* The different audio channels are played at the same volume regardless of which way the player is facing, without needing to use 0 attenuation. */\n#endif\n#if defined(CSQC) || defined(SSQC)\nconst float SOUNDFLAG_NOREVERB = 32;\t/* Disables the use of underwater/reverb effects on this sound effect. */\nconst float SOUNDFLAG_FOLLOW = 64;\t/* The sound's origin will updated to follow the emitting entity. */\n#endif\n#ifdef SSQC\nconst float SOUNDFLAG_UNICAST = 256;\t/* The sound will be sent only by the player specified by msg_entity. Spectators and related splitscreen players will also hear the sound. */\nconst float SOUNDFLAG_SENDVELOCITY = 512;\t/* The entity's current velocity will be sent to the client, only useful if doppler is enabled. */\n#endif\n#if defined(CSQC) || defined(SSQC)\nconst float ATTN_NONE = 0;\t/* Sounds with this attenuation can be heard throughout the map */\nconst float ATTN_NORM = 1;\t/* Standard attenuation */\nconst float ATTN_IDLE = 2;\t/* Extra attenuation so that sounds don't travel too far. */\nconst float ATTN_STATIC = 3;\t/* Even more attenuation to avoid torches drowing out everything else throughout the map. */\n#endif\n#ifdef SSQC\nconst float SVC_CGAMEPACKET = 83;\t/* Direct ssqc->csqc message. Must only be multicast. The data triggers a CSQC_Parse_Event call in the csqc for the csqc to read the contents. The server *may* insert length information for clients connected via proxies which are not able to cope with custom csqc payloads. This should only ever be used in conjunction with the MSG_MULTICAST destination. */\n#endif\n#if defined(CSQC) || defined(SSQC)\nconst float TE_SPIKE = 0;\nconst float TE_SUPERSPIKE = 1;\n#endif\n#if defined(CSQC) || defined(QWSSQC)\nconst float TE_GUNSHOT = 2;\nconst float TE_EXPLOSION = 3;\n#endif\n#if defined(NQSSQC)\nconst float TE_GUNSHOT = 2;\nconst float TE_EXPLOSION = 3;\n#endif\n#if defined(CSQC) || defined(SSQC)\nconst float TE_TAREXPLOSION = 4;\nconst float TE_LIGHTNING1 = 5;\nconst float TE_LIGHTNING2 = 6;\nconst float TE_WIZSPIKE = 7;\nconst float TE_KNIGHTSPIKE = 8;\nconst float TE_LIGHTNING3 = 9;\nconst float TE_LAVASPLASH = 10;\nconst float TE_TELEPORT = 11;\n#endif\n#if defined(CSQC) || defined(QWSSQC)\nconst float TE_BLOOD = 12;\n#endif\n#if defined(NQSSQC)\nconst float TE_EXPLOSION2 = 12;\n#endif\n#if defined(CSQC) || defined(QWSSQC)\nconst float TE_LIGHTNINGBLOOD = 13;\n#endif\n#if defined(NQSSQC)\nconst float TE_BEAM = 13;\nconst float MSG_BROADCAST = 0;\t/* The byte(s) will be unreliably sent to all players. MSG_ constants are valid arguments to the Write* builtin family. */\nconst float MSG_ONE = 1;\t/* The byte(s) will be reliably sent to the player specified in the msg_entity global. WARNING: in quakeworld servers without network preparsing enabled, this can result in illegible server messages (due to individual reliable messages being split between multiple backbuffers/packets). NQ has larger reliable buffers which avoids this issue, but still kicks the client. */\nconst float MSG_ALL = 2;\t/* The byte(s) will be reliably sent to all players. */\n#endif\n#if defined(QWSSQC)\n__deprecated(\"Use MSG_MULTICAST+multicast(MULTICAST_*)\") const float MSG_BROADCAST;\t/* The byte(s) will be unreliably sent to all players. MSG_ constants are valid arguments to the Write* builtin family. */\n__deprecated(\"Use MSG_MULTICAST+multicast(MULTICAST_ONE_R)\") const float MSG_ONE = 1;\t/* The byte(s) will be reliably sent to the player specified in the msg_entity global. WARNING: in quakeworld servers without network preparsing enabled, this can result in illegible server messages (due to individual reliable messages being split between multiple backbuffers/packets). NQ has larger reliable buffers which avoids this issue, but still kicks the client. */\n__deprecated(\"Use MSG_MULTICAST+multicast(MULTICAST_ALL)\") const float MSG_ALL = 2;\t/* The byte(s) will be reliably sent to all players. */\n#endif\n#ifdef SSQC\nconst float MSG_INIT = 3;\t/* The byte(s) will be written into the signon buffer. Clients will see these messages when they connect later. This buffer is only flushed on map changes, so spamming it _WILL_ result in overflows. */\nconst float MSG_MULTICAST = 4;\t/* The byte(s) will be written into the multicast buffer for more selective sending. Messages sent this way will never be split across packets, and using this for csqc-only messages will not break protocol translation. */\nconst float MSG_ENTITY = 5;\t/* The byte(s) will be written into the entity buffer. This is a special value used only inside 'SendEntity' functions. */\nconst float MULTICAST_ALL = 0;\t/* The multicast message is unreliably sent to all players. MULTICAST_ constants are valid arguments for the multicast builtin, which ignores the specified origin when given this constant. */\nconst float MULTICAST_PHS = 1;\t/* The multicast message is unreliably sent to only players that can potentially hear the specified origin. Its quite loose. */\nconst float MULTICAST_PVS = 2;\t/* The multicast message is unreliably sent to only players that can potentially see the specified origin. */\nconst float MULTICAST_ONE = 6;\t/* The multicast message is unreliably sent to the player (AND ALL TRACKING SPECTATORS) specified in the msg_entity global. The specified origin is ignored. */\nconst float MULTICAST_ONE_NOSPECS = 9;\t/* The multicast message is unreliably sent to the player specified in the msg_entity global. The specified origin is ignored. */\nconst float MULTICAST_ALL_R = 3;\t/* The multicast message is reliably sent to all players. The specified origin is ignored. */\nconst float MULTICAST_PHS_R = 4;\t/* The multicast message is reliably sent to only players that can potentially hear the specified origin. Players might still not receive it if they are out of range. */\nconst float MULTICAST_PVS_R = 5;\t/* The multicast message is reliably sent to only players that can potentially see the specified origin. Players might still not receive it if they cannot see the event. */\nconst float MULTICAST_ONE_R = 7;\t/* The multicast message is reliably sent to the player (AND ALL TRACKING SPECTATORS) specified in the msg_entity global. The specified origin is ignored */\nconst float MULTICAST_ONE_R_NOSPECS = 10;\t/* The multicast message is reliably sent to the player specified in the msg_entity global. The specified origin is ignored */\n#endif\n#if defined(QWSSQC)\nconst float PRINT_LOW = 0;\nconst float PRINT_MEDIUM = 1;\nconst float PRINT_HIGH = 2;\nconst float PRINT_CHAT = 3;\n#endif\n#ifdef SSQC\nconst float PVSF_NORMALPVS = 0;\t/* Filter first by PVS, then filter this entity using tracelines if sv_cullentities is enabled. */\nconst float PVSF_NOTRACECHECK = 1;\t/* Filter strictly by PVS. */\nconst float PVSF_USEPHS = 2;\t/* Send if we're close enough to be able to hear this entity. */\nconst float PVSF_IGNOREPVS = 3;\t/* Ignores pvs. This entity is visible whereever you are on the map. Updates will be sent regardless of pvs or phs */\nconst float PVSF_NOREMOVE = 128;\t/* Once visible to a client, this entity will remain visible. This can be useful for csqc and corpses. While this flag is set, no CSQC_Remove events will be sent for the entity, but this does NOT mean that it will still receive further updates while outside of the pvs. */\nconst string INFOKEY_P_IP = \"ip\";\t/* The apparent ip address of the client. This may be a proxy's ip address. */\nconst string INFOKEY_P_REALIP = \"realip\";\t/* If sv_getrealip is set, this gives the ip as determine using that algorithm. */\nconst string INFOKEY_P_CSQCACTIVE = \"csqcactive\";\t/* Client has csqc enabled. CSQC ents etc will be sent to this player. */\nconst string INFOKEY_P_SVPING = \"svping\";\nconst string INFOKEY_P_GUID = \"guid\";\t/* Some hash string which should be reasonably unique to this player's quake installation. */\nconst string INFOKEY_P_CHALLENGE = \"challenge\";\nconst string INFOKEY_P_USERID = \"*userid\";\nconst string INFOKEY_P_DOWNLOADPCT = \"download\";\t/* The client's download percentage for the current file. Additional files are not known. */\nconst string INFOKEY_P_TRUSTLEVEL = \"trustlevel\";\nconst string INFOKEY_P_PROTOCOL = \"protocol\";\t/* The network protocol the client is using to connect to the server. */\nconst string INFOKEY_P_VIP = \"*VIP\";\t/* 1 if the player has the VIP 'penalty'. */\nconst string INFOKEY_P_ISMUTED = \"*ismuted\";\t/* 1 if the player has the 'mute' penalty and is not allowed to use the say/say_team commands. */\nconst string INFOKEY_P_ISDEAF = \"*isdeaf\";\t/* 1 if the player has the 'deaf' penalty and cannot see other people's say/say_team commands. */\nconst string INFOKEY_P_ISCRIPPLED = \"*ismuted\";\t/* 1 if the player has the cripple penalty, and their movement values are ignored (.movement is locked to 0). */\nconst string INFOKEY_P_ISCUFFED = \"*ismuted\";\t/* 1 if the player has the cuff penalty, and is unable to attack or use impulses(.button0 and .impulse fields are locked to 0). */\nconst string INFOKEY_P_ISLAGGED = \"*ismuted\";\t/* 1 if the player has the fakelag penalty and has an extra 200ms of lag. */\n#endif\n#if defined(CSQC) || defined(SSQC)\nconst string INFOKEY_P_PING = \"ping\";\t/* The player's ping time, in milliseconds. */\nconst string INFOKEY_P_NAME = \"name\";\t/* The player's name. */\nconst string INFOKEY_P_SPECTATOR = \"*spectator\";\t/* Whether the player is a spectator or not. */\nconst string INFOKEY_P_TOPCOLOR = \"topcolor\";\t/* The player's upper/shirt colour (palette index). */\nconst string INFOKEY_P_BOTTOMCOLOR = \"bottomcolor\";\t/* The player's lower/pants/trouser colour (palette index). */\n#endif\n#ifdef CSQC\nconst string INFOKEY_P_TOPCOLOR_RGB = \"topcolor_rgb\";\t/* The player's upper/shirt colour as an rgb value in a format usable with stov. */\nconst string INFOKEY_P_BOTTOMCOLOR_RGB = \"bottomcolor_rgb\";\t/* The player's lower/pants/trouser colour as an rgb value in a format usable with stov. */\nconst string INFOKEY_P_MUTED = \"ignored\";\t/* 0: we can see the result of the player's say/say_team commands.   1: we see no say/say_team messages from this player. Use the ignore command to toggle this value. */\nconst string INFOKEY_P_VOIP_MUTED = \"vignored\";\t/* 0: we can hear this player when they speak (assuming voip is generally enabled). 1: we ignore everything this player says. Use cl_voip_mute to change the values. */\nconst string INFOKEY_P_ENTERTIME = \"entertime\";\t/* Reads the timestamp at which the player entered the game, in terms of csqc's time global. */\nconst string INFOKEY_P_FRAGS = \"frags\";\t/* Reads a player's frag count. */\nconst string INFOKEY_P_PACKETLOSS = \"pl\";\t/* Reads a player's packetloss, as a percentage. */\nconst string INFOKEY_P_VOIPSPEAKING = \"voipspeaking\";\t/* Boolean value that says whether the given player is currently sending voice information. */\nconst string INFOKEY_P_VOIPLOUDNESS = \"voiploudness\";\t/* Only valid for the local player. Gives a value between 0 and 1 to indicate to the user how loud their mic is. */\nconst string SERVERKEY_IP = \"ip\";\t/* The address of the server we connected to. */\nconst string SERVERKEY_SERVERNAME = \"servername\";\t/* The hostname that was last passed to the connect command. */\nconst string SERVERKEY_CONSTATE = \"constate\";\t/* The current connection state. Will be set to one of: disconnected (menu-only mode), active (gamestate received and loaded), connecting(connecting, downloading, or precaching content, aka: loading screen). */\nconst string SERVERKEY_TRANSFERRING = \"transferring\";\t/* Set to the hostname of the server that we are attempting to connect or transfer to. */\nconst string SERVERKEY_LOADSTATE = \"loadstate\";\t/* loadstage, loading image name, current step, max steps\nStages are: 1=connecting, 2=serverside, 3=clientside\nKey will be empty if we are not loading. */\nconst string SERVERKEY_PAUSESTATE = \"pausestate\";\t/* 1 if the server claimed to be paused. 0 otherwise */\nconst string SERVERKEY_DLSTATE = \"dlstate\";\t/* The progress of any current downloads. Empty string if no download is active, otherwise a tokenizable string containing this info:\nfiles-remaining, total-size, unknown-sizes-flag, file-localname, file-remotename, file-percent, file-rate, file-received-bytes, file-total-bytes\nIf the current file info is omitted, then we are waiting for a download to start. */\nconst string SERVERKEY_PROTOCOL = \"protocol\";\t/* The protocol we are connected to the server with. */\nconst string SERVERKEY_MAXPLAYERS = \"maxplayers\";\t/* The number of player/spectator slots allocated on the server. */\n#endif\n#ifdef SSQC\nconst float STUFFCMD_IGNOREINDEMO = 1;\t/* This stuffcmd will NOT be written to mvds/qtv. */\nconst float STUFFCMD_DEMOONLY = 2;\t/* This stuffcmd will ONLY be written into mvds/qtv streams. */\nconst float STUFFCMD_BROADCAST = 4;\t/* The stuffcmd will be broadcast server-wide (according to the mvd filters). */\nconst float STUFFCMD_UNRELIABLE = 8;\t/* The stuffcmd might not arrive. It might also get there faster than ones sent over the reliable channel. */\n#endif\n#if defined(CSQC) || defined(SSQC)\nconst float FL_FLY = 1;\nconst float FL_SWIM = 2;\nconst float FL_CLIENT = 8;\nconst float FL_INWATER = 16;\nconst float FL_MONSTER = 32;\n#endif\n#ifdef SSQC\nconst float FL_GODMODE = 64;\nconst float FL_NOTARGET = 128;\n#endif\n#if defined(CSQC) || defined(SSQC)\nconst float FL_ITEM = 256;\nconst float FL_ONGROUND = 512;\nconst float FL_PARTIALGROUND = 1024;\nconst float FL_WATERJUMP = 2048;\n#endif\n#if defined(CSQC) || defined(NQSSQC)\nconst float FL_JUMPRELEASED = 4096;\n#endif\n#if defined(CSQC) || defined(SSQC)\nconst float FL_FINDABLE_NONSOLID = 16384;\t/* Allows this entity to be found with findradius */\n#endif\n#ifdef SSQC\nconst float FL_LAGGEDMOVE = 65536;\t/* Enables anti-lag on rockets etc. */\n#endif\n#if defined(CSQC) || defined(SSQC)\nconst float MOVE_NORMAL = 0;\nconst float MOVE_NOMONSTERS = 1;\t/* The trace will ignore all non-solid_bsp entities. */\nconst float MOVE_MISSILE = 2;\t/* The trace will use a bbox size of +/- 15 against entities with FL_MONSTER set. */\nconst float MOVE_WORLDONLY = 3;\t/* The trace will ignore everything but the worldmodel. This is useful for to prevent the q3bsp pvs+culling issues that come with spectator modes leaving the world . */\nconst float MOVE_HITMODEL = 4;\t/* Traces will impact the actual mesh of the model instead of merely their bounding box. Should generally only be used for tracelines. Note that this flag is unreliable as an object can animate through projectiles. The bounding box MUST be set to completely encompass the entity or those extra areas will be non-solid (leaving a hole for things to go through). */\nconst float MOVE_TRIGGERS = 16;\t/* This trace type will impact only triggers. It will ignore non-solid entities. */\nconst float MOVE_EVERYTHING = 32;\t/* This type of trace will hit solids and triggers alike. Even non-solid entities. */\n#endif\n#ifdef SSQC\nconst float MOVE_LAGGED = 64;\t/* Will use antilag based upon the player's latency. Traces will be performed against old positions for entities instead of their current origin. */\n#endif\n#if defined(CSQC) || defined(SSQC)\nconst float MOVE_ENTCHAIN = 128;\t/* Returns a list of entities impacted via the trace_ent.chain field */\nconst float MOVE_OTHERONLY = 256;\t/* Traces that use this trace type will collide against *only* the entity specified via the 'other' global, and will ignore all owner/solid_not/dimension etc rules, they will still adhere to contents and bsp/bbox rules though. */\n#endif\nconst float CVAR_TYPEFLAG_EXISTS = 1;\t/* Cvar name actually exists. */\nconst float CVAR_TYPEFLAG_SAVED = 2;\t/* Cvar is flaged for archival (might need cfg_save to actually save). */\nconst float CVAR_TYPEFLAG_PRIVATE = 4;\t/* QC is not allowed to read. */\nconst float CVAR_TYPEFLAG_ENGINE = 8;\t/* Cvar was created by the engine itself (not user/mod created). */\nconst float CVAR_TYPEFLAG_HASDESCRIPTION = 16;\t/* cvar_description will return something (hopefully) useful. */\nconst float CVAR_TYPEFLAG_READONLY = 32;\t/* cvar may not be changed by qc. */\nconst float RESTYPE_MODEL = 0;\t/* RESTYPE_* constants are used as arguments with the resourcestatus builtin. */\nconst float RESTYPE_SOUND = 1;\t/* precache_sound */\nconst float RESTYPE_PARTICLE = 2;\t/* particleeffectnum */\n#if defined(CSQC) || defined(MENU)\nconst float RESTYPE_PIC = 3;\t/* precache_pic. Status results are an amalgomation of the textures used by the named shader. */\nconst float RESTYPE_SKIN = 4;\t/* setcustomskin */\nconst float RESTYPE_TEXTURE = 5;\t/* Individual textures within shaders. These are not directly usable, but may be named as part of a skin file, or a shader. */\n#endif\nconst float RESSTATE_NOTKNOWN = 0;\t/* RESSTATE_* constants are return values from the resourcestatus builtin. The engine doesn't know about the resource if it is in this state. This means you will need to precache it. Attempting to use it anyway may result in warnings, errors, or silently succeed, depending on engine version and resource type. */\nconst float RESSTATE_NOTLOADED = 1;\t/* The resource was precached, but has been flushed and there has not been an attempt to reload it. If you use the resource normally, chances are it'll be loaded but at the cost of a stall. */\nconst float RESSTATE_LOADING = 2;\t/* Resources in this this state are queued for loading, and will be loaded at the engine's convienience. If you attempt to query the resource now, the engine will stall until the result is available. sounds in this state may be delayed, while models/pics/shaders may be invisible. */\nconst float RESSTATE_FAILED = 3;\t/* Resources in this state are unusable/could not be loaded. You will get placeholders or dummy results. Queries will not stall the engine. The engine may display placeholder content. */\nconst float RESSTATE_LOADED = 4;\t/* Resources in this state are finally usable, everything will work okay. Hurrah. Queries will not stall the engine. */\n#if defined(CSQC) || defined(SSQC)\nconst float EF_BRIGHTFIELD = 1;\n#endif\n#if defined(CSQC) || defined(NQSSQC)\nconst float EF_MUZZLEFLASH = 2;\n#endif\n#if defined(CSQC) || defined(SSQC)\nconst float EF_BRIGHTLIGHT = 4;\nconst float EF_DIMLIGHT = 8;\n#endif\n#if defined(QWSSQC)\nconst float EF_FLAG1 = 16;\nconst float EF_FLAG2 = 32;\n#endif\n#if defined(CSQC) || defined(NQSSQC)\nconst float EF_NODRAW = 16;\t/* Disables drawing of the model. Does NOT work on QW players. */\n#endif\n#if defined(CSQC) || defined(SSQC)\nconst float EF_ADDITIVE = 32;\t/* The entity will be drawn with an additive blend. This is NOT supported on players in any quakeworld engine. */\nconst float EF_BLUE = 64;\t/* A blue glow */\nconst float EF_RED = 128;\t/* A red glow */\nconst float EF_GREEN = 262144;\t/* A green glow */\nconst float EF_FULLBRIGHT = 512;\t/* This entity will ignore lighting */\nconst float EF_NOSHADOW = 4096;\t/* This entity will not cast shadows */\nconst float EF_NODEPTHTEST = 8192;\t/* This entity will be drawn over the top of other things that are closer. */\n#endif\n#ifdef SSQC\nconst float EF_NOMODELFLAGS = 8388608;\t/* Surpresses the normal flags specified in the model. */\n#endif\n#if defined(CSQC) || defined(SSQC)\nconst float MF_ROCKET = 1;\nconst float MF_GRENADE = 2;\nconst float MF_GIB = 4;\t/* Regular blood trail */\nconst float MF_ROTATE = 8;\nconst float MF_TRACER = 16;\t/* AKA: green scrag trail */\nconst float MF_ZOMGIB = 32;\t/* Dark blood trail */\nconst float MF_TRACER2 = 64;\t/* AKA: hellknight projectile trail */\nconst float MF_TRACER3 = 128;\t/* AKA: purple vore trail */\n#endif\n#ifdef SSQC\nDEP_CSQC const float SL_ORG_TL = 20;\t/* Used with showpic etc, specifies that the x+y values are relative to the top-left of the screen */\nDEP_CSQC const float SL_ORG_TR = 21;\nDEP_CSQC const float SL_ORG_BL = 22;\nDEP_CSQC const float SL_ORG_BR = 23;\nDEP_CSQC const float SL_ORG_MM = 24;\nDEP_CSQC const float SL_ORG_TM = 25;\nDEP_CSQC const float SL_ORG_BM = 26;\nDEP_CSQC const float SL_ORG_ML = 27;\nDEP_CSQC const float SL_ORG_MR = 28;\n#endif\n#if defined(CSQC) || defined(SSQC)\nconst float PFLAGS_NOSHADOW = 1;\t/* Associated RT lights attached will not cast shadows, making them significantly faster to draw. */\nconst float PFLAGS_CORONA = 2;\t/* Enables support of coronas on the associated rtlights. */\n#endif\n#ifdef SSQC\nconst float PFLAGS_FULLDYNAMIC = 128;\t/* When set in self.pflags, enables fully-customised dynamic lights. Custom rtlight information is not otherwise used. */\n#endif\nconst float EV_STRING = 1;\nconst float EV_FLOAT = 2;\nconst float EV_VECTOR = 3;\nconst float EV_ENTITY = 4;\nconst float EV_FIELD = 5;\nconst float EV_FUNCTION = 6;\nconst float EV_POINTER = 7;\nconst float EV_INTEGER = 8;\nconst float EV_UINT = 9;\nconst float EV_INT64 = 10;\nconst float EV_UINT64 = 11;\nconst float EV_DOUBLE = 12;\nhashtable gamestate;\t/* Special hash table index for hash_add and hash_get. Entries in this table will persist over map changes (and doesn't need to be created/deleted). */\nconst float HASH_REPLACE = 256;\t/* Used with hash_add. Attempts to remove the old value instead of adding two values for a single key. */\nconst float HASH_ADD = 512;\t/* Used with hash_add. The new entry will be inserted in addition to the existing entry. */\n#ifdef CSQC\nconst float STAT_HEALTH = 0;\t/* Player's health. */\nconst float STAT_WEAPONMODELI = 2;\t/* This is the modelindex of the current viewmodel (renamed from the original name 'STAT_WEAPON' due to confusions). */\nconst float STAT_AMMO = 3;\t/* player.currentammo */\nconst float STAT_ARMOR = 4;\nconst float STAT_WEAPONFRAME = 5;\nconst float STAT_SHELLS = 6;\nconst float STAT_NAILS = 7;\nconst float STAT_ROCKETS = 8;\nconst float STAT_CELLS = 9;\nconst float STAT_ACTIVEWEAPON = 10;\t/* player.weapon */\nconst float STAT_TOTALSECRETS = 11;\nconst float STAT_TOTALMONSTERS = 12;\nconst float STAT_FOUNDSECRETS = 13;\nconst float STAT_KILLEDMONSTERS = 14;\nconst float STAT_ITEMS = 15;\t/* self.items | (self.items2<<23). In order to decode this stat properly, you need to use getstatbits(STAT_ITEMS,0,23) to read self.items, and getstatbits(STAT_ITEMS,23,11) to read self.items2 or getstatbits(STAT_ITEMS,28,4) to read the visible part of serverflags, whichever is applicable. */\nconst float STAT_VIEWHEIGHT = 16;\t/* player.view_ofs_z */\nconst float STAT_VIEW2 = 20;\t/* This stat contains the number of the entity in the server's .view2 field. */\nconst float STAT_VIEWZOOM = 21;\t/* Scales fov and sensitiity. Part of DP_VIEWZOOM. */\n#endif\n#if defined(CSQC) || defined(SSQC)\nconst float STAT_USER = 32;\t/* Custom user stats start here (lower values are reserved for engine use). */\n#endif\n#if defined(CSQC) || defined(MENU)\nconst float PRECACHE_PIC_FROMWAD = 1;\t/* Attempt to load it from the legacy gfx.wad file (usually its better to just use a gfx/ prefix instead). */\nconst float PRECACHE_PIC_NOCLAMP = 4;\t/* Texture coords for the pic will not be clamped nor padded nor atlased. */\nconst float PRECACHE_PIC_DOWNLOAD = 256;\t/* If no image could be loaded then attempt to download one from the server. This flag can cause the function to block until completion. (Slow!) */\nconst float PRECACHE_PIC_TEST = 512;\t/* The precache will block until the image is fully loaded, returning a null string on failure. (Slow!) */\nconst float VF_MIN = 1;\t/* The top-left of the 3d viewport in screenspace. The VF_ values are used via the setviewprop/getviewprop builtins. */\nconst float VF_MIN_X = 2;\nconst float VF_MIN_Y = 3;\nconst float VF_SIZE = 4;\t/* The width+height of the 3d viewport in screenspace. */\nconst float VF_SIZE_X = 5;\nconst float VF_SIZE_Y = 6;\nconst float VF_VIEWPORT = 7;\t/* vector+vector. Two argument shortcut for VF_MIN and VF_SIZE */\nconst float VF_FOV = 8;\t/* sets both fovx and fovy. consider using afov instead. */\nconst float VF_FOVX = 9;\t/* horizontal field of view. does not consider aspect at all. */\nconst float VF_FOVY = 10;\t/* vertical field of view. does not consider aspect at all. */\nconst float VF_ORIGIN = 11;\t/* The origin of the view. Not of the player. */\nconst float VF_ORIGIN_X = 12;\nconst float VF_ORIGIN_Y = 13;\nconst float VF_ORIGIN_Z = 14;\nconst float VF_ANGLES = 15;\t/* The angles the view will be drawn at. Not the angle the client reports to the server. */\nconst float VF_ANGLES_X = 16;\nconst float VF_ANGLES_Y = 17;\nconst float VF_ANGLES_Z = 18;\n#endif\n#ifdef CSQC\nconst float VF_DRAWWORLD = 19;\t/* boolean. If set to 1, the engine will draw the world and static/persistant rtlights. If 0, the world will be skipped and everything will be fullbright. */\nconst float VF_DRAWENGINESBAR = 20;\t/* boolean. If set to 1, the sbar will be drawn, and viewsize will be honoured automatically. */\nconst float VF_DRAWCROSSHAIR = 21;\t/* boolean. If set to 1, the engine will draw its default crosshair. */\n#endif\n#if defined(CSQC) || defined(MENU)\nconst float VF_MINDIST = 23;\t/* The distance of the near clip plane from the view position. Should generally not be <=0, as this would introduce NANs. */\nconst float VF_MAXDIST = 24;\t/* The distance of the far clip plane from the view position. If 0, will be considered infinite. */\n#endif\n#ifdef CSQC\nconst float VF_CL_VIEWANGLES = 33;\nconst float VF_CL_VIEWANGLES_X = 34;\nconst float VF_CL_VIEWANGLES_Y = 35;\nconst float VF_CL_VIEWANGLES_Z = 36;\n#endif\n#if defined(CSQC) || defined(MENU)\nconst float VF_PERSPECTIVE = 200;\t/* 1: regular rendering. Fov specifies the angle. 0: isometric-style. Fov specifies the number of Quake Units each side of the viewport, and mindist restrictions are removed, pvs culling should be disabled. */\n#endif\n#ifdef CSQC\n#define VF_LPLAYER VF_ACTIVESEAT\nconst float VF_ACTIVESEAT = 202;\t/* The 'seat' number, used when running splitscreen. */\n#endif\n#if defined(CSQC) || defined(MENU)\nconst float VF_AFOV = 203;\t/* Aproximate fov. Matches the 'fov' cvar. The engine handles the aspect ratio for you. */\nconst float VF_SCREENVSIZE = 204;\t/* Provides a reliable way to retrieve the current virtual screen size (even if the screen is automatically scaled to retain aspect). */\nconst float VF_SCREENPSIZE = 205;\t/* Provides a reliable way to retrieve the current physical screen size (cvars need vid_restart for them to take effect). */\n#endif\n#ifdef CSQC\nconst float VF_VIEWENTITY = 206;\t/* Changes the RF_EXTERNALMODEL flag on entities to match the new selection, and removes entities flaged with RF_VIEWENTITY. Requires cunning use of .entnum and typically requires calling addentities(MASK_VIEWMODEL) too. */\n#endif\n#if defined(CSQC) || defined(MENU)\nconst float VF_RT_DESTCOLOUR = 212;\t/* The texture name to write colour info into, this includes both 3d and 2d drawing.\nAdditional arguments are: format (IMGFMT_*), sizexy.\nWritten to by both 3d and 2d rendering.\nNote that any rendertarget textures may be destroyed on video mode changes or so. Shaders can name render targets by prefixing texture names with '$rt:', or $sourcecolour. */\nconst float VF_RT_DESTCOLOUR1 = 213;\t/* Like VF_RT_DESTCOLOUR, for multiple render targets. */\nconst float VF_RT_DESTCOLOUR2 = 214;\t/* Like VF_RT_DESTCOLOUR, for multiple render targets. */\nconst float VF_RT_DESTCOLOUR3 = 215;\t/* Like VF_RT_DESTCOLOUR, for multiple render targets. */\nconst float VF_RT_SOURCECOLOUR = 209;\t/* The texture name to use with shaders that specify a $sourcecolour map. */\nconst float VF_RT_DEPTH = 210;\t/* The texture name to use as a depth buffer. Also used for shaders that specify $sourcedepth. 1-based. Additional arguments are: format (IMGFMT_D*), sizexy. */\nconst float VF_RT_RIPPLE = 211;\t/* The texture name to use as a ripplemap (target for shaders with 'sort ripple'). Also used for shaders that specify $ripplemap. 1-based. Additional arguments are: format, sizexy. */\nconst float VF_ENVMAP = 220;\t/* The cubemap name to use as a fallback for $reflectcube, if a shader was unable to load one. Note that this doesn't automatically change shader permutations or anything. */\nconst float VF_USERDATA = 221;\t/* Pointer (and byte size) to an array of vec4s. This data is then globally visible to all glsl via the w_user uniform. */\n#endif\n#ifdef CSQC\nconst float VF_SKYROOM_CAMERA = 222;\t/* Controls the camera position of the skyroom (which will be drawn underneath transparent sky surfaces). This should move slightly with the real camera, but not so much that the skycamera enters walls. Requires a skyshader with a blend mode on the first pass (or no passes). */\n#endif\n#if defined(CSQC) || defined(MENU)\nconst float VF_PROJECTIONOFFSET = 224;\t/* vec2 horizontal+vertical offset for the projection matrix, for weird off-centre rendering. */\nconst float DRAWFLAG_NORMAL = 0;\t/* Args for drawpic/drawfill/beginpolygon. Not to be confused with the hexen2-compatibility feature. */\nconst float DRAWFLAG_ADD = 1;\t/* Forces additive blending, overriding any shader settings. */\nconst float DRAWFLAG_MODULATE = 2;\t/* Forces alpha blending, overriding any shader settings. */\nconst float DRAWFLAG_2D = 4;\t/* For use with beginpolygon. The polygon will be drawn to the 2d screen, instead of being added to the 3d scene. */\nconst float DRAWFLAG_TWOSIDED = 1024;\t/* For use with beginpolygon. The polygon will be two-sided without any backface culling. */\nconst float DRAWFLAG_LINES = 2048;\t/* For use with beginpolygon. The surface verticies should be interpreted as a line loop, instead of a triangle fan. */\nconst float IMGFMT_R8G8B8A8 = 1;\t/* Typical 32bit rgba pixel format. */\nconst float IMGFMT_R16G16B16A16F = 2;\t/* Half-Float pixel format. Requires gl3 support. */\nconst float IMGFMT_R32G32B32A32F = 3;\t/* Regular Float pixel format. Requires gl3 support. */\nconst float IMGFMT_D16 = 4;\t/* 16-bit depth pixel format. Must not be used with VF_RT_DESTCOLOUR*. */\nconst float IMGFMT_D24 = 5;\t/* 24-bit depth pixel format. Must not be used with VF_RT_DESTCOLOUR*. */\nconst float IMGFMT_D32 = 6;\t/* 32-bit depth pixel format. Must not be used with VF_RT_DESTCOLOUR*. */\nconst float IMGFMT_R8 = 7;\t/* Single channel red-only 8bit pixel format. */\nconst float IMGFMT_R16F = 8;\t/* Single channel red-only Half-Float pixel format. Requires gl3 support. */\nconst float IMGFMT_R32F = 9;\t/* Single channel red-only Float pixel format. Requires gl3 support. */\nconst float IMGFMT_A2B10G10R10 = 10;\t/* Packed 32-bit packed 10-bit colour pixel format. Requires gl3 support. */\nconst float IMGFMT_R5G6B5 = 11;\t/* Packed 16-bit colour pixel format. */\nconst float IMGFMT_R4G4B4A4 = 12;\t/* Packed 16-bit colour pixel format, with alpha */\nconst float IMGFMT_R8G8 = 13;\t/* 16-bit two-channel pixel format. */\nconst float IMGFMT_R32G32B32F = 14;\t/* A pixel format that matches QC's vector type. */\n#endif\n#ifdef CSQC\nconst float RF_VIEWMODEL = 1;\t/* Specifies that the entity is a view model, and that its origin is relative to the current view position. These entities are also subject to viewweapon bob. */\nconst float RF_EXTERNALMODEL = 2;\t/* Specifies that this entity should be displayed in mirrors (and may still cast shadows), but will not otherwise be visible. */\n#endif\n#if defined(CSQC) || defined(MENU)\nconst float RF_DEPTHHACK = 4;\t/* Hacks the depth values such that the entity uses depth values as if it were closer to the screen. This is useful when combined with viewmodels to avoid weapons poking in to walls. */\nconst float RF_ADDITIVE = 8;\t/* Shaders from this entity will temporarily be hacked to use an additive blend mode instead of their normal blend mode. */\n#endif\n#ifdef CSQC\nconst float RF_USEAXIS = 16;\t/* The entity will be oriented according to the current v_forward+v_right+v_up vector values instead of the entity's .angles field. */\nconst float RF_NOSHADOW = 32;\t/* This entity will not cast shadows. Often useful on view models. */\nconst float RF_FRAMETIMESARESTARTTIMES = 64;\t/* Specifies that the frame1time, frame2time field are timestamps (denoting the start of the animation) rather than time into the animation. */\n#endif\n#if defined(CSQC) || defined(MENU)\nconst float IE_KEYDOWN = 0;\t/* Specifies that a key was pressed. Second argument is the scan code. Third argument is the unicode (printable) char value. Fourth argument denotes which keyboard(or mouse, if its a mouse 'scan' key) the event came from. Note that some systems may completely separate scan codes and unicode values, with a 0 value for the unspecified argument. */\nconst float IE_KEYUP = 1;\t/* Specifies that a key was released. Arguments are the same as IE_KEYDOWN. On some systems, this may be fired instantly after IE_KEYDOWN was fired. */\nconst float IE_MOUSEDELTA = 2;\t/* Specifies that a mouse was moved (touch screens and tablets typically give IE_MOUSEABS events instead, use in_windowed_mouse 0 to test code to cope with either). Second argument is the X displacement, third argument is the Y displacement. Fourth argument is which mouse or touch event triggered the event. */\nconst float IE_MOUSEABS = 3;\t/* Specifies that a mouse cursor or touch event was moved to a specific location relative to the virtual screen space. Second argument is the new X position, third argument is the new Y position. Fourth argument is which mouse or touch event triggered the event. */\nconst float IE_ACCELEROMETER = 4;\nconst float IE_FOCUS = 5;\t/* Specifies that input focus was given. parama says mouse focus, paramb says keyboard focus. If either are -1, then it is unchanged. */\nconst float IE_JOYAXIS = 6;\t/* Specifies that what value a joystick/controller axis currently specifies. x=axis, y=value. Will be called multiple times, once for each axis of each active controller. */\nconst float IE_GYROSCOPE = 7;\nconst float GGDI_GAMEDIR = 0;\t/* Used with getgamedirinfo to query the mod's public gamedir. There is often other info that cannot be expressed with just a gamedir name, resulting in dupes or other weirdness. */\nconst float GGDI_DESCRIPTION = 1;\t/* The human-readable title of the mod. Empty when no data is known (ie: the gamedir just contains some maps). */\nconst float GGDI_OVERRIDES = 2;\t/* A list of settings overrides. */\nconst float GGDI_LOADCOMMAND = 3;\t/* The console command needed to actually load the mod. */\nconst float GGDI_ICON = 4;\t/* The mod's Icon path, ready for drawpic. */\nconst float GGDI_GAMEDIRLIST = 5;\t/* A semi-colon delimited list of gamedirs that the mod's content can be loaded through. */\n#endif\n#ifdef SSQC\nconst float CLIENTTYPE_DISCONNECTED = 0;\t/* Return value from clienttype() builtin. This entity is a player slot that is currently empty. */\nconst float CLIENTTYPE_REAL = 1;\t/* This is a real player, and not a bot. */\nconst float CLIENTTYPE_BOT = 2;\t/* This player slot does not correlate to a real player, any messages sent to this client will be ignored. */\nconst float CLIENTTYPE_NOTACLIENT = 3;\t/* This entity is not even a player slot. This is typically an error condition. */\n#endif\nconst float FILE_READ = 0;\t/* The file may be read via fgets to read a single line at a time. */\nconst float FILE_APPEND = 1;\t/* Like FILE_WRITE, but writing starts at the end of the file. */\nconst float FILE_WRITE = 2;\t/* fputs will be used to write to the file. */\n#if defined(CSQC) || defined(SSQC)\nconst float FILE_READNL = 4;\t/* Like FILE_READ, except newlines are not special. fgets reads the entire file into a tempstring. */\nconst float FILE_MMAP_READ = 5;\t/* The file will be loaded into memory. fgets returns a pointer to the first byte (and will always return the same value for this file). Cast this to your datatype. */\nconst float FILE_MMAP_RW = 6;\t/* Like FILE_MMAP_READ, except any changes to the data will be written back to disk once the file is closed. */\n#endif\n#ifdef CSQC\nconst float MASK_ENGINE = 1;\t/* Valid as an argument for addentities. If specified, all non-csqc entities will be added to the scene. */\nconst float MASK_VIEWMODEL = 2;\t/* Valid as an argument for addentities. If specified, the regular engine viewmodel will be added to the scene. */\nconst float PREDRAW_AUTOADD = 0;\t/* Valid as a return value from the predraw function. Returning this will cause the engine to automatically invoke addentity(self) for you. */\nconst float PREDRAW_NEXT = 1;\t/* Valid as a return value from the predraw function. Returning this will simply move on to the next entity without the autoadd behaviour, so can be used for particle/invisible/special entites, or entities that were explicitly drawn with addentity. */\nconst float LFIELD_ORIGIN = 0;\nconst float LFIELD_COLOUR = 1;\nconst float LFIELD_RADIUS = 2;\nconst float LFIELD_FLAGS = 3;\nconst float LFIELD_STYLE = 4;\nconst float LFIELD_ANGLES = 5;\nconst float LFIELD_FOV = 6;\nconst float LFIELD_CORONA = 7;\nconst float LFIELD_CORONASCALE = 8;\nconst float LFIELD_CUBEMAPNAME = 9;\nconst float LFIELD_AMBIENTSCALE = 10;\nconst float LFIELD_DIFFUSESCALE = 11;\nconst float LFIELD_SPECULARSCALE = 12;\nconst float LFIELD_ROTATION = 13;\nconst float LFIELD_DIETIME = 14;\nconst float LFIELD_RGBDECAY = 15;\nconst float LFIELD_RADIUSDECAY = 16;\nconst float LFIELD_STYLESTRING = 17;\nconst float LFIELD_NEARCLIP = 18;\nconst float LFLAG_NORMALMODE = 1;\nconst float LFLAG_REALTIMEMODE = 2;\nconst float LFLAG_LIGHTMAP = 4;\nconst float LFLAG_FLASHBLEND = 8;\nconst float LFLAG_NOSHADOWS = 256;\nconst float LFLAG_SHADOWMAP = 512;\nconst float LFLAG_CREPUSCULAR = 1024;\nconst float LFLAG_ORTHOSUN = 2048;\nconst float TEREDIT_RELOAD = 0;\nconst float TEREDIT_SAVE = 1;\nconst float TEREDIT_SETHOLE = 2;\nconst float TEREDIT_HEIGHT_SET = 3;\nconst float TEREDIT_HEIGHT_SMOOTH = 4;\nconst float TEREDIT_HEIGHT_SPREAD = 5;\nconst float TEREDIT_HEIGHT_RAISE = 6;\nconst float TEREDIT_HEIGHT_FLATTEN = 18;\nconst float TEREDIT_HEIGHT_LOWER = 7;\nconst float TEREDIT_TEX_KILL = 8;\nconst float TEREDIT_TEX_GET = 9;\nconst float TEREDIT_TEX_BLEND = 10;\nconst float TEREDIT_TEX_UNIFY = 11;\nconst float TEREDIT_TEX_NOISE = 12;\nconst float TEREDIT_TEX_BLUR = 13;\nconst float TEREDIT_TEX_REPLACE = 19;\nconst float TEREDIT_TEX_SETMASK = 25;\nconst float TEREDIT_WATER_SET = 14;\nconst float TEREDIT_MESH_ADD = 15;\nconst float TEREDIT_MESH_KILL = 16;\nconst float TEREDIT_TINT = 17;\nconst float TEREDIT_RESET_SECT = 20;\nconst float TEREDIT_RELOAD_SECT = 21;\nconst float TEREDIT_ENT_GET = 26;\nconst float TEREDIT_ENT_SET = 27;\nconst float TEREDIT_ENT_ADD = 28;\nconst float TEREDIT_ENT_COUNT = 29;\n#endif\n#if defined(CSQC) || defined(MENU)\nconst float SLIST_HOSTCACHEVIEWCOUNT = 0;\nconst float SLIST_HOSTCACHETOTALCOUNT = 1;\nconst float SLIST_MASTERQUERYCOUNT = 2;\nconst float SLIST_MASTERREPLYCOUNT = 3;\nconst float SLIST_SERVERQUERYCOUNT = 4;\nconst float SLIST_SERVERREPLYCOUNT = 5;\nconst float SLIST_SORTFIELD = 6;\nconst float SLIST_SORTDESCENDING = 7;\nconst float SLIST_TEST_CONTAINS = 0;\nconst float SLIST_TEST_NOTCONTAIN = 1;\nconst float SLIST_TEST_LESSEQUAL = 2;\nconst float SLIST_TEST_LESS = 3;\nconst float SLIST_TEST_EQUAL = 4;\nconst float SLIST_TEST_GREATER = 5;\nconst float SLIST_TEST_GREATEREQUAL = 6;\nconst float SLIST_TEST_NOTEQUAL = 7;\nconst float SLIST_TEST_STARTSWITH = 8;\nconst float SLIST_TEST_NOTSTARTSWITH = 9;\n#endif\n#ifdef MENU\nfloat(string ext) checkextension = #1; /*\n\t\tChecks if the named extension is supported by the running engine. */\n\nvoid(string err,...) error = #2; /*\n\t\tFatal error that will trigger a crash-to-console that users will actually notice. */\n\nvoid(string err,...) objerror = #3; /*\n\t\tFor some reason this has been redefined as non-fatal, and as it won't force the user to look at the console it'll generally be ignored completely so really what's the point? Other than as a convoluted way to remove(self) that is. */\n\nvoid(string text,...) print = #4; /* Part of DP_SV_PRINT\n\t\tHello, world. Shoves junk on the console. Hopefully people will bother to read it, maybe. */\n\nDEP void(string text,...) bprint = #5;\nDEP void(float clientnum, string text,...) msprint = #6;\nvoid(string text,...) cprint = #7; /*\n\t\tTries to show the given message in the centre of the screen, assuming that its not obscured by menus. Oh hey look, you're calling it in menuqc! */\n\nvector(vector) normalize = #8;\nfloat(vector) vlen = #9;\nfloat(vector) vectoyaw = #10;\nvector(vector) vectoangles = #11;\nfloat() random = #12;\nvoid(string,...) localcmd = #13;\nfloat(string name) cvar = #14;\nvoid(string name, string value) cvar_set = #15;\nvoid(string text, ...) dprint = #16;\nstring(float) ftos = #17;\nfloat(float) fabs = #18;\nstring(vector) vtos = #19;\nstring(entity) etos = #20; /* Part of DP_QC_ETOS*/\nfloat(string) stof = #21; /* Part of FRIK_FILE, FTE_QC_INFOKEY, FTE_STRINGS, QW_ENGINE, ZQ_QC_STRINGS*/\nentity() spawn = #22;\nvoid(entity) remove = #23;\nentity(entity start, .string field, string match) find = #24;\nentity(entity start, .__variant field, __variant match) findfloat = #25; /* Part of DP_QC_FINDFLOAT*/\nentity(.string field, string match) findchain = #26; /* Part of DP_QC_FINDCHAIN*/\nentity(.__variant field, __variant match) findchainfloat = #27; /* Part of DP_QC_FINDCHAINFLOAT*/\nstring(string file) precache_file = #28; /*\n\t\tAttempts to download the named file from the current server, if it isn't found locally. Not very useful as menuqc is normally meant to work before joining servers too. */\n\nstring(string sample) precache_sound = #29;\nvoid() coredump = #30; /*\n\t\tTakes a dump, writing the qcvm's state to disk. There are normally easier ways to debug, but I suppose this one still beats print spam. */\n\nvoid() traceon = #31; /*\n\t\tEnables single-stepping. Its generally easier to just set a breakpoint. */\n\nvoid() traceoff = #32; /*\n\t\tDisables single-stepping. Which sucks if you started said singlestepping outside of qc. */\n\nvoid(entity) eprint = #33;\nfloat(float) rint = #34;\nfloat(float) floor = #35;\nfloat(float) ceil = #36;\nentity(entity) nextent = #37;\nfloat(float) sin = #38; /* Part of DP_QC_SINCOSSQRTPOW*/\nfloat(float) cos = #39; /* Part of DP_QC_SINCOSSQRTPOW*/\nfloat(float) sqrt = #40; /* Part of DP_QC_SINCOSSQRTPOW*/\nvector() randomvector = #41;\nfloat(string name, string value, float flags) registercvar = #42; /* Part of DP_REGISTERCVAR\n\t\tCreates the cvar if it didn't already exist. This presents issues for setting those cvars via startup configs of course, and autocvars are easier but I suppose they don't get any flags (which are ignored anyway, of course). */\n\nfloat(float,...) min = #43; /* Part of DP_QC_MINMAXBOUND*/\nfloat(float,...) max = #44; /* Part of DP_QC_MINMAXBOUND*/\nfloat(float min,float value,float max) bound = #45; /* Part of DP_QC_MINMAXBOUND*/\nfloat(float,float) pow = #46; /* Part of DP_QC_SINCOSSQRTPOW*/\nvoid(entity src, entity dst) copyentity = #47; /* Part of DP_QC_COPYENTITY\n\t\tCopies all entity fields from one entity into another (forgetting any that were previously set on the destination). */\n\nfilestream(string filename, float mode) fopen = #48; /* Part of FRIK_FILE*/\nvoid(filestream fhandle) fclose = #49; /* Part of FRIK_FILE*/\nstring(filestream fhandle) fgets = #50; /* Part of FRIK_FILE*/\nvoid(filestream fhandle, string s) fputs = #51; /* Part of FRIK_FILE*/\nfloat(string) strlen = #52; /* Part of FRIK_FILE, FTE_STRINGS, ZQ_QC_STRINGS*/\nstring(string, optional string, optional string, optional string, optional string, optional string, optional string, optional string) strcat = #53; /* Part of FRIK_FILE, FTE_STRINGS, ZQ_QC_STRINGS*/\nstring(string s, float start, float length) substring = #54; /* Part of FRIK_FILE, FTE_STRINGS, ZQ_QC_STRINGS*/\nvector(string) stov = #55; /* Part of FRIK_FILE, FTE_STRINGS, ZQ_QC_STRINGS*/\nFTEDEP(\"Redundant\") string(string) strzone = #56; /* Part of FRIK_FILE, FTE_STRINGS, ZQ_QC_STRINGS\n\t\tExists in FTE for compat only, no different from strcat. */\n\nFTEDEP(\"Redundant\") void(string) strunzone = #57; /* Part of FRIK_FILE, FTE_STRINGS, ZQ_QC_STRINGS\n\t\tExists in FTE for compat only, does nothing. */\n\nfloat(string) tokenize = #58; /* Part of KRIMZON_SV_PARSECLIENTCOMMAND\n\t\tSplits up the given string into its different components (what constitutes a token separator is not well defined and has been hacked about with over the years so have fun with that), returning the number of tokens that were found. Call argv(0 through ret-1) to retrieve each individual token. Take care to not use this recursively. */\n\nstring(float) argv = #59; /* Part of KRIMZON_SV_PARSECLIENTCOMMAND\n\t\tReturns one of the tokens found via tokenize (and equivelent builtins). */\n\nfloat() isserver = #60; /*\n\t\tReturns true if the local engine is running a server, and thus cvars and localcmds are shared with said server. */\n\nfloat() clientcount = #61; /*\n\t\tReturns the maximum number of players on the server. Useless if its a remote server, so its a kinda useless builtin really. */\n\nfloat() clientstate = #62; /*\n\t\tTells you whether the client is actually connected to anything. 0 for a dedicated server (but dedicated servers don't normally run menuqc anyway), 2 if connecting or connected to a server (but not necessarily spawned+active), 1 for sitting around idle without trying to connect to anything yet. */\n\nvoid(string map) changelevel = #64; /*\n\t\tNot really any different from a localcmd, but with proper string escapes. */\n\nvoid(string sample, optional float channel, optional float volume) localsound = #65; /*\n\t\tPlays a sound, locally. precaching is optional, but recommended. */\n\nvector() getmousepos = #66; /*\n\t\tObsolete. Return values depend upon the current cursor mode. Implement Menu_InputEvent instead, so you can handle deltas as-is or absolutes if that's all the OS can provide. */\n\nfloat(optional float timetype) gettime = #67;\nvoid(string s) loadfromdata = #68; /*\n\t\tReads a set of entities from the given string. This string should have the same format as a .ent file or a saved game. Entities will be spawned as required. If you need to see the entities that were created, you should use parseentitydata instead. */\n\nvoid(string s) loadfromfile = #69; /*\n\t\tReads a set of entities from the named file. This file should have the same format as a .ent file or a saved game. Entities will be spawned as required. If you need to see the entities that were created, you should use parseentitydata instead. */\n\nfloat(float val, float m) mod = #70;\nstring(string name) cvar_string = #71; /* Part of DP_QC_CVAR_STRING\n\t\tReturns the value of a cvar, as a string. */\n\nvoid() crash = #72; /*\n\t\tDemonstrates that no program is bug free. */\n\nvoid() stackdump = #73; /*\n\t\tPrints out the QC's stack, for console-based error reports. */\n\nsearchhandle(string pattern, enumflags:float{SB_CASEINSENSITIVE=1<<0,SB_FULLPACKAGEPATH=1<<1,SB_ALLOWDUPES=1<<2,SB_FORCESEARCH=1<<3,SB_MULTISEARCH=1<<4} flags, float quiet, optional string package) search_begin = #74; /* Part of DP_QC_FS_SEARCH, DP_QC_FS_SEARCH_PACKFILE*/\nvoid(searchhandle handle) search_end = #75; /* Part of DP_QC_FS_SEARCH, DP_QC_FS_SEARCH_PACKFILE*/\nfloat(searchhandle handle) search_getsize = #76; /* Part of DP_QC_FS_SEARCH, DP_QC_FS_SEARCH_PACKFILE*/\nstring(searchhandle handle, float num) search_getfilename = #77; /* Part of DP_QC_FS_SEARCH, DP_QC_FS_SEARCH_PACKFILE*/\nfloat(entity) etof = #79;\nentity(float) ftoe = #80;\nfloat(string) validstring = #81; /*\n\t\tReturns true if str isn't null. In case 'if [not](str)' was configured to test for empty instead of null. */\n\nDEP float(string str) altstr_count = #82; /*\n\t\tReports how many single-quotes there were in the string, divided by 2. */\n\nDEP string(string str) altstr_prepare = #83; /*\n\t\tAdds markup to escape only single-quotes. Does not add any. */\n\nDEP string(string str, float num) altstr_get = #84; /*\n\t\tGets the Nth single-quoted token in the input. */\n\nDEP string(string str, float num, string setval) altstr_set = #85; /*\n\t\tChanges the Nth single-quoted token. The setval argument must not contain any single-quotes (use altstr_prepare to ensure this). */\n\nentity(entity start, .float field, float match) findflags = #87; /* Part of DP_QC_FINDFLAGS*/\nentity(.float field, float match) findchainflags = #88; /* Part of DP_QC_FINDCHAINFLAGS*/\nstring(string name) cvar_defstring = #89; /* Part of DP_QC_CVAR_DEFSTRING*/\nvoid(entity ent, string mname) setmodel = #90; /*\n\t\tMenuqc-specific version. */\n\nvoid(string mname) precache_model = #91; /*\n\t\tMenuqc-specific version. */\n\nvoid(entity ent, vector neworg) setorigin = #92; /*\n\t\tMenuqc-specific version. */\n\n#endif\n#if defined(CSQC) || defined(SSQC)\nvoid(vector vang) makevectors = #1; /*\n\t\tTakes an angle vector (pitch,yaw,roll) (+x=DOWN). Writes its results into v_forward, v_right, v_up vectors. */\n\nvoid(entity e, vector o) setorigin = #2; /*\n\t\tChanges e's origin to be equal to o. Also relinks collision state (as well as setting absmin+absmax), which is required after changing .solid */\n\nvoid(entity e, string m) setmodel = #3; /*\n\t\tLooks up m in the model precache list, and sets both e.model and e.modelindex to match. BSP models will set e.mins and e.maxs accordingly, other models depend upon the value of sv_gameplayfix_setmodelrealbox - for compatibility you should always call setsize after all pickups or non-bsp models. Also relinks collision state. */\n\nvoid(entity e, vector min, vector max) setsize = #4; /*\n\t\tSets the e's mins and maxs fields. Also relinks collision state, which sets absmin and absmax too. */\n\n#endif\n#ifdef SSQC\nvoid() breakpoint = #6; /*\n\t\tTrigger a debugging event. FTE will break into the qc debugger. Other engines may crash with a debug execption. */\n\n#endif\n#if defined(CSQC) || defined(SSQC)\nfloat() random = #7; /*\n\t\tReturns a random value between 0 and 1. Be warned, this builtin can return 1 in most engines, which can break arrays. */\n\nvoid(entity e, float chan, string samp, float vol, float atten, optional float speedpct, optional float flags, optional float timeofs) sound = #8; /*\n\t\tStarts a sound centered upon the given entity.\n\t\tchan is the entity sound channel to use, channel 0 will allow you to mix many samples at once, others will replace the old sample\n\t\t'samp' must have been precached first\n\t\tif specified, 'speedpct' should normally be around 100 (or =0), 200 for double speed or 50 for half speed.\n\t\tIf flags is specified, the reliable flag in the channels argument is used for additional channels. Flags should be made from SOUNDFLAG_* constants\n\t\ttimeofs should be negative in order to provide a delay before the sound actually starts. */\n\nvector(vector v) normalize = #9; /*\n\t\tShorten or lengthen a direction vector such that it is only one quake unit long. */\n\nvoid(string e) error = #10; /*\n\t\tEnds the game with an easily readable error message. */\n\nvoid(string e) objerror = #11; /*\n\t\tDisplays a non-fatal easily readable error message concerning the self entity, including a field dump. self will be removed! */\n\nfloat(vector v) vlen = #12; /*\n\t\tReturns the square root of the dotproduct of a vector with itself. Or in other words the length of a distance vector, in quake units. */\n\nfloat(vector v, optional entity reference) vectoyaw = #13; /*\n\t\tGiven a direction vector, returns the yaw angle in which that direction vector points. If an entity is passed, the yaw angle will be relative to that entity's gravity direction. */\n\nentity() spawn = #14; /*\n\t\tAdds a brand new entity into the world! Hurrah, you're now a parent! */\n\nvoid(entity e) remove = #15; /*\n\t\tDestroys the given entity and clears some limited fields (including model, modelindex, solid, classname). Any references to the entity following the call are an error. After half a second the entity will be reused, in the interim you can unfortunatly still read its fields to see if the reference is no longer valid. */\n\n#endif\nvoid(entity e) removeinstant = #0:removeinstant; /*\n\t\tSame thing as the regular remove builtin, but bypasses the half-second rule. The entity slot may be reused instantly. Be CERTAIN that you have no lingering references, because if they're followed they will end up poking an entirely different type of entity! So only use this where you're sure its safe. */\n\n#if defined(CSQC) || defined(SSQC)\nvoid(vector v1, vector v2, float flags, entity ent) traceline = #16; /*\n\t\tTraces a thin line through the world from v1 towards v2.\n\t\tWill not collide with ent, ent.owner, or any entity who's owner field refers to ent.\n\t\tThe passed entity will also be used to determine whether to use a capsule trace, the contents that the trace should impact, and a couple of other extra fields that define the trace.\n\t\tThere are no side effects beyond the trace_* globals being written.\n\t\tflags&MOVE_NOMONSTERS will not impact on non-bsp entities.\n\t\tflags&MOVE_MISSILE will impact with increased size.\n\t\tflags&MOVE_HITMODEL will impact upon model meshes, instead of their bounding boxes.\n\t\tflags&MOVE_TRIGGERS will also stop on triggers\n\t\tflags&MOVE_EVERYTHING will stop if it hits anything, even non-solid entities.\n\t\tflags&MOVE_LAGGED will backdate entity positions for the purposes of this builtin according to the indicated player ent's latency, to provide lag compensation. */\n\n#endif\n#ifdef SSQC\nentity() checkclient = #17; /*\n\t\tReturns one of the player entities. The returned player will change periodically. */\n\n#endif\n#if defined(CSQC) || defined(SSQC)\nentity(entity start, .string fld, string match) find = #18; /*\n\t\tScan for the next entity with a given field set to the given 'match' value. start should be either world, or the previous entity that was found. Returns world on failure/if there are no more.\n\t\tIf you have many many entities then you may find that hashtables will give more performance (but requires extra upkeep). */\n\n#endif\nentity*(.__variant fld, __variant match, int type=EV_STRING, __out int count) find_list = #0:find_list; /*\n\t\tScan for the next entity with a given field set to the given 'match' value. start should be either world, or the previous entity that was found. Returns world on failure/if there are no more.\n\t\tIf you have many many entities then you may find that hashtables will give more performance (but requires extra upkeep). */\n\n#if defined(CSQC) || defined(SSQC)\nstring(string s) precache_sound = #19; /*\n\t\tPrecaches a sound, making it known to clients and loading it from disk. This builtin (strongly) should be called during spawn functions. This builtin must be called for the sound before the sound builtin is called, or it might not even be heard. */\n\nstring(string s) precache_model = #20; /*\n\t\tPrecaches a model, making it known to clients and loading it from disk if it has a .bsp extension. This builtin (strongly) should be called during spawn functions. This must be called for each model name before setmodel may use that model name.\n\t\tModelindicies precached in SSQC will always be positive. CSQC precaches will be negative if they are not also on the server. */\n\n#endif\n#ifdef SSQC\nvoid(entity client, string s, optional string s2, optional string s3, optional string s4, optional string s5, optional string s6, optional string s7) stuffcmd = #21; /*\n\t\tSends a console command (or cvar) to the client, where it will be executed. Different clients support different commands. Do NOT forget the final \\n.\n\t\tThis builtin is generally considered evil. */\n\nvoid(entity client, float flags, string s, optional string s2, optional string s3, optional string s4, optional string s5, optional string s6) stuffcmdflags = #0:stuffcmdflags; /* Part of FTE_QC_STUFFCMDFLAGS\n\t\tSends a console command (or cvar) to the client, where it will be executed. Different clients support different commands. Do NOT forget the final \\n.\n\t\tThis (just as evil) variant allows specifying some flags too. See the STUFFCMD_* constants. */\n\n#endif\n#if defined(CSQC) || defined(SSQC)\nentity(vector org, float rad, optional .entity chainfield) findradius = #22; /*\n\t\tFinds all entities within a distance of the 'org' specified. One entity is returned directly, while other entities are returned via that entity's .chain field. Use findradius_list for an updated alternative without reenterancy issues. */\n\nentity*(vector org, float rad, __out int foundcount, int sort=0) findradius_list = #0:findradius_list; /*\n\t\tFinds all entities linked with a bbox within a distance of the 'org' specified, returning the list as a temp-array (world signifies the end). Unlike findradius, sv_gameplayfix_blowupfallenzombies is ignored (use FL_FINDABLE_NONSOLID instead), while sv_gameplayfix_findradiusdistancetobox and dpcompat_findradiusarealinks are force-enabled. The resulting buffer will automatically be cleaned up by the engine and does not need to be freed. */\n\n#endif\n#if defined(NQSSQC)\nvoid(string s, optional string s2, optional string s3, optional string s4, optional string s5, optional string s6, optional string s7, optional string s8) bprint = #23; /*\n\t\tNQ: Concatenates all arguments, and prints the messsage on the console of all connected clients. */\n\n#endif\n#if defined(QWSSQC)\nvoid(float msglvl, string s, optional string s2, optional string s3, optional string s4, optional string s5, optional string s6, optional string s7) bprint = #23; /*\n\t\tQW: Concatenates all string arguments, and prints the messsage on the console of only all clients who's 'msg' infokey is set lower or equal to the supplied 'msglvl' argument. */\n\n#endif\n#if defined(NQSSQC)\nvoid(entity client, string s, optional string s2, optional string s3, optional string s4, optional string s5, optional string s6, optional string s7) sprint = #24; /*\n\t\tNQ: Concatenates all string arguments, and prints the messsage on the named client's console */\n\n#endif\n#if defined(QWSSQC)\nvoid(entity client, float msglvl, string s, optional string s2, optional string s3, optional string s4, optional string s5, optional string s6) sprint = #24; /*\n\t\tQW: Concatenates all string arguments, and prints the messsage on the named client's console, but only if that client's 'msg' infokey is set lower or equal to the supplied 'msglvl' argument. */\n\n#endif\n#if defined(CSQC) || defined(NQSSQC)\nvoid(string s, ...) dprint = #25; /*\n\t\tNQ: Prints the given message on the server's console, but only if the developer cvar is set. Arguments will be concatenated into a single message. */\n\n#endif\n#if defined(CSQC) || defined(QWSSQC)\nvoid(string s, ...) dprint = #25; /*\n\t\tQW: Unconditionally prints the given message on the server's console.  Arguments will be concatenated into a single message. */\n\n#endif\n#if defined(CSQC) || defined(SSQC)\nstring(float val) ftos = #26; /*\n\t\tReturns a tempstring containing a representation of the given float. Precision depends upon engine. */\n\nstring(vector val) vtos = #27; /*\n\t\tReturns a tempstring containing a representation of the given vector. Precision depends upon engine. */\n\nvoid() coredump = #28; /*\n\t\tWrites out a coredump. This contains stack, globals, and field info for all ents. This can be handy for debugging. */\n\nvoid() traceon = #29; /*\n\t\tEnables tracing. This may be spammy, slow, and stuff. Set debugger 1 in order to use fte's qc debugger. */\n\nvoid() traceoff = #30; /*\n\t\tDisables tracing again. */\n\nvoid(entity e) eprint = #31; /*\n\t\tDebugging builtin that prints all fields of the given entity to the console. */\n\nfloat(float yaw, float dist, optional float settraceglobals) walkmove = #32; /*\n\t\tAttempt to walk the entity at a given angle for a given distance.\n\t\tif settraceglobals is set, the trace_* globals will be set, showing the results of the movement.\n\t\tThis function will trigger touch events. */\n\nfloat() droptofloor = #34; /*\n\t\tInstantly moves the entity downwards until it hits the ground. If the entity is in solid or would need to drop more than 'pr_droptofloorunits' quake units, its position will be considered invalid and the builtin will abort, returning FALSE, otherwise TRUE. */\n\nvoid(float lightstyle, string stylestring, optional vector rgb) lightstyle = #35; /*\n\t\tSpecifies an auto-animating string that specifies the light intensity for entities using that lightstyle.\n\t\ta is off, z is fully lit. Should be lower case only.\n\t\trgb will recolour all lights using that lightstyle. */\n\nfloat(float) rint = #36; /*\n\t\tRounds the given float up or down to the closest integeral value. X.5 rounds away from 0 */\n\nfloat(float) floor = #37; /*\n\t\tRounds the given float downwards, even when negative. */\n\nfloat(float) ceil = #38; /*\n\t\tRounds the given float upwards, even when negative. */\n\nfloat(entity ent) checkbottom = #40; /*\n\t\tExpensive checks to ensure that the entity is actually sitting on something solid, returns true if it is. */\n\nfloat(vector pos) pointcontents = #41; /*\n\t\tChecks the given point to see what is there. Returns one of the SOLID_* constants. Just because a spot is empty does not mean that the player can stand there due to the size of the player - use tracebox for such tests. */\n\nfloat(float) fabs = #43; /*\n\t\tRemoves the sign of the float, making it positive if it is negative. */\n\n#endif\n#ifdef SSQC\nvector(entity player, float missilespeed) aim = #44; /*\n\t\tReturns a tweaked copy of the v_forward vector (must be set! ie: makevectors(player.v_angle) ). This is important for keyboard users (that don't want to have to look up/down the whole time), as well as joystick users (who's aim is otherwise annoyingly imprecise). Only the upwards component of the result will differ from the value of v_forward. The builtin will select the enemy closest to the crosshair within the angle of acos(sv_aim). */\n\n#endif\n#if defined(CSQC) || defined(SSQC)\nfloat(string) cvar = #45; /*\n\t\tReturns the numeric value of the named cvar */\n\nvoid(string, ...) localcmd = #46; /*\n\t\tAdds the string to the console command queue. Commands will not be executed immediately, but rather at the start of the following frame. */\n\nentity(entity) nextent = #47; /*\n\t\tReturns the following entity. Skips over removed entities. Returns world when passed the last valid entity. */\n\nvoid(vector pos, vector dir, float colour, float count) particle = #48; /*\n\t\tSpawn 'count' particles around 'pos' moving in the direction 'dir', with a palette colour index between 'colour' and 'colour+8'. */\n\n#define ChangeYaw changeyaw\nvoid() changeyaw = #49; /*\n\t\tChanges the self.angles_y field towards self.ideal_yaw by up to self.yaw_speed. */\n\nvector(vector fwd, optional vector up) vectoangles = #51; /*\n\t\tReturns the angles (+x=UP) required to orient an entity to look in the given direction. The 'up' argument is required if you wish to set a roll angle, otherwise it will be limited to just monster-style turning. */\n\n#endif\n#ifdef SSQC\nvoid(float to, float val) WriteByte = #52; /*\n\t\tWrites a single byte into a network message buffer. Typically you will find a more correct alternative to writing arbitary data. 'to' should be one of the MSG_* constants. MSG_ONE must have msg_entity set first. */\n\nvoid(float to, float val) WriteChar = #53; /*\n\t\tWrites a signed value between -128 and 127. */\n\nvoid(float to, float val) WriteShort = #54; /*\n\t\tWrites a signed value between -32768 and 32767. As an exception, values up to 65535 will not trigger warnings (but readshort will read the result as negative!) */\n\nvoid(float to, float val) WriteLong = #55; /*\n\t\tWrites a signed 32bit integer. Note that the input argument being of float type limits the resulting integer to a mere 24 consecutive bits of validity. Use WriteInt if you want to write an entire 32bit int without data loss. */\n\nvoid(float to, float val) WriteCoord = #56; /*\n\t\tWrites a single value suitable for a map coordinate axis. The precision is not strictly specified but is assumed to be of at least 13.3 fixed-point precision (ie: +/-4k with 1/8th precision). */\n\nvoid(float to, float val) WriteAngle = #57; /*\n\t\tWrites a single value suitable for an angle axis. The precision is not strictly specified but is assumed to be 8bit, giving 256 notches instead of the assumed 360 range passed in. */\n\nvoid(float to, string val) WriteString = #58; /*\n\t\tWrites a variable-length null terminated string. There are length limits. The codepage is not translated, so be sure that client+server agree on whether utf-8 is being used or not (or just stick to ascii+markup). */\n\nvoid(float to, entity val) WriteEntity = #59; /*\n\t\tWrites the index of the specified entity (the network data size is not specified). This can be read clientside using the readentitynum builtin, with caveats. */\n\n#endif\n#if defined(CSQC) || defined(SSQC)\nfloat(float angle) sin = #60; /* Part of DP_QC_SINCOSSQRTPOW\n\t\tForgive me father, for I have trigonometry homework. */\n\nfloat(float angle) cos = #61; /* Part of DP_QC_SINCOSSQRTPOW*/\nfloat(float value) sqrt = #62; /* Part of DP_QC_SINCOSSQRTPOW*/\n#endif\n#ifdef SSQC\nfloat(float a, float n) modulo = #0:modulo;\n#endif\n#if defined(CSQC) || defined(SSQC)\nvoid(entity ent) changepitch = #63; /* Part of DP_QC_CHANGEPITCH*/\nvoid(entity ent, entity ignore) tracetoss = #64;\nstring(entity ent) etos = #65; /* Part of DP_QC_ETOS*/\nvoid(float step) movetogoal = #67; /*\n\t\tRuns lots and lots of fancy logic in order to try to step the entity the specified distance towards its goalentity. */\n\nstring(string s) precache_file = #68; /*\n\t\tThis builtin does nothing. It was used only as a hint for pak generation. */\n\nvoid(entity e) makestatic = #69; /*\n\t\tSends a copy of the entity's renderable fields to all clients, and REMOVES the entity, preventing further changes. This means it will be unmutable and non-solid. */\n\n#endif\n#ifdef SSQC\nvoid(string mapname, optional string newmapstartspot) changelevel = #70; /*\n\t\tAttempts to change the map to the named map. If 'newmapstartspot' is specified, the state of the current map will be preserved, and the argument will be passed to the next map in the 'startspot' global, and the next map will be loaded from archived state if it was previously visited. If not specified, all archived map states will be purged. */\n\n#endif\n#if defined(CSQC) || defined(SSQC)\nvoid(string cvarname, string valuetoset) cvar_set = #72; /*\n\t\tInstantly sets a cvar to the given string value. Warning: the resulting string includes apostrophies surrounding the result. You may wish to use sprintf instead. */\n\n#endif\n#ifdef SSQC\nvoid(entity ent, string text, optional string text2, optional string text3, optional string text4, optional string text5, optional string text6, optional string text7) centerprint = #73;\n#endif\n#if defined(CSQC) || defined(SSQC)\nvoid (vector pos, string samp, float vol, float atten) ambientsound = #74;\nstring(string str) precache_model2 = #75;\nstring(string str) precache_sound2 = #76;\nstring(string str) precache_file2 = #77;\n#endif\n#ifdef SSQC\nvoid(entity player) setspawnparms = #78;\nvoid(entity killer, entity killee) logfrag = #79; /* Part of QW_ENGINE*/\n#endif\n#if defined(CSQC) || defined(SSQC)\nstring(entity e, string key) infokey = #80; /* Part of FTE_QC_INFOKEY, QW_ENGINE\n\t\tIf e is world, returns the field 'key' from either the serverinfo or the localinfo. If e is a player, returns the value of 'key' from the player's userinfo string. There are a few special exceptions, like 'ip' which is not technically part of the userinfo. */\n\n#endif\n#ifdef SSQC\nfloat(entity e, string key) infokeyf = #0:infokeyf; /*\n\t\tIdentical to regular infokey, except returns a float. */\n\n#endif\n#if defined(CSQC) || defined(SSQC)\nfloat(string) stof = #81; /* Part of FRIK_FILE, FTE_QC_INFOKEY, FTE_STRINGS, QW_ENGINE, ZQ_QC_STRINGS*/\n#endif\n#ifdef SSQC\n#define unicast(pl,reli) do{msg_entity = pl; multicast('0 0 0', reli?MULITCAST_ONE_R:MULTICAST_ONE);}while(0)\nvoid(vector where, float set) multicast = #82; /* Part of FTE_QC_MULTICAST\n\t\tOnce the MSG_MULTICAST network message buffer has been filled with data, this builtin is used to dispatch it to the given target, filtering by pvs for reduced network bandwidth. */\n\nDEP void(entity to, string str) redirectcmd = #101; /* Part of ??MVDSV_BUILTINS\n\t\tExecutes a single console command, and sends the text generated by it to the specified player. The command will be executed at the end of the frame once QC is no longer running - you may wish to pre/postfix it with 'echo'. */\n\n#endif\n#if defined(CSQC) || defined(SSQC)\nstring(float style, optional __out vector rgb) getlightstyle = #0:getlightstyle; /*\n\t\tObtains the light style string for the given style. */\n\nvector(float style) getlightstylergb = #0:getlightstylergb; /*\n\t\tObtains the current rgb value of the specified light style. In csqc, this is correct with regard to the current frame, while ssqc gives no guarentees about time and ignores client cvars. Note: use getlight if you want the actual light value at a point. */\n\n#endif\n#ifdef SSQC\nvoid(float style, float val, optional vector rgb) lightstylestatic = #5; /*\n\t\tSets the lightstyle to an explicit numerical level. From Hexen2. */\n\n#endif\n#if defined(CSQC) || defined(SSQC)\nvoid(vector start, vector mins, vector maxs, vector end, float nomonsters, entity ent) tracebox = #90; /* Part of DP_QC_TRACEBOX\n\t\tExactly like traceline, but a box instead of a uselessly thin point. Acceptable sizes are limited by bsp format, q1bsp has strict acceptable size values. */\n\nvector() randomvec = #91; /* Part of DP_QC_RANDOMVEC\n\t\tReturns a vector with random values. Each axis is independantly a value between -1 and 1 inclusive. */\n\nvector(vector org) getlight = #92;\nfloat(string cvarname, string defaultvalue) registercvar = #93; /* Part of DP_REGISTERCVAR\n\t\tCreates a new cvar on the fly. If it does not already exist, it will be given the specified value. If it does exist, this is a no-op.\n\t\tThis builtin has the limitation that it does not apply to configs or commandlines. Such configs will need to use the set or seta command causing this builtin to be a noop.\n\t\tIn engines that support it, you will generally find the autocvar feature easier and more efficient to use. */\n\nfloat(float a, float b, ...) min = #94; /* Part of DP_QC_MINMAXBOUND\n\t\tReturns the lowest value of its arguments. */\n\nfloat(float a, float b, ...) max = #95; /* Part of DP_QC_MINMAXBOUND\n\t\tReturns the highest value of its arguments. */\n\nfloat(float minimum, float val, float maximum) bound = #96; /* Part of DP_QC_MINMAXBOUND\n\t\tReturns val, unless minimum is higher, or maximum is less. */\n\nfloat(float value, float exp) pow = #97; /* Part of DP_QC_SINCOSSQRTPOW*/\n#endif\nfloat(float v, optional float base) logarithm = #0:logarithm; /*\n\t\tDetermines the logarithm of the input value according to the specified base. This can be used to calculate how much something was shifted by. */\n\n#if defined(CSQC) || defined(SSQC)\n#define findentity findfloat\nentity(entity start, .__variant fld, __variant match) findfloat = #98; /* Part of DP_QC_FINDFLOAT\n\t\tEquivelent to the find builtin, but instead of comparing strings contents, this builtin compares the raw values. This builtin requires multiple calls in order to scan all entities - set start to the previous call's return value.\n\t\tworld is returned when there are no more entities. */\n\nfloat(string extname) checkextension = #99; /*\n\t\tChecks for an extension by its name (eg: checkextension(\"FRIK_FILE\") says that its okay to go ahead and use strcat).\n\t\tUse cvar(\"pr_checkextension\") to see if this builtin exists. */\n\n#endif\nfloat(__variant funcref) checkbuiltin = #0:checkbuiltin; /*\n\t\tChecks to see if the specified builtin is supported/mapped. This is intended as a way to check for #0 functions, allowing for simple single-builtin functions. Warning, if two different engines map different builtins to the same number, then this function will not tell you which will be called, only that it won't crash (the exception being #0, which are remapped as available). */\n\n#ifdef SSQC\nfloat(string builtinname) builtin_find = #100; /*\n\t\tLooks to see if the named builtin is valid, and returns the builtin number it exists at. */\n\n#endif\n#if defined(CSQC) || defined(SSQC)\nfloat(float value) anglemod = #102;\n#endif\n#ifdef SSQC\nDEP_CSQC void(string slot, string picname, float x, float y, float zone, optional entity player) showpic = #104; /* Part of TEI_SHOWLMP2*/\nDEP_CSQC void(string slot, optional entity player) hidepic = #105; /* Part of TEI_SHOWLMP2*/\nDEP_CSQC void(string slot, float x, float y, float zone, optional entity player) movepic = #106; /* Part of TEI_SHOWLMP2*/\nDEP_CSQC void(string slot, string picname, optional entity player) changepic = #107; /* Part of TEI_SHOWLMP2*/\n#endif\n#if defined(CSQC) || defined(SSQC)\nfilestream(string filename, float mode, optional float mmapminsize) fopen = #110; /* Part of FRIK_FILE\n\t\tOpens a file, typically prefixed with \"data/\", for either read or write access. */\n\nvoid(filestream fhandle) fclose = #111; /* Part of FRIK_FILE*/\nstring(filestream fhandle) fgets = #112; /* Part of FRIK_FILE\n\t\tReads a single line out of the file. The new line character is not returned as part of the string. Returns the null string on EOF (use if not(string) to easily test for this, which distinguishes it from the empty string which is returned if the line being read is blank */\n\nvoid(filestream fhandle, string s, optional string s2, optional string s3, optional string s4, optional string s5, optional string s6, optional string s7) fputs = #113; /* Part of FRIK_FILE\n\t\tWrites the given string(s) into the file. For compatibility with fgets, you should ensure that the string is terminated with a \\n - this will not otherwise be done for you. It is up to the engine whether dos or unix line endings are actually written. */\n\n#endif\nint(filestream fhandle, void *ptr, int size) fread = #0:fread; /* Part of FTE_QC_FILE_BINARY\n\t\tReads binary data out of the file. Returns truncated lengths if the read exceeds the length of the file. */\n\nint(filestream fhandle, void *ptr, int size) fwrite = #0:fwrite; /* Part of FTE_QC_FILE_BINARY\n\t\tWrites binary data out of the file. */\n\n#define ftell fseek //c compat\nint(filestream fhandle, optional int newoffset) fseek = #0:fseek; /* Part of FTE_QC_FILE_BINARY\n\t\tChanges the current position of the file, if specified. Returns prior position, in bytes. */\n\nint(filestream fhandle, optional int newsize) fsize = #0:fsize; /* Part of FTE_QC_FILE_BINARY\n\t\tReports the total size of the file, in bytes. Can also be used to truncate/extend the file */\n\n#if defined(CSQC) || defined(SSQC)\nfloat(string s) strlen = #114; /* Part of FRIK_FILE, FTE_STRINGS, ZQ_QC_STRINGS*/\nstring(string s1, optional string s2, optional string s3, optional string s4, optional string s5, optional string s6, optional string s7, optional string s8) strcat = #115; /* Part of FRIK_FILE, FTE_STRINGS, ZQ_QC_STRINGS*/\nstring(string s, float start, float length) substring = #116; /* Part of FRIK_FILE, FTE_STRINGS, ZQ_QC_STRINGS*/\nvector(string s) stov = #117; /* Part of FRIK_FILE, FTE_STRINGS, ZQ_QC_STRINGS*/\nFTEDEP(\"Redundant\") string(string s, ...) strzone = #118; /* Part of FRIK_FILE, FTE_STRINGS, ZQ_QC_STRINGS\n\t\tCreate a semi-permanent copy of a string that only becomes invalid once strunzone is called on the string (instead of when the engine assumes your string has left scope). This builtin has become redundant in FTEQW due to the FTE_QC_PERSISTENTTEMPSTRINGS extension and is now functionally identical to strcat for compatibility with old engines+mods. */\n\nFTEDEP(\"Redundant\") void(string s) strunzone = #119; /* Part of FRIK_FILE, FTE_STRINGS, ZQ_QC_STRINGS\n\t\tDestroys a string that was allocated by strunzone. Further references to the string MAY crash the game. In FTE, this function became redundant and now does nothing. */\n\n#endif\nvoid*(int bytes) createbuffer = #0:createbuffer; /*\n\t\tReturns a temporary buffer that can be written to / read from. The buffer will be garbage collected and thus cannot be explicitly freed. Tempstrings and buffer references must not be stored into the buffer as the garbage collector will not scan these. */\n\n#ifdef SSQC\nvoid(string cvar, float val) cvar_setf = #176;\n#endif\n#if defined(CSQC) || defined(SSQC)\nvoid(string soundname, optional float channel, optional float volume) localsound = #177; /*\n\t\tPlays a sound... locally... probably best not to call this from ssqc. Also disables reverb. */\n\n#endif\n#ifdef SSQC\nfloat(string soundname, float queryonly) getsoundindex = #0:getsoundindex; /*\n\t\tProvides a way to query if a sound is already precached or not. The return value can also be checked for <=255 to see if it'll work over any network protocol. The sound index can also be used for writebyte hacks, but this is discouraged - use SOUNDFLAG_UNICAST instead. */\n\n#endif\n#if defined(CSQC) || defined(SSQC)\nfloat(string modelname, optional float queryonly) getmodelindex = #200; /*\n\t\tActs as an alternative to precache_model(foo);setmodel(bar, foo); return bar.modelindex;\n\t\tIf queryonly is set and the model was not previously precached, the builtin will return 0 without needlessly precaching the model. */\n\n__variant(float prnum, string funcname, ...) externcall = #201; /* Part of FTE_MULTIPROGS\n\t\tDirectly call a function in a different/same progs by its name.\n\t\tprnum=0 is the 'default' or 'main' progs.\n\t\tprnum=-1 means current progs.\n\t\tprnum=-2 will scan through the active progs and will use the first it finds. */\n\nfloat(string progsname) addprogs = #202; /* Part of FTE_MULTIPROGS\n\t\tLoads an additional .dat file into the current qcvm. The returned handle can be used with any of the externcall/externset/externvalue builtins.\n\t\tThere are cvars that allow progs to be loaded automatically. */\n\n__variant(float prnum, string varname) externvalue = #203; /* Part of FTE_MULTIPROGS\n\t\tReads a global in the named progs by the name of that global.\n\t\tprnum=0 is the 'default' or 'main' progs.\n\t\tprnum=-1 means current progs.\n\t\tprnum=-2 will scan through the active progs and will use the first it finds. */\n\nvoid(float prnum, __variant newval, string varname) externset = #204; /* Part of FTE_MULTIPROGS\n\t\tSets a global in the named progs by name.\n\t\tprnum=0 is the 'default' or 'main' progs.\n\t\tprnum=-1 means current progs.\n\t\tprnum=-2 will scan through the active progs and will use the first it finds. */\n\nvoid(entity portal, float state) openportal = #207; /*\n\t\tOpens or closes the portals associated with a door or some such on q2 or q3 maps. On Q2BSPs, the entity should be the 'func_areaportal' entity - its style field will say which portal to open. On Q3BSPs, the entity is the door itself, the portal will be determined by the two areas found from a preceding setorigin call. */\n\n#endif\n#ifdef SSQC\nfloat(float attributes, string effectname, ...) RegisterTempEnt = #208; /* Part of FTE_PEXT_CUSTOMTENTS*/\nvoid(float type, vector pos, ...) CustomTempEnt = #209; /* Part of FTE_PEXT_CUSTOMTENTS*/\nfloat(optional float sleeptime) fork = #210; /* Part of FTE_MULTITHREADED\n\t\tWhen called, this builtin simply returns. Twice.\n\t\tThe current 'thread' will return instantly with a return value of 0. The new 'thread' will return after sleeptime seconds with a return value of 1. See documentation for the 'sleep' builtin for limitations/requirements concerning the new thread. Note that QC should probably call abort in the new thread, as otherwise the function will return to the calling qc function twice also. */\n\n#endif\nvoid(optional __variant ret) abort = #211; /* Part of FTE_MULTITHREADED\n\t\tQC execution is aborted. Parent QC functions on the stack will be skipped, effectively this forces all QC functions to 'return ret' until execution returns to the engine. If ret is ommited, it is assumed to be 0. */\n\n#ifdef SSQC\nvoid(float sleeptime) sleep = #212; /* Part of FTE_MULTITHREADED\n\t\tSuspends the current QC execution thread for 'sleeptime' seconds.\n\t\tOther QC functions can and will be executed in the interim, including changing globals and field state (but not simultaneously).\n\t\tThe self and other globals will be restored when the thread wakes up (or set to world if they were removed since the thread started sleeping). Locals will be preserved, but will not be protected from remove calls.\n\t\tIf the engine is expecting the QC to return a value (even in the parent/root function), the value 0 shall be used instead of waiting for the qc to resume. */\n\nvoid(entity player, string key, string value) forceinfokey = #213; /* Part of FTE_FORCEINFOKEY\n\t\tDirectly changes a user's info without pinging off the client. Also allows explicitly setting * keys, including *spectator. Does not affect the user's config or other servers. */\n\nvoid(entity player, string key, void *data, int size) forceinfokeyblob = #0:forceinfokeyblob; /*\n\t\tDirectly changes a user's info without pinging off the client. Also allows explicitly setting * keys, including *spectator. Does not affect the user's config or other servers. */\n\n#endif\n#if defined(CSQC) || defined(SSQC)\nvoid(vector org, vector dmin, vector dmax, float colour, float effect, float count) particle2 = #215; /* Part of FTE_HEXEN2*/\nvoid(vector org, vector box, float colour, float effect, float count) particle3 = #216; /* Part of FTE_HEXEN2*/\nvoid(vector org, float radius, float colour, float effect, float count) particle4 = #217; /* Part of FTE_HEXEN2*/\nfloat(float number, float quantity) bitshift = #218; /* Part of EXT_BITSHIFT*/\nvoid(vector pos) te_lightningblood = #219; /* Part of FTE_TE_STANDARDEFFECTBUILTINS*/\n#endif\nfloat(string s1, string sub, optional float startidx) strstrofs = #221; /* Part of FTE_STRINGS\n\t\tReturns the 0-based offset of sub within the s1 string, or -1 if sub is not in s1.\n\t\tIf startidx is set, this builtin will ignore matches before that 0-based offset. */\n\nfloat(string str, float index) str2chr = #222; /* Part of FTE_STRINGS\n\t\tRetrieves the character value at offset 'index'. */\n\nstring(float chr, ...) chr2str = #223; /* Part of FTE_STRINGS\n\t\tThe input floats are considered character values, and are concatenated. */\n\nstring(float ccase, float redalpha, float redchars, string str, ...) strconv = #224; /* Part of FTE_STRINGS\n\t\tConverts quake chars in the input string amongst different representations.\n\t\tccase specifies the new case for letters.\n\t\t 0: not changed.\n\t\t 1: forced to lower case.\n\t\t 2: forced to upper case.\n\t\tredalpha and redchars switch between colour ranges.\n\t\t 0: no change.\n\t\t 1: Forced white.\n\t\t 2: Forced red.\n\t\t 3: Forced gold(low) (numbers only).\n\t\t 4: Forced gold (high) (numbers only).\n\t\t 5+6: Forced to white and red alternately.\n\t\tYou should not use this builtin in combination with UTF-8. */\n\nstring(float pad, string str1, ...) strpad = #225; /* Part of FTE_STRINGS\n\t\tPads the string with spaces, to ensure its a specific length (so long as a fixed-width font is used, anyway). If pad is negative, the spaces are added on the left. If positive the padding is on the right. */\n\ninfostring(infostring old, string key, string value) infoadd = #226; /* Part of FTE_STRINGS\n\t\tReturns a new tempstring infostring with the named value changed (or added if it was previously unspecified). Key and value may not contain the \\ character. */\n\nstring(infostring info, string key) infoget = #227; /* Part of FTE_STRINGS\n\t\tReads a named value from an infostring. The returned value is a tempstring */\n\n#define strcmp strncmp\nfloat(string s1, string s2, optional float len, optional float s1ofs, optional float s2ofs) strncmp = #228; /* Part of FTE_STRINGS\n\t\tCompares up to 'len' chars in the two strings. s1ofs allows you to treat s2 as a substring to compare against, or should be 0.\n\t\tReturns 0 if the two strings are equal, a negative value if s1 appears numerically lower, and positive if s1 appears numerically higher. */\n\nfloat(string s1, string s2) strcasecmp = #229; /* Part of FTE_STRINGS\n\t\tCompares the two strings without case sensitivity.\n\t\tReturns 0 if they are equal. The sign of the return value may be significant, but should not be depended upon. */\n\nfloat(string s1, string s2, float len, optional float s1ofs, optional float s2ofs) strncasecmp = #230; /* Part of FTE_STRINGS\n\t\tCompares up to 'len' chars in the two strings without case sensitivity. s1ofs allows you to treat s2 as a substring to compare against, or should be 0.\n\t\tReturns 0 if they are equal. The sign of the return value may be significant, but should not be depended upon. */\n\nstring(string s) strtrim = #0:strtrim; /*\n\t\tTrims the whitespace from the start+end of the string. */\n\n#if defined(CSQC) || defined(SSQC)\n__deprecated(\"Use strftime.\") void() calltimeofday = #231; /* Part of FTE_CALLTIMEOFDAY\n\t\tAsks the engine to instantly call the qc's 'timeofday' function, before returning. For compatibility with mvdsv.\n\t\ttimeofday should have the prototype: void(float secs, float mins, float hour, float day, float mon, float year, string strvalue)\n\t\tThe strftime builtin is more versatile and less weird. */\n\n#endif\n#ifdef SSQC\nvoid(float num, float type, .__variant fld) clientstat = #232; /*\n\t\tSpecifies what data to use in order to send various stats, in a client-specific way.\n\t\t'num' should be a value between 32 and 127, other values are reserved.\n\t\t'type' must be set to one of the EV_* constants, one of EV_FLOAT, EV_STRING, EV_INTEGER, EV_ENTITY.\n\t\tfld must be a reference to the field used, each player will be sent only their own copy of these fields. */\n\nvoid(float num, float type, string name) globalstat = #233; /*\n\t\tSpecifies what data to use in order to send various stats, in a non-client-specific way. num and type are as in clientstat, name however, is the name of the global to read in the form of a string (pass \"foo\"). */\n\nvoid(float num, float type, __variant *address) pointerstat = #0:pointerstat; /*\n\t\tSpecifies what data to use in order to send various stats, in a non-client-specific way. num and type are as in clientstat, address however, is the address of the variable you would like to use (pass &foo). */\n\nfloat(entity player) isbackbuffered = #234; /* Part of FTE_ISBACKBUFFERED\n\t\tReturns if the given player's network buffer will take multiple network frames in order to clear. If this builtin returns non-zero, you should delay or reduce the amount of reliable (and also unreliable) data that you are sending to that client. */\n\n#endif\n#if defined(CSQC) || defined(SSQC)\nvoid(vector angle) rotatevectorsbyangle = #235; /*\n\t\trotates the v_forward,v_right,v_up matrix by the specified angles. */\n\nvoid(vector fwd, vector right, vector up) rotatevectorsbyvectors = #236;\nfloat(float mdlindex, string skinname) skinforname = #237;\n#endif\n#if defined(CSQC) || defined(MENU)\nfloat(string shadername, optional string defaultshader, ...) shaderforname = #238; /* Part of FTE_FORCESHADER\n\t\tCaches the named shader and returns a handle to it.\n\t\tIf the shader could not be loaded from disk (missing file or ruleset_allow_shaders 0), it will be created from the 'defaultshader' string if specified, or a 'skin shader' default will be used.\n\t\tdefaultshader if not empty should include the outer {} that you would ordinarily find in a shader. */\n\n#endif\n#ifdef CSQC\nvoid(string shadername, string replacement, float timeoffset) remapshader = #0:remapshader; /*\n\t\tAll surfaces drawn with the specified shader will instead be drawn using the specified replacement shader. Shaders can be remapped to something else later by using the same source shadername. This is mostly useful for worldmodel surfaces (eg showing which team is currently winning). Entities should generally use setcustomskin or forceshader instead. Remaps will be forgotten on vid_reload, but can be reapplied via CSQC_RendererRestarted. */\n\n#endif\n#if defined(CSQC) || defined(SSQC)\nvoid(vector org, optional float count) te_bloodqw = #239; /* Part of FTE_TE_STANDARDEFFECTBUILTINS*/\n#endif\n#ifdef SSQC\nvoid(entity ent) te_muzzleflash = #0:te_muzzleflash;\n#endif\n#if defined(CSQC) || defined(SSQC)\nfloat(vector viewpos, entity entity) checkpvs = #240; /* Part of FTE_QC_CHECKPVS*/\n#endif\n#ifdef SSQC\nentity(string match, optional float matchnum) matchclientname = #241; /* Part of FTE_QC_MATCHCLIENTNAME*/\n#endif\nfloat(string destaddress, string content) sendpacket = #242; /* Part of FTE_QC_SENDPACKET\n\t\tSends a UDP packet to the specified destination. Note that the payload will be prefixed with four 255 bytes as a sort of security feature. */\n\n#ifdef CSQC\nvector(entity ent, float tagnum) rotatevectorsbytag = #244;\n#endif\n#if defined(CSQC) || defined(SSQC)\nfloat(float dividend, float divisor) mod = #245;\n#endif\n#ifdef SSQC\nfloat(optional string host, optional string user, optional string pass, optional string defaultdb, optional string driver) sqlconnect = #250; /* Part of FTE_SQL*/\nvoid(float serveridx) sqldisconnect = #251; /* Part of FTE_SQL*/\nfloat(float serveridx, void(float serveridx, float queryidx, float rows, float columns, float eof, float firstrow) callback, float querytype, string query) sqlopenquery = #252; /* Part of FTE_SQL*/\nvoid(float serveridx, float queryidx) sqlclosequery = #253; /* Part of FTE_SQL*/\nstring(float serveridx, float queryidx, float row, float column) sqlreadfield = #254; /* Part of FTE_SQL*/\nstring(float serveridx, optional float queryidx) sqlerror = #255; /* Part of FTE_SQL*/\nstring(float serveridx, string data) sqlescape = #256; /* Part of FTE_SQL*/\nstring(float serveridx) sqlversion = #257; /* Part of FTE_SQL*/\nfloat(float serveridx, float queryidx, float row, float column) sqlreadfloat = #258; /* Part of FTE_SQL*/\nint(float serveridx, float queryidx, float row, float column, __variant *ptr, int maxsize) sqlreadblob = #0:sqlreadblob;\nstring(float serveridx, __variant *ptr, int maxsize) sqlescapeblob = #0:sqlescapeblob;\n#endif\n#if defined(CSQC) || defined(SSQC)\nint(string) stoi = #259; /* Part of FTE_QC_INTCONV\n\t\tConverts the given string into a true integer. Base 8, 10, or 16 is determined based upon the format of the string. */\n\nstring(int) itos = #260; /* Part of FTE_QC_INTCONV\n\t\tConverts the passed true integer into a base10 string. */\n\nint(string) stoh = #261; /* Part of FTE_QC_INTCONV\n\t\tReads a base-16 string (with or without 0x prefix) as an integer. Bugs out if given a base 8 or base 10 string. :P */\n\nstring(int) htos = #262; /* Part of FTE_QC_INTCONV\n\t\tFormats an integer as a base16 string, with leading 0s and no prefix. Always returns 8 characters. */\n\n#endif\nint(float) ftoi = #0:ftoi; /* Part of FTE_QC_INTCONV\n\t\tConverts the given float into a true integer without depending on extended qcvm instructions. */\n\nfloat(int, optional float shift, float mask=24) itof = #0:itof; /* Part of FTE_QC_INTCONV\n\t\tConverts the given true integer into a float without depending on extended qcvm instructions. If shift and mask are specified then only specific parts of the integer will be cast to float. */\n\n#if defined(CSQC) || defined(SSQC)\nfloat(float modlindex, optional float useabstransforms) skel_create = #263; /* Part of FTE_CSQC_SKELETONOBJECTS\n\t\tAllocates a new uninitiaised skeletal object, with enough bone info to animate the given model.\n\t\teg: self.skeletonobject = skel_create(self.modelindex); */\n\nfloat(float skel, entity ent, float modelindex, float retainfrac, float firstbone, float lastbone, optional float addfrac) skel_build = #264; /* Part of FTE_CSQC_SKELETONOBJECTS\n\t\tAnimation data (according to the entity's frame info) is pulled from the specified model and blended into the specified skeletal object.\n\t\tIf retainfrac is set to 0 on the first call and 1 on the others, you can blend multiple animations together according to the addfrac value. The final weight should be 1. Other values will result in scaling and/or other weirdness. You can use firstbone and lastbone to update only part of the skeletal object, to allow legs to animate separately from torso, use 0 for both arguments to specify all, as bones are 1-based. */\n\ntypedef struct\n{\n\tint sourcemodelindex; /*frame data will be imported from this model, bones must be compatible*/\n\tint reserved;\n\tint firstbone;\n\tint lastbone;\n\tfloat prescale;\t/*0 destroys existing data, 1 retains it*/\n\tfloat scale[4];\t/*you'll need to do lerpfrac manually*/\n\tint animation[4];\n\tfloat animationtime[4];\n\t/*halflife models*/\n\tfloat subblend[2];\n\tfloat controllers[5];\n} skelblend_t;\nfloat(float skel, int numblends, skelblend_t *weights, int structsize) skel_build_ptr = #0:skel_build_ptr; /*\n\t\tLike skel_build, but slightly simpler. */\n\nfloat(float skel) skel_get_numbones = #265; /* Part of FTE_CSQC_SKELETONOBJECTS\n\t\tRetrives the number of bones in the model. The valid range is 1<=bone<=numbones. */\n\nstring(float skel, float bonenum) skel_get_bonename = #266; /* Part of FTE_CSQC_SKELETONOBJECTS\n\t\tRetrieves the name of the specified bone. Mostly only for debugging. */\n\nfloat(float skel, float bonenum) skel_get_boneparent = #267; /* Part of FTE_CSQC_SKELETONOBJECTS\n\t\tRetrieves which bone this bone's position is relative to. Bone 0 refers to the entity's position rather than an actual bone */\n\nfloat(float skel, string tagname) skel_find_bone = #268; /* Part of FTE_CSQC_SKELETONOBJECTS\n\t\tFinds a bone by its name, from the model that was used to create the skeletal object. */\n\nvector(float skel, float bonenum) skel_get_bonerel = #269; /* Part of FTE_CSQC_SKELETONOBJECTS\n\t\tGets the bone position and orientation relative to the bone's parent. Return value is the offset, and v_forward, v_right, v_up contain the orientation. */\n\nvector(float skel, float bonenum) skel_get_boneabs = #270; /* Part of FTE_CSQC_SKELETONOBJECTS\n\t\tGets the bone position and orientation relative to the entity. Return value is the offset, and v_forward, v_right, v_up contain the orientation.\n\t\tUse gettaginfo for world coord+orientation. */\n\nvoid(float skel, float bonenum, vector org, optional vector fwd, optional vector right, optional vector up) skel_set_bone = #271; /* Part of FTE_CSQC_SKELETONOBJECTS\n\t\tSets a bone position relative to its parent. If the orientation arguments are not specified, v_forward+v_right+v_up are used instead. */\n\nvoid(float skel, float bonenum, vector org, optional vector fwd, optional vector right, optional vector up) skel_premul_bone = #272; /* Part of FTE_CSQC_SKELETONOBJECTS\n\t\tTransforms a single bone by a matrix. You can use makevectors to generate a rotation matrix from an angle. */\n\nvoid(float skel, float startbone, float endbone, vector org, optional vector fwd, optional vector right, optional vector up) skel_premul_bones = #273; /* Part of FTE_CSQC_SKELETONOBJECTS\n\t\tTransforms an entire consecutive range of bones by a matrix. You can use makevectors to generate a rotation matrix from an angle, but you'll probably want to divide the angle by the number of bones. */\n\nvoid(float skel, float bonenum, vector org, optional vector fwd, optional vector right, optional vector up) skel_postmul_bone = #0:skel_postmul_bone; /*\n\t\tTransforms a single bone by a matrix. You can use makevectors to generate a rotation matrix from an angle. */\n\nvoid(float skeldst, float skelsrc, float startbone, float entbone) skel_copybones = #274; /* Part of FTE_CSQC_SKELETONOBJECTS\n\t\tCopy bone data from one skeleton directly into another. */\n\nvoid(float skel) skel_delete = #275; /* Part of FTE_CSQC_SKELETONOBJECTS\n\t\tDeletes a skeletal object. The actual delete is delayed, allowing the skeletal object to be deleted in an entity's predraw function yet still be valid by the time the addentity+renderscene builtins need it. Also uninstanciates any ragdoll currently in effect on the skeletal object. */\n\nfloat(float modidx, string framename) frameforname = #276; /* Part of FTE_CSQC_SKELETONOBJECTS\n\t\tLooks up a framegroup from a model by name, avoiding the need for hardcoding. Returns -1 on error. */\n\nfloat(float modidx, float framenum) frameduration = #277; /* Part of FTE_CSQC_SKELETONOBJECTS\n\t\tRetrieves the duration (in seconds) of the specified framegroup. */\n\nvoid(float modidx, float framenum, __inout float basetime, float targettime, void(float timestamp, int code, string data) callback) processmodelevents = #0:processmodelevents; /* Part of FTE_GFX_MODELEVENTS\n\t\tCalls a callback for each event that has been reached. Basetime is set to targettime. */\n\nfloat(float modidx, float framenum, __inout float basetime, float targettime, __out int code, __out string data) getnextmodelevent = #0:getnextmodelevent; /*\n\t\tReports the next event within a model's animation. Returns a boolean if an event was found between basetime and targettime. Writes to basetime,code,data arguments (if an event was found, basetime is set to the event's time, otherwise to targettime).\n\t\tWARNING: this builtin cannot deal with multiple events with the same timestamp (only the first will be reported). */\n\nfloat(float modidx, float framenum, int eventidx, __out float timestamp, __out int code, __out string data) getmodeleventidx = #0:getmodeleventidx; /*\n\t\tReports an indexed event within a model's animation. Writes to timestamp,code,data arguments on success. Returns false if the animation/event/model was out of range/invalid. Does not consider looping animations (retry from index 0 if it fails and you know that its a looping animation). This builtin is more annoying to use than getnextmodelevent, but can be made to deal with multiple events with the exact same timestamp. */\n\n#endif\n#define dotproduct(v1,v2) ((vector)(v1)*(vector)(v2))\nvector(vector v1, vector v2) crossproduct = #0:crossproduct; /* Part of FTE_QC_CROSSPRODUCT\n\t\tSmall helper function to calculate the crossproduct of two vectors. */\n\n#if defined(CSQC) || defined(SSQC)\nfloat(entity pusher, vector move, vector amove) pushmove = #0:pushmove;\n__variant(float action, optional vector pos, optional float radius, optional float quant, ...) terrain_edit = #278; /* Part of FTE_TERRAIN_MAP\n\t\tRealtime terrain editing. Actions are the TEREDIT_ constants. */\n\ntypedef struct\n{\n\tstring\tshadername;\n\tvector\tplanenormal;\n\tfloat\tplanedist;\n\tvector\tsdir;\n\tfloat\tsbias;\n\tvector\ttdir;\n\tfloat\ttbias;\n} brushface_t;\nint(float modelidx, int brushid, brushface_t *out_faces, int maxfaces, int *out_contents) brush_get = #0:brush_get; /* Part of FTE_RAW_MAP\n\t\tQueries a brush's information. You must pre-allocate the face array for the builtin to write to. Return value is the number of faces retrieved, 0 on error. */\n\nint(float modelidx, brushface_t *in_faces, int numfaces, int contents, optional int brushid) brush_create = #0:brush_create; /* Part of FTE_RAW_MAP\n\t\tInserts a new brush into the model. Return value is the new brush's id. */\n\nvoid(float modelidx, int brushid) brush_delete = #0:brush_delete; /* Part of FTE_RAW_MAP\n\t\tDestroys the specified brush. */\n\nfloat(float modelid, int brushid, int faceid, float selectedstate) brush_selected = #0:brush_selected; /* Part of FTE_RAW_MAP\n\t\tAllows you to easily set transient visual properties of a brush. returns old value. selectedstate=-1 changes nothing (called for its return value). */\n\nint(float modelid, int brushid, int faceid, vector *points, int maxpoints) brush_getfacepoints = #0:brush_getfacepoints; /* Part of FTE_RAW_MAP\n\t\tReturns the list of verticies surrounding the given face. If face is 0, returns the center of the brush (if space for 1 point) or the mins+maxs (if space for 2 points). */\n\nint(int faceid, brushface_t *in_faces, int numfaces, vector *points, int maxpoints) brush_calcfacepoints = #0:brush_calcfacepoints; /* Part of FTE_RAW_MAP\n\t\tDetermines the points of the specified face, if the specified brush were to actually be created. */\n\nint(float modelid, vector *planes, float *dists, int numplanes, int *out_brushes, int *out_faces, int maxresults) brush_findinvolume = #0:brush_findinvolume; /* Part of FTE_RAW_MAP\n\t\tAllows you to easily obtain a list of brushes+faces within the given bounding region. If out_faces is not null, the same brush might be listed twice. */\n\ntypedef struct\n{\n\tstring shadername;\n\tint contents;\n\tint cpwidth;\n\tint cpheight;\n\tint tesswidth;\n\tint tessheight;\n\tvector texinfo;/*scalex,y,rot*/\n} patchinfo_t;\ntypedef struct\n{\n\tvector xyz;\n\tvector rgb; float a;\n\tfloat s, t;\n} patchvert_t;\n#define patch_delete(modelidx,patchidx) brush_delete(modelidx,patchidx)\nint(float modelidx, int patchid, patchvert_t *out_controlverts, int maxcp, patchinfo_t *out_info) patch_getcp = #0:patch_getcp; /*\n\t\tQueries a patch's information. You must pre-allocate the face array for the builtin to write to. Return value is the total number of control verts that were retrieved, 0 on error. */\n\nint(float modelidx, int patchid, patchvert_t *out_verts, int maxverts, __out patchinfo_t out_info) patch_getmesh = #0:patch_getmesh; /*\n\t\tQueries a patch's information. You must pre-allocate the face array for the builtin to write to. Return value is the total number of control verts that were retrieved, 0 on error. */\n\nint(float modelidx, int oldpatchid, patchvert_t *in_controlverts, patchinfo_t in_info) patch_create = #0:patch_create; /*\n\t\tInserts a new patch into the model. Return value is the new patch's id. */\n\ntypedef struct\n{\n\tvector dest;\n\tint linkflags;\n} nodeslist_t;\nvoid(entity ent, vector dest, int denylinkflags, void(entity ent, vector dest, int numnodes, nodeslist_t *nodelist) callback) route_calculate = #0:route_calculate; /*\n\t\tBegin calculating a route. The callback function will be called once the route has finished being calculated. The route must be memfreed once it is no longer needed. The route must be followed in reverse order (ie: the first node that must be reached is at index numnodes-1). If no route is available then the callback will be called with no nodes. */\n\nvoid(optional entity ent, optional vector neworigin) touchtriggers = #279; /*\n\t\tTriggers a touch events between self and every SOLID_TRIGGER entity that it is in contact with. This should typically just be the triggers touch functions. Also optionally updates the origin of the moved entity. */\n\n#endif\n#ifdef SSQC\nvoid(float buf, float fl) WriteFloat = #280; /*\n\t\tWrites a full 32bit float without any data conversions at all, for full precision. */\n\nvoid(float buf, __double dbl) WriteDouble = #0:WriteDouble; /*\n\t\tWrites a full 64bit double-precision float without any data conversions at all, for excessive precision. */\n\nvoid(float buf, int fl) WriteInt = #0:WriteInt; /*\n\t\tWrites all 4 bytes of a 32bit integer without truncating to a float first before converting back to an int (unlike WriteLong does, but otherwise equivelent). */\n\nvoid(float buf, __int64 fl) WriteInt64 = #0:WriteInt64; /*\n\t\tWrites all 8 bytes of a 64bit integer. */\n\n#endif\n#if defined(CSQC) || defined(SSQC)\nfloat(entity skelent, string dollcmd, float animskel) skel_ragupdate = #281; /*\n\t\tUpdates the skeletal object attached to the entity according to its origin and other properties.\n\t\tif animskel is non-zero, the ragdoll will animate towards the bone state in the animskel skeletal object, otherwise they will pick up the model's base pose which may not give nice results.\n\t\tIf dollcmd is not set, the ragdoll will update (this should be done each frame).\n\t\tIf the doll is updated without having a valid doll, the model's default .doll will be instanciated.\n\t\tcommands:\n\t\t doll foo.doll : sets up the entity to use the named doll file\n\t\t dollstring TEXT : uses the doll file directly embedded within qc, with that extra prefix.\n\t\t cleardoll : uninstanciates the doll without destroying the skeletal object.\n\t\t animate 0.5 : specifies the strength of the ragdoll as a whole \n\t\t animatebody somebody 0.5 : specifies the strength of the ragdoll on a specific body (0 will disable ragdoll animations on that body).\n\t\t enablejoint somejoint 1 : enables (or disables) a joint. Disabling joints will allow the doll to shatter. */\n\nfloat*(float skel) skel_mmap = #282; /*\n\t\tMap the bones in VM memory. They can then be accessed via pointers. Each bone is 12 floats, the four vectors interleaved (sadly). */\n\nvoid(entity ent, float bonenum, vector org, optional vector angorfwd, optional vector right, optional vector up) skel_set_bone_world = #283; /*\n\t\tSets the world position of a bone within the given entity's attached skeletal object. The world position is dependant upon the owning entity's position. If no orientation argument is specified, v_forward+v_right+v_up are used for the orientation instead. If 1 is specified, it is understood as angles. If 3 are specified, they are the forawrd/right/up vectors to use. */\n\nstring(float modidx, float framenum) frametoname = #284;\nstring(float modidx, float skin) skintoname = #285;\nfloat(float resourcetype, float tryload, string resourcename) resourcestatus = #286; /*\n\t\tresourcetype must be one of the RESTYPE_ constants. Returns one of the RESSTATE_ constants. Tryload 0 is a query only. Tryload 1 will attempt to reload the content if it was flushed. */\n\n#endif\nhashtable(float tabsize, optional float defaulttype) hash_createtab = #287; /* Part of FTE_QC_HASHTABLES\n\t\tCreates a hash table object.\n\t\tThe tabsize argument is a performance hint and should generally be set to something similar to the number of entries expected, typically a power of two assumption. Too high simply wastes memory, too low results in extra string compares but no actual bugs.\n\t\tdefaulttype must be one of the EV_* values, if specified.\n\t\tThe hash table with index 0 is a game-persistant table and will NEVER be returned by this builtin (except as an error return). */\n\nvoid(hashtable table) hash_destroytab = #288; /* Part of FTE_QC_HASHTABLES\n\t\tDestroys a hash table object. */\n\nvoid(hashtable table, string name, __variant value, optional float typeandflags) hash_add = #289; /* Part of FTE_QC_HASHTABLES\n\t\tAdds the given key with the given value to the table.\n\t\tIf flags&HASH_REPLACE, the old value will be removed, otherwise if flags&HASH_ADD then a duplicate entry will be added with a second value (can be obtained via hash_get's index argument).\n\t\tThe type argument describes how the value should be stored in saved games, as well as providing constraints with the hash_get function. While you can claim that all variables are just vectors, being more precise can result in less issues with tempstrings or saved games - be sure to be explicit with EV_STRING where appropriate because tempstrings may be reclaimed before the get (especially with saved games or table 0). */\n\n__variant(hashtable table, string name, optional __variant deflt, optional float requiretype, optional float index) hash_get = #290; /* Part of FTE_QC_HASHTABLES\n\t\tLooks up the specified key name in the hash table. Returns deflt if the key was not found.\n\t\tIf requiretype is specified then the function will only consider entries of the matching type (allowing you to store both flags+strings under a single name without getting confused).\n\t\tIf index is specified then the function will ignore the first N entries with the same key (applicable only with entries added using HASH_ADD, not HASH_REPLACE), allowing you to store multiple entries. Keep querying higher indexes starting from 0 until it returns the deflt value.\n\t\tYou will usually need to cast the result of this function to a real datatype. */\n\n__variant(hashtable table, string name) hash_delete = #291; /* Part of FTE_QC_HASHTABLES\n\t\tremoves the named key. returns the value of the object that was destroyed, or 0 on error. */\n\nstring(hashtable table, float idx) hash_getkey = #292; /* Part of FTE_QC_HASHTABLES\n\t\tgets some random key name. add+delete can change return values of this, so don't blindly increment the key index if you're removing all. */\n\nfloat(string name) checkcommand = #294; /* Part of FTE_QC_CHECKCOMMAND\n\t\tChecks to see if the supplied name is a valid command, cvar, or alias. Returns 0 if it does not exist. */\n\nstring(string s) argescape = #295; /*\n\t\tMarks up a string so that it can be reliably tokenized as a single argument later. */\n\n#ifdef SSQC\nvoid(string dest, string from, string cmd, string info) clusterevent = #0:clusterevent; /*\n\t\tOnly functions in mapcluster mode. Sends an event to whichever server the named player is on. The destination server can then dispatch the event to the client or handle it itself via the SV_ParseClusterEvent entrypoint. If dest is empty, the event is broadcast to ALL servers. If the named player can't be found, the event will be returned to this server with the cmd prefixed with 'error:'. */\n\nstring(entity player, optional string newnode) clustertransfer = #0:clustertransfer; /*\n\t\tOnly functions in mapcluster mode. Initiate transfer of the player to a different node. Can take some time. If dest is specified, returns null on error. Otherwise returns the current/new target node (or null if not transferring). */\n\n#endif\n#if defined(CSQC) || defined(SSQC)\nfloat(float mdlidx) modelframecount = #0:modelframecount; /*\n\t\tRetrieves the number of frames in the specified model. */\n\n#endif\n#if defined(CSQC) || defined(MENU)\nvoid() clearscene = #300; /*\n\t\tForgets all rentities, polygons, and temporary dlights. Resets all view properties to their default values. */\n\n#endif\n#ifdef CSQC\nvoid(float mask) addentities = #301; /*\n\t\tWalks through all entities effectively doing this:\n\t\t if (ent.drawmask&mask){ if (!ent.predaw()) addentity(ent); }\n\t\tIf mask&MASK_DELTA, non-csqc entities, particles, and related effects will also be added to the rentity list.\n\t\t If mask&MASK_STDVIEWMODEL then the default view model will also be added. */\n\n#endif\n#if defined(CSQC) || defined(MENU)\nvoid(entity ent) addentity = #302; /*\n\t\tCopies the entity fields into a new rentity for later rendering via addscene. */\n\n#endif\n#ifdef CSQC\nvoid(entity ent) removeentity = #0:removeentity; /*\n\t\tUndoes all addentities added to the scene from the given entity, without removing ALL entities (useful for splitscreen/etc, readd modified versions as desired). */\n\ntypedef float vec2[2];\ntypedef float vec3[3];\ntypedef float vec4[4];\ntypedef struct trisoup_simple_vert_s {vec3 xyz;vec2 st;vec4 rgba;} trisoup_simple_vert_t;\nvoid(string texturename, int flags, struct trisoup_simple_vert_s *verts, int *indexes, int numindexes) addtrisoup_simple = #0:addtrisoup_simple; /*\n\t\tAdds the specified trisoup into the scene as additional geometry. This permits caching geometry to reduce builtin spam. Indexes are a triangle list (so eg quads will need 6 indicies to form two triangles). NOTE: this is not going to be a speedup over polygons if you're still generating lots of new data every frame. */\n\n#endif\n#if defined(CSQC) || defined(MENU)\n#define setviewprop setproperty\nfloat(float property, ...) setproperty = #303; /*\n\t\tAllows you to override default view properties like viewport, fov, and whether the engine hud will be drawn. Different VF_ values have slightly different arguments, some are vectors, some floats. */\n\nvoid() renderscene = #304; /*\n\t\tDraws all entities, polygons, and particles on the rentity list (which were added via addentities or addentity), using the various view properties set via setproperty. There is no ordering dependancy.\n\t\tThe scene must generally be cleared again before more entities are added, as entities will persist even over to the next frame.\n\t\tYou may call this builtin multiple times per frame, but should only be called from CSQC_UpdateView. */\n\n#endif\n#ifdef CSQC\nfloat(vector org, float radius, vector lightcolours, optional float style, optional string cubemapname, optional float pflags) dynamiclight_add = #305; /*\n\t\tAdds a temporary dlight, ready to be drawn via addscene. Cubemap orientation will be read from v_forward/v_right/v_up. */\n\n#endif\nvoid(string texturename, optional float flags, optional float is2d) R_BeginPolygon = #306; /*\n\t\tSpecifies the shader to use for the following polygons, along with optional flags.\n\t\tIf is2d, the polygon will be drawn as soon as the EndPolygon call is made, rather than waiting for renderscene. This allows complex 2d effects. */\n\nvoid(vector org, vector texcoords, vector rgb, float alpha) R_PolygonVertex = #307; /*\n\t\tSpecifies a polygon vertex with its various properties. */\n\nvoid() R_EndPolygon = #308; /*\n\t\tEnds the current polygon. At least 3 verticies must have been specified. You do not need to call beginpolygon again if you wish to draw another polygon with the same shader. */\n\n#ifdef CSQC\nvoid(float radius, vector texcoordbias) R_EndPolygonRibbon = #0:R_EndPolygonRibbon; /*\n\t\tEnds the current primitive and duplicates each vertex sideways into a ribbon. The texcoordbias will be added to each duplicated vertex allowing for regular 2d textures. At least 2 verticies must have been specified. You do not need to call beginpolygon again if you wish to draw another polygon with the same shader. */\n\n#endif\n#if defined(CSQC) || defined(MENU)\n#define getviewprop getproperty\n__variant(float property) getproperty = #309; /*\n\t\tRetrieve a currently-set (typically view) property, allowing you to read the current viewport or other things. Due to cheat protection, certain values may be unretrievable. */\n\n#endif\n#ifdef CSQC\nvector (vector v) unproject = #310; /*\n\t\tTransform a 2d screen-space point (with depth) into a 3d world-space point, according the various origin+angle+fov etc settings set via setproperty. */\n\nvector (vector v) project = #311; /*\n\t\tTransform a 3d world-space point into a 2d screen-space point, according the various origin+angle+fov etc settings set via setproperty. */\n\n#endif\n#if defined(CSQC) || defined(MENU)\nfloat(vector pos, vector size, float alignflags, string text) drawtextfield = #0:drawtextfield; /*\n\t\tDraws a multi-line block of text, including word wrapping and alignment. alignflags bits are RTLB, typically 3. Returns the total number of lines. */\n\n#endif\n#ifdef CSQC\nvoid(float width, vector pos1, vector pos2, vector rgb, float alpha, optional float drawflag) drawline = #315; /*\n\t\tDraws a 2d line between the two 2d points. */\n\nfloat(string name) iscachedpic = #316; /*\n\t\tChecks to see if the image is currently loaded. Engines might lie, or cache between maps. */\n\nstring(string name, optional float flags) precache_pic = #317; /*\n\t\tForces the engine to load the named image. Flags are a bitmask of the PRECACHE_PIC_* flags. */\n\n#endif\n#if defined(CSQC) || defined(MENU)\nvoid(string imagename, int width, int height, void *pixeldata, optional int datasize, optional int format) r_uploadimage = #0:r_uploadimage; /* Part of FTE_CSQC_RAWIMAGES\n\t\tUpdates a texture with the specified rgba data (uploading it to the gpu). Will be created if needed. If datasize is specified then the image is decoded (eg .ktx or .dds data) instead of being raw R8G8B8A data. You'll typically want shaderforname to also generate a shader to use the texture. */\n\nint*(string filename, __out int width, __out int height) r_readimage = #0:r_readimage; /* Part of FTE_CSQC_RAWIMAGES\n\t\tReads and decodes an image from disk, providing raw R8G8B8A8 pixel data. Should not be used for dds or ktx etc formats. Returns __NULL__ if the image could not be read for any reason. Use memfree to free the data once you're done with it. */\n\n#endif\n#ifdef CSQC\n#define draw_getimagesize drawgetimagesize\nvector(string picname) drawgetimagesize = #318; /*\n\t\tReturns the dimensions of the named image. Images specified with .lmp should give the original .lmp's dimensions even if texture replacements use a different resolution. WARNING: this function may be slow if used without or directly after its initial precache_pic. */\n\nvoid(string name) freepic = #319; /*\n\t\tTells the engine that the image is no longer needed. The image will appear to be new the next time its needed. */\n\nstring(string modelname, int frame, float frametime) spriteframe = #0:spriteframe; /*\n\t\tObtains a suitable shader name to draw a sprite's shader via drawpic/R_BeginPolygon/etc, instead of needing to create a scene. */\n\nfloat(vector position, float character, vector size, vector rgb, float alpha, optional float drawflag) drawcharacter = #320; /*\n\t\tDraw the given quake character at the given position.\n\t\tIf flag&4, the function will consider the char to be a unicode char instead (or display as a ? if outside the 32-127 range).\n\t\tsize should normally be something like '8 8 0'.\n\t\trgb should normally be '1 1 1'\n\t\talpha normally 1.\n\t\tSoftware engines may assume the named defaults.\n\t\tNote that ALL text may be rescaled on the X axis due to variable width fonts. The X axis may even be ignored completely. */\n\nfloat(vector position, string text, vector size, vector rgb, float alpha, optional float drawflag) drawrawstring = #321; /*\n\t\tDraws the specified string without using any markup at all, even in engines that support it.\n\t\tIf UTF-8 is globally enabled in the engine, then that encoding is used (without additional markup), otherwise it is raw quake chars.\n\t\tSoftware engines may assume a size of '8 8 0', rgb='1 1 1', alpha=1, flag&3=0, but it is not an error to draw out of the screen. */\n\nfloat(vector position, string pic, vector size, vector rgb, float alpha, optional float drawflag) drawpic = #322; /*\n\t\tDraws an shader within the given 2d screen box. Software engines may omit support for rgb+alpha, but must support rescaling, and must clip to the screen without crashing. */\n\nfloat(vector position, vector size, vector rgb, float alpha, optional float drawflag) drawfill = #323; /*\n\t\tDraws a solid block over the given 2d box, with given colour, alpha, and blend mode (specified via flags).\n\t\tflags&3=0 simple blend.\n\t\tflags&3=1 additive blend */\n\nvoid(float x, float y, float width, float height) drawsetcliparea = #324; /*\n\t\tSpecifies a 2d clipping region (aka: scissor test). 2d draw calls will all be clipped to this 2d box, the area outside will not be modified by any 2d draw call (even 2d polygons). */\n\nvoid(void) drawresetcliparea = #325; /*\n\t\tReverts the scissor/clip area to the whole screen. */\n\nfloat(vector position, string text, vector size, vector rgb, float alpha, float drawflag) drawstring = #326; /*\n\t\tDraws a string, interpreting markup and recolouring as appropriate. */\n\nfloat(string text, float usecolours, optional vector fontsize) stringwidth = #327; /*\n\t\tCalculates the width of the screen in virtual pixels. If usecolours is 1, markup that does not affect the string width will be ignored. Will always be decoded as UTF-8 if UTF-8 is globally enabled.\n\t\tIf the char size is not specified, '8 8 0' will be assumed. */\n\nvoid(vector pos, vector sz, string pic, vector srcpos, vector srcsz, vector rgb, float alpha, optional float drawflag) drawsubpic = #328; /*\n\t\tDraws a rescaled subsection of an image to the screen. */\n\n#endif\n#if defined(CSQC) || defined(MENU)\nvoid(vector pivot, vector mins, vector maxs, string pic, vector rgb, float alpha, float angle) drawrotpic = #0:drawrotpic; /*\n\t\tDraws an image rotating at the pivot. To rotate in the center, use mins+maxs of half the size with mins negated. Angle is in degrees. */\n\nvoid(vector pivot, vector mins, vector maxs, string pic, vector txmin, vector txsize, vector rgb, vector alphaandangles) drawrotsubpic = #0:drawrotsubpic; /*\n\t\tOvercomplicated draw function for over complicated people. Positions follow drawrotpic, while texture coords follow drawsubpic. Due to argument count limitations in builtins, the alpha value and angles are combined into separate fields of a vector (tip: use fteqcc's [alpha, angle] feature. */\n\n#endif\n#ifdef CSQC\n#define getstati_punf(stnum) (float)(__variant)getstati(stnum)\nint(float stnum) getstati = #330; /*\n\t\tRetrieves the full precision of a stat registered as EV_INTEGER. */\n\n#define getstatbits getstatf\nfloat(float stnum, optional float firstbit, optional float bitcount) getstatf = #331; /*\n\t\tRetrieves the numerical value of the given EV_FLOAT stat. If firstbit and bitcount are specified, then this builtin acts as getstati combined with itof, and which should be used for STAT_ITEMS (but not other stats). */\n\nstring(float stnum) getstats = #332; /*\n\t\tRetrieves the value of the given EV_STRING stat, as a tempstring.\n\t\tOlder engines may use 4 consecutive integer stats, with a limit of 15 chars (yes, really. 15.), but FTE Quake uses a separate namespace for string stats and has a much higher length limit. */\n\n__variant(float playernum, float statnum, float stattype) getplayerstat = #0:getplayerstat; /*\n\t\tRetrieves a specific player's stat, matching the type specified on the server. This builtin is primarily intended for mvd playback where ALL players are known. Return value matches the specified EV_ stattype. For EV_ENTITY, world will be returned if the entity is not in the pvs, use type-punning with EV_INTEGER to get the entity number if you just want to see if its set. STAT_ITEMS should be queried as an EV_INTEGER on account of runes and items2 being packed into the upper bits. */\n\nvoid(entity e, float mdlindex) setmodelindex = #333; /*\n\t\tSets a model by precache index instead of by name. Otherwise identical to setmodel. */\n\nstring(float mdlindex) modelnameforindex = #334; /*\n\t\tRetrieves the name of the model based upon a precache index. This can be used to reduce csqc network traffic by enabling model matching. */\n\n#endif\n#if defined(CSQC) || defined(SSQC)\nfloat(string effectname) particleeffectnum = #335; /* Part of DP_ENT_TRAILEFFECTNUM, FTE_SV_POINTPARTICLES\n\t\tPrecaches the named particle effect. If your effect name is of the form 'foo.bar' then particles/foo.cfg will be loaded by the client if foo.bar was not already defined.\n\t\tDifferent engines will have different particle systems, this specifies the QC API only. */\n\nvoid(float effectnum, entity ent, vector start, vector end) trailparticles = #336; /* Part of FTE_SV_POINTPARTICLES\n\t\tDraws the given effect between the two named points. If ent is not world, distances will be cached in the entity in order to avoid framerate dependancies. The entity is not otherwise used. */\n\nvoid(float effectnum, vector origin, optional vector dir, optional float count) pointparticles = #337; /* Part of FTE_SV_POINTPARTICLES\n\t\tSpawn a load of particles from the given effect at the given point traveling or aiming along the direction specified. The number of particles are scaled by the count argument.\n\t\tFor regular particles, the dir vector is multiplied by the 'veladd' property (while orgadd will push the particles along it). Decals will use it as a hint to align to the correct surface. In both cases, it should normally be a unit vector, but other lengths will still work. If it has length 0 then FTE will assume downwards. */\n\n#endif\n#ifdef CSQC\nvoid(string s, ...) cprint = #338; /*\n\t\tPrint into the center of the screen just as ssqc's centerprint would appear. */\n\n#endif\n#if defined(CSQC) || defined(SSQC)\nvoid(string s, ...) print = #339; /* Part of DP_SV_PRINT\n\t\tUnconditionally print on the local system's console, even in ssqc (doesn't care about the value of the developer cvar). */\n\n#endif\n#ifdef CSQC\nstring(float keynum) keynumtostring = #340; /*\n\t\tReturns a hunam-readable name for the given keycode, as a tempstring. */\n\n#endif\n#ifdef MENU\nDEP string(float keynum) keynumtostring_csqc = #340; /*\n\t\tReturns a hunam-readable name for the given keycode, as a tempstring. */\n\n#endif\n#ifdef CSQC\nfloat(string keyname) stringtokeynum = #341; /*\n\t\tLooks up the key name in the same way that the bind command would, returning the keycode for that key. */\n\n#endif\n#ifdef MENU\nDEP float(string keyname) stringtokeynum_csqc = #341; /*\n\t\tLooks up the key name in the same way that the bind command would, returning the keycode for that key. */\n\n#endif\n#if defined(CSQC) || defined(MENU)\nstring(float keynum) getkeybind = #342; /*\n\t\tReturns the current binding for the given key (returning only the command executed when no modifiers are pressed). */\n\nvoid(float usecursor, optional string cursorimage, optional vector hotspot, optional float scale) setcursormode = #343; /*\n\t\tPass TRUE if you want the engine to release the mouse cursor (absolute input events + touchscreen mode). Pass FALSE if you want the engine to grab the cursor (relative input events + standard looking). If the image name is specified, the engine will use that image for a cursor (use an empty string to clear it again), in a way that will not conflict with the console. Images specified this way will be hardware accelerated, if supported by the platform/port. */\n\nfloat(float effective) getcursormode = #0:getcursormode; /*\n\t\tReports the cursor mode this module previously attempted to use. If 'effective' is true, reports the cursor mode currently active (if was overriden by a different module which has precidence, for instance, or if there is only a touchscreen and no mouse). */\n\n#endif\n#ifdef CSQC\nvector() getmousepos = #344; /*\n\t\tNasty convoluted DP extension. Typically returns deltas instead of positions. Use CSQC_InputEvent instead for such things in csqc mods. */\n\n#endif\n#if defined(CSQC) || defined(MENU)\nvoid(vector newpos) setmousepos = #0:setmousepos; /*\n\t\tWarps the mouse cursor to the given location. Should normally only be done following setcursormode(TRUE,...). The warp MAY be visible through *_InputEvent, but normally be seen as an IE_ABSMOUSE event anyway. Not all systems support cursor warping (or even cursors), so this is a hint only and you should not depend upon it. */\n\n#endif\n#ifdef CSQC\nfloat(float inputsequencenum) getinputstate = #345; /*\n\t\tLooks up an input frame from the log, setting the input_* globals accordingly.\n\t\tThe sequence number range used for prediction should normally be servercommandframe < sequence <= clientcommandframe.\n\t\tThe sequence equal to clientcommandframe will change between input frames. */\n\nvoid(float sens) setsensitivityscaler = #346; /*\n\t\tTemporarily scales the player's mouse sensitivity based upon something like zoom, avoiding potential cvar saving and thus corruption. */\n\n#endif\n#if defined(CSQC) || defined(SSQC)\nvoid(entity ent) runstandardplayerphysics = #347; /*\n\t\tPerform the engine's standard player movement prediction upon the given entity using the input_* globals to describe movement. */\n\n#endif\n#ifdef CSQC\nstring(float playernum, string keyname) getplayerkeyvalue = #348; /*\n\t\tLook up a player's userinfo, to discover things like their name, topcolor, bottomcolor, skin, team, *ver.\n\t\tAlso includes scoreboard info like frags, ping, pl, userid, entertime, as well as voipspeaking and voiploudness. */\n\nfloat(float playernum, string keyname, optional float assumevalue) getplayerkeyfloat = #0:getplayerkeyfloat; /*\n\t\tCheaper version of getplayerkeyvalue that avoids the need for so many tempstrings. */\n\nint(float playernum, string keyname, optional void *outptr, int size) getplayerkeyblob = #0:getplayerkeyblob; /*\n\t\tObtains a copy of the full data blob. Will write up to size bytes but return the full size. Does not null terminate (but memalloc(ret+1) will, if you want to cast the buffer to a string), and the blob may contain embedded nulls. Ignores all special keys, returning only what is actually there. */\n\n#endif\n#if defined(CSQC) || defined(MENU)\nvoid(float seat, string keyname, string newvalue) setlocaluserinfo = #0:setlocaluserinfo; /*\n\t\tChange a userinfo key for the specified local player seat, equivelent to the setinfo console command. The server will normally forward the setting to other clients. */\n\nstring(float seat, string keyname) getlocaluserinfo = #0:getlocaluserinfo; /*\n\t\tReads a local userinfo key for the specified local player seat. This is not quite the same as getplayerkeyvalue, due to latency and possible serverside filtering. */\n\nvoid(float seat, string keyname, void *outptr, int size) setlocaluserinfoblob = #0:setlocaluserinfoblob; /*\n\t\tSets the userinfo key to a blob that may contain nulls etc. Keys with a leading underscore will be visible to only the server (for user-specific binary settings). */\n\nint(float seat, string keyname, void *outptr, int maxsize) getlocaluserinfoblob = #0:getlocaluserinfoblob; /*\n\t\tObtains a copy of the full data blob. Will write up to size bytes but return the full size. Does not null terminate (but memalloc(ret+1) will, if you want to cast the buffer to a string), and the blob may contain embedded nulls. Ignores all special keys, returning only what is actually there. */\n\n#endif\n#ifdef SSQC\nint(string keyname, optional void *outptr, int size) getlocalinfo = #0:getlocalinfo; /*\n\t\tObtains a copy of a data blob (with spaces) from the server's private localinfo. Will write up to size bytes and return the actual size. Does not null terminate (but memalloc(ret+1) will, if you want to cast the buffer to a string), and the blob may contain embedded nulls. Ignores all special keys, returning only what is actually there. */\n\nvoid(string keyname, optional void *outptr, int size) setlocalinfo = #0:setlocalinfo; /*\n\t\tChanges the server's private localinfo. This data will be available for the following map, and will *usually* reload with saved games. */\n\n#endif\n#if defined(CSQC) || defined(MENU)\nfloat() isdemo = #349; /*\n\t\tReturns if the client is currently playing a demo or not. Returns 2 when playing an mvd (where other player's stats can be queried, or the pov can be changed freely). */\n\n#endif\n#ifdef CSQC\nfloat() isserver = #350; /*\n\t\tReturns non-zero whenever the local console can directly affect the server (ie: listen servers or single-player). Compat note: DP returns 0 for single-player. */\n\nvoid(vector origin, vector forward, vector right, vector up, optional float reverbtype) SetListener = #351; /*\n\t\tSets the position of the view, as far as the audio subsystem is concerned. This should be called once per CSQC_UpdateView as it will otherwise revert to default. For reverbtype, see setup_reverb or treat as 'underwater'. */\n\ntypedef struct {\n\tfloat flDensity;\n\tfloat flDiffusion;\n\tfloat flGain;\n\tfloat flGainHF;\n\tfloat flGainLF;\n\tfloat flDecayTime;\n\tfloat flDecayHFRatio;\n\tfloat flDecayLFRatio;\n\tfloat flReflectionsGain;\n\tfloat flReflectionsDelay;\n\tvector flReflectionsPan;\n\tfloat flLateReverbGain;\n\tfloat flLateReverbDelay;\n\tvector flLateReverbPan;\n\tfloat flEchoTime;\n\tfloat flEchoDepth;\n\tfloat flModulationTime;\n\tfloat flModulationDepth;\n\tfloat flAirAbsorptionGainHF;\n\tfloat flHFReference;\n\tfloat flLFReference;\n\tfloat flRoomRolloffFactor;\n\tint   iDecayHFLimit;\n} reverbinfo_t;\nvoid(float reverbslot, reverbinfo_t *reverbinfo, int sizeofreverinfo_t) setup_reverb = #0:setup_reverb; /* Part of FTE_CSQC_REVERB\n\t\tReconfigures a reverb slot for weird effects. Slot 0 is reserved for no effects. Slot 1 is reserved for underwater effects. Reserved slots will be reinitialised on snd_restart, but can otherwise be changed. These reverb slots can be activated with SetListener. Note that reverb will currently only work when using OpenAL. */\n\n#endif\nvoid(string cmdname, optional string desc) registercommand = #352; /*\n\t\tRegister the given console command, for easy console use.\n\t\tConsole commands that are later used will invoke CSQC_ConsoleCommand/m_consolecommand/ConsoleCmd according to module. */\n\nfloat(entity ent) wasfreed = #353; /*\n\t\tQuickly check to see if the entity is currently free. This function is only valid during the two-second non-reuse window, after that it may give bad results. Try one second to make it more robust. */\n\n#if defined(CSQC) || defined(SSQC)\nstring(string key) serverkey = #354; /*\n\t\tLook up a key in the server's public serverinfo string. If the key contains binary data then it will be truncated at the first null. */\n\nfloat(string key, optional float assumevalue) serverkeyfloat = #0:serverkeyfloat; /*\n\t\tVersion of serverkey that returns the value as a float (which avoids tempstrings). */\n\nint(string key, optional void *ptr, int maxsize) serverkeyblob = #0:serverkeyblob; /*\n\t\tVersion of serverkey that returns data as a blob (ie: binary data that may contain nulls). Returns the full blob size, even if truncated (pass maxsize=0 to query required storage). */\n\n#endif\n#ifdef SSQC\nvoid(string key, void *ptr, optional int size) setserverkey = #0:setserverkey; /*\n\t\tChanges the server's serverinfo. */\n\n#endif\n#ifdef CSQC\nstring(optional string resetstring) getentitytoken = #355; /*\n\t\tGrab the next token in the map's entity lump.\n\t\tIf resetstring is not specified, the next token will be returned with no other sideeffects.\n\t\tIf empty, will reset from the map before returning the first token, probably {.\n\t\tIf not empty, will tokenize from that string instead.\n\t\tAlways returns tempstrings. */\n\n#endif\n#if defined(CSQC) || defined(MENU)\nfloat(string s) findfont = #356; /* Part of DP_GFX_FONTS\n\t\tLooks up a named font slot. Matches the actual font name as a last resort. */\n\nfloat(string fontname, string fontmaps, string sizes, float slot, optional float fix_scale, optional float fix_voffset) loadfont = #357; /* Part of DP_GFX_FONTS\n\t\ttoo convoluted for me to even try to explain correct usage. Try drawfont = loadfont(\"\", \"cour\", \"16\", -1, 0, 0); to switch to the courier font (optimised for 16 virtual pixels high) ('cour' requires mscorefonts installed in linux). Additionally you can add \"outline=1\" as an extra token in the sizes string, to have more readable outlined fonts. */\n\n#endif\n#ifdef CSQC\nvoid(string evname, string evargs, ...) sendevent = #359; /*\n\t\tInvoke CSEv_evname_evargs in ssqc. evargs must be a string of initials refering to the types of the arguments to pass. v=vector, e=entity(.entnum field is sent), f=float, i=int. 6 arguments max - you can get more if you pack your floats into vectors. */\n\nfloat() readbyte = #360; /*\n\t\tReads an unsigned 8-bit value, pair with WriteByte. */\n\nfloat() readchar = #361; /*\n\t\tReads a signed 8-bit value. Paired with WriteChar. */\n\nfloat() readshort = #362; /*\n\t\tReads a signed 16-bit value. Paired with WriteShort. */\n\nfloat() readlong = #363; /*\n\t\tReads a signed 32-bit value. Paired with WriteLong or WriteInt. */\n\nfloat() readcoord = #364; /*\n\t\tReads a value matching the unspecified precision written ONLY by WriteCoord. */\n\nfloat() readangle = #365; /*\n\t\tReads a value matching the unspecified precision written ONLY by WriteAngle. */\n\nstring() readstring = #366; /*\n\t\tReads a null-terminated string. */\n\nfloat() readfloat = #367; /*\n\t\tReads a float without any truncation nor conversions. Data MUST have originally been written with WriteFloat. */\n\n__double() readdouble = #0:readdouble; /*\n\t\tReads a double-precision float without any truncation nor conversions. Data MUST have originally been written with WriteDouble. */\n\nint() readint = #0:readint; /*\n\t\tReads a 32bit int without any conversions to float, otherwise interchangable with readlong. */\n\n__int64() readint64 = #0:readint64; /*\n\t\tReads a 64bit int. Paired with WriteInt64. */\n\nfloat() readentitynum = #368; /*\n\t\tReads the serverside index of an entity, paired with WriteEntity. There may be nothing else known about the entity yet, so the result typically needs to be saved as-is and re-looked up each frame. This can be done via getentity(NUM, GE_*) for non-csqc ents, or findentity(world,entnum,NUM) - both of which can fail due to latency. */\n\nfloat(string modelname, float(float isnew) updatecallback, float flags) deltalisten = #371; /*\n\t\tSpecifies a per-modelindex callback to listen for engine-networking entity updates. Such entities are automatically interpolated by the engine (unless flags specifies not to).\n\t\tThe various standard entity fields will be overwritten each frame before the updatecallback function is called. */\n\nfloat(vector org, float radius, vector rgb) dynamiclight_spawnstatic = #0:dynamiclight_spawnstatic; /*\n\t\tCreates a static persistent light at the given position with the specified colour. Additional properties must be set via dynamiclight_set. */\n\n__variant(float lno, float fld) dynamiclight_get = #372; /*\n\t\tRetrieves a property from the given dynamic/rt light. Return type depends upon the light field requested. */\n\nvoid(float lno, float fld, __variant value) dynamiclight_set = #373; /*\n\t\tChanges a property on the given dynamic/rt light. Value type depends upon the light field to be changed. */\n\nstring(float efnum, float body) particleeffectquery = #374; /*\n\t\tRetrieves either the name or the body of the effect with the given number. The effect body is regenerated from internal state, and can be changed before being reapplied via the localcmd builtin. */\n\nvoid(string shadername, vector origin, vector up, vector side, vector rgb, float alpha) adddecal = #375; /*\n\t\tAdds a temporary clipped decal shader to the scene, centered at the given point with given orientation. Will be drawn by the next renderscene call, and freed by the next clearscene call. */\n\n#endif\n#if defined(CSQC) || defined(MENU)\nvoid(entity e, string skinfilename, optional string skindata) setcustomskin = #376; /* Part of FTE_QC_CUSTOMSKINS\n\t\tSets an entity's skin overrides to a new skin object. Releases the entities old skin (refcounted). */\n\n#endif\n#ifdef CSQC\nfloat(string skinfilename, optional string skindata) loadcustomskin = #377; /*\n\t\tCreates a new skin object and returns it. These are custom per-entity surface->shader lookups. The skinfilename/data should be in .skin format:\n\t\tsurfacename,shadername - makes the named surface use the named shader (legacy format for compat with q3)\n\t\treplace \"surfacename\" \"shadername\" - non-legacy equivalent.\n\t\tqwskin \"foo\" - use an unmodified quakeworld player skin (including crop+repalette rules)\n\t\tq1lower 0xff0000 - specify an override for the entity's lower colour, in this case to red\n\t\tq1upper 0x0000ff - specify an override for the entity's lower colour, in this case to blue\n\t\tcompose \"surfacename\" \"shader\" \"imagename@x,y:w,h$s,t,s2,t2?r,g,b,a\" - compose a skin texture from multiple images.\n\t\t  The texture is determined to be sufficient to hold the first named image, additional images can be named as extra tokens on the same line.\n\t\t  Use a + at the end of the line to continue reading image tokens from the next line also, the named shader must use 'map $diffuse' to read the composed texture (compatible with the defaultskin shader). Must be matched with a releasecustomskin call later, and is pointless without applycustomskin. */\n\nvoid(entity e, float skinobj) applycustomskin = #378; /*\n\t\tUpdates the entity's custom skin (refcounted). */\n\nvoid(float skinobj) releasecustomskin = #379; /*\n\t\tLets the engine know that the skin will no longer be needed. Thanks to refcounting any ents with the skin already applied will retain their skin until later changed. It is valid to destroy a skin just after applying it to an ent in the same function that it was created in, as the skin will only be destroyed once its refcount rops to 0. */\n\n#endif\n__variant*(int size) memalloc = #384; /* Part of FTE_MEMALLOC\n\t\tAllocate an arbitary block of memory */\n\nvoid(__variant *ptr) memfree = #385; /* Part of FTE_MEMALLOC\n\t\tFrees a block of memory that was allocated with memfree */\n\nvoid(__variant *dst, __variant *src, int size) memcpy = #386; /* Part of FTE_MEMALLOC\n\t\tCopys memory from one location to another */\n\nvoid(__variant *dst, int val, int size) memfill8 = #387; /* Part of FTE_MEMALLOC\n\t\tSets an entire block of memory to a specified value. Pretty much always 0. */\n\n__variant(__variant *dst, float ofs) memgetval = #388; /*\n\t\tLooks up the 32bit value stored at a pointer-with-offset. */\n\nvoid(__variant *dst, float ofs, __variant val) memsetval = #389; /*\n\t\tChanges the 32bit value stored at the specified pointer-with-offset. */\n\n__variant*(__variant *base, float ofs) memptradd = #390; /*\n\t\tPerform some pointer maths. Woo. */\n\nfloat(string s) memstrsize = #0:memstrsize; /*\n\t\tstrlen, except ignores utf-8 */\n\n#if defined(CSQC) || defined(MENU)\nstring(string conname, string field, optional string newvalue) con_getset = #391; /* Part of FTE_CSQC_ALTCONSOLES\n\t\tReads or sets a property from a console object. The old value is returned. Iterrate through consoles with the 'next' field. Valid properties: \ttitle, name, next, unseen, markup, forceutf8, close, clear, hidden, linecount */\n\nvoid(string conname, string messagefmt, ...) con_printf = #392; /* Part of FTE_CSQC_ALTCONSOLES\n\t\tPrints onto a named console. */\n\nvoid(string conname, vector pos, vector size, float fontsize) con_draw = #393; /* Part of FTE_CSQC_ALTCONSOLES\n\t\tDraws the named console. */\n\nfloat(string conname, float inevtype, float parama, float paramb, float paramc) con_input = #394; /* Part of FTE_CSQC_ALTCONSOLES\n\t\tForwards input events to the named console. Mouse updates should be absolute only. */\n\nvoid(string newcaption) setwindowcaption = #0:setwindowcaption; /* Part of FTE_CSQC_WINDOWCAPTION\n\t\tReplaces the title of the game window, as seen when task switching or just running in windowed mode. */\n\nfloat() cvars_haveunsaved = #0:cvars_haveunsaved; /*\n\t\tReturns true if any archived cvar has an unsaved value. */\n\n#endif\nfloat(entity e, float nowreadonly) entityprotection = #0:entityprotection; /*\n\t\tChanges the protection on the specified entity to protect it from further edits from QC. The return value is the previous setting. Note that this can be used to unprotect the world, but doing so long term is not advised as you will no longer be able to detect invalid entity references. Also, world is not networked, so results might not be seen by clients (or in other words, world.avelocity_y=64 is a bad idea). */\n\n#ifdef CSQC\nstring(vector pos) getlocationname = #0:getlocationname; /*\n\t\tLooks up the specified position in the current map's .loc file and reports the nearest marked name. */\n\n#endif\n#ifdef MENU\nvoid(int cliptype) clipboard_get = #0:clipboard_get; /*\n\t\tAttempts to query the system clipboard. Any pasted text will be returned via Menu_InputEvent */\n\n#endif\n#if defined(CSQC) || defined(MENU)\nvoid(int cliptype, string text) clipboard_set = #0:clipboard_set; /*\n\t\tChanges the system clipboard to the specified text. */\n\n#endif\n#if defined(CSQC) || defined(SSQC)\nentity(entity from, optional entity to) copyentity = #400; /* Part of DP_QC_COPYENTITY\n\t\tCopies all fields from one entity to another. */\n\n#endif\n#ifdef SSQC\n__deprecated(\"No RGB support.\") void(entity ent, float colours) setcolors = #401; /*\n\t\tChanges a player's colours. The bits 0-3 are the lower/trouser colour, bits 4-7 are the upper/shirt colours. */\n\n#endif\n#if defined(CSQC) || defined(SSQC)\nentity(.string field, string match, optional .entity chainfield) findchain = #402; /* Part of DP_QC_FINDCHAIN*/\nentity(.float fld, float match, optional .entity chainfield) findchainfloat = #403; /* Part of DP_QC_FINDCHAINFLOAT*/\nvoid(vector org, string modelname, float startframe, float endframe, float framerate) effect = #404; /* Part of DP_SV_EFFECT\n\t\tSpawns a self-animating sprite */\n\nvoid(vector org, vector dir, float count) te_blood = #405; /* Part of DP_TE_BLOOD*/\nvoid(vector mincorner, vector maxcorner, float explosionspeed, float howmany) te_bloodshower = #406; /* Part of _DP_TE_BLOODSHOWER*/\nvoid(vector org, vector color) te_explosionrgb = #407; /* Part of DP_TE_EXPLOSIONRGB*/\nvoid(vector mincorner, vector maxcorner, vector vel, float howmany, float color, float gravityflag, float randomveljitter) te_particlecube = #408; /* Part of DP_TE_PARTICLECUBE*/\nvoid(vector mincorner, vector maxcorner, vector vel, float howmany, float color) te_particlerain = #409; /* Part of DP_TE_PARTICLERAIN*/\nvoid(vector mincorner, vector maxcorner, vector vel, float howmany, float color) te_particlesnow = #410; /* Part of DP_TE_PARTICLESNOW*/\nvoid(vector org, vector vel, float howmany) te_spark = #411; /* Part of DP_TE_SPARK*/\nvoid(vector org) te_gunshotquad = #412; /* Part of _DP_TE_QUADEFFECTS1*/\nvoid(vector org) te_spikequad = #413; /* Part of _DP_TE_QUADEFFECTS1*/\nvoid(vector org) te_superspikequad = #414; /* Part of _DP_TE_QUADEFFECTS1*/\nvoid(vector org) te_explosionquad = #415; /* Part of _DP_TE_QUADEFFECTS1*/\nvoid(vector org) te_smallflash = #416; /* Part of DP_TE_SMALLFLASH*/\nvoid(vector org, float radius, float lifetime, vector color) te_customflash = #417; /* Part of DP_TE_CUSTOMFLASH*/\nvoid(vector org, optional float count) te_gunshot = #418; /* Part of DP_TE_STANDARDEFFECTBUILTINS, FTE_TE_STANDARDEFFECTBUILTINS*/\nvoid(vector org) te_spike = #419; /* Part of DP_TE_STANDARDEFFECTBUILTINS, FTE_TE_STANDARDEFFECTBUILTINS*/\nvoid(vector org) te_superspike = #420; /* Part of DP_TE_STANDARDEFFECTBUILTINS, FTE_TE_STANDARDEFFECTBUILTINS*/\nvoid(vector org) te_explosion = #421; /* Part of DP_TE_STANDARDEFFECTBUILTINS, FTE_TE_STANDARDEFFECTBUILTINS*/\nvoid(vector org) te_tarexplosion = #422; /* Part of DP_TE_STANDARDEFFECTBUILTINS, FTE_TE_STANDARDEFFECTBUILTINS*/\nvoid(vector org) te_wizspike = #423; /* Part of DP_TE_STANDARDEFFECTBUILTINS, FTE_TE_STANDARDEFFECTBUILTINS*/\nvoid(vector org) te_knightspike = #424; /* Part of DP_TE_STANDARDEFFECTBUILTINS, FTE_TE_STANDARDEFFECTBUILTINS*/\nvoid(vector org) te_lavasplash = #425; /* Part of DP_TE_STANDARDEFFECTBUILTINS, FTE_TE_STANDARDEFFECTBUILTINS*/\nvoid(vector org) te_teleport = #426; /* Part of DP_TE_STANDARDEFFECTBUILTINS, FTE_TE_STANDARDEFFECTBUILTINS*/\nvoid(vector org, float color, float colorlength) te_explosion2 = #427; /* Part of DP_TE_STANDARDEFFECTBUILTINS*/\nvoid(entity own, vector start, vector end) te_lightning1 = #428; /* Part of DP_TE_STANDARDEFFECTBUILTINS, FTE_TE_STANDARDEFFECTBUILTINS*/\nvoid(entity own, vector start, vector end) te_lightning2 = #429; /* Part of DP_TE_STANDARDEFFECTBUILTINS, FTE_TE_STANDARDEFFECTBUILTINS*/\nvoid(entity own, vector start, vector end) te_lightning3 = #430; /* Part of DP_TE_STANDARDEFFECTBUILTINS, FTE_TE_STANDARDEFFECTBUILTINS*/\nvoid(entity own, vector start, vector end) te_beam = #431; /* Part of DP_TE_STANDARDEFFECTBUILTINS*/\nvoid(vector dir) vectorvectors = #432; /* Part of DP_QC_VECTORVECTORS*/\nvoid(vector org) te_plasmaburn = #433; /* Part of _DP_TE_PLASMABURN*/\nfloat(entity e, float s) getsurfacenumpoints = #434; /* Part of DP_QC_GETSURFACE*/\nvector(entity e, float s, float n) getsurfacepoint = #435; /* Part of DP_QC_GETSURFACE*/\nvector(entity e, float s) getsurfacenormal = #436; /* Part of DP_QC_GETSURFACE*/\nstring(entity e, float s) getsurfacetexture = #437; /* Part of DP_QC_GETSURFACE*/\nfloat(entity e, vector p) getsurfacenearpoint = #438; /* Part of DP_QC_GETSURFACE*/\nvector(entity e, float s, vector p) getsurfaceclippedpoint = #439; /* Part of DP_QC_GETSURFACE*/\n#endif\n#ifdef MENU\nstrbuf() buf_create = #440; /* Part of DP_QC_STRINGBUFFERS*/\nvoid(strbuf bufhandle) buf_del = #441; /* Part of DP_QC_STRINGBUFFERS*/\nfloat(strbuf bufhandle) buf_getsize = #442; /* Part of DP_QC_STRINGBUFFERS*/\nvoid(strbuf bufhandle_from, float bufhandle_to) buf_copy = #443; /* Part of DP_QC_STRINGBUFFERS*/\nvoid(strbuf bufhandle, float sortprefixlen, float backward) buf_sort = #444; /* Part of DP_QC_STRINGBUFFERS*/\nstring(strbuf bufhandle, string glue) buf_implode = #445; /* Part of DP_QC_STRINGBUFFERS*/\nstring(strbuf bufhandle, float string_index) bufstr_get = #446; /* Part of DP_QC_STRINGBUFFERS*/\nvoid(strbuf bufhandle, float string_index, string str) bufstr_set = #447; /* Part of DP_QC_STRINGBUFFERS*/\nfloat(strbuf bufhandle, string str, float ordered) bufstr_add = #448; /* Part of DP_QC_STRINGBUFFERS*/\nvoid(strbuf bufhandle, float string_index) bufstr_free = #449; /* Part of DP_QC_STRINGBUFFERS*/\nfloat(string name) iscachedpic = #451;\nstring(string name, optional float flags) precache_pic = #452;\nfloat(vector position, float character, vector scale, vector rgb, float alpha, optional float flag) drawcharacter = #454;\nfloat(vector position, string text, vector scale, vector rgb, float alpha, optional float flag) drawrawstring = #455;\nfloat(vector position, string pic, vector size, vector rgb, float alpha, optional float flag) drawpic = #456;\nfloat(vector position, vector size, vector rgb, float alpha, optional float flag) drawfill = #457;\nvoid(float x, float y, float width, float height) drawsetcliparea = #458;\nvoid(void) drawresetcliparea = #459;\nvector(string picname) drawgetimagesize = #460;\nvoid(float width, vector pos1, vector pos2) drawline = #466;\nfloat(vector position, string text, vector scale, vector rgb, float alpha, float flag) drawstring = #467;\nfloat(string text, float usecolours, optional vector fontsize) stringwidth = #468;\nvoid(vector pos, vector sz, string pic, vector srcpos, vector srcsz, vector rgb, float alpha, float flag) drawsubpic = #469;\n#endif\n#ifdef SSQC\nvoid(entity e, string s) clientcommand = #440; /* Part of KRIMZON_SV_PARSECLIENTCOMMAND*/\n#endif\n#if defined(CSQC) || defined(SSQC)\nfloat(string s) tokenize = #441; /* Part of KRIMZON_SV_PARSECLIENTCOMMAND*/\nstring(float n) argv = #442; /* Part of KRIMZON_SV_PARSECLIENTCOMMAND*/\nvoid(entity e, entity tagentity, string tagname) setattachment = #443; /* Part of DP_GFX_QUAKE3MODELTAGS*/\nsearchhandle(string pattern, enumflags:float{SB_CASEINSENSITIVE=1<<0,SB_FULLPACKAGEPATH=1<<1,SB_ALLOWDUPES=1<<2,SB_FORCESEARCH=1<<3,SB_MULTISEARCH=1<<4} flags, float quiet, optional string filterpackage) search_begin = #444; /* Part of DP_QC_FS_SEARCH, DP_QC_FS_SEARCH_PACKFILE\n\t\tinitiate a filesystem scan based upon filenames. Be sure to call search_end on the returned handle. SB_FULLPACKAGEPATH interprets the filterpackage arg as a full package path to avoid gamedir ambiguity, equivelent to whichpack's WP_FULLPACKAGEPATH flag. SB_ALLOWDUPES allows returning multiple entries with the same name (but different package, useful with search_fopen). SB_FORCESEARCH requires use of the filterpackage and SB_FULLPACKAGEPATH flag, initiating searches from gamedirs/packages which are not currently active. */\n\nvoid(searchhandle handle) search_end = #445; /* Part of DP_QC_FS_SEARCH, DP_QC_FS_SEARCH_PACKFILE*/\nfloat(searchhandle handle) search_getsize = #446; /* Part of DP_QC_FS_SEARCH, DP_QC_FS_SEARCH_PACKFILE\n\t\tRetrieves the number of files that were found. */\n\nstring(searchhandle handle, float num) search_getfilename = #447; /* Part of DP_QC_FS_SEARCH, DP_QC_FS_SEARCH_PACKFILE\n\t\tRetrieves name of one of the files that was found by the initial search. */\n\n#endif\nfloat(searchhandle handle, float num) search_getfilesize = #0:search_getfilesize; /* Part of FTE_QC_FS_SEARCH_SIZEMTIME\n\t\tRetrieves the size of one of the files that was found by the initial search. */\n\nstring(searchhandle handle, float num) search_getfilemtime = #0:search_getfilemtime; /* Part of FTE_QC_FS_SEARCH_SIZEMTIME\n\t\tRetrieves modification time of one of the files. */\n\nstring(searchhandle handle, float num) search_getpackagename = #0:search_getpackagename; /*\n\t\tRetrieves the name of the package containing the file. Search with SB_FULLPACKAGEPATH to see gamedir/package info */\n\nfilestream(searchhandle handle, float num) search_fopen = #0:search_fopen; /*\n\t\tOpens the file directly, without getting confused about entries from other packages. Read access only. */\n\n#if defined(CSQC) || defined(SSQC)\nstring(string cvarname) cvar_string = #448; /* Part of DP_QC_CVAR_STRING*/\nentity(entity start, .float fld, float match) findflags = #449; /* Part of DP_QC_FINDFLAGS*/\nentity(.float fld, float match, optional .entity chainfield) findchainflags = #450; /* Part of DP_QC_FINDCHAINFLAGS*/\nfloat(entity ent, string tagname) gettagindex = #451; /* Part of DP_QC_GETTAGINFO*/\nvector(entity ent, float tagindex) gettaginfo = #452; /* Part of DP_QC_GETTAGINFO\n\t\tObtains the current worldspace position+orientation of the bone or tag from the given entity. The return value is the world coord, v_forward, v_right, v_up are also set according to the bone/tag's orientation. */\n\n#endif\n#ifdef SSQC\nvoid(entity player) dropclient = #453; /* Part of DP_SV_DROPCLIENT*/\nentity() spawnclient = #454; /* Part of DP_SV_BOTCLIENT*/\nfloat(entity client) clienttype = #455; /* Part of DP_SV_BOTCLIENT*/\nvoid(float target, string str) WriteUnterminatedString = #456; /* Part of DP_SV_WRITEUNTERMINATEDSTRING*/\n#endif\n#if defined(CSQC) || defined(SSQC)\nvoid(vector org, vector vel, float howmany) te_flamejet = #457; /* Part of _DP_TE_FLAMEJET*/\nentity(float entnum) edict_num = #459; /* Part of DP_QC_EDICT_NUM*/\nstrbuf() buf_create = #460; /* Part of DP_QC_STRINGBUFFERS*/\nvoid(strbuf bufhandle) buf_del = #461; /* Part of DP_QC_STRINGBUFFERS*/\nfloat(strbuf bufhandle) buf_getsize = #462; /* Part of DP_QC_STRINGBUFFERS*/\nvoid(strbuf bufhandle_from, strbuf bufhandle_to) buf_copy = #463; /* Part of DP_QC_STRINGBUFFERS*/\nvoid(strbuf bufhandle, float sortprefixlen, float backward) buf_sort = #464; /* Part of DP_QC_STRINGBUFFERS*/\nstring(strbuf bufhandle, string glue) buf_implode = #465; /* Part of DP_QC_STRINGBUFFERS*/\nstring(strbuf bufhandle, float string_index) bufstr_get = #466; /* Part of DP_QC_STRINGBUFFERS*/\nvoid(strbuf bufhandle, float string_index, string str) bufstr_set = #467; /* Part of DP_QC_STRINGBUFFERS*/\nfloat(strbuf bufhandle, string str, float ordered) bufstr_add = #468; /* Part of DP_QC_STRINGBUFFERS*/\nvoid(strbuf bufhandle, float string_index) bufstr_free = #469; /* Part of DP_QC_STRINGBUFFERS*/\n#endif\nfloat(float s) asin = #471; /* Part of DP_QC_ASINACOSATANATAN2TAN*/\nfloat(float c) acos = #472; /* Part of DP_QC_ASINACOSATANATAN2TAN*/\nfloat(float t) atan = #473; /* Part of DP_QC_ASINACOSATANATAN2TAN*/\nfloat(float c, float s) atan2 = #474; /* Part of DP_QC_ASINACOSATANATAN2TAN*/\nfloat(float a) tan = #475; /* Part of DP_QC_ASINACOSATANATAN2TAN\n\t\tForgive me father, for I have a sunbed and I'm not afraid to use it. */\n\nfloat(string s) strlennocol = #476; /* Part of DP_QC_STRINGCOLORFUNCTIONS\n\t\tReturns the number of characters in the string after any colour codes or other markup has been parsed. */\n\nstring(string s) strdecolorize = #477; /* Part of DP_QC_STRINGCOLORFUNCTIONS\n\t\tFlattens any markup/colours, removing them from the string. */\n\nstring(float uselocaltime, string format, ...) strftime = #478; /* Part of DP_QC_STRFTIME*/\nfloat(string s, string separator1, ...) tokenizebyseparator = #479; /* Part of DP_QC_TOKENIZEBYSEPARATOR\n\t\tSplits up the string using only the specified delimiters/separators. Multiple delimiters can be given, they are each considered equivelent (though should start with the longest if you want to do weird subseparator stuff).\n\t\tThe resulting tokens can be queried via argv (and argv_start|end_index builtins, if you want to determine which of the separators was present between two tokens).\n\t\tNote that while an input string containing JUST a separator will return 2, a string with no delimiter will return 1, while (in FTE) an empty string will ALWAYS return 0. */\n\nstring(string s) strtolower = #480; /* Part of DP_QC_STRING_CASE_FUNCTIONS*/\nstring(string s) strtoupper = #481; /* Part of DP_QC_STRING_CASE_FUNCTIONS*/\n#if defined(CSQC) || defined(SSQC)\nstring(string s) cvar_defstring = #482; /* Part of DP_QC_CVAR_DEFSTRING*/\nvoid(vector origin, string sample, float volume, float attenuation) pointsound = #483; /* Part of DP_SV_POINTSOUND*/\n#endif\nstring(string search, string replace, string subject) strreplace = #484; /* Part of DP_QC_STRREPLACE*/\nstring(string search, string replace, string subject) strireplace = #485; /* Part of DP_QC_STRREPLACE*/\n#if defined(CSQC) || defined(SSQC)\nvector(entity e, float s, float n, float a) getsurfacepointattribute = #486; /* Part of DP_QC_GETSURFACEPOINTATTRIBUTE*/\n#endif\n#if defined(CSQC) || defined(MENU)\nfloat(string name, optional string initialURI) gecko_create = #487; /* Part of DP_GECKO_SUPPORT\n\t\tCreate a new 'browser tab' shader with the specified name that can then be drawn via drawpic (shader should not already exist - including from map/model textures or disk). In order to function correctly, this builtin depends upon external plugins being available. Use gecko_navigate to navigate it to a page of your choosing. */\n\nvoid(string name) gecko_destroy = #488; /* Part of DP_GECKO_SUPPORT\n\t\tDestroy a shader. */\n\nvoid(string name, string URI) gecko_navigate = #489; /* Part of DP_GECKO_SUPPORT\n\t\tSends a command to the media decoder attached to the specified shader. In the case of a browser decoder, this changes the url that the browser displays. 'cmd:[un]focus' will tell the decoder that it has focus. */\n\nfloat(string name, float key, float eventtype, optional float charcode) gecko_keyevent = #490; /* Part of DP_GECKO_SUPPORT\n\t\tSend a key event to a media decoder. This applies only to interactive decoders like browsers. */\n\nvoid(string name, float x, float y) gecko_mousemove = #491; /* Part of DP_GECKO_SUPPORT\n\t\tSets a media decoder shader's mouse position. Values should be 0-1. */\n\nvoid(string name, float w, float h) gecko_resize = #492; /* Part of DP_GECKO_SUPPORT\n\t\tRequest to resize a media decoder. */\n\nvector(string name) gecko_get_texture_extent = #493; /* Part of DP_GECKO_SUPPORT\n\t\tRetrieves a media decoder current image pixel sizes. */\n\nstring(string shadname, string propname) gecko_getproperty = #0:gecko_getproperty; /*\n\t\tQueries the media decoder (especially browser ones) for decoder-specific properties. The cef plugin recognises url, title, status. */\n\n#endif\n#ifdef CSQC\nfloat(string file, string id) cin_open = #0:cin_open;\nvoid(string id) cin_close = #0:cin_close;\nvoid(string id, float newstate) cin_setstate = #0:cin_setstate;\nfloat(string id) cin_getstate = #0:cin_getstate;\nvoid(string file) cin_restart = #0:cin_restart;\n#endif\n__deprecated(\"Use digest_hex\") float(float caseinsensitive, string s, ...) crc16 = #494; /* Part of DP_QC_CRC16*/\nfloat(string name) cvar_type = #495; /* Part of DP_QC_CVAR_TYPE*/\nfloat() numentityfields = #496; /* Part of DP_QC_ENTITYDATA\n\t\tGives the number of named entity fields. Note that this is not the size of an entity, but rather just the number of unique names (ie: vectors use 4 names rather than 3). */\n\nfloat(string fieldname) findentityfield = #0:findentityfield; /*\n\t\tFind a field index by name. */\n\ntypedef .__variant field_t;\nfield_t(float fieldnum) entityfieldref = #0:entityfieldref; /*\n\t\tReturns a field value that can be directly used to read entity fields. Be sure to validate the type with entityfieldtype before using. */\n\nstring(float fieldnum) entityfieldname = #497; /* Part of DP_QC_ENTITYDATA\n\t\tRetrieves the name of the given entity field. */\n\nfloat(float fieldnum) entityfieldtype = #498; /* Part of DP_QC_ENTITYDATA\n\t\tProvides information about the type of the field specified by the field num. Returns one of the EV_ values. */\n\nstring(float fieldnum, entity ent) getentityfieldstring = #499; /* Part of DP_QC_ENTITYDATA*/\nfloat(float fieldnum, entity ent, string s) putentityfieldstring = #500; /* Part of DP_QC_ENTITYDATA*/\n#ifdef SSQC\nvoid(float to, string s, float sz) WritePicture = #501; /* Part of DP_SV_WRITEPICTURE\n\t\tEncodes the named image across the network as-is adhering to some size limit. In FTE, this simply writes the string and is equivelent to writestring and sz is ignored. WritePicture should be paired with ReadPicture in csqc. */\n\n#endif\n#ifdef CSQC\nstring() ReadPicture = #501; /*\n\t\tReads a picture that was written by ReadPicture, and returns a name that can be used in drawpic and other 2d drawing functions. In FTE, this acts as a readstring-with-downloadcheck - the image will appear normally once it has been downloaded, but its size may be incorrect until then. */\n\nvoid(float effectindex, entity own, vector org_from, vector org_to, vector dir_from, vector dir_to, float countmultiplier, optional float flags) boxparticles = #502;\n#endif\nstring(string filename, optional enumflags:float{WP_REFERENCEPACKAGE,WP_FULLPACKAGEPATH} flags) whichpack = #503; /* Part of DP_QC_WHICHPACK\n\t\tReturns the pak file name that contains the file specified. progs/player.mdl will generally return something like 'pak0.pak'. If WP_REFERENCE, clients will automatically be told that the returned package should be pre-downloaded and used, even if allow_download_refpackages is not set. */\n\n#ifdef CSQC\n__variant(float entnum, float fieldnum) getentity = #504; /*\n\t\tLooks up fields from non-csqc-visible entities. The entity will need to be within the player's pvs. fieldnum should be one of the GE_ constants. */\n\n#endif\nstring(string in) uri_escape = #510; /* Part of DP_QC_URI_ESCAPE\n\t\tUses percent-encoding to encode any bytes in the input string which are not ascii alphanumeric, period, hyphen, or underscore. All other bytes will expand to eg '%20' for a single space char. This encoding scheme is compatible with http and other uris. */\n\nstring(string in) uri_unescape = #511; /* Part of DP_QC_URI_ESCAPE\n\t\tUndo any percent-encoding in the input string, hopefully resulting in the same original sequence of bytes (and thus chars too). */\n\nfloat(entity ent) num_for_edict = #512;\n#define uri_post uri_get\nfloat(string uril, float id, optional string postmimetype, optional string postdata) uri_get = #513; /* Part of DP_QC_URI_GET, DP_QC_URI_POST\n\t\turi_get() gets content from an URL and calls a callback \"uri_get_callback\" with it set as string; an unique ID of the transfer is returned\n\t\treturns 1 on success, and then calls the callback with the ID, 0 or the HTTP status code, and the received data in a string\n\t\tFor a POST request, you will typically want the postmimetype set to application/x-www-form-urlencoded.\n\t\tFor a GET request, omit the mime+data entirely.\n\t\tConsult your webserver/php/etc documentation for best-practise. */\n\nfloat(string str) tokenize_console = #514; /*\n\t\tTokenize a string exactly as the console's tokenizer would do so. The regular tokenize builtin became bastardized for convienient string parsing, which resulted in a large disparity that can be exploited to bypass checks implemented in a naive SV_ParseClientCommand function, therefore you can use this builtin to make sure it exactly matches. */\n\nfloat(float idx) argv_start_index = #515; /*\n\t\tReturns the character index that the tokenized arg started at. */\n\nfloat(float idx) argv_end_index = #516; /*\n\t\tReturns the character index that the tokenized arg stopped at. */\n\nvoid(strbuf strbuf, string pattern, string antipattern) buf_cvarlist = #517;\nstring(string cvarname) cvar_description = #518; /*\n\t\tRetrieves the description of a cvar, which might be useful for tooltips or help files. This may still not be useful. */\n\n#if defined(CSQC) || defined(SSQC)\nfloat(optional float timetype) gettime = #519;\n#endif\n#ifdef CSQC\nDEP string(float keynum) keynumtostring_omgwtf = #520;\n__deprecated(\"Does not support modifiers\") string(string command, optional float bindmap) findkeysforcommand = #521; /*\n\t\tReturns a list of keycodes that perform the given console command in a format that can only be parsed via tokenize (NOT tokenize_console). This only and always returns two values - if only one key is actually bound, -1 will be returned. The bindmap argument is listed for compatibility with dp-specific defs, but is ignored in FTE. */\n\nstring(string command, optional float bindmap) findkeysforcommandex = #0:findkeysforcommandex; /*\n\t\tReturns a list of key bindings in keyname format instead of keynums. Use tokenize to parse. This list may contain modifiers. May return large numbers of keys. */\n\n#endif\n#if defined(CSQC) || defined(SSQC)\nvoid(string s) loadfromdata = #529; /*\n\t\tReads a set of entities from the given string. This string should have the same format as a .ent file or a saved game. Entities will be spawned as required. If you need to see the entities that were created, you should use parseentitydata instead. */\n\nvoid(string s) loadfromfile = #530; /*\n\t\tReads a set of entities from the named file. This file should have the same format as a .ent file or a saved game. Entities will be spawned as required. If you need to see the entities that were created, you should use parseentitydata instead. */\n\nvoid(float pause) setpause = #531; /*\n\t\tSets whether the server should or should not be paused. This does not affect auto-paused things like when the console is down. */\n\n#endif\n#ifdef SSQC\nfloat(string mname) precache_vwep_model = #532; /* Part of ZQ_VWEP*/\n#endif\nfloat(float v, optional float base) log = #532; /* Part of ??MVDSV_BUILTINS\n\t\tDetermines the logarithm of the input value according to the specified base. This can be used to calculate how much something was shifted by. */\n\n#ifdef CSQC\nfloat(entity e, float channel, string newsample, float volume, float attenuation, float pitchpct, float flags, float timeoffset) soundupdate = #0:soundupdate; /*\n\t\tChanges the properties of the current sound being played on the given entity channel. newsample may be empty, and will be ignored in this case. timeoffset is relative to the current position (subtract the result of getsoundtime for absolute positions). Negative volume can be used to stop the sound. Return value is a fractional value based upon the number of audio devices that could be updated - test against TRUE rather than non-zero. */\n\nfloat(entity e, float channel) getsoundtime = #533; /*\n\t\tReturns the current playback time of the sample on the given entity's channel. Beware CHAN_AUTO (in csqc, channels are not limited by network protocol). */\n\nfloat(entity e, float channel) getchannellevel = #0:getchannellevel; /* */\n\n#endif\n#if defined(CSQC) || defined(MENU)\nfloat(string sample) soundlength = #534; /*\n\t\tProvides a way to query the duration of a sound sample, allowing you to set up a timer to chain samples. */\n\n#endif\nfloat(string filename, strbuf bufhandle) buf_loadfile = #535; /*\n\t\tAppends the named file into a string buffer (which must have been created in advance). The return value merely says whether the file was readable. */\n\nfloat(filestream filehandle, strbuf bufhandle, optional float startpos, optional float numstrings) buf_writefile = #536; /*\n\t\tWrites the contents of a string buffer onto the end of the supplied filehandle (you must have already used fopen). Additional optional arguments permit you to constrain the writes to a subsection of the stringbuffer. */\n\nfloat(float bufhandle, string match, float matchrule, float startpos, float step) bufstr_find = #537; /*\n\t\tLooks for the first occurence of the specified string in the buffer, returning its index or -1 on failure. */\n\n#ifdef SSQC\nfloat(optional float forcestate) physics_supported = #0:physics_supported; /*\n\t\tQueries whether rigid body physics is enabled or not. CSQC and SSQC may report different values. If the force argument is specified then the engine will try to activate or release physics (returning the new state, which may fail if plugins or dlls are missing). Note that restarting the physics engine is likely to result in hitches when collision trees get generated. The state may change if a plugin is disabled mid-map. */\n\n#endif\n#if defined(CSQC) || defined(SSQC)\nvoid(entity e, float physics_enabled) physics_enable = #540; /*\n\t\tEnable or disable the physics attached to a MOVETYPE_PHYSICS entity. Entities which have been disabled in this way will stop taking so much cpu time. */\n\nvoid(entity e, vector force, vector relative_ofs) physics_addforce = #541; /*\n\t\tApply some impulse directional force upon a MOVETYPE_PHYSICS entity. */\n\nvoid(entity e, vector torque) physics_addtorque = #542; /*\n\t\tApply some impulse rotational force upon a MOVETYPE_PHYSICS entity. */\n\n#endif\n#ifdef MENU\nvoid(float dest) setkeydest = #601;\nfloat() getkeydest = #602;\n#endif\n#if defined(CSQC) || defined(MENU)\nvoid(float trg) setmousetarget = #603;\nfloat() getmousetarget = #604;\n#endif\nvoid(.../*, string funcname*/) callfunction = #605; /*\n\t\tInvokes the named function. The function name is always passed as the last parameter and must always be present. The others are passed to the named function as-is */\n\nvoid(filestream fh, entity e) writetofile = #606; /*\n\t\tWrites an entity's fields to the named frik_file file handle. */\n\nfloat(string s) isfunction = #607; /*\n\t\tReturns true if the named function exists and can be called with the callfunction builtin. */\n\n#if defined(CSQC) || defined(MENU)\nvector(float vidmode, optional float forfullscreen) getresolution = #608; /*\n\t\tSupposed to query the driver for supported video modes. FTE does not query drivers in this way, nor would it trust drivers anyway. */\n\n#endif\n#ifdef CSQC\nDEP string(float keynum) keynumtostring_menu = #609;\n#endif\n#ifdef MENU\nstring(float keynum) keynumtostring = #609; /*\n\t\tConverts a qscancode key number into a mostly-human-readable name, matching the bind command. */\n\nstring(string command, optional float bindmap) findkeysforcommand = #610;\n#endif\n#if defined(CSQC) || defined(MENU)\nfloat(float type) gethostcachevalue = #611; /* Part of FTE_CSQC_SERVERBROWSER*/\nstring(float type, float hostnr) gethostcachestring = #612; /* Part of FTE_CSQC_SERVERBROWSER*/\n#endif\nfloat(entity e, string s, optional float offset) parseentitydata = #613; /*\n\t\tReads a single entity's fields into an already-spawned entity. s should contain field pairs like in a saved game: {\"foo1\" \"bar\" \"foo2\" \"5\"}. Returns <=0 on failure, otherwise returns the offset in the string that was read to. */\n\nstring(entity e) generateentitydata = #0:generateentitydata; /*\n\t\tDumps the entities fields into a string which can later be parsed with parseentitydata. */\n\n#ifdef MENU\nfloat(string key) stringtokeynum = #614; /*\n\t\tReturns the qscancode of a key from its name. Names are identical to the bind command. ctrl/shift/alt modifiers are ignored. */\n\n#endif\n#ifdef CSQC\nfloat(string key) stringtokeynum_menu = #614;\n#endif\n#if defined(CSQC) || defined(MENU)\nvoid() resethostcachemasks = #615; /* Part of FTE_CSQC_SERVERBROWSER*/\nvoid(float mask, float fld, string str, float op) sethostcachemaskstring = #616; /* Part of FTE_CSQC_SERVERBROWSER*/\nvoid(float mask, float fld, float num, float op) sethostcachemasknumber = #617; /* Part of FTE_CSQC_SERVERBROWSER*/\nvoid() resorthostcache = #618; /* Part of FTE_CSQC_SERVERBROWSER*/\nvoid(float fld, float descending) sethostcachesort = #619; /* Part of FTE_CSQC_SERVERBROWSER*/\nvoid(optional float dopurge) refreshhostcache = #620; /* Part of FTE_CSQC_SERVERBROWSER*/\nfloat(float fld, float hostnr) gethostcachenumber = #621; /* Part of FTE_CSQC_SERVERBROWSER*/\nfloat(string key) gethostcacheindexforkey = #622; /* Part of FTE_CSQC_SERVERBROWSER*/\nvoid(string key) addwantedhostcachekey = #623; /* Part of FTE_CSQC_SERVERBROWSER*/\nstring() getextresponse = #624; /* Part of FTE_CSQC_SERVERBROWSER*/\n#endif\nstring(string dnsname, optional float defport) netaddress_resolve = #625;\n#if defined(CSQC) || defined(MENU)\nstring(float n, float prop) getgamedirinfo = #626;\nstring(int n, int prop) getpackagemanagerinfo = #0:getpackagemanagerinfo; /*\n\t\tQueries information about a package from the engine's package manager subsystem. Actions can be taken via the pkg console command. */\n\n#endif\nstring(string fmt, ...) sprintf = #627; /* Part of DP_QC_SPRINTF\n\t\t'prints' to a formatted temp-string. Mostly acts as in C, however %d assumes floats (fteqcc has arg checking. Use it.).\n\t\ttype conversions: l=arg is an int, h=arg is a float, and will work as a prefix for any float or int representation.\n\t\tfloat representations: d=decimal, e,E=exponent-notation, f,F=floating-point notation, g,G=terse float, c=char code, x,X=hex\n\t\tother representations: i=int, s=string, S=quoted and marked-up string, v=vector, p=pointer\n\t\tso %ld will accept an int arg, while %hi will expect a float arg.\n\t\tentities, fields, and functions will generally need to be printed as ints with %i. */\n\n#if defined(CSQC) || defined(SSQC)\nfloat(entity e, float s) getsurfacenumtriangles = #628;\nvector(entity e, float s, float n) getsurfacetriangle = #629;\n#endif\n#if defined(CSQC) || defined(MENU)\nfloat(float key, string bind, optional float bindmap, optional float modifier) setkeybind = #630;\nvector() getbindmaps = #631;\nfloat(vector bm) setbindmaps = #632;\n#endif\nstring(string digest, string data, ...) digest_hex = #639;\nstring(string digest, void *data, int length) digest_ptr = #0:digest_ptr; /*\n\t\tCalculates the digest of a single contiguous block of memory (including nulls) using the specified hash function. */\n\nfloat(string src, string dst) fcopy = #650; /*\n\t\tEquivelent to fopen+fread+fwrite+fclose from QC (ie: reads from $gamedir/data/ or $gamedir, but always writes to $gamedir/data/ ) */\n\nfloat(string src, string dst) frename = #651; /*\n\t\tRenames the file, returning 0 on success. Both paths are relative to the data/ subdir. */\n\nfloat(string fname) fremove = #652; /*\n\t\tDeletes the named file - path is relative to data/ subdir, like fopen's FILE_WRITE. Returns 0 on success. */\n\nfloat(string fname) fexists = #653; /*\n\t\tReturns true if it exists inside the default writable path. Use whichpack for greater portability. */\n\nfloat(string path) rmtree = #654; /*\n\t\tDangerous, but sandboxed to data/ */\n\n#if defined(CSQC) || defined(MENU)\nconst float K_TAB = 9;\nconst float K_ENTER = 13;\nconst float K_ESCAPE = 27;\nconst float K_SPACE = 32;\nconst float K_BACKSPACE = 127;\nconst float K_UPARROW = 128;\nconst float K_DOWNARROW = 129;\nconst float K_LEFTARROW = 130;\nconst float K_RIGHTARROW = 131;\nconst float K_LALT = 132;\nconst float K_RALT = -245;\nconst float K_LCTRL = 133;\nconst float K_RCTRL = -246;\nconst float K_LSHIFT = 134;\nconst float K_RSHIFT = -247;\nconst float K_F1 = 135;\nconst float K_F2 = 136;\nconst float K_F3 = 137;\nconst float K_F4 = 138;\nconst float K_F5 = 139;\nconst float K_F6 = 140;\nconst float K_F7 = 141;\nconst float K_F8 = 142;\nconst float K_F9 = 143;\nconst float K_F10 = 144;\nconst float K_F11 = 145;\nconst float K_F12 = 146;\nconst float K_INS = 147;\nconst float K_DEL = 148;\nconst float K_PGDN = 149;\nconst float K_PGUP = 150;\nconst float K_HOME = 151;\nconst float K_END = 152;\nconst float K_KP_HOME = 164;\nconst float K_KP_UPARROW = 165;\nconst float K_KP_PGUP = 166;\nconst float K_KP_LEFTARROW = 161;\nconst float K_KP_5 = 162;\nconst float K_KP_RIGHTARROW = 163;\nconst float K_KP_END = 158;\nconst float K_KP_DOWNARROW = 159;\nconst float K_KP_PGDN = 160;\nconst float K_KP_ENTER = 172;\nconst float K_KP_INS = 157;\nconst float K_KP_DEL = 167;\nconst float K_KP_SLASH = 168;\nconst float K_KP_MINUS = 170;\nconst float K_KP_PLUS = 171;\nconst float K_KP_NUMLOCK = 154;\nconst float K_KP_STAR = 169;\nconst float K_KP_EQUALS = 173;\nconst float K_MOUSE1 = 512;\nconst float K_MOUSE2 = 513;\nconst float K_MOUSE3 = 514;\nconst float K_MOUSE4 = 517;\nconst float K_MOUSE5 = 518;\nconst float K_MOUSE6 = 519;\nconst float K_MOUSE7 = 520;\nconst float K_MOUSE8 = 521;\nconst float K_MOUSE9 = 522;\nconst float K_MOUSE10 = 523;\nconst float K_MWHEELUP = 515;\nconst float K_MWHEELDOWN = 516;\nconst float K_TOUCH = 600;\nconst float K_TOUCHSLIDE = 601;\nconst float K_TOUCHTAP = 602;\nconst float K_TOUCHLONG = 603;\nconst float K_LWIN = -239;\nconst float K_RWIN = -240;\nconst float K_APP = -241;\nconst float K_SEARCH = -242;\nconst float K_POWER = -130;\nconst float K_VOLUP = -243;\nconst float K_VOLDOWN = -244;\nconst float K_JOY1 = 768;\nconst float K_JOY2 = 769;\nconst float K_JOY3 = 770;\nconst float K_JOY4 = 771;\nconst float K_AUX1 = 784;\nconst float K_AUX2 = 785;\nconst float K_AUX3 = 786;\nconst float K_AUX4 = 787;\nconst float K_AUX5 = 788;\nconst float K_AUX6 = 789;\nconst float K_AUX7 = 790;\nconst float K_AUX8 = 791;\nconst float K_AUX9 = 792;\nconst float K_AUX10 = 793;\nconst float K_AUX11 = 794;\nconst float K_AUX12 = 795;\nconst float K_AUX13 = 796;\nconst float K_AUX14 = 797;\nconst float K_AUX15 = 798;\nconst float K_AUX16 = 799;\nconst float K_AUX17 = 800;\nconst float K_AUX18 = 801;\nconst float K_AUX19 = 802;\nconst float K_AUX20 = 803;\nconst float K_AUX21 = 804;\nconst float K_AUX22 = 805;\nconst float K_AUX23 = 806;\nconst float K_AUX24 = 807;\nconst float K_AUX25 = 808;\nconst float K_AUX26 = 809;\nconst float K_AUX27 = 810;\nconst float K_AUX28 = 811;\nconst float K_AUX29 = 812;\nconst float K_AUX30 = 813;\nconst float K_AUX31 = 814;\nconst float K_AUX32 = 815;\nconst float K_PAUSE = 153;\nconst float K_PRINTSCREEN = 174;\nconst float K_CAPSLOCK = 155;\nconst float K_SCROLLLOCK = 156;\nconst float K_SEMICOLON = 59;\nconst float K_PLUS = 43;\nconst float K_MINUS = 45;\nconst float K_TILDE = 126;\nconst float K_BACKQUOTE = 96;\nconst float K_BACKSLASH = 92;\nconst float K_GP_A = 826;\nconst float K_GP_B = 827;\nconst float K_GP_X = 828;\nconst float K_GP_Y = 829;\nconst float K_GP_LSHOULDER = 824;\nconst float K_GP_RSHOULDER = 825;\nconst float K_GP_LTRIGGER = 830;\nconst float K_GP_RTRIGGER = 831;\nconst float K_GP_BACK = 821;\nconst float K_GP_START = 820;\nconst float K_GP_LTHUMB = 822;\nconst float K_GP_RTHUMB = 823;\nconst float K_GP_DPAD_UP = 816;\nconst float K_GP_DPAD_DOWN = 817;\nconst float K_GP_DPAD_LEFT = 818;\nconst float K_GP_DPAD_RIGHT = 819;\nconst float K_GP_GUIDE = -202;\nconst float K_GP_UNKNOWN = -255;\nconst float K_GP_LTHUMB_UP = 832;\nconst float K_GP_LTHUMB_DOWN = 833;\nconst float K_GP_LTHUMB_LEFT = 834;\nconst float K_GP_LTHUMB_RIGHT = 835;\nconst float K_GP_RTHUMB_UP = 836;\nconst float K_GP_RTHUMB_DOWN = 837;\nconst float K_GP_RTHUMB_LEFT = 838;\nconst float K_GP_RTHUMB_RIGHT = 839;\n#endif\n#ifdef _ACCESSORS\naccessor strbuf : float\n{\n\tinline get float asfloat[float idx] = {return stof(bufstr_get(this, idx));};\n\tinline set float asfloat[float idx] = {bufstr_set(this, idx, ftos(value));};\n\tget string[float] = bufstr_get;\n\tset string[float] = bufstr_set;\n\tget float length = buf_getsize;\n};\naccessor searchhandle : float\n{\n\tget string[float] = search_getfilename;\n\tget float length = search_getsize;\n};\naccessor hashtable : float\n{\n\tinline get vector v[string key] = {return hash_get(this, key, '0 0 0', EV_VECTOR);};\n\tinline set vector v[string key] = {hash_add(this, key, value, HASH_REPLACE|EV_VECTOR);};\n\tinline get string s[string key] = {return hash_get(this, key, \"\", EV_STRING);};\n\tinline set string s[string key] = {hash_add(this, key, value, HASH_REPLACE|EV_STRING);};\n\tinline get float f[string key] = {return hash_get(this, key, 0.0, EV_FLOAT);};\n\tinline set float f[string key] = {hash_add(this, key, value, HASH_REPLACE|EV_FLOAT);};\n\tinline get __variant[string key] = {return hash_get(this, key, __NULL__);};\n\tinline set __variant[string key] = {hash_add(this, key, value, HASH_REPLACE);};\n};\naccessor infostring : string\n{\n\tget string[string] = infoget;\n\tinline set& string[string fld] = {this = infoadd(this, fld, value);};\n};\naccessor filestream : float\n{\n\tget string = fgets;\n\tinline set string = {fputs(this,value);};\n};\n#endif\n#undef DEP_CSQC\n#undef FTEDEP\n#undef DEP\n#pragma noref 0\n"
  },
  {
    "path": "quakec/menusys/menu/cvars.qc",
    "content": "#include \"../menusys/mitem_grid.qc\"\n\nclass mitem_cvargrid : mitem_grid\n{\n\tstrbuf\t\tgrid_buf_names;\t//left column (\n\tstatic void(strbuf newbuf) grid_setbuf =\n\t{\n\t\tif (grid_buf_names >= 0)\n\t\t\tbuf_del(grid_buf_names);\n\t\tgrid_buf_names = newbuf;\n\t\tgrid_numchildren = buf_getsize(grid_buf_names);\n\t\titem_resized();\n\t\tgrid_kactive = grid_numchildren?0:-1;\n\t\tgrid_selectionchanged(-1,grid_kactive);\n\t};\n\tfloat \tcursorpos;\n\tstring \tnewval;\n\tvoid() mitem_cvargrid =\n\t{\n\t\tgrid_buf_names = -1;\n\t\tcursorpos = -1;\n\t};\n\tstatic void(float idx) startedit =\n\t{\n\t\tif (cursorpos < 0)\n\t\t{\n\t\t\tstring v = bufstr_get(grid_buf_names, idx);\n\t\t\tfloat flags = cvar_type(v);\n\t\t\tif (!(flags&1) || (flags&32)) //(!exists || readonly). we don't bother to check for private - we can still set them (they'll just read as empty).\n\t\t\t\treturn;\n\t\t\tnewval = strzone(cvar_string(v));\n\t\t\tcursorpos = strlen(newval);\n\t\t}\n\t};\n\tstatic string() getdesc =\n\t{\n\t\tfloat idx = grid_kactive;\n\t\tif (idx < 0 || idx >= grid_numchildren)\n\t\t\treturn __NULL__;\n\t\tstring v = bufstr_get(grid_buf_names, idx);\n\t\treturn cvar_description(v);\n\t};\n\tvirtual void(vector pos, float idx) grid_draw;\n\tvirtual float(vector pos, float scan, float char, float down, float idx) grid_keypress;\n\tvirtual void(float olditem, float newitem) grid_selectionchanged;\n};\nvoid(vector pos, float idx) mitem_cvargrid::grid_draw =\n{\n\tstring text = bufstr_get(grid_buf_names, idx);\n\tstring value = cvar_string(text);\n\tstring defvalue = cvar_defstring(text);\n\tfloat flags;\n\n\tvector col = item_rgb;\n\tif (item_flags & IF_MFOCUSED && idx == grid_mactive)\n\t\tcol_z = 0;\n\tif (item_flags & IF_KFOCUSED && idx == grid_kactive)\n\t\tcol_x = 0;\n\n\tflags = cvar_type(text);\n\tif (!(flags&1))\n\t\tvalue = \"<INVALID>\";\n//\tif (flags & 2)\t//archive\n//\t\ttext = strcat(\"^hseta^h \", text);\n\tif (flags & 4)\t//private\n\t{\n\t\tif (!value)\t//should always be true...\n\t\t\tvalue = \"???\";\n\t\tif (checkextension(\"FTE_EXTENDEDTEXTCODES\"))\n\t\t\tvalue = strcat(\"^&FE\", value);\t//yellow, so you know its valid is meaningless.\n\t}\n\t//8 means engine, or something\n\t//16 just means it has a description. who even cares?\n\tif (flags & 32)\n\t{\n\t\tif (checkextension(\"FTE_EXTENDEDTEXTCODES\"))\n\t\t\tvalue = strcat(\"^&F4\", value);\t//make it red so you know its pointless trying to change it.\n\t}\n\n\tif (idx == grid_kactive && cursorpos >= 0)\n\t{\t//we're editing...\n\t\tvalue = newval;\n\t\tif (value == defvalue)\n\t\t\tcol *= 0.5;\n\t\tif (((cltime*4)&1) && (item_flags & IF_KFOCUSED))\n\t\t\tvalue = strcat(substring(value, 0, cursorpos), chr2str(0xe00b), substring(value, cursorpos+1, -1));\t//replace the char with a box... ugly, whatever\n\t}\n\telse if (value == defvalue)\n\t\tcol *= 0.5;\n\n\tvector valuepos = [pos_x+item_size_x/2, pos_y];\n\tpos_x = valuepos_x - stringwidth(text, TRUE, '1 1 0'*this.item_scale) - 8;\n\tvaluepos_x += 1;\n\tui.drawstring(pos, text, '1 1 0' * item_scale, col, item_alpha, 0);\n\tui.drawstring(valuepos, value, '1 1 0' * item_scale, col, item_alpha, 0);\n};\nvoid(float olditem, float newitem) mitem_cvargrid::grid_selectionchanged =\n{\n\tif (olditem == newitem)\n\t\treturn;\n\tif (olditem && cursorpos >= 0)\n\t{\t//we were editing... change it now.\n\t\tstring v = bufstr_get(grid_buf_names, olditem);\n\t\tcvar_set(v, newval);\n\t}\n\tcursorpos = -1;\t//stop editing now.\n\tnewval = \"\";\n};\nfloat(vector pos, float scan, float char, float down, float idx) mitem_cvargrid::grid_keypress =\n{\n\tif (!down)\n\t\treturn FALSE;\n\telse if (scan == K_ESCAPE)\n\t{\n\t\tif (cursorpos >= 0)\n\t\t\tcursorpos = -1;\t//cancel editing, forgetting the change.\n\t\telse\n\t\t\treturn FALSE;\n\t}\n\telse if (scan == K_ENTER)\n\t{\n\t\tif (cursorpos >= 0)\n\t\t\tgrid_selectionchanged(idx, -1);\n\t\telse\n\t\t\tstartedit(idx);\n\t}\n\telse if (scan == K_LEFTARROW && cursorpos>=0)\n\t\tcursorpos = max(cursorpos-1, 0);\n\telse if (scan == K_RIGHTARROW && cursorpos>=0)\n\t\tcursorpos+=1;\n\telse if (scan == K_MOUSE1 || scan == K_TOUCHTAP)\n\t{\n\t\tstartedit(idx);\n\t\tif (cursorpos>=0)\n\t\t{\n\t\t\tfloat valuepos = pos_x+(item_size_x/2)+1;\n\t\t\tcursorpos = strlen(newval);\n\t\t\tif (ui.mousepos[0] > valuepos-8)\n\t\t\t\twhile (cursorpos>0 && ui.mousepos[0] < valuepos+stringwidth(substring(newval, 0, cursorpos), TRUE, '1 1 0'*item_scale))\n\t\t\t\t\tcursorpos--;\n\t\t}\n\t}\n\telse if (scan == K_DEL && cursorpos<0)\n\t{\t//reset to default\n\t\tstring v = bufstr_get(grid_buf_names, idx);\n\t\tcvar_set(v, cvar_defstring(v));\n\t}\n\telse if (scan == K_HOME && cursorpos>= 0)\n\t\tcursorpos = 0;\n\telse if (scan == K_END && cursorpos>= 0)\n\t\tcursorpos = strlen(newval);\n\telse if (scan == K_DEL && cursorpos>=0 && cursorpos<strlen(newval))\n\t\tnewval = strzone(strcat(substring(newval, 0, cursorpos), substring(newval, cursorpos+1, -1)));\n\telse if ((scan == K_BACKSPACE || scan == K_DEL) && cursorpos>=0)\n\t{\n\t\tif (cursorpos>0)\n\t\t{\n\t\t\tnewval = strzone(strcat(substring(newval, 0, cursorpos-1), substring(newval, cursorpos, -1)));\n\t\t\tcursorpos -= 1;\n\t\t}\n\t}\n\telse if (char >= ' ' && cursorpos>=0)\n\t{\n\t\tstring ins = chr2str(char);\n\t\tnewval = strzone(strcat(substring(newval, 0, cursorpos), ins, substring(newval, cursorpos, -1)));\n\t\tcursorpos += strlen(ins);\n\t}\n\telse\n\t\treturn FALSE;\n\treturn TRUE;\n};\n\n\n\n\nclass cvarsmenu : mitem_exmenu\n{\n\tstring filter;\n\tfloat modifiedonly;\n\n\tmitem_cvargrid grid;\n\n\tstatic void(void) updatefilters =\n\t{\n\t\tstrbuf b = buf_create();\n\t\tbuf_cvarlist(b, filter, \"_*\");\n\t\tif (modifiedonly)\n\t\t{\n\t\t\tfor (float i = buf_getsize(b); i --> 0; )\n\t\t\t{\n\t\t\t\tstring v = bufstr_get(b, i);\n\t\t\t\tif (cvar_string(v) == cvar_defstring(v))\n\t\t\t\t\tbufstr_free(b, i);\n\t\t\t\telse if (cvar_type(v)&32)\n\t\t\t\t\tbufstr_free(b, i);\t//also remove read-only cvars. we can't reset them or anything so there's no point listing them here.\n\t\t\t}\n\t\t\tbuf_sort(b, 0, 0);\t//sort the names, to clean up any gaps\n\t\t}\n\n\t\tgrid.grid_setbuf(b);\n\t};\n\n\tvirtual string(string key) get =\n\t{\n\t\tif (key == \"*filter\")\n\t\t\treturn filter;\n\t\tif (key == \"*modified\")\n\t\t\treturn ftos(modifiedonly);\n\t\tif (key == \"*desc\")\n\t\t\treturn grid.getdesc();\n\t\treturn super::get(key);\n\t};\n\tvirtual void(string key, string newval) set =\n\t{\n\t\tif (key == \"*filter\")\n\t\t{\n\t\t\tstring old = filter;\n\t\t\tfilter = strzone(newval);\n\t\t\tif (old)\n\t\t\t\tstrunzone(old);\n\t\t}\n\t\telse if (key == \"*modified\")\n\t\t\tmodifiedonly = stof(newval);\n\t\telse\n\t\t\treturn super::set(key, newval);\n\t\tupdatefilters();\n\t};\n\tvirtual float(string key) isvalid =\n\t{\n\t\tif (key == \"*filter\")\n\t\t\treturn TRUE;\n\t\tif (key == \"*modified\")\n\t\t\treturn TRUE;\n\t\treturn super::isvalid(key);\n\t};\n};\n\nvoid(mitem_desktop desktop) M_Menu_Cvars =\n{\n\tmitem it;\n\tfloat h = (480+240)/2;\n\n\t//create the menu, give it focus, and make sure its displayed over everything else.\n\tcvarsmenu m = spawn(cvarsmenu, item_text:_(\"Mods List\"), item_flags:IF_SELECTABLE, item_command:\"m_main\", frame_stdheight:h);\n\tdesktop.add(m, RS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MIN | RS_X_MAX_PARENT_MAX|RS_Y_MAX_PARENT_MAX, '0 0', '0 0');\n\tdesktop.item_focuschange(m, IF_KFOCUSED);\n\tm.totop();\n\n\t//draw title art above the options\n\tmitem_pic banner = spawn(mitem_pic, item_text:\"gfx/ttl_cstm.lmp\", item_size_y:24, item_flags:IF_CENTERALIGN);\n\tm.add(banner, RS_X_MIN_PARENT_MID|RS_Y_MIN_PARENT_MID | RS_X_MAX_PARENT_MID|RS_Y_MAX_OWN_MIN, [banner.item_size_x*-0.5, h*-0.5], [banner.item_size_x*0.5, 24]);\n\n\tit = menuitemeditt_spawn(\"Cvar Filter\", \"*filter\", '280 8');\n\tm.add(it, RS_X_MIN_PARENT_MID|RS_Y_MIN_PARENT_MID | RS_X_MAX_PARENT_MID|RS_Y_MAX_OWN_MIN, [-160, h*-0.5+32], [40, 8]);\n\n\tit = menuitemcheck_spawn(\"Modified Only\", \"*modified\", '280 8');\n\tm.add(it, RS_X_MIN_PARENT_MID|RS_Y_MIN_PARENT_MID | RS_X_MAX_PARENT_MID|RS_Y_MAX_OWN_MIN, [40, h*-0.5+32], [200, 8]);\n\n\t//spawn our grid for the actual options. this provides a scrollbar if we have too many items.\n\tm.grid = spawn(mitem_cvargrid, item_flags: IF_SELECTABLE, item_scale: 8);\n\tm.add(m.grid, RS_X_MIN_PARENT_MID|RS_Y_MIN_PARENT_MID | RS_X_MAX_OWN_MIN|RS_Y_MAX_PARENT_MID, [-160, h*-0.5+48], [320, h*0.5-64]);\n\n\tit = spawn(mitem_label, item_text: \"*desc\", item_flags: 0, item_scale: 8);\n\tm.add(it, RS_X_MIN_PARENT_MID|RS_Y_MIN_PARENT_MID | RS_X_MAX_OWN_MIN|RS_Y_MAX_PARENT_MID, [-160, h*0.5-60], [320, h*0.5]);\n\n\tm.updatefilters();\n\t\n\t//and give us a suitable menu tint too, just because.\n\taddmenuback(m);\n};\n"
  },
  {
    "path": "quakec/menusys/menu/loadsave.qc",
    "content": "#ifndef LOADSAVE_QC\r\n#define LOADSAVE_QC\r\n\r\n//I'm feeling lazy, so I'm going to only provide X slots, like quake's menu.\r\nstatic string savenames[] =\r\n{\r\n\t\"a0\",\r\n\t\"a1\",\r\n\t\"a2\",\r\n\t\"quick\",\r\n\t\"s0\",\r\n\t\"s1\",\r\n\t\"s2\",\r\n\t\"s3\",\r\n\t\"s4\",\r\n\t\"s5\",\r\n\t\"s6\",\r\n\t\"s7\",\r\n\t\"s8\",\r\n\t\"s9\",\r\n};\r\n#define NUMSAVESLOTS savenames.length\r\n\r\n/*\r\nclass mitem_savescreeny : mitem\r\n{\r\n\tvirtual void(vector pos) item_draw =\r\n\t{\r\n\t\tstring s = sprintf(\"saves/s%g/screeny.png\", selectedsaveslot);\r\n\t\tif not(whichpack(s))\r\n\t\t\tif (drawgetimagesize(s) != '0 0 0')\r\n\t\t\t\tui.drawpic(pos, s, item_size, item_rgb, item_alpha, 0);\r\n\t};\r\n};\r\n*/\r\nclass mitem_saveoption : mitem_text\r\n{\r\n\tstring slot;\r\n\tfloat mode;\r\n\t\r\n\tvirtual void() mitem_saveoption =\r\n\t{\r\n\t\tif (mode)\r\n\t\t\titem_flags |= IF_SELECTABLE;\r\n\t};\r\n\r\n\tvirtual void(vector pos) item_draw =\r\n\t{\r\n\t\t//some sort of pulsing if its active.\r\n\t\tif (item_flags & IF_KFOCUSED)\r\n\t\t\tui.drawfill(pos, item_size, '1 0 0', sin(cltime)*0.125+0.150, 0);\r\n\t\tfloat w = stringwidth(item_text, TRUE, '1 1 0'*item_scale);\r\n\t\tui.drawstring(pos + [(item_size_x-w)/2, 0], item_text, '1 1 0' * item_scale, menuitem_textcolour(this), item_alpha, 0);\r\n\t};\r\n\r\n\tvirtual float(vector pos, float scan, float char, float down) item_keypress =\r\n\t{\r\n\t\tif (!down)\r\n\t\t\treturn FALSE;\r\n\t\tif (ISCONFIRMKEY(scan) || ((scan == K_MOUSE1 || scan == K_TOUCHTAP) && mouseinbox(pos, this.item_size)))\r\n\t\t{\r\n\t\t\tif (item_flags & IF_KFOCUSED)\r\n\t\t\t{\r\n\t\t\t\tswitch(mode)\r\n\t\t\t\t{\r\n\t\t\t\tcase 0:\r\n\t\t\t\t\tbreak;\t//can't load a slot which is empty.\r\n\t\t\t\tcase 1:\r\n\t\t\t\t\tlocalcmd(sprintf(\"m_pop;load %s\\n\", slot));\r\n\t\t\t\t\tbreak;\r\n\t\t\t\tcase 2:\r\n\t\t\t\t\t//FTE has a savegame_legacy command if you want compatibility with other engines.\r\n\t\t\t\t\tlocalcmd(sprintf(\"m_pop;wait;echo \\\"%s\\\";save %s\\n\", _(\"Saving Game\"), slot));\r\n\t\t\t\t\t//localcmd(sprintf(\"m_pop;wait;screenshot saves/s%g/screeny.png;echo \\\"%s\\\";save s%g\\n\", slot, _(\"Saving Game\"), slot));\r\n\t\t\t\t\tbreak;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\titem_parent.item_focuschange(this, IF_KFOCUSED);\r\n\t\t\t}\r\n\t\t\treturn TRUE;\r\n\t\t}\r\n\t\treturn FALSE;\r\n\t};\r\n};\r\n\r\nclass mitem_savepreview : mitem\r\n{\r\n\t//assumption: the only selectable children in the parent are save options.\r\n\tvirtual void(vector pos) item_draw =\r\n\t{\r\n\t\tmitem_saveoption sel;\r\n\t\tsel = (mitem_saveoption)item_parent.item_kactivechild;\r\n\t\tif (sel)\r\n\t\t{\r\n\t\t\tstring s = sprintf(\"saves/%s/screeny.tga\", sel.slot);\r\n\t\t\tif (drawgetimagesize(s) != '0 0 0')\r\n\t\t\t\tui.drawpic(pos, s, item_size, item_rgb, item_alpha, 0);\r\n\t\t}\r\n\t};\r\n};\r\n\r\nstatic string(string savename) scansave =\r\n{\r\n\tstring l;\r\n\tfloat f = fopen(sprintf(\"saves/%s/info.fsv\", savename), FILE_READ);\r\n\tif (f < 0)\r\n\t\tf = fopen(sprintf(\"%s.sav\", savename), FILE_READ);\r\n\tif (f < 0)\r\n\t\treturn __NULL__;\t//weird\r\n\r\n\tfgets(f);\t//should be the version\r\n\tl = fgets(f);\t//description\r\n\tif (l)\r\n\t\tl = strreplace(\"_\", \" \", l);\r\n\tfclose(f);\r\n\treturn l;\r\n};\r\n\r\nvoid(mitem_desktop desktop, float mode) M_LoadSave =\r\n{\r\n\tmitem_exmenu m = spawn(mitem_exmenu, item_text:\"Load/Save\", item_flags:IF_SELECTABLE, item_command:\"m_main\");\r\n\tdesktop.add(m, RS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MIN | RS_X_MAX_PARENT_MAX|RS_Y_MAX_PARENT_MAX, '0 0', '0 0');\r\n\tdesktop.item_focuschange(m, IF_KFOCUSED);\r\n\tm.totop();\r\n\r\n\tstring l;\r\n\tfloat i;\r\n\tfloat smode;\r\n\tfloat pos = NUMSAVESLOTS*16/-2;\r\n\r\n\tmitem_pic banner = spawn(mitem_pic, item_text:((mode==2)?\"gfx/p_save.lmp\":\"gfx/p_load.lmp\"), item_size_y:24, item_flags:IF_CENTERALIGN);\r\n\tm.add(banner, RS_X_MIN_PARENT_MID|RS_Y_MIN_PARENT_MID | RS_X_MAX_PARENT_MID|RS_Y_MAX_PARENT_MID, [(banner.item_size_x)*-0.5, pos-32], [(banner.item_size_x)*0.5, pos-8]);\r\n\r\n\tfor (i = 0; i < NUMSAVESLOTS; i++)\r\n\t{\r\n\t\tl = scansave(savenames[i]);\r\n\t\tsmode = mode;\r\n\t\tif (l==\"\")\r\n\t\t{\r\n\t\t\tl = \"Empty Slot\";\r\n\t\t\tif (mode==1)\r\n\t\t\t\tsmode = 0;\r\n\t\t}\r\n\t\tm.addm(spawn (mitem_saveoption, item_scale:16, slot:savenames[i], mode:smode, item_text:l), [-320, pos+i*16], [320, pos+(i+1)*16]);\r\n\t}\r\n\r\n\tm.addm(spawn(mitem_savepreview), [-320, -240], [320, 240]);\r\n\taddmenuback(m);\r\n};\r\n\r\nvoid(mitem_desktop desktop) M_Load =\r\n{\r\n\tM_LoadSave(desktop, 1);\r\n};\r\nvoid(mitem_desktop desktop) M_Save =\r\n{\r\n\tif (!(isserver() || dp_workarounds))\r\n\t\tM_Main(desktop);\t//can't save when not connected. this should be rare, but can if you use the console or the main menu options are stale.\r\n\telse\r\n\t\tM_LoadSave(desktop, 2);\r\n};\r\n#endif\r\n"
  },
  {
    "path": "quakec/menusys/menu/main.qc",
    "content": "/*\r\nThe main / root menu.\r\nJust a load of text with console commands attached.\r\nChoice of buttons available is somewhat dynamic.\r\n\r\nThere's also some generic kludge crap in here, like menu background tints\r\n*/\r\n\r\nenum\r\n{\r\n\tE_FTE,\r\n\tE_QSS,\r\n} engine; //For use ONLY with known possible cvar values.\r\nvoid() FingerprintEngine =\r\n{\t//this is evil.\r\n\t//different engines have different values/meanings for different cvars.\r\n\t//basically we end up having no choice but to guess which engine we're running in, otherwise we can't tailor all the options properly.\r\n\r\n\tstring e = cvar_string(\"pr_engine\");\r\n\tif (strstrofs(e, \"QSS\")>=0)\r\n\t\tengine = E_QSS;\r\n\telse if (strstrofs(e, \"FTE\")>=0)\r\n\t\tengine = E_FTE;\r\n\telse\r\n\t\tengine = E_FTE;\t//don't really know.\r\n};\r\n\r\n/*\r\nAdds a background tint to a (typically) exmenu parent.\r\nIn FTE, we use built-in stuff to give a sepia effect.\r\nIn DP, we just tint it black.\r\n*/\r\nnonstatic void(mitem_frame m) addmenuback =\r\n{\r\n\tif (iscachedpic(\"menutint\"))\t//fte internal hacks! meh, admit it. its cool.\r\n\t\tm.add(spawn (mitem_pic, item_text:\"menutint\", item_alpha:0.5),\r\n\t\t\t\t\t\t\t\t\t\t\t\tRS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MIN | RS_X_MAX_PARENT_MAX|RS_Y_MAX_PARENT_MAX, [0, 0], [0, 0]);\r\n\telse\r\n\t\tm.add(spawn (mitem_fill, item_rgb:'0 0 0.01', item_alpha:0.5),\r\n\t\t\t\t\t\t\t\t\t\t\t\tRS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MIN | RS_X_MAX_PARENT_MAX|RS_Y_MAX_PARENT_MAX, [0, 0], [0, 0]);\r\n};\r\n\r\n/*\r\nhelper functions to avoid blowing up in older clients.\r\n\r\n*/\r\n#define cv2(dpc,qwc) (cvar_type(dpc)?dpc:qwc)\r\n#define cv3(dpc,qs,qwc) (cvar_type(dpc)?dpc:(cvar_type(qs)?qs:qwc))\r\n#ifdef CSQC\r\n\t#define ISMENUQC\tFALSE\r\n#else\r\n\t#define ISMENUQC\tTRUE\r\n#endif\r\nfloat(string cmd, float assumption) checkcommand2 =\r\n{\r\n\t__using checkcommand;\r\n\tif (!checkextension(\"FTE_QC_CHECKCOMMAND\"))\r\n\t\treturn assumption;\r\n\treturn checkcommand(cmd);\r\n};\r\nfloat(__variant cmd, float assumption) checkbuiltin2 =\r\n{\r\n\tif (!checkbuiltin(checkbuiltin))\t//teehee. if the #0 stuff isn't working (DP) then it ends up calling an OP_DONE(0) instruction giving a return false.\r\n\t\treturn assumption;\r\n\treturn checkbuiltin(cmd);\r\n};\r\n#define checkbuiltin2(c,a) ((a)?checkbuiltin2(c,a):checkbuiltin(c))\r\n\r\n#ifdef CSQC\r\nfloat() clientstate =\r\n{\r\n\tif (serverkey(\"constate\") != \"disconnected\")\r\n\t\treturn 2;\r\n\treturn 1;\r\n};\r\n#endif\r\n\r\nnonstatic void(mitem_desktop desktop) M_Main =\r\n{\r\n\tlocal float y;\r\n\tlocal mitem_exmenu m;\r\n\r\n\t//no dupes please.\r\n\tm = (mitem_exmenu)desktop.findchildtext(_(\"Main Menu\"));\r\n\tif (m)\r\n\t{\r\n\t\tm.totop();\r\n\t\treturn;\r\n\t}\r\n\r\n\t//create a fullscreen frame\r\n\tm = spawn(mitem_exmenu, item_text:_(\"Main Menu\"), item_flags:IF_SELECTABLE);\r\n\tdesktop.add(m, RS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MIN | RS_X_MAX_PARENT_MAX|RS_Y_MAX_PARENT_MAX, '0 0', '0 0');\r\n\tdesktop.item_focuschange(m, IF_KFOCUSED);\r\n\tm.totop();\r\n\r\n//\tm.item_flags |= IF_NOKILL;\r\n//\tm.adda(menuitempic_spawn (\"gfx/qplaque.lmp\", \t'32 144'), \t'16 4');\r\n\r\n\ty = 7*-16/2;\r\n\r\n\t//draw title art above the options\r\n\tmitem_pic banner = spawn(mitem_pic, item_text:\"gfx/ttl_main.lmp\", item_size_y:24, item_flags:IF_CENTERALIGN);\r\n\tm.add(banner, RS_X_MIN_PARENT_MID|RS_Y_MIN_PARENT_MID | RS_X_MAX_PARENT_MID|RS_Y_MAX_PARENT_MID, [(160-banner.item_size_x)*0.5, y-32], [(160+banner.item_size_x)*0.5, y-8]);\r\n\r\n\r\n//a macro, in a desperate attempt at readability\r\n#define menuitemtext_cladd16(m,t,c,y) m.addm(spawn(mitem_text, item_text:t, item_command:c, item_scale:16, item_flags:IF_CENTERALIGN), [0, y], [160, y+16])\r\n\r\n\tif (clientstate() == 2)\t\t\t\t\t\t\t\t{menuitemtext_cladd16(m, isdemo()?_(\"Return To Demo\"):_(\"Return To Game\"),\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t \t\t\t\"m_pop\",\t\t\t\ty); y += 16;}\r\n\tif (clientstate() == 2)\t\t\t\t\t\t\t\t{menuitemtext_cladd16(m, isdemo()?_(\"End Demo\"):(isserver?_(\"End Game\"):_(\"Disconnect\")),\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"m_pop;disconnect\",\t\ty); y += 16;}\r\n\tif (isserver() != 1 && checkbuiltin2(gethostcacheindexforkey,ISMENUQC))\t{menuitemtext_cladd16(m, _(\"Join Server\"),\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"m_pop;m_servers\",\t\ty); y += 16;}\r\n\tif (checkcommand2(\"map\", TRUE))\t\t\t\t\t\t{menuitemtext_cladd16(m, _(\"New Game\"),\t\t\t\t\"m_pop;m_newgame\",\t\ty); y += 16;}\r\n\tif (checkcommand2(\"menu_demo\", FALSE))\t\t\t\t{menuitemtext_cladd16(m, _(\"Demos\"),\t\t\t\t\"m_pop;menu_demo\",\t\ty); y += 16;}\r\n\tif (checkcommand2(\"save\", TRUE) && (isserver()||dp_workarounds))\t{menuitemtext_cladd16(m, _(\"Save\"),\t\"m_pop;m_save\",\t\t\ty); y += 16;}\r\n\tif (checkcommand2(\"load\", TRUE))\t\t\t\t\t{menuitemtext_cladd16(m, _(\"Load\"),\t\t\t\t\t\"m_pop;m_load\",\t\t\ty); y += 16;}\r\n\tif (checkcommand2(\"cef\", FALSE))\t\t\t\t\t{menuitemtext_cladd16(m, _(\"Browser\"),\t\t\t\t\"m_pop;cef google.com\",\ty); y += 16;}\r\n\tif (checkcommand2(\"xmpp\", FALSE))\t\t\t\t\t{menuitemtext_cladd16(m, _(\"Social\"),\t\t\t\t\"m_pop;xmpp\",\t\t\ty); y += 16;}\r\n\tif (checkcommand2(\"irc\", FALSE))\t\t\t\t\t{menuitemtext_cladd16(m, _(\"IRC\"),\t\t\t\t\t\"m_pop;irc /info\",\t\ty); y += 16;}\r\n\t/*if (checkbuiltin2(getpackagemanagerinfo,FALSE))\t{menuitemtext_cladd16(m, _(\"Updates+Packages\"),\t\t\"m_pop;m_updates\",\t\ty); y += 16;}\r\n\telse*/ if (checkcommand2(\"menu_download\", FALSE))\t{menuitemtext_cladd16(m, _(\"Updates+Packages\"),\t\t\"m_pop;menu_download\",\ty); y += 16;}\r\n\tif (checkcommand2(\"qi\", FALSE))\t\t\t\t\t\t{menuitemtext_cladd16(m, _(\"Quake Injector\"),\t\t\"m_pop;qi\",\t\t\t\ty); y += 16;}\r\n\tif (checkbuiltin2(getgamedirinfo,TRUE))\t\t\t\t{menuitemtext_cladd16(m, _(\"Mods\"),\t\t\t\t\t\"m_pop;m_mods\",\t\t\ty); y += 16;}\r\n\tif (checkcommand2(\"sys_openfile\", FALSE))\t\t\t{menuitemtext_cladd16(m, _(\"Open/Add External File\"),\"m_pop;sys_openfile\",\ty); y += 16;}\t//opens a file chooser dialog in eg chromium.\r\n\tif (checkcommand2(\"xr_toggle\", FALSE))\t\t\t\t{menuitemtext_cladd16(m, _(\"Toggle VR\"),\t\t\t\"m_pop;xr_toggle\",\t\ty); y += 16;}\t//for webxr support\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{menuitemtext_cladd16(m, _(\"Options\"),\t\t\t\t\"m_pop;m_options\",\t\ty); y += 16;}\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{menuitemtext_cladd16(m, _(\"Quit\"), \t\t\t\t\"m_pop;m_quit\",\t\t\ty); y += 16;}\r\n\r\n\t//spinny quad/pent, for the luls\r\n\tlocal string it = (random()<0.9)?\"progs/quaddama.mdl\":\"progs/invulner.mdl\";\r\n\tm.add(spawn (mitem_spinnymodel, item_text: it), RS_X_MIN_PARENT_MID|RS_Y_MIN_PARENT_MID | RS_X_MAX_PARENT_MID|RS_Y_MAX_PARENT_MID, [-160, 12*-16/2], [0, 12*16/2]);\r\n\r\n#ifdef REVISION\r\n\t#define MENUSYS_REVISION REVISION\r\n#else\r\n\t#define MENUSYS_REVISION \"<UNKNOWN>\"\r\n#endif\r\n#ifdef CSQC\r\n\t#define MODLEPOSTFIX \"-CSQC\"\r\n#else\r\n\t#define MODLEPOSTFIX \"-MQC\"\r\n#endif\r\n\tm.add(spawn(mitem_text, item_text:strcat(\"MenuSys \"MENUSYS_REVISION MODLEPOSTFIX\"; \", cvar_string(\"pr_engine\")), item_scale:8), RS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MAX|RS_X_MAX_PARENT_MAX|RS_Y_MAX_PARENT_MAX,[0,-8],[0,0]);\r\n\taddmenuback(m);\r\n};\r\n"
  },
  {
    "path": "quakec/menusys/menu/mods.qc",
    "content": "class mitem_pictext : mitem_text\r\n{\r\n\tstring pic;\r\n\tvirtual void(vector pos) item_draw =\r\n\t{\r\n\t\tif (pic)\r\n\t\t\tdrawpic(pos, pic, [item_size_y,item_size_y], '1 1 1', 1, 0); \r\n\t\tpos_x += item_size_y;\r\n\t\tsuper::item_draw(pos);\r\n\t};\r\n};\r\n\r\nvoid(mitem_desktop desktop) M_Menu_Mods =\r\n{\r\n\tfloat h = (480+240)/2;\r\n\r\n\t//create the menu, give it focus, and make sure its displayed over everything else.\r\n\tmitem_exmenu m = spawn(mitem_exmenu, item_text:_(\"Mods List\"), item_flags:IF_SELECTABLE, item_command:\"m_main\", frame_stdheight:h);\r\n\tdesktop.add(m, RS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MIN | RS_X_MAX_PARENT_MAX|RS_Y_MAX_PARENT_MAX, '0 0', '0 0');\r\n\tdesktop.item_focuschange(m, IF_KFOCUSED);\r\n\tm.totop();\r\n\r\n\t//draw title art above the options\r\n\tmitem_pic banner = spawn(mitem_pic, item_text:\"gfx/ttl_cstm.lmp\", item_size_y:24, item_flags:IF_CENTERALIGN);\r\n\tm.add(banner, RS_X_MIN_PARENT_MID|RS_Y_MIN_PARENT_MID | RS_X_MAX_PARENT_MID|RS_Y_MAX_OWN_MIN, [banner.item_size_x*-0.5, h*-0.5], [banner.item_size_x*0.5, 24]);\r\n\r\n\t//spawn a container frame for the actual options. this provides a scrollbar if we have too many items.\r\n\tmitem_frame fr = spawn(mitem_frame, item_flags: IF_SELECTABLE, frame_hasscroll:TRUE);\r\n\tm.add(fr, RS_X_MIN_PARENT_MID|RS_Y_MIN_PARENT_MID | RS_X_MAX_OWN_MIN|RS_Y_MAX_PARENT_MID, [-140, h*-0.5+32], [280, h*0.5]);\r\n\r\n\tfloat mod;\r\n\tfor (mod = 0; ; mod++)\r\n\t{\r\n\t\t__using GGDI_DESCRIPTION, GGDI_LOADCOMMAND, GGDI_ICON;\r\n\r\n\t\tstring gamedir = getgamedirinfo(mod, GGDI_GAMEDIR);\r\n\t\tif not(gamedir)\r\n\t\t\tbreak;\r\n\t\tstring desc = getgamedirinfo(mod, GGDI_DESCRIPTION);\r\n\t\tif (desc==\"\")\r\n\t\t{\t//clean up some stuff a little, if we know it.\r\n\t\t\tstring lwr = strtolower(gamedir);\t//windows sucks.\r\n\t\t\tif (lwr == \"hipnotic\")\r\n\t\t\t\tdesc = \"Scourge of Armagon\";\r\n\t\t\telse if (lwr == \"rogue\")\r\n\t\t\t\tdesc = \"Dissolution of Eternity\";\r\n\t\t\telse if (lwr == \"dopa\")\r\n\t\t\t\tdesc = \"Dimension of the Past\";\r\n\t\t\telse if (lwr == \"mg1\")\r\n\t\t\t\tdesc = \"Dimensions of the Machine\";\r\n\t\t\telse if (lwr == \"ad\")\r\n\t\t\t\tdesc = \"Arcane Dimensions\";\r\n\t\t\telse if (lwr == \"quoth\")\r\n\t\t\t\tdesc = \"Quoth\";\r\n\t\t\telse if (lwr == \"rally\")\r\n\t\t\t\tdesc = \"Quake Rally\";\r\n\t\t\telse if (lwr == \"fortress\")\r\n\t\t\t\tdesc = \"Team Fortress\";\r\n\t\t}\r\n\t\tif (desc==\"\")\r\n\t\t\tdesc = sprintf(\"%s/\", gamedir);\t//no description given, so make it clear that its an actual subdir name\r\n\t\telse\r\n\t\t\tdesc = sprintf(\"%s ^h(%s)\", desc, gamedir);\t//include the gamedir, faded somewhat.\r\n\t\tstring cmd = getgamedirinfo(mod, GGDI_LOADCOMMAND);\r\n\t\tif not(cmd)\t//for dp users, if they somehow run this\r\n\t\t\tcmd = sprintf(\"gamedir %s\", gamedir);\r\n\r\n\t\tstring icon = getgamedirinfo(mod, GGDI_ICON);\r\n\r\n\t\t//add some extra stuff to reset the menu\r\n\t\tcmd = strcat(\"m_pop; \", cmd, \"; togglemenu\");\r\n\r\n\t\tfr.add(spawn(mitem_pictext, pic:icon, item_text:desc, item_command:cmd, item_scale:16, item_flags:0), RS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MIN | RS_X_MAX_PARENT_MAX|RS_Y_MAX_OWN_MIN, [0, mod*16], [0, 16]);\r\n\t}\r\n\r\n\tif (!mod)\r\n\t\tfr.add(spawn(mitem_text, item_text:\"No mods detected\", item_scale:16, item_flags:0), RS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MIN | RS_X_MAX_PARENT_MAX|RS_Y_MAX_OWN_MIN, [0, mod*16], [0, 16]);\r\n\t\r\n\t//and give us a suitable menu tint too, just because.\r\n\taddmenuback(m);\r\n};\r\n"
  },
  {
    "path": "quakec/menusys/menu/newgame.qc",
    "content": "//maxclients is a QW thing. NQ engines use maxplayers (which requires a disconnect to apply)\r\n#include \"../menusys/mitem_grid.qc\"\r\n\r\nstatic string newgameinfo;\r\n\r\nnonstatic void(mitem_desktop desktop) M_Dir =\r\n{\t//implementing this primarily so its available in QSS. :P\r\n\tstring path = argv(1);\r\n\tstring pack = argv(2);\r\n\tsearchhandle h;\r\n\tif (path == \"\")\r\n\t{\r\n\t\tprint(\"m_dir <FILEPATH> [PACKAGE]\\n\");\r\n\t\treturn;\r\n\t}\r\n\tif (pack)\r\n\t\th = search_begin(path, SB_FULLPACKAGEPATH|SB_FORCESEARCH|16, 0, argv(2));\r\n\telse\r\n\t\th = search_begin(path, SB_FULLPACKAGEPATH|16|32, 0);\r\n\tprint(sprintf(\"Directory listing of %S (%g files)\\n\", path, search_getsize(h)));\r\n\tfor (float i = 0; i < search_getsize(h); i++)\r\n\t{\r\n\t\tfloat sz = search_getfilesize(h,i);\r\n\t\tstring szfmt = \"b\";\r\n\t\tif (sz > 512*1024*1024)\r\n\t\t\tsz /= 1024*1024*1024, szfmt = \"gb\";\r\n\t\telse if (sz > 512*1024)\r\n\t\t\tsz /= 1024*1024, szfmt = \"mb\";\r\n\t\telse if (sz > 512)\r\n\t\t\tsz /= 1024, szfmt = \"kb\";\r\n\t\t\r\n\t\tprint(sprintf(\"  %S (%.4g %s, %s, %S)\\n\",\r\n\t\t\tsearch_getfilename(h,i),\r\n\t\t\tsz,szfmt,\r\n\t\t\tsearch_getfilemtime(h,i),\r\n\t\t\tsearch_getpackagename(h,i)\r\n\t\t\t));\r\n\r\n#if 1\r\n\t\tfloat f = search_fopen(h,i);\r\n\t\tif (f>=0)\r\n\t\t{\r\n\t\t\tprint(sprintf(\"^`u8:\" \"\\t%S\\n\", substring(fgets(f), 0, 64)));\r\n\t\t\tfclose(f);\r\n\t\t}\r\n\t\telse\r\n\t\t\tprint(\"\\tsearch_fopen failure\\n\");\r\n#endif\r\n\t}\r\n\tsearch_end(h);\r\n};\r\nnonstatic void(mitem_desktop desktop) M_FOpen =\r\n{\r\n\tfloat f = fopen(argv(1), FILE_READ);\r\n\tstring s;\r\n\tif (f>=0)\r\n\t{\r\n\t\twhile ((s=fgets(f)))\r\n\t\t\tprint(sprintf(\"^`u8:\" \"%s\\n\", s));\r\n\t\tfclose(f);\r\n\t\tprint(\"<EOF>\\n\");\r\n\t}\r\n\telse\r\n\t\tprint(\"fopen failure\\n\");\r\n};\r\n\r\nstatic string(string name) basemapname =\r\n{\r\n\tif (!strncmp(name, \"maps/\", 5))\r\n\t\tname = substring(name, 5, -1);\r\n\tif (substring(name, -4, -1) == \".bsp\")\r\n\t\tname = substring(name, 0, -5);\r\n\treturn name;\r\n};\r\nstatic string(string name) enginemapname =\r\n{\r\n\tfloat ofs = strstrofs(name, \":\");\r\n\tif (ofs<0)\r\n\t{\r\n\t\tif (!strncmp(name, \"maps/\", 5))\r\n\t\t\tname = substring(name, 5, -1);\r\n\t\tif (substring(name, -8, -1) == \".bsp.gz\")\r\n\t\t\tname = strcat(substring(name, 0, -9),\"^8.gz\");\t//fiddle a little with its display\r\n\t\tif (substring(name, -4, -1) == \".bsp\")\r\n\t\t\tname = substring(name, 0, -5);\r\n\t}\r\n\telse\r\n\t{\t//faff with the cullers to try to make it more readable.\r\n\t\tname = sprintf(\"^8%s:^7%s\", substring(name, 0, ofs), substring(name, ofs, -1));\r\n\t}\r\n\treturn name;\r\n};\r\n\r\n#define FOURCC(a,b,c,d) ((int)(a)<<0i)|((int)(b)<<8i)|((int)(c)<<16i)|((int)(d)<<24i)\r\nstatic string(string name) getmapdesc =\r\n{\r\n\tif (strstrofs(name, \":\")>=0)\r\n\t{\t//FIXME: look up the package's title?\r\n\t\treturn strcat(\"^8\", basemapname(name));\r\n\t}\r\n\tif (checkbuiltin(fread) && checkbuiltin(fseek) && checkbuiltin(memalloc) && checkbuiltin(memfree) && checkbuiltin(memgetval) && checkbuiltin(itof) && checkbuiltin(ftoi))\r\n\t{\t//we can do it, so do it.\r\n\t\tfilestream f = fopen(name, FILE_READ);\r\n\t\tif (f < 0)\r\n\t\t\tprint(sprintf(\"Unable to read %s\\n\", name));\r\n\t\t\r\n\t\tname = basemapname(name);\r\n\t\tif (f < 0)\r\n\t\t\treturn name;\r\n\r\n\t\tint *header = memalloc(sizeof(int)*4);\r\n\t\tint bspver = 0, entofs, entlen;\r\n\t\tif (fread(f, (void*)header, sizeof(int)*4) == sizeof(int)*4 && ((bspver=header[0]),(\r\n\t\t\tbspver == FOURCC('I','B','S','P') ||\t//IBSP (q2/q3)\r\n\t\t\tbspver == FOURCC('R','B','S','P') ||\t//RBSP (jk2o etc)\r\n\t\t\tbspver == FOURCC('F','B','S','P') ||\t//IBSP (qfusion/warsow)\r\n\t\t\tbspver == 29i ||\t//q1\r\n\t\t\tbspver == 30i ||\t//hl\r\n\t\t\tbspver == FOURCC('B','S','P','2'))))\t//bsp2\r\n\t\t{\r\n\t\t\tif (bspver == FOURCC('I','B','S','P'))\r\n\t\t\t{\t//has an actual version number! ooo...\r\n\t\t\t\tentofs = header[2];\r\n\t\t\t\tentlen = header[3];\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tentofs = header[1];\r\n\t\t\t\tentlen = header[2];\r\n\t\t\t}\r\n\t\t\tfseek(f, entofs);\r\n\t\t\tstring s = (string)memalloc(entlen+1);\r\n\t\t\tfread(f, (void*)s, entlen);\r\n\t\t\tfloat argc = tokenize(s);\r\n\t\t\tif (argv(0) == \"{\")\r\n\t\t\t{\r\n\t\t\t\tfor (float p = 1; p < argc; p+=2)\r\n\t\t\t\t{\r\n\t\t\t\t\tstring t = argv(p);\r\n\t\t\t\t\tif (t == \"message\")\r\n\t\t\t\t\t{\t//finally found the human-readable name of the map. woo.\r\n\t\t\t\t\t\tname = argv(p+1);\r\n\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t}\r\n\t\t\t\t\tif (t == \"}\")\t//don't read the message from some kind of trigger\r\n\t\t\t\t\t\tbreak;\r\n\t\t\t\t\tif (t == \"{\")\t//some sort of corruption\r\n\t\t\t\t\t\tbreak;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t\tname = \"ERROR\";\r\n\t\t\tmemfree((void*)s);\r\n\t\t}\r\n\t\telse\r\n\t\t\tname = sprintf(\"UNSUPPORTED %i\", bspver);\r\n\t\tmemfree(header);\r\n\t\tfclose(f);\r\n\t\treturn name;\r\n\t}\r\n\treturn \"\";\t//we can't do that in this engine... don't show anything, because its pointless showing the same thing twice.\r\n};\r\n\r\nstatic string(string name) packagetogamedir =\r\n{\r\n\tfloat so = strstrofs(name, \"/\");\r\n\tif (so>=0)\r\n\t\tname = substring(name, 0, so);\r\n\r\n\t//don't hide id1/dm4 etc just because the user previously auto-downloaded eg qw/aerowalk\r\n\tname = strtolower(name);\r\n\tif (name == \"qw\" || name == \"fte\")\r\n\t\tname = \"id1\";\r\n\r\n\treturn name;\r\n};\r\nclass mitem_maplist : mitem_grid\r\n{\r\n\tstrbuf\tnames;\r\n\tstrbuf\tdescs;\r\n\tvirtual void() item_remove =\r\n\t{\r\n\t\tbuf_del(names);\r\n\t\tbuf_del(descs);\r\n\t\tsuper::item_remove();\r\n\t};\r\n\tvoid() mitem_maplist =\r\n\t{\r\n\t\tsearchhandle searchtable;\r\n\t\tsearchtable = search_begin(\"maps/*.bsp:maps/*.bsp.gz\", SB_FULLPACKAGEPATH|SB_MULTISEARCH, 0);\r\n\t\tfloat files = search_getsize(searchtable);\r\n\t\tif (files && checkbuiltin(search_getpackagename))\r\n\t\t{\t//find which gamedir the first entry is in (highest-priority is first)\r\n\t\t\tstring gd = packagetogamedir(search_getpackagename(searchtable,0));\r\n\r\n\t\t\tfor (float count = 1; count < files; count++)\r\n\t\t\t{\r\n\t\t\t\tstring mgd = packagetogamedir(search_getpackagename(searchtable,count));\r\n\t\t\t\tif (mgd != gd)\r\n\t\t\t\t{\t//the gamedir changed... truncate the list here.\r\n\t\t\t\t\tfiles = count;\r\n\t\t\t\t\tbreak;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t\tgrid_numchildren = 0;\r\n\t\tnames = buf_create();\r\n\t\tfor (float count = 0; count < files; count++)\r\n\t\t{\r\n\t\t\tstring n = search_getfilename(searchtable, count);\r\n\t\t\tif (!strncmp(n, \"maps/_\", 6) || !strncmp(n, \"maps/b_\", 7))\r\n\t\t\t\tcontinue;\t//don't add b_* names nor _*\r\n\t\t\tbufstr_add(names, n, TRUE);\r\n\t\t}\r\n\t\tsearch_end(searchtable);\r\n\r\n\t\tfor (float pkg = 0; ; pkg++)\r\n\t\t{\r\n\t\t\tstring n = getpackagemanagerinfo(pkg, GPMI_NAME);\r\n\t\t\tif not(n)\r\n\t\t\t\tbreak;\t//end of list.\r\n\t\t\tstring maps = getpackagemanagerinfo(pkg, GPMI_MAPS);\r\n\t\t\tif not(maps)\r\n\t\t\t\tcontinue;\t//none? not a mappack? don't waste time on the tokenizing.\r\n\t\t\tfloat eq = strstrofs(n, \"=\");\r\n\t\t\tif (eq >= 0)\r\n\t\t\t\tn = substring(n, 0, eq);\r\n\t\t\tfloat c = tokenize_console(maps);\r\n\t\t\tfor (float i = 0; i < c; i++)\r\n\t\t\t\tbufstr_add(names, sprintf(\"%s:%s\", n, argv(i)), TRUE);\r\n\t\t}\r\n\r\n\t\tbuf_sort(names, 0, FALSE);\t//make sure they're sorted now.\r\n\t\tgrid_numchildren = buf_getsize(names);\r\n\t\tdescs = buf_create();\r\n\r\n\t\titem_resized();\t//FIXME: is this needed?\r\n\t\tgrid_kactive = grid_numchildren?0:-1;\r\n\t\tgrid_selectionchanged(-1,grid_kactive);\r\n\t};\r\n\tvirtual void(vector pos, float idx) grid_draw =\r\n\t{\r\n\t\tstring map = bufstr_get(names, idx);\r\n\t\tstring desc = bufstr_get(descs, idx);\r\n\t\tif not(desc)\r\n\t\t{\r\n\t\t\tdesc = map;\r\n\t\t\tstatic float lasttime;\r\n\t\t\tif (time != lasttime)\r\n\t\t\t{\t//fix up the name and cache it.\r\n\t\t\t\tlasttime = time;\r\n\t\t\t\tdesc = getmapdesc(map);\r\n\t\t\t\tbufstr_set(descs, idx, desc);\r\n\t\t\t}\r\n\t\t}\r\n\t\tmap = basemapname(map);\r\n\r\n\t\tvector col = (map == get(\"map\"))?[1.16, 0.54, 0.41]:'1 1 1';\r\n\t\tif (item_flags & IF_MFOCUSED && idx == grid_mactive)\r\n\t\t\tcol_z = 0; \r\n\t\tif (item_flags & IF_KFOCUSED && idx == grid_kactive)\r\n\t\t\tcol_x = 0;\r\n\r\n\t\tvector valuepos = [pos_x+item_size_x/2, pos_y]; \r\n\t\tpos_x = valuepos_x - stringwidth(map, TRUE, '1 1 0'*this.item_scale) - 8;\r\n\t\tvaluepos_x += 1;\r\n\t\tui.drawstring(pos, map, '1 1 0' * item_scale, col, item_alpha, 0);\r\n\t\tui.drawstring(valuepos, desc, '1 1 0' * item_scale, col, item_alpha, 0);\r\n\t};\r\n\tvirtual float(vector pos, float scan, float chr, float down, float idx) grid_keypress =\r\n\t{\r\n\t\tif (down)\r\n\t\t\tif (ISCONFIRMKEY(scan) || ((scan == K_MOUSE1 || scan == K_TOUCHTAP) && mouseinbox(pos, this.item_size)))\r\n\t\t\t{\r\n\t\t\t\tstring map = bufstr_get(names, idx);\r\n\t\t\t\tmap = enginemapname(map);\r\n\t\t\t\tset(\"map\", map);\r\n\t\t\t\treturn TRUE;\r\n\t\t\t}\r\n\t\treturn FALSE;\r\n\t};\r\n//\tvirtual void(float olditem, float newitem) grid_selectionchanged;\r\n};\r\n\r\nclass mitem_newgame : mitem_exmenu\r\n{\r\n\tvirtual float(string key) isvalid =\r\n\t{\r\n\t\treturn TRUE;\r\n\t};\r\n\tvirtual string(string key) get =\r\n\t{\r\n\t\treturn infoget(newgameinfo, key);\r\n\t};\r\n\tvirtual void(string key, string newval) set =\r\n\t{\r\n\t\tstring old = newgameinfo;\r\n\t\tnewgameinfo = strzone(infoadd(newgameinfo, key, newval));\r\n\t\tif (old)\r\n\t\t\tstrunzone(old);\r\n\t};\r\n};\r\n\r\nnonstatic void(mitem_desktop desktop) M_NewGame =\r\n{\r\n\tmitem_pic banner;\r\n\tstring gametype = argv(1);\r\n\tlocal float pos;\r\n\tmitem_exmenu m;\r\n\tif (gametype == \"sp\")\r\n\t{\r\n\t\t//single player has no options. the start map itself gives skill+episode options.\r\n\t\t//use 0 for maxplayers. this allows splitscreen to allocate more.\r\n\t\tlocalcmd(\"\\ndisconnect; deathmatch 0; coop 0; maxplayers 0; timelimit 0; fraglimit 0; teamplay 0; samelevel 0; startmap_sp\\n\");\r\n\t\treturn;\r\n\t}\r\n\tif (gametype == \"begin\")\r\n\t{\r\n\t\tcvar_set(\"hostname\", \t\tinfoget(newgameinfo, \"hostname\"));\r\n\t\tcvar_set(\"deathmatch\", \tinfoget(newgameinfo, \"deathmatch\"));\r\n\t\tcvar_set(\"coop\", \t\tinfoget(newgameinfo, \"coop\"));\r\n\t\tcvar_set(\"teamplay\", \t\tinfoget(newgameinfo, \"teamplay\"));\r\n\t\tcvar_set(\"sv_public\", \tinfoget(newgameinfo, \"sv_public\"));\r\n\t\tcvar_set(\"timelimit\", \tinfoget(newgameinfo, \"timelimit\"));\r\n\t\tcvar_set(\"fraglimit\", \tinfoget(newgameinfo, \"fraglimit\"));\r\n\t\tstring map = \t\t\tinfoget(newgameinfo, \"map\");\r\n\t\tif (map == \"\")\r\n\t\t{\r\n\t\t\tif (infoget(newgameinfo, \"deathmatch\"))\r\n\t\t\t\tmap = sprintf(\"dm%g\", floor(random(1, 6)));\r\n\t\t\telse\r\n\t\t\t\tmap = \"start\";\r\n\t\t}\r\n\r\n\t\tif (engine == E_QSS)\r\n\t\t{\r\n\t\t\tif (stof(infoget(newgameinfo, \"maxclients\")) != cvar(\"maxplayers\"))\r\n\t\t\t\tlocalcmd(sprintf(\"\\ndisconnect; maxplayers %S\\n\", infoget(newgameinfo, \"maxclients\")?:\"0\"));\r\n\t\t}\r\n\t\telse\r\n\t\t\tcvar_set(\"maxclients\", \tinfoget(newgameinfo, \"maxclients\"));\r\n\r\n\t\t//'map' acts slightly differently in different engines.\r\n\t\t//qss: kicks everyone, resets all parms\r\n\t\t//fte: just resets parms.\r\n\t\t//whereas 'changelevel' works more predictably, but does mean you might get more weapons than were really intended.\r\n\t\tif (engine == E_QSS && gametype == \"warp\")\r\n\t\t\tlocalcmd(sprintf(\"\\nchangelevel %S\\n\", map));\r\n\t\telse\r\n\t\t\tlocalcmd(sprintf(\"\\nmap %S\\n\", map));\r\n\t\treturn;\r\n\t}\r\n\r\n\tif (newgameinfo)\r\n\t\tstrunzone(newgameinfo);\r\n\tnewgameinfo = \"\";\r\n\tnewgameinfo = infoadd(newgameinfo, \"hostname\", cvar_string(\"hostname\"));\r\n\tnewgameinfo = infoadd(newgameinfo, \"deathmatch\", cvar_string(\"deathmatch\"));\r\n\tnewgameinfo = infoadd(newgameinfo, \"teamplay\", cvar_string(\"teamplay\"));\r\n\tnewgameinfo = infoadd(newgameinfo, \"sv_public\", cvar_string(\"sv_public\"));\r\n\tnewgameinfo = infoadd(newgameinfo, \"maxclients\", cvar_string((engine == E_QSS)?\"maxplayers\":\"maxclients\"));\r\n\tnewgameinfo = infoadd(newgameinfo, \"timelimit\", cvar_string(\"timelimit\"));\r\n\tnewgameinfo = infoadd(newgameinfo, \"fraglimit\", cvar_string(\"fraglimit\"));\r\n\tnewgameinfo = strzone(newgameinfo);\r\n\r\n\t//create the menu\r\n\tm = spawn(mitem_newgame, item_text:_(\"New Game\"), item_flags:IF_SELECTABLE, item_command:\"m_main\");\r\n\tdesktop.add(m, RS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MIN | RS_X_MAX_PARENT_MAX|RS_Y_MAX_PARENT_MAX, '0 0', '0 0');\r\n\tdesktop.item_focuschange(m, IF_KFOCUSED);\r\n\tm.totop();\r\n\r\n#if 1\r\n\tmitem_frame fr = m;\r\n#else\r\n\t//create a frame for its scrollbar.\r\n\tfloat h = 100;\r\n\tmitem_frame fr = spawn(mitem_frame, item_flags: IF_SELECTABLE, frame_hasscroll:TRUE);\r\n\tm.add(fr, RS_X_MIN_PARENT_MID|RS_Y_MIN_PARENT_MID | RS_X_MAX_PARENT_MID|RS_Y_MAX_OWN_MIN, [-160, -h], [160, h*2]);\r\n#endif\r\n\r\n\tswitch(gametype)\r\n\t{\r\n\tcase \"tdm\":\r\n\tcase \"dm\":\r\n\tcase \"coop\":\r\n\tcase \"sp\":\r\n\tcase \"warp\":\r\n\t\tbreak;\r\n\tdefault:\r\n\t\t//show game type selection\r\n\t\tpos = (16/-2)*(4);\r\n\t\tbanner = spawn(mitem_pic, item_text:\"gfx/p_option.lmp\", item_size_y:24, item_flags:IF_CENTERALIGN);\r\n\t\tm.addm(banner, [(160-banner.item_size_x)*0.5, pos-32], [(160+banner.item_size_x)*0.5, pos-8]);\r\n\t\tm.addm(spawn(mitem_text, item_text:\"Single Player\", item_command:\"m_pop;m_newgame sp\", item_scale:16, item_flags:IF_CENTERALIGN), [0, pos], [160, pos+16]);\tpos += 16;\r\n\t\tm.addm(spawn(mitem_text, item_text:\"Cooperative\", item_command:\"m_pop;m_newgame coop\", item_scale:16, item_flags:IF_CENTERALIGN), [0, pos], [160, pos+16]);\tpos += 16;\r\n\t\tm.addm(spawn(mitem_text, item_text:\"Deathmatch\", item_command:\"m_pop;m_newgame dm\", item_scale:16, item_flags:IF_CENTERALIGN), [0, pos], [160, pos+16]);\tpos += 16;\r\n\t\tm.addm(spawn(mitem_text, item_text:\"Team Deathmatch\", item_command:\"m_pop;m_newgame tdm\", item_scale:16, item_flags:IF_CENTERALIGN), [0, pos], [160, pos+16]);\tpos += 16;\r\n\r\n\t\tm.addm(spawn(mitem_text, item_text:\"Map Selection\", item_command:\"m_pop;m_newgame warp\", item_scale:16, item_flags:IF_CENTERALIGN), [0, pos], [160, pos+16]);\tpos += 16;\r\n\r\n\t\tm.add(spawn (mitem_spinnymodel, item_text: \"progs/soldier.mdl\",firstframe:73, framecount:8, shootframe:81, shootframes:9), RS_X_MIN_PARENT_MID|RS_Y_MIN_PARENT_MID | RS_X_MAX_PARENT_MID|RS_Y_MAX_PARENT_MID, [-160, 12*-16/2], [0, 12*16/2]);\r\n\t\treturn;\r\n\t}\r\n\t\r\n\tpos = -100;\r\n\r\n\tbanner = spawn(mitem_pic, item_text:\"gfx/p_multi.lmp\", item_size_y:24, item_flags:IF_CENTERALIGN);\r\n\tm.addm(banner, [(0-banner.item_size_x)*0.5, pos-32], [(0+banner.item_size_x)*0.5, pos-8]);\r\n\r\n\tif (gametype != \"warp\")\r\n\t{\r\n\t\tfr.addm(menuitemeditt_spawn(_(\"Hostname\"), \t\t\"hostname\", '280 8'), \t\t\t\t\t[-160, pos], [160, pos+8]); pos += 8;\r\n\t\tfr.addm(menuitemcombo_spawn(_(\"Public\"),\t\t\t\"sv_public\",\t\t\t\t'280 8', \t(engine == E_QSS)?_(\t\"0\t\\\"LAN\\\" \"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"1\t\\\"Internet\\\" \"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t):_(\t\"-1\t\\\"Reject All (Splitscreen)\\\" \"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"0\t\\\"Private (Manual IP Sharing)\\\" \"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"1\t\\\"Public (Manual Config)\\\" \"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"2\t\\\"Public (Holepunch)\\\" \"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t)),\t[-160, pos], [160, pos+8]); pos += 8;\r\n\t\tfr.addm(menuitemcombo_spawn(_(\"Max Clients\"),\t\"maxclients\",\t\t\t\t'280 8', _(\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"2\t\\\"Two\\\" \"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"3\t\\\"Three\\\" \"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"4\t\\\"Four\\\" \"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"8\t\\\"Eight\\\" \"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"16\t\\\"Sixteen\\\" \"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"32\t\\\"Thirty Two\\\" \"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t)),\t[-160, pos], [160, pos+8]); pos += 8;\r\n\t}\r\n\r\n\tif (gametype == \"dm\" || gametype == \"tdm\")\r\n\t{\r\n\t\tif (m.get(\"deathmatch\") == \"0\")\r\n\t\t{\r\n\t\t\tm.set(\"deathmatch\", \"1\");\r\n\t\t\tm.set(\"coop\", \"0\");\r\n\t\t}\r\n\t\tfr.addm(menuitemcombo_spawn(_(\"Deathmatch Mode\"),\t\"deathmatch\",\t\t\t\t\t'280 8', _(\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"1\t\\\"Weapons Respawn\\\" \"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"2\t\\\"Weapons Stay\\\" \"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"3\t\\\"Powerups Respawn\\\" \"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"4\t\\\"Start With Weapons\\\" \"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t)),\t[-160, pos], [160, pos+8]); pos += 8;\r\n\t}\r\n\telse\r\n\t{\r\n\t\tif (m.get(\"coop\") == \"0\")\r\n\t\t{\r\n\t\t\tm.set(\"deathmatch\", \"0\");\r\n\t\t\tm.set(\"coop\", \"1\");\r\n\t\t}\r\n\t}\r\n\tif (gametype == \"tdm\")\r\n\t{\r\n\t\tif (m.get(\"teamplay\") == \"0\")\r\n\t\t\tm.set(\"teamplay\", \"1\");\r\n\t}\r\n\tif (gametype == \"dm\")\r\n\t{\r\n\t\tif (m.get(\"teamplay\") != \"0\")\r\n\t\t\tm.set(\"teamplay\", \"0\");\r\n\t}\r\n\tif (gametype == \"coop\")\r\n\t\tfr.addm(menuitemcheck_spawn(_(\"No Friendly Fire\"),\t\t\"teamplay\", \t\t\t'280 8'),\t[-160, pos], [160, pos+8]), pos += 8;\r\n//\tif (gametype == \"dm\" || gametype == \"tdm\")\r\n\r\n\tif (gametype == \"coop\")\r\n\t\tm.set(\"map\", \"start\");\r\n\telse\r\n\t{\r\n\t\tm.set(\"map\", \"\");\r\n\r\n\t\tif (gametype != \"warp\")\r\n\t\t{\r\n\t\t\tfr.addm(menuitemcombo_spawn(_(\"Time Limit\"),\t\"timelimit\",\t\t\t\t\t'280 8', _(\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"0\t\\\"No Limit\\\" \"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"5\t\\\"5 minutes\\\" \"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"10\t\\\"10 minutes\\\" \"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"15\t\\\"15 minutes\\\" \"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"20\t\\\"20 minutes\\\" \"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"25\t\\\"25 minutes\\\" \"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"30\t\\\"30 minutes\\\" \"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"35\t\\\"35 minutes\\\" \"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"40\t\\\"40 minutes\\\" \"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"45\t\\\"45 minutes\\\" \"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"50\t\\\"50 minutes\\\" \"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"55\t\\\"55 minutes\\\" \"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"60\t\\\"1 hour\\\" \"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t)),\t[-160, pos], [160, pos+8]); pos += 8;\r\n\t\t\tfr.addm(menuitemcombo_spawn(_(\"Frag Limit\"),\t\"fraglimit\",\t\t\t\t\t'280 8', _(\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"0\t\\\"No Limit\\\" \"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"10\t\\\"10 frags\\\" \"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"20\t\\\"20 frags\\\" \"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"30\t\\\"30 frags\\\" \"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"40\t\\\"40 frags\\\" \"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"50\t\\\"50 frags\\\" \"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"60\t\\\"60 frags\\\" \"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"70\t\\\"70 frags\\\" \"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"80\t\\\"80 frags\\\" \"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"90\t\\\"90 frags\\\" \"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"100\t\\\"100 frags\\\" \"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t)),\t[-160, pos], [160, pos+8]); pos += 8;\r\n\t\t}\r\n\r\n\t\tpos += 8;\r\n\t\tfr.addm(spawn(mitem_maplist, item_scale:8, item_flags:IF_CENTERALIGN|IF_SELECTABLE), [-160, pos], [150, 100-16-8]), pos = 100-16;\r\n\t}\r\n\r\n\tfr.addm(spawn(mitem_text, item_text:\"BEGIN!\", item_command:\"m_pop;m_newgame begin\", item_scale:16, item_flags:IF_CENTERALIGN), [-160, pos], [160, pos+16]);pos += 16;\r\n\r\n\r\n\t//random art for style\r\n\tif (gametype == \"coop\")\r\n\t{\r\n\t\tm.add(spawn (mitem_spinnymodel, item_text: \"progs/soldier.mdl\", firstframe:73, framecount:8, shootframe:81, shootframes:9), RS_X_MIN_PARENT_MID|RS_Y_MIN_PARENT_MID | RS_X_MAX_PARENT_MID|RS_Y_MAX_PARENT_MID, [-160, -240/2], [0, 240/2]);\r\n\t}\r\n\telse\r\n\t{\r\n\t\tm.add(spawn (mitem_spinnymodel, item_text: \"progs/player.mdl\", firstframe:0, framecount:6, shootframe:119, shootframes:6), RS_X_MIN_PARENT_MID|RS_Y_MIN_PARENT_MID | RS_X_MAX_PARENT_MID|RS_Y_MAX_PARENT_MID, [-160, 12*-16/2], [0, 12*16/2]);\r\n\t}\r\n\r\n\taddmenuback(m);\r\n};\r\n"
  },
  {
    "path": "quakec/menusys/menu/options.qc",
    "content": "/***************************************************************************\r\nOptions menu.\r\njust a simple list.\r\n*/\r\nnonstatic void(mitem_desktop desktop) M_Options =\r\n{\r\n\tlocal float pos;\r\n\tmitem_exmenu m;\r\n\tm = spawn(mitem_exmenu, item_text:_(\"Options\"), item_flags:IF_SELECTABLE, item_command:\"m_main\");\r\n\tdesktop.add(m, RS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MIN | RS_X_MAX_PARENT_MAX|RS_Y_MAX_PARENT_MAX, '0 0', '0 0');\r\n\tdesktop.item_focuschange(m, IF_KFOCUSED);\r\n\tm.totop();\r\n\r\n/*\t//center the actual items\r\n\tpos = (16/-2)*(9);\r\n\r\n\t//draw title art above the options\r\n\tmitem_pic banner = spawn(mitem_pic, item_text:\"gfx/p_option.lmp\", item_size_y:24, item_flags:IF_CENTERALIGN);\r\n\tm.addm(banner, [(-160-banner.item_size_x)*0.5, pos-32], [(-160+banner.item_size_x)*0.5, pos-8]);\r\n*/\r\n\r\n\tfloat h = 200 * 0.5;\r\n\t//draw title art above the options\r\n\tmitem_pic banner = spawn(mitem_pic, item_text:\"gfx/p_option.lmp\", item_size_y:24, item_flags:IF_CENTERALIGN);\r\n\tm.add(banner, RS_X_MIN_PARENT_MID|RS_Y_MIN_PARENT_MID | RS_X_MAX_PARENT_MID|RS_Y_MAX_PARENT_MID, [-80-banner.item_size_x*0.5, -h-32], [-80+banner.item_size_x*0.5, -h-8]);\r\n\r\n\t//spawn a container frame for the actual options. this provides a scrollbar if we have too many items.\r\n\tmitem_frame fr = spawn(mitem_frame, item_flags: IF_SELECTABLE, frame_hasscroll:TRUE);\r\n\tm.add(fr, RS_X_MIN_PARENT_MID|RS_Y_MIN_PARENT_MID | RS_X_MAX_PARENT_MID|RS_Y_MAX_OWN_MIN, [-160-160, -h], [0+160, h*2]);\r\n\r\n\tfloat fl = RS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MIN | RS_X_MAX_PARENT_MAX|RS_Y_MAX_OWN_MIN;\r\n\tpos = 0;\r\n\r\n\r\n\r\n\t//and show the options.\r\n\tif (checkcommand2(\"fps_preset\", FALSE))\r\n\t\t{fr.add(spawn(mitem_text, item_text:\"Graphical Presets\",\titem_command:\"m_pop;m_preset\", \t\titem_scale:16, item_flags:IF_CENTERALIGN), fl, [0, pos], [0, 16]);\tpos += 16;}\r\n\tfr.add(spawn(mitem_text, item_text:\"Game Configs\", \t\t\titem_command:\"m_pop;m_configs\", \titem_scale:16, item_flags:IF_CENTERALIGN), fl, [0, pos], [0, 16]);\tpos += 16;\r\n\tfr.add(spawn(mitem_text, item_text:\"Basic Setup\", \t\t\titem_command:\"m_pop;m_basicopts\",\titem_scale:16, item_flags:IF_CENTERALIGN), fl, [0, pos], [0, 16]);\tpos += 16;\r\n\tfr.add(spawn(mitem_text, item_text:\"Keys\", \t\t\t\titem_command:\"m_pop;m_keys\", \t\titem_scale:16, item_flags:IF_CENTERALIGN), fl, [0, pos], [0, 16]);\tpos += 16;\r\n\tfr.add(spawn(mitem_text, item_text:\"Audio\", \t\t\t\titem_command:\"m_pop;m_audio\", \t\titem_scale:16, item_flags:IF_CENTERALIGN), fl, [0, pos], [0, 16]);\tpos += 16;\r\n\tfr.add(spawn(mitem_text, item_text:\"Video\", \t\t\t\titem_command:\"m_pop;m_video\", \t\titem_scale:16, item_flags:IF_CENTERALIGN), fl, [0, pos], [0, 16]);\tpos += 16;\r\n\tfr.add(spawn(mitem_text, item_text:\"Effects\", \t\t\t\titem_command:\"m_pop;m_effects\", \titem_scale:16, item_flags:IF_CENTERALIGN), fl, [0, pos], [0, 16]);\tpos += 16;\r\n\tif (checkcommand2(\"r_particledesc\", FALSE))\r\n\t\t{fr.add(spawn(mitem_text, item_text:\"Particles\",\t\titem_command:\"m_pop;m_particles\", \titem_scale:16, item_flags:IF_CENTERALIGN), fl, [0, pos], [0, 16]);\tpos += 16;}\r\n\tif (checkcommand2(\"ezhud_nquake\", FALSE))\r\n\t\t{fr.add(spawn(mitem_text, item_text:\"Hud\",\t\t\titem_command:\"m_pop;m_hud\", \t\titem_scale:16, item_flags:IF_CENTERALIGN), fl, [0, pos], [0, 16]);\tpos += 16;}\r\n\tif (checkbuiltin2(buf_cvarlist, TRUE))\r\n\t\t{fr.add(spawn(mitem_text, item_text:\"Advanced Guru Settings\",\titem_command:\"m_pop;m_cvars\", \t\titem_scale:16, item_flags:IF_CENTERALIGN), fl, [0, pos], [0, 16]);\tpos += 16;}\r\n\tif (checkcommand2(\"cvarreset\", FALSE) || checkcommand2(\"resetall\", FALSE))\r\n\t\t{fr.add(spawn(mitem_text, item_text:\"Reset to Defaults\", \titem_command:\"m_reset\", \t\titem_scale:16, item_flags:IF_CENTERALIGN), fl, [0, pos], [0, 16]);\tpos += 16;}\r\n\tif (checkcommand2(\"cfg_save\", FALSE))\r\n\t\t{fr.add(spawn(mitem_text, item_text:\"Save Settings\", \t\titem_command:\"cfg_save\", \t\titem_scale:16, item_flags:IF_CENTERALIGN), fl, [0, pos], [0, 16]);\tpos += 16;}\r\n\t\t\r\n\t//random art for style\r\n\tm.addm(spawn (mitem_spinnymodel, item_text: \"progs/suit.mdl\"), [0, 12*-16/2], [160, 12*16/2]);\r\n\taddmenuback(m);\r\n};\r\n\r\n\r\n\r\nstatic void(mitem_desktop desktop, string question, string affirmitive, string affirmitiveaction, string negative, string negativeaction) M_SimplePrompt =\r\n{\r\n\tlocal float pos;\r\n\tmitem_exmenu m;\r\n\tm = spawn(mitem_exmenu, item_text:_(\"Options\"), item_flags:IF_SELECTABLE, item_command:\"\");\r\n\tdesktop.add(m, RS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MIN | RS_X_MAX_PARENT_MAX|RS_Y_MAX_PARENT_MAX, '0 0', '0 0');\r\n\tdesktop.item_exclusive = m;\r\n\tdesktop.item_focuschange(m, IF_KFOCUSED);\r\n\tm.totop();\r\n\r\n\t//center the actual items\r\n\tpos = (16/-2)*(2);\r\n\r\n\t//draw title art above the options\r\n//\tmitem_pic banner = spawn(mitem_pic, item_text:\"gfx/p_option.lmp\", item_size_y:24, item_flags:IF_CENTERALIGN);\r\n//\tm.add(banner, RS_X_MIN_PARENT_MID|RS_Y_MIN_PARENT_MID | RS_X_MAX_PARENT_MID|RS_Y_MAX_PARENT_MID, [(-160-banner.item_size_x)*0.5, pos-32], [(-160+banner.item_size_x)*0.5, pos-8]);\r\n\r\n\tm.add(spawn(mitem_text, item_text:question, item_scale:16, item_flags:IF_CENTERALIGN), RS_X_MIN_PARENT_MID|RS_Y_MIN_PARENT_MID | RS_X_MAX_PARENT_MID|RS_Y_MAX_PARENT_MID, [-160, pos-(8+16)], [0, pos-8]);\tpos += 16;\r\n\r\n\tm.add(spawn(mitem_text, item_text:affirmitive, item_command:affirmitiveaction, item_scale:16, item_flags:IF_CENTERALIGN), RS_X_MIN_PARENT_MID|RS_Y_MIN_PARENT_MID | RS_X_MAX_PARENT_MID|RS_Y_MAX_PARENT_MID, [-160, pos], [0, pos+16]);\tpos += 16;\r\n\tm.add(spawn(mitem_text, item_text:negative, item_command:negativeaction, item_scale:16, item_flags:IF_CENTERALIGN), RS_X_MIN_PARENT_MID|RS_Y_MIN_PARENT_MID | RS_X_MAX_PARENT_MID|RS_Y_MAX_PARENT_MID, [-160, pos], [0, pos+16]);\tpos += 16;\r\n\r\n\t//random art for style\r\n\tm.add(spawn (mitem_spinnymodel, item_text: \"progs/suit.mdl\"), RS_X_MIN_PARENT_MID|RS_Y_MIN_PARENT_MID | RS_X_MAX_PARENT_MID|RS_Y_MAX_PARENT_MID, [0, 12*-16/2], [160, 12*16/2]);\r\n\taddmenuback(m);\r\n};\r\n\r\nnonstatic void(mitem_desktop desktop) M_Reset =\r\n{\r\n\tstring cmd = \"m_pop;cvarreset *;exec default.cfg\";\r\n\tif (!checkcommand2(\"cvarreset\", FALSE))\r\n\t\tcmd = \"m_pop;resetall;exec default.cfg\";\r\n\tM_SimplePrompt(desktop, \"Really Reset All Settings?\", \"Reset All\", cmd, \"Cancel\", \"m_pop\");\r\n};\r\n"
  },
  {
    "path": "quakec/menusys/menu/options_audio.qc",
    "content": "static class audiomenu : mitem_exmenu\r\n{\r\n\tvirtual float(string key) isvalid =\r\n\t{\r\n\t\tif (key == \"sndspeed\")\r\n\t\t\treturn super::isvalid(key) && stof(super::get(\"snd_mixspeed\"))==44100;\r\n\t\treturn super::isvalid(key);\r\n\t};\r\n\tvirtual string(string key) get =\r\n\t{\r\n\t\tif (key == \"s_device\" || key == \"cl_voip_capturedevice\")\r\n\t\t{\t//the cvar supports multiple options, but we only support one. :(\r\n\t\t\t//so just return the first to avoid getting too confused.\r\n\t\t\ttokenize(super::get(key));\r\n\t\t\treturn argv(0);\r\n\t\t}\r\n\t\treturn super::get(key);\r\n\t};\r\n\tvirtual void(string key, string val) set =\r\n\t{\r\n\t\tif (key == \"s_device\" || key == \"cl_voip_capturedevice\")\r\n\t\t{\t//add some quotes.\r\n\t\t\tval = strcat(\"\\\"\", val, \"\\\"\");\r\n\t\t}\r\n\t\tsuper::set(key, val);\r\n\t};\r\n};\r\n\r\nnonstatic void(mitem_desktop desktop) M_Options_Audio =\r\n{\r\n\tlocal float pos;\r\n\tmitem_exmenu m;\r\n\tm = spawn(audiomenu, item_text:_(\"Audio Options\"), item_flags:IF_SELECTABLE, item_command:\"m_options\");\r\n\tdesktop.add(m, RS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MIN | RS_X_MAX_PARENT_MAX|RS_Y_MAX_PARENT_MAX, '0 0', '0 0');\r\n\tdesktop.item_focuschange(m, IF_KFOCUSED);\r\n\tm.totop();\r\n\r\n\tfloat h = 200 * 0.5;\r\n\t//draw title art above the options\r\n\tmitem_pic banner = spawn(mitem_pic, item_text:\"gfx/p_option.lmp\", item_size_y:24, item_flags:IF_CENTERALIGN);\r\n\tm.add(banner, RS_X_MIN_PARENT_MID|RS_Y_MIN_PARENT_MID | RS_X_MAX_PARENT_MID|RS_Y_MAX_PARENT_MID, [banner.item_size_x*-0.5, -h-32], [banner.item_size_x*0.5, -h-8]);\r\n\t\r\n\t//spawn a container frame for the actual options. this provides a scrollbar if we have too many items.\r\n\tmitem_frame fr = spawn(mitem_frame, item_flags: IF_SELECTABLE, frame_hasscroll:TRUE);\r\n\tm.add(fr, RS_X_MIN_PARENT_MID|RS_Y_MIN_PARENT_MID | RS_X_MAX_PARENT_MID|RS_Y_MAX_OWN_MIN, [-160, -h], [160, h*2]);\r\n\t\r\n\tfloat fl = RS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MIN | RS_X_MAX_PARENT_MAX|RS_Y_MAX_OWN_MIN;\r\n\tpos = 0;\r\n\r\n\t//add the options\r\n\tfr.add(spawn(mitem_text, item_text:_(\"Restart Sound\"), \titem_command:\"snd_restart\", item_scale:8, item_flags:IF_RIGHTALIGN),\t\tRS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MIN | RS_X_MAX_PARENT_MID|RS_Y_MAX_OWN_MIN, [0, pos], [-8, 8]); pos += 8;\r\n\tpos += 8;\r\n\tif (engine!=E_QSS)\r\n\tfr.add(menuitemcombo_spawn(_(\"Sound Device\"),\t\t\"s_device\",\t\t\t\t\t\t\t'280 8', cvar_string(\"_s_device_opts\")),\tfl, [0, pos], [0, 8]); pos += 8;\r\n\tfr.add(menuitemslider_spawn(_(\"Master Volume\"),\t\t\"volume\",\t\t\t'0.0 1 0.1', \t'280 8'),\t\tfl, [0, pos], [0, 8]), pos += 8;\r\n\tfr.add(menuitemslider_spawn(_(\"Ambient Volume\"),\t\"ambient_level\",\t'0 0.5 0.05', \t'280 8'),\t\tfl, [0, pos], [0, 8]), pos += 8;\r\n\tif (engine!=E_QSS)\r\n\tfr.add(menuitemslider_spawn(_(\"Self Volume\"),\t\t\"s_localvolume\",\t'0 1 0.1', \t\t'280 8'),\t\tfl, [0, pos], [0, 8]), pos += 8;\r\n\tfr.add(menuitemslider_spawn(_(\"Music Volume\"),\t\t\"bgmvolume\",\t\t'0 0.5 0.05', \t'280 8'),\t\tfl, [0, pos], [0, 8]), pos += 8;\r\n\tif (engine!=E_QSS)\r\n\tfr.add(menuitemcombo_spawn(_(\"Channels\"),\t\t\t\"s_numspeakers\",\t\t\t\t\t'280 8', _(\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"1 \\\"Mono\\\" \"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"2 \\\"Stereo\\\" \"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"4 \\\"Quad\\\" \"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"6 \\\"Surround\\\" \"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t)),\t\tfl, [0, pos], [0, 8]), pos += 8;\r\n\tif (engine==E_QSS)\r\n\tfr.add(menuitemcombo_spawn(_(\"Audio Filtering\"),\t\"sndspeed\",\t'280 8', _(\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\\\"\\\"\t\\\"Off\\\" \"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"11025\t\\\"On\\\" \"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t)),\tfl, [0, pos], [0, 8]), pos += 8;\r\n\tfr.add(menuitemcombo_spawn(_(\"Audio Quality\"),\t\tcv2(\"snd_mixspeed\"/*qs*/, \"s_khz\"),\t'280 8', _(\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"8000\t\\\"8000hz (telephone quality)\\\" \"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"11025\t\\\"11025hz (vanilla quake)\\\" \"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"22050\t\\\"22050hz\\\" \"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"44100\t\\\"44100hz (cd quality)\\\" \"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"48000\t\\\"48000hz (dvd quality)\\\" \"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t//higher values are probably pointless when source data doesn't go that high, so not going to list them.\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t//\"96000\t\\\"96000hz (blu-ray quality)\\\" \"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t//\"192000\t\\\"192000hz (professional quality)\\\" \"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t)),\tfl, [0, pos], [0, 8]), pos += 8;\r\n\tif (engine!=E_QSS)\r\n\tfr.add(menuitemcheck_spawn(_(\"Doppler\"),\t\t\t\"s_doppler\", \t\t\t\t\t\t'280 8'),\t\tfl, [0, pos], [0, 8]), pos += 8;\r\n\tfr.add(menuitemcheck_spawn(_(\"8bit audio\"),\t\t\tcv2(\"s_loadas8bit\"/*fte*/, \"loadas8bit\"), \t\t\t\t\t'280 8'),\t\tfl, [0, pos], [0, 8]), pos += 8;\r\n\tif (engine!=E_QSS)\r\n\tfr.add(menuitemcheck_spawn(_(\"Swap Speakers\"),\t\t\"s_swapstereo\", \t\t\t\t\t'280 8'),\t\tfl, [0, pos], [0, 8]), pos += 8;\r\n\tfr.add(menuitemslider_spawn(_(\"Latency\"),\t\t\tcv2(\"s_mixahead\"/*fte*/, \"_snd_mixahead\"),\t'0.1 0.3 0.01',\t'280 8'),\t\tfl, [0, pos], [0, 8]), pos += 8;\r\n\tfr.add(menuitemcheck_spawn(_(\"Disable Sound\"),\t\t\"nosound\",\t\t\t\t\t\t\t'280 8'),\t\tfl, [0, pos], [0, 8]), pos += 8;\r\n\t//ambient fade\r\n\tif (engine!=E_QSS)\r\n\tfr.add(menuitemcheck_spawn(_(\"Static Sounds\"),\t\t\"cl_staticsounds\", \t\t\t\t\t'280 8'),\t\tfl, [0, pos], [0, 8]), pos += 8;\r\n\tif (engine!=E_QSS)\r\n\tfr.add(menuitemcheck_spawn(_(\"Mix in Background\"),\t\"s_inactive\",\t\t\t\t\t\t'280 8'),\t\tfl, [0, pos], [0, 8]), pos += 8;\r\n\r\n\tpos += 8;\r\n\tif (engine!=E_QSS)\r\n\tfr.add(menuitemcombo_spawn(_(\"Microphone Device\"),\t\"cl_voip_capturedevice\",\t\t\t'280 8', cvar_string(\"_cl_voip_capturedevice_opts\")),\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tfl, [0, pos], [0, 8]), pos += 8;\r\n\tfr.add(menuitemslider_spawn(_(\"VOIP Playback Vol\"),\t\"cl_voip_play\",\t\t'0 2 0.1',\t\t'280 8'),\t\tfl, [0, pos], [0, 8]), pos += 8;\r\n\tfr.add(menuitemcheck_spawn(_(\"VOIP Test\"),\t\t\t\"cl_voip_test\",\t\t\t\t\t\t'280 8'),\t\tfl, [0, pos], [0, 8]), pos += 8;\r\n\tfr.add(menuitemslider_spawn(_(\"VOIP Record Vol\"),\t\"cl_voip_micamp\",\t'0 4 0.1',\t\t'280 8'),\t\tfl, [0, pos], [0, 8]), pos += 8;\r\n\tfr.add(menuitemcombo_spawn(_(\"VOIP Mode\"),\t\t\t\"cl_voip_send\",\t\t\t\t\t\t'280 8', _(\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"0 \\\"Push-To-Talk\\\" 1 \"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\\\"Voice Activation\\\" \"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"2 \\\"Continuous\\\"\"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t)),\t\tfl, [0, pos], [0, 8]), pos += 8;\r\n\t//VAD threshhold\r\n\t//ducking\r\n\t//noise cancelation\r\n\tfr.add(menuitemcombo_spawn(_(\"VOIP Codec\"),\t\t\t\"cl_voip_codec\",\t\t\t'280 8',_(\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\\\"\\\"\t\\\"Auto\\\" \"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"0\t\\\"speex (narrow 11khz)\\\" \"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t//\"1\t\\\"raw (wasteful)\\\" \"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"2\t\\\"opus\\\" \"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"3\t\\\"speex (narrow 8khz)\\\" \"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"4\t\\\"speex (wide 16khz)\\\" \"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"5\t\\\"speex (ultrawide 32khz)\\\" \"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t)),\t\tfl, [0, pos], [0, 8]), pos += 8;\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\r\n\tfr.add(menuitemslider_spawn(_(\"Opus bitrate\"),\t\t\"cl_voip_bitrate\",\t'0.5 128 0.5','280 8'),\t\tfl, [0, pos], [0, 8]), pos += 8;\r\n\r\n\taddmenuback(m);\r\n};"
  },
  {
    "path": "quakec/menusys/menu/options_basic.qc",
    "content": "class mitem_playerpreview : mitem_spinnymodel\r\n{\r\n\tvirtual void(vector pos) item_draw =\r\n\t{\r\n\t\tif (checkbuiltin(setcustomskin))\r\n\t\t{\r\n\t\t\t//if you wanted to get more advanced, you could use q3 skins here.\r\n\t\t\tif (cvar(\"noskins\")==1)\r\n\t\t\t\tsetcustomskin(self, \"\", sprintf(\"q1upper \\\"%s\\\"\\nq1lower \\\"%s\\\"\\n\\n\", cvar_string(\"topcolor\"), cvar_string(\"bottomcolor\")));\r\n\t\t\telse if (cvar_string(\"cl_teamskin\") != \"\")\r\n\t\t\t\tsetcustomskin(self, \"\", sprintf(\"q1upper \\\"%s\\\"\\nq1lower \\\"%s\\\"\\nqwskin \\\"%s\\\"\\n\", cvar_string(\"topcolor\"), cvar_string(\"bottomcolor\"), cvar_string(\"cl_teamskin\")));\r\n\t\t\telse\r\n\t\t\t\tsetcustomskin(self, \"\", sprintf(\"q1upper \\\"%s\\\"\\nq1lower \\\"%s\\\"\\nqwskin \\\"%s\\\"\\n\", cvar_string(\"topcolor\"), cvar_string(\"bottomcolor\"), cvar_string(\"skin\")));\r\n\t\t}\r\n\t\r\n\t\tsuper::item_draw(pos);\r\n\t};\r\n};\r\n\r\nstatic string() skinopts =\r\n{\r\n\tstring opts = \"\";\r\n\tfloat s = search_begin(\"skins/*.*\", TRUE, TRUE);\r\n\tif (s < 0)\r\n\t\treturn opts;\r\n\tfloat n = search_getsize(s);\r\n\tfor (float i = 0; i < n; i++)\r\n\t{\r\n\t\tstring f = substring(search_getfilename(s, i), 6, -5);\r\n\r\n\t\t//urgh!\r\n\t\tif (strstrofs(f, \"_pants\") >= 0)\r\n\t\t\tcontinue;\r\n\t\tif (strstrofs(f, \"_shirt\") >= 0)\r\n\t\t\tcontinue;\r\n\t\tif (strstrofs(f, \"_bump\") >= 0)\r\n\t\t\tcontinue;\r\n\t\tif (strstrofs(f, \"_norm\") >= 0)\r\n\t\t\tcontinue;\r\n\t\tif (strstrofs(f, \"_luma\") >= 0)\r\n\t\t\tcontinue;\r\n\t\tif (strstrofs(f, \"_glow\") >= 0)\r\n\t\t\tcontinue;\r\n\t\tif (strstrofs(f, \"_gloss\") >= 0)\r\n\t\t\tcontinue;\r\n\r\n\t\topts = strcat(opts, \"\\\"\", f, \"\\\" \\\"\", f, \"\\\" \");\r\n\t}\r\n\treturn opts;\r\n};\r\n\r\nvar float autocvar_m_pitch = 0.022;\r\nclass options_basic : mitem_exmenu\r\n{\r\n\tvirtual float(string key) isvalid =\r\n\t{\r\n\t\tif (key == \"m_pitchsign\")\r\n\t\t\treturn TRUE;\r\n\t\tif (key == \"cl_run\")\r\n\t\t\treturn TRUE;\r\n\t\tif (engine == E_QSS)\r\n\t\t{\r\n\t\t\tif (key == \"topcolor\")\r\n\t\t\t\treturn super::isvalid(\"_cl_color\");\r\n\t\t\tif (key == \"bottomcolor\")\r\n\t\t\t\treturn super::isvalid(\"_cl_color\");\r\n\t\t}\r\n\t\treturn super::isvalid(key);\r\n\t};\r\n\tvirtual string(string key) get =\r\n\t{\r\n\t\tif (key == \"m_pitchsign\")\r\n\t\t\treturn (autocvar_m_pitch<0)?\"1\":\"0\";\r\n\t\tif (key == \"cl_run\")\r\n\t\t\treturn (stof(super::get(\"cl_forwardspeed\")) > 200)?\"1\":\"0\";\r\n\t\tif (engine == E_QSS)\r\n\t\t{\r\n\t\t\tif (key == \"topcolor\")\r\n\t\t\t\treturn ftos(floor(stof(super::get(\"_cl_color\"))/16));\r\n\t\t\tif (key == \"bottomcolor\")\r\n\t\t\t\treturn ftos(stof(super::get(\"_cl_color\"))&15);\r\n\t\t}\r\n\t\treturn super::get(key);\r\n\t};\r\n\tvirtual void(string key, string newval) set =\r\n\t{\r\n\t\tif (key == \"m_pitchsign\")\r\n\t\t{\r\n\t\t\tfloat invert;\r\n\t\t\tif (stof(newval))\r\n\t\t\t\tinvert = autocvar_m_pitch > 0;\r\n\t\t\telse\r\n\t\t\t\tinvert = autocvar_m_pitch < 0;\r\n\t\t\tif (invert)\r\n\t\t\t\tcvar_set(\"m_pitch\", ftos(-autocvar_m_pitch));\r\n\t\t}\r\n\t\telse if (key == \"cl_run\")\r\n\t\t{\r\n\t\t\tfloat setbackspeed = (super::get(\"cl_backspeed\") != \"\");\r\n\t\t\tif (stof(newval))\r\n\t\t\t{\r\n\t\t\t\tsuper::set(\"cl_forwardspeed\", \"400\");\r\n\t\t\t\tsuper::set(\"cl_sidespeed\", \"400\");\r\n\t\t\t\tif (setbackspeed)\r\n\t\t\t\t\tsuper::set(\"cl_backspeed\", \"400\");\r\n\t\t\t\tsuper::set(\"cl_movespeedkey\", \"0.5\");\t//makes +speed act like +walk\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\t//these are the defaults from winquake.\r\n\t\t\t\tsuper::set(\"cl_forwardspeed\", \"200\");\r\n\t\t\t\tsuper::set(\"cl_sidespeed\", \"350\");\r\n\t\t\t\tif (setbackspeed)\r\n\t\t\t\t\tsuper::set(\"cl_backspeed\", \"200\");\r\n\t\t\t\tsuper::set(\"cl_movespeedkey\", \"2.0\");\r\n\t\t\t}\r\n\t\t}\r\n\t\telse if (engine == E_QSS && (key == \"topcolor\" || key == \"bottomcolor\"))\r\n\t\t{\r\n\t\t\tfloat c = stof(super::get(\"_cl_color\"));\r\n\t\t\tif (key == \"topcolor\")\r\n\t\t\t\tc = (c&~0xf0)|(stof(newval)*16);\r\n\t\t\telse\r\n\t\t\t\tc = (c&~0x0f)|stof(newval);\r\n\t\t\tsuper::set(\"_cl_color\", ftos(c));\r\n\t\t}\r\n\t\telse\r\n\t\t\tsuper::set(key, newval);\r\n\t};\r\n};\r\nnonstatic void(mitem_desktop desktop) M_Options_Basic =\r\n{\r\n\tmitem_exmenu m;\r\n\tm = spawn(options_basic, item_text:_(\"Basic Options\"), item_flags:IF_SELECTABLE, item_command:\"m_options\");\r\n\tdesktop.add(m, RS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MIN | RS_X_MAX_PARENT_MAX|RS_Y_MAX_PARENT_MAX, '0 0', '0 0');\r\n\tdesktop.item_focuschange(m, IF_KFOCUSED);\r\n\tm.totop();\r\n\r\n\tfloat h = 200 * 0.5;\r\n\t//draw title art above the options\r\n\tmitem_pic banner = spawn(mitem_pic, item_text:\"gfx/ttl_cstm.lmp\", item_size_y:24, item_flags:IF_CENTERALIGN);\r\n\tm.add(banner, RS_X_MIN_PARENT_MID|RS_Y_MIN_PARENT_MID | RS_X_MAX_OWN_MIN|RS_Y_MAX_PARENT_MID, [(160-160-banner.item_size_x)*0.5, -h-32], [banner.item_size_x, -h-8]);\r\n\t//spawn a container frame for the actual options. this provides a scrollbar if we have too many items.\r\n\tmitem_frame fr = spawn(mitem_frame, item_flags: IF_SELECTABLE, frame_hasscroll:TRUE);\r\n\tm.add(fr, RS_X_MIN_PARENT_MID|RS_Y_MIN_PARENT_MID | RS_X_MAX_PARENT_MID|RS_Y_MAX_OWN_MIN, [-160, -h], [160, h*2]);\r\n\tfloat fl = RS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MIN | RS_X_MAX_PARENT_MAX|RS_Y_MAX_OWN_MIN;\r\n\tfloat pos = 0;\r\n\t\r\n\r\n\tfr.add(menuitemeditt_spawn(_(\"Player Name\"), \t\tcv2(\"_cl_name\"/*nq*/, \"name\"),\t\t\t\t\t'280 8'), \tfl, [0, pos], [0, 8]), pos += 8;\r\n\tif (engine!=E_QSS)\r\n\tfr.add(menuitemeditt_spawn(_(\"Player Team\"), \t\t\"team\",\t\t\t\t\t\t\t\t\t\t\t'280 8'), \tfl, [0, pos], [0, 8]), pos += 8;\r\n\tif (engine!=E_QSS)\r\n\tfr.add(menuitemcombo_spawn(_(\"Player Skin\"), \t\t\"skin\",\t\t\t\t\t\t\t\t\t\t\t'280 8', \tskinopts()), fl, [0, pos], [0, 8]), pos += 8;\r\n\r\n\r\n\tfr.add(menuitemcolour_spawn(_(\"Upper Colour\"),\t\t\"topcolor\",\t\t\t\t\t\tengine==E_QSS,\t'280 8'),\tfl, [0, pos], [0, 8]), pos += 8;\r\n\tfr.add(menuitemcolour_spawn(_(\"Team Colour\"),\t\t\"bottomcolor\",\t\t\t\t\tengine==E_QSS,\t'280 8'),\tfl, [0, pos], [0, 8]), pos += 8;\t/*aka: arse colour, used for .team field in nq*/\r\n\tpos += 8;\r\n\tfr.add(menuitemcheck_spawn (_(\"Always Run\"),\t\t\"cl_run\",\t\t\t\t\t\t\t\t\t\t'280 8'),\tfl, [0, pos], [0, 8]), pos += 8;\r\n\tfr.add(menuitemcheck_spawn (_(\"Invert Mouse\"),\t\t\"m_pitchsign\",\t\t\t\t\t\t\t\t\t'280 8'),\tfl, [0, pos], [0, 8]), pos += 8;\r\n\tfr.add(menuitemslider_spawn(_(\"Sensitivity\"),\t\t\"sensitivity\",\t\t\t\t\t'3 20 1',\t\t'280 8'),\tfl, [0, pos], [0, 8]), pos += 8;\r\n\tfr.add(menuitemslider_spawn(_(\"Fov\"),\t\t\t\t\"fov\",\t\t\t\t\t\t\t'80 130 5',\t\t'280 8'),\tfl, [0, pos], [0, 8]), pos += 8;\r\n\tif (engine!=E_QSS)\r\n\tfr.add(menuitemslider_spawn(_(\"Viewmodel Fov\"),\t\t\"r_viewmodel_fov\",\t\t\t\t'80 130 5',\t\t'280 8'),\tfl, [0, pos], [0, 8]), pos += 8;\r\n\tfr.add(menuitemslider_spawn(_(\"Gamma\"),\t\t\t\tcv2(\"v_gamma\", \"gamma\"),\t\t'0.4 1.3 0.1',\t'280 8'),\tfl, [0, pos], [0, 8]), pos += 8;\r\n\tfr.add(menuitemslider_spawn(_(\"Contrast\"),\t\t\tcv2(\"v_contrast\", \"contrast\"),\t'0.8 1.8 0.1',\t'280 8'),\tfl, [0, pos], [0, 8]), pos += 8;\r\n\tif (engine!=E_QSS)\r\n\tfr.add(menuitemslider_spawn(_(\"Brightness\"),\t\tcv2(\"v_brightness\", \"brightness\"),'0.0 0.5 0.1','280 8'),\tfl, [0, pos], [0, 8]), pos += 8;\r\n\tif (engine==E_FTE)\r\n\t\tfr.add(menuitemslider_spawn(_(\"Crosshair\"),\t\t\"crosshair\",\t\t\t\t\t'0.0 19 1',\t\t'280 8'),\tfl, [0, pos], [0, 8]), pos += 8;\r\n\telse\r\n\t\tfr.add(menuitemcheck_spawn(_(\"Crosshair\"),\t\t\"crosshair\",\t\t\t\t\t\t\t\t\t'280 8'),\tfl, [0, pos], [0, 8]), pos += 8;\r\n\r\n\tm.add(spawn (mitem_playerpreview, item_text: \"progs/player.mdl\", firstframe:0, framecount:6, shootframe:119, shootframes:6), RS_X_MIN_PARENT_MID|RS_Y_MIN_PARENT_MID | RS_X_MAX_PARENT_MID|RS_Y_MAX_PARENT_MID, [-200, 12*-16/2], [-40, 12*16/2]);\r\n\r\n\taddmenuback(m);\r\n};\r\n"
  },
  {
    "path": "quakec/menusys/menu/options_configs.qc",
    "content": "/***************************************************************************\r\nUses the engine command to apply the preset, and tries an exec instead if a config file exists.\r\nDoesn't track the current one or anything.\r\nReally simple and stupid menu.\r\nno background tint, so the game is still visible so you can preview it.\r\n*/\r\n\r\nnonstatic void(mitem_desktop desktop) M_Configs =\r\n{\r\n\tfloat i;\r\n\tmitem_exmenu m;\r\n\tm = spawn(mitem_exmenu, item_text:_(\"Game Presets / Configs\"), item_flags:IF_SELECTABLE, item_command:\"m_options\");\r\n\tdesktop.add(m, RS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MIN | RS_X_MAX_PARENT_MAX|RS_Y_MAX_PARENT_MAX, '0 0', '0 0');\r\n\tdesktop.item_focuschange(m, IF_KFOCUSED);\r\n\tm.totop();\r\n\r\n\r\n\tfloat h = 200*0.5;\r\n\t//draw title art above the options\r\n\tmitem_pic banner = spawn(mitem_pic, item_text:\"gfx/p_option.lmp\", item_size_y:24, item_flags:IF_CENTERALIGN);\r\n\tm.add(banner, RS_X_MIN_PARENT_MID|RS_Y_MIN_PARENT_MID | RS_X_MAX_OWN_MIN|RS_Y_MAX_PARENT_MID, [(160-60-banner.item_size_x)*0.5, -h-32], [banner.item_size_x, -h-8]);\r\n\t\r\n\t//spawn a container frame for the actual options. this provides a scrollbar if we have too many items.\r\n\tmitem_frame fr = spawn(mitem_frame, item_flags: IF_SELECTABLE, frame_hasscroll:TRUE);\r\n\tm.add(fr, RS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MID | RS_X_MAX_PARENT_MAX|RS_Y_MAX_OWN_MIN, [0, -h], [0, h*2]);\r\n\t\r\n\r\n\r\n\r\n\tfloat fs, y=0;\r\n\tfloat c = 0;\r\n\tfs = search_begin(\"configs/game_*.cfg\", TRUE, TRUE);\t\r\n\tif (fs >= 0)\r\n\t{\r\n\t\tc = search_getsize(fs);\r\n\t\tfor (i = 0; i < c; i++)\r\n\t\t{\r\n\t\t\tstring fname = search_getfilename(fs, i);\r\n\t\t\tstring iname = strzone(substring(fname, 13, -5));\r\n\t\t\tstring dname = GetFirstLineComment(fname, iname);\r\n\t\t\tiname = sprintf(\"exec \\\"%s\\\"\", fname);\r\n\t\t\tif (dname && !fr.findchildcmd(iname))\r\n\t\t\t\tfr.add(spawn(mitem_text, item_text:dname, item_command:iname, item_scale:16, item_flags:IF_CENTERALIGN), RS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MIN | RS_X_MAX_PARENT_MAX|RS_Y_MAX_OWN_MIN, [0, y+=16], '100 16');\r\n\t\t}\r\n\t\tsearch_end(fs);\t\r\n\t}\r\n\tif (c <= 0)\r\n\t\tfr.add(spawn(mitem_text, item_text:\"No configs found\", item_scale:16, item_flags:IF_CENTERALIGN), RS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MIN | RS_X_MAX_PARENT_MAX|RS_Y_MAX_OWN_MIN, [0, y], '100 16');\r\n\r\n\t//random art for style\r\n\tm.addm(spawn (mitem_spinnymodel, item_text: \"progs/g_rock2.mdl\", zbias:-16), [-160-60, 12*-16/2], [-60, 12*16/2]);\r\n\r\n\taddmenuback(m);\r\n};\r\n\r\n"
  },
  {
    "path": "quakec/menusys/menu/options_effects.qc",
    "content": "class options_effects : mitem_exmenu\r\n{\r\n//\tvirtual float(string key) isvalid = {};\r\n\tvirtual string(string key) get =\r\n\t{\r\n\t\tif (key == \"r_replacemodels\")\r\n\t\t\treturn super::get(key)!=\"\"?\"1\":\"0\";\t//make it a boolean\r\n\t\treturn super::get(key);\r\n\t};\r\n\tvirtual void(string key, string newval) set =\r\n\t{\r\n\t\tif (key == \"r_replacemodels\")\t//convert from boolean to some arbitrary list.\r\n\t\t\tnewval = stof(newval)?\"iqm md5mesh md3\":\"\";\r\n\t\tsuper::set(key, newval);\r\n\t};\r\n};\r\n\r\nnonstatic void(mitem_desktop desktop) M_Options_Effects =\r\n{\r\n\tmitem_exmenu m;\r\n\tm = spawn(options_effects, item_text:_(\"Effects Options\"), item_flags:IF_SELECTABLE, item_command:\"m_options\");\r\n\tdesktop.add(m, RS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MIN | RS_X_MAX_PARENT_MAX|RS_Y_MAX_PARENT_MAX, '0 0', '0 0');\r\n\tdesktop.item_focuschange(m, IF_KFOCUSED);\r\n\tm.totop();\r\n\r\n\tfloat h = 200 * 0.5;\r\n\t//draw title art above the options\r\n\tmitem_pic banner = spawn(mitem_pic, item_text:\"gfx/p_option.lmp\", item_size_y:24, item_flags:IF_CENTERALIGN);\r\n\tm.add(banner, RS_X_MIN_PARENT_MID|RS_Y_MIN_PARENT_MID | RS_X_MAX_OWN_MIN|RS_Y_MAX_PARENT_MID, [(160-160-banner.item_size_x)*0.5, -h-32], [banner.item_size_x, -h-8]);\r\n\t//spawn a container frame for the actual options. this provides a scrollbar if we have too many items.\r\n\tmitem_frame fr = spawn(mitem_frame, item_flags: IF_SELECTABLE, frame_hasscroll:TRUE);\r\n\tm.add(fr, RS_X_MIN_PARENT_MID|RS_Y_MIN_PARENT_MID | RS_X_MAX_PARENT_MID|RS_Y_MAX_OWN_MIN, [-160, -h], [160, h*2]);\r\n\tfloat fl = RS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MIN | RS_X_MAX_PARENT_MAX|RS_Y_MAX_OWN_MIN;\r\n\tfloat pos = 0;\r\n\r\n\tfr.add(menuitemcheck_spawn(_(\"Show Framerate\"),\t\t\tcv3(\"showfps\", \"scr_showfps\", \"show_fps\"), \t\t'280 8'),\tfl, [0, pos], [0, 8]); pos += 8;\r\n\tfr.add(menuitemcheck_spawn(_(\"High Res Textures\"),\t\t\"gl_load24bit\", \t\t\t'280 8'),\tfl, [0, pos], [0, 8]); pos += 8;\r\n\tfr.add(menuitemcombo_spawn(_(\"Replacement Models\"),\t\t\"r_replacemodels\",\t\t\t'280 8', _(\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\\\"\\\" \\\"Off\\\"\"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\\\"md3 md2 md5mesh\\\" \\\"On\\\"\"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t)),\tfl, [0, pos], [0, 8]); pos += 8;\r\n\r\n\tfr.add(menuitemcombo_spawn(_(\"Texture Mode\"),\t\t\"gl_texturemode\",\t\t\t'280 8', _(\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"GL_NEAREST\t\\\"Nearest\\\" \"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"GL_NEAREST_MIPMAP_NEAREST\t\\\"Nearest (Nearest mips)\\\" \"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"GL_NEAREST_MIPMAP_LINEAR\t\\\"Nearest (Linear mips)\\\" \"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"GL_LINEAR\t\\\"Linear\\\" \"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"GL_LINEAR_MIPMAP_NEAREST\t\\\"Linear (Nearest mips)\\\" \"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"GL_LINEAR_MIPMAP_LINEAR\t\\\"Linear (Linear mips)\\\" \"\r\n\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"n.l\t\\\"Nearest (Smooth)\\\" \"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t//\"l.n\t\\\"Linear (Unsmooth)\\\" \"\r\n\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t//\"nnl\t\\\"Nearest (Nearest mips, Smooth)\\\" \"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t//\"lnn\t\\\"Linear (Nearest mips, Unsmooth)\\\" \"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"nll\t\\\"Nearest (Smooth mips)\\\" \"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t//\"lln\t\\\"Linear (Linear mips, Unsmooth)\\\" \"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t)),\tfl, [0, pos], [0, 8]); pos += 8;\r\n\r\n\tfr.add(menuitemcombo_spawn(_(\"Anistrophy\"),\t\tcv2(\"gl_texture_anisotropy\"/*qs*/, \"gl_texture_anisotropic_filtering\"/*old fte*/),\t\t\t\t'280 8', _(\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"0\t\\\"Off\\\" \"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"2\t\\\"2\\\" \"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"4\t\\\"4\\\" \"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"8\t\\\"8\\\" \"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"16\t\\\"16\\\" \"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t)),\tfl, [0, pos], [0, 8]); pos += 8;\r\n\r\n\tfr.add(menuitemcheck_spawn(_(\"Replacement Models\"),\t\t\"r_replacemodels\",\t\t\t'280 8'),\tfl, [0, pos], [0, 8]); pos += 8;\r\n\tfr.add(menuitemcheck_spawn(_(\"Double-bright Models\"),\t\"gl_overbright_models\", \t'280 8'),\tfl, [0, pos], [0, 8]); pos += 8;\r\n\tfr.add(menuitemcheck_spawn(_(\"Lightmaps Only\"),\t\t\t\"r_lightmap\", \t\t\t\t'280 8'),\tfl, [0, pos], [0, 8]); pos += 8;\r\n\tif (engine == E_QSS)\r\n\t{\r\n\t\tfr.add(menuitemcheck_spawn(_(\"Wireframe\"),\t\t\t\"r_showtris\", \t\t\t\t'280 8'),\tfl, [0, pos], [0, 8]); pos += 8;\r\n\t\tfr.add(menuitemcheck_spawn(_(\"Simple DLights\"),\t\t\"gl_flashblend\", \t\t\t'280 8'),\tfl, [0, pos], [0, 8]); pos += 8;\r\n\r\n\t\tfr.add(menuitemslider_spawn(_(\"Stereo\"),\t\t\t\"r_stereo\",\t'0 8 0.25',\t\t'280 8'),\tfl, [0, pos], [0, 8]); pos += 8;\r\n\t}\r\n\telse\r\n\t{\r\n\t\tfr.add(menuitemcheck_spawn(_(\"Wireframe\"),\t\t\t\"r_wireframe\", \t\t\t\t'280 8'),\tfl, [0, pos], [0, 8]); pos += 8;\r\n\t\tfr.add(menuitemcheck_spawn(_(\"Bloom\"),\t\t\t\t\"r_bloom\",\t\t\t\t\t'280 8'),\tfl, [0, pos], [0, 8]); pos += 8;\r\n\t\tfr.add(menuitemcheck_spawn(_(\"Simple Textures\"),\t\"r_drawflat\", \t\t\t\t'280 8'),\tfl, [0, pos], [0, 8]); pos += 8;\r\n\t\tfr.add(menuitemcheck_spawn(_(\"Paletted Rendering\"),\t\"r_softwarebanding\", \t\t'280 8'),\tfl, [0, pos], [0, 8]); pos += 8;\r\n\t\tfr.add(menuitemcheck_spawn(_(\"HDR\"),\t\t\t\t\"r_hdr_irisadaptation\", \t'280 8'),\tfl, [0, pos], [0, 8]); pos += 8;\r\n\t\tfr.add(menuitemcheck_spawn(_(\"Coronas\"),\t\t\t\"r_coronas\", \t\t\t\t'280 8'),\tfl, [0, pos], [0, 8]); pos += 8;\r\n\t\tfr.add(menuitemcheck_spawn(_(\"Relief Mapping\"),\t\t\"r_glsl_offsetmapping\", \t'280 8'),\tfl, [0, pos], [0, 8]); pos += 8;\r\n\t\tfr.add(menuitemcheck_spawn(_(\"RT Dynamic Lights\"),\t\"r_shadow_realtime_dlight\", '280 8'),\tfl, [0, pos], [0, 8]); pos += 8;\r\n\t\tfr.add(menuitemcheck_spawn(_(\"RT World Lighting\"),\t\"r_shadow_realtime_world\", \t'280 8'),\tfl, [0, pos], [0, 8]); pos += 8;\r\n\t\tfr.add(menuitemcheck_spawn(_(\"Raytraced Shadows\"),\t\"r_shadow_raytrace\",\t\t'280 8'),\tfl, [0, pos], [0, 8]); pos += 8;\r\n\t\tfr.add(menuitemcheck_spawn(_(\"Half-Rate Shading\"),\t\"r_halfrate\",\t\t\t\t'280 8'),\tfl, [0, pos], [0, 8]); pos += 8;\r\n\r\n\t\tfr.add(menuitemcombo_spawn(_(\"Water Effects\"),\t\t\t\"r_waterstyle\",\t\t\t'280 8', _(\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"1\t\\\"Classic\\\" \"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"2\t\\\"Ripples\\\" \"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"3\t\\\"Reflections\\\" \"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t)),\tfl, [0, pos], [0, 8]); pos += 8;\r\n\t\tfr.add(menuitemcombo_spawn(_(\"View Projection\"),\t\t\"r_projection\",\t\t\t'280 8', _(\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"0\t\\\"Standard\\\" \"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"1\t\\\"Stereographic\\\" \"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"2\t\\\"Fish-Eye\\\" \"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"3\t\\\"Panoramic\\\" \"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"4\t\\\"Lambert Azimuthal Equal-Area\\\" \"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"5\t\\\"Equirectangular\\\" \"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"6\t\\\"Panini\\\" \"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t)),\tfl, [0, pos], [0, 8]); pos += 8;\r\n\t\tfr.add(menuitemcombo_spawn(_(\"View Projection Fov\"),\t\t\"ffov\",\t\t\t\t'280 8', _(\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"90\t\\\"Normal\\\" \"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"180\t\\\"180 degrees\\\" \"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"270\t\\\"270 degrees\\\" \"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"360\t\\\"360 degrees\\\" \"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t)),\tfl, [0, pos], [0, 8]); pos += 8;\r\n\t}\r\n\tfr.add(menuitemslider_spawn(_(\"Particle Density\"),\t\t\"r_part_density\",\t'0.25 4 0.25',\t'280 8'),\tfl, [0, pos], [0, 8]); pos += 8;\r\n\r\n\tfr.add(spawn(mitem_text, item_text:_(\"Apply\"), \titem_command:\"vid_restart\", item_scale:8, item_flags:IF_RIGHTALIGN),\tRS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MIN | RS_X_MAX_PARENT_MID|RS_Y_MAX_OWN_MIN, [0, pos], [-8, 8]); pos += 8;\r\n\r\n\taddmenuback(m);\r\n};\r\n\r\n"
  },
  {
    "path": "quakec/menusys/menu/options_hud.qc",
    "content": "nonstatic void(mitem_desktop desktop) M_Options_Hud =\r\n{\r\n\tlocal float i;\r\n\tlocal float h;\r\n\tmitem_exmenu m;\r\n\tm = spawn(mitem_exmenu, item_text:_(\"Hud Configurations\"), item_flags:IF_SELECTABLE, item_command:\"m_options\");\r\n\tdesktop.add(m, RS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MIN | RS_X_MAX_PARENT_MAX|RS_Y_MAX_PARENT_MAX, '0 0', '0 0');\r\n\tdesktop.item_focuschange(m, IF_KFOCUSED);\r\n\tm.totop();\r\n\r\n\th = 200;\r\n\th *= 0.5;\t//and halve it\r\n\r\n\t//draw title art above the options\r\n\tmitem_pic banner = spawn(mitem_pic, item_text:\"gfx/p_option.lmp\", item_size_y:24, item_flags:IF_CENTERALIGN);\r\n\tm.add(banner, RS_X_MIN_PARENT_MID|RS_Y_MIN_PARENT_MID | RS_X_MAX_PARENT_MID|RS_Y_MAX_PARENT_MID, [banner.item_size_x*-0.5, -h-32], [banner.item_size_x*0.5, -h-8]);\r\n\t\r\n\t//spawn a container frame for the actual options. this provides a scrollbar if we have too many items.\r\n\tmitem_frame fr = spawn(mitem_frame, item_flags: IF_SELECTABLE, frame_hasscroll:TRUE);\r\n\tm.add(fr, RS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MID | RS_X_MAX_PARENT_MAX|RS_Y_MAX_OWN_MIN, [0, -h+16], [0, h*2+32]);\r\n\r\n\tfloat fs = search_begin(\"huds/*.cfg\", TRUE, TRUE);\r\n\tfloat fc = search_getsize(fs);\r\n\r\n\tfloat y = 0;\r\n\tstring fname = \"huds/hud_default.cfg\";\r\n\tstring iname = \"Default\";\r\n\tstring dname = iname;\r\n\tif ((int)whichpack(fname))\r\n\t{\r\n\t\tdname = GetFirstLineComment(fname, iname);\r\n\t\tfr.add(spawn(mitem_text, item_text:dname, item_command:sprintf(\"set plug_sbar 3; cvarreset hud_*; exec \\\"%s\\\"; hud_recalculate\", fname), item_scale:16, item_flags:IF_CENTERALIGN), RS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MIN | RS_X_MAX_PARENT_MAX|RS_Y_MAX_OWN_MIN, [0, y], [0, 16]);\r\n\t}\r\n\telse\r\n\t\tfr.add(spawn(mitem_text, item_text:dname, item_command:\"cvarreset hud_*; set plug_sbar 0\", item_scale:16, item_flags:IF_CENTERALIGN), RS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MIN | RS_X_MAX_PARENT_MAX|RS_Y_MAX_OWN_MIN, [0, y], [0, 16]);\r\n\ty += 16;\r\n\r\n\t//add the options\r\n\tfor (i = 0; i < fc; i++)\r\n\t{\r\n\t\tfname = search_getfilename(fs, i);\r\n\t\tiname = substring(fname, 5, -5);\r\n\t\tif (iname == \"hud_default\")\r\n\t\t\tcontinue;\r\n\t\tif (substring(fname, 0, 4) == \"hud_\")\r\n\t\t\tiname = substring(fname, 4, -1);\r\n\t\tdname = GetFirstLineComment(fname, iname);\r\n//\t\tprint(sprintf(\"\\\"%s\\\" \\\"%s\\\" - \\\"%s\\\"\\n\", fname, iname, dname));\r\n\t\tif (dname)\r\n\t\t{\r\n\t\t\tfr.add(spawn(mitem_text, item_text:dname, item_command:sprintf(\"set plug_sbar 3; cvarreset hud_*; exec \\\"%s\\\"; hud_recalculate\", fname), item_scale:16, item_flags:IF_CENTERALIGN), RS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MIN | RS_X_MAX_PARENT_MAX|RS_Y_MAX_OWN_MIN, [0, y], [0, 16]);\r\n\t\t\ty += 16;\r\n\t\t}\r\n\t}\r\n\tsearch_end(fs);\r\n\r\n\r\n\t//random art for style\r\n\tm.addm(spawn (mitem_spinnymodel, item_text: \"progs/g_nail2.mdl\", zbias:-16), [-160-60, 12*-16/2], [-60, 12*16/2]);\r\n\r\n\taddmenuback(m);\r\n};\r\n"
  },
  {
    "path": "quakec/menusys/menu/options_keys.qc",
    "content": "const static struct\r\n{\r\n\tstring name;\r\n\tstring cmd;\r\n} binds[] =\r\n{\r\n\t{_(\"Forwards\"), \t\t\"+forward\"},\r\n\t{_(\"Back\"),\t \t\t\"+back\"},\r\n\t{_(\"Move Left\"),\t\t\"+moveleft\"},\r\n\t{_(\"Move Right\"),\t\t\"+moveright\"},\r\n\t{_(\"Turn Left\"),\t\t\"+left\"},\r\n\t{_(\"Turn Right\"),\t\t\"+right\"},\r\n\t{_(\"Look Up\"),\t\t\t\"+lookup\"},\r\n\t{_(\"Look Down\"),\t\t\"+lookdown\"},\r\n\t{_(\"Swim Up\"),\t\t\t\"+moveup\"},\r\n\t{_(\"Swim Down\"),\t\t\"+movedown\"},\r\n\t{_(\"Center view\"),\t\t\"centerview\"},\r\n\t{_(\"Jump\"),\t \t\t\"+jump\"},\r\n\t{_(\"Attack\"),\t \t\t\"+attack\"},\r\n\t{_(\"Next Weapon\"),\t\t\"impulse 10\"},\r\n\t{_(\"Prev Weapon\"),\t\t\"impulse 12\"},\r\n\t{_(\"Scores\"),\t\t\t\"+showscores\"},\r\n\t{_(\"Server Chat\"),\t\t\"messagemode\"},\r\n\t{_(\"Team Chat\"),\t\t\"messagemode2\"},\r\n\t{_(\"Voice Chat\"),\t\t\"+voip\"},\r\n//\t{_(\"Mouse Look\"),\t\t\"+mlook\"},\r\n\t{_(\"Keyboard Look\"),\t\t\"+klook\"},\r\n\t{_(\"Strafe\"),\t\t\t\"+strafe\"},\r\n\t{_(\"Run\"),\t\t\t\"+speed\"},\r\n\t{0,\t\t\t\t0},\r\n\t{_(\"Axe\"),\t\t\t\"impulse 1\"},\r\n\t{_(\"Shotgun\"),\t\t\t\"impulse 2\"},\r\n\t{_(\"Super Shotgun\"),\t\t\"impulse 3\"},\r\n\t{_(\"Nailgun\"),\t\t\t\"impulse 4\"},\r\n\t{_(\"Super Nailgun\"),\t\t\"impulse 5\"},\r\n\t{_(\"Grenade Launcher\"),\t\t\"impulse 6\"},\r\n\t{_(\"Rocket Launcher\"),\t\t\"impulse 7\"},\r\n\t{_(\"Lightning Gun\"),\t\t\"impulse 8\"},\r\n//\t{_(\"Railgun\"),\t\t\t\"impulse 9\"},\r\n\r\n\t{0,\t\t\t\t0},\r\n\t{_(\"Ready Up\"),\t\t\t\"ready\"},\r\n\t{_(\"Break Match\"),\t\t\"break\"},\r\n};\r\nvoid(mitem_desktop desktop) M_Options_Keys =\r\n{\r\n\tfloat i;\r\n\tfloat h;\r\n\r\n\t//create the menu, give it focus, and make sure its displayed over everything else.\r\n\tmitem_exmenu m = spawn(mitem_exmenu, item_text:_(\"Key Options\"), item_flags:IF_SELECTABLE, item_command:\"m_options\");\r\n\tdesktop.add(m, RS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MIN | RS_X_MAX_PARENT_MAX|RS_Y_MAX_PARENT_MAX, '0 0', '0 0');\r\n\tdesktop.item_focuschange(m, IF_KFOCUSED);\r\n\tm.totop();\r\n\r\n\t//figure out the size of the stuff\r\n//\th = sizeof(binds) / sizeof(binds[0]);\r\n//\th *= 8;\r\n\th = 200;\r\n\th *= 0.5;\t//and halve it\r\n\r\n\t//draw title art above the options\r\n\tmitem_pic banner = spawn(mitem_pic, item_text:\"gfx/ttl_cstm.lmp\", item_size_y:24, item_flags:IF_CENTERALIGN);\r\n\tm.add(banner, RS_X_MIN_PARENT_MID|RS_Y_MIN_PARENT_MID | RS_X_MAX_PARENT_MID|RS_Y_MAX_PARENT_MID, [banner.item_size_x*-0.5, -h-32], [banner.item_size_x*0.5, -h-8]);\r\n\r\n\t//spawn a container frame for the actual options. this provides a scrollbar if we have too many items.\r\n\tmitem_frame fr = spawn(mitem_frame, item_flags: IF_SELECTABLE, frame_hasscroll:TRUE);\r\n\tm.add(fr, RS_X_MIN_PARENT_MID|RS_Y_MIN_PARENT_MID | RS_X_MAX_OWN_MIN|RS_Y_MAX_OWN_MIN, [-140, -h], [280, h*2]);\r\n\r\n\tfloat f = fopen(\"bindlist.lst\", FILE_READ);\r\n\tif (f >= 0)\r\n\t{\r\n\t\t//throw a load of bind options onto it by reading from the array.\r\n\t\tfor (i = 0; ; )\r\n\t\t{\r\n\t\t\tstring line = fgets(f);\r\n\t\t\tif not (line)\r\n\t\t\t\tbreak;\t//eof\r\n\t\t\tfloat args = tokenize(line);\r\n\t\t\tif (!args)\r\n\t\t\t\tcontinue;\t//blank line\r\n\t\t\tstring c = argv(0);\r\n\t\t\tstring n = argv(1);\r\n\t\t\tstring t = argv(2);\r\n\t\t\tif (c == \"-\")\t//command only\r\n\t\t\t{\r\n\t\t\t\tif (n != \"\")\r\n\t\t\t\t{\r\n\t\t\t\t\tmitem it = menuitemtext_spawn(n, \"\", 8);\r\n\t\t\t\t\tit.item_flags &= ~IF_SELECTABLE;\r\n\t\t\t\t\tfr.add(it, RS_X_MIN_PARENT_MID|RS_Y_MIN_PARENT_MIN | RS_X_MAX_OWN_MIN|RS_Y_MAX_OWN_MIN, [-it.item_size_x/2, i], it.item_size);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t\tfr.add(menuitembind_spawn(n, \t\tc, '280 8'), RS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MIN | RS_X_MAX_PARENT_MAX|RS_Y_MAX_OWN_MIN, [0, i], '0 8');\r\n\t\t\ti += 8;\r\n\t\t}\r\n\t\tfclose(f);\r\n\t}\r\n\telse\r\n\t{\r\n\t\t//throw a load of bind options onto it by reading from the array.\r\n\t\tfor (i = 0; i < sizeof(binds) / sizeof(binds[0]); i++)\r\n\t\t{\r\n\t\t\tif (binds[i].name == \"\")\t//no name is a spacer\r\n\t\t\t\tcontinue;\r\n\t\t\tfr.add(menuitembind_spawn(binds[i].name, \t\tbinds[i].cmd, '280 8'), RS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MIN | RS_X_MAX_PARENT_MAX|RS_Y_MAX_OWN_MIN, [0, (i*8)], '0 8');\r\n\t\t}\r\n\t}\r\n\t\r\n\t//and give us a suitable menu tint too, just because.\r\n\taddmenuback(m);\r\n};\r\n"
  },
  {
    "path": "quakec/menusys/menu/options_particles.qc",
    "content": "//menu that configues r_particledesc according to the files available in the particles/ subdir, including the (known) internal effects.\r\n\r\nclass particlesmenu : mitem_frame\r\n{\r\n\tstring thelist;\r\n\tvoid() particlesmenu =\r\n\t{\r\n\t\tthelist = cvar_string(\"r_particledesc\");\r\n\t};\r\n\tvirtual void() item_remove =\r\n\t{\r\n\t\tcvar_set(\"r_particledesc\", thelist);\r\n\t};\r\n\t\r\n\t//to reconfigure colours for when something changees.\r\n\tnonvirtual void() UpdateSelections =\r\n\t{\r\n\t\tfloat sc = tokenize(thelist);\r\n\t\tfor (mitem ch = item_children; ch; ch = ch.item_next)\r\n\t\t{\r\n\t\t\tch.item_rgb = '1 0.5 0.5';\r\n\t\t\tfor (float i = 0; i < sc; i++)\r\n\t\t\t{\r\n\t\t\t\tif (!strcasecmp(argv(i), ch.item_command))\r\n\t\t\t\t{\r\n\t\t\t\t\tch.item_rgb = '1 1 1';\r\n\t\t\t\t\tbreak;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t};\r\n\tvirtual void(mitem fromitem, string cmd)\titem_execcommand =\r\n\t{\r\n\t\tfloat sc = tokenize(thelist);\r\n\t\tfor (float i = 0; i < sc; i++)\r\n\t\t{\r\n\t\t\tif (!strcasecmp(argv(i), cmd))\r\n\t\t\t{\r\n\t\t\t\tthelist = strtrim(strcat(strtrim(substring(thelist, 0, argv_start_index(i))), \" \", strtrim(substring(thelist, argv_end_index(i), -1))));\r\n\t\t\t\tUpdateSelections();\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\t\t}\r\n\t\t\r\n\t\tif (strstrofs(cmd, \"\\t\") >= 0 || strstrofs(cmd, \" \") >= 0 || strstrofs(cmd, \"\\\"\") >= 0 || strstrofs(cmd, \";\") >= 0 || strstrofs(cmd, \"\\n\") >= 0)\r\n\t\t\tthelist = strtrim(strcat(thelist, \" \\\"\", cmd, \"\\\"\"));\r\n\t\telse\r\n\t\t\tthelist = strtrim(strcat(thelist, \" \", cmd));\r\n\t\tUpdateSelections();\r\n\t};\r\n};\r\n\r\nstring(string fname, string dflt) GetFirstLineComment =\r\n{\r\n\tfloat f = fopen(fname, FILE_READ);\r\n\tif (f < 0)\r\n\t\treturn 0;\r\n\tstring l = strtrim(fgets(f));\r\n\tfclose(f);\r\n\tif (!strcasecmp(substring(l, 0, 9), \"//private\"))\r\n\t\treturn 0;\r\n\tif (!strcasecmp(substring(l, 0, 8), \"//hidden\"))\r\n\t\treturn 0;\r\n\tif (!strcasecmp(substring(l, 0, 7), \"//desc:\"))\r\n\t\treturn strtrim(substring(l, 7, -1));\r\n//\tif (substring(l, 0, 1) == \"#\")\r\n//\t\treturn strtrim(substring(l, 1, -1));\r\n\treturn dflt;\r\n};\r\n\r\nnonstatic void(mitem_desktop desktop) M_Options_Particles =\r\n{\r\n\tfloat y = -8;\r\n\tfloat h;\r\n\r\n\t//create the menu, give it focus, and make sure its displayed over everything else.\r\n\tmitem_exmenu m = spawn(mitem_exmenu, item_text:_(\"Particles Options\"), item_flags:IF_SELECTABLE, item_command:\"m_options\");\r\n\tdesktop.add(m, RS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MIN | RS_X_MAX_PARENT_MAX|RS_Y_MAX_PARENT_MAX, '0 0', '0 0');\r\n\tdesktop.item_focuschange(m, IF_KFOCUSED);\r\n\tm.totop();\r\n\r\n\t//figure out the size of the stuff\r\n//\th = sizeof(binds) / sizeof(binds[0]);\r\n//\th *= 8;\r\n\th = 200;\r\n\th *= 0.5;\t//and halve it\r\n\r\n\t//draw title art above the options\r\n\tmitem_pic banner = spawn(mitem_pic, item_text:\"gfx/ttl_cstm.lmp\", item_size_y:24, item_flags:IF_CENTERALIGN);\r\n\tm.add(banner, RS_X_MIN_PARENT_MID|RS_Y_MIN_PARENT_MID | RS_X_MAX_PARENT_MID|RS_Y_MAX_PARENT_MID, [banner.item_size_x*-0.5, -h-32], [banner.item_size_x*0.5, -h-8]);\r\n\r\n\t//spawn a container frame for the actual options. this provides a scrollbar if we have too many items.\r\n\tparticlesmenu fr = spawn(particlesmenu, item_flags: IF_SELECTABLE, frame_hasscroll:TRUE);\r\n\tm.add(fr, RS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MID | RS_X_MAX_PARENT_MAX|RS_Y_MAX_OWN_MIN, [0, -h], [0, h*2]);\r\n\r\n\t//FIXME: this stuff should be listed in order of selection, to reflect the priorities given to the various effects.\r\n\r\n\t//FIXME: these should not be listed if its a no-compat/no-legacy build. this'll do for now, but its a bit wrong\r\n\tif (checkextension(\"FTE_PART_NAMESPACE_EFFECTINFO\"))\r\n\t{\r\n\t\tfr.add(spawn(mitem_text, item_text:\"Classic Particles\",\titem_command:\"classic\", item_scale:8, item_flags:IF_CENTERALIGN), RS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MIN | RS_X_MAX_PARENT_MAX|RS_Y_MAX_OWN_MIN, [0, y+=8], '0 8');\r\n\t\tif (engine==E_FTE)\r\n\t\t{\r\n\t\t\tfr.add(spawn(mitem_text, item_text:\"High Quality\",\titem_command:\"high\", item_scale:8, item_flags:IF_CENTERALIGN), RS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MIN | RS_X_MAX_PARENT_MAX|RS_Y_MAX_OWN_MIN, [0, y+=8], '0 8');\r\n\t\t\tfr.add(spawn(mitem_text, item_text:\"TimeServ's Shaft\", item_command:\"tsshaft\", item_scale:8, item_flags:IF_CENTERALIGN), RS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MIN | RS_X_MAX_PARENT_MAX|RS_Y_MAX_OWN_MIN, [0, y+=8], '0 8');\r\n\t\t}\r\n\t\r\n\t\tstring efi = GetFirstLineComment(\"effectinfo.txt\", \"Effectinfo\");\r\n\t\tif (efi)\r\n\t\t\tfr.add(spawn(mitem_text, item_text:efi, item_command:\"effectinfo\", item_scale:8, item_flags:IF_CENTERALIGN), RS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MIN | RS_X_MAX_PARENT_MAX|RS_Y_MAX_OWN_MIN, [0, y+=8], '0 8');\r\n\t}\r\n\r\n\tfloat fs;\r\n\tfs = search_begin(\"particles/*.cfg\", TRUE, TRUE);\t\r\n\tfor (float c = search_getsize(fs), float i = 0; i < c; i++)\r\n\t{\r\n\t\tstring fname = search_getfilename(fs, i);\r\n\t\tstring iname = substring(fname, 10, -5);\r\n\t\tstring dname = GetFirstLineComment(fname, iname);\r\n\t\tif (dname && !fr.findchildcmd(iname))\r\n\t\t\tfr.add(spawn(mitem_text, item_text:dname, item_command:iname, item_scale:8, item_flags:IF_CENTERALIGN), RS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MIN | RS_X_MAX_PARENT_MAX|RS_Y_MAX_OWN_MIN, [0, y+=8], '0 8');\r\n\t}\r\n\tsearch_end(fs);\r\n\tfs = search_begin(\"particles/*/*.cfg\", TRUE, TRUE);\t\r\n\tfor (float c = search_getsize(fs), float i = 0; i < c; i++)\r\n\t{\r\n\t\tstring fname = search_getfilename(fs, i);\r\n\t\tstring iname = substring(fname, 10, -5);\r\n\t\tstring dname = GetFirstLineComment(fname, iname);\r\n\t\tif (dname && !fr.findchildcmd(iname))\r\n\t\t\tfr.add(spawn(mitem_text, item_text:dname, item_command:iname, item_scale:8, item_flags:IF_CENTERALIGN), RS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MIN | RS_X_MAX_PARENT_MAX|RS_Y_MAX_OWN_MIN, [0, y+=8], '0 8');\r\n\t}\r\n\tsearch_end(fs);\r\n\r\n\tfr.UpdateSelections();\r\n\t\r\n\t//and give us a suitable menu tint too, just because.\r\n\taddmenuback(m);\r\n};\r\n"
  },
  {
    "path": "quakec/menusys/menu/options_video.qc",
    "content": "static string() PrepareVideoModes =\r\n{\r\n\tstring s = _(\"\\\"0x0\\\" \\\"Default\\\"\");\r\n\t\r\n\tfor (float i = 0; ; i++)\r\n\t{\r\n\t\tvector m = getresolution(i);\r\n\t\tif (!m_x || !m_y)\r\n\t\t\tbreak;\r\n\t\ts = sprintf(\"%s \\\"%dx%d\\\" \\\"%d x %d\\\"\", s, m_x, m_y, m_x, m_y);\r\n\t}\r\n\treturn s;\r\n};\r\n\r\nclass options_video : mitem_exmenu\r\n{\r\n\tstring videomode;\r\n\tvoid() options_video =\r\n\t{\r\n\t\tvideomode = strzone(strcat(super::get(\"vid_width\"), \"x\", super::get(\"vid_height\")));\r\n\t};\r\n\r\n\tvirtual float(string key) isvalid =\r\n\t{\r\n\t\tif (key == \"vid_mode\")\r\n\t\t\treturn super::isvalid(\"vid_width\") && super::isvalid(\"vid_height\");\r\n\t\treturn super::isvalid(key);\r\n\t};\r\n\tvirtual string(string key) get =\r\n\t{\r\n\t\tif (key == \"vid_mode\")\r\n\t\t\treturn videomode;\r\n\t\treturn super::get(key);\r\n\t};\r\n\tvirtual void(string key, string newval) set =\r\n\t{\r\n\t\tif (key == \"vid_mode\")\r\n\t\t{\r\n\t\t\tstring old = videomode;\r\n\t\t\tvideomode = strzone(newval);\r\n\t\t\ttokenizebyseparator(newval, \"x\");\r\n\t\t\tsuper::set(\"vid_width\", argv(0));\r\n\t\t\tsuper::set(\"vid_height\", argv(1));\r\n\t\t\tif (old)\r\n\t\t\t\tstrunzone(old);\r\n\t\t}\r\n\t\telse\r\n\t\t\tsuper::set(key, newval);\r\n\t};\r\n};\r\n\r\nnonstatic void(mitem_desktop desktop) M_Options_Video =\r\n{\r\n\tmitem_exmenu m;\r\n\tm = spawn(options_video, item_text:_(\"Video Options\"), item_flags:IF_SELECTABLE, item_command:\"m_options\");\r\n\tdesktop.add(m, RS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MIN | RS_X_MAX_PARENT_MAX|RS_Y_MAX_PARENT_MAX, '0 0', '0 0');\r\n\tdesktop.item_focuschange(m, IF_KFOCUSED);\r\n\tm.totop();\r\n\r\n\tfloat h = 200 * 0.5;\r\n\t//draw title art above the options\r\n\tmitem_pic banner = spawn(mitem_pic, item_text:\"gfx/p_option.lmp\", item_size_y:24, item_flags:IF_CENTERALIGN);\r\n\tm.add(banner, RS_X_MIN_PARENT_MID|RS_Y_MIN_PARENT_MID | RS_X_MAX_OWN_MIN|RS_Y_MAX_PARENT_MID, [(160-160-banner.item_size_x)*0.5, -h-32], [banner.item_size_x, -h-8]);\r\n\t//spawn a container frame for the actual options. this provides a scrollbar if we have too many items.\r\n\tmitem_frame fr = spawn(mitem_frame, item_flags: IF_SELECTABLE, frame_hasscroll:TRUE);\r\n\tm.add(fr, RS_X_MIN_PARENT_MID|RS_Y_MIN_PARENT_MID | RS_X_MAX_PARENT_MID|RS_Y_MAX_OWN_MIN, [-160, -h], [160, h*2]);\r\n\tfloat fl = RS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MIN | RS_X_MAX_PARENT_MAX|RS_Y_MAX_OWN_MIN;\r\n\tfloat pos = 0;\r\n\t\r\n\tpos += 8;\r\n\tfr.add(spawn(mitem_text, item_text:_(\"Apply / Restart\"), \titem_command:\"vid_restart\", item_scale:8, item_flags:IF_RIGHTALIGN),\tRS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MIN | RS_X_MAX_PARENT_MID|RS_Y_MAX_OWN_MIN, [0, pos], [-8, 8]); pos += 8;\r\n\tpos += 8;\r\n\t\r\n\tif (cvar_type(\"vid_renderer\")) fr.add(menuitemcombo_spawn(_(\"Renderer\"),\t\"vid_renderer\",\t\t'280 8', strcat(\"\\\"\\\" \\\"Default\\\" \", cvar_string(\"_vid_renderer_opts\"))),\tfl, [0, pos], [0, 8]); pos += 8;\r\n\r\n\t//add the options\r\n\tif (!dp_workarounds)\r\n\t{\r\n\t\tfr.add(menuitemcombo_spawn(_(\"Display Mode\"),\t\"vid_fullscreen\",\t\t\t\t'280 8', \r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tengine==E_FTE?\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"0\t\\\"Windowed\\\" \"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"1\t\\\"Fullscreen\\\" \"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"2\t\\\"Borderless Windowed\\\" \"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t:\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"0\t\\\"Windowed\\\" \"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"1\t\\\"Fullscreen\\\" \"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t),\tfl, [0, pos], [0, 8]); pos += 8;\r\n\t}\r\n\telse\r\n\t{\r\n\t\tfr.add(menuitemcheck_spawn(_(\"Fullscreen\"),\t\t\"vid_fullscreen\", \t\t\t'280 8'),\tfl, [0, pos], [0, 8]); pos += 8;\r\n\t}\r\n\tif (cvar_type(\"vid_resizable\")) fr.add(menuitemcheck_spawn(_(\"Resizable\"),\t\"vid_resizable\", \t'280 8'),\tfl, [0, pos], [0, 8]), pos += 8;\r\n\tfr.add(menuitemcombo_spawn(_(\"MSAA\"),\tcv3(\"vid_samples\"/*dp*/, \"vid_fsaa\"/*qs*/, \"vid_multisample\"/*fte*/),\t\t\t'280 8',\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"0\t\\\"Off\\\" \"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"2\t\\\"2x\\\" \"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"4\t\\\"4x\\\" \"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t),\tfl, [0, pos], [0, 8]); pos += 8;\r\n\r\n\tfr.add(menuitemcombo_spawn(_(\"Video Mode\"),\t\t\"vid_mode\",\t\t'280 8', PrepareVideoModes()), fl, [0, pos], [0, 8]); pos += 8;\r\n\r\nstatic const string scaleoptions = _(\r\n\t\"0  \\\"Default\\\" \"\r\n\t\"1.5    \\\"x1.5\\\" \"\r\n\t\"2  \\\"x2\\\" \"\r\n\t\"4  \\\"x4\\\" \");\r\n\r\n\tif (cvar_type(\"vid_conautoscale\"))\r\n\t\tfr.add(menuitemcombo_spawn(_(\"Video Zoom\"),\t\"vid_conautoscale\",\t'280 8', scaleoptions),\tfl, [0, pos], [0, 8]), pos += 8;\r\n\tif (cvar_type(\"scr_conscale\"))\r\n\t\tfr.add(menuitemcombo_spawn(_(\"Console Zoom\"),\t\"scr_conscale\",\t'280 8', scaleoptions),\tfl, [0, pos], [0, 8]), pos += 8;\r\n\tif (cvar_type(\"scr_sbarscale\"))\r\n\t\tfr.add(menuitemcombo_spawn(_(\"Sbar Zoom\"),\t\"scr_sbarscale\",\t'280 8', scaleoptions),\tfl, [0, pos], [0, 8]), pos += 8;\r\n\tif (cvar_type(\"scr_menuscale\"))\r\n\t\tfr.add(menuitemcombo_spawn(_(\"Menu Zoom\"),\t\"scr_menuscale\",\t'280 8', scaleoptions),\tfl, [0, pos], [0, 8]), pos += 8;\r\n\tfr.add(menuitemcombo_spawn(_(\"Colour Depth\"),\tcv2(\"vid_bitsperpixel\", \"vid_bpp\"),\t'280 8', _(\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"16\t\\\"16bit\\\" \" //r5g6b5, or so. unsupported on win10.\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"24\t\\\"24bit\\\" \"\t//rgba8 - we don't count the alpha.\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"30\t\\\"30bit\\\" \"\t//rgb10a2 - we don't count the alpha. unsupported on nvidia.\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"48\t\\\"48bit\\\" \"\t//half-floats - we don't count the alpha. unsupported on nvidia.\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t)),\tfl, [0, pos], [0, 8]); pos += 8;\r\n\tfr.add(menuitemcombo_spawn(_(\"Refresh Rate\"),\tcv2(\"vid_refreshrate\"/*qs*/, \"vid_displayfrequency\"),\t\t\t'280 8', _(\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"0\t\\\"Default\\\" \"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t//\t\"60\t\\\"60\\\" \"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t//\t\"75\t\\\"75\\\" \"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t)),\tfl, [0, pos], [0, 8]); pos += 8;\r\n\tfr.add(menuitemcheck_spawn(_(\"VSync\"),\t\tcv2(\"vid_vsync\", \"vid_wait\"), \t'280 8'),\tfl, [0, pos], [0, 8]); pos += 8;\r\n\tfr.add(menuitemcheck_spawn(_(\"Show Framerate\"),\tcv3(\"showfps\"/*dp*/, \"scr_showfps\"/*qs*/, \"show_fps\"/*id/qw*/), \t'280 8'),\tfl, [0, pos], [0, 8]); pos += 8;\r\n\tfr.add(menuitemslider_spawn(_(\"View Size\"),\t\"viewsize\",\t\t\t\t'50 120 10', '280 8'),\tfl, [0, pos], [0, 8]); pos += 8;\r\n\tfr.add(menuitemslider_spawn(_(\"Field Of View\"),\t\"fov\",\t\t\t\t'50 140 5', '280 8'),\tfl, [0, pos], [0, 8]); pos += 8;\r\n\tfr.add(menuitemslider_spawn(_(\"Gamma\"),\tcv2(\"v_gamma\"/*dp*/, \"gamma\"/*quake*/),\t\t\t\t'1.3 0.5 -0.1', '280 8'),\tfl, [0, pos], [0, 8]); pos += 8;\r\n\tfr.add(menuitemslider_spawn(_(\"Contrast\"),\tcv2(\"v_contrast\"/*dp*/, \"contrast\"/*quake*/),\t\t\t\t'0.7 2 0.1', '280 8'),\tfl, [0, pos], [0, 8]); pos += 8;\r\n\tif (engine!=E_QSS)fr.add(menuitemslider_spawn(_(\"Brightness\"),\tcv2(\"v_brightness\"/*dp*/, \"brightness\"/*quake*/),\t\t\t'0 0.4 0.05', '280 8'),\tfl, [0, pos], [0, 8]), pos += 8;\r\n\tif (engine!=E_QSS)fr.add(menuitemcombo_spawn(_(\"Hardware Gamma\"),\t\"vid_hardwaregamma\",\t\t\t'280 8',\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"0\t\\\"Off\\\" \"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"1\t\\\"Auto\\\" \"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"2\t\\\"Soft\\\" \"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"3\t\\\"Hard\\\" \"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"4\t\\\"Scene Only\\\" \"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t),\tfl, [0, pos], [0, 8]), pos += 8;\r\n\r\n\taddmenuback(m);\r\n};\r\n\r\n"
  },
  {
    "path": "quakec/menusys/menu/presets.qc",
    "content": "/***************************************************************************\r\nUses the engine command to apply the preset, and tries an exec instead if a config file exists.\r\nDoesn't track the current one or anything.\r\nReally simple and stupid menu.\r\nno background tint, so the game is still visible so you can preview it.\r\n\r\nyou should probably remove the fps_preset part. I left it in so that I don't have to bother with providing cfg files. note that this isn't a valid solution for dp, but whatever.\r\nthe great thing about providing source is that you can change it.\r\nyou can instead just shove your preset settings directly into the quotes instead of exec.\r\n*/\r\n\r\nstruct\r\n{\r\n\tstring name;\r\n\tstring preset;\r\n\tstring config;\r\n} presets[] =\r\n{\r\n\t{\"Simple\",\t\t\"286\",\t\t\"configs/preset_simple.cfg\"},\r\n\t{\"Fast\",\t\t\"fast\",\t\t\"configs/preset_fast.cfg\"},\r\n\t{\"Regression\",\t\"vanilla\",\t\t\"configs/preset_vanilla.cfg\"},\r\n\t{\"Faithful\",\t\"normal\",\t\t\"configs/preset_faithful.cfg\"},\r\n\t{\"Nice\",\t\t\"nice\",\t\t\"configs/preset_nice.cfg\"},\r\n\t{\"Realtime\",\t\"realtime\",\t\t\"configs/preset_realtime.cfg\"}\r\n};\r\nnonstatic float(string fname) checkfileexists =\r\n{\r\n\tif (dp_workarounds)\r\n\t{\r\n\t\tfloat sh = search_begin(fname, FALSE, TRUE);\r\n\t\tfloat result = FALSE;\r\n\t\tif (sh >= 0)\r\n\t\t{\r\n\t\t\tresult = !!search_getsize(sh);\r\n\t\t\tsearch_end(sh);\r\n\t\t}\r\n\t\treturn result;\r\n\t}\r\n\telse\r\n\t{\r\n\t\t//FTE's whichpack returns empty if its not in a pack, and null if its not found anywhere.\r\n\t\tif (whichpack(fname))\r\n\t\t\treturn TRUE;\r\n\t\treturn FALSE;\r\n\t}\r\n};\r\nnonstatic void(mitem_desktop desktop) M_Preset =\r\n{\r\n\tlocal float i;\r\n\tmitem_exmenu m;\r\n\tm = spawn(mitem_exmenu, item_text:_(\"Graphical Presets\"), item_flags:IF_SELECTABLE, item_command:\"m_options\");\r\n\tdesktop.add(m, RS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MIN | RS_X_MAX_PARENT_MAX|RS_Y_MAX_PARENT_MAX, '0 0', '0 0');\r\n\tdesktop.item_focuschange(m, IF_KFOCUSED);\r\n\tm.totop();\r\n\r\n\r\n\tfloat h = 200 * 0.5;\r\n\t//draw title art above the options\r\n\tmitem_pic banner = spawn(mitem_pic, item_text:\"gfx/p_option.lmp\", item_size_y:24, item_flags:IF_CENTERALIGN);\r\n\tm.add(banner, RS_X_MIN_PARENT_MID|RS_Y_MIN_PARENT_MID | RS_X_MAX_OWN_MIN|RS_Y_MAX_PARENT_MID, [(160-60-banner.item_size_x)*0.5, -h-32], [banner.item_size_x, -h-8]);\r\n\t//spawn a container frame for the actual options. this provides a scrollbar if we have too many items.\r\n\tmitem_frame fr = spawn(mitem_frame, item_flags: IF_SELECTABLE, frame_hasscroll:TRUE);\r\n\tm.add(fr, RS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MID | RS_X_MAX_PARENT_MAX|RS_Y_MAX_OWN_MIN, [0, -h], [0, h*2]);\r\n\tfloat fl = RS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MIN | RS_X_MAX_PARENT_MAX|RS_Y_MAX_OWN_MIN;\r\n\r\n\tfloat fs, y=0;\r\n\tfs = search_begin(\"configs/preset_*.cfg\", TRUE, TRUE);\t\r\n\tfloat c = search_getsize(fs);\r\n\tfor (i = 0; i < c; i++)\r\n\t{\r\n\t\tstring fname = search_getfilename(fs, i);\r\n\t\tstring iname = substring(fname, 15, -5);\r\n\t\tstring dname = GetFirstLineComment(fname, iname);\r\n\t\tiname = sprintf(\"exec \\\"configs/preset_%s.cfg\\\";vid_reload\", iname);\r\n\t\tif (dname && !fr.findchildcmd(iname))\r\n\t\t{\r\n\t\t\tfr.add(spawn(mitem_text, item_text:dname, item_command:iname, item_scale:16, item_flags:IF_CENTERALIGN), fl, [0, y], '100 16');\r\n\t\t\ty+=16;\r\n\t\t}\r\n\t}\r\n\tsearch_end(fs);\t\r\n\tif (c <= 0)\r\n\t{\r\n\t\t//add the default options\r\n\t\tfor (i = 0, float pos = 0; i < presets.length; i++)\r\n\t\t{\r\n\t\t\tfr.add(spawn(mitem_text, item_text:presets[i].name, item_command:sprintf(\"fps_preset %s\", presets[i].preset), item_scale:16, item_flags:IF_CENTERALIGN), fl, [0, pos], [100, 16]);\r\n\t\t\tpos += 16;\r\n\t\t}\r\n\t}\r\n\r\n\t//random art for style\r\n\tm.addm(spawn (mitem_spinnymodel, item_text: \"progs/suit.mdl\"), [-160, 12*-16/2], [0, 12*16/2]);\r\n\r\n\taddmenuback(m);\r\n};\r\n"
  },
  {
    "path": "quakec/menusys/menu/quit.qc",
    "content": "/***************************************************************************\r\nQuit menu. Quite lame for now.\r\n*/\r\nfloat() cvars_haveunsaved = #0;\r\nnonstatic void(mitem_desktop desktop) M_Quit =\r\n{\r\n\tlocal float pos;\r\n\tmitem_exmenu m;\r\n\tfloat unsaved = FALSE;\r\n\tm = spawn(mitem_exmenu, item_text:_(\"Options\"), item_flags:IF_SELECTABLE, item_command:\"m_main\");\r\n\tdesktop.add(m, RS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MIN | RS_X_MAX_PARENT_MAX|RS_Y_MAX_PARENT_MAX, '0 0', '0 0');\r\n\tdesktop.item_focuschange(m, IF_KFOCUSED);\r\n\tm.totop();\r\n\r\n\t//center the actual items\r\n\tpos = (16/-2)*(2);\r\n\r\n\t//draw title art above the options\r\n//\tmitem_pic banner = spawn(mitem_pic, item_text:\"gfx/p_option.lmp\", item_size_y:24, item_flags:IF_CENTERALIGN);\r\n//\tm.add(banner, RS_X_MIN_PARENT_MID|RS_Y_MIN_PARENT_MID | RS_X_MAX_PARENT_MID|RS_Y_MAX_PARENT_MID, [(-160-banner.item_size_x)*0.5, pos-32], [(-160+banner.item_size_x)*0.5, pos-8]);\r\n\r\n\tif (checkbuiltin(cvars_haveunsaved))\r\n\t\tunsaved = cvars_haveunsaved();\t//engines that don't have this are assumed to always save regardless. Which makes prompting for it irrelevant. The all-saved text ignores saving entirely so it still makes sense.\r\n\tif (unsaved)\r\n\t{\r\n\t\tm.add(spawn(mitem_text, item_text:\"Save configuration?\", item_scale:16, item_flags:IF_CENTERALIGN), RS_X_MIN_PARENT_MID|RS_Y_MIN_PARENT_MID | RS_X_MAX_PARENT_MID|RS_Y_MAX_PARENT_MID, [-160, pos-(8+16)], [0, pos-8]);\tpos += 16;\r\n\r\n\t\tm.add(spawn(mitem_text, item_text:\"Save and quit\", item_command:\"m_pop;cfg_save;quit\", item_scale:16, item_flags:IF_CENTERALIGN), RS_X_MIN_PARENT_MID|RS_Y_MIN_PARENT_MID | RS_X_MAX_PARENT_MID|RS_Y_MAX_PARENT_MID, [-160, pos], [0, pos+16]);\tpos += 16;\r\n\t\tm.add(spawn(mitem_text, item_text:\"Quit and discard settings\", item_command:\"m_pop;quit\", item_scale:16, item_flags:IF_CENTERALIGN), RS_X_MIN_PARENT_MID|RS_Y_MIN_PARENT_MID | RS_X_MAX_PARENT_MID|RS_Y_MAX_PARENT_MID, [-160, pos], [0, pos+16]);\tpos += 16;\r\n\t\tm.add(spawn(mitem_text, item_text:\"Keep playing.\", item_command:\"m_pop;m_main\", item_scale:16, item_flags:IF_CENTERALIGN), RS_X_MIN_PARENT_MID|RS_Y_MIN_PARENT_MID | RS_X_MAX_PARENT_MID|RS_Y_MAX_PARENT_MID, [-160, pos], [0, pos+16]);\tpos += 16;\r\n\t}\r\n\telse\r\n\t{\r\n\t\tm.add(spawn(mitem_text, item_text:\"Really Quit?\", item_scale:16, item_flags:IF_CENTERALIGN), RS_X_MIN_PARENT_MID|RS_Y_MIN_PARENT_MID | RS_X_MAX_PARENT_MID|RS_Y_MAX_PARENT_MID, [-160, pos-(8+16)], [0, pos-8]);\tpos += 16;\r\n\r\n\t\tm.add(spawn(mitem_text, item_text:\"Yes, I'm a quitter.\", item_command:\"m_pop;quit\", item_scale:16, item_flags:IF_CENTERALIGN), RS_X_MIN_PARENT_MID|RS_Y_MIN_PARENT_MID | RS_X_MAX_PARENT_MID|RS_Y_MAX_PARENT_MID, [-160, pos], [0, pos+16]);\tpos += 16;\r\n\t\tm.add(spawn(mitem_text, item_text:\"No, keep playing!\", item_command:\"m_pop;m_main\", item_scale:16, item_flags:IF_CENTERALIGN), RS_X_MIN_PARENT_MID|RS_Y_MIN_PARENT_MID | RS_X_MAX_PARENT_MID|RS_Y_MAX_PARENT_MID, [-160, pos], [0, pos+16]);\tpos += 16;\r\n\t}\r\n\r\n\t//random art for style\r\n\tm.add(spawn (mitem_spinnymodel, item_text: \"progs/suit.mdl\"), RS_X_MIN_PARENT_MID|RS_Y_MIN_PARENT_MID | RS_X_MAX_PARENT_MID|RS_Y_MAX_PARENT_MID, [0, 12*-16/2], [160, 12*16/2]);\r\n\taddmenuback(m);\r\n};\r\n"
  },
  {
    "path": "quakec/menusys/menu/servers.qc",
    "content": "//very very basic server browser.\r\n//this was written as an example for the api, rather than a truely usable server browser.\r\n\r\n//WARNING: make sure you only create one server browser widget otherwise they'll fight each other\r\n\r\n#ifndef MOD_GAMEDIR\r\n//look up what its meant to be.\r\n//#define MOD_GAMEDIR cvar_string(\"game\")\r\n#endif\r\n\r\n#include \"../menusys/mitem_menu.qc\"\t\t//subwindow\r\n\r\nvar float autocvar_sb_shownumplayers = 1;\r\nvar float autocvar_sb_showmaxplayers = 0;\r\nvar float autocvar_sb_showfraglimit = 0;\r\nvar float autocvar_sb_showtimelimit = 0;\r\nvar float autocvar_sb_showping = 1;\r\nvar float autocvar_sb_showgamedir = 0;\r\nvar float autocvar_sb_showaddress = 0;\r\nvar float autocvar_sb_showmap = 1;\r\nvar float autocvar_sb_showname = 1;\r\n\r\n//#define COLUMN(width, sortname, title, draw)\r\n#define COLUMN_NUMPLAYERS\t\tCOLUMN(2*8,\tnumplayers,\t\"Pl\", \t\t\tui.drawstring(pos, sprintf(\"%-2g\", gethostcachenumber(field_numplayers, sv)), '8 8', col, 1, 0);)\r\n#define COLUMN_MAXPLAYERS\t\tCOLUMN(2*8,\tmaxplayers,\t\"MP\", \t\tui.drawstring(pos, sprintf(\"%-2g\", gethostcachenumber(field_maxplayers, sv)), '8 8', col, 1, 0);)\r\n#define COLUMN_PING\t\t\tCOLUMN(4*8,\tping,\t\t\t\"Ping\", \t\tui.drawstring(pos, sprintf(\"%-4g\", gethostcachenumber(field_ping, sv)), '8 8', col, 1, 0);)\r\n#define COLUMN_FRAGLIMIT\t\tCOLUMN(4*8,\tfraglimit,\t\t\"FL\",\t\t\tui.drawstring(pos, sprintf(\"%-3g\", gethostcachenumber(field_fraglimit, sv)), '8 8', col, 1, 0);)\r\n#define COLUMN_TIMELIMIT\t\tCOLUMN(4*8,\ttimelimit,\t\t\"TL\",\t\t\tui.drawstring(pos, sprintf(\"%-3g\", gethostcachenumber(field_timelimit, sv)), '8 8', col, 1, 0);)\r\n#define COLUMN_GAMEDIR\t\tCOLUMN(8*8,\tgamedir,\t\t\"Gamedir\",\tui.drawstring(pos, sprintf(\"%-.8s\", gethostcachestring(field_gamedir, sv)), '8 8', col, 1, 0);)\r\n#define COLUMN_ADDRESS\t\tCOLUMN(21*8,\taddress,\t\t\"Address\",\t\tui.drawstring(pos, sprintf(\"%-.21s\", gethostcachestring(field_address, sv)), '8 8', col, 1, 0);)\r\n#define COLUMN_MAP\t\t\tCOLUMN(8*8,\tmap,\t\t\t\"Map\",\t\tui.drawstring(pos, sprintf(\"%-.8s\", gethostcachestring(field_map, sv)), '8 8', col, 1, 0);)\r\n#define COLUMN_HOSTNAME\t\tCOLUMN(64*8,\tname,\t\t\"Name\",\t\tui.drawstring(pos, sprintf(\"%s\", gethostcachestring(field_name, sv)), '8 8', col, 1, 0);)\r\n//FIXME: add a little * icon before the hostname for favourites or something\r\n\r\n#define COLUMNS \t\t\t\tCOLUMN_NUMPLAYERS\tCOLUMN_MAXPLAYERS\tCOLUMN_PING\tCOLUMN_FRAGLIMIT\tCOLUMN_TIMELIMIT\tCOLUMN_GAMEDIR\tCOLUMN_ADDRESS\tCOLUMN_MAP\tCOLUMN_HOSTNAME\r\n\r\nclass mitem_servers : mitem\r\n{\r\n\tfloat server_selected;\r\n\tmitem_vslider slider;\r\n\tfloat dbltime;\r\n\tfloat dobound;\r\n\r\n\tvoid() mitem_servers =\r\n\t{\r\n\t\tdbltime = cltime - 10;\r\n\t\tthis.item_flags |= IF_SELECTABLE;\r\n\t\tserver_selected = -1;\r\n\r\n\t\t//clear the filter\r\n\t\tresethostcachemasks();\r\n#ifdef MOD_GAMEDIR\r\n\t\tif (MOD_GAMEDIR != \"\")\r\n\t\t{\r\n\t\t\t//constrain the list to only servers with the right gamedir.\r\n\t\t\tsethostcachemaskstring(0, gethostcacheindexforkey(\"gamedir\"), MOD_GAMEDIR, SLIST_TEST_EQUAL);\r\n\t\t}\r\n#endif\r\n\t\t//sort by ping by default\r\n\t\tsethostcachesort(gethostcacheindexforkey(\"ping\"), FALSE);\r\n\t\t//(re)query the servers.\r\n\t\trefreshhostcache();\r\n\r\n\t\tresorthostcache();\r\n\t};\r\n\r\n\tvirtual void(vector pos) item_draw =\r\n\t{\r\n\t\tlocal float sv, maxsv = gethostcachevalue(SLIST_HOSTCACHEVIEWCOUNT);\r\n\t\tlocal float maxy = self.item_size_y;\r\n\t\tfloat left = pos_x;\r\n\t\r\n\t\tlocal vector omin = ui.drawrectmin, omax = ui.drawrectmax;\r\n\t\tlocal vector cmin = pos + '0 8', cmax = pos + item_size;\r\n\t\r\n\t\tcmin_x = max(omin_x, cmin_x);\r\n\t\tcmin_y = max(omin_y, cmin_y);\r\n\t\tcmax_x = min(omax_x, cmax_x);\r\n\t\tcmax_y = min(omax_y, cmax_y);\r\n\r\n\t\tslider.maxv = maxsv - (floor(maxy/8) - 1);\r\n\r\n\t\t//constrain the view the lazy way.\r\n\t\tif (dobound)\r\n\t\t{\r\n\t\t\tdobound = FALSE;\r\n\t\t\tif (slider.val < 0)\r\n\t\t\t\tslider.val = 0;\r\n\t\t\twhile (slider.val + floor(maxy/8) - 1 <= server_selected)\r\n\t\t\t\tslider.val+=1;\r\n\t\t\twhile (server_selected < slider.val && slider.val > 0)\r\n\t\t\t\tslider.val-=1;\r\n\t\t}\r\n\r\n\t\tfloat field_name = gethostcacheindexforkey(\"name\");\r\n\t\tfloat field_ping = gethostcacheindexforkey(\"ping\");\r\n\t\tfloat field_numplayers = gethostcacheindexforkey(\"numhumans\");\r\n\t\tif (field_numplayers < 0)\r\n\t\t\tfield_numplayers = gethostcacheindexforkey(\"numplayers\");\r\n\t\tfloat field_maxplayers = gethostcacheindexforkey(\"maxplayers\");\r\n\t\tfloat field_gamedir = gethostcacheindexforkey(\"gamedir\");\r\n\t\tif (field_gamedir < 0)\r\n\t\t\tfield_gamedir = gethostcacheindexforkey(\"mod\");\r\n\t\tfloat field_address = gethostcacheindexforkey(\"cname\");\r\n\t\tfloat field_map = gethostcacheindexforkey(\"map\");\r\n\t\tfloat field_timelimit = gethostcacheindexforkey(\"timelimit\");\r\n\t\tfloat field_fraglimit = gethostcacheindexforkey(\"fraglimit\");\r\n\r\n\t\tmaxy = ceil(maxy/8) - 1;\r\n\t\tif (maxsv  > slider.val + maxy)\r\n\t\t\tmaxsv = slider.val + maxy;\r\n\t\r\n\t\tfloat sort = gethostcachevalue(SLIST_SORTFIELD);\r\n\t\tstring colkey = __NULL__;\r\n#define COLUMN(width, sortname, title, draw) if (field_##sortname<0) autocvar_sb_show##sortname = FALSE; if (autocvar_sb_show##sortname) {if (ui.mousepos[0] > pos_x && ui.mousepos[1] < pos_y+8) colkey = #sortname; pos_x += width+8;}\r\n\t\tCOLUMNS\r\n#undef COLUMN\r\n\r\n\t\tvector col;\r\n\t\tpos_x = left;\r\n#define COLUMN(width, sortname, title, draw) if (autocvar_sb_show##sortname) {col = '1 1 1'; if (sort == field_##sortname) col_z = 0; if (colkey == #sortname) col_x = 0; ui.drawstring(pos, title, '8 8', col, 1, 0); pos_x += width+8;}\r\n\t\tCOLUMNS\r\n#undef COLUMN\r\n\r\n\t\t//make sure things get cut off if we have too many rows (or fractional start)\r\n\t\tui.setcliparea(cmin[0], cmin[1], cmax[0] - cmin[0], cmax[1] - cmin[1]);\r\n\r\n\t\tpos_y += 8 * (1-(slider.val-floor(slider.val)));\r\n\t\tfor (float y=pos_y,\tsv = max(0, floor(slider.val)); sv < maxsv; sv+=1)\r\n\t\t{\r\n\t\t\tcol = (sv&1)?'0.1 0.1 0.1':'0.15 0.1 0.05';\r\n\t\t\tdrawfill([left, y], [item_size_x,8], col, 0.8, 0); \r\n\t\t\ty += 8;\r\n\t\t}\r\n\t\tfor (\tsv = max(0, floor(slider.val)); sv < maxsv; sv+=1)\r\n\t\t{\r\n\t\t\tcol = ((server_selected==sv)?'1 1 0':'1 1 1');\r\n\t\r\n\t\t\t//if isproxy\r\n\t\t\t//if islocal\r\n\t\t\t//if isfavorite\r\n\t\r\n\t\t\tpos_x = left;\r\n#define COLUMN(width, sortname, title, draw) if (autocvar_sb_show##sortname) {draw   pos_x += width+8; }\r\n\t\t\tCOLUMNS\r\n#undef COLUMN\r\n\t\t\tpos_y += 8;\r\n\t\t}\r\n\t\r\n\t\tui.setcliparea(omin_x, omin_y, omax_x - omin_x, omax_y - omin_y);\r\n\t};\r\n\tstatic string() getgamedircmd =\r\n\t{\t//sadly the vanilla NQ network protocol gave clients no idea which gamedir the server is actually using.\r\n\t\t//many extended protocols still lack that info, so be sure to try to switch according to the server browser's gamedir info, because its more robust than not even trying.\r\n\t\tfloat field_gamedir = gethostcacheindexforkey(\"gamedir\");\r\n\t\tif (field_gamedir < 0)\r\n\t\t\tfield_gamedir = gethostcacheindexforkey(\"mod\");\r\n\t\tif (field_gamedir < 0)\r\n\t\t\treturn \"\";\t//erk. dodgy engine?\r\n\r\n\t\tstring gd = gethostcachestring(field_gamedir, server_selected);\r\n\t\tif (strstrofs(gd, \"\\\"\")>=0 || strstrofs(gd, \"\\n\")>=0 || strstrofs(gd, \"\\r\")>=0)\r\n\t\t\treturn \"\";\t//no, just no. trust the engine to reject bad paths, we're just preventing cbuf corruption here (DP doesn't support \"%S\").\r\n\t\treturn sprintf(\"gamedir \\\"%s\\\";\", gd);\r\n\t};\r\n\tvirtual float(vector pos, float scan, float char, float down) item_keypress =\r\n\t{\r\n\t\tfloat displaysize;\r\n\t\tstring addr;\r\n\t\t/*just sink all inputs*/\r\n\t\tif (!down)\r\n\t\t\treturn FALSE;\r\n\t\tif (scan != K_MOUSE1 && scan != K_TOUCHTAP)\r\n\t\t\tdbltime = cltime - 10;\r\n\t\tif (scan == K_MOUSE1 || scan == K_TOUCHTAP)\r\n\t\t{\r\n\t\t\tfloat news = ui.mousepos[1] - (pos_y+8);\r\n\t\t\tnews = floor(news / 8);\r\n\t\t\tif (news == -1)\r\n\t\t\t{\r\n\t\t\t\tstring colkey = \"\";\r\n#define COLUMN(width, sortname, title, draw) if (autocvar_sb_show##sortname) {if (ui.mousepos[0] > pos_x) colkey = #sortname; pos_x += width+8;}\r\n\t\t\t\tCOLUMNS\r\n#undef COLUMN\r\n\t\t\t\tif (colkey != \"\")\r\n\t\t\t\t{\r\n\t\t\t\t\tfloat fld = gethostcacheindexforkey(colkey);\r\n\t\t\t\t\tif (colkey == \"numplayers\")\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\t//favour descending order\r\n\t\t\t\t\t\tsethostcachesort(fld, (gethostcachevalue(SLIST_SORTFIELD) != fld) || !gethostcachevalue(SLIST_SORTDESCENDING));\r\n\t\t\t\t\t}\r\n\t\t\t\t\telse\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\t//favour ascending order\r\n\t\t\t\t\t\tsethostcachesort(fld, (gethostcachevalue(SLIST_SORTFIELD) == fld) && !gethostcachevalue(SLIST_SORTDESCENDING));\r\n\t\t\t\t\t}\r\n\t\t\t\t\tresorthostcache();\t//tell the engine that its okay to resort everything.\r\n\t\t\t\t\tdobound = TRUE;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\tif (news < 0 || news >= gethostcachevalue(SLIST_HOSTCACHEVIEWCOUNT))\r\n\t\t\t\treturn FALSE;\r\n\t\r\n\t\t\tnews += floor(slider.val);\r\n\r\n\t\t\tif (server_selected == news && dbltime > cltime)\r\n\t\t\t{\r\n\t\t\t\t//connect on double clicks. because we can.\r\n\t\t\t\taddr = gethostcachestring(gethostcacheindexforkey(\"cname\"), server_selected);\r\n\t\t\t\tif (addr)\r\n\t\t\t\t\tlocalcmd(sprintf(\"m_pop;%sconnect \\\"%s\\\"\\n\", getgamedircmd(), addr));\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t\tserver_selected = news;\r\n\t\t\tdobound = TRUE;\r\n\t\r\n\t\t\tdbltime = cltime + 0.5;\r\n\t\t}\r\n\t\telse if (ISCONFIRMKEY(scan))\r\n\t\t{\t//connect normally\r\n\t\t\taddr = gethostcachestring(gethostcacheindexforkey(\"cname\"), server_selected);\r\n\t\t\tif (addr)\r\n\t\t\t\tlocalcmd(sprintf(\"m_pop;%sconnect \\\"%s\\\"\\n\", getgamedircmd(), addr));\r\n\t\t}\r\n\t\telse if (scan == 's' || scan == K_GP_X)\r\n\t\t{\t//s = join as a spectator\r\n\t\t\taddr = gethostcachestring(gethostcacheindexforkey(\"cname\"), server_selected);\r\n\t\t\tif (addr)\r\n\t\t\t\tlocalcmd(sprintf(\"m_pop;%sobserve \\\"%s\\\"\\n\", getgamedircmd(), addr));\r\n\t\t}\r\n\t\telse if (scan == 'j')\r\n\t\t{\t//j = join as a player\r\n\t\t\taddr = gethostcachestring(gethostcacheindexforkey(\"cname\"), server_selected);\r\n\t\t\tif (addr)\r\n\t\t\t\tlocalcmd(sprintf(\"m_pop;%sjoin \\\"%s\\\"\\n\", getgamedircmd(), addr));\r\n\t\t}\r\n\t\telse if (ISUPARROW(scan) || scan == K_MWHEELUP)\r\n\t\t{\r\n\t\t\tthis.server_selected -= 1;\r\n\t\t\tif (this.server_selected < 0)\r\n\t\t\t{\r\n\t\t\t\tthis.server_selected = gethostcachevalue(SLIST_HOSTCACHEVIEWCOUNT);\r\n\t\t\t\tif (this.server_selected)\r\n\t\t\t\t\tthis.server_selected -= 1;\r\n\t\t\t}\r\n\t\t\tdobound = TRUE;\r\n\t\t}\r\n\t\telse if (ISDOWNARROW(scan) || scan == K_MWHEELDOWN)\r\n\t\t{\r\n\t\t\tthis.server_selected += 1;\r\n\t\t\tif (this.server_selected >= gethostcachevalue(SLIST_HOSTCACHEVIEWCOUNT))\r\n\t\t\t\tthis.server_selected = 0;\r\n\t\t\tdobound = TRUE;\r\n\t\t}\r\n\t\telse if (scan == K_PGUP)\r\n\t\t{\r\n\t\t\tdisplaysize = (item_size[1]-8)/(8);\t//this many rows\r\n\t\t\tdisplaysize = floor(displaysize*0.5);\r\n\t\t\tif (displaysize < 1)\r\n\t\t\t\tdisplaysize = 1;\r\n\t\t\tthis.server_selected -= displaysize ;\r\n\t\t\tif (this.server_selected < 0)\r\n\t\t\t\tthis.server_selected = 0;\r\n\t\t\tdobound = TRUE;\r\n\t\t}\r\n\t\telse if (scan == K_PGDN)\r\n\t\t{\r\n\t\t\tdisplaysize = (item_size[1]-8)/(8);\t//this many rows\r\n\t\t\tdisplaysize = floor(displaysize*0.5);\r\n\t\t\tif (displaysize < 1)\r\n\t\t\t\tdisplaysize = 1;\r\n\t\t\tthis.server_selected += displaysize;\r\n\t\t\tif (this.server_selected >= gethostcachevalue(SLIST_HOSTCACHEVIEWCOUNT))\r\n\t\t\t\tthis.server_selected = gethostcachevalue(SLIST_HOSTCACHEVIEWCOUNT)-1;\r\n\t\t\tdobound = TRUE;\r\n\t\t}\r\n\t\telse if (scan == K_HOME)\r\n\t\t{\r\n\t\t\tthis.server_selected = 0;\r\n\t\t\tdobound = TRUE;\r\n\t\t}\r\n\t\telse if (scan == K_END)\r\n\t\t{\r\n\t\t\tthis.server_selected = gethostcachevalue(SLIST_HOSTCACHEVIEWCOUNT)-1;\r\n\t\t\tif (this.server_selected < 0)\r\n\t\t\t\tthis.server_selected = 0;\r\n\t\t\tdobound = TRUE;\r\n\t\t}\r\n\t\telse if (char == 'r' || char == 'R' || scan == K_F5 || scan == K_GP_Y)\r\n\t\t{\r\n\t\t\trefreshhostcache();\r\n\t\t\tresorthostcache();\r\n\t\t}\r\n\t\telse\r\n\t\t\treturn FALSE;\r\n\t\treturn TRUE;\r\n\t};\r\n};\r\n\r\nclass mitem_servers_players : mitem\r\n{\r\n\tmitem_servers listing;\r\n\r\n\tvoid() mitem_servers_players =\r\n\t{\r\n//\t\tthis.item_flags |= IF_SELECTABLE;\r\n\t};\r\n\t\r\n\tvirtual void(vector pos) item_draw =\r\n\t{\r\n\t\tfloat player;\r\n\t\tfloat y;\r\n\t\tfloat m;\r\n\t\tvector opos = pos;\r\n\t\tif (listing.server_selected < 0)\r\n\t\t\treturn;\r\n\t\tfor (player = 0, y = 0; player < 256; player++)\r\n\t\t{\r\n\t\t\tfloat key = gethostcacheindexforkey(sprintf(\"player%g\", player));\r\n\t\t\tif (key < 0)\r\n\t\t\t\tbreak;\t//probably DP. this isn't going to work for us.\r\n\t\t\tstring playerinfo = gethostcachestring(key, listing.server_selected);\r\n\t\t\tif (!playerinfo)\r\n\t\t\t\tbreak;\r\n\t\t\ttokenize(playerinfo);\r\n\t\t\tfloat userid\t= stof(argv(0));\r\n\t\t\tfloat frags\t\t= stof(argv(1));\r\n\t\t\tfloat ontime\t= stof(argv(2));\r\n\t\t\tfloat ping\t\t= stof(argv(3));\r\n\t\t\tstring name\t= argv(4);\r\n\t\t\tstring skin\t\t= argv(5);\r\n\t\t\tvector top\t\t= stov(argv(6));\r\n\t\t\tvector bot\t\t= stov(argv(7));\r\n\r\n\t\t\tdrawfill(pos,\t\t'16 4',\ttop, 1, 0);\r\n\t\t\tdrawfill(pos+'0 4 0',\t'16 4',\tbot, 1, 0);\r\n\t\t\tdrawstring(pos,\tsprintf(\"%g\", frags), '8 8', '1 1 1', 1, 0);\r\n\t\t\tdrawstring(pos+'20 0',\tname, '8 8', '1 1 1', 1, 0);\r\n\t\t\tpos_y += 8;\r\n\t\t\tif (++y == 8)\r\n\t\t\t{\r\n\t\t\t\ty-= 8;\r\n\t\t\t\tpos_y = opos_y;\r\n\t\t\t\tpos_x += 16*8;\r\n\t\t\t}\r\n\t\t}\r\n\t\r\n\t\tif (y)\r\n\t\t{\r\n\t\t\tpos_y = opos_y;\r\n\t\t\tpos_x += 16*6;\r\n\t\t\ty = 0;\r\n\t\t}\r\n\r\n\t\tif (checkbuiltin(drawtextfield))\r\n\t\t{\r\n//\t\t\tdrawtextfield(opos, item_size, 3, gethostcachestring(gethostcacheindexforkey(\"serverinfo\"), listing.server_selected));\r\n\t\t\tm = tokenizebyseparator(gethostcachestring(gethostcacheindexforkey(\"serverinfo\"), listing.server_selected), \"\\\\\");\r\n\t\t\tfor(player = 1; player <= m; player += 2)\r\n\t\t\t{\r\n\t\t\t\tdrawtextfield(pos,\t\t'64 8', 6, argv(player));\r\n\t\t\t\tdrawtextfield(pos+'68 0',\t[32*8-40, 8], 3, argv(player+1));\r\n\t\t\r\n\t\t\t\tpos_y += 8;\r\n\t\t\t\tif (++y == 8)\r\n\t\t\t\t{\r\n\t\t\t\t\ty-= 8;\r\n\t\t\t\t\tpos_y -= 8*8;\r\n\t\t\t\t\tpos_x += 32*8;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t};\r\n};\r\n\r\n\r\nnonstatic void(mitem_desktop desktop) M_Servers =\r\n{\r\n\tmitem_menu o;\r\n\r\n\tif (checkcommand2(\"menu_servers\", FALSE) && argv(1) != \"force\")\r\n\t{\r\n\t\tlocalcmd(\"menu_servers\\n\");\r\n\t\treturn;\r\n\t}\r\n\t\r\n#ifdef CSQC\r\n\tif not (checkextension(\"FTE_CSQC_SERVERBROWSER\"))\r\n\t{\r\n\t\tprint(_(\"Sorry, your client does not support FTE_CSQC_SERVERBROWSER\\n\"));\r\n\t\treturn;\r\n\t}\r\n#endif\r\n\r\n\to = (mitem_menu)desktop.findchildtext(_(\"Servers List\"));\r\n\tif (o)\r\n\t\to.totop();\r\n\telse\r\n\t{\r\n#if 1\r\n\t\tmitem_exmenu m;\r\n\t\tm = spawn(mitem_exmenu, item_text:_(\"Options\"), item_flags:IF_SELECTABLE, item_command:\"m_main\");\r\n\t\tdesktop.add(m, RS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MIN | RS_X_MAX_PARENT_MAX|RS_Y_MAX_PARENT_MAX, '0 0', '0 0');\r\n\t\tdesktop.item_focuschange(m, IF_KFOCUSED);\r\n\t\tm.totop();\r\n#else\r\n\t\tmitem_menu m;\r\n\t\tm = menu_spawn(desktop, _(\"Servers List\"), '320 200');\r\n\t\tm.item_command = \"m_main\";\r\n\t\tm.item_flags |= IF_RESIZABLE;\r\n\t\tdesktop.item_focuschange(m, IF_KFOCUSED);\r\n\t\tm.totop();\r\n#endif\r\n\r\n\t\tmitem_vslider sl = spawn(mitem_vslider, stride:4, item_flags:IF_SELECTABLE);\r\n\t\tmitem_servers ls = spawn(mitem_servers, slider:sl);\r\n\t\r\n\t\tm.add(ls, RS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MIN | RS_X_MAX_PARENT_MAX|RS_Y_MAX_PARENT_MAX, '0 0', '-16 -68');\r\n\t\tm.add(spawn(mitem_servers_players, listing:ls),\tRS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MAX | RS_X_MAX_PARENT_MAX|RS_Y_MAX_PARENT_MAX, [8*20, -8*8], [0, -8*0]);\r\n\t\tm.add(sl,\tRS_X_MIN_PARENT_MAX|RS_Y_MIN_PARENT_MIN | RS_X_MAX_PARENT_MAX|RS_Y_MAX_PARENT_MAX, [-16, 8], [0, -68]);\r\n\r\n\t\t//only add checkboxes for field names accepted by this engine.\r\n\t\tif (gethostcacheindexforkey(\"ping\") >= 0)\r\n\t\t\tm.add(menuitemcheck_spawn(_(\"Ping\"),\t\t\t\"sb_showping\", \t\t\t[8*8, 8]),\tRS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MAX | RS_X_MAX_PARENT_MIN|RS_Y_MAX_PARENT_MAX, [0, -8*8], [8*20, -8*7]);\r\n\t\tif (gethostcacheindexforkey(\"cname\") >= 0)\r\n\t\t\tm.add(menuitemcheck_spawn(_(\"Address\"),\t\t\"sb_showaddress\", \t\t[8*8, 8]),\tRS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MAX | RS_X_MAX_PARENT_MIN|RS_Y_MAX_PARENT_MAX, [0, -8*7], [8*20, -8*6]);\r\n\t\tif (gethostcacheindexforkey(\"map\") >= 0)\r\n\t\t\tm.add(menuitemcheck_spawn(_(\"Map\"),\t\t\t\"sb_showmap\", \t\t\t[8*8, 8]),\tRS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MAX | RS_X_MAX_PARENT_MIN|RS_Y_MAX_PARENT_MAX, [0, -8*6], [8*20, -8*5]);\r\n\t\tif (gethostcacheindexforkey(\"gamedir\") >= 0 || gethostcacheindexforkey(\"mod\") >= 0)\r\n\t\t\tm.add(menuitemcheck_spawn(_(\"Gamedir\"),\t\t\"sb_showgamedir\", \t\t[8*8, 8]),\tRS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MAX | RS_X_MAX_PARENT_MIN|RS_Y_MAX_PARENT_MAX, [0, -8*5], [8*20, -8*4]);\r\n\t\tif (gethostcacheindexforkey(\"numplayers\") >= 0)\r\n\t\t\tm.add(menuitemcheck_spawn(_(\"NumPlayers\"),\t\t\"sb_shownumplayers\",\t[8*8, 8]),\tRS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MAX | RS_X_MAX_PARENT_MIN|RS_Y_MAX_PARENT_MAX, [0, -8*4], [8*20, -8*3]);\r\n\t\tif (gethostcacheindexforkey(\"maxplayers\") >= 0)\r\n\t\t\tm.add(menuitemcheck_spawn(_(\"MaxPlayers\"),\t\t\"sb_showmaxplayers\",\t[8*8, 8]),\tRS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MAX | RS_X_MAX_PARENT_MIN|RS_Y_MAX_PARENT_MAX, [0, -8*3], [8*20, -8*2]);\r\n\t\tif (gethostcacheindexforkey(\"fraglimit\") >= 0)\r\n\t\t\tm.add(menuitemcheck_spawn(_(\"Fraglimit\"),\t\t\"sb_showfraglimit\", \t\t[8*8, 8]),\tRS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MAX | RS_X_MAX_PARENT_MIN|RS_Y_MAX_PARENT_MAX, [0, -8*2], [8*20, -8*1]);\r\n\t\tif (gethostcacheindexforkey(\"timelimit\") >= 0)\r\n\t\t\tm.add(menuitemcheck_spawn(_(\"Timelimit\"),\t\t\"sb_showtimelimit\", \t\t[8*8, 8]),\tRS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MAX | RS_X_MAX_PARENT_MIN|RS_Y_MAX_PARENT_MAX, [0, -8*1], [8*20, -8*0]);\r\n\t}\r\n};\r\n\r\n"
  },
  {
    "path": "quakec/menusys/menu/updates.qc",
    "content": "#include \"../menusys/mitem_grid.qc\"\r\n\r\nenum : int\r\n{\r\n\tGPMI_NAME,\t\t//name of the package, for use with the pkg command.\r\n\tGPMI_CATEGORY,\t\t//category text\r\n\tGPMI_TITLE,\t\t//name of the package, for showing the user.\r\n\tGPMI_VERSION,\t\t//version info (may have multiple with the same name but different versions)\r\n\tGPMI_DESCRIPTION,\t//some blurb\r\n\tGPMI_LICENSE,\t\t//what license its distributed under\r\n\tGPMI_AUTHOR,\t\t//name of the person(s) who created it\r\n\tGPMI_WEBSITE,\t\t//where to contribute/find out more info/etc\r\n\tGPMI_INSTALLED,\t\t//current state\r\n\tGPMI_ACTION,\t\t//desired state\r\n\tGPMI_AVAILABLE,\t\t//whether it may be downloaded or not.\r\n\tGPMI_FILESIZE,\t\t//whether it may be downloaded or not.\r\n\tGPMI_GAMEDIR,\t\t//whether it may be downloaded or not.\r\n\tGPMI_MAPS,\t\t\t//whether it may be downloaded or not.\r\n};\r\n\r\nclass mitem_updategrid : mitem_grid\r\n{\r\n\tvirtual void(vector pos) item_draw =\r\n\t{\t//make sure we see any updates as they're detected...\r\n\t\t__using getpackagemanagerinfo, ftoi;\r\n\t\tif (getpackagemanagerinfo(grid_numchildren, GPMI_NAME))\r\n\t\t{\r\n\t\t\tgrid_numchildren++;\r\n\t\t\twhile(getpackagemanagerinfo(grid_numchildren, GPMI_NAME))\r\n\t\t\t\tgrid_numchildren++;\r\n\t\t\titem_resized();\r\n\t\t}\r\n\t\tsuper::item_draw(pos);\r\n\t};\r\n\tvirtual void(vector pos, float idx) grid_draw;\r\n\tvirtual float(vector pos, float scan, float char, float down, float idx) grid_keypress;\r\n//\tvirtual void(float olditem, float newitem) grid_selectionchanged;\r\n};\r\nvoid(vector pos, float idx) mitem_updategrid::grid_draw =\r\n{\r\n\t__using getpackagemanagerinfo, ftoi;\r\n\tstring text = getpackagemanagerinfo(idx, GPMI_TITLE);\r\n\r\n\tvector col = item_rgb;\r\n\tif (item_flags & IF_MFOCUSED && idx == grid_mactive)\r\n\t\tcol_z = 0;\r\n\tif (item_flags & IF_KFOCUSED && idx == grid_kactive)\r\n\t\tcol_x = 0;\r\n\r\n\tstring status = getpackagemanagerinfo(idx, GPMI_INSTALLED);\r\n\tstring action = getpackagemanagerinfo(idx, GPMI_ACTION);\r\n\r\n\tif (action == \"purge\")\t\t//delete the thing\r\n\t\taction = \"^&F4 \";\r\n\telse if (action==\"reinstall\")\t//purge then reinstall(the purge status was explicit at least.)\r\n\t\taction = \"^&F2P\";\r\n\telse if (action==\"user\")\t//install(explcitly by user)\r\n\t\taction = \"^&F2 \";\r\n\telse if (action==\"auto\")\t//install(to satisfy dependancies)\r\n\t\taction = \"^&FA \";\r\n\telse if (action==\"disable\")\r\n\t\taction = \"^&FE \";\r\n\telse if (action==\"retain\")\r\n\t\taction = \"^&FE \";\r\n\telse\r\n\t\taction = \"^&F4 \";\r\n\r\n\tif (status == \"0%\" || status == \"pending\")\r\n\t{\r\n\t\tui.drawfill(pos, [item_size_x, item_scale], '0 0 0.5', 0.1, 0);\r\n\t\taction = status = \" \";\r\n\t}\r\n\telse if (strstrofs(status, \"%\")>=0)\r\n\t{\r\n\t\tfloat frac = stof(status)/100;\r\n\t\tui.drawfill(pos, [item_size_x*frac, item_scale], '0 0 0.5', 0.9, 0);\r\n\t\taction = status = \" \";\r\n\t}\r\n\telse if (status == \"corrupt\")\r\n\t\tstatus = \"^&FE?\";\t//yellow\r\n\telse if (status == \"enabled\")\r\n\t\tstatus = \"^&F2 \";\t//green\r\n\telse if (status == \"present\")\r\n\t\tstatus = \"^&FE \";\t//yellow\r\n\telse\r\n\t\tstatus = \"^&F4 \";\t//red\r\n\r\n\tui.drawstring(pos, strcat(status, action, \"^&F-\", text), '1 1 0' * item_scale, col, item_alpha, 0);\r\n};\r\n/*void(float olditem, float newitem) mitem_updategrid::grid_selectionchanged =\r\n{\r\n};\r\n*/float(vector pos, float scan, float char, float down, float idx) mitem_updategrid::grid_keypress =\r\n{\r\n\t__using getpackagemanagerinfo, ftoi;\r\n\tstring text;\r\n\tif (!down)\r\n\t\treturn FALSE;\r\n\telse if (scan == K_DEL||scan == K_BACKSPACE||scan == K_LEFTARROW)\r\n\t{\r\n\t\ttext = getpackagemanagerinfo(idx, GPMI_NAME);\r\n\t\tif (!text)\r\n\t\t\treturn FALSE;\r\n\r\n\t\tstring action = getpackagemanagerinfo(idx, GPMI_ACTION);\r\n\t\tif (action == \"purge\")\t\t//delete the thing\r\n\t\t\taction = \"rem\";\r\n\t\telse if (action==\"reinstall\")\t//purge then reinstall(the purge status was explicit at least.)\r\n\t\t\taction = \"rem\";\r\n\t\telse if (action==\"user\")\t//install(explcitly by user)\r\n\t\t\taction = \"rem\";\r\n\t\telse if (action==\"auto\")\t//install(to satisfy dependancies)\r\n\t\t\taction = \"rem\";\r\n\t\telse \r\n\t\t\taction = \"del\";\r\n\r\n\t\ttext = strcat(\"pkg quiet_\", action, \" \\\"\", text, \"\\\"\\n\");\r\n\t\tlocalcmd(text);\r\n\t}\r\n\telse if (scan == K_RIGHTARROW || scan==K_ENTER || scan == K_MOUSE1 || scan == K_TOUCHTAP)\r\n\t{\r\n\t\ttext = getpackagemanagerinfo(idx, GPMI_NAME);\r\n\t\tif (!text)\r\n\t\t\treturn FALSE;\r\n\t\ttext = strcat(\"pkg quiet_add \\\"\", text, \"\\\"\\n\");\r\n\t\tlocalcmd(text);\r\n\t}\r\n\telse\r\n\t\treturn FALSE;\r\n\treturn TRUE;\r\n};\r\n\r\n\r\n\r\n\r\nclass menu_updates : mitem_exmenu\r\n{\r\n\tstring filter;\r\n\r\n\tmitem_updategrid grid;\r\n\r\n\tvirtual string(string key) get =\r\n\t{\r\n\t\t__using getpackagemanagerinfo, ftoi;\r\n\t\tif (key == \"info\")\r\n\t\t{\r\n\t\t\tstring text=__NULL__, tmp;\r\n\t\t\ttmp = getpackagemanagerinfo(grid.grid_kactive, GPMI_VERSION);\t\ttext = strcat(text, \"^aVersion:^a \", tmp?:\"Unspecified\");\r\n\t\t\ttmp = getpackagemanagerinfo(grid.grid_kactive, GPMI_AUTHOR);\t\ttext = strcat(text, \"\\n^aAuthor:^a \", tmp?:\"Unknown\");\r\n\t\t\ttmp = getpackagemanagerinfo(grid.grid_kactive, GPMI_LICENSE);\t\ttext = strcat(text, \"\\n^aLicense:^a \", tmp?:\"Unknown\");\r\n\t\t\ttmp = getpackagemanagerinfo(grid.grid_kactive, GPMI_WEBSITE);\t\ttext = strcat(text, \"\\n^aWebsite:^a \", tmp?:\"Unknown\");\r\n\t\t\ttmp = getpackagemanagerinfo(grid.grid_kactive, GPMI_DESCRIPTION);\ttext = strcat(text, \"\\n^aDescription:^a \", tmp?:\"No description specified\");\r\n\t\t\treturn text;\r\n\t\t}\r\n\t\treturn super::get(key);\r\n\t};\r\n};\r\n\r\n\r\n\r\nvoid(mitem_desktop desktop) M_Menu_Updates =\r\n{\r\n\tmitem it;\r\n\tfloat h = (480+240)/2;\r\n\r\n\tif (!checkbuiltin(getpackagemanagerinfo))\r\n\t\treturn;\t//not supported in this engine...\r\n\r\n\tlocalcmd(\"pkg update\\n\");\r\n\r\n\t//create the menu, give it focus, and make sure its displayed over everything else.\r\n\tmenu_updates m = spawn(menu_updates, item_text:_(\"Updates List\"), item_flags:IF_SELECTABLE, item_command:\"m_main\", frame_stdheight:h);\r\n\tdesktop.add(m, RS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MIN | RS_X_MAX_PARENT_MAX|RS_Y_MAX_PARENT_MAX, '0 0', '0 0');\r\n\tdesktop.item_focuschange(m, IF_KFOCUSED);\r\n\tm.totop();\r\n\r\n\t//draw title art above the options\r\n\tmitem_pic banner = spawn(mitem_pic, item_text:\"gfx/ttl_cstm.lmp\", item_size_y:24, item_flags:IF_CENTERALIGN);\r\n\tm.add(banner, RS_X_MIN_PARENT_MID|RS_Y_MIN_PARENT_MID | RS_X_MAX_PARENT_MID|RS_Y_MAX_OWN_MIN, [banner.item_size_x*-0.5, h*-0.5], [banner.item_size_x*0.5, 24]);\r\n\r\n\t//spawn our grid for the actual options. this provides a scrollbar if we have too many items.\r\n\tm.grid = spawn(mitem_updategrid, item_flags: IF_SELECTABLE, item_scale: 8);\r\n\tm.add(m.grid, RS_X_MIN_PARENT_MID|RS_Y_MIN_PARENT_MID | RS_X_MAX_PARENT_MID|RS_Y_MAX_PARENT_MID, [-160, h*-0.5+32], [40, h*0.5]);\r\n\r\n\tit = spawn(mitem_label, item_text: \"info\", item_flags: 0, item_scale: 8);\r\n\tm.add(it, RS_X_MIN_PARENT_MID|RS_Y_MIN_PARENT_MID | RS_X_MAX_PARENT_MAX|RS_Y_MAX_PARENT_MID, [40, h*-0.5+32], [0, h*0.5-16]);\r\n\r\n\tit = spawn(mitem_button, item_text: \"Apply\", item_command: \"pkg apply\\n\", item_flags: IF_SELECTABLE, item_scale: 8);\r\n\tm.add(it, RS_X_MIN_PARENT_MID|RS_Y_MIN_PARENT_MID | RS_X_MAX_PARENT_MAX|RS_Y_MAX_PARENT_MID, [40+1, h*0.5-16+1], [0-1, h*0.5-1]);\r\n\r\n\t//and give us a suitable menu tint too, just because.\r\n\taddmenuback(m);\r\n};\r\n"
  },
  {
    "path": "quakec/menusys/menu.src",
    "content": "#pragma progs_dat \"../menu.dat\"\r\n\r\n//#pragma target fte\r\n#define MENU\t\t\t\t\t//select the module\r\n#include \"fteextensions.qc\"\t\t//also sets up system defs\r\n\r\n#includelist\r\nmenusys/mitems.qc\t\t\t\t//root item type\r\nmenusys/mitems_common.qc\t\t//basic types\r\nmenusys/mitem_desktop.qc\t\t//other sort of root item\r\nmenusys/mitem_exmenu.qc\t\t\t//fullscreen/exclusive menus\r\nmenusys/mitem_edittext.qc\t\t//simple text editor\r\nmenusys/mitem_tabs.qc\t\t\t//tabs\r\nmenusys/mitem_colours.qc\t\t//colour picker\r\nmenusys/mitem_checkbox.qc\t\t//checkbox (boolean thingies)\r\nmenusys/mitem_slider.qc\t\t\t//scrollbars\r\nmenusys/mitem_combo.qc\t\t\t//multiple-choice thingies\r\nmenusys/mitem_bind.qc\t\t\t//key binding thingie\r\nmenusys/mitem_spinnymodel.qc\t//menu art\r\n#endlist\r\n\r\n//might as well put this here.\r\nvoid(mitem_desktop desktop) M_Pop =\r\n{\r\n\tmitem it = desktop.item_kactivechild;\r\n\tif (it)\r\n\t\tit.item_remove();\r\n};\r\n\r\n//define the commands.\r\n//cmd argments are: Name, Function, Sourcefile(may be empty)\r\n#define concommandslist\t\t\t\t\t\t\t\t\t\\\r\n\tcmd(\"m_main\",\t\tM_Main,\t\t\t\t\tmenu/main.qc,\t\t\t\t\"Main Menu\")\t\\\r\n\tcmd(\"m_pop\",\t\tM_Pop,\t\t\t\t\t,\t\t\t\t\t\t\t__NULL__)\t\\\r\n\tcmd(\"m_options\",\tM_Options,\t\t\t\tmenu/options.qc,\t\t\t__NULL__)\t\\\r\n\tcmd(\"m_keys\",\t\tM_Options_Keys,\t\t\tmenu/options_keys.qc,\t\t\"Fix up the stupid default key bindings.\")\t\\\r\n\tcmd(\"m_basicopts\",\tM_Options_Basic,\t\tmenu/options_basic.qc,\t\t\"Change your public player settings, like name.\")\t\\\r\n\tcmd(\"m_video\",\t\tM_Options_Video,\t\tmenu/options_video.qc,\t\t\"Change video modes and devices.\")\t\\\r\n\tcmd(\"m_effects\",\tM_Options_Effects,\t\tmenu/options_effects.qc,\t__NULL__)\t\\\r\n\tcmd(\"m_audio\",\t\tM_Options_Audio,\t\tmenu/options_audio.qc,\t\t\"Tweak audio/voip devices+volumes+etc.\")\t\\\r\n\tcmd(\"m_particles\",\tM_Options_Particles,\tmenu/options_particles.qc,\t__NULL__)\t\\\r\n\tcmd(\"m_hud\",\t\tM_Options_Hud,\t\t\tmenu/options_hud.qc,\t\t\"Select which HUD layout to use\")\t\\\r\n\tcmd(\"m_load\",\t\tM_Load,\t\t\t\t\tmenu/loadsave.qc,\t\t\t\"Load a saved game\")\t\\\r\n\tcmd(\"m_save\",\t\tM_Save,\t\t\t\t\t,\t\t\t\t\t\t\t\"Save the game for later, or for retconnoisseur purposes.\")\t\\\r\n\tcmd(\"m_quit\",\t\tM_Quit,\t\t\t\t\tmenu/quit.qc,\t\t\t\t\"Choose the cowards option\")\t\\\r\n\tcmd(\"m_mods\",\t\tM_Menu_Mods,\t\t\tmenu/mods.qc,\t\t\t\t\"Switch mod\")\t\\\r\n\tcmd(\"m_updates\",\tM_Menu_Updates,\t\t\tmenu/updates.qc,\t\t\t__NULL__)\t\\\r\n\tcmd(\"m_cvars\",\t\tM_Menu_Cvars,\t\t\tmenu/cvars.qc,\t\t\t\t\"Advanced confguration of all settings.\")\t\\\r\n\tcmd(\"m_newgame\",\tM_NewGame,\t\t\t\tmenu/newgame.qc,\t\t\t\"Start a new game!\")\t\\\r\n\tcmd(\"m_servers\",\tM_Servers,\t\t\t\tmenu/servers.qc,\t\t\t\"Join someone else's game\")\t\\\r\n\tcmd(\"m_configs\",\tM_Configs,\t\t\t\tmenu/options_configs.qc,\t__NULL__)\t\\\r\n\tcmd(\"m_reset\",\t\tM_Reset,\t\t\t\t,\t\t\t\t\t\t\t__NULL__)\t\\\r\n\tcmd(\"m_dir\",\t\tM_Dir,\t\t\t\t\t,\t\t\t\t\t\t\t\"Debug command to list data files (from qc's perspective).\")\t\\\r\n\tcmd(\"m_cat\",\t\tM_FOpen,\t\t\t\t,\t\t\t\t\t\t\t\"Debug command to display the contents of data files (with qc's access rights).\")\t\\\r\n\tcmd(\"m_preset\",\t\tM_Preset,\t\t\t\tmenu/presets.qc,\t\t\t__NULL__)\r\n\t\r\n//make sure all the right files are included\r\n#define cmd(n,fnc,inc,desc) inc\r\n#includelist\r\n\tconcommandslist\r\n#endlist\r\n#undef cmd\r\n\r\nmitem_desktop desktop;\r\nvoid() m_shutdown = {};\r\nvoid(vector screensize) m_draw =\r\n{\r\n\tif (dp_workarounds)\r\n\t\tcltime = gettime(0);\r\n\titems_draw(desktop, screensize);\r\n};\r\nfloat(float evtype, float scanx, float chary, float devid) Menu_InputEvent = {\r\n\tif (scanx == K_TOUCH)\r\n\t\treturn TRUE;\t//always report this as handled. we'll not get fake mouse events then, and can handle K_TOUCHLONG/K_TOUCHTAP/etc without worry of conflicts.\r\n\treturn items_keypress(desktop, evtype, scanx, chary, devid);\r\n};\r\nvoid(float scan, float chr) m_keydown = {ui.mousepos = getmousepos();queryscreensize();items_keypress(desktop, IE_KEYDOWN, scan, chr, 0);};\t//for DP compat.\r\nvoid(float scan, float chr) m_keyup = {ui.mousepos = getmousepos();queryscreensize();items_keypress(desktop, IE_KEYUP, scan, chr, 0);};\t\t//for DP compat.\r\nvoid(float mode) m_toggle\r\n{\t//mode is stupid. 1=enable,0=disable,-1=actually toggle.\r\n\tif (mode < 0)\r\n\t\tmode = !desktop.item_kactivechild;\r\n\tif (mode)\r\n\t\tM_Main(desktop);\r\n\telse while(desktop.item_kactivechild)\r\n\t{\r\n\t\tmitem it = desktop.item_kactivechild;\r\n\t\tif (it.item_flags & IF_NOKILL)\r\n\t\t\tbreak;\r\n\t\tit.item_remove();\r\n\t}\r\n\r\n\titems_updategrabs(TRUE);\r\n};\r\nfloat(string cstr) m_consolecommand =\r\n{\t//for properly registered commands\r\n\ttokenize_console(cstr);\r\n\tstring cmd = argv(0);\r\n\r\n\tswitch(cmd)\r\n\t{\r\n//switch on the known commands.\r\n#define cmd(n,f,inc,desc) case n: f(desktop); break;\r\n\tconcommandslist\r\n#undef cmd\r\n\tdefault:\r\n\t\treturn FALSE;\r\n\t}\r\n\titems_updategrabs(TRUE);\r\n\treturn TRUE;\r\n}\r\nvoid(string cstr) GameCommand =\r\n{\t//'menu_cmd' hack for compat with DP, used via aliases and mess, instead of using the m_consolecommand entrypoint.\r\n\ttokenize_console(cstr);\r\n\tstring cmd = argv(0);\r\n\r\n\tswitch(cmd)\r\n\t{\r\n//switch on the known commands.\r\n#define cmd(n,f,inc,desc) case n: f(desktop); break;\r\n\tconcommandslist\r\n#undef cmd\r\n\tdefault:\r\n\t\tprint(\"unknown command \", cmd, \"\\n\");\r\n\t\treturn;\r\n\t}\r\n\titems_updategrabs(TRUE);\r\n};\r\n\r\n\r\nvar float autocvar_dp_workarounds = FALSE;\r\nvoid() m_init =\r\n{\r\n\tFingerprintEngine();\r\n\r\n\tdesktop = spawn(mitem_desktop);\r\n\r\n\tif (checkbuiltin(registercommand))\r\n\t{\r\n#define cmd(n,f,inc,desc) registercommand(n,desc);\r\n\t\tconcommandslist\r\n#undef cmd\r\n\t}\r\n\telse\r\n\t{\r\n\t\t//register the console commands via the alias command. ugly.\r\n#define cmd(n,f,inc,desc) localcmd(\"alias \" n \" \\\"menu_cmd \" n \" $*\\\"\\n\");\r\n\t\tconcommandslist\r\n#undef cmd\r\n\t}\r\n\r\n\t//work around some dp differences/bugs.\r\n\tif (autocvar(dp_workarounds, FALSE))\r\n\t\tdp_workarounds = TRUE;\r\n\t\r\n\tif (dp_workarounds)\r\n\t\tprint(\"^1WORKING AROUND DP BUGS\\n\");\r\n\t\r\n\t//for compat with DP, 'none' is the default cursor in menuqc.\r\n\t//naturally this is not ideal.\r\n\tif (checkextension(\"FTE_QC_HARDWARECURSORS\"))\r\n\t\tsetcursormode(TRUE, \"\");\r\n\telse\r\n\t\tprint(\"No hardware cursors\\n\");\r\n\r\n\tif (clientstate() == 1)\t//disconnected==1, supposedly\r\n\t\tm_toggle(1);\r\n};\r\n\r\n"
  },
  {
    "path": "quakec/menusys/menusys/mitem_bind.qc",
    "content": "/***************************************************************************\r\nkey binding item.\r\ninteractable - queries binds for a command, and accepts new scan codes to bind for its given command.\r\n*/\r\nclass mitem_bind : mitem\r\n{\r\n\tvirtual void(vector pos) item_draw;\r\n\tvirtual float(vector pos, float scan, float char, float down) item_keypress;\r\n\tvirtual void(mitem newfocus, float flag) item_focuschange;\r\n\r\n\tvoid() mitem_bind =\r\n\t{\r\n\t\titem_scale = item_size[1];\r\n\r\n\t\titem_flags |= IF_SELECTABLE;\r\n\t\t\r\n\t\titem_text = strzone(item_text);\r\n\t\titem_command = strzone(item_command);\r\n\t};\r\n\tvirtual void() item_remove =\r\n\t{\r\n\t\tstrunzone(item_text);\r\n\t\tstrunzone(item_command);\r\n\t};\r\n};\r\n#define menuitembind_spawn(text,command,sz)\t\\\r\n\tspawn(mitem_bind,\t\t\t\\\r\n\t\titem_text:\t\ttext,\t\t\\\r\n\t\titem_command:\tcommand,\t\\\r\n\t\titem_size:\t\tsz\t\t\\\r\n\t\t)\r\n\r\nvoid(vector pos) mitem_bind::item_draw =\r\n{\r\n#ifdef CSQC\r\n\ttokenize(findkeysforcommandex(self.item_command));\r\n\tstring key1 = argv(0);\r\n\tstring key2 = argv(1);\r\n#else\r\n\t/*this is not my API...*/\r\n\ttokenize(findkeysforcommand(self.item_command));\r\n\tstring key1 = argv(0);\r\n\tstring key2 = argv(1);\r\n\tif (key1 != \"\") key1 = (key1==\"-1\")?\"\":keynumtostring(stof(key1));\r\n\tif (key2 != \"\") key2 = (key2==\"-1\")?\"\":keynumtostring(stof(key2));\r\n#endif\r\n\r\n\tsuper::item_draw(pos);\r\n\tpos_x += self.item_size_x / 2;\r\n\r\n\tif (self.item_flags & IF_INTERACT)\r\n\t{\r\n\t\tui.drawstring(pos, \"Please press a key\", '1 1 0' * self.item_scale, menuitem_textcolour(self), self.item_alpha, 0);\r\n\t}\r\n\telse\r\n\t{\r\n\t\tui.drawstring(pos, key1, '1 1 0' * self.item_scale, menuitem_textcolour(self), self.item_alpha, 0);\r\n\t\tpos_x += stringwidth(key1, TRUE, '1 1 0'*self.item_scale);\r\n\r\n\t\tif (key2 != \"\")\r\n\t\t{\r\n\t\t\tui.drawstring(pos, \"  or  \", '1 1 0' * self.item_scale, menuitem_textcolour(self), self.item_alpha, 0);\r\n\t\t\tpos_x += stringwidth(\"  or  \", TRUE, '1 1 0'*self.item_scale);\r\n\r\n\t\t\tui.drawstring(pos, key2, '1 1 0' * self.item_scale, menuitem_textcolour(self), self.item_alpha, 0);\r\n//\t\t\tpos_x += stringwidth(key2, TRUE, '1 1 0'*self.item_scale);\r\n\t\t}\r\n\t}\r\n};\r\nfloat(vector pos, float scan, float char, float down) mitem_bind::item_keypress =\r\n{\r\n\tif (!down)\r\n\t\treturn FALSE;\r\n\r\n\tif (self.item_flags & IF_INTERACT)\r\n\t{\r\n\t\tif (scan == K_ESCAPE || scan == K_GP_START)\t//keys that cannot/shouldnot be bound can be used to cancel here.\r\n\t\t{\r\n\t\t}\r\n\t\telse if (scan)\r\n\t\t\tlocalcmd(sprintf(\"bind \\\"%s\\\" \\\"%s\\\"\\n\", keynumtostring(scan), self.item_command));\r\n\t\telse\r\n\t\t\treturn FALSE;\r\n\t\tself.item_flags -= IF_INTERACT;\r\n\t\treturn TRUE;\r\n\t}\r\n\telse\r\n\t{\r\n\t\tif (scan == K_ENTER || scan == K_GP_A || ((scan == K_TOUCHTAP || scan == K_MOUSE1) && mouseinbox(pos, self.item_size)))\r\n\t\t{\r\n\t\t\tself.item_flags |= IF_INTERACT;\r\n\t\t\treturn TRUE;\r\n\t\t}\r\n\t\tif (scan == K_DEL || scan == K_GP_Y || scan == K_BACKSPACE)\r\n\t\t{\r\n#ifdef CSQC\r\n\t\t\tfloat c = tokenize(findkeysforcommandex(self.item_command));\r\n\t\t\tfor (float i = 0; i < c; i++)\r\n\t\t\t\tlocalcmd(sprintf(\"bind \\\"%s\\\" \\\"\\\"\\n\", argv(i)));\r\n#else\r\n\t\t\t/*again, this is not my API...*/\r\n\t\t\ttokenize(findkeysforcommand(self.item_command));\r\n\t\t\tstring key1 = argv(0);\r\n\t\t\tstring key2 = argv(1);\r\n\t\t\tif (key1 != \"\") key1 = (key1==\"-1\")?\"\":keynumtostring(stof(key1));\r\n\t\t\tif (key2 != \"\") key2 = (key2==\"-1\")?\"\":keynumtostring(stof(key2));\r\n\t\t\tif (key1 != \"\") localcmd(sprintf(\"bind \\\"%s\\\" \\\"\\\"\\n\", key1));\r\n\t\t\tif (key2 != \"\") localcmd(sprintf(\"bind \\\"%s\\\" \\\"\\\"\\n\", key2));\r\n#endif\r\n\t\t\treturn TRUE;\r\n\t\t}\r\n\t\treturn FALSE;\r\n\t}\r\n};\r\nvoid(mitem newfocus, float flag) mitem_bind::item_focuschange =\r\n{\r\n\tif (!(self.item_flags & IF_KFOCUSED))\r\n\t\tself.item_flags = self.item_flags - (self.item_flags & IF_INTERACT);\r\n};\r\n\r\n"
  },
  {
    "path": "quakec/menusys/menusys/mitem_checkbox.qc",
    "content": "/***************************************************************************\r\ncheckbox, directly linked to a cvar.\r\n*/\r\nclass mitem_check : mitem\r\n{\r\n\tvirtual float(vector pos, float scan, float char, float down) item_keypress =\r\n\t{\r\n\t\tif (!down)\r\n\t\t\treturn FALSE;\r\n\t\r\n\t\tif (ISCONFIRMKEY(scan) || scan == K_SPACE || ISLEFTARROW(scan) || ISRIGHTARROW(scan) || scan == K_MOUSE1 || scan == K_TOUCHTAP)\r\n\t\t{\r\n\t\t\tpos_x += this.item_size_x / 2;\r\n//\t\t\tif (ui.mousepos[0] > pos_x || scan != K_MOUSE1)\t//don't do anything if they clicked the bit on the left to select it\r\n\t\t\t\tset(item_command, ftos(!stof(get(item_command))));\r\n\t\t\treturn TRUE;\r\n\t\t}\r\n\t\telse if ((scan == K_DEL || scan == K_GP_Y) && down && cvar_type(item_command))\r\n\t\t\tset(item_command, cvar_defstring(item_command));\r\n\t\treturn FALSE;\r\n\t};\r\n\tvirtual void(vector pos) item_draw =\r\n\t{\r\n\t\tvector rgb = item_rgb;\r\n\t\tif (!(item_flags & IF_SELECTABLE))\r\n\t\t\trgb *= 0.2;\r\n\t\tlocal float truth = stof(get(item_command));\r\n\r\n\t\tsuper::item_draw(pos);\r\n\t\tpos_x += item_size_x / 2;\r\n\r\n\t\tif (dp_workarounds)\r\n\t\t{\t//lame, but whatever\r\n\t\t\tui.drawstring(pos, chr2str(0xe080, 0xe082), '1 1 0' * this.item_scale, rgb, this.item_alpha, 0);\r\n\t\t\tif (truth)\r\n\t\t\t\tui.drawstring(pos + '4 0', chr2str(0xe083), '1 1 0' * this.item_scale, rgb, this.item_alpha, 0);\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tui.drawstring(pos, \"^{e080}^{e082}\", '1 1 0' * this.item_scale, rgb, this.item_alpha, 0);\r\n\t\t\tif (truth)\r\n\t\t\t\tui.drawstring(pos + '4 0', \"^{e083}\", '1 1 0' * this.item_scale, rgb, this.item_alpha, 0);\r\n\t\t}\r\n\t};\r\n\tvoid() mitem_check =\r\n\t{\r\n\t\tif (!item_scale)\r\n\t\t\titem_scale = 8;\r\n\t\tif (!item_size_y)\r\n\t\t\titem_size_y = item_scale;\r\n\t\tif (isvalid(item_command))\r\n\t\t\titem_flags |= IF_SELECTABLE;\r\n\t};\r\n\t\r\n\tvirtual void() item_resized =\r\n\t{\r\n\t\tif (isvalid(item_command))\r\n\t\t\titem_flags |= IF_SELECTABLE;\t\t\r\n\t\telse\r\n\t\t\titem_flags &= ~IF_SELECTABLE;\r\n\t\tsuper::item_resized();\r\n\t};\r\n};\r\n\r\n//optional, can spawn direcly\r\nmitem_check(string text, string command, vector sz) menuitemcheck_spawn =\r\n{\r\n\treturn spawn(mitem_check, item_scale: sz_y, item_text: text, item_size: sz, item_command: command);\r\n};\r\n"
  },
  {
    "path": "quakec/menusys/menusys/mitem_colours.qc",
    "content": "/***************************************************************************\r\nhue selection thing, directly linked to a cvar.\r\nWe hard code 1 in the saturation+value arguments for the selection\r\n\r\nScrew Quake colours, they're too brown!\r\n*/\r\nclass mitem_colours : mitem\r\n{\r\n\tfloat quakecolours;\r\n\tvirtual void(vector pos) item_draw;\r\n\tvirtual float(vector pos, float scan, float char, float down) item_keypress;\r\n\t\r\n\tvirtual void() item_resized =\r\n\t{\r\n\t\tif (isvalid(item_command))\r\n\t\t\titem_flags |= IF_SELECTABLE;\t\t\r\n\t\telse\r\n\t\t\titem_flags &= ~IF_SELECTABLE;\r\n\t\tsuper::item_resized();\r\n\t};\r\n};\r\n\r\n//http://axonflux.com/handy-rgb-to-hsl-and-rgb-to-hsv-color-model-c\r\nstatic vector(vector rgb) rgbtohsv =\r\n{\r\n\tfloat r = rgb_x, g = rgb_y, b = rgb_z;\r\n\tfloat maxc = max(r, g, b), minc = min(r, g, b);\r\n    \tfloat h, s, l = (maxc + minc) / 2;\r\n\r\n\tlocal float d = maxc - minc;\r\n\tif (maxc)\r\n\t\ts = d / maxc;\r\n\telse\r\n\t\ts = 0;\r\n\r\n\tif(maxc == minc)\r\n\t{\r\n\t\th = 0; // achromatic\r\n\t}\r\n\telse\r\n\t{\r\n\t\tif (maxc == r)\r\n\t\t\th = (g - b) / d + ((g < b) ? 6 : 0);\r\n\t\telse if (maxc == g)\r\n\t\t\th = (b - r) / d + 2;\r\n\t\telse\r\n\t\t\th = (r - g) / d + 4;\r\n\t\th /= 6;\r\n    }\r\n\r\n    return [h, s, l];\r\n};\r\n\r\n//http://axonflux.com/handy-rgb-to-hsl-and-rgb-to-hsv-color-model-c\r\nstatic vector(vector hsv) hsvtorgb =\r\n{\r\n\tfloat h = hsv_x, s = hsv_y, v = hsv_z;\r\n\tfloat r=0, g=1, b=0;\r\n\r\n\twhile(h < 0)\r\n\t\th+=1;\r\n\twhile(h >= 1)\r\n\t\th-=1;\r\n\r\n\tfloat i = floor(h * 6);\r\n\tfloat f = h * 6 - i;\r\n\tfloat p = v * (1 - s);\r\n\tfloat q = v * (1 - f * s);\r\n\tfloat t = v * (1 - (1 - f) * s);\r\n\tswitch(i)\r\n\t{\r\n\t\tcase 0: r = v, g = t, b = p; break;\r\n\t\tcase 1: r = q, g = v, b = p; break;\r\n\t\tcase 2: r = p, g = v, b = t; break;\r\n\t\tcase 3: r = p, g = q, b = v; break;\r\n\t\tcase 4: r = t, g = p, b = v; break;\r\n\t\tcase 5: r = v, g = p, b = q; break;\r\n\t}\r\n\r\n\treturn [r, g, b];\r\n};\r\n\r\nfloat(float chr) nibbletofloat =\r\n{\r\n\tchr = chr&0xff;\r\n\tif (chr >= '0' && chr <= '9')\r\n\t\treturn chr - '0';\r\n\tif (chr >= 'a' && chr <= 'f')\r\n\t\treturn chr - 'a' + 10;\r\n\tif (chr >= 'A' && chr <= 'F')\r\n\t\treturn chr - 'A' + 10;\r\n\treturn 0;\r\n};\r\n\r\nstatic vector(string v) hextorgb =\r\n{\r\n\tif (!strncmp(v, \"0x\", 2))\r\n\t{\r\n\t\tvector r;\r\n\t\tr_x = (nibbletofloat(str2chr(v, 2))*16 + nibbletofloat(str2chr(v, 3)))/255;\r\n\t\tr_y = (nibbletofloat(str2chr(v, 4))*16 + nibbletofloat(str2chr(v, 5)))/255;\r\n\t\tr_z = (nibbletofloat(str2chr(v, 6))*16 + nibbletofloat(str2chr(v, 7)))/255;\r\n\t\treturn r;\r\n\t}\r\n\telse\r\n\t{\r\n\t\tfloat legacycolour = stof(v);\r\n\t\tswitch(legacycolour)\r\n\t\t{\r\n\t\tcase  0:\treturn [0xeb, 0xeb, 0xeb]/255;\r\n\t\tcase  1:\treturn [0x8f, 0x6f, 0x23]/255;\r\n\t\tcase  2:\treturn [0x8b, 0x8b, 0xcb]/255;\r\n\t\tcase  3:\treturn [0x6b, 0x6b, 0x0f]/255;\r\n\t\tcase  4:\treturn [0x7f, 0x00, 0x00]/255;\r\n\t\tcase  5:\treturn [0xaf, 0x67, 0x23]/255;\r\n\t\tcase  6:\treturn [0xff, 0xf3, 0x1b]/255;\r\n\t\tcase  7:\treturn [0xe3, 0xb3, 0x97]/255;\r\n\t\t\r\n\t\tcase  8:\treturn [0xab, 0x8b, 0xa3]/255;\r\n\t\tcase  9:\treturn [0xbb, 0x73, 0x97]/255;\r\n\t\tcase 10:\treturn [0xdb, 0xc3, 0xbb]/255;\r\n\t\tcase 11:\treturn [0x6f, 0x83, 0x7b]/255;\r\n\t\tcase 12:\treturn [0xff, 0xf3, 0x1b]/255;\r\n\t\tcase 13:\treturn [0x00, 0x00, 0xff]/255;\r\n\t\t//14+15 are fullbrights, so not valid.\r\n\r\n\t\tdefault:\r\n\t\t\treturn '0 0 0';\r\n\t\t}\r\n\t}\r\n};\r\nstatic string(vector v) rgbtohex =\r\n{\r\n\tv *= 255;\r\n\treturn sprintf(\"0x%02x%02x%02x\", v_x, v_y, v_z);\r\n};\r\n\r\nvoid(vector pos) mitem_colours::item_draw =\r\n{\r\n\tlocal float step;\r\n\tlocal float stride;\r\n\tlocal string curval;\r\n\tlocal vector rgb;\r\n\r\n\tsuper::item_draw(pos);\r\n\r\n\t//calculate the rgb from hue at each step across the colour block\r\n\tpos_x += item_size_x / 2;\r\n\r\n\tif (ui.mgrabs == this)\r\n\t{\r\n\t\tfloat frac;\r\n\t\t//if we're sliding it, update the value\r\n\t\tfrac = (ui.mousepos[1] - pos_x-(item_size_y+4)) / (item_size_x / 2 - (item_size_y+4));\r\n\t\tif (frac >= 0 && frac <= 1)\r\n\t\t{\r\n\t\t\tset(item_command, rgbtohex(hsvtorgb([frac, 1, 1])));\r\n\t\t}\r\n\t}\r\n\tcurval = get(item_command);\r\n\r\n\tif (quakecolours)\r\n\t{\r\n#define STEPS 14\r\n\t\tstride = (item_size_x / 2) / STEPS;\r\n\t\tfor (step = 0; step < STEPS; step++, pos_x += stride)\r\n\t\t{\r\n\t\t\tstring v = ftos(step);\r\n\t\t\trgb = hextorgb(v);\r\n\t\t\tif (!(item_flags & IF_SELECTABLE))\r\n\t\t\t\trgb *= 0.2;\r\n\t\t\trgb *= 0.5;\r\n\t\t\tif (v == curval)\r\n\t\t\t{\r\n\t\t\t\tui.drawfill(pos, [stride, item_size_y], rgb*2, item_alpha, 0);\r\n\t\t\t\trgb *= 1 + sin(time*4)*.2;\r\n\t\t\t\tui.drawfill(pos+[0,2], [stride, item_size_y-4], rgb, item_alpha, 0);\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t\tui.drawfill(pos+[0,2], [stride, item_size_y-4], rgb, item_alpha, 0);\r\n\t\t}\r\n\t\treturn;\r\n#undef STEPS\r\n\t}\r\n\r\n#define STEPS 32\r\n\tstride = (item_size_x / 2 - (item_size_y+4)) / STEPS;\r\n\r\n\tui.drawfill(pos, [item_size_y, item_size_y], hextorgb(curval), item_alpha, 0);\r\n\tpos_x += item_size_y+4;\r\n\r\n\tpos_y += 1;\r\n#if defined(MENU) || 1\r\n\tfor (step = 0; step < STEPS; step += 1, pos_x += stride)\r\n\t{\r\n\t\trgb = hsvtorgb([step/STEPS, 1, 1]);\r\n\t\tif (!(item_flags & IF_SELECTABLE))\r\n\t\t\trgb *= 0.2;\r\n\t\tui.drawfill(pos, [stride, item_size_y-2], rgb, item_alpha, 0);\r\n\t}\r\n#else\r\n//FIXME: WTF is going on here? it comes out as black? wtf?\r\n\t//draw quads (we should probably not use an internal-to-engine shader here...)\r\n\tR_BeginPolygon(\"fill_opaque\", 4);\t//outside so we can skip it for faster reuse by avoiding lookups\r\n\trgb = hsvtorgb([0, 1, 1]);\r\n\tfor (step = 0; step < STEPS;)\r\n\t{\r\n\t\tR_PolygonVertex([pos_x, pos_y+item_size_y-2], '0 1', rgb, item_alpha);\r\n\t\tR_PolygonVertex([pos_x, pos_y], '0 0', rgb, 1);\r\n\r\n\t\tpos_x += stride;\r\n\t\tstep += 1;\r\n\t\trgb = hsvtorgb([step/STEPS, 1, 1]);\r\n\r\n\t\tR_PolygonVertex([pos_x, pos_y], '1 0', rgb, 1);\r\n\t\tR_PolygonVertex([pos_x, pos_y+item_size_y-2], '1 1', rgb, item_alpha);\r\n\r\n\t\tR_EndPolygon();\r\n\t}\r\n#endif\r\n#undef STEPS\r\n};\r\n\r\nfloat(vector pos, float scan, float char, float down) mitem_colours::item_keypress =\r\n{\r\n\tif (!down)\r\n\t{\r\n\t\tif (ui.mgrabs == this)\r\n\t\t\tui.mgrabs = __NULL__;\r\n\t\treturn FALSE;\r\n\t}\r\n\tif (scan == K_MWHEELUP || scan == K_MWHEELDOWN)\r\n\t{\r\n\t\tif (mouseinbox(pos, item_size))\r\n\t\t\tscan = ((scan == K_MWHEELDOWN)?K_LEFTARROW:K_RIGHTARROW);\r\n\t}\r\n\r\n\r\n\tlocal float curval = rgbtohsv(hextorgb(get(item_command)))[0];\r\n\tif (scan == K_TOUCHTAP || scan == K_MOUSE1)\r\n\t{\r\n\t\tfloat width = item_size_x / 2;\r\n\t\tpos_x += width;\r\n\t\tif (!quakecolours)\r\n\t\t{\r\n\t\t\tpos_x += item_size_y+4;\r\n\t\t\twidth = width - item_size_y+4;\r\n\t\t}\r\n\t\tcurval = (ui.mousepos[0] - pos_x) / width;\r\n\t\tif (curval < 0 || curval > 1)\r\n\t\t\treturn FALSE;\r\n\t\tcurval = curval;\r\n\t\tif (quakecolours)\r\n\t\t\tset(item_command, ftos(floor(curval*14)));\r\n\t\telse\r\n\t\t\tset(item_command, rgbtohex(hsvtorgb([(curval), 1, 1])));\r\n\t\tui.mgrabs = this;\r\n\t}\r\n\telse if (ISLEFTARROW(scan) || scan == K_SPACE)\r\n\t{\r\n\t\tset(item_command, rgbtohex(hsvtorgb([curval - (1/64.0), 1, 1])));\t//yay autorepeat\r\n\t}\r\n\telse if (ISRIGHTARROW(scan) || scan == K_ENTER)\r\n\t{\r\n\t\tset(item_command, rgbtohex(hsvtorgb([curval + (1/64.0), 1, 1])));\r\n\t}\r\n\telse\r\n\t\treturn FALSE;\r\n\treturn TRUE;\r\n};\r\nmitem_colours(string text, string command, float limited, vector sz) menuitemcolour_spawn =\r\n{\r\n\tmitem_colours n = spawn(mitem_colours);\r\n\tn.item_scale = sz_y;\r\n\tn.item_text = text;\r\n\tn.item_size = sz;\r\n\r\n\tn.quakecolours = limited;\r\n\r\n\tn.item_command = command;\r\n\tn.item_flags |= IF_SELECTABLE;\r\n\treturn n;\r\n};\r\n\r\n"
  },
  {
    "path": "quakec/menusys/menusys/mitem_combo.qc",
    "content": "/***************************************************************************\r\ncombo item.\r\nNo longer using pointers, now using a tokenized string. Less efficient, but saves creating lots of different arrays, just pass a string.\r\nThe string list is doubled, first is the actual value, second is the friendly text.\r\nWill show actual value when focused, and will show readable value when not.\r\nThe possible values is a separate popup.\r\n*/\r\n\r\n//FIXME: should probably set up a grabs to intercept right-click / escape outside of the item\r\n\r\nclass mitem_combo;\r\nclass mitem_combo_popup;\r\n\r\nclass mitem_combo : mitem_edit\r\n{\r\n\tvirtual void(vector pos)\t\t\t\t\t\t\t\titem_draw;\r\n\tvirtual float(vector pos, float scan, float char, float down)\titem_keypress;\r\n\tvirtual void(mitem newfocus, float changedflag)\t\t\t\titem_focuschange;\r\n\r\n\tmitem_combo_popup\t\tcpopup;\r\n\tstring\tmstrlist;\r\n\tfloat\t\tfirstrow;\r\n\tfloat\t\tvisrows;\r\n\r\n\tvirtual void() item_remove;\r\n\t\r\n\tvirtual void() item_resized =\r\n\t{\r\n\t\tif (isvalid(item_command))\r\n\t\t\titem_flags |= IF_SELECTABLE;\t\t\r\n\t\telse\r\n\t\t\titem_flags &= ~IF_SELECTABLE;\r\n\t\tsuper::item_resized();\r\n\t};\r\n\tvoid() mitem_combo =\r\n\t{\r\n\t\tspos = -1;\r\n\t};\r\n};\r\n\r\nclass mitem_combo_popup : mitem\r\n{\r\n\tvirtual void(vector pos)\t\t\t\t\t\t\t\titem_draw;\r\n\tvirtual float(vector pos, float scan, float char, float down)\titem_keypress;\r\n\tvirtual void(mitem newfocus, float changedflag)\t\t\t\titem_focuschange;\r\n\r\n\tmitem_combo \t\tpcombo;\r\n\tmitem_vslider\t\tpslider;\r\n\tfloat\t\t\tslidercache;\r\n\t\r\n\tvirtual void() item_remove =\r\n\t{\r\n\t\tif (pslider)\r\n\t\t\tpslider.item_remove();\r\n\t\tif (pcombo)\r\n\t\t\tpcombo.cpopup = 0;\r\n\t\tsuper::item_remove();\r\n\t};\r\n};\r\n\r\nvoid() mitem_combo::item_remove =\r\n{\r\n\tmitem_combo_popup p = cpopup;\r\n\tif (p)\r\n\t\tp.item_remove();\r\n\tstrunzone(mstrlist);\r\n\tsuper::item_remove();\r\n};\r\n\r\nvoid(vector pos) mitem_combo::item_draw =\r\n{\r\n\tlocal float i, v;\r\n\tlocal string curval = get(item_command);\r\n\tvector rgb = item_rgb;\r\n\tif (!(item_flags & IF_SELECTABLE))\r\n\t\trgb *= 0.2;\r\n\t\r\n\tif (cpopup)\r\n\t\tcpopup.item_position = pos + [item_size_x / 2, item_size_y];\r\n\r\n\tif (spos >= 0)\r\n\t{\r\n\t\tsuper::item_draw(pos);\r\n\t\treturn;\r\n\t}\r\n\telse\r\n\t\tmitem::item_draw(pos);\r\n\r\n\tv = tokenize(mstrlist);\r\n\r\n\t//display the friendly string if the current value matches\r\n//\tif (!(item_flags & IF_KFOCUSED) && (!cfriend || !(cfriend.item_flags & IF_KFOCUSED)))\r\n\t{\r\n\t\tfor (i = 0; i < v; i+=2)\r\n\t\t{\r\n\t\t\tif (argv(i) == curval)\r\n\t\t\t{\r\n\t\t\t\tcurval = argv(i+1);\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\tpos_x += item_size_x / 2;\r\n\r\n\r\n/*\t//border\r\n\tui.drawfill(pos, [item_size_x/2, 1], TD_BOT, item_alpha, 0);\r\n\tui.drawfill(pos, [1, item_size_y - 1], TD_RGT, item_alpha, 0);\r\n\tui.drawfill(pos + [item_size_x/2-1, 1], [1, item_size_y - 1], TD_LFT, item_alpha, 0);\r\n\tui.drawfill(pos + [0, item_size_y-1], [item_size_x/2, 1], TD_TOP, item_alpha, 0);\r\n*/\r\n\t//silly strings need to get cut off properly.\r\n\tui.setcliparea(pos[0], pos[1], item_size_x/2, item_size_y);\r\n\tpos_y += (item_size_y - item_scale)*0.5;\r\n\tpos_x += 1;\r\n\tui.drawstring(pos, curval, '1 1 0' * item_scale, rgb, item_alpha, 0);\r\n\tui.setcliparea(ui.drawrectmin[0], ui.drawrectmin[1], ui.drawrectmax[0] - ui.drawrectmin[0], ui.drawrectmax[1] - ui.drawrectmin[1]);\r\n};\r\nvoid(mitem newfocus, float flag) mitem_combo::item_focuschange =\r\n{\r\n\tif (!cpopup || !(flag & IF_KFOCUSED))\r\n\t\treturn;\t//don't care\r\n\tif (newfocus != (mitem)this && newfocus != (mitem)cpopup)\r\n\t{\r\n\t\tcpopup.item_size = cpopup.maxs = '0 0';\r\n\t\tcpopup.item_flags &~= IF_SELECTABLE;\r\n\t}\r\n\tspos = -1;\r\n};\r\nvoid(mitem newfocus, float flag) mitem_combo_popup::item_focuschange =\r\n{\r\n\tpcombo.item_focuschange(newfocus, flag);\r\n};\r\nvoid(vector pos) mitem_combo_popup::item_draw =\r\n{\r\n\tvector col;\r\n\tif (item_size_y < 1)\r\n\t\treturn;\r\n\r\n\tlocal mitem_combo f = pcombo;\r\n\titem_command = f.item_command;\r\n\tlocal string curval = f.get(f.item_command);\r\n\tlocal float i, m, c, v;\r\n\r\n\tvector popupsize = item_size;\r\n\r\n\t\r\n\r\n\tif (!((f.item_flags | item_flags) & IF_KFOCUSED))\r\n\t{\r\n\t\titem_size = maxs = '0 0';\r\n\t\titem_flags &~= IF_SELECTABLE;\r\n\t\treturn;\r\n\t}\r\n\r\n\tui.drawfill(pos, popupsize, item_rgb, item_alpha, 0);\r\n\r\n/*\t//border\r\n\tui.drawfill(pos, [popupsize_x, 1], TD_BOT, item_alpha, 0);\r\n\tui.drawfill(pos, [1, popupsize_y - 1], TD_RGT, item_alpha, 0);\r\n\tui.drawfill(pos + [popupsize_x-1, 1], [1, popupsize_y - 1], TD_LFT, item_alpha, 0);\r\n\tui.drawfill(pos + [0, popupsize_y-1], [popupsize_x, 1], TD_TOP, item_alpha, 0);\r\n*/\tpos_x += 1;\r\n\r\n\tv = tokenize(f.mstrlist);\r\n\tfor (c = 0; c < v; c += 2)\r\n\t\tif (argv(c) == curval)\r\n\t\t\tbreak;\r\n\tif (c >= v)\r\n\t\tc = -1;\r\n\r\n\ti = f.firstrow;\r\n\ti = i*2;\r\n\tif (!f.visrows)\r\n\t\ti = 0;\r\n\telse if (c >= 0)\r\n\t{\r\n\t\t//bound the displayed position \r\n\t\tif (c < i)\r\n\t\t\ti = c;\r\n\t\tif (i < c - (f.visrows-1)*2)\r\n\t\t\ti = c - (f.visrows-1)*2;\r\n\t}\r\n\tf.firstrow = floor(i*0.5);\r\n\r\n\tif (v > f.visrows*2)\r\n\t{\r\n\t\tif (!pslider)\r\n\t\t{\r\n\t\t\tslidercache = -2;\r\n\t\t\tpslider = spawn(mitem_vslider);\r\n\t\t}\r\n\t}\r\n\telse if (pslider)\r\n\t{\r\n\t\tpslider.item_remove();\r\n\t\tpslider = __NULL__;\r\n\t}\r\n\r\n\tif (pslider)\r\n\t{\r\n\t\tpopupsize_x -= pslider.item_size_x;\r\n\t\tif (slidercache != c)\r\n\t\t{\t//current index changed, force the slider to the active item.\r\n\t\t\tslidercache = c;\r\n\t\t\tpslider.val = f.firstrow;\r\n\t\t}\r\n\t\tpslider.maxv = v/2 - f.visrows;\r\n\t\tpslider.item_size_y = popupsize_y;\r\n\t\tpslider.item_draw(pos + [popupsize_x, 0]);\r\n\t}\r\n\r\n\t//constrain the drawing so it doesn't overflow the popup\r\n\tui.setcliparea(pos[0], pos[1], popupsize[0], popupsize[1]);\r\n\r\n\tif (pslider)\r\n\t{\r\n\t\tf.firstrow = i = floor(pslider.val);\r\n\t\tm = ((i!=pslider.val) + i + f.visrows)*2;\r\n\t\ti *= 2;\r\n\t\tpos[1] -= (pslider.val-i/2) * item_scale;\r\n\t}\r\n\telse m = i + f.visrows*2;\r\n\r\n\tfor (; i < m && i < v ; i+=2)\r\n\t{\r\n\t\tcol = f.item_rgb;\r\n\t\tif (item_flags & IF_MFOCUSED)\r\n\t\t\tif (mouseinbox(pos, [popupsize_x, item_scale]))\r\n\t\t\t\tcol_z = 0;\r\n\t\tif (c == i)\r\n\t\t\tcol_x = 0;\r\n\r\n\t\tui.drawstring(pos, argv(i+1), '1 1 0' * item_scale, col, f.item_alpha, 0);\r\n\t\tpos_y += item_scale;\r\n\t}\r\n\r\n\t//reset the clip area\r\n\tui.setcliparea(ui.drawrectmin[0], ui.drawrectmin[1], ui.drawrectmax[0] - ui.drawrectmin[0], ui.drawrectmax[1] - ui.drawrectmin[1]);\r\n};\r\nfloat(vector pos, float scan, float char, float down) mitem_combo_popup::item_keypress =\r\n{\r\n\tif (pslider && (scan == K_TOUCHTAP || scan == K_MOUSE1))\r\n\t{\r\n\t\tvector sliderpos = pos + [item_size_x-pslider.item_size_x,0];\r\n\t\tif (mouseinbox(pos + [item_size_x-pslider.item_size_x,0], pslider.item_size))\r\n\t\t\treturn pslider.item_keypress(sliderpos, scan, char, down);\r\n\t}\r\n\t\t\t\r\n\treturn pcombo.item_keypress(pos - [0, pcombo.item_size_y], scan, char, down);\r\n};\r\nfloat(vector pos, float scan, float char, float down) mitem_combo::item_keypress =\r\n{\r\n\tif (!down)\r\n\t\treturn FALSE;\r\n\r\n\tlocal string curval = get(item_command);\r\n\tlocal float i, c;\r\n\tlocal float f;\r\n\tc = tokenize(mstrlist);\r\n\r\n\t//find which one is active\r\n\tfor (i = 0; i < c; i+=2)\r\n\t{\r\n\t\tif (argv(i) == curval)\r\n\t\t{\r\n\t\t\tbreak;\r\n\t\t}\r\n\t}\r\n\r\n\tif (scan == K_ESCAPE || scan == K_MOUSE2 || scan == K_GP_B || scan == K_GP_BACK)\r\n\t{\r\n\t\tif (cpopup)\r\n\t\t{\r\n\t\t\tcpopup.item_remove();\r\n\t\t\treturn TRUE;\r\n\t\t}\r\n\t\treturn FALSE;\r\n\t}\r\n\telse if (scan == K_MWHEELUP || (cpopup && ISUPARROW(scan)) || (!cpopup && spos < 0 && ISLEFTARROW(scan)))\r\n\t{\r\n\t\ti -= 2;\r\n\t\tif (i < 0)\r\n\t\t\ti = c - 2;\r\n\t\tcurval = argv(i);\r\n\t}\r\n\telse if (scan == K_MWHEELDOWN || (cpopup && ISDOWNARROW(scan)) || (!cpopup && spos < 0 && ISRIGHTARROW(scan)))\r\n\t{\r\n\t\ti += 2;\r\n\t\tif (i >= c)\r\n\t\t\ti = 0;\r\n\t\tcurval = argv(i);\r\n\t}\r\n\telse if (scan == K_TOUCHTAP || scan == K_MOUSE1 || ISCONFIRMKEY(scan))\r\n\t{\r\n\t\tif (ISCONFIRMKEY(scan) && cpopup)\r\n\t\t{\r\n\t\t\tcpopup.item_remove();\r\n\t\t\treturn TRUE;\r\n\t\t}\r\n\r\n\t\tvisrows = ((c>18)?18/2:c/2);\r\n\t\tif (!cpopup)\r\n\t\t{\r\n\t\t\tcpopup = spawn(mitem_combo_popup);\r\n\t\t\tcpopup.pcombo = this;\r\n\t\t\tcpopup.item_scale = 8;\r\n\t\t\tcpopup.item_rgb = MENUBACK_RGB;\r\n\t\t\tcpopup.item_alpha = MENUBACK_ALPHA;\r\n\t\t\tpos = item_position;\r\n\t\t\tmitem_frame fr = item_parent;\r\n\t\t\twhile (fr.item_parent)\r\n\t\t\t{\t//try to inject the combo thingie into the desktop item. this is to avoid scissoring.\r\n\t\t\t\tpos += fr.item_position;\r\n\t\t\t\tfr = fr.item_parent;\r\n\t\t\t}\r\n\t\t\tfr.addr(cpopup, RS_X_MIN_PARENT_MIN | RS_X_MAX_OWN_MIN | RS_Y_MIN_PARENT_MIN | RS_Y_MAX_OWN_MIN, pos + [self.item_size_x / 2, self.item_size_y], [item_size_x*0.5, item_size_y*visrows]);\r\n\t\t}\r\n\t\tcpopup.item_size = cpopup.maxs = [item_size_x*0.5, item_size_y*visrows];\r\n\t\tcpopup.item_flags |= IF_SELECTABLE;\r\n\t\tcpopup.totop();\r\n\r\n\t\tif ((scan == K_TOUCHTAP || scan == K_MOUSE1) && (cpopup.item_flags & IF_MFOCUSED))\r\n\t\t{\r\n\t\t\t//if they clicked inside the popup, change the selected item.\r\n\t\t\tf = ui.mousepos[1] - (pos_y + item_size_y);\r\n\t\t\tf /= cpopup.item_scale;\r\n\t\t\tf += firstrow;\r\n\t\t\ti = floor(f) * 2;\r\n\t\t\tif (i < c)\r\n\t\t\t{\r\n\t\t\t\tcurval = argv(i);\r\n\t\t\t\tcpopup.item_flags &~= IF_SELECTABLE;\r\n\t\t\t\tcpopup.item_size = cpopup.maxs = '0 0';\r\n\t\t\t\titem_parent.item_focuschange(this, IF_MFOCUSED|IF_KFOCUSED);\r\n\r\n\t\t\t\tcpopup.item_remove();\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\telse\r\n\t{\r\n\t\tif (spos < 0)\r\n\t\t{\r\n\t\t\tspos = strlen(curval);\r\n\t\t\tif (!super::item_keypress(pos, scan, char, down))\r\n\t\t\t{\r\n\t\t\t\tspos = -1;\r\n\t\t\t\treturn FALSE;\r\n\t\t\t}\r\n\t\t\treturn TRUE;\r\n\t\t}\r\n\t\telse return super::item_keypress(pos, scan, char, down);\r\n\t}\r\n\r\n\tset(item_command, curval);\r\n\treturn TRUE;\r\n};\r\nmitem_combo(string text, string command, vector sz, string valuelist) menuitemcombo_spawn =\r\n{\r\n\tmitem_combo n = spawn(mitem_combo, item_text:text, item_command:command);\r\n\tn.item_scale = sz_y;\r\n\tn.item_size = sz;\r\n\tn.mstrlist = strzone(valuelist);\r\n\r\n\tif (n.isvalid(command))\r\n\t\tn.item_flags |= IF_SELECTABLE;\r\n\treturn n;\r\n};\r\n"
  },
  {
    "path": "quakec/menusys/menusys/mitem_console.qc",
    "content": "/***************************************************************************\r\nEmbed a (sub) console in a menu item thing.\r\n\r\nyou can print to these consoles with con_print(\"consolename\", \"text to be printed\\n\");\r\nthey can be detached from the system console with con_getset(\"consolename\", \"hidden\", \"1\"). be sure to getset(con, \"close\", \"1\") or unhide afterwards.\r\nyou can enumerate the active consoles with con=\"\";while((con=con_getset(con,\"next\"))!=\"\"){print(con_getset(con,\"title\"));}  this is useful for finding xmpp conversations. you might then want to hide them afterwards.\r\n*/\r\n\r\nclass mitem_console : mitem\r\n{\r\n\tvirtual void() item_remove =\r\n\t{\r\n\t\tstrunzone(item_command);\r\n\t\tsuper::item_remove();\r\n\t};\r\n\tvirtual float(vector pos, float scan, float char, float down) item_keypress =\r\n\t{\r\n\t\tlocal float ret;\r\n\t\tif (scan == K_ESCAPE)\t//let the owning menu take it.\r\n\t\t\treturn FALSE;\r\n\t\tif (down)\r\n\t\t{\r\n\t\t\tret = con_input(item_command, IE_KEYDOWN, scan, char, 0);\r\n\t\t\tif (scan == K_MOUSE1 || scan == K_MOUSE2)\t//grab mouse, so we still receive mouse up events.\r\n\t\t\t{\r\n\t\t\t\tui.mgrabs = this;\r\n\t\t\t\tret = TRUE;\r\n\t\t\t\tlocal mitem_frame p = item_parent;\r\n\t\t\t\tp.item_focuschange(this, IF_KFOCUSED);\r\n\t\t\t}\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tret = con_input(item_command, IE_KEYUP, scan, char, 0);\t//note the engine never tries to cancel key up events anyway.\r\n\t\t\tif (ui.mgrabs == this)\r\n\t\t\t\tui.mgrabs = __NULL__;\r\n\t\t}\r\n\t\treturn ret;\r\n\t};\r\n\tvirtual void(mitem newfocus, float flag) item_focuschange =\r\n\t{\r\n\t\tcon_input(item_command, IE_FOCUS, !!(item_flags&IF_MFOCUSED), !!(item_flags&IF_KFOCUSED), 0);\t\r\n\t};\r\n\tvirtual void(vector pos) item_draw =\r\n\t{\r\n\t\tcon_input(item_command, IE_MOUSEABS, ui.mousepos[0] - pos_x, ui.mousepos[1] - pos_y, 0);\r\n\t\tcon_draw(item_command, pos, item_size, 12);\t//use a 12-point font, if we can.\r\n\t};\r\n\tvoid() mitem_console =\r\n\t{\r\n\t\titem_flags |= IF_SELECTABLE;\r\n\t\titem_command = strzone(item_command);\r\n\t};\r\n};\r\n\r\n#define menuitemconsole_spawn(conname) spawn(mitem_console, item_command:conname)\r\n\r\n\r\n"
  },
  {
    "path": "quakec/menusys/menusys/mitem_desktop.qc",
    "content": "/***************************************************************************\r\ndesktop and top-level functions.\r\n*/\r\n\r\n#ifdef USEPOINTERS\r\ntypedef mitem mitem_desktop;\r\n#else\r\nclass mitem_desktop : mitem_frame\r\n{\r\n\tvirtual float(vector pos, float scan, float char, float down)\titem_keypress;\r\n\tvirtual void(mitem newfocus, float flag) item_focuschange;\r\n\tvirtual void(vector pos) item_draw;\t\t//draws the game, then calls mitem_frame::item_draw for children.\r\n#ifndef MENU\r\n\tvirtual void(float seat, vector minpos, vector size) drawgame = __NULL__;\t//if overridden, should call renderscene and then draw whatever hud it needs to. this will do splitscreen efficiently and automatically.\r\n#endif\r\n\tvoid() mitem_desktop;\t\t\t//the constructor. uninteresting. just ensures the size defaults to fullscreen.\r\n};\r\n#endif\r\n\r\n#if !defined(MENU) && !defined(CSQC_SIMPLE)\r\nfloat sb_showscores;\r\n#endif\r\n\r\nfloat drawfont;\r\nfloat(string fontname, string fontmaps, string sizes, float slot, optional float fix_scale, optional float fix_voffset) loadfont = #357;\r\n\r\nvoid() mitem_desktop::mitem_desktop =\r\n{\r\n#define menu_font_win \t\tautocvar(menu_font_win, \"cour\")\r\n#define menu_font\t\tautocvar(menu_font, \"Courier New\")\r\n#define menu_font_fallback\tautocvar(gl_font, \"\")\r\n\tqueryscreensize();\r\n\r\n\t__using(loadfont, drawfont)\r\n\tif (checkextension(\"DP_GFX_FONTS\"))\r\n\t{\r\n\t\t//make sure we have a font that can cope with slightly up-scaled stuff.\r\n\t\t//windows is special because we can load from the system fonts\r\n\t\tstring fontname = menu_font_fallback;\r\n\t\tif (menu_font_win != \"\" && !strncasecmp(cvar_string(\"sys_platform\"), \"Win\", 3))\r\n\t\t\tfontname = menu_font_win;\r\n\t\telse if (menu_font != \"\")\r\n\t\t\tfontname = menu_font;\r\n\t\tdrawfont = loadfont(\"\", fontname, \"8 12 16 outline=1\", -1);\r\n\t}\r\n\r\n\titem_text = \"desktop\";\r\n\tif (!item_flags)\r\n\t{\r\n\t\titem_size = ui.screensize;\r\n\t\titem_flags = IF_SELECTABLE | IF_MFOCUSED | IF_KFOCUSED | IF_RESIZABLE | IF_NOCURSOR;\r\n\t}\r\n\r\n\tif (!ui.kgrabs && !ui.mgrabs && (item_flags & IF_NOCURSOR))\r\n\t{\r\n\t\tui.kgrabs = this;\r\n\t\tui.mgrabs = this;\r\n\t}\r\n};\r\n\r\nvoid(mitem newfocus, float flag) mitem_desktop::item_focuschange =\r\n{\r\n\tsuper::item_focuschange(newfocus, flag);\r\n\r\n\tif (flag & IF_KFOCUSED)\r\n\t{\r\n\t\t//if we're deselecting the current one, reenable grabs\r\n\t\tif (newfocus == __NULL__)\r\n\t\t{\r\n\t\t\tif(item_flags & IF_NOCURSOR)\r\n\t\t\t{\r\n\t\t\t\tui.kgrabs = this;\r\n\t\t\t\tui.mgrabs = this;\r\n\t\t\t}\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tif (ui.kgrabs == this)\r\n\t\t\t{\r\n\t\t\t\tui.kgrabs = __NULL__;\r\n\t\t\t\tui.mgrabs = __NULL__;\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n};\r\n\r\n//the interact flag says that the mouse is held down on the desktop\r\nfloat(vector pos, float scan, float char, float down) mitem_desktop::item_keypress =\r\n{\r\n\tfloat oldfl = item_flags;\r\n\r\n\tif (down & 2)\r\n\t{\r\n\t\tdown &= 1;\r\n\t\t//if we're grabbing, then cancel if they press escape, otherwise block other items from taking the keys.\r\n\t\tif (scan == K_TOUCHTAP || (scan >= K_MOUSE1 && scan <= K_MOUSE5))\r\n\t\t\treturn 2;\t//block other wigits, don't cancel the event so the engine still does its thing\r\n\t\telse\r\n\t\t{\r\n\t\t\tif (scan == K_ESCAPE && down)\r\n\t\t\t{\r\n\t\t\t\tlocal mitem sc;\r\n\t\t\t\t//block the keyevent if we already have menus loaded but not focused (select one if we do).\r\n\t\t\t\tfor (sc = item_children; sc; sc = sc.item_next)\r\n\t\t\t\t{\r\n\t\t\t\t\tif (sc.item_flags & IF_SELECTABLE)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tif (!item_kactivechild)\r\n\t\t\t\t\t\t\titem_focuschange(sc, IF_KFOCUSED);\r\n\t\t\t\t\t\treturn 3;\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t\t//make sure our code takes it, instead of showing the engine menu...\r\n#ifdef MENU\r\n\t\t\t\tm_toggle(0);\r\n\t\t\t\treturn 3;\r\n#else\r\n\t\t\t\treturn 2|CSQC_ConsoleCommand(\"togglemenu\");\r\n#endif\r\n\t\t\t}\r\n\t\t\treturn 2;\r\n\t\t}\r\n\t}\r\n\r\n\tif (mitem_frame::item_keypress(pos, scan, char, down))\r\n\t\treturn TRUE;\r\n\r\n\tif ((scan == K_TOUCHTAP || scan == K_MOUSE1) && down)\r\n\t{\r\n#if defined(CSQC) && defined(FTE_SPLITSCREEN)\r\n\t\t__using(numclientseats)\r\n\t\t{\r\n\t\t\tif (numclientseats)\r\n\t\t\t\tcvar_set(\"in_forceseat\", \"0\");\t//disable it.\r\n\t\t}\r\n#endif\r\n\t\tif (item_flags & IF_NOCURSOR)\r\n\t\t{\r\n\t\t\tui.kgrabs = this;\r\n\t\t\tui.mgrabs = this;\r\n\t\t}\r\n\t\treturn TRUE;\r\n\t}\r\n\tif (scan == K_MOUSE2 && down)\r\n\t{\r\n#if defined(CSQC) && defined(FTE_SPLITSCREEN)\r\n\t\t__using(numclientseats)\r\n\t\t{\r\n\t\t\tif (numclientseats > 3)\r\n\t\t\t\tcvar_set(\"in_forceseat\", ftos(1 + ((ui.mousepos[0]>ui.screensize[0]/2)?1:0) + ((ui.mousepos[1]>ui.screensize[1]/2)?2:0)));\r\n\t\t\telse if (numclientseats > 1)\r\n\t\t\t\tcvar_set(\"in_forceseat\", ftos(1 + floor(ui.mousepos[1]*numclientseats/ui.screensize[1])));\r\n\t\t\telse if (numclientseats)\r\n\t\t\t\tcvar_set(\"in_forceseat\", \"0\");\t//disable it.\r\n\t\t}\r\n#endif\r\n\t\tif (item_flags & IF_NOCURSOR)\r\n\t\t{\r\n\t\t\tui.kgrabs = this;\r\n\t\t\tui.mgrabs = this;\r\n\t\t}\r\n\t\treturn TRUE;\r\n\t}\r\n\r\n#ifdef CSQC\r\n\t//catch otherwise unhangled escape presses, just to be sure we can use escape to toggle the menu\r\n\tif (scan == K_ESCAPE && down)\r\n\t{\r\n\t\treturn CSQC_ConsoleCommand(\"togglemenu\");\r\n\t}\r\n#endif\r\n\treturn FALSE;\r\n};\r\n\r\n#if !defined(MENU) && !defined(CSQC_SIMPLE)\r\nstatic vector(vector v) vtodpp =\r\n{\r\n\t//so fucking disgustingly ugly.\r\n\tif (dp_workarounds)\r\n\t{\r\n\t\tv_x *= cvar(\"vid_width\") / cvar(\"vid_conwidth\");\r\n\t\tv_y *= cvar(\"vid_height\") / cvar(\"vid_conheight\");\r\n\t}\r\n\treturn v;\r\n};\r\n\r\n//vector pmove_org;\r\nvoid(float seat, vector minpos, vector size) mitem_desktop::drawgame_helper =\r\n{\r\n\tif not(seat)\r\n\t{\r\n\t\tclearscene();\r\n\t\taddentities(MASK_ENGINE|MASK_VIEWMODEL);\r\n\t}\r\n\telse __using(VF_VIEWENTITY, VF_LPLAYER)\r\n\t{\r\n\t\tsetviewprop(VF_LPLAYER, seat);\r\n\t\tsetproperty(VF_VIEWENTITY, player_localentnum);\t\r\n\t\taddentities(MASK_VIEWMODEL);\t//don't do mask_engine because that's already done\r\n\t}\r\n//\tif (dp_workarounds)\r\n//\t\tsetproperty(VF_ORIGIN, pmove_org);\r\n\tsetproperty(VF_MIN, minpos);\r\n\tsetproperty(VF_SIZE, vtodpp(size));\r\n\r\n\tsetproperty(VF_DRAWCROSSHAIR, (ui.mgrabs == this));\r\n\r\n\tdrawgame(seat, minpos, size);\r\n\tif (mouseinbox(minpos, size))\r\n\t{\r\n\t\tui.havemouseworld = TRUE;\r\n//\t\tui.mouseworldnear = unproject([ui.mousepos[0], ui.mousepos[1], 0]);\r\n//\t\tui.mouseworldfar = unproject([ui.mousepos[0], ui.mousepos[1], 100000]);\r\n\t}\r\n};\r\n#endif\r\n\r\nvoid(vector pos) mitem_desktop::item_draw =\r\n{\r\n#if !defined(MENU) && !defined(CSQC_SIMPLE)\r\n//menuqc picks up the game/console as a background\r\n\tstring constate = serverkey(\"constate\");\r\n\tif (constate != \"\" && constate != \"active\")\t//allow empty, so things still kinda work with dp too.\r\n\t{\r\n\t\tdrawfill(pos, ui.screensize, '0 0 0', 1, 0);\r\n\t}\r\n\telse if (this.drawgame != __NULL__)\r\n\t{\r\n#ifdef FTE_SPLITSCREEN\r\n\t\t__using numclientseats, VF_LPLAYER;\r\n\t\tif (numclientseats > 3)\r\n\t\t{\r\n\t\t\tdrawgame_helper(0, [0, 0], 0.5*ui.screensize);\r\n\t\t\tdrawgame_helper(1, [ui.screensize[0]*0.5, 0], 0.5*ui.screensize);\r\n\t\t\tdrawgame_helper(2, [0, ui.screensize[1]*0.5], 0.5*ui.screensize);\r\n\t\t\tdrawgame_helper(3, [ui.screensize[0]*0.5, ui.screensize[1]*0.5], 0.5*ui.screensize);\r\n\t\t\tsetviewprop(VF_LPLAYER, 0);\r\n\t\t}\r\n\t\telse if (numclientseats > 2)\r\n\t\t{\r\n\t\t\tdrawgame_helper(0, [0, 0], [ui.screensize[0], ui.screensize[1]*0.333]);\r\n\t\t\tdrawgame_helper(1, [0, ui.screensize[1]*0.333], [ui.screensize[0], ui.screensize[1]*0.333]);\r\n\t\t\tdrawgame_helper(2, [0, ui.screensize[1]*0.666], [ui.screensize[0], ui.screensize[1]*0.333]);\r\n\t\t\tsetviewprop(VF_LPLAYER, 0);\r\n\t\t}\r\n\t\telse if (numclientseats > 1)\r\n\t\t{\r\n\t\t\tdrawgame(0, [0, 0], [ui.screensize[0], ui.screensize[1]*0.5]);\r\n\t\t\tdrawgame(1, [0, ui.screensize[1]*0.5], [ui.screensize[0], ui.screensize[1]*0.5]);\r\n\t\t\tsetviewprop(VF_LPLAYER, 0);\r\n\t\t}\r\n\t\telse\r\n#endif\r\n\t\t{\r\n\t\t\tdrawgame_helper(0, '0 0', ui.screensize);\r\n\t\t}\r\n\t}\r\n#endif\r\n\tsuper::item_draw(pos);\r\n\r\n\tif (ui.kgrabs == this)\r\n\t{\r\n#if defined(CSQC) && !defined(CSQC_SIMPLE)\r\n\t\tif (sb_showscores && ui.mgrabs == this)\r\n\t\t\tui.mgrabs = __NULL__;\r\n\t\telse\r\n#endif\r\n\t\tif (!ui.mgrabs)\r\n\t\t\tui.mgrabs = this;\r\n\t}\r\n};\r\n\r\n\r\nstatic var float oldgrabstate;\t//to work around a DP bug (as well as unnecessary spam)\r\n\r\nvoid(float force) items_updategrabs =\r\n{\r\n\tif (!ui.mgrabs || !(ui.mgrabs.item_flags & IF_NOCURSOR))\r\n\t{\r\n\t\tif (!oldgrabstate || force)\r\n\t\t{\r\n\t\t\toldgrabstate = TRUE;\r\n#ifdef MENU\r\n\t\t\tsetkeydest(2);\r\n\t\t\tif (!checkbuiltin(setcursormode))\r\n\t\t\t\tsetmousetarget(2);\t//legacy junk\r\n\t\t\telse\r\n#endif\r\n\t\t\tif (checkextension(\"FTE_QC_HARDWARECURSORS\"))\r\n\t\t\t\tsetcursormode(TRUE, autocvar(cl_cursor, \"gfx/cursor.lmp\"), autocvar(cl_cursorbias, 4.0)*'1 1', autocvar(cl_cursorscale, 1.0));\t//because we can\r\n\t\t\telse\r\n\t\t\t\tsetcursormode(TRUE);\t//because DP sucks.\r\n\r\n\t\t\t//evilness. make sure we fit the screen by forcing the screen size upwards. this may also affect reported video mode choices as well as being just extra scaling.\r\n\t\t\tif (autocvar(vid_minsize,   '640 480').x < 640 ||\r\n\t\t\t\tautocvar(vid_minsize,   '640 480').y < 480)\r\n\t\t\t\tcvar_set(\"vid_minsize\", \"640 480\");\r\n\t\t}\r\n\t}\r\n\telse if (oldgrabstate || force)\r\n\t{\r\n\t\toldgrabstate = FALSE;\r\n#ifdef MENU\r\n\t\tsetkeydest(0);\r\n\t\tif (!checkbuiltin(setcursormode))\r\n\t\t\tsetmousetarget(1);\r\n\t\telse\r\n#endif\r\n\t\t\tsetcursormode(FALSE);\r\n\t}\r\n};\r\n\r\nvoid(mitem_desktop desktop, vector screensize) items_draw =\r\n{\r\n\tui.screensize = screensize;\r\n\tqueryscreensize();\r\n\r\n#ifdef MENU\r\n\tui.mousepos = getmousepos();\r\n#else\r\n\tif (ui.havemouseworld)\r\n\t\tui.havemouseworld = 2;\t//stale, but not too stale\r\n#endif\r\n\r\n\tif (desktop.item_size != ui.screensize)\r\n\t{\r\n\t\tdesktop.item_size = ui.screensize;\r\n\t\tdesktop.item_resized();\r\n\t}\r\n\tui.drawrectmax = ui.screensize;\r\n\r\n\tdesktop.item_draw(desktop.item_position);\r\n\tdrawresetcliparea();\r\n\r\n\titems_updategrabs(FALSE);\r\n\tif (dp_workarounds && oldgrabstate)\r\n\t{\r\n//\t\t//hopefully dp isn't broken and reports zero sizes for files that failed...\r\n//\t\tif (drawgetimagesize(autocvar_cl_cursor) == '0 0')\r\n//\t\t\tui.drawpic(ui.mousepos - autocvar_cl_cursorbias*'1 1', autocvar_cl_cursor, autocvar_cl_cursorsize*'1 1', '1 1 1', 1, 0);\r\n//\t\telse\r\n\t\t\tui.drawcharacter(ui.mousepos - [stringwidth(\"+\", TRUE, '4 4')*0.5, 4], '+', '8 8', '1 1 1', 1, 0);\r\n\t}\r\n\r\n#ifndef MENU\r\n\tif (ui.havemouseworld == 2)\t//if its still stale then its totally invalid.\r\n\t\tui.havemouseworld = FALSE;\r\n#endif\r\n\tui.oldmousepos = ui.mousepos;\r\n};\r\n\r\nfloat(mitem_desktop desktop, float evtype, float scanx, float chary, float devid) items_keypress =\r\n{\r\n\tlocal float result = FALSE;\r\n\tvector pos;\r\n\tmitem p;\r\n\r\n\tswitch(evtype)\r\n\t{\r\n#define IE_PASTE 8\r\n\tcase IE_PASTE: //down, with JUST char info.\r\n\tcase IE_KEYDOWN:\r\n\tcase IE_KEYUP:\r\n\t\tif (scanx == K_LCTRL)\r\n\t\t\tui.ctrlheld = ((evtype==IE_KEYDOWN)?ui.ctrlheld|1:ui.ctrlheld&~1);\r\n\t\tif (scanx == K_RCTRL)\r\n\t\t\tui.ctrlheld = ((evtype==IE_KEYDOWN)?ui.ctrlheld|2:ui.ctrlheld&~2);\r\n\t\tif (scanx == K_LSHIFT)\r\n\t\t\tui.shiftheld = ((evtype==IE_KEYDOWN)?ui.shiftheld|1:ui.shiftheld&~1);\r\n\t\tif (scanx == K_RSHIFT)\r\n\t\t\tui.shiftheld = ((evtype==IE_KEYDOWN)?ui.shiftheld|2:ui.shiftheld&~2);\r\n#ifdef HEIRACHYDEBUG\r\n\t\tif (scanx == K_F1 && evtype == IE_KEYDOWN)\r\n\t\t{\r\n\t\t\tmitem_printtree(desktop, \"items_keypress\", __LINE__);\r\n\t\t\treturn TRUE;\r\n\t\t}\r\n#endif\r\n\t\tif (scanx == K_TOUCHTAP || (scanx >= K_MOUSE1 && scanx <= K_MOUSE5))\r\n\t\t{\r\n\t\t\tif (ui.mgrabs)\r\n\t\t\t{\r\n\t\t\t\tpos = '0 0 0';\r\n\t\t\t\tfor (p = ui.mgrabs; p; p = p.item_parent)\r\n\t\t\t\t\tpos += p.item_position;\r\n\t\t\t\tresult = ui.mgrabs.item_keypress(pos, scanx, chary, (evtype == IE_KEYDOWN)|2);\r\n\t\t\t\tif (result & 2)\r\n\t\t\t\t{\r\n\t\t\t\t\tui.mousedown = 0;\r\n\t\t\t\t\treturn result & 1;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tif (ui.kgrabs)\r\n\t\t\t{\r\n\t\t\t\tpos = '0 0 0';\r\n\t\t\t\tfor (p = ui.kgrabs; p; p = p.item_parent)\r\n\t\t\t\t\tpos += p.item_position;\r\n\t\t\t\tresult = ui.kgrabs.item_keypress(pos, scanx, chary, (evtype == IE_KEYDOWN||evtype == IE_PASTE)|2);\r\n\t\t\t\tif (result & 2)\r\n\t\t\t\t\treturn result & 1;\r\n\t\t\t}\r\n\t\t}\r\n\t\tif (desktop && desktop.item_keypress)\r\n\t\t\tresult = desktop.item_keypress(desktop.item_position, scanx, chary, evtype == IE_KEYDOWN||evtype == IE_PASTE);\r\n\t\tif (scanx == K_TOUCHTAP)\r\n\t\t{\r\n\t\t\tif (evtype == IE_KEYDOWN)\r\n\t\t\t\tui.mousedown |= 1<<23;\r\n\t\t\telse\r\n\t\t\t\tui.mousedown &~= 1<<23;\r\n\t\t}\r\n\t\telse if (scanx >= K_MOUSE1 && scanx <= K_MOUSE5)\r\n\t\t{\r\n\t\t\tif (evtype == IE_KEYDOWN)\r\n\t\t\t\tui.mousedown |= pow(1, scanx-K_MOUSE1);\r\n\t\t\telse\r\n\t\t\t\tui.mousedown &~= pow(1, scanx-K_MOUSE1);\r\n\t\t}\r\n\t\tresult = result & 1;\r\n\t\tbreak;\r\n\tcase IE_MOUSEDELTA:\r\n\t\tresult = !ui.mgrabs || !(ui.mgrabs.item_flags & IF_NOCURSOR);\r\n\t\tif (result)\r\n\t\t{\r\n\t\t\tqueryscreensize();\r\n\t\t\tui.mousepos[0] = bound(0, ui.mousepos[0]+scanx, ui.screensize[0]);\r\n\t\t\tui.mousepos[1] = bound(0, ui.mousepos[1]+chary, ui.screensize[1]);\r\n\t\t}\r\n\t\tbreak;\r\n\tcase IE_MOUSEABS:\r\n\t\tui.mousepos[0] = scanx;\r\n\t\tui.mousepos[1] = chary;\r\n\t\tresult = !ui.mgrabs || !(ui.mgrabs.item_flags & IF_NOCURSOR);\r\n\t\tbreak;\r\n\t}\r\n\treturn result;\r\n};\r\n\r\n\r\n"
  },
  {
    "path": "quakec/menusys/menusys/mitem_edittext.qc",
    "content": "/***************************************************************************\r\neditable text, directly linked to a cvar.\r\nFIXME: This can only edit the end of the string.\r\n*/\r\nclass mitem_edit : mitem\r\n{\r\n\tvirtual void(vector pos) item_draw;\r\n\tvirtual float(vector pos, float scan, float char, float down) item_keypress;\r\n\tvirtual void() item_remove;\r\n\tfloat spos;\r\n\t\r\n\tvoid() mitem_edit =\r\n\t{\r\n\t\titem_text = strzone(item_text);\r\n\t\titem_command = strzone(item_command);\r\n\t};\r\n\tvirtual void() item_resized =\r\n\t{\r\n\t\tif (isvalid(item_command))\r\n\t\t\titem_flags |= IF_SELECTABLE;\t\t\r\n\t\telse\r\n\t\t\titem_flags &= ~IF_SELECTABLE;\r\n\t\tsuper::item_resized();\r\n\t};\r\n};\r\n\r\nvoid() mitem_edit::item_remove =\r\n{\r\n\tif (item_text)\r\n\t\tstrunzone(item_text);\r\n\tif (item_command)\r\n\t\tstrunzone(item_command);\r\n\tsuper::item_remove();\r\n};\r\n\r\nvoid(vector pos) mitem_edit::item_draw =\r\n{\r\n\tlocal string curval = get(item_command);\r\n\r\n\tsuper::item_draw(pos);\r\n\r\n\tpos_x += item_size_x / 2;\r\n/*\tui.drawfill(pos, [item_size_x/2, 1], TD_BOT, item_alpha, 0);\r\n\tui.drawfill(pos, [1, self.item_size_y - 1], TD_RGT, item_alpha, 0);\r\n\tui.drawfill(pos + [item_size_x/2-1, 1], [1, item_size_y - 1], TD_LFT, item_alpha, 0);\r\n\tui.drawfill(pos + [0, item_size_y-1], [item_size_x/2, 1], TD_TOP, item_alpha, 0);\r\n*/\tpos_y += (item_size_y - item_scale)*0.5;\r\n\tpos_x += 1;\r\n\r\n\tspos = min(spos, strlen(curval));\r\n\tif (((cltime*4)&1) && (item_flags & IF_KFOCUSED))\r\n\t\tcurval = strcat(substring(curval, 0, spos), chr2str(0xe00b), substring(curval, spos+1, -1));\t//replace the char with a box... ugly, whatever\r\n\tui.drawstring(pos, curval, '1 1 0' * item_scale, item_rgb, item_alpha, 0);\r\n};\r\nfloat(vector pos, float scan, float chr, float down) mitem_edit::item_keypress =\r\n{\r\n\tif (!down)\r\n\t\treturn FALSE;\r\n\r\n\tlocal string curval = get(item_command);\r\n\tspos = min(spos, strlen(curval));\r\n\r\n\tif (scan == K_ESCAPE)\r\n\t\treturn FALSE;\r\n\t//FIXME: onscreen keyboard?\r\n\telse if (scan == K_LEFTARROW)\r\n\t\tspos = max(spos-1, 0);\r\n\telse if (scan == K_RIGHTARROW)\r\n\t\tspos+=1;\r\n\telse if (scan == K_HOME)\r\n\t\tspos = 0;\r\n\telse if (scan == K_END)\r\n\t\tspos = strlen(curval);\r\n\telse if (scan == K_TOUCHTAP || scan == K_MOUSE1)\r\n\t{\r\n\t\tfloat valuepos = pos_x+(item_size_x/2)+1;\r\n\t\tif (ui.mousepos[0] > valuepos-8)\r\n\t\t{\r\n\t\t\tspos = strlen(curval);\r\n\t\t\twhile (spos>0 && ui.mousepos[0] < valuepos+stringwidth(substring(curval, 0, spos), TRUE, '1 1 0'*item_scale))\r\n\t\t\t\tspos--;\r\n\t\t}\r\n\t}\r\n\telse if (scan == K_BACKSPACE || scan == K_DEL)\r\n\t{\r\n\t\tif (spos)\r\n\t\t{\r\n\t\t\tcurval = strcat(substring(curval, 0, spos-1), substring(curval, spos, -1));\r\n\t\t\tspos -= 1;\r\n\t\t}\r\n\t}\r\n\telse if ((ui.shiftheld && scan == K_INS) || (ui.ctrlheld && chr == 'v'))\r\n\t{\t//paste\r\n#ifndef CSQC\r\n\t\tif (checkbuiltin(clipboard_get))\r\n\t\t\tclipboard_get(0);\r\n#endif\r\n\t\treturn TRUE;\r\n\t}\r\n\telse if ((ui.ctrlheld && scan == K_INS) || (ui.ctrlheld && chr == 'c'))\r\n\t{\t//copy\r\n\t\tif (checkbuiltin(clipboard_set))\r\n\t\t\tclipboard_set(0, curval);\r\n\t\treturn TRUE;\r\n\t}\r\n\telse if (chr >= ' ')\r\n\t{\r\n\t\tcurval = strcat(substring(curval, 0, spos), chr2str(chr), substring(curval, spos, -1));\r\n\t\tspos += strlen(chr2str(chr));\r\n\t}\r\n\telse\r\n\t\treturn FALSE;\r\n\r\n\tset(item_command, curval);\r\n\treturn TRUE;\r\n};\r\nmitem_edit(string text, string command, vector sz) menuitemeditt_spawn =\r\n{\r\n\tmitem_edit n = spawn(mitem_edit, item_text:text, item_command:command);\r\n\tn.item_scale = sz_y;\r\n\tn.item_size = sz;\r\n\tn.spos = 10000000;\t//will be clipped so meh\r\n\r\n\tn.item_flags |= IF_SELECTABLE;\r\n\treturn n;\r\n};\r\n\r\n"
  },
  {
    "path": "quakec/menusys/menusys/mitem_exmenu.qc",
    "content": "/***************************************************************************\r\nfullscreen exclusive menu\r\nyou should only have ONE of these visible at once.\r\ninteractable - basically just a container for the items in the menu, but also handles killing them+itself when the user presses escape.\r\nwill keep stealing focus from the desktop, so you won't be able to play while one of these is active.\r\nwill not steal focus from siblings. this means console slideouts or whatever are still usable.\r\n\r\nthese items will automatically be added to the desktop/workspace thing\r\nCall it.item_remove() to pop the menu.\r\nRegular items can be added to the menu by first spawning them, then calling menu_additem to actually add it to the desired menu in the right place.\r\n*/\r\n\r\nclass mitem_exmenu : mitem_frame\r\n{\r\n\tvirtual float(vector pos, float scan, float char, float down) item_keypress =\r\n\t{\r\n\t\tlocal float ret = super::item_keypress(pos, scan, char, down);\r\n\t\tif (!ret && down)\r\n\t\t{\r\n\t\t\tret = TRUE;\r\n\t\t\tif (scan == K_MOUSE2 || scan == K_TOUCHLONG || scan == K_ESCAPE || scan == K_GP_BACK || scan == K_GP_B)\r\n\t\t\t{\r\n\t\t\t\tlocalcmd(strcat(item_command, \"\\n\"));\t//console command to exec if someone clicks the close button.\r\n\t\t\t\titem_remove();\r\n\t\t\t}\r\n\t\t\telse if (ISUPARROW(scan))\r\n\t\t\t\tmenu_selectnextitem(this, TRUE);\r\n\t\t\telse if (ISDOWNARROW(scan))\r\n\t\t\t\tmenu_selectnextitem(this, FALSE);\r\n\t\t\telse if (scan >= K_F1 && scan <= K_F12)\t//allow f1-f12 to work, but every other button event gets canceled.\r\n\t\t\t\tret = FALSE;\r\n\t\t}\r\n\t\treturn ret;\r\n\t};\r\n\tvirtual void(vector pos) item_draw =\r\n\t{\r\n\t\tif (ui.mgrabs == item_parent || ui.kgrabs == item_parent)\t//if our parent has grabs, steal it instead, this means you can select the console, but the game never gets focus\r\n\t\t{\r\n\t\t\tif (item_flags & IF_SELECTABLE)\r\n\t\t\t{\r\n\t\t\t\tui.mgrabs = __NULL__;\r\n\t\t\t\tui.kgrabs = __NULL__;\r\n\t\t\t\titem_parent.item_focuschange(this, IF_KFOCUSED);\r\n\t\t\t}\r\n\t\t}\r\n\t\tsuper::item_draw(pos);\r\n\t};\r\n\r\n\t//same as mitem, but does not propogate to parent.\r\n\tvirtual string(string key) get =\r\n\t{\r\n\t\treturn cvar_string(key);\r\n\t};\r\n\tvirtual void(string key, string newval) set =\r\n\t{\r\n\t\tcvar_set(key, newval);\r\n\t};\r\n};"
  },
  {
    "path": "quakec/menusys/menusys/mitem_frame.qc",
    "content": "/***************************************************************************\r\nBasic frame, containing sub-items.\r\nLogically the parent of menu objects. Also used for tabs.\r\n*/\r\n\r\n#ifndef MITEM_FRAME_QC__\r\n#define MITEM_FRAME_QC__\r\n\r\nclass mitem_vslider : mitem\r\n{\r\n\tvirtual float(vector pos, float scan, float char, float down) item_keypress;\r\n\tvirtual void(vector pos) item_draw;\r\n\tvoid() mitem_vslider;\r\n\tfloat val;\r\n\tfloat minv;\r\n\tfloat maxv;\r\n\tfloat stride;\t//size of one 'notch'\r\n};\r\n\r\nclass mitem_frame : mitem\r\n{\r\n\tvirtual void(mitem newfocus, float changedflag)\t\t\t\titem_focuschange;\r\n\tvirtual float(vector pos, float scan, float char, float down)\titem_keypress;\r\n\tvirtual void()\t\t\titem_resized;\r\n\tvirtual void(vector pos)\titem_draw;\r\n\tvirtual void()\t\t\titem_remove;\r\n\tvirtual void(mitem fromitem, string cmd)\titem_execcommand;\r\n\r\n\tmitem \t\titem_children;\r\n\tmitem \t\titem_mactivechild;\r\n\tmitem \t\titem_kactivechild;\r\n\tmitem\t\titem_exclusive;\r\n\r\n\tmitem_vslider\tvslider;\t//displayed if any client item's max[y] > our item_size[y]\r\n\tvector \titem_framesize;\t//x=sides, y=top, z=bottom\r\n\tfloat frame_stdheight;\t//adjust RS_Y_MIN_PARENT_MID to pinch inwards when the frame is smaller than this. Child items crossing the mid-point must size gracefully (like subframes).\r\n\tfloat frame_hasscroll;\r\n\r\n\tstatic mitem(string title) findchildtext;\r\n\tstatic mitem(string title) findchildcmd;\r\n\tstatic void(mitem newitem, float originflags, vector originmin, vector originmax) add;\r\n\tstatic void(mitem newitem, vector pos) adda;\r\n\tstatic void(mitem newitem, vector originmin, vector originmax) addm;\r\n\tstatic void(mitem newitem, float originflags, vector originmin, vector originmax) addr;\r\n\tstatic void(mitem newitem, float ypos) addc;\r\n\r\n\tvoid() mitem_frame =\r\n\t{\r\n\t\titem_flags |= IF_ISFRAME;\r\n\t};\r\n};\r\n\r\nvoid(mitem ch, vector parentsize) mitem_parentresized =\r\n{\r\n\tfloat f = ch.resizeflags;\r\n\r\n\tif (f & RS_X_FRACTION)\r\n\t\tch.item_position[0] = parentsize[0] * ch.mins[0];\r\n\telse if (f & RS_X_MIN_PARENT_MAX)\r\n\t\tch.item_position[0] = parentsize[0] + ch.mins[0];\r\n\telse if (f & RS_X_MIN_PARENT_MID)\r\n\t\tch.item_position[0] = parentsize[0]/2 + ch.mins[0];\r\n\telse //if (f & RS_X_MIN_PARENT_MIN)\r\n\t\tch.item_position[0] = ch.mins[0];\r\n\r\n\tif (f & RS_X_FRACTION)\r\n\t\tch.item_position[0] = parentsize[0] * ch.maxs[0];\r\n\telse if (f & RS_X_MAX_PARENT_MIN)\r\n\t\tch.item_size[0] = ch.maxs[0] - ch.item_position[0];\r\n\telse if (f & RS_X_MAX_PARENT_MID)\r\n\t\tch.item_size[0] = ch.maxs[0]+parentsize[0]/2 - ch.item_position[0];\r\n\telse if (f & RS_X_MAX_PARENT_MAX)\r\n\t\tch.item_size[0] = ch.maxs[0]+parentsize[0] - ch.item_position[0];\r\n\telse //if (f & RS_X_MAX_OWN_MIN)\r\n\t\tch.item_size[0] = ch.maxs[0];\r\n\r\n\tif (f & RS_Y_FRACTION)\r\n\t\tch.item_position[1] = parentsize[1] * ch.mins[1];\r\n\telse if (f & RS_Y_MIN_PARENT_MAX)\r\n\t\tch.item_position[1] = parentsize[1] + ch.mins[1];\r\n\telse if (f & RS_Y_MIN_PARENT_MID)\r\n\t{\r\n\t\tch.item_position[1] = parentsize[1]/2 + ch.mins[1];\r\n\t\tif (ch.item_parent.frame_stdheight)\r\n\t\tif (parentsize[1] < ch.item_parent.frame_stdheight)\r\n\t\t{\r\n\t\t\tif (ch.item_position[1] < parentsize[1]*0.5)\r\n\t\t\t\tch.item_position[1] += (ch.item_parent.frame_stdheight-parentsize[1])*0.5;\r\n\t\t\telse\r\n\t\t\t\tch.item_position[1] -= (ch.item_parent.frame_stdheight-parentsize[1])*0.5;\r\n\t\t}\r\n\t}\r\n\telse //if (f & RS_Y_MIN_PARENT_MIN)\r\n\t\tch.item_position[1] = ch.mins[1];\r\n\r\n\tif (f & RS_Y_FRACTION)\r\n\t\tch.item_position[1] = parentsize[1] * ch.maxs[1];\r\n\telse if (f & RS_Y_MAX_PARENT_MIN)\r\n\t\tch.item_size[1] = ch.maxs[1] - ch.item_position[1];\r\n\telse if (f & RS_Y_MAX_PARENT_MID)\r\n\t{\r\n\t\tch.item_size[1] = ch.maxs[1]+parentsize[1]/2 - ch.item_position[1];\r\n\t\tif (ch.item_parent.frame_stdheight)\r\n\t\tif (parentsize[1] < ch.item_parent.frame_stdheight)\r\n\t\t{\r\n\t\t\tif (ch.item_position[1]+ch.item_size[1] < parentsize[1]*0.5)\r\n\t\t\t\tch.item_size[1] += (ch.item_parent.frame_stdheight-parentsize[1])*0.5;\r\n\t\t\telse\r\n\t\t\t\tch.item_size[1] -= (ch.item_parent.frame_stdheight-parentsize[1])*0.5;\r\n\t\t}\r\n\t}\r\n\telse if (f & RS_Y_MAX_PARENT_MAX)\r\n\t\tch.item_size[1] = ch.maxs[1]+parentsize[1] - ch.item_position[1];\r\n\telse //if (f & RS_Y_MAX_OWN_MIN)\r\n\t\tch.item_size[1] = ch.maxs[1];\r\n};\r\n\r\nvoid(mitem fromitem, string cmd) mitem_frame::item_execcommand =\r\n{\r\n\tif (item_parent)\r\n\t\titem_parent.item_execcommand(fromitem, cmd);\r\n\telse\r\n\t\tlocalcmd(strcat(cmd, \"\\n\"));\r\n};\r\n\r\nmitem(string title) mitem_frame::findchildtext =\r\n{\r\n\tmitem ch;\r\n\tfor (ch = item_children; ch; ch = ch.item_next)\r\n\t{\r\n\t\tif (ch.item_text == title)\r\n\t\t\treturn ch;\r\n\t}\r\n\treturn __NULL__;\r\n};\r\nmitem(string title) mitem_frame::findchildcmd =\r\n{\r\n\tmitem ch;\r\n\tfor (ch = item_children; ch; ch = ch.item_next)\r\n\t{\r\n\t\tif (ch.item_command == title)\r\n\t\t\treturn ch;\r\n\t}\r\n\treturn __NULL__;\r\n};\r\n\r\n\r\n//adds an item with the desired origin settings\r\nvoid(mitem newitem, float originflags, vector originmin, vector originmax) mitem_frame::add =\r\n{\r\n\tnewitem.item_next = item_children;\r\n\titem_children = newitem;\r\n\tnewitem.item_parent = this;\r\n\r\n\t//set up position and resize\r\n\tnewitem.resizeflags = originflags;\r\n\tnewitem.mins = originmin;\r\n\tnewitem.maxs = originmax;\r\n\tlocal vector parentsize = [item_size[0] - item_framesize[0]*2, item_size[1] - (item_framesize[1] + item_framesize[2])];\r\n\tmitem_parentresized(newitem, parentsize);\r\n\tnewitem.item_resized();\r\n\t\r\n\titem_flags |= IF_CLIENTMOVED;\t//make sure it happens.\r\n\r\n//\tif (!item_kactivechild && (newitem.item_flags & IF_SELECTABLE))\r\n//\t\titem_focuschange(newitem, IF_KFOCUSED);\r\n};\r\n\r\n//adds an item to the parent with an absolute position relative to the parent's top-left. objects are expected to already have a size specified.\r\nvoid(mitem newitem, vector pos) mitem_frame::adda =\r\n{\r\n\tlocal vector parentsize = [item_size[0] - item_framesize[0]*2, item_size[1] - (item_framesize[1] + item_framesize[2])];\r\n\tnewitem.item_next = item_children;\r\n\titem_children = newitem;\r\n\tnewitem.item_parent = this;\r\n\r\n\tnewitem.resizeflags = RS_X_MIN_PARENT_MIN | RS_X_MAX_OWN_MIN | RS_Y_MIN_PARENT_MIN | RS_Y_MAX_OWN_MIN;\r\n\tnewitem.mins = pos;\r\n\tnewitem.maxs = newitem.item_size;\r\n\r\n\tmitem_parentresized(newitem, parentsize);\r\n\tnewitem.item_resized();\r\n\t\r\n\titem_flags |= IF_CLIENTMOVED;\t//make sure it happens.\r\n\r\n//\tif (!item_kactivechild && (newitem.item_flags & IF_SELECTABLE))\r\n//\t\titem_focuschange(newitem, IF_KFOCUSED);\r\n};\r\n\r\n//adds an item to the parent in reverse order (ie: at the tail, so the actual order in code)\r\nvoid(mitem newitem, float originflags, vector originmin, vector originmax) mitem_frame::addr =\r\n{\r\n\tlocal vector parentsize = [item_size[0] - item_framesize[0]*2, item_size[1] - (item_framesize[1] + item_framesize[2])];\r\n\tif (item_children)\r\n\t{\r\n\t\tlocal mitem prev;\r\n\t\tfor (prev = item_children; prev.item_next; prev = prev.item_next)\r\n\t\t\t;\r\n\t\tprev.item_next = newitem;\r\n\t\tnewitem.item_next = __NULL__;\r\n\t}\r\n\telse\r\n\t{\r\n\t\tnewitem.item_next = item_children;\r\n\t\titem_children = newitem;\r\n\t}\r\n\tnewitem.item_parent = this;\r\n\r\n\tnewitem.resizeflags = originflags;\r\n\tnewitem.mins = originmin;\r\n\tnewitem.maxs = originmax;\r\n\tmitem_parentresized(newitem, parentsize);\r\n\tnewitem.item_resized();\r\n\t\r\n\titem_flags |= IF_CLIENTMOVED;\t//make sure it happens.\r\n\r\n//\tif (!item_kactivechild && (newitem.item_flags & IF_SELECTABLE))\r\n//\t\titem_focuschange(newitem, IF_KFOCUSED);\r\n};\r\n\r\n//adds an item on the parent with the x coord centered, and absolute y.\r\n//if multiple items are at the same ypos, it will recenter all with respect to the others\r\nvoid(mitem newitem, float ypos) mitem_frame::addc =\r\n{\r\n\tfloat w, c;\r\n\tlocal mitem prev;\r\n\tlocal vector parentsize = [item_size[0] - item_framesize[0]*2, item_size[1] - (item_framesize[1] + item_framesize[2])];\r\n\r\n\tnewitem.item_next = item_children;\r\n\titem_children = newitem;\r\n\tnewitem.item_position[1] = ypos;\r\n\tnewitem.item_position[0] = (parentsize[0] - newitem.item_size[0])*0.5;\r\n\tnewitem.item_parent = this;\r\n\r\n\r\n\tnewitem.resizeflags = RS_X_MIN_PARENT_MID | RS_X_MAX_OWN_MIN | RS_Y_MIN_PARENT_MIN | RS_Y_MAX_OWN_MIN;\r\n\tnewitem.mins[0] = 0;\r\n\tnewitem.mins[1] = ypos;\r\n\tnewitem.maxs = newitem.item_size;\r\n\r\n\t//count the width of the other items at this height.\r\n\tw = 0;\r\n\tc = 0;\r\n\tfor (prev = item_children; prev; prev = prev.item_next)\r\n\t\tif (prev.resizeflags == newitem.resizeflags && prev.mins[1] == ypos && prev.maxs[1] == newitem.maxs[1])\r\n\t\t{\r\n\t\t\tw += prev.maxs[0];\r\n\t\t\tc += 1;\r\n\t\t}\r\n\r\n\t//distribute them evenly (from the right, because its add-at-head)\r\n\tw += (c-1)*16;\t//this much gap space\r\n\tfor (prev = item_children; prev; prev = prev.item_next)\r\n\t{\r\n\t\tif (prev.resizeflags == newitem.resizeflags && prev.mins[1] == ypos && prev.maxs[1] == newitem.maxs[1])\r\n\t\t{\r\n\t\t\tw -= prev.maxs[0];\r\n\t\t\tprev.mins[0] = (w+prev.maxs[0])/-2;\r\n\t\t\tmitem_parentresized(prev, parentsize);\r\n\t\t\tw -= 16;\r\n\t\t}\r\n\t}\r\n\tnewitem.item_resized();\r\n\t\r\n\titem_flags |= IF_CLIENTMOVED;\t//make sure it happens.\r\n\r\n//\tif (!item_kactivechild && (newitem.item_flags & IF_SELECTABLE))\r\n//\t\titem_focuschange(newitem, IF_KFOCUSED);\r\n};\r\n\r\n//Adds the item in the exact middle of the parent, in both axis\r\nvoid(mitem newitem, vector min, vector max) mitem_frame::addm =\r\n{\r\n\tthis.add(newitem, RS_X_MIN_PARENT_MID|RS_Y_MIN_PARENT_MID | RS_X_MAX_PARENT_MID|RS_Y_MAX_PARENT_MID, min, max);\r\n};\r\n\r\n//updates this.item_activechild, and calls focus notifications to ensure things get the message. \r\n//flag should be IF_MFOCUSED or IF_KFOCUSED\r\nvoid(mitem newfocus, float flag) mitem_frame::item_focuschange =\r\n{\r\n\tlocal mitem it;\r\n\r\n\tif (newfocus == this)\r\n\t{\r\n\t\t//our focus didn't change, but the parent is telling us that *it* got changed while we're the focus\r\n\t\tif (flag & IF_MFOCUSED)\r\n\t\t{\r\n\t\t\tif (item_parent.item_flags & IF_MFOCUSED && item_parent.item_mactivechild == this)\r\n\t\t\t\titem_flags |= IF_MFOCUSED;\r\n\t\t\telse\r\n\t\t\t\titem_flags = item_flags - (item_flags&IF_MFOCUSED);\r\n\t\t\t//and tell the child\r\n\t\t\tit = item_mactivechild;\r\n\t\t\tif (it)\r\n\t\t\t\tif (it.item_focuschange)\r\n\t\t\t\t\tit.item_focuschange(it, IF_MFOCUSED);\r\n\t\t}\r\n\t\tif (flag & IF_KFOCUSED)\r\n\t\t{\r\n\t\t\tif (item_parent.item_flags & IF_KFOCUSED && item_parent.item_kactivechild == this)\r\n\t\t\t\titem_flags |= IF_KFOCUSED;\r\n\t\t\telse\r\n\t\t\t\titem_flags = item_flags - (item_flags&IF_KFOCUSED);\r\n\t\t\t//and tell the child\r\n\t\t\tit = item_kactivechild;\r\n\t\t\tif (it)\r\n\t\t\t\tif (it.item_focuschange)\r\n\t\t\t\t\tit.item_focuschange(it, IF_KFOCUSED);\r\n\t\t}\r\n\t\treturn;\r\n\t}\r\n\r\n\tif ((flag & IF_MFOCUSED) && item_mactivechild != newfocus)\r\n\t{\r\n\t\t//make key focus follow the mouse cursor. this should probably only apply to button menus or something? :s\r\n\t\tif (newfocus && (item_flags & IF_FOCUSFOLLOWSMOUSE))\r\n\t\t\tflag |= IF_KFOCUSED;\r\n\r\n\t\tit = item_mactivechild;\r\n\t\titem_mactivechild = newfocus;\r\n\t\tif (it)\r\n\t\t{\r\n\t\t\tit.item_flags = it.item_flags - (it.item_flags&IF_MFOCUSED);\r\n\t\t\tif (it.item_focuschange)\r\n\t\t\t\tit.item_focuschange(it, IF_MFOCUSED);\r\n\t\t}\r\n\t\tit = item_mactivechild;\r\n\t\tif (it)\r\n\t\t{\r\n\t\t\tit.item_flags = it.item_flags | (item_flags & IF_MFOCUSED);\r\n\t\t\tif (it.item_focuschange)\r\n\t\t\t\tit.item_focuschange(it, IF_MFOCUSED);\r\n\t\t}\r\n\t}\r\n\r\n\tif ((flag & IF_KFOCUSED) && item_kactivechild != newfocus)\r\n\t{\r\n\t\tit = item_kactivechild;\r\n\t\titem_kactivechild = newfocus;\r\n\t\tif (it)\r\n\t\t{\r\n\t\t\tit.item_flags = it.item_flags - (it.item_flags&IF_KFOCUSED);\r\n\t\t\tif (it.item_focuschange)\r\n\t\t\t\tit.item_focuschange(it, IF_KFOCUSED);\r\n\t\t}\r\n\t\tit = item_kactivechild;\r\n\t\tif (it)\r\n\t\t{\r\n\t\t\tit.item_flags = it.item_flags | (item_flags & IF_KFOCUSED);\r\n\t\t\tif (it.item_focuschange)\r\n\t\t\t\tit.item_focuschange(it, IF_KFOCUSED);\r\n\t\t}\r\n\t}\r\n};\r\n\r\n\r\nfloat(vector pos, float scan, float char, float down) mitem_vslider::item_keypress =\r\n{\r\n\tif (down != 1)\r\n\t{\r\n\t\tif ((scan == K_TOUCHTAP || scan == K_MOUSE1) && ui.mgrabs == this)\r\n\t\t\tui.mgrabs = __NULL__;\r\n\t\treturn FALSE;\r\n\t}\r\n\tif (scan == K_MWHEELDOWN)\r\n\t\tval = bound(minv, val+stride, maxv);\r\n\telse if (scan == K_MWHEELUP)\r\n\t\tval = bound(minv, val-stride, maxv);\r\n\telse if (scan == K_TOUCHTAP || scan == K_MOUSE1)\r\n\t\tui.mgrabs = this;\r\n\telse\r\n\t\treturn FALSE;\r\n\treturn TRUE;\r\n};\r\nvoid(vector pos) mitem_vslider::item_draw =\r\n{\r\n\tfloat f;\r\n\tfloat w = item_size[0];\r\n\tfloat h = item_size[1];\r\n\tfloat isize = w;\r\n\t\r\n\tvector v = dp_workarounds?'0 0 0':drawgetimagesize(\"scrollbars/slider.tga\");\r\n\r\n\tif (v != '0 0 0')\r\n\t{\r\n\t\tui.drawpic(pos, \"scrollbars/arrow_up.tga\", [w, w], '1 1 1', item_alpha, 0);\t//top\r\n\t\tpos_y += w;\r\n\t\th -= w*2;\r\n\t\tui.drawpic(pos + [0, h], \"scrollbars/arrow_down.tga\", [w, w], '1 1 1', item_alpha, 0);\t//bottom\r\n\t\tui.drawpic(pos, \"scrollbars/slidebg.tga\", [w, h], '1 1 1', item_alpha, 0);\t//back-middle\r\n\t\r\n\t\tisize = (v_y * w) / (v_x);\r\n\t\tif (isize > h/2)\r\n\t\t\tisize = h/2;\r\n\t}\r\n\telse\r\n\t{\r\n//\t\tui.drawfill(pos, [w, w], '1 1 1', item_alpha, 0);\t//top\r\n//\t\tpos_y += w;\r\n//\t\th -= w*2;\r\n//\t\tui.drawfill(pos + [0, h], [w, w], '1 1 1', item_alpha, 0);\t//bottom\r\n\t\tui.drawfill(pos, [w, h], '0.5 0.5 0.5', item_alpha, 0);\t//back-middle\r\n\t}\r\n\r\n\tif (ui.mgrabs == this)\r\n\t{\r\n\t\tf = (ui.mousepos[1] - pos_y - (isize/2))/(h - isize);\r\n\t\tf = bound(0, f, 1);\r\n\t\tval = minv + (f * (maxv - minv));\r\n\t}\r\n\telse\r\n\t\tf = bound(0, (val - minv) / (maxv - minv), 1);\r\n\r\n\th -= isize;\r\n\tif (v != '0 0 0')\r\n\t\tui.drawpic(pos + [0, h*f], \"scrollbars/slider.tga\", [w, isize], '1 1 1', item_alpha, 0);\t//back-middle\r\n\telse\r\n\t\tui.drawfill(pos + [0, h*f], [w, isize], '1 1 1', item_alpha, 0);\t//back-middle\r\n};\r\nvoid() mitem_vslider::mitem_vslider =\r\n{\r\n\titem_size[0] = 8;\r\n\titem_size[1] = 128;\r\n};\r\n\r\n//does NOT wrap.\r\n//does NOT pass go.\r\nstatic mitem(mitem item, float upwards) menu_simplenextitem =\r\n{\r\n\tmitem_frame menu = item.item_parent;\r\n\tmitem prev;\r\n\tif (upwards)\r\n\t{\r\n\t\tif (item)\r\n\t\t{\r\n\t\t\titem = item.item_next;\r\n\t\t\tif (!item)\r\n\t\t\t\treturn __NULL__;\r\n\t\t\treturn item;\r\n\t\t}\r\n\t\telse\r\n\t\t\treturn __NULL__;\r\n\t}\r\n\telse\r\n\t{\r\n\t\tfor(prev = menu.item_children; prev.item_next; prev = prev.item_next)\r\n\t\t{\r\n\t\t\tif (prev.item_next == item)\r\n\t\t\t\treturn prev;\r\n\t\t}\r\n\t\treturn __NULL__;\r\n\t}\r\n};\r\n\r\n//finds the next/prev item through multiple children, returning NULL when it runs out of items in the sequence.\r\n//call this with item==null to find the first item in the sequence (to handle wraps).\r\nstatic mitem(mitem_frame menu, float upwards, mitem item) menu_findnextitem =\r\n{\r\n\tmitem_frame frame;\r\n\tmitem citem;\r\n\t\r\n\tif (item && (item.item_flags & IF_ISFRAME))\r\n\t{\r\n\t\tframe = (mitem_frame)item;\r\n\t\tcitem = menu_findnextitem(frame, upwards, frame.item_kactivechild?frame.item_kactivechild:frame.item_mactivechild);\r\n\t\tif (citem)\r\n\t\t\treturn citem;\r\n\t}\r\n\t\r\n\tfor(;;)\r\n\t{\r\n\t\tif (!item)\r\n\t\t{\t//we go for the opposite end here, as we assume to be starting/unfocused\r\n\t\t\titem = menu.item_children;\r\n\t\t\tif (!upwards && item)\r\n\t\t\t{\r\n\t\t\t\twhile(item.item_next)\r\n\t\t\t\t\titem = item.item_next;\r\n\t\t\t}\r\n\t\t}\r\n\t\telse\r\n\t\t\titem = menu_simplenextitem(item, upwards);\r\n\t\t\r\n\t\tif (!item)\r\n\t\t{\t//we reached the end of the list, let the parent frame try its next\r\n\t\t\treturn __NULL__;\r\n\t\t}\r\n\t\t\r\n\t\tif (item.item_flags & IF_ISFRAME)\r\n\t\t{\t//if the next item is a frame, try and select its first element instead\r\n\t\t\tframe = (mitem_frame)item;\r\n\t\t\tcitem = menu_findnextitem(frame, upwards, __NULL__);\r\n\t\t\tif (citem)\r\n\t\t\t\treturn citem;\r\n\t\t}\r\n\r\n\t\tif (item.item_flags & IF_INVISIBLE)\r\n\t\t\tcontinue;\r\n\t\tif (item.item_flags & IF_SELECTABLE)\r\n\t\t\treturn item;\r\n\t}\r\n\t\r\n};\r\nstatic void(mitem item) menu_deselectitem =\r\n{\r\n\tif (!item)\r\n\t\treturn;\r\n\tif (item && (item.item_flags & IF_ISFRAME))\r\n\t{\r\n\t\tmitem_frame frame = (mitem_frame)item;\r\n\t\tif (frame.item_kactivechild)\r\n\t\t\tmenu_deselectitem(frame.item_kactivechild);\r\n\t}\r\n\titem.item_focuschange(__NULL__, IF_KFOCUSED);\t//deselect the previous one\t\r\n};\r\nstatic void(mitem item) menu_selectitem =\r\n{\r\n\tif (!item)\r\n\t\treturn;\r\n\tmitem_frame menu = item.item_parent;\r\n\tif (menu)\r\n\t{\r\n\t\tif (menu.item_kactivechild != item)\r\n\t\t\tmenu_deselectitem(menu.item_kactivechild);\r\n\t\tmenu_selectitem(menu);\r\n\t\tmenu.item_focuschange(item, IF_KFOCUSED);\t//focus on the new\r\n\t}\r\n};\r\nvoid(mitem_frame rootmenu, float upwards) menu_selectnextitem =\r\n{\r\n\tmitem item = menu_findnextitem(rootmenu, upwards, rootmenu.item_kactivechild?rootmenu.item_kactivechild:rootmenu.item_mactivechild);\r\n\tif (!item)\r\n\t\titem = menu_findnextitem(rootmenu, upwards, __NULL__);\r\n\r\n\tmenu_selectitem(item);\r\n\titem.item_focuschange(item, IF_KFOCUSED);\t//focus on the new\r\n};\r\n\r\nfloat(vector pos, float scan, float char, float down) mitem_frame::item_keypress =\r\n{\r\n\tmitem ch;\r\n\tfloat handled = FALSE;\r\n\r\n\t//compensate for the frame\r\n\tpos[0] += item_framesize[0];\r\n\tpos[1] += item_framesize[1];\r\n\r\n\tif ((scan >= K_MOUSE1 && scan <= K_MOUSE5 && scan != K_MWHEELUP && scan != K_MWHEELDOWN) || scan == K_TOUCHTAP)\r\n\t{\r\n\t\tif (item_exclusive)\r\n\t\t\tch = item_exclusive;\r\n\t\telse \r\n\t\t\tch = item_mactivechild;\r\n\t\tif (down)\t//keyboard focus follows on mouse click.\r\n\t\t{\r\n\t\t\titem_focuschange(ch, IF_KFOCUSED);\r\n\t\t}\r\n\t}\r\n\telse\r\n\t{\r\n\t\tch = item_kactivechild;\r\n\t\tif (!ch && down)\r\n\t\t{\r\n\t\t\t//if there's no key child active, then go and pick one so you can just start using keyboard access etc.\r\n\t\t\tif (item_exclusive)\r\n\t\t\t\tch = item_exclusive;\r\n\t\t\telse if (item_mactivechild)\r\n\t\t\t\tch = item_mactivechild;\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tmitem c;\r\n\t\t\t\tfor (c = item_children; c; c = c.item_next)\r\n\t\t\t\t{\r\n\t\t\t\t\tif (c.item_flags & IF_SELECTABLE)\r\n\t\t\t\t\t\tch = c;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\tif (ch)\r\n\t\t\t\titem_focuschange(ch, IF_KFOCUSED);\r\n\t\t\tch = item_kactivechild;\r\n\t\t}\r\n\t}\r\n\r\n\tif (vslider)\r\n\t\tpos[1] -= vslider.val;\r\n\r\n\tif (ch)\r\n\t{\r\n\t\tif (ch.item_keypress)\r\n\t\t\thandled = ch.item_keypress(pos + ch.item_position, scan, char, down);\r\n\t}\r\n\tif (vslider && !handled && (scan == K_MWHEELUP || scan == K_MWHEELDOWN))\t//give mwheel to the slider if its visible.\r\n\t\thandled = vslider.item_keypress(pos + vslider.item_position, scan, char, down);\r\n\r\n\treturn handled;\r\n};\r\nvoid() mitem_frame::item_remove =\r\n{\r\n\tlocal mitem ch;\r\n\twhile (this.item_children)\r\n\t{\r\n\t\tch = this.item_children;\r\n\t\tthis.item_children = ch.item_next;\r\n\t\tch.item_remove();\r\n\t}\r\n\tsuper::item_remove();\r\n};\r\nvoid() mitem_frame::item_resized =\r\n{\r\n\tmitem ch;\r\n\tvector framemax = [item_size[0] - item_framesize[0]*2, item_size[1] - (item_framesize[1] + item_framesize[2])];\r\n\tfor (ch = this.item_children; ch; ch = ch.item_next)\r\n\t{\r\n\t\tvector os = ch.item_size;\r\n\t\tmitem_parentresized(ch, framemax);\r\n\t\tif (ch.item_resized && ch.item_size != os)\r\n\t\t\tch.item_resized();\r\n\t}\r\n\titem_flags |= IF_CLIENTMOVED;\t//make sure it happens.\r\n\r\n\tsuper::item_resized();\r\n};\r\nvoid(vector pos) mitem_frame::item_draw =\r\n{\r\n\tlocal vector omin = ui.drawrectmin, omax = ui.drawrectmax;\r\n\tlocal mitem ch = __NULL__;\r\n\tlocal vector clientpos;\r\n\tlocal vector clientsize;\r\n\r\n\tif (frame_hasscroll && (item_flags & IF_CLIENTMOVED))\r\n\t{\r\n\t\t//if a client object moved, make sure the scrollbar is updated\r\n\t\titem_flags &~= IF_CLIENTMOVED;\r\n\t\tclientsize= '0 0';\r\n\t\tfor(ch = item_children; ch; ch = ch.item_next)\r\n\t\t{\r\n\t\t\tif (clientsize[0] < ch.item_position[0] + ch.item_size[0])\r\n\t\t\t\tclientsize[0] = ch.item_position[0] + ch.item_size[0];\r\n\t\t\tif (clientsize[1] < ch.item_position[1] + ch.item_size[1])\r\n\t\t\t\tclientsize[1] = ch.item_position[1] + ch.item_size[1];\r\n\t\t}\r\n//\t\tif (clientsize[0] > item_size[0] - item_framesize[0]*2)\r\n//\t\t\t//add hscroll\r\n\t\tif (clientsize[1] > item_size[1] - (item_framesize[1]+item_framesize[2]))\r\n\t\t{\r\n\t\t\tif (!vslider)\r\n\t\t\t{\r\n\t\t\t\tlocal mitem_vslider tmp;\r\n\t\t\t\ttmp = spawn(mitem_vslider, val:0, stride:8);\r\n\t\t\t\tvslider = tmp;\r\n\t\t\t}\r\n\t\t\tvslider.maxv = clientsize[1] - (item_size[1] - (item_framesize[1]+item_framesize[2]));\r\n\t\t}\r\n\t\telse if (vslider)\r\n\t\t{\r\n\t\t\tvslider.item_remove();\r\n\t\t\tvslider = (mitem_vslider)__NULL__;\r\n\t\t}\r\n\t}\r\n\telse if (!frame_hasscroll && this.vslider)\r\n\t{\r\n\t\tvslider.item_remove();\r\n\t\tvslider = (mitem_vslider)__NULL__;\r\n\t}\r\n\r\n\r\n\t//compensate for the frame\r\n\tpos[0] += item_framesize[0];\r\n\tpos[1] += item_framesize[1];\r\n\r\n\tclientpos = pos;\r\n\tclientsize = this.item_size;\r\n\tclientsize[0] -= item_framesize[0]*2;\r\n\tclientsize[1] -= (item_framesize[1]+item_framesize[2]);\r\n\r\n\tif (vslider)\r\n\t{\r\n\t\t//scroll+shrink the client area to fit the slider on it.\r\n\t\tclientpos[1] -= vslider.val;\r\n\t\tclientsize[0] -= vslider.item_size[0];\r\n\t}\r\n\r\n\r\n\tif (ui.mousepos != ui.oldmousepos)\r\n\t{\r\n\t\tlocal mitem newfocus = __NULL__;\r\n\t\t//if the mouse moved, select the new item\r\n\t\tif (item_exclusive)\r\n\t\t\tnewfocus = item_exclusive;\r\n\t\telse\r\n\t\t{\r\n\t\t\tfor(ch = item_children; ch; ch = ch.item_next)\r\n\t\t\t{\r\n\t\t\t\tif (ch.item_flags & IF_SELECTABLE)\r\n\t\t\t\tif (mouseinbox(clientpos + ch.item_position, ch.item_size))\r\n\t\t\t\t{\r\n\t\t\t\t\tnewfocus = ch;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t\tif (vslider)\r\n\t\t\tif (mouseinbox(pos + [clientsize[0], 0], vslider.item_size))\r\n\t\t\t\tnewfocus = vslider;\r\n\t\tthis.item_focuschange(newfocus, IF_MFOCUSED);\r\n\t}\r\n\r\n\t//clip the draw rect to our area, so children don't draw outside it. don't draw if its inverted.\r\n\tif (pos_x > ui.drawrectmin[0])\r\n\t\tui.drawrectmin[0] = pos_x;\r\n\tif (pos_y > ui.drawrectmin[1])\r\n\t\tui.drawrectmin[1] = pos_y;\r\n\tif (pos_x+clientsize_x < ui.drawrectmax[0])\r\n\t\tui.drawrectmax[0] = pos_x+clientsize_x;\r\n\tif (pos_y+clientsize_y < ui.drawrectmax[1])\r\n\t\tui.drawrectmax[1] = pos_y+clientsize[1];\r\n\tif (ui.drawrectmax[0] > ui.drawrectmin[0] && ui.drawrectmax[1] > ui.drawrectmin[1])\r\n\t{\r\n\t\tui.setcliparea(ui.drawrectmin[0], ui.drawrectmin[1], ui.drawrectmax[0] - ui.drawrectmin[0], ui.drawrectmax[1] - ui.drawrectmin[1]);\r\n\t\tif (item_exclusive)\r\n\t\t\titem_exclusive.item_draw(clientpos + ch.item_position);\r\n\t\telse\r\n\t\t{\r\n\t\t\tfor(ch = item_children; ch; ch = ch.item_next)\r\n\t\t\t{\r\n\t\t\t\tif not (ch.item_flags & IF_INVISIBLE)\r\n\t\t\t\t\tch.item_draw(clientpos + ch.item_position);\r\n\t\t\t}\r\n\t\t}\r\n\t\tui.setcliparea(omin_x, omin_y, omax_x - omin_x, omax_y - omin_y);\r\n\r\n\t\tif (vslider)\r\n\t\t{\r\n\t\t\tvslider.item_size[1] = clientsize[1];\r\n\t\t\tvslider.item_draw(pos + [clientsize[0], 0]);\r\n\t\t}\r\n\t}\r\n\tui.drawrectmin = omin;\r\n\tui.drawrectmax = omax;\r\n};\r\n#define menuitemframe_spawn(sz) spawn(mitem_frame, item_size:sz)\r\n\r\n#endif //MITEM_FRAME_QC__\r\n"
  },
  {
    "path": "quakec/menusys/menusys/mitem_grid.qc",
    "content": "#ifndef MITEM_GRID_QC__\r\n#define MITEM_GRID_QC__\r\n\r\n//simple key/value grid (with editing).\r\nclass mitem_grid : mitem\r\n{\r\n\t//virtual void(mitem newfocus, float changedflag)\t\t\titem_focuschange;\r\n\tvirtual float(vector pos, float scan, float char, float down)\titem_keypress;\r\n\tvirtual void(vector pos)\titem_draw;\r\n\tvirtual void() \t\t\titem_resized;\r\n\r\n\t//first child is determined from the vslider.\r\n\tfloat\t\tgrid_numchildren;\r\n\tfloat\t\tgrid_mactive;\r\n\tfloat\t\tgrid_kactive;\r\n\r\n\tmitem_vslider\tvslider;\t//displayed if any client item's max[y] > our item_size[y]\r\n\r\n\tvirtual void(vector pos, float idx) grid_draw = 0;\r\n\tvirtual float(vector pos, float scan, float char, float down, float idx) grid_keypress = {return FALSE;};\r\n\tvirtual void(float olditem, float newitem) grid_selectionchanged = {};\t//just key focus\r\n};\r\n\r\n//does NOT wrap.\r\n//does NOT pass go.\r\nstatic mitem(mitem item, float upwards) menu_simplenextitem =\r\n{\r\n\tmitem_frame menu = item.item_parent;\r\n\tmitem prev;\r\n\tif (upwards)\r\n\t{\r\n\t\tif (item)\r\n\t\t{\r\n\t\t\titem = item.item_next;\r\n\t\t\tif (!item)\r\n\t\t\t\treturn __NULL__;\r\n\t\t\treturn item;\r\n\t\t}\r\n\t\telse\r\n\t\t\treturn __NULL__;\r\n\t}\r\n\telse\r\n\t{\r\n\t\tfor(prev = menu.item_children; prev.item_next; prev = prev.item_next)\r\n\t\t{\r\n\t\t\tif (prev.item_next == item)\r\n\t\t\t\treturn prev;\r\n\t\t}\r\n\t\treturn __NULL__;\r\n\t}\r\n};\r\n\r\nfloat(vector pos, float scan, float char, float down) mitem_grid::item_keypress =\r\n{\r\n\tfloat ch;\r\n\tfloat handled = FALSE;\r\n\r\n\tif (scan >= K_MOUSE1 && scan <= K_MOUSE5 && scan != K_MWHEELUP && scan != K_MWHEELDOWN && scan != K_TOUCHTAP)\r\n\t{\r\n\t\tch = grid_mactive;\r\n\t\tif (ch != grid_kactive)\r\n\t\tif (down && (scan == K_TOUCHTAP || scan == K_MOUSE1))\t//keyboard focus follows on mouse click.\r\n\t\t{\r\n\t\t\titem_focuschange((ch>=0)?__NULL__:vslider, IF_KFOCUSED);\r\n\t\t\tgrid_selectionchanged(grid_kactive, ch);\r\n\t\t\tgrid_kactive = ch;\r\n\t\t\thandled = TRUE;\r\n\t\t}\r\n\t}\r\n\telse\r\n\t{\r\n\t\tch = grid_kactive;\r\n\t\tif (ch<0 && down)\r\n\t\t{\r\n\t\t\t//if there's no key child active, then go and pick one so you can just start using keyboard access etc.\r\n\t\t\tif (grid_mactive)\r\n\t\t\t\tch = grid_mactive;\r\n\t\t\telse\r\n\t\t\t\tch = 0;\r\n\t\t\titem_focuschange((ch>=0)?__NULL__:vslider, IF_KFOCUSED);\r\n\t\t\tgrid_selectionchanged(grid_kactive, ch);\r\n\t\t\tgrid_kactive = ch;\r\n\t\t}\r\n\t}\r\n\r\n\tif (vslider)\r\n\t\tpos[1] -= vslider.val;\r\n\r\n\tif (ch<0&&vslider)\r\n\t{\r\n\t\tif (vslider.item_keypress)\r\n\t\t\thandled = vslider.item_keypress(pos + vslider.item_position, scan, char, down);\r\n\t}\r\n\telse if (!handled && ch >= 0 && ch < grid_numchildren)\r\n\t\thandled = grid_keypress(pos + vslider.item_position + [0,ch]*item_scale, scan, char, down, ch);\r\n\tif (!handled && down)\r\n\t{\r\n\t\tch = grid_kactive;\r\n\t\tswitch(scan)\r\n\t\t{\r\n\t\tcase K_MWHEELUP:\r\n\t\tcase K_MWHEELDOWN:\r\n\t\t\tif (vslider)\t//give mwheel to the slider if its visible.\r\n\t\t\t\thandled = vslider.item_keypress(pos + vslider.item_position, scan, char, down);\r\n\t\t\tbreak;\r\n\t\tcase K_HOME:\r\n\t\t\tch = 0;\r\n\t\t\tbreak;\r\n\t\tcase K_END:\r\n\t\t\tch = grid_numchildren-1;\r\n\t\t\tbreak;\r\n\t\tcase K_PGUP:\r\n\t\t\tch = max(0, grid_kactive-5);\r\n\t\t\tbreak;\r\n\t\tcase K_PGDN:\r\n\t\t\tch = min(grid_numchildren-1, grid_kactive+5);\r\n\t\t\tbreak;\r\n\t\tcase K_UPARROW:\r\n\t\tcase K_GP_DPAD_UP:\r\n\t\tcase K_GP_LTHUMB_UP:\r\n\t\tcase K_GP_RTHUMB_UP:\r\n\t\t\tch = max(grid_kactive-1, 0);\r\n\t\t\tbreak;\r\n\t\tcase K_DOWNARROW:\r\n\t\tcase K_GP_DPAD_DOWN:\r\n\t\tcase K_GP_LTHUMB_DOWN:\r\n\t\tcase K_GP_RTHUMB_DOWN:\r\n\t\t\tch = min(grid_kactive+1, grid_numchildren-1);\r\n\t\t\tbreak;\r\n\t\t}\r\n\t\tif (ch != grid_kactive)\r\n\t\t{\r\n\t\t\tgrid_selectionchanged(grid_kactive, ch);\r\n\t\t\tgrid_kactive = ch;\r\n\t\t\thandled = TRUE;\r\n\t\t}\r\n\t}\r\n\r\n\treturn handled;\r\n};\r\nvoid() mitem_grid::item_resized =\r\n{\r\n\tfloat clientheight = grid_numchildren * item_scale;\r\n\tif (clientheight > item_size[1])\r\n\t{\r\n\t\tif (!vslider)\r\n\t\t\tvslider = spawn(mitem_vslider, val:0, stride:8);\r\n\t\tvslider.maxv = clientheight - item_size[1];\r\n\t}\r\n\telse if (vslider)\r\n\t{\r\n\t\tvslider.item_remove();\r\n\t\tvslider = (mitem_vslider)__NULL__;\r\n\t}\r\n}\r\n\r\nvoid(vector pos) mitem_grid::item_draw =\r\n{\r\n\tlocal vector omin = ui.drawrectmin, omax = ui.drawrectmax;\r\n\tlocal vector clientpos;\r\n\tlocal vector clientsize;\r\n\r\n\tclientpos = pos;\r\n\tclientsize = this.item_size;\r\n\r\n\tif (vslider)\r\n\t{\r\n\t\t//scroll+shrink the client area to fit the slider on it.\r\n\t\tclientpos[1] -= vslider.val;\r\n\t\tclientsize[0] -= vslider.item_size[0];\r\n\t}\r\n\r\n\tif (ui.mousepos != ui.oldmousepos)\r\n\t{\r\n\t\tlocal mitem newfocus = __NULL__;\r\n\t\tgrid_mactive = floor((ui.mousepos[1]-clientpos_y)/item_scale);\r\n\r\n\t\tif (vslider)\r\n\t\t\tif (mouseinbox(pos + [clientsize[0], 0], vslider.item_size))\r\n\t\t\t{\r\n\t\t\t\tnewfocus = vslider;\r\n\t\t\t\tgrid_mactive = -1;\r\n\t\t\t}\r\n\t\tthis.item_focuschange(newfocus, IF_MFOCUSED);\r\n\t}\r\n\r\n\t//clip the draw rect to our area, so children don't draw outside it. don't draw if its inverted.\r\n\tif (pos_x > ui.drawrectmin[0])\r\n\t\tui.drawrectmin[0] = pos_x;\r\n\tif (pos_y > ui.drawrectmin[1])\r\n\t\tui.drawrectmin[1] = pos_y;\r\n\tif (pos_x+clientsize_x < ui.drawrectmax[0])\r\n\t\tui.drawrectmax[0] = pos_x+clientsize_x;\r\n\tif (pos_y+clientsize_y < ui.drawrectmax[1])\r\n\t\tui.drawrectmax[1] = pos_y+clientsize[1];\r\n\tif (ui.drawrectmax[0] > ui.drawrectmin[0] && ui.drawrectmax[1] > ui.drawrectmin[1])\r\n\t{\r\n\t\tui.setcliparea(ui.drawrectmin[0], ui.drawrectmin[1], ui.drawrectmax[0] - ui.drawrectmin[0], ui.drawrectmax[1] - ui.drawrectmin[1]);\r\n\r\n\t\tfloat c = max(0,floor((ui.drawrectmin[1]-clientpos_y)/item_scale));\t//calculate how many we can skip\r\n\t\tfor (clientpos_y += item_scale * c; c < grid_numchildren; c++, clientpos_y += item_scale)\r\n\t\t{\r\n\t\t\tif (clientpos_y >= ui.drawrectmax[1])\r\n\t\t\t\tbreak;\r\n\t\t\tgrid_draw(clientpos, c);\r\n\t\t}\r\n\t\tui.setcliparea(omin_x, omin_y, omax_x - omin_x, omax_y - omin_y);\r\n\r\n\t\tif (vslider)\r\n\t\t{\r\n\t\t\tvslider.item_size[1] = clientsize[1];\r\n\t\t\tvslider.item_draw(pos + [clientsize[0], 0]);\r\n\t\t}\r\n\t}\r\n\tui.drawrectmin = omin;\r\n\tui.drawrectmax = omax;\r\n};\r\n#endif\r\n"
  },
  {
    "path": "quakec/menusys/menusys/mitem_menu.qc",
    "content": "/***************************************************************************\r\nwindow-like menu.\r\ninteractable - basically just a container for the items in the menu, but also handles killing them+itself when the user presses escape.\r\n\r\nthese items will automatically be added to the desktop/workspace thing\r\nCall it.item_remove() to pop the menu.\r\nRegular items can be added to the menu by first spawning them, then calling menu_additem to actually add it to the desired menu in the right place.\r\n*/\r\n\r\nclass mitem_menu : mitem_frame\r\n{\r\n\tvector draginfo;\t//x, y, resizeflags={0:none, 1:left, 2:bottom, 4:right, 8:top, 16:move}\r\n\r\n\tvirtual float(vector pos, float scan, float char, float down) item_keypress;\r\n\tvirtual void(mitem newfocus, float flag) item_focuschange;\r\n\tvirtual void(vector pos) item_draw;\r\n\r\n\tvirtual void() item_remove =\r\n\t{\r\n\t\tstrunzone(item_text);\r\n\t\tsuper::item_remove();\r\n\t};\r\n\r\n\tnonvirtual float(vector pos) whichgrabhandle;\r\n\tnonvirtual void(vector pos, float handle) drawgrabhandle;\r\n\tvoid() mitem_menu =\r\n\t{\r\n\t\titem_text = strzone(item_text);\r\n\t\titem_scale = 8;\r\n\t\titem_rgb = MENUBACK_RGB;\r\n\t\titem_alpha = MENUBACK_ALPHA;\r\n\r\n\t\tif (item_framesize == '0 0 0')\r\n\t\t\titem_framesize = '4 16 4';\r\n\r\n\t\titem_size[0] += item_framesize[0];\r\n\t\titem_size[1] += item_framesize[1]+item_framesize[2];\r\n\r\n\t\titem_flags |= IF_SELECTABLE;\r\n\t\t\r\n#ifdef CSQC\r\n\t\tcprint(\"\");\r\n#endif\r\n\t};\r\n\r\n\t//same as mitem, but does not propogate to parent.\r\n\tvirtual string(string key) get =\r\n\t{\r\n\t\treturn cvar_string(key);\r\n\t};\r\n\tvirtual void(string key, string newval) set =\r\n\t{\r\n\t\tcvar_set(key, newval);\r\n\t};\r\n};\r\n\r\nvoid(mitem newfocus, float flag) mitem_menu::item_focuschange =\r\n{\r\n\tif (flag & IF_KFOCUSED)\r\n\t{\r\n\t\t//move the window to the top of the z-order when it gets focus.\r\n\t\tif (item_flags & IF_KFOCUSED)\r\n\t\t\ttotop();\r\n\t\t//release grabs if someone else took focus.\t\r\n\t\telse if (ui.mgrabs == this)\r\n\t\t{\r\n\t\t\tui.mgrabs = __NULL__;\r\n\t\t\tdraginfo[2] = 0;\r\n\t\t}\r\n\t}\r\n\r\n\tsuper::item_focuschange(newfocus, flag);\r\n};\r\n\r\nvoid(vector pos, float handle) mitem_menu::drawgrabhandle =\r\n{\r\n\tif ((handle & 3) == 3)\r\n\t{\r\n\t\t//bottom left\r\n\t\tdrawfill(pos + [0, item_size[1]-item_framesize[2]], [item_framesize[0], item_framesize[2]], '1 1 0', 1, 0);\r\n\t\thandle &~= 3;\r\n\t}\r\n\tif ((handle & 6) == 6)\r\n\t{\r\n\t\t//bottom right\r\n\t\tdrawfill(pos + [item_size[0]-item_framesize[0], item_size[1]-item_framesize[2]], [item_framesize[0], item_framesize[2]], '1 1 0', 1, 0);\r\n\t\thandle &~= 6;\r\n\t}\r\n\tif (handle & 1)\r\n\t{\r\n\t\t//left\r\n\t\tdrawfill(pos + [0, 0], [item_framesize[0], item_size[1]], '1 1 0', 1, 0);\r\n\t}\r\n\tif (handle & 2)\r\n\t{\r\n\t\t//bottom\r\n\t\tdrawfill(pos + [0, item_size[1]-item_framesize[2]], [item_size[0], item_framesize[2]], '1 1 0', 1, 0);\r\n\t}\r\n\tif (handle & 4)\r\n\t{\r\n\t\t//right\r\n\t\tdrawfill(pos + [item_size[0]-item_framesize[0], 0], [item_framesize[0], item_size[1]], '1 1 0', 1, 0);\r\n\t}\r\n};\r\n\r\nfloat(vector pos) mitem_menu::whichgrabhandle =\r\n{\r\n\t//handle dragging and resizing.\r\n\tif (mouseinbox(pos, [item_size[0]-item_framesize[1], item_framesize[1]]))\r\n\t{\r\n\t\t//drag\r\n\t\treturn 16;\r\n\t}\r\n\telse if (this.item_flags & IF_RESIZABLE)\r\n\t{\r\n\t\tif (mouseinbox(pos + [0, item_size[1]-item_framesize[2]], [item_framesize[0], item_framesize[2]]))\r\n\t\t{\r\n\t\t\t//bottom-left\r\n\t\t\treturn 3;\r\n\t\t}\r\n\t\telse if (mouseinbox(pos + [item_size[0]-item_framesize[0], item_size[1]-item_framesize[2]], [item_framesize[0], item_framesize[2]]))\r\n\t\t{\r\n\t\t\t//bottom-right\r\n\t\t\treturn 6;\r\n\t\t}\r\n\t\telse if (mouseinbox(pos + [0, 0], [item_framesize[0], item_size[1]]))\r\n\t\t{\r\n\t\t\t//left\r\n\t\t\treturn 1;\r\n\t\t}\r\n\t\telse if (mouseinbox(pos + [0, item_size[1]-item_framesize[2]], [item_size[0], item_framesize[2]]))\r\n\t\t{\r\n\t\t\t//bottom\r\n\t\t\treturn 2;\r\n\t\t}\r\n\t\telse if (mouseinbox(pos + [item_size[0]-item_framesize[0], 0], [item_framesize[0], item_size[1]]))\r\n\t\t{\r\n\t\t\t//right\r\n\t\t\treturn 4;\r\n\t\t}\r\n\t}\r\n\treturn 0;\r\n};\r\n\r\nfloat(vector pos, float scan, float char, float down) mitem_menu::item_keypress =\r\n{\r\n\tif (scan == K_MOUSE1 && (down&1))\r\n\t{\r\n\t\tif (!(item_flags & IF_NOKILL) && mouseinbox(pos + [item_size[0]-item_framesize[1], 0], item_framesize[1] * '1 1'))\r\n\t\t{\r\n\t\t\tlocalcmd(strcat(item_command, \"\\n\"));\t//console command to exec if someone clicks the close button.\r\n\t\t\tthis.item_remove();\r\n\t\t\treturn TRUE;\r\n\t\t}\r\n\t\tdraginfo[2] = this::whichgrabhandle(pos);\r\n\t\tif (draginfo[2])\r\n\t\t\tui.mgrabs = this;\r\n\t\tif (draginfo[2] & (1|16))\r\n\t\t\tdraginfo[0] = ui.mousepos[0] - pos_x;\r\n\t\tif (draginfo[2] & 2)\r\n\t\t\tdraginfo[1] = (ui.mousepos[1] - pos_y - item_size[1]) + item_position[1];\r\n\t\tif (draginfo[2] & 4)\r\n\t\t\tdraginfo[0] = ui.mousepos[0] - pos_x - item_size[0] + item_position[0];\r\n\t\tif (draginfo[2] & (8|16))\r\n\t\t\tdraginfo[1] = ui.mousepos[1] - pos_y;\r\n\t\tif (draginfo[2])\r\n\t\t\treturn TRUE;\r\n\t}\r\n\telse if (scan == K_MOUSE1 && draginfo[2])\r\n\t{\r\n\t\tif (!draginfo[2] && (item_flags & IF_RESIZABLE))\r\n\t\t{\r\n\t\t\t//if they dropped the window at the top of the screen, make sure its fullscreened to match how it was drawn...\r\n\t\t\tqueryscreensize();\r\n\t\t\tif (ui.mousepos[1] < 8)\r\n\t\t\t\titem_size = ui.screensize;\r\n\t\t\tif (this.item_resized)\r\n\t\t\t\titem_resized();\r\n\t\t}\r\n\t\tui.mgrabs = __NULL__;\r\n\t\tdraginfo[2] = 0;\r\n\t\treturn TRUE;\r\n\t}\r\n\t\r\n\tif (!super::item_keypress(pos, scan, char, down))\r\n\t{\r\n\t\tdown &= 1;\r\n\t\tif (scan == K_ESCAPE && down)\r\n\t\t{\r\n\t\t\tif (this.item_flags & IF_NOKILL)\r\n\t\t\t\treturn FALSE;\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tlocalcmd(strcat(this.item_command, \"\\n\"));\t//console command to exec if someone clicks the close button.\r\n\t\t\t\tthis.item_remove();\r\n\t\t\t\treturn TRUE;\r\n\t\t\t}\r\n\t\t}\r\n\t\telse if (scan == K_UPARROW && down)\r\n\t\t\tmenu_selectnextitem(this, TRUE);\r\n\t\telse if (scan == K_DOWNARROW && down)\r\n\t\t\tmenu_selectnextitem(this, FALSE);\r\n\t\telse if (scan == K_MOUSE2 && down && !(this.item_flags & IF_NOKILL))\r\n\t\t{\t//unhandled right click closes menus, if we're allowed\r\n\t\t\tlocalcmd(strcat(item_command, \"\\n\"));\t//console command to exec if someone clicks the close button.\r\n\t\t\tthis.item_remove();\r\n\t\t\treturn TRUE;\r\n\t\t}\r\n\t\telse\tif (scan >= K_MOUSE1 && scan <= K_MWHEELDOWN)\r\n\t\t\treturn TRUE;\t//don't let the parent take unhandled mouse events.\r\n\t\telse\r\n\t\t\treturn FALSE;\r\n\t}\r\n\treturn TRUE;\r\n};\r\nvoid(vector pos) mitem_menu::item_draw =\r\n{\r\n\tfloat inset;\r\n\tvector efsize = item_size;\r\n\tif (draginfo[2])\r\n\t{\r\n\t\tif (draginfo[2] & 1)\r\n\t\t{\t//resize left\r\n\t\t\tlocal float nmax = item_position[0] + item_size[0];\r\n\t\t\tlocal float nmin = item_position[0] + item_size[0];\r\n\t\t\tnmin = ui.mousepos[0] - (pos_x - item_position[0]) - draginfo[0];\r\n\t\t\tif (nmax-nmin < 128)\r\n\t\t\t\tnmin = nmax - 128;\r\n\t\t\tpos_x -= item_position[0];\r\n\t\t\titem_position[0] = nmin;\r\n\t\t\tpos_x += item_position[0];\r\n\t\t\titem_size[0] = nmax - nmin;\r\n\t\t\tif (item_resized)\r\n\t\t\t\titem_resized();\r\n\t\t}\r\n\t\tif (draginfo[2] & 2)\r\n\t\t{\t//resize bottom\r\n\t\t\titem_size[1] = ui.mousepos[1] - (pos_y - item_position[1]) - draginfo[1];\r\n\t\t\tif (item_size[1] < 16)\r\n\t\t\t\titem_size[1] = 16;\r\n\t\t\tif (item_resized)\r\n\t\t\t\titem_resized();\r\n\t\t}\r\n\t\tif (draginfo[2] & 4)\r\n\t\t{\t//resize right\r\n\t\t\titem_size[0] = ui.mousepos[0] - (pos_x - item_position[0]) - draginfo[0];\r\n\t\t\tif (item_size[0] < 128)\r\n\t\t\t\titem_size[0] = 128;\r\n\t\t\tif (item_resized)\r\n\t\t\t\titem_resized();\r\n\t\t}\r\n\t\tefsize = item_size;\r\n\t\tif (draginfo[2]&16)\r\n\t\t{\r\n\t\t\tqueryscreensize();\r\n\t\t\tif (ui.mousepos[1] < 8 && (item_flags & IF_RESIZABLE))\r\n\t\t\t{\r\n\t\t\t\t//make it look as though its fullscreen, without destroying its actual size.\r\n\t\t\t\t//yes, we will fake-resize everything inside.\r\n\t\t\t\titem_position = '0 0';\r\n\r\n\t\t\t\titem_size = ui.screensize;\r\n\t\t\t\tif (this.item_resized)\r\n\t\t\t\t\tthis.item_resized();\r\n\t\t\t\titem_size = efsize;\r\n\t\t\t\tefsize = ui.screensize;\r\n\t\t\t}\r\n\t\t\telse if (item_size == ui.screensize && (item_flags & IF_RESIZABLE))\r\n\t\t\t{\r\n\t\t\t\titem_size = item_size*0.5;\r\n\t\t\t\tdraginfo[0] = draginfo[0]*0.5;\r\n\t\t\t\tif (this.item_resized)\r\n\t\t\t\t\tthis.item_resized();\r\n\t\t\t\tefsize = item_size;\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\titem_position = ui.mousepos - (pos - item_position) - draginfo;\r\n\t\t\t\titem_position[2] = 0;\r\n\t\t\t\tif (item_flags & IF_RESIZABLE)\r\n\t\t\t\t\tif (item_resized)\r\n\t\t\t\t\t\titem_resized();\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\t//bound it to the screen... just in case the screen got resized or something\r\n\tif (item_position[0] > ui.screensize[0] - item_size[0]*0.25)\r\n\t\titem_position[0] = ui.screensize[0] - item_size[0]*0.25;\r\n\tif (item_position[1] > ui.screensize[1] - item_framesize[1])\r\n\t\titem_position[1] = ui.screensize[1] - item_framesize[1];\r\n\tif (item_position[0] < item_size[0]*-0.75)\r\n\t\titem_position[0] = item_size[0]*-0.75;\r\n\tif (item_position[1] < 0)\r\n\t\titem_position[1] = 0;\r\n\r\n\tlocal float drag = 0;\r\n\tif (draginfo[2])\r\n\t\tdrag = draginfo[2];\r\n\telse if (item_flags & IF_MFOCUSED)\r\n\t\tdrag = this::whichgrabhandle(pos);\r\n\tif (drag)\r\n\t\tthis::drawgrabhandle(pos, drag);\r\n\r\n\tui.drawfill(pos, efsize, item_rgb, item_alpha, 0);\r\n\tlocal vector col = '1 1 1';\r\n\tif (item_flags & IF_KFOCUSED)\r\n\t\tcol_x = 0;\r\n\tif ((item_flags & IF_MFOCUSED) && mouseinbox(pos, [efsize_x-item_framesize[1], item_framesize[1]]))\r\n\t\tcol_z = 0;\r\n\tinset = (item_framesize[1]-item_scale)*0.5;\r\n\tui.drawstring(pos + '1 1'*inset, item_text, item_scale*'1 1', col, 1, 0);\r\n\tif (!(item_flags & IF_NOKILL))\r\n\t{\r\n\t\tif (mouseinbox(pos + [efsize_x-item_framesize[1], 0], item_framesize[1]*'1 1'))\r\n\t\t\tcol = '1 1 0';\r\n\t\telse\r\n\t\t\tcol = '1 1 1';\r\n\t\tui.drawstring(pos + [efsize_x+inset-item_framesize[1], inset], \"X\", item_scale*'1 1', col, 1, 0);\r\n\t}\r\n\tsuper::item_draw(pos);\r\n};\r\nmitem_menu(mitem_frame desktop, string mname, vector sz) menu_spawn =\r\n{\r\n\tmitem_menu n = spawn(mitem_menu, item_text:mname, item_framesize:'4 16 4', item_size:sz);\r\n\r\n//\tn.item_flags |= IF_RESIZABLE;\r\n\r\n\tif (desktop)\r\n\t\tdesktop.addr(n, RS_X_MIN_PARENT_MIN | RS_X_MAX_OWN_MIN | RS_Y_MIN_PARENT_MIN | RS_Y_MAX_OWN_MIN, (desktop.item_size - n.item_size) * 0.5, n.item_size);\r\n\treturn n;\r\n};\r\n"
  },
  {
    "path": "quakec/menusys/menusys/mitem_slider.qc",
    "content": "/***************************************************************************\r\nslider item, directly attached to a cvar.\r\ninteractable - executes a given console command.\r\n*/\r\nclass mitem_hslider : mitem\r\n{\r\n\tvirtual void(vector pos) item_draw;\r\n\tvirtual float(vector pos, float scan, float char, float down) item_keypress;\r\n\r\n\tvector item_slidercontrols;\t//min, max, step\r\n\t\r\n\tvirtual void() item_resized =\r\n\t{\r\n\t\tif (isvalid(item_command))\r\n\t\t\titem_flags |= IF_SELECTABLE;\t\t\r\n\t\telse\r\n\t\t\titem_flags &= ~IF_SELECTABLE;\r\n\t\tsuper::item_resized();\r\n\t};\r\n};\r\nvoid(vector pos) mitem_hslider::item_draw =\r\n{\r\n\tlocal float curval;\r\n\tvector rgb = self.item_rgb;\r\n\tif (!(item_flags & IF_SELECTABLE))\r\n\t\trgb *= 0.2;\r\n\r\n\tsuper::item_draw(pos);\r\n\tpos_x += item_size_x / 2;\r\n\r\n\tif (ui.mgrabs == this)\r\n\t{\r\n\t\t//if we're sliding it, update the value\r\n\t\tcurval = (ui.mousepos[0] - pos_x-8) / (10*8);\r\n\t\tcurval = bound(0, curval, 1);\r\n\t\tcurval = curval * (item_slidercontrols_y - item_slidercontrols_x);\r\n\t\tif (!ui.shiftheld)\r\n\t\t\tcurval = rint(curval / item_slidercontrols_z) * item_slidercontrols_z;\t//round it.\r\n\t\tcurval += item_slidercontrols_x;\r\n\t\tset(item_command, sprintf(\"%g\", curval));\r\n\t}\r\n\tcurval = stof(get(item_command));\r\n\r\n\tif (dp_workarounds)\r\n\t{\t//no ^U markup support. chr2str avoids warnings about non-utf8 strings which at least allows bi-compat to work.\r\n\t\tstring s = strcat(chr2str(0xe080, 0xe081, 0xe081, 0xe081, 0xe081, 0xe081), chr2str(0xe081, 0xe081, 0xe081, 0xe081, 0xe081, 0xe082));\r\n\t\t//slider background uses the fallback quake chars\r\n\t\tui.drawstring(pos, sprintf(\"%s (%g)\", s, curval), '1 1 0' * self.item_scale, rgb, self.item_alpha, 0);\r\n\t\t//now draw an indicater char in the right place.\r\n\t\t//the inner side of the boundary is 4 pixels wide so we can overlap the ends by that many pixels.\r\n\t\tcurval = (curval - self.item_slidercontrols_x) / (self.item_slidercontrols_y - self.item_slidercontrols_x);\t//fractionize it\r\n\t\tcurval = bound(0, curval, 1);\r\n\t\tui.drawstring(pos + [4 + curval*10*8, 0], chr2str(0xe083), '1 1 0' * self.item_scale, rgb, self.item_alpha, 0);\r\n\t}\r\n\telse\r\n\t{\r\n\t\t//slider background uses the fallback quake chars\r\n\t\tui.drawstring(pos, sprintf(\"^{e080}^{e081}^{e081}^{e081}^{e081}^{e081}^{e081}^{e081}^{e081}^{e081}^{e081}^{e082} (%g)\", curval), '1 1 0' * self.item_scale, rgb, self.item_alpha, 0);\r\n\t\t//now draw an indicater char in the right place.\r\n\t\t//the inner side of the boundary is 4 pixels wide so we can overlap the ends by that many pixels.\r\n\t\tcurval = (curval - self.item_slidercontrols_x) / (self.item_slidercontrols_y - self.item_slidercontrols_x);\t//fractionize it\r\n\t\tcurval = bound(0, curval, 1);\r\n\t\tui.drawstring(pos + [4 + curval*10*8, 0], \"^{e083}\", '1 1 0' * self.item_scale, rgb, self.item_alpha, 0);\r\n\t}\r\n};\r\nfloat(vector pos, float scan, float char, float down) mitem_hslider::item_keypress =\r\n{\r\n\tif (down&2)\r\n\t{\r\n\t\t//we have grabs, and mouse was released?\r\n\t\tif ((scan == K_TOUCHTAP || scan == K_MOUSE1) && !(down&1))\r\n\t\t{\t//we're done here.\r\n\t\t\tui.mgrabs = __NULL__;\r\n\t\t\treturn TRUE;\r\n\t\t}\r\n\t\treturn FALSE;\t//not handled, don't inhibit\r\n\t}\r\n\tif (down)\r\n\t{\r\n\t\tlocal float curval = stof(get(item_command));\r\n\t\tif (scan == K_MWHEELUP || scan == K_MWHEELDOWN)\r\n\t\t{\r\n\t\t\tif (ui.mousepos[0] > pos_x + item_size[0]/2)\r\n\t\t\t\tscan = ((scan == K_MWHEELDOWN)?K_LEFTARROW:K_RIGHTARROW);\r\n\t\t}\r\n\t\tif ((scan == K_TOUCHTAP || scan == K_MOUSE1) && down)\r\n\t\t{\r\n\t\t\tpos_x += item_size_x / 2;\r\n\t\t\tif (ui.mousepos[0] < pos_x)\r\n\t\t\t\treturn TRUE;//goto keyenter;\r\n\t\t\tcurval = (ui.mousepos[0] - pos_x-8) / (10*8);\r\n\t\t\tif (curval < 0 || curval > 1)\r\n\t\t\t\treturn FALSE;\r\n\t\t\tcurval = curval * (item_slidercontrols_y - item_slidercontrols_x) + item_slidercontrols_x;\r\n\t\t\tset(item_command, sprintf(\"%g\", curval));\r\n\t\t\tui.mgrabs = this;\r\n\t\t}\r\n\t\telse if ((scan == K_DEL || scan == K_GP_Y) && down && cvar_type(item_command))\r\n\t\t\tset(item_command, cvar_defstring(item_command));\r\n\t\telse if ((ISLEFTARROW(scan) || scan == K_MWHEELUP) && down)\r\n\t\t{\r\n\t\t\tif (item_slidercontrols_x > item_slidercontrols_y)\r\n\t\t\t\tset(item_command, sprintf(\"%g\", min(curval - item_slidercontrols_z, item_slidercontrols_x)));\r\n\t\t\telse\r\n\t\t\t\tset(item_command, sprintf(\"%g\", max(curval - item_slidercontrols_z, item_slidercontrols_x)));\r\n\t\t}\r\n\t\telse if ((ISRIGHTARROW(scan) || scan == K_MWHEELDOWN) && down)\r\n\t\t{\r\n\t\t\tif (item_slidercontrols_x > item_slidercontrols_y)\r\n\t\t\t\tset(item_command, sprintf(\"%g\", max(curval + item_slidercontrols_z, item_slidercontrols_y)));\r\n\t\t\telse\r\n\t\t\t\tset(item_command, sprintf(\"%g\", min(curval + item_slidercontrols_z, item_slidercontrols_y)));\r\n\t\t}\r\n\t\telse if ((ISCONFIRMKEY(scan) || scan == K_SPACE) && down)\r\n\t\t{\r\n//keyenter:\r\n\t\t\tif (item_slidercontrols_x > item_slidercontrols_y)\r\n\t\t\t{\r\n\t\t\t\tif (curval-0.001 <= item_slidercontrols_y)\r\n\t\t\t\t\tset(item_command, sprintf(\"%g\", item_slidercontrols_x));\r\n\t\t\t\telse\r\n\t\t\t\t\tset(item_command, sprintf(\"%g\", max(curval + item_slidercontrols_z, item_slidercontrols_y)));\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tif (curval+0.001 >= item_slidercontrols_y)\r\n\t\t\t\t\tset(item_command, sprintf(\"%g\", item_slidercontrols_x));\r\n\t\t\t\telse\r\n\t\t\t\t\tset(item_command, sprintf(\"%g\", min(curval + item_slidercontrols_z, item_slidercontrols_y)));\r\n\t\t\t}\r\n\t\t}\r\n\t\telse\r\n\t\t\treturn FALSE;\r\n\t\treturn TRUE;\r\n\t}\r\n\telse if ((scan == K_TOUCHTAP || scan == K_MOUSE1) && ui.mgrabs == this)\r\n\t\tui.mgrabs = __NULL__;\r\n\treturn FALSE;\r\n};\r\nmitem_hslider(string text, string command, vector controls, vector sz) menuitemslider_spawn =\r\n{\r\n\tmitem_hslider n = spawn(mitem_hslider);\r\n\tn.item_scale = sz_y;\r\n\tn.item_text = text;\r\n\tn.item_size = sz;\r\n\r\n\tn.item_slidercontrols = controls;\r\n\r\n\tn.item_command = command;\r\n\tif (n.isvalid(command))\r\n\t\tn.item_flags |= IF_SELECTABLE;\r\n\treturn n;\r\n};\r\n"
  },
  {
    "path": "quakec/menusys/menusys/mitem_spinnymodel.qc",
    "content": "/*\r\nrenderscene stuff is always available in csqc.\r\nIf the engine supports DP_QC_RENDER_SCENE then its meant to be available in menuqc too.\r\nIn practise, DP advertises this to menuqc even though it has never officially supported it.\r\nAt the time of writing, FTE's dev builds can do it (and only advertise the extension in builds that try to support it).\r\n\r\nNote: the basemenu mod does not make use of this in menuqc if only because its vaugely trying to support both engines, and its easier to just use an ifdef.\r\n\r\nThe mitem_spinnymodel item just shows a rotating model centered on the z axis. Simple as that.\r\nFor the sake of fun gimmicks, you can specify frame information with firstframe and framecount.\r\nAdditionally, you can cause it to switch to a different animation when it faces towards the camera with shootframe and shootframes.\r\nThis style of animation shouldn't really be considered very complex, but might help give a small demo of the basic concept of animation.\r\n*/\r\n/*\r\nNote - DP Bugs:\r\n1: \tviewport positions are interpreted in physical pixels in DP.\r\n  \tthere is no reliable way to know how many physical pixels there actually are.\r\n\tcustom viewports are thus near unusable.\r\n2:\tfailure to revert the viewport to default afterwards fucks over any 2d stuff drawn afterwards.\r\n\tincluding the console.\r\n3:\tbloom on worldless models draws the world.\r\n4:\tlighting is applied on models even if there's no world.\r\n\tenabling rtlights really fucks everything up.\r\n5:\tgamma/contrast/brightness are applied to the 3d view. even if there's no world.\r\n\tthis results in horrible squares if these settings are used.\r\n6:\tDP_QC_RENDER_SCENE is advertised in menuqc, but renderscene is not supported there at all.\r\n7:\tavoid the use of cltime. DP doesn't support it.\r\n\r\nprobably others. it really wouldn't surprise me.\r\n*/\r\n\r\n//helper function to work around a DP bug.\r\nstatic vector(vector v) vtodpp =\r\n{\r\n#ifndef CSQC_SIMPLE\r\n\t//so fucking disgustingly ugly.\r\n\tif (dp_workarounds)\r\n\t{\r\n#pragma warning disable F333\r\n\t\tv_x *= cvar(\"vid_width\") / cvar(\"vid_conwidth\");\r\n\t\tv_y *= cvar(\"vid_height\") / cvar(\"vid_conheight\");\r\n#pragma warning enable F333\r\n\t}\r\n#endif\r\n\treturn v;\r\n};\r\n//make sure the fields are all defined if we're in menuqc or something\r\nnoref .vector origin;\r\nnoref .vector angles;\r\nnoref .vector mins;\r\nnoref .vector maxs;\r\nnoref .string model;\r\nnoref .float frame, frame2, lerpfrac, renderflags;\r\nfloat frametime;\r\nclass mitem_spinnymodel : mitem\r\n{\r\n\tfloat zbias;\r\n\tfloat firstframe;\r\n\tfloat framecount;\r\n\tfloat shootframe;\r\n\tfloat shootframes;\r\n\tfloat rotatespeed;\r\n\t\r\n#ifndef EXT_CSQC\r\n\tvirtual void(vector pos) item_draw = {};\r\n#else\r\n\r\n\t//might as well use the class as the entity that is drawn.\r\n\t//it might end up clobbering any fields used for multiple things...\r\n\tvirtual void(vector pos) item_draw =\r\n\t{\r\n#ifndef CSQC\r\n\t\tif (!checkbuiltin(clearscene) || !checkbuiltin(setviewprop) || !checkbuiltin(addentity) || !checkbuiltin(renderscene))\r\n\t\t{\r\n\t\t\tdrawstring(pos, \"Renderscene unsupported\", [8,8], [1,1,1],1,0);\r\n\t\t\treturn;\r\n\t\t}\r\n\t\t__using VF_ORIGIN, VF_ANGLES, VF_MIN, VF_SIZE, VF_FOV, VF_AFOV;\r\n#endif\r\n\t\tvector orgbias;\r\n\t\torgbias = '0 0 0';\r\n\t\tif (dp_workarounds)\r\n\t\t\t__using(getviewprop) orgbias = (vector)getviewprop(VF_ORIGIN);\t//DP still lights the entity even if the world isn't drawn. this results in inconsistant/buggy light levels. there's nothing we can do about that other than stopping it from being completely black.\r\n\t\torigin = orgbias;\r\n\t\torigin_z += zbias;\r\n\t\r\n//\t\tangles_y = cltime*90;//frametime*90;\r\n\t\tangles_y += frametime*rotatespeed;\r\n\r\n\t\tclearscene(); //wipe the scene, and apply the default rendering values.\r\n\t\tsetviewprop(VF_MIN, vtodpp(pos)); //min pos\r\n\t\tsetviewprop(VF_SIZE, vtodpp(item_size));\t//size=maxpos-minpos\r\n\t\tif (dp_workarounds)\r\n\t\t\tsetviewprop(VF_FOV, [25, atan(item_size_y/(item_size_x/tan(25/360*6.28))) * 360/6.28]); //set an explicit fov. this thing had better be square. DP doesn't support VF_AFOV\r\n\t\telse\r\n\t\t\tsetviewprop(VF_AFOV, 25); //set the aproximate fov (ie: engine takes care of aspect ratio).\r\n#ifdef CSQC\r\n\t\tsetproperty(VF_DRAWENGINESBAR, FALSE);\r\n\t\tsetproperty(VF_DRAWWORLD, FALSE);\r\n\t\tsetproperty(VF_DRAWCROSSHAIR, FALSE);\r\n#endif\r\n\t\tsetviewprop(VF_ORIGIN, orgbias + '-128 0 0');\t//look towards it.\r\n\t\tsetviewprop(VF_ANGLES, '0 0 0');\r\n\t\t\r\n\t\t//animate it a bit\r\n\t\tlerpfrac -= frametime*10;\r\n\t\twhile(lerpfrac < 0)\r\n\t\t{\r\n\t\t\tlerpfrac += 1;\r\n\t\t\tframe2 = frame;\r\n\t\t\tframe += 1;\r\n\t\t\tif (angles_y >= 170 && shootframes)\r\n\t\t\t{\r\n\t\t\t\tif (frame == shootframe+shootframes)\r\n\t\t\t\t{\t//reached the last frame. clear the shooting 'flag' and go back to idle\r\n\t\t\t\t\tframe = firstframe;\r\n\t\t\t\t\tangles_y -= 360;\r\n\t\t\t\t}\r\n\t\t\t\telse if (frame < shootframe || frame >= shootframe+shootframes)\r\n\t\t\t\t\tframe = shootframe;\t//we were still idle, apparently.\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tif (frame < firstframe || frame >= firstframe+framecount)\r\n\t\t\t\t\tframe = firstframe;\r\n\t\t\t}\r\n\t\t}\r\n\t\t\r\n\t\taddentity(this);\r\n\t\trenderscene();\r\n\t\tif (dp_workarounds)\r\n\t\t{\t//dp fucks up 2d stuff if we don't explicitly restore the 3d view to fullscreen\r\n\t\t\tsetviewprop(VF_MIN, vtodpp('0 0 0'));\r\n\t\t\tsetviewprop(VF_SIZE, vtodpp(ui.screensize));\r\n\t\t}\r\n\t};\r\n\t\r\n\tvirtual void(vector pos) dp_draw =\r\n\t{\r\n\t};\r\n\r\n\tvoid() mitem_spinnymodel =\r\n\t{\r\n\t\tif (!checkbuiltin(renderscene))\r\n\t\t{\r\n//\t\t\titem_draw = dp_draw;\r\n//\t\t\treturn;\r\n\t\t}\r\n\r\n#ifndef CSQC\r\n\t\tif (checkbuiltin(precache_model) && checkbuiltin(setmodel))\r\n\t\t{\r\n\t\t\tprecache_model(item_text);\r\n\t\t\tsetmodel(this, item_text);\t//use the size information from the engine, woo for unreliability.\r\n\t\t}\r\n#endif\r\n\t\tzbias += (mins_z - maxs_z)/2 - mins_z; //center the model on its z axis, so the whole thing is visible.\r\n\t\tframe = firstframe;\r\n\t\tif (!angles_y && !rotatespeed)\r\n\t\t\trotatespeed=90;\r\n\r\n\t};\r\n#endif\r\n};\r\n\r\n"
  },
  {
    "path": "quakec/menusys/menusys/mitem_tabs.qc",
    "content": "/***************************************************************************\r\ntabs/tab widgets.\r\nthe 'tabs' widget is simply a tab-selection control. horizontal multiple choice. it draws only its currently active child.\r\nthe 'tab' widget is merely a container of other widgets, no different from a standard frame object, just has a name and a specific size.\r\n*/\r\nclass mitem_tabs : mitem_frame\t/*frame... but not really*/\r\n{\r\n\tvirtual void(vector pos) item_draw;\r\n\tvirtual float(vector pos, float scan, float char, float down) item_keypress;\r\n//\tvirtual void() item_resize;\r\n\r\n\tvoid() mitem_tabs =\r\n\t{\r\n\t\titem_framesize = '2 16 2';\r\n\t\titem_flags |= IF_SELECTABLE|IF_RESIZABLE;\r\n\t};\r\n};\r\n\r\nclass mitem_tab : mitem_frame\r\n{\r\n\tvirtual float(vector pos, float scan, float char, float down) item_keypress =\r\n\t{\r\n\t\tif (down && ISUPARROW(scan))\r\n\t\t\tmenu_selectnextitem(this, TRUE);\r\n\t\telse if (down && ISDOWNARROW(scan))\r\n\t\t\tmenu_selectnextitem(this, FALSE);\r\n\t\telse if (super::item_keypress(pos, scan, char, down))\r\n\t\t\treturn TRUE;\r\n\t\telse\r\n\t\t\treturn FALSE;\r\n\t\treturn TRUE;\r\n\t};\r\n\r\n\tvoid() mitem_tab =\r\n\t{\r\n\t\titem_framesize = '0 0 0';\r\n\t\titem_flags |= IF_SELECTABLE|IF_RESIZABLE;\r\n\t};\r\n};\r\n\r\nvoid(vector pos) mitem_tabs::item_draw =\r\n{\r\n\tlocal mitem ch;\r\n\tlocal vector tpos = pos;\r\n\tlocal float w;\r\n\tlocal vector col;\r\n\r\n\t//to highlight the active tab, we draw the top line 1 pixel higher, and no bottom line\r\n\tfor (ch = item_children; ch; ch = ch.item_next)\r\n\t{\r\n\t\tw = stringwidth(ch.item_text, TRUE, '8 8') + 8;\r\n\r\n\t\tui.drawfill(tpos + '0 1', [1, 15], TD_LFT, item_alpha, 0);\r\n\t\tui.drawfill(tpos + [w-1, 1], [1, 14], TD_RGT, item_alpha, 0);\r\n\t\tif (ch == item_kactivechild)\r\n\t\t{\r\n\t\t\t//top line\r\n\t\t\tui.drawfill(tpos, [w, 1], TD_TOP, item_alpha, 0);\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\t//top line\r\n\t\t\tui.drawfill(tpos + '0 1', [w, 1], TD_TOP, item_alpha, 0);\r\n\t\t\t//bottom\r\n\t\t\tui.drawfill(tpos + '0 15', [w, 1], TD_TOP, item_alpha, 0);\r\n\t\t}\r\n\r\n\t\tcol = item_rgb;\r\n\t\tif (!(ch.item_flags & IF_SELECTABLE))\r\n\t\t\tcol *= 0.2;\r\n\t\telse\r\n\t\t{\r\n\t\t\tif (!item_kactivechild)\r\n\t\t\t\titem_focuschange(ch, IF_KFOCUSED);\r\n\t\t\tif (mouseinbox(tpos, [w, 16]))\r\n\t\t\t\tcol_z = 0;\r\n\t\t\tif (ch.item_flags & IF_KFOCUSED)\r\n\t\t\t\tcol_x = 0;\r\n\t\t}\r\n\r\n\t\tui.drawstring(tpos + '4 4', ch.item_text, '8 8', col, item_alpha, 0);\r\n\t\ttpos_x += w;\r\n\t}\r\n\tui.drawfill(tpos + '0 15', [pos_x + item_size_x - tpos_x, 1], TD_TOP, item_alpha, 0);\t\t//top\r\n\tui.drawfill(pos + '0 16', [1, item_size_y - 16], TD_LFT, item_alpha, 0);\t\t\t\t\t//left\r\n\tui.drawfill(pos + [item_size_x-1, 16], [1, item_size_y - 17], TD_RGT, item_alpha, 0);\t//right\r\n\tui.drawfill(pos + [1, item_size_y-1], [item_size_x-1, 1], TD_BOT, item_alpha, 0);\t\t//bottom\r\n\r\n\tif (item_mactivechild != item_kactivechild)\r\n\t\titem_focuschange(item_kactivechild, IF_MFOCUSED);\t//give the tab full focus.\r\n\tch = item_kactivechild;\r\n\tif (ch)\r\n\t\tch.item_draw(pos + ch.item_position + [item_framesize[0], item_framesize[1]]);\r\n};\r\nfloat(vector pos, float scan, float char, float down) mitem_tabs::item_keypress =\r\n{\r\n\tlocal mitem ch;\r\n\tlocal vector tpos = pos;\r\n\tlocal vector sz;\r\n\tlocal float result;\r\n\r\n\tif (down && (scan == K_TOUCHTAP || scan == K_MOUSE1 || scan == K_MOUSE2 || scan == K_MOUSE3))\r\n\t{\r\n\t\tsz = [8,16];\r\n\t\t//to highlight the active tab, we draw the top line 1 pixel higher, and no bottom line\r\n\t\tfor (ch = this.item_children; ch; ch = ch.item_next)\r\n\t\t{\r\n\t\t\tsz_x = stringwidth(ch.item_text, TRUE, '8 8') + 8;\r\n\t\t\tif (mouseinbox(tpos, sz))\r\n\t\t\t{\r\n\t\t\t\titem_focuschange(ch, IF_KFOCUSED);\t//give the tab full focus.\r\n\t\t\t\treturn TRUE;\r\n\t\t\t}\r\n\t\t\t\r\n\t\t\ttpos_x += sz_x;\r\n\t\t}\r\n\t}\r\n\tch = item_kactivechild;\r\n\tif (ch)\r\n\t{\r\n\t\tresult = ch.item_keypress(pos + [item_framesize[0], item_framesize[1]] + ch.item_position, scan, char, down);\r\n\t\tif (!result && down)\r\n\t\t{\r\n\t\t\tif ((scan == K_TAB && !ui.shiftheld) || scan == K_GP_RSHOULDER || scan == K_RIGHTARROW)\r\n\t\t\t{\r\n\t\t\t\tch = ch.item_next;\r\n\t\t\t\tif (!ch)\r\n\t\t\t\t\tch = item_children;\r\n\t\t\t\titem_focuschange(ch, IF_KFOCUSED);\r\n\t\t\t\tresult = TRUE;\r\n\t\t\t}\r\n//\t\t\telse if ((scan == K_TAB && ui.shiftheld) || scan == K_GP_LSHOULDER || scan == K_LEFTARROW)\r\n//\t\t\t{\r\n//\t\t\t\tch = ch.item_next;\r\n//\t\t\t\tif (!ch)\r\n//\t\t\t\t\tch = item_children;\r\n//\t\t\t\titem_focuschange((ch.item_next?ch.item_next:this.item_children), IF_KFOCUSED);\r\n//\t\t\t\tresult = TRUE;\r\n//\t\t\t}\r\n\t\t}\r\n\t}\r\n\telse\r\n\t\tresult = FALSE;\r\n\treturn result;\r\n};\r\n/*void() mitem_tabs::item_resize =\r\n{\r\n\tlocal mitem ch;\r\n\tfor (ch = this.item_children; ch; ch = ch.item_next)\r\n\t{\r\n\t\tch.item_size = this.item_size;\r\n\t\tif (ch.item_resized)\r\n\t\t\tch.item_resized();\r\n\t}\r\n};*/\r\n\r\nmitem_tabs(vector sz) menuitemtabs_spawn =\r\n{\r\n\treturn spawn(mitem_tabs, item_size:sz);\r\n};\r\nmitem_tab(mitem_tabs tabs, string itname) menuitemtab_spawn =\r\n{\r\n\t//a tab itself is little different from a frame, just has no implicit focus, and has a title\r\n\tmitem_tab n = spawn(mitem_tab, item_text:itname, frame_hasscroll:TRUE);\r\n\r\n\ttabs.addr(n, RS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MIN | RS_X_MAX_PARENT_MAX|RS_Y_MAX_PARENT_MAX, '0 0', '0 0');\r\n\treturn n;\r\n};\r\n"
  },
  {
    "path": "quakec/menusys/menusys/mitems.qc",
    "content": "#ifdef MENU\r\n#define cltime time\r\n#endif\r\n\r\n#ifdef CSQC\r\n#define csqconly\r\n#else\r\n#define csqconly __strip\r\n#endif\r\n#ifdef MENU\r\n#define menuonly\r\n#else\r\n#define menuonly __strip\r\n#endif\r\n\r\n\r\n\r\n//#include \"mitems.qh\"\r\n\r\n__strip var float dp_workarounds;\r\n\r\n#define IF_SELECTABLE\t\t(1<<0)\t//can accept KFOCUSED/MFOCUSED and key events etc. cannot be selected otherwise.\r\n#define IF_INTERACT\t\t(1<<1)\t//generic interaction flag for use by the widgets themselves.\r\n#define IF_RESIZABLE\t\t(1<<2)\t//may be resized, either by parent (it takes the full space) or by user.\r\n#define IF_MFOCUSED\t\t(1<<3)\t//mouse is currently sitting over it\r\n#define IF_KFOCUSED\t\t(1<<4)\t//has keyboard focus\r\n#define IF_NOKILL\t\t(1<<5)\t//kill button is disabled. move to frame/menu?\r\n#define IF_DRAGGABLE\t\t(1<<6)\t//can be dragged. this is stupid.\r\n#define IF_DROPPABLE\t\t(1<<7)\t//dragged items can be dropped on this item.\r\n#define IF_CLIENTMOVED\t\t(1<<8)\t//recalc required client dimensions (and toggle scrollbars if needed). move to frame?\r\n#define IF_CENTERALIGN\t\t(1<<9)\t//\r\n#define IF_RIGHTALIGN\t\t(1<<10)\t//\r\n#define IF_NOCURSOR\t\t(1<<11)\t//when mgrabbed, the cursor will not be shown\r\n#define IF_ISFRAME\t\t(1<<12)\t//is derived from mitem_frame (helps debugging recurion).\r\n#define IF_FOCUSFOLLOWSMOUSE \t(1<<13)\t//on frames, child keyboard focus (mostly) follows the mouse cursor. not like windows, but handy on things with lots of buttons. annoying on the desktop. move to frame?\r\n#define IF_INVISIBLE\t\t(1<<14)\r\n\r\n#define RS_X_MIN_PARENT_MIN\t0x0000\r\n#define RS_X_MIN_PARENT_MID\t0x0001\r\n#define RS_X_MIN_PARENT_MAX\t0x0002\r\n#define RS_X_FRACTION\t\t\t0x0004\r\n\r\n#define RS_X_MAX_OWN_MIN\t\t0x0000\r\n#define RS_X_MAX_PARENT_MIN\t0x0010\r\n#define RS_X_MAX_PARENT_MID\t0x0020\r\n#define RS_X_MAX_PARENT_MAX\t0x0040\r\n\r\n#define RS_Y_MIN_PARENT_MIN\t0x0000\r\n#define RS_Y_MIN_PARENT_MID\t0x0100\r\n#define RS_Y_MIN_PARENT_MAX\t0x0200\r\n#define RS_Y_FRACTION\t\t\t0x0400\r\n\r\n#define RS_Y_MAX_OWN_MIN\t\t0x0000\r\n#define RS_Y_MAX_PARENT_MIN\t0x1000\r\n#define RS_Y_MAX_PARENT_MID\t0x2000\r\n#define RS_Y_MAX_PARENT_MAX\t0x4000\r\n\r\n//the 3d effect needs some sort of fake lighting values.\r\n//these are for bumps. invert for inset things.\r\n#define TD_TOP '0.8 0.8 0.8'\r\n#define TD_LFT '0.6 0.6 0.6'\r\n#define TD_RGT '0.2 0.2 0.2'\r\n#define TD_BOT '0.0 0.0 0.0'\r\n\r\n#ifndef MENUBACK_RGB\r\n#define MENUBACK_RGB '0.4 0.365 0.29'\r\n//#define MENUBACK_RGB '0.5 0.5 0.6'\r\n#endif\r\n#ifndef MENUBACK_ALPHA\r\n#define MENUBACK_ALPHA 0.8\r\n#endif\r\n\r\n//input helpers - gamepads need 3 different 'keys' for natural movement through menus. :(\r\n#define ISUPARROW(scan)   (scan == K_UPARROW || scan == K_GP_DPAD_UP || scan == K_GP_LTHUMB_UP || scan == K_GP_RTHUMB_UP)\r\n#define ISDOWNARROW(scan) (scan == K_DOWNARROW || scan == K_GP_DPAD_DOWN || scan == K_GP_LTHUMB_DOWN || scan == K_GP_RTHUMB_DOWN)\r\n#define ISLEFTARROW(scan) (scan == K_LEFTARROW || scan == K_GP_DPAD_LEFT || scan == K_GP_LTHUMB_LEFT || scan == K_GP_RTHUMB_LEFT)\r\n#define ISRIGHTARROW(scan) (scan == K_RIGHTARROW || scan == K_GP_DPAD_RIGHT || scan == K_GP_LTHUMB_RIGHT || scan == K_GP_RTHUMB_RIGHT)\r\n#define ISCONFIRMKEY(scan) (scan == K_ENTER || scan == K_GP_A || scan == K_GP_START)\r\n#define ISCANCELKEY(scan) (scan == K_ENTER || scan == K_GP_B || scan == K_GP_VIEW)\r\n\r\n//#ifdef TARGET_FTE\r\n//#pragma TARGET FTE\r\n//#endif\r\n\r\n//#define HEIRACHYDEBUG\t//enable this and press f1 to print out the current menuitem tree\r\n\r\nclass mitem_frame;\r\n.vector\t\tmins;\t\t//gravity mins\r\n.vector\t\tmaxs;\t\t//gravity mins\r\nclass mitem\r\n{\r\n\tvoid() \tmitem;\r\n\tvirtual void(vector pos)\t\t\t\t\t\t\t\t\t\titem_draw;\r\n\tvirtual float(vector pos, float scan, float char, float down)\titem_keypress = {return FALSE;};\r\n\tvirtual void(mitem newfocus, float changedflag)\t\t\t\t\titem_focuschange;\t//move into frame?\r\n\tvirtual string(string key)\t\t\t\t\t\t\t\t\t\tget;\r\n\tvirtual void(string key, string newval)\t\t\t\t\t\t\tset;\r\n\tvirtual float(string key)\t\t\t\t\t\t\t\t\t\tisvalid;\r\n\tvirtual void() \t\t\t\t\t\t\t\t\t\t\t\t\titem_remove;\r\n\tvirtual void() \t\t\t\t\t\t\t\t\t\t\t\t\titem_resized;\r\n\tfloat \t\t\titem_flags;\t\t//contains IF_ flags\r\n\tvector \t\titem_position;\t\t//relative to the parent's client area. which is admittedly not well defined...\r\n\tvector \t\titem_size;\t\t\t//interaction bounding box.\r\n\tfloat \t\t\titem_scale;\t\t//text etc scale\r\n\tvector \t\titem_rgb;\t\t\t//colours!\r\n\tfloat \t\t\titem_alpha;\t\t//transparency value\r\n\tstring \t\titem_text;\t\t\t//used as a unique identifier\r\n\tstring \t\titem_command;\t\t//something to do when clicked. move out?\r\n\tmitem_frame\titem_parent;\t\t//the item that contains us. make mitem_frame?\r\n\tmitem \t\titem_next;\t\t//the next child within the parent\r\n\r\n\tfloat\t\t\tresizeflags;\r\n\r\n\r\n\tstatic void() totop;\r\n};\r\n\r\n//this struct is used to access the various drawing functions. this allows the functions to be replaced for worldspace stuff\r\ntypedef struct uiinfo_s\r\n{\r\n\tvoid(float min_x, float min_y, float max_x, float max_y)\t\t\t\t\t\t\tsetcliparea;\r\n\tfloat(vector min, string imagename, vector size, vector col, float alph, float drawflag)\tdrawpic;\r\n\tfloat(vector min, vector size, vector col, float alph, float drawflag)\t\t\t\tdrawfill;\r\n\tfloat(vector min, float charcode, vector scale, vector col, float alph, float drawflag)\tdrawcharacter;\r\n\tfloat(vector min, string text, vector scale, vector col, float alph, float drawflag)\t\tdrawstring;\r\n\r\n\tmitem kgrabs;\t\t//kfocused or mfocused or both or none to say what sort of grabs is in effect.\r\n\tmitem mgrabs;\t\t//says who has stolen all input events.\r\n\r\n\tfloat ctrlheld;\t\t//ctrl is held.\r\n\tfloat shiftheld;\t//shift is held.\r\n\tfloat mousedown;\t//which mouse buttons are currently held.\r\n\tvector oldmousepos;\t//old mouse position, to track whether its moved.\r\n\tvector mousepos;\t//current mouse position.\r\n\tvector screensize;\t//total screen size\r\n\tvector drawrectmin;\t//minimum scissor region, to clamp children to within the confines of its parent.\r\n\tvector drawrectmax;\t//maximum scissor region\r\n\r\n#ifndef MENU\r\n\t//menuqc has no concept of the world and cannot display or query 3d positions nor projections. Any related UI elements are thus not available to menuqc.\r\n\t//these globals are not part of the ui struct either, because they're illegal in world UIs.\r\n\tfloat havemouseworld;\t\t//whether the mouseworld stuff is valid - ie: that the cursor is in a 3d view\r\n\tvector mouseworldnear;\t//position of the mouse cursor upon the near clip plane in world space\r\n\tvector mouseworldfar;\t\t//position of the mouse cursor upon the far(ish) clip plane in world space\r\n#endif\r\n} uiinfo_t;\r\nvar uiinfo_t ui =\r\n{\r\n\tdrawsetcliparea,\r\n\tdrawpic,\r\n\tdrawfill,\r\n\tdrawcharacter,\r\n\tdrawstring\r\n};\r\n\r\n\r\n\r\nvoid() queryscreensize =\r\n{\r\n#pragma warning disable F333\r\n#ifdef MENU\r\n\t//there is no proper way to do this.\r\n\t//fte thus has special checks for these cvars, and they should not be autocvars if you want them to work properly.\r\n\tui.screensize[0] = cvar(\"vid_conwidth\");\r\n\tui.screensize[1] = cvar(\"vid_conheight\");\r\n\tui.screensize[2] = 0;\r\n#endif\r\n#ifdef CSQC\r\n\t#ifndef CSQC_SIMPLE\r\n\t\t//make sure the screensize is set.\r\n\t\tnormalize('0 0 1');\t//when getproperty fails to return a meaningful value, make sure that we don't read some random stale value.\r\n\t\tui.screensize = (vector)getproperty(VF_SCREENVSIZE);\r\n\t\tif (ui.screensize[2])\t//lingering return value from normalize.\r\n\t\t{\r\n\t\t\tui.screensize[2] = 0;\r\n\t\t\tui.screensize[0] = cvar(\"vid_conwidth\");\r\n\t\t\tui.screensize[1] = cvar(\"vid_conheight\");\r\n\t\t}\r\n\t#endif\r\n#endif\r\n#pragma warning enable F333\r\n};\r\n\r\n//helper function\r\nfloat(vector minp, vector sz) mouseinbox =\r\n{\r\n\tif (ui.mousepos[0] < minp_x)\r\n\t\treturn FALSE;\r\n\tif (ui.mousepos[1] < minp_y)\r\n\t\treturn FALSE;\r\n\tif (ui.mousepos[0] >= minp_x + sz_x)\r\n\t\treturn FALSE;\r\n\tif (ui.mousepos[1] >= minp_y + sz_y)\r\n\t\treturn FALSE;\r\n\r\n\treturn TRUE;\r\n};\r\n\r\n\r\nvoid(mitem ch, vector parentsize) mitem_parentresized;\r\n\r\n#include \"mitem_frame.qc\"\r\nvoid() mitem::item_resized =\r\n{\r\n\tif (this.item_parent)\r\n\t\tthis.item_parent.item_flags |= IF_CLIENTMOVED;\r\n};\r\n\r\n\r\n#ifdef HEIRACHYDEBUG\r\nvoid mitem_printnode(float depth, mitem root, string prefix)\r\n{\r\n\tmitem ch;\r\n\tstring col = \"\";\r\n\tif (root.item_flags & IF_KFOCUSED && root.item_flags & IF_MFOCUSED)\r\n\t\tcol = \"^2\";\r\n\telse if (root.item_flags & IF_KFOCUSED)\r\n\t\tcol = \"^5\";\r\n\telse if (root.item_flags & IF_MFOCUSED)\r\n\t\tcol = \"^3\";\r\n\r\n\tprint(sprintf(\"%s%s%i:%s%s\\n\", col, strpad(depth, \"\"), root, prefix, root.item_text));\r\n\r\n\t//can only recurse into items which are actually frames.\r\n\tif (root.item_flags & IF_ISFRAME)\r\n\t{\r\n\t\tmitem_frame fr = (mitem_frame)root;\r\n\t\tdepth+=1;\r\n\t\tfor(ch = fr.item_children; ch; ch = ch.item_next)\r\n\t\t{\r\n\t\t\tif (ch.item_parent != root)\r\n\t\t\t\tprint(\"corrupt parent\\n\");\r\n\t\t\tif (ch.item_next == ch)\r\n\t\t\t{\r\n\t\t\t\tprint(\"infinite loop\\n\");\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\t\t\tstring pre = \"\";\r\n\t\t\tif (fr.item_mactivechild == ch)\r\n\t\t\t\tpre = strcat(pre, \"m\");\r\n\t\t\tif (fr.item_kactivechild == ch)\r\n\t\t\t\tpre = strcat(pre, \"k\");\r\n\t\t\tif (pre)\r\n\t\t\t\tpre = strcat(pre, \" \");\r\n\t\t\tmitem_printnode(depth, ch, pre);\r\n\t\t}\r\n\t}\r\n};\r\nvoid mitem_printtree(mitem_frame root, string from, float line)\r\n{\r\n\tprint(sprintf(\"%s:%g\\n\", from, line));\r\n\tmitem_printnode(0, root, \"\");\r\n}\r\n#endif\r\n\r\nstring(string key) mitem::get =\r\n{\r\n\t//walk through to the parent for menu parents to track all this.\r\n\tif (this.item_parent)\r\n\t\treturn this.item_parent.get(key);\r\n\telse\t//no parent, just assume its a cvar.\r\n\t\treturn cvar_string(key);\r\n};\r\nvoid(string key, string newval) mitem::set =\r\n{\r\n\t//walk through to the parent for menu parents to track all this.\r\n\tif (this.item_parent)\r\n\t\tthis.item_parent.set(key, newval);\r\n\telse\t//no parent, just assume its a cvar.\r\n\t\tcvar_set(key, newval);\r\n};\r\nfloat(string key) mitem::isvalid =\r\n{\r\n\t//walk through to the parent for menu parents to track all this.\r\n\tif (this.item_parent)\r\n\t\treturn this.item_parent.isvalid(key);\r\n\telse\t//no parent, just assume its a cvar.\r\n\t{\r\n\t\tfloat t = cvar_type(key);\r\n\t\treturn (t & CVAR_TYPEFLAG_EXISTS) && !(t & CVAR_TYPEFLAG_READONLY);\r\n\t}\r\n};\r\n\r\n\r\n/***************************************************************************\r\nbasic 'null' item, for completeness, every other item logically inherits from this.\r\ndrawing this just shows its text right-aligned at w/2. most things will want to override this.\r\n*/\r\nvoid(mitem newfocus, float flag) mitem::item_focuschange =\r\n{\r\n};\r\n\r\n//z order is determined by the list order. the ones at the end (oldest) are on top.\r\nvoid() mitem::totop =\r\n{\r\n\tmitem_frame p = item_parent;\r\n\tmitem prev;\r\n\t//unlink it\r\n\tif (p.item_children == this)\r\n\t\tprev = p.item_children = item_next;\r\n\telse\r\n\t{\r\n\t\tfor (prev = p.item_children; prev; prev = prev.item_next)\r\n\t\t{\r\n\t\t\tif (prev.item_next == this)\r\n\t\t\t{\r\n\t\t\t\tprev.item_next = item_next;\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\titem_next = __NULL__;\r\n\tif (prev)\r\n\t{\r\n\t\t//add it on the end\r\n\t\twhile(prev.item_next)\r\n\t\t\tprev = prev.item_next;\r\n\t\tprev.item_next = this;\r\n\t}\r\n\telse\r\n\t\tp.item_children = this;\r\n};\r\n\r\nvector(mitem it) menuitem_textcolour =\r\n{\r\n\tlocal vector col;\r\n\tcol = it.item_rgb;\r\n\tif (!(it.item_flags & IF_SELECTABLE) && it.item_keypress && it.item_command != \"\")\r\n\t\tcol *= 0.2;\r\n\telse\r\n\t{\r\n\t\tif (it.item_flags & IF_MFOCUSED)\r\n\t\t\tcol_z = 0;\r\n\t\tif (it.item_flags & IF_KFOCUSED)\r\n\t\t\tcol_x = 0;\r\n\t}\r\n\treturn col;\r\n};\r\nvoid(vector pos) mitem::item_draw =\r\n{\r\n\tvector col;\r\n\tpos_x += this.item_size[0] / 2 - stringwidth(this.item_text, TRUE, '1 1 0'*this.item_scale) - 8;\r\n\tcol = menuitem_textcolour(this);\r\n\tui.drawstring(pos, this.item_text, '1 1 0' * this.item_scale, col, this.item_alpha, 0);\r\n};\r\nvoid() mitem::item_remove =\r\n{\r\n\tlocal mitem ch;\r\n\t//any children got removed by the frame code.\r\n\r\n\t//make sure the item is removed from its parent.\r\n\tlocal mitem_frame p = this.item_parent;\r\n\tif (p)\r\n\t{\r\n\t\tif (p.item_exclusive == this)\r\n\t\t\tp.item_exclusive = __NULL__;\r\n\r\n\t\tif (p.item_children == this)\r\n\t\t\tp.item_children = this.item_next;\r\n\t\telse\r\n\t\t{\r\n\t\t\tfor(ch = p.item_children; ch; ch = ch.item_next)\r\n\t\t\t{\r\n\t\t\t\tif (ch.item_next == this)\r\n\t\t\t\t{\r\n\t\t\t\t\tch.item_next = this.item_next;\r\n\t\t\t\t\tbreak;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\t//pick a different item on the parent.\r\n\tif (p.item_mactivechild == this)\r\n\t\tp.item_focuschange(__NULL__, IF_MFOCUSED);\r\n\tif (p.item_kactivechild == this)\r\n\t{\r\n\t\tfor (ch = p.item_children; ch; ch = ch.item_next)\r\n\t\t{\r\n\t\t\tif (ch.item_flags & IF_SELECTABLE)\r\n\t\t\t\tbreak;\r\n\t\t}\r\n\t\tp.item_focuschange(ch, IF_KFOCUSED);\r\n\t}\r\n\tif (ui.kgrabs == this)\r\n\t\tui.kgrabs = __NULL__;\r\n\tif (ui.mgrabs == this)\r\n\t\tui.mgrabs = __NULL__;\r\n\tremove((entity)this);\r\n\r\n\t//force mousefocus to update\r\n\tui.oldmousepos = '-1 -1';\r\n};\r\nvoid() mitem::mitem =\r\n{\r\n\tif (!this.item_scale)\r\n\t\tthis.item_scale = 1;\r\n\tif (!this.item_rgb)\r\n\t\tthis.item_rgb = '1 1 1';\r\n\tif (!this.item_alpha)\r\n\t\tthis.item_alpha = 1;\r\n\r\n\t//force mousefocus to update\r\n\tui.oldmousepos = '-1 -1';\r\n};\r\n"
  },
  {
    "path": "quakec/menusys/menusys/mitems_common.qc",
    "content": "/***************************************************************************\r\nsimple block-fill item\r\nnon-interactable.\r\n*/\r\nclass mitem_fill : mitem\r\n{\r\n\tvirtual void(vector pos) item_draw =\r\n\t{\r\n\t\tui.drawfill(pos, this.item_size, this.item_rgb, this.item_alpha, 0);\r\n\t};\r\n};\r\n#define menuitemfill_spawn(sz,rgb,alph) spawn(mitem_fill, item_size:sz, item_rgb:rgb, item_alpha:alph)\r\n\r\n/***************************************************************************\r\nbasic picture item.\r\nnon-interactable.\r\n\r\nitem_text: the normal image to use\r\nitem_text_mactive: the image to use when the mouse is over it.\r\nitem_size: if not set, will be set to the size of the image named by item_text. if only y is set, x will be sized to match aspect with y.\r\n*/\r\nclass mitem_pic : mitem\r\n{\r\n\tstring item_text_mactive;\r\n\tvirtual void(vector pos) item_draw =\r\n\t{\r\n\t\tif (item_text_mactive != \"\" && (item_flags & IF_MFOCUSED))\r\n\t\t\tui.drawpic(pos, item_text_mactive, item_size, item_rgb, item_alpha, 0);\r\n\t\telse\r\n\t\t\tui.drawpic(pos, item_text, item_size, item_rgb, item_alpha, 0);\r\n\t};\r\n\tvoid() mitem_pic =\r\n\t{\r\n\t\tif (dp_workarounds)\r\n\t\t{\r\n\t\t\tif (substring(item_text, -4, 4) == \".lmp\")\r\n\t\t\t\titem_text = substring(item_text, 0, -5);\r\n\t\t\tif (substring(item_text_mactive , -4, 5) == \".lmp\")\r\n\t\t\t\titem_text_mactive = substring(item_text_mactive, 0, -4);\r\n\t\t}\r\n\r\n\t\titem_text = strzone(item_text);\r\n\t\tprecache_pic(item_text);\r\n\t\tif (item_text_mactive)\r\n\t\t{\r\n\t\t\titem_text_mactive = strzone(item_text_mactive);\r\n\t\t\tprecache_pic(item_text_mactive);\r\n\t\t}\r\n\r\n\t\tif (!item_size[0])\r\n\t\t{\r\n\t\t\tfloat y = item_size[1];\r\n\t\t\titem_size = drawgetimagesize(item_text);\r\n\t\t\tif (y)\t//rescale x to ma\r\n\t\t\t{\r\n\t\t\t\tif (!item_size[1])\t//bad image? don't glitch out too much.\r\n\t\t\t\t\titem_size = item_size[0] * '1 1 0';\r\n\t\t\t\telse\r\n\t\t\t\t\titem_size = [item_size[0] * (y / item_size[1]), y, 0];\r\n\t\t\t}\r\n\t\t}\r\n\t};\r\n\tvirtual void() item_remove = \r\n\t{\r\n\t\tstrunzone(item_text);\r\n\t\tif (item_text_mactive)\r\n\t\t\tstrunzone(item_text_mactive);\r\n\t\tsuper::item_remove();\r\n\t};\r\n};\r\n#define menuitempic_spawn(img,sz) spawn(mitem_pic, item_text:img, item_size:sz)\r\n\r\n/***************************************************************************\r\nbasic text item.\r\ninteractable - executes a given console command.\r\n*/\r\nclass mitem_text : mitem\r\n{\r\n\tvirtual void(vector pos) item_draw =\r\n\t{\r\n\t\tvector rgb = menuitem_textcolour(this);\r\n\t\tfloat w;\r\n\t\tif (item_flags & IF_CENTERALIGN)\r\n\t\t{\r\n\t\t\tw = stringwidth(item_text, TRUE, '1 1 0'*item_scale);\r\n\t\t\tui.drawstring(pos + [(item_size_x-w)/2, 0], item_text, '1 1 0' * item_scale, rgb, item_alpha, 0);\r\n\t\t}\r\n\t\telse if (item_flags & IF_RIGHTALIGN)\r\n\t\t{\r\n\t\t\tw = stringwidth(item_text, TRUE, '1 1 0'*item_scale);\r\n\t\t\tui.drawstring(pos + [(item_size_x-w), 0], item_text, '1 1 0' * item_scale, rgb, item_alpha, 0);\r\n\t\t}\r\n\t\telse\r\n\t\t\tui.drawstring(pos, item_text, '1 1 0' * item_scale, rgb, item_alpha, 0);\r\n\t};\r\n\tvirtual float(vector pos, float scan, float char, float down) item_keypress =\r\n\t{\r\n\t\tif (this.item_command)\r\n\t\t{\r\n\t\t\tif (!down)\r\n\t\t\t\treturn FALSE;\r\n\t\t\tif (ISCONFIRMKEY(scan) || ((scan == K_TOUCHTAP || scan == K_MOUSE1) && mouseinbox(pos, this.item_size)))\r\n\t\t\t{\r\n\t\t\t\titem_parent.item_execcommand(this, this.item_command);\r\n//\t\t\t\tlocalcmd(strcat(this.item_command, \"\\n\"));\r\n\t\t\t\treturn TRUE;\r\n\t\t\t}\r\n\t\t}\r\n\t\treturn FALSE;\r\n\t};\r\n\r\n\t//zone+unzone input strings as needed\r\n\tvirtual void() item_remove = \r\n\t{\r\n\t\tstrunzone(item_text);\r\n\t\tif (item_command)\r\n\t\t\tstrunzone(item_command);\r\n\t\tsuper::item_remove();\r\n\t};\r\n\tvoid() mitem_text =\r\n\t{\r\n\t\titem_text = strzone(item_text);\r\n\t\tif (item_command != \"\")\r\n\t\t{\r\n\t\t\titem_command = strzone(item_command);\r\n\t\t\titem_flags |= IF_SELECTABLE;\r\n\t\t}\r\n\t};\r\n};\r\nmitem(string text, string command, float height) menuitemtext_spawn =\r\n{\r\n\treturn spawn(mitem_text, item_scale:height, item_text:text, item_command:command, item_size:[stringwidth(text, TRUE, '1 1 0'*height), height, 0]);\r\n};\r\n\r\n\r\nclass mitem_label : mitem\r\n{\t//class that queries its frame to find the text to show.\r\n\tmitem_vslider vslider;\r\n\tvirtual void(vector pos) item_draw =\r\n\t{\r\n\t\tvector rgb = menuitem_textcolour(this);\r\n\t\tstring text = get(item_text);\r\n\t\tfloat fl = 2;\t//top-align\r\n\t\tvector textpos = pos;\r\n\t\tvector clientsize = item_size;\r\n\t\tif (item_flags & IF_CENTERALIGN)\r\n\t\t\tfl |= 0;\t//default is to center align\r\n\t\telse if (item_flags & IF_RIGHTALIGN)\r\n\t\t\tfl |= 4;\t//right align.\r\n\t\telse\r\n\t\t\tfl |= 1;\t//left align.\r\n\r\n\t\tif (vslider)\r\n\t\t{\r\n\t\t\ttextpos[1] -= vslider.val;\r\n\t\t\tclientsize[1] += vslider.val;\r\n\t\t\tclientsize[0] -= vslider.item_size[0];\r\n\t\t}\r\n\r\n\t\tlocal vector omin = ui.drawrectmin, omax = ui.drawrectmax;\r\n\r\n\t\t//clip the draw rect to our area, so text doesn't appear outside it. don't draw if its inverted.\r\n\t\tif (pos_x > ui.drawrectmin[0])\r\n\t\t\tui.drawrectmin[0] = pos_x;\r\n\t\tif (pos_y > ui.drawrectmin[1])\r\n\t\t\tui.drawrectmin[1] = pos_y;\r\n\t\tif (pos_x+clientsize_x < ui.drawrectmax[0])\r\n\t\t\tui.drawrectmax[0] = pos_x+clientsize_x;\r\n\t\tif (pos_y+clientsize_y < ui.drawrectmax[1])\r\n\t\t\tui.drawrectmax[1] = pos_y+clientsize[1];\r\n\t\tif (ui.drawrectmax[0] > ui.drawrectmin[0] && ui.drawrectmax[1] > ui.drawrectmin[1])\r\n\t\t{\r\n\t\t\tui.setcliparea(ui.drawrectmin[0], ui.drawrectmin[1], ui.drawrectmax[0] - ui.drawrectmin[0], ui.drawrectmax[1] - ui.drawrectmin[1]);\r\n\t\t\tif (checkbuiltin(drawtextfield))\r\n\t\t\t\tclientsize[1] = drawtextfield(textpos, clientsize, fl, text) * item_scale;\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tdrawstring(textpos, text, [8,8], [1,1,1],1,0);\r\n\t\t\t\tclientsize[1] = 8;\r\n\t\t\t}\r\n\t\t\tui.setcliparea(omin_x, omin_y, omax_x - omin_x, omax_y - omin_y);\r\n\r\n\t\t\tif (vslider)\r\n\t\t\t{\r\n\t\t\t\tvslider.item_size[1] = item_size[1];\r\n\t\t\t\tif (ui.mousepos != ui.oldmousepos)\r\n\t\t\t\t{\r\n\t\t\t\t\tif (mouseinbox(pos + [clientsize[0], 0], vslider.item_size))\r\n\t\t\t\t\t\tthis.item_focuschange(vslider, IF_MFOCUSED);\r\n\t\t\t\t}\r\n\t\t\t\tvslider.item_draw(pos + [clientsize[0], 0]);\r\n\t\t\t}\r\n\t\t}\r\n\t\tui.drawrectmin = omin;\r\n\t\tui.drawrectmax = omax;\r\n\r\n\t\tif (clientsize[1] > item_size[1])\r\n\t\t{\t//enough text that we need a scrollbar.\r\n\t\t\tif (!vslider)\r\n\t\t\t\tvslider = spawn(mitem_vslider, val:0, stride:8);\r\n\t\t\tvslider.maxv = clientsize[1] - item_size[1];\r\n\t\t\titem_flags |= IF_SELECTABLE;\r\n\t\t}\r\n\t\telse if (vslider)\r\n\t\t{\t//the text got shorter...\r\n\t\t\tvslider.item_remove();\r\n\t\t\tvslider = (mitem_vslider)__NULL__;\r\n\t\t\tif (item_command == \"\")\r\n\t\t\t\titem_flags &~= IF_SELECTABLE;\r\n\t\t}\r\n\t};\r\n\tvirtual float(vector pos, float scan, float char, float down) item_keypress =\r\n\t{\r\n\t\tif (vslider)\r\n\t\t\tif (vslider.item_keypress(pos, scan, char, down))\r\n\t\t\t\treturn TRUE;\r\n\t\tif (this.item_command)\r\n\t\t{\r\n\t\t\tif (!down)\r\n\t\t\t\treturn FALSE;\r\n\t\t\tif (ISCONFIRMKEY(scan) || ((scan == K_TOUCHTAP || scan == K_MOUSE1) && mouseinbox(pos, this.item_size)))\r\n\t\t\t{\r\n\t\t\t\titem_parent.item_execcommand(this, this.item_command);\r\n//\t\t\t\tlocalcmd(strcat(this.item_command, \"\\n\"));\r\n\t\t\t\treturn TRUE;\r\n\t\t\t}\r\n\t\t}\r\n\t\treturn FALSE;\r\n\t};\r\n\r\n\t//zone+unzone input strings as needed\r\n\tvirtual void() item_remove = \r\n\t{\r\n\t\tif (item_command)\r\n\t\t\tstrunzone(item_command);\r\n\t\tif (vslider)\r\n\t\t{\r\n\t\t\tvslider.item_remove();\r\n\t\t\tvslider = (mitem_vslider)__NULL__;\r\n\t\t}\r\n\t\tsuper::item_remove();\r\n\t};\r\n\tvoid() mitem_label =\r\n\t{\r\n\t\tif (item_command != \"\")\r\n\t\t{\r\n\t\t\titem_command = strzone(item_command);\r\n\t\t\titem_flags |= IF_SELECTABLE;\r\n\t\t}\r\n\t};\r\n};\r\n\r\n\r\n/***************************************************************************\r\nbasic text item.\r\nidentical to text, but includes a 3dish border.\r\n*/\r\nclass mitem_button : mitem\r\n{\r\n\tvirtual void(vector pos) item_draw =\r\n\t{\r\n\t\tui.drawfill(pos, [this.item_size[0], 1], TD_TOP, this.item_alpha, 0);\r\n\t\tui.drawfill(pos, [1, this.item_size[1] - 1], TD_LFT, this.item_alpha, 0);\r\n\t\tui.drawfill(pos + [this.item_size[0]-1, 1], [1, this.item_size[1] - 1], TD_RGT, this.item_alpha, 0);\r\n\t\tui.drawfill(pos + [0, this.item_size[1]-1], [this.item_size[0], 1], TD_BOT, this.item_alpha, 0);\r\n\r\n\t\tpos_x += (this.item_size[0] - stringwidth(this.item_text, TRUE, '1 1 0'*this.item_scale)) * 0.5;\r\n\t\tpos_y += (this.item_size[1] - this.item_scale)*0.5;\r\n\t\tui.drawstring(pos, this.item_text, '1 1 0' * this.item_scale, menuitem_textcolour(this), this.item_alpha, 0);\r\n\t};\r\n\tvirtual float(vector pos, float scan, float char, float down) item_keypress =\r\n\t{\r\n\t\tif (!down)\r\n\t\t\treturn FALSE;\r\n\t\tif (ISCONFIRMKEY(scan) || (scan == K_MOUSE1 && mouseinbox(pos, this.item_size)))\r\n\t\t\titem_parent.item_execcommand(this, item_command);\r\n\t\telse\r\n\t\t\treturn FALSE;\r\n\t\treturn TRUE;\r\n\t};\r\n\tvirtual void() item_remove = \r\n\t{\r\n\t\tstrunzone(item_text);\r\n\t\tif (item_command)\r\n\t\t\tstrunzone(item_command);\r\n\t\tsuper::item_remove();\r\n\t};\r\n};\r\nmitem_button(string text, string command, vector sz) menuitembutton_spawn =\r\n{\r\n\tmitem_button n = spawn(mitem_button);\r\n\tn.item_scale = sz_y - 4;\r\n\tn.item_text = strzone(text);\r\n\tn.item_size = sz;\r\n\r\n\tif (command != \"\")\r\n\t{\r\n\t\tn.item_command = strzone(command);\r\n\t\tn.item_flags |= IF_SELECTABLE;\r\n\t}\t\r\n\treturn n;\r\n};\r\n\r\n"
  },
  {
    "path": "quakec/menusys/menusys/readme.txt",
    "content": "integrating into an existing mod:\r\nthe csqc/menu needs to instanciate a mitem_desktop (or derived class). items_keypress+items_draw need to be called in the relevent places with the relevent arguments, then you can start creating children and throwing them at the screen.\r\nthe desktop is the root object, and tracks whether the mouse/keys should be grabbed.\r\n\r\ngeneral overview:\r\nThe menu system is build from a heirachy/tree of mitem nodes.\r\nTo draw the menu, the tree is walked from the root (the 'desktop' object) through its children (pictures, text, or whatever).\r\nEach mitem inherits its various properties from a parent object using classes. For instance, the desktop inherits from mitem_frame(read: a container object), which in turn inherits from mitem(the root type).\r\nWhen a container is moved or resized, all of its children (and their children) will automatically be resized accordingly. Once the container is destroyed, the children will also be automatically destroyed.\r\nDrawing items is fairly simple. The root node is told to draw itself. Once it has drawn its background, it walks through its children asking them to draw themselves (telling the child exactly where it is on the scree). Other containers(like windows) do the same. In this way the entire tree is drawn.\r\nInput happens in a somewhat similar manner. The parent decides which child the mouse is over, and sends mouse or keyboard to the relevent focused child. As a special exception, if an element has set ui.*grabs to refer to itself, that object gets to snoop on input first before even the desktop gets a chance.\r\nTo make a quake-style menu, you should create an mitem_exmenu, and then call the desktop's .add method to insert it. Then you need to spawn things like cvar sliders/combos/etc and use one of the menu's add methods to place it on the menu/window. Check the example code.\r\n\r\nItems that don't specify a method will inherit that method from its parent.\r\nIf you want only a minor change, for instance drawing a background under the item, you can make an item_draw method which draws the background then calls super::item_draw() with the same arguments in order to provide the parent's normal behaviour. Doing this with a menu's get+set methods allows you to use slider/etc mitems with things other than direct cvars (if you want an 'apply changes' button or so).\r\n\r\n\r\nglobals:\r\nautocvar_menu_font:\tdefaults to 'cour', which will only work in windows, fallback font otherwise. The menu code will register+use a font with this name suitable for 8 vpixels high, and 16vpixels.\r\ndp_workarounds:\t\tneeded to work around DP bugs. Set to 1 if you detect that the code is running inside DP.\r\nui:\t\t\t\ta struct containing the current ui state. Can be temporarily overwritten for alternative menu systems (like ones drawn on walls or whatever).\r\n\t\t\t\tprobably would have been nicer if it was a pointer, but I didn't want to depend upon those.\r\n\t\t\t\tWithin the ui struct are a few functions which typically just map to the equivelent builtins.\r\n\t\t\t\tPlacing a copy of these in the ui struct allows them to be overridden in order to project a menu/ui upon a wall efficiently without extra code.\r\n\t\t\t\tthese are the functions available: setcliparea, drawpic, drawfill, drawcharacter, drawstring\r\nui.mousedown:\t\tthe mouse buttons that are currently pressed.\r\nui.oldmousepos:\t\tthe mouse position that was set in the previous frame. this can be used to detect whether the mouse has moved.\r\nui.mousepos:\t\tthe current virtual screen position of the mouse cursor.\r\nui.screensize:\t\tthe virtual resolution of the screen - the dimensions the UI needs to fill.\r\nui.drawrectmin:\t\tthe current viewport scissor min position (so over-sized items can draw outside of their containing frames without issue).\r\nui.drawrectmax:\t\tthe current viewport scissor max position (so over-sized items can draw outside of their containing frames without issue).\r\nui.havemouseworld:\twhether the mouseworld[near/far] globals are valid - ie: that the cursor is in a 3d view. only exists in csqc.\r\nui.mouseworldnear:\tposition of the mouse cursor upon the near clip plane in world space.\r\nui.mouseworldfar:\tposition of the mouse cursor upon the far(ish) clip plane in world space. You can trace a line between these two vectors to detect which entities the mouse should interact with.\r\nui.kgrabs:\t\t\tset this global in order to redirect all keyboard buttons to this object.\r\nui.mgrabs:\t\t\tset this global in order to redirect all mouse buttons to this object. If the object that has focus also has item_flags&IF_NOCURSOR, the mouse cursor will be hidden.\r\n\r\nhelper functions:\r\nmouseinbox:\t\treturns whether ui.mousepos is within the box specified by the min+size arguments.\r\n\r\nmitems.qc:class mitem\r\n\troot menuitem object that all else inherits from\r\n\r\n\tEVENTS:\r\n\tvoid item_draw(pos): override this to replace how the item displays. the default is quite lame. pos is the virtual screen position.\r\n\tfloat item_keypress(vector pos,float scan,float char, float down): the object got a keyboard or mouse click. pos is the virtual screen position. scan is the K_FOO scancode constant. char is the unicode value of the codepoint. either may be 0. some systems might always use 0 for one of scan+char. down says that the key was just pressed, false means it just got released.\r\n\tvoid item_focuschange(mitem newfocus, float changedflag): the object's focus changed. newfocus is the item that gained focus, changedflag is either IF_MFOCUSED, IF_KFOCUSED, or both. check this against newfocus to see if this object gained focus.\r\n\tstring(string key) get:\thelper function. calls the parent's get method. \r\n\tvoid(string key, string newval) set:\thelper function. calls the parent's set method. \r\n\tvoid() item_remove: the item got removed. this callback can be used to free memory. be sure to notify super.\r\n\tvoid() item_resized: the item has been resized or moved. provided to resize+move helper objects to match a parent, or recalculate cached extents or whatever.\r\n\r\n\tFIELDS:\r\n\titem_flags: various flags\r\n\t\tIF_SELECTABLE: item can be selected, either with mouse or keyboard.\r\n\t\tIF_INTERACT: flag reserved for child classes to use.\r\n\t\tIF_RESIZABLE: item can be resized. menus will show resize corners.\r\n\t\tIF_MFOCUSED: item has mouse focus (parents will retain the flag)\r\n\t\tIF_KFOCUSED: item has keyboard focus (parents will retain the flag)\r\n\t\tIF_NOKILL: item has been marked as resisting closure. This applies to menus and will disable the implicit 'x' button.\r\n\t\tIF_NOCURSOR: if the item grabs the mouse, the cursor will be hidden, allowing mlook to still work when some widget is grabbing everything (eg: an exmenu using right-click to look).\r\n\titem_position: the (virtual) position of the object relative to its parent.\r\n\titem_size: the current (virtual) size of the object.\r\n\titem_scale: typically serves to rescale text. not used by the menu framework itself.\r\n\titem_rgb: the colour field for the item. typically defaults to '1 1 1' if not otherwise set.\r\n\titem_alpha: the alpha value for the item. typically defaults to 1.\r\n\titem_text: typically used as the caption. also used as a searchable item name.\r\n\titem_command: the cvar or console command associated with the object. potentially other uses. yay for repurposing fields.\r\n\titem_parent: the frame that contains this object.\r\n\titem_next: the next sibling within the parent.\r\n\tmins: the resize gravity bias for the 'min' pos.\r\n\tmaxs: the resize gravity bias for the 'max' pos.\r\n\tresizeflags: controls the meaning of the mins+maxs positions.\r\n\t\tRS_[X/Y]_[MIN/MAX]_PARENT_MIN: the min/max position is relative to the top/left of the parent.\r\n\t\tRS_[X/Y]_[MIN/MAX]_PARENT_MID: the min/max position is relative to the center of the parent.\r\n\t\tRS_[X/Y]_[MIN/MAX]_PARENT_MAX: the min/max position is relative to the bottom/right of the parent.\r\n\t\tRS_[X/Y]_FRACTION: the min+max positions are 0-1 values scaled within the min/max position of the parent.\r\n\t\tRS_[X/Y]_MAX_OWN_MIN: the max position is set relative to the objects own minimum size, giving an absolute object size.\r\n\r\n\tstatic functions:\r\n\ttotop(): moves the object to the top of the parent's z-order. drawn last, this object will now appear over everything else. does not affect focus.\r\n\r\n\tnon-member functions (FIXME):\r\n\tmitem_printtree: debug function to print out a list of the items children+siblings.\r\n\tqueryscreensize: updates the screensize global.\r\n\tmitem_parentresized: updates an item's position and size according to its resizeflags.\r\n\tmenuitem_textcolour: helper function. determines the text colour to use for cvar widgets based upon selectable and mouse/keyboard focus.\r\n\r\nmitem_frame.qc:class mitem_frame : mitem\r\n\tgeneric borderless container object for other items.\r\n\r\n\tFIELDS:\r\n\titem_framesize_x: border width.\tchildren will not overlap this.\r\n\titem_framesize_y: border at top. children will not overlap this.\r\n\titem_framesize_z: border at bottom. children will not overlap this.\r\n\tframe_hasscroll: if true, enables a vertical slider so the frame is still usable if the screen is too small.\r\n\titem_children: the first child object of the frame\r\n\titem_mactivechild: the current child that has the mouse over it.\r\n\titem_kactivechild: the current child that has keyboard focus.\r\n\r\n\tstatic functions:\r\n\tfindchild(string title): scans through a frame's children looking for an item with a matching item_text.\r\n\tadd(mitem newitem, float resizeflags, vector originmin, vector originmax): adds an item to a frame, with specified gravity+position etc settings. see mitem::resizeflags for details.\r\n\tadda(mitem newitem, vector pos): adds an item to a frame with preset item_size and position relative to the top-left of the parent frame.\r\n\taddr(mitem newitem, float resizeflags, vector originmin, vector originmax): adds an item to a frame, with specified gravity+position etc settings, with reversed z order. this item will be drawn over the top of the previous objects, and is the more natural ordering.\r\n\taddc(mitem newitem, float ypos): adds an item to a frame with a preset item_size at a specific y position. multiple objects with the same mins_y+maxs_y will automatically be spread across horizontally with a gap between each.\r\n\r\nmitem_bind.qc:class mitem_bind : mitem\r\n\ta widget to change a key binding. up to two keys will be listed. additional keys will currently remain hidden.\r\n\titem_text: the description of the key binding (ie: \"Attack\")\r\n\titem_command: the console command to bind the key to (ie: \"+attack\")\r\n\r\nmitem_checkbox.qc:class mitem_check : mitem\r\n\ta true/false cvar checkbox.\r\n\titem_text: the description of the setting\r\n\titem_command: the name of the cvar to toggle\r\n\r\n\toptional factory: mitem_check(string text, string command, vector sz) menuitemcheck_spawn;\r\n\r\nmitem_colours.qc:class mitem_colours : mitem\r\n\tA simple colour picker. supports only hue.\r\n\titem_text: the description of the setting\r\n\titem_command: the name of the cvar to change (sets to 0xRRGGBB notation).\r\n\r\n\tfactory: mitem_colours(string text, string command, vector sz) menuitemcolour_spawn;\r\n\r\nmitem_combo.qc:class mitem_combo : mitem\r\n\tmultiple choice widget.\r\n\titem_text: the description of the setting\r\n\tmstrlist: a list of the valid settings, in \"\\\"value\\\" \\\"description\\\" \\\"value\\\" \\\"description\\\"\" notation.\r\n\tFIXME: must spawn through: mitem_combo(string text, string command, vector sz, string valuelist) menuitemcombo_spawn;\r\n\r\nmitem_combo.qc:class mitem_combo_popup : mitem\r\n\tinternal friend class of the combo.\r\n\tthis holds the list of options when clicked.\r\n\tyou should not use this class directly.\r\n\r\nmitem_desktop.qc:class mitem_desktop : mitem_frame\r\n\tthe root item that should be used as the parent of all other elements in order to be visible.\r\n\tforces itself to fullscreen, handles grabs, etc.\r\n\tin csqc, will display the default game view (including split screen views).\r\n\r\n\tcsqc\r\n\tfloat(mitem_desktop desktop, float evtype, float scanx, float chary, float devid) items_keypress\r\n\tmenuqc:\r\n\tfloat(mitem_desktop desktop, float scan, float char, float down) items_keypress\r\n\tboth:\r\n\tvoid(mitem_desktop desktop) items_draw\r\n\r\n\r\nmitem_edittext.qc:class mitem_edit : mitem\r\n\ttext entry widget (typically) attached to a cvar.\r\n\titem_text: the short description of the setting.\r\n\titem_command: the name of the cvar to receive a new value/be displayed\r\n\r\nmitem_exmenu.qc:class mitem_exmenu : mitem_frame\r\n\t'exclusive' menu object.\r\n\tthis container has no borders.\r\n\tdoes not provide a background. use a mitem_pic or mitem_fill for that.\r\n\tnot much more than a frame, but can take unhandled escape/right click to close the menu.\r\n\ttypically used fullscreen.\r\n\titem_text: a searchable identifier for the object\r\n\titem_command: the console command to execute when the item is removed. used to restore the parent menu.\r\n\r\nmitem_frame.qc:class mitem_vslider : mitem\r\n\tvertical up/down slider not attached to a cvar.\r\n\tused to scroll frames up+down for when you have too many items in there for the current video mode.\r\n\tfixme: document\r\n\r\n\tfactory macro: menuitemframe_spawn(sz)\r\n\ttypically created via inheritance.\r\n\r\nmitem_menu.qc:class mitem_menu : mitem_frame\r\n\tmovable resizable window\r\n\r\n\titem_flags:\r\n\t\tIF_RESIZABLE: enables resize handles on left+bottom+right+lower corners.\r\n\t\tIF_NOKILL: disables the 'x' button on the top-right.\r\n\tnon-member construtor:\r\n\tmenu_spawn(mitem_frame desktop, string mname, vector sz): centers and automatically adds the object. required for the default border size. do not call any of the add* methods yourself if you use this.\r\n\r\nmitem_slider.qc:class mitem_hslider : mitem\r\n\thorizontal slider attached to a cvar.\r\n\titem_text: the short description of the setting.\r\n\titem_command: the name of the cvar to change.\r\n\titem_slidercontrols_x: the minimum value.\r\n\titem_slidercontrols_y: the maximum value.\r\n\titem_slidercontrols_z: how much to change the cvar by each time the user uses the left/right arrows to change its value.\r\n\r\nmitem_tabs.qc:class mitem_tabs : mitem_frame\r\n\ttabstrip object that appears presenting the various tab options.\r\n\tacts as a container only for mitem_tab objects which are the real containers.\r\n\r\nmitem_tabs.qc:class mitem_tab : mitem_frame\r\n\tchild of a tabstrip that contains the various elements sited upon a single tab.\r\n\titem_text: the caption displayed for the tab.\r\n\r\nmitems_common.qc:class mitem_fill : mitem\r\n\tfills the screen region with a block of colour.\r\n\titem_rgb: colour to fill it with.\r\n\titem_alpha: alpha blended by this much. 1 for full block colour.\r\n\r\nmitems_common.qc:class mitem_pic : mitem\r\n\tsimple image display.\r\n\titem_text: the default image to display.\r\n\titem_text_mactive: if specified, the image to display when the object has mouse focus.\r\n\titem_command: console command to execute when clicked.\r\n\r\nmitems_common.qc:class mitem_text : mitem\r\n\tsimple plain text\r\n\titem_text: the text to display.\r\n\titem_command: the console command to execute when clicked.\r\n\titem_scale: the height of the text, in virtual pixels, so ideally 8 or more.\r\n\r\nmitems_common.qc:class mitem_button : mitem\r\n\tcentered text with a noticable border that just seems to say 'click me'...\r\n\titem_text: the text to display.\r\n\titem_command: the console command to execute when clicked.\r\n\titem_scale: the height of the text, in virtual pixels, so ideally 8 or more.\r\n\r\n"
  },
  {
    "path": "specs/antilag.txt",
    "content": "FTE is able to perform serverside lag compensation\r\nIt is primarily controlled via the sv_antilag cvar, which shall be present in the serverinfo.\r\n\r\nIf the cvar is set to 0, then no anti-lag will be performed, even if the mod attempts it.\r\nIf the cvar is 1, then the MOVE_LAGGED and FL_LAGGEDMOVE bit flags are valid.\r\nIf the cvar is 2, then the server will act as 1, but ALL tracelines will have laggedmove set (but not traceboxes), and ALL movetype_flymissile or movetype_bounce entities will act as if they have FL_LAGGEDMOVE set. This value is to force anti-lag within a mod, without any mod changes. Using this value may potentially cause a mod to malfunction, as it affects all tracelines - this does depend upon the mod. Thus it is better/safer if mods are adapted to use the new flags instead of it being forced.\r\n\r\nA separate but related feature found in FTE servers is the sv_minping cvar, which will enforce a minimum specific ping, however this doesn't always ensure a fair ping.\r\n\r\n\r\n//antilag.qc:\r\n//new cvar, sv_antilag. values 0, 1, 2\r\nfloat FL_LAGGEDMOVE = 65536;\t/*if set in self.flags, the entity will collide against a lagged copy of the world. The lagged copy depends upon the player, which is either this entity, or the entity's owner, whichever is the first player.*/\r\nfloat MOVE_LAGGED = 64; /*if this bit is set within the 'nomonsters' argument of traceline or tracebox, the trace will collide against the lagged world. Uses the ent parameter as the lagged player. If its not a player then the owner is used instead.*/"
  },
  {
    "path": "specs/browser.txt",
    "content": "There are multiple ways to embed a program into a browser. The 'web'/emscripten port, and the activex port.\r\n\r\nQuick start with browser-servers:\r\n(this uses webrtc, which should give low latency but is incompatible with native servers at this time.)\r\nOn the server:\r\nsv_port_rtc /magic\t//this assumes the manifest provides a default broker. Then start a game (remember to set deathmatch or coop first).\r\nOn the client:\r\nconnect /magic\t\t//connects to the default broker and attempts to start a webrtc connection with whichever browser-server that registered itself with that name.\r\n\r\nQuick start with dedicated servers:\r\n(this uses websocket connections, which will have terrible latency on any network with notable packetloss, but can work on any correctly configured native fte server.)\r\nOn your server:\r\nsv_port_tcp 27500 //listens for tcp connections, including websocket clients\r\nIn the browser client:\r\nconnect ws://serverip:27500 // ws:// is assumed if ommitted. it is best to always include the port.\r\nAlternatively, if configured properly, just load http://serverip:27500 in your browser and be greated with the game!\r\n\r\n\r\n\r\nCvars:\r\nsv_port_tcp   -\tThis cvar controls which tcp port non-browser builds of fte should listen on.\r\n\t\t\tThis provides qizmo-tcp compat (including the tcpconnect in native clients).\r\n\t\t\tIt also provides websocket access for browser clients to connect to regular servers. It should allow quake connections from any site (as is supported for udp).\r\n\t\t\tAdditionally, you can use it to serve files, subject to allow_download_* cvars, the same as for vanilla quakeworld clients etc.\r\n\t\t\tFurther more, it also provides webrtc broker support. Browsers can use rtc://broker:port/NAME to connect games with potential clients (without them appearing on the server itself).\r\n\t\t\t\tBrokering should be fairly low traffic, but will result in an extra tcp connection for each rtc client or server active at the time.\r\nsv_port_rtc   -\tThis says the broker+resource used to register a webrtc server. Clients should connect using the same string, and by doing so will be able to relay webrtc connection info to the target browser-server.\r\ncfg_save\t   -\tThis command saves your config to your browser's local storage. In combination with seta, you can save most settings this way.\r\n\t\t\tBeware that browsers might still wipe it all eventually.\r\n\r\nHosting Quake:\r\nTo get fte running on a web page, you will need:\r\nftewebgl.html - An html file that embeds the javascript. You can probably modify fte's default if you want to integrate it better with your site, it doesn't change much.\r\nftewebgl.js - This is the meat of the engine. All in a single file. pre-gzip it if you can, to keep sizes down.\r\nftewebgl.js.mem - This is just the 'data', and annoyingly the 'bss' section too when emscripten is being buggy. Its version MUST match the js file.\r\nftewebgl.html.fmf - This is the manifest file that the browser port depends upon to figure out where all of its data files should come from. It will need to be modified for each mod.\r\n\t\t\tIdeally, servers will share fmf files between them, one per mod. This should avoid downloading the same packages from multiple different servers.\r\n*.pak / *.pk3 - These files are pulled in by the fmf. fte's web port doesn't include zlib, so pk3s should not use compression for individual files. Instead gzip them for solid archives with the browser doing all the decompression.\r\n\r\nArguments:\r\nYou can pass arguments by eg browsing to the following url:\r\nhttp://triptohell.info/moodles/web/ftewebgl.html?+connect%20ws://example.com:27500\r\nThe browser port is set up to ignore most args when linked to from another site. This blocks other sites from screwing with any saved user configs.\r\n\r\nBuilt-in http server:\r\nThe http server provided by sv_port_tcp will provide a page (either directly or with a redirect) to a version of the webgl client.\r\nThanks to allow_download_* cvars, only certain things may be downloaded.\r\nUnlike over game connections, the server will transparently also provide http content compression only where a .gz file has also been provided (and is more recent and in the same gamedir).\r\n\r\nAdditionally, there are also some files generated by the server.\r\nindex.html (and no resource) - attempts to provide a link to the webgl version of fte.\r\nall other files match what you would be able to download over udp.\r\n\r\n\r\n\r\nManifest files:\r\nThese are FTE's way of reconfiguring FTE for standalone mods. They offer basic rebranding features as well as content updates.\r\nThey contain a number of attributes, and frankly its easier to start with an example. Check http://triptohell.info/moodles/web/ for a few.\r\nNote that the content you pull in will still be subject to various copyrights. If nquake is anything to go by, you can get away with pak0, but don't bother serving up the full registered version of quake.\r\nYes, what you can legally run is limited.\r\n\r\nCustom maps do not need to be named in manifests as they will just be downloaded from the game server automatically anyway. They will be redownloaded if the page is refreshed, yes, but this can also be good for people still working on their maps...\r\n\r\n"
  },
  {
    "path": "specs/browserexample.html",
    "content": "\r\n<!-- embedding example \r\nNote the two separate objects. This is to cope with IE and Firefox incompatibilities.\r\n\r\ndataDownload specifies a zip file to download and extract pak and pk3 files. If the prefixed pak/pk3 file already exists, the download will be skipped (the contents are presumed to already be available). The zip must contain at least a \"[foo/]id1/pak0.pak\" file in this example, but you could also include a pak1.pak (ignoring copyright issues) if you wished.\r\n\r\ngame specifies which game you are trying to target. Explicitly supported values are: q1, q2, q3, h2. Unrecognised values load that mod directory _only_, which is useful for total conversions.\r\n-->\r\n\r\n<object name=\"axfte\"\r\n type=\"text/x-quaketvident\"\r\n classid=\"clsid:7d676c9f-fb84-40b6-b3ff-e10831557eeb\"\r\n width=100%\r\n height=100%\r\n>\r\n\t<param name=\"splash\" value=\"http://www.fteqw.com/images/phocagallery/fteqw/new_bloom_stuff/fte00076.jpg\">\r\n\t<param name=\"availver\" value=\"10000\">\r\n\t<param name=\"game\" value=\"q1\">\r\n\t<param name=\"dataDownload\" value='id1/pak0.pak:http://example.com/examplequake1demo.zip'>\r\n\r\n\t<object name=\"npfte\"\r\n\t type=\"text/x-quaketvident\"\r\n\t width=100%\r\n\t height=100%\r\n\t>\r\n\t\t<param name=\"splash\" value=\"http://www.fteqw.com/images/phocagallery/fteqw/new_bloom_stuff/fte00076.jpg\">\r\n\t\t<param name=\"availver\" value=\"10000\">\r\n\t\t<param name=\"game\" value=\"q1\">\r\n\t\t<param name=\"dataDownload\" value='id1/pak0.pak:http://example.com/examplequake1demo.zip'>\r\n\r\n\t\tThis text is displayed if the plugin isn't installed or is disabled.\r\n\r\n\t</object>\r\n</object>\r\n\r\n\r\n<!-- scripting example follows -->\r\n\r\n\r\n<script>\r\n\t/*cover the fact that firefox won't load a plugin if it has a classid, and IE won't load a plugin if it does not. Other browsers may follow other behaviours and \tmay load the npapi using the object intended for iefte, but the params are all the same.*/\r\n\tvar fte;\r\n\tif (axfte.plugver == null)\r\n\t\tfte = npfte;\r\n\telse\r\n\t\tfte = axfte;\r\n\r\n\t/*set the image for when the plugin isn't loaded (also used when it isn't maximized). The url is already specified above, so this is totally redundant, but this is \tan example after all.*/\r\n\tfte.splash = \"http://www.fteqw.com/images/phocagallery/fteqw/new_bloom_stuff/fte00076.jpg\";\r\n\r\n\t/*set it to some qtv stream*/\r\n\tfte.stream = \"file:foo@192.168.0.1\";\r\n\t/*set it to some server*/\r\n\tfte.server = \"192.168.0.1:27500\";\r\n\t/*explicitly activate it without requiring the user to click on it. only one plugin instance can be active at once. use with caution, or at least not on more than \tone plugin object on page site.*/\r\n//\tfte.running = 1;\r\n\tdocument.write(fte.plugver);\r\n</script>\r\n\r\n<br/>\r\n<a onclick=\"script:fte.running=1;\">run</a><br/>\r\n<a onclick=\"script:fte.width=640;fte.height=480;\">640*480</a><br/>\r\n<a onclick=\"script:fte.width=800;fte.height=600;\">800*600</a><br/>\r\n<a onclick=\"script:fte.width=1024;fte.height=768;\">1024*768</a><br/>\r\n"
  },
  {
    "path": "specs/bspx.txt",
    "content": "BSPX is a format originally invented by Tonik, I believe, and is already implemented in both ezQuake and FTE.\r\nIt is not so much a format itself, but more of an extensible way to shove additional lumps within an existing BSP.\r\nTypically these additional lumps will provide supplemental information or replace existing info.\r\nBSPX itself can logically be applied to the BSP file format of Quake, Quake II, or Quake III.\r\n\r\nBSPX itself can be defined in just the following two structures:\r\ntypedef struct {\r\n\tchar lumpname[24]; // up to 23 chars, zero-padded\r\n\tint fileofs;  // from file start\r\n\tint filelen;\r\n} bspx_lump_t;\r\ntypedef struct {\r\n\tchar id[4];  // 'BSPX'\r\n\tint numlumps;\r\n\tbspx_lump_t lumps[1];\r\n} bspx_header_t;\r\n\r\nThe bspx_header_t struct can be found immediately following the BSP's standard header. It is only valid if the standard header specifies no lump as starting at that location, and if the magic id matches.\r\nThe engine can then walk the lump list looking for lumps that it recognises. Unknown lumps MUST be ignored.\r\n\r\n\r\n\r\nThese lumps are currently defined:\r\n\r\nRGBLIGHTING:\r\n(applies to fte, ezquake, qss)\r\nThis is equivelent to the information stored in a .lit file (sans header), and must contain the same number of samples as the lightmap lump would normally contain, because it doesn't make much sense otherwise.\r\nPresence of this lump permits omitting the regular mono lightmap to save space, but doing so will harm compatibility.\r\n\r\nLIGHTING_E5BGR9:\r\n(applies to fte, parsed by qss only as a fallback)\r\nThis is a more advanced alternative to RGBLIGHTING.\r\nEach luxel is a E5BGR9 int32 packed value (ie: on little-endian machines, the exponent is the high 5 bits), resulting in what is effectively a memory-efficient floating point rgb value.\r\nThis lightmap format virtually removes all oversaturation limits, and promises greater precision in dark areas too.\r\nThis format is directly supported on ALL OpenGL[ES] 3.0+ gpus (aka: GL_EXT_texture_shared_exponent).\r\nAs a floating point format, a logical value of 1.0 is considered as identity (instead of 255 being an [overbright] multiplier of 2.0).\r\nLighting values are always assumed to be linear.\r\n\r\nLIGHTINGDIR:\r\n(applies to fte)\r\nThis lump contains averaged surface-space light directions (read: deluxemap), equivelent to fte's .lux file, or dp's .dlit files (sans header).\r\n(as unorm values, these need to be biased before use).\r\nIf bumpmaps or specular maps are not available then the effects of this may not be significant/noticable.\r\n\r\nLMSHIFT:\r\n(applies to fte, qss)\r\nThis is a series of per-surface bytes. Each byte provides the (1<<shift) ratio of texels-per-luxel of the corresponding surface.\r\nNote that the engine's per-surface lightmap size limit still applies, but any engine that supports LMSHIFT must support up to 256 luxels in either axis (note that 1 luxel is reserved for edges, so a shift of 0 requires that surfaces be subdivided to at least a 255qu boundary while a shift of 4 reduces the required subdivisions on an infinitely sized surface by a factor of 256, or to a max extent of 4080qu).\r\nAs a result, the subdivide size argument of qbsp can limit the minimum shift value (maximum lightmap scale).\r\n\r\nLMOFFSET:\r\n(applies to fte, qss)\r\nThis replaces the lightmap offset value of the surface lumps. Should only be used in conjunction with LMSHIFT.\r\nThis allows a bsp to contain both scaled surfaces and unscaled ones, without looking broken in engines that use either mode.\r\n\r\nLMSTYLE:\r\n(applies to fte, qss)\r\nThis replaces the four lightstyle indexes of each surface lump entry. Normally only makes sense when used in conjunction with LMSHIFT.\r\nObsoleted by DECOUPLED_LM.\r\n\r\nLMSTYLE16:\r\n(applies to fte, qss)\r\nAn upgrade for the LMSTYLE lump, potentially giving up to 65k lightstyles.\r\nAdditionally, the number of styles per face is variable, and must be inferred by the lump size vs surface count.\r\nBoth of these tweeks make it useful even without LMSHIFT.\r\n\r\nVERTEXNORMALS:\r\n(applies to fte)\r\nThis lump contains a series of vec3_t values. One per vertex lump entry.\r\nSurfaces with a texinfo flag of 0x800 will use this lump in order to determine vertex normals, otherwise they will use their plane's normal.\r\nThese normals allow rtlights to respond to curved forms, while the flag prevents the need for excessive vertex+edge counts.\r\nVerticies that are not part of any 0x800 surface will not be read, and will usually hold values of 0 (if only because it compresses better).\r\n\r\nBRUSHLIST:\r\n(applies to fte)\r\nEngines that utilise this lump will no longer need to use hull-based collisions.\r\nstruct {\r\n\tunsigned int ver = 1;\r\n\tunsigned int modelnum;\t\t//inline model number. 0 for world, obviously.\r\n\tunsigned int numbrushes;\t//size of following array.\r\n\tunsigned int numplanes;\t\t//total count. for validation.\r\n\tstruct\r\n\t{\r\n\t\tvec_t mins;\r\n\t\tvec_t maxs;\r\n\t\tsigned short contents;\r\n\t\tunsigned short numplanes;\r\n\t\t{\r\n\t\t\tvec3_t \tnormal;\r\n\t\t\tfloat \tdist;\r\n\t\t} planes[numplanes];\r\n\t} brush[numbrushes];\r\n} permodel[];\r\nA submodel could be an illusionary model with no solid surfaces. Such a model will become non-solid if there is a BRUSHLIST lump that does not name the submodel.\r\nAxial planes MUST NOT be written - they will be inferred from the brush's mins+maxs. This guarentees that brush expansion does not extend points beyond the brush itself, and saves size on maps made by lazy people.\r\nContents values are equal to Quake's normal CONTENT_FOO values. This outbreak of lack of imagination is brought to you by a desire to avoid politics about content masks.\r\nCONTENTS_EMPTY\t= -1\t//an error. pointless.\r\nCONTENTS_SOLID\t= -2\t//regular solidity\r\nCONTENTS_WATER\t= -3\t//required for pointcontents to work properly.\r\nCONTENTS_SLIME\t= -4\t//really I'm only listing these for completeness. only the last two are 'new'.\r\nCONTENTS_LAVA\t= -5\t//it burns!\r\nCONTENTS_SKY\t= -6\t//nothing explodes here!\r\nCONTENTS_CLIP\t= -8\t//borrowed from halflife, blocks only entities with size.\r\nCONTENTS_LADDER\t= -16\t//borrowed from halflife\r\nPresence of this lump permits hulls 1 and 2 to be entirely omitted from the BSP (0 is still used for rendering), but doing so will harm compatibility with engines that do not understand this (presumably a solid-but-valid tree should be written, just in case). This could boost load times.\r\nThe engine is expected to insert the brushes into the bsp's various nodes. Inserting them into the leafs is suboptimal as leaf 0 will end up containing every brush...\r\n\r\nENVMAP:\r\n(applies to fte)\r\nGenerated by fte's map_findcubemaps by parsing the entities lump for env_cubemap entities.\r\nstruct\r\n{\r\n\tvec3_t origin;\r\n\tint cubesize;\r\n} envmaps[];\r\nThis defines the various envmaps in the bsp, for mdls to use.\r\nenvmaps will be loaded from textures/env/MAPNAME_X_Y_Z using the engine's regular cubemap rules (xyz are rounded to ints).\r\n\r\nSURFENVMAP:\r\n(applies to fte)\r\nGenerated by fte's map_findcubemaps by parsing the entities lump for env_cubemap entities.\r\nstruct\r\n{\r\n\tunsigned int envmapindex;\r\n} persurface[];\r\nThis provides each surface in the bsp with info about its most appropriate(nearest) envmap. if the index is ~0u, then no envmap is specified.\r\n\r\nDECOUPLED_LM:\r\nstruct\r\n{\r\n\tuint16_t lmsize[2];\t\t//made explicit. beware MAX_\r\n\tuint32_t lmoffset;\t\t//replacement offset for vanilla compat.\r\n\tvec4_t lmvecs[2]; \t\t//lmcoord[] = dotproduct3(vertexcoord, lmvecs[])+lmvecs[][3]\r\n} persurface[];\r\nReplaces LMSHIFT and LMOFFSET, but _NOT_ LMSTYLE(16). Allows the lightmap to be oriented+scaled entirely independantly, reducing seams between surfaces.\r\n\r\nLIGHTGRID_OCTREE:\r\nvec3_t  step;\r\nivec3_t size;\r\nvec3_t  mins;\r\nbyte    numstyles;\t//WARNING: misaligns the rest of the data\r\nuint32_t rootnode;\r\nuint32_t numnodes;\r\nstruct\r\n{\r\n\tuint32_t mid[3];\r\n\tuint32_t child[8];\t//((z>=mid[2])<<0) | ((y>=mid[1])<<1) | ((x>=mid[0])<<2)\r\n\t\t#define LGNODE_LEAF (1u<<31)\r\n\t\t#define LGNODE_MISSING (1u<<30)\r\n} lgnode[numnodes];\r\nuint32_t numleafs;\r\nblob\r\n{\r\n\tivec3_t mins;\r\n\tivec3_t size;\r\n\tblob\r\n\t{\r\n\t\tbyte stylecount;\t//compression...\r\n\t\tstruct\r\n\t\t{\r\n\t\t\tbyte stylenum; \t//which lightstyle to scale the rgb values by\r\n\t\t\tbyte rgb[3];\t//the actual sample\r\n\t\t} [stylecount];\r\n\t} perpoint[size[0]*size[1]*size[2]];\r\n} lgleaf[numleafs];\r\nThe nodes exists to provide support for sparse data, allowing solid unplayable areas of the map to be omitted entirely. Convert the sample position you want into an index, walk the tree until either missing or leaf. If leaf then index and add up the weighted rgb values. If missing or stylecount is 0xff then there's no data at that point. You should sample the 8 points around your target position and weight+blend them - with the missing values compensated for by increasing the weight of the other samples so that an object next to a wall does not become black/fullbright/ugly.\r\nMisalignment and leaf compression means it must be repacked at load time for efficient access (which is likely to mean stylecount gets clamped to 4). Does not support HDR nor 16bit styles.\r\n\r\nOther features:\r\nFTE supports mipmaps with all four offsets set to 0 as external textures. Such texture will ignore gl_load24bit, treating it as always enabled. This saves space and simplifies copyright ownership without resulting in untextured maps (when the textures are actually available, anyway).\r\n\r\nMisc Compatibility Concerns:\r\nmipmap lump - mipmaps with all four offsets set to 0 are external textures and contain no actual data. Note that vanilla quake will glitch out if given such a texture, although its unlikely to crash.\r\nhulls - presence of BRUSHLIST allows hulls 1 and 2 to be ommitted. This will crash vanilla glquake but not winquake, and can be reproduced with the vanilla tools too.\r\nlightmap sizes - winquake is limited to 18*18 lightmaps. Many quake engines support up to 256*256 now (AFTER lmscale). Note that such large lightmaps are not recommended due to all the resulting texture switches.\r\n"
  },
  {
    "path": "specs/changelevels.md",
    "content": "\nThe FTEQW engine offers many improvements in changelevel functionality over the\noriginal Quake engine from 1996. In the original engine, your only way to send\nplayer data across a level transition was through the `parm` globals in the\nQuakeC server code, which meant you had exactly 16 floating-point numbers to\nuse to store any data the players needed to remember across a level change.\nEverything else about the player's state was dropped. This worked well enough\nfor Quake, but is severely limiting if you need to send anything that couldn't\nfit into a `float`.\n\nNot only does FTEQW offer 64 `parm` globals instead of 16 (for DarkPlaces\ncompatibility), it also has a `parm_string` global which can store a\ntheoretically unlimited amount of string or buffer data. In this document I'll\nshow you how I decided to use it for my projects.\n\nWhen the player spawns in a level for the first time, the function\n`SetNewParms()` is called in the SSQC progs. This is where you set the default\n`parm` values for every player that spawns in a level. For example, let's say\nthat `parm1` is going to store the player's health value across a level change.\nYou would set the default health value like so:\n\n```c\nvoid() SetNewParms =\n{\n\tparm1 = 100;\n};\n```\n\nWhen the engine is sending the players through a level transition, it calls the\nfunction `SetChangeParms()` for each player entity in sequence. For storing the\nplayer's health, you would do it like so:\n\n```c\nvoid() SetChangeParms =\n{\n\tparm1 = self.health;\n};\n```\n\nBut, as stated before, this can get rather limiting if the player has data that\ndoesn't fit into a floating point number, such as a dynamic inventory of items\nand weapons or an stats tree or something. This is where `parm_string` comes\nin. If you're using a modern version of FTEQW and the FTEQCC compiler, you also\nhave access to several JSON parsing builtins. We will be using these to make\nthe usage of `parm_string` easier.\n\nIf we wanted to store more complex data in `parm_string`, the default value for\nit should be a valid empty JSON node, like so:\n\n```c\nvoid() SetNewParms =\n{\n\tparm1 = 100;\n\tparm_string = \"{}\";\n};\n```\n\nAnd when `SetChangeParms()` is called, you will write your extended data as\nvalid JSON entries in `parm_string`, like so:\n\n```c\nvoid() SetChangeParms =\n{\n\tparm1 = self.health;\n\tparm_string = strcat(\n\t\t\"{\\\"some_extended_field_string\\\":\\\"\",\n\t\tself.some_extended_field_string,\n\t\t\"\\\"}\"\n\t);\n};\n```\n\nNote we are using the `strcat()` builtin to construct our JSON string rather\nthan `sprintf()`, because `sprintf()` seems to have some difficulties with very\nlong strings.\n\nThe above code will store a per-player string value called\n`some_extended_field_string` into `parm_string` for storage across the level\ntransition. Ideally you'd wanna make some helper functions to easily write and\nformat your JSON, but this is just a basic example that constructs it directly.\n\nNow the interesting part comes when it's time to read the data back. You'd want\nto do this in the function `PutClientInServer()` after setting up your\nfundamental player class, like so:\n\n```c\nvoid() PutClientInServer =\n{\n\t// setup fundamental player state\n\tself.takedamage = DAMAGE_YES;\n\tself.solid = SOLID_SLIDEBOX;\n\tself.movetype = MOVETYPE_WALK;\n\tself.fixangle = TRUE;\n\tsetsize(self, [-16, -16, -36], [16, 16, 36]);\n\tself.flags |= FL_CLIENT;\n\tself.view_ofs = [0, 0, 28];\n\tself.classname = \"player\";\n\n\t// load health value from parm1\n\tself.health = parm1;\n\n\t// parse parm_string as json\n\tjsonnode root = json_parse(parm_string);\n\tif (!root)\n\t\terror(\"couldn't decode parm_string as JSON!\");\n\n\t// check if we have the field\n\t// if so, copy it out as a string\n\tif (root[\"some_extended_field_string\"])\n\t\tself.some_extended_field_string = root[\"some_extended_field_string\"].s;\n\n\t// clean up\n\tjson_free(root);\n};\n```\n\nYou don't have to use `parm_string` as JSON, though. You could just print the\nvalues as space-separated tokens and use the `tokenize()` or\n`tokenize_console()` builtins to read them back. Either way, it's a good method\nfor storing lots of custom data across level transitions. Enjoy!\n"
  },
  {
    "path": "specs/chunkeddownloads.txt",
    "content": "Establishment:\r\n\tUseful defines:\r\n\t\t#define PROTOCOL_VERSION_FTE\t\t(('F'<<0) + ('T'<<8) + ('E'<<16) + ('X' << 24))\r\n\t\t#define PEXT_CHUNKEDDOWNLOADS\t\t0x20000000\r\n\r\n\tQW: Extensions are established during the handshake.\r\n\t\tCL: \"\\xff\\xff\\xff\\xffgetchallenge\\n\"\r\n\t\tSV: \"\\xff\\xff\\xff\\xffc$challenge\\0<binary key value pairs>\"\r\n\t\tCL: \"\\xff\\xff\\xff\\xffconnect $ver $qport $challenge \\\"$userinfo\\\"\\n%#x %#x\\n\", key1, value1\r\n\t\tSV: \"\\xff\\xff\\xff\\xffj\\n\"\r\n\t\tSee the 'both' part for additional info.\r\n\t\tThis handshake will be stripped by proxies. This is by design. It prevents qizmo and similar proxies from erroring on extensions.\r\n\t\t\r\n\tNQ: This extension is not currently supported.\r\n\t\tit can be implemented, but darkplaces' stream downloads may be more standard for the nq protocol.\r\n\r\n\tBoth:\r\n\t\tThe server will update the svc_serverinfo message to have this ordering:\r\n\t\tbyte svcqw_serverdata / svcnq_serverinfo \r\n\t\toptional long PROTOCOL_VERSION_FTE\r\n\t\toptional long flags=PEXT_CHUNKEDDOWNLOADS|etc\r\n\t\tlong PROTOCOL_VERSION\r\n\t\t...\r\n\t\tThe protocol version should be parsed in a loop, stopping on unrecognised or non-extension-bits protocols.\r\n\t\tThe server must mask the extension bits with those that it supports, to avoid future extensions appearing active on servers that don't support them.\r\n\t\tIf the extension bits are not echoed from the server in the list, those extensions WILL NOT be used.\r\n\t\tThis mechanism allows the server to mask the client's extensions with the extensionbits that it supports, and to tell the client which protocol bits will actually be used for each map.\r\n\t\tThe server may kick the client if the client does not support extensions required by the server, just as a server sending an unsupported protocol version would result in a client getting kicked, but at least the server has the chance to run without.\r\n\r\nModified SVCs:\r\n\tsvc_download: completely rewritten, but uses the old svc value.\r\n\t\tTakes three forms.\r\n\t\tStartup/failure:\r\n\t\t\tSent reliably.\r\n\r\n\t\t\tunsigned long chunknum < 0\r\n\t\t\tlong totalsize\r\n\t\t\tstring nameonserver\r\n\r\n\t\t\ttotalsize < 0 denotes an error.\r\n\t\t\t-1 = file not found\r\n\t\t\t-2 = server rejected download based upon name. file may or may not exist.\r\n\t\t\t-3 = generic error, active download aborted.\r\n\r\n\t\t\tnameonserver should match the filename the client requested. It is present to potentially allow replacement extensions, but any such replacements are subject to other extensions.\r\n\t\t\tThe client must ensure that the server does not have a way to write out files such as *.dll or *.so\r\n\r\n\t\tData:\r\n\t\t\tSent unreliably.\r\n\r\n\t\t\tunsigned long chunknum\r\n\t\t\tbyte data[1024]\r\n\r\n\t\t\tthe start offset of the data is chunknum*1024\r\n\t\t\twith the final chunk, the data sent is padded to 1024 bytes. the client must write only as many bytes as are in the file.\r\n\r\n\t\tOOB Data:\r\n\t\t\tSent unreliably in its own udp packet.\r\n\r\n\t\t\tstring\"\\xff\\xff\\xff\\xffn\\\\chunk\"\r\n\t\t\tlong filenum\r\n\t\t\tbyte svc_download\r\n\t\t\tunsigned long chunknum\r\n\t\t\tbyte data[1024]\r\n\r\n\t\t\tMuch like the in-band data chunk.\r\n\t\t\tRemember to ensure that it came from the right source address.\r\n\t\t\tThe filenum value is a copy of the filenum argument of the nextdl client command.\r\n\t\t\t\r\n\r\nModified client stringcmds:\r\n\tdownload \"$filename\"\r\n\t\tBegins download.\r\n\t\tTriggers a svc_download Startup or failure command.\r\n\t\totherwise unmodified.\r\n\r\n\tnextdl $chunknum $percent $filenum\r\n\t\tonly the first argument is required.\r\n\t\tthe percentage is for display on the server only, and does not affect anything. If it is set to '-', servers should guess based upon the chunk number.\r\n\t\tthe third argument permits out-of-band file downloads, and is a cookie. increment the value each time a new file starts, and ignore oob chunks that do not match.\r\n\r\n\t\tThe client must periodically generate new chunk requests, sending it unreliably. If the third argument is present, multiple nextdl commands can be sent in a single client packet.\r\n\t\tThe server typically sends out a new svc_download data message upon receipt of each nextdl, either in band or out of band. The server must take care to rate limit oob messages.\r\n\t\tThe client will need to track which chunks have been received, which ones have not, and avoid requesting the same chunk too many times in rapid succession.\r\n\t\tThe client should take its requested netchan rate and its packetloss into consideration in order to avoid spamming itself off the internet.\r\n\t\tYou may wish to implement a slow-start algorithm or some such, and cut back if the packetloss rises.\r\n\t\tThe actual algorithm used to decide which chunk to request and when is up to the client implementor, but should generally be roughly sequential to avoid unnecessary cache thrashing on the server.\r\n\t\tWhile unadvised, it is not an error to start at the last chunk and work backwards, though its generally not recommended to do so, and such implementations really should be explicit with the percentage hints.\r\n\t\tThe server should report an error if there is no active download or if the chunk is invalid (however, note ezquake incompatibilities).\r\n\r\n\tstopdownload\r\n\t\tSent by the client whenever a download failed or finished. This allows the server to clean up resources.\r\n\t\tThe server will only allow one active download at a time, so this is not strictly required.\r\n\t\tThe server must not reply (except maybe with a print if there is no download currently active).\r\n\r\nEZQuake's Incompatiblities:\r\n\tezquake clients do not send stopdownload. Instead it sends 'nextdl -1'. FTE servers print a warning and closes the download upon reception. Older versions reported error -3 due to this, but as ezquake cannot handle error conditions, servers that care about compatibility over correctness should not report error conditions if the chunk index is ~0.\r\n\tmvdsv spams like fuck when it receives the stopdownload command.\r\n\tmvdsv does not always report filenames in svc_download messages. Such messages can safely be ignored.\r\n\tout of band downloads were not supported in the earliest versions, but do not present any compatibility issues.\r\n\r\nCompeting extensions:\r\n\tdarkplaces streamed downloads. more common with dp's own protocol.\r\n\tquakeforge redirects. chunked downloads redefine the svc_download message, meaning quakeforge's redirects are incompatible."
  },
  {
    "path": "specs/compile farm scripts/linux/.bitchxrc",
    "content": "^on -join \"% #testdisabled *\" {\n/exec -msg #fte /home/moodles/svninfo.sh\n/exec -msg #fte /home/moodles/count.sh\ntimer 60 //^part #fte Until the next SVN commit..\ntimer 120 //^quit\n}\non connect \"*quakenet.org*\" {\n/msg Q@CServe.quakenet.org AUTH AGorilla passwordhere\n}\non ^channel_synch \"#fte *\" \n{ \nexec -msg $0 /home/moodles/svninfo.sh\ntimer 1 //exec -msg $0 /home/moodles/count.sh\ntimer 60 //^part $0 Until the next SVN commit..\ntimer 120 //^quit\n}\n"
  },
  {
    "path": "specs/compile farm scripts/linux/build.sh",
    "content": "#!/bin/bash\r\nSTART=$(date +%s)\r\nHOME=/home/moodles\r\nWEBFOLDER=/htdocs\r\nLOGFOLDER=/htdocs/build_logs\r\nBUILDFOLDER=$HOME$WEBFOLDER\r\nBUILDLOGFOLDER=$HOME$LOGFOLDER\r\nSVNFOLDER=$HOME/fteqw/engine/release\r\nTHREADS=\"-j 4\"\r\nPATH=$PATH:/opt/mac/bin:/usr/local/bin:/opt/llvm/bin:/opt/intel/Compiler/11.0/083/bin/ia32/:/opt/morphos-dev/bin\r\nTIMETAKENTF=/tmp/timetaken.txt\r\nif [ -f $TIMETAKENTF ];\r\nthen\r\n\trm $TIMETAKENTF\r\nfi\r\n\r\ncd $BUILDLOGFOLDER/\r\necho \"Deleting old build logs\"\r\nrm *.txt\r\n\r\ncd $BUILDFOLDER/\r\necho \"Deleteing old binaries\"\r\nrm * >> /dev/null 2>> /dev/null\r\nrm -rf ./win32/\r\nrm -rf ./linux_32bit/\r\nrm -rf ./linux_64bit/\r\nrm -rf ./morphos/\r\nrm -rf ./macosx_tiger_10.4/\r\n\r\necho \"Making folders\"\r\nmkdir win32\r\nmkdir linux_32bit\r\nmkdir linux_64bit\r\nmkdir morphos\r\nmkdir macosx_tiger_10.4\r\n\r\ncd /home/moodles/fteqw/engine/\r\n\r\necho \"SVN Update\"\r\nsvn update\r\n\r\nmake clean >> /dev/null\r\necho \"Making Linux 32bit (llvm)\"\r\nmake $THREADS FTE_TARGET=linux32 CC=\"llvm-gcc -m32\" >> $BUILDLOGFOLDER/linux32_llvm.txt 2>> $BUILDLOGFOLDER/linux32_llvm.txt\r\ncd $SVNFOLDER\r\nls -d *fteqw* | sed 's/\\(.*\\)32$/mv \"&\" \"\\132.llvm\"/' | sh\r\nmv *fteqw* $BUILDFOLDER/linux_32bit/\r\ncd ..\r\necho \"Nuking llvm object files\"\r\nmake clean >> /dev/null\r\necho \"Making Linux 32bit (clang)\"\r\nmake $THREADS FTE_TARGET=linux32 CC=\"clang -DCLANG\" >> $BUILDLOGFOLDER/linux32_clang.txt 2>> $BUILDLOGFOLDER/linux32_clang.txt\r\ncd $SVNFOLDER\r\nls -d *fteqw* | sed 's/\\(.*\\)32$/mv \"&\" \"\\132.clang\"/' | sh\r\nmv *fteqw* $BUILDFOLDER/linux_32bit/\r\ncd ..\r\necho \"Nuking clang object files\"\r\nmake clean >> /dev/null\r\necho \"Making Linux 32bit (icc)\"\r\nmake $THREADS FTE_TARGET=linux32 CC=\"icc\" >> $BUILDLOGFOLDER/linux32_icc.txt 2>> $BUILDLOGFOLDER/linux32_icc.txt\r\ncd $SVNFOLDER\r\nls -d *fteqw* | sed 's/\\(.*\\)32$/mv \"&\" \"\\132.icc\"/' | sh\r\nmv *fteqw* $BUILDFOLDER/linux_32bit/\r\ncd ..\r\necho \"Nuking icc object files\"\r\nmake clean >> /dev/null\r\necho \"Making Linux 32bit SDL (llvm)\"\r\nmake $THREADS FTE_TARGET=sdl BITS=32 CC=\"llvm-gcc -m32\" >> $BUILDLOGFOLDER/linux32_SDL_llvm.txt 2>> $BUILDLOGFOLDER/linux32_SDL_llvm.txt\r\ncd $SVNFOLDER\r\nls -d *fteqw* | sed 's/\\(.*\\)32$/mv \"&\" \"\\132.llvm\"/' | sh\r\nmv *fteqw* $BUILDFOLDER/linux_32bit/\r\ncd ..\r\necho \"Nuking 32bit SDL llvm object files\"\r\nmake clean >> /dev/null\r\necho \"Making Linux 32bit SDL (clang)\"\r\nmake $THREADS FTE_TARGET=sdl BITS=32 CC=\"clang -m32 -DCLANG\" >> $BUILDLOGFOLDER/linux32_SDL_clang.txt 2>> $BUILDLOGFOLDER/linux32_SDL_clang.txt\r\ncd $SVNFOLDER\r\nls -d *fteqw* | sed 's/\\(.*\\)32$/mv \"&\" \"\\132.clang\"/' | sh\r\nmv *fteqw* $BUILDFOLDER/linux_32bit/\r\ncd ..\r\necho \"Nuking 32bit SDL clang object files\"\r\nmake clean >> /dev/null\r\necho \"Making Linux 32bit SDL (icc)\"\r\nmake $THREADS FTE_TARGET=sdl BITS=32 CC=\"icc -m32\" >> $BUILDLOGFOLDER/linux32_SDL_icc.txt 2>> $BUILDLOGFOLDER/linux32_SDL_icc.txt\r\ncd $SVNFOLDER\r\nls -d *fteqw* | sed 's/\\(.*\\)32$/mv \"&\" \"\\132.icc\"/' | sh\r\nmv *fteqw* $BUILDFOLDER/linux_32bit/\r\ncd ..\r\necho \"Nuking 32bit SDL icc object files\"\r\nmake clean >> /dev/null\r\necho \"Making Linux 32bit (gcc)\"\r\nmake $THREADS FTE_TARGET=linux32 >> $BUILDLOGFOLDER/linux32.txt 2>> $BUILDLOGFOLDER/linux32.txt\r\ncp $SVNFOLDER/* $BUILDFOLDER/linux_32bit/ >> /dev/null 2>> /dev/null\r\nmake clean >> /dev/null\r\necho \"Making Windows\\n\"\r\nmake $THREADS FTE_TARGET=win32 sv-rel gl-rel mingl-rel >> $BUILDLOGFOLDER/win32.txt 2>> $BUILDLOGFOLDER/win32.txt\r\ncp $SVNFOLDER/* $BUILDFOLDER/win32/ >> /dev/null 2>> /dev/null\r\nmake clean >> /dev/null\r\necho \"Making MorphOS\\n\"\r\nmake $THREADS FTE_TARGET=morphos gl-rel mingl-rel  >> $BUILDLOGFOLDER/morphos.txt 2>> $BUILDLOGFOLDER/morphos.txt\r\ncp $SVNFOLDER/* $BUILDFOLDER/morphos/ >> /dev/null 2>> /dev/null\r\nchmod ugo+x $BUILDFOLDER/morphos/*\r\nmake clean >> /dev/null\r\necho \"Making MacOSX\"\r\nmake $THREADS FTE_TARGET=macosx sv-rel gl-rel mingl-rel CFLAGS=\"-I/home/moodles/mac/include/ -L/home/moodles/mac/lib\"  >> $BUILDLOGFOLDER/osx_ppc.txt 2>> $BUILDLOGFOLDER/osx_ppc.txt\r\nmake $THREADS FTE_TARGET=macosx_x86 sv-rel gl-rel mingl-rel CFLAGS=\"-I/home/moodles/mac/x86/include/ -L/home/moodles/mac/x86/lib\"  >> $BUILDLOGFOLDER/osx_86.txt 2>> $BUILDLOGFOLDER/osx_86.txt\r\ncp $SVNFOLDER/* $BUILDFOLDER/macosx_tiger_10.4/ >> /dev/null 2>> /dev/null\r\nmake clean >> /dev/null\r\necho \"Making Windows SDL\"\r\nmake $THREADS FTE_TARGET=win32_SDL CFLAGS=\"-D_SDL\" >> $BUILDLOGFOLDER/win32_SDL.txt 2>> $BUILDLOGFOLDER/win32_SDL.txt\r\ncp $SVNFOLDER/* $BUILDFOLDER/win32/ >> /dev/null 2>> /dev/null\r\nmake clean >> /dev/null\r\necho \"Making Linux 32bit SDL\"\r\nmake $THREADS FTE_TARGET=SDL BITS=32 >> $BUILDLOGFOLDER/linux32_SDL.txt 2>> $BUILDLOGFOLDER/linux32_SDL.txt\r\ncp $SVNFOLDER/* $BUILDFOLDER/linux_32bit/ >> /dev/null 2>> /dev/null\r\nmake clean >> /dev/null\r\necho \"Making Linux 64bit\"\r\nmake $THREADS FTE_TARGET=linux64 LDFLAGS=\"-L./libs/64/ -I./libs/64/ -lz -lX11-xcb -lxcb-xlib -lxcb -lXdmcp -lXpm -lXau -lX11 -lXext\" >> $BUILDLOGFOLDER/linux64.txt 2>> $BUILDLOGFOLDER/linux64.txt\r\ncp $SVNFOLDER/* $BUILDFOLDER/linux_64bit/ >> /dev/null 2>> /dev/null\r\nmake clean >> /dev/null\r\necho \"Making Linux 64bit (clang)\"\r\nmake $THREADS FTE_TARGET=linux64 CC=\"clang -m64 -DCLANG\" LDFLAGS=\"-L./libs/64/ -I./libs/64/ -lz -lX11-xcb -lxcb-xlib -lxcb -lXdmcp -lXpm -lXau -lX11 -lXext\" >> $BUILDLOGFOLDER/linux64_clang.txt 2>> $BUILDLOGFOLDER/linux64_clang.txt\r\ncd $SVNFOLDER\r\nls -d *fteqw* | sed 's/\\(.*\\)64$/mv \"&\" \"\\164.clang\"/' | sh\r\nmv *fteqw* $BUILDFOLDER/linux_64bit/\r\ncd ..\r\nmake clean >> /dev/null\r\necho \"Making Linux 64bit SDL (very ambitious)\"\r\nmake $THREADS FTE_TARGET=SDL gl-rel CC=\"gcc -m64\" LDFLAGS=\"-L./libs/64/ -I./libs/64/ -lz -lX11-xcb -lxcb-xlib -lxcb -lXdmcp -lXpm -lXau -lX11 -lXext\" >> $BUILDLOGFOLDER/linux64_SDL.txt 2>> $BUILDLOGFOLDER/linux64_SDL.txt\r\n\r\ncd /home/moodles/fteqw/engine\r\nsvn info >> $BUILDFOLDER/version.txt\r\necho \"All done\"\r\n\r\nEND=$(date +%s)\r\nDIFF=$(( $END - $START ))\r\nMINS=$(( $DIFF / 60 ))\r\necho \"(\u0002Total Compile Time\u0002: $MINS minutes)\" > $TIMETAKENTF\r\necho \"\u0002Total Compile Time\u0002: $MINS minutes\"\r\n\r\ncd /home/moodles\r\nrm .bitchxrc\r\ncp ./fteqw/.bitchxrc ./\r\n./BitchX -a gameservers.nj.us.quakenet.org -A -c \"#fte\" -n A_Gorilla\r\n"
  },
  {
    "path": "specs/compile farm scripts/linux/build_ccache.sh",
    "content": "#!/bin/bash\r\nSTART=$(date +%s)\r\nHOME=/home/moodles\r\nWEBFOLDER=/htdocs\r\nLOGFOLDER=/htdocs/build_logs\r\nBUILDFOLDER=$HOME$WEBFOLDER\r\nBUILDLOGFOLDER=$HOME$LOGFOLDER\r\nSVNFOLDER=$HOME/fteqw/engine/release\r\nTHREADS=\"-j 4\"\r\nPATH=$PATH:/opt/mac/bin:/usr/local/bin:/opt/llvm/bin:/opt/intel/Compiler/11.0/083/bin/ia32/:/opt/morphos-dev/bin\r\nTIMETAKENTF=/tmp/timetaken.txt\r\nif [ -f $TIMETAKENTF ];\r\nthen\r\n\trm $TIMETAKENTF\r\nfi\r\n\r\ncd $BUILDLOGFOLDER/\r\necho \"Deleting old build logs\"\r\nrm *.txt\r\n\r\ncd $BUILDFOLDER/\r\necho \"Deleteing old binaries\"\r\nrm * >> /dev/null 2>> /dev/null\r\nrm -rf ./win32/\r\nrm -rf ./linux_32bit/\r\nrm -rf ./linux_64bit/\r\nrm -rf ./morphos/\r\nrm -rf ./macosx_tiger_10.4/\r\n\r\necho \"Making folders\"\r\nmkdir win32\r\nmkdir linux_32bit\r\nmkdir linux_64bit\r\nmkdir morphos\r\nmkdir macosx_tiger_10.4\r\n\r\ncd /home/moodles/fteqw/engine/\r\n\r\necho \"SVN Update\"\r\nsvn update\r\n\r\nmake clean >> /dev/null\r\necho \"Making Linux 32bit (llvm)\"\r\nmake $THREADS FTE_TARGET=linux32 CC=\"ccache llvm-gcc -m32\" >> $BUILDLOGFOLDER/linux32_llvm.txt 2>> $BUILDLOGFOLDER/linux32_llvm.txt\r\ncd $SVNFOLDER\r\nls -d *fteqw* | sed 's/\\(.*\\)32$/mv \"&\" \"\\132.llvm\"/' | sh\r\nmv *fteqw* $BUILDFOLDER/linux_32bit/\r\ncd ..\r\necho \"Nuking llvm object files\"\r\nmake clean >> /dev/null\r\necho \"Making Linux 32bit (clang)\"\r\nmake $THREADS FTE_TARGET=linux32 CC=\"ccache clang -DCLANG\" >> $BUILDLOGFOLDER/linux32_clang.txt 2>> $BUILDLOGFOLDER/linux32_clang.txt\r\ncd $SVNFOLDER\r\nls -d *fteqw* | sed 's/\\(.*\\)32$/mv \"&\" \"\\132.clang\"/' | sh\r\nmv *fteqw* $BUILDFOLDER/linux_32bit/\r\ncd ..\r\necho \"Nuking clang object files\"\r\nmake clean >> /dev/null\r\necho \"Making Linux 32bit (icc)\"\r\nmake $THREADS FTE_TARGET=linux32 CC=\"ccache icc\" >> $BUILDLOGFOLDER/linux32_icc.txt 2>> $BUILDLOGFOLDER/linux32_icc.txt\r\ncd $SVNFOLDER\r\nls -d *fteqw* | sed 's/\\(.*\\)32$/mv \"&\" \"\\132.icc\"/' | sh\r\nmv *fteqw* $BUILDFOLDER/linux_32bit/\r\ncd ..\r\necho \"Nuking icc object files\"\r\nmake clean >> /dev/null\r\necho \"Making Linux 32bit SDL (llvm)\"\r\nmake $THREADS FTE_TARGET=sdl BITS=32 CC=\"ccache llvm-gcc -m32\" >> $BUILDLOGFOLDER/linux32_SDL_llvm.txt 2>> $BUILDLOGFOLDER/linux32_SDL_llvm.txt\r\ncd $SVNFOLDER\r\nls -d *fteqw* | sed 's/\\(.*\\)32$/mv \"&\" \"\\132.llvm\"/' | sh\r\nmv *fteqw* $BUILDFOLDER/linux_32bit/\r\ncd ..\r\necho \"Nuking 32bit SDL llvm object files\"\r\nmake clean >> /dev/null\r\necho \"Making Linux 32bit SDL (clang)\"\r\nmake $THREADS FTE_TARGET=sdl BITS=32 CC=\"ccache clang -m32 -DCLANG\" >> $BUILDLOGFOLDER/linux32_SDL_clang.txt 2>> $BUILDLOGFOLDER/linux32_SDL_clang.txt\r\ncd $SVNFOLDER\r\nls -d *fteqw* | sed 's/\\(.*\\)32$/mv \"&\" \"\\132.clang\"/' | sh\r\nmv *fteqw* $BUILDFOLDER/linux_32bit/\r\ncd ..\r\necho \"Nuking 32bit SDL clang object files\"\r\nmake clean >> /dev/null\r\necho \"Making Linux 32bit SDL (icc)\"\r\nmake $THREADS FTE_TARGET=sdl BITS=32 CC=\"ccache icc -m32\" >> $BUILDLOGFOLDER/linux32_SDL_icc.txt 2>> $BUILDLOGFOLDER/linux32_SDL_icc.txt\r\ncd $SVNFOLDER\r\nls -d *fteqw* | sed 's/\\(.*\\)32$/mv \"&\" \"\\132.icc\"/' | sh\r\nmv *fteqw* $BUILDFOLDER/linux_32bit/\r\ncd ..\r\necho \"Nuking 32bit SDL icc object files\"\r\nmake clean >> /dev/null\r\necho \"Making Linux 32bit (gcc)\"\r\nmake $THREADS FTE_TARGET=linux32 CC=\"ccache gcc -m32\" >> $BUILDLOGFOLDER/linux32.txt 2>> $BUILDLOGFOLDER/linux32.txt\r\ncp $SVNFOLDER/* $BUILDFOLDER/linux_32bit/ >> /dev/null 2>> /dev/null\r\nmake clean >> /dev/null\r\necho \"Making Windows\\n\"\r\nmake $THREADS FTE_TARGET=win32 CC=\"ccache i586-mingw32msvc-gcc\" sv-rel gl-rel mingl-rel >> $BUILDLOGFOLDER/win32.txt 2>> $BUILDLOGFOLDER/win32.txt\r\ncp $SVNFOLDER/* $BUILDFOLDER/win32/ >> /dev/null 2>> /dev/null\r\nmake clean >> /dev/null\r\necho \"Making MorphOS\\n\"\r\nmake $THREADS FTE_TARGET=morphos CC=\"ccache ppc-morphos-gcc\" gl-rel mingl-rel  >> $BUILDLOGFOLDER/morphos.txt 2>> $BUILDLOGFOLDER/morphos.txt\r\ncp $SVNFOLDER/* $BUILDFOLDER/morphos/ >> /dev/null 2>> /dev/null\r\nchmod ugo+x $BUILDFOLDER/morphos/*\r\nmake clean >> /dev/null\r\necho \"Making MacOSX\"\r\nmake $THREADS FTE_TARGET=macosx CC=\"ccache powerpc-apple-darwin8-gcc\" sv-rel gl-rel mingl-rel CFLAGS=\"-I/home/moodles/mac/include/ -L/home/moodles/mac/lib\"  >> $BUILDLOGFOLDER/osx_ppc.txt 2>> $BUILDLOGFOLDER/osx_ppc.txt\r\nmake $THREADS FTE_TARGET=macosx_x86 CC=\"ccache i686-apple-darwin8-gcc\" sv-rel gl-rel mingl-rel CFLAGS=\"-I/home/moodles/mac/x86/include/ -L/home/moodles/mac/x86/lib\"  >> $BUILDLOGFOLDER/osx_86.txt 2>> $BUILDLOGFOLDER/osx_86.txt\r\ncp $SVNFOLDER/* $BUILDFOLDER/macosx_tiger_10.4/ >> /dev/null 2>> /dev/null\r\nmake clean >> /dev/null\r\necho \"Making Windows SDL\"\r\nmake $THREADS FTE_TARGET=win32_SDL CFLAGS=\"-D_SDL\" CC=\"ccache i586-mingw32msvc-gcc\" >> $BUILDLOGFOLDER/win32_SDL.txt 2>> $BUILDLOGFOLDER/win32_SDL.txt\r\ncp $SVNFOLDER/* $BUILDFOLDER/win32/ >> /dev/null 2>> /dev/null\r\nmake clean >> /dev/null\r\necho \"Making Linux 32bit SDL\"\r\nmake $THREADS FTE_TARGET=SDL BITS=32 CC=\"ccache gcc -m32\" >> $BUILDLOGFOLDER/linux32_SDL.txt 2>> $BUILDLOGFOLDER/linux32_SDL.txt\r\ncp $SVNFOLDER/* $BUILDFOLDER/linux_32bit/ >> /dev/null 2>> /dev/null\r\nmake clean >> /dev/null\r\necho \"Making Linux 64bit\"\r\nmake $THREADS FTE_TARGET=linux64 CC=\"ccache gcc -m64\" LDFLAGS=\"-L./libs/64/ -I./libs/64/ -lz -lX11-xcb -lxcb-xlib -lxcb -lXdmcp -lXpm -lXau -lX11 -lXext\" >> $BUILDLOGFOLDER/linux64.txt 2>> $BUILDLOGFOLDER/linux64.txt\r\ncp $SVNFOLDER/* $BUILDFOLDER/linux_64bit/ >> /dev/null 2>> /dev/null\r\nmake clean >> /dev/null\r\necho \"Making Linux 64bit (clang)\"\r\nmake $THREADS FTE_TARGET=linux64 CC=\"ccache clang -m64 -DCLANG\" LDFLAGS=\"-L./libs/64/ -I./libs/64/ -lz -lX11-xcb -lxcb-xlib -lxcb -lXdmcp -lXpm -lXau -lX11 -lXext\" >> $BUILDLOGFOLDER/linux64_clang.txt 2>> $BUILDLOGFOLDER/linux64_clang.txt\r\ncd $SVNFOLDER\r\nls -d *fteqw* | sed 's/\\(.*\\)64$/mv \"&\" \"\\164.clang\"/' | sh\r\nmv *fteqw* $BUILDFOLDER/linux_64bit/\r\ncd ..\r\nmake clean >> /dev/null\r\necho \"Making Linux 64bit SDL (very ambitious)\"\r\nmake $THREADS FTE_TARGET=SDL gl-rel CC=\"ccache gcc -m64\" LDFLAGS=\"-L./libs/64/ -I./libs/64/ -lz -lX11-xcb -lxcb-xlib -lxcb -lXdmcp -lXpm -lXau -lX11 -lXext\" >> $BUILDLOGFOLDER/linux64_SDL.txt 2>> $BUILDLOGFOLDER/linux64_SDL.txt\r\n\r\ncd /home/moodles/fteqw/engine\r\nsvn info >> $BUILDFOLDER/version.txt\r\necho \"All done\"\r\n\r\nEND=$(date +%s)\r\nDIFF=$(( $END - $START ))\r\nMINS=$(( $DIFF / 60 ))\r\necho \"(\u0002Total Compile Time\u0002: $MINS minutes)\" > $TIMETAKENTF\r\necho \"\u0002Total Compile Time\u0002: $MINS minutes\"\r\n\r\ncd /home/moodles\r\nrm .bitchxrc\r\ncp ./fteqw/.bitchxrc ./\r\n#./BitchX -a gameservers.nj.us.quakenet.org -A -c \"#fte\" -n A_Gorilla\r\n"
  },
  {
    "path": "specs/compile farm scripts/linux/build_fteqcc.sh",
    "content": "#!/bin/bash\r\nSTART=$(date +%s)\r\nHOME=/home/moodles\r\nWEBFOLDER=/htdocs/fteqcc\r\nLOGFOLDER=/htdocs/build_logs/fteqcc\r\nBUILDFOLDER=$HOME$WEBFOLDER\r\nBUILDLOGFOLDER=$HOME$LOGFOLDER\r\nTHREADS=\"-j 4\"\r\nPATH=$PATH:/opt/mac/bin:/usr/local/bin:/opt/llvm/bin:/opt/intel/Compiler/11.0/083/bin/ia32/:/opt/morphos-dev/bin\r\n\r\ncd /home/moodles/fteqw/engine/qclib\r\nrm version.txt\r\nrm -rf /home/moodles/fteqw/fteqw/trunk/engine/export\r\nrm $BUILDFOLDER/*\r\nrm /home/moodles/htdocs/build_logs/fteqcc/*\r\n\r\nsvn update\r\nsvn info >> version.txt\r\ncd ..\r\n\r\nsvn export qclib export\r\ncp ./qclib/version.txt ./export/version.txt\r\ncd export\r\ntar -cjf fteqcc_sourcecode.tar.bz2 *\r\nmv *.bz2 $BUILDFOLDER/\r\ncd ..\r\nrm -rf export\r\n\r\ncd qclib\r\n\r\nsvn info >> $BUILDFOLDER/version.txt\r\n\r\nmake clean\r\nmake $THREADS win CC=i586-mingw32msvc-gcc >> $BUILDLOGFOLDER/win32-fteqccgui.txt 2>> $BUILDLOGFOLDER/win32-fteqccgui.txt\r\nmv fteqcc.exe $BUILDFOLDER/win32-fteqccgui.exe\r\n\r\nmake clean\r\nmake $THREADS win CC=\"x86_64-w64-mingw32-gcc -m64\" >> $BUILDLOGFOLDER/win64-fteqccgui.txt 2>> $BUILDLOGFOLDER/win64-fteqccgui.txt\r\nmv fteqcc.exe $BUILDFOLDER/win64-fteqccgui.exe\r\n\r\nmake clean\r\nmake $THREADS >> $BUILDLOGFOLDER/linux32-fteqcc.txt 2>> $BUILDLOGFOLDER/linux32-fteqcc.txt\r\nmv fteqcc.bin $BUILDFOLDER/linux32-fteqcc\r\n\r\nmake clean\r\nmake $THREADS CC=\"llvm-gcc -m32\" >> $BUILDLOGFOLDER/linux32-fteqcc_llvm.txt 2>> $BUILDLOGFOLDER/linux32-fteqcc_llvm.txt\r\nmv fteqcc.bin $BUILDFOLDER/linux32-fteqcc.llvm\r\n\r\nmake clean\r\nmake $THREADS CC=\"clang -m32 -Dinline=\" >> $BUILDLOGFOLDER/linux32-fteqcc_clang.txt 2>> $BUILDLOGFOLDER/linux32-fteqcc_clang.txt\r\nmv fteqcc.bin $BUILDFOLDER/linux32-fteqcc.clang\r\n\r\nmake clean\r\nmake $THREADS CC=\"icc -m32\" >> $BUILDLOGFOLDER/linux32-fteqcc_icc.txt 2>> $BUILDLOGFOLDER/linux32-fteqcc_icc.txt\r\nmv fteqcc.bin $BUILDFOLDER/linux32-fteqcc.icc\r\n\r\nmake clean\r\nmake $THREADS CC=\"gcc -m64\" >> $BUILDLOGFOLDER/linux64-fteqcc.txt 2>> $BUILDLOGFOLDER/linux64-fteqcc.txt\r\nmv fteqcc.bin $BUILDFOLDER/linux64-fteqcc\r\n\r\nmake clean\r\nmake $THREADS CC=\"clang -m64 -Dinline=\" >> $BUILDLOGFOLDER/linux64-fteqcc_clang.txt 2>> $BUILDLOGFOLDER/linux64-fteqcc_clang.txt\r\nmv fteqcc.bin $BUILDFOLDER/linux64-fteqcc.clang\r\n\r\nmake clean\r\nmake $THREADS CC=i586-mingw32msvc-gcc >> $BUILDLOGFOLDER/win32-fteqcc.txt 2>> $BUILDLOGFOLDER/win32_fteqcc.txt\r\nmv fteqcc.bin $BUILDFOLDER/win32-fteqcc.exe\r\n\r\nmake clean\r\nmake $THREADS win CC=\"x86_64-w64-mingw32-gcc -m64\" >> $BUILDLOGFOLDER/win64-fteqcc.txt 2>> $BUILDLOGFOLDER/win64-fteqcc.txt\r\nmv fteqcc.exe $BUILDFOLDER/win64-fteqcc.exe\r\n\r\nmake clean\r\nmake $THREADS CC=\"powerpc-apple-darwin8-gcc -arch ppc -arch i686\" >> $BUILDLOGFOLDER/macosx_tiger-fteqcc.txt 2>> $BUILDLOGFOLDER/macosx_tiger-fteqcc.txt\r\nmv fteqcc.bin $BUILDFOLDER/macosx_tiger-fteqcc\r\n\r\nmake clean\r\nmake $THREADS CC=\"ppc-morphos-gcc -noixemul\" >> $BUILDLOGFOLDER/morphos-fteqcc.txt 2>> $BUILDLOGFOLDER/morphos-fteqcc.txt\r\nmv fteqcc.bin $BUILDFOLDER/morphos-fteqcc\r\nchmod ugo+x $BUILDFOLDER/morphos-fteqcc\r\n\r\nEND=$(date +%s)\r\nDIFF=$(( $END - $START ))\r\necho \"It took $DIFF seconds\"\r\n"
  },
  {
    "path": "specs/compile farm scripts/linux/build_fteqtv.sh",
    "content": "#!/bin/bash\r\nSTART=$(date +%s)\r\nHOME=/home/moodles\r\nWEBFOLDER=/htdocs/fteqtv\r\nLOGFOLDER=/htdocs/build_logs/fteqtv\r\nBUILDFOLDER=$HOME$WEBFOLDER\r\nBUILDLOGFOLDER=$HOME$LOGFOLDER\r\nTHREADS=\"-j 4\"\r\nPATH=$PATH:/opt/mac/bin:/usr/local/bin:/opt/llvm/bin:/opt/intel/Compiler/11.0/083/bin/ia32/:/opt/morphos-dev/bin\r\n\r\nrm /home/moodles/htdocs/build_logs/fteqtv/*\r\ncd /home/moodles/fteqw/fteqw/trunk/fteqtv/\r\nrm -r release\r\nmkdir release\r\n\r\nsvn update\r\nsvn info >> version.txt\r\ncp version.txt $BUILDFOLDER/\r\ncp LICENSE $BUILDFOLDER/\r\n\r\ncd ..\r\nrm -rf export\r\nsvn export fteqtv export\r\ncp $BUILDFOLDER/version.txt ./export/version.txt\r\ncd export\r\ntar -cjf fteqtv_sourcecode.tar.bz2 *\r\nmv *.bz2 $BUILDFOLDER/\r\n\r\ncd /home/moodles/fteqw/fteqw/trunk/fteqtv\r\nmake clean\r\nmake $THREADS >> $BUILDLOGFOLDER/linux32-fteqtv.txt 2>> $BUILDLOGFOLDER/linux32-fteqtv.txt\r\nmv qtv $BUILDFOLDER/linux32-qtv\r\n\r\nmake clean\r\nmake $THREADS CC=\"gcc -m64\" >> $BUILDLOGFOLDER/linux64-fteqtv.txt 2>> $BUILDLOGFOLDER/linux64-fteqtv.txt\r\nmv qtv $BUILDFOLDER/linux64-qtv\r\n\r\nmake clean\r\nmake $THREADS CC=\"llvm-gcc\" >> $BUILDLOGFOLDER/linux32-fteqtv_llvm.txt 2>> $BUILDLOGFOLDER/linux32-fteqtv_llvm.txt\r\nmv qtv $BUILDFOLDER/linux32-qtv.llvm\r\n\r\nmake clean\r\nmake $THREADS CC=\"clang\" >> $BUILDLOGFOLDER/linux32-fteqtv_clang.txt 2>> $BUILDLOGFOLDER/linux32-fteqtv_clang.txt\r\nmv qtv $BUILDFOLDER/linux32-qtv.clang\r\n\r\nmake clean\r\nmake $THREADS CC=\"clang -m64\" >> $BUILDLOGFOLDER/linux64-fteqtv_clang.txt 2>> $BUILDLOGFOLDER/linux64-fteqtv_clang.txt\r\nmv qtv $BUILDFOLDER/linux64-qtv.clang\r\n\r\nmake clean\r\nmake $THREADS CC=\"icc\" >> $BUILDLOGFOLDER/linux32-fteqtv_icc.txt 2>> $BUILDLOGFOLDER/linux32-fteqtv_icc.txt\r\nmv qtv $BUILDFOLDER/linux32-qtv.icc\r\n\r\nmake clean\r\nmake $THREADS TOOLPREFIX=ppc-morphos- >> $BUILDLOGFOLDER/morphos-fteqtv.txt 2>> $BUILDLOGFOLDER/morphos-fteqtv.txt\r\nmv qtv $BUILDFOLDER/morphos-qtv\r\nchmod ugo+x $BUILDFOLDER/morphos-qtv\r\n\r\nmake clean\r\nmake $THREADS TOOLPREFIX=i586-mingw32msvc- qtv.exe >> $BUILDLOGFOLDER/win32-fteqtv.txt 2>> $BUILDLOGFOLDER/win32-fteqtv.txt\r\nmv qtv.exe $BUILDFOLDER/win32-qtv.exe\r\n\r\nmake clean\r\nmake $THREADS TOOLPREFIX=powerpc-apple-darwin8- STRIPFLAGS=\"\" CFLAGS=\"-arch i686 -arch ppc\" >> $BUILDLOGFOLDER/macosx_tiger.txt 2>> $BUILDLOGFOLDER/macosx_tiger-fteqtv.txt\r\n#powerpc-apple-darwin8-strip qtv.db -o qtv\r\nmv qtv $BUILDFOLDER/macosx_tiger-qtv\r\n\r\nEND=$(date +%s)\r\nDIFF=$(( $END - $START ))\r\necho \"It took $DIFF seconds\"\r\n\r\n"
  },
  {
    "path": "specs/compile farm scripts/linux/build_wip.sh",
    "content": "#!/bin/bash\r\n./ccache-alias.sh\r\nSTART=$(date +%s)\r\nHOME=/home/moodles\r\nWEBFOLDER=/htdocs/unstable\r\nLOGFOLDER=/htdocs/unstable/build_logs\r\nBUILDFOLDER=$HOME$WEBFOLDER\r\nBUILDLOGFOLDER=$HOME$LOGFOLDER\r\nSVNFOLDER=$HOME/wip/wip/engine/release\r\nTHREADS=\"-j 4\"\r\nPATH=$PATH:/opt/mac/bin:/usr/local/bin:/opt/llvm/bin:/opt/intel/Compiler/11.0/083/bin/ia32/:/opt/morphos-dev/bin\r\nTIMETAKENTF=/tmp/timetaken.txt\r\nif [ -f $TIMETAKENTF ];\r\nthen\r\n\trm $TIMETAKENTF\r\nfi\r\n\r\ncd $BUILDLOGFOLDER/\r\necho \"Deleting old build logs\"\r\nrm *.txt\r\n\r\ncd $BUILDFOLDER/\r\necho \"Deleteing old binaries\"\r\nrm * >> /dev/null 2>> /dev/null\r\nrm -rf ./win32/\r\nrm -rf ./linux_32bit/\r\nrm -rf ./linux_64bit/\r\nrm -rf ./morphos/\r\nrm -rf ./macosx_tiger_10.4/\r\n\r\necho \"Making folders\"\r\nmkdir win32\r\nmkdir linux_32bit\r\nmkdir linux_64bit\r\nmkdir morphos\r\nmkdir macosx_tiger_10.4\r\n\r\ncd /home/moodles/wip/wip/engine\r\n\r\necho \"SVN Update\"\r\nsvn update\r\n\r\nmake clean >> /dev/null\r\necho \"Making Linux 32bit (llvm)\"\r\nmake $THREADS FTE_TARGET=linux32 CC=\"llvm-gcc -m32\" >> $BUILDLOGFOLDER/linux32_llvm.txt 2>> $BUILDLOGFOLDER/linux32_llvm.txt\r\ncd $SVNFOLDER\r\nls -d *fteqw* | sed 's/\\(.*\\)32$/mv \"&\" \"\\132.llvm\"/' | sh\r\nmv *fteqw* $BUILDFOLDER/linux_32bit/\r\ncd ..\r\necho \"Nuking llvm object files\"\r\nmake clean >> /dev/null\r\necho \"Making Linux 32bit (clang)\"\r\nmake $THREADS FTE_TARGET=linux32 CC=\"clang -DCLANG\" >> $BUILDLOGFOLDER/linux32_clang.txt 2>> $BUILDLOGFOLDER/linux32_clang.txt\r\ncd $SVNFOLDER\r\nls -d *fteqw* | sed 's/\\(.*\\)32$/mv \"&\" \"\\132.clang\"/' | sh\r\nmv *fteqw* $BUILDFOLDER/linux_32bit/\r\ncd ..\r\necho \"Nuking clang object files\"\r\nmake clean >> /dev/null\r\necho \"Making Linux 32bit (icc)\"\r\nmake $THREADS FTE_TARGET=linux32 CC=\"icc\" >> $BUILDLOGFOLDER/linux32_icc.txt 2>> $BUILDLOGFOLDER/linux32_icc.txt\r\ncd $SVNFOLDER\r\nls -d *fteqw* | sed 's/\\(.*\\)32$/mv \"&\" \"\\132.icc\"/' | sh\r\nmv *fteqw* $BUILDFOLDER/linux_32bit/\r\ncd ..\r\necho \"Nuking icc object files\"\r\nmake clean >> /dev/null\r\necho \"Making Linux 32bit SDL (llvm)\"\r\nmake $THREADS FTE_TARGET=sdl BITS=32 CC=\"llvm-gcc -m32\" >> $BUILDLOGFOLDER/linux32_SDL_llvm.txt 2>> $BUILDLOGFOLDER/linux32_SDL_llvm.txt\r\ncd $SVNFOLDER\r\nls -d *fteqw* | sed 's/\\(.*\\)32$/mv \"&\" \"\\132.llvm\"/' | sh\r\nmv *fteqw* $BUILDFOLDER/linux_32bit/\r\ncd ..\r\necho \"Nuking 32bit SDL llvm object files\"\r\nmake clean >> /dev/null\r\necho \"Making Linux 32bit SDL (clang)\"\r\nmake $THREADS FTE_TARGET=sdl BITS=32 CC=\"clang -m32 -DCLANG\" >> $BUILDLOGFOLDER/linux32_SDL_clang.txt 2>> $BUILDLOGFOLDER/linux32_SDL_clang.txt\r\ncd $SVNFOLDER\r\nls -d *fteqw* | sed 's/\\(.*\\)32$/mv \"&\" \"\\132.clang\"/' | sh\r\nmv *fteqw* $BUILDFOLDER/linux_32bit/\r\ncd ..\r\necho \"Nuking 32bit SDL clang object files\"\r\nmake clean >> /dev/null\r\necho \"Making Linux 32bit SDL (icc)\"\r\nmake $THREADS FTE_TARGET=sdl BITS=32 CC=\"icc -m32\" >> $BUILDLOGFOLDER/linux32_SDL_icc.txt 2>> $BUILDLOGFOLDER/linux32_SDL_icc.txt\r\ncd $SVNFOLDER\r\nls -d *fteqw* | sed 's/\\(.*\\)32$/mv \"&\" \"\\132.icc\"/' | sh\r\nmv *fteqw* $BUILDFOLDER/linux_32bit/\r\ncd ..\r\necho \"Nuking 32bit SDL icc object files\"\r\nmake clean >> /dev/null\r\necho \"Making Linux 32bit (gcc)\"\r\nmake $THREADS FTE_TARGET=linux32 >> $BUILDLOGFOLDER/linux32.txt 2>> $BUILDLOGFOLDER/linux32.txt\r\ncp $SVNFOLDER/* $BUILDFOLDER/linux_32bit/ >> /dev/null 2>> /dev/null\r\nmake clean >> /dev/null\r\necho \"Making Windows\\n\"\r\nmake $THREADS FTE_TARGET=win32 sv-rel gl-rel mingl-rel >> $BUILDLOGFOLDER/win32.txt 2>> $BUILDLOGFOLDER/win32.txt\r\ncp $SVNFOLDER/* $BUILDFOLDER/win32/ >> /dev/null 2>> /dev/null\r\nmake clean >> /dev/null\r\necho \"Making MorphOS\\n\"\r\nmake $THREADS FTE_TARGET=morphos gl-rel mingl-rel  >> $BUILDLOGFOLDER/morphos.txt 2>> $BUILDLOGFOLDER/morphos.txt\r\ncp $SVNFOLDER/* $BUILDFOLDER/morphos/ >> /dev/null 2>> /dev/null\r\nchmod ugo+x $BUILDFOLDER/morphos/*\r\nmake clean >> /dev/null\r\necho \"Making MacOSX\"\r\nmake $THREADS FTE_TARGET=macosx sv-rel gl-rel mingl-rel CFLAGS=\"-I/home/moodles/mac/include/ -L/home/moodles/mac/lib\"  >> $BUILDLOGFOLDER/osx_ppc.txt 2>> $BUILDLOGFOLDER/osx_ppc.txt\r\nmake $THREADS FTE_TARGET=macosx_x86 sv-rel gl-rel mingl-rel CFLAGS=\"-I/home/moodles/mac/x86/include/ -L/home/moodles/mac/x86/lib\"  >> $BUILDLOGFOLDER/osx_86.txt 2>> $BUILDLOGFOLDER/osx_86.txt\r\ncp $SVNFOLDER/* $BUILDFOLDER/macosx_tiger_10.4/ >> /dev/null 2>> /dev/null\r\nmake clean >> /dev/null\r\necho \"Making Windows SDL\"\r\nmake $THREADS FTE_TARGET=win32_SDL CFLAGS=\"-D_SDL\" >> $BUILDLOGFOLDER/win32_SDL.txt 2>> $BUILDLOGFOLDER/win32_SDL.txt\r\ncp $SVNFOLDER/* $BUILDFOLDER/win32/ >> /dev/null 2>> /dev/null\r\nmake clean >> /dev/null\r\necho \"Making Linux 32bit SDL\"\r\nmake $THREADS FTE_TARGET=SDL BITS=32 >> $BUILDLOGFOLDER/linux32_SDL.txt 2>> $BUILDLOGFOLDER/linux32_SDL.txt\r\ncp $SVNFOLDER/* $BUILDFOLDER/linux_32bit/ >> /dev/null 2>> /dev/null\r\nmake clean >> /dev/null\r\necho \"Making Linux 64bit\"\r\nmake $THREADS FTE_TARGET=linux64 LDFLAGS=\"-L./libs/64/ -I./libs/64/ -lz -lX11-xcb -lxcb-xlib -lxcb -lXdmcp -lXpm -lXau -lX11 -lXext\" >> $BUILDLOGFOLDER/linux64.txt 2>> $BUILDLOGFOLDER/linux64.txt\r\ncp $SVNFOLDER/* $BUILDFOLDER/linux_64bit/ >> /dev/null 2>> /dev/null\r\nmake clean >> /dev/null\r\necho \"Making Linux 64bit (clang)\"\r\nmake $THREADS FTE_TARGET=linux64 CC=\"clang -m64 -DCLANG\" LDFLAGS=\"-L./libs/64/ -I./libs/64/ -lz -lX11-xcb -lxcb-xlib -lxcb -lXdmcp -lXpm -lXau -lX11 -lXext\" >> $BUILDLOGFOLDER/linux64_clang.txt 2>> $BUILDLOGFOLDER/linux64_clang.txt\r\ncd $SVNFOLDER\r\nls -d *fteqw* | sed 's/\\(.*\\)64$/mv \"&\" \"\\164.clang\"/' | sh\r\nmv *fteqw* $BUILDFOLDER/linux_64bit/\r\ncd ..\r\nmake clean >> /dev/null\r\necho \"Making Linux 64bit SDL (very ambitious)\"\r\nmake $THREADS FTE_TARGET=SDL gl-rel CC=\"gcc -m64\" LDFLAGS=\"-L./libs/64/ -I./libs/64/ -lz -lX11-xcb -lxcb-xlib -lxcb -lXdmcp -lXpm -lXau -lX11 -lXext\" >> $BUILDLOGFOLDER/linux64_SDL.txt 2>> $BUILDLOGFOLDER/linux64_SDL.txt\r\n\r\ncd /home/moodles/wip/wip/engine\r\nsvn info >> $BUILDFOLDER/version.txt\r\necho \"All done\"\r\n\r\nEND=$(date +%s)\r\nDIFF=$(( $END - $START ))\r\nMINS=$(( $DIFF / 60 ))\r\necho \"(\u0002Total Compile Time\u0002: $MINS minutes)\" > $TIMETAKENTF\r\necho \"\u0002Total Compile Time\u0002: $MINS minutes\"\r\n\r\ncd /home/moodles\r\nrm .bitchxrc\r\ncp ./wip/.bitchxrc ./\r\n./BitchX -a gameservers.nj.us.quakenet.org -A -c \"#fte\" -n A_WIP_Gorilla\r\n"
  },
  {
    "path": "specs/compile farm scripts/linux/build_wip_ccache.sh",
    "content": "#!/bin/bash\r\nSTART=$(date +%s)\r\nHOME=/home/moodles\r\nWEBFOLDER=/htdocs/unstable\r\nLOGFOLDER=/htdocs/unstable/build_logs\r\nBUILDFOLDER=$HOME$WEBFOLDER\r\nBUILDLOGFOLDER=$HOME$LOGFOLDER\r\nSVNFOLDER=$HOME/wip/wip/engine/release\r\nTHREADS=\"-j 4\"\r\nPATH=$PATH:/opt/mac/bin:/usr/local/bin:/opt/llvm/bin:/opt/intel/Compiler/11.0/083/bin/ia32/:/opt/morphos-dev/bin\r\nTIMETAKENTF=/tmp/timetaken.txt\r\nif [ -f $TIMETAKENTF ];\r\nthen\r\n\trm $TIMETAKENTF\r\nfi\r\n\r\ncd $BUILDLOGFOLDER/\r\necho \"Deleting old build logs\"\r\nrm *.txt\r\n\r\ncd $BUILDFOLDER/\r\necho \"Deleteing old binaries\"\r\nrm * >> /dev/null 2>> /dev/null\r\nrm -rf ./win32/\r\nrm -rf ./linux_32bit/\r\nrm -rf ./linux_64bit/\r\nrm -rf ./morphos/\r\nrm -rf ./macosx_tiger_10.4/\r\n\r\necho \"Making folders\"\r\nmkdir win32\r\nmkdir linux_32bit\r\nmkdir linux_64bit\r\nmkdir morphos\r\nmkdir macosx_tiger_10.4\r\n\r\ncd /home/moodles/wip/wip/engine\r\n\r\necho \"SVN Update\"\r\nsvn update\r\n\r\nmake clean >> /dev/null\r\necho \"Making Linux 32bit (llvm)\"\r\nmake $THREADS FTE_TARGET=linux32 CC=\"ccache llvm-gcc -m32\" >> $BUILDLOGFOLDER/linux32_llvm.txt 2>> $BUILDLOGFOLDER/linux32_llvm.txt\r\ncd $SVNFOLDER\r\nls -d *fteqw* | sed 's/\\(.*\\)32$/mv \"&\" \"\\132.llvm\"/' | sh\r\nmv *fteqw* $BUILDFOLDER/linux_32bit/\r\ncd ..\r\necho \"Nuking llvm object files\"\r\nmake clean >> /dev/null\r\necho \"Making Linux 32bit (clang)\"\r\nmake $THREADS FTE_TARGET=linux32 CC=\"ccache clang -DCLANG\" >> $BUILDLOGFOLDER/linux32_clang.txt 2>> $BUILDLOGFOLDER/linux32_clang.txt\r\ncd $SVNFOLDER\r\nls -d *fteqw* | sed 's/\\(.*\\)32$/mv \"&\" \"\\132.clang\"/' | sh\r\nmv *fteqw* $BUILDFOLDER/linux_32bit/\r\ncd ..\r\necho \"Nuking clang object files\"\r\nmake clean >> /dev/null\r\necho \"Making Linux 32bit (icc)\"\r\nmake $THREADS FTE_TARGET=linux32 CC=\"ccache icc\" >> $BUILDLOGFOLDER/linux32_icc.txt 2>> $BUILDLOGFOLDER/linux32_icc.txt\r\ncd $SVNFOLDER\r\nls -d *fteqw* | sed 's/\\(.*\\)32$/mv \"&\" \"\\132.icc\"/' | sh\r\nmv *fteqw* $BUILDFOLDER/linux_32bit/\r\ncd ..\r\necho \"Nuking icc object files\"\r\nmake clean >> /dev/null\r\necho \"Making Linux 32bit SDL (llvm)\"\r\nmake $THREADS FTE_TARGET=sdl BITS=32 CC=\"ccache llvm-gcc -m32\" >> $BUILDLOGFOLDER/linux32_SDL_llvm.txt 2>> $BUILDLOGFOLDER/linux32_SDL_llvm.txt\r\ncd $SVNFOLDER\r\nls -d *fteqw* | sed 's/\\(.*\\)32$/mv \"&\" \"\\132.llvm\"/' | sh\r\nmv *fteqw* $BUILDFOLDER/linux_32bit/\r\ncd ..\r\necho \"Nuking 32bit SDL llvm object files\"\r\nmake clean >> /dev/null\r\necho \"Making Linux 32bit SDL (clang)\"\r\nmake $THREADS FTE_TARGET=sdl BITS=32 CC=\"ccache clang -m32 -DCLANG\" >> $BUILDLOGFOLDER/linux32_SDL_clang.txt 2>> $BUILDLOGFOLDER/linux32_SDL_clang.txt\r\ncd $SVNFOLDER\r\nls -d *fteqw* | sed 's/\\(.*\\)32$/mv \"&\" \"\\132.clang\"/' | sh\r\nmv *fteqw* $BUILDFOLDER/linux_32bit/\r\ncd ..\r\necho \"Nuking 32bit SDL clang object files\"\r\nmake clean >> /dev/null\r\necho \"Making Linux 32bit SDL (icc)\"\r\nmake $THREADS FTE_TARGET=sdl BITS=32 CC=\"ccache icc -m32\" >> $BUILDLOGFOLDER/linux32_SDL_icc.txt 2>> $BUILDLOGFOLDER/linux32_SDL_icc.txt\r\ncd $SVNFOLDER\r\nls -d *fteqw* | sed 's/\\(.*\\)32$/mv \"&\" \"\\132.icc\"/' | sh\r\nmv *fteqw* $BUILDFOLDER/linux_32bit/\r\ncd ..\r\necho \"Nuking 32bit SDL icc object files\"\r\nmake clean >> /dev/null\r\necho \"Making Linux 32bit (gcc)\"\r\nmake $THREADS FTE_TARGET=linux32 CC=\"ccache gcc -m32\" >> $BUILDLOGFOLDER/linux32.txt 2>> $BUILDLOGFOLDER/linux32.txt\r\ncp $SVNFOLDER/* $BUILDFOLDER/linux_32bit/ >> /dev/null 2>> /dev/null\r\nmake clean >> /dev/null\r\necho \"Making Windows\\n\"\r\nmake $THREADS FTE_TARGET=win32 CC=\"ccache i586-mingw32msvc-gcc\" WINDRES=\"i586-mingw32msvc-windres\" STRIP=\"i586-mingw32msvc-strip\" sv-rel gl-rel mingl-rel >> $BUILDLOGFOLDER/win32.txt 2>> $BUILDLOGFOLDER/win32.txt\r\ncp $SVNFOLDER/* $BUILDFOLDER/win32/ >> /dev/null 2>> /dev/null\r\nmake clean >> /dev/null\r\necho \"Making MorphOS\\n\"\r\nmake $THREADS FTE_TARGET=morphos CC=\"ccache ppc-morphos-gcc\" STRIP=\"ppc-morphos-strip\" gl-rel mingl-rel  >> $BUILDLOGFOLDER/morphos.txt 2>> $BUILDLOGFOLDER/morphos.txt\r\ncp $SVNFOLDER/* $BUILDFOLDER/morphos/ >> /dev/null 2>> /dev/null\r\nchmod ugo+x $BUILDFOLDER/morphos/*\r\nmake clean >> /dev/null\r\necho \"Making MacOSX\"\r\nmake $THREADS FTE_TARGET=macosx CC=\"ccache powerpc-apple-darwin8-gcc -arch ppc\" STRIP=\"powerpc-apple-darwin8-strip\" sv-rel gl-rel mingl-rel CFLAGS=\"-I/home/moodles/mac/include/ -L/home/moodles/mac/lib\"  >> $BUILDLOGFOLDER/osx_ppc.txt 2>> $BUILDLOGFOLDER/osx_ppc.txt\r\nmake $THREADS FTE_TARGET=macosx_x86 CC=\"ccache i686-apple-darwin8-gcc -arch i686\" STRIP=\"i686-apple-darwin8-strip\" sv-rel gl-rel mingl-rel CFLAGS=\"-I/home/moodles/mac/x86/include/ -L/home/moodles/mac/x86/lib\"  >> $BUILDLOGFOLDER/osx_86.txt 2>> $BUILDLOGFOLDER/osx_86.txt\r\ncp $SVNFOLDER/* $BUILDFOLDER/macosx_tiger_10.4/ >> /dev/null 2>> /dev/null\r\nmake clean >> /dev/null\r\necho \"Making Windows SDL\"\r\nmake $THREADS FTE_TARGET=win32_SDL CC=\"ccache i586-mingw32msvc-gcc\" CFLAGS=\"-D_SDL\" WINDRES=\"i586-mingw32msvc-windres\" STRIP=\"i586-mingw32msvc-strip\" >> $BUILDLOGFOLDER/win32_SDL.txt 2>> $BUILDLOGFOLDER/win32_SDL.txt\r\ncp $SVNFOLDER/* $BUILDFOLDER/win32/ >> /dev/null 2>> /dev/null\r\nmake clean >> /dev/null\r\necho \"Making Linux 32bit SDL\"\r\nmake $THREADS FTE_TARGET=SDL BITS=32 CC=\"ccache gcc -m32\" >> $BUILDLOGFOLDER/linux32_SDL.txt 2>> $BUILDLOGFOLDER/linux32_SDL.txt\r\ncp $SVNFOLDER/* $BUILDFOLDER/linux_32bit/ >> /dev/null 2>> /dev/null\r\nmake clean >> /dev/null\r\necho \"Making Linux 64bit\"\r\nmake $THREADS FTE_TARGET=linux64 CC=\"ccache gcc -m64\" LDFLAGS=\"-L./libs/64/ -I./libs/64/ -lz -lX11-xcb -lxcb-xlib -lxcb -lXdmcp -lXpm -lXau -lX11 -lXext\" >> $BUILDLOGFOLDER/linux64.txt 2>> $BUILDLOGFOLDER/linux64.txt\r\ncp $SVNFOLDER/* $BUILDFOLDER/linux_64bit/ >> /dev/null 2>> /dev/null\r\nmake clean >> /dev/null\r\necho \"Making Linux 64bit (clang)\"\r\nmake $THREADS FTE_TARGET=linux64 CC=\"ccache clang -m64 -DCLANG\" LDFLAGS=\"-L./libs/64/ -I./libs/64/ -lz -lX11-xcb -lxcb-xlib -lxcb -lXdmcp -lXpm -lXau -lX11 -lXext\" >> $BUILDLOGFOLDER/linux64_clang.txt 2>> $BUILDLOGFOLDER/linux64_clang.txt\r\ncd $SVNFOLDER\r\nls -d *fteqw* | sed 's/\\(.*\\)64$/mv \"&\" \"\\164.clang\"/' | sh\r\nmv *fteqw* $BUILDFOLDER/linux_64bit/\r\ncd ..\r\nmake clean >> /dev/null\r\necho \"Making Linux 64bit SDL (very ambitious) (disabled)\"\r\n#make $THREADS FTE_TARGET=SDL gl-rel CC=\"ccache gcc -m64\" LDFLAGS=\"-L./libs/64/ -I./libs/64/ -lz -lX11-xcb -lxcb-xlib -lxcb -lXdmcp -lXpm -lXau -lX11 -lXext\" >> $BUILDLOGFOLDER/linux64_SDL.txt 2>> $BUILDLOGFOLDER/linux64_SDL.txt\r\n\r\ncd /home/moodles/wip/wip/engine\r\nsvn info >> $BUILDFOLDER/version.txt\r\necho \"All done\"\r\n\r\nEND=$(date +%s)\r\nDIFF=$(( $END - $START ))\r\nMINS=$(( $DIFF / 60 ))\r\necho \"(\u0002Total Compile Time (w/ ccache)\u0002: $MINS minutes)\" > $TIMETAKENTF\r\necho \"\u0002Total Compile Time\u0002: $MINS minutes\"\r\n\r\ncd /home/moodles\r\nrm .bitchxrc\r\ncp ./wip/.bitchxrc ./\r\n./BitchX -a gameservers.nj.us.quakenet.org -A -c \"#fte\" -n A_WIP_Gorilla\r\n"
  },
  {
    "path": "specs/compile farm scripts/linux/build_wip_fteqcc.sh",
    "content": "#!/bin/bash\nSTART=$(date +%s)\nHOME=/home/moodles\nWEBFOLDER=/htdocs/unstable/fteqcc\nLOGFOLDER=/htdocs/unstable/build_logs/fteqcc\nBUILDFOLDER=$HOME$WEBFOLDER\nBUILDLOGFOLDER=$HOME$LOGFOLDER\nTHREADS=\"-j 4\"\nPATH=$PATH:/opt/mac/bin:/usr/local/bin:/opt/llvm/bin:/opt/intel/Compiler/11.0/083/bin/ia32/:/opt/morphos-dev/bin\n\ncd /home/moodles/wip/wip/engine/qclib\nrm version.txt\nrm -rf /home/moodles/wip/wip/engine/export\nrm $BUILDFOLDER/*\nrm /home/moodles/htdocs/unstable/build_logs/fteqcc/*\n\nsvn update\nsvn info >> version.txt\ncd ..\n\nsvn export qclib export\ncp ./qclib/version.txt ./export/version.txt\ncd export\ntar -cjf fteqcc_sourcecode.tar.bz2 *\nmv *.bz2 $BUILDFOLDER/\ncd ..\nrm -rf export\n\ncd qclib\n\nsvn info >> $BUILDFOLDER/version.txt\n\nmake clean\nmake $THREADS win CC=i586-mingw32msvc-gcc >> $BUILDLOGFOLDER/win32-fteqccgui.txt 2>> $BUILDLOGFOLDER/win32-fteqccgui.txt\nmv fteqcc.exe $BUILDFOLDER/win32-fteqccgui.exe\n\nmake clean\nmake $THREADS win CC=\"x86_64-w64-mingw32-gcc -m64\" >> $BUILDLOGFOLDER/win64-fteqccgui.txt 2>> $BUILDLOGFOLDER/win64-fteqccgui.txt\nmv fteqcc.exe $BUILDFOLDER/win64-fteqccgui.exe\n\nmake clean\nmake $THREADS >> $BUILDLOGFOLDER/linux32-fteqcc.txt 2>> $BUILDLOGFOLDER/linux32-fteqcc.txt\nmv fteqcc.bin $BUILDFOLDER/linux32-fteqcc\n\nmake clean\nmake $THREADS CC=\"llvm-gcc -m32\" >> $BUILDLOGFOLDER/linux32-fteqcc_llvm.txt 2>> $BUILDLOGFOLDER/linux32-fteqcc_llvm.txt\nmv fteqcc.bin $BUILDFOLDER/linux32-fteqcc.llvm\n\nmake clean\nmake $THREADS CC=\"clang -m32 -Dinline=\" >> $BUILDLOGFOLDER/linux32-fteqcc_clang.txt 2>> $BUILDLOGFOLDER/linux32-fteqcc_clang.txt\nmv fteqcc.bin $BUILDFOLDER/linux32-fteqcc.clang\n\nmake clean\nmake $THREADS CC=\"icc -m32\" >> $BUILDLOGFOLDER/linux32-fteqcc_icc.txt 2>> $BUILDLOGFOLDER/linux32-fteqcc_icc.txt\nmv fteqcc.bin $BUILDFOLDER/linux32-fteqcc.icc\n\nmake clean\nmake $THREADS CC=\"gcc -m64\" >> $BUILDLOGFOLDER/linux64-fteqcc.txt 2>> $BUILDLOGFOLDER/linux64-fteqcc.txt\nmv fteqcc.bin $BUILDFOLDER/linux64-fteqcc\n\nmake clean\nmake $THREADS CC=\"clang -m64 -Dinline=\" >> $BUILDLOGFOLDER/linux64-fteqcc_clang.txt 2>> $BUILDLOGFOLDER/linux64-fteqcc_clang.txt\nmv fteqcc.bin $BUILDFOLDER/linux64-fteqcc.clang\n\nmake clean\nmake $THREADS CC=i586-mingw32msvc-gcc >> $BUILDLOGFOLDER/win32-fteqcc.txt 2>> $BUILDLOGFOLDER/win32_fteqcc.txt\nmv fteqcc.bin $BUILDFOLDER/win32-fteqcc.exe\n\nmake clean\nmake $THREADS win CC=\"x86_64-w64-mingw32-gcc -m64\" >> $BUILDLOGFOLDER/win64-fteqcc.txt 2>> $BUILDLOGFOLDER/win64-fteqcc.txt\nmv fteqcc.exe $BUILDFOLDER/win64-fteqcc.exe\n\nmake clean\nmake $THREADS CC=\"powerpc-apple-darwin8-gcc -arch ppc -arch i686\" >> $BUILDLOGFOLDER/macosx_tiger-fteqcc.txt 2>> $BUILDLOGFOLDER/macosx_tiger-fteqcc.txt\nmv fteqcc.bin $BUILDFOLDER/macosx_tiger-fteqcc\n\nmake clean\nmake $THREADS CC=\"ppc-morphos-gcc -noixemul\" >> $BUILDLOGFOLDER/morphos-fteqcc.txt 2>> $BUILDLOGFOLDER/morphos-fteqcc.txt\nmv fteqcc.bin $BUILDFOLDER/morphos-fteqcc\nchmod ugo+x $BUILDFOLDER/morphos-fteqcc\n\nEND=$(date +%s)\nDIFF=$(( $END - $START ))\necho \"It took $DIFF seconds\"\n"
  },
  {
    "path": "specs/compile farm scripts/linux/build_wip_fteqtv.sh",
    "content": "#!/bin/bash\nSTART=$(date +%s)\nHOME=/home/moodles\nWEBFOLDER=/htdocs/unstable/fteqtv\nLOGFOLDER=/htdocs/unstable/build_logs/fteqtv\nBUILDFOLDER=$HOME$WEBFOLDER\nBUILDLOGFOLDER=$HOME$LOGFOLDER\nTHREADS=\"-j 4\"\nPATH=$PATH:/opt/mac/bin:/usr/local/bin:/opt/llvm/bin:/opt/intel/Compiler/11.0/083/bin/ia32/:/opt/morphos-dev/bin\n\nrm /home/moodles/htdocs/unstable/build_logs/fteqtv/*\ncd /home/moodles/wip/wip/fteqtv/\nrm -r release\nmkdir release\n\nsvn update\nsvn info >> version.txt\ncp version.txt $BUILDFOLDER/\ncp LICENSE $BUILDFOLDER/\n\ncd ..\nrm -rf export\nsvn export fteqtv export\ncp $BUILDFOLDER/version.txt ./export/version.txt\ncd export\ntar -cjf fteqtv_sourcecode.tar.bz2 *\nmv *.bz2 $BUILDFOLDER/\n\ncd /home/moodles/wip/wip/fteqtv\nmake clean\nmake $THREADS >> $BUILDLOGFOLDER/linux32-fteqtv.txt 2>> $BUILDLOGFOLDER/linux32-fteqtv.txt\nmv qtv $BUILDFOLDER/linux32-qtv\n\nmake clean\nmake $THREADS CC=\"gcc -m64\" >> $BUILDLOGFOLDER/linux64-fteqtv.txt 2>> $BUILDLOGFOLDER/linux64-fteqtv.txt\nmv qtv $BUILDFOLDER/linux64-qtv\n\nmake clean\nmake $THREADS CC=\"llvm-gcc\" >> $BUILDLOGFOLDER/linux32-fteqtv_llvm.txt 2>> $BUILDLOGFOLDER/linux32-fteqtv_llvm.txt\nmv qtv $BUILDFOLDER/linux32-qtv.llvm\n\nmake clean\nmake $THREADS CC=\"clang\" >> $BUILDLOGFOLDER/linux32-fteqtv_clang.txt 2>> $BUILDLOGFOLDER/linux32-fteqtv_clang.txt\nmv qtv $BUILDFOLDER/linux32-qtv.clang\n\nmake clean\nmake $THREADS CC=\"clang -m64\" >> $BUILDLOGFOLDER/linux64-fteqtv_clang.txt 2>> $BUILDLOGFOLDER/linux64-fteqtv_clang.txt\nmv qtv $BUILDFOLDER/linux64-qtv.clang\n\nmake clean\nmake $THREADS CC=\"icc\" >> $BUILDLOGFOLDER/linux32-fteqtv_icc.txt 2>> $BUILDLOGFOLDER/linux32-fteqtv_icc.txt\nmv qtv $BUILDFOLDER/linux32-qtv.icc\n\nmake clean\nmake $THREADS TOOLPREFIX=ppc-morphos- >> $BUILDLOGFOLDER/morphos-fteqtv.txt 2>> $BUILDLOGFOLDER/morphos-fteqtv.txt\nmv qtv $BUILDFOLDER/morphos-qtv\nchmod ugo+x $BUILDFOLDER/morphos-qtv\n\nmake clean\nmake $THREADS TOOLPREFIX=i586-mingw32msvc- qtv.exe >> $BUILDLOGFOLDER/win32-fteqtv.txt 2>> $BUILDLOGFOLDER/win32-fteqtv.txt\nmv qtv.exe $BUILDFOLDER/win32-qtv.exe\n\nmake clean\nmake $THREADS TOOLPREFIX=powerpc-apple-darwin8- STRIPFLAGS=\"\" CFLAGS=\"-arch i686 -arch ppc\" >> $BUILDLOGFOLDER/macosx_tiger.txt 2>> $BUILDLOGFOLDER/macosx_tiger-fteqtv.txt\n#powerpc-apple-darwin8-strip qtv.db -o qtv\nmv qtv $BUILDFOLDER/macosx_tiger-qtv\n\nEND=$(date +%s)\nDIFF=$(( $END - $START ))\necho \"It took $DIFF seconds\"\n\n"
  },
  {
    "path": "specs/compile farm scripts/linux/buildnumber.sh",
    "content": "#!/bin/bash\r\nCURRENTYEAR=$(date +'%Y')\r\nCURRENTDAYOFYEAR=$(date +'%j')\r\n\r\nwhile [ $CURRENTYEAR != 1998 ]\r\ndo\r\n\tCURRENTYEAR=$(expr $CURRENTYEAR - 1)\r\n\r\n\tif [ `expr $CURRENTYEAR % 4` -ne 0 ]\r\n\tthen\r\n\t\techo hi > /dev/null\r\n\telif [ `expr $CURRENTYEAR % 400` -eq 0 ]\r\n\tthen\r\n\t\tCURRENTDAYOFYEAR=$(expr $CURRENTDAYOFYEAR + 1)\r\n\telif [ `expr $CURRENTYEAR % 100` -eq 0 ]\r\n\tthen\r\n\t\techo hi > /dev/null\r\n\telse\r\n\t\tCURRENTDAYOFYEAR=$(expr $CURRENTDAYOFYEAR + 1)\t\t\r\n\tfi\r\n\r\n\tif [ $CURRENTYEAR == 1998 ]\r\n\tthen\r\n\t\tCURRENTDAYOFYEAR=$(expr $CURRENTDAYOFYEAR + 15)\r\n\telse\r\n\t\tCURRENTDAYOFYEAR=$(expr $CURRENTDAYOFYEAR + 365)\r\n\tfi\r\ndone\r\n\r\necho $CURRENTDAYOFYEAR\r\n"
  },
  {
    "path": "specs/compile farm scripts/linux/ccache-alias.sh",
    "content": "alias gcc='ccache gcc'\r\nalias i586-mingw32msvc-gcc='ccache i586-mingw32msvc-gcc'\r\nalias llvm-gcc='ccache llvm-gcc'\r\nalias clang='ccache clang'\r\nalias icc='ccache icc'\r\nalias powerpc-apple-darwin8-gcc='ccache powerpc-apple-darwin8-gcc'\r\nalias i686-apple-darwin8-gcc='ccache i686-apple-darwin8-gcc'\r\nalias ppc-morphos-gcc='ccache ppc-morphos-gcc'"
  },
  {
    "path": "specs/compile farm scripts/linux/count.sh",
    "content": "#!/bin/bash\r\nTEMPFILE=/tmp/moodlescountjunk.txt\r\nTEMPFILE2=/tmp/moodlescountjunk2.txt\r\nAUTHOR=`cat /home/moodles/fteqw/engine/svninfo.txt | grep 'Last Changed Author' | sed s/'Last Changed Author: '//`\r\nif [ -f $TEMPFILE ];\r\nthen\r\n\trm $TEMPFILE\r\nfi\r\nif [ -f $TEMPFILE2 ];\r\nthen\r\n\trm $TEMPFILE2\r\nfi\r\n\r\n# count folder contents\r\nLINUX32=$(ls -1 /home/moodles/htdocs/linux_32bit/ | wc -l)\r\nLINUX64=$(ls -1 /home/moodles/htdocs/linux_64bit/ | wc -l)\r\nWIN32=$(ls -1 /home/moodles/htdocs/win32/ | wc -l)\r\nMORPHOS=$(ls -1 /home/moodles/htdocs/morphos/ | wc -l)\r\nMACOSX=$(ls -1 /home/moodles/htdocs/macosx_tiger_10.4/ | wc -l)\r\n\r\n# known count when all binaries build\r\nLIN32TOTAL=24 #4 compilers (GCC, ICC, CLANG & LLVM) * 3 targets (sv, mingl, gl) * 2 (SDL versions)\r\nLIN64TOTAL=6 #2 compilers (GCC, CLANG) * 3 targets (sv, mingl, gl)\r\nWIN32TOTAL=7 #1 compiler (MinGW32) * 3 targets (sv, mingl, gl) * 2 (SDL versions)\r\nMORPHTOTAL=2 #1 compiler (GCC based by bigfoot) * 2 targets (gl, mingl)\r\nMACOSTOTAL=6 #2 compilers (10.4 x86 and ppc) * 3 targets (gl, mingl, sv)\r\n\r\n# subtract known directory count from realtime counted directory count to find total of failed builds\r\nL32NUM=`expr $LIN32TOTAL - $LINUX32`\r\nL64NUM=`expr $LIN64TOTAL - $LINUX64`\r\nW32NUM=`expr $WIN32TOTAL - $WIN32`\r\nMORNUM=`expr $MORPHTOTAL - $MORPHOS`\r\nMACNUM=`expr $MACOSTOTAL - $MACOSX`\r\n\r\nif [ $L32NUM -ne 0 ];\r\nthen\r\n\techo \"$L32NUM/$LIN32TOTAL Lin32 failed\" >> $TEMPFILE \r\nfi\r\nif [ $L64NUM -ne 0 ];\r\nthen\r\n        echo \" $L64NUM/$LIN64TOTAL Lin64 failed\" >> $TEMPFILE\r\nfi\r\nif [ $W32NUM -ne 0 ];\r\nthen\r\n        echo \" $W32NUM/$WIN32TOTAL Win32 failed\" >> $TEMPFILE\r\nfi\r\nif [ $MORNUM -ne 0 ];\r\nthen\r\n        echo \" $MORNUM/$MORPHTOTAL MorphOS failed\" >> $TEMPFILE\r\nfi\r\nif [ $MACNUM -ne 0 ];\r\nthen\r\n        echo \" $MACNUM/$MACOSTOTAL MacOSX 10.4 failed\" >> $TEMPFILE\r\nfi\r\nif [ $L32NUM -eq 0 ] && [ $L64NUM -eq 0 ] && [ $W32NUM -eq 0 ] && [ $MORNUM -eq 0 ] && [ $MACNUM -eq 0 ];\r\nthen\r\n\techo \"Everything built successfully\" >> $TEMPFILE\r\n\r\nelif [ $LIN32TOTAL -eq 0 ] && [ $LIN64TOTAL -eq 0 ] && [ $WIN32TOTAL -eq 0 ] && [ $MORPHTOTAL -eq 0 ] && [ $MACOSTOTAL -eq 0 ];\r\nthen\r\n        echo \" Nothing built at all!\" >> $TEMPFILE\r\nelif [ $L32NUM -ne 0 ] && [ $L64NUM -ne 0 ] && [ $W32NUM -ne 0 ] && [ $MORNUM -ne 0 ] && [ $MACNUM -ne 0 ];\r\nthen\r\n        if [ $AUTHOR == \"acceptthis\" ];\r\n        then\r\n\t\techo \" At least 1 target from each OS failed to build, on the bright side, at least everything didn't compile (Your typical average Spike commit, breaking Lunix as usual)\" >> $TEMPFILE\r\n\telse\r\n               echo \" At least 1 target from each OS failed to build, on the bright side, at least everythinng didn't compile\" >> $TEMPFILE\r\n\tfi\t\r\nelse\r\n\tif [ $AUTHOR == \"acceptthis\" ];\r\n\tthen\r\n\t\techo \" Everything \u0002else\u0002 built successfully (Your typical average Spike commit, breaking Lunix as usual)\" >> $TEMPFILE\r\n\telse\r\n\t\techo \" Everything \u0002else\u0002 built successfully\" >> $TEMPFILE\r\n\tfi\r\nfi\r\n\r\ntr \"\\n\" \",\" <$TEMPFILE | sed \"s/,$/\\./\" >$TEMPFILE2\r\necho -e '\\n' >> $TEMPFILE2\r\ncat $TEMPFILE2\r\n"
  },
  {
    "path": "specs/compile farm scripts/linux/count_wip.sh",
    "content": "#!/bin/bash\nTEMPFILE=/tmp/moodlescountjunk.txt\nTEMPFILE2=/tmp/moodlescountjunk2.txt\nAUTHOR=`cat /home/moodles/wip/wip/engine/svninfo.txt | grep 'Last Changed Author' | sed s/'Last Changed Author: '//`\nif [ -f $TEMPFILE ];\nthen\n\trm $TEMPFILE\nfi\nif [ -f $TEMPFILE2 ];\nthen\n\trm $TEMPFILE2\nfi\n\n# count folder contents\nLINUX32=$(ls -1 /home/moodles/htdocs/unstable/linux_32bit/ | wc -l)\nLINUX64=$(ls -1 /home/moodles/htdocs/unstable/linux_64bit/ | wc -l)\nWIN32=$(ls -1 /home/moodles/htdocs/unstable/win32/ | wc -l)\nMORPHOS=$(ls -1 /home/moodles/htdocs/unstable/morphos/ | wc -l)\nMACOSX=$(ls -1 /home/moodles/htdocs/unstable/macosx_tiger_10.4/ | wc -l)\n\n# known count when all binaries build\nLIN32TOTAL=24 # 4 compilers (GCC, ICC, CLANG & LLVM) * 3 targets (sv, mingl, gl) * 2 (SDL versions)\nLIN64TOTAL=6 # 2 compilers (GCC, CLANG) * 3 targets (sv, mingl, gl)\nWIN32TOTAL=6 # 1 compiler (MinGW32) * 3 targets (sv, mingl, gl) * 2 (SDL versions)\nMORPHTOTAL=1 # 1 compiler (GCC based by bigfoot) * 2 targets (gl, mingl)\nMACOSTOTAL=4 # 2 compilers (10.4 x86 and ppc) * 3 targets (gl, mingl, sv)\n\n# subtract known directory count from realtime counted directory count to find total of failed builds\nL32NUM=`expr $LIN32TOTAL - $LINUX32`\nL64NUM=`expr $LIN64TOTAL - $LINUX64`\nW32NUM=`expr $WIN32TOTAL - $WIN32`\nMORNUM=`expr $MORPHTOTAL - $MORPHOS`\nMACNUM=`expr $MACOSTOTAL - $MACOSX`\n\nif [ $L32NUM -ne 0 ];\nthen\n\techo \"$L32NUM/$LIN32TOTAL Lin32 failed\" >> $TEMPFILE \nfi\nif [ $L64NUM -ne 0 ];\nthen\n        echo \" $L64NUM/$LIN64TOTAL Lin64 failed\" >> $TEMPFILE\nfi\nif [ $W32NUM -ne 0 ];\nthen\n        echo \" $W32NUM/$WIN32TOTAL Win32 failed\" >> $TEMPFILE\nfi\nif [ $MORNUM -ne 0 ];\nthen\n        echo \" $MORNUM/$MORPHTOTAL MorphOS failed\" >> $TEMPFILE\nfi\nif [ $MACNUM -ne 0 ];\nthen\n        echo \" $MACNUM/$MACOSTOTAL MacOSX 10.4 failed\" >> $TEMPFILE\nfi\nif [ $L32NUM -eq 0 ] && [ $L64NUM -eq 0 ] && [ $W32NUM -eq 0 ] && [ $MORNUM -eq 0 ] && [ $MACNUM -eq 0 ];\nthen\n\techo \"Everything built successfully\" >> $TEMPFILE\n\nelif [ $LIN32TOTAL -eq 0 ] && [ $LIN64TOTAL -eq 0 ] && [ $WIN32TOTAL -eq 0 ] && [ $MORPHTOTAL -eq 0 ] && [ $MACOSTOTAL -eq 0 ];\nthen\n        echo \" Nothing built at all!\" >> $TEMPFILE\nelif [ $L32NUM -ne 0 ] && [ $L64NUM -ne 0 ] && [ $W32NUM -ne 0 ] && [ $MORNUM -ne 0 ] && [ $MACNUM -ne 0 ];\nthen\n        if [ $AUTHOR == \"acceptthis\" ];\n        then\n\t\techo \" At least 1 target from each OS failed to build, on the bright side, at least everything didn't compile (Your typical average Spike commit, breaking Lunix as usual)\" >> $TEMPFILE\n\telse\n               echo \" At least 1 target from each OS failed to build, on the bright side, at least everythinng didn't compile\" >> $TEMPFILE\n\tfi\t\nelse\n\tif [ $AUTHOR == \"acceptthis\" ];\n\tthen\n\t\techo \" Everything \u0002else\u0002 built successfully (Your typical average Spike commit, breaking Lunix as usual)\" >> $TEMPFILE\n\telse\n\t\techo \" Everything \u0002else\u0002 built successfully\" >> $TEMPFILE\n\tfi\nfi\n\ntr \"\\n\" \",\" <$TEMPFILE | sed \"s/,$/\\./\" >$TEMPFILE2\necho -e '\\n' >> $TEMPFILE2\ncat $TEMPFILE2\n"
  },
  {
    "path": "specs/compile farm scripts/linux/svninfo.sh",
    "content": "TEMP1=/tmp/moodlesjunk1.txt\r\nTEMP2=/tmp/moodlesjunk2.txt\r\nTIMETAKENTF=/tmp/timetaken.txt\r\nif [ -f $TEMP1 ];\r\nthen\r\n\trm $TEMP1\r\nfi\r\nif [ -f $TEMP2 ];\r\nthen\r\n\trm $TEMP2\r\nfi\r\n\r\ncd /home/moodles/fteqw/engine\r\nsvn info > svninfo.txt\r\necho \"\u0002SVN updated by\u0002:\" >> $TEMP1\r\nAUTHOR=`cat svninfo.txt | grep 'Last Changed Author' | sed s/'Last Changed Author: '//`\r\necho $AUTHOR >> $TEMP1\r\nif [ $AUTHOR == \"acceptthis\" ];\r\nthen\r\n\techo \"(AKA Spike)\" >> $TEMP1\r\nelif [ $AUTHOR == \"isthisinuse\" ];\r\nthen\r\n\techo \"(AKA bigfoot)\" >> $TEMP1\r\nfi\r\necho \"*\" >> $TEMP1\r\ncat svninfo.txt | grep 'Revision' | sed s/'Revision'/'\u0002Revision\u0002'/ >> $TEMP1\r\necho \"(Build #\"`~/buildnumber.sh`\")\" >> $TEMP1\r\necho \"* \u0002New binaries\u0002 @ http://nightly.fteqw.com\" >> $TEMP1\r\ncat /tmp/timetaken.txt >> $TEMP1\r\ntr \"\\n\" \" \" <$TEMP1 >$TEMP2\r\necho -e '\\n' >> $TEMP2\r\ncat $TEMP2\r\n"
  },
  {
    "path": "specs/compile farm scripts/linux/svninfo_wip.sh",
    "content": "TEMP1=/tmp/moodlesjunk1.txt\r\nTEMP2=/tmp/moodlesjunk2.txt\r\nTIMETAKENTF=/tmp/timetaken.txt\r\nif [ -f $TEMP1 ];\r\nthen\r\n\trm $TEMP1\r\nfi\r\nif [ -f $TEMP2 ];\r\nthen\r\n\trm $TEMP2\r\nfi\r\n\r\ncd /home/moodles/wip/wip/engine\r\nsvn info > svninfo.txt\r\necho \"\u0002\u00034Unstable WIP\u0003 SVN branch updated by\u0002:\" >> $TEMP1\r\nAUTHOR=`cat svninfo.txt | grep 'Last Changed Author' | sed s/'Last Changed Author: '//`\r\necho $AUTHOR >> $TEMP1\r\nif [ $AUTHOR == \"acceptthis\" ];\r\nthen\r\n\techo \"(AKA Spike)\" >> $TEMP1\r\nelif [ $AUTHOR == \"isthisinuse\" ];\r\nthen\r\n\techo \"(AKA bigfoot)\" >> $TEMP1\r\nfi\r\necho \"*\" >> $TEMP1\r\ncat svninfo.txt | grep 'Revision' | sed s/'Revision'/'\u0002Revision\u0002'/ >> $TEMP1\r\necho \"(Build #\"`~/buildnumber.sh`\")\" >> $TEMP1\r\necho \"* \u0002New binaries\u0002 @ http://www.triptohell.info/moodles/unstable/\" >> $TEMP1\r\ncat /tmp/timetaken.txt >> $TEMP1\r\ntr \"\\n\" \" \" <$TEMP1 >$TEMP2\r\necho -e '\\n' >> $TEMP2\r\ncat $TEMP2\r\n"
  },
  {
    "path": "specs/compile farm scripts/macosx/build.sh",
    "content": "rm version.txt\nrm macosx_fteqw*\nrm *.zip\n\nsvn update\n\nsvn info >> version.txt\n\nREV=`cat version.txt | grep 'Last Changed Rev:' | sed s/'Last Changed Rev: '//`\nDATE=`date +%B-%d-%Y`\n\nmake clean\n\nmake FTE_TARGET=macosx gl-rel mingl-rel sv-rel\n\ncd release\n\nmv macosx_fteqw.mingl ../macosx_fteqw.mingl_x86\nmv macosx_fteqw.gl ../macosx_fteqw.gl_x86\nmv macosx_fteqw.sv ../macosx_fteqw.sv_x86\n\ncd ..\n\nmake clean\n\nmv Makefile Makefile.backup\nmv GoodMakefile Makefile\n\nmake FTE_TARGET=macosx gl-rel mingl-rel sv-rel CFLAGS=\"-arch ppc -L/Users/pcwiz/ppc-lib/lib/ -I/Users/pcwiz/ppc-lib/include/\"\n\nmv Makefile GoodMakefile\nmv Makefile.backup Makefile\n\ncd release\n\nmv macosx_fteqw.mingl ../macosx_fteqw.mingl_ppc\nmv macosx_fteqw.gl ../macosx_fteqw.gl_ppc\nmv macosx_fteqw.sv ../macosx_fteqw.sv_ppc\n\ncd ..\n\nlipo -create macosx_fteqw.gl_x86 macosx_fteqw.gl_ppc -output macosx_fteqw.gl\nlipo -create macosx_fteqw.mingl_x86 macosx_fteqw.mingl_ppc -output macosx_fteqw.mingl\nlipo -create macosx_fteqw.sv_x86 macosx_fteqw.sv_ppc -output macosx_fteqw.sv\n\nzip -9 -v \"macosx_Leopard_10.5_-fteqwgl(r$REV $DATE).zip\" macosx_fteqw.gl version.txt LICENSE\nzip -9 -v \"macosx_Leopard_10.5_-fteqwmingl(r$REV $DATE).zip\" macosx_fteqw.mingl version.txt LICENSE\nzip -9 -v \"macosx_Leopard_10.5_-fteqwsv(r$REV $DATE).zip\" macosx_fteqw.sv version.txt LICENSE\n"
  },
  {
    "path": "specs/console.txt",
    "content": "Autocompletion:\r\n\tFTE will not randomly complete commands. The ctrl+space key combo is directly equivelent to tab in vanilla engines.\r\n\tSome commands/cvars have descriptions. These will be shown if the command is entered.\r\n\r\nChat:\r\n\tLines typed in may be interpreted as either chat or commands, depending on the value of cl_chatmode.\r\n\tLines starting with a / are always commands.\r\n\tLines where the first word is automatically coloured yellow will be executed as commands.\r\n\tLines where the first word is automatically coloured white will be broadcast to other players as chat.\r\n\tTab completion will always insert a leading / to designate the line as a command rather than chat.\r\n\r\n\tcl_chatmode 0 - Acts like NQ where the console ONLY accepts commands.\r\n\tcl_chatmode 1 - Acts like Q3 where the input is assumed to be chat. Commands require a leading / (which is also inserted by tab, by the way).\r\n\tcl_chatmode 2 - Acts like QW where the input is assumed to be chat if its not a valid command.\r\n\r\nKeys:\r\n\ttab: attempt to complete the command. If there's multiple, will complete only as far as the commonality goes.\r\n\tctrl+space: completes the current line using the highlighted suggestion\r\n\tctrl+tab: cycles consoles.\r\n\tmouse1+drag: scroll console up/down.\r\n\tmouse1+click: 'use' link underneath the cursor.\r\n\tshift+mouse1: insert word/link/ip under the cursor to the input line.\r\n\tmouse2: select+copy text to the clipboard.\r\n\tctrl+c/ctrl+ins: copy the entire input line to the clipboard.\r\n\tctrl+v/shift+ins: insert the clipboard contents onto the input line. newlines are replaced with semi-colons.\r\n\tleft/right: move cursor left/right.\r\n\tdel: remove input-line character under the cursor. if at the end of line removes just before the cursor.\r\n\tbackspace: removes char to the left of the cursor.\r\n\tup/down: scroll through command history.\r\n\tpgup/pgdn/mwheel: scroll console up/down.\r\n\thome: go to top of console (oldest).\r\n\tend: go to bottom of console (most recent).\r\n\talt: make text red\r\n\tctrl+: 0123456789[]gryb(=)a<->,.BC ctrl with one of the prior chars give various special chars. yellow numbers, LEDs, separators.\r\n\r\nLinks:\r\n\tBasic format:\r\n\t\t^[^2VISIBLE TEXT\\key1\\value1\\key2\\value2^]\r\n\t\tColour markup for the link must be contained within, and will be reset at the end of it.\r\n\r\n\tCSQC:\r\n\t\tIf CSQC is running at the time, clicking on a link will result in this call:\r\n\t\t\tfloat(string linktext, string linkinfo) CSQC_ConsoleLink;\r\n\t\tIf the CSQC returns false, the engine will examine the link for default fields, so if you handle it yourself you must return true to prevent both csqc and engine trying to do the same thing.\r\n\t\tThe 'linktext' field is the visible text from the link (ie: what the user saw). However, any colour modifiers may have been converted/mangled.\r\n\t\tThe 'linkinfo' is the first \\ and everything up to the closing ^]. Again, colours should be avoided.\r\n\t\tFor easy parsing/generation of the linkinfo field, you can use the infoget/infoadd builtins from the FTE_STRINGS extension.\r\n\t\tNote that links can be sent from other players also. The behaviour of the link is also hidden from the user.\r\n\t\tThus you should either require the user to confirm the action, or provide links that are merely informative.\r\n\t\tThe builtin field names are somewhat generic. You can use them as a fallback for if the player disconnects.\r\n\t\t\r\n\tBuilt in link fields:\r\n\t\tplayer: the link is associated with a player slot. The value should be entnum(playerentity)-1.\r\n\t\t\taction: if this field is also set, then when clicked the engine shall carry out the action named in the value upon that player slot.\r\n\t\t\t\tkick: kicks the player (requires rcon).\r\n\t\t\t\tban: bans the player's ip (requires rcon). \r\n\t\t\t\tmute: mutes the player's voicechat feature.\r\n\t\t\t\tignore: any 'say' commands received from the player are ignored. also mutes.\r\n\t\tdesc: displays the value as text. The use of this field is somewhat limited by length.\r\n\t\tconnect: the client will attempt to 'connect $value', so the value must be an acceptable hostname or ip:port. You should probably always include the port.\r\n\t\tqtv: the client will attempt to 'qtvplay $value', so the value must be an acceptable qtv stream identifier.\r\n\t\tdemo: the client will attempt to 'playdemo $value', so this value must include any subdirectory for the named demo.\r\n\t\tcmd: the client will attempt to 'cmd $value'. A mod can see the results of this using KRIMZON_SV_PARSECLIENTCOMMAND.\r\n\t\timpulse: the client will attempt to 'impulse $value'. A mod can thus snoop on this by checking self.impulse on the player. Must naturally be between 0 and 255.\r\n\t\tleading forward-slash: Links with no info and a leading forward slash will be inserted into the input line when clicked. The user must still press enter to issue the given command. Use semi-colons if you wish to include multiple cvars/commands in a single link.\r\n\t\tThis list is subject to change.\r\n\t\t"
  },
  {
    "path": "specs/csqc_for_idiots.txt",
    "content": "This little document is a little primer on CSQC for people that have no understanding of it.\r\nThe reader is expected to have an understanding of SSQC basics (entities, globals, functions, prototypes, fields, etc).\r\nAs I am the primary author of FTEQW+FTEQCC, I am biased towards it and its feature set. I will attempt to include sidenotes where my instructions might not match other engines (namely: DarkPlaces), but the code examples given are intended to run in either engine.\r\n\r\n\r\n\r\n\r\nMotivation for CSQC:\r\nQuake's archetecture is that of client and server. Even in a single player game, your engine is running both a client and server simultaneously (for example, demos are simply a capture of all the server->client packets).\r\nThis archetecture brings with it the ability for new clients to connect mid-game, without having to have run the game right from the start of the map.\r\nAnd this is the distinction between SSQC and CSQC.\r\nTraditional QC code is compiled into a single progs.dat file which runs purely on the server. It controls where, when, and how things move from one place to another, and the server will automatically 'replicate' a subset of the current state of the game to each connected client each network frame, and that is that subset which the client draws (for terminology, Quake3 refers to these gamestate copies as 'snapshots', which is a good way to think of it).\r\nSSQC, running on the server, has absolutely no control over the client beyond what it can send it. Primarily this includes snapshots, stuffcmds, stats, and 'temporary entities'. But worse than this, SSQC is commonly running on a remote machine, with latency, packetloss, and other networking limitations.\r\n\r\nCSQC provides a way around these issues.\r\nA) CSQC has full control over the screen. It can draw the game where it wants. It can draw a hud how it wants.\r\nB) CSQC is running locally. It has no latency issues at all. Mouse movement is present directly within the frame that follows, rather than needing to bounce off the server.\r\nC) CSQC can perform its own player prediction, allowing mods to freely provide their own player physics without breaking client expectations.\r\nD) CSQC can interact with input devices like keyboard and mice, providing rich user interfaces.\r\n\r\nCSQC's down sides.\r\nA) CSQC is separate and distinct from SSQC. The two modules share no data (but may still share significant parts of code). You will need to learn how to get data from ssqc to csqc and vice versa.\r\nB) As a large after-gpl feature, different engines tend to have their own interpretation of the CSQC spec. As a result, cross-engine compatibility may be hard, but is not impossible.\r\nC) CSQC's versatility can be a curse. The engine tries to avoid doing too many things automatically, most noticably in regards to the screen - a CSQC mod with functions but no code results in nothing being drawn to the screen. Mods need a small bootstrap chunk of code to retain feature compatibility.\r\nD) CSQC attempts to clean up the API slightly, which can result in certain differences between SSQC and CSQC.\r\nE) Compared to SSQC, CSQC is often written in the context of a single client. It has a greater focus on callbacks, globals, and builtins to retrieve data from snapshots and other hiding places within the engine.\r\nF) CSQC needs a certain amount of cheat protection. Thus there are limitations in place to ensure that the csprogs matches one available on the server (the original versions had other additional limitations!).\r\nG) CSQC is running on the client, and is typically autodownloaded. The client must thus also place certain additional restrictions on console commands and cvars, just as it would via stuffcmds.\r\n\r\nIn essence, csqc sits somewhere between the middle of the client's renderer, networking, and input modules. Basically, the use input is filtered via csqc before going to the client/networking. Snapshots are still parsed by the engine's networking, but the renderer will not draw them without the csqc saying to do so.\r\n\r\n\r\n\r\n\r\nSetting up a basic CSQC mod and compiling:\r\nCSQC is compiled just like SSQC. Both start from a .src file, but typically a different one.\r\nWhile you can use a progs.src inside a different directory, I will write this with the expectation that your CSQC's .src file can be found as eg: c:\\quake\\mymod\\src\\csprogs.src\r\nWhere SSQC outputs to a 'progs.dat' (or 'qwprogs.dat'), CSQC outputs to a 'csprogs.dat' file. This is forms the first line of your csprogs.src file. Typically with a ..\\ prefix so that the qcc writes it to the right directory...\r\nThe second line of your csprogs.src should be to specify some sort of 'defs.qc' equivelent for CSQC. Sadly, CSQC has different system+field+builtin defs from SSQC and thus needs a different file.\r\nWith FTEQW, you can get the engine to generate such a file for you by issuing the command 'pr_dumpplatform -O csdefs -Tcs' at FTEQW's console (be warned that it includes FTEQCC extensions which will not work in other qccs). You'll want to copy this file to your src directory (instead of it being in your home dir where the qcc can't see it - the given command will name the system path that you'll need to copy it from). If you're exclusively targetting DP, you may wish to get DP-specific defs from other sources, but be warned that certain builtins may have different names, and you will need to work around that. For the things documented here, using an FTEQW-generated csdefs.qc should work okay with current DP versions.\r\nAnd the third line of your csprogs.src file should name one of your own code files (which you will need to write).\r\n\r\nSo, inside c:\\quake\\mymod\\src\\csprogs.src we have these lines:\r\n../csprogs.dat  //the name of the file to \r\ncsdefs.qc       //system defs to match the engine, including various extensions\r\nview.qc         //name it whatever you want. This is where you will be writing code.\r\n\r\nTo make things easy (assuming you're using FTEQCC), you can add the following line to your defs.qc\r\n#pragma sourcefile csprogs.src\r\nThis line will cause fteqcc to automatically start compiling your csprogs.dat after it compiles your progs.dat. Its one of those convienience features.\r\nNote that this will not affect your actual progs.dat file (beyond compiling it).\r\nAlternatively you can rename your progs.src to ssprogs.src, and make a new progs.src that contains two pragmas that name your two src files, then you can freely choose just ssqc, just csqc, or both.\r\nOr you can instead use separate batch files and the -srcfile argument to distinguish between which src file to use.\r\nCreate an empty view.qc file and try compiling. You should get a csprogs.dat from it (side note: if you used DP-specific defs instead, you'll get errors about functions not being defined, you'll need to make stubs for these at some point, typically they'll need to return false).\r\n\r\n\r\n\r\n\r\nOur first Code (the 3d game view):\r\nopen up your empty view.qc file and add this function:\r\nvoid(float width, float height, float menushown) CSQC_UpdateView =\r\n{\r\n\tdrawfill('0 0 0', [width, height, 0], '1 1 0', 1, 0);\r\n\tdrawstring('0 0 0', \"Hello, World\", '8 8 0', '0 0 1', 1, 0);\r\n};\r\nIf you save+compile+run, you will now find that your fledgeling mod has had its first effect... The game view has disappeared, to be replaced by a yellow screen! oh noes! By adding that function, the engine now knows you want to take explicit control of the screen, and it is no longer doing that automatically for you. So we need to at least get back to what we had. This will need some explaination. If you're just aiming for a hud and nothing else, you can grab the example code at the end of this section and otherwise skip this section.\r\n\r\nContrary to 2d drawing, 3d drawing in csqc is based around building up an entire scene then telling the renderer to do its stuff in one go. This model is somewhat derived from the way Quake3 works, and allows the engine to provide its own shadow, rtlight, etc effects within the gameview as a whole. 3d rendering is thus based upon the concept of an active scene or view (with its set of view properties), as well as a list of the entities present in that scene.\r\nDue to certain expectations with input, the scene is NOT normally cleared automatically by the engine. Even if it was, certain default properties change over frames anyway (like the player position). Thus the First thing we need to do is to clear the scene with:\r\nclearscene();\r\nWell, okay, that function name was a little obvious. Remember I said that the scene had various 'view properties'? Well, clearscene just set them to their default values for you. However, the default is for the engine to not draw a hud at all. So if only as an example of how to reconfigure scene properties, you'll need the following line too:\r\nsetviewprop(VF_DRAWENGINESBAR, 1);\r\nLets throw in a crosshair too:\r\nsetviewprop(VF_DRAWCROSSHAIR, 1);\r\nFor reference:\r\n//setviewprop(VF_MIN, '0 0 0');\r\n//setviewprop(VF_SIZE, [width, height, 0]);\r\nWill allow you to move the 3d view around on the screen. The min+size values should be in virtual positions, but DP currently uses physical pixels (sidenote: alternative coordinate systems SUCK!)\r\nRight, so now our scene includes a crosshair, a status bar (aka: hud). The view position and angles are in their default positions (ie: angle pulled from client state, origin pulled from prediction or lerped from snapshots).\r\nIts still not being drawn though. We've only said where it should be. We've not actually said to draw it.\r\nSo lets do that for the luls:\r\nrenderscene();\r\nUrr, yeah, another obvious name there from Mr Imaginative.\r\nIf you compile and run, what do we get? Try it!\r\nYou didn't try it did you. You just read the next line without bothering... Well, if you HAD run it, you would have seen that we now get a fullscreen view of the game, with an sbar and the world. We can run around as normal... But there's something missing, and that, my friend, is entities.\r\nrenderscene will not automatically hunt for entities for you. The easiest way to achieve this is with the following line:\r\naddentities((intermission?0:MASK_VIEWMODEL)|MASK_ENGINE);\r\nOf course, you'll want to ensure that the call is between clearscene and renderscene, or they'll just be wiped at the start of the next frame.\r\nThis builtin reaches into the networking snapshot code and pulls out all the entities that match the masks. MASK_ENGINE refers to the snapshot entities. MASK_VIEWMODEL refers to the entity that is normally generated from your ssqc player's .weaponmodel and .weaponfame fields (which you generally don't want to appear during intermission).\r\nIf you have spawned some entities in csqc, you can call addentity(yourent) to add a copy of it the scene also. This is useful for one-only entities like custom view models being predicted in csqc.\r\nBut for csqc entities, the easiest way to add them to the scene is to set your entity's .drawmask to something evil like MASK_ENGINE, and then addentities will automatically add it to the scene for you. More on this later.\r\n\r\nOne thing to note about the following code is that getviewprop also exists, if you wish to read a default like VF_ORIGIN. For instance, you can make the view bob randomly by reading its current position, adding sin(time*period)*magnitude to the z coord.\r\n\r\nSo, to compact that into something small for the lazy people who skipped most of this section. I'll add a few other no-op values commented out as the value given is the default(ish) already.\r\nvoid(float width, float height, float menushown) CSQC_UpdateView =\r\n{\r\n\tclearscene();\t\t\t\t\t//wipe entity lists. reset view properties to their defaults.\r\n\tsetviewprop(VF_DRAWENGINESBAR, 1);\t\t//draw a status bar (or hud or whatever the engine normally does) around the screen.\r\n\tsetviewprop(VF_DRAWCROSSHAIR, 1);\t\t//draw a crosshair in the middle of the screen.\r\n\t//setviewprop(VF_ORIGIN, '0 0 0');\t\t//view position of the scene (after view_ofs effects).\r\n\t//setviewprop(VF_ANGLES, '0 0 0');\t\t//override the view angles. input will work as normal. other players will see your player as normal. your screen will just be pointing a different direction.\r\n\t//setviewprop(VF_DRAWWORLD, 1);\t\t\t//whether the world entity should be drawn. set to 0 if you want a completely empty scene.\r\n\t//setviewprop(VF_MIN, '0 0 0');\t\t\t//top-left coord (x,y) of the scene viewport in virtual pixels (or annoying physical pixels in dp).\r\n\t//setviewprop(VF_SIZE, [width, height, 0]);\t//virtual size (width,height) of the scene viewport in virtual pixels (or annoying physical pixels in dp).\r\n\t//setviewprop(VF_AFOV, cvar(\"fov\"));\t\t//note: fov_x and fov_y control individual axis. afov is general\r\n\t//setviewprop(VF_PERSPECTIVE, 1);\t\t//1 means like quake and other 3d games. 0 means isometric.\r\n\taddentities((intermission?0:MASK_VIEWMODEL)|MASK_ENGINE);\t//add various entities to the scene's lists.\r\n\trenderscene();\t\t\t\t\t//draw the scene to the screen using the various properties.\r\n};\r\n\r\n\r\n\r\n\r\nOur first hud (2d stuff and stats):\r\nUnlike 3d stuff which uses some huge complex scene concept, 2d stuff is immediate only. All 2d drawing calls are immediately sent to the renderer in the exact order they are given, and can thus overlap without issues. Later calls will always overwrite whatever is already on the screen at the position specified.\r\nFirst of all, add a call at the bottom of your CSQC_UpdateView function to some Hud_Draw function:\r\nHud_Draw([width, height]);\r\nAnd then include the following code just before your CSQC_UpdateView function (or in a different file ideally, just be sure to prototype etc like you would in ssqc):\r\nfloat stitems, stitems2, stweapon;\r\nvoid Hud_Draw(vector scrsz)\r\n{\r\n\tvector pos = [(scrsz_x-320)/2, pos_y = scrsz_y - 24, 0];\t//calculate the top-left of the sbar, assuming it is 320 units wide and placed in the bottom-middle of the screen\r\n\t//get some commonly refered to values\r\n\tstitems = getstatbits(STAT_ITEMS, 0, 23);\t\t\t//this is the player's self.items value (STAT_ITEMS is generated specially by the server)\r\n\tstitems2 = getstatbits(STAT_ITEMS, 23, 9);\t\t\t//this is the player's self.items2 value if it exists, otherwise it is the serverflags global\r\n\tstweapon = getstatf(STAT_ACTIVEWEAPON);\t\t\t\t//this is the player's self.weapon value.\r\n\r\n\tdrawpic(pos, \"sbar\", '320 24 0', '1 1 1', 0.5, 0);\t\t//draw the sbar background image slightly transparently\r\n\r\n\tHud_DrawLargeValue(pos+'24 0 0', getstatf(STAT_ARMOR), 25);\r\n\tHud_DrawLargeValue(pos+'136 0 0', getstatf(STAT_HEALTH), 25);\r\n\tHud_DrawLargeValue(pos+'248 0 0', getstatf(STAT_AMMO), 10);\r\n};\r\n\r\nAnd here's the helper function I used which wasn't defined above.\r\nvoid(vector pos, float value, float threshhold) Hud_DrawLargeValue =\r\n{\r\n\tfloat c;\r\n\tfloat len;\r\n\tstring s;\r\n\tif (value < 0)\r\n\t\tvalue = 0;\t//hrm\r\n\tif (value>999)\r\n\t\tvalue = 999;\r\n\ts = ftos(floor(value));\r\n\tlen = strlen(s);\r\n\tpos_x += 24 * (3-len);\r\n\tif (value <= threshhold)\r\n\t{\t//use alternate (red) numbers\r\n\r\n\t\twhile(len>0)\r\n\t\t{\r\n\t\t\tlen-=1;\r\n\t\t\tc = str2chr(s, len);\r\n\t\t\tdrawpic(pos+len * '24 0 0', sprintf(\"anum_%g\", c-'0'), '24 24 0', '1 1 1', 1, 0);\r\n\t\t}\r\n\t}\r\n\telse\r\n\t{\t//use normal numbers\r\n\r\n\t\twhile(len>0)\r\n\t\t{\r\n\t\t\tlen-=1;\r\n\t\t\tc = str2chr(s, len);\r\n\t\t\tdrawpic(pos+len * '24 0 0', sprintf(\"num_%g\", c-'0'), '24 24 0', '1 1 1', 1, 0);\r\n\t\t}\r\n\t}\r\n};\r\n\r\nOkay, try running that.\r\nNow you get your basic sbar appearing, with health and armor values, that goes red if the values are low.\r\nNote that I'm not going to give any more hud code. Go write your own! Here's one I wrote earlier: http://sourceforge.net/p/fteqw/code/HEAD/tree/trunk/quakec/csqctest/src/cs/hud.qc?format=raw\r\n\r\nThere's a few generic builtins there which you may have never seen before. sprintf: generates a formatted temp-string (%g, %f are replaced with a float argument, %s uses a string argument, %v uses a vector argument, most other things can use %i for debugging), str2chr(x,y): reads the character at position Y from string X. These are both available in ssqc as parts of various extensions and are really quite useful.\r\nHere are the builtins for 2d stuff:\r\nvector(string picname) drawgetimagesize = #318;\t\t\t\t\t\t\t\t\t\t//retrieves the size of a picture.\r\nfloat(vector position, float character, vector size, vector rgb, float alpha, optional float drawflag) drawcharacter = #320;//draws a single character.\r\nfloat(vector position, string text, vector size, vector rgb, float alpha, optional float drawflag) drawrawstring = #321;//draws a string without stopping for colour codes\r\nfloat(vector position, string text, vector size, vector rgb, float alpha, float drawflag) drawstring = #326;\t\t//supports colour codes\r\nfloat(vector position, string pic, vector size, vector rgb, float alpha, optional float drawflag) drawpic = #322;\t//draw a picture on the screen. gfx/foo.lmp, or no extra path+extension for wad images.\r\nfloat(vector position, vector size, vector rgb, float alpha, optional float drawflag) drawfill = #323;\t\t\t//draws a simple box\r\nfloat(string text, float usecolours, optional vector fontsize) stringwidth = #327;\t\t\t\t\t//gets the virtual width of the string, so you can size things properly.\r\nvoid(vector pos, vector sz, string pic, vector srcpos, vector srcsz, vector rgb, float alpha, optional float drawflag) drawsubpic = #328;//draws a subregion of a picture.\r\nAll of those builtins can be used to draw various 2d stuff. Using different fonts is an extra extension that I'm too lazy to explain here.\r\nBut there's also getstatf and getstatbits that need some explaining. To do so, I'll need to explain the network protocol a little.\r\n\r\nQuake's networking protocol is more than just entity snapshots. It also includes a concept of stats. In NQ, different stats are transfered to the client with various limitations, while in QW they're generally more generic. Generic aproaches are good, so all stats are exposed via the same sort of interface similar to the one that the engine uses.\r\nBasically, all the various player-specific fields form the various bits of your hud are various 'stats' (technical term rather than general term).\r\nThe first 32 stats are reserved by the engine for one reason or another, either for future expansion or because its just part of the protocol that has always existed (they are typically some representation of various player fields and are safe to read - later ones may depend upon server extensions). Stats between 32 and 127 inclusive are 'free' stats reserved for mod-specific purposes by QC.\r\ngetstatf reads the numeric stat into a float.\r\ngetstati reads the numeric stat into an integer (as most qc code doesn't support ints, you would normally think it useless).\r\ngetstats reads a string stat, returning a tempstring.\r\ngetstatbits is a bit more awkward, and is provided only for legacy compatibility with STAT_ITEMS. This needs more explaining... STAT_ITEMS, as you will see in the Hud_Draw function I gave you, is read twice. Why read it twice, you might ask... Because STAT_ITEMS is an integer bitfield over the network. Worse - the upper bits of this integer (which cannot normally be used in floats due to precision) is packed with info on runes currently held! So, in order to read both parts of this stat, you need a special builtin which can decode the various integer bits into a float which qc can actually use. The positions and bitcounts should generally be useable for any mod, although you might need to handle runes being a little shifted if your mod does not make use of items2.\r\nSo that's how you read a stat, but you'll still need to know how to create your own stats for your awesome hud, and that requires some ssqc (which is where the info is).\r\n\r\nThe main way to add a stat is with this builtin:\r\nvoid(float num, float type, .__variant fld) clientstat = #232;\r\nWhich is typically invoked from worldspawn using something like:\r\nclientstat(STAT_MYSUPERSTAT, EV_FLOAT, mysuperstat);\r\nwhere mysuperstat was defined in defs.qc or whereever as:\r\n.float mysuperstat;\r\nAnd, as noted above, is read with getstatf(STAT_MYSUPERSTAT)\r\nYou'll want to share the STAT_FOO defines between ssqc+csqc.\r\n\r\n.int foo; clientstat(STAT_FOO, EV_INTEGER, foo); int myfoo = getstati(STAT_FOO); works too, if your qcc+engine support integers.\r\n.string foobar; clientstat(STAT_FOOBAR, EV_STRING, foobar); string myfoobar = getstats(STAT_FOOBAR); works as well. Hurrah.\r\n.entity enemy; clientstat(STAT_ENEMYNUM, EV_INTEGER, foo); float myenemynum = getstatf(STAT_ENEMYNUM); works too, if your qcc+engine support integers.\r\nHowever, vectors are not normally supported as stats. You will need to separate the various componants (ie: myvector_x) and send+read as 3 separate floats.\r\nEntities are sent only as numbers _in the ssqc_. The numbers don't always match up, thus findfloat(world, entnum, getstatf(STAT_ENTNUM)) generally needs to be used if its to refer to some csqc entity. Note that it may still evaluate to world, as entities may not be in the pvs or may not even be visible to csqc. Stats and snapshots may all have different latency and arrive at different times due to packetloss or whatever.\r\nAs noted above, those custom STAT_FOO values should normally be between 32 and 127 for futureproofing. You can generally go higher too, but its not guarenteed.\r\n\r\nSidenote: DP compat with stats is more awkward. Unlike FTEQW, DP still has exclusively integer stats. numeric stats are specifically integer stats thus float stats are thus rounded towards 0. strings on the other hand are limited to 15 chars, and actually consume 4 consecutive stats. If you actually need floating point precision with DP, you can tell the engine that the stat is actually an integer, using ev_integer and getstati. Doing so will avoid truncation and has resulted in some enterprising dp users renaming getstati to getstatf just to confuse everyone, so be careful of that if you're using dp-specific defs...\r\n\r\n\r\n\r\n\r\nUser Input (guis):\r\nUser input (keyboard and mice and stuff) can be intercepted using the CSQC_InputEvent function.\r\nThis function is a generic entry point for multiple different sorts of events. The type of event is passed in evtype. The later arguments have different meanings depending on the event type.\r\nEvent handling is basically implemented via interception. That is, if you want to do something with the event you can do so, and you can then 'cancel' the event in the engine's eyes by returning TRUE.\r\nSo, if you did something and now want the engine to ignore it, return TRUE. If you want the engine to handle the event for you, return FALSE.\r\nNote that not all events can be meaningfully canceled, and other events should not be canceled.\r\nFor example, if the user presses a key, the key has been pressed, the unicode meaning of that key combination is still determined (normally by the operating system), and you will still receive a release event when the user releases the key, but by cancelling the event, you can get the engine to avoid doing any key binding logic. Of course, key UP events should generally never be canceled as a general rule. The reasoning for this is that if you are cancelling up events, you must be very careful in tracking down events and the owners of those down events, because if you cancel a down event and NOT an up event, you can end up with the engine's key binding system thinking a key is still pressed and thus with the player running forwards endlessly.\r\nSo:\r\nKey down events:\r\n\tscanx: this is the quake KEY_ code value associated with the physical hardware button. May be set to 0. You'll always get the same code for each key regardless of whether shift etc are pressed also.\r\n\tchary: this is the unicode code point of the key. May be set to 0. You'll get different values from the same key depending on whether shift etc is pressed.\r\n\tdevid: for mouse buttons, this is a mouse-device/touch-event id (see mouse absolute events for details). for keyboard buttons, this is a keyboard device id.\r\n\tcancel to avoid bindings (like disabling weapon switches or +forward etc).\r\n\tEither scanx or chary may be set to 0, but not both at the same time.\r\n\tSome windowing systems support scanx and chary both being set in a single call (like scanx==KEY_1 and chary=='!'), or may invoke the function twice (ie: scanx==KEY_1 and chary==0, and scanx==0 and chary=='!').\r\n\tThis is significant when it comes to dead keys and unicode input in certain locales.\r\n\tThus if you are typing, you should use ONLY chary for actual typing, with scanx for control keys like the cursor keys+delete, and otherwise support bindings via the scanx value and ignore chary.\r\nkey up events:\r\n\tscanx: as in key down events.\r\n\tchary: as in key down events.\r\n\tdevid: as in key down events.\r\n\tIt is not guarenteed that you will receive unicode up events as the windowing system may not support it, but you will receive scancode up events.\r\n\tcancelling avoids cancelling -forward type bindings which should normally always be safe for duplicates, thus cancelling up events should generally be avoided.\r\nmouse motion events:\r\n\tscanx: how many pixels across the screen the mouse has moved (scaled in terms of virtual pixels, so may be fractional).\r\n\tchary: how many pixels down the screen the mouse has moved (scaled in terms of virtual pixels, so may be fractional).\r\n\tdevid: a mouse device or touch event id. see mouse absolute events for details.\r\n\tIf running a UI, you would want to add the two values to the previous mouse cursor position before returning TRUE.\r\n\tcancel to avoid the engine changing view angles.\r\nmouse absolute events:\r\n\tscanx: the absolute X position on the screen in terms of virtual pixels.\r\n\tchary: the absolute X position on the screen in terms of virtual pixels.\r\n\tdevid: a mouse device or touch event id.\r\n\tThe device id for mice and touch events can be somewhat complex.\r\n\tFor mice, you are likely to see all mice appear as id 0, simply because the operating system merges all mouse devices to simulate a single one which is all the engine can see.\r\n\tOn windows, you tend to need an engine that supports raw input in order to get distinguishable mouse devices.\r\n\tWhen used in a mouse-driven user interface, a mouse devid is a simple mouse device id that is constant for every event that the mouse generates.\r\n\tFor touch events, things are more complicated. Touch ids have a limited life span in order to facilitate multitouch screens.\r\n\tA multitouch sequence thus follows these lines:\r\n\t\tmouse absolute\r\n\t\tkey down (scanx=key_mouse1)\r\n\t\tmouse absolute etc\r\n\t\tkey up (scanx=key_mouse1)\r\n\tAfter which, the touch id can be recycled as a different touch event.\r\n\t(Side note: to test multitouch in fteqw+vista+, use these console commands: in_simulatemultitouch 1; in_rawinput 1; in_restart; note that you will need multiple mice plugged in, one for each 'finger')\r\n\tA mouse device may use either mouse motion or mouse absolute events depending on whether the mouse is grabbed for use exclusively with quake or not.\r\n\tIt is not uncommon for engines to automatically switch to absolute positions when the console is down.\r\n\tcancel to avoid changing view angles if the engine has some sort of touchscreen mode enabled, otherwise does nothing.\r\naccelerometer events:\r\n\tscanx: x acceleration\r\n\tchary: y acceleration (-9.8 (gravity) if the screen is held vertically I believe)\r\n\tdevid: z acceleration (-9.8 (gravity) if the screen is flat facing upwards)\r\nfocus events:\r\n\tscanx: now has mouse focus. true=focused. false=unfocused. -1=unchanged\r\n\tchary: now has keyboard focus. true=focused. false=unfocused. -1=unchanged\r\n\tdevid: the mouse or keyboard for which focus changed.\r\n\tfor the sake of simplicity, you can just ignore any events with a devid other than 0.\r\n\tnote that it is the engine's choice whether the qc receives input or not. if the console or the menus are active, those may exclusively receive input instead.\r\n\t\r\nSo for a windowing system with a mouse cursor and a simple text field, we can write this simple code:\r\nstring userinputtext;\r\nvector mousecursor;\r\nfloat(float evtype, float scanx, float chary, float devid) CSQC_InputEvent =\r\n{\r\n\tswitch(evtype)\r\n\t{\r\n\tcase IE_KEYDOWN:\r\n\t\tstring newt = userinputtext;\r\n\t\tif (scanx == KEY_BACKSPACE)\r\n\t\t\tnewt = substring(newt, 0, -2);\t//-1 = end of string. -2=one char before the end of the string\r\n\t\telse if (scanx == KEY_ENTER)\r\n\t\t{\r\n\t\t\t//send it to the server in the form of a say command. just to show some useful(ish) interaction with the server.\r\n\t\t\tlocalcmd(strcat(\"cmd say \", userinputtext, \"\\n\"));\r\n\t\t\tnewt = \"\";\r\n\t\t}\r\n\t\telse if (chary)\r\n\t\t\tnewt = strcat(newt, chr2str(chary)); //add the char to the end.\r\n\t\telse if (scanx == KEY_MOUSE2)\r\n\t\t\tnewt = \"\"; //clear it on right click.\r\n\t\t//other scancodes not recognised, but don't add redundant 0s on the end.\r\n\t\t//temp strings are not valid between calls.\r\n\t\tif (newt != userinputtext)\r\n\t\t{\r\n\t\t\tstrunzone(userinputtext);\r\n\t\t\tuserinputtext = strzone(newt);\r\n\t\t}\r\n\t\tbreak;\r\n\tcase IE_KEYUP:\r\n\t\treturn FALSE;\t//don't break things if the menu was enabled mid-game while other keys are potentially still held, or some other way.\r\n\tcase IE_MOUSEDELTA:\r\n\t\t//note: we can cope with multiple separate mouse devices here.\r\n\t\tmousecursor_x += scanx;\r\n\t\tmousecursor_y += chary;\r\n\t\treturn TRUE; //don't change view angles\r\n\tcase IE_MOUSEABS:\r\n\t\tif (devid != 0)\t//no stuttering please.\r\n\t\t\treturn FALSE;\r\n\t\tmousecursor_x = scanx;\r\n\t\tmousecursor_y = chary;\r\n\t\treturn TRUE; //don't change view angles\r\n\tdefault:\r\n\t\tbreak;\r\n\t}\r\n\r\n\treturn FALSE;\r\n};\r\n//NOTE: this conflicts with previous examples! skip the drawfill and add the rest after your renderscene call or something.\r\nvoid(float width, float height, float menushown) CSQC_UpdateView =\r\n{\r\n\tdrawfill('0 0 0', [width, height, 0], '1 1 0', 1, 0);\t//clear the screen\r\n\tdrawstring('0 0 0', userinputtext, '8 8 0', '0 0 1', 1, 0);\t//draw their input text\r\n\tfloat sc = (sin(time*8)+1.5)*8\r\n\tdrawcharacter(mousecursor - '0.5 0.5 0'*sc, '+', sc*'1 1 0', '0 0 1', 1, 0);\t//draw a lame cursor with pulsing size (to distinguish it from any possible engine cursor)\r\n};\r\n\r\nOf course, as you go further into windowing systems, you'll need to make some sort of heirachy or screen regions or some other logic so that things can be clicked on. Explaining how to write UI wigits is somewhat outside of the scope of this tutorial, so look elsewhere for such things.\r\n\r\nIf you wish to use a 2d mouse cursor to interact with 3d positions, you can do something like:\r\nvector() CursorToWorldCoord =\r\n{\r\n\tvector wnear = unproject([mousecursor_x, mousecursor_y, 0]);\t//determine the world coordinate for the mouse cursor upon the near clip plane\r\n\tvector wfar = unproject([mousecursor_x, mousecursor_y, 100000]);//determine the world coordinate for the mouse cursor upon the far clip plane, with an outrageously large value as a workaround for dp.\r\n\ttraceline(wnear, wfar, TRUE, world);\r\n\treturn trace_endpos;\r\n};\r\nSimilarly:\r\nvector(vector worldpos) WorldToScreen =\r\n{\r\n\tvector twodee = project(worldpos);\r\n\ttwodee_z = 0;\t//discard all depth info (note that depth is typically scaled between 0 and 1, so avoid)\r\n\treturn twodee;\r\n};\r\n\r\n\r\n\r\n\r\nRight, now its time to explain CSQC-only entities in proper detail:\r\nI'm going to introduce you to a new entrypoint just for the luls:\r\nvoid(float apiver, string enginename, float enginever) CSQC_Init =\r\n{\t//note: the three arguments are engine-defined, and can be used to to detect if the engine version you're running in has bugs. Just call error(\"please update your engine\") if it's got known fatal bugs, but try not to exclude engines you've not tested against (ie: forks where your code will otherwise work fine).\r\n\tlocal entity foo = spawn();\r\n\tprecache_model(\"progs/player.mdl\");\t//or something else\r\n\tsetmodel(foo, \"progs/player.mdl\");\t//same as ssqc, see, its easy!\r\n\tsetorigin(foo, '0 0 0');\t\t//you'll need to fix the origin to ensure you don't put it in a wall...\r\n\tfoo.drawmask = MASK_ENGINE;\t\t//cause the entity to be added to the scene automatically via the addentities(MASK_ENGINE) builtin.\r\n};\r\n\r\nSo yeah, when the csprogs is loaded, the CSQC_Init function is automatically called by the engine. Inside we spawn an entity, just like we would in ssqc. Give it a position, just like you would in ssqc, etc.\r\nIn fact, the only difference so far is the extra drawmask line. This line is so that the core csqc can handle subviews gracefully instead of all entities being added to the scene unconditionally.\r\nSo if we try running it, our CSQC_UpdateView (at least ones that actually draw a view) automatically pick up our new entity, but we've not set a think function nor are we trying to animate it.\r\nCSQC entities do not interpolate or autoanimate, nor does the engine interpolate them. What's worse, is that think functions with their predefined interval can result in video frames skipping with jerky animations! oh noes!\r\nSo lets add an extra csqc-specific line to where you spawned your entity.\r\nfoo.predraw = AnimateSomeLameModel;\r\nNice simple function field assignment there...\r\nAnd we need to define that function. Here's one I wrote earlier:\r\nfloat() AnimateSomeLameModel =\r\n{\r\n\t#define FIRSTFRAME 0\r\n\t#define NUMFRAMES 6\r\n\tself.lerpfrac -= frametime * 10;\t//animate at 10fps\r\n\twhile(self.lerpfrac < 0) //protects against sudden low framerates.\r\n\t{\r\n\t\tself.frame2 = self.frame; //if we're at 0, frame2 became redundant anyway\r\n\t\tself.frame += 1;\t//move on to the next frame\r\n\t\tif (self.frame >= FIRSTFRAME+NUMFRAMES) //this should be relative to the current animation that should be shown\r\n\t\t\tself.frame = FIRSTFRAME;\t//restart it.\r\n\t\tself.lerpfrac += 1; //go to the start of the frame\r\n\t}\r\n\r\n\tmakevectors(getviewprop(VF_ANGLES));\t//set v_forward etc.\r\n\tvector vieworg = getviewprop(VF_ORIGIN);//read the current view origin\r\n\tsetorigin(self, vieworg + v_forward*128);//reposition the entity to 128 units infront of the view so it doesn't get lost in the wall, so we know its visible.\r\n\treturn FALSE;\r\n};\r\nYes, its completely lame, but it demonstrates the basics of animation, which is what is important, right?\r\nAnyway, the three fields 'frame', 'frame2', and 'lerpfrac' work together to provide animation blending. A lerpfrac of 1 means that only frame2 is used, while a lerpfrac of 0 means that only frame is used. A lerpfrac of 0.5 means that both are used with the result being 50% between them. If you don't do the lerpfrac thing and instead just set frame inside some think function, the animation will NOT be smooth due to the lack of auto interpolation.\r\nWith the code above, I've made lerpfrac act as a stepping timer that keeps counting down from 1 to 0. when it drops below 0, a new frame is chosen and the old one is archived off in frame2 and lerpfrac is reset to about 1. This means that the frame choice after a frame switch is still mostly the old frame, blending into the new frame over time.\r\nBe warned that frametime is in local time rather than tied to server time, and thus the animation is purely clientside with no relation to server timings and is thus unsyncronised. If you want a more robust solution, you would need to syncronise based upon the time difference between time and some starttime value when the new action became valid.\r\nSidenote: To animate within a framegroup, you will need to update self.frame1time and self.frame2time each frame. This is the absolute time into the animation. If you increment the value by something like: frametime*distance_traveled/animated_distance_traveled, it should be possible to achieve foot syncronisation with different movement speed settings.\r\n\r\n\r\n\r\n\r\nCSQC Entity Networking:\r\nCSQC entity networking is works with callbacks, and includes tolerance for packetloss and latency. Basically, the SSQC sets various SendFlags on the entity to say which parts of the entity have changed, and the server calls the SendEntity callback in order to build an update mssage at some point in the future. The callback is told the SendFlags that need to be sent.\r\nThe redundantcy and vauge wording is where packetloss tolerance comes in. Basically, the server is responsible for tracking the flags in each message, and any which never arrived on the client must be resent. This can take a successful round-trip before dropped packets are noticed, hence the 'in the future', and why the callback is told the fields which got dropped.\r\nThe network message itself is framed only by the entity number. The callback is responsible for stating exactly what sort of entity it is, and what is actually inside the message. This is typically achieved with a leading entitytype id byte.\r\n\r\nIn SSQC:\r\nfloat(entity to, float sendflags) MySendEntity =\r\n{\r\n\tWriteByte(MSG_ENTITY, 5);\t\t//write some entity type header\r\n\tWriteByte(MSG_ENTITY, sendflags);\t//tell the csqc what is actually present.\r\n\tif (sendflags & 1)\r\n\t{\r\n\t\tWriteCoord(MSG_ENTITY, self.origin_x);\r\n\t\tWriteCoord(MSG_ENTITY, self.origin_y);\r\n\t\tWriteCoord(MSG_ENTITY, self.origin_z);\r\n\t}\r\n\tif (sendflags & 2)\r\n\t\tWriteByte(MSG_ENTITY, self.frame);\r\n\r\n\tif (sendflags & 128)\r\n\t\tWriteByte(MSG_ENTITY, self.modelindex);\t//sending a modelindex is smaller than sending an entire string.\r\n\r\n\treturn TRUE;\t//handled. If you return FALSE here, the entity will be considered invisible to the player.\r\n};\r\n\r\nIn your spawn function:\r\n\tself.SendEntity = MySendEntity;\r\nAnd when the origin changes:\r\n\tself.SendFlags |= 1;\r\nAnd when the frame changes:\r\n\tself.SendFlags |= 2;\r\nNote how the SendEntity function refers to sendflag 128, but its not set anywhere. Well, 'new' entities always have sendflags set to 0xfffff or something just large enough to avoid overflowing floats. Thus entities which have just been spawned or otherwise have just entered a player's pvs will always have sendflag bit 128 set, and thus the modelindex will be sent for such new entities without needing to explicitly state it.\r\n\r\nFrom the CSQC side, the engine notices the entity update and then calls CSQC_Ent_Update. If the entity is new, it calls CSQC_Ent_Update beforehand.\r\nThe CSQC must then read whatever header the ssqc wrote, determine what sort of entity it is, and determine what the meaning of the various fields are.\r\nOnce the SSQC removes the entity or it just becomes invisible, the server will send a remove event, which the client will handle by calling CSQC_Ent_Remove. The CSQC is expected to then call remove(self) before returning.\r\nvoid(float isnew) CSQC_Ent_Update =\r\n{\r\n\tfloat enttype = readbyte():\r\n\tfloat flags;\r\n\tswitch (enttype)\r\n\t{\r\n\tcase 5:\r\n\t\tflags = readbyte();\r\n\t\tif (flags & 1)\r\n\t\t{\r\n\t\t\tself.origin_x = readcoord();\r\n\t\t\tself.origin_y = readcoord();\r\n\t\t\tself.origin_z = readcoord();\r\n\t\t\tsetorigin(self, self.origin);\t//for correctness.\r\n\t\t}\r\n\t\tif (flags & 2)\r\n\t\t\tself.frame = readbyte();\r\n\t\tif (flags & 128)\r\n\t\t{\r\n\t\t\tsetmodelindex(self, readbyte());\r\n\t\t\tself.drawmask = MASK_ENGINE;\r\n\t\t}\r\n\t\tbreak;\r\n\tdefault:\r\n\t\terror(\"Unknown entity type! oh noes! panic!\");\r\n\t}\r\n};\r\nvoid() CSQC_Ent_Remove =\r\n{\r\n\tremove(self);\r\n};\r\nAs you add new types of entities, you'll need to add more cases. Ideally you'd just move much of that code into a function call.\r\nAlso, you might want to combine the enttype byte and the flags byte, at least for entities which don't have many flags.\r\nIf you keep your entity networking fairly generic, you can combine various projectiles into common classes, and doing so shouldn't really need more than 16 different classes, thus giving four flags for potentially less overhead. But that's optimisation talk and thus perhaps out of place here. Anyway, you get the idea.\r\nThe above example is very simplistic, of course, as it only sets origin, frame, and model.\r\nThere's a few things to look out for. As described in the previous section, there is no automatic interpolation or animation lerping, so you'll need to combine that with the predraw stuff. To interpolate positions, you'll have to keep track of the update time, old update time, new origin and old origin. lerping is then:\r\nlocal float frac = (time - self.lastupdate) / (self.lastupdate - self.prevupdate);\r\nfrac = bound(0, frac, 1);\r\nself.origin = self.prevorigin + (self.lastorigin-self.prevorigin) * frac;\r\nYou can of course make assumptions along the lines of an entity updating 10 times a second on the dot, which may or may not result in more robust interpolation.\r\nWhen interpolating angles, be sure to take care with wrapping from 360 down to 0. \r\n\r\n\r\n\r\n\r\nPrediction:\r\nIn theory, prediction is easy. Simply keep an input frame log, track which input frame was the last one applied to movement data, and applay any unacknowledged input frames to the last state received using the same logic that the server will use.\r\nIn practise, prediction is not reliable. It is a guess. A guess which is not particuarly compatible with traditional NQ player entity physics frames with their lack of syncronisation or even acknowledgements.\r\nGenerally this means that you must implement ALL player physics code yourself, using tracebox. Alternatively you may be able to get away with using the engine's runstandardplayerphysics function instead, modifying the various input_* values slightly before calling it.\r\n\r\nFrom the SSQC side, things are fairly simple. The server calls:\r\nvoid() SV_RunClientCommand =\r\n{\r\n\tinput_angles_y += 180; \t\t//a fun mindfuck.\r\n\trunstandardplayerphysics(self); //and apply the input frame to the current entity state.\r\n}:\r\n\r\nFrom the CSQC side, when you receive an entity state the servercommandframe global will say the input frame number that was last acknowledged by the server. Thus any player entity from the server will have this input frame already applied. While the clientcommandframe global is the input frame that is currently being generated.\r\nThus if 'self' has its fields set to the most recently received state, you can apply all pending input frames with:\r\nfor (if = servercommandframe+1; if <= clientcommandframe; if+=1)\r\n{\r\n\tif (!getinputstate(if))\r\n\t\tcontinue;\t\t//that input frame is too old.\r\n\tinput_angles_y += 180; \t\t//a fun mindfuck.\r\n\trunstandardplayerphysics(self);\t//and apply the input frame to the current entity state.\r\n};\r\nThe input frame that matches clientcommandframe is updated each frame, until it is finally sent at which point the global is bumped. Input frames will not be remembered forever, but will otherwise be static until they are forgotten.\r\nYou can then set the VF_ORIGIN view property to match the predicted position. You will also likely want to smooth out steps, and perhaps add error correction too.\r\n\r\n\r\n\r\n\r\nAdvanced Skeletal Animation (FTE_CSQC_SKELETONOBJECTS):\r\nWhen animating players and such, you may desire more than just frames.\r\nIf you're using a skeletal model format (like IQM), then more complete bone control becomes available to you.\r\nWhen your entity is first spawned after you set the model, add some code like:\r\n\tself.skeletonindex = skel_create(self.modelindex);\r\nEach frame, update your .frame, .frame2, .lerpfrac, .frame1time, .frame2time accordingly and then call:\r\n\tskel_build(self.skeletonindex, self, self.modelindex, retainfrac, firstbone, lastbone, addfrac);\r\nThat call needs some explaining. Especially the last four arguments.\r\nretainfrac is what fraction of the pose info to retain. At the start of each frame you should generally pass 0. Later calls will generally pass 1.\r\naddfrac is what fraction of the animation data to add. Note that this also serves as a sort of scaler. All skel_build calls for each bone in a single skeletal object should add up to 1 to avoid extra scaling.\r\nUsing these two values you can blend multiple animations together, for instance allowing running animations to fade into a standing animation, or to blend both sideways and forwards running animations together at rates depending on direction+speed in order to achieve foot-sync.\r\nfirstbone and lastbone provide a way for you to affect only a specific region of the model. Beware that this requires that your skeleton has bones that are carefully ordered.\r\nIf firstbone is 0, it'll be corrected to 1. Bone 0 is not otherwise valid, as 0 refers to the entity position itself.\r\nIf lastbone is 0, it'll be replaced with the total bone count.\r\nIf ordered correctly, you can split the model by legs+torso by a bone at the base of the spine. With that achieved, you can find the spine bone with skel_find_bone(self.skeletonindex, \"spine1\") or so (depends what your model calls it), and then build the two parts of your animation with:\r\nfloat() playerpredraw =\r\n{\r\n\t//interpolate origin, and calc displacement\r\n\t//note: you probably want to replace this with prediction if you're working on the local player's entity.\r\n\tfloat frac = (time - self.lastupdate) / (self.lastupdate - self.prevupdate);\r\n\tfrac = bound(0, frac, 1);\r\n\tvector newpos = self.prevorigin + (self.lastorigin-self.prevorigin) * frac;\r\n\tvector moved = newpos - self.origin;\t//this is how much we've moved since the last render frame, note that it implies frametime.\r\n\tself.origin = newpos;\r\n\t//determine animation weights\r\n\tmakevectors([0, self.angles_y, 0]);\t//set v_forward,v_right vectors so we can do dot products to see how much of which direction the entity moved in.\r\n\tself.forwardweight += fabs(v_forward*moved) * 32;\t//scaler should rise to 1 after 0.1 secs when moving at 320qu\r\n\tself.sideweight += fabs(v_right*moved) * 32;\t//scaler should rise to 1 after 0.1 secs when moving at 320qu\r\n\t//clamp \r\n\tfloat sc = self.forwardweight + self.sideweight;\r\n\tif (sc > 1)\r\n\t\tsc = 1/sc;\t//greater than 1 would add negative legs...\r\n\telse if (moved_x || moved_y)\r\n\t\tsc = 1;\t\t//don't drop any movement weights if we're moving\r\n\telse\r\n\t\tsc = max(0, sc - frametime * 10);\t//drop down to 0 over 0.1 secs if we stopped moving.\r\n\tself.forwardweight *= sc;\r\n\tself.sideweight *= sc;\r\n\tself.lerpfrac = 0; //just in case...\r\n\t//add forward animation (retain frac = 0 to reset it)\r\n\tself.frame = $forwardanimation;\r\n\tself.frame1time = (self.forwardtime += frametime * (v_forward*moved));\r\n\tskel_build(self.skeletonindex, self, self.modelindex, 0, 0, spinebone, self.forwardweight);\r\n\t//add side animation (retain frac = 1 to not overwrite forward)\r\n\tself.frame = $sideanimation;\r\n\tself.frame1time = (self.sidetime += frametime * (v_right*moved));\r\n\tskel_build(self.skeletonindex, self, self.modelindex, 1, 0, spinebone, self.sideweight);\r\n\t//add idle animation (retain frac = 1 to not overwrite directions)\r\n\tself.frame = $standanimation;\r\n\tself.frame1time = (self.sidetime += frametime);\r\n\tskel_build(self.skeletonindex, self, self.modelindex, 1, 0, spinebone, 1-(self.forwardweight+self.sideweight));\r\n\t//add torso animation (retain frac = 0 to reset torso parts. note that this doesn't affect legs due to the bone ranges)\r\n\tself.frame = $torsoanimation;\r\n\tself.frame1time = time - torsostarttime;\r\n\tskel_build(self.skeletonindex, self, self.modelindex, 0, spinebone, 0, 1);\r\n\treturn FALSE;\r\n}\r\nand in your spawn code:\r\n\tsetmodel(self, \"progs/skelplayer.iqm\");\r\n\tself.skeletonindex = skel_create(self.modelindex);\r\n\tself.predraw = playerpredraw;\r\n\tself.drawmask = MASK_ENGINE;\r\nand when its killed:\r\n\tskel_delete(self.skeletonindex);\r\nThen when your entity is added to the scene (via addentities), the predraw function is called to interpolate your entity's origin and update its skeltal object.\r\nYou'll have to ensure your parse code updates lastupdate and prevupdate timestamps, along with lastorigin and prevorigin, that it updates the angles.\r\n$forwardanimation etc frame macros will need to be set to the correct framegroups within the model (note that this code requires framegroups, and would become more complex if you wished to animate between descrete frames for each animation, but that doing so is possible by using lerpfrac and frame2 instead of frame1time).\r\nThe code does not interpolate angles, which is something you'll probably want to add, as well as add a concept of actions so that you can blend between idle+shoot animations as appropriate.\r\n\r\n\r\n\r\n\r\nRagdoll (FTE_QC_RAGDOLL / FTE_QC_RAGDOLL_WIP):\r\nRagdoll support is dependant upon ODE, which requires cvar(\"physics_ode_enable\") to be set in order to work properly.\r\nOnce you have a skeletal object, you can add in ragdoll by calling skel_ragupdate(self, \"doll foo.doll\", 0); at the end of your predraw function (which will cause the ragdoll to update and move based upon an animated model, and then call skel_ragupdate(self, \"animate 0\", 0); once your entity dies and goes limp (typically, this is done part-way through the death animation, so the ragdoll inherits a certain amount of velocity from said animation).\r\nThe .doll file describes the bodies and joints within the model. If a body is set to animate, it will use the bone data your code specifies. If a body is not set to animate, it will be ragdolled even when the player is still alive. This can be used for things like ponytails, cloth, etc."
  },
  {
    "path": "specs/distort.txt",
    "content": "What is this?\r\n\r\nIts a simple particle config with somewhat more complex (glsl) rendering effect.\r\nSimply put, it'll distort the screen behind rockets and grenades.\r\n\r\nTo use, place the pk3 in your quake/fte/ dir.\r\nStart up FTE.\r\nr_particlesystem script\r\nr_particlesdesc \"spikeset tsshaft distort\"\r\n\r\nThe second line will give you the default particle config, with the two replacement trails overriding the built in effects.\r\n\r\nIf you want to look at how its done, you can open the pk3 with winrar/7zip/winzip/windows explorer (if you change the filename extension). There are two text files inside. The effect could be adapted and applied to explosions and other stuff too. Enjoy.\r\n"
  },
  {
    "path": "specs/example.shader",
    "content": "//this file describes fte's extensions to the q3 shader syntax\r\n//it can be used directly as a shader file.\r\n\r\n\r\n//lets put some glsl on the console (Set gl_console to 'console')\r\n//note that the _glsl postfix is only used if the user has a gfx card that supports glsl.\r\nconsole_glsl\r\n{\r\n\tnopicmip\r\n\tnomipmaps\r\n\r\n\tprogram shaders/generic.vp shaders/console.fp\r\n\tparam texture 0 baset\t//tell it which samplers it can use\r\n\tparam time time\t//tell it the time\r\n\t//param cvarf gl_specularexponant specexponant //example of feeding in the value of a cvar\r\n\t//param upper topcol //could be used on entities to read the (player's) shirt colour into a topcol uniform\r\n\t//param lower botcol //could be used on entities to read the (player's) lower colour into a botcol uniform\r\n\t//param eyepos vieworg //put the render origin into uniform named vieworg\r\n\t//param colors colourmod //reads the ent's colourmod and puts it in the colourmod uniform\r\n\r\n\t{\r\n\t\tmap \"conback\"\r\n\t\t//videomap \"video/intro.roq\"\t//use this line instead in order to feed a roq/avi into the glsl/shader\r\n\t}\r\n}\r\n\r\n\r\n"
  },
  {
    "path": "specs/ext_csqc_1.txt",
    "content": "This specification is subject to change. Engines should not yet advertise or detect the extension key in public releases.\r\n\r\nCSQC - or as engine modders call it: a good way to get other people to stop other people asking for new features - is a highly adaptable extension.\r\n\r\nFeel free to place random comments all over the place. This isn't in use yet, so it's quite easy to change all this babble.\r\n\r\nMajor features:\r\n\r\n   * Able to draw custom status bar.\r\n   * Able to adjust models and stuff on models.\r\n   * Prediction.\r\n   * Able to change fov/pov/picture-in-picture.\r\n   * Able to launch sounds localy\r\n   * A brilliant starting point for umpteen extra extensions.\r\n\r\nCSQC progs should be compiled into a csprogs.dat by default, but there's no reason why an engine can't provide cvars to use a different name. This must match the one present on the server to be considered valid. It doesn't have to of course, but it should do.\r\n\r\nCSQC is intended to replace as much of the fixed-function client code as possible, without requiring in-depth knoledge of the engine's network protocol. Networking will be discussed later in the document. As far as drawing anything game related is concerned, the CSQC orders it all. Views, scoreboards and huds included. If the csqc were to simply return, you'd have nothing drawn. Areas that the CSQC fails to draw over will contain undefined pixels.\r\n\r\nIn order to draw the screen, the CSQC code must export a CSQC_UpdateView function. Here's an example:\r\n\r\nvoid(float width, float height, float menushown) CSQC_UpdateView =\r\n{\r\n\tclearscene();       //wipe the scene, and apply the default rendering values.\r\n\r\n\tsetviewprop(VF_MIN_X, 0);\t//set the left of the view\r\n\tsetviewprop(VF_MIN_Y, 0);\t//set the top of the view\r\n\tsetviewprop(VF_SIZE_X, width);\t//set how wide the view is (full width)\r\n\tsetviewprop(VF_SIZE_Y, height); //set how high the view is (full height)\r\n//     \tsetviewprop(VF_FOV, cvar(\"fov\"));\t//this is entirely optional\tFIXME: fov is x,y\r\n\r\n\t//if you use prediction, you'll need the following four lines\r\n//\tsetviewprop(VF_ORIGIN, myvieworg);\t//change where the view is drawn\r\n//\tsetviewprop(VF_ANGLES, myviewang);\t//change the angle the view is drawn at\r\n//\tmakevectors(myviewang);\t\t\t//calc the listener directions\r\n//\tsetlistener(myvieworg, v_forward, v_right, v_up);\t//change where sounds come from\r\n\r\n\taddentities(MASK_NORMAL|MASK_ENGINE|MASK_ENGINEVIEWMODEL);    //Add all the known ents.\r\n\r\n\trenderscene();\r\n\tmydrawsbar(width, height);\r\n};\r\n\r\nAs you can see, that's a basic example used by a mod which just wants to adapt it's status bar (actual sbar drawing code shown later). The structure allows csqc to draw it's own subwindows, secondary views and whatnot. The view is fully customisable, and the engine is free to add new view properties as it needs (eg: seperate fov_x/fov_y keys). The engine sets it's defaults inside the builtin clearscene.\r\n\r\n(VERY)Simple sbar code:\r\n\r\nvoid(float width, float height) mydrawsbar =\r\n{\r\n\tlocal vector barpos;\r\n\tbarpos_x = 0;\r\n\tbarpos_y = 480 - 64;\r\n\thealth = getstatf(STAT_HEALTH);\r\n\tif (health < 20)\r\n\t\tdrawpic(barpos, \"sbar/lowhealth.tga\");\r\n\telse if (health > 80)\r\n\t\tdrawpic(barpos, \"sbar/highhealth.tga\");\r\n\telse\r\n\t\tdrawpic(barpos, \"sbar/midhealth.tga\");\r\n};\r\n\r\nNote: stats MUST match across engines, even if in an emulated sense. stats 0-31 are for engine maintaners to standardize. stats 32-128 are for QC modders to do as they choose on a per-mod basis. Refer to the 'clientstat' server builtin for how a server mod sends a custom stat.\r\n\r\n\r\ncsqc builtins:\r\n\r\nFor the most part, the csqc vm should provide the same builtins as the ssqc. There are exceptions though, where ssqc builtins don't make sence. This is the list of the builtins specifically removed:\r\n\r\n   * checkclient\r\n   * stuffcmd\r\n   * bprint\r\n   * sprint (use 'print' instead)\r\n   * aim\r\n   * writebyte\r\n   * writechar\r\n   * wruteshort\r\n   * writelong\r\n   * writecoord\r\n   * writeangle\r\n   * writestring\r\n   * writeentity\r\n   * precache_file\r\n   * changelevel\r\n   * logfrag (qw)\r\n   * infokey (qw)\r\n   * multicast (qw)\r\n\r\n3d scene management:\r\n\r\nvoid() clearscene = #300;\r\n\tClear the scene, reset the view values to default.\r\n\r\nvoid(float mask) addentities = #301;\r\n\tAdd all entities according to a drawmask (added if they match).\r\n\tActs as if R_AddEntity was called for each one of them.\r\n\tFor each entity added, the predraw of that entity function will be called before being any fields are read.\r\n\tThe mask parameter is a bitfield parameter, with the following bits having special meanings:\r\n\tMASK_ENGINE=1: add standard entities that were sent via the non-csqc protocol, including temp entities. (also triggers delta updates if registered)\r\n\tMASK_ENGINEVIEWMODEL=2: add the normal viewmodel (potentially multiple if ssqc .viewmodelforclient is supported).\r\n\tThe bits with value of 4 through 256 is guarenteed to never be used by the engine for anything special.\r\n\r\nvoid(entity e) addentity = #302;\r\n\tAdd a single entity to the scene. It's fields are copied out immediatly. This allows for repeated calling.\r\n\tDoes NOT call the predraw function. This is useful for adding the entity within a predraw function from addentitymask without recursion. (think Quad shells).\r\n\r\nfloat(float propertyindex, ...) setviewprop = #303;\r\n\tChange a property of the 3d view.\r\n\tReturns 0 if the property id wasn't recognised or was invalid, and returns non-zero if it was applied.\r\n\tEngineHackers: Extension property indexes can be added using the same ranges as for builtins (IE: DP should add at 400+).\r\n\tValid parameters in any compliant engine:\r\n\tVF_MIN = 1 (vector) (top left of the screen)\r\n\tVF_MIN_X = 2 (float)\r\n\tVF_MIN_Y = 3 (float)\r\n\tVF_SIZE = 4 (vector) (game view width/height)\r\n\tVF_SIZE_X = 5 (float)\r\n\tVF_SIZE_Y = 6 (float)\r\n\tVF_VIEWPORT = 7 (vector, vector)\r\n\tVF_FOV = 8 (vector) (changes both fovx and fovy)\r\n\tVF_FOVX = 9 (float) (changes the horizontal fov. be careful with this)\r\n\tVF_FOVY = 10 (float)\r\n\tVF_ORIGIN = 11 (vector)\t(current view origin. default is player' origin + STAT_VIEWHEIGHT)\r\n\tVF_ORIGIN_X = 12 (float)\r\n\tVF_ORIGIN_Y = 13 (float)\r\n\tVF_ORIGIN_Z = 14 (float)\r\n\tVF_ANGLES = 15 (vector) (the angles that entity is looking on, acording to the wow resource.\r\n\tVF_ANGLES_X = 16 (float)\r\n\tVF_ANGLES_Y = 17 (float)\r\n\tVF_ANGLES_Z = 18 (float)\r\n\tVF_DRAWWORLD = 19 (float) (defaults to enabled. if set to 0 disables particles.)\r\n\tVF_DRAWENGINESBAR = 20 (float)\r\n\tVF_DRAWCROSSHAIR = 21 (float)\r\n\r\nfloat(float propertyindex) getviewpropf = #309;\r\nvector(float propertyindex) getviewpropv = #309;\r\n\tpropertyindex is as described above in setviewprop.\r\n\tReturn type is dictated by propertyindex. Ensure you use the correct form.\r\n\tParanoid engines are fully within their rights to return '0 0 0' for VF_ORIGIN (and sub componants). \r\n\r\nvoid(vector org, float radius, vector rgb) adddynamiclight = #305;\r\n\tAdds a light to the scene with the specified properties.\r\n\tEngines without coloured lighting can average out the light colours.\r\n\r\nvoid() renderscene = #304;\r\n\tInvokes the renderer to actually draw the scene inside the call.\r\n\tDoes not clear the display lists.\r\n\r\nfloat(string s) precache_model = #20;\r\n\tPrecaches a model for clientside usage. If the server qc already precached it, reuse that instead.\r\n\tSome protocols allow precaching mid-level. This may require two lists of model indexes. The indexes from the server *must* match the server at all times. It is possible that a model can end up on both lists if the server precaches mid-level. This builtin must favour the server's index over any local index.\r\n\tThis can be called at any time.\r\n\tThe return value is the modelindex of the specified model.\r\n\tNote that this is the same builtin as the server, but the return value is different, in order to be more useful.\r\n\tIf the model does not exist, an engine may trigger a download request and load the model when it arrives (with a valid index returned), or may return 0.\r\n\r\nvector (vector v) unproject            = #310;\r\n\tConverts from 2d-space to 3d-space. Uses the currently set viewport, origin, angles, and projection info from the current view properties (setviewprop).\r\n\tNote that 2d space contains depth info, in the range of 0 to 1.\r\n\r\nvector (vector v) project              = #311;\r\n\tConverts from 3d-space to 2d space. Uses the currently set viewport, origin, angles, and projection info from the current view properties (setviewprop).\r\n\tNote that 2d space contains depth info, in the range of 0 to 1.\r\n\r\n\r\n\r\n2d display:\r\n\r\n2d display Note that the vectors only use the first two componants, the third is ignored. With texture names, should the csqc leave off the filename extension, The engine should scan through all the extensions that it supports. The engine must support lmp files, and 32bit bottom up tgas should be supported, pngs and jpegs are just bonuses.\r\nImages without paths may be obtained from the gfx.wad.\r\n\r\nvoid(vector pos, vector size, float paletteindex) drawfillpal = #314;\r\n\tDraws a single block of colour.\r\n\tObtains the pixel colour from the palette. If no palette could be loaded, use an r3g3b2 palette (FIXME: sane?).\r\n\r\nfloat(string name) is_cached_pic = #316;\r\n\tReturns true only if an image is already cached.\r\n\tAn engine which doesn't support unloading may attempt to cache the image before checking.\r\n\tTo see if a picture is valid, drawgetimagesize instead.\r\n\r\nvoid(string name) precache_pic = #317;\r\n\tAttempts to precache an image without drawing it.\r\n\r\nvector(string picname) drawgetimagesize = #318;\r\n\tRetrieves the size of an image that the engine believes it to be.\r\n\tNote that image replacement on hud elements may do inaccurate things to image scale. If a replacement image is used, it is unspecified whether the size comes from a lmp or a replacement png.\r\n\tReturns '0 0 0' if the image failed to load (FIXME: DP replaces with a replacement image, with size 64*64, this is incompatible with that).\r\n\r\nvoid(string name) free_pic = #319;\r\n\tUn-caches an image.\r\n\tAn engine is not required to implement this - can be a no-op.\r\n\r\nvoid(vector pos, float char, vector size, vector rgb, float alpha) drawcharacter = #320;\r\n\tDraws a quake character using the quake font.\r\n\tA software rendering engine is not required to honour the size, rgb, or alpha parameters, size may be forced to '8 8 0'.\r\n\r\nvoid(vector pos, string text, vector size, vector rgb, float alpha) drawrawstring = #321;\r\n\tDraws a text string using the quake font. Size is per-characture.\r\n\tA software rendering engine is not required to honour the size, rgb, or alpha parameters, size may be forced to '8 8 0'.\r\n\tNo markup is used. No wrapping is performed. The entire string is printed as-is, up to the end of the string.\r\n\r\nvoid(vector pos, string picname, vector size, vector rgb, float alpha) drawpic = #322;\r\n\tDraws an image.\r\n\tA software rendering engine is permitted to ignore size, rgb, and alpha parameters.\r\n\tAlpha testing or blending from transparencies in lmp files must be supported.\r\n\r\nvoid(vector pos, vector size, vector rgb, float alpha) drawfillrgb = #323;\r\n\tDraws a single block of colour.\r\n\tA software renderer is not required to implement this function.\r\n\r\nvoid(vector pos, string text, vector scale, float alpha) drawcolorcodedstring = #326;\r\n\tDraws a given text string at the given location.\r\n\tThis builtin is not required to use the same font as the console, but any markup supported by the console must be supported by this builtin. This includes the \\1 prefix on chat messages.\r\n\tThe used font may be variable-width. The y scale states the pixel height of the line. The glyph width will be aproximatly scaled as: newwidth=glyphwidth * (x/glyphheight); (the expectation is that mods use 8*8).\r\n\r\n\r\n\r\n\r\n\r\nvoid(entity e, float modelindex) setmodelindex = #333;\r\n\tSets an entity's model according to a modelindex.\r\n\tThis is useful when transfering modelindexes across the network but otherwise should not be used.\r\n\r\nstring(float modelindex) modelnameforindex = #334;\r\n\tRetrieves the modelname from an assosiated modelindex, as a tempstring.\r\n\r\nvoid(float scale) setsensitivityscaler = #346;\r\n\tScales the user's sensitivity by the parameter.\r\n\tUseful for sniper zoom.\r\n\tThere is no way to retrieve the value currently set.\r\n\tIt is not reset by the engine until the csprogs is closed.\r\n\tThe default is 1.\r\n\r\nvoid(string s, ...) cprint = #338;\r\n\tDirects the engine to draw centered text over the screen using it's regular code as if responding to the server.\r\n\tThis is a convienience function. Does not trigger CSQC_Parse_CenterPrint.\r\n\tFIXME: under which conditions is centerprint text shown? Part of hud, or world, or a new viewprop?\r\n\r\nvoid(string s, ...) print = #339;\r\n\tprint text on the console and notifiction lines. Like dprint, but doesn't need developer set.\r\n\r\nvoid(float effectnum, vector org, vector vel, float countmultiplier) pointparticles = #337;\r\n\tSpawns particles in the style of an ef_efname effect.\r\n\tDoesn't play sound. Doesn't spawn a dynamic light.\r\n\tThe normal 'particle' builtin also works, and should be used in that event.\r\n\r\nvoid(float effectnum, entity ent, vector start, vector end) trailparticles = #336;\r\n\tSpawns particles in the style of an ef_efname effect.\r\n\tDoesn't play sound. Doesn't spawn a dynamic light.\r\n\tThe normal 'particle' builtin also works, and should be used in that event.\r\n\tThe ent parameter is used to track trail states (eg: sparse trails and high framerates). You can use 'world' if you want an independant non-progressive beam.\r\n\r\nfloat(string efname) particleeffectnum = #335;\r\n\tReturns an effect number which can be passed into one of the particle effect spawn functions.\r\n\tReturns 0 if the effect name was not recognised (or was not loaded/supported).\r\n\tDo not assume all engines will return the same values. Engines with fully scriptable effects will not do so, do not assume that the same engine will always return the same indexes either.\r\n\tThese are the non-extended names that should be supported:\r\n               te_explosion (rockets), te_tarexplosion (tarbaby), te_gunshot, te_wizspike, te_knightspike, te_spike, te_superspike, te_teleport, te_lavasplash\r\n\r\nfloat(float framenum) getinputstate = #345;\r\n\tReads view-angles and other properties into csqc's input_* globals.\r\n\tReturns false if the framenum is out-of-date (failure).\r\n\tThis forms part of the prediction support.\r\n\tThe servercommandframe global contains the last input frame acknowledged by the server, while clientcommandframe contains the most recent frame generated by the client.\r\n\tThus frames (servercommandframe+1) to (clientcommandframe-1), inclusive, must be applied to the player's entity state in order for prediction to work.\r\n\r\n\r\nvoid(entity ent) runplayerphysics = #347;\r\n\tRuns the engine's standard prediction algorithms.\r\n\tCan do nothing if the engine doesn't want to provide one (but must be present).\r\n\tExact physics behaviour is up to the implementation. Behaviour should be consistant between alternate implementations of the same network protocol.\r\n\tReads input_* globals.\r\n\tChanges origin, velocity, pmove_flags. Triggers touch functions.\r\n\r\nfloat() isdemo = #349;\r\n\tReturns non-zero if playing a demo.\r\n\r\nfloat() isserver = #350;\r\n\tReturns true if the mod can communicate with the server via cvars and console commands and things (eg: map).\r\n\tThis is intended for admin/single-player purposes. A mod with full voting will not want to use this.\r\n\r\n\r\nstring(float keynum) keynumtostring = #340;\r\n\tRetrieves the name of a key (as used by the bind console command)\r\n\r\nfloat(string keyname) stringtokeynum = #341;\r\n\tFinds the key number of the named keyboard/mouse/joystick button/key.\r\n\r\nstring(float keynum) getkeybind = #342;\r\n\tObtains the full command that a button is bound to.\r\n\tTo set the key binding, use localcmd with the bind console command, preferably do not force it.\r\n\r\nvoid(vector origin, vector forward, vector right, vector up) setlistener = #351;\r\n\tUpdates the sound listener origin and orientation.\r\n\tShould be done once per frame. Failure to do so will revert to the engine's default, which will be inaccurate if you're using player prediction.\r\n\r\nvoid(string model, void(float isnew) updatefunc, float flags) deltalisten = #371;\r\n\tRegisters a function to be called whenever a delta entity with a model matching the given name would be linked.\r\n\tThis gives a way to expose delta entities to the csqc filtered by name.\r\n\tIf model is \"*\", this registers all models (can be overwrite previous registrations. Use a null updatefunc to unregister.).\r\n\tFlags must be one of:\r\n\t\tRSES_NOLERP=1\tdisables origin/angles lerping of the entity. This is vital for player entities which will be predicted.\r\n\t\tRSES_NOROTATE=2 disables auto-rotate based on effects. FIXME: do rotate as part of addentity instead?\r\n\t\tRSES_NOTRAILS=4\tdisables the addition of rocket trails. FIXME: part of addentity instead?\r\n\t\tRSES_NOLIGHTS=8 disables dlights. FIXME: part of addentity instead?\r\n\tFields set each frame before the registered updatefunc is called are:\r\n\t\tmodelindex\r\n\t\torigin\r\n\t\tangles\r\n\t\tvelocity (if known, unchanged if not)\r\n\t\tskin\r\n\t\teffects\r\n\t\tcolormap\r\n\t\tframe\r\n\t\tframe2\r\n\t\tlerpfrac\r\n\t\tframe1time\r\n\t\tframe2time\r\n\tWarning: Additional fields may be set depending on network protocol.\r\n\tWarning: If this builtin is used, entities can be spawned and removed as part of addentities.\r\n\tWarning: If the modelindex changed, the ent may have lingering (custom) fields from the previous ent type.\r\n\r\nfloat() readbyte = #360;\r\nfloat() readchar = #361;\r\nfloat() readshort = #362;\r\nfloat() readlong = #363;\r\nfloat() readcoord = #364;\r\nfloat() readangle = #365;\r\nstring() readstring = #366;\r\nfloat() readfloat = #367;\r\nfloat() readentitynum = #368;\r\n\tReads a part of a network message.\tNote that these builtins are only valid when the engine directs the csqc to start reading. Valid only during that call.\r\n\tIn the case of readstring, it is returned within a temporary string.\r\n\tIt is up to the engine exactly how big coords and angles are, so the csqc must ensure correct matching.\r\n\tFor entities, readentitynum reads only an entity number. This must be matched to an entity with a matching .entnum field. The given entity may not always be known, so matching up in the parse function may not be advantageous.\r\n\r\nfloat(float statnum) getstatf = #330;\r\n\tReturns the floating point value of a client stat.\r\n\tIt is not recommended to use this builtin for STAT_ITEMS due to overflowing floats, but is fine for any of the other standard stats.\r\n\r\nfloat(float statnum) getstati = #331;\r\nfloat(float statnum, float first, float count) getstati_bits = #331;\r\n\tReturns the client stat value rounded to an integer. This may give additional precision.\r\n\tIf first and count are used, retrieves only the bits specified and shifts down (this is used for sigils in the STAT_ITEMS stat)\r\n\tFIXME: Is it sane that this returns a float when its obtaining an int?\r\n\r\nstring(float statnum) getstats = #332;\r\n\tReturns a client stat string. String stats have independant stat indexes from numerical stats.\r\n\tThis will return a temp string.\r\n\r\nstring(float playernum, string keyname) getplayerkey = #348;\r\n\tObtains various player specific things. Items that must be supported are:\r\n\tname, frags, bottomcolor, topcolor.\r\n\tThis info is required for scoreboards.\tNegative player indexes are interpreted as frag-sorted. -1 is the leading player, -2 is the second best player. There is no explicit teamplay support.\r\n\tThis is akin to quakeworld's infokey builtin with a player number.\r\n\tNote: QuakeWorld engines normally provide a team field. NQ engines typically use bottomcolor.\r\n\r\nstring(string key) serverkey = #354;\r\n\tObtains various server related things. Items that must be supported are:\r\n\tip. Retrieves the hostname or ip that was used to connect to the server. Optionally includes a port number.\r\n\tThis is akin to quakeworld's infokey builtin with serverinfo.\r\n\r\nstring() getentitytoken = #355;\r\n\tThis builtin is valid only inside CSQC_WorldLoaded.\r\n\tIt returns the next token from the bsp.\r\n\tEntities take the form { key value }\r\n\tEach return is a tempstring, of which there will be at least two alternating tempstrings.\r\n\tReturns the null string when there are no more tokens. This is distinct from an empty string.\r\n\tTokenizing rules match the server loading.\r\n\r\nvoid(string str) registercommand = #352;\r\n\tRegisters a console command. Any commands that are registered will cause CSQC_ConsoleCommand to be called any time the user tries using the command. The command should have top-most priority, above even aliases.\r\n\r\nfloat() wasfreed = #353;\r\n\tReturns true if the entity was recently freed with the remove builtin, and hasn't been reused yet. Note that at the start of a map this builtin can be unreliable. Otherwise it should have 2 seconds to be reliable (before the entity is reused). This makes the physics code with touch/think functions easier.\r\n\tAvoid using this builtin unless you know the entity was valid before a function was called.\r\n\r\nvoid(vector org, vector forward, vector right, vector up) setlistener = #351;\r\n\tThis builtin allows the csqc to position the 'listener'. This will affect the volume of sounds playing nearby. The directions are required for correct audio spacialisation (just use makevectors).\r\n\r\nvoid(string evname, string evargs, ...) sendevent = #359;\r\n\tWhen called, sends an event to the server gamecode.\r\n\tOn the server, a function named Cmd_evname_EVARGS will be called with the args passed in.\r\n\tevargs specifies the types of the additional arguments.\r\n\tOnly f: float, e: entity, v: vector, and s: string are supported.\r\n\tIf e is used, the entity's entnum field is used instead for sending. If anything is invalid or freed, the server qc will receive 'world' instead.\r\n\tIf s is used, the server will receive a tempstring.\r\n\tThis function is limited to 6 custom arguments.\r\n\tThere is nothing stopping you from packing multiple floats into a vector if you need more arguments.\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\nNetwork Protocol: \r\nThe way csqc networking is designed, is to have the csqc entities seperate from the engine's entities. The entities are considered individual packets, thier length known only to the csqc. It is recommended that engines have a cvar for forcing packet size to be written, but this is not required except as a handy debugging tool.\r\nIf the entity has a 'SendEntity' field set on the server, it is to be sent via the csqc transport, otherwise it goes over whatever transport the engine already has in place and is then never known to the csqc.\r\nThe csqc entity protocol is based upon entity flags set in 'SendFlags'. The server gamecode will never clear the SendFlags field, and will only ever set it.\r\nNew entity packets are only ever sent when SendFlags is non-zero. The server will clear out the SendFlags periodically to allow them to be resent.\r\nConceptually the csqc transport is a versioned one. New packets are typically only sent when the version on the server changes. However, due to packetloss, there are other times when a packet might be sent. The engine must ensure that a version of the entity arries at some point, either by sending reliably, spamming packets until one is acknoledged, or retransmitting only on a lost packet. The server and client qc MUST expect multiple read/write requests from the engine.\r\n\r\nServer changes: The server's qc is mostly the same, though there are a few extensions as follows:\r\n\r\nvoid(float index, float type, .void field) clientstat = #232;\r\n\tSet up an auto-sent player stat. Client's get thier own fields sent to them.\r\n\tThis affects all clients.\r\n\tThere are two stat namespaces. Strings and numbers. Strings use independant indexes from numbers. Eg, stat 64 can hold \"hello world\" and the number 53.6 at the same time, depending on which you query.\r\n\tIndex should not be less than 32. An engine is not required to support more than 128.\r\n\tSending a float is more efficient if it is rounded in advance. However, if it is not rounded, the stat will be sent with full precision.\r\n\tType is a value equating to the ev_ values found in qcc to dictate types. Valid ones are:\r\n\t\t1: string\r\n\t\t2: float (if rounded between 0 and 255, this is sent compacted)\r\n\t\t3: vector (actually registers 3 consecutive floats. Note rounding efficiencies.)\r\n\t\t4: entity (actually the server's entity number, to match .entnum on the client, clients should read this as a whole number).\r\n\t\t8: integer (applicable only if your qcc and qcvm support integer types).\r\n\tCertain other type values have meaning, but are not practical and thus are not valid arguments.\r\n\r\nvoid(float index, float type, string globalname) globalstat = #233;\r\n\tSets up a stat based upon a global.\r\n\tWorks the same way as clientstat, except that the global is passed by way of the name of the global, so beware of typos.\r\n\tAll clients will receive the same stat value.\r\n\r\n.float(entity viewer, float sendflags) SendEntity;\r\n\tField used to store which function is responsible for sending the current (self) entity data.\r\n\tIf it returns false, the entity is not sent to that player. Removing it if the client already has a version.\r\n\tIn certain cases, viewer may be set to world. It is not recommended that you use viewer for anything.\r\n\tsendflags contains the send flags mask that have been changed since the client last received a packet.\r\n\tIt is valid to set additional SendFlags inside this function.\r\n\r\n.float SendFlags;\r\n\tThis field contains a bitmask to state which fields have been changed. The ssqc must add additional bits to this field when some transmitted field is changed.\r\n\tAs this is a float, the limit is 24 bits/6 nibbles/0xffffff. Values higher than that are unreliable and are not supported. The engine makes no assumptions other than this.\r\n\tUpdate this field each time you update the entity, by using the | operator to add bits. Do not use straight assignments (except for 0xffffff). Do not read this field other than to add additional bits to its value.\r\n\tThe server periodically resets this field to empty (after copying off the bits).\r\n\r\nFIXME: should pvsflags not be core?\r\n.float pvsflags;\r\n\tOne of:\r\n\t\tPVSF_NORMALPVS=0\tThis gives standard behaviour for the engine.\r\n\t\tPVSF_NOTRACECHECK=1\tThis gives standard behaviour even if the engine supports traceline cull checking.\r\n\t\tPVSF_USEPHS=2\t\tThis states that the entity should be sent if you're in an area that could possibly hear sounds that the entity makes. An expanded PVS. Not mandatory.\r\n\t\tPVSF_IGNOREPVS=3\tThis states that the entity.\r\n\tOptional:\r\n\t\tPVSF_NOREMOVE=128\tThis states that once known, an entity should not be removed when it becomes obscured. It is still removed when the ent is removed. This is only for entities sent via SendEntity.\r\n\r\nlegacy:\r\n.float Version;\r\n\tThis field may be supported purely for legacy mods. Server engines may choose to not implement this.\r\n\tWhen bumped, counts as SendFlags=0xffffff instead.\r\n\tWill be phased out hopefully.\r\n\tWhile logically simple, it has potential uptime issues.\r\n\tEngines are discouraged from implementing this field.\r\n\t\r\n\r\nThe SendEntity function should issue writebytes and stuff to send the entity data to the client. This includes origin, angle and stuff. You can send whatever you wish in there, encoded however you see fit. It will be called by the server each time the Version value changes and is visible to the player.\r\n\r\nPrediction, how it works: The client maintains a number of movement packets. Coceptually, it bounces them off the server. The echo it recieves is it's entity with the movement command applied to it. It also needs a sequence bounced too, which is performed by the engine (the ssqc never knows the sequence numbers), and the server only bounces it. So the client knows the last input state applied, and is able to reapply all the additional input to the servers state. This happens inside the csqc by the csqc requesting inputs and working on globals to figure out where the ent should be. Prediction errors are still likly so the csqc should apply some sort of iterpolation to slide the view to it's corrected position rather than snapping it. Input packets contain fixed info in the base specification where the csqc is expected to use clientcommands for anything more complex. Note that the sequence numbers could potentially be based on time rather than framerate\r\n\r\nDifferences between Quake and QuakeWorld: The csqc favours the Quake builtin behaviour. It does not use message levels in the engine, nor does it unconditionally print in the dprint builtin. It does use the QuakeWorld player physics model\r\n\r\nCSQC entry points:\r\n\r\nvoid() CSQC_Init;\r\n\tThis QC function is called by the engine when the csqc is first loaded.\r\n\r\nvoid() CSQC_Shutdown;\r\n\tCalled when the csqc is shutting down cleanly (not called if there was some sort of error).\r\n\r\nvoid() CSQC_WorldLoaded;\r\n\tCalled when the client has loaded a new world model.\r\n\tUse getentitytoken to read in the entities from the bsp in order to spawn client-only entities (like teleporters).\r\n\r\nvoid(float width, float height, float menushown) CSQC_UpdateView;\r\n\tCalled when the engine wants the csqc to update the player's view.\r\n\tThis is called once a frame, and must update the entire screen.\r\n\tIf menushown is false, it must also draw huds and scoreboards as required (menushown is provided so that you do not end up with two layers of userinterface obscuring each other).\r\n\r\nvoid(string cmd) CSQC_Parse_StuffCmd;\r\n\tCalled whenever the engine receives an svc_stuffcmd. Instead of executing it immediatly on the console, it is instead passed to the csqc for correct handling. This could be passed on to localcmd, for example (the string includes a new line, and could be multiple lines).\r\n\r\nvoid(string text) CSQC_Parse_CenterPrint;\r\n\tCalled when the client receives an svc_centerprint. The csqc either draws it's own menu in response, or passes it on to the cprint builtin.\r\n\r\nvoid(string text, float msgtype) CSQC_Parse_Print;\r\n\tCalled when the client receives a complete console print line.\r\n\tThe csqc either draws it's own menu in response, or passes it on to the print builtin.\r\n\tNOTE: The engine will collect multiple individual prints into a single combined line.\r\n\tmsgtype is a hint. Regular prints will have a lower value. This matches QW print levels.\r\n\tAn NQ engine will use only 2 or 3, based on the leading character. QW engines can use 0-3 via the print level. 3 is chat.\r\n\tThis string may contain additional markup, so long as the string can be printed correctly via print, centerprint, or drawstring.\r\n\r\nfloat(float event, float parama, float paramb) CSQC_InputEvent;\r\n\tevent is one of: 0:key pressed, 1:key released, 2:mouse move.\r\n\tThis is called if setwantskeys was last called with the parameter true.\r\n\tThis func returns a value. \"false\" signalize that engine *should* take care of the keypress or mouse move, a return value of true will make the engine otherwise ignore the event ever happened. This helps to use only keys that are really needed.\r\n\tWith keyboard events, parama is set to the keycode. The key values are as in the menu.dat (and DarkPlaces), and consistant between engines.\r\n\tWith mouse movement events, parama contains the X motion of the mouse, while paramb contains the Y motion of the mouse.\r\n\tTo implement a mouse cursor, update your internal mouse cursor pos based on the params, and return true (to stop it from looking around). Remember to draw the mouse cursor, and clip it to the screen.\r\n\r\nfloat(string cmd) CSQC_ConsoleCommand;\r\n\tThis qc function is called when a command registered with the registercommand builtin is used.\r\n\r\nvoid(float isnew) CSQC_Ent_Update;\r\n\tCalled when an entity is recieved from the server. isnew is set if the engine spawned a new entity. The engine is responsible for assigning spawning entities, and must call with the same one, of course. The entnum field will be set before this is called for matching with the writeentity builtin on the server.\r\n\tThe entity reference is passed to the csqc in the traditional 'self' builtin.\r\n\r\nfloat(float entnum, float channel, string soundname, float volume, float attenuation) CSQC_Event_Sound;\r\n\tCalled when a sound event is received from the server.\r\n\tSelf is set to the entity that the sound was centered upon if known. World if not (latency/packetloss/pvs culling).\r\n\r\nvoid() CSQC_Remove;\r\n\tCalled when an entity was removed/hidden on the server. The self global refers to the csqc's engine allocated entity (even if you removed it from the csqc already - be warned). The csqc is assumed to call the remove builtin inside this function (you should either remove it, or clear the entnum feld). If you want to leave gibs around, you're free to do so. Just fade them out inside the csqc please, or people will complain that it's too messy.\r\n\r\nNote: All global functions are entirely optional. Even CSQC_UpdateView.\r\nThere is absolutely no provision for csqc-parsing of temp entities. Please use entities as events.\r\n\r\n\r\n\r\n\r\nCSQC global variables: \r\nThis is more QC style than real documentation. These all do the same thing as on the server where names match. New globals are commented.\r\nThis list is available in easy QC form here: http://fteqw.svn.sourceforge.net/viewvc/fteqw/trunk/quakec/csqctest/src/cs/system.qc (requires FTEQCC or FrikQCC to compile due to #ifdef)\r\n\r\nentity\t\tself;\r\nentity\t\tother;\r\nentity\t\tworld;\t\t//the null entity\r\n\r\nfloat\t\ttime;\t\t//predicted interpolated time, kept in sync with the server\r\nfloat\t\tcltime;\t\t//local time, increases independantly of the game in progress.\r\nfloat\t\tframetime;      //time since last CSQC_UpdateView;\r\n\r\nfloat\t\tplayer_localentnum;     //the entnum (can change when svc_setview is received. 0 is unknown)\r\nfloat\t\tplayer_localnum;        //the playernum (0 to maxclients-1)\r\nfloat\t\tmaxclients;     //a constant filled in by the engine. gah, portability eh?\r\n\r\nfloat\t\tclientcommandframe;     //player movement (getinputstate(clientcommandframe-1) is the most recent available full frame)\r\nfloat\t\tservercommandframe;     //clientframe echoed off the server. By the time the client receives this, the command has been applied. Thus the first frame that needs predicting is servercommandframe+1. If this is 0, then prediction is not supported in the current network protocol (demos, legacy server - depends how csqc is authorised).\r\n\r\nstring\t\tmapname;        //the map as found in the map command. Will not contain non-filename charactures. (expected to be used with fopen type things). Contains no directory or extension information.\r\n\r\nfloat\t\tintermission;\t//set on receipt of an svc_intermission\r\n\r\nvector\t\tv_forward, v_up, v_right;\r\nvector\t\tview_angles;\t//set at the start of each frame to state the current view angles.\r\n\r\n// set by traceline / tracebox\r\n\r\nfloat\t\ttrace_allsolid;\r\nfloat\t\ttrace_startsolid;\r\nfloat\t\ttrace_fraction;\r\nvector\t\ttrace_endpos;\r\nvector\t\ttrace_plane_normal;\r\nfloat\t\ttrace_plane_dist;\r\nentity\t\ttrace_ent;\r\nfloat\t\ttrace_inopen;\r\nfloat\t\ttrace_inwater;\r\n\r\n//retrieved from the current movement commands (read by player physics), set via getinputstate\r\n\r\nfloat\t\tinput_timelength;\r\nvector\t\tinput_angles;\r\nvector\t\tinput_movevalues;       //forwards, right, up.\r\nfloat\t\tinput_buttons;          //attack, use, jump (default physics only uses jump)\r\nfloat\t\tinput_impulse;\r\n\r\nCSQC Field Variables:\r\n\r\n//\r\n// system fields (*** = do not set in prog code, maintained by C code)\r\n//\r\n.float\t\tmodelindex; \t// *** model index in the precached list\r\n.vector\t\tabsmin, absmax;\t// *** origin + mins / maxs\r\n\r\n.float\t\tentnum;\t// *** the ent number as on the server\r\n.float\t\tdrawmask;\r\n.void()\t\tpredraw;\r\n\r\n.float\t\tmovetype;\t//FIXME: should this be implemented?\r\n.float\t\tsolid;\r\n\r\n.vector\t\torigin;\t\t// *** use setorigin to change this.\r\n.vector\t\toldorigin;\r\n.vector\t\tvelocity;\r\n.vector\t\tangles;\r\n.vector\t\tavelocity;\r\n.float\t\tpmove_flags;\t//used for prediction. Its contents must be preserved between calls. And reset whenever prediction is reverted.\r\n\r\n.string\t\tclassname;\t// for debugging engines. not really required.\r\n.float\t\trenderflags; //See RF constants\r\n.string\t\tmodel;\r\n.float\t\tframe;\r\n.float\t\tframe1time;\r\n.float\t\tframe2;\r\n.float\t\tframe2time;\r\n.float\t\tlerpfrac;\t//this is how much of frame2 should be used.\r\n.float\t\tskin;\r\n\r\n.float\t\teffects;\t//set via delta updates\r\n\r\n.vector\t\tmins, maxs; // bounding box extents reletive to origin\r\n.vector\t\tsize; // maxs - mins\r\n\r\n.void()\t\ttouch;\r\n.void()\t\tthink;\r\n.void()\t\tblocked;\t// for doors or plats, called when can't push other\r\n\r\n.float\t\tnextthink;\r\n\r\n.entity\t\tchain;\r\n\r\n.entity\t\tenemy;\r\n\r\n.float\t\tflags;\r\n\r\n.float\t\tcolormap;\r\n\r\n.entity\t\towner; // who launched a missile\r\n\r\nCSQC Constants:\r\n\r\nrenderflags field uses these:\r\nfloat RF_VIEWMODEL = 1;\r\n\tThe entity is never drawn in mirrors. In engines with realtime lighting, it casts no shadows.\r\n\tIt is drawn on the screen in the center, where the weapon would normally be.\r\n\tView model bobbing is applied to this entity.\r\n\r\nfloat RF_EXTERNALMODEL = 2;\r\n\tThe entity is appears in mirrors but not in the normal view. It does still cast shadows in engines with realtime lighting.\r\n\r\nfloat RF_DEPTHHACK = 4;\r\n\tThe entity appears closer to the view than normal, either by scaling it wierdly or by just using a depthrange. This will usually be found in conjunction with RF_VIEWMODEL\r\n\r\nfloat RF_USEAXIS = 16;\r\n\tWhen set, the entity will use the v_forward, v_right and v_up globals instead of it's angles field for orientation. Angles will be ignored compleatly.\r\n\tNote that to use this properly, you'll need to use the predraw function to set the globals, or to add the models individually.\r\n\tAn engine only needs to implement this if tags are supported.\r\n"
  },
  {
    "path": "specs/fonts.txt",
    "content": "FTE supports ttf fonts on the console.\r\nNote that they are only supported in gl-only builds, for the moment.\r\nUses freetype2, dynamically linked.\r\n\r\nNotes:\r\nUse gl_font to specify the font file name to load. Does not yet go through the FTE filesystem, so cannot be in any pak files, and gamedirs are ignored. c:/windows/fonts/cour.ttf gives courier.\r\nMost fonts seem to use 12 pixels as a minimum height for readability. FTE allows smaller, but you probably want vid_conautoscale to 1.5 (12/8 = 1.5), or greater, or vid_conheight set to some equivelent value.\r\nVariable width fonts are supported, the width of the font ignores vid_conwidth.\r\n^UXXXX can be used to print a unicode character on the console (capital U, lowercase is not recognised). U is hex between 0000 and ffff\r\nIf com_parseutf8 is set to 1, any text printed onto the console will be read as utf8 text. If there are any errors, parsing will be disabled until the next string.\r\nin_builtincharmap can be set to 0 in order to get FTE to enter characters according to their system keyboard map (only required in windows).\r\nThe quake charset is mapped to ^Ue000 through ^Ue0ff.\r\n^Ue100 upwards displays some hud images. This mechanism will likely change some time.\r\nLinks dynamically to FreeType. You can get a compatible version from http://gnuwin32.sourceforge.net/downlinks/freetype-bin-zip.php (freetype6.dll is the only file needed).\r\nIf you're compiling yourself and you have issues with FreeType2 dependancies, it can be disabled by tweeking bothdefs.h or by doing eg: 'make CFLAGS=-DNO_FREETYPE gl-rel'.\r\n"
  },
  {
    "path": "specs/fte_manifests.txt",
    "content": "Manifest files are small files that describe various attributes of the game, primarily branding and filesystem configuration for mods or standalone games.\r\nManifest files are useful for standalone games that need to distance themselves from other games using the same engine. They are also useful for auto-updating mods or for mod compilations where mods should not be downloaded up-front.\r\n\r\nFTE Manifest Files (.fmf) can either be invoked by opening them with FTE (set up a file association), or automatically via a default.fmf file in the path specified by the -basedir argument, or in the working directory (settable via shortcuts). A default.fmf file will override the engine's game determination routines completely.\r\nThese files are also supported by the Android port, where opening one will give an option to open with FTEDroid automatically (much like on a desktop OS), or in the webgl port with the url specified after a # in the url.\r\nThe gamedir command (eg: 'gamedir foo', or '-game foo' on the commandline) will attempt to load an fmf file with the specified name (ie: foo.fmf) from the current basedir. If the new manifest has a different 'game' value, the engine may ignore.\r\n\r\nEach line of a manifest file forms a single command. New lines denote new commands. Comments are C++-style // double forward slashes.\r\n\r\n\r\nExample:\r\n\r\n//example manifest file for Sock's 'In The Shadows' stealth mod. Because why not?\r\nFTEMANIFEST 1\r\nGAME quake\r\nNAME \"In The Shadows\"\r\nPROTOCOLNAME ITShadows\r\nRTCBROKER \"tls://master.frag-net.com:27950\"\r\nDEFAULTEXEC \"\"\r\n//UPDATEURL \"http://example.com/mods/its.fmf\"\r\nBASEGAME id1\r\nBASEGAME qw\r\nBASEGAME *fte\r\nGAMEDIR shadows\r\n//it would be nice if we could specify pak0.pak and pak1.pak here, alas doing so would violate id's various copyrights. instead, hope the user has a copy already installed.\r\n//this is a major pain for systems where there is no official installer (read: fte's webgl port, or android... or win64).\r\n//its pak0 can be downloaded from sock's public server\r\nARCHIVEDPACKAGE shadows/pak0.pak \"0xb1768147\" \"shadows/pak0.pak\" \"http://simonoc.com/files/maps/sp/its_demo_v1_1.zip\"\r\n-seta cl_forwardspeed 400\r\n+bind w +forward\r\n\r\n\r\n\r\nCommands:\r\n\r\nFTEMANIFEST 1\r\nSpecifies the actual version of the manifest file, and what the file actually is. Only valid as the first line. The version number is included for potential future use. Version 1 is the current version.\r\n\r\nGAME quake\r\nThis specifies the code-name of the game upon which your mod is based (and MAY be used to generate homedir paths, so must not contain weird chars).\r\nStandalone games should pick their own string to use here.\r\nMods for one of the games that FTE intrinsically understands may be automatically located via the system registry for an existing installation. This is useful when the fmf file has been downloaded and opened via a web browser.\r\nKnown game name (namely: quake, quake2, quake3, hexen2) may imply other settings if they were not otherwise specified.\r\nIt is NOT recommended to omit this, but if you do, 'quake' will be assumed.\r\n\r\nNAME \"Example Quake Mod\"\r\nThis specifies what the game should be referred to by various console prints etc. This should be the formal name of your mod.\r\n\r\nPROTOCOLNAME \"foo\"\r\nThis is how the game/mod should be identified to master servers. A unique value here helps prevent servers from other games being listed, as well as preventing other games from seeing this game/mod.\r\nThis should be reasonably human readable - master servers might show it publically. This actually gets written into the com_protocolname cvar. You should use the com_protocolversion cvar in your default.cfg if you want to exclude older versions, avoiding annoying numbers etc.\r\n\r\nRTCBROKER \"url\"\r\nThis should be a tls:// or tcp:// address that tells the engine where it can find a running instance of an 'ftemaster' program.\r\nThe broker program is used to allow client and target server to exchange address and port information in order to punch holes through NATs and Firewalls, allowing users to play without having to configure any of those monstrosities.\r\n\r\nDEFAULTEXEC \"\"\r\nIf specified, this is inserted before the game's normal default.cfg file, and can be used to override the engine's default cvar settings for mods where the default.cfg was originally written for a different engine (and thus omits certain settings). This should only be used for games where the default.cfg itself is not replacable. Standalone mods should not need to specify this, and should instead just put everything in the mod's default.cfg file.\r\nFor recognised games, the engine will automatically assume usable defaults, but its possible that a mod depends upon a setting that I overlooked.\r\nThis is obsolete, other than to disable the built-in overrides for known games. See the following - and + prefixes for easier usage.\r\n\r\n-set foo bar\r\nThe text following a leading minus are commands that are included before the mod's default.cfg, allowing to override engine behaviour without breaking mods that require a specific value. This mechanism is quite handy for mimicing other engines.\r\nAny commands may be used, but its best to limit yourself to set/seta/setfl, alias, and bind.\r\n\r\n+set foo bar\r\nThe text following a leading plus are commands that are included AFTER the mod's default.cfg, allowing you to stomp all over defaults that were stupid and flawed (or just outdated). This is useful for making quake's default settings and binds usable, but its generally better to just provide a new default.cfg instead.\r\nAny commands may be used, but its best to limit yourself to set/seta/setfl, alias, and bind.\r\n\r\nBASEGAME \"id1\"\r\nMultiple basegame lines can be specified. These are the core subdirectories that should always be loaded, even if the gamedir command was used to load a different mod.\r\nStandadlone games will typically specify only a single value.\r\nEach additional basegame will be favoured in preference to the prior ones.\r\n(note: 'basedir' refers to the path from which all game subdirs are found in, thus manifests use basegame to avoid ambiguity at the cost of consistancy)\r\nA basedir with a leading * is a private directory that will not be reported to clients nor appear in server browsers. This is typically used on fte-specific game directories, so that other-engine clients will not get confused.\r\n\r\nGAMEDIR \"mymod\"\r\nThese act almost exactly like basegame, except they flushed and replaced if the gamedir command is used. Additionally these are never implied by the 'game' command.\r\nThese will always have a higher preference than any BASEGAME subdir.\r\nThere are arguments both for and against a mod specifying its subdir as a basegame or as just a gamedir.\r\n\r\nDISABLEHOMEDIR 1\r\nIf non-zero, the game/mod will not attempt to use any directory within the user's homedir by default. This may cause issues if the base game was installed in some read-only system location like C:\\Program Files\\, and so should only be used for manually installed games (ones that contain just the exe+fmf file).\r\n\r\nUPDATEURL \"http://example.com/foo.fmf\"\r\nThis command gives the url from which to update the manifest file from. The updated manifest *MUST* contain the same url for the update to be accepted. Because of this, you should ensure that it is has a url that will be valid for as long as your mod will be relevent.\r\nThis replaces just the manifest, but because the manifest file specifies a list of packages that should be used, a new set of packages can be downloaded automatically, and this is the true power of the update url.\r\n\r\nDOWNLOADSURL \"https://updates.triptohell.info/downloadables.php?game=Q1\"\r\nThis command tells the engine where it can obtain a list of available packages. The user is able to enable/disable as they desire.\r\n\r\nINSTALL \"package1;package2\"\r\nThis command tells the engine which packages should be 'strongly encouraged' onto the user (ie: the user will be prompted to install them if they're missing). Naturally this should not be abused, however the user is better able to see what is happening (they must still accept installation).\r\n\r\nPACKAGE \"id1/pak0.pak\" 0x4f069cac \"http://mirror1.example.com/qsw106_pak0.pak\" \"http://mirror2.example.com/qsw106_pak0.pak\"\r\nWARNING: be sure to respect third-party copyrights.\r\nThis command, of which multiple can be specified, lists the packages which should automatically be downloaded.\r\nThe first name (in this case 'id1/pak0.pak') is the logical name of the package, including the gamedir in which it is to be found. The second argument is the CRC of the package's table of contents, and any additional arguments are alternative mirrors from which to download updated versions. The engine will randomize the order on load (for load balancing) and will try other mirrors if the any fails to provide a file.\r\nMultiple versions of the same package may be installed. This is handled via the crc. If a mirror provides a file who's crc does not match, the download will be ignored and another mirror will be tried.\r\nA CRC of '-' can be used if you really do not want to provide crcs. This suppresses all crc checks, and so is not recommended (if you instead use a different filename for each version, this is not always an issue). The CRC can be determined by using the wrong crc and fixing it up to the correct value that the engine claims that it should be.\r\nIf the user already has an actual id1/pak0.pak installed (ie: a prior install of quake), then an updated version will not be downloaded even if their crc does not match. Otherwise the downloaded file will be placed inside a 'dlcache' directory, with a crc postfix, allowing multiple versions.\r\nIf you are directly serving pk3 files, it is recommended to just compress the pk3.\r\nIf you are directly serving pak files, you will likely wish to ensure that gzip transfer compression is enabled on your mirrors for the files in question.\r\nIts recommended to place pk3 files directly on your mirrors as they are already compressed, which keeps things consistant and avoids stalls. However, if you use a single installation zip that you wish to reuse with manifest files, you can use ARCHIVEDPACKAGE instead.\r\n\r\nARCHIVEDPACKAGE \"id1/pak0.pak\" 0x4f069cac \"id1/pak0.pak\" \"http://mirror1.example.com/qsw106.zip\" \"http://mirror2.example.com/qsw106.zip\"\r\nMostly identical to the PACKAGE command, but with an extra argument before the mirror list.\r\nThe difference is that if the file is downloaded, it is assumed to be a zip archive from which to extract the package. The extra/third argument is the filename that is read from the zip.\r\nThis command is provided for compatibility with existing installation media from third parties who do not themselves support manifest files.\r\n\r\nMINVER FTE 4778\r\nThe manifest should not be used if the engine parsing it is a lower version than this. There is no default limit.\r\nThe first arg (ie: 'fte' in this case) specifies that the limit is specific to a specific fork or reimplementation of the file format.\r\nThe second arg should be an svn revision number (visible from the 'version' console command).\r\nWhen updating a manifest, the engine will ignore the new manifest if its minver is too high. This can be used to avoid breaking installations if the user tries running the mod with too old an engine.\r\n\r\nMAXVER FTE 4778\r\nThe manifest should not be used if the engine parsing it is a higher version than this. See minver for argument info.\r\nI'm not sure how useful this will actually be, but it is included for completeness.\r\n\r\nMAINCONFIG \"fte.cfg\"\r\nIsolates fte's config files from those of other engines that might be running this mod. Set to \"config.cfg\" to annoy users with endless conflicts.\r\n\r\n\r\nConsole Commands:\r\n/gamedir foo\r\nThis console command will read foo.fmf from the current basedir and load that instead. The basedir will not be changed to match the installation path of the specified game.\r\nIf there was no fmf file with that name, it will load the default.fmf file from that basedir instead, or try to guess which game is installed in the current basedir instead. From this default.fmf file, the gamedir attributes will be stripped and 'GAMEDIR foo' will be inserted internally.\r\n/fs_showmanifest\r\nThis console command displays the effective/active manifest file that is currently in effect.\r\n/fs_changegame\r\nThis command recognises various game names and will switch the basedir in order to try loading it (the game may be found via a prompt for the game's basedir, via a cached result of that prompt, or via known registry keys from that game's installer).\r\nIf the specified game is not recognised, the argument will be assumed to be a manifest named via a url or a system path.\r\n\r\nPossible Future Direction:\r\nARCHIVEDPACKAGE should support multiple files without redownloading.\r\nARCHIVEDPACKAGE needs some way to not care about crcs/filenames, and extract+use all included files. May be better to just decompress the zip and directly use it with a path prefix.\r\nUninstallation/auto-pruning.\r\nExtra polish.\r\nMultiple mods specified within a single fmf.\r\nIndividual files (ie: maps) that can be downloaded on demand?\r\nOther stuff.\r\nScan for a new manifest instead of merely switching gamedirs.\r\nReadme/license/credits info is needed.\r\n"
  },
  {
    "path": "specs/glsl.md",
    "content": "\nFTEQW uses [GLSL](https://www.khronos.org/opengl/wiki/Core_Language_%28GLSL%29)\nfor all of its shaders, with the legacy\n[Quake 3 shader format](https://graphics.stanford.edu/courses/cs448-00-spring/q3ashader_manual.pdf)\nacting as the glue between the raw textures and the GLSL.\n\nA standard \"wall\" Q3 shader in FTEQW might be located at\n`mygame/scripts/brick.shader` and might look like this:\n\n```glsl\n// material name used in level file\nbrick/wall001a\n{\n\t// glsl program to use\n\tprogram defaultwall\n\n\t// diffuse texture file path, relative to gamedir\n\tdiffusemap textures/brick/wall001a.tga\n}\n```\n\n`defaultwall` is the embedded GLSL shader in FTEQW for anything that is not\nwater, sky, sprites, or models. These shader files are present in\n[FTEQW's source code](https://github.com/fte-team/fteqw/tree/master/engine/shaders/glsl),\nbut you can also dump them with the console command `r_dumpshaders`. `diffusemap`\nis an FTEQW-specific shortcut that passes the texture name into the GLSL shader\nas the diffuse sampler.\n\nHere is an incomplete list of the FTEQW shortcuts for passing samplers to GLSL\nshaders:\n\n- `diffusemap`: Used for the base texture.\n- `fullbrightmap`: Used for glowing pixels.\n- `specularmap`: Used for specular reflections.\n- `normalmap`: Used for bump mapping.\n- `lightmap`: Used for adding baked lighting to a surface.\n\nHere is an incomplete list of the default shaders, and their uses:\n\n- `defaultskin.glsl`: Used for models and their skins.\n- `defaultsky.glsl`: Used for Quake's scrolling skies.\n- `defaultskybox.glsl`: Used for Quake II and Half-Life's skyboxes.\n- `defaultsprite.glsl`: Used for particles and sprites.\n- `defaultwall.glsl`: Used for most level geometry.\n- `defaultwarp.glsl`: Used for Quake's turbulent water surfaces.\n\nThese can be used in any Q3 shader with `program <name>`. Any Q3 shader file\nwith the extension `.shader` in the `scripts` folder of your game or mod will be\nautomatically loaded by the engine.\n\n## Example Code\n\nHere is an example of a very simple `defaultwall.glsl` replacement. It only takes\na diffuse sampler, an optional fullbrightmap, and a lightmap:\n\n```glsl\n!!ver 130 460\n!!permu FULLBRIGHT\n!!permu FOG\n!!permu LIGHTSTYLED\n!!samps diffuse lightmap fullbright\n\nvarying vec2 txc;\nvarying vec2 lmc;\n\n#include \"sys/defs.h\"\n#include \"sys/fog.h\"\n\n#ifdef VERTEX_SHADER\n\tvoid main()\n\t{\n\t\ttxc = v_texcoord;\n\t\tlmc = v_lmcoord;\n\n\t\tgl_Position = ftetransform();\n\t}\n#endif\n\n#ifdef FRAGMENT_SHADER\n\tvoid main()\n\t{\n\t\t// diffuse sampler\n\t\tvec4 diffuse = texture2D(s_diffuse, txc);\n\n\t\t// apply lightmap\n\t\tvec3 lightmaps = (texture2D(s_lightmap, lmc) * e_lmscale).rgb;\n\t\tdiffuse.rgb *= lightmaps.rgb;\n\n\t\t// apply brightmap\n\t\t#if defined(FULLBRIGHT)\n\t\t\tvec4 brightmap = texture2D(s_fullbright, txc);\n\t\t\tdiffuse = brightmap * brightmap.a + diffuse * (1.0 - brightmap.a);\n\t\t#endif\n\n\t\t// final color\n\t\tgl_FragColor = fog4(diffuse * e_colourident);\n\t}\n#endif\n```\n"
  },
  {
    "path": "specs/hosting.txt",
    "content": "TL;DR:\n\tServer: set `sv_public 1` for a public server. Otherwise don't. Make sure deathmatch or coop are set before switching to a suitable map. Or use the menus...\n\tClient: Use the menus to find+connect...\n\tIf stuff fails, try and set net_mtu to 1200 or so.\n\tThe rest of this file is only interesting if you're doing weird extra stuff involving other clients, particuarly QE.\n\t\n\t\nsv_port [IP:]UDPPORT:\n\tSets the udp port number(s) a server listens on, space-separated list if you want to specify multiple. Explicit IPs can be used for systems that need separate sockets for each interface to work around routing issues. Where possible these should be configured to accept inbound connections (see the ICE/RTC stuff)...\n\tSee also: net_enable_dtls\n\nsv_port_tcp [IP:]TCPPROT:\n\tLike sv_port, but for tcp connections.\n\tSets the tcp port number(s)\n\tThis is used for all tcp connections - be they tcp/tls, ws(s), http(s), qtv, etc.\n\tSee also: net_enable_tls\n\nsv_port_rtp /serverid:\n\tSpecifies a name for the server to be advertised under.\n\t(This activates an outbound TCP connection)\n\tYour clients can 'connect /serverid' to join your server via ICE/holepunching, if you're too lazy.\n\tMust also have a udp port open.\n\nsv_public:\n\t-1: don't accept new clients, don't respond to queries (but don't harm anyone already connected).\n\t0: don't send heartbeats, can be discovered/queried just fine for lan games.\n\t1: send heartbeats to the configured masters.\n\t2: use rtp with an auto-generated name instead of sending heartbeats (if the master's servers are unusable anyway, why heartbeat).\n\t\nsetmaster:\n\tThis is deprecated in favour of net_[qw]master[extra]# cvars.\n\tIts use will disable the built-in masters.\n\t\ncom_protocolname:\n\tThis cvar should be set via your mod's fmf file. It must match between client+server or the servers will be filtered out or otherwise warn/error about it.\n\nsv_playerslots:\n\tDefaults to 32 in deathmatch or coop, or 1 otherwise (disabling networking in singleplayer), but can potentially be set all the way up to 255. Requires a map change to update (which may kick people)\nmaxclients:\n\tPrevents more than this many active players on the server. Can be changed mid-game.\n\t\n\t\nQW:\n\tRegular quakeworld clients just need an open udp port to connect to. make sure your router has the port open.\n\tquakeworld traditionally uses port 27500 but most hosts run multiple such servers so port numbers are basically always given anyway.\n\tsv_listen:\n\t\t0: ignore qw client connection requests (without preventing nq/dp/qe requests)\n\t\t1: accept qw clients.\n\t\nNQ:\n\tNQ defaults to udp port 26000 instead of QW's port 27500.\n\tsv_listen_nq:\n\t\t0: doesn't accept nq clients\n\t\t1: favour compat. blocks protocol extensions.\n\t\t2: prevent possible denial of service attacks, but may misleadingly fail due to packetloss (just reconnect if it happens).\n\t\t\nQE:\n\t`connectqe IP:UDPPORT` can be used to connect a native fte client to a QE server. QE clients can also connect to FTE servers via its connect command (specify one of the udp ports, obviously).\n\tFor this to work properly, you MUST have a working dtls provider (read: windows users will need to find+enable the openssl plugin). Additionally you need to set up some PSK cvars correctly.\n\tRequired cvars:\n\t\tset `dtls_psk_hint id-quake-ex-dtls`\n\t\tset `dtls_psk_user id-quake-ex-dtls`\n\t\tset `dtls_psk_key 7a55b980c49b9ae4d4ffdb3513c2e471d94b14c22077ed86`\n\n\tServerside:\n\t\tset `net_enable_dtls 2` //or 3, if you want to force encryption on all connections.\n\t\tthe behaviour of `sv_listen_nq 2` is not required, as the client will have already passed dtls's challenge check so there's no need for a second.\n\t\t\n\tClientside:\n\t\tQE will not handle `connect dns:port` properly (ignoring the port bit). `connect ip:port` DOES work though.\n\t\tConnecting an FTE server to a QE server can be done with FTE's `connectqe ip:port` command, which makes assumptions to match QE.\n\t\tQE clients will generally block stuffcmds, so there may be some issues.\n\t\t\n\tNote that the playfab stuff does NOT work - although you can use FTE's RTC stuff as an equivelent, just that they're not mutually compatible.\n\t(Use of QE's playfab auth keys results in charges to id for each usage and are thus all hidden and secret and C&D/DMCA-ey. while it would theoretically be possible to intercept these messages to determine the keys and then use FTE's webrtc support with the appropriate handshakes, its best not to go there)\n\t\nDP:\n\tDefaults to port 26000, same as regular NQ.\n\tServerside:\n\t\tDP's protocols requires `sv_listen_dp 1` to be set on the fte server, otherwise it'll fall back to acting as a qw or nq client.\n\t\tsv_bigcoords is required for dpp7, but note that doing so breaks compat with many other engines. Not using it will generally fall back to the bjp3 protocol when using sv_listen_dp.\n\t\tpacketloss may result dp clients randomly getting stuck with vanilla NQ protocols. You may wish to set sv_listen_nq to 0 to prevent this, again beware of triggering other compat issues.\n\tClientside:\n\t\tJust use the connect command as normal.\n\t\t\nBrowser port:\n\tThe browser port can connect to servers hosting via ws:// and wss:// schemes. Be sure to specify port numbers when connecting (the TCP ones set via the server's sv_port_tcp).\n\tAlternatively, the browser port is able to use webrtc via a broker (net_ice_broker). This allows the browser port to even host games, while also using udp instead of tcp; resulting in fewer stalls.\n\tWhen told to connect to a plain IP :PORT address, the web port will convert that into a webrtc connection via the default broker (`/udp/ip:port`) - if the broker knows the server then it will relay your packets to the udp server in question so it doesn't need to open+forward any extra ports.\n\tYou will not be able to determine ping times before connecting, and dns lookups will not be performed, but the user's experience should otherwise be equivelent for public FTE servers. There's no relaying for servers (unless you want to provide TURN auth details), so it'll be as low latency as regular udp connections but will not work on legacy servers that lack support for the extra signalling.\n\t\n\tBrowser Serverside:\n\t\tYou can host games via webrtc, compatible with both other browsers and native FTE clients. Other engines will not be able to connect.\n\tNative Serverside:\n\t\tIf using ws:// then browsers will block connectivity when your site is hosted from an https:// scheme.\n\t\tIf using wss:// then your server MUST be using a certificate that the user's browser will accept. The browser does not inform about why the connection failed if its wrong. You may want to proxy ws(s)->ws via nginx/apache or whatever, but note that until X-Forwarded-For is supported this will mean you have to apply any IP-based bans in your wss proxy instead of in the game server.\n\t\tWebRTC/broker stuff can work with self-signed certificates, but may have more MTU isues. You should not need to worry about proxying, just make sure that inbound udp will work properly and the webrtc stuff will also work.\n\t\tHosting on windows will require reliable (d)tls support (read: openssl plugin).\n\nHexen2:\n\tFTE is *NOT* network-compatible with any other hexen2 engines. Follow the QW guidelines when running a hexen2 server, as FTE just treats it as a glorified mod.\n\t\nQ2:\n\tQ2 clients are only supported when running Q2 gamecode. Connectivity cvars then use the same cvars for QW.\n\t\nQ2E(playfab):\n\tplayfab is not supported. too many private crypto keys and ids with third-party charges for infrastructure upkeep.\nQ2E('lan'):\n\tUnlike QE, dtls is not required.\n\tQ2E defaults to udp port 5069\n\tFTE does not implement a lobby system - only to a level as required for Q2E compat.\n\t\n\tServerside, ensure udp port 5069 is open, and set net_enable_q2e:\n\t\t0: q2e clients will be ignored.\n\t\t1: q2e clients will be able to send requests to the udp port.\n\n\tClientside, use `connectq2e IP:PORT` to connect (assumes port 5069). They will not show in the server browser.\n\nQ3:\n\tQ3 clients are only supported when running Q3 gamecode. Connectivity cvars then use the same cvars for QW.\n\tClientside, just use the connect command as normal (or the q3 ui's server browser stuff).\n\nQizmo(tcp):\n\tTo connect to a qizmo or compatibleish quakeworld server over tcp instead of udp (eg because someone blocked UDP), use one of the following:\n\t\tconnecttcp ip:port\n\t\tconnect tcp://ip:port\n\n\tServers will need sv_port_tcp set, as well as net_enable_qizmo (which defaults to enabled anyway).\n\n(D)TLS:\n\tnet_enable_dtls:\n\t\t0: don't allow it.\n\t\t1: be prepared to use it when required for connectivity instead of failing outright.\n\t\t2: use dtls whenever the peer is believed to support it. Due to the abundance of other quake engines that lack dtls, this is probably the mode you should be using.\n\t\t3: use dtls. block connections that do not support it. The recommended setting, except for compatibility. Also blocks non-tls clients.\n\tnet_enable_tls:\n\t\t0: drop inboumd connections that look like tls (blocking wss too).\n\t\t1: allow tls.\n\t\t\n\tWindows warning:\n\t\tOn windows FTE uses microsoft's schannel tls+dtls system library. This library has numerous issues and even worse documentation.\n\t\tUsers should instead enable the openssh plugin. This will ensure tls+dtls actually work properly instead of being stuck with outdated cyphers and protocols that eg QE or the browser port will reject.\n\n\tTo connect to a server with a known fingerprint (eg to bypass other certificate checks, you can:\n\t\tconnect dtls://HOST:PORT/?fp=CERTIFICATEFINGERPRINT\n\tThe fingerprint can be obtained from serverinfo queries.\n\t\n\tNote: With dtls, the client provides security via certificate pinning rather than certificate signing (cert authorities tend not to sign certs for plain IPs, and we don't really have domain names for servers listed in server browsers).\n\tYou can add an ?fp=BASE64 after your server's port number to avoid fingerprint issues with your first-connection - or when sharing addresses (eg with qw:// uris).\n\nWS(S):\n\tTo connect to a websocket server, use a scheme like the following:\n\t\tconnect ws://HOST:PORT/\n\t\tconnect wss://HOST:PORT/\n\tOmitting the port number is not recommended\n\n\tServers will need sv_port_tcp set. net_enable_websockets should also be set, but at least it defaults to on.\n\tFor wss support, the server will additionally need net_enable_tls with a suitable certificate.\n\nWebRTC / ICE:\n\tftemaster can act as a broker, set net_ice_broker to a different one if frag-net ever goes down.\n\tThe broker relays basic metadata between the client+server allowing them to punch a hole.\n\tFTE's native implementation does not leak private IP addresses by default (nor will modern browsers).\n\tIf you wish to also hide your public address from the peer, set `net_ice_relayonly 1` and enable a TURN server.\n\n\tTURN:\n\t\tUsed as part of ICE/webrtc, This is used as a fallback when direct connections fail.\n\t\tFTE does not act as a TURN server at all, but does support TURN as a client via webrtc.\n\t\tset `net_ice_servers` to something like `turn:yourhost:port?user=key?auth=value` (your ip will still be visible to both the broker+relay, but not to the server/client you're connecting with). Ideally use a turn server near you to keep latency down because this will normally harm your latency.\n\t\t\n\tICE+WebRTC names are used somewhat interchangably in FTE. ICE is the part that establishes a connection while WebRTC says that its used in a way that web browsers find tollerable, so really the difference is that ICE has no sctp nor dtls requirement.\n\nQTV:\n\tClientside:\n\t\tUse the qtvplay commands to connect from a client to a qtv server (the qtvlist command can be used to list streams available on a server).\n\t\n\tServerside:\n\t\tSet net_enable_qtv and sv_port_tcp.\n\t\tYou will likely want to set qtv_password to prevent people from connecting to the server itself.\n\t\tYou will also need to set qtv_maxstreams to something other than 0.\n\t\tA server with these set will then allow qtv proxies (or unfortunately qtv clients) to connect directly to the server in order to stream to other clients.\n\t\tFIXME: make this more intuitive.\n\nHTTP:\n\tFTE servers can act as regular web servers. This is very basic compared to other servers, but can be used for downloads of mvds or other files.\n\tYou will need to set net_enable_http to use this, as well as a tcp port too obviously.\n\tFor https support, also set net_enable_tls.\n\tGamecode may generate pages/resources by implementing the HTTP_GeneratePage entrypoint in ssqc.\n\tYou can use the /demolist resource to view+download demos, otherwise resources effectively match those available"
  },
  {
    "path": "specs/mapcluster.txt",
    "content": "WARNING: this mode is not currently compiled in by default, in order to avoid potential unexpected resource usage.\r\nyou will need to enable it by enabling the SUBSERVERS preprocessor define at compile time.\r\n\r\n\r\n\r\n\r\n\r\nrandom useful commands (bash or console):\r\nmake sv-rel CFLAGS=-DSUBSERVERS\r\nmapcluster <enablelogins>\r\nssv 1 quit\r\nssv * say The Admin Is Awesome\r\nsv_serverip 127.0.0.1\r\n\r\nthe mapcluster command provides support for a single host running multiple maps as a single server cluster.\r\nthis is achieved by running multiple instances of the server in multiple processes, each with their own port numbers.\r\nthe initial server acts as a login server, which communicates with the various nodes.\r\nssqc is able to transfer clients from one node to another within the cluster, or send messages to the node hosting clients (or, if the node's id is explicitly known, can be used to directly communicate with other nodes).\r\n\r\n\r\n\r\nnode naming rules:\r\na node name takes the form of id:name\r\nif the id is empty, then it is considered unspecified, and the message/transfer will id used will be updated to any existing node with the same name (:e1m1 will pick ANY e1m1).\r\nif the id is 0, then it will NEVER use an existing node.\r\nif the id is invalid/0/empty, and the name is not blank, and the node is named via a transfer request, a new node will be started using that name as the new node's initial map.\r\nnote that \"0:start$\\\\foo\\\\bar\" can be used, in which case you will ALWAYS get a new instance, using maps/start.bsp and that infoget(startspot, \"foo\") will return \"bar\".\r\n\r\nevent destinations:\r\nevent destinations logically use player names. However, if the name takes the form \"\\\\5\" for example, then the message will be sent to the node with id 5, rather than to the node of a name player. You can use this to directly communicate server-to-server. If the node id does not exist, the event will be returned to the sender as an error - no new node will be created with this (fixme: it probably should be, but blurgh).\r\n\r\n\r\nssqc builtins:\r\nforceinfokey(player, \"*transfer\", \":e1m1\");\r\n  initiates a transfer request. the infokey will be cleared out upon failure, and the client will drop on success.\r\n  their parms will be transfered immediately, and will be available to the new server once the transfer completes.\r\n  WARNING: it is the QC's responsibility to terminate servers {localcmd(\"quit\\n\");} - the engine will not automatically do this.\r\n  If you are implementing an mmo-like game, you would presumably do this when there are no players and all monsters were respawned (such that restarting will not be noticed). Otherwise you may choose to terminate the node instantly or after some delay after the last player has left. If you only have limited maps, you might opt to never terminate any nodes - lucky you.\r\n\r\nvoid(string dest, string from, string cmd, string info) clusterevent = #;\r\n  sends a message to the destination.\r\n  if dest is blank, the event will be broadcast to EVERY node.\r\n  if the destination is prefixed with a \\ char, (eg: \"\\\\1\"), then the message is sent to node id 1.\r\n  if the destination is a player name, will send to whichever node that player is currently on. the receiving server is expected to handle the event on behalf of the player (perhaps by forwarding to csqc, or just doing a sprint or something).\r\n  if the destination appears invalid (and cmd is not prefixed with 'error:'), the cmd will be prefixed with error:, the dest+from args swapped, and the event will be sent back to the receiver.\r\n  info and cmd have meaning to the engine only if the receiver's ssqc does not handle the event.\r\n\r\nfloat serverid\r\n  this float contains the node's unique id. no two active nodes will use the same id, and can be used in replies to identify the node as the receipient of a transfer.\r\n\r\nfloat(string dest, string from, string cmd, string info) SV_ParseClusterEvent = {};\r\n  this function is called by the engine in response to a clusterevent call. the arguments match those of the prior call.\r\n  if you return false, the engine will attempt to handle it the best it can. which is limited and probably not ideal. any other result will cause the node to carry out no further action (meaning the from+cmd+info args are completely free-form if you do so).\r\n  the dest argument follows the restrictions/expectations specified for clusterevent.\r\n  this is handled by the receipient server, and as such the qc is expected to find the named player (if not direct-to-server) and handle the event itself.\r\n  the senders and receivers of these messages are all part of the same cluster, and so should all be considered trustworthy - unless you permit direct message injection. for this reason, you should *probably* ALWAYS use constants for the cmd argument, and thereby avoid surprises. this allows you to make assumptions about the dest+from+info fields more easily, simplifying your parsing.\r\n  it is possible for a client to have transfered off the server (or be in the process of transfering and thus ownership of the player's properties uncertain). in such cases, you are recommended to mimic the error: behaviour so that the sender can handle things in a consistent way.\r\n\r\n\r\ndebug/cheat commands (mods are expected to provide their own functionality for these things, and thus these are considered cheats to limit their use):\r\ncmd ssvtransfer :e1m1\r\n  transfers the player to the named node (see node naming rules), just like forceinfokey would.\r\n\r\ncmd ssvsay hello world\r\n  broadcasts the message to every single player on every single node (instead of merely the node the player is on).\r\n  (this does: clusterevent(\"\", self.netname, \"say\", args())  )\r\n\r\ncmd ssvjoin playername\r\n  attempts to teleport the player to the named player's server.\r\n  if the named player is already on the same node, teleports the player on top of the named player.\r\n  (this does: clusterevent(player, self.netname, \"join\", \"\"), the default join handler will reply with joinnode, and the handler of that will initiate a transfer request to the node specified by the joinnode's info string)\r\n\r\n\r\ngotchas:\r\nin order to invite another player, you will likely need to implement some sort of timeout serverside, in order to avoid issues with people's UIs ignoring the request (or not receiving it due to a player transfering at the time).\r\nyou probably shouldn't allow people to join others without mutual consent, to reduce trolling. probably you'll want the server of the person being joined in ultimate control of the join, if only because its nice and easy to pull another player to your node on account of the puller already knowing the destination's serverid.\r\n\r\ndue to players disconnecting or randomly transfering before your messages get to them, all of these messages should be considered unreliable.\r\nif you block transfers of one player, you can hopefully get away with a race condition between the reply and a timeout. for non-parm based things, you can use sql transaction support to ensure atomicity."
  },
  {
    "path": "specs/modmaking.txt",
    "content": "Making mods in FTE can be done the same way you make mods for any other engine.\r\nHowever, there are some nice tricks you can use to make it easier/faster/simpler.\r\n\r\nBuilt in compiler:\r\nFTEQW generally contains a built in version of FTEQCC. You can access it with the 'compile' console command.\r\nGenerally, you would make a mod 'egmod' as such:\r\nQuake/\r\nQuake/fteglqw.exe\r\nQuake/egmod/\r\nQuake/egmod/src/progs.src\r\nQuake/egmod/src/everythingelse.qc\r\nQuake/egmod/progs.dat (or qwprogs.dat)\r\nIf your progs.src contains an output of '../progs.dat' (at least a matching path) then the output will be directly runnable.\r\nThe 'compile' command will thus compile progs.src into progs.dat.\r\nYou can also 'compile csprogs.src' if you want to compile, eg, a csqc mod.\r\nAdditionally, the 'applycompile' basically does a quick savegame, effectively applying a newly compiled mod without restarting a map. This is useful if you just want to test a single repeatable function without booting players or anything - this won't automagically change any fields!\r\n\r\nMultiplayer testing:\r\nUnlike other QuakeWorld engines, FTE permits multiple instances of itself.\r\n(Note that other quakeworld engines will refuse to start up if FTE is already running, but FTE will start up if they are running, so if you want to test ezquake+FTE compatibility, start ezquake first).\r\nAlso...\r\nFTE supports splitscreen! This can simplify testing multiplayer when you can't be bothered switching between two clients.\r\nMostly this should be documented elsewhere, but here's the basics.\r\ncl_splitscreen can be set to 0, 1, 2, 3. This cvar says how many additional clients should be used. If you're running a listen server, this is the only cvar you 'need' to set.\r\nThere are a few other cvars, but for testing you can set up one half of your keyboard for the second player. Or you can make some bind to change the value of the 'cl_forcesplitclient' cvar, to force input to a different splitclient.\r\n\r\nDebugging:\r\nFTE has a 'breakpoint' command.\r\nThis command accepts either a function name, in which case it uses the first statement of that function, or a file+lineno pair.\r\nWhen the breakpoint is hit, you'll get either just a print saying it was hit (once) or it'll bring up the editor...\r\nEditor? Yes, editor.\r\nIn order to debug, set developer 1, and make sure you have an .lno file matching your progs (this file is generated by any form of fteqcc).\r\nNote: A few builtins in FTE detect error conditions and force tracing on, hopefully along with a message saying why.\r\nYou can use the traceon builtin to force tracing on as a qc-coded breakpoint.\r\nf11 will single-step (step-into).\r\nf9 will toggle a breakpoint\r\nf5 will resume execution.\r\nf3 will bring up an inspection line. Type QC values to inspect stuff (or 'foo = 5' to assign to foo - doesn't support calls).\r\nescape will close the editor and resume execution until the next breakpoint.\r\nThere is limited console access. Please be very careful about the commands you exec in this state.\r\nDo note that while debugging, there is no network activity. People will time out if you debug for too long in a single frame.\r\nDedicated servers do not support a built in editor. If you set developer and hit a breakpoint, a dedicated server will simply print out the source code of each line which is executed, in a cheesy trace fashion.\r\nTo recap: developer 1+.lno file = step-by-step debugging/code tracing.\r\n\r\nCoredumps:\r\nIf there's a crash which FTE believes to be caused by QC code, FTE will generate a coredump in the current gamedir (generally this is in your home directory somewhere - note the path command to find out where).\r\nThese coredumps will contain information on locals within the functions that are still on the QC stack.\r\nYou can also force a core dump with the coredump_ssqc command."
  },
  {
    "path": "specs/multiprogs.txt",
    "content": "FTE_MULTIPROGS\r\n\r\nvoid() init; //called before ents are available (spawn() will fail, but you can use addprogs() here)\r\nvoid() initents; //called just before worldspawn, after ents are spawnable (spawn() works but addprogs() may fail)\r\n\r\nfloat thisprogs; //set by the engine. main progs will always be 0, addons will be non-zero and unique.\r\nfloat(string modname) addprogs = #202; //load another progs as well - may fail if too many fields are added.\r\n__variant(float progsno, string defname) externvalue = #203; //retrieve a value from another mod\r\nvoid(float progsno, __variant value, string defname) externset = #204; //set a value in another mod (don't use entire vectors, it'll only set the x part)\r\n\r\nAdditional cvars:\r\naddon0..15: explicitly name an addon progs to be loaded. roughly equivelent to calling addprogs at the end of the main prog's init() function.\r\n\r\nAdditional fteqcc keywords:\r\nshared: when prefixed in a type on a global def, the engine will propagate that global between progs. Caution: make sure its always used at the same global offset, so only use it in the shared defs.qc, before any differences. self, other, time will forcibly have this set, but it won't hurt to put it there (yet is the classic example of this keyword's use).\r\nextern: when prefixed on a function def, the function will be compiled successfully without a body. if you use FTE's progs7 format, it'll be auto-imported and be directly callable, otherwise you can define it with 'var' and use externvalue to initialise it before its callable (in one of the init funcs).\r\n__variant: type that stands for any, removes related type check compiler warnings/errors, limits operations to assignments only. may have issues with vectors.\r\n\r\n\r\n\r\nTo hijack/hook a function in the main progs:\r\n#pragma PROGS_DAT myaddon.dat\r\n#include \"defs.qc\"\r\nvar void() real_myfunc;\r\nvoid() replacement_myfunc =\r\n{\r\n\tdostuff();\r\n\treal_myfunc();\r\n\tdostuff();\r\n};\r\nvoid() dohooks =\r\n{\r\n\treal_myfunc = externvalue(0, \"myfunc\");\r\n\texternset(0, replacement_myfunc, \"myfunc\");\r\n};\r\n\r\n\r\n\r\nQuirks:\r\nspawn funcs will be initialised from progs 0 upwards. So the first progs with a function matching the ents classname spawns the ent.\r\nMonsters need CheckAttack and ClientObituary hooked.\r\nCan't mix crcs. NQ does not mix with QW.\r\nWeapons are messy as heck - ut uses a different object for each item in a player's inventory. qc does not (normally). changing the fire function is simple enough, but adding much more than that becomes messy.\r\nFields are shared between all mods. They'll be remapped as required, but this can also mean addprogs can fail if fields are already mapped - ie: if initents has been called, you can't add more fields.\r\nGlobals are NOT shared. The shared keyword on a global will mark that global's index as one to be copied each time control passes from one progs to another. Avoid passing variables via globals. This also limits you to 8 arguments per cross-progs call.\r\nsome optimisations can stop you from being able to use a progs as an addon. there is otherwise no format difference between main progs and addon progs. system defs must match.\r\nexternvalue/externset may find locals.\r\nexternset on constants may be unreliable as the constant may be stripped/merged, but on functions (which are default const) its reliable.\r\nYou can use frikqcc instead, but you can't use the keywords above (and no __variant means you need alternative copies of externvalue - externset can use an elipsis).\r\nFTE will already attempt to load addons based upon map format. See source for specifics.\r\nFTE will call a notification function each time an addon is loaded due to cvars or user request.\r\nYou cannot use spawn() or world or any entity fields inside the 'init' function. save it for initents. You should use this function only for hooking important functions.\r\ninitents permits the use of spawn(). spawned ents will be *after* world, even though worldspawn was not yet invoked.\r\nusing addprogs later than the main progs' init function can fail if too many(any) new fields are defined.\r\n\r\nnote that a great use of this extension is to add frikbots to any mod without modding it.\r\nexternvalue and externset do work for the current progs too. it can be used to implement arrays or some kind of scripts (fteqcc's builtin arrays are more efficient).\r\n"
  },
  {
    "path": "specs/particles.txt",
    "content": "Example:\r\nr_part blah\r\n{\r\n\ttexture textures/particles/myimage.tga\r\n\ttcoords 0 0 64 64 64\r\n\tdie 2\r\n\tscalefactor 1\r\n\tscale 20\r\n\talpha 0.5\r\n\trgb 255 0 0\r\n\trgbdelta 0 128 128\r\n\tspawnmode ball\r\n\tspawnorg 32\r\n}\r\n\r\nwill give transparent (0.5) red particles that will fade to fully white when they go fully transparent 2 seconds later.\r\nthey will spawn within a ball of 32 units radius.\r\n\r\n\r\nTypes:\r\nAlways try to include a type. The engine will guess what your particle is meant to be otherwise, based on textures and other nonsense. That stuff is generally unreliable, and probably not what you want.\r\n\r\nChaining:\r\nYou can add multiple particle descriptions to a single effect. The first should be 'r_part foo', and the others should be 'r_part +foo'. Particles without a + will reset the chain.\r\n\r\nEffect naming:\r\nEffects loaded by r_particledesc will be given an internal prefix, eg: foo.cfg.\r\nIf the gamecode explicitly states foo.bar, your foo.cfg will automatically be executed, and will automatically use the bar effect from it.\r\nThe effect can still be overriden from a custom config by explicitly naming the effect foo.bar - the effect bar in the config foo will not override this, but would override bar on its own.\r\n\r\nScale:\r\nscale values are defined in 1/4th qu, for some reason.\r\nscalefactor typically needs to be explicitly set to 1. this value affects how the particle scales with distance from view, rather than the actual size of the particle.\r\n\r\n\r\n\r\ntexture <TEXTURENAME>\r\n\tspecifies to use an image named TEXTURENAME for this effect.\r\n\r\nshader [shadername] [\\n{\\nshaderbody\\n}\\n]\r\n\tSpecifies to use a named shader.\r\n\tIf shaderbody is included (as a nested block on the following line) then that shader text will be used to create the shader if it doesn't otherwise exist.\r\n\tThis overrides blendmodes, as no default shader will need to be created.\r\n\r\ntcoords <s1> <t1> <s2> <t2> [tscale] [rsmax] [rsstep]\r\n\tspecifies to use a subsection of the image.\r\n\tif tscale is set, all units are divided by this. it is the virtual size of your texture. So a value of 1 means that your texture coords must be between 0 and 1. But if it properly matches your texture's size, the coords are in pixels.\r\n\tif rsmax is present, each particle will use a random image. These images must be on a single row in your particle font.\r\n\trsstep specifies the stride (gap from one to the next) in your particle font, and is only needed if rsmax is present and greater than 1.\r\n\r\natlas count_in_each_axis firstidx [last]\r\n\tAn alternative to tcoords.\r\n\tThe specified texture (or shader) is to be considered as a grid of sprites (x*x, where x is specified in that first arg).\r\n\tfirstidx specifies the first image to use (horizontal THEN vertical).\r\n\tlast specifies the last image to use (inclusive). The engine will pick one at random. They should not span multiple rows.\r\n\r\nrotation <startmin> <startmax> <speedmin> <speedmax>\r\n\tthe particle will start with a rotation rotated between startmin and startmax.\r\n\tIt will then rotate with some per-particle value randomly picked between the speedmin+speedmax values.\r\n\tShould NOT be used on beam particles.\r\n\r\nrotationstart <min> [max]\r\n\tobsolete, see rotation.\r\n\tthe particle will start with a rotation rotated between min and max.\r\n\tif max is missing, the particle will always start with the min value.\r\n\r\nrotationspeed <min> [max]\r\n\tobsolete, see rotation.\r\n\r\nbeamtexstep <value>\r\n\tonly valid if the effect is a beam.\r\n\tspecifies the number of quake units per beam texture repitition.\r\n\r\nbeamtexspeed <value>\r\n\tonly valid if the effect is a beam.\r\n\tcontrols how fast the texture scrolls on the beam.\r\n\r\nscale <min> [max]\r\n\tparticles will start with a diameter of this many quake units.\r\n\tactual scale will be randomly chosen between min and max (max defaults to equal if min is missing)\r\n\r\nscalerand <extra>\r\n\tobsolete\r\n\toverrides the scale max value\r\n\tactual scale will be now be randomly chosen between min and min+extra\r\n\r\nscalefactor <frac>\r\n\tcontrols how the particle scales with distance.\r\n\t1 makes the particle scale the same as anything else\r\n\t0 makes the particle not change size no matter how far it is\r\n\r\nstretchfactor <factor>\r\n\tcontrols how spark particles stretch according to their velocity.\r\n\tnegative values give fixed length sparks.\r\n\r\nscaledelta <value>\r\n\tcontrols how the particle scales over time\r\n\tspecifies the change in the particle scale per second.\r\n\r\nstep <min> <max>\r\n\ttrails/beams only\r\n\tspecifies the distance between each particle in the trail (or beam).\r\n\r\ncount <min> <max>\r\n\tpoint/box effects only (not trails or beams)\r\n\tspecifies how many particles are spawned per effect (some classic effects contain an extra scaler which is multiplied by the resulting value)\r\n\r\nalpha <value>\r\n\tspecifies the initial alpha value of the effect\r\n\r\nalpharand <value>\r\n\tspecifies a randomized additonal value added to each particle's initial alpha.\r\n\r\nalphadelta <value>\r\n\tspecifies how much the alpha value of the effect changes per second (subtracted)\r\n\r\ndie <maximum age> <minimum age>\r\n\tspecifies the maximum age of the particle\r\n\r\ndiesubrand <value>\r\n\tobsolete (set by die)\r\n\tspecifies the maximum starting age of the particle.\r\n\tbasically the particle will live up to this much less time. the alpha value will also be aged by the amount chosen by this value\r\n\r\nrandomvel <horiz> [vert]\r\n\tcontrols how fast the particle moves when it spawns (according to its spawn pattern). This works regardless of any requested velocities.\r\n\tif vert is not specified, horiz is used instead.\r\n\r\nveladd <value>\r\n\tcontrols how much of the effect's spawn velocity is used, can be greater than 1, or negative.\r\n\r\norgadd <value>\r\n\tbiases how much to add to the starting origin relative to the requested velocity.\r\n\r\norgbias <x> <y> <z>\r\n\tbiases the particle's origin by this absolute worldspace vector, regardless of spawn mode.\r\n\r\nvelbias\r\n\tbiases the particle's velocity by this absolute worldspace vector, regardless of spawn mode.\r\n\r\norgwrand\r\n\trandomised offset for the particle's origin, in worldspace.\r\n\r\nvelwrand\r\n\trandomised offset for the particle's origin, in worldspace.\r\n\r\n\r\n\r\nfriction <<xyz>|<xy> <z> | <x> <y> <z>>\r\n\tProportion of the particle's speed that should be lost from friction. Negative values are accepted.\r\n\r\ngravity <value>\r\n\tamount that the particle's velocity changes per second, in quake units.\r\n\r\nclipbounce <value>\r\n\thow much of the particle's velocity to use if the particle is clipped. See cliptype.\r\n\tDefaults to 0.8\r\n\r\ncliptype <effectname>\r\n\tSpecifies which new effect to spawn when the particle hits something.\r\n\tThe origin and velocity of the particle are used to spawn the new effect.\r\n\tThe clipbounce value is used as a scaler for the reflected velocity.\r\n\tIf the effect named is the effect itself, the particle will merely bounce, instead of spawning a new effect.\r\n\tFIXME: make default to bounce if clipbounce is set without cliptype.\r\n\r\nclipcount <count>\r\n\tThe scaler to use for the number of particles to spawn upon a clip event.\r\n\tOnly valid in conjunction with cliptype.\r\n\r\nassoc <effectname>\r\n\tSpecifies another effect to spawn at the same time that this effect is spawned.\r\n\tThus allowing two sets of particles from one effect.\r\n\r\ninwater <effectname>\r\n\tobsolete\r\n\tSpecifies a replacement effect to use when this one is spawned underwater.\r\n\tassoc used is the replacement effect. the assoc value from the replaced effect is ignored (this includes +foo chains).\r\n\r\nnotunderwater [content names]\r\n\tSpecifies that this particle should ONLY be spawned when out of water.\r\n\tThe particle will not spawn under water (this does not affect assoc chains).\r\n\tContent names are a space-separated list of: water slime lava sky solid fluid. Default is fluid if not specified.\r\n\tThe r_part_contentswitch cvar must be enabled for this to function correctly.\r\n\r\nunderwater [content names]\r\n\tSpecifies that this particle should ONLY be spawned when underwater.\r\n\tThe particle will not spawn if the spawn position is non-water (this does not affect assoc chains).\r\n\r\ncolorindex <index> [rand]\r\n\tSpecifies a palette index to spawn the particle with.\r\n\tThe index used is between index and index+rand.\r\n\toverrides the normal starting colours.\r\n\r\ncolorrand <rand>\r\n\tobsolete.\r\n\treplaces the [rand] part of the colorindex setting.\r\n\r\ncitracer\r\n\tonly valid if colorindex is set.\r\n\tadds a palette index between 0 and 3, based on the particle index in the effect or trail.\r\n\r\nred <value>\r\ngreen <value>\r\nblue <value>\r\nrgb <red> <green> <blue>\r\nrgb <value>\r\nrgbf <red> <green> <blue>\r\n\tSpecifies the initial red, green, and/or blue values for each particle.\r\n\tFully opaque is 255 or above.\r\n\tValues above 255 are valid, but will remain opaque until the value drops below 255 from the \r\ncolour deltas.\r\n\tThe rgbf form takes values scaled by 1 instead of 255.\r\n\r\nredrand <value>\r\ngreenrand <value>\r\nbluerand <value>\r\nrgbrand <red> <green> <blue>\r\nrgbrand <value>\r\nrgbrandf <red> <green> <blue>\r\n\tSpecifies how much extra red, green, and/or blue there may be for particles.\r\n\tThe initial colour will be multiplied by this amount before addition.\r\n\tEach componant is separately randomized. EG, red might  add nothing, while the full green is added, and only half the blue.\r\n\tFully opaque is 255 or above.\r\n\tThe rgbrandf form takes values scaled by 1 instead of 255.\r\n\r\nredrandsync <value>\r\ngreenrandsync <value>\r\nbluerandsync <value>\r\nrgbrandsync <red> <green> <blue>\r\nrgbrandsync <value>\r\n\tSpecifies how much extra red, green, and/or blue there may be for particles.\r\n\tThe initial colour will be multiplied by this amount before addition.\r\n\tComponants are syncronised. EG, if the full amount of red is added, the full amount of green and blue will also be added.\r\n\tFully opaque is 255 or above.\r\n\r\nreddelta <value>\r\ngreendelta <value>\r\nbluedelta <value>\r\nrgbdelta <red> <green> <blue>\r\nrgbdelta <value>\r\nrgbdeltaf <red> <green> <blue>\r\n\tSpecifies how much the red, green, and/or blue values of each particle change over time.\r\n\tThe value 255 is the value required to go from opaque to invisible in 1 second.\r\n\r\nrgbdeltatime <value>\r\n\tSpecifies for how long the particle may change colours for. After this many seconds, the particle may no longer change colours (delta becomes 0).\r\n\r\nrampmode <mode>\r\n\tmode may be one of:\r\n\tnone: uses rgb+rand+sync+delta+scale+scaledelta values.\r\n\tnearest(or absolute): the ramp overrides all colour+scale values. The effect moves from one absolute ramp index to the next.\r\n\tlerp: smoothly interpolates from one value to the next.\r\n\tdelta: uses rgb+rand+sync+scale, but not delta values. All delta values come from the colour ramp instead.\r\n\r\n\tif not none, the ramp index used is based upon the particle's age, its lifetime, and how many ramp elements there are.\r\n\r\nrampindexlist <idx1> [<idx2> [idx3 ...]]\r\n\tScale used is the currently set scale value.\r\n\tSpecifies a set of palette index values to use for the effect as part of the effect's colour ramp.\r\n\r\nrampindex <idx> <scale>\r\n\tSpecifies an individual palette index value and particle scale to use for the effect as part of the effect's colour ramp\r\n\r\nramp <red> <green> <blue> [alpha] [scale]\r\n\tSpecifies a ramp index in rgb terms, regardless of palette.\r\n\r\nstains <value>\r\n\t(Not supported in QSS)\r\n\tHow much the effect discolours the wall upon impact.\r\n\tThe stained colour is based upon the colour of the particle upon impact.\r\n\r\nblend <mode>\r\n\tIf the texture used is actually a shader then that shader's blend mode will take precidence.\r\n\tAs a general rule you should try to use only the premul blend modes (as well as atlasing).\r\n\tmode may be one of:\r\n\t\tadda: \r\n\t\taddc:\r\n\t\tsubtract:\r\n\t\tinvmoda:\r\n\t\tinvmodc:\r\n\t\tblendcolour:\r\n\t\tblendalpha:\r\n\t\tpremul_subtract:\r\n\t\tpremul_add:\r\n\t\tpremul_blend:\r\n\t\trtsmoke:\r\n\r\nspawnmode <mode> [arg1] [arg2]\r\n\tThis affects how particles are positioned when they first spawn, and their initial velocities.\r\n\tfor point effects, mode may be one of:\r\n\t\tbox: simple axially aligned box of particles.\r\n\t\tcircle: particles spawn within a ball with uniform distance from the center. none will appear in the middle.\r\n\t\t\targ1: percentage of the circle to cover. a value of 5 will go around the circle 5 separate times. this can be used for weird beam effects.\r\n\t\t\tareaspread: the radius of the ball\r\n\t\t\tareaspreadvert: the height of the ball. if 0, will be a flat circle\r\n\t\tball: particles spawn randomly within a ball.\r\n\t\t\tareaspread: the radius of the ball\r\n\t\t\tareaspreadvert: the height of the ball. if 0, will be a flat circle.\r\n\t\ttelebox: matches quake's telebox\r\n\t\tlavasplash: like chthon's lava splash\r\n\t\tuniformcircle: particles are spawned in a circle with uniform distance between and from the center. z=0.\r\n\t\tsyncfield: particles spawn at predictable locations based upon time within a rough sphere. Only useful for effects that are regenerated+replaced every frame.\r\n\t\tdistball:\r\n\t\t*default*: regular box. particles are spawned inside an axially aligned box.\r\n\r\n\tfor trail effects, mode may be one of:\r\n\t\tspiral: particles are given velocities perpendicular to the direction based on the distance moved.\r\n\t\ttracer: particles spawn with alternating horizontal velocities (a wake effect).\r\n\t\t*default*: particles spawn as a regular trail.\r\n\r\nspawnparam1 <value>\r\n\tobsolete. see spawnmode.\r\n\r\nspawnparam2 <value>\r\n\tobsolete. see spawnmode.\r\n\r\nup <value>\r\n\tobsoleted by orgbias.\r\n\tthe particle's starting origin is moved upwards by this amount (worldspace).\r\n\r\ntype <mode>\r\n\tHow the particles look.\r\n\tmode may be:\r\n\t\tbeam: valid only for trails. Particles form a single textured beam acting as nodes along it.\r\n\t\tspark: particles are lines, their length depending upon their speed.\r\n\t\tsparkfan: particles are non-planar triangle fans, their length depending upon their speed.\r\n\t\ttexturedspark: textured particles are aligned along their direction of movement, their length depending upon their speed, width equal to their scale.\r\n\t\tcdecal/decal: particles are spawned only upon bsp geometry. They are clipped by it.\r\n\t\tudecal: unclipped decal. exact semantics are subject to change.\r\n\t\tnormal/*default*: Particles are regular, rotating, 2d images.\r\n\r\nisbeam\r\n\tobsolete.\r\n\tplease use 'type beam' instead.\r\n\r\nclippeddecal <mask> [match]\r\n\timplies 'type decal'.\r\n\tThe two extra args allow you to spawn these decals ONLY on surfaces with matching surfaceflags.\r\n\tSeparation of mask+match allows you to create many descrete surface types instead of being limited to 32 bits/types.\r\n\r\nspawntime <value>\r\nspawnchance <value>\r\n\r\n\r\nemit <effectname>\r\n\tSpecifies the effect to periodically emit.\r\n\r\nemitinterval <min>\r\n\tParticles will not emit additional effects for this duration after emitting one.\r\n\r\nemitintervalrand <extra>\r\n\tFIXME: fold into emitinterval\r\n\t\r\n\r\nemitstart <seconds>\r\n\tPrevents the particle from emitting anything for this duration when it first spawns.\r\n\r\nspawnorg <horiz> [vert]\r\nspawnvel <horiz> [vert]\r\n\tobsolete\r\n\r\nviewspace [frac]\r\n\t(Not supported in QSS)\r\n\tSpecifies that this particle type should move relative to the camera.\r\n\tNot compatible with splitscreen.\r\n\tShould not normally be used in combination with clipping/bouncing.\r\n\r\nperframe\r\n\tapply inverse frametime to count (causes emits to be per frame).\r\n\r\naverageout\r\n\taverage trail points from start to end, useful with t_lightning, etc\r\n\r\nnostate\r\n\tCauses the particle system to ignore all state information.\r\n\r\nnospreadfirst\r\n\tdon't randomize org/vel for first generated particle\r\n\r\nnospreadlast\r\n\tdon't randomize org/vel for last generated particle\r\n\r\nsound <soundname> <vol> <attenuation> [pitch] [delay]\r\n\tWhen the effect is first spawned, the named sound will play with the given volume and attenuation at the center point. This may not work with all spawn methods, as it will ignore any count scales.\r\n\r\nlightradius <radius>\r\n\tSpawns a dlight when the effect is spawned.\r\n\tdlight is removed when radius drops to 0 or the age is exceeded.\r\n\tat this time it is not possible to override the corona/specular levels.\r\n\r\nlightradiusfade <radiuspersecond>\r\n\thow fast the light radius shrinks per second\r\n\r\nlightrgb <r> <g> <b>\r\n\tdlight rgb colours. 1=white. higher values can over-saturate.\r\n\r\nlightrgbfade <r/s> <g/s> <b/s>\r\n\thow fast lightrgb changes over time.\r\n\r\nlighttime <maxage>\r\n\tSpecifies the maximum lifetime of your dlight.\r\n\r\nlightcubemap <cubemapnum>\r\n\tvalue 0 means no cubemap.\r\n\totherwise with eg cubemap 5, uses image files cubemaps/5ft.tga, cubemaps/5bk.tga, etc.\r\n\tfixme: at the current time, the cubemap is world-aligned and cannot rotate.\r\n\r\nlightscales <ambient> <diffuse> <specular>\r\n\tmultipliers for the rtlight's various types of lighting\r\n\r\nlightshadows <castshadows>\r\n\t0 or 1, specifies whether the rtlight will cast shadows or not. Its faster if it doesn't.\r\n\r\nlightcorona <intensity> <scale>\r\n\r\nmodel <name> [options]\r\n\t(Not supported in QSS)\r\n\toptions are:\r\n\t\tframe=\r\n\t\tframestart=\r\n\t\tframecount=\r\n\t\tframeend=\r\n\t\tframes=\r\n\t\tframerate=\r\n\t\tskin=\r\n\t\talpha=\r\n\t\tscalemin=\r\n\t\tscalemax=\r\n\t\ttrail=\r\n\t\torient\r\n\t\tadditive\r\n\t\ttransparent\r\n\t\tfullbright\r\n\t\tshadow\r\n\t\tnoshadow\r\n\tspawns sprites or models that fly away with random angles and run through some frame sequence. handy for simple gib effects.\r\n\r\nsound <name> [options]\r\n\toptions are:\r\n\t\tvol=\r\n\t\tattn=\r\n\t\tpitch=\r\n\t\tdelay=\r\n\t\tweight=\r\n\tPlays a sound when the effect is spawned. Only ONE sound will be used, picked randomly from the included sounds according to their weights.\r\n\r\nspawnstain <radius> <r> <g> <b>\r\n\t(Not supported in QSS)\r\n\tControls whether a stain will be created at the same time as any particles (instead of depending upon impacts).\r\n\r\nrainfrequency <multiplier>\r\n\tSpecifies the interval between spawning new particle puffs on surfaces.\r\n\r\nflurry <magnitude>\r\n\tThese particles will periodically all change their direction, in a vauge attempt to approximate snow flurries.\r\n"
  },
  {
    "path": "specs/qc_extensions.txt",
    "content": "//default to ssqc, as that's the most common sort of mod in use.\r\n#ifndef CSQC\r\n#ifndef MENU\r\n#ifndef SSQC\r\n#define SSQC\r\n#endif\r\n#endif\r\n#endif\r\n\r\n//EXT_BITSHIFT\r\n//if quantity is negative, value is shifted right.\r\n//if quantity is positive, value is shifted left.\r\n//be warned that floats are not ideal for precision...\r\nfloat(float number, float quantity) bitshift = #218;\r\n\r\n//EXT_DIMENSION_VISIBILITY\r\n//if (player.dimension_see && other.dimension_seen) then allow entity to be visible to client. This affects which entities are visible to each other.\r\n//dimension_send is equivelent to seen, but applies to multicasts instead of entities.\r\n//suggestion: this extension can be used to filter ents based on csqc support or not.\r\n#ifdef SSQC\r\n.float dimension_see;\r\n.float dimension_seen;\r\nfloat dimension_send;\r\n#endif\r\n\r\n//EXT_DIMENSION_GHOST\r\n//if self.dimension_ghost specifies a bit which is not in dimension_seen, then the entity can still be seen but its alpha value will be multiplied by ghost_alpha (or 0.5 if that's set to 0).\r\n#ifdef SSQC\r\n.float dimension_ghost;\r\n.float dimension_ghost_alpha;\r\n#endif\r\n\r\n//EXT_DIMENSION_PHYSICS\r\n//provides two (float) bitfields that control whether two entities are allowed to collide, and in which direction.\r\n//logically: if (self.dimention_hit & other.dimension_solid) self.touch();\r\n//note that if one ent is solid one way, and not the other, one may move through while the other cannot (unless its already inside).\r\n.float dimension_solid;\r\n.float dimension_hit;\r\n\r\n\r\n\r\n//FTE_CALLTIMEOFDAY\r\n//originally an mvdsv extension, but in a builtin number that wasn't compatible (was 102).\r\n//calltimeofday invokes a timeofday function with arguments containing the time of day, oddly enough.\r\nvoid() calltimeofday = #231;\r\n//In order to make it more useful, this header includes a timeofday function which just grabs the values for you to use in your qc code straight after calling calltimeofday.\r\nfloat tod_sec, tod_min, tod_hour, tod_day, tod_mon, tod_year;\r\nstring tod_string;\r\n//This function is to facilitate use, and need not be modified.\r\nvoid(float s, float mi, float h, float d, float mo, float y, string tmp_timeofday) timeofday =\r\n{ tod_sec = s; tod_min = mi; tod_hour = h; tod_day = d; tod_mon = mo; tod_year = y; strunzone(tod_string); tod_string = strzone(tmp_timeofday);};\r\n\r\n//FTE_CSQC_HALFLIFE_MODELS\r\n//engine supports halflife models.\r\n#ifdef CSQC\r\n.float bonecontrol1;\r\n.float bonecontrol2;\r\n.float bonecontrol3;\r\n.float bonecontrol4;\r\n.float bonecontrol5; /*aka mouth*/\r\n.float subblendfrac;\r\n.float basesubblendfrac; /*depends upon FTE_CSQC_BASEFRAME*/\r\n#endif\r\n\r\n//FTE_CSQC_BASEFRAME\r\n#ifdef CSQC\r\n.float baseframe;\r\n.float baseframe2;\r\n.float baselerpfrac;\r\n.float baseframe1time;\r\n.float baseframe2time;\r\n.float basebone;\r\n#endif\r\n\r\n//FTE_CSQC_SKELETONOBJECTS\r\n//idea: Spike, LordHavoc\r\n//builtin definitions:\r\n#ifdef CSQC\r\nfloat(float modlindex) skel_create = #263; // create a skeleton (be sure to assign the value to .skeletonindex for use), returns skeleton index (1 or higher) on success, returns 0 on failure  (for example if the modelindex is not skeletal)\r\nfloat(float skel, entity ent, float modlindex, float retainfrac, float firstbone, float lastbone) skel_build = #264; // blend in a percentage of standard animation, 0 replaces entirely, 1 does nothing, 0.5 blends half, etc, and this only alters the bones in the specified range for which out of bounds values like 0,100000 are safe (uses .frame, .frame2, .lerpfrac, .frame1time, .frame2time, FTE_CSQC_BASEFRAME fields), returns skel on success, 0 on failure\r\nfloat(float skel) skel_get_numbones = #265; // returns how many bones exist in the created skeleton\r\nstring(float skel, float bonenum) skel_get_bonename = #266; // returns name of bone (as a tempstring)\r\nfloat(float skel, float bonenum) skel_get_boneparent = #267; // returns parent num for supplied bonenum, -1 if bonenum has no parent or bone does not exist (returned value is always less than bonenum, you can loop on this)\r\nfloat(float skel, string tagname) skel_find_bone = #268; // get number of bone with specified name, 0 on failure, tagindex (bonenum+1) on success, same as using gettagindex on the modelindex\r\nvector(float skel, float bonenum) skel_get_bonerel = #269; // get matrix of bone in skeleton relative to its parent - sets v_forward, v_right, v_up, returns origin (relative to parent bone)\r\nvector(float skel, float bonenum) skel_get_boneabs = #270; // get matrix of bone in skeleton in model space - sets v_forward, v_right, v_up, returns origin (relative to entity)\r\nvoid(float skel, float bonenum, vector org) skel_set_bone = #271; // set matrix of bone relative to its parent, reads v_forward, v_right, v_up, takes origin as parameter (relative to parent bone)\r\nvoid(float skel, float bonenum, vector org) skel_mul_bone = #272; // transform bone matrix (relative to its parent) by the supplied matrix in v_forward, v_right, v_up, takes origin as parameter (relative to parent bone)\r\nvoid(float skel, float startbone, float endbone, vector org) skel_mul_bones = #273; // transform bone matrices (relative to their parents) by the supplied matrix in v_forward, v_right, v_up, takes origin as parameter (relative to parent bones)\r\nvoid(float skeldst, float skelsrc, float startbone, float endbone) skel_copybones = #274; // copy bone matrices (relative to their parents) from one skeleton to another, useful for copying a skeleton to a corpse\r\nvoid(float skel) skel_delete = #275; // deletes skeleton at the beginning of the next frame (you can add the entity, delete the skeleton, renderscene, and it will still work)\r\nfloat(float modlindex, string framename) frameforname = #276; // finds number of a specified frame in the animation, returns -1 if no match found\r\nfloat(float modlindex, float framenum) frameduration = #277; // returns the intended play time (in seconds) of the specified framegroup, if it does not exist the result is 0, if it is a single frame it may be a small value around 0.1 or 0.\r\n//fields:\r\n.float skeletonindex; // active skeleton overriding standard animation on model\r\n.float frame; // primary framegroup animation (strength = 1 - lerpfrac)\r\n.float frame2; // secondary framegroup animation (strength = lerpfrac)\r\n.float lerpfrac; // strength of framegroup blend\r\n.float frame1time; // start time of framegroup animation\r\n.float frame2time; // start time of framegroup animation\r\n#endif\r\n//description:\r\n//this extension provides a way to do complex skeletal animation on an entity.\r\n//\r\n//see also DP_SKELETONOBJECTS (this extension implemented on server as well as client)\r\n//\r\n//notes:\r\n//each model contains its own skeleton, reusing a skeleton with incompatible models will yield garbage (or not render).\r\n//each model contains its own animation data, you can use animations from other model files (for example saving out all character animations as separate model files).\r\n//if an engine supports loading an animation-only file format such as .md5anim in FTEQW, it can be used to animate any model with a compatible skeleton.\r\n//proper use of this extension may require understanding matrix transforms (v_forward, v_right, v_up, origin), and you must keep in mind that v_right is negative for this purpose.\r\n//\r\n//features include:\r\n//multiple animations blended together.\r\n//animating a model with animations from another model with a compatible skeleton.\r\n//restricting animation blends to certain bones of a model - for example independent animation of legs, torso, head.\r\n//custom bone controllers - for example making eyes track a target location.\r\n\r\n\r\n//FTE_ENT_UNIQUESPAWNID\r\n//Provides a field that is incremented each time an entity is spawn()ed. Always changes so no two spawn()s have the same value.\r\n.float uniquespawnid;\r\n\r\n//FTE_EXTENDEDTEXTCODES\r\n\r\n\r\n//FTE_FORCEINFOKEY\r\nforceinfokey\r\n\r\n//FTE_GFX_QUAKE3SHADERS\r\n//The engine supports Q3-compatible shaders, to the best of its ability. Specifically on models.\r\n#ifdef CSQC\r\n//returns a skin value that can be used on entities with the given modelindex for the given .skin file name.\r\nfloat(float modelindex, string skinname) skinforname = #237;\r\n//returns a shader id which can be passed back into the engine.\r\nfloat(string shadername) shaderforname = #238;\r\n//if set on an entity, forces all surfaces of the entity to use that shader.\r\n.float forceshader;\r\n#endif\r\n\r\n//FTE_ISBACKBUFFERED\r\n//idea: Spike\r\n//Adds a builtin that allows you to see if data sent to a player will likely just be buffered.\r\n//This permits ensuring that it does not overflow and get kicked, nor block other data for long periods, if you wish to generate/send a lot of data at once.\r\nfloat(entity playerent) isbackbuffered = #233;\r\n\r\n\r\n//FTE_MEDIA_AVI\r\n//specifies that the playfilm command and associated functionality accepts .avi format files.\r\n\r\n//FTE_MEDIA_CIN\r\n//specifies that the playfilm command and associated functionality accepts .cin format (q2) files.\r\n\r\n//FTE_MEDIA_ROQ\r\n//specifies that the playfilm command and associated functionality accepts RoQ format (q3) files.\r\n\r\n//FTE_MULTIPROGS\r\n//multiprogs: aka mutators or addons.\r\n//Even if you do not use addons, you are able to use this extension to query globals and stuff by name within the current progs.\r\n//The actual form of these builtins:\r\n//__variant(float progsnum, string funcname, ...) externcall = #201; /*calls a function by name in another progs. Accepts up to 6 additional arguments to pass through.*/\r\n//__variant(float progsnum, string varname) externvalue = #203;\t/*retrieves a value from another progs by name*/\r\n//Convienience:\r\nfloat(float progsnum, string funcname, ...) externcall_f = #201;\r\nfloat(float progsnum, string varname) externvalue_f = #203;\r\nvector(float progsnum, string funcname, ...) externcall_v = #201; /*calls a function, expects a vector response*/\r\nvector(float progsnum, string varname) externvalue_v = #203;\r\n//other funcs\r\nvoid(float progsnum, string varname, ...) externset = #204;\t/*sets a global/function/field-index in a given progs. Argument is passed in the ... bit (variant, will expect a vector only if the changed var type is a vector)*/\r\nfloat(string fname) addprogs = #202; /*not always valid after initents has been called*/\r\nfloat thisprogs;\r\n//engine-callable functions\r\n//void() init; /*called as soon as the progs is loaded - warning don't call spawn(). Hook your parent's functions here.*/\r\n//void() initents; /*called once addons are already loaded. Init the rest of your code here.*/\r\n//void(string pname) AddAddonProgs; /*engine asks main progs to add an addon*/\r\n\r\n\r\n\r\n//FTE_MULTITHREADED\r\n//FTE provides a pseudo-threading mechanism (cooperative execution threads).\r\n//Such threads preserve their entire QC stack, but not the engine stack beforehand.\r\n//The only globals that are thread-local are self and other. All others might be changed by any other task between scheduling.\r\n//As threads are cooperative, so there is no dire need for mutexes so long as you make no assumptions over sleeps/forks.\r\n//Note that fork forks the thread, so you can return twice from the same function. Use abort to prevent such unexpected situations.\r\n//The engine requires that the thread return to it. Thus sleep conceptually acts as an if(!fork())abort(0); pair in situations where the engine depends upon return values.\r\n//Expected usage is to fork on an event, invoke sleep with periodic intervals triggering sub-events, and eventually calling abort on the forked thread.\r\n//A forked or sleeped thread should not return twice through engine code.\r\n//Be aware that entities can be freed while your thread is sleeping. Use the wasfreed builtin or uniquespawnid field to detect this situation.\r\n\r\nvoid(float secs) sleep = #212;\t/*Causes the current QC execution thread to stop and sleep for X secs. If the engine expected a return value from the current thread, then 0 is forcibly returned - consider: if (!fork())abort(5); if you need a different return value.*/\r\nfloat() fork = #210; /*called once, returns twice, with two different return values. If the engine expects a return value from the current thread, then it is the instance that returned 0 from fork that must still prove that return value.*/\r\nvoid(...) abort = #211; /*Kills the current thread, returning to the engine. If the engine expected you to return a value, you can pass one to the abort call.*/\r\n\r\n\r\n//FTE_MVD_PLAYBACK\r\n//no longer supported.\r\n\r\n//FTE_NPCCHAT\r\n//builtin chat\r\n//not documented.\r\n\r\n//FTE_QC_CHECKPVS\r\n//idea: Spike, Urre\r\n//builtin definitions:\r\nfloat checkpvs(vector viewpos, entity viewee) = #240;\r\n//description:\r\n//returns true if viewee can be seen from viewpos according to PVS data\r\n//note that viewpos is cached, and you'll get better results if you keep that argument the same when calling multiple times.\r\n\r\n//FTE_QC_MATCHCLIENTNAME\r\n//Given a rough client name, attempts to find a client with the matching name.\r\n//Can find multiple (if * is used, for example).\r\n//match is the name to match against\r\n//if matchnum is specified, 0 is the first, 1 is the second.\r\n//if matchnum is not present (only 1 arg) then only one client will be returned, and it'll fail if there were more matches.\r\nentity(string match, float matchnum) matchclients = #241;\r\nentity(string match) matchclient = #241;\r\n\r\n//FTE_QC_PAUSED\r\n//deprecated, use ZQ_QC_PAUSED instead.\r\n\r\n//FTE_QC_SENDPACKET\r\n//document me!\r\n/*\r\nsendpacket\r\n*/\r\n\r\n//FTE_QC_TRACETRIGGER\r\n//idea: often\r\n//Adds two new sorts of traces\r\n//.flags flags:\r\nfloat FL_FINDABLE_NONSOLID\t= 16384;\t//Affects findradius. SOLID_NOT entities may be returned if this is set in QW mods.\r\n//'nomonsters' flags for traceline:\r\nfloat MOVE_TRIGGERS\t= 16;\t//also hits triggers with the FL_FINDABLE_NONSOLID flag. Still hits everything else.\r\nfloat MOVE_EVERYTHING\t= 32;\t//hits SOLID_NOT entites and SOLID_TRIGGER, so long as they have FL_FINDABLE_NONSOLID set. Still hits everything else.\r\n\r\n//FTE_SOLID_LADDER\r\n//acts like SOLID_TRIGGER, except that players within the trigger will have their movement changed so they act as if on a ladder. Gravity is removed.\r\nfloat SOLID_LADDER = 20;\r\n\r\n//FTE_SQL\r\n//document me!\r\n/*\r\nsqlconnect\r\nsqldisconnect\r\nsqlopenquery\r\nsqlclosequery\r\nsqlreadfield\r\nsqlerror\r\nsqlescape\r\nsqlversion\r\nsqlreadfloat\r\n*/\r\n\r\n//FTE_STRINGS\r\n//idea: many\r\n//darkplaces implementation: KrimZon\r\n//builtin definitions:\r\nfloat(string str, string sub, float startpos) strstrofs = #221; // returns the offset into a string of the matching text, or -1 if not found, case sensitive\r\nfloat(string str, float ofs) str2chr = #222; // returns the character at the specified offset as an integer, or 0 if an invalid index, or byte value - 256 if the engine supports UTF8 and the byte is part of an extended character\r\nstring(float c, ...) chr2str = #223; // returns a string representing the character given, if the engine supports UTF8 this may be a multi-byte sequence (length may be more than 1) for characters over 127.\r\nstring(float ccase, float calpha, float cnum, string s, ...) strconv = #224; // reformat a string with special color characters in the font, DO NOT USE THIS ON UTF8 ENGINES (if you are lucky they will emit ^4 and such color codes instead), the parameter values are 0=same/1=lower/2=upper for ccase, 0=same/1=white/2=red/5=alternate/6=alternate-alternate for redalpha, 0=same/1=white/2=red/3=redspecial/4=whitespecial/5=alternate/6=alternate-alternate for rednum.\r\nstring(float chars, string s, ...) strpad = #225; // pad string with spaces to a specified length, < 0 = left padding, > 0 = right padding\r\nstring(string info, string key, string value, ...) infoadd = #226; // sets or adds a key/value pair to an infostring - note: forbidden characters are \\ and \"\r\nstring(string info, string key) infoget = #227; // gets a key/value pair in an infostring, returns value or null if not found\r\nfloat(string s1, string s2, float len) strncmp = #228; // compare two strings up to the specified number of characters, if their length differs and is within the specified limit the result will be negative, otherwise it is the difference in value of their first non-matching character.\r\nfloat(string s1, string s2) strcasecmp = #229; // compare two strings with case-insensitive matching, characters a-z are considered equivalent to the matching A-Z character, no other differences, and this does not consider special characters equal even if they look similar\r\nfloat(string s1, string s2, float len) strncasecmp = #230; // same as strcasecmp but with a length limit, see strncmp\r\n//string(string s, float start, float length) substring = #116; // see note below\r\n//description:\r\n//various string manipulation functions\r\n//note: substring also exists in FRIK_FILE but this extension adds negative start and length as valid cases (see note above), substring is consistent with the php 5.2.0 substr function (not 5.2.3 behavior)\r\n//substring returns a section of a string as a tempstring, if given negative\r\n// start the start is measured back from the end of the string, if given a\r\n// negative length the length is the offset back from the end of the string to\r\n// stop at, rather than being relative to start, if start is negative and\r\n// larger than length it is treated as 0.\r\n// examples of substring:\r\n// substring(\"blah\", -3, 3) returns \"lah\"\r\n// substring(\"blah\", 3, 3) returns \"h\"\r\n// substring(\"blah\", -10, 3) returns \"bla\"\r\n// substring(\"blah\", -10, -3) returns \"b\"\r\n\r\n\r\n//FTE_SV_REENTER\r\n//If ClientReEnter is defined, it will be called instead of the ClientConnect/PutClientInServer pair at map start.\r\n#ifdef CSQC\r\n//void() ClientReEnter;\r\n//self is defined to be the player entity with a copy of all saved fields from the previous map. This does not affect initial spawning.\r\n#endif\r\n\r\n\r\n\r\n//FTE_TE_STANDARDEFFECTBUILTINS\r\n//equivelent to DP_TE_STANDARDEFFECTBUILTINS, but applicable to QuakeWorld instead.\r\n//document me!\r\n/*\r\nte_gunshot\r\nte_spike\r\nte_superspike\r\nte_explosion\r\nte_tarexplosion\r\nte_wizspike\r\nte_knightspike\r\nte_lavasplash\r\nte_teleport\r\nte_lightning1\r\nte_lightning2\r\nte_lightning3\r\nte_lightningblood\r\nte_bloodqw\r\n*/\r\nvoid(vector org) te_gunshot = #418;\r\nvoid(vector org) te_spike = #419;\r\nvoid(vector org) te_superspike = #420;\r\nvoid(vector org) te_explosion = #421;\r\nvoid(vector org) te_tarexplosion = #420;\r\nvoid(vector org) te_wizspike = #423;\r\nvoid(vector org) te_knightspike = #424;\r\nvoid(vector org) te_lavasplash = #425;\r\nvoid(vector org) te_teleport = #426;\r\nvoid(entity own, vector start, vector end) te_lightning1 = #428;\r\nvoid(entity own, vector start, vector end) te_lightning2 = #429;\r\nvoid(entity own, vector start, vector end) te_lightning3 = #430;\r\nvoid(vector org, float count) te_bloodqw = #239;\r\nvoid(vector org) te_lightningblood = #219;\r\n"
  },
  {
    "path": "specs/replacementdeltas.txt",
    "content": "Establishment:\r\n\tUseful defines:\r\n\t\t#define PROTOCOL_VERSION_FTE\t\t(('F'<<0) + ('T'<<8) + ('E'<<16) + ('X' << 24))\r\n\t\t#define PEXT_FLOATCOORDS\t\t0x00008000\r\n\r\n\t\t#define PROTOCOL_VERSION_FTE2\t\t(('F'<<0) + ('T'<<8) + ('E'<<16) + ('2' << 24))\r\n\t\t#define PEXT2_REPLACEMENTDELTAS\t\t0x00000008\r\n\r\n\tQW: Extensions are established during the handshake.\r\n\t\tCL: \"\\xff\\xff\\xff\\xffgetchallenge\\n\"\r\n\t\tSV: \"\\xff\\xff\\xff\\xffc$challenge\\0<binary key value pairs>\"\r\n\t\tCL: \"\\xff\\xff\\xff\\xffconnect $ver $qport $challenge \\\"$userinfo\\\"\\n%#x %#x\\n%#x %#x\\n\", key1, value1, key2, value2\r\n\t\tSV: \"\\xff\\xff\\xff\\xffj\\n\"\r\n\t\tSee the 'both' part for additional info.\r\n\t\tThis handshake will be stripped by proxies. This is by design. It prevents qizmo and similar proxies from erroring on extensions.\r\n\t\t\r\n\tNQ: At connection, the server will stuffcmd 'cmd pext\\n'.\r\n\t\tThis must be explicitly parsed clientside, and effectively replaced with:\r\n\t\t\tva(\"cmd pext %#x %#x %#x %#x\\n\", PROTOCOL_VERSION_FTE, 0|PEXT_FLOATCOORDS, PROTOCOL_VERSION_FTE2, 0|PEXT2_REPLACEMENTDELTAS)\r\n\t\tYou are free to add additional/private extensions there, but they MUST be sent as KEY VALUE pairs. The value may be quoted and freely contain whitespace as required, but the key MUST be hex.\r\n\t\tSee the 'both' part for additional info.\r\n\r\n\tBoth:\r\n\t\tThe server will update the svc_serverinfo message to have this ordering:\r\n\t\tbyte svcqw_serverdata / svcnq_serverinfo \r\n\t\toptional long PROTOCOL_VERSION_FTE\r\n\t\toptional long flags\r\n\t\toptional long PROTOCOL_VERSION_FTE2\r\n\t\toptional long flags\r\n\t\tlong PROTOCOL_VERSION\r\n\t\t...\r\n\t\tThe protocol version should be parsed in a loop, stopping on unrecognised or non-extension-bits protocols.\r\n\t\tThe server must mask the extension bits with those that it supports, to avoid future extensions appearing active on servers that don't support them.\r\n\t\tIf the extension bits are not echoed from the server in the list, those extensions WILL NOT be used.\r\n\t\tThis mechanism allows the server to mask the client's extensions with the extensionbits that it supports, and to tell the client which protocol bits will actually be used for each map.\r\n\t\tThe server may kick the client if the client does not support extensions required by the server, just as a server sending an unsupported protocol version would result in a client getting kicked, but at least the server has the chance to run without.\r\n\t\tI've included PEXT_FLOATCOORDS above as an example of stating more than one set of extensions. You should remove those tokens if you do not wish to support that extension. FTE servers may very well not utilize it on every map, and it will only be active if sv_bigcoords 1 is set.\r\n\r\nUpdated network primitives:\r\n\tEntity numbers:\r\n\t\tEntities are no longer coded as just a short.\r\n\t\tInstead, they are coded as:\r\n\t\t\tshort. bit15=extend byte. bits14-0=entity number bits 14 - 0.\r\n\t\t\toptional byte. bit7=unused. bits6-0=entity number bits 21-15.\r\n\t\tThis coding gives compatibility with mods using writeshort up to ent 32767.\r\n\t\tCertain QuakeWorld mods already use writeshort hacks to pass entities less than 0. This practise should be discouraged. If you want a railgun effect, use TE=17 or a call to trailparticles.\r\n\tEntity deltas:\r\n\t\tNote that the entity coding below does not match regular entity numbers.\r\n\t\tNote that if the entity number is read as 0, then either it is the end of the message (remove=0) (do not read any flags), or ALL known entities must be discarded(remove=1).\r\n\t\tIf the remove flag is set, there are no flags, just the 2-3 bytes of entity number with the embedded remove flag.\r\n\t\tIn the description below, the flag name is followed by its bit (0-based) value. If the flag is set, the data is read.\r\n\r\n\t\tshort. bit15=REMOVE flag. bit14=EEXTEND flag. bits13-0=entity number bits 13 - 0.\r\n\t\tEEXTEND: byte. bits7-0=entity number bits 21-14.\r\n\t\tbyte - UF flag bits&0xff\r\n\t\tUF_EXTEND1(7): byte - UF flag bits&0xff00\r\n\t\tUF_EXTEND2(15): byte - UF flag bits&0xff0000\r\n\t\tUF_EXTEND3(23): byte - UF flag bits&0xff000000\r\n\r\n\t\tUF_FRAME(0): updated frame index:\r\n\t\t\tUF_16BIT: ushort\r\n\t\t\totherwise: byte\r\n\t\tUF_ORIGINXY(1): coord - origin[0]\r\n\t\tUF_ORIGINXY(1): coord - origin[1]\r\n\t\tUF_ORIGINZ(2):  coord - origin[2]\r\n\t\tUF_ANGLESXZ(3): angles[0], angles[2]\r\n\t\t\tUF_PREDINFO: shortangle, shortangle\r\n\t\t\totherwise: angle, angle\r\n\t\tUF_ANGLESY(4): angle[1]\r\n\t\t\tUF_PREDINFO: shortangle\r\n\t\t\totherwise: stdangle\r\n\t\tone of: entity effects\r\n\t\t\tUF_EFFECTS(5)|UF_EFFECTS2(29): ulong\r\n\t\t\tUF_EFFECTS2(29): ushort\r\n\t\t\tUF_EFFECTS(5): byte\r\n\t\t\totherwise: nothing\r\n\t\tUF_PREDINFO(6):\r\n\t\t\tthe bits listed here are stored within the predbits byte.\r\n\t\t\tmovetype and weaponframe are the only parts that are deltaed. the other fields all reset to 0 if their bit is not set.\r\n\t\t\tWARNING: at this time, only players will have this set. a future version will enable this for rockets/nails also.\r\n\t\t\t\tthis means that nq engines will need to support MOVETYPE_FLY as a simple VectorMA projection, but should otherwise be able to use interpolation for all other movetypes.\r\n\t\t\tbyte - UFP pred flags\r\n\t\t\tUFP_FORWARD(0): short, otherwise 0 - +forwards/+back speed\r\n\t\t\tUFP_SIDE(1): short, otherwise 0 - +right/+left speed\r\n\t\t\tUFP_UP(2): short, otherwise 0 - +moveup/+movedown speed\r\n\t\t\tUFP_MOVETYPE(3): byte - .movetype\r\n\t\t\tUFP_VELOCITYXY(4): short, otherwise 0 - .velocity[0]*8, .velocity[1]*8.\r\n\t\t\tUFP_VELOCITYZ(5): short, otherwise 0 - .velocity[2]*8.\r\n\t\t\tUFP_MSEC(6): byte, otherwise 0 - how long since the server received an update from this player. Clients should internally add half of their own latency to this value.\r\n\t\t\tUFP_WEAPONFRAME(7): byte - bits 0-6 of .weaponframe. If it has bit 7 set, read another byte for the high bits\r\n\t\t\t\t\t optional byte - the high bits\r\n\t\tUF_EXTEND1(7): nothing here - already sent. listed for completeness.\r\n\r\n\t\tUF_RESET(8): nothing - if bit is set, reset from baseline before parsing data. First two updates containing this entity will always contain this bit set.\r\n\t\tUF_16BIT(9): nothing itself - merely extends the size of other bits of info (model, skin, frame)\r\n\t\tUF_MODEL(10): modelindex. bit 15 enables full ragdoll, and should otherwise be masked out.\r\n\t\t\tUF_16BIT: ushort\r\n\t\t\totherwise: byte\r\n\t\tUF_SKIN(11): the skin number to use. note that it is signed. negative values instead state q1 contents. This is consistant with fte's ssqc and with halflife. for compatibility with hexen2, values 100-109 inclusive are 'global skins' and will use the image \"gfx/skin%d.lmp\" instead, but only if the model does not have that many skins.\r\n\t\t\tUF_16BIT: short\r\n\t\t\totherwise: char\r\n\t\tUF_COLORMAP(12): byte - normally the 1-basedentity player slot of owning player, or 0 for world. treat-as-0 if invalid.\r\n\t\tUF_SOLID(13): ushort - encoding of bbox size. 0 means non-solid, 31 means 'use model size', otherwise the low 5 bits are the -mins_x,-mins_y,maxs_x,maxs_y, the next 5 bits are -maxs_z, the final 6 bits state the maxs_z+32 (thereby allowing maxs_z to range between -32 and 31). This encoding is consistant with Q2. Without client-side prediction, you can ignore this setting.\r\n\t\tUF_FLAGS(14): byte - some misc flags, yay. Flags are consistant with the DP5+ protocol.\r\n\t\t\t1 = stepping.\r\n\t\t\t2 = glowtrail. leaves a trail the same colour as the glowmod stuff.\r\n\t\t\t4 = viewmodelforclient was set\r\n\t\t\t8 = exteriormodelforclient was set\r\n\t\t\t16 = reserved: low precision.\r\n\t\t\t32 = specific colourmap. UF_COLORMAP byte's low 4 bits are pants. high 4 bits are shirt.\r\n\t\t\t64 = reserved: act as if part of world.\r\n\t\t\t128 = onground, this hint is for nq engines.\r\n\t\tUF_EXTEND2(15): nothing here - already sent. listed for completeness.\r\n\r\n\t\tUF_ALPHA(16): byte - 0-255 expresses effective range of 0-1. 0 is invisible. 255 is fully visible and default.\r\n\t\tUF_SCALE(17): byte - default value 16 is equivelent to scale=1. 0 means scaled by 0.\r\n\t\tUF_UNUSED(18): not used\r\n\t\tUF_DRAWFLAGS(19):\r\n\t\t\tbyte - various flags to affect scale, abslight, etc. required for hexen2 compat.\r\n\t\t\toptional byte - abslight. sent+used if (drawflags&7)==7.\r\n\t\tUF_TAGINFO(20):\r\n\t\t\tentityidx - tagentity. 0 = disable.\r\n\t\t\tbyte - tagindex. 0 = use tagentity's origin, otherwise 1-based bone index.\r\n\t\tUF_LIGHT(21):\r\n\t\t\tshort - red light scale\r\n\t\t\tshort - green light scale\r\n\t\t\tshort - blue light scale\r\n\t\t\tshort - light radius\r\n\t\t\tbyte - light style.\r\n\t\t\tbyte - light flags.\r\n\t\tUF_TRAILEFFECT(22): short - particleffect num to attach and use as this entity's particle trail. the effect index value is compatible with DP_SV_POINTPARTICLES.\r\n\t\tUF_EXTEND3(23): nothing here - already sent. listed for completeness.\r\n\r\n\t\tUF_COLORMOD(24): discolours the entity. default byte value 32 is equivelent to 1.0.\r\n\t\t\tbyte - red\r\n\t\t\tbyte - green\r\n\t\t\tbyte - blue\r\n\t\tUF_GLOW(25): gives a 'flashblend' type effect.\r\n\t\t\tbyte - size/4\r\n\t\t\tbyte - colour\r\n\t\t\tbyte - red, default byte value 32 is equivelent to 1.0.\r\n\t\t\tbyte - green\r\n\t\t\tbyte - blue\r\n\t\tUF_FATNESS(26): byte - how many qu/16 to move expand models by (move them outwards along their normal)\r\n\t\tUF_MODELINDEX2(27): second modelindex to use for the entity. for visible weapon models or some such. Note: uses the same frame as the normal entity. For compatibility, if qw vweaps were sent then this uses those instead (and changes the normal modelindex to a weapon-less player as appropriate too).\r\n\t\t\tUF_16BIT: ushort\r\n\t\t\totherwise: byte\r\n\t\tUF_GRAVITYDIR(28): both bytes=0 correlates to normal gravity.\r\n\t\t\tbyte - ((pitch/360) * 256) - 192\r\n\t\t\tbyte - (yaw/360) * 256\r\n\t\tUF_EFFECTS2(29): nothing here - sent combined with UF_EFFECTS. mentioned for documentation.\r\n\t\tUF_UNUSED(30): not used.\r\n\t\tUF_EXTEND4(31): not used.\r\n\t\t\r\n\t\tFIXME weirdness:\r\n\t\t\tpredicting non-players? yes please.\r\n\t\t\tplayer angles are a huge wtf. this protocol uses view angles then divides the angle by 1/3rd post-prediction for MOVETYPE_WALK entities, see PEXT2_PREDINFO for a fix.\r\n\t\t\tthere is no information regarding the nextthink of entities. It *should* be possible to guess well enough without it.\r\n\t\t\t\tEither way it is rare enough that engines already need to interpolate well enough without this info, making it somewhat redundant, imho.\r\n\r\n\t\tthe first 7 flags are the flags that are normally going to be encountered every update.\r\n\t\tthe 6 flags following those are often used only when an entity first becomes visible. the 16bit flag allows for more modelindicies etc and is used as a general 'whoa dude' flag.\r\n\t\tthe 2 flags after that are generally considered extensions (solid+flags), although the 'stepping' hint is useful.\r\n\t\t\teither way, these bits are likely to be present in a vanilla progs, and common during entity setup.\r\n\t\tthe following 2 flags are required for many fairly generic custom maps now, that target fitzquake (alpha).\r\n\t\tthe 13 flags after those are much more rarely used. You can justifyably get away with not implementing them (assuming you error if you do receive any), though its prefered to parse and ignore the results.\r\n\t\tthe final 2 flags are reserved for future expansion. yay.\r\n\r\n\t\tThe information is sent in the unreliable channel.\r\n\t\tThe client must ack the frame using the unreliable packet sequence (via clcfte_ackframe or quakeworld netchan).\r\n\t\tThis means that the server can detect packets/frames which are not mentioned. Such unmentioned packets are assumed to be dropped.\r\n\t\tThe server keeps a log of the entities and updated bits that were sent with each outgoing packet. When the server thinks a frame has been dropped, the ents+UFlags in that frame are folded back into the current outgoing state (taking care of added/removed entities). If too many frames are dropped, the server cannot track the entities which were sent and will drop ALL entities, starting from scratch.\r\n\t\tWhen a bit is resent, current information is used, rather than the stale information that was previously sent, thus its possible to not see an update.\r\n\t\tThis all means that the server needs to track one set of previous-frame state (maximum visible at once) to detect when a new flag must be sent, one set of pending UFlags (maximum on map, set when the previous state was no longer valid or if a dropped packet contained that flag, cleared when sent), and 64 or so sets of entnum+UFlags (maximum updatable in a single packet). This can be used to keep memory use down when there are 4 million potential ents on the server.\r\n\t\tThe server needs to be smart enough to handle dropped frames with remove flags, as well as double-sending(or tripple-sending) reset flags to cover packet loss.\r\n\t\tThe client can be pretty stupid and just update whichever fields are said, so long as it correctly ack the datagrams containing received frames.\r\n\t\tThe server is assumed to build an entity state list containing each visible entity. It is assumed to cross-reference this list with the previous frame in order to compare values and set the pending UFlags. The previous frame is then discarded, and the new list is retained for the following frame. This is why the server only needs to track max-visible instead of max-on-map states. Entity transmission uses only the state for the current frame and the pending UFlags (entities newly no longer visible should must have the remove flag set (due to the cross-reference). non-current entities with other UFlags set due to dropped packets can be ignored as there's already a remove flag pending). If the server runs out of space in the outgoing packet, it can simply leave the pending bits set for the extra ents (and should start mid-way through the list for the next frame).\r\n\r\n\r\nModified/Added SVCs:\r\n\tsvcnq_time:\r\n\tnq fast update:\r\n\tsvcqw_packetentities:\r\n\tsvcqw_deltapacketentities:\r\n\tsvcqw_playerinfo:\r\n\t\tNo longer sent.\r\n\tsvcfte_spawnstatic2=21\r\n\t\tInstead of the standard baseline info, the packet will contain only a delta from the null entity state. This allows transparent entities etc.\r\n\tsvcfte_spawnbaseline2=66\r\n\t\tInstead of the standard baseline info, the packet will contain an entity number followed by a delta from the null entity state. Yay for transparent ents.\r\n\t\tOtherwise identical to the regular svc_spawnbaseline in purpose.\r\n\tsvcfte_updateentities=86\r\n\t\tNQ: Replaces fast updates, svc_time. Note: Do not invalidate all entities simply because its a new netframe!\r\n\t\tQW: Replaces svc_[delta]packetentities, svc_playerinfo.\r\n\t\tThe contents of this packet are:\r\n\t\t\tservertime of update, as a float32.\r\n\t\t\toptional short=0x8000. (ie: when the entity index reads as 0 with the remove flag set) If present, remove ALL entities.\r\n\t\t\t[\r\n\t\t\t\tentity index.\r\n\t\t\t\t\tshort. bit15=remove flag. bit14=largeentity. bits13to0=entity number bits 13 - 0.\r\n\t\t\t\t\toptional byte. bits0to7=entity number bits 21-14.\r\n\t\t\t\t\tThis coding gives max 22 bit entity indicies (about 4 million ents max, and up to 16384 using just shorts).\r\n\t\t\t\tnewdelta.\r\n\t\t\t\t\tonly present if the entity index does not contain the remove flag.\r\n\t\t\t\t\totherwise contains the changed fields.\r\n\t\t\t\t\tif the ent was not previously known, copy it from its baseline before deltaing and hope it contains a model update...\r\n\t\t\t\t\tbeware of the special handling for entity number 0 required by\r\n\t\t\t]\r\n\t\t\tshort=0 (ie: when the entity index reads as 0)\r\n\t\tUnlike svc_deltapacketentities, there is no specific previous frame. The previous state to be used is merely the last state received.\r\n\tsvcfte_csqcentities=76\r\n\t\tIf supported, the entity index coding in this packet matches that of svcfte_updateentities.\r\n\r\nModified/Added CLCs:\r\n\tclcfte_ackframe=50    (note: same clc+data as dp)\r\n\t\tMessage data consists of a single 32bit integer, which is the entity frame that is being acked.\r\n\t\tFrames not mentioned are assumed to be dropped. Acks MUST arrive at the server in ascending order, and should be sent unreliably to reduce latency (even though this is more likely to result in resending the frame).\r\n\t\tYou may ack multiple entity frames within a single packet.\r\n\t\tQuakeWorld: as QW packets contain an ack sequence in the packet header itself, you should omit that ack. If multiple packets are received without an outgoing client packet, you should send explicit acks for all but the last (as that will automatically be acked by the netchan).\r\n\t\tBecause of this fact and typical packet sequences, QuakeWorld clients can omit sending these entirely.\r\n\t\tIf frame ~0u is 'acked', the server will reset and resend all ents.\r\n\r\nInformation:\r\n\tPlayer prediction is implemented by building a log of all the input frames clientside.\r\n\tEach video frame, all input frames from ack+1 to latest are applied to the player's entity, according to its movetype+velocity, etc.\r\n\t(as an optimisation, you instead apply new input frames since last video frame to the previous video frame's results, so long as you still rewind when a new entity state is received).\r\n\tQW: the client's outgoing packet sequence specifies the 'latest' input frame available. the server's outgoing sequence specifies the last input frame applied as well as the entity frame to ack.\r\n\t\t\r\nConflicts:\r\n\tPEXT_SPAWNSTATIC2 defines both svc_spawnstatic2+svc_baseline2. It defines these as containing a QW entity delta.\r\n\tPEXT2_REPLACEMENTDELTAS states that these svcs exist, and that use replacement deltas.\r\n\tPEXT2_REPLACEMENTDELTAS takes precidence, due to practicality.\r\n\r\n\tMSG_WriteEntity's index coding is updated (and separated from writeshort).\r\n\tPEXT_CSQC's entity index coding is updated.\r\n\r\n\tPEXT_FLOATCOORDS is a separate extension that changes the network's primitives. Specifically that angles become 16bit and coords become 32bit floats. The two extensions do not otherwise interact.\r\n\r\n\tPEXT2_REPLACEMENTDELTAS thus obsoletes PEXT_SCALE, PEXT_TRANS, PEXT_ACCURATETIMINGS, PEXT_FATNESS, PEXT_HULLSIZE, PEXT_MODELDBL, PEXT_ENTITYDBL, PEXT_ENTITYDBL2, PEXT_COLOURMOD, PEXT_SPAWNSTATIC2, PEXT_256PACKETENTITIES, PEXT_SETATTACHMENT, and PEXT_DPFLAGS. Not bad. :s\r\n\r\nThings not updated:\r\n\tNQ stats are not changed, and cannot exceed the value 256. FTE might send an svc_updatestat to force it, but this would be needlessly spammy.\r\n\tSoundindex limits are not changed. That's an independant extension.\r\n\r\nHack Zone:\r\n\tprediction support with NQ has been defered for a later extension (which should hopefully replace svc_clientdata also). There is no information about input frames or physics cvars. However, this info should be added for csqc support... PEXT2_PREDINFO marks the preliminary code in fte.\r\n\tFTE servers will not wait for modellist/soundlist/prespawn commands to be acked before sending the next part of data, other than to correctly parse the map checksum. FTE servers will only burst all this information if ReplacementDeltas is supported. It was found that certain clients (namely FTE) was triggering some code to detect messed up ordering - messed up ordering that is fixed having the server track progress instead of relying on echos.\r\nThis extension is simply a handy check to see if the client can cope and without spamming the console.\r\nVanilla QuakeWorld clients appeared to be able to cope with this, but there are indeed certain ways it can fail, like if the client tries to pause for downloads.\r\n\r\n\r\n\r\nPredInfo Extension:\r\n\t#define PEXT2_PREDINFO\t\t\t0x00000020\r\n\tPurpose:\r\n\t\tThis extra extension was to fix up some issues with the NQ protocol, but some parts also apply to QW too.\r\n\tProtocol changes:\r\n\t\tstats are sent semi-unreliably using the same nack mechanism. some extra stats are added to ensure that clc_clientdata is fully redundant.\r\n\t\tweaponframe is moved out of entity deltas and back into a stat, the bit is reused for the client's v_angle to allow spectating without assumptions about angles.\r\n\t\tthere is an added 16-bit movement sequence number added directly after both clc_move and svcfte_updateentities bytes, allowing the server to ack the client's input sequence, allowing for prediction in NQ.\r\n\t\tnq's clc_move's timestamp is no longer used to calculate pings. the server calculates pings based upon acks instead. this is harder to spoof.\r\n\t\tguarentees that clc_move has at least 16bit precision.\r\n\t\t"
  },
  {
    "path": "specs/rotating-brushes.md",
    "content": "\nWith the `DP_SV_ROTATINGBMODEL` QuakeC extension, brush entities can be made to\nrotate using the `.avelocity` field. The brush entity must have an \"origin brush\"\nor else it will rotate around the map origin.\n\n![func_rotate example in TrenchBroom](./images/func_rotate.png)\n\nTo create an origin brush, create another brush in your level editor of choice\nand make it a part of the entity. The center of the new brush should be where\nyou want the entity to rotate from. The new brush should also have an \"ORIGIN\"\ntexture on it, so the map compiler can properly mark it as the center of that\nentity. In Q1-based games, this is usually just a texture named \"ORIGIN\" with no\nother special properties. In Q2-based games, it will be a texture with the \"ORIGIN\"\ncontents flag set.\n\nFinally, it needs some extra additions in QuakeC. In your entities `.think`\nfunction, assign the `.avelocity` field to a vector representing the rotation,\nin degrees per second.\n\n**NOTE**: If you're having issues getting this to work, try using the\n`checkextension` builtin to query the `DP_SV_ROTATINGBMODEL` extension before\nusing it.\n\n## Example Code\n\nHere is an extremely basic `func_rotate` implementation in QuakeC for FTE:\n\n```c\nenumflags {\n\tFUNC_ROTATE_X_AXIS,\n\tFUNC_ROTATE_Y_AXIS,\n\tFUNC_ROTATE_Z_AXIS\n};\n\nvoid func_rotate_think()\n{\n\t// do rotations\n\tif (self.spawnflags & FUNC_ROTATE_X_AXIS)\n\t\tself.avelocity.x = self.speed;\n\tif (self.spawnflags & FUNC_ROTATE_Y_AXIS)\n\t\tself.avelocity.y = self.speed;\n\tif (self.spawnflags & FUNC_ROTATE_Z_AXIS)\n\t\tself.avelocity.z = self.speed;\n\n\tself.nextthink = self.ltime + 0.1;\n}\n\nvoid func_rotate()\n{\n\t// setup brush model\n\tsetorigin(self, self.origin);\n\tsetmodel(self, self.model);\n\tself.movetype = MOVETYPE_PUSH;\n\tself.solid = SOLID_BSP;\n\n\t// start thinking\n\tself.think = func_rotate_think;\n\tself.nextthink = self.ltime + 0.1;\n}\n```\n"
  },
  {
    "path": "specs/rtlights.txt",
    "content": "r_shadow_realtime_world: if 1, enables static realtime lighting\r\nr_shadow_realtime_world_lightmaps: sets the intensity of the precomputed lightstyles. 0 means pure realtime lights, everything else is black. only effective if r_shadow_realtime_world is on\r\nr_shadow_realtime_world_shadows: 0 disables shadows on static realtime lights. tbh not that useful, you're more likely to disable them entirely.\r\n\r\nr_shadow_realtime_dlight: if 1, enables dynamic shadows (on rockets and stuff).\r\nr_shadow_realtime_dlight_shadows: controls whether you want to see shadows moving as the rocket flys past stuff or not.\r\n\r\nr_editlights_reload: command - changes the static realtime lights\r\n                     <no argument>: attempts to load lighting from maps/$mapname.rtlights. If that fails, acts as if bsp was specified.\r\n                               bsp: generates static realtime lighting from the light entities within a bsp. not ideal, but can be useful depending on the number of lights on the map\r\n                              none: removes all static realtime lights. the map goes dark... well, depending upon r_shadow_realtime_world_lightmaps anyway\r\n                     note that this is performed implicitly at the start of each map. if static realtime lights is on, rtlights will be loaded. otherwise none will be. This command allows you to force reload, either because they're not loaded yet or you changed the rtlights file.\r\nr_editlights_save: overwrites the rtlight file for the current map with the currently loaded rtlights. more for mods to provide a light editor interface. only really useful to generate a 'default' rtlights file which could then be edited by hand. not convienient.\r\n\r\ngl_bump: allows the loading and thus usage of bumpmaps, which basically add depth to the lighting effects.\r\ngl_specular: makes surfaces shiny. aka: gloss.\r\n\r\n\r\nat the time of writing, r_polygonoffset_* need to be set to 0, or you'll get pure black doors and stuff, doing so will renable z-fighting with co-planer bsp objects. To Be Fixed.\r\n\r\nCheats: static realtime lighting _used_ to be cheat protected. Which made it useless and unusable. Darkplaces has permitted realtime lights for a while on quakeworld servers without complaint. Thus FTE no longer mandates that the server explicitly allows it.\r\nHaving said that, you can tell if static or dynamic lights are enabled in someone else's client via the f_version say request.\r\n\r\n\r\n\r\n\r\ntodo:\r\nlight util that omits direct lighting + engine that forces r_shadow_realtime_world_lightmaps 1\r\nlight util that emits e5bgr9 lighting + worldspawn key that forces srgb.\r\nlight util that generates complete .rtlights files, with radiuses and colours and falloffs and spotlights etc.\r\nggx.\r\nscreenspace refections."
  },
  {
    "path": "specs/scriptable menus.txt",
    "content": "Scriptable Menus\r\n\r\nScriptable menus have a few uses.\r\n1: Large teamplay mods might wish to provide key bindings and customisable settings, like abreviated player name, possibly sensitivity, etc.\r\n2: Adding new menus for stuff that the FTE team might have neglected to add to the menus for one reason or annother.\r\n3: Modders might wish to use them to provide easy configuration options to the user. Key bindings, for instance. Mods could also involve server admin componants via the use of KRIMZON_SV_PARSECLIENTCOMMAND (or equivelents).\r\n4: To show off your l33tness.\r\n\r\n\r\n\r\nThis means that the client supports a few additional console commands and supplementry extensions.\r\nThe system revolves around using aliases and conditional commands within configs.\r\nMost of the commands affect only cvars, but the menutext commands can be used to run aliases. This allows for scripts to read the values of the other items via the cvars that they control.\r\n\r\nIt is highly recommended that use of this extension be assisted with multiline aliases.\r\nAn example of a multiline alias is:\r\n\r\nalias somealias\r\n{\r\n line1\r\n line2\r\n line3\r\n}\r\n\r\nMultiline aliases can be nested inside each other.\r\nThe main reason for aliases to be used is for convienience, although execs could be used instead.\r\nFTE's if command allows a similar syntax:\r\nif condition\r\n{\r\n  stuff to do if true\r\n}\r\nelse\r\n{\r\n  stuff to do if false\r\n}\r\n\r\nThese are also nestable.\r\n\r\n\r\nHere's an example menu script that was made for testing.\r\nalias MenuScriptTest\r\n{\r\n\talias menucallback\r\n\t{\r\n\t\techo callback $option\r\n\t\tif (($option == \"cancel\") or ($option == \"7\"))\r\n\t\t{\r\n\t\t\tmenuclear\r\n\t\t}\r\n\t}\r\n\tconmenu menucallback\r\n\r\n\tmenutext 0 0 \"Cool test menu\"\r\n\tmenucheck 0 8 \"Bouncy sparks!\" \"r_bouncy_sparks\"\r\n\tmenuslider 0 16 \"Forward speed\" \"cl_forwardspeed\" 20 1000\r\n\tmenuedit 0 30 \"name\" \"name\"\r\n\tmenubind 0 48 \"+use\" \"use\"\r\n\tmenubind 0 64 \"impulse 10\" \"change weapon\"\r\n\r\n\tmenutext 0 128 \"7... Cancel\"\t\t\"cancel\"\r\n}\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\nconmenu [aliasname]\r\n\r\nThis command tells the engine to create a new menu. This is mandatory at the start of a script.\r\nThe aliasname parameter names an alias which will be called when certain events happen, with the $option containing the name of the event\r\nThe current events are:\r\nescape is pressed, $option contains 'cancel'\r\nbuttons 1 to 9 are pressed, $option contains the number pressed\r\nbutton 0 is pressed, $option contains '10'\r\nA menutext option is clicked, $option contains the menutext's command.\r\n \r\n\r\n\r\nmenuclear\r\n\r\nThis command clears the currently shown menu, closing it.\r\n\r\n\r\n\r\nmenubox <x> <y> <width> <height>\r\n\r\nThis command creates a box background. Like the quit message in normal quake.\r\nThis is intended for text, but doesn't actually do anything text based itself. Use with menutext.\r\n\r\n\r\n\r\nmenuedit <x> <y> <text> <cvar>\r\n\r\nThis command creates an editable text box. The text box is attached to the named cvar. If the cvar doesn't exist, it'll be created with an empty value. This is usually best used in conjunction with menutexts with scripts to use when clicked.\r\nThe text box size is 8 'pixels' high, with typically 4 pixels each side. But this additional height is dependant on the user's custom images, and could be up to 8 pixels high.\r\n\r\n\r\n\r\nmenutext <x> <y> <text> [command]\r\nmenutextbig <x> <y> <text> [command]\r\n\r\nPlaces a regular piece of text on the menu. If command is specified then the item will be clickable. When clicked, the option cvar will be filled with the option's command string, and the menu's callback will be executed. If there is no callback, the option will be executed directly.\r\nmenutextbig uses the fte-only large font, and can be used to construct control menus.\r\n\r\n\r\n\r\nmenupic <x> <y> <imgname>\r\n\r\nPlaces an image (typically a lmp) at the specified point on the screen. The position specified is the top left of the image.\r\nIf x is a \"-\", and not an actual number, the x coord will be replaced in order to center the image hoizontally.\r\n\r\n\r\n\r\nmenucheck <x> <y> <text> <cvarname> [bitmask]\r\n\r\nA clickable cvar toggle.\r\nIf bitmask is set, the option will apply a bitmask to the cvar, affecting only that bit. (of course, they're not technically bits).\r\nIf the bitmask is not provided (or is 0 / empty), then the option will switch between 0 and 'true' (true being anything that's not 0/empty, but usually 1).\r\n\r\n\r\n\r\nmenuslider <x> <y> <text> <cvar> <min> <max>\r\n\r\nCreates a slider option (like sensitivity) attached to a cvar.\r\nThe ammount scrolled is chosen based on the values of min and max.\r\n\r\n\r\n\r\nmenubind <x> <y> <text> <command>\r\n\r\nCreates a menu option which can be used to change key bindings. This is one of the few options that doesn't affect any cvars."
  },
  {
    "path": "specs/skeletal.txt",
    "content": "Supported skeletal formats:\r\n FTE supports:\r\n  iqm (prefered)\r\n  psk (.psa is loaded automatically, slow to load)\r\n  md5mesh (animationless mesh-only models)\r\n  md5anim (meshless animation-only models, each one is a single framegroup)\r\n  zym (supported only for compat. single-weighted)\r\n  dpm (supported only for compat. lacks framegroups)\r\n\r\nWith the exception of md5 models, all of the above can be used as a drop-in replacement for regular .mdl files.\r\n\r\nAs you may have noticed, md5anim models contain no mesh, making them pointless, right?\r\nWell, kinda.\r\nUsing the skeletal objects extension, you can actually 'build' the animation data from one model into a skeletal object, and then use that skeleton to render an entity with the actual md5mesh model instead.\r\nThis can be used to share a single set of animation data between X different mesh models (this feature is not specific to md5anims, but the various models do need to have the same bones+bone order, and similar enough stature for it to not look absurd).\r\n\r\nThe ragdoll extension builds upon skeletal objects, where physics bodies+joints are instanciated+moved from the animation data within a skeletal object, and fed back to later update that skeletal object after physics is run.\r\nBecause you can control which bodies are 'animated' as opposed to 'simulated', you can simulate a pony tail for instance, and use animation data for the head itself. Doing so will result in the pony tail following the head naturally, adhering to angle+position changes of the entity itself.\r\n\r\n\r\n\r\n\r\nTo use a skeletal object, you must first create the skeletal object with skel_create, and store the object handle in the entity's skeletonindex field. This will cause rendering to use the bone data from the skeletal object instead of the entity's frame info (it will still use the entity's model as normal, so the bone counts must be correct).\r\nAn uninitialised skeletal object is generally invalid, so after creating the skeletal object, you must fill its bone data with info from the animation model. You can do this with skel_build.\r\n\r\nSo for csqc:\r\nvoid() mycsqcpredrawfunction =\r\n{\r\n\tif (!self.skeletonindex)\r\n\t\tself.skeletonindex = skel_create(self.modelindex);\r\n\tself.frame1time = time;\r\n\tself.frame2time = time;\r\n\tself.frame = 0;\r\n\tself.frame2 = 1;\r\n\tself.lerpfrac = 0.5;\r\n\tskel_build(self.skeletonindex, self, self.modelindex, 0, 0, 0, 1);\r\n};\r\nBe sure to call skel_delete when the entity is removed. The engine will not do this for you.\r\n\r\nThe code above will create a skeletal object for the entity, and fill it with animation data (firstbone, lastbone set to 0 means 'all bones').\r\nThe animation data used will be blended between framegroup 0 and framegroup 1 at a ratio of 50% vs 50%, and will animate with time. Note that you can set frameXtime to be dependant upon distance traveled. If your model animates a run speed of 320 qu per second, then self.frame1time += distancemoved/320; will keep your legs moving in sync with the ground.\r\nThe lerpfrac field is a bit useless, of course. This should generally be used for blending between separate animations. You can of course ditch framegroups entirely and use only frame+frame2 to animate your model, resetting lerpfrac 10 times a second or whatever.\r\n\r\nSeparation of torso+legs can also be achieved, by calling skel_build multiple times with separate bone ranges.\r\nIf your model is arranged with the legs as the early bones, and the torso as the later bones, you can use skel_find_bone to find the lower spine bone, and animate the two bone groups separately by updating the animation fields for torso, then for legs.\r\nNote that you can simply pass something other than 'self' for the legs, and it'll use the animation fields from a different (invisible) entity instead.\r\n\r\nYou can blend forward+back+right+left animations together smoothly by calling skel_build with retainfrac=0 for the first animation, and with retainfrac=1 for all the others. If addfrac for all skel_build calls add up to 1 for each bone, you will have a valid animation that can completely avoid the need for messing with lerpfrac/frame2. Calling skel_build with addfrac not adding up to 1 will result in the model getting rescaled weirdly.\r\n\r\n\r\nAiming:\r\nThe easiest way to make the head aim at some fixed place separately from the gun, is to have the bone set directly straight forward in your modeling program, and to call skel_set_bone_world in the QC after making all your skel_build calls. The forward/right/up vectors should be the v_forward/v_right/v_up vectors after calling makevectors(vectoangles(target - org)); or some such.\r\n\r\nSpinning:\r\nTo have a bone spin 1 rotation every second since time=0, you can use the following code:\r\n{\r\n\tif (!self.skeletonindex) self.skeletonindex = skel_create(self.modelindex);\r\n\tskel_build(self.skeletonindex, self, self.modelindex, 0, 0, 0, 1);\r\n\tmakevectors([0, time*360, 0]);\r\n\tskel_mul_bone(self.skeletonindex, skel_find_bone(self.skeletonindex, \"spine\"), '0 0 0', v_forward, v_right, v_up);\r\n}\r\nThere's nothing special about the skel_create or skel_build calls here, and skel_find_bone should probably be cached or something.\r\nNote that the skel_build is not strictly required every frame. You could change 'time' to 'frametime' and move the skel_build into the skel_create's if-statement block, and the result would be the same.\r\nBecause skeletal objects are (generally) relative, rotating the spine like that will also rotate every single child bone along with the 'spine' bone.\r\nA small note: skel_get_boneabs is in model space, not world space. You should use gettaginfo in order to get the world-space position of the bone (bones are equivelent to tags).\r\n\r\nskel_set_bone will generally set the position of the bone relative to its parent, ignoring that bone's current rotation+position. skel_mul_bone on the other hand will rotate/move without destroying previous rotations/movements. Typically the children of that bone will also move.\r\n\r\n\r\n\r\nRagdolls:\r\nTo use ragdolls, you must have:\r\na) a skeletal model.\r\nb) a skeletal object.\r\nc) a body/joint (doll) definition.\r\n\r\nWhile the entity is still alive, you will be calling skel_build on the skeletal object as normal, but also calling skel_ragupdate afterwards.\r\nHere's some uncompiled example code.\r\n\r\n{\r\n\tfloat alive = self.frame != itsdeadjim;\r\n\tif (!self.skeletonindex)\r\n\t\tself.skeletonindex = skel_create(self.modelindex);\r\n\tif (alive || self.oldalive != alive)\r\n\t{\r\n\t\t//you should update these fields properly...\r\n\t\tself.frame1time = time;\r\n\t\tself.frame2time = time;\r\n\t\tself.frame = 0;\r\n\t\tself.frame2 = 1;\r\n\t\tself.lerpfrac = 0.5;\r\n\t\tskel_build(self.skeletonindex, self, self.modelindex, 0, 0, 0, 1);\r\n\t}\r\n\tif (self.oldalive != alive)\r\n\t{\r\n\t\tif (alive)\r\n\t\t\tskel_ragupdate(self, \"animate 1\", 0);\r\n\t\telse\r\n\t\t\tskel_ragupdate(self, \"animate 0\", 0);\r\n\t}\r\n\tskel_ragupdate(self, \"doll foo.doll\", 0);\r\n\tself.oldalive = alive;\r\n}\r\n\r\nThere's a few noteworthy things in the code above.\r\nSetting the animate value to 1 means the doll will begin animating the doll as normal. This will give some sort of inverse kinematics (BUG: the current version forces positions rather than updating velocities, meaning the bodies will just spaz out as the joints break).\r\nSetting the animate value to 0 means that the doll will ignore all animation values, thus the current info in the skeletal object is ignored and completely overwritten, thus removing the need to call skel_build. You can still call it, it just won't do anything useful.\r\nskel_ragupdate will change the skeletal object to an absolute-skeletal-object (the bones are specified in modelspace rather than relative to their parents).\r\nskel_build can only update relative-skeletal-objects, and will throw an error if given an absolute skeleton. If retainfrac=0 then it'll just silently update the skeletal object to relative as all data will be overwritten anyway.\r\nThe third argument to skel_ragupdate can be used as an animation source. This allows you to retain a set of animation data without destroying it. Specifically, if this is set to 0, the bones without bodies will snap back to their base pose, which can be rather ugly when it dies if the preliminary death animations do not revert them first. This animation source argument must be a relative-skeletal-object (ie: filled in with skel_build).\r\n\r\nFor portability/sanity reasons, don't call skel_get_bonerel+skel_set_bone+skel_mul_bone if ragupdate was called more recently than build.\r\n\r\nCommands that skel_ragupdate accepts are:\r\n enablejoint $jointname $truth\r\n\tCan be used to disable(truth=0)/enable(truth=1) joints.\r\n\tDisabling joints when the entity is killed allows things like dismemberment. You should probably stop calling skel_build for the affected bones if the entity is still alive.\r\n animatebody $bodyname $strength\r\n\tCan be used to override/change whether a single body is animated or not. The skeletal object must be valid/built for the bones of all animating bodies.\r\n\tBy setting and animating a single bone's world position, you can drag the entity around by that bone, even if all other bones are in their non-animated simulate-only state, before reverting that body setting when released.\r\n animate $strength\r\n\tSets/clears the animate flag on all bodies to default.\r\n\tUsed to easily enter ragdoll state or reset the bodies to an alive state.\r\n\r\n doll $dollfilename\r\n\tLoads a doll file from disk and applies it to the current model.\r\n\tSee below for a vauge description of the file format.\r\n\tMultiple instances of the same model will use the same internal representation of the file, but the file also be instanciated with separate models.\r\n dollstring\\n$dolltext\r\n\tCan be used if you wish to embed the doll text within the qc, or generate it yourself.\r\n\tEach skeletal object will have its own copy of the doll.\r\n cleardoll\r\n\tCan be used to uninstanciate the ragdoll. The skeletal object returns to being a regular non-ragdoll skeletal object.\r\n\tNote that skel_delete will do this as required, so this is not really needed.\r\n\r\nLoading a doll with one of the above commands will instanciate the model using the current pose. Make sure you have called skel_build appropriately before doing so.\r\nAttempting to update a ragdoll skeletal object with no command specified will update the skeletal object to follow the ragdoll. If there is no current ragdoll, the default ragdoll for the skeletal object's initial model will be instanciated (the one that would be used from ssqc).\r\nThe default doll can be precached.\r\n\r\n\r\nDifferences between csqc and ssqc:\r\nSSQC is visually crippled at this time. While the various skeleton builtins are available, it is not possible to render them onscreen. They can be used for hitmodel support and the like, but that is all. Other than this severe limitation, ssqc is fully functional. If you wish to test, FTE's csqc and ssqc modules share access to valid skeletal objects - THIS WILL ONLY WORK IN SINGLE PLAYER - thus it is possible to pass the skeletal object index to csqc for debug rendering, and ONLY debug.\r\nPractical use is limited to either only hitmodels (to mirror csqc's positions), or replicating bones via csqc. It should be enough to network just the positions, and to allow the joints to correct the angles.\r\nAs a special exception for ssqc, if the gamecode uses a file named eg 'foo.iqm', the engine will also attempt to load 'foo.doll'. If the frame is set to something rediculous (try 65535), the skeletal object will ignore all 'animate 1' bodies, treating them all as 'animate 0'. This will allow you to activate ragdoll part way through a death animation, but also means you don't have control over the initial frame if the entity enters pvs late. You will likely want to use PVSF_NOREMOVE to mitigate this issue somewhat.\r\nThere is no functionality to copy player ragdolls to bodyqueue corpses, and it is not possible to reassign players to other entity slots. The only way around this is to have the ragdoll entity spawn at the start of the death animation, or be permanantly tracking the (invisible) player with exteriormodeltoclient set.\r\n\r\n\r\n\r\n\r\nRagdoll description files:\r\nNote that the bones are all specified by name. The same .doll description file can be used with multiple models with different bone ordering without an issue.\r\n\r\nto create a body:\r\nbody BODYNAME BONENAME\r\nATTRIBUTE ARGUMENTS\r\n...\r\nattributes are:\r\n shape - one of:\r\n\tbox\r\n\tsphere\r\n\tcylinder\r\n\tcapsule\r\n animate - 1 means uses the animation data instead of the body (and drives the body to match, so other bones follow)\r\n size X [[Y] Z] - the z is the length of cylinders/capsules.\r\n orient [BONE] - orient the body such that the top tip touches the body's bone, and the bottom touches the named bone. if BONE is omitted, uses the body's bone's parent.\r\n\r\nto create a joint:\r\njoint JOINTNAME BODY1 BODY2\r\nATTRIBUTE ARGUMENTS\r\n...\r\nattributes are:\r\n type - one of:\r\n\tfixed\r\n\tpoint\r\n\thinge\r\n\tslider\r\n\tuniversal\r\n\thinge2\r\n ERP,ERP2 - \r\n CFM,CFM2 - should be kept low.\r\n FMax,FMax2 - \r\n HiStop,HiStop2 - the maximum angle allowed.\r\n LoStop,LoStop2 - the minimum angle allowed.\r\n axis,axis2 X Y Z - the hinge axis, a direction vector, around which the hinge will bend, spring will move, etc.\r\n pivot BONE X Y Z - the pivot point of the joint will be created relative to the current position of the given bone. the xyz is relative to the angles of the bone. cannot be used on the default joint.\r\n offset X Y Z - the pivot point relative to the parent bone, which can be used on the default joint.\r\n\r\n\r\n\r\n\r\n\r\nQC definitions for the associated extensions:\r\n\r\n.float skeletonindex;\r\n\tSpecifies the skeletal object to be used to render the entity. The named object overrides the frame etc fields but not the modelindex of the entity.\r\nfloat(float modlindex) skel_create = #263;\r\n\tCreates a new empty skeletal object to be used to animate the given model.\r\n\tEvery skeletal object you create MUST be deleted again - the remove builtin will _NOT_ do this for you.\r\nfloat(float skel, entity ent, float modelindex, float retainfrac, float firstbone, float lastbone, optional float addfrac) skel_build = #264;\r\n\tFill a skeletal object with animation data from a model. model must be compatible with the skeletal object's model.\r\n\tretainfrac+addfrac can be used to blend between simultaneous animations. The gamecode should ensure that the final result aproximates a total strength of '1' for each bone.\r\n\tA firstbone of 0 means 'start with bone 1'.\r\n\tA lastbone of 0 means 'include the last bone'.\r\n\tYou should use different bone regions if you wish to separate torso+legs.\r\n\tent determines which entity's fields to use. frame, frame2, lerpfrac, frame1time, frame2time are all used.\r\n\tCSQCONLY: baseframe, baseframe2, baseframe1time, baseframe2time, baselerpfrac, basebone fields still apply, as they would without skeletal objects.\r\nfloat(float skel) skel_get_numbones = #265;\r\n\tReturns the number of bones in the model. as bone indicies are 1-based, the last bone is actually (skel_get_numbones(sko)+1)\r\nstring(float skel, float bonenum) skel_get_bonename = #266;\r\n\tRetrieves the name of the bone.\r\nfloat(float skel, float bonenum) skel_get_boneparent = #267;\r\n\tRetrieves the index of the given bone's parent. 0 means no parent.\r\nfloat(float skel, string tagname) skel_find_bone = #268;\r\n\tFinds a bone by name. Use this for easily splitting a model at the waste or so.\r\nvector(float skel, float bonenum) skel_get_bonerel = #269;\r\n\tGets the matrix transformation of the bone relative to its parent.\r\n\tthe offset is in the return value. v_forward, v_right, v_up contain the angle transform.\r\nvector(float skel, float bonenum) skel_get_boneabs = #270;\r\n\tGets the matrix transformation of the bone relative to the entity that it is attached to.\r\n\tthe offset is in the return value. v_forward, v_right, v_up contain the angle transform.\r\nvector(entity ent, float tagindex) gettaginfo = #452;\r\n\tGets the matrix transformation of the bone in world space.\r\n\tthe origin is in the return value. v_forward, v_right, v_up contain the angle transform. Use vectoangles(v_forward, v_up) to get the required angles for an entity to be positioned on that bone.\r\nvoid(float skel, float bonenum, vector org, optional vector fwd, optional vector right, optional vector up) skel_set_bone = #271;\r\n\tSets the relative transform of the bone relative to its parent.\r\n\tAlso affects the child bones.\r\n\tThis can be used to remove the coord changes from certain md5anim files, for example.\r\nvoid(float skel, float bonenum, vector org, optional vector fwd, optional vector right, optional vector up) skel_mul_bone = #272;\r\n\tRotates/moves the bone relative to the parent.\r\n\tAlso affects the children.\r\n\tAn easy trick is to use:\r\n\tmakevectors('0 15 0');\r\n\tskel_mul_bone(skel, bone, '0 0 0');\r\n\tTo rotate the given bone 15 degrees around its parent.\r\nvoid(float skel, float startbone, float endbone, vector org, optional vector fwd, optional vector right, optional vector up) skel_mul_bones = #273;\r\n\tRotates/moves a range of bones individually. Useful for character spines where you want to affect each rib. Note that the angle is applied separately to each bone, so the angle used should be =(totalangle/numbones).\r\nvoid(float skeldst, float skelsrc, float startbone, float entbone) skel_copybones = #274;\r\n\tCopies the bones directly from one skeletal object to another. If the parent bones still differ then the visible result can still differ.\r\nvoid(float skel) skel_delete = #275;\r\n\tDeletes a skeletal object. The skeletal object is still valid and usable until the end of the frame. This allows you to add entities using that sko and delete them as part of the same frame.\r\nvoid(entity ent, float bonenum, vector org, optional vector angorfwd, optional vector right, optional vector up) skel_set_bone_world = #283;\r\n\tEdits the skeletal object attached to the entity to position+rotate the bone to the exact worldspace coordinate/angles specified, for things like eyes tracking victims etc.\r\nfloat*(float skel) skel_mmap = #282;\r\n\tReturns a pointer to the bonematrix of the skeletal object. Starting at bone 1, each bone takes 12 floats, ordered: fwd_x, lft_x, up_x, org_x, fwd_y, lft_y, up_y, org_y, fwd_z, lft_z, up_z, org_z.\r\n\tThe pointer is valid for as long as the sko is.\r\nfloat(entity skelent, string dollcommand, float animskel) skel_ragupdate = #281;\r\n\tCreates, modifies, or animates the physics bodies attached to the ent's sko, and copies the results back into the sko bone data.\r\n\tdollcommand can be blank or for example \"DOLL models/test.doll\" to specify the ragdoll definition to use.\r\n\tFor performance reasons, the skeletal object will be defined using matricies relative to the entity rather than the parent bones. This will give weird results with the skel_set_bone skel_mul_* builtins, which can be fixed only with skel_build with retainfrac=0 refering to all bones.\r\n\tIf animskel is set: bones without bodies will use the information in the given sko.\r\n\tIf animskel is 0 and skel_build was called more recently than skel_ragupdate: bones without bodies will use the sko bone data already in the object.\r\n\tIf animskel is 0 and skel_build was not called recently: bones without bodies will snap to the base pose (relative to parent).\r\n\r\n.float pvsflags;\r\n\tModifiers to control how the PVS affects entities.\r\n#define PVSF_NORMALPVS\t\t0\r\n\tCulled by sv_cullentities_trace. This is the tighest PVS possible.\r\n#define PVSF_NOTRACECHECK\t1\r\n\tUses PVS only, and will not be subject to sv_cullentities_trace, like vanilla Quake.\r\n#define PVSF_USEPHS\t\t2\r\n\tThe entity will be visible if any sounds it makes could be heard. Basically a fatter pvs.\r\n#define PVSF_IGNOREPVS\t\t3\r\n\tThe entity will be globally visible.\r\n#define PVSF_NOREMOVE 128\r\n\tIf set in pvsflags, the entity will not be hidden from the client simply because it is no longer visible, yet still not globally visible.\r\n\tShould probably always be used on ssqc ents \r\n\r\n"
  },
  {
    "path": "specs/spawning-entities-from-external-bsps.md",
    "content": "\nOne way to have prefab elements in the FTEQW engine is to use separate BSP or\nMAP files that contain their own lighting, geometry and entities. These are\ncalled bmodels, or brush models. By default, FTEQW does not handle the spawning\nof entities from these files, and will ignore them. But with the\n`FTE_TERRAIN_MAP` extension, you can spawn them in QuakeC.\n\n## Example Code\n\n```c\n// spawn all child entities inside brush model\n// NOTE: this must be called as self! it uses self.modelindex to get the bmodel\nvoid spawn_child_entities()\n{\n\t// get number of child entities\n\tint num_entities = (int)terrain_edit(TEREDIT_ENT_COUNT);\n\n\t// start at 1 so we don't create another worldspawn\n\tfor (int entity_id = 1i; entity_id < num_entities; entity_id++)\n\t{\n\t\t// spawn child entity\n\t\tentity ent = spawn();\n\n\t\t// get entity data\n\t\tstring entdata = (string)terrain_edit(TEREDIT_ENT_GET, entity_id);\n\t\tentdata = strcat(\"{\", entdata, \"}\");\n\n\t\t// parse it into the entity we spawned\n\t\tparseentitydata(ent, entdata);\n\n\t\t// fix up origin and angles\n\t\tsetorigin(ent, ent.origin + self.origin);\n\t\tent.angles += self.angles;\n\n\t\t// get spawn function\n\t\tvar void() spawnfunc = externvalue(0, ent.classname);\n\n\t\t// run spawnfunc as self\n\t\tentity oself = self;\n\t\tself = ent;\n\t\tspawnfunc();\n\t\tself = oself;\n\t}\n}\n```\n"
  },
  {
    "path": "specs/splitscreen.txt",
    "content": "FTE's splitscreen is supported for Quake, Hexen2, or Quake2. It is not supported in Quake3.\r\n\r\n\r\nUsage:\r\nSet cl_splitscreen to the number of additional clients you wish to use. This can be changed mid-game, thereby adding or removing clients as needed.\r\n0: disabled\r\n1: 2-way\r\n2: 3-way\r\n4: 4-way\r\nother values: not supported.\r\nFor convenience, if cl_splitscreen is enabled then servers will enable coop instead of running singleplayer.\r\nAlso note the input support section.\r\n\r\nTo enable splitscreen on a server, you need to set allow_splitscreen 1 on that public server. Note that this is not normally needed unless you're using a separate/dedicated server.\r\n\r\nAdditionally, protocol extensions must be enabled. cl_nopext 0 will reenable them (this is the default setting, so you won't need to worry too much).\r\n\r\n\r\nMod support:\r\nNot all mods will work perfectly with it, an example being (especially q2) gamecode that handles the 'say' command which is not splitscreen-aware will end up sending multiple prints for each other player. This is not too much of an issue in private games, but can be annoying on public servers.\r\nOther mods that specially craft sound events or other such things for clients may similarly have issues.\r\n\r\nThe QC features viewmodelforclient and exteriormodelforclient are not supported in conjunction with splitscreen. These will be be checked against only the first player, but will apply to all players.\r\n\r\nMods that use CSQC will typically only see the first player unless the csqc code explicitly supports splitscreen.\r\n\r\n\r\n\r\nUserInfo and Names:\r\nYou can change the second / 3rd / 4th player's name or other settings with:\r\np2 setinfo name Foo\r\n\r\n\r\n\r\nInput support:\r\nThere are multiple ways to deal with input, and multiple gotchas.\r\nThe engine generates a device-id per input device of each type. This device id is used to decide which player (aka seat) that input device controls.\r\nThese device ids typically depend upon the order that they are enumerated in, and can thus be renumbered between restarts or reboots, depending upon the operating system and device types.\r\n\r\nin_xinput cvar:\r\non windows, the xinput api provides support for up to 4 game controllers (so long as they're just clones of microsoft's console controllers...). Windows is responsible for deciding which controller is assigned to each seat, so don't blame me if they get reassigned randomly.\r\nThese controllers may optionally have headsets attached to them. If they do, each headset will automatically receive its own player-specific audio stream.\r\nIf this cvar is changed, you will need to use in_restart in order to apply that change.\r\n\r\nin_rawinput cvar:\r\nRawinput provides a way to determine which mouse generated which input events. It also disables mouse acceleration, which is generally a good thing.\r\nIf this cvar is changed, you will need to use in_restart in order to apply that change.\r\n\r\nin_rawinput_keyboard:\r\nRawinput also supports keyboards, along with actual device ids, thereby allowing each player to have their own keyboard, if your computer is set up weirdly like that.\r\nIf this cvar is changed, you will need to use in_restart in order to apply that change.\r\n\r\nin_dinput cvar:\r\nDirectInput supports reporting device ids, however microsoft broke this with Vista where mice are concerned. Use rawinput instead. FTE only supports directinput for mice anyway.\r\n\r\nin_deviceids command:\r\nin_deviceids with no arguments can be used to list the device types, mappings, and names.\r\nYou can reassign device ids with eg: in_deviceids mouse 1 \"whateverwaslistedhere\"\r\nNote that device ids themselves are 0-based, so the previous line will remap the named mouse to player 1.\r\n\r\nin_forceseat cvar:\r\nif set, the device id mechanism is ignored, and all input explicitly controls the specified seat. This is primarily only useful to developers that want to test multiplayer mods. However, it can also be set from the binds menu (where it configures which player's keys are being explicitly controlled).\r\n\r\np2 / +p2 / p3 / etc commands:\r\nWhen playing with only a single keyboard, individual keys can be bound to eg 'p2 impulse 4' or '+p2 attack'. By doing so, those keys will select player 2's nailgun, or move player 2 forward despite other keys controlling player 1 by default.\r\nThis can be configured via the binds menu (when splitscreen is enabled), but be sure to turn 'force client' back off.\r\n\r\ncsqc:\r\ncsqc receives all input events before any in_forceseat modifiers or binds etc. This includes which device generated the event. This allows csqc mods to provide their own interpretation of any key events etc that it wants."
  },
  {
    "path": "specs/videocapture.txt",
    "content": "Video Capturing with FTE\r\n\r\nOverview:\r\nFTE supports screen capture, outputting to either a series of screenshots or (on windows) AVI output. See the description of capturecodec for how output is chosen.\r\nCapturing will work anywhere, in game, in menus, at the console, etc, with one exception. If a demo is currently being played, both the capturing and demo playback will pause whenever the game is not shown fullscreen - that is, if the console is shown, the menu is loaded, etc.\r\nThis allows you to visit the console and issue commands without breaks and pauses.\r\nAlso, when a demo is playing, the demo will be slowed to keep exactly in sync with the capturing. This is important with slow codecs, cpus, framerates, or hard disks.\r\n\r\nTo capture part of a demo, issue the command:\r\ncapture demo.mvd;demo_jump 30:30;capturepause\r\nThis will begin capturing demo.mvd at a specific point, and will be paused.\r\nYou can then change the spectator to the correct point\r\n\r\n\r\n\r\n\r\nRelevent commands:\r\n\r\ncapture [outfilename]\r\n This command tells FTE to begin capturing.\r\n\r\ncapturedemo\r\n This command tells FTE to begin capturing, and to play a demo at the same time.\r\n This has only one advantage over capture and playdemo in that the result is fully synced to the demo. No loading screens and no stuff on the end.\r\n This has limited use to making frag videos.\r\n\r\ncapturestop\r\n Terminates capturing immediatly.\r\n\r\ncapturepause\r\n Pauses capturing. Video displayed during this time will not be in the result.\r\n\r\ndemo_jump [+][mins:]seconds\r\n If the + is present, will jump relative to the current position.\r\n Does not truly support rewinding, but will restart and jump the demo if you try.\r\n\r\n\r\n\r\nRelevent cvars:\r\n\r\ncl_demospeed\r\n Can be used to slow down or accelerate a demo for one reason or annother. Set to 0 to freeze, and 1 for normal speed.\r\n\r\ncapturemessage\r\n Specifies a message to be placed on the screen, visible only in the output.\r\n This can be changed at any point during capturing.\r\n\r\ncapturesound\r\n Specifies weather sound should be captured. This is only supported for AVI output.\r\n Audio will be captured at the current snd_khz speed. Use snd_restart while not capturing to apply a new snd_khz setting.\r\n Changes will not take effect until capturing is restarted.\r\n\r\ncapturerate\r\n Specifies the video frames per second to capture\r\n Changes will not take effect until capturing is restarted.\r\n\r\ncapturecodec\r\n Specifies the type of output you want.\r\n If left empty, the output will be uncompressed AVI.\r\n If one of tga, jpg, png, or pcx are specified, output will be a series of screenshots, one image per file.\r\n If it's a four-character-code, then it specifies what sort of video compression to use. Some codecs force splashscreens, so it might be a good idea to try new ones when running windowed.\r\n Common four-character-codes are:\r\n divx\r\n xvid"
  },
  {
    "path": "specs/viewmodels.md",
    "content": "\nIn FTEQW, you can use CSQC to give you more control over weapon viewmodels.\n\nHere is a chunk of code showing how to spawn a viewmodel entity in CSQC, with\nthe appropriate flags set.\n\n```c\nstatic entity viewmodel;\n\nvoid CSQC_Init(float apilevel, string enginename, float engineversion)\n{\n\tviewmodel = spawn();\n\tviewmodel.drawmask = MASK_VIEWMODEL;\n\tviewmodel.renderflags |= RF_VIEWMODEL;\n\tviewmodel.effects |= EF_NOSHADOW;\n\t// you may have to rotate the viewmodel angles depending on how you\n\t// exported it from your modeling software\n\tviewmodel.angles = [0, 0, 0];\n\tsetorigin(viewmodel, [0, 0, 0]);\n\tsetsize(viewmodel, [0, 0, 0], [0, 0, 0]);\n}\n```\n\n...and if your model is skeletally animated, you would do this in\n`CSQC_UpdateView`:\n\n```c\nvoid CSQC_UpdateView(float vwidth, float vheight, float notmenu)\n{\n\t// push viewmodel animation at constant framerate\n\t// note the use of frametime rather than clframetime, you probably don't\n\t// want the viewmodel animating while the game is paused\n\tviewmodel.frame1time += frametime;\n\n\t// other stuff down here...\n}\n```\n\nNote that we have not set a model on it yet, so it will not yet be visible. To\ndo that, we should first figure out what weapon the player is holding. There's\na few different ways you could do this, but I'm gonna show you the Quake-y way.\nTo get the modelindex of the player's viewmodel, you could do this:\n\n```c\nsetmodelindex(viewmodel, getstatf(STAT_WEAPONMODELI));\n```\n\n`STAT_WEAPONMODELI` is a networked player stat that corresponds to the\nmodelindex of the model specified by `self.weaponmodel` in the SSQC module. You\nshould probably call the above code only if you detect that the player has\nswitched weapons, or if the viewmodel's current modelindex is different from\nthe value read from `STAT_WEAPONMODELI`. You can also read the player's current\nweapon index by reading the stat `STAT_ACTIVEWEAPON`, though you must set this\nyourself in the SSQC module with the `self.weapon` field, otherwise it will\nmean nothing.\n\nTo set the weapon's animation, you could set `self.weaponframe` in SSQC and\nthen read it from CSQC with the `STAT_WEAPONFRAME` stat. In the CSQC module\nyou'd then do:\n\n```c\nviewmodel.frame = getstatf(STAT_WEAPONFRAME);\n```\n\nNote that whenever a new animation starts on the viewmodel, you should set\n`viewmodel.frame1time` back to 0. This will reset the animation time.\n"
  }
]